linux/drivers/input/touchscreen/w90p910_ts.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (c) 2008 Nuvoton technology corporation.
   4 *
   5 * Wan ZongShun <mcuos.com@gmail.com>
   6 */
   7
   8#include <linux/delay.h>
   9#include <linux/module.h>
  10#include <linux/platform_device.h>
  11#include <linux/io.h>
  12#include <linux/clk.h>
  13#include <linux/input.h>
  14#include <linux/interrupt.h>
  15#include <linux/slab.h>
  16
  17/* ADC controller bit defines */
  18#define ADC_DELAY       0xf00
  19#define ADC_DOWN        0x01
  20#define ADC_TSC_Y       (0x01 << 8)
  21#define ADC_TSC_X       (0x00 << 8)
  22#define TSC_FOURWIRE    (~(0x03 << 1))
  23#define ADC_CLK_EN      (0x01 << 28)    /* ADC clock enable */
  24#define ADC_READ_CON    (0x01 << 12)
  25#define ADC_CONV        (0x01 << 13)
  26#define ADC_SEMIAUTO    (0x01 << 14)
  27#define ADC_WAITTRIG    (0x03 << 14)
  28#define ADC_RST1        (0x01 << 16)
  29#define ADC_RST0        (0x00 << 16)
  30#define ADC_EN          (0x01 << 17)
  31#define ADC_INT         (0x01 << 18)
  32#define WT_INT          (0x01 << 20)
  33#define ADC_INT_EN      (0x01 << 21)
  34#define LVD_INT_EN      (0x01 << 22)
  35#define WT_INT_EN       (0x01 << 23)
  36#define ADC_DIV         (0x04 << 1)     /* div = 6 */
  37
  38enum ts_state {
  39        TS_WAIT_NEW_PACKET,     /* We are waiting next touch report */
  40        TS_WAIT_X_COORD,        /* We are waiting for ADC to report X coord */
  41        TS_WAIT_Y_COORD,        /* We are waiting for ADC to report Y coord */
  42        TS_IDLE,                /* Input device is closed, don't do anything */
  43};
  44
  45struct w90p910_ts {
  46        struct input_dev *input;
  47        struct timer_list timer;
  48        struct clk *clk;
  49        int irq_num;
  50        void __iomem *ts_reg;
  51        spinlock_t lock;
  52        enum ts_state state;
  53};
  54
  55static void w90p910_report_event(struct w90p910_ts *w90p910_ts, bool down)
  56{
  57        struct input_dev *dev = w90p910_ts->input;
  58
  59        if (down) {
  60                input_report_abs(dev, ABS_X,
  61                                 __raw_readl(w90p910_ts->ts_reg + 0x0c));
  62                input_report_abs(dev, ABS_Y,
  63                                 __raw_readl(w90p910_ts->ts_reg + 0x10));
  64        }
  65
  66        input_report_key(dev, BTN_TOUCH, down);
  67        input_sync(dev);
  68}
  69
  70static void w90p910_prepare_x_reading(struct w90p910_ts *w90p910_ts)
  71{
  72        unsigned long ctlreg;
  73
  74        __raw_writel(ADC_TSC_X, w90p910_ts->ts_reg + 0x04);
  75        ctlreg = __raw_readl(w90p910_ts->ts_reg);
  76        ctlreg &= ~(ADC_WAITTRIG | WT_INT | WT_INT_EN);
  77        ctlreg |= ADC_SEMIAUTO | ADC_INT_EN | ADC_CONV;
  78        __raw_writel(ctlreg, w90p910_ts->ts_reg);
  79
  80        w90p910_ts->state = TS_WAIT_X_COORD;
  81}
  82
  83static void w90p910_prepare_y_reading(struct w90p910_ts *w90p910_ts)
  84{
  85        unsigned long ctlreg;
  86
  87        __raw_writel(ADC_TSC_Y, w90p910_ts->ts_reg + 0x04);
  88        ctlreg = __raw_readl(w90p910_ts->ts_reg);
  89        ctlreg &= ~(ADC_WAITTRIG | ADC_INT | WT_INT_EN);
  90        ctlreg |= ADC_SEMIAUTO | ADC_INT_EN | ADC_CONV;
  91        __raw_writel(ctlreg, w90p910_ts->ts_reg);
  92
  93        w90p910_ts->state = TS_WAIT_Y_COORD;
  94}
  95
  96static void w90p910_prepare_next_packet(struct w90p910_ts *w90p910_ts)
  97{
  98        unsigned long ctlreg;
  99
 100        ctlreg = __raw_readl(w90p910_ts->ts_reg);
 101        ctlreg &= ~(ADC_INT | ADC_INT_EN | ADC_SEMIAUTO | ADC_CONV);
 102        ctlreg |= ADC_WAITTRIG | WT_INT_EN;
 103        __raw_writel(ctlreg, w90p910_ts->ts_reg);
 104
 105        w90p910_ts->state = TS_WAIT_NEW_PACKET;
 106}
 107
 108static irqreturn_t w90p910_ts_interrupt(int irq, void *dev_id)
 109{
 110        struct w90p910_ts *w90p910_ts = dev_id;
 111        unsigned long flags;
 112
 113        spin_lock_irqsave(&w90p910_ts->lock, flags);
 114
 115        switch (w90p910_ts->state) {
 116        case TS_WAIT_NEW_PACKET:
 117                /*
 118                 * The controller only generates interrupts when pen
 119                 * is down.
 120                 */
 121                del_timer(&w90p910_ts->timer);
 122                w90p910_prepare_x_reading(w90p910_ts);
 123                break;
 124
 125
 126        case TS_WAIT_X_COORD:
 127                w90p910_prepare_y_reading(w90p910_ts);
 128                break;
 129
 130        case TS_WAIT_Y_COORD:
 131                w90p910_report_event(w90p910_ts, true);
 132                w90p910_prepare_next_packet(w90p910_ts);
 133                mod_timer(&w90p910_ts->timer, jiffies + msecs_to_jiffies(100));
 134                break;
 135
 136        case TS_IDLE:
 137                break;
 138        }
 139
 140        spin_unlock_irqrestore(&w90p910_ts->lock, flags);
 141
 142        return IRQ_HANDLED;
 143}
 144
 145static void w90p910_check_pen_up(struct timer_list *t)
 146{
 147        struct w90p910_ts *w90p910_ts = from_timer(w90p910_ts, t, timer);
 148        unsigned long flags;
 149
 150        spin_lock_irqsave(&w90p910_ts->lock, flags);
 151
 152        if (w90p910_ts->state == TS_WAIT_NEW_PACKET &&
 153            !(__raw_readl(w90p910_ts->ts_reg + 0x04) & ADC_DOWN)) {
 154
 155                w90p910_report_event(w90p910_ts, false);
 156        }
 157
 158        spin_unlock_irqrestore(&w90p910_ts->lock, flags);
 159}
 160
 161static int w90p910_open(struct input_dev *dev)
 162{
 163        struct w90p910_ts *w90p910_ts = input_get_drvdata(dev);
 164        unsigned long val;
 165
 166        /* enable the ADC clock */
 167        clk_enable(w90p910_ts->clk);
 168
 169        __raw_writel(ADC_RST1, w90p910_ts->ts_reg);
 170        msleep(1);
 171        __raw_writel(ADC_RST0, w90p910_ts->ts_reg);
 172        msleep(1);
 173
 174        /* set delay and screen type */
 175        val = __raw_readl(w90p910_ts->ts_reg + 0x04);
 176        __raw_writel(val & TSC_FOURWIRE, w90p910_ts->ts_reg + 0x04);
 177        __raw_writel(ADC_DELAY, w90p910_ts->ts_reg + 0x08);
 178
 179        w90p910_ts->state = TS_WAIT_NEW_PACKET;
 180        wmb();
 181
 182        /* set trigger mode */
 183        val = __raw_readl(w90p910_ts->ts_reg);
 184        val |= ADC_WAITTRIG | ADC_DIV | ADC_EN | WT_INT_EN;
 185        __raw_writel(val, w90p910_ts->ts_reg);
 186
 187        return 0;
 188}
 189
 190static void w90p910_close(struct input_dev *dev)
 191{
 192        struct w90p910_ts *w90p910_ts = input_get_drvdata(dev);
 193        unsigned long val;
 194
 195        /* disable trigger mode */
 196
 197        spin_lock_irq(&w90p910_ts->lock);
 198
 199        w90p910_ts->state = TS_IDLE;
 200
 201        val = __raw_readl(w90p910_ts->ts_reg);
 202        val &= ~(ADC_WAITTRIG | ADC_DIV | ADC_EN | WT_INT_EN | ADC_INT_EN);
 203        __raw_writel(val, w90p910_ts->ts_reg);
 204
 205        spin_unlock_irq(&w90p910_ts->lock);
 206
 207        /* Now that interrupts are shut off we can safely delete timer */
 208        del_timer_sync(&w90p910_ts->timer);
 209
 210        /* stop the ADC clock */
 211        clk_disable(w90p910_ts->clk);
 212}
 213
 214static int w90x900ts_probe(struct platform_device *pdev)
 215{
 216        struct w90p910_ts *w90p910_ts;
 217        struct input_dev *input_dev;
 218        struct resource *res;
 219        int err;
 220
 221        w90p910_ts = kzalloc(sizeof(struct w90p910_ts), GFP_KERNEL);
 222        input_dev = input_allocate_device();
 223        if (!w90p910_ts || !input_dev) {
 224                err = -ENOMEM;
 225                goto fail1;
 226        }
 227
 228        w90p910_ts->input = input_dev;
 229        w90p910_ts->state = TS_IDLE;
 230        spin_lock_init(&w90p910_ts->lock);
 231        timer_setup(&w90p910_ts->timer, w90p910_check_pen_up, 0);
 232
 233        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 234        if (!res) {
 235                err = -ENXIO;
 236                goto fail1;
 237        }
 238
 239        if (!request_mem_region(res->start, resource_size(res),
 240                                pdev->name)) {
 241                err = -EBUSY;
 242                goto fail1;
 243        }
 244
 245        w90p910_ts->ts_reg = ioremap(res->start, resource_size(res));
 246        if (!w90p910_ts->ts_reg) {
 247                err = -ENOMEM;
 248                goto fail2;
 249        }
 250
 251        w90p910_ts->clk = clk_get(&pdev->dev, NULL);
 252        if (IS_ERR(w90p910_ts->clk)) {
 253                err = PTR_ERR(w90p910_ts->clk);
 254                goto fail3;
 255        }
 256
 257        input_dev->name = "W90P910 TouchScreen";
 258        input_dev->phys = "w90p910ts/event0";
 259        input_dev->id.bustype = BUS_HOST;
 260        input_dev->id.vendor  = 0x0005;
 261        input_dev->id.product = 0x0001;
 262        input_dev->id.version = 0x0100;
 263        input_dev->dev.parent = &pdev->dev;
 264        input_dev->open = w90p910_open;
 265        input_dev->close = w90p910_close;
 266
 267        input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
 268        input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
 269
 270        input_set_abs_params(input_dev, ABS_X, 0, 0x400, 0, 0);
 271        input_set_abs_params(input_dev, ABS_Y, 0, 0x400, 0, 0);
 272
 273        input_set_drvdata(input_dev, w90p910_ts);
 274
 275        w90p910_ts->irq_num = platform_get_irq(pdev, 0);
 276        if (request_irq(w90p910_ts->irq_num, w90p910_ts_interrupt,
 277                        0, "w90p910ts", w90p910_ts)) {
 278                err = -EBUSY;
 279                goto fail4;
 280        }
 281
 282        err = input_register_device(w90p910_ts->input);
 283        if (err)
 284                goto fail5;
 285
 286        platform_set_drvdata(pdev, w90p910_ts);
 287
 288        return 0;
 289
 290fail5:  free_irq(w90p910_ts->irq_num, w90p910_ts);
 291fail4:  clk_put(w90p910_ts->clk);
 292fail3:  iounmap(w90p910_ts->ts_reg);
 293fail2:  release_mem_region(res->start, resource_size(res));
 294fail1:  input_free_device(input_dev);
 295        kfree(w90p910_ts);
 296        return err;
 297}
 298
 299static int w90x900ts_remove(struct platform_device *pdev)
 300{
 301        struct w90p910_ts *w90p910_ts = platform_get_drvdata(pdev);
 302        struct resource *res;
 303
 304        free_irq(w90p910_ts->irq_num, w90p910_ts);
 305        del_timer_sync(&w90p910_ts->timer);
 306        iounmap(w90p910_ts->ts_reg);
 307
 308        clk_put(w90p910_ts->clk);
 309
 310        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 311        release_mem_region(res->start, resource_size(res));
 312
 313        input_unregister_device(w90p910_ts->input);
 314        kfree(w90p910_ts);
 315
 316        return 0;
 317}
 318
 319static struct platform_driver w90x900ts_driver = {
 320        .probe          = w90x900ts_probe,
 321        .remove         = w90x900ts_remove,
 322        .driver         = {
 323                .name   = "nuc900-ts",
 324        },
 325};
 326module_platform_driver(w90x900ts_driver);
 327
 328MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
 329MODULE_DESCRIPTION("w90p910 touch screen driver!");
 330MODULE_LICENSE("GPL");
 331MODULE_ALIAS("platform:nuc900-ts");
 332