| 1 | /* |
| 2 | * PXA270-based Zipit Z2 device |
| 3 | * |
| 4 | * Copyright (c) 2011 by Vasily Khoruzhick <anarsoul@gmail.com> |
| 5 | * |
| 6 | * Code is based on mainstone platform. |
| 7 | * |
| 8 | * This code is licensed under the GNU GPL v2. |
| 9 | */ |
| 10 | |
| 11 | #include "hw.h" |
| 12 | #include "pxa.h" |
| 13 | #include "arm-misc.h" |
| 14 | #include "devices.h" |
| 15 | #include "i2c.h" |
| 16 | #include "ssi.h" |
| 17 | #include "boards.h" |
| 18 | #include "sysemu.h" |
| 19 | #include "flash.h" |
| 20 | #include "blockdev.h" |
| 21 | #include "console.h" |
| 22 | #include "audio/audio.h" |
| 23 | |
| 24 | #ifdef DEBUG_Z2 |
| 25 | #define DPRINTF(fmt, ...) \ |
| 26 | printf(fmt, ## __VA_ARGS__) |
| 27 | #else |
| 28 | #define DPRINTF(fmt, ...) |
| 29 | #endif |
| 30 | |
| 31 | static struct keymap map[0x100] = { |
| 32 | [0 ... 0xff] = { -1, -1 }, |
| 33 | [0x3b] = {0, 0}, /* Option = F1 */ |
| 34 | [0xc8] = {0, 1}, /* Up */ |
| 35 | [0xd0] = {0, 2}, /* Down */ |
| 36 | [0xcb] = {0, 3}, /* Left */ |
| 37 | [0xcd] = {0, 4}, /* Right */ |
| 38 | [0xcf] = {0, 5}, /* End */ |
| 39 | [0x0d] = {0, 6}, /* KPPLUS */ |
| 40 | [0xc7] = {1, 0}, /* Home */ |
| 41 | [0x10] = {1, 1}, /* Q */ |
| 42 | [0x17] = {1, 2}, /* I */ |
| 43 | [0x22] = {1, 3}, /* G */ |
| 44 | [0x2d] = {1, 4}, /* X */ |
| 45 | [0x1c] = {1, 5}, /* Enter */ |
| 46 | [0x0c] = {1, 6}, /* KPMINUS */ |
| 47 | [0xc9] = {2, 0}, /* PageUp */ |
| 48 | [0x11] = {2, 1}, /* W */ |
| 49 | [0x18] = {2, 2}, /* O */ |
| 50 | [0x23] = {2, 3}, /* H */ |
| 51 | [0x2e] = {2, 4}, /* C */ |
| 52 | [0x38] = {2, 5}, /* LeftAlt */ |
| 53 | [0xd1] = {3, 0}, /* PageDown */ |
| 54 | [0x12] = {3, 1}, /* E */ |
| 55 | [0x19] = {3, 2}, /* P */ |
| 56 | [0x24] = {3, 3}, /* J */ |
| 57 | [0x2f] = {3, 4}, /* V */ |
| 58 | [0x2a] = {3, 5}, /* LeftShift */ |
| 59 | [0x01] = {4, 0}, /* Esc */ |
| 60 | [0x13] = {4, 1}, /* R */ |
| 61 | [0x1e] = {4, 2}, /* A */ |
| 62 | [0x25] = {4, 3}, /* K */ |
| 63 | [0x30] = {4, 4}, /* B */ |
| 64 | [0x1d] = {4, 5}, /* LeftCtrl */ |
| 65 | [0x0f] = {5, 0}, /* Tab */ |
| 66 | [0x14] = {5, 1}, /* T */ |
| 67 | [0x1f] = {5, 2}, /* S */ |
| 68 | [0x26] = {5, 3}, /* L */ |
| 69 | [0x31] = {5, 4}, /* N */ |
| 70 | [0x39] = {5, 5}, /* Space */ |
| 71 | [0x3c] = {6, 0}, /* Stop = F2 */ |
| 72 | [0x15] = {6, 1}, /* Y */ |
| 73 | [0x20] = {6, 2}, /* D */ |
| 74 | [0x0e] = {6, 3}, /* Backspace */ |
| 75 | [0x32] = {6, 4}, /* M */ |
| 76 | [0x33] = {6, 5}, /* Comma */ |
| 77 | [0x3d] = {7, 0}, /* Play = F3 */ |
| 78 | [0x16] = {7, 1}, /* U */ |
| 79 | [0x21] = {7, 2}, /* F */ |
| 80 | [0x2c] = {7, 3}, /* Z */ |
| 81 | [0x27] = {7, 4}, /* Semicolon */ |
| 82 | [0x34] = {7, 5}, /* Dot */ |
| 83 | }; |
| 84 | |
| 85 | #define Z2_RAM_SIZE 0x02000000 |
| 86 | #define Z2_FLASH_BASE 0x00000000 |
| 87 | #define Z2_FLASH_SIZE 0x00800000 |
| 88 | |
| 89 | static struct arm_boot_info z2_binfo = { |
| 90 | .loader_start = PXA2XX_SDRAM_BASE, |
| 91 | .ram_size = Z2_RAM_SIZE, |
| 92 | }; |
| 93 | |
| 94 | #define Z2_GPIO_SD_DETECT 96 |
| 95 | #define Z2_GPIO_AC_IN 0 |
| 96 | #define Z2_GPIO_KEY_ON 1 |
| 97 | #define Z2_GPIO_LCD_CS 88 |
| 98 | |
| 99 | typedef struct { |
| 100 | SSISlave ssidev; |
| 101 | int32_t selected; |
| 102 | int32_t enabled; |
| 103 | uint8_t buf[3]; |
| 104 | uint32_t cur_reg; |
| 105 | int pos; |
| 106 | } ZipitLCD; |
| 107 | |
| 108 | static uint32_t zipit_lcd_transfer(SSISlave *dev, uint32_t value) |
| 109 | { |
| 110 | ZipitLCD *z = FROM_SSI_SLAVE(ZipitLCD, dev); |
| 111 | uint16_t val; |
| 112 | if (z->selected) { |
| 113 | z->buf[z->pos] = value & 0xff; |
| 114 | z->pos++; |
| 115 | } |
| 116 | if (z->pos == 3) { |
| 117 | switch (z->buf[0]) { |
| 118 | case 0x74: |
| 119 | DPRINTF("%s: reg: 0x%.2x\n", __func__, z->buf[2]); |
| 120 | z->cur_reg = z->buf[2]; |
| 121 | break; |
| 122 | case 0x76: |
| 123 | val = z->buf[1] << 8 | z->buf[2]; |
| 124 | DPRINTF("%s: value: 0x%.4x\n", __func__, val); |
| 125 | if (z->cur_reg == 0x22 && val == 0x0000) { |
| 126 | z->enabled = 1; |
| 127 | printf("%s: LCD enabled\n", __func__); |
| 128 | } else if (z->cur_reg == 0x10 && val == 0x0000) { |
| 129 | z->enabled = 0; |
| 130 | printf("%s: LCD disabled\n", __func__); |
| 131 | } |
| 132 | break; |
| 133 | default: |
| 134 | DPRINTF("%s: unknown command!\n", __func__); |
| 135 | break; |
| 136 | } |
| 137 | z->pos = 0; |
| 138 | } |
| 139 | return 0; |
| 140 | } |
| 141 | |
| 142 | static void z2_lcd_cs(void *opaque, int line, int level) |
| 143 | { |
| 144 | ZipitLCD *z2_lcd = opaque; |
| 145 | z2_lcd->selected = !level; |
| 146 | } |
| 147 | |
| 148 | static int zipit_lcd_init(SSISlave *dev) |
| 149 | { |
| 150 | ZipitLCD *z = FROM_SSI_SLAVE(ZipitLCD, dev); |
| 151 | z->selected = 0; |
| 152 | z->enabled = 0; |
| 153 | z->pos = 0; |
| 154 | |
| 155 | return 0; |
| 156 | } |
| 157 | |
| 158 | static VMStateDescription vmstate_zipit_lcd_state = { |
| 159 | .name = "zipit-lcd", |
| 160 | .version_id = 1, |
| 161 | .minimum_version_id = 1, |
| 162 | .minimum_version_id_old = 1, |
| 163 | .fields = (VMStateField[]) { |
| 164 | VMSTATE_INT32(selected, ZipitLCD), |
| 165 | VMSTATE_INT32(enabled, ZipitLCD), |
| 166 | VMSTATE_BUFFER(buf, ZipitLCD), |
| 167 | VMSTATE_UINT32(cur_reg, ZipitLCD), |
| 168 | VMSTATE_INT32(pos, ZipitLCD), |
| 169 | VMSTATE_END_OF_LIST(), |
| 170 | } |
| 171 | }; |
| 172 | |
| 173 | static SSISlaveInfo zipit_lcd_info = { |
| 174 | .qdev.name = "zipit-lcd", |
| 175 | .qdev.size = sizeof(ZipitLCD), |
| 176 | .qdev.vmsd = &vmstate_zipit_lcd_state, |
| 177 | .init = zipit_lcd_init, |
| 178 | .transfer = zipit_lcd_transfer |
| 179 | }; |
| 180 | |
| 181 | typedef struct { |
| 182 | i2c_slave i2c; |
| 183 | int len; |
| 184 | uint8_t buf[3]; |
| 185 | } AER915State; |
| 186 | |
| 187 | static int aer915_send(i2c_slave *i2c, uint8_t data) |
| 188 | { |
| 189 | AER915State *s = FROM_I2C_SLAVE(AER915State, i2c); |
| 190 | s->buf[s->len] = data; |
| 191 | if (s->len++ > 2) { |
| 192 | DPRINTF("%s: message too long (%i bytes)\n", |
| 193 | __func__, s->len); |
| 194 | return 1; |
| 195 | } |
| 196 | |
| 197 | if (s->len == 2) { |
| 198 | DPRINTF("%s: reg %d value 0x%02x\n", __func__, |
| 199 | s->buf[0], s->buf[1]); |
| 200 | } |
| 201 | |
| 202 | return 0; |
| 203 | } |
| 204 | |
| 205 | static void aer915_event(i2c_slave *i2c, enum i2c_event event) |
| 206 | { |
| 207 | AER915State *s = FROM_I2C_SLAVE(AER915State, i2c); |
| 208 | switch (event) { |
| 209 | case I2C_START_SEND: |
| 210 | s->len = 0; |
| 211 | break; |
| 212 | case I2C_START_RECV: |
| 213 | if (s->len != 1) { |
| 214 | DPRINTF("%s: short message!?\n", __func__); |
| 215 | } |
| 216 | break; |
| 217 | case I2C_FINISH: |
| 218 | break; |
| 219 | default: |
| 220 | break; |
| 221 | } |
| 222 | } |
| 223 | |
| 224 | static int aer915_recv(i2c_slave *slave) |
| 225 | { |
| 226 | int retval = 0x00; |
| 227 | AER915State *s = FROM_I2C_SLAVE(AER915State, slave); |
| 228 | |
| 229 | switch (s->buf[0]) { |
| 230 | /* Return hardcoded battery voltage, |
| 231 | * 0xf0 means ~4.1V |
| 232 | */ |
| 233 | case 0x02: |
| 234 | retval = 0xf0; |
| 235 | break; |
| 236 | /* Return 0x00 for other regs, |
| 237 | * we don't know what they are for, |
| 238 | * anyway they return 0x00 on real hardware. |
| 239 | */ |
| 240 | default: |
| 241 | break; |
| 242 | } |
| 243 | |
| 244 | return retval; |
| 245 | } |
| 246 | |
| 247 | static int aer915_init(i2c_slave *i2c) |
| 248 | { |
| 249 | /* Nothing to do. */ |
| 250 | return 0; |
| 251 | } |
| 252 | |
| 253 | static VMStateDescription vmstate_aer915_state = { |
| 254 | .name = "aer915", |
| 255 | .version_id = 1, |
| 256 | .minimum_version_id = 1, |
| 257 | .minimum_version_id_old = 1, |
| 258 | .fields = (VMStateField[]) { |
| 259 | VMSTATE_INT32(len, AER915State), |
| 260 | VMSTATE_BUFFER(buf, AER915State), |
| 261 | VMSTATE_END_OF_LIST(), |
| 262 | } |
| 263 | }; |
| 264 | |
| 265 | static I2CSlaveInfo aer915_info = { |
| 266 | .qdev.name = "aer915", |
| 267 | .qdev.size = sizeof(AER915State), |
| 268 | .qdev.vmsd = &vmstate_aer915_state, |
| 269 | .init = aer915_init, |
| 270 | .event = aer915_event, |
| 271 | .recv = aer915_recv, |
| 272 | .send = aer915_send |
| 273 | }; |
| 274 | |
| 275 | static void z2_init(ram_addr_t ram_size, |
| 276 | const char *boot_device, |
| 277 | const char *kernel_filename, const char *kernel_cmdline, |
| 278 | const char *initrd_filename, const char *cpu_model) |
| 279 | { |
| 280 | uint32_t sector_len = 0x10000; |
| 281 | PXA2xxState *cpu; |
| 282 | DriveInfo *dinfo; |
| 283 | int be; |
| 284 | void *z2_lcd; |
| 285 | i2c_bus *bus; |
| 286 | DeviceState *wm; |
| 287 | |
| 288 | if (!cpu_model) { |
| 289 | cpu_model = "pxa270-c5"; |
| 290 | } |
| 291 | |
| 292 | /* Setup CPU & memory */ |
| 293 | cpu = pxa270_init(z2_binfo.ram_size, cpu_model); |
| 294 | |
| 295 | #ifdef TARGET_WORDS_BIGENDIAN |
| 296 | be = 1; |
| 297 | #else |
| 298 | be = 0; |
| 299 | #endif |
| 300 | dinfo = drive_get(IF_PFLASH, 0, 0); |
| 301 | if (!dinfo) { |
| 302 | fprintf(stderr, "Flash image must be given with the " |
| 303 | "'pflash' parameter\n"); |
| 304 | exit(1); |
| 305 | } |
| 306 | |
| 307 | if (!pflash_cfi01_register(Z2_FLASH_BASE, |
| 308 | qemu_ram_alloc(NULL, "z2.flash0", Z2_FLASH_SIZE), |
| 309 | dinfo->bdrv, sector_len, |
| 310 | Z2_FLASH_SIZE / sector_len, 4, 0, 0, 0, 0, |
| 311 | be)) { |
| 312 | fprintf(stderr, "qemu: Error registering flash memory.\n"); |
| 313 | exit(1); |
| 314 | } |
| 315 | |
| 316 | /* setup keypad */ |
| 317 | pxa27x_register_keypad(cpu->kp, map, 0x100); |
| 318 | |
| 319 | /* MMC/SD host */ |
| 320 | pxa2xx_mmci_handlers(cpu->mmc, |
| 321 | NULL, |
| 322 | qdev_get_gpio_in(cpu->gpio, Z2_GPIO_SD_DETECT)); |
| 323 | |
| 324 | ssi_register_slave(&zipit_lcd_info); |
| 325 | i2c_register_slave(&aer915_info); |
| 326 | z2_lcd = ssi_create_slave(cpu->ssp[1], "zipit-lcd"); |
| 327 | bus = pxa2xx_i2c_bus(cpu->i2c[0]); |
| 328 | i2c_create_slave(bus, "aer915", 0x55); |
| 329 | wm = i2c_create_slave(bus, "wm8750", 0x1b); |
| 330 | cpu->i2s->opaque = wm; |
| 331 | cpu->i2s->codec_out = wm8750_dac_dat; |
| 332 | cpu->i2s->codec_in = wm8750_adc_dat; |
| 333 | wm8750_data_req_set(wm, cpu->i2s->data_req, cpu->i2s); |
| 334 | |
| 335 | qdev_connect_gpio_out(cpu->gpio, Z2_GPIO_LCD_CS, |
| 336 | qemu_allocate_irqs(z2_lcd_cs, z2_lcd, 1)[0]); |
| 337 | |
| 338 | if (kernel_filename) { |
| 339 | z2_binfo.kernel_filename = kernel_filename; |
| 340 | z2_binfo.kernel_cmdline = kernel_cmdline; |
| 341 | z2_binfo.initrd_filename = initrd_filename; |
| 342 | z2_binfo.board_id = 0x6dd; |
| 343 | arm_load_kernel(cpu->env, &z2_binfo); |
| 344 | } |
| 345 | } |
| 346 | |
| 347 | static QEMUMachine z2_machine = { |
| 348 | .name = "z2", |
| 349 | .desc = "Zipit Z2 (PXA27x)", |
| 350 | .init = z2_init, |
| 351 | }; |
| 352 | |
| 353 | static void z2_machine_init(void) |
| 354 | { |
| 355 | qemu_register_machine(&z2_machine); |
| 356 | } |
| 357 | |
| 358 | machine_init(z2_machine_init); |