18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * TI Touch Screen driver 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or 78c2ecf20Sopenharmony_ci * modify it under the terms of the GNU General Public License as 88c2ecf20Sopenharmony_ci * published by the Free Software Foundation version 2. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * This program is distributed "as is" WITHOUT ANY WARRANTY of any 118c2ecf20Sopenharmony_ci * kind, whether express or implied; without even the implied warranty 128c2ecf20Sopenharmony_ci * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 138c2ecf20Sopenharmony_ci * GNU General Public License for more details. 148c2ecf20Sopenharmony_ci */ 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <linux/kernel.h> 188c2ecf20Sopenharmony_ci#include <linux/err.h> 198c2ecf20Sopenharmony_ci#include <linux/module.h> 208c2ecf20Sopenharmony_ci#include <linux/input.h> 218c2ecf20Sopenharmony_ci#include <linux/slab.h> 228c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 238c2ecf20Sopenharmony_ci#include <linux/clk.h> 248c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 258c2ecf20Sopenharmony_ci#include <linux/io.h> 268c2ecf20Sopenharmony_ci#include <linux/delay.h> 278c2ecf20Sopenharmony_ci#include <linux/of.h> 288c2ecf20Sopenharmony_ci#include <linux/of_device.h> 298c2ecf20Sopenharmony_ci#include <linux/sort.h> 308c2ecf20Sopenharmony_ci#include <linux/pm_wakeirq.h> 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#include <linux/mfd/ti_am335x_tscadc.h> 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define ADCFSM_STEPID 0x10 358c2ecf20Sopenharmony_ci#define SEQ_SETTLE 275 368c2ecf20Sopenharmony_ci#define MAX_12BIT ((1 << 12) - 1) 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define TSC_IRQENB_MASK (IRQENB_FIFO0THRES | IRQENB_EOS | IRQENB_HW_PEN) 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic const int config_pins[] = { 418c2ecf20Sopenharmony_ci STEPCONFIG_XPP, 428c2ecf20Sopenharmony_ci STEPCONFIG_XNN, 438c2ecf20Sopenharmony_ci STEPCONFIG_YPP, 448c2ecf20Sopenharmony_ci STEPCONFIG_YNN, 458c2ecf20Sopenharmony_ci}; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistruct titsc { 488c2ecf20Sopenharmony_ci struct input_dev *input; 498c2ecf20Sopenharmony_ci struct ti_tscadc_dev *mfd_tscadc; 508c2ecf20Sopenharmony_ci struct device *dev; 518c2ecf20Sopenharmony_ci unsigned int irq; 528c2ecf20Sopenharmony_ci unsigned int wires; 538c2ecf20Sopenharmony_ci unsigned int x_plate_resistance; 548c2ecf20Sopenharmony_ci bool pen_down; 558c2ecf20Sopenharmony_ci int coordinate_readouts; 568c2ecf20Sopenharmony_ci u32 config_inp[4]; 578c2ecf20Sopenharmony_ci u32 bit_xp, bit_xn, bit_yp, bit_yn; 588c2ecf20Sopenharmony_ci u32 inp_xp, inp_xn, inp_yp, inp_yn; 598c2ecf20Sopenharmony_ci u32 step_mask; 608c2ecf20Sopenharmony_ci u32 charge_delay; 618c2ecf20Sopenharmony_ci}; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic unsigned int titsc_readl(struct titsc *ts, unsigned int reg) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci return readl(ts->mfd_tscadc->tscadc_base + reg); 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic void titsc_writel(struct titsc *tsc, unsigned int reg, 698c2ecf20Sopenharmony_ci unsigned int val) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci writel(val, tsc->mfd_tscadc->tscadc_base + reg); 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic int titsc_config_wires(struct titsc *ts_dev) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci u32 analog_line[4]; 778c2ecf20Sopenharmony_ci u32 wire_order[4]; 788c2ecf20Sopenharmony_ci int i, bit_cfg; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 818c2ecf20Sopenharmony_ci /* 828c2ecf20Sopenharmony_ci * Get the order in which TSC wires are attached 838c2ecf20Sopenharmony_ci * w.r.t. each of the analog input lines on the EVM. 848c2ecf20Sopenharmony_ci */ 858c2ecf20Sopenharmony_ci analog_line[i] = (ts_dev->config_inp[i] & 0xF0) >> 4; 868c2ecf20Sopenharmony_ci wire_order[i] = ts_dev->config_inp[i] & 0x0F; 878c2ecf20Sopenharmony_ci if (WARN_ON(analog_line[i] > 7)) 888c2ecf20Sopenharmony_ci return -EINVAL; 898c2ecf20Sopenharmony_ci if (WARN_ON(wire_order[i] > ARRAY_SIZE(config_pins))) 908c2ecf20Sopenharmony_ci return -EINVAL; 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 948c2ecf20Sopenharmony_ci int an_line; 958c2ecf20Sopenharmony_ci int wi_order; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci an_line = analog_line[i]; 988c2ecf20Sopenharmony_ci wi_order = wire_order[i]; 998c2ecf20Sopenharmony_ci bit_cfg = config_pins[wi_order]; 1008c2ecf20Sopenharmony_ci if (bit_cfg == 0) 1018c2ecf20Sopenharmony_ci return -EINVAL; 1028c2ecf20Sopenharmony_ci switch (wi_order) { 1038c2ecf20Sopenharmony_ci case 0: 1048c2ecf20Sopenharmony_ci ts_dev->bit_xp = bit_cfg; 1058c2ecf20Sopenharmony_ci ts_dev->inp_xp = an_line; 1068c2ecf20Sopenharmony_ci break; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci case 1: 1098c2ecf20Sopenharmony_ci ts_dev->bit_xn = bit_cfg; 1108c2ecf20Sopenharmony_ci ts_dev->inp_xn = an_line; 1118c2ecf20Sopenharmony_ci break; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci case 2: 1148c2ecf20Sopenharmony_ci ts_dev->bit_yp = bit_cfg; 1158c2ecf20Sopenharmony_ci ts_dev->inp_yp = an_line; 1168c2ecf20Sopenharmony_ci break; 1178c2ecf20Sopenharmony_ci case 3: 1188c2ecf20Sopenharmony_ci ts_dev->bit_yn = bit_cfg; 1198c2ecf20Sopenharmony_ci ts_dev->inp_yn = an_line; 1208c2ecf20Sopenharmony_ci break; 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci return 0; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic void titsc_step_config(struct titsc *ts_dev) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci unsigned int config; 1298c2ecf20Sopenharmony_ci int i; 1308c2ecf20Sopenharmony_ci int end_step, first_step, tsc_steps; 1318c2ecf20Sopenharmony_ci u32 stepenable; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci config = STEPCONFIG_MODE_HWSYNC | 1348c2ecf20Sopenharmony_ci STEPCONFIG_AVG_16 | ts_dev->bit_xp; 1358c2ecf20Sopenharmony_ci switch (ts_dev->wires) { 1368c2ecf20Sopenharmony_ci case 4: 1378c2ecf20Sopenharmony_ci config |= STEPCONFIG_INP(ts_dev->inp_yp) | ts_dev->bit_xn; 1388c2ecf20Sopenharmony_ci break; 1398c2ecf20Sopenharmony_ci case 5: 1408c2ecf20Sopenharmony_ci config |= ts_dev->bit_yn | 1418c2ecf20Sopenharmony_ci STEPCONFIG_INP_AN4 | ts_dev->bit_xn | 1428c2ecf20Sopenharmony_ci ts_dev->bit_yp; 1438c2ecf20Sopenharmony_ci break; 1448c2ecf20Sopenharmony_ci case 8: 1458c2ecf20Sopenharmony_ci config |= STEPCONFIG_INP(ts_dev->inp_yp) | ts_dev->bit_xn; 1468c2ecf20Sopenharmony_ci break; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci tsc_steps = ts_dev->coordinate_readouts * 2 + 2; 1508c2ecf20Sopenharmony_ci first_step = TOTAL_STEPS - tsc_steps; 1518c2ecf20Sopenharmony_ci /* Steps 16 to 16-coordinate_readouts is for X */ 1528c2ecf20Sopenharmony_ci end_step = first_step + tsc_steps; 1538c2ecf20Sopenharmony_ci for (i = end_step - ts_dev->coordinate_readouts; i < end_step; i++) { 1548c2ecf20Sopenharmony_ci titsc_writel(ts_dev, REG_STEPCONFIG(i), config); 1558c2ecf20Sopenharmony_ci titsc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY); 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci config = 0; 1598c2ecf20Sopenharmony_ci config = STEPCONFIG_MODE_HWSYNC | 1608c2ecf20Sopenharmony_ci STEPCONFIG_AVG_16 | ts_dev->bit_yn | 1618c2ecf20Sopenharmony_ci STEPCONFIG_INM_ADCREFM; 1628c2ecf20Sopenharmony_ci switch (ts_dev->wires) { 1638c2ecf20Sopenharmony_ci case 4: 1648c2ecf20Sopenharmony_ci config |= ts_dev->bit_yp | STEPCONFIG_INP(ts_dev->inp_xp); 1658c2ecf20Sopenharmony_ci break; 1668c2ecf20Sopenharmony_ci case 5: 1678c2ecf20Sopenharmony_ci config |= ts_dev->bit_xp | STEPCONFIG_INP_AN4 | 1688c2ecf20Sopenharmony_ci STEPCONFIG_XNP | STEPCONFIG_YPN; 1698c2ecf20Sopenharmony_ci break; 1708c2ecf20Sopenharmony_ci case 8: 1718c2ecf20Sopenharmony_ci config |= ts_dev->bit_yp | STEPCONFIG_INP(ts_dev->inp_xp); 1728c2ecf20Sopenharmony_ci break; 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci /* 1 ... coordinate_readouts is for Y */ 1768c2ecf20Sopenharmony_ci end_step = first_step + ts_dev->coordinate_readouts; 1778c2ecf20Sopenharmony_ci for (i = first_step; i < end_step; i++) { 1788c2ecf20Sopenharmony_ci titsc_writel(ts_dev, REG_STEPCONFIG(i), config); 1798c2ecf20Sopenharmony_ci titsc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY); 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci /* Make CHARGECONFIG same as IDLECONFIG */ 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci config = titsc_readl(ts_dev, REG_IDLECONFIG); 1858c2ecf20Sopenharmony_ci titsc_writel(ts_dev, REG_CHARGECONFIG, config); 1868c2ecf20Sopenharmony_ci titsc_writel(ts_dev, REG_CHARGEDELAY, ts_dev->charge_delay); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci /* coordinate_readouts + 1 ... coordinate_readouts + 2 is for Z */ 1898c2ecf20Sopenharmony_ci config = STEPCONFIG_MODE_HWSYNC | 1908c2ecf20Sopenharmony_ci STEPCONFIG_AVG_16 | ts_dev->bit_yp | 1918c2ecf20Sopenharmony_ci ts_dev->bit_xn | STEPCONFIG_INM_ADCREFM | 1928c2ecf20Sopenharmony_ci STEPCONFIG_INP(ts_dev->inp_xp); 1938c2ecf20Sopenharmony_ci titsc_writel(ts_dev, REG_STEPCONFIG(end_step), config); 1948c2ecf20Sopenharmony_ci titsc_writel(ts_dev, REG_STEPDELAY(end_step), 1958c2ecf20Sopenharmony_ci STEPCONFIG_OPENDLY); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci end_step++; 1988c2ecf20Sopenharmony_ci config |= STEPCONFIG_INP(ts_dev->inp_yn); 1998c2ecf20Sopenharmony_ci titsc_writel(ts_dev, REG_STEPCONFIG(end_step), config); 2008c2ecf20Sopenharmony_ci titsc_writel(ts_dev, REG_STEPDELAY(end_step), 2018c2ecf20Sopenharmony_ci STEPCONFIG_OPENDLY); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci /* The steps end ... end - readouts * 2 + 2 and bit 0 for TS_Charge */ 2048c2ecf20Sopenharmony_ci stepenable = 1; 2058c2ecf20Sopenharmony_ci for (i = 0; i < tsc_steps; i++) 2068c2ecf20Sopenharmony_ci stepenable |= 1 << (first_step + i + 1); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci ts_dev->step_mask = stepenable; 2098c2ecf20Sopenharmony_ci am335x_tsc_se_set_cache(ts_dev->mfd_tscadc, ts_dev->step_mask); 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic int titsc_cmp_coord(const void *a, const void *b) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci return *(int *)a - *(int *)b; 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic void titsc_read_coordinates(struct titsc *ts_dev, 2188c2ecf20Sopenharmony_ci u32 *x, u32 *y, u32 *z1, u32 *z2) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci unsigned int yvals[7], xvals[7]; 2218c2ecf20Sopenharmony_ci unsigned int i, xsum = 0, ysum = 0; 2228c2ecf20Sopenharmony_ci unsigned int creads = ts_dev->coordinate_readouts; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci for (i = 0; i < creads; i++) { 2258c2ecf20Sopenharmony_ci yvals[i] = titsc_readl(ts_dev, REG_FIFO0); 2268c2ecf20Sopenharmony_ci yvals[i] &= 0xfff; 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci *z1 = titsc_readl(ts_dev, REG_FIFO0); 2308c2ecf20Sopenharmony_ci *z1 &= 0xfff; 2318c2ecf20Sopenharmony_ci *z2 = titsc_readl(ts_dev, REG_FIFO0); 2328c2ecf20Sopenharmony_ci *z2 &= 0xfff; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci for (i = 0; i < creads; i++) { 2358c2ecf20Sopenharmony_ci xvals[i] = titsc_readl(ts_dev, REG_FIFO0); 2368c2ecf20Sopenharmony_ci xvals[i] &= 0xfff; 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci /* 2408c2ecf20Sopenharmony_ci * If co-ordinates readouts is less than 4 then 2418c2ecf20Sopenharmony_ci * report the average. In case of 4 or more 2428c2ecf20Sopenharmony_ci * readouts, sort the co-ordinate samples, drop 2438c2ecf20Sopenharmony_ci * min and max values and report the average of 2448c2ecf20Sopenharmony_ci * remaining values. 2458c2ecf20Sopenharmony_ci */ 2468c2ecf20Sopenharmony_ci if (creads <= 3) { 2478c2ecf20Sopenharmony_ci for (i = 0; i < creads; i++) { 2488c2ecf20Sopenharmony_ci ysum += yvals[i]; 2498c2ecf20Sopenharmony_ci xsum += xvals[i]; 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci ysum /= creads; 2528c2ecf20Sopenharmony_ci xsum /= creads; 2538c2ecf20Sopenharmony_ci } else { 2548c2ecf20Sopenharmony_ci sort(yvals, creads, sizeof(unsigned int), 2558c2ecf20Sopenharmony_ci titsc_cmp_coord, NULL); 2568c2ecf20Sopenharmony_ci sort(xvals, creads, sizeof(unsigned int), 2578c2ecf20Sopenharmony_ci titsc_cmp_coord, NULL); 2588c2ecf20Sopenharmony_ci for (i = 1; i < creads - 1; i++) { 2598c2ecf20Sopenharmony_ci ysum += yvals[i]; 2608c2ecf20Sopenharmony_ci xsum += xvals[i]; 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci ysum /= creads - 2; 2638c2ecf20Sopenharmony_ci xsum /= creads - 2; 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci *y = ysum; 2668c2ecf20Sopenharmony_ci *x = xsum; 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic irqreturn_t titsc_irq(int irq, void *dev) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci struct titsc *ts_dev = dev; 2728c2ecf20Sopenharmony_ci struct input_dev *input_dev = ts_dev->input; 2738c2ecf20Sopenharmony_ci unsigned int fsm, status, irqclr = 0; 2748c2ecf20Sopenharmony_ci unsigned int x = 0, y = 0; 2758c2ecf20Sopenharmony_ci unsigned int z1, z2, z; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci status = titsc_readl(ts_dev, REG_RAWIRQSTATUS); 2788c2ecf20Sopenharmony_ci if (status & IRQENB_HW_PEN) { 2798c2ecf20Sopenharmony_ci ts_dev->pen_down = true; 2808c2ecf20Sopenharmony_ci irqclr |= IRQENB_HW_PEN; 2818c2ecf20Sopenharmony_ci pm_stay_awake(ts_dev->dev); 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci if (status & IRQENB_PENUP) { 2858c2ecf20Sopenharmony_ci fsm = titsc_readl(ts_dev, REG_ADCFSM); 2868c2ecf20Sopenharmony_ci if (fsm == ADCFSM_STEPID) { 2878c2ecf20Sopenharmony_ci ts_dev->pen_down = false; 2888c2ecf20Sopenharmony_ci input_report_key(input_dev, BTN_TOUCH, 0); 2898c2ecf20Sopenharmony_ci input_report_abs(input_dev, ABS_PRESSURE, 0); 2908c2ecf20Sopenharmony_ci input_sync(input_dev); 2918c2ecf20Sopenharmony_ci pm_relax(ts_dev->dev); 2928c2ecf20Sopenharmony_ci } else { 2938c2ecf20Sopenharmony_ci ts_dev->pen_down = true; 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci irqclr |= IRQENB_PENUP; 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci if (status & IRQENB_EOS) 2998c2ecf20Sopenharmony_ci irqclr |= IRQENB_EOS; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci /* 3028c2ecf20Sopenharmony_ci * ADC and touchscreen share the IRQ line. 3038c2ecf20Sopenharmony_ci * FIFO1 interrupts are used by ADC. Handle FIFO0 IRQs here only 3048c2ecf20Sopenharmony_ci */ 3058c2ecf20Sopenharmony_ci if (status & IRQENB_FIFO0THRES) { 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci titsc_read_coordinates(ts_dev, &x, &y, &z1, &z2); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci if (ts_dev->pen_down && z1 != 0 && z2 != 0) { 3108c2ecf20Sopenharmony_ci /* 3118c2ecf20Sopenharmony_ci * Calculate pressure using formula 3128c2ecf20Sopenharmony_ci * Resistance(touch) = x plate resistance * 3138c2ecf20Sopenharmony_ci * x postion/4096 * ((z2 / z1) - 1) 3148c2ecf20Sopenharmony_ci */ 3158c2ecf20Sopenharmony_ci z = z1 - z2; 3168c2ecf20Sopenharmony_ci z *= x; 3178c2ecf20Sopenharmony_ci z *= ts_dev->x_plate_resistance; 3188c2ecf20Sopenharmony_ci z /= z2; 3198c2ecf20Sopenharmony_ci z = (z + 2047) >> 12; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci if (z <= MAX_12BIT) { 3228c2ecf20Sopenharmony_ci input_report_abs(input_dev, ABS_X, x); 3238c2ecf20Sopenharmony_ci input_report_abs(input_dev, ABS_Y, y); 3248c2ecf20Sopenharmony_ci input_report_abs(input_dev, ABS_PRESSURE, z); 3258c2ecf20Sopenharmony_ci input_report_key(input_dev, BTN_TOUCH, 1); 3268c2ecf20Sopenharmony_ci input_sync(input_dev); 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci irqclr |= IRQENB_FIFO0THRES; 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci if (irqclr) { 3328c2ecf20Sopenharmony_ci titsc_writel(ts_dev, REG_IRQSTATUS, irqclr); 3338c2ecf20Sopenharmony_ci if (status & IRQENB_EOS) 3348c2ecf20Sopenharmony_ci am335x_tsc_se_set_cache(ts_dev->mfd_tscadc, 3358c2ecf20Sopenharmony_ci ts_dev->step_mask); 3368c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci return IRQ_NONE; 3398c2ecf20Sopenharmony_ci} 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_cistatic int titsc_parse_dt(struct platform_device *pdev, 3428c2ecf20Sopenharmony_ci struct titsc *ts_dev) 3438c2ecf20Sopenharmony_ci{ 3448c2ecf20Sopenharmony_ci struct device_node *node = pdev->dev.of_node; 3458c2ecf20Sopenharmony_ci int err; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci if (!node) 3488c2ecf20Sopenharmony_ci return -EINVAL; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci err = of_property_read_u32(node, "ti,wires", &ts_dev->wires); 3518c2ecf20Sopenharmony_ci if (err < 0) 3528c2ecf20Sopenharmony_ci return err; 3538c2ecf20Sopenharmony_ci switch (ts_dev->wires) { 3548c2ecf20Sopenharmony_ci case 4: 3558c2ecf20Sopenharmony_ci case 5: 3568c2ecf20Sopenharmony_ci case 8: 3578c2ecf20Sopenharmony_ci break; 3588c2ecf20Sopenharmony_ci default: 3598c2ecf20Sopenharmony_ci return -EINVAL; 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci err = of_property_read_u32(node, "ti,x-plate-resistance", 3638c2ecf20Sopenharmony_ci &ts_dev->x_plate_resistance); 3648c2ecf20Sopenharmony_ci if (err < 0) 3658c2ecf20Sopenharmony_ci return err; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci /* 3688c2ecf20Sopenharmony_ci * Try with the new binding first. If it fails, try again with 3698c2ecf20Sopenharmony_ci * bogus, miss-spelled version. 3708c2ecf20Sopenharmony_ci */ 3718c2ecf20Sopenharmony_ci err = of_property_read_u32(node, "ti,coordinate-readouts", 3728c2ecf20Sopenharmony_ci &ts_dev->coordinate_readouts); 3738c2ecf20Sopenharmony_ci if (err < 0) { 3748c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "please use 'ti,coordinate-readouts' instead\n"); 3758c2ecf20Sopenharmony_ci err = of_property_read_u32(node, "ti,coordiante-readouts", 3768c2ecf20Sopenharmony_ci &ts_dev->coordinate_readouts); 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci if (err < 0) 3808c2ecf20Sopenharmony_ci return err; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci if (ts_dev->coordinate_readouts <= 0) { 3838c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, 3848c2ecf20Sopenharmony_ci "invalid co-ordinate readouts, resetting it to 5\n"); 3858c2ecf20Sopenharmony_ci ts_dev->coordinate_readouts = 5; 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci err = of_property_read_u32(node, "ti,charge-delay", 3898c2ecf20Sopenharmony_ci &ts_dev->charge_delay); 3908c2ecf20Sopenharmony_ci /* 3918c2ecf20Sopenharmony_ci * If ti,charge-delay value is not specified, then use 3928c2ecf20Sopenharmony_ci * CHARGEDLY_OPENDLY as the default value. 3938c2ecf20Sopenharmony_ci */ 3948c2ecf20Sopenharmony_ci if (err < 0) { 3958c2ecf20Sopenharmony_ci ts_dev->charge_delay = CHARGEDLY_OPENDLY; 3968c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "ti,charge-delay not specified\n"); 3978c2ecf20Sopenharmony_ci } 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci return of_property_read_u32_array(node, "ti,wire-config", 4008c2ecf20Sopenharmony_ci ts_dev->config_inp, ARRAY_SIZE(ts_dev->config_inp)); 4018c2ecf20Sopenharmony_ci} 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci/* 4048c2ecf20Sopenharmony_ci * The functions for inserting/removing driver as a module. 4058c2ecf20Sopenharmony_ci */ 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cistatic int titsc_probe(struct platform_device *pdev) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci struct titsc *ts_dev; 4108c2ecf20Sopenharmony_ci struct input_dev *input_dev; 4118c2ecf20Sopenharmony_ci struct ti_tscadc_dev *tscadc_dev = ti_tscadc_dev_get(pdev); 4128c2ecf20Sopenharmony_ci int err; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci /* Allocate memory for device */ 4158c2ecf20Sopenharmony_ci ts_dev = kzalloc(sizeof(*ts_dev), GFP_KERNEL); 4168c2ecf20Sopenharmony_ci input_dev = input_allocate_device(); 4178c2ecf20Sopenharmony_ci if (!ts_dev || !input_dev) { 4188c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to allocate memory.\n"); 4198c2ecf20Sopenharmony_ci err = -ENOMEM; 4208c2ecf20Sopenharmony_ci goto err_free_mem; 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci tscadc_dev->tsc = ts_dev; 4248c2ecf20Sopenharmony_ci ts_dev->mfd_tscadc = tscadc_dev; 4258c2ecf20Sopenharmony_ci ts_dev->input = input_dev; 4268c2ecf20Sopenharmony_ci ts_dev->irq = tscadc_dev->irq; 4278c2ecf20Sopenharmony_ci ts_dev->dev = &pdev->dev; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci err = titsc_parse_dt(pdev, ts_dev); 4308c2ecf20Sopenharmony_ci if (err) { 4318c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Could not find valid DT data.\n"); 4328c2ecf20Sopenharmony_ci goto err_free_mem; 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci err = request_irq(ts_dev->irq, titsc_irq, 4368c2ecf20Sopenharmony_ci IRQF_SHARED, pdev->dev.driver->name, ts_dev); 4378c2ecf20Sopenharmony_ci if (err) { 4388c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to allocate irq.\n"); 4398c2ecf20Sopenharmony_ci goto err_free_mem; 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci device_init_wakeup(&pdev->dev, true); 4438c2ecf20Sopenharmony_ci err = dev_pm_set_wake_irq(&pdev->dev, ts_dev->irq); 4448c2ecf20Sopenharmony_ci if (err) 4458c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "irq wake enable failed.\n"); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci titsc_writel(ts_dev, REG_IRQSTATUS, TSC_IRQENB_MASK); 4488c2ecf20Sopenharmony_ci titsc_writel(ts_dev, REG_IRQENABLE, IRQENB_FIFO0THRES); 4498c2ecf20Sopenharmony_ci titsc_writel(ts_dev, REG_IRQENABLE, IRQENB_EOS); 4508c2ecf20Sopenharmony_ci err = titsc_config_wires(ts_dev); 4518c2ecf20Sopenharmony_ci if (err) { 4528c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "wrong i/p wire configuration\n"); 4538c2ecf20Sopenharmony_ci goto err_free_irq; 4548c2ecf20Sopenharmony_ci } 4558c2ecf20Sopenharmony_ci titsc_step_config(ts_dev); 4568c2ecf20Sopenharmony_ci titsc_writel(ts_dev, REG_FIFO0THR, 4578c2ecf20Sopenharmony_ci ts_dev->coordinate_readouts * 2 + 2 - 1); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci input_dev->name = "ti-tsc"; 4608c2ecf20Sopenharmony_ci input_dev->dev.parent = &pdev->dev; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); 4638c2ecf20Sopenharmony_ci input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0); 4668c2ecf20Sopenharmony_ci input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0); 4678c2ecf20Sopenharmony_ci input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci /* register to the input system */ 4708c2ecf20Sopenharmony_ci err = input_register_device(input_dev); 4718c2ecf20Sopenharmony_ci if (err) 4728c2ecf20Sopenharmony_ci goto err_free_irq; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, ts_dev); 4758c2ecf20Sopenharmony_ci return 0; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_cierr_free_irq: 4788c2ecf20Sopenharmony_ci dev_pm_clear_wake_irq(&pdev->dev); 4798c2ecf20Sopenharmony_ci device_init_wakeup(&pdev->dev, false); 4808c2ecf20Sopenharmony_ci free_irq(ts_dev->irq, ts_dev); 4818c2ecf20Sopenharmony_cierr_free_mem: 4828c2ecf20Sopenharmony_ci input_free_device(input_dev); 4838c2ecf20Sopenharmony_ci kfree(ts_dev); 4848c2ecf20Sopenharmony_ci return err; 4858c2ecf20Sopenharmony_ci} 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_cistatic int titsc_remove(struct platform_device *pdev) 4888c2ecf20Sopenharmony_ci{ 4898c2ecf20Sopenharmony_ci struct titsc *ts_dev = platform_get_drvdata(pdev); 4908c2ecf20Sopenharmony_ci u32 steps; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci dev_pm_clear_wake_irq(&pdev->dev); 4938c2ecf20Sopenharmony_ci device_init_wakeup(&pdev->dev, false); 4948c2ecf20Sopenharmony_ci free_irq(ts_dev->irq, ts_dev); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci /* total steps followed by the enable mask */ 4978c2ecf20Sopenharmony_ci steps = 2 * ts_dev->coordinate_readouts + 2; 4988c2ecf20Sopenharmony_ci steps = (1 << steps) - 1; 4998c2ecf20Sopenharmony_ci am335x_tsc_se_clr(ts_dev->mfd_tscadc, steps); 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci input_unregister_device(ts_dev->input); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci kfree(ts_dev); 5048c2ecf20Sopenharmony_ci return 0; 5058c2ecf20Sopenharmony_ci} 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_cistatic int __maybe_unused titsc_suspend(struct device *dev) 5088c2ecf20Sopenharmony_ci{ 5098c2ecf20Sopenharmony_ci struct titsc *ts_dev = dev_get_drvdata(dev); 5108c2ecf20Sopenharmony_ci unsigned int idle; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci if (device_may_wakeup(dev)) { 5138c2ecf20Sopenharmony_ci titsc_writel(ts_dev, REG_IRQSTATUS, TSC_IRQENB_MASK); 5148c2ecf20Sopenharmony_ci idle = titsc_readl(ts_dev, REG_IRQENABLE); 5158c2ecf20Sopenharmony_ci titsc_writel(ts_dev, REG_IRQENABLE, 5168c2ecf20Sopenharmony_ci (idle | IRQENB_HW_PEN)); 5178c2ecf20Sopenharmony_ci titsc_writel(ts_dev, REG_IRQWAKEUP, IRQWKUP_ENB); 5188c2ecf20Sopenharmony_ci } 5198c2ecf20Sopenharmony_ci return 0; 5208c2ecf20Sopenharmony_ci} 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_cistatic int __maybe_unused titsc_resume(struct device *dev) 5238c2ecf20Sopenharmony_ci{ 5248c2ecf20Sopenharmony_ci struct titsc *ts_dev = dev_get_drvdata(dev); 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci if (device_may_wakeup(dev)) { 5278c2ecf20Sopenharmony_ci titsc_writel(ts_dev, REG_IRQWAKEUP, 5288c2ecf20Sopenharmony_ci 0x00); 5298c2ecf20Sopenharmony_ci titsc_writel(ts_dev, REG_IRQCLR, IRQENB_HW_PEN); 5308c2ecf20Sopenharmony_ci pm_relax(dev); 5318c2ecf20Sopenharmony_ci } 5328c2ecf20Sopenharmony_ci titsc_step_config(ts_dev); 5338c2ecf20Sopenharmony_ci titsc_writel(ts_dev, REG_FIFO0THR, 5348c2ecf20Sopenharmony_ci ts_dev->coordinate_readouts * 2 + 2 - 1); 5358c2ecf20Sopenharmony_ci return 0; 5368c2ecf20Sopenharmony_ci} 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(titsc_pm_ops, titsc_suspend, titsc_resume); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_cistatic const struct of_device_id ti_tsc_dt_ids[] = { 5418c2ecf20Sopenharmony_ci { .compatible = "ti,am3359-tsc", }, 5428c2ecf20Sopenharmony_ci { } 5438c2ecf20Sopenharmony_ci}; 5448c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ti_tsc_dt_ids); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_cistatic struct platform_driver ti_tsc_driver = { 5478c2ecf20Sopenharmony_ci .probe = titsc_probe, 5488c2ecf20Sopenharmony_ci .remove = titsc_remove, 5498c2ecf20Sopenharmony_ci .driver = { 5508c2ecf20Sopenharmony_ci .name = "TI-am335x-tsc", 5518c2ecf20Sopenharmony_ci .pm = &titsc_pm_ops, 5528c2ecf20Sopenharmony_ci .of_match_table = ti_tsc_dt_ids, 5538c2ecf20Sopenharmony_ci }, 5548c2ecf20Sopenharmony_ci}; 5558c2ecf20Sopenharmony_cimodule_platform_driver(ti_tsc_driver); 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("TI touchscreen controller driver"); 5588c2ecf20Sopenharmony_ciMODULE_AUTHOR("Rachna Patil <rachna@ti.com>"); 5598c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 560