1 /* 2 * Marvell MV88W8618 / Freecom MusicPal emulation. 3 * 4 * Copyright (c) 2008 Jan Kiszka 5 * 6 * This code is licensed under the GNU GPL v2. 7 * 8 * Contributions after 2012-01-13 are licensed under the terms of the 9 * GNU GPL, version 2 or (at your option) any later version. 10 */ 11 12 #include "qemu/osdep.h" 13 #include "qapi/error.h" 14 #include "hw/sysbus.h" 15 #include "hw/arm/arm.h" 16 #include "hw/devices.h" 17 #include "net/net.h" 18 #include "sysemu/sysemu.h" 19 #include "hw/boards.h" 20 #include "hw/char/serial.h" 21 #include "qemu/timer.h" 22 #include "hw/ptimer.h" 23 #include "hw/block/flash.h" 24 #include "ui/console.h" 25 #include "hw/i2c/i2c.h" 26 #include "sysemu/block-backend.h" 27 #include "exec/address-spaces.h" 28 #include "ui/pixel_ops.h" 29 30 #define MP_MISC_BASE 0x80002000 31 #define MP_MISC_SIZE 0x00001000 32 33 #define MP_ETH_BASE 0x80008000 34 #define MP_ETH_SIZE 0x00001000 35 36 #define MP_WLAN_BASE 0x8000C000 37 #define MP_WLAN_SIZE 0x00000800 38 39 #define MP_UART1_BASE 0x8000C840 40 #define MP_UART2_BASE 0x8000C940 41 42 #define MP_GPIO_BASE 0x8000D000 43 #define MP_GPIO_SIZE 0x00001000 44 45 #define MP_FLASHCFG_BASE 0x90006000 46 #define MP_FLASHCFG_SIZE 0x00001000 47 48 #define MP_AUDIO_BASE 0x90007000 49 50 #define MP_PIC_BASE 0x90008000 51 #define MP_PIC_SIZE 0x00001000 52 53 #define MP_PIT_BASE 0x90009000 54 #define MP_PIT_SIZE 0x00001000 55 56 #define MP_LCD_BASE 0x9000c000 57 #define MP_LCD_SIZE 0x00001000 58 59 #define MP_SRAM_BASE 0xC0000000 60 #define MP_SRAM_SIZE 0x00020000 61 62 #define MP_RAM_DEFAULT_SIZE 32*1024*1024 63 #define MP_FLASH_SIZE_MAX 32*1024*1024 64 65 #define MP_TIMER1_IRQ 4 66 #define MP_TIMER2_IRQ 5 67 #define MP_TIMER3_IRQ 6 68 #define MP_TIMER4_IRQ 7 69 #define MP_EHCI_IRQ 8 70 #define MP_ETH_IRQ 9 71 #define MP_UART1_IRQ 11 72 #define MP_UART2_IRQ 11 73 #define MP_GPIO_IRQ 12 74 #define MP_RTC_IRQ 28 75 #define MP_AUDIO_IRQ 30 76 77 /* Wolfson 8750 I2C address */ 78 #define MP_WM_ADDR 0x1A 79 80 /* Ethernet register offsets */ 81 #define MP_ETH_SMIR 0x010 82 #define MP_ETH_PCXR 0x408 83 #define MP_ETH_SDCMR 0x448 84 #define MP_ETH_ICR 0x450 85 #define MP_ETH_IMR 0x458 86 #define MP_ETH_FRDP0 0x480 87 #define MP_ETH_FRDP1 0x484 88 #define MP_ETH_FRDP2 0x488 89 #define MP_ETH_FRDP3 0x48C 90 #define MP_ETH_CRDP0 0x4A0 91 #define MP_ETH_CRDP1 0x4A4 92 #define MP_ETH_CRDP2 0x4A8 93 #define MP_ETH_CRDP3 0x4AC 94 #define MP_ETH_CTDP0 0x4E0 95 #define MP_ETH_CTDP1 0x4E4 96 97 /* MII PHY access */ 98 #define MP_ETH_SMIR_DATA 0x0000FFFF 99 #define MP_ETH_SMIR_ADDR 0x03FF0000 100 #define MP_ETH_SMIR_OPCODE (1 << 26) /* Read value */ 101 #define MP_ETH_SMIR_RDVALID (1 << 27) 102 103 /* PHY registers */ 104 #define MP_ETH_PHY1_BMSR 0x00210000 105 #define MP_ETH_PHY1_PHYSID1 0x00410000 106 #define MP_ETH_PHY1_PHYSID2 0x00610000 107 108 #define MP_PHY_BMSR_LINK 0x0004 109 #define MP_PHY_BMSR_AUTONEG 0x0008 110 111 #define MP_PHY_88E3015 0x01410E20 112 113 /* TX descriptor status */ 114 #define MP_ETH_TX_OWN (1U << 31) 115 116 /* RX descriptor status */ 117 #define MP_ETH_RX_OWN (1U << 31) 118 119 /* Interrupt cause/mask bits */ 120 #define MP_ETH_IRQ_RX_BIT 0 121 #define MP_ETH_IRQ_RX (1 << MP_ETH_IRQ_RX_BIT) 122 #define MP_ETH_IRQ_TXHI_BIT 2 123 #define MP_ETH_IRQ_TXLO_BIT 3 124 125 /* Port config bits */ 126 #define MP_ETH_PCXR_2BSM_BIT 28 /* 2-byte incoming suffix */ 127 128 /* SDMA command bits */ 129 #define MP_ETH_CMD_TXHI (1 << 23) 130 #define MP_ETH_CMD_TXLO (1 << 22) 131 132 typedef struct mv88w8618_tx_desc { 133 uint32_t cmdstat; 134 uint16_t res; 135 uint16_t bytes; 136 uint32_t buffer; 137 uint32_t next; 138 } mv88w8618_tx_desc; 139 140 typedef struct mv88w8618_rx_desc { 141 uint32_t cmdstat; 142 uint16_t bytes; 143 uint16_t buffer_size; 144 uint32_t buffer; 145 uint32_t next; 146 } mv88w8618_rx_desc; 147 148 #define TYPE_MV88W8618_ETH "mv88w8618_eth" 149 #define MV88W8618_ETH(obj) \ 150 OBJECT_CHECK(mv88w8618_eth_state, (obj), TYPE_MV88W8618_ETH) 151 152 typedef struct mv88w8618_eth_state { 153 /*< private >*/ 154 SysBusDevice parent_obj; 155 /*< public >*/ 156 157 MemoryRegion iomem; 158 qemu_irq irq; 159 uint32_t smir; 160 uint32_t icr; 161 uint32_t imr; 162 int mmio_index; 163 uint32_t vlan_header; 164 uint32_t tx_queue[2]; 165 uint32_t rx_queue[4]; 166 uint32_t frx_queue[4]; 167 uint32_t cur_rx[4]; 168 NICState *nic; 169 NICConf conf; 170 } mv88w8618_eth_state; 171 172 static void eth_rx_desc_put(uint32_t addr, mv88w8618_rx_desc *desc) 173 { 174 cpu_to_le32s(&desc->cmdstat); 175 cpu_to_le16s(&desc->bytes); 176 cpu_to_le16s(&desc->buffer_size); 177 cpu_to_le32s(&desc->buffer); 178 cpu_to_le32s(&desc->next); 179 cpu_physical_memory_write(addr, desc, sizeof(*desc)); 180 } 181 182 static void eth_rx_desc_get(uint32_t addr, mv88w8618_rx_desc *desc) 183 { 184 cpu_physical_memory_read(addr, desc, sizeof(*desc)); 185 le32_to_cpus(&desc->cmdstat); 186 le16_to_cpus(&desc->bytes); 187 le16_to_cpus(&desc->buffer_size); 188 le32_to_cpus(&desc->buffer); 189 le32_to_cpus(&desc->next); 190 } 191 192 static ssize_t eth_receive(NetClientState *nc, const uint8_t *buf, size_t size) 193 { 194 mv88w8618_eth_state *s = qemu_get_nic_opaque(nc); 195 uint32_t desc_addr; 196 mv88w8618_rx_desc desc; 197 int i; 198 199 for (i = 0; i < 4; i++) { 200 desc_addr = s->cur_rx[i]; 201 if (!desc_addr) { 202 continue; 203 } 204 do { 205 eth_rx_desc_get(desc_addr, &desc); 206 if ((desc.cmdstat & MP_ETH_RX_OWN) && desc.buffer_size >= size) { 207 cpu_physical_memory_write(desc.buffer + s->vlan_header, 208 buf, size); 209 desc.bytes = size + s->vlan_header; 210 desc.cmdstat &= ~MP_ETH_RX_OWN; 211 s->cur_rx[i] = desc.next; 212 213 s->icr |= MP_ETH_IRQ_RX; 214 if (s->icr & s->imr) { 215 qemu_irq_raise(s->irq); 216 } 217 eth_rx_desc_put(desc_addr, &desc); 218 return size; 219 } 220 desc_addr = desc.next; 221 } while (desc_addr != s->rx_queue[i]); 222 } 223 return size; 224 } 225 226 static void eth_tx_desc_put(uint32_t addr, mv88w8618_tx_desc *desc) 227 { 228 cpu_to_le32s(&desc->cmdstat); 229 cpu_to_le16s(&desc->res); 230 cpu_to_le16s(&desc->bytes); 231 cpu_to_le32s(&desc->buffer); 232 cpu_to_le32s(&desc->next); 233 cpu_physical_memory_write(addr, desc, sizeof(*desc)); 234 } 235 236 static void eth_tx_desc_get(uint32_t addr, mv88w8618_tx_desc *desc) 237 { 238 cpu_physical_memory_read(addr, desc, sizeof(*desc)); 239 le32_to_cpus(&desc->cmdstat); 240 le16_to_cpus(&desc->res); 241 le16_to_cpus(&desc->bytes); 242 le32_to_cpus(&desc->buffer); 243 le32_to_cpus(&desc->next); 244 } 245 246 static void eth_send(mv88w8618_eth_state *s, int queue_index) 247 { 248 uint32_t desc_addr = s->tx_queue[queue_index]; 249 mv88w8618_tx_desc desc; 250 uint32_t next_desc; 251 uint8_t buf[2048]; 252 int len; 253 254 do { 255 eth_tx_desc_get(desc_addr, &desc); 256 next_desc = desc.next; 257 if (desc.cmdstat & MP_ETH_TX_OWN) { 258 len = desc.bytes; 259 if (len < 2048) { 260 cpu_physical_memory_read(desc.buffer, buf, len); 261 qemu_send_packet(qemu_get_queue(s->nic), buf, len); 262 } 263 desc.cmdstat &= ~MP_ETH_TX_OWN; 264 s->icr |= 1 << (MP_ETH_IRQ_TXLO_BIT - queue_index); 265 eth_tx_desc_put(desc_addr, &desc); 266 } 267 desc_addr = next_desc; 268 } while (desc_addr != s->tx_queue[queue_index]); 269 } 270 271 static uint64_t mv88w8618_eth_read(void *opaque, hwaddr offset, 272 unsigned size) 273 { 274 mv88w8618_eth_state *s = opaque; 275 276 switch (offset) { 277 case MP_ETH_SMIR: 278 if (s->smir & MP_ETH_SMIR_OPCODE) { 279 switch (s->smir & MP_ETH_SMIR_ADDR) { 280 case MP_ETH_PHY1_BMSR: 281 return MP_PHY_BMSR_LINK | MP_PHY_BMSR_AUTONEG | 282 MP_ETH_SMIR_RDVALID; 283 case MP_ETH_PHY1_PHYSID1: 284 return (MP_PHY_88E3015 >> 16) | MP_ETH_SMIR_RDVALID; 285 case MP_ETH_PHY1_PHYSID2: 286 return (MP_PHY_88E3015 & 0xFFFF) | MP_ETH_SMIR_RDVALID; 287 default: 288 return MP_ETH_SMIR_RDVALID; 289 } 290 } 291 return 0; 292 293 case MP_ETH_ICR: 294 return s->icr; 295 296 case MP_ETH_IMR: 297 return s->imr; 298 299 case MP_ETH_FRDP0 ... MP_ETH_FRDP3: 300 return s->frx_queue[(offset - MP_ETH_FRDP0)/4]; 301 302 case MP_ETH_CRDP0 ... MP_ETH_CRDP3: 303 return s->rx_queue[(offset - MP_ETH_CRDP0)/4]; 304 305 case MP_ETH_CTDP0 ... MP_ETH_CTDP1: 306 return s->tx_queue[(offset - MP_ETH_CTDP0)/4]; 307 308 default: 309 return 0; 310 } 311 } 312 313 static void mv88w8618_eth_write(void *opaque, hwaddr offset, 314 uint64_t value, unsigned size) 315 { 316 mv88w8618_eth_state *s = opaque; 317 318 switch (offset) { 319 case MP_ETH_SMIR: 320 s->smir = value; 321 break; 322 323 case MP_ETH_PCXR: 324 s->vlan_header = ((value >> MP_ETH_PCXR_2BSM_BIT) & 1) * 2; 325 break; 326 327 case MP_ETH_SDCMR: 328 if (value & MP_ETH_CMD_TXHI) { 329 eth_send(s, 1); 330 } 331 if (value & MP_ETH_CMD_TXLO) { 332 eth_send(s, 0); 333 } 334 if (value & (MP_ETH_CMD_TXHI | MP_ETH_CMD_TXLO) && s->icr & s->imr) { 335 qemu_irq_raise(s->irq); 336 } 337 break; 338 339 case MP_ETH_ICR: 340 s->icr &= value; 341 break; 342 343 case MP_ETH_IMR: 344 s->imr = value; 345 if (s->icr & s->imr) { 346 qemu_irq_raise(s->irq); 347 } 348 break; 349 350 case MP_ETH_FRDP0 ... MP_ETH_FRDP3: 351 s->frx_queue[(offset - MP_ETH_FRDP0)/4] = value; 352 break; 353 354 case MP_ETH_CRDP0 ... MP_ETH_CRDP3: 355 s->rx_queue[(offset - MP_ETH_CRDP0)/4] = 356 s->cur_rx[(offset - MP_ETH_CRDP0)/4] = value; 357 break; 358 359 case MP_ETH_CTDP0 ... MP_ETH_CTDP1: 360 s->tx_queue[(offset - MP_ETH_CTDP0)/4] = value; 361 break; 362 } 363 } 364 365 static const MemoryRegionOps mv88w8618_eth_ops = { 366 .read = mv88w8618_eth_read, 367 .write = mv88w8618_eth_write, 368 .endianness = DEVICE_NATIVE_ENDIAN, 369 }; 370 371 static void eth_cleanup(NetClientState *nc) 372 { 373 mv88w8618_eth_state *s = qemu_get_nic_opaque(nc); 374 375 s->nic = NULL; 376 } 377 378 static NetClientInfo net_mv88w8618_info = { 379 .type = NET_CLIENT_OPTIONS_KIND_NIC, 380 .size = sizeof(NICState), 381 .receive = eth_receive, 382 .cleanup = eth_cleanup, 383 }; 384 385 static int mv88w8618_eth_init(SysBusDevice *sbd) 386 { 387 DeviceState *dev = DEVICE(sbd); 388 mv88w8618_eth_state *s = MV88W8618_ETH(dev); 389 390 sysbus_init_irq(sbd, &s->irq); 391 s->nic = qemu_new_nic(&net_mv88w8618_info, &s->conf, 392 object_get_typename(OBJECT(dev)), dev->id, s); 393 memory_region_init_io(&s->iomem, OBJECT(s), &mv88w8618_eth_ops, s, 394 "mv88w8618-eth", MP_ETH_SIZE); 395 sysbus_init_mmio(sbd, &s->iomem); 396 return 0; 397 } 398 399 static const VMStateDescription mv88w8618_eth_vmsd = { 400 .name = "mv88w8618_eth", 401 .version_id = 1, 402 .minimum_version_id = 1, 403 .fields = (VMStateField[]) { 404 VMSTATE_UINT32(smir, mv88w8618_eth_state), 405 VMSTATE_UINT32(icr, mv88w8618_eth_state), 406 VMSTATE_UINT32(imr, mv88w8618_eth_state), 407 VMSTATE_UINT32(vlan_header, mv88w8618_eth_state), 408 VMSTATE_UINT32_ARRAY(tx_queue, mv88w8618_eth_state, 2), 409 VMSTATE_UINT32_ARRAY(rx_queue, mv88w8618_eth_state, 4), 410 VMSTATE_UINT32_ARRAY(frx_queue, mv88w8618_eth_state, 4), 411 VMSTATE_UINT32_ARRAY(cur_rx, mv88w8618_eth_state, 4), 412 VMSTATE_END_OF_LIST() 413 } 414 }; 415 416 static Property mv88w8618_eth_properties[] = { 417 DEFINE_NIC_PROPERTIES(mv88w8618_eth_state, conf), 418 DEFINE_PROP_END_OF_LIST(), 419 }; 420 421 static void mv88w8618_eth_class_init(ObjectClass *klass, void *data) 422 { 423 DeviceClass *dc = DEVICE_CLASS(klass); 424 SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); 425 426 k->init = mv88w8618_eth_init; 427 dc->vmsd = &mv88w8618_eth_vmsd; 428 dc->props = mv88w8618_eth_properties; 429 } 430 431 static const TypeInfo mv88w8618_eth_info = { 432 .name = TYPE_MV88W8618_ETH, 433 .parent = TYPE_SYS_BUS_DEVICE, 434 .instance_size = sizeof(mv88w8618_eth_state), 435 .class_init = mv88w8618_eth_class_init, 436 }; 437 438 /* LCD register offsets */ 439 #define MP_LCD_IRQCTRL 0x180 440 #define MP_LCD_IRQSTAT 0x184 441 #define MP_LCD_SPICTRL 0x1ac 442 #define MP_LCD_INST 0x1bc 443 #define MP_LCD_DATA 0x1c0 444 445 /* Mode magics */ 446 #define MP_LCD_SPI_DATA 0x00100011 447 #define MP_LCD_SPI_CMD 0x00104011 448 #define MP_LCD_SPI_INVALID 0x00000000 449 450 /* Commmands */ 451 #define MP_LCD_INST_SETPAGE0 0xB0 452 /* ... */ 453 #define MP_LCD_INST_SETPAGE7 0xB7 454 455 #define MP_LCD_TEXTCOLOR 0xe0e0ff /* RRGGBB */ 456 457 #define TYPE_MUSICPAL_LCD "musicpal_lcd" 458 #define MUSICPAL_LCD(obj) \ 459 OBJECT_CHECK(musicpal_lcd_state, (obj), TYPE_MUSICPAL_LCD) 460 461 typedef struct musicpal_lcd_state { 462 /*< private >*/ 463 SysBusDevice parent_obj; 464 /*< public >*/ 465 466 MemoryRegion iomem; 467 uint32_t brightness; 468 uint32_t mode; 469 uint32_t irqctrl; 470 uint32_t page; 471 uint32_t page_off; 472 QemuConsole *con; 473 uint8_t video_ram[128*64/8]; 474 } musicpal_lcd_state; 475 476 static uint8_t scale_lcd_color(musicpal_lcd_state *s, uint8_t col) 477 { 478 switch (s->brightness) { 479 case 7: 480 return col; 481 case 0: 482 return 0; 483 default: 484 return (col * s->brightness) / 7; 485 } 486 } 487 488 #define SET_LCD_PIXEL(depth, type) \ 489 static inline void glue(set_lcd_pixel, depth) \ 490 (musicpal_lcd_state *s, int x, int y, type col) \ 491 { \ 492 int dx, dy; \ 493 DisplaySurface *surface = qemu_console_surface(s->con); \ 494 type *pixel = &((type *) surface_data(surface))[(y * 128 * 3 + x) * 3]; \ 495 \ 496 for (dy = 0; dy < 3; dy++, pixel += 127 * 3) \ 497 for (dx = 0; dx < 3; dx++, pixel++) \ 498 *pixel = col; \ 499 } 500 SET_LCD_PIXEL(8, uint8_t) 501 SET_LCD_PIXEL(16, uint16_t) 502 SET_LCD_PIXEL(32, uint32_t) 503 504 static void lcd_refresh(void *opaque) 505 { 506 musicpal_lcd_state *s = opaque; 507 DisplaySurface *surface = qemu_console_surface(s->con); 508 int x, y, col; 509 510 switch (surface_bits_per_pixel(surface)) { 511 case 0: 512 return; 513 #define LCD_REFRESH(depth, func) \ 514 case depth: \ 515 col = func(scale_lcd_color(s, (MP_LCD_TEXTCOLOR >> 16) & 0xff), \ 516 scale_lcd_color(s, (MP_LCD_TEXTCOLOR >> 8) & 0xff), \ 517 scale_lcd_color(s, MP_LCD_TEXTCOLOR & 0xff)); \ 518 for (x = 0; x < 128; x++) { \ 519 for (y = 0; y < 64; y++) { \ 520 if (s->video_ram[x + (y/8)*128] & (1 << (y % 8))) { \ 521 glue(set_lcd_pixel, depth)(s, x, y, col); \ 522 } else { \ 523 glue(set_lcd_pixel, depth)(s, x, y, 0); \ 524 } \ 525 } \ 526 } \ 527 break; 528 LCD_REFRESH(8, rgb_to_pixel8) 529 LCD_REFRESH(16, rgb_to_pixel16) 530 LCD_REFRESH(32, (is_surface_bgr(surface) ? 531 rgb_to_pixel32bgr : rgb_to_pixel32)) 532 default: 533 hw_error("unsupported colour depth %i\n", 534 surface_bits_per_pixel(surface)); 535 } 536 537 dpy_gfx_update(s->con, 0, 0, 128*3, 64*3); 538 } 539 540 static void lcd_invalidate(void *opaque) 541 { 542 } 543 544 static void musicpal_lcd_gpio_brightness_in(void *opaque, int irq, int level) 545 { 546 musicpal_lcd_state *s = opaque; 547 s->brightness &= ~(1 << irq); 548 s->brightness |= level << irq; 549 } 550 551 static uint64_t musicpal_lcd_read(void *opaque, hwaddr offset, 552 unsigned size) 553 { 554 musicpal_lcd_state *s = opaque; 555 556 switch (offset) { 557 case MP_LCD_IRQCTRL: 558 return s->irqctrl; 559 560 default: 561 return 0; 562 } 563 } 564 565 static void musicpal_lcd_write(void *opaque, hwaddr offset, 566 uint64_t value, unsigned size) 567 { 568 musicpal_lcd_state *s = opaque; 569 570 switch (offset) { 571 case MP_LCD_IRQCTRL: 572 s->irqctrl = value; 573 break; 574 575 case MP_LCD_SPICTRL: 576 if (value == MP_LCD_SPI_DATA || value == MP_LCD_SPI_CMD) { 577 s->mode = value; 578 } else { 579 s->mode = MP_LCD_SPI_INVALID; 580 } 581 break; 582 583 case MP_LCD_INST: 584 if (value >= MP_LCD_INST_SETPAGE0 && value <= MP_LCD_INST_SETPAGE7) { 585 s->page = value - MP_LCD_INST_SETPAGE0; 586 s->page_off = 0; 587 } 588 break; 589 590 case MP_LCD_DATA: 591 if (s->mode == MP_LCD_SPI_CMD) { 592 if (value >= MP_LCD_INST_SETPAGE0 && 593 value <= MP_LCD_INST_SETPAGE7) { 594 s->page = value - MP_LCD_INST_SETPAGE0; 595 s->page_off = 0; 596 } 597 } else if (s->mode == MP_LCD_SPI_DATA) { 598 s->video_ram[s->page*128 + s->page_off] = value; 599 s->page_off = (s->page_off + 1) & 127; 600 } 601 break; 602 } 603 } 604 605 static const MemoryRegionOps musicpal_lcd_ops = { 606 .read = musicpal_lcd_read, 607 .write = musicpal_lcd_write, 608 .endianness = DEVICE_NATIVE_ENDIAN, 609 }; 610 611 static const GraphicHwOps musicpal_gfx_ops = { 612 .invalidate = lcd_invalidate, 613 .gfx_update = lcd_refresh, 614 }; 615 616 static int musicpal_lcd_init(SysBusDevice *sbd) 617 { 618 DeviceState *dev = DEVICE(sbd); 619 musicpal_lcd_state *s = MUSICPAL_LCD(dev); 620 621 s->brightness = 7; 622 623 memory_region_init_io(&s->iomem, OBJECT(s), &musicpal_lcd_ops, s, 624 "musicpal-lcd", MP_LCD_SIZE); 625 sysbus_init_mmio(sbd, &s->iomem); 626 627 s->con = graphic_console_init(dev, 0, &musicpal_gfx_ops, s); 628 qemu_console_resize(s->con, 128*3, 64*3); 629 630 qdev_init_gpio_in(dev, musicpal_lcd_gpio_brightness_in, 3); 631 632 return 0; 633 } 634 635 static const VMStateDescription musicpal_lcd_vmsd = { 636 .name = "musicpal_lcd", 637 .version_id = 1, 638 .minimum_version_id = 1, 639 .fields = (VMStateField[]) { 640 VMSTATE_UINT32(brightness, musicpal_lcd_state), 641 VMSTATE_UINT32(mode, musicpal_lcd_state), 642 VMSTATE_UINT32(irqctrl, musicpal_lcd_state), 643 VMSTATE_UINT32(page, musicpal_lcd_state), 644 VMSTATE_UINT32(page_off, musicpal_lcd_state), 645 VMSTATE_BUFFER(video_ram, musicpal_lcd_state), 646 VMSTATE_END_OF_LIST() 647 } 648 }; 649 650 static void musicpal_lcd_class_init(ObjectClass *klass, void *data) 651 { 652 DeviceClass *dc = DEVICE_CLASS(klass); 653 SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); 654 655 k->init = musicpal_lcd_init; 656 dc->vmsd = &musicpal_lcd_vmsd; 657 } 658 659 static const TypeInfo musicpal_lcd_info = { 660 .name = TYPE_MUSICPAL_LCD, 661 .parent = TYPE_SYS_BUS_DEVICE, 662 .instance_size = sizeof(musicpal_lcd_state), 663 .class_init = musicpal_lcd_class_init, 664 }; 665 666 /* PIC register offsets */ 667 #define MP_PIC_STATUS 0x00 668 #define MP_PIC_ENABLE_SET 0x08 669 #define MP_PIC_ENABLE_CLR 0x0C 670 671 #define TYPE_MV88W8618_PIC "mv88w8618_pic" 672 #define MV88W8618_PIC(obj) \ 673 OBJECT_CHECK(mv88w8618_pic_state, (obj), TYPE_MV88W8618_PIC) 674 675 typedef struct mv88w8618_pic_state { 676 /*< private >*/ 677 SysBusDevice parent_obj; 678 /*< public >*/ 679 680 MemoryRegion iomem; 681 uint32_t level; 682 uint32_t enabled; 683 qemu_irq parent_irq; 684 } mv88w8618_pic_state; 685 686 static void mv88w8618_pic_update(mv88w8618_pic_state *s) 687 { 688 qemu_set_irq(s->parent_irq, (s->level & s->enabled)); 689 } 690 691 static void mv88w8618_pic_set_irq(void *opaque, int irq, int level) 692 { 693 mv88w8618_pic_state *s = opaque; 694 695 if (level) { 696 s->level |= 1 << irq; 697 } else { 698 s->level &= ~(1 << irq); 699 } 700 mv88w8618_pic_update(s); 701 } 702 703 static uint64_t mv88w8618_pic_read(void *opaque, hwaddr offset, 704 unsigned size) 705 { 706 mv88w8618_pic_state *s = opaque; 707 708 switch (offset) { 709 case MP_PIC_STATUS: 710 return s->level & s->enabled; 711 712 default: 713 return 0; 714 } 715 } 716 717 static void mv88w8618_pic_write(void *opaque, hwaddr offset, 718 uint64_t value, unsigned size) 719 { 720 mv88w8618_pic_state *s = opaque; 721 722 switch (offset) { 723 case MP_PIC_ENABLE_SET: 724 s->enabled |= value; 725 break; 726 727 case MP_PIC_ENABLE_CLR: 728 s->enabled &= ~value; 729 s->level &= ~value; 730 break; 731 } 732 mv88w8618_pic_update(s); 733 } 734 735 static void mv88w8618_pic_reset(DeviceState *d) 736 { 737 mv88w8618_pic_state *s = MV88W8618_PIC(d); 738 739 s->level = 0; 740 s->enabled = 0; 741 } 742 743 static const MemoryRegionOps mv88w8618_pic_ops = { 744 .read = mv88w8618_pic_read, 745 .write = mv88w8618_pic_write, 746 .endianness = DEVICE_NATIVE_ENDIAN, 747 }; 748 749 static int mv88w8618_pic_init(SysBusDevice *dev) 750 { 751 mv88w8618_pic_state *s = MV88W8618_PIC(dev); 752 753 qdev_init_gpio_in(DEVICE(dev), mv88w8618_pic_set_irq, 32); 754 sysbus_init_irq(dev, &s->parent_irq); 755 memory_region_init_io(&s->iomem, OBJECT(s), &mv88w8618_pic_ops, s, 756 "musicpal-pic", MP_PIC_SIZE); 757 sysbus_init_mmio(dev, &s->iomem); 758 return 0; 759 } 760 761 static const VMStateDescription mv88w8618_pic_vmsd = { 762 .name = "mv88w8618_pic", 763 .version_id = 1, 764 .minimum_version_id = 1, 765 .fields = (VMStateField[]) { 766 VMSTATE_UINT32(level, mv88w8618_pic_state), 767 VMSTATE_UINT32(enabled, mv88w8618_pic_state), 768 VMSTATE_END_OF_LIST() 769 } 770 }; 771 772 static void mv88w8618_pic_class_init(ObjectClass *klass, void *data) 773 { 774 DeviceClass *dc = DEVICE_CLASS(klass); 775 SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); 776 777 k->init = mv88w8618_pic_init; 778 dc->reset = mv88w8618_pic_reset; 779 dc->vmsd = &mv88w8618_pic_vmsd; 780 } 781 782 static const TypeInfo mv88w8618_pic_info = { 783 .name = TYPE_MV88W8618_PIC, 784 .parent = TYPE_SYS_BUS_DEVICE, 785 .instance_size = sizeof(mv88w8618_pic_state), 786 .class_init = mv88w8618_pic_class_init, 787 }; 788 789 /* PIT register offsets */ 790 #define MP_PIT_TIMER1_LENGTH 0x00 791 /* ... */ 792 #define MP_PIT_TIMER4_LENGTH 0x0C 793 #define MP_PIT_CONTROL 0x10 794 #define MP_PIT_TIMER1_VALUE 0x14 795 /* ... */ 796 #define MP_PIT_TIMER4_VALUE 0x20 797 #define MP_BOARD_RESET 0x34 798 799 /* Magic board reset value (probably some watchdog behind it) */ 800 #define MP_BOARD_RESET_MAGIC 0x10000 801 802 typedef struct mv88w8618_timer_state { 803 ptimer_state *ptimer; 804 uint32_t limit; 805 int freq; 806 qemu_irq irq; 807 } mv88w8618_timer_state; 808 809 #define TYPE_MV88W8618_PIT "mv88w8618_pit" 810 #define MV88W8618_PIT(obj) \ 811 OBJECT_CHECK(mv88w8618_pit_state, (obj), TYPE_MV88W8618_PIT) 812 813 typedef struct mv88w8618_pit_state { 814 /*< private >*/ 815 SysBusDevice parent_obj; 816 /*< public >*/ 817 818 MemoryRegion iomem; 819 mv88w8618_timer_state timer[4]; 820 } mv88w8618_pit_state; 821 822 static void mv88w8618_timer_tick(void *opaque) 823 { 824 mv88w8618_timer_state *s = opaque; 825 826 qemu_irq_raise(s->irq); 827 } 828 829 static void mv88w8618_timer_init(SysBusDevice *dev, mv88w8618_timer_state *s, 830 uint32_t freq) 831 { 832 QEMUBH *bh; 833 834 sysbus_init_irq(dev, &s->irq); 835 s->freq = freq; 836 837 bh = qemu_bh_new(mv88w8618_timer_tick, s); 838 s->ptimer = ptimer_init(bh); 839 } 840 841 static uint64_t mv88w8618_pit_read(void *opaque, hwaddr offset, 842 unsigned size) 843 { 844 mv88w8618_pit_state *s = opaque; 845 mv88w8618_timer_state *t; 846 847 switch (offset) { 848 case MP_PIT_TIMER1_VALUE ... MP_PIT_TIMER4_VALUE: 849 t = &s->timer[(offset-MP_PIT_TIMER1_VALUE) >> 2]; 850 return ptimer_get_count(t->ptimer); 851 852 default: 853 return 0; 854 } 855 } 856 857 static void mv88w8618_pit_write(void *opaque, hwaddr offset, 858 uint64_t value, unsigned size) 859 { 860 mv88w8618_pit_state *s = opaque; 861 mv88w8618_timer_state *t; 862 int i; 863 864 switch (offset) { 865 case MP_PIT_TIMER1_LENGTH ... MP_PIT_TIMER4_LENGTH: 866 t = &s->timer[offset >> 2]; 867 t->limit = value; 868 if (t->limit > 0) { 869 ptimer_set_limit(t->ptimer, t->limit, 1); 870 } else { 871 ptimer_stop(t->ptimer); 872 } 873 break; 874 875 case MP_PIT_CONTROL: 876 for (i = 0; i < 4; i++) { 877 t = &s->timer[i]; 878 if (value & 0xf && t->limit > 0) { 879 ptimer_set_limit(t->ptimer, t->limit, 0); 880 ptimer_set_freq(t->ptimer, t->freq); 881 ptimer_run(t->ptimer, 0); 882 } else { 883 ptimer_stop(t->ptimer); 884 } 885 value >>= 4; 886 } 887 break; 888 889 case MP_BOARD_RESET: 890 if (value == MP_BOARD_RESET_MAGIC) { 891 qemu_system_reset_request(); 892 } 893 break; 894 } 895 } 896 897 static void mv88w8618_pit_reset(DeviceState *d) 898 { 899 mv88w8618_pit_state *s = MV88W8618_PIT(d); 900 int i; 901 902 for (i = 0; i < 4; i++) { 903 ptimer_stop(s->timer[i].ptimer); 904 s->timer[i].limit = 0; 905 } 906 } 907 908 static const MemoryRegionOps mv88w8618_pit_ops = { 909 .read = mv88w8618_pit_read, 910 .write = mv88w8618_pit_write, 911 .endianness = DEVICE_NATIVE_ENDIAN, 912 }; 913 914 static int mv88w8618_pit_init(SysBusDevice *dev) 915 { 916 mv88w8618_pit_state *s = MV88W8618_PIT(dev); 917 int i; 918 919 /* Letting them all run at 1 MHz is likely just a pragmatic 920 * simplification. */ 921 for (i = 0; i < 4; i++) { 922 mv88w8618_timer_init(dev, &s->timer[i], 1000000); 923 } 924 925 memory_region_init_io(&s->iomem, OBJECT(s), &mv88w8618_pit_ops, s, 926 "musicpal-pit", MP_PIT_SIZE); 927 sysbus_init_mmio(dev, &s->iomem); 928 return 0; 929 } 930 931 static const VMStateDescription mv88w8618_timer_vmsd = { 932 .name = "timer", 933 .version_id = 1, 934 .minimum_version_id = 1, 935 .fields = (VMStateField[]) { 936 VMSTATE_PTIMER(ptimer, mv88w8618_timer_state), 937 VMSTATE_UINT32(limit, mv88w8618_timer_state), 938 VMSTATE_END_OF_LIST() 939 } 940 }; 941 942 static const VMStateDescription mv88w8618_pit_vmsd = { 943 .name = "mv88w8618_pit", 944 .version_id = 1, 945 .minimum_version_id = 1, 946 .fields = (VMStateField[]) { 947 VMSTATE_STRUCT_ARRAY(timer, mv88w8618_pit_state, 4, 1, 948 mv88w8618_timer_vmsd, mv88w8618_timer_state), 949 VMSTATE_END_OF_LIST() 950 } 951 }; 952 953 static void mv88w8618_pit_class_init(ObjectClass *klass, void *data) 954 { 955 DeviceClass *dc = DEVICE_CLASS(klass); 956 SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); 957 958 k->init = mv88w8618_pit_init; 959 dc->reset = mv88w8618_pit_reset; 960 dc->vmsd = &mv88w8618_pit_vmsd; 961 } 962 963 static const TypeInfo mv88w8618_pit_info = { 964 .name = TYPE_MV88W8618_PIT, 965 .parent = TYPE_SYS_BUS_DEVICE, 966 .instance_size = sizeof(mv88w8618_pit_state), 967 .class_init = mv88w8618_pit_class_init, 968 }; 969 970 /* Flash config register offsets */ 971 #define MP_FLASHCFG_CFGR0 0x04 972 973 #define TYPE_MV88W8618_FLASHCFG "mv88w8618_flashcfg" 974 #define MV88W8618_FLASHCFG(obj) \ 975 OBJECT_CHECK(mv88w8618_flashcfg_state, (obj), TYPE_MV88W8618_FLASHCFG) 976 977 typedef struct mv88w8618_flashcfg_state { 978 /*< private >*/ 979 SysBusDevice parent_obj; 980 /*< public >*/ 981 982 MemoryRegion iomem; 983 uint32_t cfgr0; 984 } mv88w8618_flashcfg_state; 985 986 static uint64_t mv88w8618_flashcfg_read(void *opaque, 987 hwaddr offset, 988 unsigned size) 989 { 990 mv88w8618_flashcfg_state *s = opaque; 991 992 switch (offset) { 993 case MP_FLASHCFG_CFGR0: 994 return s->cfgr0; 995 996 default: 997 return 0; 998 } 999 } 1000 1001 static void mv88w8618_flashcfg_write(void *opaque, hwaddr offset, 1002 uint64_t value, unsigned size) 1003 { 1004 mv88w8618_flashcfg_state *s = opaque; 1005 1006 switch (offset) { 1007 case MP_FLASHCFG_CFGR0: 1008 s->cfgr0 = value; 1009 break; 1010 } 1011 } 1012 1013 static const MemoryRegionOps mv88w8618_flashcfg_ops = { 1014 .read = mv88w8618_flashcfg_read, 1015 .write = mv88w8618_flashcfg_write, 1016 .endianness = DEVICE_NATIVE_ENDIAN, 1017 }; 1018 1019 static int mv88w8618_flashcfg_init(SysBusDevice *dev) 1020 { 1021 mv88w8618_flashcfg_state *s = MV88W8618_FLASHCFG(dev); 1022 1023 s->cfgr0 = 0xfffe4285; /* Default as set by U-Boot for 8 MB flash */ 1024 memory_region_init_io(&s->iomem, OBJECT(s), &mv88w8618_flashcfg_ops, s, 1025 "musicpal-flashcfg", MP_FLASHCFG_SIZE); 1026 sysbus_init_mmio(dev, &s->iomem); 1027 return 0; 1028 } 1029 1030 static const VMStateDescription mv88w8618_flashcfg_vmsd = { 1031 .name = "mv88w8618_flashcfg", 1032 .version_id = 1, 1033 .minimum_version_id = 1, 1034 .fields = (VMStateField[]) { 1035 VMSTATE_UINT32(cfgr0, mv88w8618_flashcfg_state), 1036 VMSTATE_END_OF_LIST() 1037 } 1038 }; 1039 1040 static void mv88w8618_flashcfg_class_init(ObjectClass *klass, void *data) 1041 { 1042 DeviceClass *dc = DEVICE_CLASS(klass); 1043 SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); 1044 1045 k->init = mv88w8618_flashcfg_init; 1046 dc->vmsd = &mv88w8618_flashcfg_vmsd; 1047 } 1048 1049 static const TypeInfo mv88w8618_flashcfg_info = { 1050 .name = TYPE_MV88W8618_FLASHCFG, 1051 .parent = TYPE_SYS_BUS_DEVICE, 1052 .instance_size = sizeof(mv88w8618_flashcfg_state), 1053 .class_init = mv88w8618_flashcfg_class_init, 1054 }; 1055 1056 /* Misc register offsets */ 1057 #define MP_MISC_BOARD_REVISION 0x18 1058 1059 #define MP_BOARD_REVISION 0x31 1060 1061 typedef struct { 1062 SysBusDevice parent_obj; 1063 MemoryRegion iomem; 1064 } MusicPalMiscState; 1065 1066 #define TYPE_MUSICPAL_MISC "musicpal-misc" 1067 #define MUSICPAL_MISC(obj) \ 1068 OBJECT_CHECK(MusicPalMiscState, (obj), TYPE_MUSICPAL_MISC) 1069 1070 static uint64_t musicpal_misc_read(void *opaque, hwaddr offset, 1071 unsigned size) 1072 { 1073 switch (offset) { 1074 case MP_MISC_BOARD_REVISION: 1075 return MP_BOARD_REVISION; 1076 1077 default: 1078 return 0; 1079 } 1080 } 1081 1082 static void musicpal_misc_write(void *opaque, hwaddr offset, 1083 uint64_t value, unsigned size) 1084 { 1085 } 1086 1087 static const MemoryRegionOps musicpal_misc_ops = { 1088 .read = musicpal_misc_read, 1089 .write = musicpal_misc_write, 1090 .endianness = DEVICE_NATIVE_ENDIAN, 1091 }; 1092 1093 static void musicpal_misc_init(Object *obj) 1094 { 1095 SysBusDevice *sd = SYS_BUS_DEVICE(obj); 1096 MusicPalMiscState *s = MUSICPAL_MISC(obj); 1097 1098 memory_region_init_io(&s->iomem, OBJECT(s), &musicpal_misc_ops, NULL, 1099 "musicpal-misc", MP_MISC_SIZE); 1100 sysbus_init_mmio(sd, &s->iomem); 1101 } 1102 1103 static const TypeInfo musicpal_misc_info = { 1104 .name = TYPE_MUSICPAL_MISC, 1105 .parent = TYPE_SYS_BUS_DEVICE, 1106 .instance_init = musicpal_misc_init, 1107 .instance_size = sizeof(MusicPalMiscState), 1108 }; 1109 1110 /* WLAN register offsets */ 1111 #define MP_WLAN_MAGIC1 0x11c 1112 #define MP_WLAN_MAGIC2 0x124 1113 1114 static uint64_t mv88w8618_wlan_read(void *opaque, hwaddr offset, 1115 unsigned size) 1116 { 1117 switch (offset) { 1118 /* Workaround to allow loading the binary-only wlandrv.ko crap 1119 * from the original Freecom firmware. */ 1120 case MP_WLAN_MAGIC1: 1121 return ~3; 1122 case MP_WLAN_MAGIC2: 1123 return -1; 1124 1125 default: 1126 return 0; 1127 } 1128 } 1129 1130 static void mv88w8618_wlan_write(void *opaque, hwaddr offset, 1131 uint64_t value, unsigned size) 1132 { 1133 } 1134 1135 static const MemoryRegionOps mv88w8618_wlan_ops = { 1136 .read = mv88w8618_wlan_read, 1137 .write =mv88w8618_wlan_write, 1138 .endianness = DEVICE_NATIVE_ENDIAN, 1139 }; 1140 1141 static int mv88w8618_wlan_init(SysBusDevice *dev) 1142 { 1143 MemoryRegion *iomem = g_new(MemoryRegion, 1); 1144 1145 memory_region_init_io(iomem, OBJECT(dev), &mv88w8618_wlan_ops, NULL, 1146 "musicpal-wlan", MP_WLAN_SIZE); 1147 sysbus_init_mmio(dev, iomem); 1148 return 0; 1149 } 1150 1151 /* GPIO register offsets */ 1152 #define MP_GPIO_OE_LO 0x008 1153 #define MP_GPIO_OUT_LO 0x00c 1154 #define MP_GPIO_IN_LO 0x010 1155 #define MP_GPIO_IER_LO 0x014 1156 #define MP_GPIO_IMR_LO 0x018 1157 #define MP_GPIO_ISR_LO 0x020 1158 #define MP_GPIO_OE_HI 0x508 1159 #define MP_GPIO_OUT_HI 0x50c 1160 #define MP_GPIO_IN_HI 0x510 1161 #define MP_GPIO_IER_HI 0x514 1162 #define MP_GPIO_IMR_HI 0x518 1163 #define MP_GPIO_ISR_HI 0x520 1164 1165 /* GPIO bits & masks */ 1166 #define MP_GPIO_LCD_BRIGHTNESS 0x00070000 1167 #define MP_GPIO_I2C_DATA_BIT 29 1168 #define MP_GPIO_I2C_CLOCK_BIT 30 1169 1170 /* LCD brightness bits in GPIO_OE_HI */ 1171 #define MP_OE_LCD_BRIGHTNESS 0x0007 1172 1173 #define TYPE_MUSICPAL_GPIO "musicpal_gpio" 1174 #define MUSICPAL_GPIO(obj) \ 1175 OBJECT_CHECK(musicpal_gpio_state, (obj), TYPE_MUSICPAL_GPIO) 1176 1177 typedef struct musicpal_gpio_state { 1178 /*< private >*/ 1179 SysBusDevice parent_obj; 1180 /*< public >*/ 1181 1182 MemoryRegion iomem; 1183 uint32_t lcd_brightness; 1184 uint32_t out_state; 1185 uint32_t in_state; 1186 uint32_t ier; 1187 uint32_t imr; 1188 uint32_t isr; 1189 qemu_irq irq; 1190 qemu_irq out[5]; /* 3 brightness out + 2 lcd (data and clock ) */ 1191 } musicpal_gpio_state; 1192 1193 static void musicpal_gpio_brightness_update(musicpal_gpio_state *s) { 1194 int i; 1195 uint32_t brightness; 1196 1197 /* compute brightness ratio */ 1198 switch (s->lcd_brightness) { 1199 case 0x00000007: 1200 brightness = 0; 1201 break; 1202 1203 case 0x00020000: 1204 brightness = 1; 1205 break; 1206 1207 case 0x00020001: 1208 brightness = 2; 1209 break; 1210 1211 case 0x00040000: 1212 brightness = 3; 1213 break; 1214 1215 case 0x00010006: 1216 brightness = 4; 1217 break; 1218 1219 case 0x00020005: 1220 brightness = 5; 1221 break; 1222 1223 case 0x00040003: 1224 brightness = 6; 1225 break; 1226 1227 case 0x00030004: 1228 default: 1229 brightness = 7; 1230 } 1231 1232 /* set lcd brightness GPIOs */ 1233 for (i = 0; i <= 2; i++) { 1234 qemu_set_irq(s->out[i], (brightness >> i) & 1); 1235 } 1236 } 1237 1238 static void musicpal_gpio_pin_event(void *opaque, int pin, int level) 1239 { 1240 musicpal_gpio_state *s = opaque; 1241 uint32_t mask = 1 << pin; 1242 uint32_t delta = level << pin; 1243 uint32_t old = s->in_state & mask; 1244 1245 s->in_state &= ~mask; 1246 s->in_state |= delta; 1247 1248 if ((old ^ delta) && 1249 ((level && (s->imr & mask)) || (!level && (s->ier & mask)))) { 1250 s->isr = mask; 1251 qemu_irq_raise(s->irq); 1252 } 1253 } 1254 1255 static uint64_t musicpal_gpio_read(void *opaque, hwaddr offset, 1256 unsigned size) 1257 { 1258 musicpal_gpio_state *s = opaque; 1259 1260 switch (offset) { 1261 case MP_GPIO_OE_HI: /* used for LCD brightness control */ 1262 return s->lcd_brightness & MP_OE_LCD_BRIGHTNESS; 1263 1264 case MP_GPIO_OUT_LO: 1265 return s->out_state & 0xFFFF; 1266 case MP_GPIO_OUT_HI: 1267 return s->out_state >> 16; 1268 1269 case MP_GPIO_IN_LO: 1270 return s->in_state & 0xFFFF; 1271 case MP_GPIO_IN_HI: 1272 return s->in_state >> 16; 1273 1274 case MP_GPIO_IER_LO: 1275 return s->ier & 0xFFFF; 1276 case MP_GPIO_IER_HI: 1277 return s->ier >> 16; 1278 1279 case MP_GPIO_IMR_LO: 1280 return s->imr & 0xFFFF; 1281 case MP_GPIO_IMR_HI: 1282 return s->imr >> 16; 1283 1284 case MP_GPIO_ISR_LO: 1285 return s->isr & 0xFFFF; 1286 case MP_GPIO_ISR_HI: 1287 return s->isr >> 16; 1288 1289 default: 1290 return 0; 1291 } 1292 } 1293 1294 static void musicpal_gpio_write(void *opaque, hwaddr offset, 1295 uint64_t value, unsigned size) 1296 { 1297 musicpal_gpio_state *s = opaque; 1298 switch (offset) { 1299 case MP_GPIO_OE_HI: /* used for LCD brightness control */ 1300 s->lcd_brightness = (s->lcd_brightness & MP_GPIO_LCD_BRIGHTNESS) | 1301 (value & MP_OE_LCD_BRIGHTNESS); 1302 musicpal_gpio_brightness_update(s); 1303 break; 1304 1305 case MP_GPIO_OUT_LO: 1306 s->out_state = (s->out_state & 0xFFFF0000) | (value & 0xFFFF); 1307 break; 1308 case MP_GPIO_OUT_HI: 1309 s->out_state = (s->out_state & 0xFFFF) | (value << 16); 1310 s->lcd_brightness = (s->lcd_brightness & 0xFFFF) | 1311 (s->out_state & MP_GPIO_LCD_BRIGHTNESS); 1312 musicpal_gpio_brightness_update(s); 1313 qemu_set_irq(s->out[3], (s->out_state >> MP_GPIO_I2C_DATA_BIT) & 1); 1314 qemu_set_irq(s->out[4], (s->out_state >> MP_GPIO_I2C_CLOCK_BIT) & 1); 1315 break; 1316 1317 case MP_GPIO_IER_LO: 1318 s->ier = (s->ier & 0xFFFF0000) | (value & 0xFFFF); 1319 break; 1320 case MP_GPIO_IER_HI: 1321 s->ier = (s->ier & 0xFFFF) | (value << 16); 1322 break; 1323 1324 case MP_GPIO_IMR_LO: 1325 s->imr = (s->imr & 0xFFFF0000) | (value & 0xFFFF); 1326 break; 1327 case MP_GPIO_IMR_HI: 1328 s->imr = (s->imr & 0xFFFF) | (value << 16); 1329 break; 1330 } 1331 } 1332 1333 static const MemoryRegionOps musicpal_gpio_ops = { 1334 .read = musicpal_gpio_read, 1335 .write = musicpal_gpio_write, 1336 .endianness = DEVICE_NATIVE_ENDIAN, 1337 }; 1338 1339 static void musicpal_gpio_reset(DeviceState *d) 1340 { 1341 musicpal_gpio_state *s = MUSICPAL_GPIO(d); 1342 1343 s->lcd_brightness = 0; 1344 s->out_state = 0; 1345 s->in_state = 0xffffffff; 1346 s->ier = 0; 1347 s->imr = 0; 1348 s->isr = 0; 1349 } 1350 1351 static int musicpal_gpio_init(SysBusDevice *sbd) 1352 { 1353 DeviceState *dev = DEVICE(sbd); 1354 musicpal_gpio_state *s = MUSICPAL_GPIO(dev); 1355 1356 sysbus_init_irq(sbd, &s->irq); 1357 1358 memory_region_init_io(&s->iomem, OBJECT(s), &musicpal_gpio_ops, s, 1359 "musicpal-gpio", MP_GPIO_SIZE); 1360 sysbus_init_mmio(sbd, &s->iomem); 1361 1362 qdev_init_gpio_out(dev, s->out, ARRAY_SIZE(s->out)); 1363 1364 qdev_init_gpio_in(dev, musicpal_gpio_pin_event, 32); 1365 1366 return 0; 1367 } 1368 1369 static const VMStateDescription musicpal_gpio_vmsd = { 1370 .name = "musicpal_gpio", 1371 .version_id = 1, 1372 .minimum_version_id = 1, 1373 .fields = (VMStateField[]) { 1374 VMSTATE_UINT32(lcd_brightness, musicpal_gpio_state), 1375 VMSTATE_UINT32(out_state, musicpal_gpio_state), 1376 VMSTATE_UINT32(in_state, musicpal_gpio_state), 1377 VMSTATE_UINT32(ier, musicpal_gpio_state), 1378 VMSTATE_UINT32(imr, musicpal_gpio_state), 1379 VMSTATE_UINT32(isr, musicpal_gpio_state), 1380 VMSTATE_END_OF_LIST() 1381 } 1382 }; 1383 1384 static void musicpal_gpio_class_init(ObjectClass *klass, void *data) 1385 { 1386 DeviceClass *dc = DEVICE_CLASS(klass); 1387 SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); 1388 1389 k->init = musicpal_gpio_init; 1390 dc->reset = musicpal_gpio_reset; 1391 dc->vmsd = &musicpal_gpio_vmsd; 1392 } 1393 1394 static const TypeInfo musicpal_gpio_info = { 1395 .name = TYPE_MUSICPAL_GPIO, 1396 .parent = TYPE_SYS_BUS_DEVICE, 1397 .instance_size = sizeof(musicpal_gpio_state), 1398 .class_init = musicpal_gpio_class_init, 1399 }; 1400 1401 /* Keyboard codes & masks */ 1402 #define KEY_RELEASED 0x80 1403 #define KEY_CODE 0x7f 1404 1405 #define KEYCODE_TAB 0x0f 1406 #define KEYCODE_ENTER 0x1c 1407 #define KEYCODE_F 0x21 1408 #define KEYCODE_M 0x32 1409 1410 #define KEYCODE_EXTENDED 0xe0 1411 #define KEYCODE_UP 0x48 1412 #define KEYCODE_DOWN 0x50 1413 #define KEYCODE_LEFT 0x4b 1414 #define KEYCODE_RIGHT 0x4d 1415 1416 #define MP_KEY_WHEEL_VOL (1 << 0) 1417 #define MP_KEY_WHEEL_VOL_INV (1 << 1) 1418 #define MP_KEY_WHEEL_NAV (1 << 2) 1419 #define MP_KEY_WHEEL_NAV_INV (1 << 3) 1420 #define MP_KEY_BTN_FAVORITS (1 << 4) 1421 #define MP_KEY_BTN_MENU (1 << 5) 1422 #define MP_KEY_BTN_VOLUME (1 << 6) 1423 #define MP_KEY_BTN_NAVIGATION (1 << 7) 1424 1425 #define TYPE_MUSICPAL_KEY "musicpal_key" 1426 #define MUSICPAL_KEY(obj) \ 1427 OBJECT_CHECK(musicpal_key_state, (obj), TYPE_MUSICPAL_KEY) 1428 1429 typedef struct musicpal_key_state { 1430 /*< private >*/ 1431 SysBusDevice parent_obj; 1432 /*< public >*/ 1433 1434 MemoryRegion iomem; 1435 uint32_t kbd_extended; 1436 uint32_t pressed_keys; 1437 qemu_irq out[8]; 1438 } musicpal_key_state; 1439 1440 static void musicpal_key_event(void *opaque, int keycode) 1441 { 1442 musicpal_key_state *s = opaque; 1443 uint32_t event = 0; 1444 int i; 1445 1446 if (keycode == KEYCODE_EXTENDED) { 1447 s->kbd_extended = 1; 1448 return; 1449 } 1450 1451 if (s->kbd_extended) { 1452 switch (keycode & KEY_CODE) { 1453 case KEYCODE_UP: 1454 event = MP_KEY_WHEEL_NAV | MP_KEY_WHEEL_NAV_INV; 1455 break; 1456 1457 case KEYCODE_DOWN: 1458 event = MP_KEY_WHEEL_NAV; 1459 break; 1460 1461 case KEYCODE_LEFT: 1462 event = MP_KEY_WHEEL_VOL | MP_KEY_WHEEL_VOL_INV; 1463 break; 1464 1465 case KEYCODE_RIGHT: 1466 event = MP_KEY_WHEEL_VOL; 1467 break; 1468 } 1469 } else { 1470 switch (keycode & KEY_CODE) { 1471 case KEYCODE_F: 1472 event = MP_KEY_BTN_FAVORITS; 1473 break; 1474 1475 case KEYCODE_TAB: 1476 event = MP_KEY_BTN_VOLUME; 1477 break; 1478 1479 case KEYCODE_ENTER: 1480 event = MP_KEY_BTN_NAVIGATION; 1481 break; 1482 1483 case KEYCODE_M: 1484 event = MP_KEY_BTN_MENU; 1485 break; 1486 } 1487 /* Do not repeat already pressed buttons */ 1488 if (!(keycode & KEY_RELEASED) && (s->pressed_keys & event)) { 1489 event = 0; 1490 } 1491 } 1492 1493 if (event) { 1494 /* Raise GPIO pin first if repeating a key */ 1495 if (!(keycode & KEY_RELEASED) && (s->pressed_keys & event)) { 1496 for (i = 0; i <= 7; i++) { 1497 if (event & (1 << i)) { 1498 qemu_set_irq(s->out[i], 1); 1499 } 1500 } 1501 } 1502 for (i = 0; i <= 7; i++) { 1503 if (event & (1 << i)) { 1504 qemu_set_irq(s->out[i], !!(keycode & KEY_RELEASED)); 1505 } 1506 } 1507 if (keycode & KEY_RELEASED) { 1508 s->pressed_keys &= ~event; 1509 } else { 1510 s->pressed_keys |= event; 1511 } 1512 } 1513 1514 s->kbd_extended = 0; 1515 } 1516 1517 static int musicpal_key_init(SysBusDevice *sbd) 1518 { 1519 DeviceState *dev = DEVICE(sbd); 1520 musicpal_key_state *s = MUSICPAL_KEY(dev); 1521 1522 memory_region_init(&s->iomem, OBJECT(s), "dummy", 0); 1523 sysbus_init_mmio(sbd, &s->iomem); 1524 1525 s->kbd_extended = 0; 1526 s->pressed_keys = 0; 1527 1528 qdev_init_gpio_out(dev, s->out, ARRAY_SIZE(s->out)); 1529 1530 qemu_add_kbd_event_handler(musicpal_key_event, s); 1531 1532 return 0; 1533 } 1534 1535 static const VMStateDescription musicpal_key_vmsd = { 1536 .name = "musicpal_key", 1537 .version_id = 1, 1538 .minimum_version_id = 1, 1539 .fields = (VMStateField[]) { 1540 VMSTATE_UINT32(kbd_extended, musicpal_key_state), 1541 VMSTATE_UINT32(pressed_keys, musicpal_key_state), 1542 VMSTATE_END_OF_LIST() 1543 } 1544 }; 1545 1546 static void musicpal_key_class_init(ObjectClass *klass, void *data) 1547 { 1548 DeviceClass *dc = DEVICE_CLASS(klass); 1549 SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); 1550 1551 k->init = musicpal_key_init; 1552 dc->vmsd = &musicpal_key_vmsd; 1553 } 1554 1555 static const TypeInfo musicpal_key_info = { 1556 .name = TYPE_MUSICPAL_KEY, 1557 .parent = TYPE_SYS_BUS_DEVICE, 1558 .instance_size = sizeof(musicpal_key_state), 1559 .class_init = musicpal_key_class_init, 1560 }; 1561 1562 static struct arm_boot_info musicpal_binfo = { 1563 .loader_start = 0x0, 1564 .board_id = 0x20e, 1565 }; 1566 1567 static void musicpal_init(MachineState *machine) 1568 { 1569 const char *cpu_model = machine->cpu_model; 1570 const char *kernel_filename = machine->kernel_filename; 1571 const char *kernel_cmdline = machine->kernel_cmdline; 1572 const char *initrd_filename = machine->initrd_filename; 1573 ARMCPU *cpu; 1574 qemu_irq pic[32]; 1575 DeviceState *dev; 1576 DeviceState *i2c_dev; 1577 DeviceState *lcd_dev; 1578 DeviceState *key_dev; 1579 DeviceState *wm8750_dev; 1580 SysBusDevice *s; 1581 I2CBus *i2c; 1582 int i; 1583 unsigned long flash_size; 1584 DriveInfo *dinfo; 1585 MemoryRegion *address_space_mem = get_system_memory(); 1586 MemoryRegion *ram = g_new(MemoryRegion, 1); 1587 MemoryRegion *sram = g_new(MemoryRegion, 1); 1588 1589 if (!cpu_model) { 1590 cpu_model = "arm926"; 1591 } 1592 cpu = cpu_arm_init(cpu_model); 1593 if (!cpu) { 1594 fprintf(stderr, "Unable to find CPU definition\n"); 1595 exit(1); 1596 } 1597 1598 /* For now we use a fixed - the original - RAM size */ 1599 memory_region_allocate_system_memory(ram, NULL, "musicpal.ram", 1600 MP_RAM_DEFAULT_SIZE); 1601 memory_region_add_subregion(address_space_mem, 0, ram); 1602 1603 memory_region_init_ram(sram, NULL, "musicpal.sram", MP_SRAM_SIZE, 1604 &error_fatal); 1605 vmstate_register_ram_global(sram); 1606 memory_region_add_subregion(address_space_mem, MP_SRAM_BASE, sram); 1607 1608 dev = sysbus_create_simple(TYPE_MV88W8618_PIC, MP_PIC_BASE, 1609 qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ)); 1610 for (i = 0; i < 32; i++) { 1611 pic[i] = qdev_get_gpio_in(dev, i); 1612 } 1613 sysbus_create_varargs(TYPE_MV88W8618_PIT, MP_PIT_BASE, pic[MP_TIMER1_IRQ], 1614 pic[MP_TIMER2_IRQ], pic[MP_TIMER3_IRQ], 1615 pic[MP_TIMER4_IRQ], NULL); 1616 1617 if (serial_hds[0]) { 1618 serial_mm_init(address_space_mem, MP_UART1_BASE, 2, pic[MP_UART1_IRQ], 1619 1825000, serial_hds[0], DEVICE_NATIVE_ENDIAN); 1620 } 1621 if (serial_hds[1]) { 1622 serial_mm_init(address_space_mem, MP_UART2_BASE, 2, pic[MP_UART2_IRQ], 1623 1825000, serial_hds[1], DEVICE_NATIVE_ENDIAN); 1624 } 1625 1626 /* Register flash */ 1627 dinfo = drive_get(IF_PFLASH, 0, 0); 1628 if (dinfo) { 1629 BlockBackend *blk = blk_by_legacy_dinfo(dinfo); 1630 1631 flash_size = blk_getlength(blk); 1632 if (flash_size != 8*1024*1024 && flash_size != 16*1024*1024 && 1633 flash_size != 32*1024*1024) { 1634 fprintf(stderr, "Invalid flash image size\n"); 1635 exit(1); 1636 } 1637 1638 /* 1639 * The original U-Boot accesses the flash at 0xFE000000 instead of 1640 * 0xFF800000 (if there is 8 MB flash). So remap flash access if the 1641 * image is smaller than 32 MB. 1642 */ 1643 #ifdef TARGET_WORDS_BIGENDIAN 1644 pflash_cfi02_register(0x100000000ULL-MP_FLASH_SIZE_MAX, NULL, 1645 "musicpal.flash", flash_size, 1646 blk, 0x10000, (flash_size + 0xffff) >> 16, 1647 MP_FLASH_SIZE_MAX / flash_size, 1648 2, 0x00BF, 0x236D, 0x0000, 0x0000, 1649 0x5555, 0x2AAA, 1); 1650 #else 1651 pflash_cfi02_register(0x100000000ULL-MP_FLASH_SIZE_MAX, NULL, 1652 "musicpal.flash", flash_size, 1653 blk, 0x10000, (flash_size + 0xffff) >> 16, 1654 MP_FLASH_SIZE_MAX / flash_size, 1655 2, 0x00BF, 0x236D, 0x0000, 0x0000, 1656 0x5555, 0x2AAA, 0); 1657 #endif 1658 1659 } 1660 sysbus_create_simple(TYPE_MV88W8618_FLASHCFG, MP_FLASHCFG_BASE, NULL); 1661 1662 qemu_check_nic_model(&nd_table[0], "mv88w8618"); 1663 dev = qdev_create(NULL, TYPE_MV88W8618_ETH); 1664 qdev_set_nic_properties(dev, &nd_table[0]); 1665 qdev_init_nofail(dev); 1666 sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, MP_ETH_BASE); 1667 sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[MP_ETH_IRQ]); 1668 1669 sysbus_create_simple("mv88w8618_wlan", MP_WLAN_BASE, NULL); 1670 1671 sysbus_create_simple(TYPE_MUSICPAL_MISC, MP_MISC_BASE, NULL); 1672 1673 dev = sysbus_create_simple(TYPE_MUSICPAL_GPIO, MP_GPIO_BASE, 1674 pic[MP_GPIO_IRQ]); 1675 i2c_dev = sysbus_create_simple("gpio_i2c", -1, NULL); 1676 i2c = (I2CBus *)qdev_get_child_bus(i2c_dev, "i2c"); 1677 1678 lcd_dev = sysbus_create_simple(TYPE_MUSICPAL_LCD, MP_LCD_BASE, NULL); 1679 key_dev = sysbus_create_simple(TYPE_MUSICPAL_KEY, -1, NULL); 1680 1681 /* I2C read data */ 1682 qdev_connect_gpio_out(i2c_dev, 0, 1683 qdev_get_gpio_in(dev, MP_GPIO_I2C_DATA_BIT)); 1684 /* I2C data */ 1685 qdev_connect_gpio_out(dev, 3, qdev_get_gpio_in(i2c_dev, 0)); 1686 /* I2C clock */ 1687 qdev_connect_gpio_out(dev, 4, qdev_get_gpio_in(i2c_dev, 1)); 1688 1689 for (i = 0; i < 3; i++) { 1690 qdev_connect_gpio_out(dev, i, qdev_get_gpio_in(lcd_dev, i)); 1691 } 1692 for (i = 0; i < 4; i++) { 1693 qdev_connect_gpio_out(key_dev, i, qdev_get_gpio_in(dev, i + 8)); 1694 } 1695 for (i = 4; i < 8; i++) { 1696 qdev_connect_gpio_out(key_dev, i, qdev_get_gpio_in(dev, i + 15)); 1697 } 1698 1699 wm8750_dev = i2c_create_slave(i2c, "wm8750", MP_WM_ADDR); 1700 dev = qdev_create(NULL, "mv88w8618_audio"); 1701 s = SYS_BUS_DEVICE(dev); 1702 qdev_prop_set_ptr(dev, "wm8750", wm8750_dev); 1703 qdev_init_nofail(dev); 1704 sysbus_mmio_map(s, 0, MP_AUDIO_BASE); 1705 sysbus_connect_irq(s, 0, pic[MP_AUDIO_IRQ]); 1706 1707 musicpal_binfo.ram_size = MP_RAM_DEFAULT_SIZE; 1708 musicpal_binfo.kernel_filename = kernel_filename; 1709 musicpal_binfo.kernel_cmdline = kernel_cmdline; 1710 musicpal_binfo.initrd_filename = initrd_filename; 1711 arm_load_kernel(cpu, &musicpal_binfo); 1712 } 1713 1714 static void musicpal_machine_init(MachineClass *mc) 1715 { 1716 mc->desc = "Marvell 88w8618 / MusicPal (ARM926EJ-S)"; 1717 mc->init = musicpal_init; 1718 } 1719 1720 DEFINE_MACHINE("musicpal", musicpal_machine_init) 1721 1722 static void mv88w8618_wlan_class_init(ObjectClass *klass, void *data) 1723 { 1724 SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); 1725 1726 sdc->init = mv88w8618_wlan_init; 1727 } 1728 1729 static const TypeInfo mv88w8618_wlan_info = { 1730 .name = "mv88w8618_wlan", 1731 .parent = TYPE_SYS_BUS_DEVICE, 1732 .instance_size = sizeof(SysBusDevice), 1733 .class_init = mv88w8618_wlan_class_init, 1734 }; 1735 1736 static void musicpal_register_types(void) 1737 { 1738 type_register_static(&mv88w8618_pic_info); 1739 type_register_static(&mv88w8618_pit_info); 1740 type_register_static(&mv88w8618_flashcfg_info); 1741 type_register_static(&mv88w8618_eth_info); 1742 type_register_static(&mv88w8618_wlan_info); 1743 type_register_static(&musicpal_lcd_info); 1744 type_register_static(&musicpal_gpio_info); 1745 type_register_static(&musicpal_key_info); 1746 type_register_static(&musicpal_misc_info); 1747 } 1748 1749 type_init(musicpal_register_types) 1750