diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/input/serio/maceps2.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/input/serio/maceps2.c')
-rw-r--r-- | drivers/input/serio/maceps2.c | 176 |
1 files changed, 176 insertions, 0 deletions
diff --git a/drivers/input/serio/maceps2.c b/drivers/input/serio/maceps2.c new file mode 100644 index 000000000000..9880fc145d90 --- /dev/null +++ b/drivers/input/serio/maceps2.c | |||
@@ -0,0 +1,176 @@ | |||
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> | ||
17 | #include <linux/device.h> | ||
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); | ||