1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * ICS MK712 touchscreen controller driver
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * Copyright (c) 1999-2002 Transmeta Corporation
61da177e4SLinus Torvalds  * Copyright (c) 2005 Rick Koch <n1gp@hotmail.com>
71da177e4SLinus Torvalds  * Copyright (c) 2005 Vojtech Pavlik <vojtech@suse.cz>
81da177e4SLinus Torvalds  */
91da177e4SLinus Torvalds 
101da177e4SLinus Torvalds 
111da177e4SLinus Torvalds /*
121da177e4SLinus Torvalds  * This driver supports the ICS MicroClock MK712 TouchScreen controller,
131da177e4SLinus Torvalds  * found in Gateway AOL Connected Touchpad computers.
141da177e4SLinus Torvalds  *
151da177e4SLinus Torvalds  * Documentation for ICS MK712 can be found at:
16cbd606ecSMartin Kepplinger  *	https://www.idt.com/general-parts/mk712-touch-screen-controller
171da177e4SLinus Torvalds  */
181da177e4SLinus Torvalds 
191da177e4SLinus Torvalds /*
201da177e4SLinus Torvalds  * 1999-12-18: original version, Daniel Quinlan
211da177e4SLinus Torvalds  * 1999-12-19: added anti-jitter code, report pen-up events, fixed mk712_poll
221da177e4SLinus Torvalds  *             to use queue_empty, Nathan Laredo
231da177e4SLinus Torvalds  * 1999-12-20: improved random point rejection, Nathan Laredo
241da177e4SLinus Torvalds  * 2000-01-05: checked in new anti-jitter code, changed mouse protocol, fixed
251da177e4SLinus Torvalds  *             queue code, added module options, other fixes, Daniel Quinlan
261da177e4SLinus Torvalds  * 2002-03-15: Clean up for kernel merge <alan@redhat.com>
271da177e4SLinus Torvalds  *             Fixed multi open race, fixed memory checks, fixed resource
281da177e4SLinus Torvalds  *             allocation, fixed close/powerdown bug, switched to new init
291da177e4SLinus Torvalds  * 2005-01-18: Ported to 2.6 from 2.4.28, Rick Koch
301da177e4SLinus Torvalds  * 2005-02-05: Rewritten for the input layer, Vojtech Pavlik
311da177e4SLinus Torvalds  *
321da177e4SLinus Torvalds  */
331da177e4SLinus Torvalds 
341da177e4SLinus Torvalds #include <linux/module.h>
351da177e4SLinus Torvalds #include <linux/kernel.h>
361da177e4SLinus Torvalds #include <linux/init.h>
371da177e4SLinus Torvalds #include <linux/errno.h>
381da177e4SLinus Torvalds #include <linux/delay.h>
391da177e4SLinus Torvalds #include <linux/ioport.h>
401da177e4SLinus Torvalds #include <linux/interrupt.h>
411da177e4SLinus Torvalds #include <linux/input.h>
421da177e4SLinus Torvalds #include <asm/io.h>
431da177e4SLinus Torvalds 
441da177e4SLinus Torvalds MODULE_AUTHOR("Daniel Quinlan <quinlan@pathname.com>, Vojtech Pavlik <vojtech@suse.cz>");
451da177e4SLinus Torvalds MODULE_DESCRIPTION("ICS MicroClock MK712 TouchScreen driver");
461da177e4SLinus Torvalds MODULE_LICENSE("GPL");
471da177e4SLinus Torvalds 
481da177e4SLinus Torvalds static unsigned int mk712_io = 0x260;	/* Also 0x200, 0x208, 0x300 */
49f6b12d04SDavid Howells module_param_hw_named(io, mk712_io, uint, ioport, 0);
501da177e4SLinus Torvalds MODULE_PARM_DESC(io, "I/O base address of MK712 touchscreen controller");
511da177e4SLinus Torvalds 
521da177e4SLinus Torvalds static unsigned int mk712_irq = 10;	/* Also 12, 14, 15 */
53f6b12d04SDavid Howells module_param_hw_named(irq, mk712_irq, uint, irq, 0);
541da177e4SLinus Torvalds MODULE_PARM_DESC(irq, "IRQ of MK712 touchscreen controller");
551da177e4SLinus Torvalds 
561da177e4SLinus Torvalds /* eight 8-bit registers */
571da177e4SLinus Torvalds #define MK712_STATUS		0
581da177e4SLinus Torvalds #define MK712_X			2
591da177e4SLinus Torvalds #define MK712_Y			4
601da177e4SLinus Torvalds #define MK712_CONTROL		6
611da177e4SLinus Torvalds #define MK712_RATE		7
621da177e4SLinus Torvalds 
631da177e4SLinus Torvalds /* status */
641da177e4SLinus Torvalds #define	MK712_STATUS_TOUCH			0x10
651da177e4SLinus Torvalds #define	MK712_CONVERSION_COMPLETE		0x80
661da177e4SLinus Torvalds 
671da177e4SLinus Torvalds /* control */
681da177e4SLinus Torvalds #define MK712_ENABLE_INT			0x01
691da177e4SLinus Torvalds #define MK712_INT_ON_CONVERSION_COMPLETE	0x02
701da177e4SLinus Torvalds #define MK712_INT_ON_CHANGE_IN_TOUCH_STATUS	0x04
711da177e4SLinus Torvalds #define MK712_ENABLE_PERIODIC_CONVERSIONS	0x10
721da177e4SLinus Torvalds #define MK712_READ_ONE_POINT			0x20
731da177e4SLinus Torvalds #define MK712_POWERUP				0x40
741da177e4SLinus Torvalds 
75eca1ed19SDmitry Torokhov static struct input_dev *mk712_dev;
761da177e4SLinus Torvalds static DEFINE_SPINLOCK(mk712_lock);
771da177e4SLinus Torvalds 
mk712_interrupt(int irq,void * dev_id)787d12e780SDavid Howells static irqreturn_t mk712_interrupt(int irq, void *dev_id)
791da177e4SLinus Torvalds {
801da177e4SLinus Torvalds 	unsigned char status;
811da177e4SLinus Torvalds 	static int debounce = 1;
821da177e4SLinus Torvalds 	static unsigned short last_x;
831da177e4SLinus Torvalds 	static unsigned short last_y;
841da177e4SLinus Torvalds 
851da177e4SLinus Torvalds 	spin_lock(&mk712_lock);
861da177e4SLinus Torvalds 
871da177e4SLinus Torvalds 	status = inb(mk712_io + MK712_STATUS);
881da177e4SLinus Torvalds 
891da177e4SLinus Torvalds 	if (~status & MK712_CONVERSION_COMPLETE) {
901da177e4SLinus Torvalds 		debounce = 1;
911da177e4SLinus Torvalds 		goto end;
921da177e4SLinus Torvalds 	}
931da177e4SLinus Torvalds 
9452c1f570SDmitry Torokhov 	if (~status & MK712_STATUS_TOUCH) {
951da177e4SLinus Torvalds 		debounce = 1;
96eca1ed19SDmitry Torokhov 		input_report_key(mk712_dev, BTN_TOUCH, 0);
971da177e4SLinus Torvalds 		goto end;
981da177e4SLinus Torvalds 	}
991da177e4SLinus Torvalds 
10052c1f570SDmitry Torokhov 	if (debounce) {
1011da177e4SLinus Torvalds 		debounce = 0;
1021da177e4SLinus Torvalds 		goto end;
1031da177e4SLinus Torvalds 	}
1041da177e4SLinus Torvalds 
105eca1ed19SDmitry Torokhov 	input_report_key(mk712_dev, BTN_TOUCH, 1);
106eca1ed19SDmitry Torokhov 	input_report_abs(mk712_dev, ABS_X, last_x);
107eca1ed19SDmitry Torokhov 	input_report_abs(mk712_dev, ABS_Y, last_y);
1081da177e4SLinus Torvalds 
1091da177e4SLinus Torvalds  end:
1101da177e4SLinus Torvalds 	last_x = inw(mk712_io + MK712_X) & 0x0fff;
1111da177e4SLinus Torvalds 	last_y = inw(mk712_io + MK712_Y) & 0x0fff;
112eca1ed19SDmitry Torokhov 	input_sync(mk712_dev);
1131da177e4SLinus Torvalds 	spin_unlock(&mk712_lock);
1141da177e4SLinus Torvalds 	return IRQ_HANDLED;
1151da177e4SLinus Torvalds }
1161da177e4SLinus Torvalds 
mk712_open(struct input_dev * dev)1171da177e4SLinus Torvalds static int mk712_open(struct input_dev *dev)
1181da177e4SLinus Torvalds {
1191da177e4SLinus Torvalds 	unsigned long flags;
1201da177e4SLinus Torvalds 
1211da177e4SLinus Torvalds 	spin_lock_irqsave(&mk712_lock, flags);
1221da177e4SLinus Torvalds 
1231da177e4SLinus Torvalds 	outb(0, mk712_io + MK712_CONTROL); /* Reset */
1241da177e4SLinus Torvalds 
1251da177e4SLinus Torvalds 	outb(MK712_ENABLE_INT | MK712_INT_ON_CONVERSION_COMPLETE |
1261da177e4SLinus Torvalds 		MK712_INT_ON_CHANGE_IN_TOUCH_STATUS |
1271da177e4SLinus Torvalds 		MK712_ENABLE_PERIODIC_CONVERSIONS |
1281da177e4SLinus Torvalds 		MK712_POWERUP, mk712_io + MK712_CONTROL);
1291da177e4SLinus Torvalds 
1301da177e4SLinus Torvalds 	outb(10, mk712_io + MK712_RATE); /* 187 points per second */
1311da177e4SLinus Torvalds 
1321da177e4SLinus Torvalds 	spin_unlock_irqrestore(&mk712_lock, flags);
1331da177e4SLinus Torvalds 
1341da177e4SLinus Torvalds 	return 0;
1351da177e4SLinus Torvalds }
1361da177e4SLinus Torvalds 
mk712_close(struct input_dev * dev)1371da177e4SLinus Torvalds static void mk712_close(struct input_dev *dev)
1381da177e4SLinus Torvalds {
1391da177e4SLinus Torvalds 	unsigned long flags;
1401da177e4SLinus Torvalds 
1411da177e4SLinus Torvalds 	spin_lock_irqsave(&mk712_lock, flags);
1421da177e4SLinus Torvalds 
1431da177e4SLinus Torvalds 	outb(0, mk712_io + MK712_CONTROL);
1441da177e4SLinus Torvalds 
1451da177e4SLinus Torvalds 	spin_unlock_irqrestore(&mk712_lock, flags);
1461da177e4SLinus Torvalds }
1471da177e4SLinus Torvalds 
mk712_init(void)148ffc6b529SAdrian Bunk static int __init mk712_init(void)
1491da177e4SLinus Torvalds {
150eca1ed19SDmitry Torokhov 	int err;
1511da177e4SLinus Torvalds 
152eca1ed19SDmitry Torokhov 	if (!request_region(mk712_io, 8, "mk712")) {
1531da177e4SLinus Torvalds 		printk(KERN_WARNING "mk712: unable to get IO region\n");
1541da177e4SLinus Torvalds 		return -ENODEV;
1551da177e4SLinus Torvalds 	}
1561da177e4SLinus Torvalds 
1571da177e4SLinus Torvalds 	outb(0, mk712_io + MK712_CONTROL);
1581da177e4SLinus Torvalds 
1591da177e4SLinus Torvalds 	if ((inw(mk712_io + MK712_X) & 0xf000) ||	/* Sanity check */
1601da177e4SLinus Torvalds 	    (inw(mk712_io + MK712_Y) & 0xf000) ||
1611da177e4SLinus Torvalds 	    (inw(mk712_io + MK712_STATUS) & 0xf333)) {
1621da177e4SLinus Torvalds 		printk(KERN_WARNING "mk712: device not present\n");
163eca1ed19SDmitry Torokhov 		err = -ENODEV;
16452c1f570SDmitry Torokhov 		goto fail1;
1651da177e4SLinus Torvalds 	}
1661da177e4SLinus Torvalds 
16752c1f570SDmitry Torokhov 	mk712_dev = input_allocate_device();
16852c1f570SDmitry Torokhov 	if (!mk712_dev) {
169eca1ed19SDmitry Torokhov 		printk(KERN_ERR "mk712: not enough memory\n");
170eca1ed19SDmitry Torokhov 		err = -ENOMEM;
17152c1f570SDmitry Torokhov 		goto fail1;
172eca1ed19SDmitry Torokhov 	}
173eca1ed19SDmitry Torokhov 
174eca1ed19SDmitry Torokhov 	mk712_dev->name = "ICS MicroClock MK712 TouchScreen";
175eca1ed19SDmitry Torokhov 	mk712_dev->phys = "isa0260/input0";
176eca1ed19SDmitry Torokhov 	mk712_dev->id.bustype = BUS_ISA;
177eca1ed19SDmitry Torokhov 	mk712_dev->id.vendor  = 0x0005;
178eca1ed19SDmitry Torokhov 	mk712_dev->id.product = 0x0001;
179eca1ed19SDmitry Torokhov 	mk712_dev->id.version = 0x0100;
180eca1ed19SDmitry Torokhov 
181eca1ed19SDmitry Torokhov 	mk712_dev->open    = mk712_open;
182eca1ed19SDmitry Torokhov 	mk712_dev->close   = mk712_close;
183eca1ed19SDmitry Torokhov 
1847b19ada2SJiri Slaby 	mk712_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
1857b19ada2SJiri Slaby 	mk712_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
186eca1ed19SDmitry Torokhov 	input_set_abs_params(mk712_dev, ABS_X, 0, 0xfff, 88, 0);
187eca1ed19SDmitry Torokhov 	input_set_abs_params(mk712_dev, ABS_Y, 0, 0xfff, 88, 0);
188eca1ed19SDmitry Torokhov 
189eca1ed19SDmitry Torokhov 	if (request_irq(mk712_irq, mk712_interrupt, 0, "mk712", mk712_dev)) {
1901da177e4SLinus Torvalds 		printk(KERN_WARNING "mk712: unable to get IRQ\n");
191eca1ed19SDmitry Torokhov 		err = -EBUSY;
19252c1f570SDmitry Torokhov 		goto fail1;
1931da177e4SLinus Torvalds 	}
1941da177e4SLinus Torvalds 
19552c1f570SDmitry Torokhov 	err = input_register_device(mk712_dev);
19652c1f570SDmitry Torokhov 	if (err)
19752c1f570SDmitry Torokhov 		goto fail2;
19852c1f570SDmitry Torokhov 
1991da177e4SLinus Torvalds 	return 0;
200eca1ed19SDmitry Torokhov 
20152c1f570SDmitry Torokhov  fail2:	free_irq(mk712_irq, mk712_dev);
20252c1f570SDmitry Torokhov  fail1:	input_free_device(mk712_dev);
203eca1ed19SDmitry Torokhov 	release_region(mk712_io, 8);
204eca1ed19SDmitry Torokhov 	return err;
2051da177e4SLinus Torvalds }
2061da177e4SLinus Torvalds 
mk712_exit(void)2071da177e4SLinus Torvalds static void __exit mk712_exit(void)
2081da177e4SLinus Torvalds {
209eca1ed19SDmitry Torokhov 	input_unregister_device(mk712_dev);
210eca1ed19SDmitry Torokhov 	free_irq(mk712_irq, mk712_dev);
2111da177e4SLinus Torvalds 	release_region(mk712_io, 8);
2121da177e4SLinus Torvalds }
2131da177e4SLinus Torvalds 
2141da177e4SLinus Torvalds module_init(mk712_init);
2151da177e4SLinus Torvalds module_exit(mk712_exit);
216