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 1283c9f4caSPaolo Bonzini #include "hw/sysbus.h" 13*0d09e41aSPaolo Bonzini #include "hw/arm.h" 14*0d09e41aSPaolo Bonzini #include "hw/arm/devices.h" 151422e32dSPaolo Bonzini #include "net/net.h" 169c17d615SPaolo Bonzini #include "sysemu/sysemu.h" 1783c9f4caSPaolo Bonzini #include "hw/boards.h" 18*0d09e41aSPaolo Bonzini #include "hw/char/serial.h" 191de7afc9SPaolo Bonzini #include "qemu/timer.h" 2083c9f4caSPaolo Bonzini #include "hw/ptimer.h" 21737e150eSPaolo Bonzini #include "block/block.h" 22*0d09e41aSPaolo Bonzini #include "hw/block/flash.h" 2328ecbaeeSPaolo Bonzini #include "ui/console.h" 24*0d09e41aSPaolo Bonzini #include "hw/i2c/i2c.h" 259c17d615SPaolo Bonzini #include "sysemu/blockdev.h" 26022c62cbSPaolo Bonzini #include "exec/address-spaces.h" 2728ecbaeeSPaolo Bonzini #include "ui/pixel_ops.h" 2824859b68Sbalrog 29718ec0beSmalc #define MP_MISC_BASE 0x80002000 30718ec0beSmalc #define MP_MISC_SIZE 0x00001000 31718ec0beSmalc 3224859b68Sbalrog #define MP_ETH_BASE 0x80008000 3324859b68Sbalrog #define MP_ETH_SIZE 0x00001000 3424859b68Sbalrog 35718ec0beSmalc #define MP_WLAN_BASE 0x8000C000 36718ec0beSmalc #define MP_WLAN_SIZE 0x00000800 37718ec0beSmalc 3824859b68Sbalrog #define MP_UART1_BASE 0x8000C840 3924859b68Sbalrog #define MP_UART2_BASE 0x8000C940 4024859b68Sbalrog 41718ec0beSmalc #define MP_GPIO_BASE 0x8000D000 42718ec0beSmalc #define MP_GPIO_SIZE 0x00001000 43718ec0beSmalc 4424859b68Sbalrog #define MP_FLASHCFG_BASE 0x90006000 4524859b68Sbalrog #define MP_FLASHCFG_SIZE 0x00001000 4624859b68Sbalrog 4724859b68Sbalrog #define MP_AUDIO_BASE 0x90007000 4824859b68Sbalrog 4924859b68Sbalrog #define MP_PIC_BASE 0x90008000 5024859b68Sbalrog #define MP_PIC_SIZE 0x00001000 5124859b68Sbalrog 5224859b68Sbalrog #define MP_PIT_BASE 0x90009000 5324859b68Sbalrog #define MP_PIT_SIZE 0x00001000 5424859b68Sbalrog 5524859b68Sbalrog #define MP_LCD_BASE 0x9000c000 5624859b68Sbalrog #define MP_LCD_SIZE 0x00001000 5724859b68Sbalrog 5824859b68Sbalrog #define MP_SRAM_BASE 0xC0000000 5924859b68Sbalrog #define MP_SRAM_SIZE 0x00020000 6024859b68Sbalrog 6124859b68Sbalrog #define MP_RAM_DEFAULT_SIZE 32*1024*1024 6224859b68Sbalrog #define MP_FLASH_SIZE_MAX 32*1024*1024 6324859b68Sbalrog 6424859b68Sbalrog #define MP_TIMER1_IRQ 4 65b47b50faSPaul Brook #define MP_TIMER2_IRQ 5 66b47b50faSPaul Brook #define MP_TIMER3_IRQ 6 6724859b68Sbalrog #define MP_TIMER4_IRQ 7 6824859b68Sbalrog #define MP_EHCI_IRQ 8 6924859b68Sbalrog #define MP_ETH_IRQ 9 7024859b68Sbalrog #define MP_UART1_IRQ 11 7124859b68Sbalrog #define MP_UART2_IRQ 11 7224859b68Sbalrog #define MP_GPIO_IRQ 12 7324859b68Sbalrog #define MP_RTC_IRQ 28 7424859b68Sbalrog #define MP_AUDIO_IRQ 30 7524859b68Sbalrog 7624859b68Sbalrog /* Wolfson 8750 I2C address */ 7764258229SJan Kiszka #define MP_WM_ADDR 0x1A 7824859b68Sbalrog 7924859b68Sbalrog /* Ethernet register offsets */ 8024859b68Sbalrog #define MP_ETH_SMIR 0x010 8124859b68Sbalrog #define MP_ETH_PCXR 0x408 8224859b68Sbalrog #define MP_ETH_SDCMR 0x448 8324859b68Sbalrog #define MP_ETH_ICR 0x450 8424859b68Sbalrog #define MP_ETH_IMR 0x458 8524859b68Sbalrog #define MP_ETH_FRDP0 0x480 8624859b68Sbalrog #define MP_ETH_FRDP1 0x484 8724859b68Sbalrog #define MP_ETH_FRDP2 0x488 8824859b68Sbalrog #define MP_ETH_FRDP3 0x48C 8924859b68Sbalrog #define MP_ETH_CRDP0 0x4A0 9024859b68Sbalrog #define MP_ETH_CRDP1 0x4A4 9124859b68Sbalrog #define MP_ETH_CRDP2 0x4A8 9224859b68Sbalrog #define MP_ETH_CRDP3 0x4AC 9324859b68Sbalrog #define MP_ETH_CTDP0 0x4E0 9424859b68Sbalrog #define MP_ETH_CTDP1 0x4E4 9524859b68Sbalrog #define MP_ETH_CTDP2 0x4E8 9624859b68Sbalrog #define MP_ETH_CTDP3 0x4EC 9724859b68Sbalrog 9824859b68Sbalrog /* MII PHY access */ 9924859b68Sbalrog #define MP_ETH_SMIR_DATA 0x0000FFFF 10024859b68Sbalrog #define MP_ETH_SMIR_ADDR 0x03FF0000 10124859b68Sbalrog #define MP_ETH_SMIR_OPCODE (1 << 26) /* Read value */ 10224859b68Sbalrog #define MP_ETH_SMIR_RDVALID (1 << 27) 10324859b68Sbalrog 10424859b68Sbalrog /* PHY registers */ 10524859b68Sbalrog #define MP_ETH_PHY1_BMSR 0x00210000 10624859b68Sbalrog #define MP_ETH_PHY1_PHYSID1 0x00410000 10724859b68Sbalrog #define MP_ETH_PHY1_PHYSID2 0x00610000 10824859b68Sbalrog 10924859b68Sbalrog #define MP_PHY_BMSR_LINK 0x0004 11024859b68Sbalrog #define MP_PHY_BMSR_AUTONEG 0x0008 11124859b68Sbalrog 11224859b68Sbalrog #define MP_PHY_88E3015 0x01410E20 11324859b68Sbalrog 11424859b68Sbalrog /* TX descriptor status */ 11524859b68Sbalrog #define MP_ETH_TX_OWN (1 << 31) 11624859b68Sbalrog 11724859b68Sbalrog /* RX descriptor status */ 11824859b68Sbalrog #define MP_ETH_RX_OWN (1 << 31) 11924859b68Sbalrog 12024859b68Sbalrog /* Interrupt cause/mask bits */ 12124859b68Sbalrog #define MP_ETH_IRQ_RX_BIT 0 12224859b68Sbalrog #define MP_ETH_IRQ_RX (1 << MP_ETH_IRQ_RX_BIT) 12324859b68Sbalrog #define MP_ETH_IRQ_TXHI_BIT 2 12424859b68Sbalrog #define MP_ETH_IRQ_TXLO_BIT 3 12524859b68Sbalrog 12624859b68Sbalrog /* Port config bits */ 12724859b68Sbalrog #define MP_ETH_PCXR_2BSM_BIT 28 /* 2-byte incoming suffix */ 12824859b68Sbalrog 12924859b68Sbalrog /* SDMA command bits */ 13024859b68Sbalrog #define MP_ETH_CMD_TXHI (1 << 23) 13124859b68Sbalrog #define MP_ETH_CMD_TXLO (1 << 22) 13224859b68Sbalrog 13324859b68Sbalrog typedef struct mv88w8618_tx_desc { 13424859b68Sbalrog uint32_t cmdstat; 13524859b68Sbalrog uint16_t res; 13624859b68Sbalrog uint16_t bytes; 13724859b68Sbalrog uint32_t buffer; 13824859b68Sbalrog uint32_t next; 13924859b68Sbalrog } mv88w8618_tx_desc; 14024859b68Sbalrog 14124859b68Sbalrog typedef struct mv88w8618_rx_desc { 14224859b68Sbalrog uint32_t cmdstat; 14324859b68Sbalrog uint16_t bytes; 14424859b68Sbalrog uint16_t buffer_size; 14524859b68Sbalrog uint32_t buffer; 14624859b68Sbalrog uint32_t next; 14724859b68Sbalrog } mv88w8618_rx_desc; 14824859b68Sbalrog 14924859b68Sbalrog typedef struct mv88w8618_eth_state { 150b47b50faSPaul Brook SysBusDevice busdev; 15119b4a424SAvi Kivity MemoryRegion iomem; 15224859b68Sbalrog qemu_irq irq; 15324859b68Sbalrog uint32_t smir; 15424859b68Sbalrog uint32_t icr; 15524859b68Sbalrog uint32_t imr; 156b946a153Saliguori int mmio_index; 157d5b61dddSJan Kiszka uint32_t vlan_header; 158930c8682Spbrook uint32_t tx_queue[2]; 159930c8682Spbrook uint32_t rx_queue[4]; 160930c8682Spbrook uint32_t frx_queue[4]; 161930c8682Spbrook uint32_t cur_rx[4]; 1623a94dd18SMark McLoughlin NICState *nic; 1634c91cd28SGerd Hoffmann NICConf conf; 16424859b68Sbalrog } mv88w8618_eth_state; 16524859b68Sbalrog 166930c8682Spbrook static void eth_rx_desc_put(uint32_t addr, mv88w8618_rx_desc *desc) 167930c8682Spbrook { 168930c8682Spbrook cpu_to_le32s(&desc->cmdstat); 169930c8682Spbrook cpu_to_le16s(&desc->bytes); 170930c8682Spbrook cpu_to_le16s(&desc->buffer_size); 171930c8682Spbrook cpu_to_le32s(&desc->buffer); 172930c8682Spbrook cpu_to_le32s(&desc->next); 173930c8682Spbrook cpu_physical_memory_write(addr, (void *)desc, sizeof(*desc)); 174930c8682Spbrook } 175930c8682Spbrook 176930c8682Spbrook static void eth_rx_desc_get(uint32_t addr, mv88w8618_rx_desc *desc) 177930c8682Spbrook { 178930c8682Spbrook cpu_physical_memory_read(addr, (void *)desc, sizeof(*desc)); 179930c8682Spbrook le32_to_cpus(&desc->cmdstat); 180930c8682Spbrook le16_to_cpus(&desc->bytes); 181930c8682Spbrook le16_to_cpus(&desc->buffer_size); 182930c8682Spbrook le32_to_cpus(&desc->buffer); 183930c8682Spbrook le32_to_cpus(&desc->next); 184930c8682Spbrook } 185930c8682Spbrook 1864e68f7a0SStefan Hajnoczi static int eth_can_receive(NetClientState *nc) 18724859b68Sbalrog { 18824859b68Sbalrog return 1; 18924859b68Sbalrog } 19024859b68Sbalrog 1914e68f7a0SStefan Hajnoczi static ssize_t eth_receive(NetClientState *nc, const uint8_t *buf, size_t size) 19224859b68Sbalrog { 193cc1f0f45SJason Wang mv88w8618_eth_state *s = qemu_get_nic_opaque(nc); 194930c8682Spbrook uint32_t desc_addr; 195930c8682Spbrook mv88w8618_rx_desc desc; 19624859b68Sbalrog int i; 19724859b68Sbalrog 19824859b68Sbalrog for (i = 0; i < 4; i++) { 199930c8682Spbrook desc_addr = s->cur_rx[i]; 20049fedd0dSJan Kiszka if (!desc_addr) { 20124859b68Sbalrog continue; 20249fedd0dSJan Kiszka } 20324859b68Sbalrog do { 204930c8682Spbrook eth_rx_desc_get(desc_addr, &desc); 205930c8682Spbrook if ((desc.cmdstat & MP_ETH_RX_OWN) && desc.buffer_size >= size) { 206930c8682Spbrook cpu_physical_memory_write(desc.buffer + s->vlan_header, 20724859b68Sbalrog buf, size); 208930c8682Spbrook desc.bytes = size + s->vlan_header; 209930c8682Spbrook desc.cmdstat &= ~MP_ETH_RX_OWN; 210930c8682Spbrook s->cur_rx[i] = desc.next; 21124859b68Sbalrog 21224859b68Sbalrog s->icr |= MP_ETH_IRQ_RX; 21349fedd0dSJan Kiszka if (s->icr & s->imr) { 21424859b68Sbalrog qemu_irq_raise(s->irq); 21549fedd0dSJan Kiszka } 216930c8682Spbrook eth_rx_desc_put(desc_addr, &desc); 2174f1c942bSMark McLoughlin return size; 21824859b68Sbalrog } 219930c8682Spbrook desc_addr = desc.next; 220930c8682Spbrook } while (desc_addr != s->rx_queue[i]); 22124859b68Sbalrog } 2224f1c942bSMark McLoughlin return size; 22324859b68Sbalrog } 22424859b68Sbalrog 225930c8682Spbrook static void eth_tx_desc_put(uint32_t addr, mv88w8618_tx_desc *desc) 226930c8682Spbrook { 227930c8682Spbrook cpu_to_le32s(&desc->cmdstat); 228930c8682Spbrook cpu_to_le16s(&desc->res); 229930c8682Spbrook cpu_to_le16s(&desc->bytes); 230930c8682Spbrook cpu_to_le32s(&desc->buffer); 231930c8682Spbrook cpu_to_le32s(&desc->next); 232930c8682Spbrook cpu_physical_memory_write(addr, (void *)desc, sizeof(*desc)); 233930c8682Spbrook } 234930c8682Spbrook 235930c8682Spbrook static void eth_tx_desc_get(uint32_t addr, mv88w8618_tx_desc *desc) 236930c8682Spbrook { 237930c8682Spbrook cpu_physical_memory_read(addr, (void *)desc, sizeof(*desc)); 238930c8682Spbrook le32_to_cpus(&desc->cmdstat); 239930c8682Spbrook le16_to_cpus(&desc->res); 240930c8682Spbrook le16_to_cpus(&desc->bytes); 241930c8682Spbrook le32_to_cpus(&desc->buffer); 242930c8682Spbrook le32_to_cpus(&desc->next); 243930c8682Spbrook } 244930c8682Spbrook 24524859b68Sbalrog static void eth_send(mv88w8618_eth_state *s, int queue_index) 24624859b68Sbalrog { 247930c8682Spbrook uint32_t desc_addr = s->tx_queue[queue_index]; 248930c8682Spbrook mv88w8618_tx_desc desc; 24907b064e9SJan Kiszka uint32_t next_desc; 250930c8682Spbrook uint8_t buf[2048]; 251930c8682Spbrook int len; 252930c8682Spbrook 25324859b68Sbalrog do { 254930c8682Spbrook eth_tx_desc_get(desc_addr, &desc); 25507b064e9SJan Kiszka next_desc = desc.next; 256930c8682Spbrook if (desc.cmdstat & MP_ETH_TX_OWN) { 257930c8682Spbrook len = desc.bytes; 258930c8682Spbrook if (len < 2048) { 259930c8682Spbrook cpu_physical_memory_read(desc.buffer, buf, len); 260b356f76dSJason Wang qemu_send_packet(qemu_get_queue(s->nic), buf, len); 26124859b68Sbalrog } 262930c8682Spbrook desc.cmdstat &= ~MP_ETH_TX_OWN; 263930c8682Spbrook s->icr |= 1 << (MP_ETH_IRQ_TXLO_BIT - queue_index); 264930c8682Spbrook eth_tx_desc_put(desc_addr, &desc); 265930c8682Spbrook } 26607b064e9SJan Kiszka desc_addr = next_desc; 267930c8682Spbrook } while (desc_addr != s->tx_queue[queue_index]); 26824859b68Sbalrog } 26924859b68Sbalrog 270a8170e5eSAvi Kivity static uint64_t mv88w8618_eth_read(void *opaque, hwaddr offset, 27119b4a424SAvi Kivity unsigned size) 27224859b68Sbalrog { 27324859b68Sbalrog mv88w8618_eth_state *s = opaque; 27424859b68Sbalrog 27524859b68Sbalrog switch (offset) { 27624859b68Sbalrog case MP_ETH_SMIR: 27724859b68Sbalrog if (s->smir & MP_ETH_SMIR_OPCODE) { 27824859b68Sbalrog switch (s->smir & MP_ETH_SMIR_ADDR) { 27924859b68Sbalrog case MP_ETH_PHY1_BMSR: 28024859b68Sbalrog return MP_PHY_BMSR_LINK | MP_PHY_BMSR_AUTONEG | 28124859b68Sbalrog MP_ETH_SMIR_RDVALID; 28224859b68Sbalrog case MP_ETH_PHY1_PHYSID1: 28324859b68Sbalrog return (MP_PHY_88E3015 >> 16) | MP_ETH_SMIR_RDVALID; 28424859b68Sbalrog case MP_ETH_PHY1_PHYSID2: 28524859b68Sbalrog return (MP_PHY_88E3015 & 0xFFFF) | MP_ETH_SMIR_RDVALID; 28624859b68Sbalrog default: 28724859b68Sbalrog return MP_ETH_SMIR_RDVALID; 28824859b68Sbalrog } 28924859b68Sbalrog } 29024859b68Sbalrog return 0; 29124859b68Sbalrog 29224859b68Sbalrog case MP_ETH_ICR: 29324859b68Sbalrog return s->icr; 29424859b68Sbalrog 29524859b68Sbalrog case MP_ETH_IMR: 29624859b68Sbalrog return s->imr; 29724859b68Sbalrog 29824859b68Sbalrog case MP_ETH_FRDP0 ... MP_ETH_FRDP3: 299930c8682Spbrook return s->frx_queue[(offset - MP_ETH_FRDP0)/4]; 30024859b68Sbalrog 30124859b68Sbalrog case MP_ETH_CRDP0 ... MP_ETH_CRDP3: 302930c8682Spbrook return s->rx_queue[(offset - MP_ETH_CRDP0)/4]; 30324859b68Sbalrog 30424859b68Sbalrog case MP_ETH_CTDP0 ... MP_ETH_CTDP3: 305930c8682Spbrook return s->tx_queue[(offset - MP_ETH_CTDP0)/4]; 30624859b68Sbalrog 30724859b68Sbalrog default: 30824859b68Sbalrog return 0; 30924859b68Sbalrog } 31024859b68Sbalrog } 31124859b68Sbalrog 312a8170e5eSAvi Kivity static void mv88w8618_eth_write(void *opaque, hwaddr offset, 31319b4a424SAvi Kivity uint64_t value, unsigned size) 31424859b68Sbalrog { 31524859b68Sbalrog mv88w8618_eth_state *s = opaque; 31624859b68Sbalrog 31724859b68Sbalrog switch (offset) { 31824859b68Sbalrog case MP_ETH_SMIR: 31924859b68Sbalrog s->smir = value; 32024859b68Sbalrog break; 32124859b68Sbalrog 32224859b68Sbalrog case MP_ETH_PCXR: 32324859b68Sbalrog s->vlan_header = ((value >> MP_ETH_PCXR_2BSM_BIT) & 1) * 2; 32424859b68Sbalrog break; 32524859b68Sbalrog 32624859b68Sbalrog case MP_ETH_SDCMR: 32749fedd0dSJan Kiszka if (value & MP_ETH_CMD_TXHI) { 32824859b68Sbalrog eth_send(s, 1); 32949fedd0dSJan Kiszka } 33049fedd0dSJan Kiszka if (value & MP_ETH_CMD_TXLO) { 33124859b68Sbalrog eth_send(s, 0); 33249fedd0dSJan Kiszka } 33349fedd0dSJan Kiszka if (value & (MP_ETH_CMD_TXHI | MP_ETH_CMD_TXLO) && s->icr & s->imr) { 33424859b68Sbalrog qemu_irq_raise(s->irq); 33549fedd0dSJan Kiszka } 33624859b68Sbalrog break; 33724859b68Sbalrog 33824859b68Sbalrog case MP_ETH_ICR: 33924859b68Sbalrog s->icr &= value; 34024859b68Sbalrog break; 34124859b68Sbalrog 34224859b68Sbalrog case MP_ETH_IMR: 34324859b68Sbalrog s->imr = value; 34449fedd0dSJan Kiszka if (s->icr & s->imr) { 34524859b68Sbalrog qemu_irq_raise(s->irq); 34649fedd0dSJan Kiszka } 34724859b68Sbalrog break; 34824859b68Sbalrog 34924859b68Sbalrog case MP_ETH_FRDP0 ... MP_ETH_FRDP3: 350930c8682Spbrook s->frx_queue[(offset - MP_ETH_FRDP0)/4] = value; 35124859b68Sbalrog break; 35224859b68Sbalrog 35324859b68Sbalrog case MP_ETH_CRDP0 ... MP_ETH_CRDP3: 35424859b68Sbalrog s->rx_queue[(offset - MP_ETH_CRDP0)/4] = 355930c8682Spbrook s->cur_rx[(offset - MP_ETH_CRDP0)/4] = value; 35624859b68Sbalrog break; 35724859b68Sbalrog 35824859b68Sbalrog case MP_ETH_CTDP0 ... MP_ETH_CTDP3: 359930c8682Spbrook s->tx_queue[(offset - MP_ETH_CTDP0)/4] = value; 36024859b68Sbalrog break; 36124859b68Sbalrog } 36224859b68Sbalrog } 36324859b68Sbalrog 36419b4a424SAvi Kivity static const MemoryRegionOps mv88w8618_eth_ops = { 36519b4a424SAvi Kivity .read = mv88w8618_eth_read, 36619b4a424SAvi Kivity .write = mv88w8618_eth_write, 36719b4a424SAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 36824859b68Sbalrog }; 36924859b68Sbalrog 3704e68f7a0SStefan Hajnoczi static void eth_cleanup(NetClientState *nc) 371b946a153Saliguori { 372cc1f0f45SJason Wang mv88w8618_eth_state *s = qemu_get_nic_opaque(nc); 373b946a153Saliguori 3743a94dd18SMark McLoughlin s->nic = NULL; 375b946a153Saliguori } 376b946a153Saliguori 3773a94dd18SMark McLoughlin static NetClientInfo net_mv88w8618_info = { 3782be64a68SLaszlo Ersek .type = NET_CLIENT_OPTIONS_KIND_NIC, 3793a94dd18SMark McLoughlin .size = sizeof(NICState), 3803a94dd18SMark McLoughlin .can_receive = eth_can_receive, 3813a94dd18SMark McLoughlin .receive = eth_receive, 3823a94dd18SMark McLoughlin .cleanup = eth_cleanup, 3833a94dd18SMark McLoughlin }; 3843a94dd18SMark McLoughlin 38581a322d4SGerd Hoffmann static int mv88w8618_eth_init(SysBusDevice *dev) 38624859b68Sbalrog { 387b47b50faSPaul Brook mv88w8618_eth_state *s = FROM_SYSBUS(mv88w8618_eth_state, dev); 38824859b68Sbalrog 389b47b50faSPaul Brook sysbus_init_irq(dev, &s->irq); 3903a94dd18SMark McLoughlin s->nic = qemu_new_nic(&net_mv88w8618_info, &s->conf, 391f79f2bfcSAnthony Liguori object_get_typename(OBJECT(dev)), dev->qdev.id, s); 39219b4a424SAvi Kivity memory_region_init_io(&s->iomem, &mv88w8618_eth_ops, s, "mv88w8618-eth", 39319b4a424SAvi Kivity MP_ETH_SIZE); 394750ecd44SAvi Kivity sysbus_init_mmio(dev, &s->iomem); 39581a322d4SGerd Hoffmann return 0; 39624859b68Sbalrog } 39724859b68Sbalrog 398d5b61dddSJan Kiszka static const VMStateDescription mv88w8618_eth_vmsd = { 399d5b61dddSJan Kiszka .name = "mv88w8618_eth", 400d5b61dddSJan Kiszka .version_id = 1, 401d5b61dddSJan Kiszka .minimum_version_id = 1, 402d5b61dddSJan Kiszka .minimum_version_id_old = 1, 403d5b61dddSJan Kiszka .fields = (VMStateField[]) { 404d5b61dddSJan Kiszka VMSTATE_UINT32(smir, mv88w8618_eth_state), 405d5b61dddSJan Kiszka VMSTATE_UINT32(icr, mv88w8618_eth_state), 406d5b61dddSJan Kiszka VMSTATE_UINT32(imr, mv88w8618_eth_state), 407d5b61dddSJan Kiszka VMSTATE_UINT32(vlan_header, mv88w8618_eth_state), 408d5b61dddSJan Kiszka VMSTATE_UINT32_ARRAY(tx_queue, mv88w8618_eth_state, 2), 409d5b61dddSJan Kiszka VMSTATE_UINT32_ARRAY(rx_queue, mv88w8618_eth_state, 4), 410d5b61dddSJan Kiszka VMSTATE_UINT32_ARRAY(frx_queue, mv88w8618_eth_state, 4), 411d5b61dddSJan Kiszka VMSTATE_UINT32_ARRAY(cur_rx, mv88w8618_eth_state, 4), 412d5b61dddSJan Kiszka VMSTATE_END_OF_LIST() 413d5b61dddSJan Kiszka } 414d5b61dddSJan Kiszka }; 415d5b61dddSJan Kiszka 416999e12bbSAnthony Liguori static Property mv88w8618_eth_properties[] = { 4174c91cd28SGerd Hoffmann DEFINE_NIC_PROPERTIES(mv88w8618_eth_state, conf), 4184c91cd28SGerd Hoffmann DEFINE_PROP_END_OF_LIST(), 419999e12bbSAnthony Liguori }; 420999e12bbSAnthony Liguori 421999e12bbSAnthony Liguori static void mv88w8618_eth_class_init(ObjectClass *klass, void *data) 422999e12bbSAnthony Liguori { 42339bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 424999e12bbSAnthony Liguori SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); 425999e12bbSAnthony Liguori 426999e12bbSAnthony Liguori k->init = mv88w8618_eth_init; 42739bffca2SAnthony Liguori dc->vmsd = &mv88w8618_eth_vmsd; 42839bffca2SAnthony Liguori dc->props = mv88w8618_eth_properties; 429999e12bbSAnthony Liguori } 430999e12bbSAnthony Liguori 4318c43a6f0SAndreas Färber static const TypeInfo mv88w8618_eth_info = { 432999e12bbSAnthony Liguori .name = "mv88w8618_eth", 43339bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 43439bffca2SAnthony Liguori .instance_size = sizeof(mv88w8618_eth_state), 435999e12bbSAnthony Liguori .class_init = mv88w8618_eth_class_init, 436d5b61dddSJan Kiszka }; 437d5b61dddSJan Kiszka 43824859b68Sbalrog /* LCD register offsets */ 43924859b68Sbalrog #define MP_LCD_IRQCTRL 0x180 44024859b68Sbalrog #define MP_LCD_IRQSTAT 0x184 44124859b68Sbalrog #define MP_LCD_SPICTRL 0x1ac 44224859b68Sbalrog #define MP_LCD_INST 0x1bc 44324859b68Sbalrog #define MP_LCD_DATA 0x1c0 44424859b68Sbalrog 44524859b68Sbalrog /* Mode magics */ 44624859b68Sbalrog #define MP_LCD_SPI_DATA 0x00100011 44724859b68Sbalrog #define MP_LCD_SPI_CMD 0x00104011 44824859b68Sbalrog #define MP_LCD_SPI_INVALID 0x00000000 44924859b68Sbalrog 45024859b68Sbalrog /* Commmands */ 45124859b68Sbalrog #define MP_LCD_INST_SETPAGE0 0xB0 45224859b68Sbalrog /* ... */ 45324859b68Sbalrog #define MP_LCD_INST_SETPAGE7 0xB7 45424859b68Sbalrog 45524859b68Sbalrog #define MP_LCD_TEXTCOLOR 0xe0e0ff /* RRGGBB */ 45624859b68Sbalrog 45724859b68Sbalrog typedef struct musicpal_lcd_state { 458b47b50faSPaul Brook SysBusDevice busdev; 45919b4a424SAvi Kivity MemoryRegion iomem; 460343ec8e4SBenoit Canet uint32_t brightness; 46124859b68Sbalrog uint32_t mode; 46224859b68Sbalrog uint32_t irqctrl; 463d5b61dddSJan Kiszka uint32_t page; 464d5b61dddSJan Kiszka uint32_t page_off; 465c78f7137SGerd Hoffmann QemuConsole *con; 46624859b68Sbalrog uint8_t video_ram[128*64/8]; 46724859b68Sbalrog } musicpal_lcd_state; 46824859b68Sbalrog 469343ec8e4SBenoit Canet static uint8_t scale_lcd_color(musicpal_lcd_state *s, uint8_t col) 47024859b68Sbalrog { 471343ec8e4SBenoit Canet switch (s->brightness) { 472343ec8e4SBenoit Canet case 7: 47324859b68Sbalrog return col; 474343ec8e4SBenoit Canet case 0: 475343ec8e4SBenoit Canet return 0; 476343ec8e4SBenoit Canet default: 477343ec8e4SBenoit Canet return (col * s->brightness) / 7; 47824859b68Sbalrog } 47924859b68Sbalrog } 48024859b68Sbalrog 4810266f2c7Sbalrog #define SET_LCD_PIXEL(depth, type) \ 4820266f2c7Sbalrog static inline void glue(set_lcd_pixel, depth) \ 4830266f2c7Sbalrog (musicpal_lcd_state *s, int x, int y, type col) \ 4840266f2c7Sbalrog { \ 4850266f2c7Sbalrog int dx, dy; \ 486c78f7137SGerd Hoffmann DisplaySurface *surface = qemu_console_surface(s->con); \ 487c78f7137SGerd Hoffmann type *pixel = &((type *) surface_data(surface))[(y * 128 * 3 + x) * 3]; \ 4880266f2c7Sbalrog \ 4890266f2c7Sbalrog for (dy = 0; dy < 3; dy++, pixel += 127 * 3) \ 4900266f2c7Sbalrog for (dx = 0; dx < 3; dx++, pixel++) \ 4910266f2c7Sbalrog *pixel = col; \ 4920266f2c7Sbalrog } 4930266f2c7Sbalrog SET_LCD_PIXEL(8, uint8_t) 4940266f2c7Sbalrog SET_LCD_PIXEL(16, uint16_t) 4950266f2c7Sbalrog SET_LCD_PIXEL(32, uint32_t) 49624859b68Sbalrog 49724859b68Sbalrog static void lcd_refresh(void *opaque) 49824859b68Sbalrog { 49924859b68Sbalrog musicpal_lcd_state *s = opaque; 500c78f7137SGerd Hoffmann DisplaySurface *surface = qemu_console_surface(s->con); 5010266f2c7Sbalrog int x, y, col; 50224859b68Sbalrog 503c78f7137SGerd Hoffmann switch (surface_bits_per_pixel(surface)) { 5040266f2c7Sbalrog case 0: 5050266f2c7Sbalrog return; 5060266f2c7Sbalrog #define LCD_REFRESH(depth, func) \ 5070266f2c7Sbalrog case depth: \ 508343ec8e4SBenoit Canet col = func(scale_lcd_color(s, (MP_LCD_TEXTCOLOR >> 16) & 0xff), \ 509343ec8e4SBenoit Canet scale_lcd_color(s, (MP_LCD_TEXTCOLOR >> 8) & 0xff), \ 510343ec8e4SBenoit Canet scale_lcd_color(s, MP_LCD_TEXTCOLOR & 0xff)); \ 51149fedd0dSJan Kiszka for (x = 0; x < 128; x++) { \ 51249fedd0dSJan Kiszka for (y = 0; y < 64; y++) { \ 51349fedd0dSJan Kiszka if (s->video_ram[x + (y/8)*128] & (1 << (y % 8))) { \ 5140266f2c7Sbalrog glue(set_lcd_pixel, depth)(s, x, y, col); \ 51549fedd0dSJan Kiszka } else { \ 5160266f2c7Sbalrog glue(set_lcd_pixel, depth)(s, x, y, 0); \ 51749fedd0dSJan Kiszka } \ 51849fedd0dSJan Kiszka } \ 51949fedd0dSJan Kiszka } \ 5200266f2c7Sbalrog break; 5210266f2c7Sbalrog LCD_REFRESH(8, rgb_to_pixel8) 5220266f2c7Sbalrog LCD_REFRESH(16, rgb_to_pixel16) 523c78f7137SGerd Hoffmann LCD_REFRESH(32, (is_surface_bgr(surface) ? 524bf9b48afSaliguori rgb_to_pixel32bgr : rgb_to_pixel32)) 5250266f2c7Sbalrog default: 5262ac71179SPaul Brook hw_error("unsupported colour depth %i\n", 527c78f7137SGerd Hoffmann surface_bits_per_pixel(surface)); 5280266f2c7Sbalrog } 52924859b68Sbalrog 530c78f7137SGerd Hoffmann dpy_gfx_update(s->con, 0, 0, 128*3, 64*3); 53124859b68Sbalrog } 53224859b68Sbalrog 533167bc3d2Sbalrog static void lcd_invalidate(void *opaque) 534167bc3d2Sbalrog { 535167bc3d2Sbalrog } 536167bc3d2Sbalrog 537343ec8e4SBenoit Canet static void musicpal_lcd_gpio_brigthness_in(void *opaque, int irq, int level) 538343ec8e4SBenoit Canet { 539243cd13cSJan Kiszka musicpal_lcd_state *s = opaque; 540343ec8e4SBenoit Canet s->brightness &= ~(1 << irq); 541343ec8e4SBenoit Canet s->brightness |= level << irq; 542343ec8e4SBenoit Canet } 543343ec8e4SBenoit Canet 544a8170e5eSAvi Kivity static uint64_t musicpal_lcd_read(void *opaque, hwaddr offset, 54519b4a424SAvi Kivity unsigned size) 54624859b68Sbalrog { 54724859b68Sbalrog musicpal_lcd_state *s = opaque; 54824859b68Sbalrog 54924859b68Sbalrog switch (offset) { 55024859b68Sbalrog case MP_LCD_IRQCTRL: 55124859b68Sbalrog return s->irqctrl; 55224859b68Sbalrog 55324859b68Sbalrog default: 55424859b68Sbalrog return 0; 55524859b68Sbalrog } 55624859b68Sbalrog } 55724859b68Sbalrog 558a8170e5eSAvi Kivity static void musicpal_lcd_write(void *opaque, hwaddr offset, 55919b4a424SAvi Kivity uint64_t value, unsigned size) 56024859b68Sbalrog { 56124859b68Sbalrog musicpal_lcd_state *s = opaque; 56224859b68Sbalrog 56324859b68Sbalrog switch (offset) { 56424859b68Sbalrog case MP_LCD_IRQCTRL: 56524859b68Sbalrog s->irqctrl = value; 56624859b68Sbalrog break; 56724859b68Sbalrog 56824859b68Sbalrog case MP_LCD_SPICTRL: 56949fedd0dSJan Kiszka if (value == MP_LCD_SPI_DATA || value == MP_LCD_SPI_CMD) { 57024859b68Sbalrog s->mode = value; 57149fedd0dSJan Kiszka } else { 57224859b68Sbalrog s->mode = MP_LCD_SPI_INVALID; 57349fedd0dSJan Kiszka } 57424859b68Sbalrog break; 57524859b68Sbalrog 57624859b68Sbalrog case MP_LCD_INST: 57724859b68Sbalrog if (value >= MP_LCD_INST_SETPAGE0 && value <= MP_LCD_INST_SETPAGE7) { 57824859b68Sbalrog s->page = value - MP_LCD_INST_SETPAGE0; 57924859b68Sbalrog s->page_off = 0; 58024859b68Sbalrog } 58124859b68Sbalrog break; 58224859b68Sbalrog 58324859b68Sbalrog case MP_LCD_DATA: 58424859b68Sbalrog if (s->mode == MP_LCD_SPI_CMD) { 58524859b68Sbalrog if (value >= MP_LCD_INST_SETPAGE0 && 58624859b68Sbalrog value <= MP_LCD_INST_SETPAGE7) { 58724859b68Sbalrog s->page = value - MP_LCD_INST_SETPAGE0; 58824859b68Sbalrog s->page_off = 0; 58924859b68Sbalrog } 59024859b68Sbalrog } else if (s->mode == MP_LCD_SPI_DATA) { 59124859b68Sbalrog s->video_ram[s->page*128 + s->page_off] = value; 59224859b68Sbalrog s->page_off = (s->page_off + 1) & 127; 59324859b68Sbalrog } 59424859b68Sbalrog break; 59524859b68Sbalrog } 59624859b68Sbalrog } 59724859b68Sbalrog 59819b4a424SAvi Kivity static const MemoryRegionOps musicpal_lcd_ops = { 59919b4a424SAvi Kivity .read = musicpal_lcd_read, 60019b4a424SAvi Kivity .write = musicpal_lcd_write, 60119b4a424SAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 60224859b68Sbalrog }; 60324859b68Sbalrog 60481a322d4SGerd Hoffmann static int musicpal_lcd_init(SysBusDevice *dev) 60524859b68Sbalrog { 606b47b50faSPaul Brook musicpal_lcd_state *s = FROM_SYSBUS(musicpal_lcd_state, dev); 60724859b68Sbalrog 608343ec8e4SBenoit Canet s->brightness = 7; 609343ec8e4SBenoit Canet 61019b4a424SAvi Kivity memory_region_init_io(&s->iomem, &musicpal_lcd_ops, s, 61119b4a424SAvi Kivity "musicpal-lcd", MP_LCD_SIZE); 612750ecd44SAvi Kivity sysbus_init_mmio(dev, &s->iomem); 61324859b68Sbalrog 614c78f7137SGerd Hoffmann s->con = graphic_console_init(lcd_refresh, lcd_invalidate, 615c60e08d9Spbrook NULL, NULL, s); 616c78f7137SGerd Hoffmann qemu_console_resize(s->con, 128*3, 64*3); 617343ec8e4SBenoit Canet 618343ec8e4SBenoit Canet qdev_init_gpio_in(&dev->qdev, musicpal_lcd_gpio_brigthness_in, 3); 61981a322d4SGerd Hoffmann 62081a322d4SGerd Hoffmann return 0; 62124859b68Sbalrog } 62224859b68Sbalrog 623d5b61dddSJan Kiszka static const VMStateDescription musicpal_lcd_vmsd = { 624d5b61dddSJan Kiszka .name = "musicpal_lcd", 625d5b61dddSJan Kiszka .version_id = 1, 626d5b61dddSJan Kiszka .minimum_version_id = 1, 627d5b61dddSJan Kiszka .minimum_version_id_old = 1, 628d5b61dddSJan Kiszka .fields = (VMStateField[]) { 629d5b61dddSJan Kiszka VMSTATE_UINT32(brightness, musicpal_lcd_state), 630d5b61dddSJan Kiszka VMSTATE_UINT32(mode, musicpal_lcd_state), 631d5b61dddSJan Kiszka VMSTATE_UINT32(irqctrl, musicpal_lcd_state), 632d5b61dddSJan Kiszka VMSTATE_UINT32(page, musicpal_lcd_state), 633d5b61dddSJan Kiszka VMSTATE_UINT32(page_off, musicpal_lcd_state), 634d5b61dddSJan Kiszka VMSTATE_BUFFER(video_ram, musicpal_lcd_state), 635d5b61dddSJan Kiszka VMSTATE_END_OF_LIST() 636d5b61dddSJan Kiszka } 637d5b61dddSJan Kiszka }; 638d5b61dddSJan Kiszka 639999e12bbSAnthony Liguori static void musicpal_lcd_class_init(ObjectClass *klass, void *data) 640999e12bbSAnthony Liguori { 64139bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 642999e12bbSAnthony Liguori SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); 643999e12bbSAnthony Liguori 644999e12bbSAnthony Liguori k->init = musicpal_lcd_init; 64539bffca2SAnthony Liguori dc->vmsd = &musicpal_lcd_vmsd; 646999e12bbSAnthony Liguori } 647999e12bbSAnthony Liguori 6488c43a6f0SAndreas Färber static const TypeInfo musicpal_lcd_info = { 649999e12bbSAnthony Liguori .name = "musicpal_lcd", 65039bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 65139bffca2SAnthony Liguori .instance_size = sizeof(musicpal_lcd_state), 652999e12bbSAnthony Liguori .class_init = musicpal_lcd_class_init, 653d5b61dddSJan Kiszka }; 654d5b61dddSJan Kiszka 65524859b68Sbalrog /* PIC register offsets */ 65624859b68Sbalrog #define MP_PIC_STATUS 0x00 65724859b68Sbalrog #define MP_PIC_ENABLE_SET 0x08 65824859b68Sbalrog #define MP_PIC_ENABLE_CLR 0x0C 65924859b68Sbalrog 66024859b68Sbalrog typedef struct mv88w8618_pic_state 66124859b68Sbalrog { 662b47b50faSPaul Brook SysBusDevice busdev; 66319b4a424SAvi Kivity MemoryRegion iomem; 66424859b68Sbalrog uint32_t level; 66524859b68Sbalrog uint32_t enabled; 66624859b68Sbalrog qemu_irq parent_irq; 66724859b68Sbalrog } mv88w8618_pic_state; 66824859b68Sbalrog 66924859b68Sbalrog static void mv88w8618_pic_update(mv88w8618_pic_state *s) 67024859b68Sbalrog { 67124859b68Sbalrog qemu_set_irq(s->parent_irq, (s->level & s->enabled)); 67224859b68Sbalrog } 67324859b68Sbalrog 67424859b68Sbalrog static void mv88w8618_pic_set_irq(void *opaque, int irq, int level) 67524859b68Sbalrog { 67624859b68Sbalrog mv88w8618_pic_state *s = opaque; 67724859b68Sbalrog 67849fedd0dSJan Kiszka if (level) { 67924859b68Sbalrog s->level |= 1 << irq; 68049fedd0dSJan Kiszka } else { 68124859b68Sbalrog s->level &= ~(1 << irq); 68249fedd0dSJan Kiszka } 68324859b68Sbalrog mv88w8618_pic_update(s); 68424859b68Sbalrog } 68524859b68Sbalrog 686a8170e5eSAvi Kivity static uint64_t mv88w8618_pic_read(void *opaque, hwaddr offset, 68719b4a424SAvi Kivity unsigned size) 68824859b68Sbalrog { 68924859b68Sbalrog mv88w8618_pic_state *s = opaque; 69024859b68Sbalrog 69124859b68Sbalrog switch (offset) { 69224859b68Sbalrog case MP_PIC_STATUS: 69324859b68Sbalrog return s->level & s->enabled; 69424859b68Sbalrog 69524859b68Sbalrog default: 69624859b68Sbalrog return 0; 69724859b68Sbalrog } 69824859b68Sbalrog } 69924859b68Sbalrog 700a8170e5eSAvi Kivity static void mv88w8618_pic_write(void *opaque, hwaddr offset, 70119b4a424SAvi Kivity uint64_t value, unsigned size) 70224859b68Sbalrog { 70324859b68Sbalrog mv88w8618_pic_state *s = opaque; 70424859b68Sbalrog 70524859b68Sbalrog switch (offset) { 70624859b68Sbalrog case MP_PIC_ENABLE_SET: 70724859b68Sbalrog s->enabled |= value; 70824859b68Sbalrog break; 70924859b68Sbalrog 71024859b68Sbalrog case MP_PIC_ENABLE_CLR: 71124859b68Sbalrog s->enabled &= ~value; 71224859b68Sbalrog s->level &= ~value; 71324859b68Sbalrog break; 71424859b68Sbalrog } 71524859b68Sbalrog mv88w8618_pic_update(s); 71624859b68Sbalrog } 71724859b68Sbalrog 718d5b61dddSJan Kiszka static void mv88w8618_pic_reset(DeviceState *d) 71924859b68Sbalrog { 720d5b61dddSJan Kiszka mv88w8618_pic_state *s = FROM_SYSBUS(mv88w8618_pic_state, 7211356b98dSAndreas Färber SYS_BUS_DEVICE(d)); 72224859b68Sbalrog 72324859b68Sbalrog s->level = 0; 72424859b68Sbalrog s->enabled = 0; 72524859b68Sbalrog } 72624859b68Sbalrog 72719b4a424SAvi Kivity static const MemoryRegionOps mv88w8618_pic_ops = { 72819b4a424SAvi Kivity .read = mv88w8618_pic_read, 72919b4a424SAvi Kivity .write = mv88w8618_pic_write, 73019b4a424SAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 73124859b68Sbalrog }; 73224859b68Sbalrog 73381a322d4SGerd Hoffmann static int mv88w8618_pic_init(SysBusDevice *dev) 73424859b68Sbalrog { 735b47b50faSPaul Brook mv88w8618_pic_state *s = FROM_SYSBUS(mv88w8618_pic_state, dev); 73624859b68Sbalrog 737067a3ddcSPaul Brook qdev_init_gpio_in(&dev->qdev, mv88w8618_pic_set_irq, 32); 738b47b50faSPaul Brook sysbus_init_irq(dev, &s->parent_irq); 73919b4a424SAvi Kivity memory_region_init_io(&s->iomem, &mv88w8618_pic_ops, s, 74019b4a424SAvi Kivity "musicpal-pic", MP_PIC_SIZE); 741750ecd44SAvi Kivity sysbus_init_mmio(dev, &s->iomem); 74281a322d4SGerd Hoffmann return 0; 74324859b68Sbalrog } 74424859b68Sbalrog 745d5b61dddSJan Kiszka static const VMStateDescription mv88w8618_pic_vmsd = { 746d5b61dddSJan Kiszka .name = "mv88w8618_pic", 747d5b61dddSJan Kiszka .version_id = 1, 748d5b61dddSJan Kiszka .minimum_version_id = 1, 749d5b61dddSJan Kiszka .minimum_version_id_old = 1, 750d5b61dddSJan Kiszka .fields = (VMStateField[]) { 751d5b61dddSJan Kiszka VMSTATE_UINT32(level, mv88w8618_pic_state), 752d5b61dddSJan Kiszka VMSTATE_UINT32(enabled, mv88w8618_pic_state), 753d5b61dddSJan Kiszka VMSTATE_END_OF_LIST() 754d5b61dddSJan Kiszka } 755d5b61dddSJan Kiszka }; 756d5b61dddSJan Kiszka 757999e12bbSAnthony Liguori static void mv88w8618_pic_class_init(ObjectClass *klass, void *data) 758999e12bbSAnthony Liguori { 75939bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 760999e12bbSAnthony Liguori SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); 761999e12bbSAnthony Liguori 762999e12bbSAnthony Liguori k->init = mv88w8618_pic_init; 76339bffca2SAnthony Liguori dc->reset = mv88w8618_pic_reset; 76439bffca2SAnthony Liguori dc->vmsd = &mv88w8618_pic_vmsd; 765999e12bbSAnthony Liguori } 766999e12bbSAnthony Liguori 7678c43a6f0SAndreas Färber static const TypeInfo mv88w8618_pic_info = { 768999e12bbSAnthony Liguori .name = "mv88w8618_pic", 76939bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 77039bffca2SAnthony Liguori .instance_size = sizeof(mv88w8618_pic_state), 771999e12bbSAnthony Liguori .class_init = mv88w8618_pic_class_init, 772d5b61dddSJan Kiszka }; 773d5b61dddSJan Kiszka 77424859b68Sbalrog /* PIT register offsets */ 77524859b68Sbalrog #define MP_PIT_TIMER1_LENGTH 0x00 77624859b68Sbalrog /* ... */ 77724859b68Sbalrog #define MP_PIT_TIMER4_LENGTH 0x0C 77824859b68Sbalrog #define MP_PIT_CONTROL 0x10 77924859b68Sbalrog #define MP_PIT_TIMER1_VALUE 0x14 78024859b68Sbalrog /* ... */ 78124859b68Sbalrog #define MP_PIT_TIMER4_VALUE 0x20 78224859b68Sbalrog #define MP_BOARD_RESET 0x34 78324859b68Sbalrog 78424859b68Sbalrog /* Magic board reset value (probably some watchdog behind it) */ 78524859b68Sbalrog #define MP_BOARD_RESET_MAGIC 0x10000 78624859b68Sbalrog 78724859b68Sbalrog typedef struct mv88w8618_timer_state { 788b47b50faSPaul Brook ptimer_state *ptimer; 78924859b68Sbalrog uint32_t limit; 79024859b68Sbalrog int freq; 79124859b68Sbalrog qemu_irq irq; 79224859b68Sbalrog } mv88w8618_timer_state; 79324859b68Sbalrog 79424859b68Sbalrog typedef struct mv88w8618_pit_state { 795b47b50faSPaul Brook SysBusDevice busdev; 79619b4a424SAvi Kivity MemoryRegion iomem; 797b47b50faSPaul Brook mv88w8618_timer_state timer[4]; 79824859b68Sbalrog } mv88w8618_pit_state; 79924859b68Sbalrog 80024859b68Sbalrog static void mv88w8618_timer_tick(void *opaque) 80124859b68Sbalrog { 80224859b68Sbalrog mv88w8618_timer_state *s = opaque; 80324859b68Sbalrog 80424859b68Sbalrog qemu_irq_raise(s->irq); 80524859b68Sbalrog } 80624859b68Sbalrog 807b47b50faSPaul Brook static void mv88w8618_timer_init(SysBusDevice *dev, mv88w8618_timer_state *s, 808b47b50faSPaul Brook uint32_t freq) 80924859b68Sbalrog { 81024859b68Sbalrog QEMUBH *bh; 81124859b68Sbalrog 812b47b50faSPaul Brook sysbus_init_irq(dev, &s->irq); 81324859b68Sbalrog s->freq = freq; 81424859b68Sbalrog 81524859b68Sbalrog bh = qemu_bh_new(mv88w8618_timer_tick, s); 816b47b50faSPaul Brook s->ptimer = ptimer_init(bh); 81724859b68Sbalrog } 81824859b68Sbalrog 819a8170e5eSAvi Kivity static uint64_t mv88w8618_pit_read(void *opaque, hwaddr offset, 82019b4a424SAvi Kivity unsigned size) 82124859b68Sbalrog { 82224859b68Sbalrog mv88w8618_pit_state *s = opaque; 82324859b68Sbalrog mv88w8618_timer_state *t; 82424859b68Sbalrog 82524859b68Sbalrog switch (offset) { 82624859b68Sbalrog case MP_PIT_TIMER1_VALUE ... MP_PIT_TIMER4_VALUE: 827b47b50faSPaul Brook t = &s->timer[(offset-MP_PIT_TIMER1_VALUE) >> 2]; 828b47b50faSPaul Brook return ptimer_get_count(t->ptimer); 82924859b68Sbalrog 83024859b68Sbalrog default: 83124859b68Sbalrog return 0; 83224859b68Sbalrog } 83324859b68Sbalrog } 83424859b68Sbalrog 835a8170e5eSAvi Kivity static void mv88w8618_pit_write(void *opaque, hwaddr offset, 83619b4a424SAvi Kivity uint64_t value, unsigned size) 83724859b68Sbalrog { 83824859b68Sbalrog mv88w8618_pit_state *s = opaque; 83924859b68Sbalrog mv88w8618_timer_state *t; 84024859b68Sbalrog int i; 84124859b68Sbalrog 84224859b68Sbalrog switch (offset) { 84324859b68Sbalrog case MP_PIT_TIMER1_LENGTH ... MP_PIT_TIMER4_LENGTH: 844b47b50faSPaul Brook t = &s->timer[offset >> 2]; 84524859b68Sbalrog t->limit = value; 846c88d6bdeSJan Kiszka if (t->limit > 0) { 847b47b50faSPaul Brook ptimer_set_limit(t->ptimer, t->limit, 1); 848c88d6bdeSJan Kiszka } else { 849c88d6bdeSJan Kiszka ptimer_stop(t->ptimer); 850c88d6bdeSJan Kiszka } 85124859b68Sbalrog break; 85224859b68Sbalrog 85324859b68Sbalrog case MP_PIT_CONTROL: 85424859b68Sbalrog for (i = 0; i < 4; i++) { 855b47b50faSPaul Brook t = &s->timer[i]; 856c88d6bdeSJan Kiszka if (value & 0xf && t->limit > 0) { 857b47b50faSPaul Brook ptimer_set_limit(t->ptimer, t->limit, 0); 858b47b50faSPaul Brook ptimer_set_freq(t->ptimer, t->freq); 859b47b50faSPaul Brook ptimer_run(t->ptimer, 0); 860c88d6bdeSJan Kiszka } else { 861c88d6bdeSJan Kiszka ptimer_stop(t->ptimer); 86224859b68Sbalrog } 86324859b68Sbalrog value >>= 4; 86424859b68Sbalrog } 86524859b68Sbalrog break; 86624859b68Sbalrog 86724859b68Sbalrog case MP_BOARD_RESET: 86849fedd0dSJan Kiszka if (value == MP_BOARD_RESET_MAGIC) { 86924859b68Sbalrog qemu_system_reset_request(); 87049fedd0dSJan Kiszka } 87124859b68Sbalrog break; 87224859b68Sbalrog } 87324859b68Sbalrog } 87424859b68Sbalrog 875d5b61dddSJan Kiszka static void mv88w8618_pit_reset(DeviceState *d) 876c88d6bdeSJan Kiszka { 877d5b61dddSJan Kiszka mv88w8618_pit_state *s = FROM_SYSBUS(mv88w8618_pit_state, 8781356b98dSAndreas Färber SYS_BUS_DEVICE(d)); 879c88d6bdeSJan Kiszka int i; 880c88d6bdeSJan Kiszka 881c88d6bdeSJan Kiszka for (i = 0; i < 4; i++) { 882c88d6bdeSJan Kiszka ptimer_stop(s->timer[i].ptimer); 883c88d6bdeSJan Kiszka s->timer[i].limit = 0; 884c88d6bdeSJan Kiszka } 885c88d6bdeSJan Kiszka } 886c88d6bdeSJan Kiszka 88719b4a424SAvi Kivity static const MemoryRegionOps mv88w8618_pit_ops = { 88819b4a424SAvi Kivity .read = mv88w8618_pit_read, 88919b4a424SAvi Kivity .write = mv88w8618_pit_write, 89019b4a424SAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 89124859b68Sbalrog }; 89224859b68Sbalrog 89381a322d4SGerd Hoffmann static int mv88w8618_pit_init(SysBusDevice *dev) 89424859b68Sbalrog { 895b47b50faSPaul Brook mv88w8618_pit_state *s = FROM_SYSBUS(mv88w8618_pit_state, dev); 896b47b50faSPaul Brook int i; 89724859b68Sbalrog 89824859b68Sbalrog /* Letting them all run at 1 MHz is likely just a pragmatic 89924859b68Sbalrog * simplification. */ 900b47b50faSPaul Brook for (i = 0; i < 4; i++) { 901b47b50faSPaul Brook mv88w8618_timer_init(dev, &s->timer[i], 1000000); 902b47b50faSPaul Brook } 90324859b68Sbalrog 90419b4a424SAvi Kivity memory_region_init_io(&s->iomem, &mv88w8618_pit_ops, s, 90519b4a424SAvi Kivity "musicpal-pit", MP_PIT_SIZE); 906750ecd44SAvi Kivity sysbus_init_mmio(dev, &s->iomem); 90781a322d4SGerd Hoffmann return 0; 90824859b68Sbalrog } 90924859b68Sbalrog 910d5b61dddSJan Kiszka static const VMStateDescription mv88w8618_timer_vmsd = { 911d5b61dddSJan Kiszka .name = "timer", 912d5b61dddSJan Kiszka .version_id = 1, 913d5b61dddSJan Kiszka .minimum_version_id = 1, 914d5b61dddSJan Kiszka .minimum_version_id_old = 1, 915d5b61dddSJan Kiszka .fields = (VMStateField[]) { 916d5b61dddSJan Kiszka VMSTATE_PTIMER(ptimer, mv88w8618_timer_state), 917d5b61dddSJan Kiszka VMSTATE_UINT32(limit, mv88w8618_timer_state), 918d5b61dddSJan Kiszka VMSTATE_END_OF_LIST() 919d5b61dddSJan Kiszka } 920d5b61dddSJan Kiszka }; 921d5b61dddSJan Kiszka 922d5b61dddSJan Kiszka static const VMStateDescription mv88w8618_pit_vmsd = { 923d5b61dddSJan Kiszka .name = "mv88w8618_pit", 924d5b61dddSJan Kiszka .version_id = 1, 925d5b61dddSJan Kiszka .minimum_version_id = 1, 926d5b61dddSJan Kiszka .minimum_version_id_old = 1, 927d5b61dddSJan Kiszka .fields = (VMStateField[]) { 928d5b61dddSJan Kiszka VMSTATE_STRUCT_ARRAY(timer, mv88w8618_pit_state, 4, 1, 929d5b61dddSJan Kiszka mv88w8618_timer_vmsd, mv88w8618_timer_state), 930d5b61dddSJan Kiszka VMSTATE_END_OF_LIST() 931d5b61dddSJan Kiszka } 932d5b61dddSJan Kiszka }; 933d5b61dddSJan Kiszka 934999e12bbSAnthony Liguori static void mv88w8618_pit_class_init(ObjectClass *klass, void *data) 935999e12bbSAnthony Liguori { 93639bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 937999e12bbSAnthony Liguori SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); 938999e12bbSAnthony Liguori 939999e12bbSAnthony Liguori k->init = mv88w8618_pit_init; 94039bffca2SAnthony Liguori dc->reset = mv88w8618_pit_reset; 94139bffca2SAnthony Liguori dc->vmsd = &mv88w8618_pit_vmsd; 942999e12bbSAnthony Liguori } 943999e12bbSAnthony Liguori 9448c43a6f0SAndreas Färber static const TypeInfo mv88w8618_pit_info = { 945999e12bbSAnthony Liguori .name = "mv88w8618_pit", 94639bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 94739bffca2SAnthony Liguori .instance_size = sizeof(mv88w8618_pit_state), 948999e12bbSAnthony Liguori .class_init = mv88w8618_pit_class_init, 949c88d6bdeSJan Kiszka }; 950c88d6bdeSJan Kiszka 95124859b68Sbalrog /* Flash config register offsets */ 95224859b68Sbalrog #define MP_FLASHCFG_CFGR0 0x04 95324859b68Sbalrog 95424859b68Sbalrog typedef struct mv88w8618_flashcfg_state { 955b47b50faSPaul Brook SysBusDevice busdev; 95619b4a424SAvi Kivity MemoryRegion iomem; 95724859b68Sbalrog uint32_t cfgr0; 95824859b68Sbalrog } mv88w8618_flashcfg_state; 95924859b68Sbalrog 96019b4a424SAvi Kivity static uint64_t mv88w8618_flashcfg_read(void *opaque, 961a8170e5eSAvi Kivity hwaddr offset, 96219b4a424SAvi Kivity unsigned size) 96324859b68Sbalrog { 96424859b68Sbalrog mv88w8618_flashcfg_state *s = opaque; 96524859b68Sbalrog 96624859b68Sbalrog switch (offset) { 96724859b68Sbalrog case MP_FLASHCFG_CFGR0: 96824859b68Sbalrog return s->cfgr0; 96924859b68Sbalrog 97024859b68Sbalrog default: 97124859b68Sbalrog return 0; 97224859b68Sbalrog } 97324859b68Sbalrog } 97424859b68Sbalrog 975a8170e5eSAvi Kivity static void mv88w8618_flashcfg_write(void *opaque, hwaddr offset, 97619b4a424SAvi Kivity uint64_t value, unsigned size) 97724859b68Sbalrog { 97824859b68Sbalrog mv88w8618_flashcfg_state *s = opaque; 97924859b68Sbalrog 98024859b68Sbalrog switch (offset) { 98124859b68Sbalrog case MP_FLASHCFG_CFGR0: 98224859b68Sbalrog s->cfgr0 = value; 98324859b68Sbalrog break; 98424859b68Sbalrog } 98524859b68Sbalrog } 98624859b68Sbalrog 98719b4a424SAvi Kivity static const MemoryRegionOps mv88w8618_flashcfg_ops = { 98819b4a424SAvi Kivity .read = mv88w8618_flashcfg_read, 98919b4a424SAvi Kivity .write = mv88w8618_flashcfg_write, 99019b4a424SAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 99124859b68Sbalrog }; 99224859b68Sbalrog 99381a322d4SGerd Hoffmann static int mv88w8618_flashcfg_init(SysBusDevice *dev) 99424859b68Sbalrog { 995b47b50faSPaul Brook mv88w8618_flashcfg_state *s = FROM_SYSBUS(mv88w8618_flashcfg_state, dev); 99624859b68Sbalrog 99724859b68Sbalrog s->cfgr0 = 0xfffe4285; /* Default as set by U-Boot for 8 MB flash */ 99819b4a424SAvi Kivity memory_region_init_io(&s->iomem, &mv88w8618_flashcfg_ops, s, 99919b4a424SAvi Kivity "musicpal-flashcfg", MP_FLASHCFG_SIZE); 1000750ecd44SAvi Kivity sysbus_init_mmio(dev, &s->iomem); 100181a322d4SGerd Hoffmann return 0; 100224859b68Sbalrog } 100324859b68Sbalrog 1004d5b61dddSJan Kiszka static const VMStateDescription mv88w8618_flashcfg_vmsd = { 1005d5b61dddSJan Kiszka .name = "mv88w8618_flashcfg", 1006d5b61dddSJan Kiszka .version_id = 1, 1007d5b61dddSJan Kiszka .minimum_version_id = 1, 1008d5b61dddSJan Kiszka .minimum_version_id_old = 1, 1009d5b61dddSJan Kiszka .fields = (VMStateField[]) { 1010d5b61dddSJan Kiszka VMSTATE_UINT32(cfgr0, mv88w8618_flashcfg_state), 1011d5b61dddSJan Kiszka VMSTATE_END_OF_LIST() 1012d5b61dddSJan Kiszka } 1013d5b61dddSJan Kiszka }; 1014d5b61dddSJan Kiszka 1015999e12bbSAnthony Liguori static void mv88w8618_flashcfg_class_init(ObjectClass *klass, void *data) 1016999e12bbSAnthony Liguori { 101739bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 1018999e12bbSAnthony Liguori SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); 1019999e12bbSAnthony Liguori 1020999e12bbSAnthony Liguori k->init = mv88w8618_flashcfg_init; 102139bffca2SAnthony Liguori dc->vmsd = &mv88w8618_flashcfg_vmsd; 1022999e12bbSAnthony Liguori } 1023999e12bbSAnthony Liguori 10248c43a6f0SAndreas Färber static const TypeInfo mv88w8618_flashcfg_info = { 1025999e12bbSAnthony Liguori .name = "mv88w8618_flashcfg", 102639bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 102739bffca2SAnthony Liguori .instance_size = sizeof(mv88w8618_flashcfg_state), 1028999e12bbSAnthony Liguori .class_init = mv88w8618_flashcfg_class_init, 1029d5b61dddSJan Kiszka }; 1030d5b61dddSJan Kiszka 1031718ec0beSmalc /* Misc register offsets */ 1032718ec0beSmalc #define MP_MISC_BOARD_REVISION 0x18 103324859b68Sbalrog 1034718ec0beSmalc #define MP_BOARD_REVISION 0x31 103524859b68Sbalrog 1036a86f200aSPeter Maydell typedef struct { 1037a86f200aSPeter Maydell SysBusDevice parent_obj; 1038a86f200aSPeter Maydell MemoryRegion iomem; 1039a86f200aSPeter Maydell } MusicPalMiscState; 1040a86f200aSPeter Maydell 1041a86f200aSPeter Maydell #define TYPE_MUSICPAL_MISC "musicpal-misc" 1042a86f200aSPeter Maydell #define MUSICPAL_MISC(obj) \ 1043a86f200aSPeter Maydell OBJECT_CHECK(MusicPalMiscState, (obj), TYPE_MUSICPAL_MISC) 1044a86f200aSPeter Maydell 1045a8170e5eSAvi Kivity static uint64_t musicpal_misc_read(void *opaque, hwaddr offset, 104619b4a424SAvi Kivity unsigned size) 1047718ec0beSmalc { 1048718ec0beSmalc switch (offset) { 1049718ec0beSmalc case MP_MISC_BOARD_REVISION: 1050718ec0beSmalc return MP_BOARD_REVISION; 1051718ec0beSmalc 1052718ec0beSmalc default: 1053718ec0beSmalc return 0; 1054718ec0beSmalc } 1055718ec0beSmalc } 1056718ec0beSmalc 1057a8170e5eSAvi Kivity static void musicpal_misc_write(void *opaque, hwaddr offset, 105819b4a424SAvi Kivity uint64_t value, unsigned size) 1059718ec0beSmalc { 1060718ec0beSmalc } 1061718ec0beSmalc 106219b4a424SAvi Kivity static const MemoryRegionOps musicpal_misc_ops = { 106319b4a424SAvi Kivity .read = musicpal_misc_read, 106419b4a424SAvi Kivity .write = musicpal_misc_write, 106519b4a424SAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 1066718ec0beSmalc }; 1067718ec0beSmalc 1068a86f200aSPeter Maydell static void musicpal_misc_init(Object *obj) 1069718ec0beSmalc { 1070a86f200aSPeter Maydell SysBusDevice *sd = SYS_BUS_DEVICE(obj); 1071a86f200aSPeter Maydell MusicPalMiscState *s = MUSICPAL_MISC(obj); 1072718ec0beSmalc 1073a86f200aSPeter Maydell memory_region_init_io(&s->iomem, &musicpal_misc_ops, NULL, 107419b4a424SAvi Kivity "musicpal-misc", MP_MISC_SIZE); 1075a86f200aSPeter Maydell sysbus_init_mmio(sd, &s->iomem); 1076718ec0beSmalc } 1077718ec0beSmalc 1078a86f200aSPeter Maydell static const TypeInfo musicpal_misc_info = { 1079a86f200aSPeter Maydell .name = TYPE_MUSICPAL_MISC, 1080a86f200aSPeter Maydell .parent = TYPE_SYS_BUS_DEVICE, 1081a86f200aSPeter Maydell .instance_init = musicpal_misc_init, 1082a86f200aSPeter Maydell .instance_size = sizeof(MusicPalMiscState), 1083a86f200aSPeter Maydell }; 1084a86f200aSPeter Maydell 1085718ec0beSmalc /* WLAN register offsets */ 1086718ec0beSmalc #define MP_WLAN_MAGIC1 0x11c 1087718ec0beSmalc #define MP_WLAN_MAGIC2 0x124 1088718ec0beSmalc 1089a8170e5eSAvi Kivity static uint64_t mv88w8618_wlan_read(void *opaque, hwaddr offset, 109019b4a424SAvi Kivity unsigned size) 1091718ec0beSmalc { 1092718ec0beSmalc switch (offset) { 1093718ec0beSmalc /* Workaround to allow loading the binary-only wlandrv.ko crap 1094718ec0beSmalc * from the original Freecom firmware. */ 1095718ec0beSmalc case MP_WLAN_MAGIC1: 1096718ec0beSmalc return ~3; 1097718ec0beSmalc case MP_WLAN_MAGIC2: 1098718ec0beSmalc return -1; 1099718ec0beSmalc 1100718ec0beSmalc default: 1101718ec0beSmalc return 0; 1102718ec0beSmalc } 1103718ec0beSmalc } 1104718ec0beSmalc 1105a8170e5eSAvi Kivity static void mv88w8618_wlan_write(void *opaque, hwaddr offset, 110619b4a424SAvi Kivity uint64_t value, unsigned size) 1107718ec0beSmalc { 1108718ec0beSmalc } 1109718ec0beSmalc 111019b4a424SAvi Kivity static const MemoryRegionOps mv88w8618_wlan_ops = { 111119b4a424SAvi Kivity .read = mv88w8618_wlan_read, 111219b4a424SAvi Kivity .write =mv88w8618_wlan_write, 111319b4a424SAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 1114718ec0beSmalc }; 1115718ec0beSmalc 111681a322d4SGerd Hoffmann static int mv88w8618_wlan_init(SysBusDevice *dev) 1117718ec0beSmalc { 111819b4a424SAvi Kivity MemoryRegion *iomem = g_new(MemoryRegion, 1); 1119718ec0beSmalc 112019b4a424SAvi Kivity memory_region_init_io(iomem, &mv88w8618_wlan_ops, NULL, 112119b4a424SAvi Kivity "musicpal-wlan", MP_WLAN_SIZE); 1122750ecd44SAvi Kivity sysbus_init_mmio(dev, iomem); 112381a322d4SGerd Hoffmann return 0; 1124718ec0beSmalc } 1125718ec0beSmalc 1126718ec0beSmalc /* GPIO register offsets */ 1127718ec0beSmalc #define MP_GPIO_OE_LO 0x008 1128718ec0beSmalc #define MP_GPIO_OUT_LO 0x00c 1129718ec0beSmalc #define MP_GPIO_IN_LO 0x010 1130708afdf3SJan Kiszka #define MP_GPIO_IER_LO 0x014 1131708afdf3SJan Kiszka #define MP_GPIO_IMR_LO 0x018 1132718ec0beSmalc #define MP_GPIO_ISR_LO 0x020 1133718ec0beSmalc #define MP_GPIO_OE_HI 0x508 1134718ec0beSmalc #define MP_GPIO_OUT_HI 0x50c 1135718ec0beSmalc #define MP_GPIO_IN_HI 0x510 1136708afdf3SJan Kiszka #define MP_GPIO_IER_HI 0x514 1137708afdf3SJan Kiszka #define MP_GPIO_IMR_HI 0x518 1138718ec0beSmalc #define MP_GPIO_ISR_HI 0x520 113924859b68Sbalrog 114024859b68Sbalrog /* GPIO bits & masks */ 114124859b68Sbalrog #define MP_GPIO_LCD_BRIGHTNESS 0x00070000 114224859b68Sbalrog #define MP_GPIO_I2C_DATA_BIT 29 114324859b68Sbalrog #define MP_GPIO_I2C_CLOCK_BIT 30 114424859b68Sbalrog 114524859b68Sbalrog /* LCD brightness bits in GPIO_OE_HI */ 114624859b68Sbalrog #define MP_OE_LCD_BRIGHTNESS 0x0007 114724859b68Sbalrog 1148343ec8e4SBenoit Canet typedef struct musicpal_gpio_state { 1149343ec8e4SBenoit Canet SysBusDevice busdev; 115019b4a424SAvi Kivity MemoryRegion iomem; 1151343ec8e4SBenoit Canet uint32_t lcd_brightness; 1152343ec8e4SBenoit Canet uint32_t out_state; 1153343ec8e4SBenoit Canet uint32_t in_state; 1154708afdf3SJan Kiszka uint32_t ier; 1155708afdf3SJan Kiszka uint32_t imr; 1156343ec8e4SBenoit Canet uint32_t isr; 1157343ec8e4SBenoit Canet qemu_irq irq; 1158708afdf3SJan Kiszka qemu_irq out[5]; /* 3 brightness out + 2 lcd (data and clock ) */ 1159343ec8e4SBenoit Canet } musicpal_gpio_state; 1160343ec8e4SBenoit Canet 1161343ec8e4SBenoit Canet static void musicpal_gpio_brightness_update(musicpal_gpio_state *s) { 1162343ec8e4SBenoit Canet int i; 1163343ec8e4SBenoit Canet uint32_t brightness; 1164343ec8e4SBenoit Canet 1165343ec8e4SBenoit Canet /* compute brightness ratio */ 1166343ec8e4SBenoit Canet switch (s->lcd_brightness) { 1167343ec8e4SBenoit Canet case 0x00000007: 1168343ec8e4SBenoit Canet brightness = 0; 1169343ec8e4SBenoit Canet break; 1170343ec8e4SBenoit Canet 1171343ec8e4SBenoit Canet case 0x00020000: 1172343ec8e4SBenoit Canet brightness = 1; 1173343ec8e4SBenoit Canet break; 1174343ec8e4SBenoit Canet 1175343ec8e4SBenoit Canet case 0x00020001: 1176343ec8e4SBenoit Canet brightness = 2; 1177343ec8e4SBenoit Canet break; 1178343ec8e4SBenoit Canet 1179343ec8e4SBenoit Canet case 0x00040000: 1180343ec8e4SBenoit Canet brightness = 3; 1181343ec8e4SBenoit Canet break; 1182343ec8e4SBenoit Canet 1183343ec8e4SBenoit Canet case 0x00010006: 1184343ec8e4SBenoit Canet brightness = 4; 1185343ec8e4SBenoit Canet break; 1186343ec8e4SBenoit Canet 1187343ec8e4SBenoit Canet case 0x00020005: 1188343ec8e4SBenoit Canet brightness = 5; 1189343ec8e4SBenoit Canet break; 1190343ec8e4SBenoit Canet 1191343ec8e4SBenoit Canet case 0x00040003: 1192343ec8e4SBenoit Canet brightness = 6; 1193343ec8e4SBenoit Canet break; 1194343ec8e4SBenoit Canet 1195343ec8e4SBenoit Canet case 0x00030004: 1196343ec8e4SBenoit Canet default: 1197343ec8e4SBenoit Canet brightness = 7; 1198343ec8e4SBenoit Canet } 1199343ec8e4SBenoit Canet 1200343ec8e4SBenoit Canet /* set lcd brightness GPIOs */ 120149fedd0dSJan Kiszka for (i = 0; i <= 2; i++) { 1202343ec8e4SBenoit Canet qemu_set_irq(s->out[i], (brightness >> i) & 1); 1203343ec8e4SBenoit Canet } 120449fedd0dSJan Kiszka } 1205343ec8e4SBenoit Canet 1206708afdf3SJan Kiszka static void musicpal_gpio_pin_event(void *opaque, int pin, int level) 1207343ec8e4SBenoit Canet { 1208243cd13cSJan Kiszka musicpal_gpio_state *s = opaque; 1209708afdf3SJan Kiszka uint32_t mask = 1 << pin; 1210708afdf3SJan Kiszka uint32_t delta = level << pin; 1211708afdf3SJan Kiszka uint32_t old = s->in_state & mask; 1212343ec8e4SBenoit Canet 1213708afdf3SJan Kiszka s->in_state &= ~mask; 1214708afdf3SJan Kiszka s->in_state |= delta; 1215708afdf3SJan Kiszka 1216708afdf3SJan Kiszka if ((old ^ delta) && 1217708afdf3SJan Kiszka ((level && (s->imr & mask)) || (!level && (s->ier & mask)))) { 1218708afdf3SJan Kiszka s->isr = mask; 1219708afdf3SJan Kiszka qemu_irq_raise(s->irq); 1220d074769cSAndrzej Zaborowski } 1221343ec8e4SBenoit Canet } 1222343ec8e4SBenoit Canet 1223a8170e5eSAvi Kivity static uint64_t musicpal_gpio_read(void *opaque, hwaddr offset, 122419b4a424SAvi Kivity unsigned size) 122524859b68Sbalrog { 1226243cd13cSJan Kiszka musicpal_gpio_state *s = opaque; 1227343ec8e4SBenoit Canet 122824859b68Sbalrog switch (offset) { 122924859b68Sbalrog case MP_GPIO_OE_HI: /* used for LCD brightness control */ 1230343ec8e4SBenoit Canet return s->lcd_brightness & MP_OE_LCD_BRIGHTNESS; 123124859b68Sbalrog 123224859b68Sbalrog case MP_GPIO_OUT_LO: 1233343ec8e4SBenoit Canet return s->out_state & 0xFFFF; 123424859b68Sbalrog case MP_GPIO_OUT_HI: 1235343ec8e4SBenoit Canet return s->out_state >> 16; 123624859b68Sbalrog 123724859b68Sbalrog case MP_GPIO_IN_LO: 1238343ec8e4SBenoit Canet return s->in_state & 0xFFFF; 123924859b68Sbalrog case MP_GPIO_IN_HI: 1240343ec8e4SBenoit Canet return s->in_state >> 16; 124124859b68Sbalrog 1242708afdf3SJan Kiszka case MP_GPIO_IER_LO: 1243708afdf3SJan Kiszka return s->ier & 0xFFFF; 1244708afdf3SJan Kiszka case MP_GPIO_IER_HI: 1245708afdf3SJan Kiszka return s->ier >> 16; 1246708afdf3SJan Kiszka 1247708afdf3SJan Kiszka case MP_GPIO_IMR_LO: 1248708afdf3SJan Kiszka return s->imr & 0xFFFF; 1249708afdf3SJan Kiszka case MP_GPIO_IMR_HI: 1250708afdf3SJan Kiszka return s->imr >> 16; 1251708afdf3SJan Kiszka 125224859b68Sbalrog case MP_GPIO_ISR_LO: 1253343ec8e4SBenoit Canet return s->isr & 0xFFFF; 125424859b68Sbalrog case MP_GPIO_ISR_HI: 1255343ec8e4SBenoit Canet return s->isr >> 16; 125624859b68Sbalrog 125724859b68Sbalrog default: 125824859b68Sbalrog return 0; 125924859b68Sbalrog } 126024859b68Sbalrog } 126124859b68Sbalrog 1262a8170e5eSAvi Kivity static void musicpal_gpio_write(void *opaque, hwaddr offset, 126319b4a424SAvi Kivity uint64_t value, unsigned size) 126424859b68Sbalrog { 1265243cd13cSJan Kiszka musicpal_gpio_state *s = opaque; 126624859b68Sbalrog switch (offset) { 126724859b68Sbalrog case MP_GPIO_OE_HI: /* used for LCD brightness control */ 1268343ec8e4SBenoit Canet s->lcd_brightness = (s->lcd_brightness & MP_GPIO_LCD_BRIGHTNESS) | 126924859b68Sbalrog (value & MP_OE_LCD_BRIGHTNESS); 1270343ec8e4SBenoit Canet musicpal_gpio_brightness_update(s); 127124859b68Sbalrog break; 127224859b68Sbalrog 127324859b68Sbalrog case MP_GPIO_OUT_LO: 1274343ec8e4SBenoit Canet s->out_state = (s->out_state & 0xFFFF0000) | (value & 0xFFFF); 127524859b68Sbalrog break; 127624859b68Sbalrog case MP_GPIO_OUT_HI: 1277343ec8e4SBenoit Canet s->out_state = (s->out_state & 0xFFFF) | (value << 16); 1278343ec8e4SBenoit Canet s->lcd_brightness = (s->lcd_brightness & 0xFFFF) | 1279343ec8e4SBenoit Canet (s->out_state & MP_GPIO_LCD_BRIGHTNESS); 1280343ec8e4SBenoit Canet musicpal_gpio_brightness_update(s); 1281d074769cSAndrzej Zaborowski qemu_set_irq(s->out[3], (s->out_state >> MP_GPIO_I2C_DATA_BIT) & 1); 1282d074769cSAndrzej Zaborowski qemu_set_irq(s->out[4], (s->out_state >> MP_GPIO_I2C_CLOCK_BIT) & 1); 128324859b68Sbalrog break; 128424859b68Sbalrog 1285708afdf3SJan Kiszka case MP_GPIO_IER_LO: 1286708afdf3SJan Kiszka s->ier = (s->ier & 0xFFFF0000) | (value & 0xFFFF); 1287708afdf3SJan Kiszka break; 1288708afdf3SJan Kiszka case MP_GPIO_IER_HI: 1289708afdf3SJan Kiszka s->ier = (s->ier & 0xFFFF) | (value << 16); 1290708afdf3SJan Kiszka break; 1291708afdf3SJan Kiszka 1292708afdf3SJan Kiszka case MP_GPIO_IMR_LO: 1293708afdf3SJan Kiszka s->imr = (s->imr & 0xFFFF0000) | (value & 0xFFFF); 1294708afdf3SJan Kiszka break; 1295708afdf3SJan Kiszka case MP_GPIO_IMR_HI: 1296708afdf3SJan Kiszka s->imr = (s->imr & 0xFFFF) | (value << 16); 1297708afdf3SJan Kiszka break; 129824859b68Sbalrog } 129924859b68Sbalrog } 130024859b68Sbalrog 130119b4a424SAvi Kivity static const MemoryRegionOps musicpal_gpio_ops = { 130219b4a424SAvi Kivity .read = musicpal_gpio_read, 130319b4a424SAvi Kivity .write = musicpal_gpio_write, 130419b4a424SAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 1305718ec0beSmalc }; 1306718ec0beSmalc 1307d5b61dddSJan Kiszka static void musicpal_gpio_reset(DeviceState *d) 1308718ec0beSmalc { 1309d5b61dddSJan Kiszka musicpal_gpio_state *s = FROM_SYSBUS(musicpal_gpio_state, 13101356b98dSAndreas Färber SYS_BUS_DEVICE(d)); 131130624c92SJan Kiszka 131230624c92SJan Kiszka s->lcd_brightness = 0; 131330624c92SJan Kiszka s->out_state = 0; 1314343ec8e4SBenoit Canet s->in_state = 0xffffffff; 1315708afdf3SJan Kiszka s->ier = 0; 1316708afdf3SJan Kiszka s->imr = 0; 1317343ec8e4SBenoit Canet s->isr = 0; 1318343ec8e4SBenoit Canet } 1319343ec8e4SBenoit Canet 132081a322d4SGerd Hoffmann static int musicpal_gpio_init(SysBusDevice *dev) 1321343ec8e4SBenoit Canet { 1322343ec8e4SBenoit Canet musicpal_gpio_state *s = FROM_SYSBUS(musicpal_gpio_state, dev); 1323718ec0beSmalc 1324343ec8e4SBenoit Canet sysbus_init_irq(dev, &s->irq); 1325343ec8e4SBenoit Canet 132619b4a424SAvi Kivity memory_region_init_io(&s->iomem, &musicpal_gpio_ops, s, 132719b4a424SAvi Kivity "musicpal-gpio", MP_GPIO_SIZE); 1328750ecd44SAvi Kivity sysbus_init_mmio(dev, &s->iomem); 1329343ec8e4SBenoit Canet 1330708afdf3SJan Kiszka qdev_init_gpio_out(&dev->qdev, s->out, ARRAY_SIZE(s->out)); 1331708afdf3SJan Kiszka 1332708afdf3SJan Kiszka qdev_init_gpio_in(&dev->qdev, musicpal_gpio_pin_event, 32); 133381a322d4SGerd Hoffmann 133481a322d4SGerd Hoffmann return 0; 1335718ec0beSmalc } 1336718ec0beSmalc 1337d5b61dddSJan Kiszka static const VMStateDescription musicpal_gpio_vmsd = { 1338d5b61dddSJan Kiszka .name = "musicpal_gpio", 1339d5b61dddSJan Kiszka .version_id = 1, 1340d5b61dddSJan Kiszka .minimum_version_id = 1, 1341d5b61dddSJan Kiszka .minimum_version_id_old = 1, 1342d5b61dddSJan Kiszka .fields = (VMStateField[]) { 1343d5b61dddSJan Kiszka VMSTATE_UINT32(lcd_brightness, musicpal_gpio_state), 1344d5b61dddSJan Kiszka VMSTATE_UINT32(out_state, musicpal_gpio_state), 1345d5b61dddSJan Kiszka VMSTATE_UINT32(in_state, musicpal_gpio_state), 1346d5b61dddSJan Kiszka VMSTATE_UINT32(ier, musicpal_gpio_state), 1347d5b61dddSJan Kiszka VMSTATE_UINT32(imr, musicpal_gpio_state), 1348d5b61dddSJan Kiszka VMSTATE_UINT32(isr, musicpal_gpio_state), 1349d5b61dddSJan Kiszka VMSTATE_END_OF_LIST() 1350d5b61dddSJan Kiszka } 1351d5b61dddSJan Kiszka }; 1352d5b61dddSJan Kiszka 1353999e12bbSAnthony Liguori static void musicpal_gpio_class_init(ObjectClass *klass, void *data) 1354999e12bbSAnthony Liguori { 135539bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 1356999e12bbSAnthony Liguori SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); 1357999e12bbSAnthony Liguori 1358999e12bbSAnthony Liguori k->init = musicpal_gpio_init; 135939bffca2SAnthony Liguori dc->reset = musicpal_gpio_reset; 136039bffca2SAnthony Liguori dc->vmsd = &musicpal_gpio_vmsd; 1361999e12bbSAnthony Liguori } 1362999e12bbSAnthony Liguori 13638c43a6f0SAndreas Färber static const TypeInfo musicpal_gpio_info = { 1364999e12bbSAnthony Liguori .name = "musicpal_gpio", 136539bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 136639bffca2SAnthony Liguori .instance_size = sizeof(musicpal_gpio_state), 1367999e12bbSAnthony Liguori .class_init = musicpal_gpio_class_init, 136830624c92SJan Kiszka }; 136930624c92SJan Kiszka 137024859b68Sbalrog /* Keyboard codes & masks */ 13717c6ce4baSbalrog #define KEY_RELEASED 0x80 137224859b68Sbalrog #define KEY_CODE 0x7f 137324859b68Sbalrog 137424859b68Sbalrog #define KEYCODE_TAB 0x0f 137524859b68Sbalrog #define KEYCODE_ENTER 0x1c 137624859b68Sbalrog #define KEYCODE_F 0x21 137724859b68Sbalrog #define KEYCODE_M 0x32 137824859b68Sbalrog 137924859b68Sbalrog #define KEYCODE_EXTENDED 0xe0 138024859b68Sbalrog #define KEYCODE_UP 0x48 138124859b68Sbalrog #define KEYCODE_DOWN 0x50 138224859b68Sbalrog #define KEYCODE_LEFT 0x4b 138324859b68Sbalrog #define KEYCODE_RIGHT 0x4d 138424859b68Sbalrog 1385708afdf3SJan Kiszka #define MP_KEY_WHEEL_VOL (1 << 0) 1386343ec8e4SBenoit Canet #define MP_KEY_WHEEL_VOL_INV (1 << 1) 1387343ec8e4SBenoit Canet #define MP_KEY_WHEEL_NAV (1 << 2) 1388343ec8e4SBenoit Canet #define MP_KEY_WHEEL_NAV_INV (1 << 3) 1389343ec8e4SBenoit Canet #define MP_KEY_BTN_FAVORITS (1 << 4) 1390343ec8e4SBenoit Canet #define MP_KEY_BTN_MENU (1 << 5) 1391343ec8e4SBenoit Canet #define MP_KEY_BTN_VOLUME (1 << 6) 1392343ec8e4SBenoit Canet #define MP_KEY_BTN_NAVIGATION (1 << 7) 1393343ec8e4SBenoit Canet 1394343ec8e4SBenoit Canet typedef struct musicpal_key_state { 1395343ec8e4SBenoit Canet SysBusDevice busdev; 13964f5c9479SAvi Kivity MemoryRegion iomem; 1397343ec8e4SBenoit Canet uint32_t kbd_extended; 1398708afdf3SJan Kiszka uint32_t pressed_keys; 1399708afdf3SJan Kiszka qemu_irq out[8]; 1400343ec8e4SBenoit Canet } musicpal_key_state; 1401343ec8e4SBenoit Canet 140224859b68Sbalrog static void musicpal_key_event(void *opaque, int keycode) 140324859b68Sbalrog { 1404243cd13cSJan Kiszka musicpal_key_state *s = opaque; 140524859b68Sbalrog uint32_t event = 0; 1406343ec8e4SBenoit Canet int i; 140724859b68Sbalrog 140824859b68Sbalrog if (keycode == KEYCODE_EXTENDED) { 1409343ec8e4SBenoit Canet s->kbd_extended = 1; 141024859b68Sbalrog return; 141124859b68Sbalrog } 141224859b68Sbalrog 141349fedd0dSJan Kiszka if (s->kbd_extended) { 141424859b68Sbalrog switch (keycode & KEY_CODE) { 141524859b68Sbalrog case KEYCODE_UP: 1416343ec8e4SBenoit Canet event = MP_KEY_WHEEL_NAV | MP_KEY_WHEEL_NAV_INV; 141724859b68Sbalrog break; 141824859b68Sbalrog 141924859b68Sbalrog case KEYCODE_DOWN: 1420343ec8e4SBenoit Canet event = MP_KEY_WHEEL_NAV; 142124859b68Sbalrog break; 142224859b68Sbalrog 142324859b68Sbalrog case KEYCODE_LEFT: 1424343ec8e4SBenoit Canet event = MP_KEY_WHEEL_VOL | MP_KEY_WHEEL_VOL_INV; 142524859b68Sbalrog break; 142624859b68Sbalrog 142724859b68Sbalrog case KEYCODE_RIGHT: 1428343ec8e4SBenoit Canet event = MP_KEY_WHEEL_VOL; 142924859b68Sbalrog break; 143024859b68Sbalrog } 143149fedd0dSJan Kiszka } else { 143224859b68Sbalrog switch (keycode & KEY_CODE) { 143324859b68Sbalrog case KEYCODE_F: 1434343ec8e4SBenoit Canet event = MP_KEY_BTN_FAVORITS; 143524859b68Sbalrog break; 143624859b68Sbalrog 143724859b68Sbalrog case KEYCODE_TAB: 1438343ec8e4SBenoit Canet event = MP_KEY_BTN_VOLUME; 143924859b68Sbalrog break; 144024859b68Sbalrog 144124859b68Sbalrog case KEYCODE_ENTER: 1442343ec8e4SBenoit Canet event = MP_KEY_BTN_NAVIGATION; 144324859b68Sbalrog break; 144424859b68Sbalrog 144524859b68Sbalrog case KEYCODE_M: 1446343ec8e4SBenoit Canet event = MP_KEY_BTN_MENU; 144724859b68Sbalrog break; 144824859b68Sbalrog } 14497c6ce4baSbalrog /* Do not repeat already pressed buttons */ 1450708afdf3SJan Kiszka if (!(keycode & KEY_RELEASED) && (s->pressed_keys & event)) { 14517c6ce4baSbalrog event = 0; 14527c6ce4baSbalrog } 1453708afdf3SJan Kiszka } 145424859b68Sbalrog 14557c6ce4baSbalrog if (event) { 1456708afdf3SJan Kiszka /* Raise GPIO pin first if repeating a key */ 1457708afdf3SJan Kiszka if (!(keycode & KEY_RELEASED) && (s->pressed_keys & event)) { 1458708afdf3SJan Kiszka for (i = 0; i <= 7; i++) { 1459708afdf3SJan Kiszka if (event & (1 << i)) { 1460708afdf3SJan Kiszka qemu_set_irq(s->out[i], 1); 14617c6ce4baSbalrog } 1462708afdf3SJan Kiszka } 1463708afdf3SJan Kiszka } 1464708afdf3SJan Kiszka for (i = 0; i <= 7; i++) { 1465708afdf3SJan Kiszka if (event & (1 << i)) { 1466708afdf3SJan Kiszka qemu_set_irq(s->out[i], !!(keycode & KEY_RELEASED)); 1467708afdf3SJan Kiszka } 1468708afdf3SJan Kiszka } 1469708afdf3SJan Kiszka if (keycode & KEY_RELEASED) { 1470708afdf3SJan Kiszka s->pressed_keys &= ~event; 1471708afdf3SJan Kiszka } else { 1472708afdf3SJan Kiszka s->pressed_keys |= event; 1473708afdf3SJan Kiszka } 1474343ec8e4SBenoit Canet } 1475343ec8e4SBenoit Canet 1476343ec8e4SBenoit Canet s->kbd_extended = 0; 1477343ec8e4SBenoit Canet } 1478343ec8e4SBenoit Canet 147981a322d4SGerd Hoffmann static int musicpal_key_init(SysBusDevice *dev) 1480343ec8e4SBenoit Canet { 1481343ec8e4SBenoit Canet musicpal_key_state *s = FROM_SYSBUS(musicpal_key_state, dev); 1482343ec8e4SBenoit Canet 14834f5c9479SAvi Kivity memory_region_init(&s->iomem, "dummy", 0); 1484750ecd44SAvi Kivity sysbus_init_mmio(dev, &s->iomem); 1485343ec8e4SBenoit Canet 1486343ec8e4SBenoit Canet s->kbd_extended = 0; 1487708afdf3SJan Kiszka s->pressed_keys = 0; 1488343ec8e4SBenoit Canet 1489708afdf3SJan Kiszka qdev_init_gpio_out(&dev->qdev, s->out, ARRAY_SIZE(s->out)); 1490343ec8e4SBenoit Canet 1491343ec8e4SBenoit Canet qemu_add_kbd_event_handler(musicpal_key_event, s); 149281a322d4SGerd Hoffmann 149381a322d4SGerd Hoffmann return 0; 149424859b68Sbalrog } 149524859b68Sbalrog 1496d5b61dddSJan Kiszka static const VMStateDescription musicpal_key_vmsd = { 1497d5b61dddSJan Kiszka .name = "musicpal_key", 1498d5b61dddSJan Kiszka .version_id = 1, 1499d5b61dddSJan Kiszka .minimum_version_id = 1, 1500d5b61dddSJan Kiszka .minimum_version_id_old = 1, 1501d5b61dddSJan Kiszka .fields = (VMStateField[]) { 1502d5b61dddSJan Kiszka VMSTATE_UINT32(kbd_extended, musicpal_key_state), 1503d5b61dddSJan Kiszka VMSTATE_UINT32(pressed_keys, musicpal_key_state), 1504d5b61dddSJan Kiszka VMSTATE_END_OF_LIST() 1505d5b61dddSJan Kiszka } 1506d5b61dddSJan Kiszka }; 1507d5b61dddSJan Kiszka 1508999e12bbSAnthony Liguori static void musicpal_key_class_init(ObjectClass *klass, void *data) 1509999e12bbSAnthony Liguori { 151039bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 1511999e12bbSAnthony Liguori SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); 1512999e12bbSAnthony Liguori 1513999e12bbSAnthony Liguori k->init = musicpal_key_init; 151439bffca2SAnthony Liguori dc->vmsd = &musicpal_key_vmsd; 1515999e12bbSAnthony Liguori } 1516999e12bbSAnthony Liguori 15178c43a6f0SAndreas Färber static const TypeInfo musicpal_key_info = { 1518999e12bbSAnthony Liguori .name = "musicpal_key", 151939bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 152039bffca2SAnthony Liguori .instance_size = sizeof(musicpal_key_state), 1521999e12bbSAnthony Liguori .class_init = musicpal_key_class_init, 1522d5b61dddSJan Kiszka }; 1523d5b61dddSJan Kiszka 152424859b68Sbalrog static struct arm_boot_info musicpal_binfo = { 152524859b68Sbalrog .loader_start = 0x0, 152624859b68Sbalrog .board_id = 0x20e, 152724859b68Sbalrog }; 152824859b68Sbalrog 15295f072e1fSEduardo Habkost static void musicpal_init(QEMUMachineInitArgs *args) 153024859b68Sbalrog { 15315f072e1fSEduardo Habkost const char *cpu_model = args->cpu_model; 15325f072e1fSEduardo Habkost const char *kernel_filename = args->kernel_filename; 15335f072e1fSEduardo Habkost const char *kernel_cmdline = args->kernel_cmdline; 15345f072e1fSEduardo Habkost const char *initrd_filename = args->initrd_filename; 1535f25608e9SAndreas Färber ARMCPU *cpu; 1536b47b50faSPaul Brook qemu_irq *cpu_pic; 1537b47b50faSPaul Brook qemu_irq pic[32]; 1538b47b50faSPaul Brook DeviceState *dev; 1539d074769cSAndrzej Zaborowski DeviceState *i2c_dev; 1540343ec8e4SBenoit Canet DeviceState *lcd_dev; 1541343ec8e4SBenoit Canet DeviceState *key_dev; 1542d074769cSAndrzej Zaborowski DeviceState *wm8750_dev; 1543d074769cSAndrzej Zaborowski SysBusDevice *s; 1544d074769cSAndrzej Zaborowski i2c_bus *i2c; 1545b47b50faSPaul Brook int i; 154624859b68Sbalrog unsigned long flash_size; 1547751c6a17SGerd Hoffmann DriveInfo *dinfo; 154819b4a424SAvi Kivity MemoryRegion *address_space_mem = get_system_memory(); 154919b4a424SAvi Kivity MemoryRegion *ram = g_new(MemoryRegion, 1); 155019b4a424SAvi Kivity MemoryRegion *sram = g_new(MemoryRegion, 1); 155124859b68Sbalrog 155249fedd0dSJan Kiszka if (!cpu_model) { 155324859b68Sbalrog cpu_model = "arm926"; 155449fedd0dSJan Kiszka } 1555f25608e9SAndreas Färber cpu = cpu_arm_init(cpu_model); 1556f25608e9SAndreas Färber if (!cpu) { 155724859b68Sbalrog fprintf(stderr, "Unable to find CPU definition\n"); 155824859b68Sbalrog exit(1); 155924859b68Sbalrog } 15604bd74661SAndreas Färber cpu_pic = arm_pic_init_cpu(cpu); 156124859b68Sbalrog 156224859b68Sbalrog /* For now we use a fixed - the original - RAM size */ 1563c5705a77SAvi Kivity memory_region_init_ram(ram, "musicpal.ram", MP_RAM_DEFAULT_SIZE); 1564c5705a77SAvi Kivity vmstate_register_ram_global(ram); 156519b4a424SAvi Kivity memory_region_add_subregion(address_space_mem, 0, ram); 156624859b68Sbalrog 1567c5705a77SAvi Kivity memory_region_init_ram(sram, "musicpal.sram", MP_SRAM_SIZE); 1568c5705a77SAvi Kivity vmstate_register_ram_global(sram); 156919b4a424SAvi Kivity memory_region_add_subregion(address_space_mem, MP_SRAM_BASE, sram); 157024859b68Sbalrog 1571b47b50faSPaul Brook dev = sysbus_create_simple("mv88w8618_pic", MP_PIC_BASE, 1572b47b50faSPaul Brook cpu_pic[ARM_PIC_CPU_IRQ]); 1573b47b50faSPaul Brook for (i = 0; i < 32; i++) { 1574067a3ddcSPaul Brook pic[i] = qdev_get_gpio_in(dev, i); 1575b47b50faSPaul Brook } 1576b47b50faSPaul Brook sysbus_create_varargs("mv88w8618_pit", MP_PIT_BASE, pic[MP_TIMER1_IRQ], 1577b47b50faSPaul Brook pic[MP_TIMER2_IRQ], pic[MP_TIMER3_IRQ], 1578b47b50faSPaul Brook pic[MP_TIMER4_IRQ], NULL); 157924859b68Sbalrog 158049fedd0dSJan Kiszka if (serial_hds[0]) { 158139186d8aSRichard Henderson serial_mm_init(address_space_mem, MP_UART1_BASE, 2, pic[MP_UART1_IRQ], 158239186d8aSRichard Henderson 1825000, serial_hds[0], DEVICE_NATIVE_ENDIAN); 158349fedd0dSJan Kiszka } 158449fedd0dSJan Kiszka if (serial_hds[1]) { 158539186d8aSRichard Henderson serial_mm_init(address_space_mem, MP_UART2_BASE, 2, pic[MP_UART2_IRQ], 158639186d8aSRichard Henderson 1825000, serial_hds[1], DEVICE_NATIVE_ENDIAN); 158749fedd0dSJan Kiszka } 158824859b68Sbalrog 158924859b68Sbalrog /* Register flash */ 1590751c6a17SGerd Hoffmann dinfo = drive_get(IF_PFLASH, 0, 0); 1591751c6a17SGerd Hoffmann if (dinfo) { 1592751c6a17SGerd Hoffmann flash_size = bdrv_getlength(dinfo->bdrv); 159324859b68Sbalrog if (flash_size != 8*1024*1024 && flash_size != 16*1024*1024 && 159424859b68Sbalrog flash_size != 32*1024*1024) { 159524859b68Sbalrog fprintf(stderr, "Invalid flash image size\n"); 159624859b68Sbalrog exit(1); 159724859b68Sbalrog } 159824859b68Sbalrog 159924859b68Sbalrog /* 160024859b68Sbalrog * The original U-Boot accesses the flash at 0xFE000000 instead of 160124859b68Sbalrog * 0xFF800000 (if there is 8 MB flash). So remap flash access if the 160224859b68Sbalrog * image is smaller than 32 MB. 160324859b68Sbalrog */ 16045f9fc5adSBlue Swirl #ifdef TARGET_WORDS_BIGENDIAN 16050c267217SJan Kiszka pflash_cfi02_register(0x100000000ULL-MP_FLASH_SIZE_MAX, NULL, 1606cfe5f011SAvi Kivity "musicpal.flash", flash_size, 1607751c6a17SGerd Hoffmann dinfo->bdrv, 0x10000, 160824859b68Sbalrog (flash_size + 0xffff) >> 16, 160924859b68Sbalrog MP_FLASH_SIZE_MAX / flash_size, 161024859b68Sbalrog 2, 0x00BF, 0x236D, 0x0000, 0x0000, 161101e0451aSAnthony Liguori 0x5555, 0x2AAA, 1); 16125f9fc5adSBlue Swirl #else 16130c267217SJan Kiszka pflash_cfi02_register(0x100000000ULL-MP_FLASH_SIZE_MAX, NULL, 1614cfe5f011SAvi Kivity "musicpal.flash", flash_size, 16155f9fc5adSBlue Swirl dinfo->bdrv, 0x10000, 16165f9fc5adSBlue Swirl (flash_size + 0xffff) >> 16, 16175f9fc5adSBlue Swirl MP_FLASH_SIZE_MAX / flash_size, 16185f9fc5adSBlue Swirl 2, 0x00BF, 0x236D, 0x0000, 0x0000, 161901e0451aSAnthony Liguori 0x5555, 0x2AAA, 0); 16205f9fc5adSBlue Swirl #endif 16215f9fc5adSBlue Swirl 162224859b68Sbalrog } 1623b47b50faSPaul Brook sysbus_create_simple("mv88w8618_flashcfg", MP_FLASHCFG_BASE, NULL); 162424859b68Sbalrog 1625b47b50faSPaul Brook qemu_check_nic_model(&nd_table[0], "mv88w8618"); 1626b47b50faSPaul Brook dev = qdev_create(NULL, "mv88w8618_eth"); 16274c91cd28SGerd Hoffmann qdev_set_nic_properties(dev, &nd_table[0]); 1628e23a1b33SMarkus Armbruster qdev_init_nofail(dev); 16291356b98dSAndreas Färber sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, MP_ETH_BASE); 16301356b98dSAndreas Färber sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[MP_ETH_IRQ]); 163124859b68Sbalrog 1632b47b50faSPaul Brook sysbus_create_simple("mv88w8618_wlan", MP_WLAN_BASE, NULL); 1633718ec0beSmalc 1634a86f200aSPeter Maydell sysbus_create_simple(TYPE_MUSICPAL_MISC, MP_MISC_BASE, NULL); 1635343ec8e4SBenoit Canet 1636343ec8e4SBenoit Canet dev = sysbus_create_simple("musicpal_gpio", MP_GPIO_BASE, pic[MP_GPIO_IRQ]); 1637d04fba94SJan Kiszka i2c_dev = sysbus_create_simple("gpio_i2c", -1, NULL); 1638d074769cSAndrzej Zaborowski i2c = (i2c_bus *)qdev_get_child_bus(i2c_dev, "i2c"); 1639d074769cSAndrzej Zaborowski 1640343ec8e4SBenoit Canet lcd_dev = sysbus_create_simple("musicpal_lcd", MP_LCD_BASE, NULL); 1641d04fba94SJan Kiszka key_dev = sysbus_create_simple("musicpal_key", -1, NULL); 1642343ec8e4SBenoit Canet 1643d074769cSAndrzej Zaborowski /* I2C read data */ 1644708afdf3SJan Kiszka qdev_connect_gpio_out(i2c_dev, 0, 1645708afdf3SJan Kiszka qdev_get_gpio_in(dev, MP_GPIO_I2C_DATA_BIT)); 1646d074769cSAndrzej Zaborowski /* I2C data */ 1647d074769cSAndrzej Zaborowski qdev_connect_gpio_out(dev, 3, qdev_get_gpio_in(i2c_dev, 0)); 1648d074769cSAndrzej Zaborowski /* I2C clock */ 1649d074769cSAndrzej Zaborowski qdev_connect_gpio_out(dev, 4, qdev_get_gpio_in(i2c_dev, 1)); 1650d074769cSAndrzej Zaborowski 165149fedd0dSJan Kiszka for (i = 0; i < 3; i++) { 1652343ec8e4SBenoit Canet qdev_connect_gpio_out(dev, i, qdev_get_gpio_in(lcd_dev, i)); 165349fedd0dSJan Kiszka } 1654708afdf3SJan Kiszka for (i = 0; i < 4; i++) { 1655708afdf3SJan Kiszka qdev_connect_gpio_out(key_dev, i, qdev_get_gpio_in(dev, i + 8)); 1656708afdf3SJan Kiszka } 1657708afdf3SJan Kiszka for (i = 4; i < 8; i++) { 1658708afdf3SJan Kiszka qdev_connect_gpio_out(key_dev, i, qdev_get_gpio_in(dev, i + 15)); 1659708afdf3SJan Kiszka } 166024859b68Sbalrog 1661d074769cSAndrzej Zaborowski wm8750_dev = i2c_create_slave(i2c, "wm8750", MP_WM_ADDR); 1662d074769cSAndrzej Zaborowski dev = qdev_create(NULL, "mv88w8618_audio"); 16631356b98dSAndreas Färber s = SYS_BUS_DEVICE(dev); 1664d074769cSAndrzej Zaborowski qdev_prop_set_ptr(dev, "wm8750", wm8750_dev); 1665e23a1b33SMarkus Armbruster qdev_init_nofail(dev); 1666d074769cSAndrzej Zaborowski sysbus_mmio_map(s, 0, MP_AUDIO_BASE); 1667d074769cSAndrzej Zaborowski sysbus_connect_irq(s, 0, pic[MP_AUDIO_IRQ]); 1668d074769cSAndrzej Zaborowski 166924859b68Sbalrog musicpal_binfo.ram_size = MP_RAM_DEFAULT_SIZE; 167024859b68Sbalrog musicpal_binfo.kernel_filename = kernel_filename; 167124859b68Sbalrog musicpal_binfo.kernel_cmdline = kernel_cmdline; 167224859b68Sbalrog musicpal_binfo.initrd_filename = initrd_filename; 16733aaa8dfaSAndreas Färber arm_load_kernel(cpu, &musicpal_binfo); 167424859b68Sbalrog } 167524859b68Sbalrog 1676f80f9ec9SAnthony Liguori static QEMUMachine musicpal_machine = { 16774b32e168Saliguori .name = "musicpal", 16784b32e168Saliguori .desc = "Marvell 88w8618 / MusicPal (ARM926EJ-S)", 16794b32e168Saliguori .init = musicpal_init, 1680e4ada29eSAvik Sil DEFAULT_MACHINE_OPTIONS, 168124859b68Sbalrog }; 1682b47b50faSPaul Brook 1683f80f9ec9SAnthony Liguori static void musicpal_machine_init(void) 1684f80f9ec9SAnthony Liguori { 1685f80f9ec9SAnthony Liguori qemu_register_machine(&musicpal_machine); 1686f80f9ec9SAnthony Liguori } 1687f80f9ec9SAnthony Liguori 1688f80f9ec9SAnthony Liguori machine_init(musicpal_machine_init); 1689f80f9ec9SAnthony Liguori 1690999e12bbSAnthony Liguori static void mv88w8618_wlan_class_init(ObjectClass *klass, void *data) 1691999e12bbSAnthony Liguori { 1692999e12bbSAnthony Liguori SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); 1693999e12bbSAnthony Liguori 1694999e12bbSAnthony Liguori sdc->init = mv88w8618_wlan_init; 1695999e12bbSAnthony Liguori } 1696999e12bbSAnthony Liguori 16978c43a6f0SAndreas Färber static const TypeInfo mv88w8618_wlan_info = { 1698999e12bbSAnthony Liguori .name = "mv88w8618_wlan", 169939bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 170039bffca2SAnthony Liguori .instance_size = sizeof(SysBusDevice), 1701999e12bbSAnthony Liguori .class_init = mv88w8618_wlan_class_init, 1702999e12bbSAnthony Liguori }; 1703999e12bbSAnthony Liguori 170483f7d43aSAndreas Färber static void musicpal_register_types(void) 1705b47b50faSPaul Brook { 170639bffca2SAnthony Liguori type_register_static(&mv88w8618_pic_info); 170739bffca2SAnthony Liguori type_register_static(&mv88w8618_pit_info); 170839bffca2SAnthony Liguori type_register_static(&mv88w8618_flashcfg_info); 170939bffca2SAnthony Liguori type_register_static(&mv88w8618_eth_info); 171039bffca2SAnthony Liguori type_register_static(&mv88w8618_wlan_info); 171139bffca2SAnthony Liguori type_register_static(&musicpal_lcd_info); 171239bffca2SAnthony Liguori type_register_static(&musicpal_gpio_info); 171339bffca2SAnthony Liguori type_register_static(&musicpal_key_info); 1714a86f200aSPeter Maydell type_register_static(&musicpal_misc_info); 1715b47b50faSPaul Brook } 1716b47b50faSPaul Brook 171783f7d43aSAndreas Färber type_init(musicpal_register_types) 1718