xref: /OK3568_Linux_fs/kernel/drivers/input/keyboard/locomokbd.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * LoCoMo keyboard driver for Linux-based ARM PDAs:
4*4882a593Smuzhiyun  * 	- SHARP Zaurus Collie (SL-5500)
5*4882a593Smuzhiyun  * 	- SHARP Zaurus Poodle (SL-5600)
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  * Copyright (c) 2005 John Lenz
8*4882a593Smuzhiyun  * Based on from xtkbd.c
9*4882a593Smuzhiyun  */
10*4882a593Smuzhiyun 
11*4882a593Smuzhiyun #include <linux/slab.h>
12*4882a593Smuzhiyun #include <linux/module.h>
13*4882a593Smuzhiyun #include <linux/init.h>
14*4882a593Smuzhiyun #include <linux/input.h>
15*4882a593Smuzhiyun #include <linux/delay.h>
16*4882a593Smuzhiyun #include <linux/device.h>
17*4882a593Smuzhiyun #include <linux/interrupt.h>
18*4882a593Smuzhiyun #include <linux/ioport.h>
19*4882a593Smuzhiyun 
20*4882a593Smuzhiyun #include <asm/hardware/locomo.h>
21*4882a593Smuzhiyun #include <asm/irq.h>
22*4882a593Smuzhiyun 
23*4882a593Smuzhiyun MODULE_AUTHOR("John Lenz <lenz@cs.wisc.edu>");
24*4882a593Smuzhiyun MODULE_DESCRIPTION("LoCoMo keyboard driver");
25*4882a593Smuzhiyun MODULE_LICENSE("GPL");
26*4882a593Smuzhiyun 
27*4882a593Smuzhiyun #define LOCOMOKBD_NUMKEYS	128
28*4882a593Smuzhiyun 
29*4882a593Smuzhiyun #define KEY_ACTIVITY		KEY_F16
30*4882a593Smuzhiyun #define KEY_CONTACT		KEY_F18
31*4882a593Smuzhiyun #define KEY_CENTER		KEY_F15
32*4882a593Smuzhiyun 
33*4882a593Smuzhiyun static const unsigned char
34*4882a593Smuzhiyun locomokbd_keycode[LOCOMOKBD_NUMKEYS] = {
35*4882a593Smuzhiyun 	0, KEY_ESC, KEY_ACTIVITY, 0, 0, 0, 0, 0, 0, 0,				/* 0 - 9 */
36*4882a593Smuzhiyun 	0, 0, 0, 0, 0, 0, 0, KEY_MENU, KEY_HOME, KEY_CONTACT,			/* 10 - 19 */
37*4882a593Smuzhiyun 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0,						/* 20 - 29 */
38*4882a593Smuzhiyun 	0, 0, 0, KEY_CENTER, 0, KEY_MAIL, 0, 0, 0, 0,				/* 30 - 39 */
39*4882a593Smuzhiyun 	0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_RIGHT,					/* 40 - 49 */
40*4882a593Smuzhiyun 	KEY_UP, KEY_LEFT, 0, 0, KEY_P, 0, KEY_O, KEY_I, KEY_Y, KEY_T,		/* 50 - 59 */
41*4882a593Smuzhiyun 	KEY_E, KEY_W, 0, 0, 0, 0, KEY_DOWN, KEY_ENTER, 0, 0,			/* 60 - 69 */
42*4882a593Smuzhiyun 	KEY_BACKSPACE, 0, KEY_L, KEY_U, KEY_H, KEY_R, KEY_D, KEY_Q, 0, 0,	/* 70 - 79 */
43*4882a593Smuzhiyun 	0, 0, 0, 0, 0, 0, KEY_ENTER, KEY_RIGHTSHIFT, KEY_K, KEY_J,		/* 80 - 89 */
44*4882a593Smuzhiyun 	KEY_G, KEY_F, KEY_X, KEY_S, 0, 0, 0, 0, 0, 0,				/* 90 - 99 */
45*4882a593Smuzhiyun 	0, 0, KEY_DOT, 0, KEY_COMMA, KEY_N, KEY_B, KEY_C, KEY_Z, KEY_A,		/* 100 - 109 */
46*4882a593Smuzhiyun 	KEY_LEFTSHIFT, KEY_TAB, KEY_LEFTCTRL, 0, 0, 0, 0, 0, 0, 0,		/* 110 - 119 */
47*4882a593Smuzhiyun 	KEY_M, KEY_SPACE, KEY_V, KEY_APOSTROPHE, KEY_SLASH, 0, 0, 0		/* 120 - 128 */
48*4882a593Smuzhiyun };
49*4882a593Smuzhiyun 
50*4882a593Smuzhiyun #define KB_ROWS			16
51*4882a593Smuzhiyun #define KB_COLS			8
52*4882a593Smuzhiyun #define KB_ROWMASK(r)		(1 << (r))
53*4882a593Smuzhiyun #define SCANCODE(c,r)		( ((c)<<4) + (r) + 1 )
54*4882a593Smuzhiyun 
55*4882a593Smuzhiyun #define KB_DELAY		8
56*4882a593Smuzhiyun #define SCAN_INTERVAL		(HZ/10)
57*4882a593Smuzhiyun 
58*4882a593Smuzhiyun struct locomokbd {
59*4882a593Smuzhiyun 	unsigned char keycode[LOCOMOKBD_NUMKEYS];
60*4882a593Smuzhiyun 	struct input_dev *input;
61*4882a593Smuzhiyun 	char phys[32];
62*4882a593Smuzhiyun 
63*4882a593Smuzhiyun 	unsigned long base;
64*4882a593Smuzhiyun 	spinlock_t lock;
65*4882a593Smuzhiyun 
66*4882a593Smuzhiyun 	struct timer_list timer;
67*4882a593Smuzhiyun 	unsigned long suspend_jiffies;
68*4882a593Smuzhiyun 	unsigned int count_cancel;
69*4882a593Smuzhiyun };
70*4882a593Smuzhiyun 
71*4882a593Smuzhiyun /* helper functions for reading the keyboard matrix */
locomokbd_charge_all(unsigned long membase)72*4882a593Smuzhiyun static inline void locomokbd_charge_all(unsigned long membase)
73*4882a593Smuzhiyun {
74*4882a593Smuzhiyun 	locomo_writel(0x00FF, membase + LOCOMO_KSC);
75*4882a593Smuzhiyun }
76*4882a593Smuzhiyun 
locomokbd_activate_all(unsigned long membase)77*4882a593Smuzhiyun static inline void locomokbd_activate_all(unsigned long membase)
78*4882a593Smuzhiyun {
79*4882a593Smuzhiyun 	unsigned long r;
80*4882a593Smuzhiyun 
81*4882a593Smuzhiyun 	locomo_writel(0, membase + LOCOMO_KSC);
82*4882a593Smuzhiyun 	r = locomo_readl(membase + LOCOMO_KIC);
83*4882a593Smuzhiyun 	r &= 0xFEFF;
84*4882a593Smuzhiyun 	locomo_writel(r, membase + LOCOMO_KIC);
85*4882a593Smuzhiyun }
86*4882a593Smuzhiyun 
locomokbd_activate_col(unsigned long membase,int col)87*4882a593Smuzhiyun static inline void locomokbd_activate_col(unsigned long membase, int col)
88*4882a593Smuzhiyun {
89*4882a593Smuzhiyun 	unsigned short nset;
90*4882a593Smuzhiyun 	unsigned short nbset;
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun 	nset = 0xFF & ~(1 << col);
93*4882a593Smuzhiyun 	nbset = (nset << 8) + nset;
94*4882a593Smuzhiyun 	locomo_writel(nbset, membase + LOCOMO_KSC);
95*4882a593Smuzhiyun }
96*4882a593Smuzhiyun 
locomokbd_reset_col(unsigned long membase,int col)97*4882a593Smuzhiyun static inline void locomokbd_reset_col(unsigned long membase, int col)
98*4882a593Smuzhiyun {
99*4882a593Smuzhiyun 	unsigned short nbset;
100*4882a593Smuzhiyun 
101*4882a593Smuzhiyun 	nbset = ((0xFF & ~(1 << col)) << 8) + 0xFF;
102*4882a593Smuzhiyun 	locomo_writel(nbset, membase + LOCOMO_KSC);
103*4882a593Smuzhiyun }
104*4882a593Smuzhiyun 
105*4882a593Smuzhiyun /*
106*4882a593Smuzhiyun  * The LoCoMo keyboard only generates interrupts when a key is pressed.
107*4882a593Smuzhiyun  * So when a key is pressed, we enable a timer.  This timer scans the
108*4882a593Smuzhiyun  * keyboard, and this is how we detect when the key is released.
109*4882a593Smuzhiyun  */
110*4882a593Smuzhiyun 
111*4882a593Smuzhiyun /* Scan the hardware keyboard and push any changes up through the input layer */
locomokbd_scankeyboard(struct locomokbd * locomokbd)112*4882a593Smuzhiyun static void locomokbd_scankeyboard(struct locomokbd *locomokbd)
113*4882a593Smuzhiyun {
114*4882a593Smuzhiyun 	unsigned int row, col, rowd;
115*4882a593Smuzhiyun 	unsigned long flags;
116*4882a593Smuzhiyun 	unsigned int num_pressed;
117*4882a593Smuzhiyun 	unsigned long membase = locomokbd->base;
118*4882a593Smuzhiyun 
119*4882a593Smuzhiyun 	spin_lock_irqsave(&locomokbd->lock, flags);
120*4882a593Smuzhiyun 
121*4882a593Smuzhiyun 	locomokbd_charge_all(membase);
122*4882a593Smuzhiyun 
123*4882a593Smuzhiyun 	num_pressed = 0;
124*4882a593Smuzhiyun 	for (col = 0; col < KB_COLS; col++) {
125*4882a593Smuzhiyun 
126*4882a593Smuzhiyun 		locomokbd_activate_col(membase, col);
127*4882a593Smuzhiyun 		udelay(KB_DELAY);
128*4882a593Smuzhiyun 
129*4882a593Smuzhiyun 		rowd = ~locomo_readl(membase + LOCOMO_KIB);
130*4882a593Smuzhiyun 		for (row = 0; row < KB_ROWS; row++) {
131*4882a593Smuzhiyun 			unsigned int scancode, pressed, key;
132*4882a593Smuzhiyun 
133*4882a593Smuzhiyun 			scancode = SCANCODE(col, row);
134*4882a593Smuzhiyun 			pressed = rowd & KB_ROWMASK(row);
135*4882a593Smuzhiyun 			key = locomokbd->keycode[scancode];
136*4882a593Smuzhiyun 
137*4882a593Smuzhiyun 			input_report_key(locomokbd->input, key, pressed);
138*4882a593Smuzhiyun 			if (likely(!pressed))
139*4882a593Smuzhiyun 				continue;
140*4882a593Smuzhiyun 
141*4882a593Smuzhiyun 			num_pressed++;
142*4882a593Smuzhiyun 
143*4882a593Smuzhiyun 			/* The "Cancel/ESC" key is labeled "On/Off" on
144*4882a593Smuzhiyun 			 * Collie and Poodle and should suspend the device
145*4882a593Smuzhiyun 			 * if it was pressed for more than a second. */
146*4882a593Smuzhiyun 			if (unlikely(key == KEY_ESC)) {
147*4882a593Smuzhiyun 				if (!time_after(jiffies,
148*4882a593Smuzhiyun 					locomokbd->suspend_jiffies + HZ))
149*4882a593Smuzhiyun 					continue;
150*4882a593Smuzhiyun 				if (locomokbd->count_cancel++
151*4882a593Smuzhiyun 					!= (HZ/SCAN_INTERVAL + 1))
152*4882a593Smuzhiyun 					continue;
153*4882a593Smuzhiyun 				input_event(locomokbd->input, EV_PWR,
154*4882a593Smuzhiyun 					KEY_SUSPEND, 1);
155*4882a593Smuzhiyun 				locomokbd->suspend_jiffies = jiffies;
156*4882a593Smuzhiyun 			} else
157*4882a593Smuzhiyun 				locomokbd->count_cancel = 0;
158*4882a593Smuzhiyun 		}
159*4882a593Smuzhiyun 		locomokbd_reset_col(membase, col);
160*4882a593Smuzhiyun 	}
161*4882a593Smuzhiyun 	locomokbd_activate_all(membase);
162*4882a593Smuzhiyun 
163*4882a593Smuzhiyun 	input_sync(locomokbd->input);
164*4882a593Smuzhiyun 
165*4882a593Smuzhiyun 	/* if any keys are pressed, enable the timer */
166*4882a593Smuzhiyun 	if (num_pressed)
167*4882a593Smuzhiyun 		mod_timer(&locomokbd->timer, jiffies + SCAN_INTERVAL);
168*4882a593Smuzhiyun 	else
169*4882a593Smuzhiyun 		locomokbd->count_cancel = 0;
170*4882a593Smuzhiyun 
171*4882a593Smuzhiyun 	spin_unlock_irqrestore(&locomokbd->lock, flags);
172*4882a593Smuzhiyun }
173*4882a593Smuzhiyun 
174*4882a593Smuzhiyun /*
175*4882a593Smuzhiyun  * LoCoMo keyboard interrupt handler.
176*4882a593Smuzhiyun  */
locomokbd_interrupt(int irq,void * dev_id)177*4882a593Smuzhiyun static irqreturn_t locomokbd_interrupt(int irq, void *dev_id)
178*4882a593Smuzhiyun {
179*4882a593Smuzhiyun 	struct locomokbd *locomokbd = dev_id;
180*4882a593Smuzhiyun 	u16 r;
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun 	r = locomo_readl(locomokbd->base + LOCOMO_KIC);
183*4882a593Smuzhiyun 	if ((r & 0x0001) == 0)
184*4882a593Smuzhiyun 		return IRQ_HANDLED;
185*4882a593Smuzhiyun 
186*4882a593Smuzhiyun 	locomo_writel(r & ~0x0100, locomokbd->base + LOCOMO_KIC); /* Ack */
187*4882a593Smuzhiyun 
188*4882a593Smuzhiyun 	/** wait chattering delay **/
189*4882a593Smuzhiyun 	udelay(100);
190*4882a593Smuzhiyun 
191*4882a593Smuzhiyun 	locomokbd_scankeyboard(locomokbd);
192*4882a593Smuzhiyun 	return IRQ_HANDLED;
193*4882a593Smuzhiyun }
194*4882a593Smuzhiyun 
195*4882a593Smuzhiyun /*
196*4882a593Smuzhiyun  * LoCoMo timer checking for released keys
197*4882a593Smuzhiyun  */
locomokbd_timer_callback(struct timer_list * t)198*4882a593Smuzhiyun static void locomokbd_timer_callback(struct timer_list *t)
199*4882a593Smuzhiyun {
200*4882a593Smuzhiyun 	struct locomokbd *locomokbd = from_timer(locomokbd, t, timer);
201*4882a593Smuzhiyun 
202*4882a593Smuzhiyun 	locomokbd_scankeyboard(locomokbd);
203*4882a593Smuzhiyun }
204*4882a593Smuzhiyun 
locomokbd_open(struct input_dev * dev)205*4882a593Smuzhiyun static int locomokbd_open(struct input_dev *dev)
206*4882a593Smuzhiyun {
207*4882a593Smuzhiyun 	struct locomokbd *locomokbd = input_get_drvdata(dev);
208*4882a593Smuzhiyun 	u16 r;
209*4882a593Smuzhiyun 
210*4882a593Smuzhiyun 	r = locomo_readl(locomokbd->base + LOCOMO_KIC) | 0x0010;
211*4882a593Smuzhiyun 	locomo_writel(r, locomokbd->base + LOCOMO_KIC);
212*4882a593Smuzhiyun 	return 0;
213*4882a593Smuzhiyun }
214*4882a593Smuzhiyun 
locomokbd_close(struct input_dev * dev)215*4882a593Smuzhiyun static void locomokbd_close(struct input_dev *dev)
216*4882a593Smuzhiyun {
217*4882a593Smuzhiyun 	struct locomokbd *locomokbd = input_get_drvdata(dev);
218*4882a593Smuzhiyun 	u16 r;
219*4882a593Smuzhiyun 
220*4882a593Smuzhiyun 	r = locomo_readl(locomokbd->base + LOCOMO_KIC) & ~0x0010;
221*4882a593Smuzhiyun 	locomo_writel(r, locomokbd->base + LOCOMO_KIC);
222*4882a593Smuzhiyun }
223*4882a593Smuzhiyun 
locomokbd_probe(struct locomo_dev * dev)224*4882a593Smuzhiyun static int locomokbd_probe(struct locomo_dev *dev)
225*4882a593Smuzhiyun {
226*4882a593Smuzhiyun 	struct locomokbd *locomokbd;
227*4882a593Smuzhiyun 	struct input_dev *input_dev;
228*4882a593Smuzhiyun 	int i, err;
229*4882a593Smuzhiyun 
230*4882a593Smuzhiyun 	locomokbd = kzalloc(sizeof(struct locomokbd), GFP_KERNEL);
231*4882a593Smuzhiyun 	input_dev = input_allocate_device();
232*4882a593Smuzhiyun 	if (!locomokbd || !input_dev) {
233*4882a593Smuzhiyun 		err = -ENOMEM;
234*4882a593Smuzhiyun 		goto err_free_mem;
235*4882a593Smuzhiyun 	}
236*4882a593Smuzhiyun 
237*4882a593Smuzhiyun 	/* try and claim memory region */
238*4882a593Smuzhiyun 	if (!request_mem_region((unsigned long) dev->mapbase,
239*4882a593Smuzhiyun 				dev->length,
240*4882a593Smuzhiyun 				LOCOMO_DRIVER_NAME(dev))) {
241*4882a593Smuzhiyun 		err = -EBUSY;
242*4882a593Smuzhiyun 		printk(KERN_ERR "locomokbd: Can't acquire access to io memory for keyboard\n");
243*4882a593Smuzhiyun 		goto err_free_mem;
244*4882a593Smuzhiyun 	}
245*4882a593Smuzhiyun 
246*4882a593Smuzhiyun 	locomo_set_drvdata(dev, locomokbd);
247*4882a593Smuzhiyun 
248*4882a593Smuzhiyun 	locomokbd->base = (unsigned long) dev->mapbase;
249*4882a593Smuzhiyun 
250*4882a593Smuzhiyun 	spin_lock_init(&locomokbd->lock);
251*4882a593Smuzhiyun 
252*4882a593Smuzhiyun 	timer_setup(&locomokbd->timer, locomokbd_timer_callback, 0);
253*4882a593Smuzhiyun 
254*4882a593Smuzhiyun 	locomokbd->suspend_jiffies = jiffies;
255*4882a593Smuzhiyun 
256*4882a593Smuzhiyun 	locomokbd->input = input_dev;
257*4882a593Smuzhiyun 	strcpy(locomokbd->phys, "locomokbd/input0");
258*4882a593Smuzhiyun 
259*4882a593Smuzhiyun 	input_dev->name = "LoCoMo keyboard";
260*4882a593Smuzhiyun 	input_dev->phys = locomokbd->phys;
261*4882a593Smuzhiyun 	input_dev->id.bustype = BUS_HOST;
262*4882a593Smuzhiyun 	input_dev->id.vendor = 0x0001;
263*4882a593Smuzhiyun 	input_dev->id.product = 0x0001;
264*4882a593Smuzhiyun 	input_dev->id.version = 0x0100;
265*4882a593Smuzhiyun 	input_dev->open = locomokbd_open;
266*4882a593Smuzhiyun 	input_dev->close = locomokbd_close;
267*4882a593Smuzhiyun 	input_dev->dev.parent = &dev->dev;
268*4882a593Smuzhiyun 
269*4882a593Smuzhiyun 	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP) |
270*4882a593Smuzhiyun 				BIT_MASK(EV_PWR);
271*4882a593Smuzhiyun 	input_dev->keycode = locomokbd->keycode;
272*4882a593Smuzhiyun 	input_dev->keycodesize = sizeof(locomokbd_keycode[0]);
273*4882a593Smuzhiyun 	input_dev->keycodemax = ARRAY_SIZE(locomokbd_keycode);
274*4882a593Smuzhiyun 
275*4882a593Smuzhiyun 	input_set_drvdata(input_dev, locomokbd);
276*4882a593Smuzhiyun 
277*4882a593Smuzhiyun 	memcpy(locomokbd->keycode, locomokbd_keycode, sizeof(locomokbd->keycode));
278*4882a593Smuzhiyun 	for (i = 0; i < LOCOMOKBD_NUMKEYS; i++)
279*4882a593Smuzhiyun 		set_bit(locomokbd->keycode[i], input_dev->keybit);
280*4882a593Smuzhiyun 	clear_bit(0, input_dev->keybit);
281*4882a593Smuzhiyun 
282*4882a593Smuzhiyun 	/* attempt to get the interrupt */
283*4882a593Smuzhiyun 	err = request_irq(dev->irq[0], locomokbd_interrupt, 0, "locomokbd", locomokbd);
284*4882a593Smuzhiyun 	if (err) {
285*4882a593Smuzhiyun 		printk(KERN_ERR "locomokbd: Can't get irq for keyboard\n");
286*4882a593Smuzhiyun 		goto err_release_region;
287*4882a593Smuzhiyun 	}
288*4882a593Smuzhiyun 
289*4882a593Smuzhiyun 	err = input_register_device(locomokbd->input);
290*4882a593Smuzhiyun 	if (err)
291*4882a593Smuzhiyun 		goto err_free_irq;
292*4882a593Smuzhiyun 
293*4882a593Smuzhiyun 	return 0;
294*4882a593Smuzhiyun 
295*4882a593Smuzhiyun  err_free_irq:
296*4882a593Smuzhiyun 	free_irq(dev->irq[0], locomokbd);
297*4882a593Smuzhiyun  err_release_region:
298*4882a593Smuzhiyun 	release_mem_region((unsigned long) dev->mapbase, dev->length);
299*4882a593Smuzhiyun 	locomo_set_drvdata(dev, NULL);
300*4882a593Smuzhiyun  err_free_mem:
301*4882a593Smuzhiyun 	input_free_device(input_dev);
302*4882a593Smuzhiyun 	kfree(locomokbd);
303*4882a593Smuzhiyun 
304*4882a593Smuzhiyun 	return err;
305*4882a593Smuzhiyun }
306*4882a593Smuzhiyun 
locomokbd_remove(struct locomo_dev * dev)307*4882a593Smuzhiyun static int locomokbd_remove(struct locomo_dev *dev)
308*4882a593Smuzhiyun {
309*4882a593Smuzhiyun 	struct locomokbd *locomokbd = locomo_get_drvdata(dev);
310*4882a593Smuzhiyun 
311*4882a593Smuzhiyun 	free_irq(dev->irq[0], locomokbd);
312*4882a593Smuzhiyun 
313*4882a593Smuzhiyun 	del_timer_sync(&locomokbd->timer);
314*4882a593Smuzhiyun 
315*4882a593Smuzhiyun 	input_unregister_device(locomokbd->input);
316*4882a593Smuzhiyun 	locomo_set_drvdata(dev, NULL);
317*4882a593Smuzhiyun 
318*4882a593Smuzhiyun 	release_mem_region((unsigned long) dev->mapbase, dev->length);
319*4882a593Smuzhiyun 
320*4882a593Smuzhiyun 	kfree(locomokbd);
321*4882a593Smuzhiyun 
322*4882a593Smuzhiyun 	return 0;
323*4882a593Smuzhiyun }
324*4882a593Smuzhiyun 
325*4882a593Smuzhiyun static struct locomo_driver keyboard_driver = {
326*4882a593Smuzhiyun 	.drv = {
327*4882a593Smuzhiyun 		.name = "locomokbd"
328*4882a593Smuzhiyun 	},
329*4882a593Smuzhiyun 	.devid	= LOCOMO_DEVID_KEYBOARD,
330*4882a593Smuzhiyun 	.probe	= locomokbd_probe,
331*4882a593Smuzhiyun 	.remove	= locomokbd_remove,
332*4882a593Smuzhiyun };
333*4882a593Smuzhiyun 
locomokbd_init(void)334*4882a593Smuzhiyun static int __init locomokbd_init(void)
335*4882a593Smuzhiyun {
336*4882a593Smuzhiyun 	return locomo_driver_register(&keyboard_driver);
337*4882a593Smuzhiyun }
338*4882a593Smuzhiyun 
locomokbd_exit(void)339*4882a593Smuzhiyun static void __exit locomokbd_exit(void)
340*4882a593Smuzhiyun {
341*4882a593Smuzhiyun 	locomo_driver_unregister(&keyboard_driver);
342*4882a593Smuzhiyun }
343*4882a593Smuzhiyun 
344*4882a593Smuzhiyun module_init(locomokbd_init);
345*4882a593Smuzhiyun module_exit(locomokbd_exit);
346