Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * SGI O2 MACE PS2 controller driver for linux | |
3 | * | |
4 | * Copyright (C) 2002 Vivien Chappelier | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License version 2 as | |
8 | * published by the Free Software Foundation | |
9 | */ | |
10 | #include <linux/module.h> | |
11 | #include <linux/init.h> | |
12 | #include <linux/serio.h> | |
13 | #include <linux/errno.h> | |
14 | #include <linux/interrupt.h> | |
15 | #include <linux/ioport.h> | |
16 | #include <linux/delay.h> | |
d052d1be | 17 | #include <linux/platform_device.h> |
1da177e4 LT |
18 | #include <linux/slab.h> |
19 | #include <linux/spinlock.h> | |
20 | #include <linux/err.h> | |
21 | ||
22 | #include <asm/io.h> | |
23 | #include <asm/irq.h> | |
24 | #include <asm/system.h> | |
25 | #include <asm/ip32/mace.h> | |
26 | #include <asm/ip32/ip32_ints.h> | |
27 | ||
28 | MODULE_AUTHOR("Vivien Chappelier <vivien.chappelier@linux-mips.org"); | |
29 | MODULE_DESCRIPTION("SGI O2 MACE PS2 controller driver"); | |
30 | MODULE_LICENSE("GPL"); | |
31 | ||
32 | #define MACE_PS2_TIMEOUT 10000 /* in 50us unit */ | |
33 | ||
34 | #define PS2_STATUS_CLOCK_SIGNAL BIT(0) /* external clock signal */ | |
35 | #define PS2_STATUS_CLOCK_INHIBIT BIT(1) /* clken output signal */ | |
36 | #define PS2_STATUS_TX_INPROGRESS BIT(2) /* transmission in progress */ | |
37 | #define PS2_STATUS_TX_EMPTY BIT(3) /* empty transmit buffer */ | |
38 | #define PS2_STATUS_RX_FULL BIT(4) /* full receive buffer */ | |
39 | #define PS2_STATUS_RX_INPROGRESS BIT(5) /* reception in progress */ | |
40 | #define PS2_STATUS_ERROR_PARITY BIT(6) /* parity error */ | |
41 | #define PS2_STATUS_ERROR_FRAMING BIT(7) /* framing error */ | |
42 | ||
43 | #define PS2_CONTROL_TX_CLOCK_DISABLE BIT(0) /* inhibit clock signal after TX */ | |
44 | #define PS2_CONTROL_TX_ENABLE BIT(1) /* transmit enable */ | |
45 | #define PS2_CONTROL_TX_INT_ENABLE BIT(2) /* enable transmit interrupt */ | |
46 | #define PS2_CONTROL_RX_INT_ENABLE BIT(3) /* enable receive interrupt */ | |
47 | #define PS2_CONTROL_RX_CLOCK_ENABLE BIT(4) /* pause reception if set to 0 */ | |
48 | #define PS2_CONTROL_RESET BIT(5) /* reset */ | |
49 | ||
50 | struct maceps2_data { | |
51 | struct mace_ps2port *port; | |
52 | int irq; | |
53 | }; | |
54 | ||
55 | static struct maceps2_data port_data[2]; | |
56 | static struct serio *maceps2_port[2]; | |
57 | static struct platform_device *maceps2_device; | |
58 | ||
59 | static int maceps2_write(struct serio *dev, unsigned char val) | |
60 | { | |
61 | struct mace_ps2port *port = ((struct maceps2_data *)dev->port_data)->port; | |
62 | unsigned int timeout = MACE_PS2_TIMEOUT; | |
63 | ||
64 | do { | |
65 | if (port->status & PS2_STATUS_TX_EMPTY) { | |
66 | port->tx = val; | |
67 | return 0; | |
68 | } | |
69 | udelay(50); | |
70 | } while (timeout--); | |
71 | ||
72 | return -1; | |
73 | } | |
74 | ||
7d12e780 | 75 | static irqreturn_t maceps2_interrupt(int irq, void *dev_id) |
1da177e4 LT |
76 | { |
77 | struct serio *dev = dev_id; | |
78 | struct mace_ps2port *port = ((struct maceps2_data *)dev->port_data)->port; | |
79 | unsigned long byte; | |
80 | ||
81 | if (port->status & PS2_STATUS_RX_FULL) { | |
82 | byte = port->rx; | |
7d12e780 | 83 | serio_interrupt(dev, byte & 0xff, 0); |
1da177e4 LT |
84 | } |
85 | ||
86 | return IRQ_HANDLED; | |
87 | } | |
88 | ||
89 | static int maceps2_open(struct serio *dev) | |
90 | { | |
91 | struct maceps2_data *data = (struct maceps2_data *)dev->port_data; | |
92 | ||
93 | if (request_irq(data->irq, maceps2_interrupt, 0, "PS2 port", dev)) { | |
94 | printk(KERN_ERR "Could not allocate PS/2 IRQ\n"); | |
95 | return -EBUSY; | |
96 | } | |
97 | ||
98 | /* Reset port */ | |
99 | data->port->control = PS2_CONTROL_TX_CLOCK_DISABLE | PS2_CONTROL_RESET; | |
100 | udelay(100); | |
101 | ||
102 | /* Enable interrupts */ | |
103 | data->port->control = PS2_CONTROL_RX_CLOCK_ENABLE | | |
104 | PS2_CONTROL_TX_ENABLE | | |
105 | PS2_CONTROL_RX_INT_ENABLE; | |
106 | ||
107 | return 0; | |
108 | } | |
109 | ||
110 | static void maceps2_close(struct serio *dev) | |
111 | { | |
112 | struct maceps2_data *data = (struct maceps2_data *)dev->port_data; | |
113 | ||
114 | data->port->control = PS2_CONTROL_TX_CLOCK_DISABLE | PS2_CONTROL_RESET; | |
115 | udelay(100); | |
116 | free_irq(data->irq, dev); | |
117 | } | |
118 | ||
119 | ||
9d6c2502 | 120 | static struct serio * __devinit maceps2_allocate_port(int idx) |
1da177e4 LT |
121 | { |
122 | struct serio *serio; | |
123 | ||
9d6c2502 | 124 | serio = kzalloc(sizeof(struct serio), GFP_KERNEL); |
1da177e4 | 125 | if (serio) { |
1da177e4 LT |
126 | serio->id.type = SERIO_8042; |
127 | serio->write = maceps2_write; | |
128 | serio->open = maceps2_open; | |
129 | serio->close = maceps2_close; | |
130 | snprintf(serio->name, sizeof(serio->name), "MACE PS/2 port%d", idx); | |
131 | snprintf(serio->phys, sizeof(serio->phys), "mace/serio%d", idx); | |
132 | serio->port_data = &port_data[idx]; | |
133 | serio->dev.parent = &maceps2_device->dev; | |
134 | } | |
135 | ||
136 | return serio; | |
137 | } | |
138 | ||
9d6c2502 | 139 | static int __devinit maceps2_probe(struct platform_device *dev) |
1da177e4 | 140 | { |
1da177e4 LT |
141 | maceps2_port[0] = maceps2_allocate_port(0); |
142 | maceps2_port[1] = maceps2_allocate_port(1); | |
143 | if (!maceps2_port[0] || !maceps2_port[1]) { | |
144 | kfree(maceps2_port[0]); | |
145 | kfree(maceps2_port[1]); | |
1da177e4 LT |
146 | return -ENOMEM; |
147 | } | |
148 | ||
149 | serio_register_port(maceps2_port[0]); | |
150 | serio_register_port(maceps2_port[1]); | |
151 | ||
152 | return 0; | |
153 | } | |
154 | ||
9d6c2502 | 155 | static int __devexit maceps2_remove(struct platform_device *dev) |
1da177e4 LT |
156 | { |
157 | serio_unregister_port(maceps2_port[0]); | |
158 | serio_unregister_port(maceps2_port[1]); | |
9d6c2502 DT |
159 | |
160 | return 0; | |
161 | } | |
162 | ||
163 | static struct platform_driver maceps2_driver = { | |
164 | .driver = { | |
165 | .name = "maceps2", | |
166 | .owner = THIS_MODULE, | |
167 | }, | |
168 | .probe = maceps2_probe, | |
169 | .remove = __devexit_p(maceps2_remove), | |
170 | }; | |
171 | ||
172 | static int __init maceps2_init(void) | |
173 | { | |
174 | int error; | |
175 | ||
176 | error = platform_driver_register(&maceps2_driver); | |
177 | if (error) | |
178 | return error; | |
179 | ||
180 | maceps2_device = platform_device_alloc("maceps2", -1); | |
181 | if (!maceps2_device) { | |
182 | error = -ENOMEM; | |
183 | goto err_unregister_driver; | |
184 | } | |
185 | ||
186 | port_data[0].port = &mace->perif.ps2.keyb; | |
187 | port_data[0].irq = MACEISA_KEYB_IRQ; | |
188 | port_data[1].port = &mace->perif.ps2.mouse; | |
189 | port_data[1].irq = MACEISA_MOUSE_IRQ; | |
190 | ||
191 | error = platform_device_add(maceps2_device); | |
192 | if (error) | |
193 | goto err_free_device; | |
194 | ||
195 | return 0; | |
196 | ||
197 | err_free_device: | |
198 | platform_device_put(maceps2_device); | |
199 | err_unregister_driver: | |
200 | platform_driver_unregister(&maceps2_driver); | |
201 | return error; | |
202 | } | |
203 | ||
204 | static void __exit maceps2_exit(void) | |
205 | { | |
1da177e4 | 206 | platform_device_unregister(maceps2_device); |
9d6c2502 | 207 | platform_driver_unregister(&maceps2_driver); |
1da177e4 LT |
208 | } |
209 | ||
210 | module_init(maceps2_init); | |
211 | module_exit(maceps2_exit); |