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,7 +52,7 @@ OBJS+=block.o | ||
| 52 | OBJS+=irq.o | 52 | OBJS+=irq.o |
| 53 | OBJS+=i2c.o smbus.o smbus_eeprom.o max7310.o max111x.o wm8750.o | 53 | OBJS+=i2c.o smbus.o smbus_eeprom.o max7310.o max111x.o wm8750.o |
| 54 | OBJS+=ssd0303.o ssd0323.o ads7846.o stellaris_input.o twl92230.o | 54 | OBJS+=ssd0303.o ssd0323.o ads7846.o stellaris_input.o twl92230.o |
| 55 | -OBJS+=tmp105.o | 55 | +OBJS+=tmp105.o lm832x.o |
| 56 | OBJS+=scsi-disk.o cdrom.o | 56 | OBJS+=scsi-disk.o cdrom.o |
| 57 | OBJS+=scsi-generic.o | 57 | OBJS+=scsi-generic.o |
| 58 | OBJS+=usb.o usb-hub.o usb-linux.o usb-hid.o usb-msd.o usb-wacom.o usb-serial.o | 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,4 +84,8 @@ struct i2c_slave *tmp105_init(i2c_bus *bus, qemu_irq alarm); | ||
| 84 | void tmp105_reset(i2c_slave *i2c); | 84 | void tmp105_reset(i2c_slave *i2c); |
| 85 | void tmp105_set(i2c_slave *i2c, int temp); | 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 | #endif | 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,6 +45,7 @@ struct n800_s { | ||
| 45 | i2c_bus *i2c; | 45 | i2c_bus *i2c; |
| 46 | 46 | ||
| 47 | int keymap[0x80]; | 47 | int keymap[0x80]; |
| 48 | + i2c_slave *kbd; | ||
| 48 | 49 | ||
| 49 | struct tusb_s *usb; | 50 | struct tusb_s *usb; |
| 50 | void *retu; | 51 | void *retu; |
| @@ -92,16 +93,22 @@ struct n800_s { | @@ -92,16 +93,22 @@ struct n800_s { | ||
| 92 | #define N8X0_TAHVO_GPIO 111 | 93 | #define N8X0_TAHVO_GPIO 111 |
| 93 | #define N800_UNKNOWN_GPIO4 112 /* out */ | 94 | #define N800_UNKNOWN_GPIO4 112 /* out */ |
| 94 | #define N810_SLEEPX_LED_GPIO 112 | 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 | #define N8X0_TMP105_GPIO 125 | 98 | #define N8X0_TMP105_GPIO 125 |
| 98 | 99 | ||
| 99 | /* Config */ | 100 | /* Config */ |
| 100 | #define XLDR_LL_UART 1 | 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 | /* Chipselects on GPMC NOR interface */ | 113 | /* Chipselects on GPMC NOR interface */ |
| 107 | #define N8X0_ONENAND_CS 0 | 114 | #define N8X0_ONENAND_CS 0 |
| @@ -190,12 +197,12 @@ static const int n800_keys[16] = { | @@ -190,12 +197,12 @@ static const int n800_keys[16] = { | ||
| 190 | 28, /* Enter */ | 197 | 28, /* Enter */ |
| 191 | 77, /* Right */ | 198 | 77, /* Right */ |
| 192 | -1, | 199 | -1, |
| 193 | - 1, /* Cycle (ESC) */ | 200 | + 1, /* Cycle (ESC) */ |
| 194 | 80, /* Down */ | 201 | 80, /* Down */ |
| 195 | 62, /* Menu (F4) */ | 202 | 62, /* Menu (F4) */ |
| 196 | -1, | 203 | -1, |
| 197 | 66, /* Zoom- (F8) */ | 204 | 66, /* Zoom- (F8) */ |
| 198 | - 64, /* FS (F6) */ | 205 | + 64, /* FullScreen (F6) */ |
| 199 | 65, /* Zoom+ (F7) */ | 206 | 65, /* Zoom+ (F7) */ |
| 200 | -1, | 207 | -1, |
| 201 | }; | 208 | }; |
| @@ -235,6 +242,107 @@ static void n810_tsc_setup(struct n800_s *s) | @@ -235,6 +242,107 @@ static void n810_tsc_setup(struct n800_s *s) | ||
| 235 | tsc2005_set_transform(s->ts.opaque, &n810_pointercal); | 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 | /* LCD MIPI DBI-C controller (URAL) */ | 346 | /* LCD MIPI DBI-C controller (URAL) */ |
| 239 | struct mipid_s { | 347 | struct mipid_s { |
| 240 | int resp[4]; | 348 | int resp[4]; |
| @@ -954,8 +1062,10 @@ static void n8x0_init(ram_addr_t ram_size, const char *boot_device, | @@ -954,8 +1062,10 @@ static void n8x0_init(ram_addr_t ram_size, const char *boot_device, | ||
| 954 | n8x0_i2c_setup(s); | 1062 | n8x0_i2c_setup(s); |
| 955 | if (model == 800) | 1063 | if (model == 800) |
| 956 | n800_tsc_kbd_setup(s); | 1064 | n800_tsc_kbd_setup(s); |
| 957 | - else if (model == 810) | 1065 | + else if (model == 810) { |
| 958 | n810_tsc_setup(s); | 1066 | n810_tsc_setup(s); |
| 1067 | + n810_kbd_setup(s); | ||
| 1068 | + } | ||
| 959 | n8x0_spi_setup(s); | 1069 | n8x0_spi_setup(s); |
| 960 | n8x0_dss_setup(s, ds); | 1070 | n8x0_dss_setup(s, ds); |
| 961 | n8x0_cbus_setup(s); | 1071 | n8x0_cbus_setup(s); |