]> git.sur5r.net Git - u-boot/blob - drivers/input/i8042.c
input: i8042: Convert to use the input library
[u-boot] / drivers / input / i8042.c
1 /*
2  * (C) Copyright 2002 ELTEC Elektronik AG
3  * Frank Gottschling <fgottschling@eltec.de>
4  *
5  * SPDX-License-Identifier:     GPL-2.0+
6  */
7
8 /* i8042.c - Intel 8042 keyboard driver routines */
9
10 #include <common.h>
11 #include <i8042.h>
12 #include <input.h>
13 #include <asm/io.h>
14
15 /* defines */
16 #define in8(p)          inb(p)
17 #define out8(p, v)      outb(v, p)
18
19 /* locals */
20 static struct input_config config;
21 static bool extended;
22
23 static unsigned char ext_key_map[] = {
24         0x1c, /* keypad enter */
25         0x1d, /* right control */
26         0x35, /* keypad slash */
27         0x37, /* print screen */
28         0x38, /* right alt */
29         0x46, /* break */
30         0x47, /* editpad home */
31         0x48, /* editpad up */
32         0x49, /* editpad pgup */
33         0x4b, /* editpad left */
34         0x4d, /* editpad right */
35         0x4f, /* editpad end */
36         0x50, /* editpad dn */
37         0x51, /* editpad pgdn */
38         0x52, /* editpad ins */
39         0x53, /* editpad del */
40         0x00  /* map end */
41         };
42
43 static int kbd_input_empty(void)
44 {
45         int kbd_timeout = KBD_TIMEOUT * 1000;
46
47         while ((in8(I8042_STS_REG) & STATUS_IBF) && kbd_timeout--)
48                 udelay(1);
49
50         return kbd_timeout != -1;
51 }
52
53 static int kbd_output_full(void)
54 {
55         int kbd_timeout = KBD_TIMEOUT * 1000;
56
57         while (((in8(I8042_STS_REG) & STATUS_OBF) == 0) && kbd_timeout--)
58                 udelay(1);
59
60         return kbd_timeout != -1;
61 }
62
63 static void kbd_led_set(int flags)
64 {
65         kbd_input_empty();
66         out8(I8042_DATA_REG, CMD_SET_KBD_LED);
67         kbd_input_empty();
68         out8(I8042_DATA_REG, flags & 0x7);
69 }
70
71 static int kbd_write(int reg, int value)
72 {
73         if (!kbd_input_empty())
74                 return -1;
75         out8(reg, value);
76
77         return 0;
78 }
79
80 static int kbd_read(int reg)
81 {
82         if (!kbd_output_full())
83                 return -1;
84
85         return in8(reg);
86 }
87
88 static int kbd_cmd_read(int cmd)
89 {
90         if (kbd_write(I8042_CMD_REG, cmd))
91                 return -1;
92
93         return kbd_read(I8042_DATA_REG);
94 }
95
96 static int kbd_cmd_write(int cmd, int data)
97 {
98         if (kbd_write(I8042_CMD_REG, cmd))
99                 return -1;
100
101         return kbd_write(I8042_DATA_REG, data);
102 }
103
104 static int kbd_reset(void)
105 {
106         int config;
107
108         /* controller self test */
109         if (kbd_cmd_read(CMD_SELF_TEST) != KBC_TEST_OK)
110                 goto err;
111
112         /* keyboard reset */
113         if (kbd_write(I8042_DATA_REG, CMD_RESET_KBD) ||
114             kbd_read(I8042_DATA_REG) != KBD_ACK ||
115             kbd_read(I8042_DATA_REG) != KBD_POR)
116                 goto err;
117
118         /* set AT translation and disable irq */
119         config = kbd_cmd_read(CMD_RD_CONFIG);
120         if (config == -1)
121                 goto err;
122
123         config |= CONFIG_AT_TRANS;
124         config &= ~(CONFIG_KIRQ_EN | CONFIG_MIRQ_EN);
125         if (kbd_cmd_write(CMD_WR_CONFIG, config))
126                 goto err;
127
128         /* enable keyboard */
129         if (kbd_write(I8042_CMD_REG, CMD_KBD_EN) ||
130             !kbd_input_empty())
131                 goto err;
132
133         return 0;
134 err:
135         debug("%s: Keyboard failure\n", __func__);
136         return -1;
137 }
138
139 static int kbd_controller_present(void)
140 {
141         return in8(I8042_STS_REG) != 0xff;
142 }
143
144 /*
145  * Implement a weak default function for boards that optionally
146  * need to skip the i8042 initialization.
147  */
148 int __weak board_i8042_skip(void)
149 {
150         /* As default, don't skip */
151         return 0;
152 }
153
154 void i8042_flush(void)
155 {
156         int timeout;
157
158         /*
159          * The delay is to give the keyboard controller some time
160          * to fill the next byte.
161          */
162         while (1) {
163                 timeout = 100;  /* wait for no longer than 100us */
164                 while (timeout > 0 && !(in8(I8042_STS_REG) & STATUS_OBF)) {
165                         udelay(1);
166                         timeout--;
167                 }
168
169                 /* Try to pull next byte if not timeout */
170                 if (in8(I8042_STS_REG) & STATUS_OBF)
171                         in8(I8042_DATA_REG);
172                 else
173                         break;
174         }
175 }
176
177 int i8042_disable(void)
178 {
179         if (kbd_input_empty() == 0)
180                 return -1;
181
182         /* Disable keyboard */
183         out8(I8042_CMD_REG, CMD_KBD_DIS);
184
185         if (kbd_input_empty() == 0)
186                 return -1;
187
188         return 0;
189 }
190
191 static int i8042_kbd_check(struct input_config *input)
192 {
193         if ((in8(I8042_STS_REG) & STATUS_OBF) == 0) {
194                 return 0;
195         } else {
196                 bool release = false;
197                 int scan_code;
198                 int i;
199
200                 scan_code = in8(I8042_DATA_REG);
201                 if (scan_code == 0xfa) {
202                         return 0;
203                 } else if (scan_code == 0xe0) {
204                         extended = true;
205                         return 0;
206                 }
207                 if (scan_code & 0x80) {
208                         scan_code &= 0x7f;
209                         release = true;
210                 }
211                 if (extended) {
212                         extended = false;
213                         for (i = 0; ext_key_map[i]; i++) {
214                                 if (ext_key_map[i] == scan_code) {
215                                         scan_code = 0x60 + i;
216                                         break;
217                                 }
218                         }
219                         /* not found ? */
220                         if (!ext_key_map[i])
221                                 return 0;
222                 }
223
224                 input_add_keycode(&config, scan_code, release);
225                 return 1;
226         }
227 }
228
229 /* i8042_kbd_init - reset keyboard and init state flags */
230 int i8042_kbd_init(void)
231 {
232         int keymap, try;
233         char *penv;
234         int ret;
235
236         if (!kbd_controller_present() || board_i8042_skip()) {
237                 debug("i8042 keyboard controller is not present\n");
238                 return -1;
239         }
240
241         /* Init keyboard device (default US layout) */
242         keymap = KBD_US;
243         penv = getenv("keymap");
244         if (penv != NULL) {
245                 if (strncmp(penv, "de", 3) == 0)
246                         keymap = KBD_GER;
247         }
248
249         for (try = 0; kbd_reset() != 0; try++) {
250                 if (try >= KBD_RESET_TRIES)
251                         return -1;
252         }
253
254         ret = input_init(&config, keymap == KBD_GER);
255         if (ret)
256                 return ret;
257         config.read_keys = i8042_kbd_check;
258         input_allow_repeats(&config, true);
259
260         kbd_led_set(NORMAL);
261
262         return 0;
263 }
264
265 /**
266  * check_leds() - Check the keyboard LEDs and update them it needed
267  *
268  * @ret:        Value to return
269  * @return value of @ret
270  */
271 static int check_leds(int ret)
272 {
273         int leds;
274
275         leds = input_leds_changed(&config);
276         if (leds >= 0)
277                 kbd_led_set(leds);
278
279         return ret;
280 }
281
282 /* i8042_tstc - test if keyboard input is available */
283 int i8042_tstc(struct stdio_dev *dev)
284 {
285         return check_leds(input_tstc(&config));
286 }
287
288 /* i8042_getc - wait till keyboard input is available */
289 int i8042_getc(struct stdio_dev *dev)
290 {
291         return check_leds(input_getc(&config));
292 }