diff options
-rw-r--r-- | MAINTAINERS | 7 | ||||
-rw-r--r-- | Makefile.target | 5 | ||||
-rw-r--r-- | hw/cadence_gem.c | 1233 | ||||
-rw-r--r-- | hw/cadence_ttc.c | 489 | ||||
-rw-r--r-- | hw/cadence_uart.c | 513 | ||||
-rw-r--r-- | hw/microblaze_boot.c | 177 | ||||
-rw-r--r-- | hw/microblaze_boot.h | 10 | ||||
-rw-r--r-- | hw/petalogix_ml605_mmu.c | 144 | ||||
-rw-r--r-- | hw/petalogix_s3adsp1800_mmu.c | 147 | ||||
-rw-r--r-- | hw/xilinx_zynq.c | 157 | ||||
-rw-r--r-- | hw/zynq_slcr.c | 535 | ||||
-rw-r--r-- | libcacard/vcardt.h | 4 | ||||
-rw-r--r-- | qemu-options.hx | 2 |
13 files changed, 3161 insertions, 262 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 0b3b3d8dc6..d249947d12 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -251,6 +251,13 @@ M: Peter Maydell <peter.maydell@linaro.org> S: Maintained F: hw/versatilepb.c +Xilinx Zynq +M: Peter Crosthwaite <peter.crosthwaite@petalogix.com> +S: Maintained +F: hw/xilinx_zynq.c +F: hw/zynq_slcr.c +F: hw/cadence_* + CRIS Machines ------------- Axis Dev88 diff --git a/Makefile.target b/Makefile.target index 343eb78494..5f3fc40fe5 100644 --- a/Makefile.target +++ b/Makefile.target @@ -307,6 +307,7 @@ obj-mips-$(CONFIG_FULONG) += bonito.o vt82c686.o mips_fulong2e.o obj-microblaze-y = petalogix_s3adsp1800_mmu.o obj-microblaze-y += petalogix_ml605_mmu.o +obj-microblaze-y += microblaze_boot.o obj-microblaze-y += microblaze_pic_cpu.o obj-microblaze-y += xilinx_intc.o @@ -345,6 +346,10 @@ endif obj-arm-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o obj-arm-y += arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o pl190.o obj-arm-y += versatile_pci.o +obj-arm-y += cadence_uart.o +obj-arm-y += cadence_ttc.o +obj-arm-y += cadence_gem.o +obj-arm-y += xilinx_zynq.o zynq_slcr.o obj-arm-y += realview_gic.o realview.o arm_sysctl.o arm11mpcore.o a9mpcore.o obj-arm-y += exynos4210_gic.o exynos4210_combiner.o exynos4210.o obj-arm-y += exynos4_boards.o exynos4210_uart.o exynos4210_pwm.o diff --git a/hw/cadence_gem.c b/hw/cadence_gem.c new file mode 100644 index 0000000000..e2140aea2b --- /dev/null +++ b/hw/cadence_gem.c @@ -0,0 +1,1233 @@ +/* + * QEMU Xilinx GEM emulation + * + * Copyright (c) 2011 Xilinx, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include <zlib.h> /* For crc32 */ + +#include "sysbus.h" +#include "net.h" +#include "net/checksum.h" + +#ifdef CADENCE_GEM_ERR_DEBUG +#define DB_PRINT(...) do { \ + fprintf(stderr, ": %s: ", __func__); \ + fprintf(stderr, ## __VA_ARGS__); \ + } while (0); +#else + #define DB_PRINT(...) +#endif + +#define GEM_NWCTRL (0x00000000/4) /* Network Control reg */ +#define GEM_NWCFG (0x00000004/4) /* Network Config reg */ +#define GEM_NWSTATUS (0x00000008/4) /* Network Status reg */ +#define GEM_USERIO (0x0000000C/4) /* User IO reg */ +#define GEM_DMACFG (0x00000010/4) /* DMA Control reg */ +#define GEM_TXSTATUS (0x00000014/4) /* TX Status reg */ +#define GEM_RXQBASE (0x00000018/4) /* RX Q Base address reg */ +#define GEM_TXQBASE (0x0000001C/4) /* TX Q Base address reg */ +#define GEM_RXSTATUS (0x00000020/4) /* RX Status reg */ +#define GEM_ISR (0x00000024/4) /* Interrupt Status reg */ +#define GEM_IER (0x00000028/4) /* Interrupt Enable reg */ +#define GEM_IDR (0x0000002C/4) /* Interrupt Disable reg */ +#define GEM_IMR (0x00000030/4) /* Interrupt Mask reg */ +#define GEM_PHYMNTNC (0x00000034/4) /* Phy Maintaince reg */ +#define GEM_RXPAUSE (0x00000038/4) /* RX Pause Time reg */ +#define GEM_TXPAUSE (0x0000003C/4) /* TX Pause Time reg */ +#define GEM_TXPARTIALSF (0x00000040/4) /* TX Partial Store and Forward */ +#define GEM_RXPARTIALSF (0x00000044/4) /* RX Partial Store and Forward */ +#define GEM_HASHLO (0x00000080/4) /* Hash Low address reg */ +#define GEM_HASHHI (0x00000084/4) /* Hash High address reg */ +#define GEM_SPADDR1LO (0x00000088/4) /* Specific addr 1 low reg */ +#define GEM_SPADDR1HI (0x0000008C/4) /* Specific addr 1 high reg */ +#define GEM_SPADDR2LO (0x00000090/4) /* Specific addr 2 low reg */ +#define GEM_SPADDR2HI (0x00000094/4) /* Specific addr 2 high reg */ +#define GEM_SPADDR3LO (0x00000098/4) /* Specific addr 3 low reg */ +#define GEM_SPADDR3HI (0x0000009C/4) /* Specific addr 3 high reg */ +#define GEM_SPADDR4LO (0x000000A0/4) /* Specific addr 4 low reg */ +#define GEM_SPADDR4HI (0x000000A4/4) /* Specific addr 4 high reg */ +#define GEM_TIDMATCH1 (0x000000A8/4) /* Type ID1 Match reg */ +#define GEM_TIDMATCH2 (0x000000AC/4) /* Type ID2 Match reg */ +#define GEM_TIDMATCH3 (0x000000B0/4) /* Type ID3 Match reg */ +#define GEM_TIDMATCH4 (0x000000B4/4) /* Type ID4 Match reg */ +#define GEM_WOLAN (0x000000B8/4) /* Wake on LAN reg */ +#define GEM_IPGSTRETCH (0x000000BC/4) /* IPG Stretch reg */ +#define GEM_SVLAN (0x000000C0/4) /* Stacked VLAN reg */ +#define GEM_MODID (0x000000FC/4) /* Module ID reg */ +#define GEM_OCTTXLO (0x00000100/4) /* Octects transmitted Low reg */ +#define GEM_OCTTXHI (0x00000104/4) /* Octects transmitted High reg */ +#define GEM_TXCNT (0x00000108/4) /* Error-free Frames transmitted */ +#define GEM_TXBCNT (0x0000010C/4) /* Error-free Broadcast Frames */ +#define GEM_TXMCNT (0x00000110/4) /* Error-free Multicast Frame */ +#define GEM_TXPAUSECNT (0x00000114/4) /* Pause Frames Transmitted */ +#define GEM_TX64CNT (0x00000118/4) /* Error-free 64 TX */ +#define GEM_TX65CNT (0x0000011C/4) /* Error-free 65-127 TX */ +#define GEM_TX128CNT (0x00000120/4) /* Error-free 128-255 TX */ +#define GEM_TX256CNT (0x00000124/4) /* Error-free 256-511 */ +#define GEM_TX512CNT (0x00000128/4) /* Error-free 512-1023 TX */ +#define GEM_TX1024CNT (0x0000012C/4) /* Error-free 1024-1518 TX */ +#define GEM_TX1519CNT (0x00000130/4) /* Error-free larger than 1519 TX */ +#define GEM_TXURUNCNT (0x00000134/4) /* TX under run error counter */ +#define GEM_SINGLECOLLCNT (0x00000138/4) /* Single Collision Frames */ +#define GEM_MULTCOLLCNT (0x0000013C/4) /* Multiple Collision Frames */ +#define GEM_EXCESSCOLLCNT (0x00000140/4) /* Excessive Collision Frames */ +#define GEM_LATECOLLCNT (0x00000144/4) /* Late Collision Frames */ +#define GEM_DEFERTXCNT (0x00000148/4) /* Deferred Transmission Frames */ +#define GEM_CSENSECNT (0x0000014C/4) /* Carrier Sense Error Counter */ +#define GEM_OCTRXLO (0x00000150/4) /* Octects Received register Low */ +#define GEM_OCTRXHI (0x00000154/4) /* Octects Received register High */ +#define GEM_RXCNT (0x00000158/4) /* Error-free Frames Received */ +#define GEM_RXBROADCNT (0x0000015C/4) /* Error-free Broadcast Frames RX */ +#define GEM_RXMULTICNT (0x00000160/4) /* Error-free Multicast Frames RX */ +#define GEM_RXPAUSECNT (0x00000164/4) /* Pause Frames Received Counter */ +#define GEM_RX64CNT (0x00000168/4) /* Error-free 64 byte Frames RX */ +#define GEM_RX65CNT (0x0000016C/4) /* Error-free 65-127B Frames RX */ +#define GEM_RX128CNT (0x00000170/4) /* Error-free 128-255B Frames RX */ +#define GEM_RX256CNT (0x00000174/4) /* Error-free 256-512B Frames RX */ +#define GEM_RX512CNT (0x00000178/4) /* Error-free 512-1023B Frames RX */ +#define GEM_RX1024CNT (0x0000017C/4) /* Error-free 1024-1518B Frames RX */ +#define GEM_RX1519CNT (0x00000180/4) /* Error-free 1519-max Frames RX */ +#define GEM_RXUNDERCNT (0x00000184/4) /* Undersize Frames Received */ +#define GEM_RXOVERCNT (0x00000188/4) /* Oversize Frames Received */ +#define GEM_RXJABCNT (0x0000018C/4) /* Jabbers Received Counter */ +#define GEM_RXFCSCNT (0x00000190/4) /* Frame Check seq. Error Counter */ +#define GEM_RXLENERRCNT (0x00000194/4) /* Length Field Error Counter */ +#define GEM_RXSYMERRCNT (0x00000198/4) /* Symbol Error Counter */ +#define GEM_RXALIGNERRCNT (0x0000019C/4) /* Alignment Error Counter */ +#define GEM_RXRSCERRCNT (0x000001A0/4) /* Receive Resource Error Counter */ +#define GEM_RXORUNCNT (0x000001A4/4) /* Receive Overrun Counter */ +#define GEM_RXIPCSERRCNT (0x000001A8/4) /* IP header Checksum Error Counter */ +#define GEM_RXTCPCCNT (0x000001AC/4) /* TCP Checksum Error Counter */ +#define GEM_RXUDPCCNT (0x000001B0/4) /* UDP Checksum Error Counter */ + +#define GEM_1588S (0x000001D0/4) /* 1588 Timer Seconds */ +#define GEM_1588NS (0x000001D4/4) /* 1588 Timer Nanoseconds */ +#define GEM_1588ADJ (0x000001D8/4) /* 1588 Timer Adjust */ +#define GEM_1588INC (0x000001DC/4) /* 1588 Timer Increment */ +#define GEM_PTPETXS (0x000001E0/4) /* PTP Event Frame Transmitted (s) */ +#define GEM_PTPETXNS (0x000001E4/4) /* PTP Event Frame Transmitted (ns) */ +#define GEM_PTPERXS (0x000001E8/4) /* PTP Event Frame Received (s) */ +#define GEM_PTPERXNS (0x000001EC/4) /* PTP Event Frame Received (ns) */ +#define GEM_PTPPTXS (0x000001E0/4) /* PTP Peer Frame Transmitted (s) */ +#define GEM_PTPPTXNS (0x000001E4/4) /* PTP Peer Frame Transmitted (ns) */ +#define GEM_PTPPRXS (0x000001E8/4) /* PTP Peer Frame Received (s) */ +#define GEM_PTPPRXNS (0x000001EC/4) /* PTP Peer Frame Received (ns) */ + +/* Design Configuration Registers */ +#define GEM_DESCONF (0x00000280/4) +#define GEM_DESCONF2 (0x00000284/4) +#define GEM_DESCONF3 (0x00000288/4) +#define GEM_DESCONF4 (0x0000028C/4) +#define GEM_DESCONF5 (0x00000290/4) +#define GEM_DESCONF6 (0x00000294/4) +#define GEM_DESCONF7 (0x00000298/4) + +#define GEM_MAXREG (0x00000640/4) /* Last valid GEM address */ + +/*****************************************/ +#define GEM_NWCTRL_TXSTART 0x00000200 /* Transmit Enable */ +#define GEM_NWCTRL_TXENA 0x00000008 /* Transmit Enable */ +#define GEM_NWCTRL_RXENA 0x00000004 /* Receive Enable */ +#define GEM_NWCTRL_LOCALLOOP 0x00000002 /* Local Loopback */ + +#define GEM_NWCFG_STRIP_FCS 0x00020000 /* Strip FCS field */ +#define GEM_NWCFG_LERR_DISC 0x00010000 /* Discard RX frames with lenth err */ +#define GEM_NWCFG_BUFF_OFST_M 0x0000C000 /* Receive buffer offset mask */ +#define GEM_NWCFG_BUFF_OFST_S 14 /* Receive buffer offset shift */ +#define GEM_NWCFG_UCAST_HASH 0x00000080 /* accept unicast if hash match */ +#define GEM_NWCFG_MCAST_HASH 0x00000040 /* accept multicast if hash match */ +#define GEM_NWCFG_BCAST_REJ 0x00000020 /* Reject broadcast packets */ +#define GEM_NWCFG_PROMISC 0x00000010 /* Accept all packets */ + +#define GEM_DMACFG_RBUFSZ_M 0x007F0000 /* DMA RX Buffer Size mask */ +#define GEM_DMACFG_RBUFSZ_S 16 /* DMA RX Buffer Size shift */ +#define GEM_DMACFG_RBUFSZ_MUL 64 /* DMA RX Buffer Size multiplier */ +#define GEM_DMACFG_TXCSUM_OFFL 0x00000800 /* Transmit checksum offload */ + +#define GEM_TXSTATUS_TXCMPL 0x00000020 /* Transmit Complete */ +#define GEM_TXSTATUS_USED 0x00000001 /* sw owned descriptor encountered */ + +#define GEM_RXSTATUS_FRMRCVD 0x00000002 /* Frame received */ +#define GEM_RXSTATUS_NOBUF 0x00000001 /* Buffer unavailable */ + +/* GEM_ISR GEM_IER GEM_IDR GEM_IMR */ +#define GEM_INT_TXCMPL 0x00000080 /* Transmit Complete */ +#define GEM_INT_TXUSED 0x00000008 +#define GEM_INT_RXUSED 0x00000004 +#define GEM_INT_RXCMPL 0x00000002 + +#define GEM_PHYMNTNC_OP_R 0x20000000 /* read operation */ +#define GEM_PHYMNTNC_OP_W 0x10000000 /* write operation */ +#define GEM_PHYMNTNC_ADDR 0x0F800000 /* Address bits */ +#define GEM_PHYMNTNC_ADDR_SHFT 23 +#define GEM_PHYMNTNC_REG 0x007C0000 /* register bits */ +#define GEM_PHYMNTNC_REG_SHIFT 18 + +/* Marvell PHY definitions */ +#define BOARD_PHY_ADDRESS 23 /* PHY address we will emulate a device at */ + +#define PHY_REG_CONTROL 0 +#define PHY_REG_STATUS 1 +#define PHY_REG_PHYID1 2 +#define PHY_REG_PHYID2 3 +#define PHY_REG_ANEGADV 4 +#define PHY_REG_LINKPABIL 5 +#define PHY_REG_ANEGEXP 6 +#define PHY_REG_NEXTP 7 +#define PHY_REG_LINKPNEXTP 8 +#define PHY_REG_100BTCTRL 9 +#define PHY_REG_1000BTSTAT 10 +#define PHY_REG_EXTSTAT 15 +#define PHY_REG_PHYSPCFC_CTL 16 +#define PHY_REG_PHYSPCFC_ST 17 +#define PHY_REG_INT_EN 18 +#define PHY_REG_INT_ST 19 +#define PHY_REG_EXT_PHYSPCFC_CTL 20 +#define PHY_REG_RXERR 21 +#define PHY_REG_EACD 22 +#define PHY_REG_LED 24 +#define PHY_REG_LED_OVRD 25 +#define PHY_REG_EXT_PHYSPCFC_CTL2 26 +#define PHY_REG_EXT_PHYSPCFC_ST 27 +#define PHY_REG_CABLE_DIAG 28 + +#define PHY_REG_CONTROL_RST 0x8000 +#define PHY_REG_CONTROL_LOOP 0x4000 +#define PHY_REG_CONTROL_ANEG 0x1000 + +#define PHY_REG_STATUS_LINK 0x0004 +#define PHY_REG_STATUS_ANEGCMPL 0x0020 + +#define PHY_REG_INT_ST_ANEGCMPL 0x0800 +#define PHY_REG_INT_ST_LINKC 0x0400 +#define PHY_REG_INT_ST_ENERGY 0x0010 + +/***********************************************************************/ +#define GEM_RX_REJECT 1 +#define GEM_RX_ACCEPT 0 + +/***********************************************************************/ + +#define DESC_1_USED 0x80000000 +#define DESC_1_LENGTH 0x00001FFF + +#define DESC_1_TX_WRAP 0x40000000 +#define DESC_1_TX_LAST 0x00008000 + +#define DESC_0_RX_WRAP 0x00000002 +#define DESC_0_RX_OWNERSHIP 0x00000001 + +#define DESC_1_RX_SOF 0x00004000 +#define DESC_1_RX_EOF 0x00008000 + +static inline unsigned tx_desc_get_buffer(unsigned *desc) +{ + return desc[0]; +} + +static inline unsigned tx_desc_get_used(unsigned *desc) +{ + return (desc[1] & DESC_1_USED) ? 1 : 0; +} + +static inline void tx_desc_set_used(unsigned *desc) +{ + desc[1] |= DESC_1_USED; +} + +static inline unsigned tx_desc_get_wrap(unsigned *desc) +{ + return (desc[1] & DESC_1_TX_WRAP) ? 1 : 0; +} + +static inline unsigned tx_desc_get_last(unsigned *desc) +{ + return (desc[1] & DESC_1_TX_LAST) ? 1 : 0; +} + +static inline unsigned tx_desc_get_length(unsigned *desc) +{ + return desc[1] & DESC_1_LENGTH; +} + +static inline void print_gem_tx_desc(unsigned *desc) +{ + DB_PRINT("TXDESC:\n"); + DB_PRINT("bufaddr: 0x%08x\n", *desc); + DB_PRINT("used_hw: %d\n", tx_desc_get_used(desc)); + DB_PRINT("wrap: %d\n", tx_desc_get_wrap(desc)); + DB_PRINT("last: %d\n", tx_desc_get_last(desc)); + DB_PRINT("length: %d\n", tx_desc_get_length(desc)); +} + +static inline unsigned rx_desc_get_buffer(unsigned *desc) +{ + return desc[0] & ~0x3UL; +} + +static inline unsigned rx_desc_get_wrap(unsigned *desc) +{ + return desc[0] & DESC_0_RX_WRAP ? 1 : 0; +} + +static inline unsigned rx_desc_get_ownership(unsigned *desc) +{ + return desc[0] & DESC_0_RX_OWNERSHIP ? 1 : 0; +} + +static inline void rx_desc_set_ownership(unsigned *desc) +{ + desc[0] |= DESC_0_RX_OWNERSHIP; +} + +static inline void rx_desc_set_sof(unsigned *desc) +{ + desc[1] |= DESC_1_RX_SOF; +} + +static inline void rx_desc_set_eof(unsigned *desc) +{ + desc[1] |= DESC_1_RX_EOF; +} + +static inline void rx_desc_set_length(unsigned *desc, unsigned len) +{ + desc[1] &= ~DESC_1_LENGTH; + desc[1] |= len; +} + +typedef struct { + SysBusDevice busdev; + MemoryRegion iomem; + NICState *nic; + NICConf conf; + qemu_irq irq; + + /* GEM registers backing store */ + uint32_t regs[GEM_MAXREG]; + /* Mask of register bits which are write only */ + uint32_t regs_wo[GEM_MAXREG]; + /* Mask of register bits which are read only */ + uint32_t regs_ro[GEM_MAXREG]; + /* Mask of register bits which are clear on read */ + uint32_t regs_rtc[GEM_MAXREG]; + /* Mask of register bits which are write 1 to clear */ + uint32_t regs_w1c[GEM_MAXREG]; + + /* PHY registers backing store */ + uint16_t phy_regs[32]; + + uint8_t phy_loop; /* Are we in phy loopback? */ + + /* The current DMA descriptor pointers */ + target_phys_addr_t rx_desc_addr; + target_phys_addr_t tx_desc_addr; + +} GemState; + +/* The broadcast MAC address: 0xFFFFFFFFFFFF */ +const uint8_t broadcast_addr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + +/* + * gem_init_register_masks: + * One time initialization. + * Set masks to identify which register bits have magical clear properties + */ +static void gem_init_register_masks(GemState *s) +{ + /* Mask of register bits which are read only*/ + memset(&s->regs_ro[0], 0, sizeof(s->regs_ro)); + s->regs_ro[GEM_NWCTRL] = 0xFFF80000; + s->regs_ro[GEM_NWSTATUS] = 0xFFFFFFFF; + s->regs_ro[GEM_DMACFG] = 0xFE00F000; + s->regs_ro[GEM_TXSTATUS] = 0xFFFFFE08; + s->regs_ro[GEM_RXQBASE] = 0x00000003; + s->regs_ro[GEM_TXQBASE] = 0x00000003; + s->regs_ro[GEM_RXSTATUS] = 0xFFFFFFF0; + s->regs_ro[GEM_ISR] = 0xFFFFFFFF; + s->regs_ro[GEM_IMR] = 0xFFFFFFFF; + s->regs_ro[GEM_MODID] = 0xFFFFFFFF; + + /* Mask of register bits which are clear on read */ + memset(&s->regs_rtc[0], 0, sizeof(s->regs_rtc)); + s->regs_rtc[GEM_ISR] = 0xFFFFFFFF; + + /* Mask of register bits which are write 1 to clear */ + memset(&s->regs_w1c[0], 0, sizeof(s->regs_w1c)); + s->regs_w1c[GEM_TXSTATUS] = 0x000001F7; + s->regs_w1c[GEM_RXSTATUS] = 0x0000000F; + + /* Mask of register bits which are write only */ + memset(&s->regs_wo[0], 0, sizeof(s->regs_wo)); + s->regs_wo[GEM_NWCTRL] = 0x00073E60; + s->regs_wo[GEM_IER] = 0x07FFFFFF; + s->regs_wo[GEM_IDR] = 0x07FFFFFF; +} + +/* + * phy_update_link: + * Make the emulated PHY link state match the QEMU "interface" state. + */ +static void phy_update_link(GemState *s) +{ + DB_PRINT("down %d\n", s->nic->nc.link_down); + + /* Autonegotiation status mirrors link status. */ + if (s->nic->nc.link_down) { + s->phy_regs[PHY_REG_STATUS] &= ~(PHY_REG_STATUS_ANEGCMPL | + PHY_REG_STATUS_LINK); + s->phy_regs[PHY_REG_INT_ST] |= PHY_REG_INT_ST_LINKC; + } else { + s->phy_regs[PHY_REG_STATUS] |= (PHY_REG_STATUS_ANEGCMPL | + PHY_REG_STATUS_LINK); + s->phy_regs[PHY_REG_INT_ST] |= (PHY_REG_INT_ST_LINKC | + PHY_REG_INT_ST_ANEGCMPL | + PHY_REG_INT_ST_ENERGY); + } +} + +static int gem_can_receive(VLANClientState *nc) +{ + GemState *s; + + s = DO_UPCAST(NICState, nc, nc)->opaque; + + DB_PRINT("\n"); + + /* Do nothing if receive is not enabled. */ + if (!(s->regs[GEM_NWCTRL] & GEM_NWCTRL_RXENA)) { + return 0; + } + + return 1; +} + +/* + * gem_update_int_status: + * Raise or lower interrupt based on current status. + */ +static void gem_update_int_status(GemState *s) +{ + uint32_t new_interrupts = 0; + /* Packet transmitted ? */ + if (s->regs[GEM_TXSTATUS] & GEM_TXSTATUS_TXCMPL) { + new_interrupts |= GEM_INT_TXCMPL; + } + /* End of TX ring ? */ + if (s->regs[GEM_TXSTATUS] & GEM_TXSTATUS_USED) { + new_interrupts |= GEM_INT_TXUSED; + } + + /* Frame received ? */ + if (s->regs[GEM_RXSTATUS] & GEM_RXSTATUS_FRMRCVD) { + new_interrupts |= GEM_INT_RXCMPL; + } + /* RX ring full ? */ + if (s->regs[GEM_RXSTATUS] & GEM_RXSTATUS_NOBUF) { + new_interrupts |= GEM_INT_RXUSED; + } + + s->regs[GEM_ISR] |= new_interrupts & ~(s->regs[GEM_IMR]); + + if (s->regs[GEM_ISR]) { + DB_PRINT("asserting int. (0x%08x)\n", s->regs[GEM_ISR]); + qemu_set_irq(s->irq, 1); + } else { + qemu_set_irq(s->irq, 0); + } +} + +/* + * gem_receive_updatestats: + * Increment receive statistics. + */ +static void gem_receive_updatestats(GemState *s, const uint8_t *packet, + unsigned bytes) +{ + uint64_t octets; + + /* Total octets (bytes) received */ + octets = ((uint64_t)(s->regs[GEM_OCTRXLO]) << 32) | + s->regs[GEM_OCTRXHI]; + octets += bytes; + s->regs[GEM_OCTRXLO] = octets >> 32; + s->regs[GEM_OCTRXHI] = octets; + + /* Error-free Frames received */ + s->regs[GEM_RXCNT]++; + + /* Error-free Broadcast Frames counter */ + if (!memcmp(packet, broadcast_addr, 6)) { + s->regs[GEM_RXBROADCNT]++; + } + + /* Error-free Multicast Frames counter */ + if (packet[0] == 0x01) { + s->regs[GEM_RXMULTICNT]++; + } + + if (bytes <= 64) { + s->regs[GEM_RX64CNT]++; + } else if (bytes <= 127) { + s->regs[GEM_RX65CNT]++; + } else if (bytes <= 255) { + s->regs[GEM_RX128CNT]++; + } else if (bytes <= 511) { + s->regs[GEM_RX256CNT]++; + } else if (bytes <= 1023) { + s->regs[GEM_RX512CNT]++; + } else if (bytes <= 1518) { + s->regs[GEM_RX1024CNT]++; + } else { + s->regs[GEM_RX1519CNT]++; + } +} + +/* + * Get the MAC Address bit from the specified position + */ +static unsigned get_bit(const uint8_t *mac, unsigned bit) +{ + unsigned byte; + + byte = mac[bit / 8]; + byte >>= (bit & 0x7); + byte &= 1; + + return byte; +} + +/* + * Calculate a GEM MAC Address hash index + */ +static unsigned calc_mac_hash(const uint8_t *mac) +{ + int index_bit, mac_bit; + unsigned hash_index; + + hash_index = 0; + mac_bit = 5; + for (index_bit = 5; index_bit >= 0; index_bit--) { + hash_index |= (get_bit(mac, mac_bit) ^ + get_bit(mac, mac_bit + 6) ^ + get_bit(mac, mac_bit + 12) ^ + get_bit(mac, mac_bit + 18) ^ + get_bit(mac, mac_bit + 24) ^ + get_bit(mac, mac_bit + 30) ^ + get_bit(mac, mac_bit + 36) ^ + get_bit(mac, mac_bit + 42)) << index_bit; + mac_bit--; + } + + return hash_index; +} + +/* + * gem_mac_address_filter: + * Accept or reject this destination address? + * Returns: + * GEM_RX_REJECT: reject + * GEM_RX_ACCEPT: accept + */ +static int gem_mac_address_filter(GemState *s, const uint8_t *packet) +{ + uint8_t *gem_spaddr; + int i; + + /* Promiscuous mode? */ + if (s->regs[GEM_NWCFG] & GEM_NWCFG_PROMISC) { + return GEM_RX_ACCEPT; + } + + if (!memcmp(packet, broadcast_addr, 6)) { + /* Reject broadcast packets? */ + if (s->regs[GEM_NWCFG] & GEM_NWCFG_BCAST_REJ) { + return GEM_RX_REJECT; + } + return GEM_RX_ACCEPT; + } + + /* Accept packets -w- hash match? */ + if ((packet[0] == 0x01 && (s->regs[GEM_NWCFG] & GEM_NWCFG_MCAST_HASH)) || + (packet[0] != 0x01 && (s->regs[GEM_NWCFG] & GEM_NWCFG_UCAST_HASH))) { + unsigned hash_index; + + hash_index = calc_mac_hash(packet); + if (hash_index < 32) { + if (s->regs[GEM_HASHLO] & (1<<hash_index)) { + return GEM_RX_ACCEPT; + } + } else { + hash_index -= 32; + if (s->regs[GEM_HASHHI] & (1<<hash_index)) { + return GEM_RX_ACCEPT; + } + } + } + + /* Check all 4 specific addresses */ + gem_spaddr = (uint8_t *)&(s->regs[GEM_SPADDR1LO]); + for (i = 0; i < 4; i++) { + if (!memcmp(packet, gem_spaddr, 6)) { + return GEM_RX_ACCEPT; + } + + gem_spaddr += 8; + } + + /* No address match; reject the packet */ + return GEM_RX_REJECT; +} + +/* + * gem_receive: + * Fit a packet handed to us by QEMU into the receive descriptor ring. + */ +static ssize_t gem_receive(VLANClientState *nc, const uint8_t *buf, size_t size) +{ + unsigned desc[2]; + target_phys_addr_t packet_desc_addr, last_desc_addr; + GemState *s; + unsigned rxbufsize, bytes_to_copy; + unsigned rxbuf_offset; + uint8_t rxbuf[2048]; + uint8_t *rxbuf_ptr; + + s = DO_UPCAST(NICState, nc, nc)->opaque; + + /* Do nothing if receive is not enabled. */ + if (!(s->regs[GEM_NWCTRL] & GEM_NWCTRL_RXENA)) { + return -1; + } + + /* Is this destination MAC address "for us" ? */ + if (gem_mac_address_filter(s, buf) == GEM_RX_REJECT) { + return -1; + } + + /* Discard packets with receive length error enabled ? */ + if (s->regs[GEM_NWCFG] & GEM_NWCFG_LERR_DISC) { + unsigned type_len; + + /* Fish the ethertype / length field out of the RX packet */ + type_len = buf[12] << 8 | buf[13]; + /* It is a length field, not an ethertype */ + if (type_len < 0x600) { + if (size < type_len) { + /* discard */ + return -1; + } + } + } + + /* + * Determine configured receive buffer offset (probably 0) + */ + rxbuf_offset = (s->regs[GEM_NWCFG] & GEM_NWCFG_BUFF_OFST_M) >> + GEM_NWCFG_BUFF_OFST_S; + + /* The configure size of each receive buffer. Determines how many + * buffers needed to hold this packet. + */ + rxbufsize = ((s->regs[GEM_DMACFG] & GEM_DMACFG_RBUFSZ_M) >> + GEM_DMACFG_RBUFSZ_S) * GEM_DMACFG_RBUFSZ_MUL; + bytes_to_copy = size; + + /* Strip of FCS field ? (usually yes) */ + if (s->regs[GEM_NWCFG] & GEM_NWCFG_STRIP_FCS) { + rxbuf_ptr = (void *)buf; + } else { + unsigned crc_val; + int crc_offset; + + /* The application wants the FCS field, which QEMU does not provide. + * We must try and caclculate one. + */ + + memcpy(rxbuf, buf, size); + memset(rxbuf + size, 0, sizeof(rxbuf - size)); + rxbuf_ptr = rxbuf; + crc_val = cpu_to_le32(crc32(0, rxbuf, MAX(size, 60))); + if (size < 60) { + crc_offset = 60; + } else { + crc_offset = size; + } + memcpy(rxbuf + crc_offset, &crc_val, sizeof(crc_val)); + + bytes_to_copy += 4; + size += 4; + } + + /* Pad to minimum length */ + if (size < 64) { + size = 64; + } + + DB_PRINT("config bufsize: %d packet size: %ld\n", rxbufsize, size); + + packet_desc_addr = s->rx_desc_addr; + while (1) { + DB_PRINT("read descriptor 0x%x\n", packet_desc_addr); + /* read current descriptor */ + cpu_physical_memory_read(packet_desc_addr, + (uint8_t *)&desc[0], sizeof(desc)); + + /* Descriptor owned by software ? */ + if (rx_desc_get_ownership(desc) == 1) { + DB_PRINT("descriptor 0x%x owned by sw.\n", packet_desc_addr); + s->regs[GEM_RXSTATUS] |= GEM_RXSTATUS_NOBUF; + /* Handle interrupt consequences */ + gem_update_int_status(s); + return -1; + } + + DB_PRINT("copy %d bytes to 0x%x\n", MIN(bytes_to_copy, rxbufsize), + rx_desc_get_buffer(desc)); + + /* + * Let's have QEMU lend a helping hand. + */ + if (rx_desc_get_buffer(desc) == 0) { + DB_PRINT("Invalid RX buffer (NULL) for descriptor 0x%x\n", + packet_desc_addr); + break; + } + + /* Copy packet data to emulated DMA buffer */ + cpu_physical_memory_write(rx_desc_get_buffer(desc) + rxbuf_offset, + rxbuf_ptr, MIN(bytes_to_copy, rxbufsize)); + bytes_to_copy -= MIN(bytes_to_copy, rxbufsize); + rxbuf_ptr += MIN(bytes_to_copy, rxbufsize); + if (bytes_to_copy == 0) { + break; + } + + /* Next descriptor */ + if (rx_desc_get_wrap(desc)) { + packet_desc_addr = s->regs[GEM_RXQBASE]; + } else { + packet_desc_addr += 8; + } + } + + DB_PRINT("set length: %ld, EOF on descriptor 0x%x\n", size, + (unsigned)packet_desc_addr); + + /* Update last descriptor with EOF and total length */ + rx_desc_set_eof(desc); + rx_desc_set_length(desc, size); + cpu_physical_memory_write(packet_desc_addr, + (uint8_t *)&desc[0], sizeof(desc)); + + /* Advance RX packet descriptor Q */ + last_desc_addr = packet_desc_addr; + packet_desc_addr = s->rx_desc_addr; + s->rx_desc_addr = last_desc_addr; + if (rx_desc_get_wrap(desc)) { + s->rx_desc_addr = s->regs[GEM_RXQBASE]; + } else { + s->rx_desc_addr += 8; + } + + DB_PRINT("set SOF, OWN on descriptor 0x%08x\n", packet_desc_addr); + + /* Count it */ + gem_receive_updatestats(s, buf, size); + + /* Update first descriptor (which could also be the last) */ + /* read descriptor */ + cpu_physical_memory_read(packet_desc_addr, + (uint8_t *)&desc[0], sizeof(desc)); + rx_desc_set_sof(desc); + rx_desc_set_ownership(desc); + cpu_physical_memory_write(packet_desc_addr, + (uint8_t *)&desc[0], sizeof(desc)); + + s->regs[GEM_RXSTATUS] |= GEM_RXSTATUS_FRMRCVD; + + /* Handle interrupt consequences */ + gem_update_int_status(s); + + return size; +} + +/* + * gem_transmit_updatestats: + * Increment transmit statistics. + */ +static void gem_transmit_updatestats(GemState *s, const uint8_t *packet, + unsigned bytes) +{ + uint64_t octets; + + /* Total octets (bytes) transmitted */ + octets = ((uint64_t)(s->regs[GEM_OCTTXLO]) << 32) | + s->regs[GEM_OCTTXHI]; + octets += bytes; + s->regs[GEM_OCTTXLO] = octets >> 32; + s->regs[GEM_OCTTXHI] = octets; + + /* Error-free Frames transmitted */ + s->regs[GEM_TXCNT]++; + + /* Error-free Broadcast Frames counter */ + if (!memcmp(packet, broadcast_addr, 6)) { + s->regs[GEM_TXBCNT]++; + } + + /* Error-free Multicast Frames counter */ + if (packet[0] == 0x01) { + s->regs[GEM_TXMCNT]++; + } + + if (bytes <= 64) { + s->regs[GEM_TX64CNT]++; + } else if (bytes <= 127) { + s->regs[GEM_TX65CNT]++; + } else if (bytes <= 255) { + s->regs[GEM_TX128CNT]++; + } else if (bytes <= 511) { + s->regs[GEM_TX256CNT]++; + } else if (bytes <= 1023) { + s->regs[GEM_TX512CNT]++; + } else if (bytes <= 1518) { + s->regs[GEM_TX1024CNT]++; + } else { + s->regs[GEM_TX1519CNT]++; + } +} + +/* + * gem_transmit: + * Fish packets out of the descriptor ring and feed them to QEMU + */ +static void gem_transmit(GemState *s) +{ + unsigned desc[2]; + target_phys_addr_t packet_desc_addr; + uint8_t tx_packet[2048]; + uint8_t *p; + unsigned total_bytes; + + /* Do nothing if transmit is not enabled. */ + if (!(s->regs[GEM_NWCTRL] & GEM_NWCTRL_TXENA)) { + return; + } + + DB_PRINT("\n"); + + /* The packet we will hand off to qemu. + * Packets scattered across multiple descriptors are gathered to this + * one contiguous buffer first. + */ + p = tx_packet; + total_bytes = 0; + + /* read current descriptor */ + packet_desc_addr = s->tx_desc_addr; + cpu_physical_memory_read(packet_desc_addr, + (uint8_t *)&desc[0], sizeof(desc)); + /* Handle all descriptors owned by hardware */ + while (tx_desc_get_used(desc) == 0) { + + /* Do nothing if transmit is not enabled. */ + if (!(s->regs[GEM_NWCTRL] & GEM_NWCTRL_TXENA)) { + return; + } + print_gem_tx_desc(desc); + + /* The real hardware would eat this (and possibly crash). + * For QEMU let's lend a helping hand. + */ + if ((tx_desc_get_buffer(desc) == 0) || + (tx_desc_get_length(desc) == 0)) { + DB_PRINT("Invalid TX descriptor @ 0x%x\n", packet_desc_addr); + break; + } + + /* Gather this fragment of the packet from "dma memory" to our contig. + * buffer. + */ + cpu_physical_memory_read(tx_desc_get_buffer(desc), p, + tx_desc_get_length(desc)); + p += tx_desc_get_length(desc); + total_bytes += tx_desc_get_length(desc); + + /* Last descriptor for this packet; hand the whole thing off */ + if (tx_desc_get_last(desc)) { + /* Modify the 1st descriptor of this packet to be owned by + * the processor. + */ + cpu_physical_memory_read(s->tx_desc_addr, + (uint8_t *)&desc[0], sizeof(desc)); + tx_desc_set_used(desc); + cpu_physical_memory_write(s->tx_desc_addr, + (uint8_t *)&desc[0], sizeof(desc)); + /* Advance the hardare current descriptor past this packet */ + if (tx_desc_get_wrap(desc)) { + s->tx_desc_addr = s->regs[GEM_TXQBASE]; + } else { + s->tx_desc_addr = packet_desc_addr + 8; + } + DB_PRINT("TX descriptor next: 0x%08x\n", s->tx_desc_addr); + + s->regs[GEM_TXSTATUS] |= GEM_TXSTATUS_TXCMPL; + + /* Handle interrupt consequences */ + gem_update_int_status(s); + + /* Is checksum offload enabled? */ + if (s->regs[GEM_DMACFG] & GEM_DMACFG_TXCSUM_OFFL) { + net_checksum_calculate(tx_packet, total_bytes); + } + + /* Update MAC statistics */ + gem_transmit_updatestats(s, tx_packet, total_bytes); + + /* Send the packet somewhere */ + if (s->phy_loop) { + gem_receive(&s->nic->nc, tx_packet, total_bytes); + } else { + qemu_send_packet(&s->nic->nc, tx_packet, total_bytes); + } + + /* Prepare for next packet */ + p = tx_packet; + total_bytes = 0; + } + + /* read next descriptor */ + if (tx_desc_get_wrap(desc)) { + packet_desc_addr = s->regs[GEM_TXQBASE]; + } else { + packet_desc_addr += 8; + } + cpu_physical_memory_read(packet_desc_addr, + (uint8_t *)&desc[0], sizeof(desc)); + } + + if (tx_desc_get_used(desc)) { + s->regs[GEM_TXSTATUS] |= GEM_TXSTATUS_USED; + gem_update_int_status(s); + } +} + +static void gem_phy_reset(GemState *s) +{ + memset(&s->phy_regs[0], 0, sizeof(s->phy_regs)); + s->phy_regs[PHY_REG_CONTROL] = 0x1140; + s->phy_regs[PHY_REG_STATUS] = 0x7969; + s->phy_regs[PHY_REG_PHYID1] = 0x0141; + s->phy_regs[PHY_REG_PHYID2] = 0x0CC2; + s->phy_regs[PHY_REG_ANEGADV] = 0x01E1; + s->phy_regs[PHY_REG_LINKPABIL] = 0xCDE1; + s->phy_regs[PHY_REG_ANEGEXP] = 0x000F; + s->phy_regs[PHY_REG_NEXTP] = 0x2001; + s->phy_regs[PHY_REG_LINKPNEXTP] = 0x40E6; + s->phy_regs[PHY_REG_100BTCTRL] = 0x0300; + s->phy_regs[PHY_REG_1000BTSTAT] = 0x7C00; + s->phy_regs[PHY_REG_EXTSTAT] = 0x3000; + s->phy_regs[PHY_REG_PHYSPCFC_CTL] = 0x0078; + s->phy_regs[PHY_REG_PHYSPCFC_ST] = 0xBC00; + s->phy_regs[PHY_REG_EXT_PHYSPCFC_CTL] = 0x0C60; + s->phy_regs[PHY_REG_LED] = 0x4100; + s->phy_regs[PHY_REG_EXT_PHYSPCFC_CTL2] = 0x000A; + s->phy_regs[PHY_REG_EXT_PHYSPCFC_ST] = 0x848B; + + phy_update_link(s); +} + +static void gem_reset(DeviceState *d) +{ + GemState *s = FROM_SYSBUS(GemState, sysbus_from_qdev(d)); + + DB_PRINT("\n"); + + /* Set post reset register values */ + memset(&s->regs[0], 0, sizeof(s->regs)); + s->regs[GEM_NWCFG] = 0x00080000; + s->regs[GEM_NWSTATUS] = 0x00000006; + s->regs[GEM_DMACFG] = 0x00020784; + s->regs[GEM_IMR] = 0x07ffffff; + s->regs[GEM_TXPAUSE] = 0x0000ffff; + s->regs[GEM_TXPARTIALSF] = 0x000003ff; + s->regs[GEM_RXPARTIALSF] = 0x000003ff; + s->regs[GEM_MODID] = 0x00020118; + s->regs[GEM_DESCONF] = 0x02500111; + s->regs[GEM_DESCONF2] = 0x2ab13fff; + s->regs[GEM_DESCONF5] = 0x002f2145; + s->regs[GEM_DESCONF6] = 0x00000200; + + gem_phy_reset(s); + + gem_update_int_status(s); +} + +static uint16_t gem_phy_read(GemState *s, unsigned reg_num) +{ + DB_PRINT("reg: %d value: 0x%04x\n", reg_num, s->phy_regs[reg_num]); + return s->phy_regs[reg_num]; +} + +static void gem_phy_write(GemState *s, unsigned reg_num, uint16_t val) +{ + DB_PRINT("reg: %d value: 0x%04x\n", reg_num, val); + + switch (reg_num) { + case PHY_REG_CONTROL: + if (val & PHY_REG_CONTROL_RST) { + /* Phy reset */ + gem_phy_reset(s); + val &= ~(PHY_REG_CONTROL_RST | PHY_REG_CONTROL_LOOP); + s->phy_loop = 0; + } + if (val & PHY_REG_CONTROL_ANEG) { + /* Complete autonegotiation immediately */ + val &= ~PHY_REG_CONTROL_ANEG; + s->phy_regs[PHY_REG_STATUS] |= PHY_REG_STATUS_ANEGCMPL; + } + if (val & PHY_REG_CONTROL_LOOP) { + DB_PRINT("PHY placed in loopback\n"); + s->phy_loop = 1; + } else { + s->phy_loop = 0; + } + break; + } + s->phy_regs[reg_num] = val; +} + +/* + * gem_read32: + * Read a GEM register. + */ +static uint64_t gem_read(void *opaque, target_phys_addr_t offset, unsigned size) +{ + GemState *s; + uint32_t retval; + + s = (GemState *)opaque; + + offset >>= 2; + retval = s->regs[offset]; + + DB_PRINT("offset: 0x%04x read: 0x%08x\n", offset*4, retval); + + switch (offset) { + case GEM_ISR: + qemu_set_irq(s->irq, 0); + break; + case GEM_PHYMNTNC: + if (retval & GEM_PHYMNTNC_OP_R) { + uint32_t phy_addr, reg_num; + + phy_addr = (retval & GEM_PHYMNTNC_ADDR) >> GEM_PHYMNTNC_ADDR_SHFT; + if (phy_addr == BOARD_PHY_ADDRESS) { + reg_num = (retval & GEM_PHYMNTNC_REG) >> GEM_PHYMNTNC_REG_SHIFT; + retval &= 0xFFFF0000; + retval |= gem_phy_read(s, reg_num); + } else { + retval |= 0xFFFF; /* No device at this address */ + } + } + break; + } + + /* Squash read to clear bits */ + s->regs[offset] &= ~(s->regs_rtc[offset]); + + /* Do not provide write only bits */ + retval &= ~(s->regs_wo[offset]); + + DB_PRINT("0x%08x\n", retval); + return retval; +} + +/* + * gem_write32: + * Write a GEM register. + */ +static void gem_write(void *opaque, target_phys_addr_t offset, uint64_t val, + unsigned size) +{ + GemState *s = (GemState *)opaque; + uint32_t readonly; + + DB_PRINT("offset: 0x%04x write: 0x%08x ", offset, (unsigned)val); + offset >>= 2; + + /* Squash bits which are read only in write value */ + val &= ~(s->regs_ro[offset]); + /* Preserve (only) bits which are read only in register */ + readonly = s->regs[offset]; + readonly &= s->regs_ro[offset]; + + /* Squash bits which are write 1 to clear */ + val &= ~(s->regs_w1c[offset] & val); + + /* Copy register write to backing store */ + s->regs[offset] = val | readonly; + + /* Handle register write side effects */ + switch (offset) { + case GEM_NWCTRL: + if (val & GEM_NWCTRL_TXSTART) { + gem_transmit(s); + } + if (!(val & GEM_NWCTRL_TXENA)) { + /* Reset to start of Q when transmit disabled. */ + s->tx_desc_addr = s->regs[GEM_TXQBASE]; + } + if (!(val & GEM_NWCTRL_RXENA)) { + /* Reset to start of Q when receive disabled. */ + s->rx_desc_addr = s->regs[GEM_RXQBASE]; + } + break; + + case GEM_TXSTATUS: + gem_update_int_status(s); + break; + case GEM_RXQBASE: + s->rx_desc_addr = val; + break; + case GEM_TXQBASE: + s->tx_desc_addr = val; + break; + case GEM_RXSTATUS: + gem_update_int_status(s); + break; + case GEM_IER: + s->regs[GEM_IMR] &= ~val; + gem_update_int_status(s); + break; + case GEM_IDR: + s->regs[GEM_IMR] |= val; + gem_update_int_status(s); + break; + case GEM_PHYMNTNC: + if (val & GEM_PHYMNTNC_OP_W) { + uint32_t phy_addr, reg_num; + + phy_addr = (val & GEM_PHYMNTNC_ADDR) >> GEM_PHYMNTNC_ADDR_SHFT; + if (phy_addr == BOARD_PHY_ADDRESS) { + reg_num = (val & GEM_PHYMNTNC_REG) >> GEM_PHYMNTNC_REG_SHIFT; + gem_phy_write(s, reg_num, val); + } + } + break; + } + + DB_PRINT("newval: 0x%08x\n", s->regs[offset]); +} + +static const MemoryRegionOps gem_ops = { + .read = gem_read, + .write = gem_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void gem_cleanup(VLANClientState *nc) +{ + GemState *s = DO_UPCAST(NICState, nc, nc)->opaque; + + DB_PRINT("\n"); + s->nic = NULL; +} + +static void gem_set_link(VLANClientState *nc) +{ + DB_PRINT("\n"); + phy_update_link(DO_UPCAST(NICState, nc, nc)->opaque); +} + +static NetClientInfo net_gem_info = { + .type = NET_CLIENT_TYPE_NIC, + .size = sizeof(NICState), + .can_receive = gem_can_receive, + .receive = gem_receive, + .cleanup = gem_cleanup, + .link_status_changed = gem_set_link, +}; + +static int gem_init(SysBusDevice *dev) +{ + GemState *s; + + DB_PRINT("\n"); + + s = FROM_SYSBUS(GemState, dev); + gem_init_register_masks(s); + memory_region_init_io(&s->iomem, &gem_ops, s, "enet", sizeof(s->regs)); + sysbus_init_mmio(dev, &s->iomem); + sysbus_init_irq(dev, &s->irq); + qemu_macaddr_default_if_unset(&s->conf.macaddr); + + s->nic = qemu_new_nic(&net_gem_info, &s->conf, + object_get_typename(OBJECT(dev)), dev->qdev.id, s); + + return 0; +} + +static const VMStateDescription vmstate_cadence_gem = { + .name = "cadence_gem", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, GemState, GEM_MAXREG), + VMSTATE_UINT16_ARRAY(phy_regs, GemState, 32), + VMSTATE_UINT8(phy_loop, GemState), + VMSTATE_UINT32(rx_desc_addr, GemState), + VMSTATE_UINT32(tx_desc_addr, GemState), + } +}; + +static Property gem_properties[] = { + DEFINE_NIC_PROPERTIES(GemState, conf), + DEFINE_PROP_END_OF_LIST(), +}; + +static void gem_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); + + sdc->init = gem_init; + dc->props = gem_properties; + dc->vmsd = &vmstate_cadence_gem; + dc->reset = gem_reset; +} + +static TypeInfo gem_info = { + .class_init = gem_class_init, + .name = "cadence_gem", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(GemState), +}; + +static void gem_register_types(void) +{ + type_register_static(&gem_info); +} + +type_init(gem_register_types) diff --git a/hw/cadence_ttc.c b/hw/cadence_ttc.c new file mode 100644 index 0000000000..2b5477b688 --- /dev/null +++ b/hw/cadence_ttc.c @@ -0,0 +1,489 @@ +/* + * Xilinx Zynq cadence TTC model + * + * Copyright (c) 2011 Xilinx Inc. + * Copyright (c) 2012 Peter A.G. Crosthwaite (peter.crosthwaite@petalogix.com) + * Copyright (c) 2012 PetaLogix Pty Ltd. + * Written By Haibing Ma + * M. Habib + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "sysbus.h" +#include "qemu-timer.h" + +#ifdef CADENCE_TTC_ERR_DEBUG +#define DB_PRINT(...) do { \ + fprintf(stderr, ": %s: ", __func__); \ + fprintf(stderr, ## __VA_ARGS__); \ + } while (0); +#else + #define DB_PRINT(...) +#endif + +#define COUNTER_INTR_IV 0x00000001 +#define COUNTER_INTR_M1 0x00000002 +#define COUNTER_INTR_M2 0x00000004 +#define COUNTER_INTR_M3 0x00000008 +#define COUNTER_INTR_OV 0x00000010 +#define COUNTER_INTR_EV 0x00000020 + +#define COUNTER_CTRL_DIS 0x00000001 +#define COUNTER_CTRL_INT 0x00000002 +#define COUNTER_CTRL_DEC 0x00000004 +#define COUNTER_CTRL_MATCH 0x00000008 +#define COUNTER_CTRL_RST 0x00000010 + +#define CLOCK_CTRL_PS_EN 0x00000001 +#define CLOCK_CTRL_PS_V 0x0000001e + +typedef struct { + QEMUTimer *timer; + int freq; + + uint32_t reg_clock; + uint32_t reg_count; + uint32_t reg_value; + uint16_t reg_interval; + uint16_t reg_match[3]; + uint32_t reg_intr; + uint32_t reg_intr_en; + uint32_t reg_event_ctrl; + uint32_t reg_event; + + uint64_t cpu_time; + unsigned int cpu_time_valid; + + qemu_irq irq; +} CadenceTimerState; + +typedef struct { + SysBusDevice busdev; + MemoryRegion iomem; + CadenceTimerState timer[3]; +} CadenceTTCState; + +static void cadence_timer_update(CadenceTimerState *s) +{ + qemu_set_irq(s->irq, !!(s->reg_intr & s->reg_intr_en)); +} + +static CadenceTimerState *cadence_timer_from_addr(void *opaque, + target_phys_addr_t offset) +{ + unsigned int index; + CadenceTTCState *s = (CadenceTTCState *)opaque; + + index = (offset >> 2) % 3; + + return &s->timer[index]; +} + +static uint64_t cadence_timer_get_ns(CadenceTimerState *s, uint64_t timer_steps) +{ + /* timer_steps has max value of 0x100000000. double check it + * (or overflow can happen below) */ + assert(timer_steps <= 1ULL << 32); + + uint64_t r = timer_steps * 1000000000ULL; + if (s->reg_clock & CLOCK_CTRL_PS_EN) { + r >>= 16 - (((s->reg_clock & CLOCK_CTRL_PS_V) >> 1) + 1); + } else { + r >>= 16; + } + r /= (uint64_t)s->freq; + return r; +} + +static uint64_t cadence_timer_get_steps(CadenceTimerState *s, uint64_t ns) +{ + uint64_t to_divide = 1000000000ULL; + + uint64_t r = ns; + /* for very large intervals (> 8s) do some division first to stop + * overflow (costs some prescision) */ + while (r >= 8ULL << 30 && to_divide > 1) { + r /= 1000; + to_divide /= 1000; + } + r <<= 16; + /* keep early-dividing as needed */ + while (r >= 8ULL << 30 && to_divide > 1) { + r /= 1000; + to_divide /= 1000; + } + r *= (uint64_t)s->freq; + if (s->reg_clock & CLOCK_CTRL_PS_EN) { + r /= 1 << (((s->reg_clock & CLOCK_CTRL_PS_V) >> 1) + 1); + } + + r /= to_divide; + return r; +} + +/* determine if x is in between a and b, exclusive of a, inclusive of b */ + +static inline int64_t is_between(int64_t x, int64_t a, int64_t b) +{ + if (a < b) { + return x > a && x <= b; + } + return x < a && x >= b; +} + +static void cadence_timer_run(CadenceTimerState *s) +{ + int i; + int64_t event_interval, next_value; + + assert(s->cpu_time_valid); /* cadence_timer_sync must be called first */ + + if (s->reg_count & COUNTER_CTRL_DIS) { + s->cpu_time_valid = 0; + return; + } + + { /* figure out what's going to happen next (rollover or match) */ + int64_t interval = (uint64_t)((s->reg_count & COUNTER_CTRL_INT) ? + (int64_t)s->reg_interval + 1 : 0x10000ULL) << 16; + next_value = (s->reg_count & COUNTER_CTRL_DEC) ? -1ULL : interval; + for (i = 0; i < 3; ++i) { + int64_t cand = (uint64_t)s->reg_match[i] << 16; + if (is_between(cand, (uint64_t)s->reg_value, next_value)) { + next_value = cand; + } + } + } + DB_PRINT("next timer event value: %09llx\n", + (unsigned long long)next_value); + + event_interval = next_value - (int64_t)s->reg_value; + event_interval = (event_interval < 0) ? -event_interval : event_interval; + + qemu_mod_timer(s->timer, s->cpu_time + + cadence_timer_get_ns(s, event_interval)); +} + +static void cadence_timer_sync(CadenceTimerState *s) +{ + int i; + int64_t r, x; + int64_t interval = ((s->reg_count & COUNTER_CTRL_INT) ? + (int64_t)s->reg_interval + 1 : 0x10000ULL) << 16; + uint64_t old_time = s->cpu_time; + + s->cpu_time = qemu_get_clock_ns(vm_clock); + DB_PRINT("cpu time: %lld ns\n", (long long)old_time); + + if (!s->cpu_time_valid || old_time == s->cpu_time) { + s->cpu_time_valid = 1; + return; + } + + r = (int64_t)cadence_timer_get_steps(s, s->cpu_time - old_time); + x = (int64_t)s->reg_value + ((s->reg_count & COUNTER_CTRL_DEC) ? -r : r); + + for (i = 0; i < 3; ++i) { + int64_t m = (int64_t)s->reg_match[i] << 16; + if (m > interval) { + continue; + } + /* check to see if match event has occurred. check m +/- interval + * to account for match events in wrap around cases */ + if (is_between(m, s->reg_value, x) || + is_between(m + interval, s->reg_value, x) || + is_between(m - interval, s->reg_value, x)) { + s->reg_intr |= (2 << i); + } + } + while (x < 0) { + x += interval; + } + s->reg_value = (uint32_t)(x % interval); + + if (s->reg_value != x) { + s->reg_intr |= (s->reg_count & COUNTER_CTRL_INT) ? + COUNTER_INTR_IV : COUNTER_INTR_OV; + } + cadence_timer_update(s); +} + +static void cadence_timer_tick(void *opaque) +{ + CadenceTimerState *s = opaque; + + DB_PRINT("\n"); + cadence_timer_sync(s); + cadence_timer_run(s); +} + +static uint32_t cadence_ttc_read_imp(void *opaque, target_phys_addr_t offset) +{ + CadenceTimerState *s = cadence_timer_from_addr(opaque, offset); + uint32_t value; + + cadence_timer_sync(s); + cadence_timer_run(s); + + switch (offset) { + case 0x00: /* clock control */ + case 0x04: + case 0x08: + return s->reg_clock; + + case 0x0c: /* counter control */ + case 0x10: + case 0x14: + return s->reg_count; + + case 0x18: /* counter value */ + case 0x1c: + case 0x20: + return (uint16_t)(s->reg_value >> 16); + + case 0x24: /* reg_interval counter */ + case 0x28: + case 0x2c: + return s->reg_interval; + + case 0x30: /* match 1 counter */ + case 0x34: + case 0x38: + return s->reg_match[0]; + + case 0x3c: /* match 2 counter */ + case 0x40: + case 0x44: + return s->reg_match[1]; + + case 0x48: /* match 3 counter */ + case 0x4c: + case 0x50: + return s->reg_match[2]; + + case 0x54: /* interrupt register */ + case 0x58: + case 0x5c: + /* cleared after read */ + value = s->reg_intr; + s->reg_intr = 0; + return value; + + case 0x60: /* interrupt enable */ + case 0x64: + case 0x68: + return s->reg_intr_en; + + case 0x6c: + case 0x70: + case 0x74: + return s->reg_event_ctrl; + + case 0x78: + case 0x7c: + case 0x80: + return s->reg_event; + + default: + return 0; + } +} + +static uint64_t cadence_ttc_read(void *opaque, target_phys_addr_t offset, + unsigned size) +{ + uint32_t ret = cadence_ttc_read_imp(opaque, offset); + + DB_PRINT("addr: %08x data: %08x\n", offset, ret); + return ret; +} + +static void cadence_ttc_write(void *opaque, target_phys_addr_t offset, + uint64_t value, unsigned size) +{ + CadenceTimerState *s = cadence_timer_from_addr(opaque, offset); + + DB_PRINT("addr: %08x data %08x\n", offset, (unsigned)value); + + cadence_timer_sync(s); + + switch (offset) { + case 0x00: /* clock control */ + case 0x04: + case 0x08: + s->reg_clock = value & 0x3F; + break; + + case 0x0c: /* counter control */ + case 0x10: + case 0x14: + if (value & COUNTER_CTRL_RST) { + s->reg_value = 0; + } + s->reg_count = value & 0x3f & ~COUNTER_CTRL_RST; + break; + + case 0x24: /* interval register */ + case 0x28: + case 0x2c: + s->reg_interval = value & 0xffff; + break; + + case 0x30: /* match register */ + case 0x34: + case 0x38: + s->reg_match[0] = value & 0xffff; + + case 0x3c: /* match register */ + case 0x40: + case 0x44: + s->reg_match[1] = value & 0xffff; + + case 0x48: /* match register */ + case 0x4c: + case 0x50: + s->reg_match[2] = value & 0xffff; + break; + + case 0x54: /* interrupt register */ + case 0x58: + case 0x5c: + s->reg_intr &= (~value & 0xfff); + break; + + case 0x60: /* interrupt enable */ + case 0x64: + case 0x68: + s->reg_intr_en = value & 0x3f; + break; + + case 0x6c: /* event control */ + case 0x70: + case 0x74: + s->reg_event_ctrl = value & 0x07; + break; + + default: + return; + } + + cadence_timer_run(s); + cadence_timer_update(s); +} + +static const MemoryRegionOps cadence_ttc_ops = { + .read = cadence_ttc_read, + .write = cadence_ttc_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void cadence_timer_reset(CadenceTimerState *s) +{ + s->reg_count = 0x21; +} + +static void cadence_timer_init(uint32_t freq, CadenceTimerState *s) +{ + memset(s, 0, sizeof(CadenceTimerState)); + s->freq = freq; + + cadence_timer_reset(s); + + s->timer = qemu_new_timer_ns(vm_clock, cadence_timer_tick, s); +} + +static int cadence_ttc_init(SysBusDevice *dev) +{ + CadenceTTCState *s = FROM_SYSBUS(CadenceTTCState, dev); + int i; + + for (i = 0; i < 3; ++i) { + cadence_timer_init(2500000, &s->timer[i]); + sysbus_init_irq(dev, &s->timer[i].irq); + } + + memory_region_init_io(&s->iomem, &cadence_ttc_ops, s, "timer", 0x1000); + sysbus_init_mmio(dev, &s->iomem); + + return 0; +} + +static void cadence_timer_pre_save(void *opaque) +{ + cadence_timer_sync((CadenceTimerState *)opaque); +} + +static int cadence_timer_post_load(void *opaque, int version_id) +{ + CadenceTimerState *s = opaque; + + s->cpu_time_valid = 0; + cadence_timer_sync(s); + cadence_timer_run(s); + cadence_timer_update(s); + return 0; +} + +static const VMStateDescription vmstate_cadence_timer = { + .name = "cadence_timer", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .pre_save = cadence_timer_pre_save, + .post_load = cadence_timer_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT32(reg_clock, CadenceTimerState), + VMSTATE_UINT32(reg_count, CadenceTimerState), + VMSTATE_UINT32(reg_value, CadenceTimerState), + VMSTATE_UINT16(reg_interval, CadenceTimerState), + VMSTATE_UINT16_ARRAY(reg_match, CadenceTimerState, 3), + VMSTATE_UINT32(reg_intr, CadenceTimerState), + VMSTATE_UINT32(reg_intr_en, CadenceTimerState), + VMSTATE_UINT32(reg_event_ctrl, CadenceTimerState), + VMSTATE_UINT32(reg_event, CadenceTimerState), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_cadence_ttc = { + .name = "cadence_TTC", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_STRUCT_ARRAY(timer, CadenceTTCState, 3, 0, + vmstate_cadence_timer, + CadenceTimerState), + VMSTATE_END_OF_LIST() + } +}; + +static void cadence_ttc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); + + sdc->init = cadence_ttc_init; + dc->vmsd = &vmstate_cadence_ttc; +} + +static TypeInfo cadence_ttc_info = { + .name = "cadence_ttc", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(CadenceTTCState), + .class_init = cadence_ttc_class_init, +}; + +static void cadence_ttc_register_types(void) +{ + type_register_static(&cadence_ttc_info); +} + +type_init(cadence_ttc_register_types) diff --git a/hw/cadence_uart.c b/hw/cadence_uart.c new file mode 100644 index 0000000000..d98e531372 --- /dev/null +++ b/hw/cadence_uart.c @@ -0,0 +1,513 @@ +/* + * Device model for Cadence UART + * + * Copyright (c) 2010 Xilinx Inc. + * Copyright (c) 2012 Peter A.G. Crosthwaite (peter.crosthwaite@petalogix.com) + * Copyright (c) 2012 PetaLogix Pty Ltd. + * Written by Haibing Ma + * M.Habib + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "sysbus.h" +#include "qemu-char.h" +#include "qemu-timer.h" + +#ifdef CADENCE_UART_ERR_DEBUG +#define DB_PRINT(...) do { \ + fprintf(stderr, ": %s: ", __func__); \ + fprintf(stderr, ## __VA_ARGS__); \ + } while (0); +#else + #define DB_PRINT(...) +#endif + +#define UART_SR_INTR_RTRIG 0x00000001 +#define UART_SR_INTR_REMPTY 0x00000002 +#define UART_SR_INTR_RFUL 0x00000004 +#define UART_SR_INTR_TEMPTY 0x00000008 +#define UART_SR_INTR_TFUL 0x00000010 +/* bits fields in CSR that correlate to CISR. If any of these bits are set in + * SR, then the same bit in CISR is set high too */ +#define UART_SR_TO_CISR_MASK 0x0000001F + +#define UART_INTR_ROVR 0x00000020 +#define UART_INTR_FRAME 0x00000040 +#define UART_INTR_PARE 0x00000080 +#define UART_INTR_TIMEOUT 0x00000100 +#define UART_INTR_DMSI 0x00000200 + +#define UART_SR_RACTIVE 0x00000400 +#define UART_SR_TACTIVE 0x00000800 +#define UART_SR_FDELT 0x00001000 + +#define UART_CR_RXRST 0x00000001 +#define UART_CR_TXRST 0x00000002 +#define UART_CR_RX_EN 0x00000004 +#define UART_CR_RX_DIS 0x00000008 +#define UART_CR_TX_EN 0x00000010 +#define UART_CR_TX_DIS 0x00000020 +#define UART_CR_RST_TO 0x00000040 +#define UART_CR_STARTBRK 0x00000080 +#define UART_CR_STOPBRK 0x00000100 + +#define UART_MR_CLKS 0x00000001 +#define UART_MR_CHRL 0x00000006 +#define UART_MR_CHRL_SH 1 +#define UART_MR_PAR 0x00000038 +#define UART_MR_PAR_SH 3 +#define UART_MR_NBSTOP 0x000000C0 +#define UART_MR_NBSTOP_SH 6 +#define UART_MR_CHMODE 0x00000300 +#define UART_MR_CHMODE_SH 8 +#define UART_MR_UCLKEN 0x00000400 +#define UART_MR_IRMODE 0x00000800 + +#define UART_DATA_BITS_6 (0x3 << UART_MR_CHRL_SH) +#define UART_DATA_BITS_7 (0x2 << UART_MR_CHRL_SH) +#define UART_PARITY_ODD (0x1 << UART_MR_PAR_SH) +#define UART_PARITY_EVEN (0x0 << UART_MR_PAR_SH) +#define UART_STOP_BITS_1 (0x3 << UART_MR_NBSTOP_SH) +#define UART_STOP_BITS_2 (0x2 << UART_MR_NBSTOP_SH) +#define NORMAL_MODE (0x0 << UART_MR_CHMODE_SH) +#define ECHO_MODE (0x1 << UART_MR_CHMODE_SH) +#define LOCAL_LOOPBACK (0x2 << UART_MR_CHMODE_SH) +#define REMOTE_LOOPBACK (0x3 << UART_MR_CHMODE_SH) + +#define RX_FIFO_SIZE 16 +#define TX_FIFO_SIZE 16 +#define UART_INPUT_CLK 50000000 + +#define R_CR (0x00/4) +#define R_MR (0x04/4) +#define R_IER (0x08/4) +#define R_IDR (0x0C/4) +#define R_IMR (0x10/4) +#define R_CISR (0x14/4) +#define R_BRGR (0x18/4) +#define R_RTOR (0x1C/4) +#define R_RTRIG (0x20/4) +#define R_MCR (0x24/4) +#define R_MSR (0x28/4) +#define R_SR (0x2C/4) +#define R_TX_RX (0x30/4) +#define R_BDIV (0x34/4) +#define R_FDEL (0x38/4) +#define R_PMIN (0x3C/4) +#define R_PWID (0x40/4) +#define R_TTRIG (0x44/4) + +#define R_MAX (R_TTRIG + 1) + +typedef struct { + SysBusDevice busdev; + MemoryRegion iomem; + uint32_t r[R_MAX]; + uint8_t r_fifo[RX_FIFO_SIZE]; + uint32_t rx_wpos; + uint32_t rx_count; + uint64_t char_tx_time; + CharDriverState *chr; + qemu_irq irq; + struct QEMUTimer *fifo_trigger_handle; + struct QEMUTimer *tx_time_handle; +} UartState; + +static void uart_update_status(UartState *s) +{ + s->r[R_CISR] |= s->r[R_SR] & UART_SR_TO_CISR_MASK; + qemu_set_irq(s->irq, !!(s->r[R_IMR] & s->r[R_CISR])); +} + +static void fifo_trigger_update(void *opaque) +{ + UartState *s = (UartState *)opaque; + + s->r[R_CISR] |= UART_INTR_TIMEOUT; + + uart_update_status(s); +} + +static void uart_tx_redo(UartState *s) +{ + uint64_t new_tx_time = qemu_get_clock_ns(vm_clock); + + qemu_mod_timer(s->tx_time_handle, new_tx_time + s->char_tx_time); + + s->r[R_SR] |= UART_SR_INTR_TEMPTY; + + uart_update_status(s); +} + +static void uart_tx_write(void *opaque) +{ + UartState *s = (UartState *)opaque; + + uart_tx_redo(s); +} + +static void uart_rx_reset(UartState *s) +{ + s->rx_wpos = 0; + s->rx_count = 0; + + s->r[R_SR] |= UART_SR_INTR_REMPTY; + s->r[R_SR] &= ~UART_SR_INTR_RFUL; +} + +static void uart_tx_reset(UartState *s) +{ + s->r[R_SR] |= UART_SR_INTR_TEMPTY; + s->r[R_SR] &= ~UART_SR_INTR_TFUL; +} + +static void uart_send_breaks(UartState *s) +{ + int break_enabled = 1; + + qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_BREAK, + &break_enabled); +} + +static void uart_parameters_setup(UartState *s) +{ + QEMUSerialSetParams ssp; + unsigned int baud_rate, packet_size; + + baud_rate = (s->r[R_MR] & UART_MR_CLKS) ? + UART_INPUT_CLK / 8 : UART_INPUT_CLK; + + ssp.speed = baud_rate / (s->r[R_BRGR] * (s->r[R_BDIV] + 1)); + packet_size = 1; + + switch (s->r[R_MR] & UART_MR_PAR) { + case UART_PARITY_EVEN: + ssp.parity = 'E'; + packet_size++; + break; + case UART_PARITY_ODD: + ssp.parity = 'O'; + packet_size++; + break; + default: + ssp.parity = 'N'; + break; + } + + switch (s->r[R_MR] & UART_MR_CHRL) { + case UART_DATA_BITS_6: + ssp.data_bits = 6; + break; + case UART_DATA_BITS_7: + ssp.data_bits = 7; + break; + default: + ssp.data_bits = 8; + break; + } + + switch (s->r[R_MR] & UART_MR_NBSTOP) { + case UART_STOP_BITS_1: + ssp.stop_bits = 1; + break; + default: + ssp.stop_bits = 2; + break; + } + + packet_size += ssp.data_bits + ssp.stop_bits; + s->char_tx_time = (get_ticks_per_sec() / ssp.speed) * packet_size; + qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp); +} + +static int uart_can_receive(void *opaque) +{ + UartState *s = (UartState *)opaque; + + return RX_FIFO_SIZE - s->rx_count; +} + +static void uart_ctrl_update(UartState *s) +{ + if (s->r[R_CR] & UART_CR_TXRST) { + uart_tx_reset(s); + } + + if (s->r[R_CR] & UART_CR_RXRST) { + uart_rx_reset(s); + } + + s->r[R_CR] &= ~(UART_CR_TXRST | UART_CR_RXRST); + + if ((s->r[R_CR] & UART_CR_TX_EN) && !(s->r[R_CR] & UART_CR_TX_DIS)) { + uart_tx_redo(s); + } + + if (s->r[R_CR] & UART_CR_STARTBRK && !(s->r[R_CR] & UART_CR_STOPBRK)) { + uart_send_breaks(s); + } +} + +static void uart_write_rx_fifo(void *opaque, const uint8_t *buf, int size) +{ + UartState *s = (UartState *)opaque; + uint64_t new_rx_time = qemu_get_clock_ns(vm_clock); + int i; + + if ((s->r[R_CR] & UART_CR_RX_DIS) || !(s->r[R_CR] & UART_CR_RX_EN)) { + return; + } + + s->r[R_SR] &= ~UART_SR_INTR_REMPTY; + + if (s->rx_count == RX_FIFO_SIZE) { + s->r[R_CISR] |= UART_INTR_ROVR; + } else { + for (i = 0; i < size; i++) { + s->r_fifo[s->rx_wpos] = buf[i]; + s->rx_wpos = (s->rx_wpos + 1) % RX_FIFO_SIZE; + s->rx_count++; + + if (s->rx_count == RX_FIFO_SIZE) { + s->r[R_SR] |= UART_SR_INTR_RFUL; + break; + } + + if (s->rx_count >= s->r[R_RTRIG]) { + s->r[R_SR] |= UART_SR_INTR_RTRIG; + } + } + qemu_mod_timer(s->fifo_trigger_handle, new_rx_time + + (s->char_tx_time * 4)); + } + uart_update_status(s); +} + +static void uart_write_tx_fifo(UartState *s, const uint8_t *buf, int size) +{ + if ((s->r[R_CR] & UART_CR_TX_DIS) || !(s->r[R_CR] & UART_CR_TX_EN)) { + return; + } + + while (size) { + size -= qemu_chr_fe_write(s->chr, buf, size); + } +} + +static void uart_receive(void *opaque, const uint8_t *buf, int size) +{ + UartState *s = (UartState *)opaque; + uint32_t ch_mode = s->r[R_MR] & UART_MR_CHMODE; + + if (ch_mode == NORMAL_MODE || ch_mode == ECHO_MODE) { + uart_write_rx_fifo(opaque, buf, size); + } + if (ch_mode == REMOTE_LOOPBACK || ch_mode == ECHO_MODE) { + uart_write_tx_fifo(s, buf, size); + } +} + +static void uart_event(void *opaque, int event) +{ + UartState *s = (UartState *)opaque; + uint8_t buf = '\0'; + + if (event == CHR_EVENT_BREAK) { + uart_write_rx_fifo(opaque, &buf, 1); + } + + uart_update_status(s); +} + +static void uart_read_rx_fifo(UartState *s, uint32_t *c) +{ + if ((s->r[R_CR] & UART_CR_RX_DIS) || !(s->r[R_CR] & UART_CR_RX_EN)) { + return; + } + + s->r[R_SR] &= ~UART_SR_INTR_RFUL; + + if (s->rx_count) { + uint32_t rx_rpos = + (RX_FIFO_SIZE + s->rx_wpos - s->rx_count) % RX_FIFO_SIZE; + *c = s->r_fifo[rx_rpos]; + s->rx_count--; + + if (!s->rx_count) { + s->r[R_SR] |= UART_SR_INTR_REMPTY; + } + } else { + *c = 0; + s->r[R_SR] |= UART_SR_INTR_REMPTY; + } + + if (s->rx_count < s->r[R_RTRIG]) { + s->r[R_SR] &= ~UART_SR_INTR_RTRIG; + } + uart_update_status(s); +} + +static void uart_write(void *opaque, target_phys_addr_t offset, + uint64_t value, unsigned size) +{ + UartState *s = (UartState *)opaque; + + DB_PRINT(" offset:%x data:%08x\n", offset, (unsigned)value); + offset >>= 2; + switch (offset) { + case R_IER: /* ier (wts imr) */ + s->r[R_IMR] |= value; + break; + case R_IDR: /* idr (wtc imr) */ + s->r[R_IMR] &= ~value; + break; + case R_IMR: /* imr (read only) */ + break; + case R_CISR: /* cisr (wtc) */ + s->r[R_CISR] &= ~value; + break; + case R_TX_RX: /* UARTDR */ + switch (s->r[R_MR] & UART_MR_CHMODE) { + case NORMAL_MODE: + uart_write_tx_fifo(s, (uint8_t *) &value, 1); + break; + case LOCAL_LOOPBACK: + uart_write_rx_fifo(opaque, (uint8_t *) &value, 1); + break; + } + break; + default: + s->r[offset] = value; + } + + switch (offset) { + case R_CR: + uart_ctrl_update(s); + break; + case R_MR: + uart_parameters_setup(s); + break; + } +} + +static uint64_t uart_read(void *opaque, target_phys_addr_t offset, + unsigned size) +{ + UartState *s = (UartState *)opaque; + uint32_t c = 0; + + offset >>= 2; + if (offset > R_MAX) { + return 0; + } else if (offset == R_TX_RX) { + uart_read_rx_fifo(s, &c); + return c; + } + return s->r[offset]; +} + +static const MemoryRegionOps uart_ops = { + .read = uart_read, + .write = uart_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void cadence_uart_reset(UartState *s) +{ + s->r[R_CR] = 0x00000128; + s->r[R_IMR] = 0; + s->r[R_CISR] = 0; + s->r[R_RTRIG] = 0x00000020; + s->r[R_BRGR] = 0x0000000F; + s->r[R_TTRIG] = 0x00000020; + + uart_rx_reset(s); + uart_tx_reset(s); + + s->rx_count = 0; + s->rx_wpos = 0; +} + +static int cadence_uart_init(SysBusDevice *dev) +{ + UartState *s = FROM_SYSBUS(UartState, dev); + + memory_region_init_io(&s->iomem, &uart_ops, s, "uart", 0x1000); + sysbus_init_mmio(dev, &s->iomem); + sysbus_init_irq(dev, &s->irq); + + s->fifo_trigger_handle = qemu_new_timer_ns(vm_clock, + (QEMUTimerCB *)fifo_trigger_update, s); + + s->tx_time_handle = qemu_new_timer_ns(vm_clock, + (QEMUTimerCB *)uart_tx_write, s); + + s->char_tx_time = (get_ticks_per_sec() / 9600) * 10; + + s->chr = qemu_char_get_next_serial(); + + cadence_uart_reset(s); + + if (s->chr) { + qemu_chr_add_handlers(s->chr, uart_can_receive, uart_receive, + uart_event, s); + } + + return 0; +} + +static int cadence_uart_post_load(void *opaque, int version_id) +{ + UartState *s = opaque; + + uart_parameters_setup(s); + uart_update_status(s); + return 0; +} + +static const VMStateDescription vmstate_cadence_uart = { + .name = "cadence_uart", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .post_load = cadence_uart_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(r, UartState, R_MAX), + VMSTATE_UINT8_ARRAY(r_fifo, UartState, RX_FIFO_SIZE), + VMSTATE_UINT32(rx_count, UartState), + VMSTATE_UINT32(rx_wpos, UartState), + VMSTATE_TIMER(fifo_trigger_handle, UartState), + VMSTATE_TIMER(tx_time_handle, UartState), + VMSTATE_END_OF_LIST() + } +}; + +static void cadence_uart_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); + + sdc->init = cadence_uart_init; + dc->vmsd = &vmstate_cadence_uart; +} + +static TypeInfo cadence_uart_info = { + .name = "cadence_uart", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(UartState), + .class_init = cadence_uart_class_init, +}; + +static void cadence_uart_register_types(void) +{ + type_register_static(&cadence_uart_info); +} + +type_init(cadence_uart_register_types) diff --git a/hw/microblaze_boot.c b/hw/microblaze_boot.c new file mode 100644 index 0000000000..b2f96df344 --- /dev/null +++ b/hw/microblaze_boot.c @@ -0,0 +1,177 @@ +/* + * Microblaze kernel loader + * + * Copyright (c) 2012 Peter Crosthwaite <peter.crosthwaite@petalogix.com> + * Copyright (c) 2012 PetaLogix + * Copyright (c) 2009 Edgar E. Iglesias. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu-option.h" +#include "qemu-config.h" +#include "qemu-common.h" +#include "device_tree.h" +#include "loader.h" +#include "elf.h" + +#include "microblaze_boot.h" + +static struct +{ + void (*machine_cpu_reset)(CPUState *); + uint32_t bootstrap_pc; + uint32_t cmdline; + uint32_t fdt; +} boot_info; + +static void main_cpu_reset(void *opaque) +{ + CPUState *env = opaque; + + cpu_reset(env); + env->regs[5] = boot_info.cmdline; + env->regs[7] = boot_info.fdt; + env->sregs[SR_PC] = boot_info.bootstrap_pc; + if (boot_info.machine_cpu_reset) { + boot_info.machine_cpu_reset(env); + } +} + +static int microblaze_load_dtb(target_phys_addr_t addr, + uint32_t ramsize, + const char *kernel_cmdline, + const char *dtb_filename) +{ + int fdt_size; +#ifdef CONFIG_FDT + void *fdt = NULL; + int r; + + if (dtb_filename) { + fdt = load_device_tree(dtb_filename, &fdt_size); + } + if (!fdt) { + return 0; + } + + if (kernel_cmdline) { + r = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs", + kernel_cmdline); + if (r < 0) { + fprintf(stderr, "couldn't set /chosen/bootargs\n"); + } + } + + cpu_physical_memory_write(addr, (void *)fdt, fdt_size); +#else + /* We lack libfdt so we cannot manipulate the fdt. Just pass on the blob + to the kernel. */ + if (dtb_filename) { + fdt_size = load_image_targphys(dtb_filename, addr, 0x10000); + } + if (kernel_cmdline) { + fprintf(stderr, + "Warning: missing libfdt, cannot pass cmdline to kernel!\n"); + } +#endif + return fdt_size; +} + +static uint64_t translate_kernel_address(void *opaque, uint64_t addr) +{ + return addr - 0x30000000LL; +} + +void microblaze_load_kernel(CPUState *env, target_phys_addr_t ddr_base, + uint32_t ramsize, const char *dtb_filename, + void (*machine_cpu_reset)(CPUState *)) +{ + + QemuOpts *machine_opts; + const char *kernel_filename = NULL; + const char *kernel_cmdline = NULL; + + machine_opts = qemu_opts_find(qemu_find_opts("machine"), 0); + if (machine_opts) { + const char *dtb_arg; + kernel_filename = qemu_opt_get(machine_opts, "kernel"); + kernel_cmdline = qemu_opt_get(machine_opts, "append"); + dtb_arg = qemu_opt_get(machine_opts, "dtb"); + if (dtb_arg) { /* Preference a -dtb argument */ + dtb_filename = dtb_arg; + } else { /* default to pcbios dtb as passed by machine_init */ + dtb_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, dtb_filename); + } + } + + boot_info.machine_cpu_reset = machine_cpu_reset; + qemu_register_reset(main_cpu_reset, env); + + if (kernel_filename) { + int kernel_size; + uint64_t entry, low, high; + uint32_t base32; + int big_endian = 0; + +#ifdef TARGET_WORDS_BIGENDIAN + big_endian = 1; +#endif + + /* Boots a kernel elf binary. */ + kernel_size = load_elf(kernel_filename, NULL, NULL, + &entry, &low, &high, + big_endian, ELF_MACHINE, 0); + base32 = entry; + if (base32 == 0xc0000000) { + kernel_size = load_elf(kernel_filename, translate_kernel_address, + NULL, &entry, NULL, NULL, + big_endian, ELF_MACHINE, 0); + } + /* Always boot into physical ram. */ + boot_info.bootstrap_pc = ddr_base + (entry & 0x0fffffff); + + /* If it wasn't an ELF image, try an u-boot image. */ + if (kernel_size < 0) { + target_phys_addr_t uentry, loadaddr; + + kernel_size = load_uimage(kernel_filename, &uentry, &loadaddr, 0); + boot_info.bootstrap_pc = uentry; + high = (loadaddr + kernel_size + 3) & ~3; + } + + /* Not an ELF image nor an u-boot image, try a RAW image. */ + if (kernel_size < 0) { + kernel_size = load_image_targphys(kernel_filename, ddr_base, + ram_size); + boot_info.bootstrap_pc = ddr_base; + high = (ddr_base + kernel_size + 3) & ~3; + } + + boot_info.cmdline = high + 4096; + if (kernel_cmdline && strlen(kernel_cmdline)) { + pstrcpy_targphys("cmdline", boot_info.cmdline, 256, kernel_cmdline); + } + /* Provide a device-tree. */ + boot_info.fdt = boot_info.cmdline + 4096; + microblaze_load_dtb(boot_info.fdt, ram_size, kernel_cmdline, + dtb_filename); + } + +} diff --git a/hw/microblaze_boot.h b/hw/microblaze_boot.h new file mode 100644 index 0000000000..69d4ac635b --- /dev/null +++ b/hw/microblaze_boot.h @@ -0,0 +1,10 @@ +#ifndef __MICROBLAZE_BOOT__ +#define __MICROBLAZE_BOOT__ + +#include "hw.h" + +void microblaze_load_kernel(CPUState *env, target_phys_addr_t ddr_base, + uint32_t ramsize, const char *dtb_filename, + void (*machine_cpu_reset)(CPUState *)); + +#endif /* __MICROBLAZE_BOOT __ */ diff --git a/hw/petalogix_ml605_mmu.c b/hw/petalogix_ml605_mmu.c index 98978f89b6..c87fa1186c 100644 --- a/hw/petalogix_ml605_mmu.c +++ b/hw/petalogix_ml605_mmu.c @@ -32,35 +32,30 @@ #include "sysemu.h" #include "devices.h" #include "boards.h" -#include "device_tree.h" #include "xilinx.h" -#include "loader.h" -#include "elf.h" #include "blockdev.h" #include "pc.h" #include "exec-memory.h" +#include "microblaze_boot.h" #include "microblaze_pic_cpu.h" #include "xilinx_axidma.h" #define LMB_BRAM_SIZE (128 * 1024) #define FLASH_SIZE (32 * 1024 * 1024) -static struct -{ - uint32_t bootstrap_pc; - uint32_t cmdline; - uint32_t fdt; -} boot_info; +#define BINARY_DEVICE_TREE_FILE "petalogix-ml605.dtb" -static void main_cpu_reset(void *opaque) -{ - CPUState *env = opaque; +#define MEMORY_BASEADDR 0x50000000 +#define FLASH_BASEADDR 0x86000000 +#define INTC_BASEADDR 0x81800000 +#define TIMER_BASEADDR 0x83c00000 +#define UART16550_BASEADDR 0x83e00000 +#define AXIENET_BASEADDR 0x82780000 +#define AXIDMA_BASEADDR 0x84600000 - cpu_reset(env); - env->regs[5] = boot_info.cmdline; - env->regs[7] = boot_info.fdt; - env->sregs[SR_PC] = boot_info.bootstrap_pc; +static void machine_cpu_reset(CPUState *env) +{ env->pvr.regs[10] = 0x0e000000; /* virtex 6 */ /* setup pvr to match kernel setting */ env->pvr.regs[5] |= PVR5_DCACHE_WRITEBACK_MASK; @@ -71,70 +66,6 @@ static void main_cpu_reset(void *opaque) env->pvr.regs[5] = 0xc56be000; } -#define BINARY_DEVICE_TREE_FILE "petalogix-ml605.dtb" -static int petalogix_load_device_tree(target_phys_addr_t addr, - uint32_t ramsize, - target_phys_addr_t initrd_base, - target_phys_addr_t initrd_size, - const char *kernel_cmdline) -{ - char *path; - int fdt_size; -#ifdef CONFIG_FDT - void *fdt; - int r; - - /* Try the local "mb.dtb" override. */ - fdt = load_device_tree("mb.dtb", &fdt_size); - if (!fdt) { - path = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE); - if (path) { - fdt = load_device_tree(path, &fdt_size); - g_free(path); - } - if (!fdt) { - return 0; - } - } - - r = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs", kernel_cmdline); - if (r < 0) { - fprintf(stderr, "couldn't set /chosen/bootargs\n"); - } - cpu_physical_memory_write(addr, (void *)fdt, fdt_size); -#else - /* We lack libfdt so we cannot manipulate the fdt. Just pass on the blob - to the kernel. */ - fdt_size = load_image_targphys("mb.dtb", addr, 0x10000); - if (fdt_size < 0) { - path = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE); - if (path) { - fdt_size = load_image_targphys(path, addr, 0x10000); - g_free(path); - } - } - - if (kernel_cmdline) { - fprintf(stderr, - "Warning: missing libfdt, cannot pass cmdline to kernel!\n"); - } -#endif - return fdt_size; -} - -static uint64_t translate_kernel_address(void *opaque, uint64_t addr) -{ - return addr - 0x30000000LL; -} - -#define MEMORY_BASEADDR 0x50000000 -#define FLASH_BASEADDR 0x86000000 -#define INTC_BASEADDR 0x81800000 -#define TIMER_BASEADDR 0x83c00000 -#define UART16550_BASEADDR 0x83e00000 -#define AXIENET_BASEADDR 0x82780000 -#define AXIDMA_BASEADDR 0x84600000 - static void petalogix_ml605_init(ram_addr_t ram_size, const char *boot_device, @@ -145,7 +76,6 @@ petalogix_ml605_init(ram_addr_t ram_size, MemoryRegion *address_space_mem = get_system_memory(); DeviceState *dev; CPUState *env; - int kernel_size; DriveInfo *dinfo; int i; target_phys_addr_t ddr_base = MEMORY_BASEADDR; @@ -159,8 +89,6 @@ petalogix_ml605_init(ram_addr_t ram_size, } env = cpu_init(cpu_model); - qemu_register_reset(main_cpu_reset, env); - /* Attach emulated BRAM through the LMB. */ memory_region_init_ram(phys_lmb_bram, "petalogix_ml605.lmb_bram", LMB_BRAM_SIZE); @@ -203,55 +131,9 @@ petalogix_ml605_init(ram_addr_t ram_size, irq[1], irq[0], 100 * 1000000); } - if (kernel_filename) { - uint64_t entry, low, high; - uint32_t base32; - int big_endian = 0; - -#ifdef TARGET_WORDS_BIGENDIAN - big_endian = 1; -#endif + microblaze_load_kernel(env, ddr_base, ram_size, BINARY_DEVICE_TREE_FILE, + machine_cpu_reset); - /* Boots a kernel elf binary. */ - kernel_size = load_elf(kernel_filename, NULL, NULL, - &entry, &low, &high, - big_endian, ELF_MACHINE, 0); - base32 = entry; - if (base32 == 0xc0000000) { - kernel_size = load_elf(kernel_filename, translate_kernel_address, - NULL, &entry, NULL, NULL, - big_endian, ELF_MACHINE, 0); - } - /* Always boot into physical ram. */ - boot_info.bootstrap_pc = ddr_base + (entry & 0x0fffffff); - - /* If it wasn't an ELF image, try an u-boot image. */ - if (kernel_size < 0) { - target_phys_addr_t uentry, loadaddr; - - kernel_size = load_uimage(kernel_filename, &uentry, &loadaddr, 0); - boot_info.bootstrap_pc = uentry; - high = (loadaddr + kernel_size + 3) & ~3; - } - - /* Not an ELF image nor an u-boot image, try a RAW image. */ - if (kernel_size < 0) { - kernel_size = load_image_targphys(kernel_filename, ddr_base, - ram_size); - boot_info.bootstrap_pc = ddr_base; - high = (ddr_base + kernel_size + 3) & ~3; - } - - boot_info.cmdline = high + 4096; - if (kernel_cmdline && strlen(kernel_cmdline)) { - pstrcpy_targphys("cmdline", boot_info.cmdline, 256, kernel_cmdline); - } - /* Provide a device-tree. */ - boot_info.fdt = boot_info.cmdline + 4096; - petalogix_load_device_tree(boot_info.fdt, ram_size, - 0, 0, - kernel_cmdline); - } } static QEMUMachine petalogix_ml605_machine = { diff --git a/hw/petalogix_s3adsp1800_mmu.c b/hw/petalogix_s3adsp1800_mmu.c index d448a41f68..2cb0b1f10a 100644 --- a/hw/petalogix_s3adsp1800_mmu.c +++ b/hw/petalogix_s3adsp1800_mmu.c @@ -30,87 +30,29 @@ #include "sysemu.h" #include "devices.h" #include "boards.h" -#include "device_tree.h" #include "xilinx.h" -#include "loader.h" -#include "elf.h" #include "blockdev.h" #include "exec-memory.h" +#include "microblaze_boot.h" #include "microblaze_pic_cpu.h" #define LMB_BRAM_SIZE (128 * 1024) #define FLASH_SIZE (16 * 1024 * 1024) -static struct -{ - uint32_t bootstrap_pc; - uint32_t cmdline; - uint32_t fdt; -} boot_info; - -static void main_cpu_reset(void *opaque) -{ - CPUState *env = opaque; - - cpu_reset(env); - env->regs[5] = boot_info.cmdline; - env->regs[7] = boot_info.fdt; - env->sregs[SR_PC] = boot_info.bootstrap_pc; -} - #define BINARY_DEVICE_TREE_FILE "petalogix-s3adsp1800.dtb" -static int petalogix_load_device_tree(target_phys_addr_t addr, - uint32_t ramsize, - target_phys_addr_t initrd_base, - target_phys_addr_t initrd_size, - const char *kernel_cmdline) -{ - char *path; - int fdt_size; -#ifdef CONFIG_FDT - void *fdt; - int r; - - /* Try the local "mb.dtb" override. */ - fdt = load_device_tree("mb.dtb", &fdt_size); - if (!fdt) { - path = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE); - if (path) { - fdt = load_device_tree(path, &fdt_size); - g_free(path); - } - if (!fdt) - return 0; - } - r = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs", kernel_cmdline); - if (r < 0) - fprintf(stderr, "couldn't set /chosen/bootargs\n"); - cpu_physical_memory_write (addr, (void *)fdt, fdt_size); -#else - /* We lack libfdt so we cannot manipulate the fdt. Just pass on the blob - to the kernel. */ - fdt_size = load_image_targphys("mb.dtb", addr, 0x10000); - if (fdt_size < 0) { - path = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE); - if (path) { - fdt_size = load_image_targphys(path, addr, 0x10000); - g_free(path); - } - } +#define MEMORY_BASEADDR 0x90000000 +#define FLASH_BASEADDR 0xa0000000 +#define INTC_BASEADDR 0x81800000 +#define TIMER_BASEADDR 0x83c00000 +#define UARTLITE_BASEADDR 0x84000000 +#define ETHLITE_BASEADDR 0x81000000 - if (kernel_cmdline) { - fprintf(stderr, - "Warning: missing libfdt, cannot pass cmdline to kernel!\n"); - } -#endif - return fdt_size; -} - -static uint64_t translate_kernel_address(void *opaque, uint64_t addr) +static void machine_cpu_reset(CPUState *env) { - return addr - 0x30000000LL; + /* FIXME: move to machine specfic cpu reset */ + env->pvr.regs[10] = 0x0c000000; /* spartan 3a dsp family. */ } static void @@ -122,10 +64,9 @@ petalogix_s3adsp1800_init(ram_addr_t ram_size, { DeviceState *dev; CPUState *env; - int kernel_size; DriveInfo *dinfo; int i; - target_phys_addr_t ddr_base = 0x90000000; + target_phys_addr_t ddr_base = MEMORY_BASEADDR; MemoryRegion *phys_lmb_bram = g_new(MemoryRegion, 1); MemoryRegion *phys_ram = g_new(MemoryRegion, 1); qemu_irq irq[32], *cpu_irq; @@ -137,9 +78,6 @@ petalogix_s3adsp1800_init(ram_addr_t ram_size, } env = cpu_init(cpu_model); - env->pvr.regs[10] = 0x0c000000; /* spartan 3a dsp family. */ - qemu_register_reset(main_cpu_reset, env); - /* Attach emulated BRAM through the LMB. */ memory_region_init_ram(phys_lmb_bram, "petalogix_s3adsp1800.lmb_bram", LMB_BRAM_SIZE); @@ -151,72 +89,25 @@ petalogix_s3adsp1800_init(ram_addr_t ram_size, memory_region_add_subregion(sysmem, ddr_base, phys_ram); dinfo = drive_get(IF_PFLASH, 0, 0); - pflash_cfi01_register(0xa0000000, + pflash_cfi01_register(FLASH_BASEADDR, NULL, "petalogix_s3adsp1800.flash", FLASH_SIZE, dinfo ? dinfo->bdrv : NULL, (64 * 1024), FLASH_SIZE >> 16, 1, 0x89, 0x18, 0x0000, 0x0, 1); cpu_irq = microblaze_pic_init_cpu(env); - dev = xilinx_intc_create(0x81800000, cpu_irq[0], 2); + dev = xilinx_intc_create(INTC_BASEADDR, cpu_irq[0], 2); for (i = 0; i < 32; i++) { irq[i] = qdev_get_gpio_in(dev, i); } - sysbus_create_simple("xilinx,uartlite", 0x84000000, irq[3]); + sysbus_create_simple("xilinx,uartlite", UARTLITE_BASEADDR, irq[3]); /* 2 timers at irq 2 @ 62 Mhz. */ - xilinx_timer_create(0x83c00000, irq[0], 2, 62 * 1000000); - xilinx_ethlite_create(&nd_table[0], 0x81000000, irq[1], 0, 0); - - if (kernel_filename) { - uint64_t entry, low, high; - uint32_t base32; - int big_endian = 0; - -#ifdef TARGET_WORDS_BIGENDIAN - big_endian = 1; -#endif - - /* Boots a kernel elf binary. */ - kernel_size = load_elf(kernel_filename, NULL, NULL, - &entry, &low, &high, - big_endian, ELF_MACHINE, 0); - base32 = entry; - if (base32 == 0xc0000000) { - kernel_size = load_elf(kernel_filename, translate_kernel_address, - NULL, &entry, NULL, NULL, - big_endian, ELF_MACHINE, 0); - } - /* Always boot into physical ram. */ - boot_info.bootstrap_pc = ddr_base + (entry & 0x0fffffff); - - /* If it wasn't an ELF image, try an u-boot image. */ - if (kernel_size < 0) { - target_phys_addr_t uentry, loadaddr; - - kernel_size = load_uimage(kernel_filename, &uentry, &loadaddr, 0); - boot_info.bootstrap_pc = uentry; - high = (loadaddr + kernel_size + 3) & ~3; - } - - /* Not an ELF image nor an u-boot image, try a RAW image. */ - if (kernel_size < 0) { - kernel_size = load_image_targphys(kernel_filename, ddr_base, - ram_size); - boot_info.bootstrap_pc = ddr_base; - high = (ddr_base + kernel_size + 3) & ~3; - } - - boot_info.cmdline = high + 4096; - if (kernel_cmdline && strlen(kernel_cmdline)) { - pstrcpy_targphys("cmdline", boot_info.cmdline, 256, kernel_cmdline); - } - /* Provide a device-tree. */ - boot_info.fdt = boot_info.cmdline + 4096; - petalogix_load_device_tree(boot_info.fdt, ram_size, - 0, 0, - kernel_cmdline); - } + xilinx_timer_create(TIMER_BASEADDR, irq[0], 2, 62 * 1000000); + xilinx_ethlite_create(&nd_table[0], ETHLITE_BASEADDR, irq[1], 0, 0); + + microblaze_load_kernel(env, ddr_base, ram_size, + BINARY_DEVICE_TREE_FILE, machine_cpu_reset); } static QEMUMachine petalogix_s3adsp1800_machine = { diff --git a/hw/xilinx_zynq.c b/hw/xilinx_zynq.c new file mode 100644 index 0000000000..ea13e8c844 --- /dev/null +++ b/hw/xilinx_zynq.c @@ -0,0 +1,157 @@ +/* + * Xilinx Zynq Baseboard System emulation. + * + * Copyright (c) 2010 Xilinx. + * Copyright (c) 2012 Peter A.G. Crosthwaite (peter.croshtwaite@petalogix.com) + * Copyright (c) 2012 Petalogix Pty Ltd. + * Written by Haibing Ma + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "sysbus.h" +#include "arm-misc.h" +#include "net.h" +#include "exec-memory.h" +#include "sysemu.h" +#include "boards.h" +#include "flash.h" +#include "blockdev.h" +#include "loader.h" + +#define FLASH_SIZE (64 * 1024 * 1024) +#define FLASH_SECTOR_SIZE (128 * 1024) + +#define IRQ_OFFSET 32 /* pic interrupts start from index 32 */ + +static struct arm_boot_info zynq_binfo = {}; + +static void gem_init(NICInfo *nd, uint32_t base, qemu_irq irq) +{ + DeviceState *dev; + SysBusDevice *s; + + qemu_check_nic_model(nd, "cadence_gem"); + dev = qdev_create(NULL, "cadence_gem"); + qdev_set_nic_properties(dev, nd); + qdev_init_nofail(dev); + s = sysbus_from_qdev(dev); + sysbus_mmio_map(s, 0, base); + sysbus_connect_irq(s, 0, irq); +} + +static void zynq_init(ram_addr_t ram_size, const char *boot_device, + const char *kernel_filename, const char *kernel_cmdline, + const char *initrd_filename, const char *cpu_model) +{ + CPUState *env = NULL; + MemoryRegion *address_space_mem = get_system_memory(); + MemoryRegion *ext_ram = g_new(MemoryRegion, 1); + MemoryRegion *ocm_ram = g_new(MemoryRegion, 1); + DeviceState *dev; + SysBusDevice *busdev; + qemu_irq *irqp; + qemu_irq pic[64]; + NICInfo *nd; + int n; + qemu_irq cpu_irq; + + if (!cpu_model) { + cpu_model = "cortex-a9"; + } + + env = cpu_init(cpu_model); + if (!env) { + fprintf(stderr, "Unable to find CPU definition\n"); + exit(1); + } + irqp = arm_pic_init_cpu(env); + cpu_irq = irqp[ARM_PIC_CPU_IRQ]; + + /* max 2GB ram */ + if (ram_size > 0x80000000) { + ram_size = 0x80000000; + } + + /* DDR remapped to address zero. */ + memory_region_init_ram(ext_ram, "zynq.ext_ram", ram_size); + vmstate_register_ram_global(ext_ram); + memory_region_add_subregion(address_space_mem, 0, ext_ram); + + /* 256K of on-chip memory */ + memory_region_init_ram(ocm_ram, "zynq.ocm_ram", 256 << 10); + vmstate_register_ram_global(ocm_ram); + memory_region_add_subregion(address_space_mem, 0xFFFC0000, ocm_ram); + + DriveInfo *dinfo = drive_get(IF_PFLASH, 0, 0); + + /* AMD */ + pflash_cfi02_register(0xe2000000, NULL, "zynq.pflash", FLASH_SIZE, + dinfo ? dinfo->bdrv : NULL, FLASH_SECTOR_SIZE, + FLASH_SIZE/FLASH_SECTOR_SIZE, 1, + 1, 0x0066, 0x0022, 0x0000, 0x0000, 0x0555, 0x2aa, + 0); + + dev = qdev_create(NULL, "xilinx,zynq_slcr"); + qdev_init_nofail(dev); + sysbus_mmio_map(sysbus_from_qdev(dev), 0, 0xF8000000); + + dev = qdev_create(NULL, "a9mpcore_priv"); + qdev_prop_set_uint32(dev, "num-cpu", 1); + qdev_init_nofail(dev); + busdev = sysbus_from_qdev(dev); + sysbus_mmio_map(busdev, 0, 0xF8F00000); + sysbus_connect_irq(busdev, 0, cpu_irq); + + for (n = 0; n < 64; n++) { + pic[n] = qdev_get_gpio_in(dev, n); + } + + sysbus_create_simple("cadence_uart", 0xE0000000, pic[59-IRQ_OFFSET]); + sysbus_create_simple("cadence_uart", 0xE0001000, pic[82-IRQ_OFFSET]); + + sysbus_create_varargs("cadence_ttc", 0xF8001000, + pic[42-IRQ_OFFSET], pic[43-IRQ_OFFSET], pic[44-IRQ_OFFSET], NULL); + sysbus_create_varargs("cadence_ttc", 0xF8002000, + pic[69-IRQ_OFFSET], pic[70-IRQ_OFFSET], pic[71-IRQ_OFFSET], NULL); + + for (n = 0; n < nb_nics; n++) { + nd = &nd_table[n]; + if (n == 0) { + gem_init(nd, 0xE000B000, pic[54-IRQ_OFFSET]); + } else if (n == 1) { + gem_init(nd, 0xE000C000, pic[77-IRQ_OFFSET]); + } + } + + zynq_binfo.ram_size = ram_size; + zynq_binfo.kernel_filename = kernel_filename; + zynq_binfo.kernel_cmdline = kernel_cmdline; + zynq_binfo.initrd_filename = initrd_filename; + zynq_binfo.nb_cpus = 1; + zynq_binfo.board_id = 0xd32; + zynq_binfo.loader_start = 0; + arm_load_kernel(first_cpu, &zynq_binfo); +} + +static QEMUMachine zynq_machine = { + .name = "xilinx-zynq-a9", + .desc = "Xilinx Zynq Platform Baseboard for Cortex-A9", + .init = zynq_init, + .use_scsi = 1, + .max_cpus = 1, + .no_sdcard = 1 +}; + +static void zynq_machine_init(void) +{ + qemu_register_machine(&zynq_machine); +} + +machine_init(zynq_machine_init); diff --git a/hw/zynq_slcr.c b/hw/zynq_slcr.c new file mode 100644 index 0000000000..4f97575770 --- /dev/null +++ b/hw/zynq_slcr.c @@ -0,0 +1,535 @@ +/* + * Status and system control registers for Xilinx Zynq Platform + * + * Copyright (c) 2011 Michal Simek <monstr@monstr.eu> + * Copyright (c) 2012 PetaLogix Pty Ltd. + * Based on hw/arm_sysctl.c, written by Paul Brook + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "hw.h" +#include "qemu-timer.h" +#include "sysbus.h" +#include "sysemu.h" + +#ifdef ZYNQ_ARM_SLCR_ERR_DEBUG +#define DB_PRINT(...) do { \ + fprintf(stderr, ": %s: ", __func__); \ + fprintf(stderr, ## __VA_ARGS__); \ + } while (0); +#else + #define DB_PRINT(...) +#endif + +#define XILINX_LOCK_KEY 0x767b +#define XILINX_UNLOCK_KEY 0xdf0d + +typedef enum { + ARM_PLL_CTRL, + DDR_PLL_CTRL, + IO_PLL_CTRL, + PLL_STATUS, + ARM_PPL_CFG, + DDR_PLL_CFG, + IO_PLL_CFG, + PLL_BG_CTRL, + PLL_MAX +} PLLValues; + +typedef enum { + ARM_CLK_CTRL, + DDR_CLK_CTRL, + DCI_CLK_CTRL, + APER_CLK_CTRL, + USB0_CLK_CTRL, + USB1_CLK_CTRL, + GEM0_RCLK_CTRL, + GEM1_RCLK_CTRL, + GEM0_CLK_CTRL, + GEM1_CLK_CTRL, + SMC_CLK_CTRL, + LQSPI_CLK_CTRL, + SDIO_CLK_CTRL, + UART_CLK_CTRL, + SPI_CLK_CTRL, + CAN_CLK_CTRL, + CAN_MIOCLK_CTRL, + DBG_CLK_CTRL, + PCAP_CLK_CTRL, + TOPSW_CLK_CTRL, + CLK_MAX +} ClkValues; + +typedef enum { + CLK_CTRL, + THR_CTRL, + THR_CNT, + THR_STA, + FPGA_MAX +} FPGAValues; + +typedef enum { + SYNC_CTRL, + SYNC_STATUS, + BANDGAP_TRIP, + CC_TEST, + PLL_PREDIVISOR, + CLK_621_TRUE, + PICTURE_DBG, + PICTURE_DBG_UCNT, + PICTURE_DBG_LCNT, + MISC_MAX +} MiscValues; + +typedef enum { + PSS, + DDDR, + DMAC, + USB, + GEM, + SDIO, + SPI, + CAN, + I2C, + UART, + GPIO, + LQSPI, + SMC, + OCM, + DEVCI, + FPGA, + A9_CPU, + RS_AWDT, + RST_REASON, + RST_REASON_CLR, + REBOOT_STATUS, + BOOT_MODE, + RESET_MAX +} ResetValues; + +typedef struct { + SysBusDevice busdev; + MemoryRegion iomem; + + union { + struct { + uint16_t scl; + uint16_t lockval; + uint32_t pll[PLL_MAX]; /* 0x100 - 0x11C */ + uint32_t clk[CLK_MAX]; /* 0x120 - 0x16C */ + uint32_t fpga[4][FPGA_MAX]; /* 0x170 - 0x1AC */ + uint32_t misc[MISC_MAX]; /* 0x1B0 - 0x1D8 */ + uint32_t reset[RESET_MAX]; /* 0x200 - 0x25C */ + uint32_t apu_ctrl; /* 0x300 */ + uint32_t wdt_clk_sel; /* 0x304 */ + uint32_t tz_ocm[3]; /* 0x400 - 0x408 */ + uint32_t tz_ddr; /* 0x430 */ + uint32_t tz_dma[3]; /* 0x440 - 0x448 */ + uint32_t tz_misc[3]; /* 0x450 - 0x458 */ + uint32_t tz_fpga[2]; /* 0x484 - 0x488 */ + uint32_t dbg_ctrl; /* 0x500 */ + uint32_t pss_idcode; /* 0x530 */ + uint32_t ddr[8]; /* 0x600 - 0x620 - 0x604-missing */ + uint32_t mio[54]; /* 0x700 - 0x7D4 */ + uint32_t mio_func[4]; /* 0x800 - 0x810 */ + uint32_t sd[2]; /* 0x830 - 0x834 */ + uint32_t lvl_shftr_en; /* 0x900 */ + uint32_t ocm_cfg; /* 0x910 */ + uint32_t cpu_ram[8]; /* 0xA00 - 0xA1C */ + uint32_t iou[7]; /* 0xA30 - 0xA48 */ + uint32_t dmac_ram; /* 0xA50 */ + uint32_t afi[4][3]; /* 0xA60 - 0xA8C */ + uint32_t ocm[3]; /* 0xA90 - 0xA98 */ + uint32_t devci_ram; /* 0xAA0 */ + uint32_t csg_ram; /* 0xAB0 */ + uint32_t gpiob[12]; /* 0xB00 - 0xB2C */ + uint32_t ddriob[14]; /* 0xB40 - 0xB74 */ + }; + uint8_t data[0x1000]; + }; +} ZynqSLCRState; + +static void zynq_slcr_reset(DeviceState *d) +{ + int i; + ZynqSLCRState *s = + FROM_SYSBUS(ZynqSLCRState, sysbus_from_qdev(d)); + + DB_PRINT("RESET\n"); + + s->lockval = 1; + /* 0x100 - 0x11C */ + s->pll[ARM_PLL_CTRL] = 0x0001A008; + s->pll[DDR_PLL_CTRL] = 0x0001A008; + s->pll[IO_PLL_CTRL] = 0x0001A008; + s->pll[PLL_STATUS] = 0x0000003F; + s->pll[ARM_PPL_CFG] = 0x00014000; + s->pll[DDR_PLL_CFG] = 0x00014000; + s->pll[IO_PLL_CFG] = 0x00014000; + + /* 0x120 - 0x16C */ + s->clk[ARM_CLK_CTRL] = 0x1F000400; + s->clk[DDR_CLK_CTRL] = 0x18400003; + s->clk[DCI_CLK_CTRL] = 0x01E03201; + s->clk[APER_CLK_CTRL] = 0x01FFCCCD; + s->clk[USB0_CLK_CTRL] = s->clk[USB1_CLK_CTRL] = 0x00101941; + s->clk[GEM0_RCLK_CTRL] = s->clk[GEM1_RCLK_CTRL] = 0x00000001; + s->clk[GEM0_CLK_CTRL] = s->clk[GEM1_CLK_CTRL] = 0x00003C01; + s->clk[SMC_CLK_CTRL] = 0x00003C01; + s->clk[LQSPI_CLK_CTRL] = 0x00002821; + s->clk[SDIO_CLK_CTRL] = 0x00001E03; + s->clk[UART_CLK_CTRL] = 0x00003F03; + s->clk[SPI_CLK_CTRL] = 0x00003F03; + s->clk[CAN_CLK_CTRL] = 0x00501903; + s->clk[DBG_CLK_CTRL] = 0x00000F03; + s->clk[PCAP_CLK_CTRL] = 0x00000F01; + + /* 0x170 - 0x1AC */ + s->fpga[0][CLK_CTRL] = s->fpga[1][CLK_CTRL] = s->fpga[2][CLK_CTRL] = + s->fpga[3][CLK_CTRL] = 0x00101800; + s->fpga[0][THR_STA] = s->fpga[1][THR_STA] = s->fpga[2][THR_STA] = + s->fpga[3][THR_STA] = 0x00010000; + + /* 0x1B0 - 0x1D8 */ + s->misc[BANDGAP_TRIP] = 0x0000001F; + s->misc[PLL_PREDIVISOR] = 0x00000001; + s->misc[CLK_621_TRUE] = 0x00000001; + + /* 0x200 - 0x25C */ + s->reset[FPGA] = 0x01F33F0F; + s->reset[RST_REASON] = 0x00000040; + + /* 0x700 - 0x7D4 */ + for (i = 0; i < 54; i++) { + s->mio[i] = 0x00001601; + } + for (i = 2; i <= 8; i++) { + s->mio[i] = 0x00000601; + } + + /* MIO_MST_TRI0, MIO_MST_TRI1 */ + s->mio_func[2] = s->mio_func[3] = 0xFFFFFFFF; + + s->cpu_ram[0] = s->cpu_ram[1] = s->cpu_ram[3] = + s->cpu_ram[4] = s->cpu_ram[7] = 0x00010101; + s->cpu_ram[2] = s->cpu_ram[5] = 0x01010101; + s->cpu_ram[6] = 0x00000001; + + s->iou[0] = s->iou[1] = s->iou[2] = s->iou[3] = 0x09090909; + s->iou[4] = s->iou[5] = 0x00090909; + s->iou[6] = 0x00000909; + + s->dmac_ram = 0x00000009; + + s->afi[0][0] = s->afi[0][1] = 0x09090909; + s->afi[1][0] = s->afi[1][1] = 0x09090909; + s->afi[2][0] = s->afi[2][1] = 0x09090909; + s->afi[3][0] = s->afi[3][1] = 0x09090909; + s->afi[0][2] = s->afi[1][2] = s->afi[2][2] = s->afi[3][2] = 0x00000909; + + s->ocm[0] = 0x01010101; + s->ocm[1] = s->ocm[2] = 0x09090909; + + s->devci_ram = 0x00000909; + s->csg_ram = 0x00000001; + + s->ddriob[0] = s->ddriob[1] = s->ddriob[2] = s->ddriob[3] = 0x00000e00; + s->ddriob[4] = s->ddriob[5] = s->ddriob[6] = 0x00000e00; + s->ddriob[12] = 0x00000021; +} + +static inline uint32_t zynq_slcr_read_imp(void *opaque, + target_phys_addr_t offset) +{ + ZynqSLCRState *s = (ZynqSLCRState *)opaque; + + switch (offset) { + case 0x0: /* SCL */ + return s->scl; + case 0x4: /* LOCK */ + case 0x8: /* UNLOCK */ + DB_PRINT("Reading SCLR_LOCK/UNLOCK is not enabled\n"); + return 0; + case 0x0C: /* LOCKSTA */ + return s->lockval; + case 0x100 ... 0x11C: + return s->pll[(offset - 0x100) / 4]; + case 0x120 ... 0x16C: + return s->clk[(offset - 0x120) / 4]; + case 0x170 ... 0x1AC: + return s->fpga[0][(offset - 0x170) / 4]; + case 0x1B0 ... 0x1D8: + return s->misc[(offset - 0x1B0) / 4]; + case 0x200 ... 0x258: + return s->reset[(offset - 0x200) / 4]; + case 0x25c: + return 1; + case 0x300: + return s->apu_ctrl; + case 0x304: + return s->wdt_clk_sel; + case 0x400 ... 0x408: + return s->tz_ocm[(offset - 0x400) / 4]; + case 0x430: + return s->tz_ddr; + case 0x440 ... 0x448: + return s->tz_dma[(offset - 0x440) / 4]; + case 0x450 ... 0x458: + return s->tz_misc[(offset - 0x450) / 4]; + case 0x484 ... 0x488: + return s->tz_fpga[(offset - 0x484) / 4]; + case 0x500: + return s->dbg_ctrl; + case 0x530: + return s->pss_idcode; + case 0x600 ... 0x620: + if (offset == 0x604) { + goto bad_reg; + } + return s->ddr[(offset - 0x600) / 4]; + case 0x700 ... 0x7D4: + return s->mio[(offset - 0x700) / 4]; + case 0x800 ... 0x810: + return s->mio_func[(offset - 0x800) / 4]; + case 0x830 ... 0x834: + return s->sd[(offset - 0x830) / 4]; + case 0x900: + return s->lvl_shftr_en; + case 0x910: + return s->ocm_cfg; + case 0xA00 ... 0xA1C: + return s->cpu_ram[(offset - 0xA00) / 4]; + case 0xA30 ... 0xA48: + return s->iou[(offset - 0xA30) / 4]; + case 0xA50: + return s->dmac_ram; + case 0xA60 ... 0xA8C: + return s->afi[0][(offset - 0xA60) / 4]; + case 0xA90 ... 0xA98: + return s->ocm[(offset - 0xA90) / 4]; + case 0xAA0: + return s->devci_ram; + case 0xAB0: + return s->csg_ram; + case 0xB00 ... 0xB2C: + return s->gpiob[(offset - 0xB00) / 4]; + case 0xB40 ... 0xB74: + return s->ddriob[(offset - 0xB40) / 4]; + default: + bad_reg: + DB_PRINT("Bad register offset 0x%x\n", (int)offset); + return 0; + } +} + +static uint64_t zynq_slcr_read(void *opaque, target_phys_addr_t offset, + unsigned size) +{ + uint32_t ret = zynq_slcr_read_imp(opaque, offset); + + DB_PRINT("addr: %08x data: %08x\n", offset, ret); + return ret; +} + +static void zynq_slcr_write(void *opaque, target_phys_addr_t offset, + uint64_t val, unsigned size) +{ + ZynqSLCRState *s = (ZynqSLCRState *)opaque; + + DB_PRINT("offset: %08x data: %08x\n", offset, (unsigned)val); + + switch (offset) { + case 0x00: /* SCL */ + s->scl = val & 0x1; + return; + case 0x4: /* SLCR_LOCK */ + if ((val & 0xFFFF) == XILINX_LOCK_KEY) { + DB_PRINT("XILINX LOCK 0xF8000000 + 0x%x <= 0x%x\n", (int)offset, + (unsigned)val & 0xFFFF); + s->lockval = 1; + } else { + DB_PRINT("WRONG XILINX LOCK KEY 0xF8000000 + 0x%x <= 0x%x\n", + (int)offset, (unsigned)val & 0xFFFF); + } + return; + case 0x8: /* SLCR_UNLOCK */ + if ((val & 0xFFFF) == XILINX_UNLOCK_KEY) { + DB_PRINT("XILINX UNLOCK 0xF8000000 + 0x%x <= 0x%x\n", (int)offset, + (unsigned)val & 0xFFFF); + s->lockval = 0; + } else { + DB_PRINT("WRONG XILINX UNLOCK KEY 0xF8000000 + 0x%x <= 0x%x\n", + (int)offset, (unsigned)val & 0xFFFF); + } + return; + case 0xc: /* LOCKSTA */ + DB_PRINT("Writing SCLR_LOCKSTA is not enabled\n"); + return; + } + + if (!s->lockval) { + switch (offset) { + case 0x100 ... 0x11C: + if (offset == 0x10C) { + goto bad_reg; + } + s->pll[(offset - 0x100) / 4] = val; + break; + case 0x120 ... 0x16C: + s->clk[(offset - 0x120) / 4] = val; + break; + case 0x170 ... 0x1AC: + s->fpga[0][(offset - 0x170) / 4] = val; + break; + case 0x1B0 ... 0x1D8: + s->misc[(offset - 0x1B0) / 4] = val; + break; + case 0x200 ... 0x25C: + if (offset == 0x250) { + goto bad_reg; + } + s->reset[(offset - 0x200) / 4] = val; + break; + case 0x300: + s->apu_ctrl = val; + break; + case 0x304: + s->wdt_clk_sel = val; + break; + case 0x400 ... 0x408: + s->tz_ocm[(offset - 0x400) / 4] = val; + break; + case 0x430: + s->tz_ddr = val; + break; + case 0x440 ... 0x448: + s->tz_dma[(offset - 0x440) / 4] = val; + break; + case 0x450 ... 0x458: + s->tz_misc[(offset - 0x450) / 4] = val; + break; + case 0x484 ... 0x488: + s->tz_fpga[(offset - 0x484) / 4] = val; + break; + case 0x500: + s->dbg_ctrl = val; + break; + case 0x530: + s->pss_idcode = val; + break; + case 0x600 ... 0x620: + if (offset == 0x604) { + goto bad_reg; + } + s->ddr[(offset - 0x600) / 4] = val; + break; + case 0x700 ... 0x7D4: + s->mio[(offset - 0x700) / 4] = val; + break; + case 0x800 ... 0x810: + s->mio_func[(offset - 0x800) / 4] = val; + break; + case 0x830 ... 0x834: + s->sd[(offset - 0x830) / 4] = val; + break; + case 0x900: + s->lvl_shftr_en = val; + break; + case 0x910: + break; + case 0xA00 ... 0xA1C: + s->cpu_ram[(offset - 0xA00) / 4] = val; + break; + case 0xA30 ... 0xA48: + s->iou[(offset - 0xA30) / 4] = val; + break; + case 0xA50: + s->dmac_ram = val; + break; + case 0xA60 ... 0xA8C: + s->afi[0][(offset - 0xA60) / 4] = val; + break; + case 0xA90: + s->ocm[0] = val; + break; + case 0xAA0: + s->devci_ram = val; + break; + case 0xAB0: + s->csg_ram = val; + break; + case 0xB00 ... 0xB2C: + if (offset == 0xB20 || offset == 0xB2C) { + goto bad_reg; + } + s->gpiob[(offset - 0xB00) / 4] = val; + break; + case 0xB40 ... 0xB74: + s->ddriob[(offset - 0xB40) / 4] = val; + break; + default: + bad_reg: + DB_PRINT("Bad register write %x <= %08x\n", (int)offset, val); + } + } else { + DB_PRINT("SCLR registers are locked. Unlock them first\n"); + } +} + +static const MemoryRegionOps slcr_ops = { + .read = zynq_slcr_read, + .write = zynq_slcr_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static int zynq_slcr_init(SysBusDevice *dev) +{ + ZynqSLCRState *s = FROM_SYSBUS(ZynqSLCRState, dev); + + memory_region_init_io(&s->iomem, &slcr_ops, s, "slcr", 0x1000); + sysbus_init_mmio(dev, &s->iomem); + + return 0; +} + +static const VMStateDescription vmstate_zynq_slcr = { + .name = "zynq_slcr", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8_ARRAY(data, ZynqSLCRState, 0x1000), + VMSTATE_END_OF_LIST() + } +}; + +static void zynq_slcr_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); + + sdc->init = zynq_slcr_init; + dc->vmsd = &vmstate_zynq_slcr; + dc->reset = zynq_slcr_reset; +} + +static TypeInfo zynq_slcr_info = { + .class_init = zynq_slcr_class_init, + .name = "xilinx,zynq_slcr", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(ZynqSLCRState), +}; + +static void zynq_slcr_register_types(void) +{ + type_register_static(&zynq_slcr_info); +} + +type_init(zynq_slcr_register_types) diff --git a/libcacard/vcardt.h b/libcacard/vcardt.h index d4d8e2ed18..d3e9522774 100644 --- a/libcacard/vcardt.h +++ b/libcacard/vcardt.h @@ -26,8 +26,8 @@ typedef struct VCardEmulStruct VCardEmul; #define MAX_CHANNEL 4 /* create an ATR with appropriate historical bytes */ -#define VCARD_ATR_PREFIX(size) (0x3b, 0x68+(size), 0x00, 0xff, \ - 'V', 'C', 'A', 'R', 'D', '_') +#define VCARD_ATR_PREFIX(size) 0x3b, 0x68+(size), 0x00, 0xff, \ + 'V', 'C', 'A', 'R', 'D', '_' typedef enum { diff --git a/qemu-options.hx b/qemu-options.hx index e38799c648..daefce3038 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -2038,7 +2038,7 @@ first module. ETEXI DEF("dtb", HAS_ARG, QEMU_OPTION_dtb, \ - "-dtb file use 'file' as device tree image\n", QEMU_ARCH_ARM) + "-dtb file use 'file' as device tree image\n", QEMU_ARCH_ALL) STEXI @item -dtb @var{file} @findex -dtb |