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 -----BEGIN PGP SIGNATURE----- iQJNBAABCAA3FiEE4aXFk81BneKOgxXPPCUl7RQ2DN4FAmBGEOgZHHBldGVyLm1h eWRlbGxAbGluYXJvLm9yZwAKCRA8JSXtFDYM3jE7D/9WjvsQPfIPHTEmXGh9gdRp p+SDRpA+h+f1TH9a4Ohooi/4+KvXqnyrTa+/YBuUn2rphkpeOEK9N0j7BO8pKFzC pyDJ53oZiSygFwpju3j6sg/CWyWn1lzAHXDd2OzMR1KC+5qD62VnUdgmYU2AuJu+ DyaN41ZfeaFOwQcbvzeDr73xqsAhHm7aQ8AY78Sx8be+W532fUjaceqsSafx0Ok7 L8qZRo1Zz/h4A3A0YwMg5ry2T7kwviPYPzVrIOQu9n+jcRNC/4YRWfzvyLi/JqwS oPRFJ3iA3ulIxQxF7DH3xc84A8dDuaDfoTqsSTtt+5+TtdRJXkDyjeH9ICdhrHci DAy+1OlSbzaxEim3DBxTaZ721X/R3X3FW8DI9mt9KBPHmpzhWXHawLEtxQL8U7Yi qBz2HB5uONImuC9D1Ssx3f2/Ig1OrnSGtidotwXYffi0B6wQ+TUTHxVHQEznIi7T Muv35x9jHd179EmcTtyzPfJV5SeOcRiS91LHr2bO5c0tvHxTRgUqD1uzM4eVnjXj DyjgAKP3hujyKTjh+nkREcxkkhyWdsqAhoTdJlyhZJ/wMDc9oVoegikxBJ3XU02f vKQ2u5onZcE7tAVvHS93l+MVsDsB1tHmE3LEyu+D3GXZDiQFy8uFX3Thsu8CNUE0 6CnFznXHnQFbqRNsF8O0NQ== =nphT -----END PGP SIGNATURE----- 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>
This commit is contained in:
commit
138d293197
@ -1,12 +1,15 @@
|
||||
Arm MPS2 boards (``mps2-an385``, ``mps2-an386``, ``mps2-an500``, ``mps2-an505``, ``mps2-an511``, ``mps2-an521``)
|
||||
================================================================================================================
|
||||
Arm MPS2 and MPS3 boards (``mps2-an385``, ``mps2-an386``, ``mps2-an500``, ``mps2-an505``, ``mps2-an511``, ``mps2-an521``, ``mps3-an524``)
|
||||
=========================================================================================================================================
|
||||
|
||||
These board models all use Arm M-profile CPUs.
|
||||
|
||||
The Arm MPS2 and MPS2+ dev boards are FPGA based (the 2+ has a bigger
|
||||
FPGA but is otherwise the same as the 2). Since the CPU itself
|
||||
and most of the devices are in the FPGA, the details of the board
|
||||
as seen by the guest depend significantly on the FPGA image.
|
||||
The Arm MPS2, MPS2+ and MPS3 dev boards are FPGA based (the 2+ has a
|
||||
bigger FPGA but is otherwise the same as the 2; the 3 has a bigger
|
||||
FPGA again, can handle 4GB of RAM and has a USB controller and QSPI flash).
|
||||
|
||||
Since the CPU itself and most of the devices are in the FPGA, the
|
||||
details of the board as seen by the guest depend significantly on the
|
||||
FPGA image.
|
||||
|
||||
QEMU models the following FPGA images:
|
||||
|
||||
@ -22,12 +25,21 @@ QEMU models the following FPGA images:
|
||||
Cortex-M3 'DesignStart' as documented in Arm Application Note AN511
|
||||
``mps2-an521``
|
||||
Dual Cortex-M33 as documented in Arm Application Note AN521
|
||||
``mps3-an524``
|
||||
Dual Cortex-M33 on an MPS3, as documented in Arm Application Note AN524
|
||||
|
||||
Differences between QEMU and real hardware:
|
||||
|
||||
- AN385/AN386 remapping of low 16K of memory to either ZBT SSRAM1 or to
|
||||
block RAM is unimplemented (QEMU always maps this to ZBT SSRAM1, as
|
||||
if zbt_boot_ctrl is always zero)
|
||||
- AN524 remapping of low memory to either BRAM or to QSPI flash is
|
||||
unimplemented (QEMU always maps this to BRAM, ignoring the
|
||||
SCC CFG_REG0 memory-remap bit)
|
||||
- QEMU provides a LAN9118 ethernet rather than LAN9220; the only guest
|
||||
visible difference is that the LAN9118 doesn't support checksum
|
||||
offloading
|
||||
- QEMU does not model the QSPI flash in MPS3 boards as real QSPI
|
||||
flash, but only as simple ROM, so attempting to rewrite the flash
|
||||
from the guest will fail
|
||||
- QEMU does not model the USB controller in MPS3 boards
|
||||
|
@ -44,6 +44,7 @@ Supported devices
|
||||
* Analog to Digital Converter (ADC)
|
||||
* Pulse Width Modulation (PWM)
|
||||
* SMBus controller (SMBF)
|
||||
* Ethernet controller (EMC)
|
||||
|
||||
Missing devices
|
||||
---------------
|
||||
@ -57,7 +58,7 @@ Missing devices
|
||||
* Shared memory (SHM)
|
||||
* eSPI slave interface
|
||||
|
||||
* Ethernet controllers (GMAC and EMC)
|
||||
* Ethernet controller (GMAC)
|
||||
* USB device (USBD)
|
||||
* Peripheral SPI controller (PSPI)
|
||||
* SD/MMC host
|
||||
|
642
hw/arm/mps2-tz.c
642
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);
|
||||
|
@ -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",
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
@ -589,6 +603,40 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp)
|
||||
sysbus_connect_irq(sbd, i, npcm7xx_irq(s, NPCM7XX_PWM0_IRQ + i));
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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
|
@ -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;
|
||||
|
||||
#define DEPTH 32
|
||||
#include "omap_lcd_template.h"
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
@ -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);
|
||||
int i;
|
||||
uint16_t *data_buffer;
|
||||
uint8_t *data_display;
|
||||
|
||||
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;
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -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
|
@ -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;
|
||||
|
@ -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 = {
|
||||
|
@ -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.
|
||||
|
@ -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"
|
||||
|
@ -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.
|
||||
*/
|
||||
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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(),
|
||||
};
|
||||
|
||||
|
@ -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(),
|
||||
};
|
||||
|
||||
|
@ -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'))
|
||||
|
857
hw/net/npcm7xx_emc.c
Normal file
857
hw/net/npcm7xx_emc.c
Normal file
@ -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)
|
@ -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"
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -14,9 +14,9 @@
|
||||
* hardware, which include the IoT Kit and the SSE-050, SSE-100 and
|
||||
* SSE-200. Currently we model:
|
||||
* - the Arm IoT Kit which is documented in
|
||||
* http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ecm0601256/index.html
|
||||
* https://developer.arm.com/documentation/ecm0601256/latest
|
||||
* - the SSE-200 which is 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/
|
||||
*
|
||||
* The IoTKit contains:
|
||||
* a Cortex-M33
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "hw/misc/npcm7xx_gcr.h"
|
||||
#include "hw/misc/npcm7xx_pwm.h"
|
||||
#include "hw/misc/npcm7xx_rng.h"
|
||||
#include "hw/net/npcm7xx_emc.h"
|
||||
#include "hw/nvram/npcm7xx_otp.h"
|
||||
#include "hw/timer/npcm7xx_timer.h"
|
||||
#include "hw/ssi/npcm7xx_fiu.h"
|
||||
@ -90,6 +91,7 @@ typedef struct NPCM7xxState {
|
||||
EHCISysBusState ehci;
|
||||
OHCISysBusState ohci;
|
||||
NPCM7xxFIUState fiu[2];
|
||||
NPCM7xxEMCState emc[2];
|
||||
} NPCM7xxState;
|
||||
|
||||
#define TYPE_NPCM7XX "npcm7xx"
|
||||
|
@ -115,8 +115,6 @@ struct XlnxZynqMPState {
|
||||
bool secure;
|
||||
/* Has the ARM Virtualization extensions? */
|
||||
bool virt;
|
||||
/* Has the RPU subsystem? */
|
||||
bool has_rpu;
|
||||
|
||||
/* CAN bus. */
|
||||
CanBusState *canbus[XLNX_ZYNQMP_NUM_CAN];
|
||||
|
@ -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/
|
||||
*
|
||||
* QEMU interface:
|
||||
* + QOM property "CPUID": the value to use for the CPUID register
|
||||
|
@ -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/
|
||||
*
|
||||
* QEMU interface:
|
||||
* + sysbus MMIO region 0: the system information register bank
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
/* This is a model of the security controller which is part of the
|
||||
* Arm IoT Kit and documented in
|
||||
* http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ecm0601256/index.html
|
||||
* https://developer.arm.com/documentation/ecm0601256/latest
|
||||
*
|
||||
* QEMU interface:
|
||||
* + sysbus MMIO region 0 is the "secure privilege control block" registers
|
||||
|
@ -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 information block" and
|
||||
* "system control register" blocks.
|
||||
*
|
||||
|
@ -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
|
||||
* QEMU interface:
|
||||
* + QOM property "SYS_VERSION": value to use for SYS_VERSION register
|
||||
* + QOM property "SYS_CONFIG": value to use for SYS_CONFIG register
|
||||
|
@ -12,7 +12,7 @@
|
||||
/* This is a model of the FPGAIO register block in the AN505
|
||||
* FPGA image for the MPS2 dev board; it is documented in the
|
||||
* application note:
|
||||
* http://infocenter.arm.com/help/topic/com.arm.doc.dai0505b/index.html
|
||||
* https://developer.arm.com/documentation/dai0505/latest/
|
||||
*
|
||||
* QEMU interface:
|
||||
* + sysbus MMIO region 0: the register bank
|
||||
@ -28,13 +28,17 @@
|
||||
#define TYPE_MPS2_FPGAIO "mps2-fpgaio"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(MPS2FPGAIO, MPS2_FPGAIO)
|
||||
|
||||
#define MPS2FPGAIO_MAX_LEDS 32
|
||||
|
||||
struct MPS2FPGAIO {
|
||||
/*< private >*/
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
/*< public >*/
|
||||
MemoryRegion iomem;
|
||||
LEDState *led[2];
|
||||
LEDState *led[MPS2FPGAIO_MAX_LEDS];
|
||||
uint32_t num_leds;
|
||||
bool has_switches;
|
||||
|
||||
uint32_t led0;
|
||||
uint32_t prescale;
|
||||
|
@ -19,8 +19,6 @@
|
||||
#define TYPE_MPS2_SCC "mps2-scc"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(MPS2SCC, MPS2_SCC)
|
||||
|
||||
#define NUM_OSCCLK 3
|
||||
|
||||
struct MPS2SCC {
|
||||
/*< private >*/
|
||||
SysBusDevice parent_obj;
|
||||
@ -31,7 +29,10 @@ struct MPS2SCC {
|
||||
|
||||
uint32_t cfg0;
|
||||
uint32_t cfg1;
|
||||
uint32_t cfg2;
|
||||
uint32_t cfg4;
|
||||
uint32_t cfg5;
|
||||
uint32_t cfg6;
|
||||
uint32_t cfgdata_rtn;
|
||||
uint32_t cfgdata_out;
|
||||
uint32_t cfgctrl;
|
||||
@ -39,8 +40,9 @@ struct MPS2SCC {
|
||||
uint32_t dll;
|
||||
uint32_t aid;
|
||||
uint32_t id;
|
||||
uint32_t oscclk[NUM_OSCCLK];
|
||||
uint32_t oscclk_reset[NUM_OSCCLK];
|
||||
uint32_t num_oscclk;
|
||||
uint32_t *oscclk;
|
||||
uint32_t *oscclk_reset;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
286
include/hw/net/npcm7xx_emc.h
Normal file
286
include/hw/net/npcm7xx_emc.h
Normal file
@ -0,0 +1,286 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef NPCM7XX_EMC_H
|
||||
#define NPCM7XX_EMC_H
|
||||
|
||||
#include "hw/irq.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "net/net.h"
|
||||
|
||||
/* 32-bit register indices. */
|
||||
enum NPCM7xxPWMRegister {
|
||||
/* Control registers. */
|
||||
REG_CAMCMR,
|
||||
REG_CAMEN,
|
||||
|
||||
/* There are 16 CAMn[ML] registers. */
|
||||
REG_CAMM_BASE,
|
||||
REG_CAML_BASE,
|
||||
REG_CAMML_LAST = 0x21,
|
||||
|
||||
REG_TXDLSA = 0x22,
|
||||
REG_RXDLSA,
|
||||
REG_MCMDR,
|
||||
REG_MIID,
|
||||
REG_MIIDA,
|
||||
REG_FFTCR,
|
||||
REG_TSDR,
|
||||
REG_RSDR,
|
||||
REG_DMARFC,
|
||||
REG_MIEN,
|
||||
|
||||
/* Status registers. */
|
||||
REG_MISTA,
|
||||
REG_MGSTA,
|
||||
REG_MPCNT,
|
||||
REG_MRPC,
|
||||
REG_MRPCC,
|
||||
REG_MREPC,
|
||||
REG_DMARFS,
|
||||
REG_CTXDSA,
|
||||
REG_CTXBSA,
|
||||
REG_CRXDSA,
|
||||
REG_CRXBSA,
|
||||
|
||||
NPCM7XX_NUM_EMC_REGS,
|
||||
};
|
||||
|
||||
/* REG_CAMCMR fields */
|
||||
/* Enable CAM Compare */
|
||||
#define REG_CAMCMR_ECMP (1 << 4)
|
||||
/* Complement CAM Compare */
|
||||
#define REG_CAMCMR_CCAM (1 << 3)
|
||||
/* Accept Broadcast Packet */
|
||||
#define REG_CAMCMR_ABP (1 << 2)
|
||||
/* Accept Multicast Packet */
|
||||
#define REG_CAMCMR_AMP (1 << 1)
|
||||
/* Accept Unicast Packet */
|
||||
#define REG_CAMCMR_AUP (1 << 0)
|
||||
|
||||
/* REG_MCMDR fields */
|
||||
/* Software Reset */
|
||||
#define REG_MCMDR_SWR (1 << 24)
|
||||
/* Internal Loopback Select */
|
||||
#define REG_MCMDR_LBK (1 << 21)
|
||||
/* Operation Mode Select */
|
||||
#define REG_MCMDR_OPMOD (1 << 20)
|
||||
/* Enable MDC Clock Generation */
|
||||
#define REG_MCMDR_ENMDC (1 << 19)
|
||||
/* Full-Duplex Mode Select */
|
||||
#define REG_MCMDR_FDUP (1 << 18)
|
||||
/* Enable SQE Checking */
|
||||
#define REG_MCMDR_ENSEQ (1 << 17)
|
||||
/* Send PAUSE Frame */
|
||||
#define REG_MCMDR_SDPZ (1 << 16)
|
||||
/* No Defer */
|
||||
#define REG_MCMDR_NDEF (1 << 9)
|
||||
/* Frame Transmission On */
|
||||
#define REG_MCMDR_TXON (1 << 8)
|
||||
/* Strip CRC Checksum */
|
||||
#define REG_MCMDR_SPCRC (1 << 5)
|
||||
/* Accept CRC Error Packet */
|
||||
#define REG_MCMDR_AEP (1 << 4)
|
||||
/* Accept Control Packet */
|
||||
#define REG_MCMDR_ACP (1 << 3)
|
||||
/* Accept Runt Packet */
|
||||
#define REG_MCMDR_ARP (1 << 2)
|
||||
/* Accept Long Packet */
|
||||
#define REG_MCMDR_ALP (1 << 1)
|
||||
/* Frame Reception On */
|
||||
#define REG_MCMDR_RXON (1 << 0)
|
||||
|
||||
/* REG_MIEN fields */
|
||||
/* Enable Transmit Descriptor Unavailable Interrupt */
|
||||
#define REG_MIEN_ENTDU (1 << 23)
|
||||
/* Enable Transmit Completion Interrupt */
|
||||
#define REG_MIEN_ENTXCP (1 << 18)
|
||||
/* Enable Transmit Interrupt */
|
||||
#define REG_MIEN_ENTXINTR (1 << 16)
|
||||
/* Enable Receive Descriptor Unavailable Interrupt */
|
||||
#define REG_MIEN_ENRDU (1 << 10)
|
||||
/* Enable Receive Good Interrupt */
|
||||
#define REG_MIEN_ENRXGD (1 << 4)
|
||||
/* Enable Receive Interrupt */
|
||||
#define REG_MIEN_ENRXINTR (1 << 0)
|
||||
|
||||
/* REG_MISTA fields */
|
||||
/* TODO: Add error fields and support simulated errors? */
|
||||
/* Transmit Bus Error Interrupt */
|
||||
#define REG_MISTA_TXBERR (1 << 24)
|
||||
/* Transmit Descriptor Unavailable Interrupt */
|
||||
#define REG_MISTA_TDU (1 << 23)
|
||||
/* Transmit Completion Interrupt */
|
||||
#define REG_MISTA_TXCP (1 << 18)
|
||||
/* Transmit Interrupt */
|
||||
#define REG_MISTA_TXINTR (1 << 16)
|
||||
/* Receive Bus Error Interrupt */
|
||||
#define REG_MISTA_RXBERR (1 << 11)
|
||||
/* Receive Descriptor Unavailable Interrupt */
|
||||
#define REG_MISTA_RDU (1 << 10)
|
||||
/* DMA Early Notification Interrupt */
|
||||
#define REG_MISTA_DENI (1 << 9)
|
||||
/* Maximum Frame Length Interrupt */
|
||||
#define REG_MISTA_DFOI (1 << 8)
|
||||
/* Receive Good Interrupt */
|
||||
#define REG_MISTA_RXGD (1 << 4)
|
||||
/* Packet Too Long Interrupt */
|
||||
#define REG_MISTA_PTLE (1 << 3)
|
||||
/* Receive Interrupt */
|
||||
#define REG_MISTA_RXINTR (1 << 0)
|
||||
|
||||
/* REG_MGSTA fields */
|
||||
/* Transmission Halted */
|
||||
#define REG_MGSTA_TXHA (1 << 11)
|
||||
/* Receive Halted */
|
||||
#define REG_MGSTA_RXHA (1 << 11)
|
||||
|
||||
/* REG_DMARFC fields */
|
||||
/* Maximum Receive Frame Length */
|
||||
#define REG_DMARFC_RXMS(word) extract32((word), 0, 16)
|
||||
|
||||
/* REG MIIDA fields */
|
||||
/* Busy Bit */
|
||||
#define REG_MIIDA_BUSY (1 << 17)
|
||||
|
||||
/* Transmit and receive descriptors */
|
||||
typedef struct NPCM7xxEMCTxDesc NPCM7xxEMCTxDesc;
|
||||
typedef struct NPCM7xxEMCRxDesc NPCM7xxEMCRxDesc;
|
||||
|
||||
struct NPCM7xxEMCTxDesc {
|
||||
uint32_t flags;
|
||||
uint32_t txbsa;
|
||||
uint32_t status_and_length;
|
||||
uint32_t ntxdsa;
|
||||
};
|
||||
|
||||
struct NPCM7xxEMCRxDesc {
|
||||
uint32_t status_and_length;
|
||||
uint32_t rxbsa;
|
||||
uint32_t reserved;
|
||||
uint32_t nrxdsa;
|
||||
};
|
||||
|
||||
/* NPCM7xxEMCTxDesc.flags values */
|
||||
/* Owner: 0 = cpu, 1 = emc */
|
||||
#define TX_DESC_FLAG_OWNER_MASK (1 << 31)
|
||||
/* Transmit interrupt enable */
|
||||
#define TX_DESC_FLAG_INTEN (1 << 2)
|
||||
/* CRC append */
|
||||
#define TX_DESC_FLAG_CRCAPP (1 << 1)
|
||||
/* Padding enable */
|
||||
#define TX_DESC_FLAG_PADEN (1 << 0)
|
||||
|
||||
/* NPCM7xxEMCTxDesc.status_and_length values */
|
||||
/* Collision count */
|
||||
#define TX_DESC_STATUS_CCNT_SHIFT 28
|
||||
#define TX_DESC_STATUS_CCNT_BITSIZE 4
|
||||
/* SQE error */
|
||||
#define TX_DESC_STATUS_SQE (1 << 26)
|
||||
/* Transmission paused */
|
||||
#define TX_DESC_STATUS_PAU (1 << 25)
|
||||
/* P transmission halted */
|
||||
#define TX_DESC_STATUS_TXHA (1 << 24)
|
||||
/* Late collision */
|
||||
#define TX_DESC_STATUS_LC (1 << 23)
|
||||
/* Transmission abort */
|
||||
#define TX_DESC_STATUS_TXABT (1 << 22)
|
||||
/* No carrier sense */
|
||||
#define TX_DESC_STATUS_NCS (1 << 21)
|
||||
/* Defer exceed */
|
||||
#define TX_DESC_STATUS_EXDEF (1 << 20)
|
||||
/* Transmission complete */
|
||||
#define TX_DESC_STATUS_TXCP (1 << 19)
|
||||
/* Transmission deferred */
|
||||
#define TX_DESC_STATUS_DEF (1 << 17)
|
||||
/* Transmit interrupt */
|
||||
#define TX_DESC_STATUS_TXINTR (1 << 16)
|
||||
|
||||
#define TX_DESC_PKT_LEN(word) extract32((word), 0, 16)
|
||||
|
||||
/* Transmit buffer start address */
|
||||
#define TX_DESC_TXBSA(word) ((uint32_t) (word) & ~3u)
|
||||
|
||||
/* Next transmit descriptor start address */
|
||||
#define TX_DESC_NTXDSA(word) ((uint32_t) (word) & ~3u)
|
||||
|
||||
/* NPCM7xxEMCRxDesc.status_and_length values */
|
||||
/* Owner: 0b00 = cpu, 0b01 = undefined, 0b10 = emc, 0b11 = undefined */
|
||||
#define RX_DESC_STATUS_OWNER_SHIFT 30
|
||||
#define RX_DESC_STATUS_OWNER_BITSIZE 2
|
||||
#define RX_DESC_STATUS_OWNER_MASK (3 << RX_DESC_STATUS_OWNER_SHIFT)
|
||||
/* Runt packet */
|
||||
#define RX_DESC_STATUS_RP (1 << 22)
|
||||
/* Alignment error */
|
||||
#define RX_DESC_STATUS_ALIE (1 << 21)
|
||||
/* Frame reception complete */
|
||||
#define RX_DESC_STATUS_RXGD (1 << 20)
|
||||
/* Packet too long */
|
||||
#define RX_DESC_STATUS_PTLE (1 << 19)
|
||||
/* CRC error */
|
||||
#define RX_DESC_STATUS_CRCE (1 << 17)
|
||||
/* Receive interrupt */
|
||||
#define RX_DESC_STATUS_RXINTR (1 << 16)
|
||||
|
||||
#define RX_DESC_PKT_LEN(word) extract32((word), 0, 16)
|
||||
|
||||
/* Receive buffer start address */
|
||||
#define RX_DESC_RXBSA(word) ((uint32_t) (word) & ~3u)
|
||||
|
||||
/* Next receive descriptor start address */
|
||||
#define RX_DESC_NRXDSA(word) ((uint32_t) (word) & ~3u)
|
||||
|
||||
/* Minimum packet length, when TX_DESC_FLAG_PADEN is set. */
|
||||
#define MIN_PACKET_LENGTH 64
|
||||
|
||||
struct NPCM7xxEMCState {
|
||||
/*< private >*/
|
||||
SysBusDevice parent;
|
||||
/*< public >*/
|
||||
|
||||
MemoryRegion iomem;
|
||||
|
||||
qemu_irq tx_irq;
|
||||
qemu_irq rx_irq;
|
||||
|
||||
NICState *nic;
|
||||
NICConf conf;
|
||||
|
||||
/* 0 or 1, for log messages */
|
||||
uint8_t emc_num;
|
||||
|
||||
uint32_t regs[NPCM7XX_NUM_EMC_REGS];
|
||||
|
||||
/*
|
||||
* tx is active. Set to true by TSDR and then switches off when out of
|
||||
* descriptors. If the TXON bit in REG_MCMDR is off then this is off.
|
||||
*/
|
||||
bool tx_active;
|
||||
|
||||
/*
|
||||
* rx is active. Set to true by RSDR and then switches off when out of
|
||||
* descriptors. If the RXON bit in REG_MCMDR is off then this is off.
|
||||
*/
|
||||
bool rx_active;
|
||||
};
|
||||
|
||||
typedef struct NPCM7xxEMCState NPCM7xxEMCState;
|
||||
|
||||
#define TYPE_NPCM7XX_EMC "npcm7xx-emc"
|
||||
#define NPCM7XX_EMC(obj) \
|
||||
OBJECT_CHECK(NPCM7xxEMCState, (obj), TYPE_NPCM7XX_EMC)
|
||||
|
||||
#endif /* NPCM7XX_EMC_H */
|
@ -1972,7 +1972,8 @@ static void cortex_a8_initfn(Object *obj)
|
||||
}
|
||||
|
||||
static const ARMCPRegInfo cortexa9_cp_reginfo[] = {
|
||||
/* power_control should be set to maximum latency. Again,
|
||||
/*
|
||||
* power_control should be set to maximum latency. Again,
|
||||
* default to 0 and set by private hook
|
||||
*/
|
||||
{ .name = "A9_PWRCTL", .cp = 15, .crn = 15, .crm = 0, .opc1 = 0, .opc2 = 0,
|
||||
@ -2009,7 +2010,8 @@ static void cortex_a9_initfn(Object *obj)
|
||||
set_feature(&cpu->env, ARM_FEATURE_NEON);
|
||||
set_feature(&cpu->env, ARM_FEATURE_THUMB2EE);
|
||||
set_feature(&cpu->env, ARM_FEATURE_EL3);
|
||||
/* Note that A9 supports the MP extensions even for
|
||||
/*
|
||||
* Note that A9 supports the MP extensions even for
|
||||
* A9UP and single-core A9MP (which are both different
|
||||
* and valid configurations; we don't model A9UP).
|
||||
*/
|
||||
@ -2046,7 +2048,8 @@ static uint64_t a15_l2ctlr_read(CPUARMState *env, const ARMCPRegInfo *ri)
|
||||
{
|
||||
MachineState *ms = MACHINE(qdev_get_machine());
|
||||
|
||||
/* Linux wants the number of processors from here.
|
||||
/*
|
||||
* Linux wants the number of processors from here.
|
||||
* Might as well set the interrupt-controller bit too.
|
||||
*/
|
||||
return ((ms->smp.cpus - 1) << 24) | (1 << 23);
|
||||
@ -2093,7 +2096,8 @@ static void cortex_a7_initfn(Object *obj)
|
||||
cpu->isar.id_mmfr1 = 0x40000000;
|
||||
cpu->isar.id_mmfr2 = 0x01240000;
|
||||
cpu->isar.id_mmfr3 = 0x02102211;
|
||||
/* a7_mpcore_r0p5_trm, page 4-4 gives 0x01101110; but
|
||||
/*
|
||||
* a7_mpcore_r0p5_trm, page 4-4 gives 0x01101110; but
|
||||
* table 4-41 gives 0x02101110, which includes the arm div insns.
|
||||
*/
|
||||
cpu->isar.id_isar0 = 0x02101110;
|
||||
@ -2217,6 +2221,10 @@ static void arm_max_initfn(Object *obj)
|
||||
t = cpu->isar.id_pfr0;
|
||||
t = FIELD_DP32(t, ID_PFR0, DIT, 1);
|
||||
cpu->isar.id_pfr0 = t;
|
||||
|
||||
t = cpu->isar.id_pfr2;
|
||||
t = FIELD_DP32(t, ID_PFR2, SSBS, 1);
|
||||
cpu->isar.id_pfr2 = t;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@ -2380,12 +2388,6 @@ static const TypeInfo arm_cpu_type_info = {
|
||||
.class_init = arm_cpu_class_init,
|
||||
};
|
||||
|
||||
static const TypeInfo idau_interface_type_info = {
|
||||
.name = TYPE_IDAU_INTERFACE,
|
||||
.parent = TYPE_INTERFACE,
|
||||
.class_size = sizeof(IDAUInterfaceClass),
|
||||
};
|
||||
|
||||
static void arm_cpu_register_types(void)
|
||||
{
|
||||
const size_t cpu_count = ARRAY_SIZE(arm_cpus);
|
||||
@ -2399,7 +2401,6 @@ static void arm_cpu_register_types(void)
|
||||
if (cpu_count) {
|
||||
size_t i;
|
||||
|
||||
type_register_static(&idau_interface_type_info);
|
||||
for (i = 0; i < cpu_count; ++i) {
|
||||
arm_cpu_register(&arm_cpus[i]);
|
||||
}
|
||||
|
@ -1206,6 +1206,7 @@ void pmu_init(ARMCPU *cpu);
|
||||
#define SCTLR_TE (1U << 30) /* AArch32 only */
|
||||
#define SCTLR_EnIB (1U << 30) /* v8.3, AArch64 only */
|
||||
#define SCTLR_EnIA (1U << 31) /* v8.3, AArch64 only */
|
||||
#define SCTLR_DSSBS_32 (1U << 31) /* v8.5, AArch32 only */
|
||||
#define SCTLR_BT0 (1ULL << 35) /* v8.5-BTI */
|
||||
#define SCTLR_BT1 (1ULL << 36) /* v8.5-BTI */
|
||||
#define SCTLR_ITFSB (1ULL << 37) /* v8.5-MemTag */
|
||||
@ -1213,7 +1214,7 @@ void pmu_init(ARMCPU *cpu);
|
||||
#define SCTLR_TCF (3ULL << 40) /* v8.5-MemTag */
|
||||
#define SCTLR_ATA0 (1ULL << 42) /* v8.5-MemTag */
|
||||
#define SCTLR_ATA (1ULL << 43) /* v8.5-MemTag */
|
||||
#define SCTLR_DSSBS (1ULL << 44) /* v8.5 */
|
||||
#define SCTLR_DSSBS_64 (1ULL << 44) /* v8.5, AArch64 only */
|
||||
|
||||
#define CPTR_TCPAC (1U << 31)
|
||||
#define CPTR_TTA (1U << 20)
|
||||
@ -1250,6 +1251,7 @@ void pmu_init(ARMCPU *cpu);
|
||||
#define CPSR_IL (1U << 20)
|
||||
#define CPSR_DIT (1U << 21)
|
||||
#define CPSR_PAN (1U << 22)
|
||||
#define CPSR_SSBS (1U << 23)
|
||||
#define CPSR_J (1U << 24)
|
||||
#define CPSR_IT_0_1 (3U << 25)
|
||||
#define CPSR_Q (1U << 27)
|
||||
@ -1312,6 +1314,7 @@ void pmu_init(ARMCPU *cpu);
|
||||
#define PSTATE_A (1U << 8)
|
||||
#define PSTATE_D (1U << 9)
|
||||
#define PSTATE_BTYPE (3U << 10)
|
||||
#define PSTATE_SSBS (1U << 12)
|
||||
#define PSTATE_IL (1U << 20)
|
||||
#define PSTATE_SS (1U << 21)
|
||||
#define PSTATE_PAN (1U << 22)
|
||||
@ -3915,6 +3918,11 @@ static inline bool isar_feature_aa32_dit(const ARMISARegisters *id)
|
||||
return FIELD_EX32(id->id_pfr0, ID_PFR0, DIT) != 0;
|
||||
}
|
||||
|
||||
static inline bool isar_feature_aa32_ssbs(const ARMISARegisters *id)
|
||||
{
|
||||
return FIELD_EX32(id->id_pfr2, ID_PFR2, SSBS) != 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 64-bit feature tests via id registers.
|
||||
*/
|
||||
@ -4169,6 +4177,11 @@ static inline bool isar_feature_aa64_dit(const ARMISARegisters *id)
|
||||
return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, DIT) != 0;
|
||||
}
|
||||
|
||||
static inline bool isar_feature_aa64_ssbs(const ARMISARegisters *id)
|
||||
{
|
||||
return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, SSBS) != 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Feature tests for "does this exist in either 32-bit or 64-bit?"
|
||||
*/
|
||||
|
@ -674,6 +674,7 @@ static void aarch64_max_initfn(Object *obj)
|
||||
|
||||
t = cpu->isar.id_aa64pfr1;
|
||||
t = FIELD_DP64(t, ID_AA64PFR1, BT, 1);
|
||||
t = FIELD_DP64(t, ID_AA64PFR1, SSBS, 2);
|
||||
/*
|
||||
* Begin with full support for MTE. This will be downgraded to MTE=0
|
||||
* during realize if the board provides no tag memory, much like
|
||||
@ -723,6 +724,10 @@ static void aarch64_max_initfn(Object *obj)
|
||||
u = FIELD_DP32(u, ID_PFR0, DIT, 1);
|
||||
cpu->isar.id_pfr0 = u;
|
||||
|
||||
u = cpu->isar.id_pfr2;
|
||||
u = FIELD_DP32(u, ID_PFR2, SSBS, 1);
|
||||
cpu->isar.id_pfr2 = u;
|
||||
|
||||
u = cpu->isar.id_mmfr3;
|
||||
u = FIELD_DP32(u, ID_MMFR3, PAN, 2); /* ATS1E1 */
|
||||
cpu->isar.id_mmfr3 = u;
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "hw/core/tcg-cpu-ops.h"
|
||||
#endif /* CONFIG_TCG */
|
||||
#include "internals.h"
|
||||
#include "target/arm/idau.h"
|
||||
|
||||
/* CPU models. These are not needed for the AArch64 linux-user build. */
|
||||
#if !defined(CONFIG_USER_ONLY) || !defined(TARGET_AARCH64)
|
||||
@ -739,10 +740,17 @@ static const ARMCPUInfo arm_tcg_cpus[] = {
|
||||
{ .name = "pxa270-c5", .initfn = pxa270c5_initfn },
|
||||
};
|
||||
|
||||
static const TypeInfo idau_interface_type_info = {
|
||||
.name = TYPE_IDAU_INTERFACE,
|
||||
.parent = TYPE_INTERFACE,
|
||||
.class_size = sizeof(IDAUInterfaceClass),
|
||||
};
|
||||
|
||||
static void arm_tcg_cpu_register_types(void)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
type_register_static(&idau_interface_type_info);
|
||||
for (i = 0; i < ARRAY_SIZE(arm_tcg_cpus); ++i) {
|
||||
arm_cpu_register(&arm_tcg_cpus[i]);
|
||||
}
|
||||
|
@ -179,38 +179,6 @@ float64 HELPER(vfp_mulxd)(float64 a, float64 b, void *fpstp)
|
||||
return float64_mul(a, b, fpst);
|
||||
}
|
||||
|
||||
uint64_t HELPER(simd_tbl)(CPUARMState *env, uint64_t result, uint64_t indices,
|
||||
uint32_t rn, uint32_t numregs)
|
||||
{
|
||||
/* Helper function for SIMD TBL and TBX. We have to do the table
|
||||
* lookup part for the 64 bits worth of indices we're passed in.
|
||||
* result is the initial results vector (either zeroes for TBL
|
||||
* or some guest values for TBX), rn the register number where
|
||||
* the table starts, and numregs the number of registers in the table.
|
||||
* We return the results of the lookups.
|
||||
*/
|
||||
int shift;
|
||||
|
||||
for (shift = 0; shift < 64; shift += 8) {
|
||||
int index = extract64(indices, shift, 8);
|
||||
if (index < 16 * numregs) {
|
||||
/* Convert index (a byte offset into the virtual table
|
||||
* which is a series of 128-bit vectors concatenated)
|
||||
* into the correct register element plus a bit offset
|
||||
* into that element, bearing in mind that the table
|
||||
* can wrap around from V31 to V0.
|
||||
*/
|
||||
int elt = (rn * 2 + (index >> 3)) % 64;
|
||||
int bitidx = (index & 7) * 8;
|
||||
uint64_t *q = aa64_vfp_qreg(env, elt >> 1);
|
||||
uint64_t val = extract64(q[elt & 1], bitidx, 8);
|
||||
|
||||
result = deposit64(result, shift, 8, val);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* 64bit/double versions of the neon float compare functions */
|
||||
uint64_t HELPER(neon_ceq_f64)(float64 a, float64 b, void *fpstp)
|
||||
{
|
||||
|
@ -28,7 +28,7 @@ DEF_HELPER_3(vfp_cmps_a64, i64, f32, f32, ptr)
|
||||
DEF_HELPER_3(vfp_cmpes_a64, i64, f32, f32, ptr)
|
||||
DEF_HELPER_3(vfp_cmpd_a64, i64, f64, f64, ptr)
|
||||
DEF_HELPER_3(vfp_cmped_a64, i64, f64, f64, ptr)
|
||||
DEF_HELPER_FLAGS_5(simd_tbl, TCG_CALL_NO_RWG_SE, i64, env, i64, i64, i32, i32)
|
||||
DEF_HELPER_FLAGS_4(simd_tblx, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_3(vfp_mulxs, TCG_CALL_NO_RWG, f32, f32, f32, ptr)
|
||||
DEF_HELPER_FLAGS_3(vfp_mulxd, TCG_CALL_NO_RWG, f64, f64, f64, ptr)
|
||||
DEF_HELPER_FLAGS_3(neon_ceq_f64, TCG_CALL_NO_RWG, i64, i64, i64, ptr)
|
||||
|
@ -4450,6 +4450,24 @@ static const ARMCPRegInfo dit_reginfo = {
|
||||
.readfn = aa64_dit_read, .writefn = aa64_dit_write
|
||||
};
|
||||
|
||||
static uint64_t aa64_ssbs_read(CPUARMState *env, const ARMCPRegInfo *ri)
|
||||
{
|
||||
return env->pstate & PSTATE_SSBS;
|
||||
}
|
||||
|
||||
static void aa64_ssbs_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
uint64_t value)
|
||||
{
|
||||
env->pstate = (env->pstate & ~PSTATE_SSBS) | (value & PSTATE_SSBS);
|
||||
}
|
||||
|
||||
static const ARMCPRegInfo ssbs_reginfo = {
|
||||
.name = "SSBS", .state = ARM_CP_STATE_AA64,
|
||||
.opc0 = 3, .opc1 = 3, .crn = 4, .crm = 2, .opc2 = 6,
|
||||
.type = ARM_CP_NO_RAW, .access = PL0_RW,
|
||||
.readfn = aa64_ssbs_read, .writefn = aa64_ssbs_write
|
||||
};
|
||||
|
||||
static CPAccessResult aa64_cacheop_poc_access(CPUARMState *env,
|
||||
const ARMCPRegInfo *ri,
|
||||
bool isread)
|
||||
@ -8244,6 +8262,9 @@ void register_cp_regs_for_features(ARMCPU *cpu)
|
||||
if (cpu_isar_feature(aa64_dit, cpu)) {
|
||||
define_one_arm_cp_reg(cpu, &dit_reginfo);
|
||||
}
|
||||
if (cpu_isar_feature(aa64_ssbs, cpu)) {
|
||||
define_one_arm_cp_reg(cpu, &ssbs_reginfo);
|
||||
}
|
||||
|
||||
if (arm_feature(env, ARM_FEATURE_EL2) && cpu_isar_feature(aa64_vh, cpu)) {
|
||||
define_arm_cp_regs(cpu, vhe_reginfo);
|
||||
@ -9463,6 +9484,14 @@ static void take_aarch32_exception(CPUARMState *env, int new_mode,
|
||||
env->uncached_cpsr &= ~(CPSR_IL | CPSR_J);
|
||||
env->daif |= mask;
|
||||
|
||||
if (cpu_isar_feature(aa32_ssbs, env_archcpu(env))) {
|
||||
if (env->cp15.sctlr_el[new_el] & SCTLR_DSSBS_32) {
|
||||
env->uncached_cpsr |= CPSR_SSBS;
|
||||
} else {
|
||||
env->uncached_cpsr &= ~CPSR_SSBS;
|
||||
}
|
||||
}
|
||||
|
||||
if (new_mode == ARM_CPU_MODE_HYP) {
|
||||
env->thumb = (env->cp15.sctlr_el[2] & SCTLR_TE) != 0;
|
||||
env->elr_el[2] = env->regs[15];
|
||||
@ -9973,6 +10002,14 @@ static void arm_cpu_do_interrupt_aarch64(CPUState *cs)
|
||||
new_mode |= PSTATE_TCO;
|
||||
}
|
||||
|
||||
if (cpu_isar_feature(aa64_ssbs, cpu)) {
|
||||
if (env->cp15.sctlr_el[new_el] & SCTLR_DSSBS_64) {
|
||||
new_mode |= PSTATE_SSBS;
|
||||
} else {
|
||||
new_mode &= ~PSTATE_SSBS;
|
||||
}
|
||||
}
|
||||
|
||||
pstate_write(env, PSTATE_DAIF | new_mode);
|
||||
env->aarch64 = 1;
|
||||
aarch64_restore_sp(env, new_el);
|
||||
@ -13133,7 +13170,7 @@ static uint32_t rebuild_hflags_a64(CPUARMState *env, int el, int fp_el,
|
||||
if (FIELD_EX32(flags, TBFLAG_A64, UNPRIV)
|
||||
&& tbid
|
||||
&& !(env->pstate & PSTATE_TCO)
|
||||
&& (sctlr & SCTLR_TCF)
|
||||
&& (sctlr & SCTLR_TCF0)
|
||||
&& allocation_tag_access_enabled(env, 0, sctlr)) {
|
||||
flags = FIELD_DP32(flags, TBFLAG_A64, MTE0_ACTIVE, 1);
|
||||
}
|
||||
|
@ -987,6 +987,9 @@ static inline uint32_t aarch32_cpsr_valid_mask(uint64_t features,
|
||||
if (isar_feature_aa32_dit(id)) {
|
||||
valid |= CPSR_DIT;
|
||||
}
|
||||
if (isar_feature_aa32_ssbs(id)) {
|
||||
valid |= CPSR_SSBS;
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
@ -1008,6 +1011,9 @@ static inline uint32_t aarch64_pstate_valid_mask(const ARMISARegisters *id)
|
||||
if (isar_feature_aa64_dit(id)) {
|
||||
valid |= PSTATE_DIT;
|
||||
}
|
||||
if (isar_feature_aa64_ssbs(id)) {
|
||||
valid |= PSTATE_SSBS;
|
||||
}
|
||||
if (isar_feature_aa64_mte(id)) {
|
||||
valid |= PSTATE_TCO;
|
||||
}
|
||||
|
@ -550,10 +550,14 @@ static void mte_check_fail(CPUARMState *env, uint32_t desc,
|
||||
reg_el = regime_el(env, arm_mmu_idx);
|
||||
sctlr = env->cp15.sctlr_el[reg_el];
|
||||
|
||||
el = arm_current_el(env);
|
||||
if (el == 0) {
|
||||
switch (arm_mmu_idx) {
|
||||
case ARMMMUIdx_E10_0:
|
||||
case ARMMMUIdx_E20_0:
|
||||
el = 0;
|
||||
tcf = extract64(sctlr, 38, 2);
|
||||
} else {
|
||||
break;
|
||||
default:
|
||||
el = reg_el;
|
||||
tcf = extract64(sctlr, 40, 2);
|
||||
}
|
||||
|
||||
@ -570,7 +574,8 @@ static void mte_check_fail(CPUARMState *env, uint32_t desc,
|
||||
env->exception.vaddress = dirty_ptr;
|
||||
|
||||
is_write = FIELD_EX32(desc, MTEDESC, WRITE);
|
||||
syn = syn_data_abort_no_iss(el != 0, 0, 0, 0, 0, is_write, 0x11);
|
||||
syn = syn_data_abort_no_iss(arm_current_el(env) != 0, 0, 0, 0, 0,
|
||||
is_write, 0x11);
|
||||
raise_exception(env, EXCP_DATA_ABORT, syn, exception_target_el(env));
|
||||
/* noreturn, but fall through to the assert anyway */
|
||||
|
||||
|
@ -1703,6 +1703,18 @@ static void handle_msr_i(DisasContext *s, uint32_t insn,
|
||||
tcg_temp_free_i32(t1);
|
||||
break;
|
||||
|
||||
case 0x19: /* SSBS */
|
||||
if (!dc_isar_feature(aa64_ssbs, s)) {
|
||||
goto do_unallocated;
|
||||
}
|
||||
if (crm & 1) {
|
||||
set_pstate_bits(PSTATE_SSBS);
|
||||
} else {
|
||||
clear_pstate_bits(PSTATE_SSBS);
|
||||
}
|
||||
/* Don't need to rebuild hflags since SSBS is a nop */
|
||||
break;
|
||||
|
||||
case 0x1a: /* DIT */
|
||||
if (!dc_isar_feature(aa64_dit, s)) {
|
||||
goto do_unallocated;
|
||||
@ -7520,10 +7532,8 @@ static void disas_simd_tb(DisasContext *s, uint32_t insn)
|
||||
int rm = extract32(insn, 16, 5);
|
||||
int rn = extract32(insn, 5, 5);
|
||||
int rd = extract32(insn, 0, 5);
|
||||
int is_tblx = extract32(insn, 12, 1);
|
||||
int len = extract32(insn, 13, 2);
|
||||
TCGv_i64 tcg_resl, tcg_resh, tcg_idx;
|
||||
TCGv_i32 tcg_regno, tcg_numregs;
|
||||
int is_tbx = extract32(insn, 12, 1);
|
||||
int len = (extract32(insn, 13, 2) + 1) * 16;
|
||||
|
||||
if (op2 != 0) {
|
||||
unallocated_encoding(s);
|
||||
@ -7534,53 +7544,11 @@ static void disas_simd_tb(DisasContext *s, uint32_t insn)
|
||||
return;
|
||||
}
|
||||
|
||||
/* This does a table lookup: for every byte element in the input
|
||||
* we index into a table formed from up to four vector registers,
|
||||
* and then the output is the result of the lookups. Our helper
|
||||
* function does the lookup operation for a single 64 bit part of
|
||||
* the input.
|
||||
*/
|
||||
tcg_resl = tcg_temp_new_i64();
|
||||
tcg_resh = NULL;
|
||||
|
||||
if (is_tblx) {
|
||||
read_vec_element(s, tcg_resl, rd, 0, MO_64);
|
||||
} else {
|
||||
tcg_gen_movi_i64(tcg_resl, 0);
|
||||
}
|
||||
|
||||
if (is_q) {
|
||||
tcg_resh = tcg_temp_new_i64();
|
||||
if (is_tblx) {
|
||||
read_vec_element(s, tcg_resh, rd, 1, MO_64);
|
||||
} else {
|
||||
tcg_gen_movi_i64(tcg_resh, 0);
|
||||
}
|
||||
}
|
||||
|
||||
tcg_idx = tcg_temp_new_i64();
|
||||
tcg_regno = tcg_const_i32(rn);
|
||||
tcg_numregs = tcg_const_i32(len + 1);
|
||||
read_vec_element(s, tcg_idx, rm, 0, MO_64);
|
||||
gen_helper_simd_tbl(tcg_resl, cpu_env, tcg_resl, tcg_idx,
|
||||
tcg_regno, tcg_numregs);
|
||||
if (is_q) {
|
||||
read_vec_element(s, tcg_idx, rm, 1, MO_64);
|
||||
gen_helper_simd_tbl(tcg_resh, cpu_env, tcg_resh, tcg_idx,
|
||||
tcg_regno, tcg_numregs);
|
||||
}
|
||||
tcg_temp_free_i64(tcg_idx);
|
||||
tcg_temp_free_i32(tcg_regno);
|
||||
tcg_temp_free_i32(tcg_numregs);
|
||||
|
||||
write_vec_element(s, tcg_resl, rd, 0, MO_64);
|
||||
tcg_temp_free_i64(tcg_resl);
|
||||
|
||||
if (is_q) {
|
||||
write_vec_element(s, tcg_resh, rd, 1, MO_64);
|
||||
tcg_temp_free_i64(tcg_resh);
|
||||
}
|
||||
clear_vec_high(s, is_q, rd);
|
||||
tcg_gen_gvec_2_ptr(vec_full_reg_offset(s, rd),
|
||||
vec_full_reg_offset(s, rm), cpu_env,
|
||||
is_q ? 16 : 8, vec_full_reg_size(s),
|
||||
(len << 6) | (is_tbx << 5) | rn,
|
||||
gen_helper_simd_tblx);
|
||||
}
|
||||
|
||||
/* ZIP/UZP/TRN
|
||||
|
@ -1937,3 +1937,51 @@ DO_VRINT_RMODE(gvec_vrint_rm_h, helper_rinth, uint16_t)
|
||||
DO_VRINT_RMODE(gvec_vrint_rm_s, helper_rints, uint32_t)
|
||||
|
||||
#undef DO_VRINT_RMODE
|
||||
|
||||
#ifdef TARGET_AARCH64
|
||||
void HELPER(simd_tblx)(void *vd, void *vm, void *venv, uint32_t desc)
|
||||
{
|
||||
const uint8_t *indices = vm;
|
||||
CPUARMState *env = venv;
|
||||
size_t oprsz = simd_oprsz(desc);
|
||||
uint32_t rn = extract32(desc, SIMD_DATA_SHIFT, 5);
|
||||
bool is_tbx = extract32(desc, SIMD_DATA_SHIFT + 5, 1);
|
||||
uint32_t table_len = desc >> (SIMD_DATA_SHIFT + 6);
|
||||
union {
|
||||
uint8_t b[16];
|
||||
uint64_t d[2];
|
||||
} result;
|
||||
|
||||
/*
|
||||
* We must construct the final result in a temp, lest the output
|
||||
* overlaps the input table. For TBL, begin with zero; for TBX,
|
||||
* begin with the original register contents. Note that we always
|
||||
* copy 16 bytes here to avoid an extra branch; clearing the high
|
||||
* bits of the register for oprsz == 8 is handled below.
|
||||
*/
|
||||
if (is_tbx) {
|
||||
memcpy(&result, vd, 16);
|
||||
} else {
|
||||
memset(&result, 0, 16);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < oprsz; ++i) {
|
||||
uint32_t index = indices[H1(i)];
|
||||
|
||||
if (index < table_len) {
|
||||
/*
|
||||
* Convert index (a byte offset into the virtual table
|
||||
* which is a series of 128-bit vectors concatenated)
|
||||
* into the correct register element, bearing in mind
|
||||
* that the table can wrap around from V31 to V0.
|
||||
*/
|
||||
const uint8_t *table = (const uint8_t *)
|
||||
aa64_vfp_qreg(env, (rn + (index >> 4)) % 32);
|
||||
result.b[H1(i)] = table[H1(index % 16)];
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(vd, &result, 16);
|
||||
clear_tail(vd, oprsz, simd_maxsz(desc));
|
||||
}
|
||||
#endif
|
||||
|
@ -141,7 +141,8 @@ qtests_npcm7xx = \
|
||||
'npcm7xx_rng-test',
|
||||
'npcm7xx_smbus-test',
|
||||
'npcm7xx_timer-test',
|
||||
'npcm7xx_watchdog_timer-test']
|
||||
'npcm7xx_watchdog_timer-test'] + \
|
||||
(slirp.found() ? ['npcm7xx_emc-test'] : [])
|
||||
qtests_arm = \
|
||||
(config_all_devices.has_key('CONFIG_CMSDK_APB_DUALTIMER') ? ['cmsdk-apb-dualtimer-test'] : []) + \
|
||||
(config_all_devices.has_key('CONFIG_CMSDK_APB_TIMER') ? ['cmsdk-apb-timer-test'] : []) + \
|
||||
|
862
tests/qtest/npcm7xx_emc-test.c
Normal file
862
tests/qtest/npcm7xx_emc-test.c
Normal file
@ -0,0 +1,862 @@
|
||||
/*
|
||||
* QTests for Nuvoton NPCM7xx EMC Modules.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "libqos/libqos.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/qmp/qnum.h"
|
||||
#include "qemu/bitops.h"
|
||||
#include "qemu/iov.h"
|
||||
|
||||
/* Name of the emc device. */
|
||||
#define TYPE_NPCM7XX_EMC "npcm7xx-emc"
|
||||
|
||||
/* Timeout for various operations, in seconds. */
|
||||
#define TIMEOUT_SECONDS 10
|
||||
|
||||
/* Address in memory of the descriptor. */
|
||||
#define DESC_ADDR (1 << 20) /* 1 MiB */
|
||||
|
||||
/* Address in memory of the data packet. */
|
||||
#define DATA_ADDR (DESC_ADDR + 4096)
|
||||
|
||||
#define CRC_LENGTH 4
|
||||
|
||||
#define NUM_TX_DESCRIPTORS 3
|
||||
#define NUM_RX_DESCRIPTORS 2
|
||||
|
||||
/* Size of tx,rx test buffers. */
|
||||
#define TX_DATA_LEN 64
|
||||
#define RX_DATA_LEN 64
|
||||
|
||||
#define TX_STEP_COUNT 10000
|
||||
#define RX_STEP_COUNT 10000
|
||||
|
||||
/* 32-bit register indices. */
|
||||
typedef enum NPCM7xxPWMRegister {
|
||||
/* Control registers. */
|
||||
REG_CAMCMR,
|
||||
REG_CAMEN,
|
||||
|
||||
/* There are 16 CAMn[ML] registers. */
|
||||
REG_CAMM_BASE,
|
||||
REG_CAML_BASE,
|
||||
|
||||
REG_TXDLSA = 0x22,
|
||||
REG_RXDLSA,
|
||||
REG_MCMDR,
|
||||
REG_MIID,
|
||||
REG_MIIDA,
|
||||
REG_FFTCR,
|
||||
REG_TSDR,
|
||||
REG_RSDR,
|
||||
REG_DMARFC,
|
||||
REG_MIEN,
|
||||
|
||||
/* Status registers. */
|
||||
REG_MISTA,
|
||||
REG_MGSTA,
|
||||
REG_MPCNT,
|
||||
REG_MRPC,
|
||||
REG_MRPCC,
|
||||
REG_MREPC,
|
||||
REG_DMARFS,
|
||||
REG_CTXDSA,
|
||||
REG_CTXBSA,
|
||||
REG_CRXDSA,
|
||||
REG_CRXBSA,
|
||||
|
||||
NPCM7XX_NUM_EMC_REGS,
|
||||
} NPCM7xxPWMRegister;
|
||||
|
||||
enum { NUM_CAMML_REGS = 16 };
|
||||
|
||||
/* REG_CAMCMR fields */
|
||||
/* Enable CAM Compare */
|
||||
#define REG_CAMCMR_ECMP (1 << 4)
|
||||
/* Accept Unicast Packet */
|
||||
#define REG_CAMCMR_AUP (1 << 0)
|
||||
|
||||
/* REG_MCMDR fields */
|
||||
/* Software Reset */
|
||||
#define REG_MCMDR_SWR (1 << 24)
|
||||
/* Frame Transmission On */
|
||||
#define REG_MCMDR_TXON (1 << 8)
|
||||
/* Accept Long Packet */
|
||||
#define REG_MCMDR_ALP (1 << 1)
|
||||
/* Frame Reception On */
|
||||
#define REG_MCMDR_RXON (1 << 0)
|
||||
|
||||
/* REG_MIEN fields */
|
||||
/* Enable Transmit Completion Interrupt */
|
||||
#define REG_MIEN_ENTXCP (1 << 18)
|
||||
/* Enable Transmit Interrupt */
|
||||
#define REG_MIEN_ENTXINTR (1 << 16)
|
||||
/* Enable Receive Good Interrupt */
|
||||
#define REG_MIEN_ENRXGD (1 << 4)
|
||||
/* ENable Receive Interrupt */
|
||||
#define REG_MIEN_ENRXINTR (1 << 0)
|
||||
|
||||
/* REG_MISTA fields */
|
||||
/* Transmit Bus Error Interrupt */
|
||||
#define REG_MISTA_TXBERR (1 << 24)
|
||||
/* Transmit Descriptor Unavailable Interrupt */
|
||||
#define REG_MISTA_TDU (1 << 23)
|
||||
/* Transmit Completion Interrupt */
|
||||
#define REG_MISTA_TXCP (1 << 18)
|
||||
/* Transmit Interrupt */
|
||||
#define REG_MISTA_TXINTR (1 << 16)
|
||||
/* Receive Bus Error Interrupt */
|
||||
#define REG_MISTA_RXBERR (1 << 11)
|
||||
/* Receive Descriptor Unavailable Interrupt */
|
||||
#define REG_MISTA_RDU (1 << 10)
|
||||
/* DMA Early Notification Interrupt */
|
||||
#define REG_MISTA_DENI (1 << 9)
|
||||
/* Maximum Frame Length Interrupt */
|
||||
#define REG_MISTA_DFOI (1 << 8)
|
||||
/* Receive Good Interrupt */
|
||||
#define REG_MISTA_RXGD (1 << 4)
|
||||
/* Packet Too Long Interrupt */
|
||||
#define REG_MISTA_PTLE (1 << 3)
|
||||
/* Receive Interrupt */
|
||||
#define REG_MISTA_RXINTR (1 << 0)
|
||||
|
||||
typedef struct NPCM7xxEMCTxDesc NPCM7xxEMCTxDesc;
|
||||
typedef struct NPCM7xxEMCRxDesc NPCM7xxEMCRxDesc;
|
||||
|
||||
struct NPCM7xxEMCTxDesc {
|
||||
uint32_t flags;
|
||||
uint32_t txbsa;
|
||||
uint32_t status_and_length;
|
||||
uint32_t ntxdsa;
|
||||
};
|
||||
|
||||
struct NPCM7xxEMCRxDesc {
|
||||
uint32_t status_and_length;
|
||||
uint32_t rxbsa;
|
||||
uint32_t reserved;
|
||||
uint32_t nrxdsa;
|
||||
};
|
||||
|
||||
/* NPCM7xxEMCTxDesc.flags values */
|
||||
/* Owner: 0 = cpu, 1 = emc */
|
||||
#define TX_DESC_FLAG_OWNER_MASK (1 << 31)
|
||||
/* Transmit interrupt enable */
|
||||
#define TX_DESC_FLAG_INTEN (1 << 2)
|
||||
|
||||
/* NPCM7xxEMCTxDesc.status_and_length values */
|
||||
/* Transmission complete */
|
||||
#define TX_DESC_STATUS_TXCP (1 << 19)
|
||||
/* Transmit interrupt */
|
||||
#define TX_DESC_STATUS_TXINTR (1 << 16)
|
||||
|
||||
/* NPCM7xxEMCRxDesc.status_and_length values */
|
||||
/* Owner: 0b00 = cpu, 0b10 = emc */
|
||||
#define RX_DESC_STATUS_OWNER_SHIFT 30
|
||||
#define RX_DESC_STATUS_OWNER_MASK 0xc0000000
|
||||
/* Frame Reception Complete */
|
||||
#define RX_DESC_STATUS_RXGD (1 << 20)
|
||||
/* Packet too long */
|
||||
#define RX_DESC_STATUS_PTLE (1 << 19)
|
||||
/* Receive Interrupt */
|
||||
#define RX_DESC_STATUS_RXINTR (1 << 16)
|
||||
|
||||
#define RX_DESC_PKT_LEN(word) ((uint32_t) (word) & 0xffff)
|
||||
|
||||
typedef struct EMCModule {
|
||||
int rx_irq;
|
||||
int tx_irq;
|
||||
uint64_t base_addr;
|
||||
} EMCModule;
|
||||
|
||||
typedef struct TestData {
|
||||
const EMCModule *module;
|
||||
} TestData;
|
||||
|
||||
static const EMCModule emc_module_list[] = {
|
||||
{
|
||||
.rx_irq = 15,
|
||||
.tx_irq = 16,
|
||||
.base_addr = 0xf0825000
|
||||
},
|
||||
{
|
||||
.rx_irq = 114,
|
||||
.tx_irq = 115,
|
||||
.base_addr = 0xf0826000
|
||||
}
|
||||
};
|
||||
|
||||
/* Returns the index of the EMC module. */
|
||||
static int emc_module_index(const EMCModule *mod)
|
||||
{
|
||||
ptrdiff_t diff = mod - emc_module_list;
|
||||
|
||||
g_assert_true(diff >= 0 && diff < ARRAY_SIZE(emc_module_list));
|
||||
|
||||
return diff;
|
||||
}
|
||||
|
||||
static void packet_test_clear(void *sockets)
|
||||
{
|
||||
int *test_sockets = sockets;
|
||||
|
||||
close(test_sockets[0]);
|
||||
g_free(test_sockets);
|
||||
}
|
||||
|
||||
static int *packet_test_init(int module_num, GString *cmd_line)
|
||||
{
|
||||
int *test_sockets = g_new(int, 2);
|
||||
int ret = socketpair(PF_UNIX, SOCK_STREAM, 0, test_sockets);
|
||||
g_assert_cmpint(ret, != , -1);
|
||||
|
||||
/*
|
||||
* KISS and use -nic. We specify two nics (both emc{0,1}) because there's
|
||||
* currently no way to specify only emc1: The driver implicitly relies on
|
||||
* emc[i] == nd_table[i].
|
||||
*/
|
||||
if (module_num == 0) {
|
||||
g_string_append_printf(cmd_line,
|
||||
" -nic socket,fd=%d,model=" TYPE_NPCM7XX_EMC " "
|
||||
" -nic user,model=" TYPE_NPCM7XX_EMC " ",
|
||||
test_sockets[1]);
|
||||
} else {
|
||||
g_string_append_printf(cmd_line,
|
||||
" -nic user,model=" TYPE_NPCM7XX_EMC " "
|
||||
" -nic socket,fd=%d,model=" TYPE_NPCM7XX_EMC " ",
|
||||
test_sockets[1]);
|
||||
}
|
||||
|
||||
g_test_queue_destroy(packet_test_clear, test_sockets);
|
||||
return test_sockets;
|
||||
}
|
||||
|
||||
static uint32_t emc_read(QTestState *qts, const EMCModule *mod,
|
||||
NPCM7xxPWMRegister regno)
|
||||
{
|
||||
return qtest_readl(qts, mod->base_addr + regno * sizeof(uint32_t));
|
||||
}
|
||||
|
||||
static void emc_write(QTestState *qts, const EMCModule *mod,
|
||||
NPCM7xxPWMRegister regno, uint32_t value)
|
||||
{
|
||||
qtest_writel(qts, mod->base_addr + regno * sizeof(uint32_t), value);
|
||||
}
|
||||
|
||||
static void emc_read_tx_desc(QTestState *qts, uint32_t addr,
|
||||
NPCM7xxEMCTxDesc *desc)
|
||||
{
|
||||
qtest_memread(qts, addr, desc, sizeof(*desc));
|
||||
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);
|
||||
}
|
||||
|
||||
static void emc_write_tx_desc(QTestState *qts, const NPCM7xxEMCTxDesc *desc,
|
||||
uint32_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);
|
||||
qtest_memwrite(qts, addr, &le_desc, sizeof(le_desc));
|
||||
}
|
||||
|
||||
static void emc_read_rx_desc(QTestState *qts, uint32_t addr,
|
||||
NPCM7xxEMCRxDesc *desc)
|
||||
{
|
||||
qtest_memread(qts, addr, desc, sizeof(*desc));
|
||||
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);
|
||||
}
|
||||
|
||||
static void emc_write_rx_desc(QTestState *qts, const NPCM7xxEMCRxDesc *desc,
|
||||
uint32_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);
|
||||
qtest_memwrite(qts, addr, &le_desc, sizeof(le_desc));
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset the EMC module.
|
||||
* The module must be reset before, e.g., TXDLSA,RXDLSA are changed.
|
||||
*/
|
||||
static bool emc_soft_reset(QTestState *qts, const EMCModule *mod)
|
||||
{
|
||||
uint32_t val;
|
||||
uint64_t end_time;
|
||||
|
||||
emc_write(qts, mod, REG_MCMDR, REG_MCMDR_SWR);
|
||||
|
||||
/*
|
||||
* Wait for device to reset as the linux driver does.
|
||||
* During reset the AHB reads 0 for all registers. So first wait for
|
||||
* something that resets to non-zero, and then wait for SWR becoming 0.
|
||||
*/
|
||||
end_time = g_get_monotonic_time() + TIMEOUT_SECONDS * G_TIME_SPAN_SECOND;
|
||||
|
||||
do {
|
||||
qtest_clock_step(qts, 100);
|
||||
val = emc_read(qts, mod, REG_FFTCR);
|
||||
} while (val == 0 && g_get_monotonic_time() < end_time);
|
||||
if (val != 0) {
|
||||
do {
|
||||
qtest_clock_step(qts, 100);
|
||||
val = emc_read(qts, mod, REG_MCMDR);
|
||||
if ((val & REG_MCMDR_SWR) == 0) {
|
||||
/*
|
||||
* N.B. The CAMs have been reset here, so macaddr matching of
|
||||
* incoming packets will not work.
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
} while (g_get_monotonic_time() < end_time);
|
||||
}
|
||||
|
||||
g_message("%s: Timeout expired", __func__);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check emc registers are reset to default value. */
|
||||
static void test_init(gconstpointer test_data)
|
||||
{
|
||||
const TestData *td = test_data;
|
||||
const EMCModule *mod = td->module;
|
||||
QTestState *qts = qtest_init("-machine quanta-gsj");
|
||||
int i;
|
||||
|
||||
#define CHECK_REG(regno, value) \
|
||||
do { \
|
||||
g_assert_cmphex(emc_read(qts, mod, (regno)), ==, (value)); \
|
||||
} while (0)
|
||||
|
||||
CHECK_REG(REG_CAMCMR, 0);
|
||||
CHECK_REG(REG_CAMEN, 0);
|
||||
CHECK_REG(REG_TXDLSA, 0xfffffffc);
|
||||
CHECK_REG(REG_RXDLSA, 0xfffffffc);
|
||||
CHECK_REG(REG_MCMDR, 0);
|
||||
CHECK_REG(REG_MIID, 0);
|
||||
CHECK_REG(REG_MIIDA, 0x00900000);
|
||||
CHECK_REG(REG_FFTCR, 0x0101);
|
||||
CHECK_REG(REG_DMARFC, 0x0800);
|
||||
CHECK_REG(REG_MIEN, 0);
|
||||
CHECK_REG(REG_MISTA, 0);
|
||||
CHECK_REG(REG_MGSTA, 0);
|
||||
CHECK_REG(REG_MPCNT, 0x7fff);
|
||||
CHECK_REG(REG_MRPC, 0);
|
||||
CHECK_REG(REG_MRPCC, 0);
|
||||
CHECK_REG(REG_MREPC, 0);
|
||||
CHECK_REG(REG_DMARFS, 0);
|
||||
CHECK_REG(REG_CTXDSA, 0);
|
||||
CHECK_REG(REG_CTXBSA, 0);
|
||||
CHECK_REG(REG_CRXDSA, 0);
|
||||
CHECK_REG(REG_CRXBSA, 0);
|
||||
|
||||
#undef CHECK_REG
|
||||
|
||||
for (i = 0; i < NUM_CAMML_REGS; ++i) {
|
||||
g_assert_cmpuint(emc_read(qts, mod, REG_CAMM_BASE + i * 2), ==,
|
||||
0);
|
||||
g_assert_cmpuint(emc_read(qts, mod, REG_CAML_BASE + i * 2), ==,
|
||||
0);
|
||||
}
|
||||
|
||||
qtest_quit(qts);
|
||||
}
|
||||
|
||||
static bool emc_wait_irq(QTestState *qts, const EMCModule *mod, int step,
|
||||
bool is_tx)
|
||||
{
|
||||
uint64_t end_time =
|
||||
g_get_monotonic_time() + TIMEOUT_SECONDS * G_TIME_SPAN_SECOND;
|
||||
|
||||
do {
|
||||
if (qtest_get_irq(qts, is_tx ? mod->tx_irq : mod->rx_irq)) {
|
||||
return true;
|
||||
}
|
||||
qtest_clock_step(qts, step);
|
||||
} while (g_get_monotonic_time() < end_time);
|
||||
|
||||
g_message("%s: Timeout expired", __func__);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool emc_wait_mista(QTestState *qts, const EMCModule *mod, int step,
|
||||
uint32_t flag)
|
||||
{
|
||||
uint64_t end_time =
|
||||
g_get_monotonic_time() + TIMEOUT_SECONDS * G_TIME_SPAN_SECOND;
|
||||
|
||||
do {
|
||||
uint32_t mista = emc_read(qts, mod, REG_MISTA);
|
||||
if (mista & flag) {
|
||||
return true;
|
||||
}
|
||||
qtest_clock_step(qts, step);
|
||||
} while (g_get_monotonic_time() < end_time);
|
||||
|
||||
g_message("%s: Timeout expired", __func__);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool wait_socket_readable(int fd)
|
||||
{
|
||||
fd_set read_fds;
|
||||
struct timeval tv;
|
||||
int rv;
|
||||
|
||||
FD_ZERO(&read_fds);
|
||||
FD_SET(fd, &read_fds);
|
||||
tv.tv_sec = TIMEOUT_SECONDS;
|
||||
tv.tv_usec = 0;
|
||||
rv = select(fd + 1, &read_fds, NULL, NULL, &tv);
|
||||
if (rv == -1) {
|
||||
perror("select");
|
||||
} else if (rv == 0) {
|
||||
g_message("%s: Timeout expired", __func__);
|
||||
}
|
||||
return rv == 1;
|
||||
}
|
||||
|
||||
/* Initialize *desc (in host endian format). */
|
||||
static void init_tx_desc(NPCM7xxEMCTxDesc *desc, size_t count,
|
||||
uint32_t desc_addr)
|
||||
{
|
||||
g_assert(count >= 2);
|
||||
memset(&desc[0], 0, sizeof(*desc) * count);
|
||||
/* Leave the last one alone, owned by the cpu -> stops transmission. */
|
||||
for (size_t i = 0; i < count - 1; ++i) {
|
||||
desc[i].flags =
|
||||
(TX_DESC_FLAG_OWNER_MASK | /* owner = 1: emc */
|
||||
TX_DESC_FLAG_INTEN |
|
||||
0 | /* crc append = 0 */
|
||||
0 /* padding enable = 0 */);
|
||||
desc[i].status_and_length =
|
||||
(0 | /* collision count = 0 */
|
||||
0 | /* SQE = 0 */
|
||||
0 | /* PAU = 0 */
|
||||
0 | /* TXHA = 0 */
|
||||
0 | /* LC = 0 */
|
||||
0 | /* TXABT = 0 */
|
||||
0 | /* NCS = 0 */
|
||||
0 | /* EXDEF = 0 */
|
||||
0 | /* TXCP = 0 */
|
||||
0 | /* DEF = 0 */
|
||||
0 | /* TXINTR = 0 */
|
||||
0 /* length filled in later */);
|
||||
desc[i].ntxdsa = desc_addr + (i + 1) * sizeof(*desc);
|
||||
}
|
||||
}
|
||||
|
||||
static void enable_tx(QTestState *qts, const EMCModule *mod,
|
||||
const NPCM7xxEMCTxDesc *desc, size_t count,
|
||||
uint32_t desc_addr, uint32_t mien_flags)
|
||||
{
|
||||
/* Write the descriptors to guest memory. */
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
emc_write_tx_desc(qts, desc + i, desc_addr + i * sizeof(*desc));
|
||||
}
|
||||
|
||||
/* Trigger sending the packet. */
|
||||
/* The module must be reset before changing TXDLSA. */
|
||||
g_assert(emc_soft_reset(qts, mod));
|
||||
emc_write(qts, mod, REG_TXDLSA, desc_addr);
|
||||
emc_write(qts, mod, REG_CTXDSA, ~0);
|
||||
emc_write(qts, mod, REG_MIEN, REG_MIEN_ENTXCP | mien_flags);
|
||||
{
|
||||
uint32_t mcmdr = emc_read(qts, mod, REG_MCMDR);
|
||||
mcmdr |= REG_MCMDR_TXON;
|
||||
emc_write(qts, mod, REG_MCMDR, mcmdr);
|
||||
}
|
||||
|
||||
/* Prod the device to send the packet. */
|
||||
emc_write(qts, mod, REG_TSDR, 1);
|
||||
}
|
||||
|
||||
static void emc_send_verify1(QTestState *qts, const EMCModule *mod, int fd,
|
||||
bool with_irq, uint32_t desc_addr,
|
||||
uint32_t next_desc_addr,
|
||||
const char *test_data, int test_size)
|
||||
{
|
||||
NPCM7xxEMCTxDesc result_desc;
|
||||
uint32_t expected_mask, expected_value, recv_len;
|
||||
int ret;
|
||||
char buffer[TX_DATA_LEN];
|
||||
|
||||
g_assert(wait_socket_readable(fd));
|
||||
|
||||
/* Read the descriptor back. */
|
||||
emc_read_tx_desc(qts, desc_addr, &result_desc);
|
||||
/* Descriptor should be owned by cpu now. */
|
||||
g_assert((result_desc.flags & TX_DESC_FLAG_OWNER_MASK) == 0);
|
||||
/* Test the status bits, ignoring the length field. */
|
||||
expected_mask = 0xffff << 16;
|
||||
expected_value = TX_DESC_STATUS_TXCP;
|
||||
if (with_irq) {
|
||||
expected_value |= TX_DESC_STATUS_TXINTR;
|
||||
}
|
||||
g_assert_cmphex((result_desc.status_and_length & expected_mask), ==,
|
||||
expected_value);
|
||||
|
||||
/* Check data sent to the backend. */
|
||||
recv_len = ~0;
|
||||
ret = qemu_recv(fd, &recv_len, sizeof(recv_len), MSG_DONTWAIT);
|
||||
g_assert_cmpint(ret, == , sizeof(recv_len));
|
||||
|
||||
g_assert(wait_socket_readable(fd));
|
||||
memset(buffer, 0xff, sizeof(buffer));
|
||||
ret = qemu_recv(fd, buffer, test_size, MSG_DONTWAIT);
|
||||
g_assert_cmpmem(buffer, ret, test_data, test_size);
|
||||
}
|
||||
|
||||
static void emc_send_verify(QTestState *qts, const EMCModule *mod, int fd,
|
||||
bool with_irq)
|
||||
{
|
||||
NPCM7xxEMCTxDesc desc[NUM_TX_DESCRIPTORS];
|
||||
uint32_t desc_addr = DESC_ADDR;
|
||||
static const char test1_data[] = "TEST1";
|
||||
static const char test2_data[] = "Testing 1 2 3 ...";
|
||||
uint32_t data1_addr = DATA_ADDR;
|
||||
uint32_t data2_addr = data1_addr + sizeof(test1_data);
|
||||
bool got_tdu;
|
||||
uint32_t end_desc_addr;
|
||||
|
||||
/* Prepare test data buffer. */
|
||||
qtest_memwrite(qts, data1_addr, test1_data, sizeof(test1_data));
|
||||
qtest_memwrite(qts, data2_addr, test2_data, sizeof(test2_data));
|
||||
|
||||
init_tx_desc(&desc[0], NUM_TX_DESCRIPTORS, desc_addr);
|
||||
desc[0].txbsa = data1_addr;
|
||||
desc[0].status_and_length |= sizeof(test1_data);
|
||||
desc[1].txbsa = data2_addr;
|
||||
desc[1].status_and_length |= sizeof(test2_data);
|
||||
|
||||
enable_tx(qts, mod, &desc[0], NUM_TX_DESCRIPTORS, desc_addr,
|
||||
with_irq ? REG_MIEN_ENTXINTR : 0);
|
||||
|
||||
/*
|
||||
* It's problematic to observe the interrupt for each packet.
|
||||
* Instead just wait until all the packets go out.
|
||||
*/
|
||||
got_tdu = false;
|
||||
while (!got_tdu) {
|
||||
if (with_irq) {
|
||||
g_assert_true(emc_wait_irq(qts, mod, TX_STEP_COUNT,
|
||||
/*is_tx=*/true));
|
||||
} else {
|
||||
g_assert_true(emc_wait_mista(qts, mod, TX_STEP_COUNT,
|
||||
REG_MISTA_TXINTR));
|
||||
}
|
||||
got_tdu = !!(emc_read(qts, mod, REG_MISTA) & REG_MISTA_TDU);
|
||||
/* If we don't have TDU yet, reset the interrupt. */
|
||||
if (!got_tdu) {
|
||||
emc_write(qts, mod, REG_MISTA,
|
||||
emc_read(qts, mod, REG_MISTA) & 0xffff0000);
|
||||
}
|
||||
}
|
||||
|
||||
end_desc_addr = desc_addr + 2 * sizeof(desc[0]);
|
||||
g_assert_cmphex(emc_read(qts, mod, REG_CTXDSA), ==, end_desc_addr);
|
||||
g_assert_cmphex(emc_read(qts, mod, REG_MISTA), ==,
|
||||
REG_MISTA_TXCP | REG_MISTA_TXINTR | REG_MISTA_TDU);
|
||||
|
||||
emc_send_verify1(qts, mod, fd, with_irq,
|
||||
desc_addr, end_desc_addr,
|
||||
test1_data, sizeof(test1_data));
|
||||
emc_send_verify1(qts, mod, fd, with_irq,
|
||||
desc_addr + sizeof(desc[0]), end_desc_addr,
|
||||
test2_data, sizeof(test2_data));
|
||||
}
|
||||
|
||||
/* Initialize *desc (in host endian format). */
|
||||
static void init_rx_desc(NPCM7xxEMCRxDesc *desc, size_t count,
|
||||
uint32_t desc_addr, uint32_t data_addr)
|
||||
{
|
||||
g_assert_true(count >= 2);
|
||||
memset(desc, 0, sizeof(*desc) * count);
|
||||
desc[0].rxbsa = data_addr;
|
||||
desc[0].status_and_length =
|
||||
(0b10 << RX_DESC_STATUS_OWNER_SHIFT | /* owner = 10: emc */
|
||||
0 | /* RP = 0 */
|
||||
0 | /* ALIE = 0 */
|
||||
0 | /* RXGD = 0 */
|
||||
0 | /* PTLE = 0 */
|
||||
0 | /* CRCE = 0 */
|
||||
0 | /* RXINTR = 0 */
|
||||
0 /* length (filled in later) */);
|
||||
/* Leave the last one alone, owned by the cpu -> stops transmission. */
|
||||
desc[0].nrxdsa = desc_addr + sizeof(*desc);
|
||||
}
|
||||
|
||||
static void enable_rx(QTestState *qts, const EMCModule *mod,
|
||||
const NPCM7xxEMCRxDesc *desc, size_t count,
|
||||
uint32_t desc_addr, uint32_t mien_flags,
|
||||
uint32_t mcmdr_flags)
|
||||
{
|
||||
/*
|
||||
* Write the descriptor to guest memory.
|
||||
* FWIW, IWBN if the docs said the buffer needs to be at least DMARFC
|
||||
* bytes.
|
||||
*/
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
emc_write_rx_desc(qts, desc + i, desc_addr + i * sizeof(*desc));
|
||||
}
|
||||
|
||||
/* Trigger receiving the packet. */
|
||||
/* The module must be reset before changing RXDLSA. */
|
||||
g_assert(emc_soft_reset(qts, mod));
|
||||
emc_write(qts, mod, REG_RXDLSA, desc_addr);
|
||||
emc_write(qts, mod, REG_MIEN, REG_MIEN_ENRXGD | mien_flags);
|
||||
|
||||
/*
|
||||
* We don't know what the device's macaddr is, so just accept all
|
||||
* unicast packets (AUP).
|
||||
*/
|
||||
emc_write(qts, mod, REG_CAMCMR, REG_CAMCMR_AUP);
|
||||
emc_write(qts, mod, REG_CAMEN, 1 << 0);
|
||||
{
|
||||
uint32_t mcmdr = emc_read(qts, mod, REG_MCMDR);
|
||||
mcmdr |= REG_MCMDR_RXON | mcmdr_flags;
|
||||
emc_write(qts, mod, REG_MCMDR, mcmdr);
|
||||
}
|
||||
|
||||
/* Prod the device to accept a packet. */
|
||||
emc_write(qts, mod, REG_RSDR, 1);
|
||||
}
|
||||
|
||||
static void emc_recv_verify(QTestState *qts, const EMCModule *mod, int fd,
|
||||
bool with_irq)
|
||||
{
|
||||
NPCM7xxEMCRxDesc desc[NUM_RX_DESCRIPTORS];
|
||||
uint32_t desc_addr = DESC_ADDR;
|
||||
uint32_t data_addr = DATA_ADDR;
|
||||
int ret;
|
||||
uint32_t expected_mask, expected_value;
|
||||
NPCM7xxEMCRxDesc result_desc;
|
||||
|
||||
/* Prepare test data buffer. */
|
||||
const char test[RX_DATA_LEN] = "TEST";
|
||||
int len = htonl(sizeof(test));
|
||||
const struct iovec iov[] = {
|
||||
{
|
||||
.iov_base = &len,
|
||||
.iov_len = sizeof(len),
|
||||
},{
|
||||
.iov_base = (char *) test,
|
||||
.iov_len = sizeof(test),
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* Reset the device BEFORE sending a test packet, otherwise the packet
|
||||
* may get swallowed by an active device of an earlier test.
|
||||
*/
|
||||
init_rx_desc(&desc[0], NUM_RX_DESCRIPTORS, desc_addr, data_addr);
|
||||
enable_rx(qts, mod, &desc[0], NUM_RX_DESCRIPTORS, desc_addr,
|
||||
with_irq ? REG_MIEN_ENRXINTR : 0, 0);
|
||||
|
||||
/* Send test packet to device's socket. */
|
||||
ret = iov_send(fd, iov, 2, 0, sizeof(len) + sizeof(test));
|
||||
g_assert_cmpint(ret, == , sizeof(test) + sizeof(len));
|
||||
|
||||
/* Wait for RX interrupt. */
|
||||
if (with_irq) {
|
||||
g_assert_true(emc_wait_irq(qts, mod, RX_STEP_COUNT, /*is_tx=*/false));
|
||||
} else {
|
||||
g_assert_true(emc_wait_mista(qts, mod, RX_STEP_COUNT, REG_MISTA_RXGD));
|
||||
}
|
||||
|
||||
g_assert_cmphex(emc_read(qts, mod, REG_CRXDSA), ==,
|
||||
desc_addr + sizeof(desc[0]));
|
||||
|
||||
expected_mask = 0xffff;
|
||||
expected_value = (REG_MISTA_DENI |
|
||||
REG_MISTA_RXGD |
|
||||
REG_MISTA_RXINTR);
|
||||
g_assert_cmphex((emc_read(qts, mod, REG_MISTA) & expected_mask),
|
||||
==, expected_value);
|
||||
|
||||
/* Read the descriptor back. */
|
||||
emc_read_rx_desc(qts, desc_addr, &result_desc);
|
||||
/* Descriptor should be owned by cpu now. */
|
||||
g_assert((result_desc.status_and_length & RX_DESC_STATUS_OWNER_MASK) == 0);
|
||||
/* Test the status bits, ignoring the length field. */
|
||||
expected_mask = 0xffff << 16;
|
||||
expected_value = RX_DESC_STATUS_RXGD;
|
||||
if (with_irq) {
|
||||
expected_value |= RX_DESC_STATUS_RXINTR;
|
||||
}
|
||||
g_assert_cmphex((result_desc.status_and_length & expected_mask), ==,
|
||||
expected_value);
|
||||
g_assert_cmpint(RX_DESC_PKT_LEN(result_desc.status_and_length), ==,
|
||||
RX_DATA_LEN + CRC_LENGTH);
|
||||
|
||||
{
|
||||
char buffer[RX_DATA_LEN];
|
||||
qtest_memread(qts, data_addr, buffer, sizeof(buffer));
|
||||
g_assert_cmpstr(buffer, == , "TEST");
|
||||
}
|
||||
}
|
||||
|
||||
static void emc_test_ptle(QTestState *qts, const EMCModule *mod, int fd)
|
||||
{
|
||||
NPCM7xxEMCRxDesc desc[NUM_RX_DESCRIPTORS];
|
||||
uint32_t desc_addr = DESC_ADDR;
|
||||
uint32_t data_addr = DATA_ADDR;
|
||||
int ret;
|
||||
NPCM7xxEMCRxDesc result_desc;
|
||||
uint32_t expected_mask, expected_value;
|
||||
|
||||
/* Prepare test data buffer. */
|
||||
#define PTLE_DATA_LEN 1600
|
||||
char test_data[PTLE_DATA_LEN];
|
||||
int len = htonl(sizeof(test_data));
|
||||
const struct iovec iov[] = {
|
||||
{
|
||||
.iov_base = &len,
|
||||
.iov_len = sizeof(len),
|
||||
},{
|
||||
.iov_base = (char *) test_data,
|
||||
.iov_len = sizeof(test_data),
|
||||
},
|
||||
};
|
||||
memset(test_data, 42, sizeof(test_data));
|
||||
|
||||
/*
|
||||
* Reset the device BEFORE sending a test packet, otherwise the packet
|
||||
* may get swallowed by an active device of an earlier test.
|
||||
*/
|
||||
init_rx_desc(&desc[0], NUM_RX_DESCRIPTORS, desc_addr, data_addr);
|
||||
enable_rx(qts, mod, &desc[0], NUM_RX_DESCRIPTORS, desc_addr,
|
||||
REG_MIEN_ENRXINTR, REG_MCMDR_ALP);
|
||||
|
||||
/* Send test packet to device's socket. */
|
||||
ret = iov_send(fd, iov, 2, 0, sizeof(len) + sizeof(test_data));
|
||||
g_assert_cmpint(ret, == , sizeof(test_data) + sizeof(len));
|
||||
|
||||
/* Wait for RX interrupt. */
|
||||
g_assert_true(emc_wait_irq(qts, mod, RX_STEP_COUNT, /*is_tx=*/false));
|
||||
|
||||
/* Read the descriptor back. */
|
||||
emc_read_rx_desc(qts, desc_addr, &result_desc);
|
||||
/* Descriptor should be owned by cpu now. */
|
||||
g_assert((result_desc.status_and_length & RX_DESC_STATUS_OWNER_MASK) == 0);
|
||||
/* Test the status bits, ignoring the length field. */
|
||||
expected_mask = 0xffff << 16;
|
||||
expected_value = (RX_DESC_STATUS_RXGD |
|
||||
RX_DESC_STATUS_PTLE |
|
||||
RX_DESC_STATUS_RXINTR);
|
||||
g_assert_cmphex((result_desc.status_and_length & expected_mask), ==,
|
||||
expected_value);
|
||||
g_assert_cmpint(RX_DESC_PKT_LEN(result_desc.status_and_length), ==,
|
||||
PTLE_DATA_LEN + CRC_LENGTH);
|
||||
|
||||
{
|
||||
char buffer[PTLE_DATA_LEN];
|
||||
qtest_memread(qts, data_addr, buffer, sizeof(buffer));
|
||||
g_assert(memcmp(buffer, test_data, PTLE_DATA_LEN) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_tx(gconstpointer test_data)
|
||||
{
|
||||
const TestData *td = test_data;
|
||||
GString *cmd_line = g_string_new("-machine quanta-gsj");
|
||||
int *test_sockets = packet_test_init(emc_module_index(td->module),
|
||||
cmd_line);
|
||||
QTestState *qts = qtest_init(cmd_line->str);
|
||||
|
||||
/*
|
||||
* TODO: For pedantic correctness test_sockets[0] should be closed after
|
||||
* the fork and before the exec, but that will require some harness
|
||||
* improvements.
|
||||
*/
|
||||
close(test_sockets[1]);
|
||||
/* Defensive programming */
|
||||
test_sockets[1] = -1;
|
||||
|
||||
qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
|
||||
|
||||
emc_send_verify(qts, td->module, test_sockets[0], /*with_irq=*/false);
|
||||
emc_send_verify(qts, td->module, test_sockets[0], /*with_irq=*/true);
|
||||
|
||||
qtest_quit(qts);
|
||||
}
|
||||
|
||||
static void test_rx(gconstpointer test_data)
|
||||
{
|
||||
const TestData *td = test_data;
|
||||
GString *cmd_line = g_string_new("-machine quanta-gsj");
|
||||
int *test_sockets = packet_test_init(emc_module_index(td->module),
|
||||
cmd_line);
|
||||
QTestState *qts = qtest_init(cmd_line->str);
|
||||
|
||||
/*
|
||||
* TODO: For pedantic correctness test_sockets[0] should be closed after
|
||||
* the fork and before the exec, but that will require some harness
|
||||
* improvements.
|
||||
*/
|
||||
close(test_sockets[1]);
|
||||
/* Defensive programming */
|
||||
test_sockets[1] = -1;
|
||||
|
||||
qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
|
||||
|
||||
emc_recv_verify(qts, td->module, test_sockets[0], /*with_irq=*/false);
|
||||
emc_recv_verify(qts, td->module, test_sockets[0], /*with_irq=*/true);
|
||||
emc_test_ptle(qts, td->module, test_sockets[0]);
|
||||
|
||||
qtest_quit(qts);
|
||||
}
|
||||
|
||||
static void emc_add_test(const char *name, const TestData* td,
|
||||
GTestDataFunc fn)
|
||||
{
|
||||
g_autofree char *full_name = g_strdup_printf(
|
||||
"npcm7xx_emc/emc[%d]/%s", emc_module_index(td->module), name);
|
||||
qtest_add_data_func(full_name, td, fn);
|
||||
}
|
||||
#define add_test(name, td) emc_add_test(#name, td, test_##name)
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
TestData test_data_list[ARRAY_SIZE(emc_module_list)];
|
||||
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
|
||||
for (int i = 0; i < ARRAY_SIZE(emc_module_list); ++i) {
|
||||
TestData *td = &test_data_list[i];
|
||||
|
||||
td->module = &emc_module_list[i];
|
||||
|
||||
add_test(init, td);
|
||||
add_test(tx, td);
|
||||
add_test(rx, td);
|
||||
}
|
||||
|
||||
return g_test_run();
|
||||
}
|
Loading…
Reference in New Issue
Block a user