1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (c) 2000-2001 Vojtech Pavlik |
4 | * |
5 | * Based on the work of: |
6 | * Richard Zidlicky <Richard.Zidlicky@stud.informatik.uni-erlangen.de> |
7 | */ |
8 | |
9 | /* |
10 | * Q40 PS/2 keyboard controller driver for Linux/m68k |
11 | */ |
12 | |
13 | #include <linux/module.h> |
14 | #include <linux/serio.h> |
15 | #include <linux/interrupt.h> |
16 | #include <linux/err.h> |
17 | #include <linux/bitops.h> |
18 | #include <linux/platform_device.h> |
19 | #include <linux/slab.h> |
20 | |
21 | #include <asm/io.h> |
22 | #include <linux/uaccess.h> |
23 | #include <asm/q40_master.h> |
24 | #include <asm/irq.h> |
25 | #include <asm/q40ints.h> |
26 | |
27 | #define DRV_NAME "q40kbd" |
28 | |
29 | MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>" ); |
30 | MODULE_DESCRIPTION("Q40 PS/2 keyboard controller driver" ); |
31 | MODULE_LICENSE("GPL" ); |
32 | MODULE_ALIAS("platform:" DRV_NAME); |
33 | |
34 | struct q40kbd { |
35 | struct serio *port; |
36 | spinlock_t lock; |
37 | }; |
38 | |
39 | static irqreturn_t q40kbd_interrupt(int irq, void *dev_id) |
40 | { |
41 | struct q40kbd *q40kbd = dev_id; |
42 | unsigned long flags; |
43 | |
44 | spin_lock_irqsave(&q40kbd->lock, flags); |
45 | |
46 | if (Q40_IRQ_KEYB_MASK & master_inb(INTERRUPT_REG)) |
47 | serio_interrupt(serio: q40kbd->port, data: master_inb(KEYCODE_REG), flags: 0); |
48 | |
49 | master_outb(-1, KEYBOARD_UNLOCK_REG); |
50 | |
51 | spin_unlock_irqrestore(lock: &q40kbd->lock, flags); |
52 | |
53 | return IRQ_HANDLED; |
54 | } |
55 | |
56 | /* |
57 | * q40kbd_flush() flushes all data that may be in the keyboard buffers |
58 | */ |
59 | |
60 | static void q40kbd_flush(struct q40kbd *q40kbd) |
61 | { |
62 | int maxread = 100; |
63 | unsigned long flags; |
64 | |
65 | spin_lock_irqsave(&q40kbd->lock, flags); |
66 | |
67 | while (maxread-- && (Q40_IRQ_KEYB_MASK & master_inb(INTERRUPT_REG))) |
68 | master_inb(KEYCODE_REG); |
69 | |
70 | spin_unlock_irqrestore(lock: &q40kbd->lock, flags); |
71 | } |
72 | |
73 | static void q40kbd_stop(void) |
74 | { |
75 | master_outb(0, KEY_IRQ_ENABLE_REG); |
76 | master_outb(-1, KEYBOARD_UNLOCK_REG); |
77 | } |
78 | |
79 | /* |
80 | * q40kbd_open() is called when a port is open by the higher layer. |
81 | * It allocates the interrupt and enables in in the chip. |
82 | */ |
83 | |
84 | static int q40kbd_open(struct serio *port) |
85 | { |
86 | struct q40kbd *q40kbd = port->port_data; |
87 | |
88 | q40kbd_flush(q40kbd); |
89 | |
90 | /* off we go */ |
91 | master_outb(-1, KEYBOARD_UNLOCK_REG); |
92 | master_outb(1, KEY_IRQ_ENABLE_REG); |
93 | |
94 | return 0; |
95 | } |
96 | |
97 | static void q40kbd_close(struct serio *port) |
98 | { |
99 | struct q40kbd *q40kbd = port->port_data; |
100 | |
101 | q40kbd_stop(); |
102 | q40kbd_flush(q40kbd); |
103 | } |
104 | |
105 | static int q40kbd_probe(struct platform_device *pdev) |
106 | { |
107 | struct q40kbd *q40kbd; |
108 | struct serio *port; |
109 | int error; |
110 | |
111 | q40kbd = kzalloc(size: sizeof(struct q40kbd), GFP_KERNEL); |
112 | port = kzalloc(size: sizeof(struct serio), GFP_KERNEL); |
113 | if (!q40kbd || !port) { |
114 | error = -ENOMEM; |
115 | goto err_free_mem; |
116 | } |
117 | |
118 | q40kbd->port = port; |
119 | spin_lock_init(&q40kbd->lock); |
120 | |
121 | port->id.type = SERIO_8042; |
122 | port->open = q40kbd_open; |
123 | port->close = q40kbd_close; |
124 | port->port_data = q40kbd; |
125 | port->dev.parent = &pdev->dev; |
126 | strscpy(p: port->name, q: "Q40 Kbd Port" , size: sizeof(port->name)); |
127 | strscpy(p: port->phys, q: "Q40" , size: sizeof(port->phys)); |
128 | |
129 | q40kbd_stop(); |
130 | |
131 | error = request_irq(irq: Q40_IRQ_KEYBOARD, handler: q40kbd_interrupt, flags: 0, |
132 | DRV_NAME, dev: q40kbd); |
133 | if (error) { |
134 | dev_err(&pdev->dev, "Can't get irq %d.\n" , Q40_IRQ_KEYBOARD); |
135 | goto err_free_mem; |
136 | } |
137 | |
138 | serio_register_port(q40kbd->port); |
139 | |
140 | platform_set_drvdata(pdev, data: q40kbd); |
141 | printk(KERN_INFO "serio: Q40 kbd registered\n" ); |
142 | |
143 | return 0; |
144 | |
145 | err_free_mem: |
146 | kfree(objp: port); |
147 | kfree(objp: q40kbd); |
148 | return error; |
149 | } |
150 | |
151 | static int q40kbd_remove(struct platform_device *pdev) |
152 | { |
153 | struct q40kbd *q40kbd = platform_get_drvdata(pdev); |
154 | |
155 | /* |
156 | * q40kbd_close() will be called as part of unregistering |
157 | * and will ensure that IRQ is turned off, so it is safe |
158 | * to unregister port first and free IRQ later. |
159 | */ |
160 | serio_unregister_port(serio: q40kbd->port); |
161 | free_irq(Q40_IRQ_KEYBOARD, q40kbd); |
162 | kfree(objp: q40kbd); |
163 | |
164 | return 0; |
165 | } |
166 | |
167 | static struct platform_driver q40kbd_driver = { |
168 | .driver = { |
169 | .name = "q40kbd" , |
170 | }, |
171 | .remove = q40kbd_remove, |
172 | }; |
173 | |
174 | module_platform_driver_probe(q40kbd_driver, q40kbd_probe); |
175 | |