xref: /qemu/hw/arm/musicpal.c (revision 06b40d250ecfa1633209c2e431a7a38acfd03a98)
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"
1583c9f4caSPaolo Bonzini #include "hw/sysbus.h"
16d6454270SMarkus Armbruster #include "migration/vmstate.h"
1712ec8bd5SPeter Maydell #include "hw/arm/boot.h"
181422e32dSPaolo Bonzini #include "net/net.h"
1932cad1ffSPhilippe Mathieu-Daudé #include "system/system.h"
2083c9f4caSPaolo Bonzini #include "hw/boards.h"
217e6b5497SBernhard Beschow #include "hw/char/serial-mm.h"
221de7afc9SPaolo Bonzini #include "qemu/timer.h"
2383c9f4caSPaolo Bonzini #include "hw/ptimer.h"
24a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
250d09e41aSPaolo Bonzini #include "hw/block/flash.h"
2628ecbaeeSPaolo Bonzini #include "ui/console.h"
270d09e41aSPaolo Bonzini #include "hw/i2c/i2c.h"
28da8df26dSPhilippe Mathieu-Daudé #include "hw/i2c/bitbang_i2c.h"
2964552b6bSMarkus Armbruster #include "hw/irq.h"
30498661ddSPhilippe Mathieu-Daudé #include "hw/or-irq.h"
317ab14c5aSPhilippe Mathieu-Daudé #include "hw/audio/wm8750.h"
3232cad1ffSPhilippe Mathieu-Daudé #include "system/block-backend.h"
3332cad1ffSPhilippe Mathieu-Daudé #include "system/runstate.h"
3432cad1ffSPhilippe Mathieu-Daudé #include "system/dma.h"
3528ecbaeeSPaolo Bonzini #include "ui/pixel_ops.h"
363ed61312SIgor Mammedov #include "qemu/cutils.h"
37db1015e9SEduardo Habkost #include "qom/object.h"
386d81f488SPhilippe Mathieu-Daudé #include "hw/net/mv88w8618_eth.h"
39b8ab0303SMartin Kletzander #include "audio/audio.h"
40cc37d98bSRichard Henderson #include "qemu/error-report.h"
41d780d056SPhilippe Mathieu-Daudé #include "target/arm/cpu-qom.h"
42cc37d98bSRichard Henderson 
43718ec0beSmalc #define MP_MISC_BASE            0x80002000
44718ec0beSmalc #define MP_MISC_SIZE            0x00001000
45718ec0beSmalc 
4624859b68Sbalrog #define MP_ETH_BASE             0x80008000
4724859b68Sbalrog 
48718ec0beSmalc #define MP_WLAN_BASE            0x8000C000
49718ec0beSmalc #define MP_WLAN_SIZE            0x00000800
50718ec0beSmalc 
5124859b68Sbalrog #define MP_UART1_BASE           0x8000C840
5224859b68Sbalrog #define MP_UART2_BASE           0x8000C940
5324859b68Sbalrog 
54718ec0beSmalc #define MP_GPIO_BASE            0x8000D000
55718ec0beSmalc #define MP_GPIO_SIZE            0x00001000
56718ec0beSmalc 
5724859b68Sbalrog #define MP_FLASHCFG_BASE        0x90006000
5824859b68Sbalrog #define MP_FLASHCFG_SIZE        0x00001000
5924859b68Sbalrog 
6024859b68Sbalrog #define MP_AUDIO_BASE           0x90007000
6124859b68Sbalrog 
6224859b68Sbalrog #define MP_PIC_BASE             0x90008000
6324859b68Sbalrog #define MP_PIC_SIZE             0x00001000
6424859b68Sbalrog 
6524859b68Sbalrog #define MP_PIT_BASE             0x90009000
6624859b68Sbalrog #define MP_PIT_SIZE             0x00001000
6724859b68Sbalrog 
6824859b68Sbalrog #define MP_LCD_BASE             0x9000c000
6924859b68Sbalrog #define MP_LCD_SIZE             0x00001000
7024859b68Sbalrog 
7124859b68Sbalrog #define MP_SRAM_BASE            0xC0000000
7224859b68Sbalrog #define MP_SRAM_SIZE            0x00020000
7324859b68Sbalrog 
7424859b68Sbalrog #define MP_RAM_DEFAULT_SIZE     32*1024*1024
7524859b68Sbalrog #define MP_FLASH_SIZE_MAX       32*1024*1024
7624859b68Sbalrog 
7724859b68Sbalrog #define MP_TIMER1_IRQ           4
78b47b50faSPaul Brook #define MP_TIMER2_IRQ           5
79b47b50faSPaul Brook #define MP_TIMER3_IRQ           6
8024859b68Sbalrog #define MP_TIMER4_IRQ           7
8124859b68Sbalrog #define MP_EHCI_IRQ             8
8224859b68Sbalrog #define MP_ETH_IRQ              9
83498661ddSPhilippe Mathieu-Daudé #define MP_UART_SHARED_IRQ      11
8424859b68Sbalrog #define MP_GPIO_IRQ             12
8524859b68Sbalrog #define MP_RTC_IRQ              28
8624859b68Sbalrog #define MP_AUDIO_IRQ            30
8724859b68Sbalrog 
8824859b68Sbalrog /* Wolfson 8750 I2C address */
8964258229SJan Kiszka #define MP_WM_ADDR              0x1A
9024859b68Sbalrog 
9124859b68Sbalrog /* LCD register offsets */
9224859b68Sbalrog #define MP_LCD_IRQCTRL          0x180
9324859b68Sbalrog #define MP_LCD_IRQSTAT          0x184
9424859b68Sbalrog #define MP_LCD_SPICTRL          0x1ac
9524859b68Sbalrog #define MP_LCD_INST             0x1bc
9624859b68Sbalrog #define MP_LCD_DATA             0x1c0
9724859b68Sbalrog 
9824859b68Sbalrog /* Mode magics */
9924859b68Sbalrog #define MP_LCD_SPI_DATA         0x00100011
10024859b68Sbalrog #define MP_LCD_SPI_CMD          0x00104011
10124859b68Sbalrog #define MP_LCD_SPI_INVALID      0x00000000
10224859b68Sbalrog 
103b3db996fSStefan Weil /* Commands */
10424859b68Sbalrog #define MP_LCD_INST_SETPAGE0    0xB0
10524859b68Sbalrog /* ... */
10624859b68Sbalrog #define MP_LCD_INST_SETPAGE7    0xB7
10724859b68Sbalrog 
10824859b68Sbalrog #define MP_LCD_TEXTCOLOR        0xe0e0ff /* RRGGBB */
10924859b68Sbalrog 
1102cca58fdSAndreas Färber #define TYPE_MUSICPAL_LCD "musicpal_lcd"
1118063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(musicpal_lcd_state, MUSICPAL_LCD)
1122cca58fdSAndreas Färber 
113db1015e9SEduardo Habkost struct musicpal_lcd_state {
1142cca58fdSAndreas Färber     /*< private >*/
1152cca58fdSAndreas Färber     SysBusDevice parent_obj;
1162cca58fdSAndreas Färber     /*< public >*/
1172cca58fdSAndreas Färber 
11819b4a424SAvi Kivity     MemoryRegion iomem;
119343ec8e4SBenoit Canet     uint32_t brightness;
12024859b68Sbalrog     uint32_t mode;
12124859b68Sbalrog     uint32_t irqctrl;
122d5b61dddSJan Kiszka     uint32_t page;
123d5b61dddSJan Kiszka     uint32_t page_off;
124c78f7137SGerd Hoffmann     QemuConsole *con;
12524859b68Sbalrog     uint8_t video_ram[128*64/8];
126db1015e9SEduardo Habkost };
12724859b68Sbalrog 
scale_lcd_color(musicpal_lcd_state * s,uint8_t col)128343ec8e4SBenoit Canet static uint8_t scale_lcd_color(musicpal_lcd_state *s, uint8_t col)
12924859b68Sbalrog {
130343ec8e4SBenoit Canet     switch (s->brightness) {
131343ec8e4SBenoit Canet     case 7:
13224859b68Sbalrog         return col;
133343ec8e4SBenoit Canet     case 0:
134343ec8e4SBenoit Canet         return 0;
135343ec8e4SBenoit Canet     default:
136343ec8e4SBenoit Canet         return (col * s->brightness) / 7;
13724859b68Sbalrog     }
13824859b68Sbalrog }
13924859b68Sbalrog 
set_lcd_pixel32(musicpal_lcd_state * s,int x,int y,uint32_t col)1409aee50eeSPeter Maydell static inline void set_lcd_pixel32(musicpal_lcd_state *s,
1419aee50eeSPeter Maydell                                    int x, int y, uint32_t col)
1429aee50eeSPeter Maydell {
1439aee50eeSPeter Maydell     int dx, dy;
1449aee50eeSPeter Maydell     DisplaySurface *surface = qemu_console_surface(s->con);
1459aee50eeSPeter Maydell     uint32_t *pixel =
1469aee50eeSPeter Maydell         &((uint32_t *) surface_data(surface))[(y * 128 * 3 + x) * 3];
1479aee50eeSPeter Maydell 
1489aee50eeSPeter Maydell     for (dy = 0; dy < 3; dy++, pixel += 127 * 3) {
1499aee50eeSPeter Maydell         for (dx = 0; dx < 3; dx++, pixel++) {
1509aee50eeSPeter Maydell             *pixel = col;
1510266f2c7Sbalrog         }
1529aee50eeSPeter Maydell     }
1539aee50eeSPeter Maydell }
15424859b68Sbalrog 
lcd_refresh(void * opaque)15524859b68Sbalrog static void lcd_refresh(void *opaque)
15624859b68Sbalrog {
15724859b68Sbalrog     musicpal_lcd_state *s = opaque;
1580266f2c7Sbalrog     int x, y, col;
15924859b68Sbalrog 
1609aee50eeSPeter Maydell     col = rgb_to_pixel32(scale_lcd_color(s, (MP_LCD_TEXTCOLOR >> 16) & 0xff),
1619aee50eeSPeter Maydell                          scale_lcd_color(s, (MP_LCD_TEXTCOLOR >> 8) & 0xff),
1629aee50eeSPeter Maydell                          scale_lcd_color(s, MP_LCD_TEXTCOLOR & 0xff));
1639aee50eeSPeter Maydell     for (x = 0; x < 128; x++) {
1649aee50eeSPeter Maydell         for (y = 0; y < 64; y++) {
1659aee50eeSPeter Maydell             if (s->video_ram[x + (y / 8) * 128] & (1 << (y % 8))) {
1669aee50eeSPeter Maydell                 set_lcd_pixel32(s, x, y, col);
1679aee50eeSPeter Maydell             } else {
1689aee50eeSPeter Maydell                 set_lcd_pixel32(s, x, y, 0);
1699aee50eeSPeter Maydell             }
1709aee50eeSPeter Maydell         }
1710266f2c7Sbalrog     }
17224859b68Sbalrog 
173c78f7137SGerd Hoffmann     dpy_gfx_update(s->con, 0, 0, 128*3, 64*3);
17424859b68Sbalrog }
17524859b68Sbalrog 
lcd_invalidate(void * opaque)176167bc3d2Sbalrog static void lcd_invalidate(void *opaque)
177167bc3d2Sbalrog {
178167bc3d2Sbalrog }
179167bc3d2Sbalrog 
musicpal_lcd_gpio_brightness_in(void * opaque,int irq,int level)1802c79fed3SStefan Weil static void musicpal_lcd_gpio_brightness_in(void *opaque, int irq, int level)
181343ec8e4SBenoit Canet {
182243cd13cSJan Kiszka     musicpal_lcd_state *s = opaque;
183343ec8e4SBenoit Canet     s->brightness &= ~(1 << irq);
184343ec8e4SBenoit Canet     s->brightness |= level << irq;
185343ec8e4SBenoit Canet }
186343ec8e4SBenoit Canet 
musicpal_lcd_read(void * opaque,hwaddr offset,unsigned size)187a8170e5eSAvi Kivity static uint64_t musicpal_lcd_read(void *opaque, hwaddr offset,
18819b4a424SAvi Kivity                                   unsigned size)
18924859b68Sbalrog {
19024859b68Sbalrog     musicpal_lcd_state *s = opaque;
19124859b68Sbalrog 
19224859b68Sbalrog     switch (offset) {
19324859b68Sbalrog     case MP_LCD_IRQCTRL:
19424859b68Sbalrog         return s->irqctrl;
19524859b68Sbalrog 
19624859b68Sbalrog     default:
19724859b68Sbalrog         return 0;
19824859b68Sbalrog     }
19924859b68Sbalrog }
20024859b68Sbalrog 
musicpal_lcd_write(void * opaque,hwaddr offset,uint64_t value,unsigned size)201a8170e5eSAvi Kivity static void musicpal_lcd_write(void *opaque, hwaddr offset,
20219b4a424SAvi Kivity                                uint64_t value, unsigned size)
20324859b68Sbalrog {
20424859b68Sbalrog     musicpal_lcd_state *s = opaque;
20524859b68Sbalrog 
20624859b68Sbalrog     switch (offset) {
20724859b68Sbalrog     case MP_LCD_IRQCTRL:
20824859b68Sbalrog         s->irqctrl = value;
20924859b68Sbalrog         break;
21024859b68Sbalrog 
21124859b68Sbalrog     case MP_LCD_SPICTRL:
21249fedd0dSJan Kiszka         if (value == MP_LCD_SPI_DATA || value == MP_LCD_SPI_CMD) {
21324859b68Sbalrog             s->mode = value;
21449fedd0dSJan Kiszka         } else {
21524859b68Sbalrog             s->mode = MP_LCD_SPI_INVALID;
21649fedd0dSJan Kiszka         }
21724859b68Sbalrog         break;
21824859b68Sbalrog 
21924859b68Sbalrog     case MP_LCD_INST:
22024859b68Sbalrog         if (value >= MP_LCD_INST_SETPAGE0 && value <= MP_LCD_INST_SETPAGE7) {
22124859b68Sbalrog             s->page = value - MP_LCD_INST_SETPAGE0;
22224859b68Sbalrog             s->page_off = 0;
22324859b68Sbalrog         }
22424859b68Sbalrog         break;
22524859b68Sbalrog 
22624859b68Sbalrog     case MP_LCD_DATA:
22724859b68Sbalrog         if (s->mode == MP_LCD_SPI_CMD) {
22824859b68Sbalrog             if (value >= MP_LCD_INST_SETPAGE0 &&
22924859b68Sbalrog                 value <= MP_LCD_INST_SETPAGE7) {
23024859b68Sbalrog                 s->page = value - MP_LCD_INST_SETPAGE0;
23124859b68Sbalrog                 s->page_off = 0;
23224859b68Sbalrog             }
23324859b68Sbalrog         } else if (s->mode == MP_LCD_SPI_DATA) {
23424859b68Sbalrog             s->video_ram[s->page*128 + s->page_off] = value;
23524859b68Sbalrog             s->page_off = (s->page_off + 1) & 127;
23624859b68Sbalrog         }
23724859b68Sbalrog         break;
23824859b68Sbalrog     }
23924859b68Sbalrog }
24024859b68Sbalrog 
24119b4a424SAvi Kivity static const MemoryRegionOps musicpal_lcd_ops = {
24219b4a424SAvi Kivity     .read = musicpal_lcd_read,
24319b4a424SAvi Kivity     .write = musicpal_lcd_write,
24419b4a424SAvi Kivity     .endianness = DEVICE_NATIVE_ENDIAN,
24524859b68Sbalrog };
24624859b68Sbalrog 
247380cd056SGerd Hoffmann static const GraphicHwOps musicpal_gfx_ops = {
248380cd056SGerd Hoffmann     .invalidate  = lcd_invalidate,
249380cd056SGerd Hoffmann     .gfx_update  = lcd_refresh,
250380cd056SGerd Hoffmann };
251380cd056SGerd Hoffmann 
musicpal_lcd_realize(DeviceState * dev,Error ** errp)252ece71994Sxiaoqiang zhao static void musicpal_lcd_realize(DeviceState *dev, Error **errp)
25324859b68Sbalrog {
254ece71994Sxiaoqiang zhao     musicpal_lcd_state *s = MUSICPAL_LCD(dev);
255ece71994Sxiaoqiang zhao     s->con = graphic_console_init(dev, 0, &musicpal_gfx_ops, s);
256ece71994Sxiaoqiang zhao     qemu_console_resize(s->con, 128 * 3, 64 * 3);
257ece71994Sxiaoqiang zhao }
258ece71994Sxiaoqiang zhao 
musicpal_lcd_init(Object * obj)259ece71994Sxiaoqiang zhao static void musicpal_lcd_init(Object *obj)
260ece71994Sxiaoqiang zhao {
261ece71994Sxiaoqiang zhao     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
2622cca58fdSAndreas Färber     DeviceState *dev = DEVICE(sbd);
2632cca58fdSAndreas Färber     musicpal_lcd_state *s = MUSICPAL_LCD(dev);
26424859b68Sbalrog 
265343ec8e4SBenoit Canet     s->brightness = 7;
266343ec8e4SBenoit Canet 
267ece71994Sxiaoqiang zhao     memory_region_init_io(&s->iomem, obj, &musicpal_lcd_ops, s,
26819b4a424SAvi Kivity                           "musicpal-lcd", MP_LCD_SIZE);
2692cca58fdSAndreas Färber     sysbus_init_mmio(sbd, &s->iomem);
27024859b68Sbalrog 
2712cca58fdSAndreas Färber     qdev_init_gpio_in(dev, musicpal_lcd_gpio_brightness_in, 3);
27224859b68Sbalrog }
27324859b68Sbalrog 
274d5b61dddSJan Kiszka static const VMStateDescription musicpal_lcd_vmsd = {
275d5b61dddSJan Kiszka     .name = "musicpal_lcd",
276d5b61dddSJan Kiszka     .version_id = 1,
277d5b61dddSJan Kiszka     .minimum_version_id = 1,
278607ef570SRichard Henderson     .fields = (const VMStateField[]) {
279d5b61dddSJan Kiszka         VMSTATE_UINT32(brightness, musicpal_lcd_state),
280d5b61dddSJan Kiszka         VMSTATE_UINT32(mode, musicpal_lcd_state),
281d5b61dddSJan Kiszka         VMSTATE_UINT32(irqctrl, musicpal_lcd_state),
282d5b61dddSJan Kiszka         VMSTATE_UINT32(page, musicpal_lcd_state),
283d5b61dddSJan Kiszka         VMSTATE_UINT32(page_off, musicpal_lcd_state),
284d5b61dddSJan Kiszka         VMSTATE_BUFFER(video_ram, musicpal_lcd_state),
285d5b61dddSJan Kiszka         VMSTATE_END_OF_LIST()
286d5b61dddSJan Kiszka     }
287d5b61dddSJan Kiszka };
288d5b61dddSJan Kiszka 
musicpal_lcd_class_init(ObjectClass * klass,const void * data)289*12d1a768SPhilippe Mathieu-Daudé static void musicpal_lcd_class_init(ObjectClass *klass, const void *data)
290999e12bbSAnthony Liguori {
29139bffca2SAnthony Liguori     DeviceClass *dc = DEVICE_CLASS(klass);
292999e12bbSAnthony Liguori 
29339bffca2SAnthony Liguori     dc->vmsd = &musicpal_lcd_vmsd;
294ece71994Sxiaoqiang zhao     dc->realize = musicpal_lcd_realize;
295999e12bbSAnthony Liguori }
296999e12bbSAnthony Liguori 
2978c43a6f0SAndreas Färber static const TypeInfo musicpal_lcd_info = {
2982cca58fdSAndreas Färber     .name          = TYPE_MUSICPAL_LCD,
29939bffca2SAnthony Liguori     .parent        = TYPE_SYS_BUS_DEVICE,
30039bffca2SAnthony Liguori     .instance_size = sizeof(musicpal_lcd_state),
301ece71994Sxiaoqiang zhao     .instance_init = musicpal_lcd_init,
302999e12bbSAnthony Liguori     .class_init    = musicpal_lcd_class_init,
303d5b61dddSJan Kiszka };
304d5b61dddSJan Kiszka 
30524859b68Sbalrog /* PIC register offsets */
30624859b68Sbalrog #define MP_PIC_STATUS           0x00
30724859b68Sbalrog #define MP_PIC_ENABLE_SET       0x08
30824859b68Sbalrog #define MP_PIC_ENABLE_CLR       0x0C
30924859b68Sbalrog 
310c7bd0fd9SAndreas Färber #define TYPE_MV88W8618_PIC "mv88w8618_pic"
3118063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(mv88w8618_pic_state, MV88W8618_PIC)
312c7bd0fd9SAndreas Färber 
313db1015e9SEduardo Habkost struct mv88w8618_pic_state {
314c7bd0fd9SAndreas Färber     /*< private >*/
315c7bd0fd9SAndreas Färber     SysBusDevice parent_obj;
316c7bd0fd9SAndreas Färber     /*< public >*/
317c7bd0fd9SAndreas Färber 
31819b4a424SAvi Kivity     MemoryRegion iomem;
31924859b68Sbalrog     uint32_t level;
32024859b68Sbalrog     uint32_t enabled;
32124859b68Sbalrog     qemu_irq parent_irq;
322db1015e9SEduardo Habkost };
32324859b68Sbalrog 
mv88w8618_pic_update(mv88w8618_pic_state * s)32424859b68Sbalrog static void mv88w8618_pic_update(mv88w8618_pic_state *s)
32524859b68Sbalrog {
32624859b68Sbalrog     qemu_set_irq(s->parent_irq, (s->level & s->enabled));
32724859b68Sbalrog }
32824859b68Sbalrog 
mv88w8618_pic_set_irq(void * opaque,int irq,int level)32924859b68Sbalrog static void mv88w8618_pic_set_irq(void *opaque, int irq, int level)
33024859b68Sbalrog {
33124859b68Sbalrog     mv88w8618_pic_state *s = opaque;
33224859b68Sbalrog 
33349fedd0dSJan Kiszka     if (level) {
33424859b68Sbalrog         s->level |= 1 << irq;
33549fedd0dSJan Kiszka     } else {
33624859b68Sbalrog         s->level &= ~(1 << irq);
33749fedd0dSJan Kiszka     }
33824859b68Sbalrog     mv88w8618_pic_update(s);
33924859b68Sbalrog }
34024859b68Sbalrog 
mv88w8618_pic_read(void * opaque,hwaddr offset,unsigned size)341a8170e5eSAvi Kivity static uint64_t mv88w8618_pic_read(void *opaque, hwaddr offset,
34219b4a424SAvi Kivity                                    unsigned size)
34324859b68Sbalrog {
34424859b68Sbalrog     mv88w8618_pic_state *s = opaque;
34524859b68Sbalrog 
34624859b68Sbalrog     switch (offset) {
34724859b68Sbalrog     case MP_PIC_STATUS:
34824859b68Sbalrog         return s->level & s->enabled;
34924859b68Sbalrog 
35024859b68Sbalrog     default:
35124859b68Sbalrog         return 0;
35224859b68Sbalrog     }
35324859b68Sbalrog }
35424859b68Sbalrog 
mv88w8618_pic_write(void * opaque,hwaddr offset,uint64_t value,unsigned size)355a8170e5eSAvi Kivity static void mv88w8618_pic_write(void *opaque, hwaddr offset,
35619b4a424SAvi Kivity                                 uint64_t value, unsigned size)
35724859b68Sbalrog {
35824859b68Sbalrog     mv88w8618_pic_state *s = opaque;
35924859b68Sbalrog 
36024859b68Sbalrog     switch (offset) {
36124859b68Sbalrog     case MP_PIC_ENABLE_SET:
36224859b68Sbalrog         s->enabled |= value;
36324859b68Sbalrog         break;
36424859b68Sbalrog 
36524859b68Sbalrog     case MP_PIC_ENABLE_CLR:
36624859b68Sbalrog         s->enabled &= ~value;
36724859b68Sbalrog         s->level &= ~value;
36824859b68Sbalrog         break;
36924859b68Sbalrog     }
37024859b68Sbalrog     mv88w8618_pic_update(s);
37124859b68Sbalrog }
37224859b68Sbalrog 
mv88w8618_pic_reset(DeviceState * d)373d5b61dddSJan Kiszka static void mv88w8618_pic_reset(DeviceState *d)
37424859b68Sbalrog {
375c7bd0fd9SAndreas Färber     mv88w8618_pic_state *s = MV88W8618_PIC(d);
37624859b68Sbalrog 
37724859b68Sbalrog     s->level = 0;
37824859b68Sbalrog     s->enabled = 0;
37924859b68Sbalrog }
38024859b68Sbalrog 
38119b4a424SAvi Kivity static const MemoryRegionOps mv88w8618_pic_ops = {
38219b4a424SAvi Kivity     .read = mv88w8618_pic_read,
38319b4a424SAvi Kivity     .write = mv88w8618_pic_write,
38419b4a424SAvi Kivity     .endianness = DEVICE_NATIVE_ENDIAN,
38524859b68Sbalrog };
38624859b68Sbalrog 
mv88w8618_pic_init(Object * obj)387ece71994Sxiaoqiang zhao static void mv88w8618_pic_init(Object *obj)
38824859b68Sbalrog {
389ece71994Sxiaoqiang zhao     SysBusDevice *dev = SYS_BUS_DEVICE(obj);
390c7bd0fd9SAndreas Färber     mv88w8618_pic_state *s = MV88W8618_PIC(dev);
39124859b68Sbalrog 
392c7bd0fd9SAndreas Färber     qdev_init_gpio_in(DEVICE(dev), mv88w8618_pic_set_irq, 32);
393b47b50faSPaul Brook     sysbus_init_irq(dev, &s->parent_irq);
394ece71994Sxiaoqiang zhao     memory_region_init_io(&s->iomem, obj, &mv88w8618_pic_ops, s,
39519b4a424SAvi Kivity                           "musicpal-pic", MP_PIC_SIZE);
396750ecd44SAvi Kivity     sysbus_init_mmio(dev, &s->iomem);
39724859b68Sbalrog }
39824859b68Sbalrog 
399d5b61dddSJan Kiszka static const VMStateDescription mv88w8618_pic_vmsd = {
400d5b61dddSJan Kiszka     .name = "mv88w8618_pic",
401d5b61dddSJan Kiszka     .version_id = 1,
402d5b61dddSJan Kiszka     .minimum_version_id = 1,
403607ef570SRichard Henderson     .fields = (const VMStateField[]) {
404d5b61dddSJan Kiszka         VMSTATE_UINT32(level, mv88w8618_pic_state),
405d5b61dddSJan Kiszka         VMSTATE_UINT32(enabled, mv88w8618_pic_state),
406d5b61dddSJan Kiszka         VMSTATE_END_OF_LIST()
407d5b61dddSJan Kiszka     }
408d5b61dddSJan Kiszka };
409d5b61dddSJan Kiszka 
mv88w8618_pic_class_init(ObjectClass * klass,const void * data)410*12d1a768SPhilippe Mathieu-Daudé static void mv88w8618_pic_class_init(ObjectClass *klass, const void *data)
411999e12bbSAnthony Liguori {
41239bffca2SAnthony Liguori     DeviceClass *dc = DEVICE_CLASS(klass);
413999e12bbSAnthony Liguori 
414e3d08143SPeter Maydell     device_class_set_legacy_reset(dc, mv88w8618_pic_reset);
41539bffca2SAnthony Liguori     dc->vmsd = &mv88w8618_pic_vmsd;
416999e12bbSAnthony Liguori }
417999e12bbSAnthony Liguori 
4188c43a6f0SAndreas Färber static const TypeInfo mv88w8618_pic_info = {
419c7bd0fd9SAndreas Färber     .name          = TYPE_MV88W8618_PIC,
42039bffca2SAnthony Liguori     .parent        = TYPE_SYS_BUS_DEVICE,
42139bffca2SAnthony Liguori     .instance_size = sizeof(mv88w8618_pic_state),
422ece71994Sxiaoqiang zhao     .instance_init = mv88w8618_pic_init,
423999e12bbSAnthony Liguori     .class_init    = mv88w8618_pic_class_init,
424d5b61dddSJan Kiszka };
425d5b61dddSJan Kiszka 
42624859b68Sbalrog /* PIT register offsets */
42724859b68Sbalrog #define MP_PIT_TIMER1_LENGTH    0x00
42824859b68Sbalrog /* ... */
42924859b68Sbalrog #define MP_PIT_TIMER4_LENGTH    0x0C
43024859b68Sbalrog #define MP_PIT_CONTROL          0x10
43124859b68Sbalrog #define MP_PIT_TIMER1_VALUE     0x14
43224859b68Sbalrog /* ... */
43324859b68Sbalrog #define MP_PIT_TIMER4_VALUE     0x20
43424859b68Sbalrog #define MP_BOARD_RESET          0x34
43524859b68Sbalrog 
43624859b68Sbalrog /* Magic board reset value (probably some watchdog behind it) */
43724859b68Sbalrog #define MP_BOARD_RESET_MAGIC    0x10000
43824859b68Sbalrog 
43924859b68Sbalrog typedef struct mv88w8618_timer_state {
440b47b50faSPaul Brook     ptimer_state *ptimer;
44124859b68Sbalrog     uint32_t limit;
44224859b68Sbalrog     int freq;
44324859b68Sbalrog     qemu_irq irq;
44424859b68Sbalrog } mv88w8618_timer_state;
44524859b68Sbalrog 
4464adc8541SAndreas Färber #define TYPE_MV88W8618_PIT "mv88w8618_pit"
4478063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(mv88w8618_pit_state, MV88W8618_PIT)
4484adc8541SAndreas Färber 
449db1015e9SEduardo Habkost struct mv88w8618_pit_state {
4504adc8541SAndreas Färber     /*< private >*/
4514adc8541SAndreas Färber     SysBusDevice parent_obj;
4524adc8541SAndreas Färber     /*< public >*/
4534adc8541SAndreas Färber 
45419b4a424SAvi Kivity     MemoryRegion iomem;
455b47b50faSPaul Brook     mv88w8618_timer_state timer[4];
456db1015e9SEduardo Habkost };
45724859b68Sbalrog 
mv88w8618_timer_tick(void * opaque)45824859b68Sbalrog static void mv88w8618_timer_tick(void *opaque)
45924859b68Sbalrog {
46024859b68Sbalrog     mv88w8618_timer_state *s = opaque;
46124859b68Sbalrog 
46224859b68Sbalrog     qemu_irq_raise(s->irq);
46324859b68Sbalrog }
46424859b68Sbalrog 
mv88w8618_timer_init(SysBusDevice * dev,mv88w8618_timer_state * s,uint32_t freq)465b47b50faSPaul Brook static void mv88w8618_timer_init(SysBusDevice *dev, mv88w8618_timer_state *s,
466b47b50faSPaul Brook                                  uint32_t freq)
46724859b68Sbalrog {
468b47b50faSPaul Brook     sysbus_init_irq(dev, &s->irq);
46924859b68Sbalrog     s->freq = freq;
47024859b68Sbalrog 
4719598c1bbSPeter Maydell     s->ptimer = ptimer_init(mv88w8618_timer_tick, s, PTIMER_POLICY_LEGACY);
47224859b68Sbalrog }
47324859b68Sbalrog 
mv88w8618_pit_read(void * opaque,hwaddr offset,unsigned size)474a8170e5eSAvi Kivity static uint64_t mv88w8618_pit_read(void *opaque, hwaddr offset,
47519b4a424SAvi Kivity                                    unsigned size)
47624859b68Sbalrog {
47724859b68Sbalrog     mv88w8618_pit_state *s = opaque;
47824859b68Sbalrog     mv88w8618_timer_state *t;
47924859b68Sbalrog 
48024859b68Sbalrog     switch (offset) {
48124859b68Sbalrog     case MP_PIT_TIMER1_VALUE ... MP_PIT_TIMER4_VALUE:
482b47b50faSPaul Brook         t = &s->timer[(offset-MP_PIT_TIMER1_VALUE) >> 2];
483b47b50faSPaul Brook         return ptimer_get_count(t->ptimer);
48424859b68Sbalrog 
48524859b68Sbalrog     default:
48624859b68Sbalrog         return 0;
48724859b68Sbalrog     }
48824859b68Sbalrog }
48924859b68Sbalrog 
mv88w8618_pit_write(void * opaque,hwaddr offset,uint64_t value,unsigned size)490a8170e5eSAvi Kivity static void mv88w8618_pit_write(void *opaque, hwaddr offset,
49119b4a424SAvi Kivity                                 uint64_t value, unsigned size)
49224859b68Sbalrog {
49324859b68Sbalrog     mv88w8618_pit_state *s = opaque;
49424859b68Sbalrog     mv88w8618_timer_state *t;
49524859b68Sbalrog     int i;
49624859b68Sbalrog 
49724859b68Sbalrog     switch (offset) {
49824859b68Sbalrog     case MP_PIT_TIMER1_LENGTH ... MP_PIT_TIMER4_LENGTH:
499b47b50faSPaul Brook         t = &s->timer[offset >> 2];
50024859b68Sbalrog         t->limit = value;
501d8052a2eSPeter Maydell         ptimer_transaction_begin(t->ptimer);
502c88d6bdeSJan Kiszka         if (t->limit > 0) {
503b47b50faSPaul Brook             ptimer_set_limit(t->ptimer, t->limit, 1);
504c88d6bdeSJan Kiszka         } else {
505c88d6bdeSJan Kiszka             ptimer_stop(t->ptimer);
506c88d6bdeSJan Kiszka         }
507d8052a2eSPeter Maydell         ptimer_transaction_commit(t->ptimer);
50824859b68Sbalrog         break;
50924859b68Sbalrog 
51024859b68Sbalrog     case MP_PIT_CONTROL:
51124859b68Sbalrog         for (i = 0; i < 4; i++) {
512b47b50faSPaul Brook             t = &s->timer[i];
513d8052a2eSPeter Maydell             ptimer_transaction_begin(t->ptimer);
514c88d6bdeSJan Kiszka             if (value & 0xf && t->limit > 0) {
515b47b50faSPaul Brook                 ptimer_set_limit(t->ptimer, t->limit, 0);
516b47b50faSPaul Brook                 ptimer_set_freq(t->ptimer, t->freq);
517b47b50faSPaul Brook                 ptimer_run(t->ptimer, 0);
518c88d6bdeSJan Kiszka             } else {
519c88d6bdeSJan Kiszka                 ptimer_stop(t->ptimer);
52024859b68Sbalrog             }
521d8052a2eSPeter Maydell             ptimer_transaction_commit(t->ptimer);
52224859b68Sbalrog             value >>= 4;
52324859b68Sbalrog         }
52424859b68Sbalrog         break;
52524859b68Sbalrog 
52624859b68Sbalrog     case MP_BOARD_RESET:
52749fedd0dSJan Kiszka         if (value == MP_BOARD_RESET_MAGIC) {
528cf83f140SEric Blake             qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
52949fedd0dSJan Kiszka         }
53024859b68Sbalrog         break;
53124859b68Sbalrog     }
53224859b68Sbalrog }
53324859b68Sbalrog 
mv88w8618_pit_reset(DeviceState * d)534d5b61dddSJan Kiszka static void mv88w8618_pit_reset(DeviceState *d)
535c88d6bdeSJan Kiszka {
5364adc8541SAndreas Färber     mv88w8618_pit_state *s = MV88W8618_PIT(d);
537c88d6bdeSJan Kiszka     int i;
538c88d6bdeSJan Kiszka 
539c88d6bdeSJan Kiszka     for (i = 0; i < 4; i++) {
540d8052a2eSPeter Maydell         mv88w8618_timer_state *t = &s->timer[i];
541d8052a2eSPeter Maydell         ptimer_transaction_begin(t->ptimer);
542d8052a2eSPeter Maydell         ptimer_stop(t->ptimer);
543d8052a2eSPeter Maydell         ptimer_transaction_commit(t->ptimer);
544d8052a2eSPeter Maydell         t->limit = 0;
545c88d6bdeSJan Kiszka     }
546c88d6bdeSJan Kiszka }
547c88d6bdeSJan Kiszka 
54819b4a424SAvi Kivity static const MemoryRegionOps mv88w8618_pit_ops = {
54919b4a424SAvi Kivity     .read = mv88w8618_pit_read,
55019b4a424SAvi Kivity     .write = mv88w8618_pit_write,
55119b4a424SAvi Kivity     .endianness = DEVICE_NATIVE_ENDIAN,
55224859b68Sbalrog };
55324859b68Sbalrog 
mv88w8618_pit_init(Object * obj)554ece71994Sxiaoqiang zhao static void mv88w8618_pit_init(Object *obj)
55524859b68Sbalrog {
556ece71994Sxiaoqiang zhao     SysBusDevice *dev = SYS_BUS_DEVICE(obj);
5574adc8541SAndreas Färber     mv88w8618_pit_state *s = MV88W8618_PIT(dev);
558b47b50faSPaul Brook     int i;
55924859b68Sbalrog 
56024859b68Sbalrog     /* Letting them all run at 1 MHz is likely just a pragmatic
56124859b68Sbalrog      * simplification. */
562b47b50faSPaul Brook     for (i = 0; i < 4; i++) {
563b47b50faSPaul Brook         mv88w8618_timer_init(dev, &s->timer[i], 1000000);
564b47b50faSPaul Brook     }
56524859b68Sbalrog 
566ece71994Sxiaoqiang zhao     memory_region_init_io(&s->iomem, obj, &mv88w8618_pit_ops, s,
56719b4a424SAvi Kivity                           "musicpal-pit", MP_PIT_SIZE);
568750ecd44SAvi Kivity     sysbus_init_mmio(dev, &s->iomem);
56924859b68Sbalrog }
57024859b68Sbalrog 
mv88w8618_pit_finalize(Object * obj)571a4bc0334SGan Qixin static void mv88w8618_pit_finalize(Object *obj)
572a4bc0334SGan Qixin {
573a4bc0334SGan Qixin     SysBusDevice *dev = SYS_BUS_DEVICE(obj);
574a4bc0334SGan Qixin     mv88w8618_pit_state *s = MV88W8618_PIT(dev);
575a4bc0334SGan Qixin     int i;
576a4bc0334SGan Qixin 
577a4bc0334SGan Qixin     for (i = 0; i < 4; i++) {
578a4bc0334SGan Qixin         ptimer_free(s->timer[i].ptimer);
579a4bc0334SGan Qixin     }
580a4bc0334SGan Qixin }
581a4bc0334SGan Qixin 
582d5b61dddSJan Kiszka static const VMStateDescription mv88w8618_timer_vmsd = {
583d5b61dddSJan Kiszka     .name = "timer",
584d5b61dddSJan Kiszka     .version_id = 1,
585d5b61dddSJan Kiszka     .minimum_version_id = 1,
586607ef570SRichard Henderson     .fields = (const VMStateField[]) {
587d5b61dddSJan Kiszka         VMSTATE_PTIMER(ptimer, mv88w8618_timer_state),
588d5b61dddSJan Kiszka         VMSTATE_UINT32(limit, mv88w8618_timer_state),
589d5b61dddSJan Kiszka         VMSTATE_END_OF_LIST()
590d5b61dddSJan Kiszka     }
591d5b61dddSJan Kiszka };
592d5b61dddSJan Kiszka 
593d5b61dddSJan Kiszka static const VMStateDescription mv88w8618_pit_vmsd = {
594d5b61dddSJan Kiszka     .name = "mv88w8618_pit",
595d5b61dddSJan Kiszka     .version_id = 1,
596d5b61dddSJan Kiszka     .minimum_version_id = 1,
597607ef570SRichard Henderson     .fields = (const VMStateField[]) {
598d5b61dddSJan Kiszka         VMSTATE_STRUCT_ARRAY(timer, mv88w8618_pit_state, 4, 1,
599d5b61dddSJan Kiszka                              mv88w8618_timer_vmsd, mv88w8618_timer_state),
600d5b61dddSJan Kiszka         VMSTATE_END_OF_LIST()
601d5b61dddSJan Kiszka     }
602d5b61dddSJan Kiszka };
603d5b61dddSJan Kiszka 
mv88w8618_pit_class_init(ObjectClass * klass,const void * data)604*12d1a768SPhilippe Mathieu-Daudé static void mv88w8618_pit_class_init(ObjectClass *klass, const void *data)
605999e12bbSAnthony Liguori {
60639bffca2SAnthony Liguori     DeviceClass *dc = DEVICE_CLASS(klass);
607999e12bbSAnthony Liguori 
608e3d08143SPeter Maydell     device_class_set_legacy_reset(dc, mv88w8618_pit_reset);
60939bffca2SAnthony Liguori     dc->vmsd = &mv88w8618_pit_vmsd;
610999e12bbSAnthony Liguori }
611999e12bbSAnthony Liguori 
6128c43a6f0SAndreas Färber static const TypeInfo mv88w8618_pit_info = {
6134adc8541SAndreas Färber     .name          = TYPE_MV88W8618_PIT,
61439bffca2SAnthony Liguori     .parent        = TYPE_SYS_BUS_DEVICE,
61539bffca2SAnthony Liguori     .instance_size = sizeof(mv88w8618_pit_state),
616ece71994Sxiaoqiang zhao     .instance_init = mv88w8618_pit_init,
617a4bc0334SGan Qixin     .instance_finalize = mv88w8618_pit_finalize,
618999e12bbSAnthony Liguori     .class_init    = mv88w8618_pit_class_init,
619c88d6bdeSJan Kiszka };
620c88d6bdeSJan Kiszka 
62124859b68Sbalrog /* Flash config register offsets */
62224859b68Sbalrog #define MP_FLASHCFG_CFGR0    0x04
62324859b68Sbalrog 
6245952b01cSAndreas Färber #define TYPE_MV88W8618_FLASHCFG "mv88w8618_flashcfg"
6258063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(mv88w8618_flashcfg_state, MV88W8618_FLASHCFG)
6265952b01cSAndreas Färber 
627db1015e9SEduardo Habkost struct mv88w8618_flashcfg_state {
6285952b01cSAndreas Färber     /*< private >*/
6295952b01cSAndreas Färber     SysBusDevice parent_obj;
6305952b01cSAndreas Färber     /*< public >*/
6315952b01cSAndreas Färber 
63219b4a424SAvi Kivity     MemoryRegion iomem;
63324859b68Sbalrog     uint32_t cfgr0;
634db1015e9SEduardo Habkost };
63524859b68Sbalrog 
mv88w8618_flashcfg_read(void * opaque,hwaddr offset,unsigned size)63619b4a424SAvi Kivity static uint64_t mv88w8618_flashcfg_read(void *opaque,
637a8170e5eSAvi Kivity                                         hwaddr offset,
63819b4a424SAvi Kivity                                         unsigned size)
63924859b68Sbalrog {
64024859b68Sbalrog     mv88w8618_flashcfg_state *s = opaque;
64124859b68Sbalrog 
64224859b68Sbalrog     switch (offset) {
64324859b68Sbalrog     case MP_FLASHCFG_CFGR0:
64424859b68Sbalrog         return s->cfgr0;
64524859b68Sbalrog 
64624859b68Sbalrog     default:
64724859b68Sbalrog         return 0;
64824859b68Sbalrog     }
64924859b68Sbalrog }
65024859b68Sbalrog 
mv88w8618_flashcfg_write(void * opaque,hwaddr offset,uint64_t value,unsigned size)651a8170e5eSAvi Kivity static void mv88w8618_flashcfg_write(void *opaque, hwaddr offset,
65219b4a424SAvi Kivity                                      uint64_t value, unsigned size)
65324859b68Sbalrog {
65424859b68Sbalrog     mv88w8618_flashcfg_state *s = opaque;
65524859b68Sbalrog 
65624859b68Sbalrog     switch (offset) {
65724859b68Sbalrog     case MP_FLASHCFG_CFGR0:
65824859b68Sbalrog         s->cfgr0 = value;
65924859b68Sbalrog         break;
66024859b68Sbalrog     }
66124859b68Sbalrog }
66224859b68Sbalrog 
66319b4a424SAvi Kivity static const MemoryRegionOps mv88w8618_flashcfg_ops = {
66419b4a424SAvi Kivity     .read = mv88w8618_flashcfg_read,
66519b4a424SAvi Kivity     .write = mv88w8618_flashcfg_write,
66619b4a424SAvi Kivity     .endianness = DEVICE_NATIVE_ENDIAN,
66724859b68Sbalrog };
66824859b68Sbalrog 
mv88w8618_flashcfg_init(Object * obj)669ece71994Sxiaoqiang zhao static void mv88w8618_flashcfg_init(Object *obj)
67024859b68Sbalrog {
671ece71994Sxiaoqiang zhao     SysBusDevice *dev = SYS_BUS_DEVICE(obj);
6725952b01cSAndreas Färber     mv88w8618_flashcfg_state *s = MV88W8618_FLASHCFG(dev);
67324859b68Sbalrog 
67424859b68Sbalrog     s->cfgr0 = 0xfffe4285; /* Default as set by U-Boot for 8 MB flash */
675ece71994Sxiaoqiang zhao     memory_region_init_io(&s->iomem, obj, &mv88w8618_flashcfg_ops, s,
67619b4a424SAvi Kivity                           "musicpal-flashcfg", MP_FLASHCFG_SIZE);
677750ecd44SAvi Kivity     sysbus_init_mmio(dev, &s->iomem);
67824859b68Sbalrog }
67924859b68Sbalrog 
680d5b61dddSJan Kiszka static const VMStateDescription mv88w8618_flashcfg_vmsd = {
681d5b61dddSJan Kiszka     .name = "mv88w8618_flashcfg",
682d5b61dddSJan Kiszka     .version_id = 1,
683d5b61dddSJan Kiszka     .minimum_version_id = 1,
684607ef570SRichard Henderson     .fields = (const VMStateField[]) {
685d5b61dddSJan Kiszka         VMSTATE_UINT32(cfgr0, mv88w8618_flashcfg_state),
686d5b61dddSJan Kiszka         VMSTATE_END_OF_LIST()
687d5b61dddSJan Kiszka     }
688d5b61dddSJan Kiszka };
689d5b61dddSJan Kiszka 
mv88w8618_flashcfg_class_init(ObjectClass * klass,const void * data)690*12d1a768SPhilippe Mathieu-Daudé static void mv88w8618_flashcfg_class_init(ObjectClass *klass, const void *data)
691999e12bbSAnthony Liguori {
69239bffca2SAnthony Liguori     DeviceClass *dc = DEVICE_CLASS(klass);
693999e12bbSAnthony Liguori 
69439bffca2SAnthony Liguori     dc->vmsd = &mv88w8618_flashcfg_vmsd;
695999e12bbSAnthony Liguori }
696999e12bbSAnthony Liguori 
6978c43a6f0SAndreas Färber static const TypeInfo mv88w8618_flashcfg_info = {
6985952b01cSAndreas Färber     .name          = TYPE_MV88W8618_FLASHCFG,
69939bffca2SAnthony Liguori     .parent        = TYPE_SYS_BUS_DEVICE,
70039bffca2SAnthony Liguori     .instance_size = sizeof(mv88w8618_flashcfg_state),
701ece71994Sxiaoqiang zhao     .instance_init = mv88w8618_flashcfg_init,
702999e12bbSAnthony Liguori     .class_init    = mv88w8618_flashcfg_class_init,
703d5b61dddSJan Kiszka };
704d5b61dddSJan Kiszka 
705718ec0beSmalc /* Misc register offsets */
706718ec0beSmalc #define MP_MISC_BOARD_REVISION  0x18
70724859b68Sbalrog 
708718ec0beSmalc #define MP_BOARD_REVISION       0x31
70924859b68Sbalrog 
710db1015e9SEduardo Habkost struct MusicPalMiscState {
711a86f200aSPeter Maydell     SysBusDevice parent_obj;
712a86f200aSPeter Maydell     MemoryRegion iomem;
713db1015e9SEduardo Habkost };
714a86f200aSPeter Maydell 
715a86f200aSPeter Maydell #define TYPE_MUSICPAL_MISC "musicpal-misc"
OBJECT_DECLARE_SIMPLE_TYPE(MusicPalMiscState,MUSICPAL_MISC)7168063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(MusicPalMiscState, MUSICPAL_MISC)
717a86f200aSPeter Maydell 
718a8170e5eSAvi Kivity static uint64_t musicpal_misc_read(void *opaque, hwaddr offset,
71919b4a424SAvi Kivity                                    unsigned size)
720718ec0beSmalc {
721718ec0beSmalc     switch (offset) {
722718ec0beSmalc     case MP_MISC_BOARD_REVISION:
723718ec0beSmalc         return MP_BOARD_REVISION;
724718ec0beSmalc 
725718ec0beSmalc     default:
726718ec0beSmalc         return 0;
727718ec0beSmalc     }
728718ec0beSmalc }
729718ec0beSmalc 
musicpal_misc_write(void * opaque,hwaddr offset,uint64_t value,unsigned size)730a8170e5eSAvi Kivity static void musicpal_misc_write(void *opaque, hwaddr offset,
73119b4a424SAvi Kivity                                 uint64_t value, unsigned size)
732718ec0beSmalc {
733718ec0beSmalc }
734718ec0beSmalc 
73519b4a424SAvi Kivity static const MemoryRegionOps musicpal_misc_ops = {
73619b4a424SAvi Kivity     .read = musicpal_misc_read,
73719b4a424SAvi Kivity     .write = musicpal_misc_write,
73819b4a424SAvi Kivity     .endianness = DEVICE_NATIVE_ENDIAN,
739718ec0beSmalc };
740718ec0beSmalc 
musicpal_misc_init(Object * obj)741a86f200aSPeter Maydell static void musicpal_misc_init(Object *obj)
742718ec0beSmalc {
743a86f200aSPeter Maydell     SysBusDevice *sd = SYS_BUS_DEVICE(obj);
744a86f200aSPeter Maydell     MusicPalMiscState *s = MUSICPAL_MISC(obj);
745718ec0beSmalc 
74664bde0f3SPaolo Bonzini     memory_region_init_io(&s->iomem, OBJECT(s), &musicpal_misc_ops, NULL,
74719b4a424SAvi Kivity                           "musicpal-misc", MP_MISC_SIZE);
748a86f200aSPeter Maydell     sysbus_init_mmio(sd, &s->iomem);
749718ec0beSmalc }
750718ec0beSmalc 
751a86f200aSPeter Maydell static const TypeInfo musicpal_misc_info = {
752a86f200aSPeter Maydell     .name = TYPE_MUSICPAL_MISC,
753a86f200aSPeter Maydell     .parent = TYPE_SYS_BUS_DEVICE,
754a86f200aSPeter Maydell     .instance_init = musicpal_misc_init,
755a86f200aSPeter Maydell     .instance_size = sizeof(MusicPalMiscState),
756a86f200aSPeter Maydell };
757a86f200aSPeter Maydell 
758718ec0beSmalc /* WLAN register offsets */
759718ec0beSmalc #define MP_WLAN_MAGIC1          0x11c
760718ec0beSmalc #define MP_WLAN_MAGIC2          0x124
761718ec0beSmalc 
mv88w8618_wlan_read(void * opaque,hwaddr offset,unsigned size)762a8170e5eSAvi Kivity static uint64_t mv88w8618_wlan_read(void *opaque, hwaddr offset,
76319b4a424SAvi Kivity                                     unsigned size)
764718ec0beSmalc {
765718ec0beSmalc     switch (offset) {
766718ec0beSmalc     /* Workaround to allow loading the binary-only wlandrv.ko crap
767718ec0beSmalc      * from the original Freecom firmware. */
768718ec0beSmalc     case MP_WLAN_MAGIC1:
769718ec0beSmalc         return ~3;
770718ec0beSmalc     case MP_WLAN_MAGIC2:
771718ec0beSmalc         return -1;
772718ec0beSmalc 
773718ec0beSmalc     default:
774718ec0beSmalc         return 0;
775718ec0beSmalc     }
776718ec0beSmalc }
777718ec0beSmalc 
mv88w8618_wlan_write(void * opaque,hwaddr offset,uint64_t value,unsigned size)778a8170e5eSAvi Kivity static void mv88w8618_wlan_write(void *opaque, hwaddr offset,
77919b4a424SAvi Kivity                                  uint64_t value, unsigned size)
780718ec0beSmalc {
781718ec0beSmalc }
782718ec0beSmalc 
78319b4a424SAvi Kivity static const MemoryRegionOps mv88w8618_wlan_ops = {
78419b4a424SAvi Kivity     .read = mv88w8618_wlan_read,
78519b4a424SAvi Kivity     .write =mv88w8618_wlan_write,
78619b4a424SAvi Kivity     .endianness = DEVICE_NATIVE_ENDIAN,
787718ec0beSmalc };
788718ec0beSmalc 
mv88w8618_wlan_realize(DeviceState * dev,Error ** errp)7897f7420a0SMao Zhongyi static void mv88w8618_wlan_realize(DeviceState *dev, Error **errp)
790718ec0beSmalc {
79119b4a424SAvi Kivity     MemoryRegion *iomem = g_new(MemoryRegion, 1);
792718ec0beSmalc 
79364bde0f3SPaolo Bonzini     memory_region_init_io(iomem, OBJECT(dev), &mv88w8618_wlan_ops, NULL,
79419b4a424SAvi Kivity                           "musicpal-wlan", MP_WLAN_SIZE);
7957f7420a0SMao Zhongyi     sysbus_init_mmio(SYS_BUS_DEVICE(dev), iomem);
796718ec0beSmalc }
797718ec0beSmalc 
798718ec0beSmalc /* GPIO register offsets */
799718ec0beSmalc #define MP_GPIO_OE_LO           0x008
800718ec0beSmalc #define MP_GPIO_OUT_LO          0x00c
801718ec0beSmalc #define MP_GPIO_IN_LO           0x010
802708afdf3SJan Kiszka #define MP_GPIO_IER_LO          0x014
803708afdf3SJan Kiszka #define MP_GPIO_IMR_LO          0x018
804718ec0beSmalc #define MP_GPIO_ISR_LO          0x020
805718ec0beSmalc #define MP_GPIO_OE_HI           0x508
806718ec0beSmalc #define MP_GPIO_OUT_HI          0x50c
807718ec0beSmalc #define MP_GPIO_IN_HI           0x510
808708afdf3SJan Kiszka #define MP_GPIO_IER_HI          0x514
809708afdf3SJan Kiszka #define MP_GPIO_IMR_HI          0x518
810718ec0beSmalc #define MP_GPIO_ISR_HI          0x520
81124859b68Sbalrog 
81224859b68Sbalrog /* GPIO bits & masks */
81324859b68Sbalrog #define MP_GPIO_LCD_BRIGHTNESS  0x00070000
81424859b68Sbalrog #define MP_GPIO_I2C_DATA_BIT    29
81524859b68Sbalrog #define MP_GPIO_I2C_CLOCK_BIT   30
81624859b68Sbalrog 
81724859b68Sbalrog /* LCD brightness bits in GPIO_OE_HI */
81824859b68Sbalrog #define MP_OE_LCD_BRIGHTNESS    0x0007
81924859b68Sbalrog 
8207012d4b4SAndreas Färber #define TYPE_MUSICPAL_GPIO "musicpal_gpio"
8218063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(musicpal_gpio_state, MUSICPAL_GPIO)
8227012d4b4SAndreas Färber 
823db1015e9SEduardo Habkost struct musicpal_gpio_state {
8247012d4b4SAndreas Färber     /*< private >*/
8257012d4b4SAndreas Färber     SysBusDevice parent_obj;
8267012d4b4SAndreas Färber     /*< public >*/
8277012d4b4SAndreas Färber 
82819b4a424SAvi Kivity     MemoryRegion iomem;
829343ec8e4SBenoit Canet     uint32_t lcd_brightness;
830343ec8e4SBenoit Canet     uint32_t out_state;
831343ec8e4SBenoit Canet     uint32_t in_state;
832708afdf3SJan Kiszka     uint32_t ier;
833708afdf3SJan Kiszka     uint32_t imr;
834343ec8e4SBenoit Canet     uint32_t isr;
835343ec8e4SBenoit Canet     qemu_irq irq;
836708afdf3SJan Kiszka     qemu_irq out[5]; /* 3 brightness out + 2 lcd (data and clock ) */
837db1015e9SEduardo Habkost };
838343ec8e4SBenoit Canet 
musicpal_gpio_brightness_update(musicpal_gpio_state * s)839343ec8e4SBenoit Canet static void musicpal_gpio_brightness_update(musicpal_gpio_state *s) {
840343ec8e4SBenoit Canet     int i;
841343ec8e4SBenoit Canet     uint32_t brightness;
842343ec8e4SBenoit Canet 
843343ec8e4SBenoit Canet     /* compute brightness ratio */
844343ec8e4SBenoit Canet     switch (s->lcd_brightness) {
845343ec8e4SBenoit Canet     case 0x00000007:
846343ec8e4SBenoit Canet         brightness = 0;
847343ec8e4SBenoit Canet         break;
848343ec8e4SBenoit Canet 
849343ec8e4SBenoit Canet     case 0x00020000:
850343ec8e4SBenoit Canet         brightness = 1;
851343ec8e4SBenoit Canet         break;
852343ec8e4SBenoit Canet 
853343ec8e4SBenoit Canet     case 0x00020001:
854343ec8e4SBenoit Canet         brightness = 2;
855343ec8e4SBenoit Canet         break;
856343ec8e4SBenoit Canet 
857343ec8e4SBenoit Canet     case 0x00040000:
858343ec8e4SBenoit Canet         brightness = 3;
859343ec8e4SBenoit Canet         break;
860343ec8e4SBenoit Canet 
861343ec8e4SBenoit Canet     case 0x00010006:
862343ec8e4SBenoit Canet         brightness = 4;
863343ec8e4SBenoit Canet         break;
864343ec8e4SBenoit Canet 
865343ec8e4SBenoit Canet     case 0x00020005:
866343ec8e4SBenoit Canet         brightness = 5;
867343ec8e4SBenoit Canet         break;
868343ec8e4SBenoit Canet 
869343ec8e4SBenoit Canet     case 0x00040003:
870343ec8e4SBenoit Canet         brightness = 6;
871343ec8e4SBenoit Canet         break;
872343ec8e4SBenoit Canet 
873343ec8e4SBenoit Canet     case 0x00030004:
874343ec8e4SBenoit Canet     default:
875343ec8e4SBenoit Canet         brightness = 7;
876343ec8e4SBenoit Canet     }
877343ec8e4SBenoit Canet 
878343ec8e4SBenoit Canet     /* set lcd brightness GPIOs  */
87949fedd0dSJan Kiszka     for (i = 0; i <= 2; i++) {
880343ec8e4SBenoit Canet         qemu_set_irq(s->out[i], (brightness >> i) & 1);
881343ec8e4SBenoit Canet     }
88249fedd0dSJan Kiszka }
883343ec8e4SBenoit Canet 
musicpal_gpio_pin_event(void * opaque,int pin,int level)884708afdf3SJan Kiszka static void musicpal_gpio_pin_event(void *opaque, int pin, int level)
885343ec8e4SBenoit Canet {
886243cd13cSJan Kiszka     musicpal_gpio_state *s = opaque;
887708afdf3SJan Kiszka     uint32_t mask = 1 << pin;
888708afdf3SJan Kiszka     uint32_t delta = level << pin;
889708afdf3SJan Kiszka     uint32_t old = s->in_state & mask;
890343ec8e4SBenoit Canet 
891708afdf3SJan Kiszka     s->in_state &= ~mask;
892708afdf3SJan Kiszka     s->in_state |= delta;
893708afdf3SJan Kiszka 
894708afdf3SJan Kiszka     if ((old ^ delta) &&
895708afdf3SJan Kiszka         ((level && (s->imr & mask)) || (!level && (s->ier & mask)))) {
896708afdf3SJan Kiszka         s->isr = mask;
897708afdf3SJan Kiszka         qemu_irq_raise(s->irq);
898d074769cSAndrzej Zaborowski     }
899343ec8e4SBenoit Canet }
900343ec8e4SBenoit Canet 
musicpal_gpio_read(void * opaque,hwaddr offset,unsigned size)901a8170e5eSAvi Kivity static uint64_t musicpal_gpio_read(void *opaque, hwaddr offset,
90219b4a424SAvi Kivity                                    unsigned size)
90324859b68Sbalrog {
904243cd13cSJan Kiszka     musicpal_gpio_state *s = opaque;
905343ec8e4SBenoit Canet 
90624859b68Sbalrog     switch (offset) {
90724859b68Sbalrog     case MP_GPIO_OE_HI: /* used for LCD brightness control */
908343ec8e4SBenoit Canet         return s->lcd_brightness & MP_OE_LCD_BRIGHTNESS;
90924859b68Sbalrog 
91024859b68Sbalrog     case MP_GPIO_OUT_LO:
911343ec8e4SBenoit Canet         return s->out_state & 0xFFFF;
91224859b68Sbalrog     case MP_GPIO_OUT_HI:
913343ec8e4SBenoit Canet         return s->out_state >> 16;
91424859b68Sbalrog 
91524859b68Sbalrog     case MP_GPIO_IN_LO:
916343ec8e4SBenoit Canet         return s->in_state & 0xFFFF;
91724859b68Sbalrog     case MP_GPIO_IN_HI:
918343ec8e4SBenoit Canet         return s->in_state >> 16;
91924859b68Sbalrog 
920708afdf3SJan Kiszka     case MP_GPIO_IER_LO:
921708afdf3SJan Kiszka         return s->ier & 0xFFFF;
922708afdf3SJan Kiszka     case MP_GPIO_IER_HI:
923708afdf3SJan Kiszka         return s->ier >> 16;
924708afdf3SJan Kiszka 
925708afdf3SJan Kiszka     case MP_GPIO_IMR_LO:
926708afdf3SJan Kiszka         return s->imr & 0xFFFF;
927708afdf3SJan Kiszka     case MP_GPIO_IMR_HI:
928708afdf3SJan Kiszka         return s->imr >> 16;
929708afdf3SJan Kiszka 
93024859b68Sbalrog     case MP_GPIO_ISR_LO:
931343ec8e4SBenoit Canet         return s->isr & 0xFFFF;
93224859b68Sbalrog     case MP_GPIO_ISR_HI:
933343ec8e4SBenoit Canet         return s->isr >> 16;
93424859b68Sbalrog 
93524859b68Sbalrog     default:
93624859b68Sbalrog         return 0;
93724859b68Sbalrog     }
93824859b68Sbalrog }
93924859b68Sbalrog 
musicpal_gpio_write(void * opaque,hwaddr offset,uint64_t value,unsigned size)940a8170e5eSAvi Kivity static void musicpal_gpio_write(void *opaque, hwaddr offset,
94119b4a424SAvi Kivity                                 uint64_t value, unsigned size)
94224859b68Sbalrog {
943243cd13cSJan Kiszka     musicpal_gpio_state *s = opaque;
94424859b68Sbalrog     switch (offset) {
94524859b68Sbalrog     case MP_GPIO_OE_HI: /* used for LCD brightness control */
946343ec8e4SBenoit Canet         s->lcd_brightness = (s->lcd_brightness & MP_GPIO_LCD_BRIGHTNESS) |
94724859b68Sbalrog                          (value & MP_OE_LCD_BRIGHTNESS);
948343ec8e4SBenoit Canet         musicpal_gpio_brightness_update(s);
94924859b68Sbalrog         break;
95024859b68Sbalrog 
95124859b68Sbalrog     case MP_GPIO_OUT_LO:
952343ec8e4SBenoit Canet         s->out_state = (s->out_state & 0xFFFF0000) | (value & 0xFFFF);
95324859b68Sbalrog         break;
95424859b68Sbalrog     case MP_GPIO_OUT_HI:
955343ec8e4SBenoit Canet         s->out_state = (s->out_state & 0xFFFF) | (value << 16);
956343ec8e4SBenoit Canet         s->lcd_brightness = (s->lcd_brightness & 0xFFFF) |
957343ec8e4SBenoit Canet                             (s->out_state & MP_GPIO_LCD_BRIGHTNESS);
958343ec8e4SBenoit Canet         musicpal_gpio_brightness_update(s);
959d074769cSAndrzej Zaborowski         qemu_set_irq(s->out[3], (s->out_state >> MP_GPIO_I2C_DATA_BIT) & 1);
960d074769cSAndrzej Zaborowski         qemu_set_irq(s->out[4], (s->out_state >> MP_GPIO_I2C_CLOCK_BIT) & 1);
96124859b68Sbalrog         break;
96224859b68Sbalrog 
963708afdf3SJan Kiszka     case MP_GPIO_IER_LO:
964708afdf3SJan Kiszka         s->ier = (s->ier & 0xFFFF0000) | (value & 0xFFFF);
965708afdf3SJan Kiszka         break;
966708afdf3SJan Kiszka     case MP_GPIO_IER_HI:
967708afdf3SJan Kiszka         s->ier = (s->ier & 0xFFFF) | (value << 16);
968708afdf3SJan Kiszka         break;
969708afdf3SJan Kiszka 
970708afdf3SJan Kiszka     case MP_GPIO_IMR_LO:
971708afdf3SJan Kiszka         s->imr = (s->imr & 0xFFFF0000) | (value & 0xFFFF);
972708afdf3SJan Kiszka         break;
973708afdf3SJan Kiszka     case MP_GPIO_IMR_HI:
974708afdf3SJan Kiszka         s->imr = (s->imr & 0xFFFF) | (value << 16);
975708afdf3SJan Kiszka         break;
97624859b68Sbalrog     }
97724859b68Sbalrog }
97824859b68Sbalrog 
97919b4a424SAvi Kivity static const MemoryRegionOps musicpal_gpio_ops = {
98019b4a424SAvi Kivity     .read = musicpal_gpio_read,
98119b4a424SAvi Kivity     .write = musicpal_gpio_write,
98219b4a424SAvi Kivity     .endianness = DEVICE_NATIVE_ENDIAN,
983718ec0beSmalc };
984718ec0beSmalc 
musicpal_gpio_reset(DeviceState * d)985d5b61dddSJan Kiszka static void musicpal_gpio_reset(DeviceState *d)
986718ec0beSmalc {
9877012d4b4SAndreas Färber     musicpal_gpio_state *s = MUSICPAL_GPIO(d);
98830624c92SJan Kiszka 
98930624c92SJan Kiszka     s->lcd_brightness = 0;
99030624c92SJan Kiszka     s->out_state = 0;
991343ec8e4SBenoit Canet     s->in_state = 0xffffffff;
992708afdf3SJan Kiszka     s->ier = 0;
993708afdf3SJan Kiszka     s->imr = 0;
994343ec8e4SBenoit Canet     s->isr = 0;
995343ec8e4SBenoit Canet }
996343ec8e4SBenoit Canet 
musicpal_gpio_init(Object * obj)997ece71994Sxiaoqiang zhao static void musicpal_gpio_init(Object *obj)
998343ec8e4SBenoit Canet {
999ece71994Sxiaoqiang zhao     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
10007012d4b4SAndreas Färber     DeviceState *dev = DEVICE(sbd);
10017012d4b4SAndreas Färber     musicpal_gpio_state *s = MUSICPAL_GPIO(dev);
1002718ec0beSmalc 
10037012d4b4SAndreas Färber     sysbus_init_irq(sbd, &s->irq);
1004343ec8e4SBenoit Canet 
1005ece71994Sxiaoqiang zhao     memory_region_init_io(&s->iomem, obj, &musicpal_gpio_ops, s,
100619b4a424SAvi Kivity                           "musicpal-gpio", MP_GPIO_SIZE);
10077012d4b4SAndreas Färber     sysbus_init_mmio(sbd, &s->iomem);
1008343ec8e4SBenoit Canet 
10097012d4b4SAndreas Färber     qdev_init_gpio_out(dev, s->out, ARRAY_SIZE(s->out));
1010708afdf3SJan Kiszka 
10117012d4b4SAndreas Färber     qdev_init_gpio_in(dev, musicpal_gpio_pin_event, 32);
1012718ec0beSmalc }
1013718ec0beSmalc 
1014d5b61dddSJan Kiszka static const VMStateDescription musicpal_gpio_vmsd = {
1015d5b61dddSJan Kiszka     .name = "musicpal_gpio",
1016d5b61dddSJan Kiszka     .version_id = 1,
1017d5b61dddSJan Kiszka     .minimum_version_id = 1,
1018607ef570SRichard Henderson     .fields = (const VMStateField[]) {
1019d5b61dddSJan Kiszka         VMSTATE_UINT32(lcd_brightness, musicpal_gpio_state),
1020d5b61dddSJan Kiszka         VMSTATE_UINT32(out_state, musicpal_gpio_state),
1021d5b61dddSJan Kiszka         VMSTATE_UINT32(in_state, musicpal_gpio_state),
1022d5b61dddSJan Kiszka         VMSTATE_UINT32(ier, musicpal_gpio_state),
1023d5b61dddSJan Kiszka         VMSTATE_UINT32(imr, musicpal_gpio_state),
1024d5b61dddSJan Kiszka         VMSTATE_UINT32(isr, musicpal_gpio_state),
1025d5b61dddSJan Kiszka         VMSTATE_END_OF_LIST()
1026d5b61dddSJan Kiszka     }
1027d5b61dddSJan Kiszka };
1028d5b61dddSJan Kiszka 
musicpal_gpio_class_init(ObjectClass * klass,const void * data)1029*12d1a768SPhilippe Mathieu-Daudé static void musicpal_gpio_class_init(ObjectClass *klass, const void *data)
1030999e12bbSAnthony Liguori {
103139bffca2SAnthony Liguori     DeviceClass *dc = DEVICE_CLASS(klass);
1032999e12bbSAnthony Liguori 
1033e3d08143SPeter Maydell     device_class_set_legacy_reset(dc, musicpal_gpio_reset);
103439bffca2SAnthony Liguori     dc->vmsd = &musicpal_gpio_vmsd;
1035999e12bbSAnthony Liguori }
1036999e12bbSAnthony Liguori 
10378c43a6f0SAndreas Färber static const TypeInfo musicpal_gpio_info = {
10387012d4b4SAndreas Färber     .name          = TYPE_MUSICPAL_GPIO,
103939bffca2SAnthony Liguori     .parent        = TYPE_SYS_BUS_DEVICE,
104039bffca2SAnthony Liguori     .instance_size = sizeof(musicpal_gpio_state),
1041ece71994Sxiaoqiang zhao     .instance_init = musicpal_gpio_init,
1042999e12bbSAnthony Liguori     .class_init    = musicpal_gpio_class_init,
104330624c92SJan Kiszka };
104430624c92SJan Kiszka 
104524859b68Sbalrog /* Keyboard codes & masks */
1046708afdf3SJan Kiszka #define MP_KEY_WHEEL_VOL       (1 << 0)
1047343ec8e4SBenoit Canet #define MP_KEY_WHEEL_VOL_INV   (1 << 1)
1048343ec8e4SBenoit Canet #define MP_KEY_WHEEL_NAV       (1 << 2)
1049343ec8e4SBenoit Canet #define MP_KEY_WHEEL_NAV_INV   (1 << 3)
1050343ec8e4SBenoit Canet #define MP_KEY_BTN_FAVORITS    (1 << 4)
1051343ec8e4SBenoit Canet #define MP_KEY_BTN_MENU        (1 << 5)
1052343ec8e4SBenoit Canet #define MP_KEY_BTN_VOLUME      (1 << 6)
1053343ec8e4SBenoit Canet #define MP_KEY_BTN_NAVIGATION  (1 << 7)
1054343ec8e4SBenoit Canet 
10553bdf5327SAndreas Färber #define TYPE_MUSICPAL_KEY "musicpal_key"
10568063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(musicpal_key_state, MUSICPAL_KEY)
10573bdf5327SAndreas Färber 
1058db1015e9SEduardo Habkost struct musicpal_key_state {
10593bdf5327SAndreas Färber     /*< private >*/
10603bdf5327SAndreas Färber     SysBusDevice parent_obj;
10613bdf5327SAndreas Färber     /*< public >*/
10623bdf5327SAndreas Färber 
1063708afdf3SJan Kiszka     uint32_t pressed_keys;
1064708afdf3SJan Kiszka     qemu_irq out[8];
1065db1015e9SEduardo Habkost };
1066343ec8e4SBenoit Canet 
musicpal_key_event(DeviceState * dev,QemuConsole * src,InputEvent * evt)1067ff7888dcSPeter Maydell static void musicpal_key_event(DeviceState *dev, QemuConsole *src,
1068ff7888dcSPeter Maydell                                InputEvent *evt)
106924859b68Sbalrog {
1070ff7888dcSPeter Maydell     musicpal_key_state *s = MUSICPAL_KEY(dev);
1071ff7888dcSPeter Maydell     InputKeyEvent *key = evt->u.key.data;
1072ff7888dcSPeter Maydell     int qcode = qemu_input_key_value_to_qcode(key->key);
107324859b68Sbalrog     uint32_t event = 0;
1074343ec8e4SBenoit Canet     int i;
107524859b68Sbalrog 
1076ff7888dcSPeter Maydell     switch (qcode) {
1077ff7888dcSPeter Maydell     case Q_KEY_CODE_UP:
1078343ec8e4SBenoit Canet         event = MP_KEY_WHEEL_NAV | MP_KEY_WHEEL_NAV_INV;
107924859b68Sbalrog         break;
108024859b68Sbalrog 
1081ff7888dcSPeter Maydell     case Q_KEY_CODE_DOWN:
1082343ec8e4SBenoit Canet         event = MP_KEY_WHEEL_NAV;
108324859b68Sbalrog         break;
108424859b68Sbalrog 
1085ff7888dcSPeter Maydell     case Q_KEY_CODE_LEFT:
1086343ec8e4SBenoit Canet         event = MP_KEY_WHEEL_VOL | MP_KEY_WHEEL_VOL_INV;
108724859b68Sbalrog         break;
108824859b68Sbalrog 
1089ff7888dcSPeter Maydell     case Q_KEY_CODE_RIGHT:
1090343ec8e4SBenoit Canet         event = MP_KEY_WHEEL_VOL;
109124859b68Sbalrog         break;
1092ff7888dcSPeter Maydell 
1093ff7888dcSPeter Maydell     case Q_KEY_CODE_F:
1094343ec8e4SBenoit Canet         event = MP_KEY_BTN_FAVORITS;
109524859b68Sbalrog         break;
109624859b68Sbalrog 
1097ff7888dcSPeter Maydell     case Q_KEY_CODE_TAB:
1098343ec8e4SBenoit Canet         event = MP_KEY_BTN_VOLUME;
109924859b68Sbalrog         break;
110024859b68Sbalrog 
1101ff7888dcSPeter Maydell     case Q_KEY_CODE_RET:
1102343ec8e4SBenoit Canet         event = MP_KEY_BTN_NAVIGATION;
110324859b68Sbalrog         break;
110424859b68Sbalrog 
1105ff7888dcSPeter Maydell     case Q_KEY_CODE_M:
1106343ec8e4SBenoit Canet         event = MP_KEY_BTN_MENU;
110724859b68Sbalrog         break;
110824859b68Sbalrog     }
1109ff7888dcSPeter Maydell 
1110ff7888dcSPeter Maydell     /*
1111ff7888dcSPeter Maydell      * We allow repeated wheel-events when the arrow keys are held down,
1112ff7888dcSPeter Maydell      * but do not repeat already-pressed buttons for the other key inputs.
1113ff7888dcSPeter Maydell      */
1114ff7888dcSPeter Maydell     if (!(event & (MP_KEY_WHEEL_NAV | MP_KEY_WHEEL_VOL))) {
1115ff7888dcSPeter Maydell         if (key->down && (s->pressed_keys & event)) {
11167c6ce4baSbalrog             event = 0;
11177c6ce4baSbalrog         }
1118708afdf3SJan Kiszka     }
111924859b68Sbalrog 
11207c6ce4baSbalrog     if (event) {
1121708afdf3SJan Kiszka         /* Raise GPIO pin first if repeating a key */
1122ff7888dcSPeter Maydell         if (key->down && (s->pressed_keys & event)) {
1123708afdf3SJan Kiszka             for (i = 0; i <= 7; i++) {
1124708afdf3SJan Kiszka                 if (event & (1 << i)) {
1125708afdf3SJan Kiszka                     qemu_set_irq(s->out[i], 1);
11267c6ce4baSbalrog                 }
1127708afdf3SJan Kiszka             }
1128708afdf3SJan Kiszka         }
1129708afdf3SJan Kiszka         for (i = 0; i <= 7; i++) {
1130708afdf3SJan Kiszka             if (event & (1 << i)) {
1131ff7888dcSPeter Maydell                 qemu_set_irq(s->out[i], !key->down);
1132708afdf3SJan Kiszka             }
1133708afdf3SJan Kiszka         }
1134ff7888dcSPeter Maydell         if (key->down) {
1135708afdf3SJan Kiszka             s->pressed_keys |= event;
1136ff7888dcSPeter Maydell         } else {
1137ff7888dcSPeter Maydell             s->pressed_keys &= ~event;
1138708afdf3SJan Kiszka         }
1139343ec8e4SBenoit Canet     }
1140343ec8e4SBenoit Canet }
1141343ec8e4SBenoit Canet 
musicpal_key_init(Object * obj)1142ece71994Sxiaoqiang zhao static void musicpal_key_init(Object *obj)
1143343ec8e4SBenoit Canet {
1144ece71994Sxiaoqiang zhao     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
11453bdf5327SAndreas Färber     DeviceState *dev = DEVICE(sbd);
11463bdf5327SAndreas Färber     musicpal_key_state *s = MUSICPAL_KEY(dev);
1147343ec8e4SBenoit Canet 
1148708afdf3SJan Kiszka     s->pressed_keys = 0;
1149343ec8e4SBenoit Canet 
11503bdf5327SAndreas Färber     qdev_init_gpio_out(dev, s->out, ARRAY_SIZE(s->out));
1151ff7888dcSPeter Maydell }
1152343ec8e4SBenoit Canet 
1153ff7888dcSPeter Maydell static const QemuInputHandler musicpal_key_handler = {
1154ff7888dcSPeter Maydell     .name = "musicpal_key",
1155ff7888dcSPeter Maydell     .mask = INPUT_EVENT_MASK_KEY,
1156ff7888dcSPeter Maydell     .event = musicpal_key_event,
1157ff7888dcSPeter Maydell };
1158ff7888dcSPeter Maydell 
musicpal_key_realize(DeviceState * dev,Error ** errp)1159ff7888dcSPeter Maydell static void musicpal_key_realize(DeviceState *dev, Error **errp)
1160ff7888dcSPeter Maydell {
1161ff7888dcSPeter Maydell     qemu_input_handler_register(dev, &musicpal_key_handler);
116224859b68Sbalrog }
116324859b68Sbalrog 
1164d5b61dddSJan Kiszka static const VMStateDescription musicpal_key_vmsd = {
1165d5b61dddSJan Kiszka     .name = "musicpal_key",
1166ff7888dcSPeter Maydell     .version_id = 2,
1167ff7888dcSPeter Maydell     .minimum_version_id = 2,
1168607ef570SRichard Henderson     .fields = (const VMStateField[]) {
1169d5b61dddSJan Kiszka         VMSTATE_UINT32(pressed_keys, musicpal_key_state),
1170d5b61dddSJan Kiszka         VMSTATE_END_OF_LIST()
1171d5b61dddSJan Kiszka     }
1172d5b61dddSJan Kiszka };
1173d5b61dddSJan Kiszka 
musicpal_key_class_init(ObjectClass * klass,const void * data)1174*12d1a768SPhilippe Mathieu-Daudé static void musicpal_key_class_init(ObjectClass *klass, const void *data)
1175999e12bbSAnthony Liguori {
117639bffca2SAnthony Liguori     DeviceClass *dc = DEVICE_CLASS(klass);
1177999e12bbSAnthony Liguori 
117839bffca2SAnthony Liguori     dc->vmsd = &musicpal_key_vmsd;
1179ff7888dcSPeter Maydell     dc->realize = musicpal_key_realize;
1180999e12bbSAnthony Liguori }
1181999e12bbSAnthony Liguori 
11828c43a6f0SAndreas Färber static const TypeInfo musicpal_key_info = {
11833bdf5327SAndreas Färber     .name          = TYPE_MUSICPAL_KEY,
118439bffca2SAnthony Liguori     .parent        = TYPE_SYS_BUS_DEVICE,
118539bffca2SAnthony Liguori     .instance_size = sizeof(musicpal_key_state),
1186ece71994Sxiaoqiang zhao     .instance_init = musicpal_key_init,
1187999e12bbSAnthony Liguori     .class_init    = musicpal_key_class_init,
1188d5b61dddSJan Kiszka };
1189d5b61dddSJan Kiszka 
1190e0ee6413SPhilippe Mathieu-Daudé #define FLASH_SECTOR_SIZE   (64 * KiB)
1191e0ee6413SPhilippe Mathieu-Daudé 
119224859b68Sbalrog static struct arm_boot_info musicpal_binfo = {
119324859b68Sbalrog     .loader_start = 0x0,
119424859b68Sbalrog     .board_id = 0x20e,
119524859b68Sbalrog };
119624859b68Sbalrog 
musicpal_init(MachineState * machine)11973ef96221SMarcel Apfelbaum static void musicpal_init(MachineState *machine)
119824859b68Sbalrog {
1199f25608e9SAndreas Färber     ARMCPU *cpu;
1200b47b50faSPaul Brook     DeviceState *dev;
120144cbf349SPhilippe Mathieu-Daudé     DeviceState *pic;
1202498661ddSPhilippe Mathieu-Daudé     DeviceState *uart_orgate;
1203d074769cSAndrzej Zaborowski     DeviceState *i2c_dev;
1204343ec8e4SBenoit Canet     DeviceState *lcd_dev;
1205343ec8e4SBenoit Canet     DeviceState *key_dev;
12061373b15bSPhilippe Mathieu-Daudé     I2CSlave *wm8750_dev;
1207d074769cSAndrzej Zaborowski     SysBusDevice *s;
1208a5c82852SAndreas Färber     I2CBus *i2c;
1209b47b50faSPaul Brook     int i;
121024859b68Sbalrog     unsigned long flash_size;
1211751c6a17SGerd Hoffmann     DriveInfo *dinfo;
12123ed61312SIgor Mammedov     MachineClass *mc = MACHINE_GET_CLASS(machine);
121319b4a424SAvi Kivity     MemoryRegion *address_space_mem = get_system_memory();
121419b4a424SAvi Kivity     MemoryRegion *sram = g_new(MemoryRegion, 1);
121524859b68Sbalrog 
12163ed61312SIgor Mammedov     /* For now we use a fixed - the original - RAM size */
12173ed61312SIgor Mammedov     if (machine->ram_size != mc->default_ram_size) {
12183ed61312SIgor Mammedov         char *sz = size_to_str(mc->default_ram_size);
12193ed61312SIgor Mammedov         error_report("Invalid RAM size, should be %s", sz);
12203ed61312SIgor Mammedov         g_free(sz);
12213ed61312SIgor Mammedov         exit(EXIT_FAILURE);
12223ed61312SIgor Mammedov     }
12233ed61312SIgor Mammedov 
1224ba1ba5ccSIgor Mammedov     cpu = ARM_CPU(cpu_create(machine->cpu_type));
122524859b68Sbalrog 
12263ed61312SIgor Mammedov     memory_region_add_subregion(address_space_mem, 0, machine->ram);
122724859b68Sbalrog 
122898a99ce0SPeter Maydell     memory_region_init_ram(sram, NULL, "musicpal.sram", MP_SRAM_SIZE,
1229f8ed85acSMarkus Armbruster                            &error_fatal);
123019b4a424SAvi Kivity     memory_region_add_subregion(address_space_mem, MP_SRAM_BASE, sram);
123124859b68Sbalrog 
123244cbf349SPhilippe Mathieu-Daudé     pic = sysbus_create_simple(TYPE_MV88W8618_PIC, MP_PIC_BASE,
1233fcef61ecSPeter Maydell                                qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ));
123444cbf349SPhilippe Mathieu-Daudé     sysbus_create_varargs(TYPE_MV88W8618_PIT, MP_PIT_BASE,
123544cbf349SPhilippe Mathieu-Daudé                           qdev_get_gpio_in(pic, MP_TIMER1_IRQ),
123644cbf349SPhilippe Mathieu-Daudé                           qdev_get_gpio_in(pic, MP_TIMER2_IRQ),
123744cbf349SPhilippe Mathieu-Daudé                           qdev_get_gpio_in(pic, MP_TIMER3_IRQ),
123844cbf349SPhilippe Mathieu-Daudé                           qdev_get_gpio_in(pic, MP_TIMER4_IRQ), NULL);
123924859b68Sbalrog 
1240498661ddSPhilippe Mathieu-Daudé     /* Logically OR both UART IRQs together */
1241901b78a0SPhilippe Mathieu-Daudé     uart_orgate = qdev_new(TYPE_OR_IRQ);
1242498661ddSPhilippe Mathieu-Daudé     object_property_set_int(OBJECT(uart_orgate), "num-lines", 2, &error_fatal);
1243498661ddSPhilippe Mathieu-Daudé     qdev_realize_and_unref(uart_orgate, NULL, &error_fatal);
12447d5b0d68SPhilippe Mathieu-Daudé     qdev_connect_gpio_out(uart_orgate, 0,
124544cbf349SPhilippe Mathieu-Daudé                           qdev_get_gpio_in(pic, MP_UART_SHARED_IRQ));
1246498661ddSPhilippe Mathieu-Daudé 
1247498661ddSPhilippe Mathieu-Daudé     serial_mm_init(address_space_mem, MP_UART1_BASE, 2,
1248498661ddSPhilippe Mathieu-Daudé                    qdev_get_gpio_in(uart_orgate, 0),
12499bca0edbSPeter Maydell                    1825000, serial_hd(0), DEVICE_NATIVE_ENDIAN);
1250498661ddSPhilippe Mathieu-Daudé     serial_mm_init(address_space_mem, MP_UART2_BASE, 2,
1251498661ddSPhilippe Mathieu-Daudé                    qdev_get_gpio_in(uart_orgate, 1),
12529bca0edbSPeter Maydell                    1825000, serial_hd(1), DEVICE_NATIVE_ENDIAN);
125324859b68Sbalrog 
125424859b68Sbalrog     /* Register flash */
1255751c6a17SGerd Hoffmann     dinfo = drive_get(IF_PFLASH, 0, 0);
1256751c6a17SGerd Hoffmann     if (dinfo) {
12574be74634SMarkus Armbruster         BlockBackend *blk = blk_by_legacy_dinfo(dinfo);
1258fa1d36dfSMarkus Armbruster 
12594be74634SMarkus Armbruster         flash_size = blk_getlength(blk);
1260e0ee6413SPhilippe Mathieu-Daudé         if (flash_size != 8 * MiB && flash_size != 16 * MiB &&
1261e0ee6413SPhilippe Mathieu-Daudé             flash_size != 32 * MiB) {
1262c0dbca36SAlistair Francis             error_report("Invalid flash image size");
126324859b68Sbalrog             exit(1);
126424859b68Sbalrog         }
126524859b68Sbalrog 
126624859b68Sbalrog         /*
126724859b68Sbalrog          * The original U-Boot accesses the flash at 0xFE000000 instead of
126824859b68Sbalrog          * 0xFF800000 (if there is 8 MB flash). So remap flash access if the
126924859b68Sbalrog          * image is smaller than 32 MB.
127024859b68Sbalrog          */
1271940d5b13SMarkus Armbruster         pflash_cfi02_register(0x100000000ULL - MP_FLASH_SIZE_MAX,
1272cfe5f011SAvi Kivity                               "musicpal.flash", flash_size,
1273e0ee6413SPhilippe Mathieu-Daudé                               blk, FLASH_SECTOR_SIZE,
12745f9fc5adSBlue Swirl                               MP_FLASH_SIZE_MAX / flash_size,
12755f9fc5adSBlue Swirl                               2, 0x00BF, 0x236D, 0x0000, 0x0000,
127601e0451aSAnthony Liguori                               0x5555, 0x2AAA, 0);
127724859b68Sbalrog     }
12785952b01cSAndreas Färber     sysbus_create_simple(TYPE_MV88W8618_FLASHCFG, MP_FLASHCFG_BASE, NULL);
127924859b68Sbalrog 
12803e80f690SMarkus Armbruster     dev = qdev_new(TYPE_MV88W8618_ETH);
1281e8c003c4SDavid Woodhouse     qemu_configure_nic_device(dev, true, "mv88w8618");
128279ed6fd6SPhilippe Mathieu-Daudé     object_property_set_link(OBJECT(dev), "dma-memory",
128379ed6fd6SPhilippe Mathieu-Daudé                              OBJECT(get_system_memory()), &error_fatal);
12843c6ef471SMarkus Armbruster     sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
12851356b98dSAndreas Färber     sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, MP_ETH_BASE);
128644cbf349SPhilippe Mathieu-Daudé     sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0,
128744cbf349SPhilippe Mathieu-Daudé                        qdev_get_gpio_in(pic, MP_ETH_IRQ));
128824859b68Sbalrog 
1289b47b50faSPaul Brook     sysbus_create_simple("mv88w8618_wlan", MP_WLAN_BASE, NULL);
1290718ec0beSmalc 
1291a86f200aSPeter Maydell     sysbus_create_simple(TYPE_MUSICPAL_MISC, MP_MISC_BASE, NULL);
1292343ec8e4SBenoit Canet 
12937012d4b4SAndreas Färber     dev = sysbus_create_simple(TYPE_MUSICPAL_GPIO, MP_GPIO_BASE,
129444cbf349SPhilippe Mathieu-Daudé                                qdev_get_gpio_in(pic, MP_GPIO_IRQ));
1295da8df26dSPhilippe Mathieu-Daudé     i2c_dev = sysbus_create_simple(TYPE_GPIO_I2C, -1, NULL);
1296a5c82852SAndreas Färber     i2c = (I2CBus *)qdev_get_child_bus(i2c_dev, "i2c");
1297d074769cSAndrzej Zaborowski 
12982cca58fdSAndreas Färber     lcd_dev = sysbus_create_simple(TYPE_MUSICPAL_LCD, MP_LCD_BASE, NULL);
12993bdf5327SAndreas Färber     key_dev = sysbus_create_simple(TYPE_MUSICPAL_KEY, -1, NULL);
1300343ec8e4SBenoit Canet 
1301d074769cSAndrzej Zaborowski     /* I2C read data */
1302708afdf3SJan Kiszka     qdev_connect_gpio_out(i2c_dev, 0,
1303708afdf3SJan Kiszka                           qdev_get_gpio_in(dev, MP_GPIO_I2C_DATA_BIT));
1304d074769cSAndrzej Zaborowski     /* I2C data */
1305d074769cSAndrzej Zaborowski     qdev_connect_gpio_out(dev, 3, qdev_get_gpio_in(i2c_dev, 0));
1306d074769cSAndrzej Zaborowski     /* I2C clock */
1307d074769cSAndrzej Zaborowski     qdev_connect_gpio_out(dev, 4, qdev_get_gpio_in(i2c_dev, 1));
1308d074769cSAndrzej Zaborowski 
130949fedd0dSJan Kiszka     for (i = 0; i < 3; i++) {
1310343ec8e4SBenoit Canet         qdev_connect_gpio_out(dev, i, qdev_get_gpio_in(lcd_dev, i));
131149fedd0dSJan Kiszka     }
1312708afdf3SJan Kiszka     for (i = 0; i < 4; i++) {
1313708afdf3SJan Kiszka         qdev_connect_gpio_out(key_dev, i, qdev_get_gpio_in(dev, i + 8));
1314708afdf3SJan Kiszka     }
1315708afdf3SJan Kiszka     for (i = 4; i < 8; i++) {
1316708afdf3SJan Kiszka         qdev_connect_gpio_out(key_dev, i, qdev_get_gpio_in(dev, i + 15));
1317708afdf3SJan Kiszka     }
131824859b68Sbalrog 
1319b8ab0303SMartin Kletzander     wm8750_dev = i2c_slave_new(TYPE_WM8750, MP_WM_ADDR);
1320b8ab0303SMartin Kletzander     if (machine->audiodev) {
1321b8ab0303SMartin Kletzander         qdev_prop_set_string(DEVICE(wm8750_dev), "audiodev", machine->audiodev);
1322b8ab0303SMartin Kletzander     }
1323b8ab0303SMartin Kletzander     i2c_slave_realize_and_unref(wm8750_dev, i2c, &error_abort);
1324b8ab0303SMartin Kletzander 
13253e80f690SMarkus Armbruster     dev = qdev_new(TYPE_MV88W8618_AUDIO);
13261356b98dSAndreas Färber     s = SYS_BUS_DEVICE(dev);
13275325cc34SMarkus Armbruster     object_property_set_link(OBJECT(dev), "wm8750", OBJECT(wm8750_dev),
13285325cc34SMarkus Armbruster                              NULL);
13293c6ef471SMarkus Armbruster     sysbus_realize_and_unref(s, &error_fatal);
1330d074769cSAndrzej Zaborowski     sysbus_mmio_map(s, 0, MP_AUDIO_BASE);
133144cbf349SPhilippe Mathieu-Daudé     sysbus_connect_irq(s, 0, qdev_get_gpio_in(pic, MP_AUDIO_IRQ));
1332d074769cSAndrzej Zaborowski 
133324859b68Sbalrog     musicpal_binfo.ram_size = MP_RAM_DEFAULT_SIZE;
13342744ece8STao Xu     arm_load_kernel(cpu, machine, &musicpal_binfo);
133524859b68Sbalrog }
133624859b68Sbalrog 
musicpal_machine_init(MachineClass * mc)1337e264d29dSEduardo Habkost static void musicpal_machine_init(MachineClass *mc)
1338f80f9ec9SAnthony Liguori {
1339e264d29dSEduardo Habkost     mc->desc = "Marvell 88w8618 / MusicPal (ARM926EJ-S)";
1340e264d29dSEduardo Habkost     mc->init = musicpal_init;
13414672cbd7SPeter Maydell     mc->ignore_memory_transaction_failures = true;
1342ba1ba5ccSIgor Mammedov     mc->default_cpu_type = ARM_CPU_TYPE_NAME("arm926");
13433ed61312SIgor Mammedov     mc->default_ram_size = MP_RAM_DEFAULT_SIZE;
13443ed61312SIgor Mammedov     mc->default_ram_id = "musicpal.ram";
1345b8ab0303SMartin Kletzander 
1346b8ab0303SMartin Kletzander     machine_add_audiodev_property(mc);
1347f80f9ec9SAnthony Liguori }
1348f80f9ec9SAnthony Liguori 
1349e264d29dSEduardo Habkost DEFINE_MACHINE("musicpal", musicpal_machine_init)
1350f80f9ec9SAnthony Liguori 
mv88w8618_wlan_class_init(ObjectClass * klass,const void * data)1351*12d1a768SPhilippe Mathieu-Daudé static void mv88w8618_wlan_class_init(ObjectClass *klass, const void *data)
1352999e12bbSAnthony Liguori {
13537f7420a0SMao Zhongyi     DeviceClass *dc = DEVICE_CLASS(klass);
1354999e12bbSAnthony Liguori 
13557f7420a0SMao Zhongyi     dc->realize = mv88w8618_wlan_realize;
1356999e12bbSAnthony Liguori }
1357999e12bbSAnthony Liguori 
13588c43a6f0SAndreas Färber static const TypeInfo mv88w8618_wlan_info = {
1359999e12bbSAnthony Liguori     .name          = "mv88w8618_wlan",
136039bffca2SAnthony Liguori     .parent        = TYPE_SYS_BUS_DEVICE,
136139bffca2SAnthony Liguori     .instance_size = sizeof(SysBusDevice),
1362999e12bbSAnthony Liguori     .class_init    = mv88w8618_wlan_class_init,
1363999e12bbSAnthony Liguori };
1364999e12bbSAnthony Liguori 
musicpal_register_types(void)136583f7d43aSAndreas Färber static void musicpal_register_types(void)
1366b47b50faSPaul Brook {
136739bffca2SAnthony Liguori     type_register_static(&mv88w8618_pic_info);
136839bffca2SAnthony Liguori     type_register_static(&mv88w8618_pit_info);
136939bffca2SAnthony Liguori     type_register_static(&mv88w8618_flashcfg_info);
137039bffca2SAnthony Liguori     type_register_static(&mv88w8618_wlan_info);
137139bffca2SAnthony Liguori     type_register_static(&musicpal_lcd_info);
137239bffca2SAnthony Liguori     type_register_static(&musicpal_gpio_info);
137339bffca2SAnthony Liguori     type_register_static(&musicpal_key_info);
1374a86f200aSPeter Maydell     type_register_static(&musicpal_misc_info);
1375b47b50faSPaul Brook }
1376b47b50faSPaul Brook 
137783f7d43aSAndreas Färber type_init(musicpal_register_types)
1378