18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci* Copyright (C) 2015 Broadcom Corporation 38c2ecf20Sopenharmony_ci* 48c2ecf20Sopenharmony_ci* This program is free software; you can redistribute it and/or 58c2ecf20Sopenharmony_ci* modify it under the terms of the GNU General Public License as 68c2ecf20Sopenharmony_ci* published by the Free Software Foundation version 2. 78c2ecf20Sopenharmony_ci* 88c2ecf20Sopenharmony_ci* This program is distributed "as is" WITHOUT ANY WARRANTY of any 98c2ecf20Sopenharmony_ci* kind, whether express or implied; without even the implied warranty 108c2ecf20Sopenharmony_ci* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 118c2ecf20Sopenharmony_ci* GNU General Public License for more details. 128c2ecf20Sopenharmony_ci*/ 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/init.h> 158c2ecf20Sopenharmony_ci#include <linux/input.h> 168c2ecf20Sopenharmony_ci#include <linux/delay.h> 178c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 188c2ecf20Sopenharmony_ci#include <linux/keyboard.h> 198c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 208c2ecf20Sopenharmony_ci#include <linux/slab.h> 218c2ecf20Sopenharmony_ci#include <linux/of.h> 228c2ecf20Sopenharmony_ci#include <asm/irq.h> 238c2ecf20Sopenharmony_ci#include <linux/io.h> 248c2ecf20Sopenharmony_ci#include <linux/clk.h> 258c2ecf20Sopenharmony_ci#include <linux/serio.h> 268c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 278c2ecf20Sopenharmony_ci#include <linux/regmap.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define IPROC_TS_NAME "iproc-ts" 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define PEN_DOWN_STATUS 1 328c2ecf20Sopenharmony_ci#define PEN_UP_STATUS 0 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define X_MIN 0 358c2ecf20Sopenharmony_ci#define Y_MIN 0 368c2ecf20Sopenharmony_ci#define X_MAX 0xFFF 378c2ecf20Sopenharmony_ci#define Y_MAX 0xFFF 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/* Value given by controller for invalid coordinate. */ 408c2ecf20Sopenharmony_ci#define INVALID_COORD 0xFFFFFFFF 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/* Register offsets */ 438c2ecf20Sopenharmony_ci#define REGCTL1 0x00 448c2ecf20Sopenharmony_ci#define REGCTL2 0x04 458c2ecf20Sopenharmony_ci#define INTERRUPT_THRES 0x08 468c2ecf20Sopenharmony_ci#define INTERRUPT_MASK 0x0c 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#define INTERRUPT_STATUS 0x10 498c2ecf20Sopenharmony_ci#define CONTROLLER_STATUS 0x14 508c2ecf20Sopenharmony_ci#define FIFO_DATA 0x18 518c2ecf20Sopenharmony_ci#define FIFO_DATA_X_Y_MASK 0xFFFF 528c2ecf20Sopenharmony_ci#define ANALOG_CONTROL 0x1c 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci#define AUX_DATA 0x20 558c2ecf20Sopenharmony_ci#define DEBOUNCE_CNTR_STAT 0x24 568c2ecf20Sopenharmony_ci#define SCAN_CNTR_STAT 0x28 578c2ecf20Sopenharmony_ci#define REM_CNTR_STAT 0x2c 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci#define SETTLING_TIMER_STAT 0x30 608c2ecf20Sopenharmony_ci#define SPARE_REG 0x34 618c2ecf20Sopenharmony_ci#define SOFT_BYPASS_CONTROL 0x38 628c2ecf20Sopenharmony_ci#define SOFT_BYPASS_DATA 0x3c 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci/* Bit values for INTERRUPT_MASK and INTERRUPT_STATUS regs */ 668c2ecf20Sopenharmony_ci#define TS_PEN_INTR_MASK BIT(0) 678c2ecf20Sopenharmony_ci#define TS_FIFO_INTR_MASK BIT(2) 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci/* Bit values for CONTROLLER_STATUS reg1 */ 708c2ecf20Sopenharmony_ci#define TS_PEN_DOWN BIT(0) 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/* Shift values for control reg1 */ 738c2ecf20Sopenharmony_ci#define SCANNING_PERIOD_SHIFT 24 748c2ecf20Sopenharmony_ci#define DEBOUNCE_TIMEOUT_SHIFT 16 758c2ecf20Sopenharmony_ci#define SETTLING_TIMEOUT_SHIFT 8 768c2ecf20Sopenharmony_ci#define TOUCH_TIMEOUT_SHIFT 0 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci/* Shift values for coordinates from fifo */ 798c2ecf20Sopenharmony_ci#define X_COORD_SHIFT 0 808c2ecf20Sopenharmony_ci#define Y_COORD_SHIFT 16 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci/* Bit values for REGCTL2 */ 838c2ecf20Sopenharmony_ci#define TS_CONTROLLER_EN_BIT BIT(16) 848c2ecf20Sopenharmony_ci#define TS_CONTROLLER_AVGDATA_SHIFT 8 858c2ecf20Sopenharmony_ci#define TS_CONTROLLER_AVGDATA_MASK (0x7 << TS_CONTROLLER_AVGDATA_SHIFT) 868c2ecf20Sopenharmony_ci#define TS_CONTROLLER_PWR_LDO BIT(5) 878c2ecf20Sopenharmony_ci#define TS_CONTROLLER_PWR_ADC BIT(4) 888c2ecf20Sopenharmony_ci#define TS_CONTROLLER_PWR_BGP BIT(3) 898c2ecf20Sopenharmony_ci#define TS_CONTROLLER_PWR_TS BIT(2) 908c2ecf20Sopenharmony_ci#define TS_WIRE_MODE_BIT BIT(1) 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci#define dbg_reg(dev, priv, reg) \ 938c2ecf20Sopenharmony_cido { \ 948c2ecf20Sopenharmony_ci u32 val; \ 958c2ecf20Sopenharmony_ci regmap_read(priv->regmap, reg, &val); \ 968c2ecf20Sopenharmony_ci dev_dbg(dev, "%20s= 0x%08x\n", #reg, val); \ 978c2ecf20Sopenharmony_ci} while (0) 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistruct tsc_param { 1008c2ecf20Sopenharmony_ci /* Each step is 1024 us. Valid 1-256 */ 1018c2ecf20Sopenharmony_ci u32 scanning_period; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci /* Each step is 512 us. Valid 0-255 */ 1048c2ecf20Sopenharmony_ci u32 debounce_timeout; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci /* 1078c2ecf20Sopenharmony_ci * The settling duration (in ms) is the amount of time the tsc 1088c2ecf20Sopenharmony_ci * waits to allow the voltage to settle after turning on the 1098c2ecf20Sopenharmony_ci * drivers in detection mode. Valid values: 0-11 1108c2ecf20Sopenharmony_ci * 0 = 0.008 ms 1118c2ecf20Sopenharmony_ci * 1 = 0.01 ms 1128c2ecf20Sopenharmony_ci * 2 = 0.02 ms 1138c2ecf20Sopenharmony_ci * 3 = 0.04 ms 1148c2ecf20Sopenharmony_ci * 4 = 0.08 ms 1158c2ecf20Sopenharmony_ci * 5 = 0.16 ms 1168c2ecf20Sopenharmony_ci * 6 = 0.32 ms 1178c2ecf20Sopenharmony_ci * 7 = 0.64 ms 1188c2ecf20Sopenharmony_ci * 8 = 1.28 ms 1198c2ecf20Sopenharmony_ci * 9 = 2.56 ms 1208c2ecf20Sopenharmony_ci * 10 = 5.12 ms 1218c2ecf20Sopenharmony_ci * 11 = 10.24 ms 1228c2ecf20Sopenharmony_ci */ 1238c2ecf20Sopenharmony_ci u32 settling_timeout; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci /* touch timeout in sample counts */ 1268c2ecf20Sopenharmony_ci u32 touch_timeout; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci /* 1298c2ecf20Sopenharmony_ci * Number of data samples which are averaged before a final data point 1308c2ecf20Sopenharmony_ci * is placed into the FIFO 1318c2ecf20Sopenharmony_ci */ 1328c2ecf20Sopenharmony_ci u32 average_data; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci /* FIFO threshold */ 1358c2ecf20Sopenharmony_ci u32 fifo_threshold; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci /* Optional standard touchscreen properties. */ 1388c2ecf20Sopenharmony_ci u32 max_x; 1398c2ecf20Sopenharmony_ci u32 max_y; 1408c2ecf20Sopenharmony_ci u32 fuzz_x; 1418c2ecf20Sopenharmony_ci u32 fuzz_y; 1428c2ecf20Sopenharmony_ci bool invert_x; 1438c2ecf20Sopenharmony_ci bool invert_y; 1448c2ecf20Sopenharmony_ci}; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistruct iproc_ts_priv { 1478c2ecf20Sopenharmony_ci struct platform_device *pdev; 1488c2ecf20Sopenharmony_ci struct input_dev *idev; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci struct regmap *regmap; 1518c2ecf20Sopenharmony_ci struct clk *tsc_clk; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci int pen_status; 1548c2ecf20Sopenharmony_ci struct tsc_param cfg_params; 1558c2ecf20Sopenharmony_ci}; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci/* 1588c2ecf20Sopenharmony_ci * Set default values the same as hardware reset values 1598c2ecf20Sopenharmony_ci * except for fifo_threshold with is set to 1. 1608c2ecf20Sopenharmony_ci */ 1618c2ecf20Sopenharmony_cistatic const struct tsc_param iproc_default_config = { 1628c2ecf20Sopenharmony_ci .scanning_period = 0x5, /* 1 to 256 */ 1638c2ecf20Sopenharmony_ci .debounce_timeout = 0x28, /* 0 to 255 */ 1648c2ecf20Sopenharmony_ci .settling_timeout = 0x7, /* 0 to 11 */ 1658c2ecf20Sopenharmony_ci .touch_timeout = 0xa, /* 0 to 255 */ 1668c2ecf20Sopenharmony_ci .average_data = 5, /* entry 5 = 32 pts */ 1678c2ecf20Sopenharmony_ci .fifo_threshold = 1, /* 0 to 31 */ 1688c2ecf20Sopenharmony_ci .max_x = X_MAX, 1698c2ecf20Sopenharmony_ci .max_y = Y_MAX, 1708c2ecf20Sopenharmony_ci}; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic void ts_reg_dump(struct iproc_ts_priv *priv) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci struct device *dev = &priv->pdev->dev; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci dbg_reg(dev, priv, REGCTL1); 1778c2ecf20Sopenharmony_ci dbg_reg(dev, priv, REGCTL2); 1788c2ecf20Sopenharmony_ci dbg_reg(dev, priv, INTERRUPT_THRES); 1798c2ecf20Sopenharmony_ci dbg_reg(dev, priv, INTERRUPT_MASK); 1808c2ecf20Sopenharmony_ci dbg_reg(dev, priv, INTERRUPT_STATUS); 1818c2ecf20Sopenharmony_ci dbg_reg(dev, priv, CONTROLLER_STATUS); 1828c2ecf20Sopenharmony_ci dbg_reg(dev, priv, FIFO_DATA); 1838c2ecf20Sopenharmony_ci dbg_reg(dev, priv, ANALOG_CONTROL); 1848c2ecf20Sopenharmony_ci dbg_reg(dev, priv, AUX_DATA); 1858c2ecf20Sopenharmony_ci dbg_reg(dev, priv, DEBOUNCE_CNTR_STAT); 1868c2ecf20Sopenharmony_ci dbg_reg(dev, priv, SCAN_CNTR_STAT); 1878c2ecf20Sopenharmony_ci dbg_reg(dev, priv, REM_CNTR_STAT); 1888c2ecf20Sopenharmony_ci dbg_reg(dev, priv, SETTLING_TIMER_STAT); 1898c2ecf20Sopenharmony_ci dbg_reg(dev, priv, SPARE_REG); 1908c2ecf20Sopenharmony_ci dbg_reg(dev, priv, SOFT_BYPASS_CONTROL); 1918c2ecf20Sopenharmony_ci dbg_reg(dev, priv, SOFT_BYPASS_DATA); 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistatic irqreturn_t iproc_touchscreen_interrupt(int irq, void *data) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci struct platform_device *pdev = data; 1978c2ecf20Sopenharmony_ci struct iproc_ts_priv *priv = platform_get_drvdata(pdev); 1988c2ecf20Sopenharmony_ci u32 intr_status; 1998c2ecf20Sopenharmony_ci u32 raw_coordinate; 2008c2ecf20Sopenharmony_ci u16 x; 2018c2ecf20Sopenharmony_ci u16 y; 2028c2ecf20Sopenharmony_ci int i; 2038c2ecf20Sopenharmony_ci bool needs_sync = false; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci regmap_read(priv->regmap, INTERRUPT_STATUS, &intr_status); 2068c2ecf20Sopenharmony_ci intr_status &= TS_PEN_INTR_MASK | TS_FIFO_INTR_MASK; 2078c2ecf20Sopenharmony_ci if (intr_status == 0) 2088c2ecf20Sopenharmony_ci return IRQ_NONE; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci /* Clear all interrupt status bits, write-1-clear */ 2118c2ecf20Sopenharmony_ci regmap_write(priv->regmap, INTERRUPT_STATUS, intr_status); 2128c2ecf20Sopenharmony_ci /* Pen up/down */ 2138c2ecf20Sopenharmony_ci if (intr_status & TS_PEN_INTR_MASK) { 2148c2ecf20Sopenharmony_ci regmap_read(priv->regmap, CONTROLLER_STATUS, &priv->pen_status); 2158c2ecf20Sopenharmony_ci if (priv->pen_status & TS_PEN_DOWN) 2168c2ecf20Sopenharmony_ci priv->pen_status = PEN_DOWN_STATUS; 2178c2ecf20Sopenharmony_ci else 2188c2ecf20Sopenharmony_ci priv->pen_status = PEN_UP_STATUS; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci input_report_key(priv->idev, BTN_TOUCH, priv->pen_status); 2218c2ecf20Sopenharmony_ci needs_sync = true; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci dev_dbg(&priv->pdev->dev, 2248c2ecf20Sopenharmony_ci "pen up-down (%d)\n", priv->pen_status); 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci /* coordinates in FIFO exceed the theshold */ 2288c2ecf20Sopenharmony_ci if (intr_status & TS_FIFO_INTR_MASK) { 2298c2ecf20Sopenharmony_ci for (i = 0; i < priv->cfg_params.fifo_threshold; i++) { 2308c2ecf20Sopenharmony_ci regmap_read(priv->regmap, FIFO_DATA, &raw_coordinate); 2318c2ecf20Sopenharmony_ci if (raw_coordinate == INVALID_COORD) 2328c2ecf20Sopenharmony_ci continue; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci /* 2358c2ecf20Sopenharmony_ci * The x and y coordinate are 16 bits each 2368c2ecf20Sopenharmony_ci * with the x in the lower 16 bits and y in the 2378c2ecf20Sopenharmony_ci * upper 16 bits. 2388c2ecf20Sopenharmony_ci */ 2398c2ecf20Sopenharmony_ci x = (raw_coordinate >> X_COORD_SHIFT) & 2408c2ecf20Sopenharmony_ci FIFO_DATA_X_Y_MASK; 2418c2ecf20Sopenharmony_ci y = (raw_coordinate >> Y_COORD_SHIFT) & 2428c2ecf20Sopenharmony_ci FIFO_DATA_X_Y_MASK; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci /* We only want to retain the 12 msb of the 16 */ 2458c2ecf20Sopenharmony_ci x = (x >> 4) & 0x0FFF; 2468c2ecf20Sopenharmony_ci y = (y >> 4) & 0x0FFF; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci /* Adjust x y according to LCD tsc mount angle. */ 2498c2ecf20Sopenharmony_ci if (priv->cfg_params.invert_x) 2508c2ecf20Sopenharmony_ci x = priv->cfg_params.max_x - x; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci if (priv->cfg_params.invert_y) 2538c2ecf20Sopenharmony_ci y = priv->cfg_params.max_y - y; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci input_report_abs(priv->idev, ABS_X, x); 2568c2ecf20Sopenharmony_ci input_report_abs(priv->idev, ABS_Y, y); 2578c2ecf20Sopenharmony_ci needs_sync = true; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci dev_dbg(&priv->pdev->dev, "xy (0x%x 0x%x)\n", x, y); 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci if (needs_sync) 2648c2ecf20Sopenharmony_ci input_sync(priv->idev); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic int iproc_ts_start(struct input_dev *idev) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci u32 val; 2728c2ecf20Sopenharmony_ci u32 mask; 2738c2ecf20Sopenharmony_ci int error; 2748c2ecf20Sopenharmony_ci struct iproc_ts_priv *priv = input_get_drvdata(idev); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci /* Enable clock */ 2778c2ecf20Sopenharmony_ci error = clk_prepare_enable(priv->tsc_clk); 2788c2ecf20Sopenharmony_ci if (error) { 2798c2ecf20Sopenharmony_ci dev_err(&priv->pdev->dev, "%s clk_prepare_enable failed %d\n", 2808c2ecf20Sopenharmony_ci __func__, error); 2818c2ecf20Sopenharmony_ci return error; 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci /* 2858c2ecf20Sopenharmony_ci * Interrupt is generated when: 2868c2ecf20Sopenharmony_ci * FIFO reaches the int_th value, and pen event(up/down) 2878c2ecf20Sopenharmony_ci */ 2888c2ecf20Sopenharmony_ci val = TS_PEN_INTR_MASK | TS_FIFO_INTR_MASK; 2898c2ecf20Sopenharmony_ci regmap_update_bits(priv->regmap, INTERRUPT_MASK, val, val); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci val = priv->cfg_params.fifo_threshold; 2928c2ecf20Sopenharmony_ci regmap_write(priv->regmap, INTERRUPT_THRES, val); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci /* Initialize control reg1 */ 2958c2ecf20Sopenharmony_ci val = 0; 2968c2ecf20Sopenharmony_ci val |= priv->cfg_params.scanning_period << SCANNING_PERIOD_SHIFT; 2978c2ecf20Sopenharmony_ci val |= priv->cfg_params.debounce_timeout << DEBOUNCE_TIMEOUT_SHIFT; 2988c2ecf20Sopenharmony_ci val |= priv->cfg_params.settling_timeout << SETTLING_TIMEOUT_SHIFT; 2998c2ecf20Sopenharmony_ci val |= priv->cfg_params.touch_timeout << TOUCH_TIMEOUT_SHIFT; 3008c2ecf20Sopenharmony_ci regmap_write(priv->regmap, REGCTL1, val); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci /* Try to clear all interrupt status */ 3038c2ecf20Sopenharmony_ci val = TS_FIFO_INTR_MASK | TS_PEN_INTR_MASK; 3048c2ecf20Sopenharmony_ci regmap_update_bits(priv->regmap, INTERRUPT_STATUS, val, val); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci /* Initialize control reg2 */ 3078c2ecf20Sopenharmony_ci val = TS_CONTROLLER_EN_BIT | TS_WIRE_MODE_BIT; 3088c2ecf20Sopenharmony_ci val |= priv->cfg_params.average_data << TS_CONTROLLER_AVGDATA_SHIFT; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci mask = (TS_CONTROLLER_AVGDATA_MASK); 3118c2ecf20Sopenharmony_ci mask |= (TS_CONTROLLER_PWR_LDO | /* PWR up LDO */ 3128c2ecf20Sopenharmony_ci TS_CONTROLLER_PWR_ADC | /* PWR up ADC */ 3138c2ecf20Sopenharmony_ci TS_CONTROLLER_PWR_BGP | /* PWR up BGP */ 3148c2ecf20Sopenharmony_ci TS_CONTROLLER_PWR_TS); /* PWR up TS */ 3158c2ecf20Sopenharmony_ci mask |= val; 3168c2ecf20Sopenharmony_ci regmap_update_bits(priv->regmap, REGCTL2, mask, val); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci ts_reg_dump(priv); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci return 0; 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_cistatic void iproc_ts_stop(struct input_dev *dev) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci u32 val; 3268c2ecf20Sopenharmony_ci struct iproc_ts_priv *priv = input_get_drvdata(dev); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci /* 3298c2ecf20Sopenharmony_ci * Disable FIFO int_th and pen event(up/down)Interrupts only 3308c2ecf20Sopenharmony_ci * as the interrupt mask register is shared between ADC, TS and 3318c2ecf20Sopenharmony_ci * flextimer. 3328c2ecf20Sopenharmony_ci */ 3338c2ecf20Sopenharmony_ci val = TS_PEN_INTR_MASK | TS_FIFO_INTR_MASK; 3348c2ecf20Sopenharmony_ci regmap_update_bits(priv->regmap, INTERRUPT_MASK, val, 0); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci /* Only power down touch screen controller */ 3378c2ecf20Sopenharmony_ci val = TS_CONTROLLER_PWR_TS; 3388c2ecf20Sopenharmony_ci regmap_update_bits(priv->regmap, REGCTL2, val, val); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci clk_disable(priv->tsc_clk); 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_cistatic int iproc_get_tsc_config(struct device *dev, struct iproc_ts_priv *priv) 3448c2ecf20Sopenharmony_ci{ 3458c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 3468c2ecf20Sopenharmony_ci u32 val; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci priv->cfg_params = iproc_default_config; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci if (!np) 3518c2ecf20Sopenharmony_ci return 0; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci if (of_property_read_u32(np, "scanning_period", &val) >= 0) { 3548c2ecf20Sopenharmony_ci if (val < 1 || val > 256) { 3558c2ecf20Sopenharmony_ci dev_err(dev, "scanning_period (%u) must be [1-256]\n", 3568c2ecf20Sopenharmony_ci val); 3578c2ecf20Sopenharmony_ci return -EINVAL; 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci priv->cfg_params.scanning_period = val; 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci if (of_property_read_u32(np, "debounce_timeout", &val) >= 0) { 3638c2ecf20Sopenharmony_ci if (val > 255) { 3648c2ecf20Sopenharmony_ci dev_err(dev, "debounce_timeout (%u) must be [0-255]\n", 3658c2ecf20Sopenharmony_ci val); 3668c2ecf20Sopenharmony_ci return -EINVAL; 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci priv->cfg_params.debounce_timeout = val; 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci if (of_property_read_u32(np, "settling_timeout", &val) >= 0) { 3728c2ecf20Sopenharmony_ci if (val > 11) { 3738c2ecf20Sopenharmony_ci dev_err(dev, "settling_timeout (%u) must be [0-11]\n", 3748c2ecf20Sopenharmony_ci val); 3758c2ecf20Sopenharmony_ci return -EINVAL; 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci priv->cfg_params.settling_timeout = val; 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci if (of_property_read_u32(np, "touch_timeout", &val) >= 0) { 3818c2ecf20Sopenharmony_ci if (val > 255) { 3828c2ecf20Sopenharmony_ci dev_err(dev, "touch_timeout (%u) must be [0-255]\n", 3838c2ecf20Sopenharmony_ci val); 3848c2ecf20Sopenharmony_ci return -EINVAL; 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci priv->cfg_params.touch_timeout = val; 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci if (of_property_read_u32(np, "average_data", &val) >= 0) { 3908c2ecf20Sopenharmony_ci if (val > 8) { 3918c2ecf20Sopenharmony_ci dev_err(dev, "average_data (%u) must be [0-8]\n", val); 3928c2ecf20Sopenharmony_ci return -EINVAL; 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci priv->cfg_params.average_data = val; 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci if (of_property_read_u32(np, "fifo_threshold", &val) >= 0) { 3988c2ecf20Sopenharmony_ci if (val > 31) { 3998c2ecf20Sopenharmony_ci dev_err(dev, "fifo_threshold (%u)) must be [0-31]\n", 4008c2ecf20Sopenharmony_ci val); 4018c2ecf20Sopenharmony_ci return -EINVAL; 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci priv->cfg_params.fifo_threshold = val; 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci /* Parse optional properties. */ 4078c2ecf20Sopenharmony_ci of_property_read_u32(np, "touchscreen-size-x", &priv->cfg_params.max_x); 4088c2ecf20Sopenharmony_ci of_property_read_u32(np, "touchscreen-size-y", &priv->cfg_params.max_y); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci of_property_read_u32(np, "touchscreen-fuzz-x", 4118c2ecf20Sopenharmony_ci &priv->cfg_params.fuzz_x); 4128c2ecf20Sopenharmony_ci of_property_read_u32(np, "touchscreen-fuzz-y", 4138c2ecf20Sopenharmony_ci &priv->cfg_params.fuzz_y); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci priv->cfg_params.invert_x = 4168c2ecf20Sopenharmony_ci of_property_read_bool(np, "touchscreen-inverted-x"); 4178c2ecf20Sopenharmony_ci priv->cfg_params.invert_y = 4188c2ecf20Sopenharmony_ci of_property_read_bool(np, "touchscreen-inverted-y"); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci return 0; 4218c2ecf20Sopenharmony_ci} 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_cistatic int iproc_ts_probe(struct platform_device *pdev) 4248c2ecf20Sopenharmony_ci{ 4258c2ecf20Sopenharmony_ci struct iproc_ts_priv *priv; 4268c2ecf20Sopenharmony_ci struct input_dev *idev; 4278c2ecf20Sopenharmony_ci int irq; 4288c2ecf20Sopenharmony_ci int error; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 4318c2ecf20Sopenharmony_ci if (!priv) 4328c2ecf20Sopenharmony_ci return -ENOMEM; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci /* touchscreen controller memory mapped regs via syscon*/ 4358c2ecf20Sopenharmony_ci priv->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, 4368c2ecf20Sopenharmony_ci "ts_syscon"); 4378c2ecf20Sopenharmony_ci if (IS_ERR(priv->regmap)) { 4388c2ecf20Sopenharmony_ci error = PTR_ERR(priv->regmap); 4398c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "unable to map I/O memory:%d\n", error); 4408c2ecf20Sopenharmony_ci return error; 4418c2ecf20Sopenharmony_ci } 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci priv->tsc_clk = devm_clk_get(&pdev->dev, "tsc_clk"); 4448c2ecf20Sopenharmony_ci if (IS_ERR(priv->tsc_clk)) { 4458c2ecf20Sopenharmony_ci error = PTR_ERR(priv->tsc_clk); 4468c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 4478c2ecf20Sopenharmony_ci "failed getting clock tsc_clk: %d\n", error); 4488c2ecf20Sopenharmony_ci return error; 4498c2ecf20Sopenharmony_ci } 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci priv->pdev = pdev; 4528c2ecf20Sopenharmony_ci error = iproc_get_tsc_config(&pdev->dev, priv); 4538c2ecf20Sopenharmony_ci if (error) { 4548c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "get_tsc_config failed: %d\n", error); 4558c2ecf20Sopenharmony_ci return error; 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci idev = devm_input_allocate_device(&pdev->dev); 4598c2ecf20Sopenharmony_ci if (!idev) { 4608c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to allocate input device\n"); 4618c2ecf20Sopenharmony_ci return -ENOMEM; 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci priv->idev = idev; 4658c2ecf20Sopenharmony_ci priv->pen_status = PEN_UP_STATUS; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci /* Set input device info */ 4688c2ecf20Sopenharmony_ci idev->name = IPROC_TS_NAME; 4698c2ecf20Sopenharmony_ci idev->dev.parent = &pdev->dev; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci idev->id.bustype = BUS_HOST; 4728c2ecf20Sopenharmony_ci idev->id.vendor = SERIO_UNKNOWN; 4738c2ecf20Sopenharmony_ci idev->id.product = 0; 4748c2ecf20Sopenharmony_ci idev->id.version = 0; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); 4778c2ecf20Sopenharmony_ci __set_bit(BTN_TOUCH, idev->keybit); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci input_set_abs_params(idev, ABS_X, X_MIN, priv->cfg_params.max_x, 4808c2ecf20Sopenharmony_ci priv->cfg_params.fuzz_x, 0); 4818c2ecf20Sopenharmony_ci input_set_abs_params(idev, ABS_Y, Y_MIN, priv->cfg_params.max_y, 4828c2ecf20Sopenharmony_ci priv->cfg_params.fuzz_y, 0); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci idev->open = iproc_ts_start; 4858c2ecf20Sopenharmony_ci idev->close = iproc_ts_stop; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci input_set_drvdata(idev, priv); 4888c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, priv); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci /* get interrupt */ 4918c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 4928c2ecf20Sopenharmony_ci if (irq < 0) 4938c2ecf20Sopenharmony_ci return irq; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci error = devm_request_irq(&pdev->dev, irq, 4968c2ecf20Sopenharmony_ci iproc_touchscreen_interrupt, 4978c2ecf20Sopenharmony_ci IRQF_SHARED, IPROC_TS_NAME, pdev); 4988c2ecf20Sopenharmony_ci if (error) 4998c2ecf20Sopenharmony_ci return error; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci error = input_register_device(priv->idev); 5028c2ecf20Sopenharmony_ci if (error) { 5038c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 5048c2ecf20Sopenharmony_ci "failed to register input device: %d\n", error); 5058c2ecf20Sopenharmony_ci return error; 5068c2ecf20Sopenharmony_ci } 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci return 0; 5098c2ecf20Sopenharmony_ci} 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_cistatic const struct of_device_id iproc_ts_of_match[] = { 5128c2ecf20Sopenharmony_ci {.compatible = "brcm,iproc-touchscreen", }, 5138c2ecf20Sopenharmony_ci { }, 5148c2ecf20Sopenharmony_ci}; 5158c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, iproc_ts_of_match); 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_cistatic struct platform_driver iproc_ts_driver = { 5188c2ecf20Sopenharmony_ci .probe = iproc_ts_probe, 5198c2ecf20Sopenharmony_ci .driver = { 5208c2ecf20Sopenharmony_ci .name = IPROC_TS_NAME, 5218c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(iproc_ts_of_match), 5228c2ecf20Sopenharmony_ci }, 5238c2ecf20Sopenharmony_ci}; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_cimodule_platform_driver(iproc_ts_driver); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("IPROC Touchscreen driver"); 5288c2ecf20Sopenharmony_ciMODULE_AUTHOR("Broadcom"); 5298c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 530