18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// Freescale i.MX6UL touchscreen controller driver 48c2ecf20Sopenharmony_ci// 58c2ecf20Sopenharmony_ci// Copyright (C) 2015 Freescale Semiconductor, Inc. 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/errno.h> 88c2ecf20Sopenharmony_ci#include <linux/kernel.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 118c2ecf20Sopenharmony_ci#include <linux/input.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/completion.h> 148c2ecf20Sopenharmony_ci#include <linux/delay.h> 158c2ecf20Sopenharmony_ci#include <linux/of.h> 168c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 178c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 188c2ecf20Sopenharmony_ci#include <linux/clk.h> 198c2ecf20Sopenharmony_ci#include <linux/io.h> 208c2ecf20Sopenharmony_ci#include <linux/log2.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* ADC configuration registers field define */ 238c2ecf20Sopenharmony_ci#define ADC_AIEN (0x1 << 7) 248c2ecf20Sopenharmony_ci#define ADC_CONV_DISABLE 0x1F 258c2ecf20Sopenharmony_ci#define ADC_AVGE (0x1 << 5) 268c2ecf20Sopenharmony_ci#define ADC_CAL (0x1 << 7) 278c2ecf20Sopenharmony_ci#define ADC_CALF 0x2 288c2ecf20Sopenharmony_ci#define ADC_12BIT_MODE (0x2 << 2) 298c2ecf20Sopenharmony_ci#define ADC_CONV_MODE_MASK (0x3 << 2) 308c2ecf20Sopenharmony_ci#define ADC_IPG_CLK 0x00 318c2ecf20Sopenharmony_ci#define ADC_INPUT_CLK_MASK 0x3 328c2ecf20Sopenharmony_ci#define ADC_CLK_DIV_8 (0x03 << 5) 338c2ecf20Sopenharmony_ci#define ADC_CLK_DIV_MASK (0x3 << 5) 348c2ecf20Sopenharmony_ci#define ADC_SHORT_SAMPLE_MODE (0x0 << 4) 358c2ecf20Sopenharmony_ci#define ADC_SAMPLE_MODE_MASK (0x1 << 4) 368c2ecf20Sopenharmony_ci#define ADC_HARDWARE_TRIGGER (0x1 << 13) 378c2ecf20Sopenharmony_ci#define ADC_AVGS_SHIFT 14 388c2ecf20Sopenharmony_ci#define ADC_AVGS_MASK (0x3 << 14) 398c2ecf20Sopenharmony_ci#define SELECT_CHANNEL_4 0x04 408c2ecf20Sopenharmony_ci#define SELECT_CHANNEL_1 0x01 418c2ecf20Sopenharmony_ci#define DISABLE_CONVERSION_INT (0x0 << 7) 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/* ADC registers */ 448c2ecf20Sopenharmony_ci#define REG_ADC_HC0 0x00 458c2ecf20Sopenharmony_ci#define REG_ADC_HC1 0x04 468c2ecf20Sopenharmony_ci#define REG_ADC_HC2 0x08 478c2ecf20Sopenharmony_ci#define REG_ADC_HC3 0x0C 488c2ecf20Sopenharmony_ci#define REG_ADC_HC4 0x10 498c2ecf20Sopenharmony_ci#define REG_ADC_HS 0x14 508c2ecf20Sopenharmony_ci#define REG_ADC_R0 0x18 518c2ecf20Sopenharmony_ci#define REG_ADC_CFG 0x2C 528c2ecf20Sopenharmony_ci#define REG_ADC_GC 0x30 538c2ecf20Sopenharmony_ci#define REG_ADC_GS 0x34 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#define ADC_TIMEOUT msecs_to_jiffies(100) 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci/* TSC registers */ 588c2ecf20Sopenharmony_ci#define REG_TSC_BASIC_SETING 0x00 598c2ecf20Sopenharmony_ci#define REG_TSC_PRE_CHARGE_TIME 0x10 608c2ecf20Sopenharmony_ci#define REG_TSC_FLOW_CONTROL 0x20 618c2ecf20Sopenharmony_ci#define REG_TSC_MEASURE_VALUE 0x30 628c2ecf20Sopenharmony_ci#define REG_TSC_INT_EN 0x40 638c2ecf20Sopenharmony_ci#define REG_TSC_INT_SIG_EN 0x50 648c2ecf20Sopenharmony_ci#define REG_TSC_INT_STATUS 0x60 658c2ecf20Sopenharmony_ci#define REG_TSC_DEBUG_MODE 0x70 668c2ecf20Sopenharmony_ci#define REG_TSC_DEBUG_MODE2 0x80 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/* TSC configuration registers field define */ 698c2ecf20Sopenharmony_ci#define DETECT_4_WIRE_MODE (0x0 << 4) 708c2ecf20Sopenharmony_ci#define AUTO_MEASURE 0x1 718c2ecf20Sopenharmony_ci#define MEASURE_SIGNAL 0x1 728c2ecf20Sopenharmony_ci#define DETECT_SIGNAL (0x1 << 4) 738c2ecf20Sopenharmony_ci#define VALID_SIGNAL (0x1 << 8) 748c2ecf20Sopenharmony_ci#define MEASURE_INT_EN 0x1 758c2ecf20Sopenharmony_ci#define MEASURE_SIG_EN 0x1 768c2ecf20Sopenharmony_ci#define VALID_SIG_EN (0x1 << 8) 778c2ecf20Sopenharmony_ci#define DE_GLITCH_2 (0x2 << 29) 788c2ecf20Sopenharmony_ci#define START_SENSE (0x1 << 12) 798c2ecf20Sopenharmony_ci#define TSC_DISABLE (0x1 << 16) 808c2ecf20Sopenharmony_ci#define DETECT_MODE 0x2 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistruct imx6ul_tsc { 838c2ecf20Sopenharmony_ci struct device *dev; 848c2ecf20Sopenharmony_ci struct input_dev *input; 858c2ecf20Sopenharmony_ci void __iomem *tsc_regs; 868c2ecf20Sopenharmony_ci void __iomem *adc_regs; 878c2ecf20Sopenharmony_ci struct clk *tsc_clk; 888c2ecf20Sopenharmony_ci struct clk *adc_clk; 898c2ecf20Sopenharmony_ci struct gpio_desc *xnur_gpio; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci u32 measure_delay_time; 928c2ecf20Sopenharmony_ci u32 pre_charge_time; 938c2ecf20Sopenharmony_ci bool average_enable; 948c2ecf20Sopenharmony_ci u32 average_select; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci struct completion completion; 978c2ecf20Sopenharmony_ci}; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci/* 1008c2ecf20Sopenharmony_ci * TSC module need ADC to get the measure value. So 1018c2ecf20Sopenharmony_ci * before config TSC, we should initialize ADC module. 1028c2ecf20Sopenharmony_ci */ 1038c2ecf20Sopenharmony_cistatic int imx6ul_adc_init(struct imx6ul_tsc *tsc) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci u32 adc_hc = 0; 1068c2ecf20Sopenharmony_ci u32 adc_gc; 1078c2ecf20Sopenharmony_ci u32 adc_gs; 1088c2ecf20Sopenharmony_ci u32 adc_cfg; 1098c2ecf20Sopenharmony_ci unsigned long timeout; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci reinit_completion(&tsc->completion); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci adc_cfg = readl(tsc->adc_regs + REG_ADC_CFG); 1148c2ecf20Sopenharmony_ci adc_cfg &= ~(ADC_CONV_MODE_MASK | ADC_INPUT_CLK_MASK); 1158c2ecf20Sopenharmony_ci adc_cfg |= ADC_12BIT_MODE | ADC_IPG_CLK; 1168c2ecf20Sopenharmony_ci adc_cfg &= ~(ADC_CLK_DIV_MASK | ADC_SAMPLE_MODE_MASK); 1178c2ecf20Sopenharmony_ci adc_cfg |= ADC_CLK_DIV_8 | ADC_SHORT_SAMPLE_MODE; 1188c2ecf20Sopenharmony_ci if (tsc->average_enable) { 1198c2ecf20Sopenharmony_ci adc_cfg &= ~ADC_AVGS_MASK; 1208c2ecf20Sopenharmony_ci adc_cfg |= (tsc->average_select) << ADC_AVGS_SHIFT; 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci adc_cfg &= ~ADC_HARDWARE_TRIGGER; 1238c2ecf20Sopenharmony_ci writel(adc_cfg, tsc->adc_regs + REG_ADC_CFG); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci /* enable calibration interrupt */ 1268c2ecf20Sopenharmony_ci adc_hc |= ADC_AIEN; 1278c2ecf20Sopenharmony_ci adc_hc |= ADC_CONV_DISABLE; 1288c2ecf20Sopenharmony_ci writel(adc_hc, tsc->adc_regs + REG_ADC_HC0); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci /* start ADC calibration */ 1318c2ecf20Sopenharmony_ci adc_gc = readl(tsc->adc_regs + REG_ADC_GC); 1328c2ecf20Sopenharmony_ci adc_gc |= ADC_CAL; 1338c2ecf20Sopenharmony_ci if (tsc->average_enable) 1348c2ecf20Sopenharmony_ci adc_gc |= ADC_AVGE; 1358c2ecf20Sopenharmony_ci writel(adc_gc, tsc->adc_regs + REG_ADC_GC); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci timeout = wait_for_completion_timeout 1388c2ecf20Sopenharmony_ci (&tsc->completion, ADC_TIMEOUT); 1398c2ecf20Sopenharmony_ci if (timeout == 0) { 1408c2ecf20Sopenharmony_ci dev_err(tsc->dev, "Timeout for adc calibration\n"); 1418c2ecf20Sopenharmony_ci return -ETIMEDOUT; 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci adc_gs = readl(tsc->adc_regs + REG_ADC_GS); 1458c2ecf20Sopenharmony_ci if (adc_gs & ADC_CALF) { 1468c2ecf20Sopenharmony_ci dev_err(tsc->dev, "ADC calibration failed\n"); 1478c2ecf20Sopenharmony_ci return -EINVAL; 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci /* TSC need the ADC work in hardware trigger */ 1518c2ecf20Sopenharmony_ci adc_cfg = readl(tsc->adc_regs + REG_ADC_CFG); 1528c2ecf20Sopenharmony_ci adc_cfg |= ADC_HARDWARE_TRIGGER; 1538c2ecf20Sopenharmony_ci writel(adc_cfg, tsc->adc_regs + REG_ADC_CFG); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci return 0; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci/* 1598c2ecf20Sopenharmony_ci * This is a TSC workaround. Currently TSC misconnect two 1608c2ecf20Sopenharmony_ci * ADC channels, this function remap channel configure for 1618c2ecf20Sopenharmony_ci * hardware trigger. 1628c2ecf20Sopenharmony_ci */ 1638c2ecf20Sopenharmony_cistatic void imx6ul_tsc_channel_config(struct imx6ul_tsc *tsc) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci u32 adc_hc0, adc_hc1, adc_hc2, adc_hc3, adc_hc4; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci adc_hc0 = DISABLE_CONVERSION_INT; 1688c2ecf20Sopenharmony_ci writel(adc_hc0, tsc->adc_regs + REG_ADC_HC0); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci adc_hc1 = DISABLE_CONVERSION_INT | SELECT_CHANNEL_4; 1718c2ecf20Sopenharmony_ci writel(adc_hc1, tsc->adc_regs + REG_ADC_HC1); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci adc_hc2 = DISABLE_CONVERSION_INT; 1748c2ecf20Sopenharmony_ci writel(adc_hc2, tsc->adc_regs + REG_ADC_HC2); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci adc_hc3 = DISABLE_CONVERSION_INT | SELECT_CHANNEL_1; 1778c2ecf20Sopenharmony_ci writel(adc_hc3, tsc->adc_regs + REG_ADC_HC3); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci adc_hc4 = DISABLE_CONVERSION_INT; 1808c2ecf20Sopenharmony_ci writel(adc_hc4, tsc->adc_regs + REG_ADC_HC4); 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci/* 1848c2ecf20Sopenharmony_ci * TSC setting, confige the pre-charge time and measure delay time. 1858c2ecf20Sopenharmony_ci * different touch screen may need different pre-charge time and 1868c2ecf20Sopenharmony_ci * measure delay time. 1878c2ecf20Sopenharmony_ci */ 1888c2ecf20Sopenharmony_cistatic void imx6ul_tsc_set(struct imx6ul_tsc *tsc) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci u32 basic_setting = 0; 1918c2ecf20Sopenharmony_ci u32 start; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci basic_setting |= tsc->measure_delay_time << 8; 1948c2ecf20Sopenharmony_ci basic_setting |= DETECT_4_WIRE_MODE | AUTO_MEASURE; 1958c2ecf20Sopenharmony_ci writel(basic_setting, tsc->tsc_regs + REG_TSC_BASIC_SETING); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci writel(DE_GLITCH_2, tsc->tsc_regs + REG_TSC_DEBUG_MODE2); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci writel(tsc->pre_charge_time, tsc->tsc_regs + REG_TSC_PRE_CHARGE_TIME); 2008c2ecf20Sopenharmony_ci writel(MEASURE_INT_EN, tsc->tsc_regs + REG_TSC_INT_EN); 2018c2ecf20Sopenharmony_ci writel(MEASURE_SIG_EN | VALID_SIG_EN, 2028c2ecf20Sopenharmony_ci tsc->tsc_regs + REG_TSC_INT_SIG_EN); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci /* start sense detection */ 2058c2ecf20Sopenharmony_ci start = readl(tsc->tsc_regs + REG_TSC_FLOW_CONTROL); 2068c2ecf20Sopenharmony_ci start |= START_SENSE; 2078c2ecf20Sopenharmony_ci start &= ~TSC_DISABLE; 2088c2ecf20Sopenharmony_ci writel(start, tsc->tsc_regs + REG_TSC_FLOW_CONTROL); 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic int imx6ul_tsc_init(struct imx6ul_tsc *tsc) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci int err; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci err = imx6ul_adc_init(tsc); 2168c2ecf20Sopenharmony_ci if (err) 2178c2ecf20Sopenharmony_ci return err; 2188c2ecf20Sopenharmony_ci imx6ul_tsc_channel_config(tsc); 2198c2ecf20Sopenharmony_ci imx6ul_tsc_set(tsc); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci return 0; 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic void imx6ul_tsc_disable(struct imx6ul_tsc *tsc) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci u32 tsc_flow; 2278c2ecf20Sopenharmony_ci u32 adc_cfg; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci /* TSC controller enters to idle status */ 2308c2ecf20Sopenharmony_ci tsc_flow = readl(tsc->tsc_regs + REG_TSC_FLOW_CONTROL); 2318c2ecf20Sopenharmony_ci tsc_flow |= TSC_DISABLE; 2328c2ecf20Sopenharmony_ci writel(tsc_flow, tsc->tsc_regs + REG_TSC_FLOW_CONTROL); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci /* ADC controller enters to stop mode */ 2358c2ecf20Sopenharmony_ci adc_cfg = readl(tsc->adc_regs + REG_ADC_HC0); 2368c2ecf20Sopenharmony_ci adc_cfg |= ADC_CONV_DISABLE; 2378c2ecf20Sopenharmony_ci writel(adc_cfg, tsc->adc_regs + REG_ADC_HC0); 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci/* Delay some time (max 2ms), wait the pre-charge done. */ 2418c2ecf20Sopenharmony_cistatic bool tsc_wait_detect_mode(struct imx6ul_tsc *tsc) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci unsigned long timeout = jiffies + msecs_to_jiffies(2); 2448c2ecf20Sopenharmony_ci u32 state_machine; 2458c2ecf20Sopenharmony_ci u32 debug_mode2; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci do { 2488c2ecf20Sopenharmony_ci if (time_after(jiffies, timeout)) 2498c2ecf20Sopenharmony_ci return false; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci usleep_range(200, 400); 2528c2ecf20Sopenharmony_ci debug_mode2 = readl(tsc->tsc_regs + REG_TSC_DEBUG_MODE2); 2538c2ecf20Sopenharmony_ci state_machine = (debug_mode2 >> 20) & 0x7; 2548c2ecf20Sopenharmony_ci } while (state_machine != DETECT_MODE); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci usleep_range(200, 400); 2578c2ecf20Sopenharmony_ci return true; 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_cistatic irqreturn_t tsc_irq_fn(int irq, void *dev_id) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci struct imx6ul_tsc *tsc = dev_id; 2638c2ecf20Sopenharmony_ci u32 status; 2648c2ecf20Sopenharmony_ci u32 value; 2658c2ecf20Sopenharmony_ci u32 x, y; 2668c2ecf20Sopenharmony_ci u32 start; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci status = readl(tsc->tsc_regs + REG_TSC_INT_STATUS); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci /* write 1 to clear the bit measure-signal */ 2718c2ecf20Sopenharmony_ci writel(MEASURE_SIGNAL | DETECT_SIGNAL, 2728c2ecf20Sopenharmony_ci tsc->tsc_regs + REG_TSC_INT_STATUS); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci /* It's a HW self-clean bit. Set this bit and start sense detection */ 2758c2ecf20Sopenharmony_ci start = readl(tsc->tsc_regs + REG_TSC_FLOW_CONTROL); 2768c2ecf20Sopenharmony_ci start |= START_SENSE; 2778c2ecf20Sopenharmony_ci writel(start, tsc->tsc_regs + REG_TSC_FLOW_CONTROL); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci if (status & MEASURE_SIGNAL) { 2808c2ecf20Sopenharmony_ci value = readl(tsc->tsc_regs + REG_TSC_MEASURE_VALUE); 2818c2ecf20Sopenharmony_ci x = (value >> 16) & 0x0fff; 2828c2ecf20Sopenharmony_ci y = value & 0x0fff; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci /* 2858c2ecf20Sopenharmony_ci * In detect mode, we can get the xnur gpio value, 2868c2ecf20Sopenharmony_ci * otherwise assume contact is stiull active. 2878c2ecf20Sopenharmony_ci */ 2888c2ecf20Sopenharmony_ci if (!tsc_wait_detect_mode(tsc) || 2898c2ecf20Sopenharmony_ci gpiod_get_value_cansleep(tsc->xnur_gpio)) { 2908c2ecf20Sopenharmony_ci input_report_key(tsc->input, BTN_TOUCH, 1); 2918c2ecf20Sopenharmony_ci input_report_abs(tsc->input, ABS_X, x); 2928c2ecf20Sopenharmony_ci input_report_abs(tsc->input, ABS_Y, y); 2938c2ecf20Sopenharmony_ci } else { 2948c2ecf20Sopenharmony_ci input_report_key(tsc->input, BTN_TOUCH, 0); 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci input_sync(tsc->input); 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3018c2ecf20Sopenharmony_ci} 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cistatic irqreturn_t adc_irq_fn(int irq, void *dev_id) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci struct imx6ul_tsc *tsc = dev_id; 3068c2ecf20Sopenharmony_ci u32 coco; 3078c2ecf20Sopenharmony_ci u32 value; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci coco = readl(tsc->adc_regs + REG_ADC_HS); 3108c2ecf20Sopenharmony_ci if (coco & 0x01) { 3118c2ecf20Sopenharmony_ci value = readl(tsc->adc_regs + REG_ADC_R0); 3128c2ecf20Sopenharmony_ci complete(&tsc->completion); 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistatic int imx6ul_tsc_start(struct imx6ul_tsc *tsc) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci int err; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci err = clk_prepare_enable(tsc->adc_clk); 3238c2ecf20Sopenharmony_ci if (err) { 3248c2ecf20Sopenharmony_ci dev_err(tsc->dev, 3258c2ecf20Sopenharmony_ci "Could not prepare or enable the adc clock: %d\n", 3268c2ecf20Sopenharmony_ci err); 3278c2ecf20Sopenharmony_ci return err; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci err = clk_prepare_enable(tsc->tsc_clk); 3318c2ecf20Sopenharmony_ci if (err) { 3328c2ecf20Sopenharmony_ci dev_err(tsc->dev, 3338c2ecf20Sopenharmony_ci "Could not prepare or enable the tsc clock: %d\n", 3348c2ecf20Sopenharmony_ci err); 3358c2ecf20Sopenharmony_ci goto disable_adc_clk; 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci err = imx6ul_tsc_init(tsc); 3398c2ecf20Sopenharmony_ci if (err) 3408c2ecf20Sopenharmony_ci goto disable_tsc_clk; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci return 0; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_cidisable_tsc_clk: 3458c2ecf20Sopenharmony_ci clk_disable_unprepare(tsc->tsc_clk); 3468c2ecf20Sopenharmony_cidisable_adc_clk: 3478c2ecf20Sopenharmony_ci clk_disable_unprepare(tsc->adc_clk); 3488c2ecf20Sopenharmony_ci return err; 3498c2ecf20Sopenharmony_ci} 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_cistatic void imx6ul_tsc_stop(struct imx6ul_tsc *tsc) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci imx6ul_tsc_disable(tsc); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci clk_disable_unprepare(tsc->tsc_clk); 3568c2ecf20Sopenharmony_ci clk_disable_unprepare(tsc->adc_clk); 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_cistatic int imx6ul_tsc_open(struct input_dev *input_dev) 3618c2ecf20Sopenharmony_ci{ 3628c2ecf20Sopenharmony_ci struct imx6ul_tsc *tsc = input_get_drvdata(input_dev); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci return imx6ul_tsc_start(tsc); 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_cistatic void imx6ul_tsc_close(struct input_dev *input_dev) 3688c2ecf20Sopenharmony_ci{ 3698c2ecf20Sopenharmony_ci struct imx6ul_tsc *tsc = input_get_drvdata(input_dev); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci imx6ul_tsc_stop(tsc); 3728c2ecf20Sopenharmony_ci} 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_cistatic int imx6ul_tsc_probe(struct platform_device *pdev) 3758c2ecf20Sopenharmony_ci{ 3768c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 3778c2ecf20Sopenharmony_ci struct imx6ul_tsc *tsc; 3788c2ecf20Sopenharmony_ci struct input_dev *input_dev; 3798c2ecf20Sopenharmony_ci int err; 3808c2ecf20Sopenharmony_ci int tsc_irq; 3818c2ecf20Sopenharmony_ci int adc_irq; 3828c2ecf20Sopenharmony_ci u32 average_samples; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci tsc = devm_kzalloc(&pdev->dev, sizeof(*tsc), GFP_KERNEL); 3858c2ecf20Sopenharmony_ci if (!tsc) 3868c2ecf20Sopenharmony_ci return -ENOMEM; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci input_dev = devm_input_allocate_device(&pdev->dev); 3898c2ecf20Sopenharmony_ci if (!input_dev) 3908c2ecf20Sopenharmony_ci return -ENOMEM; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci input_dev->name = "iMX6UL Touchscreen Controller"; 3938c2ecf20Sopenharmony_ci input_dev->id.bustype = BUS_HOST; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci input_dev->open = imx6ul_tsc_open; 3968c2ecf20Sopenharmony_ci input_dev->close = imx6ul_tsc_close; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci input_set_capability(input_dev, EV_KEY, BTN_TOUCH); 3998c2ecf20Sopenharmony_ci input_set_abs_params(input_dev, ABS_X, 0, 0xFFF, 0, 0); 4008c2ecf20Sopenharmony_ci input_set_abs_params(input_dev, ABS_Y, 0, 0xFFF, 0, 0); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci input_set_drvdata(input_dev, tsc); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci tsc->dev = &pdev->dev; 4058c2ecf20Sopenharmony_ci tsc->input = input_dev; 4068c2ecf20Sopenharmony_ci init_completion(&tsc->completion); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci tsc->xnur_gpio = devm_gpiod_get(&pdev->dev, "xnur", GPIOD_IN); 4098c2ecf20Sopenharmony_ci if (IS_ERR(tsc->xnur_gpio)) { 4108c2ecf20Sopenharmony_ci err = PTR_ERR(tsc->xnur_gpio); 4118c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 4128c2ecf20Sopenharmony_ci "failed to request GPIO tsc_X- (xnur): %d\n", err); 4138c2ecf20Sopenharmony_ci return err; 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci tsc->tsc_regs = devm_platform_ioremap_resource(pdev, 0); 4178c2ecf20Sopenharmony_ci if (IS_ERR(tsc->tsc_regs)) { 4188c2ecf20Sopenharmony_ci err = PTR_ERR(tsc->tsc_regs); 4198c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to remap tsc memory: %d\n", err); 4208c2ecf20Sopenharmony_ci return err; 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci tsc->adc_regs = devm_platform_ioremap_resource(pdev, 1); 4248c2ecf20Sopenharmony_ci if (IS_ERR(tsc->adc_regs)) { 4258c2ecf20Sopenharmony_ci err = PTR_ERR(tsc->adc_regs); 4268c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to remap adc memory: %d\n", err); 4278c2ecf20Sopenharmony_ci return err; 4288c2ecf20Sopenharmony_ci } 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci tsc->tsc_clk = devm_clk_get(&pdev->dev, "tsc"); 4318c2ecf20Sopenharmony_ci if (IS_ERR(tsc->tsc_clk)) { 4328c2ecf20Sopenharmony_ci err = PTR_ERR(tsc->tsc_clk); 4338c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed getting tsc clock: %d\n", err); 4348c2ecf20Sopenharmony_ci return err; 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci tsc->adc_clk = devm_clk_get(&pdev->dev, "adc"); 4388c2ecf20Sopenharmony_ci if (IS_ERR(tsc->adc_clk)) { 4398c2ecf20Sopenharmony_ci err = PTR_ERR(tsc->adc_clk); 4408c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed getting adc clock: %d\n", err); 4418c2ecf20Sopenharmony_ci return err; 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci tsc_irq = platform_get_irq(pdev, 0); 4458c2ecf20Sopenharmony_ci if (tsc_irq < 0) 4468c2ecf20Sopenharmony_ci return tsc_irq; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci adc_irq = platform_get_irq(pdev, 1); 4498c2ecf20Sopenharmony_ci if (adc_irq < 0) 4508c2ecf20Sopenharmony_ci return adc_irq; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci err = devm_request_threaded_irq(tsc->dev, tsc_irq, 4538c2ecf20Sopenharmony_ci NULL, tsc_irq_fn, IRQF_ONESHOT, 4548c2ecf20Sopenharmony_ci dev_name(&pdev->dev), tsc); 4558c2ecf20Sopenharmony_ci if (err) { 4568c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 4578c2ecf20Sopenharmony_ci "failed requesting tsc irq %d: %d\n", 4588c2ecf20Sopenharmony_ci tsc_irq, err); 4598c2ecf20Sopenharmony_ci return err; 4608c2ecf20Sopenharmony_ci } 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci err = devm_request_irq(tsc->dev, adc_irq, adc_irq_fn, 0, 4638c2ecf20Sopenharmony_ci dev_name(&pdev->dev), tsc); 4648c2ecf20Sopenharmony_ci if (err) { 4658c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 4668c2ecf20Sopenharmony_ci "failed requesting adc irq %d: %d\n", 4678c2ecf20Sopenharmony_ci adc_irq, err); 4688c2ecf20Sopenharmony_ci return err; 4698c2ecf20Sopenharmony_ci } 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci err = of_property_read_u32(np, "measure-delay-time", 4728c2ecf20Sopenharmony_ci &tsc->measure_delay_time); 4738c2ecf20Sopenharmony_ci if (err) 4748c2ecf20Sopenharmony_ci tsc->measure_delay_time = 0xffff; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci err = of_property_read_u32(np, "pre-charge-time", 4778c2ecf20Sopenharmony_ci &tsc->pre_charge_time); 4788c2ecf20Sopenharmony_ci if (err) 4798c2ecf20Sopenharmony_ci tsc->pre_charge_time = 0xfff; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci err = of_property_read_u32(np, "touchscreen-average-samples", 4828c2ecf20Sopenharmony_ci &average_samples); 4838c2ecf20Sopenharmony_ci if (err) 4848c2ecf20Sopenharmony_ci average_samples = 1; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci switch (average_samples) { 4878c2ecf20Sopenharmony_ci case 1: 4888c2ecf20Sopenharmony_ci tsc->average_enable = false; 4898c2ecf20Sopenharmony_ci tsc->average_select = 0; /* value unused; initialize anyway */ 4908c2ecf20Sopenharmony_ci break; 4918c2ecf20Sopenharmony_ci case 4: 4928c2ecf20Sopenharmony_ci case 8: 4938c2ecf20Sopenharmony_ci case 16: 4948c2ecf20Sopenharmony_ci case 32: 4958c2ecf20Sopenharmony_ci tsc->average_enable = true; 4968c2ecf20Sopenharmony_ci tsc->average_select = ilog2(average_samples) - 2; 4978c2ecf20Sopenharmony_ci break; 4988c2ecf20Sopenharmony_ci default: 4998c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 5008c2ecf20Sopenharmony_ci "touchscreen-average-samples (%u) must be 1, 4, 8, 16 or 32\n", 5018c2ecf20Sopenharmony_ci average_samples); 5028c2ecf20Sopenharmony_ci return -EINVAL; 5038c2ecf20Sopenharmony_ci } 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci err = input_register_device(tsc->input); 5068c2ecf20Sopenharmony_ci if (err) { 5078c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 5088c2ecf20Sopenharmony_ci "failed to register input device: %d\n", err); 5098c2ecf20Sopenharmony_ci return err; 5108c2ecf20Sopenharmony_ci } 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, tsc); 5138c2ecf20Sopenharmony_ci return 0; 5148c2ecf20Sopenharmony_ci} 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_cistatic int __maybe_unused imx6ul_tsc_suspend(struct device *dev) 5178c2ecf20Sopenharmony_ci{ 5188c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 5198c2ecf20Sopenharmony_ci struct imx6ul_tsc *tsc = platform_get_drvdata(pdev); 5208c2ecf20Sopenharmony_ci struct input_dev *input_dev = tsc->input; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci mutex_lock(&input_dev->mutex); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci if (input_dev->users) 5258c2ecf20Sopenharmony_ci imx6ul_tsc_stop(tsc); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci mutex_unlock(&input_dev->mutex); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci return 0; 5308c2ecf20Sopenharmony_ci} 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_cistatic int __maybe_unused imx6ul_tsc_resume(struct device *dev) 5338c2ecf20Sopenharmony_ci{ 5348c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 5358c2ecf20Sopenharmony_ci struct imx6ul_tsc *tsc = platform_get_drvdata(pdev); 5368c2ecf20Sopenharmony_ci struct input_dev *input_dev = tsc->input; 5378c2ecf20Sopenharmony_ci int retval = 0; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci mutex_lock(&input_dev->mutex); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci if (input_dev->users) 5428c2ecf20Sopenharmony_ci retval = imx6ul_tsc_start(tsc); 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci mutex_unlock(&input_dev->mutex); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci return retval; 5478c2ecf20Sopenharmony_ci} 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(imx6ul_tsc_pm_ops, 5508c2ecf20Sopenharmony_ci imx6ul_tsc_suspend, imx6ul_tsc_resume); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_cistatic const struct of_device_id imx6ul_tsc_match[] = { 5538c2ecf20Sopenharmony_ci { .compatible = "fsl,imx6ul-tsc", }, 5548c2ecf20Sopenharmony_ci { /* sentinel */ } 5558c2ecf20Sopenharmony_ci}; 5568c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, imx6ul_tsc_match); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_cistatic struct platform_driver imx6ul_tsc_driver = { 5598c2ecf20Sopenharmony_ci .driver = { 5608c2ecf20Sopenharmony_ci .name = "imx6ul-tsc", 5618c2ecf20Sopenharmony_ci .of_match_table = imx6ul_tsc_match, 5628c2ecf20Sopenharmony_ci .pm = &imx6ul_tsc_pm_ops, 5638c2ecf20Sopenharmony_ci }, 5648c2ecf20Sopenharmony_ci .probe = imx6ul_tsc_probe, 5658c2ecf20Sopenharmony_ci}; 5668c2ecf20Sopenharmony_cimodule_platform_driver(imx6ul_tsc_driver); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ciMODULE_AUTHOR("Haibo Chen <haibo.chen@freescale.com>"); 5698c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Freescale i.MX6UL Touchscreen controller driver"); 5708c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 571