target-arm queue:

* Add new mps3-an547 board
  * target/arm: Restrict v7A TCG cpus to TCG accel
  * Implement a Xilinx CSU DMA model
  * hw/timer/renesas_tmr: Fix use of uninitialized data in read_tcnt()
 -----BEGIN PGP SIGNATURE-----
 
 iQJMBAABCAA3FiEE4aXFk81BneKOgxXPPCUl7RQ2DN4FAmBI0AQZHHBldGVyLm1h
 eWRlbGxAbGluYXJvLm9yZwAKCRA8JSXtFDYM3n1WD/iXrX/YMZDrBOzP5h6sE5W/
 W5tiTJBCKskRW1HduJjObFFl29yuiTzYld7+zfOTQUFgecpCN8q7AHuN0y9Sg+9B
 cyHNHwseOfkHE4CGe2ImjmpYSZUUEkQBtCN2OmJjsaiEoK/eZCIErPw4JMUcusLL
 VPccPjXS92WtqQkGshNYribMOhZnuBcvX/LsT7IL8THDVPv8OECIeq8ewTZtLMe/
 l/x3D3PJ56q69EFMnYt6TS1cd9OtD7pw3Jnbfv0iStE/TiJQB92ft8H07kpE6KVI
 jhRkhyVBnrVI2deTFcFC+rZDQggzWGRVAbUzMikZOZycUML/zjJKEIGM2V6iCHmL
 bQMUOTR4GLFbVyabJ/IH6YoCFS+8hUboyQXQL0gOKtcJiryoDI3AjyKNjxRbIY3b
 qEV3xQYTtrS7mdrwQZqwH6Rs/54jDyX7eBNWfZwq4dOQKvnTQPQEj6iWmj4rOvma
 McWbhu6bAfU6ZINOxkr8HcG/AxF3IYw9Gtb8KRg7/87JRvNvxj++kqqjSRRLU4Co
 QUYuQnpd+ux7eSXKcRfnzd27iaZ++dKJMct4Gq8i0VASb+uk+xmkG2MiGEBVSJv8
 kIK5NLzDGjWjrZd366uAUtHBnWktP/5J4DKq1XKx0mtrkt7tbU6Oh90a7rSiOgur
 LtpGH4IPQDwp/YGXZD86
 =Qu2a
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20210310' into staging

target-arm queue:
 * Add new mps3-an547 board
 * target/arm: Restrict v7A TCG cpus to TCG accel
 * Implement a Xilinx CSU DMA model
 * hw/timer/renesas_tmr: Fix use of uninitialized data in read_tcnt()

# gpg: Signature made Wed 10 Mar 2021 13:56:20 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-20210310: (54 commits)
  hw/timer/renesas_tmr: Fix use of uninitialized data in read_tcnt()
  hw/timer/renesas_tmr: Prefix constants for CSS values with CSS_
  hw/ssi: xilinx_spips: Remove DMA related dead codes from zynqmp_spips
  hw/ssi: xilinx_spips: Clean up coding convention issues
  hw/arm: xlnx-zynqmp: Connect a Xilinx CSU DMA module for QSPI
  hw/arm: xlnx-zynqmp: Clean up coding convention issues
  hw/dma: Implement a Xilinx CSU DMA model
  target/arm: Restrict v7A TCG cpus to TCG accel
  tests/qtest/sse-timer-test: Test counter scaling changes
  tests/qtest/sse-timer-test: Test the system timer
  tests/qtest/sse-timer-test: Add simple test of the SSE counter
  docs/system/arm/mps2.rst: Document the new mps3-an547 board
  hw/arm/mps2-tz: Add new mps3-an547 board
  hw/arm/mps2-tz: Make initsvtor0 setting board-specific
  hw/arm/mps2-tz: Support running APB peripherals on different clock
  hw/misc/mps2-scc: Implement changes for AN547
  hw/misc/mps2-fpgaio: Support AN547 DBGCTRL register
  hw/misc/mps2-fpgaio: Fold counters subsection into main vmstate
  hw/arm/mps2-tz: Make UART overflow IRQ board-specific
  hw/arm/armsse: Add SSE-300 support
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2021-03-10 13:57:31 +00:00
commit 5c6295a45b
60 changed files with 4537 additions and 846 deletions

View File

@ -747,10 +747,17 @@ F: hw/misc/iotkit-sysctl.c
F: include/hw/misc/iotkit-sysctl.h
F: hw/misc/iotkit-sysinfo.c
F: include/hw/misc/iotkit-sysinfo.h
F: hw/misc/armsse-cpu-pwrctrl.c
F: include/hw/misc/armsse-cpu-pwrctrl.h
F: hw/misc/armsse-cpuid.c
F: include/hw/misc/armsse-cpuid.h
F: hw/misc/armsse-mhu.c
F: include/hw/misc/armsse-mhu.h
F: hw/timer/sse-counter.c
F: include/hw/timer/sse-counter.h
F: hw/timer/sse-timer.c
F: include/hw/timer/sse-timer.h
F: tests/qtest/sse-timer-test.c
F: docs/system/arm/mps2.rst
Musca

View File

@ -80,11 +80,12 @@ Adding clocks to a device must be done during the init method of the Device
instance.
To add an input clock to a device, the function ``qdev_init_clock_in()``
must be used. It takes the name, a callback and an opaque parameter
for the callback (this will be explained in a following section).
must be used. It takes the name, a callback, an opaque parameter
for the callback and a mask of events when the callback should be
called (this will be explained in a following section).
Output is simpler; only the name is required. Typically::
qdev_init_clock_in(DEVICE(dev), "clk_in", clk_in_callback, dev);
qdev_init_clock_in(DEVICE(dev), "clk_in", clk_in_callback, dev, ClockUpdate);
qdev_init_clock_out(DEVICE(dev), "clk_out");
Both functions return the created Clock pointer, which should be saved in the
@ -113,7 +114,7 @@ output.
* callback for the input clock (see "Callback on input clock
* change" section below for more information).
*/
static void clk_in_callback(void *opaque);
static void clk_in_callback(void *opaque, ClockEvent event);
/*
* static array describing clocks:
@ -124,7 +125,7 @@ output.
* the clk_out field of a MyDeviceState structure.
*/
static const ClockPortInitArray mydev_clocks = {
QDEV_CLOCK_IN(MyDeviceState, clk_in, clk_in_callback),
QDEV_CLOCK_IN(MyDeviceState, clk_in, clk_in_callback, ClockUpdate),
QDEV_CLOCK_OUT(MyDeviceState, clk_out),
QDEV_CLOCK_END
};
@ -153,6 +154,47 @@ nothing else to do. This value will be propagated to other clocks when
connecting the clocks together and devices will fetch the right value during
the first reset.
Clock callbacks
---------------
You can give a clock a callback function in several ways:
* by passing it as an argument to ``qdev_init_clock_in()``
* as an argument to the ``QDEV_CLOCK_IN()`` macro initializing an
array to be passed to ``qdev_init_clocks()``
* by directly calling the ``clock_set_callback()`` function
The callback function must be of this type:
.. code-block:: c
typedef void ClockCallback(void *opaque, ClockEvent event);
The ``opaque`` argument is the pointer passed to ``qdev_init_clock_in()``
or ``clock_set_callback()``; for ``qdev_init_clocks()`` it is the
``dev`` device pointer.
The ``event`` argument specifies why the callback has been called.
When you register the callback you specify a mask of ClockEvent values
that you are interested in. The callback will only be called for those
events.
The events currently supported are:
* ``ClockPreUpdate`` : called when the input clock's period is about to
update. This is useful if the device needs to do some action for
which it needs to know the old value of the clock period. During
this callback, Clock API functions like ``clock_get()`` or
``clock_ticks_to_ns()`` will use the old period.
* ``ClockUpdate`` : called after the input clock's period has changed.
During this callback, Clock API functions like ``clock_ticks_to_ns()``
will use the new period.
Note that a clock only has one callback: it is not possible to register
different functions for different events. You must register a single
callback which listens for all of the events you are interested in,
and use the ``event`` argument to identify which event has happened.
Retrieving clocks from a device
-------------------------------
@ -231,7 +273,7 @@ object during device instance init. For example:
.. code-block:: c
clk = qdev_init_clock_in(DEVICE(dev), "clk-in", clk_in_callback,
dev);
dev, ClockUpdate);
/* set initial value to 10ns / 100MHz */
clock_set_ns(clk, 10);
@ -267,11 +309,12 @@ next lowest integer. This implies some inaccuracy due to the rounding,
so be cautious about using it in calculations.
It is also possible to register a callback on clock frequency changes.
Here is an example:
Here is an example, which assumes that ``clock_callback`` has been
specified as the callback for the ``ClockUpdate`` event:
.. code-block:: c
void clock_callback(void *opaque) {
void clock_callback(void *opaque, ClockEvent event) {
MyDeviceState *s = (MyDeviceState *) opaque;
/*
* 'opaque' is the argument passed to qdev_init_clock_in();
@ -317,6 +360,18 @@ rather than simply passing it to a QEMUTimer function like
``timer_mod_ns()`` then you should be careful to avoid overflow
in those calculations, of course.)
Obtaining tick counts
---------------------
For calculations where you need to know the number of ticks in
a given duration, use ``clock_ns_to_ticks()``. This function handles
possible non-whole-number-of-nanoseconds periods and avoids
potential rounding errors. It will return '0' if the clock is stopped
(i.e. it has period zero). If the inputs imply a tick count that
overflows a 64-bit value (a very long duration for a clock with a
very short period) the output value is truncated, so effectively
the 64-bit output wraps around.
Changing a clock period
-----------------------

View File

@ -1,5 +1,5 @@
Arm MPS2 and MPS3 boards (``mps2-an385``, ``mps2-an386``, ``mps2-an500``, ``mps2-an505``, ``mps2-an511``, ``mps2-an521``, ``mps3-an524``)
=========================================================================================================================================
Arm MPS2 and MPS3 boards (``mps2-an385``, ``mps2-an386``, ``mps2-an500``, ``mps2-an505``, ``mps2-an511``, ``mps2-an521``, ``mps3-an524``, ``mps3-an547``)
=========================================================================================================================================================
These board models all use Arm M-profile CPUs.
@ -27,6 +27,8 @@ QEMU models the following FPGA images:
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
``mps3-an547``
Cortex-M55 on an MPS3, as documented in Arm Application Note AN547
Differences between QEMU and real hardware:

View File

@ -238,7 +238,7 @@ static void npcm7xx_adc_init(Object *obj)
memory_region_init_io(&s->iomem, obj, &npcm7xx_adc_ops, s,
TYPE_NPCM7XX_ADC, 4 * KiB);
sysbus_init_mmio(sbd, &s->iomem);
s->clock = qdev_init_clock_in(DEVICE(s), "clock", NULL, NULL);
s->clock = qdev_init_clock_in(DEVICE(s), "clock", NULL, NULL, 0);
for (i = 0; i < NPCM7XX_ADC_NUM_INPUTS; ++i) {
object_property_add_uint32_ptr(obj, "adci[*]",

View File

@ -353,6 +353,7 @@ config XLNX_ZYNQMP_ARM
select SSI_M25P80
select XILINX_AXI
select XILINX_SPIPS
select XLNX_CSU_DMA
select XLNX_ZYNQMP
select XLNX_ZDMA
@ -505,6 +506,7 @@ config ARM11MPCORE
config ARMSSE
bool
select ARM_V7M
select ARMSSE_CPU_PWRCTRL
select ARMSSE_CPUID
select ARMSSE_MHU
select CMSDK_APB_TIMER
@ -520,9 +522,5 @@ config ARMSSE
select TZ_MSC
select TZ_PPC
select UNIMP
config ARMSSE_CPUID
bool
config ARMSSE_MHU
bool
select SSE_COUNTER
select SSE_TIMER

File diff suppressed because it is too large Load Diff

View File

@ -17,6 +17,7 @@
* "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
* "mps2-an547" -- Single Cortex-M55 as documented in Application Note AN547
*
* Links to the TRM for the board itself and to the various Application
* Notes which document the FPGA images can be found here:
@ -30,6 +31,8 @@
* https://developer.arm.com/documentation/dai0521/latest/
* Application Note AN524:
* https://developer.arm.com/documentation/dai0524/latest/
* Application Note AN547:
* https://developer.arm.com/-/media/Arm%20Developer%20Community/PDF/DAI0547B_SSE300_PLUS_U55_FPGA_for_mps3.pdf
*
* 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:
@ -37,6 +40,8 @@
* Similarly, the AN521 and AN524 use the SSE-200, and the SSE-200 TRM defines
* most of the device layout:
* https://developer.arm.com/documentation/101104/latest/
* and the AN547 uses the SSE-300, whose layout is in the SSE-300 TRM:
* https://developer.arm.com/documentation/101773/latest/
*/
#include "qemu/osdep.h"
@ -68,13 +73,14 @@
#include "hw/qdev-clock.h"
#include "qom/object.h"
#define MPS2TZ_NUMIRQ_MAX 95
#define MPS2TZ_RAM_MAX 4
#define MPS2TZ_NUMIRQ_MAX 96
#define MPS2TZ_RAM_MAX 5
typedef enum MPS2TZFPGAType {
FPGA_AN505,
FPGA_AN521,
FPGA_AN524,
FPGA_AN547,
} MPS2TZFPGAType;
/*
@ -106,11 +112,15 @@ struct MPS2TZMachineClass {
MPS2TZFPGAType fpga_type;
uint32_t scc_id;
uint32_t sysclk_frq; /* Main SYSCLK frequency in Hz */
uint32_t apb_periph_frq; /* APB peripheral 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? */
bool fpgaio_has_dbgctrl; /* Does FPGAIO have DBGCTRL register? */
int numirq; /* Number of external interrupts */
int uart_overflow_irq; /* number of the combined UART overflow IRQ */
uint32_t init_svtor; /* init-svtor setting for SSE */
const RAMInfo *raminfo;
const char *armsse_type;
};
@ -149,6 +159,7 @@ struct MPS2TZMachineState {
#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")
#define TYPE_MPS3TZ_AN547_MACHINE MACHINE_TYPE_NAME("mps3-an547")
OBJECT_DECLARE_TYPE(MPS2TZMachineState, MPS2TZMachineClass, MPS2TZ_MACHINE)
@ -248,6 +259,49 @@ static const RAMInfo an524_raminfo[] = { {
},
};
static const RAMInfo an547_raminfo[] = { {
.name = "itcm",
.base = 0x00000000,
.size = 512 * KiB,
.mpc = -1,
.mrindex = 0,
}, {
.name = "sram",
.base = 0x01000000,
.size = 2 * MiB,
.mpc = 0,
.mrindex = 1,
}, {
.name = "dtcm",
.base = 0x20000000,
.size = 4 * 128 * KiB,
.mpc = -1,
.mrindex = 2,
}, {
.name = "sram 2",
.base = 0x21000000,
.size = 4 * MiB,
.mpc = -1,
.mrindex = 3,
}, {
/* We don't model QSPI flash yet; for now expose it as simple ROM */
.name = "QSPI",
.base = 0x28000000,
.size = 8 * MiB,
.mpc = 1,
.mrindex = 4,
.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);
@ -377,7 +431,7 @@ static MemoryRegion *make_uart(MPS2TZMachineState *mms, void *opaque,
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", mmc->sysclk_frq);
qdev_prop_set_uint32(DEVICE(uart), "pclk-frq", mmc->apb_periph_frq);
sysbus_realize(SYS_BUS_DEVICE(uart), &error_fatal);
s = SYS_BUS_DEVICE(uart);
sysbus_connect_irq(s, 0, get_sse_irq_in(mms, irqs[0]));
@ -421,6 +475,7 @@ static MemoryRegion *make_fpgaio(MPS2TZMachineState *mms, void *opaque,
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);
qdev_prop_set_bit(DEVICE(fpgaio), "has-dbgctrl", mmc->fpgaio_has_dbgctrl);
sysbus_realize(SYS_BUS_DEVICE(fpgaio), &error_fatal);
return sysbus_mmio_get_region(SYS_BUS_DEVICE(fpgaio), 0);
}
@ -696,6 +751,7 @@ static void mps2tz_common_init(MachineState *machine)
object_property_set_link(OBJECT(&mms->iotkit), "memory",
OBJECT(system_memory), &error_abort);
qdev_prop_set_uint32(iotkitdev, "EXP_NUMIRQ", mmc->numirq);
qdev_prop_set_uint32(iotkitdev, "init-svtor", mmc->init_svtor);
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);
@ -770,7 +826,7 @@ static void mps2tz_common_init(MachineState *machine)
&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, 47));
get_sse_irq_in(mms, mmc->uart_overflow_irq));
/* Most of the devices in the FPGA are behind Peripheral Protection
* Controllers. The required order for initializing things is:
@ -887,6 +943,55 @@ static void mps2tz_common_init(MachineState *machine)
},
};
const PPCInfo an547_ppcs[] = { {
.name = "apb_ppcexp0",
.ports = {
{ "ssram-mpc", make_mpc, &mms->mpc[0], 0x57000000, 0x1000 },
{ "qspi-mpc", make_mpc, &mms->mpc[1], 0x57001000, 0x1000 },
{ "ddr-mpc", make_mpc, &mms->mpc[2], 0x57002000, 0x1000 },
},
}, {
.name = "apb_ppcexp1",
.ports = {
{ "i2c0", make_i2c, &mms->i2c[0], 0x49200000, 0x1000 },
{ "i2c1", make_i2c, &mms->i2c[1], 0x49201000, 0x1000 },
{ "spi0", make_spi, &mms->spi[0], 0x49202000, 0x1000, { 53 } },
{ "spi1", make_spi, &mms->spi[1], 0x49203000, 0x1000, { 54 } },
{ "spi2", make_spi, &mms->spi[2], 0x49204000, 0x1000, { 55 } },
{ "i2c2", make_i2c, &mms->i2c[2], 0x49205000, 0x1000 },
{ "i2c3", make_i2c, &mms->i2c[3], 0x49206000, 0x1000 },
{ /* port 7 reserved */ },
{ "i2c4", make_i2c, &mms->i2c[4], 0x49208000, 0x1000 },
},
}, {
.name = "apb_ppcexp2",
.ports = {
{ "scc", make_scc, &mms->scc, 0x49300000, 0x1000 },
{ "i2s-audio", make_unimp_dev, &mms->i2s_audio, 0x49301000, 0x1000 },
{ "fpgaio", make_fpgaio, &mms->fpgaio, 0x49302000, 0x1000 },
{ "uart0", make_uart, &mms->uart[0], 0x49303000, 0x1000, { 33, 34, 43 } },
{ "uart1", make_uart, &mms->uart[1], 0x49304000, 0x1000, { 35, 36, 44 } },
{ "uart2", make_uart, &mms->uart[2], 0x49305000, 0x1000, { 37, 38, 45 } },
{ "uart3", make_uart, &mms->uart[3], 0x49306000, 0x1000, { 39, 40, 46 } },
{ "uart4", make_uart, &mms->uart[4], 0x49307000, 0x1000, { 41, 42, 47 } },
{ "uart5", make_uart, &mms->uart[5], 0x49308000, 0x1000, { 125, 126, 127 } },
{ /* port 9 reserved */ },
{ "clcd", make_unimp_dev, &mms->cldc, 0x4930a000, 0x1000 },
{ "rtc", make_rtc, &mms->rtc, 0x4930b000, 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, { 49 } },
},
},
};
switch (mmc->fpga_type) {
case FPGA_AN505:
case FPGA_AN521:
@ -897,6 +1002,10 @@ static void mps2tz_common_init(MachineState *machine)
ppcs = an524_ppcs;
num_ppcs = ARRAY_SIZE(an524_ppcs);
break;
case FPGA_AN547:
ppcs = an547_ppcs;
num_ppcs = ARRAY_SIZE(an547_ppcs);
break;
default:
g_assert_not_reached();
}
@ -975,6 +1084,11 @@ static void mps2tz_common_init(MachineState *machine)
create_unimplemented_device("FPGA NS PC", 0x48007000, 0x1000);
if (mmc->fpga_type == FPGA_AN547) {
create_unimplemented_device("U55 timing adapter 0", 0x48102000, 0x1000);
create_unimplemented_device("U55 timing adapter 1", 0x48103000, 0x1000);
}
create_non_mpc_ram(mms);
armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename,
@ -1041,11 +1155,15 @@ static void mps2tz_an505_class_init(ObjectClass *oc, void *data)
mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m33");
mmc->scc_id = 0x41045050;
mmc->sysclk_frq = 20 * 1000 * 1000; /* 20MHz */
mmc->apb_periph_frq = mmc->sysclk_frq;
mmc->oscclk = an505_oscclk;
mmc->len_oscclk = ARRAY_SIZE(an505_oscclk);
mmc->fpgaio_num_leds = 2;
mmc->fpgaio_has_switches = false;
mmc->fpgaio_has_dbgctrl = false;
mmc->numirq = 92;
mmc->uart_overflow_irq = 47;
mmc->init_svtor = 0x10000000;
mmc->raminfo = an505_raminfo;
mmc->armsse_type = TYPE_IOTKIT;
mps2tz_set_default_ram_info(mmc);
@ -1064,11 +1182,15 @@ static void mps2tz_an521_class_init(ObjectClass *oc, void *data)
mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m33");
mmc->scc_id = 0x41045210;
mmc->sysclk_frq = 20 * 1000 * 1000; /* 20MHz */
mmc->apb_periph_frq = mmc->sysclk_frq;
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->fpgaio_has_dbgctrl = false;
mmc->numirq = 92;
mmc->uart_overflow_irq = 47;
mmc->init_svtor = 0x10000000;
mmc->raminfo = an505_raminfo; /* AN521 is the same as AN505 here */
mmc->armsse_type = TYPE_SSE200;
mps2tz_set_default_ram_info(mmc);
@ -1087,16 +1209,47 @@ static void mps3tz_an524_class_init(ObjectClass *oc, void *data)
mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m33");
mmc->scc_id = 0x41045240;
mmc->sysclk_frq = 32 * 1000 * 1000; /* 32MHz */
mmc->apb_periph_frq = mmc->sysclk_frq;
mmc->oscclk = an524_oscclk;
mmc->len_oscclk = ARRAY_SIZE(an524_oscclk);
mmc->fpgaio_num_leds = 10;
mmc->fpgaio_has_switches = true;
mmc->fpgaio_has_dbgctrl = false;
mmc->numirq = 95;
mmc->uart_overflow_irq = 47;
mmc->init_svtor = 0x10000000;
mmc->raminfo = an524_raminfo;
mmc->armsse_type = TYPE_SSE200;
mps2tz_set_default_ram_info(mmc);
}
static void mps3tz_an547_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
MPS2TZMachineClass *mmc = MPS2TZ_MACHINE_CLASS(oc);
mc->desc = "ARM MPS3 with AN547 FPGA image for Cortex-M55";
mc->default_cpus = 1;
mc->min_cpus = mc->default_cpus;
mc->max_cpus = mc->default_cpus;
mmc->fpga_type = FPGA_AN547;
mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m55");
mmc->scc_id = 0x41055470;
mmc->sysclk_frq = 32 * 1000 * 1000; /* 32MHz */
mmc->apb_periph_frq = 25 * 1000 * 1000; /* 25MHz */
mmc->oscclk = an524_oscclk; /* same as AN524 */
mmc->len_oscclk = ARRAY_SIZE(an524_oscclk);
mmc->fpgaio_num_leds = 10;
mmc->fpgaio_has_switches = true;
mmc->fpgaio_has_dbgctrl = true;
mmc->numirq = 96;
mmc->uart_overflow_irq = 48;
mmc->init_svtor = 0x00000000;
mmc->raminfo = an547_raminfo;
mmc->armsse_type = TYPE_SSE300;
mps2tz_set_default_ram_info(mmc);
}
static const TypeInfo mps2tz_info = {
.name = TYPE_MPS2TZ_MACHINE,
.parent = TYPE_MACHINE,
@ -1128,12 +1281,19 @@ static const TypeInfo mps3tz_an524_info = {
.class_init = mps3tz_an524_class_init,
};
static const TypeInfo mps3tz_an547_info = {
.name = TYPE_MPS3TZ_AN547_MACHINE,
.parent = TYPE_MPS2TZ_MACHINE,
.class_init = mps3tz_an547_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_register_static(&mps3tz_an547_info);
}
type_init(mps2tz_machine_init);

View File

@ -50,6 +50,7 @@
#define QSPI_ADDR 0xff0f0000
#define LQSPI_ADDR 0xc0000000
#define QSPI_IRQ 15
#define QSPI_DMA_ADDR 0xff0f0800
#define DP_ADDR 0xfd4a0000
#define DP_IRQ 113
@ -284,6 +285,8 @@ static void xlnx_zynqmp_init(Object *obj)
for (i = 0; i < XLNX_ZYNQMP_NUM_ADMA_CH; i++) {
object_initialize_child(obj, "adma[*]", &s->adma[i], TYPE_XLNX_ZDMA);
}
object_initialize_child(obj, "qspi-dma", &s->qspi_dma, TYPE_XLNX_CSU_DMA);
}
static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
@ -301,11 +304,13 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
ram_size = memory_region_size(s->ddr_ram);
/* Create the DDR Memory Regions. User friendly checks should happen at
/*
* Create the DDR Memory Regions. User friendly checks should happen at
* the board level
*/
if (ram_size > XLNX_ZYNQMP_MAX_LOW_RAM_SIZE) {
/* The RAM size is above the maximum available for the low DDR.
/*
* The RAM size is above the maximum available for the low DDR.
* Create the high DDR memory region as well.
*/
assert(ram_size <= XLNX_ZYNQMP_MAX_RAM_SIZE);
@ -521,7 +526,8 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
SysBusDevice *sbd = SYS_BUS_DEVICE(&s->sdhci[i]);
Object *sdhci = OBJECT(&s->sdhci[i]);
/* Compatible with:
/*
* Compatible with:
* - SD Host Controller Specification Version 3.00
* - SDIO Specification Version 3.0
* - eMMC Specification Version 4.51
@ -635,6 +641,15 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
sysbus_connect_irq(SYS_BUS_DEVICE(&s->adma[i]), 0,
gic_spi[adma_ch_intr[i]]);
}
if (!sysbus_realize(SYS_BUS_DEVICE(&s->qspi_dma), errp)) {
return;
}
sysbus_mmio_map(SYS_BUS_DEVICE(&s->qspi_dma), 0, QSPI_DMA_ADDR);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->qspi_dma), 0, gic_spi[QSPI_IRQ]);
object_property_set_link(OBJECT(&s->qspi), "stream-connected-dma",
OBJECT(&s->qspi_dma), errp);
}
static Property xlnx_zynqmp_props[] = {

View File

@ -519,7 +519,7 @@ static void cadence_uart_realize(DeviceState *dev, Error **errp)
uart_event, NULL, s, NULL, true);
}
static void cadence_uart_refclk_update(void *opaque)
static void cadence_uart_refclk_update(void *opaque, ClockEvent event)
{
CadenceUARTState *s = opaque;
@ -537,7 +537,7 @@ static void cadence_uart_init(Object *obj)
sysbus_init_irq(sbd, &s->irq);
s->refclk = qdev_init_clock_in(DEVICE(obj), "refclk",
cadence_uart_refclk_update, s);
cadence_uart_refclk_update, s, ClockUpdate);
/* initialize the frequency in case the clock remains unconnected */
clock_set_hz(s->refclk, UART_DEFAULT_REF_CLK);

View File

@ -396,7 +396,7 @@ static void ibex_uart_write(void *opaque, hwaddr addr,
}
}
static void ibex_uart_clk_update(void *opaque)
static void ibex_uart_clk_update(void *opaque, ClockEvent event)
{
IbexUartState *s = opaque;
@ -466,7 +466,7 @@ static void ibex_uart_init(Object *obj)
IbexUartState *s = IBEX_UART(obj);
s->f_clk = qdev_init_clock_in(DEVICE(obj), "f_clock",
ibex_uart_clk_update, s);
ibex_uart_clk_update, s, ClockUpdate);
clock_set_hz(s->f_clk, IBEX_UART_CLOCK);
sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->tx_watermark);

View File

@ -309,7 +309,7 @@ static void pl011_event(void *opaque, QEMUChrEvent event)
pl011_put_fifo(opaque, 0x400);
}
static void pl011_clock_update(void *opaque)
static void pl011_clock_update(void *opaque, ClockEvent event)
{
PL011State *s = PL011(opaque);
@ -378,7 +378,8 @@ static void pl011_init(Object *obj)
sysbus_init_irq(sbd, &s->irq[i]);
}
s->clk = qdev_init_clock_in(DEVICE(obj), "clk", pl011_clock_update, s);
s->clk = qdev_init_clock_in(DEVICE(obj), "clk", pl011_clock_update, s,
ClockUpdate);
s->read_trigger = 1;
s->ifl = 0x12;

View File

@ -39,15 +39,17 @@ Clock *clock_new(Object *parent, const char *name)
return clk;
}
void clock_set_callback(Clock *clk, ClockCallback *cb, void *opaque)
void clock_set_callback(Clock *clk, ClockCallback *cb, void *opaque,
unsigned int events)
{
clk->callback = cb;
clk->callback_opaque = opaque;
clk->callback_events = events;
}
void clock_clear_callback(Clock *clk)
{
clock_set_callback(clk, NULL, NULL);
clock_set_callback(clk, NULL, NULL, 0);
}
bool clock_set(Clock *clk, uint64_t period)
@ -62,18 +64,32 @@ bool clock_set(Clock *clk, uint64_t period)
return true;
}
static void clock_call_callback(Clock *clk, ClockEvent event)
{
/*
* Call the Clock's callback for this event, if it has one and
* is interested in this event.
*/
if (clk->callback && (clk->callback_events & event)) {
clk->callback(clk->callback_opaque, event);
}
}
static void clock_propagate_period(Clock *clk, bool call_callbacks)
{
Clock *child;
QLIST_FOREACH(child, &clk->children, sibling) {
if (child->period != clk->period) {
if (call_callbacks) {
clock_call_callback(child, ClockPreUpdate);
}
child->period = clk->period;
trace_clock_update(CLOCK_PATH(child), CLOCK_PATH(clk),
CLOCK_PERIOD_TO_HZ(clk->period),
call_callbacks);
if (call_callbacks && child->callback) {
child->callback(child->callback_opaque);
if (call_callbacks) {
clock_call_callback(child, ClockUpdate);
}
clock_propagate_period(child, call_callbacks);
}

View File

@ -111,7 +111,8 @@ Clock *qdev_init_clock_out(DeviceState *dev, const char *name)
}
Clock *qdev_init_clock_in(DeviceState *dev, const char *name,
ClockCallback *callback, void *opaque)
ClockCallback *callback, void *opaque,
unsigned int events)
{
NamedClockList *ncl;
@ -120,7 +121,7 @@ Clock *qdev_init_clock_in(DeviceState *dev, const char *name,
ncl = qdev_init_clocklist(dev, name, false, NULL);
if (callback) {
clock_set_callback(ncl->clock, callback, opaque);
clock_set_callback(ncl->clock, callback, opaque, events);
}
return ncl->clock;
}
@ -137,7 +138,8 @@ void qdev_init_clocks(DeviceState *dev, const ClockPortInitArray clocks)
if (elem->is_output) {
*clkp = qdev_init_clock_out(dev, elem->name);
} else {
*clkp = qdev_init_clock_in(dev, elem->name, elem->callback, dev);
*clkp = qdev_init_clock_in(dev, elem->name, elem->callback, dev,
elem->callback_events);
}
}
}

View File

@ -26,3 +26,7 @@ config STP2000
config SIFIVE_PDMA
bool
config XLNX_CSU_DMA
bool
select REGISTER

View File

@ -14,3 +14,4 @@ softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_dma.c', 'soc_dma.c'))
softmmu_ss.add(when: 'CONFIG_PXA2XX', if_true: files('pxa2xx_dma.c'))
softmmu_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_dma.c'))
softmmu_ss.add(when: 'CONFIG_SIFIVE_PDMA', if_true: files('sifive_pdma.c'))
softmmu_ss.add(when: 'CONFIG_XLNX_CSU_DMA', if_true: files('xlnx_csu_dma.c'))

745
hw/dma/xlnx_csu_dma.c Normal file
View File

@ -0,0 +1,745 @@
/*
* Xilinx Platform CSU Stream DMA emulation
*
* This implementation is based on
* https://github.com/Xilinx/qemu/blob/master/hw/dma/csu_stream_dma.c
*
* 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 or
* (at your option) version 3 of the License.
*
* 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/>.
*/
#include "qemu/osdep.h"
#include "qemu/log.h"
#include "qapi/error.h"
#include "hw/hw.h"
#include "hw/irq.h"
#include "hw/qdev-properties.h"
#include "hw/sysbus.h"
#include "migration/vmstate.h"
#include "sysemu/dma.h"
#include "hw/ptimer.h"
#include "hw/stream.h"
#include "hw/register.h"
#include "hw/dma/xlnx_csu_dma.h"
/*
* Ref: UG1087 (v1.7) February 8, 2019
* https://www.xilinx.com/html_docs/registers/ug1087/ug1087-zynq-ultrascale-registers.html
* CSUDMA Module section
*/
REG32(ADDR, 0x0)
FIELD(ADDR, ADDR, 2, 30) /* wo */
REG32(SIZE, 0x4)
FIELD(SIZE, SIZE, 2, 27) /* wo */
FIELD(SIZE, LAST_WORD, 0, 1) /* rw, only exists in SRC */
REG32(STATUS, 0x8)
FIELD(STATUS, DONE_CNT, 13, 3) /* wtc */
FIELD(STATUS, FIFO_LEVEL, 5, 8) /* ro */
FIELD(STATUS, OUTSTANDING, 1, 4) /* ro */
FIELD(STATUS, BUSY, 0, 1) /* ro */
REG32(CTRL, 0xc)
FIELD(CTRL, FIFOTHRESH, 25, 7) /* rw, only exists in DST, reset 0x40 */
FIELD(CTRL, APB_ERR_RESP, 24, 1) /* rw */
FIELD(CTRL, ENDIANNESS, 23, 1) /* rw */
FIELD(CTRL, AXI_BRST_TYPE, 22, 1) /* rw */
FIELD(CTRL, TIMEOUT_VAL, 10, 12) /* rw, reset: 0xFFE */
FIELD(CTRL, FIFO_THRESH, 2, 8) /* rw, reset: 0x80 */
FIELD(CTRL, PAUSE_STRM, 1, 1) /* rw */
FIELD(CTRL, PAUSE_MEM, 0, 1) /* rw */
REG32(CRC, 0x10)
REG32(INT_STATUS, 0x14)
FIELD(INT_STATUS, FIFO_OVERFLOW, 7, 1) /* wtc */
FIELD(INT_STATUS, INVALID_APB, 6, 1) /* wtc */
FIELD(INT_STATUS, THRESH_HIT, 5, 1) /* wtc */
FIELD(INT_STATUS, TIMEOUT_MEM, 4, 1) /* wtc */
FIELD(INT_STATUS, TIMEOUT_STRM, 3, 1) /* wtc */
FIELD(INT_STATUS, AXI_BRESP_ERR, 2, 1) /* wtc, SRC: AXI_RDERR */
FIELD(INT_STATUS, DONE, 1, 1) /* wtc */
FIELD(INT_STATUS, MEM_DONE, 0, 1) /* wtc */
REG32(INT_ENABLE, 0x18)
FIELD(INT_ENABLE, FIFO_OVERFLOW, 7, 1) /* wtc */
FIELD(INT_ENABLE, INVALID_APB, 6, 1) /* wtc */
FIELD(INT_ENABLE, THRESH_HIT, 5, 1) /* wtc */
FIELD(INT_ENABLE, TIMEOUT_MEM, 4, 1) /* wtc */
FIELD(INT_ENABLE, TIMEOUT_STRM, 3, 1) /* wtc */
FIELD(INT_ENABLE, AXI_BRESP_ERR, 2, 1) /* wtc, SRC: AXI_RDERR */
FIELD(INT_ENABLE, DONE, 1, 1) /* wtc */
FIELD(INT_ENABLE, MEM_DONE, 0, 1) /* wtc */
REG32(INT_DISABLE, 0x1c)
FIELD(INT_DISABLE, FIFO_OVERFLOW, 7, 1) /* wtc */
FIELD(INT_DISABLE, INVALID_APB, 6, 1) /* wtc */
FIELD(INT_DISABLE, THRESH_HIT, 5, 1) /* wtc */
FIELD(INT_DISABLE, TIMEOUT_MEM, 4, 1) /* wtc */
FIELD(INT_DISABLE, TIMEOUT_STRM, 3, 1) /* wtc */
FIELD(INT_DISABLE, AXI_BRESP_ERR, 2, 1) /* wtc, SRC: AXI_RDERR */
FIELD(INT_DISABLE, DONE, 1, 1) /* wtc */
FIELD(INT_DISABLE, MEM_DONE, 0, 1) /* wtc */
REG32(INT_MASK, 0x20)
FIELD(INT_MASK, FIFO_OVERFLOW, 7, 1) /* ro, reset: 0x1 */
FIELD(INT_MASK, INVALID_APB, 6, 1) /* ro, reset: 0x1 */
FIELD(INT_MASK, THRESH_HIT, 5, 1) /* ro, reset: 0x1 */
FIELD(INT_MASK, TIMEOUT_MEM, 4, 1) /* ro, reset: 0x1 */
FIELD(INT_MASK, TIMEOUT_STRM, 3, 1) /* ro, reset: 0x1 */
FIELD(INT_MASK, AXI_BRESP_ERR, 2, 1) /* ro, reset: 0x1, SRC: AXI_RDERR */
FIELD(INT_MASK, DONE, 1, 1) /* ro, reset: 0x1 */
FIELD(INT_MASK, MEM_DONE, 0, 1) /* ro, reset: 0x1 */
REG32(CTRL2, 0x24)
FIELD(CTRL2, ARCACHE, 24, 3) /* rw */
FIELD(CTRL2, ROUTE_BIT, 23, 1) /* rw */
FIELD(CTRL2, TIMEOUT_EN, 22, 1) /* rw */
FIELD(CTRL2, TIMEOUT_PRE, 4, 12) /* rw, reset: 0xFFF */
FIELD(CTRL2, MAX_OUTS_CMDS, 0, 4) /* rw, reset: 0x8 */
REG32(ADDR_MSB, 0x28)
FIELD(ADDR_MSB, ADDR_MSB, 0, 17) /* wo */
#define R_CTRL_TIMEOUT_VAL_RESET (0xFFE)
#define R_CTRL_FIFO_THRESH_RESET (0x80)
#define R_CTRL_FIFOTHRESH_RESET (0x40)
#define R_CTRL2_TIMEOUT_PRE_RESET (0xFFF)
#define R_CTRL2_MAX_OUTS_CMDS_RESET (0x8)
#define XLNX_CSU_DMA_ERR_DEBUG (0)
#define XLNX_CSU_DMA_INT_R_MASK (0xff)
/* UG1807: Set the prescaler value for the timeout in clk (~2.5ns) cycles */
#define XLNX_CSU_DMA_TIMER_FREQ (400 * 1000 * 1000)
static bool xlnx_csu_dma_is_paused(XlnxCSUDMA *s)
{
bool paused;
paused = !!(s->regs[R_CTRL] & R_CTRL_PAUSE_STRM_MASK);
paused |= !!(s->regs[R_CTRL] & R_CTRL_PAUSE_MEM_MASK);
return paused;
}
static bool xlnx_csu_dma_get_eop(XlnxCSUDMA *s)
{
return s->r_size_last_word;
}
static bool xlnx_csu_dma_burst_is_fixed(XlnxCSUDMA *s)
{
return !!(s->regs[R_CTRL] & R_CTRL_AXI_BRST_TYPE_MASK);
}
static bool xlnx_csu_dma_timeout_enabled(XlnxCSUDMA *s)
{
return !!(s->regs[R_CTRL2] & R_CTRL2_TIMEOUT_EN_MASK);
}
static void xlnx_csu_dma_update_done_cnt(XlnxCSUDMA *s, int a)
{
int cnt;
/* Increase DONE_CNT */
cnt = ARRAY_FIELD_EX32(s->regs, STATUS, DONE_CNT) + a;
ARRAY_FIELD_DP32(s->regs, STATUS, DONE_CNT, cnt);
}
static void xlnx_csu_dma_data_process(XlnxCSUDMA *s, uint8_t *buf, uint32_t len)
{
uint32_t bswap;
uint32_t i;
bswap = s->regs[R_CTRL] & R_CTRL_ENDIANNESS_MASK;
if (s->is_dst && !bswap) {
/* Fast when ENDIANNESS cleared */
return;
}
for (i = 0; i < len; i += 4) {
uint8_t *b = &buf[i];
union {
uint8_t u8[4];
uint32_t u32;
} v = {
.u8 = { b[0], b[1], b[2], b[3] }
};
if (!s->is_dst) {
s->regs[R_CRC] += v.u32;
}
if (bswap) {
/*
* No point using bswap, we need to writeback
* into a potentially unaligned pointer.
*/
b[0] = v.u8[3];
b[1] = v.u8[2];
b[2] = v.u8[1];
b[3] = v.u8[0];
}
}
}
static void xlnx_csu_dma_update_irq(XlnxCSUDMA *s)
{
qemu_set_irq(s->irq, !!(s->regs[R_INT_STATUS] & ~s->regs[R_INT_MASK]));
}
/* len is in bytes */
static uint32_t xlnx_csu_dma_read(XlnxCSUDMA *s, uint8_t *buf, uint32_t len)
{
hwaddr addr = (hwaddr)s->regs[R_ADDR_MSB] << 32 | s->regs[R_ADDR];
MemTxResult result = MEMTX_OK;
if (xlnx_csu_dma_burst_is_fixed(s)) {
uint32_t i;
for (i = 0; i < len && (result == MEMTX_OK); i += s->width) {
uint32_t mlen = MIN(len - i, s->width);
result = address_space_rw(s->dma_as, addr, s->attr,
buf + i, mlen, false);
}
} else {
result = address_space_rw(s->dma_as, addr, s->attr, buf, len, false);
}
if (result == MEMTX_OK) {
xlnx_csu_dma_data_process(s, buf, len);
} else {
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad address " TARGET_FMT_plx
" for mem read", __func__, addr);
s->regs[R_INT_STATUS] |= R_INT_STATUS_AXI_BRESP_ERR_MASK;
xlnx_csu_dma_update_irq(s);
}
return len;
}
/* len is in bytes */
static uint32_t xlnx_csu_dma_write(XlnxCSUDMA *s, uint8_t *buf, uint32_t len)
{
hwaddr addr = (hwaddr)s->regs[R_ADDR_MSB] << 32 | s->regs[R_ADDR];
MemTxResult result = MEMTX_OK;
xlnx_csu_dma_data_process(s, buf, len);
if (xlnx_csu_dma_burst_is_fixed(s)) {
uint32_t i;
for (i = 0; i < len && (result == MEMTX_OK); i += s->width) {
uint32_t mlen = MIN(len - i, s->width);
result = address_space_rw(s->dma_as, addr, s->attr,
buf, mlen, true);
buf += mlen;
}
} else {
result = address_space_rw(s->dma_as, addr, s->attr, buf, len, true);
}
if (result != MEMTX_OK) {
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad address " TARGET_FMT_plx
" for mem write", __func__, addr);
s->regs[R_INT_STATUS] |= R_INT_STATUS_AXI_BRESP_ERR_MASK;
xlnx_csu_dma_update_irq(s);
}
return len;
}
static void xlnx_csu_dma_done(XlnxCSUDMA *s)
{
s->regs[R_STATUS] &= ~R_STATUS_BUSY_MASK;
s->regs[R_INT_STATUS] |= R_INT_STATUS_DONE_MASK;
if (!s->is_dst) {
s->regs[R_INT_STATUS] |= R_INT_STATUS_MEM_DONE_MASK;
}
xlnx_csu_dma_update_done_cnt(s, 1);
}
static uint32_t xlnx_csu_dma_advance(XlnxCSUDMA *s, uint32_t len)
{
uint32_t size = s->regs[R_SIZE];
hwaddr dst = (hwaddr)s->regs[R_ADDR_MSB] << 32 | s->regs[R_ADDR];
assert(len <= size);
size -= len;
s->regs[R_SIZE] = size;
if (!xlnx_csu_dma_burst_is_fixed(s)) {
dst += len;
s->regs[R_ADDR] = (uint32_t) dst;
s->regs[R_ADDR_MSB] = dst >> 32;
}
if (size == 0) {
xlnx_csu_dma_done(s);
}
return size;
}
static void xlnx_csu_dma_src_notify(void *opaque)
{
XlnxCSUDMA *s = XLNX_CSU_DMA(opaque);
unsigned char buf[4 * 1024];
size_t rlen = 0;
ptimer_transaction_begin(s->src_timer);
/* Stop the backpreassure timer */
ptimer_stop(s->src_timer);
while (s->regs[R_SIZE] && !xlnx_csu_dma_is_paused(s) &&
stream_can_push(s->tx_dev, xlnx_csu_dma_src_notify, s)) {
uint32_t plen = MIN(s->regs[R_SIZE], sizeof buf);
bool eop = false;
/* Did we fit it all? */
if (s->regs[R_SIZE] == plen && xlnx_csu_dma_get_eop(s)) {
eop = true;
}
/* DMA transfer */
xlnx_csu_dma_read(s, buf, plen);
rlen = stream_push(s->tx_dev, buf, plen, eop);
xlnx_csu_dma_advance(s, rlen);
}
if (xlnx_csu_dma_timeout_enabled(s) && s->regs[R_SIZE] &&
!stream_can_push(s->tx_dev, xlnx_csu_dma_src_notify, s)) {
uint32_t timeout = ARRAY_FIELD_EX32(s->regs, CTRL, TIMEOUT_VAL);
uint32_t div = ARRAY_FIELD_EX32(s->regs, CTRL2, TIMEOUT_PRE) + 1;
uint32_t freq = XLNX_CSU_DMA_TIMER_FREQ;
freq /= div;
ptimer_set_freq(s->src_timer, freq);
ptimer_set_count(s->src_timer, timeout);
ptimer_run(s->src_timer, 1);
}
ptimer_transaction_commit(s->src_timer);
xlnx_csu_dma_update_irq(s);
}
static uint64_t addr_pre_write(RegisterInfo *reg, uint64_t val)
{
/* Address is word aligned */
return val & R_ADDR_ADDR_MASK;
}
static uint64_t size_pre_write(RegisterInfo *reg, uint64_t val)
{
XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque);
if (s->regs[R_SIZE] != 0) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: Starting DMA while already running.\n", __func__);
}
if (!s->is_dst) {
s->r_size_last_word = !!(val & R_SIZE_LAST_WORD_MASK);
}
/* Size is word aligned */
return val & R_SIZE_SIZE_MASK;
}
static uint64_t size_post_read(RegisterInfo *reg, uint64_t val)
{
XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque);
return val | s->r_size_last_word;
}
static void size_post_write(RegisterInfo *reg, uint64_t val)
{
XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque);
s->regs[R_STATUS] |= R_STATUS_BUSY_MASK;
/*
* Note that if SIZE is programmed to 0, and the DMA is started,
* the interrupts DONE and MEM_DONE will be asserted.
*/
if (s->regs[R_SIZE] == 0) {
xlnx_csu_dma_done(s);
xlnx_csu_dma_update_irq(s);
return;
}
/* Set SIZE is considered the last step in transfer configuration */
if (!s->is_dst) {
xlnx_csu_dma_src_notify(s);
} else {
if (s->notify) {
s->notify(s->notify_opaque);
}
}
}
static uint64_t status_pre_write(RegisterInfo *reg, uint64_t val)
{
return val & (R_STATUS_DONE_CNT_MASK | R_STATUS_BUSY_MASK);
}
static void ctrl_post_write(RegisterInfo *reg, uint64_t val)
{
XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque);
if (!s->is_dst) {
if (!xlnx_csu_dma_is_paused(s)) {
xlnx_csu_dma_src_notify(s);
}
} else {
if (!xlnx_csu_dma_is_paused(s) && s->notify) {
s->notify(s->notify_opaque);
}
}
}
static uint64_t int_status_pre_write(RegisterInfo *reg, uint64_t val)
{
XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque);
/* DMA counter decrements when flag 'DONE' is cleared */
if ((val & s->regs[R_INT_STATUS] & R_INT_STATUS_DONE_MASK)) {
xlnx_csu_dma_update_done_cnt(s, -1);
}
return s->regs[R_INT_STATUS] & ~val;
}
static void int_status_post_write(RegisterInfo *reg, uint64_t val)
{
XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque);
xlnx_csu_dma_update_irq(s);
}
static uint64_t int_enable_pre_write(RegisterInfo *reg, uint64_t val)
{
XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque);
uint32_t v32 = val;
/*
* R_INT_ENABLE doesn't have its own state.
* It is used to indirectly modify R_INT_MASK.
*
* 1: Enable this interrupt field (the mask bit will be cleared to 0)
* 0: No effect
*/
s->regs[R_INT_MASK] &= ~v32;
return 0;
}
static void int_enable_post_write(RegisterInfo *reg, uint64_t val)
{
XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque);
xlnx_csu_dma_update_irq(s);
}
static uint64_t int_disable_pre_write(RegisterInfo *reg, uint64_t val)
{
XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque);
uint32_t v32 = val;
/*
* R_INT_DISABLE doesn't have its own state.
* It is used to indirectly modify R_INT_MASK.
*
* 1: Disable this interrupt field (the mask bit will be set to 1)
* 0: No effect
*/
s->regs[R_INT_MASK] |= v32;
return 0;
}
static void int_disable_post_write(RegisterInfo *reg, uint64_t val)
{
XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque);
xlnx_csu_dma_update_irq(s);
}
static uint64_t addr_msb_pre_write(RegisterInfo *reg, uint64_t val)
{
return val & R_ADDR_MSB_ADDR_MSB_MASK;
}
static const RegisterAccessInfo *xlnx_csu_dma_regs_info[] = {
#define DMACH_REGINFO(NAME, snd) \
(const RegisterAccessInfo []) { \
{ \
.name = #NAME "_ADDR", \
.addr = A_ADDR, \
.pre_write = addr_pre_write \
}, { \
.name = #NAME "_SIZE", \
.addr = A_SIZE, \
.pre_write = size_pre_write, \
.post_write = size_post_write, \
.post_read = size_post_read \
}, { \
.name = #NAME "_STATUS", \
.addr = A_STATUS, \
.pre_write = status_pre_write, \
.w1c = R_STATUS_DONE_CNT_MASK, \
.ro = (R_STATUS_BUSY_MASK \
| R_STATUS_FIFO_LEVEL_MASK \
| R_STATUS_OUTSTANDING_MASK) \
}, { \
.name = #NAME "_CTRL", \
.addr = A_CTRL, \
.post_write = ctrl_post_write, \
.reset = ((R_CTRL_TIMEOUT_VAL_RESET << R_CTRL_TIMEOUT_VAL_SHIFT) \
| (R_CTRL_FIFO_THRESH_RESET << R_CTRL_FIFO_THRESH_SHIFT)\
| (snd ? 0 : R_CTRL_FIFOTHRESH_RESET \
<< R_CTRL_FIFOTHRESH_SHIFT)) \
}, { \
.name = #NAME "_CRC", \
.addr = A_CRC, \
}, { \
.name = #NAME "_INT_STATUS", \
.addr = A_INT_STATUS, \
.pre_write = int_status_pre_write, \
.post_write = int_status_post_write \
}, { \
.name = #NAME "_INT_ENABLE", \
.addr = A_INT_ENABLE, \
.pre_write = int_enable_pre_write, \
.post_write = int_enable_post_write \
}, { \
.name = #NAME "_INT_DISABLE", \
.addr = A_INT_DISABLE, \
.pre_write = int_disable_pre_write, \
.post_write = int_disable_post_write \
}, { \
.name = #NAME "_INT_MASK", \
.addr = A_INT_MASK, \
.ro = ~0, \
.reset = XLNX_CSU_DMA_INT_R_MASK \
}, { \
.name = #NAME "_CTRL2", \
.addr = A_CTRL2, \
.reset = ((R_CTRL2_TIMEOUT_PRE_RESET \
<< R_CTRL2_TIMEOUT_PRE_SHIFT) \
| (R_CTRL2_MAX_OUTS_CMDS_RESET \
<< R_CTRL2_MAX_OUTS_CMDS_SHIFT)) \
}, { \
.name = #NAME "_ADDR_MSB", \
.addr = A_ADDR_MSB, \
.pre_write = addr_msb_pre_write \
} \
}
DMACH_REGINFO(DMA_SRC, true),
DMACH_REGINFO(DMA_DST, false)
};
static const MemoryRegionOps xlnx_csu_dma_ops = {
.read = register_read_memory,
.write = register_write_memory,
.endianness = DEVICE_LITTLE_ENDIAN,
.valid = {
.min_access_size = 4,
.max_access_size = 4,
}
};
static void xlnx_csu_dma_src_timeout_hit(void *opaque)
{
XlnxCSUDMA *s = XLNX_CSU_DMA(opaque);
/* Ignore if the timeout is masked */
if (!xlnx_csu_dma_timeout_enabled(s)) {
return;
}
s->regs[R_INT_STATUS] |= R_INT_STATUS_TIMEOUT_STRM_MASK;
xlnx_csu_dma_update_irq(s);
}
static size_t xlnx_csu_dma_stream_push(StreamSink *obj, uint8_t *buf,
size_t len, bool eop)
{
XlnxCSUDMA *s = XLNX_CSU_DMA(obj);
uint32_t size = s->regs[R_SIZE];
uint32_t mlen = MIN(size, len) & (~3); /* Size is word aligned */
/* Be called when it's DST */
assert(s->is_dst);
if (size == 0 || len <= 0) {
return 0;
}
if (len && (xlnx_csu_dma_is_paused(s) || mlen == 0)) {
qemu_log_mask(LOG_GUEST_ERROR,
"csu-dma: DST channel dropping %zd b of data.\n", len);
s->regs[R_INT_STATUS] |= R_INT_STATUS_FIFO_OVERFLOW_MASK;
return len;
}
if (xlnx_csu_dma_write(s, buf, mlen) != mlen) {
return 0;
}
xlnx_csu_dma_advance(s, mlen);
xlnx_csu_dma_update_irq(s);
return mlen;
}
static bool xlnx_csu_dma_stream_can_push(StreamSink *obj,
StreamCanPushNotifyFn notify,
void *notify_opaque)
{
XlnxCSUDMA *s = XLNX_CSU_DMA(obj);
if (s->regs[R_SIZE] != 0) {
return true;
} else {
s->notify = notify;
s->notify_opaque = notify_opaque;
return false;
}
}
static void xlnx_csu_dma_reset(DeviceState *dev)
{
XlnxCSUDMA *s = XLNX_CSU_DMA(dev);
unsigned int i;
for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) {
register_reset(&s->regs_info[i]);
}
}
static void xlnx_csu_dma_realize(DeviceState *dev, Error **errp)
{
XlnxCSUDMA *s = XLNX_CSU_DMA(dev);
RegisterInfoArray *reg_array;
reg_array =
register_init_block32(dev, xlnx_csu_dma_regs_info[!!s->is_dst],
XLNX_CSU_DMA_R_MAX,
s->regs_info, s->regs,
&xlnx_csu_dma_ops,
XLNX_CSU_DMA_ERR_DEBUG,
XLNX_CSU_DMA_R_MAX * 4);
memory_region_add_subregion(&s->iomem,
0x0,
&reg_array->mem);
sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem);
sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq);
if (!s->is_dst && !s->tx_dev) {
error_setg(errp, "zynqmp.csu-dma: Stream not connected");
return;
}
s->src_timer = ptimer_init(xlnx_csu_dma_src_timeout_hit,
s, PTIMER_POLICY_DEFAULT);
if (s->dma_mr) {
s->dma_as = g_malloc0(sizeof(AddressSpace));
address_space_init(s->dma_as, s->dma_mr, NULL);
} else {
s->dma_as = &address_space_memory;
}
s->attr = MEMTXATTRS_UNSPECIFIED;
s->r_size_last_word = 0;
}
static const VMStateDescription vmstate_xlnx_csu_dma = {
.name = TYPE_XLNX_CSU_DMA,
.version_id = 0,
.minimum_version_id = 0,
.minimum_version_id_old = 0,
.fields = (VMStateField[]) {
VMSTATE_PTIMER(src_timer, XlnxCSUDMA),
VMSTATE_UINT16(width, XlnxCSUDMA),
VMSTATE_BOOL(is_dst, XlnxCSUDMA),
VMSTATE_BOOL(r_size_last_word, XlnxCSUDMA),
VMSTATE_UINT32_ARRAY(regs, XlnxCSUDMA, XLNX_CSU_DMA_R_MAX),
VMSTATE_END_OF_LIST(),
}
};
static Property xlnx_csu_dma_properties[] = {
/*
* Ref PG021, Stream Data Width:
* Data width in bits of the AXI S2MM AXI4-Stream Data bus.
* This value must be equal or less than the Memory Map Data Width.
* Valid values are 8, 16, 32, 64, 128, 512 and 1024.
* "dma-width" is the byte value of the "Stream Data Width".
*/
DEFINE_PROP_UINT16("dma-width", XlnxCSUDMA, width, 4),
/*
* The CSU DMA is a two-channel, simple DMA, allowing separate control of
* the SRC (read) channel and DST (write) channel. "is-dst" is used to mark
* which channel the device is connected to.
*/
DEFINE_PROP_BOOL("is-dst", XlnxCSUDMA, is_dst, true),
DEFINE_PROP_END_OF_LIST(),
};
static void xlnx_csu_dma_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
StreamSinkClass *ssc = STREAM_SINK_CLASS(klass);
dc->reset = xlnx_csu_dma_reset;
dc->realize = xlnx_csu_dma_realize;
dc->vmsd = &vmstate_xlnx_csu_dma;
device_class_set_props(dc, xlnx_csu_dma_properties);
ssc->push = xlnx_csu_dma_stream_push;
ssc->can_push = xlnx_csu_dma_stream_can_push;
}
static void xlnx_csu_dma_init(Object *obj)
{
XlnxCSUDMA *s = XLNX_CSU_DMA(obj);
memory_region_init(&s->iomem, obj, TYPE_XLNX_CSU_DMA,
XLNX_CSU_DMA_R_MAX * 4);
object_property_add_link(obj, "stream-connected-dma", TYPE_STREAM_SINK,
(Object **)&s->tx_dev,
qdev_prop_allow_set_link_before_realize,
OBJ_PROP_LINK_STRONG);
object_property_add_link(obj, "dma", TYPE_MEMORY_REGION,
(Object **)&s->dma_mr,
qdev_prop_allow_set_link_before_realize,
OBJ_PROP_LINK_STRONG);
}
static const TypeInfo xlnx_csu_dma_info = {
.name = TYPE_XLNX_CSU_DMA,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(XlnxCSUDMA),
.class_init = xlnx_csu_dma_class_init,
.instance_init = xlnx_csu_dma_init,
.interfaces = (InterfaceInfo[]) {
{ TYPE_STREAM_SINK },
{ }
}
};
static void xlnx_csu_dma_register_types(void)
{
type_register_static(&xlnx_csu_dma_info);
}
type_init(xlnx_csu_dma_register_types)

View File

@ -39,7 +39,7 @@ static void mips_cps_init(Object *obj)
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
MIPSCPSState *s = MIPS_CPS(obj);
s->clock = qdev_init_clock_in(DEVICE(obj), "clk-in", NULL, NULL);
s->clock = qdev_init_clock_in(DEVICE(obj), "clk-in", NULL, NULL, 0);
/*
* Cover entire address space as there do not seem to be any
* constraints for the base address of CPC and GIC.

View File

@ -2,6 +2,15 @@ config APPLESMC
bool
depends on ISA_BUS
config ARMSSE_CPUID
bool
config ARMSSE_MHU
bool
config ARMSSE_CPU_PWRCTRL
bool
config MAX111X
bool

View File

@ -0,0 +1,149 @@
/*
* Arm SSE CPU PWRCTRL register block
*
* Copyright (c) 2021 Linaro Limited
* Written by Peter Maydell
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 or
* (at your option) any later version.
*/
/*
* This is a model of the "CPU<N>_PWRCTRL block" which is part of the
* Arm Corstone SSE-300 Example Subsystem and documented in
* https://developer.arm.com/documentation/101773/0000
*/
#include "qemu/osdep.h"
#include "qemu/log.h"
#include "qemu/module.h"
#include "trace.h"
#include "qapi/error.h"
#include "migration/vmstate.h"
#include "hw/sysbus.h"
#include "hw/registerfields.h"
#include "hw/misc/armsse-cpu-pwrctrl.h"
REG32(CPUPWRCFG, 0x0)
REG32(PID4, 0xfd0)
REG32(PID5, 0xfd4)
REG32(PID6, 0xfd8)
REG32(PID7, 0xfdc)
REG32(PID0, 0xfe0)
REG32(PID1, 0xfe4)
REG32(PID2, 0xfe8)
REG32(PID3, 0xfec)
REG32(CID0, 0xff0)
REG32(CID1, 0xff4)
REG32(CID2, 0xff8)
REG32(CID3, 0xffc)
/* PID/CID values */
static const int cpu_pwrctrl_id[] = {
0x04, 0x00, 0x00, 0x00, /* PID4..PID7 */
0x5a, 0xb8, 0x0b, 0x00, /* PID0..PID3 */
0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */
};
static uint64_t pwrctrl_read(void *opaque, hwaddr offset, unsigned size)
{
ARMSSECPUPwrCtrl *s = ARMSSE_CPU_PWRCTRL(opaque);
uint64_t r;
switch (offset) {
case A_CPUPWRCFG:
r = s->cpupwrcfg;
break;
case A_PID4 ... A_CID3:
r = cpu_pwrctrl_id[(offset - A_PID4) / 4];
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"SSE CPU_PWRCTRL read: bad offset %x\n", (int)offset);
r = 0;
break;
}
trace_armsse_cpu_pwrctrl_read(offset, r, size);
return r;
}
static void pwrctrl_write(void *opaque, hwaddr offset,
uint64_t value, unsigned size)
{
ARMSSECPUPwrCtrl *s = ARMSSE_CPU_PWRCTRL(opaque);
trace_armsse_cpu_pwrctrl_write(offset, value, size);
switch (offset) {
case A_CPUPWRCFG:
qemu_log_mask(LOG_UNIMP,
"SSE CPU_PWRCTRL: CPUPWRCFG unimplemented\n");
s->cpupwrcfg = value;
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"SSE CPU_PWRCTRL write: bad offset 0x%x\n", (int)offset);
break;
}
}
static const MemoryRegionOps pwrctrl_ops = {
.read = pwrctrl_read,
.write = pwrctrl_write,
.endianness = DEVICE_LITTLE_ENDIAN,
.impl.min_access_size = 4,
.impl.max_access_size = 4,
.valid.min_access_size = 4,
.valid.max_access_size = 4,
};
static void pwrctrl_reset(DeviceState *dev)
{
ARMSSECPUPwrCtrl *s = ARMSSE_CPU_PWRCTRL(dev);
s->cpupwrcfg = 0;
}
static const VMStateDescription pwrctrl_vmstate = {
.name = "armsse-cpu-pwrctrl",
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT32(cpupwrcfg, ARMSSECPUPwrCtrl),
VMSTATE_END_OF_LIST()
},
};
static void pwrctrl_init(Object *obj)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
ARMSSECPUPwrCtrl *s = ARMSSE_CPU_PWRCTRL(obj);
memory_region_init_io(&s->iomem, obj, &pwrctrl_ops,
s, "armsse-cpu-pwrctrl", 0x1000);
sysbus_init_mmio(sbd, &s->iomem);
}
static void pwrctrl_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->reset = pwrctrl_reset;
dc->vmsd = &pwrctrl_vmstate;
}
static const TypeInfo pwrctrl_info = {
.name = TYPE_ARMSSE_CPU_PWRCTRL,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(ARMSSECPUPwrCtrl),
.instance_init = pwrctrl_init,
.class_init = pwrctrl_class_init,
};
static void pwrctrl_register_types(void)
{
type_register_static(&pwrctrl_info);
}
type_init(pwrctrl_register_types);

View File

@ -107,7 +107,7 @@ static void pll_update(CprmanPllState *pll)
clock_update_hz(pll->out, freq);
}
static void pll_xosc_update(void *opaque)
static void pll_xosc_update(void *opaque, ClockEvent event)
{
pll_update(CPRMAN_PLL(opaque));
}
@ -116,7 +116,8 @@ static void pll_init(Object *obj)
{
CprmanPllState *s = CPRMAN_PLL(obj);
s->xosc_in = qdev_init_clock_in(DEVICE(s), "xosc-in", pll_xosc_update, s);
s->xosc_in = qdev_init_clock_in(DEVICE(s), "xosc-in", pll_xosc_update,
s, ClockUpdate);
s->out = qdev_init_clock_out(DEVICE(s), "out");
}
@ -209,7 +210,7 @@ static void pll_update_all_channels(BCM2835CprmanState *s,
}
}
static void pll_channel_pll_in_update(void *opaque)
static void pll_channel_pll_in_update(void *opaque, ClockEvent event)
{
pll_channel_update(CPRMAN_PLL_CHANNEL(opaque));
}
@ -219,7 +220,8 @@ static void pll_channel_init(Object *obj)
CprmanPllChannelState *s = CPRMAN_PLL_CHANNEL(obj);
s->pll_in = qdev_init_clock_in(DEVICE(s), "pll-in",
pll_channel_pll_in_update, s);
pll_channel_pll_in_update, s,
ClockUpdate);
s->out = qdev_init_clock_out(DEVICE(s), "out");
}
@ -303,7 +305,7 @@ static void clock_mux_update(CprmanClockMuxState *mux)
clock_update_hz(mux->out, freq);
}
static void clock_mux_src_update(void *opaque)
static void clock_mux_src_update(void *opaque, ClockEvent event)
{
CprmanClockMuxState **backref = opaque;
CprmanClockMuxState *s = *backref;
@ -335,7 +337,8 @@ static void clock_mux_init(Object *obj)
s->backref[i] = s;
s->srcs[i] = qdev_init_clock_in(DEVICE(s), name,
clock_mux_src_update,
&s->backref[i]);
&s->backref[i],
ClockUpdate);
g_free(name);
}
@ -380,7 +383,7 @@ static void dsi0hsck_mux_update(CprmanDsi0HsckMuxState *s)
clock_update(s->out, clock_get(src));
}
static void dsi0hsck_mux_in_update(void *opaque)
static void dsi0hsck_mux_in_update(void *opaque, ClockEvent event)
{
dsi0hsck_mux_update(CPRMAN_DSI0HSCK_MUX(opaque));
}
@ -390,8 +393,10 @@ static void dsi0hsck_mux_init(Object *obj)
CprmanDsi0HsckMuxState *s = CPRMAN_DSI0HSCK_MUX(obj);
DeviceState *dev = DEVICE(obj);
s->plla_in = qdev_init_clock_in(dev, "plla-in", dsi0hsck_mux_in_update, s);
s->plld_in = qdev_init_clock_in(dev, "plld-in", dsi0hsck_mux_in_update, s);
s->plla_in = qdev_init_clock_in(dev, "plla-in", dsi0hsck_mux_in_update,
s, ClockUpdate);
s->plld_in = qdev_init_clock_in(dev, "plld-in", dsi0hsck_mux_in_update,
s, ClockUpdate);
s->out = qdev_init_clock_out(DEVICE(s), "out");
}

View File

@ -19,6 +19,8 @@
#include "hw/registerfields.h"
#include "hw/irq.h"
#include "hw/misc/iotkit-secctl.h"
#include "hw/arm/armsse-version.h"
#include "hw/qdev-properties.h"
/* Registers in the secure privilege control block */
REG32(SECRESPCFG, 0x10)
@ -95,6 +97,19 @@ static const uint8_t iotkit_secctl_ns_idregs[] = {
0x0d, 0xf0, 0x05, 0xb1,
};
static const uint8_t iotkit_secctl_s_sse300_idregs[] = {
0x04, 0x00, 0x00, 0x00,
0x52, 0xb8, 0x2b, 0x00,
0x0d, 0xf0, 0x05, 0xb1,
};
static const uint8_t iotkit_secctl_ns_sse300_idregs[] = {
0x04, 0x00, 0x00, 0x00,
0x53, 0xb8, 0x2b, 0x00,
0x0d, 0xf0, 0x05, 0xb1,
};
/* The register sets for the various PPCs (AHB internal, APB internal,
* AHB expansion, APB expansion) are all set up so that they are
* in 16-aligned blocks so offsets 0xN0, 0xN4, 0xN8, 0xNC are PPCs
@ -213,7 +228,14 @@ static MemTxResult iotkit_secctl_s_read(void *opaque, hwaddr addr,
case A_CID1:
case A_CID2:
case A_CID3:
r = iotkit_secctl_s_idregs[(offset - A_PID4) / 4];
switch (s->sse_version) {
case ARMSSE_SSE300:
r = iotkit_secctl_s_sse300_idregs[(offset - A_PID4) / 4];
break;
default:
r = iotkit_secctl_s_idregs[(offset - A_PID4) / 4];
break;
}
break;
case A_SECPPCINTCLR:
case A_SECMSCINTCLR:
@ -473,7 +495,14 @@ static MemTxResult iotkit_secctl_ns_read(void *opaque, hwaddr addr,
case A_CID1:
case A_CID2:
case A_CID3:
r = iotkit_secctl_ns_idregs[(offset - A_PID4) / 4];
switch (s->sse_version) {
case ARMSSE_SSE300:
r = iotkit_secctl_ns_sse300_idregs[(offset - A_PID4) / 4];
break;
default:
r = iotkit_secctl_ns_idregs[(offset - A_PID4) / 4];
break;
}
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
@ -710,6 +739,16 @@ static void iotkit_secctl_init(Object *obj)
sysbus_init_mmio(sbd, &s->ns_regs);
}
static void iotkit_secctl_realize(DeviceState *dev, Error **errp)
{
IoTKitSecCtl *s = IOTKIT_SECCTL(dev);
if (!armsse_version_valid(s->sse_version)) {
error_setg(errp, "invalid sse-version value %d", s->sse_version);
return;
}
}
static const VMStateDescription iotkit_secctl_ppc_vmstate = {
.name = "iotkit-secctl-ppc",
.version_id = 1,
@ -775,12 +814,19 @@ static const VMStateDescription iotkit_secctl_vmstate = {
},
};
static Property iotkit_secctl_props[] = {
DEFINE_PROP_UINT32("sse-version", IoTKitSecCtl, sse_version, 0),
DEFINE_PROP_END_OF_LIST()
};
static void iotkit_secctl_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->vmsd = &iotkit_secctl_vmstate;
dc->reset = iotkit_secctl_reset;
dc->realize = iotkit_secctl_realize;
device_class_set_props(dc, iotkit_secctl_props);
}
static const TypeInfo iotkit_secctl_info = {

View File

@ -28,6 +28,7 @@
#include "hw/registerfields.h"
#include "hw/misc/iotkit-sysctl.h"
#include "hw/qdev-properties.h"
#include "hw/arm/armsse-version.h"
#include "target/arm/arm-powerctl.h"
#include "target/arm/cpu.h"
@ -44,16 +45,22 @@ REG32(SWRESET, 0x108)
FIELD(SWRESET, SWRESETREQ, 9, 1)
REG32(GRETREG, 0x10c)
REG32(INITSVTOR0, 0x110)
FIELD(INITSVTOR0, LOCK, 0, 1)
FIELD(INITSVTOR0, VTOR, 7, 25)
REG32(INITSVTOR1, 0x114)
REG32(CPUWAIT, 0x118)
REG32(NMI_ENABLE, 0x11c) /* BUSWAIT in IoTKit */
REG32(WICCTRL, 0x120)
REG32(EWCTRL, 0x124)
REG32(PWRCTRL, 0x1fc)
FIELD(PWRCTRL, PPU_ACCESS_UNLOCK, 0, 1)
FIELD(PWRCTRL, PPU_ACCESS_FILTER, 1, 1)
REG32(PDCM_PD_SYS_SENSE, 0x200)
REG32(PDCM_PD_CPU0_SENSE, 0x204)
REG32(PDCM_PD_SRAM0_SENSE, 0x20c)
REG32(PDCM_PD_SRAM1_SENSE, 0x210)
REG32(PDCM_PD_SRAM2_SENSE, 0x214)
REG32(PDCM_PD_SRAM3_SENSE, 0x218)
REG32(PDCM_PD_SRAM2_SENSE, 0x214) /* PDCM_PD_VMR0_SENSE on SSE300 */
REG32(PDCM_PD_SRAM3_SENSE, 0x218) /* PDCM_PD_VMR1_SENSE on SSE300 */
REG32(PID4, 0xfd0)
REG32(PID5, 0xfd4)
REG32(PID6, 0xfd8)
@ -68,12 +75,19 @@ REG32(CID2, 0xff8)
REG32(CID3, 0xffc)
/* PID/CID values */
static const int sysctl_id[] = {
static const int iotkit_sysctl_id[] = {
0x04, 0x00, 0x00, 0x00, /* PID4..PID7 */
0x54, 0xb8, 0x0b, 0x00, /* PID0..PID3 */
0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */
};
/* Also used by the SSE300 */
static const int sse200_sysctl_id[] = {
0x04, 0x00, 0x00, 0x00, /* PID4..PID7 */
0x54, 0xb8, 0x1b, 0x00, /* PID0..PID3 */
0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */
};
/*
* Set the initial secure vector table offset address for the core.
* This will take effect when the CPU next resets.
@ -100,28 +114,52 @@ static uint64_t iotkit_sysctl_read(void *opaque, hwaddr offset,
r = s->secure_debug;
break;
case A_SCSECCTRL:
if (!s->is_sse200) {
switch (s->sse_version) {
case ARMSSE_IOTKIT:
goto bad_offset;
case ARMSSE_SSE200:
case ARMSSE_SSE300:
r = s->scsecctrl;
break;
default:
g_assert_not_reached();
}
r = s->scsecctrl;
break;
case A_FCLK_DIV:
if (!s->is_sse200) {
switch (s->sse_version) {
case ARMSSE_IOTKIT:
goto bad_offset;
case ARMSSE_SSE200:
case ARMSSE_SSE300:
r = s->fclk_div;
break;
default:
g_assert_not_reached();
}
r = s->fclk_div;
break;
case A_SYSCLK_DIV:
if (!s->is_sse200) {
switch (s->sse_version) {
case ARMSSE_IOTKIT:
goto bad_offset;
case ARMSSE_SSE200:
case ARMSSE_SSE300:
r = s->sysclk_div;
break;
default:
g_assert_not_reached();
}
r = s->sysclk_div;
break;
case A_CLOCK_FORCE:
if (!s->is_sse200) {
switch (s->sse_version) {
case ARMSSE_IOTKIT:
goto bad_offset;
case ARMSSE_SSE200:
case ARMSSE_SSE300:
r = s->clock_force;
break;
default:
g_assert_not_reached();
}
r = s->clock_force;
break;
case A_RESET_SYNDROME:
r = s->reset_syndrome;
@ -136,63 +174,178 @@ static uint64_t iotkit_sysctl_read(void *opaque, hwaddr offset,
r = s->initsvtor0;
break;
case A_INITSVTOR1:
if (!s->is_sse200) {
switch (s->sse_version) {
case ARMSSE_IOTKIT:
goto bad_offset;
case ARMSSE_SSE200:
r = s->initsvtor1;
break;
case ARMSSE_SSE300:
goto bad_offset;
default:
g_assert_not_reached();
}
r = s->initsvtor1;
break;
case A_CPUWAIT:
r = s->cpuwait;
switch (s->sse_version) {
case ARMSSE_IOTKIT:
case ARMSSE_SSE200:
r = s->cpuwait;
break;
case ARMSSE_SSE300:
/* In SSE300 this is reserved (for INITSVTOR2) */
goto bad_offset;
default:
g_assert_not_reached();
}
break;
case A_NMI_ENABLE:
/* In IoTKit this is named BUSWAIT but is marked reserved, R/O, zero */
if (!s->is_sse200) {
switch (s->sse_version) {
case ARMSSE_IOTKIT:
/* In IoTKit this is named BUSWAIT but marked reserved, R/O, zero */
r = 0;
break;
case ARMSSE_SSE200:
r = s->nmi_enable;
break;
case ARMSSE_SSE300:
/* In SSE300 this is reserved (for INITSVTOR3) */
goto bad_offset;
default:
g_assert_not_reached();
}
r = s->nmi_enable;
break;
case A_WICCTRL:
r = s->wicctrl;
switch (s->sse_version) {
case ARMSSE_IOTKIT:
case ARMSSE_SSE200:
r = s->wicctrl;
break;
case ARMSSE_SSE300:
/* In SSE300 this offset is CPUWAIT */
r = s->cpuwait;
break;
default:
g_assert_not_reached();
}
break;
case A_EWCTRL:
if (!s->is_sse200) {
switch (s->sse_version) {
case ARMSSE_IOTKIT:
goto bad_offset;
case ARMSSE_SSE200:
r = s->ewctrl;
break;
case ARMSSE_SSE300:
/* In SSE300 this offset is is NMI_ENABLE */
r = s->nmi_enable;
break;
default:
g_assert_not_reached();
}
break;
case A_PWRCTRL:
switch (s->sse_version) {
case ARMSSE_IOTKIT:
case ARMSSE_SSE200:
goto bad_offset;
case ARMSSE_SSE300:
r = s->pwrctrl;
break;
default:
g_assert_not_reached();
}
r = s->ewctrl;
break;
case A_PDCM_PD_SYS_SENSE:
if (!s->is_sse200) {
switch (s->sse_version) {
case ARMSSE_IOTKIT:
goto bad_offset;
case ARMSSE_SSE200:
case ARMSSE_SSE300:
r = s->pdcm_pd_sys_sense;
break;
default:
g_assert_not_reached();
}
break;
case A_PDCM_PD_CPU0_SENSE:
switch (s->sse_version) {
case ARMSSE_IOTKIT:
case ARMSSE_SSE200:
goto bad_offset;
case ARMSSE_SSE300:
r = s->pdcm_pd_cpu0_sense;
break;
default:
g_assert_not_reached();
}
r = s->pdcm_pd_sys_sense;
break;
case A_PDCM_PD_SRAM0_SENSE:
if (!s->is_sse200) {
switch (s->sse_version) {
case ARMSSE_IOTKIT:
goto bad_offset;
case ARMSSE_SSE200:
r = s->pdcm_pd_sram0_sense;
break;
case ARMSSE_SSE300:
goto bad_offset;
default:
g_assert_not_reached();
}
r = s->pdcm_pd_sram0_sense;
break;
case A_PDCM_PD_SRAM1_SENSE:
if (!s->is_sse200) {
switch (s->sse_version) {
case ARMSSE_IOTKIT:
goto bad_offset;
case ARMSSE_SSE200:
r = s->pdcm_pd_sram1_sense;
break;
case ARMSSE_SSE300:
goto bad_offset;
default:
g_assert_not_reached();
}
r = s->pdcm_pd_sram1_sense;
break;
case A_PDCM_PD_SRAM2_SENSE:
if (!s->is_sse200) {
switch (s->sse_version) {
case ARMSSE_IOTKIT:
goto bad_offset;
case ARMSSE_SSE200:
r = s->pdcm_pd_sram2_sense;
break;
case ARMSSE_SSE300:
r = s->pdcm_pd_vmr0_sense;
break;
default:
g_assert_not_reached();
}
r = s->pdcm_pd_sram2_sense;
break;
case A_PDCM_PD_SRAM3_SENSE:
if (!s->is_sse200) {
switch (s->sse_version) {
case ARMSSE_IOTKIT:
goto bad_offset;
case ARMSSE_SSE200:
r = s->pdcm_pd_sram3_sense;
break;
case ARMSSE_SSE300:
r = s->pdcm_pd_vmr1_sense;
break;
default:
g_assert_not_reached();
}
r = s->pdcm_pd_sram3_sense;
break;
case A_PID4 ... A_CID3:
r = sysctl_id[(offset - A_PID4) / 4];
switch (s->sse_version) {
case ARMSSE_IOTKIT:
r = iotkit_sysctl_id[(offset - A_PID4) / 4];
break;
case ARMSSE_SSE200:
case ARMSSE_SSE300:
r = sse200_sysctl_id[(offset - A_PID4) / 4];
break;
default:
g_assert_not_reached();
}
break;
case A_SECDBGSET:
case A_SECDBGCLR:
@ -213,6 +366,21 @@ static uint64_t iotkit_sysctl_read(void *opaque, hwaddr offset,
return r;
}
static void cpuwait_write(IoTKitSysCtl *s, uint32_t value)
{
int num_cpus = (s->sse_version == ARMSSE_SSE300) ? 1 : 2;
int i;
for (i = 0; i < num_cpus; i++) {
uint32_t mask = 1 << i;
if ((s->cpuwait & mask) && !(value & mask)) {
/* Powering up CPU 0 */
arm_set_cpu_on_and_reset(i);
}
}
s->cpuwait = value;
}
static void iotkit_sysctl_write(void *opaque, hwaddr offset,
uint64_t value, unsigned size)
{
@ -249,23 +417,53 @@ static void iotkit_sysctl_write(void *opaque, hwaddr offset,
s->gretreg = value;
break;
case A_INITSVTOR0:
s->initsvtor0 = value;
set_init_vtor(0, s->initsvtor0);
switch (s->sse_version) {
case ARMSSE_SSE300:
/* SSE300 has a LOCK bit which prevents further writes when set */
if (s->initsvtor0 & R_INITSVTOR0_LOCK_MASK) {
qemu_log_mask(LOG_GUEST_ERROR,
"IoTKit INITSVTOR0 write when register locked\n");
break;
}
s->initsvtor0 = value;
set_init_vtor(0, s->initsvtor0 & R_INITSVTOR0_VTOR_MASK);
break;
case ARMSSE_IOTKIT:
case ARMSSE_SSE200:
s->initsvtor0 = value;
set_init_vtor(0, s->initsvtor0);
break;
default:
g_assert_not_reached();
}
break;
case A_CPUWAIT:
if ((s->cpuwait & 1) && !(value & 1)) {
/* Powering up CPU 0 */
arm_set_cpu_on_and_reset(0);
switch (s->sse_version) {
case ARMSSE_IOTKIT:
case ARMSSE_SSE200:
cpuwait_write(s, value);
break;
case ARMSSE_SSE300:
/* In SSE300 this is reserved (for INITSVTOR2) */
goto bad_offset;
default:
g_assert_not_reached();
}
if ((s->cpuwait & 2) && !(value & 2)) {
/* Powering up CPU 1 */
arm_set_cpu_on_and_reset(1);
}
s->cpuwait = value;
break;
case A_WICCTRL:
qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl WICCTRL unimplemented\n");
s->wicctrl = value;
switch (s->sse_version) {
case ARMSSE_IOTKIT:
case ARMSSE_SSE200:
qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl WICCTRL unimplemented\n");
s->wicctrl = value;
break;
case ARMSSE_SSE300:
/* In SSE300 this offset is CPUWAIT */
cpuwait_write(s, value);
break;
default:
g_assert_not_reached();
}
break;
case A_SECDBGSET:
/* write-1-to-set */
@ -283,94 +481,214 @@ static void iotkit_sysctl_write(void *opaque, hwaddr offset,
}
break;
case A_SCSECCTRL:
if (!s->is_sse200) {
switch (s->sse_version) {
case ARMSSE_IOTKIT:
goto bad_offset;
case ARMSSE_SSE200:
case ARMSSE_SSE300:
qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl SCSECCTRL unimplemented\n");
s->scsecctrl = value;
break;
default:
g_assert_not_reached();
}
qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl SCSECCTRL unimplemented\n");
s->scsecctrl = value;
break;
case A_FCLK_DIV:
if (!s->is_sse200) {
switch (s->sse_version) {
case ARMSSE_IOTKIT:
goto bad_offset;
case ARMSSE_SSE200:
case ARMSSE_SSE300:
qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl FCLK_DIV unimplemented\n");
s->fclk_div = value;
break;
default:
g_assert_not_reached();
}
qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl FCLK_DIV unimplemented\n");
s->fclk_div = value;
break;
case A_SYSCLK_DIV:
if (!s->is_sse200) {
switch (s->sse_version) {
case ARMSSE_IOTKIT:
goto bad_offset;
case ARMSSE_SSE200:
case ARMSSE_SSE300:
qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl SYSCLK_DIV unimplemented\n");
s->sysclk_div = value;
break;
default:
g_assert_not_reached();
}
qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl SYSCLK_DIV unimplemented\n");
s->sysclk_div = value;
break;
case A_CLOCK_FORCE:
if (!s->is_sse200) {
switch (s->sse_version) {
case ARMSSE_IOTKIT:
goto bad_offset;
case ARMSSE_SSE200:
case ARMSSE_SSE300:
qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl CLOCK_FORCE unimplemented\n");
s->clock_force = value;
break;
default:
g_assert_not_reached();
}
qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl CLOCK_FORCE unimplemented\n");
s->clock_force = value;
break;
case A_INITSVTOR1:
if (!s->is_sse200) {
switch (s->sse_version) {
case ARMSSE_IOTKIT:
goto bad_offset;
case ARMSSE_SSE200:
s->initsvtor1 = value;
set_init_vtor(1, s->initsvtor1);
break;
case ARMSSE_SSE300:
goto bad_offset;
default:
g_assert_not_reached();
}
s->initsvtor1 = value;
set_init_vtor(1, s->initsvtor1);
break;
case A_EWCTRL:
if (!s->is_sse200) {
switch (s->sse_version) {
case ARMSSE_IOTKIT:
goto bad_offset;
case ARMSSE_SSE200:
qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl EWCTRL unimplemented\n");
s->ewctrl = value;
break;
case ARMSSE_SSE300:
/* In SSE300 this offset is is NMI_ENABLE */
qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl NMI_ENABLE unimplemented\n");
s->nmi_enable = value;
break;
default:
g_assert_not_reached();
}
break;
case A_PWRCTRL:
switch (s->sse_version) {
case ARMSSE_IOTKIT:
case ARMSSE_SSE200:
goto bad_offset;
case ARMSSE_SSE300:
if (!(s->pwrctrl & R_PWRCTRL_PPU_ACCESS_UNLOCK_MASK)) {
qemu_log_mask(LOG_GUEST_ERROR,
"IoTKit PWRCTRL write when register locked\n");
break;
}
s->pwrctrl = value;
break;
default:
g_assert_not_reached();
}
qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl EWCTRL unimplemented\n");
s->ewctrl = value;
break;
case A_PDCM_PD_SYS_SENSE:
if (!s->is_sse200) {
switch (s->sse_version) {
case ARMSSE_IOTKIT:
goto bad_offset;
case ARMSSE_SSE200:
case ARMSSE_SSE300:
qemu_log_mask(LOG_UNIMP,
"IoTKit SysCtl PDCM_PD_SYS_SENSE unimplemented\n");
s->pdcm_pd_sys_sense = value;
break;
default:
g_assert_not_reached();
}
break;
case A_PDCM_PD_CPU0_SENSE:
switch (s->sse_version) {
case ARMSSE_IOTKIT:
case ARMSSE_SSE200:
goto bad_offset;
case ARMSSE_SSE300:
qemu_log_mask(LOG_UNIMP,
"IoTKit SysCtl PDCM_PD_CPU0_SENSE unimplemented\n");
s->pdcm_pd_cpu0_sense = value;
break;
default:
g_assert_not_reached();
}
qemu_log_mask(LOG_UNIMP,
"IoTKit SysCtl PDCM_PD_SYS_SENSE unimplemented\n");
s->pdcm_pd_sys_sense = value;
break;
case A_PDCM_PD_SRAM0_SENSE:
if (!s->is_sse200) {
switch (s->sse_version) {
case ARMSSE_IOTKIT:
goto bad_offset;
case ARMSSE_SSE200:
qemu_log_mask(LOG_UNIMP,
"IoTKit SysCtl PDCM_PD_SRAM0_SENSE unimplemented\n");
s->pdcm_pd_sram0_sense = value;
break;
case ARMSSE_SSE300:
goto bad_offset;
default:
g_assert_not_reached();
}
qemu_log_mask(LOG_UNIMP,
"IoTKit SysCtl PDCM_PD_SRAM0_SENSE unimplemented\n");
s->pdcm_pd_sram0_sense = value;
break;
case A_PDCM_PD_SRAM1_SENSE:
if (!s->is_sse200) {
switch (s->sse_version) {
case ARMSSE_IOTKIT:
goto bad_offset;
case ARMSSE_SSE200:
qemu_log_mask(LOG_UNIMP,
"IoTKit SysCtl PDCM_PD_SRAM1_SENSE unimplemented\n");
s->pdcm_pd_sram1_sense = value;
break;
case ARMSSE_SSE300:
goto bad_offset;
default:
g_assert_not_reached();
}
qemu_log_mask(LOG_UNIMP,
"IoTKit SysCtl PDCM_PD_SRAM1_SENSE unimplemented\n");
s->pdcm_pd_sram1_sense = value;
break;
case A_PDCM_PD_SRAM2_SENSE:
if (!s->is_sse200) {
switch (s->sse_version) {
case ARMSSE_IOTKIT:
goto bad_offset;
case ARMSSE_SSE200:
qemu_log_mask(LOG_UNIMP,
"IoTKit SysCtl PDCM_PD_SRAM2_SENSE unimplemented\n");
s->pdcm_pd_sram2_sense = value;
break;
case ARMSSE_SSE300:
qemu_log_mask(LOG_UNIMP,
"IoTKit SysCtl PDCM_PD_VMR0_SENSE unimplemented\n");
s->pdcm_pd_vmr0_sense = value;
break;
default:
g_assert_not_reached();
}
qemu_log_mask(LOG_UNIMP,
"IoTKit SysCtl PDCM_PD_SRAM2_SENSE unimplemented\n");
s->pdcm_pd_sram2_sense = value;
break;
case A_PDCM_PD_SRAM3_SENSE:
if (!s->is_sse200) {
switch (s->sse_version) {
case ARMSSE_IOTKIT:
goto bad_offset;
case ARMSSE_SSE200:
qemu_log_mask(LOG_UNIMP,
"IoTKit SysCtl PDCM_PD_SRAM3_SENSE unimplemented\n");
s->pdcm_pd_sram3_sense = value;
break;
case ARMSSE_SSE300:
qemu_log_mask(LOG_UNIMP,
"IoTKit SysCtl PDCM_PD_VMR1_SENSE unimplemented\n");
s->pdcm_pd_vmr1_sense = value;
break;
default:
g_assert_not_reached();
}
qemu_log_mask(LOG_UNIMP,
"IoTKit SysCtl PDCM_PD_SRAM3_SENSE unimplemented\n");
s->pdcm_pd_sram3_sense = value;
break;
case A_NMI_ENABLE:
/* In IoTKit this is BUSWAIT: reserved, R/O, zero */
if (!s->is_sse200) {
switch (s->sse_version) {
case ARMSSE_IOTKIT:
goto ro_offset;
case ARMSSE_SSE200:
qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl NMI_ENABLE unimplemented\n");
s->nmi_enable = value;
break;
case ARMSSE_SSE300:
/* In SSE300 this is reserved (for INITSVTOR3) */
goto bad_offset;
default:
g_assert_not_reached();
}
qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl NMI_ENABLE unimplemented\n");
s->nmi_enable = value;
break;
case A_SECDBGSTAT:
case A_PID4 ... A_CID3:
@ -417,11 +735,15 @@ static void iotkit_sysctl_reset(DeviceState *dev)
s->clock_force = 0;
s->nmi_enable = 0;
s->ewctrl = 0;
s->pwrctrl = 0x3;
s->pdcm_pd_sys_sense = 0x7f;
s->pdcm_pd_sram0_sense = 0;
s->pdcm_pd_sram1_sense = 0;
s->pdcm_pd_sram2_sense = 0;
s->pdcm_pd_sram3_sense = 0;
s->pdcm_pd_cpu0_sense = 0;
s->pdcm_pd_vmr0_sense = 0;
s->pdcm_pd_vmr1_sense = 0;
}
static void iotkit_sysctl_init(Object *obj)
@ -438,17 +760,38 @@ static void iotkit_sysctl_realize(DeviceState *dev, Error **errp)
{
IoTKitSysCtl *s = IOTKIT_SYSCTL(dev);
/* The top 4 bits of the SYS_VERSION register tell us if we're an SSE-200 */
if (extract32(s->sys_version, 28, 4) == 2) {
s->is_sse200 = true;
if (!armsse_version_valid(s->sse_version)) {
error_setg(errp, "invalid sse-version value %d", s->sse_version);
return;
}
}
static bool sse300_needed(void *opaque)
{
IoTKitSysCtl *s = IOTKIT_SYSCTL(opaque);
return s->sse_version == ARMSSE_SSE300;
}
static const VMStateDescription iotkit_sysctl_sse300_vmstate = {
.name = "iotkit-sysctl/sse-300",
.version_id = 1,
.minimum_version_id = 1,
.needed = sse300_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT32(pwrctrl, IoTKitSysCtl),
VMSTATE_UINT32(pdcm_pd_cpu0_sense, IoTKitSysCtl),
VMSTATE_UINT32(pdcm_pd_vmr0_sense, IoTKitSysCtl),
VMSTATE_UINT32(pdcm_pd_vmr1_sense, IoTKitSysCtl),
VMSTATE_END_OF_LIST()
}
};
static bool sse200_needed(void *opaque)
{
IoTKitSysCtl *s = IOTKIT_SYSCTL(opaque);
return s->is_sse200;
return s->sse_version != ARMSSE_IOTKIT;
}
static const VMStateDescription iotkit_sysctl_sse200_vmstate = {
@ -488,12 +831,13 @@ static const VMStateDescription iotkit_sysctl_vmstate = {
},
.subsections = (const VMStateDescription*[]) {
&iotkit_sysctl_sse200_vmstate,
&iotkit_sysctl_sse300_vmstate,
NULL
}
};
static Property iotkit_sysctl_props[] = {
DEFINE_PROP_UINT32("SYS_VERSION", IoTKitSysCtl, sys_version, 0),
DEFINE_PROP_UINT32("sse-version", IoTKitSysCtl, sse_version, 0),
DEFINE_PROP_UINT32("CPUWAIT_RST", IoTKitSysCtl, cpuwait_rst, 0),
DEFINE_PROP_UINT32("INITSVTOR0_RST", IoTKitSysCtl, initsvtor0_rst,
0x10000000),

View File

@ -26,9 +26,12 @@
#include "hw/registerfields.h"
#include "hw/misc/iotkit-sysinfo.h"
#include "hw/qdev-properties.h"
#include "hw/arm/armsse-version.h"
REG32(SYS_VERSION, 0x0)
REG32(SYS_CONFIG, 0x4)
REG32(SYS_CONFIG1, 0x8)
REG32(IIDR, 0xfc8)
REG32(PID4, 0xfd0)
REG32(PID5, 0xfd4)
REG32(PID6, 0xfd8)
@ -49,6 +52,12 @@ static const int sysinfo_id[] = {
0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */
};
static const int sysinfo_sse300_id[] = {
0x04, 0x00, 0x00, 0x00, /* PID4..PID7 */
0x58, 0xb8, 0x1b, 0x00, /* PID0..PID3 */
0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */
};
static uint64_t iotkit_sysinfo_read(void *opaque, hwaddr offset,
unsigned size)
{
@ -63,10 +72,36 @@ static uint64_t iotkit_sysinfo_read(void *opaque, hwaddr offset,
case A_SYS_CONFIG:
r = s->sys_config;
break;
case A_SYS_CONFIG1:
switch (s->sse_version) {
case ARMSSE_SSE300:
return 0;
break;
default:
goto bad_read;
}
break;
case A_IIDR:
switch (s->sse_version) {
case ARMSSE_SSE300:
return s->iidr;
break;
default:
goto bad_read;
}
break;
case A_PID4 ... A_CID3:
r = sysinfo_id[(offset - A_PID4) / 4];
switch (s->sse_version) {
case ARMSSE_SSE300:
r = sysinfo_sse300_id[(offset - A_PID4) / 4];
break;
default:
r = sysinfo_id[(offset - A_PID4) / 4];
break;
}
break;
default:
bad_read:
qemu_log_mask(LOG_GUEST_ERROR,
"IoTKit SysInfo read: bad offset %x\n", (int)offset);
r = 0;
@ -99,6 +134,8 @@ static const MemoryRegionOps iotkit_sysinfo_ops = {
static Property iotkit_sysinfo_props[] = {
DEFINE_PROP_UINT32("SYS_VERSION", IoTKitSysInfo, sys_version, 0),
DEFINE_PROP_UINT32("SYS_CONFIG", IoTKitSysInfo, sys_config, 0),
DEFINE_PROP_UINT32("sse-version", IoTKitSysInfo, sse_version, 0),
DEFINE_PROP_UINT32("IIDR", IoTKitSysInfo, iidr, 0),
DEFINE_PROP_END_OF_LIST()
};
@ -112,6 +149,16 @@ static void iotkit_sysinfo_init(Object *obj)
sysbus_init_mmio(sbd, &s->iomem);
}
static void iotkit_sysinfo_realize(DeviceState *dev, Error **errp)
{
IoTKitSysInfo *s = IOTKIT_SYSINFO(dev);
if (!armsse_version_valid(s->sse_version)) {
error_setg(errp, "invalid sse-version value %d", s->sse_version);
return;
}
}
static void iotkit_sysinfo_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
@ -120,7 +167,7 @@ static void iotkit_sysinfo_class_init(ObjectClass *klass, void *data)
* This device has no guest-modifiable state and so it
* does not need a reset function or VMState.
*/
dc->realize = iotkit_sysinfo_realize;
device_class_set_props(dc, iotkit_sysinfo_props);
}

View File

@ -96,6 +96,7 @@ softmmu_ss.add(when: 'CONFIG_TZ_MSC', if_true: files('tz-msc.c'))
softmmu_ss.add(when: 'CONFIG_TZ_PPC', if_true: files('tz-ppc.c'))
softmmu_ss.add(when: 'CONFIG_IOTKIT_SECCTL', if_true: files('iotkit-secctl.c'))
softmmu_ss.add(when: 'CONFIG_IOTKIT_SYSINFO', if_true: files('iotkit-sysinfo.c'))
softmmu_ss.add(when: 'CONFIG_ARMSSE_CPU_PWRCTRL', if_true: files('armsse-cpu-pwrctrl.c'))
softmmu_ss.add(when: 'CONFIG_ARMSSE_CPUID', if_true: files('armsse-cpuid.c'))
softmmu_ss.add(when: 'CONFIG_ARMSSE_MHU', if_true: files('armsse-mhu.c'))

View File

@ -29,6 +29,7 @@
#include "qemu/timer.h"
REG32(LED0, 0)
REG32(DBGCTRL, 4)
REG32(BUTTON, 8)
REG32(CLK1HZ, 0x10)
REG32(CLK100HZ, 0x14)
@ -129,6 +130,12 @@ static uint64_t mps2_fpgaio_read(void *opaque, hwaddr offset, unsigned size)
case A_LED0:
r = s->led0;
break;
case A_DBGCTRL:
if (!s->has_dbgctrl) {
goto bad_offset;
}
r = s->dbgctrl;
break;
case A_BUTTON:
/* User-pressable board buttons. We don't model that, so just return
* zeroes.
@ -195,6 +202,14 @@ static void mps2_fpgaio_write(void *opaque, hwaddr offset, uint64_t value,
}
}
break;
case A_DBGCTRL:
if (!s->has_dbgctrl) {
goto bad_offset;
}
qemu_log_mask(LOG_UNIMP,
"MPS2 FPGAIO: DBGCTRL unimplemented\n");
s->dbgctrl = value;
break;
case A_PRESCALE:
resync_counter(s);
s->prescale = value;
@ -225,6 +240,7 @@ static void mps2_fpgaio_write(void *opaque, hwaddr offset, uint64_t value,
s->pscntr = value;
break;
default:
bad_offset:
qemu_log_mask(LOG_GUEST_ERROR,
"MPS2 FPGAIO write: bad offset 0x%x\n", (int) offset);
break;
@ -285,41 +301,22 @@ static void mps2_fpgaio_realize(DeviceState *dev, Error **errp)
}
}
static bool mps2_fpgaio_counters_needed(void *opaque)
{
/* Currently vmstate.c insists all subsections have a 'needed' function */
return true;
}
static const VMStateDescription mps2_fpgaio_counters_vmstate = {
.name = "mps2-fpgaio/counters",
.version_id = 2,
.minimum_version_id = 2,
.needed = mps2_fpgaio_counters_needed,
static const VMStateDescription mps2_fpgaio_vmstate = {
.name = "mps2-fpgaio",
.version_id = 3,
.minimum_version_id = 3,
.fields = (VMStateField[]) {
VMSTATE_UINT32(led0, MPS2FPGAIO),
VMSTATE_UINT32(prescale, MPS2FPGAIO),
VMSTATE_UINT32(misc, MPS2FPGAIO),
VMSTATE_UINT32(dbgctrl, MPS2FPGAIO),
VMSTATE_INT64(clk1hz_tick_offset, MPS2FPGAIO),
VMSTATE_INT64(clk100hz_tick_offset, MPS2FPGAIO),
VMSTATE_UINT32(counter, MPS2FPGAIO),
VMSTATE_UINT32(pscntr, MPS2FPGAIO),
VMSTATE_INT64(pscntr_sync_ticks, MPS2FPGAIO),
VMSTATE_END_OF_LIST()
}
};
static const VMStateDescription mps2_fpgaio_vmstate = {
.name = "mps2-fpgaio",
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT32(led0, MPS2FPGAIO),
VMSTATE_UINT32(prescale, MPS2FPGAIO),
VMSTATE_UINT32(misc, MPS2FPGAIO),
VMSTATE_END_OF_LIST()
},
.subsections = (const VMStateDescription*[]) {
&mps2_fpgaio_counters_vmstate,
NULL
}
};
static Property mps2_fpgaio_properties[] = {
@ -328,6 +325,7 @@ static Property mps2_fpgaio_properties[] = {
/* 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_BOOL("has-dbgctrl", MPS2FPGAIO, has_dbgctrl, false),
DEFINE_PROP_END_OF_LIST(),
};

View File

@ -110,14 +110,14 @@ static uint64_t mps2_scc_read(void *opaque, hwaddr offset, unsigned size)
r = s->cfg1;
break;
case A_CFG2:
if (scc_partno(s) != 0x524) {
if (scc_partno(s) != 0x524 && scc_partno(s) != 0x547) {
/* CFG2 reserved on other boards */
goto bad_offset;
}
r = s->cfg2;
break;
case A_CFG3:
if (scc_partno(s) == 0x524) {
if (scc_partno(s) == 0x524 && scc_partno(s) == 0x547) {
/* CFG3 reserved on AN524 */
goto bad_offset;
}
@ -130,7 +130,7 @@ static uint64_t mps2_scc_read(void *opaque, hwaddr offset, unsigned size)
r = s->cfg4;
break;
case A_CFG5:
if (scc_partno(s) != 0x524) {
if (scc_partno(s) != 0x524 && scc_partno(s) != 0x547) {
/* CFG5 reserved on other boards */
goto bad_offset;
}
@ -185,7 +185,10 @@ static void mps2_scc_write(void *opaque, hwaddr offset, uint64_t value,
switch (offset) {
case A_CFG0:
/* TODO on some boards bit 0 controls RAM remapping */
/*
* TODO on some boards bit 0 controls RAM remapping;
* on others bit 1 is CPU_WAIT.
*/
s->cfg0 = value;
break;
case A_CFG1:
@ -195,7 +198,7 @@ static void mps2_scc_write(void *opaque, hwaddr offset, uint64_t value,
}
break;
case A_CFG2:
if (scc_partno(s) != 0x524) {
if (scc_partno(s) != 0x524 && scc_partno(s) != 0x547) {
/* CFG2 reserved on other boards */
goto bad_offset;
}
@ -203,7 +206,7 @@ static void mps2_scc_write(void *opaque, hwaddr offset, uint64_t value,
s->cfg2 = value;
break;
case A_CFG5:
if (scc_partno(s) != 0x524) {
if (scc_partno(s) != 0x524 && scc_partno(s) != 0x547) {
/* CFG5 reserved on other boards */
goto bad_offset;
}

View File

@ -586,15 +586,26 @@ static const DividerInitInfo divider_init_info_list[] = {
},
};
static void npcm7xx_clk_update_pll_cb(void *opaque, ClockEvent event)
{
npcm7xx_clk_update_pll(opaque);
}
static void npcm7xx_clk_pll_init(Object *obj)
{
NPCM7xxClockPLLState *pll = NPCM7XX_CLOCK_PLL(obj);
pll->clock_in = qdev_init_clock_in(DEVICE(pll), "clock-in",
npcm7xx_clk_update_pll, pll);
npcm7xx_clk_update_pll_cb, pll,
ClockUpdate);
pll->clock_out = qdev_init_clock_out(DEVICE(pll), "clock-out");
}
static void npcm7xx_clk_update_sel_cb(void *opaque, ClockEvent event)
{
npcm7xx_clk_update_sel(opaque);
}
static void npcm7xx_clk_sel_init(Object *obj)
{
int i;
@ -603,16 +614,23 @@ static void npcm7xx_clk_sel_init(Object *obj)
for (i = 0; i < NPCM7XX_CLK_SEL_MAX_INPUT; ++i) {
sel->clock_in[i] = qdev_init_clock_in(DEVICE(sel),
g_strdup_printf("clock-in[%d]", i),
npcm7xx_clk_update_sel, sel);
npcm7xx_clk_update_sel_cb, sel, ClockUpdate);
}
sel->clock_out = qdev_init_clock_out(DEVICE(sel), "clock-out");
}
static void npcm7xx_clk_update_divider_cb(void *opaque, ClockEvent event)
{
npcm7xx_clk_update_divider(opaque);
}
static void npcm7xx_clk_divider_init(Object *obj)
{
NPCM7xxClockDividerState *div = NPCM7XX_CLOCK_DIVIDER(obj);
div->clock_in = qdev_init_clock_in(DEVICE(div), "clock-in",
npcm7xx_clk_update_divider, div);
npcm7xx_clk_update_divider_cb,
div, ClockUpdate);
div->clock_out = qdev_init_clock_out(DEVICE(div), "clock-out");
}
@ -875,7 +893,7 @@ static void npcm7xx_clk_init_clock_hierarchy(NPCM7xxCLKState *s)
{
int i;
s->clkref = qdev_init_clock_in(DEVICE(s), "clkref", NULL, NULL);
s->clkref = qdev_init_clock_in(DEVICE(s), "clkref", NULL, NULL, 0);
/* First pass: init all converter modules */
QEMU_BUILD_BUG_ON(ARRAY_SIZE(pll_init_info_list) != NPCM7XX_CLOCK_NR_PLLS);

View File

@ -493,7 +493,7 @@ static void npcm7xx_pwm_init(Object *obj)
memory_region_init_io(&s->iomem, obj, &npcm7xx_pwm_ops, s,
TYPE_NPCM7XX_PWM, 4 * KiB);
sysbus_init_mmio(sbd, &s->iomem);
s->clock = qdev_init_clock_in(DEVICE(s), "clock", NULL, NULL);
s->clock = qdev_init_clock_in(DEVICE(s), "clock", NULL, NULL, 0);
for (i = 0; i < NPCM7XX_PWM_PER_MODULE; ++i) {
object_property_add_uint32_ptr(obj, "freq[*]",

View File

@ -186,6 +186,10 @@ iotkit_sysctl_read(uint64_t offset, uint64_t data, unsigned size) "IoTKit SysCtl
iotkit_sysctl_write(uint64_t offset, uint64_t data, unsigned size) "IoTKit SysCtl write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
iotkit_sysctl_reset(void) "IoTKit SysCtl: reset"
# armsse-cpu-pwrctrl.c
armsse_cpu_pwrctrl_read(uint64_t offset, uint64_t data, unsigned size) "SSE-300 CPU_PWRCTRL read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
armsse_cpu_pwrctrl_write(uint64_t offset, uint64_t data, unsigned size) "SSE-300 CPU_PWRCTRL write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
# armsse-cpuid.c
armsse_cpuid_read(uint64_t offset, uint64_t data, unsigned size) "SSE-200 CPU_IDENTITY read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
armsse_cpuid_write(uint64_t offset, uint64_t data, unsigned size) "SSE-200 CPU_IDENTITY write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"

View File

@ -307,9 +307,10 @@ static void zynq_slcr_propagate_clocks(ZynqSLCRState *s)
clock_propagate(s->uart1_ref_clk);
}
static void zynq_slcr_ps_clk_callback(void *opaque)
static void zynq_slcr_ps_clk_callback(void *opaque, ClockEvent event)
{
ZynqSLCRState *s = (ZynqSLCRState *) opaque;
zynq_slcr_compute_clocks(s);
zynq_slcr_propagate_clocks(s);
}
@ -576,7 +577,7 @@ static const MemoryRegionOps slcr_ops = {
};
static const ClockPortInitArray zynq_slcr_clocks = {
QDEV_CLOCK_IN(ZynqSLCRState, ps_clk, zynq_slcr_ps_clk_callback),
QDEV_CLOCK_IN(ZynqSLCRState, ps_clk, zynq_slcr_ps_clk_callback, ClockUpdate),
QDEV_CLOCK_OUT(ZynqSLCRState, uart0_ref_clk),
QDEV_CLOCK_OUT(ZynqSLCRState, uart1_ref_clk),
QDEV_CLOCK_END

View File

@ -176,7 +176,8 @@
FIELD(GQSPI_FIFO_CTRL, GENERIC_FIFO_RESET, 0, 1)
#define R_GQSPI_GFIFO_THRESH (0x150 / 4)
#define R_GQSPI_DATA_STS (0x15c / 4)
/* We use the snapshot register to hold the core state for the currently
/*
* We use the snapshot register to hold the core state for the currently
* or most recently executed command. So the generic fifo format is defined
* for the snapshot register
*/
@ -194,13 +195,6 @@
#define R_GQSPI_MOD_ID (0x1fc / 4)
#define R_GQSPI_MOD_ID_RESET (0x10a0000)
#define R_QSPIDMA_DST_CTRL (0x80c / 4)
#define R_QSPIDMA_DST_CTRL_RESET (0x803ffa00)
#define R_QSPIDMA_DST_I_MASK (0x820 / 4)
#define R_QSPIDMA_DST_I_MASK_RESET (0xfe)
#define R_QSPIDMA_DST_CTRL2 (0x824 / 4)
#define R_QSPIDMA_DST_CTRL2_RESET (0x081bfff8)
/* size of TXRX FIFOs */
#define RXFF_A (128)
#define TXFF_A (128)
@ -416,15 +410,13 @@ static void xlnx_zynqmp_qspips_reset(DeviceState *d)
s->regs[R_GQSPI_GPIO] = 1;
s->regs[R_GQSPI_LPBK_DLY_ADJ] = R_GQSPI_LPBK_DLY_ADJ_RESET;
s->regs[R_GQSPI_MOD_ID] = R_GQSPI_MOD_ID_RESET;
s->regs[R_QSPIDMA_DST_CTRL] = R_QSPIDMA_DST_CTRL_RESET;
s->regs[R_QSPIDMA_DST_I_MASK] = R_QSPIDMA_DST_I_MASK_RESET;
s->regs[R_QSPIDMA_DST_CTRL2] = R_QSPIDMA_DST_CTRL2_RESET;
s->man_start_com_g = false;
s->gqspi_irqline = 0;
xlnx_zynqmp_qspips_update_ixr(s);
}
/* N way (num) in place bit striper. Lay out row wise bits (MSB to LSB)
/*
* N way (num) in place bit striper. Lay out row wise bits (MSB to LSB)
* column wise (from element 0 to N-1). num is the length of x, and dir
* reverses the direction of the transform. Best illustrated by example:
* Each digit in the below array is a single bit (num == 3):
@ -637,8 +629,10 @@ static void xilinx_spips_flush_txfifo(XilinxSPIPS *s)
tx_rx[i] = tx;
}
} else {
/* Extract a dummy byte and generate dummy cycles according to the
* link state */
/*
* Extract a dummy byte and generate dummy cycles according to the
* link state
*/
tx = fifo8_pop(&s->tx_fifo);
dummy_cycles = 8 / s->link_state;
}
@ -721,8 +715,9 @@ static void xilinx_spips_flush_txfifo(XilinxSPIPS *s)
}
break;
case (SNOOP_ADDR):
/* Address has been transmitted, transmit dummy cycles now if
* needed */
/*
* Address has been transmitted, transmit dummy cycles now if needed
*/
if (s->cmd_dummies < 0) {
s->snoop_state = SNOOP_NONE;
} else {
@ -876,7 +871,7 @@ static void xlnx_zynqmp_qspips_notify(void *opaque)
}
static uint64_t xilinx_spips_read(void *opaque, hwaddr addr,
unsigned size)
unsigned size)
{
XilinxSPIPS *s = opaque;
uint32_t mask = ~0;
@ -970,7 +965,7 @@ static uint64_t xlnx_zynqmp_qspips_read(void *opaque,
}
static void xilinx_spips_write(void *opaque, hwaddr addr,
uint64_t value, unsigned size)
uint64_t value, unsigned size)
{
int mask = ~0;
XilinxSPIPS *s = opaque;
@ -1072,7 +1067,7 @@ static void xilinx_qspips_write(void *opaque, hwaddr addr,
}
static void xlnx_zynqmp_qspips_write(void *opaque, hwaddr addr,
uint64_t value, unsigned size)
uint64_t value, unsigned size)
{
XlnxZynqMPQSPIPS *s = XLNX_ZYNQMP_QSPIPS(opaque);
uint32_t reg = addr / 4;

View File

@ -46,5 +46,11 @@ config RENESAS_TMR
config RENESAS_CMT
bool
config SSE_COUNTER
bool
config SSE_TIMER
bool
config AVR_TIMER16
bool

View File

@ -449,7 +449,7 @@ static void cmsdk_apb_dualtimer_reset(DeviceState *dev)
s->timeritop = 0;
}
static void cmsdk_apb_dualtimer_clk_update(void *opaque)
static void cmsdk_apb_dualtimer_clk_update(void *opaque, ClockEvent event)
{
CMSDKAPBDualTimer *s = CMSDK_APB_DUALTIMER(opaque);
int i;
@ -478,7 +478,8 @@ static void cmsdk_apb_dualtimer_init(Object *obj)
sysbus_init_irq(sbd, &s->timermod[i].timerint);
}
s->timclk = qdev_init_clock_in(DEVICE(s), "TIMCLK",
cmsdk_apb_dualtimer_clk_update, s);
cmsdk_apb_dualtimer_clk_update, s,
ClockUpdate);
}
static void cmsdk_apb_dualtimer_realize(DeviceState *dev, Error **errp)

View File

@ -204,7 +204,7 @@ static void cmsdk_apb_timer_reset(DeviceState *dev)
ptimer_transaction_commit(s->timer);
}
static void cmsdk_apb_timer_clk_update(void *opaque)
static void cmsdk_apb_timer_clk_update(void *opaque, ClockEvent event)
{
CMSDKAPBTimer *s = CMSDK_APB_TIMER(opaque);
@ -223,7 +223,7 @@ static void cmsdk_apb_timer_init(Object *obj)
sysbus_init_mmio(sbd, &s->iomem);
sysbus_init_irq(sbd, &s->timerint);
s->pclk = qdev_init_clock_in(DEVICE(s), "pclk",
cmsdk_apb_timer_clk_update, s);
cmsdk_apb_timer_clk_update, s, ClockUpdate);
}
static void cmsdk_apb_timer_realize(DeviceState *dev, Error **errp)

View File

@ -32,6 +32,8 @@ softmmu_ss.add(when: 'CONFIG_PXA2XX', if_true: files('pxa2xx_timer.c'))
softmmu_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_systmr.c'))
softmmu_ss.add(when: 'CONFIG_SH_TIMER', if_true: files('sh_timer.c'))
softmmu_ss.add(when: 'CONFIG_SLAVIO', if_true: files('slavio_timer.c'))
softmmu_ss.add(when: 'CONFIG_SSE_COUNTER', if_true: files('sse-counter.c'))
softmmu_ss.add(when: 'CONFIG_SSE_TIMER', if_true: files('sse-timer.c'))
softmmu_ss.add(when: 'CONFIG_STM32F2XX_TIMER', if_true: files('stm32f2xx_timer.c'))
softmmu_ss.add(when: 'CONFIG_XILINX', if_true: files('xilinx_timer.c'))

View File

@ -138,8 +138,8 @@ static int64_t npcm7xx_timer_count_to_ns(NPCM7xxTimer *t, uint32_t count)
/* Convert a time interval in nanoseconds to a timer cycle count. */
static uint32_t npcm7xx_timer_ns_to_count(NPCM7xxTimer *t, int64_t ns)
{
return ns / clock_ticks_to_ns(t->ctrl->clock,
npcm7xx_tcsr_prescaler(t->tcsr));
return clock_ns_to_ticks(t->ctrl->clock, ns) /
npcm7xx_tcsr_prescaler(t->tcsr);
}
static uint32_t npcm7xx_watchdog_timer_prescaler(const NPCM7xxWatchdogTimer *t)
@ -627,7 +627,7 @@ static void npcm7xx_timer_init(Object *obj)
sysbus_init_mmio(sbd, &s->iomem);
qdev_init_gpio_out_named(dev, &w->reset_signal,
NPCM7XX_WATCHDOG_RESET_GPIO_OUT, 1);
s->clock = qdev_init_clock_in(dev, "clock", NULL, NULL);
s->clock = qdev_init_clock_in(dev, "clock", NULL, NULL, 0);
}
static const VMStateDescription vmstate_npcm7xx_base_timer = {

View File

@ -46,8 +46,10 @@ REG8(TCCR, 10)
FIELD(TCCR, CSS, 3, 2)
FIELD(TCCR, TMRIS, 7, 1)
#define INTERNAL 0x01
#define CASCADING 0x03
#define CSS_EXTERNAL 0x00
#define CSS_INTERNAL 0x01
#define CSS_INVALID 0x02
#define CSS_CASCADING 0x03
#define CCLR_A 0x01
#define CCLR_B 0x02
@ -72,7 +74,7 @@ static void update_events(RTMRState *tmr, int ch)
/* event not happened */
return ;
}
if (FIELD_EX8(tmr->tccr[0], TCCR, CSS) == CASCADING) {
if (FIELD_EX8(tmr->tccr[0], TCCR, CSS) == CSS_CASCADING) {
/* cascading mode */
if (ch == 1) {
tmr->next[ch] = none;
@ -130,23 +132,32 @@ static uint16_t read_tcnt(RTMRState *tmr, unsigned size, int ch)
if (delta > 0) {
tmr->tick = now;
if (FIELD_EX8(tmr->tccr[1], TCCR, CSS) == INTERNAL) {
switch (FIELD_EX8(tmr->tccr[1], TCCR, CSS)) {
case CSS_INTERNAL:
/* timer1 count update */
elapsed = elapsed_time(tmr, 1, delta);
if (elapsed >= 0x100) {
ovf = elapsed >> 8;
}
tcnt[1] = tmr->tcnt[1] + (elapsed & 0xff);
break;
case CSS_INVALID: /* guest error to have set this */
case CSS_EXTERNAL: /* QEMU doesn't implement these */
case CSS_CASCADING:
tcnt[1] = tmr->tcnt[1];
break;
}
switch (FIELD_EX8(tmr->tccr[0], TCCR, CSS)) {
case INTERNAL:
case CSS_INTERNAL:
elapsed = elapsed_time(tmr, 0, delta);
tcnt[0] = tmr->tcnt[0] + elapsed;
break;
case CASCADING:
if (ovf > 0) {
tcnt[0] = tmr->tcnt[0] + ovf;
}
case CSS_CASCADING:
tcnt[0] = tmr->tcnt[0] + ovf;
break;
case CSS_INVALID: /* guest error to have set this */
case CSS_EXTERNAL: /* QEMU doesn't implement this */
tcnt[0] = tmr->tcnt[0];
break;
}
} else {
@ -330,7 +341,7 @@ static uint16_t issue_event(RTMRState *tmr, int ch, int sz,
qemu_irq_pulse(tmr->cmia[ch]);
}
if (sz == 8 && ch == 0 &&
FIELD_EX8(tmr->tccr[1], TCCR, CSS) == CASCADING) {
FIELD_EX8(tmr->tccr[1], TCCR, CSS) == CSS_CASCADING) {
tmr->tcnt[1]++;
timer_events(tmr, 1);
}
@ -362,7 +373,7 @@ static void timer_events(RTMRState *tmr, int ch)
uint16_t tcnt;
tmr->tcnt[ch] = read_tcnt(tmr, 1, ch);
if (FIELD_EX8(tmr->tccr[0], TCCR, CSS) != CASCADING) {
if (FIELD_EX8(tmr->tccr[0], TCCR, CSS) != CSS_CASCADING) {
tmr->tcnt[ch] = issue_event(tmr, ch, 8,
tmr->tcnt[ch],
tmr->tcora[ch],

474
hw/timer/sse-counter.c Normal file
View File

@ -0,0 +1,474 @@
/*
* Arm SSE Subsystem System Counter
*
* Copyright (c) 2020 Linaro Limited
* Written by Peter Maydell
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 or
* (at your option) any later version.
*/
/*
* This is a model of the "System counter" which is documented in
* the Arm SSE-123 Example Subsystem Technical Reference Manual:
* https://developer.arm.com/documentation/101370/latest/
*
* The system counter is a non-stop 64-bit up-counter. It provides
* this count value to other devices like the SSE system timer,
* which are driven by this system timestamp rather than directly
* from a clock. Internally to the counter the count is actually
* 88-bit precision (64.24 fixed point), with a programmable scale factor.
*
* The hardware has the optional feature that it supports dynamic
* clock switching, where two clock inputs are connected, and which
* one is used is selected via a CLKSEL input signal. Since the
* users of this device in QEMU don't use this feature, we only model
* the HWCLKSW=0 configuration.
*/
#include "qemu/osdep.h"
#include "qemu/log.h"
#include "qemu/timer.h"
#include "qapi/error.h"
#include "trace.h"
#include "hw/timer/sse-counter.h"
#include "hw/sysbus.h"
#include "hw/irq.h"
#include "hw/registerfields.h"
#include "hw/clock.h"
#include "hw/qdev-clock.h"
#include "migration/vmstate.h"
/* Registers in the control frame */
REG32(CNTCR, 0x0)
FIELD(CNTCR, EN, 0, 1)
FIELD(CNTCR, HDBG, 1, 1)
FIELD(CNTCR, SCEN, 2, 1)
FIELD(CNTCR, INTRMASK, 3, 1)
FIELD(CNTCR, PSLVERRDIS, 4, 1)
FIELD(CNTCR, INTRCLR, 5, 1)
/*
* Although CNTCR defines interrupt-related bits, the counter doesn't
* appear to actually have an interrupt output. So INTRCLR is
* effectively a RAZ/WI bit, as are the reserved bits [31:6].
*/
#define CNTCR_VALID_MASK (R_CNTCR_EN_MASK | R_CNTCR_HDBG_MASK | \
R_CNTCR_SCEN_MASK | R_CNTCR_INTRMASK_MASK | \
R_CNTCR_PSLVERRDIS_MASK)
REG32(CNTSR, 0x4)
REG32(CNTCV_LO, 0x8)
REG32(CNTCV_HI, 0xc)
REG32(CNTSCR, 0x10) /* Aliased with CNTSCR0 */
REG32(CNTID, 0x1c)
FIELD(CNTID, CNTSC, 0, 4)
FIELD(CNTID, CNTCS, 16, 1)
FIELD(CNTID, CNTSELCLK, 17, 2)
FIELD(CNTID, CNTSCR_OVR, 19, 1)
REG32(CNTSCR0, 0xd0)
REG32(CNTSCR1, 0xd4)
/* Registers in the status frame */
REG32(STATUS_CNTCV_LO, 0x0)
REG32(STATUS_CNTCV_HI, 0x4)
/* Standard ID registers, present in both frames */
REG32(PID4, 0xFD0)
REG32(PID5, 0xFD4)
REG32(PID6, 0xFD8)
REG32(PID7, 0xFDC)
REG32(PID0, 0xFE0)
REG32(PID1, 0xFE4)
REG32(PID2, 0xFE8)
REG32(PID3, 0xFEC)
REG32(CID0, 0xFF0)
REG32(CID1, 0xFF4)
REG32(CID2, 0xFF8)
REG32(CID3, 0xFFC)
/* PID/CID values */
static const int control_id[] = {
0x04, 0x00, 0x00, 0x00, /* PID4..PID7 */
0xba, 0xb0, 0x0b, 0x00, /* PID0..PID3 */
0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */
};
static const int status_id[] = {
0x04, 0x00, 0x00, 0x00, /* PID4..PID7 */
0xbb, 0xb0, 0x0b, 0x00, /* PID0..PID3 */
0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */
};
static void sse_counter_notify_users(SSECounter *s)
{
/*
* Notify users of the count timestamp that they may
* need to recalculate.
*/
notifier_list_notify(&s->notifier_list, NULL);
}
static bool sse_counter_enabled(SSECounter *s)
{
return (s->cntcr & R_CNTCR_EN_MASK) != 0;
}
uint64_t sse_counter_tick_to_time(SSECounter *s, uint64_t tick)
{
if (!sse_counter_enabled(s)) {
return UINT64_MAX;
}
tick -= s->ticks_then;
if (s->cntcr & R_CNTCR_SCEN_MASK) {
/* Adjust the tick count to account for the scale factor */
tick = muldiv64(tick, 0x01000000, s->cntscr0);
}
return s->ns_then + clock_ticks_to_ns(s->clk, tick);
}
void sse_counter_register_consumer(SSECounter *s, Notifier *notifier)
{
/*
* For the moment we assume that both we and the devices
* which consume us last for the life of the simulation,
* and so there is no mechanism for removing a notifier.
*/
notifier_list_add(&s->notifier_list, notifier);
}
uint64_t sse_counter_for_timestamp(SSECounter *s, uint64_t now)
{
/* Return the CNTCV value for a particular timestamp (clock ns value). */
uint64_t ticks;
if (!sse_counter_enabled(s)) {
/* Counter is disabled and does not increment */
return s->ticks_then;
}
ticks = clock_ns_to_ticks(s->clk, now - s->ns_then);
if (s->cntcr & R_CNTCR_SCEN_MASK) {
/*
* Scaling is enabled. The CNTSCR value is the amount added to
* the underlying 88-bit counter for every tick of the
* underlying clock; CNTCV is the top 64 bits of that full
* 88-bit value. Multiplying the tick count by CNTSCR tells us
* how much the full 88-bit counter has moved on; we then
* divide that by 0x01000000 to find out how much the 64-bit
* visible portion has advanced. muldiv64() gives us the
* necessary at-least-88-bit precision for the intermediate
* result.
*/
ticks = muldiv64(ticks, s->cntscr0, 0x01000000);
}
return s->ticks_then + ticks;
}
static uint64_t sse_cntcv(SSECounter *s)
{
/* Return the CNTCV value for the current time */
return sse_counter_for_timestamp(s, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
}
static void sse_write_cntcv(SSECounter *s, uint32_t value, unsigned startbit)
{
/*
* Write one 32-bit half of the counter value; startbit is the
* bit position of this half in the 64-bit word, either 0 or 32.
*/
uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
uint64_t cntcv = sse_counter_for_timestamp(s, now);
cntcv = deposit64(cntcv, startbit, 32, value);
s->ticks_then = cntcv;
s->ns_then = now;
sse_counter_notify_users(s);
}
static uint64_t sse_counter_control_read(void *opaque, hwaddr offset,
unsigned size)
{
SSECounter *s = SSE_COUNTER(opaque);
uint64_t r;
switch (offset) {
case A_CNTCR:
r = s->cntcr;
break;
case A_CNTSR:
/*
* The only bit here is DBGH, indicating that the counter has been
* halted via the Halt-on-Debug signal. We don't implement halting
* debug, so the whole register always reads as zero.
*/
r = 0;
break;
case A_CNTCV_LO:
r = extract64(sse_cntcv(s), 0, 32);
break;
case A_CNTCV_HI:
r = extract64(sse_cntcv(s), 32, 32);
break;
case A_CNTID:
/*
* For our implementation:
* - CNTSCR can only be written when CNTCR.EN == 0
* - HWCLKSW=0, so selected clock is always CLK0
* - counter scaling is implemented
*/
r = (1 << R_CNTID_CNTSELCLK_SHIFT) | (1 << R_CNTID_CNTSC_SHIFT);
break;
case A_CNTSCR:
case A_CNTSCR0:
r = s->cntscr0;
break;
case A_CNTSCR1:
/* If HWCLKSW == 0, CNTSCR1 is RAZ/WI */
r = 0;
break;
case A_PID4 ... A_CID3:
r = control_id[(offset - A_PID4) / 4];
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"SSE System Counter control frame read: bad offset 0x%x",
(unsigned)offset);
r = 0;
break;
}
trace_sse_counter_control_read(offset, r, size);
return r;
}
static void sse_counter_control_write(void *opaque, hwaddr offset,
uint64_t value, unsigned size)
{
SSECounter *s = SSE_COUNTER(opaque);
trace_sse_counter_control_write(offset, value, size);
switch (offset) {
case A_CNTCR:
/*
* Although CNTCR defines interrupt-related bits, the counter doesn't
* appear to actually have an interrupt output. So INTRCLR is
* effectively a RAZ/WI bit, as are the reserved bits [31:6].
* The documentation does not explicitly say so, but we assume
* that changing the scale factor while the counter is enabled
* by toggling CNTCR.SCEN has the same behaviour (making the counter
* value UNKNOWN) as changing it by writing to CNTSCR, and so we
* don't need to try to recalculate for that case.
*/
value &= CNTCR_VALID_MASK;
if ((value ^ s->cntcr) & R_CNTCR_EN_MASK) {
/*
* Whether the counter is being enabled or disabled, the
* required action is the same: sync the (ns_then, ticks_then)
* tuple.
*/
uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
s->ticks_then = sse_counter_for_timestamp(s, now);
s->ns_then = now;
sse_counter_notify_users(s);
}
s->cntcr = value;
break;
case A_CNTCV_LO:
sse_write_cntcv(s, value, 0);
break;
case A_CNTCV_HI:
sse_write_cntcv(s, value, 32);
break;
case A_CNTSCR:
case A_CNTSCR0:
/*
* If the scale registers are changed when the counter is enabled,
* the count value becomes UNKNOWN. So we don't try to recalculate
* anything here but only do it on a write to CNTCR.EN.
*/
s->cntscr0 = value;
break;
case A_CNTSCR1:
/* If HWCLKSW == 0, CNTSCR1 is RAZ/WI */
break;
case A_CNTSR:
case A_CNTID:
case A_PID4 ... A_CID3:
qemu_log_mask(LOG_GUEST_ERROR,
"SSE System Counter control frame: write to RO offset 0x%x\n",
(unsigned)offset);
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"SSE System Counter control frame: write to bad offset 0x%x\n",
(unsigned)offset);
break;
}
}
static uint64_t sse_counter_status_read(void *opaque, hwaddr offset,
unsigned size)
{
SSECounter *s = SSE_COUNTER(opaque);
uint64_t r;
switch (offset) {
case A_STATUS_CNTCV_LO:
r = extract64(sse_cntcv(s), 0, 32);
break;
case A_STATUS_CNTCV_HI:
r = extract64(sse_cntcv(s), 32, 32);
break;
case A_PID4 ... A_CID3:
r = status_id[(offset - A_PID4) / 4];
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"SSE System Counter status frame read: bad offset 0x%x",
(unsigned)offset);
r = 0;
break;
}
trace_sse_counter_status_read(offset, r, size);
return r;
}
static void sse_counter_status_write(void *opaque, hwaddr offset,
uint64_t value, unsigned size)
{
trace_sse_counter_status_write(offset, value, size);
switch (offset) {
case A_STATUS_CNTCV_LO:
case A_STATUS_CNTCV_HI:
case A_PID4 ... A_CID3:
qemu_log_mask(LOG_GUEST_ERROR,
"SSE System Counter status frame: write to RO offset 0x%x\n",
(unsigned)offset);
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"SSE System Counter status frame: write to bad offset 0x%x\n",
(unsigned)offset);
break;
}
}
static const MemoryRegionOps sse_counter_control_ops = {
.read = sse_counter_control_read,
.write = sse_counter_control_write,
.endianness = DEVICE_LITTLE_ENDIAN,
.valid.min_access_size = 4,
.valid.max_access_size = 4,
};
static const MemoryRegionOps sse_counter_status_ops = {
.read = sse_counter_status_read,
.write = sse_counter_status_write,
.endianness = DEVICE_LITTLE_ENDIAN,
.valid.min_access_size = 4,
.valid.max_access_size = 4,
};
static void sse_counter_reset(DeviceState *dev)
{
SSECounter *s = SSE_COUNTER(dev);
trace_sse_counter_reset();
s->cntcr = 0;
s->cntscr0 = 0x01000000;
s->ns_then = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
s->ticks_then = 0;
}
static void sse_clk_callback(void *opaque, ClockEvent event)
{
SSECounter *s = SSE_COUNTER(opaque);
uint64_t now;
switch (event) {
case ClockPreUpdate:
/*
* Before the clock period updates, set (ticks_then, ns_then)
* to the current time and tick count (as calculated with
* the old clock period).
*/
if (sse_counter_enabled(s)) {
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
s->ticks_then = sse_counter_for_timestamp(s, now);
s->ns_then = now;
}
break;
case ClockUpdate:
sse_counter_notify_users(s);
break;
default:
break;
}
}
static void sse_counter_init(Object *obj)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
SSECounter *s = SSE_COUNTER(obj);
notifier_list_init(&s->notifier_list);
s->clk = qdev_init_clock_in(DEVICE(obj), "CLK", sse_clk_callback, s,
ClockPreUpdate | ClockUpdate);
memory_region_init_io(&s->control_mr, obj, &sse_counter_control_ops,
s, "sse-counter-control", 0x1000);
memory_region_init_io(&s->status_mr, obj, &sse_counter_status_ops,
s, "sse-counter-status", 0x1000);
sysbus_init_mmio(sbd, &s->control_mr);
sysbus_init_mmio(sbd, &s->status_mr);
}
static void sse_counter_realize(DeviceState *dev, Error **errp)
{
SSECounter *s = SSE_COUNTER(dev);
if (!clock_has_source(s->clk)) {
error_setg(errp, "SSE system counter: CLK must be connected");
return;
}
}
static const VMStateDescription sse_counter_vmstate = {
.name = "sse-counter",
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_CLOCK(clk, SSECounter),
VMSTATE_END_OF_LIST()
}
};
static void sse_counter_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->realize = sse_counter_realize;
dc->vmsd = &sse_counter_vmstate;
dc->reset = sse_counter_reset;
}
static const TypeInfo sse_counter_info = {
.name = TYPE_SSE_COUNTER,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(SSECounter),
.instance_init = sse_counter_init,
.class_init = sse_counter_class_init,
};
static void sse_counter_register_types(void)
{
type_register_static(&sse_counter_info);
}
type_init(sse_counter_register_types);

470
hw/timer/sse-timer.c Normal file
View File

@ -0,0 +1,470 @@
/*
* Arm SSE Subsystem System Timer
*
* Copyright (c) 2020 Linaro Limited
* Written by Peter Maydell
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 or
* (at your option) any later version.
*/
/*
* This is a model of the "System timer" which is documented in
* the Arm SSE-123 Example Subsystem Technical Reference Manual:
* https://developer.arm.com/documentation/101370/latest/
*
* The timer is based around a simple 64-bit incrementing counter
* (readable from CNTPCT_HI/LO). The timer fires when
* Counter - CompareValue >= 0.
* The CompareValue is guest-writable, via CNTP_CVAL_HI/LO.
* CNTP_TVAL is an alternative view of the CompareValue defined by
* TimerValue = CompareValue[31:0] - Counter[31:0]
* which can be both read and written.
* This part is similar to the generic timer in an Arm A-class CPU.
*
* The timer also has a separate auto-increment timer. When this
* timer is enabled, then the AutoIncrValue is set to:
* AutoIncrValue = Reload + Counter
* and this timer fires when
* Counter - AutoIncrValue >= 0
* at which point, an interrupt is generated and the new AutoIncrValue
* is calculated.
* When the auto-increment timer is enabled, interrupt generation
* via the compare/timervalue registers is disabled.
*/
#include "qemu/osdep.h"
#include "qemu/log.h"
#include "qemu/timer.h"
#include "qapi/error.h"
#include "trace.h"
#include "hw/timer/sse-timer.h"
#include "hw/timer/sse-counter.h"
#include "hw/sysbus.h"
#include "hw/irq.h"
#include "hw/registerfields.h"
#include "hw/clock.h"
#include "hw/qdev-clock.h"
#include "hw/qdev-properties.h"
#include "migration/vmstate.h"
REG32(CNTPCT_LO, 0x0)
REG32(CNTPCT_HI, 0x4)
REG32(CNTFRQ, 0x10)
REG32(CNTP_CVAL_LO, 0x20)
REG32(CNTP_CVAL_HI, 0x24)
REG32(CNTP_TVAL, 0x28)
REG32(CNTP_CTL, 0x2c)
FIELD(CNTP_CTL, ENABLE, 0, 1)
FIELD(CNTP_CTL, IMASK, 1, 1)
FIELD(CNTP_CTL, ISTATUS, 2, 1)
REG32(CNTP_AIVAL_LO, 0x40)
REG32(CNTP_AIVAL_HI, 0x44)
REG32(CNTP_AIVAL_RELOAD, 0x48)
REG32(CNTP_AIVAL_CTL, 0x4c)
FIELD(CNTP_AIVAL_CTL, EN, 0, 1)
FIELD(CNTP_AIVAL_CTL, CLR, 1, 1)
REG32(CNTP_CFG, 0x50)
FIELD(CNTP_CFG, AIVAL, 0, 4)
#define R_CNTP_CFG_AIVAL_IMPLEMENTED 1
REG32(PID4, 0xFD0)
REG32(PID5, 0xFD4)
REG32(PID6, 0xFD8)
REG32(PID7, 0xFDC)
REG32(PID0, 0xFE0)
REG32(PID1, 0xFE4)
REG32(PID2, 0xFE8)
REG32(PID3, 0xFEC)
REG32(CID0, 0xFF0)
REG32(CID1, 0xFF4)
REG32(CID2, 0xFF8)
REG32(CID3, 0xFFC)
/* PID/CID values */
static const int timer_id[] = {
0x04, 0x00, 0x00, 0x00, /* PID4..PID7 */
0xb7, 0xb0, 0x0b, 0x00, /* PID0..PID3 */
0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */
};
static bool sse_is_autoinc(SSETimer *s)
{
return (s->cntp_aival_ctl & R_CNTP_AIVAL_CTL_EN_MASK) != 0;
}
static bool sse_enabled(SSETimer *s)
{
return (s->cntp_ctl & R_CNTP_CTL_ENABLE_MASK) != 0;
}
static uint64_t sse_cntpct(SSETimer *s)
{
/* Return the CNTPCT value for the current time */
return sse_counter_for_timestamp(s->counter,
qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
}
static bool sse_timer_status(SSETimer *s)
{
/*
* Return true if timer condition is met. This is used for both
* the CNTP_CTL.ISTATUS bit and for whether (unless masked) we
* assert our IRQ.
* The documentation is unclear about the behaviour of ISTATUS when
* in autoincrement mode; we assume that it follows CNTP_AIVAL_CTL.CLR
* (ie whether the autoincrement timer is asserting the interrupt).
*/
if (!sse_enabled(s)) {
return false;
}
if (sse_is_autoinc(s)) {
return s->cntp_aival_ctl & R_CNTP_AIVAL_CTL_CLR_MASK;
} else {
return sse_cntpct(s) >= s->cntp_cval;
}
}
static void sse_update_irq(SSETimer *s)
{
bool irqstate = (!(s->cntp_ctl & R_CNTP_CTL_IMASK_MASK) &&
sse_timer_status(s));
qemu_set_irq(s->irq, irqstate);
}
static void sse_set_timer(SSETimer *s, uint64_t nexttick)
{
/* Set the timer to expire at nexttick */
uint64_t expiry = sse_counter_tick_to_time(s->counter, nexttick);
if (expiry <= INT64_MAX) {
timer_mod_ns(&s->timer, expiry);
} else {
/*
* nexttick is so far in the future that it would overflow the
* signed 64-bit range of a QEMUTimer. Since timer_mod_ns()
* expiry times are absolute, not relative, we are never going
* to be able to set the timer to this value, so we must just
* assume that guest execution can never run so long that it
* reaches the theoretical point when the timer fires.
* This is also the code path for "counter is not running",
* which is signalled by expiry == UINT64_MAX.
*/
timer_del(&s->timer);
}
}
static void sse_recalc_timer(SSETimer *s)
{
/* Recalculate the normal timer */
uint64_t count, nexttick;
if (sse_is_autoinc(s)) {
return;
}
if (!sse_enabled(s)) {
timer_del(&s->timer);
return;
}
count = sse_cntpct(s);
if (count >= s->cntp_cval) {
/*
* Timer condition already met. In theory we have a transition when
* the count rolls back over to 0, but that is so far in the future
* that it is not representable as a timer_mod() expiry, so in
* fact sse_set_timer() will always just delete the timer.
*/
nexttick = UINT64_MAX;
} else {
/* Next transition is when count hits cval */
nexttick = s->cntp_cval;
}
sse_set_timer(s, nexttick);
sse_update_irq(s);
}
static void sse_autoinc(SSETimer *s)
{
/* Auto-increment the AIVAL, and set the timer accordingly */
s->cntp_aival = sse_cntpct(s) + s->cntp_aival_reload;
sse_set_timer(s, s->cntp_aival);
}
static void sse_timer_cb(void *opaque)
{
SSETimer *s = SSE_TIMER(opaque);
if (sse_is_autoinc(s)) {
uint64_t count = sse_cntpct(s);
if (count >= s->cntp_aival) {
/* Timer condition met, set CLR and do another autoinc */
s->cntp_aival_ctl |= R_CNTP_AIVAL_CTL_CLR_MASK;
s->cntp_aival = count + s->cntp_aival_reload;
}
sse_set_timer(s, s->cntp_aival);
sse_update_irq(s);
} else {
sse_recalc_timer(s);
}
}
static uint64_t sse_timer_read(void *opaque, hwaddr offset, unsigned size)
{
SSETimer *s = SSE_TIMER(opaque);
uint64_t r;
switch (offset) {
case A_CNTPCT_LO:
r = extract64(sse_cntpct(s), 0, 32);
break;
case A_CNTPCT_HI:
r = extract64(sse_cntpct(s), 32, 32);
break;
case A_CNTFRQ:
r = s->cntfrq;
break;
case A_CNTP_CVAL_LO:
r = extract64(s->cntp_cval, 0, 32);
break;
case A_CNTP_CVAL_HI:
r = extract64(s->cntp_cval, 32, 32);
break;
case A_CNTP_TVAL:
r = extract64(s->cntp_cval - sse_cntpct(s), 0, 32);
break;
case A_CNTP_CTL:
r = s->cntp_ctl;
if (sse_timer_status(s)) {
r |= R_CNTP_CTL_ISTATUS_MASK;
}
break;
case A_CNTP_AIVAL_LO:
r = extract64(s->cntp_aival, 0, 32);
break;
case A_CNTP_AIVAL_HI:
r = extract64(s->cntp_aival, 32, 32);
break;
case A_CNTP_AIVAL_RELOAD:
r = s->cntp_aival_reload;
break;
case A_CNTP_AIVAL_CTL:
/*
* All the bits of AIVAL_CTL are documented as WO, but this is probably
* a documentation error. We implement them as readable.
*/
r = s->cntp_aival_ctl;
break;
case A_CNTP_CFG:
r = R_CNTP_CFG_AIVAL_IMPLEMENTED << R_CNTP_CFG_AIVAL_SHIFT;
break;
case A_PID4 ... A_CID3:
r = timer_id[(offset - A_PID4) / 4];
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"SSE System Timer read: bad offset 0x%x",
(unsigned) offset);
r = 0;
break;
}
trace_sse_timer_read(offset, r, size);
return r;
}
static void sse_timer_write(void *opaque, hwaddr offset, uint64_t value,
unsigned size)
{
SSETimer *s = SSE_TIMER(opaque);
trace_sse_timer_write(offset, value, size);
switch (offset) {
case A_CNTFRQ:
s->cntfrq = value;
break;
case A_CNTP_CVAL_LO:
s->cntp_cval = deposit64(s->cntp_cval, 0, 32, value);
sse_recalc_timer(s);
break;
case A_CNTP_CVAL_HI:
s->cntp_cval = deposit64(s->cntp_cval, 32, 32, value);
sse_recalc_timer(s);
break;
case A_CNTP_TVAL:
s->cntp_cval = sse_cntpct(s) + sextract64(value, 0, 32);
sse_recalc_timer(s);
break;
case A_CNTP_CTL:
{
uint32_t old_ctl = s->cntp_ctl;
value &= R_CNTP_CTL_ENABLE_MASK | R_CNTP_CTL_IMASK_MASK;
s->cntp_ctl = value;
if ((old_ctl ^ s->cntp_ctl) & R_CNTP_CTL_ENABLE_MASK) {
if (sse_enabled(s)) {
if (sse_is_autoinc(s)) {
sse_autoinc(s);
} else {
sse_recalc_timer(s);
}
}
}
sse_update_irq(s);
break;
}
case A_CNTP_AIVAL_RELOAD:
s->cntp_aival_reload = value;
break;
case A_CNTP_AIVAL_CTL:
{
uint32_t old_ctl = s->cntp_aival_ctl;
/* EN bit is writeable; CLR bit is write-0-to-clear, write-1-ignored */
s->cntp_aival_ctl &= ~R_CNTP_AIVAL_CTL_EN_MASK;
s->cntp_aival_ctl |= value & R_CNTP_AIVAL_CTL_EN_MASK;
if (!(value & R_CNTP_AIVAL_CTL_CLR_MASK)) {
s->cntp_aival_ctl &= ~R_CNTP_AIVAL_CTL_CLR_MASK;
}
if ((old_ctl ^ s->cntp_aival_ctl) & R_CNTP_AIVAL_CTL_EN_MASK) {
/* Auto-increment toggled on/off */
if (sse_enabled(s)) {
if (sse_is_autoinc(s)) {
sse_autoinc(s);
} else {
sse_recalc_timer(s);
}
}
}
sse_update_irq(s);
break;
}
case A_CNTPCT_LO:
case A_CNTPCT_HI:
case A_CNTP_CFG:
case A_CNTP_AIVAL_LO:
case A_CNTP_AIVAL_HI:
case A_PID4 ... A_CID3:
qemu_log_mask(LOG_GUEST_ERROR,
"SSE System Timer write: write to RO offset 0x%x\n",
(unsigned)offset);
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"SSE System Timer write: bad offset 0x%x\n",
(unsigned)offset);
break;
}
}
static const MemoryRegionOps sse_timer_ops = {
.read = sse_timer_read,
.write = sse_timer_write,
.endianness = DEVICE_LITTLE_ENDIAN,
.valid.min_access_size = 4,
.valid.max_access_size = 4,
};
static void sse_timer_reset(DeviceState *dev)
{
SSETimer *s = SSE_TIMER(dev);
trace_sse_timer_reset();
timer_del(&s->timer);
s->cntfrq = 0;
s->cntp_ctl = 0;
s->cntp_cval = 0;
s->cntp_aival = 0;
s->cntp_aival_ctl = 0;
s->cntp_aival_reload = 0;
}
static void sse_timer_counter_callback(Notifier *notifier, void *data)
{
SSETimer *s = container_of(notifier, SSETimer, counter_notifier);
/* System counter told us we need to recalculate */
if (sse_enabled(s)) {
if (sse_is_autoinc(s)) {
sse_set_timer(s, s->cntp_aival);
} else {
sse_recalc_timer(s);
}
}
}
static void sse_timer_init(Object *obj)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
SSETimer *s = SSE_TIMER(obj);
memory_region_init_io(&s->iomem, obj, &sse_timer_ops,
s, "sse-timer", 0x1000);
sysbus_init_mmio(sbd, &s->iomem);
sysbus_init_irq(sbd, &s->irq);
}
static void sse_timer_realize(DeviceState *dev, Error **errp)
{
SSETimer *s = SSE_TIMER(dev);
if (!s->counter) {
error_setg(errp, "counter property was not set");
}
s->counter_notifier.notify = sse_timer_counter_callback;
sse_counter_register_consumer(s->counter, &s->counter_notifier);
timer_init_ns(&s->timer, QEMU_CLOCK_VIRTUAL, sse_timer_cb, s);
}
static const VMStateDescription sse_timer_vmstate = {
.name = "sse-timer",
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_TIMER(timer, SSETimer),
VMSTATE_UINT32(cntfrq, SSETimer),
VMSTATE_UINT32(cntp_ctl, SSETimer),
VMSTATE_UINT64(cntp_cval, SSETimer),
VMSTATE_UINT64(cntp_aival, SSETimer),
VMSTATE_UINT32(cntp_aival_ctl, SSETimer),
VMSTATE_UINT32(cntp_aival_reload, SSETimer),
VMSTATE_END_OF_LIST()
}
};
static Property sse_timer_properties[] = {
DEFINE_PROP_LINK("counter", SSETimer, counter, TYPE_SSE_COUNTER, SSECounter *),
DEFINE_PROP_END_OF_LIST(),
};
static void sse_timer_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->realize = sse_timer_realize;
dc->vmsd = &sse_timer_vmstate;
dc->reset = sse_timer_reset;
device_class_set_props(dc, sse_timer_properties);
}
static const TypeInfo sse_timer_info = {
.name = TYPE_SSE_TIMER,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(SSETimer),
.instance_init = sse_timer_init,
.class_init = sse_timer_class_init,
};
static void sse_timer_register_types(void)
{
type_register_static(&sse_timer_info);
}
type_init(sse_timer_register_types);

View File

@ -93,3 +93,15 @@ avr_timer16_interrupt_count(uint8_t cnt) "count: %u"
avr_timer16_interrupt_overflow(const char *reason) "overflow: %s"
avr_timer16_next_alarm(uint64_t delay_ns) "next alarm: %" PRIu64 " ns from now"
avr_timer16_clksrc_update(uint64_t freq_hz, uint64_t period_ns, uint64_t delay_s) "timer frequency: %" PRIu64 " Hz, period: %" PRIu64 " ns (%" PRId64 " us)"
# sse_counter.c
sse_counter_control_read(uint64_t offset, uint64_t data, unsigned size) "SSE system counter control frame read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
sse_counter_control_write(uint64_t offset, uint64_t data, unsigned size) "SSE system counter control framen write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
sse_counter_status_read(uint64_t offset, uint64_t data, unsigned size) "SSE system counter status frame read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
sse_counter_status_write(uint64_t offset, uint64_t data, unsigned size) "SSE system counter status frame write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
sse_counter_reset(void) "SSE system counter: reset"
# sse_timer.c
sse_timer_read(uint64_t offset, uint64_t data, unsigned size) "SSE system timer read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
sse_timer_write(uint64_t offset, uint64_t data, unsigned size) "SSE system timer write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
sse_timer_reset(void) "SSE system timer: reset"

View File

@ -310,7 +310,7 @@ static void cmsdk_apb_watchdog_reset(DeviceState *dev)
ptimer_transaction_commit(s->timer);
}
static void cmsdk_apb_watchdog_clk_update(void *opaque)
static void cmsdk_apb_watchdog_clk_update(void *opaque, ClockEvent event)
{
CMSDKAPBWatchdog *s = CMSDK_APB_WATCHDOG(opaque);
@ -329,7 +329,8 @@ static void cmsdk_apb_watchdog_init(Object *obj)
sysbus_init_mmio(sbd, &s->iomem);
sysbus_init_irq(sbd, &s->wdogint);
s->wdogclk = qdev_init_clock_in(DEVICE(s), "WDOGCLK",
cmsdk_apb_watchdog_clk_update, s);
cmsdk_apb_watchdog_clk_update, s,
ClockUpdate);
s->is_luminary = false;
s->id = cmsdk_apb_watchdog_id;

View File

@ -0,0 +1,42 @@
/*
* ARM SSE (Subsystems for Embedded): IoTKit, SSE-200
*
* Copyright (c) 2020 Linaro Limited
* Written by Peter Maydell
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 or
* (at your option) any later version.
*/
#ifndef ARMSSE_VERSION_H
#define ARMSSE_VERSION_H
/*
* Define an enumeration of the possible values of the sse-version
* property implemented by various sub-devices of the SSE, and
* a validation function that checks that a valid value has been passed.
* These are arbitrary QEMU-internal values (nobody should be creating
* the sub-devices of the SSE except for the SSE object itself), but
* we pick obvious numbers for the benefit of people debugging with gdb.
*/
enum {
ARMSSE_IOTKIT = 0,
ARMSSE_SSE200 = 200,
ARMSSE_SSE300 = 300,
};
static inline bool armsse_version_valid(uint32_t sse_version)
{
switch (sse_version) {
case ARMSSE_IOTKIT:
case ARMSSE_SSE200:
case ARMSSE_SSE300:
return true;
default:
return false;
}
}
#endif

View File

@ -97,11 +97,14 @@
#include "hw/misc/tz-mpc.h"
#include "hw/timer/cmsdk-apb-timer.h"
#include "hw/timer/cmsdk-apb-dualtimer.h"
#include "hw/timer/sse-counter.h"
#include "hw/timer/sse-timer.h"
#include "hw/watchdog/cmsdk-apb-watchdog.h"
#include "hw/misc/iotkit-sysctl.h"
#include "hw/misc/iotkit-sysinfo.h"
#include "hw/misc/armsse-cpuid.h"
#include "hw/misc/armsse-mhu.h"
#include "hw/misc/armsse-cpu-pwrctrl.h"
#include "hw/misc/unimp.h"
#include "hw/or-irq.h"
#include "hw/clock.h"
@ -120,12 +123,14 @@ OBJECT_DECLARE_TYPE(ARMSSE, ARMSSEClass,
*/
#define TYPE_IOTKIT "iotkit"
#define TYPE_SSE200 "sse-200"
#define TYPE_SSE300 "sse-300"
/* We have an IRQ splitter and an OR gate input for each external PPC
* and the 2 internal PPCs
*/
#define NUM_INTERNAL_PPCS 2
#define NUM_EXTERNAL_PPCS (IOTS_NUM_AHB_EXP_PPC + IOTS_NUM_APB_EXP_PPC)
#define NUM_PPCS (NUM_EXTERNAL_PPCS + 2)
#define NUM_PPCS (NUM_EXTERNAL_PPCS + NUM_INTERNAL_PPCS)
#define MAX_SRAM_BANKS 4
#if MAX_SRAM_BANKS > IOTS_NUM_MPC
@ -134,15 +139,10 @@ OBJECT_DECLARE_TYPE(ARMSSE, ARMSSEClass,
#define SSE_MAX_CPUS 2
/* These define what each PPU in the ppu[] index is for */
#define CPU0CORE_PPU 0
#define CPU1CORE_PPU 1
#define DBG_PPU 2
#define RAM0_PPU 3
#define RAM1_PPU 4
#define RAM2_PPU 5
#define RAM3_PPU 6
#define NUM_PPUS 7
#define NUM_PPUS 8
/* Number of CPU IRQs used by the SSE itself */
#define NUM_SSE_IRQS 32
struct ARMSSE {
/*< private >*/
@ -152,12 +152,9 @@ struct ARMSSE {
ARMv7MState armv7m[SSE_MAX_CPUS];
CPUClusterState cluster[SSE_MAX_CPUS];
IoTKitSecCtl secctl;
TZPPC apb_ppc0;
TZPPC apb_ppc1;
TZPPC apb_ppc[NUM_INTERNAL_PPCS];
TZMPC mpc[IOTS_NUM_MPC];
CMSDKAPBTimer timer0;
CMSDKAPBTimer timer1;
CMSDKAPBTimer s32ktimer;
CMSDKAPBTimer timer[3];
qemu_or_irq ppc_irq_orgate;
SplitIRQ sec_resp_splitter;
SplitIRQ ppc_irq_splitter[NUM_PPCS];
@ -165,24 +162,27 @@ struct ARMSSE {
qemu_or_irq mpc_irq_orgate;
qemu_or_irq nmi_orgate;
SplitIRQ cpu_irq_splitter[32];
SplitIRQ cpu_irq_splitter[NUM_SSE_IRQS];
CMSDKAPBDualTimer dualtimer;
CMSDKAPBWatchdog s32kwatchdog;
CMSDKAPBWatchdog nswatchdog;
CMSDKAPBWatchdog swatchdog;
CMSDKAPBWatchdog cmsdk_watchdog[3];
SSECounter sse_counter;
SSETimer sse_timer[4];
IoTKitSysCtl sysctl;
IoTKitSysCtl sysinfo;
ARMSSEMHU mhu[2];
UnimplementedDeviceState ppu[NUM_PPUS];
UnimplementedDeviceState unimp[NUM_PPUS];
UnimplementedDeviceState cachectrl[SSE_MAX_CPUS];
UnimplementedDeviceState cpusecctrl[SSE_MAX_CPUS];
ARMSSECPUID cpuid[SSE_MAX_CPUS];
ARMSSECPUPwrCtrl cpu_pwrctrl[SSE_MAX_CPUS];
/*
* 'container' holds all devices seen by all CPUs.
* 'cpu_container[i]' is the view that CPU i has: this has the

View File

@ -35,6 +35,7 @@
#include "target/arm/cpu.h"
#include "qom/object.h"
#include "net/can_emu.h"
#include "hw/dma/xlnx_csu_dma.h"
#define TYPE_XLNX_ZYNQMP "xlnx,zynqmp"
OBJECT_DECLARE_SIMPLE_TYPE(XlnxZynqMPState, XLNX_ZYNQMP)
@ -60,7 +61,8 @@ OBJECT_DECLARE_SIMPLE_TYPE(XlnxZynqMPState, XLNX_ZYNQMP)
#define XLNX_ZYNQMP_GIC_REGIONS 6
/* ZynqMP maps the ARM GIC regions (GICC, GICD ...) at consecutive 64k offsets
/*
* ZynqMP maps the ARM GIC regions (GICC, GICD ...) at consecutive 64k offsets
* and under-decodes the 64k region. This mirrors the 4k regions to every 4k
* aligned address in the 64k region. To implement each GIC region needs a
* number of memory region aliases.
@ -107,6 +109,7 @@ struct XlnxZynqMPState {
XlnxZynqMPRTC rtc;
XlnxZDMA gdma[XLNX_ZYNQMP_NUM_GDMA_CH];
XlnxZDMA adma[XLNX_ZYNQMP_NUM_ADMA_CH];
XlnxCSUDMA qspi_dma;
char *boot_cpu;
ARMCPU *boot_cpu_ptr;

View File

@ -22,7 +22,18 @@
#define TYPE_CLOCK "clock"
OBJECT_DECLARE_SIMPLE_TYPE(Clock, CLOCK)
typedef void ClockCallback(void *opaque);
/*
* Argument to ClockCallback functions indicating why the callback
* has been called. A mask of these values logically ORed together
* is used to specify which events are interesting when the callback
* is registered, so these values must all be different bit values.
*/
typedef enum ClockEvent {
ClockUpdate = 1, /* Clock period has just updated */
ClockPreUpdate = 2, /* Clock period is about to update */
} ClockEvent;
typedef void ClockCallback(void *opaque, ClockEvent event);
/*
* clock store a value representing the clock's period in 2^-32ns unit.
@ -50,6 +61,7 @@ typedef void ClockCallback(void *opaque);
* @canonical_path: clock path string cache (used for trace purpose)
* @callback: called when clock changes
* @callback_opaque: argument for @callback
* @callback_events: mask of events when callback should be called
* @source: source (or parent in clock tree) of the clock
* @children: list of clocks connected to this one (it is their source)
* @sibling: structure used to form a clock list
@ -67,6 +79,7 @@ struct Clock {
char *canonical_path;
ClockCallback *callback;
void *callback_opaque;
unsigned int callback_events;
/* Clocks are organized in a clock tree */
Clock *source;
@ -114,10 +127,15 @@ Clock *clock_new(Object *parent, const char *name);
* @clk: the clock to register the callback into
* @cb: the callback function
* @opaque: the argument to the callback
* @events: the events the callback should be called for
* (logical OR of ClockEvent enum values)
*
* Register a callback called on every clock update.
* Note that a clock has only one callback: you cannot register
* different callback functions for different events.
*/
void clock_set_callback(Clock *clk, ClockCallback *cb, void *opaque);
void clock_set_callback(Clock *clk, ClockCallback *cb,
void *opaque, unsigned int events);
/**
* clock_clear_callback:
@ -268,6 +286,47 @@ static inline uint64_t clock_ticks_to_ns(const Clock *clk, uint64_t ticks)
return ns_low >> 32 | ns_high << 32;
}
/**
* clock_ns_to_ticks:
* @clk: the clock to query
* @ns: duration in nanoseconds
*
* Returns the number of ticks this clock would make in the given
* number of nanoseconds. Because a clock can have a period which
* is not a whole number of nanoseconds, it is important to use this
* function rather than attempting to obtain a "period in nanoseconds"
* value and then dividing the duration by that value.
*
* If the clock is stopped (ie it has period zero), returns 0.
*
* For some inputs the result could overflow a 64-bit value (because
* the clock's period is short and the duration is long). In these
* cases we truncate the result to a 64-bit value. This is on the
* assumption that generally the result is going to be used to report
* a 32-bit or 64-bit guest register value, so wrapping either cannot
* happen or is the desired behaviour.
*/
static inline uint64_t clock_ns_to_ticks(const Clock *clk, uint64_t ns)
{
/*
* ticks = duration_in_ns / period_in_ns
* = ns / (period / 2^32)
* = (ns * 2^32) / period
* The hi, lo inputs to divu128() are (ns << 32) as a 128 bit value.
*/
uint64_t lo = ns << 32;
uint64_t hi = ns >> 32;
if (clk->period == 0) {
return 0;
}
/*
* Ignore divu128() return value as we've caught div-by-zero and don't
* need different behaviour for overflow.
*/
divu128(&lo, &hi, clk->period);
return lo;
}
/**
* clock_is_enabled:
* @clk: a clock

View File

@ -0,0 +1,52 @@
/*
* Xilinx Platform CSU Stream DMA emulation
*
* This implementation is based on
* https://github.com/Xilinx/qemu/blob/master/hw/dma/csu_stream_dma.c
*
* 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 or
* (at your option) version 3 of the License.
*
* 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/>.
*/
#ifndef XLNX_CSU_DMA_H
#define XLNX_CSU_DMA_H
#define TYPE_XLNX_CSU_DMA "xlnx.csu_dma"
#define XLNX_CSU_DMA_R_MAX (0x2c / 4)
typedef struct XlnxCSUDMA {
SysBusDevice busdev;
MemoryRegion iomem;
MemTxAttrs attr;
MemoryRegion *dma_mr;
AddressSpace *dma_as;
qemu_irq irq;
StreamSink *tx_dev; /* Used as generic StreamSink */
ptimer_state *src_timer;
uint16_t width;
bool is_dst;
bool r_size_last_word;
StreamCanPushNotifyFn notify;
void *notify_opaque;
uint32_t regs[XLNX_CSU_DMA_R_MAX];
RegisterInfo regs_info[XLNX_CSU_DMA_R_MAX];
} XlnxCSUDMA;
#define XLNX_CSU_DMA(obj) \
OBJECT_CHECK(XlnxCSUDMA, (obj), TYPE_XLNX_CSU_DMA)
#endif

View File

@ -0,0 +1,40 @@
/*
* ARM SSE CPU PWRCTRL register block
*
* Copyright (c) 2021 Linaro Limited
* Written by Peter Maydell
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 or
* (at your option) any later version.
*/
/*
* This is a model of the "CPU<N>_PWRCTRL block" which is part of the
* Arm Corstone SSE-300 Example Subsystem and documented in
* https://developer.arm.com/documentation/101773/0000
*
* QEMU interface:
* + sysbus MMIO region 0: the register bank
*/
#ifndef HW_MISC_ARMSSE_CPU_PWRCTRL_H
#define HW_MISC_ARMSSE_CPU_PWRCTRL_H
#include "hw/sysbus.h"
#include "qom/object.h"
#define TYPE_ARMSSE_CPU_PWRCTRL "armsse-cpu-pwrctrl"
OBJECT_DECLARE_SIMPLE_TYPE(ARMSSECPUPwrCtrl, ARMSSE_CPU_PWRCTRL)
struct ARMSSECPUPwrCtrl {
/*< private >*/
SysBusDevice parent_obj;
/*< public >*/
MemoryRegion iomem;
uint32_t cpupwrcfg;
};
#endif

View File

@ -120,6 +120,8 @@ struct IoTKitSecCtl {
IoTKitSecCtlPPC apb[IOTS_NUM_APB_PPC];
IoTKitSecCtlPPC apbexp[IOTS_NUM_APB_EXP_PPC];
IoTKitSecCtlPPC ahbexp[IOTS_NUM_APB_EXP_PPC];
uint32_t sse_version;
};
#endif

View File

@ -17,9 +17,8 @@
* "system control register" blocks.
*
* QEMU interface:
* + QOM property "SYS_VERSION": value of the SYS_VERSION register of the
* system information block of the SSE
* (used to identify whether to provide SSE-200-only registers)
* + QOM property "sse-version": indicates which SSE version this is part of
* (used to identify whether to provide SSE-200-only registers, etc)
* + sysbus MMIO region 0: the system information register bank
* + sysbus MMIO region 1: the system control register bank
*/
@ -54,19 +53,21 @@ struct IoTKitSysCtl {
uint32_t initsvtor1;
uint32_t nmi_enable;
uint32_t ewctrl;
uint32_t pwrctrl;
uint32_t pdcm_pd_sys_sense;
uint32_t pdcm_pd_sram0_sense;
uint32_t pdcm_pd_sram1_sense;
uint32_t pdcm_pd_sram2_sense;
uint32_t pdcm_pd_sram3_sense;
uint32_t pdcm_pd_cpu0_sense;
uint32_t pdcm_pd_vmr0_sense;
uint32_t pdcm_pd_vmr1_sense;
/* Properties */
uint32_t sys_version;
uint32_t sse_version;
uint32_t cpuwait_rst;
uint32_t initsvtor0_rst;
uint32_t initsvtor1_rst;
bool is_sse200;
};
#endif

View File

@ -38,6 +38,8 @@ struct IoTKitSysInfo {
/* Properties */
uint32_t sys_version;
uint32_t sys_config;
uint32_t sse_version;
uint32_t iidr;
};
#endif

View File

@ -39,10 +39,12 @@ struct MPS2FPGAIO {
LEDState *led[MPS2FPGAIO_MAX_LEDS];
uint32_t num_leds;
bool has_switches;
bool has_dbgctrl;
uint32_t led0;
uint32_t prescale;
uint32_t misc;
uint32_t dbgctrl;
/* QEMU_CLOCK_VIRTUAL time at which counter and pscntr were last synced */
int64_t pscntr_sync_ticks;

View File

@ -22,6 +22,8 @@
* @name: the name of the clock (can't be NULL).
* @callback: optional callback to be called on update or NULL.
* @opaque: argument for the callback
* @events: the events the callback should be called for
* (logical OR of ClockEvent enum values)
* @returns: a pointer to the newly added clock
*
* Add an input clock to device @dev as a clock named @name.
@ -29,7 +31,8 @@
* The callback will be called with @opaque as opaque parameter.
*/
Clock *qdev_init_clock_in(DeviceState *dev, const char *name,
ClockCallback *callback, void *opaque);
ClockCallback *callback, void *opaque,
unsigned int events);
/**
* qdev_init_clock_out:
@ -105,6 +108,7 @@ void qdev_finalize_clocklist(DeviceState *dev);
* @output: indicates whether the clock is input or output
* @callback: for inputs, optional callback to be called on clock's update
* with device as opaque
* @callback_events: mask of ClockEvent values for when callback is called
* @offset: optional offset to store the ClockIn or ClockOut pointer in device
* state structure (0 means unused)
*/
@ -112,6 +116,7 @@ struct ClockPortInitElem {
const char *name;
bool is_output;
ClockCallback *callback;
unsigned int callback_events;
size_t offset;
};
@ -119,10 +124,11 @@ struct ClockPortInitElem {
(offsetof(devstate, field) + \
type_check(Clock *, typeof_field(devstate, field)))
#define QDEV_CLOCK(out_not_in, devstate, field, cb) { \
#define QDEV_CLOCK(out_not_in, devstate, field, cb, cbevents) { \
.name = (stringify(field)), \
.is_output = out_not_in, \
.callback = cb, \
.callback_events = cbevents, \
.offset = clock_offset_value(devstate, field), \
}
@ -133,14 +139,15 @@ struct ClockPortInitElem {
* @field: a field in @_devstate (must be Clock*)
* @callback: (for input only) callback (or NULL) to be called with the device
* state as argument
* @cbevents: (for input only) ClockEvent mask for when callback is called
*
* The name of the clock will be derived from @field
*/
#define QDEV_CLOCK_IN(devstate, field, callback) \
QDEV_CLOCK(false, devstate, field, callback)
#define QDEV_CLOCK_IN(devstate, field, callback, cbevents) \
QDEV_CLOCK(false, devstate, field, callback, cbevents)
#define QDEV_CLOCK_OUT(devstate, field) \
QDEV_CLOCK(true, devstate, field, NULL)
QDEV_CLOCK(true, devstate, field, NULL, 0)
#define QDEV_CLOCK_END { .name = NULL }

View File

@ -34,7 +34,7 @@
typedef struct XilinxSPIPS XilinxSPIPS;
#define XLNX_SPIPS_R_MAX (0x100 / 4)
#define XLNX_ZYNQMP_SPIPS_R_MAX (0x830 / 4)
#define XLNX_ZYNQMP_SPIPS_R_MAX (0x200 / 4)
/* Bite off 4k chunks at a time */
#define LQSPI_CACHE_SIZE 1024

View File

@ -0,0 +1,105 @@
/*
* Arm SSE Subsystem System Counter
*
* Copyright (c) 2020 Linaro Limited
* Written by Peter Maydell
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 or
* (at your option) any later version.
*/
/*
* This is a model of the "System counter" which is documented in
* the Arm SSE-123 Example Subsystem Technical Reference Manual:
* https://developer.arm.com/documentation/101370/latest/
*
* QEMU interface:
* + Clock input "CLK": clock
* + sysbus MMIO region 0: the control register frame
* + sysbus MMIO region 1: the status register frame
*
* Consumers of the system counter's timestamp, such as the SSE
* System Timer device, can also use the APIs sse_counter_for_timestamp(),
* sse_counter_tick_to_time() and sse_counter_register_consumer() to
* interact with an instance of the System Counter. Generally the
* consumer device should have a QOM link property which the board
* code can set to the appropriate instance of the system counter.
*/
#ifndef SSE_COUNTER_H
#define SSE_COUNTER_H
#include "hw/sysbus.h"
#include "qom/object.h"
#include "qemu/notify.h"
#define TYPE_SSE_COUNTER "sse-counter"
OBJECT_DECLARE_SIMPLE_TYPE(SSECounter, SSE_COUNTER)
struct SSECounter {
/*< private >*/
SysBusDevice parent_obj;
/*< public >*/
MemoryRegion control_mr;
MemoryRegion status_mr;
Clock *clk;
NotifierList notifier_list;
uint32_t cntcr;
uint32_t cntscr0;
/*
* These are used for handling clock frequency changes: they are a
* tuple of (QEMU_CLOCK_VIRTUAL timestamp, CNTCV at that time),
* taken when the clock frequency changes. sse_cntcv() needs them
* to calculate the current CNTCV.
*/
uint64_t ns_then;
uint64_t ticks_then;
};
/*
* These functions are the interface by which a consumer of
* the system timestamp (such as the SSE system timer device)
* can communicate with the SSECounter.
*/
/**
* sse_counter_for_timestamp:
* @counter: SSECounter
* @ns: timestamp of QEMU_CLOCK_VIRTUAL in nanoseconds
*
* Returns the value of the timestamp counter at the specified
* point in time (assuming that no changes to scale factor, enable, etc
* happen in the meantime).
*/
uint64_t sse_counter_for_timestamp(SSECounter *counter, uint64_t ns);
/**
* sse_counter_tick_to_time:
* @counter: SSECounter
* @tick: tick value
*
* Returns the time (a QEMU_CLOCK_VIRTUAL timestamp in nanoseconds)
* when the timestamp counter will reach the specified tick count.
* If the counter is not currently running, returns UINT64_MAX.
*/
uint64_t sse_counter_tick_to_time(SSECounter *counter, uint64_t tick);
/**
* sse_counter_register_consumer:
* @counter: SSECounter
* @notifier: Notifier which is notified on counter changes
*
* Registers @notifier with the SSECounter. When the counter's
* configuration changes in a way that might invalidate information
* previously returned via sse_counter_for_timestamp() or
* sse_counter_tick_to_time(), the notifier will be called.
* Devices which consume the timestamp counter can use this as
* a cue to recalculate timer events.
*/
void sse_counter_register_consumer(SSECounter *counter, Notifier *notifier);
#endif

View File

@ -0,0 +1,53 @@
/*
* Arm SSE Subsystem System Timer
*
* Copyright (c) 2020 Linaro Limited
* Written by Peter Maydell
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 or
* (at your option) any later version.
*/
/*
* This is a model of the "System timer" which is documented in
* the Arm SSE-123 Example Subsystem Technical Reference Manual:
* https://developer.arm.com/documentation/101370/latest/
*
* QEMU interface:
* + QOM property "counter": link property to be set to the
* TYPE_SSE_COUNTER timestamp counter device this timer runs off
* + sysbus MMIO region 0: the register bank
* + sysbus IRQ 0: timer interrupt
*/
#ifndef SSE_TIMER_H
#define SSE_TIMER_H
#include "hw/sysbus.h"
#include "qom/object.h"
#include "hw/timer/sse-counter.h"
#define TYPE_SSE_TIMER "sse-timer"
OBJECT_DECLARE_SIMPLE_TYPE(SSETimer, SSE_TIMER)
struct SSETimer {
/*< private >*/
SysBusDevice parent_obj;
/*< public >*/
MemoryRegion iomem;
qemu_irq irq;
SSECounter *counter;
QEMUTimer timer;
Notifier counter_notifier;
uint32_t cntfrq;
uint32_t cntp_ctl;
uint64_t cntp_cval;
uint64_t cntp_aival;
uint32_t cntp_aival_ctl;
uint32_t cntp_aival_reload;
};
#endif

View File

@ -1922,331 +1922,6 @@ static ObjectClass *arm_cpu_class_by_name(const char *cpu_model)
return oc;
}
/* CPU models. These are not needed for the AArch64 linux-user build. */
#if !defined(CONFIG_USER_ONLY) || !defined(TARGET_AARCH64)
static const ARMCPRegInfo cortexa8_cp_reginfo[] = {
{ .name = "L2LOCKDOWN", .cp = 15, .crn = 9, .crm = 0, .opc1 = 1, .opc2 = 0,
.access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
{ .name = "L2AUXCR", .cp = 15, .crn = 9, .crm = 0, .opc1 = 1, .opc2 = 2,
.access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
REGINFO_SENTINEL
};
static void cortex_a8_initfn(Object *obj)
{
ARMCPU *cpu = ARM_CPU(obj);
cpu->dtb_compatible = "arm,cortex-a8";
set_feature(&cpu->env, ARM_FEATURE_V7);
set_feature(&cpu->env, ARM_FEATURE_NEON);
set_feature(&cpu->env, ARM_FEATURE_THUMB2EE);
set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS);
set_feature(&cpu->env, ARM_FEATURE_EL3);
cpu->midr = 0x410fc080;
cpu->reset_fpsid = 0x410330c0;
cpu->isar.mvfr0 = 0x11110222;
cpu->isar.mvfr1 = 0x00011111;
cpu->ctr = 0x82048004;
cpu->reset_sctlr = 0x00c50078;
cpu->isar.id_pfr0 = 0x1031;
cpu->isar.id_pfr1 = 0x11;
cpu->isar.id_dfr0 = 0x400;
cpu->id_afr0 = 0;
cpu->isar.id_mmfr0 = 0x31100003;
cpu->isar.id_mmfr1 = 0x20000000;
cpu->isar.id_mmfr2 = 0x01202000;
cpu->isar.id_mmfr3 = 0x11;
cpu->isar.id_isar0 = 0x00101111;
cpu->isar.id_isar1 = 0x12112111;
cpu->isar.id_isar2 = 0x21232031;
cpu->isar.id_isar3 = 0x11112131;
cpu->isar.id_isar4 = 0x00111142;
cpu->isar.dbgdidr = 0x15141000;
cpu->clidr = (1 << 27) | (2 << 24) | 3;
cpu->ccsidr[0] = 0xe007e01a; /* 16k L1 dcache. */
cpu->ccsidr[1] = 0x2007e01a; /* 16k L1 icache. */
cpu->ccsidr[2] = 0xf0000000; /* No L2 icache. */
cpu->reset_auxcr = 2;
define_arm_cp_regs(cpu, cortexa8_cp_reginfo);
}
static const ARMCPRegInfo cortexa9_cp_reginfo[] = {
/*
* 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,
.access = PL1_RW, .resetvalue = 0,
.fieldoffset = offsetof(CPUARMState, cp15.c15_power_control) },
{ .name = "A9_DIAG", .cp = 15, .crn = 15, .crm = 0, .opc1 = 0, .opc2 = 1,
.access = PL1_RW, .resetvalue = 0,
.fieldoffset = offsetof(CPUARMState, cp15.c15_diagnostic) },
{ .name = "A9_PWRDIAG", .cp = 15, .crn = 15, .crm = 0, .opc1 = 0, .opc2 = 2,
.access = PL1_RW, .resetvalue = 0,
.fieldoffset = offsetof(CPUARMState, cp15.c15_power_diagnostic) },
{ .name = "NEONBUSY", .cp = 15, .crn = 15, .crm = 1, .opc1 = 0, .opc2 = 0,
.access = PL1_RW, .resetvalue = 0, .type = ARM_CP_CONST },
/* TLB lockdown control */
{ .name = "TLB_LOCKR", .cp = 15, .crn = 15, .crm = 4, .opc1 = 5, .opc2 = 2,
.access = PL1_W, .resetvalue = 0, .type = ARM_CP_NOP },
{ .name = "TLB_LOCKW", .cp = 15, .crn = 15, .crm = 4, .opc1 = 5, .opc2 = 4,
.access = PL1_W, .resetvalue = 0, .type = ARM_CP_NOP },
{ .name = "TLB_VA", .cp = 15, .crn = 15, .crm = 5, .opc1 = 5, .opc2 = 2,
.access = PL1_RW, .resetvalue = 0, .type = ARM_CP_CONST },
{ .name = "TLB_PA", .cp = 15, .crn = 15, .crm = 6, .opc1 = 5, .opc2 = 2,
.access = PL1_RW, .resetvalue = 0, .type = ARM_CP_CONST },
{ .name = "TLB_ATTR", .cp = 15, .crn = 15, .crm = 7, .opc1 = 5, .opc2 = 2,
.access = PL1_RW, .resetvalue = 0, .type = ARM_CP_CONST },
REGINFO_SENTINEL
};
static void cortex_a9_initfn(Object *obj)
{
ARMCPU *cpu = ARM_CPU(obj);
cpu->dtb_compatible = "arm,cortex-a9";
set_feature(&cpu->env, ARM_FEATURE_V7);
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
* A9UP and single-core A9MP (which are both different
* and valid configurations; we don't model A9UP).
*/
set_feature(&cpu->env, ARM_FEATURE_V7MP);
set_feature(&cpu->env, ARM_FEATURE_CBAR);
cpu->midr = 0x410fc090;
cpu->reset_fpsid = 0x41033090;
cpu->isar.mvfr0 = 0x11110222;
cpu->isar.mvfr1 = 0x01111111;
cpu->ctr = 0x80038003;
cpu->reset_sctlr = 0x00c50078;
cpu->isar.id_pfr0 = 0x1031;
cpu->isar.id_pfr1 = 0x11;
cpu->isar.id_dfr0 = 0x000;
cpu->id_afr0 = 0;
cpu->isar.id_mmfr0 = 0x00100103;
cpu->isar.id_mmfr1 = 0x20000000;
cpu->isar.id_mmfr2 = 0x01230000;
cpu->isar.id_mmfr3 = 0x00002111;
cpu->isar.id_isar0 = 0x00101111;
cpu->isar.id_isar1 = 0x13112111;
cpu->isar.id_isar2 = 0x21232041;
cpu->isar.id_isar3 = 0x11112131;
cpu->isar.id_isar4 = 0x00111142;
cpu->isar.dbgdidr = 0x35141000;
cpu->clidr = (1 << 27) | (1 << 24) | 3;
cpu->ccsidr[0] = 0xe00fe019; /* 16k L1 dcache. */
cpu->ccsidr[1] = 0x200fe019; /* 16k L1 icache. */
define_arm_cp_regs(cpu, cortexa9_cp_reginfo);
}
#ifndef CONFIG_USER_ONLY
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.
* Might as well set the interrupt-controller bit too.
*/
return ((ms->smp.cpus - 1) << 24) | (1 << 23);
}
#endif
static const ARMCPRegInfo cortexa15_cp_reginfo[] = {
#ifndef CONFIG_USER_ONLY
{ .name = "L2CTLR", .cp = 15, .crn = 9, .crm = 0, .opc1 = 1, .opc2 = 2,
.access = PL1_RW, .resetvalue = 0, .readfn = a15_l2ctlr_read,
.writefn = arm_cp_write_ignore, },
#endif
{ .name = "L2ECTLR", .cp = 15, .crn = 9, .crm = 0, .opc1 = 1, .opc2 = 3,
.access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
REGINFO_SENTINEL
};
static void cortex_a7_initfn(Object *obj)
{
ARMCPU *cpu = ARM_CPU(obj);
cpu->dtb_compatible = "arm,cortex-a7";
set_feature(&cpu->env, ARM_FEATURE_V7VE);
set_feature(&cpu->env, ARM_FEATURE_NEON);
set_feature(&cpu->env, ARM_FEATURE_THUMB2EE);
set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER);
set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS);
set_feature(&cpu->env, ARM_FEATURE_CBAR_RO);
set_feature(&cpu->env, ARM_FEATURE_EL2);
set_feature(&cpu->env, ARM_FEATURE_EL3);
set_feature(&cpu->env, ARM_FEATURE_PMU);
cpu->kvm_target = QEMU_KVM_ARM_TARGET_CORTEX_A7;
cpu->midr = 0x410fc075;
cpu->reset_fpsid = 0x41023075;
cpu->isar.mvfr0 = 0x10110222;
cpu->isar.mvfr1 = 0x11111111;
cpu->ctr = 0x84448003;
cpu->reset_sctlr = 0x00c50078;
cpu->isar.id_pfr0 = 0x00001131;
cpu->isar.id_pfr1 = 0x00011011;
cpu->isar.id_dfr0 = 0x02010555;
cpu->id_afr0 = 0x00000000;
cpu->isar.id_mmfr0 = 0x10101105;
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
* table 4-41 gives 0x02101110, which includes the arm div insns.
*/
cpu->isar.id_isar0 = 0x02101110;
cpu->isar.id_isar1 = 0x13112111;
cpu->isar.id_isar2 = 0x21232041;
cpu->isar.id_isar3 = 0x11112131;
cpu->isar.id_isar4 = 0x10011142;
cpu->isar.dbgdidr = 0x3515f005;
cpu->clidr = 0x0a200023;
cpu->ccsidr[0] = 0x701fe00a; /* 32K L1 dcache */
cpu->ccsidr[1] = 0x201fe00a; /* 32K L1 icache */
cpu->ccsidr[2] = 0x711fe07a; /* 4096K L2 unified cache */
define_arm_cp_regs(cpu, cortexa15_cp_reginfo); /* Same as A15 */
}
static void cortex_a15_initfn(Object *obj)
{
ARMCPU *cpu = ARM_CPU(obj);
cpu->dtb_compatible = "arm,cortex-a15";
set_feature(&cpu->env, ARM_FEATURE_V7VE);
set_feature(&cpu->env, ARM_FEATURE_NEON);
set_feature(&cpu->env, ARM_FEATURE_THUMB2EE);
set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER);
set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS);
set_feature(&cpu->env, ARM_FEATURE_CBAR_RO);
set_feature(&cpu->env, ARM_FEATURE_EL2);
set_feature(&cpu->env, ARM_FEATURE_EL3);
set_feature(&cpu->env, ARM_FEATURE_PMU);
cpu->kvm_target = QEMU_KVM_ARM_TARGET_CORTEX_A15;
cpu->midr = 0x412fc0f1;
cpu->reset_fpsid = 0x410430f0;
cpu->isar.mvfr0 = 0x10110222;
cpu->isar.mvfr1 = 0x11111111;
cpu->ctr = 0x8444c004;
cpu->reset_sctlr = 0x00c50078;
cpu->isar.id_pfr0 = 0x00001131;
cpu->isar.id_pfr1 = 0x00011011;
cpu->isar.id_dfr0 = 0x02010555;
cpu->id_afr0 = 0x00000000;
cpu->isar.id_mmfr0 = 0x10201105;
cpu->isar.id_mmfr1 = 0x20000000;
cpu->isar.id_mmfr2 = 0x01240000;
cpu->isar.id_mmfr3 = 0x02102211;
cpu->isar.id_isar0 = 0x02101110;
cpu->isar.id_isar1 = 0x13112111;
cpu->isar.id_isar2 = 0x21232041;
cpu->isar.id_isar3 = 0x11112131;
cpu->isar.id_isar4 = 0x10011142;
cpu->isar.dbgdidr = 0x3515f021;
cpu->clidr = 0x0a200023;
cpu->ccsidr[0] = 0x701fe00a; /* 32K L1 dcache */
cpu->ccsidr[1] = 0x201fe00a; /* 32K L1 icache */
cpu->ccsidr[2] = 0x711fe07a; /* 4096K L2 unified cache */
define_arm_cp_regs(cpu, cortexa15_cp_reginfo);
}
#ifndef TARGET_AARCH64
/*
* -cpu max: a CPU with as many features enabled as our emulation supports.
* The version of '-cpu max' for qemu-system-aarch64 is defined in cpu64.c;
* this only needs to handle 32 bits, and need not care about KVM.
*/
static void arm_max_initfn(Object *obj)
{
ARMCPU *cpu = ARM_CPU(obj);
cortex_a15_initfn(obj);
/* old-style VFP short-vector support */
cpu->isar.mvfr0 = FIELD_DP32(cpu->isar.mvfr0, MVFR0, FPSHVEC, 1);
#ifdef CONFIG_USER_ONLY
/*
* We don't set these in system emulation mode for the moment,
* since we don't correctly set (all of) the ID registers to
* advertise them.
*/
set_feature(&cpu->env, ARM_FEATURE_V8);
{
uint32_t t;
t = cpu->isar.id_isar5;
t = FIELD_DP32(t, ID_ISAR5, AES, 2);
t = FIELD_DP32(t, ID_ISAR5, SHA1, 1);
t = FIELD_DP32(t, ID_ISAR5, SHA2, 1);
t = FIELD_DP32(t, ID_ISAR5, CRC32, 1);
t = FIELD_DP32(t, ID_ISAR5, RDM, 1);
t = FIELD_DP32(t, ID_ISAR5, VCMA, 1);
cpu->isar.id_isar5 = t;
t = cpu->isar.id_isar6;
t = FIELD_DP32(t, ID_ISAR6, JSCVT, 1);
t = FIELD_DP32(t, ID_ISAR6, DP, 1);
t = FIELD_DP32(t, ID_ISAR6, FHM, 1);
t = FIELD_DP32(t, ID_ISAR6, SB, 1);
t = FIELD_DP32(t, ID_ISAR6, SPECRES, 1);
cpu->isar.id_isar6 = t;
t = cpu->isar.mvfr1;
t = FIELD_DP32(t, MVFR1, FPHP, 3); /* v8.2-FP16 */
t = FIELD_DP32(t, MVFR1, SIMDHP, 2); /* v8.2-FP16 */
cpu->isar.mvfr1 = t;
t = cpu->isar.mvfr2;
t = FIELD_DP32(t, MVFR2, SIMDMISC, 3); /* SIMD MaxNum */
t = FIELD_DP32(t, MVFR2, FPMISC, 4); /* FP MaxNum */
cpu->isar.mvfr2 = t;
t = cpu->isar.id_mmfr3;
t = FIELD_DP32(t, ID_MMFR3, PAN, 2); /* ATS1E1 */
cpu->isar.id_mmfr3 = t;
t = cpu->isar.id_mmfr4;
t = FIELD_DP32(t, ID_MMFR4, HPDS, 1); /* AA32HPD */
t = FIELD_DP32(t, ID_MMFR4, AC2, 1); /* ACTLR2, HACTLR2 */
t = FIELD_DP32(t, ID_MMFR4, CNP, 1); /* TTCNP */
t = FIELD_DP32(t, ID_MMFR4, XNX, 1); /* TTS2UXN */
cpu->isar.id_mmfr4 = t;
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
}
#endif
#endif /* !defined(CONFIG_USER_ONLY) || !defined(TARGET_AARCH64) */
static const ARMCPUInfo arm_cpus[] = {
#if !defined(CONFIG_USER_ONLY) || !defined(TARGET_AARCH64)
{ .name = "cortex-a7", .initfn = cortex_a7_initfn },
{ .name = "cortex-a8", .initfn = cortex_a8_initfn },
{ .name = "cortex-a9", .initfn = cortex_a9_initfn },
{ .name = "cortex-a15", .initfn = cortex_a15_initfn },
#ifndef TARGET_AARCH64
{ .name = "max", .initfn = arm_max_initfn },
#endif
#ifdef CONFIG_USER_ONLY
{ .name = "any", .initfn = arm_max_initfn },
#endif
#endif
};
static Property arm_cpu_properties[] = {
DEFINE_PROP_UINT32("psci-conduit", ARMCPU, psci_conduit, 0),
DEFINE_PROP_UINT64("midr", ARMCPU, midr, 0),
@ -2390,21 +2065,11 @@ static const TypeInfo arm_cpu_type_info = {
static void arm_cpu_register_types(void)
{
const size_t cpu_count = ARRAY_SIZE(arm_cpus);
type_register_static(&arm_cpu_type_info);
#ifdef CONFIG_KVM
type_register_static(&host_arm_cpu_type_info);
#endif
if (cpu_count) {
size_t i;
for (i = 0; i < cpu_count; ++i) {
arm_cpu_register(&arm_cpus[i]);
}
}
}
type_init(arm_cpu_register_types)

View File

@ -15,6 +15,9 @@
#endif /* CONFIG_TCG */
#include "internals.h"
#include "target/arm/idau.h"
#if !defined(CONFIG_USER_ONLY)
#include "hw/boards.h"
#endif
/* CPU models. These are not needed for the AArch64 linux-user build. */
#if !defined(CONFIG_USER_ONLY) || !defined(TARGET_AARCH64)
@ -255,6 +258,236 @@ static void arm11mpcore_initfn(Object *obj)
cpu->reset_auxcr = 1;
}
static const ARMCPRegInfo cortexa8_cp_reginfo[] = {
{ .name = "L2LOCKDOWN", .cp = 15, .crn = 9, .crm = 0, .opc1 = 1, .opc2 = 0,
.access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
{ .name = "L2AUXCR", .cp = 15, .crn = 9, .crm = 0, .opc1 = 1, .opc2 = 2,
.access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
REGINFO_SENTINEL
};
static void cortex_a8_initfn(Object *obj)
{
ARMCPU *cpu = ARM_CPU(obj);
cpu->dtb_compatible = "arm,cortex-a8";
set_feature(&cpu->env, ARM_FEATURE_V7);
set_feature(&cpu->env, ARM_FEATURE_NEON);
set_feature(&cpu->env, ARM_FEATURE_THUMB2EE);
set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS);
set_feature(&cpu->env, ARM_FEATURE_EL3);
cpu->midr = 0x410fc080;
cpu->reset_fpsid = 0x410330c0;
cpu->isar.mvfr0 = 0x11110222;
cpu->isar.mvfr1 = 0x00011111;
cpu->ctr = 0x82048004;
cpu->reset_sctlr = 0x00c50078;
cpu->isar.id_pfr0 = 0x1031;
cpu->isar.id_pfr1 = 0x11;
cpu->isar.id_dfr0 = 0x400;
cpu->id_afr0 = 0;
cpu->isar.id_mmfr0 = 0x31100003;
cpu->isar.id_mmfr1 = 0x20000000;
cpu->isar.id_mmfr2 = 0x01202000;
cpu->isar.id_mmfr3 = 0x11;
cpu->isar.id_isar0 = 0x00101111;
cpu->isar.id_isar1 = 0x12112111;
cpu->isar.id_isar2 = 0x21232031;
cpu->isar.id_isar3 = 0x11112131;
cpu->isar.id_isar4 = 0x00111142;
cpu->isar.dbgdidr = 0x15141000;
cpu->clidr = (1 << 27) | (2 << 24) | 3;
cpu->ccsidr[0] = 0xe007e01a; /* 16k L1 dcache. */
cpu->ccsidr[1] = 0x2007e01a; /* 16k L1 icache. */
cpu->ccsidr[2] = 0xf0000000; /* No L2 icache. */
cpu->reset_auxcr = 2;
define_arm_cp_regs(cpu, cortexa8_cp_reginfo);
}
static const ARMCPRegInfo cortexa9_cp_reginfo[] = {
/*
* 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,
.access = PL1_RW, .resetvalue = 0,
.fieldoffset = offsetof(CPUARMState, cp15.c15_power_control) },
{ .name = "A9_DIAG", .cp = 15, .crn = 15, .crm = 0, .opc1 = 0, .opc2 = 1,
.access = PL1_RW, .resetvalue = 0,
.fieldoffset = offsetof(CPUARMState, cp15.c15_diagnostic) },
{ .name = "A9_PWRDIAG", .cp = 15, .crn = 15, .crm = 0, .opc1 = 0, .opc2 = 2,
.access = PL1_RW, .resetvalue = 0,
.fieldoffset = offsetof(CPUARMState, cp15.c15_power_diagnostic) },
{ .name = "NEONBUSY", .cp = 15, .crn = 15, .crm = 1, .opc1 = 0, .opc2 = 0,
.access = PL1_RW, .resetvalue = 0, .type = ARM_CP_CONST },
/* TLB lockdown control */
{ .name = "TLB_LOCKR", .cp = 15, .crn = 15, .crm = 4, .opc1 = 5, .opc2 = 2,
.access = PL1_W, .resetvalue = 0, .type = ARM_CP_NOP },
{ .name = "TLB_LOCKW", .cp = 15, .crn = 15, .crm = 4, .opc1 = 5, .opc2 = 4,
.access = PL1_W, .resetvalue = 0, .type = ARM_CP_NOP },
{ .name = "TLB_VA", .cp = 15, .crn = 15, .crm = 5, .opc1 = 5, .opc2 = 2,
.access = PL1_RW, .resetvalue = 0, .type = ARM_CP_CONST },
{ .name = "TLB_PA", .cp = 15, .crn = 15, .crm = 6, .opc1 = 5, .opc2 = 2,
.access = PL1_RW, .resetvalue = 0, .type = ARM_CP_CONST },
{ .name = "TLB_ATTR", .cp = 15, .crn = 15, .crm = 7, .opc1 = 5, .opc2 = 2,
.access = PL1_RW, .resetvalue = 0, .type = ARM_CP_CONST },
REGINFO_SENTINEL
};
static void cortex_a9_initfn(Object *obj)
{
ARMCPU *cpu = ARM_CPU(obj);
cpu->dtb_compatible = "arm,cortex-a9";
set_feature(&cpu->env, ARM_FEATURE_V7);
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
* A9UP and single-core A9MP (which are both different
* and valid configurations; we don't model A9UP).
*/
set_feature(&cpu->env, ARM_FEATURE_V7MP);
set_feature(&cpu->env, ARM_FEATURE_CBAR);
cpu->midr = 0x410fc090;
cpu->reset_fpsid = 0x41033090;
cpu->isar.mvfr0 = 0x11110222;
cpu->isar.mvfr1 = 0x01111111;
cpu->ctr = 0x80038003;
cpu->reset_sctlr = 0x00c50078;
cpu->isar.id_pfr0 = 0x1031;
cpu->isar.id_pfr1 = 0x11;
cpu->isar.id_dfr0 = 0x000;
cpu->id_afr0 = 0;
cpu->isar.id_mmfr0 = 0x00100103;
cpu->isar.id_mmfr1 = 0x20000000;
cpu->isar.id_mmfr2 = 0x01230000;
cpu->isar.id_mmfr3 = 0x00002111;
cpu->isar.id_isar0 = 0x00101111;
cpu->isar.id_isar1 = 0x13112111;
cpu->isar.id_isar2 = 0x21232041;
cpu->isar.id_isar3 = 0x11112131;
cpu->isar.id_isar4 = 0x00111142;
cpu->isar.dbgdidr = 0x35141000;
cpu->clidr = (1 << 27) | (1 << 24) | 3;
cpu->ccsidr[0] = 0xe00fe019; /* 16k L1 dcache. */
cpu->ccsidr[1] = 0x200fe019; /* 16k L1 icache. */
define_arm_cp_regs(cpu, cortexa9_cp_reginfo);
}
#ifndef CONFIG_USER_ONLY
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.
* Might as well set the interrupt-controller bit too.
*/
return ((ms->smp.cpus - 1) << 24) | (1 << 23);
}
#endif
static const ARMCPRegInfo cortexa15_cp_reginfo[] = {
#ifndef CONFIG_USER_ONLY
{ .name = "L2CTLR", .cp = 15, .crn = 9, .crm = 0, .opc1 = 1, .opc2 = 2,
.access = PL1_RW, .resetvalue = 0, .readfn = a15_l2ctlr_read,
.writefn = arm_cp_write_ignore, },
#endif
{ .name = "L2ECTLR", .cp = 15, .crn = 9, .crm = 0, .opc1 = 1, .opc2 = 3,
.access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
REGINFO_SENTINEL
};
static void cortex_a7_initfn(Object *obj)
{
ARMCPU *cpu = ARM_CPU(obj);
cpu->dtb_compatible = "arm,cortex-a7";
set_feature(&cpu->env, ARM_FEATURE_V7VE);
set_feature(&cpu->env, ARM_FEATURE_NEON);
set_feature(&cpu->env, ARM_FEATURE_THUMB2EE);
set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER);
set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS);
set_feature(&cpu->env, ARM_FEATURE_CBAR_RO);
set_feature(&cpu->env, ARM_FEATURE_EL2);
set_feature(&cpu->env, ARM_FEATURE_EL3);
set_feature(&cpu->env, ARM_FEATURE_PMU);
cpu->kvm_target = QEMU_KVM_ARM_TARGET_CORTEX_A7;
cpu->midr = 0x410fc075;
cpu->reset_fpsid = 0x41023075;
cpu->isar.mvfr0 = 0x10110222;
cpu->isar.mvfr1 = 0x11111111;
cpu->ctr = 0x84448003;
cpu->reset_sctlr = 0x00c50078;
cpu->isar.id_pfr0 = 0x00001131;
cpu->isar.id_pfr1 = 0x00011011;
cpu->isar.id_dfr0 = 0x02010555;
cpu->id_afr0 = 0x00000000;
cpu->isar.id_mmfr0 = 0x10101105;
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
* table 4-41 gives 0x02101110, which includes the arm div insns.
*/
cpu->isar.id_isar0 = 0x02101110;
cpu->isar.id_isar1 = 0x13112111;
cpu->isar.id_isar2 = 0x21232041;
cpu->isar.id_isar3 = 0x11112131;
cpu->isar.id_isar4 = 0x10011142;
cpu->isar.dbgdidr = 0x3515f005;
cpu->clidr = 0x0a200023;
cpu->ccsidr[0] = 0x701fe00a; /* 32K L1 dcache */
cpu->ccsidr[1] = 0x201fe00a; /* 32K L1 icache */
cpu->ccsidr[2] = 0x711fe07a; /* 4096K L2 unified cache */
define_arm_cp_regs(cpu, cortexa15_cp_reginfo); /* Same as A15 */
}
static void cortex_a15_initfn(Object *obj)
{
ARMCPU *cpu = ARM_CPU(obj);
cpu->dtb_compatible = "arm,cortex-a15";
set_feature(&cpu->env, ARM_FEATURE_V7VE);
set_feature(&cpu->env, ARM_FEATURE_NEON);
set_feature(&cpu->env, ARM_FEATURE_THUMB2EE);
set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER);
set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS);
set_feature(&cpu->env, ARM_FEATURE_CBAR_RO);
set_feature(&cpu->env, ARM_FEATURE_EL2);
set_feature(&cpu->env, ARM_FEATURE_EL3);
set_feature(&cpu->env, ARM_FEATURE_PMU);
cpu->kvm_target = QEMU_KVM_ARM_TARGET_CORTEX_A15;
cpu->midr = 0x412fc0f1;
cpu->reset_fpsid = 0x410430f0;
cpu->isar.mvfr0 = 0x10110222;
cpu->isar.mvfr1 = 0x11111111;
cpu->ctr = 0x8444c004;
cpu->reset_sctlr = 0x00c50078;
cpu->isar.id_pfr0 = 0x00001131;
cpu->isar.id_pfr1 = 0x00011011;
cpu->isar.id_dfr0 = 0x02010555;
cpu->id_afr0 = 0x00000000;
cpu->isar.id_mmfr0 = 0x10201105;
cpu->isar.id_mmfr1 = 0x20000000;
cpu->isar.id_mmfr2 = 0x01240000;
cpu->isar.id_mmfr3 = 0x02102211;
cpu->isar.id_isar0 = 0x02101110;
cpu->isar.id_isar1 = 0x13112111;
cpu->isar.id_isar2 = 0x21232041;
cpu->isar.id_isar3 = 0x11112131;
cpu->isar.id_isar4 = 0x10011142;
cpu->isar.dbgdidr = 0x3515f021;
cpu->clidr = 0x0a200023;
cpu->ccsidr[0] = 0x701fe00a; /* 32K L1 dcache */
cpu->ccsidr[1] = 0x201fe00a; /* 32K L1 icache */
cpu->ccsidr[2] = 0x711fe07a; /* 4096K L2 unified cache */
define_arm_cp_regs(cpu, cortexa15_cp_reginfo);
}
static void cortex_m0_initfn(Object *obj)
{
ARMCPU *cpu = ARM_CPU(obj);
@ -695,6 +928,81 @@ static void arm_v7m_class_init(ObjectClass *oc, void *data)
cc->gdb_core_xml_file = "arm-m-profile.xml";
}
#ifndef TARGET_AARCH64
/*
* -cpu max: a CPU with as many features enabled as our emulation supports.
* The version of '-cpu max' for qemu-system-aarch64 is defined in cpu64.c;
* this only needs to handle 32 bits, and need not care about KVM.
*/
static void arm_max_initfn(Object *obj)
{
ARMCPU *cpu = ARM_CPU(obj);
cortex_a15_initfn(obj);
/* old-style VFP short-vector support */
cpu->isar.mvfr0 = FIELD_DP32(cpu->isar.mvfr0, MVFR0, FPSHVEC, 1);
#ifdef CONFIG_USER_ONLY
/*
* We don't set these in system emulation mode for the moment,
* since we don't correctly set (all of) the ID registers to
* advertise them.
*/
set_feature(&cpu->env, ARM_FEATURE_V8);
{
uint32_t t;
t = cpu->isar.id_isar5;
t = FIELD_DP32(t, ID_ISAR5, AES, 2);
t = FIELD_DP32(t, ID_ISAR5, SHA1, 1);
t = FIELD_DP32(t, ID_ISAR5, SHA2, 1);
t = FIELD_DP32(t, ID_ISAR5, CRC32, 1);
t = FIELD_DP32(t, ID_ISAR5, RDM, 1);
t = FIELD_DP32(t, ID_ISAR5, VCMA, 1);
cpu->isar.id_isar5 = t;
t = cpu->isar.id_isar6;
t = FIELD_DP32(t, ID_ISAR6, JSCVT, 1);
t = FIELD_DP32(t, ID_ISAR6, DP, 1);
t = FIELD_DP32(t, ID_ISAR6, FHM, 1);
t = FIELD_DP32(t, ID_ISAR6, SB, 1);
t = FIELD_DP32(t, ID_ISAR6, SPECRES, 1);
cpu->isar.id_isar6 = t;
t = cpu->isar.mvfr1;
t = FIELD_DP32(t, MVFR1, FPHP, 3); /* v8.2-FP16 */
t = FIELD_DP32(t, MVFR1, SIMDHP, 2); /* v8.2-FP16 */
cpu->isar.mvfr1 = t;
t = cpu->isar.mvfr2;
t = FIELD_DP32(t, MVFR2, SIMDMISC, 3); /* SIMD MaxNum */
t = FIELD_DP32(t, MVFR2, FPMISC, 4); /* FP MaxNum */
cpu->isar.mvfr2 = t;
t = cpu->isar.id_mmfr3;
t = FIELD_DP32(t, ID_MMFR3, PAN, 2); /* ATS1E1 */
cpu->isar.id_mmfr3 = t;
t = cpu->isar.id_mmfr4;
t = FIELD_DP32(t, ID_MMFR4, HPDS, 1); /* AA32HPD */
t = FIELD_DP32(t, ID_MMFR4, AC2, 1); /* ACTLR2, HACTLR2 */
t = FIELD_DP32(t, ID_MMFR4, CNP, 1); /* TTCNP */
t = FIELD_DP32(t, ID_MMFR4, XNX, 1); /* TTS2UXN */
cpu->isar.id_mmfr4 = t;
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 /* CONFIG_USER_ONLY */
}
#endif /* !TARGET_AARCH64 */
static const ARMCPUInfo arm_tcg_cpus[] = {
{ .name = "arm926", .initfn = arm926_initfn },
{ .name = "arm946", .initfn = arm946_initfn },
@ -708,6 +1016,10 @@ static const ARMCPUInfo arm_tcg_cpus[] = {
{ .name = "arm1136", .initfn = arm1136_initfn },
{ .name = "arm1176", .initfn = arm1176_initfn },
{ .name = "arm11mpcore", .initfn = arm11mpcore_initfn },
{ .name = "cortex-a7", .initfn = cortex_a7_initfn },
{ .name = "cortex-a8", .initfn = cortex_a8_initfn },
{ .name = "cortex-a9", .initfn = cortex_a9_initfn },
{ .name = "cortex-a15", .initfn = cortex_a15_initfn },
{ .name = "cortex-m0", .initfn = cortex_m0_initfn,
.class_init = arm_v7m_class_init },
{ .name = "cortex-m3", .initfn = cortex_m3_initfn,
@ -738,6 +1050,12 @@ static const ARMCPUInfo arm_tcg_cpus[] = {
{ .name = "pxa270-b1", .initfn = pxa270b1_initfn },
{ .name = "pxa270-c0", .initfn = pxa270c0_initfn },
{ .name = "pxa270-c5", .initfn = pxa270c5_initfn },
#ifndef TARGET_AARCH64
{ .name = "max", .initfn = arm_max_initfn },
#endif
#ifdef CONFIG_USER_ONLY
{ .name = "any", .initfn = arm_max_initfn },
#endif
};
static const TypeInfo idau_interface_type_info = {

View File

@ -653,7 +653,7 @@ static void mips_cpu_initfn(Object *obj)
MIPSCPUClass *mcc = MIPS_CPU_GET_CLASS(obj);
cpu_set_cpustate_pointers(cpu);
cpu->clock = qdev_init_clock_in(DEVICE(obj), "clk-in", NULL, cpu);
cpu->clock = qdev_init_clock_in(DEVICE(obj), "clk-in", NULL, cpu, 0);
env->cpu_model = mcc->cpu_def;
}

View File

@ -157,6 +157,7 @@ qtests_npcm7xx = \
'npcm7xx_watchdog_timer-test'] + \
(slirp.found() ? ['npcm7xx_emc-test'] : [])
qtests_arm = \
(config_all_devices.has_key('CONFIG_MPS2') ? ['sse-timer-test'] : []) + \
(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'] : []) + \
(config_all_devices.has_key('CONFIG_CMSDK_APB_WATCHDOG') ? ['cmsdk-apb-watchdog-test'] : []) + \

View File

@ -0,0 +1,240 @@
/*
* QTest testcase for the SSE timer device
*
* Copyright (c) 2021 Linaro Limited
*
* 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 "libqtest-single.h"
/*
* SSE-123/SSE-300 timer in the mps3-an547 board, where it is driven
* at 32MHz, so 31.25ns per tick.
*/
#define TIMER_BASE 0x48000000
/* PERIPHNSPPC0 register in the SSE-300 Secure Access Configuration block */
#define PERIPHNSPPC0 (0x50080000 + 0x70)
/* Base of the System Counter control frame */
#define COUNTER_BASE 0x58100000
/* SSE counter register offsets in the control frame */
#define CNTCR 0
#define CNTSR 0x4
#define CNTCV_LO 0x8
#define CNTCV_HI 0xc
#define CNTSCR 0x10
/* SSE timer register offsets */
#define CNTPCT_LO 0
#define CNTPCT_HI 4
#define CNTFRQ 0x10
#define CNTP_CVAL_LO 0x20
#define CNTP_CVAL_HI 0x24
#define CNTP_TVAL 0x28
#define CNTP_CTL 0x2c
#define CNTP_AIVAL_LO 0x40
#define CNTP_AIVAL_HI 0x44
#define CNTP_AIVAL_RELOAD 0x48
#define CNTP_AIVAL_CTL 0x4c
/* 4 ticks in nanoseconds (so we can work in integers) */
#define FOUR_TICKS 125
static void clock_step_ticks(uint64_t ticks)
{
/*
* Advance the qtest clock by however many nanoseconds we
* need to move the timer forward the specified number of ticks.
* ticks must be a multiple of 4, so we get a whole number of ns.
*/
assert(!(ticks & 3));
clock_step(FOUR_TICKS * (ticks >> 2));
}
static void reset_counter_and_timer(void)
{
/*
* Reset the system counter and the timer between tests. This
* isn't a full reset, but it's sufficient for what the tests check.
*/
writel(COUNTER_BASE + CNTCR, 0);
writel(TIMER_BASE + CNTP_CTL, 0);
writel(TIMER_BASE + CNTP_AIVAL_CTL, 0);
writel(COUNTER_BASE + CNTCV_LO, 0);
writel(COUNTER_BASE + CNTCV_HI, 0);
}
static void test_counter(void)
{
/* Basic counter functionality test */
reset_counter_and_timer();
/* The counter should start disabled: check that it doesn't move */
clock_step_ticks(100);
g_assert_cmpuint(readl(COUNTER_BASE + CNTCV_LO), ==, 0);
g_assert_cmpuint(readl(COUNTER_BASE + CNTCV_HI), ==, 0);
/* Now enable it and check that it does count */
writel(COUNTER_BASE + CNTCR, 1);
clock_step_ticks(100);
g_assert_cmpuint(readl(COUNTER_BASE + CNTCV_LO), ==, 100);
g_assert_cmpuint(readl(COUNTER_BASE + CNTCV_HI), ==, 0);
/* Check the counter scaling functionality */
writel(COUNTER_BASE + CNTCR, 0);
writel(COUNTER_BASE + CNTSCR, 0x00100000); /* 1/16th normal speed */
writel(COUNTER_BASE + CNTCR, 5); /* EN, SCEN */
clock_step_ticks(160);
g_assert_cmpuint(readl(COUNTER_BASE + CNTCV_LO), ==, 110);
g_assert_cmpuint(readl(COUNTER_BASE + CNTCV_HI), ==, 0);
}
static void test_timer(void)
{
/* Basic timer functionality test */
reset_counter_and_timer();
/*
* The timer is behind a Peripheral Protection Controller, and
* qtest accesses are always non-secure (no memory attributes),
* so we must program the PPC to accept NS transactions. TIMER0
* is on port 0 of PPC0, controlled by bit 0 of this register.
*/
writel(PERIPHNSPPC0, 1);
/* We must enable the System Counter or the timer won't run. */
writel(COUNTER_BASE + CNTCR, 1);
/* Timer starts disabled and with a counter of 0 */
g_assert_cmpuint(readl(TIMER_BASE + CNTP_CTL), ==, 0);
g_assert_cmpuint(readl(TIMER_BASE + CNTPCT_LO), ==, 0);
g_assert_cmpuint(readl(TIMER_BASE + CNTPCT_HI), ==, 0);
/* Turn it on */
writel(TIMER_BASE + CNTP_CTL, 1);
/* Is the timer ticking? */
clock_step_ticks(100);
g_assert_cmpuint(readl(TIMER_BASE + CNTPCT_LO), ==, 100);
g_assert_cmpuint(readl(TIMER_BASE + CNTPCT_HI), ==, 0);
/* Set the CompareValue to 4000 ticks */
writel(TIMER_BASE + CNTP_CVAL_LO, 4000);
writel(TIMER_BASE + CNTP_CVAL_HI, 0);
/* Check TVAL view of the counter */
g_assert_cmpuint(readl(TIMER_BASE + CNTP_TVAL), ==, 3900);
/* Advance to the CompareValue mark and check ISTATUS is set */
clock_step_ticks(3900);
g_assert_cmpuint(readl(TIMER_BASE + CNTP_TVAL), ==, 0);
g_assert_cmpuint(readl(TIMER_BASE + CNTP_CTL), ==, 5);
/* Now exercise the auto-reload part of the timer */
writel(TIMER_BASE + CNTP_AIVAL_RELOAD, 200);
writel(TIMER_BASE + CNTP_AIVAL_CTL, 1);
/* Check AIVAL was reloaded and that ISTATUS is now clear */
g_assert_cmpuint(readl(TIMER_BASE + CNTP_AIVAL_LO), ==, 4200);
g_assert_cmpuint(readl(TIMER_BASE + CNTP_AIVAL_HI), ==, 0);
g_assert_cmpuint(readl(TIMER_BASE + CNTP_CTL), ==, 1);
/*
* Check that when we advance forward to the reload time the interrupt
* fires and the value reloads
*/
clock_step_ticks(100);
g_assert_cmpuint(readl(TIMER_BASE + CNTP_CTL), ==, 1);
clock_step_ticks(100);
g_assert_cmpuint(readl(TIMER_BASE + CNTP_CTL), ==, 5);
g_assert_cmpuint(readl(TIMER_BASE + CNTP_AIVAL_LO), ==, 4400);
g_assert_cmpuint(readl(TIMER_BASE + CNTP_AIVAL_HI), ==, 0);
clock_step_ticks(100);
g_assert_cmpuint(readl(TIMER_BASE + CNTP_CTL), ==, 5);
/* Check that writing 0 to CLR clears the interrupt */
writel(TIMER_BASE + CNTP_AIVAL_CTL, 1);
g_assert_cmpuint(readl(TIMER_BASE + CNTP_CTL), ==, 1);
/* Check that when we move forward to the reload time it fires again */
clock_step_ticks(100);
g_assert_cmpuint(readl(TIMER_BASE + CNTP_CTL), ==, 5);
g_assert_cmpuint(readl(TIMER_BASE + CNTP_AIVAL_LO), ==, 4600);
g_assert_cmpuint(readl(TIMER_BASE + CNTP_AIVAL_HI), ==, 0);
/*
* Step the clock far enough that we overflow the low half of the
* CNTPCT and AIVAL registers, and check that their high halves
* give the right values. We do the forward movement in
* non-autoinc mode because otherwise it takes forever as the
* timer has to emulate all the 'reload at t + N, t + 2N, etc'
* steps.
*/
writel(TIMER_BASE + CNTP_AIVAL_CTL, 0);
clock_step_ticks(0x42ULL << 32);
g_assert_cmpuint(readl(TIMER_BASE + CNTPCT_LO), ==, 4400);
g_assert_cmpuint(readl(TIMER_BASE + CNTPCT_HI), ==, 0x42);
/* Turn on the autoinc again to check AIVAL_HI */
writel(TIMER_BASE + CNTP_AIVAL_CTL, 1);
g_assert_cmpuint(readl(TIMER_BASE + CNTP_AIVAL_LO), ==, 4600);
g_assert_cmpuint(readl(TIMER_BASE + CNTP_AIVAL_HI), ==, 0x42);
}
static void test_timer_scale_change(void)
{
/*
* Test that the timer responds correctly to counter
* scaling changes while it has an active timer.
*/
reset_counter_and_timer();
/* Give ourselves access to the timer, and enable the counter and timer */
writel(PERIPHNSPPC0, 1);
writel(COUNTER_BASE + CNTCR, 1);
writel(TIMER_BASE + CNTP_CTL, 1);
/* Set the CompareValue to 4000 ticks */
writel(TIMER_BASE + CNTP_CVAL_LO, 4000);
writel(TIMER_BASE + CNTP_CVAL_HI, 0);
/* Advance halfway and check ISTATUS is not set */
clock_step_ticks(2000);
g_assert_cmpuint(readl(TIMER_BASE + CNTP_CTL), ==, 1);
/* Reprogram the counter to run at 1/16th speed */
writel(COUNTER_BASE + CNTCR, 0);
writel(COUNTER_BASE + CNTSCR, 0x00100000); /* 1/16th normal speed */
writel(COUNTER_BASE + CNTCR, 5); /* EN, SCEN */
/* Advance to where the timer would have fired and check it has not */
clock_step_ticks(2000);
g_assert_cmpuint(readl(TIMER_BASE + CNTP_CTL), ==, 1);
/* Advance to where the timer must fire at the new clock rate */
clock_step_ticks(29996);
g_assert_cmpuint(readl(TIMER_BASE + CNTP_CTL), ==, 1);
clock_step_ticks(4);
g_assert_cmpuint(readl(TIMER_BASE + CNTP_CTL), ==, 5);
}
int main(int argc, char **argv)
{
int r;
g_test_init(&argc, &argv, NULL);
qtest_start("-machine mps3-an547");
qtest_add_func("/sse-timer/counter", test_counter);
qtest_add_func("/sse-timer/timer", test_timer);
qtest_add_func("/sse-timer/timer-scale-change", test_timer_scale_change);
r = g_test_run();
qtest_end();
return r;
}