cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

ucb1x00-ts.c (10943B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 *  Touchscreen driver for UCB1x00-based touchscreens
      4 *
      5 *  Copyright (C) 2001 Russell King, All Rights Reserved.
      6 *  Copyright (C) 2005 Pavel Machek
      7 *
      8 * 21-Jan-2002 <jco@ict.es> :
      9 *
     10 * Added support for synchronous A/D mode. This mode is useful to
     11 * avoid noise induced in the touchpanel by the LCD, provided that
     12 * the UCB1x00 has a valid LCD sync signal routed to its ADCSYNC pin.
     13 * It is important to note that the signal connected to the ADCSYNC
     14 * pin should provide pulses even when the LCD is blanked, otherwise
     15 * a pen touch needed to unblank the LCD will never be read.
     16 */
     17#include <linux/module.h>
     18#include <linux/moduleparam.h>
     19#include <linux/init.h>
     20#include <linux/interrupt.h>
     21#include <linux/sched.h>
     22#include <linux/spinlock.h>
     23#include <linux/completion.h>
     24#include <linux/delay.h>
     25#include <linux/string.h>
     26#include <linux/input.h>
     27#include <linux/device.h>
     28#include <linux/freezer.h>
     29#include <linux/slab.h>
     30#include <linux/kthread.h>
     31#include <linux/mfd/ucb1x00.h>
     32
     33#include <mach/collie.h>
     34#include <asm/mach-types.h>
     35
     36
     37
     38struct ucb1x00_ts {
     39	struct input_dev	*idev;
     40	struct ucb1x00		*ucb;
     41
     42	spinlock_t		irq_lock;
     43	unsigned		irq_disabled;
     44	wait_queue_head_t	irq_wait;
     45	struct task_struct	*rtask;
     46	u16			x_res;
     47	u16			y_res;
     48
     49	unsigned int		adcsync:1;
     50};
     51
     52static int adcsync;
     53
     54static inline void ucb1x00_ts_evt_add(struct ucb1x00_ts *ts, u16 pressure, u16 x, u16 y)
     55{
     56	struct input_dev *idev = ts->idev;
     57
     58	input_report_abs(idev, ABS_X, x);
     59	input_report_abs(idev, ABS_Y, y);
     60	input_report_abs(idev, ABS_PRESSURE, pressure);
     61	input_report_key(idev, BTN_TOUCH, 1);
     62	input_sync(idev);
     63}
     64
     65static inline void ucb1x00_ts_event_release(struct ucb1x00_ts *ts)
     66{
     67	struct input_dev *idev = ts->idev;
     68
     69	input_report_abs(idev, ABS_PRESSURE, 0);
     70	input_report_key(idev, BTN_TOUCH, 0);
     71	input_sync(idev);
     72}
     73
     74/*
     75 * Switch to interrupt mode.
     76 */
     77static inline void ucb1x00_ts_mode_int(struct ucb1x00_ts *ts)
     78{
     79	ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
     80			UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW |
     81			UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND |
     82			UCB_TS_CR_MODE_INT);
     83}
     84
     85/*
     86 * Switch to pressure mode, and read pressure.  We don't need to wait
     87 * here, since both plates are being driven.
     88 */
     89static inline unsigned int ucb1x00_ts_read_pressure(struct ucb1x00_ts *ts)
     90{
     91	if (machine_is_collie()) {
     92		ucb1x00_io_write(ts->ucb, COLLIE_TC35143_GPIO_TBL_CHK, 0);
     93		ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
     94				  UCB_TS_CR_TSPX_POW | UCB_TS_CR_TSMX_POW |
     95				  UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA);
     96
     97		udelay(55);
     98
     99		return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_AD2, ts->adcsync);
    100	} else {
    101		ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
    102				  UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW |
    103				  UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND |
    104				  UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
    105
    106		return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync);
    107	}
    108}
    109
    110/*
    111 * Switch to X position mode and measure Y plate.  We switch the plate
    112 * configuration in pressure mode, then switch to position mode.  This
    113 * gives a faster response time.  Even so, we need to wait about 55us
    114 * for things to stabilise.
    115 */
    116static inline unsigned int ucb1x00_ts_read_xpos(struct ucb1x00_ts *ts)
    117{
    118	if (machine_is_collie())
    119		ucb1x00_io_write(ts->ucb, 0, COLLIE_TC35143_GPIO_TBL_CHK);
    120	else {
    121		ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
    122				  UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
    123				  UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
    124		ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
    125				  UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
    126				  UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
    127	}
    128	ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
    129			UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
    130			UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA);
    131
    132	udelay(55);
    133
    134	return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync);
    135}
    136
    137/*
    138 * Switch to Y position mode and measure X plate.  We switch the plate
    139 * configuration in pressure mode, then switch to position mode.  This
    140 * gives a faster response time.  Even so, we need to wait about 55us
    141 * for things to stabilise.
    142 */
    143static inline unsigned int ucb1x00_ts_read_ypos(struct ucb1x00_ts *ts)
    144{
    145	if (machine_is_collie())
    146		ucb1x00_io_write(ts->ucb, 0, COLLIE_TC35143_GPIO_TBL_CHK);
    147	else {
    148		ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
    149				  UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
    150				  UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
    151		ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
    152				  UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
    153				  UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
    154	}
    155
    156	ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
    157			UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
    158			UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA);
    159
    160	udelay(55);
    161
    162	return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPX, ts->adcsync);
    163}
    164
    165/*
    166 * Switch to X plate resistance mode.  Set MX to ground, PX to
    167 * supply.  Measure current.
    168 */
    169static inline unsigned int ucb1x00_ts_read_xres(struct ucb1x00_ts *ts)
    170{
    171	ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
    172			UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
    173			UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
    174	return ucb1x00_adc_read(ts->ucb, 0, ts->adcsync);
    175}
    176
    177/*
    178 * Switch to Y plate resistance mode.  Set MY to ground, PY to
    179 * supply.  Measure current.
    180 */
    181static inline unsigned int ucb1x00_ts_read_yres(struct ucb1x00_ts *ts)
    182{
    183	ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
    184			UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
    185			UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
    186	return ucb1x00_adc_read(ts->ucb, 0, ts->adcsync);
    187}
    188
    189static inline int ucb1x00_ts_pen_down(struct ucb1x00_ts *ts)
    190{
    191	unsigned int val = ucb1x00_reg_read(ts->ucb, UCB_TS_CR);
    192
    193	if (machine_is_collie())
    194		return (!(val & (UCB_TS_CR_TSPX_LOW)));
    195	else
    196		return (val & (UCB_TS_CR_TSPX_LOW | UCB_TS_CR_TSMX_LOW));
    197}
    198
    199/*
    200 * This is a RT kernel thread that handles the ADC accesses
    201 * (mainly so we can use semaphores in the UCB1200 core code
    202 * to serialise accesses to the ADC).
    203 */
    204static int ucb1x00_thread(void *_ts)
    205{
    206	struct ucb1x00_ts *ts = _ts;
    207	DECLARE_WAITQUEUE(wait, current);
    208	bool frozen, ignore = false;
    209	int valid = 0;
    210
    211	set_freezable();
    212	add_wait_queue(&ts->irq_wait, &wait);
    213	while (!kthread_freezable_should_stop(&frozen)) {
    214		unsigned int x, y, p;
    215		signed long timeout;
    216
    217		if (frozen)
    218			ignore = true;
    219
    220		ucb1x00_adc_enable(ts->ucb);
    221
    222		x = ucb1x00_ts_read_xpos(ts);
    223		y = ucb1x00_ts_read_ypos(ts);
    224		p = ucb1x00_ts_read_pressure(ts);
    225
    226		/*
    227		 * Switch back to interrupt mode.
    228		 */
    229		ucb1x00_ts_mode_int(ts);
    230		ucb1x00_adc_disable(ts->ucb);
    231
    232		msleep(10);
    233
    234		ucb1x00_enable(ts->ucb);
    235
    236
    237		if (ucb1x00_ts_pen_down(ts)) {
    238			set_current_state(TASK_INTERRUPTIBLE);
    239
    240			spin_lock_irq(&ts->irq_lock);
    241			if (ts->irq_disabled) {
    242				ts->irq_disabled = 0;
    243				enable_irq(ts->ucb->irq_base + UCB_IRQ_TSPX);
    244			}
    245			spin_unlock_irq(&ts->irq_lock);
    246			ucb1x00_disable(ts->ucb);
    247
    248			/*
    249			 * If we spat out a valid sample set last time,
    250			 * spit out a "pen off" sample here.
    251			 */
    252			if (valid) {
    253				ucb1x00_ts_event_release(ts);
    254				valid = 0;
    255			}
    256
    257			timeout = MAX_SCHEDULE_TIMEOUT;
    258		} else {
    259			ucb1x00_disable(ts->ucb);
    260
    261			/*
    262			 * Filtering is policy.  Policy belongs in user
    263			 * space.  We therefore leave it to user space
    264			 * to do any filtering they please.
    265			 */
    266			if (!ignore) {
    267				ucb1x00_ts_evt_add(ts, p, x, y);
    268				valid = 1;
    269			}
    270
    271			set_current_state(TASK_INTERRUPTIBLE);
    272			timeout = HZ / 100;
    273		}
    274
    275		schedule_timeout(timeout);
    276	}
    277
    278	remove_wait_queue(&ts->irq_wait, &wait);
    279
    280	ts->rtask = NULL;
    281	return 0;
    282}
    283
    284/*
    285 * We only detect touch screen _touches_ with this interrupt
    286 * handler, and even then we just schedule our task.
    287 */
    288static irqreturn_t ucb1x00_ts_irq(int irq, void *id)
    289{
    290	struct ucb1x00_ts *ts = id;
    291
    292	spin_lock(&ts->irq_lock);
    293	ts->irq_disabled = 1;
    294	disable_irq_nosync(ts->ucb->irq_base + UCB_IRQ_TSPX);
    295	spin_unlock(&ts->irq_lock);
    296	wake_up(&ts->irq_wait);
    297
    298	return IRQ_HANDLED;
    299}
    300
    301static int ucb1x00_ts_open(struct input_dev *idev)
    302{
    303	struct ucb1x00_ts *ts = input_get_drvdata(idev);
    304	unsigned long flags = 0;
    305	int ret = 0;
    306
    307	BUG_ON(ts->rtask);
    308
    309	if (machine_is_collie())
    310		flags = IRQF_TRIGGER_RISING;
    311	else
    312		flags = IRQF_TRIGGER_FALLING;
    313
    314	ts->irq_disabled = 0;
    315
    316	init_waitqueue_head(&ts->irq_wait);
    317	ret = request_irq(ts->ucb->irq_base + UCB_IRQ_TSPX, ucb1x00_ts_irq,
    318			  flags, "ucb1x00-ts", ts);
    319	if (ret < 0)
    320		goto out;
    321
    322	/*
    323	 * If we do this at all, we should allow the user to
    324	 * measure and read the X and Y resistance at any time.
    325	 */
    326	ucb1x00_adc_enable(ts->ucb);
    327	ts->x_res = ucb1x00_ts_read_xres(ts);
    328	ts->y_res = ucb1x00_ts_read_yres(ts);
    329	ucb1x00_adc_disable(ts->ucb);
    330
    331	ts->rtask = kthread_run(ucb1x00_thread, ts, "ktsd");
    332	if (!IS_ERR(ts->rtask)) {
    333		ret = 0;
    334	} else {
    335		free_irq(ts->ucb->irq_base + UCB_IRQ_TSPX, ts);
    336		ts->rtask = NULL;
    337		ret = -EFAULT;
    338	}
    339
    340 out:
    341	return ret;
    342}
    343
    344/*
    345 * Release touchscreen resources.  Disable IRQs.
    346 */
    347static void ucb1x00_ts_close(struct input_dev *idev)
    348{
    349	struct ucb1x00_ts *ts = input_get_drvdata(idev);
    350
    351	if (ts->rtask)
    352		kthread_stop(ts->rtask);
    353
    354	ucb1x00_enable(ts->ucb);
    355	free_irq(ts->ucb->irq_base + UCB_IRQ_TSPX, ts);
    356	ucb1x00_reg_write(ts->ucb, UCB_TS_CR, 0);
    357	ucb1x00_disable(ts->ucb);
    358}
    359
    360
    361/*
    362 * Initialisation.
    363 */
    364static int ucb1x00_ts_add(struct ucb1x00_dev *dev)
    365{
    366	struct ucb1x00_ts *ts;
    367	struct input_dev *idev;
    368	int err;
    369
    370	ts = kzalloc(sizeof(struct ucb1x00_ts), GFP_KERNEL);
    371	idev = input_allocate_device();
    372	if (!ts || !idev) {
    373		err = -ENOMEM;
    374		goto fail;
    375	}
    376
    377	ts->ucb = dev->ucb;
    378	ts->idev = idev;
    379	ts->adcsync = adcsync ? UCB_SYNC : UCB_NOSYNC;
    380	spin_lock_init(&ts->irq_lock);
    381
    382	idev->name       = "Touchscreen panel";
    383	idev->id.product = ts->ucb->id;
    384	idev->open       = ucb1x00_ts_open;
    385	idev->close      = ucb1x00_ts_close;
    386	idev->dev.parent = &ts->ucb->dev;
    387
    388	idev->evbit[0]   = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY);
    389	idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
    390
    391	input_set_drvdata(idev, ts);
    392
    393	ucb1x00_adc_enable(ts->ucb);
    394	ts->x_res = ucb1x00_ts_read_xres(ts);
    395	ts->y_res = ucb1x00_ts_read_yres(ts);
    396	ucb1x00_adc_disable(ts->ucb);
    397
    398	input_set_abs_params(idev, ABS_X, 0, ts->x_res, 0, 0);
    399	input_set_abs_params(idev, ABS_Y, 0, ts->y_res, 0, 0);
    400	input_set_abs_params(idev, ABS_PRESSURE, 0, 0, 0, 0);
    401
    402	err = input_register_device(idev);
    403	if (err)
    404		goto fail;
    405
    406	dev->priv = ts;
    407
    408	return 0;
    409
    410 fail:
    411	input_free_device(idev);
    412	kfree(ts);
    413	return err;
    414}
    415
    416static void ucb1x00_ts_remove(struct ucb1x00_dev *dev)
    417{
    418	struct ucb1x00_ts *ts = dev->priv;
    419
    420	input_unregister_device(ts->idev);
    421	kfree(ts);
    422}
    423
    424static struct ucb1x00_driver ucb1x00_ts_driver = {
    425	.add		= ucb1x00_ts_add,
    426	.remove		= ucb1x00_ts_remove,
    427};
    428
    429static int __init ucb1x00_ts_init(void)
    430{
    431	return ucb1x00_register_driver(&ucb1x00_ts_driver);
    432}
    433
    434static void __exit ucb1x00_ts_exit(void)
    435{
    436	ucb1x00_unregister_driver(&ucb1x00_ts_driver);
    437}
    438
    439module_param(adcsync, int, 0444);
    440module_init(ucb1x00_ts_init);
    441module_exit(ucb1x00_ts_exit);
    442
    443MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
    444MODULE_DESCRIPTION("UCB1x00 touchscreen driver");
    445MODULE_LICENSE("GPL");