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

mk712.c (5688B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * ICS MK712 touchscreen controller driver
      4 *
      5 * Copyright (c) 1999-2002 Transmeta Corporation
      6 * Copyright (c) 2005 Rick Koch <n1gp@hotmail.com>
      7 * Copyright (c) 2005 Vojtech Pavlik <vojtech@suse.cz>
      8 */
      9
     10
     11/*
     12 * This driver supports the ICS MicroClock MK712 TouchScreen controller,
     13 * found in Gateway AOL Connected Touchpad computers.
     14 *
     15 * Documentation for ICS MK712 can be found at:
     16 *	https://www.idt.com/general-parts/mk712-touch-screen-controller
     17 */
     18
     19/*
     20 * 1999-12-18: original version, Daniel Quinlan
     21 * 1999-12-19: added anti-jitter code, report pen-up events, fixed mk712_poll
     22 *             to use queue_empty, Nathan Laredo
     23 * 1999-12-20: improved random point rejection, Nathan Laredo
     24 * 2000-01-05: checked in new anti-jitter code, changed mouse protocol, fixed
     25 *             queue code, added module options, other fixes, Daniel Quinlan
     26 * 2002-03-15: Clean up for kernel merge <alan@redhat.com>
     27 *             Fixed multi open race, fixed memory checks, fixed resource
     28 *             allocation, fixed close/powerdown bug, switched to new init
     29 * 2005-01-18: Ported to 2.6 from 2.4.28, Rick Koch
     30 * 2005-02-05: Rewritten for the input layer, Vojtech Pavlik
     31 *
     32 */
     33
     34#include <linux/module.h>
     35#include <linux/kernel.h>
     36#include <linux/init.h>
     37#include <linux/errno.h>
     38#include <linux/delay.h>
     39#include <linux/ioport.h>
     40#include <linux/interrupt.h>
     41#include <linux/input.h>
     42#include <asm/io.h>
     43
     44MODULE_AUTHOR("Daniel Quinlan <quinlan@pathname.com>, Vojtech Pavlik <vojtech@suse.cz>");
     45MODULE_DESCRIPTION("ICS MicroClock MK712 TouchScreen driver");
     46MODULE_LICENSE("GPL");
     47
     48static unsigned int mk712_io = 0x260;	/* Also 0x200, 0x208, 0x300 */
     49module_param_hw_named(io, mk712_io, uint, ioport, 0);
     50MODULE_PARM_DESC(io, "I/O base address of MK712 touchscreen controller");
     51
     52static unsigned int mk712_irq = 10;	/* Also 12, 14, 15 */
     53module_param_hw_named(irq, mk712_irq, uint, irq, 0);
     54MODULE_PARM_DESC(irq, "IRQ of MK712 touchscreen controller");
     55
     56/* eight 8-bit registers */
     57#define MK712_STATUS		0
     58#define MK712_X			2
     59#define MK712_Y			4
     60#define MK712_CONTROL		6
     61#define MK712_RATE		7
     62
     63/* status */
     64#define	MK712_STATUS_TOUCH			0x10
     65#define	MK712_CONVERSION_COMPLETE		0x80
     66
     67/* control */
     68#define MK712_ENABLE_INT			0x01
     69#define MK712_INT_ON_CONVERSION_COMPLETE	0x02
     70#define MK712_INT_ON_CHANGE_IN_TOUCH_STATUS	0x04
     71#define MK712_ENABLE_PERIODIC_CONVERSIONS	0x10
     72#define MK712_READ_ONE_POINT			0x20
     73#define MK712_POWERUP				0x40
     74
     75static struct input_dev *mk712_dev;
     76static DEFINE_SPINLOCK(mk712_lock);
     77
     78static irqreturn_t mk712_interrupt(int irq, void *dev_id)
     79{
     80	unsigned char status;
     81	static int debounce = 1;
     82	static unsigned short last_x;
     83	static unsigned short last_y;
     84
     85	spin_lock(&mk712_lock);
     86
     87	status = inb(mk712_io + MK712_STATUS);
     88
     89	if (~status & MK712_CONVERSION_COMPLETE) {
     90		debounce = 1;
     91		goto end;
     92	}
     93
     94	if (~status & MK712_STATUS_TOUCH) {
     95		debounce = 1;
     96		input_report_key(mk712_dev, BTN_TOUCH, 0);
     97		goto end;
     98	}
     99
    100	if (debounce) {
    101		debounce = 0;
    102		goto end;
    103	}
    104
    105	input_report_key(mk712_dev, BTN_TOUCH, 1);
    106	input_report_abs(mk712_dev, ABS_X, last_x);
    107	input_report_abs(mk712_dev, ABS_Y, last_y);
    108
    109 end:
    110	last_x = inw(mk712_io + MK712_X) & 0x0fff;
    111	last_y = inw(mk712_io + MK712_Y) & 0x0fff;
    112	input_sync(mk712_dev);
    113	spin_unlock(&mk712_lock);
    114	return IRQ_HANDLED;
    115}
    116
    117static int mk712_open(struct input_dev *dev)
    118{
    119	unsigned long flags;
    120
    121	spin_lock_irqsave(&mk712_lock, flags);
    122
    123	outb(0, mk712_io + MK712_CONTROL); /* Reset */
    124
    125	outb(MK712_ENABLE_INT | MK712_INT_ON_CONVERSION_COMPLETE |
    126		MK712_INT_ON_CHANGE_IN_TOUCH_STATUS |
    127		MK712_ENABLE_PERIODIC_CONVERSIONS |
    128		MK712_POWERUP, mk712_io + MK712_CONTROL);
    129
    130	outb(10, mk712_io + MK712_RATE); /* 187 points per second */
    131
    132	spin_unlock_irqrestore(&mk712_lock, flags);
    133
    134	return 0;
    135}
    136
    137static void mk712_close(struct input_dev *dev)
    138{
    139	unsigned long flags;
    140
    141	spin_lock_irqsave(&mk712_lock, flags);
    142
    143	outb(0, mk712_io + MK712_CONTROL);
    144
    145	spin_unlock_irqrestore(&mk712_lock, flags);
    146}
    147
    148static int __init mk712_init(void)
    149{
    150	int err;
    151
    152	if (!request_region(mk712_io, 8, "mk712")) {
    153		printk(KERN_WARNING "mk712: unable to get IO region\n");
    154		return -ENODEV;
    155	}
    156
    157	outb(0, mk712_io + MK712_CONTROL);
    158
    159	if ((inw(mk712_io + MK712_X) & 0xf000) ||	/* Sanity check */
    160	    (inw(mk712_io + MK712_Y) & 0xf000) ||
    161	    (inw(mk712_io + MK712_STATUS) & 0xf333)) {
    162		printk(KERN_WARNING "mk712: device not present\n");
    163		err = -ENODEV;
    164		goto fail1;
    165	}
    166
    167	mk712_dev = input_allocate_device();
    168	if (!mk712_dev) {
    169		printk(KERN_ERR "mk712: not enough memory\n");
    170		err = -ENOMEM;
    171		goto fail1;
    172	}
    173
    174	mk712_dev->name = "ICS MicroClock MK712 TouchScreen";
    175	mk712_dev->phys = "isa0260/input0";
    176	mk712_dev->id.bustype = BUS_ISA;
    177	mk712_dev->id.vendor  = 0x0005;
    178	mk712_dev->id.product = 0x0001;
    179	mk712_dev->id.version = 0x0100;
    180
    181	mk712_dev->open    = mk712_open;
    182	mk712_dev->close   = mk712_close;
    183
    184	mk712_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
    185	mk712_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
    186	input_set_abs_params(mk712_dev, ABS_X, 0, 0xfff, 88, 0);
    187	input_set_abs_params(mk712_dev, ABS_Y, 0, 0xfff, 88, 0);
    188
    189	if (request_irq(mk712_irq, mk712_interrupt, 0, "mk712", mk712_dev)) {
    190		printk(KERN_WARNING "mk712: unable to get IRQ\n");
    191		err = -EBUSY;
    192		goto fail1;
    193	}
    194
    195	err = input_register_device(mk712_dev);
    196	if (err)
    197		goto fail2;
    198
    199	return 0;
    200
    201 fail2:	free_irq(mk712_irq, mk712_dev);
    202 fail1:	input_free_device(mk712_dev);
    203	release_region(mk712_io, 8);
    204	return err;
    205}
    206
    207static void __exit mk712_exit(void)
    208{
    209	input_unregister_device(mk712_dev);
    210	free_irq(mk712_irq, mk712_dev);
    211	release_region(mk712_io, 8);
    212}
    213
    214module_init(mk712_init);
    215module_exit(mk712_exit);