15ae37699SFabio Estevam // SPDX-License-Identifier: GPL-2.0 25ae37699SFabio Estevam // 35ae37699SFabio Estevam // Freescale i.MX6UL touchscreen controller driver 45ae37699SFabio Estevam // 55ae37699SFabio Estevam // Copyright (C) 2015 Freescale Semiconductor, Inc. 69a436d52SHaibo Chen 79a436d52SHaibo Chen #include <linux/errno.h> 89a436d52SHaibo Chen #include <linux/kernel.h> 99a436d52SHaibo Chen #include <linux/module.h> 109a436d52SHaibo Chen #include <linux/gpio/consumer.h> 119a436d52SHaibo Chen #include <linux/input.h> 129a436d52SHaibo Chen #include <linux/slab.h> 139a436d52SHaibo Chen #include <linux/completion.h> 149a436d52SHaibo Chen #include <linux/delay.h> 159a436d52SHaibo Chen #include <linux/of.h> 169a436d52SHaibo Chen #include <linux/interrupt.h> 179a436d52SHaibo Chen #include <linux/platform_device.h> 189a436d52SHaibo Chen #include <linux/clk.h> 199a436d52SHaibo Chen #include <linux/io.h> 20f43d3ec3SGuy Shapiro #include <linux/log2.h> 219a436d52SHaibo Chen 229a436d52SHaibo Chen /* ADC configuration registers field define */ 239a436d52SHaibo Chen #define ADC_AIEN (0x1 << 7) 249a436d52SHaibo Chen #define ADC_CONV_DISABLE 0x1F 25031bfed2SGuy Shapiro #define ADC_AVGE (0x1 << 5) 269a436d52SHaibo Chen #define ADC_CAL (0x1 << 7) 279a436d52SHaibo Chen #define ADC_CALF 0x2 289a436d52SHaibo Chen #define ADC_12BIT_MODE (0x2 << 2) 2970f5a294SHaibo Chen #define ADC_CONV_MODE_MASK (0x3 << 2) 309a436d52SHaibo Chen #define ADC_IPG_CLK 0x00 3170f5a294SHaibo Chen #define ADC_INPUT_CLK_MASK 0x3 329a436d52SHaibo Chen #define ADC_CLK_DIV_8 (0x03 << 5) 3370f5a294SHaibo Chen #define ADC_CLK_DIV_MASK (0x3 << 5) 349a436d52SHaibo Chen #define ADC_SHORT_SAMPLE_MODE (0x0 << 4) 3570f5a294SHaibo Chen #define ADC_SAMPLE_MODE_MASK (0x1 << 4) 369a436d52SHaibo Chen #define ADC_HARDWARE_TRIGGER (0x1 << 13) 37031bfed2SGuy Shapiro #define ADC_AVGS_SHIFT 14 3870f5a294SHaibo Chen #define ADC_AVGS_MASK (0x3 << 14) 399a436d52SHaibo Chen #define SELECT_CHANNEL_4 0x04 409a436d52SHaibo Chen #define SELECT_CHANNEL_1 0x01 419a436d52SHaibo Chen #define DISABLE_CONVERSION_INT (0x0 << 7) 429a436d52SHaibo Chen 439a436d52SHaibo Chen /* ADC registers */ 449a436d52SHaibo Chen #define REG_ADC_HC0 0x00 459a436d52SHaibo Chen #define REG_ADC_HC1 0x04 469a436d52SHaibo Chen #define REG_ADC_HC2 0x08 479a436d52SHaibo Chen #define REG_ADC_HC3 0x0C 489a436d52SHaibo Chen #define REG_ADC_HC4 0x10 499a436d52SHaibo Chen #define REG_ADC_HS 0x14 509a436d52SHaibo Chen #define REG_ADC_R0 0x18 519a436d52SHaibo Chen #define REG_ADC_CFG 0x2C 529a436d52SHaibo Chen #define REG_ADC_GC 0x30 539a436d52SHaibo Chen #define REG_ADC_GS 0x34 549a436d52SHaibo Chen 559a436d52SHaibo Chen #define ADC_TIMEOUT msecs_to_jiffies(100) 569a436d52SHaibo Chen 579a436d52SHaibo Chen /* TSC registers */ 589a436d52SHaibo Chen #define REG_TSC_BASIC_SETING 0x00 599a436d52SHaibo Chen #define REG_TSC_PRE_CHARGE_TIME 0x10 609a436d52SHaibo Chen #define REG_TSC_FLOW_CONTROL 0x20 619a436d52SHaibo Chen #define REG_TSC_MEASURE_VALUE 0x30 629a436d52SHaibo Chen #define REG_TSC_INT_EN 0x40 639a436d52SHaibo Chen #define REG_TSC_INT_SIG_EN 0x50 649a436d52SHaibo Chen #define REG_TSC_INT_STATUS 0x60 659a436d52SHaibo Chen #define REG_TSC_DEBUG_MODE 0x70 669a436d52SHaibo Chen #define REG_TSC_DEBUG_MODE2 0x80 679a436d52SHaibo Chen 689a436d52SHaibo Chen /* TSC configuration registers field define */ 699a436d52SHaibo Chen #define DETECT_4_WIRE_MODE (0x0 << 4) 709a436d52SHaibo Chen #define AUTO_MEASURE 0x1 719a436d52SHaibo Chen #define MEASURE_SIGNAL 0x1 729a436d52SHaibo Chen #define DETECT_SIGNAL (0x1 << 4) 739a436d52SHaibo Chen #define VALID_SIGNAL (0x1 << 8) 749a436d52SHaibo Chen #define MEASURE_INT_EN 0x1 759a436d52SHaibo Chen #define MEASURE_SIG_EN 0x1 769a436d52SHaibo Chen #define VALID_SIG_EN (0x1 << 8) 779a436d52SHaibo Chen #define DE_GLITCH_2 (0x2 << 29) 789a436d52SHaibo Chen #define START_SENSE (0x1 << 12) 799a436d52SHaibo Chen #define TSC_DISABLE (0x1 << 16) 809a436d52SHaibo Chen #define DETECT_MODE 0x2 819a436d52SHaibo Chen 829a436d52SHaibo Chen struct imx6ul_tsc { 839a436d52SHaibo Chen struct device *dev; 849a436d52SHaibo Chen struct input_dev *input; 859a436d52SHaibo Chen void __iomem *tsc_regs; 869a436d52SHaibo Chen void __iomem *adc_regs; 879a436d52SHaibo Chen struct clk *tsc_clk; 889a436d52SHaibo Chen struct clk *adc_clk; 899a436d52SHaibo Chen struct gpio_desc *xnur_gpio; 909a436d52SHaibo Chen 91accbcea3SGuy Shapiro u32 measure_delay_time; 92accbcea3SGuy Shapiro u32 pre_charge_time; 93f43d3ec3SGuy Shapiro bool average_enable; 94f43d3ec3SGuy Shapiro u32 average_select; 959a436d52SHaibo Chen 969a436d52SHaibo Chen struct completion completion; 979a436d52SHaibo Chen }; 989a436d52SHaibo Chen 999a436d52SHaibo Chen /* 1009a436d52SHaibo Chen * TSC module need ADC to get the measure value. So 1019a436d52SHaibo Chen * before config TSC, we should initialize ADC module. 1029a436d52SHaibo Chen */ 1036cc527b0SFabio Estevam static int imx6ul_adc_init(struct imx6ul_tsc *tsc) 1049a436d52SHaibo Chen { 105accbcea3SGuy Shapiro u32 adc_hc = 0; 106accbcea3SGuy Shapiro u32 adc_gc; 107accbcea3SGuy Shapiro u32 adc_gs; 108accbcea3SGuy Shapiro u32 adc_cfg; 109accbcea3SGuy Shapiro unsigned long timeout; 1109a436d52SHaibo Chen 1119a436d52SHaibo Chen reinit_completion(&tsc->completion); 1129a436d52SHaibo Chen 1139a436d52SHaibo Chen adc_cfg = readl(tsc->adc_regs + REG_ADC_CFG); 11470f5a294SHaibo Chen adc_cfg &= ~(ADC_CONV_MODE_MASK | ADC_INPUT_CLK_MASK); 1159a436d52SHaibo Chen adc_cfg |= ADC_12BIT_MODE | ADC_IPG_CLK; 11670f5a294SHaibo Chen adc_cfg &= ~(ADC_CLK_DIV_MASK | ADC_SAMPLE_MODE_MASK); 1179a436d52SHaibo Chen adc_cfg |= ADC_CLK_DIV_8 | ADC_SHORT_SAMPLE_MODE; 118f43d3ec3SGuy Shapiro if (tsc->average_enable) { 11970f5a294SHaibo Chen adc_cfg &= ~ADC_AVGS_MASK; 120f43d3ec3SGuy Shapiro adc_cfg |= (tsc->average_select) << ADC_AVGS_SHIFT; 12170f5a294SHaibo Chen } 1229a436d52SHaibo Chen adc_cfg &= ~ADC_HARDWARE_TRIGGER; 1239a436d52SHaibo Chen writel(adc_cfg, tsc->adc_regs + REG_ADC_CFG); 1249a436d52SHaibo Chen 1259a436d52SHaibo Chen /* enable calibration interrupt */ 1269a436d52SHaibo Chen adc_hc |= ADC_AIEN; 1279a436d52SHaibo Chen adc_hc |= ADC_CONV_DISABLE; 1289a436d52SHaibo Chen writel(adc_hc, tsc->adc_regs + REG_ADC_HC0); 1299a436d52SHaibo Chen 1309a436d52SHaibo Chen /* start ADC calibration */ 1319a436d52SHaibo Chen adc_gc = readl(tsc->adc_regs + REG_ADC_GC); 1329a436d52SHaibo Chen adc_gc |= ADC_CAL; 133f43d3ec3SGuy Shapiro if (tsc->average_enable) 134031bfed2SGuy Shapiro adc_gc |= ADC_AVGE; 1359a436d52SHaibo Chen writel(adc_gc, tsc->adc_regs + REG_ADC_GC); 1369a436d52SHaibo Chen 1379a436d52SHaibo Chen timeout = wait_for_completion_timeout 1389a436d52SHaibo Chen (&tsc->completion, ADC_TIMEOUT); 1396cc527b0SFabio Estevam if (timeout == 0) { 1409a436d52SHaibo Chen dev_err(tsc->dev, "Timeout for adc calibration\n"); 1416cc527b0SFabio Estevam return -ETIMEDOUT; 1426cc527b0SFabio Estevam } 1439a436d52SHaibo Chen 1449a436d52SHaibo Chen adc_gs = readl(tsc->adc_regs + REG_ADC_GS); 1456cc527b0SFabio Estevam if (adc_gs & ADC_CALF) { 1469a436d52SHaibo Chen dev_err(tsc->dev, "ADC calibration failed\n"); 1476cc527b0SFabio Estevam return -EINVAL; 1486cc527b0SFabio Estevam } 1499a436d52SHaibo Chen 1509a436d52SHaibo Chen /* TSC need the ADC work in hardware trigger */ 1519a436d52SHaibo Chen adc_cfg = readl(tsc->adc_regs + REG_ADC_CFG); 1529a436d52SHaibo Chen adc_cfg |= ADC_HARDWARE_TRIGGER; 1539a436d52SHaibo Chen writel(adc_cfg, tsc->adc_regs + REG_ADC_CFG); 1546cc527b0SFabio Estevam 1556cc527b0SFabio Estevam return 0; 1569a436d52SHaibo Chen } 1579a436d52SHaibo Chen 1589a436d52SHaibo Chen /* 1599a436d52SHaibo Chen * This is a TSC workaround. Currently TSC misconnect two 1609a436d52SHaibo Chen * ADC channels, this function remap channel configure for 1619a436d52SHaibo Chen * hardware trigger. 1629a436d52SHaibo Chen */ 1639a436d52SHaibo Chen static void imx6ul_tsc_channel_config(struct imx6ul_tsc *tsc) 1649a436d52SHaibo Chen { 165accbcea3SGuy Shapiro u32 adc_hc0, adc_hc1, adc_hc2, adc_hc3, adc_hc4; 1669a436d52SHaibo Chen 1679a436d52SHaibo Chen adc_hc0 = DISABLE_CONVERSION_INT; 1689a436d52SHaibo Chen writel(adc_hc0, tsc->adc_regs + REG_ADC_HC0); 1699a436d52SHaibo Chen 1709a436d52SHaibo Chen adc_hc1 = DISABLE_CONVERSION_INT | SELECT_CHANNEL_4; 1719a436d52SHaibo Chen writel(adc_hc1, tsc->adc_regs + REG_ADC_HC1); 1729a436d52SHaibo Chen 1739a436d52SHaibo Chen adc_hc2 = DISABLE_CONVERSION_INT; 1749a436d52SHaibo Chen writel(adc_hc2, tsc->adc_regs + REG_ADC_HC2); 1759a436d52SHaibo Chen 1769a436d52SHaibo Chen adc_hc3 = DISABLE_CONVERSION_INT | SELECT_CHANNEL_1; 1779a436d52SHaibo Chen writel(adc_hc3, tsc->adc_regs + REG_ADC_HC3); 1789a436d52SHaibo Chen 1799a436d52SHaibo Chen adc_hc4 = DISABLE_CONVERSION_INT; 1809a436d52SHaibo Chen writel(adc_hc4, tsc->adc_regs + REG_ADC_HC4); 1819a436d52SHaibo Chen } 1829a436d52SHaibo Chen 1839a436d52SHaibo Chen /* 1849a436d52SHaibo Chen * TSC setting, confige the pre-charge time and measure delay time. 1859a436d52SHaibo Chen * different touch screen may need different pre-charge time and 1869a436d52SHaibo Chen * measure delay time. 1879a436d52SHaibo Chen */ 1889a436d52SHaibo Chen static void imx6ul_tsc_set(struct imx6ul_tsc *tsc) 1899a436d52SHaibo Chen { 190accbcea3SGuy Shapiro u32 basic_setting = 0; 191accbcea3SGuy Shapiro u32 start; 1929a436d52SHaibo Chen 1939a436d52SHaibo Chen basic_setting |= tsc->measure_delay_time << 8; 1949a436d52SHaibo Chen basic_setting |= DETECT_4_WIRE_MODE | AUTO_MEASURE; 1959a436d52SHaibo Chen writel(basic_setting, tsc->tsc_regs + REG_TSC_BASIC_SETING); 1969a436d52SHaibo Chen 1979a436d52SHaibo Chen writel(DE_GLITCH_2, tsc->tsc_regs + REG_TSC_DEBUG_MODE2); 1989a436d52SHaibo Chen 1999a436d52SHaibo Chen writel(tsc->pre_charge_time, tsc->tsc_regs + REG_TSC_PRE_CHARGE_TIME); 2009a436d52SHaibo Chen writel(MEASURE_INT_EN, tsc->tsc_regs + REG_TSC_INT_EN); 2019a436d52SHaibo Chen writel(MEASURE_SIG_EN | VALID_SIG_EN, 2029a436d52SHaibo Chen tsc->tsc_regs + REG_TSC_INT_SIG_EN); 2039a436d52SHaibo Chen 2049a436d52SHaibo Chen /* start sense detection */ 2059a436d52SHaibo Chen start = readl(tsc->tsc_regs + REG_TSC_FLOW_CONTROL); 2069a436d52SHaibo Chen start |= START_SENSE; 2079a436d52SHaibo Chen start &= ~TSC_DISABLE; 2089a436d52SHaibo Chen writel(start, tsc->tsc_regs + REG_TSC_FLOW_CONTROL); 2099a436d52SHaibo Chen } 2109a436d52SHaibo Chen 2116cc527b0SFabio Estevam static int imx6ul_tsc_init(struct imx6ul_tsc *tsc) 2129a436d52SHaibo Chen { 2136cc527b0SFabio Estevam int err; 2146cc527b0SFabio Estevam 2156cc527b0SFabio Estevam err = imx6ul_adc_init(tsc); 2166cc527b0SFabio Estevam if (err) 2176cc527b0SFabio Estevam return err; 2189a436d52SHaibo Chen imx6ul_tsc_channel_config(tsc); 2199a436d52SHaibo Chen imx6ul_tsc_set(tsc); 2206cc527b0SFabio Estevam 2216cc527b0SFabio Estevam return 0; 2229a436d52SHaibo Chen } 2239a436d52SHaibo Chen 2249a436d52SHaibo Chen static void imx6ul_tsc_disable(struct imx6ul_tsc *tsc) 2259a436d52SHaibo Chen { 226accbcea3SGuy Shapiro u32 tsc_flow; 227accbcea3SGuy Shapiro u32 adc_cfg; 2289a436d52SHaibo Chen 2299a436d52SHaibo Chen /* TSC controller enters to idle status */ 2309a436d52SHaibo Chen tsc_flow = readl(tsc->tsc_regs + REG_TSC_FLOW_CONTROL); 2319a436d52SHaibo Chen tsc_flow |= TSC_DISABLE; 2329a436d52SHaibo Chen writel(tsc_flow, tsc->tsc_regs + REG_TSC_FLOW_CONTROL); 2339a436d52SHaibo Chen 2349a436d52SHaibo Chen /* ADC controller enters to stop mode */ 2359a436d52SHaibo Chen adc_cfg = readl(tsc->adc_regs + REG_ADC_HC0); 2369a436d52SHaibo Chen adc_cfg |= ADC_CONV_DISABLE; 2379a436d52SHaibo Chen writel(adc_cfg, tsc->adc_regs + REG_ADC_HC0); 2389a436d52SHaibo Chen } 2399a436d52SHaibo Chen 2409a436d52SHaibo Chen /* Delay some time (max 2ms), wait the pre-charge done. */ 2419a436d52SHaibo Chen static bool tsc_wait_detect_mode(struct imx6ul_tsc *tsc) 2429a436d52SHaibo Chen { 2439a436d52SHaibo Chen unsigned long timeout = jiffies + msecs_to_jiffies(2); 244accbcea3SGuy Shapiro u32 state_machine; 245accbcea3SGuy Shapiro u32 debug_mode2; 2469a436d52SHaibo Chen 2479a436d52SHaibo Chen do { 2489a436d52SHaibo Chen if (time_after(jiffies, timeout)) 2499a436d52SHaibo Chen return false; 2509a436d52SHaibo Chen 2519a436d52SHaibo Chen usleep_range(200, 400); 2529a436d52SHaibo Chen debug_mode2 = readl(tsc->tsc_regs + REG_TSC_DEBUG_MODE2); 2539a436d52SHaibo Chen state_machine = (debug_mode2 >> 20) & 0x7; 2549a436d52SHaibo Chen } while (state_machine != DETECT_MODE); 2559a436d52SHaibo Chen 2569a436d52SHaibo Chen usleep_range(200, 400); 2579a436d52SHaibo Chen return true; 2589a436d52SHaibo Chen } 2599a436d52SHaibo Chen 2609a436d52SHaibo Chen static irqreturn_t tsc_irq_fn(int irq, void *dev_id) 2619a436d52SHaibo Chen { 2629a436d52SHaibo Chen struct imx6ul_tsc *tsc = dev_id; 263accbcea3SGuy Shapiro u32 status; 264accbcea3SGuy Shapiro u32 value; 265accbcea3SGuy Shapiro u32 x, y; 266accbcea3SGuy Shapiro u32 start; 2679a436d52SHaibo Chen 2689a436d52SHaibo Chen status = readl(tsc->tsc_regs + REG_TSC_INT_STATUS); 2699a436d52SHaibo Chen 2709a436d52SHaibo Chen /* write 1 to clear the bit measure-signal */ 2719a436d52SHaibo Chen writel(MEASURE_SIGNAL | DETECT_SIGNAL, 2729a436d52SHaibo Chen tsc->tsc_regs + REG_TSC_INT_STATUS); 2739a436d52SHaibo Chen 2749a436d52SHaibo Chen /* It's a HW self-clean bit. Set this bit and start sense detection */ 2759a436d52SHaibo Chen start = readl(tsc->tsc_regs + REG_TSC_FLOW_CONTROL); 2769a436d52SHaibo Chen start |= START_SENSE; 2779a436d52SHaibo Chen writel(start, tsc->tsc_regs + REG_TSC_FLOW_CONTROL); 2789a436d52SHaibo Chen 2799a436d52SHaibo Chen if (status & MEASURE_SIGNAL) { 2809a436d52SHaibo Chen value = readl(tsc->tsc_regs + REG_TSC_MEASURE_VALUE); 2819a436d52SHaibo Chen x = (value >> 16) & 0x0fff; 2829a436d52SHaibo Chen y = value & 0x0fff; 2839a436d52SHaibo Chen 2849a436d52SHaibo Chen /* 2859a436d52SHaibo Chen * In detect mode, we can get the xnur gpio value, 2869a436d52SHaibo Chen * otherwise assume contact is stiull active. 2879a436d52SHaibo Chen */ 2889a436d52SHaibo Chen if (!tsc_wait_detect_mode(tsc) || 2899a436d52SHaibo Chen gpiod_get_value_cansleep(tsc->xnur_gpio)) { 2909a436d52SHaibo Chen input_report_key(tsc->input, BTN_TOUCH, 1); 2919a436d52SHaibo Chen input_report_abs(tsc->input, ABS_X, x); 2929a436d52SHaibo Chen input_report_abs(tsc->input, ABS_Y, y); 2939a436d52SHaibo Chen } else { 2949a436d52SHaibo Chen input_report_key(tsc->input, BTN_TOUCH, 0); 2959a436d52SHaibo Chen } 2969a436d52SHaibo Chen 2979a436d52SHaibo Chen input_sync(tsc->input); 2989a436d52SHaibo Chen } 2999a436d52SHaibo Chen 3009a436d52SHaibo Chen return IRQ_HANDLED; 3019a436d52SHaibo Chen } 3029a436d52SHaibo Chen 3039a436d52SHaibo Chen static irqreturn_t adc_irq_fn(int irq, void *dev_id) 3049a436d52SHaibo Chen { 3059a436d52SHaibo Chen struct imx6ul_tsc *tsc = dev_id; 306accbcea3SGuy Shapiro u32 coco; 3079a436d52SHaibo Chen 3089a436d52SHaibo Chen coco = readl(tsc->adc_regs + REG_ADC_HS); 3099a436d52SHaibo Chen if (coco & 0x01) { 310cd536aa5SLee Jones readl(tsc->adc_regs + REG_ADC_R0); 3119a436d52SHaibo Chen complete(&tsc->completion); 3129a436d52SHaibo Chen } 3139a436d52SHaibo Chen 3149a436d52SHaibo Chen return IRQ_HANDLED; 3159a436d52SHaibo Chen } 3169a436d52SHaibo Chen 317925145f9SDmitry Torokhov static int imx6ul_tsc_start(struct imx6ul_tsc *tsc) 3189a436d52SHaibo Chen { 3199a436d52SHaibo Chen int err; 3209a436d52SHaibo Chen 3219a436d52SHaibo Chen err = clk_prepare_enable(tsc->adc_clk); 3229a436d52SHaibo Chen if (err) { 3239a436d52SHaibo Chen dev_err(tsc->dev, 3249a436d52SHaibo Chen "Could not prepare or enable the adc clock: %d\n", 3259a436d52SHaibo Chen err); 3269a436d52SHaibo Chen return err; 3279a436d52SHaibo Chen } 3289a436d52SHaibo Chen 3299a436d52SHaibo Chen err = clk_prepare_enable(tsc->tsc_clk); 3309a436d52SHaibo Chen if (err) { 3319a436d52SHaibo Chen dev_err(tsc->dev, 3329a436d52SHaibo Chen "Could not prepare or enable the tsc clock: %d\n", 3339a436d52SHaibo Chen err); 334c2868417SFabio Estevam goto disable_adc_clk; 3359a436d52SHaibo Chen } 3369a436d52SHaibo Chen 337c2868417SFabio Estevam err = imx6ul_tsc_init(tsc); 338c2868417SFabio Estevam if (err) 339c2868417SFabio Estevam goto disable_tsc_clk; 340c2868417SFabio Estevam 341c2868417SFabio Estevam return 0; 342c2868417SFabio Estevam 343c2868417SFabio Estevam disable_tsc_clk: 344c2868417SFabio Estevam clk_disable_unprepare(tsc->tsc_clk); 345c2868417SFabio Estevam disable_adc_clk: 346c2868417SFabio Estevam clk_disable_unprepare(tsc->adc_clk); 347c2868417SFabio Estevam return err; 3489a436d52SHaibo Chen } 3499a436d52SHaibo Chen 350925145f9SDmitry Torokhov static void imx6ul_tsc_stop(struct imx6ul_tsc *tsc) 3519a436d52SHaibo Chen { 3529a436d52SHaibo Chen imx6ul_tsc_disable(tsc); 3539a436d52SHaibo Chen 3549a436d52SHaibo Chen clk_disable_unprepare(tsc->tsc_clk); 3559a436d52SHaibo Chen clk_disable_unprepare(tsc->adc_clk); 3569a436d52SHaibo Chen } 3579a436d52SHaibo Chen 358925145f9SDmitry Torokhov 359925145f9SDmitry Torokhov static int imx6ul_tsc_open(struct input_dev *input_dev) 360925145f9SDmitry Torokhov { 361925145f9SDmitry Torokhov struct imx6ul_tsc *tsc = input_get_drvdata(input_dev); 362925145f9SDmitry Torokhov 363925145f9SDmitry Torokhov return imx6ul_tsc_start(tsc); 364925145f9SDmitry Torokhov } 365925145f9SDmitry Torokhov 366925145f9SDmitry Torokhov static void imx6ul_tsc_close(struct input_dev *input_dev) 367925145f9SDmitry Torokhov { 368925145f9SDmitry Torokhov struct imx6ul_tsc *tsc = input_get_drvdata(input_dev); 369925145f9SDmitry Torokhov 370925145f9SDmitry Torokhov imx6ul_tsc_stop(tsc); 371925145f9SDmitry Torokhov } 372925145f9SDmitry Torokhov 3739a436d52SHaibo Chen static int imx6ul_tsc_probe(struct platform_device *pdev) 3749a436d52SHaibo Chen { 3759a436d52SHaibo Chen struct device_node *np = pdev->dev.of_node; 3769a436d52SHaibo Chen struct imx6ul_tsc *tsc; 3779a436d52SHaibo Chen struct input_dev *input_dev; 3789a436d52SHaibo Chen int err; 3799a436d52SHaibo Chen int tsc_irq; 3809a436d52SHaibo Chen int adc_irq; 381f43d3ec3SGuy Shapiro u32 average_samples; 3829a436d52SHaibo Chen 3835eab3cf3SFabio Estevam tsc = devm_kzalloc(&pdev->dev, sizeof(*tsc), GFP_KERNEL); 3849a436d52SHaibo Chen if (!tsc) 3859a436d52SHaibo Chen return -ENOMEM; 3869a436d52SHaibo Chen 3879a436d52SHaibo Chen input_dev = devm_input_allocate_device(&pdev->dev); 3889a436d52SHaibo Chen if (!input_dev) 3899a436d52SHaibo Chen return -ENOMEM; 3909a436d52SHaibo Chen 391002801fcSFabio Estevam input_dev->name = "iMX6UL Touchscreen Controller"; 3929a436d52SHaibo Chen input_dev->id.bustype = BUS_HOST; 3939a436d52SHaibo Chen 3949a436d52SHaibo Chen input_dev->open = imx6ul_tsc_open; 3959a436d52SHaibo Chen input_dev->close = imx6ul_tsc_close; 3969a436d52SHaibo Chen 3979a436d52SHaibo Chen input_set_capability(input_dev, EV_KEY, BTN_TOUCH); 3989a436d52SHaibo Chen input_set_abs_params(input_dev, ABS_X, 0, 0xFFF, 0, 0); 3999a436d52SHaibo Chen input_set_abs_params(input_dev, ABS_Y, 0, 0xFFF, 0, 0); 4009a436d52SHaibo Chen 4019a436d52SHaibo Chen input_set_drvdata(input_dev, tsc); 4029a436d52SHaibo Chen 4039a436d52SHaibo Chen tsc->dev = &pdev->dev; 4049a436d52SHaibo Chen tsc->input = input_dev; 4059a436d52SHaibo Chen init_completion(&tsc->completion); 4069a436d52SHaibo Chen 4079a436d52SHaibo Chen tsc->xnur_gpio = devm_gpiod_get(&pdev->dev, "xnur", GPIOD_IN); 4089a436d52SHaibo Chen if (IS_ERR(tsc->xnur_gpio)) { 4099a436d52SHaibo Chen err = PTR_ERR(tsc->xnur_gpio); 4109a436d52SHaibo Chen dev_err(&pdev->dev, 4119a436d52SHaibo Chen "failed to request GPIO tsc_X- (xnur): %d\n", err); 4129a436d52SHaibo Chen return err; 4139a436d52SHaibo Chen } 4149a436d52SHaibo Chen 415f1222f5eSAnson Huang tsc->tsc_regs = devm_platform_ioremap_resource(pdev, 0); 4169a436d52SHaibo Chen if (IS_ERR(tsc->tsc_regs)) { 4179a436d52SHaibo Chen err = PTR_ERR(tsc->tsc_regs); 4189a436d52SHaibo Chen dev_err(&pdev->dev, "failed to remap tsc memory: %d\n", err); 4199a436d52SHaibo Chen return err; 4209a436d52SHaibo Chen } 4219a436d52SHaibo Chen 422f1222f5eSAnson Huang tsc->adc_regs = devm_platform_ioremap_resource(pdev, 1); 4239a436d52SHaibo Chen if (IS_ERR(tsc->adc_regs)) { 4249a436d52SHaibo Chen err = PTR_ERR(tsc->adc_regs); 4259a436d52SHaibo Chen dev_err(&pdev->dev, "failed to remap adc memory: %d\n", err); 4269a436d52SHaibo Chen return err; 4279a436d52SHaibo Chen } 4289a436d52SHaibo Chen 4299a436d52SHaibo Chen tsc->tsc_clk = devm_clk_get(&pdev->dev, "tsc"); 4309a436d52SHaibo Chen if (IS_ERR(tsc->tsc_clk)) { 4319a436d52SHaibo Chen err = PTR_ERR(tsc->tsc_clk); 4329a436d52SHaibo Chen dev_err(&pdev->dev, "failed getting tsc clock: %d\n", err); 4339a436d52SHaibo Chen return err; 4349a436d52SHaibo Chen } 4359a436d52SHaibo Chen 4369a436d52SHaibo Chen tsc->adc_clk = devm_clk_get(&pdev->dev, "adc"); 4379a436d52SHaibo Chen if (IS_ERR(tsc->adc_clk)) { 4389a436d52SHaibo Chen err = PTR_ERR(tsc->adc_clk); 4399a436d52SHaibo Chen dev_err(&pdev->dev, "failed getting adc clock: %d\n", err); 4409a436d52SHaibo Chen return err; 4419a436d52SHaibo Chen } 4429a436d52SHaibo Chen 4439a436d52SHaibo Chen tsc_irq = platform_get_irq(pdev, 0); 4440bec8b7eSStephen Boyd if (tsc_irq < 0) 4459a436d52SHaibo Chen return tsc_irq; 4469a436d52SHaibo Chen 4479a436d52SHaibo Chen adc_irq = platform_get_irq(pdev, 1); 4480bec8b7eSStephen Boyd if (adc_irq < 0) 4499a436d52SHaibo Chen return adc_irq; 4509a436d52SHaibo Chen 4519a436d52SHaibo Chen err = devm_request_threaded_irq(tsc->dev, tsc_irq, 4529a436d52SHaibo Chen NULL, tsc_irq_fn, IRQF_ONESHOT, 4539a436d52SHaibo Chen dev_name(&pdev->dev), tsc); 4549a436d52SHaibo Chen if (err) { 4559a436d52SHaibo Chen dev_err(&pdev->dev, 4569a436d52SHaibo Chen "failed requesting tsc irq %d: %d\n", 4579a436d52SHaibo Chen tsc_irq, err); 4589a436d52SHaibo Chen return err; 4599a436d52SHaibo Chen } 4609a436d52SHaibo Chen 4619a436d52SHaibo Chen err = devm_request_irq(tsc->dev, adc_irq, adc_irq_fn, 0, 4629a436d52SHaibo Chen dev_name(&pdev->dev), tsc); 4639a436d52SHaibo Chen if (err) { 4649a436d52SHaibo Chen dev_err(&pdev->dev, 4659a436d52SHaibo Chen "failed requesting adc irq %d: %d\n", 4669a436d52SHaibo Chen adc_irq, err); 4679a436d52SHaibo Chen return err; 4689a436d52SHaibo Chen } 4699a436d52SHaibo Chen 4709a436d52SHaibo Chen err = of_property_read_u32(np, "measure-delay-time", 4719a436d52SHaibo Chen &tsc->measure_delay_time); 4729a436d52SHaibo Chen if (err) 4739a436d52SHaibo Chen tsc->measure_delay_time = 0xffff; 4749a436d52SHaibo Chen 4759a436d52SHaibo Chen err = of_property_read_u32(np, "pre-charge-time", 4769a436d52SHaibo Chen &tsc->pre_charge_time); 4779a436d52SHaibo Chen if (err) 4789a436d52SHaibo Chen tsc->pre_charge_time = 0xfff; 4799a436d52SHaibo Chen 480f43d3ec3SGuy Shapiro err = of_property_read_u32(np, "touchscreen-average-samples", 481f43d3ec3SGuy Shapiro &average_samples); 482031bfed2SGuy Shapiro if (err) 483f43d3ec3SGuy Shapiro average_samples = 1; 484031bfed2SGuy Shapiro 485f43d3ec3SGuy Shapiro switch (average_samples) { 486f43d3ec3SGuy Shapiro case 1: 487f43d3ec3SGuy Shapiro tsc->average_enable = false; 488f43d3ec3SGuy Shapiro tsc->average_select = 0; /* value unused; initialize anyway */ 489f43d3ec3SGuy Shapiro break; 490f43d3ec3SGuy Shapiro case 4: 491f43d3ec3SGuy Shapiro case 8: 492f43d3ec3SGuy Shapiro case 16: 493f43d3ec3SGuy Shapiro case 32: 494f43d3ec3SGuy Shapiro tsc->average_enable = true; 495f43d3ec3SGuy Shapiro tsc->average_select = ilog2(average_samples) - 2; 496f43d3ec3SGuy Shapiro break; 497f43d3ec3SGuy Shapiro default: 498f43d3ec3SGuy Shapiro dev_err(&pdev->dev, 499f43d3ec3SGuy Shapiro "touchscreen-average-samples (%u) must be 1, 4, 8, 16 or 32\n", 500f43d3ec3SGuy Shapiro average_samples); 501031bfed2SGuy Shapiro return -EINVAL; 502031bfed2SGuy Shapiro } 503031bfed2SGuy Shapiro 5049a436d52SHaibo Chen err = input_register_device(tsc->input); 5059a436d52SHaibo Chen if (err) { 5069a436d52SHaibo Chen dev_err(&pdev->dev, 5079a436d52SHaibo Chen "failed to register input device: %d\n", err); 5089a436d52SHaibo Chen return err; 5099a436d52SHaibo Chen } 5109a436d52SHaibo Chen 5119a436d52SHaibo Chen platform_set_drvdata(pdev, tsc); 5129a436d52SHaibo Chen return 0; 5139a436d52SHaibo Chen } 5149a436d52SHaibo Chen 5159a436d52SHaibo Chen static int __maybe_unused imx6ul_tsc_suspend(struct device *dev) 5169a436d52SHaibo Chen { 5179a436d52SHaibo Chen struct platform_device *pdev = to_platform_device(dev); 5189a436d52SHaibo Chen struct imx6ul_tsc *tsc = platform_get_drvdata(pdev); 5199a436d52SHaibo Chen struct input_dev *input_dev = tsc->input; 5209a436d52SHaibo Chen 5219a436d52SHaibo Chen mutex_lock(&input_dev->mutex); 5229a436d52SHaibo Chen 523*d69f0a43SAndrzej Pietrasiewicz if (input_device_enabled(input_dev)) 524925145f9SDmitry Torokhov imx6ul_tsc_stop(tsc); 5259a436d52SHaibo Chen 5269a436d52SHaibo Chen mutex_unlock(&input_dev->mutex); 5279a436d52SHaibo Chen 5289a436d52SHaibo Chen return 0; 5299a436d52SHaibo Chen } 5309a436d52SHaibo Chen 5319a436d52SHaibo Chen static int __maybe_unused imx6ul_tsc_resume(struct device *dev) 5329a436d52SHaibo Chen { 5339a436d52SHaibo Chen struct platform_device *pdev = to_platform_device(dev); 5349a436d52SHaibo Chen struct imx6ul_tsc *tsc = platform_get_drvdata(pdev); 5359a436d52SHaibo Chen struct input_dev *input_dev = tsc->input; 5369a436d52SHaibo Chen int retval = 0; 5379a436d52SHaibo Chen 5389a436d52SHaibo Chen mutex_lock(&input_dev->mutex); 5399a436d52SHaibo Chen 540*d69f0a43SAndrzej Pietrasiewicz if (input_device_enabled(input_dev)) 541925145f9SDmitry Torokhov retval = imx6ul_tsc_start(tsc); 54230df23c5SDan Carpenter 5439a436d52SHaibo Chen mutex_unlock(&input_dev->mutex); 544925145f9SDmitry Torokhov 5459a436d52SHaibo Chen return retval; 5469a436d52SHaibo Chen } 5479a436d52SHaibo Chen 5489a436d52SHaibo Chen static SIMPLE_DEV_PM_OPS(imx6ul_tsc_pm_ops, 5499a436d52SHaibo Chen imx6ul_tsc_suspend, imx6ul_tsc_resume); 5509a436d52SHaibo Chen 5519a436d52SHaibo Chen static const struct of_device_id imx6ul_tsc_match[] = { 5529a436d52SHaibo Chen { .compatible = "fsl,imx6ul-tsc", }, 5539a436d52SHaibo Chen { /* sentinel */ } 5549a436d52SHaibo Chen }; 5559a436d52SHaibo Chen MODULE_DEVICE_TABLE(of, imx6ul_tsc_match); 5569a436d52SHaibo Chen 5579a436d52SHaibo Chen static struct platform_driver imx6ul_tsc_driver = { 5589a436d52SHaibo Chen .driver = { 5599a436d52SHaibo Chen .name = "imx6ul-tsc", 5609a436d52SHaibo Chen .of_match_table = imx6ul_tsc_match, 5619a436d52SHaibo Chen .pm = &imx6ul_tsc_pm_ops, 5629a436d52SHaibo Chen }, 5639a436d52SHaibo Chen .probe = imx6ul_tsc_probe, 5649a436d52SHaibo Chen }; 5659a436d52SHaibo Chen module_platform_driver(imx6ul_tsc_driver); 5669a436d52SHaibo Chen 5679a436d52SHaibo Chen MODULE_AUTHOR("Haibo Chen <haibo.chen@freescale.com>"); 5689a436d52SHaibo Chen MODULE_DESCRIPTION("Freescale i.MX6UL Touchscreen controller driver"); 5699a436d52SHaibo Chen MODULE_LICENSE("GPL v2"); 570