124859b68Sbalrog /* 224859b68Sbalrog * Marvell MV88W8618 / Freecom MusicPal emulation. 324859b68Sbalrog * 424859b68Sbalrog * Copyright (c) 2008 Jan Kiszka 524859b68Sbalrog * 68e31bf38SMatthew Fernandez * This code is licensed under the GNU GPL v2. 76b620ca3SPaolo Bonzini * 86b620ca3SPaolo Bonzini * Contributions after 2012-01-13 are licensed under the terms of the 96b620ca3SPaolo Bonzini * GNU GPL, version 2 or (at your option) any later version. 1024859b68Sbalrog */ 1124859b68Sbalrog 1212b16722SPeter Maydell #include "qemu/osdep.h" 13da34e65cSMarkus Armbruster #include "qapi/error.h" 14*4771d756SPaolo Bonzini #include "qemu-common.h" 15*4771d756SPaolo Bonzini #include "cpu.h" 1683c9f4caSPaolo Bonzini #include "hw/sysbus.h" 17bd2be150SPeter Maydell #include "hw/arm/arm.h" 18bd2be150SPeter Maydell #include "hw/devices.h" 191422e32dSPaolo Bonzini #include "net/net.h" 209c17d615SPaolo Bonzini #include "sysemu/sysemu.h" 2183c9f4caSPaolo Bonzini #include "hw/boards.h" 220d09e41aSPaolo Bonzini #include "hw/char/serial.h" 231de7afc9SPaolo Bonzini #include "qemu/timer.h" 2483c9f4caSPaolo Bonzini #include "hw/ptimer.h" 250d09e41aSPaolo Bonzini #include "hw/block/flash.h" 2628ecbaeeSPaolo Bonzini #include "ui/console.h" 270d09e41aSPaolo Bonzini #include "hw/i2c/i2c.h" 28fa1d36dfSMarkus Armbruster #include "sysemu/block-backend.h" 29022c62cbSPaolo Bonzini #include "exec/address-spaces.h" 3028ecbaeeSPaolo Bonzini #include "ui/pixel_ops.h" 3124859b68Sbalrog 32718ec0beSmalc #define MP_MISC_BASE 0x80002000 33718ec0beSmalc #define MP_MISC_SIZE 0x00001000 34718ec0beSmalc 3524859b68Sbalrog #define MP_ETH_BASE 0x80008000 3624859b68Sbalrog #define MP_ETH_SIZE 0x00001000 3724859b68Sbalrog 38718ec0beSmalc #define MP_WLAN_BASE 0x8000C000 39718ec0beSmalc #define MP_WLAN_SIZE 0x00000800 40718ec0beSmalc 4124859b68Sbalrog #define MP_UART1_BASE 0x8000C840 4224859b68Sbalrog #define MP_UART2_BASE 0x8000C940 4324859b68Sbalrog 44718ec0beSmalc #define MP_GPIO_BASE 0x8000D000 45718ec0beSmalc #define MP_GPIO_SIZE 0x00001000 46718ec0beSmalc 4724859b68Sbalrog #define MP_FLASHCFG_BASE 0x90006000 4824859b68Sbalrog #define MP_FLASHCFG_SIZE 0x00001000 4924859b68Sbalrog 5024859b68Sbalrog #define MP_AUDIO_BASE 0x90007000 5124859b68Sbalrog 5224859b68Sbalrog #define MP_PIC_BASE 0x90008000 5324859b68Sbalrog #define MP_PIC_SIZE 0x00001000 5424859b68Sbalrog 5524859b68Sbalrog #define MP_PIT_BASE 0x90009000 5624859b68Sbalrog #define MP_PIT_SIZE 0x00001000 5724859b68Sbalrog 5824859b68Sbalrog #define MP_LCD_BASE 0x9000c000 5924859b68Sbalrog #define MP_LCD_SIZE 0x00001000 6024859b68Sbalrog 6124859b68Sbalrog #define MP_SRAM_BASE 0xC0000000 6224859b68Sbalrog #define MP_SRAM_SIZE 0x00020000 6324859b68Sbalrog 6424859b68Sbalrog #define MP_RAM_DEFAULT_SIZE 32*1024*1024 6524859b68Sbalrog #define MP_FLASH_SIZE_MAX 32*1024*1024 6624859b68Sbalrog 6724859b68Sbalrog #define MP_TIMER1_IRQ 4 68b47b50faSPaul Brook #define MP_TIMER2_IRQ 5 69b47b50faSPaul Brook #define MP_TIMER3_IRQ 6 7024859b68Sbalrog #define MP_TIMER4_IRQ 7 7124859b68Sbalrog #define MP_EHCI_IRQ 8 7224859b68Sbalrog #define MP_ETH_IRQ 9 7324859b68Sbalrog #define MP_UART1_IRQ 11 7424859b68Sbalrog #define MP_UART2_IRQ 11 7524859b68Sbalrog #define MP_GPIO_IRQ 12 7624859b68Sbalrog #define MP_RTC_IRQ 28 7724859b68Sbalrog #define MP_AUDIO_IRQ 30 7824859b68Sbalrog 7924859b68Sbalrog /* Wolfson 8750 I2C address */ 8064258229SJan Kiszka #define MP_WM_ADDR 0x1A 8124859b68Sbalrog 8224859b68Sbalrog /* Ethernet register offsets */ 8324859b68Sbalrog #define MP_ETH_SMIR 0x010 8424859b68Sbalrog #define MP_ETH_PCXR 0x408 8524859b68Sbalrog #define MP_ETH_SDCMR 0x448 8624859b68Sbalrog #define MP_ETH_ICR 0x450 8724859b68Sbalrog #define MP_ETH_IMR 0x458 8824859b68Sbalrog #define MP_ETH_FRDP0 0x480 8924859b68Sbalrog #define MP_ETH_FRDP1 0x484 9024859b68Sbalrog #define MP_ETH_FRDP2 0x488 9124859b68Sbalrog #define MP_ETH_FRDP3 0x48C 9224859b68Sbalrog #define MP_ETH_CRDP0 0x4A0 9324859b68Sbalrog #define MP_ETH_CRDP1 0x4A4 9424859b68Sbalrog #define MP_ETH_CRDP2 0x4A8 9524859b68Sbalrog #define MP_ETH_CRDP3 0x4AC 9624859b68Sbalrog #define MP_ETH_CTDP0 0x4E0 9724859b68Sbalrog #define MP_ETH_CTDP1 0x4E4 9824859b68Sbalrog 9924859b68Sbalrog /* MII PHY access */ 10024859b68Sbalrog #define MP_ETH_SMIR_DATA 0x0000FFFF 10124859b68Sbalrog #define MP_ETH_SMIR_ADDR 0x03FF0000 10224859b68Sbalrog #define MP_ETH_SMIR_OPCODE (1 << 26) /* Read value */ 10324859b68Sbalrog #define MP_ETH_SMIR_RDVALID (1 << 27) 10424859b68Sbalrog 10524859b68Sbalrog /* PHY registers */ 10624859b68Sbalrog #define MP_ETH_PHY1_BMSR 0x00210000 10724859b68Sbalrog #define MP_ETH_PHY1_PHYSID1 0x00410000 10824859b68Sbalrog #define MP_ETH_PHY1_PHYSID2 0x00610000 10924859b68Sbalrog 11024859b68Sbalrog #define MP_PHY_BMSR_LINK 0x0004 11124859b68Sbalrog #define MP_PHY_BMSR_AUTONEG 0x0008 11224859b68Sbalrog 11324859b68Sbalrog #define MP_PHY_88E3015 0x01410E20 11424859b68Sbalrog 11524859b68Sbalrog /* TX descriptor status */ 1162b194951SPeter Maydell #define MP_ETH_TX_OWN (1U << 31) 11724859b68Sbalrog 11824859b68Sbalrog /* RX descriptor status */ 1192b194951SPeter Maydell #define MP_ETH_RX_OWN (1U << 31) 12024859b68Sbalrog 12124859b68Sbalrog /* Interrupt cause/mask bits */ 12224859b68Sbalrog #define MP_ETH_IRQ_RX_BIT 0 12324859b68Sbalrog #define MP_ETH_IRQ_RX (1 << MP_ETH_IRQ_RX_BIT) 12424859b68Sbalrog #define MP_ETH_IRQ_TXHI_BIT 2 12524859b68Sbalrog #define MP_ETH_IRQ_TXLO_BIT 3 12624859b68Sbalrog 12724859b68Sbalrog /* Port config bits */ 12824859b68Sbalrog #define MP_ETH_PCXR_2BSM_BIT 28 /* 2-byte incoming suffix */ 12924859b68Sbalrog 13024859b68Sbalrog /* SDMA command bits */ 13124859b68Sbalrog #define MP_ETH_CMD_TXHI (1 << 23) 13224859b68Sbalrog #define MP_ETH_CMD_TXLO (1 << 22) 13324859b68Sbalrog 13424859b68Sbalrog typedef struct mv88w8618_tx_desc { 13524859b68Sbalrog uint32_t cmdstat; 13624859b68Sbalrog uint16_t res; 13724859b68Sbalrog uint16_t bytes; 13824859b68Sbalrog uint32_t buffer; 13924859b68Sbalrog uint32_t next; 14024859b68Sbalrog } mv88w8618_tx_desc; 14124859b68Sbalrog 14224859b68Sbalrog typedef struct mv88w8618_rx_desc { 14324859b68Sbalrog uint32_t cmdstat; 14424859b68Sbalrog uint16_t bytes; 14524859b68Sbalrog uint16_t buffer_size; 14624859b68Sbalrog uint32_t buffer; 14724859b68Sbalrog uint32_t next; 14824859b68Sbalrog } mv88w8618_rx_desc; 14924859b68Sbalrog 150a77d90e6SAndreas Färber #define TYPE_MV88W8618_ETH "mv88w8618_eth" 151a77d90e6SAndreas Färber #define MV88W8618_ETH(obj) \ 152a77d90e6SAndreas Färber OBJECT_CHECK(mv88w8618_eth_state, (obj), TYPE_MV88W8618_ETH) 153a77d90e6SAndreas Färber 15424859b68Sbalrog typedef struct mv88w8618_eth_state { 155a77d90e6SAndreas Färber /*< private >*/ 156a77d90e6SAndreas Färber SysBusDevice parent_obj; 157a77d90e6SAndreas Färber /*< public >*/ 158a77d90e6SAndreas Färber 15919b4a424SAvi Kivity MemoryRegion iomem; 16024859b68Sbalrog qemu_irq irq; 16124859b68Sbalrog uint32_t smir; 16224859b68Sbalrog uint32_t icr; 16324859b68Sbalrog uint32_t imr; 164b946a153Saliguori int mmio_index; 165d5b61dddSJan Kiszka uint32_t vlan_header; 166930c8682Spbrook uint32_t tx_queue[2]; 167930c8682Spbrook uint32_t rx_queue[4]; 168930c8682Spbrook uint32_t frx_queue[4]; 169930c8682Spbrook uint32_t cur_rx[4]; 1703a94dd18SMark McLoughlin NICState *nic; 1714c91cd28SGerd Hoffmann NICConf conf; 17224859b68Sbalrog } mv88w8618_eth_state; 17324859b68Sbalrog 174930c8682Spbrook static void eth_rx_desc_put(uint32_t addr, mv88w8618_rx_desc *desc) 175930c8682Spbrook { 176930c8682Spbrook cpu_to_le32s(&desc->cmdstat); 177930c8682Spbrook cpu_to_le16s(&desc->bytes); 178930c8682Spbrook cpu_to_le16s(&desc->buffer_size); 179930c8682Spbrook cpu_to_le32s(&desc->buffer); 180930c8682Spbrook cpu_to_le32s(&desc->next); 181e1fe50dcSStefan Weil cpu_physical_memory_write(addr, desc, sizeof(*desc)); 182930c8682Spbrook } 183930c8682Spbrook 184930c8682Spbrook static void eth_rx_desc_get(uint32_t addr, mv88w8618_rx_desc *desc) 185930c8682Spbrook { 186e1fe50dcSStefan Weil cpu_physical_memory_read(addr, desc, sizeof(*desc)); 187930c8682Spbrook le32_to_cpus(&desc->cmdstat); 188930c8682Spbrook le16_to_cpus(&desc->bytes); 189930c8682Spbrook le16_to_cpus(&desc->buffer_size); 190930c8682Spbrook le32_to_cpus(&desc->buffer); 191930c8682Spbrook le32_to_cpus(&desc->next); 192930c8682Spbrook } 193930c8682Spbrook 1944e68f7a0SStefan Hajnoczi static ssize_t eth_receive(NetClientState *nc, const uint8_t *buf, size_t size) 19524859b68Sbalrog { 196cc1f0f45SJason Wang mv88w8618_eth_state *s = qemu_get_nic_opaque(nc); 197930c8682Spbrook uint32_t desc_addr; 198930c8682Spbrook mv88w8618_rx_desc desc; 19924859b68Sbalrog int i; 20024859b68Sbalrog 20124859b68Sbalrog for (i = 0; i < 4; i++) { 202930c8682Spbrook desc_addr = s->cur_rx[i]; 20349fedd0dSJan Kiszka if (!desc_addr) { 20424859b68Sbalrog continue; 20549fedd0dSJan Kiszka } 20624859b68Sbalrog do { 207930c8682Spbrook eth_rx_desc_get(desc_addr, &desc); 208930c8682Spbrook if ((desc.cmdstat & MP_ETH_RX_OWN) && desc.buffer_size >= size) { 209930c8682Spbrook cpu_physical_memory_write(desc.buffer + s->vlan_header, 21024859b68Sbalrog buf, size); 211930c8682Spbrook desc.bytes = size + s->vlan_header; 212930c8682Spbrook desc.cmdstat &= ~MP_ETH_RX_OWN; 213930c8682Spbrook s->cur_rx[i] = desc.next; 21424859b68Sbalrog 21524859b68Sbalrog s->icr |= MP_ETH_IRQ_RX; 21649fedd0dSJan Kiszka if (s->icr & s->imr) { 21724859b68Sbalrog qemu_irq_raise(s->irq); 21849fedd0dSJan Kiszka } 219930c8682Spbrook eth_rx_desc_put(desc_addr, &desc); 2204f1c942bSMark McLoughlin return size; 22124859b68Sbalrog } 222930c8682Spbrook desc_addr = desc.next; 223930c8682Spbrook } while (desc_addr != s->rx_queue[i]); 22424859b68Sbalrog } 2254f1c942bSMark McLoughlin return size; 22624859b68Sbalrog } 22724859b68Sbalrog 228930c8682Spbrook static void eth_tx_desc_put(uint32_t addr, mv88w8618_tx_desc *desc) 229930c8682Spbrook { 230930c8682Spbrook cpu_to_le32s(&desc->cmdstat); 231930c8682Spbrook cpu_to_le16s(&desc->res); 232930c8682Spbrook cpu_to_le16s(&desc->bytes); 233930c8682Spbrook cpu_to_le32s(&desc->buffer); 234930c8682Spbrook cpu_to_le32s(&desc->next); 235e1fe50dcSStefan Weil cpu_physical_memory_write(addr, desc, sizeof(*desc)); 236930c8682Spbrook } 237930c8682Spbrook 238930c8682Spbrook static void eth_tx_desc_get(uint32_t addr, mv88w8618_tx_desc *desc) 239930c8682Spbrook { 240e1fe50dcSStefan Weil cpu_physical_memory_read(addr, desc, sizeof(*desc)); 241930c8682Spbrook le32_to_cpus(&desc->cmdstat); 242930c8682Spbrook le16_to_cpus(&desc->res); 243930c8682Spbrook le16_to_cpus(&desc->bytes); 244930c8682Spbrook le32_to_cpus(&desc->buffer); 245930c8682Spbrook le32_to_cpus(&desc->next); 246930c8682Spbrook } 247930c8682Spbrook 24824859b68Sbalrog static void eth_send(mv88w8618_eth_state *s, int queue_index) 24924859b68Sbalrog { 250930c8682Spbrook uint32_t desc_addr = s->tx_queue[queue_index]; 251930c8682Spbrook mv88w8618_tx_desc desc; 25207b064e9SJan Kiszka uint32_t next_desc; 253930c8682Spbrook uint8_t buf[2048]; 254930c8682Spbrook int len; 255930c8682Spbrook 25624859b68Sbalrog do { 257930c8682Spbrook eth_tx_desc_get(desc_addr, &desc); 25807b064e9SJan Kiszka next_desc = desc.next; 259930c8682Spbrook if (desc.cmdstat & MP_ETH_TX_OWN) { 260930c8682Spbrook len = desc.bytes; 261930c8682Spbrook if (len < 2048) { 262930c8682Spbrook cpu_physical_memory_read(desc.buffer, buf, len); 263b356f76dSJason Wang qemu_send_packet(qemu_get_queue(s->nic), buf, len); 26424859b68Sbalrog } 265930c8682Spbrook desc.cmdstat &= ~MP_ETH_TX_OWN; 266930c8682Spbrook s->icr |= 1 << (MP_ETH_IRQ_TXLO_BIT - queue_index); 267930c8682Spbrook eth_tx_desc_put(desc_addr, &desc); 268930c8682Spbrook } 26907b064e9SJan Kiszka desc_addr = next_desc; 270930c8682Spbrook } while (desc_addr != s->tx_queue[queue_index]); 27124859b68Sbalrog } 27224859b68Sbalrog 273a8170e5eSAvi Kivity static uint64_t mv88w8618_eth_read(void *opaque, hwaddr offset, 27419b4a424SAvi Kivity unsigned size) 27524859b68Sbalrog { 27624859b68Sbalrog mv88w8618_eth_state *s = opaque; 27724859b68Sbalrog 27824859b68Sbalrog switch (offset) { 27924859b68Sbalrog case MP_ETH_SMIR: 28024859b68Sbalrog if (s->smir & MP_ETH_SMIR_OPCODE) { 28124859b68Sbalrog switch (s->smir & MP_ETH_SMIR_ADDR) { 28224859b68Sbalrog case MP_ETH_PHY1_BMSR: 28324859b68Sbalrog return MP_PHY_BMSR_LINK | MP_PHY_BMSR_AUTONEG | 28424859b68Sbalrog MP_ETH_SMIR_RDVALID; 28524859b68Sbalrog case MP_ETH_PHY1_PHYSID1: 28624859b68Sbalrog return (MP_PHY_88E3015 >> 16) | MP_ETH_SMIR_RDVALID; 28724859b68Sbalrog case MP_ETH_PHY1_PHYSID2: 28824859b68Sbalrog return (MP_PHY_88E3015 & 0xFFFF) | MP_ETH_SMIR_RDVALID; 28924859b68Sbalrog default: 29024859b68Sbalrog return MP_ETH_SMIR_RDVALID; 29124859b68Sbalrog } 29224859b68Sbalrog } 29324859b68Sbalrog return 0; 29424859b68Sbalrog 29524859b68Sbalrog case MP_ETH_ICR: 29624859b68Sbalrog return s->icr; 29724859b68Sbalrog 29824859b68Sbalrog case MP_ETH_IMR: 29924859b68Sbalrog return s->imr; 30024859b68Sbalrog 30124859b68Sbalrog case MP_ETH_FRDP0 ... MP_ETH_FRDP3: 302930c8682Spbrook return s->frx_queue[(offset - MP_ETH_FRDP0)/4]; 30324859b68Sbalrog 30424859b68Sbalrog case MP_ETH_CRDP0 ... MP_ETH_CRDP3: 305930c8682Spbrook return s->rx_queue[(offset - MP_ETH_CRDP0)/4]; 30624859b68Sbalrog 307cf143ad3SPeter Maydell case MP_ETH_CTDP0 ... MP_ETH_CTDP1: 308930c8682Spbrook return s->tx_queue[(offset - MP_ETH_CTDP0)/4]; 30924859b68Sbalrog 31024859b68Sbalrog default: 31124859b68Sbalrog return 0; 31224859b68Sbalrog } 31324859b68Sbalrog } 31424859b68Sbalrog 315a8170e5eSAvi Kivity static void mv88w8618_eth_write(void *opaque, hwaddr offset, 31619b4a424SAvi Kivity uint64_t value, unsigned size) 31724859b68Sbalrog { 31824859b68Sbalrog mv88w8618_eth_state *s = opaque; 31924859b68Sbalrog 32024859b68Sbalrog switch (offset) { 32124859b68Sbalrog case MP_ETH_SMIR: 32224859b68Sbalrog s->smir = value; 32324859b68Sbalrog break; 32424859b68Sbalrog 32524859b68Sbalrog case MP_ETH_PCXR: 32624859b68Sbalrog s->vlan_header = ((value >> MP_ETH_PCXR_2BSM_BIT) & 1) * 2; 32724859b68Sbalrog break; 32824859b68Sbalrog 32924859b68Sbalrog case MP_ETH_SDCMR: 33049fedd0dSJan Kiszka if (value & MP_ETH_CMD_TXHI) { 33124859b68Sbalrog eth_send(s, 1); 33249fedd0dSJan Kiszka } 33349fedd0dSJan Kiszka if (value & MP_ETH_CMD_TXLO) { 33424859b68Sbalrog eth_send(s, 0); 33549fedd0dSJan Kiszka } 33649fedd0dSJan Kiszka if (value & (MP_ETH_CMD_TXHI | MP_ETH_CMD_TXLO) && s->icr & s->imr) { 33724859b68Sbalrog qemu_irq_raise(s->irq); 33849fedd0dSJan Kiszka } 33924859b68Sbalrog break; 34024859b68Sbalrog 34124859b68Sbalrog case MP_ETH_ICR: 34224859b68Sbalrog s->icr &= value; 34324859b68Sbalrog break; 34424859b68Sbalrog 34524859b68Sbalrog case MP_ETH_IMR: 34624859b68Sbalrog s->imr = value; 34749fedd0dSJan Kiszka if (s->icr & s->imr) { 34824859b68Sbalrog qemu_irq_raise(s->irq); 34949fedd0dSJan Kiszka } 35024859b68Sbalrog break; 35124859b68Sbalrog 35224859b68Sbalrog case MP_ETH_FRDP0 ... MP_ETH_FRDP3: 353930c8682Spbrook s->frx_queue[(offset - MP_ETH_FRDP0)/4] = value; 35424859b68Sbalrog break; 35524859b68Sbalrog 35624859b68Sbalrog case MP_ETH_CRDP0 ... MP_ETH_CRDP3: 35724859b68Sbalrog s->rx_queue[(offset - MP_ETH_CRDP0)/4] = 358930c8682Spbrook s->cur_rx[(offset - MP_ETH_CRDP0)/4] = value; 35924859b68Sbalrog break; 36024859b68Sbalrog 361cf143ad3SPeter Maydell case MP_ETH_CTDP0 ... MP_ETH_CTDP1: 362930c8682Spbrook s->tx_queue[(offset - MP_ETH_CTDP0)/4] = value; 36324859b68Sbalrog break; 36424859b68Sbalrog } 36524859b68Sbalrog } 36624859b68Sbalrog 36719b4a424SAvi Kivity static const MemoryRegionOps mv88w8618_eth_ops = { 36819b4a424SAvi Kivity .read = mv88w8618_eth_read, 36919b4a424SAvi Kivity .write = mv88w8618_eth_write, 37019b4a424SAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 37124859b68Sbalrog }; 37224859b68Sbalrog 3734e68f7a0SStefan Hajnoczi static void eth_cleanup(NetClientState *nc) 374b946a153Saliguori { 375cc1f0f45SJason Wang mv88w8618_eth_state *s = qemu_get_nic_opaque(nc); 376b946a153Saliguori 3773a94dd18SMark McLoughlin s->nic = NULL; 378b946a153Saliguori } 379b946a153Saliguori 3803a94dd18SMark McLoughlin static NetClientInfo net_mv88w8618_info = { 3812be64a68SLaszlo Ersek .type = NET_CLIENT_OPTIONS_KIND_NIC, 3823a94dd18SMark McLoughlin .size = sizeof(NICState), 3833a94dd18SMark McLoughlin .receive = eth_receive, 3843a94dd18SMark McLoughlin .cleanup = eth_cleanup, 3853a94dd18SMark McLoughlin }; 3863a94dd18SMark McLoughlin 387a77d90e6SAndreas Färber static int mv88w8618_eth_init(SysBusDevice *sbd) 38824859b68Sbalrog { 389a77d90e6SAndreas Färber DeviceState *dev = DEVICE(sbd); 390a77d90e6SAndreas Färber mv88w8618_eth_state *s = MV88W8618_ETH(dev); 39124859b68Sbalrog 392a77d90e6SAndreas Färber sysbus_init_irq(sbd, &s->irq); 3933a94dd18SMark McLoughlin s->nic = qemu_new_nic(&net_mv88w8618_info, &s->conf, 394a77d90e6SAndreas Färber object_get_typename(OBJECT(dev)), dev->id, s); 39564bde0f3SPaolo Bonzini memory_region_init_io(&s->iomem, OBJECT(s), &mv88w8618_eth_ops, s, 39664bde0f3SPaolo Bonzini "mv88w8618-eth", MP_ETH_SIZE); 397a77d90e6SAndreas Färber sysbus_init_mmio(sbd, &s->iomem); 39881a322d4SGerd Hoffmann return 0; 39924859b68Sbalrog } 40024859b68Sbalrog 401d5b61dddSJan Kiszka static const VMStateDescription mv88w8618_eth_vmsd = { 402d5b61dddSJan Kiszka .name = "mv88w8618_eth", 403d5b61dddSJan Kiszka .version_id = 1, 404d5b61dddSJan Kiszka .minimum_version_id = 1, 405d5b61dddSJan Kiszka .fields = (VMStateField[]) { 406d5b61dddSJan Kiszka VMSTATE_UINT32(smir, mv88w8618_eth_state), 407d5b61dddSJan Kiszka VMSTATE_UINT32(icr, mv88w8618_eth_state), 408d5b61dddSJan Kiszka VMSTATE_UINT32(imr, mv88w8618_eth_state), 409d5b61dddSJan Kiszka VMSTATE_UINT32(vlan_header, mv88w8618_eth_state), 410d5b61dddSJan Kiszka VMSTATE_UINT32_ARRAY(tx_queue, mv88w8618_eth_state, 2), 411d5b61dddSJan Kiszka VMSTATE_UINT32_ARRAY(rx_queue, mv88w8618_eth_state, 4), 412d5b61dddSJan Kiszka VMSTATE_UINT32_ARRAY(frx_queue, mv88w8618_eth_state, 4), 413d5b61dddSJan Kiszka VMSTATE_UINT32_ARRAY(cur_rx, mv88w8618_eth_state, 4), 414d5b61dddSJan Kiszka VMSTATE_END_OF_LIST() 415d5b61dddSJan Kiszka } 416d5b61dddSJan Kiszka }; 417d5b61dddSJan Kiszka 418999e12bbSAnthony Liguori static Property mv88w8618_eth_properties[] = { 4194c91cd28SGerd Hoffmann DEFINE_NIC_PROPERTIES(mv88w8618_eth_state, conf), 4204c91cd28SGerd Hoffmann DEFINE_PROP_END_OF_LIST(), 421999e12bbSAnthony Liguori }; 422999e12bbSAnthony Liguori 423999e12bbSAnthony Liguori static void mv88w8618_eth_class_init(ObjectClass *klass, void *data) 424999e12bbSAnthony Liguori { 42539bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 426999e12bbSAnthony Liguori SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); 427999e12bbSAnthony Liguori 428999e12bbSAnthony Liguori k->init = mv88w8618_eth_init; 42939bffca2SAnthony Liguori dc->vmsd = &mv88w8618_eth_vmsd; 43039bffca2SAnthony Liguori dc->props = mv88w8618_eth_properties; 431999e12bbSAnthony Liguori } 432999e12bbSAnthony Liguori 4338c43a6f0SAndreas Färber static const TypeInfo mv88w8618_eth_info = { 434a77d90e6SAndreas Färber .name = TYPE_MV88W8618_ETH, 43539bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 43639bffca2SAnthony Liguori .instance_size = sizeof(mv88w8618_eth_state), 437999e12bbSAnthony Liguori .class_init = mv88w8618_eth_class_init, 438d5b61dddSJan Kiszka }; 439d5b61dddSJan Kiszka 44024859b68Sbalrog /* LCD register offsets */ 44124859b68Sbalrog #define MP_LCD_IRQCTRL 0x180 44224859b68Sbalrog #define MP_LCD_IRQSTAT 0x184 44324859b68Sbalrog #define MP_LCD_SPICTRL 0x1ac 44424859b68Sbalrog #define MP_LCD_INST 0x1bc 44524859b68Sbalrog #define MP_LCD_DATA 0x1c0 44624859b68Sbalrog 44724859b68Sbalrog /* Mode magics */ 44824859b68Sbalrog #define MP_LCD_SPI_DATA 0x00100011 44924859b68Sbalrog #define MP_LCD_SPI_CMD 0x00104011 45024859b68Sbalrog #define MP_LCD_SPI_INVALID 0x00000000 45124859b68Sbalrog 45224859b68Sbalrog /* Commmands */ 45324859b68Sbalrog #define MP_LCD_INST_SETPAGE0 0xB0 45424859b68Sbalrog /* ... */ 45524859b68Sbalrog #define MP_LCD_INST_SETPAGE7 0xB7 45624859b68Sbalrog 45724859b68Sbalrog #define MP_LCD_TEXTCOLOR 0xe0e0ff /* RRGGBB */ 45824859b68Sbalrog 4592cca58fdSAndreas Färber #define TYPE_MUSICPAL_LCD "musicpal_lcd" 4602cca58fdSAndreas Färber #define MUSICPAL_LCD(obj) \ 4612cca58fdSAndreas Färber OBJECT_CHECK(musicpal_lcd_state, (obj), TYPE_MUSICPAL_LCD) 4622cca58fdSAndreas Färber 46324859b68Sbalrog typedef struct musicpal_lcd_state { 4642cca58fdSAndreas Färber /*< private >*/ 4652cca58fdSAndreas Färber SysBusDevice parent_obj; 4662cca58fdSAndreas Färber /*< public >*/ 4672cca58fdSAndreas Färber 46819b4a424SAvi Kivity MemoryRegion iomem; 469343ec8e4SBenoit Canet uint32_t brightness; 47024859b68Sbalrog uint32_t mode; 47124859b68Sbalrog uint32_t irqctrl; 472d5b61dddSJan Kiszka uint32_t page; 473d5b61dddSJan Kiszka uint32_t page_off; 474c78f7137SGerd Hoffmann QemuConsole *con; 47524859b68Sbalrog uint8_t video_ram[128*64/8]; 47624859b68Sbalrog } musicpal_lcd_state; 47724859b68Sbalrog 478343ec8e4SBenoit Canet static uint8_t scale_lcd_color(musicpal_lcd_state *s, uint8_t col) 47924859b68Sbalrog { 480343ec8e4SBenoit Canet switch (s->brightness) { 481343ec8e4SBenoit Canet case 7: 48224859b68Sbalrog return col; 483343ec8e4SBenoit Canet case 0: 484343ec8e4SBenoit Canet return 0; 485343ec8e4SBenoit Canet default: 486343ec8e4SBenoit Canet return (col * s->brightness) / 7; 48724859b68Sbalrog } 48824859b68Sbalrog } 48924859b68Sbalrog 4900266f2c7Sbalrog #define SET_LCD_PIXEL(depth, type) \ 4910266f2c7Sbalrog static inline void glue(set_lcd_pixel, depth) \ 4920266f2c7Sbalrog (musicpal_lcd_state *s, int x, int y, type col) \ 4930266f2c7Sbalrog { \ 4940266f2c7Sbalrog int dx, dy; \ 495c78f7137SGerd Hoffmann DisplaySurface *surface = qemu_console_surface(s->con); \ 496c78f7137SGerd Hoffmann type *pixel = &((type *) surface_data(surface))[(y * 128 * 3 + x) * 3]; \ 4970266f2c7Sbalrog \ 4980266f2c7Sbalrog for (dy = 0; dy < 3; dy++, pixel += 127 * 3) \ 4990266f2c7Sbalrog for (dx = 0; dx < 3; dx++, pixel++) \ 5000266f2c7Sbalrog *pixel = col; \ 5010266f2c7Sbalrog } 5020266f2c7Sbalrog SET_LCD_PIXEL(8, uint8_t) 5030266f2c7Sbalrog SET_LCD_PIXEL(16, uint16_t) 5040266f2c7Sbalrog SET_LCD_PIXEL(32, uint32_t) 50524859b68Sbalrog 50624859b68Sbalrog static void lcd_refresh(void *opaque) 50724859b68Sbalrog { 50824859b68Sbalrog musicpal_lcd_state *s = opaque; 509c78f7137SGerd Hoffmann DisplaySurface *surface = qemu_console_surface(s->con); 5100266f2c7Sbalrog int x, y, col; 51124859b68Sbalrog 512c78f7137SGerd Hoffmann switch (surface_bits_per_pixel(surface)) { 5130266f2c7Sbalrog case 0: 5140266f2c7Sbalrog return; 5150266f2c7Sbalrog #define LCD_REFRESH(depth, func) \ 5160266f2c7Sbalrog case depth: \ 517343ec8e4SBenoit Canet col = func(scale_lcd_color(s, (MP_LCD_TEXTCOLOR >> 16) & 0xff), \ 518343ec8e4SBenoit Canet scale_lcd_color(s, (MP_LCD_TEXTCOLOR >> 8) & 0xff), \ 519343ec8e4SBenoit Canet scale_lcd_color(s, MP_LCD_TEXTCOLOR & 0xff)); \ 52049fedd0dSJan Kiszka for (x = 0; x < 128; x++) { \ 52149fedd0dSJan Kiszka for (y = 0; y < 64; y++) { \ 52249fedd0dSJan Kiszka if (s->video_ram[x + (y/8)*128] & (1 << (y % 8))) { \ 5230266f2c7Sbalrog glue(set_lcd_pixel, depth)(s, x, y, col); \ 52449fedd0dSJan Kiszka } else { \ 5250266f2c7Sbalrog glue(set_lcd_pixel, depth)(s, x, y, 0); \ 52649fedd0dSJan Kiszka } \ 52749fedd0dSJan Kiszka } \ 52849fedd0dSJan Kiszka } \ 5290266f2c7Sbalrog break; 5300266f2c7Sbalrog LCD_REFRESH(8, rgb_to_pixel8) 5310266f2c7Sbalrog LCD_REFRESH(16, rgb_to_pixel16) 532c78f7137SGerd Hoffmann LCD_REFRESH(32, (is_surface_bgr(surface) ? 533bf9b48afSaliguori rgb_to_pixel32bgr : rgb_to_pixel32)) 5340266f2c7Sbalrog default: 5352ac71179SPaul Brook hw_error("unsupported colour depth %i\n", 536c78f7137SGerd Hoffmann surface_bits_per_pixel(surface)); 5370266f2c7Sbalrog } 53824859b68Sbalrog 539c78f7137SGerd Hoffmann dpy_gfx_update(s->con, 0, 0, 128*3, 64*3); 54024859b68Sbalrog } 54124859b68Sbalrog 542167bc3d2Sbalrog static void lcd_invalidate(void *opaque) 543167bc3d2Sbalrog { 544167bc3d2Sbalrog } 545167bc3d2Sbalrog 5462c79fed3SStefan Weil static void musicpal_lcd_gpio_brightness_in(void *opaque, int irq, int level) 547343ec8e4SBenoit Canet { 548243cd13cSJan Kiszka musicpal_lcd_state *s = opaque; 549343ec8e4SBenoit Canet s->brightness &= ~(1 << irq); 550343ec8e4SBenoit Canet s->brightness |= level << irq; 551343ec8e4SBenoit Canet } 552343ec8e4SBenoit Canet 553a8170e5eSAvi Kivity static uint64_t musicpal_lcd_read(void *opaque, hwaddr offset, 55419b4a424SAvi Kivity unsigned size) 55524859b68Sbalrog { 55624859b68Sbalrog musicpal_lcd_state *s = opaque; 55724859b68Sbalrog 55824859b68Sbalrog switch (offset) { 55924859b68Sbalrog case MP_LCD_IRQCTRL: 56024859b68Sbalrog return s->irqctrl; 56124859b68Sbalrog 56224859b68Sbalrog default: 56324859b68Sbalrog return 0; 56424859b68Sbalrog } 56524859b68Sbalrog } 56624859b68Sbalrog 567a8170e5eSAvi Kivity static void musicpal_lcd_write(void *opaque, hwaddr offset, 56819b4a424SAvi Kivity uint64_t value, unsigned size) 56924859b68Sbalrog { 57024859b68Sbalrog musicpal_lcd_state *s = opaque; 57124859b68Sbalrog 57224859b68Sbalrog switch (offset) { 57324859b68Sbalrog case MP_LCD_IRQCTRL: 57424859b68Sbalrog s->irqctrl = value; 57524859b68Sbalrog break; 57624859b68Sbalrog 57724859b68Sbalrog case MP_LCD_SPICTRL: 57849fedd0dSJan Kiszka if (value == MP_LCD_SPI_DATA || value == MP_LCD_SPI_CMD) { 57924859b68Sbalrog s->mode = value; 58049fedd0dSJan Kiszka } else { 58124859b68Sbalrog s->mode = MP_LCD_SPI_INVALID; 58249fedd0dSJan Kiszka } 58324859b68Sbalrog break; 58424859b68Sbalrog 58524859b68Sbalrog case MP_LCD_INST: 58624859b68Sbalrog if (value >= MP_LCD_INST_SETPAGE0 && value <= MP_LCD_INST_SETPAGE7) { 58724859b68Sbalrog s->page = value - MP_LCD_INST_SETPAGE0; 58824859b68Sbalrog s->page_off = 0; 58924859b68Sbalrog } 59024859b68Sbalrog break; 59124859b68Sbalrog 59224859b68Sbalrog case MP_LCD_DATA: 59324859b68Sbalrog if (s->mode == MP_LCD_SPI_CMD) { 59424859b68Sbalrog if (value >= MP_LCD_INST_SETPAGE0 && 59524859b68Sbalrog value <= MP_LCD_INST_SETPAGE7) { 59624859b68Sbalrog s->page = value - MP_LCD_INST_SETPAGE0; 59724859b68Sbalrog s->page_off = 0; 59824859b68Sbalrog } 59924859b68Sbalrog } else if (s->mode == MP_LCD_SPI_DATA) { 60024859b68Sbalrog s->video_ram[s->page*128 + s->page_off] = value; 60124859b68Sbalrog s->page_off = (s->page_off + 1) & 127; 60224859b68Sbalrog } 60324859b68Sbalrog break; 60424859b68Sbalrog } 60524859b68Sbalrog } 60624859b68Sbalrog 60719b4a424SAvi Kivity static const MemoryRegionOps musicpal_lcd_ops = { 60819b4a424SAvi Kivity .read = musicpal_lcd_read, 60919b4a424SAvi Kivity .write = musicpal_lcd_write, 61019b4a424SAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 61124859b68Sbalrog }; 61224859b68Sbalrog 613380cd056SGerd Hoffmann static const GraphicHwOps musicpal_gfx_ops = { 614380cd056SGerd Hoffmann .invalidate = lcd_invalidate, 615380cd056SGerd Hoffmann .gfx_update = lcd_refresh, 616380cd056SGerd Hoffmann }; 617380cd056SGerd Hoffmann 6182cca58fdSAndreas Färber static int musicpal_lcd_init(SysBusDevice *sbd) 61924859b68Sbalrog { 6202cca58fdSAndreas Färber DeviceState *dev = DEVICE(sbd); 6212cca58fdSAndreas Färber musicpal_lcd_state *s = MUSICPAL_LCD(dev); 62224859b68Sbalrog 623343ec8e4SBenoit Canet s->brightness = 7; 624343ec8e4SBenoit Canet 62564bde0f3SPaolo Bonzini memory_region_init_io(&s->iomem, OBJECT(s), &musicpal_lcd_ops, s, 62619b4a424SAvi Kivity "musicpal-lcd", MP_LCD_SIZE); 6272cca58fdSAndreas Färber sysbus_init_mmio(sbd, &s->iomem); 62824859b68Sbalrog 6295643706aSGerd Hoffmann s->con = graphic_console_init(dev, 0, &musicpal_gfx_ops, s); 630c78f7137SGerd Hoffmann qemu_console_resize(s->con, 128*3, 64*3); 631343ec8e4SBenoit Canet 6322cca58fdSAndreas Färber qdev_init_gpio_in(dev, musicpal_lcd_gpio_brightness_in, 3); 63381a322d4SGerd Hoffmann 63481a322d4SGerd Hoffmann return 0; 63524859b68Sbalrog } 63624859b68Sbalrog 637d5b61dddSJan Kiszka static const VMStateDescription musicpal_lcd_vmsd = { 638d5b61dddSJan Kiszka .name = "musicpal_lcd", 639d5b61dddSJan Kiszka .version_id = 1, 640d5b61dddSJan Kiszka .minimum_version_id = 1, 641d5b61dddSJan Kiszka .fields = (VMStateField[]) { 642d5b61dddSJan Kiszka VMSTATE_UINT32(brightness, musicpal_lcd_state), 643d5b61dddSJan Kiszka VMSTATE_UINT32(mode, musicpal_lcd_state), 644d5b61dddSJan Kiszka VMSTATE_UINT32(irqctrl, musicpal_lcd_state), 645d5b61dddSJan Kiszka VMSTATE_UINT32(page, musicpal_lcd_state), 646d5b61dddSJan Kiszka VMSTATE_UINT32(page_off, musicpal_lcd_state), 647d5b61dddSJan Kiszka VMSTATE_BUFFER(video_ram, musicpal_lcd_state), 648d5b61dddSJan Kiszka VMSTATE_END_OF_LIST() 649d5b61dddSJan Kiszka } 650d5b61dddSJan Kiszka }; 651d5b61dddSJan Kiszka 652999e12bbSAnthony Liguori static void musicpal_lcd_class_init(ObjectClass *klass, void *data) 653999e12bbSAnthony Liguori { 65439bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 655999e12bbSAnthony Liguori SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); 656999e12bbSAnthony Liguori 657999e12bbSAnthony Liguori k->init = musicpal_lcd_init; 65839bffca2SAnthony Liguori dc->vmsd = &musicpal_lcd_vmsd; 659999e12bbSAnthony Liguori } 660999e12bbSAnthony Liguori 6618c43a6f0SAndreas Färber static const TypeInfo musicpal_lcd_info = { 6622cca58fdSAndreas Färber .name = TYPE_MUSICPAL_LCD, 66339bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 66439bffca2SAnthony Liguori .instance_size = sizeof(musicpal_lcd_state), 665999e12bbSAnthony Liguori .class_init = musicpal_lcd_class_init, 666d5b61dddSJan Kiszka }; 667d5b61dddSJan Kiszka 66824859b68Sbalrog /* PIC register offsets */ 66924859b68Sbalrog #define MP_PIC_STATUS 0x00 67024859b68Sbalrog #define MP_PIC_ENABLE_SET 0x08 67124859b68Sbalrog #define MP_PIC_ENABLE_CLR 0x0C 67224859b68Sbalrog 673c7bd0fd9SAndreas Färber #define TYPE_MV88W8618_PIC "mv88w8618_pic" 674c7bd0fd9SAndreas Färber #define MV88W8618_PIC(obj) \ 675c7bd0fd9SAndreas Färber OBJECT_CHECK(mv88w8618_pic_state, (obj), TYPE_MV88W8618_PIC) 676c7bd0fd9SAndreas Färber 677c7bd0fd9SAndreas Färber typedef struct mv88w8618_pic_state { 678c7bd0fd9SAndreas Färber /*< private >*/ 679c7bd0fd9SAndreas Färber SysBusDevice parent_obj; 680c7bd0fd9SAndreas Färber /*< public >*/ 681c7bd0fd9SAndreas Färber 68219b4a424SAvi Kivity MemoryRegion iomem; 68324859b68Sbalrog uint32_t level; 68424859b68Sbalrog uint32_t enabled; 68524859b68Sbalrog qemu_irq parent_irq; 68624859b68Sbalrog } mv88w8618_pic_state; 68724859b68Sbalrog 68824859b68Sbalrog static void mv88w8618_pic_update(mv88w8618_pic_state *s) 68924859b68Sbalrog { 69024859b68Sbalrog qemu_set_irq(s->parent_irq, (s->level & s->enabled)); 69124859b68Sbalrog } 69224859b68Sbalrog 69324859b68Sbalrog static void mv88w8618_pic_set_irq(void *opaque, int irq, int level) 69424859b68Sbalrog { 69524859b68Sbalrog mv88w8618_pic_state *s = opaque; 69624859b68Sbalrog 69749fedd0dSJan Kiszka if (level) { 69824859b68Sbalrog s->level |= 1 << irq; 69949fedd0dSJan Kiszka } else { 70024859b68Sbalrog s->level &= ~(1 << irq); 70149fedd0dSJan Kiszka } 70224859b68Sbalrog mv88w8618_pic_update(s); 70324859b68Sbalrog } 70424859b68Sbalrog 705a8170e5eSAvi Kivity static uint64_t mv88w8618_pic_read(void *opaque, hwaddr offset, 70619b4a424SAvi Kivity unsigned size) 70724859b68Sbalrog { 70824859b68Sbalrog mv88w8618_pic_state *s = opaque; 70924859b68Sbalrog 71024859b68Sbalrog switch (offset) { 71124859b68Sbalrog case MP_PIC_STATUS: 71224859b68Sbalrog return s->level & s->enabled; 71324859b68Sbalrog 71424859b68Sbalrog default: 71524859b68Sbalrog return 0; 71624859b68Sbalrog } 71724859b68Sbalrog } 71824859b68Sbalrog 719a8170e5eSAvi Kivity static void mv88w8618_pic_write(void *opaque, hwaddr offset, 72019b4a424SAvi Kivity uint64_t value, unsigned size) 72124859b68Sbalrog { 72224859b68Sbalrog mv88w8618_pic_state *s = opaque; 72324859b68Sbalrog 72424859b68Sbalrog switch (offset) { 72524859b68Sbalrog case MP_PIC_ENABLE_SET: 72624859b68Sbalrog s->enabled |= value; 72724859b68Sbalrog break; 72824859b68Sbalrog 72924859b68Sbalrog case MP_PIC_ENABLE_CLR: 73024859b68Sbalrog s->enabled &= ~value; 73124859b68Sbalrog s->level &= ~value; 73224859b68Sbalrog break; 73324859b68Sbalrog } 73424859b68Sbalrog mv88w8618_pic_update(s); 73524859b68Sbalrog } 73624859b68Sbalrog 737d5b61dddSJan Kiszka static void mv88w8618_pic_reset(DeviceState *d) 73824859b68Sbalrog { 739c7bd0fd9SAndreas Färber mv88w8618_pic_state *s = MV88W8618_PIC(d); 74024859b68Sbalrog 74124859b68Sbalrog s->level = 0; 74224859b68Sbalrog s->enabled = 0; 74324859b68Sbalrog } 74424859b68Sbalrog 74519b4a424SAvi Kivity static const MemoryRegionOps mv88w8618_pic_ops = { 74619b4a424SAvi Kivity .read = mv88w8618_pic_read, 74719b4a424SAvi Kivity .write = mv88w8618_pic_write, 74819b4a424SAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 74924859b68Sbalrog }; 75024859b68Sbalrog 75181a322d4SGerd Hoffmann static int mv88w8618_pic_init(SysBusDevice *dev) 75224859b68Sbalrog { 753c7bd0fd9SAndreas Färber mv88w8618_pic_state *s = MV88W8618_PIC(dev); 75424859b68Sbalrog 755c7bd0fd9SAndreas Färber qdev_init_gpio_in(DEVICE(dev), mv88w8618_pic_set_irq, 32); 756b47b50faSPaul Brook sysbus_init_irq(dev, &s->parent_irq); 75764bde0f3SPaolo Bonzini memory_region_init_io(&s->iomem, OBJECT(s), &mv88w8618_pic_ops, s, 75819b4a424SAvi Kivity "musicpal-pic", MP_PIC_SIZE); 759750ecd44SAvi Kivity sysbus_init_mmio(dev, &s->iomem); 76081a322d4SGerd Hoffmann return 0; 76124859b68Sbalrog } 76224859b68Sbalrog 763d5b61dddSJan Kiszka static const VMStateDescription mv88w8618_pic_vmsd = { 764d5b61dddSJan Kiszka .name = "mv88w8618_pic", 765d5b61dddSJan Kiszka .version_id = 1, 766d5b61dddSJan Kiszka .minimum_version_id = 1, 767d5b61dddSJan Kiszka .fields = (VMStateField[]) { 768d5b61dddSJan Kiszka VMSTATE_UINT32(level, mv88w8618_pic_state), 769d5b61dddSJan Kiszka VMSTATE_UINT32(enabled, mv88w8618_pic_state), 770d5b61dddSJan Kiszka VMSTATE_END_OF_LIST() 771d5b61dddSJan Kiszka } 772d5b61dddSJan Kiszka }; 773d5b61dddSJan Kiszka 774999e12bbSAnthony Liguori static void mv88w8618_pic_class_init(ObjectClass *klass, void *data) 775999e12bbSAnthony Liguori { 77639bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 777999e12bbSAnthony Liguori SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); 778999e12bbSAnthony Liguori 779999e12bbSAnthony Liguori k->init = mv88w8618_pic_init; 78039bffca2SAnthony Liguori dc->reset = mv88w8618_pic_reset; 78139bffca2SAnthony Liguori dc->vmsd = &mv88w8618_pic_vmsd; 782999e12bbSAnthony Liguori } 783999e12bbSAnthony Liguori 7848c43a6f0SAndreas Färber static const TypeInfo mv88w8618_pic_info = { 785c7bd0fd9SAndreas Färber .name = TYPE_MV88W8618_PIC, 78639bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 78739bffca2SAnthony Liguori .instance_size = sizeof(mv88w8618_pic_state), 788999e12bbSAnthony Liguori .class_init = mv88w8618_pic_class_init, 789d5b61dddSJan Kiszka }; 790d5b61dddSJan Kiszka 79124859b68Sbalrog /* PIT register offsets */ 79224859b68Sbalrog #define MP_PIT_TIMER1_LENGTH 0x00 79324859b68Sbalrog /* ... */ 79424859b68Sbalrog #define MP_PIT_TIMER4_LENGTH 0x0C 79524859b68Sbalrog #define MP_PIT_CONTROL 0x10 79624859b68Sbalrog #define MP_PIT_TIMER1_VALUE 0x14 79724859b68Sbalrog /* ... */ 79824859b68Sbalrog #define MP_PIT_TIMER4_VALUE 0x20 79924859b68Sbalrog #define MP_BOARD_RESET 0x34 80024859b68Sbalrog 80124859b68Sbalrog /* Magic board reset value (probably some watchdog behind it) */ 80224859b68Sbalrog #define MP_BOARD_RESET_MAGIC 0x10000 80324859b68Sbalrog 80424859b68Sbalrog typedef struct mv88w8618_timer_state { 805b47b50faSPaul Brook ptimer_state *ptimer; 80624859b68Sbalrog uint32_t limit; 80724859b68Sbalrog int freq; 80824859b68Sbalrog qemu_irq irq; 80924859b68Sbalrog } mv88w8618_timer_state; 81024859b68Sbalrog 8114adc8541SAndreas Färber #define TYPE_MV88W8618_PIT "mv88w8618_pit" 8124adc8541SAndreas Färber #define MV88W8618_PIT(obj) \ 8134adc8541SAndreas Färber OBJECT_CHECK(mv88w8618_pit_state, (obj), TYPE_MV88W8618_PIT) 8144adc8541SAndreas Färber 81524859b68Sbalrog typedef struct mv88w8618_pit_state { 8164adc8541SAndreas Färber /*< private >*/ 8174adc8541SAndreas Färber SysBusDevice parent_obj; 8184adc8541SAndreas Färber /*< public >*/ 8194adc8541SAndreas Färber 82019b4a424SAvi Kivity MemoryRegion iomem; 821b47b50faSPaul Brook mv88w8618_timer_state timer[4]; 82224859b68Sbalrog } mv88w8618_pit_state; 82324859b68Sbalrog 82424859b68Sbalrog static void mv88w8618_timer_tick(void *opaque) 82524859b68Sbalrog { 82624859b68Sbalrog mv88w8618_timer_state *s = opaque; 82724859b68Sbalrog 82824859b68Sbalrog qemu_irq_raise(s->irq); 82924859b68Sbalrog } 83024859b68Sbalrog 831b47b50faSPaul Brook static void mv88w8618_timer_init(SysBusDevice *dev, mv88w8618_timer_state *s, 832b47b50faSPaul Brook uint32_t freq) 83324859b68Sbalrog { 83424859b68Sbalrog QEMUBH *bh; 83524859b68Sbalrog 836b47b50faSPaul Brook sysbus_init_irq(dev, &s->irq); 83724859b68Sbalrog s->freq = freq; 83824859b68Sbalrog 83924859b68Sbalrog bh = qemu_bh_new(mv88w8618_timer_tick, s); 840b47b50faSPaul Brook s->ptimer = ptimer_init(bh); 84124859b68Sbalrog } 84224859b68Sbalrog 843a8170e5eSAvi Kivity static uint64_t mv88w8618_pit_read(void *opaque, hwaddr offset, 84419b4a424SAvi Kivity unsigned size) 84524859b68Sbalrog { 84624859b68Sbalrog mv88w8618_pit_state *s = opaque; 84724859b68Sbalrog mv88w8618_timer_state *t; 84824859b68Sbalrog 84924859b68Sbalrog switch (offset) { 85024859b68Sbalrog case MP_PIT_TIMER1_VALUE ... MP_PIT_TIMER4_VALUE: 851b47b50faSPaul Brook t = &s->timer[(offset-MP_PIT_TIMER1_VALUE) >> 2]; 852b47b50faSPaul Brook return ptimer_get_count(t->ptimer); 85324859b68Sbalrog 85424859b68Sbalrog default: 85524859b68Sbalrog return 0; 85624859b68Sbalrog } 85724859b68Sbalrog } 85824859b68Sbalrog 859a8170e5eSAvi Kivity static void mv88w8618_pit_write(void *opaque, hwaddr offset, 86019b4a424SAvi Kivity uint64_t value, unsigned size) 86124859b68Sbalrog { 86224859b68Sbalrog mv88w8618_pit_state *s = opaque; 86324859b68Sbalrog mv88w8618_timer_state *t; 86424859b68Sbalrog int i; 86524859b68Sbalrog 86624859b68Sbalrog switch (offset) { 86724859b68Sbalrog case MP_PIT_TIMER1_LENGTH ... MP_PIT_TIMER4_LENGTH: 868b47b50faSPaul Brook t = &s->timer[offset >> 2]; 86924859b68Sbalrog t->limit = value; 870c88d6bdeSJan Kiszka if (t->limit > 0) { 871b47b50faSPaul Brook ptimer_set_limit(t->ptimer, t->limit, 1); 872c88d6bdeSJan Kiszka } else { 873c88d6bdeSJan Kiszka ptimer_stop(t->ptimer); 874c88d6bdeSJan Kiszka } 87524859b68Sbalrog break; 87624859b68Sbalrog 87724859b68Sbalrog case MP_PIT_CONTROL: 87824859b68Sbalrog for (i = 0; i < 4; i++) { 879b47b50faSPaul Brook t = &s->timer[i]; 880c88d6bdeSJan Kiszka if (value & 0xf && t->limit > 0) { 881b47b50faSPaul Brook ptimer_set_limit(t->ptimer, t->limit, 0); 882b47b50faSPaul Brook ptimer_set_freq(t->ptimer, t->freq); 883b47b50faSPaul Brook ptimer_run(t->ptimer, 0); 884c88d6bdeSJan Kiszka } else { 885c88d6bdeSJan Kiszka ptimer_stop(t->ptimer); 88624859b68Sbalrog } 88724859b68Sbalrog value >>= 4; 88824859b68Sbalrog } 88924859b68Sbalrog break; 89024859b68Sbalrog 89124859b68Sbalrog case MP_BOARD_RESET: 89249fedd0dSJan Kiszka if (value == MP_BOARD_RESET_MAGIC) { 89324859b68Sbalrog qemu_system_reset_request(); 89449fedd0dSJan Kiszka } 89524859b68Sbalrog break; 89624859b68Sbalrog } 89724859b68Sbalrog } 89824859b68Sbalrog 899d5b61dddSJan Kiszka static void mv88w8618_pit_reset(DeviceState *d) 900c88d6bdeSJan Kiszka { 9014adc8541SAndreas Färber mv88w8618_pit_state *s = MV88W8618_PIT(d); 902c88d6bdeSJan Kiszka int i; 903c88d6bdeSJan Kiszka 904c88d6bdeSJan Kiszka for (i = 0; i < 4; i++) { 905c88d6bdeSJan Kiszka ptimer_stop(s->timer[i].ptimer); 906c88d6bdeSJan Kiszka s->timer[i].limit = 0; 907c88d6bdeSJan Kiszka } 908c88d6bdeSJan Kiszka } 909c88d6bdeSJan Kiszka 91019b4a424SAvi Kivity static const MemoryRegionOps mv88w8618_pit_ops = { 91119b4a424SAvi Kivity .read = mv88w8618_pit_read, 91219b4a424SAvi Kivity .write = mv88w8618_pit_write, 91319b4a424SAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 91424859b68Sbalrog }; 91524859b68Sbalrog 91681a322d4SGerd Hoffmann static int mv88w8618_pit_init(SysBusDevice *dev) 91724859b68Sbalrog { 9184adc8541SAndreas Färber mv88w8618_pit_state *s = MV88W8618_PIT(dev); 919b47b50faSPaul Brook int i; 92024859b68Sbalrog 92124859b68Sbalrog /* Letting them all run at 1 MHz is likely just a pragmatic 92224859b68Sbalrog * simplification. */ 923b47b50faSPaul Brook for (i = 0; i < 4; i++) { 924b47b50faSPaul Brook mv88w8618_timer_init(dev, &s->timer[i], 1000000); 925b47b50faSPaul Brook } 92624859b68Sbalrog 92764bde0f3SPaolo Bonzini memory_region_init_io(&s->iomem, OBJECT(s), &mv88w8618_pit_ops, s, 92819b4a424SAvi Kivity "musicpal-pit", MP_PIT_SIZE); 929750ecd44SAvi Kivity sysbus_init_mmio(dev, &s->iomem); 93081a322d4SGerd Hoffmann return 0; 93124859b68Sbalrog } 93224859b68Sbalrog 933d5b61dddSJan Kiszka static const VMStateDescription mv88w8618_timer_vmsd = { 934d5b61dddSJan Kiszka .name = "timer", 935d5b61dddSJan Kiszka .version_id = 1, 936d5b61dddSJan Kiszka .minimum_version_id = 1, 937d5b61dddSJan Kiszka .fields = (VMStateField[]) { 938d5b61dddSJan Kiszka VMSTATE_PTIMER(ptimer, mv88w8618_timer_state), 939d5b61dddSJan Kiszka VMSTATE_UINT32(limit, mv88w8618_timer_state), 940d5b61dddSJan Kiszka VMSTATE_END_OF_LIST() 941d5b61dddSJan Kiszka } 942d5b61dddSJan Kiszka }; 943d5b61dddSJan Kiszka 944d5b61dddSJan Kiszka static const VMStateDescription mv88w8618_pit_vmsd = { 945d5b61dddSJan Kiszka .name = "mv88w8618_pit", 946d5b61dddSJan Kiszka .version_id = 1, 947d5b61dddSJan Kiszka .minimum_version_id = 1, 948d5b61dddSJan Kiszka .fields = (VMStateField[]) { 949d5b61dddSJan Kiszka VMSTATE_STRUCT_ARRAY(timer, mv88w8618_pit_state, 4, 1, 950d5b61dddSJan Kiszka mv88w8618_timer_vmsd, mv88w8618_timer_state), 951d5b61dddSJan Kiszka VMSTATE_END_OF_LIST() 952d5b61dddSJan Kiszka } 953d5b61dddSJan Kiszka }; 954d5b61dddSJan Kiszka 955999e12bbSAnthony Liguori static void mv88w8618_pit_class_init(ObjectClass *klass, void *data) 956999e12bbSAnthony Liguori { 95739bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 958999e12bbSAnthony Liguori SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); 959999e12bbSAnthony Liguori 960999e12bbSAnthony Liguori k->init = mv88w8618_pit_init; 96139bffca2SAnthony Liguori dc->reset = mv88w8618_pit_reset; 96239bffca2SAnthony Liguori dc->vmsd = &mv88w8618_pit_vmsd; 963999e12bbSAnthony Liguori } 964999e12bbSAnthony Liguori 9658c43a6f0SAndreas Färber static const TypeInfo mv88w8618_pit_info = { 9664adc8541SAndreas Färber .name = TYPE_MV88W8618_PIT, 96739bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 96839bffca2SAnthony Liguori .instance_size = sizeof(mv88w8618_pit_state), 969999e12bbSAnthony Liguori .class_init = mv88w8618_pit_class_init, 970c88d6bdeSJan Kiszka }; 971c88d6bdeSJan Kiszka 97224859b68Sbalrog /* Flash config register offsets */ 97324859b68Sbalrog #define MP_FLASHCFG_CFGR0 0x04 97424859b68Sbalrog 9755952b01cSAndreas Färber #define TYPE_MV88W8618_FLASHCFG "mv88w8618_flashcfg" 9765952b01cSAndreas Färber #define MV88W8618_FLASHCFG(obj) \ 9775952b01cSAndreas Färber OBJECT_CHECK(mv88w8618_flashcfg_state, (obj), TYPE_MV88W8618_FLASHCFG) 9785952b01cSAndreas Färber 97924859b68Sbalrog typedef struct mv88w8618_flashcfg_state { 9805952b01cSAndreas Färber /*< private >*/ 9815952b01cSAndreas Färber SysBusDevice parent_obj; 9825952b01cSAndreas Färber /*< public >*/ 9835952b01cSAndreas Färber 98419b4a424SAvi Kivity MemoryRegion iomem; 98524859b68Sbalrog uint32_t cfgr0; 98624859b68Sbalrog } mv88w8618_flashcfg_state; 98724859b68Sbalrog 98819b4a424SAvi Kivity static uint64_t mv88w8618_flashcfg_read(void *opaque, 989a8170e5eSAvi Kivity hwaddr offset, 99019b4a424SAvi Kivity unsigned size) 99124859b68Sbalrog { 99224859b68Sbalrog mv88w8618_flashcfg_state *s = opaque; 99324859b68Sbalrog 99424859b68Sbalrog switch (offset) { 99524859b68Sbalrog case MP_FLASHCFG_CFGR0: 99624859b68Sbalrog return s->cfgr0; 99724859b68Sbalrog 99824859b68Sbalrog default: 99924859b68Sbalrog return 0; 100024859b68Sbalrog } 100124859b68Sbalrog } 100224859b68Sbalrog 1003a8170e5eSAvi Kivity static void mv88w8618_flashcfg_write(void *opaque, hwaddr offset, 100419b4a424SAvi Kivity uint64_t value, unsigned size) 100524859b68Sbalrog { 100624859b68Sbalrog mv88w8618_flashcfg_state *s = opaque; 100724859b68Sbalrog 100824859b68Sbalrog switch (offset) { 100924859b68Sbalrog case MP_FLASHCFG_CFGR0: 101024859b68Sbalrog s->cfgr0 = value; 101124859b68Sbalrog break; 101224859b68Sbalrog } 101324859b68Sbalrog } 101424859b68Sbalrog 101519b4a424SAvi Kivity static const MemoryRegionOps mv88w8618_flashcfg_ops = { 101619b4a424SAvi Kivity .read = mv88w8618_flashcfg_read, 101719b4a424SAvi Kivity .write = mv88w8618_flashcfg_write, 101819b4a424SAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 101924859b68Sbalrog }; 102024859b68Sbalrog 102181a322d4SGerd Hoffmann static int mv88w8618_flashcfg_init(SysBusDevice *dev) 102224859b68Sbalrog { 10235952b01cSAndreas Färber mv88w8618_flashcfg_state *s = MV88W8618_FLASHCFG(dev); 102424859b68Sbalrog 102524859b68Sbalrog s->cfgr0 = 0xfffe4285; /* Default as set by U-Boot for 8 MB flash */ 102664bde0f3SPaolo Bonzini memory_region_init_io(&s->iomem, OBJECT(s), &mv88w8618_flashcfg_ops, s, 102719b4a424SAvi Kivity "musicpal-flashcfg", MP_FLASHCFG_SIZE); 1028750ecd44SAvi Kivity sysbus_init_mmio(dev, &s->iomem); 102981a322d4SGerd Hoffmann return 0; 103024859b68Sbalrog } 103124859b68Sbalrog 1032d5b61dddSJan Kiszka static const VMStateDescription mv88w8618_flashcfg_vmsd = { 1033d5b61dddSJan Kiszka .name = "mv88w8618_flashcfg", 1034d5b61dddSJan Kiszka .version_id = 1, 1035d5b61dddSJan Kiszka .minimum_version_id = 1, 1036d5b61dddSJan Kiszka .fields = (VMStateField[]) { 1037d5b61dddSJan Kiszka VMSTATE_UINT32(cfgr0, mv88w8618_flashcfg_state), 1038d5b61dddSJan Kiszka VMSTATE_END_OF_LIST() 1039d5b61dddSJan Kiszka } 1040d5b61dddSJan Kiszka }; 1041d5b61dddSJan Kiszka 1042999e12bbSAnthony Liguori static void mv88w8618_flashcfg_class_init(ObjectClass *klass, void *data) 1043999e12bbSAnthony Liguori { 104439bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 1045999e12bbSAnthony Liguori SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); 1046999e12bbSAnthony Liguori 1047999e12bbSAnthony Liguori k->init = mv88w8618_flashcfg_init; 104839bffca2SAnthony Liguori dc->vmsd = &mv88w8618_flashcfg_vmsd; 1049999e12bbSAnthony Liguori } 1050999e12bbSAnthony Liguori 10518c43a6f0SAndreas Färber static const TypeInfo mv88w8618_flashcfg_info = { 10525952b01cSAndreas Färber .name = TYPE_MV88W8618_FLASHCFG, 105339bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 105439bffca2SAnthony Liguori .instance_size = sizeof(mv88w8618_flashcfg_state), 1055999e12bbSAnthony Liguori .class_init = mv88w8618_flashcfg_class_init, 1056d5b61dddSJan Kiszka }; 1057d5b61dddSJan Kiszka 1058718ec0beSmalc /* Misc register offsets */ 1059718ec0beSmalc #define MP_MISC_BOARD_REVISION 0x18 106024859b68Sbalrog 1061718ec0beSmalc #define MP_BOARD_REVISION 0x31 106224859b68Sbalrog 1063a86f200aSPeter Maydell typedef struct { 1064a86f200aSPeter Maydell SysBusDevice parent_obj; 1065a86f200aSPeter Maydell MemoryRegion iomem; 1066a86f200aSPeter Maydell } MusicPalMiscState; 1067a86f200aSPeter Maydell 1068a86f200aSPeter Maydell #define TYPE_MUSICPAL_MISC "musicpal-misc" 1069a86f200aSPeter Maydell #define MUSICPAL_MISC(obj) \ 1070a86f200aSPeter Maydell OBJECT_CHECK(MusicPalMiscState, (obj), TYPE_MUSICPAL_MISC) 1071a86f200aSPeter Maydell 1072a8170e5eSAvi Kivity static uint64_t musicpal_misc_read(void *opaque, hwaddr offset, 107319b4a424SAvi Kivity unsigned size) 1074718ec0beSmalc { 1075718ec0beSmalc switch (offset) { 1076718ec0beSmalc case MP_MISC_BOARD_REVISION: 1077718ec0beSmalc return MP_BOARD_REVISION; 1078718ec0beSmalc 1079718ec0beSmalc default: 1080718ec0beSmalc return 0; 1081718ec0beSmalc } 1082718ec0beSmalc } 1083718ec0beSmalc 1084a8170e5eSAvi Kivity static void musicpal_misc_write(void *opaque, hwaddr offset, 108519b4a424SAvi Kivity uint64_t value, unsigned size) 1086718ec0beSmalc { 1087718ec0beSmalc } 1088718ec0beSmalc 108919b4a424SAvi Kivity static const MemoryRegionOps musicpal_misc_ops = { 109019b4a424SAvi Kivity .read = musicpal_misc_read, 109119b4a424SAvi Kivity .write = musicpal_misc_write, 109219b4a424SAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 1093718ec0beSmalc }; 1094718ec0beSmalc 1095a86f200aSPeter Maydell static void musicpal_misc_init(Object *obj) 1096718ec0beSmalc { 1097a86f200aSPeter Maydell SysBusDevice *sd = SYS_BUS_DEVICE(obj); 1098a86f200aSPeter Maydell MusicPalMiscState *s = MUSICPAL_MISC(obj); 1099718ec0beSmalc 110064bde0f3SPaolo Bonzini memory_region_init_io(&s->iomem, OBJECT(s), &musicpal_misc_ops, NULL, 110119b4a424SAvi Kivity "musicpal-misc", MP_MISC_SIZE); 1102a86f200aSPeter Maydell sysbus_init_mmio(sd, &s->iomem); 1103718ec0beSmalc } 1104718ec0beSmalc 1105a86f200aSPeter Maydell static const TypeInfo musicpal_misc_info = { 1106a86f200aSPeter Maydell .name = TYPE_MUSICPAL_MISC, 1107a86f200aSPeter Maydell .parent = TYPE_SYS_BUS_DEVICE, 1108a86f200aSPeter Maydell .instance_init = musicpal_misc_init, 1109a86f200aSPeter Maydell .instance_size = sizeof(MusicPalMiscState), 1110a86f200aSPeter Maydell }; 1111a86f200aSPeter Maydell 1112718ec0beSmalc /* WLAN register offsets */ 1113718ec0beSmalc #define MP_WLAN_MAGIC1 0x11c 1114718ec0beSmalc #define MP_WLAN_MAGIC2 0x124 1115718ec0beSmalc 1116a8170e5eSAvi Kivity static uint64_t mv88w8618_wlan_read(void *opaque, hwaddr offset, 111719b4a424SAvi Kivity unsigned size) 1118718ec0beSmalc { 1119718ec0beSmalc switch (offset) { 1120718ec0beSmalc /* Workaround to allow loading the binary-only wlandrv.ko crap 1121718ec0beSmalc * from the original Freecom firmware. */ 1122718ec0beSmalc case MP_WLAN_MAGIC1: 1123718ec0beSmalc return ~3; 1124718ec0beSmalc case MP_WLAN_MAGIC2: 1125718ec0beSmalc return -1; 1126718ec0beSmalc 1127718ec0beSmalc default: 1128718ec0beSmalc return 0; 1129718ec0beSmalc } 1130718ec0beSmalc } 1131718ec0beSmalc 1132a8170e5eSAvi Kivity static void mv88w8618_wlan_write(void *opaque, hwaddr offset, 113319b4a424SAvi Kivity uint64_t value, unsigned size) 1134718ec0beSmalc { 1135718ec0beSmalc } 1136718ec0beSmalc 113719b4a424SAvi Kivity static const MemoryRegionOps mv88w8618_wlan_ops = { 113819b4a424SAvi Kivity .read = mv88w8618_wlan_read, 113919b4a424SAvi Kivity .write =mv88w8618_wlan_write, 114019b4a424SAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 1141718ec0beSmalc }; 1142718ec0beSmalc 114381a322d4SGerd Hoffmann static int mv88w8618_wlan_init(SysBusDevice *dev) 1144718ec0beSmalc { 114519b4a424SAvi Kivity MemoryRegion *iomem = g_new(MemoryRegion, 1); 1146718ec0beSmalc 114764bde0f3SPaolo Bonzini memory_region_init_io(iomem, OBJECT(dev), &mv88w8618_wlan_ops, NULL, 114819b4a424SAvi Kivity "musicpal-wlan", MP_WLAN_SIZE); 1149750ecd44SAvi Kivity sysbus_init_mmio(dev, iomem); 115081a322d4SGerd Hoffmann return 0; 1151718ec0beSmalc } 1152718ec0beSmalc 1153718ec0beSmalc /* GPIO register offsets */ 1154718ec0beSmalc #define MP_GPIO_OE_LO 0x008 1155718ec0beSmalc #define MP_GPIO_OUT_LO 0x00c 1156718ec0beSmalc #define MP_GPIO_IN_LO 0x010 1157708afdf3SJan Kiszka #define MP_GPIO_IER_LO 0x014 1158708afdf3SJan Kiszka #define MP_GPIO_IMR_LO 0x018 1159718ec0beSmalc #define MP_GPIO_ISR_LO 0x020 1160718ec0beSmalc #define MP_GPIO_OE_HI 0x508 1161718ec0beSmalc #define MP_GPIO_OUT_HI 0x50c 1162718ec0beSmalc #define MP_GPIO_IN_HI 0x510 1163708afdf3SJan Kiszka #define MP_GPIO_IER_HI 0x514 1164708afdf3SJan Kiszka #define MP_GPIO_IMR_HI 0x518 1165718ec0beSmalc #define MP_GPIO_ISR_HI 0x520 116624859b68Sbalrog 116724859b68Sbalrog /* GPIO bits & masks */ 116824859b68Sbalrog #define MP_GPIO_LCD_BRIGHTNESS 0x00070000 116924859b68Sbalrog #define MP_GPIO_I2C_DATA_BIT 29 117024859b68Sbalrog #define MP_GPIO_I2C_CLOCK_BIT 30 117124859b68Sbalrog 117224859b68Sbalrog /* LCD brightness bits in GPIO_OE_HI */ 117324859b68Sbalrog #define MP_OE_LCD_BRIGHTNESS 0x0007 117424859b68Sbalrog 11757012d4b4SAndreas Färber #define TYPE_MUSICPAL_GPIO "musicpal_gpio" 11767012d4b4SAndreas Färber #define MUSICPAL_GPIO(obj) \ 11777012d4b4SAndreas Färber OBJECT_CHECK(musicpal_gpio_state, (obj), TYPE_MUSICPAL_GPIO) 11787012d4b4SAndreas Färber 1179343ec8e4SBenoit Canet typedef struct musicpal_gpio_state { 11807012d4b4SAndreas Färber /*< private >*/ 11817012d4b4SAndreas Färber SysBusDevice parent_obj; 11827012d4b4SAndreas Färber /*< public >*/ 11837012d4b4SAndreas Färber 118419b4a424SAvi Kivity MemoryRegion iomem; 1185343ec8e4SBenoit Canet uint32_t lcd_brightness; 1186343ec8e4SBenoit Canet uint32_t out_state; 1187343ec8e4SBenoit Canet uint32_t in_state; 1188708afdf3SJan Kiszka uint32_t ier; 1189708afdf3SJan Kiszka uint32_t imr; 1190343ec8e4SBenoit Canet uint32_t isr; 1191343ec8e4SBenoit Canet qemu_irq irq; 1192708afdf3SJan Kiszka qemu_irq out[5]; /* 3 brightness out + 2 lcd (data and clock ) */ 1193343ec8e4SBenoit Canet } musicpal_gpio_state; 1194343ec8e4SBenoit Canet 1195343ec8e4SBenoit Canet static void musicpal_gpio_brightness_update(musicpal_gpio_state *s) { 1196343ec8e4SBenoit Canet int i; 1197343ec8e4SBenoit Canet uint32_t brightness; 1198343ec8e4SBenoit Canet 1199343ec8e4SBenoit Canet /* compute brightness ratio */ 1200343ec8e4SBenoit Canet switch (s->lcd_brightness) { 1201343ec8e4SBenoit Canet case 0x00000007: 1202343ec8e4SBenoit Canet brightness = 0; 1203343ec8e4SBenoit Canet break; 1204343ec8e4SBenoit Canet 1205343ec8e4SBenoit Canet case 0x00020000: 1206343ec8e4SBenoit Canet brightness = 1; 1207343ec8e4SBenoit Canet break; 1208343ec8e4SBenoit Canet 1209343ec8e4SBenoit Canet case 0x00020001: 1210343ec8e4SBenoit Canet brightness = 2; 1211343ec8e4SBenoit Canet break; 1212343ec8e4SBenoit Canet 1213343ec8e4SBenoit Canet case 0x00040000: 1214343ec8e4SBenoit Canet brightness = 3; 1215343ec8e4SBenoit Canet break; 1216343ec8e4SBenoit Canet 1217343ec8e4SBenoit Canet case 0x00010006: 1218343ec8e4SBenoit Canet brightness = 4; 1219343ec8e4SBenoit Canet break; 1220343ec8e4SBenoit Canet 1221343ec8e4SBenoit Canet case 0x00020005: 1222343ec8e4SBenoit Canet brightness = 5; 1223343ec8e4SBenoit Canet break; 1224343ec8e4SBenoit Canet 1225343ec8e4SBenoit Canet case 0x00040003: 1226343ec8e4SBenoit Canet brightness = 6; 1227343ec8e4SBenoit Canet break; 1228343ec8e4SBenoit Canet 1229343ec8e4SBenoit Canet case 0x00030004: 1230343ec8e4SBenoit Canet default: 1231343ec8e4SBenoit Canet brightness = 7; 1232343ec8e4SBenoit Canet } 1233343ec8e4SBenoit Canet 1234343ec8e4SBenoit Canet /* set lcd brightness GPIOs */ 123549fedd0dSJan Kiszka for (i = 0; i <= 2; i++) { 1236343ec8e4SBenoit Canet qemu_set_irq(s->out[i], (brightness >> i) & 1); 1237343ec8e4SBenoit Canet } 123849fedd0dSJan Kiszka } 1239343ec8e4SBenoit Canet 1240708afdf3SJan Kiszka static void musicpal_gpio_pin_event(void *opaque, int pin, int level) 1241343ec8e4SBenoit Canet { 1242243cd13cSJan Kiszka musicpal_gpio_state *s = opaque; 1243708afdf3SJan Kiszka uint32_t mask = 1 << pin; 1244708afdf3SJan Kiszka uint32_t delta = level << pin; 1245708afdf3SJan Kiszka uint32_t old = s->in_state & mask; 1246343ec8e4SBenoit Canet 1247708afdf3SJan Kiszka s->in_state &= ~mask; 1248708afdf3SJan Kiszka s->in_state |= delta; 1249708afdf3SJan Kiszka 1250708afdf3SJan Kiszka if ((old ^ delta) && 1251708afdf3SJan Kiszka ((level && (s->imr & mask)) || (!level && (s->ier & mask)))) { 1252708afdf3SJan Kiszka s->isr = mask; 1253708afdf3SJan Kiszka qemu_irq_raise(s->irq); 1254d074769cSAndrzej Zaborowski } 1255343ec8e4SBenoit Canet } 1256343ec8e4SBenoit Canet 1257a8170e5eSAvi Kivity static uint64_t musicpal_gpio_read(void *opaque, hwaddr offset, 125819b4a424SAvi Kivity unsigned size) 125924859b68Sbalrog { 1260243cd13cSJan Kiszka musicpal_gpio_state *s = opaque; 1261343ec8e4SBenoit Canet 126224859b68Sbalrog switch (offset) { 126324859b68Sbalrog case MP_GPIO_OE_HI: /* used for LCD brightness control */ 1264343ec8e4SBenoit Canet return s->lcd_brightness & MP_OE_LCD_BRIGHTNESS; 126524859b68Sbalrog 126624859b68Sbalrog case MP_GPIO_OUT_LO: 1267343ec8e4SBenoit Canet return s->out_state & 0xFFFF; 126824859b68Sbalrog case MP_GPIO_OUT_HI: 1269343ec8e4SBenoit Canet return s->out_state >> 16; 127024859b68Sbalrog 127124859b68Sbalrog case MP_GPIO_IN_LO: 1272343ec8e4SBenoit Canet return s->in_state & 0xFFFF; 127324859b68Sbalrog case MP_GPIO_IN_HI: 1274343ec8e4SBenoit Canet return s->in_state >> 16; 127524859b68Sbalrog 1276708afdf3SJan Kiszka case MP_GPIO_IER_LO: 1277708afdf3SJan Kiszka return s->ier & 0xFFFF; 1278708afdf3SJan Kiszka case MP_GPIO_IER_HI: 1279708afdf3SJan Kiszka return s->ier >> 16; 1280708afdf3SJan Kiszka 1281708afdf3SJan Kiszka case MP_GPIO_IMR_LO: 1282708afdf3SJan Kiszka return s->imr & 0xFFFF; 1283708afdf3SJan Kiszka case MP_GPIO_IMR_HI: 1284708afdf3SJan Kiszka return s->imr >> 16; 1285708afdf3SJan Kiszka 128624859b68Sbalrog case MP_GPIO_ISR_LO: 1287343ec8e4SBenoit Canet return s->isr & 0xFFFF; 128824859b68Sbalrog case MP_GPIO_ISR_HI: 1289343ec8e4SBenoit Canet return s->isr >> 16; 129024859b68Sbalrog 129124859b68Sbalrog default: 129224859b68Sbalrog return 0; 129324859b68Sbalrog } 129424859b68Sbalrog } 129524859b68Sbalrog 1296a8170e5eSAvi Kivity static void musicpal_gpio_write(void *opaque, hwaddr offset, 129719b4a424SAvi Kivity uint64_t value, unsigned size) 129824859b68Sbalrog { 1299243cd13cSJan Kiszka musicpal_gpio_state *s = opaque; 130024859b68Sbalrog switch (offset) { 130124859b68Sbalrog case MP_GPIO_OE_HI: /* used for LCD brightness control */ 1302343ec8e4SBenoit Canet s->lcd_brightness = (s->lcd_brightness & MP_GPIO_LCD_BRIGHTNESS) | 130324859b68Sbalrog (value & MP_OE_LCD_BRIGHTNESS); 1304343ec8e4SBenoit Canet musicpal_gpio_brightness_update(s); 130524859b68Sbalrog break; 130624859b68Sbalrog 130724859b68Sbalrog case MP_GPIO_OUT_LO: 1308343ec8e4SBenoit Canet s->out_state = (s->out_state & 0xFFFF0000) | (value & 0xFFFF); 130924859b68Sbalrog break; 131024859b68Sbalrog case MP_GPIO_OUT_HI: 1311343ec8e4SBenoit Canet s->out_state = (s->out_state & 0xFFFF) | (value << 16); 1312343ec8e4SBenoit Canet s->lcd_brightness = (s->lcd_brightness & 0xFFFF) | 1313343ec8e4SBenoit Canet (s->out_state & MP_GPIO_LCD_BRIGHTNESS); 1314343ec8e4SBenoit Canet musicpal_gpio_brightness_update(s); 1315d074769cSAndrzej Zaborowski qemu_set_irq(s->out[3], (s->out_state >> MP_GPIO_I2C_DATA_BIT) & 1); 1316d074769cSAndrzej Zaborowski qemu_set_irq(s->out[4], (s->out_state >> MP_GPIO_I2C_CLOCK_BIT) & 1); 131724859b68Sbalrog break; 131824859b68Sbalrog 1319708afdf3SJan Kiszka case MP_GPIO_IER_LO: 1320708afdf3SJan Kiszka s->ier = (s->ier & 0xFFFF0000) | (value & 0xFFFF); 1321708afdf3SJan Kiszka break; 1322708afdf3SJan Kiszka case MP_GPIO_IER_HI: 1323708afdf3SJan Kiszka s->ier = (s->ier & 0xFFFF) | (value << 16); 1324708afdf3SJan Kiszka break; 1325708afdf3SJan Kiszka 1326708afdf3SJan Kiszka case MP_GPIO_IMR_LO: 1327708afdf3SJan Kiszka s->imr = (s->imr & 0xFFFF0000) | (value & 0xFFFF); 1328708afdf3SJan Kiszka break; 1329708afdf3SJan Kiszka case MP_GPIO_IMR_HI: 1330708afdf3SJan Kiszka s->imr = (s->imr & 0xFFFF) | (value << 16); 1331708afdf3SJan Kiszka break; 133224859b68Sbalrog } 133324859b68Sbalrog } 133424859b68Sbalrog 133519b4a424SAvi Kivity static const MemoryRegionOps musicpal_gpio_ops = { 133619b4a424SAvi Kivity .read = musicpal_gpio_read, 133719b4a424SAvi Kivity .write = musicpal_gpio_write, 133819b4a424SAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 1339718ec0beSmalc }; 1340718ec0beSmalc 1341d5b61dddSJan Kiszka static void musicpal_gpio_reset(DeviceState *d) 1342718ec0beSmalc { 13437012d4b4SAndreas Färber musicpal_gpio_state *s = MUSICPAL_GPIO(d); 134430624c92SJan Kiszka 134530624c92SJan Kiszka s->lcd_brightness = 0; 134630624c92SJan Kiszka s->out_state = 0; 1347343ec8e4SBenoit Canet s->in_state = 0xffffffff; 1348708afdf3SJan Kiszka s->ier = 0; 1349708afdf3SJan Kiszka s->imr = 0; 1350343ec8e4SBenoit Canet s->isr = 0; 1351343ec8e4SBenoit Canet } 1352343ec8e4SBenoit Canet 13537012d4b4SAndreas Färber static int musicpal_gpio_init(SysBusDevice *sbd) 1354343ec8e4SBenoit Canet { 13557012d4b4SAndreas Färber DeviceState *dev = DEVICE(sbd); 13567012d4b4SAndreas Färber musicpal_gpio_state *s = MUSICPAL_GPIO(dev); 1357718ec0beSmalc 13587012d4b4SAndreas Färber sysbus_init_irq(sbd, &s->irq); 1359343ec8e4SBenoit Canet 136064bde0f3SPaolo Bonzini memory_region_init_io(&s->iomem, OBJECT(s), &musicpal_gpio_ops, s, 136119b4a424SAvi Kivity "musicpal-gpio", MP_GPIO_SIZE); 13627012d4b4SAndreas Färber sysbus_init_mmio(sbd, &s->iomem); 1363343ec8e4SBenoit Canet 13647012d4b4SAndreas Färber qdev_init_gpio_out(dev, s->out, ARRAY_SIZE(s->out)); 1365708afdf3SJan Kiszka 13667012d4b4SAndreas Färber qdev_init_gpio_in(dev, musicpal_gpio_pin_event, 32); 136781a322d4SGerd Hoffmann 136881a322d4SGerd Hoffmann return 0; 1369718ec0beSmalc } 1370718ec0beSmalc 1371d5b61dddSJan Kiszka static const VMStateDescription musicpal_gpio_vmsd = { 1372d5b61dddSJan Kiszka .name = "musicpal_gpio", 1373d5b61dddSJan Kiszka .version_id = 1, 1374d5b61dddSJan Kiszka .minimum_version_id = 1, 1375d5b61dddSJan Kiszka .fields = (VMStateField[]) { 1376d5b61dddSJan Kiszka VMSTATE_UINT32(lcd_brightness, musicpal_gpio_state), 1377d5b61dddSJan Kiszka VMSTATE_UINT32(out_state, musicpal_gpio_state), 1378d5b61dddSJan Kiszka VMSTATE_UINT32(in_state, musicpal_gpio_state), 1379d5b61dddSJan Kiszka VMSTATE_UINT32(ier, musicpal_gpio_state), 1380d5b61dddSJan Kiszka VMSTATE_UINT32(imr, musicpal_gpio_state), 1381d5b61dddSJan Kiszka VMSTATE_UINT32(isr, musicpal_gpio_state), 1382d5b61dddSJan Kiszka VMSTATE_END_OF_LIST() 1383d5b61dddSJan Kiszka } 1384d5b61dddSJan Kiszka }; 1385d5b61dddSJan Kiszka 1386999e12bbSAnthony Liguori static void musicpal_gpio_class_init(ObjectClass *klass, void *data) 1387999e12bbSAnthony Liguori { 138839bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 1389999e12bbSAnthony Liguori SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); 1390999e12bbSAnthony Liguori 1391999e12bbSAnthony Liguori k->init = musicpal_gpio_init; 139239bffca2SAnthony Liguori dc->reset = musicpal_gpio_reset; 139339bffca2SAnthony Liguori dc->vmsd = &musicpal_gpio_vmsd; 1394999e12bbSAnthony Liguori } 1395999e12bbSAnthony Liguori 13968c43a6f0SAndreas Färber static const TypeInfo musicpal_gpio_info = { 13977012d4b4SAndreas Färber .name = TYPE_MUSICPAL_GPIO, 139839bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 139939bffca2SAnthony Liguori .instance_size = sizeof(musicpal_gpio_state), 1400999e12bbSAnthony Liguori .class_init = musicpal_gpio_class_init, 140130624c92SJan Kiszka }; 140230624c92SJan Kiszka 140324859b68Sbalrog /* Keyboard codes & masks */ 14047c6ce4baSbalrog #define KEY_RELEASED 0x80 140524859b68Sbalrog #define KEY_CODE 0x7f 140624859b68Sbalrog 140724859b68Sbalrog #define KEYCODE_TAB 0x0f 140824859b68Sbalrog #define KEYCODE_ENTER 0x1c 140924859b68Sbalrog #define KEYCODE_F 0x21 141024859b68Sbalrog #define KEYCODE_M 0x32 141124859b68Sbalrog 141224859b68Sbalrog #define KEYCODE_EXTENDED 0xe0 141324859b68Sbalrog #define KEYCODE_UP 0x48 141424859b68Sbalrog #define KEYCODE_DOWN 0x50 141524859b68Sbalrog #define KEYCODE_LEFT 0x4b 141624859b68Sbalrog #define KEYCODE_RIGHT 0x4d 141724859b68Sbalrog 1418708afdf3SJan Kiszka #define MP_KEY_WHEEL_VOL (1 << 0) 1419343ec8e4SBenoit Canet #define MP_KEY_WHEEL_VOL_INV (1 << 1) 1420343ec8e4SBenoit Canet #define MP_KEY_WHEEL_NAV (1 << 2) 1421343ec8e4SBenoit Canet #define MP_KEY_WHEEL_NAV_INV (1 << 3) 1422343ec8e4SBenoit Canet #define MP_KEY_BTN_FAVORITS (1 << 4) 1423343ec8e4SBenoit Canet #define MP_KEY_BTN_MENU (1 << 5) 1424343ec8e4SBenoit Canet #define MP_KEY_BTN_VOLUME (1 << 6) 1425343ec8e4SBenoit Canet #define MP_KEY_BTN_NAVIGATION (1 << 7) 1426343ec8e4SBenoit Canet 14273bdf5327SAndreas Färber #define TYPE_MUSICPAL_KEY "musicpal_key" 14283bdf5327SAndreas Färber #define MUSICPAL_KEY(obj) \ 14293bdf5327SAndreas Färber OBJECT_CHECK(musicpal_key_state, (obj), TYPE_MUSICPAL_KEY) 14303bdf5327SAndreas Färber 1431343ec8e4SBenoit Canet typedef struct musicpal_key_state { 14323bdf5327SAndreas Färber /*< private >*/ 14333bdf5327SAndreas Färber SysBusDevice parent_obj; 14343bdf5327SAndreas Färber /*< public >*/ 14353bdf5327SAndreas Färber 14364f5c9479SAvi Kivity MemoryRegion iomem; 1437343ec8e4SBenoit Canet uint32_t kbd_extended; 1438708afdf3SJan Kiszka uint32_t pressed_keys; 1439708afdf3SJan Kiszka qemu_irq out[8]; 1440343ec8e4SBenoit Canet } musicpal_key_state; 1441343ec8e4SBenoit Canet 144224859b68Sbalrog static void musicpal_key_event(void *opaque, int keycode) 144324859b68Sbalrog { 1444243cd13cSJan Kiszka musicpal_key_state *s = opaque; 144524859b68Sbalrog uint32_t event = 0; 1446343ec8e4SBenoit Canet int i; 144724859b68Sbalrog 144824859b68Sbalrog if (keycode == KEYCODE_EXTENDED) { 1449343ec8e4SBenoit Canet s->kbd_extended = 1; 145024859b68Sbalrog return; 145124859b68Sbalrog } 145224859b68Sbalrog 145349fedd0dSJan Kiszka if (s->kbd_extended) { 145424859b68Sbalrog switch (keycode & KEY_CODE) { 145524859b68Sbalrog case KEYCODE_UP: 1456343ec8e4SBenoit Canet event = MP_KEY_WHEEL_NAV | MP_KEY_WHEEL_NAV_INV; 145724859b68Sbalrog break; 145824859b68Sbalrog 145924859b68Sbalrog case KEYCODE_DOWN: 1460343ec8e4SBenoit Canet event = MP_KEY_WHEEL_NAV; 146124859b68Sbalrog break; 146224859b68Sbalrog 146324859b68Sbalrog case KEYCODE_LEFT: 1464343ec8e4SBenoit Canet event = MP_KEY_WHEEL_VOL | MP_KEY_WHEEL_VOL_INV; 146524859b68Sbalrog break; 146624859b68Sbalrog 146724859b68Sbalrog case KEYCODE_RIGHT: 1468343ec8e4SBenoit Canet event = MP_KEY_WHEEL_VOL; 146924859b68Sbalrog break; 147024859b68Sbalrog } 147149fedd0dSJan Kiszka } else { 147224859b68Sbalrog switch (keycode & KEY_CODE) { 147324859b68Sbalrog case KEYCODE_F: 1474343ec8e4SBenoit Canet event = MP_KEY_BTN_FAVORITS; 147524859b68Sbalrog break; 147624859b68Sbalrog 147724859b68Sbalrog case KEYCODE_TAB: 1478343ec8e4SBenoit Canet event = MP_KEY_BTN_VOLUME; 147924859b68Sbalrog break; 148024859b68Sbalrog 148124859b68Sbalrog case KEYCODE_ENTER: 1482343ec8e4SBenoit Canet event = MP_KEY_BTN_NAVIGATION; 148324859b68Sbalrog break; 148424859b68Sbalrog 148524859b68Sbalrog case KEYCODE_M: 1486343ec8e4SBenoit Canet event = MP_KEY_BTN_MENU; 148724859b68Sbalrog break; 148824859b68Sbalrog } 14897c6ce4baSbalrog /* Do not repeat already pressed buttons */ 1490708afdf3SJan Kiszka if (!(keycode & KEY_RELEASED) && (s->pressed_keys & event)) { 14917c6ce4baSbalrog event = 0; 14927c6ce4baSbalrog } 1493708afdf3SJan Kiszka } 149424859b68Sbalrog 14957c6ce4baSbalrog if (event) { 1496708afdf3SJan Kiszka /* Raise GPIO pin first if repeating a key */ 1497708afdf3SJan Kiszka if (!(keycode & KEY_RELEASED) && (s->pressed_keys & event)) { 1498708afdf3SJan Kiszka for (i = 0; i <= 7; i++) { 1499708afdf3SJan Kiszka if (event & (1 << i)) { 1500708afdf3SJan Kiszka qemu_set_irq(s->out[i], 1); 15017c6ce4baSbalrog } 1502708afdf3SJan Kiszka } 1503708afdf3SJan Kiszka } 1504708afdf3SJan Kiszka for (i = 0; i <= 7; i++) { 1505708afdf3SJan Kiszka if (event & (1 << i)) { 1506708afdf3SJan Kiszka qemu_set_irq(s->out[i], !!(keycode & KEY_RELEASED)); 1507708afdf3SJan Kiszka } 1508708afdf3SJan Kiszka } 1509708afdf3SJan Kiszka if (keycode & KEY_RELEASED) { 1510708afdf3SJan Kiszka s->pressed_keys &= ~event; 1511708afdf3SJan Kiszka } else { 1512708afdf3SJan Kiszka s->pressed_keys |= event; 1513708afdf3SJan Kiszka } 1514343ec8e4SBenoit Canet } 1515343ec8e4SBenoit Canet 1516343ec8e4SBenoit Canet s->kbd_extended = 0; 1517343ec8e4SBenoit Canet } 1518343ec8e4SBenoit Canet 15193bdf5327SAndreas Färber static int musicpal_key_init(SysBusDevice *sbd) 1520343ec8e4SBenoit Canet { 15213bdf5327SAndreas Färber DeviceState *dev = DEVICE(sbd); 15223bdf5327SAndreas Färber musicpal_key_state *s = MUSICPAL_KEY(dev); 1523343ec8e4SBenoit Canet 152464bde0f3SPaolo Bonzini memory_region_init(&s->iomem, OBJECT(s), "dummy", 0); 15253bdf5327SAndreas Färber sysbus_init_mmio(sbd, &s->iomem); 1526343ec8e4SBenoit Canet 1527343ec8e4SBenoit Canet s->kbd_extended = 0; 1528708afdf3SJan Kiszka s->pressed_keys = 0; 1529343ec8e4SBenoit Canet 15303bdf5327SAndreas Färber qdev_init_gpio_out(dev, s->out, ARRAY_SIZE(s->out)); 1531343ec8e4SBenoit Canet 1532343ec8e4SBenoit Canet qemu_add_kbd_event_handler(musicpal_key_event, s); 153381a322d4SGerd Hoffmann 153481a322d4SGerd Hoffmann return 0; 153524859b68Sbalrog } 153624859b68Sbalrog 1537d5b61dddSJan Kiszka static const VMStateDescription musicpal_key_vmsd = { 1538d5b61dddSJan Kiszka .name = "musicpal_key", 1539d5b61dddSJan Kiszka .version_id = 1, 1540d5b61dddSJan Kiszka .minimum_version_id = 1, 1541d5b61dddSJan Kiszka .fields = (VMStateField[]) { 1542d5b61dddSJan Kiszka VMSTATE_UINT32(kbd_extended, musicpal_key_state), 1543d5b61dddSJan Kiszka VMSTATE_UINT32(pressed_keys, musicpal_key_state), 1544d5b61dddSJan Kiszka VMSTATE_END_OF_LIST() 1545d5b61dddSJan Kiszka } 1546d5b61dddSJan Kiszka }; 1547d5b61dddSJan Kiszka 1548999e12bbSAnthony Liguori static void musicpal_key_class_init(ObjectClass *klass, void *data) 1549999e12bbSAnthony Liguori { 155039bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 1551999e12bbSAnthony Liguori SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); 1552999e12bbSAnthony Liguori 1553999e12bbSAnthony Liguori k->init = musicpal_key_init; 155439bffca2SAnthony Liguori dc->vmsd = &musicpal_key_vmsd; 1555999e12bbSAnthony Liguori } 1556999e12bbSAnthony Liguori 15578c43a6f0SAndreas Färber static const TypeInfo musicpal_key_info = { 15583bdf5327SAndreas Färber .name = TYPE_MUSICPAL_KEY, 155939bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 156039bffca2SAnthony Liguori .instance_size = sizeof(musicpal_key_state), 1561999e12bbSAnthony Liguori .class_init = musicpal_key_class_init, 1562d5b61dddSJan Kiszka }; 1563d5b61dddSJan Kiszka 156424859b68Sbalrog static struct arm_boot_info musicpal_binfo = { 156524859b68Sbalrog .loader_start = 0x0, 156624859b68Sbalrog .board_id = 0x20e, 156724859b68Sbalrog }; 156824859b68Sbalrog 15693ef96221SMarcel Apfelbaum static void musicpal_init(MachineState *machine) 157024859b68Sbalrog { 15713ef96221SMarcel Apfelbaum const char *cpu_model = machine->cpu_model; 15723ef96221SMarcel Apfelbaum const char *kernel_filename = machine->kernel_filename; 15733ef96221SMarcel Apfelbaum const char *kernel_cmdline = machine->kernel_cmdline; 15743ef96221SMarcel Apfelbaum const char *initrd_filename = machine->initrd_filename; 1575f25608e9SAndreas Färber ARMCPU *cpu; 1576b47b50faSPaul Brook qemu_irq pic[32]; 1577b47b50faSPaul Brook DeviceState *dev; 1578d074769cSAndrzej Zaborowski DeviceState *i2c_dev; 1579343ec8e4SBenoit Canet DeviceState *lcd_dev; 1580343ec8e4SBenoit Canet DeviceState *key_dev; 1581d074769cSAndrzej Zaborowski DeviceState *wm8750_dev; 1582d074769cSAndrzej Zaborowski SysBusDevice *s; 1583a5c82852SAndreas Färber I2CBus *i2c; 1584b47b50faSPaul Brook int i; 158524859b68Sbalrog unsigned long flash_size; 1586751c6a17SGerd Hoffmann DriveInfo *dinfo; 158719b4a424SAvi Kivity MemoryRegion *address_space_mem = get_system_memory(); 158819b4a424SAvi Kivity MemoryRegion *ram = g_new(MemoryRegion, 1); 158919b4a424SAvi Kivity MemoryRegion *sram = g_new(MemoryRegion, 1); 159024859b68Sbalrog 159149fedd0dSJan Kiszka if (!cpu_model) { 159224859b68Sbalrog cpu_model = "arm926"; 159349fedd0dSJan Kiszka } 1594f25608e9SAndreas Färber cpu = cpu_arm_init(cpu_model); 1595f25608e9SAndreas Färber if (!cpu) { 159624859b68Sbalrog fprintf(stderr, "Unable to find CPU definition\n"); 159724859b68Sbalrog exit(1); 159824859b68Sbalrog } 159924859b68Sbalrog 160024859b68Sbalrog /* For now we use a fixed - the original - RAM size */ 1601c8623c02SDirk Müller memory_region_allocate_system_memory(ram, NULL, "musicpal.ram", 1602c8623c02SDirk Müller MP_RAM_DEFAULT_SIZE); 160319b4a424SAvi Kivity memory_region_add_subregion(address_space_mem, 0, ram); 160424859b68Sbalrog 160549946538SHu Tao memory_region_init_ram(sram, NULL, "musicpal.sram", MP_SRAM_SIZE, 1606f8ed85acSMarkus Armbruster &error_fatal); 1607c5705a77SAvi Kivity vmstate_register_ram_global(sram); 160819b4a424SAvi Kivity memory_region_add_subregion(address_space_mem, MP_SRAM_BASE, sram); 160924859b68Sbalrog 1610c7bd0fd9SAndreas Färber dev = sysbus_create_simple(TYPE_MV88W8618_PIC, MP_PIC_BASE, 1611fcef61ecSPeter Maydell qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ)); 1612b47b50faSPaul Brook for (i = 0; i < 32; i++) { 1613067a3ddcSPaul Brook pic[i] = qdev_get_gpio_in(dev, i); 1614b47b50faSPaul Brook } 16154adc8541SAndreas Färber sysbus_create_varargs(TYPE_MV88W8618_PIT, MP_PIT_BASE, pic[MP_TIMER1_IRQ], 1616b47b50faSPaul Brook pic[MP_TIMER2_IRQ], pic[MP_TIMER3_IRQ], 1617b47b50faSPaul Brook pic[MP_TIMER4_IRQ], NULL); 161824859b68Sbalrog 161949fedd0dSJan Kiszka if (serial_hds[0]) { 162039186d8aSRichard Henderson serial_mm_init(address_space_mem, MP_UART1_BASE, 2, pic[MP_UART1_IRQ], 162139186d8aSRichard Henderson 1825000, serial_hds[0], DEVICE_NATIVE_ENDIAN); 162249fedd0dSJan Kiszka } 162349fedd0dSJan Kiszka if (serial_hds[1]) { 162439186d8aSRichard Henderson serial_mm_init(address_space_mem, MP_UART2_BASE, 2, pic[MP_UART2_IRQ], 162539186d8aSRichard Henderson 1825000, serial_hds[1], DEVICE_NATIVE_ENDIAN); 162649fedd0dSJan Kiszka } 162724859b68Sbalrog 162824859b68Sbalrog /* Register flash */ 1629751c6a17SGerd Hoffmann dinfo = drive_get(IF_PFLASH, 0, 0); 1630751c6a17SGerd Hoffmann if (dinfo) { 16314be74634SMarkus Armbruster BlockBackend *blk = blk_by_legacy_dinfo(dinfo); 1632fa1d36dfSMarkus Armbruster 16334be74634SMarkus Armbruster flash_size = blk_getlength(blk); 163424859b68Sbalrog if (flash_size != 8*1024*1024 && flash_size != 16*1024*1024 && 163524859b68Sbalrog flash_size != 32*1024*1024) { 163624859b68Sbalrog fprintf(stderr, "Invalid flash image size\n"); 163724859b68Sbalrog exit(1); 163824859b68Sbalrog } 163924859b68Sbalrog 164024859b68Sbalrog /* 164124859b68Sbalrog * The original U-Boot accesses the flash at 0xFE000000 instead of 164224859b68Sbalrog * 0xFF800000 (if there is 8 MB flash). So remap flash access if the 164324859b68Sbalrog * image is smaller than 32 MB. 164424859b68Sbalrog */ 16455f9fc5adSBlue Swirl #ifdef TARGET_WORDS_BIGENDIAN 16460c267217SJan Kiszka pflash_cfi02_register(0x100000000ULL-MP_FLASH_SIZE_MAX, NULL, 1647cfe5f011SAvi Kivity "musicpal.flash", flash_size, 16484be74634SMarkus Armbruster blk, 0x10000, (flash_size + 0xffff) >> 16, 164924859b68Sbalrog MP_FLASH_SIZE_MAX / flash_size, 165024859b68Sbalrog 2, 0x00BF, 0x236D, 0x0000, 0x0000, 165101e0451aSAnthony Liguori 0x5555, 0x2AAA, 1); 16525f9fc5adSBlue Swirl #else 16530c267217SJan Kiszka pflash_cfi02_register(0x100000000ULL-MP_FLASH_SIZE_MAX, NULL, 1654cfe5f011SAvi Kivity "musicpal.flash", flash_size, 16554be74634SMarkus Armbruster blk, 0x10000, (flash_size + 0xffff) >> 16, 16565f9fc5adSBlue Swirl MP_FLASH_SIZE_MAX / flash_size, 16575f9fc5adSBlue Swirl 2, 0x00BF, 0x236D, 0x0000, 0x0000, 165801e0451aSAnthony Liguori 0x5555, 0x2AAA, 0); 16595f9fc5adSBlue Swirl #endif 16605f9fc5adSBlue Swirl 166124859b68Sbalrog } 16625952b01cSAndreas Färber sysbus_create_simple(TYPE_MV88W8618_FLASHCFG, MP_FLASHCFG_BASE, NULL); 166324859b68Sbalrog 1664b47b50faSPaul Brook qemu_check_nic_model(&nd_table[0], "mv88w8618"); 1665a77d90e6SAndreas Färber dev = qdev_create(NULL, TYPE_MV88W8618_ETH); 16664c91cd28SGerd Hoffmann qdev_set_nic_properties(dev, &nd_table[0]); 1667e23a1b33SMarkus Armbruster qdev_init_nofail(dev); 16681356b98dSAndreas Färber sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, MP_ETH_BASE); 16691356b98dSAndreas Färber sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[MP_ETH_IRQ]); 167024859b68Sbalrog 1671b47b50faSPaul Brook sysbus_create_simple("mv88w8618_wlan", MP_WLAN_BASE, NULL); 1672718ec0beSmalc 1673a86f200aSPeter Maydell sysbus_create_simple(TYPE_MUSICPAL_MISC, MP_MISC_BASE, NULL); 1674343ec8e4SBenoit Canet 16757012d4b4SAndreas Färber dev = sysbus_create_simple(TYPE_MUSICPAL_GPIO, MP_GPIO_BASE, 16767012d4b4SAndreas Färber pic[MP_GPIO_IRQ]); 1677d04fba94SJan Kiszka i2c_dev = sysbus_create_simple("gpio_i2c", -1, NULL); 1678a5c82852SAndreas Färber i2c = (I2CBus *)qdev_get_child_bus(i2c_dev, "i2c"); 1679d074769cSAndrzej Zaborowski 16802cca58fdSAndreas Färber lcd_dev = sysbus_create_simple(TYPE_MUSICPAL_LCD, MP_LCD_BASE, NULL); 16813bdf5327SAndreas Färber key_dev = sysbus_create_simple(TYPE_MUSICPAL_KEY, -1, NULL); 1682343ec8e4SBenoit Canet 1683d074769cSAndrzej Zaborowski /* I2C read data */ 1684708afdf3SJan Kiszka qdev_connect_gpio_out(i2c_dev, 0, 1685708afdf3SJan Kiszka qdev_get_gpio_in(dev, MP_GPIO_I2C_DATA_BIT)); 1686d074769cSAndrzej Zaborowski /* I2C data */ 1687d074769cSAndrzej Zaborowski qdev_connect_gpio_out(dev, 3, qdev_get_gpio_in(i2c_dev, 0)); 1688d074769cSAndrzej Zaborowski /* I2C clock */ 1689d074769cSAndrzej Zaborowski qdev_connect_gpio_out(dev, 4, qdev_get_gpio_in(i2c_dev, 1)); 1690d074769cSAndrzej Zaborowski 169149fedd0dSJan Kiszka for (i = 0; i < 3; i++) { 1692343ec8e4SBenoit Canet qdev_connect_gpio_out(dev, i, qdev_get_gpio_in(lcd_dev, i)); 169349fedd0dSJan Kiszka } 1694708afdf3SJan Kiszka for (i = 0; i < 4; i++) { 1695708afdf3SJan Kiszka qdev_connect_gpio_out(key_dev, i, qdev_get_gpio_in(dev, i + 8)); 1696708afdf3SJan Kiszka } 1697708afdf3SJan Kiszka for (i = 4; i < 8; i++) { 1698708afdf3SJan Kiszka qdev_connect_gpio_out(key_dev, i, qdev_get_gpio_in(dev, i + 15)); 1699708afdf3SJan Kiszka } 170024859b68Sbalrog 1701d074769cSAndrzej Zaborowski wm8750_dev = i2c_create_slave(i2c, "wm8750", MP_WM_ADDR); 1702d074769cSAndrzej Zaborowski dev = qdev_create(NULL, "mv88w8618_audio"); 17031356b98dSAndreas Färber s = SYS_BUS_DEVICE(dev); 1704d074769cSAndrzej Zaborowski qdev_prop_set_ptr(dev, "wm8750", wm8750_dev); 1705e23a1b33SMarkus Armbruster qdev_init_nofail(dev); 1706d074769cSAndrzej Zaborowski sysbus_mmio_map(s, 0, MP_AUDIO_BASE); 1707d074769cSAndrzej Zaborowski sysbus_connect_irq(s, 0, pic[MP_AUDIO_IRQ]); 1708d074769cSAndrzej Zaborowski 170924859b68Sbalrog musicpal_binfo.ram_size = MP_RAM_DEFAULT_SIZE; 171024859b68Sbalrog musicpal_binfo.kernel_filename = kernel_filename; 171124859b68Sbalrog musicpal_binfo.kernel_cmdline = kernel_cmdline; 171224859b68Sbalrog musicpal_binfo.initrd_filename = initrd_filename; 17133aaa8dfaSAndreas Färber arm_load_kernel(cpu, &musicpal_binfo); 171424859b68Sbalrog } 171524859b68Sbalrog 1716e264d29dSEduardo Habkost static void musicpal_machine_init(MachineClass *mc) 1717f80f9ec9SAnthony Liguori { 1718e264d29dSEduardo Habkost mc->desc = "Marvell 88w8618 / MusicPal (ARM926EJ-S)"; 1719e264d29dSEduardo Habkost mc->init = musicpal_init; 1720f80f9ec9SAnthony Liguori } 1721f80f9ec9SAnthony Liguori 1722e264d29dSEduardo Habkost DEFINE_MACHINE("musicpal", musicpal_machine_init) 1723f80f9ec9SAnthony Liguori 1724999e12bbSAnthony Liguori static void mv88w8618_wlan_class_init(ObjectClass *klass, void *data) 1725999e12bbSAnthony Liguori { 1726999e12bbSAnthony Liguori SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); 1727999e12bbSAnthony Liguori 1728999e12bbSAnthony Liguori sdc->init = mv88w8618_wlan_init; 1729999e12bbSAnthony Liguori } 1730999e12bbSAnthony Liguori 17318c43a6f0SAndreas Färber static const TypeInfo mv88w8618_wlan_info = { 1732999e12bbSAnthony Liguori .name = "mv88w8618_wlan", 173339bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 173439bffca2SAnthony Liguori .instance_size = sizeof(SysBusDevice), 1735999e12bbSAnthony Liguori .class_init = mv88w8618_wlan_class_init, 1736999e12bbSAnthony Liguori }; 1737999e12bbSAnthony Liguori 173883f7d43aSAndreas Färber static void musicpal_register_types(void) 1739b47b50faSPaul Brook { 174039bffca2SAnthony Liguori type_register_static(&mv88w8618_pic_info); 174139bffca2SAnthony Liguori type_register_static(&mv88w8618_pit_info); 174239bffca2SAnthony Liguori type_register_static(&mv88w8618_flashcfg_info); 174339bffca2SAnthony Liguori type_register_static(&mv88w8618_eth_info); 174439bffca2SAnthony Liguori type_register_static(&mv88w8618_wlan_info); 174539bffca2SAnthony Liguori type_register_static(&musicpal_lcd_info); 174639bffca2SAnthony Liguori type_register_static(&musicpal_gpio_info); 174739bffca2SAnthony Liguori type_register_static(&musicpal_key_info); 1748a86f200aSPeter Maydell type_register_static(&musicpal_misc_info); 1749b47b50faSPaul Brook } 1750b47b50faSPaul Brook 175183f7d43aSAndreas Färber type_init(musicpal_register_types) 1752