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" 13da34e65cSMarkus Armbruster #include "qapi/error.h" 144771d756SPaolo Bonzini #include "cpu.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" 199c17d615SPaolo Bonzini #include "sysemu/sysemu.h" 2083c9f4caSPaolo Bonzini #include "hw/boards.h" 210d09e41aSPaolo Bonzini #include "hw/char/serial.h" 22650d103dSMarkus Armbruster #include "hw/hw.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" 2964552b6bSMarkus Armbruster #include "hw/irq.h" 30*498661ddSPhilippe Mathieu-Daudé #include "hw/or-irq.h" 317ab14c5aSPhilippe Mathieu-Daudé #include "hw/audio/wm8750.h" 32fa1d36dfSMarkus Armbruster #include "sysemu/block-backend.h" 3354d31236SMarkus Armbruster #include "sysemu/runstate.h" 3479ed6fd6SPhilippe Mathieu-Daudé #include "sysemu/dma.h" 35022c62cbSPaolo Bonzini #include "exec/address-spaces.h" 3628ecbaeeSPaolo Bonzini #include "ui/pixel_ops.h" 373ed61312SIgor Mammedov #include "qemu/cutils.h" 38db1015e9SEduardo Habkost #include "qom/object.h" 3924859b68Sbalrog 40718ec0beSmalc #define MP_MISC_BASE 0x80002000 41718ec0beSmalc #define MP_MISC_SIZE 0x00001000 42718ec0beSmalc 4324859b68Sbalrog #define MP_ETH_BASE 0x80008000 4424859b68Sbalrog #define MP_ETH_SIZE 0x00001000 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 81*498661ddSPhilippe 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 /* Ethernet register offsets */ 9024859b68Sbalrog #define MP_ETH_SMIR 0x010 9124859b68Sbalrog #define MP_ETH_PCXR 0x408 9224859b68Sbalrog #define MP_ETH_SDCMR 0x448 9324859b68Sbalrog #define MP_ETH_ICR 0x450 9424859b68Sbalrog #define MP_ETH_IMR 0x458 9524859b68Sbalrog #define MP_ETH_FRDP0 0x480 9624859b68Sbalrog #define MP_ETH_FRDP1 0x484 9724859b68Sbalrog #define MP_ETH_FRDP2 0x488 9824859b68Sbalrog #define MP_ETH_FRDP3 0x48C 9924859b68Sbalrog #define MP_ETH_CRDP0 0x4A0 10024859b68Sbalrog #define MP_ETH_CRDP1 0x4A4 10124859b68Sbalrog #define MP_ETH_CRDP2 0x4A8 10224859b68Sbalrog #define MP_ETH_CRDP3 0x4AC 10324859b68Sbalrog #define MP_ETH_CTDP0 0x4E0 10424859b68Sbalrog #define MP_ETH_CTDP1 0x4E4 10524859b68Sbalrog 10624859b68Sbalrog /* MII PHY access */ 10724859b68Sbalrog #define MP_ETH_SMIR_DATA 0x0000FFFF 10824859b68Sbalrog #define MP_ETH_SMIR_ADDR 0x03FF0000 10924859b68Sbalrog #define MP_ETH_SMIR_OPCODE (1 << 26) /* Read value */ 11024859b68Sbalrog #define MP_ETH_SMIR_RDVALID (1 << 27) 11124859b68Sbalrog 11224859b68Sbalrog /* PHY registers */ 11324859b68Sbalrog #define MP_ETH_PHY1_BMSR 0x00210000 11424859b68Sbalrog #define MP_ETH_PHY1_PHYSID1 0x00410000 11524859b68Sbalrog #define MP_ETH_PHY1_PHYSID2 0x00610000 11624859b68Sbalrog 11724859b68Sbalrog #define MP_PHY_BMSR_LINK 0x0004 11824859b68Sbalrog #define MP_PHY_BMSR_AUTONEG 0x0008 11924859b68Sbalrog 12024859b68Sbalrog #define MP_PHY_88E3015 0x01410E20 12124859b68Sbalrog 12224859b68Sbalrog /* TX descriptor status */ 1232b194951SPeter Maydell #define MP_ETH_TX_OWN (1U << 31) 12424859b68Sbalrog 12524859b68Sbalrog /* RX descriptor status */ 1262b194951SPeter Maydell #define MP_ETH_RX_OWN (1U << 31) 12724859b68Sbalrog 12824859b68Sbalrog /* Interrupt cause/mask bits */ 12924859b68Sbalrog #define MP_ETH_IRQ_RX_BIT 0 13024859b68Sbalrog #define MP_ETH_IRQ_RX (1 << MP_ETH_IRQ_RX_BIT) 13124859b68Sbalrog #define MP_ETH_IRQ_TXHI_BIT 2 13224859b68Sbalrog #define MP_ETH_IRQ_TXLO_BIT 3 13324859b68Sbalrog 13424859b68Sbalrog /* Port config bits */ 13524859b68Sbalrog #define MP_ETH_PCXR_2BSM_BIT 28 /* 2-byte incoming suffix */ 13624859b68Sbalrog 13724859b68Sbalrog /* SDMA command bits */ 13824859b68Sbalrog #define MP_ETH_CMD_TXHI (1 << 23) 13924859b68Sbalrog #define MP_ETH_CMD_TXLO (1 << 22) 14024859b68Sbalrog 14124859b68Sbalrog typedef struct mv88w8618_tx_desc { 14224859b68Sbalrog uint32_t cmdstat; 14324859b68Sbalrog uint16_t res; 14424859b68Sbalrog uint16_t bytes; 14524859b68Sbalrog uint32_t buffer; 14624859b68Sbalrog uint32_t next; 14724859b68Sbalrog } mv88w8618_tx_desc; 14824859b68Sbalrog 14924859b68Sbalrog typedef struct mv88w8618_rx_desc { 15024859b68Sbalrog uint32_t cmdstat; 15124859b68Sbalrog uint16_t bytes; 15224859b68Sbalrog uint16_t buffer_size; 15324859b68Sbalrog uint32_t buffer; 15424859b68Sbalrog uint32_t next; 15524859b68Sbalrog } mv88w8618_rx_desc; 15624859b68Sbalrog 157a77d90e6SAndreas Färber #define TYPE_MV88W8618_ETH "mv88w8618_eth" 1588063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(mv88w8618_eth_state, MV88W8618_ETH) 159a77d90e6SAndreas Färber 160db1015e9SEduardo Habkost struct mv88w8618_eth_state { 161a77d90e6SAndreas Färber /*< private >*/ 162a77d90e6SAndreas Färber SysBusDevice parent_obj; 163a77d90e6SAndreas Färber /*< public >*/ 164a77d90e6SAndreas Färber 16519b4a424SAvi Kivity MemoryRegion iomem; 16624859b68Sbalrog qemu_irq irq; 16779ed6fd6SPhilippe Mathieu-Daudé MemoryRegion *dma_mr; 16879ed6fd6SPhilippe Mathieu-Daudé AddressSpace dma_as; 16924859b68Sbalrog uint32_t smir; 17024859b68Sbalrog uint32_t icr; 17124859b68Sbalrog uint32_t imr; 172b946a153Saliguori int mmio_index; 173d5b61dddSJan Kiszka uint32_t vlan_header; 174930c8682Spbrook uint32_t tx_queue[2]; 175930c8682Spbrook uint32_t rx_queue[4]; 176930c8682Spbrook uint32_t frx_queue[4]; 177930c8682Spbrook uint32_t cur_rx[4]; 1783a94dd18SMark McLoughlin NICState *nic; 1794c91cd28SGerd Hoffmann NICConf conf; 180db1015e9SEduardo Habkost }; 18124859b68Sbalrog 18279ed6fd6SPhilippe Mathieu-Daudé static void eth_rx_desc_put(AddressSpace *dma_as, uint32_t addr, 18379ed6fd6SPhilippe Mathieu-Daudé mv88w8618_rx_desc *desc) 184930c8682Spbrook { 185930c8682Spbrook cpu_to_le32s(&desc->cmdstat); 186930c8682Spbrook cpu_to_le16s(&desc->bytes); 187930c8682Spbrook cpu_to_le16s(&desc->buffer_size); 188930c8682Spbrook cpu_to_le32s(&desc->buffer); 189930c8682Spbrook cpu_to_le32s(&desc->next); 19079ed6fd6SPhilippe Mathieu-Daudé dma_memory_write(dma_as, addr, desc, sizeof(*desc)); 191930c8682Spbrook } 192930c8682Spbrook 19379ed6fd6SPhilippe Mathieu-Daudé static void eth_rx_desc_get(AddressSpace *dma_as, uint32_t addr, 19479ed6fd6SPhilippe Mathieu-Daudé mv88w8618_rx_desc *desc) 195930c8682Spbrook { 19679ed6fd6SPhilippe Mathieu-Daudé dma_memory_read(dma_as, addr, desc, sizeof(*desc)); 197930c8682Spbrook le32_to_cpus(&desc->cmdstat); 198930c8682Spbrook le16_to_cpus(&desc->bytes); 199930c8682Spbrook le16_to_cpus(&desc->buffer_size); 200930c8682Spbrook le32_to_cpus(&desc->buffer); 201930c8682Spbrook le32_to_cpus(&desc->next); 202930c8682Spbrook } 203930c8682Spbrook 2044e68f7a0SStefan Hajnoczi static ssize_t eth_receive(NetClientState *nc, const uint8_t *buf, size_t size) 20524859b68Sbalrog { 206cc1f0f45SJason Wang mv88w8618_eth_state *s = qemu_get_nic_opaque(nc); 207930c8682Spbrook uint32_t desc_addr; 208930c8682Spbrook mv88w8618_rx_desc desc; 20924859b68Sbalrog int i; 21024859b68Sbalrog 21124859b68Sbalrog for (i = 0; i < 4; i++) { 212930c8682Spbrook desc_addr = s->cur_rx[i]; 21349fedd0dSJan Kiszka if (!desc_addr) { 21424859b68Sbalrog continue; 21549fedd0dSJan Kiszka } 21624859b68Sbalrog do { 21779ed6fd6SPhilippe Mathieu-Daudé eth_rx_desc_get(&s->dma_as, desc_addr, &desc); 218930c8682Spbrook if ((desc.cmdstat & MP_ETH_RX_OWN) && desc.buffer_size >= size) { 21979ed6fd6SPhilippe Mathieu-Daudé dma_memory_write(&s->dma_as, desc.buffer + s->vlan_header, 22024859b68Sbalrog buf, size); 221930c8682Spbrook desc.bytes = size + s->vlan_header; 222930c8682Spbrook desc.cmdstat &= ~MP_ETH_RX_OWN; 223930c8682Spbrook s->cur_rx[i] = desc.next; 22424859b68Sbalrog 22524859b68Sbalrog s->icr |= MP_ETH_IRQ_RX; 22649fedd0dSJan Kiszka if (s->icr & s->imr) { 22724859b68Sbalrog qemu_irq_raise(s->irq); 22849fedd0dSJan Kiszka } 22979ed6fd6SPhilippe Mathieu-Daudé eth_rx_desc_put(&s->dma_as, desc_addr, &desc); 2304f1c942bSMark McLoughlin return size; 23124859b68Sbalrog } 232930c8682Spbrook desc_addr = desc.next; 233930c8682Spbrook } while (desc_addr != s->rx_queue[i]); 23424859b68Sbalrog } 2354f1c942bSMark McLoughlin return size; 23624859b68Sbalrog } 23724859b68Sbalrog 23879ed6fd6SPhilippe Mathieu-Daudé static void eth_tx_desc_put(AddressSpace *dma_as, uint32_t addr, 23979ed6fd6SPhilippe Mathieu-Daudé mv88w8618_tx_desc *desc) 240930c8682Spbrook { 241930c8682Spbrook cpu_to_le32s(&desc->cmdstat); 242930c8682Spbrook cpu_to_le16s(&desc->res); 243930c8682Spbrook cpu_to_le16s(&desc->bytes); 244930c8682Spbrook cpu_to_le32s(&desc->buffer); 245930c8682Spbrook cpu_to_le32s(&desc->next); 24679ed6fd6SPhilippe Mathieu-Daudé dma_memory_write(dma_as, addr, desc, sizeof(*desc)); 247930c8682Spbrook } 248930c8682Spbrook 24979ed6fd6SPhilippe Mathieu-Daudé static void eth_tx_desc_get(AddressSpace *dma_as, uint32_t addr, 25079ed6fd6SPhilippe Mathieu-Daudé mv88w8618_tx_desc *desc) 251930c8682Spbrook { 25279ed6fd6SPhilippe Mathieu-Daudé dma_memory_read(dma_as, addr, desc, sizeof(*desc)); 253930c8682Spbrook le32_to_cpus(&desc->cmdstat); 254930c8682Spbrook le16_to_cpus(&desc->res); 255930c8682Spbrook le16_to_cpus(&desc->bytes); 256930c8682Spbrook le32_to_cpus(&desc->buffer); 257930c8682Spbrook le32_to_cpus(&desc->next); 258930c8682Spbrook } 259930c8682Spbrook 26024859b68Sbalrog static void eth_send(mv88w8618_eth_state *s, int queue_index) 26124859b68Sbalrog { 262930c8682Spbrook uint32_t desc_addr = s->tx_queue[queue_index]; 263930c8682Spbrook mv88w8618_tx_desc desc; 26407b064e9SJan Kiszka uint32_t next_desc; 265930c8682Spbrook uint8_t buf[2048]; 266930c8682Spbrook int len; 267930c8682Spbrook 26824859b68Sbalrog do { 26979ed6fd6SPhilippe Mathieu-Daudé eth_tx_desc_get(&s->dma_as, desc_addr, &desc); 27007b064e9SJan Kiszka next_desc = desc.next; 271930c8682Spbrook if (desc.cmdstat & MP_ETH_TX_OWN) { 272930c8682Spbrook len = desc.bytes; 273930c8682Spbrook if (len < 2048) { 27479ed6fd6SPhilippe Mathieu-Daudé dma_memory_read(&s->dma_as, desc.buffer, buf, len); 275b356f76dSJason Wang qemu_send_packet(qemu_get_queue(s->nic), buf, len); 27624859b68Sbalrog } 277930c8682Spbrook desc.cmdstat &= ~MP_ETH_TX_OWN; 278930c8682Spbrook s->icr |= 1 << (MP_ETH_IRQ_TXLO_BIT - queue_index); 27979ed6fd6SPhilippe Mathieu-Daudé eth_tx_desc_put(&s->dma_as, desc_addr, &desc); 280930c8682Spbrook } 28107b064e9SJan Kiszka desc_addr = next_desc; 282930c8682Spbrook } while (desc_addr != s->tx_queue[queue_index]); 28324859b68Sbalrog } 28424859b68Sbalrog 285a8170e5eSAvi Kivity static uint64_t mv88w8618_eth_read(void *opaque, hwaddr offset, 28619b4a424SAvi Kivity unsigned size) 28724859b68Sbalrog { 28824859b68Sbalrog mv88w8618_eth_state *s = opaque; 28924859b68Sbalrog 29024859b68Sbalrog switch (offset) { 29124859b68Sbalrog case MP_ETH_SMIR: 29224859b68Sbalrog if (s->smir & MP_ETH_SMIR_OPCODE) { 29324859b68Sbalrog switch (s->smir & MP_ETH_SMIR_ADDR) { 29424859b68Sbalrog case MP_ETH_PHY1_BMSR: 29524859b68Sbalrog return MP_PHY_BMSR_LINK | MP_PHY_BMSR_AUTONEG | 29624859b68Sbalrog MP_ETH_SMIR_RDVALID; 29724859b68Sbalrog case MP_ETH_PHY1_PHYSID1: 29824859b68Sbalrog return (MP_PHY_88E3015 >> 16) | MP_ETH_SMIR_RDVALID; 29924859b68Sbalrog case MP_ETH_PHY1_PHYSID2: 30024859b68Sbalrog return (MP_PHY_88E3015 & 0xFFFF) | MP_ETH_SMIR_RDVALID; 30124859b68Sbalrog default: 30224859b68Sbalrog return MP_ETH_SMIR_RDVALID; 30324859b68Sbalrog } 30424859b68Sbalrog } 30524859b68Sbalrog return 0; 30624859b68Sbalrog 30724859b68Sbalrog case MP_ETH_ICR: 30824859b68Sbalrog return s->icr; 30924859b68Sbalrog 31024859b68Sbalrog case MP_ETH_IMR: 31124859b68Sbalrog return s->imr; 31224859b68Sbalrog 31324859b68Sbalrog case MP_ETH_FRDP0 ... MP_ETH_FRDP3: 314930c8682Spbrook return s->frx_queue[(offset - MP_ETH_FRDP0)/4]; 31524859b68Sbalrog 31624859b68Sbalrog case MP_ETH_CRDP0 ... MP_ETH_CRDP3: 317930c8682Spbrook return s->rx_queue[(offset - MP_ETH_CRDP0)/4]; 31824859b68Sbalrog 319cf143ad3SPeter Maydell case MP_ETH_CTDP0 ... MP_ETH_CTDP1: 320930c8682Spbrook return s->tx_queue[(offset - MP_ETH_CTDP0)/4]; 32124859b68Sbalrog 32224859b68Sbalrog default: 32324859b68Sbalrog return 0; 32424859b68Sbalrog } 32524859b68Sbalrog } 32624859b68Sbalrog 327a8170e5eSAvi Kivity static void mv88w8618_eth_write(void *opaque, hwaddr offset, 32819b4a424SAvi Kivity uint64_t value, unsigned size) 32924859b68Sbalrog { 33024859b68Sbalrog mv88w8618_eth_state *s = opaque; 33124859b68Sbalrog 33224859b68Sbalrog switch (offset) { 33324859b68Sbalrog case MP_ETH_SMIR: 33424859b68Sbalrog s->smir = value; 33524859b68Sbalrog break; 33624859b68Sbalrog 33724859b68Sbalrog case MP_ETH_PCXR: 33824859b68Sbalrog s->vlan_header = ((value >> MP_ETH_PCXR_2BSM_BIT) & 1) * 2; 33924859b68Sbalrog break; 34024859b68Sbalrog 34124859b68Sbalrog case MP_ETH_SDCMR: 34249fedd0dSJan Kiszka if (value & MP_ETH_CMD_TXHI) { 34324859b68Sbalrog eth_send(s, 1); 34449fedd0dSJan Kiszka } 34549fedd0dSJan Kiszka if (value & MP_ETH_CMD_TXLO) { 34624859b68Sbalrog eth_send(s, 0); 34749fedd0dSJan Kiszka } 34849fedd0dSJan Kiszka if (value & (MP_ETH_CMD_TXHI | MP_ETH_CMD_TXLO) && s->icr & s->imr) { 34924859b68Sbalrog qemu_irq_raise(s->irq); 35049fedd0dSJan Kiszka } 35124859b68Sbalrog break; 35224859b68Sbalrog 35324859b68Sbalrog case MP_ETH_ICR: 35424859b68Sbalrog s->icr &= value; 35524859b68Sbalrog break; 35624859b68Sbalrog 35724859b68Sbalrog case MP_ETH_IMR: 35824859b68Sbalrog s->imr = value; 35949fedd0dSJan Kiszka if (s->icr & s->imr) { 36024859b68Sbalrog qemu_irq_raise(s->irq); 36149fedd0dSJan Kiszka } 36224859b68Sbalrog break; 36324859b68Sbalrog 36424859b68Sbalrog case MP_ETH_FRDP0 ... MP_ETH_FRDP3: 365930c8682Spbrook s->frx_queue[(offset - MP_ETH_FRDP0)/4] = value; 36624859b68Sbalrog break; 36724859b68Sbalrog 36824859b68Sbalrog case MP_ETH_CRDP0 ... MP_ETH_CRDP3: 36924859b68Sbalrog s->rx_queue[(offset - MP_ETH_CRDP0)/4] = 370930c8682Spbrook s->cur_rx[(offset - MP_ETH_CRDP0)/4] = value; 37124859b68Sbalrog break; 37224859b68Sbalrog 373cf143ad3SPeter Maydell case MP_ETH_CTDP0 ... MP_ETH_CTDP1: 374930c8682Spbrook s->tx_queue[(offset - MP_ETH_CTDP0)/4] = value; 37524859b68Sbalrog break; 37624859b68Sbalrog } 37724859b68Sbalrog } 37824859b68Sbalrog 37919b4a424SAvi Kivity static const MemoryRegionOps mv88w8618_eth_ops = { 38019b4a424SAvi Kivity .read = mv88w8618_eth_read, 38119b4a424SAvi Kivity .write = mv88w8618_eth_write, 38219b4a424SAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 38324859b68Sbalrog }; 38424859b68Sbalrog 3854e68f7a0SStefan Hajnoczi static void eth_cleanup(NetClientState *nc) 386b946a153Saliguori { 387cc1f0f45SJason Wang mv88w8618_eth_state *s = qemu_get_nic_opaque(nc); 388b946a153Saliguori 3893a94dd18SMark McLoughlin s->nic = NULL; 390b946a153Saliguori } 391b946a153Saliguori 3923a94dd18SMark McLoughlin static NetClientInfo net_mv88w8618_info = { 393f394b2e2SEric Blake .type = NET_CLIENT_DRIVER_NIC, 3943a94dd18SMark McLoughlin .size = sizeof(NICState), 3953a94dd18SMark McLoughlin .receive = eth_receive, 3963a94dd18SMark McLoughlin .cleanup = eth_cleanup, 3973a94dd18SMark McLoughlin }; 3983a94dd18SMark McLoughlin 399ece71994Sxiaoqiang zhao static void mv88w8618_eth_init(Object *obj) 40024859b68Sbalrog { 401ece71994Sxiaoqiang zhao SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 402a77d90e6SAndreas Färber DeviceState *dev = DEVICE(sbd); 403a77d90e6SAndreas Färber mv88w8618_eth_state *s = MV88W8618_ETH(dev); 40424859b68Sbalrog 405a77d90e6SAndreas Färber sysbus_init_irq(sbd, &s->irq); 406ece71994Sxiaoqiang zhao memory_region_init_io(&s->iomem, obj, &mv88w8618_eth_ops, s, 40764bde0f3SPaolo Bonzini "mv88w8618-eth", MP_ETH_SIZE); 408a77d90e6SAndreas Färber sysbus_init_mmio(sbd, &s->iomem); 409ece71994Sxiaoqiang zhao } 410ece71994Sxiaoqiang zhao 411ece71994Sxiaoqiang zhao static void mv88w8618_eth_realize(DeviceState *dev, Error **errp) 412ece71994Sxiaoqiang zhao { 413ece71994Sxiaoqiang zhao mv88w8618_eth_state *s = MV88W8618_ETH(dev); 414ece71994Sxiaoqiang zhao 41579ed6fd6SPhilippe Mathieu-Daudé if (!s->dma_mr) { 41679ed6fd6SPhilippe Mathieu-Daudé error_setg(errp, TYPE_MV88W8618_ETH " 'dma-memory' link not set"); 41779ed6fd6SPhilippe Mathieu-Daudé return; 41879ed6fd6SPhilippe Mathieu-Daudé } 41979ed6fd6SPhilippe Mathieu-Daudé 42079ed6fd6SPhilippe Mathieu-Daudé address_space_init(&s->dma_as, s->dma_mr, "emac-dma"); 421ece71994Sxiaoqiang zhao s->nic = qemu_new_nic(&net_mv88w8618_info, &s->conf, 422ece71994Sxiaoqiang zhao object_get_typename(OBJECT(dev)), dev->id, s); 42324859b68Sbalrog } 42424859b68Sbalrog 425d5b61dddSJan Kiszka static const VMStateDescription mv88w8618_eth_vmsd = { 426d5b61dddSJan Kiszka .name = "mv88w8618_eth", 427d5b61dddSJan Kiszka .version_id = 1, 428d5b61dddSJan Kiszka .minimum_version_id = 1, 429d5b61dddSJan Kiszka .fields = (VMStateField[]) { 430d5b61dddSJan Kiszka VMSTATE_UINT32(smir, mv88w8618_eth_state), 431d5b61dddSJan Kiszka VMSTATE_UINT32(icr, mv88w8618_eth_state), 432d5b61dddSJan Kiszka VMSTATE_UINT32(imr, mv88w8618_eth_state), 433d5b61dddSJan Kiszka VMSTATE_UINT32(vlan_header, mv88w8618_eth_state), 434d5b61dddSJan Kiszka VMSTATE_UINT32_ARRAY(tx_queue, mv88w8618_eth_state, 2), 435d5b61dddSJan Kiszka VMSTATE_UINT32_ARRAY(rx_queue, mv88w8618_eth_state, 4), 436d5b61dddSJan Kiszka VMSTATE_UINT32_ARRAY(frx_queue, mv88w8618_eth_state, 4), 437d5b61dddSJan Kiszka VMSTATE_UINT32_ARRAY(cur_rx, mv88w8618_eth_state, 4), 438d5b61dddSJan Kiszka VMSTATE_END_OF_LIST() 439d5b61dddSJan Kiszka } 440d5b61dddSJan Kiszka }; 441d5b61dddSJan Kiszka 442999e12bbSAnthony Liguori static Property mv88w8618_eth_properties[] = { 4434c91cd28SGerd Hoffmann DEFINE_NIC_PROPERTIES(mv88w8618_eth_state, conf), 44479ed6fd6SPhilippe Mathieu-Daudé DEFINE_PROP_LINK("dma-memory", mv88w8618_eth_state, dma_mr, 44579ed6fd6SPhilippe Mathieu-Daudé TYPE_MEMORY_REGION, MemoryRegion *), 4464c91cd28SGerd Hoffmann DEFINE_PROP_END_OF_LIST(), 447999e12bbSAnthony Liguori }; 448999e12bbSAnthony Liguori 449999e12bbSAnthony Liguori static void mv88w8618_eth_class_init(ObjectClass *klass, void *data) 450999e12bbSAnthony Liguori { 45139bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 452999e12bbSAnthony Liguori 45339bffca2SAnthony Liguori dc->vmsd = &mv88w8618_eth_vmsd; 4544f67d30bSMarc-André Lureau device_class_set_props(dc, mv88w8618_eth_properties); 455ece71994Sxiaoqiang zhao dc->realize = mv88w8618_eth_realize; 456999e12bbSAnthony Liguori } 457999e12bbSAnthony Liguori 4588c43a6f0SAndreas Färber static const TypeInfo mv88w8618_eth_info = { 459a77d90e6SAndreas Färber .name = TYPE_MV88W8618_ETH, 46039bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 46139bffca2SAnthony Liguori .instance_size = sizeof(mv88w8618_eth_state), 462ece71994Sxiaoqiang zhao .instance_init = mv88w8618_eth_init, 463999e12bbSAnthony Liguori .class_init = mv88w8618_eth_class_init, 464d5b61dddSJan Kiszka }; 465d5b61dddSJan Kiszka 46624859b68Sbalrog /* LCD register offsets */ 46724859b68Sbalrog #define MP_LCD_IRQCTRL 0x180 46824859b68Sbalrog #define MP_LCD_IRQSTAT 0x184 46924859b68Sbalrog #define MP_LCD_SPICTRL 0x1ac 47024859b68Sbalrog #define MP_LCD_INST 0x1bc 47124859b68Sbalrog #define MP_LCD_DATA 0x1c0 47224859b68Sbalrog 47324859b68Sbalrog /* Mode magics */ 47424859b68Sbalrog #define MP_LCD_SPI_DATA 0x00100011 47524859b68Sbalrog #define MP_LCD_SPI_CMD 0x00104011 47624859b68Sbalrog #define MP_LCD_SPI_INVALID 0x00000000 47724859b68Sbalrog 47824859b68Sbalrog /* Commmands */ 47924859b68Sbalrog #define MP_LCD_INST_SETPAGE0 0xB0 48024859b68Sbalrog /* ... */ 48124859b68Sbalrog #define MP_LCD_INST_SETPAGE7 0xB7 48224859b68Sbalrog 48324859b68Sbalrog #define MP_LCD_TEXTCOLOR 0xe0e0ff /* RRGGBB */ 48424859b68Sbalrog 4852cca58fdSAndreas Färber #define TYPE_MUSICPAL_LCD "musicpal_lcd" 4868063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(musicpal_lcd_state, MUSICPAL_LCD) 4872cca58fdSAndreas Färber 488db1015e9SEduardo Habkost struct musicpal_lcd_state { 4892cca58fdSAndreas Färber /*< private >*/ 4902cca58fdSAndreas Färber SysBusDevice parent_obj; 4912cca58fdSAndreas Färber /*< public >*/ 4922cca58fdSAndreas Färber 49319b4a424SAvi Kivity MemoryRegion iomem; 494343ec8e4SBenoit Canet uint32_t brightness; 49524859b68Sbalrog uint32_t mode; 49624859b68Sbalrog uint32_t irqctrl; 497d5b61dddSJan Kiszka uint32_t page; 498d5b61dddSJan Kiszka uint32_t page_off; 499c78f7137SGerd Hoffmann QemuConsole *con; 50024859b68Sbalrog uint8_t video_ram[128*64/8]; 501db1015e9SEduardo Habkost }; 50224859b68Sbalrog 503343ec8e4SBenoit Canet static uint8_t scale_lcd_color(musicpal_lcd_state *s, uint8_t col) 50424859b68Sbalrog { 505343ec8e4SBenoit Canet switch (s->brightness) { 506343ec8e4SBenoit Canet case 7: 50724859b68Sbalrog return col; 508343ec8e4SBenoit Canet case 0: 509343ec8e4SBenoit Canet return 0; 510343ec8e4SBenoit Canet default: 511343ec8e4SBenoit Canet return (col * s->brightness) / 7; 51224859b68Sbalrog } 51324859b68Sbalrog } 51424859b68Sbalrog 5150266f2c7Sbalrog #define SET_LCD_PIXEL(depth, type) \ 5160266f2c7Sbalrog static inline void glue(set_lcd_pixel, depth) \ 5170266f2c7Sbalrog (musicpal_lcd_state *s, int x, int y, type col) \ 5180266f2c7Sbalrog { \ 5190266f2c7Sbalrog int dx, dy; \ 520c78f7137SGerd Hoffmann DisplaySurface *surface = qemu_console_surface(s->con); \ 521c78f7137SGerd Hoffmann type *pixel = &((type *) surface_data(surface))[(y * 128 * 3 + x) * 3]; \ 5220266f2c7Sbalrog \ 5230266f2c7Sbalrog for (dy = 0; dy < 3; dy++, pixel += 127 * 3) \ 5240266f2c7Sbalrog for (dx = 0; dx < 3; dx++, pixel++) \ 5250266f2c7Sbalrog *pixel = col; \ 5260266f2c7Sbalrog } 5270266f2c7Sbalrog SET_LCD_PIXEL(8, uint8_t) 5280266f2c7Sbalrog SET_LCD_PIXEL(16, uint16_t) 5290266f2c7Sbalrog SET_LCD_PIXEL(32, uint32_t) 53024859b68Sbalrog 53124859b68Sbalrog static void lcd_refresh(void *opaque) 53224859b68Sbalrog { 53324859b68Sbalrog musicpal_lcd_state *s = opaque; 534c78f7137SGerd Hoffmann DisplaySurface *surface = qemu_console_surface(s->con); 5350266f2c7Sbalrog int x, y, col; 53624859b68Sbalrog 537c78f7137SGerd Hoffmann switch (surface_bits_per_pixel(surface)) { 5380266f2c7Sbalrog case 0: 5390266f2c7Sbalrog return; 5400266f2c7Sbalrog #define LCD_REFRESH(depth, func) \ 5410266f2c7Sbalrog case depth: \ 542343ec8e4SBenoit Canet col = func(scale_lcd_color(s, (MP_LCD_TEXTCOLOR >> 16) & 0xff), \ 543343ec8e4SBenoit Canet scale_lcd_color(s, (MP_LCD_TEXTCOLOR >> 8) & 0xff), \ 544343ec8e4SBenoit Canet scale_lcd_color(s, MP_LCD_TEXTCOLOR & 0xff)); \ 54549fedd0dSJan Kiszka for (x = 0; x < 128; x++) { \ 54649fedd0dSJan Kiszka for (y = 0; y < 64; y++) { \ 54749fedd0dSJan Kiszka if (s->video_ram[x + (y/8)*128] & (1 << (y % 8))) { \ 5480266f2c7Sbalrog glue(set_lcd_pixel, depth)(s, x, y, col); \ 54949fedd0dSJan Kiszka } else { \ 5500266f2c7Sbalrog glue(set_lcd_pixel, depth)(s, x, y, 0); \ 55149fedd0dSJan Kiszka } \ 55249fedd0dSJan Kiszka } \ 55349fedd0dSJan Kiszka } \ 5540266f2c7Sbalrog break; 5550266f2c7Sbalrog LCD_REFRESH(8, rgb_to_pixel8) 5560266f2c7Sbalrog LCD_REFRESH(16, rgb_to_pixel16) 557c78f7137SGerd Hoffmann LCD_REFRESH(32, (is_surface_bgr(surface) ? 558bf9b48afSaliguori rgb_to_pixel32bgr : rgb_to_pixel32)) 5590266f2c7Sbalrog default: 5602ac71179SPaul Brook hw_error("unsupported colour depth %i\n", 561c78f7137SGerd Hoffmann surface_bits_per_pixel(surface)); 5620266f2c7Sbalrog } 56324859b68Sbalrog 564c78f7137SGerd Hoffmann dpy_gfx_update(s->con, 0, 0, 128*3, 64*3); 56524859b68Sbalrog } 56624859b68Sbalrog 567167bc3d2Sbalrog static void lcd_invalidate(void *opaque) 568167bc3d2Sbalrog { 569167bc3d2Sbalrog } 570167bc3d2Sbalrog 5712c79fed3SStefan Weil static void musicpal_lcd_gpio_brightness_in(void *opaque, int irq, int level) 572343ec8e4SBenoit Canet { 573243cd13cSJan Kiszka musicpal_lcd_state *s = opaque; 574343ec8e4SBenoit Canet s->brightness &= ~(1 << irq); 575343ec8e4SBenoit Canet s->brightness |= level << irq; 576343ec8e4SBenoit Canet } 577343ec8e4SBenoit Canet 578a8170e5eSAvi Kivity static uint64_t musicpal_lcd_read(void *opaque, hwaddr offset, 57919b4a424SAvi Kivity unsigned size) 58024859b68Sbalrog { 58124859b68Sbalrog musicpal_lcd_state *s = opaque; 58224859b68Sbalrog 58324859b68Sbalrog switch (offset) { 58424859b68Sbalrog case MP_LCD_IRQCTRL: 58524859b68Sbalrog return s->irqctrl; 58624859b68Sbalrog 58724859b68Sbalrog default: 58824859b68Sbalrog return 0; 58924859b68Sbalrog } 59024859b68Sbalrog } 59124859b68Sbalrog 592a8170e5eSAvi Kivity static void musicpal_lcd_write(void *opaque, hwaddr offset, 59319b4a424SAvi Kivity uint64_t value, unsigned size) 59424859b68Sbalrog { 59524859b68Sbalrog musicpal_lcd_state *s = opaque; 59624859b68Sbalrog 59724859b68Sbalrog switch (offset) { 59824859b68Sbalrog case MP_LCD_IRQCTRL: 59924859b68Sbalrog s->irqctrl = value; 60024859b68Sbalrog break; 60124859b68Sbalrog 60224859b68Sbalrog case MP_LCD_SPICTRL: 60349fedd0dSJan Kiszka if (value == MP_LCD_SPI_DATA || value == MP_LCD_SPI_CMD) { 60424859b68Sbalrog s->mode = value; 60549fedd0dSJan Kiszka } else { 60624859b68Sbalrog s->mode = MP_LCD_SPI_INVALID; 60749fedd0dSJan Kiszka } 60824859b68Sbalrog break; 60924859b68Sbalrog 61024859b68Sbalrog case MP_LCD_INST: 61124859b68Sbalrog if (value >= MP_LCD_INST_SETPAGE0 && value <= MP_LCD_INST_SETPAGE7) { 61224859b68Sbalrog s->page = value - MP_LCD_INST_SETPAGE0; 61324859b68Sbalrog s->page_off = 0; 61424859b68Sbalrog } 61524859b68Sbalrog break; 61624859b68Sbalrog 61724859b68Sbalrog case MP_LCD_DATA: 61824859b68Sbalrog if (s->mode == MP_LCD_SPI_CMD) { 61924859b68Sbalrog if (value >= MP_LCD_INST_SETPAGE0 && 62024859b68Sbalrog value <= MP_LCD_INST_SETPAGE7) { 62124859b68Sbalrog s->page = value - MP_LCD_INST_SETPAGE0; 62224859b68Sbalrog s->page_off = 0; 62324859b68Sbalrog } 62424859b68Sbalrog } else if (s->mode == MP_LCD_SPI_DATA) { 62524859b68Sbalrog s->video_ram[s->page*128 + s->page_off] = value; 62624859b68Sbalrog s->page_off = (s->page_off + 1) & 127; 62724859b68Sbalrog } 62824859b68Sbalrog break; 62924859b68Sbalrog } 63024859b68Sbalrog } 63124859b68Sbalrog 63219b4a424SAvi Kivity static const MemoryRegionOps musicpal_lcd_ops = { 63319b4a424SAvi Kivity .read = musicpal_lcd_read, 63419b4a424SAvi Kivity .write = musicpal_lcd_write, 63519b4a424SAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 63624859b68Sbalrog }; 63724859b68Sbalrog 638380cd056SGerd Hoffmann static const GraphicHwOps musicpal_gfx_ops = { 639380cd056SGerd Hoffmann .invalidate = lcd_invalidate, 640380cd056SGerd Hoffmann .gfx_update = lcd_refresh, 641380cd056SGerd Hoffmann }; 642380cd056SGerd Hoffmann 643ece71994Sxiaoqiang zhao static void musicpal_lcd_realize(DeviceState *dev, Error **errp) 64424859b68Sbalrog { 645ece71994Sxiaoqiang zhao musicpal_lcd_state *s = MUSICPAL_LCD(dev); 646ece71994Sxiaoqiang zhao s->con = graphic_console_init(dev, 0, &musicpal_gfx_ops, s); 647ece71994Sxiaoqiang zhao qemu_console_resize(s->con, 128 * 3, 64 * 3); 648ece71994Sxiaoqiang zhao } 649ece71994Sxiaoqiang zhao 650ece71994Sxiaoqiang zhao static void musicpal_lcd_init(Object *obj) 651ece71994Sxiaoqiang zhao { 652ece71994Sxiaoqiang zhao SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 6532cca58fdSAndreas Färber DeviceState *dev = DEVICE(sbd); 6542cca58fdSAndreas Färber musicpal_lcd_state *s = MUSICPAL_LCD(dev); 65524859b68Sbalrog 656343ec8e4SBenoit Canet s->brightness = 7; 657343ec8e4SBenoit Canet 658ece71994Sxiaoqiang zhao memory_region_init_io(&s->iomem, obj, &musicpal_lcd_ops, s, 65919b4a424SAvi Kivity "musicpal-lcd", MP_LCD_SIZE); 6602cca58fdSAndreas Färber sysbus_init_mmio(sbd, &s->iomem); 66124859b68Sbalrog 6622cca58fdSAndreas Färber qdev_init_gpio_in(dev, musicpal_lcd_gpio_brightness_in, 3); 66324859b68Sbalrog } 66424859b68Sbalrog 665d5b61dddSJan Kiszka static const VMStateDescription musicpal_lcd_vmsd = { 666d5b61dddSJan Kiszka .name = "musicpal_lcd", 667d5b61dddSJan Kiszka .version_id = 1, 668d5b61dddSJan Kiszka .minimum_version_id = 1, 669d5b61dddSJan Kiszka .fields = (VMStateField[]) { 670d5b61dddSJan Kiszka VMSTATE_UINT32(brightness, musicpal_lcd_state), 671d5b61dddSJan Kiszka VMSTATE_UINT32(mode, musicpal_lcd_state), 672d5b61dddSJan Kiszka VMSTATE_UINT32(irqctrl, musicpal_lcd_state), 673d5b61dddSJan Kiszka VMSTATE_UINT32(page, musicpal_lcd_state), 674d5b61dddSJan Kiszka VMSTATE_UINT32(page_off, musicpal_lcd_state), 675d5b61dddSJan Kiszka VMSTATE_BUFFER(video_ram, musicpal_lcd_state), 676d5b61dddSJan Kiszka VMSTATE_END_OF_LIST() 677d5b61dddSJan Kiszka } 678d5b61dddSJan Kiszka }; 679d5b61dddSJan Kiszka 680999e12bbSAnthony Liguori static void musicpal_lcd_class_init(ObjectClass *klass, void *data) 681999e12bbSAnthony Liguori { 68239bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 683999e12bbSAnthony Liguori 68439bffca2SAnthony Liguori dc->vmsd = &musicpal_lcd_vmsd; 685ece71994Sxiaoqiang zhao dc->realize = musicpal_lcd_realize; 686999e12bbSAnthony Liguori } 687999e12bbSAnthony Liguori 6888c43a6f0SAndreas Färber static const TypeInfo musicpal_lcd_info = { 6892cca58fdSAndreas Färber .name = TYPE_MUSICPAL_LCD, 69039bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 69139bffca2SAnthony Liguori .instance_size = sizeof(musicpal_lcd_state), 692ece71994Sxiaoqiang zhao .instance_init = musicpal_lcd_init, 693999e12bbSAnthony Liguori .class_init = musicpal_lcd_class_init, 694d5b61dddSJan Kiszka }; 695d5b61dddSJan Kiszka 69624859b68Sbalrog /* PIC register offsets */ 69724859b68Sbalrog #define MP_PIC_STATUS 0x00 69824859b68Sbalrog #define MP_PIC_ENABLE_SET 0x08 69924859b68Sbalrog #define MP_PIC_ENABLE_CLR 0x0C 70024859b68Sbalrog 701c7bd0fd9SAndreas Färber #define TYPE_MV88W8618_PIC "mv88w8618_pic" 7028063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(mv88w8618_pic_state, MV88W8618_PIC) 703c7bd0fd9SAndreas Färber 704db1015e9SEduardo Habkost struct mv88w8618_pic_state { 705c7bd0fd9SAndreas Färber /*< private >*/ 706c7bd0fd9SAndreas Färber SysBusDevice parent_obj; 707c7bd0fd9SAndreas Färber /*< public >*/ 708c7bd0fd9SAndreas Färber 70919b4a424SAvi Kivity MemoryRegion iomem; 71024859b68Sbalrog uint32_t level; 71124859b68Sbalrog uint32_t enabled; 71224859b68Sbalrog qemu_irq parent_irq; 713db1015e9SEduardo Habkost }; 71424859b68Sbalrog 71524859b68Sbalrog static void mv88w8618_pic_update(mv88w8618_pic_state *s) 71624859b68Sbalrog { 71724859b68Sbalrog qemu_set_irq(s->parent_irq, (s->level & s->enabled)); 71824859b68Sbalrog } 71924859b68Sbalrog 72024859b68Sbalrog static void mv88w8618_pic_set_irq(void *opaque, int irq, int level) 72124859b68Sbalrog { 72224859b68Sbalrog mv88w8618_pic_state *s = opaque; 72324859b68Sbalrog 72449fedd0dSJan Kiszka if (level) { 72524859b68Sbalrog s->level |= 1 << irq; 72649fedd0dSJan Kiszka } else { 72724859b68Sbalrog s->level &= ~(1 << irq); 72849fedd0dSJan Kiszka } 72924859b68Sbalrog mv88w8618_pic_update(s); 73024859b68Sbalrog } 73124859b68Sbalrog 732a8170e5eSAvi Kivity static uint64_t mv88w8618_pic_read(void *opaque, hwaddr offset, 73319b4a424SAvi Kivity unsigned size) 73424859b68Sbalrog { 73524859b68Sbalrog mv88w8618_pic_state *s = opaque; 73624859b68Sbalrog 73724859b68Sbalrog switch (offset) { 73824859b68Sbalrog case MP_PIC_STATUS: 73924859b68Sbalrog return s->level & s->enabled; 74024859b68Sbalrog 74124859b68Sbalrog default: 74224859b68Sbalrog return 0; 74324859b68Sbalrog } 74424859b68Sbalrog } 74524859b68Sbalrog 746a8170e5eSAvi Kivity static void mv88w8618_pic_write(void *opaque, hwaddr offset, 74719b4a424SAvi Kivity uint64_t value, unsigned size) 74824859b68Sbalrog { 74924859b68Sbalrog mv88w8618_pic_state *s = opaque; 75024859b68Sbalrog 75124859b68Sbalrog switch (offset) { 75224859b68Sbalrog case MP_PIC_ENABLE_SET: 75324859b68Sbalrog s->enabled |= value; 75424859b68Sbalrog break; 75524859b68Sbalrog 75624859b68Sbalrog case MP_PIC_ENABLE_CLR: 75724859b68Sbalrog s->enabled &= ~value; 75824859b68Sbalrog s->level &= ~value; 75924859b68Sbalrog break; 76024859b68Sbalrog } 76124859b68Sbalrog mv88w8618_pic_update(s); 76224859b68Sbalrog } 76324859b68Sbalrog 764d5b61dddSJan Kiszka static void mv88w8618_pic_reset(DeviceState *d) 76524859b68Sbalrog { 766c7bd0fd9SAndreas Färber mv88w8618_pic_state *s = MV88W8618_PIC(d); 76724859b68Sbalrog 76824859b68Sbalrog s->level = 0; 76924859b68Sbalrog s->enabled = 0; 77024859b68Sbalrog } 77124859b68Sbalrog 77219b4a424SAvi Kivity static const MemoryRegionOps mv88w8618_pic_ops = { 77319b4a424SAvi Kivity .read = mv88w8618_pic_read, 77419b4a424SAvi Kivity .write = mv88w8618_pic_write, 77519b4a424SAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 77624859b68Sbalrog }; 77724859b68Sbalrog 778ece71994Sxiaoqiang zhao static void mv88w8618_pic_init(Object *obj) 77924859b68Sbalrog { 780ece71994Sxiaoqiang zhao SysBusDevice *dev = SYS_BUS_DEVICE(obj); 781c7bd0fd9SAndreas Färber mv88w8618_pic_state *s = MV88W8618_PIC(dev); 78224859b68Sbalrog 783c7bd0fd9SAndreas Färber qdev_init_gpio_in(DEVICE(dev), mv88w8618_pic_set_irq, 32); 784b47b50faSPaul Brook sysbus_init_irq(dev, &s->parent_irq); 785ece71994Sxiaoqiang zhao memory_region_init_io(&s->iomem, obj, &mv88w8618_pic_ops, s, 78619b4a424SAvi Kivity "musicpal-pic", MP_PIC_SIZE); 787750ecd44SAvi Kivity sysbus_init_mmio(dev, &s->iomem); 78824859b68Sbalrog } 78924859b68Sbalrog 790d5b61dddSJan Kiszka static const VMStateDescription mv88w8618_pic_vmsd = { 791d5b61dddSJan Kiszka .name = "mv88w8618_pic", 792d5b61dddSJan Kiszka .version_id = 1, 793d5b61dddSJan Kiszka .minimum_version_id = 1, 794d5b61dddSJan Kiszka .fields = (VMStateField[]) { 795d5b61dddSJan Kiszka VMSTATE_UINT32(level, mv88w8618_pic_state), 796d5b61dddSJan Kiszka VMSTATE_UINT32(enabled, mv88w8618_pic_state), 797d5b61dddSJan Kiszka VMSTATE_END_OF_LIST() 798d5b61dddSJan Kiszka } 799d5b61dddSJan Kiszka }; 800d5b61dddSJan Kiszka 801999e12bbSAnthony Liguori static void mv88w8618_pic_class_init(ObjectClass *klass, void *data) 802999e12bbSAnthony Liguori { 80339bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 804999e12bbSAnthony Liguori 80539bffca2SAnthony Liguori dc->reset = mv88w8618_pic_reset; 80639bffca2SAnthony Liguori dc->vmsd = &mv88w8618_pic_vmsd; 807999e12bbSAnthony Liguori } 808999e12bbSAnthony Liguori 8098c43a6f0SAndreas Färber static const TypeInfo mv88w8618_pic_info = { 810c7bd0fd9SAndreas Färber .name = TYPE_MV88W8618_PIC, 81139bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 81239bffca2SAnthony Liguori .instance_size = sizeof(mv88w8618_pic_state), 813ece71994Sxiaoqiang zhao .instance_init = mv88w8618_pic_init, 814999e12bbSAnthony Liguori .class_init = mv88w8618_pic_class_init, 815d5b61dddSJan Kiszka }; 816d5b61dddSJan Kiszka 81724859b68Sbalrog /* PIT register offsets */ 81824859b68Sbalrog #define MP_PIT_TIMER1_LENGTH 0x00 81924859b68Sbalrog /* ... */ 82024859b68Sbalrog #define MP_PIT_TIMER4_LENGTH 0x0C 82124859b68Sbalrog #define MP_PIT_CONTROL 0x10 82224859b68Sbalrog #define MP_PIT_TIMER1_VALUE 0x14 82324859b68Sbalrog /* ... */ 82424859b68Sbalrog #define MP_PIT_TIMER4_VALUE 0x20 82524859b68Sbalrog #define MP_BOARD_RESET 0x34 82624859b68Sbalrog 82724859b68Sbalrog /* Magic board reset value (probably some watchdog behind it) */ 82824859b68Sbalrog #define MP_BOARD_RESET_MAGIC 0x10000 82924859b68Sbalrog 83024859b68Sbalrog typedef struct mv88w8618_timer_state { 831b47b50faSPaul Brook ptimer_state *ptimer; 83224859b68Sbalrog uint32_t limit; 83324859b68Sbalrog int freq; 83424859b68Sbalrog qemu_irq irq; 83524859b68Sbalrog } mv88w8618_timer_state; 83624859b68Sbalrog 8374adc8541SAndreas Färber #define TYPE_MV88W8618_PIT "mv88w8618_pit" 8388063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(mv88w8618_pit_state, MV88W8618_PIT) 8394adc8541SAndreas Färber 840db1015e9SEduardo Habkost struct mv88w8618_pit_state { 8414adc8541SAndreas Färber /*< private >*/ 8424adc8541SAndreas Färber SysBusDevice parent_obj; 8434adc8541SAndreas Färber /*< public >*/ 8444adc8541SAndreas Färber 84519b4a424SAvi Kivity MemoryRegion iomem; 846b47b50faSPaul Brook mv88w8618_timer_state timer[4]; 847db1015e9SEduardo Habkost }; 84824859b68Sbalrog 84924859b68Sbalrog static void mv88w8618_timer_tick(void *opaque) 85024859b68Sbalrog { 85124859b68Sbalrog mv88w8618_timer_state *s = opaque; 85224859b68Sbalrog 85324859b68Sbalrog qemu_irq_raise(s->irq); 85424859b68Sbalrog } 85524859b68Sbalrog 856b47b50faSPaul Brook static void mv88w8618_timer_init(SysBusDevice *dev, mv88w8618_timer_state *s, 857b47b50faSPaul Brook uint32_t freq) 85824859b68Sbalrog { 859b47b50faSPaul Brook sysbus_init_irq(dev, &s->irq); 86024859b68Sbalrog s->freq = freq; 86124859b68Sbalrog 862d8052a2eSPeter Maydell s->ptimer = ptimer_init(mv88w8618_timer_tick, s, PTIMER_POLICY_DEFAULT); 86324859b68Sbalrog } 86424859b68Sbalrog 865a8170e5eSAvi Kivity static uint64_t mv88w8618_pit_read(void *opaque, hwaddr offset, 86619b4a424SAvi Kivity unsigned size) 86724859b68Sbalrog { 86824859b68Sbalrog mv88w8618_pit_state *s = opaque; 86924859b68Sbalrog mv88w8618_timer_state *t; 87024859b68Sbalrog 87124859b68Sbalrog switch (offset) { 87224859b68Sbalrog case MP_PIT_TIMER1_VALUE ... MP_PIT_TIMER4_VALUE: 873b47b50faSPaul Brook t = &s->timer[(offset-MP_PIT_TIMER1_VALUE) >> 2]; 874b47b50faSPaul Brook return ptimer_get_count(t->ptimer); 87524859b68Sbalrog 87624859b68Sbalrog default: 87724859b68Sbalrog return 0; 87824859b68Sbalrog } 87924859b68Sbalrog } 88024859b68Sbalrog 881a8170e5eSAvi Kivity static void mv88w8618_pit_write(void *opaque, hwaddr offset, 88219b4a424SAvi Kivity uint64_t value, unsigned size) 88324859b68Sbalrog { 88424859b68Sbalrog mv88w8618_pit_state *s = opaque; 88524859b68Sbalrog mv88w8618_timer_state *t; 88624859b68Sbalrog int i; 88724859b68Sbalrog 88824859b68Sbalrog switch (offset) { 88924859b68Sbalrog case MP_PIT_TIMER1_LENGTH ... MP_PIT_TIMER4_LENGTH: 890b47b50faSPaul Brook t = &s->timer[offset >> 2]; 89124859b68Sbalrog t->limit = value; 892d8052a2eSPeter Maydell ptimer_transaction_begin(t->ptimer); 893c88d6bdeSJan Kiszka if (t->limit > 0) { 894b47b50faSPaul Brook ptimer_set_limit(t->ptimer, t->limit, 1); 895c88d6bdeSJan Kiszka } else { 896c88d6bdeSJan Kiszka ptimer_stop(t->ptimer); 897c88d6bdeSJan Kiszka } 898d8052a2eSPeter Maydell ptimer_transaction_commit(t->ptimer); 89924859b68Sbalrog break; 90024859b68Sbalrog 90124859b68Sbalrog case MP_PIT_CONTROL: 90224859b68Sbalrog for (i = 0; i < 4; i++) { 903b47b50faSPaul Brook t = &s->timer[i]; 904d8052a2eSPeter Maydell ptimer_transaction_begin(t->ptimer); 905c88d6bdeSJan Kiszka if (value & 0xf && t->limit > 0) { 906b47b50faSPaul Brook ptimer_set_limit(t->ptimer, t->limit, 0); 907b47b50faSPaul Brook ptimer_set_freq(t->ptimer, t->freq); 908b47b50faSPaul Brook ptimer_run(t->ptimer, 0); 909c88d6bdeSJan Kiszka } else { 910c88d6bdeSJan Kiszka ptimer_stop(t->ptimer); 91124859b68Sbalrog } 912d8052a2eSPeter Maydell ptimer_transaction_commit(t->ptimer); 91324859b68Sbalrog value >>= 4; 91424859b68Sbalrog } 91524859b68Sbalrog break; 91624859b68Sbalrog 91724859b68Sbalrog case MP_BOARD_RESET: 91849fedd0dSJan Kiszka if (value == MP_BOARD_RESET_MAGIC) { 919cf83f140SEric Blake qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); 92049fedd0dSJan Kiszka } 92124859b68Sbalrog break; 92224859b68Sbalrog } 92324859b68Sbalrog } 92424859b68Sbalrog 925d5b61dddSJan Kiszka static void mv88w8618_pit_reset(DeviceState *d) 926c88d6bdeSJan Kiszka { 9274adc8541SAndreas Färber mv88w8618_pit_state *s = MV88W8618_PIT(d); 928c88d6bdeSJan Kiszka int i; 929c88d6bdeSJan Kiszka 930c88d6bdeSJan Kiszka for (i = 0; i < 4; i++) { 931d8052a2eSPeter Maydell mv88w8618_timer_state *t = &s->timer[i]; 932d8052a2eSPeter Maydell ptimer_transaction_begin(t->ptimer); 933d8052a2eSPeter Maydell ptimer_stop(t->ptimer); 934d8052a2eSPeter Maydell ptimer_transaction_commit(t->ptimer); 935d8052a2eSPeter Maydell t->limit = 0; 936c88d6bdeSJan Kiszka } 937c88d6bdeSJan Kiszka } 938c88d6bdeSJan Kiszka 93919b4a424SAvi Kivity static const MemoryRegionOps mv88w8618_pit_ops = { 94019b4a424SAvi Kivity .read = mv88w8618_pit_read, 94119b4a424SAvi Kivity .write = mv88w8618_pit_write, 94219b4a424SAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 94324859b68Sbalrog }; 94424859b68Sbalrog 945ece71994Sxiaoqiang zhao static void mv88w8618_pit_init(Object *obj) 94624859b68Sbalrog { 947ece71994Sxiaoqiang zhao SysBusDevice *dev = SYS_BUS_DEVICE(obj); 9484adc8541SAndreas Färber mv88w8618_pit_state *s = MV88W8618_PIT(dev); 949b47b50faSPaul Brook int i; 95024859b68Sbalrog 95124859b68Sbalrog /* Letting them all run at 1 MHz is likely just a pragmatic 95224859b68Sbalrog * simplification. */ 953b47b50faSPaul Brook for (i = 0; i < 4; i++) { 954b47b50faSPaul Brook mv88w8618_timer_init(dev, &s->timer[i], 1000000); 955b47b50faSPaul Brook } 95624859b68Sbalrog 957ece71994Sxiaoqiang zhao memory_region_init_io(&s->iomem, obj, &mv88w8618_pit_ops, s, 95819b4a424SAvi Kivity "musicpal-pit", MP_PIT_SIZE); 959750ecd44SAvi Kivity sysbus_init_mmio(dev, &s->iomem); 96024859b68Sbalrog } 96124859b68Sbalrog 962d5b61dddSJan Kiszka static const VMStateDescription mv88w8618_timer_vmsd = { 963d5b61dddSJan Kiszka .name = "timer", 964d5b61dddSJan Kiszka .version_id = 1, 965d5b61dddSJan Kiszka .minimum_version_id = 1, 966d5b61dddSJan Kiszka .fields = (VMStateField[]) { 967d5b61dddSJan Kiszka VMSTATE_PTIMER(ptimer, mv88w8618_timer_state), 968d5b61dddSJan Kiszka VMSTATE_UINT32(limit, mv88w8618_timer_state), 969d5b61dddSJan Kiszka VMSTATE_END_OF_LIST() 970d5b61dddSJan Kiszka } 971d5b61dddSJan Kiszka }; 972d5b61dddSJan Kiszka 973d5b61dddSJan Kiszka static const VMStateDescription mv88w8618_pit_vmsd = { 974d5b61dddSJan Kiszka .name = "mv88w8618_pit", 975d5b61dddSJan Kiszka .version_id = 1, 976d5b61dddSJan Kiszka .minimum_version_id = 1, 977d5b61dddSJan Kiszka .fields = (VMStateField[]) { 978d5b61dddSJan Kiszka VMSTATE_STRUCT_ARRAY(timer, mv88w8618_pit_state, 4, 1, 979d5b61dddSJan Kiszka mv88w8618_timer_vmsd, mv88w8618_timer_state), 980d5b61dddSJan Kiszka VMSTATE_END_OF_LIST() 981d5b61dddSJan Kiszka } 982d5b61dddSJan Kiszka }; 983d5b61dddSJan Kiszka 984999e12bbSAnthony Liguori static void mv88w8618_pit_class_init(ObjectClass *klass, void *data) 985999e12bbSAnthony Liguori { 98639bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 987999e12bbSAnthony Liguori 98839bffca2SAnthony Liguori dc->reset = mv88w8618_pit_reset; 98939bffca2SAnthony Liguori dc->vmsd = &mv88w8618_pit_vmsd; 990999e12bbSAnthony Liguori } 991999e12bbSAnthony Liguori 9928c43a6f0SAndreas Färber static const TypeInfo mv88w8618_pit_info = { 9934adc8541SAndreas Färber .name = TYPE_MV88W8618_PIT, 99439bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 99539bffca2SAnthony Liguori .instance_size = sizeof(mv88w8618_pit_state), 996ece71994Sxiaoqiang zhao .instance_init = mv88w8618_pit_init, 997999e12bbSAnthony Liguori .class_init = mv88w8618_pit_class_init, 998c88d6bdeSJan Kiszka }; 999c88d6bdeSJan Kiszka 100024859b68Sbalrog /* Flash config register offsets */ 100124859b68Sbalrog #define MP_FLASHCFG_CFGR0 0x04 100224859b68Sbalrog 10035952b01cSAndreas Färber #define TYPE_MV88W8618_FLASHCFG "mv88w8618_flashcfg" 10048063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(mv88w8618_flashcfg_state, MV88W8618_FLASHCFG) 10055952b01cSAndreas Färber 1006db1015e9SEduardo Habkost struct mv88w8618_flashcfg_state { 10075952b01cSAndreas Färber /*< private >*/ 10085952b01cSAndreas Färber SysBusDevice parent_obj; 10095952b01cSAndreas Färber /*< public >*/ 10105952b01cSAndreas Färber 101119b4a424SAvi Kivity MemoryRegion iomem; 101224859b68Sbalrog uint32_t cfgr0; 1013db1015e9SEduardo Habkost }; 101424859b68Sbalrog 101519b4a424SAvi Kivity static uint64_t mv88w8618_flashcfg_read(void *opaque, 1016a8170e5eSAvi Kivity hwaddr offset, 101719b4a424SAvi Kivity unsigned size) 101824859b68Sbalrog { 101924859b68Sbalrog mv88w8618_flashcfg_state *s = opaque; 102024859b68Sbalrog 102124859b68Sbalrog switch (offset) { 102224859b68Sbalrog case MP_FLASHCFG_CFGR0: 102324859b68Sbalrog return s->cfgr0; 102424859b68Sbalrog 102524859b68Sbalrog default: 102624859b68Sbalrog return 0; 102724859b68Sbalrog } 102824859b68Sbalrog } 102924859b68Sbalrog 1030a8170e5eSAvi Kivity static void mv88w8618_flashcfg_write(void *opaque, hwaddr offset, 103119b4a424SAvi Kivity uint64_t value, unsigned size) 103224859b68Sbalrog { 103324859b68Sbalrog mv88w8618_flashcfg_state *s = opaque; 103424859b68Sbalrog 103524859b68Sbalrog switch (offset) { 103624859b68Sbalrog case MP_FLASHCFG_CFGR0: 103724859b68Sbalrog s->cfgr0 = value; 103824859b68Sbalrog break; 103924859b68Sbalrog } 104024859b68Sbalrog } 104124859b68Sbalrog 104219b4a424SAvi Kivity static const MemoryRegionOps mv88w8618_flashcfg_ops = { 104319b4a424SAvi Kivity .read = mv88w8618_flashcfg_read, 104419b4a424SAvi Kivity .write = mv88w8618_flashcfg_write, 104519b4a424SAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 104624859b68Sbalrog }; 104724859b68Sbalrog 1048ece71994Sxiaoqiang zhao static void mv88w8618_flashcfg_init(Object *obj) 104924859b68Sbalrog { 1050ece71994Sxiaoqiang zhao SysBusDevice *dev = SYS_BUS_DEVICE(obj); 10515952b01cSAndreas Färber mv88w8618_flashcfg_state *s = MV88W8618_FLASHCFG(dev); 105224859b68Sbalrog 105324859b68Sbalrog s->cfgr0 = 0xfffe4285; /* Default as set by U-Boot for 8 MB flash */ 1054ece71994Sxiaoqiang zhao memory_region_init_io(&s->iomem, obj, &mv88w8618_flashcfg_ops, s, 105519b4a424SAvi Kivity "musicpal-flashcfg", MP_FLASHCFG_SIZE); 1056750ecd44SAvi Kivity sysbus_init_mmio(dev, &s->iomem); 105724859b68Sbalrog } 105824859b68Sbalrog 1059d5b61dddSJan Kiszka static const VMStateDescription mv88w8618_flashcfg_vmsd = { 1060d5b61dddSJan Kiszka .name = "mv88w8618_flashcfg", 1061d5b61dddSJan Kiszka .version_id = 1, 1062d5b61dddSJan Kiszka .minimum_version_id = 1, 1063d5b61dddSJan Kiszka .fields = (VMStateField[]) { 1064d5b61dddSJan Kiszka VMSTATE_UINT32(cfgr0, mv88w8618_flashcfg_state), 1065d5b61dddSJan Kiszka VMSTATE_END_OF_LIST() 1066d5b61dddSJan Kiszka } 1067d5b61dddSJan Kiszka }; 1068d5b61dddSJan Kiszka 1069999e12bbSAnthony Liguori static void mv88w8618_flashcfg_class_init(ObjectClass *klass, void *data) 1070999e12bbSAnthony Liguori { 107139bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 1072999e12bbSAnthony Liguori 107339bffca2SAnthony Liguori dc->vmsd = &mv88w8618_flashcfg_vmsd; 1074999e12bbSAnthony Liguori } 1075999e12bbSAnthony Liguori 10768c43a6f0SAndreas Färber static const TypeInfo mv88w8618_flashcfg_info = { 10775952b01cSAndreas Färber .name = TYPE_MV88W8618_FLASHCFG, 107839bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 107939bffca2SAnthony Liguori .instance_size = sizeof(mv88w8618_flashcfg_state), 1080ece71994Sxiaoqiang zhao .instance_init = mv88w8618_flashcfg_init, 1081999e12bbSAnthony Liguori .class_init = mv88w8618_flashcfg_class_init, 1082d5b61dddSJan Kiszka }; 1083d5b61dddSJan Kiszka 1084718ec0beSmalc /* Misc register offsets */ 1085718ec0beSmalc #define MP_MISC_BOARD_REVISION 0x18 108624859b68Sbalrog 1087718ec0beSmalc #define MP_BOARD_REVISION 0x31 108824859b68Sbalrog 1089db1015e9SEduardo Habkost struct MusicPalMiscState { 1090a86f200aSPeter Maydell SysBusDevice parent_obj; 1091a86f200aSPeter Maydell MemoryRegion iomem; 1092db1015e9SEduardo Habkost }; 1093a86f200aSPeter Maydell 1094a86f200aSPeter Maydell #define TYPE_MUSICPAL_MISC "musicpal-misc" 10958063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(MusicPalMiscState, MUSICPAL_MISC) 1096a86f200aSPeter Maydell 1097a8170e5eSAvi Kivity static uint64_t musicpal_misc_read(void *opaque, hwaddr offset, 109819b4a424SAvi Kivity unsigned size) 1099718ec0beSmalc { 1100718ec0beSmalc switch (offset) { 1101718ec0beSmalc case MP_MISC_BOARD_REVISION: 1102718ec0beSmalc return MP_BOARD_REVISION; 1103718ec0beSmalc 1104718ec0beSmalc default: 1105718ec0beSmalc return 0; 1106718ec0beSmalc } 1107718ec0beSmalc } 1108718ec0beSmalc 1109a8170e5eSAvi Kivity static void musicpal_misc_write(void *opaque, hwaddr offset, 111019b4a424SAvi Kivity uint64_t value, unsigned size) 1111718ec0beSmalc { 1112718ec0beSmalc } 1113718ec0beSmalc 111419b4a424SAvi Kivity static const MemoryRegionOps musicpal_misc_ops = { 111519b4a424SAvi Kivity .read = musicpal_misc_read, 111619b4a424SAvi Kivity .write = musicpal_misc_write, 111719b4a424SAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 1118718ec0beSmalc }; 1119718ec0beSmalc 1120a86f200aSPeter Maydell static void musicpal_misc_init(Object *obj) 1121718ec0beSmalc { 1122a86f200aSPeter Maydell SysBusDevice *sd = SYS_BUS_DEVICE(obj); 1123a86f200aSPeter Maydell MusicPalMiscState *s = MUSICPAL_MISC(obj); 1124718ec0beSmalc 112564bde0f3SPaolo Bonzini memory_region_init_io(&s->iomem, OBJECT(s), &musicpal_misc_ops, NULL, 112619b4a424SAvi Kivity "musicpal-misc", MP_MISC_SIZE); 1127a86f200aSPeter Maydell sysbus_init_mmio(sd, &s->iomem); 1128718ec0beSmalc } 1129718ec0beSmalc 1130a86f200aSPeter Maydell static const TypeInfo musicpal_misc_info = { 1131a86f200aSPeter Maydell .name = TYPE_MUSICPAL_MISC, 1132a86f200aSPeter Maydell .parent = TYPE_SYS_BUS_DEVICE, 1133a86f200aSPeter Maydell .instance_init = musicpal_misc_init, 1134a86f200aSPeter Maydell .instance_size = sizeof(MusicPalMiscState), 1135a86f200aSPeter Maydell }; 1136a86f200aSPeter Maydell 1137718ec0beSmalc /* WLAN register offsets */ 1138718ec0beSmalc #define MP_WLAN_MAGIC1 0x11c 1139718ec0beSmalc #define MP_WLAN_MAGIC2 0x124 1140718ec0beSmalc 1141a8170e5eSAvi Kivity static uint64_t mv88w8618_wlan_read(void *opaque, hwaddr offset, 114219b4a424SAvi Kivity unsigned size) 1143718ec0beSmalc { 1144718ec0beSmalc switch (offset) { 1145718ec0beSmalc /* Workaround to allow loading the binary-only wlandrv.ko crap 1146718ec0beSmalc * from the original Freecom firmware. */ 1147718ec0beSmalc case MP_WLAN_MAGIC1: 1148718ec0beSmalc return ~3; 1149718ec0beSmalc case MP_WLAN_MAGIC2: 1150718ec0beSmalc return -1; 1151718ec0beSmalc 1152718ec0beSmalc default: 1153718ec0beSmalc return 0; 1154718ec0beSmalc } 1155718ec0beSmalc } 1156718ec0beSmalc 1157a8170e5eSAvi Kivity static void mv88w8618_wlan_write(void *opaque, hwaddr offset, 115819b4a424SAvi Kivity uint64_t value, unsigned size) 1159718ec0beSmalc { 1160718ec0beSmalc } 1161718ec0beSmalc 116219b4a424SAvi Kivity static const MemoryRegionOps mv88w8618_wlan_ops = { 116319b4a424SAvi Kivity .read = mv88w8618_wlan_read, 116419b4a424SAvi Kivity .write =mv88w8618_wlan_write, 116519b4a424SAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 1166718ec0beSmalc }; 1167718ec0beSmalc 11687f7420a0SMao Zhongyi static void mv88w8618_wlan_realize(DeviceState *dev, Error **errp) 1169718ec0beSmalc { 117019b4a424SAvi Kivity MemoryRegion *iomem = g_new(MemoryRegion, 1); 1171718ec0beSmalc 117264bde0f3SPaolo Bonzini memory_region_init_io(iomem, OBJECT(dev), &mv88w8618_wlan_ops, NULL, 117319b4a424SAvi Kivity "musicpal-wlan", MP_WLAN_SIZE); 11747f7420a0SMao Zhongyi sysbus_init_mmio(SYS_BUS_DEVICE(dev), iomem); 1175718ec0beSmalc } 1176718ec0beSmalc 1177718ec0beSmalc /* GPIO register offsets */ 1178718ec0beSmalc #define MP_GPIO_OE_LO 0x008 1179718ec0beSmalc #define MP_GPIO_OUT_LO 0x00c 1180718ec0beSmalc #define MP_GPIO_IN_LO 0x010 1181708afdf3SJan Kiszka #define MP_GPIO_IER_LO 0x014 1182708afdf3SJan Kiszka #define MP_GPIO_IMR_LO 0x018 1183718ec0beSmalc #define MP_GPIO_ISR_LO 0x020 1184718ec0beSmalc #define MP_GPIO_OE_HI 0x508 1185718ec0beSmalc #define MP_GPIO_OUT_HI 0x50c 1186718ec0beSmalc #define MP_GPIO_IN_HI 0x510 1187708afdf3SJan Kiszka #define MP_GPIO_IER_HI 0x514 1188708afdf3SJan Kiszka #define MP_GPIO_IMR_HI 0x518 1189718ec0beSmalc #define MP_GPIO_ISR_HI 0x520 119024859b68Sbalrog 119124859b68Sbalrog /* GPIO bits & masks */ 119224859b68Sbalrog #define MP_GPIO_LCD_BRIGHTNESS 0x00070000 119324859b68Sbalrog #define MP_GPIO_I2C_DATA_BIT 29 119424859b68Sbalrog #define MP_GPIO_I2C_CLOCK_BIT 30 119524859b68Sbalrog 119624859b68Sbalrog /* LCD brightness bits in GPIO_OE_HI */ 119724859b68Sbalrog #define MP_OE_LCD_BRIGHTNESS 0x0007 119824859b68Sbalrog 11997012d4b4SAndreas Färber #define TYPE_MUSICPAL_GPIO "musicpal_gpio" 12008063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(musicpal_gpio_state, MUSICPAL_GPIO) 12017012d4b4SAndreas Färber 1202db1015e9SEduardo Habkost struct musicpal_gpio_state { 12037012d4b4SAndreas Färber /*< private >*/ 12047012d4b4SAndreas Färber SysBusDevice parent_obj; 12057012d4b4SAndreas Färber /*< public >*/ 12067012d4b4SAndreas Färber 120719b4a424SAvi Kivity MemoryRegion iomem; 1208343ec8e4SBenoit Canet uint32_t lcd_brightness; 1209343ec8e4SBenoit Canet uint32_t out_state; 1210343ec8e4SBenoit Canet uint32_t in_state; 1211708afdf3SJan Kiszka uint32_t ier; 1212708afdf3SJan Kiszka uint32_t imr; 1213343ec8e4SBenoit Canet uint32_t isr; 1214343ec8e4SBenoit Canet qemu_irq irq; 1215708afdf3SJan Kiszka qemu_irq out[5]; /* 3 brightness out + 2 lcd (data and clock ) */ 1216db1015e9SEduardo Habkost }; 1217343ec8e4SBenoit Canet 1218343ec8e4SBenoit Canet static void musicpal_gpio_brightness_update(musicpal_gpio_state *s) { 1219343ec8e4SBenoit Canet int i; 1220343ec8e4SBenoit Canet uint32_t brightness; 1221343ec8e4SBenoit Canet 1222343ec8e4SBenoit Canet /* compute brightness ratio */ 1223343ec8e4SBenoit Canet switch (s->lcd_brightness) { 1224343ec8e4SBenoit Canet case 0x00000007: 1225343ec8e4SBenoit Canet brightness = 0; 1226343ec8e4SBenoit Canet break; 1227343ec8e4SBenoit Canet 1228343ec8e4SBenoit Canet case 0x00020000: 1229343ec8e4SBenoit Canet brightness = 1; 1230343ec8e4SBenoit Canet break; 1231343ec8e4SBenoit Canet 1232343ec8e4SBenoit Canet case 0x00020001: 1233343ec8e4SBenoit Canet brightness = 2; 1234343ec8e4SBenoit Canet break; 1235343ec8e4SBenoit Canet 1236343ec8e4SBenoit Canet case 0x00040000: 1237343ec8e4SBenoit Canet brightness = 3; 1238343ec8e4SBenoit Canet break; 1239343ec8e4SBenoit Canet 1240343ec8e4SBenoit Canet case 0x00010006: 1241343ec8e4SBenoit Canet brightness = 4; 1242343ec8e4SBenoit Canet break; 1243343ec8e4SBenoit Canet 1244343ec8e4SBenoit Canet case 0x00020005: 1245343ec8e4SBenoit Canet brightness = 5; 1246343ec8e4SBenoit Canet break; 1247343ec8e4SBenoit Canet 1248343ec8e4SBenoit Canet case 0x00040003: 1249343ec8e4SBenoit Canet brightness = 6; 1250343ec8e4SBenoit Canet break; 1251343ec8e4SBenoit Canet 1252343ec8e4SBenoit Canet case 0x00030004: 1253343ec8e4SBenoit Canet default: 1254343ec8e4SBenoit Canet brightness = 7; 1255343ec8e4SBenoit Canet } 1256343ec8e4SBenoit Canet 1257343ec8e4SBenoit Canet /* set lcd brightness GPIOs */ 125849fedd0dSJan Kiszka for (i = 0; i <= 2; i++) { 1259343ec8e4SBenoit Canet qemu_set_irq(s->out[i], (brightness >> i) & 1); 1260343ec8e4SBenoit Canet } 126149fedd0dSJan Kiszka } 1262343ec8e4SBenoit Canet 1263708afdf3SJan Kiszka static void musicpal_gpio_pin_event(void *opaque, int pin, int level) 1264343ec8e4SBenoit Canet { 1265243cd13cSJan Kiszka musicpal_gpio_state *s = opaque; 1266708afdf3SJan Kiszka uint32_t mask = 1 << pin; 1267708afdf3SJan Kiszka uint32_t delta = level << pin; 1268708afdf3SJan Kiszka uint32_t old = s->in_state & mask; 1269343ec8e4SBenoit Canet 1270708afdf3SJan Kiszka s->in_state &= ~mask; 1271708afdf3SJan Kiszka s->in_state |= delta; 1272708afdf3SJan Kiszka 1273708afdf3SJan Kiszka if ((old ^ delta) && 1274708afdf3SJan Kiszka ((level && (s->imr & mask)) || (!level && (s->ier & mask)))) { 1275708afdf3SJan Kiszka s->isr = mask; 1276708afdf3SJan Kiszka qemu_irq_raise(s->irq); 1277d074769cSAndrzej Zaborowski } 1278343ec8e4SBenoit Canet } 1279343ec8e4SBenoit Canet 1280a8170e5eSAvi Kivity static uint64_t musicpal_gpio_read(void *opaque, hwaddr offset, 128119b4a424SAvi Kivity unsigned size) 128224859b68Sbalrog { 1283243cd13cSJan Kiszka musicpal_gpio_state *s = opaque; 1284343ec8e4SBenoit Canet 128524859b68Sbalrog switch (offset) { 128624859b68Sbalrog case MP_GPIO_OE_HI: /* used for LCD brightness control */ 1287343ec8e4SBenoit Canet return s->lcd_brightness & MP_OE_LCD_BRIGHTNESS; 128824859b68Sbalrog 128924859b68Sbalrog case MP_GPIO_OUT_LO: 1290343ec8e4SBenoit Canet return s->out_state & 0xFFFF; 129124859b68Sbalrog case MP_GPIO_OUT_HI: 1292343ec8e4SBenoit Canet return s->out_state >> 16; 129324859b68Sbalrog 129424859b68Sbalrog case MP_GPIO_IN_LO: 1295343ec8e4SBenoit Canet return s->in_state & 0xFFFF; 129624859b68Sbalrog case MP_GPIO_IN_HI: 1297343ec8e4SBenoit Canet return s->in_state >> 16; 129824859b68Sbalrog 1299708afdf3SJan Kiszka case MP_GPIO_IER_LO: 1300708afdf3SJan Kiszka return s->ier & 0xFFFF; 1301708afdf3SJan Kiszka case MP_GPIO_IER_HI: 1302708afdf3SJan Kiszka return s->ier >> 16; 1303708afdf3SJan Kiszka 1304708afdf3SJan Kiszka case MP_GPIO_IMR_LO: 1305708afdf3SJan Kiszka return s->imr & 0xFFFF; 1306708afdf3SJan Kiszka case MP_GPIO_IMR_HI: 1307708afdf3SJan Kiszka return s->imr >> 16; 1308708afdf3SJan Kiszka 130924859b68Sbalrog case MP_GPIO_ISR_LO: 1310343ec8e4SBenoit Canet return s->isr & 0xFFFF; 131124859b68Sbalrog case MP_GPIO_ISR_HI: 1312343ec8e4SBenoit Canet return s->isr >> 16; 131324859b68Sbalrog 131424859b68Sbalrog default: 131524859b68Sbalrog return 0; 131624859b68Sbalrog } 131724859b68Sbalrog } 131824859b68Sbalrog 1319a8170e5eSAvi Kivity static void musicpal_gpio_write(void *opaque, hwaddr offset, 132019b4a424SAvi Kivity uint64_t value, unsigned size) 132124859b68Sbalrog { 1322243cd13cSJan Kiszka musicpal_gpio_state *s = opaque; 132324859b68Sbalrog switch (offset) { 132424859b68Sbalrog case MP_GPIO_OE_HI: /* used for LCD brightness control */ 1325343ec8e4SBenoit Canet s->lcd_brightness = (s->lcd_brightness & MP_GPIO_LCD_BRIGHTNESS) | 132624859b68Sbalrog (value & MP_OE_LCD_BRIGHTNESS); 1327343ec8e4SBenoit Canet musicpal_gpio_brightness_update(s); 132824859b68Sbalrog break; 132924859b68Sbalrog 133024859b68Sbalrog case MP_GPIO_OUT_LO: 1331343ec8e4SBenoit Canet s->out_state = (s->out_state & 0xFFFF0000) | (value & 0xFFFF); 133224859b68Sbalrog break; 133324859b68Sbalrog case MP_GPIO_OUT_HI: 1334343ec8e4SBenoit Canet s->out_state = (s->out_state & 0xFFFF) | (value << 16); 1335343ec8e4SBenoit Canet s->lcd_brightness = (s->lcd_brightness & 0xFFFF) | 1336343ec8e4SBenoit Canet (s->out_state & MP_GPIO_LCD_BRIGHTNESS); 1337343ec8e4SBenoit Canet musicpal_gpio_brightness_update(s); 1338d074769cSAndrzej Zaborowski qemu_set_irq(s->out[3], (s->out_state >> MP_GPIO_I2C_DATA_BIT) & 1); 1339d074769cSAndrzej Zaborowski qemu_set_irq(s->out[4], (s->out_state >> MP_GPIO_I2C_CLOCK_BIT) & 1); 134024859b68Sbalrog break; 134124859b68Sbalrog 1342708afdf3SJan Kiszka case MP_GPIO_IER_LO: 1343708afdf3SJan Kiszka s->ier = (s->ier & 0xFFFF0000) | (value & 0xFFFF); 1344708afdf3SJan Kiszka break; 1345708afdf3SJan Kiszka case MP_GPIO_IER_HI: 1346708afdf3SJan Kiszka s->ier = (s->ier & 0xFFFF) | (value << 16); 1347708afdf3SJan Kiszka break; 1348708afdf3SJan Kiszka 1349708afdf3SJan Kiszka case MP_GPIO_IMR_LO: 1350708afdf3SJan Kiszka s->imr = (s->imr & 0xFFFF0000) | (value & 0xFFFF); 1351708afdf3SJan Kiszka break; 1352708afdf3SJan Kiszka case MP_GPIO_IMR_HI: 1353708afdf3SJan Kiszka s->imr = (s->imr & 0xFFFF) | (value << 16); 1354708afdf3SJan Kiszka break; 135524859b68Sbalrog } 135624859b68Sbalrog } 135724859b68Sbalrog 135819b4a424SAvi Kivity static const MemoryRegionOps musicpal_gpio_ops = { 135919b4a424SAvi Kivity .read = musicpal_gpio_read, 136019b4a424SAvi Kivity .write = musicpal_gpio_write, 136119b4a424SAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 1362718ec0beSmalc }; 1363718ec0beSmalc 1364d5b61dddSJan Kiszka static void musicpal_gpio_reset(DeviceState *d) 1365718ec0beSmalc { 13667012d4b4SAndreas Färber musicpal_gpio_state *s = MUSICPAL_GPIO(d); 136730624c92SJan Kiszka 136830624c92SJan Kiszka s->lcd_brightness = 0; 136930624c92SJan Kiszka s->out_state = 0; 1370343ec8e4SBenoit Canet s->in_state = 0xffffffff; 1371708afdf3SJan Kiszka s->ier = 0; 1372708afdf3SJan Kiszka s->imr = 0; 1373343ec8e4SBenoit Canet s->isr = 0; 1374343ec8e4SBenoit Canet } 1375343ec8e4SBenoit Canet 1376ece71994Sxiaoqiang zhao static void musicpal_gpio_init(Object *obj) 1377343ec8e4SBenoit Canet { 1378ece71994Sxiaoqiang zhao SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 13797012d4b4SAndreas Färber DeviceState *dev = DEVICE(sbd); 13807012d4b4SAndreas Färber musicpal_gpio_state *s = MUSICPAL_GPIO(dev); 1381718ec0beSmalc 13827012d4b4SAndreas Färber sysbus_init_irq(sbd, &s->irq); 1383343ec8e4SBenoit Canet 1384ece71994Sxiaoqiang zhao memory_region_init_io(&s->iomem, obj, &musicpal_gpio_ops, s, 138519b4a424SAvi Kivity "musicpal-gpio", MP_GPIO_SIZE); 13867012d4b4SAndreas Färber sysbus_init_mmio(sbd, &s->iomem); 1387343ec8e4SBenoit Canet 13887012d4b4SAndreas Färber qdev_init_gpio_out(dev, s->out, ARRAY_SIZE(s->out)); 1389708afdf3SJan Kiszka 13907012d4b4SAndreas Färber qdev_init_gpio_in(dev, musicpal_gpio_pin_event, 32); 1391718ec0beSmalc } 1392718ec0beSmalc 1393d5b61dddSJan Kiszka static const VMStateDescription musicpal_gpio_vmsd = { 1394d5b61dddSJan Kiszka .name = "musicpal_gpio", 1395d5b61dddSJan Kiszka .version_id = 1, 1396d5b61dddSJan Kiszka .minimum_version_id = 1, 1397d5b61dddSJan Kiszka .fields = (VMStateField[]) { 1398d5b61dddSJan Kiszka VMSTATE_UINT32(lcd_brightness, musicpal_gpio_state), 1399d5b61dddSJan Kiszka VMSTATE_UINT32(out_state, musicpal_gpio_state), 1400d5b61dddSJan Kiszka VMSTATE_UINT32(in_state, musicpal_gpio_state), 1401d5b61dddSJan Kiszka VMSTATE_UINT32(ier, musicpal_gpio_state), 1402d5b61dddSJan Kiszka VMSTATE_UINT32(imr, musicpal_gpio_state), 1403d5b61dddSJan Kiszka VMSTATE_UINT32(isr, musicpal_gpio_state), 1404d5b61dddSJan Kiszka VMSTATE_END_OF_LIST() 1405d5b61dddSJan Kiszka } 1406d5b61dddSJan Kiszka }; 1407d5b61dddSJan Kiszka 1408999e12bbSAnthony Liguori static void musicpal_gpio_class_init(ObjectClass *klass, void *data) 1409999e12bbSAnthony Liguori { 141039bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 1411999e12bbSAnthony Liguori 141239bffca2SAnthony Liguori dc->reset = musicpal_gpio_reset; 141339bffca2SAnthony Liguori dc->vmsd = &musicpal_gpio_vmsd; 1414999e12bbSAnthony Liguori } 1415999e12bbSAnthony Liguori 14168c43a6f0SAndreas Färber static const TypeInfo musicpal_gpio_info = { 14177012d4b4SAndreas Färber .name = TYPE_MUSICPAL_GPIO, 141839bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 141939bffca2SAnthony Liguori .instance_size = sizeof(musicpal_gpio_state), 1420ece71994Sxiaoqiang zhao .instance_init = musicpal_gpio_init, 1421999e12bbSAnthony Liguori .class_init = musicpal_gpio_class_init, 142230624c92SJan Kiszka }; 142330624c92SJan Kiszka 142424859b68Sbalrog /* Keyboard codes & masks */ 14257c6ce4baSbalrog #define KEY_RELEASED 0x80 142624859b68Sbalrog #define KEY_CODE 0x7f 142724859b68Sbalrog 142824859b68Sbalrog #define KEYCODE_TAB 0x0f 142924859b68Sbalrog #define KEYCODE_ENTER 0x1c 143024859b68Sbalrog #define KEYCODE_F 0x21 143124859b68Sbalrog #define KEYCODE_M 0x32 143224859b68Sbalrog 143324859b68Sbalrog #define KEYCODE_EXTENDED 0xe0 143424859b68Sbalrog #define KEYCODE_UP 0x48 143524859b68Sbalrog #define KEYCODE_DOWN 0x50 143624859b68Sbalrog #define KEYCODE_LEFT 0x4b 143724859b68Sbalrog #define KEYCODE_RIGHT 0x4d 143824859b68Sbalrog 1439708afdf3SJan Kiszka #define MP_KEY_WHEEL_VOL (1 << 0) 1440343ec8e4SBenoit Canet #define MP_KEY_WHEEL_VOL_INV (1 << 1) 1441343ec8e4SBenoit Canet #define MP_KEY_WHEEL_NAV (1 << 2) 1442343ec8e4SBenoit Canet #define MP_KEY_WHEEL_NAV_INV (1 << 3) 1443343ec8e4SBenoit Canet #define MP_KEY_BTN_FAVORITS (1 << 4) 1444343ec8e4SBenoit Canet #define MP_KEY_BTN_MENU (1 << 5) 1445343ec8e4SBenoit Canet #define MP_KEY_BTN_VOLUME (1 << 6) 1446343ec8e4SBenoit Canet #define MP_KEY_BTN_NAVIGATION (1 << 7) 1447343ec8e4SBenoit Canet 14483bdf5327SAndreas Färber #define TYPE_MUSICPAL_KEY "musicpal_key" 14498063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(musicpal_key_state, MUSICPAL_KEY) 14503bdf5327SAndreas Färber 1451db1015e9SEduardo Habkost struct musicpal_key_state { 14523bdf5327SAndreas Färber /*< private >*/ 14533bdf5327SAndreas Färber SysBusDevice parent_obj; 14543bdf5327SAndreas Färber /*< public >*/ 14553bdf5327SAndreas Färber 14564f5c9479SAvi Kivity MemoryRegion iomem; 1457343ec8e4SBenoit Canet uint32_t kbd_extended; 1458708afdf3SJan Kiszka uint32_t pressed_keys; 1459708afdf3SJan Kiszka qemu_irq out[8]; 1460db1015e9SEduardo Habkost }; 1461343ec8e4SBenoit Canet 146224859b68Sbalrog static void musicpal_key_event(void *opaque, int keycode) 146324859b68Sbalrog { 1464243cd13cSJan Kiszka musicpal_key_state *s = opaque; 146524859b68Sbalrog uint32_t event = 0; 1466343ec8e4SBenoit Canet int i; 146724859b68Sbalrog 146824859b68Sbalrog if (keycode == KEYCODE_EXTENDED) { 1469343ec8e4SBenoit Canet s->kbd_extended = 1; 147024859b68Sbalrog return; 147124859b68Sbalrog } 147224859b68Sbalrog 147349fedd0dSJan Kiszka if (s->kbd_extended) { 147424859b68Sbalrog switch (keycode & KEY_CODE) { 147524859b68Sbalrog case KEYCODE_UP: 1476343ec8e4SBenoit Canet event = MP_KEY_WHEEL_NAV | MP_KEY_WHEEL_NAV_INV; 147724859b68Sbalrog break; 147824859b68Sbalrog 147924859b68Sbalrog case KEYCODE_DOWN: 1480343ec8e4SBenoit Canet event = MP_KEY_WHEEL_NAV; 148124859b68Sbalrog break; 148224859b68Sbalrog 148324859b68Sbalrog case KEYCODE_LEFT: 1484343ec8e4SBenoit Canet event = MP_KEY_WHEEL_VOL | MP_KEY_WHEEL_VOL_INV; 148524859b68Sbalrog break; 148624859b68Sbalrog 148724859b68Sbalrog case KEYCODE_RIGHT: 1488343ec8e4SBenoit Canet event = MP_KEY_WHEEL_VOL; 148924859b68Sbalrog break; 149024859b68Sbalrog } 149149fedd0dSJan Kiszka } else { 149224859b68Sbalrog switch (keycode & KEY_CODE) { 149324859b68Sbalrog case KEYCODE_F: 1494343ec8e4SBenoit Canet event = MP_KEY_BTN_FAVORITS; 149524859b68Sbalrog break; 149624859b68Sbalrog 149724859b68Sbalrog case KEYCODE_TAB: 1498343ec8e4SBenoit Canet event = MP_KEY_BTN_VOLUME; 149924859b68Sbalrog break; 150024859b68Sbalrog 150124859b68Sbalrog case KEYCODE_ENTER: 1502343ec8e4SBenoit Canet event = MP_KEY_BTN_NAVIGATION; 150324859b68Sbalrog break; 150424859b68Sbalrog 150524859b68Sbalrog case KEYCODE_M: 1506343ec8e4SBenoit Canet event = MP_KEY_BTN_MENU; 150724859b68Sbalrog break; 150824859b68Sbalrog } 15097c6ce4baSbalrog /* Do not repeat already pressed buttons */ 1510708afdf3SJan Kiszka if (!(keycode & KEY_RELEASED) && (s->pressed_keys & event)) { 15117c6ce4baSbalrog event = 0; 15127c6ce4baSbalrog } 1513708afdf3SJan Kiszka } 151424859b68Sbalrog 15157c6ce4baSbalrog if (event) { 1516708afdf3SJan Kiszka /* Raise GPIO pin first if repeating a key */ 1517708afdf3SJan Kiszka if (!(keycode & KEY_RELEASED) && (s->pressed_keys & event)) { 1518708afdf3SJan Kiszka for (i = 0; i <= 7; i++) { 1519708afdf3SJan Kiszka if (event & (1 << i)) { 1520708afdf3SJan Kiszka qemu_set_irq(s->out[i], 1); 15217c6ce4baSbalrog } 1522708afdf3SJan Kiszka } 1523708afdf3SJan Kiszka } 1524708afdf3SJan Kiszka for (i = 0; i <= 7; i++) { 1525708afdf3SJan Kiszka if (event & (1 << i)) { 1526708afdf3SJan Kiszka qemu_set_irq(s->out[i], !!(keycode & KEY_RELEASED)); 1527708afdf3SJan Kiszka } 1528708afdf3SJan Kiszka } 1529708afdf3SJan Kiszka if (keycode & KEY_RELEASED) { 1530708afdf3SJan Kiszka s->pressed_keys &= ~event; 1531708afdf3SJan Kiszka } else { 1532708afdf3SJan Kiszka s->pressed_keys |= event; 1533708afdf3SJan Kiszka } 1534343ec8e4SBenoit Canet } 1535343ec8e4SBenoit Canet 1536343ec8e4SBenoit Canet s->kbd_extended = 0; 1537343ec8e4SBenoit Canet } 1538343ec8e4SBenoit Canet 1539ece71994Sxiaoqiang zhao static void musicpal_key_init(Object *obj) 1540343ec8e4SBenoit Canet { 1541ece71994Sxiaoqiang zhao SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 15423bdf5327SAndreas Färber DeviceState *dev = DEVICE(sbd); 15433bdf5327SAndreas Färber musicpal_key_state *s = MUSICPAL_KEY(dev); 1544343ec8e4SBenoit Canet 1545ece71994Sxiaoqiang zhao memory_region_init(&s->iomem, obj, "dummy", 0); 15463bdf5327SAndreas Färber sysbus_init_mmio(sbd, &s->iomem); 1547343ec8e4SBenoit Canet 1548343ec8e4SBenoit Canet s->kbd_extended = 0; 1549708afdf3SJan Kiszka s->pressed_keys = 0; 1550343ec8e4SBenoit Canet 15513bdf5327SAndreas Färber qdev_init_gpio_out(dev, s->out, ARRAY_SIZE(s->out)); 1552343ec8e4SBenoit Canet 1553343ec8e4SBenoit Canet qemu_add_kbd_event_handler(musicpal_key_event, s); 155424859b68Sbalrog } 155524859b68Sbalrog 1556d5b61dddSJan Kiszka static const VMStateDescription musicpal_key_vmsd = { 1557d5b61dddSJan Kiszka .name = "musicpal_key", 1558d5b61dddSJan Kiszka .version_id = 1, 1559d5b61dddSJan Kiszka .minimum_version_id = 1, 1560d5b61dddSJan Kiszka .fields = (VMStateField[]) { 1561d5b61dddSJan Kiszka VMSTATE_UINT32(kbd_extended, musicpal_key_state), 1562d5b61dddSJan Kiszka VMSTATE_UINT32(pressed_keys, musicpal_key_state), 1563d5b61dddSJan Kiszka VMSTATE_END_OF_LIST() 1564d5b61dddSJan Kiszka } 1565d5b61dddSJan Kiszka }; 1566d5b61dddSJan Kiszka 1567999e12bbSAnthony Liguori static void musicpal_key_class_init(ObjectClass *klass, void *data) 1568999e12bbSAnthony Liguori { 156939bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 1570999e12bbSAnthony Liguori 157139bffca2SAnthony Liguori dc->vmsd = &musicpal_key_vmsd; 1572999e12bbSAnthony Liguori } 1573999e12bbSAnthony Liguori 15748c43a6f0SAndreas Färber static const TypeInfo musicpal_key_info = { 15753bdf5327SAndreas Färber .name = TYPE_MUSICPAL_KEY, 157639bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 157739bffca2SAnthony Liguori .instance_size = sizeof(musicpal_key_state), 1578ece71994Sxiaoqiang zhao .instance_init = musicpal_key_init, 1579999e12bbSAnthony Liguori .class_init = musicpal_key_class_init, 1580d5b61dddSJan Kiszka }; 1581d5b61dddSJan Kiszka 158224859b68Sbalrog static struct arm_boot_info musicpal_binfo = { 158324859b68Sbalrog .loader_start = 0x0, 158424859b68Sbalrog .board_id = 0x20e, 158524859b68Sbalrog }; 158624859b68Sbalrog 15873ef96221SMarcel Apfelbaum static void musicpal_init(MachineState *machine) 158824859b68Sbalrog { 1589f25608e9SAndreas Färber ARMCPU *cpu; 1590b47b50faSPaul Brook qemu_irq pic[32]; 1591b47b50faSPaul Brook DeviceState *dev; 1592*498661ddSPhilippe Mathieu-Daudé DeviceState *uart_orgate; 1593d074769cSAndrzej Zaborowski DeviceState *i2c_dev; 1594343ec8e4SBenoit Canet DeviceState *lcd_dev; 1595343ec8e4SBenoit Canet DeviceState *key_dev; 15961373b15bSPhilippe Mathieu-Daudé I2CSlave *wm8750_dev; 1597d074769cSAndrzej Zaborowski SysBusDevice *s; 1598a5c82852SAndreas Färber I2CBus *i2c; 1599b47b50faSPaul Brook int i; 160024859b68Sbalrog unsigned long flash_size; 1601751c6a17SGerd Hoffmann DriveInfo *dinfo; 16023ed61312SIgor Mammedov MachineClass *mc = MACHINE_GET_CLASS(machine); 160319b4a424SAvi Kivity MemoryRegion *address_space_mem = get_system_memory(); 160419b4a424SAvi Kivity MemoryRegion *sram = g_new(MemoryRegion, 1); 160524859b68Sbalrog 16063ed61312SIgor Mammedov /* For now we use a fixed - the original - RAM size */ 16073ed61312SIgor Mammedov if (machine->ram_size != mc->default_ram_size) { 16083ed61312SIgor Mammedov char *sz = size_to_str(mc->default_ram_size); 16093ed61312SIgor Mammedov error_report("Invalid RAM size, should be %s", sz); 16103ed61312SIgor Mammedov g_free(sz); 16113ed61312SIgor Mammedov exit(EXIT_FAILURE); 16123ed61312SIgor Mammedov } 16133ed61312SIgor Mammedov 1614ba1ba5ccSIgor Mammedov cpu = ARM_CPU(cpu_create(machine->cpu_type)); 161524859b68Sbalrog 16163ed61312SIgor Mammedov memory_region_add_subregion(address_space_mem, 0, machine->ram); 161724859b68Sbalrog 161898a99ce0SPeter Maydell memory_region_init_ram(sram, NULL, "musicpal.sram", MP_SRAM_SIZE, 1619f8ed85acSMarkus Armbruster &error_fatal); 162019b4a424SAvi Kivity memory_region_add_subregion(address_space_mem, MP_SRAM_BASE, sram); 162124859b68Sbalrog 1622c7bd0fd9SAndreas Färber dev = sysbus_create_simple(TYPE_MV88W8618_PIC, MP_PIC_BASE, 1623fcef61ecSPeter Maydell qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ)); 1624b47b50faSPaul Brook for (i = 0; i < 32; i++) { 1625067a3ddcSPaul Brook pic[i] = qdev_get_gpio_in(dev, i); 1626b47b50faSPaul Brook } 16274adc8541SAndreas Färber sysbus_create_varargs(TYPE_MV88W8618_PIT, MP_PIT_BASE, pic[MP_TIMER1_IRQ], 1628b47b50faSPaul Brook pic[MP_TIMER2_IRQ], pic[MP_TIMER3_IRQ], 1629b47b50faSPaul Brook pic[MP_TIMER4_IRQ], NULL); 163024859b68Sbalrog 1631*498661ddSPhilippe Mathieu-Daudé /* Logically OR both UART IRQs together */ 1632*498661ddSPhilippe Mathieu-Daudé uart_orgate = DEVICE(object_new(TYPE_OR_IRQ)); 1633*498661ddSPhilippe Mathieu-Daudé object_property_set_int(OBJECT(uart_orgate), "num-lines", 2, &error_fatal); 1634*498661ddSPhilippe Mathieu-Daudé qdev_realize_and_unref(uart_orgate, NULL, &error_fatal); 1635*498661ddSPhilippe Mathieu-Daudé qdev_connect_gpio_out(DEVICE(uart_orgate), 0, pic[MP_UART_SHARED_IRQ]); 1636*498661ddSPhilippe Mathieu-Daudé 1637*498661ddSPhilippe Mathieu-Daudé serial_mm_init(address_space_mem, MP_UART1_BASE, 2, 1638*498661ddSPhilippe Mathieu-Daudé qdev_get_gpio_in(uart_orgate, 0), 16399bca0edbSPeter Maydell 1825000, serial_hd(0), DEVICE_NATIVE_ENDIAN); 1640*498661ddSPhilippe Mathieu-Daudé serial_mm_init(address_space_mem, MP_UART2_BASE, 2, 1641*498661ddSPhilippe Mathieu-Daudé qdev_get_gpio_in(uart_orgate, 1), 16429bca0edbSPeter Maydell 1825000, serial_hd(1), DEVICE_NATIVE_ENDIAN); 164324859b68Sbalrog 164424859b68Sbalrog /* Register flash */ 1645751c6a17SGerd Hoffmann dinfo = drive_get(IF_PFLASH, 0, 0); 1646751c6a17SGerd Hoffmann if (dinfo) { 16474be74634SMarkus Armbruster BlockBackend *blk = blk_by_legacy_dinfo(dinfo); 1648fa1d36dfSMarkus Armbruster 16494be74634SMarkus Armbruster flash_size = blk_getlength(blk); 165024859b68Sbalrog if (flash_size != 8*1024*1024 && flash_size != 16*1024*1024 && 165124859b68Sbalrog flash_size != 32*1024*1024) { 1652c0dbca36SAlistair Francis error_report("Invalid flash image size"); 165324859b68Sbalrog exit(1); 165424859b68Sbalrog } 165524859b68Sbalrog 165624859b68Sbalrog /* 165724859b68Sbalrog * The original U-Boot accesses the flash at 0xFE000000 instead of 165824859b68Sbalrog * 0xFF800000 (if there is 8 MB flash). So remap flash access if the 165924859b68Sbalrog * image is smaller than 32 MB. 166024859b68Sbalrog */ 1661940d5b13SMarkus Armbruster pflash_cfi02_register(0x100000000ULL - MP_FLASH_SIZE_MAX, 1662cfe5f011SAvi Kivity "musicpal.flash", flash_size, 1663ce14710fSMarkus Armbruster blk, 0x10000, 16645f9fc5adSBlue Swirl MP_FLASH_SIZE_MAX / flash_size, 16655f9fc5adSBlue Swirl 2, 0x00BF, 0x236D, 0x0000, 0x0000, 166601e0451aSAnthony Liguori 0x5555, 0x2AAA, 0); 166724859b68Sbalrog } 16685952b01cSAndreas Färber sysbus_create_simple(TYPE_MV88W8618_FLASHCFG, MP_FLASHCFG_BASE, NULL); 166924859b68Sbalrog 1670b47b50faSPaul Brook qemu_check_nic_model(&nd_table[0], "mv88w8618"); 16713e80f690SMarkus Armbruster dev = qdev_new(TYPE_MV88W8618_ETH); 16724c91cd28SGerd Hoffmann qdev_set_nic_properties(dev, &nd_table[0]); 167379ed6fd6SPhilippe Mathieu-Daudé object_property_set_link(OBJECT(dev), "dma-memory", 167479ed6fd6SPhilippe Mathieu-Daudé OBJECT(get_system_memory()), &error_fatal); 16753c6ef471SMarkus Armbruster sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); 16761356b98dSAndreas Färber sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, MP_ETH_BASE); 16771356b98dSAndreas Färber sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[MP_ETH_IRQ]); 167824859b68Sbalrog 1679b47b50faSPaul Brook sysbus_create_simple("mv88w8618_wlan", MP_WLAN_BASE, NULL); 1680718ec0beSmalc 1681a86f200aSPeter Maydell sysbus_create_simple(TYPE_MUSICPAL_MISC, MP_MISC_BASE, NULL); 1682343ec8e4SBenoit Canet 16837012d4b4SAndreas Färber dev = sysbus_create_simple(TYPE_MUSICPAL_GPIO, MP_GPIO_BASE, 16847012d4b4SAndreas Färber pic[MP_GPIO_IRQ]); 1685d04fba94SJan Kiszka i2c_dev = sysbus_create_simple("gpio_i2c", -1, NULL); 1686a5c82852SAndreas Färber i2c = (I2CBus *)qdev_get_child_bus(i2c_dev, "i2c"); 1687d074769cSAndrzej Zaborowski 16882cca58fdSAndreas Färber lcd_dev = sysbus_create_simple(TYPE_MUSICPAL_LCD, MP_LCD_BASE, NULL); 16893bdf5327SAndreas Färber key_dev = sysbus_create_simple(TYPE_MUSICPAL_KEY, -1, NULL); 1690343ec8e4SBenoit Canet 1691d074769cSAndrzej Zaborowski /* I2C read data */ 1692708afdf3SJan Kiszka qdev_connect_gpio_out(i2c_dev, 0, 1693708afdf3SJan Kiszka qdev_get_gpio_in(dev, MP_GPIO_I2C_DATA_BIT)); 1694d074769cSAndrzej Zaborowski /* I2C data */ 1695d074769cSAndrzej Zaborowski qdev_connect_gpio_out(dev, 3, qdev_get_gpio_in(i2c_dev, 0)); 1696d074769cSAndrzej Zaborowski /* I2C clock */ 1697d074769cSAndrzej Zaborowski qdev_connect_gpio_out(dev, 4, qdev_get_gpio_in(i2c_dev, 1)); 1698d074769cSAndrzej Zaborowski 169949fedd0dSJan Kiszka for (i = 0; i < 3; i++) { 1700343ec8e4SBenoit Canet qdev_connect_gpio_out(dev, i, qdev_get_gpio_in(lcd_dev, i)); 170149fedd0dSJan Kiszka } 1702708afdf3SJan Kiszka for (i = 0; i < 4; i++) { 1703708afdf3SJan Kiszka qdev_connect_gpio_out(key_dev, i, qdev_get_gpio_in(dev, i + 8)); 1704708afdf3SJan Kiszka } 1705708afdf3SJan Kiszka for (i = 4; i < 8; i++) { 1706708afdf3SJan Kiszka qdev_connect_gpio_out(key_dev, i, qdev_get_gpio_in(dev, i + 15)); 1707708afdf3SJan Kiszka } 170824859b68Sbalrog 17091373b15bSPhilippe Mathieu-Daudé wm8750_dev = i2c_slave_create_simple(i2c, TYPE_WM8750, MP_WM_ADDR); 17103e80f690SMarkus Armbruster dev = qdev_new(TYPE_MV88W8618_AUDIO); 17111356b98dSAndreas Färber s = SYS_BUS_DEVICE(dev); 17125325cc34SMarkus Armbruster object_property_set_link(OBJECT(dev), "wm8750", OBJECT(wm8750_dev), 17135325cc34SMarkus Armbruster NULL); 17143c6ef471SMarkus Armbruster sysbus_realize_and_unref(s, &error_fatal); 1715d074769cSAndrzej Zaborowski sysbus_mmio_map(s, 0, MP_AUDIO_BASE); 1716d074769cSAndrzej Zaborowski sysbus_connect_irq(s, 0, pic[MP_AUDIO_IRQ]); 1717d074769cSAndrzej Zaborowski 171824859b68Sbalrog musicpal_binfo.ram_size = MP_RAM_DEFAULT_SIZE; 17192744ece8STao Xu arm_load_kernel(cpu, machine, &musicpal_binfo); 172024859b68Sbalrog } 172124859b68Sbalrog 1722e264d29dSEduardo Habkost static void musicpal_machine_init(MachineClass *mc) 1723f80f9ec9SAnthony Liguori { 1724e264d29dSEduardo Habkost mc->desc = "Marvell 88w8618 / MusicPal (ARM926EJ-S)"; 1725e264d29dSEduardo Habkost mc->init = musicpal_init; 17264672cbd7SPeter Maydell mc->ignore_memory_transaction_failures = true; 1727ba1ba5ccSIgor Mammedov mc->default_cpu_type = ARM_CPU_TYPE_NAME("arm926"); 17283ed61312SIgor Mammedov mc->default_ram_size = MP_RAM_DEFAULT_SIZE; 17293ed61312SIgor Mammedov mc->default_ram_id = "musicpal.ram"; 1730f80f9ec9SAnthony Liguori } 1731f80f9ec9SAnthony Liguori 1732e264d29dSEduardo Habkost DEFINE_MACHINE("musicpal", musicpal_machine_init) 1733f80f9ec9SAnthony Liguori 1734999e12bbSAnthony Liguori static void mv88w8618_wlan_class_init(ObjectClass *klass, void *data) 1735999e12bbSAnthony Liguori { 17367f7420a0SMao Zhongyi DeviceClass *dc = DEVICE_CLASS(klass); 1737999e12bbSAnthony Liguori 17387f7420a0SMao Zhongyi dc->realize = mv88w8618_wlan_realize; 1739999e12bbSAnthony Liguori } 1740999e12bbSAnthony Liguori 17418c43a6f0SAndreas Färber static const TypeInfo mv88w8618_wlan_info = { 1742999e12bbSAnthony Liguori .name = "mv88w8618_wlan", 174339bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 174439bffca2SAnthony Liguori .instance_size = sizeof(SysBusDevice), 1745999e12bbSAnthony Liguori .class_init = mv88w8618_wlan_class_init, 1746999e12bbSAnthony Liguori }; 1747999e12bbSAnthony Liguori 174883f7d43aSAndreas Färber static void musicpal_register_types(void) 1749b47b50faSPaul Brook { 175039bffca2SAnthony Liguori type_register_static(&mv88w8618_pic_info); 175139bffca2SAnthony Liguori type_register_static(&mv88w8618_pit_info); 175239bffca2SAnthony Liguori type_register_static(&mv88w8618_flashcfg_info); 175339bffca2SAnthony Liguori type_register_static(&mv88w8618_eth_info); 175439bffca2SAnthony Liguori type_register_static(&mv88w8618_wlan_info); 175539bffca2SAnthony Liguori type_register_static(&musicpal_lcd_info); 175639bffca2SAnthony Liguori type_register_static(&musicpal_gpio_info); 175739bffca2SAnthony Liguori type_register_static(&musicpal_key_info); 1758a86f200aSPeter Maydell type_register_static(&musicpal_misc_info); 1759b47b50faSPaul Brook } 1760b47b50faSPaul Brook 176183f7d43aSAndreas Färber type_init(musicpal_register_types) 1762