musicpal.c 48.0 KB
Newer Older
1 2 3 4 5
/*
 * Marvell MV88W8618 / Freecom MusicPal emulation.
 *
 * Copyright (c) 2008 Jan Kiszka
 *
M
Matthew Fernandez 已提交
6
 * This code is licensed under the GNU GPL v2.
7 8 9
 *
 * Contributions after 2012-01-13 are licensed under the terms of the
 * GNU GPL, version 2 or (at your option) any later version.
10 11
 */

P
Peter Maydell 已提交
12
#include "qemu/osdep.h"
13
#include "qapi/error.h"
14 15
#include "qemu-common.h"
#include "cpu.h"
16
#include "hw/sysbus.h"
17 18
#include "hw/arm/arm.h"
#include "hw/devices.h"
P
Paolo Bonzini 已提交
19
#include "net/net.h"
20
#include "sysemu/sysemu.h"
21
#include "hw/boards.h"
P
Paolo Bonzini 已提交
22
#include "hw/char/serial.h"
23
#include "qemu/timer.h"
24
#include "hw/ptimer.h"
P
Paolo Bonzini 已提交
25
#include "hw/block/flash.h"
26
#include "ui/console.h"
P
Paolo Bonzini 已提交
27
#include "hw/i2c/i2c.h"
28
#include "sysemu/block-backend.h"
29
#include "exec/address-spaces.h"
30
#include "ui/pixel_ops.h"
31

32 33 34
#define MP_MISC_BASE            0x80002000
#define MP_MISC_SIZE            0x00001000

35 36 37
#define MP_ETH_BASE             0x80008000
#define MP_ETH_SIZE             0x00001000

38 39 40
#define MP_WLAN_BASE            0x8000C000
#define MP_WLAN_SIZE            0x00000800

41 42 43
#define MP_UART1_BASE           0x8000C840
#define MP_UART2_BASE           0x8000C940

44 45 46
#define MP_GPIO_BASE            0x8000D000
#define MP_GPIO_SIZE            0x00001000

47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
#define MP_FLASHCFG_BASE        0x90006000
#define MP_FLASHCFG_SIZE        0x00001000

#define MP_AUDIO_BASE           0x90007000

#define MP_PIC_BASE             0x90008000
#define MP_PIC_SIZE             0x00001000

#define MP_PIT_BASE             0x90009000
#define MP_PIT_SIZE             0x00001000

#define MP_LCD_BASE             0x9000c000
#define MP_LCD_SIZE             0x00001000

#define MP_SRAM_BASE            0xC0000000
#define MP_SRAM_SIZE            0x00020000

#define MP_RAM_DEFAULT_SIZE     32*1024*1024
#define MP_FLASH_SIZE_MAX       32*1024*1024

#define MP_TIMER1_IRQ           4
P
Paul Brook 已提交
68 69
#define MP_TIMER2_IRQ           5
#define MP_TIMER3_IRQ           6
70 71 72 73 74 75 76 77 78 79
#define MP_TIMER4_IRQ           7
#define MP_EHCI_IRQ             8
#define MP_ETH_IRQ              9
#define MP_UART1_IRQ            11
#define MP_UART2_IRQ            11
#define MP_GPIO_IRQ             12
#define MP_RTC_IRQ              28
#define MP_AUDIO_IRQ            30

/* Wolfson 8750 I2C address */
J
Jan Kiszka 已提交
80
#define MP_WM_ADDR              0x1A
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115

/* Ethernet register offsets */
#define MP_ETH_SMIR             0x010
#define MP_ETH_PCXR             0x408
#define MP_ETH_SDCMR            0x448
#define MP_ETH_ICR              0x450
#define MP_ETH_IMR              0x458
#define MP_ETH_FRDP0            0x480
#define MP_ETH_FRDP1            0x484
#define MP_ETH_FRDP2            0x488
#define MP_ETH_FRDP3            0x48C
#define MP_ETH_CRDP0            0x4A0
#define MP_ETH_CRDP1            0x4A4
#define MP_ETH_CRDP2            0x4A8
#define MP_ETH_CRDP3            0x4AC
#define MP_ETH_CTDP0            0x4E0
#define MP_ETH_CTDP1            0x4E4

/* MII PHY access */
#define MP_ETH_SMIR_DATA        0x0000FFFF
#define MP_ETH_SMIR_ADDR        0x03FF0000
#define MP_ETH_SMIR_OPCODE      (1 << 26) /* Read value */
#define MP_ETH_SMIR_RDVALID     (1 << 27)

/* PHY registers */
#define MP_ETH_PHY1_BMSR        0x00210000
#define MP_ETH_PHY1_PHYSID1     0x00410000
#define MP_ETH_PHY1_PHYSID2     0x00610000

#define MP_PHY_BMSR_LINK        0x0004
#define MP_PHY_BMSR_AUTONEG     0x0008

#define MP_PHY_88E3015          0x01410E20

/* TX descriptor status */
116
#define MP_ETH_TX_OWN           (1U << 31)
117 118

/* RX descriptor status */
119
#define MP_ETH_RX_OWN           (1U << 31)
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149

/* Interrupt cause/mask bits */
#define MP_ETH_IRQ_RX_BIT       0
#define MP_ETH_IRQ_RX           (1 << MP_ETH_IRQ_RX_BIT)
#define MP_ETH_IRQ_TXHI_BIT     2
#define MP_ETH_IRQ_TXLO_BIT     3

/* Port config bits */
#define MP_ETH_PCXR_2BSM_BIT    28 /* 2-byte incoming suffix */

/* SDMA command bits */
#define MP_ETH_CMD_TXHI         (1 << 23)
#define MP_ETH_CMD_TXLO         (1 << 22)

typedef struct mv88w8618_tx_desc {
    uint32_t cmdstat;
    uint16_t res;
    uint16_t bytes;
    uint32_t buffer;
    uint32_t next;
} mv88w8618_tx_desc;

typedef struct mv88w8618_rx_desc {
    uint32_t cmdstat;
    uint16_t bytes;
    uint16_t buffer_size;
    uint32_t buffer;
    uint32_t next;
} mv88w8618_rx_desc;

150 151 152 153
#define TYPE_MV88W8618_ETH "mv88w8618_eth"
#define MV88W8618_ETH(obj) \
    OBJECT_CHECK(mv88w8618_eth_state, (obj), TYPE_MV88W8618_ETH)

154
typedef struct mv88w8618_eth_state {
155 156 157 158
    /*< private >*/
    SysBusDevice parent_obj;
    /*< public >*/

A
Avi Kivity 已提交
159
    MemoryRegion iomem;
160 161 162 163
    qemu_irq irq;
    uint32_t smir;
    uint32_t icr;
    uint32_t imr;
164
    int mmio_index;
J
Jan Kiszka 已提交
165
    uint32_t vlan_header;
P
pbrook 已提交
166 167 168 169
    uint32_t tx_queue[2];
    uint32_t rx_queue[4];
    uint32_t frx_queue[4];
    uint32_t cur_rx[4];
170
    NICState *nic;
171
    NICConf conf;
172 173
} mv88w8618_eth_state;

P
pbrook 已提交
174 175 176 177 178 179 180
static void eth_rx_desc_put(uint32_t addr, mv88w8618_rx_desc *desc)
{
    cpu_to_le32s(&desc->cmdstat);
    cpu_to_le16s(&desc->bytes);
    cpu_to_le16s(&desc->buffer_size);
    cpu_to_le32s(&desc->buffer);
    cpu_to_le32s(&desc->next);
S
Stefan Weil 已提交
181
    cpu_physical_memory_write(addr, desc, sizeof(*desc));
P
pbrook 已提交
182 183 184 185
}

static void eth_rx_desc_get(uint32_t addr, mv88w8618_rx_desc *desc)
{
S
Stefan Weil 已提交
186
    cpu_physical_memory_read(addr, desc, sizeof(*desc));
P
pbrook 已提交
187 188 189 190 191 192 193
    le32_to_cpus(&desc->cmdstat);
    le16_to_cpus(&desc->bytes);
    le16_to_cpus(&desc->buffer_size);
    le32_to_cpus(&desc->buffer);
    le32_to_cpus(&desc->next);
}

194
static ssize_t eth_receive(NetClientState *nc, const uint8_t *buf, size_t size)
195
{
J
Jason Wang 已提交
196
    mv88w8618_eth_state *s = qemu_get_nic_opaque(nc);
P
pbrook 已提交
197 198
    uint32_t desc_addr;
    mv88w8618_rx_desc desc;
199 200 201
    int i;

    for (i = 0; i < 4; i++) {
P
pbrook 已提交
202
        desc_addr = s->cur_rx[i];
J
Jan Kiszka 已提交
203
        if (!desc_addr) {
204
            continue;
J
Jan Kiszka 已提交
205
        }
206
        do {
P
pbrook 已提交
207 208 209 210 211 212 213
            eth_rx_desc_get(desc_addr, &desc);
            if ((desc.cmdstat & MP_ETH_RX_OWN) && desc.buffer_size >= size) {
                cpu_physical_memory_write(desc.buffer + s->vlan_header,
                                          buf, size);
                desc.bytes = size + s->vlan_header;
                desc.cmdstat &= ~MP_ETH_RX_OWN;
                s->cur_rx[i] = desc.next;
214 215

                s->icr |= MP_ETH_IRQ_RX;
J
Jan Kiszka 已提交
216
                if (s->icr & s->imr) {
217
                    qemu_irq_raise(s->irq);
J
Jan Kiszka 已提交
218
                }
P
pbrook 已提交
219
                eth_rx_desc_put(desc_addr, &desc);
220
                return size;
221
            }
P
pbrook 已提交
222 223
            desc_addr = desc.next;
        } while (desc_addr != s->rx_queue[i]);
224
    }
225
    return size;
226 227
}

P
pbrook 已提交
228 229 230 231 232 233 234
static void eth_tx_desc_put(uint32_t addr, mv88w8618_tx_desc *desc)
{
    cpu_to_le32s(&desc->cmdstat);
    cpu_to_le16s(&desc->res);
    cpu_to_le16s(&desc->bytes);
    cpu_to_le32s(&desc->buffer);
    cpu_to_le32s(&desc->next);
S
Stefan Weil 已提交
235
    cpu_physical_memory_write(addr, desc, sizeof(*desc));
P
pbrook 已提交
236 237 238 239
}

static void eth_tx_desc_get(uint32_t addr, mv88w8618_tx_desc *desc)
{
S
Stefan Weil 已提交
240
    cpu_physical_memory_read(addr, desc, sizeof(*desc));
P
pbrook 已提交
241 242 243 244 245 246 247
    le32_to_cpus(&desc->cmdstat);
    le16_to_cpus(&desc->res);
    le16_to_cpus(&desc->bytes);
    le32_to_cpus(&desc->buffer);
    le32_to_cpus(&desc->next);
}

248 249
static void eth_send(mv88w8618_eth_state *s, int queue_index)
{
P
pbrook 已提交
250 251
    uint32_t desc_addr = s->tx_queue[queue_index];
    mv88w8618_tx_desc desc;
252
    uint32_t next_desc;
P
pbrook 已提交
253 254 255
    uint8_t buf[2048];
    int len;

256
    do {
P
pbrook 已提交
257
        eth_tx_desc_get(desc_addr, &desc);
258
        next_desc = desc.next;
P
pbrook 已提交
259 260 261 262
        if (desc.cmdstat & MP_ETH_TX_OWN) {
            len = desc.bytes;
            if (len < 2048) {
                cpu_physical_memory_read(desc.buffer, buf, len);
J
Jason Wang 已提交
263
                qemu_send_packet(qemu_get_queue(s->nic), buf, len);
P
pbrook 已提交
264 265
            }
            desc.cmdstat &= ~MP_ETH_TX_OWN;
266
            s->icr |= 1 << (MP_ETH_IRQ_TXLO_BIT - queue_index);
P
pbrook 已提交
267
            eth_tx_desc_put(desc_addr, &desc);
268
        }
269
        desc_addr = next_desc;
P
pbrook 已提交
270
    } while (desc_addr != s->tx_queue[queue_index]);
271 272
}

A
Avi Kivity 已提交
273
static uint64_t mv88w8618_eth_read(void *opaque, hwaddr offset,
A
Avi Kivity 已提交
274
                                   unsigned size)
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
{
    mv88w8618_eth_state *s = opaque;

    switch (offset) {
    case MP_ETH_SMIR:
        if (s->smir & MP_ETH_SMIR_OPCODE) {
            switch (s->smir & MP_ETH_SMIR_ADDR) {
            case MP_ETH_PHY1_BMSR:
                return MP_PHY_BMSR_LINK | MP_PHY_BMSR_AUTONEG |
                       MP_ETH_SMIR_RDVALID;
            case MP_ETH_PHY1_PHYSID1:
                return (MP_PHY_88E3015 >> 16) | MP_ETH_SMIR_RDVALID;
            case MP_ETH_PHY1_PHYSID2:
                return (MP_PHY_88E3015 & 0xFFFF) | MP_ETH_SMIR_RDVALID;
            default:
                return MP_ETH_SMIR_RDVALID;
            }
        }
        return 0;

    case MP_ETH_ICR:
        return s->icr;

    case MP_ETH_IMR:
        return s->imr;

    case MP_ETH_FRDP0 ... MP_ETH_FRDP3:
P
pbrook 已提交
302
        return s->frx_queue[(offset - MP_ETH_FRDP0)/4];
303 304

    case MP_ETH_CRDP0 ... MP_ETH_CRDP3:
P
pbrook 已提交
305
        return s->rx_queue[(offset - MP_ETH_CRDP0)/4];
306

307
    case MP_ETH_CTDP0 ... MP_ETH_CTDP1:
P
pbrook 已提交
308
        return s->tx_queue[(offset - MP_ETH_CTDP0)/4];
309 310 311 312 313 314

    default:
        return 0;
    }
}

A
Avi Kivity 已提交
315
static void mv88w8618_eth_write(void *opaque, hwaddr offset,
A
Avi Kivity 已提交
316
                                uint64_t value, unsigned size)
317 318 319 320 321 322 323 324 325 326 327 328 329
{
    mv88w8618_eth_state *s = opaque;

    switch (offset) {
    case MP_ETH_SMIR:
        s->smir = value;
        break;

    case MP_ETH_PCXR:
        s->vlan_header = ((value >> MP_ETH_PCXR_2BSM_BIT) & 1) * 2;
        break;

    case MP_ETH_SDCMR:
J
Jan Kiszka 已提交
330
        if (value & MP_ETH_CMD_TXHI) {
331
            eth_send(s, 1);
J
Jan Kiszka 已提交
332 333
        }
        if (value & MP_ETH_CMD_TXLO) {
334
            eth_send(s, 0);
J
Jan Kiszka 已提交
335 336
        }
        if (value & (MP_ETH_CMD_TXHI | MP_ETH_CMD_TXLO) && s->icr & s->imr) {
337
            qemu_irq_raise(s->irq);
J
Jan Kiszka 已提交
338
        }
339 340 341 342 343 344 345 346
        break;

    case MP_ETH_ICR:
        s->icr &= value;
        break;

    case MP_ETH_IMR:
        s->imr = value;
J
Jan Kiszka 已提交
347
        if (s->icr & s->imr) {
348
            qemu_irq_raise(s->irq);
J
Jan Kiszka 已提交
349
        }
350 351 352
        break;

    case MP_ETH_FRDP0 ... MP_ETH_FRDP3:
P
pbrook 已提交
353
        s->frx_queue[(offset - MP_ETH_FRDP0)/4] = value;
354 355 356 357
        break;

    case MP_ETH_CRDP0 ... MP_ETH_CRDP3:
        s->rx_queue[(offset - MP_ETH_CRDP0)/4] =
P
pbrook 已提交
358
            s->cur_rx[(offset - MP_ETH_CRDP0)/4] = value;
359 360
        break;

361
    case MP_ETH_CTDP0 ... MP_ETH_CTDP1:
P
pbrook 已提交
362
        s->tx_queue[(offset - MP_ETH_CTDP0)/4] = value;
363 364 365 366
        break;
    }
}

A
Avi Kivity 已提交
367 368 369 370
static const MemoryRegionOps mv88w8618_eth_ops = {
    .read = mv88w8618_eth_read,
    .write = mv88w8618_eth_write,
    .endianness = DEVICE_NATIVE_ENDIAN,
371 372
};

373
static void eth_cleanup(NetClientState *nc)
374
{
J
Jason Wang 已提交
375
    mv88w8618_eth_state *s = qemu_get_nic_opaque(nc);
376

377
    s->nic = NULL;
378 379
}

380
static NetClientInfo net_mv88w8618_info = {
381
    .type = NET_CLIENT_DRIVER_NIC,
382 383 384 385 386
    .size = sizeof(NICState),
    .receive = eth_receive,
    .cleanup = eth_cleanup,
};

X
xiaoqiang zhao 已提交
387
static void mv88w8618_eth_init(Object *obj)
388
{
X
xiaoqiang zhao 已提交
389
    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
390 391
    DeviceState *dev = DEVICE(sbd);
    mv88w8618_eth_state *s = MV88W8618_ETH(dev);
392

393
    sysbus_init_irq(sbd, &s->irq);
X
xiaoqiang zhao 已提交
394
    memory_region_init_io(&s->iomem, obj, &mv88w8618_eth_ops, s,
395
                          "mv88w8618-eth", MP_ETH_SIZE);
396
    sysbus_init_mmio(sbd, &s->iomem);
X
xiaoqiang zhao 已提交
397 398 399 400 401 402 403 404
}

static void mv88w8618_eth_realize(DeviceState *dev, Error **errp)
{
    mv88w8618_eth_state *s = MV88W8618_ETH(dev);

    s->nic = qemu_new_nic(&net_mv88w8618_info, &s->conf,
                          object_get_typename(OBJECT(dev)), dev->id, s);
405 406
}

J
Jan Kiszka 已提交
407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423
static const VMStateDescription mv88w8618_eth_vmsd = {
    .name = "mv88w8618_eth",
    .version_id = 1,
    .minimum_version_id = 1,
    .fields = (VMStateField[]) {
        VMSTATE_UINT32(smir, mv88w8618_eth_state),
        VMSTATE_UINT32(icr, mv88w8618_eth_state),
        VMSTATE_UINT32(imr, mv88w8618_eth_state),
        VMSTATE_UINT32(vlan_header, mv88w8618_eth_state),
        VMSTATE_UINT32_ARRAY(tx_queue, mv88w8618_eth_state, 2),
        VMSTATE_UINT32_ARRAY(rx_queue, mv88w8618_eth_state, 4),
        VMSTATE_UINT32_ARRAY(frx_queue, mv88w8618_eth_state, 4),
        VMSTATE_UINT32_ARRAY(cur_rx, mv88w8618_eth_state, 4),
        VMSTATE_END_OF_LIST()
    }
};

424 425 426 427 428 429 430
static Property mv88w8618_eth_properties[] = {
    DEFINE_NIC_PROPERTIES(mv88w8618_eth_state, conf),
    DEFINE_PROP_END_OF_LIST(),
};

static void mv88w8618_eth_class_init(ObjectClass *klass, void *data)
{
431
    DeviceClass *dc = DEVICE_CLASS(klass);
432

433 434
    dc->vmsd = &mv88w8618_eth_vmsd;
    dc->props = mv88w8618_eth_properties;
X
xiaoqiang zhao 已提交
435
    dc->realize = mv88w8618_eth_realize;
436 437
}

438
static const TypeInfo mv88w8618_eth_info = {
439
    .name          = TYPE_MV88W8618_ETH,
440 441
    .parent        = TYPE_SYS_BUS_DEVICE,
    .instance_size = sizeof(mv88w8618_eth_state),
X
xiaoqiang zhao 已提交
442
    .instance_init = mv88w8618_eth_init,
443
    .class_init    = mv88w8618_eth_class_init,
J
Jan Kiszka 已提交
444 445
};

446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464
/* LCD register offsets */
#define MP_LCD_IRQCTRL          0x180
#define MP_LCD_IRQSTAT          0x184
#define MP_LCD_SPICTRL          0x1ac
#define MP_LCD_INST             0x1bc
#define MP_LCD_DATA             0x1c0

/* Mode magics */
#define MP_LCD_SPI_DATA         0x00100011
#define MP_LCD_SPI_CMD          0x00104011
#define MP_LCD_SPI_INVALID      0x00000000

/* Commmands */
#define MP_LCD_INST_SETPAGE0    0xB0
/* ... */
#define MP_LCD_INST_SETPAGE7    0xB7

#define MP_LCD_TEXTCOLOR        0xe0e0ff /* RRGGBB */

465 466 467 468
#define TYPE_MUSICPAL_LCD "musicpal_lcd"
#define MUSICPAL_LCD(obj) \
    OBJECT_CHECK(musicpal_lcd_state, (obj), TYPE_MUSICPAL_LCD)

469
typedef struct musicpal_lcd_state {
470 471 472 473
    /*< private >*/
    SysBusDevice parent_obj;
    /*< public >*/

A
Avi Kivity 已提交
474
    MemoryRegion iomem;
475
    uint32_t brightness;
476 477
    uint32_t mode;
    uint32_t irqctrl;
J
Jan Kiszka 已提交
478 479
    uint32_t page;
    uint32_t page_off;
480
    QemuConsole *con;
481 482 483
    uint8_t video_ram[128*64/8];
} musicpal_lcd_state;

484
static uint8_t scale_lcd_color(musicpal_lcd_state *s, uint8_t col)
485
{
486 487 488 489
    switch (s->brightness) {
    case 7:
        return col;
    case 0:
490 491
        return 0;
    default:
492
        return (col * s->brightness) / 7;
493 494 495
    }
}

496 497 498 499 500
#define SET_LCD_PIXEL(depth, type) \
static inline void glue(set_lcd_pixel, depth) \
        (musicpal_lcd_state *s, int x, int y, type col) \
{ \
    int dx, dy; \
501 502
    DisplaySurface *surface = qemu_console_surface(s->con); \
    type *pixel = &((type *) surface_data(surface))[(y * 128 * 3 + x) * 3]; \
503 504 505 506
\
    for (dy = 0; dy < 3; dy++, pixel += 127 * 3) \
        for (dx = 0; dx < 3; dx++, pixel++) \
            *pixel = col; \
507
}
508 509 510 511
SET_LCD_PIXEL(8, uint8_t)
SET_LCD_PIXEL(16, uint16_t)
SET_LCD_PIXEL(32, uint32_t)

512 513 514
static void lcd_refresh(void *opaque)
{
    musicpal_lcd_state *s = opaque;
515
    DisplaySurface *surface = qemu_console_surface(s->con);
516
    int x, y, col;
517

518
    switch (surface_bits_per_pixel(surface)) {
519 520 521 522
    case 0:
        return;
#define LCD_REFRESH(depth, func) \
    case depth: \
523 524 525
        col = func(scale_lcd_color(s, (MP_LCD_TEXTCOLOR >> 16) & 0xff), \
                   scale_lcd_color(s, (MP_LCD_TEXTCOLOR >> 8) & 0xff), \
                   scale_lcd_color(s, MP_LCD_TEXTCOLOR & 0xff)); \
J
Jan Kiszka 已提交
526 527 528
        for (x = 0; x < 128; x++) { \
            for (y = 0; y < 64; y++) { \
                if (s->video_ram[x + (y/8)*128] & (1 << (y % 8))) { \
529
                    glue(set_lcd_pixel, depth)(s, x, y, col); \
J
Jan Kiszka 已提交
530
                } else { \
531
                    glue(set_lcd_pixel, depth)(s, x, y, 0); \
J
Jan Kiszka 已提交
532 533 534
                } \
            } \
        } \
535 536 537
        break;
    LCD_REFRESH(8, rgb_to_pixel8)
    LCD_REFRESH(16, rgb_to_pixel16)
538
    LCD_REFRESH(32, (is_surface_bgr(surface) ?
539
                     rgb_to_pixel32bgr : rgb_to_pixel32))
540
    default:
P
Paul Brook 已提交
541
        hw_error("unsupported colour depth %i\n",
542
                 surface_bits_per_pixel(surface));
543
    }
544

545
    dpy_gfx_update(s->con, 0, 0, 128*3, 64*3);
546 547
}

548 549 550 551
static void lcd_invalidate(void *opaque)
{
}

552
static void musicpal_lcd_gpio_brightness_in(void *opaque, int irq, int level)
553
{
J
Jan Kiszka 已提交
554
    musicpal_lcd_state *s = opaque;
555 556 557 558
    s->brightness &= ~(1 << irq);
    s->brightness |= level << irq;
}

A
Avi Kivity 已提交
559
static uint64_t musicpal_lcd_read(void *opaque, hwaddr offset,
A
Avi Kivity 已提交
560
                                  unsigned size)
561 562 563 564 565 566 567 568 569 570 571 572
{
    musicpal_lcd_state *s = opaque;

    switch (offset) {
    case MP_LCD_IRQCTRL:
        return s->irqctrl;

    default:
        return 0;
    }
}

A
Avi Kivity 已提交
573
static void musicpal_lcd_write(void *opaque, hwaddr offset,
A
Avi Kivity 已提交
574
                               uint64_t value, unsigned size)
575 576 577 578 579 580 581 582 583
{
    musicpal_lcd_state *s = opaque;

    switch (offset) {
    case MP_LCD_IRQCTRL:
        s->irqctrl = value;
        break;

    case MP_LCD_SPICTRL:
J
Jan Kiszka 已提交
584
        if (value == MP_LCD_SPI_DATA || value == MP_LCD_SPI_CMD) {
585
            s->mode = value;
J
Jan Kiszka 已提交
586
        } else {
587
            s->mode = MP_LCD_SPI_INVALID;
J
Jan Kiszka 已提交
588
        }
589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612
        break;

    case MP_LCD_INST:
        if (value >= MP_LCD_INST_SETPAGE0 && value <= MP_LCD_INST_SETPAGE7) {
            s->page = value - MP_LCD_INST_SETPAGE0;
            s->page_off = 0;
        }
        break;

    case MP_LCD_DATA:
        if (s->mode == MP_LCD_SPI_CMD) {
            if (value >= MP_LCD_INST_SETPAGE0 &&
                value <= MP_LCD_INST_SETPAGE7) {
                s->page = value - MP_LCD_INST_SETPAGE0;
                s->page_off = 0;
            }
        } else if (s->mode == MP_LCD_SPI_DATA) {
            s->video_ram[s->page*128 + s->page_off] = value;
            s->page_off = (s->page_off + 1) & 127;
        }
        break;
    }
}

A
Avi Kivity 已提交
613 614 615 616
static const MemoryRegionOps musicpal_lcd_ops = {
    .read = musicpal_lcd_read,
    .write = musicpal_lcd_write,
    .endianness = DEVICE_NATIVE_ENDIAN,
617 618
};

G
Gerd Hoffmann 已提交
619 620 621 622 623
static const GraphicHwOps musicpal_gfx_ops = {
    .invalidate  = lcd_invalidate,
    .gfx_update  = lcd_refresh,
};

X
xiaoqiang zhao 已提交
624 625 626 627 628 629 630 631
static void musicpal_lcd_realize(DeviceState *dev, Error **errp)
{
    musicpal_lcd_state *s = MUSICPAL_LCD(dev);
    s->con = graphic_console_init(dev, 0, &musicpal_gfx_ops, s);
    qemu_console_resize(s->con, 128 * 3, 64 * 3);
}

static void musicpal_lcd_init(Object *obj)
632
{
X
xiaoqiang zhao 已提交
633
    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
634 635
    DeviceState *dev = DEVICE(sbd);
    musicpal_lcd_state *s = MUSICPAL_LCD(dev);
636

637 638
    s->brightness = 7;

X
xiaoqiang zhao 已提交
639
    memory_region_init_io(&s->iomem, obj, &musicpal_lcd_ops, s,
A
Avi Kivity 已提交
640
                          "musicpal-lcd", MP_LCD_SIZE);
641
    sysbus_init_mmio(sbd, &s->iomem);
642

643
    qdev_init_gpio_in(dev, musicpal_lcd_gpio_brightness_in, 3);
644 645
}

J
Jan Kiszka 已提交
646 647 648 649 650 651 652 653 654 655 656 657 658 659 660
static const VMStateDescription musicpal_lcd_vmsd = {
    .name = "musicpal_lcd",
    .version_id = 1,
    .minimum_version_id = 1,
    .fields = (VMStateField[]) {
        VMSTATE_UINT32(brightness, musicpal_lcd_state),
        VMSTATE_UINT32(mode, musicpal_lcd_state),
        VMSTATE_UINT32(irqctrl, musicpal_lcd_state),
        VMSTATE_UINT32(page, musicpal_lcd_state),
        VMSTATE_UINT32(page_off, musicpal_lcd_state),
        VMSTATE_BUFFER(video_ram, musicpal_lcd_state),
        VMSTATE_END_OF_LIST()
    }
};

661 662
static void musicpal_lcd_class_init(ObjectClass *klass, void *data)
{
663
    DeviceClass *dc = DEVICE_CLASS(klass);
664

665
    dc->vmsd = &musicpal_lcd_vmsd;
X
xiaoqiang zhao 已提交
666
    dc->realize = musicpal_lcd_realize;
667 668
}

669
static const TypeInfo musicpal_lcd_info = {
670
    .name          = TYPE_MUSICPAL_LCD,
671 672
    .parent        = TYPE_SYS_BUS_DEVICE,
    .instance_size = sizeof(musicpal_lcd_state),
X
xiaoqiang zhao 已提交
673
    .instance_init = musicpal_lcd_init,
674
    .class_init    = musicpal_lcd_class_init,
J
Jan Kiszka 已提交
675 676
};

677 678 679 680 681
/* PIC register offsets */
#define MP_PIC_STATUS           0x00
#define MP_PIC_ENABLE_SET       0x08
#define MP_PIC_ENABLE_CLR       0x0C

682 683 684 685 686 687 688 689 690
#define TYPE_MV88W8618_PIC "mv88w8618_pic"
#define MV88W8618_PIC(obj) \
    OBJECT_CHECK(mv88w8618_pic_state, (obj), TYPE_MV88W8618_PIC)

typedef struct mv88w8618_pic_state {
    /*< private >*/
    SysBusDevice parent_obj;
    /*< public >*/

A
Avi Kivity 已提交
691
    MemoryRegion iomem;
692 693 694 695 696 697 698 699 700 701 702 703 704 705
    uint32_t level;
    uint32_t enabled;
    qemu_irq parent_irq;
} mv88w8618_pic_state;

static void mv88w8618_pic_update(mv88w8618_pic_state *s)
{
    qemu_set_irq(s->parent_irq, (s->level & s->enabled));
}

static void mv88w8618_pic_set_irq(void *opaque, int irq, int level)
{
    mv88w8618_pic_state *s = opaque;

J
Jan Kiszka 已提交
706
    if (level) {
707
        s->level |= 1 << irq;
J
Jan Kiszka 已提交
708
    } else {
709
        s->level &= ~(1 << irq);
J
Jan Kiszka 已提交
710
    }
711 712 713
    mv88w8618_pic_update(s);
}

A
Avi Kivity 已提交
714
static uint64_t mv88w8618_pic_read(void *opaque, hwaddr offset,
A
Avi Kivity 已提交
715
                                   unsigned size)
716 717 718 719 720 721 722 723 724 725 726 727
{
    mv88w8618_pic_state *s = opaque;

    switch (offset) {
    case MP_PIC_STATUS:
        return s->level & s->enabled;

    default:
        return 0;
    }
}

A
Avi Kivity 已提交
728
static void mv88w8618_pic_write(void *opaque, hwaddr offset,
A
Avi Kivity 已提交
729
                                uint64_t value, unsigned size)
730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745
{
    mv88w8618_pic_state *s = opaque;

    switch (offset) {
    case MP_PIC_ENABLE_SET:
        s->enabled |= value;
        break;

    case MP_PIC_ENABLE_CLR:
        s->enabled &= ~value;
        s->level &= ~value;
        break;
    }
    mv88w8618_pic_update(s);
}

J
Jan Kiszka 已提交
746
static void mv88w8618_pic_reset(DeviceState *d)
747
{
748
    mv88w8618_pic_state *s = MV88W8618_PIC(d);
749 750 751 752 753

    s->level = 0;
    s->enabled = 0;
}

A
Avi Kivity 已提交
754 755 756 757
static const MemoryRegionOps mv88w8618_pic_ops = {
    .read = mv88w8618_pic_read,
    .write = mv88w8618_pic_write,
    .endianness = DEVICE_NATIVE_ENDIAN,
758 759
};

X
xiaoqiang zhao 已提交
760
static void mv88w8618_pic_init(Object *obj)
761
{
X
xiaoqiang zhao 已提交
762
    SysBusDevice *dev = SYS_BUS_DEVICE(obj);
763
    mv88w8618_pic_state *s = MV88W8618_PIC(dev);
764

765
    qdev_init_gpio_in(DEVICE(dev), mv88w8618_pic_set_irq, 32);
P
Paul Brook 已提交
766
    sysbus_init_irq(dev, &s->parent_irq);
X
xiaoqiang zhao 已提交
767
    memory_region_init_io(&s->iomem, obj, &mv88w8618_pic_ops, s,
A
Avi Kivity 已提交
768
                          "musicpal-pic", MP_PIC_SIZE);
769
    sysbus_init_mmio(dev, &s->iomem);
770 771
}

J
Jan Kiszka 已提交
772 773 774 775 776 777 778 779 780 781 782
static const VMStateDescription mv88w8618_pic_vmsd = {
    .name = "mv88w8618_pic",
    .version_id = 1,
    .minimum_version_id = 1,
    .fields = (VMStateField[]) {
        VMSTATE_UINT32(level, mv88w8618_pic_state),
        VMSTATE_UINT32(enabled, mv88w8618_pic_state),
        VMSTATE_END_OF_LIST()
    }
};

783 784
static void mv88w8618_pic_class_init(ObjectClass *klass, void *data)
{
785
    DeviceClass *dc = DEVICE_CLASS(klass);
786

787 788
    dc->reset = mv88w8618_pic_reset;
    dc->vmsd = &mv88w8618_pic_vmsd;
789 790
}

791
static const TypeInfo mv88w8618_pic_info = {
792
    .name          = TYPE_MV88W8618_PIC,
793 794
    .parent        = TYPE_SYS_BUS_DEVICE,
    .instance_size = sizeof(mv88w8618_pic_state),
X
xiaoqiang zhao 已提交
795
    .instance_init = mv88w8618_pic_init,
796
    .class_init    = mv88w8618_pic_class_init,
J
Jan Kiszka 已提交
797 798
};

799 800 801 802 803 804 805 806 807 808 809 810 811 812
/* PIT register offsets */
#define MP_PIT_TIMER1_LENGTH    0x00
/* ... */
#define MP_PIT_TIMER4_LENGTH    0x0C
#define MP_PIT_CONTROL          0x10
#define MP_PIT_TIMER1_VALUE     0x14
/* ... */
#define MP_PIT_TIMER4_VALUE     0x20
#define MP_BOARD_RESET          0x34

/* Magic board reset value (probably some watchdog behind it) */
#define MP_BOARD_RESET_MAGIC    0x10000

typedef struct mv88w8618_timer_state {
P
Paul Brook 已提交
813
    ptimer_state *ptimer;
814 815 816 817 818
    uint32_t limit;
    int freq;
    qemu_irq irq;
} mv88w8618_timer_state;

819 820 821 822
#define TYPE_MV88W8618_PIT "mv88w8618_pit"
#define MV88W8618_PIT(obj) \
    OBJECT_CHECK(mv88w8618_pit_state, (obj), TYPE_MV88W8618_PIT)

823
typedef struct mv88w8618_pit_state {
824 825 826 827
    /*< private >*/
    SysBusDevice parent_obj;
    /*< public >*/

A
Avi Kivity 已提交
828
    MemoryRegion iomem;
P
Paul Brook 已提交
829
    mv88w8618_timer_state timer[4];
830 831 832 833 834 835 836 837 838
} mv88w8618_pit_state;

static void mv88w8618_timer_tick(void *opaque)
{
    mv88w8618_timer_state *s = opaque;

    qemu_irq_raise(s->irq);
}

P
Paul Brook 已提交
839 840
static void mv88w8618_timer_init(SysBusDevice *dev, mv88w8618_timer_state *s,
                                 uint32_t freq)
841 842 843
{
    QEMUBH *bh;

P
Paul Brook 已提交
844
    sysbus_init_irq(dev, &s->irq);
845 846 847
    s->freq = freq;

    bh = qemu_bh_new(mv88w8618_timer_tick, s);
848
    s->ptimer = ptimer_init(bh, PTIMER_POLICY_DEFAULT);
849 850
}

A
Avi Kivity 已提交
851
static uint64_t mv88w8618_pit_read(void *opaque, hwaddr offset,
A
Avi Kivity 已提交
852
                                   unsigned size)
853 854 855 856 857 858
{
    mv88w8618_pit_state *s = opaque;
    mv88w8618_timer_state *t;

    switch (offset) {
    case MP_PIT_TIMER1_VALUE ... MP_PIT_TIMER4_VALUE:
P
Paul Brook 已提交
859 860
        t = &s->timer[(offset-MP_PIT_TIMER1_VALUE) >> 2];
        return ptimer_get_count(t->ptimer);
861 862 863 864 865 866

    default:
        return 0;
    }
}

A
Avi Kivity 已提交
867
static void mv88w8618_pit_write(void *opaque, hwaddr offset,
A
Avi Kivity 已提交
868
                                uint64_t value, unsigned size)
869 870 871 872 873 874 875
{
    mv88w8618_pit_state *s = opaque;
    mv88w8618_timer_state *t;
    int i;

    switch (offset) {
    case MP_PIT_TIMER1_LENGTH ... MP_PIT_TIMER4_LENGTH:
P
Paul Brook 已提交
876
        t = &s->timer[offset >> 2];
877
        t->limit = value;
878 879 880 881 882
        if (t->limit > 0) {
            ptimer_set_limit(t->ptimer, t->limit, 1);
        } else {
            ptimer_stop(t->ptimer);
        }
883 884 885 886
        break;

    case MP_PIT_CONTROL:
        for (i = 0; i < 4; i++) {
887 888
            t = &s->timer[i];
            if (value & 0xf && t->limit > 0) {
P
Paul Brook 已提交
889 890 891
                ptimer_set_limit(t->ptimer, t->limit, 0);
                ptimer_set_freq(t->ptimer, t->freq);
                ptimer_run(t->ptimer, 0);
892 893
            } else {
                ptimer_stop(t->ptimer);
894 895 896 897 898 899
            }
            value >>= 4;
        }
        break;

    case MP_BOARD_RESET:
J
Jan Kiszka 已提交
900
        if (value == MP_BOARD_RESET_MAGIC) {
901
            qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
J
Jan Kiszka 已提交
902
        }
903 904 905 906
        break;
    }
}

J
Jan Kiszka 已提交
907
static void mv88w8618_pit_reset(DeviceState *d)
908
{
909
    mv88w8618_pit_state *s = MV88W8618_PIT(d);
910 911 912 913 914 915 916 917
    int i;

    for (i = 0; i < 4; i++) {
        ptimer_stop(s->timer[i].ptimer);
        s->timer[i].limit = 0;
    }
}

A
Avi Kivity 已提交
918 919 920 921
static const MemoryRegionOps mv88w8618_pit_ops = {
    .read = mv88w8618_pit_read,
    .write = mv88w8618_pit_write,
    .endianness = DEVICE_NATIVE_ENDIAN,
922 923
};

X
xiaoqiang zhao 已提交
924
static void mv88w8618_pit_init(Object *obj)
925
{
X
xiaoqiang zhao 已提交
926
    SysBusDevice *dev = SYS_BUS_DEVICE(obj);
927
    mv88w8618_pit_state *s = MV88W8618_PIT(dev);
P
Paul Brook 已提交
928
    int i;
929 930 931

    /* Letting them all run at 1 MHz is likely just a pragmatic
     * simplification. */
P
Paul Brook 已提交
932 933 934
    for (i = 0; i < 4; i++) {
        mv88w8618_timer_init(dev, &s->timer[i], 1000000);
    }
935

X
xiaoqiang zhao 已提交
936
    memory_region_init_io(&s->iomem, obj, &mv88w8618_pit_ops, s,
A
Avi Kivity 已提交
937
                          "musicpal-pit", MP_PIT_SIZE);
938
    sysbus_init_mmio(dev, &s->iomem);
939 940
}

J
Jan Kiszka 已提交
941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962
static const VMStateDescription mv88w8618_timer_vmsd = {
    .name = "timer",
    .version_id = 1,
    .minimum_version_id = 1,
    .fields = (VMStateField[]) {
        VMSTATE_PTIMER(ptimer, mv88w8618_timer_state),
        VMSTATE_UINT32(limit, mv88w8618_timer_state),
        VMSTATE_END_OF_LIST()
    }
};

static const VMStateDescription mv88w8618_pit_vmsd = {
    .name = "mv88w8618_pit",
    .version_id = 1,
    .minimum_version_id = 1,
    .fields = (VMStateField[]) {
        VMSTATE_STRUCT_ARRAY(timer, mv88w8618_pit_state, 4, 1,
                             mv88w8618_timer_vmsd, mv88w8618_timer_state),
        VMSTATE_END_OF_LIST()
    }
};

963 964
static void mv88w8618_pit_class_init(ObjectClass *klass, void *data)
{
965
    DeviceClass *dc = DEVICE_CLASS(klass);
966

967 968
    dc->reset = mv88w8618_pit_reset;
    dc->vmsd = &mv88w8618_pit_vmsd;
969 970
}

971
static const TypeInfo mv88w8618_pit_info = {
972
    .name          = TYPE_MV88W8618_PIT,
973 974
    .parent        = TYPE_SYS_BUS_DEVICE,
    .instance_size = sizeof(mv88w8618_pit_state),
X
xiaoqiang zhao 已提交
975
    .instance_init = mv88w8618_pit_init,
976
    .class_init    = mv88w8618_pit_class_init,
977 978
};

979 980 981
/* Flash config register offsets */
#define MP_FLASHCFG_CFGR0    0x04

982 983 984 985
#define TYPE_MV88W8618_FLASHCFG "mv88w8618_flashcfg"
#define MV88W8618_FLASHCFG(obj) \
    OBJECT_CHECK(mv88w8618_flashcfg_state, (obj), TYPE_MV88W8618_FLASHCFG)

986
typedef struct mv88w8618_flashcfg_state {
987 988 989 990
    /*< private >*/
    SysBusDevice parent_obj;
    /*< public >*/

A
Avi Kivity 已提交
991
    MemoryRegion iomem;
992 993 994
    uint32_t cfgr0;
} mv88w8618_flashcfg_state;

A
Avi Kivity 已提交
995
static uint64_t mv88w8618_flashcfg_read(void *opaque,
A
Avi Kivity 已提交
996
                                        hwaddr offset,
A
Avi Kivity 已提交
997
                                        unsigned size)
998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009
{
    mv88w8618_flashcfg_state *s = opaque;

    switch (offset) {
    case MP_FLASHCFG_CFGR0:
        return s->cfgr0;

    default:
        return 0;
    }
}

A
Avi Kivity 已提交
1010
static void mv88w8618_flashcfg_write(void *opaque, hwaddr offset,
A
Avi Kivity 已提交
1011
                                     uint64_t value, unsigned size)
1012 1013 1014 1015 1016 1017 1018 1019 1020 1021
{
    mv88w8618_flashcfg_state *s = opaque;

    switch (offset) {
    case MP_FLASHCFG_CFGR0:
        s->cfgr0 = value;
        break;
    }
}

A
Avi Kivity 已提交
1022 1023 1024 1025
static const MemoryRegionOps mv88w8618_flashcfg_ops = {
    .read = mv88w8618_flashcfg_read,
    .write = mv88w8618_flashcfg_write,
    .endianness = DEVICE_NATIVE_ENDIAN,
1026 1027
};

X
xiaoqiang zhao 已提交
1028
static void mv88w8618_flashcfg_init(Object *obj)
1029
{
X
xiaoqiang zhao 已提交
1030
    SysBusDevice *dev = SYS_BUS_DEVICE(obj);
1031
    mv88w8618_flashcfg_state *s = MV88W8618_FLASHCFG(dev);
1032 1033

    s->cfgr0 = 0xfffe4285; /* Default as set by U-Boot for 8 MB flash */
X
xiaoqiang zhao 已提交
1034
    memory_region_init_io(&s->iomem, obj, &mv88w8618_flashcfg_ops, s,
A
Avi Kivity 已提交
1035
                          "musicpal-flashcfg", MP_FLASHCFG_SIZE);
1036
    sysbus_init_mmio(dev, &s->iomem);
1037 1038
}

J
Jan Kiszka 已提交
1039 1040 1041 1042 1043 1044 1045 1046 1047 1048
static const VMStateDescription mv88w8618_flashcfg_vmsd = {
    .name = "mv88w8618_flashcfg",
    .version_id = 1,
    .minimum_version_id = 1,
    .fields = (VMStateField[]) {
        VMSTATE_UINT32(cfgr0, mv88w8618_flashcfg_state),
        VMSTATE_END_OF_LIST()
    }
};

1049 1050
static void mv88w8618_flashcfg_class_init(ObjectClass *klass, void *data)
{
1051
    DeviceClass *dc = DEVICE_CLASS(klass);
1052

1053
    dc->vmsd = &mv88w8618_flashcfg_vmsd;
1054 1055
}

1056
static const TypeInfo mv88w8618_flashcfg_info = {
1057
    .name          = TYPE_MV88W8618_FLASHCFG,
1058 1059
    .parent        = TYPE_SYS_BUS_DEVICE,
    .instance_size = sizeof(mv88w8618_flashcfg_state),
X
xiaoqiang zhao 已提交
1060
    .instance_init = mv88w8618_flashcfg_init,
1061
    .class_init    = mv88w8618_flashcfg_class_init,
J
Jan Kiszka 已提交
1062 1063
};

1064 1065 1066 1067 1068
/* Misc register offsets */
#define MP_MISC_BOARD_REVISION  0x18

#define MP_BOARD_REVISION       0x31

P
Peter Maydell 已提交
1069 1070 1071 1072 1073 1074 1075 1076 1077
typedef struct {
    SysBusDevice parent_obj;
    MemoryRegion iomem;
} MusicPalMiscState;

#define TYPE_MUSICPAL_MISC "musicpal-misc"
#define MUSICPAL_MISC(obj) \
     OBJECT_CHECK(MusicPalMiscState, (obj), TYPE_MUSICPAL_MISC)

A
Avi Kivity 已提交
1078
static uint64_t musicpal_misc_read(void *opaque, hwaddr offset,
A
Avi Kivity 已提交
1079
                                   unsigned size)
1080 1081 1082 1083 1084 1085 1086 1087 1088 1089
{
    switch (offset) {
    case MP_MISC_BOARD_REVISION:
        return MP_BOARD_REVISION;

    default:
        return 0;
    }
}

A
Avi Kivity 已提交
1090
static void musicpal_misc_write(void *opaque, hwaddr offset,
A
Avi Kivity 已提交
1091
                                uint64_t value, unsigned size)
1092 1093 1094
{
}

A
Avi Kivity 已提交
1095 1096 1097 1098
static const MemoryRegionOps musicpal_misc_ops = {
    .read = musicpal_misc_read,
    .write = musicpal_misc_write,
    .endianness = DEVICE_NATIVE_ENDIAN,
1099 1100
};

P
Peter Maydell 已提交
1101
static void musicpal_misc_init(Object *obj)
1102
{
P
Peter Maydell 已提交
1103 1104
    SysBusDevice *sd = SYS_BUS_DEVICE(obj);
    MusicPalMiscState *s = MUSICPAL_MISC(obj);
1105

1106
    memory_region_init_io(&s->iomem, OBJECT(s), &musicpal_misc_ops, NULL,
A
Avi Kivity 已提交
1107
                          "musicpal-misc", MP_MISC_SIZE);
P
Peter Maydell 已提交
1108
    sysbus_init_mmio(sd, &s->iomem);
1109 1110
}

P
Peter Maydell 已提交
1111 1112 1113 1114 1115 1116 1117
static const TypeInfo musicpal_misc_info = {
    .name = TYPE_MUSICPAL_MISC,
    .parent = TYPE_SYS_BUS_DEVICE,
    .instance_init = musicpal_misc_init,
    .instance_size = sizeof(MusicPalMiscState),
};

1118 1119 1120 1121
/* WLAN register offsets */
#define MP_WLAN_MAGIC1          0x11c
#define MP_WLAN_MAGIC2          0x124

A
Avi Kivity 已提交
1122
static uint64_t mv88w8618_wlan_read(void *opaque, hwaddr offset,
A
Avi Kivity 已提交
1123
                                    unsigned size)
1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137
{
    switch (offset) {
    /* Workaround to allow loading the binary-only wlandrv.ko crap
     * from the original Freecom firmware. */
    case MP_WLAN_MAGIC1:
        return ~3;
    case MP_WLAN_MAGIC2:
        return -1;

    default:
        return 0;
    }
}

A
Avi Kivity 已提交
1138
static void mv88w8618_wlan_write(void *opaque, hwaddr offset,
A
Avi Kivity 已提交
1139
                                 uint64_t value, unsigned size)
1140 1141 1142
{
}

A
Avi Kivity 已提交
1143 1144 1145 1146
static const MemoryRegionOps mv88w8618_wlan_ops = {
    .read = mv88w8618_wlan_read,
    .write =mv88w8618_wlan_write,
    .endianness = DEVICE_NATIVE_ENDIAN,
1147 1148
};

1149
static int mv88w8618_wlan_init(SysBusDevice *dev)
1150
{
A
Avi Kivity 已提交
1151
    MemoryRegion *iomem = g_new(MemoryRegion, 1);
1152

1153
    memory_region_init_io(iomem, OBJECT(dev), &mv88w8618_wlan_ops, NULL,
A
Avi Kivity 已提交
1154
                          "musicpal-wlan", MP_WLAN_SIZE);
1155
    sysbus_init_mmio(dev, iomem);
1156
    return 0;
1157
}
1158

1159 1160 1161 1162
/* GPIO register offsets */
#define MP_GPIO_OE_LO           0x008
#define MP_GPIO_OUT_LO          0x00c
#define MP_GPIO_IN_LO           0x010
J
Jan Kiszka 已提交
1163 1164
#define MP_GPIO_IER_LO          0x014
#define MP_GPIO_IMR_LO          0x018
1165 1166 1167 1168
#define MP_GPIO_ISR_LO          0x020
#define MP_GPIO_OE_HI           0x508
#define MP_GPIO_OUT_HI          0x50c
#define MP_GPIO_IN_HI           0x510
J
Jan Kiszka 已提交
1169 1170
#define MP_GPIO_IER_HI          0x514
#define MP_GPIO_IMR_HI          0x518
1171
#define MP_GPIO_ISR_HI          0x520
1172 1173 1174 1175 1176 1177 1178 1179 1180

/* GPIO bits & masks */
#define MP_GPIO_LCD_BRIGHTNESS  0x00070000
#define MP_GPIO_I2C_DATA_BIT    29
#define MP_GPIO_I2C_CLOCK_BIT   30

/* LCD brightness bits in GPIO_OE_HI */
#define MP_OE_LCD_BRIGHTNESS    0x0007

1181 1182 1183 1184
#define TYPE_MUSICPAL_GPIO "musicpal_gpio"
#define MUSICPAL_GPIO(obj) \
    OBJECT_CHECK(musicpal_gpio_state, (obj), TYPE_MUSICPAL_GPIO)

1185
typedef struct musicpal_gpio_state {
1186 1187 1188 1189
    /*< private >*/
    SysBusDevice parent_obj;
    /*< public >*/

A
Avi Kivity 已提交
1190
    MemoryRegion iomem;
1191 1192 1193
    uint32_t lcd_brightness;
    uint32_t out_state;
    uint32_t in_state;
J
Jan Kiszka 已提交
1194 1195
    uint32_t ier;
    uint32_t imr;
1196 1197
    uint32_t isr;
    qemu_irq irq;
J
Jan Kiszka 已提交
1198
    qemu_irq out[5]; /* 3 brightness out + 2 lcd (data and clock ) */
1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240
} musicpal_gpio_state;

static void musicpal_gpio_brightness_update(musicpal_gpio_state *s) {
    int i;
    uint32_t brightness;

    /* compute brightness ratio */
    switch (s->lcd_brightness) {
    case 0x00000007:
        brightness = 0;
        break;

    case 0x00020000:
        brightness = 1;
        break;

    case 0x00020001:
        brightness = 2;
        break;

    case 0x00040000:
        brightness = 3;
        break;

    case 0x00010006:
        brightness = 4;
        break;

    case 0x00020005:
        brightness = 5;
        break;

    case 0x00040003:
        brightness = 6;
        break;

    case 0x00030004:
    default:
        brightness = 7;
    }

    /* set lcd brightness GPIOs  */
J
Jan Kiszka 已提交
1241
    for (i = 0; i <= 2; i++) {
1242
        qemu_set_irq(s->out[i], (brightness >> i) & 1);
J
Jan Kiszka 已提交
1243
    }
1244 1245
}

J
Jan Kiszka 已提交
1246
static void musicpal_gpio_pin_event(void *opaque, int pin, int level)
1247
{
J
Jan Kiszka 已提交
1248
    musicpal_gpio_state *s = opaque;
J
Jan Kiszka 已提交
1249 1250 1251
    uint32_t mask = 1 << pin;
    uint32_t delta = level << pin;
    uint32_t old = s->in_state & mask;
1252

J
Jan Kiszka 已提交
1253 1254
    s->in_state &= ~mask;
    s->in_state |= delta;
1255

J
Jan Kiszka 已提交
1256 1257 1258 1259
    if ((old ^ delta) &&
        ((level && (s->imr & mask)) || (!level && (s->ier & mask)))) {
        s->isr = mask;
        qemu_irq_raise(s->irq);
1260 1261 1262
    }
}

A
Avi Kivity 已提交
1263
static uint64_t musicpal_gpio_read(void *opaque, hwaddr offset,
A
Avi Kivity 已提交
1264
                                   unsigned size)
1265
{
J
Jan Kiszka 已提交
1266
    musicpal_gpio_state *s = opaque;
1267

1268 1269
    switch (offset) {
    case MP_GPIO_OE_HI: /* used for LCD brightness control */
1270
        return s->lcd_brightness & MP_OE_LCD_BRIGHTNESS;
1271 1272

    case MP_GPIO_OUT_LO:
1273
        return s->out_state & 0xFFFF;
1274
    case MP_GPIO_OUT_HI:
1275
        return s->out_state >> 16;
1276 1277

    case MP_GPIO_IN_LO:
1278
        return s->in_state & 0xFFFF;
1279
    case MP_GPIO_IN_HI:
1280
        return s->in_state >> 16;
1281

J
Jan Kiszka 已提交
1282 1283 1284 1285 1286 1287 1288 1289 1290 1291
    case MP_GPIO_IER_LO:
        return s->ier & 0xFFFF;
    case MP_GPIO_IER_HI:
        return s->ier >> 16;

    case MP_GPIO_IMR_LO:
        return s->imr & 0xFFFF;
    case MP_GPIO_IMR_HI:
        return s->imr >> 16;

1292
    case MP_GPIO_ISR_LO:
1293
        return s->isr & 0xFFFF;
1294
    case MP_GPIO_ISR_HI:
1295
        return s->isr >> 16;
1296 1297 1298 1299 1300 1301

    default:
        return 0;
    }
}

A
Avi Kivity 已提交
1302
static void musicpal_gpio_write(void *opaque, hwaddr offset,
A
Avi Kivity 已提交
1303
                                uint64_t value, unsigned size)
1304
{
J
Jan Kiszka 已提交
1305
    musicpal_gpio_state *s = opaque;
1306 1307
    switch (offset) {
    case MP_GPIO_OE_HI: /* used for LCD brightness control */
1308
        s->lcd_brightness = (s->lcd_brightness & MP_GPIO_LCD_BRIGHTNESS) |
1309
                         (value & MP_OE_LCD_BRIGHTNESS);
1310
        musicpal_gpio_brightness_update(s);
1311 1312 1313
        break;

    case MP_GPIO_OUT_LO:
1314
        s->out_state = (s->out_state & 0xFFFF0000) | (value & 0xFFFF);
1315 1316
        break;
    case MP_GPIO_OUT_HI:
1317 1318 1319 1320
        s->out_state = (s->out_state & 0xFFFF) | (value << 16);
        s->lcd_brightness = (s->lcd_brightness & 0xFFFF) |
                            (s->out_state & MP_GPIO_LCD_BRIGHTNESS);
        musicpal_gpio_brightness_update(s);
1321 1322
        qemu_set_irq(s->out[3], (s->out_state >> MP_GPIO_I2C_DATA_BIT) & 1);
        qemu_set_irq(s->out[4], (s->out_state >> MP_GPIO_I2C_CLOCK_BIT) & 1);
1323 1324
        break;

J
Jan Kiszka 已提交
1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337
    case MP_GPIO_IER_LO:
        s->ier = (s->ier & 0xFFFF0000) | (value & 0xFFFF);
        break;
    case MP_GPIO_IER_HI:
        s->ier = (s->ier & 0xFFFF) | (value << 16);
        break;

    case MP_GPIO_IMR_LO:
        s->imr = (s->imr & 0xFFFF0000) | (value & 0xFFFF);
        break;
    case MP_GPIO_IMR_HI:
        s->imr = (s->imr & 0xFFFF) | (value << 16);
        break;
1338 1339 1340
    }
}

A
Avi Kivity 已提交
1341 1342 1343 1344
static const MemoryRegionOps musicpal_gpio_ops = {
    .read = musicpal_gpio_read,
    .write = musicpal_gpio_write,
    .endianness = DEVICE_NATIVE_ENDIAN,
1345 1346
};

J
Jan Kiszka 已提交
1347
static void musicpal_gpio_reset(DeviceState *d)
1348
{
1349
    musicpal_gpio_state *s = MUSICPAL_GPIO(d);
J
Jan Kiszka 已提交
1350 1351 1352

    s->lcd_brightness = 0;
    s->out_state = 0;
1353
    s->in_state = 0xffffffff;
J
Jan Kiszka 已提交
1354 1355
    s->ier = 0;
    s->imr = 0;
1356 1357 1358
    s->isr = 0;
}

X
xiaoqiang zhao 已提交
1359
static void musicpal_gpio_init(Object *obj)
1360
{
X
xiaoqiang zhao 已提交
1361
    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
1362 1363
    DeviceState *dev = DEVICE(sbd);
    musicpal_gpio_state *s = MUSICPAL_GPIO(dev);
1364

1365
    sysbus_init_irq(sbd, &s->irq);
1366

X
xiaoqiang zhao 已提交
1367
    memory_region_init_io(&s->iomem, obj, &musicpal_gpio_ops, s,
A
Avi Kivity 已提交
1368
                          "musicpal-gpio", MP_GPIO_SIZE);
1369
    sysbus_init_mmio(sbd, &s->iomem);
1370

1371
    qdev_init_gpio_out(dev, s->out, ARRAY_SIZE(s->out));
J
Jan Kiszka 已提交
1372

1373
    qdev_init_gpio_in(dev, musicpal_gpio_pin_event, 32);
1374 1375
}

J
Jan Kiszka 已提交
1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390
static const VMStateDescription musicpal_gpio_vmsd = {
    .name = "musicpal_gpio",
    .version_id = 1,
    .minimum_version_id = 1,
    .fields = (VMStateField[]) {
        VMSTATE_UINT32(lcd_brightness, musicpal_gpio_state),
        VMSTATE_UINT32(out_state, musicpal_gpio_state),
        VMSTATE_UINT32(in_state, musicpal_gpio_state),
        VMSTATE_UINT32(ier, musicpal_gpio_state),
        VMSTATE_UINT32(imr, musicpal_gpio_state),
        VMSTATE_UINT32(isr, musicpal_gpio_state),
        VMSTATE_END_OF_LIST()
    }
};

1391 1392
static void musicpal_gpio_class_init(ObjectClass *klass, void *data)
{
1393
    DeviceClass *dc = DEVICE_CLASS(klass);
1394

1395 1396
    dc->reset = musicpal_gpio_reset;
    dc->vmsd = &musicpal_gpio_vmsd;
1397 1398
}

1399
static const TypeInfo musicpal_gpio_info = {
1400
    .name          = TYPE_MUSICPAL_GPIO,
1401 1402
    .parent        = TYPE_SYS_BUS_DEVICE,
    .instance_size = sizeof(musicpal_gpio_state),
X
xiaoqiang zhao 已提交
1403
    .instance_init = musicpal_gpio_init,
1404
    .class_init    = musicpal_gpio_class_init,
J
Jan Kiszka 已提交
1405 1406
};

1407
/* Keyboard codes & masks */
1408
#define KEY_RELEASED            0x80
1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421
#define KEY_CODE                0x7f

#define KEYCODE_TAB             0x0f
#define KEYCODE_ENTER           0x1c
#define KEYCODE_F               0x21
#define KEYCODE_M               0x32

#define KEYCODE_EXTENDED        0xe0
#define KEYCODE_UP              0x48
#define KEYCODE_DOWN            0x50
#define KEYCODE_LEFT            0x4b
#define KEYCODE_RIGHT           0x4d

J
Jan Kiszka 已提交
1422
#define MP_KEY_WHEEL_VOL       (1 << 0)
1423 1424 1425 1426 1427 1428 1429 1430
#define MP_KEY_WHEEL_VOL_INV   (1 << 1)
#define MP_KEY_WHEEL_NAV       (1 << 2)
#define MP_KEY_WHEEL_NAV_INV   (1 << 3)
#define MP_KEY_BTN_FAVORITS    (1 << 4)
#define MP_KEY_BTN_MENU        (1 << 5)
#define MP_KEY_BTN_VOLUME      (1 << 6)
#define MP_KEY_BTN_NAVIGATION  (1 << 7)

1431 1432 1433 1434
#define TYPE_MUSICPAL_KEY "musicpal_key"
#define MUSICPAL_KEY(obj) \
    OBJECT_CHECK(musicpal_key_state, (obj), TYPE_MUSICPAL_KEY)

1435
typedef struct musicpal_key_state {
1436 1437 1438 1439
    /*< private >*/
    SysBusDevice parent_obj;
    /*< public >*/

1440
    MemoryRegion iomem;
1441
    uint32_t kbd_extended;
J
Jan Kiszka 已提交
1442 1443
    uint32_t pressed_keys;
    qemu_irq out[8];
1444 1445
} musicpal_key_state;

1446 1447
static void musicpal_key_event(void *opaque, int keycode)
{
J
Jan Kiszka 已提交
1448
    musicpal_key_state *s = opaque;
1449
    uint32_t event = 0;
1450
    int i;
1451 1452

    if (keycode == KEYCODE_EXTENDED) {
1453
        s->kbd_extended = 1;
1454 1455 1456
        return;
    }

J
Jan Kiszka 已提交
1457
    if (s->kbd_extended) {
1458 1459
        switch (keycode & KEY_CODE) {
        case KEYCODE_UP:
1460
            event = MP_KEY_WHEEL_NAV | MP_KEY_WHEEL_NAV_INV;
1461 1462 1463
            break;

        case KEYCODE_DOWN:
1464
            event = MP_KEY_WHEEL_NAV;
1465 1466 1467
            break;

        case KEYCODE_LEFT:
1468
            event = MP_KEY_WHEEL_VOL | MP_KEY_WHEEL_VOL_INV;
1469 1470 1471
            break;

        case KEYCODE_RIGHT:
1472
            event = MP_KEY_WHEEL_VOL;
1473 1474
            break;
        }
J
Jan Kiszka 已提交
1475
    } else {
1476 1477
        switch (keycode & KEY_CODE) {
        case KEYCODE_F:
1478
            event = MP_KEY_BTN_FAVORITS;
1479 1480 1481
            break;

        case KEYCODE_TAB:
1482
            event = MP_KEY_BTN_VOLUME;
1483 1484 1485
            break;

        case KEYCODE_ENTER:
1486
            event = MP_KEY_BTN_NAVIGATION;
1487 1488 1489
            break;

        case KEYCODE_M:
1490
            event = MP_KEY_BTN_MENU;
1491 1492
            break;
        }
1493
        /* Do not repeat already pressed buttons */
J
Jan Kiszka 已提交
1494
        if (!(keycode & KEY_RELEASED) && (s->pressed_keys & event)) {
1495
            event = 0;
J
Jan Kiszka 已提交
1496
        }
1497
    }
1498

1499
    if (event) {
J
Jan Kiszka 已提交
1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512
        /* Raise GPIO pin first if repeating a key */
        if (!(keycode & KEY_RELEASED) && (s->pressed_keys & event)) {
            for (i = 0; i <= 7; i++) {
                if (event & (1 << i)) {
                    qemu_set_irq(s->out[i], 1);
                }
            }
        }
        for (i = 0; i <= 7; i++) {
            if (event & (1 << i)) {
                qemu_set_irq(s->out[i], !!(keycode & KEY_RELEASED));
            }
        }
1513
        if (keycode & KEY_RELEASED) {
J
Jan Kiszka 已提交
1514
            s->pressed_keys &= ~event;
1515
        } else {
J
Jan Kiszka 已提交
1516
            s->pressed_keys |= event;
1517
        }
1518 1519
    }

1520 1521 1522
    s->kbd_extended = 0;
}

X
xiaoqiang zhao 已提交
1523
static void musicpal_key_init(Object *obj)
1524
{
X
xiaoqiang zhao 已提交
1525
    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
1526 1527
    DeviceState *dev = DEVICE(sbd);
    musicpal_key_state *s = MUSICPAL_KEY(dev);
1528

X
xiaoqiang zhao 已提交
1529
    memory_region_init(&s->iomem, obj, "dummy", 0);
1530
    sysbus_init_mmio(sbd, &s->iomem);
1531 1532

    s->kbd_extended = 0;
J
Jan Kiszka 已提交
1533
    s->pressed_keys = 0;
1534

1535
    qdev_init_gpio_out(dev, s->out, ARRAY_SIZE(s->out));
1536 1537

    qemu_add_kbd_event_handler(musicpal_key_event, s);
1538 1539
}

J
Jan Kiszka 已提交
1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550
static const VMStateDescription musicpal_key_vmsd = {
    .name = "musicpal_key",
    .version_id = 1,
    .minimum_version_id = 1,
    .fields = (VMStateField[]) {
        VMSTATE_UINT32(kbd_extended, musicpal_key_state),
        VMSTATE_UINT32(pressed_keys, musicpal_key_state),
        VMSTATE_END_OF_LIST()
    }
};

1551 1552
static void musicpal_key_class_init(ObjectClass *klass, void *data)
{
1553
    DeviceClass *dc = DEVICE_CLASS(klass);
1554

1555
    dc->vmsd = &musicpal_key_vmsd;
1556 1557
}

1558
static const TypeInfo musicpal_key_info = {
1559
    .name          = TYPE_MUSICPAL_KEY,
1560 1561
    .parent        = TYPE_SYS_BUS_DEVICE,
    .instance_size = sizeof(musicpal_key_state),
X
xiaoqiang zhao 已提交
1562
    .instance_init = musicpal_key_init,
1563
    .class_init    = musicpal_key_class_init,
J
Jan Kiszka 已提交
1564 1565
};

1566 1567 1568 1569 1570
static struct arm_boot_info musicpal_binfo = {
    .loader_start = 0x0,
    .board_id = 0x20e,
};

1571
static void musicpal_init(MachineState *machine)
1572
{
1573 1574 1575 1576
    const char *cpu_model = machine->cpu_model;
    const char *kernel_filename = machine->kernel_filename;
    const char *kernel_cmdline = machine->kernel_cmdline;
    const char *initrd_filename = machine->initrd_filename;
1577
    ARMCPU *cpu;
P
Paul Brook 已提交
1578 1579
    qemu_irq pic[32];
    DeviceState *dev;
1580
    DeviceState *i2c_dev;
1581 1582
    DeviceState *lcd_dev;
    DeviceState *key_dev;
1583 1584
    DeviceState *wm8750_dev;
    SysBusDevice *s;
A
Andreas Färber 已提交
1585
    I2CBus *i2c;
P
Paul Brook 已提交
1586
    int i;
1587
    unsigned long flash_size;
G
Gerd Hoffmann 已提交
1588
    DriveInfo *dinfo;
A
Avi Kivity 已提交
1589 1590 1591
    MemoryRegion *address_space_mem = get_system_memory();
    MemoryRegion *ram = g_new(MemoryRegion, 1);
    MemoryRegion *sram = g_new(MemoryRegion, 1);
1592

J
Jan Kiszka 已提交
1593
    if (!cpu_model) {
1594
        cpu_model = "arm926";
J
Jan Kiszka 已提交
1595
    }
1596 1597
    cpu = cpu_arm_init(cpu_model);
    if (!cpu) {
1598 1599 1600 1601 1602
        fprintf(stderr, "Unable to find CPU definition\n");
        exit(1);
    }

    /* For now we use a fixed - the original - RAM size */
1603 1604
    memory_region_allocate_system_memory(ram, NULL, "musicpal.ram",
                                         MP_RAM_DEFAULT_SIZE);
A
Avi Kivity 已提交
1605
    memory_region_add_subregion(address_space_mem, 0, ram);
1606

1607
    memory_region_init_ram(sram, NULL, "musicpal.sram", MP_SRAM_SIZE,
1608
                           &error_fatal);
A
Avi Kivity 已提交
1609
    memory_region_add_subregion(address_space_mem, MP_SRAM_BASE, sram);
1610

1611
    dev = sysbus_create_simple(TYPE_MV88W8618_PIC, MP_PIC_BASE,
1612
                               qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ));
P
Paul Brook 已提交
1613
    for (i = 0; i < 32; i++) {
P
Paul Brook 已提交
1614
        pic[i] = qdev_get_gpio_in(dev, i);
P
Paul Brook 已提交
1615
    }
1616
    sysbus_create_varargs(TYPE_MV88W8618_PIT, MP_PIT_BASE, pic[MP_TIMER1_IRQ],
P
Paul Brook 已提交
1617 1618
                          pic[MP_TIMER2_IRQ], pic[MP_TIMER3_IRQ],
                          pic[MP_TIMER4_IRQ], NULL);
1619

J
Jan Kiszka 已提交
1620
    if (serial_hds[0]) {
1621 1622
        serial_mm_init(address_space_mem, MP_UART1_BASE, 2, pic[MP_UART1_IRQ],
                       1825000, serial_hds[0], DEVICE_NATIVE_ENDIAN);
J
Jan Kiszka 已提交
1623 1624
    }
    if (serial_hds[1]) {
1625 1626
        serial_mm_init(address_space_mem, MP_UART2_BASE, 2, pic[MP_UART2_IRQ],
                       1825000, serial_hds[1], DEVICE_NATIVE_ENDIAN);
J
Jan Kiszka 已提交
1627
    }
1628 1629

    /* Register flash */
G
Gerd Hoffmann 已提交
1630 1631
    dinfo = drive_get(IF_PFLASH, 0, 0);
    if (dinfo) {
1632
        BlockBackend *blk = blk_by_legacy_dinfo(dinfo);
1633

1634
        flash_size = blk_getlength(blk);
1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645
        if (flash_size != 8*1024*1024 && flash_size != 16*1024*1024 &&
            flash_size != 32*1024*1024) {
            fprintf(stderr, "Invalid flash image size\n");
            exit(1);
        }

        /*
         * The original U-Boot accesses the flash at 0xFE000000 instead of
         * 0xFF800000 (if there is 8 MB flash). So remap flash access if the
         * image is smaller than 32 MB.
         */
B
Blue Swirl 已提交
1646
#ifdef TARGET_WORDS_BIGENDIAN
J
Jan Kiszka 已提交
1647
        pflash_cfi02_register(0x100000000ULL-MP_FLASH_SIZE_MAX, NULL,
1648
                              "musicpal.flash", flash_size,
1649
                              blk, 0x10000, (flash_size + 0xffff) >> 16,
1650 1651
                              MP_FLASH_SIZE_MAX / flash_size,
                              2, 0x00BF, 0x236D, 0x0000, 0x0000,
1652
                              0x5555, 0x2AAA, 1);
B
Blue Swirl 已提交
1653
#else
J
Jan Kiszka 已提交
1654
        pflash_cfi02_register(0x100000000ULL-MP_FLASH_SIZE_MAX, NULL,
1655
                              "musicpal.flash", flash_size,
1656
                              blk, 0x10000, (flash_size + 0xffff) >> 16,
B
Blue Swirl 已提交
1657 1658
                              MP_FLASH_SIZE_MAX / flash_size,
                              2, 0x00BF, 0x236D, 0x0000, 0x0000,
1659
                              0x5555, 0x2AAA, 0);
B
Blue Swirl 已提交
1660 1661
#endif

1662
    }
1663
    sysbus_create_simple(TYPE_MV88W8618_FLASHCFG, MP_FLASHCFG_BASE, NULL);
1664

P
Paul Brook 已提交
1665
    qemu_check_nic_model(&nd_table[0], "mv88w8618");
1666
    dev = qdev_create(NULL, TYPE_MV88W8618_ETH);
1667
    qdev_set_nic_properties(dev, &nd_table[0]);
M
Markus Armbruster 已提交
1668
    qdev_init_nofail(dev);
1669 1670
    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, MP_ETH_BASE);
    sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[MP_ETH_IRQ]);
1671

P
Paul Brook 已提交
1672
    sysbus_create_simple("mv88w8618_wlan", MP_WLAN_BASE, NULL);
1673

P
Peter Maydell 已提交
1674
    sysbus_create_simple(TYPE_MUSICPAL_MISC, MP_MISC_BASE, NULL);
1675

1676 1677
    dev = sysbus_create_simple(TYPE_MUSICPAL_GPIO, MP_GPIO_BASE,
                               pic[MP_GPIO_IRQ]);
1678
    i2c_dev = sysbus_create_simple("gpio_i2c", -1, NULL);
A
Andreas Färber 已提交
1679
    i2c = (I2CBus *)qdev_get_child_bus(i2c_dev, "i2c");
1680

1681
    lcd_dev = sysbus_create_simple(TYPE_MUSICPAL_LCD, MP_LCD_BASE, NULL);
1682
    key_dev = sysbus_create_simple(TYPE_MUSICPAL_KEY, -1, NULL);
1683

1684
    /* I2C read data */
J
Jan Kiszka 已提交
1685 1686
    qdev_connect_gpio_out(i2c_dev, 0,
                          qdev_get_gpio_in(dev, MP_GPIO_I2C_DATA_BIT));
1687 1688 1689 1690 1691
    /* I2C data */
    qdev_connect_gpio_out(dev, 3, qdev_get_gpio_in(i2c_dev, 0));
    /* I2C clock */
    qdev_connect_gpio_out(dev, 4, qdev_get_gpio_in(i2c_dev, 1));

J
Jan Kiszka 已提交
1692
    for (i = 0; i < 3; i++) {
1693
        qdev_connect_gpio_out(dev, i, qdev_get_gpio_in(lcd_dev, i));
J
Jan Kiszka 已提交
1694
    }
J
Jan Kiszka 已提交
1695 1696 1697 1698 1699 1700
    for (i = 0; i < 4; i++) {
        qdev_connect_gpio_out(key_dev, i, qdev_get_gpio_in(dev, i + 8));
    }
    for (i = 4; i < 8; i++) {
        qdev_connect_gpio_out(key_dev, i, qdev_get_gpio_in(dev, i + 15));
    }
1701

1702 1703
    wm8750_dev = i2c_create_slave(i2c, "wm8750", MP_WM_ADDR);
    dev = qdev_create(NULL, "mv88w8618_audio");
1704
    s = SYS_BUS_DEVICE(dev);
1705
    qdev_prop_set_ptr(dev, "wm8750", wm8750_dev);
M
Markus Armbruster 已提交
1706
    qdev_init_nofail(dev);
1707 1708 1709
    sysbus_mmio_map(s, 0, MP_AUDIO_BASE);
    sysbus_connect_irq(s, 0, pic[MP_AUDIO_IRQ]);

1710 1711 1712 1713
    musicpal_binfo.ram_size = MP_RAM_DEFAULT_SIZE;
    musicpal_binfo.kernel_filename = kernel_filename;
    musicpal_binfo.kernel_cmdline = kernel_cmdline;
    musicpal_binfo.initrd_filename = initrd_filename;
1714
    arm_load_kernel(cpu, &musicpal_binfo);
1715 1716
}

1717
static void musicpal_machine_init(MachineClass *mc)
1718
{
1719 1720
    mc->desc = "Marvell 88w8618 / MusicPal (ARM926EJ-S)";
    mc->init = musicpal_init;
1721 1722
}

1723
DEFINE_MACHINE("musicpal", musicpal_machine_init)
1724

1725 1726 1727 1728 1729 1730 1731
static void mv88w8618_wlan_class_init(ObjectClass *klass, void *data)
{
    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);

    sdc->init = mv88w8618_wlan_init;
}

1732
static const TypeInfo mv88w8618_wlan_info = {
1733 1734 1735 1736
    .name          = "mv88w8618_wlan",
    .parent        = TYPE_SYS_BUS_DEVICE,
    .instance_size = sizeof(SysBusDevice),
    .class_init    = mv88w8618_wlan_class_init,
1737 1738
};

A
Andreas Färber 已提交
1739
static void musicpal_register_types(void)
P
Paul Brook 已提交
1740
{
1741 1742 1743 1744 1745 1746 1747 1748
    type_register_static(&mv88w8618_pic_info);
    type_register_static(&mv88w8618_pit_info);
    type_register_static(&mv88w8618_flashcfg_info);
    type_register_static(&mv88w8618_eth_info);
    type_register_static(&mv88w8618_wlan_info);
    type_register_static(&musicpal_lcd_info);
    type_register_static(&musicpal_gpio_info);
    type_register_static(&musicpal_key_info);
P
Peter Maydell 已提交
1749
    type_register_static(&musicpal_misc_info);
P
Paul Brook 已提交
1750 1751
}

A
Andreas Färber 已提交
1752
type_init(musicpal_register_types)