xref: /qemu/hw/arm/musicpal.c (revision d780d056f8acdee73a1c34d95733851d58aecd60)
124859b68Sbalrog /*
224859b68Sbalrog  * Marvell MV88W8618 / Freecom MusicPal emulation.
324859b68Sbalrog  *
424859b68Sbalrog  * Copyright (c) 2008 Jan Kiszka
524859b68Sbalrog  *
68e31bf38SMatthew Fernandez  * This code is licensed under the GNU GPL v2.
76b620ca3SPaolo Bonzini  *
86b620ca3SPaolo Bonzini  * Contributions after 2012-01-13 are licensed under the terms of the
96b620ca3SPaolo Bonzini  * GNU GPL, version 2 or (at your option) any later version.
1024859b68Sbalrog  */
1124859b68Sbalrog 
1212b16722SPeter Maydell #include "qemu/osdep.h"
13e0ee6413SPhilippe Mathieu-Daudé #include "qemu/units.h"
14da34e65cSMarkus Armbruster #include "qapi/error.h"
154771d756SPaolo Bonzini #include "cpu.h"
1683c9f4caSPaolo Bonzini #include "hw/sysbus.h"
17d6454270SMarkus Armbruster #include "migration/vmstate.h"
1812ec8bd5SPeter Maydell #include "hw/arm/boot.h"
191422e32dSPaolo Bonzini #include "net/net.h"
209c17d615SPaolo Bonzini #include "sysemu/sysemu.h"
2183c9f4caSPaolo Bonzini #include "hw/boards.h"
220d09e41aSPaolo Bonzini #include "hw/char/serial.h"
231de7afc9SPaolo Bonzini #include "qemu/timer.h"
2483c9f4caSPaolo Bonzini #include "hw/ptimer.h"
25a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
260d09e41aSPaolo Bonzini #include "hw/block/flash.h"
2728ecbaeeSPaolo Bonzini #include "ui/console.h"
280d09e41aSPaolo Bonzini #include "hw/i2c/i2c.h"
29da8df26dSPhilippe Mathieu-Daudé #include "hw/i2c/bitbang_i2c.h"
3064552b6bSMarkus Armbruster #include "hw/irq.h"
31498661ddSPhilippe Mathieu-Daudé #include "hw/or-irq.h"
327ab14c5aSPhilippe Mathieu-Daudé #include "hw/audio/wm8750.h"
33fa1d36dfSMarkus Armbruster #include "sysemu/block-backend.h"
3454d31236SMarkus Armbruster #include "sysemu/runstate.h"
3579ed6fd6SPhilippe Mathieu-Daudé #include "sysemu/dma.h"
3628ecbaeeSPaolo Bonzini #include "ui/pixel_ops.h"
373ed61312SIgor Mammedov #include "qemu/cutils.h"
38db1015e9SEduardo Habkost #include "qom/object.h"
396d81f488SPhilippe Mathieu-Daudé #include "hw/net/mv88w8618_eth.h"
40b8ab0303SMartin Kletzander #include "audio/audio.h"
41cc37d98bSRichard Henderson #include "qemu/error-report.h"
42*d780d056SPhilippe Mathieu-Daudé #include "target/arm/cpu-qom.h"
43cc37d98bSRichard Henderson 
44718ec0beSmalc #define MP_MISC_BASE            0x80002000
45718ec0beSmalc #define MP_MISC_SIZE            0x00001000
46718ec0beSmalc 
4724859b68Sbalrog #define MP_ETH_BASE             0x80008000
4824859b68Sbalrog 
49718ec0beSmalc #define MP_WLAN_BASE            0x8000C000
50718ec0beSmalc #define MP_WLAN_SIZE            0x00000800
51718ec0beSmalc 
5224859b68Sbalrog #define MP_UART1_BASE           0x8000C840
5324859b68Sbalrog #define MP_UART2_BASE           0x8000C940
5424859b68Sbalrog 
55718ec0beSmalc #define MP_GPIO_BASE            0x8000D000
56718ec0beSmalc #define MP_GPIO_SIZE            0x00001000
57718ec0beSmalc 
5824859b68Sbalrog #define MP_FLASHCFG_BASE        0x90006000
5924859b68Sbalrog #define MP_FLASHCFG_SIZE        0x00001000
6024859b68Sbalrog 
6124859b68Sbalrog #define MP_AUDIO_BASE           0x90007000
6224859b68Sbalrog 
6324859b68Sbalrog #define MP_PIC_BASE             0x90008000
6424859b68Sbalrog #define MP_PIC_SIZE             0x00001000
6524859b68Sbalrog 
6624859b68Sbalrog #define MP_PIT_BASE             0x90009000
6724859b68Sbalrog #define MP_PIT_SIZE             0x00001000
6824859b68Sbalrog 
6924859b68Sbalrog #define MP_LCD_BASE             0x9000c000
7024859b68Sbalrog #define MP_LCD_SIZE             0x00001000
7124859b68Sbalrog 
7224859b68Sbalrog #define MP_SRAM_BASE            0xC0000000
7324859b68Sbalrog #define MP_SRAM_SIZE            0x00020000
7424859b68Sbalrog 
7524859b68Sbalrog #define MP_RAM_DEFAULT_SIZE     32*1024*1024
7624859b68Sbalrog #define MP_FLASH_SIZE_MAX       32*1024*1024
7724859b68Sbalrog 
7824859b68Sbalrog #define MP_TIMER1_IRQ           4
79b47b50faSPaul Brook #define MP_TIMER2_IRQ           5
80b47b50faSPaul Brook #define MP_TIMER3_IRQ           6
8124859b68Sbalrog #define MP_TIMER4_IRQ           7
8224859b68Sbalrog #define MP_EHCI_IRQ             8
8324859b68Sbalrog #define MP_ETH_IRQ              9
84498661ddSPhilippe Mathieu-Daudé #define MP_UART_SHARED_IRQ      11
8524859b68Sbalrog #define MP_GPIO_IRQ             12
8624859b68Sbalrog #define MP_RTC_IRQ              28
8724859b68Sbalrog #define MP_AUDIO_IRQ            30
8824859b68Sbalrog 
8924859b68Sbalrog /* Wolfson 8750 I2C address */
9064258229SJan Kiszka #define MP_WM_ADDR              0x1A
9124859b68Sbalrog 
9224859b68Sbalrog /* LCD register offsets */
9324859b68Sbalrog #define MP_LCD_IRQCTRL          0x180
9424859b68Sbalrog #define MP_LCD_IRQSTAT          0x184
9524859b68Sbalrog #define MP_LCD_SPICTRL          0x1ac
9624859b68Sbalrog #define MP_LCD_INST             0x1bc
9724859b68Sbalrog #define MP_LCD_DATA             0x1c0
9824859b68Sbalrog 
9924859b68Sbalrog /* Mode magics */
10024859b68Sbalrog #define MP_LCD_SPI_DATA         0x00100011
10124859b68Sbalrog #define MP_LCD_SPI_CMD          0x00104011
10224859b68Sbalrog #define MP_LCD_SPI_INVALID      0x00000000
10324859b68Sbalrog 
104b3db996fSStefan Weil /* Commands */
10524859b68Sbalrog #define MP_LCD_INST_SETPAGE0    0xB0
10624859b68Sbalrog /* ... */
10724859b68Sbalrog #define MP_LCD_INST_SETPAGE7    0xB7
10824859b68Sbalrog 
10924859b68Sbalrog #define MP_LCD_TEXTCOLOR        0xe0e0ff /* RRGGBB */
11024859b68Sbalrog 
1112cca58fdSAndreas Färber #define TYPE_MUSICPAL_LCD "musicpal_lcd"
1128063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(musicpal_lcd_state, MUSICPAL_LCD)
1132cca58fdSAndreas Färber 
114db1015e9SEduardo Habkost struct musicpal_lcd_state {
1152cca58fdSAndreas Färber     /*< private >*/
1162cca58fdSAndreas Färber     SysBusDevice parent_obj;
1172cca58fdSAndreas Färber     /*< public >*/
1182cca58fdSAndreas Färber 
11919b4a424SAvi Kivity     MemoryRegion iomem;
120343ec8e4SBenoit Canet     uint32_t brightness;
12124859b68Sbalrog     uint32_t mode;
12224859b68Sbalrog     uint32_t irqctrl;
123d5b61dddSJan Kiszka     uint32_t page;
124d5b61dddSJan Kiszka     uint32_t page_off;
125c78f7137SGerd Hoffmann     QemuConsole *con;
12624859b68Sbalrog     uint8_t video_ram[128*64/8];
127db1015e9SEduardo Habkost };
12824859b68Sbalrog 
129343ec8e4SBenoit Canet static uint8_t scale_lcd_color(musicpal_lcd_state *s, uint8_t col)
13024859b68Sbalrog {
131343ec8e4SBenoit Canet     switch (s->brightness) {
132343ec8e4SBenoit Canet     case 7:
13324859b68Sbalrog         return col;
134343ec8e4SBenoit Canet     case 0:
135343ec8e4SBenoit Canet         return 0;
136343ec8e4SBenoit Canet     default:
137343ec8e4SBenoit Canet         return (col * s->brightness) / 7;
13824859b68Sbalrog     }
13924859b68Sbalrog }
14024859b68Sbalrog 
1419aee50eeSPeter Maydell static inline void set_lcd_pixel32(musicpal_lcd_state *s,
1429aee50eeSPeter Maydell                                    int x, int y, uint32_t col)
1439aee50eeSPeter Maydell {
1449aee50eeSPeter Maydell     int dx, dy;
1459aee50eeSPeter Maydell     DisplaySurface *surface = qemu_console_surface(s->con);
1469aee50eeSPeter Maydell     uint32_t *pixel =
1479aee50eeSPeter Maydell         &((uint32_t *) surface_data(surface))[(y * 128 * 3 + x) * 3];
1489aee50eeSPeter Maydell 
1499aee50eeSPeter Maydell     for (dy = 0; dy < 3; dy++, pixel += 127 * 3) {
1509aee50eeSPeter Maydell         for (dx = 0; dx < 3; dx++, pixel++) {
1519aee50eeSPeter Maydell             *pixel = col;
1520266f2c7Sbalrog         }
1539aee50eeSPeter Maydell     }
1549aee50eeSPeter Maydell }
15524859b68Sbalrog 
15624859b68Sbalrog static void lcd_refresh(void *opaque)
15724859b68Sbalrog {
15824859b68Sbalrog     musicpal_lcd_state *s = opaque;
1590266f2c7Sbalrog     int x, y, col;
16024859b68Sbalrog 
1619aee50eeSPeter Maydell     col = rgb_to_pixel32(scale_lcd_color(s, (MP_LCD_TEXTCOLOR >> 16) & 0xff),
1629aee50eeSPeter Maydell                          scale_lcd_color(s, (MP_LCD_TEXTCOLOR >> 8) & 0xff),
1639aee50eeSPeter Maydell                          scale_lcd_color(s, MP_LCD_TEXTCOLOR & 0xff));
1649aee50eeSPeter Maydell     for (x = 0; x < 128; x++) {
1659aee50eeSPeter Maydell         for (y = 0; y < 64; y++) {
1669aee50eeSPeter Maydell             if (s->video_ram[x + (y / 8) * 128] & (1 << (y % 8))) {
1679aee50eeSPeter Maydell                 set_lcd_pixel32(s, x, y, col);
1689aee50eeSPeter Maydell             } else {
1699aee50eeSPeter Maydell                 set_lcd_pixel32(s, x, y, 0);
1709aee50eeSPeter Maydell             }
1719aee50eeSPeter Maydell         }
1720266f2c7Sbalrog     }
17324859b68Sbalrog 
174c78f7137SGerd Hoffmann     dpy_gfx_update(s->con, 0, 0, 128*3, 64*3);
17524859b68Sbalrog }
17624859b68Sbalrog 
177167bc3d2Sbalrog static void lcd_invalidate(void *opaque)
178167bc3d2Sbalrog {
179167bc3d2Sbalrog }
180167bc3d2Sbalrog 
1812c79fed3SStefan Weil static void musicpal_lcd_gpio_brightness_in(void *opaque, int irq, int level)
182343ec8e4SBenoit Canet {
183243cd13cSJan Kiszka     musicpal_lcd_state *s = opaque;
184343ec8e4SBenoit Canet     s->brightness &= ~(1 << irq);
185343ec8e4SBenoit Canet     s->brightness |= level << irq;
186343ec8e4SBenoit Canet }
187343ec8e4SBenoit Canet 
188a8170e5eSAvi Kivity static uint64_t musicpal_lcd_read(void *opaque, hwaddr offset,
18919b4a424SAvi Kivity                                   unsigned size)
19024859b68Sbalrog {
19124859b68Sbalrog     musicpal_lcd_state *s = opaque;
19224859b68Sbalrog 
19324859b68Sbalrog     switch (offset) {
19424859b68Sbalrog     case MP_LCD_IRQCTRL:
19524859b68Sbalrog         return s->irqctrl;
19624859b68Sbalrog 
19724859b68Sbalrog     default:
19824859b68Sbalrog         return 0;
19924859b68Sbalrog     }
20024859b68Sbalrog }
20124859b68Sbalrog 
202a8170e5eSAvi Kivity static void musicpal_lcd_write(void *opaque, hwaddr offset,
20319b4a424SAvi Kivity                                uint64_t value, unsigned size)
20424859b68Sbalrog {
20524859b68Sbalrog     musicpal_lcd_state *s = opaque;
20624859b68Sbalrog 
20724859b68Sbalrog     switch (offset) {
20824859b68Sbalrog     case MP_LCD_IRQCTRL:
20924859b68Sbalrog         s->irqctrl = value;
21024859b68Sbalrog         break;
21124859b68Sbalrog 
21224859b68Sbalrog     case MP_LCD_SPICTRL:
21349fedd0dSJan Kiszka         if (value == MP_LCD_SPI_DATA || value == MP_LCD_SPI_CMD) {
21424859b68Sbalrog             s->mode = value;
21549fedd0dSJan Kiszka         } else {
21624859b68Sbalrog             s->mode = MP_LCD_SPI_INVALID;
21749fedd0dSJan Kiszka         }
21824859b68Sbalrog         break;
21924859b68Sbalrog 
22024859b68Sbalrog     case MP_LCD_INST:
22124859b68Sbalrog         if (value >= MP_LCD_INST_SETPAGE0 && value <= MP_LCD_INST_SETPAGE7) {
22224859b68Sbalrog             s->page = value - MP_LCD_INST_SETPAGE0;
22324859b68Sbalrog             s->page_off = 0;
22424859b68Sbalrog         }
22524859b68Sbalrog         break;
22624859b68Sbalrog 
22724859b68Sbalrog     case MP_LCD_DATA:
22824859b68Sbalrog         if (s->mode == MP_LCD_SPI_CMD) {
22924859b68Sbalrog             if (value >= MP_LCD_INST_SETPAGE0 &&
23024859b68Sbalrog                 value <= MP_LCD_INST_SETPAGE7) {
23124859b68Sbalrog                 s->page = value - MP_LCD_INST_SETPAGE0;
23224859b68Sbalrog                 s->page_off = 0;
23324859b68Sbalrog             }
23424859b68Sbalrog         } else if (s->mode == MP_LCD_SPI_DATA) {
23524859b68Sbalrog             s->video_ram[s->page*128 + s->page_off] = value;
23624859b68Sbalrog             s->page_off = (s->page_off + 1) & 127;
23724859b68Sbalrog         }
23824859b68Sbalrog         break;
23924859b68Sbalrog     }
24024859b68Sbalrog }
24124859b68Sbalrog 
24219b4a424SAvi Kivity static const MemoryRegionOps musicpal_lcd_ops = {
24319b4a424SAvi Kivity     .read = musicpal_lcd_read,
24419b4a424SAvi Kivity     .write = musicpal_lcd_write,
24519b4a424SAvi Kivity     .endianness = DEVICE_NATIVE_ENDIAN,
24624859b68Sbalrog };
24724859b68Sbalrog 
248380cd056SGerd Hoffmann static const GraphicHwOps musicpal_gfx_ops = {
249380cd056SGerd Hoffmann     .invalidate  = lcd_invalidate,
250380cd056SGerd Hoffmann     .gfx_update  = lcd_refresh,
251380cd056SGerd Hoffmann };
252380cd056SGerd Hoffmann 
253ece71994Sxiaoqiang zhao static void musicpal_lcd_realize(DeviceState *dev, Error **errp)
25424859b68Sbalrog {
255ece71994Sxiaoqiang zhao     musicpal_lcd_state *s = MUSICPAL_LCD(dev);
256ece71994Sxiaoqiang zhao     s->con = graphic_console_init(dev, 0, &musicpal_gfx_ops, s);
257ece71994Sxiaoqiang zhao     qemu_console_resize(s->con, 128 * 3, 64 * 3);
258ece71994Sxiaoqiang zhao }
259ece71994Sxiaoqiang zhao 
260ece71994Sxiaoqiang zhao static void musicpal_lcd_init(Object *obj)
261ece71994Sxiaoqiang zhao {
262ece71994Sxiaoqiang zhao     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
2632cca58fdSAndreas Färber     DeviceState *dev = DEVICE(sbd);
2642cca58fdSAndreas Färber     musicpal_lcd_state *s = MUSICPAL_LCD(dev);
26524859b68Sbalrog 
266343ec8e4SBenoit Canet     s->brightness = 7;
267343ec8e4SBenoit Canet 
268ece71994Sxiaoqiang zhao     memory_region_init_io(&s->iomem, obj, &musicpal_lcd_ops, s,
26919b4a424SAvi Kivity                           "musicpal-lcd", MP_LCD_SIZE);
2702cca58fdSAndreas Färber     sysbus_init_mmio(sbd, &s->iomem);
27124859b68Sbalrog 
2722cca58fdSAndreas Färber     qdev_init_gpio_in(dev, musicpal_lcd_gpio_brightness_in, 3);
27324859b68Sbalrog }
27424859b68Sbalrog 
275d5b61dddSJan Kiszka static const VMStateDescription musicpal_lcd_vmsd = {
276d5b61dddSJan Kiszka     .name = "musicpal_lcd",
277d5b61dddSJan Kiszka     .version_id = 1,
278d5b61dddSJan Kiszka     .minimum_version_id = 1,
279607ef570SRichard Henderson     .fields = (const VMStateField[]) {
280d5b61dddSJan Kiszka         VMSTATE_UINT32(brightness, musicpal_lcd_state),
281d5b61dddSJan Kiszka         VMSTATE_UINT32(mode, musicpal_lcd_state),
282d5b61dddSJan Kiszka         VMSTATE_UINT32(irqctrl, musicpal_lcd_state),
283d5b61dddSJan Kiszka         VMSTATE_UINT32(page, musicpal_lcd_state),
284d5b61dddSJan Kiszka         VMSTATE_UINT32(page_off, musicpal_lcd_state),
285d5b61dddSJan Kiszka         VMSTATE_BUFFER(video_ram, musicpal_lcd_state),
286d5b61dddSJan Kiszka         VMSTATE_END_OF_LIST()
287d5b61dddSJan Kiszka     }
288d5b61dddSJan Kiszka };
289d5b61dddSJan Kiszka 
290999e12bbSAnthony Liguori static void musicpal_lcd_class_init(ObjectClass *klass, void *data)
291999e12bbSAnthony Liguori {
29239bffca2SAnthony Liguori     DeviceClass *dc = DEVICE_CLASS(klass);
293999e12bbSAnthony Liguori 
29439bffca2SAnthony Liguori     dc->vmsd = &musicpal_lcd_vmsd;
295ece71994Sxiaoqiang zhao     dc->realize = musicpal_lcd_realize;
296999e12bbSAnthony Liguori }
297999e12bbSAnthony Liguori 
2988c43a6f0SAndreas Färber static const TypeInfo musicpal_lcd_info = {
2992cca58fdSAndreas Färber     .name          = TYPE_MUSICPAL_LCD,
30039bffca2SAnthony Liguori     .parent        = TYPE_SYS_BUS_DEVICE,
30139bffca2SAnthony Liguori     .instance_size = sizeof(musicpal_lcd_state),
302ece71994Sxiaoqiang zhao     .instance_init = musicpal_lcd_init,
303999e12bbSAnthony Liguori     .class_init    = musicpal_lcd_class_init,
304d5b61dddSJan Kiszka };
305d5b61dddSJan Kiszka 
30624859b68Sbalrog /* PIC register offsets */
30724859b68Sbalrog #define MP_PIC_STATUS           0x00
30824859b68Sbalrog #define MP_PIC_ENABLE_SET       0x08
30924859b68Sbalrog #define MP_PIC_ENABLE_CLR       0x0C
31024859b68Sbalrog 
311c7bd0fd9SAndreas Färber #define TYPE_MV88W8618_PIC "mv88w8618_pic"
3128063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(mv88w8618_pic_state, MV88W8618_PIC)
313c7bd0fd9SAndreas Färber 
314db1015e9SEduardo Habkost struct mv88w8618_pic_state {
315c7bd0fd9SAndreas Färber     /*< private >*/
316c7bd0fd9SAndreas Färber     SysBusDevice parent_obj;
317c7bd0fd9SAndreas Färber     /*< public >*/
318c7bd0fd9SAndreas Färber 
31919b4a424SAvi Kivity     MemoryRegion iomem;
32024859b68Sbalrog     uint32_t level;
32124859b68Sbalrog     uint32_t enabled;
32224859b68Sbalrog     qemu_irq parent_irq;
323db1015e9SEduardo Habkost };
32424859b68Sbalrog 
32524859b68Sbalrog static void mv88w8618_pic_update(mv88w8618_pic_state *s)
32624859b68Sbalrog {
32724859b68Sbalrog     qemu_set_irq(s->parent_irq, (s->level & s->enabled));
32824859b68Sbalrog }
32924859b68Sbalrog 
33024859b68Sbalrog static void mv88w8618_pic_set_irq(void *opaque, int irq, int level)
33124859b68Sbalrog {
33224859b68Sbalrog     mv88w8618_pic_state *s = opaque;
33324859b68Sbalrog 
33449fedd0dSJan Kiszka     if (level) {
33524859b68Sbalrog         s->level |= 1 << irq;
33649fedd0dSJan Kiszka     } else {
33724859b68Sbalrog         s->level &= ~(1 << irq);
33849fedd0dSJan Kiszka     }
33924859b68Sbalrog     mv88w8618_pic_update(s);
34024859b68Sbalrog }
34124859b68Sbalrog 
342a8170e5eSAvi Kivity static uint64_t mv88w8618_pic_read(void *opaque, hwaddr offset,
34319b4a424SAvi Kivity                                    unsigned size)
34424859b68Sbalrog {
34524859b68Sbalrog     mv88w8618_pic_state *s = opaque;
34624859b68Sbalrog 
34724859b68Sbalrog     switch (offset) {
34824859b68Sbalrog     case MP_PIC_STATUS:
34924859b68Sbalrog         return s->level & s->enabled;
35024859b68Sbalrog 
35124859b68Sbalrog     default:
35224859b68Sbalrog         return 0;
35324859b68Sbalrog     }
35424859b68Sbalrog }
35524859b68Sbalrog 
356a8170e5eSAvi Kivity static void mv88w8618_pic_write(void *opaque, hwaddr offset,
35719b4a424SAvi Kivity                                 uint64_t value, unsigned size)
35824859b68Sbalrog {
35924859b68Sbalrog     mv88w8618_pic_state *s = opaque;
36024859b68Sbalrog 
36124859b68Sbalrog     switch (offset) {
36224859b68Sbalrog     case MP_PIC_ENABLE_SET:
36324859b68Sbalrog         s->enabled |= value;
36424859b68Sbalrog         break;
36524859b68Sbalrog 
36624859b68Sbalrog     case MP_PIC_ENABLE_CLR:
36724859b68Sbalrog         s->enabled &= ~value;
36824859b68Sbalrog         s->level &= ~value;
36924859b68Sbalrog         break;
37024859b68Sbalrog     }
37124859b68Sbalrog     mv88w8618_pic_update(s);
37224859b68Sbalrog }
37324859b68Sbalrog 
374d5b61dddSJan Kiszka static void mv88w8618_pic_reset(DeviceState *d)
37524859b68Sbalrog {
376c7bd0fd9SAndreas Färber     mv88w8618_pic_state *s = MV88W8618_PIC(d);
37724859b68Sbalrog 
37824859b68Sbalrog     s->level = 0;
37924859b68Sbalrog     s->enabled = 0;
38024859b68Sbalrog }
38124859b68Sbalrog 
38219b4a424SAvi Kivity static const MemoryRegionOps mv88w8618_pic_ops = {
38319b4a424SAvi Kivity     .read = mv88w8618_pic_read,
38419b4a424SAvi Kivity     .write = mv88w8618_pic_write,
38519b4a424SAvi Kivity     .endianness = DEVICE_NATIVE_ENDIAN,
38624859b68Sbalrog };
38724859b68Sbalrog 
388ece71994Sxiaoqiang zhao static void mv88w8618_pic_init(Object *obj)
38924859b68Sbalrog {
390ece71994Sxiaoqiang zhao     SysBusDevice *dev = SYS_BUS_DEVICE(obj);
391c7bd0fd9SAndreas Färber     mv88w8618_pic_state *s = MV88W8618_PIC(dev);
39224859b68Sbalrog 
393c7bd0fd9SAndreas Färber     qdev_init_gpio_in(DEVICE(dev), mv88w8618_pic_set_irq, 32);
394b47b50faSPaul Brook     sysbus_init_irq(dev, &s->parent_irq);
395ece71994Sxiaoqiang zhao     memory_region_init_io(&s->iomem, obj, &mv88w8618_pic_ops, s,
39619b4a424SAvi Kivity                           "musicpal-pic", MP_PIC_SIZE);
397750ecd44SAvi Kivity     sysbus_init_mmio(dev, &s->iomem);
39824859b68Sbalrog }
39924859b68Sbalrog 
400d5b61dddSJan Kiszka static const VMStateDescription mv88w8618_pic_vmsd = {
401d5b61dddSJan Kiszka     .name = "mv88w8618_pic",
402d5b61dddSJan Kiszka     .version_id = 1,
403d5b61dddSJan Kiszka     .minimum_version_id = 1,
404607ef570SRichard Henderson     .fields = (const VMStateField[]) {
405d5b61dddSJan Kiszka         VMSTATE_UINT32(level, mv88w8618_pic_state),
406d5b61dddSJan Kiszka         VMSTATE_UINT32(enabled, mv88w8618_pic_state),
407d5b61dddSJan Kiszka         VMSTATE_END_OF_LIST()
408d5b61dddSJan Kiszka     }
409d5b61dddSJan Kiszka };
410d5b61dddSJan Kiszka 
411999e12bbSAnthony Liguori static void mv88w8618_pic_class_init(ObjectClass *klass, void *data)
412999e12bbSAnthony Liguori {
41339bffca2SAnthony Liguori     DeviceClass *dc = DEVICE_CLASS(klass);
414999e12bbSAnthony Liguori 
41539bffca2SAnthony Liguori     dc->reset = mv88w8618_pic_reset;
41639bffca2SAnthony Liguori     dc->vmsd = &mv88w8618_pic_vmsd;
417999e12bbSAnthony Liguori }
418999e12bbSAnthony Liguori 
4198c43a6f0SAndreas Färber static const TypeInfo mv88w8618_pic_info = {
420c7bd0fd9SAndreas Färber     .name          = TYPE_MV88W8618_PIC,
42139bffca2SAnthony Liguori     .parent        = TYPE_SYS_BUS_DEVICE,
42239bffca2SAnthony Liguori     .instance_size = sizeof(mv88w8618_pic_state),
423ece71994Sxiaoqiang zhao     .instance_init = mv88w8618_pic_init,
424999e12bbSAnthony Liguori     .class_init    = mv88w8618_pic_class_init,
425d5b61dddSJan Kiszka };
426d5b61dddSJan Kiszka 
42724859b68Sbalrog /* PIT register offsets */
42824859b68Sbalrog #define MP_PIT_TIMER1_LENGTH    0x00
42924859b68Sbalrog /* ... */
43024859b68Sbalrog #define MP_PIT_TIMER4_LENGTH    0x0C
43124859b68Sbalrog #define MP_PIT_CONTROL          0x10
43224859b68Sbalrog #define MP_PIT_TIMER1_VALUE     0x14
43324859b68Sbalrog /* ... */
43424859b68Sbalrog #define MP_PIT_TIMER4_VALUE     0x20
43524859b68Sbalrog #define MP_BOARD_RESET          0x34
43624859b68Sbalrog 
43724859b68Sbalrog /* Magic board reset value (probably some watchdog behind it) */
43824859b68Sbalrog #define MP_BOARD_RESET_MAGIC    0x10000
43924859b68Sbalrog 
44024859b68Sbalrog typedef struct mv88w8618_timer_state {
441b47b50faSPaul Brook     ptimer_state *ptimer;
44224859b68Sbalrog     uint32_t limit;
44324859b68Sbalrog     int freq;
44424859b68Sbalrog     qemu_irq irq;
44524859b68Sbalrog } mv88w8618_timer_state;
44624859b68Sbalrog 
4474adc8541SAndreas Färber #define TYPE_MV88W8618_PIT "mv88w8618_pit"
4488063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(mv88w8618_pit_state, MV88W8618_PIT)
4494adc8541SAndreas Färber 
450db1015e9SEduardo Habkost struct mv88w8618_pit_state {
4514adc8541SAndreas Färber     /*< private >*/
4524adc8541SAndreas Färber     SysBusDevice parent_obj;
4534adc8541SAndreas Färber     /*< public >*/
4544adc8541SAndreas Färber 
45519b4a424SAvi Kivity     MemoryRegion iomem;
456b47b50faSPaul Brook     mv88w8618_timer_state timer[4];
457db1015e9SEduardo Habkost };
45824859b68Sbalrog 
45924859b68Sbalrog static void mv88w8618_timer_tick(void *opaque)
46024859b68Sbalrog {
46124859b68Sbalrog     mv88w8618_timer_state *s = opaque;
46224859b68Sbalrog 
46324859b68Sbalrog     qemu_irq_raise(s->irq);
46424859b68Sbalrog }
46524859b68Sbalrog 
466b47b50faSPaul Brook static void mv88w8618_timer_init(SysBusDevice *dev, mv88w8618_timer_state *s,
467b47b50faSPaul Brook                                  uint32_t freq)
46824859b68Sbalrog {
469b47b50faSPaul Brook     sysbus_init_irq(dev, &s->irq);
47024859b68Sbalrog     s->freq = freq;
47124859b68Sbalrog 
4729598c1bbSPeter Maydell     s->ptimer = ptimer_init(mv88w8618_timer_tick, s, PTIMER_POLICY_LEGACY);
47324859b68Sbalrog }
47424859b68Sbalrog 
475a8170e5eSAvi Kivity static uint64_t mv88w8618_pit_read(void *opaque, hwaddr offset,
47619b4a424SAvi Kivity                                    unsigned size)
47724859b68Sbalrog {
47824859b68Sbalrog     mv88w8618_pit_state *s = opaque;
47924859b68Sbalrog     mv88w8618_timer_state *t;
48024859b68Sbalrog 
48124859b68Sbalrog     switch (offset) {
48224859b68Sbalrog     case MP_PIT_TIMER1_VALUE ... MP_PIT_TIMER4_VALUE:
483b47b50faSPaul Brook         t = &s->timer[(offset-MP_PIT_TIMER1_VALUE) >> 2];
484b47b50faSPaul Brook         return ptimer_get_count(t->ptimer);
48524859b68Sbalrog 
48624859b68Sbalrog     default:
48724859b68Sbalrog         return 0;
48824859b68Sbalrog     }
48924859b68Sbalrog }
49024859b68Sbalrog 
491a8170e5eSAvi Kivity static void mv88w8618_pit_write(void *opaque, hwaddr offset,
49219b4a424SAvi Kivity                                 uint64_t value, unsigned size)
49324859b68Sbalrog {
49424859b68Sbalrog     mv88w8618_pit_state *s = opaque;
49524859b68Sbalrog     mv88w8618_timer_state *t;
49624859b68Sbalrog     int i;
49724859b68Sbalrog 
49824859b68Sbalrog     switch (offset) {
49924859b68Sbalrog     case MP_PIT_TIMER1_LENGTH ... MP_PIT_TIMER4_LENGTH:
500b47b50faSPaul Brook         t = &s->timer[offset >> 2];
50124859b68Sbalrog         t->limit = value;
502d8052a2eSPeter Maydell         ptimer_transaction_begin(t->ptimer);
503c88d6bdeSJan Kiszka         if (t->limit > 0) {
504b47b50faSPaul Brook             ptimer_set_limit(t->ptimer, t->limit, 1);
505c88d6bdeSJan Kiszka         } else {
506c88d6bdeSJan Kiszka             ptimer_stop(t->ptimer);
507c88d6bdeSJan Kiszka         }
508d8052a2eSPeter Maydell         ptimer_transaction_commit(t->ptimer);
50924859b68Sbalrog         break;
51024859b68Sbalrog 
51124859b68Sbalrog     case MP_PIT_CONTROL:
51224859b68Sbalrog         for (i = 0; i < 4; i++) {
513b47b50faSPaul Brook             t = &s->timer[i];
514d8052a2eSPeter Maydell             ptimer_transaction_begin(t->ptimer);
515c88d6bdeSJan Kiszka             if (value & 0xf && t->limit > 0) {
516b47b50faSPaul Brook                 ptimer_set_limit(t->ptimer, t->limit, 0);
517b47b50faSPaul Brook                 ptimer_set_freq(t->ptimer, t->freq);
518b47b50faSPaul Brook                 ptimer_run(t->ptimer, 0);
519c88d6bdeSJan Kiszka             } else {
520c88d6bdeSJan Kiszka                 ptimer_stop(t->ptimer);
52124859b68Sbalrog             }
522d8052a2eSPeter Maydell             ptimer_transaction_commit(t->ptimer);
52324859b68Sbalrog             value >>= 4;
52424859b68Sbalrog         }
52524859b68Sbalrog         break;
52624859b68Sbalrog 
52724859b68Sbalrog     case MP_BOARD_RESET:
52849fedd0dSJan Kiszka         if (value == MP_BOARD_RESET_MAGIC) {
529cf83f140SEric Blake             qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
53049fedd0dSJan Kiszka         }
53124859b68Sbalrog         break;
53224859b68Sbalrog     }
53324859b68Sbalrog }
53424859b68Sbalrog 
535d5b61dddSJan Kiszka static void mv88w8618_pit_reset(DeviceState *d)
536c88d6bdeSJan Kiszka {
5374adc8541SAndreas Färber     mv88w8618_pit_state *s = MV88W8618_PIT(d);
538c88d6bdeSJan Kiszka     int i;
539c88d6bdeSJan Kiszka 
540c88d6bdeSJan Kiszka     for (i = 0; i < 4; i++) {
541d8052a2eSPeter Maydell         mv88w8618_timer_state *t = &s->timer[i];
542d8052a2eSPeter Maydell         ptimer_transaction_begin(t->ptimer);
543d8052a2eSPeter Maydell         ptimer_stop(t->ptimer);
544d8052a2eSPeter Maydell         ptimer_transaction_commit(t->ptimer);
545d8052a2eSPeter Maydell         t->limit = 0;
546c88d6bdeSJan Kiszka     }
547c88d6bdeSJan Kiszka }
548c88d6bdeSJan Kiszka 
54919b4a424SAvi Kivity static const MemoryRegionOps mv88w8618_pit_ops = {
55019b4a424SAvi Kivity     .read = mv88w8618_pit_read,
55119b4a424SAvi Kivity     .write = mv88w8618_pit_write,
55219b4a424SAvi Kivity     .endianness = DEVICE_NATIVE_ENDIAN,
55324859b68Sbalrog };
55424859b68Sbalrog 
555ece71994Sxiaoqiang zhao static void mv88w8618_pit_init(Object *obj)
55624859b68Sbalrog {
557ece71994Sxiaoqiang zhao     SysBusDevice *dev = SYS_BUS_DEVICE(obj);
5584adc8541SAndreas Färber     mv88w8618_pit_state *s = MV88W8618_PIT(dev);
559b47b50faSPaul Brook     int i;
56024859b68Sbalrog 
56124859b68Sbalrog     /* Letting them all run at 1 MHz is likely just a pragmatic
56224859b68Sbalrog      * simplification. */
563b47b50faSPaul Brook     for (i = 0; i < 4; i++) {
564b47b50faSPaul Brook         mv88w8618_timer_init(dev, &s->timer[i], 1000000);
565b47b50faSPaul Brook     }
56624859b68Sbalrog 
567ece71994Sxiaoqiang zhao     memory_region_init_io(&s->iomem, obj, &mv88w8618_pit_ops, s,
56819b4a424SAvi Kivity                           "musicpal-pit", MP_PIT_SIZE);
569750ecd44SAvi Kivity     sysbus_init_mmio(dev, &s->iomem);
57024859b68Sbalrog }
57124859b68Sbalrog 
572a4bc0334SGan Qixin static void mv88w8618_pit_finalize(Object *obj)
573a4bc0334SGan Qixin {
574a4bc0334SGan Qixin     SysBusDevice *dev = SYS_BUS_DEVICE(obj);
575a4bc0334SGan Qixin     mv88w8618_pit_state *s = MV88W8618_PIT(dev);
576a4bc0334SGan Qixin     int i;
577a4bc0334SGan Qixin 
578a4bc0334SGan Qixin     for (i = 0; i < 4; i++) {
579a4bc0334SGan Qixin         ptimer_free(s->timer[i].ptimer);
580a4bc0334SGan Qixin     }
581a4bc0334SGan Qixin }
582a4bc0334SGan Qixin 
583d5b61dddSJan Kiszka static const VMStateDescription mv88w8618_timer_vmsd = {
584d5b61dddSJan Kiszka     .name = "timer",
585d5b61dddSJan Kiszka     .version_id = 1,
586d5b61dddSJan Kiszka     .minimum_version_id = 1,
587607ef570SRichard Henderson     .fields = (const VMStateField[]) {
588d5b61dddSJan Kiszka         VMSTATE_PTIMER(ptimer, mv88w8618_timer_state),
589d5b61dddSJan Kiszka         VMSTATE_UINT32(limit, mv88w8618_timer_state),
590d5b61dddSJan Kiszka         VMSTATE_END_OF_LIST()
591d5b61dddSJan Kiszka     }
592d5b61dddSJan Kiszka };
593d5b61dddSJan Kiszka 
594d5b61dddSJan Kiszka static const VMStateDescription mv88w8618_pit_vmsd = {
595d5b61dddSJan Kiszka     .name = "mv88w8618_pit",
596d5b61dddSJan Kiszka     .version_id = 1,
597d5b61dddSJan Kiszka     .minimum_version_id = 1,
598607ef570SRichard Henderson     .fields = (const VMStateField[]) {
599d5b61dddSJan Kiszka         VMSTATE_STRUCT_ARRAY(timer, mv88w8618_pit_state, 4, 1,
600d5b61dddSJan Kiszka                              mv88w8618_timer_vmsd, mv88w8618_timer_state),
601d5b61dddSJan Kiszka         VMSTATE_END_OF_LIST()
602d5b61dddSJan Kiszka     }
603d5b61dddSJan Kiszka };
604d5b61dddSJan Kiszka 
605999e12bbSAnthony Liguori static void mv88w8618_pit_class_init(ObjectClass *klass, void *data)
606999e12bbSAnthony Liguori {
60739bffca2SAnthony Liguori     DeviceClass *dc = DEVICE_CLASS(klass);
608999e12bbSAnthony Liguori 
60939bffca2SAnthony Liguori     dc->reset = mv88w8618_pit_reset;
61039bffca2SAnthony Liguori     dc->vmsd = &mv88w8618_pit_vmsd;
611999e12bbSAnthony Liguori }
612999e12bbSAnthony Liguori 
6138c43a6f0SAndreas Färber static const TypeInfo mv88w8618_pit_info = {
6144adc8541SAndreas Färber     .name          = TYPE_MV88W8618_PIT,
61539bffca2SAnthony Liguori     .parent        = TYPE_SYS_BUS_DEVICE,
61639bffca2SAnthony Liguori     .instance_size = sizeof(mv88w8618_pit_state),
617ece71994Sxiaoqiang zhao     .instance_init = mv88w8618_pit_init,
618a4bc0334SGan Qixin     .instance_finalize = mv88w8618_pit_finalize,
619999e12bbSAnthony Liguori     .class_init    = mv88w8618_pit_class_init,
620c88d6bdeSJan Kiszka };
621c88d6bdeSJan Kiszka 
62224859b68Sbalrog /* Flash config register offsets */
62324859b68Sbalrog #define MP_FLASHCFG_CFGR0    0x04
62424859b68Sbalrog 
6255952b01cSAndreas Färber #define TYPE_MV88W8618_FLASHCFG "mv88w8618_flashcfg"
6268063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(mv88w8618_flashcfg_state, MV88W8618_FLASHCFG)
6275952b01cSAndreas Färber 
628db1015e9SEduardo Habkost struct mv88w8618_flashcfg_state {
6295952b01cSAndreas Färber     /*< private >*/
6305952b01cSAndreas Färber     SysBusDevice parent_obj;
6315952b01cSAndreas Färber     /*< public >*/
6325952b01cSAndreas Färber 
63319b4a424SAvi Kivity     MemoryRegion iomem;
63424859b68Sbalrog     uint32_t cfgr0;
635db1015e9SEduardo Habkost };
63624859b68Sbalrog 
63719b4a424SAvi Kivity static uint64_t mv88w8618_flashcfg_read(void *opaque,
638a8170e5eSAvi Kivity                                         hwaddr offset,
63919b4a424SAvi Kivity                                         unsigned size)
64024859b68Sbalrog {
64124859b68Sbalrog     mv88w8618_flashcfg_state *s = opaque;
64224859b68Sbalrog 
64324859b68Sbalrog     switch (offset) {
64424859b68Sbalrog     case MP_FLASHCFG_CFGR0:
64524859b68Sbalrog         return s->cfgr0;
64624859b68Sbalrog 
64724859b68Sbalrog     default:
64824859b68Sbalrog         return 0;
64924859b68Sbalrog     }
65024859b68Sbalrog }
65124859b68Sbalrog 
652a8170e5eSAvi Kivity static void mv88w8618_flashcfg_write(void *opaque, hwaddr offset,
65319b4a424SAvi Kivity                                      uint64_t value, unsigned size)
65424859b68Sbalrog {
65524859b68Sbalrog     mv88w8618_flashcfg_state *s = opaque;
65624859b68Sbalrog 
65724859b68Sbalrog     switch (offset) {
65824859b68Sbalrog     case MP_FLASHCFG_CFGR0:
65924859b68Sbalrog         s->cfgr0 = value;
66024859b68Sbalrog         break;
66124859b68Sbalrog     }
66224859b68Sbalrog }
66324859b68Sbalrog 
66419b4a424SAvi Kivity static const MemoryRegionOps mv88w8618_flashcfg_ops = {
66519b4a424SAvi Kivity     .read = mv88w8618_flashcfg_read,
66619b4a424SAvi Kivity     .write = mv88w8618_flashcfg_write,
66719b4a424SAvi Kivity     .endianness = DEVICE_NATIVE_ENDIAN,
66824859b68Sbalrog };
66924859b68Sbalrog 
670ece71994Sxiaoqiang zhao static void mv88w8618_flashcfg_init(Object *obj)
67124859b68Sbalrog {
672ece71994Sxiaoqiang zhao     SysBusDevice *dev = SYS_BUS_DEVICE(obj);
6735952b01cSAndreas Färber     mv88w8618_flashcfg_state *s = MV88W8618_FLASHCFG(dev);
67424859b68Sbalrog 
67524859b68Sbalrog     s->cfgr0 = 0xfffe4285; /* Default as set by U-Boot for 8 MB flash */
676ece71994Sxiaoqiang zhao     memory_region_init_io(&s->iomem, obj, &mv88w8618_flashcfg_ops, s,
67719b4a424SAvi Kivity                           "musicpal-flashcfg", MP_FLASHCFG_SIZE);
678750ecd44SAvi Kivity     sysbus_init_mmio(dev, &s->iomem);
67924859b68Sbalrog }
68024859b68Sbalrog 
681d5b61dddSJan Kiszka static const VMStateDescription mv88w8618_flashcfg_vmsd = {
682d5b61dddSJan Kiszka     .name = "mv88w8618_flashcfg",
683d5b61dddSJan Kiszka     .version_id = 1,
684d5b61dddSJan Kiszka     .minimum_version_id = 1,
685607ef570SRichard Henderson     .fields = (const VMStateField[]) {
686d5b61dddSJan Kiszka         VMSTATE_UINT32(cfgr0, mv88w8618_flashcfg_state),
687d5b61dddSJan Kiszka         VMSTATE_END_OF_LIST()
688d5b61dddSJan Kiszka     }
689d5b61dddSJan Kiszka };
690d5b61dddSJan Kiszka 
691999e12bbSAnthony Liguori static void mv88w8618_flashcfg_class_init(ObjectClass *klass, void *data)
692999e12bbSAnthony Liguori {
69339bffca2SAnthony Liguori     DeviceClass *dc = DEVICE_CLASS(klass);
694999e12bbSAnthony Liguori 
69539bffca2SAnthony Liguori     dc->vmsd = &mv88w8618_flashcfg_vmsd;
696999e12bbSAnthony Liguori }
697999e12bbSAnthony Liguori 
6988c43a6f0SAndreas Färber static const TypeInfo mv88w8618_flashcfg_info = {
6995952b01cSAndreas Färber     .name          = TYPE_MV88W8618_FLASHCFG,
70039bffca2SAnthony Liguori     .parent        = TYPE_SYS_BUS_DEVICE,
70139bffca2SAnthony Liguori     .instance_size = sizeof(mv88w8618_flashcfg_state),
702ece71994Sxiaoqiang zhao     .instance_init = mv88w8618_flashcfg_init,
703999e12bbSAnthony Liguori     .class_init    = mv88w8618_flashcfg_class_init,
704d5b61dddSJan Kiszka };
705d5b61dddSJan Kiszka 
706718ec0beSmalc /* Misc register offsets */
707718ec0beSmalc #define MP_MISC_BOARD_REVISION  0x18
70824859b68Sbalrog 
709718ec0beSmalc #define MP_BOARD_REVISION       0x31
71024859b68Sbalrog 
711db1015e9SEduardo Habkost struct MusicPalMiscState {
712a86f200aSPeter Maydell     SysBusDevice parent_obj;
713a86f200aSPeter Maydell     MemoryRegion iomem;
714db1015e9SEduardo Habkost };
715a86f200aSPeter Maydell 
716a86f200aSPeter Maydell #define TYPE_MUSICPAL_MISC "musicpal-misc"
7178063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(MusicPalMiscState, MUSICPAL_MISC)
718a86f200aSPeter Maydell 
719a8170e5eSAvi Kivity static uint64_t musicpal_misc_read(void *opaque, hwaddr offset,
72019b4a424SAvi Kivity                                    unsigned size)
721718ec0beSmalc {
722718ec0beSmalc     switch (offset) {
723718ec0beSmalc     case MP_MISC_BOARD_REVISION:
724718ec0beSmalc         return MP_BOARD_REVISION;
725718ec0beSmalc 
726718ec0beSmalc     default:
727718ec0beSmalc         return 0;
728718ec0beSmalc     }
729718ec0beSmalc }
730718ec0beSmalc 
731a8170e5eSAvi Kivity static void musicpal_misc_write(void *opaque, hwaddr offset,
73219b4a424SAvi Kivity                                 uint64_t value, unsigned size)
733718ec0beSmalc {
734718ec0beSmalc }
735718ec0beSmalc 
73619b4a424SAvi Kivity static const MemoryRegionOps musicpal_misc_ops = {
73719b4a424SAvi Kivity     .read = musicpal_misc_read,
73819b4a424SAvi Kivity     .write = musicpal_misc_write,
73919b4a424SAvi Kivity     .endianness = DEVICE_NATIVE_ENDIAN,
740718ec0beSmalc };
741718ec0beSmalc 
742a86f200aSPeter Maydell static void musicpal_misc_init(Object *obj)
743718ec0beSmalc {
744a86f200aSPeter Maydell     SysBusDevice *sd = SYS_BUS_DEVICE(obj);
745a86f200aSPeter Maydell     MusicPalMiscState *s = MUSICPAL_MISC(obj);
746718ec0beSmalc 
74764bde0f3SPaolo Bonzini     memory_region_init_io(&s->iomem, OBJECT(s), &musicpal_misc_ops, NULL,
74819b4a424SAvi Kivity                           "musicpal-misc", MP_MISC_SIZE);
749a86f200aSPeter Maydell     sysbus_init_mmio(sd, &s->iomem);
750718ec0beSmalc }
751718ec0beSmalc 
752a86f200aSPeter Maydell static const TypeInfo musicpal_misc_info = {
753a86f200aSPeter Maydell     .name = TYPE_MUSICPAL_MISC,
754a86f200aSPeter Maydell     .parent = TYPE_SYS_BUS_DEVICE,
755a86f200aSPeter Maydell     .instance_init = musicpal_misc_init,
756a86f200aSPeter Maydell     .instance_size = sizeof(MusicPalMiscState),
757a86f200aSPeter Maydell };
758a86f200aSPeter Maydell 
759718ec0beSmalc /* WLAN register offsets */
760718ec0beSmalc #define MP_WLAN_MAGIC1          0x11c
761718ec0beSmalc #define MP_WLAN_MAGIC2          0x124
762718ec0beSmalc 
763a8170e5eSAvi Kivity static uint64_t mv88w8618_wlan_read(void *opaque, hwaddr offset,
76419b4a424SAvi Kivity                                     unsigned size)
765718ec0beSmalc {
766718ec0beSmalc     switch (offset) {
767718ec0beSmalc     /* Workaround to allow loading the binary-only wlandrv.ko crap
768718ec0beSmalc      * from the original Freecom firmware. */
769718ec0beSmalc     case MP_WLAN_MAGIC1:
770718ec0beSmalc         return ~3;
771718ec0beSmalc     case MP_WLAN_MAGIC2:
772718ec0beSmalc         return -1;
773718ec0beSmalc 
774718ec0beSmalc     default:
775718ec0beSmalc         return 0;
776718ec0beSmalc     }
777718ec0beSmalc }
778718ec0beSmalc 
779a8170e5eSAvi Kivity static void mv88w8618_wlan_write(void *opaque, hwaddr offset,
78019b4a424SAvi Kivity                                  uint64_t value, unsigned size)
781718ec0beSmalc {
782718ec0beSmalc }
783718ec0beSmalc 
78419b4a424SAvi Kivity static const MemoryRegionOps mv88w8618_wlan_ops = {
78519b4a424SAvi Kivity     .read = mv88w8618_wlan_read,
78619b4a424SAvi Kivity     .write =mv88w8618_wlan_write,
78719b4a424SAvi Kivity     .endianness = DEVICE_NATIVE_ENDIAN,
788718ec0beSmalc };
789718ec0beSmalc 
7907f7420a0SMao Zhongyi static void mv88w8618_wlan_realize(DeviceState *dev, Error **errp)
791718ec0beSmalc {
79219b4a424SAvi Kivity     MemoryRegion *iomem = g_new(MemoryRegion, 1);
793718ec0beSmalc 
79464bde0f3SPaolo Bonzini     memory_region_init_io(iomem, OBJECT(dev), &mv88w8618_wlan_ops, NULL,
79519b4a424SAvi Kivity                           "musicpal-wlan", MP_WLAN_SIZE);
7967f7420a0SMao Zhongyi     sysbus_init_mmio(SYS_BUS_DEVICE(dev), iomem);
797718ec0beSmalc }
798718ec0beSmalc 
799718ec0beSmalc /* GPIO register offsets */
800718ec0beSmalc #define MP_GPIO_OE_LO           0x008
801718ec0beSmalc #define MP_GPIO_OUT_LO          0x00c
802718ec0beSmalc #define MP_GPIO_IN_LO           0x010
803708afdf3SJan Kiszka #define MP_GPIO_IER_LO          0x014
804708afdf3SJan Kiszka #define MP_GPIO_IMR_LO          0x018
805718ec0beSmalc #define MP_GPIO_ISR_LO          0x020
806718ec0beSmalc #define MP_GPIO_OE_HI           0x508
807718ec0beSmalc #define MP_GPIO_OUT_HI          0x50c
808718ec0beSmalc #define MP_GPIO_IN_HI           0x510
809708afdf3SJan Kiszka #define MP_GPIO_IER_HI          0x514
810708afdf3SJan Kiszka #define MP_GPIO_IMR_HI          0x518
811718ec0beSmalc #define MP_GPIO_ISR_HI          0x520
81224859b68Sbalrog 
81324859b68Sbalrog /* GPIO bits & masks */
81424859b68Sbalrog #define MP_GPIO_LCD_BRIGHTNESS  0x00070000
81524859b68Sbalrog #define MP_GPIO_I2C_DATA_BIT    29
81624859b68Sbalrog #define MP_GPIO_I2C_CLOCK_BIT   30
81724859b68Sbalrog 
81824859b68Sbalrog /* LCD brightness bits in GPIO_OE_HI */
81924859b68Sbalrog #define MP_OE_LCD_BRIGHTNESS    0x0007
82024859b68Sbalrog 
8217012d4b4SAndreas Färber #define TYPE_MUSICPAL_GPIO "musicpal_gpio"
8228063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(musicpal_gpio_state, MUSICPAL_GPIO)
8237012d4b4SAndreas Färber 
824db1015e9SEduardo Habkost struct musicpal_gpio_state {
8257012d4b4SAndreas Färber     /*< private >*/
8267012d4b4SAndreas Färber     SysBusDevice parent_obj;
8277012d4b4SAndreas Färber     /*< public >*/
8287012d4b4SAndreas Färber 
82919b4a424SAvi Kivity     MemoryRegion iomem;
830343ec8e4SBenoit Canet     uint32_t lcd_brightness;
831343ec8e4SBenoit Canet     uint32_t out_state;
832343ec8e4SBenoit Canet     uint32_t in_state;
833708afdf3SJan Kiszka     uint32_t ier;
834708afdf3SJan Kiszka     uint32_t imr;
835343ec8e4SBenoit Canet     uint32_t isr;
836343ec8e4SBenoit Canet     qemu_irq irq;
837708afdf3SJan Kiszka     qemu_irq out[5]; /* 3 brightness out + 2 lcd (data and clock ) */
838db1015e9SEduardo Habkost };
839343ec8e4SBenoit Canet 
840343ec8e4SBenoit Canet static void musicpal_gpio_brightness_update(musicpal_gpio_state *s) {
841343ec8e4SBenoit Canet     int i;
842343ec8e4SBenoit Canet     uint32_t brightness;
843343ec8e4SBenoit Canet 
844343ec8e4SBenoit Canet     /* compute brightness ratio */
845343ec8e4SBenoit Canet     switch (s->lcd_brightness) {
846343ec8e4SBenoit Canet     case 0x00000007:
847343ec8e4SBenoit Canet         brightness = 0;
848343ec8e4SBenoit Canet         break;
849343ec8e4SBenoit Canet 
850343ec8e4SBenoit Canet     case 0x00020000:
851343ec8e4SBenoit Canet         brightness = 1;
852343ec8e4SBenoit Canet         break;
853343ec8e4SBenoit Canet 
854343ec8e4SBenoit Canet     case 0x00020001:
855343ec8e4SBenoit Canet         brightness = 2;
856343ec8e4SBenoit Canet         break;
857343ec8e4SBenoit Canet 
858343ec8e4SBenoit Canet     case 0x00040000:
859343ec8e4SBenoit Canet         brightness = 3;
860343ec8e4SBenoit Canet         break;
861343ec8e4SBenoit Canet 
862343ec8e4SBenoit Canet     case 0x00010006:
863343ec8e4SBenoit Canet         brightness = 4;
864343ec8e4SBenoit Canet         break;
865343ec8e4SBenoit Canet 
866343ec8e4SBenoit Canet     case 0x00020005:
867343ec8e4SBenoit Canet         brightness = 5;
868343ec8e4SBenoit Canet         break;
869343ec8e4SBenoit Canet 
870343ec8e4SBenoit Canet     case 0x00040003:
871343ec8e4SBenoit Canet         brightness = 6;
872343ec8e4SBenoit Canet         break;
873343ec8e4SBenoit Canet 
874343ec8e4SBenoit Canet     case 0x00030004:
875343ec8e4SBenoit Canet     default:
876343ec8e4SBenoit Canet         brightness = 7;
877343ec8e4SBenoit Canet     }
878343ec8e4SBenoit Canet 
879343ec8e4SBenoit Canet     /* set lcd brightness GPIOs  */
88049fedd0dSJan Kiszka     for (i = 0; i <= 2; i++) {
881343ec8e4SBenoit Canet         qemu_set_irq(s->out[i], (brightness >> i) & 1);
882343ec8e4SBenoit Canet     }
88349fedd0dSJan Kiszka }
884343ec8e4SBenoit Canet 
885708afdf3SJan Kiszka static void musicpal_gpio_pin_event(void *opaque, int pin, int level)
886343ec8e4SBenoit Canet {
887243cd13cSJan Kiszka     musicpal_gpio_state *s = opaque;
888708afdf3SJan Kiszka     uint32_t mask = 1 << pin;
889708afdf3SJan Kiszka     uint32_t delta = level << pin;
890708afdf3SJan Kiszka     uint32_t old = s->in_state & mask;
891343ec8e4SBenoit Canet 
892708afdf3SJan Kiszka     s->in_state &= ~mask;
893708afdf3SJan Kiszka     s->in_state |= delta;
894708afdf3SJan Kiszka 
895708afdf3SJan Kiszka     if ((old ^ delta) &&
896708afdf3SJan Kiszka         ((level && (s->imr & mask)) || (!level && (s->ier & mask)))) {
897708afdf3SJan Kiszka         s->isr = mask;
898708afdf3SJan Kiszka         qemu_irq_raise(s->irq);
899d074769cSAndrzej Zaborowski     }
900343ec8e4SBenoit Canet }
901343ec8e4SBenoit Canet 
902a8170e5eSAvi Kivity static uint64_t musicpal_gpio_read(void *opaque, hwaddr offset,
90319b4a424SAvi Kivity                                    unsigned size)
90424859b68Sbalrog {
905243cd13cSJan Kiszka     musicpal_gpio_state *s = opaque;
906343ec8e4SBenoit Canet 
90724859b68Sbalrog     switch (offset) {
90824859b68Sbalrog     case MP_GPIO_OE_HI: /* used for LCD brightness control */
909343ec8e4SBenoit Canet         return s->lcd_brightness & MP_OE_LCD_BRIGHTNESS;
91024859b68Sbalrog 
91124859b68Sbalrog     case MP_GPIO_OUT_LO:
912343ec8e4SBenoit Canet         return s->out_state & 0xFFFF;
91324859b68Sbalrog     case MP_GPIO_OUT_HI:
914343ec8e4SBenoit Canet         return s->out_state >> 16;
91524859b68Sbalrog 
91624859b68Sbalrog     case MP_GPIO_IN_LO:
917343ec8e4SBenoit Canet         return s->in_state & 0xFFFF;
91824859b68Sbalrog     case MP_GPIO_IN_HI:
919343ec8e4SBenoit Canet         return s->in_state >> 16;
92024859b68Sbalrog 
921708afdf3SJan Kiszka     case MP_GPIO_IER_LO:
922708afdf3SJan Kiszka         return s->ier & 0xFFFF;
923708afdf3SJan Kiszka     case MP_GPIO_IER_HI:
924708afdf3SJan Kiszka         return s->ier >> 16;
925708afdf3SJan Kiszka 
926708afdf3SJan Kiszka     case MP_GPIO_IMR_LO:
927708afdf3SJan Kiszka         return s->imr & 0xFFFF;
928708afdf3SJan Kiszka     case MP_GPIO_IMR_HI:
929708afdf3SJan Kiszka         return s->imr >> 16;
930708afdf3SJan Kiszka 
93124859b68Sbalrog     case MP_GPIO_ISR_LO:
932343ec8e4SBenoit Canet         return s->isr & 0xFFFF;
93324859b68Sbalrog     case MP_GPIO_ISR_HI:
934343ec8e4SBenoit Canet         return s->isr >> 16;
93524859b68Sbalrog 
93624859b68Sbalrog     default:
93724859b68Sbalrog         return 0;
93824859b68Sbalrog     }
93924859b68Sbalrog }
94024859b68Sbalrog 
941a8170e5eSAvi Kivity static void musicpal_gpio_write(void *opaque, hwaddr offset,
94219b4a424SAvi Kivity                                 uint64_t value, unsigned size)
94324859b68Sbalrog {
944243cd13cSJan Kiszka     musicpal_gpio_state *s = opaque;
94524859b68Sbalrog     switch (offset) {
94624859b68Sbalrog     case MP_GPIO_OE_HI: /* used for LCD brightness control */
947343ec8e4SBenoit Canet         s->lcd_brightness = (s->lcd_brightness & MP_GPIO_LCD_BRIGHTNESS) |
94824859b68Sbalrog                          (value & MP_OE_LCD_BRIGHTNESS);
949343ec8e4SBenoit Canet         musicpal_gpio_brightness_update(s);
95024859b68Sbalrog         break;
95124859b68Sbalrog 
95224859b68Sbalrog     case MP_GPIO_OUT_LO:
953343ec8e4SBenoit Canet         s->out_state = (s->out_state & 0xFFFF0000) | (value & 0xFFFF);
95424859b68Sbalrog         break;
95524859b68Sbalrog     case MP_GPIO_OUT_HI:
956343ec8e4SBenoit Canet         s->out_state = (s->out_state & 0xFFFF) | (value << 16);
957343ec8e4SBenoit Canet         s->lcd_brightness = (s->lcd_brightness & 0xFFFF) |
958343ec8e4SBenoit Canet                             (s->out_state & MP_GPIO_LCD_BRIGHTNESS);
959343ec8e4SBenoit Canet         musicpal_gpio_brightness_update(s);
960d074769cSAndrzej Zaborowski         qemu_set_irq(s->out[3], (s->out_state >> MP_GPIO_I2C_DATA_BIT) & 1);
961d074769cSAndrzej Zaborowski         qemu_set_irq(s->out[4], (s->out_state >> MP_GPIO_I2C_CLOCK_BIT) & 1);
96224859b68Sbalrog         break;
96324859b68Sbalrog 
964708afdf3SJan Kiszka     case MP_GPIO_IER_LO:
965708afdf3SJan Kiszka         s->ier = (s->ier & 0xFFFF0000) | (value & 0xFFFF);
966708afdf3SJan Kiszka         break;
967708afdf3SJan Kiszka     case MP_GPIO_IER_HI:
968708afdf3SJan Kiszka         s->ier = (s->ier & 0xFFFF) | (value << 16);
969708afdf3SJan Kiszka         break;
970708afdf3SJan Kiszka 
971708afdf3SJan Kiszka     case MP_GPIO_IMR_LO:
972708afdf3SJan Kiszka         s->imr = (s->imr & 0xFFFF0000) | (value & 0xFFFF);
973708afdf3SJan Kiszka         break;
974708afdf3SJan Kiszka     case MP_GPIO_IMR_HI:
975708afdf3SJan Kiszka         s->imr = (s->imr & 0xFFFF) | (value << 16);
976708afdf3SJan Kiszka         break;
97724859b68Sbalrog     }
97824859b68Sbalrog }
97924859b68Sbalrog 
98019b4a424SAvi Kivity static const MemoryRegionOps musicpal_gpio_ops = {
98119b4a424SAvi Kivity     .read = musicpal_gpio_read,
98219b4a424SAvi Kivity     .write = musicpal_gpio_write,
98319b4a424SAvi Kivity     .endianness = DEVICE_NATIVE_ENDIAN,
984718ec0beSmalc };
985718ec0beSmalc 
986d5b61dddSJan Kiszka static void musicpal_gpio_reset(DeviceState *d)
987718ec0beSmalc {
9887012d4b4SAndreas Färber     musicpal_gpio_state *s = MUSICPAL_GPIO(d);
98930624c92SJan Kiszka 
99030624c92SJan Kiszka     s->lcd_brightness = 0;
99130624c92SJan Kiszka     s->out_state = 0;
992343ec8e4SBenoit Canet     s->in_state = 0xffffffff;
993708afdf3SJan Kiszka     s->ier = 0;
994708afdf3SJan Kiszka     s->imr = 0;
995343ec8e4SBenoit Canet     s->isr = 0;
996343ec8e4SBenoit Canet }
997343ec8e4SBenoit Canet 
998ece71994Sxiaoqiang zhao static void musicpal_gpio_init(Object *obj)
999343ec8e4SBenoit Canet {
1000ece71994Sxiaoqiang zhao     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
10017012d4b4SAndreas Färber     DeviceState *dev = DEVICE(sbd);
10027012d4b4SAndreas Färber     musicpal_gpio_state *s = MUSICPAL_GPIO(dev);
1003718ec0beSmalc 
10047012d4b4SAndreas Färber     sysbus_init_irq(sbd, &s->irq);
1005343ec8e4SBenoit Canet 
1006ece71994Sxiaoqiang zhao     memory_region_init_io(&s->iomem, obj, &musicpal_gpio_ops, s,
100719b4a424SAvi Kivity                           "musicpal-gpio", MP_GPIO_SIZE);
10087012d4b4SAndreas Färber     sysbus_init_mmio(sbd, &s->iomem);
1009343ec8e4SBenoit Canet 
10107012d4b4SAndreas Färber     qdev_init_gpio_out(dev, s->out, ARRAY_SIZE(s->out));
1011708afdf3SJan Kiszka 
10127012d4b4SAndreas Färber     qdev_init_gpio_in(dev, musicpal_gpio_pin_event, 32);
1013718ec0beSmalc }
1014718ec0beSmalc 
1015d5b61dddSJan Kiszka static const VMStateDescription musicpal_gpio_vmsd = {
1016d5b61dddSJan Kiszka     .name = "musicpal_gpio",
1017d5b61dddSJan Kiszka     .version_id = 1,
1018d5b61dddSJan Kiszka     .minimum_version_id = 1,
1019607ef570SRichard Henderson     .fields = (const VMStateField[]) {
1020d5b61dddSJan Kiszka         VMSTATE_UINT32(lcd_brightness, musicpal_gpio_state),
1021d5b61dddSJan Kiszka         VMSTATE_UINT32(out_state, musicpal_gpio_state),
1022d5b61dddSJan Kiszka         VMSTATE_UINT32(in_state, musicpal_gpio_state),
1023d5b61dddSJan Kiszka         VMSTATE_UINT32(ier, musicpal_gpio_state),
1024d5b61dddSJan Kiszka         VMSTATE_UINT32(imr, musicpal_gpio_state),
1025d5b61dddSJan Kiszka         VMSTATE_UINT32(isr, musicpal_gpio_state),
1026d5b61dddSJan Kiszka         VMSTATE_END_OF_LIST()
1027d5b61dddSJan Kiszka     }
1028d5b61dddSJan Kiszka };
1029d5b61dddSJan Kiszka 
1030999e12bbSAnthony Liguori static void musicpal_gpio_class_init(ObjectClass *klass, void *data)
1031999e12bbSAnthony Liguori {
103239bffca2SAnthony Liguori     DeviceClass *dc = DEVICE_CLASS(klass);
1033999e12bbSAnthony Liguori 
103439bffca2SAnthony Liguori     dc->reset = musicpal_gpio_reset;
103539bffca2SAnthony Liguori     dc->vmsd = &musicpal_gpio_vmsd;
1036999e12bbSAnthony Liguori }
1037999e12bbSAnthony Liguori 
10388c43a6f0SAndreas Färber static const TypeInfo musicpal_gpio_info = {
10397012d4b4SAndreas Färber     .name          = TYPE_MUSICPAL_GPIO,
104039bffca2SAnthony Liguori     .parent        = TYPE_SYS_BUS_DEVICE,
104139bffca2SAnthony Liguori     .instance_size = sizeof(musicpal_gpio_state),
1042ece71994Sxiaoqiang zhao     .instance_init = musicpal_gpio_init,
1043999e12bbSAnthony Liguori     .class_init    = musicpal_gpio_class_init,
104430624c92SJan Kiszka };
104530624c92SJan Kiszka 
104624859b68Sbalrog /* Keyboard codes & masks */
1047708afdf3SJan Kiszka #define MP_KEY_WHEEL_VOL       (1 << 0)
1048343ec8e4SBenoit Canet #define MP_KEY_WHEEL_VOL_INV   (1 << 1)
1049343ec8e4SBenoit Canet #define MP_KEY_WHEEL_NAV       (1 << 2)
1050343ec8e4SBenoit Canet #define MP_KEY_WHEEL_NAV_INV   (1 << 3)
1051343ec8e4SBenoit Canet #define MP_KEY_BTN_FAVORITS    (1 << 4)
1052343ec8e4SBenoit Canet #define MP_KEY_BTN_MENU        (1 << 5)
1053343ec8e4SBenoit Canet #define MP_KEY_BTN_VOLUME      (1 << 6)
1054343ec8e4SBenoit Canet #define MP_KEY_BTN_NAVIGATION  (1 << 7)
1055343ec8e4SBenoit Canet 
10563bdf5327SAndreas Färber #define TYPE_MUSICPAL_KEY "musicpal_key"
10578063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(musicpal_key_state, MUSICPAL_KEY)
10583bdf5327SAndreas Färber 
1059db1015e9SEduardo Habkost struct musicpal_key_state {
10603bdf5327SAndreas Färber     /*< private >*/
10613bdf5327SAndreas Färber     SysBusDevice parent_obj;
10623bdf5327SAndreas Färber     /*< public >*/
10633bdf5327SAndreas Färber 
1064708afdf3SJan Kiszka     uint32_t pressed_keys;
1065708afdf3SJan Kiszka     qemu_irq out[8];
1066db1015e9SEduardo Habkost };
1067343ec8e4SBenoit Canet 
1068ff7888dcSPeter Maydell static void musicpal_key_event(DeviceState *dev, QemuConsole *src,
1069ff7888dcSPeter Maydell                                InputEvent *evt)
107024859b68Sbalrog {
1071ff7888dcSPeter Maydell     musicpal_key_state *s = MUSICPAL_KEY(dev);
1072ff7888dcSPeter Maydell     InputKeyEvent *key = evt->u.key.data;
1073ff7888dcSPeter Maydell     int qcode = qemu_input_key_value_to_qcode(key->key);
107424859b68Sbalrog     uint32_t event = 0;
1075343ec8e4SBenoit Canet     int i;
107624859b68Sbalrog 
1077ff7888dcSPeter Maydell     switch (qcode) {
1078ff7888dcSPeter Maydell     case Q_KEY_CODE_UP:
1079343ec8e4SBenoit Canet         event = MP_KEY_WHEEL_NAV | MP_KEY_WHEEL_NAV_INV;
108024859b68Sbalrog         break;
108124859b68Sbalrog 
1082ff7888dcSPeter Maydell     case Q_KEY_CODE_DOWN:
1083343ec8e4SBenoit Canet         event = MP_KEY_WHEEL_NAV;
108424859b68Sbalrog         break;
108524859b68Sbalrog 
1086ff7888dcSPeter Maydell     case Q_KEY_CODE_LEFT:
1087343ec8e4SBenoit Canet         event = MP_KEY_WHEEL_VOL | MP_KEY_WHEEL_VOL_INV;
108824859b68Sbalrog         break;
108924859b68Sbalrog 
1090ff7888dcSPeter Maydell     case Q_KEY_CODE_RIGHT:
1091343ec8e4SBenoit Canet         event = MP_KEY_WHEEL_VOL;
109224859b68Sbalrog         break;
1093ff7888dcSPeter Maydell 
1094ff7888dcSPeter Maydell     case Q_KEY_CODE_F:
1095343ec8e4SBenoit Canet         event = MP_KEY_BTN_FAVORITS;
109624859b68Sbalrog         break;
109724859b68Sbalrog 
1098ff7888dcSPeter Maydell     case Q_KEY_CODE_TAB:
1099343ec8e4SBenoit Canet         event = MP_KEY_BTN_VOLUME;
110024859b68Sbalrog         break;
110124859b68Sbalrog 
1102ff7888dcSPeter Maydell     case Q_KEY_CODE_RET:
1103343ec8e4SBenoit Canet         event = MP_KEY_BTN_NAVIGATION;
110424859b68Sbalrog         break;
110524859b68Sbalrog 
1106ff7888dcSPeter Maydell     case Q_KEY_CODE_M:
1107343ec8e4SBenoit Canet         event = MP_KEY_BTN_MENU;
110824859b68Sbalrog         break;
110924859b68Sbalrog     }
1110ff7888dcSPeter Maydell 
1111ff7888dcSPeter Maydell     /*
1112ff7888dcSPeter Maydell      * We allow repeated wheel-events when the arrow keys are held down,
1113ff7888dcSPeter Maydell      * but do not repeat already-pressed buttons for the other key inputs.
1114ff7888dcSPeter Maydell      */
1115ff7888dcSPeter Maydell     if (!(event & (MP_KEY_WHEEL_NAV | MP_KEY_WHEEL_VOL))) {
1116ff7888dcSPeter Maydell         if (key->down && (s->pressed_keys & event)) {
11177c6ce4baSbalrog             event = 0;
11187c6ce4baSbalrog         }
1119708afdf3SJan Kiszka     }
112024859b68Sbalrog 
11217c6ce4baSbalrog     if (event) {
1122708afdf3SJan Kiszka         /* Raise GPIO pin first if repeating a key */
1123ff7888dcSPeter Maydell         if (key->down && (s->pressed_keys & event)) {
1124708afdf3SJan Kiszka             for (i = 0; i <= 7; i++) {
1125708afdf3SJan Kiszka                 if (event & (1 << i)) {
1126708afdf3SJan Kiszka                     qemu_set_irq(s->out[i], 1);
11277c6ce4baSbalrog                 }
1128708afdf3SJan Kiszka             }
1129708afdf3SJan Kiszka         }
1130708afdf3SJan Kiszka         for (i = 0; i <= 7; i++) {
1131708afdf3SJan Kiszka             if (event & (1 << i)) {
1132ff7888dcSPeter Maydell                 qemu_set_irq(s->out[i], !key->down);
1133708afdf3SJan Kiszka             }
1134708afdf3SJan Kiszka         }
1135ff7888dcSPeter Maydell         if (key->down) {
1136708afdf3SJan Kiszka             s->pressed_keys |= event;
1137ff7888dcSPeter Maydell         } else {
1138ff7888dcSPeter Maydell             s->pressed_keys &= ~event;
1139708afdf3SJan Kiszka         }
1140343ec8e4SBenoit Canet     }
1141343ec8e4SBenoit Canet }
1142343ec8e4SBenoit Canet 
1143ece71994Sxiaoqiang zhao static void musicpal_key_init(Object *obj)
1144343ec8e4SBenoit Canet {
1145ece71994Sxiaoqiang zhao     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
11463bdf5327SAndreas Färber     DeviceState *dev = DEVICE(sbd);
11473bdf5327SAndreas Färber     musicpal_key_state *s = MUSICPAL_KEY(dev);
1148343ec8e4SBenoit Canet 
1149708afdf3SJan Kiszka     s->pressed_keys = 0;
1150343ec8e4SBenoit Canet 
11513bdf5327SAndreas Färber     qdev_init_gpio_out(dev, s->out, ARRAY_SIZE(s->out));
1152ff7888dcSPeter Maydell }
1153343ec8e4SBenoit Canet 
1154ff7888dcSPeter Maydell static const QemuInputHandler musicpal_key_handler = {
1155ff7888dcSPeter Maydell     .name = "musicpal_key",
1156ff7888dcSPeter Maydell     .mask = INPUT_EVENT_MASK_KEY,
1157ff7888dcSPeter Maydell     .event = musicpal_key_event,
1158ff7888dcSPeter Maydell };
1159ff7888dcSPeter Maydell 
1160ff7888dcSPeter Maydell static void musicpal_key_realize(DeviceState *dev, Error **errp)
1161ff7888dcSPeter Maydell {
1162ff7888dcSPeter Maydell     qemu_input_handler_register(dev, &musicpal_key_handler);
116324859b68Sbalrog }
116424859b68Sbalrog 
1165d5b61dddSJan Kiszka static const VMStateDescription musicpal_key_vmsd = {
1166d5b61dddSJan Kiszka     .name = "musicpal_key",
1167ff7888dcSPeter Maydell     .version_id = 2,
1168ff7888dcSPeter Maydell     .minimum_version_id = 2,
1169607ef570SRichard Henderson     .fields = (const VMStateField[]) {
1170d5b61dddSJan Kiszka         VMSTATE_UINT32(pressed_keys, musicpal_key_state),
1171d5b61dddSJan Kiszka         VMSTATE_END_OF_LIST()
1172d5b61dddSJan Kiszka     }
1173d5b61dddSJan Kiszka };
1174d5b61dddSJan Kiszka 
1175999e12bbSAnthony Liguori static void musicpal_key_class_init(ObjectClass *klass, void *data)
1176999e12bbSAnthony Liguori {
117739bffca2SAnthony Liguori     DeviceClass *dc = DEVICE_CLASS(klass);
1178999e12bbSAnthony Liguori 
117939bffca2SAnthony Liguori     dc->vmsd = &musicpal_key_vmsd;
1180ff7888dcSPeter Maydell     dc->realize = musicpal_key_realize;
1181999e12bbSAnthony Liguori }
1182999e12bbSAnthony Liguori 
11838c43a6f0SAndreas Färber static const TypeInfo musicpal_key_info = {
11843bdf5327SAndreas Färber     .name          = TYPE_MUSICPAL_KEY,
118539bffca2SAnthony Liguori     .parent        = TYPE_SYS_BUS_DEVICE,
118639bffca2SAnthony Liguori     .instance_size = sizeof(musicpal_key_state),
1187ece71994Sxiaoqiang zhao     .instance_init = musicpal_key_init,
1188999e12bbSAnthony Liguori     .class_init    = musicpal_key_class_init,
1189d5b61dddSJan Kiszka };
1190d5b61dddSJan Kiszka 
1191e0ee6413SPhilippe Mathieu-Daudé #define FLASH_SECTOR_SIZE   (64 * KiB)
1192e0ee6413SPhilippe Mathieu-Daudé 
119324859b68Sbalrog static struct arm_boot_info musicpal_binfo = {
119424859b68Sbalrog     .loader_start = 0x0,
119524859b68Sbalrog     .board_id = 0x20e,
119624859b68Sbalrog };
119724859b68Sbalrog 
11983ef96221SMarcel Apfelbaum static void musicpal_init(MachineState *machine)
119924859b68Sbalrog {
1200f25608e9SAndreas Färber     ARMCPU *cpu;
1201b47b50faSPaul Brook     DeviceState *dev;
120244cbf349SPhilippe Mathieu-Daudé     DeviceState *pic;
1203498661ddSPhilippe Mathieu-Daudé     DeviceState *uart_orgate;
1204d074769cSAndrzej Zaborowski     DeviceState *i2c_dev;
1205343ec8e4SBenoit Canet     DeviceState *lcd_dev;
1206343ec8e4SBenoit Canet     DeviceState *key_dev;
12071373b15bSPhilippe Mathieu-Daudé     I2CSlave *wm8750_dev;
1208d074769cSAndrzej Zaborowski     SysBusDevice *s;
1209a5c82852SAndreas Färber     I2CBus *i2c;
1210b47b50faSPaul Brook     int i;
121124859b68Sbalrog     unsigned long flash_size;
1212751c6a17SGerd Hoffmann     DriveInfo *dinfo;
12133ed61312SIgor Mammedov     MachineClass *mc = MACHINE_GET_CLASS(machine);
121419b4a424SAvi Kivity     MemoryRegion *address_space_mem = get_system_memory();
121519b4a424SAvi Kivity     MemoryRegion *sram = g_new(MemoryRegion, 1);
121624859b68Sbalrog 
12173ed61312SIgor Mammedov     /* For now we use a fixed - the original - RAM size */
12183ed61312SIgor Mammedov     if (machine->ram_size != mc->default_ram_size) {
12193ed61312SIgor Mammedov         char *sz = size_to_str(mc->default_ram_size);
12203ed61312SIgor Mammedov         error_report("Invalid RAM size, should be %s", sz);
12213ed61312SIgor Mammedov         g_free(sz);
12223ed61312SIgor Mammedov         exit(EXIT_FAILURE);
12233ed61312SIgor Mammedov     }
12243ed61312SIgor Mammedov 
1225ba1ba5ccSIgor Mammedov     cpu = ARM_CPU(cpu_create(machine->cpu_type));
122624859b68Sbalrog 
12273ed61312SIgor Mammedov     memory_region_add_subregion(address_space_mem, 0, machine->ram);
122824859b68Sbalrog 
122998a99ce0SPeter Maydell     memory_region_init_ram(sram, NULL, "musicpal.sram", MP_SRAM_SIZE,
1230f8ed85acSMarkus Armbruster                            &error_fatal);
123119b4a424SAvi Kivity     memory_region_add_subregion(address_space_mem, MP_SRAM_BASE, sram);
123224859b68Sbalrog 
123344cbf349SPhilippe Mathieu-Daudé     pic = sysbus_create_simple(TYPE_MV88W8618_PIC, MP_PIC_BASE,
1234fcef61ecSPeter Maydell                                qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ));
123544cbf349SPhilippe Mathieu-Daudé     sysbus_create_varargs(TYPE_MV88W8618_PIT, MP_PIT_BASE,
123644cbf349SPhilippe Mathieu-Daudé                           qdev_get_gpio_in(pic, MP_TIMER1_IRQ),
123744cbf349SPhilippe Mathieu-Daudé                           qdev_get_gpio_in(pic, MP_TIMER2_IRQ),
123844cbf349SPhilippe Mathieu-Daudé                           qdev_get_gpio_in(pic, MP_TIMER3_IRQ),
123944cbf349SPhilippe Mathieu-Daudé                           qdev_get_gpio_in(pic, MP_TIMER4_IRQ), NULL);
124024859b68Sbalrog 
1241498661ddSPhilippe Mathieu-Daudé     /* Logically OR both UART IRQs together */
1242498661ddSPhilippe Mathieu-Daudé     uart_orgate = DEVICE(object_new(TYPE_OR_IRQ));
1243498661ddSPhilippe Mathieu-Daudé     object_property_set_int(OBJECT(uart_orgate), "num-lines", 2, &error_fatal);
1244498661ddSPhilippe Mathieu-Daudé     qdev_realize_and_unref(uart_orgate, NULL, &error_fatal);
12457d5b0d68SPhilippe Mathieu-Daudé     qdev_connect_gpio_out(uart_orgate, 0,
124644cbf349SPhilippe Mathieu-Daudé                           qdev_get_gpio_in(pic, MP_UART_SHARED_IRQ));
1247498661ddSPhilippe Mathieu-Daudé 
1248498661ddSPhilippe Mathieu-Daudé     serial_mm_init(address_space_mem, MP_UART1_BASE, 2,
1249498661ddSPhilippe Mathieu-Daudé                    qdev_get_gpio_in(uart_orgate, 0),
12509bca0edbSPeter Maydell                    1825000, serial_hd(0), DEVICE_NATIVE_ENDIAN);
1251498661ddSPhilippe Mathieu-Daudé     serial_mm_init(address_space_mem, MP_UART2_BASE, 2,
1252498661ddSPhilippe Mathieu-Daudé                    qdev_get_gpio_in(uart_orgate, 1),
12539bca0edbSPeter Maydell                    1825000, serial_hd(1), DEVICE_NATIVE_ENDIAN);
125424859b68Sbalrog 
125524859b68Sbalrog     /* Register flash */
1256751c6a17SGerd Hoffmann     dinfo = drive_get(IF_PFLASH, 0, 0);
1257751c6a17SGerd Hoffmann     if (dinfo) {
12584be74634SMarkus Armbruster         BlockBackend *blk = blk_by_legacy_dinfo(dinfo);
1259fa1d36dfSMarkus Armbruster 
12604be74634SMarkus Armbruster         flash_size = blk_getlength(blk);
1261e0ee6413SPhilippe Mathieu-Daudé         if (flash_size != 8 * MiB && flash_size != 16 * MiB &&
1262e0ee6413SPhilippe Mathieu-Daudé             flash_size != 32 * MiB) {
1263c0dbca36SAlistair Francis             error_report("Invalid flash image size");
126424859b68Sbalrog             exit(1);
126524859b68Sbalrog         }
126624859b68Sbalrog 
126724859b68Sbalrog         /*
126824859b68Sbalrog          * The original U-Boot accesses the flash at 0xFE000000 instead of
126924859b68Sbalrog          * 0xFF800000 (if there is 8 MB flash). So remap flash access if the
127024859b68Sbalrog          * image is smaller than 32 MB.
127124859b68Sbalrog          */
1272940d5b13SMarkus Armbruster         pflash_cfi02_register(0x100000000ULL - MP_FLASH_SIZE_MAX,
1273cfe5f011SAvi Kivity                               "musicpal.flash", flash_size,
1274e0ee6413SPhilippe Mathieu-Daudé                               blk, FLASH_SECTOR_SIZE,
12755f9fc5adSBlue Swirl                               MP_FLASH_SIZE_MAX / flash_size,
12765f9fc5adSBlue Swirl                               2, 0x00BF, 0x236D, 0x0000, 0x0000,
127701e0451aSAnthony Liguori                               0x5555, 0x2AAA, 0);
127824859b68Sbalrog     }
12795952b01cSAndreas Färber     sysbus_create_simple(TYPE_MV88W8618_FLASHCFG, MP_FLASHCFG_BASE, NULL);
128024859b68Sbalrog 
1281b47b50faSPaul Brook     qemu_check_nic_model(&nd_table[0], "mv88w8618");
12823e80f690SMarkus Armbruster     dev = qdev_new(TYPE_MV88W8618_ETH);
12834c91cd28SGerd Hoffmann     qdev_set_nic_properties(dev, &nd_table[0]);
128479ed6fd6SPhilippe Mathieu-Daudé     object_property_set_link(OBJECT(dev), "dma-memory",
128579ed6fd6SPhilippe Mathieu-Daudé                              OBJECT(get_system_memory()), &error_fatal);
12863c6ef471SMarkus Armbruster     sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
12871356b98dSAndreas Färber     sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, MP_ETH_BASE);
128844cbf349SPhilippe Mathieu-Daudé     sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0,
128944cbf349SPhilippe Mathieu-Daudé                        qdev_get_gpio_in(pic, MP_ETH_IRQ));
129024859b68Sbalrog 
1291b47b50faSPaul Brook     sysbus_create_simple("mv88w8618_wlan", MP_WLAN_BASE, NULL);
1292718ec0beSmalc 
1293a86f200aSPeter Maydell     sysbus_create_simple(TYPE_MUSICPAL_MISC, MP_MISC_BASE, NULL);
1294343ec8e4SBenoit Canet 
12957012d4b4SAndreas Färber     dev = sysbus_create_simple(TYPE_MUSICPAL_GPIO, MP_GPIO_BASE,
129644cbf349SPhilippe Mathieu-Daudé                                qdev_get_gpio_in(pic, MP_GPIO_IRQ));
1297da8df26dSPhilippe Mathieu-Daudé     i2c_dev = sysbus_create_simple(TYPE_GPIO_I2C, -1, NULL);
1298a5c82852SAndreas Färber     i2c = (I2CBus *)qdev_get_child_bus(i2c_dev, "i2c");
1299d074769cSAndrzej Zaborowski 
13002cca58fdSAndreas Färber     lcd_dev = sysbus_create_simple(TYPE_MUSICPAL_LCD, MP_LCD_BASE, NULL);
13013bdf5327SAndreas Färber     key_dev = sysbus_create_simple(TYPE_MUSICPAL_KEY, -1, NULL);
1302343ec8e4SBenoit Canet 
1303d074769cSAndrzej Zaborowski     /* I2C read data */
1304708afdf3SJan Kiszka     qdev_connect_gpio_out(i2c_dev, 0,
1305708afdf3SJan Kiszka                           qdev_get_gpio_in(dev, MP_GPIO_I2C_DATA_BIT));
1306d074769cSAndrzej Zaborowski     /* I2C data */
1307d074769cSAndrzej Zaborowski     qdev_connect_gpio_out(dev, 3, qdev_get_gpio_in(i2c_dev, 0));
1308d074769cSAndrzej Zaborowski     /* I2C clock */
1309d074769cSAndrzej Zaborowski     qdev_connect_gpio_out(dev, 4, qdev_get_gpio_in(i2c_dev, 1));
1310d074769cSAndrzej Zaborowski 
131149fedd0dSJan Kiszka     for (i = 0; i < 3; i++) {
1312343ec8e4SBenoit Canet         qdev_connect_gpio_out(dev, i, qdev_get_gpio_in(lcd_dev, i));
131349fedd0dSJan Kiszka     }
1314708afdf3SJan Kiszka     for (i = 0; i < 4; i++) {
1315708afdf3SJan Kiszka         qdev_connect_gpio_out(key_dev, i, qdev_get_gpio_in(dev, i + 8));
1316708afdf3SJan Kiszka     }
1317708afdf3SJan Kiszka     for (i = 4; i < 8; i++) {
1318708afdf3SJan Kiszka         qdev_connect_gpio_out(key_dev, i, qdev_get_gpio_in(dev, i + 15));
1319708afdf3SJan Kiszka     }
132024859b68Sbalrog 
1321b8ab0303SMartin Kletzander     wm8750_dev = i2c_slave_new(TYPE_WM8750, MP_WM_ADDR);
1322b8ab0303SMartin Kletzander     if (machine->audiodev) {
1323b8ab0303SMartin Kletzander         qdev_prop_set_string(DEVICE(wm8750_dev), "audiodev", machine->audiodev);
1324b8ab0303SMartin Kletzander     }
1325b8ab0303SMartin Kletzander     i2c_slave_realize_and_unref(wm8750_dev, i2c, &error_abort);
1326b8ab0303SMartin Kletzander 
13273e80f690SMarkus Armbruster     dev = qdev_new(TYPE_MV88W8618_AUDIO);
13281356b98dSAndreas Färber     s = SYS_BUS_DEVICE(dev);
13295325cc34SMarkus Armbruster     object_property_set_link(OBJECT(dev), "wm8750", OBJECT(wm8750_dev),
13305325cc34SMarkus Armbruster                              NULL);
13313c6ef471SMarkus Armbruster     sysbus_realize_and_unref(s, &error_fatal);
1332d074769cSAndrzej Zaborowski     sysbus_mmio_map(s, 0, MP_AUDIO_BASE);
133344cbf349SPhilippe Mathieu-Daudé     sysbus_connect_irq(s, 0, qdev_get_gpio_in(pic, MP_AUDIO_IRQ));
1334d074769cSAndrzej Zaborowski 
133524859b68Sbalrog     musicpal_binfo.ram_size = MP_RAM_DEFAULT_SIZE;
13362744ece8STao Xu     arm_load_kernel(cpu, machine, &musicpal_binfo);
133724859b68Sbalrog }
133824859b68Sbalrog 
1339e264d29dSEduardo Habkost static void musicpal_machine_init(MachineClass *mc)
1340f80f9ec9SAnthony Liguori {
1341e264d29dSEduardo Habkost     mc->desc = "Marvell 88w8618 / MusicPal (ARM926EJ-S)";
1342e264d29dSEduardo Habkost     mc->init = musicpal_init;
13434672cbd7SPeter Maydell     mc->ignore_memory_transaction_failures = true;
1344ba1ba5ccSIgor Mammedov     mc->default_cpu_type = ARM_CPU_TYPE_NAME("arm926");
13453ed61312SIgor Mammedov     mc->default_ram_size = MP_RAM_DEFAULT_SIZE;
13463ed61312SIgor Mammedov     mc->default_ram_id = "musicpal.ram";
1347b8ab0303SMartin Kletzander 
1348b8ab0303SMartin Kletzander     machine_add_audiodev_property(mc);
1349f80f9ec9SAnthony Liguori }
1350f80f9ec9SAnthony Liguori 
1351e264d29dSEduardo Habkost DEFINE_MACHINE("musicpal", musicpal_machine_init)
1352f80f9ec9SAnthony Liguori 
1353999e12bbSAnthony Liguori static void mv88w8618_wlan_class_init(ObjectClass *klass, void *data)
1354999e12bbSAnthony Liguori {
13557f7420a0SMao Zhongyi     DeviceClass *dc = DEVICE_CLASS(klass);
1356999e12bbSAnthony Liguori 
13577f7420a0SMao Zhongyi     dc->realize = mv88w8618_wlan_realize;
1358999e12bbSAnthony Liguori }
1359999e12bbSAnthony Liguori 
13608c43a6f0SAndreas Färber static const TypeInfo mv88w8618_wlan_info = {
1361999e12bbSAnthony Liguori     .name          = "mv88w8618_wlan",
136239bffca2SAnthony Liguori     .parent        = TYPE_SYS_BUS_DEVICE,
136339bffca2SAnthony Liguori     .instance_size = sizeof(SysBusDevice),
1364999e12bbSAnthony Liguori     .class_init    = mv88w8618_wlan_class_init,
1365999e12bbSAnthony Liguori };
1366999e12bbSAnthony Liguori 
136783f7d43aSAndreas Färber static void musicpal_register_types(void)
1368b47b50faSPaul Brook {
136939bffca2SAnthony Liguori     type_register_static(&mv88w8618_pic_info);
137039bffca2SAnthony Liguori     type_register_static(&mv88w8618_pit_info);
137139bffca2SAnthony Liguori     type_register_static(&mv88w8618_flashcfg_info);
137239bffca2SAnthony Liguori     type_register_static(&mv88w8618_wlan_info);
137339bffca2SAnthony Liguori     type_register_static(&musicpal_lcd_info);
137439bffca2SAnthony Liguori     type_register_static(&musicpal_gpio_info);
137539bffca2SAnthony Liguori     type_register_static(&musicpal_key_info);
1376a86f200aSPeter Maydell     type_register_static(&musicpal_misc_info);
1377b47b50faSPaul Brook }
1378b47b50faSPaul Brook 
137983f7d43aSAndreas Färber type_init(musicpal_register_types)
1380