1e5163596SFabio Estevam // SPDX-License-Identifier: GPL-2.0 2e5163596SFabio Estevam // 3e5163596SFabio Estevam // Copyright (C) 2014-2015 Pengutronix, Markus Pargmann <mpa@pengutronix.de> 4e5163596SFabio Estevam // Based on driver from 2011: 5e5163596SFabio Estevam // Juergen Beisert, Pengutronix <kernel@pengutronix.de> 6e5163596SFabio Estevam // 7e5163596SFabio Estevam // This is the driver for the imx25 TCQ (Touchscreen Conversion Queue) 8e5163596SFabio Estevam // connected to the imx25 ADC. 94f7ed234SMarkus Pargmann 104f7ed234SMarkus Pargmann #include <linux/clk.h> 114f7ed234SMarkus Pargmann #include <linux/device.h> 124f7ed234SMarkus Pargmann #include <linux/input.h> 134f7ed234SMarkus Pargmann #include <linux/interrupt.h> 144f7ed234SMarkus Pargmann #include <linux/mfd/imx25-tsadc.h> 154f7ed234SMarkus Pargmann #include <linux/module.h> 164f7ed234SMarkus Pargmann #include <linux/of.h> 174f7ed234SMarkus Pargmann #include <linux/platform_device.h> 184f7ed234SMarkus Pargmann #include <linux/regmap.h> 194f7ed234SMarkus Pargmann 204f7ed234SMarkus Pargmann static const char mx25_tcq_name[] = "mx25-tcq"; 214f7ed234SMarkus Pargmann 224f7ed234SMarkus Pargmann enum mx25_tcq_mode { 234f7ed234SMarkus Pargmann MX25_TS_4WIRE, 244f7ed234SMarkus Pargmann }; 254f7ed234SMarkus Pargmann 264f7ed234SMarkus Pargmann struct mx25_tcq_priv { 274f7ed234SMarkus Pargmann struct regmap *regs; 284f7ed234SMarkus Pargmann struct regmap *core_regs; 294f7ed234SMarkus Pargmann struct input_dev *idev; 304f7ed234SMarkus Pargmann enum mx25_tcq_mode mode; 314f7ed234SMarkus Pargmann unsigned int pen_threshold; 324f7ed234SMarkus Pargmann unsigned int sample_count; 334f7ed234SMarkus Pargmann unsigned int expected_samples; 344f7ed234SMarkus Pargmann unsigned int pen_debounce; 354f7ed234SMarkus Pargmann unsigned int settling_time; 364f7ed234SMarkus Pargmann struct clk *clk; 374f7ed234SMarkus Pargmann int irq; 384f7ed234SMarkus Pargmann struct device *dev; 394f7ed234SMarkus Pargmann }; 404f7ed234SMarkus Pargmann 414f7ed234SMarkus Pargmann static struct regmap_config mx25_tcq_regconfig = { 424f7ed234SMarkus Pargmann .fast_io = true, 434f7ed234SMarkus Pargmann .max_register = 0x5c, 444f7ed234SMarkus Pargmann .reg_bits = 32, 454f7ed234SMarkus Pargmann .val_bits = 32, 464f7ed234SMarkus Pargmann .reg_stride = 4, 474f7ed234SMarkus Pargmann }; 484f7ed234SMarkus Pargmann 494f7ed234SMarkus Pargmann static const struct of_device_id mx25_tcq_ids[] = { 504f7ed234SMarkus Pargmann { .compatible = "fsl,imx25-tcq", }, 514f7ed234SMarkus Pargmann { /* Sentinel */ } 524f7ed234SMarkus Pargmann }; 53e1cb73f6SJavier Martinez Canillas MODULE_DEVICE_TABLE(of, mx25_tcq_ids); 544f7ed234SMarkus Pargmann 554f7ed234SMarkus Pargmann #define TSC_4WIRE_PRE_INDEX 0 564f7ed234SMarkus Pargmann #define TSC_4WIRE_X_INDEX 1 574f7ed234SMarkus Pargmann #define TSC_4WIRE_Y_INDEX 2 584f7ed234SMarkus Pargmann #define TSC_4WIRE_POST_INDEX 3 594f7ed234SMarkus Pargmann #define TSC_4WIRE_LEAVE 4 604f7ed234SMarkus Pargmann 614f7ed234SMarkus Pargmann #define MX25_TSC_DEF_THRESHOLD 80 624f7ed234SMarkus Pargmann #define TSC_MAX_SAMPLES 16 634f7ed234SMarkus Pargmann 644f7ed234SMarkus Pargmann #define MX25_TSC_REPEAT_WAIT 14 654f7ed234SMarkus Pargmann 664f7ed234SMarkus Pargmann enum mx25_adc_configurations { 674f7ed234SMarkus Pargmann MX25_CFG_PRECHARGE = 0, 684f7ed234SMarkus Pargmann MX25_CFG_TOUCH_DETECT, 694f7ed234SMarkus Pargmann MX25_CFG_X_MEASUREMENT, 704f7ed234SMarkus Pargmann MX25_CFG_Y_MEASUREMENT, 714f7ed234SMarkus Pargmann }; 724f7ed234SMarkus Pargmann 734f7ed234SMarkus Pargmann #define MX25_PRECHARGE_VALUE (\ 744f7ed234SMarkus Pargmann MX25_ADCQ_CFG_YPLL_OFF | \ 754f7ed234SMarkus Pargmann MX25_ADCQ_CFG_XNUR_OFF | \ 764f7ed234SMarkus Pargmann MX25_ADCQ_CFG_XPUL_HIGH | \ 774f7ed234SMarkus Pargmann MX25_ADCQ_CFG_REFP_INT | \ 784f7ed234SMarkus Pargmann MX25_ADCQ_CFG_IN_XP | \ 794f7ed234SMarkus Pargmann MX25_ADCQ_CFG_REFN_NGND2 | \ 804f7ed234SMarkus Pargmann MX25_ADCQ_CFG_IGS) 814f7ed234SMarkus Pargmann 824f7ed234SMarkus Pargmann #define MX25_TOUCH_DETECT_VALUE (\ 834f7ed234SMarkus Pargmann MX25_ADCQ_CFG_YNLR | \ 844f7ed234SMarkus Pargmann MX25_ADCQ_CFG_YPLL_OFF | \ 854f7ed234SMarkus Pargmann MX25_ADCQ_CFG_XNUR_OFF | \ 864f7ed234SMarkus Pargmann MX25_ADCQ_CFG_XPUL_OFF | \ 874f7ed234SMarkus Pargmann MX25_ADCQ_CFG_REFP_INT | \ 884f7ed234SMarkus Pargmann MX25_ADCQ_CFG_IN_XP | \ 894f7ed234SMarkus Pargmann MX25_ADCQ_CFG_REFN_NGND2 | \ 904f7ed234SMarkus Pargmann MX25_ADCQ_CFG_PENIACK) 914f7ed234SMarkus Pargmann 924f7ed234SMarkus Pargmann static void imx25_setup_queue_cfgs(struct mx25_tcq_priv *priv, 934f7ed234SMarkus Pargmann unsigned int settling_cnt) 944f7ed234SMarkus Pargmann { 954f7ed234SMarkus Pargmann u32 precharge_cfg = 964f7ed234SMarkus Pargmann MX25_PRECHARGE_VALUE | 974f7ed234SMarkus Pargmann MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt); 984f7ed234SMarkus Pargmann u32 touch_detect_cfg = 994f7ed234SMarkus Pargmann MX25_TOUCH_DETECT_VALUE | 1004f7ed234SMarkus Pargmann MX25_ADCQ_CFG_NOS(1) | 1014f7ed234SMarkus Pargmann MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt); 1024f7ed234SMarkus Pargmann 1034f7ed234SMarkus Pargmann regmap_write(priv->core_regs, MX25_TSC_TICR, precharge_cfg); 1044f7ed234SMarkus Pargmann 1054f7ed234SMarkus Pargmann /* PRECHARGE */ 1064f7ed234SMarkus Pargmann regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_PRECHARGE), 1074f7ed234SMarkus Pargmann precharge_cfg); 1084f7ed234SMarkus Pargmann 1094f7ed234SMarkus Pargmann /* TOUCH_DETECT */ 1104f7ed234SMarkus Pargmann regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_TOUCH_DETECT), 1114f7ed234SMarkus Pargmann touch_detect_cfg); 1124f7ed234SMarkus Pargmann 1134f7ed234SMarkus Pargmann /* X Measurement */ 1144f7ed234SMarkus Pargmann regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_X_MEASUREMENT), 1154f7ed234SMarkus Pargmann MX25_ADCQ_CFG_YPLL_OFF | 1164f7ed234SMarkus Pargmann MX25_ADCQ_CFG_XNUR_LOW | 1174f7ed234SMarkus Pargmann MX25_ADCQ_CFG_XPUL_HIGH | 1184f7ed234SMarkus Pargmann MX25_ADCQ_CFG_REFP_XP | 1194f7ed234SMarkus Pargmann MX25_ADCQ_CFG_IN_YP | 1204f7ed234SMarkus Pargmann MX25_ADCQ_CFG_REFN_XN | 1214f7ed234SMarkus Pargmann MX25_ADCQ_CFG_NOS(priv->sample_count) | 1224f7ed234SMarkus Pargmann MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt)); 1234f7ed234SMarkus Pargmann 1244f7ed234SMarkus Pargmann /* Y Measurement */ 1254f7ed234SMarkus Pargmann regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_Y_MEASUREMENT), 1264f7ed234SMarkus Pargmann MX25_ADCQ_CFG_YNLR | 1274f7ed234SMarkus Pargmann MX25_ADCQ_CFG_YPLL_HIGH | 1284f7ed234SMarkus Pargmann MX25_ADCQ_CFG_XNUR_OFF | 1294f7ed234SMarkus Pargmann MX25_ADCQ_CFG_XPUL_OFF | 1304f7ed234SMarkus Pargmann MX25_ADCQ_CFG_REFP_YP | 1314f7ed234SMarkus Pargmann MX25_ADCQ_CFG_IN_XP | 1324f7ed234SMarkus Pargmann MX25_ADCQ_CFG_REFN_YN | 1334f7ed234SMarkus Pargmann MX25_ADCQ_CFG_NOS(priv->sample_count) | 1344f7ed234SMarkus Pargmann MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt)); 1354f7ed234SMarkus Pargmann 1364f7ed234SMarkus Pargmann /* Enable the touch detection right now */ 1374f7ed234SMarkus Pargmann regmap_write(priv->core_regs, MX25_TSC_TICR, touch_detect_cfg | 1384f7ed234SMarkus Pargmann MX25_ADCQ_CFG_IGS); 1394f7ed234SMarkus Pargmann } 1404f7ed234SMarkus Pargmann 1414f7ed234SMarkus Pargmann static int imx25_setup_queue_4wire(struct mx25_tcq_priv *priv, 1424f7ed234SMarkus Pargmann unsigned settling_cnt, int *items) 1434f7ed234SMarkus Pargmann { 1444f7ed234SMarkus Pargmann imx25_setup_queue_cfgs(priv, settling_cnt); 1454f7ed234SMarkus Pargmann 1464f7ed234SMarkus Pargmann /* Setup the conversion queue */ 1474f7ed234SMarkus Pargmann regmap_write(priv->regs, MX25_ADCQ_ITEM_7_0, 1484f7ed234SMarkus Pargmann MX25_ADCQ_ITEM(0, MX25_CFG_PRECHARGE) | 1494f7ed234SMarkus Pargmann MX25_ADCQ_ITEM(1, MX25_CFG_TOUCH_DETECT) | 1504f7ed234SMarkus Pargmann MX25_ADCQ_ITEM(2, MX25_CFG_X_MEASUREMENT) | 1514f7ed234SMarkus Pargmann MX25_ADCQ_ITEM(3, MX25_CFG_Y_MEASUREMENT) | 1524f7ed234SMarkus Pargmann MX25_ADCQ_ITEM(4, MX25_CFG_PRECHARGE) | 1534f7ed234SMarkus Pargmann MX25_ADCQ_ITEM(5, MX25_CFG_TOUCH_DETECT)); 1544f7ed234SMarkus Pargmann 1554f7ed234SMarkus Pargmann /* 1564f7ed234SMarkus Pargmann * We measure X/Y with 'sample_count' number of samples and execute a 1574f7ed234SMarkus Pargmann * touch detection twice, with 1 sample each 1584f7ed234SMarkus Pargmann */ 1594f7ed234SMarkus Pargmann priv->expected_samples = priv->sample_count * 2 + 2; 1604f7ed234SMarkus Pargmann *items = 6; 1614f7ed234SMarkus Pargmann 1624f7ed234SMarkus Pargmann return 0; 1634f7ed234SMarkus Pargmann } 1644f7ed234SMarkus Pargmann 1654f7ed234SMarkus Pargmann static void mx25_tcq_disable_touch_irq(struct mx25_tcq_priv *priv) 1664f7ed234SMarkus Pargmann { 1674f7ed234SMarkus Pargmann regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_PDMSK, 1684f7ed234SMarkus Pargmann MX25_ADCQ_CR_PDMSK); 1694f7ed234SMarkus Pargmann } 1704f7ed234SMarkus Pargmann 1714f7ed234SMarkus Pargmann static void mx25_tcq_enable_touch_irq(struct mx25_tcq_priv *priv) 1724f7ed234SMarkus Pargmann { 1734f7ed234SMarkus Pargmann regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_PDMSK, 0); 1744f7ed234SMarkus Pargmann } 1754f7ed234SMarkus Pargmann 1764f7ed234SMarkus Pargmann static void mx25_tcq_disable_fifo_irq(struct mx25_tcq_priv *priv) 1774f7ed234SMarkus Pargmann { 1784f7ed234SMarkus Pargmann regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_FDRY_IRQ, 1794f7ed234SMarkus Pargmann MX25_ADCQ_MR_FDRY_IRQ); 1804f7ed234SMarkus Pargmann } 1814f7ed234SMarkus Pargmann 1824f7ed234SMarkus Pargmann static void mx25_tcq_enable_fifo_irq(struct mx25_tcq_priv *priv) 1834f7ed234SMarkus Pargmann { 1844f7ed234SMarkus Pargmann regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_FDRY_IRQ, 0); 1854f7ed234SMarkus Pargmann } 1864f7ed234SMarkus Pargmann 1874f7ed234SMarkus Pargmann static void mx25_tcq_force_queue_start(struct mx25_tcq_priv *priv) 1884f7ed234SMarkus Pargmann { 1894f7ed234SMarkus Pargmann regmap_update_bits(priv->regs, MX25_ADCQ_CR, 1904f7ed234SMarkus Pargmann MX25_ADCQ_CR_FQS, 1914f7ed234SMarkus Pargmann MX25_ADCQ_CR_FQS); 1924f7ed234SMarkus Pargmann } 1934f7ed234SMarkus Pargmann 1944f7ed234SMarkus Pargmann static void mx25_tcq_force_queue_stop(struct mx25_tcq_priv *priv) 1954f7ed234SMarkus Pargmann { 1964f7ed234SMarkus Pargmann regmap_update_bits(priv->regs, MX25_ADCQ_CR, 1974f7ed234SMarkus Pargmann MX25_ADCQ_CR_FQS, 0); 1984f7ed234SMarkus Pargmann } 1994f7ed234SMarkus Pargmann 2004f7ed234SMarkus Pargmann static void mx25_tcq_fifo_reset(struct mx25_tcq_priv *priv) 2014f7ed234SMarkus Pargmann { 2024f7ed234SMarkus Pargmann u32 tcqcr; 2034f7ed234SMarkus Pargmann 2044f7ed234SMarkus Pargmann regmap_read(priv->regs, MX25_ADCQ_CR, &tcqcr); 2054f7ed234SMarkus Pargmann regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FRST, 2064f7ed234SMarkus Pargmann MX25_ADCQ_CR_FRST); 2074f7ed234SMarkus Pargmann regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FRST, 0); 2084f7ed234SMarkus Pargmann regmap_write(priv->regs, MX25_ADCQ_CR, tcqcr); 2094f7ed234SMarkus Pargmann } 2104f7ed234SMarkus Pargmann 2114f7ed234SMarkus Pargmann static void mx25_tcq_re_enable_touch_detection(struct mx25_tcq_priv *priv) 2124f7ed234SMarkus Pargmann { 2134f7ed234SMarkus Pargmann /* stop the queue from looping */ 2144f7ed234SMarkus Pargmann mx25_tcq_force_queue_stop(priv); 2154f7ed234SMarkus Pargmann 2164f7ed234SMarkus Pargmann /* for a clean touch detection, preload the X plane */ 2174f7ed234SMarkus Pargmann regmap_write(priv->core_regs, MX25_TSC_TICR, MX25_PRECHARGE_VALUE); 2184f7ed234SMarkus Pargmann 2194f7ed234SMarkus Pargmann /* waste some time now to pre-load the X plate to high voltage */ 2204f7ed234SMarkus Pargmann mx25_tcq_fifo_reset(priv); 2214f7ed234SMarkus Pargmann 2224f7ed234SMarkus Pargmann /* re-enable the detection right now */ 2234f7ed234SMarkus Pargmann regmap_write(priv->core_regs, MX25_TSC_TICR, 2244f7ed234SMarkus Pargmann MX25_TOUCH_DETECT_VALUE | MX25_ADCQ_CFG_IGS); 2254f7ed234SMarkus Pargmann 2264f7ed234SMarkus Pargmann regmap_update_bits(priv->regs, MX25_ADCQ_SR, MX25_ADCQ_SR_PD, 2274f7ed234SMarkus Pargmann MX25_ADCQ_SR_PD); 2284f7ed234SMarkus Pargmann 2294f7ed234SMarkus Pargmann /* enable the pen down event to be a source for the interrupt */ 2304f7ed234SMarkus Pargmann regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_PD_IRQ, 0); 2314f7ed234SMarkus Pargmann 2324f7ed234SMarkus Pargmann /* lets fire the next IRQ if someone touches the touchscreen */ 2334f7ed234SMarkus Pargmann mx25_tcq_enable_touch_irq(priv); 2344f7ed234SMarkus Pargmann } 2354f7ed234SMarkus Pargmann 2364f7ed234SMarkus Pargmann static void mx25_tcq_create_event_for_4wire(struct mx25_tcq_priv *priv, 2374f7ed234SMarkus Pargmann u32 *sample_buf, 2384f7ed234SMarkus Pargmann unsigned int samples) 2394f7ed234SMarkus Pargmann { 2404f7ed234SMarkus Pargmann unsigned int x_pos = 0; 2414f7ed234SMarkus Pargmann unsigned int y_pos = 0; 2424f7ed234SMarkus Pargmann unsigned int touch_pre = 0; 2434f7ed234SMarkus Pargmann unsigned int touch_post = 0; 2444f7ed234SMarkus Pargmann unsigned int i; 2454f7ed234SMarkus Pargmann 2464f7ed234SMarkus Pargmann for (i = 0; i < samples; i++) { 2474f7ed234SMarkus Pargmann unsigned int index = MX25_ADCQ_FIFO_ID(sample_buf[i]); 2484f7ed234SMarkus Pargmann unsigned int val = MX25_ADCQ_FIFO_DATA(sample_buf[i]); 2494f7ed234SMarkus Pargmann 2504f7ed234SMarkus Pargmann switch (index) { 2514f7ed234SMarkus Pargmann case 1: 2524f7ed234SMarkus Pargmann touch_pre = val; 2534f7ed234SMarkus Pargmann break; 2544f7ed234SMarkus Pargmann case 2: 2554f7ed234SMarkus Pargmann x_pos = val; 2564f7ed234SMarkus Pargmann break; 2574f7ed234SMarkus Pargmann case 3: 2584f7ed234SMarkus Pargmann y_pos = val; 2594f7ed234SMarkus Pargmann break; 2604f7ed234SMarkus Pargmann case 5: 2614f7ed234SMarkus Pargmann touch_post = val; 2624f7ed234SMarkus Pargmann break; 2634f7ed234SMarkus Pargmann default: 2644f7ed234SMarkus Pargmann dev_dbg(priv->dev, "Dropped samples because of invalid index %d\n", 2654f7ed234SMarkus Pargmann index); 2664f7ed234SMarkus Pargmann return; 2674f7ed234SMarkus Pargmann } 2684f7ed234SMarkus Pargmann } 2694f7ed234SMarkus Pargmann 2704f7ed234SMarkus Pargmann if (samples != 0) { 2714f7ed234SMarkus Pargmann /* 2724f7ed234SMarkus Pargmann * only if both touch measures are below a threshold, 2734f7ed234SMarkus Pargmann * the position is valid 2744f7ed234SMarkus Pargmann */ 2754f7ed234SMarkus Pargmann if (touch_pre < priv->pen_threshold && 2764f7ed234SMarkus Pargmann touch_post < priv->pen_threshold) { 2774f7ed234SMarkus Pargmann /* valid samples, generate a report */ 2784f7ed234SMarkus Pargmann x_pos /= priv->sample_count; 2794f7ed234SMarkus Pargmann y_pos /= priv->sample_count; 2804f7ed234SMarkus Pargmann input_report_abs(priv->idev, ABS_X, x_pos); 2814f7ed234SMarkus Pargmann input_report_abs(priv->idev, ABS_Y, y_pos); 2824f7ed234SMarkus Pargmann input_report_key(priv->idev, BTN_TOUCH, 1); 2834f7ed234SMarkus Pargmann input_sync(priv->idev); 2844f7ed234SMarkus Pargmann 2854f7ed234SMarkus Pargmann /* get next sample */ 2864f7ed234SMarkus Pargmann mx25_tcq_enable_fifo_irq(priv); 2874f7ed234SMarkus Pargmann } else if (touch_pre >= priv->pen_threshold && 2884f7ed234SMarkus Pargmann touch_post >= priv->pen_threshold) { 2894f7ed234SMarkus Pargmann /* 2904f7ed234SMarkus Pargmann * if both samples are invalid, 2914f7ed234SMarkus Pargmann * generate a release report 2924f7ed234SMarkus Pargmann */ 2934f7ed234SMarkus Pargmann input_report_key(priv->idev, BTN_TOUCH, 0); 2944f7ed234SMarkus Pargmann input_sync(priv->idev); 2954f7ed234SMarkus Pargmann mx25_tcq_re_enable_touch_detection(priv); 2964f7ed234SMarkus Pargmann } else { 2974f7ed234SMarkus Pargmann /* 2984f7ed234SMarkus Pargmann * if only one of both touch measurements are 2994f7ed234SMarkus Pargmann * below the threshold, still some bouncing 3004f7ed234SMarkus Pargmann * happens. Take additional samples in this 3014f7ed234SMarkus Pargmann * case to be sure 3024f7ed234SMarkus Pargmann */ 3034f7ed234SMarkus Pargmann mx25_tcq_enable_fifo_irq(priv); 3044f7ed234SMarkus Pargmann } 3054f7ed234SMarkus Pargmann } 3064f7ed234SMarkus Pargmann } 3074f7ed234SMarkus Pargmann 3084f7ed234SMarkus Pargmann static irqreturn_t mx25_tcq_irq_thread(int irq, void *dev_id) 3094f7ed234SMarkus Pargmann { 3104f7ed234SMarkus Pargmann struct mx25_tcq_priv *priv = dev_id; 3114f7ed234SMarkus Pargmann u32 sample_buf[TSC_MAX_SAMPLES]; 3124f7ed234SMarkus Pargmann unsigned int samples; 3134f7ed234SMarkus Pargmann u32 stats; 3144f7ed234SMarkus Pargmann unsigned int i; 3154f7ed234SMarkus Pargmann 3164f7ed234SMarkus Pargmann /* 3174f7ed234SMarkus Pargmann * Check how many samples are available. We always have to read exactly 3184f7ed234SMarkus Pargmann * sample_count samples from the fifo, or a multiple of sample_count. 3194f7ed234SMarkus Pargmann * Otherwise we mixup samples into different touch events. 3204f7ed234SMarkus Pargmann */ 3214f7ed234SMarkus Pargmann regmap_read(priv->regs, MX25_ADCQ_SR, &stats); 3224f7ed234SMarkus Pargmann samples = MX25_ADCQ_SR_FDN(stats); 3234f7ed234SMarkus Pargmann samples -= samples % priv->sample_count; 3244f7ed234SMarkus Pargmann 3254f7ed234SMarkus Pargmann if (!samples) 3264f7ed234SMarkus Pargmann return IRQ_HANDLED; 3274f7ed234SMarkus Pargmann 3284f7ed234SMarkus Pargmann for (i = 0; i != samples; ++i) 3294f7ed234SMarkus Pargmann regmap_read(priv->regs, MX25_ADCQ_FIFO, &sample_buf[i]); 3304f7ed234SMarkus Pargmann 3314f7ed234SMarkus Pargmann mx25_tcq_create_event_for_4wire(priv, sample_buf, samples); 3324f7ed234SMarkus Pargmann 3334f7ed234SMarkus Pargmann return IRQ_HANDLED; 3344f7ed234SMarkus Pargmann } 3354f7ed234SMarkus Pargmann 3364f7ed234SMarkus Pargmann static irqreturn_t mx25_tcq_irq(int irq, void *dev_id) 3374f7ed234SMarkus Pargmann { 3384f7ed234SMarkus Pargmann struct mx25_tcq_priv *priv = dev_id; 3394f7ed234SMarkus Pargmann u32 stat; 3404f7ed234SMarkus Pargmann int ret = IRQ_HANDLED; 3414f7ed234SMarkus Pargmann 3424f7ed234SMarkus Pargmann regmap_read(priv->regs, MX25_ADCQ_SR, &stat); 3434f7ed234SMarkus Pargmann 3444f7ed234SMarkus Pargmann if (stat & (MX25_ADCQ_SR_FRR | MX25_ADCQ_SR_FUR | MX25_ADCQ_SR_FOR)) 3454f7ed234SMarkus Pargmann mx25_tcq_re_enable_touch_detection(priv); 3464f7ed234SMarkus Pargmann 3474f7ed234SMarkus Pargmann if (stat & MX25_ADCQ_SR_PD) { 3484f7ed234SMarkus Pargmann mx25_tcq_disable_touch_irq(priv); 3494f7ed234SMarkus Pargmann mx25_tcq_force_queue_start(priv); 3504f7ed234SMarkus Pargmann mx25_tcq_enable_fifo_irq(priv); 3514f7ed234SMarkus Pargmann } 3524f7ed234SMarkus Pargmann 3534f7ed234SMarkus Pargmann if (stat & MX25_ADCQ_SR_FDRY) { 3544f7ed234SMarkus Pargmann mx25_tcq_disable_fifo_irq(priv); 3554f7ed234SMarkus Pargmann ret = IRQ_WAKE_THREAD; 3564f7ed234SMarkus Pargmann } 3574f7ed234SMarkus Pargmann 3584f7ed234SMarkus Pargmann regmap_update_bits(priv->regs, MX25_ADCQ_SR, MX25_ADCQ_SR_FRR | 3594f7ed234SMarkus Pargmann MX25_ADCQ_SR_FUR | MX25_ADCQ_SR_FOR | 3604f7ed234SMarkus Pargmann MX25_ADCQ_SR_PD, 3614f7ed234SMarkus Pargmann MX25_ADCQ_SR_FRR | MX25_ADCQ_SR_FUR | 3624f7ed234SMarkus Pargmann MX25_ADCQ_SR_FOR | MX25_ADCQ_SR_PD); 3634f7ed234SMarkus Pargmann 3644f7ed234SMarkus Pargmann return ret; 3654f7ed234SMarkus Pargmann } 3664f7ed234SMarkus Pargmann 3674f7ed234SMarkus Pargmann /* configure the state machine for a 4-wire touchscreen */ 3684f7ed234SMarkus Pargmann static int mx25_tcq_init(struct mx25_tcq_priv *priv) 3694f7ed234SMarkus Pargmann { 3704f7ed234SMarkus Pargmann u32 tgcr; 3714f7ed234SMarkus Pargmann unsigned int ipg_div; 3724f7ed234SMarkus Pargmann unsigned int adc_period; 3734f7ed234SMarkus Pargmann unsigned int debounce_cnt; 3744f7ed234SMarkus Pargmann unsigned int settling_cnt; 3754f7ed234SMarkus Pargmann int itemct; 3764f7ed234SMarkus Pargmann int error; 3774f7ed234SMarkus Pargmann 3784f7ed234SMarkus Pargmann regmap_read(priv->core_regs, MX25_TSC_TGCR, &tgcr); 3794f7ed234SMarkus Pargmann ipg_div = max_t(unsigned int, 4, MX25_TGCR_GET_ADCCLK(tgcr)); 3804f7ed234SMarkus Pargmann adc_period = USEC_PER_SEC * ipg_div * 2 + 2; 3814f7ed234SMarkus Pargmann adc_period /= clk_get_rate(priv->clk) / 1000 + 1; 3824f7ed234SMarkus Pargmann debounce_cnt = DIV_ROUND_UP(priv->pen_debounce, adc_period * 8) - 1; 3834f7ed234SMarkus Pargmann settling_cnt = DIV_ROUND_UP(priv->settling_time, adc_period * 8) - 1; 3844f7ed234SMarkus Pargmann 3854f7ed234SMarkus Pargmann /* Reset */ 3864f7ed234SMarkus Pargmann regmap_write(priv->regs, MX25_ADCQ_CR, 3874f7ed234SMarkus Pargmann MX25_ADCQ_CR_QRST | MX25_ADCQ_CR_FRST); 3884f7ed234SMarkus Pargmann regmap_update_bits(priv->regs, MX25_ADCQ_CR, 3894f7ed234SMarkus Pargmann MX25_ADCQ_CR_QRST | MX25_ADCQ_CR_FRST, 0); 3904f7ed234SMarkus Pargmann 3914f7ed234SMarkus Pargmann /* up to 128 * 8 ADC clocks are possible */ 3924f7ed234SMarkus Pargmann if (debounce_cnt > 127) 3934f7ed234SMarkus Pargmann debounce_cnt = 127; 3944f7ed234SMarkus Pargmann 3954f7ed234SMarkus Pargmann /* up to 255 * 8 ADC clocks are possible */ 3964f7ed234SMarkus Pargmann if (settling_cnt > 255) 3974f7ed234SMarkus Pargmann settling_cnt = 255; 3984f7ed234SMarkus Pargmann 3994f7ed234SMarkus Pargmann error = imx25_setup_queue_4wire(priv, settling_cnt, &itemct); 4004f7ed234SMarkus Pargmann if (error) 4014f7ed234SMarkus Pargmann return error; 4024f7ed234SMarkus Pargmann 4034f7ed234SMarkus Pargmann regmap_update_bits(priv->regs, MX25_ADCQ_CR, 4044f7ed234SMarkus Pargmann MX25_ADCQ_CR_LITEMID_MASK | MX25_ADCQ_CR_WMRK_MASK, 4054f7ed234SMarkus Pargmann MX25_ADCQ_CR_LITEMID(itemct - 1) | 4064f7ed234SMarkus Pargmann MX25_ADCQ_CR_WMRK(priv->expected_samples - 1)); 4074f7ed234SMarkus Pargmann 4084f7ed234SMarkus Pargmann /* setup debounce count */ 4094f7ed234SMarkus Pargmann regmap_update_bits(priv->core_regs, MX25_TSC_TGCR, 4104f7ed234SMarkus Pargmann MX25_TGCR_PDBTIME_MASK, 4114f7ed234SMarkus Pargmann MX25_TGCR_PDBTIME(debounce_cnt)); 4124f7ed234SMarkus Pargmann 4134f7ed234SMarkus Pargmann /* enable debounce */ 4144f7ed234SMarkus Pargmann regmap_update_bits(priv->core_regs, MX25_TSC_TGCR, MX25_TGCR_PDBEN, 4154f7ed234SMarkus Pargmann MX25_TGCR_PDBEN); 4164f7ed234SMarkus Pargmann regmap_update_bits(priv->core_regs, MX25_TSC_TGCR, MX25_TGCR_PDEN, 4174f7ed234SMarkus Pargmann MX25_TGCR_PDEN); 4184f7ed234SMarkus Pargmann 4194f7ed234SMarkus Pargmann /* enable the engine on demand */ 4204f7ed234SMarkus Pargmann regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_QSM_MASK, 4214f7ed234SMarkus Pargmann MX25_ADCQ_CR_QSM_FQS); 4224f7ed234SMarkus Pargmann 4234f7ed234SMarkus Pargmann /* Enable repeat and repeat wait */ 4244f7ed234SMarkus Pargmann regmap_update_bits(priv->regs, MX25_ADCQ_CR, 4254f7ed234SMarkus Pargmann MX25_ADCQ_CR_RPT | MX25_ADCQ_CR_RWAIT_MASK, 4264f7ed234SMarkus Pargmann MX25_ADCQ_CR_RPT | 4274f7ed234SMarkus Pargmann MX25_ADCQ_CR_RWAIT(MX25_TSC_REPEAT_WAIT)); 4284f7ed234SMarkus Pargmann 4294f7ed234SMarkus Pargmann return 0; 4304f7ed234SMarkus Pargmann } 4314f7ed234SMarkus Pargmann 4324f7ed234SMarkus Pargmann static int mx25_tcq_parse_dt(struct platform_device *pdev, 4334f7ed234SMarkus Pargmann struct mx25_tcq_priv *priv) 4344f7ed234SMarkus Pargmann { 4354f7ed234SMarkus Pargmann struct device_node *np = pdev->dev.of_node; 4364f7ed234SMarkus Pargmann u32 wires; 4374f7ed234SMarkus Pargmann int error; 4384f7ed234SMarkus Pargmann 4394f7ed234SMarkus Pargmann /* Setup defaults */ 4404f7ed234SMarkus Pargmann priv->pen_threshold = 500; 4414f7ed234SMarkus Pargmann priv->sample_count = 3; 4424f7ed234SMarkus Pargmann priv->pen_debounce = 1000000; 4434f7ed234SMarkus Pargmann priv->settling_time = 250000; 4444f7ed234SMarkus Pargmann 4454f7ed234SMarkus Pargmann error = of_property_read_u32(np, "fsl,wires", &wires); 4464f7ed234SMarkus Pargmann if (error) { 4474f7ed234SMarkus Pargmann dev_err(&pdev->dev, "Failed to find fsl,wires properties\n"); 4484f7ed234SMarkus Pargmann return error; 4494f7ed234SMarkus Pargmann } 4504f7ed234SMarkus Pargmann 4514f7ed234SMarkus Pargmann if (wires == 4) { 4524f7ed234SMarkus Pargmann priv->mode = MX25_TS_4WIRE; 4534f7ed234SMarkus Pargmann } else { 4544f7ed234SMarkus Pargmann dev_err(&pdev->dev, "%u-wire mode not supported\n", wires); 4554f7ed234SMarkus Pargmann return -EINVAL; 4564f7ed234SMarkus Pargmann } 4574f7ed234SMarkus Pargmann 4584f7ed234SMarkus Pargmann /* These are optional, we don't care about the return values */ 4594f7ed234SMarkus Pargmann of_property_read_u32(np, "fsl,pen-threshold", &priv->pen_threshold); 4604f7ed234SMarkus Pargmann of_property_read_u32(np, "fsl,settling-time-ns", &priv->settling_time); 4614f7ed234SMarkus Pargmann of_property_read_u32(np, "fsl,pen-debounce-ns", &priv->pen_debounce); 4624f7ed234SMarkus Pargmann 4634f7ed234SMarkus Pargmann return 0; 4644f7ed234SMarkus Pargmann } 4654f7ed234SMarkus Pargmann 4664f7ed234SMarkus Pargmann static int mx25_tcq_open(struct input_dev *idev) 4674f7ed234SMarkus Pargmann { 4684f7ed234SMarkus Pargmann struct device *dev = &idev->dev; 4694f7ed234SMarkus Pargmann struct mx25_tcq_priv *priv = dev_get_drvdata(dev); 4704f7ed234SMarkus Pargmann int error; 4714f7ed234SMarkus Pargmann 4724f7ed234SMarkus Pargmann error = clk_prepare_enable(priv->clk); 4734f7ed234SMarkus Pargmann if (error) { 4744f7ed234SMarkus Pargmann dev_err(dev, "Failed to enable ipg clock\n"); 4754f7ed234SMarkus Pargmann return error; 4764f7ed234SMarkus Pargmann } 4774f7ed234SMarkus Pargmann 4784f7ed234SMarkus Pargmann error = mx25_tcq_init(priv); 4794f7ed234SMarkus Pargmann if (error) { 4804f7ed234SMarkus Pargmann dev_err(dev, "Failed to init tcq\n"); 4814f7ed234SMarkus Pargmann clk_disable_unprepare(priv->clk); 4824f7ed234SMarkus Pargmann return error; 4834f7ed234SMarkus Pargmann } 4844f7ed234SMarkus Pargmann 4854f7ed234SMarkus Pargmann mx25_tcq_re_enable_touch_detection(priv); 4864f7ed234SMarkus Pargmann 4874f7ed234SMarkus Pargmann return 0; 4884f7ed234SMarkus Pargmann } 4894f7ed234SMarkus Pargmann 4904f7ed234SMarkus Pargmann static void mx25_tcq_close(struct input_dev *idev) 4914f7ed234SMarkus Pargmann { 4924f7ed234SMarkus Pargmann struct mx25_tcq_priv *priv = input_get_drvdata(idev); 4934f7ed234SMarkus Pargmann 4944f7ed234SMarkus Pargmann mx25_tcq_force_queue_stop(priv); 4954f7ed234SMarkus Pargmann mx25_tcq_disable_touch_irq(priv); 4964f7ed234SMarkus Pargmann mx25_tcq_disable_fifo_irq(priv); 4974f7ed234SMarkus Pargmann clk_disable_unprepare(priv->clk); 4984f7ed234SMarkus Pargmann } 4994f7ed234SMarkus Pargmann 5004f7ed234SMarkus Pargmann static int mx25_tcq_probe(struct platform_device *pdev) 5014f7ed234SMarkus Pargmann { 5024f7ed234SMarkus Pargmann struct device *dev = &pdev->dev; 5034f7ed234SMarkus Pargmann struct input_dev *idev; 5044f7ed234SMarkus Pargmann struct mx25_tcq_priv *priv; 505d7ddf154SGuenter Roeck struct mx25_tsadc *tsadc = dev_get_drvdata(dev->parent); 5064f7ed234SMarkus Pargmann void __iomem *mem; 5074f7ed234SMarkus Pargmann int error; 5084f7ed234SMarkus Pargmann 5094f7ed234SMarkus Pargmann priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 5104f7ed234SMarkus Pargmann if (!priv) 5114f7ed234SMarkus Pargmann return -ENOMEM; 5124f7ed234SMarkus Pargmann priv->dev = dev; 5134f7ed234SMarkus Pargmann 514*9d41cbe2SMukesh Ojha mem = devm_platform_ioremap_resource(pdev, 0); 5154f7ed234SMarkus Pargmann if (IS_ERR(mem)) 5164f7ed234SMarkus Pargmann return PTR_ERR(mem); 5174f7ed234SMarkus Pargmann 5184f7ed234SMarkus Pargmann error = mx25_tcq_parse_dt(pdev, priv); 5194f7ed234SMarkus Pargmann if (error) 5204f7ed234SMarkus Pargmann return error; 5214f7ed234SMarkus Pargmann 5224f7ed234SMarkus Pargmann priv->regs = devm_regmap_init_mmio(dev, mem, &mx25_tcq_regconfig); 5234f7ed234SMarkus Pargmann if (IS_ERR(priv->regs)) { 5244f7ed234SMarkus Pargmann dev_err(dev, "Failed to initialize regmap\n"); 5254f7ed234SMarkus Pargmann return PTR_ERR(priv->regs); 5264f7ed234SMarkus Pargmann } 5274f7ed234SMarkus Pargmann 5284f7ed234SMarkus Pargmann priv->irq = platform_get_irq(pdev, 0); 5294f7ed234SMarkus Pargmann if (priv->irq <= 0) { 5304f7ed234SMarkus Pargmann dev_err(dev, "Failed to get IRQ\n"); 5314f7ed234SMarkus Pargmann return priv->irq; 5324f7ed234SMarkus Pargmann } 5334f7ed234SMarkus Pargmann 5344f7ed234SMarkus Pargmann idev = devm_input_allocate_device(dev); 5354f7ed234SMarkus Pargmann if (!idev) { 5364f7ed234SMarkus Pargmann dev_err(dev, "Failed to allocate input device\n"); 5374f7ed234SMarkus Pargmann return -ENOMEM; 5384f7ed234SMarkus Pargmann } 5394f7ed234SMarkus Pargmann 5404f7ed234SMarkus Pargmann idev->name = mx25_tcq_name; 5414f7ed234SMarkus Pargmann input_set_capability(idev, EV_KEY, BTN_TOUCH); 5424f7ed234SMarkus Pargmann input_set_abs_params(idev, ABS_X, 0, 0xfff, 0, 0); 5434f7ed234SMarkus Pargmann input_set_abs_params(idev, ABS_Y, 0, 0xfff, 0, 0); 5444f7ed234SMarkus Pargmann 5454f7ed234SMarkus Pargmann idev->id.bustype = BUS_HOST; 5464f7ed234SMarkus Pargmann idev->open = mx25_tcq_open; 5474f7ed234SMarkus Pargmann idev->close = mx25_tcq_close; 5484f7ed234SMarkus Pargmann 5494f7ed234SMarkus Pargmann priv->idev = idev; 5504f7ed234SMarkus Pargmann input_set_drvdata(idev, priv); 5514f7ed234SMarkus Pargmann 5524f7ed234SMarkus Pargmann priv->core_regs = tsadc->regs; 5534f7ed234SMarkus Pargmann if (!priv->core_regs) 5544f7ed234SMarkus Pargmann return -EINVAL; 5554f7ed234SMarkus Pargmann 5564f7ed234SMarkus Pargmann priv->clk = tsadc->clk; 5574f7ed234SMarkus Pargmann if (!priv->clk) 5584f7ed234SMarkus Pargmann return -EINVAL; 5594f7ed234SMarkus Pargmann 5604f7ed234SMarkus Pargmann platform_set_drvdata(pdev, priv); 5614f7ed234SMarkus Pargmann 5624f7ed234SMarkus Pargmann error = devm_request_threaded_irq(dev, priv->irq, mx25_tcq_irq, 5634f7ed234SMarkus Pargmann mx25_tcq_irq_thread, 0, pdev->name, 5644f7ed234SMarkus Pargmann priv); 5654f7ed234SMarkus Pargmann if (error) { 5664f7ed234SMarkus Pargmann dev_err(dev, "Failed requesting IRQ\n"); 5674f7ed234SMarkus Pargmann return error; 5684f7ed234SMarkus Pargmann } 5694f7ed234SMarkus Pargmann 5704f7ed234SMarkus Pargmann error = input_register_device(idev); 5714f7ed234SMarkus Pargmann if (error) { 5724f7ed234SMarkus Pargmann dev_err(dev, "Failed to register input device\n"); 5734f7ed234SMarkus Pargmann return error; 5744f7ed234SMarkus Pargmann } 5754f7ed234SMarkus Pargmann 5764f7ed234SMarkus Pargmann return 0; 5774f7ed234SMarkus Pargmann } 5784f7ed234SMarkus Pargmann 5794f7ed234SMarkus Pargmann static struct platform_driver mx25_tcq_driver = { 5804f7ed234SMarkus Pargmann .driver = { 5814f7ed234SMarkus Pargmann .name = "mx25-tcq", 5824f7ed234SMarkus Pargmann .of_match_table = mx25_tcq_ids, 5834f7ed234SMarkus Pargmann }, 5844f7ed234SMarkus Pargmann .probe = mx25_tcq_probe, 5854f7ed234SMarkus Pargmann }; 5864f7ed234SMarkus Pargmann module_platform_driver(mx25_tcq_driver); 5874f7ed234SMarkus Pargmann 5884f7ed234SMarkus Pargmann MODULE_DESCRIPTION("TS input driver for Freescale mx25"); 5894f7ed234SMarkus Pargmann MODULE_AUTHOR("Markus Pargmann <mpa@pengutronix.de>"); 5904f7ed234SMarkus Pargmann MODULE_LICENSE("GPL v2"); 591