1d5ae685fSJonathan Richardson /* 2d5ae685fSJonathan Richardson * Copyright (C) 2015 Broadcom Corporation 3d5ae685fSJonathan Richardson * 4d5ae685fSJonathan Richardson * This program is free software; you can redistribute it and/or 5d5ae685fSJonathan Richardson * modify it under the terms of the GNU General Public License as 6d5ae685fSJonathan Richardson * published by the Free Software Foundation version 2. 7d5ae685fSJonathan Richardson * 8d5ae685fSJonathan Richardson * This program is distributed "as is" WITHOUT ANY WARRANTY of any 9d5ae685fSJonathan Richardson * kind, whether express or implied; without even the implied warranty 10d5ae685fSJonathan Richardson * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11d5ae685fSJonathan Richardson * GNU General Public License for more details. 12d5ae685fSJonathan Richardson */ 13d5ae685fSJonathan Richardson #include <linux/module.h> 14d5ae685fSJonathan Richardson #include <linux/init.h> 15d5ae685fSJonathan Richardson #include <linux/input.h> 16d5ae685fSJonathan Richardson #include <linux/delay.h> 17d5ae685fSJonathan Richardson #include <linux/interrupt.h> 18d5ae685fSJonathan Richardson #include <linux/keyboard.h> 19d5ae685fSJonathan Richardson #include <linux/platform_device.h> 20d5ae685fSJonathan Richardson #include <linux/slab.h> 21d5ae685fSJonathan Richardson #include <linux/of.h> 22d5ae685fSJonathan Richardson #include <asm/irq.h> 23d5ae685fSJonathan Richardson #include <linux/io.h> 24d5ae685fSJonathan Richardson #include <linux/clk.h> 25d5ae685fSJonathan Richardson #include <linux/serio.h> 2674813cebSRaveendra Padasalagi #include <linux/mfd/syscon.h> 2774813cebSRaveendra Padasalagi #include <linux/regmap.h> 28d5ae685fSJonathan Richardson 29d5ae685fSJonathan Richardson #define IPROC_TS_NAME "iproc-ts" 30d5ae685fSJonathan Richardson 31d5ae685fSJonathan Richardson #define PEN_DOWN_STATUS 1 32d5ae685fSJonathan Richardson #define PEN_UP_STATUS 0 33d5ae685fSJonathan Richardson 34d5ae685fSJonathan Richardson #define X_MIN 0 35d5ae685fSJonathan Richardson #define Y_MIN 0 36d5ae685fSJonathan Richardson #define X_MAX 0xFFF 37d5ae685fSJonathan Richardson #define Y_MAX 0xFFF 38d5ae685fSJonathan Richardson 39d5ae685fSJonathan Richardson /* Value given by controller for invalid coordinate. */ 40d5ae685fSJonathan Richardson #define INVALID_COORD 0xFFFFFFFF 41d5ae685fSJonathan Richardson 42d5ae685fSJonathan Richardson /* Register offsets */ 43d5ae685fSJonathan Richardson #define REGCTL1 0x00 44d5ae685fSJonathan Richardson #define REGCTL2 0x04 45d5ae685fSJonathan Richardson #define INTERRUPT_THRES 0x08 46d5ae685fSJonathan Richardson #define INTERRUPT_MASK 0x0c 47d5ae685fSJonathan Richardson 48d5ae685fSJonathan Richardson #define INTERRUPT_STATUS 0x10 49d5ae685fSJonathan Richardson #define CONTROLLER_STATUS 0x14 50d5ae685fSJonathan Richardson #define FIFO_DATA 0x18 51d5ae685fSJonathan Richardson #define FIFO_DATA_X_Y_MASK 0xFFFF 52d5ae685fSJonathan Richardson #define ANALOG_CONTROL 0x1c 53d5ae685fSJonathan Richardson 54d5ae685fSJonathan Richardson #define AUX_DATA 0x20 55d5ae685fSJonathan Richardson #define DEBOUNCE_CNTR_STAT 0x24 56d5ae685fSJonathan Richardson #define SCAN_CNTR_STAT 0x28 57d5ae685fSJonathan Richardson #define REM_CNTR_STAT 0x2c 58d5ae685fSJonathan Richardson 59d5ae685fSJonathan Richardson #define SETTLING_TIMER_STAT 0x30 60d5ae685fSJonathan Richardson #define SPARE_REG 0x34 61d5ae685fSJonathan Richardson #define SOFT_BYPASS_CONTROL 0x38 62d5ae685fSJonathan Richardson #define SOFT_BYPASS_DATA 0x3c 63d5ae685fSJonathan Richardson 64d5ae685fSJonathan Richardson 65d5ae685fSJonathan Richardson /* Bit values for INTERRUPT_MASK and INTERRUPT_STATUS regs */ 66d5ae685fSJonathan Richardson #define TS_PEN_INTR_MASK BIT(0) 67d5ae685fSJonathan Richardson #define TS_FIFO_INTR_MASK BIT(2) 68d5ae685fSJonathan Richardson 69d5ae685fSJonathan Richardson /* Bit values for CONTROLLER_STATUS reg1 */ 70d5ae685fSJonathan Richardson #define TS_PEN_DOWN BIT(0) 71d5ae685fSJonathan Richardson 72d5ae685fSJonathan Richardson /* Shift values for control reg1 */ 73d5ae685fSJonathan Richardson #define SCANNING_PERIOD_SHIFT 24 74d5ae685fSJonathan Richardson #define DEBOUNCE_TIMEOUT_SHIFT 16 75d5ae685fSJonathan Richardson #define SETTLING_TIMEOUT_SHIFT 8 76d5ae685fSJonathan Richardson #define TOUCH_TIMEOUT_SHIFT 0 77d5ae685fSJonathan Richardson 78d5ae685fSJonathan Richardson /* Shift values for coordinates from fifo */ 79d5ae685fSJonathan Richardson #define X_COORD_SHIFT 0 80d5ae685fSJonathan Richardson #define Y_COORD_SHIFT 16 81d5ae685fSJonathan Richardson 82d5ae685fSJonathan Richardson /* Bit values for REGCTL2 */ 83d5ae685fSJonathan Richardson #define TS_CONTROLLER_EN_BIT BIT(16) 84d5ae685fSJonathan Richardson #define TS_CONTROLLER_AVGDATA_SHIFT 8 85d5ae685fSJonathan Richardson #define TS_CONTROLLER_AVGDATA_MASK (0x7 << TS_CONTROLLER_AVGDATA_SHIFT) 86d5ae685fSJonathan Richardson #define TS_CONTROLLER_PWR_LDO BIT(5) 87d5ae685fSJonathan Richardson #define TS_CONTROLLER_PWR_ADC BIT(4) 88d5ae685fSJonathan Richardson #define TS_CONTROLLER_PWR_BGP BIT(3) 89d5ae685fSJonathan Richardson #define TS_CONTROLLER_PWR_TS BIT(2) 90d5ae685fSJonathan Richardson #define TS_WIRE_MODE_BIT BIT(1) 91d5ae685fSJonathan Richardson 92d5ae685fSJonathan Richardson #define dbg_reg(dev, priv, reg) \ 9374813cebSRaveendra Padasalagi do { \ 9474813cebSRaveendra Padasalagi u32 val; \ 9574813cebSRaveendra Padasalagi regmap_read(priv->regmap, reg, &val); \ 9674813cebSRaveendra Padasalagi dev_dbg(dev, "%20s= 0x%08x\n", #reg, val); \ 9774813cebSRaveendra Padasalagi } while (0) 98d5ae685fSJonathan Richardson 99d5ae685fSJonathan Richardson struct tsc_param { 100d5ae685fSJonathan Richardson /* Each step is 1024 us. Valid 1-256 */ 101d5ae685fSJonathan Richardson u32 scanning_period; 102d5ae685fSJonathan Richardson 103d5ae685fSJonathan Richardson /* Each step is 512 us. Valid 0-255 */ 104d5ae685fSJonathan Richardson u32 debounce_timeout; 105d5ae685fSJonathan Richardson 106d5ae685fSJonathan Richardson /* 107d5ae685fSJonathan Richardson * The settling duration (in ms) is the amount of time the tsc 108d5ae685fSJonathan Richardson * waits to allow the voltage to settle after turning on the 109d5ae685fSJonathan Richardson * drivers in detection mode. Valid values: 0-11 110d5ae685fSJonathan Richardson * 0 = 0.008 ms 111d5ae685fSJonathan Richardson * 1 = 0.01 ms 112d5ae685fSJonathan Richardson * 2 = 0.02 ms 113d5ae685fSJonathan Richardson * 3 = 0.04 ms 114d5ae685fSJonathan Richardson * 4 = 0.08 ms 115d5ae685fSJonathan Richardson * 5 = 0.16 ms 116d5ae685fSJonathan Richardson * 6 = 0.32 ms 117d5ae685fSJonathan Richardson * 7 = 0.64 ms 118d5ae685fSJonathan Richardson * 8 = 1.28 ms 119d5ae685fSJonathan Richardson * 9 = 2.56 ms 120d5ae685fSJonathan Richardson * 10 = 5.12 ms 121d5ae685fSJonathan Richardson * 11 = 10.24 ms 122d5ae685fSJonathan Richardson */ 123d5ae685fSJonathan Richardson u32 settling_timeout; 124d5ae685fSJonathan Richardson 125d5ae685fSJonathan Richardson /* touch timeout in sample counts */ 126d5ae685fSJonathan Richardson u32 touch_timeout; 127d5ae685fSJonathan Richardson 128d5ae685fSJonathan Richardson /* 129d5ae685fSJonathan Richardson * Number of data samples which are averaged before a final data point 130d5ae685fSJonathan Richardson * is placed into the FIFO 131d5ae685fSJonathan Richardson */ 132d5ae685fSJonathan Richardson u32 average_data; 133d5ae685fSJonathan Richardson 134d5ae685fSJonathan Richardson /* FIFO threshold */ 135d5ae685fSJonathan Richardson u32 fifo_threshold; 136d5ae685fSJonathan Richardson 137d5ae685fSJonathan Richardson /* Optional standard touchscreen properties. */ 138d5ae685fSJonathan Richardson u32 max_x; 139d5ae685fSJonathan Richardson u32 max_y; 140d5ae685fSJonathan Richardson u32 fuzz_x; 141d5ae685fSJonathan Richardson u32 fuzz_y; 142d5ae685fSJonathan Richardson bool invert_x; 143d5ae685fSJonathan Richardson bool invert_y; 144d5ae685fSJonathan Richardson }; 145d5ae685fSJonathan Richardson 146d5ae685fSJonathan Richardson struct iproc_ts_priv { 147d5ae685fSJonathan Richardson struct platform_device *pdev; 148d5ae685fSJonathan Richardson struct input_dev *idev; 149d5ae685fSJonathan Richardson 15074813cebSRaveendra Padasalagi struct regmap *regmap; 151d5ae685fSJonathan Richardson struct clk *tsc_clk; 152d5ae685fSJonathan Richardson 153d5ae685fSJonathan Richardson int pen_status; 154d5ae685fSJonathan Richardson struct tsc_param cfg_params; 155d5ae685fSJonathan Richardson }; 156d5ae685fSJonathan Richardson 157d5ae685fSJonathan Richardson /* 158d5ae685fSJonathan Richardson * Set default values the same as hardware reset values 159d5ae685fSJonathan Richardson * except for fifo_threshold with is set to 1. 160d5ae685fSJonathan Richardson */ 161d5ae685fSJonathan Richardson static const struct tsc_param iproc_default_config = { 162d5ae685fSJonathan Richardson .scanning_period = 0x5, /* 1 to 256 */ 163d5ae685fSJonathan Richardson .debounce_timeout = 0x28, /* 0 to 255 */ 164d5ae685fSJonathan Richardson .settling_timeout = 0x7, /* 0 to 11 */ 165d5ae685fSJonathan Richardson .touch_timeout = 0xa, /* 0 to 255 */ 166d5ae685fSJonathan Richardson .average_data = 5, /* entry 5 = 32 pts */ 167d5ae685fSJonathan Richardson .fifo_threshold = 1, /* 0 to 31 */ 168d5ae685fSJonathan Richardson .max_x = X_MAX, 169d5ae685fSJonathan Richardson .max_y = Y_MAX, 170d5ae685fSJonathan Richardson }; 171d5ae685fSJonathan Richardson 172d5ae685fSJonathan Richardson static void ts_reg_dump(struct iproc_ts_priv *priv) 173d5ae685fSJonathan Richardson { 174d5ae685fSJonathan Richardson struct device *dev = &priv->pdev->dev; 175d5ae685fSJonathan Richardson 176d5ae685fSJonathan Richardson dbg_reg(dev, priv, REGCTL1); 177d5ae685fSJonathan Richardson dbg_reg(dev, priv, REGCTL2); 178d5ae685fSJonathan Richardson dbg_reg(dev, priv, INTERRUPT_THRES); 179d5ae685fSJonathan Richardson dbg_reg(dev, priv, INTERRUPT_MASK); 180d5ae685fSJonathan Richardson dbg_reg(dev, priv, INTERRUPT_STATUS); 181d5ae685fSJonathan Richardson dbg_reg(dev, priv, CONTROLLER_STATUS); 182d5ae685fSJonathan Richardson dbg_reg(dev, priv, FIFO_DATA); 183d5ae685fSJonathan Richardson dbg_reg(dev, priv, ANALOG_CONTROL); 184d5ae685fSJonathan Richardson dbg_reg(dev, priv, AUX_DATA); 185d5ae685fSJonathan Richardson dbg_reg(dev, priv, DEBOUNCE_CNTR_STAT); 186d5ae685fSJonathan Richardson dbg_reg(dev, priv, SCAN_CNTR_STAT); 187d5ae685fSJonathan Richardson dbg_reg(dev, priv, REM_CNTR_STAT); 188d5ae685fSJonathan Richardson dbg_reg(dev, priv, SETTLING_TIMER_STAT); 189d5ae685fSJonathan Richardson dbg_reg(dev, priv, SPARE_REG); 190d5ae685fSJonathan Richardson dbg_reg(dev, priv, SOFT_BYPASS_CONTROL); 191d5ae685fSJonathan Richardson dbg_reg(dev, priv, SOFT_BYPASS_DATA); 192d5ae685fSJonathan Richardson } 193d5ae685fSJonathan Richardson 194d5ae685fSJonathan Richardson static irqreturn_t iproc_touchscreen_interrupt(int irq, void *data) 195d5ae685fSJonathan Richardson { 196d5ae685fSJonathan Richardson struct platform_device *pdev = data; 197d5ae685fSJonathan Richardson struct iproc_ts_priv *priv = platform_get_drvdata(pdev); 198d5ae685fSJonathan Richardson u32 intr_status; 199d5ae685fSJonathan Richardson u32 raw_coordinate; 200d5ae685fSJonathan Richardson u16 x; 201d5ae685fSJonathan Richardson u16 y; 202d5ae685fSJonathan Richardson int i; 203d5ae685fSJonathan Richardson bool needs_sync = false; 204d5ae685fSJonathan Richardson 20574813cebSRaveendra Padasalagi regmap_read(priv->regmap, INTERRUPT_STATUS, &intr_status); 206d5ae685fSJonathan Richardson intr_status &= TS_PEN_INTR_MASK | TS_FIFO_INTR_MASK; 207d5ae685fSJonathan Richardson if (intr_status == 0) 208d5ae685fSJonathan Richardson return IRQ_NONE; 209d5ae685fSJonathan Richardson 210d5ae685fSJonathan Richardson /* Clear all interrupt status bits, write-1-clear */ 21174813cebSRaveendra Padasalagi regmap_write(priv->regmap, INTERRUPT_STATUS, intr_status); 212d5ae685fSJonathan Richardson /* Pen up/down */ 213d5ae685fSJonathan Richardson if (intr_status & TS_PEN_INTR_MASK) { 21474813cebSRaveendra Padasalagi regmap_read(priv->regmap, CONTROLLER_STATUS, &priv->pen_status); 21574813cebSRaveendra Padasalagi if (priv->pen_status & TS_PEN_DOWN) 216d5ae685fSJonathan Richardson priv->pen_status = PEN_DOWN_STATUS; 217d5ae685fSJonathan Richardson else 218d5ae685fSJonathan Richardson priv->pen_status = PEN_UP_STATUS; 219d5ae685fSJonathan Richardson 220d5ae685fSJonathan Richardson input_report_key(priv->idev, BTN_TOUCH, priv->pen_status); 221d5ae685fSJonathan Richardson needs_sync = true; 222d5ae685fSJonathan Richardson 223d5ae685fSJonathan Richardson dev_dbg(&priv->pdev->dev, 224d5ae685fSJonathan Richardson "pen up-down (%d)\n", priv->pen_status); 225d5ae685fSJonathan Richardson } 226d5ae685fSJonathan Richardson 227d5ae685fSJonathan Richardson /* coordinates in FIFO exceed the theshold */ 228d5ae685fSJonathan Richardson if (intr_status & TS_FIFO_INTR_MASK) { 229d5ae685fSJonathan Richardson for (i = 0; i < priv->cfg_params.fifo_threshold; i++) { 23074813cebSRaveendra Padasalagi regmap_read(priv->regmap, FIFO_DATA, &raw_coordinate); 231d5ae685fSJonathan Richardson if (raw_coordinate == INVALID_COORD) 232d5ae685fSJonathan Richardson continue; 233d5ae685fSJonathan Richardson 234d5ae685fSJonathan Richardson /* 235d5ae685fSJonathan Richardson * The x and y coordinate are 16 bits each 236d5ae685fSJonathan Richardson * with the x in the lower 16 bits and y in the 237d5ae685fSJonathan Richardson * upper 16 bits. 238d5ae685fSJonathan Richardson */ 239d5ae685fSJonathan Richardson x = (raw_coordinate >> X_COORD_SHIFT) & 240d5ae685fSJonathan Richardson FIFO_DATA_X_Y_MASK; 241d5ae685fSJonathan Richardson y = (raw_coordinate >> Y_COORD_SHIFT) & 242d5ae685fSJonathan Richardson FIFO_DATA_X_Y_MASK; 243d5ae685fSJonathan Richardson 244d5ae685fSJonathan Richardson /* We only want to retain the 12 msb of the 16 */ 245d5ae685fSJonathan Richardson x = (x >> 4) & 0x0FFF; 246d5ae685fSJonathan Richardson y = (y >> 4) & 0x0FFF; 247d5ae685fSJonathan Richardson 24874813cebSRaveendra Padasalagi /* Adjust x y according to LCD tsc mount angle. */ 249d5ae685fSJonathan Richardson if (priv->cfg_params.invert_x) 250d5ae685fSJonathan Richardson x = priv->cfg_params.max_x - x; 251d5ae685fSJonathan Richardson 252d5ae685fSJonathan Richardson if (priv->cfg_params.invert_y) 253d5ae685fSJonathan Richardson y = priv->cfg_params.max_y - y; 254d5ae685fSJonathan Richardson 255d5ae685fSJonathan Richardson input_report_abs(priv->idev, ABS_X, x); 256d5ae685fSJonathan Richardson input_report_abs(priv->idev, ABS_Y, y); 257d5ae685fSJonathan Richardson needs_sync = true; 258d5ae685fSJonathan Richardson 259d5ae685fSJonathan Richardson dev_dbg(&priv->pdev->dev, "xy (0x%x 0x%x)\n", x, y); 260d5ae685fSJonathan Richardson } 261d5ae685fSJonathan Richardson } 262d5ae685fSJonathan Richardson 263d5ae685fSJonathan Richardson if (needs_sync) 264d5ae685fSJonathan Richardson input_sync(priv->idev); 265d5ae685fSJonathan Richardson 266d5ae685fSJonathan Richardson return IRQ_HANDLED; 267d5ae685fSJonathan Richardson } 268d5ae685fSJonathan Richardson 269d5ae685fSJonathan Richardson static int iproc_ts_start(struct input_dev *idev) 270d5ae685fSJonathan Richardson { 271d5ae685fSJonathan Richardson u32 val; 27274813cebSRaveendra Padasalagi u32 mask; 273d5ae685fSJonathan Richardson int error; 27474813cebSRaveendra Padasalagi struct iproc_ts_priv *priv = input_get_drvdata(idev); 275d5ae685fSJonathan Richardson 276d5ae685fSJonathan Richardson /* Enable clock */ 277d5ae685fSJonathan Richardson error = clk_prepare_enable(priv->tsc_clk); 278d5ae685fSJonathan Richardson if (error) { 279d5ae685fSJonathan Richardson dev_err(&priv->pdev->dev, "%s clk_prepare_enable failed %d\n", 280d5ae685fSJonathan Richardson __func__, error); 281d5ae685fSJonathan Richardson return error; 282d5ae685fSJonathan Richardson } 283d5ae685fSJonathan Richardson 284d5ae685fSJonathan Richardson /* 285d5ae685fSJonathan Richardson * Interrupt is generated when: 286d5ae685fSJonathan Richardson * FIFO reaches the int_th value, and pen event(up/down) 287d5ae685fSJonathan Richardson */ 288d5ae685fSJonathan Richardson val = TS_PEN_INTR_MASK | TS_FIFO_INTR_MASK; 28974813cebSRaveendra Padasalagi regmap_update_bits(priv->regmap, INTERRUPT_MASK, val, val); 290d5ae685fSJonathan Richardson 29174813cebSRaveendra Padasalagi val = priv->cfg_params.fifo_threshold; 29274813cebSRaveendra Padasalagi regmap_write(priv->regmap, INTERRUPT_THRES, val); 293d5ae685fSJonathan Richardson 294d5ae685fSJonathan Richardson /* Initialize control reg1 */ 295d5ae685fSJonathan Richardson val = 0; 296d5ae685fSJonathan Richardson val |= priv->cfg_params.scanning_period << SCANNING_PERIOD_SHIFT; 297d5ae685fSJonathan Richardson val |= priv->cfg_params.debounce_timeout << DEBOUNCE_TIMEOUT_SHIFT; 298d5ae685fSJonathan Richardson val |= priv->cfg_params.settling_timeout << SETTLING_TIMEOUT_SHIFT; 299d5ae685fSJonathan Richardson val |= priv->cfg_params.touch_timeout << TOUCH_TIMEOUT_SHIFT; 30074813cebSRaveendra Padasalagi regmap_write(priv->regmap, REGCTL1, val); 301d5ae685fSJonathan Richardson 302d5ae685fSJonathan Richardson /* Try to clear all interrupt status */ 30374813cebSRaveendra Padasalagi val = TS_FIFO_INTR_MASK | TS_PEN_INTR_MASK; 30474813cebSRaveendra Padasalagi regmap_update_bits(priv->regmap, INTERRUPT_STATUS, val, val); 305d5ae685fSJonathan Richardson 306d5ae685fSJonathan Richardson /* Initialize control reg2 */ 30774813cebSRaveendra Padasalagi val = TS_CONTROLLER_EN_BIT | TS_WIRE_MODE_BIT; 308d5ae685fSJonathan Richardson val |= priv->cfg_params.average_data << TS_CONTROLLER_AVGDATA_SHIFT; 309d5ae685fSJonathan Richardson 31074813cebSRaveendra Padasalagi mask = (TS_CONTROLLER_AVGDATA_MASK); 31174813cebSRaveendra Padasalagi mask |= (TS_CONTROLLER_PWR_LDO | /* PWR up LDO */ 312d5ae685fSJonathan Richardson TS_CONTROLLER_PWR_ADC | /* PWR up ADC */ 313d5ae685fSJonathan Richardson TS_CONTROLLER_PWR_BGP | /* PWR up BGP */ 314d5ae685fSJonathan Richardson TS_CONTROLLER_PWR_TS); /* PWR up TS */ 31574813cebSRaveendra Padasalagi mask |= val; 31674813cebSRaveendra Padasalagi regmap_update_bits(priv->regmap, REGCTL2, mask, val); 317d5ae685fSJonathan Richardson 318d5ae685fSJonathan Richardson ts_reg_dump(priv); 319d5ae685fSJonathan Richardson 320d5ae685fSJonathan Richardson return 0; 321d5ae685fSJonathan Richardson } 322d5ae685fSJonathan Richardson 323d5ae685fSJonathan Richardson static void iproc_ts_stop(struct input_dev *dev) 324d5ae685fSJonathan Richardson { 325d5ae685fSJonathan Richardson u32 val; 326d5ae685fSJonathan Richardson struct iproc_ts_priv *priv = input_get_drvdata(dev); 327d5ae685fSJonathan Richardson 32874813cebSRaveendra Padasalagi /* 32974813cebSRaveendra Padasalagi * Disable FIFO int_th and pen event(up/down)Interrupts only 33074813cebSRaveendra Padasalagi * as the interrupt mask register is shared between ADC, TS and 33174813cebSRaveendra Padasalagi * flextimer. 33274813cebSRaveendra Padasalagi */ 33374813cebSRaveendra Padasalagi val = TS_PEN_INTR_MASK | TS_FIFO_INTR_MASK; 33474813cebSRaveendra Padasalagi regmap_update_bits(priv->regmap, INTERRUPT_MASK, val, 0); 335d5ae685fSJonathan Richardson 336d5ae685fSJonathan Richardson /* Only power down touch screen controller */ 33774813cebSRaveendra Padasalagi val = TS_CONTROLLER_PWR_TS; 33874813cebSRaveendra Padasalagi regmap_update_bits(priv->regmap, REGCTL2, val, val); 339d5ae685fSJonathan Richardson 340d5ae685fSJonathan Richardson clk_disable(priv->tsc_clk); 341d5ae685fSJonathan Richardson } 342d5ae685fSJonathan Richardson 343d5ae685fSJonathan Richardson static int iproc_get_tsc_config(struct device *dev, struct iproc_ts_priv *priv) 344d5ae685fSJonathan Richardson { 345d5ae685fSJonathan Richardson struct device_node *np = dev->of_node; 346d5ae685fSJonathan Richardson u32 val; 347d5ae685fSJonathan Richardson 348d5ae685fSJonathan Richardson priv->cfg_params = iproc_default_config; 349d5ae685fSJonathan Richardson 350d5ae685fSJonathan Richardson if (!np) 351d5ae685fSJonathan Richardson return 0; 352d5ae685fSJonathan Richardson 353d5ae685fSJonathan Richardson if (of_property_read_u32(np, "scanning_period", &val) >= 0) { 354d5ae685fSJonathan Richardson if (val < 1 || val > 256) { 355d5ae685fSJonathan Richardson dev_err(dev, "scanning_period (%u) must be [1-256]\n", 356d5ae685fSJonathan Richardson val); 357d5ae685fSJonathan Richardson return -EINVAL; 358d5ae685fSJonathan Richardson } 359d5ae685fSJonathan Richardson priv->cfg_params.scanning_period = val; 360d5ae685fSJonathan Richardson } 361d5ae685fSJonathan Richardson 362d5ae685fSJonathan Richardson if (of_property_read_u32(np, "debounce_timeout", &val) >= 0) { 363d5ae685fSJonathan Richardson if (val > 255) { 364d5ae685fSJonathan Richardson dev_err(dev, "debounce_timeout (%u) must be [0-255]\n", 365d5ae685fSJonathan Richardson val); 366d5ae685fSJonathan Richardson return -EINVAL; 367d5ae685fSJonathan Richardson } 368d5ae685fSJonathan Richardson priv->cfg_params.debounce_timeout = val; 369d5ae685fSJonathan Richardson } 370d5ae685fSJonathan Richardson 371d5ae685fSJonathan Richardson if (of_property_read_u32(np, "settling_timeout", &val) >= 0) { 372d5ae685fSJonathan Richardson if (val > 11) { 373d5ae685fSJonathan Richardson dev_err(dev, "settling_timeout (%u) must be [0-11]\n", 374d5ae685fSJonathan Richardson val); 375d5ae685fSJonathan Richardson return -EINVAL; 376d5ae685fSJonathan Richardson } 377d5ae685fSJonathan Richardson priv->cfg_params.settling_timeout = val; 378d5ae685fSJonathan Richardson } 379d5ae685fSJonathan Richardson 380d5ae685fSJonathan Richardson if (of_property_read_u32(np, "touch_timeout", &val) >= 0) { 381d5ae685fSJonathan Richardson if (val > 255) { 382d5ae685fSJonathan Richardson dev_err(dev, "touch_timeout (%u) must be [0-255]\n", 383d5ae685fSJonathan Richardson val); 384d5ae685fSJonathan Richardson return -EINVAL; 385d5ae685fSJonathan Richardson } 386d5ae685fSJonathan Richardson priv->cfg_params.touch_timeout = val; 387d5ae685fSJonathan Richardson } 388d5ae685fSJonathan Richardson 389d5ae685fSJonathan Richardson if (of_property_read_u32(np, "average_data", &val) >= 0) { 390d5ae685fSJonathan Richardson if (val > 8) { 391d5ae685fSJonathan Richardson dev_err(dev, "average_data (%u) must be [0-8]\n", val); 392d5ae685fSJonathan Richardson return -EINVAL; 393d5ae685fSJonathan Richardson } 394d5ae685fSJonathan Richardson priv->cfg_params.average_data = val; 395d5ae685fSJonathan Richardson } 396d5ae685fSJonathan Richardson 397d5ae685fSJonathan Richardson if (of_property_read_u32(np, "fifo_threshold", &val) >= 0) { 398d5ae685fSJonathan Richardson if (val > 31) { 399d5ae685fSJonathan Richardson dev_err(dev, "fifo_threshold (%u)) must be [0-31]\n", 400d5ae685fSJonathan Richardson val); 401d5ae685fSJonathan Richardson return -EINVAL; 402d5ae685fSJonathan Richardson } 403d5ae685fSJonathan Richardson priv->cfg_params.fifo_threshold = val; 404d5ae685fSJonathan Richardson } 405d5ae685fSJonathan Richardson 406d5ae685fSJonathan Richardson /* Parse optional properties. */ 407d5ae685fSJonathan Richardson of_property_read_u32(np, "touchscreen-size-x", &priv->cfg_params.max_x); 408d5ae685fSJonathan Richardson of_property_read_u32(np, "touchscreen-size-y", &priv->cfg_params.max_y); 409d5ae685fSJonathan Richardson 410d5ae685fSJonathan Richardson of_property_read_u32(np, "touchscreen-fuzz-x", 411d5ae685fSJonathan Richardson &priv->cfg_params.fuzz_x); 412d5ae685fSJonathan Richardson of_property_read_u32(np, "touchscreen-fuzz-y", 413d5ae685fSJonathan Richardson &priv->cfg_params.fuzz_y); 414d5ae685fSJonathan Richardson 415d5ae685fSJonathan Richardson priv->cfg_params.invert_x = 416d5ae685fSJonathan Richardson of_property_read_bool(np, "touchscreen-inverted-x"); 417d5ae685fSJonathan Richardson priv->cfg_params.invert_y = 418d5ae685fSJonathan Richardson of_property_read_bool(np, "touchscreen-inverted-y"); 419d5ae685fSJonathan Richardson 420d5ae685fSJonathan Richardson return 0; 421d5ae685fSJonathan Richardson } 422d5ae685fSJonathan Richardson 423d5ae685fSJonathan Richardson static int iproc_ts_probe(struct platform_device *pdev) 424d5ae685fSJonathan Richardson { 425d5ae685fSJonathan Richardson struct iproc_ts_priv *priv; 426d5ae685fSJonathan Richardson struct input_dev *idev; 427d5ae685fSJonathan Richardson int irq; 428d5ae685fSJonathan Richardson int error; 429d5ae685fSJonathan Richardson 430d5ae685fSJonathan Richardson priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 431d5ae685fSJonathan Richardson if (!priv) 432d5ae685fSJonathan Richardson return -ENOMEM; 433d5ae685fSJonathan Richardson 43474813cebSRaveendra Padasalagi /* touchscreen controller memory mapped regs via syscon*/ 43574813cebSRaveendra Padasalagi priv->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, 43674813cebSRaveendra Padasalagi "ts_syscon"); 43774813cebSRaveendra Padasalagi if (IS_ERR(priv->regmap)) { 43874813cebSRaveendra Padasalagi error = PTR_ERR(priv->regmap); 439d5ae685fSJonathan Richardson dev_err(&pdev->dev, "unable to map I/O memory:%d\n", error); 440d5ae685fSJonathan Richardson return error; 441d5ae685fSJonathan Richardson } 442d5ae685fSJonathan Richardson 443d5ae685fSJonathan Richardson priv->tsc_clk = devm_clk_get(&pdev->dev, "tsc_clk"); 444d5ae685fSJonathan Richardson if (IS_ERR(priv->tsc_clk)) { 445d5ae685fSJonathan Richardson error = PTR_ERR(priv->tsc_clk); 446d5ae685fSJonathan Richardson dev_err(&pdev->dev, 447d5ae685fSJonathan Richardson "failed getting clock tsc_clk: %d\n", error); 448d5ae685fSJonathan Richardson return error; 449d5ae685fSJonathan Richardson } 450d5ae685fSJonathan Richardson 451d5ae685fSJonathan Richardson priv->pdev = pdev; 452d5ae685fSJonathan Richardson error = iproc_get_tsc_config(&pdev->dev, priv); 453d5ae685fSJonathan Richardson if (error) { 454d5ae685fSJonathan Richardson dev_err(&pdev->dev, "get_tsc_config failed: %d\n", error); 455d5ae685fSJonathan Richardson return error; 456d5ae685fSJonathan Richardson } 457d5ae685fSJonathan Richardson 458d5ae685fSJonathan Richardson idev = devm_input_allocate_device(&pdev->dev); 459d5ae685fSJonathan Richardson if (!idev) { 460d5ae685fSJonathan Richardson dev_err(&pdev->dev, "failed to allocate input device\n"); 461d5ae685fSJonathan Richardson return -ENOMEM; 462d5ae685fSJonathan Richardson } 463d5ae685fSJonathan Richardson 464d5ae685fSJonathan Richardson priv->idev = idev; 465d5ae685fSJonathan Richardson priv->pen_status = PEN_UP_STATUS; 466d5ae685fSJonathan Richardson 467d5ae685fSJonathan Richardson /* Set input device info */ 468d5ae685fSJonathan Richardson idev->name = IPROC_TS_NAME; 469d5ae685fSJonathan Richardson idev->dev.parent = &pdev->dev; 470d5ae685fSJonathan Richardson 471d5ae685fSJonathan Richardson idev->id.bustype = BUS_HOST; 472d5ae685fSJonathan Richardson idev->id.vendor = SERIO_UNKNOWN; 473d5ae685fSJonathan Richardson idev->id.product = 0; 474d5ae685fSJonathan Richardson idev->id.version = 0; 475d5ae685fSJonathan Richardson 476d5ae685fSJonathan Richardson idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); 477d5ae685fSJonathan Richardson __set_bit(BTN_TOUCH, idev->keybit); 478d5ae685fSJonathan Richardson 479d5ae685fSJonathan Richardson input_set_abs_params(idev, ABS_X, X_MIN, priv->cfg_params.max_x, 480d5ae685fSJonathan Richardson priv->cfg_params.fuzz_x, 0); 481d5ae685fSJonathan Richardson input_set_abs_params(idev, ABS_Y, Y_MIN, priv->cfg_params.max_y, 482d5ae685fSJonathan Richardson priv->cfg_params.fuzz_y, 0); 483d5ae685fSJonathan Richardson 484d5ae685fSJonathan Richardson idev->open = iproc_ts_start; 485d5ae685fSJonathan Richardson idev->close = iproc_ts_stop; 486d5ae685fSJonathan Richardson 487d5ae685fSJonathan Richardson input_set_drvdata(idev, priv); 488d5ae685fSJonathan Richardson platform_set_drvdata(pdev, priv); 489d5ae685fSJonathan Richardson 490d5ae685fSJonathan Richardson /* get interrupt */ 491d5ae685fSJonathan Richardson irq = platform_get_irq(pdev, 0); 492*0bec8b7eSStephen Boyd if (irq < 0) 493d5ae685fSJonathan Richardson return irq; 494d5ae685fSJonathan Richardson 495d5ae685fSJonathan Richardson error = devm_request_irq(&pdev->dev, irq, 496d5ae685fSJonathan Richardson iproc_touchscreen_interrupt, 497d5ae685fSJonathan Richardson IRQF_SHARED, IPROC_TS_NAME, pdev); 498d5ae685fSJonathan Richardson if (error) 499d5ae685fSJonathan Richardson return error; 500d5ae685fSJonathan Richardson 501d5ae685fSJonathan Richardson error = input_register_device(priv->idev); 502d5ae685fSJonathan Richardson if (error) { 503d5ae685fSJonathan Richardson dev_err(&pdev->dev, 504d5ae685fSJonathan Richardson "failed to register input device: %d\n", error); 505d5ae685fSJonathan Richardson return error; 506d5ae685fSJonathan Richardson } 507d5ae685fSJonathan Richardson 508d5ae685fSJonathan Richardson return 0; 509d5ae685fSJonathan Richardson } 510d5ae685fSJonathan Richardson 511d5ae685fSJonathan Richardson static const struct of_device_id iproc_ts_of_match[] = { 512d5ae685fSJonathan Richardson {.compatible = "brcm,iproc-touchscreen", }, 513d5ae685fSJonathan Richardson { }, 514d5ae685fSJonathan Richardson }; 515d5ae685fSJonathan Richardson MODULE_DEVICE_TABLE(of, iproc_ts_of_match); 516d5ae685fSJonathan Richardson 517d5ae685fSJonathan Richardson static struct platform_driver iproc_ts_driver = { 518d5ae685fSJonathan Richardson .probe = iproc_ts_probe, 519d5ae685fSJonathan Richardson .driver = { 520d5ae685fSJonathan Richardson .name = IPROC_TS_NAME, 521d5ae685fSJonathan Richardson .of_match_table = of_match_ptr(iproc_ts_of_match), 522d5ae685fSJonathan Richardson }, 523d5ae685fSJonathan Richardson }; 524d5ae685fSJonathan Richardson 525d5ae685fSJonathan Richardson module_platform_driver(iproc_ts_driver); 526d5ae685fSJonathan Richardson 527d5ae685fSJonathan Richardson MODULE_DESCRIPTION("IPROC Touchscreen driver"); 528d5ae685fSJonathan Richardson MODULE_AUTHOR("Broadcom"); 529d5ae685fSJonathan Richardson MODULE_LICENSE("GPL v2"); 530