162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci* Copyright (C) 2015 Broadcom Corporation 462306a36Sopenharmony_ci* 562306a36Sopenharmony_ci*/ 662306a36Sopenharmony_ci#include <linux/module.h> 762306a36Sopenharmony_ci#include <linux/init.h> 862306a36Sopenharmony_ci#include <linux/input.h> 962306a36Sopenharmony_ci#include <linux/delay.h> 1062306a36Sopenharmony_ci#include <linux/interrupt.h> 1162306a36Sopenharmony_ci#include <linux/keyboard.h> 1262306a36Sopenharmony_ci#include <linux/platform_device.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci#include <linux/of.h> 1562306a36Sopenharmony_ci#include <asm/irq.h> 1662306a36Sopenharmony_ci#include <linux/io.h> 1762306a36Sopenharmony_ci#include <linux/clk.h> 1862306a36Sopenharmony_ci#include <linux/serio.h> 1962306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 2062306a36Sopenharmony_ci#include <linux/regmap.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define IPROC_TS_NAME "iproc-ts" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define PEN_DOWN_STATUS 1 2562306a36Sopenharmony_ci#define PEN_UP_STATUS 0 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define X_MIN 0 2862306a36Sopenharmony_ci#define Y_MIN 0 2962306a36Sopenharmony_ci#define X_MAX 0xFFF 3062306a36Sopenharmony_ci#define Y_MAX 0xFFF 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/* Value given by controller for invalid coordinate. */ 3362306a36Sopenharmony_ci#define INVALID_COORD 0xFFFFFFFF 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* Register offsets */ 3662306a36Sopenharmony_ci#define REGCTL1 0x00 3762306a36Sopenharmony_ci#define REGCTL2 0x04 3862306a36Sopenharmony_ci#define INTERRUPT_THRES 0x08 3962306a36Sopenharmony_ci#define INTERRUPT_MASK 0x0c 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#define INTERRUPT_STATUS 0x10 4262306a36Sopenharmony_ci#define CONTROLLER_STATUS 0x14 4362306a36Sopenharmony_ci#define FIFO_DATA 0x18 4462306a36Sopenharmony_ci#define FIFO_DATA_X_Y_MASK 0xFFFF 4562306a36Sopenharmony_ci#define ANALOG_CONTROL 0x1c 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#define AUX_DATA 0x20 4862306a36Sopenharmony_ci#define DEBOUNCE_CNTR_STAT 0x24 4962306a36Sopenharmony_ci#define SCAN_CNTR_STAT 0x28 5062306a36Sopenharmony_ci#define REM_CNTR_STAT 0x2c 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#define SETTLING_TIMER_STAT 0x30 5362306a36Sopenharmony_ci#define SPARE_REG 0x34 5462306a36Sopenharmony_ci#define SOFT_BYPASS_CONTROL 0x38 5562306a36Sopenharmony_ci#define SOFT_BYPASS_DATA 0x3c 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* Bit values for INTERRUPT_MASK and INTERRUPT_STATUS regs */ 5962306a36Sopenharmony_ci#define TS_PEN_INTR_MASK BIT(0) 6062306a36Sopenharmony_ci#define TS_FIFO_INTR_MASK BIT(2) 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci/* Bit values for CONTROLLER_STATUS reg1 */ 6362306a36Sopenharmony_ci#define TS_PEN_DOWN BIT(0) 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/* Shift values for control reg1 */ 6662306a36Sopenharmony_ci#define SCANNING_PERIOD_SHIFT 24 6762306a36Sopenharmony_ci#define DEBOUNCE_TIMEOUT_SHIFT 16 6862306a36Sopenharmony_ci#define SETTLING_TIMEOUT_SHIFT 8 6962306a36Sopenharmony_ci#define TOUCH_TIMEOUT_SHIFT 0 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci/* Shift values for coordinates from fifo */ 7262306a36Sopenharmony_ci#define X_COORD_SHIFT 0 7362306a36Sopenharmony_ci#define Y_COORD_SHIFT 16 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci/* Bit values for REGCTL2 */ 7662306a36Sopenharmony_ci#define TS_CONTROLLER_EN_BIT BIT(16) 7762306a36Sopenharmony_ci#define TS_CONTROLLER_AVGDATA_SHIFT 8 7862306a36Sopenharmony_ci#define TS_CONTROLLER_AVGDATA_MASK (0x7 << TS_CONTROLLER_AVGDATA_SHIFT) 7962306a36Sopenharmony_ci#define TS_CONTROLLER_PWR_LDO BIT(5) 8062306a36Sopenharmony_ci#define TS_CONTROLLER_PWR_ADC BIT(4) 8162306a36Sopenharmony_ci#define TS_CONTROLLER_PWR_BGP BIT(3) 8262306a36Sopenharmony_ci#define TS_CONTROLLER_PWR_TS BIT(2) 8362306a36Sopenharmony_ci#define TS_WIRE_MODE_BIT BIT(1) 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci#define dbg_reg(dev, priv, reg) \ 8662306a36Sopenharmony_cido { \ 8762306a36Sopenharmony_ci u32 val; \ 8862306a36Sopenharmony_ci regmap_read(priv->regmap, reg, &val); \ 8962306a36Sopenharmony_ci dev_dbg(dev, "%20s= 0x%08x\n", #reg, val); \ 9062306a36Sopenharmony_ci} while (0) 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistruct tsc_param { 9362306a36Sopenharmony_ci /* Each step is 1024 us. Valid 1-256 */ 9462306a36Sopenharmony_ci u32 scanning_period; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci /* Each step is 512 us. Valid 0-255 */ 9762306a36Sopenharmony_ci u32 debounce_timeout; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci /* 10062306a36Sopenharmony_ci * The settling duration (in ms) is the amount of time the tsc 10162306a36Sopenharmony_ci * waits to allow the voltage to settle after turning on the 10262306a36Sopenharmony_ci * drivers in detection mode. Valid values: 0-11 10362306a36Sopenharmony_ci * 0 = 0.008 ms 10462306a36Sopenharmony_ci * 1 = 0.01 ms 10562306a36Sopenharmony_ci * 2 = 0.02 ms 10662306a36Sopenharmony_ci * 3 = 0.04 ms 10762306a36Sopenharmony_ci * 4 = 0.08 ms 10862306a36Sopenharmony_ci * 5 = 0.16 ms 10962306a36Sopenharmony_ci * 6 = 0.32 ms 11062306a36Sopenharmony_ci * 7 = 0.64 ms 11162306a36Sopenharmony_ci * 8 = 1.28 ms 11262306a36Sopenharmony_ci * 9 = 2.56 ms 11362306a36Sopenharmony_ci * 10 = 5.12 ms 11462306a36Sopenharmony_ci * 11 = 10.24 ms 11562306a36Sopenharmony_ci */ 11662306a36Sopenharmony_ci u32 settling_timeout; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci /* touch timeout in sample counts */ 11962306a36Sopenharmony_ci u32 touch_timeout; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci /* 12262306a36Sopenharmony_ci * Number of data samples which are averaged before a final data point 12362306a36Sopenharmony_ci * is placed into the FIFO 12462306a36Sopenharmony_ci */ 12562306a36Sopenharmony_ci u32 average_data; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci /* FIFO threshold */ 12862306a36Sopenharmony_ci u32 fifo_threshold; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci /* Optional standard touchscreen properties. */ 13162306a36Sopenharmony_ci u32 max_x; 13262306a36Sopenharmony_ci u32 max_y; 13362306a36Sopenharmony_ci u32 fuzz_x; 13462306a36Sopenharmony_ci u32 fuzz_y; 13562306a36Sopenharmony_ci bool invert_x; 13662306a36Sopenharmony_ci bool invert_y; 13762306a36Sopenharmony_ci}; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistruct iproc_ts_priv { 14062306a36Sopenharmony_ci struct platform_device *pdev; 14162306a36Sopenharmony_ci struct input_dev *idev; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci struct regmap *regmap; 14462306a36Sopenharmony_ci struct clk *tsc_clk; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci int pen_status; 14762306a36Sopenharmony_ci struct tsc_param cfg_params; 14862306a36Sopenharmony_ci}; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci/* 15162306a36Sopenharmony_ci * Set default values the same as hardware reset values 15262306a36Sopenharmony_ci * except for fifo_threshold with is set to 1. 15362306a36Sopenharmony_ci */ 15462306a36Sopenharmony_cistatic const struct tsc_param iproc_default_config = { 15562306a36Sopenharmony_ci .scanning_period = 0x5, /* 1 to 256 */ 15662306a36Sopenharmony_ci .debounce_timeout = 0x28, /* 0 to 255 */ 15762306a36Sopenharmony_ci .settling_timeout = 0x7, /* 0 to 11 */ 15862306a36Sopenharmony_ci .touch_timeout = 0xa, /* 0 to 255 */ 15962306a36Sopenharmony_ci .average_data = 5, /* entry 5 = 32 pts */ 16062306a36Sopenharmony_ci .fifo_threshold = 1, /* 0 to 31 */ 16162306a36Sopenharmony_ci .max_x = X_MAX, 16262306a36Sopenharmony_ci .max_y = Y_MAX, 16362306a36Sopenharmony_ci}; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic void ts_reg_dump(struct iproc_ts_priv *priv) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci struct device *dev = &priv->pdev->dev; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci dbg_reg(dev, priv, REGCTL1); 17062306a36Sopenharmony_ci dbg_reg(dev, priv, REGCTL2); 17162306a36Sopenharmony_ci dbg_reg(dev, priv, INTERRUPT_THRES); 17262306a36Sopenharmony_ci dbg_reg(dev, priv, INTERRUPT_MASK); 17362306a36Sopenharmony_ci dbg_reg(dev, priv, INTERRUPT_STATUS); 17462306a36Sopenharmony_ci dbg_reg(dev, priv, CONTROLLER_STATUS); 17562306a36Sopenharmony_ci dbg_reg(dev, priv, FIFO_DATA); 17662306a36Sopenharmony_ci dbg_reg(dev, priv, ANALOG_CONTROL); 17762306a36Sopenharmony_ci dbg_reg(dev, priv, AUX_DATA); 17862306a36Sopenharmony_ci dbg_reg(dev, priv, DEBOUNCE_CNTR_STAT); 17962306a36Sopenharmony_ci dbg_reg(dev, priv, SCAN_CNTR_STAT); 18062306a36Sopenharmony_ci dbg_reg(dev, priv, REM_CNTR_STAT); 18162306a36Sopenharmony_ci dbg_reg(dev, priv, SETTLING_TIMER_STAT); 18262306a36Sopenharmony_ci dbg_reg(dev, priv, SPARE_REG); 18362306a36Sopenharmony_ci dbg_reg(dev, priv, SOFT_BYPASS_CONTROL); 18462306a36Sopenharmony_ci dbg_reg(dev, priv, SOFT_BYPASS_DATA); 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic irqreturn_t iproc_touchscreen_interrupt(int irq, void *data) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci struct platform_device *pdev = data; 19062306a36Sopenharmony_ci struct iproc_ts_priv *priv = platform_get_drvdata(pdev); 19162306a36Sopenharmony_ci u32 intr_status; 19262306a36Sopenharmony_ci u32 raw_coordinate; 19362306a36Sopenharmony_ci u16 x; 19462306a36Sopenharmony_ci u16 y; 19562306a36Sopenharmony_ci int i; 19662306a36Sopenharmony_ci bool needs_sync = false; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci regmap_read(priv->regmap, INTERRUPT_STATUS, &intr_status); 19962306a36Sopenharmony_ci intr_status &= TS_PEN_INTR_MASK | TS_FIFO_INTR_MASK; 20062306a36Sopenharmony_ci if (intr_status == 0) 20162306a36Sopenharmony_ci return IRQ_NONE; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci /* Clear all interrupt status bits, write-1-clear */ 20462306a36Sopenharmony_ci regmap_write(priv->regmap, INTERRUPT_STATUS, intr_status); 20562306a36Sopenharmony_ci /* Pen up/down */ 20662306a36Sopenharmony_ci if (intr_status & TS_PEN_INTR_MASK) { 20762306a36Sopenharmony_ci regmap_read(priv->regmap, CONTROLLER_STATUS, &priv->pen_status); 20862306a36Sopenharmony_ci if (priv->pen_status & TS_PEN_DOWN) 20962306a36Sopenharmony_ci priv->pen_status = PEN_DOWN_STATUS; 21062306a36Sopenharmony_ci else 21162306a36Sopenharmony_ci priv->pen_status = PEN_UP_STATUS; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci input_report_key(priv->idev, BTN_TOUCH, priv->pen_status); 21462306a36Sopenharmony_ci needs_sync = true; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci dev_dbg(&priv->pdev->dev, 21762306a36Sopenharmony_ci "pen up-down (%d)\n", priv->pen_status); 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci /* coordinates in FIFO exceed the theshold */ 22162306a36Sopenharmony_ci if (intr_status & TS_FIFO_INTR_MASK) { 22262306a36Sopenharmony_ci for (i = 0; i < priv->cfg_params.fifo_threshold; i++) { 22362306a36Sopenharmony_ci regmap_read(priv->regmap, FIFO_DATA, &raw_coordinate); 22462306a36Sopenharmony_ci if (raw_coordinate == INVALID_COORD) 22562306a36Sopenharmony_ci continue; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci /* 22862306a36Sopenharmony_ci * The x and y coordinate are 16 bits each 22962306a36Sopenharmony_ci * with the x in the lower 16 bits and y in the 23062306a36Sopenharmony_ci * upper 16 bits. 23162306a36Sopenharmony_ci */ 23262306a36Sopenharmony_ci x = (raw_coordinate >> X_COORD_SHIFT) & 23362306a36Sopenharmony_ci FIFO_DATA_X_Y_MASK; 23462306a36Sopenharmony_ci y = (raw_coordinate >> Y_COORD_SHIFT) & 23562306a36Sopenharmony_ci FIFO_DATA_X_Y_MASK; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci /* We only want to retain the 12 msb of the 16 */ 23862306a36Sopenharmony_ci x = (x >> 4) & 0x0FFF; 23962306a36Sopenharmony_ci y = (y >> 4) & 0x0FFF; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci /* Adjust x y according to LCD tsc mount angle. */ 24262306a36Sopenharmony_ci if (priv->cfg_params.invert_x) 24362306a36Sopenharmony_ci x = priv->cfg_params.max_x - x; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci if (priv->cfg_params.invert_y) 24662306a36Sopenharmony_ci y = priv->cfg_params.max_y - y; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci input_report_abs(priv->idev, ABS_X, x); 24962306a36Sopenharmony_ci input_report_abs(priv->idev, ABS_Y, y); 25062306a36Sopenharmony_ci needs_sync = true; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci dev_dbg(&priv->pdev->dev, "xy (0x%x 0x%x)\n", x, y); 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci if (needs_sync) 25762306a36Sopenharmony_ci input_sync(priv->idev); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci return IRQ_HANDLED; 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_cistatic int iproc_ts_start(struct input_dev *idev) 26362306a36Sopenharmony_ci{ 26462306a36Sopenharmony_ci u32 val; 26562306a36Sopenharmony_ci u32 mask; 26662306a36Sopenharmony_ci int error; 26762306a36Sopenharmony_ci struct iproc_ts_priv *priv = input_get_drvdata(idev); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci /* Enable clock */ 27062306a36Sopenharmony_ci error = clk_prepare_enable(priv->tsc_clk); 27162306a36Sopenharmony_ci if (error) { 27262306a36Sopenharmony_ci dev_err(&priv->pdev->dev, "%s clk_prepare_enable failed %d\n", 27362306a36Sopenharmony_ci __func__, error); 27462306a36Sopenharmony_ci return error; 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci /* 27862306a36Sopenharmony_ci * Interrupt is generated when: 27962306a36Sopenharmony_ci * FIFO reaches the int_th value, and pen event(up/down) 28062306a36Sopenharmony_ci */ 28162306a36Sopenharmony_ci val = TS_PEN_INTR_MASK | TS_FIFO_INTR_MASK; 28262306a36Sopenharmony_ci regmap_update_bits(priv->regmap, INTERRUPT_MASK, val, val); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci val = priv->cfg_params.fifo_threshold; 28562306a36Sopenharmony_ci regmap_write(priv->regmap, INTERRUPT_THRES, val); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci /* Initialize control reg1 */ 28862306a36Sopenharmony_ci val = 0; 28962306a36Sopenharmony_ci val |= priv->cfg_params.scanning_period << SCANNING_PERIOD_SHIFT; 29062306a36Sopenharmony_ci val |= priv->cfg_params.debounce_timeout << DEBOUNCE_TIMEOUT_SHIFT; 29162306a36Sopenharmony_ci val |= priv->cfg_params.settling_timeout << SETTLING_TIMEOUT_SHIFT; 29262306a36Sopenharmony_ci val |= priv->cfg_params.touch_timeout << TOUCH_TIMEOUT_SHIFT; 29362306a36Sopenharmony_ci regmap_write(priv->regmap, REGCTL1, val); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci /* Try to clear all interrupt status */ 29662306a36Sopenharmony_ci val = TS_FIFO_INTR_MASK | TS_PEN_INTR_MASK; 29762306a36Sopenharmony_ci regmap_update_bits(priv->regmap, INTERRUPT_STATUS, val, val); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci /* Initialize control reg2 */ 30062306a36Sopenharmony_ci val = TS_CONTROLLER_EN_BIT | TS_WIRE_MODE_BIT; 30162306a36Sopenharmony_ci val |= priv->cfg_params.average_data << TS_CONTROLLER_AVGDATA_SHIFT; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci mask = (TS_CONTROLLER_AVGDATA_MASK); 30462306a36Sopenharmony_ci mask |= (TS_CONTROLLER_PWR_LDO | /* PWR up LDO */ 30562306a36Sopenharmony_ci TS_CONTROLLER_PWR_ADC | /* PWR up ADC */ 30662306a36Sopenharmony_ci TS_CONTROLLER_PWR_BGP | /* PWR up BGP */ 30762306a36Sopenharmony_ci TS_CONTROLLER_PWR_TS); /* PWR up TS */ 30862306a36Sopenharmony_ci mask |= val; 30962306a36Sopenharmony_ci regmap_update_bits(priv->regmap, REGCTL2, mask, val); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci ts_reg_dump(priv); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci return 0; 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_cistatic void iproc_ts_stop(struct input_dev *dev) 31762306a36Sopenharmony_ci{ 31862306a36Sopenharmony_ci u32 val; 31962306a36Sopenharmony_ci struct iproc_ts_priv *priv = input_get_drvdata(dev); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci /* 32262306a36Sopenharmony_ci * Disable FIFO int_th and pen event(up/down)Interrupts only 32362306a36Sopenharmony_ci * as the interrupt mask register is shared between ADC, TS and 32462306a36Sopenharmony_ci * flextimer. 32562306a36Sopenharmony_ci */ 32662306a36Sopenharmony_ci val = TS_PEN_INTR_MASK | TS_FIFO_INTR_MASK; 32762306a36Sopenharmony_ci regmap_update_bits(priv->regmap, INTERRUPT_MASK, val, 0); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci /* Only power down touch screen controller */ 33062306a36Sopenharmony_ci val = TS_CONTROLLER_PWR_TS; 33162306a36Sopenharmony_ci regmap_update_bits(priv->regmap, REGCTL2, val, val); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci clk_disable(priv->tsc_clk); 33462306a36Sopenharmony_ci} 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_cistatic int iproc_get_tsc_config(struct device *dev, struct iproc_ts_priv *priv) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci struct device_node *np = dev->of_node; 33962306a36Sopenharmony_ci u32 val; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci priv->cfg_params = iproc_default_config; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci if (!np) 34462306a36Sopenharmony_ci return 0; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci if (of_property_read_u32(np, "scanning_period", &val) >= 0) { 34762306a36Sopenharmony_ci if (val < 1 || val > 256) { 34862306a36Sopenharmony_ci dev_err(dev, "scanning_period (%u) must be [1-256]\n", 34962306a36Sopenharmony_ci val); 35062306a36Sopenharmony_ci return -EINVAL; 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci priv->cfg_params.scanning_period = val; 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci if (of_property_read_u32(np, "debounce_timeout", &val) >= 0) { 35662306a36Sopenharmony_ci if (val > 255) { 35762306a36Sopenharmony_ci dev_err(dev, "debounce_timeout (%u) must be [0-255]\n", 35862306a36Sopenharmony_ci val); 35962306a36Sopenharmony_ci return -EINVAL; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci priv->cfg_params.debounce_timeout = val; 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci if (of_property_read_u32(np, "settling_timeout", &val) >= 0) { 36562306a36Sopenharmony_ci if (val > 11) { 36662306a36Sopenharmony_ci dev_err(dev, "settling_timeout (%u) must be [0-11]\n", 36762306a36Sopenharmony_ci val); 36862306a36Sopenharmony_ci return -EINVAL; 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci priv->cfg_params.settling_timeout = val; 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci if (of_property_read_u32(np, "touch_timeout", &val) >= 0) { 37462306a36Sopenharmony_ci if (val > 255) { 37562306a36Sopenharmony_ci dev_err(dev, "touch_timeout (%u) must be [0-255]\n", 37662306a36Sopenharmony_ci val); 37762306a36Sopenharmony_ci return -EINVAL; 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci priv->cfg_params.touch_timeout = val; 38062306a36Sopenharmony_ci } 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci if (of_property_read_u32(np, "average_data", &val) >= 0) { 38362306a36Sopenharmony_ci if (val > 8) { 38462306a36Sopenharmony_ci dev_err(dev, "average_data (%u) must be [0-8]\n", val); 38562306a36Sopenharmony_ci return -EINVAL; 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci priv->cfg_params.average_data = val; 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci if (of_property_read_u32(np, "fifo_threshold", &val) >= 0) { 39162306a36Sopenharmony_ci if (val > 31) { 39262306a36Sopenharmony_ci dev_err(dev, "fifo_threshold (%u)) must be [0-31]\n", 39362306a36Sopenharmony_ci val); 39462306a36Sopenharmony_ci return -EINVAL; 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci priv->cfg_params.fifo_threshold = val; 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci /* Parse optional properties. */ 40062306a36Sopenharmony_ci of_property_read_u32(np, "touchscreen-size-x", &priv->cfg_params.max_x); 40162306a36Sopenharmony_ci of_property_read_u32(np, "touchscreen-size-y", &priv->cfg_params.max_y); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci of_property_read_u32(np, "touchscreen-fuzz-x", 40462306a36Sopenharmony_ci &priv->cfg_params.fuzz_x); 40562306a36Sopenharmony_ci of_property_read_u32(np, "touchscreen-fuzz-y", 40662306a36Sopenharmony_ci &priv->cfg_params.fuzz_y); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci priv->cfg_params.invert_x = 40962306a36Sopenharmony_ci of_property_read_bool(np, "touchscreen-inverted-x"); 41062306a36Sopenharmony_ci priv->cfg_params.invert_y = 41162306a36Sopenharmony_ci of_property_read_bool(np, "touchscreen-inverted-y"); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci return 0; 41462306a36Sopenharmony_ci} 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_cistatic int iproc_ts_probe(struct platform_device *pdev) 41762306a36Sopenharmony_ci{ 41862306a36Sopenharmony_ci struct iproc_ts_priv *priv; 41962306a36Sopenharmony_ci struct input_dev *idev; 42062306a36Sopenharmony_ci int irq; 42162306a36Sopenharmony_ci int error; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 42462306a36Sopenharmony_ci if (!priv) 42562306a36Sopenharmony_ci return -ENOMEM; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci /* touchscreen controller memory mapped regs via syscon*/ 42862306a36Sopenharmony_ci priv->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, 42962306a36Sopenharmony_ci "ts_syscon"); 43062306a36Sopenharmony_ci if (IS_ERR(priv->regmap)) { 43162306a36Sopenharmony_ci error = PTR_ERR(priv->regmap); 43262306a36Sopenharmony_ci dev_err(&pdev->dev, "unable to map I/O memory:%d\n", error); 43362306a36Sopenharmony_ci return error; 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci priv->tsc_clk = devm_clk_get(&pdev->dev, "tsc_clk"); 43762306a36Sopenharmony_ci if (IS_ERR(priv->tsc_clk)) { 43862306a36Sopenharmony_ci error = PTR_ERR(priv->tsc_clk); 43962306a36Sopenharmony_ci dev_err(&pdev->dev, 44062306a36Sopenharmony_ci "failed getting clock tsc_clk: %d\n", error); 44162306a36Sopenharmony_ci return error; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci priv->pdev = pdev; 44562306a36Sopenharmony_ci error = iproc_get_tsc_config(&pdev->dev, priv); 44662306a36Sopenharmony_ci if (error) { 44762306a36Sopenharmony_ci dev_err(&pdev->dev, "get_tsc_config failed: %d\n", error); 44862306a36Sopenharmony_ci return error; 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci idev = devm_input_allocate_device(&pdev->dev); 45262306a36Sopenharmony_ci if (!idev) { 45362306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to allocate input device\n"); 45462306a36Sopenharmony_ci return -ENOMEM; 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci priv->idev = idev; 45862306a36Sopenharmony_ci priv->pen_status = PEN_UP_STATUS; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci /* Set input device info */ 46162306a36Sopenharmony_ci idev->name = IPROC_TS_NAME; 46262306a36Sopenharmony_ci idev->dev.parent = &pdev->dev; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci idev->id.bustype = BUS_HOST; 46562306a36Sopenharmony_ci idev->id.vendor = SERIO_UNKNOWN; 46662306a36Sopenharmony_ci idev->id.product = 0; 46762306a36Sopenharmony_ci idev->id.version = 0; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); 47062306a36Sopenharmony_ci __set_bit(BTN_TOUCH, idev->keybit); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci input_set_abs_params(idev, ABS_X, X_MIN, priv->cfg_params.max_x, 47362306a36Sopenharmony_ci priv->cfg_params.fuzz_x, 0); 47462306a36Sopenharmony_ci input_set_abs_params(idev, ABS_Y, Y_MIN, priv->cfg_params.max_y, 47562306a36Sopenharmony_ci priv->cfg_params.fuzz_y, 0); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci idev->open = iproc_ts_start; 47862306a36Sopenharmony_ci idev->close = iproc_ts_stop; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci input_set_drvdata(idev, priv); 48162306a36Sopenharmony_ci platform_set_drvdata(pdev, priv); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci /* get interrupt */ 48462306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 48562306a36Sopenharmony_ci if (irq < 0) 48662306a36Sopenharmony_ci return irq; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci error = devm_request_irq(&pdev->dev, irq, 48962306a36Sopenharmony_ci iproc_touchscreen_interrupt, 49062306a36Sopenharmony_ci IRQF_SHARED, IPROC_TS_NAME, pdev); 49162306a36Sopenharmony_ci if (error) 49262306a36Sopenharmony_ci return error; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci error = input_register_device(priv->idev); 49562306a36Sopenharmony_ci if (error) { 49662306a36Sopenharmony_ci dev_err(&pdev->dev, 49762306a36Sopenharmony_ci "failed to register input device: %d\n", error); 49862306a36Sopenharmony_ci return error; 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci return 0; 50262306a36Sopenharmony_ci} 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_cistatic const struct of_device_id iproc_ts_of_match[] = { 50562306a36Sopenharmony_ci {.compatible = "brcm,iproc-touchscreen", }, 50662306a36Sopenharmony_ci { }, 50762306a36Sopenharmony_ci}; 50862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, iproc_ts_of_match); 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_cistatic struct platform_driver iproc_ts_driver = { 51162306a36Sopenharmony_ci .probe = iproc_ts_probe, 51262306a36Sopenharmony_ci .driver = { 51362306a36Sopenharmony_ci .name = IPROC_TS_NAME, 51462306a36Sopenharmony_ci .of_match_table = iproc_ts_of_match, 51562306a36Sopenharmony_ci }, 51662306a36Sopenharmony_ci}; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_cimodule_platform_driver(iproc_ts_driver); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ciMODULE_DESCRIPTION("IPROC Touchscreen driver"); 52162306a36Sopenharmony_ciMODULE_AUTHOR("Broadcom"); 52262306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 523