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" 307ab14c5aSPhilippe Mathieu-Daudé #include "hw/audio/wm8750.h" 31fa1d36dfSMarkus Armbruster #include "sysemu/block-backend.h" 3254d31236SMarkus Armbruster #include "sysemu/runstate.h" 33022c62cbSPaolo Bonzini #include "exec/address-spaces.h" 3428ecbaeeSPaolo Bonzini #include "ui/pixel_ops.h" 353ed61312SIgor Mammedov #include "qemu/cutils.h" 3624859b68Sbalrog 37718ec0beSmalc #define MP_MISC_BASE 0x80002000 38718ec0beSmalc #define MP_MISC_SIZE 0x00001000 39718ec0beSmalc 4024859b68Sbalrog #define MP_ETH_BASE 0x80008000 4124859b68Sbalrog #define MP_ETH_SIZE 0x00001000 4224859b68Sbalrog 43718ec0beSmalc #define MP_WLAN_BASE 0x8000C000 44718ec0beSmalc #define MP_WLAN_SIZE 0x00000800 45718ec0beSmalc 4624859b68Sbalrog #define MP_UART1_BASE 0x8000C840 4724859b68Sbalrog #define MP_UART2_BASE 0x8000C940 4824859b68Sbalrog 49718ec0beSmalc #define MP_GPIO_BASE 0x8000D000 50718ec0beSmalc #define MP_GPIO_SIZE 0x00001000 51718ec0beSmalc 5224859b68Sbalrog #define MP_FLASHCFG_BASE 0x90006000 5324859b68Sbalrog #define MP_FLASHCFG_SIZE 0x00001000 5424859b68Sbalrog 5524859b68Sbalrog #define MP_AUDIO_BASE 0x90007000 5624859b68Sbalrog 5724859b68Sbalrog #define MP_PIC_BASE 0x90008000 5824859b68Sbalrog #define MP_PIC_SIZE 0x00001000 5924859b68Sbalrog 6024859b68Sbalrog #define MP_PIT_BASE 0x90009000 6124859b68Sbalrog #define MP_PIT_SIZE 0x00001000 6224859b68Sbalrog 6324859b68Sbalrog #define MP_LCD_BASE 0x9000c000 6424859b68Sbalrog #define MP_LCD_SIZE 0x00001000 6524859b68Sbalrog 6624859b68Sbalrog #define MP_SRAM_BASE 0xC0000000 6724859b68Sbalrog #define MP_SRAM_SIZE 0x00020000 6824859b68Sbalrog 6924859b68Sbalrog #define MP_RAM_DEFAULT_SIZE 32*1024*1024 7024859b68Sbalrog #define MP_FLASH_SIZE_MAX 32*1024*1024 7124859b68Sbalrog 7224859b68Sbalrog #define MP_TIMER1_IRQ 4 73b47b50faSPaul Brook #define MP_TIMER2_IRQ 5 74b47b50faSPaul Brook #define MP_TIMER3_IRQ 6 7524859b68Sbalrog #define MP_TIMER4_IRQ 7 7624859b68Sbalrog #define MP_EHCI_IRQ 8 7724859b68Sbalrog #define MP_ETH_IRQ 9 7824859b68Sbalrog #define MP_UART1_IRQ 11 7924859b68Sbalrog #define MP_UART2_IRQ 11 8024859b68Sbalrog #define MP_GPIO_IRQ 12 8124859b68Sbalrog #define MP_RTC_IRQ 28 8224859b68Sbalrog #define MP_AUDIO_IRQ 30 8324859b68Sbalrog 8424859b68Sbalrog /* Wolfson 8750 I2C address */ 8564258229SJan Kiszka #define MP_WM_ADDR 0x1A 8624859b68Sbalrog 8724859b68Sbalrog /* Ethernet register offsets */ 8824859b68Sbalrog #define MP_ETH_SMIR 0x010 8924859b68Sbalrog #define MP_ETH_PCXR 0x408 9024859b68Sbalrog #define MP_ETH_SDCMR 0x448 9124859b68Sbalrog #define MP_ETH_ICR 0x450 9224859b68Sbalrog #define MP_ETH_IMR 0x458 9324859b68Sbalrog #define MP_ETH_FRDP0 0x480 9424859b68Sbalrog #define MP_ETH_FRDP1 0x484 9524859b68Sbalrog #define MP_ETH_FRDP2 0x488 9624859b68Sbalrog #define MP_ETH_FRDP3 0x48C 9724859b68Sbalrog #define MP_ETH_CRDP0 0x4A0 9824859b68Sbalrog #define MP_ETH_CRDP1 0x4A4 9924859b68Sbalrog #define MP_ETH_CRDP2 0x4A8 10024859b68Sbalrog #define MP_ETH_CRDP3 0x4AC 10124859b68Sbalrog #define MP_ETH_CTDP0 0x4E0 10224859b68Sbalrog #define MP_ETH_CTDP1 0x4E4 10324859b68Sbalrog 10424859b68Sbalrog /* MII PHY access */ 10524859b68Sbalrog #define MP_ETH_SMIR_DATA 0x0000FFFF 10624859b68Sbalrog #define MP_ETH_SMIR_ADDR 0x03FF0000 10724859b68Sbalrog #define MP_ETH_SMIR_OPCODE (1 << 26) /* Read value */ 10824859b68Sbalrog #define MP_ETH_SMIR_RDVALID (1 << 27) 10924859b68Sbalrog 11024859b68Sbalrog /* PHY registers */ 11124859b68Sbalrog #define MP_ETH_PHY1_BMSR 0x00210000 11224859b68Sbalrog #define MP_ETH_PHY1_PHYSID1 0x00410000 11324859b68Sbalrog #define MP_ETH_PHY1_PHYSID2 0x00610000 11424859b68Sbalrog 11524859b68Sbalrog #define MP_PHY_BMSR_LINK 0x0004 11624859b68Sbalrog #define MP_PHY_BMSR_AUTONEG 0x0008 11724859b68Sbalrog 11824859b68Sbalrog #define MP_PHY_88E3015 0x01410E20 11924859b68Sbalrog 12024859b68Sbalrog /* TX descriptor status */ 1212b194951SPeter Maydell #define MP_ETH_TX_OWN (1U << 31) 12224859b68Sbalrog 12324859b68Sbalrog /* RX descriptor status */ 1242b194951SPeter Maydell #define MP_ETH_RX_OWN (1U << 31) 12524859b68Sbalrog 12624859b68Sbalrog /* Interrupt cause/mask bits */ 12724859b68Sbalrog #define MP_ETH_IRQ_RX_BIT 0 12824859b68Sbalrog #define MP_ETH_IRQ_RX (1 << MP_ETH_IRQ_RX_BIT) 12924859b68Sbalrog #define MP_ETH_IRQ_TXHI_BIT 2 13024859b68Sbalrog #define MP_ETH_IRQ_TXLO_BIT 3 13124859b68Sbalrog 13224859b68Sbalrog /* Port config bits */ 13324859b68Sbalrog #define MP_ETH_PCXR_2BSM_BIT 28 /* 2-byte incoming suffix */ 13424859b68Sbalrog 13524859b68Sbalrog /* SDMA command bits */ 13624859b68Sbalrog #define MP_ETH_CMD_TXHI (1 << 23) 13724859b68Sbalrog #define MP_ETH_CMD_TXLO (1 << 22) 13824859b68Sbalrog 13924859b68Sbalrog typedef struct mv88w8618_tx_desc { 14024859b68Sbalrog uint32_t cmdstat; 14124859b68Sbalrog uint16_t res; 14224859b68Sbalrog uint16_t bytes; 14324859b68Sbalrog uint32_t buffer; 14424859b68Sbalrog uint32_t next; 14524859b68Sbalrog } mv88w8618_tx_desc; 14624859b68Sbalrog 14724859b68Sbalrog typedef struct mv88w8618_rx_desc { 14824859b68Sbalrog uint32_t cmdstat; 14924859b68Sbalrog uint16_t bytes; 15024859b68Sbalrog uint16_t buffer_size; 15124859b68Sbalrog uint32_t buffer; 15224859b68Sbalrog uint32_t next; 15324859b68Sbalrog } mv88w8618_rx_desc; 15424859b68Sbalrog 155a77d90e6SAndreas Färber #define TYPE_MV88W8618_ETH "mv88w8618_eth" 156a77d90e6SAndreas Färber #define MV88W8618_ETH(obj) \ 157a77d90e6SAndreas Färber OBJECT_CHECK(mv88w8618_eth_state, (obj), TYPE_MV88W8618_ETH) 158a77d90e6SAndreas Färber 15924859b68Sbalrog typedef struct mv88w8618_eth_state { 160a77d90e6SAndreas Färber /*< private >*/ 161a77d90e6SAndreas Färber SysBusDevice parent_obj; 162a77d90e6SAndreas Färber /*< public >*/ 163a77d90e6SAndreas Färber 16419b4a424SAvi Kivity MemoryRegion iomem; 16524859b68Sbalrog qemu_irq irq; 16624859b68Sbalrog uint32_t smir; 16724859b68Sbalrog uint32_t icr; 16824859b68Sbalrog uint32_t imr; 169b946a153Saliguori int mmio_index; 170d5b61dddSJan Kiszka uint32_t vlan_header; 171930c8682Spbrook uint32_t tx_queue[2]; 172930c8682Spbrook uint32_t rx_queue[4]; 173930c8682Spbrook uint32_t frx_queue[4]; 174930c8682Spbrook uint32_t cur_rx[4]; 1753a94dd18SMark McLoughlin NICState *nic; 1764c91cd28SGerd Hoffmann NICConf conf; 17724859b68Sbalrog } mv88w8618_eth_state; 17824859b68Sbalrog 179930c8682Spbrook static void eth_rx_desc_put(uint32_t addr, mv88w8618_rx_desc *desc) 180930c8682Spbrook { 181930c8682Spbrook cpu_to_le32s(&desc->cmdstat); 182930c8682Spbrook cpu_to_le16s(&desc->bytes); 183930c8682Spbrook cpu_to_le16s(&desc->buffer_size); 184930c8682Spbrook cpu_to_le32s(&desc->buffer); 185930c8682Spbrook cpu_to_le32s(&desc->next); 186e1fe50dcSStefan Weil cpu_physical_memory_write(addr, desc, sizeof(*desc)); 187930c8682Spbrook } 188930c8682Spbrook 189930c8682Spbrook static void eth_rx_desc_get(uint32_t addr, mv88w8618_rx_desc *desc) 190930c8682Spbrook { 191e1fe50dcSStefan Weil cpu_physical_memory_read(addr, desc, sizeof(*desc)); 192930c8682Spbrook le32_to_cpus(&desc->cmdstat); 193930c8682Spbrook le16_to_cpus(&desc->bytes); 194930c8682Spbrook le16_to_cpus(&desc->buffer_size); 195930c8682Spbrook le32_to_cpus(&desc->buffer); 196930c8682Spbrook le32_to_cpus(&desc->next); 197930c8682Spbrook } 198930c8682Spbrook 1994e68f7a0SStefan Hajnoczi static ssize_t eth_receive(NetClientState *nc, const uint8_t *buf, size_t size) 20024859b68Sbalrog { 201cc1f0f45SJason Wang mv88w8618_eth_state *s = qemu_get_nic_opaque(nc); 202930c8682Spbrook uint32_t desc_addr; 203930c8682Spbrook mv88w8618_rx_desc desc; 20424859b68Sbalrog int i; 20524859b68Sbalrog 20624859b68Sbalrog for (i = 0; i < 4; i++) { 207930c8682Spbrook desc_addr = s->cur_rx[i]; 20849fedd0dSJan Kiszka if (!desc_addr) { 20924859b68Sbalrog continue; 21049fedd0dSJan Kiszka } 21124859b68Sbalrog do { 212930c8682Spbrook eth_rx_desc_get(desc_addr, &desc); 213930c8682Spbrook if ((desc.cmdstat & MP_ETH_RX_OWN) && desc.buffer_size >= size) { 214930c8682Spbrook cpu_physical_memory_write(desc.buffer + s->vlan_header, 21524859b68Sbalrog buf, size); 216930c8682Spbrook desc.bytes = size + s->vlan_header; 217930c8682Spbrook desc.cmdstat &= ~MP_ETH_RX_OWN; 218930c8682Spbrook s->cur_rx[i] = desc.next; 21924859b68Sbalrog 22024859b68Sbalrog s->icr |= MP_ETH_IRQ_RX; 22149fedd0dSJan Kiszka if (s->icr & s->imr) { 22224859b68Sbalrog qemu_irq_raise(s->irq); 22349fedd0dSJan Kiszka } 224930c8682Spbrook eth_rx_desc_put(desc_addr, &desc); 2254f1c942bSMark McLoughlin return size; 22624859b68Sbalrog } 227930c8682Spbrook desc_addr = desc.next; 228930c8682Spbrook } while (desc_addr != s->rx_queue[i]); 22924859b68Sbalrog } 2304f1c942bSMark McLoughlin return size; 23124859b68Sbalrog } 23224859b68Sbalrog 233930c8682Spbrook static void eth_tx_desc_put(uint32_t addr, mv88w8618_tx_desc *desc) 234930c8682Spbrook { 235930c8682Spbrook cpu_to_le32s(&desc->cmdstat); 236930c8682Spbrook cpu_to_le16s(&desc->res); 237930c8682Spbrook cpu_to_le16s(&desc->bytes); 238930c8682Spbrook cpu_to_le32s(&desc->buffer); 239930c8682Spbrook cpu_to_le32s(&desc->next); 240e1fe50dcSStefan Weil cpu_physical_memory_write(addr, desc, sizeof(*desc)); 241930c8682Spbrook } 242930c8682Spbrook 243930c8682Spbrook static void eth_tx_desc_get(uint32_t addr, mv88w8618_tx_desc *desc) 244930c8682Spbrook { 245e1fe50dcSStefan Weil cpu_physical_memory_read(addr, desc, sizeof(*desc)); 246930c8682Spbrook le32_to_cpus(&desc->cmdstat); 247930c8682Spbrook le16_to_cpus(&desc->res); 248930c8682Spbrook le16_to_cpus(&desc->bytes); 249930c8682Spbrook le32_to_cpus(&desc->buffer); 250930c8682Spbrook le32_to_cpus(&desc->next); 251930c8682Spbrook } 252930c8682Spbrook 25324859b68Sbalrog static void eth_send(mv88w8618_eth_state *s, int queue_index) 25424859b68Sbalrog { 255930c8682Spbrook uint32_t desc_addr = s->tx_queue[queue_index]; 256930c8682Spbrook mv88w8618_tx_desc desc; 25707b064e9SJan Kiszka uint32_t next_desc; 258930c8682Spbrook uint8_t buf[2048]; 259930c8682Spbrook int len; 260930c8682Spbrook 26124859b68Sbalrog do { 262930c8682Spbrook eth_tx_desc_get(desc_addr, &desc); 26307b064e9SJan Kiszka next_desc = desc.next; 264930c8682Spbrook if (desc.cmdstat & MP_ETH_TX_OWN) { 265930c8682Spbrook len = desc.bytes; 266930c8682Spbrook if (len < 2048) { 267930c8682Spbrook cpu_physical_memory_read(desc.buffer, buf, len); 268b356f76dSJason Wang qemu_send_packet(qemu_get_queue(s->nic), buf, len); 26924859b68Sbalrog } 270930c8682Spbrook desc.cmdstat &= ~MP_ETH_TX_OWN; 271930c8682Spbrook s->icr |= 1 << (MP_ETH_IRQ_TXLO_BIT - queue_index); 272930c8682Spbrook eth_tx_desc_put(desc_addr, &desc); 273930c8682Spbrook } 27407b064e9SJan Kiszka desc_addr = next_desc; 275930c8682Spbrook } while (desc_addr != s->tx_queue[queue_index]); 27624859b68Sbalrog } 27724859b68Sbalrog 278a8170e5eSAvi Kivity static uint64_t mv88w8618_eth_read(void *opaque, hwaddr offset, 27919b4a424SAvi Kivity unsigned size) 28024859b68Sbalrog { 28124859b68Sbalrog mv88w8618_eth_state *s = opaque; 28224859b68Sbalrog 28324859b68Sbalrog switch (offset) { 28424859b68Sbalrog case MP_ETH_SMIR: 28524859b68Sbalrog if (s->smir & MP_ETH_SMIR_OPCODE) { 28624859b68Sbalrog switch (s->smir & MP_ETH_SMIR_ADDR) { 28724859b68Sbalrog case MP_ETH_PHY1_BMSR: 28824859b68Sbalrog return MP_PHY_BMSR_LINK | MP_PHY_BMSR_AUTONEG | 28924859b68Sbalrog MP_ETH_SMIR_RDVALID; 29024859b68Sbalrog case MP_ETH_PHY1_PHYSID1: 29124859b68Sbalrog return (MP_PHY_88E3015 >> 16) | MP_ETH_SMIR_RDVALID; 29224859b68Sbalrog case MP_ETH_PHY1_PHYSID2: 29324859b68Sbalrog return (MP_PHY_88E3015 & 0xFFFF) | MP_ETH_SMIR_RDVALID; 29424859b68Sbalrog default: 29524859b68Sbalrog return MP_ETH_SMIR_RDVALID; 29624859b68Sbalrog } 29724859b68Sbalrog } 29824859b68Sbalrog return 0; 29924859b68Sbalrog 30024859b68Sbalrog case MP_ETH_ICR: 30124859b68Sbalrog return s->icr; 30224859b68Sbalrog 30324859b68Sbalrog case MP_ETH_IMR: 30424859b68Sbalrog return s->imr; 30524859b68Sbalrog 30624859b68Sbalrog case MP_ETH_FRDP0 ... MP_ETH_FRDP3: 307930c8682Spbrook return s->frx_queue[(offset - MP_ETH_FRDP0)/4]; 30824859b68Sbalrog 30924859b68Sbalrog case MP_ETH_CRDP0 ... MP_ETH_CRDP3: 310930c8682Spbrook return s->rx_queue[(offset - MP_ETH_CRDP0)/4]; 31124859b68Sbalrog 312cf143ad3SPeter Maydell case MP_ETH_CTDP0 ... MP_ETH_CTDP1: 313930c8682Spbrook return s->tx_queue[(offset - MP_ETH_CTDP0)/4]; 31424859b68Sbalrog 31524859b68Sbalrog default: 31624859b68Sbalrog return 0; 31724859b68Sbalrog } 31824859b68Sbalrog } 31924859b68Sbalrog 320a8170e5eSAvi Kivity static void mv88w8618_eth_write(void *opaque, hwaddr offset, 32119b4a424SAvi Kivity uint64_t value, unsigned size) 32224859b68Sbalrog { 32324859b68Sbalrog mv88w8618_eth_state *s = opaque; 32424859b68Sbalrog 32524859b68Sbalrog switch (offset) { 32624859b68Sbalrog case MP_ETH_SMIR: 32724859b68Sbalrog s->smir = value; 32824859b68Sbalrog break; 32924859b68Sbalrog 33024859b68Sbalrog case MP_ETH_PCXR: 33124859b68Sbalrog s->vlan_header = ((value >> MP_ETH_PCXR_2BSM_BIT) & 1) * 2; 33224859b68Sbalrog break; 33324859b68Sbalrog 33424859b68Sbalrog case MP_ETH_SDCMR: 33549fedd0dSJan Kiszka if (value & MP_ETH_CMD_TXHI) { 33624859b68Sbalrog eth_send(s, 1); 33749fedd0dSJan Kiszka } 33849fedd0dSJan Kiszka if (value & MP_ETH_CMD_TXLO) { 33924859b68Sbalrog eth_send(s, 0); 34049fedd0dSJan Kiszka } 34149fedd0dSJan Kiszka if (value & (MP_ETH_CMD_TXHI | MP_ETH_CMD_TXLO) && s->icr & s->imr) { 34224859b68Sbalrog qemu_irq_raise(s->irq); 34349fedd0dSJan Kiszka } 34424859b68Sbalrog break; 34524859b68Sbalrog 34624859b68Sbalrog case MP_ETH_ICR: 34724859b68Sbalrog s->icr &= value; 34824859b68Sbalrog break; 34924859b68Sbalrog 35024859b68Sbalrog case MP_ETH_IMR: 35124859b68Sbalrog s->imr = value; 35249fedd0dSJan Kiszka if (s->icr & s->imr) { 35324859b68Sbalrog qemu_irq_raise(s->irq); 35449fedd0dSJan Kiszka } 35524859b68Sbalrog break; 35624859b68Sbalrog 35724859b68Sbalrog case MP_ETH_FRDP0 ... MP_ETH_FRDP3: 358930c8682Spbrook s->frx_queue[(offset - MP_ETH_FRDP0)/4] = value; 35924859b68Sbalrog break; 36024859b68Sbalrog 36124859b68Sbalrog case MP_ETH_CRDP0 ... MP_ETH_CRDP3: 36224859b68Sbalrog s->rx_queue[(offset - MP_ETH_CRDP0)/4] = 363930c8682Spbrook s->cur_rx[(offset - MP_ETH_CRDP0)/4] = value; 36424859b68Sbalrog break; 36524859b68Sbalrog 366cf143ad3SPeter Maydell case MP_ETH_CTDP0 ... MP_ETH_CTDP1: 367930c8682Spbrook s->tx_queue[(offset - MP_ETH_CTDP0)/4] = value; 36824859b68Sbalrog break; 36924859b68Sbalrog } 37024859b68Sbalrog } 37124859b68Sbalrog 37219b4a424SAvi Kivity static const MemoryRegionOps mv88w8618_eth_ops = { 37319b4a424SAvi Kivity .read = mv88w8618_eth_read, 37419b4a424SAvi Kivity .write = mv88w8618_eth_write, 37519b4a424SAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 37624859b68Sbalrog }; 37724859b68Sbalrog 3784e68f7a0SStefan Hajnoczi static void eth_cleanup(NetClientState *nc) 379b946a153Saliguori { 380cc1f0f45SJason Wang mv88w8618_eth_state *s = qemu_get_nic_opaque(nc); 381b946a153Saliguori 3823a94dd18SMark McLoughlin s->nic = NULL; 383b946a153Saliguori } 384b946a153Saliguori 3853a94dd18SMark McLoughlin static NetClientInfo net_mv88w8618_info = { 386f394b2e2SEric Blake .type = NET_CLIENT_DRIVER_NIC, 3873a94dd18SMark McLoughlin .size = sizeof(NICState), 3883a94dd18SMark McLoughlin .receive = eth_receive, 3893a94dd18SMark McLoughlin .cleanup = eth_cleanup, 3903a94dd18SMark McLoughlin }; 3913a94dd18SMark McLoughlin 392ece71994Sxiaoqiang zhao static void mv88w8618_eth_init(Object *obj) 39324859b68Sbalrog { 394ece71994Sxiaoqiang zhao SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 395a77d90e6SAndreas Färber DeviceState *dev = DEVICE(sbd); 396a77d90e6SAndreas Färber mv88w8618_eth_state *s = MV88W8618_ETH(dev); 39724859b68Sbalrog 398a77d90e6SAndreas Färber sysbus_init_irq(sbd, &s->irq); 399ece71994Sxiaoqiang zhao memory_region_init_io(&s->iomem, obj, &mv88w8618_eth_ops, s, 40064bde0f3SPaolo Bonzini "mv88w8618-eth", MP_ETH_SIZE); 401a77d90e6SAndreas Färber sysbus_init_mmio(sbd, &s->iomem); 402ece71994Sxiaoqiang zhao } 403ece71994Sxiaoqiang zhao 404ece71994Sxiaoqiang zhao static void mv88w8618_eth_realize(DeviceState *dev, Error **errp) 405ece71994Sxiaoqiang zhao { 406ece71994Sxiaoqiang zhao mv88w8618_eth_state *s = MV88W8618_ETH(dev); 407ece71994Sxiaoqiang zhao 408ece71994Sxiaoqiang zhao s->nic = qemu_new_nic(&net_mv88w8618_info, &s->conf, 409ece71994Sxiaoqiang zhao object_get_typename(OBJECT(dev)), dev->id, s); 41024859b68Sbalrog } 41124859b68Sbalrog 412d5b61dddSJan Kiszka static const VMStateDescription mv88w8618_eth_vmsd = { 413d5b61dddSJan Kiszka .name = "mv88w8618_eth", 414d5b61dddSJan Kiszka .version_id = 1, 415d5b61dddSJan Kiszka .minimum_version_id = 1, 416d5b61dddSJan Kiszka .fields = (VMStateField[]) { 417d5b61dddSJan Kiszka VMSTATE_UINT32(smir, mv88w8618_eth_state), 418d5b61dddSJan Kiszka VMSTATE_UINT32(icr, mv88w8618_eth_state), 419d5b61dddSJan Kiszka VMSTATE_UINT32(imr, mv88w8618_eth_state), 420d5b61dddSJan Kiszka VMSTATE_UINT32(vlan_header, mv88w8618_eth_state), 421d5b61dddSJan Kiszka VMSTATE_UINT32_ARRAY(tx_queue, mv88w8618_eth_state, 2), 422d5b61dddSJan Kiszka VMSTATE_UINT32_ARRAY(rx_queue, mv88w8618_eth_state, 4), 423d5b61dddSJan Kiszka VMSTATE_UINT32_ARRAY(frx_queue, mv88w8618_eth_state, 4), 424d5b61dddSJan Kiszka VMSTATE_UINT32_ARRAY(cur_rx, mv88w8618_eth_state, 4), 425d5b61dddSJan Kiszka VMSTATE_END_OF_LIST() 426d5b61dddSJan Kiszka } 427d5b61dddSJan Kiszka }; 428d5b61dddSJan Kiszka 429999e12bbSAnthony Liguori static Property mv88w8618_eth_properties[] = { 4304c91cd28SGerd Hoffmann DEFINE_NIC_PROPERTIES(mv88w8618_eth_state, conf), 4314c91cd28SGerd Hoffmann DEFINE_PROP_END_OF_LIST(), 432999e12bbSAnthony Liguori }; 433999e12bbSAnthony Liguori 434999e12bbSAnthony Liguori static void mv88w8618_eth_class_init(ObjectClass *klass, void *data) 435999e12bbSAnthony Liguori { 43639bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 437999e12bbSAnthony Liguori 43839bffca2SAnthony Liguori dc->vmsd = &mv88w8618_eth_vmsd; 4394f67d30bSMarc-André Lureau device_class_set_props(dc, mv88w8618_eth_properties); 440ece71994Sxiaoqiang zhao dc->realize = mv88w8618_eth_realize; 441999e12bbSAnthony Liguori } 442999e12bbSAnthony Liguori 4438c43a6f0SAndreas Färber static const TypeInfo mv88w8618_eth_info = { 444a77d90e6SAndreas Färber .name = TYPE_MV88W8618_ETH, 44539bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 44639bffca2SAnthony Liguori .instance_size = sizeof(mv88w8618_eth_state), 447ece71994Sxiaoqiang zhao .instance_init = mv88w8618_eth_init, 448999e12bbSAnthony Liguori .class_init = mv88w8618_eth_class_init, 449d5b61dddSJan Kiszka }; 450d5b61dddSJan Kiszka 45124859b68Sbalrog /* LCD register offsets */ 45224859b68Sbalrog #define MP_LCD_IRQCTRL 0x180 45324859b68Sbalrog #define MP_LCD_IRQSTAT 0x184 45424859b68Sbalrog #define MP_LCD_SPICTRL 0x1ac 45524859b68Sbalrog #define MP_LCD_INST 0x1bc 45624859b68Sbalrog #define MP_LCD_DATA 0x1c0 45724859b68Sbalrog 45824859b68Sbalrog /* Mode magics */ 45924859b68Sbalrog #define MP_LCD_SPI_DATA 0x00100011 46024859b68Sbalrog #define MP_LCD_SPI_CMD 0x00104011 46124859b68Sbalrog #define MP_LCD_SPI_INVALID 0x00000000 46224859b68Sbalrog 46324859b68Sbalrog /* Commmands */ 46424859b68Sbalrog #define MP_LCD_INST_SETPAGE0 0xB0 46524859b68Sbalrog /* ... */ 46624859b68Sbalrog #define MP_LCD_INST_SETPAGE7 0xB7 46724859b68Sbalrog 46824859b68Sbalrog #define MP_LCD_TEXTCOLOR 0xe0e0ff /* RRGGBB */ 46924859b68Sbalrog 4702cca58fdSAndreas Färber #define TYPE_MUSICPAL_LCD "musicpal_lcd" 4712cca58fdSAndreas Färber #define MUSICPAL_LCD(obj) \ 4722cca58fdSAndreas Färber OBJECT_CHECK(musicpal_lcd_state, (obj), TYPE_MUSICPAL_LCD) 4732cca58fdSAndreas Färber 47424859b68Sbalrog typedef struct musicpal_lcd_state { 4752cca58fdSAndreas Färber /*< private >*/ 4762cca58fdSAndreas Färber SysBusDevice parent_obj; 4772cca58fdSAndreas Färber /*< public >*/ 4782cca58fdSAndreas Färber 47919b4a424SAvi Kivity MemoryRegion iomem; 480343ec8e4SBenoit Canet uint32_t brightness; 48124859b68Sbalrog uint32_t mode; 48224859b68Sbalrog uint32_t irqctrl; 483d5b61dddSJan Kiszka uint32_t page; 484d5b61dddSJan Kiszka uint32_t page_off; 485c78f7137SGerd Hoffmann QemuConsole *con; 48624859b68Sbalrog uint8_t video_ram[128*64/8]; 48724859b68Sbalrog } musicpal_lcd_state; 48824859b68Sbalrog 489343ec8e4SBenoit Canet static uint8_t scale_lcd_color(musicpal_lcd_state *s, uint8_t col) 49024859b68Sbalrog { 491343ec8e4SBenoit Canet switch (s->brightness) { 492343ec8e4SBenoit Canet case 7: 49324859b68Sbalrog return col; 494343ec8e4SBenoit Canet case 0: 495343ec8e4SBenoit Canet return 0; 496343ec8e4SBenoit Canet default: 497343ec8e4SBenoit Canet return (col * s->brightness) / 7; 49824859b68Sbalrog } 49924859b68Sbalrog } 50024859b68Sbalrog 5010266f2c7Sbalrog #define SET_LCD_PIXEL(depth, type) \ 5020266f2c7Sbalrog static inline void glue(set_lcd_pixel, depth) \ 5030266f2c7Sbalrog (musicpal_lcd_state *s, int x, int y, type col) \ 5040266f2c7Sbalrog { \ 5050266f2c7Sbalrog int dx, dy; \ 506c78f7137SGerd Hoffmann DisplaySurface *surface = qemu_console_surface(s->con); \ 507c78f7137SGerd Hoffmann type *pixel = &((type *) surface_data(surface))[(y * 128 * 3 + x) * 3]; \ 5080266f2c7Sbalrog \ 5090266f2c7Sbalrog for (dy = 0; dy < 3; dy++, pixel += 127 * 3) \ 5100266f2c7Sbalrog for (dx = 0; dx < 3; dx++, pixel++) \ 5110266f2c7Sbalrog *pixel = col; \ 5120266f2c7Sbalrog } 5130266f2c7Sbalrog SET_LCD_PIXEL(8, uint8_t) 5140266f2c7Sbalrog SET_LCD_PIXEL(16, uint16_t) 5150266f2c7Sbalrog SET_LCD_PIXEL(32, uint32_t) 51624859b68Sbalrog 51724859b68Sbalrog static void lcd_refresh(void *opaque) 51824859b68Sbalrog { 51924859b68Sbalrog musicpal_lcd_state *s = opaque; 520c78f7137SGerd Hoffmann DisplaySurface *surface = qemu_console_surface(s->con); 5210266f2c7Sbalrog int x, y, col; 52224859b68Sbalrog 523c78f7137SGerd Hoffmann switch (surface_bits_per_pixel(surface)) { 5240266f2c7Sbalrog case 0: 5250266f2c7Sbalrog return; 5260266f2c7Sbalrog #define LCD_REFRESH(depth, func) \ 5270266f2c7Sbalrog case depth: \ 528343ec8e4SBenoit Canet col = func(scale_lcd_color(s, (MP_LCD_TEXTCOLOR >> 16) & 0xff), \ 529343ec8e4SBenoit Canet scale_lcd_color(s, (MP_LCD_TEXTCOLOR >> 8) & 0xff), \ 530343ec8e4SBenoit Canet scale_lcd_color(s, MP_LCD_TEXTCOLOR & 0xff)); \ 53149fedd0dSJan Kiszka for (x = 0; x < 128; x++) { \ 53249fedd0dSJan Kiszka for (y = 0; y < 64; y++) { \ 53349fedd0dSJan Kiszka if (s->video_ram[x + (y/8)*128] & (1 << (y % 8))) { \ 5340266f2c7Sbalrog glue(set_lcd_pixel, depth)(s, x, y, col); \ 53549fedd0dSJan Kiszka } else { \ 5360266f2c7Sbalrog glue(set_lcd_pixel, depth)(s, x, y, 0); \ 53749fedd0dSJan Kiszka } \ 53849fedd0dSJan Kiszka } \ 53949fedd0dSJan Kiszka } \ 5400266f2c7Sbalrog break; 5410266f2c7Sbalrog LCD_REFRESH(8, rgb_to_pixel8) 5420266f2c7Sbalrog LCD_REFRESH(16, rgb_to_pixel16) 543c78f7137SGerd Hoffmann LCD_REFRESH(32, (is_surface_bgr(surface) ? 544bf9b48afSaliguori rgb_to_pixel32bgr : rgb_to_pixel32)) 5450266f2c7Sbalrog default: 5462ac71179SPaul Brook hw_error("unsupported colour depth %i\n", 547c78f7137SGerd Hoffmann surface_bits_per_pixel(surface)); 5480266f2c7Sbalrog } 54924859b68Sbalrog 550c78f7137SGerd Hoffmann dpy_gfx_update(s->con, 0, 0, 128*3, 64*3); 55124859b68Sbalrog } 55224859b68Sbalrog 553167bc3d2Sbalrog static void lcd_invalidate(void *opaque) 554167bc3d2Sbalrog { 555167bc3d2Sbalrog } 556167bc3d2Sbalrog 5572c79fed3SStefan Weil static void musicpal_lcd_gpio_brightness_in(void *opaque, int irq, int level) 558343ec8e4SBenoit Canet { 559243cd13cSJan Kiszka musicpal_lcd_state *s = opaque; 560343ec8e4SBenoit Canet s->brightness &= ~(1 << irq); 561343ec8e4SBenoit Canet s->brightness |= level << irq; 562343ec8e4SBenoit Canet } 563343ec8e4SBenoit Canet 564a8170e5eSAvi Kivity static uint64_t musicpal_lcd_read(void *opaque, hwaddr offset, 56519b4a424SAvi Kivity unsigned size) 56624859b68Sbalrog { 56724859b68Sbalrog musicpal_lcd_state *s = opaque; 56824859b68Sbalrog 56924859b68Sbalrog switch (offset) { 57024859b68Sbalrog case MP_LCD_IRQCTRL: 57124859b68Sbalrog return s->irqctrl; 57224859b68Sbalrog 57324859b68Sbalrog default: 57424859b68Sbalrog return 0; 57524859b68Sbalrog } 57624859b68Sbalrog } 57724859b68Sbalrog 578a8170e5eSAvi Kivity static void musicpal_lcd_write(void *opaque, hwaddr offset, 57919b4a424SAvi Kivity uint64_t value, unsigned size) 58024859b68Sbalrog { 58124859b68Sbalrog musicpal_lcd_state *s = opaque; 58224859b68Sbalrog 58324859b68Sbalrog switch (offset) { 58424859b68Sbalrog case MP_LCD_IRQCTRL: 58524859b68Sbalrog s->irqctrl = value; 58624859b68Sbalrog break; 58724859b68Sbalrog 58824859b68Sbalrog case MP_LCD_SPICTRL: 58949fedd0dSJan Kiszka if (value == MP_LCD_SPI_DATA || value == MP_LCD_SPI_CMD) { 59024859b68Sbalrog s->mode = value; 59149fedd0dSJan Kiszka } else { 59224859b68Sbalrog s->mode = MP_LCD_SPI_INVALID; 59349fedd0dSJan Kiszka } 59424859b68Sbalrog break; 59524859b68Sbalrog 59624859b68Sbalrog case MP_LCD_INST: 59724859b68Sbalrog if (value >= MP_LCD_INST_SETPAGE0 && value <= MP_LCD_INST_SETPAGE7) { 59824859b68Sbalrog s->page = value - MP_LCD_INST_SETPAGE0; 59924859b68Sbalrog s->page_off = 0; 60024859b68Sbalrog } 60124859b68Sbalrog break; 60224859b68Sbalrog 60324859b68Sbalrog case MP_LCD_DATA: 60424859b68Sbalrog if (s->mode == MP_LCD_SPI_CMD) { 60524859b68Sbalrog if (value >= MP_LCD_INST_SETPAGE0 && 60624859b68Sbalrog value <= MP_LCD_INST_SETPAGE7) { 60724859b68Sbalrog s->page = value - MP_LCD_INST_SETPAGE0; 60824859b68Sbalrog s->page_off = 0; 60924859b68Sbalrog } 61024859b68Sbalrog } else if (s->mode == MP_LCD_SPI_DATA) { 61124859b68Sbalrog s->video_ram[s->page*128 + s->page_off] = value; 61224859b68Sbalrog s->page_off = (s->page_off + 1) & 127; 61324859b68Sbalrog } 61424859b68Sbalrog break; 61524859b68Sbalrog } 61624859b68Sbalrog } 61724859b68Sbalrog 61819b4a424SAvi Kivity static const MemoryRegionOps musicpal_lcd_ops = { 61919b4a424SAvi Kivity .read = musicpal_lcd_read, 62019b4a424SAvi Kivity .write = musicpal_lcd_write, 62119b4a424SAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 62224859b68Sbalrog }; 62324859b68Sbalrog 624380cd056SGerd Hoffmann static const GraphicHwOps musicpal_gfx_ops = { 625380cd056SGerd Hoffmann .invalidate = lcd_invalidate, 626380cd056SGerd Hoffmann .gfx_update = lcd_refresh, 627380cd056SGerd Hoffmann }; 628380cd056SGerd Hoffmann 629ece71994Sxiaoqiang zhao static void musicpal_lcd_realize(DeviceState *dev, Error **errp) 63024859b68Sbalrog { 631ece71994Sxiaoqiang zhao musicpal_lcd_state *s = MUSICPAL_LCD(dev); 632ece71994Sxiaoqiang zhao s->con = graphic_console_init(dev, 0, &musicpal_gfx_ops, s); 633ece71994Sxiaoqiang zhao qemu_console_resize(s->con, 128 * 3, 64 * 3); 634ece71994Sxiaoqiang zhao } 635ece71994Sxiaoqiang zhao 636ece71994Sxiaoqiang zhao static void musicpal_lcd_init(Object *obj) 637ece71994Sxiaoqiang zhao { 638ece71994Sxiaoqiang zhao SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 6392cca58fdSAndreas Färber DeviceState *dev = DEVICE(sbd); 6402cca58fdSAndreas Färber musicpal_lcd_state *s = MUSICPAL_LCD(dev); 64124859b68Sbalrog 642343ec8e4SBenoit Canet s->brightness = 7; 643343ec8e4SBenoit Canet 644ece71994Sxiaoqiang zhao memory_region_init_io(&s->iomem, obj, &musicpal_lcd_ops, s, 64519b4a424SAvi Kivity "musicpal-lcd", MP_LCD_SIZE); 6462cca58fdSAndreas Färber sysbus_init_mmio(sbd, &s->iomem); 64724859b68Sbalrog 6482cca58fdSAndreas Färber qdev_init_gpio_in(dev, musicpal_lcd_gpio_brightness_in, 3); 64924859b68Sbalrog } 65024859b68Sbalrog 651d5b61dddSJan Kiszka static const VMStateDescription musicpal_lcd_vmsd = { 652d5b61dddSJan Kiszka .name = "musicpal_lcd", 653d5b61dddSJan Kiszka .version_id = 1, 654d5b61dddSJan Kiszka .minimum_version_id = 1, 655d5b61dddSJan Kiszka .fields = (VMStateField[]) { 656d5b61dddSJan Kiszka VMSTATE_UINT32(brightness, musicpal_lcd_state), 657d5b61dddSJan Kiszka VMSTATE_UINT32(mode, musicpal_lcd_state), 658d5b61dddSJan Kiszka VMSTATE_UINT32(irqctrl, musicpal_lcd_state), 659d5b61dddSJan Kiszka VMSTATE_UINT32(page, musicpal_lcd_state), 660d5b61dddSJan Kiszka VMSTATE_UINT32(page_off, musicpal_lcd_state), 661d5b61dddSJan Kiszka VMSTATE_BUFFER(video_ram, musicpal_lcd_state), 662d5b61dddSJan Kiszka VMSTATE_END_OF_LIST() 663d5b61dddSJan Kiszka } 664d5b61dddSJan Kiszka }; 665d5b61dddSJan Kiszka 666999e12bbSAnthony Liguori static void musicpal_lcd_class_init(ObjectClass *klass, void *data) 667999e12bbSAnthony Liguori { 66839bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 669999e12bbSAnthony Liguori 67039bffca2SAnthony Liguori dc->vmsd = &musicpal_lcd_vmsd; 671ece71994Sxiaoqiang zhao dc->realize = musicpal_lcd_realize; 672999e12bbSAnthony Liguori } 673999e12bbSAnthony Liguori 6748c43a6f0SAndreas Färber static const TypeInfo musicpal_lcd_info = { 6752cca58fdSAndreas Färber .name = TYPE_MUSICPAL_LCD, 67639bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 67739bffca2SAnthony Liguori .instance_size = sizeof(musicpal_lcd_state), 678ece71994Sxiaoqiang zhao .instance_init = musicpal_lcd_init, 679999e12bbSAnthony Liguori .class_init = musicpal_lcd_class_init, 680d5b61dddSJan Kiszka }; 681d5b61dddSJan Kiszka 68224859b68Sbalrog /* PIC register offsets */ 68324859b68Sbalrog #define MP_PIC_STATUS 0x00 68424859b68Sbalrog #define MP_PIC_ENABLE_SET 0x08 68524859b68Sbalrog #define MP_PIC_ENABLE_CLR 0x0C 68624859b68Sbalrog 687c7bd0fd9SAndreas Färber #define TYPE_MV88W8618_PIC "mv88w8618_pic" 688c7bd0fd9SAndreas Färber #define MV88W8618_PIC(obj) \ 689c7bd0fd9SAndreas Färber OBJECT_CHECK(mv88w8618_pic_state, (obj), TYPE_MV88W8618_PIC) 690c7bd0fd9SAndreas Färber 691c7bd0fd9SAndreas Färber typedef struct mv88w8618_pic_state { 692c7bd0fd9SAndreas Färber /*< private >*/ 693c7bd0fd9SAndreas Färber SysBusDevice parent_obj; 694c7bd0fd9SAndreas Färber /*< public >*/ 695c7bd0fd9SAndreas Färber 69619b4a424SAvi Kivity MemoryRegion iomem; 69724859b68Sbalrog uint32_t level; 69824859b68Sbalrog uint32_t enabled; 69924859b68Sbalrog qemu_irq parent_irq; 70024859b68Sbalrog } mv88w8618_pic_state; 70124859b68Sbalrog 70224859b68Sbalrog static void mv88w8618_pic_update(mv88w8618_pic_state *s) 70324859b68Sbalrog { 70424859b68Sbalrog qemu_set_irq(s->parent_irq, (s->level & s->enabled)); 70524859b68Sbalrog } 70624859b68Sbalrog 70724859b68Sbalrog static void mv88w8618_pic_set_irq(void *opaque, int irq, int level) 70824859b68Sbalrog { 70924859b68Sbalrog mv88w8618_pic_state *s = opaque; 71024859b68Sbalrog 71149fedd0dSJan Kiszka if (level) { 71224859b68Sbalrog s->level |= 1 << irq; 71349fedd0dSJan Kiszka } else { 71424859b68Sbalrog s->level &= ~(1 << irq); 71549fedd0dSJan Kiszka } 71624859b68Sbalrog mv88w8618_pic_update(s); 71724859b68Sbalrog } 71824859b68Sbalrog 719a8170e5eSAvi Kivity static uint64_t mv88w8618_pic_read(void *opaque, hwaddr offset, 72019b4a424SAvi Kivity unsigned size) 72124859b68Sbalrog { 72224859b68Sbalrog mv88w8618_pic_state *s = opaque; 72324859b68Sbalrog 72424859b68Sbalrog switch (offset) { 72524859b68Sbalrog case MP_PIC_STATUS: 72624859b68Sbalrog return s->level & s->enabled; 72724859b68Sbalrog 72824859b68Sbalrog default: 72924859b68Sbalrog return 0; 73024859b68Sbalrog } 73124859b68Sbalrog } 73224859b68Sbalrog 733a8170e5eSAvi Kivity static void mv88w8618_pic_write(void *opaque, hwaddr offset, 73419b4a424SAvi Kivity uint64_t value, unsigned size) 73524859b68Sbalrog { 73624859b68Sbalrog mv88w8618_pic_state *s = opaque; 73724859b68Sbalrog 73824859b68Sbalrog switch (offset) { 73924859b68Sbalrog case MP_PIC_ENABLE_SET: 74024859b68Sbalrog s->enabled |= value; 74124859b68Sbalrog break; 74224859b68Sbalrog 74324859b68Sbalrog case MP_PIC_ENABLE_CLR: 74424859b68Sbalrog s->enabled &= ~value; 74524859b68Sbalrog s->level &= ~value; 74624859b68Sbalrog break; 74724859b68Sbalrog } 74824859b68Sbalrog mv88w8618_pic_update(s); 74924859b68Sbalrog } 75024859b68Sbalrog 751d5b61dddSJan Kiszka static void mv88w8618_pic_reset(DeviceState *d) 75224859b68Sbalrog { 753c7bd0fd9SAndreas Färber mv88w8618_pic_state *s = MV88W8618_PIC(d); 75424859b68Sbalrog 75524859b68Sbalrog s->level = 0; 75624859b68Sbalrog s->enabled = 0; 75724859b68Sbalrog } 75824859b68Sbalrog 75919b4a424SAvi Kivity static const MemoryRegionOps mv88w8618_pic_ops = { 76019b4a424SAvi Kivity .read = mv88w8618_pic_read, 76119b4a424SAvi Kivity .write = mv88w8618_pic_write, 76219b4a424SAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 76324859b68Sbalrog }; 76424859b68Sbalrog 765ece71994Sxiaoqiang zhao static void mv88w8618_pic_init(Object *obj) 76624859b68Sbalrog { 767ece71994Sxiaoqiang zhao SysBusDevice *dev = SYS_BUS_DEVICE(obj); 768c7bd0fd9SAndreas Färber mv88w8618_pic_state *s = MV88W8618_PIC(dev); 76924859b68Sbalrog 770c7bd0fd9SAndreas Färber qdev_init_gpio_in(DEVICE(dev), mv88w8618_pic_set_irq, 32); 771b47b50faSPaul Brook sysbus_init_irq(dev, &s->parent_irq); 772ece71994Sxiaoqiang zhao memory_region_init_io(&s->iomem, obj, &mv88w8618_pic_ops, s, 77319b4a424SAvi Kivity "musicpal-pic", MP_PIC_SIZE); 774750ecd44SAvi Kivity sysbus_init_mmio(dev, &s->iomem); 77524859b68Sbalrog } 77624859b68Sbalrog 777d5b61dddSJan Kiszka static const VMStateDescription mv88w8618_pic_vmsd = { 778d5b61dddSJan Kiszka .name = "mv88w8618_pic", 779d5b61dddSJan Kiszka .version_id = 1, 780d5b61dddSJan Kiszka .minimum_version_id = 1, 781d5b61dddSJan Kiszka .fields = (VMStateField[]) { 782d5b61dddSJan Kiszka VMSTATE_UINT32(level, mv88w8618_pic_state), 783d5b61dddSJan Kiszka VMSTATE_UINT32(enabled, mv88w8618_pic_state), 784d5b61dddSJan Kiszka VMSTATE_END_OF_LIST() 785d5b61dddSJan Kiszka } 786d5b61dddSJan Kiszka }; 787d5b61dddSJan Kiszka 788999e12bbSAnthony Liguori static void mv88w8618_pic_class_init(ObjectClass *klass, void *data) 789999e12bbSAnthony Liguori { 79039bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 791999e12bbSAnthony Liguori 79239bffca2SAnthony Liguori dc->reset = mv88w8618_pic_reset; 79339bffca2SAnthony Liguori dc->vmsd = &mv88w8618_pic_vmsd; 794999e12bbSAnthony Liguori } 795999e12bbSAnthony Liguori 7968c43a6f0SAndreas Färber static const TypeInfo mv88w8618_pic_info = { 797c7bd0fd9SAndreas Färber .name = TYPE_MV88W8618_PIC, 79839bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 79939bffca2SAnthony Liguori .instance_size = sizeof(mv88w8618_pic_state), 800ece71994Sxiaoqiang zhao .instance_init = mv88w8618_pic_init, 801999e12bbSAnthony Liguori .class_init = mv88w8618_pic_class_init, 802d5b61dddSJan Kiszka }; 803d5b61dddSJan Kiszka 80424859b68Sbalrog /* PIT register offsets */ 80524859b68Sbalrog #define MP_PIT_TIMER1_LENGTH 0x00 80624859b68Sbalrog /* ... */ 80724859b68Sbalrog #define MP_PIT_TIMER4_LENGTH 0x0C 80824859b68Sbalrog #define MP_PIT_CONTROL 0x10 80924859b68Sbalrog #define MP_PIT_TIMER1_VALUE 0x14 81024859b68Sbalrog /* ... */ 81124859b68Sbalrog #define MP_PIT_TIMER4_VALUE 0x20 81224859b68Sbalrog #define MP_BOARD_RESET 0x34 81324859b68Sbalrog 81424859b68Sbalrog /* Magic board reset value (probably some watchdog behind it) */ 81524859b68Sbalrog #define MP_BOARD_RESET_MAGIC 0x10000 81624859b68Sbalrog 81724859b68Sbalrog typedef struct mv88w8618_timer_state { 818b47b50faSPaul Brook ptimer_state *ptimer; 81924859b68Sbalrog uint32_t limit; 82024859b68Sbalrog int freq; 82124859b68Sbalrog qemu_irq irq; 82224859b68Sbalrog } mv88w8618_timer_state; 82324859b68Sbalrog 8244adc8541SAndreas Färber #define TYPE_MV88W8618_PIT "mv88w8618_pit" 8254adc8541SAndreas Färber #define MV88W8618_PIT(obj) \ 8264adc8541SAndreas Färber OBJECT_CHECK(mv88w8618_pit_state, (obj), TYPE_MV88W8618_PIT) 8274adc8541SAndreas Färber 82824859b68Sbalrog typedef struct mv88w8618_pit_state { 8294adc8541SAndreas Färber /*< private >*/ 8304adc8541SAndreas Färber SysBusDevice parent_obj; 8314adc8541SAndreas Färber /*< public >*/ 8324adc8541SAndreas Färber 83319b4a424SAvi Kivity MemoryRegion iomem; 834b47b50faSPaul Brook mv88w8618_timer_state timer[4]; 83524859b68Sbalrog } mv88w8618_pit_state; 83624859b68Sbalrog 83724859b68Sbalrog static void mv88w8618_timer_tick(void *opaque) 83824859b68Sbalrog { 83924859b68Sbalrog mv88w8618_timer_state *s = opaque; 84024859b68Sbalrog 84124859b68Sbalrog qemu_irq_raise(s->irq); 84224859b68Sbalrog } 84324859b68Sbalrog 844b47b50faSPaul Brook static void mv88w8618_timer_init(SysBusDevice *dev, mv88w8618_timer_state *s, 845b47b50faSPaul Brook uint32_t freq) 84624859b68Sbalrog { 847b47b50faSPaul Brook sysbus_init_irq(dev, &s->irq); 84824859b68Sbalrog s->freq = freq; 84924859b68Sbalrog 850d8052a2eSPeter Maydell s->ptimer = ptimer_init(mv88w8618_timer_tick, s, PTIMER_POLICY_DEFAULT); 85124859b68Sbalrog } 85224859b68Sbalrog 853a8170e5eSAvi Kivity static uint64_t mv88w8618_pit_read(void *opaque, hwaddr offset, 85419b4a424SAvi Kivity unsigned size) 85524859b68Sbalrog { 85624859b68Sbalrog mv88w8618_pit_state *s = opaque; 85724859b68Sbalrog mv88w8618_timer_state *t; 85824859b68Sbalrog 85924859b68Sbalrog switch (offset) { 86024859b68Sbalrog case MP_PIT_TIMER1_VALUE ... MP_PIT_TIMER4_VALUE: 861b47b50faSPaul Brook t = &s->timer[(offset-MP_PIT_TIMER1_VALUE) >> 2]; 862b47b50faSPaul Brook return ptimer_get_count(t->ptimer); 86324859b68Sbalrog 86424859b68Sbalrog default: 86524859b68Sbalrog return 0; 86624859b68Sbalrog } 86724859b68Sbalrog } 86824859b68Sbalrog 869a8170e5eSAvi Kivity static void mv88w8618_pit_write(void *opaque, hwaddr offset, 87019b4a424SAvi Kivity uint64_t value, unsigned size) 87124859b68Sbalrog { 87224859b68Sbalrog mv88w8618_pit_state *s = opaque; 87324859b68Sbalrog mv88w8618_timer_state *t; 87424859b68Sbalrog int i; 87524859b68Sbalrog 87624859b68Sbalrog switch (offset) { 87724859b68Sbalrog case MP_PIT_TIMER1_LENGTH ... MP_PIT_TIMER4_LENGTH: 878b47b50faSPaul Brook t = &s->timer[offset >> 2]; 87924859b68Sbalrog t->limit = value; 880d8052a2eSPeter Maydell ptimer_transaction_begin(t->ptimer); 881c88d6bdeSJan Kiszka if (t->limit > 0) { 882b47b50faSPaul Brook ptimer_set_limit(t->ptimer, t->limit, 1); 883c88d6bdeSJan Kiszka } else { 884c88d6bdeSJan Kiszka ptimer_stop(t->ptimer); 885c88d6bdeSJan Kiszka } 886d8052a2eSPeter Maydell ptimer_transaction_commit(t->ptimer); 88724859b68Sbalrog break; 88824859b68Sbalrog 88924859b68Sbalrog case MP_PIT_CONTROL: 89024859b68Sbalrog for (i = 0; i < 4; i++) { 891b47b50faSPaul Brook t = &s->timer[i]; 892d8052a2eSPeter Maydell ptimer_transaction_begin(t->ptimer); 893c88d6bdeSJan Kiszka if (value & 0xf && t->limit > 0) { 894b47b50faSPaul Brook ptimer_set_limit(t->ptimer, t->limit, 0); 895b47b50faSPaul Brook ptimer_set_freq(t->ptimer, t->freq); 896b47b50faSPaul Brook ptimer_run(t->ptimer, 0); 897c88d6bdeSJan Kiszka } else { 898c88d6bdeSJan Kiszka ptimer_stop(t->ptimer); 89924859b68Sbalrog } 900d8052a2eSPeter Maydell ptimer_transaction_commit(t->ptimer); 90124859b68Sbalrog value >>= 4; 90224859b68Sbalrog } 90324859b68Sbalrog break; 90424859b68Sbalrog 90524859b68Sbalrog case MP_BOARD_RESET: 90649fedd0dSJan Kiszka if (value == MP_BOARD_RESET_MAGIC) { 907cf83f140SEric Blake qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); 90849fedd0dSJan Kiszka } 90924859b68Sbalrog break; 91024859b68Sbalrog } 91124859b68Sbalrog } 91224859b68Sbalrog 913d5b61dddSJan Kiszka static void mv88w8618_pit_reset(DeviceState *d) 914c88d6bdeSJan Kiszka { 9154adc8541SAndreas Färber mv88w8618_pit_state *s = MV88W8618_PIT(d); 916c88d6bdeSJan Kiszka int i; 917c88d6bdeSJan Kiszka 918c88d6bdeSJan Kiszka for (i = 0; i < 4; i++) { 919d8052a2eSPeter Maydell mv88w8618_timer_state *t = &s->timer[i]; 920d8052a2eSPeter Maydell ptimer_transaction_begin(t->ptimer); 921d8052a2eSPeter Maydell ptimer_stop(t->ptimer); 922d8052a2eSPeter Maydell ptimer_transaction_commit(t->ptimer); 923d8052a2eSPeter Maydell t->limit = 0; 924c88d6bdeSJan Kiszka } 925c88d6bdeSJan Kiszka } 926c88d6bdeSJan Kiszka 92719b4a424SAvi Kivity static const MemoryRegionOps mv88w8618_pit_ops = { 92819b4a424SAvi Kivity .read = mv88w8618_pit_read, 92919b4a424SAvi Kivity .write = mv88w8618_pit_write, 93019b4a424SAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 93124859b68Sbalrog }; 93224859b68Sbalrog 933ece71994Sxiaoqiang zhao static void mv88w8618_pit_init(Object *obj) 93424859b68Sbalrog { 935ece71994Sxiaoqiang zhao SysBusDevice *dev = SYS_BUS_DEVICE(obj); 9364adc8541SAndreas Färber mv88w8618_pit_state *s = MV88W8618_PIT(dev); 937b47b50faSPaul Brook int i; 93824859b68Sbalrog 93924859b68Sbalrog /* Letting them all run at 1 MHz is likely just a pragmatic 94024859b68Sbalrog * simplification. */ 941b47b50faSPaul Brook for (i = 0; i < 4; i++) { 942b47b50faSPaul Brook mv88w8618_timer_init(dev, &s->timer[i], 1000000); 943b47b50faSPaul Brook } 94424859b68Sbalrog 945ece71994Sxiaoqiang zhao memory_region_init_io(&s->iomem, obj, &mv88w8618_pit_ops, s, 94619b4a424SAvi Kivity "musicpal-pit", MP_PIT_SIZE); 947750ecd44SAvi Kivity sysbus_init_mmio(dev, &s->iomem); 94824859b68Sbalrog } 94924859b68Sbalrog 950d5b61dddSJan Kiszka static const VMStateDescription mv88w8618_timer_vmsd = { 951d5b61dddSJan Kiszka .name = "timer", 952d5b61dddSJan Kiszka .version_id = 1, 953d5b61dddSJan Kiszka .minimum_version_id = 1, 954d5b61dddSJan Kiszka .fields = (VMStateField[]) { 955d5b61dddSJan Kiszka VMSTATE_PTIMER(ptimer, mv88w8618_timer_state), 956d5b61dddSJan Kiszka VMSTATE_UINT32(limit, mv88w8618_timer_state), 957d5b61dddSJan Kiszka VMSTATE_END_OF_LIST() 958d5b61dddSJan Kiszka } 959d5b61dddSJan Kiszka }; 960d5b61dddSJan Kiszka 961d5b61dddSJan Kiszka static const VMStateDescription mv88w8618_pit_vmsd = { 962d5b61dddSJan Kiszka .name = "mv88w8618_pit", 963d5b61dddSJan Kiszka .version_id = 1, 964d5b61dddSJan Kiszka .minimum_version_id = 1, 965d5b61dddSJan Kiszka .fields = (VMStateField[]) { 966d5b61dddSJan Kiszka VMSTATE_STRUCT_ARRAY(timer, mv88w8618_pit_state, 4, 1, 967d5b61dddSJan Kiszka mv88w8618_timer_vmsd, mv88w8618_timer_state), 968d5b61dddSJan Kiszka VMSTATE_END_OF_LIST() 969d5b61dddSJan Kiszka } 970d5b61dddSJan Kiszka }; 971d5b61dddSJan Kiszka 972999e12bbSAnthony Liguori static void mv88w8618_pit_class_init(ObjectClass *klass, void *data) 973999e12bbSAnthony Liguori { 97439bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 975999e12bbSAnthony Liguori 97639bffca2SAnthony Liguori dc->reset = mv88w8618_pit_reset; 97739bffca2SAnthony Liguori dc->vmsd = &mv88w8618_pit_vmsd; 978999e12bbSAnthony Liguori } 979999e12bbSAnthony Liguori 9808c43a6f0SAndreas Färber static const TypeInfo mv88w8618_pit_info = { 9814adc8541SAndreas Färber .name = TYPE_MV88W8618_PIT, 98239bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 98339bffca2SAnthony Liguori .instance_size = sizeof(mv88w8618_pit_state), 984ece71994Sxiaoqiang zhao .instance_init = mv88w8618_pit_init, 985999e12bbSAnthony Liguori .class_init = mv88w8618_pit_class_init, 986c88d6bdeSJan Kiszka }; 987c88d6bdeSJan Kiszka 98824859b68Sbalrog /* Flash config register offsets */ 98924859b68Sbalrog #define MP_FLASHCFG_CFGR0 0x04 99024859b68Sbalrog 9915952b01cSAndreas Färber #define TYPE_MV88W8618_FLASHCFG "mv88w8618_flashcfg" 9925952b01cSAndreas Färber #define MV88W8618_FLASHCFG(obj) \ 9935952b01cSAndreas Färber OBJECT_CHECK(mv88w8618_flashcfg_state, (obj), TYPE_MV88W8618_FLASHCFG) 9945952b01cSAndreas Färber 99524859b68Sbalrog typedef struct mv88w8618_flashcfg_state { 9965952b01cSAndreas Färber /*< private >*/ 9975952b01cSAndreas Färber SysBusDevice parent_obj; 9985952b01cSAndreas Färber /*< public >*/ 9995952b01cSAndreas Färber 100019b4a424SAvi Kivity MemoryRegion iomem; 100124859b68Sbalrog uint32_t cfgr0; 100224859b68Sbalrog } mv88w8618_flashcfg_state; 100324859b68Sbalrog 100419b4a424SAvi Kivity static uint64_t mv88w8618_flashcfg_read(void *opaque, 1005a8170e5eSAvi Kivity hwaddr offset, 100619b4a424SAvi Kivity unsigned size) 100724859b68Sbalrog { 100824859b68Sbalrog mv88w8618_flashcfg_state *s = opaque; 100924859b68Sbalrog 101024859b68Sbalrog switch (offset) { 101124859b68Sbalrog case MP_FLASHCFG_CFGR0: 101224859b68Sbalrog return s->cfgr0; 101324859b68Sbalrog 101424859b68Sbalrog default: 101524859b68Sbalrog return 0; 101624859b68Sbalrog } 101724859b68Sbalrog } 101824859b68Sbalrog 1019a8170e5eSAvi Kivity static void mv88w8618_flashcfg_write(void *opaque, hwaddr offset, 102019b4a424SAvi Kivity uint64_t value, unsigned size) 102124859b68Sbalrog { 102224859b68Sbalrog mv88w8618_flashcfg_state *s = opaque; 102324859b68Sbalrog 102424859b68Sbalrog switch (offset) { 102524859b68Sbalrog case MP_FLASHCFG_CFGR0: 102624859b68Sbalrog s->cfgr0 = value; 102724859b68Sbalrog break; 102824859b68Sbalrog } 102924859b68Sbalrog } 103024859b68Sbalrog 103119b4a424SAvi Kivity static const MemoryRegionOps mv88w8618_flashcfg_ops = { 103219b4a424SAvi Kivity .read = mv88w8618_flashcfg_read, 103319b4a424SAvi Kivity .write = mv88w8618_flashcfg_write, 103419b4a424SAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 103524859b68Sbalrog }; 103624859b68Sbalrog 1037ece71994Sxiaoqiang zhao static void mv88w8618_flashcfg_init(Object *obj) 103824859b68Sbalrog { 1039ece71994Sxiaoqiang zhao SysBusDevice *dev = SYS_BUS_DEVICE(obj); 10405952b01cSAndreas Färber mv88w8618_flashcfg_state *s = MV88W8618_FLASHCFG(dev); 104124859b68Sbalrog 104224859b68Sbalrog s->cfgr0 = 0xfffe4285; /* Default as set by U-Boot for 8 MB flash */ 1043ece71994Sxiaoqiang zhao memory_region_init_io(&s->iomem, obj, &mv88w8618_flashcfg_ops, s, 104419b4a424SAvi Kivity "musicpal-flashcfg", MP_FLASHCFG_SIZE); 1045750ecd44SAvi Kivity sysbus_init_mmio(dev, &s->iomem); 104624859b68Sbalrog } 104724859b68Sbalrog 1048d5b61dddSJan Kiszka static const VMStateDescription mv88w8618_flashcfg_vmsd = { 1049d5b61dddSJan Kiszka .name = "mv88w8618_flashcfg", 1050d5b61dddSJan Kiszka .version_id = 1, 1051d5b61dddSJan Kiszka .minimum_version_id = 1, 1052d5b61dddSJan Kiszka .fields = (VMStateField[]) { 1053d5b61dddSJan Kiszka VMSTATE_UINT32(cfgr0, mv88w8618_flashcfg_state), 1054d5b61dddSJan Kiszka VMSTATE_END_OF_LIST() 1055d5b61dddSJan Kiszka } 1056d5b61dddSJan Kiszka }; 1057d5b61dddSJan Kiszka 1058999e12bbSAnthony Liguori static void mv88w8618_flashcfg_class_init(ObjectClass *klass, void *data) 1059999e12bbSAnthony Liguori { 106039bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 1061999e12bbSAnthony Liguori 106239bffca2SAnthony Liguori dc->vmsd = &mv88w8618_flashcfg_vmsd; 1063999e12bbSAnthony Liguori } 1064999e12bbSAnthony Liguori 10658c43a6f0SAndreas Färber static const TypeInfo mv88w8618_flashcfg_info = { 10665952b01cSAndreas Färber .name = TYPE_MV88W8618_FLASHCFG, 106739bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 106839bffca2SAnthony Liguori .instance_size = sizeof(mv88w8618_flashcfg_state), 1069ece71994Sxiaoqiang zhao .instance_init = mv88w8618_flashcfg_init, 1070999e12bbSAnthony Liguori .class_init = mv88w8618_flashcfg_class_init, 1071d5b61dddSJan Kiszka }; 1072d5b61dddSJan Kiszka 1073718ec0beSmalc /* Misc register offsets */ 1074718ec0beSmalc #define MP_MISC_BOARD_REVISION 0x18 107524859b68Sbalrog 1076718ec0beSmalc #define MP_BOARD_REVISION 0x31 107724859b68Sbalrog 1078a86f200aSPeter Maydell typedef struct { 1079a86f200aSPeter Maydell SysBusDevice parent_obj; 1080a86f200aSPeter Maydell MemoryRegion iomem; 1081a86f200aSPeter Maydell } MusicPalMiscState; 1082a86f200aSPeter Maydell 1083a86f200aSPeter Maydell #define TYPE_MUSICPAL_MISC "musicpal-misc" 1084a86f200aSPeter Maydell #define MUSICPAL_MISC(obj) \ 1085a86f200aSPeter Maydell OBJECT_CHECK(MusicPalMiscState, (obj), TYPE_MUSICPAL_MISC) 1086a86f200aSPeter Maydell 1087a8170e5eSAvi Kivity static uint64_t musicpal_misc_read(void *opaque, hwaddr offset, 108819b4a424SAvi Kivity unsigned size) 1089718ec0beSmalc { 1090718ec0beSmalc switch (offset) { 1091718ec0beSmalc case MP_MISC_BOARD_REVISION: 1092718ec0beSmalc return MP_BOARD_REVISION; 1093718ec0beSmalc 1094718ec0beSmalc default: 1095718ec0beSmalc return 0; 1096718ec0beSmalc } 1097718ec0beSmalc } 1098718ec0beSmalc 1099a8170e5eSAvi Kivity static void musicpal_misc_write(void *opaque, hwaddr offset, 110019b4a424SAvi Kivity uint64_t value, unsigned size) 1101718ec0beSmalc { 1102718ec0beSmalc } 1103718ec0beSmalc 110419b4a424SAvi Kivity static const MemoryRegionOps musicpal_misc_ops = { 110519b4a424SAvi Kivity .read = musicpal_misc_read, 110619b4a424SAvi Kivity .write = musicpal_misc_write, 110719b4a424SAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 1108718ec0beSmalc }; 1109718ec0beSmalc 1110a86f200aSPeter Maydell static void musicpal_misc_init(Object *obj) 1111718ec0beSmalc { 1112a86f200aSPeter Maydell SysBusDevice *sd = SYS_BUS_DEVICE(obj); 1113a86f200aSPeter Maydell MusicPalMiscState *s = MUSICPAL_MISC(obj); 1114718ec0beSmalc 111564bde0f3SPaolo Bonzini memory_region_init_io(&s->iomem, OBJECT(s), &musicpal_misc_ops, NULL, 111619b4a424SAvi Kivity "musicpal-misc", MP_MISC_SIZE); 1117a86f200aSPeter Maydell sysbus_init_mmio(sd, &s->iomem); 1118718ec0beSmalc } 1119718ec0beSmalc 1120a86f200aSPeter Maydell static const TypeInfo musicpal_misc_info = { 1121a86f200aSPeter Maydell .name = TYPE_MUSICPAL_MISC, 1122a86f200aSPeter Maydell .parent = TYPE_SYS_BUS_DEVICE, 1123a86f200aSPeter Maydell .instance_init = musicpal_misc_init, 1124a86f200aSPeter Maydell .instance_size = sizeof(MusicPalMiscState), 1125a86f200aSPeter Maydell }; 1126a86f200aSPeter Maydell 1127718ec0beSmalc /* WLAN register offsets */ 1128718ec0beSmalc #define MP_WLAN_MAGIC1 0x11c 1129718ec0beSmalc #define MP_WLAN_MAGIC2 0x124 1130718ec0beSmalc 1131a8170e5eSAvi Kivity static uint64_t mv88w8618_wlan_read(void *opaque, hwaddr offset, 113219b4a424SAvi Kivity unsigned size) 1133718ec0beSmalc { 1134718ec0beSmalc switch (offset) { 1135718ec0beSmalc /* Workaround to allow loading the binary-only wlandrv.ko crap 1136718ec0beSmalc * from the original Freecom firmware. */ 1137718ec0beSmalc case MP_WLAN_MAGIC1: 1138718ec0beSmalc return ~3; 1139718ec0beSmalc case MP_WLAN_MAGIC2: 1140718ec0beSmalc return -1; 1141718ec0beSmalc 1142718ec0beSmalc default: 1143718ec0beSmalc return 0; 1144718ec0beSmalc } 1145718ec0beSmalc } 1146718ec0beSmalc 1147a8170e5eSAvi Kivity static void mv88w8618_wlan_write(void *opaque, hwaddr offset, 114819b4a424SAvi Kivity uint64_t value, unsigned size) 1149718ec0beSmalc { 1150718ec0beSmalc } 1151718ec0beSmalc 115219b4a424SAvi Kivity static const MemoryRegionOps mv88w8618_wlan_ops = { 115319b4a424SAvi Kivity .read = mv88w8618_wlan_read, 115419b4a424SAvi Kivity .write =mv88w8618_wlan_write, 115519b4a424SAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 1156718ec0beSmalc }; 1157718ec0beSmalc 11587f7420a0SMao Zhongyi static void mv88w8618_wlan_realize(DeviceState *dev, Error **errp) 1159718ec0beSmalc { 116019b4a424SAvi Kivity MemoryRegion *iomem = g_new(MemoryRegion, 1); 1161718ec0beSmalc 116264bde0f3SPaolo Bonzini memory_region_init_io(iomem, OBJECT(dev), &mv88w8618_wlan_ops, NULL, 116319b4a424SAvi Kivity "musicpal-wlan", MP_WLAN_SIZE); 11647f7420a0SMao Zhongyi sysbus_init_mmio(SYS_BUS_DEVICE(dev), iomem); 1165718ec0beSmalc } 1166718ec0beSmalc 1167718ec0beSmalc /* GPIO register offsets */ 1168718ec0beSmalc #define MP_GPIO_OE_LO 0x008 1169718ec0beSmalc #define MP_GPIO_OUT_LO 0x00c 1170718ec0beSmalc #define MP_GPIO_IN_LO 0x010 1171708afdf3SJan Kiszka #define MP_GPIO_IER_LO 0x014 1172708afdf3SJan Kiszka #define MP_GPIO_IMR_LO 0x018 1173718ec0beSmalc #define MP_GPIO_ISR_LO 0x020 1174718ec0beSmalc #define MP_GPIO_OE_HI 0x508 1175718ec0beSmalc #define MP_GPIO_OUT_HI 0x50c 1176718ec0beSmalc #define MP_GPIO_IN_HI 0x510 1177708afdf3SJan Kiszka #define MP_GPIO_IER_HI 0x514 1178708afdf3SJan Kiszka #define MP_GPIO_IMR_HI 0x518 1179718ec0beSmalc #define MP_GPIO_ISR_HI 0x520 118024859b68Sbalrog 118124859b68Sbalrog /* GPIO bits & masks */ 118224859b68Sbalrog #define MP_GPIO_LCD_BRIGHTNESS 0x00070000 118324859b68Sbalrog #define MP_GPIO_I2C_DATA_BIT 29 118424859b68Sbalrog #define MP_GPIO_I2C_CLOCK_BIT 30 118524859b68Sbalrog 118624859b68Sbalrog /* LCD brightness bits in GPIO_OE_HI */ 118724859b68Sbalrog #define MP_OE_LCD_BRIGHTNESS 0x0007 118824859b68Sbalrog 11897012d4b4SAndreas Färber #define TYPE_MUSICPAL_GPIO "musicpal_gpio" 11907012d4b4SAndreas Färber #define MUSICPAL_GPIO(obj) \ 11917012d4b4SAndreas Färber OBJECT_CHECK(musicpal_gpio_state, (obj), TYPE_MUSICPAL_GPIO) 11927012d4b4SAndreas Färber 1193343ec8e4SBenoit Canet typedef struct musicpal_gpio_state { 11947012d4b4SAndreas Färber /*< private >*/ 11957012d4b4SAndreas Färber SysBusDevice parent_obj; 11967012d4b4SAndreas Färber /*< public >*/ 11977012d4b4SAndreas Färber 119819b4a424SAvi Kivity MemoryRegion iomem; 1199343ec8e4SBenoit Canet uint32_t lcd_brightness; 1200343ec8e4SBenoit Canet uint32_t out_state; 1201343ec8e4SBenoit Canet uint32_t in_state; 1202708afdf3SJan Kiszka uint32_t ier; 1203708afdf3SJan Kiszka uint32_t imr; 1204343ec8e4SBenoit Canet uint32_t isr; 1205343ec8e4SBenoit Canet qemu_irq irq; 1206708afdf3SJan Kiszka qemu_irq out[5]; /* 3 brightness out + 2 lcd (data and clock ) */ 1207343ec8e4SBenoit Canet } musicpal_gpio_state; 1208343ec8e4SBenoit Canet 1209343ec8e4SBenoit Canet static void musicpal_gpio_brightness_update(musicpal_gpio_state *s) { 1210343ec8e4SBenoit Canet int i; 1211343ec8e4SBenoit Canet uint32_t brightness; 1212343ec8e4SBenoit Canet 1213343ec8e4SBenoit Canet /* compute brightness ratio */ 1214343ec8e4SBenoit Canet switch (s->lcd_brightness) { 1215343ec8e4SBenoit Canet case 0x00000007: 1216343ec8e4SBenoit Canet brightness = 0; 1217343ec8e4SBenoit Canet break; 1218343ec8e4SBenoit Canet 1219343ec8e4SBenoit Canet case 0x00020000: 1220343ec8e4SBenoit Canet brightness = 1; 1221343ec8e4SBenoit Canet break; 1222343ec8e4SBenoit Canet 1223343ec8e4SBenoit Canet case 0x00020001: 1224343ec8e4SBenoit Canet brightness = 2; 1225343ec8e4SBenoit Canet break; 1226343ec8e4SBenoit Canet 1227343ec8e4SBenoit Canet case 0x00040000: 1228343ec8e4SBenoit Canet brightness = 3; 1229343ec8e4SBenoit Canet break; 1230343ec8e4SBenoit Canet 1231343ec8e4SBenoit Canet case 0x00010006: 1232343ec8e4SBenoit Canet brightness = 4; 1233343ec8e4SBenoit Canet break; 1234343ec8e4SBenoit Canet 1235343ec8e4SBenoit Canet case 0x00020005: 1236343ec8e4SBenoit Canet brightness = 5; 1237343ec8e4SBenoit Canet break; 1238343ec8e4SBenoit Canet 1239343ec8e4SBenoit Canet case 0x00040003: 1240343ec8e4SBenoit Canet brightness = 6; 1241343ec8e4SBenoit Canet break; 1242343ec8e4SBenoit Canet 1243343ec8e4SBenoit Canet case 0x00030004: 1244343ec8e4SBenoit Canet default: 1245343ec8e4SBenoit Canet brightness = 7; 1246343ec8e4SBenoit Canet } 1247343ec8e4SBenoit Canet 1248343ec8e4SBenoit Canet /* set lcd brightness GPIOs */ 124949fedd0dSJan Kiszka for (i = 0; i <= 2; i++) { 1250343ec8e4SBenoit Canet qemu_set_irq(s->out[i], (brightness >> i) & 1); 1251343ec8e4SBenoit Canet } 125249fedd0dSJan Kiszka } 1253343ec8e4SBenoit Canet 1254708afdf3SJan Kiszka static void musicpal_gpio_pin_event(void *opaque, int pin, int level) 1255343ec8e4SBenoit Canet { 1256243cd13cSJan Kiszka musicpal_gpio_state *s = opaque; 1257708afdf3SJan Kiszka uint32_t mask = 1 << pin; 1258708afdf3SJan Kiszka uint32_t delta = level << pin; 1259708afdf3SJan Kiszka uint32_t old = s->in_state & mask; 1260343ec8e4SBenoit Canet 1261708afdf3SJan Kiszka s->in_state &= ~mask; 1262708afdf3SJan Kiszka s->in_state |= delta; 1263708afdf3SJan Kiszka 1264708afdf3SJan Kiszka if ((old ^ delta) && 1265708afdf3SJan Kiszka ((level && (s->imr & mask)) || (!level && (s->ier & mask)))) { 1266708afdf3SJan Kiszka s->isr = mask; 1267708afdf3SJan Kiszka qemu_irq_raise(s->irq); 1268d074769cSAndrzej Zaborowski } 1269343ec8e4SBenoit Canet } 1270343ec8e4SBenoit Canet 1271a8170e5eSAvi Kivity static uint64_t musicpal_gpio_read(void *opaque, hwaddr offset, 127219b4a424SAvi Kivity unsigned size) 127324859b68Sbalrog { 1274243cd13cSJan Kiszka musicpal_gpio_state *s = opaque; 1275343ec8e4SBenoit Canet 127624859b68Sbalrog switch (offset) { 127724859b68Sbalrog case MP_GPIO_OE_HI: /* used for LCD brightness control */ 1278343ec8e4SBenoit Canet return s->lcd_brightness & MP_OE_LCD_BRIGHTNESS; 127924859b68Sbalrog 128024859b68Sbalrog case MP_GPIO_OUT_LO: 1281343ec8e4SBenoit Canet return s->out_state & 0xFFFF; 128224859b68Sbalrog case MP_GPIO_OUT_HI: 1283343ec8e4SBenoit Canet return s->out_state >> 16; 128424859b68Sbalrog 128524859b68Sbalrog case MP_GPIO_IN_LO: 1286343ec8e4SBenoit Canet return s->in_state & 0xFFFF; 128724859b68Sbalrog case MP_GPIO_IN_HI: 1288343ec8e4SBenoit Canet return s->in_state >> 16; 128924859b68Sbalrog 1290708afdf3SJan Kiszka case MP_GPIO_IER_LO: 1291708afdf3SJan Kiszka return s->ier & 0xFFFF; 1292708afdf3SJan Kiszka case MP_GPIO_IER_HI: 1293708afdf3SJan Kiszka return s->ier >> 16; 1294708afdf3SJan Kiszka 1295708afdf3SJan Kiszka case MP_GPIO_IMR_LO: 1296708afdf3SJan Kiszka return s->imr & 0xFFFF; 1297708afdf3SJan Kiszka case MP_GPIO_IMR_HI: 1298708afdf3SJan Kiszka return s->imr >> 16; 1299708afdf3SJan Kiszka 130024859b68Sbalrog case MP_GPIO_ISR_LO: 1301343ec8e4SBenoit Canet return s->isr & 0xFFFF; 130224859b68Sbalrog case MP_GPIO_ISR_HI: 1303343ec8e4SBenoit Canet return s->isr >> 16; 130424859b68Sbalrog 130524859b68Sbalrog default: 130624859b68Sbalrog return 0; 130724859b68Sbalrog } 130824859b68Sbalrog } 130924859b68Sbalrog 1310a8170e5eSAvi Kivity static void musicpal_gpio_write(void *opaque, hwaddr offset, 131119b4a424SAvi Kivity uint64_t value, unsigned size) 131224859b68Sbalrog { 1313243cd13cSJan Kiszka musicpal_gpio_state *s = opaque; 131424859b68Sbalrog switch (offset) { 131524859b68Sbalrog case MP_GPIO_OE_HI: /* used for LCD brightness control */ 1316343ec8e4SBenoit Canet s->lcd_brightness = (s->lcd_brightness & MP_GPIO_LCD_BRIGHTNESS) | 131724859b68Sbalrog (value & MP_OE_LCD_BRIGHTNESS); 1318343ec8e4SBenoit Canet musicpal_gpio_brightness_update(s); 131924859b68Sbalrog break; 132024859b68Sbalrog 132124859b68Sbalrog case MP_GPIO_OUT_LO: 1322343ec8e4SBenoit Canet s->out_state = (s->out_state & 0xFFFF0000) | (value & 0xFFFF); 132324859b68Sbalrog break; 132424859b68Sbalrog case MP_GPIO_OUT_HI: 1325343ec8e4SBenoit Canet s->out_state = (s->out_state & 0xFFFF) | (value << 16); 1326343ec8e4SBenoit Canet s->lcd_brightness = (s->lcd_brightness & 0xFFFF) | 1327343ec8e4SBenoit Canet (s->out_state & MP_GPIO_LCD_BRIGHTNESS); 1328343ec8e4SBenoit Canet musicpal_gpio_brightness_update(s); 1329d074769cSAndrzej Zaborowski qemu_set_irq(s->out[3], (s->out_state >> MP_GPIO_I2C_DATA_BIT) & 1); 1330d074769cSAndrzej Zaborowski qemu_set_irq(s->out[4], (s->out_state >> MP_GPIO_I2C_CLOCK_BIT) & 1); 133124859b68Sbalrog break; 133224859b68Sbalrog 1333708afdf3SJan Kiszka case MP_GPIO_IER_LO: 1334708afdf3SJan Kiszka s->ier = (s->ier & 0xFFFF0000) | (value & 0xFFFF); 1335708afdf3SJan Kiszka break; 1336708afdf3SJan Kiszka case MP_GPIO_IER_HI: 1337708afdf3SJan Kiszka s->ier = (s->ier & 0xFFFF) | (value << 16); 1338708afdf3SJan Kiszka break; 1339708afdf3SJan Kiszka 1340708afdf3SJan Kiszka case MP_GPIO_IMR_LO: 1341708afdf3SJan Kiszka s->imr = (s->imr & 0xFFFF0000) | (value & 0xFFFF); 1342708afdf3SJan Kiszka break; 1343708afdf3SJan Kiszka case MP_GPIO_IMR_HI: 1344708afdf3SJan Kiszka s->imr = (s->imr & 0xFFFF) | (value << 16); 1345708afdf3SJan Kiszka break; 134624859b68Sbalrog } 134724859b68Sbalrog } 134824859b68Sbalrog 134919b4a424SAvi Kivity static const MemoryRegionOps musicpal_gpio_ops = { 135019b4a424SAvi Kivity .read = musicpal_gpio_read, 135119b4a424SAvi Kivity .write = musicpal_gpio_write, 135219b4a424SAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 1353718ec0beSmalc }; 1354718ec0beSmalc 1355d5b61dddSJan Kiszka static void musicpal_gpio_reset(DeviceState *d) 1356718ec0beSmalc { 13577012d4b4SAndreas Färber musicpal_gpio_state *s = MUSICPAL_GPIO(d); 135830624c92SJan Kiszka 135930624c92SJan Kiszka s->lcd_brightness = 0; 136030624c92SJan Kiszka s->out_state = 0; 1361343ec8e4SBenoit Canet s->in_state = 0xffffffff; 1362708afdf3SJan Kiszka s->ier = 0; 1363708afdf3SJan Kiszka s->imr = 0; 1364343ec8e4SBenoit Canet s->isr = 0; 1365343ec8e4SBenoit Canet } 1366343ec8e4SBenoit Canet 1367ece71994Sxiaoqiang zhao static void musicpal_gpio_init(Object *obj) 1368343ec8e4SBenoit Canet { 1369ece71994Sxiaoqiang zhao SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 13707012d4b4SAndreas Färber DeviceState *dev = DEVICE(sbd); 13717012d4b4SAndreas Färber musicpal_gpio_state *s = MUSICPAL_GPIO(dev); 1372718ec0beSmalc 13737012d4b4SAndreas Färber sysbus_init_irq(sbd, &s->irq); 1374343ec8e4SBenoit Canet 1375ece71994Sxiaoqiang zhao memory_region_init_io(&s->iomem, obj, &musicpal_gpio_ops, s, 137619b4a424SAvi Kivity "musicpal-gpio", MP_GPIO_SIZE); 13777012d4b4SAndreas Färber sysbus_init_mmio(sbd, &s->iomem); 1378343ec8e4SBenoit Canet 13797012d4b4SAndreas Färber qdev_init_gpio_out(dev, s->out, ARRAY_SIZE(s->out)); 1380708afdf3SJan Kiszka 13817012d4b4SAndreas Färber qdev_init_gpio_in(dev, musicpal_gpio_pin_event, 32); 1382718ec0beSmalc } 1383718ec0beSmalc 1384d5b61dddSJan Kiszka static const VMStateDescription musicpal_gpio_vmsd = { 1385d5b61dddSJan Kiszka .name = "musicpal_gpio", 1386d5b61dddSJan Kiszka .version_id = 1, 1387d5b61dddSJan Kiszka .minimum_version_id = 1, 1388d5b61dddSJan Kiszka .fields = (VMStateField[]) { 1389d5b61dddSJan Kiszka VMSTATE_UINT32(lcd_brightness, musicpal_gpio_state), 1390d5b61dddSJan Kiszka VMSTATE_UINT32(out_state, musicpal_gpio_state), 1391d5b61dddSJan Kiszka VMSTATE_UINT32(in_state, musicpal_gpio_state), 1392d5b61dddSJan Kiszka VMSTATE_UINT32(ier, musicpal_gpio_state), 1393d5b61dddSJan Kiszka VMSTATE_UINT32(imr, musicpal_gpio_state), 1394d5b61dddSJan Kiszka VMSTATE_UINT32(isr, musicpal_gpio_state), 1395d5b61dddSJan Kiszka VMSTATE_END_OF_LIST() 1396d5b61dddSJan Kiszka } 1397d5b61dddSJan Kiszka }; 1398d5b61dddSJan Kiszka 1399999e12bbSAnthony Liguori static void musicpal_gpio_class_init(ObjectClass *klass, void *data) 1400999e12bbSAnthony Liguori { 140139bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 1402999e12bbSAnthony Liguori 140339bffca2SAnthony Liguori dc->reset = musicpal_gpio_reset; 140439bffca2SAnthony Liguori dc->vmsd = &musicpal_gpio_vmsd; 1405999e12bbSAnthony Liguori } 1406999e12bbSAnthony Liguori 14078c43a6f0SAndreas Färber static const TypeInfo musicpal_gpio_info = { 14087012d4b4SAndreas Färber .name = TYPE_MUSICPAL_GPIO, 140939bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 141039bffca2SAnthony Liguori .instance_size = sizeof(musicpal_gpio_state), 1411ece71994Sxiaoqiang zhao .instance_init = musicpal_gpio_init, 1412999e12bbSAnthony Liguori .class_init = musicpal_gpio_class_init, 141330624c92SJan Kiszka }; 141430624c92SJan Kiszka 141524859b68Sbalrog /* Keyboard codes & masks */ 14167c6ce4baSbalrog #define KEY_RELEASED 0x80 141724859b68Sbalrog #define KEY_CODE 0x7f 141824859b68Sbalrog 141924859b68Sbalrog #define KEYCODE_TAB 0x0f 142024859b68Sbalrog #define KEYCODE_ENTER 0x1c 142124859b68Sbalrog #define KEYCODE_F 0x21 142224859b68Sbalrog #define KEYCODE_M 0x32 142324859b68Sbalrog 142424859b68Sbalrog #define KEYCODE_EXTENDED 0xe0 142524859b68Sbalrog #define KEYCODE_UP 0x48 142624859b68Sbalrog #define KEYCODE_DOWN 0x50 142724859b68Sbalrog #define KEYCODE_LEFT 0x4b 142824859b68Sbalrog #define KEYCODE_RIGHT 0x4d 142924859b68Sbalrog 1430708afdf3SJan Kiszka #define MP_KEY_WHEEL_VOL (1 << 0) 1431343ec8e4SBenoit Canet #define MP_KEY_WHEEL_VOL_INV (1 << 1) 1432343ec8e4SBenoit Canet #define MP_KEY_WHEEL_NAV (1 << 2) 1433343ec8e4SBenoit Canet #define MP_KEY_WHEEL_NAV_INV (1 << 3) 1434343ec8e4SBenoit Canet #define MP_KEY_BTN_FAVORITS (1 << 4) 1435343ec8e4SBenoit Canet #define MP_KEY_BTN_MENU (1 << 5) 1436343ec8e4SBenoit Canet #define MP_KEY_BTN_VOLUME (1 << 6) 1437343ec8e4SBenoit Canet #define MP_KEY_BTN_NAVIGATION (1 << 7) 1438343ec8e4SBenoit Canet 14393bdf5327SAndreas Färber #define TYPE_MUSICPAL_KEY "musicpal_key" 14403bdf5327SAndreas Färber #define MUSICPAL_KEY(obj) \ 14413bdf5327SAndreas Färber OBJECT_CHECK(musicpal_key_state, (obj), TYPE_MUSICPAL_KEY) 14423bdf5327SAndreas Färber 1443343ec8e4SBenoit Canet typedef struct musicpal_key_state { 14443bdf5327SAndreas Färber /*< private >*/ 14453bdf5327SAndreas Färber SysBusDevice parent_obj; 14463bdf5327SAndreas Färber /*< public >*/ 14473bdf5327SAndreas Färber 14484f5c9479SAvi Kivity MemoryRegion iomem; 1449343ec8e4SBenoit Canet uint32_t kbd_extended; 1450708afdf3SJan Kiszka uint32_t pressed_keys; 1451708afdf3SJan Kiszka qemu_irq out[8]; 1452343ec8e4SBenoit Canet } musicpal_key_state; 1453343ec8e4SBenoit Canet 145424859b68Sbalrog static void musicpal_key_event(void *opaque, int keycode) 145524859b68Sbalrog { 1456243cd13cSJan Kiszka musicpal_key_state *s = opaque; 145724859b68Sbalrog uint32_t event = 0; 1458343ec8e4SBenoit Canet int i; 145924859b68Sbalrog 146024859b68Sbalrog if (keycode == KEYCODE_EXTENDED) { 1461343ec8e4SBenoit Canet s->kbd_extended = 1; 146224859b68Sbalrog return; 146324859b68Sbalrog } 146424859b68Sbalrog 146549fedd0dSJan Kiszka if (s->kbd_extended) { 146624859b68Sbalrog switch (keycode & KEY_CODE) { 146724859b68Sbalrog case KEYCODE_UP: 1468343ec8e4SBenoit Canet event = MP_KEY_WHEEL_NAV | MP_KEY_WHEEL_NAV_INV; 146924859b68Sbalrog break; 147024859b68Sbalrog 147124859b68Sbalrog case KEYCODE_DOWN: 1472343ec8e4SBenoit Canet event = MP_KEY_WHEEL_NAV; 147324859b68Sbalrog break; 147424859b68Sbalrog 147524859b68Sbalrog case KEYCODE_LEFT: 1476343ec8e4SBenoit Canet event = MP_KEY_WHEEL_VOL | MP_KEY_WHEEL_VOL_INV; 147724859b68Sbalrog break; 147824859b68Sbalrog 147924859b68Sbalrog case KEYCODE_RIGHT: 1480343ec8e4SBenoit Canet event = MP_KEY_WHEEL_VOL; 148124859b68Sbalrog break; 148224859b68Sbalrog } 148349fedd0dSJan Kiszka } else { 148424859b68Sbalrog switch (keycode & KEY_CODE) { 148524859b68Sbalrog case KEYCODE_F: 1486343ec8e4SBenoit Canet event = MP_KEY_BTN_FAVORITS; 148724859b68Sbalrog break; 148824859b68Sbalrog 148924859b68Sbalrog case KEYCODE_TAB: 1490343ec8e4SBenoit Canet event = MP_KEY_BTN_VOLUME; 149124859b68Sbalrog break; 149224859b68Sbalrog 149324859b68Sbalrog case KEYCODE_ENTER: 1494343ec8e4SBenoit Canet event = MP_KEY_BTN_NAVIGATION; 149524859b68Sbalrog break; 149624859b68Sbalrog 149724859b68Sbalrog case KEYCODE_M: 1498343ec8e4SBenoit Canet event = MP_KEY_BTN_MENU; 149924859b68Sbalrog break; 150024859b68Sbalrog } 15017c6ce4baSbalrog /* Do not repeat already pressed buttons */ 1502708afdf3SJan Kiszka if (!(keycode & KEY_RELEASED) && (s->pressed_keys & event)) { 15037c6ce4baSbalrog event = 0; 15047c6ce4baSbalrog } 1505708afdf3SJan Kiszka } 150624859b68Sbalrog 15077c6ce4baSbalrog if (event) { 1508708afdf3SJan Kiszka /* Raise GPIO pin first if repeating a key */ 1509708afdf3SJan Kiszka if (!(keycode & KEY_RELEASED) && (s->pressed_keys & event)) { 1510708afdf3SJan Kiszka for (i = 0; i <= 7; i++) { 1511708afdf3SJan Kiszka if (event & (1 << i)) { 1512708afdf3SJan Kiszka qemu_set_irq(s->out[i], 1); 15137c6ce4baSbalrog } 1514708afdf3SJan Kiszka } 1515708afdf3SJan Kiszka } 1516708afdf3SJan Kiszka for (i = 0; i <= 7; i++) { 1517708afdf3SJan Kiszka if (event & (1 << i)) { 1518708afdf3SJan Kiszka qemu_set_irq(s->out[i], !!(keycode & KEY_RELEASED)); 1519708afdf3SJan Kiszka } 1520708afdf3SJan Kiszka } 1521708afdf3SJan Kiszka if (keycode & KEY_RELEASED) { 1522708afdf3SJan Kiszka s->pressed_keys &= ~event; 1523708afdf3SJan Kiszka } else { 1524708afdf3SJan Kiszka s->pressed_keys |= event; 1525708afdf3SJan Kiszka } 1526343ec8e4SBenoit Canet } 1527343ec8e4SBenoit Canet 1528343ec8e4SBenoit Canet s->kbd_extended = 0; 1529343ec8e4SBenoit Canet } 1530343ec8e4SBenoit Canet 1531ece71994Sxiaoqiang zhao static void musicpal_key_init(Object *obj) 1532343ec8e4SBenoit Canet { 1533ece71994Sxiaoqiang zhao SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 15343bdf5327SAndreas Färber DeviceState *dev = DEVICE(sbd); 15353bdf5327SAndreas Färber musicpal_key_state *s = MUSICPAL_KEY(dev); 1536343ec8e4SBenoit Canet 1537ece71994Sxiaoqiang zhao memory_region_init(&s->iomem, obj, "dummy", 0); 15383bdf5327SAndreas Färber sysbus_init_mmio(sbd, &s->iomem); 1539343ec8e4SBenoit Canet 1540343ec8e4SBenoit Canet s->kbd_extended = 0; 1541708afdf3SJan Kiszka s->pressed_keys = 0; 1542343ec8e4SBenoit Canet 15433bdf5327SAndreas Färber qdev_init_gpio_out(dev, s->out, ARRAY_SIZE(s->out)); 1544343ec8e4SBenoit Canet 1545343ec8e4SBenoit Canet qemu_add_kbd_event_handler(musicpal_key_event, s); 154624859b68Sbalrog } 154724859b68Sbalrog 1548d5b61dddSJan Kiszka static const VMStateDescription musicpal_key_vmsd = { 1549d5b61dddSJan Kiszka .name = "musicpal_key", 1550d5b61dddSJan Kiszka .version_id = 1, 1551d5b61dddSJan Kiszka .minimum_version_id = 1, 1552d5b61dddSJan Kiszka .fields = (VMStateField[]) { 1553d5b61dddSJan Kiszka VMSTATE_UINT32(kbd_extended, musicpal_key_state), 1554d5b61dddSJan Kiszka VMSTATE_UINT32(pressed_keys, musicpal_key_state), 1555d5b61dddSJan Kiszka VMSTATE_END_OF_LIST() 1556d5b61dddSJan Kiszka } 1557d5b61dddSJan Kiszka }; 1558d5b61dddSJan Kiszka 1559999e12bbSAnthony Liguori static void musicpal_key_class_init(ObjectClass *klass, void *data) 1560999e12bbSAnthony Liguori { 156139bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 1562999e12bbSAnthony Liguori 156339bffca2SAnthony Liguori dc->vmsd = &musicpal_key_vmsd; 1564999e12bbSAnthony Liguori } 1565999e12bbSAnthony Liguori 15668c43a6f0SAndreas Färber static const TypeInfo musicpal_key_info = { 15673bdf5327SAndreas Färber .name = TYPE_MUSICPAL_KEY, 156839bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 156939bffca2SAnthony Liguori .instance_size = sizeof(musicpal_key_state), 1570ece71994Sxiaoqiang zhao .instance_init = musicpal_key_init, 1571999e12bbSAnthony Liguori .class_init = musicpal_key_class_init, 1572d5b61dddSJan Kiszka }; 1573d5b61dddSJan Kiszka 157424859b68Sbalrog static struct arm_boot_info musicpal_binfo = { 157524859b68Sbalrog .loader_start = 0x0, 157624859b68Sbalrog .board_id = 0x20e, 157724859b68Sbalrog }; 157824859b68Sbalrog 15793ef96221SMarcel Apfelbaum static void musicpal_init(MachineState *machine) 158024859b68Sbalrog { 1581f25608e9SAndreas Färber ARMCPU *cpu; 1582b47b50faSPaul Brook qemu_irq pic[32]; 1583b47b50faSPaul Brook DeviceState *dev; 1584d074769cSAndrzej Zaborowski DeviceState *i2c_dev; 1585343ec8e4SBenoit Canet DeviceState *lcd_dev; 1586343ec8e4SBenoit Canet DeviceState *key_dev; 1587d074769cSAndrzej Zaborowski DeviceState *wm8750_dev; 1588d074769cSAndrzej Zaborowski SysBusDevice *s; 1589a5c82852SAndreas Färber I2CBus *i2c; 1590b47b50faSPaul Brook int i; 159124859b68Sbalrog unsigned long flash_size; 1592751c6a17SGerd Hoffmann DriveInfo *dinfo; 15933ed61312SIgor Mammedov MachineClass *mc = MACHINE_GET_CLASS(machine); 159419b4a424SAvi Kivity MemoryRegion *address_space_mem = get_system_memory(); 159519b4a424SAvi Kivity MemoryRegion *sram = g_new(MemoryRegion, 1); 159624859b68Sbalrog 15973ed61312SIgor Mammedov /* For now we use a fixed - the original - RAM size */ 15983ed61312SIgor Mammedov if (machine->ram_size != mc->default_ram_size) { 15993ed61312SIgor Mammedov char *sz = size_to_str(mc->default_ram_size); 16003ed61312SIgor Mammedov error_report("Invalid RAM size, should be %s", sz); 16013ed61312SIgor Mammedov g_free(sz); 16023ed61312SIgor Mammedov exit(EXIT_FAILURE); 16033ed61312SIgor Mammedov } 16043ed61312SIgor Mammedov 1605ba1ba5ccSIgor Mammedov cpu = ARM_CPU(cpu_create(machine->cpu_type)); 160624859b68Sbalrog 16073ed61312SIgor Mammedov memory_region_add_subregion(address_space_mem, 0, machine->ram); 160824859b68Sbalrog 160998a99ce0SPeter Maydell memory_region_init_ram(sram, NULL, "musicpal.sram", MP_SRAM_SIZE, 1610f8ed85acSMarkus Armbruster &error_fatal); 161119b4a424SAvi Kivity memory_region_add_subregion(address_space_mem, MP_SRAM_BASE, sram); 161224859b68Sbalrog 1613c7bd0fd9SAndreas Färber dev = sysbus_create_simple(TYPE_MV88W8618_PIC, MP_PIC_BASE, 1614fcef61ecSPeter Maydell qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ)); 1615b47b50faSPaul Brook for (i = 0; i < 32; i++) { 1616067a3ddcSPaul Brook pic[i] = qdev_get_gpio_in(dev, i); 1617b47b50faSPaul Brook } 16184adc8541SAndreas Färber sysbus_create_varargs(TYPE_MV88W8618_PIT, MP_PIT_BASE, pic[MP_TIMER1_IRQ], 1619b47b50faSPaul Brook pic[MP_TIMER2_IRQ], pic[MP_TIMER3_IRQ], 1620b47b50faSPaul Brook pic[MP_TIMER4_IRQ], NULL); 162124859b68Sbalrog 162239186d8aSRichard Henderson serial_mm_init(address_space_mem, MP_UART1_BASE, 2, pic[MP_UART1_IRQ], 16239bca0edbSPeter Maydell 1825000, serial_hd(0), DEVICE_NATIVE_ENDIAN); 162439186d8aSRichard Henderson serial_mm_init(address_space_mem, MP_UART2_BASE, 2, pic[MP_UART2_IRQ], 16259bca0edbSPeter Maydell 1825000, serial_hd(1), DEVICE_NATIVE_ENDIAN); 162624859b68Sbalrog 162724859b68Sbalrog /* Register flash */ 1628751c6a17SGerd Hoffmann dinfo = drive_get(IF_PFLASH, 0, 0); 1629751c6a17SGerd Hoffmann if (dinfo) { 16304be74634SMarkus Armbruster BlockBackend *blk = blk_by_legacy_dinfo(dinfo); 1631fa1d36dfSMarkus Armbruster 16324be74634SMarkus Armbruster flash_size = blk_getlength(blk); 163324859b68Sbalrog if (flash_size != 8*1024*1024 && flash_size != 16*1024*1024 && 163424859b68Sbalrog flash_size != 32*1024*1024) { 1635c0dbca36SAlistair Francis error_report("Invalid flash image size"); 163624859b68Sbalrog exit(1); 163724859b68Sbalrog } 163824859b68Sbalrog 163924859b68Sbalrog /* 164024859b68Sbalrog * The original U-Boot accesses the flash at 0xFE000000 instead of 164124859b68Sbalrog * 0xFF800000 (if there is 8 MB flash). So remap flash access if the 164224859b68Sbalrog * image is smaller than 32 MB. 164324859b68Sbalrog */ 1644940d5b13SMarkus Armbruster pflash_cfi02_register(0x100000000ULL - MP_FLASH_SIZE_MAX, 1645cfe5f011SAvi Kivity "musicpal.flash", flash_size, 1646ce14710fSMarkus Armbruster blk, 0x10000, 16475f9fc5adSBlue Swirl MP_FLASH_SIZE_MAX / flash_size, 16485f9fc5adSBlue Swirl 2, 0x00BF, 0x236D, 0x0000, 0x0000, 164901e0451aSAnthony Liguori 0x5555, 0x2AAA, 0); 165024859b68Sbalrog } 16515952b01cSAndreas Färber sysbus_create_simple(TYPE_MV88W8618_FLASHCFG, MP_FLASHCFG_BASE, NULL); 165224859b68Sbalrog 1653b47b50faSPaul Brook qemu_check_nic_model(&nd_table[0], "mv88w8618"); 1654*3e80f690SMarkus Armbruster dev = qdev_new(TYPE_MV88W8618_ETH); 16554c91cd28SGerd Hoffmann qdev_set_nic_properties(dev, &nd_table[0]); 1656*3e80f690SMarkus Armbruster qdev_realize_and_unref(dev, NULL, &error_fatal); 16571356b98dSAndreas Färber sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, MP_ETH_BASE); 16581356b98dSAndreas Färber sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[MP_ETH_IRQ]); 165924859b68Sbalrog 1660b47b50faSPaul Brook sysbus_create_simple("mv88w8618_wlan", MP_WLAN_BASE, NULL); 1661718ec0beSmalc 1662a86f200aSPeter Maydell sysbus_create_simple(TYPE_MUSICPAL_MISC, MP_MISC_BASE, NULL); 1663343ec8e4SBenoit Canet 16647012d4b4SAndreas Färber dev = sysbus_create_simple(TYPE_MUSICPAL_GPIO, MP_GPIO_BASE, 16657012d4b4SAndreas Färber pic[MP_GPIO_IRQ]); 1666d04fba94SJan Kiszka i2c_dev = sysbus_create_simple("gpio_i2c", -1, NULL); 1667a5c82852SAndreas Färber i2c = (I2CBus *)qdev_get_child_bus(i2c_dev, "i2c"); 1668d074769cSAndrzej Zaborowski 16692cca58fdSAndreas Färber lcd_dev = sysbus_create_simple(TYPE_MUSICPAL_LCD, MP_LCD_BASE, NULL); 16703bdf5327SAndreas Färber key_dev = sysbus_create_simple(TYPE_MUSICPAL_KEY, -1, NULL); 1671343ec8e4SBenoit Canet 1672d074769cSAndrzej Zaborowski /* I2C read data */ 1673708afdf3SJan Kiszka qdev_connect_gpio_out(i2c_dev, 0, 1674708afdf3SJan Kiszka qdev_get_gpio_in(dev, MP_GPIO_I2C_DATA_BIT)); 1675d074769cSAndrzej Zaborowski /* I2C data */ 1676d074769cSAndrzej Zaborowski qdev_connect_gpio_out(dev, 3, qdev_get_gpio_in(i2c_dev, 0)); 1677d074769cSAndrzej Zaborowski /* I2C clock */ 1678d074769cSAndrzej Zaborowski qdev_connect_gpio_out(dev, 4, qdev_get_gpio_in(i2c_dev, 1)); 1679d074769cSAndrzej Zaborowski 168049fedd0dSJan Kiszka for (i = 0; i < 3; i++) { 1681343ec8e4SBenoit Canet qdev_connect_gpio_out(dev, i, qdev_get_gpio_in(lcd_dev, i)); 168249fedd0dSJan Kiszka } 1683708afdf3SJan Kiszka for (i = 0; i < 4; i++) { 1684708afdf3SJan Kiszka qdev_connect_gpio_out(key_dev, i, qdev_get_gpio_in(dev, i + 8)); 1685708afdf3SJan Kiszka } 1686708afdf3SJan Kiszka for (i = 4; i < 8; i++) { 1687708afdf3SJan Kiszka qdev_connect_gpio_out(key_dev, i, qdev_get_gpio_in(dev, i + 15)); 1688708afdf3SJan Kiszka } 168924859b68Sbalrog 16907ab14c5aSPhilippe Mathieu-Daudé wm8750_dev = i2c_create_slave(i2c, TYPE_WM8750, MP_WM_ADDR); 1691*3e80f690SMarkus Armbruster dev = qdev_new(TYPE_MV88W8618_AUDIO); 16921356b98dSAndreas Färber s = SYS_BUS_DEVICE(dev); 1693a8299ec1SMao Zhongyi object_property_set_link(OBJECT(dev), OBJECT(wm8750_dev), 1694bd02b014SLi Qiang "wm8750", NULL); 1695*3e80f690SMarkus Armbruster qdev_realize_and_unref(dev, NULL, &error_fatal); 1696d074769cSAndrzej Zaborowski sysbus_mmio_map(s, 0, MP_AUDIO_BASE); 1697d074769cSAndrzej Zaborowski sysbus_connect_irq(s, 0, pic[MP_AUDIO_IRQ]); 1698d074769cSAndrzej Zaborowski 169924859b68Sbalrog musicpal_binfo.ram_size = MP_RAM_DEFAULT_SIZE; 17002744ece8STao Xu arm_load_kernel(cpu, machine, &musicpal_binfo); 170124859b68Sbalrog } 170224859b68Sbalrog 1703e264d29dSEduardo Habkost static void musicpal_machine_init(MachineClass *mc) 1704f80f9ec9SAnthony Liguori { 1705e264d29dSEduardo Habkost mc->desc = "Marvell 88w8618 / MusicPal (ARM926EJ-S)"; 1706e264d29dSEduardo Habkost mc->init = musicpal_init; 17074672cbd7SPeter Maydell mc->ignore_memory_transaction_failures = true; 1708ba1ba5ccSIgor Mammedov mc->default_cpu_type = ARM_CPU_TYPE_NAME("arm926"); 17093ed61312SIgor Mammedov mc->default_ram_size = MP_RAM_DEFAULT_SIZE; 17103ed61312SIgor Mammedov mc->default_ram_id = "musicpal.ram"; 1711f80f9ec9SAnthony Liguori } 1712f80f9ec9SAnthony Liguori 1713e264d29dSEduardo Habkost DEFINE_MACHINE("musicpal", musicpal_machine_init) 1714f80f9ec9SAnthony Liguori 1715999e12bbSAnthony Liguori static void mv88w8618_wlan_class_init(ObjectClass *klass, void *data) 1716999e12bbSAnthony Liguori { 17177f7420a0SMao Zhongyi DeviceClass *dc = DEVICE_CLASS(klass); 1718999e12bbSAnthony Liguori 17197f7420a0SMao Zhongyi dc->realize = mv88w8618_wlan_realize; 1720999e12bbSAnthony Liguori } 1721999e12bbSAnthony Liguori 17228c43a6f0SAndreas Färber static const TypeInfo mv88w8618_wlan_info = { 1723999e12bbSAnthony Liguori .name = "mv88w8618_wlan", 172439bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 172539bffca2SAnthony Liguori .instance_size = sizeof(SysBusDevice), 1726999e12bbSAnthony Liguori .class_init = mv88w8618_wlan_class_init, 1727999e12bbSAnthony Liguori }; 1728999e12bbSAnthony Liguori 172983f7d43aSAndreas Färber static void musicpal_register_types(void) 1730b47b50faSPaul Brook { 173139bffca2SAnthony Liguori type_register_static(&mv88w8618_pic_info); 173239bffca2SAnthony Liguori type_register_static(&mv88w8618_pit_info); 173339bffca2SAnthony Liguori type_register_static(&mv88w8618_flashcfg_info); 173439bffca2SAnthony Liguori type_register_static(&mv88w8618_eth_info); 173539bffca2SAnthony Liguori type_register_static(&mv88w8618_wlan_info); 173639bffca2SAnthony Liguori type_register_static(&musicpal_lcd_info); 173739bffca2SAnthony Liguori type_register_static(&musicpal_gpio_info); 173839bffca2SAnthony Liguori type_register_static(&musicpal_key_info); 1739a86f200aSPeter Maydell type_register_static(&musicpal_misc_info); 1740b47b50faSPaul Brook } 1741b47b50faSPaul Brook 174283f7d43aSAndreas Färber type_init(musicpal_register_types) 1743