18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * LPC32xx built-in touchscreen driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2010 NXP Semiconductors 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 98c2ecf20Sopenharmony_ci#include <linux/input.h> 108c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/clk.h> 138c2ecf20Sopenharmony_ci#include <linux/io.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci#include <linux/of.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci/* 188c2ecf20Sopenharmony_ci * Touchscreen controller register offsets 198c2ecf20Sopenharmony_ci */ 208c2ecf20Sopenharmony_ci#define LPC32XX_TSC_STAT 0x00 218c2ecf20Sopenharmony_ci#define LPC32XX_TSC_SEL 0x04 228c2ecf20Sopenharmony_ci#define LPC32XX_TSC_CON 0x08 238c2ecf20Sopenharmony_ci#define LPC32XX_TSC_FIFO 0x0C 248c2ecf20Sopenharmony_ci#define LPC32XX_TSC_DTR 0x10 258c2ecf20Sopenharmony_ci#define LPC32XX_TSC_RTR 0x14 268c2ecf20Sopenharmony_ci#define LPC32XX_TSC_UTR 0x18 278c2ecf20Sopenharmony_ci#define LPC32XX_TSC_TTR 0x1C 288c2ecf20Sopenharmony_ci#define LPC32XX_TSC_DXP 0x20 298c2ecf20Sopenharmony_ci#define LPC32XX_TSC_MIN_X 0x24 308c2ecf20Sopenharmony_ci#define LPC32XX_TSC_MAX_X 0x28 318c2ecf20Sopenharmony_ci#define LPC32XX_TSC_MIN_Y 0x2C 328c2ecf20Sopenharmony_ci#define LPC32XX_TSC_MAX_Y 0x30 338c2ecf20Sopenharmony_ci#define LPC32XX_TSC_AUX_UTR 0x34 348c2ecf20Sopenharmony_ci#define LPC32XX_TSC_AUX_MIN 0x38 358c2ecf20Sopenharmony_ci#define LPC32XX_TSC_AUX_MAX 0x3C 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define LPC32XX_TSC_STAT_FIFO_OVRRN (1 << 8) 388c2ecf20Sopenharmony_ci#define LPC32XX_TSC_STAT_FIFO_EMPTY (1 << 7) 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define LPC32XX_TSC_SEL_DEFVAL 0x0284 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define LPC32XX_TSC_ADCCON_IRQ_TO_FIFO_4 (0x1 << 11) 438c2ecf20Sopenharmony_ci#define LPC32XX_TSC_ADCCON_X_SAMPLE_SIZE(s) ((10 - (s)) << 7) 448c2ecf20Sopenharmony_ci#define LPC32XX_TSC_ADCCON_Y_SAMPLE_SIZE(s) ((10 - (s)) << 4) 458c2ecf20Sopenharmony_ci#define LPC32XX_TSC_ADCCON_POWER_UP (1 << 2) 468c2ecf20Sopenharmony_ci#define LPC32XX_TSC_ADCCON_AUTO_EN (1 << 0) 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#define LPC32XX_TSC_FIFO_TS_P_LEVEL (1 << 31) 498c2ecf20Sopenharmony_ci#define LPC32XX_TSC_FIFO_NORMALIZE_X_VAL(x) (((x) & 0x03FF0000) >> 16) 508c2ecf20Sopenharmony_ci#define LPC32XX_TSC_FIFO_NORMALIZE_Y_VAL(y) ((y) & 0x000003FF) 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#define LPC32XX_TSC_ADCDAT_VALUE_MASK 0x000003FF 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci#define LPC32XX_TSC_MIN_XY_VAL 0x0 558c2ecf20Sopenharmony_ci#define LPC32XX_TSC_MAX_XY_VAL 0x3FF 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci#define MOD_NAME "ts-lpc32xx" 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci#define tsc_readl(dev, reg) \ 608c2ecf20Sopenharmony_ci __raw_readl((dev)->tsc_base + (reg)) 618c2ecf20Sopenharmony_ci#define tsc_writel(dev, reg, val) \ 628c2ecf20Sopenharmony_ci __raw_writel((val), (dev)->tsc_base + (reg)) 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistruct lpc32xx_tsc { 658c2ecf20Sopenharmony_ci struct input_dev *dev; 668c2ecf20Sopenharmony_ci void __iomem *tsc_base; 678c2ecf20Sopenharmony_ci int irq; 688c2ecf20Sopenharmony_ci struct clk *clk; 698c2ecf20Sopenharmony_ci}; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic void lpc32xx_fifo_clear(struct lpc32xx_tsc *tsc) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci while (!(tsc_readl(tsc, LPC32XX_TSC_STAT) & 748c2ecf20Sopenharmony_ci LPC32XX_TSC_STAT_FIFO_EMPTY)) 758c2ecf20Sopenharmony_ci tsc_readl(tsc, LPC32XX_TSC_FIFO); 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic irqreturn_t lpc32xx_ts_interrupt(int irq, void *dev_id) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci u32 tmp, rv[4], xs[4], ys[4]; 818c2ecf20Sopenharmony_ci int idx; 828c2ecf20Sopenharmony_ci struct lpc32xx_tsc *tsc = dev_id; 838c2ecf20Sopenharmony_ci struct input_dev *input = tsc->dev; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci tmp = tsc_readl(tsc, LPC32XX_TSC_STAT); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci if (tmp & LPC32XX_TSC_STAT_FIFO_OVRRN) { 888c2ecf20Sopenharmony_ci /* FIFO overflow - throw away samples */ 898c2ecf20Sopenharmony_ci lpc32xx_fifo_clear(tsc); 908c2ecf20Sopenharmony_ci return IRQ_HANDLED; 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci /* 948c2ecf20Sopenharmony_ci * Gather and normalize 4 samples. Pen-up events may have less 958c2ecf20Sopenharmony_ci * than 4 samples, but its ok to pop 4 and let the last sample 968c2ecf20Sopenharmony_ci * pen status check drop the samples. 978c2ecf20Sopenharmony_ci */ 988c2ecf20Sopenharmony_ci idx = 0; 998c2ecf20Sopenharmony_ci while (idx < 4 && 1008c2ecf20Sopenharmony_ci !(tsc_readl(tsc, LPC32XX_TSC_STAT) & 1018c2ecf20Sopenharmony_ci LPC32XX_TSC_STAT_FIFO_EMPTY)) { 1028c2ecf20Sopenharmony_ci tmp = tsc_readl(tsc, LPC32XX_TSC_FIFO); 1038c2ecf20Sopenharmony_ci xs[idx] = LPC32XX_TSC_ADCDAT_VALUE_MASK - 1048c2ecf20Sopenharmony_ci LPC32XX_TSC_FIFO_NORMALIZE_X_VAL(tmp); 1058c2ecf20Sopenharmony_ci ys[idx] = LPC32XX_TSC_ADCDAT_VALUE_MASK - 1068c2ecf20Sopenharmony_ci LPC32XX_TSC_FIFO_NORMALIZE_Y_VAL(tmp); 1078c2ecf20Sopenharmony_ci rv[idx] = tmp; 1088c2ecf20Sopenharmony_ci idx++; 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci /* Data is only valid if pen is still down in last sample */ 1128c2ecf20Sopenharmony_ci if (!(rv[3] & LPC32XX_TSC_FIFO_TS_P_LEVEL) && idx == 4) { 1138c2ecf20Sopenharmony_ci /* Use average of 2nd and 3rd sample for position */ 1148c2ecf20Sopenharmony_ci input_report_abs(input, ABS_X, (xs[1] + xs[2]) / 2); 1158c2ecf20Sopenharmony_ci input_report_abs(input, ABS_Y, (ys[1] + ys[2]) / 2); 1168c2ecf20Sopenharmony_ci input_report_key(input, BTN_TOUCH, 1); 1178c2ecf20Sopenharmony_ci } else { 1188c2ecf20Sopenharmony_ci input_report_key(input, BTN_TOUCH, 0); 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci input_sync(input); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic void lpc32xx_stop_tsc(struct lpc32xx_tsc *tsc) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci /* Disable auto mode */ 1298c2ecf20Sopenharmony_ci tsc_writel(tsc, LPC32XX_TSC_CON, 1308c2ecf20Sopenharmony_ci tsc_readl(tsc, LPC32XX_TSC_CON) & 1318c2ecf20Sopenharmony_ci ~LPC32XX_TSC_ADCCON_AUTO_EN); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci clk_disable_unprepare(tsc->clk); 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic int lpc32xx_setup_tsc(struct lpc32xx_tsc *tsc) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci u32 tmp; 1398c2ecf20Sopenharmony_ci int err; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci err = clk_prepare_enable(tsc->clk); 1428c2ecf20Sopenharmony_ci if (err) 1438c2ecf20Sopenharmony_ci return err; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci tmp = tsc_readl(tsc, LPC32XX_TSC_CON) & ~LPC32XX_TSC_ADCCON_POWER_UP; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci /* Set the TSC FIFO depth to 4 samples @ 10-bits per sample (max) */ 1488c2ecf20Sopenharmony_ci tmp = LPC32XX_TSC_ADCCON_IRQ_TO_FIFO_4 | 1498c2ecf20Sopenharmony_ci LPC32XX_TSC_ADCCON_X_SAMPLE_SIZE(10) | 1508c2ecf20Sopenharmony_ci LPC32XX_TSC_ADCCON_Y_SAMPLE_SIZE(10); 1518c2ecf20Sopenharmony_ci tsc_writel(tsc, LPC32XX_TSC_CON, tmp); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci /* These values are all preset */ 1548c2ecf20Sopenharmony_ci tsc_writel(tsc, LPC32XX_TSC_SEL, LPC32XX_TSC_SEL_DEFVAL); 1558c2ecf20Sopenharmony_ci tsc_writel(tsc, LPC32XX_TSC_MIN_X, LPC32XX_TSC_MIN_XY_VAL); 1568c2ecf20Sopenharmony_ci tsc_writel(tsc, LPC32XX_TSC_MAX_X, LPC32XX_TSC_MAX_XY_VAL); 1578c2ecf20Sopenharmony_ci tsc_writel(tsc, LPC32XX_TSC_MIN_Y, LPC32XX_TSC_MIN_XY_VAL); 1588c2ecf20Sopenharmony_ci tsc_writel(tsc, LPC32XX_TSC_MAX_Y, LPC32XX_TSC_MAX_XY_VAL); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci /* Aux support is not used */ 1618c2ecf20Sopenharmony_ci tsc_writel(tsc, LPC32XX_TSC_AUX_UTR, 0); 1628c2ecf20Sopenharmony_ci tsc_writel(tsc, LPC32XX_TSC_AUX_MIN, 0); 1638c2ecf20Sopenharmony_ci tsc_writel(tsc, LPC32XX_TSC_AUX_MAX, 0); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci /* 1668c2ecf20Sopenharmony_ci * Set sample rate to about 240Hz per X/Y pair. A single measurement 1678c2ecf20Sopenharmony_ci * consists of 4 pairs which gives about a 60Hz sample rate based on 1688c2ecf20Sopenharmony_ci * a stable 32768Hz clock source. Values are in clocks. 1698c2ecf20Sopenharmony_ci * Rate is (32768 / (RTR + XCONV + RTR + YCONV + DXP + TTR + UTR) / 4 1708c2ecf20Sopenharmony_ci */ 1718c2ecf20Sopenharmony_ci tsc_writel(tsc, LPC32XX_TSC_RTR, 0x2); 1728c2ecf20Sopenharmony_ci tsc_writel(tsc, LPC32XX_TSC_DTR, 0x2); 1738c2ecf20Sopenharmony_ci tsc_writel(tsc, LPC32XX_TSC_TTR, 0x10); 1748c2ecf20Sopenharmony_ci tsc_writel(tsc, LPC32XX_TSC_DXP, 0x4); 1758c2ecf20Sopenharmony_ci tsc_writel(tsc, LPC32XX_TSC_UTR, 88); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci lpc32xx_fifo_clear(tsc); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci /* Enable automatic ts event capture */ 1808c2ecf20Sopenharmony_ci tsc_writel(tsc, LPC32XX_TSC_CON, tmp | LPC32XX_TSC_ADCCON_AUTO_EN); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci return 0; 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic int lpc32xx_ts_open(struct input_dev *dev) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci struct lpc32xx_tsc *tsc = input_get_drvdata(dev); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci return lpc32xx_setup_tsc(tsc); 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic void lpc32xx_ts_close(struct input_dev *dev) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci struct lpc32xx_tsc *tsc = input_get_drvdata(dev); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci lpc32xx_stop_tsc(tsc); 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cistatic int lpc32xx_ts_probe(struct platform_device *pdev) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci struct lpc32xx_tsc *tsc; 2028c2ecf20Sopenharmony_ci struct input_dev *input; 2038c2ecf20Sopenharmony_ci struct resource *res; 2048c2ecf20Sopenharmony_ci resource_size_t size; 2058c2ecf20Sopenharmony_ci int irq; 2068c2ecf20Sopenharmony_ci int error; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 2098c2ecf20Sopenharmony_ci if (!res) { 2108c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Can't get memory resource\n"); 2118c2ecf20Sopenharmony_ci return -ENOENT; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 2158c2ecf20Sopenharmony_ci if (irq < 0) 2168c2ecf20Sopenharmony_ci return irq; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci tsc = kzalloc(sizeof(*tsc), GFP_KERNEL); 2198c2ecf20Sopenharmony_ci input = input_allocate_device(); 2208c2ecf20Sopenharmony_ci if (!tsc || !input) { 2218c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed allocating memory\n"); 2228c2ecf20Sopenharmony_ci error = -ENOMEM; 2238c2ecf20Sopenharmony_ci goto err_free_mem; 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci tsc->dev = input; 2278c2ecf20Sopenharmony_ci tsc->irq = irq; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci size = resource_size(res); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci if (!request_mem_region(res->start, size, pdev->name)) { 2328c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "TSC registers are not free\n"); 2338c2ecf20Sopenharmony_ci error = -EBUSY; 2348c2ecf20Sopenharmony_ci goto err_free_mem; 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci tsc->tsc_base = ioremap(res->start, size); 2388c2ecf20Sopenharmony_ci if (!tsc->tsc_base) { 2398c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Can't map memory\n"); 2408c2ecf20Sopenharmony_ci error = -ENOMEM; 2418c2ecf20Sopenharmony_ci goto err_release_mem; 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci tsc->clk = clk_get(&pdev->dev, NULL); 2458c2ecf20Sopenharmony_ci if (IS_ERR(tsc->clk)) { 2468c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed getting clock\n"); 2478c2ecf20Sopenharmony_ci error = PTR_ERR(tsc->clk); 2488c2ecf20Sopenharmony_ci goto err_unmap; 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci input->name = MOD_NAME; 2528c2ecf20Sopenharmony_ci input->phys = "lpc32xx/input0"; 2538c2ecf20Sopenharmony_ci input->id.bustype = BUS_HOST; 2548c2ecf20Sopenharmony_ci input->id.vendor = 0x0001; 2558c2ecf20Sopenharmony_ci input->id.product = 0x0002; 2568c2ecf20Sopenharmony_ci input->id.version = 0x0100; 2578c2ecf20Sopenharmony_ci input->dev.parent = &pdev->dev; 2588c2ecf20Sopenharmony_ci input->open = lpc32xx_ts_open; 2598c2ecf20Sopenharmony_ci input->close = lpc32xx_ts_close; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); 2628c2ecf20Sopenharmony_ci input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); 2638c2ecf20Sopenharmony_ci input_set_abs_params(input, ABS_X, LPC32XX_TSC_MIN_XY_VAL, 2648c2ecf20Sopenharmony_ci LPC32XX_TSC_MAX_XY_VAL, 0, 0); 2658c2ecf20Sopenharmony_ci input_set_abs_params(input, ABS_Y, LPC32XX_TSC_MIN_XY_VAL, 2668c2ecf20Sopenharmony_ci LPC32XX_TSC_MAX_XY_VAL, 0, 0); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci input_set_drvdata(input, tsc); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci error = request_irq(tsc->irq, lpc32xx_ts_interrupt, 2718c2ecf20Sopenharmony_ci 0, pdev->name, tsc); 2728c2ecf20Sopenharmony_ci if (error) { 2738c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed requesting interrupt\n"); 2748c2ecf20Sopenharmony_ci goto err_put_clock; 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci error = input_register_device(input); 2788c2ecf20Sopenharmony_ci if (error) { 2798c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed registering input device\n"); 2808c2ecf20Sopenharmony_ci goto err_free_irq; 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, tsc); 2848c2ecf20Sopenharmony_ci device_init_wakeup(&pdev->dev, 1); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci return 0; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_cierr_free_irq: 2898c2ecf20Sopenharmony_ci free_irq(tsc->irq, tsc); 2908c2ecf20Sopenharmony_cierr_put_clock: 2918c2ecf20Sopenharmony_ci clk_put(tsc->clk); 2928c2ecf20Sopenharmony_cierr_unmap: 2938c2ecf20Sopenharmony_ci iounmap(tsc->tsc_base); 2948c2ecf20Sopenharmony_cierr_release_mem: 2958c2ecf20Sopenharmony_ci release_mem_region(res->start, size); 2968c2ecf20Sopenharmony_cierr_free_mem: 2978c2ecf20Sopenharmony_ci input_free_device(input); 2988c2ecf20Sopenharmony_ci kfree(tsc); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci return error; 3018c2ecf20Sopenharmony_ci} 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cistatic int lpc32xx_ts_remove(struct platform_device *pdev) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci struct lpc32xx_tsc *tsc = platform_get_drvdata(pdev); 3068c2ecf20Sopenharmony_ci struct resource *res; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci free_irq(tsc->irq, tsc); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci input_unregister_device(tsc->dev); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci clk_put(tsc->clk); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci iounmap(tsc->tsc_base); 3158c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 3168c2ecf20Sopenharmony_ci release_mem_region(res->start, resource_size(res)); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci kfree(tsc); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci return 0; 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 3248c2ecf20Sopenharmony_cistatic int lpc32xx_ts_suspend(struct device *dev) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci struct lpc32xx_tsc *tsc = dev_get_drvdata(dev); 3278c2ecf20Sopenharmony_ci struct input_dev *input = tsc->dev; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci /* 3308c2ecf20Sopenharmony_ci * Suspend and resume can be called when the device hasn't been 3318c2ecf20Sopenharmony_ci * enabled. If there are no users that have the device open, then 3328c2ecf20Sopenharmony_ci * avoid calling the TSC stop and start functions as the TSC 3338c2ecf20Sopenharmony_ci * isn't yet clocked. 3348c2ecf20Sopenharmony_ci */ 3358c2ecf20Sopenharmony_ci mutex_lock(&input->mutex); 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci if (input->users) { 3388c2ecf20Sopenharmony_ci if (device_may_wakeup(dev)) 3398c2ecf20Sopenharmony_ci enable_irq_wake(tsc->irq); 3408c2ecf20Sopenharmony_ci else 3418c2ecf20Sopenharmony_ci lpc32xx_stop_tsc(tsc); 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci mutex_unlock(&input->mutex); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci return 0; 3478c2ecf20Sopenharmony_ci} 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_cistatic int lpc32xx_ts_resume(struct device *dev) 3508c2ecf20Sopenharmony_ci{ 3518c2ecf20Sopenharmony_ci struct lpc32xx_tsc *tsc = dev_get_drvdata(dev); 3528c2ecf20Sopenharmony_ci struct input_dev *input = tsc->dev; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci mutex_lock(&input->mutex); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci if (input->users) { 3578c2ecf20Sopenharmony_ci if (device_may_wakeup(dev)) 3588c2ecf20Sopenharmony_ci disable_irq_wake(tsc->irq); 3598c2ecf20Sopenharmony_ci else 3608c2ecf20Sopenharmony_ci lpc32xx_setup_tsc(tsc); 3618c2ecf20Sopenharmony_ci } 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci mutex_unlock(&input->mutex); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci return 0; 3668c2ecf20Sopenharmony_ci} 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_cistatic const struct dev_pm_ops lpc32xx_ts_pm_ops = { 3698c2ecf20Sopenharmony_ci .suspend = lpc32xx_ts_suspend, 3708c2ecf20Sopenharmony_ci .resume = lpc32xx_ts_resume, 3718c2ecf20Sopenharmony_ci}; 3728c2ecf20Sopenharmony_ci#define LPC32XX_TS_PM_OPS (&lpc32xx_ts_pm_ops) 3738c2ecf20Sopenharmony_ci#else 3748c2ecf20Sopenharmony_ci#define LPC32XX_TS_PM_OPS NULL 3758c2ecf20Sopenharmony_ci#endif 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 3788c2ecf20Sopenharmony_cistatic const struct of_device_id lpc32xx_tsc_of_match[] = { 3798c2ecf20Sopenharmony_ci { .compatible = "nxp,lpc3220-tsc", }, 3808c2ecf20Sopenharmony_ci { }, 3818c2ecf20Sopenharmony_ci}; 3828c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, lpc32xx_tsc_of_match); 3838c2ecf20Sopenharmony_ci#endif 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_cistatic struct platform_driver lpc32xx_ts_driver = { 3868c2ecf20Sopenharmony_ci .probe = lpc32xx_ts_probe, 3878c2ecf20Sopenharmony_ci .remove = lpc32xx_ts_remove, 3888c2ecf20Sopenharmony_ci .driver = { 3898c2ecf20Sopenharmony_ci .name = MOD_NAME, 3908c2ecf20Sopenharmony_ci .pm = LPC32XX_TS_PM_OPS, 3918c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(lpc32xx_tsc_of_match), 3928c2ecf20Sopenharmony_ci }, 3938c2ecf20Sopenharmony_ci}; 3948c2ecf20Sopenharmony_cimodule_platform_driver(lpc32xx_ts_driver); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ciMODULE_AUTHOR("Kevin Wells <kevin.wells@nxp.com"); 3978c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("LPC32XX TSC Driver"); 3988c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 3998c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:lpc32xx_ts"); 400