Commit 1d4e547b552213f91f81aadb3db5efc9ba800ef2
1 parent
64a88d5d
Add the LM8323-based keyboard of N810.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@4400 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
4 changed files
with
658 additions
and
9 deletions
Makefile
... | ... | @@ -52,7 +52,7 @@ OBJS+=block.o |
52 | 52 | OBJS+=irq.o |
53 | 53 | OBJS+=i2c.o smbus.o smbus_eeprom.o max7310.o max111x.o wm8750.o |
54 | 54 | OBJS+=ssd0303.o ssd0323.o ads7846.o stellaris_input.o twl92230.o |
55 | -OBJS+=tmp105.o | |
55 | +OBJS+=tmp105.o lm832x.o | |
56 | 56 | OBJS+=scsi-disk.o cdrom.o |
57 | 57 | OBJS+=scsi-generic.o |
58 | 58 | OBJS+=usb.o usb-hub.o usb-linux.o usb-hid.o usb-msd.o usb-wacom.o usb-serial.o | ... | ... |
hw/i2c.h
... | ... | @@ -84,4 +84,8 @@ struct i2c_slave *tmp105_init(i2c_bus *bus, qemu_irq alarm); |
84 | 84 | void tmp105_reset(i2c_slave *i2c); |
85 | 85 | void tmp105_set(i2c_slave *i2c, int temp); |
86 | 86 | |
87 | +/* lm832x.c */ | |
88 | +struct i2c_slave *lm8323_init(i2c_bus *bus, qemu_irq nirq); | |
89 | +void lm832x_key_event(struct i2c_slave *i2c, int key, int state); | |
90 | + | |
87 | 91 | #endif | ... | ... |
hw/lm832x.c
0 → 100644
1 | +/* | |
2 | + * National Semiconductor LM8322/8323 GPIO keyboard & PWM chips. | |
3 | + * | |
4 | + * Copyright (C) 2008 Nokia Corporation | |
5 | + * Written by Andrzej Zaborowski <andrew@openedhand.com> | |
6 | + * | |
7 | + * This program is free software; you can redistribute it and/or | |
8 | + * modify it under the terms of the GNU General Public License as | |
9 | + * published by the Free Software Foundation; either version 2 or | |
10 | + * (at your option) version 3 of the License. | |
11 | + * | |
12 | + * This program is distributed in the hope that it will be useful, | |
13 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | + * GNU General Public License for more details. | |
16 | + * | |
17 | + * You should have received a copy of the GNU General Public License | |
18 | + * along with this program; if not, write to the Free Software | |
19 | + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | |
20 | + * MA 02111-1307 USA | |
21 | + */ | |
22 | + | |
23 | +#include "hw.h" | |
24 | +#include "i2c.h" | |
25 | +#include "qemu-timer.h" | |
26 | +#include "console.h" | |
27 | + | |
28 | +struct lm_kbd_s { | |
29 | + i2c_slave i2c; | |
30 | + int i2c_dir; | |
31 | + int i2c_cycle; | |
32 | + int reg; | |
33 | + | |
34 | + qemu_irq nirq; | |
35 | + uint16_t model; | |
36 | + | |
37 | + struct { | |
38 | + qemu_irq out[2]; | |
39 | + int in[2][2]; | |
40 | + } mux; | |
41 | + | |
42 | + uint8_t config; | |
43 | + uint8_t status; | |
44 | + uint8_t acttime; | |
45 | + uint8_t error; | |
46 | + uint8_t clock; | |
47 | + | |
48 | + struct { | |
49 | + uint16_t pull; | |
50 | + uint16_t mask; | |
51 | + uint16_t dir; | |
52 | + uint16_t level; | |
53 | + qemu_irq out[16]; | |
54 | + } gpio; | |
55 | + | |
56 | + struct { | |
57 | + uint8_t dbnctime; | |
58 | + uint8_t size; | |
59 | + int start; | |
60 | + int len; | |
61 | + uint8_t fifo[16]; | |
62 | + } kbd; | |
63 | + | |
64 | + struct { | |
65 | + uint16_t file[256]; | |
66 | + uint8_t faddr; | |
67 | + uint8_t addr[3]; | |
68 | + QEMUTimer *tm[3]; | |
69 | + } pwm; | |
70 | +}; | |
71 | + | |
72 | +#define INT_KEYPAD (1 << 0) | |
73 | +#define INT_ERROR (1 << 3) | |
74 | +#define INT_NOINIT (1 << 4) | |
75 | +#define INT_PWMEND(n) (1 << (5 + n)) | |
76 | + | |
77 | +#define ERR_BADPAR (1 << 0) | |
78 | +#define ERR_CMDUNK (1 << 1) | |
79 | +#define ERR_KEYOVR (1 << 2) | |
80 | +#define ERR_FIFOOVR (1 << 6) | |
81 | + | |
82 | +static void lm_kbd_irq_update(struct lm_kbd_s *s) | |
83 | +{ | |
84 | + qemu_set_irq(s->nirq, !s->status); | |
85 | +} | |
86 | + | |
87 | +static void lm_kbd_gpio_update(struct lm_kbd_s *s) | |
88 | +{ | |
89 | +} | |
90 | + | |
91 | +static void lm_kbd_reset(struct lm_kbd_s *s) | |
92 | +{ | |
93 | + s->config = 0x80; | |
94 | + s->status = INT_NOINIT; | |
95 | + s->acttime = 125; | |
96 | + s->kbd.dbnctime = 3; | |
97 | + s->kbd.size = 0x33; | |
98 | + s->clock = 0x08; | |
99 | + | |
100 | + lm_kbd_irq_update(s); | |
101 | + lm_kbd_gpio_update(s); | |
102 | +} | |
103 | + | |
104 | +static void lm_kbd_error(struct lm_kbd_s *s, int err) | |
105 | +{ | |
106 | + s->error |= err; | |
107 | + s->status |= INT_ERROR; | |
108 | + lm_kbd_irq_update(s); | |
109 | +} | |
110 | + | |
111 | +static void lm_kbd_pwm_tick(struct lm_kbd_s *s, int line) | |
112 | +{ | |
113 | +} | |
114 | + | |
115 | +static void lm_kbd_pwm_start(struct lm_kbd_s *s, int line) | |
116 | +{ | |
117 | + lm_kbd_pwm_tick(s, line); | |
118 | +} | |
119 | + | |
120 | +static void lm_kbd_pwm0_tick(void *opaque) | |
121 | +{ | |
122 | + lm_kbd_pwm_tick(opaque, 0); | |
123 | +} | |
124 | +static void lm_kbd_pwm1_tick(void *opaque) | |
125 | +{ | |
126 | + lm_kbd_pwm_tick(opaque, 1); | |
127 | +} | |
128 | +static void lm_kbd_pwm2_tick(void *opaque) | |
129 | +{ | |
130 | + lm_kbd_pwm_tick(opaque, 2); | |
131 | +} | |
132 | + | |
133 | +enum { | |
134 | + LM832x_CMD_READ_ID = 0x80, /* Read chip ID. */ | |
135 | + LM832x_CMD_WRITE_CFG = 0x81, /* Set configuration item. */ | |
136 | + LM832x_CMD_READ_INT = 0x82, /* Get interrupt status. */ | |
137 | + LM832x_CMD_RESET = 0x83, /* Reset, same as external one */ | |
138 | + LM823x_CMD_WRITE_PULL_DOWN = 0x84, /* Select GPIO pull-up/down. */ | |
139 | + LM832x_CMD_WRITE_PORT_SEL = 0x85, /* Select GPIO in/out. */ | |
140 | + LM832x_CMD_WRITE_PORT_STATE = 0x86, /* Set GPIO pull-up/down. */ | |
141 | + LM832x_CMD_READ_PORT_SEL = 0x87, /* Get GPIO in/out. */ | |
142 | + LM832x_CMD_READ_PORT_STATE = 0x88, /* Get GPIO pull-up/down. */ | |
143 | + LM832x_CMD_READ_FIFO = 0x89, /* Read byte from FIFO. */ | |
144 | + LM832x_CMD_RPT_READ_FIFO = 0x8a, /* Read FIFO (no increment). */ | |
145 | + LM832x_CMD_SET_ACTIVE = 0x8b, /* Set active time. */ | |
146 | + LM832x_CMD_READ_ERROR = 0x8c, /* Get error status. */ | |
147 | + LM832x_CMD_READ_ROTATOR = 0x8e, /* Read rotator status. */ | |
148 | + LM832x_CMD_SET_DEBOUNCE = 0x8f, /* Set debouncing time. */ | |
149 | + LM832x_CMD_SET_KEY_SIZE = 0x90, /* Set keypad size. */ | |
150 | + LM832x_CMD_READ_KEY_SIZE = 0x91, /* Get keypad size. */ | |
151 | + LM832x_CMD_READ_CFG = 0x92, /* Get configuration item. */ | |
152 | + LM832x_CMD_WRITE_CLOCK = 0x93, /* Set clock config. */ | |
153 | + LM832x_CMD_READ_CLOCK = 0x94, /* Get clock config. */ | |
154 | + LM832x_CMD_PWM_WRITE = 0x95, /* Write PWM script. */ | |
155 | + LM832x_CMD_PWM_START = 0x96, /* Start PWM engine. */ | |
156 | + LM832x_CMD_PWM_STOP = 0x97, /* Stop PWM engine. */ | |
157 | +}; | |
158 | + | |
159 | +#define LM832x_MAX_KPX 8 | |
160 | +#define LM832x_MAX_KPY 12 | |
161 | + | |
162 | +static uint8_t lm_kbd_read(struct lm_kbd_s *s, int reg, int byte) | |
163 | +{ | |
164 | + int ret; | |
165 | + | |
166 | + switch (reg) { | |
167 | + case LM832x_CMD_READ_ID: | |
168 | + ret = 0x0400; | |
169 | + break; | |
170 | + | |
171 | + case LM832x_CMD_READ_INT: | |
172 | + ret = s->status; | |
173 | + if (!(s->status & INT_NOINIT)) { | |
174 | + s->status = 0; | |
175 | + lm_kbd_irq_update(s); | |
176 | + } | |
177 | + break; | |
178 | + | |
179 | + case LM832x_CMD_READ_PORT_SEL: | |
180 | + ret = s->gpio.dir; | |
181 | + break; | |
182 | + case LM832x_CMD_READ_PORT_STATE: | |
183 | + ret = s->gpio.mask; | |
184 | + break; | |
185 | + | |
186 | + case LM832x_CMD_READ_FIFO: | |
187 | + if (s->kbd.len <= 1) | |
188 | + return 0x00; | |
189 | + | |
190 | + /* Example response from the two commands after a INT_KEYPAD | |
191 | + * interrupt caused by the key 0x3c being pressed: | |
192 | + * RPT_READ_FIFO: 55 bc 00 4e ff 0a 50 08 00 29 d9 08 01 c9 01 | |
193 | + * READ_FIFO: bc 00 00 4e ff 0a 50 08 00 29 d9 08 01 c9 01 | |
194 | + * RPT_READ_FIFO: bc 00 00 4e ff 0a 50 08 00 29 d9 08 01 c9 01 | |
195 | + * | |
196 | + * 55 is the code of the key release event serviced in the previous | |
197 | + * interrupt handling. | |
198 | + * | |
199 | + * TODO: find out whether the FIFO is advanced a single character | |
200 | + * before reading every byte or the whole size of the FIFO at the | |
201 | + * last LM832x_CMD_READ_FIFO. This affects LM832x_CMD_RPT_READ_FIFO | |
202 | + * output in cases where there are more than one event in the FIFO. | |
203 | + * Assume 0xbc and 0x3c events are in the FIFO: | |
204 | + * RPT_READ_FIFO: 55 bc 3c 00 4e ff 0a 50 08 00 29 d9 08 01 c9 | |
205 | + * READ_FIFO: bc 3c 00 00 4e ff 0a 50 08 00 29 d9 08 01 c9 | |
206 | + * Does RPT_READ_FIFO now return 0xbc and 0x3c or only 0x3c? | |
207 | + */ | |
208 | + s->kbd.start ++; | |
209 | + s->kbd.start &= sizeof(s->kbd.fifo) - 1; | |
210 | + s->kbd.len --; | |
211 | + | |
212 | + return s->kbd.fifo[s->kbd.start]; | |
213 | + case LM832x_CMD_RPT_READ_FIFO: | |
214 | + if (byte >= s->kbd.len) | |
215 | + return 0x00; | |
216 | + | |
217 | + return s->kbd.fifo[(s->kbd.start + byte) & (sizeof(s->kbd.fifo) - 1)]; | |
218 | + | |
219 | + case LM832x_CMD_READ_ERROR: | |
220 | + return s->error; | |
221 | + | |
222 | + case LM832x_CMD_READ_ROTATOR: | |
223 | + return 0; | |
224 | + | |
225 | + case LM832x_CMD_READ_KEY_SIZE: | |
226 | + return s->kbd.size; | |
227 | + | |
228 | + case LM832x_CMD_READ_CFG: | |
229 | + return s->config & 0xf; | |
230 | + | |
231 | + case LM832x_CMD_READ_CLOCK: | |
232 | + return (s->clock & 0xfc) | 2; | |
233 | + | |
234 | + default: | |
235 | + lm_kbd_error(s, ERR_CMDUNK); | |
236 | + fprintf(stderr, "%s: unknown command %02x\n", __FUNCTION__, reg); | |
237 | + return 0x00; | |
238 | + } | |
239 | + | |
240 | + return ret >> (byte << 3); | |
241 | +} | |
242 | + | |
243 | +static void lm_kbd_write(struct lm_kbd_s *s, int reg, int byte, uint8_t value) | |
244 | +{ | |
245 | + switch (reg) { | |
246 | + case LM832x_CMD_WRITE_CFG: | |
247 | + s->config = value; | |
248 | + /* This must be done whenever s->mux.in is updated (never). */ | |
249 | + if ((s->config >> 1) & 1) /* MUX1EN */ | |
250 | + qemu_set_irq(s->mux.out[0], s->mux.in[0][(s->config >> 0) & 1]); | |
251 | + if ((s->config >> 3) & 1) /* MUX2EN */ | |
252 | + qemu_set_irq(s->mux.out[0], s->mux.in[0][(s->config >> 2) & 1]); | |
253 | + /* TODO: check that this is issued only following the chip reset | |
254 | + * and not in the middle of operation and that it is followed by | |
255 | + * the GPIO ports re-resablishing through WRITE_PORT_SEL and | |
256 | + * WRITE_PORT_STATE (using a timer perhaps) and otherwise output | |
257 | + * warnings. */ | |
258 | + s->status = 0; | |
259 | + lm_kbd_irq_update(s); | |
260 | + s->kbd.len = 0; | |
261 | + s->kbd.start = 0; | |
262 | + s->reg = -1; | |
263 | + break; | |
264 | + | |
265 | + case LM832x_CMD_RESET: | |
266 | + if (value == 0xaa) | |
267 | + lm_kbd_reset(s); | |
268 | + else | |
269 | + lm_kbd_error(s, ERR_BADPAR); | |
270 | + s->reg = -1; | |
271 | + break; | |
272 | + | |
273 | + case LM823x_CMD_WRITE_PULL_DOWN: | |
274 | + if (!byte) | |
275 | + s->gpio.pull = value; | |
276 | + else { | |
277 | + s->gpio.pull |= value << 8; | |
278 | + lm_kbd_gpio_update(s); | |
279 | + s->reg = -1; | |
280 | + } | |
281 | + break; | |
282 | + case LM832x_CMD_WRITE_PORT_SEL: | |
283 | + if (!byte) | |
284 | + s->gpio.dir = value; | |
285 | + else { | |
286 | + s->gpio.dir |= value << 8; | |
287 | + lm_kbd_gpio_update(s); | |
288 | + s->reg = -1; | |
289 | + } | |
290 | + break; | |
291 | + case LM832x_CMD_WRITE_PORT_STATE: | |
292 | + if (!byte) | |
293 | + s->gpio.mask = value; | |
294 | + else { | |
295 | + s->gpio.mask |= value << 8; | |
296 | + lm_kbd_gpio_update(s); | |
297 | + s->reg = -1; | |
298 | + } | |
299 | + break; | |
300 | + | |
301 | + case LM832x_CMD_SET_ACTIVE: | |
302 | + s->acttime = value; | |
303 | + s->reg = -1; | |
304 | + break; | |
305 | + | |
306 | + case LM832x_CMD_SET_DEBOUNCE: | |
307 | + s->kbd.dbnctime = value; | |
308 | + s->reg = -1; | |
309 | + if (!value) | |
310 | + lm_kbd_error(s, ERR_BADPAR); | |
311 | + break; | |
312 | + | |
313 | + case LM832x_CMD_SET_KEY_SIZE: | |
314 | + s->kbd.size = value; | |
315 | + s->reg = -1; | |
316 | + if ( | |
317 | + (value & 0xf) < 3 || (value & 0xf) > LM832x_MAX_KPY || | |
318 | + (value >> 4) < 3 || (value >> 4) > LM832x_MAX_KPX) | |
319 | + lm_kbd_error(s, ERR_BADPAR); | |
320 | + break; | |
321 | + | |
322 | + case LM832x_CMD_WRITE_CLOCK: | |
323 | + s->clock = value; | |
324 | + s->reg = -1; | |
325 | + if ((value & 3) && (value & 3) != 3) { | |
326 | + lm_kbd_error(s, ERR_BADPAR); | |
327 | + fprintf(stderr, "%s: invalid clock setting in RCPWM\n", | |
328 | + __FUNCTION__); | |
329 | + } | |
330 | + /* TODO: Validate that the command is only issued once */ | |
331 | + break; | |
332 | + | |
333 | + case LM832x_CMD_PWM_WRITE: | |
334 | + if (byte == 0) { | |
335 | + if (!(value & 3) || (value >> 2) > 59) { | |
336 | + lm_kbd_error(s, ERR_BADPAR); | |
337 | + s->reg = -1; | |
338 | + break; | |
339 | + } | |
340 | + | |
341 | + s->pwm.faddr = value; | |
342 | + s->pwm.file[s->pwm.faddr] = 0; | |
343 | + } else if (byte == 1) { | |
344 | + s->pwm.file[s->pwm.faddr] |= value << 8; | |
345 | + } else if (byte == 2) { | |
346 | + s->pwm.file[s->pwm.faddr] |= value << 0; | |
347 | + s->reg = -1; | |
348 | + } | |
349 | + break; | |
350 | + case LM832x_CMD_PWM_START: | |
351 | + s->reg = -1; | |
352 | + if (!(value & 3) || (value >> 2) > 59) { | |
353 | + lm_kbd_error(s, ERR_BADPAR); | |
354 | + break; | |
355 | + } | |
356 | + | |
357 | + s->pwm.addr[(value & 3) - 1] = value >> 2; | |
358 | + lm_kbd_pwm_start(s, (value & 3) - 1); | |
359 | + break; | |
360 | + case LM832x_CMD_PWM_STOP: | |
361 | + s->reg = -1; | |
362 | + if (!(value & 3)) { | |
363 | + lm_kbd_error(s, ERR_BADPAR); | |
364 | + break; | |
365 | + } | |
366 | + | |
367 | + qemu_del_timer(s->pwm.tm[(value & 3) - 1]); | |
368 | + break; | |
369 | + | |
370 | + case -1: | |
371 | + lm_kbd_error(s, ERR_BADPAR); | |
372 | + break; | |
373 | + default: | |
374 | + lm_kbd_error(s, ERR_CMDUNK); | |
375 | + fprintf(stderr, "%s: unknown command %02x\n", __FUNCTION__, reg); | |
376 | + break; | |
377 | + } | |
378 | +} | |
379 | + | |
380 | +static void lm_i2c_event(i2c_slave *i2c, enum i2c_event event) | |
381 | +{ | |
382 | + struct lm_kbd_s *s = (struct lm_kbd_s *) i2c; | |
383 | + | |
384 | + switch (event) { | |
385 | + case I2C_START_RECV: | |
386 | + case I2C_START_SEND: | |
387 | + s->i2c_cycle = 0; | |
388 | + s->i2c_dir = (event == I2C_START_SEND); | |
389 | + break; | |
390 | + | |
391 | + default: | |
392 | + break; | |
393 | + } | |
394 | +} | |
395 | + | |
396 | +static int lm_i2c_rx(i2c_slave *i2c) | |
397 | +{ | |
398 | + struct lm_kbd_s *s = (struct lm_kbd_s *) i2c; | |
399 | + | |
400 | + return lm_kbd_read(s, s->reg, s->i2c_cycle ++); | |
401 | +} | |
402 | + | |
403 | +static int lm_i2c_tx(i2c_slave *i2c, uint8_t data) | |
404 | +{ | |
405 | + struct lm_kbd_s *s = (struct lm_kbd_s *) i2c; | |
406 | + | |
407 | + if (!s->i2c_cycle) | |
408 | + s->reg = data; | |
409 | + else | |
410 | + lm_kbd_write(s, s->reg, s->i2c_cycle - 1, data); | |
411 | + s->i2c_cycle ++; | |
412 | + | |
413 | + return 0; | |
414 | +} | |
415 | + | |
416 | +static void lm_kbd_save(QEMUFile *f, void *opaque) | |
417 | +{ | |
418 | + struct lm_kbd_s *s = (struct lm_kbd_s *) opaque; | |
419 | + int i; | |
420 | + | |
421 | + i2c_slave_save(f, &s->i2c); | |
422 | + qemu_put_byte(f, s->i2c_dir); | |
423 | + qemu_put_byte(f, s->i2c_cycle); | |
424 | + qemu_put_byte(f, (uint8_t) s->reg); | |
425 | + | |
426 | + qemu_put_8s(f, &s->config); | |
427 | + qemu_put_8s(f, &s->status); | |
428 | + qemu_put_8s(f, &s->acttime); | |
429 | + qemu_put_8s(f, &s->error); | |
430 | + qemu_put_8s(f, &s->clock); | |
431 | + | |
432 | + qemu_put_be16s(f, &s->gpio.pull); | |
433 | + qemu_put_be16s(f, &s->gpio.mask); | |
434 | + qemu_put_be16s(f, &s->gpio.dir); | |
435 | + qemu_put_be16s(f, &s->gpio.level); | |
436 | + | |
437 | + qemu_put_byte(f, s->kbd.dbnctime); | |
438 | + qemu_put_byte(f, s->kbd.size); | |
439 | + qemu_put_byte(f, s->kbd.start); | |
440 | + qemu_put_byte(f, s->kbd.len); | |
441 | + qemu_put_buffer(f, s->kbd.fifo, sizeof(s->kbd.fifo)); | |
442 | + | |
443 | + for (i = 0; i < sizeof(s->pwm.file); i ++) | |
444 | + qemu_put_be16s(f, &s->pwm.file[i]); | |
445 | + qemu_put_8s(f, &s->pwm.faddr); | |
446 | + qemu_put_buffer(f, s->pwm.addr, sizeof(s->pwm.addr)); | |
447 | + qemu_put_timer(f, s->pwm.tm[0]); | |
448 | + qemu_put_timer(f, s->pwm.tm[1]); | |
449 | + qemu_put_timer(f, s->pwm.tm[2]); | |
450 | +} | |
451 | + | |
452 | +static int lm_kbd_load(QEMUFile *f, void *opaque, int version_id) | |
453 | +{ | |
454 | + struct lm_kbd_s *s = (struct lm_kbd_s *) opaque; | |
455 | + int i; | |
456 | + | |
457 | + i2c_slave_load(f, &s->i2c); | |
458 | + s->i2c_dir = qemu_get_byte(f); | |
459 | + s->i2c_cycle = qemu_get_byte(f); | |
460 | + s->reg = (int8_t) qemu_get_byte(f); | |
461 | + | |
462 | + qemu_get_8s(f, &s->config); | |
463 | + qemu_get_8s(f, &s->status); | |
464 | + qemu_get_8s(f, &s->acttime); | |
465 | + qemu_get_8s(f, &s->error); | |
466 | + qemu_get_8s(f, &s->clock); | |
467 | + | |
468 | + qemu_get_be16s(f, &s->gpio.pull); | |
469 | + qemu_get_be16s(f, &s->gpio.mask); | |
470 | + qemu_get_be16s(f, &s->gpio.dir); | |
471 | + qemu_get_be16s(f, &s->gpio.level); | |
472 | + | |
473 | + s->kbd.dbnctime = qemu_get_byte(f); | |
474 | + s->kbd.size = qemu_get_byte(f); | |
475 | + s->kbd.start = qemu_get_byte(f); | |
476 | + s->kbd.len = qemu_get_byte(f); | |
477 | + qemu_get_buffer(f, s->kbd.fifo, sizeof(s->kbd.fifo)); | |
478 | + | |
479 | + for (i = 0; i < sizeof(s->pwm.file); i ++) | |
480 | + qemu_get_be16s(f, &s->pwm.file[i]); | |
481 | + qemu_get_8s(f, &s->pwm.faddr); | |
482 | + qemu_get_buffer(f, s->pwm.addr, sizeof(s->pwm.addr)); | |
483 | + qemu_get_timer(f, s->pwm.tm[0]); | |
484 | + qemu_get_timer(f, s->pwm.tm[1]); | |
485 | + qemu_get_timer(f, s->pwm.tm[2]); | |
486 | + | |
487 | + lm_kbd_irq_update(s); | |
488 | + lm_kbd_gpio_update(s); | |
489 | + | |
490 | + return 0; | |
491 | +} | |
492 | + | |
493 | +static int lm_kbd_iid = 0; | |
494 | + | |
495 | +struct i2c_slave *lm8323_init(i2c_bus *bus, qemu_irq nirq) | |
496 | +{ | |
497 | + struct lm_kbd_s *s; | |
498 | + | |
499 | + s = (struct lm_kbd_s *) i2c_slave_init(bus, 0, sizeof(struct lm_kbd_s)); | |
500 | + s->model = 0x8323; | |
501 | + s->pwm.tm[0] = qemu_new_timer(vm_clock, lm_kbd_pwm0_tick, s); | |
502 | + s->pwm.tm[1] = qemu_new_timer(vm_clock, lm_kbd_pwm1_tick, s); | |
503 | + s->pwm.tm[2] = qemu_new_timer(vm_clock, lm_kbd_pwm2_tick, s); | |
504 | + s->nirq = nirq; | |
505 | + | |
506 | + s->i2c.event = lm_i2c_event; | |
507 | + s->i2c.recv = lm_i2c_rx; | |
508 | + s->i2c.send = lm_i2c_tx; | |
509 | + | |
510 | + lm_kbd_reset(s); | |
511 | + | |
512 | + qemu_register_reset((void *) lm_kbd_reset, s); | |
513 | + register_savevm("LM8323", lm_kbd_iid ++, 0, | |
514 | + lm_kbd_save, lm_kbd_load, s); | |
515 | + | |
516 | + return &s->i2c; | |
517 | +} | |
518 | + | |
519 | +void lm832x_key_event(struct i2c_slave *i2c, int key, int state) | |
520 | +{ | |
521 | + struct lm_kbd_s *s = (struct lm_kbd_s *) i2c; | |
522 | + | |
523 | + if ((s->status & INT_ERROR) && (s->error & ERR_FIFOOVR)) | |
524 | + return; | |
525 | + | |
526 | + if (s->kbd.len >= sizeof(s->kbd.fifo)) | |
527 | + return lm_kbd_error(s, ERR_FIFOOVR); | |
528 | + | |
529 | + s->kbd.fifo[(s->kbd.start + s->kbd.len ++) & (sizeof(s->kbd.fifo) - 1)] = | |
530 | + key | (state << 7); | |
531 | + | |
532 | + /* We never set ERR_KEYOVR because we support multiple keys fine. */ | |
533 | + s->status |= INT_KEYPAD; | |
534 | + lm_kbd_irq_update(s); | |
535 | +} | ... | ... |
hw/nseries.c
... | ... | @@ -45,6 +45,7 @@ struct n800_s { |
45 | 45 | i2c_bus *i2c; |
46 | 46 | |
47 | 47 | int keymap[0x80]; |
48 | + i2c_slave *kbd; | |
48 | 49 | |
49 | 50 | struct tusb_s *usb; |
50 | 51 | void *retu; |
... | ... | @@ -92,16 +93,22 @@ struct n800_s { |
92 | 93 | #define N8X0_TAHVO_GPIO 111 |
93 | 94 | #define N800_UNKNOWN_GPIO4 112 /* out */ |
94 | 95 | #define N810_SLEEPX_LED_GPIO 112 |
95 | -#define N810_TSC_UNKNOWN_GPIO 118 /* out */ | |
96 | -#define N800_TSC_RESET_GPIO 119 /* ? */ | |
96 | +#define N800_TSC_RESET_GPIO 118 /* ? */ | |
97 | +#define N800_TSC_UNKNOWN_GPIO 119 /* out */ | |
97 | 98 | #define N8X0_TMP105_GPIO 125 |
98 | 99 | |
99 | 100 | /* Config */ |
100 | 101 | #define XLDR_LL_UART 1 |
101 | 102 | |
102 | -/* Addresses on the I2C bus */ | |
103 | -#define N8X0_TMP105_ADDR 0x48 | |
104 | -#define N8X0_MENELAUS_ADDR 0x72 | |
103 | +/* Addresses on the I2C bus 0 */ | |
104 | +#define N810_TLV320AIC33_ADDR 0x18 /* Audio CODEC */ | |
105 | +#define N8X0_TCM825x_ADDR 0x29 /* Camera */ | |
106 | +#define N810_LP5521_ADDR 0x32 /* LEDs */ | |
107 | +#define N810_TSL2563_ADDR 0x3d /* Light sensor */ | |
108 | +#define N810_LM8323_ADDR 0x45 /* Keyboard */ | |
109 | +/* Addresses on the I2C bus 1 */ | |
110 | +#define N8X0_TMP105_ADDR 0x48 /* Temperature sensor */ | |
111 | +#define N8X0_MENELAUS_ADDR 0x72 /* Power management */ | |
105 | 112 | |
106 | 113 | /* Chipselects on GPMC NOR interface */ |
107 | 114 | #define N8X0_ONENAND_CS 0 |
... | ... | @@ -190,12 +197,12 @@ static const int n800_keys[16] = { |
190 | 197 | 28, /* Enter */ |
191 | 198 | 77, /* Right */ |
192 | 199 | -1, |
193 | - 1, /* Cycle (ESC) */ | |
200 | + 1, /* Cycle (ESC) */ | |
194 | 201 | 80, /* Down */ |
195 | 202 | 62, /* Menu (F4) */ |
196 | 203 | -1, |
197 | 204 | 66, /* Zoom- (F8) */ |
198 | - 64, /* FS (F6) */ | |
205 | + 64, /* FullScreen (F6) */ | |
199 | 206 | 65, /* Zoom+ (F7) */ |
200 | 207 | -1, |
201 | 208 | }; |
... | ... | @@ -235,6 +242,107 @@ static void n810_tsc_setup(struct n800_s *s) |
235 | 242 | tsc2005_set_transform(s->ts.opaque, &n810_pointercal); |
236 | 243 | } |
237 | 244 | |
245 | +/* N810 Keyboard controller */ | |
246 | +static void n810_key_event(void *opaque, int keycode) | |
247 | +{ | |
248 | + struct n800_s *s = (struct n800_s *) opaque; | |
249 | + int code = s->keymap[keycode & 0x7f]; | |
250 | + | |
251 | + if (code == -1) { | |
252 | + if ((keycode & 0x7f) == RETU_KEYCODE) | |
253 | + retu_key_event(s->retu, !(keycode & 0x80)); | |
254 | + return; | |
255 | + } | |
256 | + | |
257 | + lm832x_key_event(s->kbd, code, !(keycode & 0x80)); | |
258 | +} | |
259 | + | |
260 | +#define M 0 | |
261 | + | |
262 | +static int n810_keys[0x80] = { | |
263 | + [0x01] = 16, /* Q */ | |
264 | + [0x02] = 37, /* K */ | |
265 | + [0x03] = 24, /* O */ | |
266 | + [0x04] = 25, /* P */ | |
267 | + [0x05] = 14, /* Backspace */ | |
268 | + [0x06] = 30, /* A */ | |
269 | + [0x07] = 31, /* S */ | |
270 | + [0x08] = 32, /* D */ | |
271 | + [0x09] = 33, /* F */ | |
272 | + [0x0a] = 34, /* G */ | |
273 | + [0x0b] = 35, /* H */ | |
274 | + [0x0c] = 36, /* J */ | |
275 | + | |
276 | + [0x11] = 17, /* W */ | |
277 | + [0x12] = 62, /* Menu (F4) */ | |
278 | + [0x13] = 38, /* L */ | |
279 | + [0x14] = 40, /* ' (Apostrophe) */ | |
280 | + [0x16] = 44, /* Z */ | |
281 | + [0x17] = 45, /* X */ | |
282 | + [0x18] = 46, /* C */ | |
283 | + [0x19] = 47, /* V */ | |
284 | + [0x1a] = 48, /* B */ | |
285 | + [0x1b] = 49, /* N */ | |
286 | + [0x1c] = 42, /* Shift (Left shift) */ | |
287 | + [0x1f] = 65, /* Zoom+ (F7) */ | |
288 | + | |
289 | + [0x21] = 18, /* E */ | |
290 | + [0x22] = 39, /* ; (Semicolon) */ | |
291 | + [0x23] = 12, /* - (Minus) */ | |
292 | + [0x24] = 13, /* = (Equal) */ | |
293 | + [0x2b] = 56, /* Fn (Left Alt) */ | |
294 | + [0x2c] = 50, /* M */ | |
295 | + [0x2f] = 66, /* Zoom- (F8) */ | |
296 | + | |
297 | + [0x31] = 19, /* R */ | |
298 | + [0x32] = 29 | M, /* Right Ctrl */ | |
299 | + [0x34] = 57, /* Space */ | |
300 | + [0x35] = 51, /* , (Comma) */ | |
301 | + [0x37] = 72 | M, /* Up */ | |
302 | + [0x3c] = 82 | M, /* Compose (Insert) */ | |
303 | + [0x3f] = 64, /* FullScreen (F6) */ | |
304 | + | |
305 | + [0x41] = 20, /* T */ | |
306 | + [0x44] = 52, /* . (Dot) */ | |
307 | + [0x46] = 77 | M, /* Right */ | |
308 | + [0x4f] = 63, /* Home (F5) */ | |
309 | + [0x51] = 21, /* Y */ | |
310 | + [0x53] = 80 | M, /* Down */ | |
311 | + [0x55] = 28, /* Enter */ | |
312 | + [0x5f] = 1, /* Cycle (ESC) */ | |
313 | + | |
314 | + [0x61] = 22, /* U */ | |
315 | + [0x64] = 75 | M, /* Left */ | |
316 | + | |
317 | + [0x71] = 23, /* I */ | |
318 | +#if 0 | |
319 | + [0x75] = 28 | M, /* KP Enter (KP Enter) */ | |
320 | +#else | |
321 | + [0x75] = 15, /* KP Enter (Tab) */ | |
322 | +#endif | |
323 | +}; | |
324 | + | |
325 | +#undef M | |
326 | + | |
327 | +static void n810_kbd_setup(struct n800_s *s) | |
328 | +{ | |
329 | + qemu_irq kbd_irq = omap2_gpio_in_get(s->cpu->gpif, N810_KEYBOARD_GPIO)[0]; | |
330 | + int i; | |
331 | + | |
332 | + for (i = 0; i < 0x80; i ++) | |
333 | + s->keymap[i] = -1; | |
334 | + for (i = 0; i < 0x80; i ++) | |
335 | + if (n810_keys[i] > 0) | |
336 | + s->keymap[n810_keys[i]] = i; | |
337 | + | |
338 | + qemu_add_kbd_event_handler(n810_key_event, s); | |
339 | + | |
340 | + /* Attach the LM8322 keyboard to the I2C bus, | |
341 | + * should happen in n8x0_i2c_setup and s->kbd be initialised here. */ | |
342 | + s->kbd = lm8323_init(s->i2c, kbd_irq); | |
343 | + i2c_set_slave_address(s->kbd, N810_LM8323_ADDR); | |
344 | +} | |
345 | + | |
238 | 346 | /* LCD MIPI DBI-C controller (URAL) */ |
239 | 347 | struct mipid_s { |
240 | 348 | int resp[4]; |
... | ... | @@ -954,8 +1062,10 @@ static void n8x0_init(ram_addr_t ram_size, const char *boot_device, |
954 | 1062 | n8x0_i2c_setup(s); |
955 | 1063 | if (model == 800) |
956 | 1064 | n800_tsc_kbd_setup(s); |
957 | - else if (model == 810) | |
1065 | + else if (model == 810) { | |
958 | 1066 | n810_tsc_setup(s); |
1067 | + n810_kbd_setup(s); | |
1068 | + } | |
959 | 1069 | n8x0_spi_setup(s); |
960 | 1070 | n8x0_dss_setup(s, ds); |
961 | 1071 | n8x0_cbus_setup(s); | ... | ... |