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