mirror of https://github.com/xemu-project/xemu.git
target-arm queue:
* Implement FEAT_ECV * STM32L4x5: Implement GPIO device * Fix 32-bit SMOPA * Refactor v7m related code from cpu32.c into its own file * hw/rtc/sun4v-rtc: Relicense to GPLv2-or-later -----BEGIN PGP SIGNATURE----- iQJNBAABCAA3FiEE4aXFk81BneKOgxXPPCUl7RQ2DN4FAmXrM50ZHHBldGVyLm1h eWRlbGxAbGluYXJvLm9yZwAKCRA8JSXtFDYM3l3aD/9BDWm3LNSIyHQ0qFD1l6wc JeAymSBecMD6sfRaPloLaB5HlU9AhLQWHe8Sa/hkWdYPhvhh6keESlVScJXi6Irq wm3MuDJwr9QZgXWuHsEwXj4sve+O/MgDHcYSyEldbcyqjbivMCUKCGXeT2VxQftd LarETxUTsdPeaWm3Lm11CkiO5r0DMJyebgVc6jloT9O1oK8szrkDix09U6eCGhXy l1ep0KY2mk+MtoboDflD3W/Zu0LrAZ1159r4LqTMD2Hp9Tt222aDOjEKi+Qjns22 E86YCy7kPcsHVOskF42SkZ8M044T/tCetKgnOHqn8hbTCW5uNT+zJNC1feAB92pi 4xWErOfYy7d5UVzWfUYudGKrb91rr5h2jd1SWn2NeQtdmU8KyFEjQS1y4FNZvPTD lrzyuTv8daeKSImq6JPzws/MJRh5I87TpRgKDg6hTJDaUCLu0yIuV9pkUsIdJ5mW 01ol8tmDgpBRsxjJlIf40KxOt5SQ2VoYh7L8jgRjGv9DEP5hU1AkPqQGtyx7Wcd/ ImRYQ/cOqircJPqX60DHljZDACVOzrFIEmpKvu45tt1On0iNXKCMuIl0vwI9XERx CSgqIz7KDI5gNlruZQDyHvVehQZW7sJo9rH5RawqObsUHTlg5rLb++79Da2RWtbV yvQLaI3qPngknz//1eAKxg== =YmPl -----END PGP SIGNATURE----- Merge tag 'pull-target-arm-20240308' of https://git.linaro.org/people/pmaydell/qemu-arm into staging target-arm queue: * Implement FEAT_ECV * STM32L4x5: Implement GPIO device * Fix 32-bit SMOPA * Refactor v7m related code from cpu32.c into its own file * hw/rtc/sun4v-rtc: Relicense to GPLv2-or-later # -----BEGIN PGP SIGNATURE----- # # iQJNBAABCAA3FiEE4aXFk81BneKOgxXPPCUl7RQ2DN4FAmXrM50ZHHBldGVyLm1h # eWRlbGxAbGluYXJvLm9yZwAKCRA8JSXtFDYM3l3aD/9BDWm3LNSIyHQ0qFD1l6wc # JeAymSBecMD6sfRaPloLaB5HlU9AhLQWHe8Sa/hkWdYPhvhh6keESlVScJXi6Irq # wm3MuDJwr9QZgXWuHsEwXj4sve+O/MgDHcYSyEldbcyqjbivMCUKCGXeT2VxQftd # LarETxUTsdPeaWm3Lm11CkiO5r0DMJyebgVc6jloT9O1oK8szrkDix09U6eCGhXy # l1ep0KY2mk+MtoboDflD3W/Zu0LrAZ1159r4LqTMD2Hp9Tt222aDOjEKi+Qjns22 # E86YCy7kPcsHVOskF42SkZ8M044T/tCetKgnOHqn8hbTCW5uNT+zJNC1feAB92pi # 4xWErOfYy7d5UVzWfUYudGKrb91rr5h2jd1SWn2NeQtdmU8KyFEjQS1y4FNZvPTD # lrzyuTv8daeKSImq6JPzws/MJRh5I87TpRgKDg6hTJDaUCLu0yIuV9pkUsIdJ5mW # 01ol8tmDgpBRsxjJlIf40KxOt5SQ2VoYh7L8jgRjGv9DEP5hU1AkPqQGtyx7Wcd/ # ImRYQ/cOqircJPqX60DHljZDACVOzrFIEmpKvu45tt1On0iNXKCMuIl0vwI9XERx # CSgqIz7KDI5gNlruZQDyHvVehQZW7sJo9rH5RawqObsUHTlg5rLb++79Da2RWtbV # yvQLaI3qPngknz//1eAKxg== # =YmPl # -----END PGP SIGNATURE----- # gpg: Signature made Fri 08 Mar 2024 15:49:49 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] # gpg: aka "Peter Maydell <peter@archaic.org.uk>" [ultimate] # Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83 15CF 3C25 25ED 1436 0CDE * tag 'pull-target-arm-20240308' of https://git.linaro.org/people/pmaydell/qemu-arm: target/arm: Move v7m-related code from cpu32.c into a separate file hw/rtc/sun4v-rtc: Relicense to GPLv2-or-later target/arm: Fix 32-bit SMOPA tests/qtest: Add STM32L4x5 GPIO QTest testcase hw/arm: Connect STM32L4x5 GPIO to STM32L4x5 SoC hw/gpio: Implement STM32L4x5 GPIO target/arm: Enable FEAT_ECV for 'max' CPU target/arm: Implement FEAT_ECV CNTPOFF_EL2 handling target/arm: Define CNTPCTSS_EL0 and CNTVCTSS_EL0 target/arm: Implement new FEAT_ECV trap bits target/arm: Don't allow RES0 CNTHCTL_EL2 bits to be written target/arm: use FIELD macro for CNTHCTL bit definitions target/arm: Timer _EL02 registers UNDEF for E2H == 0 target/arm: Move some register related defines to internals.h Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
a146c6f88c
|
@ -1133,6 +1133,7 @@ F: hw/arm/stm32l4x5_soc.c
|
|||
F: hw/misc/stm32l4x5_exti.c
|
||||
F: hw/misc/stm32l4x5_syscfg.c
|
||||
F: hw/misc/stm32l4x5_rcc.c
|
||||
F: hw/gpio/stm32l4x5_gpio.c
|
||||
F: include/hw/*/stm32l4x5_*.h
|
||||
|
||||
B-L475E-IOT01A IoT Node
|
||||
|
|
|
@ -18,6 +18,7 @@ Currently B-L475E-IOT01A machine's only supports the following devices:
|
|||
- STM32L4x5 EXTI (Extended interrupts and events controller)
|
||||
- STM32L4x5 SYSCFG (System configuration controller)
|
||||
- STM32L4x5 RCC (Reset and clock control)
|
||||
- STM32L4x5 GPIOs (General-purpose I/Os)
|
||||
|
||||
Missing devices
|
||||
"""""""""""""""
|
||||
|
@ -25,7 +26,6 @@ Missing devices
|
|||
The B-L475E-IOT01A does *not* support the following devices:
|
||||
|
||||
- Serial ports (UART)
|
||||
- General-purpose I/Os (GPIO)
|
||||
- Analog to Digital Converter (ADC)
|
||||
- SPI controller
|
||||
- Timer controller (TIMER)
|
||||
|
|
|
@ -28,6 +28,7 @@ the following architecture extensions:
|
|||
- FEAT_DotProd (Advanced SIMD dot product instructions)
|
||||
- FEAT_DoubleFault (Double Fault Extension)
|
||||
- FEAT_E0PD (Preventing EL0 access to halves of address maps)
|
||||
- FEAT_ECV (Enhanced Counter Virtualization)
|
||||
- FEAT_EPAC (Enhanced pointer authentication)
|
||||
- FEAT_ETS (Enhanced Translation Synchronization)
|
||||
- FEAT_EVT (Enhanced Virtualization Traps)
|
||||
|
|
|
@ -473,9 +473,10 @@ config STM32L4X5_SOC
|
|||
bool
|
||||
select ARM_V7M
|
||||
select OR_IRQ
|
||||
select STM32L4X5_SYSCFG
|
||||
select STM32L4X5_EXTI
|
||||
select STM32L4X5_SYSCFG
|
||||
select STM32L4X5_RCC
|
||||
select STM32L4X5_GPIO
|
||||
|
||||
config XLNX_ZYNQMP_ARM
|
||||
bool
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "sysemu/sysemu.h"
|
||||
#include "hw/or-irq.h"
|
||||
#include "hw/arm/stm32l4x5_soc.h"
|
||||
#include "hw/gpio/stm32l4x5_gpio.h"
|
||||
#include "hw/qdev-clock.h"
|
||||
#include "hw/misc/unimp.h"
|
||||
|
||||
|
@ -99,6 +100,22 @@ static const int exti_or_gate1_lines_in[EXTI_OR_GATE1_NUM_LINES_IN] = {
|
|||
16, 35, 36, 37, 38,
|
||||
};
|
||||
|
||||
static const struct {
|
||||
uint32_t addr;
|
||||
uint32_t moder_reset;
|
||||
uint32_t ospeedr_reset;
|
||||
uint32_t pupdr_reset;
|
||||
} stm32l4x5_gpio_cfg[NUM_GPIOS] = {
|
||||
{ 0x48000000, 0xABFFFFFF, 0x0C000000, 0x64000000 },
|
||||
{ 0x48000400, 0xFFFFFEBF, 0x00000000, 0x00000100 },
|
||||
{ 0x48000800, 0xFFFFFFFF, 0x00000000, 0x00000000 },
|
||||
{ 0x48000C00, 0xFFFFFFFF, 0x00000000, 0x00000000 },
|
||||
{ 0x48001000, 0xFFFFFFFF, 0x00000000, 0x00000000 },
|
||||
{ 0x48001400, 0xFFFFFFFF, 0x00000000, 0x00000000 },
|
||||
{ 0x48001800, 0xFFFFFFFF, 0x00000000, 0x00000000 },
|
||||
{ 0x48001C00, 0x0000000F, 0x00000000, 0x00000000 },
|
||||
};
|
||||
|
||||
static void stm32l4x5_soc_initfn(Object *obj)
|
||||
{
|
||||
Stm32l4x5SocState *s = STM32L4X5_SOC(obj);
|
||||
|
@ -110,6 +127,11 @@ static void stm32l4x5_soc_initfn(Object *obj)
|
|||
}
|
||||
object_initialize_child(obj, "syscfg", &s->syscfg, TYPE_STM32L4X5_SYSCFG);
|
||||
object_initialize_child(obj, "rcc", &s->rcc, TYPE_STM32L4X5_RCC);
|
||||
|
||||
for (unsigned i = 0; i < NUM_GPIOS; i++) {
|
||||
g_autofree char *name = g_strdup_printf("gpio%c", 'a' + i);
|
||||
object_initialize_child(obj, name, &s->gpio[i], TYPE_STM32L4X5_GPIO);
|
||||
}
|
||||
}
|
||||
|
||||
static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp)
|
||||
|
@ -118,8 +140,9 @@ static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp)
|
|||
Stm32l4x5SocState *s = STM32L4X5_SOC(dev_soc);
|
||||
const Stm32l4x5SocClass *sc = STM32L4X5_SOC_GET_CLASS(dev_soc);
|
||||
MemoryRegion *system_memory = get_system_memory();
|
||||
DeviceState *armv7m;
|
||||
DeviceState *armv7m, *dev;
|
||||
SysBusDevice *busdev;
|
||||
uint32_t pin_index;
|
||||
|
||||
if (!memory_region_init_rom(&s->flash, OBJECT(dev_soc), "flash",
|
||||
sc->flash_size, errp)) {
|
||||
|
@ -160,17 +183,43 @@ static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp)
|
|||
return;
|
||||
}
|
||||
|
||||
/* GPIOs */
|
||||
for (unsigned i = 0; i < NUM_GPIOS; i++) {
|
||||
g_autofree char *name = g_strdup_printf("%c", 'A' + i);
|
||||
dev = DEVICE(&s->gpio[i]);
|
||||
qdev_prop_set_string(dev, "name", name);
|
||||
qdev_prop_set_uint32(dev, "mode-reset",
|
||||
stm32l4x5_gpio_cfg[i].moder_reset);
|
||||
qdev_prop_set_uint32(dev, "ospeed-reset",
|
||||
stm32l4x5_gpio_cfg[i].ospeedr_reset);
|
||||
qdev_prop_set_uint32(dev, "pupd-reset",
|
||||
stm32l4x5_gpio_cfg[i].pupdr_reset);
|
||||
busdev = SYS_BUS_DEVICE(&s->gpio[i]);
|
||||
g_free(name);
|
||||
name = g_strdup_printf("gpio%c-out", 'a' + i);
|
||||
qdev_connect_clock_in(DEVICE(&s->gpio[i]), "clk",
|
||||
qdev_get_clock_out(DEVICE(&(s->rcc)), name));
|
||||
if (!sysbus_realize(busdev, errp)) {
|
||||
return;
|
||||
}
|
||||
sysbus_mmio_map(busdev, 0, stm32l4x5_gpio_cfg[i].addr);
|
||||
}
|
||||
|
||||
/* System configuration controller */
|
||||
busdev = SYS_BUS_DEVICE(&s->syscfg);
|
||||
if (!sysbus_realize(busdev, errp)) {
|
||||
return;
|
||||
}
|
||||
sysbus_mmio_map(busdev, 0, SYSCFG_ADDR);
|
||||
/*
|
||||
* TODO: when the GPIO device is implemented, connect it
|
||||
* to SYCFG using `qdev_connect_gpio_out`, NUM_GPIOS and
|
||||
* GPIO_NUM_PINS.
|
||||
*/
|
||||
|
||||
for (unsigned i = 0; i < NUM_GPIOS; i++) {
|
||||
for (unsigned j = 0; j < GPIO_NUM_PINS; j++) {
|
||||
pin_index = GPIO_NUM_PINS * i + j;
|
||||
qdev_connect_gpio_out(DEVICE(&s->gpio[i]), j,
|
||||
qdev_get_gpio_in(DEVICE(&s->syscfg),
|
||||
pin_index));
|
||||
}
|
||||
}
|
||||
|
||||
/* EXTI device */
|
||||
busdev = SYS_BUS_DEVICE(&s->exti);
|
||||
|
@ -217,7 +266,7 @@ static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp)
|
|||
}
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < 16; i++) {
|
||||
for (unsigned i = 0; i < GPIO_NUM_PINS; i++) {
|
||||
qdev_connect_gpio_out(DEVICE(&s->syscfg), i,
|
||||
qdev_get_gpio_in(DEVICE(&s->exti), i));
|
||||
}
|
||||
|
@ -302,14 +351,6 @@ static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp)
|
|||
/* RESERVED: 0x40024400, 0x7FDBC00 */
|
||||
|
||||
/* AHB2 BUS */
|
||||
create_unimplemented_device("GPIOA", 0x48000000, 0x400);
|
||||
create_unimplemented_device("GPIOB", 0x48000400, 0x400);
|
||||
create_unimplemented_device("GPIOC", 0x48000800, 0x400);
|
||||
create_unimplemented_device("GPIOD", 0x48000C00, 0x400);
|
||||
create_unimplemented_device("GPIOE", 0x48001000, 0x400);
|
||||
create_unimplemented_device("GPIOF", 0x48001400, 0x400);
|
||||
create_unimplemented_device("GPIOG", 0x48001800, 0x400);
|
||||
create_unimplemented_device("GPIOH", 0x48001C00, 0x400);
|
||||
/* RESERVED: 0x48002000, 0x7FDBC00 */
|
||||
create_unimplemented_device("OTG_FS", 0x50000000, 0x40000);
|
||||
create_unimplemented_device("ADC", 0x50040000, 0x400);
|
||||
|
|
|
@ -16,3 +16,6 @@ config GPIO_PWR
|
|||
|
||||
config SIFIVE_GPIO
|
||||
bool
|
||||
|
||||
config STM32L4X5_GPIO
|
||||
bool
|
||||
|
|
|
@ -13,5 +13,6 @@ system_ss.add(when: 'CONFIG_RASPI', if_true: files(
|
|||
'bcm2835_gpio.c',
|
||||
'bcm2838_gpio.c'
|
||||
))
|
||||
system_ss.add(when: 'CONFIG_STM32L4X5_SOC', if_true: files('stm32l4x5_gpio.c'))
|
||||
system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_gpio.c'))
|
||||
system_ss.add(when: 'CONFIG_SIFIVE_GPIO', if_true: files('sifive_gpio.c'))
|
||||
|
|
|
@ -0,0 +1,477 @@
|
|||
/*
|
||||
* STM32L4x5 GPIO (General Purpose Input/Ouput)
|
||||
*
|
||||
* Copyright (c) 2024 Arnaud Minier <arnaud.minier@telecom-paris.fr>
|
||||
* Copyright (c) 2024 Inès Varhol <ines.varhol@telecom-paris.fr>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The reference used is the STMicroElectronics RM0351 Reference manual
|
||||
* for STM32L4x5 and STM32L4x6 advanced Arm ® -based 32-bit MCUs.
|
||||
* https://www.st.com/en/microcontrollers-microprocessors/stm32l4x5/documentation.html
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/log.h"
|
||||
#include "hw/gpio/stm32l4x5_gpio.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/qdev-clock.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "qapi/visitor.h"
|
||||
#include "qapi/error.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "trace.h"
|
||||
|
||||
#define GPIO_MODER 0x00
|
||||
#define GPIO_OTYPER 0x04
|
||||
#define GPIO_OSPEEDR 0x08
|
||||
#define GPIO_PUPDR 0x0C
|
||||
#define GPIO_IDR 0x10
|
||||
#define GPIO_ODR 0x14
|
||||
#define GPIO_BSRR 0x18
|
||||
#define GPIO_LCKR 0x1C
|
||||
#define GPIO_AFRL 0x20
|
||||
#define GPIO_AFRH 0x24
|
||||
#define GPIO_BRR 0x28
|
||||
#define GPIO_ASCR 0x2C
|
||||
|
||||
/* 0b11111111_11111111_00000000_00000000 */
|
||||
#define RESERVED_BITS_MASK 0xFFFF0000
|
||||
|
||||
static void update_gpio_idr(Stm32l4x5GpioState *s);
|
||||
|
||||
static bool is_pull_up(Stm32l4x5GpioState *s, unsigned pin)
|
||||
{
|
||||
return extract32(s->pupdr, 2 * pin, 2) == 1;
|
||||
}
|
||||
|
||||
static bool is_pull_down(Stm32l4x5GpioState *s, unsigned pin)
|
||||
{
|
||||
return extract32(s->pupdr, 2 * pin, 2) == 2;
|
||||
}
|
||||
|
||||
static bool is_output(Stm32l4x5GpioState *s, unsigned pin)
|
||||
{
|
||||
return extract32(s->moder, 2 * pin, 2) == 1;
|
||||
}
|
||||
|
||||
static bool is_open_drain(Stm32l4x5GpioState *s, unsigned pin)
|
||||
{
|
||||
return extract32(s->otyper, pin, 1) == 1;
|
||||
}
|
||||
|
||||
static bool is_push_pull(Stm32l4x5GpioState *s, unsigned pin)
|
||||
{
|
||||
return extract32(s->otyper, pin, 1) == 0;
|
||||
}
|
||||
|
||||
static void stm32l4x5_gpio_reset_hold(Object *obj)
|
||||
{
|
||||
Stm32l4x5GpioState *s = STM32L4X5_GPIO(obj);
|
||||
|
||||
s->moder = s->moder_reset;
|
||||
s->otyper = 0x00000000;
|
||||
s->ospeedr = s->ospeedr_reset;
|
||||
s->pupdr = s->pupdr_reset;
|
||||
s->idr = 0x00000000;
|
||||
s->odr = 0x00000000;
|
||||
s->lckr = 0x00000000;
|
||||
s->afrl = 0x00000000;
|
||||
s->afrh = 0x00000000;
|
||||
s->ascr = 0x00000000;
|
||||
|
||||
s->disconnected_pins = 0xFFFF;
|
||||
s->pins_connected_high = 0x0000;
|
||||
update_gpio_idr(s);
|
||||
}
|
||||
|
||||
static void stm32l4x5_gpio_set(void *opaque, int line, int level)
|
||||
{
|
||||
Stm32l4x5GpioState *s = opaque;
|
||||
/*
|
||||
* The pin isn't set if line is configured in output mode
|
||||
* except if level is 0 and the output is open-drain.
|
||||
* This way there will be no short-circuit prone situations.
|
||||
*/
|
||||
if (is_output(s, line) && !(is_open_drain(s, line) && (level == 0))) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "Line %d can't be driven externally\n",
|
||||
line);
|
||||
return;
|
||||
}
|
||||
|
||||
s->disconnected_pins &= ~(1 << line);
|
||||
if (level) {
|
||||
s->pins_connected_high |= (1 << line);
|
||||
} else {
|
||||
s->pins_connected_high &= ~(1 << line);
|
||||
}
|
||||
trace_stm32l4x5_gpio_pins(s->name, s->disconnected_pins,
|
||||
s->pins_connected_high);
|
||||
update_gpio_idr(s);
|
||||
}
|
||||
|
||||
|
||||
static void update_gpio_idr(Stm32l4x5GpioState *s)
|
||||
{
|
||||
uint32_t new_idr_mask = 0;
|
||||
uint32_t new_idr = s->odr;
|
||||
uint32_t old_idr = s->idr;
|
||||
int new_pin_state, old_pin_state;
|
||||
|
||||
for (int i = 0; i < GPIO_NUM_PINS; i++) {
|
||||
if (is_output(s, i)) {
|
||||
if (is_push_pull(s, i)) {
|
||||
new_idr_mask |= (1 << i);
|
||||
} else if (!(s->odr & (1 << i))) {
|
||||
/* open-drain ODR 0 */
|
||||
new_idr_mask |= (1 << i);
|
||||
/* open-drain ODR 1 */
|
||||
} else if (!(s->disconnected_pins & (1 << i)) &&
|
||||
!(s->pins_connected_high & (1 << i))) {
|
||||
/* open-drain ODR 1 with pin connected low */
|
||||
new_idr_mask |= (1 << i);
|
||||
new_idr &= ~(1 << i);
|
||||
/* open-drain ODR 1 with unactive pin */
|
||||
} else if (is_pull_up(s, i)) {
|
||||
new_idr_mask |= (1 << i);
|
||||
} else if (is_pull_down(s, i)) {
|
||||
new_idr_mask |= (1 << i);
|
||||
new_idr &= ~(1 << i);
|
||||
}
|
||||
/*
|
||||
* The only case left is for open-drain ODR 1
|
||||
* with unactive pin without pull-up or pull-down :
|
||||
* the value is floating.
|
||||
*/
|
||||
/* input or analog mode with connected pin */
|
||||
} else if (!(s->disconnected_pins & (1 << i))) {
|
||||
if (s->pins_connected_high & (1 << i)) {
|
||||
/* pin high */
|
||||
new_idr_mask |= (1 << i);
|
||||
new_idr |= (1 << i);
|
||||
} else {
|
||||
/* pin low */
|
||||
new_idr_mask |= (1 << i);
|
||||
new_idr &= ~(1 << i);
|
||||
}
|
||||
/* input or analog mode with disconnected pin */
|
||||
} else {
|
||||
if (is_pull_up(s, i)) {
|
||||
/* pull-up */
|
||||
new_idr_mask |= (1 << i);
|
||||
new_idr |= (1 << i);
|
||||
} else if (is_pull_down(s, i)) {
|
||||
/* pull-down */
|
||||
new_idr_mask |= (1 << i);
|
||||
new_idr &= ~(1 << i);
|
||||
}
|
||||
/*
|
||||
* The only case left is for a disconnected pin
|
||||
* without pull-up or pull-down :
|
||||
* the value is floating.
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
s->idr = (old_idr & ~new_idr_mask) | (new_idr & new_idr_mask);
|
||||
trace_stm32l4x5_gpio_update_idr(s->name, old_idr, s->idr);
|
||||
|
||||
for (int i = 0; i < GPIO_NUM_PINS; i++) {
|
||||
if (new_idr_mask & (1 << i)) {
|
||||
new_pin_state = (new_idr & (1 << i)) > 0;
|
||||
old_pin_state = (old_idr & (1 << i)) > 0;
|
||||
if (new_pin_state > old_pin_state) {
|
||||
qemu_irq_raise(s->pin[i]);
|
||||
} else if (new_pin_state < old_pin_state) {
|
||||
qemu_irq_lower(s->pin[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Return mask of pins that are both configured in output
|
||||
* mode and externally driven (except pins in open-drain
|
||||
* mode externally set to 0).
|
||||
*/
|
||||
static uint32_t get_gpio_pinmask_to_disconnect(Stm32l4x5GpioState *s)
|
||||
{
|
||||
uint32_t pins_to_disconnect = 0;
|
||||
for (int i = 0; i < GPIO_NUM_PINS; i++) {
|
||||
/* for each connected pin in output mode */
|
||||
if (!(s->disconnected_pins & (1 << i)) && is_output(s, i)) {
|
||||
/* if either push-pull or high level */
|
||||
if (is_push_pull(s, i) || s->pins_connected_high & (1 << i)) {
|
||||
pins_to_disconnect |= (1 << i);
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"Line %d can't be driven externally\n",
|
||||
i);
|
||||
}
|
||||
}
|
||||
}
|
||||
return pins_to_disconnect;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set field `disconnected_pins` and call `update_gpio_idr()`
|
||||
*/
|
||||
static void disconnect_gpio_pins(Stm32l4x5GpioState *s, uint16_t lines)
|
||||
{
|
||||
s->disconnected_pins |= lines;
|
||||
trace_stm32l4x5_gpio_pins(s->name, s->disconnected_pins,
|
||||
s->pins_connected_high);
|
||||
update_gpio_idr(s);
|
||||
}
|
||||
|
||||
static void disconnected_pins_set(Object *obj, Visitor *v,
|
||||
const char *name, void *opaque, Error **errp)
|
||||
{
|
||||
Stm32l4x5GpioState *s = STM32L4X5_GPIO(obj);
|
||||
uint16_t value;
|
||||
if (!visit_type_uint16(v, name, &value, errp)) {
|
||||
return;
|
||||
}
|
||||
disconnect_gpio_pins(s, value);
|
||||
}
|
||||
|
||||
static void disconnected_pins_get(Object *obj, Visitor *v,
|
||||
const char *name, void *opaque, Error **errp)
|
||||
{
|
||||
visit_type_uint16(v, name, (uint16_t *)opaque, errp);
|
||||
}
|
||||
|
||||
static void clock_freq_get(Object *obj, Visitor *v,
|
||||
const char *name, void *opaque, Error **errp)
|
||||
{
|
||||
Stm32l4x5GpioState *s = STM32L4X5_GPIO(obj);
|
||||
uint32_t clock_freq_hz = clock_get_hz(s->clk);
|
||||
visit_type_uint32(v, name, &clock_freq_hz, errp);
|
||||
}
|
||||
|
||||
static void stm32l4x5_gpio_write(void *opaque, hwaddr addr,
|
||||
uint64_t val64, unsigned int size)
|
||||
{
|
||||
Stm32l4x5GpioState *s = opaque;
|
||||
|
||||
uint32_t value = val64;
|
||||
trace_stm32l4x5_gpio_write(s->name, addr, val64);
|
||||
|
||||
switch (addr) {
|
||||
case GPIO_MODER:
|
||||
s->moder = value;
|
||||
disconnect_gpio_pins(s, get_gpio_pinmask_to_disconnect(s));
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"%s: Analog and AF modes aren't supported\n\
|
||||
Analog and AF mode behave like input mode\n",
|
||||
__func__);
|
||||
return;
|
||||
case GPIO_OTYPER:
|
||||
s->otyper = value & ~RESERVED_BITS_MASK;
|
||||
disconnect_gpio_pins(s, get_gpio_pinmask_to_disconnect(s));
|
||||
return;
|
||||
case GPIO_OSPEEDR:
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"%s: Changing I/O output speed isn't supported\n\
|
||||
I/O speed is already maximal\n",
|
||||
__func__);
|
||||
s->ospeedr = value;
|
||||
return;
|
||||
case GPIO_PUPDR:
|
||||
s->pupdr = value;
|
||||
update_gpio_idr(s);
|
||||
return;
|
||||
case GPIO_IDR:
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"%s: GPIO->IDR is read-only\n",
|
||||
__func__);
|
||||
return;
|
||||
case GPIO_ODR:
|
||||
s->odr = value & ~RESERVED_BITS_MASK;
|
||||
update_gpio_idr(s);
|
||||
return;
|
||||
case GPIO_BSRR: {
|
||||
uint32_t bits_to_reset = (value & RESERVED_BITS_MASK) >> GPIO_NUM_PINS;
|
||||
uint32_t bits_to_set = value & ~RESERVED_BITS_MASK;
|
||||
/* If both BSx and BRx are set, BSx has priority.*/
|
||||
s->odr &= ~bits_to_reset;
|
||||
s->odr |= bits_to_set;
|
||||
update_gpio_idr(s);
|
||||
return;
|
||||
}
|
||||
case GPIO_LCKR:
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"%s: Locking port bits configuration isn't supported\n",
|
||||
__func__);
|
||||
s->lckr = value & ~RESERVED_BITS_MASK;
|
||||
return;
|
||||
case GPIO_AFRL:
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"%s: Alternate functions aren't supported\n",
|
||||
__func__);
|
||||
s->afrl = value;
|
||||
return;
|
||||
case GPIO_AFRH:
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"%s: Alternate functions aren't supported\n",
|
||||
__func__);
|
||||
s->afrh = value;
|
||||
return;
|
||||
case GPIO_BRR: {
|
||||
uint32_t bits_to_reset = value & ~RESERVED_BITS_MASK;
|
||||
s->odr &= ~bits_to_reset;
|
||||
update_gpio_idr(s);
|
||||
return;
|
||||
}
|
||||
case GPIO_ASCR:
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"%s: ADC function isn't supported\n",
|
||||
__func__);
|
||||
s->ascr = value & ~RESERVED_BITS_MASK;
|
||||
return;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, addr);
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t stm32l4x5_gpio_read(void *opaque, hwaddr addr,
|
||||
unsigned int size)
|
||||
{
|
||||
Stm32l4x5GpioState *s = opaque;
|
||||
|
||||
trace_stm32l4x5_gpio_read(s->name, addr);
|
||||
|
||||
switch (addr) {
|
||||
case GPIO_MODER:
|
||||
return s->moder;
|
||||
case GPIO_OTYPER:
|
||||
return s->otyper;
|
||||
case GPIO_OSPEEDR:
|
||||
return s->ospeedr;
|
||||
case GPIO_PUPDR:
|
||||
return s->pupdr;
|
||||
case GPIO_IDR:
|
||||
return s->idr;
|
||||
case GPIO_ODR:
|
||||
return s->odr;
|
||||
case GPIO_BSRR:
|
||||
return 0;
|
||||
case GPIO_LCKR:
|
||||
return s->lckr;
|
||||
case GPIO_AFRL:
|
||||
return s->afrl;
|
||||
case GPIO_AFRH:
|
||||
return s->afrh;
|
||||
case GPIO_BRR:
|
||||
return 0;
|
||||
case GPIO_ASCR:
|
||||
return s->ascr;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, addr);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps stm32l4x5_gpio_ops = {
|
||||
.read = stm32l4x5_gpio_read,
|
||||
.write = stm32l4x5_gpio_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
.impl = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 4,
|
||||
.unaligned = false,
|
||||
},
|
||||
.valid = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 4,
|
||||
.unaligned = false,
|
||||
},
|
||||
};
|
||||
|
||||
static void stm32l4x5_gpio_init(Object *obj)
|
||||
{
|
||||
Stm32l4x5GpioState *s = STM32L4X5_GPIO(obj);
|
||||
|
||||
memory_region_init_io(&s->mmio, obj, &stm32l4x5_gpio_ops, s,
|
||||
TYPE_STM32L4X5_GPIO, 0x400);
|
||||
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
|
||||
|
||||
qdev_init_gpio_out(DEVICE(obj), s->pin, GPIO_NUM_PINS);
|
||||
qdev_init_gpio_in(DEVICE(obj), stm32l4x5_gpio_set, GPIO_NUM_PINS);
|
||||
|
||||
s->clk = qdev_init_clock_in(DEVICE(s), "clk", NULL, s, 0);
|
||||
|
||||
object_property_add(obj, "disconnected-pins", "uint16",
|
||||
disconnected_pins_get, disconnected_pins_set,
|
||||
NULL, &s->disconnected_pins);
|
||||
object_property_add(obj, "clock-freq-hz", "uint32",
|
||||
clock_freq_get, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
static void stm32l4x5_gpio_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
Stm32l4x5GpioState *s = STM32L4X5_GPIO(dev);
|
||||
if (!clock_has_source(s->clk)) {
|
||||
error_setg(errp, "GPIO: clk input must be connected");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_stm32l4x5_gpio = {
|
||||
.name = TYPE_STM32L4X5_GPIO,
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]){
|
||||
VMSTATE_UINT32(moder, Stm32l4x5GpioState),
|
||||
VMSTATE_UINT32(otyper, Stm32l4x5GpioState),
|
||||
VMSTATE_UINT32(ospeedr, Stm32l4x5GpioState),
|
||||
VMSTATE_UINT32(pupdr, Stm32l4x5GpioState),
|
||||
VMSTATE_UINT32(idr, Stm32l4x5GpioState),
|
||||
VMSTATE_UINT32(odr, Stm32l4x5GpioState),
|
||||
VMSTATE_UINT32(lckr, Stm32l4x5GpioState),
|
||||
VMSTATE_UINT32(afrl, Stm32l4x5GpioState),
|
||||
VMSTATE_UINT32(afrh, Stm32l4x5GpioState),
|
||||
VMSTATE_UINT32(ascr, Stm32l4x5GpioState),
|
||||
VMSTATE_UINT16(disconnected_pins, Stm32l4x5GpioState),
|
||||
VMSTATE_UINT16(pins_connected_high, Stm32l4x5GpioState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static Property stm32l4x5_gpio_properties[] = {
|
||||
DEFINE_PROP_STRING("name", Stm32l4x5GpioState, name),
|
||||
DEFINE_PROP_UINT32("mode-reset", Stm32l4x5GpioState, moder_reset, 0),
|
||||
DEFINE_PROP_UINT32("ospeed-reset", Stm32l4x5GpioState, ospeedr_reset, 0),
|
||||
DEFINE_PROP_UINT32("pupd-reset", Stm32l4x5GpioState, pupdr_reset, 0),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void stm32l4x5_gpio_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
ResettableClass *rc = RESETTABLE_CLASS(klass);
|
||||
|
||||
device_class_set_props(dc, stm32l4x5_gpio_properties);
|
||||
dc->vmsd = &vmstate_stm32l4x5_gpio;
|
||||
dc->realize = stm32l4x5_gpio_realize;
|
||||
rc->phases.hold = stm32l4x5_gpio_reset_hold;
|
||||
}
|
||||
|
||||
static const TypeInfo stm32l4x5_gpio_types[] = {
|
||||
{
|
||||
.name = TYPE_STM32L4X5_GPIO,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(Stm32l4x5GpioState),
|
||||
.instance_init = stm32l4x5_gpio_init,
|
||||
.class_init = stm32l4x5_gpio_class_init,
|
||||
},
|
||||
};
|
||||
|
||||
DEFINE_TYPES(stm32l4x5_gpio_types)
|
|
@ -31,3 +31,9 @@ sifive_gpio_update_output_irq(int64_t line, int64_t value) "line %" PRIi64 " val
|
|||
# aspeed_gpio.c
|
||||
aspeed_gpio_read(uint64_t offset, uint64_t value) "offset: 0x%" PRIx64 " value 0x%" PRIx64
|
||||
aspeed_gpio_write(uint64_t offset, uint64_t value) "offset: 0x%" PRIx64 " value 0x%" PRIx64
|
||||
|
||||
# stm32l4x5_gpio.c
|
||||
stm32l4x5_gpio_read(char *gpio, uint64_t addr) "GPIO%s addr: 0x%" PRIx64 " "
|
||||
stm32l4x5_gpio_write(char *gpio, uint64_t addr, uint64_t data) "GPIO%s addr: 0x%" PRIx64 " val: 0x%" PRIx64 ""
|
||||
stm32l4x5_gpio_update_idr(char *gpio, uint32_t old_idr, uint32_t new_idr) "GPIO%s from: 0x%x to: 0x%x"
|
||||
stm32l4x5_gpio_pins(char *gpio, uint16_t disconnected, uint16_t high) "GPIO%s disconnected pins: 0x%x levels: 0x%x"
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "hw/irq.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "hw/misc/stm32l4x5_syscfg.h"
|
||||
#include "hw/gpio/stm32l4x5_gpio.h"
|
||||
|
||||
#define SYSCFG_MEMRMP 0x00
|
||||
#define SYSCFG_CFGR1 0x04
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*
|
||||
* Copyright (c) 2016 Artyom Tarasenko
|
||||
*
|
||||
* This code is licensed under the GNU GPL v3 or (at your option) any later
|
||||
* This code is licensed under the GNU GPL v2 or (at your option) any later
|
||||
* version.
|
||||
*/
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "hw/misc/stm32l4x5_syscfg.h"
|
||||
#include "hw/misc/stm32l4x5_exti.h"
|
||||
#include "hw/misc/stm32l4x5_rcc.h"
|
||||
#include "hw/gpio/stm32l4x5_gpio.h"
|
||||
#include "qom/object.h"
|
||||
|
||||
#define TYPE_STM32L4X5_SOC "stm32l4x5-soc"
|
||||
|
@ -49,6 +50,7 @@ struct Stm32l4x5SocState {
|
|||
OrIRQState exti_or_gates[NUM_EXTI_OR_GATES];
|
||||
Stm32l4x5SyscfgState syscfg;
|
||||
Stm32l4x5RccState rcc;
|
||||
Stm32l4x5GpioState gpio[NUM_GPIOS];
|
||||
|
||||
MemoryRegion sram1;
|
||||
MemoryRegion sram2;
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* STM32L4x5 GPIO (General Purpose Input/Ouput)
|
||||
*
|
||||
* Copyright (c) 2024 Arnaud Minier <arnaud.minier@telecom-paris.fr>
|
||||
* Copyright (c) 2024 Inès Varhol <ines.varhol@telecom-paris.fr>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The reference used is the STMicroElectronics RM0351 Reference manual
|
||||
* for STM32L4x5 and STM32L4x6 advanced Arm ® -based 32-bit MCUs.
|
||||
* https://www.st.com/en/microcontrollers-microprocessors/stm32l4x5/documentation.html
|
||||
*/
|
||||
|
||||
#ifndef HW_STM32L4X5_GPIO_H
|
||||
#define HW_STM32L4X5_GPIO_H
|
||||
|
||||
#include "hw/sysbus.h"
|
||||
#include "qom/object.h"
|
||||
|
||||
#define TYPE_STM32L4X5_GPIO "stm32l4x5-gpio"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(Stm32l4x5GpioState, STM32L4X5_GPIO)
|
||||
|
||||
#define NUM_GPIOS 8
|
||||
#define GPIO_NUM_PINS 16
|
||||
|
||||
struct Stm32l4x5GpioState {
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
MemoryRegion mmio;
|
||||
|
||||
/* GPIO registers */
|
||||
uint32_t moder;
|
||||
uint32_t otyper;
|
||||
uint32_t ospeedr;
|
||||
uint32_t pupdr;
|
||||
uint32_t idr;
|
||||
uint32_t odr;
|
||||
uint32_t lckr;
|
||||
uint32_t afrl;
|
||||
uint32_t afrh;
|
||||
uint32_t ascr;
|
||||
|
||||
/* GPIO registers reset values */
|
||||
uint32_t moder_reset;
|
||||
uint32_t ospeedr_reset;
|
||||
uint32_t pupdr_reset;
|
||||
|
||||
/*
|
||||
* External driving of pins.
|
||||
* The pins can be set externally through the device
|
||||
* anonymous input GPIOs lines under certain conditions.
|
||||
* The pin must not be in push-pull output mode,
|
||||
* and can't be set high in open-drain mode.
|
||||
* Pins driven externally and configured to
|
||||
* output mode will in general be "disconnected"
|
||||
* (see `get_gpio_pinmask_to_disconnect()`)
|
||||
*/
|
||||
uint16_t disconnected_pins;
|
||||
uint16_t pins_connected_high;
|
||||
|
||||
char *name;
|
||||
Clock *clk;
|
||||
qemu_irq pin[GPIO_NUM_PINS];
|
||||
};
|
||||
|
||||
#endif
|
|
@ -26,12 +26,11 @@
|
|||
|
||||
#include "hw/sysbus.h"
|
||||
#include "qom/object.h"
|
||||
#include "hw/gpio/stm32l4x5_gpio.h"
|
||||
|
||||
#define TYPE_STM32L4X5_SYSCFG "stm32l4x5-syscfg"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(Stm32l4x5SyscfgState, STM32L4X5_SYSCFG)
|
||||
|
||||
#define NUM_GPIOS 8
|
||||
#define GPIO_NUM_PINS 16
|
||||
#define SYSCFG_NUM_EXTICR 4
|
||||
|
||||
struct Stm32l4x5SyscfgState {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*
|
||||
* Copyright (c) 2016 Artyom Tarasenko
|
||||
*
|
||||
* This code is licensed under the GNU GPL v3 or (at your option) any later
|
||||
* This code is licensed under the GNU GPL v2 or (at your option) any later
|
||||
* version.
|
||||
*/
|
||||
|
||||
|
|
|
@ -741,6 +741,16 @@ static inline bool isar_feature_aa64_fgt(const ARMISARegisters *id)
|
|||
return FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, FGT) != 0;
|
||||
}
|
||||
|
||||
static inline bool isar_feature_aa64_ecv_traps(const ARMISARegisters *id)
|
||||
{
|
||||
return FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, ECV) > 0;
|
||||
}
|
||||
|
||||
static inline bool isar_feature_aa64_ecv(const ARMISARegisters *id)
|
||||
{
|
||||
return FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, ECV) > 1;
|
||||
}
|
||||
|
||||
static inline bool isar_feature_aa64_vh(const ARMISARegisters *id)
|
||||
{
|
||||
return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, VH) != 0;
|
||||
|
|
129
target/arm/cpu.h
129
target/arm/cpu.h
|
@ -140,11 +140,6 @@ typedef struct ARMGenericTimer {
|
|||
uint64_t ctl; /* Timer Control register */
|
||||
} ARMGenericTimer;
|
||||
|
||||
#define VTCR_NSW (1u << 29)
|
||||
#define VTCR_NSA (1u << 30)
|
||||
#define VSTCR_SW VTCR_NSW
|
||||
#define VSTCR_SA VTCR_NSA
|
||||
|
||||
/* Define a maximum sized vector register.
|
||||
* For 32-bit, this is a 128-bit NEON/AdvSIMD register.
|
||||
* For 64-bit, this is a 2048-bit SVE register.
|
||||
|
@ -457,6 +452,7 @@ typedef struct CPUArchState {
|
|||
uint64_t c14_cntkctl; /* Timer Control register */
|
||||
uint64_t cnthctl_el2; /* Counter/Timer Hyp Control register */
|
||||
uint64_t cntvoff_el2; /* Counter Virtual Offset register */
|
||||
uint64_t cntpoff_el2; /* Counter Physical Offset register */
|
||||
ARMGenericTimer c14_timer[NUM_GTIMERS];
|
||||
uint32_t c15_cpar; /* XScale Coprocessor Access Register */
|
||||
uint32_t c15_ticonfig; /* TI925T configuration byte. */
|
||||
|
@ -1375,73 +1371,6 @@ void pmu_init(ARMCPU *cpu);
|
|||
#define SCTLR_SPINTMASK (1ULL << 62) /* FEAT_NMI */
|
||||
#define SCTLR_TIDCP (1ULL << 63) /* FEAT_TIDCP1 */
|
||||
|
||||
/* Bit definitions for CPACR (AArch32 only) */
|
||||
FIELD(CPACR, CP10, 20, 2)
|
||||
FIELD(CPACR, CP11, 22, 2)
|
||||
FIELD(CPACR, TRCDIS, 28, 1) /* matches CPACR_EL1.TTA */
|
||||
FIELD(CPACR, D32DIS, 30, 1) /* up to v7; RAZ in v8 */
|
||||
FIELD(CPACR, ASEDIS, 31, 1)
|
||||
|
||||
/* Bit definitions for CPACR_EL1 (AArch64 only) */
|
||||
FIELD(CPACR_EL1, ZEN, 16, 2)
|
||||
FIELD(CPACR_EL1, FPEN, 20, 2)
|
||||
FIELD(CPACR_EL1, SMEN, 24, 2)
|
||||
FIELD(CPACR_EL1, TTA, 28, 1) /* matches CPACR.TRCDIS */
|
||||
|
||||
/* Bit definitions for HCPTR (AArch32 only) */
|
||||
FIELD(HCPTR, TCP10, 10, 1)
|
||||
FIELD(HCPTR, TCP11, 11, 1)
|
||||
FIELD(HCPTR, TASE, 15, 1)
|
||||
FIELD(HCPTR, TTA, 20, 1)
|
||||
FIELD(HCPTR, TAM, 30, 1) /* matches CPTR_EL2.TAM */
|
||||
FIELD(HCPTR, TCPAC, 31, 1) /* matches CPTR_EL2.TCPAC */
|
||||
|
||||
/* Bit definitions for CPTR_EL2 (AArch64 only) */
|
||||
FIELD(CPTR_EL2, TZ, 8, 1) /* !E2H */
|
||||
FIELD(CPTR_EL2, TFP, 10, 1) /* !E2H, matches HCPTR.TCP10 */
|
||||
FIELD(CPTR_EL2, TSM, 12, 1) /* !E2H */
|
||||
FIELD(CPTR_EL2, ZEN, 16, 2) /* E2H */
|
||||
FIELD(CPTR_EL2, FPEN, 20, 2) /* E2H */
|
||||
FIELD(CPTR_EL2, SMEN, 24, 2) /* E2H */
|
||||
FIELD(CPTR_EL2, TTA, 28, 1)
|
||||
FIELD(CPTR_EL2, TAM, 30, 1) /* matches HCPTR.TAM */
|
||||
FIELD(CPTR_EL2, TCPAC, 31, 1) /* matches HCPTR.TCPAC */
|
||||
|
||||
/* Bit definitions for CPTR_EL3 (AArch64 only) */
|
||||
FIELD(CPTR_EL3, EZ, 8, 1)
|
||||
FIELD(CPTR_EL3, TFP, 10, 1)
|
||||
FIELD(CPTR_EL3, ESM, 12, 1)
|
||||
FIELD(CPTR_EL3, TTA, 20, 1)
|
||||
FIELD(CPTR_EL3, TAM, 30, 1)
|
||||
FIELD(CPTR_EL3, TCPAC, 31, 1)
|
||||
|
||||
#define MDCR_MTPME (1U << 28)
|
||||
#define MDCR_TDCC (1U << 27)
|
||||
#define MDCR_HLP (1U << 26) /* MDCR_EL2 */
|
||||
#define MDCR_SCCD (1U << 23) /* MDCR_EL3 */
|
||||
#define MDCR_HCCD (1U << 23) /* MDCR_EL2 */
|
||||
#define MDCR_EPMAD (1U << 21)
|
||||
#define MDCR_EDAD (1U << 20)
|
||||
#define MDCR_TTRF (1U << 19)
|
||||
#define MDCR_STE (1U << 18) /* MDCR_EL3 */
|
||||
#define MDCR_SPME (1U << 17) /* MDCR_EL3 */
|
||||
#define MDCR_HPMD (1U << 17) /* MDCR_EL2 */
|
||||
#define MDCR_SDD (1U << 16)
|
||||
#define MDCR_SPD (3U << 14)
|
||||
#define MDCR_TDRA (1U << 11)
|
||||
#define MDCR_TDOSA (1U << 10)
|
||||
#define MDCR_TDA (1U << 9)
|
||||
#define MDCR_TDE (1U << 8)
|
||||
#define MDCR_HPME (1U << 7)
|
||||
#define MDCR_TPM (1U << 6)
|
||||
#define MDCR_TPMCR (1U << 5)
|
||||
#define MDCR_HPMN (0x1fU)
|
||||
|
||||
/* Not all of the MDCR_EL3 bits are present in the 32-bit SDCR */
|
||||
#define SDCR_VALID_MASK (MDCR_MTPME | MDCR_TDCC | MDCR_SCCD | \
|
||||
MDCR_EPMAD | MDCR_EDAD | MDCR_TTRF | \
|
||||
MDCR_STE | MDCR_SPME | MDCR_SPD)
|
||||
|
||||
#define CPSR_M (0x1fU)
|
||||
#define CPSR_T (1U << 5)
|
||||
#define CPSR_F (1U << 6)
|
||||
|
@ -1488,41 +1417,6 @@ FIELD(CPTR_EL3, TCPAC, 31, 1)
|
|||
#define XPSR_NZCV CPSR_NZCV
|
||||
#define XPSR_IT CPSR_IT
|
||||
|
||||
#define TTBCR_N (7U << 0) /* TTBCR.EAE==0 */
|
||||
#define TTBCR_T0SZ (7U << 0) /* TTBCR.EAE==1 */
|
||||
#define TTBCR_PD0 (1U << 4)
|
||||
#define TTBCR_PD1 (1U << 5)
|
||||
#define TTBCR_EPD0 (1U << 7)
|
||||
#define TTBCR_IRGN0 (3U << 8)
|
||||
#define TTBCR_ORGN0 (3U << 10)
|
||||
#define TTBCR_SH0 (3U << 12)
|
||||
#define TTBCR_T1SZ (3U << 16)
|
||||
#define TTBCR_A1 (1U << 22)
|
||||
#define TTBCR_EPD1 (1U << 23)
|
||||
#define TTBCR_IRGN1 (3U << 24)
|
||||
#define TTBCR_ORGN1 (3U << 26)
|
||||
#define TTBCR_SH1 (1U << 28)
|
||||
#define TTBCR_EAE (1U << 31)
|
||||
|
||||
FIELD(VTCR, T0SZ, 0, 6)
|
||||
FIELD(VTCR, SL0, 6, 2)
|
||||
FIELD(VTCR, IRGN0, 8, 2)
|
||||
FIELD(VTCR, ORGN0, 10, 2)
|
||||
FIELD(VTCR, SH0, 12, 2)
|
||||
FIELD(VTCR, TG0, 14, 2)
|
||||
FIELD(VTCR, PS, 16, 3)
|
||||
FIELD(VTCR, VS, 19, 1)
|
||||
FIELD(VTCR, HA, 21, 1)
|
||||
FIELD(VTCR, HD, 22, 1)
|
||||
FIELD(VTCR, HWU59, 25, 1)
|
||||
FIELD(VTCR, HWU60, 26, 1)
|
||||
FIELD(VTCR, HWU61, 27, 1)
|
||||
FIELD(VTCR, HWU62, 28, 1)
|
||||
FIELD(VTCR, NSW, 29, 1)
|
||||
FIELD(VTCR, NSA, 30, 1)
|
||||
FIELD(VTCR, DS, 32, 1)
|
||||
FIELD(VTCR, SL2, 33, 1)
|
||||
|
||||
/* Bit definitions for ARMv8 SPSR (PSTATE) format.
|
||||
* Only these are valid when in AArch64 mode; in
|
||||
* AArch32 mode SPSRs are basically CPSR-format.
|
||||
|
@ -1730,21 +1624,6 @@ static inline void xpsr_write(CPUARMState *env, uint32_t val, uint32_t mask)
|
|||
#define HCR_TWEDEN (1ULL << 59)
|
||||
#define HCR_TWEDEL MAKE_64BIT_MASK(60, 4)
|
||||
|
||||
#define HCRX_ENAS0 (1ULL << 0)
|
||||
#define HCRX_ENALS (1ULL << 1)
|
||||
#define HCRX_ENASR (1ULL << 2)
|
||||
#define HCRX_FNXS (1ULL << 3)
|
||||
#define HCRX_FGTNXS (1ULL << 4)
|
||||
#define HCRX_SMPME (1ULL << 5)
|
||||
#define HCRX_TALLINT (1ULL << 6)
|
||||
#define HCRX_VINMI (1ULL << 7)
|
||||
#define HCRX_VFNMI (1ULL << 8)
|
||||
#define HCRX_CMOW (1ULL << 9)
|
||||
#define HCRX_MCE2 (1ULL << 10)
|
||||
#define HCRX_MSCEN (1ULL << 11)
|
||||
|
||||
#define HPFAR_NS (1ULL << 63)
|
||||
|
||||
#define SCR_NS (1ULL << 0)
|
||||
#define SCR_IRQ (1ULL << 1)
|
||||
#define SCR_FIQ (1ULL << 2)
|
||||
|
@ -1783,12 +1662,6 @@ static inline void xpsr_write(CPUARMState *env, uint32_t val, uint32_t mask)
|
|||
#define SCR_GPF (1ULL << 48)
|
||||
#define SCR_NSE (1ULL << 62)
|
||||
|
||||
#define HSTR_TTEE (1 << 16)
|
||||
#define HSTR_TJDBX (1 << 17)
|
||||
|
||||
#define CNTHCTL_CNTVMASK (1 << 18)
|
||||
#define CNTHCTL_CNTPMASK (1 << 19)
|
||||
|
||||
/* Return the current FPSCR value. */
|
||||
uint32_t vfp_get_fpscr(CPUARMState *env);
|
||||
void vfp_set_fpscr(CPUARMState *env, uint32_t val);
|
||||
|
|
|
@ -1923,6 +1923,9 @@ static void scr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value)
|
|||
if (cpu_isar_feature(aa64_rme, cpu)) {
|
||||
valid_mask |= SCR_NSE | SCR_GPF;
|
||||
}
|
||||
if (cpu_isar_feature(aa64_ecv, cpu)) {
|
||||
valid_mask |= SCR_ECVEN;
|
||||
}
|
||||
} else {
|
||||
valid_mask &= ~(SCR_RW | SCR_ST);
|
||||
if (cpu_isar_feature(aa32_ras, cpu)) {
|
||||
|
@ -2530,6 +2533,11 @@ static CPAccessResult gt_counter_access(CPUARMState *env, int timeridx,
|
|||
: !extract32(env->cp15.cnthctl_el2, 0, 1))) {
|
||||
return CP_ACCESS_TRAP_EL2;
|
||||
}
|
||||
if (has_el2 && timeridx == GTIMER_VIRT) {
|
||||
if (FIELD_EX64(env->cp15.cnthctl_el2, CNTHCTL, EL1TVCT)) {
|
||||
return CP_ACCESS_TRAP_EL2;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return CP_ACCESS_OK;
|
||||
|
@ -2573,6 +2581,11 @@ static CPAccessResult gt_timer_access(CPUARMState *env, int timeridx,
|
|||
}
|
||||
}
|
||||
}
|
||||
if (has_el2 && timeridx == GTIMER_VIRT) {
|
||||
if (FIELD_EX64(env->cp15.cnthctl_el2, CNTHCTL, EL1TVT)) {
|
||||
return CP_ACCESS_TRAP_EL2;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return CP_ACCESS_OK;
|
||||
|
@ -2652,8 +2665,8 @@ static void gt_update_irq(ARMCPU *cpu, int timeridx)
|
|||
* It is RES0 in Secure and NonSecure state.
|
||||
*/
|
||||
if ((ss == ARMSS_Root || ss == ARMSS_Realm) &&
|
||||
((timeridx == GTIMER_VIRT && (cnthctl & CNTHCTL_CNTVMASK)) ||
|
||||
(timeridx == GTIMER_PHYS && (cnthctl & CNTHCTL_CNTPMASK)))) {
|
||||
((timeridx == GTIMER_VIRT && (cnthctl & R_CNTHCTL_CNTVMASK_MASK)) ||
|
||||
(timeridx == GTIMER_PHYS && (cnthctl & R_CNTHCTL_CNTPMASK_MASK)))) {
|
||||
irqstate = 0;
|
||||
}
|
||||
|
||||
|
@ -2672,6 +2685,25 @@ void gt_rme_post_el_change(ARMCPU *cpu, void *ignored)
|
|||
gt_update_irq(cpu, GTIMER_PHYS);
|
||||
}
|
||||
|
||||
static uint64_t gt_phys_raw_cnt_offset(CPUARMState *env)
|
||||
{
|
||||
if ((env->cp15.scr_el3 & SCR_ECVEN) &&
|
||||
FIELD_EX64(env->cp15.cnthctl_el2, CNTHCTL, ECV) &&
|
||||
arm_is_el2_enabled(env) &&
|
||||
(arm_hcr_el2_eff(env) & (HCR_E2H | HCR_TGE)) != (HCR_E2H | HCR_TGE)) {
|
||||
return env->cp15.cntpoff_el2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint64_t gt_phys_cnt_offset(CPUARMState *env)
|
||||
{
|
||||
if (arm_current_el(env) >= 2) {
|
||||
return 0;
|
||||
}
|
||||
return gt_phys_raw_cnt_offset(env);
|
||||
}
|
||||
|
||||
static void gt_recalc_timer(ARMCPU *cpu, int timeridx)
|
||||
{
|
||||
ARMGenericTimer *gt = &cpu->env.cp15.c14_timer[timeridx];
|
||||
|
@ -2682,7 +2714,7 @@ static void gt_recalc_timer(ARMCPU *cpu, int timeridx)
|
|||
* reset timer to when ISTATUS next has to change
|
||||
*/
|
||||
uint64_t offset = timeridx == GTIMER_VIRT ?
|
||||
cpu->env.cp15.cntvoff_el2 : 0;
|
||||
cpu->env.cp15.cntvoff_el2 : gt_phys_raw_cnt_offset(&cpu->env);
|
||||
uint64_t count = gt_get_countervalue(&cpu->env);
|
||||
/* Note that this must be unsigned 64 bit arithmetic: */
|
||||
int istatus = count - offset >= gt->cval;
|
||||
|
@ -2745,7 +2777,7 @@ static void gt_timer_reset(CPUARMState *env, const ARMCPRegInfo *ri,
|
|||
|
||||
static uint64_t gt_cnt_read(CPUARMState *env, const ARMCPRegInfo *ri)
|
||||
{
|
||||
return gt_get_countervalue(env);
|
||||
return gt_get_countervalue(env) - gt_phys_cnt_offset(env);
|
||||
}
|
||||
|
||||
static uint64_t gt_virt_cnt_offset(CPUARMState *env)
|
||||
|
@ -2794,6 +2826,9 @@ static uint64_t gt_tval_read(CPUARMState *env, const ARMCPRegInfo *ri,
|
|||
case GTIMER_HYPVIRT:
|
||||
offset = gt_virt_cnt_offset(env);
|
||||
break;
|
||||
case GTIMER_PHYS:
|
||||
offset = gt_phys_cnt_offset(env);
|
||||
break;
|
||||
}
|
||||
|
||||
return (uint32_t)(env->cp15.c14_timer[timeridx].cval -
|
||||
|
@ -2811,6 +2846,9 @@ static void gt_tval_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
|||
case GTIMER_HYPVIRT:
|
||||
offset = gt_virt_cnt_offset(env);
|
||||
break;
|
||||
case GTIMER_PHYS:
|
||||
offset = gt_phys_cnt_offset(env);
|
||||
break;
|
||||
}
|
||||
|
||||
trace_arm_gt_tval_write(timeridx, value);
|
||||
|
@ -2968,12 +3006,40 @@ static void gt_cnthctl_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
|||
{
|
||||
ARMCPU *cpu = env_archcpu(env);
|
||||
uint32_t oldval = env->cp15.cnthctl_el2;
|
||||
uint32_t valid_mask =
|
||||
R_CNTHCTL_EL0PCTEN_E2H1_MASK |
|
||||
R_CNTHCTL_EL0VCTEN_E2H1_MASK |
|
||||
R_CNTHCTL_EVNTEN_MASK |
|
||||
R_CNTHCTL_EVNTDIR_MASK |
|
||||
R_CNTHCTL_EVNTI_MASK |
|
||||
R_CNTHCTL_EL0VTEN_MASK |
|
||||
R_CNTHCTL_EL0PTEN_MASK |
|
||||
R_CNTHCTL_EL1PCTEN_E2H1_MASK |
|
||||
R_CNTHCTL_EL1PTEN_MASK;
|
||||
|
||||
if (cpu_isar_feature(aa64_rme, cpu)) {
|
||||
valid_mask |= R_CNTHCTL_CNTVMASK_MASK | R_CNTHCTL_CNTPMASK_MASK;
|
||||
}
|
||||
if (cpu_isar_feature(aa64_ecv_traps, cpu)) {
|
||||
valid_mask |=
|
||||
R_CNTHCTL_EL1TVT_MASK |
|
||||
R_CNTHCTL_EL1TVCT_MASK |
|
||||
R_CNTHCTL_EL1NVPCT_MASK |
|
||||
R_CNTHCTL_EL1NVVCT_MASK |
|
||||
R_CNTHCTL_EVNTIS_MASK;
|
||||
}
|
||||
if (cpu_isar_feature(aa64_ecv, cpu)) {
|
||||
valid_mask |= R_CNTHCTL_ECV_MASK;
|
||||
}
|
||||
|
||||
/* Clear RES0 bits */
|
||||
value &= valid_mask;
|
||||
|
||||
raw_write(env, ri, value);
|
||||
|
||||
if ((oldval ^ value) & CNTHCTL_CNTVMASK) {
|
||||
if ((oldval ^ value) & R_CNTHCTL_CNTVMASK_MASK) {
|
||||
gt_update_irq(cpu, GTIMER_VIRT);
|
||||
} else if ((oldval ^ value) & CNTHCTL_CNTPMASK) {
|
||||
} else if ((oldval ^ value) & R_CNTHCTL_CNTPMASK_MASK) {
|
||||
gt_update_irq(cpu, GTIMER_PHYS);
|
||||
}
|
||||
}
|
||||
|
@ -3354,6 +3420,62 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = {
|
|||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* FEAT_ECV adds extra views of CNTVCT_EL0 and CNTPCT_EL0 which
|
||||
* are "self-synchronizing". For QEMU all sysregs are self-synchronizing,
|
||||
* so our implementations here are identical to the normal registers.
|
||||
*/
|
||||
static const ARMCPRegInfo gen_timer_ecv_cp_reginfo[] = {
|
||||
{ .name = "CNTVCTSS", .cp = 15, .crm = 14, .opc1 = 9,
|
||||
.access = PL0_R, .type = ARM_CP_64BIT | ARM_CP_NO_RAW | ARM_CP_IO,
|
||||
.accessfn = gt_vct_access,
|
||||
.readfn = gt_virt_cnt_read, .resetfn = arm_cp_reset_ignore,
|
||||
},
|
||||
{ .name = "CNTVCTSS_EL0", .state = ARM_CP_STATE_AA64,
|
||||
.opc0 = 3, .opc1 = 3, .crn = 14, .crm = 0, .opc2 = 6,
|
||||
.access = PL0_R, .type = ARM_CP_NO_RAW | ARM_CP_IO,
|
||||
.accessfn = gt_vct_access, .readfn = gt_virt_cnt_read,
|
||||
},
|
||||
{ .name = "CNTPCTSS", .cp = 15, .crm = 14, .opc1 = 8,
|
||||
.access = PL0_R, .type = ARM_CP_64BIT | ARM_CP_NO_RAW | ARM_CP_IO,
|
||||
.accessfn = gt_pct_access,
|
||||
.readfn = gt_cnt_read, .resetfn = arm_cp_reset_ignore,
|
||||
},
|
||||
{ .name = "CNTPCTSS_EL0", .state = ARM_CP_STATE_AA64,
|
||||
.opc0 = 3, .opc1 = 3, .crn = 14, .crm = 0, .opc2 = 5,
|
||||
.access = PL0_R, .type = ARM_CP_NO_RAW | ARM_CP_IO,
|
||||
.accessfn = gt_pct_access, .readfn = gt_cnt_read,
|
||||
},
|
||||
};
|
||||
|
||||
static CPAccessResult gt_cntpoff_access(CPUARMState *env,
|
||||
const ARMCPRegInfo *ri,
|
||||
bool isread)
|
||||
{
|
||||
if (arm_current_el(env) == 2 && !(env->cp15.scr_el3 & SCR_ECVEN)) {
|
||||
return CP_ACCESS_TRAP_EL3;
|
||||
}
|
||||
return CP_ACCESS_OK;
|
||||
}
|
||||
|
||||
static void gt_cntpoff_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
uint64_t value)
|
||||
{
|
||||
ARMCPU *cpu = env_archcpu(env);
|
||||
|
||||
trace_arm_gt_cntpoff_write(value);
|
||||
raw_write(env, ri, value);
|
||||
gt_recalc_timer(cpu, GTIMER_PHYS);
|
||||
}
|
||||
|
||||
static const ARMCPRegInfo gen_timer_cntpoff_reginfo = {
|
||||
.name = "CNTPOFF_EL2", .state = ARM_CP_STATE_AA64,
|
||||
.opc0 = 3, .opc1 = 4, .crn = 14, .crm = 0, .opc2 = 6,
|
||||
.access = PL2_RW, .type = ARM_CP_IO, .resetvalue = 0,
|
||||
.accessfn = gt_cntpoff_access, .writefn = gt_cntpoff_write,
|
||||
.nv2_redirect_offset = 0x1a8,
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.cntpoff_el2),
|
||||
};
|
||||
#else
|
||||
|
||||
/*
|
||||
|
@ -3387,6 +3509,18 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = {
|
|||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* CNTVCTSS_EL0 has the same trap conditions as CNTVCT_EL0, so it also
|
||||
* is exposed to userspace by Linux.
|
||||
*/
|
||||
static const ARMCPRegInfo gen_timer_ecv_cp_reginfo[] = {
|
||||
{ .name = "CNTVCTSS_EL0", .state = ARM_CP_STATE_AA64,
|
||||
.opc0 = 3, .opc1 = 3, .crn = 14, .crm = 0, .opc2 = 6,
|
||||
.access = PL0_R, .type = ARM_CP_NO_RAW | ARM_CP_IO,
|
||||
.readfn = gt_virt_cnt_read,
|
||||
},
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
static void par_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value)
|
||||
|
@ -6547,15 +6681,38 @@ static CPAccessResult e2h_access(CPUARMState *env, const ARMCPRegInfo *ri,
|
|||
{
|
||||
if (arm_current_el(env) == 1) {
|
||||
/* This must be a FEAT_NV access */
|
||||
/* TODO: FEAT_ECV will need to check CNTHCTL_EL2 here */
|
||||
return CP_ACCESS_OK;
|
||||
}
|
||||
if (!(arm_hcr_el2_eff(env) & HCR_E2H)) {
|
||||
return CP_ACCESS_TRAP;
|
||||
return CP_ACCESS_TRAP_UNCATEGORIZED;
|
||||
}
|
||||
return CP_ACCESS_OK;
|
||||
}
|
||||
|
||||
static CPAccessResult access_el1nvpct(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
bool isread)
|
||||
{
|
||||
if (arm_current_el(env) == 1) {
|
||||
/* This must be a FEAT_NV access with NVx == 101 */
|
||||
if (FIELD_EX64(env->cp15.cnthctl_el2, CNTHCTL, EL1NVPCT)) {
|
||||
return CP_ACCESS_TRAP_EL2;
|
||||
}
|
||||
}
|
||||
return e2h_access(env, ri, isread);
|
||||
}
|
||||
|
||||
static CPAccessResult access_el1nvvct(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
bool isread)
|
||||
{
|
||||
if (arm_current_el(env) == 1) {
|
||||
/* This must be a FEAT_NV access with NVx == 101 */
|
||||
if (FIELD_EX64(env->cp15.cnthctl_el2, CNTHCTL, EL1NVVCT)) {
|
||||
return CP_ACCESS_TRAP_EL2;
|
||||
}
|
||||
}
|
||||
return e2h_access(env, ri, isread);
|
||||
}
|
||||
|
||||
/* Test if system register redirection is to occur in the current state. */
|
||||
static bool redirect_for_e2h(CPUARMState *env)
|
||||
{
|
||||
|
@ -8381,14 +8538,14 @@ static const ARMCPRegInfo vhe_reginfo[] = {
|
|||
{ .name = "CNTP_CTL_EL02", .state = ARM_CP_STATE_AA64,
|
||||
.opc0 = 3, .opc1 = 5, .crn = 14, .crm = 2, .opc2 = 1,
|
||||
.type = ARM_CP_IO | ARM_CP_ALIAS,
|
||||
.access = PL2_RW, .accessfn = e2h_access,
|
||||
.access = PL2_RW, .accessfn = access_el1nvpct,
|
||||
.nv2_redirect_offset = 0x180 | NV2_REDIR_NO_NV1,
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].ctl),
|
||||
.writefn = gt_phys_ctl_write, .raw_writefn = raw_write },
|
||||
{ .name = "CNTV_CTL_EL02", .state = ARM_CP_STATE_AA64,
|
||||
.opc0 = 3, .opc1 = 5, .crn = 14, .crm = 3, .opc2 = 1,
|
||||
.type = ARM_CP_IO | ARM_CP_ALIAS,
|
||||
.access = PL2_RW, .accessfn = e2h_access,
|
||||
.access = PL2_RW, .accessfn = access_el1nvvct,
|
||||
.nv2_redirect_offset = 0x170 | NV2_REDIR_NO_NV1,
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].ctl),
|
||||
.writefn = gt_virt_ctl_write, .raw_writefn = raw_write },
|
||||
|
@ -8407,14 +8564,14 @@ static const ARMCPRegInfo vhe_reginfo[] = {
|
|||
.type = ARM_CP_IO | ARM_CP_ALIAS,
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].cval),
|
||||
.nv2_redirect_offset = 0x178 | NV2_REDIR_NO_NV1,
|
||||
.access = PL2_RW, .accessfn = e2h_access,
|
||||
.access = PL2_RW, .accessfn = access_el1nvpct,
|
||||
.writefn = gt_phys_cval_write, .raw_writefn = raw_write },
|
||||
{ .name = "CNTV_CVAL_EL02", .state = ARM_CP_STATE_AA64,
|
||||
.opc0 = 3, .opc1 = 5, .crn = 14, .crm = 3, .opc2 = 2,
|
||||
.type = ARM_CP_IO | ARM_CP_ALIAS,
|
||||
.nv2_redirect_offset = 0x168 | NV2_REDIR_NO_NV1,
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].cval),
|
||||
.access = PL2_RW, .accessfn = e2h_access,
|
||||
.access = PL2_RW, .accessfn = access_el1nvvct,
|
||||
.writefn = gt_virt_cval_write, .raw_writefn = raw_write },
|
||||
#endif
|
||||
};
|
||||
|
@ -9200,6 +9357,14 @@ void register_cp_regs_for_features(ARMCPU *cpu)
|
|||
if (arm_feature(env, ARM_FEATURE_GENERIC_TIMER)) {
|
||||
define_arm_cp_regs(cpu, generic_timer_cp_reginfo);
|
||||
}
|
||||
if (cpu_isar_feature(aa64_ecv_traps, cpu)) {
|
||||
define_arm_cp_regs(cpu, gen_timer_ecv_cp_reginfo);
|
||||
}
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
if (cpu_isar_feature(aa64_ecv, cpu)) {
|
||||
define_one_arm_cp_reg(cpu, &gen_timer_cntpoff_reginfo);
|
||||
}
|
||||
#endif
|
||||
if (arm_feature(env, ARM_FEATURE_VAPA)) {
|
||||
ARMCPRegInfo vapa_cp_reginfo[] = {
|
||||
{ .name = "PAR", .cp = 15, .crn = 7, .crm = 4, .opc1 = 0, .opc2 = 0,
|
||||
|
|
|
@ -99,6 +99,157 @@ FIELD(DBGWCR, WT, 20, 1)
|
|||
FIELD(DBGWCR, MASK, 24, 5)
|
||||
FIELD(DBGWCR, SSCE, 29, 1)
|
||||
|
||||
#define VTCR_NSW (1u << 29)
|
||||
#define VTCR_NSA (1u << 30)
|
||||
#define VSTCR_SW VTCR_NSW
|
||||
#define VSTCR_SA VTCR_NSA
|
||||
|
||||
/* Bit definitions for CPACR (AArch32 only) */
|
||||
FIELD(CPACR, CP10, 20, 2)
|
||||
FIELD(CPACR, CP11, 22, 2)
|
||||
FIELD(CPACR, TRCDIS, 28, 1) /* matches CPACR_EL1.TTA */
|
||||
FIELD(CPACR, D32DIS, 30, 1) /* up to v7; RAZ in v8 */
|
||||
FIELD(CPACR, ASEDIS, 31, 1)
|
||||
|
||||
/* Bit definitions for CPACR_EL1 (AArch64 only) */
|
||||
FIELD(CPACR_EL1, ZEN, 16, 2)
|
||||
FIELD(CPACR_EL1, FPEN, 20, 2)
|
||||
FIELD(CPACR_EL1, SMEN, 24, 2)
|
||||
FIELD(CPACR_EL1, TTA, 28, 1) /* matches CPACR.TRCDIS */
|
||||
|
||||
/* Bit definitions for HCPTR (AArch32 only) */
|
||||
FIELD(HCPTR, TCP10, 10, 1)
|
||||
FIELD(HCPTR, TCP11, 11, 1)
|
||||
FIELD(HCPTR, TASE, 15, 1)
|
||||
FIELD(HCPTR, TTA, 20, 1)
|
||||
FIELD(HCPTR, TAM, 30, 1) /* matches CPTR_EL2.TAM */
|
||||
FIELD(HCPTR, TCPAC, 31, 1) /* matches CPTR_EL2.TCPAC */
|
||||
|
||||
/* Bit definitions for CPTR_EL2 (AArch64 only) */
|
||||
FIELD(CPTR_EL2, TZ, 8, 1) /* !E2H */
|
||||
FIELD(CPTR_EL2, TFP, 10, 1) /* !E2H, matches HCPTR.TCP10 */
|
||||
FIELD(CPTR_EL2, TSM, 12, 1) /* !E2H */
|
||||
FIELD(CPTR_EL2, ZEN, 16, 2) /* E2H */
|
||||
FIELD(CPTR_EL2, FPEN, 20, 2) /* E2H */
|
||||
FIELD(CPTR_EL2, SMEN, 24, 2) /* E2H */
|
||||
FIELD(CPTR_EL2, TTA, 28, 1)
|
||||
FIELD(CPTR_EL2, TAM, 30, 1) /* matches HCPTR.TAM */
|
||||
FIELD(CPTR_EL2, TCPAC, 31, 1) /* matches HCPTR.TCPAC */
|
||||
|
||||
/* Bit definitions for CPTR_EL3 (AArch64 only) */
|
||||
FIELD(CPTR_EL3, EZ, 8, 1)
|
||||
FIELD(CPTR_EL3, TFP, 10, 1)
|
||||
FIELD(CPTR_EL3, ESM, 12, 1)
|
||||
FIELD(CPTR_EL3, TTA, 20, 1)
|
||||
FIELD(CPTR_EL3, TAM, 30, 1)
|
||||
FIELD(CPTR_EL3, TCPAC, 31, 1)
|
||||
|
||||
#define MDCR_MTPME (1U << 28)
|
||||
#define MDCR_TDCC (1U << 27)
|
||||
#define MDCR_HLP (1U << 26) /* MDCR_EL2 */
|
||||
#define MDCR_SCCD (1U << 23) /* MDCR_EL3 */
|
||||
#define MDCR_HCCD (1U << 23) /* MDCR_EL2 */
|
||||
#define MDCR_EPMAD (1U << 21)
|
||||
#define MDCR_EDAD (1U << 20)
|
||||
#define MDCR_TTRF (1U << 19)
|
||||
#define MDCR_STE (1U << 18) /* MDCR_EL3 */
|
||||
#define MDCR_SPME (1U << 17) /* MDCR_EL3 */
|
||||
#define MDCR_HPMD (1U << 17) /* MDCR_EL2 */
|
||||
#define MDCR_SDD (1U << 16)
|
||||
#define MDCR_SPD (3U << 14)
|
||||
#define MDCR_TDRA (1U << 11)
|
||||
#define MDCR_TDOSA (1U << 10)
|
||||
#define MDCR_TDA (1U << 9)
|
||||
#define MDCR_TDE (1U << 8)
|
||||
#define MDCR_HPME (1U << 7)
|
||||
#define MDCR_TPM (1U << 6)
|
||||
#define MDCR_TPMCR (1U << 5)
|
||||
#define MDCR_HPMN (0x1fU)
|
||||
|
||||
/* Not all of the MDCR_EL3 bits are present in the 32-bit SDCR */
|
||||
#define SDCR_VALID_MASK (MDCR_MTPME | MDCR_TDCC | MDCR_SCCD | \
|
||||
MDCR_EPMAD | MDCR_EDAD | MDCR_TTRF | \
|
||||
MDCR_STE | MDCR_SPME | MDCR_SPD)
|
||||
|
||||
#define TTBCR_N (7U << 0) /* TTBCR.EAE==0 */
|
||||
#define TTBCR_T0SZ (7U << 0) /* TTBCR.EAE==1 */
|
||||
#define TTBCR_PD0 (1U << 4)
|
||||
#define TTBCR_PD1 (1U << 5)
|
||||
#define TTBCR_EPD0 (1U << 7)
|
||||
#define TTBCR_IRGN0 (3U << 8)
|
||||
#define TTBCR_ORGN0 (3U << 10)
|
||||
#define TTBCR_SH0 (3U << 12)
|
||||
#define TTBCR_T1SZ (3U << 16)
|
||||
#define TTBCR_A1 (1U << 22)
|
||||
#define TTBCR_EPD1 (1U << 23)
|
||||
#define TTBCR_IRGN1 (3U << 24)
|
||||
#define TTBCR_ORGN1 (3U << 26)
|
||||
#define TTBCR_SH1 (1U << 28)
|
||||
#define TTBCR_EAE (1U << 31)
|
||||
|
||||
FIELD(VTCR, T0SZ, 0, 6)
|
||||
FIELD(VTCR, SL0, 6, 2)
|
||||
FIELD(VTCR, IRGN0, 8, 2)
|
||||
FIELD(VTCR, ORGN0, 10, 2)
|
||||
FIELD(VTCR, SH0, 12, 2)
|
||||
FIELD(VTCR, TG0, 14, 2)
|
||||
FIELD(VTCR, PS, 16, 3)
|
||||
FIELD(VTCR, VS, 19, 1)
|
||||
FIELD(VTCR, HA, 21, 1)
|
||||
FIELD(VTCR, HD, 22, 1)
|
||||
FIELD(VTCR, HWU59, 25, 1)
|
||||
FIELD(VTCR, HWU60, 26, 1)
|
||||
FIELD(VTCR, HWU61, 27, 1)
|
||||
FIELD(VTCR, HWU62, 28, 1)
|
||||
FIELD(VTCR, NSW, 29, 1)
|
||||
FIELD(VTCR, NSA, 30, 1)
|
||||
FIELD(VTCR, DS, 32, 1)
|
||||
FIELD(VTCR, SL2, 33, 1)
|
||||
|
||||
#define HCRX_ENAS0 (1ULL << 0)
|
||||
#define HCRX_ENALS (1ULL << 1)
|
||||
#define HCRX_ENASR (1ULL << 2)
|
||||
#define HCRX_FNXS (1ULL << 3)
|
||||
#define HCRX_FGTNXS (1ULL << 4)
|
||||
#define HCRX_SMPME (1ULL << 5)
|
||||
#define HCRX_TALLINT (1ULL << 6)
|
||||
#define HCRX_VINMI (1ULL << 7)
|
||||
#define HCRX_VFNMI (1ULL << 8)
|
||||
#define HCRX_CMOW (1ULL << 9)
|
||||
#define HCRX_MCE2 (1ULL << 10)
|
||||
#define HCRX_MSCEN (1ULL << 11)
|
||||
|
||||
#define HPFAR_NS (1ULL << 63)
|
||||
|
||||
#define HSTR_TTEE (1 << 16)
|
||||
#define HSTR_TJDBX (1 << 17)
|
||||
|
||||
/*
|
||||
* Depending on the value of HCR_EL2.E2H, bits 0 and 1
|
||||
* have different bit definitions, and EL1PCTEN might be
|
||||
* bit 0 or bit 10. We use _E2H1 and _E2H0 suffixes to
|
||||
* disambiguate if necessary.
|
||||
*/
|
||||
FIELD(CNTHCTL, EL0PCTEN_E2H1, 0, 1)
|
||||
FIELD(CNTHCTL, EL0VCTEN_E2H1, 1, 1)
|
||||
FIELD(CNTHCTL, EL1PCTEN_E2H0, 0, 1)
|
||||
FIELD(CNTHCTL, EL1PCEN_E2H0, 1, 1)
|
||||
FIELD(CNTHCTL, EVNTEN, 2, 1)
|
||||
FIELD(CNTHCTL, EVNTDIR, 3, 1)
|
||||
FIELD(CNTHCTL, EVNTI, 4, 4)
|
||||
FIELD(CNTHCTL, EL0VTEN, 8, 1)
|
||||
FIELD(CNTHCTL, EL0PTEN, 9, 1)
|
||||
FIELD(CNTHCTL, EL1PCTEN_E2H1, 10, 1)
|
||||
FIELD(CNTHCTL, EL1PTEN, 11, 1)
|
||||
FIELD(CNTHCTL, ECV, 12, 1)
|
||||
FIELD(CNTHCTL, EL1TVT, 13, 1)
|
||||
FIELD(CNTHCTL, EL1TVCT, 14, 1)
|
||||
FIELD(CNTHCTL, EL1NVPCT, 15, 1)
|
||||
FIELD(CNTHCTL, EL1NVVCT, 16, 1)
|
||||
FIELD(CNTHCTL, EVNTIS, 17, 1)
|
||||
FIELD(CNTHCTL, CNTVMASK, 18, 1)
|
||||
FIELD(CNTHCTL, CNTPMASK, 19, 1)
|
||||
|
||||
/* We use a few fake FSR values for internal purposes in M profile.
|
||||
* M profile cores don't have A/R format FSRs, but currently our
|
||||
* get_phys_addr() code assumes A/R profile and reports failures via
|
||||
|
|
|
@ -26,6 +26,8 @@ arm_system_ss.add(files(
|
|||
'ptw.c',
|
||||
))
|
||||
|
||||
arm_user_ss = ss.source_set()
|
||||
|
||||
subdir('hvf')
|
||||
|
||||
if 'CONFIG_TCG' in config_all_accel
|
||||
|
@ -36,3 +38,4 @@ endif
|
|||
|
||||
target_arch += {'arm': arm_ss}
|
||||
target_system_arch += {'arm': arm_system_ss}
|
||||
target_user_arch += {'arm': arm_user_ss}
|
||||
|
|
|
@ -0,0 +1,290 @@
|
|||
/*
|
||||
* QEMU ARMv7-M TCG-only CPUs.
|
||||
*
|
||||
* Copyright (c) 2012 SUSE LINUX Products GmbH
|
||||
*
|
||||
* This code is licensed under the GNU GPL v2 or later.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "cpu.h"
|
||||
#include "hw/core/tcg-cpu-ops.h"
|
||||
#include "internals.h"
|
||||
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
|
||||
#include "hw/intc/armv7m_nvic.h"
|
||||
|
||||
static bool arm_v7m_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
|
||||
{
|
||||
CPUClass *cc = CPU_GET_CLASS(cs);
|
||||
ARMCPU *cpu = ARM_CPU(cs);
|
||||
CPUARMState *env = &cpu->env;
|
||||
bool ret = false;
|
||||
|
||||
/*
|
||||
* ARMv7-M interrupt masking works differently than -A or -R.
|
||||
* There is no FIQ/IRQ distinction. Instead of I and F bits
|
||||
* masking FIQ and IRQ interrupts, an exception is taken only
|
||||
* if it is higher priority than the current execution priority
|
||||
* (which depends on state like BASEPRI, FAULTMASK and the
|
||||
* currently active exception).
|
||||
*/
|
||||
if (interrupt_request & CPU_INTERRUPT_HARD
|
||||
&& (armv7m_nvic_can_take_pending_exception(env->nvic))) {
|
||||
cs->exception_index = EXCP_IRQ;
|
||||
cc->tcg_ops->do_interrupt(cs);
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* !CONFIG_USER_ONLY */
|
||||
|
||||
static void cortex_m0_initfn(Object *obj)
|
||||
{
|
||||
ARMCPU *cpu = ARM_CPU(obj);
|
||||
set_feature(&cpu->env, ARM_FEATURE_V6);
|
||||
set_feature(&cpu->env, ARM_FEATURE_M);
|
||||
|
||||
cpu->midr = 0x410cc200;
|
||||
|
||||
/*
|
||||
* These ID register values are not guest visible, because
|
||||
* we do not implement the Main Extension. They must be set
|
||||
* to values corresponding to the Cortex-M0's implemented
|
||||
* features, because QEMU generally controls its emulation
|
||||
* by looking at ID register fields. We use the same values as
|
||||
* for the M3.
|
||||
*/
|
||||
cpu->isar.id_pfr0 = 0x00000030;
|
||||
cpu->isar.id_pfr1 = 0x00000200;
|
||||
cpu->isar.id_dfr0 = 0x00100000;
|
||||
cpu->id_afr0 = 0x00000000;
|
||||
cpu->isar.id_mmfr0 = 0x00000030;
|
||||
cpu->isar.id_mmfr1 = 0x00000000;
|
||||
cpu->isar.id_mmfr2 = 0x00000000;
|
||||
cpu->isar.id_mmfr3 = 0x00000000;
|
||||
cpu->isar.id_isar0 = 0x01141110;
|
||||
cpu->isar.id_isar1 = 0x02111000;
|
||||
cpu->isar.id_isar2 = 0x21112231;
|
||||
cpu->isar.id_isar3 = 0x01111110;
|
||||
cpu->isar.id_isar4 = 0x01310102;
|
||||
cpu->isar.id_isar5 = 0x00000000;
|
||||
cpu->isar.id_isar6 = 0x00000000;
|
||||
}
|
||||
|
||||
static void cortex_m3_initfn(Object *obj)
|
||||
{
|
||||
ARMCPU *cpu = ARM_CPU(obj);
|
||||
set_feature(&cpu->env, ARM_FEATURE_V7);
|
||||
set_feature(&cpu->env, ARM_FEATURE_M);
|
||||
set_feature(&cpu->env, ARM_FEATURE_M_MAIN);
|
||||
cpu->midr = 0x410fc231;
|
||||
cpu->pmsav7_dregion = 8;
|
||||
cpu->isar.id_pfr0 = 0x00000030;
|
||||
cpu->isar.id_pfr1 = 0x00000200;
|
||||
cpu->isar.id_dfr0 = 0x00100000;
|
||||
cpu->id_afr0 = 0x00000000;
|
||||
cpu->isar.id_mmfr0 = 0x00000030;
|
||||
cpu->isar.id_mmfr1 = 0x00000000;
|
||||
cpu->isar.id_mmfr2 = 0x00000000;
|
||||
cpu->isar.id_mmfr3 = 0x00000000;
|
||||
cpu->isar.id_isar0 = 0x01141110;
|
||||
cpu->isar.id_isar1 = 0x02111000;
|
||||
cpu->isar.id_isar2 = 0x21112231;
|
||||
cpu->isar.id_isar3 = 0x01111110;
|
||||
cpu->isar.id_isar4 = 0x01310102;
|
||||
cpu->isar.id_isar5 = 0x00000000;
|
||||
cpu->isar.id_isar6 = 0x00000000;
|
||||
}
|
||||
|
||||
static void cortex_m4_initfn(Object *obj)
|
||||
{
|
||||
ARMCPU *cpu = ARM_CPU(obj);
|
||||
|
||||
set_feature(&cpu->env, ARM_FEATURE_V7);
|
||||
set_feature(&cpu->env, ARM_FEATURE_M);
|
||||
set_feature(&cpu->env, ARM_FEATURE_M_MAIN);
|
||||
set_feature(&cpu->env, ARM_FEATURE_THUMB_DSP);
|
||||
cpu->midr = 0x410fc240; /* r0p0 */
|
||||
cpu->pmsav7_dregion = 8;
|
||||
cpu->isar.mvfr0 = 0x10110021;
|
||||
cpu->isar.mvfr1 = 0x11000011;
|
||||
cpu->isar.mvfr2 = 0x00000000;
|
||||
cpu->isar.id_pfr0 = 0x00000030;
|
||||
cpu->isar.id_pfr1 = 0x00000200;
|
||||
cpu->isar.id_dfr0 = 0x00100000;
|
||||
cpu->id_afr0 = 0x00000000;
|
||||
cpu->isar.id_mmfr0 = 0x00000030;
|
||||
cpu->isar.id_mmfr1 = 0x00000000;
|
||||
cpu->isar.id_mmfr2 = 0x00000000;
|
||||
cpu->isar.id_mmfr3 = 0x00000000;
|
||||
cpu->isar.id_isar0 = 0x01141110;
|
||||
cpu->isar.id_isar1 = 0x02111000;
|
||||
cpu->isar.id_isar2 = 0x21112231;
|
||||
cpu->isar.id_isar3 = 0x01111110;
|
||||
cpu->isar.id_isar4 = 0x01310102;
|
||||
cpu->isar.id_isar5 = 0x00000000;
|
||||
cpu->isar.id_isar6 = 0x00000000;
|
||||
}
|
||||
|
||||
static void cortex_m7_initfn(Object *obj)
|
||||
{
|
||||
ARMCPU *cpu = ARM_CPU(obj);
|
||||
|
||||
set_feature(&cpu->env, ARM_FEATURE_V7);
|
||||
set_feature(&cpu->env, ARM_FEATURE_M);
|
||||
set_feature(&cpu->env, ARM_FEATURE_M_MAIN);
|
||||
set_feature(&cpu->env, ARM_FEATURE_THUMB_DSP);
|
||||
cpu->midr = 0x411fc272; /* r1p2 */
|
||||
cpu->pmsav7_dregion = 8;
|
||||
cpu->isar.mvfr0 = 0x10110221;
|
||||
cpu->isar.mvfr1 = 0x12000011;
|
||||
cpu->isar.mvfr2 = 0x00000040;
|
||||
cpu->isar.id_pfr0 = 0x00000030;
|
||||
cpu->isar.id_pfr1 = 0x00000200;
|
||||
cpu->isar.id_dfr0 = 0x00100000;
|
||||
cpu->id_afr0 = 0x00000000;
|
||||
cpu->isar.id_mmfr0 = 0x00100030;
|
||||
cpu->isar.id_mmfr1 = 0x00000000;
|
||||
cpu->isar.id_mmfr2 = 0x01000000;
|
||||
cpu->isar.id_mmfr3 = 0x00000000;
|
||||
cpu->isar.id_isar0 = 0x01101110;
|
||||
cpu->isar.id_isar1 = 0x02112000;
|
||||
cpu->isar.id_isar2 = 0x20232231;
|
||||
cpu->isar.id_isar3 = 0x01111131;
|
||||
cpu->isar.id_isar4 = 0x01310132;
|
||||
cpu->isar.id_isar5 = 0x00000000;
|
||||
cpu->isar.id_isar6 = 0x00000000;
|
||||
}
|
||||
|
||||
static void cortex_m33_initfn(Object *obj)
|
||||
{
|
||||
ARMCPU *cpu = ARM_CPU(obj);
|
||||
|
||||
set_feature(&cpu->env, ARM_FEATURE_V8);
|
||||
set_feature(&cpu->env, ARM_FEATURE_M);
|
||||
set_feature(&cpu->env, ARM_FEATURE_M_MAIN);
|
||||
set_feature(&cpu->env, ARM_FEATURE_M_SECURITY);
|
||||
set_feature(&cpu->env, ARM_FEATURE_THUMB_DSP);
|
||||
cpu->midr = 0x410fd213; /* r0p3 */
|
||||
cpu->pmsav7_dregion = 16;
|
||||
cpu->sau_sregion = 8;
|
||||
cpu->isar.mvfr0 = 0x10110021;
|
||||
cpu->isar.mvfr1 = 0x11000011;
|
||||
cpu->isar.mvfr2 = 0x00000040;
|
||||
cpu->isar.id_pfr0 = 0x00000030;
|
||||
cpu->isar.id_pfr1 = 0x00000210;
|
||||
cpu->isar.id_dfr0 = 0x00200000;
|
||||
cpu->id_afr0 = 0x00000000;
|
||||
cpu->isar.id_mmfr0 = 0x00101F40;
|
||||
cpu->isar.id_mmfr1 = 0x00000000;
|
||||
cpu->isar.id_mmfr2 = 0x01000000;
|
||||
cpu->isar.id_mmfr3 = 0x00000000;
|
||||
cpu->isar.id_isar0 = 0x01101110;
|
||||
cpu->isar.id_isar1 = 0x02212000;
|
||||
cpu->isar.id_isar2 = 0x20232232;
|
||||
cpu->isar.id_isar3 = 0x01111131;
|
||||
cpu->isar.id_isar4 = 0x01310132;
|
||||
cpu->isar.id_isar5 = 0x00000000;
|
||||
cpu->isar.id_isar6 = 0x00000000;
|
||||
cpu->clidr = 0x00000000;
|
||||
cpu->ctr = 0x8000c000;
|
||||
}
|
||||
|
||||
static void cortex_m55_initfn(Object *obj)
|
||||
{
|
||||
ARMCPU *cpu = ARM_CPU(obj);
|
||||
|
||||
set_feature(&cpu->env, ARM_FEATURE_V8);
|
||||
set_feature(&cpu->env, ARM_FEATURE_V8_1M);
|
||||
set_feature(&cpu->env, ARM_FEATURE_M);
|
||||
set_feature(&cpu->env, ARM_FEATURE_M_MAIN);
|
||||
set_feature(&cpu->env, ARM_FEATURE_M_SECURITY);
|
||||
set_feature(&cpu->env, ARM_FEATURE_THUMB_DSP);
|
||||
cpu->midr = 0x410fd221; /* r0p1 */
|
||||
cpu->revidr = 0;
|
||||
cpu->pmsav7_dregion = 16;
|
||||
cpu->sau_sregion = 8;
|
||||
/* These are the MVFR* values for the FPU + full MVE configuration */
|
||||
cpu->isar.mvfr0 = 0x10110221;
|
||||
cpu->isar.mvfr1 = 0x12100211;
|
||||
cpu->isar.mvfr2 = 0x00000040;
|
||||
cpu->isar.id_pfr0 = 0x20000030;
|
||||
cpu->isar.id_pfr1 = 0x00000230;
|
||||
cpu->isar.id_dfr0 = 0x10200000;
|
||||
cpu->id_afr0 = 0x00000000;
|
||||
cpu->isar.id_mmfr0 = 0x00111040;
|
||||
cpu->isar.id_mmfr1 = 0x00000000;
|
||||
cpu->isar.id_mmfr2 = 0x01000000;
|
||||
cpu->isar.id_mmfr3 = 0x00000011;
|
||||
cpu->isar.id_isar0 = 0x01103110;
|
||||
cpu->isar.id_isar1 = 0x02212000;
|
||||
cpu->isar.id_isar2 = 0x20232232;
|
||||
cpu->isar.id_isar3 = 0x01111131;
|
||||
cpu->isar.id_isar4 = 0x01310132;
|
||||
cpu->isar.id_isar5 = 0x00000000;
|
||||
cpu->isar.id_isar6 = 0x00000000;
|
||||
cpu->clidr = 0x00000000; /* caches not implemented */
|
||||
cpu->ctr = 0x8303c003;
|
||||
}
|
||||
|
||||
static const TCGCPUOps arm_v7m_tcg_ops = {
|
||||
.initialize = arm_translate_init,
|
||||
.synchronize_from_tb = arm_cpu_synchronize_from_tb,
|
||||
.debug_excp_handler = arm_debug_excp_handler,
|
||||
.restore_state_to_opc = arm_restore_state_to_opc,
|
||||
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
.record_sigsegv = arm_cpu_record_sigsegv,
|
||||
.record_sigbus = arm_cpu_record_sigbus,
|
||||
#else
|
||||
.tlb_fill = arm_cpu_tlb_fill,
|
||||
.cpu_exec_interrupt = arm_v7m_cpu_exec_interrupt,
|
||||
.do_interrupt = arm_v7m_cpu_do_interrupt,
|
||||
.do_transaction_failed = arm_cpu_do_transaction_failed,
|
||||
.do_unaligned_access = arm_cpu_do_unaligned_access,
|
||||
.adjust_watchpoint_address = arm_adjust_watchpoint_address,
|
||||
.debug_check_watchpoint = arm_debug_check_watchpoint,
|
||||
.debug_check_breakpoint = arm_debug_check_breakpoint,
|
||||
#endif /* !CONFIG_USER_ONLY */
|
||||
};
|
||||
|
||||
static void arm_v7m_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
ARMCPUClass *acc = ARM_CPU_CLASS(oc);
|
||||
CPUClass *cc = CPU_CLASS(oc);
|
||||
|
||||
acc->info = data;
|
||||
cc->tcg_ops = &arm_v7m_tcg_ops;
|
||||
cc->gdb_core_xml_file = "arm-m-profile.xml";
|
||||
}
|
||||
|
||||
static const ARMCPUInfo arm_v7m_cpus[] = {
|
||||
{ .name = "cortex-m0", .initfn = cortex_m0_initfn,
|
||||
.class_init = arm_v7m_class_init },
|
||||
{ .name = "cortex-m3", .initfn = cortex_m3_initfn,
|
||||
.class_init = arm_v7m_class_init },
|
||||
{ .name = "cortex-m4", .initfn = cortex_m4_initfn,
|
||||
.class_init = arm_v7m_class_init },
|
||||
{ .name = "cortex-m7", .initfn = cortex_m7_initfn,
|
||||
.class_init = arm_v7m_class_init },
|
||||
{ .name = "cortex-m33", .initfn = cortex_m33_initfn,
|
||||
.class_init = arm_v7m_class_init },
|
||||
{ .name = "cortex-m55", .initfn = cortex_m55_initfn,
|
||||
.class_init = arm_v7m_class_init },
|
||||
};
|
||||
|
||||
static void arm_v7m_cpu_register_types(void)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(arm_v7m_cpus); ++i) {
|
||||
arm_cpu_register(&arm_v7m_cpus[i]);
|
||||
}
|
||||
}
|
||||
|
||||
type_init(arm_v7m_cpu_register_types)
|
|
@ -17,9 +17,6 @@
|
|||
#include "hw/boards.h"
|
||||
#endif
|
||||
#include "cpregs.h"
|
||||
#if !defined(CONFIG_USER_ONLY) && defined(CONFIG_TCG)
|
||||
#include "hw/intc/armv7m_nvic.h"
|
||||
#endif
|
||||
|
||||
|
||||
/* Share AArch32 -cpu max features with AArch64. */
|
||||
|
@ -98,32 +95,6 @@ void aa32_max_features(ARMCPU *cpu)
|
|||
/* CPU models. These are not needed for the AArch64 linux-user build. */
|
||||
#if !defined(CONFIG_USER_ONLY) || !defined(TARGET_AARCH64)
|
||||
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
static bool arm_v7m_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
|
||||
{
|
||||
CPUClass *cc = CPU_GET_CLASS(cs);
|
||||
ARMCPU *cpu = ARM_CPU(cs);
|
||||
CPUARMState *env = &cpu->env;
|
||||
bool ret = false;
|
||||
|
||||
/*
|
||||
* ARMv7-M interrupt masking works differently than -A or -R.
|
||||
* There is no FIQ/IRQ distinction. Instead of I and F bits
|
||||
* masking FIQ and IRQ interrupts, an exception is taken only
|
||||
* if it is higher priority than the current execution priority
|
||||
* (which depends on state like BASEPRI, FAULTMASK and the
|
||||
* currently active exception).
|
||||
*/
|
||||
if (interrupt_request & CPU_INTERRUPT_HARD
|
||||
&& (armv7m_nvic_can_take_pending_exception(env->nvic))) {
|
||||
cs->exception_index = EXCP_IRQ;
|
||||
cc->tcg_ops->do_interrupt(cs);
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif /* !CONFIG_USER_ONLY */
|
||||
|
||||
static void arm926_initfn(Object *obj)
|
||||
{
|
||||
ARMCPU *cpu = ARM_CPU(obj);
|
||||
|
@ -571,195 +542,6 @@ static void cortex_a15_initfn(Object *obj)
|
|||
define_arm_cp_regs(cpu, cortexa15_cp_reginfo);
|
||||
}
|
||||
|
||||
static void cortex_m0_initfn(Object *obj)
|
||||
{
|
||||
ARMCPU *cpu = ARM_CPU(obj);
|
||||
set_feature(&cpu->env, ARM_FEATURE_V6);
|
||||
set_feature(&cpu->env, ARM_FEATURE_M);
|
||||
|
||||
cpu->midr = 0x410cc200;
|
||||
|
||||
/*
|
||||
* These ID register values are not guest visible, because
|
||||
* we do not implement the Main Extension. They must be set
|
||||
* to values corresponding to the Cortex-M0's implemented
|
||||
* features, because QEMU generally controls its emulation
|
||||
* by looking at ID register fields. We use the same values as
|
||||
* for the M3.
|
||||
*/
|
||||
cpu->isar.id_pfr0 = 0x00000030;
|
||||
cpu->isar.id_pfr1 = 0x00000200;
|
||||
cpu->isar.id_dfr0 = 0x00100000;
|
||||
cpu->id_afr0 = 0x00000000;
|
||||
cpu->isar.id_mmfr0 = 0x00000030;
|
||||
cpu->isar.id_mmfr1 = 0x00000000;
|
||||
cpu->isar.id_mmfr2 = 0x00000000;
|
||||
cpu->isar.id_mmfr3 = 0x00000000;
|
||||
cpu->isar.id_isar0 = 0x01141110;
|
||||
cpu->isar.id_isar1 = 0x02111000;
|
||||
cpu->isar.id_isar2 = 0x21112231;
|
||||
cpu->isar.id_isar3 = 0x01111110;
|
||||
cpu->isar.id_isar4 = 0x01310102;
|
||||
cpu->isar.id_isar5 = 0x00000000;
|
||||
cpu->isar.id_isar6 = 0x00000000;
|
||||
}
|
||||
|
||||
static void cortex_m3_initfn(Object *obj)
|
||||
{
|
||||
ARMCPU *cpu = ARM_CPU(obj);
|
||||
set_feature(&cpu->env, ARM_FEATURE_V7);
|
||||
set_feature(&cpu->env, ARM_FEATURE_M);
|
||||
set_feature(&cpu->env, ARM_FEATURE_M_MAIN);
|
||||
cpu->midr = 0x410fc231;
|
||||
cpu->pmsav7_dregion = 8;
|
||||
cpu->isar.id_pfr0 = 0x00000030;
|
||||
cpu->isar.id_pfr1 = 0x00000200;
|
||||
cpu->isar.id_dfr0 = 0x00100000;
|
||||
cpu->id_afr0 = 0x00000000;
|
||||
cpu->isar.id_mmfr0 = 0x00000030;
|
||||
cpu->isar.id_mmfr1 = 0x00000000;
|
||||
cpu->isar.id_mmfr2 = 0x00000000;
|
||||
cpu->isar.id_mmfr3 = 0x00000000;
|
||||
cpu->isar.id_isar0 = 0x01141110;
|
||||
cpu->isar.id_isar1 = 0x02111000;
|
||||
cpu->isar.id_isar2 = 0x21112231;
|
||||
cpu->isar.id_isar3 = 0x01111110;
|
||||
cpu->isar.id_isar4 = 0x01310102;
|
||||
cpu->isar.id_isar5 = 0x00000000;
|
||||
cpu->isar.id_isar6 = 0x00000000;
|
||||
}
|
||||
|
||||
static void cortex_m4_initfn(Object *obj)
|
||||
{
|
||||
ARMCPU *cpu = ARM_CPU(obj);
|
||||
|
||||
set_feature(&cpu->env, ARM_FEATURE_V7);
|
||||
set_feature(&cpu->env, ARM_FEATURE_M);
|
||||
set_feature(&cpu->env, ARM_FEATURE_M_MAIN);
|
||||
set_feature(&cpu->env, ARM_FEATURE_THUMB_DSP);
|
||||
cpu->midr = 0x410fc240; /* r0p0 */
|
||||
cpu->pmsav7_dregion = 8;
|
||||
cpu->isar.mvfr0 = 0x10110021;
|
||||
cpu->isar.mvfr1 = 0x11000011;
|
||||
cpu->isar.mvfr2 = 0x00000000;
|
||||
cpu->isar.id_pfr0 = 0x00000030;
|
||||
cpu->isar.id_pfr1 = 0x00000200;
|
||||
cpu->isar.id_dfr0 = 0x00100000;
|
||||
cpu->id_afr0 = 0x00000000;
|
||||
cpu->isar.id_mmfr0 = 0x00000030;
|
||||
cpu->isar.id_mmfr1 = 0x00000000;
|
||||
cpu->isar.id_mmfr2 = 0x00000000;
|
||||
cpu->isar.id_mmfr3 = 0x00000000;
|
||||
cpu->isar.id_isar0 = 0x01141110;
|
||||
cpu->isar.id_isar1 = 0x02111000;
|
||||
cpu->isar.id_isar2 = 0x21112231;
|
||||
cpu->isar.id_isar3 = 0x01111110;
|
||||
cpu->isar.id_isar4 = 0x01310102;
|
||||
cpu->isar.id_isar5 = 0x00000000;
|
||||
cpu->isar.id_isar6 = 0x00000000;
|
||||
}
|
||||
|
||||
static void cortex_m7_initfn(Object *obj)
|
||||
{
|
||||
ARMCPU *cpu = ARM_CPU(obj);
|
||||
|
||||
set_feature(&cpu->env, ARM_FEATURE_V7);
|
||||
set_feature(&cpu->env, ARM_FEATURE_M);
|
||||
set_feature(&cpu->env, ARM_FEATURE_M_MAIN);
|
||||
set_feature(&cpu->env, ARM_FEATURE_THUMB_DSP);
|
||||
cpu->midr = 0x411fc272; /* r1p2 */
|
||||
cpu->pmsav7_dregion = 8;
|
||||
cpu->isar.mvfr0 = 0x10110221;
|
||||
cpu->isar.mvfr1 = 0x12000011;
|
||||
cpu->isar.mvfr2 = 0x00000040;
|
||||
cpu->isar.id_pfr0 = 0x00000030;
|
||||
cpu->isar.id_pfr1 = 0x00000200;
|
||||
cpu->isar.id_dfr0 = 0x00100000;
|
||||
cpu->id_afr0 = 0x00000000;
|
||||
cpu->isar.id_mmfr0 = 0x00100030;
|
||||
cpu->isar.id_mmfr1 = 0x00000000;
|
||||
cpu->isar.id_mmfr2 = 0x01000000;
|
||||
cpu->isar.id_mmfr3 = 0x00000000;
|
||||
cpu->isar.id_isar0 = 0x01101110;
|
||||
cpu->isar.id_isar1 = 0x02112000;
|
||||
cpu->isar.id_isar2 = 0x20232231;
|
||||
cpu->isar.id_isar3 = 0x01111131;
|
||||
cpu->isar.id_isar4 = 0x01310132;
|
||||
cpu->isar.id_isar5 = 0x00000000;
|
||||
cpu->isar.id_isar6 = 0x00000000;
|
||||
}
|
||||
|
||||
static void cortex_m33_initfn(Object *obj)
|
||||
{
|
||||
ARMCPU *cpu = ARM_CPU(obj);
|
||||
|
||||
set_feature(&cpu->env, ARM_FEATURE_V8);
|
||||
set_feature(&cpu->env, ARM_FEATURE_M);
|
||||
set_feature(&cpu->env, ARM_FEATURE_M_MAIN);
|
||||
set_feature(&cpu->env, ARM_FEATURE_M_SECURITY);
|
||||
set_feature(&cpu->env, ARM_FEATURE_THUMB_DSP);
|
||||
cpu->midr = 0x410fd213; /* r0p3 */
|
||||
cpu->pmsav7_dregion = 16;
|
||||
cpu->sau_sregion = 8;
|
||||
cpu->isar.mvfr0 = 0x10110021;
|
||||
cpu->isar.mvfr1 = 0x11000011;
|
||||
cpu->isar.mvfr2 = 0x00000040;
|
||||
cpu->isar.id_pfr0 = 0x00000030;
|
||||
cpu->isar.id_pfr1 = 0x00000210;
|
||||
cpu->isar.id_dfr0 = 0x00200000;
|
||||
cpu->id_afr0 = 0x00000000;
|
||||
cpu->isar.id_mmfr0 = 0x00101F40;
|
||||
cpu->isar.id_mmfr1 = 0x00000000;
|
||||
cpu->isar.id_mmfr2 = 0x01000000;
|
||||
cpu->isar.id_mmfr3 = 0x00000000;
|
||||
cpu->isar.id_isar0 = 0x01101110;
|
||||
cpu->isar.id_isar1 = 0x02212000;
|
||||
cpu->isar.id_isar2 = 0x20232232;
|
||||
cpu->isar.id_isar3 = 0x01111131;
|
||||
cpu->isar.id_isar4 = 0x01310132;
|
||||
cpu->isar.id_isar5 = 0x00000000;
|
||||
cpu->isar.id_isar6 = 0x00000000;
|
||||
cpu->clidr = 0x00000000;
|
||||
cpu->ctr = 0x8000c000;
|
||||
}
|
||||
|
||||
static void cortex_m55_initfn(Object *obj)
|
||||
{
|
||||
ARMCPU *cpu = ARM_CPU(obj);
|
||||
|
||||
set_feature(&cpu->env, ARM_FEATURE_V8);
|
||||
set_feature(&cpu->env, ARM_FEATURE_V8_1M);
|
||||
set_feature(&cpu->env, ARM_FEATURE_M);
|
||||
set_feature(&cpu->env, ARM_FEATURE_M_MAIN);
|
||||
set_feature(&cpu->env, ARM_FEATURE_M_SECURITY);
|
||||
set_feature(&cpu->env, ARM_FEATURE_THUMB_DSP);
|
||||
cpu->midr = 0x410fd221; /* r0p1 */
|
||||
cpu->revidr = 0;
|
||||
cpu->pmsav7_dregion = 16;
|
||||
cpu->sau_sregion = 8;
|
||||
/* These are the MVFR* values for the FPU + full MVE configuration */
|
||||
cpu->isar.mvfr0 = 0x10110221;
|
||||
cpu->isar.mvfr1 = 0x12100211;
|
||||
cpu->isar.mvfr2 = 0x00000040;
|
||||
cpu->isar.id_pfr0 = 0x20000030;
|
||||
cpu->isar.id_pfr1 = 0x00000230;
|
||||
cpu->isar.id_dfr0 = 0x10200000;
|
||||
cpu->id_afr0 = 0x00000000;
|
||||
cpu->isar.id_mmfr0 = 0x00111040;
|
||||
cpu->isar.id_mmfr1 = 0x00000000;
|
||||
cpu->isar.id_mmfr2 = 0x01000000;
|
||||
cpu->isar.id_mmfr3 = 0x00000011;
|
||||
cpu->isar.id_isar0 = 0x01103110;
|
||||
cpu->isar.id_isar1 = 0x02212000;
|
||||
cpu->isar.id_isar2 = 0x20232232;
|
||||
cpu->isar.id_isar3 = 0x01111131;
|
||||
cpu->isar.id_isar4 = 0x01310132;
|
||||
cpu->isar.id_isar5 = 0x00000000;
|
||||
cpu->isar.id_isar6 = 0x00000000;
|
||||
cpu->clidr = 0x00000000; /* caches not implemented */
|
||||
cpu->ctr = 0x8303c003;
|
||||
}
|
||||
|
||||
static const ARMCPRegInfo cortexr5_cp_reginfo[] = {
|
||||
/* Dummy the TCM region regs for the moment */
|
||||
{ .name = "ATCM", .cp = 15, .opc1 = 0, .crn = 9, .crm = 1, .opc2 = 0,
|
||||
|
@ -1127,37 +909,6 @@ static void pxa270c5_initfn(Object *obj)
|
|||
cpu->reset_sctlr = 0x00000078;
|
||||
}
|
||||
|
||||
static const TCGCPUOps arm_v7m_tcg_ops = {
|
||||
.initialize = arm_translate_init,
|
||||
.synchronize_from_tb = arm_cpu_synchronize_from_tb,
|
||||
.debug_excp_handler = arm_debug_excp_handler,
|
||||
.restore_state_to_opc = arm_restore_state_to_opc,
|
||||
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
.record_sigsegv = arm_cpu_record_sigsegv,
|
||||
.record_sigbus = arm_cpu_record_sigbus,
|
||||
#else
|
||||
.tlb_fill = arm_cpu_tlb_fill,
|
||||
.cpu_exec_interrupt = arm_v7m_cpu_exec_interrupt,
|
||||
.do_interrupt = arm_v7m_cpu_do_interrupt,
|
||||
.do_transaction_failed = arm_cpu_do_transaction_failed,
|
||||
.do_unaligned_access = arm_cpu_do_unaligned_access,
|
||||
.adjust_watchpoint_address = arm_adjust_watchpoint_address,
|
||||
.debug_check_watchpoint = arm_debug_check_watchpoint,
|
||||
.debug_check_breakpoint = arm_debug_check_breakpoint,
|
||||
#endif /* !CONFIG_USER_ONLY */
|
||||
};
|
||||
|
||||
static void arm_v7m_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
ARMCPUClass *acc = ARM_CPU_CLASS(oc);
|
||||
CPUClass *cc = CPU_CLASS(oc);
|
||||
|
||||
acc->info = data;
|
||||
cc->tcg_ops = &arm_v7m_tcg_ops;
|
||||
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.
|
||||
|
@ -1240,18 +991,6 @@ static const ARMCPUInfo arm_tcg_cpus[] = {
|
|||
{ .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,
|
||||
.class_init = arm_v7m_class_init },
|
||||
{ .name = "cortex-m4", .initfn = cortex_m4_initfn,
|
||||
.class_init = arm_v7m_class_init },
|
||||
{ .name = "cortex-m7", .initfn = cortex_m7_initfn,
|
||||
.class_init = arm_v7m_class_init },
|
||||
{ .name = "cortex-m33", .initfn = cortex_m33_initfn,
|
||||
.class_init = arm_v7m_class_init },
|
||||
{ .name = "cortex-m55", .initfn = cortex_m55_initfn,
|
||||
.class_init = arm_v7m_class_init },
|
||||
{ .name = "cortex-r5", .initfn = cortex_r5_initfn },
|
||||
{ .name = "cortex-r5f", .initfn = cortex_r5f_initfn },
|
||||
{ .name = "cortex-r52", .initfn = cortex_r52_initfn },
|
||||
|
|
|
@ -1184,6 +1184,7 @@ void aarch64_max_tcg_initfn(Object *obj)
|
|||
t = FIELD_DP64(t, ID_AA64MMFR0, TGRAN64_2, 2); /* 64k stage2 supported */
|
||||
t = FIELD_DP64(t, ID_AA64MMFR0, TGRAN4_2, 2); /* 4k stage2 supported */
|
||||
t = FIELD_DP64(t, ID_AA64MMFR0, FGT, 1); /* FEAT_FGT */
|
||||
t = FIELD_DP64(t, ID_AA64MMFR0, ECV, 2); /* FEAT_ECV */
|
||||
cpu->isar.id_aa64mmfr0 = t;
|
||||
|
||||
t = cpu->isar.id_aa64mmfr1;
|
||||
|
|
|
@ -55,3 +55,6 @@ arm_ss.add(when: 'TARGET_AARCH64', if_true: files(
|
|||
arm_system_ss.add(files(
|
||||
'psci.c',
|
||||
))
|
||||
|
||||
arm_system_ss.add(when: 'CONFIG_ARM_V7M', if_true: files('cpu-v7m.c'))
|
||||
arm_user_ss.add(when: 'TARGET_AARCH64', if_false: files('cpu-v7m.c'))
|
||||
|
|
|
@ -1083,11 +1083,32 @@ void HELPER(sme_bfmopa)(void *vza, void *vzn, void *vzm, void *vpn,
|
|||
}
|
||||
}
|
||||
|
||||
typedef uint64_t IMOPFn(uint64_t, uint64_t, uint64_t, uint8_t, bool);
|
||||
typedef uint32_t IMOPFn32(uint32_t, uint32_t, uint32_t, uint8_t, bool);
|
||||
static inline void do_imopa_s(uint32_t *za, uint32_t *zn, uint32_t *zm,
|
||||
uint8_t *pn, uint8_t *pm,
|
||||
uint32_t desc, IMOPFn32 *fn)
|
||||
{
|
||||
intptr_t row, col, oprsz = simd_oprsz(desc) / 4;
|
||||
bool neg = simd_data(desc);
|
||||
|
||||
static inline void do_imopa(uint64_t *za, uint64_t *zn, uint64_t *zm,
|
||||
uint8_t *pn, uint8_t *pm,
|
||||
uint32_t desc, IMOPFn *fn)
|
||||
for (row = 0; row < oprsz; ++row) {
|
||||
uint8_t pa = (pn[H1(row >> 1)] >> ((row & 1) * 4)) & 0xf;
|
||||
uint32_t *za_row = &za[tile_vslice_index(row)];
|
||||
uint32_t n = zn[H4(row)];
|
||||
|
||||
for (col = 0; col < oprsz; ++col) {
|
||||
uint8_t pb = pm[H1(col >> 1)] >> ((col & 1) * 4);
|
||||
uint32_t *a = &za_row[H4(col)];
|
||||
|
||||
*a = fn(n, zm[H4(col)], *a, pa & pb, neg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typedef uint64_t IMOPFn64(uint64_t, uint64_t, uint64_t, uint8_t, bool);
|
||||
static inline void do_imopa_d(uint64_t *za, uint64_t *zn, uint64_t *zm,
|
||||
uint8_t *pn, uint8_t *pm,
|
||||
uint32_t desc, IMOPFn64 *fn)
|
||||
{
|
||||
intptr_t row, col, oprsz = simd_oprsz(desc) / 8;
|
||||
bool neg = simd_data(desc);
|
||||
|
@ -1107,25 +1128,16 @@ static inline void do_imopa(uint64_t *za, uint64_t *zn, uint64_t *zm,
|
|||
}
|
||||
|
||||
#define DEF_IMOP_32(NAME, NTYPE, MTYPE) \
|
||||
static uint64_t NAME(uint64_t n, uint64_t m, uint64_t a, uint8_t p, bool neg) \
|
||||
static uint32_t NAME(uint32_t n, uint32_t m, uint32_t a, uint8_t p, bool neg) \
|
||||
{ \
|
||||
uint32_t sum0 = 0, sum1 = 0; \
|
||||
uint32_t sum = 0; \
|
||||
/* Apply P to N as a mask, making the inactive elements 0. */ \
|
||||
n &= expand_pred_b(p); \
|
||||
sum0 += (NTYPE)(n >> 0) * (MTYPE)(m >> 0); \
|
||||
sum0 += (NTYPE)(n >> 8) * (MTYPE)(m >> 8); \
|
||||
sum0 += (NTYPE)(n >> 16) * (MTYPE)(m >> 16); \
|
||||
sum0 += (NTYPE)(n >> 24) * (MTYPE)(m >> 24); \
|
||||
sum1 += (NTYPE)(n >> 32) * (MTYPE)(m >> 32); \
|
||||
sum1 += (NTYPE)(n >> 40) * (MTYPE)(m >> 40); \
|
||||
sum1 += (NTYPE)(n >> 48) * (MTYPE)(m >> 48); \
|
||||
sum1 += (NTYPE)(n >> 56) * (MTYPE)(m >> 56); \
|
||||
if (neg) { \
|
||||
sum0 = (uint32_t)a - sum0, sum1 = (uint32_t)(a >> 32) - sum1; \
|
||||
} else { \
|
||||
sum0 = (uint32_t)a + sum0, sum1 = (uint32_t)(a >> 32) + sum1; \
|
||||
} \
|
||||
return ((uint64_t)sum1 << 32) | sum0; \
|
||||
sum += (NTYPE)(n >> 0) * (MTYPE)(m >> 0); \
|
||||
sum += (NTYPE)(n >> 8) * (MTYPE)(m >> 8); \
|
||||
sum += (NTYPE)(n >> 16) * (MTYPE)(m >> 16); \
|
||||
sum += (NTYPE)(n >> 24) * (MTYPE)(m >> 24); \
|
||||
return neg ? a - sum : a + sum; \
|
||||
}
|
||||
|
||||
#define DEF_IMOP_64(NAME, NTYPE, MTYPE) \
|
||||
|
@ -1151,16 +1163,17 @@ DEF_IMOP_64(umopa_d, uint16_t, uint16_t)
|
|||
DEF_IMOP_64(sumopa_d, int16_t, uint16_t)
|
||||
DEF_IMOP_64(usmopa_d, uint16_t, int16_t)
|
||||
|
||||
#define DEF_IMOPH(NAME) \
|
||||
void HELPER(sme_##NAME)(void *vza, void *vzn, void *vzm, void *vpn, \
|
||||
void *vpm, uint32_t desc) \
|
||||
{ do_imopa(vza, vzn, vzm, vpn, vpm, desc, NAME); }
|
||||
#define DEF_IMOPH(NAME, S) \
|
||||
void HELPER(sme_##NAME##_##S)(void *vza, void *vzn, void *vzm, \
|
||||
void *vpn, void *vpm, uint32_t desc) \
|
||||
{ do_imopa_##S(vza, vzn, vzm, vpn, vpm, desc, NAME##_##S); }
|
||||
|
||||
DEF_IMOPH(smopa_s)
|
||||
DEF_IMOPH(umopa_s)
|
||||
DEF_IMOPH(sumopa_s)
|
||||
DEF_IMOPH(usmopa_s)
|
||||
DEF_IMOPH(smopa_d)
|
||||
DEF_IMOPH(umopa_d)
|
||||
DEF_IMOPH(sumopa_d)
|
||||
DEF_IMOPH(usmopa_d)
|
||||
DEF_IMOPH(smopa, s)
|
||||
DEF_IMOPH(umopa, s)
|
||||
DEF_IMOPH(sumopa, s)
|
||||
DEF_IMOPH(usmopa, s)
|
||||
|
||||
DEF_IMOPH(smopa, d)
|
||||
DEF_IMOPH(umopa, d)
|
||||
DEF_IMOPH(sumopa, d)
|
||||
DEF_IMOPH(usmopa, d)
|
||||
|
|
|
@ -8,6 +8,7 @@ arm_gt_tval_write(int timer, uint64_t value) "gt_tval_write: timer %d value 0x%"
|
|||
arm_gt_ctl_write(int timer, uint64_t value) "gt_ctl_write: timer %d value 0x%" PRIx64
|
||||
arm_gt_imask_toggle(int timer) "gt_ctl_write: timer %d IMASK toggle"
|
||||
arm_gt_cntvoff_write(uint64_t value) "gt_cntvoff_write: value 0x%" PRIx64
|
||||
arm_gt_cntpoff_write(uint64_t value) "gt_cntpoff_write: value 0x%" PRIx64
|
||||
arm_gt_update_irq(int timer, int irqstate) "gt_update_irq: timer %d irqstate %d"
|
||||
|
||||
# kvm.c
|
||||
|
|
|
@ -204,7 +204,8 @@ qtests_aspeed = \
|
|||
qtests_stm32l4x5 = \
|
||||
['stm32l4x5_exti-test',
|
||||
'stm32l4x5_syscfg-test',
|
||||
'stm32l4x5_rcc-test']
|
||||
'stm32l4x5_rcc-test',
|
||||
'stm32l4x5_gpio-test']
|
||||
|
||||
qtests_arm = \
|
||||
(config_all_devices.has_key('CONFIG_MPS2') ? ['sse-timer-test'] : []) + \
|
||||
|
|
|
@ -0,0 +1,551 @@
|
|||
/*
|
||||
* QTest testcase for STM32L4x5_GPIO
|
||||
*
|
||||
* Copyright (c) 2024 Arnaud Minier <arnaud.minier@telecom-paris.fr>
|
||||
* Copyright (c) 2024 Inès Varhol <ines.varhol@telecom-paris.fr>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "libqtest-single.h"
|
||||
|
||||
#define GPIO_BASE_ADDR 0x48000000
|
||||
#define GPIO_SIZE 0x400
|
||||
#define NUM_GPIOS 8
|
||||
#define NUM_GPIO_PINS 16
|
||||
|
||||
#define GPIO_A 0x48000000
|
||||
#define GPIO_B 0x48000400
|
||||
#define GPIO_C 0x48000800
|
||||
#define GPIO_D 0x48000C00
|
||||
#define GPIO_E 0x48001000
|
||||
#define GPIO_F 0x48001400
|
||||
#define GPIO_G 0x48001800
|
||||
#define GPIO_H 0x48001C00
|
||||
|
||||
#define MODER 0x00
|
||||
#define OTYPER 0x04
|
||||
#define PUPDR 0x0C
|
||||
#define IDR 0x10
|
||||
#define ODR 0x14
|
||||
#define BSRR 0x18
|
||||
#define BRR 0x28
|
||||
|
||||
#define MODER_INPUT 0
|
||||
#define MODER_OUTPUT 1
|
||||
|
||||
#define PUPDR_NONE 0
|
||||
#define PUPDR_PULLUP 1
|
||||
#define PUPDR_PULLDOWN 2
|
||||
|
||||
#define OTYPER_PUSH_PULL 0
|
||||
#define OTYPER_OPEN_DRAIN 1
|
||||
|
||||
const uint32_t moder_reset[NUM_GPIOS] = {
|
||||
0xABFFFFFF,
|
||||
0xFFFFFEBF,
|
||||
0xFFFFFFFF,
|
||||
0xFFFFFFFF,
|
||||
0xFFFFFFFF,
|
||||
0xFFFFFFFF,
|
||||
0xFFFFFFFF,
|
||||
0x0000000F
|
||||
};
|
||||
|
||||
const uint32_t pupdr_reset[NUM_GPIOS] = {
|
||||
0x64000000,
|
||||
0x00000100,
|
||||
0x00000000,
|
||||
0x00000000,
|
||||
0x00000000,
|
||||
0x00000000,
|
||||
0x00000000,
|
||||
0x00000000
|
||||
};
|
||||
|
||||
const uint32_t idr_reset[NUM_GPIOS] = {
|
||||
0x0000A000,
|
||||
0x00000010,
|
||||
0x00000000,
|
||||
0x00000000,
|
||||
0x00000000,
|
||||
0x00000000,
|
||||
0x00000000,
|
||||
0x00000000
|
||||
};
|
||||
|
||||
static uint32_t gpio_readl(unsigned int gpio, unsigned int offset)
|
||||
{
|
||||
return readl(gpio + offset);
|
||||
}
|
||||
|
||||
static void gpio_writel(unsigned int gpio, unsigned int offset, uint32_t value)
|
||||
{
|
||||
writel(gpio + offset, value);
|
||||
}
|
||||
|
||||
static void gpio_set_bit(unsigned int gpio, unsigned int reg,
|
||||
unsigned int pin, uint32_t value)
|
||||
{
|
||||
uint32_t mask = 0xFFFFFFFF & ~(0x1 << pin);
|
||||
gpio_writel(gpio, reg, (gpio_readl(gpio, reg) & mask) | value << pin);
|
||||
}
|
||||
|
||||
static void gpio_set_2bits(unsigned int gpio, unsigned int reg,
|
||||
unsigned int pin, uint32_t value)
|
||||
{
|
||||
uint32_t offset = 2 * pin;
|
||||
uint32_t mask = 0xFFFFFFFF & ~(0x3 << offset);
|
||||
gpio_writel(gpio, reg, (gpio_readl(gpio, reg) & mask) | value << offset);
|
||||
}
|
||||
|
||||
static unsigned int get_gpio_id(uint32_t gpio_addr)
|
||||
{
|
||||
return (gpio_addr - GPIO_BASE_ADDR) / GPIO_SIZE;
|
||||
}
|
||||
|
||||
static void gpio_set_irq(unsigned int gpio, int num, int level)
|
||||
{
|
||||
g_autofree char *name = g_strdup_printf("/machine/soc/gpio%c",
|
||||
get_gpio_id(gpio) + 'a');
|
||||
qtest_set_irq_in(global_qtest, name, NULL, num, level);
|
||||
}
|
||||
|
||||
static void disconnect_all_pins(unsigned int gpio)
|
||||
{
|
||||
g_autofree char *path = g_strdup_printf("/machine/soc/gpio%c",
|
||||
get_gpio_id(gpio) + 'a');
|
||||
QDict *r;
|
||||
|
||||
r = qtest_qmp(global_qtest, "{ 'execute': 'qom-set', 'arguments': "
|
||||
"{ 'path': %s, 'property': 'disconnected-pins', 'value': %d } }",
|
||||
path, 0xFFFF);
|
||||
g_assert_false(qdict_haskey(r, "error"));
|
||||
qobject_unref(r);
|
||||
}
|
||||
|
||||
static uint32_t get_disconnected_pins(unsigned int gpio)
|
||||
{
|
||||
g_autofree char *path = g_strdup_printf("/machine/soc/gpio%c",
|
||||
get_gpio_id(gpio) + 'a');
|
||||
uint32_t disconnected_pins = 0;
|
||||
QDict *r;
|
||||
|
||||
r = qtest_qmp(global_qtest, "{ 'execute': 'qom-get', 'arguments':"
|
||||
" { 'path': %s, 'property': 'disconnected-pins'} }", path);
|
||||
g_assert_false(qdict_haskey(r, "error"));
|
||||
disconnected_pins = qdict_get_int(r, "return");
|
||||
qobject_unref(r);
|
||||
return disconnected_pins;
|
||||
}
|
||||
|
||||
static uint32_t reset(uint32_t gpio, unsigned int offset)
|
||||
{
|
||||
switch (offset) {
|
||||
case MODER:
|
||||
return moder_reset[get_gpio_id(gpio)];
|
||||
case PUPDR:
|
||||
return pupdr_reset[get_gpio_id(gpio)];
|
||||
case IDR:
|
||||
return idr_reset[get_gpio_id(gpio)];
|
||||
}
|
||||
return 0x0;
|
||||
}
|
||||
|
||||
static void system_reset(void)
|
||||
{
|
||||
QDict *r;
|
||||
r = qtest_qmp(global_qtest, "{'execute': 'system_reset'}");
|
||||
g_assert_false(qdict_haskey(r, "error"));
|
||||
qobject_unref(r);
|
||||
}
|
||||
|
||||
static void test_idr_reset_value(void)
|
||||
{
|
||||
/*
|
||||
* Checks that the values in MODER, OTYPER, PUPDR and ODR
|
||||
* after reset are correct, and that the value in IDR is
|
||||
* coherent.
|
||||
* Since AF and analog modes aren't implemented, IDR reset
|
||||
* values aren't the same as with a real board.
|
||||
*
|
||||
* Register IDR contains the actual values of all GPIO pins.
|
||||
* Its value depends on the pins' configuration
|
||||
* (intput/output/analog : register MODER, push-pull/open-drain :
|
||||
* register OTYPER, pull-up/pull-down/none : register PUPDR)
|
||||
* and on the values stored in register ODR
|
||||
* (in case the pin is in output mode).
|
||||
*/
|
||||
|
||||
gpio_writel(GPIO_A, MODER, 0xDEADBEEF);
|
||||
gpio_writel(GPIO_A, ODR, 0xDEADBEEF);
|
||||
gpio_writel(GPIO_A, OTYPER, 0xDEADBEEF);
|
||||
gpio_writel(GPIO_A, PUPDR, 0xDEADBEEF);
|
||||
|
||||
gpio_writel(GPIO_B, MODER, 0xDEADBEEF);
|
||||
gpio_writel(GPIO_B, ODR, 0xDEADBEEF);
|
||||
gpio_writel(GPIO_B, OTYPER, 0xDEADBEEF);
|
||||
gpio_writel(GPIO_B, PUPDR, 0xDEADBEEF);
|
||||
|
||||
gpio_writel(GPIO_C, MODER, 0xDEADBEEF);
|
||||
gpio_writel(GPIO_C, ODR, 0xDEADBEEF);
|
||||
gpio_writel(GPIO_C, OTYPER, 0xDEADBEEF);
|
||||
gpio_writel(GPIO_C, PUPDR, 0xDEADBEEF);
|
||||
|
||||
gpio_writel(GPIO_H, MODER, 0xDEADBEEF);
|
||||
gpio_writel(GPIO_H, ODR, 0xDEADBEEF);
|
||||
gpio_writel(GPIO_H, OTYPER, 0xDEADBEEF);
|
||||
gpio_writel(GPIO_H, PUPDR, 0xDEADBEEF);
|
||||
|
||||
system_reset();
|
||||
|
||||
uint32_t moder = gpio_readl(GPIO_A, MODER);
|
||||
uint32_t odr = gpio_readl(GPIO_A, ODR);
|
||||
uint32_t otyper = gpio_readl(GPIO_A, OTYPER);
|
||||
uint32_t pupdr = gpio_readl(GPIO_A, PUPDR);
|
||||
uint32_t idr = gpio_readl(GPIO_A, IDR);
|
||||
/* 15: AF, 14: AF, 13: AF, 12: Analog ... */
|
||||
/* here AF is the same as Analog and Input mode */
|
||||
g_assert_cmphex(moder, ==, reset(GPIO_A, MODER));
|
||||
g_assert_cmphex(odr, ==, reset(GPIO_A, ODR));
|
||||
g_assert_cmphex(otyper, ==, reset(GPIO_A, OTYPER));
|
||||
/* 15: pull-up, 14: pull-down, 13: pull-up, 12: neither ... */
|
||||
g_assert_cmphex(pupdr, ==, reset(GPIO_A, PUPDR));
|
||||
/* 15 : 1, 14: 0, 13: 1, 12 : reset value ... */
|
||||
g_assert_cmphex(idr, ==, reset(GPIO_A, IDR));
|
||||
|
||||
moder = gpio_readl(GPIO_B, MODER);
|
||||
odr = gpio_readl(GPIO_B, ODR);
|
||||
otyper = gpio_readl(GPIO_B, OTYPER);
|
||||
pupdr = gpio_readl(GPIO_B, PUPDR);
|
||||
idr = gpio_readl(GPIO_B, IDR);
|
||||
/* ... 5: Analog, 4: AF, 3: AF, 2: Analog ... */
|
||||
/* here AF is the same as Analog and Input mode */
|
||||
g_assert_cmphex(moder, ==, reset(GPIO_B, MODER));
|
||||
g_assert_cmphex(odr, ==, reset(GPIO_B, ODR));
|
||||
g_assert_cmphex(otyper, ==, reset(GPIO_B, OTYPER));
|
||||
/* ... 5: neither, 4: pull-up, 3: neither ... */
|
||||
g_assert_cmphex(pupdr, ==, reset(GPIO_B, PUPDR));
|
||||
/* ... 5 : reset value, 4 : 1, 3 : reset value ... */
|
||||
g_assert_cmphex(idr, ==, reset(GPIO_B, IDR));
|
||||
|
||||
moder = gpio_readl(GPIO_C, MODER);
|
||||
odr = gpio_readl(GPIO_C, ODR);
|
||||
otyper = gpio_readl(GPIO_C, OTYPER);
|
||||
pupdr = gpio_readl(GPIO_C, PUPDR);
|
||||
idr = gpio_readl(GPIO_C, IDR);
|
||||
/* Analog, same as Input mode*/
|
||||
g_assert_cmphex(moder, ==, reset(GPIO_C, MODER));
|
||||
g_assert_cmphex(odr, ==, reset(GPIO_C, ODR));
|
||||
g_assert_cmphex(otyper, ==, reset(GPIO_C, OTYPER));
|
||||
/* no pull-up or pull-down */
|
||||
g_assert_cmphex(pupdr, ==, reset(GPIO_C, PUPDR));
|
||||
/* reset value */
|
||||
g_assert_cmphex(idr, ==, reset(GPIO_C, IDR));
|
||||
|
||||
moder = gpio_readl(GPIO_H, MODER);
|
||||
odr = gpio_readl(GPIO_H, ODR);
|
||||
otyper = gpio_readl(GPIO_H, OTYPER);
|
||||
pupdr = gpio_readl(GPIO_H, PUPDR);
|
||||
idr = gpio_readl(GPIO_H, IDR);
|
||||
/* Analog, same as Input mode */
|
||||
g_assert_cmphex(moder, ==, reset(GPIO_H, MODER));
|
||||
g_assert_cmphex(odr, ==, reset(GPIO_H, ODR));
|
||||
g_assert_cmphex(otyper, ==, reset(GPIO_H, OTYPER));
|
||||
/* no pull-up or pull-down */
|
||||
g_assert_cmphex(pupdr, ==, reset(GPIO_H, PUPDR));
|
||||
/* reset value */
|
||||
g_assert_cmphex(idr, ==, reset(GPIO_H, IDR));
|
||||
}
|
||||
|
||||
static void test_gpio_output_mode(const void *data)
|
||||
{
|
||||
/*
|
||||
* Checks that setting a bit in ODR sets the corresponding
|
||||
* GPIO line high : it should set the right bit in IDR
|
||||
* and send an irq to syscfg.
|
||||
* Additionally, it checks that values written to ODR
|
||||
* when not in output mode are stored and not discarded.
|
||||
*/
|
||||
unsigned int pin = ((uint64_t)data) & 0xF;
|
||||
uint32_t gpio = ((uint64_t)data) >> 32;
|
||||
unsigned int gpio_id = get_gpio_id(gpio);
|
||||
|
||||
qtest_irq_intercept_in(global_qtest, "/machine/soc/syscfg");
|
||||
|
||||
/* Set a bit in ODR and check nothing happens */
|
||||
gpio_set_bit(gpio, ODR, pin, 1);
|
||||
g_assert_cmphex(gpio_readl(gpio, IDR), ==, reset(gpio, IDR));
|
||||
g_assert_false(get_irq(gpio_id * NUM_GPIO_PINS + pin));
|
||||
|
||||
/* Configure the relevant line as output and check the pin is high */
|
||||
gpio_set_2bits(gpio, MODER, pin, MODER_OUTPUT);
|
||||
g_assert_cmphex(gpio_readl(gpio, IDR), ==, reset(gpio, IDR) | (1 << pin));
|
||||
g_assert_true(get_irq(gpio_id * NUM_GPIO_PINS + pin));
|
||||
|
||||
/* Reset the bit in ODR and check the pin is low */
|
||||
gpio_set_bit(gpio, ODR, pin, 0);
|
||||
g_assert_cmphex(gpio_readl(gpio, IDR), ==, reset(gpio, IDR) & ~(1 << pin));
|
||||
g_assert_false(get_irq(gpio_id * NUM_GPIO_PINS + pin));
|
||||
|
||||
/* Clean the test */
|
||||
gpio_writel(gpio, ODR, reset(gpio, ODR));
|
||||
gpio_writel(gpio, MODER, reset(gpio, MODER));
|
||||
g_assert_cmphex(gpio_readl(gpio, IDR), ==, reset(gpio, IDR));
|
||||
g_assert_false(get_irq(gpio_id * NUM_GPIO_PINS + pin));
|
||||
}
|
||||
|
||||
static void test_gpio_input_mode(const void *data)
|
||||
{
|
||||
/*
|
||||
* Test that setting a line high/low externally sets the
|
||||
* corresponding GPIO line high/low : it should set the
|
||||
* right bit in IDR and send an irq to syscfg.
|
||||
*/
|
||||
unsigned int pin = ((uint64_t)data) & 0xF;
|
||||
uint32_t gpio = ((uint64_t)data) >> 32;
|
||||
unsigned int gpio_id = get_gpio_id(gpio);
|
||||
|
||||
qtest_irq_intercept_in(global_qtest, "/machine/soc/syscfg");
|
||||
|
||||
/* Configure a line as input, raise it, and check that the pin is high */
|
||||
gpio_set_2bits(gpio, MODER, pin, MODER_INPUT);
|
||||
gpio_set_irq(gpio, pin, 1);
|
||||
g_assert_cmphex(gpio_readl(gpio, IDR), ==, reset(gpio, IDR) | (1 << pin));
|
||||
g_assert_true(get_irq(gpio_id * NUM_GPIO_PINS + pin));
|
||||
|
||||
/* Lower the line and check that the pin is low */
|
||||
gpio_set_irq(gpio, pin, 0);
|
||||
g_assert_cmphex(gpio_readl(gpio, IDR), ==, reset(gpio, IDR) & ~(1 << pin));
|
||||
g_assert_false(get_irq(gpio_id * NUM_GPIO_PINS + pin));
|
||||
|
||||
/* Clean the test */
|
||||
gpio_writel(gpio, MODER, reset(gpio, MODER));
|
||||
disconnect_all_pins(gpio);
|
||||
g_assert_cmphex(gpio_readl(gpio, IDR), ==, reset(gpio, IDR));
|
||||
}
|
||||
|
||||
static void test_pull_up_pull_down(const void *data)
|
||||
{
|
||||
/*
|
||||
* Test that a floating pin with pull-up sets the pin
|
||||
* high and vice-versa.
|
||||
*/
|
||||
unsigned int pin = ((uint64_t)data) & 0xF;
|
||||
uint32_t gpio = ((uint64_t)data) >> 32;
|
||||
unsigned int gpio_id = get_gpio_id(gpio);
|
||||
|
||||
qtest_irq_intercept_in(global_qtest, "/machine/soc/syscfg");
|
||||
|
||||
/* Configure a line as input with pull-up, check the line is set high */
|
||||
gpio_set_2bits(gpio, MODER, pin, MODER_INPUT);
|
||||
gpio_set_2bits(gpio, PUPDR, pin, PUPDR_PULLUP);
|
||||
g_assert_cmphex(gpio_readl(gpio, IDR), ==, reset(gpio, IDR) | (1 << pin));
|
||||
g_assert_true(get_irq(gpio_id * NUM_GPIO_PINS + pin));
|
||||
|
||||
/* Configure the line with pull-down, check the line is low */
|
||||
gpio_set_2bits(gpio, PUPDR, pin, PUPDR_PULLDOWN);
|
||||
g_assert_cmphex(gpio_readl(gpio, IDR), ==, reset(gpio, IDR) & ~(1 << pin));
|
||||
g_assert_false(get_irq(gpio_id * NUM_GPIO_PINS + pin));
|
||||
|
||||
/* Clean the test */
|
||||
gpio_writel(gpio, MODER, reset(gpio, MODER));
|
||||
gpio_writel(gpio, PUPDR, reset(gpio, PUPDR));
|
||||
g_assert_cmphex(gpio_readl(gpio, IDR), ==, reset(gpio, IDR));
|
||||
}
|
||||
|
||||
static void test_push_pull(const void *data)
|
||||
{
|
||||
/*
|
||||
* Test that configuring a line in push-pull output mode
|
||||
* disconnects the pin, that the pin can't be set or reset
|
||||
* externally afterwards.
|
||||
*/
|
||||
unsigned int pin = ((uint64_t)data) & 0xF;
|
||||
uint32_t gpio = ((uint64_t)data) >> 32;
|
||||
uint32_t gpio2 = GPIO_BASE_ADDR + (GPIO_H - gpio);
|
||||
|
||||
qtest_irq_intercept_in(global_qtest, "/machine/soc/syscfg");
|
||||
|
||||
/* Setting a line high externally, configuring it in push-pull output */
|
||||
/* And checking the pin was disconnected */
|
||||
gpio_set_irq(gpio, pin, 1);
|
||||
gpio_set_2bits(gpio, MODER, pin, MODER_OUTPUT);
|
||||
g_assert_cmphex(get_disconnected_pins(gpio), ==, 0xFFFF);
|
||||
g_assert_cmphex(gpio_readl(gpio, IDR), ==, reset(gpio, IDR) & ~(1 << pin));
|
||||
|
||||
/* Setting a line low externally, configuring it in push-pull output */
|
||||
/* And checking the pin was disconnected */
|
||||
gpio_set_irq(gpio2, pin, 0);
|
||||
gpio_set_bit(gpio2, ODR, pin, 1);
|
||||
gpio_set_2bits(gpio2, MODER, pin, MODER_OUTPUT);
|
||||
g_assert_cmphex(get_disconnected_pins(gpio2), ==, 0xFFFF);
|
||||
g_assert_cmphex(gpio_readl(gpio2, IDR), ==, reset(gpio2, IDR) | (1 << pin));
|
||||
|
||||
/* Trying to set a push-pull output pin, checking it doesn't work */
|
||||
gpio_set_irq(gpio, pin, 1);
|
||||
g_assert_cmphex(get_disconnected_pins(gpio), ==, 0xFFFF);
|
||||
g_assert_cmphex(gpio_readl(gpio, IDR), ==, reset(gpio, IDR) & ~(1 << pin));
|
||||
|
||||
/* Trying to reset a push-pull output pin, checking it doesn't work */
|
||||
gpio_set_irq(gpio2, pin, 0);
|
||||
g_assert_cmphex(get_disconnected_pins(gpio2), ==, 0xFFFF);
|
||||
g_assert_cmphex(gpio_readl(gpio2, IDR), ==, reset(gpio2, IDR) | (1 << pin));
|
||||
|
||||
/* Clean the test */
|
||||
gpio_writel(gpio, MODER, reset(gpio, MODER));
|
||||
gpio_writel(gpio2, ODR, reset(gpio2, ODR));
|
||||
gpio_writel(gpio2, MODER, reset(gpio2, MODER));
|
||||
}
|
||||
|
||||
static void test_open_drain(const void *data)
|
||||
{
|
||||
/*
|
||||
* Test that configuring a line in open-drain output mode
|
||||
* disconnects a pin set high externally and that the pin
|
||||
* can't be set high externally while configured in open-drain.
|
||||
*
|
||||
* However a pin set low externally shouldn't be disconnected,
|
||||
* and it can be set low externally when in open-drain mode.
|
||||
*/
|
||||
unsigned int pin = ((uint64_t)data) & 0xF;
|
||||
uint32_t gpio = ((uint64_t)data) >> 32;
|
||||
uint32_t gpio2 = GPIO_BASE_ADDR + (GPIO_H - gpio);
|
||||
|
||||
qtest_irq_intercept_in(global_qtest, "/machine/soc/syscfg");
|
||||
|
||||
/* Setting a line high externally, configuring it in open-drain output */
|
||||
/* And checking the pin was disconnected */
|
||||
gpio_set_irq(gpio, pin, 1);
|
||||
gpio_set_bit(gpio, OTYPER, pin, OTYPER_OPEN_DRAIN);
|
||||
gpio_set_2bits(gpio, MODER, pin, MODER_OUTPUT);
|
||||
g_assert_cmphex(get_disconnected_pins(gpio), ==, 0xFFFF);
|
||||
g_assert_cmphex(gpio_readl(gpio, IDR), ==, reset(gpio, IDR) & ~(1 << pin));
|
||||
|
||||
/* Setting a line low externally, configuring it in open-drain output */
|
||||
/* And checking the pin wasn't disconnected */
|
||||
gpio_set_irq(gpio2, pin, 0);
|
||||
gpio_set_bit(gpio2, ODR, pin, 1);
|
||||
gpio_set_bit(gpio2, OTYPER, pin, OTYPER_OPEN_DRAIN);
|
||||
gpio_set_2bits(gpio2, MODER, pin, MODER_OUTPUT);
|
||||
g_assert_cmphex(get_disconnected_pins(gpio2), ==, 0xFFFF & ~(1 << pin));
|
||||
g_assert_cmphex(gpio_readl(gpio2, IDR), ==,
|
||||
reset(gpio2, IDR) & ~(1 << pin));
|
||||
|
||||
/* Trying to set a open-drain output pin, checking it doesn't work */
|
||||
gpio_set_irq(gpio, pin, 1);
|
||||
g_assert_cmphex(get_disconnected_pins(gpio), ==, 0xFFFF);
|
||||
g_assert_cmphex(gpio_readl(gpio, IDR), ==, reset(gpio, IDR) & ~(1 << pin));
|
||||
|
||||
/* Trying to reset a open-drain output pin, checking it works */
|
||||
gpio_set_bit(gpio, ODR, pin, 1);
|
||||
gpio_set_irq(gpio, pin, 0);
|
||||
g_assert_cmphex(get_disconnected_pins(gpio2), ==, 0xFFFF & ~(1 << pin));
|
||||
g_assert_cmphex(gpio_readl(gpio2, IDR), ==,
|
||||
reset(gpio2, IDR) & ~(1 << pin));
|
||||
|
||||
/* Clean the test */
|
||||
disconnect_all_pins(gpio2);
|
||||
gpio_writel(gpio2, OTYPER, reset(gpio2, OTYPER));
|
||||
gpio_writel(gpio2, ODR, reset(gpio2, ODR));
|
||||
gpio_writel(gpio2, MODER, reset(gpio2, MODER));
|
||||
g_assert_cmphex(gpio_readl(gpio2, IDR), ==, reset(gpio2, IDR));
|
||||
disconnect_all_pins(gpio);
|
||||
gpio_writel(gpio, OTYPER, reset(gpio, OTYPER));
|
||||
gpio_writel(gpio, ODR, reset(gpio, ODR));
|
||||
gpio_writel(gpio, MODER, reset(gpio, MODER));
|
||||
g_assert_cmphex(gpio_readl(gpio, IDR), ==, reset(gpio, IDR));
|
||||
}
|
||||
|
||||
static void test_bsrr_brr(const void *data)
|
||||
{
|
||||
/*
|
||||
* Test that writing a '1' in BSS and BSRR
|
||||
* has the desired effect on ODR.
|
||||
* In BSRR, BSx has priority over BRx.
|
||||
*/
|
||||
unsigned int pin = ((uint64_t)data) & 0xF;
|
||||
uint32_t gpio = ((uint64_t)data) >> 32;
|
||||
|
||||
gpio_writel(gpio, BSRR, (1 << pin));
|
||||
g_assert_cmphex(gpio_readl(gpio, ODR), ==, reset(gpio, ODR) | (1 << pin));
|
||||
|
||||
gpio_writel(gpio, BSRR, (1 << (pin + NUM_GPIO_PINS)));
|
||||
g_assert_cmphex(gpio_readl(gpio, ODR), ==, reset(gpio, ODR));
|
||||
|
||||
gpio_writel(gpio, BSRR, (1 << pin));
|
||||
g_assert_cmphex(gpio_readl(gpio, ODR), ==, reset(gpio, ODR) | (1 << pin));
|
||||
|
||||
gpio_writel(gpio, BRR, (1 << pin));
|
||||
g_assert_cmphex(gpio_readl(gpio, ODR), ==, reset(gpio, ODR));
|
||||
|
||||
/* BSx should have priority over BRx */
|
||||
gpio_writel(gpio, BSRR, (1 << pin) | (1 << (pin + NUM_GPIO_PINS)));
|
||||
g_assert_cmphex(gpio_readl(gpio, ODR), ==, reset(gpio, ODR) | (1 << pin));
|
||||
|
||||
gpio_writel(gpio, BRR, (1 << pin));
|
||||
g_assert_cmphex(gpio_readl(gpio, ODR), ==, reset(gpio, ODR));
|
||||
|
||||
gpio_writel(gpio, ODR, reset(gpio, ODR));
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
g_test_set_nonfatal_assertions();
|
||||
qtest_add_func("stm32l4x5/gpio/test_idr_reset_value",
|
||||
test_idr_reset_value);
|
||||
/*
|
||||
* The inputs for the tests (gpio and pin) can be changed,
|
||||
* but the tests don't work for pins that are high at reset
|
||||
* (GPIOA15, GPIO13 and GPIOB5).
|
||||
* Specifically, rising the pin then checking `get_irq()`
|
||||
* is problematic since the pin was already high.
|
||||
*/
|
||||
qtest_add_data_func("stm32l4x5/gpio/test_gpioc5_output_mode",
|
||||
(void *)((uint64_t)GPIO_C << 32 | 5),
|
||||
test_gpio_output_mode);
|
||||
qtest_add_data_func("stm32l4x5/gpio/test_gpioh3_output_mode",
|
||||
(void *)((uint64_t)GPIO_H << 32 | 3),
|
||||
test_gpio_output_mode);
|
||||
qtest_add_data_func("stm32l4x5/gpio/test_gpio_input_mode1",
|
||||
(void *)((uint64_t)GPIO_D << 32 | 6),
|
||||
test_gpio_input_mode);
|
||||
qtest_add_data_func("stm32l4x5/gpio/test_gpio_input_mode2",
|
||||
(void *)((uint64_t)GPIO_C << 32 | 10),
|
||||
test_gpio_input_mode);
|
||||
qtest_add_data_func("stm32l4x5/gpio/test_gpio_pull_up_pull_down1",
|
||||
(void *)((uint64_t)GPIO_B << 32 | 5),
|
||||
test_pull_up_pull_down);
|
||||
qtest_add_data_func("stm32l4x5/gpio/test_gpio_pull_up_pull_down2",
|
||||
(void *)((uint64_t)GPIO_F << 32 | 1),
|
||||
test_pull_up_pull_down);
|
||||
qtest_add_data_func("stm32l4x5/gpio/test_gpio_push_pull1",
|
||||
(void *)((uint64_t)GPIO_G << 32 | 6),
|
||||
test_push_pull);
|
||||
qtest_add_data_func("stm32l4x5/gpio/test_gpio_push_pull2",
|
||||
(void *)((uint64_t)GPIO_H << 32 | 3),
|
||||
test_push_pull);
|
||||
qtest_add_data_func("stm32l4x5/gpio/test_gpio_open_drain1",
|
||||
(void *)((uint64_t)GPIO_C << 32 | 4),
|
||||
test_open_drain);
|
||||
qtest_add_data_func("stm32l4x5/gpio/test_gpio_open_drain2",
|
||||
(void *)((uint64_t)GPIO_E << 32 | 11),
|
||||
test_open_drain);
|
||||
qtest_add_data_func("stm32l4x5/gpio/test_bsrr_brr1",
|
||||
(void *)((uint64_t)GPIO_A << 32 | 12),
|
||||
test_bsrr_brr);
|
||||
qtest_add_data_func("stm32l4x5/gpio/test_bsrr_brr2",
|
||||
(void *)((uint64_t)GPIO_D << 32 | 0),
|
||||
test_bsrr_brr);
|
||||
|
||||
qtest_start("-machine b-l475e-iot01a");
|
||||
ret = g_test_run();
|
||||
qtest_end();
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -67,7 +67,7 @@ endif
|
|||
|
||||
# SME Tests
|
||||
ifneq ($(CROSS_AS_HAS_ARMV9_SME),)
|
||||
AARCH64_TESTS += sme-outprod1
|
||||
AARCH64_TESTS += sme-outprod1 sme-smopa-1 sme-smopa-2
|
||||
endif
|
||||
|
||||
# System Registers Tests
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
static const int cmp[4][4] = {
|
||||
{ 110, 134, 158, 182 },
|
||||
{ 390, 478, 566, 654 },
|
||||
{ 670, 822, 974, 1126 },
|
||||
{ 950, 1166, 1382, 1598 }
|
||||
};
|
||||
int dst[4][4];
|
||||
int *tmp = &dst[0][0];
|
||||
|
||||
asm volatile(
|
||||
".arch armv8-r+sme\n\t"
|
||||
"smstart\n\t"
|
||||
"index z0.b, #0, #1\n\t"
|
||||
"movprfx z1, z0\n\t"
|
||||
"add z1.b, z1.b, #16\n\t"
|
||||
"ptrue p0.b\n\t"
|
||||
"smopa za0.s, p0/m, p0/m, z0.b, z1.b\n\t"
|
||||
"ptrue p0.s, vl4\n\t"
|
||||
"mov w12, #0\n\t"
|
||||
"st1w { za0h.s[w12, #0] }, p0, [%0]\n\t"
|
||||
"add %0, %0, #16\n\t"
|
||||
"st1w { za0h.s[w12, #1] }, p0, [%0]\n\t"
|
||||
"add %0, %0, #16\n\t"
|
||||
"st1w { za0h.s[w12, #2] }, p0, [%0]\n\t"
|
||||
"add %0, %0, #16\n\t"
|
||||
"st1w { za0h.s[w12, #3] }, p0, [%0]\n\t"
|
||||
"smstop"
|
||||
: "+r"(tmp) : : "memory");
|
||||
|
||||
if (memcmp(cmp, dst, sizeof(dst)) == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* See above for correct results. */
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
printf("%6d", dst[i][j]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
return 1;
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
static const long cmp[4][4] = {
|
||||
{ 110, 134, 158, 182 },
|
||||
{ 390, 478, 566, 654 },
|
||||
{ 670, 822, 974, 1126 },
|
||||
{ 950, 1166, 1382, 1598 }
|
||||
};
|
||||
long dst[4][4];
|
||||
long *tmp = &dst[0][0];
|
||||
long svl;
|
||||
|
||||
/* Validate that we have a wide enough vector for 4 elements. */
|
||||
asm(".arch armv8-r+sme-i64\n\trdsvl %0, #1" : "=r"(svl));
|
||||
if (svl < 32) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
asm volatile(
|
||||
"smstart\n\t"
|
||||
"index z0.h, #0, #1\n\t"
|
||||
"movprfx z1, z0\n\t"
|
||||
"add z1.h, z1.h, #16\n\t"
|
||||
"ptrue p0.b\n\t"
|
||||
"smopa za0.d, p0/m, p0/m, z0.h, z1.h\n\t"
|
||||
"ptrue p0.d, vl4\n\t"
|
||||
"mov w12, #0\n\t"
|
||||
"st1d { za0h.d[w12, #0] }, p0, [%0]\n\t"
|
||||
"add %0, %0, #32\n\t"
|
||||
"st1d { za0h.d[w12, #1] }, p0, [%0]\n\t"
|
||||
"mov w12, #2\n\t"
|
||||
"add %0, %0, #32\n\t"
|
||||
"st1d { za0h.d[w12, #0] }, p0, [%0]\n\t"
|
||||
"add %0, %0, #32\n\t"
|
||||
"st1d { za0h.d[w12, #1] }, p0, [%0]\n\t"
|
||||
"smstop"
|
||||
: "+r"(tmp) : : "memory");
|
||||
|
||||
if (memcmp(cmp, dst, sizeof(dst)) == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* See above for correct results. */
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
printf("%6ld", dst[i][j]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
return 1;
|
||||
}
|
Loading…
Reference in New Issue