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