xref: /qemu/hw/arm/musicpal.c (revision da8df26d2ea7eb1bfd6cc7fec37aabf6137f385d)
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"
29*da8df26dSPhilippe 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"
4024859b68Sbalrog 
41718ec0beSmalc #define MP_MISC_BASE            0x80002000
42718ec0beSmalc #define MP_MISC_SIZE            0x00001000
43718ec0beSmalc 
4424859b68Sbalrog #define MP_ETH_BASE             0x80008000
4524859b68Sbalrog 
46718ec0beSmalc #define MP_WLAN_BASE            0x8000C000
47718ec0beSmalc #define MP_WLAN_SIZE            0x00000800
48718ec0beSmalc 
4924859b68Sbalrog #define MP_UART1_BASE           0x8000C840
5024859b68Sbalrog #define MP_UART2_BASE           0x8000C940
5124859b68Sbalrog 
52718ec0beSmalc #define MP_GPIO_BASE            0x8000D000
53718ec0beSmalc #define MP_GPIO_SIZE            0x00001000
54718ec0beSmalc 
5524859b68Sbalrog #define MP_FLASHCFG_BASE        0x90006000
5624859b68Sbalrog #define MP_FLASHCFG_SIZE        0x00001000
5724859b68Sbalrog 
5824859b68Sbalrog #define MP_AUDIO_BASE           0x90007000
5924859b68Sbalrog 
6024859b68Sbalrog #define MP_PIC_BASE             0x90008000
6124859b68Sbalrog #define MP_PIC_SIZE             0x00001000
6224859b68Sbalrog 
6324859b68Sbalrog #define MP_PIT_BASE             0x90009000
6424859b68Sbalrog #define MP_PIT_SIZE             0x00001000
6524859b68Sbalrog 
6624859b68Sbalrog #define MP_LCD_BASE             0x9000c000
6724859b68Sbalrog #define MP_LCD_SIZE             0x00001000
6824859b68Sbalrog 
6924859b68Sbalrog #define MP_SRAM_BASE            0xC0000000
7024859b68Sbalrog #define MP_SRAM_SIZE            0x00020000
7124859b68Sbalrog 
7224859b68Sbalrog #define MP_RAM_DEFAULT_SIZE     32*1024*1024
7324859b68Sbalrog #define MP_FLASH_SIZE_MAX       32*1024*1024
7424859b68Sbalrog 
7524859b68Sbalrog #define MP_TIMER1_IRQ           4
76b47b50faSPaul Brook #define MP_TIMER2_IRQ           5
77b47b50faSPaul Brook #define MP_TIMER3_IRQ           6
7824859b68Sbalrog #define MP_TIMER4_IRQ           7
7924859b68Sbalrog #define MP_EHCI_IRQ             8
8024859b68Sbalrog #define MP_ETH_IRQ              9
81498661ddSPhilippe Mathieu-Daudé #define MP_UART_SHARED_IRQ      11
8224859b68Sbalrog #define MP_GPIO_IRQ             12
8324859b68Sbalrog #define MP_RTC_IRQ              28
8424859b68Sbalrog #define MP_AUDIO_IRQ            30
8524859b68Sbalrog 
8624859b68Sbalrog /* Wolfson 8750 I2C address */
8764258229SJan Kiszka #define MP_WM_ADDR              0x1A
8824859b68Sbalrog 
8924859b68Sbalrog /* LCD register offsets */
9024859b68Sbalrog #define MP_LCD_IRQCTRL          0x180
9124859b68Sbalrog #define MP_LCD_IRQSTAT          0x184
9224859b68Sbalrog #define MP_LCD_SPICTRL          0x1ac
9324859b68Sbalrog #define MP_LCD_INST             0x1bc
9424859b68Sbalrog #define MP_LCD_DATA             0x1c0
9524859b68Sbalrog 
9624859b68Sbalrog /* Mode magics */
9724859b68Sbalrog #define MP_LCD_SPI_DATA         0x00100011
9824859b68Sbalrog #define MP_LCD_SPI_CMD          0x00104011
9924859b68Sbalrog #define MP_LCD_SPI_INVALID      0x00000000
10024859b68Sbalrog 
10124859b68Sbalrog /* Commmands */
10224859b68Sbalrog #define MP_LCD_INST_SETPAGE0    0xB0
10324859b68Sbalrog /* ... */
10424859b68Sbalrog #define MP_LCD_INST_SETPAGE7    0xB7
10524859b68Sbalrog 
10624859b68Sbalrog #define MP_LCD_TEXTCOLOR        0xe0e0ff /* RRGGBB */
10724859b68Sbalrog 
1082cca58fdSAndreas Färber #define TYPE_MUSICPAL_LCD "musicpal_lcd"
1098063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(musicpal_lcd_state, MUSICPAL_LCD)
1102cca58fdSAndreas Färber 
111db1015e9SEduardo Habkost struct musicpal_lcd_state {
1122cca58fdSAndreas Färber     /*< private >*/
1132cca58fdSAndreas Färber     SysBusDevice parent_obj;
1142cca58fdSAndreas Färber     /*< public >*/
1152cca58fdSAndreas Färber 
11619b4a424SAvi Kivity     MemoryRegion iomem;
117343ec8e4SBenoit Canet     uint32_t brightness;
11824859b68Sbalrog     uint32_t mode;
11924859b68Sbalrog     uint32_t irqctrl;
120d5b61dddSJan Kiszka     uint32_t page;
121d5b61dddSJan Kiszka     uint32_t page_off;
122c78f7137SGerd Hoffmann     QemuConsole *con;
12324859b68Sbalrog     uint8_t video_ram[128*64/8];
124db1015e9SEduardo Habkost };
12524859b68Sbalrog 
126343ec8e4SBenoit Canet static uint8_t scale_lcd_color(musicpal_lcd_state *s, uint8_t col)
12724859b68Sbalrog {
128343ec8e4SBenoit Canet     switch (s->brightness) {
129343ec8e4SBenoit Canet     case 7:
13024859b68Sbalrog         return col;
131343ec8e4SBenoit Canet     case 0:
132343ec8e4SBenoit Canet         return 0;
133343ec8e4SBenoit Canet     default:
134343ec8e4SBenoit Canet         return (col * s->brightness) / 7;
13524859b68Sbalrog     }
13624859b68Sbalrog }
13724859b68Sbalrog 
1389aee50eeSPeter Maydell static inline void set_lcd_pixel32(musicpal_lcd_state *s,
1399aee50eeSPeter Maydell                                    int x, int y, uint32_t col)
1409aee50eeSPeter Maydell {
1419aee50eeSPeter Maydell     int dx, dy;
1429aee50eeSPeter Maydell     DisplaySurface *surface = qemu_console_surface(s->con);
1439aee50eeSPeter Maydell     uint32_t *pixel =
1449aee50eeSPeter Maydell         &((uint32_t *) surface_data(surface))[(y * 128 * 3 + x) * 3];
1459aee50eeSPeter Maydell 
1469aee50eeSPeter Maydell     for (dy = 0; dy < 3; dy++, pixel += 127 * 3) {
1479aee50eeSPeter Maydell         for (dx = 0; dx < 3; dx++, pixel++) {
1489aee50eeSPeter Maydell             *pixel = col;
1490266f2c7Sbalrog         }
1509aee50eeSPeter Maydell     }
1519aee50eeSPeter Maydell }
15224859b68Sbalrog 
15324859b68Sbalrog static void lcd_refresh(void *opaque)
15424859b68Sbalrog {
15524859b68Sbalrog     musicpal_lcd_state *s = opaque;
1560266f2c7Sbalrog     int x, y, col;
15724859b68Sbalrog 
1589aee50eeSPeter Maydell     col = rgb_to_pixel32(scale_lcd_color(s, (MP_LCD_TEXTCOLOR >> 16) & 0xff),
1599aee50eeSPeter Maydell                          scale_lcd_color(s, (MP_LCD_TEXTCOLOR >> 8) & 0xff),
1609aee50eeSPeter Maydell                          scale_lcd_color(s, MP_LCD_TEXTCOLOR & 0xff));
1619aee50eeSPeter Maydell     for (x = 0; x < 128; x++) {
1629aee50eeSPeter Maydell         for (y = 0; y < 64; y++) {
1639aee50eeSPeter Maydell             if (s->video_ram[x + (y / 8) * 128] & (1 << (y % 8))) {
1649aee50eeSPeter Maydell                 set_lcd_pixel32(s, x, y, col);
1659aee50eeSPeter Maydell             } else {
1669aee50eeSPeter Maydell                 set_lcd_pixel32(s, x, y, 0);
1679aee50eeSPeter Maydell             }
1689aee50eeSPeter Maydell         }
1690266f2c7Sbalrog     }
17024859b68Sbalrog 
171c78f7137SGerd Hoffmann     dpy_gfx_update(s->con, 0, 0, 128*3, 64*3);
17224859b68Sbalrog }
17324859b68Sbalrog 
174167bc3d2Sbalrog static void lcd_invalidate(void *opaque)
175167bc3d2Sbalrog {
176167bc3d2Sbalrog }
177167bc3d2Sbalrog 
1782c79fed3SStefan Weil static void musicpal_lcd_gpio_brightness_in(void *opaque, int irq, int level)
179343ec8e4SBenoit Canet {
180243cd13cSJan Kiszka     musicpal_lcd_state *s = opaque;
181343ec8e4SBenoit Canet     s->brightness &= ~(1 << irq);
182343ec8e4SBenoit Canet     s->brightness |= level << irq;
183343ec8e4SBenoit Canet }
184343ec8e4SBenoit Canet 
185a8170e5eSAvi Kivity static uint64_t musicpal_lcd_read(void *opaque, hwaddr offset,
18619b4a424SAvi Kivity                                   unsigned size)
18724859b68Sbalrog {
18824859b68Sbalrog     musicpal_lcd_state *s = opaque;
18924859b68Sbalrog 
19024859b68Sbalrog     switch (offset) {
19124859b68Sbalrog     case MP_LCD_IRQCTRL:
19224859b68Sbalrog         return s->irqctrl;
19324859b68Sbalrog 
19424859b68Sbalrog     default:
19524859b68Sbalrog         return 0;
19624859b68Sbalrog     }
19724859b68Sbalrog }
19824859b68Sbalrog 
199a8170e5eSAvi Kivity static void musicpal_lcd_write(void *opaque, hwaddr offset,
20019b4a424SAvi Kivity                                uint64_t value, unsigned size)
20124859b68Sbalrog {
20224859b68Sbalrog     musicpal_lcd_state *s = opaque;
20324859b68Sbalrog 
20424859b68Sbalrog     switch (offset) {
20524859b68Sbalrog     case MP_LCD_IRQCTRL:
20624859b68Sbalrog         s->irqctrl = value;
20724859b68Sbalrog         break;
20824859b68Sbalrog 
20924859b68Sbalrog     case MP_LCD_SPICTRL:
21049fedd0dSJan Kiszka         if (value == MP_LCD_SPI_DATA || value == MP_LCD_SPI_CMD) {
21124859b68Sbalrog             s->mode = value;
21249fedd0dSJan Kiszka         } else {
21324859b68Sbalrog             s->mode = MP_LCD_SPI_INVALID;
21449fedd0dSJan Kiszka         }
21524859b68Sbalrog         break;
21624859b68Sbalrog 
21724859b68Sbalrog     case MP_LCD_INST:
21824859b68Sbalrog         if (value >= MP_LCD_INST_SETPAGE0 && value <= MP_LCD_INST_SETPAGE7) {
21924859b68Sbalrog             s->page = value - MP_LCD_INST_SETPAGE0;
22024859b68Sbalrog             s->page_off = 0;
22124859b68Sbalrog         }
22224859b68Sbalrog         break;
22324859b68Sbalrog 
22424859b68Sbalrog     case MP_LCD_DATA:
22524859b68Sbalrog         if (s->mode == MP_LCD_SPI_CMD) {
22624859b68Sbalrog             if (value >= MP_LCD_INST_SETPAGE0 &&
22724859b68Sbalrog                 value <= MP_LCD_INST_SETPAGE7) {
22824859b68Sbalrog                 s->page = value - MP_LCD_INST_SETPAGE0;
22924859b68Sbalrog                 s->page_off = 0;
23024859b68Sbalrog             }
23124859b68Sbalrog         } else if (s->mode == MP_LCD_SPI_DATA) {
23224859b68Sbalrog             s->video_ram[s->page*128 + s->page_off] = value;
23324859b68Sbalrog             s->page_off = (s->page_off + 1) & 127;
23424859b68Sbalrog         }
23524859b68Sbalrog         break;
23624859b68Sbalrog     }
23724859b68Sbalrog }
23824859b68Sbalrog 
23919b4a424SAvi Kivity static const MemoryRegionOps musicpal_lcd_ops = {
24019b4a424SAvi Kivity     .read = musicpal_lcd_read,
24119b4a424SAvi Kivity     .write = musicpal_lcd_write,
24219b4a424SAvi Kivity     .endianness = DEVICE_NATIVE_ENDIAN,
24324859b68Sbalrog };
24424859b68Sbalrog 
245380cd056SGerd Hoffmann static const GraphicHwOps musicpal_gfx_ops = {
246380cd056SGerd Hoffmann     .invalidate  = lcd_invalidate,
247380cd056SGerd Hoffmann     .gfx_update  = lcd_refresh,
248380cd056SGerd Hoffmann };
249380cd056SGerd Hoffmann 
250ece71994Sxiaoqiang zhao static void musicpal_lcd_realize(DeviceState *dev, Error **errp)
25124859b68Sbalrog {
252ece71994Sxiaoqiang zhao     musicpal_lcd_state *s = MUSICPAL_LCD(dev);
253ece71994Sxiaoqiang zhao     s->con = graphic_console_init(dev, 0, &musicpal_gfx_ops, s);
254ece71994Sxiaoqiang zhao     qemu_console_resize(s->con, 128 * 3, 64 * 3);
255ece71994Sxiaoqiang zhao }
256ece71994Sxiaoqiang zhao 
257ece71994Sxiaoqiang zhao static void musicpal_lcd_init(Object *obj)
258ece71994Sxiaoqiang zhao {
259ece71994Sxiaoqiang zhao     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
2602cca58fdSAndreas Färber     DeviceState *dev = DEVICE(sbd);
2612cca58fdSAndreas Färber     musicpal_lcd_state *s = MUSICPAL_LCD(dev);
26224859b68Sbalrog 
263343ec8e4SBenoit Canet     s->brightness = 7;
264343ec8e4SBenoit Canet 
265ece71994Sxiaoqiang zhao     memory_region_init_io(&s->iomem, obj, &musicpal_lcd_ops, s,
26619b4a424SAvi Kivity                           "musicpal-lcd", MP_LCD_SIZE);
2672cca58fdSAndreas Färber     sysbus_init_mmio(sbd, &s->iomem);
26824859b68Sbalrog 
2692cca58fdSAndreas Färber     qdev_init_gpio_in(dev, musicpal_lcd_gpio_brightness_in, 3);
27024859b68Sbalrog }
27124859b68Sbalrog 
272d5b61dddSJan Kiszka static const VMStateDescription musicpal_lcd_vmsd = {
273d5b61dddSJan Kiszka     .name = "musicpal_lcd",
274d5b61dddSJan Kiszka     .version_id = 1,
275d5b61dddSJan Kiszka     .minimum_version_id = 1,
276d5b61dddSJan Kiszka     .fields = (VMStateField[]) {
277d5b61dddSJan Kiszka         VMSTATE_UINT32(brightness, musicpal_lcd_state),
278d5b61dddSJan Kiszka         VMSTATE_UINT32(mode, musicpal_lcd_state),
279d5b61dddSJan Kiszka         VMSTATE_UINT32(irqctrl, musicpal_lcd_state),
280d5b61dddSJan Kiszka         VMSTATE_UINT32(page, musicpal_lcd_state),
281d5b61dddSJan Kiszka         VMSTATE_UINT32(page_off, musicpal_lcd_state),
282d5b61dddSJan Kiszka         VMSTATE_BUFFER(video_ram, musicpal_lcd_state),
283d5b61dddSJan Kiszka         VMSTATE_END_OF_LIST()
284d5b61dddSJan Kiszka     }
285d5b61dddSJan Kiszka };
286d5b61dddSJan Kiszka 
287999e12bbSAnthony Liguori static void musicpal_lcd_class_init(ObjectClass *klass, void *data)
288999e12bbSAnthony Liguori {
28939bffca2SAnthony Liguori     DeviceClass *dc = DEVICE_CLASS(klass);
290999e12bbSAnthony Liguori 
29139bffca2SAnthony Liguori     dc->vmsd = &musicpal_lcd_vmsd;
292ece71994Sxiaoqiang zhao     dc->realize = musicpal_lcd_realize;
293999e12bbSAnthony Liguori }
294999e12bbSAnthony Liguori 
2958c43a6f0SAndreas Färber static const TypeInfo musicpal_lcd_info = {
2962cca58fdSAndreas Färber     .name          = TYPE_MUSICPAL_LCD,
29739bffca2SAnthony Liguori     .parent        = TYPE_SYS_BUS_DEVICE,
29839bffca2SAnthony Liguori     .instance_size = sizeof(musicpal_lcd_state),
299ece71994Sxiaoqiang zhao     .instance_init = musicpal_lcd_init,
300999e12bbSAnthony Liguori     .class_init    = musicpal_lcd_class_init,
301d5b61dddSJan Kiszka };
302d5b61dddSJan Kiszka 
30324859b68Sbalrog /* PIC register offsets */
30424859b68Sbalrog #define MP_PIC_STATUS           0x00
30524859b68Sbalrog #define MP_PIC_ENABLE_SET       0x08
30624859b68Sbalrog #define MP_PIC_ENABLE_CLR       0x0C
30724859b68Sbalrog 
308c7bd0fd9SAndreas Färber #define TYPE_MV88W8618_PIC "mv88w8618_pic"
3098063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(mv88w8618_pic_state, MV88W8618_PIC)
310c7bd0fd9SAndreas Färber 
311db1015e9SEduardo Habkost struct mv88w8618_pic_state {
312c7bd0fd9SAndreas Färber     /*< private >*/
313c7bd0fd9SAndreas Färber     SysBusDevice parent_obj;
314c7bd0fd9SAndreas Färber     /*< public >*/
315c7bd0fd9SAndreas Färber 
31619b4a424SAvi Kivity     MemoryRegion iomem;
31724859b68Sbalrog     uint32_t level;
31824859b68Sbalrog     uint32_t enabled;
31924859b68Sbalrog     qemu_irq parent_irq;
320db1015e9SEduardo Habkost };
32124859b68Sbalrog 
32224859b68Sbalrog static void mv88w8618_pic_update(mv88w8618_pic_state *s)
32324859b68Sbalrog {
32424859b68Sbalrog     qemu_set_irq(s->parent_irq, (s->level & s->enabled));
32524859b68Sbalrog }
32624859b68Sbalrog 
32724859b68Sbalrog static void mv88w8618_pic_set_irq(void *opaque, int irq, int level)
32824859b68Sbalrog {
32924859b68Sbalrog     mv88w8618_pic_state *s = opaque;
33024859b68Sbalrog 
33149fedd0dSJan Kiszka     if (level) {
33224859b68Sbalrog         s->level |= 1 << irq;
33349fedd0dSJan Kiszka     } else {
33424859b68Sbalrog         s->level &= ~(1 << irq);
33549fedd0dSJan Kiszka     }
33624859b68Sbalrog     mv88w8618_pic_update(s);
33724859b68Sbalrog }
33824859b68Sbalrog 
339a8170e5eSAvi Kivity static uint64_t mv88w8618_pic_read(void *opaque, hwaddr offset,
34019b4a424SAvi Kivity                                    unsigned size)
34124859b68Sbalrog {
34224859b68Sbalrog     mv88w8618_pic_state *s = opaque;
34324859b68Sbalrog 
34424859b68Sbalrog     switch (offset) {
34524859b68Sbalrog     case MP_PIC_STATUS:
34624859b68Sbalrog         return s->level & s->enabled;
34724859b68Sbalrog 
34824859b68Sbalrog     default:
34924859b68Sbalrog         return 0;
35024859b68Sbalrog     }
35124859b68Sbalrog }
35224859b68Sbalrog 
353a8170e5eSAvi Kivity static void mv88w8618_pic_write(void *opaque, hwaddr offset,
35419b4a424SAvi Kivity                                 uint64_t value, unsigned size)
35524859b68Sbalrog {
35624859b68Sbalrog     mv88w8618_pic_state *s = opaque;
35724859b68Sbalrog 
35824859b68Sbalrog     switch (offset) {
35924859b68Sbalrog     case MP_PIC_ENABLE_SET:
36024859b68Sbalrog         s->enabled |= value;
36124859b68Sbalrog         break;
36224859b68Sbalrog 
36324859b68Sbalrog     case MP_PIC_ENABLE_CLR:
36424859b68Sbalrog         s->enabled &= ~value;
36524859b68Sbalrog         s->level &= ~value;
36624859b68Sbalrog         break;
36724859b68Sbalrog     }
36824859b68Sbalrog     mv88w8618_pic_update(s);
36924859b68Sbalrog }
37024859b68Sbalrog 
371d5b61dddSJan Kiszka static void mv88w8618_pic_reset(DeviceState *d)
37224859b68Sbalrog {
373c7bd0fd9SAndreas Färber     mv88w8618_pic_state *s = MV88W8618_PIC(d);
37424859b68Sbalrog 
37524859b68Sbalrog     s->level = 0;
37624859b68Sbalrog     s->enabled = 0;
37724859b68Sbalrog }
37824859b68Sbalrog 
37919b4a424SAvi Kivity static const MemoryRegionOps mv88w8618_pic_ops = {
38019b4a424SAvi Kivity     .read = mv88w8618_pic_read,
38119b4a424SAvi Kivity     .write = mv88w8618_pic_write,
38219b4a424SAvi Kivity     .endianness = DEVICE_NATIVE_ENDIAN,
38324859b68Sbalrog };
38424859b68Sbalrog 
385ece71994Sxiaoqiang zhao static void mv88w8618_pic_init(Object *obj)
38624859b68Sbalrog {
387ece71994Sxiaoqiang zhao     SysBusDevice *dev = SYS_BUS_DEVICE(obj);
388c7bd0fd9SAndreas Färber     mv88w8618_pic_state *s = MV88W8618_PIC(dev);
38924859b68Sbalrog 
390c7bd0fd9SAndreas Färber     qdev_init_gpio_in(DEVICE(dev), mv88w8618_pic_set_irq, 32);
391b47b50faSPaul Brook     sysbus_init_irq(dev, &s->parent_irq);
392ece71994Sxiaoqiang zhao     memory_region_init_io(&s->iomem, obj, &mv88w8618_pic_ops, s,
39319b4a424SAvi Kivity                           "musicpal-pic", MP_PIC_SIZE);
394750ecd44SAvi Kivity     sysbus_init_mmio(dev, &s->iomem);
39524859b68Sbalrog }
39624859b68Sbalrog 
397d5b61dddSJan Kiszka static const VMStateDescription mv88w8618_pic_vmsd = {
398d5b61dddSJan Kiszka     .name = "mv88w8618_pic",
399d5b61dddSJan Kiszka     .version_id = 1,
400d5b61dddSJan Kiszka     .minimum_version_id = 1,
401d5b61dddSJan Kiszka     .fields = (VMStateField[]) {
402d5b61dddSJan Kiszka         VMSTATE_UINT32(level, mv88w8618_pic_state),
403d5b61dddSJan Kiszka         VMSTATE_UINT32(enabled, mv88w8618_pic_state),
404d5b61dddSJan Kiszka         VMSTATE_END_OF_LIST()
405d5b61dddSJan Kiszka     }
406d5b61dddSJan Kiszka };
407d5b61dddSJan Kiszka 
408999e12bbSAnthony Liguori static void mv88w8618_pic_class_init(ObjectClass *klass, void *data)
409999e12bbSAnthony Liguori {
41039bffca2SAnthony Liguori     DeviceClass *dc = DEVICE_CLASS(klass);
411999e12bbSAnthony Liguori 
41239bffca2SAnthony Liguori     dc->reset = mv88w8618_pic_reset;
41339bffca2SAnthony Liguori     dc->vmsd = &mv88w8618_pic_vmsd;
414999e12bbSAnthony Liguori }
415999e12bbSAnthony Liguori 
4168c43a6f0SAndreas Färber static const TypeInfo mv88w8618_pic_info = {
417c7bd0fd9SAndreas Färber     .name          = TYPE_MV88W8618_PIC,
41839bffca2SAnthony Liguori     .parent        = TYPE_SYS_BUS_DEVICE,
41939bffca2SAnthony Liguori     .instance_size = sizeof(mv88w8618_pic_state),
420ece71994Sxiaoqiang zhao     .instance_init = mv88w8618_pic_init,
421999e12bbSAnthony Liguori     .class_init    = mv88w8618_pic_class_init,
422d5b61dddSJan Kiszka };
423d5b61dddSJan Kiszka 
42424859b68Sbalrog /* PIT register offsets */
42524859b68Sbalrog #define MP_PIT_TIMER1_LENGTH    0x00
42624859b68Sbalrog /* ... */
42724859b68Sbalrog #define MP_PIT_TIMER4_LENGTH    0x0C
42824859b68Sbalrog #define MP_PIT_CONTROL          0x10
42924859b68Sbalrog #define MP_PIT_TIMER1_VALUE     0x14
43024859b68Sbalrog /* ... */
43124859b68Sbalrog #define MP_PIT_TIMER4_VALUE     0x20
43224859b68Sbalrog #define MP_BOARD_RESET          0x34
43324859b68Sbalrog 
43424859b68Sbalrog /* Magic board reset value (probably some watchdog behind it) */
43524859b68Sbalrog #define MP_BOARD_RESET_MAGIC    0x10000
43624859b68Sbalrog 
43724859b68Sbalrog typedef struct mv88w8618_timer_state {
438b47b50faSPaul Brook     ptimer_state *ptimer;
43924859b68Sbalrog     uint32_t limit;
44024859b68Sbalrog     int freq;
44124859b68Sbalrog     qemu_irq irq;
44224859b68Sbalrog } mv88w8618_timer_state;
44324859b68Sbalrog 
4444adc8541SAndreas Färber #define TYPE_MV88W8618_PIT "mv88w8618_pit"
4458063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(mv88w8618_pit_state, MV88W8618_PIT)
4464adc8541SAndreas Färber 
447db1015e9SEduardo Habkost struct mv88w8618_pit_state {
4484adc8541SAndreas Färber     /*< private >*/
4494adc8541SAndreas Färber     SysBusDevice parent_obj;
4504adc8541SAndreas Färber     /*< public >*/
4514adc8541SAndreas Färber 
45219b4a424SAvi Kivity     MemoryRegion iomem;
453b47b50faSPaul Brook     mv88w8618_timer_state timer[4];
454db1015e9SEduardo Habkost };
45524859b68Sbalrog 
45624859b68Sbalrog static void mv88w8618_timer_tick(void *opaque)
45724859b68Sbalrog {
45824859b68Sbalrog     mv88w8618_timer_state *s = opaque;
45924859b68Sbalrog 
46024859b68Sbalrog     qemu_irq_raise(s->irq);
46124859b68Sbalrog }
46224859b68Sbalrog 
463b47b50faSPaul Brook static void mv88w8618_timer_init(SysBusDevice *dev, mv88w8618_timer_state *s,
464b47b50faSPaul Brook                                  uint32_t freq)
46524859b68Sbalrog {
466b47b50faSPaul Brook     sysbus_init_irq(dev, &s->irq);
46724859b68Sbalrog     s->freq = freq;
46824859b68Sbalrog 
4699598c1bbSPeter Maydell     s->ptimer = ptimer_init(mv88w8618_timer_tick, s, PTIMER_POLICY_LEGACY);
47024859b68Sbalrog }
47124859b68Sbalrog 
472a8170e5eSAvi Kivity static uint64_t mv88w8618_pit_read(void *opaque, hwaddr offset,
47319b4a424SAvi Kivity                                    unsigned size)
47424859b68Sbalrog {
47524859b68Sbalrog     mv88w8618_pit_state *s = opaque;
47624859b68Sbalrog     mv88w8618_timer_state *t;
47724859b68Sbalrog 
47824859b68Sbalrog     switch (offset) {
47924859b68Sbalrog     case MP_PIT_TIMER1_VALUE ... MP_PIT_TIMER4_VALUE:
480b47b50faSPaul Brook         t = &s->timer[(offset-MP_PIT_TIMER1_VALUE) >> 2];
481b47b50faSPaul Brook         return ptimer_get_count(t->ptimer);
48224859b68Sbalrog 
48324859b68Sbalrog     default:
48424859b68Sbalrog         return 0;
48524859b68Sbalrog     }
48624859b68Sbalrog }
48724859b68Sbalrog 
488a8170e5eSAvi Kivity static void mv88w8618_pit_write(void *opaque, hwaddr offset,
48919b4a424SAvi Kivity                                 uint64_t value, unsigned size)
49024859b68Sbalrog {
49124859b68Sbalrog     mv88w8618_pit_state *s = opaque;
49224859b68Sbalrog     mv88w8618_timer_state *t;
49324859b68Sbalrog     int i;
49424859b68Sbalrog 
49524859b68Sbalrog     switch (offset) {
49624859b68Sbalrog     case MP_PIT_TIMER1_LENGTH ... MP_PIT_TIMER4_LENGTH:
497b47b50faSPaul Brook         t = &s->timer[offset >> 2];
49824859b68Sbalrog         t->limit = value;
499d8052a2eSPeter Maydell         ptimer_transaction_begin(t->ptimer);
500c88d6bdeSJan Kiszka         if (t->limit > 0) {
501b47b50faSPaul Brook             ptimer_set_limit(t->ptimer, t->limit, 1);
502c88d6bdeSJan Kiszka         } else {
503c88d6bdeSJan Kiszka             ptimer_stop(t->ptimer);
504c88d6bdeSJan Kiszka         }
505d8052a2eSPeter Maydell         ptimer_transaction_commit(t->ptimer);
50624859b68Sbalrog         break;
50724859b68Sbalrog 
50824859b68Sbalrog     case MP_PIT_CONTROL:
50924859b68Sbalrog         for (i = 0; i < 4; i++) {
510b47b50faSPaul Brook             t = &s->timer[i];
511d8052a2eSPeter Maydell             ptimer_transaction_begin(t->ptimer);
512c88d6bdeSJan Kiszka             if (value & 0xf && t->limit > 0) {
513b47b50faSPaul Brook                 ptimer_set_limit(t->ptimer, t->limit, 0);
514b47b50faSPaul Brook                 ptimer_set_freq(t->ptimer, t->freq);
515b47b50faSPaul Brook                 ptimer_run(t->ptimer, 0);
516c88d6bdeSJan Kiszka             } else {
517c88d6bdeSJan Kiszka                 ptimer_stop(t->ptimer);
51824859b68Sbalrog             }
519d8052a2eSPeter Maydell             ptimer_transaction_commit(t->ptimer);
52024859b68Sbalrog             value >>= 4;
52124859b68Sbalrog         }
52224859b68Sbalrog         break;
52324859b68Sbalrog 
52424859b68Sbalrog     case MP_BOARD_RESET:
52549fedd0dSJan Kiszka         if (value == MP_BOARD_RESET_MAGIC) {
526cf83f140SEric Blake             qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
52749fedd0dSJan Kiszka         }
52824859b68Sbalrog         break;
52924859b68Sbalrog     }
53024859b68Sbalrog }
53124859b68Sbalrog 
532d5b61dddSJan Kiszka static void mv88w8618_pit_reset(DeviceState *d)
533c88d6bdeSJan Kiszka {
5344adc8541SAndreas Färber     mv88w8618_pit_state *s = MV88W8618_PIT(d);
535c88d6bdeSJan Kiszka     int i;
536c88d6bdeSJan Kiszka 
537c88d6bdeSJan Kiszka     for (i = 0; i < 4; i++) {
538d8052a2eSPeter Maydell         mv88w8618_timer_state *t = &s->timer[i];
539d8052a2eSPeter Maydell         ptimer_transaction_begin(t->ptimer);
540d8052a2eSPeter Maydell         ptimer_stop(t->ptimer);
541d8052a2eSPeter Maydell         ptimer_transaction_commit(t->ptimer);
542d8052a2eSPeter Maydell         t->limit = 0;
543c88d6bdeSJan Kiszka     }
544c88d6bdeSJan Kiszka }
545c88d6bdeSJan Kiszka 
54619b4a424SAvi Kivity static const MemoryRegionOps mv88w8618_pit_ops = {
54719b4a424SAvi Kivity     .read = mv88w8618_pit_read,
54819b4a424SAvi Kivity     .write = mv88w8618_pit_write,
54919b4a424SAvi Kivity     .endianness = DEVICE_NATIVE_ENDIAN,
55024859b68Sbalrog };
55124859b68Sbalrog 
552ece71994Sxiaoqiang zhao static void mv88w8618_pit_init(Object *obj)
55324859b68Sbalrog {
554ece71994Sxiaoqiang zhao     SysBusDevice *dev = SYS_BUS_DEVICE(obj);
5554adc8541SAndreas Färber     mv88w8618_pit_state *s = MV88W8618_PIT(dev);
556b47b50faSPaul Brook     int i;
55724859b68Sbalrog 
55824859b68Sbalrog     /* Letting them all run at 1 MHz is likely just a pragmatic
55924859b68Sbalrog      * simplification. */
560b47b50faSPaul Brook     for (i = 0; i < 4; i++) {
561b47b50faSPaul Brook         mv88w8618_timer_init(dev, &s->timer[i], 1000000);
562b47b50faSPaul Brook     }
56324859b68Sbalrog 
564ece71994Sxiaoqiang zhao     memory_region_init_io(&s->iomem, obj, &mv88w8618_pit_ops, s,
56519b4a424SAvi Kivity                           "musicpal-pit", MP_PIT_SIZE);
566750ecd44SAvi Kivity     sysbus_init_mmio(dev, &s->iomem);
56724859b68Sbalrog }
56824859b68Sbalrog 
569a4bc0334SGan Qixin static void mv88w8618_pit_finalize(Object *obj)
570a4bc0334SGan Qixin {
571a4bc0334SGan Qixin     SysBusDevice *dev = SYS_BUS_DEVICE(obj);
572a4bc0334SGan Qixin     mv88w8618_pit_state *s = MV88W8618_PIT(dev);
573a4bc0334SGan Qixin     int i;
574a4bc0334SGan Qixin 
575a4bc0334SGan Qixin     for (i = 0; i < 4; i++) {
576a4bc0334SGan Qixin         ptimer_free(s->timer[i].ptimer);
577a4bc0334SGan Qixin     }
578a4bc0334SGan Qixin }
579a4bc0334SGan Qixin 
580d5b61dddSJan Kiszka static const VMStateDescription mv88w8618_timer_vmsd = {
581d5b61dddSJan Kiszka     .name = "timer",
582d5b61dddSJan Kiszka     .version_id = 1,
583d5b61dddSJan Kiszka     .minimum_version_id = 1,
584d5b61dddSJan Kiszka     .fields = (VMStateField[]) {
585d5b61dddSJan Kiszka         VMSTATE_PTIMER(ptimer, mv88w8618_timer_state),
586d5b61dddSJan Kiszka         VMSTATE_UINT32(limit, mv88w8618_timer_state),
587d5b61dddSJan Kiszka         VMSTATE_END_OF_LIST()
588d5b61dddSJan Kiszka     }
589d5b61dddSJan Kiszka };
590d5b61dddSJan Kiszka 
591d5b61dddSJan Kiszka static const VMStateDescription mv88w8618_pit_vmsd = {
592d5b61dddSJan Kiszka     .name = "mv88w8618_pit",
593d5b61dddSJan Kiszka     .version_id = 1,
594d5b61dddSJan Kiszka     .minimum_version_id = 1,
595d5b61dddSJan Kiszka     .fields = (VMStateField[]) {
596d5b61dddSJan Kiszka         VMSTATE_STRUCT_ARRAY(timer, mv88w8618_pit_state, 4, 1,
597d5b61dddSJan Kiszka                              mv88w8618_timer_vmsd, mv88w8618_timer_state),
598d5b61dddSJan Kiszka         VMSTATE_END_OF_LIST()
599d5b61dddSJan Kiszka     }
600d5b61dddSJan Kiszka };
601d5b61dddSJan Kiszka 
602999e12bbSAnthony Liguori static void mv88w8618_pit_class_init(ObjectClass *klass, void *data)
603999e12bbSAnthony Liguori {
60439bffca2SAnthony Liguori     DeviceClass *dc = DEVICE_CLASS(klass);
605999e12bbSAnthony Liguori 
60639bffca2SAnthony Liguori     dc->reset = mv88w8618_pit_reset;
60739bffca2SAnthony Liguori     dc->vmsd = &mv88w8618_pit_vmsd;
608999e12bbSAnthony Liguori }
609999e12bbSAnthony Liguori 
6108c43a6f0SAndreas Färber static const TypeInfo mv88w8618_pit_info = {
6114adc8541SAndreas Färber     .name          = TYPE_MV88W8618_PIT,
61239bffca2SAnthony Liguori     .parent        = TYPE_SYS_BUS_DEVICE,
61339bffca2SAnthony Liguori     .instance_size = sizeof(mv88w8618_pit_state),
614ece71994Sxiaoqiang zhao     .instance_init = mv88w8618_pit_init,
615a4bc0334SGan Qixin     .instance_finalize = mv88w8618_pit_finalize,
616999e12bbSAnthony Liguori     .class_init    = mv88w8618_pit_class_init,
617c88d6bdeSJan Kiszka };
618c88d6bdeSJan Kiszka 
61924859b68Sbalrog /* Flash config register offsets */
62024859b68Sbalrog #define MP_FLASHCFG_CFGR0    0x04
62124859b68Sbalrog 
6225952b01cSAndreas Färber #define TYPE_MV88W8618_FLASHCFG "mv88w8618_flashcfg"
6238063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(mv88w8618_flashcfg_state, MV88W8618_FLASHCFG)
6245952b01cSAndreas Färber 
625db1015e9SEduardo Habkost struct mv88w8618_flashcfg_state {
6265952b01cSAndreas Färber     /*< private >*/
6275952b01cSAndreas Färber     SysBusDevice parent_obj;
6285952b01cSAndreas Färber     /*< public >*/
6295952b01cSAndreas Färber 
63019b4a424SAvi Kivity     MemoryRegion iomem;
63124859b68Sbalrog     uint32_t cfgr0;
632db1015e9SEduardo Habkost };
63324859b68Sbalrog 
63419b4a424SAvi Kivity static uint64_t mv88w8618_flashcfg_read(void *opaque,
635a8170e5eSAvi Kivity                                         hwaddr offset,
63619b4a424SAvi Kivity                                         unsigned size)
63724859b68Sbalrog {
63824859b68Sbalrog     mv88w8618_flashcfg_state *s = opaque;
63924859b68Sbalrog 
64024859b68Sbalrog     switch (offset) {
64124859b68Sbalrog     case MP_FLASHCFG_CFGR0:
64224859b68Sbalrog         return s->cfgr0;
64324859b68Sbalrog 
64424859b68Sbalrog     default:
64524859b68Sbalrog         return 0;
64624859b68Sbalrog     }
64724859b68Sbalrog }
64824859b68Sbalrog 
649a8170e5eSAvi Kivity static void mv88w8618_flashcfg_write(void *opaque, hwaddr offset,
65019b4a424SAvi Kivity                                      uint64_t value, unsigned size)
65124859b68Sbalrog {
65224859b68Sbalrog     mv88w8618_flashcfg_state *s = opaque;
65324859b68Sbalrog 
65424859b68Sbalrog     switch (offset) {
65524859b68Sbalrog     case MP_FLASHCFG_CFGR0:
65624859b68Sbalrog         s->cfgr0 = value;
65724859b68Sbalrog         break;
65824859b68Sbalrog     }
65924859b68Sbalrog }
66024859b68Sbalrog 
66119b4a424SAvi Kivity static const MemoryRegionOps mv88w8618_flashcfg_ops = {
66219b4a424SAvi Kivity     .read = mv88w8618_flashcfg_read,
66319b4a424SAvi Kivity     .write = mv88w8618_flashcfg_write,
66419b4a424SAvi Kivity     .endianness = DEVICE_NATIVE_ENDIAN,
66524859b68Sbalrog };
66624859b68Sbalrog 
667ece71994Sxiaoqiang zhao static void mv88w8618_flashcfg_init(Object *obj)
66824859b68Sbalrog {
669ece71994Sxiaoqiang zhao     SysBusDevice *dev = SYS_BUS_DEVICE(obj);
6705952b01cSAndreas Färber     mv88w8618_flashcfg_state *s = MV88W8618_FLASHCFG(dev);
67124859b68Sbalrog 
67224859b68Sbalrog     s->cfgr0 = 0xfffe4285; /* Default as set by U-Boot for 8 MB flash */
673ece71994Sxiaoqiang zhao     memory_region_init_io(&s->iomem, obj, &mv88w8618_flashcfg_ops, s,
67419b4a424SAvi Kivity                           "musicpal-flashcfg", MP_FLASHCFG_SIZE);
675750ecd44SAvi Kivity     sysbus_init_mmio(dev, &s->iomem);
67624859b68Sbalrog }
67724859b68Sbalrog 
678d5b61dddSJan Kiszka static const VMStateDescription mv88w8618_flashcfg_vmsd = {
679d5b61dddSJan Kiszka     .name = "mv88w8618_flashcfg",
680d5b61dddSJan Kiszka     .version_id = 1,
681d5b61dddSJan Kiszka     .minimum_version_id = 1,
682d5b61dddSJan Kiszka     .fields = (VMStateField[]) {
683d5b61dddSJan Kiszka         VMSTATE_UINT32(cfgr0, mv88w8618_flashcfg_state),
684d5b61dddSJan Kiszka         VMSTATE_END_OF_LIST()
685d5b61dddSJan Kiszka     }
686d5b61dddSJan Kiszka };
687d5b61dddSJan Kiszka 
688999e12bbSAnthony Liguori static void mv88w8618_flashcfg_class_init(ObjectClass *klass, void *data)
689999e12bbSAnthony Liguori {
69039bffca2SAnthony Liguori     DeviceClass *dc = DEVICE_CLASS(klass);
691999e12bbSAnthony Liguori 
69239bffca2SAnthony Liguori     dc->vmsd = &mv88w8618_flashcfg_vmsd;
693999e12bbSAnthony Liguori }
694999e12bbSAnthony Liguori 
6958c43a6f0SAndreas Färber static const TypeInfo mv88w8618_flashcfg_info = {
6965952b01cSAndreas Färber     .name          = TYPE_MV88W8618_FLASHCFG,
69739bffca2SAnthony Liguori     .parent        = TYPE_SYS_BUS_DEVICE,
69839bffca2SAnthony Liguori     .instance_size = sizeof(mv88w8618_flashcfg_state),
699ece71994Sxiaoqiang zhao     .instance_init = mv88w8618_flashcfg_init,
700999e12bbSAnthony Liguori     .class_init    = mv88w8618_flashcfg_class_init,
701d5b61dddSJan Kiszka };
702d5b61dddSJan Kiszka 
703718ec0beSmalc /* Misc register offsets */
704718ec0beSmalc #define MP_MISC_BOARD_REVISION  0x18
70524859b68Sbalrog 
706718ec0beSmalc #define MP_BOARD_REVISION       0x31
70724859b68Sbalrog 
708db1015e9SEduardo Habkost struct MusicPalMiscState {
709a86f200aSPeter Maydell     SysBusDevice parent_obj;
710a86f200aSPeter Maydell     MemoryRegion iomem;
711db1015e9SEduardo Habkost };
712a86f200aSPeter Maydell 
713a86f200aSPeter Maydell #define TYPE_MUSICPAL_MISC "musicpal-misc"
7148063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(MusicPalMiscState, MUSICPAL_MISC)
715a86f200aSPeter Maydell 
716a8170e5eSAvi Kivity static uint64_t musicpal_misc_read(void *opaque, hwaddr offset,
71719b4a424SAvi Kivity                                    unsigned size)
718718ec0beSmalc {
719718ec0beSmalc     switch (offset) {
720718ec0beSmalc     case MP_MISC_BOARD_REVISION:
721718ec0beSmalc         return MP_BOARD_REVISION;
722718ec0beSmalc 
723718ec0beSmalc     default:
724718ec0beSmalc         return 0;
725718ec0beSmalc     }
726718ec0beSmalc }
727718ec0beSmalc 
728a8170e5eSAvi Kivity static void musicpal_misc_write(void *opaque, hwaddr offset,
72919b4a424SAvi Kivity                                 uint64_t value, unsigned size)
730718ec0beSmalc {
731718ec0beSmalc }
732718ec0beSmalc 
73319b4a424SAvi Kivity static const MemoryRegionOps musicpal_misc_ops = {
73419b4a424SAvi Kivity     .read = musicpal_misc_read,
73519b4a424SAvi Kivity     .write = musicpal_misc_write,
73619b4a424SAvi Kivity     .endianness = DEVICE_NATIVE_ENDIAN,
737718ec0beSmalc };
738718ec0beSmalc 
739a86f200aSPeter Maydell static void musicpal_misc_init(Object *obj)
740718ec0beSmalc {
741a86f200aSPeter Maydell     SysBusDevice *sd = SYS_BUS_DEVICE(obj);
742a86f200aSPeter Maydell     MusicPalMiscState *s = MUSICPAL_MISC(obj);
743718ec0beSmalc 
74464bde0f3SPaolo Bonzini     memory_region_init_io(&s->iomem, OBJECT(s), &musicpal_misc_ops, NULL,
74519b4a424SAvi Kivity                           "musicpal-misc", MP_MISC_SIZE);
746a86f200aSPeter Maydell     sysbus_init_mmio(sd, &s->iomem);
747718ec0beSmalc }
748718ec0beSmalc 
749a86f200aSPeter Maydell static const TypeInfo musicpal_misc_info = {
750a86f200aSPeter Maydell     .name = TYPE_MUSICPAL_MISC,
751a86f200aSPeter Maydell     .parent = TYPE_SYS_BUS_DEVICE,
752a86f200aSPeter Maydell     .instance_init = musicpal_misc_init,
753a86f200aSPeter Maydell     .instance_size = sizeof(MusicPalMiscState),
754a86f200aSPeter Maydell };
755a86f200aSPeter Maydell 
756718ec0beSmalc /* WLAN register offsets */
757718ec0beSmalc #define MP_WLAN_MAGIC1          0x11c
758718ec0beSmalc #define MP_WLAN_MAGIC2          0x124
759718ec0beSmalc 
760a8170e5eSAvi Kivity static uint64_t mv88w8618_wlan_read(void *opaque, hwaddr offset,
76119b4a424SAvi Kivity                                     unsigned size)
762718ec0beSmalc {
763718ec0beSmalc     switch (offset) {
764718ec0beSmalc     /* Workaround to allow loading the binary-only wlandrv.ko crap
765718ec0beSmalc      * from the original Freecom firmware. */
766718ec0beSmalc     case MP_WLAN_MAGIC1:
767718ec0beSmalc         return ~3;
768718ec0beSmalc     case MP_WLAN_MAGIC2:
769718ec0beSmalc         return -1;
770718ec0beSmalc 
771718ec0beSmalc     default:
772718ec0beSmalc         return 0;
773718ec0beSmalc     }
774718ec0beSmalc }
775718ec0beSmalc 
776a8170e5eSAvi Kivity static void mv88w8618_wlan_write(void *opaque, hwaddr offset,
77719b4a424SAvi Kivity                                  uint64_t value, unsigned size)
778718ec0beSmalc {
779718ec0beSmalc }
780718ec0beSmalc 
78119b4a424SAvi Kivity static const MemoryRegionOps mv88w8618_wlan_ops = {
78219b4a424SAvi Kivity     .read = mv88w8618_wlan_read,
78319b4a424SAvi Kivity     .write =mv88w8618_wlan_write,
78419b4a424SAvi Kivity     .endianness = DEVICE_NATIVE_ENDIAN,
785718ec0beSmalc };
786718ec0beSmalc 
7877f7420a0SMao Zhongyi static void mv88w8618_wlan_realize(DeviceState *dev, Error **errp)
788718ec0beSmalc {
78919b4a424SAvi Kivity     MemoryRegion *iomem = g_new(MemoryRegion, 1);
790718ec0beSmalc 
79164bde0f3SPaolo Bonzini     memory_region_init_io(iomem, OBJECT(dev), &mv88w8618_wlan_ops, NULL,
79219b4a424SAvi Kivity                           "musicpal-wlan", MP_WLAN_SIZE);
7937f7420a0SMao Zhongyi     sysbus_init_mmio(SYS_BUS_DEVICE(dev), iomem);
794718ec0beSmalc }
795718ec0beSmalc 
796718ec0beSmalc /* GPIO register offsets */
797718ec0beSmalc #define MP_GPIO_OE_LO           0x008
798718ec0beSmalc #define MP_GPIO_OUT_LO          0x00c
799718ec0beSmalc #define MP_GPIO_IN_LO           0x010
800708afdf3SJan Kiszka #define MP_GPIO_IER_LO          0x014
801708afdf3SJan Kiszka #define MP_GPIO_IMR_LO          0x018
802718ec0beSmalc #define MP_GPIO_ISR_LO          0x020
803718ec0beSmalc #define MP_GPIO_OE_HI           0x508
804718ec0beSmalc #define MP_GPIO_OUT_HI          0x50c
805718ec0beSmalc #define MP_GPIO_IN_HI           0x510
806708afdf3SJan Kiszka #define MP_GPIO_IER_HI          0x514
807708afdf3SJan Kiszka #define MP_GPIO_IMR_HI          0x518
808718ec0beSmalc #define MP_GPIO_ISR_HI          0x520
80924859b68Sbalrog 
81024859b68Sbalrog /* GPIO bits & masks */
81124859b68Sbalrog #define MP_GPIO_LCD_BRIGHTNESS  0x00070000
81224859b68Sbalrog #define MP_GPIO_I2C_DATA_BIT    29
81324859b68Sbalrog #define MP_GPIO_I2C_CLOCK_BIT   30
81424859b68Sbalrog 
81524859b68Sbalrog /* LCD brightness bits in GPIO_OE_HI */
81624859b68Sbalrog #define MP_OE_LCD_BRIGHTNESS    0x0007
81724859b68Sbalrog 
8187012d4b4SAndreas Färber #define TYPE_MUSICPAL_GPIO "musicpal_gpio"
8198063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(musicpal_gpio_state, MUSICPAL_GPIO)
8207012d4b4SAndreas Färber 
821db1015e9SEduardo Habkost struct musicpal_gpio_state {
8227012d4b4SAndreas Färber     /*< private >*/
8237012d4b4SAndreas Färber     SysBusDevice parent_obj;
8247012d4b4SAndreas Färber     /*< public >*/
8257012d4b4SAndreas Färber 
82619b4a424SAvi Kivity     MemoryRegion iomem;
827343ec8e4SBenoit Canet     uint32_t lcd_brightness;
828343ec8e4SBenoit Canet     uint32_t out_state;
829343ec8e4SBenoit Canet     uint32_t in_state;
830708afdf3SJan Kiszka     uint32_t ier;
831708afdf3SJan Kiszka     uint32_t imr;
832343ec8e4SBenoit Canet     uint32_t isr;
833343ec8e4SBenoit Canet     qemu_irq irq;
834708afdf3SJan Kiszka     qemu_irq out[5]; /* 3 brightness out + 2 lcd (data and clock ) */
835db1015e9SEduardo Habkost };
836343ec8e4SBenoit Canet 
837343ec8e4SBenoit Canet static void musicpal_gpio_brightness_update(musicpal_gpio_state *s) {
838343ec8e4SBenoit Canet     int i;
839343ec8e4SBenoit Canet     uint32_t brightness;
840343ec8e4SBenoit Canet 
841343ec8e4SBenoit Canet     /* compute brightness ratio */
842343ec8e4SBenoit Canet     switch (s->lcd_brightness) {
843343ec8e4SBenoit Canet     case 0x00000007:
844343ec8e4SBenoit Canet         brightness = 0;
845343ec8e4SBenoit Canet         break;
846343ec8e4SBenoit Canet 
847343ec8e4SBenoit Canet     case 0x00020000:
848343ec8e4SBenoit Canet         brightness = 1;
849343ec8e4SBenoit Canet         break;
850343ec8e4SBenoit Canet 
851343ec8e4SBenoit Canet     case 0x00020001:
852343ec8e4SBenoit Canet         brightness = 2;
853343ec8e4SBenoit Canet         break;
854343ec8e4SBenoit Canet 
855343ec8e4SBenoit Canet     case 0x00040000:
856343ec8e4SBenoit Canet         brightness = 3;
857343ec8e4SBenoit Canet         break;
858343ec8e4SBenoit Canet 
859343ec8e4SBenoit Canet     case 0x00010006:
860343ec8e4SBenoit Canet         brightness = 4;
861343ec8e4SBenoit Canet         break;
862343ec8e4SBenoit Canet 
863343ec8e4SBenoit Canet     case 0x00020005:
864343ec8e4SBenoit Canet         brightness = 5;
865343ec8e4SBenoit Canet         break;
866343ec8e4SBenoit Canet 
867343ec8e4SBenoit Canet     case 0x00040003:
868343ec8e4SBenoit Canet         brightness = 6;
869343ec8e4SBenoit Canet         break;
870343ec8e4SBenoit Canet 
871343ec8e4SBenoit Canet     case 0x00030004:
872343ec8e4SBenoit Canet     default:
873343ec8e4SBenoit Canet         brightness = 7;
874343ec8e4SBenoit Canet     }
875343ec8e4SBenoit Canet 
876343ec8e4SBenoit Canet     /* set lcd brightness GPIOs  */
87749fedd0dSJan Kiszka     for (i = 0; i <= 2; i++) {
878343ec8e4SBenoit Canet         qemu_set_irq(s->out[i], (brightness >> i) & 1);
879343ec8e4SBenoit Canet     }
88049fedd0dSJan Kiszka }
881343ec8e4SBenoit Canet 
882708afdf3SJan Kiszka static void musicpal_gpio_pin_event(void *opaque, int pin, int level)
883343ec8e4SBenoit Canet {
884243cd13cSJan Kiszka     musicpal_gpio_state *s = opaque;
885708afdf3SJan Kiszka     uint32_t mask = 1 << pin;
886708afdf3SJan Kiszka     uint32_t delta = level << pin;
887708afdf3SJan Kiszka     uint32_t old = s->in_state & mask;
888343ec8e4SBenoit Canet 
889708afdf3SJan Kiszka     s->in_state &= ~mask;
890708afdf3SJan Kiszka     s->in_state |= delta;
891708afdf3SJan Kiszka 
892708afdf3SJan Kiszka     if ((old ^ delta) &&
893708afdf3SJan Kiszka         ((level && (s->imr & mask)) || (!level && (s->ier & mask)))) {
894708afdf3SJan Kiszka         s->isr = mask;
895708afdf3SJan Kiszka         qemu_irq_raise(s->irq);
896d074769cSAndrzej Zaborowski     }
897343ec8e4SBenoit Canet }
898343ec8e4SBenoit Canet 
899a8170e5eSAvi Kivity static uint64_t musicpal_gpio_read(void *opaque, hwaddr offset,
90019b4a424SAvi Kivity                                    unsigned size)
90124859b68Sbalrog {
902243cd13cSJan Kiszka     musicpal_gpio_state *s = opaque;
903343ec8e4SBenoit Canet 
90424859b68Sbalrog     switch (offset) {
90524859b68Sbalrog     case MP_GPIO_OE_HI: /* used for LCD brightness control */
906343ec8e4SBenoit Canet         return s->lcd_brightness & MP_OE_LCD_BRIGHTNESS;
90724859b68Sbalrog 
90824859b68Sbalrog     case MP_GPIO_OUT_LO:
909343ec8e4SBenoit Canet         return s->out_state & 0xFFFF;
91024859b68Sbalrog     case MP_GPIO_OUT_HI:
911343ec8e4SBenoit Canet         return s->out_state >> 16;
91224859b68Sbalrog 
91324859b68Sbalrog     case MP_GPIO_IN_LO:
914343ec8e4SBenoit Canet         return s->in_state & 0xFFFF;
91524859b68Sbalrog     case MP_GPIO_IN_HI:
916343ec8e4SBenoit Canet         return s->in_state >> 16;
91724859b68Sbalrog 
918708afdf3SJan Kiszka     case MP_GPIO_IER_LO:
919708afdf3SJan Kiszka         return s->ier & 0xFFFF;
920708afdf3SJan Kiszka     case MP_GPIO_IER_HI:
921708afdf3SJan Kiszka         return s->ier >> 16;
922708afdf3SJan Kiszka 
923708afdf3SJan Kiszka     case MP_GPIO_IMR_LO:
924708afdf3SJan Kiszka         return s->imr & 0xFFFF;
925708afdf3SJan Kiszka     case MP_GPIO_IMR_HI:
926708afdf3SJan Kiszka         return s->imr >> 16;
927708afdf3SJan Kiszka 
92824859b68Sbalrog     case MP_GPIO_ISR_LO:
929343ec8e4SBenoit Canet         return s->isr & 0xFFFF;
93024859b68Sbalrog     case MP_GPIO_ISR_HI:
931343ec8e4SBenoit Canet         return s->isr >> 16;
93224859b68Sbalrog 
93324859b68Sbalrog     default:
93424859b68Sbalrog         return 0;
93524859b68Sbalrog     }
93624859b68Sbalrog }
93724859b68Sbalrog 
938a8170e5eSAvi Kivity static void musicpal_gpio_write(void *opaque, hwaddr offset,
93919b4a424SAvi Kivity                                 uint64_t value, unsigned size)
94024859b68Sbalrog {
941243cd13cSJan Kiszka     musicpal_gpio_state *s = opaque;
94224859b68Sbalrog     switch (offset) {
94324859b68Sbalrog     case MP_GPIO_OE_HI: /* used for LCD brightness control */
944343ec8e4SBenoit Canet         s->lcd_brightness = (s->lcd_brightness & MP_GPIO_LCD_BRIGHTNESS) |
94524859b68Sbalrog                          (value & MP_OE_LCD_BRIGHTNESS);
946343ec8e4SBenoit Canet         musicpal_gpio_brightness_update(s);
94724859b68Sbalrog         break;
94824859b68Sbalrog 
94924859b68Sbalrog     case MP_GPIO_OUT_LO:
950343ec8e4SBenoit Canet         s->out_state = (s->out_state & 0xFFFF0000) | (value & 0xFFFF);
95124859b68Sbalrog         break;
95224859b68Sbalrog     case MP_GPIO_OUT_HI:
953343ec8e4SBenoit Canet         s->out_state = (s->out_state & 0xFFFF) | (value << 16);
954343ec8e4SBenoit Canet         s->lcd_brightness = (s->lcd_brightness & 0xFFFF) |
955343ec8e4SBenoit Canet                             (s->out_state & MP_GPIO_LCD_BRIGHTNESS);
956343ec8e4SBenoit Canet         musicpal_gpio_brightness_update(s);
957d074769cSAndrzej Zaborowski         qemu_set_irq(s->out[3], (s->out_state >> MP_GPIO_I2C_DATA_BIT) & 1);
958d074769cSAndrzej Zaborowski         qemu_set_irq(s->out[4], (s->out_state >> MP_GPIO_I2C_CLOCK_BIT) & 1);
95924859b68Sbalrog         break;
96024859b68Sbalrog 
961708afdf3SJan Kiszka     case MP_GPIO_IER_LO:
962708afdf3SJan Kiszka         s->ier = (s->ier & 0xFFFF0000) | (value & 0xFFFF);
963708afdf3SJan Kiszka         break;
964708afdf3SJan Kiszka     case MP_GPIO_IER_HI:
965708afdf3SJan Kiszka         s->ier = (s->ier & 0xFFFF) | (value << 16);
966708afdf3SJan Kiszka         break;
967708afdf3SJan Kiszka 
968708afdf3SJan Kiszka     case MP_GPIO_IMR_LO:
969708afdf3SJan Kiszka         s->imr = (s->imr & 0xFFFF0000) | (value & 0xFFFF);
970708afdf3SJan Kiszka         break;
971708afdf3SJan Kiszka     case MP_GPIO_IMR_HI:
972708afdf3SJan Kiszka         s->imr = (s->imr & 0xFFFF) | (value << 16);
973708afdf3SJan Kiszka         break;
97424859b68Sbalrog     }
97524859b68Sbalrog }
97624859b68Sbalrog 
97719b4a424SAvi Kivity static const MemoryRegionOps musicpal_gpio_ops = {
97819b4a424SAvi Kivity     .read = musicpal_gpio_read,
97919b4a424SAvi Kivity     .write = musicpal_gpio_write,
98019b4a424SAvi Kivity     .endianness = DEVICE_NATIVE_ENDIAN,
981718ec0beSmalc };
982718ec0beSmalc 
983d5b61dddSJan Kiszka static void musicpal_gpio_reset(DeviceState *d)
984718ec0beSmalc {
9857012d4b4SAndreas Färber     musicpal_gpio_state *s = MUSICPAL_GPIO(d);
98630624c92SJan Kiszka 
98730624c92SJan Kiszka     s->lcd_brightness = 0;
98830624c92SJan Kiszka     s->out_state = 0;
989343ec8e4SBenoit Canet     s->in_state = 0xffffffff;
990708afdf3SJan Kiszka     s->ier = 0;
991708afdf3SJan Kiszka     s->imr = 0;
992343ec8e4SBenoit Canet     s->isr = 0;
993343ec8e4SBenoit Canet }
994343ec8e4SBenoit Canet 
995ece71994Sxiaoqiang zhao static void musicpal_gpio_init(Object *obj)
996343ec8e4SBenoit Canet {
997ece71994Sxiaoqiang zhao     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
9987012d4b4SAndreas Färber     DeviceState *dev = DEVICE(sbd);
9997012d4b4SAndreas Färber     musicpal_gpio_state *s = MUSICPAL_GPIO(dev);
1000718ec0beSmalc 
10017012d4b4SAndreas Färber     sysbus_init_irq(sbd, &s->irq);
1002343ec8e4SBenoit Canet 
1003ece71994Sxiaoqiang zhao     memory_region_init_io(&s->iomem, obj, &musicpal_gpio_ops, s,
100419b4a424SAvi Kivity                           "musicpal-gpio", MP_GPIO_SIZE);
10057012d4b4SAndreas Färber     sysbus_init_mmio(sbd, &s->iomem);
1006343ec8e4SBenoit Canet 
10077012d4b4SAndreas Färber     qdev_init_gpio_out(dev, s->out, ARRAY_SIZE(s->out));
1008708afdf3SJan Kiszka 
10097012d4b4SAndreas Färber     qdev_init_gpio_in(dev, musicpal_gpio_pin_event, 32);
1010718ec0beSmalc }
1011718ec0beSmalc 
1012d5b61dddSJan Kiszka static const VMStateDescription musicpal_gpio_vmsd = {
1013d5b61dddSJan Kiszka     .name = "musicpal_gpio",
1014d5b61dddSJan Kiszka     .version_id = 1,
1015d5b61dddSJan Kiszka     .minimum_version_id = 1,
1016d5b61dddSJan Kiszka     .fields = (VMStateField[]) {
1017d5b61dddSJan Kiszka         VMSTATE_UINT32(lcd_brightness, musicpal_gpio_state),
1018d5b61dddSJan Kiszka         VMSTATE_UINT32(out_state, musicpal_gpio_state),
1019d5b61dddSJan Kiszka         VMSTATE_UINT32(in_state, musicpal_gpio_state),
1020d5b61dddSJan Kiszka         VMSTATE_UINT32(ier, musicpal_gpio_state),
1021d5b61dddSJan Kiszka         VMSTATE_UINT32(imr, musicpal_gpio_state),
1022d5b61dddSJan Kiszka         VMSTATE_UINT32(isr, musicpal_gpio_state),
1023d5b61dddSJan Kiszka         VMSTATE_END_OF_LIST()
1024d5b61dddSJan Kiszka     }
1025d5b61dddSJan Kiszka };
1026d5b61dddSJan Kiszka 
1027999e12bbSAnthony Liguori static void musicpal_gpio_class_init(ObjectClass *klass, void *data)
1028999e12bbSAnthony Liguori {
102939bffca2SAnthony Liguori     DeviceClass *dc = DEVICE_CLASS(klass);
1030999e12bbSAnthony Liguori 
103139bffca2SAnthony Liguori     dc->reset = musicpal_gpio_reset;
103239bffca2SAnthony Liguori     dc->vmsd = &musicpal_gpio_vmsd;
1033999e12bbSAnthony Liguori }
1034999e12bbSAnthony Liguori 
10358c43a6f0SAndreas Färber static const TypeInfo musicpal_gpio_info = {
10367012d4b4SAndreas Färber     .name          = TYPE_MUSICPAL_GPIO,
103739bffca2SAnthony Liguori     .parent        = TYPE_SYS_BUS_DEVICE,
103839bffca2SAnthony Liguori     .instance_size = sizeof(musicpal_gpio_state),
1039ece71994Sxiaoqiang zhao     .instance_init = musicpal_gpio_init,
1040999e12bbSAnthony Liguori     .class_init    = musicpal_gpio_class_init,
104130624c92SJan Kiszka };
104230624c92SJan Kiszka 
104324859b68Sbalrog /* Keyboard codes & masks */
10447c6ce4baSbalrog #define KEY_RELEASED            0x80
104524859b68Sbalrog #define KEY_CODE                0x7f
104624859b68Sbalrog 
104724859b68Sbalrog #define KEYCODE_TAB             0x0f
104824859b68Sbalrog #define KEYCODE_ENTER           0x1c
104924859b68Sbalrog #define KEYCODE_F               0x21
105024859b68Sbalrog #define KEYCODE_M               0x32
105124859b68Sbalrog 
105224859b68Sbalrog #define KEYCODE_EXTENDED        0xe0
105324859b68Sbalrog #define KEYCODE_UP              0x48
105424859b68Sbalrog #define KEYCODE_DOWN            0x50
105524859b68Sbalrog #define KEYCODE_LEFT            0x4b
105624859b68Sbalrog #define KEYCODE_RIGHT           0x4d
105724859b68Sbalrog 
1058708afdf3SJan Kiszka #define MP_KEY_WHEEL_VOL       (1 << 0)
1059343ec8e4SBenoit Canet #define MP_KEY_WHEEL_VOL_INV   (1 << 1)
1060343ec8e4SBenoit Canet #define MP_KEY_WHEEL_NAV       (1 << 2)
1061343ec8e4SBenoit Canet #define MP_KEY_WHEEL_NAV_INV   (1 << 3)
1062343ec8e4SBenoit Canet #define MP_KEY_BTN_FAVORITS    (1 << 4)
1063343ec8e4SBenoit Canet #define MP_KEY_BTN_MENU        (1 << 5)
1064343ec8e4SBenoit Canet #define MP_KEY_BTN_VOLUME      (1 << 6)
1065343ec8e4SBenoit Canet #define MP_KEY_BTN_NAVIGATION  (1 << 7)
1066343ec8e4SBenoit Canet 
10673bdf5327SAndreas Färber #define TYPE_MUSICPAL_KEY "musicpal_key"
10688063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(musicpal_key_state, MUSICPAL_KEY)
10693bdf5327SAndreas Färber 
1070db1015e9SEduardo Habkost struct musicpal_key_state {
10713bdf5327SAndreas Färber     /*< private >*/
10723bdf5327SAndreas Färber     SysBusDevice parent_obj;
10733bdf5327SAndreas Färber     /*< public >*/
10743bdf5327SAndreas Färber 
10754f5c9479SAvi Kivity     MemoryRegion iomem;
1076343ec8e4SBenoit Canet     uint32_t kbd_extended;
1077708afdf3SJan Kiszka     uint32_t pressed_keys;
1078708afdf3SJan Kiszka     qemu_irq out[8];
1079db1015e9SEduardo Habkost };
1080343ec8e4SBenoit Canet 
108124859b68Sbalrog static void musicpal_key_event(void *opaque, int keycode)
108224859b68Sbalrog {
1083243cd13cSJan Kiszka     musicpal_key_state *s = opaque;
108424859b68Sbalrog     uint32_t event = 0;
1085343ec8e4SBenoit Canet     int i;
108624859b68Sbalrog 
108724859b68Sbalrog     if (keycode == KEYCODE_EXTENDED) {
1088343ec8e4SBenoit Canet         s->kbd_extended = 1;
108924859b68Sbalrog         return;
109024859b68Sbalrog     }
109124859b68Sbalrog 
109249fedd0dSJan Kiszka     if (s->kbd_extended) {
109324859b68Sbalrog         switch (keycode & KEY_CODE) {
109424859b68Sbalrog         case KEYCODE_UP:
1095343ec8e4SBenoit Canet             event = MP_KEY_WHEEL_NAV | MP_KEY_WHEEL_NAV_INV;
109624859b68Sbalrog             break;
109724859b68Sbalrog 
109824859b68Sbalrog         case KEYCODE_DOWN:
1099343ec8e4SBenoit Canet             event = MP_KEY_WHEEL_NAV;
110024859b68Sbalrog             break;
110124859b68Sbalrog 
110224859b68Sbalrog         case KEYCODE_LEFT:
1103343ec8e4SBenoit Canet             event = MP_KEY_WHEEL_VOL | MP_KEY_WHEEL_VOL_INV;
110424859b68Sbalrog             break;
110524859b68Sbalrog 
110624859b68Sbalrog         case KEYCODE_RIGHT:
1107343ec8e4SBenoit Canet             event = MP_KEY_WHEEL_VOL;
110824859b68Sbalrog             break;
110924859b68Sbalrog         }
111049fedd0dSJan Kiszka     } else {
111124859b68Sbalrog         switch (keycode & KEY_CODE) {
111224859b68Sbalrog         case KEYCODE_F:
1113343ec8e4SBenoit Canet             event = MP_KEY_BTN_FAVORITS;
111424859b68Sbalrog             break;
111524859b68Sbalrog 
111624859b68Sbalrog         case KEYCODE_TAB:
1117343ec8e4SBenoit Canet             event = MP_KEY_BTN_VOLUME;
111824859b68Sbalrog             break;
111924859b68Sbalrog 
112024859b68Sbalrog         case KEYCODE_ENTER:
1121343ec8e4SBenoit Canet             event = MP_KEY_BTN_NAVIGATION;
112224859b68Sbalrog             break;
112324859b68Sbalrog 
112424859b68Sbalrog         case KEYCODE_M:
1125343ec8e4SBenoit Canet             event = MP_KEY_BTN_MENU;
112624859b68Sbalrog             break;
112724859b68Sbalrog         }
11287c6ce4baSbalrog         /* Do not repeat already pressed buttons */
1129708afdf3SJan Kiszka         if (!(keycode & KEY_RELEASED) && (s->pressed_keys & event)) {
11307c6ce4baSbalrog             event = 0;
11317c6ce4baSbalrog         }
1132708afdf3SJan Kiszka     }
113324859b68Sbalrog 
11347c6ce4baSbalrog     if (event) {
1135708afdf3SJan Kiszka         /* Raise GPIO pin first if repeating a key */
1136708afdf3SJan Kiszka         if (!(keycode & KEY_RELEASED) && (s->pressed_keys & event)) {
1137708afdf3SJan Kiszka             for (i = 0; i <= 7; i++) {
1138708afdf3SJan Kiszka                 if (event & (1 << i)) {
1139708afdf3SJan Kiszka                     qemu_set_irq(s->out[i], 1);
11407c6ce4baSbalrog                 }
1141708afdf3SJan Kiszka             }
1142708afdf3SJan Kiszka         }
1143708afdf3SJan Kiszka         for (i = 0; i <= 7; i++) {
1144708afdf3SJan Kiszka             if (event & (1 << i)) {
1145708afdf3SJan Kiszka                 qemu_set_irq(s->out[i], !!(keycode & KEY_RELEASED));
1146708afdf3SJan Kiszka             }
1147708afdf3SJan Kiszka         }
1148708afdf3SJan Kiszka         if (keycode & KEY_RELEASED) {
1149708afdf3SJan Kiszka             s->pressed_keys &= ~event;
1150708afdf3SJan Kiszka         } else {
1151708afdf3SJan Kiszka             s->pressed_keys |= event;
1152708afdf3SJan Kiszka         }
1153343ec8e4SBenoit Canet     }
1154343ec8e4SBenoit Canet 
1155343ec8e4SBenoit Canet     s->kbd_extended = 0;
1156343ec8e4SBenoit Canet }
1157343ec8e4SBenoit Canet 
1158ece71994Sxiaoqiang zhao static void musicpal_key_init(Object *obj)
1159343ec8e4SBenoit Canet {
1160ece71994Sxiaoqiang zhao     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
11613bdf5327SAndreas Färber     DeviceState *dev = DEVICE(sbd);
11623bdf5327SAndreas Färber     musicpal_key_state *s = MUSICPAL_KEY(dev);
1163343ec8e4SBenoit Canet 
1164ece71994Sxiaoqiang zhao     memory_region_init(&s->iomem, obj, "dummy", 0);
11653bdf5327SAndreas Färber     sysbus_init_mmio(sbd, &s->iomem);
1166343ec8e4SBenoit Canet 
1167343ec8e4SBenoit Canet     s->kbd_extended = 0;
1168708afdf3SJan Kiszka     s->pressed_keys = 0;
1169343ec8e4SBenoit Canet 
11703bdf5327SAndreas Färber     qdev_init_gpio_out(dev, s->out, ARRAY_SIZE(s->out));
1171343ec8e4SBenoit Canet 
1172343ec8e4SBenoit Canet     qemu_add_kbd_event_handler(musicpal_key_event, s);
117324859b68Sbalrog }
117424859b68Sbalrog 
1175d5b61dddSJan Kiszka static const VMStateDescription musicpal_key_vmsd = {
1176d5b61dddSJan Kiszka     .name = "musicpal_key",
1177d5b61dddSJan Kiszka     .version_id = 1,
1178d5b61dddSJan Kiszka     .minimum_version_id = 1,
1179d5b61dddSJan Kiszka     .fields = (VMStateField[]) {
1180d5b61dddSJan Kiszka         VMSTATE_UINT32(kbd_extended, musicpal_key_state),
1181d5b61dddSJan Kiszka         VMSTATE_UINT32(pressed_keys, musicpal_key_state),
1182d5b61dddSJan Kiszka         VMSTATE_END_OF_LIST()
1183d5b61dddSJan Kiszka     }
1184d5b61dddSJan Kiszka };
1185d5b61dddSJan Kiszka 
1186999e12bbSAnthony Liguori static void musicpal_key_class_init(ObjectClass *klass, void *data)
1187999e12bbSAnthony Liguori {
118839bffca2SAnthony Liguori     DeviceClass *dc = DEVICE_CLASS(klass);
1189999e12bbSAnthony Liguori 
119039bffca2SAnthony Liguori     dc->vmsd = &musicpal_key_vmsd;
1191999e12bbSAnthony Liguori }
1192999e12bbSAnthony Liguori 
11938c43a6f0SAndreas Färber static const TypeInfo musicpal_key_info = {
11943bdf5327SAndreas Färber     .name          = TYPE_MUSICPAL_KEY,
119539bffca2SAnthony Liguori     .parent        = TYPE_SYS_BUS_DEVICE,
119639bffca2SAnthony Liguori     .instance_size = sizeof(musicpal_key_state),
1197ece71994Sxiaoqiang zhao     .instance_init = musicpal_key_init,
1198999e12bbSAnthony Liguori     .class_init    = musicpal_key_class_init,
1199d5b61dddSJan Kiszka };
1200d5b61dddSJan Kiszka 
1201e0ee6413SPhilippe Mathieu-Daudé #define FLASH_SECTOR_SIZE   (64 * KiB)
1202e0ee6413SPhilippe Mathieu-Daudé 
120324859b68Sbalrog static struct arm_boot_info musicpal_binfo = {
120424859b68Sbalrog     .loader_start = 0x0,
120524859b68Sbalrog     .board_id = 0x20e,
120624859b68Sbalrog };
120724859b68Sbalrog 
12083ef96221SMarcel Apfelbaum static void musicpal_init(MachineState *machine)
120924859b68Sbalrog {
1210f25608e9SAndreas Färber     ARMCPU *cpu;
1211b47b50faSPaul Brook     DeviceState *dev;
121244cbf349SPhilippe Mathieu-Daudé     DeviceState *pic;
1213498661ddSPhilippe Mathieu-Daudé     DeviceState *uart_orgate;
1214d074769cSAndrzej Zaborowski     DeviceState *i2c_dev;
1215343ec8e4SBenoit Canet     DeviceState *lcd_dev;
1216343ec8e4SBenoit Canet     DeviceState *key_dev;
12171373b15bSPhilippe Mathieu-Daudé     I2CSlave *wm8750_dev;
1218d074769cSAndrzej Zaborowski     SysBusDevice *s;
1219a5c82852SAndreas Färber     I2CBus *i2c;
1220b47b50faSPaul Brook     int i;
122124859b68Sbalrog     unsigned long flash_size;
1222751c6a17SGerd Hoffmann     DriveInfo *dinfo;
12233ed61312SIgor Mammedov     MachineClass *mc = MACHINE_GET_CLASS(machine);
122419b4a424SAvi Kivity     MemoryRegion *address_space_mem = get_system_memory();
122519b4a424SAvi Kivity     MemoryRegion *sram = g_new(MemoryRegion, 1);
122624859b68Sbalrog 
12273ed61312SIgor Mammedov     /* For now we use a fixed - the original - RAM size */
12283ed61312SIgor Mammedov     if (machine->ram_size != mc->default_ram_size) {
12293ed61312SIgor Mammedov         char *sz = size_to_str(mc->default_ram_size);
12303ed61312SIgor Mammedov         error_report("Invalid RAM size, should be %s", sz);
12313ed61312SIgor Mammedov         g_free(sz);
12323ed61312SIgor Mammedov         exit(EXIT_FAILURE);
12333ed61312SIgor Mammedov     }
12343ed61312SIgor Mammedov 
1235ba1ba5ccSIgor Mammedov     cpu = ARM_CPU(cpu_create(machine->cpu_type));
123624859b68Sbalrog 
12373ed61312SIgor Mammedov     memory_region_add_subregion(address_space_mem, 0, machine->ram);
123824859b68Sbalrog 
123998a99ce0SPeter Maydell     memory_region_init_ram(sram, NULL, "musicpal.sram", MP_SRAM_SIZE,
1240f8ed85acSMarkus Armbruster                            &error_fatal);
124119b4a424SAvi Kivity     memory_region_add_subregion(address_space_mem, MP_SRAM_BASE, sram);
124224859b68Sbalrog 
124344cbf349SPhilippe Mathieu-Daudé     pic = sysbus_create_simple(TYPE_MV88W8618_PIC, MP_PIC_BASE,
1244fcef61ecSPeter Maydell                                qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ));
124544cbf349SPhilippe Mathieu-Daudé     sysbus_create_varargs(TYPE_MV88W8618_PIT, MP_PIT_BASE,
124644cbf349SPhilippe Mathieu-Daudé                           qdev_get_gpio_in(pic, MP_TIMER1_IRQ),
124744cbf349SPhilippe Mathieu-Daudé                           qdev_get_gpio_in(pic, MP_TIMER2_IRQ),
124844cbf349SPhilippe Mathieu-Daudé                           qdev_get_gpio_in(pic, MP_TIMER3_IRQ),
124944cbf349SPhilippe Mathieu-Daudé                           qdev_get_gpio_in(pic, MP_TIMER4_IRQ), NULL);
125024859b68Sbalrog 
1251498661ddSPhilippe Mathieu-Daudé     /* Logically OR both UART IRQs together */
1252498661ddSPhilippe Mathieu-Daudé     uart_orgate = DEVICE(object_new(TYPE_OR_IRQ));
1253498661ddSPhilippe Mathieu-Daudé     object_property_set_int(OBJECT(uart_orgate), "num-lines", 2, &error_fatal);
1254498661ddSPhilippe Mathieu-Daudé     qdev_realize_and_unref(uart_orgate, NULL, &error_fatal);
125544cbf349SPhilippe Mathieu-Daudé     qdev_connect_gpio_out(DEVICE(uart_orgate), 0,
125644cbf349SPhilippe Mathieu-Daudé                           qdev_get_gpio_in(pic, MP_UART_SHARED_IRQ));
1257498661ddSPhilippe Mathieu-Daudé 
1258498661ddSPhilippe Mathieu-Daudé     serial_mm_init(address_space_mem, MP_UART1_BASE, 2,
1259498661ddSPhilippe Mathieu-Daudé                    qdev_get_gpio_in(uart_orgate, 0),
12609bca0edbSPeter Maydell                    1825000, serial_hd(0), DEVICE_NATIVE_ENDIAN);
1261498661ddSPhilippe Mathieu-Daudé     serial_mm_init(address_space_mem, MP_UART2_BASE, 2,
1262498661ddSPhilippe Mathieu-Daudé                    qdev_get_gpio_in(uart_orgate, 1),
12639bca0edbSPeter Maydell                    1825000, serial_hd(1), DEVICE_NATIVE_ENDIAN);
126424859b68Sbalrog 
126524859b68Sbalrog     /* Register flash */
1266751c6a17SGerd Hoffmann     dinfo = drive_get(IF_PFLASH, 0, 0);
1267751c6a17SGerd Hoffmann     if (dinfo) {
12684be74634SMarkus Armbruster         BlockBackend *blk = blk_by_legacy_dinfo(dinfo);
1269fa1d36dfSMarkus Armbruster 
12704be74634SMarkus Armbruster         flash_size = blk_getlength(blk);
1271e0ee6413SPhilippe Mathieu-Daudé         if (flash_size != 8 * MiB && flash_size != 16 * MiB &&
1272e0ee6413SPhilippe Mathieu-Daudé             flash_size != 32 * MiB) {
1273c0dbca36SAlistair Francis             error_report("Invalid flash image size");
127424859b68Sbalrog             exit(1);
127524859b68Sbalrog         }
127624859b68Sbalrog 
127724859b68Sbalrog         /*
127824859b68Sbalrog          * The original U-Boot accesses the flash at 0xFE000000 instead of
127924859b68Sbalrog          * 0xFF800000 (if there is 8 MB flash). So remap flash access if the
128024859b68Sbalrog          * image is smaller than 32 MB.
128124859b68Sbalrog          */
1282940d5b13SMarkus Armbruster         pflash_cfi02_register(0x100000000ULL - MP_FLASH_SIZE_MAX,
1283cfe5f011SAvi Kivity                               "musicpal.flash", flash_size,
1284e0ee6413SPhilippe Mathieu-Daudé                               blk, FLASH_SECTOR_SIZE,
12855f9fc5adSBlue Swirl                               MP_FLASH_SIZE_MAX / flash_size,
12865f9fc5adSBlue Swirl                               2, 0x00BF, 0x236D, 0x0000, 0x0000,
128701e0451aSAnthony Liguori                               0x5555, 0x2AAA, 0);
128824859b68Sbalrog     }
12895952b01cSAndreas Färber     sysbus_create_simple(TYPE_MV88W8618_FLASHCFG, MP_FLASHCFG_BASE, NULL);
129024859b68Sbalrog 
1291b47b50faSPaul Brook     qemu_check_nic_model(&nd_table[0], "mv88w8618");
12923e80f690SMarkus Armbruster     dev = qdev_new(TYPE_MV88W8618_ETH);
12934c91cd28SGerd Hoffmann     qdev_set_nic_properties(dev, &nd_table[0]);
129479ed6fd6SPhilippe Mathieu-Daudé     object_property_set_link(OBJECT(dev), "dma-memory",
129579ed6fd6SPhilippe Mathieu-Daudé                              OBJECT(get_system_memory()), &error_fatal);
12963c6ef471SMarkus Armbruster     sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
12971356b98dSAndreas Färber     sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, MP_ETH_BASE);
129844cbf349SPhilippe Mathieu-Daudé     sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0,
129944cbf349SPhilippe Mathieu-Daudé                        qdev_get_gpio_in(pic, MP_ETH_IRQ));
130024859b68Sbalrog 
1301b47b50faSPaul Brook     sysbus_create_simple("mv88w8618_wlan", MP_WLAN_BASE, NULL);
1302718ec0beSmalc 
1303a86f200aSPeter Maydell     sysbus_create_simple(TYPE_MUSICPAL_MISC, MP_MISC_BASE, NULL);
1304343ec8e4SBenoit Canet 
13057012d4b4SAndreas Färber     dev = sysbus_create_simple(TYPE_MUSICPAL_GPIO, MP_GPIO_BASE,
130644cbf349SPhilippe Mathieu-Daudé                                qdev_get_gpio_in(pic, MP_GPIO_IRQ));
1307*da8df26dSPhilippe Mathieu-Daudé     i2c_dev = sysbus_create_simple(TYPE_GPIO_I2C, -1, NULL);
1308a5c82852SAndreas Färber     i2c = (I2CBus *)qdev_get_child_bus(i2c_dev, "i2c");
1309d074769cSAndrzej Zaborowski 
13102cca58fdSAndreas Färber     lcd_dev = sysbus_create_simple(TYPE_MUSICPAL_LCD, MP_LCD_BASE, NULL);
13113bdf5327SAndreas Färber     key_dev = sysbus_create_simple(TYPE_MUSICPAL_KEY, -1, NULL);
1312343ec8e4SBenoit Canet 
1313d074769cSAndrzej Zaborowski     /* I2C read data */
1314708afdf3SJan Kiszka     qdev_connect_gpio_out(i2c_dev, 0,
1315708afdf3SJan Kiszka                           qdev_get_gpio_in(dev, MP_GPIO_I2C_DATA_BIT));
1316d074769cSAndrzej Zaborowski     /* I2C data */
1317d074769cSAndrzej Zaborowski     qdev_connect_gpio_out(dev, 3, qdev_get_gpio_in(i2c_dev, 0));
1318d074769cSAndrzej Zaborowski     /* I2C clock */
1319d074769cSAndrzej Zaborowski     qdev_connect_gpio_out(dev, 4, qdev_get_gpio_in(i2c_dev, 1));
1320d074769cSAndrzej Zaborowski 
132149fedd0dSJan Kiszka     for (i = 0; i < 3; i++) {
1322343ec8e4SBenoit Canet         qdev_connect_gpio_out(dev, i, qdev_get_gpio_in(lcd_dev, i));
132349fedd0dSJan Kiszka     }
1324708afdf3SJan Kiszka     for (i = 0; i < 4; i++) {
1325708afdf3SJan Kiszka         qdev_connect_gpio_out(key_dev, i, qdev_get_gpio_in(dev, i + 8));
1326708afdf3SJan Kiszka     }
1327708afdf3SJan Kiszka     for (i = 4; i < 8; i++) {
1328708afdf3SJan Kiszka         qdev_connect_gpio_out(key_dev, i, qdev_get_gpio_in(dev, i + 15));
1329708afdf3SJan Kiszka     }
133024859b68Sbalrog 
13311373b15bSPhilippe Mathieu-Daudé     wm8750_dev = i2c_slave_create_simple(i2c, TYPE_WM8750, MP_WM_ADDR);
13323e80f690SMarkus Armbruster     dev = qdev_new(TYPE_MV88W8618_AUDIO);
13331356b98dSAndreas Färber     s = SYS_BUS_DEVICE(dev);
13345325cc34SMarkus Armbruster     object_property_set_link(OBJECT(dev), "wm8750", OBJECT(wm8750_dev),
13355325cc34SMarkus Armbruster                              NULL);
13363c6ef471SMarkus Armbruster     sysbus_realize_and_unref(s, &error_fatal);
1337d074769cSAndrzej Zaborowski     sysbus_mmio_map(s, 0, MP_AUDIO_BASE);
133844cbf349SPhilippe Mathieu-Daudé     sysbus_connect_irq(s, 0, qdev_get_gpio_in(pic, MP_AUDIO_IRQ));
1339d074769cSAndrzej Zaborowski 
134024859b68Sbalrog     musicpal_binfo.ram_size = MP_RAM_DEFAULT_SIZE;
13412744ece8STao Xu     arm_load_kernel(cpu, machine, &musicpal_binfo);
134224859b68Sbalrog }
134324859b68Sbalrog 
1344e264d29dSEduardo Habkost static void musicpal_machine_init(MachineClass *mc)
1345f80f9ec9SAnthony Liguori {
1346e264d29dSEduardo Habkost     mc->desc = "Marvell 88w8618 / MusicPal (ARM926EJ-S)";
1347e264d29dSEduardo Habkost     mc->init = musicpal_init;
13484672cbd7SPeter Maydell     mc->ignore_memory_transaction_failures = true;
1349ba1ba5ccSIgor Mammedov     mc->default_cpu_type = ARM_CPU_TYPE_NAME("arm926");
13503ed61312SIgor Mammedov     mc->default_ram_size = MP_RAM_DEFAULT_SIZE;
13513ed61312SIgor Mammedov     mc->default_ram_id = "musicpal.ram";
1352f80f9ec9SAnthony Liguori }
1353f80f9ec9SAnthony Liguori 
1354e264d29dSEduardo Habkost DEFINE_MACHINE("musicpal", musicpal_machine_init)
1355f80f9ec9SAnthony Liguori 
1356999e12bbSAnthony Liguori static void mv88w8618_wlan_class_init(ObjectClass *klass, void *data)
1357999e12bbSAnthony Liguori {
13587f7420a0SMao Zhongyi     DeviceClass *dc = DEVICE_CLASS(klass);
1359999e12bbSAnthony Liguori 
13607f7420a0SMao Zhongyi     dc->realize = mv88w8618_wlan_realize;
1361999e12bbSAnthony Liguori }
1362999e12bbSAnthony Liguori 
13638c43a6f0SAndreas Färber static const TypeInfo mv88w8618_wlan_info = {
1364999e12bbSAnthony Liguori     .name          = "mv88w8618_wlan",
136539bffca2SAnthony Liguori     .parent        = TYPE_SYS_BUS_DEVICE,
136639bffca2SAnthony Liguori     .instance_size = sizeof(SysBusDevice),
1367999e12bbSAnthony Liguori     .class_init    = mv88w8618_wlan_class_init,
1368999e12bbSAnthony Liguori };
1369999e12bbSAnthony Liguori 
137083f7d43aSAndreas Färber static void musicpal_register_types(void)
1371b47b50faSPaul Brook {
137239bffca2SAnthony Liguori     type_register_static(&mv88w8618_pic_info);
137339bffca2SAnthony Liguori     type_register_static(&mv88w8618_pit_info);
137439bffca2SAnthony Liguori     type_register_static(&mv88w8618_flashcfg_info);
137539bffca2SAnthony Liguori     type_register_static(&mv88w8618_wlan_info);
137639bffca2SAnthony Liguori     type_register_static(&musicpal_lcd_info);
137739bffca2SAnthony Liguori     type_register_static(&musicpal_gpio_info);
137839bffca2SAnthony Liguori     type_register_static(&musicpal_key_info);
1379a86f200aSPeter Maydell     type_register_static(&musicpal_misc_info);
1380b47b50faSPaul Brook }
1381b47b50faSPaul Brook 
138283f7d43aSAndreas Färber type_init(musicpal_register_types)
1383