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 | ||
75 | static irqreturn_t maceps2_interrupt(int irq, void *dev_id, | |
76 | struct pt_regs *regs) | |
77 | { | |
78 | struct serio *dev = dev_id; | |
79 | struct mace_ps2port *port = ((struct maceps2_data *)dev->port_data)->port; | |
80 | unsigned long byte; | |
81 | ||
82 | if (port->status & PS2_STATUS_RX_FULL) { | |
83 | byte = port->rx; | |
84 | serio_interrupt(dev, byte & 0xff, 0, regs); | |
85 | } | |
86 | ||
87 | return IRQ_HANDLED; | |
88 | } | |
89 | ||
90 | static int maceps2_open(struct serio *dev) | |
91 | { | |
92 | struct maceps2_data *data = (struct maceps2_data *)dev->port_data; | |
93 | ||
94 | if (request_irq(data->irq, maceps2_interrupt, 0, "PS2 port", dev)) { | |
95 | printk(KERN_ERR "Could not allocate PS/2 IRQ\n"); | |
96 | return -EBUSY; | |
97 | } | |
98 | ||
99 | /* Reset port */ | |
100 | data->port->control = PS2_CONTROL_TX_CLOCK_DISABLE | PS2_CONTROL_RESET; | |
101 | udelay(100); | |
102 | ||
103 | /* Enable interrupts */ | |
104 | data->port->control = PS2_CONTROL_RX_CLOCK_ENABLE | | |
105 | PS2_CONTROL_TX_ENABLE | | |
106 | PS2_CONTROL_RX_INT_ENABLE; | |
107 | ||
108 | return 0; | |
109 | } | |
110 | ||
111 | static void maceps2_close(struct serio *dev) | |
112 | { | |
113 | struct maceps2_data *data = (struct maceps2_data *)dev->port_data; | |
114 | ||
115 | data->port->control = PS2_CONTROL_TX_CLOCK_DISABLE | PS2_CONTROL_RESET; | |
116 | udelay(100); | |
117 | free_irq(data->irq, dev); | |
118 | } | |
119 | ||
120 | ||
121 | static struct serio * __init maceps2_allocate_port(int idx) | |
122 | { | |
123 | struct serio *serio; | |
124 | ||
125 | serio = kmalloc(sizeof(struct serio), GFP_KERNEL); | |
126 | if (serio) { | |
127 | memset(serio, 0, sizeof(struct serio)); | |
128 | serio->id.type = SERIO_8042; | |
129 | serio->write = maceps2_write; | |
130 | serio->open = maceps2_open; | |
131 | serio->close = maceps2_close; | |
132 | snprintf(serio->name, sizeof(serio->name), "MACE PS/2 port%d", idx); | |
133 | snprintf(serio->phys, sizeof(serio->phys), "mace/serio%d", idx); | |
134 | serio->port_data = &port_data[idx]; | |
135 | serio->dev.parent = &maceps2_device->dev; | |
136 | } | |
137 | ||
138 | return serio; | |
139 | } | |
140 | ||
141 | ||
142 | static int __init maceps2_init(void) | |
143 | { | |
144 | maceps2_device = platform_device_register_simple("maceps2", -1, NULL, 0); | |
145 | if (IS_ERR(maceps2_device)) | |
146 | return PTR_ERR(maceps2_device); | |
147 | ||
148 | port_data[0].port = &mace->perif.ps2.keyb; | |
149 | port_data[0].irq = MACEISA_KEYB_IRQ; | |
150 | port_data[1].port = &mace->perif.ps2.mouse; | |
151 | port_data[1].irq = MACEISA_MOUSE_IRQ; | |
152 | ||
153 | maceps2_port[0] = maceps2_allocate_port(0); | |
154 | maceps2_port[1] = maceps2_allocate_port(1); | |
155 | if (!maceps2_port[0] || !maceps2_port[1]) { | |
156 | kfree(maceps2_port[0]); | |
157 | kfree(maceps2_port[1]); | |
158 | platform_device_unregister(maceps2_device); | |
159 | return -ENOMEM; | |
160 | } | |
161 | ||
162 | serio_register_port(maceps2_port[0]); | |
163 | serio_register_port(maceps2_port[1]); | |
164 | ||
165 | return 0; | |
166 | } | |
167 | ||
168 | static void __exit maceps2_exit(void) | |
169 | { | |
170 | serio_unregister_port(maceps2_port[0]); | |
171 | serio_unregister_port(maceps2_port[1]); | |
172 | platform_device_unregister(maceps2_device); | |
173 | } | |
174 | ||
175 | module_init(maceps2_init); | |
176 | module_exit(maceps2_exit); |