xref: /OK3568_Linux_fs/kernel/drivers/input/serio/ct82c710.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  *  Copyright (c) 1999-2001 Vojtech Pavlik
4*4882a593Smuzhiyun  */
5*4882a593Smuzhiyun 
6*4882a593Smuzhiyun /*
7*4882a593Smuzhiyun  *  82C710 C&T mouse port chip driver for Linux
8*4882a593Smuzhiyun  */
9*4882a593Smuzhiyun 
10*4882a593Smuzhiyun /*
11*4882a593Smuzhiyun  */
12*4882a593Smuzhiyun 
13*4882a593Smuzhiyun #include <linux/delay.h>
14*4882a593Smuzhiyun #include <linux/module.h>
15*4882a593Smuzhiyun #include <linux/ioport.h>
16*4882a593Smuzhiyun #include <linux/init.h>
17*4882a593Smuzhiyun #include <linux/interrupt.h>
18*4882a593Smuzhiyun #include <linux/serio.h>
19*4882a593Smuzhiyun #include <linux/errno.h>
20*4882a593Smuzhiyun #include <linux/err.h>
21*4882a593Smuzhiyun #include <linux/platform_device.h>
22*4882a593Smuzhiyun #include <linux/slab.h>
23*4882a593Smuzhiyun 
24*4882a593Smuzhiyun #include <asm/io.h>
25*4882a593Smuzhiyun 
26*4882a593Smuzhiyun MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
27*4882a593Smuzhiyun MODULE_DESCRIPTION("82C710 C&T mouse port chip driver");
28*4882a593Smuzhiyun MODULE_LICENSE("GPL");
29*4882a593Smuzhiyun 
30*4882a593Smuzhiyun /*
31*4882a593Smuzhiyun  * ct82c710 interface
32*4882a593Smuzhiyun  */
33*4882a593Smuzhiyun 
34*4882a593Smuzhiyun #define CT82C710_DEV_IDLE     0x01		/* Device Idle */
35*4882a593Smuzhiyun #define CT82C710_RX_FULL      0x02		/* Device Char received */
36*4882a593Smuzhiyun #define CT82C710_TX_IDLE      0x04		/* Device XMIT Idle */
37*4882a593Smuzhiyun #define CT82C710_RESET        0x08		/* Device Reset */
38*4882a593Smuzhiyun #define CT82C710_INTS_ON      0x10		/* Device Interrupt On */
39*4882a593Smuzhiyun #define CT82C710_ERROR_FLAG   0x20		/* Device Error */
40*4882a593Smuzhiyun #define CT82C710_CLEAR        0x40		/* Device Clear */
41*4882a593Smuzhiyun #define CT82C710_ENABLE       0x80		/* Device Enable */
42*4882a593Smuzhiyun 
43*4882a593Smuzhiyun #define CT82C710_IRQ          12
44*4882a593Smuzhiyun 
45*4882a593Smuzhiyun #define CT82C710_DATA         ct82c710_iores.start
46*4882a593Smuzhiyun #define CT82C710_STATUS       (ct82c710_iores.start + 1)
47*4882a593Smuzhiyun 
48*4882a593Smuzhiyun static struct serio *ct82c710_port;
49*4882a593Smuzhiyun static struct platform_device *ct82c710_device;
50*4882a593Smuzhiyun static struct resource ct82c710_iores;
51*4882a593Smuzhiyun 
52*4882a593Smuzhiyun /*
53*4882a593Smuzhiyun  * Interrupt handler for the 82C710 mouse port. A character
54*4882a593Smuzhiyun  * is waiting in the 82C710.
55*4882a593Smuzhiyun  */
56*4882a593Smuzhiyun 
ct82c710_interrupt(int cpl,void * dev_id)57*4882a593Smuzhiyun static irqreturn_t ct82c710_interrupt(int cpl, void *dev_id)
58*4882a593Smuzhiyun {
59*4882a593Smuzhiyun 	return serio_interrupt(ct82c710_port, inb(CT82C710_DATA), 0);
60*4882a593Smuzhiyun }
61*4882a593Smuzhiyun 
62*4882a593Smuzhiyun /*
63*4882a593Smuzhiyun  * Wait for device to send output char and flush any input char.
64*4882a593Smuzhiyun  */
65*4882a593Smuzhiyun 
ct82c170_wait(void)66*4882a593Smuzhiyun static int ct82c170_wait(void)
67*4882a593Smuzhiyun {
68*4882a593Smuzhiyun 	int timeout = 60000;
69*4882a593Smuzhiyun 
70*4882a593Smuzhiyun 	while ((inb(CT82C710_STATUS) & (CT82C710_RX_FULL | CT82C710_TX_IDLE | CT82C710_DEV_IDLE))
71*4882a593Smuzhiyun 		       != (CT82C710_DEV_IDLE | CT82C710_TX_IDLE) && timeout) {
72*4882a593Smuzhiyun 
73*4882a593Smuzhiyun 		if (inb_p(CT82C710_STATUS) & CT82C710_RX_FULL) inb_p(CT82C710_DATA);
74*4882a593Smuzhiyun 
75*4882a593Smuzhiyun 		udelay(1);
76*4882a593Smuzhiyun 		timeout--;
77*4882a593Smuzhiyun 	}
78*4882a593Smuzhiyun 
79*4882a593Smuzhiyun 	return !timeout;
80*4882a593Smuzhiyun }
81*4882a593Smuzhiyun 
ct82c710_close(struct serio * serio)82*4882a593Smuzhiyun static void ct82c710_close(struct serio *serio)
83*4882a593Smuzhiyun {
84*4882a593Smuzhiyun 	if (ct82c170_wait())
85*4882a593Smuzhiyun 		printk(KERN_WARNING "ct82c710.c: Device busy in close()\n");
86*4882a593Smuzhiyun 
87*4882a593Smuzhiyun 	outb_p(inb_p(CT82C710_STATUS) & ~(CT82C710_ENABLE | CT82C710_INTS_ON), CT82C710_STATUS);
88*4882a593Smuzhiyun 
89*4882a593Smuzhiyun 	if (ct82c170_wait())
90*4882a593Smuzhiyun 		printk(KERN_WARNING "ct82c710.c: Device busy in close()\n");
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun 	free_irq(CT82C710_IRQ, NULL);
93*4882a593Smuzhiyun }
94*4882a593Smuzhiyun 
ct82c710_open(struct serio * serio)95*4882a593Smuzhiyun static int ct82c710_open(struct serio *serio)
96*4882a593Smuzhiyun {
97*4882a593Smuzhiyun 	unsigned char status;
98*4882a593Smuzhiyun 	int err;
99*4882a593Smuzhiyun 
100*4882a593Smuzhiyun 	err = request_irq(CT82C710_IRQ, ct82c710_interrupt, 0, "ct82c710", NULL);
101*4882a593Smuzhiyun 	if (err)
102*4882a593Smuzhiyun 		return err;
103*4882a593Smuzhiyun 
104*4882a593Smuzhiyun 	status = inb_p(CT82C710_STATUS);
105*4882a593Smuzhiyun 
106*4882a593Smuzhiyun 	status |= (CT82C710_ENABLE | CT82C710_RESET);
107*4882a593Smuzhiyun 	outb_p(status, CT82C710_STATUS);
108*4882a593Smuzhiyun 
109*4882a593Smuzhiyun 	status &= ~(CT82C710_RESET);
110*4882a593Smuzhiyun 	outb_p(status, CT82C710_STATUS);
111*4882a593Smuzhiyun 
112*4882a593Smuzhiyun 	status |= CT82C710_INTS_ON;
113*4882a593Smuzhiyun 	outb_p(status, CT82C710_STATUS);	/* Enable interrupts */
114*4882a593Smuzhiyun 
115*4882a593Smuzhiyun 	while (ct82c170_wait()) {
116*4882a593Smuzhiyun 		printk(KERN_ERR "ct82c710: Device busy in open()\n");
117*4882a593Smuzhiyun 		status &= ~(CT82C710_ENABLE | CT82C710_INTS_ON);
118*4882a593Smuzhiyun 		outb_p(status, CT82C710_STATUS);
119*4882a593Smuzhiyun 		free_irq(CT82C710_IRQ, NULL);
120*4882a593Smuzhiyun 		return -EBUSY;
121*4882a593Smuzhiyun 	}
122*4882a593Smuzhiyun 
123*4882a593Smuzhiyun 	return 0;
124*4882a593Smuzhiyun }
125*4882a593Smuzhiyun 
126*4882a593Smuzhiyun /*
127*4882a593Smuzhiyun  * Write to the 82C710 mouse device.
128*4882a593Smuzhiyun  */
129*4882a593Smuzhiyun 
ct82c710_write(struct serio * port,unsigned char c)130*4882a593Smuzhiyun static int ct82c710_write(struct serio *port, unsigned char c)
131*4882a593Smuzhiyun {
132*4882a593Smuzhiyun 	if (ct82c170_wait()) return -1;
133*4882a593Smuzhiyun 	outb_p(c, CT82C710_DATA);
134*4882a593Smuzhiyun 	return 0;
135*4882a593Smuzhiyun }
136*4882a593Smuzhiyun 
137*4882a593Smuzhiyun /*
138*4882a593Smuzhiyun  * See if we can find a 82C710 device. Read mouse address.
139*4882a593Smuzhiyun  */
140*4882a593Smuzhiyun 
ct82c710_detect(void)141*4882a593Smuzhiyun static int __init ct82c710_detect(void)
142*4882a593Smuzhiyun {
143*4882a593Smuzhiyun 	outb_p(0x55, 0x2fa);				/* Any value except 9, ff or 36 */
144*4882a593Smuzhiyun 	outb_p(0xaa, 0x3fa);				/* Inverse of 55 */
145*4882a593Smuzhiyun 	outb_p(0x36, 0x3fa);				/* Address the chip */
146*4882a593Smuzhiyun 	outb_p(0xe4, 0x3fa);				/* 390/4; 390 = config address */
147*4882a593Smuzhiyun 	outb_p(0x1b, 0x2fa);				/* Inverse of e4 */
148*4882a593Smuzhiyun 	outb_p(0x0f, 0x390);				/* Write index */
149*4882a593Smuzhiyun 	if (inb_p(0x391) != 0xe4)			/* Config address found? */
150*4882a593Smuzhiyun 		return -ENODEV;				/* No: no 82C710 here */
151*4882a593Smuzhiyun 
152*4882a593Smuzhiyun 	outb_p(0x0d, 0x390);				/* Write index */
153*4882a593Smuzhiyun 	ct82c710_iores.start = inb_p(0x391) << 2;	/* Get mouse I/O address */
154*4882a593Smuzhiyun 	ct82c710_iores.end = ct82c710_iores.start + 1;
155*4882a593Smuzhiyun 	ct82c710_iores.flags = IORESOURCE_IO;
156*4882a593Smuzhiyun 	outb_p(0x0f, 0x390);
157*4882a593Smuzhiyun 	outb_p(0x0f, 0x391);				/* Close config mode */
158*4882a593Smuzhiyun 
159*4882a593Smuzhiyun 	return 0;
160*4882a593Smuzhiyun }
161*4882a593Smuzhiyun 
ct82c710_probe(struct platform_device * dev)162*4882a593Smuzhiyun static int ct82c710_probe(struct platform_device *dev)
163*4882a593Smuzhiyun {
164*4882a593Smuzhiyun 	ct82c710_port = kzalloc(sizeof(struct serio), GFP_KERNEL);
165*4882a593Smuzhiyun 	if (!ct82c710_port)
166*4882a593Smuzhiyun 		return -ENOMEM;
167*4882a593Smuzhiyun 
168*4882a593Smuzhiyun 	ct82c710_port->id.type = SERIO_8042;
169*4882a593Smuzhiyun 	ct82c710_port->dev.parent = &dev->dev;
170*4882a593Smuzhiyun 	ct82c710_port->open = ct82c710_open;
171*4882a593Smuzhiyun 	ct82c710_port->close = ct82c710_close;
172*4882a593Smuzhiyun 	ct82c710_port->write = ct82c710_write;
173*4882a593Smuzhiyun 	strlcpy(ct82c710_port->name, "C&T 82c710 mouse port",
174*4882a593Smuzhiyun 		sizeof(ct82c710_port->name));
175*4882a593Smuzhiyun 	snprintf(ct82c710_port->phys, sizeof(ct82c710_port->phys),
176*4882a593Smuzhiyun 		 "isa%16llx/serio0", (unsigned long long)CT82C710_DATA);
177*4882a593Smuzhiyun 
178*4882a593Smuzhiyun 	serio_register_port(ct82c710_port);
179*4882a593Smuzhiyun 
180*4882a593Smuzhiyun 	printk(KERN_INFO "serio: C&T 82c710 mouse port at %#llx irq %d\n",
181*4882a593Smuzhiyun 		(unsigned long long)CT82C710_DATA, CT82C710_IRQ);
182*4882a593Smuzhiyun 
183*4882a593Smuzhiyun 	return 0;
184*4882a593Smuzhiyun }
185*4882a593Smuzhiyun 
ct82c710_remove(struct platform_device * dev)186*4882a593Smuzhiyun static int ct82c710_remove(struct platform_device *dev)
187*4882a593Smuzhiyun {
188*4882a593Smuzhiyun 	serio_unregister_port(ct82c710_port);
189*4882a593Smuzhiyun 
190*4882a593Smuzhiyun 	return 0;
191*4882a593Smuzhiyun }
192*4882a593Smuzhiyun 
193*4882a593Smuzhiyun static struct platform_driver ct82c710_driver = {
194*4882a593Smuzhiyun 	.driver		= {
195*4882a593Smuzhiyun 		.name	= "ct82c710",
196*4882a593Smuzhiyun 	},
197*4882a593Smuzhiyun 	.probe		= ct82c710_probe,
198*4882a593Smuzhiyun 	.remove		= ct82c710_remove,
199*4882a593Smuzhiyun };
200*4882a593Smuzhiyun 
201*4882a593Smuzhiyun 
ct82c710_init(void)202*4882a593Smuzhiyun static int __init ct82c710_init(void)
203*4882a593Smuzhiyun {
204*4882a593Smuzhiyun 	int error;
205*4882a593Smuzhiyun 
206*4882a593Smuzhiyun 	error = ct82c710_detect();
207*4882a593Smuzhiyun 	if (error)
208*4882a593Smuzhiyun 		return error;
209*4882a593Smuzhiyun 
210*4882a593Smuzhiyun 	error = platform_driver_register(&ct82c710_driver);
211*4882a593Smuzhiyun 	if (error)
212*4882a593Smuzhiyun 		return error;
213*4882a593Smuzhiyun 
214*4882a593Smuzhiyun 	ct82c710_device = platform_device_alloc("ct82c710", -1);
215*4882a593Smuzhiyun 	if (!ct82c710_device) {
216*4882a593Smuzhiyun 		error = -ENOMEM;
217*4882a593Smuzhiyun 		goto err_unregister_driver;
218*4882a593Smuzhiyun 	}
219*4882a593Smuzhiyun 
220*4882a593Smuzhiyun 	error = platform_device_add_resources(ct82c710_device, &ct82c710_iores, 1);
221*4882a593Smuzhiyun 	if (error)
222*4882a593Smuzhiyun 		goto err_free_device;
223*4882a593Smuzhiyun 
224*4882a593Smuzhiyun 	error = platform_device_add(ct82c710_device);
225*4882a593Smuzhiyun 	if (error)
226*4882a593Smuzhiyun 		goto err_free_device;
227*4882a593Smuzhiyun 
228*4882a593Smuzhiyun 	return 0;
229*4882a593Smuzhiyun 
230*4882a593Smuzhiyun  err_free_device:
231*4882a593Smuzhiyun 	platform_device_put(ct82c710_device);
232*4882a593Smuzhiyun  err_unregister_driver:
233*4882a593Smuzhiyun 	platform_driver_unregister(&ct82c710_driver);
234*4882a593Smuzhiyun 	return error;
235*4882a593Smuzhiyun }
236*4882a593Smuzhiyun 
ct82c710_exit(void)237*4882a593Smuzhiyun static void __exit ct82c710_exit(void)
238*4882a593Smuzhiyun {
239*4882a593Smuzhiyun 	platform_device_unregister(ct82c710_device);
240*4882a593Smuzhiyun 	platform_driver_unregister(&ct82c710_driver);
241*4882a593Smuzhiyun }
242*4882a593Smuzhiyun 
243*4882a593Smuzhiyun module_init(ct82c710_init);
244*4882a593Smuzhiyun module_exit(ct82c710_exit);
245