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" 13bd2be150SPeter Maydell #include "hw/arm/arm.h" 14bd2be150SPeter Maydell #include "hw/devices.h" 151422e32dSPaolo Bonzini #include "net/net.h" 169c17d615SPaolo Bonzini #include "sysemu/sysemu.h" 1783c9f4caSPaolo Bonzini #include "hw/boards.h" 180d09e41aSPaolo Bonzini #include "hw/char/serial.h" 191de7afc9SPaolo Bonzini #include "qemu/timer.h" 2083c9f4caSPaolo Bonzini #include "hw/ptimer.h" 21737e150eSPaolo Bonzini #include "block/block.h" 220d09e41aSPaolo Bonzini #include "hw/block/flash.h" 2328ecbaeeSPaolo Bonzini #include "ui/console.h" 240d09e41aSPaolo 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 149a77d90e6SAndreas Färber #define TYPE_MV88W8618_ETH "mv88w8618_eth" 150a77d90e6SAndreas Färber #define MV88W8618_ETH(obj) \ 151a77d90e6SAndreas Färber OBJECT_CHECK(mv88w8618_eth_state, (obj), TYPE_MV88W8618_ETH) 152a77d90e6SAndreas Färber 15324859b68Sbalrog typedef struct mv88w8618_eth_state { 154a77d90e6SAndreas Färber /*< private >*/ 155a77d90e6SAndreas Färber SysBusDevice parent_obj; 156a77d90e6SAndreas Färber /*< public >*/ 157a77d90e6SAndreas Färber 15819b4a424SAvi Kivity MemoryRegion iomem; 15924859b68Sbalrog qemu_irq irq; 16024859b68Sbalrog uint32_t smir; 16124859b68Sbalrog uint32_t icr; 16224859b68Sbalrog uint32_t imr; 163b946a153Saliguori int mmio_index; 164d5b61dddSJan Kiszka uint32_t vlan_header; 165930c8682Spbrook uint32_t tx_queue[2]; 166930c8682Spbrook uint32_t rx_queue[4]; 167930c8682Spbrook uint32_t frx_queue[4]; 168930c8682Spbrook uint32_t cur_rx[4]; 1693a94dd18SMark McLoughlin NICState *nic; 1704c91cd28SGerd Hoffmann NICConf conf; 17124859b68Sbalrog } mv88w8618_eth_state; 17224859b68Sbalrog 173930c8682Spbrook static void eth_rx_desc_put(uint32_t addr, mv88w8618_rx_desc *desc) 174930c8682Spbrook { 175930c8682Spbrook cpu_to_le32s(&desc->cmdstat); 176930c8682Spbrook cpu_to_le16s(&desc->bytes); 177930c8682Spbrook cpu_to_le16s(&desc->buffer_size); 178930c8682Spbrook cpu_to_le32s(&desc->buffer); 179930c8682Spbrook cpu_to_le32s(&desc->next); 180e1fe50dcSStefan Weil cpu_physical_memory_write(addr, desc, sizeof(*desc)); 181930c8682Spbrook } 182930c8682Spbrook 183930c8682Spbrook static void eth_rx_desc_get(uint32_t addr, mv88w8618_rx_desc *desc) 184930c8682Spbrook { 185e1fe50dcSStefan Weil cpu_physical_memory_read(addr, desc, sizeof(*desc)); 186930c8682Spbrook le32_to_cpus(&desc->cmdstat); 187930c8682Spbrook le16_to_cpus(&desc->bytes); 188930c8682Spbrook le16_to_cpus(&desc->buffer_size); 189930c8682Spbrook le32_to_cpus(&desc->buffer); 190930c8682Spbrook le32_to_cpus(&desc->next); 191930c8682Spbrook } 192930c8682Spbrook 1934e68f7a0SStefan Hajnoczi static int eth_can_receive(NetClientState *nc) 19424859b68Sbalrog { 19524859b68Sbalrog return 1; 19624859b68Sbalrog } 19724859b68Sbalrog 1984e68f7a0SStefan Hajnoczi static ssize_t eth_receive(NetClientState *nc, const uint8_t *buf, size_t size) 19924859b68Sbalrog { 200cc1f0f45SJason Wang mv88w8618_eth_state *s = qemu_get_nic_opaque(nc); 201930c8682Spbrook uint32_t desc_addr; 202930c8682Spbrook mv88w8618_rx_desc desc; 20324859b68Sbalrog int i; 20424859b68Sbalrog 20524859b68Sbalrog for (i = 0; i < 4; i++) { 206930c8682Spbrook desc_addr = s->cur_rx[i]; 20749fedd0dSJan Kiszka if (!desc_addr) { 20824859b68Sbalrog continue; 20949fedd0dSJan Kiszka } 21024859b68Sbalrog do { 211930c8682Spbrook eth_rx_desc_get(desc_addr, &desc); 212930c8682Spbrook if ((desc.cmdstat & MP_ETH_RX_OWN) && desc.buffer_size >= size) { 213930c8682Spbrook cpu_physical_memory_write(desc.buffer + s->vlan_header, 21424859b68Sbalrog buf, size); 215930c8682Spbrook desc.bytes = size + s->vlan_header; 216930c8682Spbrook desc.cmdstat &= ~MP_ETH_RX_OWN; 217930c8682Spbrook s->cur_rx[i] = desc.next; 21824859b68Sbalrog 21924859b68Sbalrog s->icr |= MP_ETH_IRQ_RX; 22049fedd0dSJan Kiszka if (s->icr & s->imr) { 22124859b68Sbalrog qemu_irq_raise(s->irq); 22249fedd0dSJan Kiszka } 223930c8682Spbrook eth_rx_desc_put(desc_addr, &desc); 2244f1c942bSMark McLoughlin return size; 22524859b68Sbalrog } 226930c8682Spbrook desc_addr = desc.next; 227930c8682Spbrook } while (desc_addr != s->rx_queue[i]); 22824859b68Sbalrog } 2294f1c942bSMark McLoughlin return size; 23024859b68Sbalrog } 23124859b68Sbalrog 232930c8682Spbrook static void eth_tx_desc_put(uint32_t addr, mv88w8618_tx_desc *desc) 233930c8682Spbrook { 234930c8682Spbrook cpu_to_le32s(&desc->cmdstat); 235930c8682Spbrook cpu_to_le16s(&desc->res); 236930c8682Spbrook cpu_to_le16s(&desc->bytes); 237930c8682Spbrook cpu_to_le32s(&desc->buffer); 238930c8682Spbrook cpu_to_le32s(&desc->next); 239e1fe50dcSStefan Weil cpu_physical_memory_write(addr, desc, sizeof(*desc)); 240930c8682Spbrook } 241930c8682Spbrook 242930c8682Spbrook static void eth_tx_desc_get(uint32_t addr, mv88w8618_tx_desc *desc) 243930c8682Spbrook { 244e1fe50dcSStefan Weil cpu_physical_memory_read(addr, desc, sizeof(*desc)); 245930c8682Spbrook le32_to_cpus(&desc->cmdstat); 246930c8682Spbrook le16_to_cpus(&desc->res); 247930c8682Spbrook le16_to_cpus(&desc->bytes); 248930c8682Spbrook le32_to_cpus(&desc->buffer); 249930c8682Spbrook le32_to_cpus(&desc->next); 250930c8682Spbrook } 251930c8682Spbrook 25224859b68Sbalrog static void eth_send(mv88w8618_eth_state *s, int queue_index) 25324859b68Sbalrog { 254930c8682Spbrook uint32_t desc_addr = s->tx_queue[queue_index]; 255930c8682Spbrook mv88w8618_tx_desc desc; 25607b064e9SJan Kiszka uint32_t next_desc; 257930c8682Spbrook uint8_t buf[2048]; 258930c8682Spbrook int len; 259930c8682Spbrook 26024859b68Sbalrog do { 261930c8682Spbrook eth_tx_desc_get(desc_addr, &desc); 26207b064e9SJan Kiszka next_desc = desc.next; 263930c8682Spbrook if (desc.cmdstat & MP_ETH_TX_OWN) { 264930c8682Spbrook len = desc.bytes; 265930c8682Spbrook if (len < 2048) { 266930c8682Spbrook cpu_physical_memory_read(desc.buffer, buf, len); 267b356f76dSJason Wang qemu_send_packet(qemu_get_queue(s->nic), buf, len); 26824859b68Sbalrog } 269930c8682Spbrook desc.cmdstat &= ~MP_ETH_TX_OWN; 270930c8682Spbrook s->icr |= 1 << (MP_ETH_IRQ_TXLO_BIT - queue_index); 271930c8682Spbrook eth_tx_desc_put(desc_addr, &desc); 272930c8682Spbrook } 27307b064e9SJan Kiszka desc_addr = next_desc; 274930c8682Spbrook } while (desc_addr != s->tx_queue[queue_index]); 27524859b68Sbalrog } 27624859b68Sbalrog 277a8170e5eSAvi Kivity static uint64_t mv88w8618_eth_read(void *opaque, hwaddr offset, 27819b4a424SAvi Kivity unsigned size) 27924859b68Sbalrog { 28024859b68Sbalrog mv88w8618_eth_state *s = opaque; 28124859b68Sbalrog 28224859b68Sbalrog switch (offset) { 28324859b68Sbalrog case MP_ETH_SMIR: 28424859b68Sbalrog if (s->smir & MP_ETH_SMIR_OPCODE) { 28524859b68Sbalrog switch (s->smir & MP_ETH_SMIR_ADDR) { 28624859b68Sbalrog case MP_ETH_PHY1_BMSR: 28724859b68Sbalrog return MP_PHY_BMSR_LINK | MP_PHY_BMSR_AUTONEG | 28824859b68Sbalrog MP_ETH_SMIR_RDVALID; 28924859b68Sbalrog case MP_ETH_PHY1_PHYSID1: 29024859b68Sbalrog return (MP_PHY_88E3015 >> 16) | MP_ETH_SMIR_RDVALID; 29124859b68Sbalrog case MP_ETH_PHY1_PHYSID2: 29224859b68Sbalrog return (MP_PHY_88E3015 & 0xFFFF) | MP_ETH_SMIR_RDVALID; 29324859b68Sbalrog default: 29424859b68Sbalrog return MP_ETH_SMIR_RDVALID; 29524859b68Sbalrog } 29624859b68Sbalrog } 29724859b68Sbalrog return 0; 29824859b68Sbalrog 29924859b68Sbalrog case MP_ETH_ICR: 30024859b68Sbalrog return s->icr; 30124859b68Sbalrog 30224859b68Sbalrog case MP_ETH_IMR: 30324859b68Sbalrog return s->imr; 30424859b68Sbalrog 30524859b68Sbalrog case MP_ETH_FRDP0 ... MP_ETH_FRDP3: 306930c8682Spbrook return s->frx_queue[(offset - MP_ETH_FRDP0)/4]; 30724859b68Sbalrog 30824859b68Sbalrog case MP_ETH_CRDP0 ... MP_ETH_CRDP3: 309930c8682Spbrook return s->rx_queue[(offset - MP_ETH_CRDP0)/4]; 31024859b68Sbalrog 31124859b68Sbalrog case MP_ETH_CTDP0 ... MP_ETH_CTDP3: 312930c8682Spbrook return s->tx_queue[(offset - MP_ETH_CTDP0)/4]; 31324859b68Sbalrog 31424859b68Sbalrog default: 31524859b68Sbalrog return 0; 31624859b68Sbalrog } 31724859b68Sbalrog } 31824859b68Sbalrog 319a8170e5eSAvi Kivity static void mv88w8618_eth_write(void *opaque, hwaddr offset, 32019b4a424SAvi Kivity uint64_t value, unsigned size) 32124859b68Sbalrog { 32224859b68Sbalrog mv88w8618_eth_state *s = opaque; 32324859b68Sbalrog 32424859b68Sbalrog switch (offset) { 32524859b68Sbalrog case MP_ETH_SMIR: 32624859b68Sbalrog s->smir = value; 32724859b68Sbalrog break; 32824859b68Sbalrog 32924859b68Sbalrog case MP_ETH_PCXR: 33024859b68Sbalrog s->vlan_header = ((value >> MP_ETH_PCXR_2BSM_BIT) & 1) * 2; 33124859b68Sbalrog break; 33224859b68Sbalrog 33324859b68Sbalrog case MP_ETH_SDCMR: 33449fedd0dSJan Kiszka if (value & MP_ETH_CMD_TXHI) { 33524859b68Sbalrog eth_send(s, 1); 33649fedd0dSJan Kiszka } 33749fedd0dSJan Kiszka if (value & MP_ETH_CMD_TXLO) { 33824859b68Sbalrog eth_send(s, 0); 33949fedd0dSJan Kiszka } 34049fedd0dSJan Kiszka if (value & (MP_ETH_CMD_TXHI | MP_ETH_CMD_TXLO) && s->icr & s->imr) { 34124859b68Sbalrog qemu_irq_raise(s->irq); 34249fedd0dSJan Kiszka } 34324859b68Sbalrog break; 34424859b68Sbalrog 34524859b68Sbalrog case MP_ETH_ICR: 34624859b68Sbalrog s->icr &= value; 34724859b68Sbalrog break; 34824859b68Sbalrog 34924859b68Sbalrog case MP_ETH_IMR: 35024859b68Sbalrog s->imr = value; 35149fedd0dSJan Kiszka if (s->icr & s->imr) { 35224859b68Sbalrog qemu_irq_raise(s->irq); 35349fedd0dSJan Kiszka } 35424859b68Sbalrog break; 35524859b68Sbalrog 35624859b68Sbalrog case MP_ETH_FRDP0 ... MP_ETH_FRDP3: 357930c8682Spbrook s->frx_queue[(offset - MP_ETH_FRDP0)/4] = value; 35824859b68Sbalrog break; 35924859b68Sbalrog 36024859b68Sbalrog case MP_ETH_CRDP0 ... MP_ETH_CRDP3: 36124859b68Sbalrog s->rx_queue[(offset - MP_ETH_CRDP0)/4] = 362930c8682Spbrook s->cur_rx[(offset - MP_ETH_CRDP0)/4] = value; 36324859b68Sbalrog break; 36424859b68Sbalrog 36524859b68Sbalrog case MP_ETH_CTDP0 ... MP_ETH_CTDP3: 366930c8682Spbrook s->tx_queue[(offset - MP_ETH_CTDP0)/4] = value; 36724859b68Sbalrog break; 36824859b68Sbalrog } 36924859b68Sbalrog } 37024859b68Sbalrog 37119b4a424SAvi Kivity static const MemoryRegionOps mv88w8618_eth_ops = { 37219b4a424SAvi Kivity .read = mv88w8618_eth_read, 37319b4a424SAvi Kivity .write = mv88w8618_eth_write, 37419b4a424SAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 37524859b68Sbalrog }; 37624859b68Sbalrog 3774e68f7a0SStefan Hajnoczi static void eth_cleanup(NetClientState *nc) 378b946a153Saliguori { 379cc1f0f45SJason Wang mv88w8618_eth_state *s = qemu_get_nic_opaque(nc); 380b946a153Saliguori 3813a94dd18SMark McLoughlin s->nic = NULL; 382b946a153Saliguori } 383b946a153Saliguori 3843a94dd18SMark McLoughlin static NetClientInfo net_mv88w8618_info = { 3852be64a68SLaszlo Ersek .type = NET_CLIENT_OPTIONS_KIND_NIC, 3863a94dd18SMark McLoughlin .size = sizeof(NICState), 3873a94dd18SMark McLoughlin .can_receive = eth_can_receive, 3883a94dd18SMark McLoughlin .receive = eth_receive, 3893a94dd18SMark McLoughlin .cleanup = eth_cleanup, 3903a94dd18SMark McLoughlin }; 3913a94dd18SMark McLoughlin 392a77d90e6SAndreas Färber static int mv88w8618_eth_init(SysBusDevice *sbd) 39324859b68Sbalrog { 394a77d90e6SAndreas Färber DeviceState *dev = DEVICE(sbd); 395a77d90e6SAndreas Färber mv88w8618_eth_state *s = MV88W8618_ETH(dev); 39624859b68Sbalrog 397a77d90e6SAndreas Färber sysbus_init_irq(sbd, &s->irq); 3983a94dd18SMark McLoughlin s->nic = qemu_new_nic(&net_mv88w8618_info, &s->conf, 399a77d90e6SAndreas Färber object_get_typename(OBJECT(dev)), dev->id, s); 40064bde0f3SPaolo Bonzini memory_region_init_io(&s->iomem, OBJECT(s), &mv88w8618_eth_ops, s, 40164bde0f3SPaolo Bonzini "mv88w8618-eth", MP_ETH_SIZE); 402a77d90e6SAndreas Färber sysbus_init_mmio(sbd, &s->iomem); 40381a322d4SGerd Hoffmann return 0; 40424859b68Sbalrog } 40524859b68Sbalrog 406d5b61dddSJan Kiszka static const VMStateDescription mv88w8618_eth_vmsd = { 407d5b61dddSJan Kiszka .name = "mv88w8618_eth", 408d5b61dddSJan Kiszka .version_id = 1, 409d5b61dddSJan Kiszka .minimum_version_id = 1, 410d5b61dddSJan Kiszka .minimum_version_id_old = 1, 411d5b61dddSJan Kiszka .fields = (VMStateField[]) { 412d5b61dddSJan Kiszka VMSTATE_UINT32(smir, mv88w8618_eth_state), 413d5b61dddSJan Kiszka VMSTATE_UINT32(icr, mv88w8618_eth_state), 414d5b61dddSJan Kiszka VMSTATE_UINT32(imr, mv88w8618_eth_state), 415d5b61dddSJan Kiszka VMSTATE_UINT32(vlan_header, mv88w8618_eth_state), 416d5b61dddSJan Kiszka VMSTATE_UINT32_ARRAY(tx_queue, mv88w8618_eth_state, 2), 417d5b61dddSJan Kiszka VMSTATE_UINT32_ARRAY(rx_queue, mv88w8618_eth_state, 4), 418d5b61dddSJan Kiszka VMSTATE_UINT32_ARRAY(frx_queue, mv88w8618_eth_state, 4), 419d5b61dddSJan Kiszka VMSTATE_UINT32_ARRAY(cur_rx, mv88w8618_eth_state, 4), 420d5b61dddSJan Kiszka VMSTATE_END_OF_LIST() 421d5b61dddSJan Kiszka } 422d5b61dddSJan Kiszka }; 423d5b61dddSJan Kiszka 424999e12bbSAnthony Liguori static Property mv88w8618_eth_properties[] = { 4254c91cd28SGerd Hoffmann DEFINE_NIC_PROPERTIES(mv88w8618_eth_state, conf), 4264c91cd28SGerd Hoffmann DEFINE_PROP_END_OF_LIST(), 427999e12bbSAnthony Liguori }; 428999e12bbSAnthony Liguori 429999e12bbSAnthony Liguori static void mv88w8618_eth_class_init(ObjectClass *klass, void *data) 430999e12bbSAnthony Liguori { 43139bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 432999e12bbSAnthony Liguori SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); 433999e12bbSAnthony Liguori 434999e12bbSAnthony Liguori k->init = mv88w8618_eth_init; 43539bffca2SAnthony Liguori dc->vmsd = &mv88w8618_eth_vmsd; 43639bffca2SAnthony Liguori dc->props = mv88w8618_eth_properties; 437999e12bbSAnthony Liguori } 438999e12bbSAnthony Liguori 4398c43a6f0SAndreas Färber static const TypeInfo mv88w8618_eth_info = { 440a77d90e6SAndreas Färber .name = TYPE_MV88W8618_ETH, 44139bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 44239bffca2SAnthony Liguori .instance_size = sizeof(mv88w8618_eth_state), 443999e12bbSAnthony Liguori .class_init = mv88w8618_eth_class_init, 444d5b61dddSJan Kiszka }; 445d5b61dddSJan Kiszka 44624859b68Sbalrog /* LCD register offsets */ 44724859b68Sbalrog #define MP_LCD_IRQCTRL 0x180 44824859b68Sbalrog #define MP_LCD_IRQSTAT 0x184 44924859b68Sbalrog #define MP_LCD_SPICTRL 0x1ac 45024859b68Sbalrog #define MP_LCD_INST 0x1bc 45124859b68Sbalrog #define MP_LCD_DATA 0x1c0 45224859b68Sbalrog 45324859b68Sbalrog /* Mode magics */ 45424859b68Sbalrog #define MP_LCD_SPI_DATA 0x00100011 45524859b68Sbalrog #define MP_LCD_SPI_CMD 0x00104011 45624859b68Sbalrog #define MP_LCD_SPI_INVALID 0x00000000 45724859b68Sbalrog 45824859b68Sbalrog /* Commmands */ 45924859b68Sbalrog #define MP_LCD_INST_SETPAGE0 0xB0 46024859b68Sbalrog /* ... */ 46124859b68Sbalrog #define MP_LCD_INST_SETPAGE7 0xB7 46224859b68Sbalrog 46324859b68Sbalrog #define MP_LCD_TEXTCOLOR 0xe0e0ff /* RRGGBB */ 46424859b68Sbalrog 465*2cca58fdSAndreas Färber #define TYPE_MUSICPAL_LCD "musicpal_lcd" 466*2cca58fdSAndreas Färber #define MUSICPAL_LCD(obj) \ 467*2cca58fdSAndreas Färber OBJECT_CHECK(musicpal_lcd_state, (obj), TYPE_MUSICPAL_LCD) 468*2cca58fdSAndreas Färber 46924859b68Sbalrog typedef struct musicpal_lcd_state { 470*2cca58fdSAndreas Färber /*< private >*/ 471*2cca58fdSAndreas Färber SysBusDevice parent_obj; 472*2cca58fdSAndreas Färber /*< public >*/ 473*2cca58fdSAndreas Färber 47419b4a424SAvi Kivity MemoryRegion iomem; 475343ec8e4SBenoit Canet uint32_t brightness; 47624859b68Sbalrog uint32_t mode; 47724859b68Sbalrog uint32_t irqctrl; 478d5b61dddSJan Kiszka uint32_t page; 479d5b61dddSJan Kiszka uint32_t page_off; 480c78f7137SGerd Hoffmann QemuConsole *con; 48124859b68Sbalrog uint8_t video_ram[128*64/8]; 48224859b68Sbalrog } musicpal_lcd_state; 48324859b68Sbalrog 484343ec8e4SBenoit Canet static uint8_t scale_lcd_color(musicpal_lcd_state *s, uint8_t col) 48524859b68Sbalrog { 486343ec8e4SBenoit Canet switch (s->brightness) { 487343ec8e4SBenoit Canet case 7: 48824859b68Sbalrog return col; 489343ec8e4SBenoit Canet case 0: 490343ec8e4SBenoit Canet return 0; 491343ec8e4SBenoit Canet default: 492343ec8e4SBenoit Canet return (col * s->brightness) / 7; 49324859b68Sbalrog } 49424859b68Sbalrog } 49524859b68Sbalrog 4960266f2c7Sbalrog #define SET_LCD_PIXEL(depth, type) \ 4970266f2c7Sbalrog static inline void glue(set_lcd_pixel, depth) \ 4980266f2c7Sbalrog (musicpal_lcd_state *s, int x, int y, type col) \ 4990266f2c7Sbalrog { \ 5000266f2c7Sbalrog int dx, dy; \ 501c78f7137SGerd Hoffmann DisplaySurface *surface = qemu_console_surface(s->con); \ 502c78f7137SGerd Hoffmann type *pixel = &((type *) surface_data(surface))[(y * 128 * 3 + x) * 3]; \ 5030266f2c7Sbalrog \ 5040266f2c7Sbalrog for (dy = 0; dy < 3; dy++, pixel += 127 * 3) \ 5050266f2c7Sbalrog for (dx = 0; dx < 3; dx++, pixel++) \ 5060266f2c7Sbalrog *pixel = col; \ 5070266f2c7Sbalrog } 5080266f2c7Sbalrog SET_LCD_PIXEL(8, uint8_t) 5090266f2c7Sbalrog SET_LCD_PIXEL(16, uint16_t) 5100266f2c7Sbalrog SET_LCD_PIXEL(32, uint32_t) 51124859b68Sbalrog 51224859b68Sbalrog static void lcd_refresh(void *opaque) 51324859b68Sbalrog { 51424859b68Sbalrog musicpal_lcd_state *s = opaque; 515c78f7137SGerd Hoffmann DisplaySurface *surface = qemu_console_surface(s->con); 5160266f2c7Sbalrog int x, y, col; 51724859b68Sbalrog 518c78f7137SGerd Hoffmann switch (surface_bits_per_pixel(surface)) { 5190266f2c7Sbalrog case 0: 5200266f2c7Sbalrog return; 5210266f2c7Sbalrog #define LCD_REFRESH(depth, func) \ 5220266f2c7Sbalrog case depth: \ 523343ec8e4SBenoit Canet col = func(scale_lcd_color(s, (MP_LCD_TEXTCOLOR >> 16) & 0xff), \ 524343ec8e4SBenoit Canet scale_lcd_color(s, (MP_LCD_TEXTCOLOR >> 8) & 0xff), \ 525343ec8e4SBenoit Canet scale_lcd_color(s, MP_LCD_TEXTCOLOR & 0xff)); \ 52649fedd0dSJan Kiszka for (x = 0; x < 128; x++) { \ 52749fedd0dSJan Kiszka for (y = 0; y < 64; y++) { \ 52849fedd0dSJan Kiszka if (s->video_ram[x + (y/8)*128] & (1 << (y % 8))) { \ 5290266f2c7Sbalrog glue(set_lcd_pixel, depth)(s, x, y, col); \ 53049fedd0dSJan Kiszka } else { \ 5310266f2c7Sbalrog glue(set_lcd_pixel, depth)(s, x, y, 0); \ 53249fedd0dSJan Kiszka } \ 53349fedd0dSJan Kiszka } \ 53449fedd0dSJan Kiszka } \ 5350266f2c7Sbalrog break; 5360266f2c7Sbalrog LCD_REFRESH(8, rgb_to_pixel8) 5370266f2c7Sbalrog LCD_REFRESH(16, rgb_to_pixel16) 538c78f7137SGerd Hoffmann LCD_REFRESH(32, (is_surface_bgr(surface) ? 539bf9b48afSaliguori rgb_to_pixel32bgr : rgb_to_pixel32)) 5400266f2c7Sbalrog default: 5412ac71179SPaul Brook hw_error("unsupported colour depth %i\n", 542c78f7137SGerd Hoffmann surface_bits_per_pixel(surface)); 5430266f2c7Sbalrog } 54424859b68Sbalrog 545c78f7137SGerd Hoffmann dpy_gfx_update(s->con, 0, 0, 128*3, 64*3); 54624859b68Sbalrog } 54724859b68Sbalrog 548167bc3d2Sbalrog static void lcd_invalidate(void *opaque) 549167bc3d2Sbalrog { 550167bc3d2Sbalrog } 551167bc3d2Sbalrog 5522c79fed3SStefan Weil static void musicpal_lcd_gpio_brightness_in(void *opaque, int irq, int level) 553343ec8e4SBenoit Canet { 554243cd13cSJan Kiszka musicpal_lcd_state *s = opaque; 555343ec8e4SBenoit Canet s->brightness &= ~(1 << irq); 556343ec8e4SBenoit Canet s->brightness |= level << irq; 557343ec8e4SBenoit Canet } 558343ec8e4SBenoit Canet 559a8170e5eSAvi Kivity static uint64_t musicpal_lcd_read(void *opaque, hwaddr offset, 56019b4a424SAvi Kivity unsigned size) 56124859b68Sbalrog { 56224859b68Sbalrog musicpal_lcd_state *s = opaque; 56324859b68Sbalrog 56424859b68Sbalrog switch (offset) { 56524859b68Sbalrog case MP_LCD_IRQCTRL: 56624859b68Sbalrog return s->irqctrl; 56724859b68Sbalrog 56824859b68Sbalrog default: 56924859b68Sbalrog return 0; 57024859b68Sbalrog } 57124859b68Sbalrog } 57224859b68Sbalrog 573a8170e5eSAvi Kivity static void musicpal_lcd_write(void *opaque, hwaddr offset, 57419b4a424SAvi Kivity uint64_t value, unsigned size) 57524859b68Sbalrog { 57624859b68Sbalrog musicpal_lcd_state *s = opaque; 57724859b68Sbalrog 57824859b68Sbalrog switch (offset) { 57924859b68Sbalrog case MP_LCD_IRQCTRL: 58024859b68Sbalrog s->irqctrl = value; 58124859b68Sbalrog break; 58224859b68Sbalrog 58324859b68Sbalrog case MP_LCD_SPICTRL: 58449fedd0dSJan Kiszka if (value == MP_LCD_SPI_DATA || value == MP_LCD_SPI_CMD) { 58524859b68Sbalrog s->mode = value; 58649fedd0dSJan Kiszka } else { 58724859b68Sbalrog s->mode = MP_LCD_SPI_INVALID; 58849fedd0dSJan Kiszka } 58924859b68Sbalrog break; 59024859b68Sbalrog 59124859b68Sbalrog case MP_LCD_INST: 59224859b68Sbalrog if (value >= MP_LCD_INST_SETPAGE0 && value <= MP_LCD_INST_SETPAGE7) { 59324859b68Sbalrog s->page = value - MP_LCD_INST_SETPAGE0; 59424859b68Sbalrog s->page_off = 0; 59524859b68Sbalrog } 59624859b68Sbalrog break; 59724859b68Sbalrog 59824859b68Sbalrog case MP_LCD_DATA: 59924859b68Sbalrog if (s->mode == MP_LCD_SPI_CMD) { 60024859b68Sbalrog if (value >= MP_LCD_INST_SETPAGE0 && 60124859b68Sbalrog value <= MP_LCD_INST_SETPAGE7) { 60224859b68Sbalrog s->page = value - MP_LCD_INST_SETPAGE0; 60324859b68Sbalrog s->page_off = 0; 60424859b68Sbalrog } 60524859b68Sbalrog } else if (s->mode == MP_LCD_SPI_DATA) { 60624859b68Sbalrog s->video_ram[s->page*128 + s->page_off] = value; 60724859b68Sbalrog s->page_off = (s->page_off + 1) & 127; 60824859b68Sbalrog } 60924859b68Sbalrog break; 61024859b68Sbalrog } 61124859b68Sbalrog } 61224859b68Sbalrog 61319b4a424SAvi Kivity static const MemoryRegionOps musicpal_lcd_ops = { 61419b4a424SAvi Kivity .read = musicpal_lcd_read, 61519b4a424SAvi Kivity .write = musicpal_lcd_write, 61619b4a424SAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 61724859b68Sbalrog }; 61824859b68Sbalrog 619380cd056SGerd Hoffmann static const GraphicHwOps musicpal_gfx_ops = { 620380cd056SGerd Hoffmann .invalidate = lcd_invalidate, 621380cd056SGerd Hoffmann .gfx_update = lcd_refresh, 622380cd056SGerd Hoffmann }; 623380cd056SGerd Hoffmann 624*2cca58fdSAndreas Färber static int musicpal_lcd_init(SysBusDevice *sbd) 62524859b68Sbalrog { 626*2cca58fdSAndreas Färber DeviceState *dev = DEVICE(sbd); 627*2cca58fdSAndreas Färber musicpal_lcd_state *s = MUSICPAL_LCD(dev); 62824859b68Sbalrog 629343ec8e4SBenoit Canet s->brightness = 7; 630343ec8e4SBenoit Canet 63164bde0f3SPaolo Bonzini memory_region_init_io(&s->iomem, OBJECT(s), &musicpal_lcd_ops, s, 63219b4a424SAvi Kivity "musicpal-lcd", MP_LCD_SIZE); 633*2cca58fdSAndreas Färber sysbus_init_mmio(sbd, &s->iomem); 63424859b68Sbalrog 635*2cca58fdSAndreas Färber s->con = graphic_console_init(dev, &musicpal_gfx_ops, s); 636c78f7137SGerd Hoffmann qemu_console_resize(s->con, 128*3, 64*3); 637343ec8e4SBenoit Canet 638*2cca58fdSAndreas Färber qdev_init_gpio_in(dev, musicpal_lcd_gpio_brightness_in, 3); 63981a322d4SGerd Hoffmann 64081a322d4SGerd Hoffmann return 0; 64124859b68Sbalrog } 64224859b68Sbalrog 643d5b61dddSJan Kiszka static const VMStateDescription musicpal_lcd_vmsd = { 644d5b61dddSJan Kiszka .name = "musicpal_lcd", 645d5b61dddSJan Kiszka .version_id = 1, 646d5b61dddSJan Kiszka .minimum_version_id = 1, 647d5b61dddSJan Kiszka .minimum_version_id_old = 1, 648d5b61dddSJan Kiszka .fields = (VMStateField[]) { 649d5b61dddSJan Kiszka VMSTATE_UINT32(brightness, musicpal_lcd_state), 650d5b61dddSJan Kiszka VMSTATE_UINT32(mode, musicpal_lcd_state), 651d5b61dddSJan Kiszka VMSTATE_UINT32(irqctrl, musicpal_lcd_state), 652d5b61dddSJan Kiszka VMSTATE_UINT32(page, musicpal_lcd_state), 653d5b61dddSJan Kiszka VMSTATE_UINT32(page_off, musicpal_lcd_state), 654d5b61dddSJan Kiszka VMSTATE_BUFFER(video_ram, musicpal_lcd_state), 655d5b61dddSJan Kiszka VMSTATE_END_OF_LIST() 656d5b61dddSJan Kiszka } 657d5b61dddSJan Kiszka }; 658d5b61dddSJan Kiszka 659999e12bbSAnthony Liguori static void musicpal_lcd_class_init(ObjectClass *klass, void *data) 660999e12bbSAnthony Liguori { 66139bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 662999e12bbSAnthony Liguori SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); 663999e12bbSAnthony Liguori 664999e12bbSAnthony Liguori k->init = musicpal_lcd_init; 66539bffca2SAnthony Liguori dc->vmsd = &musicpal_lcd_vmsd; 666999e12bbSAnthony Liguori } 667999e12bbSAnthony Liguori 6688c43a6f0SAndreas Färber static const TypeInfo musicpal_lcd_info = { 669*2cca58fdSAndreas Färber .name = TYPE_MUSICPAL_LCD, 67039bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 67139bffca2SAnthony Liguori .instance_size = sizeof(musicpal_lcd_state), 672999e12bbSAnthony Liguori .class_init = musicpal_lcd_class_init, 673d5b61dddSJan Kiszka }; 674d5b61dddSJan Kiszka 67524859b68Sbalrog /* PIC register offsets */ 67624859b68Sbalrog #define MP_PIC_STATUS 0x00 67724859b68Sbalrog #define MP_PIC_ENABLE_SET 0x08 67824859b68Sbalrog #define MP_PIC_ENABLE_CLR 0x0C 67924859b68Sbalrog 68024859b68Sbalrog typedef struct mv88w8618_pic_state 68124859b68Sbalrog { 682b47b50faSPaul Brook SysBusDevice busdev; 68319b4a424SAvi Kivity MemoryRegion iomem; 68424859b68Sbalrog uint32_t level; 68524859b68Sbalrog uint32_t enabled; 68624859b68Sbalrog qemu_irq parent_irq; 68724859b68Sbalrog } mv88w8618_pic_state; 68824859b68Sbalrog 68924859b68Sbalrog static void mv88w8618_pic_update(mv88w8618_pic_state *s) 69024859b68Sbalrog { 69124859b68Sbalrog qemu_set_irq(s->parent_irq, (s->level & s->enabled)); 69224859b68Sbalrog } 69324859b68Sbalrog 69424859b68Sbalrog static void mv88w8618_pic_set_irq(void *opaque, int irq, int level) 69524859b68Sbalrog { 69624859b68Sbalrog mv88w8618_pic_state *s = opaque; 69724859b68Sbalrog 69849fedd0dSJan Kiszka if (level) { 69924859b68Sbalrog s->level |= 1 << irq; 70049fedd0dSJan Kiszka } else { 70124859b68Sbalrog s->level &= ~(1 << irq); 70249fedd0dSJan Kiszka } 70324859b68Sbalrog mv88w8618_pic_update(s); 70424859b68Sbalrog } 70524859b68Sbalrog 706a8170e5eSAvi Kivity static uint64_t mv88w8618_pic_read(void *opaque, hwaddr offset, 70719b4a424SAvi Kivity unsigned size) 70824859b68Sbalrog { 70924859b68Sbalrog mv88w8618_pic_state *s = opaque; 71024859b68Sbalrog 71124859b68Sbalrog switch (offset) { 71224859b68Sbalrog case MP_PIC_STATUS: 71324859b68Sbalrog return s->level & s->enabled; 71424859b68Sbalrog 71524859b68Sbalrog default: 71624859b68Sbalrog return 0; 71724859b68Sbalrog } 71824859b68Sbalrog } 71924859b68Sbalrog 720a8170e5eSAvi Kivity static void mv88w8618_pic_write(void *opaque, hwaddr offset, 72119b4a424SAvi Kivity uint64_t value, unsigned size) 72224859b68Sbalrog { 72324859b68Sbalrog mv88w8618_pic_state *s = opaque; 72424859b68Sbalrog 72524859b68Sbalrog switch (offset) { 72624859b68Sbalrog case MP_PIC_ENABLE_SET: 72724859b68Sbalrog s->enabled |= value; 72824859b68Sbalrog break; 72924859b68Sbalrog 73024859b68Sbalrog case MP_PIC_ENABLE_CLR: 73124859b68Sbalrog s->enabled &= ~value; 73224859b68Sbalrog s->level &= ~value; 73324859b68Sbalrog break; 73424859b68Sbalrog } 73524859b68Sbalrog mv88w8618_pic_update(s); 73624859b68Sbalrog } 73724859b68Sbalrog 738d5b61dddSJan Kiszka static void mv88w8618_pic_reset(DeviceState *d) 73924859b68Sbalrog { 740d5b61dddSJan Kiszka mv88w8618_pic_state *s = FROM_SYSBUS(mv88w8618_pic_state, 7411356b98dSAndreas Färber SYS_BUS_DEVICE(d)); 74224859b68Sbalrog 74324859b68Sbalrog s->level = 0; 74424859b68Sbalrog s->enabled = 0; 74524859b68Sbalrog } 74624859b68Sbalrog 74719b4a424SAvi Kivity static const MemoryRegionOps mv88w8618_pic_ops = { 74819b4a424SAvi Kivity .read = mv88w8618_pic_read, 74919b4a424SAvi Kivity .write = mv88w8618_pic_write, 75019b4a424SAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 75124859b68Sbalrog }; 75224859b68Sbalrog 75381a322d4SGerd Hoffmann static int mv88w8618_pic_init(SysBusDevice *dev) 75424859b68Sbalrog { 755b47b50faSPaul Brook mv88w8618_pic_state *s = FROM_SYSBUS(mv88w8618_pic_state, dev); 75624859b68Sbalrog 757067a3ddcSPaul Brook qdev_init_gpio_in(&dev->qdev, mv88w8618_pic_set_irq, 32); 758b47b50faSPaul Brook sysbus_init_irq(dev, &s->parent_irq); 75964bde0f3SPaolo Bonzini memory_region_init_io(&s->iomem, OBJECT(s), &mv88w8618_pic_ops, s, 76019b4a424SAvi Kivity "musicpal-pic", MP_PIC_SIZE); 761750ecd44SAvi Kivity sysbus_init_mmio(dev, &s->iomem); 76281a322d4SGerd Hoffmann return 0; 76324859b68Sbalrog } 76424859b68Sbalrog 765d5b61dddSJan Kiszka static const VMStateDescription mv88w8618_pic_vmsd = { 766d5b61dddSJan Kiszka .name = "mv88w8618_pic", 767d5b61dddSJan Kiszka .version_id = 1, 768d5b61dddSJan Kiszka .minimum_version_id = 1, 769d5b61dddSJan Kiszka .minimum_version_id_old = 1, 770d5b61dddSJan Kiszka .fields = (VMStateField[]) { 771d5b61dddSJan Kiszka VMSTATE_UINT32(level, mv88w8618_pic_state), 772d5b61dddSJan Kiszka VMSTATE_UINT32(enabled, mv88w8618_pic_state), 773d5b61dddSJan Kiszka VMSTATE_END_OF_LIST() 774d5b61dddSJan Kiszka } 775d5b61dddSJan Kiszka }; 776d5b61dddSJan Kiszka 777999e12bbSAnthony Liguori static void mv88w8618_pic_class_init(ObjectClass *klass, void *data) 778999e12bbSAnthony Liguori { 77939bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 780999e12bbSAnthony Liguori SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); 781999e12bbSAnthony Liguori 782999e12bbSAnthony Liguori k->init = mv88w8618_pic_init; 78339bffca2SAnthony Liguori dc->reset = mv88w8618_pic_reset; 78439bffca2SAnthony Liguori dc->vmsd = &mv88w8618_pic_vmsd; 785999e12bbSAnthony Liguori } 786999e12bbSAnthony Liguori 7878c43a6f0SAndreas Färber static const TypeInfo mv88w8618_pic_info = { 788999e12bbSAnthony Liguori .name = "mv88w8618_pic", 78939bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 79039bffca2SAnthony Liguori .instance_size = sizeof(mv88w8618_pic_state), 791999e12bbSAnthony Liguori .class_init = mv88w8618_pic_class_init, 792d5b61dddSJan Kiszka }; 793d5b61dddSJan Kiszka 79424859b68Sbalrog /* PIT register offsets */ 79524859b68Sbalrog #define MP_PIT_TIMER1_LENGTH 0x00 79624859b68Sbalrog /* ... */ 79724859b68Sbalrog #define MP_PIT_TIMER4_LENGTH 0x0C 79824859b68Sbalrog #define MP_PIT_CONTROL 0x10 79924859b68Sbalrog #define MP_PIT_TIMER1_VALUE 0x14 80024859b68Sbalrog /* ... */ 80124859b68Sbalrog #define MP_PIT_TIMER4_VALUE 0x20 80224859b68Sbalrog #define MP_BOARD_RESET 0x34 80324859b68Sbalrog 80424859b68Sbalrog /* Magic board reset value (probably some watchdog behind it) */ 80524859b68Sbalrog #define MP_BOARD_RESET_MAGIC 0x10000 80624859b68Sbalrog 80724859b68Sbalrog typedef struct mv88w8618_timer_state { 808b47b50faSPaul Brook ptimer_state *ptimer; 80924859b68Sbalrog uint32_t limit; 81024859b68Sbalrog int freq; 81124859b68Sbalrog qemu_irq irq; 81224859b68Sbalrog } mv88w8618_timer_state; 81324859b68Sbalrog 81424859b68Sbalrog typedef struct mv88w8618_pit_state { 815b47b50faSPaul Brook SysBusDevice busdev; 81619b4a424SAvi Kivity MemoryRegion iomem; 817b47b50faSPaul Brook mv88w8618_timer_state timer[4]; 81824859b68Sbalrog } mv88w8618_pit_state; 81924859b68Sbalrog 82024859b68Sbalrog static void mv88w8618_timer_tick(void *opaque) 82124859b68Sbalrog { 82224859b68Sbalrog mv88w8618_timer_state *s = opaque; 82324859b68Sbalrog 82424859b68Sbalrog qemu_irq_raise(s->irq); 82524859b68Sbalrog } 82624859b68Sbalrog 827b47b50faSPaul Brook static void mv88w8618_timer_init(SysBusDevice *dev, mv88w8618_timer_state *s, 828b47b50faSPaul Brook uint32_t freq) 82924859b68Sbalrog { 83024859b68Sbalrog QEMUBH *bh; 83124859b68Sbalrog 832b47b50faSPaul Brook sysbus_init_irq(dev, &s->irq); 83324859b68Sbalrog s->freq = freq; 83424859b68Sbalrog 83524859b68Sbalrog bh = qemu_bh_new(mv88w8618_timer_tick, s); 836b47b50faSPaul Brook s->ptimer = ptimer_init(bh); 83724859b68Sbalrog } 83824859b68Sbalrog 839a8170e5eSAvi Kivity static uint64_t mv88w8618_pit_read(void *opaque, hwaddr offset, 84019b4a424SAvi Kivity unsigned size) 84124859b68Sbalrog { 84224859b68Sbalrog mv88w8618_pit_state *s = opaque; 84324859b68Sbalrog mv88w8618_timer_state *t; 84424859b68Sbalrog 84524859b68Sbalrog switch (offset) { 84624859b68Sbalrog case MP_PIT_TIMER1_VALUE ... MP_PIT_TIMER4_VALUE: 847b47b50faSPaul Brook t = &s->timer[(offset-MP_PIT_TIMER1_VALUE) >> 2]; 848b47b50faSPaul Brook return ptimer_get_count(t->ptimer); 84924859b68Sbalrog 85024859b68Sbalrog default: 85124859b68Sbalrog return 0; 85224859b68Sbalrog } 85324859b68Sbalrog } 85424859b68Sbalrog 855a8170e5eSAvi Kivity static void mv88w8618_pit_write(void *opaque, hwaddr offset, 85619b4a424SAvi Kivity uint64_t value, unsigned size) 85724859b68Sbalrog { 85824859b68Sbalrog mv88w8618_pit_state *s = opaque; 85924859b68Sbalrog mv88w8618_timer_state *t; 86024859b68Sbalrog int i; 86124859b68Sbalrog 86224859b68Sbalrog switch (offset) { 86324859b68Sbalrog case MP_PIT_TIMER1_LENGTH ... MP_PIT_TIMER4_LENGTH: 864b47b50faSPaul Brook t = &s->timer[offset >> 2]; 86524859b68Sbalrog t->limit = value; 866c88d6bdeSJan Kiszka if (t->limit > 0) { 867b47b50faSPaul Brook ptimer_set_limit(t->ptimer, t->limit, 1); 868c88d6bdeSJan Kiszka } else { 869c88d6bdeSJan Kiszka ptimer_stop(t->ptimer); 870c88d6bdeSJan Kiszka } 87124859b68Sbalrog break; 87224859b68Sbalrog 87324859b68Sbalrog case MP_PIT_CONTROL: 87424859b68Sbalrog for (i = 0; i < 4; i++) { 875b47b50faSPaul Brook t = &s->timer[i]; 876c88d6bdeSJan Kiszka if (value & 0xf && t->limit > 0) { 877b47b50faSPaul Brook ptimer_set_limit(t->ptimer, t->limit, 0); 878b47b50faSPaul Brook ptimer_set_freq(t->ptimer, t->freq); 879b47b50faSPaul Brook ptimer_run(t->ptimer, 0); 880c88d6bdeSJan Kiszka } else { 881c88d6bdeSJan Kiszka ptimer_stop(t->ptimer); 88224859b68Sbalrog } 88324859b68Sbalrog value >>= 4; 88424859b68Sbalrog } 88524859b68Sbalrog break; 88624859b68Sbalrog 88724859b68Sbalrog case MP_BOARD_RESET: 88849fedd0dSJan Kiszka if (value == MP_BOARD_RESET_MAGIC) { 88924859b68Sbalrog qemu_system_reset_request(); 89049fedd0dSJan Kiszka } 89124859b68Sbalrog break; 89224859b68Sbalrog } 89324859b68Sbalrog } 89424859b68Sbalrog 895d5b61dddSJan Kiszka static void mv88w8618_pit_reset(DeviceState *d) 896c88d6bdeSJan Kiszka { 897d5b61dddSJan Kiszka mv88w8618_pit_state *s = FROM_SYSBUS(mv88w8618_pit_state, 8981356b98dSAndreas Färber SYS_BUS_DEVICE(d)); 899c88d6bdeSJan Kiszka int i; 900c88d6bdeSJan Kiszka 901c88d6bdeSJan Kiszka for (i = 0; i < 4; i++) { 902c88d6bdeSJan Kiszka ptimer_stop(s->timer[i].ptimer); 903c88d6bdeSJan Kiszka s->timer[i].limit = 0; 904c88d6bdeSJan Kiszka } 905c88d6bdeSJan Kiszka } 906c88d6bdeSJan Kiszka 90719b4a424SAvi Kivity static const MemoryRegionOps mv88w8618_pit_ops = { 90819b4a424SAvi Kivity .read = mv88w8618_pit_read, 90919b4a424SAvi Kivity .write = mv88w8618_pit_write, 91019b4a424SAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 91124859b68Sbalrog }; 91224859b68Sbalrog 91381a322d4SGerd Hoffmann static int mv88w8618_pit_init(SysBusDevice *dev) 91424859b68Sbalrog { 915b47b50faSPaul Brook mv88w8618_pit_state *s = FROM_SYSBUS(mv88w8618_pit_state, dev); 916b47b50faSPaul Brook int i; 91724859b68Sbalrog 91824859b68Sbalrog /* Letting them all run at 1 MHz is likely just a pragmatic 91924859b68Sbalrog * simplification. */ 920b47b50faSPaul Brook for (i = 0; i < 4; i++) { 921b47b50faSPaul Brook mv88w8618_timer_init(dev, &s->timer[i], 1000000); 922b47b50faSPaul Brook } 92324859b68Sbalrog 92464bde0f3SPaolo Bonzini memory_region_init_io(&s->iomem, OBJECT(s), &mv88w8618_pit_ops, s, 92519b4a424SAvi Kivity "musicpal-pit", MP_PIT_SIZE); 926750ecd44SAvi Kivity sysbus_init_mmio(dev, &s->iomem); 92781a322d4SGerd Hoffmann return 0; 92824859b68Sbalrog } 92924859b68Sbalrog 930d5b61dddSJan Kiszka static const VMStateDescription mv88w8618_timer_vmsd = { 931d5b61dddSJan Kiszka .name = "timer", 932d5b61dddSJan Kiszka .version_id = 1, 933d5b61dddSJan Kiszka .minimum_version_id = 1, 934d5b61dddSJan Kiszka .minimum_version_id_old = 1, 935d5b61dddSJan Kiszka .fields = (VMStateField[]) { 936d5b61dddSJan Kiszka VMSTATE_PTIMER(ptimer, mv88w8618_timer_state), 937d5b61dddSJan Kiszka VMSTATE_UINT32(limit, mv88w8618_timer_state), 938d5b61dddSJan Kiszka VMSTATE_END_OF_LIST() 939d5b61dddSJan Kiszka } 940d5b61dddSJan Kiszka }; 941d5b61dddSJan Kiszka 942d5b61dddSJan Kiszka static const VMStateDescription mv88w8618_pit_vmsd = { 943d5b61dddSJan Kiszka .name = "mv88w8618_pit", 944d5b61dddSJan Kiszka .version_id = 1, 945d5b61dddSJan Kiszka .minimum_version_id = 1, 946d5b61dddSJan Kiszka .minimum_version_id_old = 1, 947d5b61dddSJan Kiszka .fields = (VMStateField[]) { 948d5b61dddSJan Kiszka VMSTATE_STRUCT_ARRAY(timer, mv88w8618_pit_state, 4, 1, 949d5b61dddSJan Kiszka mv88w8618_timer_vmsd, mv88w8618_timer_state), 950d5b61dddSJan Kiszka VMSTATE_END_OF_LIST() 951d5b61dddSJan Kiszka } 952d5b61dddSJan Kiszka }; 953d5b61dddSJan Kiszka 954999e12bbSAnthony Liguori static void mv88w8618_pit_class_init(ObjectClass *klass, void *data) 955999e12bbSAnthony Liguori { 95639bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 957999e12bbSAnthony Liguori SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); 958999e12bbSAnthony Liguori 959999e12bbSAnthony Liguori k->init = mv88w8618_pit_init; 96039bffca2SAnthony Liguori dc->reset = mv88w8618_pit_reset; 96139bffca2SAnthony Liguori dc->vmsd = &mv88w8618_pit_vmsd; 962999e12bbSAnthony Liguori } 963999e12bbSAnthony Liguori 9648c43a6f0SAndreas Färber static const TypeInfo mv88w8618_pit_info = { 965999e12bbSAnthony Liguori .name = "mv88w8618_pit", 96639bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 96739bffca2SAnthony Liguori .instance_size = sizeof(mv88w8618_pit_state), 968999e12bbSAnthony Liguori .class_init = mv88w8618_pit_class_init, 969c88d6bdeSJan Kiszka }; 970c88d6bdeSJan Kiszka 97124859b68Sbalrog /* Flash config register offsets */ 97224859b68Sbalrog #define MP_FLASHCFG_CFGR0 0x04 97324859b68Sbalrog 97424859b68Sbalrog typedef struct mv88w8618_flashcfg_state { 975b47b50faSPaul Brook SysBusDevice busdev; 97619b4a424SAvi Kivity MemoryRegion iomem; 97724859b68Sbalrog uint32_t cfgr0; 97824859b68Sbalrog } mv88w8618_flashcfg_state; 97924859b68Sbalrog 98019b4a424SAvi Kivity static uint64_t mv88w8618_flashcfg_read(void *opaque, 981a8170e5eSAvi Kivity hwaddr offset, 98219b4a424SAvi Kivity unsigned size) 98324859b68Sbalrog { 98424859b68Sbalrog mv88w8618_flashcfg_state *s = opaque; 98524859b68Sbalrog 98624859b68Sbalrog switch (offset) { 98724859b68Sbalrog case MP_FLASHCFG_CFGR0: 98824859b68Sbalrog return s->cfgr0; 98924859b68Sbalrog 99024859b68Sbalrog default: 99124859b68Sbalrog return 0; 99224859b68Sbalrog } 99324859b68Sbalrog } 99424859b68Sbalrog 995a8170e5eSAvi Kivity static void mv88w8618_flashcfg_write(void *opaque, hwaddr offset, 99619b4a424SAvi Kivity uint64_t value, unsigned size) 99724859b68Sbalrog { 99824859b68Sbalrog mv88w8618_flashcfg_state *s = opaque; 99924859b68Sbalrog 100024859b68Sbalrog switch (offset) { 100124859b68Sbalrog case MP_FLASHCFG_CFGR0: 100224859b68Sbalrog s->cfgr0 = value; 100324859b68Sbalrog break; 100424859b68Sbalrog } 100524859b68Sbalrog } 100624859b68Sbalrog 100719b4a424SAvi Kivity static const MemoryRegionOps mv88w8618_flashcfg_ops = { 100819b4a424SAvi Kivity .read = mv88w8618_flashcfg_read, 100919b4a424SAvi Kivity .write = mv88w8618_flashcfg_write, 101019b4a424SAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 101124859b68Sbalrog }; 101224859b68Sbalrog 101381a322d4SGerd Hoffmann static int mv88w8618_flashcfg_init(SysBusDevice *dev) 101424859b68Sbalrog { 1015b47b50faSPaul Brook mv88w8618_flashcfg_state *s = FROM_SYSBUS(mv88w8618_flashcfg_state, dev); 101624859b68Sbalrog 101724859b68Sbalrog s->cfgr0 = 0xfffe4285; /* Default as set by U-Boot for 8 MB flash */ 101864bde0f3SPaolo Bonzini memory_region_init_io(&s->iomem, OBJECT(s), &mv88w8618_flashcfg_ops, s, 101919b4a424SAvi Kivity "musicpal-flashcfg", MP_FLASHCFG_SIZE); 1020750ecd44SAvi Kivity sysbus_init_mmio(dev, &s->iomem); 102181a322d4SGerd Hoffmann return 0; 102224859b68Sbalrog } 102324859b68Sbalrog 1024d5b61dddSJan Kiszka static const VMStateDescription mv88w8618_flashcfg_vmsd = { 1025d5b61dddSJan Kiszka .name = "mv88w8618_flashcfg", 1026d5b61dddSJan Kiszka .version_id = 1, 1027d5b61dddSJan Kiszka .minimum_version_id = 1, 1028d5b61dddSJan Kiszka .minimum_version_id_old = 1, 1029d5b61dddSJan Kiszka .fields = (VMStateField[]) { 1030d5b61dddSJan Kiszka VMSTATE_UINT32(cfgr0, mv88w8618_flashcfg_state), 1031d5b61dddSJan Kiszka VMSTATE_END_OF_LIST() 1032d5b61dddSJan Kiszka } 1033d5b61dddSJan Kiszka }; 1034d5b61dddSJan Kiszka 1035999e12bbSAnthony Liguori static void mv88w8618_flashcfg_class_init(ObjectClass *klass, void *data) 1036999e12bbSAnthony Liguori { 103739bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 1038999e12bbSAnthony Liguori SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); 1039999e12bbSAnthony Liguori 1040999e12bbSAnthony Liguori k->init = mv88w8618_flashcfg_init; 104139bffca2SAnthony Liguori dc->vmsd = &mv88w8618_flashcfg_vmsd; 1042999e12bbSAnthony Liguori } 1043999e12bbSAnthony Liguori 10448c43a6f0SAndreas Färber static const TypeInfo mv88w8618_flashcfg_info = { 1045999e12bbSAnthony Liguori .name = "mv88w8618_flashcfg", 104639bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 104739bffca2SAnthony Liguori .instance_size = sizeof(mv88w8618_flashcfg_state), 1048999e12bbSAnthony Liguori .class_init = mv88w8618_flashcfg_class_init, 1049d5b61dddSJan Kiszka }; 1050d5b61dddSJan Kiszka 1051718ec0beSmalc /* Misc register offsets */ 1052718ec0beSmalc #define MP_MISC_BOARD_REVISION 0x18 105324859b68Sbalrog 1054718ec0beSmalc #define MP_BOARD_REVISION 0x31 105524859b68Sbalrog 1056a86f200aSPeter Maydell typedef struct { 1057a86f200aSPeter Maydell SysBusDevice parent_obj; 1058a86f200aSPeter Maydell MemoryRegion iomem; 1059a86f200aSPeter Maydell } MusicPalMiscState; 1060a86f200aSPeter Maydell 1061a86f200aSPeter Maydell #define TYPE_MUSICPAL_MISC "musicpal-misc" 1062a86f200aSPeter Maydell #define MUSICPAL_MISC(obj) \ 1063a86f200aSPeter Maydell OBJECT_CHECK(MusicPalMiscState, (obj), TYPE_MUSICPAL_MISC) 1064a86f200aSPeter Maydell 1065a8170e5eSAvi Kivity static uint64_t musicpal_misc_read(void *opaque, hwaddr offset, 106619b4a424SAvi Kivity unsigned size) 1067718ec0beSmalc { 1068718ec0beSmalc switch (offset) { 1069718ec0beSmalc case MP_MISC_BOARD_REVISION: 1070718ec0beSmalc return MP_BOARD_REVISION; 1071718ec0beSmalc 1072718ec0beSmalc default: 1073718ec0beSmalc return 0; 1074718ec0beSmalc } 1075718ec0beSmalc } 1076718ec0beSmalc 1077a8170e5eSAvi Kivity static void musicpal_misc_write(void *opaque, hwaddr offset, 107819b4a424SAvi Kivity uint64_t value, unsigned size) 1079718ec0beSmalc { 1080718ec0beSmalc } 1081718ec0beSmalc 108219b4a424SAvi Kivity static const MemoryRegionOps musicpal_misc_ops = { 108319b4a424SAvi Kivity .read = musicpal_misc_read, 108419b4a424SAvi Kivity .write = musicpal_misc_write, 108519b4a424SAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 1086718ec0beSmalc }; 1087718ec0beSmalc 1088a86f200aSPeter Maydell static void musicpal_misc_init(Object *obj) 1089718ec0beSmalc { 1090a86f200aSPeter Maydell SysBusDevice *sd = SYS_BUS_DEVICE(obj); 1091a86f200aSPeter Maydell MusicPalMiscState *s = MUSICPAL_MISC(obj); 1092718ec0beSmalc 109364bde0f3SPaolo Bonzini memory_region_init_io(&s->iomem, OBJECT(s), &musicpal_misc_ops, NULL, 109419b4a424SAvi Kivity "musicpal-misc", MP_MISC_SIZE); 1095a86f200aSPeter Maydell sysbus_init_mmio(sd, &s->iomem); 1096718ec0beSmalc } 1097718ec0beSmalc 1098a86f200aSPeter Maydell static const TypeInfo musicpal_misc_info = { 1099a86f200aSPeter Maydell .name = TYPE_MUSICPAL_MISC, 1100a86f200aSPeter Maydell .parent = TYPE_SYS_BUS_DEVICE, 1101a86f200aSPeter Maydell .instance_init = musicpal_misc_init, 1102a86f200aSPeter Maydell .instance_size = sizeof(MusicPalMiscState), 1103a86f200aSPeter Maydell }; 1104a86f200aSPeter Maydell 1105718ec0beSmalc /* WLAN register offsets */ 1106718ec0beSmalc #define MP_WLAN_MAGIC1 0x11c 1107718ec0beSmalc #define MP_WLAN_MAGIC2 0x124 1108718ec0beSmalc 1109a8170e5eSAvi Kivity static uint64_t mv88w8618_wlan_read(void *opaque, hwaddr offset, 111019b4a424SAvi Kivity unsigned size) 1111718ec0beSmalc { 1112718ec0beSmalc switch (offset) { 1113718ec0beSmalc /* Workaround to allow loading the binary-only wlandrv.ko crap 1114718ec0beSmalc * from the original Freecom firmware. */ 1115718ec0beSmalc case MP_WLAN_MAGIC1: 1116718ec0beSmalc return ~3; 1117718ec0beSmalc case MP_WLAN_MAGIC2: 1118718ec0beSmalc return -1; 1119718ec0beSmalc 1120718ec0beSmalc default: 1121718ec0beSmalc return 0; 1122718ec0beSmalc } 1123718ec0beSmalc } 1124718ec0beSmalc 1125a8170e5eSAvi Kivity static void mv88w8618_wlan_write(void *opaque, hwaddr offset, 112619b4a424SAvi Kivity uint64_t value, unsigned size) 1127718ec0beSmalc { 1128718ec0beSmalc } 1129718ec0beSmalc 113019b4a424SAvi Kivity static const MemoryRegionOps mv88w8618_wlan_ops = { 113119b4a424SAvi Kivity .read = mv88w8618_wlan_read, 113219b4a424SAvi Kivity .write =mv88w8618_wlan_write, 113319b4a424SAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 1134718ec0beSmalc }; 1135718ec0beSmalc 113681a322d4SGerd Hoffmann static int mv88w8618_wlan_init(SysBusDevice *dev) 1137718ec0beSmalc { 113819b4a424SAvi Kivity MemoryRegion *iomem = g_new(MemoryRegion, 1); 1139718ec0beSmalc 114064bde0f3SPaolo Bonzini memory_region_init_io(iomem, OBJECT(dev), &mv88w8618_wlan_ops, NULL, 114119b4a424SAvi Kivity "musicpal-wlan", MP_WLAN_SIZE); 1142750ecd44SAvi Kivity sysbus_init_mmio(dev, iomem); 114381a322d4SGerd Hoffmann return 0; 1144718ec0beSmalc } 1145718ec0beSmalc 1146718ec0beSmalc /* GPIO register offsets */ 1147718ec0beSmalc #define MP_GPIO_OE_LO 0x008 1148718ec0beSmalc #define MP_GPIO_OUT_LO 0x00c 1149718ec0beSmalc #define MP_GPIO_IN_LO 0x010 1150708afdf3SJan Kiszka #define MP_GPIO_IER_LO 0x014 1151708afdf3SJan Kiszka #define MP_GPIO_IMR_LO 0x018 1152718ec0beSmalc #define MP_GPIO_ISR_LO 0x020 1153718ec0beSmalc #define MP_GPIO_OE_HI 0x508 1154718ec0beSmalc #define MP_GPIO_OUT_HI 0x50c 1155718ec0beSmalc #define MP_GPIO_IN_HI 0x510 1156708afdf3SJan Kiszka #define MP_GPIO_IER_HI 0x514 1157708afdf3SJan Kiszka #define MP_GPIO_IMR_HI 0x518 1158718ec0beSmalc #define MP_GPIO_ISR_HI 0x520 115924859b68Sbalrog 116024859b68Sbalrog /* GPIO bits & masks */ 116124859b68Sbalrog #define MP_GPIO_LCD_BRIGHTNESS 0x00070000 116224859b68Sbalrog #define MP_GPIO_I2C_DATA_BIT 29 116324859b68Sbalrog #define MP_GPIO_I2C_CLOCK_BIT 30 116424859b68Sbalrog 116524859b68Sbalrog /* LCD brightness bits in GPIO_OE_HI */ 116624859b68Sbalrog #define MP_OE_LCD_BRIGHTNESS 0x0007 116724859b68Sbalrog 1168343ec8e4SBenoit Canet typedef struct musicpal_gpio_state { 1169343ec8e4SBenoit Canet SysBusDevice busdev; 117019b4a424SAvi Kivity MemoryRegion iomem; 1171343ec8e4SBenoit Canet uint32_t lcd_brightness; 1172343ec8e4SBenoit Canet uint32_t out_state; 1173343ec8e4SBenoit Canet uint32_t in_state; 1174708afdf3SJan Kiszka uint32_t ier; 1175708afdf3SJan Kiszka uint32_t imr; 1176343ec8e4SBenoit Canet uint32_t isr; 1177343ec8e4SBenoit Canet qemu_irq irq; 1178708afdf3SJan Kiszka qemu_irq out[5]; /* 3 brightness out + 2 lcd (data and clock ) */ 1179343ec8e4SBenoit Canet } musicpal_gpio_state; 1180343ec8e4SBenoit Canet 1181343ec8e4SBenoit Canet static void musicpal_gpio_brightness_update(musicpal_gpio_state *s) { 1182343ec8e4SBenoit Canet int i; 1183343ec8e4SBenoit Canet uint32_t brightness; 1184343ec8e4SBenoit Canet 1185343ec8e4SBenoit Canet /* compute brightness ratio */ 1186343ec8e4SBenoit Canet switch (s->lcd_brightness) { 1187343ec8e4SBenoit Canet case 0x00000007: 1188343ec8e4SBenoit Canet brightness = 0; 1189343ec8e4SBenoit Canet break; 1190343ec8e4SBenoit Canet 1191343ec8e4SBenoit Canet case 0x00020000: 1192343ec8e4SBenoit Canet brightness = 1; 1193343ec8e4SBenoit Canet break; 1194343ec8e4SBenoit Canet 1195343ec8e4SBenoit Canet case 0x00020001: 1196343ec8e4SBenoit Canet brightness = 2; 1197343ec8e4SBenoit Canet break; 1198343ec8e4SBenoit Canet 1199343ec8e4SBenoit Canet case 0x00040000: 1200343ec8e4SBenoit Canet brightness = 3; 1201343ec8e4SBenoit Canet break; 1202343ec8e4SBenoit Canet 1203343ec8e4SBenoit Canet case 0x00010006: 1204343ec8e4SBenoit Canet brightness = 4; 1205343ec8e4SBenoit Canet break; 1206343ec8e4SBenoit Canet 1207343ec8e4SBenoit Canet case 0x00020005: 1208343ec8e4SBenoit Canet brightness = 5; 1209343ec8e4SBenoit Canet break; 1210343ec8e4SBenoit Canet 1211343ec8e4SBenoit Canet case 0x00040003: 1212343ec8e4SBenoit Canet brightness = 6; 1213343ec8e4SBenoit Canet break; 1214343ec8e4SBenoit Canet 1215343ec8e4SBenoit Canet case 0x00030004: 1216343ec8e4SBenoit Canet default: 1217343ec8e4SBenoit Canet brightness = 7; 1218343ec8e4SBenoit Canet } 1219343ec8e4SBenoit Canet 1220343ec8e4SBenoit Canet /* set lcd brightness GPIOs */ 122149fedd0dSJan Kiszka for (i = 0; i <= 2; i++) { 1222343ec8e4SBenoit Canet qemu_set_irq(s->out[i], (brightness >> i) & 1); 1223343ec8e4SBenoit Canet } 122449fedd0dSJan Kiszka } 1225343ec8e4SBenoit Canet 1226708afdf3SJan Kiszka static void musicpal_gpio_pin_event(void *opaque, int pin, int level) 1227343ec8e4SBenoit Canet { 1228243cd13cSJan Kiszka musicpal_gpio_state *s = opaque; 1229708afdf3SJan Kiszka uint32_t mask = 1 << pin; 1230708afdf3SJan Kiszka uint32_t delta = level << pin; 1231708afdf3SJan Kiszka uint32_t old = s->in_state & mask; 1232343ec8e4SBenoit Canet 1233708afdf3SJan Kiszka s->in_state &= ~mask; 1234708afdf3SJan Kiszka s->in_state |= delta; 1235708afdf3SJan Kiszka 1236708afdf3SJan Kiszka if ((old ^ delta) && 1237708afdf3SJan Kiszka ((level && (s->imr & mask)) || (!level && (s->ier & mask)))) { 1238708afdf3SJan Kiszka s->isr = mask; 1239708afdf3SJan Kiszka qemu_irq_raise(s->irq); 1240d074769cSAndrzej Zaborowski } 1241343ec8e4SBenoit Canet } 1242343ec8e4SBenoit Canet 1243a8170e5eSAvi Kivity static uint64_t musicpal_gpio_read(void *opaque, hwaddr offset, 124419b4a424SAvi Kivity unsigned size) 124524859b68Sbalrog { 1246243cd13cSJan Kiszka musicpal_gpio_state *s = opaque; 1247343ec8e4SBenoit Canet 124824859b68Sbalrog switch (offset) { 124924859b68Sbalrog case MP_GPIO_OE_HI: /* used for LCD brightness control */ 1250343ec8e4SBenoit Canet return s->lcd_brightness & MP_OE_LCD_BRIGHTNESS; 125124859b68Sbalrog 125224859b68Sbalrog case MP_GPIO_OUT_LO: 1253343ec8e4SBenoit Canet return s->out_state & 0xFFFF; 125424859b68Sbalrog case MP_GPIO_OUT_HI: 1255343ec8e4SBenoit Canet return s->out_state >> 16; 125624859b68Sbalrog 125724859b68Sbalrog case MP_GPIO_IN_LO: 1258343ec8e4SBenoit Canet return s->in_state & 0xFFFF; 125924859b68Sbalrog case MP_GPIO_IN_HI: 1260343ec8e4SBenoit Canet return s->in_state >> 16; 126124859b68Sbalrog 1262708afdf3SJan Kiszka case MP_GPIO_IER_LO: 1263708afdf3SJan Kiszka return s->ier & 0xFFFF; 1264708afdf3SJan Kiszka case MP_GPIO_IER_HI: 1265708afdf3SJan Kiszka return s->ier >> 16; 1266708afdf3SJan Kiszka 1267708afdf3SJan Kiszka case MP_GPIO_IMR_LO: 1268708afdf3SJan Kiszka return s->imr & 0xFFFF; 1269708afdf3SJan Kiszka case MP_GPIO_IMR_HI: 1270708afdf3SJan Kiszka return s->imr >> 16; 1271708afdf3SJan Kiszka 127224859b68Sbalrog case MP_GPIO_ISR_LO: 1273343ec8e4SBenoit Canet return s->isr & 0xFFFF; 127424859b68Sbalrog case MP_GPIO_ISR_HI: 1275343ec8e4SBenoit Canet return s->isr >> 16; 127624859b68Sbalrog 127724859b68Sbalrog default: 127824859b68Sbalrog return 0; 127924859b68Sbalrog } 128024859b68Sbalrog } 128124859b68Sbalrog 1282a8170e5eSAvi Kivity static void musicpal_gpio_write(void *opaque, hwaddr offset, 128319b4a424SAvi Kivity uint64_t value, unsigned size) 128424859b68Sbalrog { 1285243cd13cSJan Kiszka musicpal_gpio_state *s = opaque; 128624859b68Sbalrog switch (offset) { 128724859b68Sbalrog case MP_GPIO_OE_HI: /* used for LCD brightness control */ 1288343ec8e4SBenoit Canet s->lcd_brightness = (s->lcd_brightness & MP_GPIO_LCD_BRIGHTNESS) | 128924859b68Sbalrog (value & MP_OE_LCD_BRIGHTNESS); 1290343ec8e4SBenoit Canet musicpal_gpio_brightness_update(s); 129124859b68Sbalrog break; 129224859b68Sbalrog 129324859b68Sbalrog case MP_GPIO_OUT_LO: 1294343ec8e4SBenoit Canet s->out_state = (s->out_state & 0xFFFF0000) | (value & 0xFFFF); 129524859b68Sbalrog break; 129624859b68Sbalrog case MP_GPIO_OUT_HI: 1297343ec8e4SBenoit Canet s->out_state = (s->out_state & 0xFFFF) | (value << 16); 1298343ec8e4SBenoit Canet s->lcd_brightness = (s->lcd_brightness & 0xFFFF) | 1299343ec8e4SBenoit Canet (s->out_state & MP_GPIO_LCD_BRIGHTNESS); 1300343ec8e4SBenoit Canet musicpal_gpio_brightness_update(s); 1301d074769cSAndrzej Zaborowski qemu_set_irq(s->out[3], (s->out_state >> MP_GPIO_I2C_DATA_BIT) & 1); 1302d074769cSAndrzej Zaborowski qemu_set_irq(s->out[4], (s->out_state >> MP_GPIO_I2C_CLOCK_BIT) & 1); 130324859b68Sbalrog break; 130424859b68Sbalrog 1305708afdf3SJan Kiszka case MP_GPIO_IER_LO: 1306708afdf3SJan Kiszka s->ier = (s->ier & 0xFFFF0000) | (value & 0xFFFF); 1307708afdf3SJan Kiszka break; 1308708afdf3SJan Kiszka case MP_GPIO_IER_HI: 1309708afdf3SJan Kiszka s->ier = (s->ier & 0xFFFF) | (value << 16); 1310708afdf3SJan Kiszka break; 1311708afdf3SJan Kiszka 1312708afdf3SJan Kiszka case MP_GPIO_IMR_LO: 1313708afdf3SJan Kiszka s->imr = (s->imr & 0xFFFF0000) | (value & 0xFFFF); 1314708afdf3SJan Kiszka break; 1315708afdf3SJan Kiszka case MP_GPIO_IMR_HI: 1316708afdf3SJan Kiszka s->imr = (s->imr & 0xFFFF) | (value << 16); 1317708afdf3SJan Kiszka break; 131824859b68Sbalrog } 131924859b68Sbalrog } 132024859b68Sbalrog 132119b4a424SAvi Kivity static const MemoryRegionOps musicpal_gpio_ops = { 132219b4a424SAvi Kivity .read = musicpal_gpio_read, 132319b4a424SAvi Kivity .write = musicpal_gpio_write, 132419b4a424SAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 1325718ec0beSmalc }; 1326718ec0beSmalc 1327d5b61dddSJan Kiszka static void musicpal_gpio_reset(DeviceState *d) 1328718ec0beSmalc { 1329d5b61dddSJan Kiszka musicpal_gpio_state *s = FROM_SYSBUS(musicpal_gpio_state, 13301356b98dSAndreas Färber SYS_BUS_DEVICE(d)); 133130624c92SJan Kiszka 133230624c92SJan Kiszka s->lcd_brightness = 0; 133330624c92SJan Kiszka s->out_state = 0; 1334343ec8e4SBenoit Canet s->in_state = 0xffffffff; 1335708afdf3SJan Kiszka s->ier = 0; 1336708afdf3SJan Kiszka s->imr = 0; 1337343ec8e4SBenoit Canet s->isr = 0; 1338343ec8e4SBenoit Canet } 1339343ec8e4SBenoit Canet 134081a322d4SGerd Hoffmann static int musicpal_gpio_init(SysBusDevice *dev) 1341343ec8e4SBenoit Canet { 1342343ec8e4SBenoit Canet musicpal_gpio_state *s = FROM_SYSBUS(musicpal_gpio_state, dev); 1343718ec0beSmalc 1344343ec8e4SBenoit Canet sysbus_init_irq(dev, &s->irq); 1345343ec8e4SBenoit Canet 134664bde0f3SPaolo Bonzini memory_region_init_io(&s->iomem, OBJECT(s), &musicpal_gpio_ops, s, 134719b4a424SAvi Kivity "musicpal-gpio", MP_GPIO_SIZE); 1348750ecd44SAvi Kivity sysbus_init_mmio(dev, &s->iomem); 1349343ec8e4SBenoit Canet 1350708afdf3SJan Kiszka qdev_init_gpio_out(&dev->qdev, s->out, ARRAY_SIZE(s->out)); 1351708afdf3SJan Kiszka 1352708afdf3SJan Kiszka qdev_init_gpio_in(&dev->qdev, musicpal_gpio_pin_event, 32); 135381a322d4SGerd Hoffmann 135481a322d4SGerd Hoffmann return 0; 1355718ec0beSmalc } 1356718ec0beSmalc 1357d5b61dddSJan Kiszka static const VMStateDescription musicpal_gpio_vmsd = { 1358d5b61dddSJan Kiszka .name = "musicpal_gpio", 1359d5b61dddSJan Kiszka .version_id = 1, 1360d5b61dddSJan Kiszka .minimum_version_id = 1, 1361d5b61dddSJan Kiszka .minimum_version_id_old = 1, 1362d5b61dddSJan Kiszka .fields = (VMStateField[]) { 1363d5b61dddSJan Kiszka VMSTATE_UINT32(lcd_brightness, musicpal_gpio_state), 1364d5b61dddSJan Kiszka VMSTATE_UINT32(out_state, musicpal_gpio_state), 1365d5b61dddSJan Kiszka VMSTATE_UINT32(in_state, musicpal_gpio_state), 1366d5b61dddSJan Kiszka VMSTATE_UINT32(ier, musicpal_gpio_state), 1367d5b61dddSJan Kiszka VMSTATE_UINT32(imr, musicpal_gpio_state), 1368d5b61dddSJan Kiszka VMSTATE_UINT32(isr, musicpal_gpio_state), 1369d5b61dddSJan Kiszka VMSTATE_END_OF_LIST() 1370d5b61dddSJan Kiszka } 1371d5b61dddSJan Kiszka }; 1372d5b61dddSJan Kiszka 1373999e12bbSAnthony Liguori static void musicpal_gpio_class_init(ObjectClass *klass, void *data) 1374999e12bbSAnthony Liguori { 137539bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 1376999e12bbSAnthony Liguori SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); 1377999e12bbSAnthony Liguori 1378999e12bbSAnthony Liguori k->init = musicpal_gpio_init; 137939bffca2SAnthony Liguori dc->reset = musicpal_gpio_reset; 138039bffca2SAnthony Liguori dc->vmsd = &musicpal_gpio_vmsd; 1381999e12bbSAnthony Liguori } 1382999e12bbSAnthony Liguori 13838c43a6f0SAndreas Färber static const TypeInfo musicpal_gpio_info = { 1384999e12bbSAnthony Liguori .name = "musicpal_gpio", 138539bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 138639bffca2SAnthony Liguori .instance_size = sizeof(musicpal_gpio_state), 1387999e12bbSAnthony Liguori .class_init = musicpal_gpio_class_init, 138830624c92SJan Kiszka }; 138930624c92SJan Kiszka 139024859b68Sbalrog /* Keyboard codes & masks */ 13917c6ce4baSbalrog #define KEY_RELEASED 0x80 139224859b68Sbalrog #define KEY_CODE 0x7f 139324859b68Sbalrog 139424859b68Sbalrog #define KEYCODE_TAB 0x0f 139524859b68Sbalrog #define KEYCODE_ENTER 0x1c 139624859b68Sbalrog #define KEYCODE_F 0x21 139724859b68Sbalrog #define KEYCODE_M 0x32 139824859b68Sbalrog 139924859b68Sbalrog #define KEYCODE_EXTENDED 0xe0 140024859b68Sbalrog #define KEYCODE_UP 0x48 140124859b68Sbalrog #define KEYCODE_DOWN 0x50 140224859b68Sbalrog #define KEYCODE_LEFT 0x4b 140324859b68Sbalrog #define KEYCODE_RIGHT 0x4d 140424859b68Sbalrog 1405708afdf3SJan Kiszka #define MP_KEY_WHEEL_VOL (1 << 0) 1406343ec8e4SBenoit Canet #define MP_KEY_WHEEL_VOL_INV (1 << 1) 1407343ec8e4SBenoit Canet #define MP_KEY_WHEEL_NAV (1 << 2) 1408343ec8e4SBenoit Canet #define MP_KEY_WHEEL_NAV_INV (1 << 3) 1409343ec8e4SBenoit Canet #define MP_KEY_BTN_FAVORITS (1 << 4) 1410343ec8e4SBenoit Canet #define MP_KEY_BTN_MENU (1 << 5) 1411343ec8e4SBenoit Canet #define MP_KEY_BTN_VOLUME (1 << 6) 1412343ec8e4SBenoit Canet #define MP_KEY_BTN_NAVIGATION (1 << 7) 1413343ec8e4SBenoit Canet 1414343ec8e4SBenoit Canet typedef struct musicpal_key_state { 1415343ec8e4SBenoit Canet SysBusDevice busdev; 14164f5c9479SAvi Kivity MemoryRegion iomem; 1417343ec8e4SBenoit Canet uint32_t kbd_extended; 1418708afdf3SJan Kiszka uint32_t pressed_keys; 1419708afdf3SJan Kiszka qemu_irq out[8]; 1420343ec8e4SBenoit Canet } musicpal_key_state; 1421343ec8e4SBenoit Canet 142224859b68Sbalrog static void musicpal_key_event(void *opaque, int keycode) 142324859b68Sbalrog { 1424243cd13cSJan Kiszka musicpal_key_state *s = opaque; 142524859b68Sbalrog uint32_t event = 0; 1426343ec8e4SBenoit Canet int i; 142724859b68Sbalrog 142824859b68Sbalrog if (keycode == KEYCODE_EXTENDED) { 1429343ec8e4SBenoit Canet s->kbd_extended = 1; 143024859b68Sbalrog return; 143124859b68Sbalrog } 143224859b68Sbalrog 143349fedd0dSJan Kiszka if (s->kbd_extended) { 143424859b68Sbalrog switch (keycode & KEY_CODE) { 143524859b68Sbalrog case KEYCODE_UP: 1436343ec8e4SBenoit Canet event = MP_KEY_WHEEL_NAV | MP_KEY_WHEEL_NAV_INV; 143724859b68Sbalrog break; 143824859b68Sbalrog 143924859b68Sbalrog case KEYCODE_DOWN: 1440343ec8e4SBenoit Canet event = MP_KEY_WHEEL_NAV; 144124859b68Sbalrog break; 144224859b68Sbalrog 144324859b68Sbalrog case KEYCODE_LEFT: 1444343ec8e4SBenoit Canet event = MP_KEY_WHEEL_VOL | MP_KEY_WHEEL_VOL_INV; 144524859b68Sbalrog break; 144624859b68Sbalrog 144724859b68Sbalrog case KEYCODE_RIGHT: 1448343ec8e4SBenoit Canet event = MP_KEY_WHEEL_VOL; 144924859b68Sbalrog break; 145024859b68Sbalrog } 145149fedd0dSJan Kiszka } else { 145224859b68Sbalrog switch (keycode & KEY_CODE) { 145324859b68Sbalrog case KEYCODE_F: 1454343ec8e4SBenoit Canet event = MP_KEY_BTN_FAVORITS; 145524859b68Sbalrog break; 145624859b68Sbalrog 145724859b68Sbalrog case KEYCODE_TAB: 1458343ec8e4SBenoit Canet event = MP_KEY_BTN_VOLUME; 145924859b68Sbalrog break; 146024859b68Sbalrog 146124859b68Sbalrog case KEYCODE_ENTER: 1462343ec8e4SBenoit Canet event = MP_KEY_BTN_NAVIGATION; 146324859b68Sbalrog break; 146424859b68Sbalrog 146524859b68Sbalrog case KEYCODE_M: 1466343ec8e4SBenoit Canet event = MP_KEY_BTN_MENU; 146724859b68Sbalrog break; 146824859b68Sbalrog } 14697c6ce4baSbalrog /* Do not repeat already pressed buttons */ 1470708afdf3SJan Kiszka if (!(keycode & KEY_RELEASED) && (s->pressed_keys & event)) { 14717c6ce4baSbalrog event = 0; 14727c6ce4baSbalrog } 1473708afdf3SJan Kiszka } 147424859b68Sbalrog 14757c6ce4baSbalrog if (event) { 1476708afdf3SJan Kiszka /* Raise GPIO pin first if repeating a key */ 1477708afdf3SJan Kiszka if (!(keycode & KEY_RELEASED) && (s->pressed_keys & event)) { 1478708afdf3SJan Kiszka for (i = 0; i <= 7; i++) { 1479708afdf3SJan Kiszka if (event & (1 << i)) { 1480708afdf3SJan Kiszka qemu_set_irq(s->out[i], 1); 14817c6ce4baSbalrog } 1482708afdf3SJan Kiszka } 1483708afdf3SJan Kiszka } 1484708afdf3SJan Kiszka for (i = 0; i <= 7; i++) { 1485708afdf3SJan Kiszka if (event & (1 << i)) { 1486708afdf3SJan Kiszka qemu_set_irq(s->out[i], !!(keycode & KEY_RELEASED)); 1487708afdf3SJan Kiszka } 1488708afdf3SJan Kiszka } 1489708afdf3SJan Kiszka if (keycode & KEY_RELEASED) { 1490708afdf3SJan Kiszka s->pressed_keys &= ~event; 1491708afdf3SJan Kiszka } else { 1492708afdf3SJan Kiszka s->pressed_keys |= event; 1493708afdf3SJan Kiszka } 1494343ec8e4SBenoit Canet } 1495343ec8e4SBenoit Canet 1496343ec8e4SBenoit Canet s->kbd_extended = 0; 1497343ec8e4SBenoit Canet } 1498343ec8e4SBenoit Canet 149981a322d4SGerd Hoffmann static int musicpal_key_init(SysBusDevice *dev) 1500343ec8e4SBenoit Canet { 1501343ec8e4SBenoit Canet musicpal_key_state *s = FROM_SYSBUS(musicpal_key_state, dev); 1502343ec8e4SBenoit Canet 150364bde0f3SPaolo Bonzini memory_region_init(&s->iomem, OBJECT(s), "dummy", 0); 1504750ecd44SAvi Kivity sysbus_init_mmio(dev, &s->iomem); 1505343ec8e4SBenoit Canet 1506343ec8e4SBenoit Canet s->kbd_extended = 0; 1507708afdf3SJan Kiszka s->pressed_keys = 0; 1508343ec8e4SBenoit Canet 1509708afdf3SJan Kiszka qdev_init_gpio_out(&dev->qdev, s->out, ARRAY_SIZE(s->out)); 1510343ec8e4SBenoit Canet 1511343ec8e4SBenoit Canet qemu_add_kbd_event_handler(musicpal_key_event, s); 151281a322d4SGerd Hoffmann 151381a322d4SGerd Hoffmann return 0; 151424859b68Sbalrog } 151524859b68Sbalrog 1516d5b61dddSJan Kiszka static const VMStateDescription musicpal_key_vmsd = { 1517d5b61dddSJan Kiszka .name = "musicpal_key", 1518d5b61dddSJan Kiszka .version_id = 1, 1519d5b61dddSJan Kiszka .minimum_version_id = 1, 1520d5b61dddSJan Kiszka .minimum_version_id_old = 1, 1521d5b61dddSJan Kiszka .fields = (VMStateField[]) { 1522d5b61dddSJan Kiszka VMSTATE_UINT32(kbd_extended, musicpal_key_state), 1523d5b61dddSJan Kiszka VMSTATE_UINT32(pressed_keys, musicpal_key_state), 1524d5b61dddSJan Kiszka VMSTATE_END_OF_LIST() 1525d5b61dddSJan Kiszka } 1526d5b61dddSJan Kiszka }; 1527d5b61dddSJan Kiszka 1528999e12bbSAnthony Liguori static void musicpal_key_class_init(ObjectClass *klass, void *data) 1529999e12bbSAnthony Liguori { 153039bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 1531999e12bbSAnthony Liguori SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); 1532999e12bbSAnthony Liguori 1533999e12bbSAnthony Liguori k->init = musicpal_key_init; 153439bffca2SAnthony Liguori dc->vmsd = &musicpal_key_vmsd; 1535999e12bbSAnthony Liguori } 1536999e12bbSAnthony Liguori 15378c43a6f0SAndreas Färber static const TypeInfo musicpal_key_info = { 1538999e12bbSAnthony Liguori .name = "musicpal_key", 153939bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 154039bffca2SAnthony Liguori .instance_size = sizeof(musicpal_key_state), 1541999e12bbSAnthony Liguori .class_init = musicpal_key_class_init, 1542d5b61dddSJan Kiszka }; 1543d5b61dddSJan Kiszka 154424859b68Sbalrog static struct arm_boot_info musicpal_binfo = { 154524859b68Sbalrog .loader_start = 0x0, 154624859b68Sbalrog .board_id = 0x20e, 154724859b68Sbalrog }; 154824859b68Sbalrog 15495f072e1fSEduardo Habkost static void musicpal_init(QEMUMachineInitArgs *args) 155024859b68Sbalrog { 15515f072e1fSEduardo Habkost const char *cpu_model = args->cpu_model; 15525f072e1fSEduardo Habkost const char *kernel_filename = args->kernel_filename; 15535f072e1fSEduardo Habkost const char *kernel_cmdline = args->kernel_cmdline; 15545f072e1fSEduardo Habkost const char *initrd_filename = args->initrd_filename; 1555f25608e9SAndreas Färber ARMCPU *cpu; 1556b47b50faSPaul Brook qemu_irq *cpu_pic; 1557b47b50faSPaul Brook qemu_irq pic[32]; 1558b47b50faSPaul Brook DeviceState *dev; 1559d074769cSAndrzej Zaborowski DeviceState *i2c_dev; 1560343ec8e4SBenoit Canet DeviceState *lcd_dev; 1561343ec8e4SBenoit Canet DeviceState *key_dev; 1562d074769cSAndrzej Zaborowski DeviceState *wm8750_dev; 1563d074769cSAndrzej Zaborowski SysBusDevice *s; 1564d074769cSAndrzej Zaborowski i2c_bus *i2c; 1565b47b50faSPaul Brook int i; 156624859b68Sbalrog unsigned long flash_size; 1567751c6a17SGerd Hoffmann DriveInfo *dinfo; 156819b4a424SAvi Kivity MemoryRegion *address_space_mem = get_system_memory(); 156919b4a424SAvi Kivity MemoryRegion *ram = g_new(MemoryRegion, 1); 157019b4a424SAvi Kivity MemoryRegion *sram = g_new(MemoryRegion, 1); 157124859b68Sbalrog 157249fedd0dSJan Kiszka if (!cpu_model) { 157324859b68Sbalrog cpu_model = "arm926"; 157449fedd0dSJan Kiszka } 1575f25608e9SAndreas Färber cpu = cpu_arm_init(cpu_model); 1576f25608e9SAndreas Färber if (!cpu) { 157724859b68Sbalrog fprintf(stderr, "Unable to find CPU definition\n"); 157824859b68Sbalrog exit(1); 157924859b68Sbalrog } 15804bd74661SAndreas Färber cpu_pic = arm_pic_init_cpu(cpu); 158124859b68Sbalrog 158224859b68Sbalrog /* For now we use a fixed - the original - RAM size */ 15832c9b15caSPaolo Bonzini memory_region_init_ram(ram, NULL, "musicpal.ram", MP_RAM_DEFAULT_SIZE); 1584c5705a77SAvi Kivity vmstate_register_ram_global(ram); 158519b4a424SAvi Kivity memory_region_add_subregion(address_space_mem, 0, ram); 158624859b68Sbalrog 15872c9b15caSPaolo Bonzini memory_region_init_ram(sram, NULL, "musicpal.sram", MP_SRAM_SIZE); 1588c5705a77SAvi Kivity vmstate_register_ram_global(sram); 158919b4a424SAvi Kivity memory_region_add_subregion(address_space_mem, MP_SRAM_BASE, sram); 159024859b68Sbalrog 1591b47b50faSPaul Brook dev = sysbus_create_simple("mv88w8618_pic", MP_PIC_BASE, 1592b47b50faSPaul Brook cpu_pic[ARM_PIC_CPU_IRQ]); 1593b47b50faSPaul Brook for (i = 0; i < 32; i++) { 1594067a3ddcSPaul Brook pic[i] = qdev_get_gpio_in(dev, i); 1595b47b50faSPaul Brook } 1596b47b50faSPaul Brook sysbus_create_varargs("mv88w8618_pit", MP_PIT_BASE, pic[MP_TIMER1_IRQ], 1597b47b50faSPaul Brook pic[MP_TIMER2_IRQ], pic[MP_TIMER3_IRQ], 1598b47b50faSPaul Brook pic[MP_TIMER4_IRQ], NULL); 159924859b68Sbalrog 160049fedd0dSJan Kiszka if (serial_hds[0]) { 160139186d8aSRichard Henderson serial_mm_init(address_space_mem, MP_UART1_BASE, 2, pic[MP_UART1_IRQ], 160239186d8aSRichard Henderson 1825000, serial_hds[0], DEVICE_NATIVE_ENDIAN); 160349fedd0dSJan Kiszka } 160449fedd0dSJan Kiszka if (serial_hds[1]) { 160539186d8aSRichard Henderson serial_mm_init(address_space_mem, MP_UART2_BASE, 2, pic[MP_UART2_IRQ], 160639186d8aSRichard Henderson 1825000, serial_hds[1], DEVICE_NATIVE_ENDIAN); 160749fedd0dSJan Kiszka } 160824859b68Sbalrog 160924859b68Sbalrog /* Register flash */ 1610751c6a17SGerd Hoffmann dinfo = drive_get(IF_PFLASH, 0, 0); 1611751c6a17SGerd Hoffmann if (dinfo) { 1612751c6a17SGerd Hoffmann flash_size = bdrv_getlength(dinfo->bdrv); 161324859b68Sbalrog if (flash_size != 8*1024*1024 && flash_size != 16*1024*1024 && 161424859b68Sbalrog flash_size != 32*1024*1024) { 161524859b68Sbalrog fprintf(stderr, "Invalid flash image size\n"); 161624859b68Sbalrog exit(1); 161724859b68Sbalrog } 161824859b68Sbalrog 161924859b68Sbalrog /* 162024859b68Sbalrog * The original U-Boot accesses the flash at 0xFE000000 instead of 162124859b68Sbalrog * 0xFF800000 (if there is 8 MB flash). So remap flash access if the 162224859b68Sbalrog * image is smaller than 32 MB. 162324859b68Sbalrog */ 16245f9fc5adSBlue Swirl #ifdef TARGET_WORDS_BIGENDIAN 16250c267217SJan Kiszka pflash_cfi02_register(0x100000000ULL-MP_FLASH_SIZE_MAX, NULL, 1626cfe5f011SAvi Kivity "musicpal.flash", flash_size, 1627751c6a17SGerd Hoffmann dinfo->bdrv, 0x10000, 162824859b68Sbalrog (flash_size + 0xffff) >> 16, 162924859b68Sbalrog MP_FLASH_SIZE_MAX / flash_size, 163024859b68Sbalrog 2, 0x00BF, 0x236D, 0x0000, 0x0000, 163101e0451aSAnthony Liguori 0x5555, 0x2AAA, 1); 16325f9fc5adSBlue Swirl #else 16330c267217SJan Kiszka pflash_cfi02_register(0x100000000ULL-MP_FLASH_SIZE_MAX, NULL, 1634cfe5f011SAvi Kivity "musicpal.flash", flash_size, 16355f9fc5adSBlue Swirl dinfo->bdrv, 0x10000, 16365f9fc5adSBlue Swirl (flash_size + 0xffff) >> 16, 16375f9fc5adSBlue Swirl MP_FLASH_SIZE_MAX / flash_size, 16385f9fc5adSBlue Swirl 2, 0x00BF, 0x236D, 0x0000, 0x0000, 163901e0451aSAnthony Liguori 0x5555, 0x2AAA, 0); 16405f9fc5adSBlue Swirl #endif 16415f9fc5adSBlue Swirl 164224859b68Sbalrog } 1643b47b50faSPaul Brook sysbus_create_simple("mv88w8618_flashcfg", MP_FLASHCFG_BASE, NULL); 164424859b68Sbalrog 1645b47b50faSPaul Brook qemu_check_nic_model(&nd_table[0], "mv88w8618"); 1646a77d90e6SAndreas Färber dev = qdev_create(NULL, TYPE_MV88W8618_ETH); 16474c91cd28SGerd Hoffmann qdev_set_nic_properties(dev, &nd_table[0]); 1648e23a1b33SMarkus Armbruster qdev_init_nofail(dev); 16491356b98dSAndreas Färber sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, MP_ETH_BASE); 16501356b98dSAndreas Färber sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[MP_ETH_IRQ]); 165124859b68Sbalrog 1652b47b50faSPaul Brook sysbus_create_simple("mv88w8618_wlan", MP_WLAN_BASE, NULL); 1653718ec0beSmalc 1654a86f200aSPeter Maydell sysbus_create_simple(TYPE_MUSICPAL_MISC, MP_MISC_BASE, NULL); 1655343ec8e4SBenoit Canet 1656343ec8e4SBenoit Canet dev = sysbus_create_simple("musicpal_gpio", MP_GPIO_BASE, pic[MP_GPIO_IRQ]); 1657d04fba94SJan Kiszka i2c_dev = sysbus_create_simple("gpio_i2c", -1, NULL); 1658d074769cSAndrzej Zaborowski i2c = (i2c_bus *)qdev_get_child_bus(i2c_dev, "i2c"); 1659d074769cSAndrzej Zaborowski 1660*2cca58fdSAndreas Färber lcd_dev = sysbus_create_simple(TYPE_MUSICPAL_LCD, MP_LCD_BASE, NULL); 1661d04fba94SJan Kiszka key_dev = sysbus_create_simple("musicpal_key", -1, NULL); 1662343ec8e4SBenoit Canet 1663d074769cSAndrzej Zaborowski /* I2C read data */ 1664708afdf3SJan Kiszka qdev_connect_gpio_out(i2c_dev, 0, 1665708afdf3SJan Kiszka qdev_get_gpio_in(dev, MP_GPIO_I2C_DATA_BIT)); 1666d074769cSAndrzej Zaborowski /* I2C data */ 1667d074769cSAndrzej Zaborowski qdev_connect_gpio_out(dev, 3, qdev_get_gpio_in(i2c_dev, 0)); 1668d074769cSAndrzej Zaborowski /* I2C clock */ 1669d074769cSAndrzej Zaborowski qdev_connect_gpio_out(dev, 4, qdev_get_gpio_in(i2c_dev, 1)); 1670d074769cSAndrzej Zaborowski 167149fedd0dSJan Kiszka for (i = 0; i < 3; i++) { 1672343ec8e4SBenoit Canet qdev_connect_gpio_out(dev, i, qdev_get_gpio_in(lcd_dev, i)); 167349fedd0dSJan Kiszka } 1674708afdf3SJan Kiszka for (i = 0; i < 4; i++) { 1675708afdf3SJan Kiszka qdev_connect_gpio_out(key_dev, i, qdev_get_gpio_in(dev, i + 8)); 1676708afdf3SJan Kiszka } 1677708afdf3SJan Kiszka for (i = 4; i < 8; i++) { 1678708afdf3SJan Kiszka qdev_connect_gpio_out(key_dev, i, qdev_get_gpio_in(dev, i + 15)); 1679708afdf3SJan Kiszka } 168024859b68Sbalrog 1681d074769cSAndrzej Zaborowski wm8750_dev = i2c_create_slave(i2c, "wm8750", MP_WM_ADDR); 1682d074769cSAndrzej Zaborowski dev = qdev_create(NULL, "mv88w8618_audio"); 16831356b98dSAndreas Färber s = SYS_BUS_DEVICE(dev); 1684d074769cSAndrzej Zaborowski qdev_prop_set_ptr(dev, "wm8750", wm8750_dev); 1685e23a1b33SMarkus Armbruster qdev_init_nofail(dev); 1686d074769cSAndrzej Zaborowski sysbus_mmio_map(s, 0, MP_AUDIO_BASE); 1687d074769cSAndrzej Zaborowski sysbus_connect_irq(s, 0, pic[MP_AUDIO_IRQ]); 1688d074769cSAndrzej Zaborowski 168924859b68Sbalrog musicpal_binfo.ram_size = MP_RAM_DEFAULT_SIZE; 169024859b68Sbalrog musicpal_binfo.kernel_filename = kernel_filename; 169124859b68Sbalrog musicpal_binfo.kernel_cmdline = kernel_cmdline; 169224859b68Sbalrog musicpal_binfo.initrd_filename = initrd_filename; 16933aaa8dfaSAndreas Färber arm_load_kernel(cpu, &musicpal_binfo); 169424859b68Sbalrog } 169524859b68Sbalrog 1696f80f9ec9SAnthony Liguori static QEMUMachine musicpal_machine = { 16974b32e168Saliguori .name = "musicpal", 16984b32e168Saliguori .desc = "Marvell 88w8618 / MusicPal (ARM926EJ-S)", 16994b32e168Saliguori .init = musicpal_init, 1700e4ada29eSAvik Sil DEFAULT_MACHINE_OPTIONS, 170124859b68Sbalrog }; 1702b47b50faSPaul Brook 1703f80f9ec9SAnthony Liguori static void musicpal_machine_init(void) 1704f80f9ec9SAnthony Liguori { 1705f80f9ec9SAnthony Liguori qemu_register_machine(&musicpal_machine); 1706f80f9ec9SAnthony Liguori } 1707f80f9ec9SAnthony Liguori 1708f80f9ec9SAnthony Liguori machine_init(musicpal_machine_init); 1709f80f9ec9SAnthony Liguori 1710999e12bbSAnthony Liguori static void mv88w8618_wlan_class_init(ObjectClass *klass, void *data) 1711999e12bbSAnthony Liguori { 1712999e12bbSAnthony Liguori SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); 1713999e12bbSAnthony Liguori 1714999e12bbSAnthony Liguori sdc->init = mv88w8618_wlan_init; 1715999e12bbSAnthony Liguori } 1716999e12bbSAnthony Liguori 17178c43a6f0SAndreas Färber static const TypeInfo mv88w8618_wlan_info = { 1718999e12bbSAnthony Liguori .name = "mv88w8618_wlan", 171939bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 172039bffca2SAnthony Liguori .instance_size = sizeof(SysBusDevice), 1721999e12bbSAnthony Liguori .class_init = mv88w8618_wlan_class_init, 1722999e12bbSAnthony Liguori }; 1723999e12bbSAnthony Liguori 172483f7d43aSAndreas Färber static void musicpal_register_types(void) 1725b47b50faSPaul Brook { 172639bffca2SAnthony Liguori type_register_static(&mv88w8618_pic_info); 172739bffca2SAnthony Liguori type_register_static(&mv88w8618_pit_info); 172839bffca2SAnthony Liguori type_register_static(&mv88w8618_flashcfg_info); 172939bffca2SAnthony Liguori type_register_static(&mv88w8618_eth_info); 173039bffca2SAnthony Liguori type_register_static(&mv88w8618_wlan_info); 173139bffca2SAnthony Liguori type_register_static(&musicpal_lcd_info); 173239bffca2SAnthony Liguori type_register_static(&musicpal_gpio_info); 173339bffca2SAnthony Liguori type_register_static(&musicpal_key_info); 1734a86f200aSPeter Maydell type_register_static(&musicpal_misc_info); 1735b47b50faSPaul Brook } 1736b47b50faSPaul Brook 173783f7d43aSAndreas Färber type_init(musicpal_register_types) 1738