diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2021-03-08 11:57:36 +0000 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2021-03-08 11:57:36 +0000 |
commit | 138d2931979cb7ee4a54a434a54088231f6980ff (patch) | |
tree | 4ef6803dee0413e8c30de657a9d61d49991d9f2a /hw | |
parent | 91e92cad67caca3bc4b8e920ddb5c8ca64aac9e1 (diff) | |
parent | 50b52b18cdb9294ce83dd49bb60b8e55a6526ea0 (diff) |
Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20210308' into staging
target-arm queue:
* sbsa-ref: remove cortex-a53 from list of supported cpus
* sbsa-ref: add 'max' to list of allowed cpus
* target/arm: Add support for FEAT_SSBS, Speculative Store Bypass Safe
* npcm7xx: add EMC model
* xlnx-zynqmp: Remove obsolete 'has_rpu' property
* target/arm: Speed up aarch64 TBL/TBX
* virtio-mmio: improve virtio-mmio get_dev_path alog
* target/arm: Use TCF0 and TFSRE0 for unprivileged tag checks
* target/arm: Restrict v8M IDAU to TCG
* target/arm/cpu: Update coding style to make checkpatch.pl happy
* musicpal, tc6393xb, omap_lcdc, tcx: drop dead code for non-32-bit-RGB surfaces
* Add new board: mps3-an524
# gpg: Signature made Mon 08 Mar 2021 11:56:24 GMT
# gpg: using RSA key E1A5C593CD419DE28E8315CF3C2525ED14360CDE
# gpg: issuer "peter.maydell@linaro.org"
# gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" [ultimate]
# gpg: aka "Peter Maydell <pmaydell@gmail.com>" [ultimate]
# gpg: aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" [ultimate]
# Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83 15CF 3C25 25ED 1436 0CDE
* remotes/pmaydell/tags/pull-target-arm-20210308: (49 commits)
hw/arm/mps2: Update old infocenter.arm.com URLs
docs/system/arm/mps2.rst: Document the new mps3-an524 board
hw/arm/mps2-tz: Provide PL031 RTC on mps3-an524
hw/arm/mps2-tz: Stub out USB controller for mps3-an524
hw/arm/mps2-tz: Add new mps3-an524 board
hw/arm/mps2-tz: Get armv7m_load_kernel() size argument from RAMInfo
hw/arm/mps2-tz: Support ROMs as well as RAMs
hw/arm/mps2-tz: Set MachineClass default_ram info from RAMInfo data
hw/arm/mps2-tz: Make RAM arrangement board-specific
hw/arm/mps2-tz: Allow boards to have different PPCInfo data
hw/arm/mps2-tz: Size the uart-irq-orgate based on the number of UARTs
hw/arm/mps2-tz: Move device IRQ info to data structures
hw/arm/mps2-tz: Allow PPCPortInfo structures to specify device interrupts
hw/arm/mps2-tz: Correct wrong interrupt numbers for DMA and SPI
hw/misc/mps2-scc: Implement CFG_REG5 and CFG_REG6 for MPS3 AN524
hw/arm/mps2-tz: Make number of IRQs board-specific
hw/arm/mps2-tz: Condition IRQ splitting on number of CPUs, not board type
hw/arm/mps2-tz: Make FPGAIO switch and LED config per-board
hw/misc/mps2-fpgaio: Support SWITCH register
hw/misc/mps2-fpgaio: Make number of LEDs configurable by board
...
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw')
-rw-r--r-- | hw/arm/mps2-tz.c | 642 | ||||
-rw-r--r-- | hw/arm/mps2.c | 5 | ||||
-rw-r--r-- | hw/arm/musicpal.c | 64 | ||||
-rw-r--r-- | hw/arm/npcm7xx.c | 50 | ||||
-rw-r--r-- | hw/arm/sbsa-ref.c | 2 | ||||
-rw-r--r-- | hw/arm/xlnx-zynqmp.c | 6 | ||||
-rw-r--r-- | hw/display/omap_lcd_template.h | 169 | ||||
-rw-r--r-- | hw/display/omap_lcdc.c | 129 | ||||
-rw-r--r-- | hw/display/tc6393xb.c | 50 | ||||
-rw-r--r-- | hw/display/tc6393xb_template.h | 72 | ||||
-rw-r--r-- | hw/display/tcx.c | 31 | ||||
-rw-r--r-- | hw/i2c/npcm7xx_smbus.c | 1 | ||||
-rw-r--r-- | hw/misc/armsse-cpuid.c | 2 | ||||
-rw-r--r-- | hw/misc/armsse-mhu.c | 2 | ||||
-rw-r--r-- | hw/misc/iotkit-sysctl.c | 2 | ||||
-rw-r--r-- | hw/misc/iotkit-sysinfo.c | 2 | ||||
-rw-r--r-- | hw/misc/mps2-fpgaio.c | 43 | ||||
-rw-r--r-- | hw/misc/mps2-scc.c | 93 | ||||
-rw-r--r-- | hw/net/meson.build | 1 | ||||
-rw-r--r-- | hw/net/npcm7xx_emc.c | 857 | ||||
-rw-r--r-- | hw/net/trace-events | 17 | ||||
-rw-r--r-- | hw/virtio/virtio-mmio.c | 13 |
22 files changed, 1758 insertions, 495 deletions
diff --git a/hw/arm/mps2-tz.c b/hw/arm/mps2-tz.c index 90caa91493..72da8cb1a1 100644 --- a/hw/arm/mps2-tz.c +++ b/hw/arm/mps2-tz.c @@ -16,25 +16,27 @@ * This source file covers the following FPGA images, for TrustZone cores: * "mps2-an505" -- Cortex-M33 as documented in ARM Application Note AN505 * "mps2-an521" -- Dual Cortex-M33 as documented in Application Note AN521 + * "mps2-an524" -- Dual Cortex-M33 as documented in Application Note AN524 * * Links to the TRM for the board itself and to the various Application * Notes which document the FPGA images can be found here: * https://developer.arm.com/products/system-design/development-boards/fpga-prototyping-boards/mps2 * * Board TRM: - * http://infocenter.arm.com/help/topic/com.arm.doc.100112_0200_06_en/versatile_express_cortex_m_prototyping_systems_v2m_mps2_and_v2m_mps2plus_technical_reference_100112_0200_06_en.pdf + * https://developer.arm.com/documentation/100112/latest/ * Application Note AN505: - * http://infocenter.arm.com/help/topic/com.arm.doc.dai0505b/index.html + * https://developer.arm.com/documentation/dai0505/latest/ * Application Note AN521: - * http://infocenter.arm.com/help/topic/com.arm.doc.dai0521c/index.html + * https://developer.arm.com/documentation/dai0521/latest/ + * Application Note AN524: + * https://developer.arm.com/documentation/dai0524/latest/ * * The AN505 defers to the Cortex-M33 processor ARMv8M IoT Kit FVP User Guide * (ARM ECM0601256) for the details of some of the device layout: - * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ecm0601256/index.html - * Similarly, the AN521 uses the SSE-200, and the SSE-200 TRM defines + * https://developer.arm.com/documentation/ecm0601256/latest + * Similarly, the AN521 and AN524 use the SSE-200, and the SSE-200 TRM defines * most of the device layout: - * http://infocenter.arm.com/help/topic/com.arm.doc.101104_0100_00_en/corelink_sse200_subsystem_for_embedded_technical_reference_manual_101104_0100_00_en.pdf - * + * https://developer.arm.com/documentation/101104/latest/ */ #include "qemu/osdep.h" @@ -57,6 +59,7 @@ #include "hw/misc/tz-msc.h" #include "hw/arm/armsse.h" #include "hw/dma/pl080.h" +#include "hw/rtc/pl031.h" #include "hw/ssi/pl022.h" #include "hw/i2c/arm_sbcon_i2c.h" #include "hw/net/lan9118.h" @@ -65,17 +68,50 @@ #include "hw/qdev-clock.h" #include "qom/object.h" -#define MPS2TZ_NUMIRQ 92 +#define MPS2TZ_NUMIRQ_MAX 95 +#define MPS2TZ_RAM_MAX 4 typedef enum MPS2TZFPGAType { FPGA_AN505, FPGA_AN521, + FPGA_AN524, } MPS2TZFPGAType; +/* + * Define the layout of RAM in a board, including which parts are + * behind which MPCs. + * mrindex specifies the index into mms->ram[] to use for the backing RAM; + * -1 means "use the system RAM". + */ +typedef struct RAMInfo { + const char *name; + uint32_t base; + uint32_t size; + int mpc; /* MPC number, -1 for "not behind an MPC" */ + int mrindex; + int flags; +} RAMInfo; + +/* + * Flag values: + * IS_ALIAS: this RAM area is an alias to the upstream end of the + * MPC specified by its .mpc value + * IS_ROM: this RAM area is read-only + */ +#define IS_ALIAS 1 +#define IS_ROM 2 + struct MPS2TZMachineClass { MachineClass parent; MPS2TZFPGAType fpga_type; uint32_t scc_id; + uint32_t sysclk_frq; /* Main SYSCLK frequency in Hz */ + uint32_t len_oscclk; + const uint32_t *oscclk; + uint32_t fpgaio_num_leds; /* Number of LEDs in FPGAIO LED0 register */ + bool fpgaio_has_switches; /* Does FPGAIO have SWITCH register? */ + int numirq; /* Number of external interrupts */ + const RAMInfo *raminfo; const char *armsse_type; }; @@ -83,24 +119,28 @@ struct MPS2TZMachineState { MachineState parent; ARMSSE iotkit; - MemoryRegion ssram[3]; - MemoryRegion ssram1_m; + MemoryRegion ram[MPS2TZ_RAM_MAX]; + MemoryRegion eth_usb_container; + MPS2SCC scc; MPS2FPGAIO fpgaio; TZPPC ppc[5]; - TZMPC ssram_mpc[3]; + TZMPC mpc[3]; PL022State spi[5]; - ArmSbconI2CState i2c[4]; + ArmSbconI2CState i2c[5]; UnimplementedDeviceState i2s_audio; UnimplementedDeviceState gpio[4]; UnimplementedDeviceState gfx; + UnimplementedDeviceState cldc; + UnimplementedDeviceState usb; + PL031State rtc; PL080State dma[4]; TZMSC msc[4]; - CMSDKAPBUART uart[5]; + CMSDKAPBUART uart[6]; SplitIRQ sec_resp_splitter; qemu_or_irq uart_irq_orgate; DeviceState *lan9118; - SplitIRQ cpu_irq_splitter[MPS2TZ_NUMIRQ]; + SplitIRQ cpu_irq_splitter[MPS2TZ_NUMIRQ_MAX]; Clock *sysclk; Clock *s32kclk; }; @@ -108,14 +148,144 @@ struct MPS2TZMachineState { #define TYPE_MPS2TZ_MACHINE "mps2tz" #define TYPE_MPS2TZ_AN505_MACHINE MACHINE_TYPE_NAME("mps2-an505") #define TYPE_MPS2TZ_AN521_MACHINE MACHINE_TYPE_NAME("mps2-an521") +#define TYPE_MPS3TZ_AN524_MACHINE MACHINE_TYPE_NAME("mps3-an524") OBJECT_DECLARE_TYPE(MPS2TZMachineState, MPS2TZMachineClass, MPS2TZ_MACHINE) -/* Main SYSCLK frequency in Hz */ -#define SYSCLK_FRQ 20000000 /* Slow 32Khz S32KCLK frequency in Hz */ #define S32KCLK_FRQ (32 * 1000) +/* + * The MPS3 DDR is 2GiB, but on a 32-bit host QEMU doesn't permit + * emulation of that much guest RAM, so artificially make it smaller. + */ +#if HOST_LONG_BITS == 32 +#define MPS3_DDR_SIZE (1 * GiB) +#else +#define MPS3_DDR_SIZE (2 * GiB) +#endif + +static const uint32_t an505_oscclk[] = { + 40000000, + 24580000, + 25000000, +}; + +static const uint32_t an524_oscclk[] = { + 24000000, + 32000000, + 50000000, + 50000000, + 24576000, + 23750000, +}; + +static const RAMInfo an505_raminfo[] = { { + .name = "ssram-0", + .base = 0x00000000, + .size = 0x00400000, + .mpc = 0, + .mrindex = 0, + }, { + .name = "ssram-1", + .base = 0x28000000, + .size = 0x00200000, + .mpc = 1, + .mrindex = 1, + }, { + .name = "ssram-2", + .base = 0x28200000, + .size = 0x00200000, + .mpc = 2, + .mrindex = 2, + }, { + .name = "ssram-0-alias", + .base = 0x00400000, + .size = 0x00400000, + .mpc = 0, + .mrindex = 3, + .flags = IS_ALIAS, + }, { + /* Use the largest bit of contiguous RAM as our "system memory" */ + .name = "mps.ram", + .base = 0x80000000, + .size = 16 * MiB, + .mpc = -1, + .mrindex = -1, + }, { + .name = NULL, + }, +}; + +static const RAMInfo an524_raminfo[] = { { + .name = "bram", + .base = 0x00000000, + .size = 512 * KiB, + .mpc = 0, + .mrindex = 0, + }, { + .name = "sram", + .base = 0x20000000, + .size = 32 * 4 * KiB, + .mpc = 1, + .mrindex = 1, + }, { + /* We don't model QSPI flash yet; for now expose it as simple ROM */ + .name = "QSPI", + .base = 0x28000000, + .size = 8 * MiB, + .mpc = 1, + .mrindex = 2, + .flags = IS_ROM, + }, { + .name = "DDR", + .base = 0x60000000, + .size = MPS3_DDR_SIZE, + .mpc = 2, + .mrindex = -1, + }, { + .name = NULL, + }, +}; + +static const RAMInfo *find_raminfo_for_mpc(MPS2TZMachineState *mms, int mpc) +{ + MPS2TZMachineClass *mmc = MPS2TZ_MACHINE_GET_CLASS(mms); + const RAMInfo *p; + + for (p = mmc->raminfo; p->name; p++) { + if (p->mpc == mpc && !(p->flags & IS_ALIAS)) { + return p; + } + } + /* if raminfo array doesn't have an entry for each MPC this is a bug */ + g_assert_not_reached(); +} + +static MemoryRegion *mr_for_raminfo(MPS2TZMachineState *mms, + const RAMInfo *raminfo) +{ + /* Return an initialized MemoryRegion for the RAMInfo. */ + MemoryRegion *ram; + + if (raminfo->mrindex < 0) { + /* Means this RAMInfo is for QEMU's "system memory" */ + MachineState *machine = MACHINE(mms); + assert(!(raminfo->flags & IS_ROM)); + return machine->ram; + } + + assert(raminfo->mrindex < MPS2TZ_RAM_MAX); + ram = &mms->ram[raminfo->mrindex]; + + memory_region_init_ram(ram, NULL, raminfo->name, + raminfo->size, &error_fatal); + if (raminfo->flags & IS_ROM) { + memory_region_set_readonly(ram, true); + } + return ram; +} + /* Create an alias of an entire original MemoryRegion @orig * located at @base in the memory map. */ @@ -129,18 +299,26 @@ static void make_ram_alias(MemoryRegion *mr, const char *name, static qemu_irq get_sse_irq_in(MPS2TZMachineState *mms, int irqno) { - /* Return a qemu_irq which will signal IRQ n to all CPUs in the SSE. */ + /* + * Return a qemu_irq which will signal IRQ n to all CPUs in the + * SSE. The irqno should be as the CPU sees it, so the first + * external-to-the-SSE interrupt is 32. + */ + MachineClass *mc = MACHINE_GET_CLASS(mms); MPS2TZMachineClass *mmc = MPS2TZ_MACHINE_GET_CLASS(mms); - assert(irqno < MPS2TZ_NUMIRQ); + assert(irqno >= 32 && irqno < (mmc->numirq + 32)); - switch (mmc->fpga_type) { - case FPGA_AN505: - return qdev_get_gpio_in_named(DEVICE(&mms->iotkit), "EXP_IRQ", irqno); - case FPGA_AN521: + /* + * Convert from "CPU irq number" (as listed in the FPGA image + * documentation) to the SSE external-interrupt number. + */ + irqno -= 32; + + if (mc->max_cpus > 1) { return qdev_get_gpio_in(DEVICE(&mms->cpu_irq_splitter[irqno]), 0); - default: - g_assert_not_reached(); + } else { + return qdev_get_gpio_in_named(DEVICE(&mms->iotkit), "EXP_IRQ", irqno); } } @@ -152,7 +330,8 @@ static qemu_irq get_sse_irq_in(MPS2TZMachineState *mms, int irqno) * needs to be plugged into the downstream end of the PPC port. */ typedef MemoryRegion *MakeDevFn(MPS2TZMachineState *mms, void *opaque, - const char *name, hwaddr size); + const char *name, hwaddr size, + const int *irqs); typedef struct PPCPortInfo { const char *name; @@ -160,6 +339,7 @@ typedef struct PPCPortInfo { void *opaque; hwaddr addr; hwaddr size; + int irqs[3]; /* currently no device needs more IRQ lines than this */ } PPCPortInfo; typedef struct PPCInfo { @@ -168,8 +348,9 @@ typedef struct PPCInfo { } PPCInfo; static MemoryRegion *make_unimp_dev(MPS2TZMachineState *mms, - void *opaque, - const char *name, hwaddr size) + void *opaque, + const char *name, hwaddr size, + const int *irqs) { /* Initialize, configure and realize a TYPE_UNIMPLEMENTED_DEVICE, * and return a pointer to its MemoryRegion. @@ -184,57 +365,69 @@ static MemoryRegion *make_unimp_dev(MPS2TZMachineState *mms, } static MemoryRegion *make_uart(MPS2TZMachineState *mms, void *opaque, - const char *name, hwaddr size) + const char *name, hwaddr size, + const int *irqs) { + /* The irq[] array is tx, rx, combined, in that order */ + MPS2TZMachineClass *mmc = MPS2TZ_MACHINE_GET_CLASS(mms); CMSDKAPBUART *uart = opaque; int i = uart - &mms->uart[0]; - int rxirqno = i * 2; - int txirqno = i * 2 + 1; - int combirqno = i + 10; SysBusDevice *s; DeviceState *orgate_dev = DEVICE(&mms->uart_irq_orgate); object_initialize_child(OBJECT(mms), name, uart, TYPE_CMSDK_APB_UART); qdev_prop_set_chr(DEVICE(uart), "chardev", serial_hd(i)); - qdev_prop_set_uint32(DEVICE(uart), "pclk-frq", SYSCLK_FRQ); + qdev_prop_set_uint32(DEVICE(uart), "pclk-frq", mmc->sysclk_frq); sysbus_realize(SYS_BUS_DEVICE(uart), &error_fatal); s = SYS_BUS_DEVICE(uart); - sysbus_connect_irq(s, 0, get_sse_irq_in(mms, txirqno)); - sysbus_connect_irq(s, 1, get_sse_irq_in(mms, rxirqno)); + sysbus_connect_irq(s, 0, get_sse_irq_in(mms, irqs[0])); + sysbus_connect_irq(s, 1, get_sse_irq_in(mms, irqs[1])); sysbus_connect_irq(s, 2, qdev_get_gpio_in(orgate_dev, i * 2)); sysbus_connect_irq(s, 3, qdev_get_gpio_in(orgate_dev, i * 2 + 1)); - sysbus_connect_irq(s, 4, get_sse_irq_in(mms, combirqno)); + sysbus_connect_irq(s, 4, get_sse_irq_in(mms, irqs[2])); return sysbus_mmio_get_region(SYS_BUS_DEVICE(uart), 0); } static MemoryRegion *make_scc(MPS2TZMachineState *mms, void *opaque, - const char *name, hwaddr size) + const char *name, hwaddr size, + const int *irqs) { MPS2SCC *scc = opaque; DeviceState *sccdev; MPS2TZMachineClass *mmc = MPS2TZ_MACHINE_GET_CLASS(mms); + uint32_t i; object_initialize_child(OBJECT(mms), "scc", scc, TYPE_MPS2_SCC); sccdev = DEVICE(scc); qdev_prop_set_uint32(sccdev, "scc-cfg4", 0x2); qdev_prop_set_uint32(sccdev, "scc-aid", 0x00200008); qdev_prop_set_uint32(sccdev, "scc-id", mmc->scc_id); + qdev_prop_set_uint32(sccdev, "len-oscclk", mmc->len_oscclk); + for (i = 0; i < mmc->len_oscclk; i++) { + g_autofree char *propname = g_strdup_printf("oscclk[%u]", i); + qdev_prop_set_uint32(sccdev, propname, mmc->oscclk[i]); + } sysbus_realize(SYS_BUS_DEVICE(scc), &error_fatal); return sysbus_mmio_get_region(SYS_BUS_DEVICE(sccdev), 0); } static MemoryRegion *make_fpgaio(MPS2TZMachineState *mms, void *opaque, - const char *name, hwaddr size) + const char *name, hwaddr size, + const int *irqs) { MPS2FPGAIO *fpgaio = opaque; + MPS2TZMachineClass *mmc = MPS2TZ_MACHINE_GET_CLASS(mms); object_initialize_child(OBJECT(mms), "fpgaio", fpgaio, TYPE_MPS2_FPGAIO); + qdev_prop_set_uint32(DEVICE(fpgaio), "num-leds", mmc->fpgaio_num_leds); + qdev_prop_set_bit(DEVICE(fpgaio), "has-switches", mmc->fpgaio_has_switches); sysbus_realize(SYS_BUS_DEVICE(fpgaio), &error_fatal); return sysbus_mmio_get_region(SYS_BUS_DEVICE(fpgaio), 0); } static MemoryRegion *make_eth_dev(MPS2TZMachineState *mms, void *opaque, - const char *name, hwaddr size) + const char *name, hwaddr size, + const int *irqs) { SysBusDevice *s; NICInfo *nd = &nd_table[0]; @@ -248,50 +441,84 @@ static MemoryRegion *make_eth_dev(MPS2TZMachineState *mms, void *opaque, s = SYS_BUS_DEVICE(mms->lan9118); sysbus_realize_and_unref(s, &error_fatal); - sysbus_connect_irq(s, 0, get_sse_irq_in(mms, 16)); + sysbus_connect_irq(s, 0, get_sse_irq_in(mms, irqs[0])); return sysbus_mmio_get_region(s, 0); } +static MemoryRegion *make_eth_usb(MPS2TZMachineState *mms, void *opaque, + const char *name, hwaddr size, + const int *irqs) +{ + /* + * The AN524 makes the ethernet and USB share a PPC port. + * irqs[] is the ethernet IRQ. + */ + SysBusDevice *s; + NICInfo *nd = &nd_table[0]; + + memory_region_init(&mms->eth_usb_container, OBJECT(mms), + "mps2-tz-eth-usb-container", 0x200000); + + /* + * In hardware this is a LAN9220; the LAN9118 is software compatible + * except that it doesn't support the checksum-offload feature. + */ + qemu_check_nic_model(nd, "lan9118"); + mms->lan9118 = qdev_new(TYPE_LAN9118); + qdev_set_nic_properties(mms->lan9118, nd); + + s = SYS_BUS_DEVICE(mms->lan9118); + sysbus_realize_and_unref(s, &error_fatal); + sysbus_connect_irq(s, 0, get_sse_irq_in(mms, irqs[0])); + + memory_region_add_subregion(&mms->eth_usb_container, + 0, sysbus_mmio_get_region(s, 0)); + + /* The USB OTG controller is an ISP1763; we don't have a model of it. */ + object_initialize_child(OBJECT(mms), "usb-otg", + &mms->usb, TYPE_UNIMPLEMENTED_DEVICE); + qdev_prop_set_string(DEVICE(&mms->usb), "name", "usb-otg"); + qdev_prop_set_uint64(DEVICE(&mms->usb), "size", 0x100000); + s = SYS_BUS_DEVICE(&mms->usb); + sysbus_realize(s, &error_fatal); + + memory_region_add_subregion(&mms->eth_usb_container, + 0x100000, sysbus_mmio_get_region(s, 0)); + + return &mms->eth_usb_container; +} + static MemoryRegion *make_mpc(MPS2TZMachineState *mms, void *opaque, - const char *name, hwaddr size) + const char *name, hwaddr size, + const int *irqs) { TZMPC *mpc = opaque; - int i = mpc - &mms->ssram_mpc[0]; - MemoryRegion *ssram = &mms->ssram[i]; + int i = mpc - &mms->mpc[0]; MemoryRegion *upstream; - char *mpcname = g_strdup_printf("%s-mpc", name); - static uint32_t ramsize[] = { 0x00400000, 0x00200000, 0x00200000 }; - static uint32_t rambase[] = { 0x00000000, 0x28000000, 0x28200000 }; + const RAMInfo *raminfo = find_raminfo_for_mpc(mms, i); + MemoryRegion *ram = mr_for_raminfo(mms, raminfo); - memory_region_init_ram(ssram, NULL, name, ramsize[i], &error_fatal); - - object_initialize_child(OBJECT(mms), mpcname, mpc, TYPE_TZ_MPC); - object_property_set_link(OBJECT(mpc), "downstream", OBJECT(ssram), + object_initialize_child(OBJECT(mms), name, mpc, TYPE_TZ_MPC); + object_property_set_link(OBJECT(mpc), "downstream", OBJECT(ram), &error_fatal); sysbus_realize(SYS_BUS_DEVICE(mpc), &error_fatal); /* Map the upstream end of the MPC into system memory */ upstream = sysbus_mmio_get_region(SYS_BUS_DEVICE(mpc), 1); - memory_region_add_subregion(get_system_memory(), rambase[i], upstream); + memory_region_add_subregion(get_system_memory(), raminfo->base, upstream); /* and connect its interrupt to the IoTKit */ qdev_connect_gpio_out_named(DEVICE(mpc), "irq", 0, qdev_get_gpio_in_named(DEVICE(&mms->iotkit), "mpcexp_status", i)); - /* The first SSRAM is a special case as it has an alias; accesses to - * the alias region at 0x00400000 must also go to the MPC upstream. - */ - if (i == 0) { - make_ram_alias(&mms->ssram1_m, "mps.ssram1_m", upstream, 0x00400000); - } - - g_free(mpcname); /* Return the register interface MR for our caller to map behind the PPC */ return sysbus_mmio_get_region(SYS_BUS_DEVICE(mpc), 0); } static MemoryRegion *make_dma(MPS2TZMachineState *mms, void *opaque, - const char *name, hwaddr size) + const char *name, hwaddr size, + const int *irqs) { + /* The irq[] array is DMACINTR, DMACINTERR, DMACINTTC, in that order */ PL080State *dma = opaque; int i = dma - &mms->dma[0]; SysBusDevice *s; @@ -336,16 +563,17 @@ static MemoryRegion *make_dma(MPS2TZMachineState *mms, void *opaque, s = SYS_BUS_DEVICE(dma); /* Wire up DMACINTR, DMACINTERR, DMACINTTC */ - sysbus_connect_irq(s, 0, get_sse_irq_in(mms, 58 + i * 3)); - sysbus_connect_irq(s, 1, get_sse_irq_in(mms, 56 + i * 3)); - sysbus_connect_irq(s, 2, get_sse_irq_in(mms, 57 + i * 3)); + sysbus_connect_irq(s, 0, get_sse_irq_in(mms, irqs[0])); + sysbus_connect_irq(s, 1, get_sse_irq_in(mms, irqs[1])); + sysbus_connect_irq(s, 2, get_sse_irq_in(mms, irqs[2])); g_free(mscname); return sysbus_mmio_get_region(s, 0); } static MemoryRegion *make_spi(MPS2TZMachineState *mms, void *opaque, - const char *name, hwaddr size) + const char *name, hwaddr size, + const int *irqs) { /* * The AN505 has five PL022 SPI controllers. @@ -356,18 +584,18 @@ static MemoryRegion *make_spi(MPS2TZMachineState *mms, void *opaque, * lines are set via the "MISC" register in the MPS2 FPGAIO device. */ PL022State *spi = opaque; - int i = spi - &mms->spi[0]; SysBusDevice *s; object_initialize_child(OBJECT(mms), name, spi, TYPE_PL022); sysbus_realize(SYS_BUS_DEVICE(spi), &error_fatal); s = SYS_BUS_DEVICE(spi); - sysbus_connect_irq(s, 0, get_sse_irq_in(mms, 51 + i)); + sysbus_connect_irq(s, 0, get_sse_irq_in(mms, irqs[0])); return sysbus_mmio_get_region(s, 0); } static MemoryRegion *make_i2c(MPS2TZMachineState *mms, void *opaque, - const char *name, hwaddr size) + const char *name, hwaddr size, + const int *irqs) { ArmSbconI2CState *i2c = opaque; SysBusDevice *s; @@ -378,6 +606,59 @@ static MemoryRegion *make_i2c(MPS2TZMachineState *mms, void *opaque, return sysbus_mmio_get_region(s, 0); } +static MemoryRegion *make_rtc(MPS2TZMachineState *mms, void *opaque, + const char *name, hwaddr size, + const int *irqs) +{ + PL031State *pl031 = opaque; + SysBusDevice *s; + + object_initialize_child(OBJECT(mms), name, pl031, TYPE_PL031); + s = SYS_BUS_DEVICE(pl031); + sysbus_realize(s, &error_fatal); + /* + * The board docs don't give an IRQ number for the PL031, so + * presumably it is not connected. + */ + return sysbus_mmio_get_region(s, 0); +} + +static void create_non_mpc_ram(MPS2TZMachineState *mms) +{ + /* + * Handle the RAMs which are either not behind MPCs or which are + * aliases to another MPC. + */ + const RAMInfo *p; + MPS2TZMachineClass *mmc = MPS2TZ_MACHINE_GET_CLASS(mms); + + for (p = mmc->raminfo; p->name; p++) { + if (p->flags & IS_ALIAS) { + SysBusDevice *mpc_sbd = SYS_BUS_DEVICE(&mms->mpc[p->mpc]); + MemoryRegion *upstream = sysbus_mmio_get_region(mpc_sbd, 1); + make_ram_alias(&mms->ram[p->mrindex], p->name, upstream, p->base); + } else if (p->mpc == -1) { + /* RAM not behind an MPC */ + MemoryRegion *mr = mr_for_raminfo(mms, p); + memory_region_add_subregion(get_system_memory(), p->base, mr); + } + } +} + +static uint32_t boot_ram_size(MPS2TZMachineState *mms) +{ + /* Return the size of the RAM block at guest address zero */ + const RAMInfo *p; + MPS2TZMachineClass *mmc = MPS2TZ_MACHINE_GET_CLASS(mms); + + for (p = mmc->raminfo; p->name; p++) { + if (p->base == 0) { + return p->size; + } + } + g_assert_not_reached(); +} + static void mps2tz_common_init(MachineState *machine) { MPS2TZMachineState *mms = MPS2TZ_MACHINE(machine); @@ -386,6 +667,8 @@ static void mps2tz_common_init(MachineState *machine) MemoryRegion *system_memory = get_system_memory(); DeviceState *iotkitdev; DeviceState *dev_splitter; + const PPCInfo *ppcs; + int num_ppcs; int i; if (strcmp(machine->cpu_type, mc->default_cpu_type) != 0) { @@ -403,7 +686,7 @@ static void mps2tz_common_init(MachineState *machine) /* These clocks don't need migration because they are fixed-frequency */ mms->sysclk = clock_new(OBJECT(machine), "SYSCLK"); - clock_set_hz(mms->sysclk, SYSCLK_FRQ); + clock_set_hz(mms->sysclk, mmc->sysclk_frq); mms->s32kclk = clock_new(OBJECT(machine), "S32KCLK"); clock_set_hz(mms->s32kclk, S32KCLK_FRQ); @@ -412,17 +695,20 @@ static void mps2tz_common_init(MachineState *machine) iotkitdev = DEVICE(&mms->iotkit); object_property_set_link(OBJECT(&mms->iotkit), "memory", OBJECT(system_memory), &error_abort); - qdev_prop_set_uint32(iotkitdev, "EXP_NUMIRQ", MPS2TZ_NUMIRQ); + qdev_prop_set_uint32(iotkitdev, "EXP_NUMIRQ", mmc->numirq); qdev_connect_clock_in(iotkitdev, "MAINCLK", mms->sysclk); qdev_connect_clock_in(iotkitdev, "S32KCLK", mms->s32kclk); sysbus_realize(SYS_BUS_DEVICE(&mms->iotkit), &error_fatal); /* - * The AN521 needs us to create splitters to feed the IRQ inputs - * for each CPU in the SSE-200 from each device in the board. + * If this board has more than one CPU, then we need to create splitters + * to feed the IRQ inputs for each CPU in the SSE from each device in the + * board. If there is only one CPU, we can just wire the device IRQ + * directly to the SSE's IRQ input. */ - if (mmc->fpga_type == FPGA_AN521) { - for (i = 0; i < MPS2TZ_NUMIRQ; i++) { + assert(mmc->numirq <= MPS2TZ_NUMIRQ_MAX); + if (mc->max_cpus > 1) { + for (i = 0; i < mmc->numirq; i++) { char *name = g_strdup_printf("mps2-irq-splitter%d", i); SplitIRQ *splitter = &mms->cpu_irq_splitter[i]; @@ -457,36 +743,34 @@ static void mps2tz_common_init(MachineState *machine) qdev_connect_gpio_out_named(iotkitdev, "sec_resp_cfg", 0, qdev_get_gpio_in(dev_splitter, 0)); - /* The IoTKit sets up much of the memory layout, including + /* + * The IoTKit sets up much of the memory layout, including * the aliases between secure and non-secure regions in the - * address space. The FPGA itself contains: - * - * 0x00000000..0x003fffff SSRAM1 - * 0x00400000..0x007fffff alias of SSRAM1 - * 0x28000000..0x283fffff 4MB SSRAM2 + SSRAM3 - * 0x40100000..0x4fffffff AHB Master Expansion 1 interface devices - * 0x80000000..0x80ffffff 16MB PSRAM - */ - - /* The FPGA images have an odd combination of different RAMs, + * address space, and also most of the devices in the system. + * The FPGA itself contains various RAMs and some additional devices. + * The FPGA images have an odd combination of different RAMs, * because in hardware they are different implementations and * connected to different buses, giving varying performance/size * tradeoffs. For QEMU they're all just RAM, though. We arbitrarily - * call the 16MB our "system memory", as it's the largest lump. + * call the largest lump our "system memory". */ - memory_region_add_subregion(system_memory, 0x80000000, machine->ram); - /* The overflow IRQs for all UARTs are ORed together. + /* + * The overflow IRQs for all UARTs are ORed together. * Tx, Rx and "combined" IRQs are sent to the NVIC separately. - * Create the OR gate for this. + * Create the OR gate for this: it has one input for the TX overflow + * and one for the RX overflow for each UART we might have. + * (If the board has fewer than the maximum possible number of UARTs + * those inputs are never wired up and are treated as always-zero.) */ object_initialize_child(OBJECT(mms), "uart-irq-orgate", &mms->uart_irq_orgate, TYPE_OR_IRQ); - object_property_set_int(OBJECT(&mms->uart_irq_orgate), "num-lines", 10, + object_property_set_int(OBJECT(&mms->uart_irq_orgate), "num-lines", + 2 * ARRAY_SIZE(mms->uart), &error_fatal); qdev_realize(DEVICE(&mms->uart_irq_orgate), NULL, &error_fatal); qdev_connect_gpio_out(DEVICE(&mms->uart_irq_orgate), 0, - get_sse_irq_in(mms, 15)); + get_sse_irq_in(mms, 47)); /* Most of the devices in the FPGA are behind Peripheral Protection * Controllers. The required order for initializing things is: @@ -499,26 +783,26 @@ static void mps2tz_common_init(MachineState *machine) * + wire up the PPC's control lines to the IoTKit object */ - const PPCInfo ppcs[] = { { + const PPCInfo an505_ppcs[] = { { .name = "apb_ppcexp0", .ports = { - { "ssram-0", make_mpc, &mms->ssram_mpc[0], 0x58007000, 0x1000 }, - { "ssram-1", make_mpc, &mms->ssram_mpc[1], 0x58008000, 0x1000 }, - { "ssram-2", make_mpc, &mms->ssram_mpc[2], 0x58009000, 0x1000 }, + { "ssram-0-mpc", make_mpc, &mms->mpc[0], 0x58007000, 0x1000 }, + { "ssram-1-mpc", make_mpc, &mms->mpc[1], 0x58008000, 0x1000 }, + { "ssram-2-mpc", make_mpc, &mms->mpc[2], 0x58009000, 0x1000 }, }, }, { .name = "apb_ppcexp1", .ports = { - { "spi0", make_spi, &mms->spi[0], 0x40205000, 0x1000 }, - { "spi1", make_spi, &mms->spi[1], 0x40206000, 0x1000 }, - { "spi2", make_spi, &mms->spi[2], 0x40209000, 0x1000 }, - { "spi3", make_spi, &mms->spi[3], 0x4020a000, 0x1000 }, - { "spi4", make_spi, &mms->spi[4], 0x4020b000, 0x1000 }, - { "uart0", make_uart, &mms->uart[0], 0x40200000, 0x1000 }, - { "uart1", make_uart, &mms->uart[1], 0x40201000, 0x1000 }, - { "uart2", make_uart, &mms->uart[2], 0x40202000, 0x1000 }, - { "uart3", make_uart, &mms->uart[3], 0x40203000, 0x1000 }, - { "uart4", make_uart, &mms->uart[4], 0x40204000, 0x1000 }, + { "spi0", make_spi, &mms->spi[0], 0x40205000, 0x1000, { 51 } }, + { "spi1", make_spi, &mms->spi[1], 0x40206000, 0x1000, { 52 } }, + { "spi2", make_spi, &mms->spi[2], 0x40209000, 0x1000, { 53 } }, + { "spi3", make_spi, &mms->spi[3], 0x4020a000, 0x1000, { 54 } }, + { "spi4", make_spi, &mms->spi[4], 0x4020b000, 0x1000, { 55 } }, + { "uart0", make_uart, &mms->uart[0], 0x40200000, 0x1000, { 32, 33, 42 } }, + { "uart1", make_uart, &mms->uart[1], 0x40201000, 0x1000, { 34, 35, 43 } }, + { "uart2", make_uart, &mms->uart[2], 0x40202000, 0x1000, { 36, 37, 44 } }, + { "uart3", make_uart, &mms->uart[3], 0x40203000, 0x1000, { 38, 39, 45 } }, + { "uart4", make_uart, &mms->uart[4], 0x40204000, 0x1000, { 40, 41, 46 } }, { "i2c0", make_i2c, &mms->i2c[0], 0x40207000, 0x1000 }, { "i2c1", make_i2c, &mms->i2c[1], 0x40208000, 0x1000 }, { "i2c2", make_i2c, &mms->i2c[2], 0x4020c000, 0x1000 }, @@ -540,20 +824,84 @@ static void mps2tz_common_init(MachineState *machine) { "gpio1", make_unimp_dev, &mms->gpio[1], 0x40101000, 0x1000 }, { "gpio2", make_unimp_dev, &mms->gpio[2], 0x40102000, 0x1000 }, { "gpio3", make_unimp_dev, &mms->gpio[3], 0x40103000, 0x1000 }, - { "eth", make_eth_dev, NULL, 0x42000000, 0x100000 }, + { "eth", make_eth_dev, NULL, 0x42000000, 0x100000, { 48 } }, }, }, { .name = "ahb_ppcexp1", .ports = { - { "dma0", make_dma, &mms->dma[0], 0x40110000, 0x1000 }, - { "dma1", make_dma, &mms->dma[1], 0x40111000, 0x1000 }, - { "dma2", make_dma, &mms->dma[2], 0x40112000, 0x1000 }, - { "dma3", make_dma, &mms->dma[3], 0x40113000, 0x1000 }, + { "dma0", make_dma, &mms->dma[0], 0x40110000, 0x1000, { 58, 56, 57 } }, + { "dma1", make_dma, &mms->dma[1], 0x40111000, 0x1000, { 61, 59, 60 } }, + { "dma2", make_dma, &mms->dma[2], 0x40112000, 0x1000, { 64, 62, 63 } }, + { "dma3", make_dma, &mms->dma[3], 0x40113000, 0x1000, { 67, 65, 66 } }, }, }, }; - for (i = 0; i < ARRAY_SIZE(ppcs); i++) { + const PPCInfo an524_ppcs[] = { { + .name = "apb_ppcexp0", + .ports = { + { "bram-mpc", make_mpc, &mms->mpc[0], 0x58007000, 0x1000 }, + { "qspi-mpc", make_mpc, &mms->mpc[1], 0x58008000, 0x1000 }, + { "ddr-mpc", make_mpc, &mms->mpc[2], 0x58009000, 0x1000 }, + }, + }, { + .name = "apb_ppcexp1", + .ports = { + { "i2c0", make_i2c, &mms->i2c[0], 0x41200000, 0x1000 }, + { "i2c1", make_i2c, &mms->i2c[1], 0x41201000, 0x1000 }, + { "spi0", make_spi, &mms->spi[0], 0x41202000, 0x1000, { 52 } }, + { "spi1", make_spi, &mms->spi[1], 0x41203000, 0x1000, { 53 } }, + { "spi2", make_spi, &mms->spi[2], 0x41204000, 0x1000, { 54 } }, + { "i2c2", make_i2c, &mms->i2c[2], 0x41205000, 0x1000 }, + { "i2c3", make_i2c, &mms->i2c[3], 0x41206000, 0x1000 }, + { /* port 7 reserved */ }, + { "i2c4", make_i2c, &mms->i2c[4], 0x41208000, 0x1000 }, + }, + }, { + .name = "apb_ppcexp2", + .ports = { + { "scc", make_scc, &mms->scc, 0x41300000, 0x1000 }, + { "i2s-audio", make_unimp_dev, &mms->i2s_audio, + 0x41301000, 0x1000 }, + { "fpgaio", make_fpgaio, &mms->fpgaio, 0x41302000, 0x1000 }, + { "uart0", make_uart, &mms->uart[0], 0x41303000, 0x1000, { 32, 33, 42 } }, + { "uart1", make_uart, &mms->uart[1], 0x41304000, 0x1000, { 34, 35, 43 } }, + { "uart2", make_uart, &mms->uart[2], 0x41305000, 0x1000, { 36, 37, 44 } }, + { "uart3", make_uart, &mms->uart[3], 0x41306000, 0x1000, { 38, 39, 45 } }, + { "uart4", make_uart, &mms->uart[4], 0x41307000, 0x1000, { 40, 41, 46 } }, + { "uart5", make_uart, &mms->uart[5], 0x41308000, 0x1000, { 124, 125, 126 } }, + + { /* port 9 reserved */ }, + { "clcd", make_unimp_dev, &mms->cldc, 0x4130a000, 0x1000 }, + { "rtc", make_rtc, &mms->rtc, 0x4130b000, 0x1000 }, + }, + }, { + .name = "ahb_ppcexp0", + .ports = { + { "gpio0", make_unimp_dev, &mms->gpio[0], 0x41100000, 0x1000 }, + { "gpio1", make_unimp_dev, &mms->gpio[1], 0x41101000, 0x1000 }, + { "gpio2", make_unimp_dev, &mms->gpio[2], 0x41102000, 0x1000 }, + { "gpio3", make_unimp_dev, &mms->gpio[3], 0x41103000, 0x1000 }, + { "eth-usb", make_eth_usb, NULL, 0x41400000, 0x200000, { 48 } }, + }, + }, + }; + + switch (mmc->fpga_type) { + case FPGA_AN505: + case FPGA_AN521: + ppcs = an505_ppcs; + num_ppcs = ARRAY_SIZE(an505_ppcs); + break; + case FPGA_AN524: + ppcs = an524_ppcs; + num_ppcs = ARRAY_SIZE(an524_ppcs); + break; + default: + g_assert_not_reached(); + } + + for (i = 0; i < num_ppcs; i++) { const PPCInfo *ppcinfo = &ppcs[i]; TZPPC *ppc = &mms->ppc[i]; DeviceState *ppcdev; @@ -573,7 +921,8 @@ static void mps2tz_common_init(MachineState *machine) continue; } - mr = pinfo->devfn(mms, pinfo->opaque, pinfo->name, pinfo->size); + mr = pinfo->devfn(mms, pinfo->opaque, pinfo->name, pinfo->size, + pinfo->irqs); portname = g_strdup_printf("port[%d]", port); object_property_set_link(OBJECT(ppc), portname, OBJECT(mr), &error_fatal); @@ -626,7 +975,10 @@ static void mps2tz_common_init(MachineState *machine) create_unimplemented_device("FPGA NS PC", 0x48007000, 0x1000); - armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename, 0x400000); + create_non_mpc_ram(mms); + + armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename, + boot_ram_size(mms)); } static void mps2_tz_idau_check(IDAUInterface *ii, uint32_t address, @@ -654,8 +1006,26 @@ static void mps2tz_class_init(ObjectClass *oc, void *data) mc->init = mps2tz_common_init; iic->check = mps2_tz_idau_check; - mc->default_ram_size = 16 * MiB; - mc->default_ram_id = "mps.ram"; +} + +static void mps2tz_set_default_ram_info(MPS2TZMachineClass *mmc) +{ + /* + * Set mc->default_ram_size and default_ram_id from the + * information in mmc->raminfo. + */ + MachineClass *mc = MACHINE_CLASS(mmc); + const RAMInfo *p; + + for (p = mmc->raminfo; p->name; p++) { + if (p->mrindex < 0) { + /* Found the entry for "system memory" */ + mc->default_ram_size = p->size; + mc->default_ram_id = p->name; + return; + } + } + g_assert_not_reached(); } static void mps2tz_an505_class_init(ObjectClass *oc, void *data) @@ -670,7 +1040,15 @@ static void mps2tz_an505_class_init(ObjectClass *oc, void *data) mmc->fpga_type = FPGA_AN505; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m33"); mmc->scc_id = 0x41045050; + mmc->sysclk_frq = 20 * 1000 * 1000; /* 20MHz */ + mmc->oscclk = an505_oscclk; + mmc->len_oscclk = ARRAY_SIZE(an505_oscclk); + mmc->fpgaio_num_leds = 2; + mmc->fpgaio_has_switches = false; + mmc->numirq = 92; + mmc->raminfo = an505_raminfo; mmc->armsse_type = TYPE_IOTKIT; + mps2tz_set_default_ram_info(mmc); } static void mps2tz_an521_class_init(ObjectClass *oc, void *data) @@ -685,7 +1063,38 @@ static void mps2tz_an521_class_init(ObjectClass *oc, void *data) mmc->fpga_type = FPGA_AN521; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m33"); mmc->scc_id = 0x41045210; + mmc->sysclk_frq = 20 * 1000 * 1000; /* 20MHz */ + mmc->oscclk = an505_oscclk; /* AN521 is the same as AN505 here */ + mmc->len_oscclk = ARRAY_SIZE(an505_oscclk); + mmc->fpgaio_num_leds = 2; + mmc->fpgaio_has_switches = false; + mmc->numirq = 92; + mmc->raminfo = an505_raminfo; /* AN521 is the same as AN505 here */ mmc->armsse_type = TYPE_SSE200; + mps2tz_set_default_ram_info(mmc); +} + +static void mps3tz_an524_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + MPS2TZMachineClass *mmc = MPS2TZ_MACHINE_CLASS(oc); + + mc->desc = "ARM MPS3 with AN524 FPGA image for dual Cortex-M33"; + mc->default_cpus = 2; + mc->min_cpus = mc->default_cpus; + mc->max_cpus = mc->default_cpus; + mmc->fpga_type = FPGA_AN524; + mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m33"); + mmc->scc_id = 0x41045240; + mmc->sysclk_frq = 32 * 1000 * 1000; /* 32MHz */ + mmc->oscclk = an524_oscclk; + mmc->len_oscclk = ARRAY_SIZE(an524_oscclk); + mmc->fpgaio_num_leds = 10; + mmc->fpgaio_has_switches = true; + mmc->numirq = 95; + mmc->raminfo = an524_raminfo; + mmc->armsse_type = TYPE_SSE200; + mps2tz_set_default_ram_info(mmc); } static const TypeInfo mps2tz_info = { @@ -713,11 +1122,18 @@ static const TypeInfo mps2tz_an521_info = { .class_init = mps2tz_an521_class_init, }; +static const TypeInfo mps3tz_an524_info = { + .name = TYPE_MPS3TZ_AN524_MACHINE, + .parent = TYPE_MPS2TZ_MACHINE, + .class_init = mps3tz_an524_class_init, +}; + static void mps2tz_machine_init(void) { type_register_static(&mps2tz_info); type_register_static(&mps2tz_an505_info); type_register_static(&mps2tz_an521_info); + type_register_static(&mps3tz_an524_info); } type_init(mps2tz_machine_init); diff --git a/hw/arm/mps2.c b/hw/arm/mps2.c index 39add416db..81413b7133 100644 --- a/hw/arm/mps2.c +++ b/hw/arm/mps2.c @@ -373,6 +373,11 @@ static void mps2_common_init(MachineState *machine) qdev_prop_set_uint32(sccdev, "scc-cfg4", 0x2); qdev_prop_set_uint32(sccdev, "scc-aid", 0x00200008); qdev_prop_set_uint32(sccdev, "scc-id", mmc->scc_id); + /* All these FPGA images have the same OSCCLK configuration */ + qdev_prop_set_uint32(sccdev, "len-oscclk", 3); + qdev_prop_set_uint32(sccdev, "oscclk[0]", 50000000); + qdev_prop_set_uint32(sccdev, "oscclk[1]", 24576000); + qdev_prop_set_uint32(sccdev, "oscclk[2]", 25000000); sysbus_realize(SYS_BUS_DEVICE(&mms->scc), &error_fatal); sysbus_mmio_map(SYS_BUS_DEVICE(sccdev), 0, 0x4002f000); object_initialize_child(OBJECT(mms), "fpgaio", diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c index 6aec84aeed..9cebece2de 100644 --- a/hw/arm/musicpal.c +++ b/hw/arm/musicpal.c @@ -512,53 +512,37 @@ static uint8_t scale_lcd_color(musicpal_lcd_state *s, uint8_t col) } } -#define SET_LCD_PIXEL(depth, type) \ -static inline void glue(set_lcd_pixel, depth) \ - (musicpal_lcd_state *s, int x, int y, type col) \ -{ \ - int dx, dy; \ - DisplaySurface *surface = qemu_console_surface(s->con); \ - type *pixel = &((type *) surface_data(surface))[(y * 128 * 3 + x) * 3]; \ -\ - for (dy = 0; dy < 3; dy++, pixel += 127 * 3) \ - for (dx = 0; dx < 3; dx++, pixel++) \ - *pixel = col; \ +static inline void set_lcd_pixel32(musicpal_lcd_state *s, + int x, int y, uint32_t col) +{ + int dx, dy; + DisplaySurface *surface = qemu_console_surface(s->con); + uint32_t *pixel = + &((uint32_t *) surface_data(surface))[(y * 128 * 3 + x) * 3]; + + for (dy = 0; dy < 3; dy++, pixel += 127 * 3) { + for (dx = 0; dx < 3; dx++, pixel++) { + *pixel = col; + } + } } -SET_LCD_PIXEL(8, uint8_t) -SET_LCD_PIXEL(16, uint16_t) -SET_LCD_PIXEL(32, uint32_t) static void lcd_refresh(void *opaque) { musicpal_lcd_state *s = opaque; - DisplaySurface *surface = qemu_console_surface(s->con); int x, y, col; - switch (surface_bits_per_pixel(surface)) { - case 0: - return; -#define LCD_REFRESH(depth, func) \ - case depth: \ - col = func(scale_lcd_color(s, (MP_LCD_TEXTCOLOR >> 16) & 0xff), \ - scale_lcd_color(s, (MP_LCD_TEXTCOLOR >> 8) & 0xff), \ - scale_lcd_color(s, MP_LCD_TEXTCOLOR & 0xff)); \ - for (x = 0; x < 128; x++) { \ - for (y = 0; y < 64; y++) { \ - if (s->video_ram[x + (y/8)*128] & (1 << (y % 8))) { \ - glue(set_lcd_pixel, depth)(s, x, y, col); \ - } else { \ - glue(set_lcd_pixel, depth)(s, x, y, 0); \ - } \ - } \ - } \ - break; - LCD_REFRESH(8, rgb_to_pixel8) - LCD_REFRESH(16, rgb_to_pixel16) - LCD_REFRESH(32, (is_surface_bgr(surface) ? - rgb_to_pixel32bgr : rgb_to_pixel32)) - default: - hw_error("unsupported colour depth %i\n", - surface_bits_per_pixel(surface)); + col = rgb_to_pixel32(scale_lcd_color(s, (MP_LCD_TEXTCOLOR >> 16) & 0xff), + scale_lcd_color(s, (MP_LCD_TEXTCOLOR >> 8) & 0xff), + scale_lcd_color(s, MP_LCD_TEXTCOLOR & 0xff)); + for (x = 0; x < 128; x++) { + for (y = 0; y < 64; y++) { + if (s->video_ram[x + (y / 8) * 128] & (1 << (y % 8))) { + set_lcd_pixel32(s, x, y, col); + } else { + set_lcd_pixel32(s, x, y, 0); + } + } } dpy_gfx_update(s->con, 0, 0, 128*3, 64*3); diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c index f8950f9470..9bd1e83f02 100644 --- a/hw/arm/npcm7xx.c +++ b/hw/arm/npcm7xx.c @@ -82,6 +82,8 @@ enum NPCM7xxInterrupt { NPCM7XX_UART1_IRQ, NPCM7XX_UART2_IRQ, NPCM7XX_UART3_IRQ, + NPCM7XX_EMC1RX_IRQ = 15, + NPCM7XX_EMC1TX_IRQ, NPCM7XX_TIMER0_IRQ = 32, /* Timer Module 0 */ NPCM7XX_TIMER1_IRQ, NPCM7XX_TIMER2_IRQ, @@ -120,6 +122,8 @@ enum NPCM7xxInterrupt { NPCM7XX_SMBUS15_IRQ, NPCM7XX_PWM0_IRQ = 93, /* PWM module 0 */ NPCM7XX_PWM1_IRQ, /* PWM module 1 */ + NPCM7XX_EMC2RX_IRQ = 114, + NPCM7XX_EMC2TX_IRQ, NPCM7XX_GPIO0_IRQ = 116, NPCM7XX_GPIO1_IRQ, NPCM7XX_GPIO2_IRQ, @@ -188,6 +192,12 @@ static const hwaddr npcm7xx_smbus_addr[] = { 0xf008f000, }; +/* Register base address for each EMC Module */ +static const hwaddr npcm7xx_emc_addr[] = { + 0xf0825000, + 0xf0826000, +}; + static const struct { hwaddr regs_addr; uint32_t unconnected_pins; @@ -406,6 +416,10 @@ static void npcm7xx_init(Object *obj) for (i = 0; i < ARRAY_SIZE(s->pwm); i++) { object_initialize_child(obj, "pwm[*]", &s->pwm[i], TYPE_NPCM7XX_PWM); } + + for (i = 0; i < ARRAY_SIZE(s->emc); i++) { + object_initialize_child(obj, "emc[*]", &s->emc[i], TYPE_NPCM7XX_EMC); + } } static void npcm7xx_realize(DeviceState *dev, Error **errp) @@ -590,6 +604,40 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp) } /* + * EMC Modules. Cannot fail. + * The mapping of the device to its netdev backend works as follows: + * emc[i] = nd_table[i] + * This works around the inability to specify the netdev property for the + * emc device: it's not pluggable and thus the -device option can't be + * used. + */ + QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_emc_addr) != ARRAY_SIZE(s->emc)); + QEMU_BUILD_BUG_ON(ARRAY_SIZE(s->emc) != 2); + for (i = 0; i < ARRAY_SIZE(s->emc); i++) { + s->emc[i].emc_num = i; + SysBusDevice *sbd = SYS_BUS_DEVICE(&s->emc[i]); + if (nd_table[i].used) { + qemu_check_nic_model(&nd_table[i], TYPE_NPCM7XX_EMC); + qdev_set_nic_properties(DEVICE(sbd), &nd_table[i]); + } + /* + * The device exists regardless of whether it's connected to a QEMU + * netdev backend. So always instantiate it even if there is no + * backend. + */ + sysbus_realize(sbd, &error_abort); + sysbus_mmio_map(sbd, 0, npcm7xx_emc_addr[i]); + int tx_irq = i == 0 ? NPCM7XX_EMC1TX_IRQ : NPCM7XX_EMC2TX_IRQ; + int rx_irq = i == 0 ? NPCM7XX_EMC1RX_IRQ : NPCM7XX_EMC2RX_IRQ; + /* + * N.B. The values for the second argument sysbus_connect_irq are + * chosen to match the registration order in npcm7xx_emc_realize. + */ + sysbus_connect_irq(sbd, 0, npcm7xx_irq(s, tx_irq)); + sysbus_connect_irq(sbd, 1, npcm7xx_irq(s, rx_irq)); + } + + /* * Flash Interface Unit (FIU). Can fail if incorrect number of chip selects * specified, but this is a programming error. */ @@ -649,8 +697,6 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp) create_unimplemented_device("npcm7xx.vcd", 0xf0810000, 64 * KiB); create_unimplemented_device("npcm7xx.ece", 0xf0820000, 8 * KiB); create_unimplemented_device("npcm7xx.vdma", 0xf0822000, 8 * KiB); - create_unimplemented_device("npcm7xx.emc1", 0xf0825000, 4 * KiB); - create_unimplemented_device("npcm7xx.emc2", 0xf0826000, 4 * KiB); create_unimplemented_device("npcm7xx.usbd[0]", 0xf0830000, 4 * KiB); create_unimplemented_device("npcm7xx.usbd[1]", 0xf0831000, 4 * KiB); create_unimplemented_device("npcm7xx.usbd[2]", 0xf0832000, 4 * KiB); diff --git a/hw/arm/sbsa-ref.c b/hw/arm/sbsa-ref.c index 9f70735153..88dfb2284c 100644 --- a/hw/arm/sbsa-ref.c +++ b/hw/arm/sbsa-ref.c @@ -145,9 +145,9 @@ static const int sbsa_ref_irqmap[] = { }; static const char * const valid_cpus[] = { - ARM_CPU_TYPE_NAME("cortex-a53"), ARM_CPU_TYPE_NAME("cortex-a57"), ARM_CPU_TYPE_NAME("cortex-a72"), + ARM_CPU_TYPE_NAME("max"), }; static bool cpu_type_valid(const char *cpu) diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c index 881847255b..46030c1ef8 100644 --- a/hw/arm/xlnx-zynqmp.c +++ b/hw/arm/xlnx-zynqmp.c @@ -443,11 +443,6 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) } } - if (s->has_rpu) { - info_report("The 'has_rpu' property is no longer required, to use the " - "RPUs just use -smp 6."); - } - xlnx_zynqmp_create_rpu(ms, s, boot_cpu, &err); if (err) { error_propagate(errp, err); @@ -646,7 +641,6 @@ static Property xlnx_zynqmp_props[] = { DEFINE_PROP_STRING("boot-cpu", XlnxZynqMPState, boot_cpu), DEFINE_PROP_BOOL("secure", XlnxZynqMPState, secure, false), DEFINE_PROP_BOOL("virtualization", XlnxZynqMPState, virt, false), - DEFINE_PROP_BOOL("has_rpu", XlnxZynqMPState, has_rpu, false), DEFINE_PROP_LINK("ddr-ram", XlnxZynqMPState, ddr_ram, TYPE_MEMORY_REGION, MemoryRegion *), DEFINE_PROP_LINK("canbus0", XlnxZynqMPState, canbus[0], TYPE_CAN_BUS, diff --git a/hw/display/omap_lcd_template.h b/hw/display/omap_lcd_template.h deleted file mode 100644 index 1025ff3825..0000000000 --- a/hw/display/omap_lcd_template.h +++ /dev/null @@ -1,169 +0,0 @@ -/* - * QEMU OMAP LCD Emulator templates - * - * Copyright (c) 2006 Andrzej Zaborowski <balrog@zabor.org> - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#if DEPTH == 32 -# define BPP 4 -# define PIXEL_TYPE uint32_t -#else -# error unsupport depth -#endif - -/* - * 2-bit colour - */ -static void glue(draw_line2_, DEPTH)(void *opaque, - uint8_t *d, const uint8_t *s, int width, int deststep) -{ - uint16_t *pal = opaque; - uint8_t v, r, g, b; - - do { - v = ldub_p((void *) s); - r = (pal[v & 3] >> 4) & 0xf0; - g = pal[v & 3] & 0xf0; - b = (pal[v & 3] << 4) & 0xf0; - ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, DEPTH)(r, g, b); - d += BPP; - v >>= 2; - r = (pal[v & 3] >> 4) & 0xf0; - g = pal[v & 3] & 0xf0; - b = (pal[v & 3] << 4) & 0xf0; - ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, DEPTH)(r, g, b); - d += BPP; - v >>= 2; - r = (pal[v & 3] >> 4) & 0xf0; - g = pal[v & 3] & 0xf0; - b = (pal[v & 3] << 4) & 0xf0; - ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, DEPTH)(r, g, b); - d += BPP; - v >>= 2; - r = (pal[v & 3] >> 4) & 0xf0; - g = pal[v & 3] & 0xf0; - b = (pal[v & 3] << 4) & 0xf0; - ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, DEPTH)(r, g, b); - d += BPP; - s ++; - width -= 4; - } while (width > 0); -} - -/* - * 4-bit colour - */ -static void glue(draw_line4_, DEPTH)(void *opaque, - uint8_t *d, const uint8_t *s, int width, int deststep) -{ - uint16_t *pal = opaque; - uint8_t v, r, g, b; - - do { - v = ldub_p((void *) s); - r = (pal[v & 0xf] >> 4) & 0xf0; - g = pal[v & 0xf] & 0xf0; - b = (pal[v & 0xf] << 4) & 0xf0; - ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, DEPTH)(r, g, b); - d += BPP; - v >>= 4; - r = (pal[v & 0xf] >> 4) & 0xf0; - g = pal[v & 0xf] & 0xf0; - b = (pal[v & 0xf] << 4) & 0xf0; - ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, DEPTH)(r, g, b); - d += BPP; - s ++; - width -= 2; - } while (width > 0); -} - -/* - * 8-bit colour - */ -static void glue(draw_line8_, DEPTH)(void *opaque, - uint8_t *d, const uint8_t *s, int width, int deststep) -{ - uint16_t *pal = opaque; - uint8_t v, r, g, b; - - do { - v = ldub_p((void *) s); - r = (pal[v] >> 4) & 0xf0; - g = pal[v] & 0xf0; - b = (pal[v] << 4) & 0xf0; - ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, DEPTH)(r, g, b); - s ++; - d += BPP; - } while (-- width != 0); -} - -/* - * 12-bit colour - */ -static void glue(draw_line12_, DEPTH)(void *opaque, - uint8_t *d, const uint8_t *s, int width, int deststep) -{ - uint16_t v; - uint8_t r, g, b; - - do { - v = lduw_le_p((void *) s); - r = (v >> 4) & 0xf0; - g = v & 0xf0; - b = (v << 4) & 0xf0; - ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, DEPTH)(r, g, b); - s += 2; - d += BPP; - } while (-- width != 0); -} - -/* - * 16-bit colour - */ -static void glue(draw_line16_, DEPTH)(void *opaque, - uint8_t *d, const uint8_t *s, int width, int deststep) -{ -#if defined(HOST_WORDS_BIGENDIAN) == defined(TARGET_WORDS_BIGENDIAN) - memcpy(d, s, width * 2); -#else - uint16_t v; - uint8_t r, g, b; - - do { - v = lduw_le_p((void *) s); - r = (v >> 8) & 0xf8; - g = (v >> 3) & 0xfc; - b = (v << 3) & 0xf8; - ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, DEPTH)(r, g, b); - s += 2; - d += BPP; - } while (-- width != 0); -#endif -} - -#undef DEPTH -#undef BPP -#undef PIXEL_TYPE diff --git a/hw/display/omap_lcdc.c b/hw/display/omap_lcdc.c index 58e659c94f..0ba42ef637 100644 --- a/hw/display/omap_lcdc.c +++ b/hw/display/omap_lcdc.c @@ -70,16 +70,137 @@ static void omap_lcd_interrupts(struct omap_lcd_panel_s *s) qemu_irq_lower(s->irq); } -#define draw_line_func drawfn +/* + * 2-bit colour + */ +static void draw_line2_32(void *opaque, uint8_t *d, const uint8_t *s, + int width, int deststep) +{ + uint16_t *pal = opaque; + uint8_t v, r, g, b; + + do { + v = ldub_p((void *) s); + r = (pal[v & 3] >> 4) & 0xf0; + g = pal[v & 3] & 0xf0; + b = (pal[v & 3] << 4) & 0xf0; + ((uint32_t *) d)[0] = rgb_to_pixel32(r, g, b); + d += 4; + v >>= 2; + r = (pal[v & 3] >> 4) & 0xf0; + g = pal[v & 3] & 0xf0; + b = (pal[v & 3] << 4) & 0xf0; + ((uint32_t *) d)[0] = rgb_to_pixel32(r, g, b); + d += 4; + v >>= 2; + r = (pal[v & 3] >> 4) & 0xf0; + g = pal[v & 3] & 0xf0; + b = (pal[v & 3] << 4) & 0xf0; + ((uint32_t *) d)[0] = rgb_to_pixel32(r, g, b); + d += 4; + v >>= 2; + r = (pal[v & 3] >> 4) & 0xf0; + g = pal[v & 3] & 0xf0; + b = (pal[v & 3] << 4) & 0xf0; + ((uint32_t *) d)[0] = rgb_to_pixel32(r, g, b); + d += 4; + s++; + width -= 4; + } while (width > 0); +} + +/* + * 4-bit colour + */ +static void draw_line4_32(void *opaque, uint8_t *d, const uint8_t *s, + int width, int deststep) +{ + uint16_t *pal = opaque; + uint8_t v, r, g, b; + + do { + v = ldub_p((void *) s); + r = (pal[v & 0xf] >> 4) & 0xf0; + g = pal[v & 0xf] & 0xf0; + b = (pal[v & 0xf] << 4) & 0xf0; + ((uint32_t *) d)[0] = rgb_to_pixel32(r, g, b); + d += 4; + v >>= 4; + r = (pal[v & 0xf] >> 4) & 0xf0; + g = pal[v & 0xf] & 0xf0; + b = (pal[v & 0xf] << 4) & 0xf0; + ((uint32_t *) d)[0] = rgb_to_pixel32(r, g, b); + d += 4; + s++; + width -= 2; + } while (width > 0); +} -#define DEPTH 32 -#include "omap_lcd_template.h" +/* + * 8-bit colour + */ +static void draw_line8_32(void *opaque, uint8_t *d, const uint8_t *s, + int width, int deststep) +{ + uint16_t *pal = opaque; + uint8_t v, r, g, b; + + do { + v = ldub_p((void *) s); + r = (pal[v] >> 4) & 0xf0; + g = pal[v] & 0xf0; + b = (pal[v] << 4) & 0xf0; + ((uint32_t *) d)[0] = rgb_to_pixel32(r, g, b); + s++; + d += 4; + } while (-- width != 0); +} + +/* + * 12-bit colour + */ +static void draw_line12_32(void *opaque, uint8_t *d, const uint8_t *s, + int width, int deststep) +{ + uint16_t v; + uint8_t r, g, b; + + do { + v = lduw_le_p((void *) s); + r = (v >> 4) & 0xf0; + g = v & 0xf0; + b = (v << 4) & 0xf0; + ((uint32_t *) d)[0] = rgb_to_pixel32(r, g, b); + s += 2; + d += 4; + } while (-- width != 0); +} + +/* + * 16-bit colour + */ +static void draw_line16_32(void *opaque, uint8_t *d, const uint8_t *s, + int width, int deststep) +{ + uint16_t v; + uint8_t r, g, b; + + do { + v = lduw_le_p((void *) s); + r = (v >> 8) & 0xf8; + g = (v >> 3) & 0xfc; + b = (v << 3) & 0xf8; + ((uint32_t *) d)[0] = rgb_to_pixel32(r, g, b); + s += 2; + d += 4; + } while (-- width != 0); +} static void omap_update_display(void *opaque) { struct omap_lcd_panel_s *omap_lcd = (struct omap_lcd_panel_s *) opaque; DisplaySurface *surface; - draw_line_func draw_line; + drawfn draw_line; int size, height, first, last; int width, linesize, step, bpp, frame_offset; hwaddr frame_base; diff --git a/hw/display/tc6393xb.c b/hw/display/tc6393xb.c index 49a676d1b0..1f28223c7b 100644 --- a/hw/display/tc6393xb.c +++ b/hw/display/tc6393xb.c @@ -410,43 +410,27 @@ static void tc6393xb_nand_writeb(TC6393xbState *s, hwaddr addr, uint32_t value) (uint32_t) addr, value & 0xff); } -#define BITS 8 -#include "tc6393xb_template.h" -#define BITS 15 -#include "tc6393xb_template.h" -#define BITS 16 -#include "tc6393xb_template.h" -#define BITS 24 -#include "tc6393xb_template.h" -#define BITS 32 -#include "tc6393xb_template.h" - static void tc6393xb_draw_graphic(TC6393xbState *s, int full_update) { DisplaySurface *surface = qemu_console_surface(s->con); - - switch (surface_bits_per_pixel(surface)) { - case 8: - tc6393xb_draw_graphic8(s); - break; - case 15: - tc6393xb_draw_graphic15(s); - break; - case 16: - tc6393xb_draw_graphic16(s); - break; - case 24: - tc6393xb_draw_graphic24(s); - break; - case 32: - tc6393xb_draw_graphic32(s); - break; - default: - printf("tc6393xb: unknown depth %d\n", - surface_bits_per_pixel(surface)); - return; + int i; + uint16_t *data_buffer; + uint8_t *data_display; + + data_buffer = s->vram_ptr; + data_display = surface_data(surface); + for (i = 0; i < s->scr_height; i++) { + int j; + for (j = 0; j < s->scr_width; j++, data_display += 4, data_buffer++) { + uint16_t color = *data_buffer; + uint32_t dest_color = rgb_to_pixel32( + ((color & 0xf800) * 0x108) >> 11, + ((color & 0x7e0) * 0x41) >> 9, + ((color & 0x1f) * 0x21) >> 2 + ); + *(uint32_t *)data_display = dest_color; + } } - dpy_gfx_update_full(s->con); } diff --git a/hw/display/tc6393xb_template.h b/hw/display/tc6393xb_template.h deleted file mode 100644 index 78629c07f9..0000000000 --- a/hw/display/tc6393xb_template.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Toshiba TC6393XB I/O Controller. - * Found in Sharp Zaurus SL-6000 (tosa) or some - * Toshiba e-Series PDAs. - * - * FB support code. Based on G364 fb emulator - * - * Copyright (c) 2007 Hervé Poussineau - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -#if BITS == 8 -# define SET_PIXEL(addr, color) (*(uint8_t *)addr = color) -#elif BITS == 15 || BITS == 16 -# define SET_PIXEL(addr, color) (*(uint16_t *)addr = color) -#elif BITS == 24 -# define SET_PIXEL(addr, color) \ - do { \ - addr[0] = color; \ - addr[1] = (color) >> 8; \ - addr[2] = (color) >> 16; \ - } while (0) -#elif BITS == 32 -# define SET_PIXEL(addr, color) (*(uint32_t *)addr = color) -#else -# error unknown bit depth -#endif - - -static void glue(tc6393xb_draw_graphic, BITS)(TC6393xbState *s) -{ - DisplaySurface *surface = qemu_console_surface(s->con); - int i; - uint16_t *data_buffer; - uint8_t *data_display; - - data_buffer = s->vram_ptr; - data_display = surface_data(surface); - for(i = 0; i < s->scr_height; i++) { -#if (BITS == 16) - memcpy(data_display, data_buffer, s->scr_width * 2); - data_buffer += s->scr_width; - data_display += surface_stride(surface); -#else - int j; - for (j = 0; j < s->scr_width; j++, data_display += BITS / 8, data_buffer++) { - uint16_t color = *data_buffer; - uint32_t dest_color = glue(rgb_to_pixel, BITS)( - ((color & 0xf800) * 0x108) >> 11, - ((color & 0x7e0) * 0x41) >> 9, - ((color & 0x1f) * 0x21) >> 2 - ); - SET_PIXEL(data_display, dest_color); - } -#endif - } -} - -#undef BITS -#undef SET_PIXEL diff --git a/hw/display/tcx.c b/hw/display/tcx.c index 965f92ff6b..d3db304657 100644 --- a/hw/display/tcx.c +++ b/hw/display/tcx.c @@ -128,15 +128,10 @@ static int tcx_check_dirty(TCXState *s, DirtyBitmapSnapshot *snap, static void update_palette_entries(TCXState *s, int start, int end) { - DisplaySurface *surface = qemu_console_surface(s->con); int i; for (i = start; i < end; i++) { - if (is_surface_bgr(surface)) { - s->palette[i] = rgb_to_pixel32bgr(s->r[i], s->g[i], s->b[i]); - } else { - s->palette[i] = rgb_to_pixel32(s->r[i], s->g[i], s->b[i]); - } + s->palette[i] = rgb_to_pixel32(s->r[i], s->g[i], s->b[i]); } tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem)); } @@ -181,21 +176,18 @@ static void tcx_draw_cursor32(TCXState *s1, uint8_t *d, } /* - XXX Could be much more optimal: - * detect if line/page/whole screen is in 24 bit mode - * if destination is also BGR, use memcpy - */ + * XXX Could be much more optimal: + * detect if line/page/whole screen is in 24 bit mode + */ static inline void tcx24_draw_line32(TCXState *s1, uint8_t *d, const uint8_t *s, int width, const uint32_t *cplane, const uint32_t *s24) { - DisplaySurface *surface = qemu_console_surface(s1->con); - int x, bgr, r, g, b; + int x, r, g, b; uint8_t val, *p8; uint32_t *p = (uint32_t *)d; uint32_t dval; - bgr = is_surface_bgr(surface); for(x = 0; x < width; x++, s++, s24++) { if (be32_to_cpu(*cplane) & 0x03000000) { /* 24-bit direct, BGR order */ @@ -204,10 +196,7 @@ static inline void tcx24_draw_line32(TCXState *s1, uint8_t *d, b = *p8++; g = *p8++; r = *p8; - if (bgr) - dval = rgb_to_pixel32bgr(r, g, b); - else - dval = rgb_to_pixel32(r, g, b); + dval = rgb_to_pixel32(r, g, b); } else { /* 8-bit pseudocolor */ val = *s; @@ -230,9 +219,7 @@ static void tcx_update_display(void *opaque) int y, y_start, dd, ds; uint8_t *d, *s; - if (surface_bits_per_pixel(surface) != 32) { - return; - } + assert(surface_bits_per_pixel(surface) == 32); page = 0; y_start = -1; @@ -283,9 +270,7 @@ static void tcx24_update_display(void *opaque) uint8_t *d, *s; uint32_t *cptr, *s24; - if (surface_bits_per_pixel(surface) != 32) { - return; - } + assert(surface_bits_per_pixel(surface) == 32); page = 0; y_start = -1; diff --git a/hw/i2c/npcm7xx_smbus.c b/hw/i2c/npcm7xx_smbus.c index 6b2f9e1aaa..e7e0ba66fe 100644 --- a/hw/i2c/npcm7xx_smbus.c +++ b/hw/i2c/npcm7xx_smbus.c @@ -1040,7 +1040,6 @@ static void npcm7xx_smbus_init(Object *obj) sysbus_init_mmio(sbd, &s->iomem); s->bus = i2c_init_bus(DEVICE(s), "i2c-bus"); - s->status = NPCM7XX_SMBUS_STATUS_IDLE; } static const VMStateDescription vmstate_npcm7xx_smbus = { diff --git a/hw/misc/armsse-cpuid.c b/hw/misc/armsse-cpuid.c index d58138dc28..e785a09051 100644 --- a/hw/misc/armsse-cpuid.c +++ b/hw/misc/armsse-cpuid.c @@ -12,7 +12,7 @@ /* * This is a model of the "CPU_IDENTITY" register block which is part of the * Arm SSE-200 and documented in - * http://infocenter.arm.com/help/topic/com.arm.doc.101104_0100_00_en/corelink_sse200_subsystem_for_embedded_technical_reference_manual_101104_0100_00_en.pdf + * https://developer.arm.com/documentation/101104/latest/ * * It consists of one read-only CPUID register (set by QOM property), plus the * usual ID registers. diff --git a/hw/misc/armsse-mhu.c b/hw/misc/armsse-mhu.c index a45d97fada..0be7f0fc87 100644 --- a/hw/misc/armsse-mhu.c +++ b/hw/misc/armsse-mhu.c @@ -12,7 +12,7 @@ /* * This is a model of the Message Handling Unit (MHU) which is part of the * Arm SSE-200 and documented in - * http://infocenter.arm.com/help/topic/com.arm.doc.101104_0100_00_en/corelink_sse200_subsystem_for_embedded_technical_reference_manual_101104_0100_00_en.pdf + * https://developer.arm.com/documentation/101104/latest/ */ #include "qemu/osdep.h" diff --git a/hw/misc/iotkit-sysctl.c b/hw/misc/iotkit-sysctl.c index 964b48c74d..222511c4b0 100644 --- a/hw/misc/iotkit-sysctl.c +++ b/hw/misc/iotkit-sysctl.c @@ -12,7 +12,7 @@ /* * This is a model of the "system control element" which is part of the * Arm IoTKit and documented in - * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ecm0601256/index.html + * https://developer.arm.com/documentation/ecm0601256/latest * Specifically, it implements the "system control register" blocks. */ diff --git a/hw/misc/iotkit-sysinfo.c b/hw/misc/iotkit-sysinfo.c index b2dcfc4376..52e70053df 100644 --- a/hw/misc/iotkit-sysinfo.c +++ b/hw/misc/iotkit-sysinfo.c @@ -12,7 +12,7 @@ /* * This is a model of the "system information block" which is part of the * Arm IoTKit and documented in - * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ecm0601256/index.html + * https://developer.arm.com/documentation/ecm0601256/latest * It consists of 2 read-only version/config registers, plus the * usual ID registers. */ diff --git a/hw/misc/mps2-fpgaio.c b/hw/misc/mps2-fpgaio.c index 6af0e8f837..f3db88ddcc 100644 --- a/hw/misc/mps2-fpgaio.c +++ b/hw/misc/mps2-fpgaio.c @@ -12,7 +12,7 @@ /* This is a model of the "FPGA system control and I/O" block found * in the AN505 FPGA image for the MPS2 devboard. * It is documented in AN505: - * http://infocenter.arm.com/help/topic/com.arm.doc.dai0505b/index.html + * https://developer.arm.com/documentation/dai0505/latest/ */ #include "qemu/osdep.h" @@ -35,6 +35,7 @@ REG32(CLK100HZ, 0x14) REG32(COUNTER, 0x18) REG32(PRESCALE, 0x1c) REG32(PSCNTR, 0x20) +REG32(SWITCH, 0x28) REG32(MISC, 0x4c) static uint32_t counter_from_tickoff(int64_t now, int64_t tick_offset, int frq) @@ -156,7 +157,15 @@ static uint64_t mps2_fpgaio_read(void *opaque, hwaddr offset, unsigned size) resync_counter(s); r = s->pscntr; break; + case A_SWITCH: + if (!s->has_switches) { + goto bad_offset; + } + /* User-togglable board switches. We don't model that, so report 0. */ + r = 0; + break; default: + bad_offset: qemu_log_mask(LOG_GUEST_ERROR, "MPS2 FPGAIO read: bad offset %x\n", (int) offset); r = 0; @@ -177,9 +186,14 @@ static void mps2_fpgaio_write(void *opaque, hwaddr offset, uint64_t value, switch (offset) { case A_LED0: - s->led0 = value & 0x3; - led_set_state(s->led[0], value & 0x01); - led_set_state(s->led[1], value & 0x02); + if (s->num_leds != 0) { + uint32_t i; + + s->led0 = value & MAKE_64BIT_MASK(0, s->num_leds); + for (i = 0; i < s->num_leds; i++) { + led_set_state(s->led[i], value & (1 << i)); + } + } break; case A_PRESCALE: resync_counter(s); @@ -238,7 +252,7 @@ static void mps2_fpgaio_reset(DeviceState *dev) s->pscntr = 0; s->pscntr_sync_ticks = now; - for (size_t i = 0; i < ARRAY_SIZE(s->led); i++) { + for (size_t i = 0; i < s->num_leds; i++) { device_cold_reset(DEVICE(s->led[i])); } } @@ -256,11 +270,19 @@ static void mps2_fpgaio_init(Object *obj) static void mps2_fpgaio_realize(DeviceState *dev, Error **errp) { MPS2FPGAIO *s = MPS2_FPGAIO(dev); + uint32_t i; - s->led[0] = led_create_simple(OBJECT(dev), GPIO_POLARITY_ACTIVE_HIGH, - LED_COLOR_GREEN, "USERLED0"); - s->led[1] = led_create_simple(OBJECT(dev), GPIO_POLARITY_ACTIVE_HIGH, - LED_COLOR_GREEN, "USERLED1"); + if (s->num_leds > MPS2FPGAIO_MAX_LEDS) { + error_setg(errp, "num-leds cannot be greater than %d", + MPS2FPGAIO_MAX_LEDS); + return; + } + + for (i = 0; i < s->num_leds; i++) { + g_autofree char *ledname = g_strdup_printf("USERLED%d", i); + s->led[i] = led_create_simple(OBJECT(dev), GPIO_POLARITY_ACTIVE_HIGH, + LED_COLOR_GREEN, ledname); + } } static bool mps2_fpgaio_counters_needed(void *opaque) @@ -303,6 +325,9 @@ static const VMStateDescription mps2_fpgaio_vmstate = { static Property mps2_fpgaio_properties[] = { /* Frequency of the prescale counter */ DEFINE_PROP_UINT32("prescale-clk", MPS2FPGAIO, prescale_clk, 20000000), + /* Number of LEDs controlled by LED0 register */ + DEFINE_PROP_UINT32("num-leds", MPS2FPGAIO, num_leds, 2), + DEFINE_PROP_BOOL("has-switches", MPS2FPGAIO, has_switches, false), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/misc/mps2-scc.c b/hw/misc/mps2-scc.c index ce1dfe9356..140a4b9ceb 100644 --- a/hw/misc/mps2-scc.c +++ b/hw/misc/mps2-scc.c @@ -13,7 +13,7 @@ * found in the FPGA images of MPS2 development boards. * * Documentation of it can be found in the MPS2 TRM: - * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.100112_0100_03_en/index.html + * https://developer.arm.com/documentation/100112/latest/ * and also in the Application Notes documenting individual FPGA images. */ @@ -31,8 +31,11 @@ REG32(CFG0, 0) REG32(CFG1, 4) +REG32(CFG2, 8) REG32(CFG3, 0xc) REG32(CFG4, 0x10) +REG32(CFG5, 0x14) +REG32(CFG6, 0x18) REG32(CFGDATA_RTN, 0xa0) REG32(CFGDATA_OUT, 0xa4) REG32(CFGCTRL, 0xa8) @@ -49,6 +52,12 @@ REG32(DLL, 0x100) REG32(AID, 0xFF8) REG32(ID, 0xFFC) +static int scc_partno(MPS2SCC *s) +{ + /* Return the partno field of the SCC_ID (0x524, 0x511, etc) */ + return extract32(s->id, 4, 8); +} + /* Handle a write via the SYS_CFG channel to the specified function/device. * Return false on error (reported to guest via SYS_CFGCTRL ERROR bit). */ @@ -57,7 +66,7 @@ static bool scc_cfg_write(MPS2SCC *s, unsigned function, { trace_mps2_scc_cfg_write(function, device, value); - if (function != 1 || device >= NUM_OSCCLK) { + if (function != 1 || device >= s->num_oscclk) { qemu_log_mask(LOG_GUEST_ERROR, "MPS2 SCC config write: bad function %d device %d\n", function, device); @@ -75,7 +84,7 @@ static bool scc_cfg_write(MPS2SCC *s, unsigned function, static bool scc_cfg_read(MPS2SCC *s, unsigned function, unsigned device, uint32_t *value) { - if (function != 1 || device >= NUM_OSCCLK) { + if (function != 1 || device >= s->num_oscclk) { qemu_log_mask(LOG_GUEST_ERROR, "MPS2 SCC config read: bad function %d device %d\n", function, device); @@ -100,7 +109,18 @@ static uint64_t mps2_scc_read(void *opaque, hwaddr offset, unsigned size) case A_CFG1: r = s->cfg1; break; + case A_CFG2: + if (scc_partno(s) != 0x524) { + /* CFG2 reserved on other boards */ + goto bad_offset; + } + r = s->cfg2; + break; case A_CFG3: + if (scc_partno(s) == 0x524) { + /* CFG3 reserved on AN524 */ + goto bad_offset; + } /* These are user-settable DIP switches on the board. We don't * model that, so just return zeroes. */ @@ -109,6 +129,20 @@ static uint64_t mps2_scc_read(void *opaque, hwaddr offset, unsigned size) case A_CFG4: r = s->cfg4; break; + case A_CFG5: + if (scc_partno(s) != 0x524) { + /* CFG5 reserved on other boards */ + goto bad_offset; + } + r = s->cfg5; + break; + case A_CFG6: + if (scc_partno(s) != 0x524) { + /* CFG6 reserved on other boards */ + goto bad_offset; + } + r = s->cfg6; + break; case A_CFGDATA_RTN: r = s->cfgdata_rtn; break; @@ -131,6 +165,7 @@ static uint64_t mps2_scc_read(void *opaque, hwaddr offset, unsigned size) r = s->id; break; default: + bad_offset: qemu_log_mask(LOG_GUEST_ERROR, "MPS2 SCC read: bad offset %x\n", (int) offset); r = 0; @@ -159,6 +194,30 @@ static void mps2_scc_write(void *opaque, hwaddr offset, uint64_t value, led_set_state(s->led[i], extract32(value, i, 1)); } break; + case A_CFG2: + if (scc_partno(s) != 0x524) { + /* CFG2 reserved on other boards */ + goto bad_offset; + } + /* AN524: QSPI Select signal */ + s->cfg2 = value; + break; + case A_CFG5: + if (scc_partno(s) != 0x524) { + /* CFG5 reserved on other boards */ + goto bad_offset; + } + /* AN524: ACLK frequency in Hz */ + s->cfg5 = value; + break; + case A_CFG6: + if (scc_partno(s) != 0x524) { + /* CFG6 reserved on other boards */ + goto bad_offset; + } + /* AN524: Clock divider for BRAM */ + s->cfg6 = value; + break; case A_CFGDATA_OUT: s->cfgdata_out = value; break; @@ -202,6 +261,7 @@ static void mps2_scc_write(void *opaque, hwaddr offset, uint64_t value, s->dll = deposit32(s->dll, 24, 8, extract32(value, 24, 8)); break; default: + bad_offset: qemu_log_mask(LOG_GUEST_ERROR, "MPS2 SCC write: bad offset 0x%x\n", (int) offset); break; @@ -222,12 +282,15 @@ static void mps2_scc_reset(DeviceState *dev) trace_mps2_scc_reset(); s->cfg0 = 0; s->cfg1 = 0; + s->cfg2 = 0; + s->cfg5 = 0; + s->cfg6 = 0; s->cfgdata_rtn = 0; s->cfgdata_out = 0; s->cfgctrl = 0x100000; s->cfgstat = 0; s->dll = 0xffff0001; - for (i = 0; i < NUM_OSCCLK; i++) { + for (i = 0; i < s->num_oscclk; i++) { s->oscclk[i] = s->oscclk_reset[i]; } for (i = 0; i < ARRAY_SIZE(s->led); i++) { @@ -254,21 +317,28 @@ static void mps2_scc_realize(DeviceState *dev, Error **errp) LED_COLOR_GREEN, name); g_free(name); } + + s->oscclk = g_new0(uint32_t, s->num_oscclk); } static const VMStateDescription mps2_scc_vmstate = { .name = "mps2-scc", - .version_id = 1, - .minimum_version_id = 1, + .version_id = 3, + .minimum_version_id = 3, .fields = (VMStateField[]) { VMSTATE_UINT32(cfg0, MPS2SCC), VMSTATE_UINT32(cfg1, MPS2SCC), + VMSTATE_UINT32(cfg2, MPS2SCC), + /* cfg3, cfg4 are read-only so need not be migrated */ + VMSTATE_UINT32(cfg5, MPS2SCC), + VMSTATE_UINT32(cfg6, MPS2SCC), VMSTATE_UINT32(cfgdata_rtn, MPS2SCC), VMSTATE_UINT32(cfgdata_out, MPS2SCC), VMSTATE_UINT32(cfgctrl, MPS2SCC), VMSTATE_UINT32(cfgstat, MPS2SCC), VMSTATE_UINT32(dll, MPS2SCC), - VMSTATE_UINT32_ARRAY(oscclk, MPS2SCC, NUM_OSCCLK), + VMSTATE_VARRAY_UINT32(oscclk, MPS2SCC, num_oscclk, + 0, vmstate_info_uint32, uint32_t), VMSTATE_END_OF_LIST() } }; @@ -280,14 +350,13 @@ static Property mps2_scc_properties[] = { DEFINE_PROP_UINT32("scc-cfg4", MPS2SCC, cfg4, 0), DEFINE_PROP_UINT32("scc-aid", MPS2SCC, aid, 0), DEFINE_PROP_UINT32("scc-id", MPS2SCC, id, 0), - /* These are the initial settings for the source clocks on the board. + /* + * These are the initial settings for the source clocks on the board. * In hardware they can be configured via a config file read by the * motherboard configuration controller to suit the FPGA image. - * These default values are used by most of the standard FPGA images. */ - DEFINE_PROP_UINT32("oscclk0", MPS2SCC, oscclk_reset[0], 50000000), - DEFINE_PROP_UINT32("oscclk1", MPS2SCC, oscclk_reset[1], 24576000), - DEFINE_PROP_UINT32("oscclk2", MPS2SCC, oscclk_reset[2], 25000000), + DEFINE_PROP_ARRAY("oscclk", MPS2SCC, num_oscclk, oscclk_reset, + qdev_prop_uint32, uint32_t), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/net/meson.build b/hw/net/meson.build index 4a7051b54a..af0749c42b 100644 --- a/hw/net/meson.build +++ b/hw/net/meson.build @@ -35,6 +35,7 @@ softmmu_ss.add(when: 'CONFIG_I82596_COMMON', if_true: files('i82596.c')) softmmu_ss.add(when: 'CONFIG_SUNHME', if_true: files('sunhme.c')) softmmu_ss.add(when: 'CONFIG_FTGMAC100', if_true: files('ftgmac100.c')) softmmu_ss.add(when: 'CONFIG_SUNGEM', if_true: files('sungem.c')) +softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_emc.c')) softmmu_ss.add(when: 'CONFIG_ETRAXFS', if_true: files('etraxfs_eth.c')) softmmu_ss.add(when: 'CONFIG_COLDFIRE', if_true: files('mcf_fec.c')) diff --git a/hw/net/npcm7xx_emc.c b/hw/net/npcm7xx_emc.c new file mode 100644 index 0000000000..714a742ba7 --- /dev/null +++ b/hw/net/npcm7xx_emc.c @@ -0,0 +1,857 @@ +/* + * Nuvoton NPCM7xx EMC Module + * + * Copyright 2020 Google LLC + * + * 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. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * Unsupported/unimplemented features: + * - MCMDR.FDUP (full duplex) is ignored, half duplex is not supported + * - Only CAM0 is supported, CAM[1-15] are not + * - writes to CAMEN.[1-15] are ignored, these bits always read as zeroes + * - MII is not implemented, MIIDA.BUSY and MIID always return zero + * - MCMDR.LBK is not implemented + * - MCMDR.{OPMOD,ENSQE,AEP,ARP} are not supported + * - H/W FIFOs are not supported, MCMDR.FFTCR is ignored + * - MGSTA.SQE is not supported + * - pause and control frames are not implemented + * - MGSTA.CCNT is not supported + * - MPCNT, DMARFS are not implemented + */ + +#include "qemu/osdep.h" + +/* For crc32 */ +#include <zlib.h> + +#include "qemu-common.h" +#include "hw/irq.h" +#include "hw/qdev-clock.h" +#include "hw/qdev-properties.h" +#include "hw/net/npcm7xx_emc.h" +#include "net/eth.h" +#include "migration/vmstate.h" +#include "qemu/bitops.h" +#include "qemu/error-report.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qemu/units.h" +#include "sysemu/dma.h" +#include "trace.h" + +#define CRC_LENGTH 4 + +/* + * The maximum size of a (layer 2) ethernet frame as defined by 802.3. + * 1518 = 6(dest macaddr) + 6(src macaddr) + 2(proto) + 4(crc) + 1500(payload) + * This does not include an additional 4 for the vlan field (802.1q). + */ +#define MAX_ETH_FRAME_SIZE 1518 + +static const char *emc_reg_name(int regno) +{ +#define REG(name) case REG_ ## name: return #name; + switch (regno) { + REG(CAMCMR) + REG(CAMEN) + REG(TXDLSA) + REG(RXDLSA) + REG(MCMDR) + REG(MIID) + REG(MIIDA) + REG(FFTCR) + REG(TSDR) + REG(RSDR) + REG(DMARFC) + REG(MIEN) + REG(MISTA) + REG(MGSTA) + REG(MPCNT) + REG(MRPC) + REG(MRPCC) + REG(MREPC) + REG(DMARFS) + REG(CTXDSA) + REG(CTXBSA) + REG(CRXDSA) + REG(CRXBSA) + case REG_CAMM_BASE + 0: return "CAM0M"; + case REG_CAML_BASE + 0: return "CAM0L"; + case REG_CAMM_BASE + 2 ... REG_CAMML_LAST: + /* Only CAM0 is supported, fold the others into something simple. */ + if (regno & 1) { + return "CAM<n>L"; + } else { + return "CAM<n>M"; + } + default: return "UNKNOWN"; + } +#undef REG +} + +static void emc_reset(NPCM7xxEMCState *emc) +{ + trace_npcm7xx_emc_reset(emc->emc_num); + + memset(&emc->regs[0], 0, sizeof(emc->regs)); + + /* These regs have non-zero reset values. */ + emc->regs[REG_TXDLSA] = 0xfffffffc; + emc->regs[REG_RXDLSA] = 0xfffffffc; + emc->regs[REG_MIIDA] = 0x00900000; + emc->regs[REG_FFTCR] = 0x0101; + emc->regs[REG_DMARFC] = 0x0800; + emc->regs[REG_MPCNT] = 0x7fff; + + emc->tx_active = false; + emc->rx_active = false; +} + +static void npcm7xx_emc_reset(DeviceState *dev) +{ + NPCM7xxEMCState *emc = NPCM7XX_EMC(dev); + emc_reset(emc); +} + +static void emc_soft_reset(NPCM7xxEMCState *emc) +{ + /* + * The docs say at least MCMDR.{LBK,OPMOD} bits are not changed during a + * soft reset, but does not go into further detail. For now, KISS. + */ + uint32_t mcmdr = emc->regs[REG_MCMDR]; + emc_reset(emc); + emc->regs[REG_MCMDR] = mcmdr & (REG_MCMDR_LBK | REG_MCMDR_OPMOD); + + qemu_set_irq(emc->tx_irq, 0); + qemu_set_irq(emc->rx_irq, 0); +} + +static void emc_set_link(NetClientState *nc) +{ + /* Nothing to do yet. */ +} + +/* MISTA.TXINTR is the union of the individual bits with their enables. */ +static void emc_update_mista_txintr(NPCM7xxEMCState *emc) +{ + /* Only look at the bits we support. */ + uint32_t mask = (REG_MISTA_TXBERR | + REG_MISTA_TDU | + REG_MISTA_TXCP); + if (emc->regs[REG_MISTA] & emc->regs[REG_MIEN] & mask) { + emc->regs[REG_MISTA] |= REG_MISTA_TXINTR; + } else { + emc->regs[REG_MISTA] &= ~REG_MISTA_TXINTR; + } +} + +/* MISTA.RXINTR is the union of the individual bits with their enables. */ +static void emc_update_mista_rxintr(NPCM7xxEMCState *emc) +{ + /* Only look at the bits we support. */ + uint32_t mask = (REG_MISTA_RXBERR | + REG_MISTA_RDU | + REG_MISTA_RXGD); + if (emc->regs[REG_MISTA] & emc->regs[REG_MIEN] & mask) { + emc->regs[REG_MISTA] |= REG_MISTA_RXINTR; + } else { + emc->regs[REG_MISTA] &= ~REG_MISTA_RXINTR; + } +} + +/* N.B. emc_update_mista_txintr must have already been called. */ +static void emc_update_tx_irq(NPCM7xxEMCState *emc) +{ + int level = !!(emc->regs[REG_MISTA] & + emc->regs[REG_MIEN] & + REG_MISTA_TXINTR); + trace_npcm7xx_emc_update_tx_irq(level); + qemu_set_irq(emc->tx_irq, level); +} + +/* N.B. emc_update_mista_rxintr must have already been called. */ +static void emc_update_rx_irq(NPCM7xxEMCState *emc) +{ + int level = !!(emc->regs[REG_MISTA] & + emc->regs[REG_MIEN] & + REG_MISTA_RXINTR); + trace_npcm7xx_emc_update_rx_irq(level); + qemu_set_irq(emc->rx_irq, level); +} + +/* Update IRQ states due to changes in MIEN,MISTA. */ +static void emc_update_irq_from_reg_change(NPCM7xxEMCState *emc) +{ + emc_update_mista_txintr(emc); + emc_update_tx_irq(emc); + + emc_update_mista_rxintr(emc); + emc_update_rx_irq(emc); +} + +static int emc_read_tx_desc(dma_addr_t addr, NPCM7xxEMCTxDesc *desc) +{ + if (dma_memory_read(&address_space_memory, addr, desc, sizeof(*desc))) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Failed to read descriptor @ 0x%" + HWADDR_PRIx "\n", __func__, addr); + return -1; + } + desc->flags = le32_to_cpu(desc->flags); + desc->txbsa = le32_to_cpu(desc->txbsa); + desc->status_and_length = le32_to_cpu(desc->status_and_length); + desc->ntxdsa = le32_to_cpu(desc->ntxdsa); + return 0; +} + +static int emc_write_tx_desc(const NPCM7xxEMCTxDesc *desc, dma_addr_t addr) +{ + NPCM7xxEMCTxDesc le_desc; + + le_desc.flags = cpu_to_le32(desc->flags); + le_desc.txbsa = cpu_to_le32(desc->txbsa); + le_desc.status_and_length = cpu_to_le32(desc->status_and_length); + le_desc.ntxdsa = cpu_to_le32(desc->ntxdsa); + if (dma_memory_write(&address_space_memory, addr, &le_desc, + sizeof(le_desc))) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Failed to write descriptor @ 0x%" + HWADDR_PRIx "\n", __func__, addr); + return -1; + } + return 0; +} + +static int emc_read_rx_desc(dma_addr_t addr, NPCM7xxEMCRxDesc *desc) +{ + if (dma_memory_read(&address_space_memory, addr, desc, sizeof(*desc))) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Failed to read descriptor @ 0x%" + HWADDR_PRIx "\n", __func__, addr); + return -1; + } + desc->status_and_length = le32_to_cpu(desc->status_and_length); + desc->rxbsa = le32_to_cpu(desc->rxbsa); + desc->reserved = le32_to_cpu(desc->reserved); + desc->nrxdsa = le32_to_cpu(desc->nrxdsa); + return 0; +} + +static int emc_write_rx_desc(const NPCM7xxEMCRxDesc *desc, dma_addr_t addr) +{ + NPCM7xxEMCRxDesc le_desc; + + le_desc.status_and_length = cpu_to_le32(desc->status_and_length); + le_desc.rxbsa = cpu_to_le32(desc->rxbsa); + le_desc.reserved = cpu_to_le32(desc->reserved); + le_desc.nrxdsa = cpu_to_le32(desc->nrxdsa); + if (dma_memory_write(&address_space_memory, addr, &le_desc, + sizeof(le_desc))) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Failed to write descriptor @ 0x%" + HWADDR_PRIx "\n", __func__, addr); + return -1; + } + return 0; +} + +static void emc_set_mista(NPCM7xxEMCState *emc, uint32_t flags) +{ + trace_npcm7xx_emc_set_mista(flags); + emc->regs[REG_MISTA] |= flags; + if (extract32(flags, 16, 16)) { + emc_update_mista_txintr(emc); + } + if (extract32(flags, 0, 16)) { + emc_update_mista_rxintr(emc); + } +} + +static void emc_halt_tx(NPCM7xxEMCState *emc, uint32_t mista_flag) +{ + emc->tx_active = false; + emc_set_mista(emc, mista_flag); +} + +static void emc_halt_rx(NPCM7xxEMCState *emc, uint32_t mista_flag) +{ + emc->rx_active = false; + emc_set_mista(emc, mista_flag); +} + +static void emc_set_next_tx_descriptor(NPCM7xxEMCState *emc, + const NPCM7xxEMCTxDesc *tx_desc, + uint32_t desc_addr) +{ + /* Update the current descriptor, if only to reset the owner flag. */ + if (emc_write_tx_desc(tx_desc, desc_addr)) { + /* + * We just read it so this shouldn't generally happen. + * Error already reported. + */ + emc_set_mista(emc, REG_MISTA_TXBERR); + } + emc->regs[REG_CTXDSA] = TX_DESC_NTXDSA(tx_desc->ntxdsa); +} + +static void emc_set_next_rx_descriptor(NPCM7xxEMCState *emc, + const NPCM7xxEMCRxDesc *rx_desc, + uint32_t desc_addr) +{ + /* Update the current descriptor, if only to reset the owner flag. */ + if (emc_write_rx_desc(rx_desc, desc_addr)) { + /* + * We just read it so this shouldn't generally happen. + * Error already reported. + */ + emc_set_mista(emc, REG_MISTA_RXBERR); + } + emc->regs[REG_CRXDSA] = RX_DESC_NRXDSA(rx_desc->nrxdsa); +} + +static void emc_try_send_next_packet(NPCM7xxEMCState *emc) +{ + /* Working buffer for sending out packets. Most packets fit in this. */ +#define TX_BUFFER_SIZE 2048 + uint8_t tx_send_buffer[TX_BUFFER_SIZE]; + uint32_t desc_addr = TX_DESC_NTXDSA(emc->regs[REG_CTXDSA]); + NPCM7xxEMCTxDesc tx_desc; + uint32_t next_buf_addr, length; + uint8_t *buf; + g_autofree uint8_t *malloced_buf = NULL; + + if (emc_read_tx_desc(desc_addr, &tx_desc)) { + /* Error reading descriptor, already reported. */ + emc_halt_tx(emc, REG_MISTA_TXBERR); + emc_update_tx_irq(emc); + return; + } + + /* Nothing we can do if we don't own the descriptor. */ + if (!(tx_desc.flags & TX_DESC_FLAG_OWNER_MASK)) { + trace_npcm7xx_emc_cpu_owned_desc(desc_addr); + emc_halt_tx(emc, REG_MISTA_TDU); + emc_update_tx_irq(emc); + return; + } + + /* Give the descriptor back regardless of what happens. */ + tx_desc.flags &= ~TX_DESC_FLAG_OWNER_MASK; + tx_desc.status_and_length &= 0xffff; + + /* + * Despite the h/w documentation saying the tx buffer is word aligned, + * the linux driver does not word align the buffer. There is value in not + * aligning the buffer: See the description of NET_IP_ALIGN in linux + * kernel sources. + */ + next_buf_addr = tx_desc.txbsa; + emc->regs[REG_CTXBSA] = next_buf_addr; + length = TX_DESC_PKT_LEN(tx_desc.status_and_length); + buf = &tx_send_buffer[0]; + + if (length > sizeof(tx_send_buffer)) { + malloced_buf = g_malloc(length); + buf = malloced_buf; + } + + if (dma_memory_read(&address_space_memory, next_buf_addr, buf, length)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Failed to read packet @ 0x%x\n", + __func__, next_buf_addr); + emc_set_mista(emc, REG_MISTA_TXBERR); + emc_set_next_tx_descriptor(emc, &tx_desc, desc_addr); + emc_update_tx_irq(emc); + trace_npcm7xx_emc_tx_done(emc->regs[REG_CTXDSA]); + return; + } + + if ((tx_desc.flags & TX_DESC_FLAG_PADEN) && (length < MIN_PACKET_LENGTH)) { + memset(buf + length, 0, MIN_PACKET_LENGTH - length); + length = MIN_PACKET_LENGTH; + } + + /* N.B. emc_receive can get called here. */ + qemu_send_packet(qemu_get_queue(emc->nic), buf, length); + trace_npcm7xx_emc_sent_packet(length); + + tx_desc.status_and_length |= TX_DESC_STATUS_TXCP; + if (tx_desc.flags & TX_DESC_FLAG_INTEN) { + emc_set_mista(emc, REG_MISTA_TXCP); + } + if (emc->regs[REG_MISTA] & emc->regs[REG_MIEN] & REG_MISTA_TXINTR) { + tx_desc.status_and_length |= TX_DESC_STATUS_TXINTR; + } + + emc_set_next_tx_descriptor(emc, &tx_desc, desc_addr); + emc_update_tx_irq(emc); + trace_npcm7xx_emc_tx_done(emc->regs[REG_CTXDSA]); +} + +static bool emc_can_receive(NetClientState *nc) +{ + NPCM7xxEMCState *emc = NPCM7XX_EMC(qemu_get_nic_opaque(nc)); + + bool can_receive = emc->rx_active; + trace_npcm7xx_emc_can_receive(can_receive); + return can_receive; +} + +/* If result is false then *fail_reason contains the reason. */ +static bool emc_receive_filter1(NPCM7xxEMCState *emc, const uint8_t *buf, + size_t len, const char **fail_reason) +{ + eth_pkt_types_e pkt_type = get_eth_packet_type(PKT_GET_ETH_HDR(buf)); + + switch (pkt_type) { + case ETH_PKT_BCAST: + if (emc->regs[REG_CAMCMR] & REG_CAMCMR_CCAM) { + return true; + } else { + *fail_reason = "Broadcast packet disabled"; + return !!(emc->regs[REG_CAMCMR] & REG_CAMCMR_ABP); + } + case ETH_PKT_MCAST: + if (emc->regs[REG_CAMCMR] & REG_CAMCMR_CCAM) { + return true; + } else { + *fail_reason = "Multicast packet disabled"; + return !!(emc->regs[REG_CAMCMR] & REG_CAMCMR_AMP); + } + case ETH_PKT_UCAST: { + bool matches; + if (emc->regs[REG_CAMCMR] & REG_CAMCMR_AUP) { + return true; + } + matches = ((emc->regs[REG_CAMCMR] & REG_CAMCMR_ECMP) && + /* We only support one CAM register, CAM0. */ + (emc->regs[REG_CAMEN] & (1 << 0)) && + memcmp(buf, emc->conf.macaddr.a, ETH_ALEN) == 0); + if (emc->regs[REG_CAMCMR] & REG_CAMCMR_CCAM) { + *fail_reason = "MACADDR matched, comparison complemented"; + return !matches; + } else { + *fail_reason = "MACADDR didn't match"; + return matches; + } + } + default: + g_assert_not_reached(); + } +} + +static bool emc_receive_filter(NPCM7xxEMCState *emc, const uint8_t *buf, + size_t len) +{ + const char *fail_reason = NULL; + bool ok = emc_receive_filter1(emc, buf, len, &fail_reason); + if (!ok) { + trace_npcm7xx_emc_packet_filtered_out(fail_reason); + } + return ok; +} + +static ssize_t emc_receive(NetClientState *nc, const uint8_t *buf, size_t len1) +{ + NPCM7xxEMCState *emc = NPCM7XX_EMC(qemu_get_nic_opaque(nc)); + const uint32_t len = len1; + size_t max_frame_len; + bool long_frame; + uint32_t desc_addr; + NPCM7xxEMCRxDesc rx_desc; + uint32_t crc; + uint8_t *crc_ptr; + uint32_t buf_addr; + + trace_npcm7xx_emc_receiving_packet(len); + + if (!emc_can_receive(nc)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Unexpected packet\n", __func__); + return -1; + } + + if (len < ETH_HLEN || + /* Defensive programming: drop unsupportable large packets. */ + len > 0xffff - CRC_LENGTH) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Dropped frame of %u bytes\n", + __func__, len); + return len; + } + + /* + * DENI is set if EMC received the Length/Type field of the incoming + * packet, so it will be set regardless of what happens next. + */ + emc_set_mista(emc, REG_MISTA_DENI); + + if (!emc_receive_filter(emc, buf, len)) { + emc_update_rx_irq(emc); + return len; + } + + /* Huge frames (> DMARFC) are dropped. */ + max_frame_len = REG_DMARFC_RXMS(emc->regs[REG_DMARFC]); + if (len + CRC_LENGTH > max_frame_len) { + trace_npcm7xx_emc_packet_dropped(len); + emc_set_mista(emc, REG_MISTA_DFOI); + emc_update_rx_irq(emc); + return len; + } + + /* + * Long Frames (> MAX_ETH_FRAME_SIZE) are also dropped, unless MCMDR.ALP + * is set. + */ + long_frame = false; + if (len + CRC_LENGTH > MAX_ETH_FRAME_SIZE) { + if (emc->regs[REG_MCMDR] & REG_MCMDR_ALP) { + long_frame = true; + } else { + trace_npcm7xx_emc_packet_dropped(len); + emc_set_mista(emc, REG_MISTA_PTLE); + emc_update_rx_irq(emc); + return len; + } + } + + desc_addr = RX_DESC_NRXDSA(emc->regs[REG_CRXDSA]); + if (emc_read_rx_desc(desc_addr, &rx_desc)) { + /* Error reading descriptor, already reported. */ + emc_halt_rx(emc, REG_MISTA_RXBERR); + emc_update_rx_irq(emc); + return len; + } + + /* Nothing we can do if we don't own the descriptor. */ + if (!(rx_desc.status_and_length & RX_DESC_STATUS_OWNER_MASK)) { + trace_npcm7xx_emc_cpu_owned_desc(desc_addr); + emc_halt_rx(emc, REG_MISTA_RDU); + emc_update_rx_irq(emc); + return len; + } + + crc = 0; + crc_ptr = (uint8_t *) &crc; + if (!(emc->regs[REG_MCMDR] & REG_MCMDR_SPCRC)) { + crc = cpu_to_be32(crc32(~0, buf, len)); + } + + /* Give the descriptor back regardless of what happens. */ + rx_desc.status_and_length &= ~RX_DESC_STATUS_OWNER_MASK; + + buf_addr = rx_desc.rxbsa; + emc->regs[REG_CRXBSA] = buf_addr; + if (dma_memory_write(&address_space_memory, buf_addr, buf, len) || + (!(emc->regs[REG_MCMDR] & REG_MCMDR_SPCRC) && + dma_memory_write(&address_space_memory, buf_addr + len, crc_ptr, + 4))) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bus error writing packet\n", + __func__); + emc_set_mista(emc, REG_MISTA_RXBERR); + emc_set_next_rx_descriptor(emc, &rx_desc, desc_addr); + emc_update_rx_irq(emc); + trace_npcm7xx_emc_rx_done(emc->regs[REG_CRXDSA]); + return len; + } + + trace_npcm7xx_emc_received_packet(len); + + /* Note: We've already verified len+4 <= 0xffff. */ + rx_desc.status_and_length = len; + if (!(emc->regs[REG_MCMDR] & REG_MCMDR_SPCRC)) { + rx_desc.status_and_length += 4; + } + rx_desc.status_and_length |= RX_DESC_STATUS_RXGD; + emc_set_mista(emc, REG_MISTA_RXGD); + + if (emc->regs[REG_MISTA] & emc->regs[REG_MIEN] & REG_MISTA_RXINTR) { + rx_desc.status_and_length |= RX_DESC_STATUS_RXINTR; + } + if (long_frame) { + rx_desc.status_and_length |= RX_DESC_STATUS_PTLE; + } + + emc_set_next_rx_descriptor(emc, &rx_desc, desc_addr); + emc_update_rx_irq(emc); + trace_npcm7xx_emc_rx_done(emc->regs[REG_CRXDSA]); + return len; +} + +static void emc_try_receive_next_packet(NPCM7xxEMCState *emc) +{ + if (emc_can_receive(qemu_get_queue(emc->nic))) { + qemu_flush_queued_packets(qemu_get_queue(emc->nic)); + } +} + +static uint64_t npcm7xx_emc_read(void *opaque, hwaddr offset, unsigned size) +{ + NPCM7xxEMCState *emc = opaque; + uint32_t reg = offset / sizeof(uint32_t); + uint32_t result; + + if (reg >= NPCM7XX_NUM_EMC_REGS) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Invalid offset 0x%04" HWADDR_PRIx "\n", + __func__, offset); + return 0; + } + + switch (reg) { + case REG_MIID: + /* + * We don't implement MII. For determinism, always return zero as + * writes record the last value written for debugging purposes. + */ + qemu_log_mask(LOG_UNIMP, "%s: Read of MIID, returning 0\n", __func__); + result = 0; + break; + case REG_TSDR: + case REG_RSDR: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Read of write-only reg, %s/%d\n", + __func__, emc_reg_name(reg), reg); + return 0; + default: + result = emc->regs[reg]; + break; + } + + trace_npcm7xx_emc_reg_read(emc->emc_num, result, emc_reg_name(reg), reg); + return result; +} + +static void npcm7xx_emc_write(void *opaque, hwaddr offset, + uint64_t v, unsigned size) +{ + NPCM7xxEMCState *emc = opaque; + uint32_t reg = offset / sizeof(uint32_t); + uint32_t value = v; + + g_assert(size == sizeof(uint32_t)); + + if (reg >= NPCM7XX_NUM_EMC_REGS) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Invalid offset 0x%04" HWADDR_PRIx "\n", + __func__, offset); + return; + } + + trace_npcm7xx_emc_reg_write(emc->emc_num, emc_reg_name(reg), reg, value); + + switch (reg) { + case REG_CAMCMR: + emc->regs[reg] = value; + break; + case REG_CAMEN: + /* Only CAM0 is supported, don't pretend otherwise. */ + if (value & ~1) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Only CAM0 is supported, cannot enable others" + ": 0x%x\n", + __func__, value); + } + emc->regs[reg] = value & 1; + break; + case REG_CAMM_BASE + 0: + emc->regs[reg] = value; + emc->conf.macaddr.a[0] = value >> 24; + emc->conf.macaddr.a[1] = value >> 16; + emc->conf.macaddr.a[2] = value >> 8; + emc->conf.macaddr.a[3] = value >> 0; + break; + case REG_CAML_BASE + 0: + emc->regs[reg] = value; + emc->conf.macaddr.a[4] = value >> 24; + emc->conf.macaddr.a[5] = value >> 16; + break; + case REG_MCMDR: { + uint32_t prev; + if (value & REG_MCMDR_SWR) { + emc_soft_reset(emc); + /* On h/w the reset happens over multiple cycles. For now KISS. */ + break; + } + prev = emc->regs[reg]; + emc->regs[reg] = value; + /* Update tx state. */ + if (!(prev & REG_MCMDR_TXON) && + (value & REG_MCMDR_TXON)) { + emc->regs[REG_CTXDSA] = emc->regs[REG_TXDLSA]; + /* + * Linux kernel turns TX on with CPU still holding descriptor, + * which suggests we should wait for a write to TSDR before trying + * to send a packet: so we don't send one here. + */ + } else if ((prev & REG_MCMDR_TXON) && + !(value & REG_MCMDR_TXON)) { + emc->regs[REG_MGSTA] |= REG_MGSTA_TXHA; + } + if (!(value & REG_MCMDR_TXON)) { + emc_halt_tx(emc, 0); + } + /* Update rx state. */ + if (!(prev & REG_MCMDR_RXON) && + (value & REG_MCMDR_RXON)) { + emc->regs[REG_CRXDSA] = emc->regs[REG_RXDLSA]; + } else if ((prev & REG_MCMDR_RXON) && + !(value & REG_MCMDR_RXON)) { + emc->regs[REG_MGSTA] |= REG_MGSTA_RXHA; + } + if (!(value & REG_MCMDR_RXON)) { + emc_halt_rx(emc, 0); + } + break; + } + case REG_TXDLSA: + case REG_RXDLSA: + case REG_DMARFC: + case REG_MIID: + emc->regs[reg] = value; + break; + case REG_MIEN: + emc->regs[reg] = value; + emc_update_irq_from_reg_change(emc); + break; + case REG_MISTA: + /* Clear the bits that have 1 in "value". */ + emc->regs[reg] &= ~value; + emc_update_irq_from_reg_change(emc); + break; + case REG_MGSTA: + /* Clear the bits that have 1 in "value". */ + emc->regs[reg] &= ~value; + break; + case REG_TSDR: + if (emc->regs[REG_MCMDR] & REG_MCMDR_TXON) { + emc->tx_active = true; + /* Keep trying to send packets until we run out. */ + while (emc->tx_active) { + emc_try_send_next_packet(emc); + } + } + break; + case REG_RSDR: + if (emc->regs[REG_MCMDR] & REG_MCMDR_RXON) { + emc->rx_active = true; + emc_try_receive_next_packet(emc); + } + break; + case REG_MIIDA: + emc->regs[reg] = value & ~REG_MIIDA_BUSY; + break; + case REG_MRPC: + case REG_MRPCC: + case REG_MREPC: + case REG_CTXDSA: + case REG_CTXBSA: + case REG_CRXDSA: + case REG_CRXBSA: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Write to read-only reg %s/%d\n", + __func__, emc_reg_name(reg), reg); + break; + default: + qemu_log_mask(LOG_UNIMP, "%s: Write to unimplemented reg %s/%d\n", + __func__, emc_reg_name(reg), reg); + break; + } +} + +static const struct MemoryRegionOps npcm7xx_emc_ops = { + .read = npcm7xx_emc_read, + .write = npcm7xx_emc_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static void emc_cleanup(NetClientState *nc) +{ + /* Nothing to do yet. */ +} + +static NetClientInfo net_npcm7xx_emc_info = { + .type = NET_CLIENT_DRIVER_NIC, + .size = sizeof(NICState), + .can_receive = emc_can_receive, + .receive = emc_receive, + .cleanup = emc_cleanup, + .link_status_changed = emc_set_link, +}; + +static void npcm7xx_emc_realize(DeviceState *dev, Error **errp) +{ + NPCM7xxEMCState *emc = NPCM7XX_EMC(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(emc); + + memory_region_init_io(&emc->iomem, OBJECT(emc), &npcm7xx_emc_ops, emc, + TYPE_NPCM7XX_EMC, 4 * KiB); + sysbus_init_mmio(sbd, &emc->iomem); + sysbus_init_irq(sbd, &emc->tx_irq); + sysbus_init_irq(sbd, &emc->rx_irq); + + qemu_macaddr_default_if_unset(&emc->conf.macaddr); + emc->nic = qemu_new_nic(&net_npcm7xx_emc_info, &emc->conf, + object_get_typename(OBJECT(dev)), dev->id, emc); + qemu_format_nic_info_str(qemu_get_queue(emc->nic), emc->conf.macaddr.a); +} + +static void npcm7xx_emc_unrealize(DeviceState *dev) +{ + NPCM7xxEMCState *emc = NPCM7XX_EMC(dev); + + qemu_del_nic(emc->nic); +} + +static const VMStateDescription vmstate_npcm7xx_emc = { + .name = TYPE_NPCM7XX_EMC, + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT8(emc_num, NPCM7xxEMCState), + VMSTATE_UINT32_ARRAY(regs, NPCM7xxEMCState, NPCM7XX_NUM_EMC_REGS), + VMSTATE_BOOL(tx_active, NPCM7xxEMCState), + VMSTATE_BOOL(rx_active, NPCM7xxEMCState), + VMSTATE_END_OF_LIST(), + }, +}; + +static Property npcm7xx_emc_properties[] = { + DEFINE_NIC_PROPERTIES(NPCM7xxEMCState, conf), + DEFINE_PROP_END_OF_LIST(), +}; + +static void npcm7xx_emc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); + dc->desc = "NPCM7xx EMC Controller"; + dc->realize = npcm7xx_emc_realize; + dc->unrealize = npcm7xx_emc_unrealize; + dc->reset = npcm7xx_emc_reset; + dc->vmsd = &vmstate_npcm7xx_emc; + device_class_set_props(dc, npcm7xx_emc_properties); +} + +static const TypeInfo npcm7xx_emc_info = { + .name = TYPE_NPCM7XX_EMC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(NPCM7xxEMCState), + .class_init = npcm7xx_emc_class_init, +}; + +static void npcm7xx_emc_register_type(void) +{ + type_register_static(&npcm7xx_emc_info); +} + +type_init(npcm7xx_emc_register_type) diff --git a/hw/net/trace-events b/hw/net/trace-events index 5db45456d9..baf25ffa7e 100644 --- a/hw/net/trace-events +++ b/hw/net/trace-events @@ -429,3 +429,20 @@ imx_fec_receive_last(int last) "rx frame flags 0x%04x" imx_enet_receive(size_t size) "len %zu" imx_enet_receive_len(uint64_t addr, int len) "rx_bd 0x%"PRIx64" length %d" imx_enet_receive_last(int last) "rx frame flags 0x%04x" + +# npcm7xx_emc.c +npcm7xx_emc_reset(int emc_num) "Resetting emc%d" +npcm7xx_emc_update_tx_irq(int level) "Setting tx irq to %d" +npcm7xx_emc_update_rx_irq(int level) "Setting rx irq to %d" +npcm7xx_emc_set_mista(uint32_t flags) "ORing 0x%x into MISTA" +npcm7xx_emc_cpu_owned_desc(uint32_t addr) "Can't process cpu-owned descriptor @0x%x" +npcm7xx_emc_sent_packet(uint32_t len) "Sent %u byte packet" +npcm7xx_emc_tx_done(uint32_t ctxdsa) "TX done, CTXDSA=0x%x" +npcm7xx_emc_can_receive(int can_receive) "Can receive: %d" +npcm7xx_emc_packet_filtered_out(const char* fail_reason) "Packet filtered out: %s" +npcm7xx_emc_packet_dropped(uint32_t len) "%u byte packet dropped" +npcm7xx_emc_receiving_packet(uint32_t len) "Receiving %u byte packet" +npcm7xx_emc_received_packet(uint32_t len) "Received %u byte packet" +npcm7xx_emc_rx_done(uint32_t crxdsa) "RX done, CRXDSA=0x%x" +npcm7xx_emc_reg_read(int emc_num, uint32_t result, const char *name, int regno) "emc%d: 0x%x = reg[%s/%d]" +npcm7xx_emc_reg_write(int emc_num, const char *name, int regno, uint32_t value) "emc%d: reg[%s/%d] = 0x%x" diff --git a/hw/virtio/virtio-mmio.c b/hw/virtio/virtio-mmio.c index 610661d6a5..6990b9879c 100644 --- a/hw/virtio/virtio-mmio.c +++ b/hw/virtio/virtio-mmio.c @@ -737,8 +737,8 @@ static char *virtio_mmio_bus_get_dev_path(DeviceState *dev) BusState *virtio_mmio_bus; VirtIOMMIOProxy *virtio_mmio_proxy; char *proxy_path; - SysBusDevice *proxy_sbd; char *path; + MemoryRegionSection section; virtio_mmio_bus = qdev_get_parent_bus(dev); virtio_mmio_proxy = VIRTIO_MMIO(virtio_mmio_bus->parent); @@ -757,17 +757,18 @@ static char *virtio_mmio_bus_get_dev_path(DeviceState *dev) } /* Otherwise, we append the base address of the transport. */ - proxy_sbd = SYS_BUS_DEVICE(virtio_mmio_proxy); - assert(proxy_sbd->num_mmio == 1); - assert(proxy_sbd->mmio[0].memory == &virtio_mmio_proxy->iomem); + section = memory_region_find(&virtio_mmio_proxy->iomem, 0, 0x200); + assert(section.mr); if (proxy_path) { path = g_strdup_printf("%s/virtio-mmio@" TARGET_FMT_plx, proxy_path, - proxy_sbd->mmio[0].addr); + section.offset_within_address_space); } else { path = g_strdup_printf("virtio-mmio@" TARGET_FMT_plx, - proxy_sbd->mmio[0].addr); + section.offset_within_address_space); } + memory_region_unref(section.mr); + g_free(proxy_path); return path; } |