109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds #include <linux/input.h>
31da177e4SLinus Torvalds #include <linux/module.h>
41da177e4SLinus Torvalds #include <linux/init.h>
51da177e4SLinus Torvalds #include <linux/interrupt.h>
61da177e4SLinus Torvalds #include <asm/io.h>
71da177e4SLinus Torvalds #include <asm/delay.h>
81da177e4SLinus Torvalds #include <asm/adc.h>
97639a454SPaul Mundt #include <mach/hp6xx.h>
101da177e4SLinus Torvalds 
111da177e4SLinus Torvalds #define MODNAME "hp680_ts_input"
121da177e4SLinus Torvalds 
131da177e4SLinus Torvalds #define HP680_TS_ABS_X_MIN	40
141da177e4SLinus Torvalds #define HP680_TS_ABS_X_MAX	950
151da177e4SLinus Torvalds #define HP680_TS_ABS_Y_MIN	80
161da177e4SLinus Torvalds #define HP680_TS_ABS_Y_MAX	910
171da177e4SLinus Torvalds 
181da177e4SLinus Torvalds #define	PHDR	0xa400012e
191da177e4SLinus Torvalds #define SCPDR	0xa4000136
201da177e4SLinus Torvalds 
21049fa57cSKristoffer Ericson static void do_softint(struct work_struct *work);
221da177e4SLinus Torvalds 
23eca1ed19SDmitry Torokhov static struct input_dev *hp680_ts_dev;
24049fa57cSKristoffer Ericson static DECLARE_DELAYED_WORK(work, do_softint);
251da177e4SLinus Torvalds 
do_softint(struct work_struct * work)26049fa57cSKristoffer Ericson static void do_softint(struct work_struct *work)
271da177e4SLinus Torvalds {
281da177e4SLinus Torvalds 	int absx = 0, absy = 0;
291da177e4SLinus Torvalds 	u8 scpdr;
301da177e4SLinus Torvalds 	int touched = 0;
311da177e4SLinus Torvalds 
32e28abafbSPaul Mundt 	if (__raw_readb(PHDR) & PHDR_TS_PEN_DOWN) {
33e28abafbSPaul Mundt 		scpdr = __raw_readb(SCPDR);
341da177e4SLinus Torvalds 		scpdr |= SCPDR_TS_SCAN_ENABLE;
351da177e4SLinus Torvalds 		scpdr &= ~SCPDR_TS_SCAN_Y;
36e28abafbSPaul Mundt 		__raw_writeb(scpdr, SCPDR);
371da177e4SLinus Torvalds 		udelay(30);
381da177e4SLinus Torvalds 
391da177e4SLinus Torvalds 		absy = adc_single(ADC_CHANNEL_TS_Y);
401da177e4SLinus Torvalds 
41e28abafbSPaul Mundt 		scpdr = __raw_readb(SCPDR);
421da177e4SLinus Torvalds 		scpdr |= SCPDR_TS_SCAN_Y;
431da177e4SLinus Torvalds 		scpdr &= ~SCPDR_TS_SCAN_X;
44e28abafbSPaul Mundt 		__raw_writeb(scpdr, SCPDR);
451da177e4SLinus Torvalds 		udelay(30);
461da177e4SLinus Torvalds 
471da177e4SLinus Torvalds 		absx = adc_single(ADC_CHANNEL_TS_X);
481da177e4SLinus Torvalds 
49e28abafbSPaul Mundt 		scpdr = __raw_readb(SCPDR);
501da177e4SLinus Torvalds 		scpdr |= SCPDR_TS_SCAN_X;
511da177e4SLinus Torvalds 		scpdr &= ~SCPDR_TS_SCAN_ENABLE;
52e28abafbSPaul Mundt 		__raw_writeb(scpdr, SCPDR);
531da177e4SLinus Torvalds 		udelay(100);
54e28abafbSPaul Mundt 		touched = __raw_readb(PHDR) & PHDR_TS_PEN_DOWN;
551da177e4SLinus Torvalds 	}
561da177e4SLinus Torvalds 
571da177e4SLinus Torvalds 	if (touched) {
58eca1ed19SDmitry Torokhov 		input_report_key(hp680_ts_dev, BTN_TOUCH, 1);
59eca1ed19SDmitry Torokhov 		input_report_abs(hp680_ts_dev, ABS_X, absx);
60eca1ed19SDmitry Torokhov 		input_report_abs(hp680_ts_dev, ABS_Y, absy);
611da177e4SLinus Torvalds 	} else {
62eca1ed19SDmitry Torokhov 		input_report_key(hp680_ts_dev, BTN_TOUCH, 0);
631da177e4SLinus Torvalds 	}
641da177e4SLinus Torvalds 
65eca1ed19SDmitry Torokhov 	input_sync(hp680_ts_dev);
661da177e4SLinus Torvalds 	enable_irq(HP680_TS_IRQ);
671da177e4SLinus Torvalds }
681da177e4SLinus Torvalds 
hp680_ts_interrupt(int irq,void * dev)697d12e780SDavid Howells static irqreturn_t hp680_ts_interrupt(int irq, void *dev)
701da177e4SLinus Torvalds {
711da177e4SLinus Torvalds 	disable_irq_nosync(irq);
721da177e4SLinus Torvalds 	schedule_delayed_work(&work, HZ / 20);
731da177e4SLinus Torvalds 
741da177e4SLinus Torvalds 	return IRQ_HANDLED;
751da177e4SLinus Torvalds }
761da177e4SLinus Torvalds 
hp680_ts_init(void)771da177e4SLinus Torvalds static int __init hp680_ts_init(void)
781da177e4SLinus Torvalds {
7952c1f570SDmitry Torokhov 	int err;
8052c1f570SDmitry Torokhov 
81eca1ed19SDmitry Torokhov 	hp680_ts_dev = input_allocate_device();
82eca1ed19SDmitry Torokhov 	if (!hp680_ts_dev)
83eca1ed19SDmitry Torokhov 		return -ENOMEM;
841da177e4SLinus Torvalds 
857b19ada2SJiri Slaby 	hp680_ts_dev->evbit[0] = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY);
867b19ada2SJiri Slaby 	hp680_ts_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
871da177e4SLinus Torvalds 
8852c1f570SDmitry Torokhov 	input_set_abs_params(hp680_ts_dev, ABS_X,
8952c1f570SDmitry Torokhov 		HP680_TS_ABS_X_MIN, HP680_TS_ABS_X_MAX, 0, 0);
9052c1f570SDmitry Torokhov 	input_set_abs_params(hp680_ts_dev, ABS_Y,
9152c1f570SDmitry Torokhov 		HP680_TS_ABS_Y_MIN, HP680_TS_ABS_Y_MAX, 0, 0);
921da177e4SLinus Torvalds 
93eca1ed19SDmitry Torokhov 	hp680_ts_dev->name = "HP Jornada touchscreen";
94eca1ed19SDmitry Torokhov 	hp680_ts_dev->phys = "hp680_ts/input0";
951da177e4SLinus Torvalds 
96eca1ed19SDmitry Torokhov 	if (request_irq(HP680_TS_IRQ, hp680_ts_interrupt,
97b51425beSJulia Lawall 			0, MODNAME, NULL) < 0) {
981da177e4SLinus Torvalds 		printk(KERN_ERR "hp680_touchscreen.c: Can't allocate irq %d\n",
991da177e4SLinus Torvalds 		       HP680_TS_IRQ);
10052c1f570SDmitry Torokhov 		err = -EBUSY;
10152c1f570SDmitry Torokhov 		goto fail1;
1021da177e4SLinus Torvalds 	}
1031da177e4SLinus Torvalds 
10452c1f570SDmitry Torokhov 	err = input_register_device(hp680_ts_dev);
10552c1f570SDmitry Torokhov 	if (err)
10652c1f570SDmitry Torokhov 		goto fail2;
10752c1f570SDmitry Torokhov 
1081da177e4SLinus Torvalds 	return 0;
10952c1f570SDmitry Torokhov 
11052c1f570SDmitry Torokhov  fail2:	free_irq(HP680_TS_IRQ, NULL);
111790d5c8dSTejun Heo 	cancel_delayed_work_sync(&work);
11252c1f570SDmitry Torokhov  fail1:	input_free_device(hp680_ts_dev);
11352c1f570SDmitry Torokhov 	return err;
1141da177e4SLinus Torvalds }
1151da177e4SLinus Torvalds 
hp680_ts_exit(void)1161da177e4SLinus Torvalds static void __exit hp680_ts_exit(void)
1171da177e4SLinus Torvalds {
11852c1f570SDmitry Torokhov 	free_irq(HP680_TS_IRQ, NULL);
119790d5c8dSTejun Heo 	cancel_delayed_work_sync(&work);
120eca1ed19SDmitry Torokhov 	input_unregister_device(hp680_ts_dev);
1211da177e4SLinus Torvalds }
1221da177e4SLinus Torvalds 
1231da177e4SLinus Torvalds module_init(hp680_ts_init);
1241da177e4SLinus Torvalds module_exit(hp680_ts_exit);
1251da177e4SLinus Torvalds 
1261da177e4SLinus Torvalds MODULE_AUTHOR("Andriy Skulysh, askulysh@image.kiev.ua");
1271da177e4SLinus Torvalds MODULE_DESCRIPTION("HP Jornada 680 touchscreen driver");
1281da177e4SLinus Torvalds MODULE_LICENSE("GPL");
129