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