target-arm queue:
* raspi: Implement Broadcom Serial Controller (BSC) for BCM2835 boards * hw/char/pl011: Add support for loopback * STM32L4x5: Implement RCC clock control device * target/arm: Do memory type alignment checks * atomic.h: Reword confusing comment for qatomic_cmpxchg * qemu-options.hx: Don't claim "-serial" has limit of 4 serial ports -----BEGIN PGP SIGNATURE----- iQJNBAABCAA3FiEE4aXFk81BneKOgxXPPCUl7RQ2DN4FAmXnI4gZHHBldGVyLm1h eWRlbGxAbGluYXJvLm9yZwAKCRA8JSXtFDYM3p5ED/wOtAHA3PK+WbQhVhnji3+k gdhvVcldf+HLaI2v4tfaW152xjY80/j3PQFNkzptoXENA9U51F47oNYOIfULLZZX FTKmw/mjTBc2LqJ8gLpS8Wkr/PFtDq9JJzDwZd0MwguXpzIJp31JJpESvXlAqjjv FhuAcqNNuGwI2SXCBmp2lPoEMn8ExLDoG9rmzjxVZeZCyzUjVnJYM61ykhC4ByvK j5+/a7pUcpgHSX5cbq7kFloPOx3JXI5lS6xUKhGXXk75qHRwiQIsxMcPq8PD1+ok yrmp7cySwK8I7AlIPdDjpJmhU0OiBu+PkYiXmHlF2nvaUy6M0nVX2lSTzqj6VpVV 7yYhvWXHrtIA9AUspqTRsX7tP7iMJkco7qWfKSzYl+3pTbxS4+rEoee4jNR3hqsU lbWC47sNVtTN507qIL1dcsu+BaeSsYVftfxtFql3odTqRB+ticsjDfKg69dRSFyk SS0t8Zy3TdomcEoQkAv/ZSpkQnQUGavbRumCG58lJdiTwTuJUmGi1ufKBrD/GeKj IlDEl9yvKiR8uvdjj6EQqr5kOj09mmN5nvokNsq5a4aNXBYoesszWK2xodzXE2x5 M9DHJ3S8xnN++p1idS2bikwEklG1XVQ/q52bDXQkUmQSNerVS1PCvg9hzYqA+x53 ihJtMcsmGVfxY8aQHyHweA== =isAe -----END PGP SIGNATURE----- Merge tag 'pull-target-arm-20240305' of https://git.linaro.org/people/pmaydell/qemu-arm into staging target-arm queue: * raspi: Implement Broadcom Serial Controller (BSC) for BCM2835 boards * hw/char/pl011: Add support for loopback * STM32L4x5: Implement RCC clock control device * target/arm: Do memory type alignment checks * atomic.h: Reword confusing comment for qatomic_cmpxchg * qemu-options.hx: Don't claim "-serial" has limit of 4 serial ports # -----BEGIN PGP SIGNATURE----- # # iQJNBAABCAA3FiEE4aXFk81BneKOgxXPPCUl7RQ2DN4FAmXnI4gZHHBldGVyLm1h # eWRlbGxAbGluYXJvLm9yZwAKCRA8JSXtFDYM3p5ED/wOtAHA3PK+WbQhVhnji3+k # gdhvVcldf+HLaI2v4tfaW152xjY80/j3PQFNkzptoXENA9U51F47oNYOIfULLZZX # FTKmw/mjTBc2LqJ8gLpS8Wkr/PFtDq9JJzDwZd0MwguXpzIJp31JJpESvXlAqjjv # FhuAcqNNuGwI2SXCBmp2lPoEMn8ExLDoG9rmzjxVZeZCyzUjVnJYM61ykhC4ByvK # j5+/a7pUcpgHSX5cbq7kFloPOx3JXI5lS6xUKhGXXk75qHRwiQIsxMcPq8PD1+ok # yrmp7cySwK8I7AlIPdDjpJmhU0OiBu+PkYiXmHlF2nvaUy6M0nVX2lSTzqj6VpVV # 7yYhvWXHrtIA9AUspqTRsX7tP7iMJkco7qWfKSzYl+3pTbxS4+rEoee4jNR3hqsU # lbWC47sNVtTN507qIL1dcsu+BaeSsYVftfxtFql3odTqRB+ticsjDfKg69dRSFyk # SS0t8Zy3TdomcEoQkAv/ZSpkQnQUGavbRumCG58lJdiTwTuJUmGi1ufKBrD/GeKj # IlDEl9yvKiR8uvdjj6EQqr5kOj09mmN5nvokNsq5a4aNXBYoesszWK2xodzXE2x5 # M9DHJ3S8xnN++p1idS2bikwEklG1XVQ/q52bDXQkUmQSNerVS1PCvg9hzYqA+x53 # ihJtMcsmGVfxY8aQHyHweA== # =isAe # -----END PGP SIGNATURE----- # gpg: Signature made Tue 05 Mar 2024 13:52:08 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-20240305' of https://git.linaro.org/people/pmaydell/qemu-arm: qemu-options.hx: Don't claim "-serial" has limit of 4 serial ports atomic.h: Reword confusing comment for qatomic_cmpxchg target/arm: Do memory type alignment check when translation enabled target/arm: Do memory type alignment check when translation disabled accel/tcg: Add TLB_CHECK_ALIGNED accel/tcg: Add tlb_fill_flags to CPUTLBEntryFull exec/memattrs: Remove target_tlb_bit* target/arm: Support 32-byte alignment in pow2_align tests/qtest/stm32l4x5_rcc-test.c: Add tests for the STM32L4x5_RCC hw/arm/stm32l4x5_soc.c: Use the RCC Sysclk hw/misc/stm32l4x5_rcc: Add write protections to CR register hw/misc/stm32l4x5_rcc: Handle Register Updates hw/misc/stm32l4x5_rcc: Initialize PLLs and clock multiplexers hw/misc/stm32l4x5_rcc: Add an internal PLL Clock object hw/misc/stm32l4x5_rcc: Add an internal clock multiplexer object hw/misc/stm32l4x5_rcc: Implement STM32L4x5_RCC skeleton hw/char/pl011: Add support for loopback tests/qtest: Add testcase for BCM2835 BSC hw/arm: Connect BSC to BCM2835 board as I2C0, I2C1 and I2C2 hw/i2c: Implement Broadcom Serial Controller (BSC) Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
db596ae190
@ -1130,7 +1130,10 @@ M: Inès Varhol <ines.varhol@telecom-paris.fr>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/arm/stm32l4x5_soc.c
|
||||
F: include/hw/arm/stm32l4x5_soc.h
|
||||
F: hw/misc/stm32l4x5_exti.c
|
||||
F: hw/misc/stm32l4x5_syscfg.c
|
||||
F: hw/misc/stm32l4x5_rcc.c
|
||||
F: include/hw/*/stm32l4x5_*.h
|
||||
|
||||
B-L475E-IOT01A IoT Node
|
||||
M: Arnaud Minier <arnaud.minier@telecom-paris.fr>
|
||||
|
@ -1145,14 +1145,11 @@ void tlb_set_page_full(CPUState *cpu, int mmu_idx,
|
||||
" prot=%x idx=%d\n",
|
||||
addr, full->phys_addr, prot, mmu_idx);
|
||||
|
||||
read_flags = 0;
|
||||
read_flags = full->tlb_fill_flags;
|
||||
if (full->lg_page_size < TARGET_PAGE_BITS) {
|
||||
/* Repeat the MMU check and TLB fill on every access. */
|
||||
read_flags |= TLB_INVALID_MASK;
|
||||
}
|
||||
if (full->attrs.byte_swap) {
|
||||
read_flags |= TLB_BSWAP;
|
||||
}
|
||||
|
||||
is_ram = memory_region_is_ram(section->mr);
|
||||
is_romd = memory_region_is_romd(section->mr);
|
||||
@ -1456,9 +1453,8 @@ static int probe_access_internal(CPUState *cpu, vaddr addr,
|
||||
flags |= full->slow_flags[access_type];
|
||||
|
||||
/* Fold all "mmio-like" bits into TLB_MMIO. This is not RAM. */
|
||||
if (unlikely(flags & ~(TLB_WATCHPOINT | TLB_NOTDIRTY))
|
||||
||
|
||||
(access_type != MMU_INST_FETCH && force_mmio)) {
|
||||
if (unlikely(flags & ~(TLB_WATCHPOINT | TLB_NOTDIRTY | TLB_CHECK_ALIGNED))
|
||||
|| (access_type != MMU_INST_FETCH && force_mmio)) {
|
||||
*phost = NULL;
|
||||
return TLB_MMIO;
|
||||
}
|
||||
@ -1839,6 +1835,31 @@ static bool mmu_lookup(CPUState *cpu, vaddr addr, MemOpIdx oi,
|
||||
tcg_debug_assert((flags & TLB_BSWAP) == 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* This alignment check differs from the one above, in that this is
|
||||
* based on the atomicity of the operation. The intended use case is
|
||||
* the ARM memory type field of each PTE, where access to pages with
|
||||
* Device memory type require alignment.
|
||||
*/
|
||||
if (unlikely(flags & TLB_CHECK_ALIGNED)) {
|
||||
MemOp size = l->memop & MO_SIZE;
|
||||
|
||||
switch (l->memop & MO_ATOM_MASK) {
|
||||
case MO_ATOM_NONE:
|
||||
size = MO_8;
|
||||
break;
|
||||
case MO_ATOM_IFALIGN_PAIR:
|
||||
case MO_ATOM_WITHIN16_PAIR:
|
||||
size = size ? size - 1 : 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (addr & ((1 << size) - 1)) {
|
||||
cpu_unaligned_access(cpu, addr, type, l->mmu_idx, ra);
|
||||
}
|
||||
}
|
||||
|
||||
return crosspage;
|
||||
}
|
||||
|
||||
|
@ -17,13 +17,13 @@ Currently B-L475E-IOT01A machine's only supports the following devices:
|
||||
- Cortex-M4F based STM32L4x5 SoC
|
||||
- STM32L4x5 EXTI (Extended interrupts and events controller)
|
||||
- STM32L4x5 SYSCFG (System configuration controller)
|
||||
- STM32L4x5 RCC (Reset and clock control)
|
||||
|
||||
Missing devices
|
||||
"""""""""""""""
|
||||
|
||||
The B-L475E-IOT01A does *not* support the following devices:
|
||||
|
||||
- Reset and clock control (RCC)
|
||||
- Serial ports (UART)
|
||||
- General-purpose I/Os (GPIO)
|
||||
- Analog to Digital Converter (ADC)
|
||||
|
@ -35,6 +35,7 @@ Implemented devices
|
||||
* MailBox controller (MBOX)
|
||||
* VideoCore firmware (property)
|
||||
* Peripheral SPI controller (SPI)
|
||||
* Broadcom Serial Controller (I2C)
|
||||
|
||||
Missing devices
|
||||
---------------
|
||||
|
@ -438,6 +438,7 @@ config RASPI
|
||||
select SDHCI
|
||||
select USB_DWC2
|
||||
select BCM2835_SPI
|
||||
select BCM2835_I2C
|
||||
|
||||
config STM32F100_SOC
|
||||
bool
|
||||
@ -474,6 +475,7 @@ config STM32L4X5_SOC
|
||||
select OR_IRQ
|
||||
select STM32L4X5_SYSCFG
|
||||
select STM32L4X5_EXTI
|
||||
select STM32L4X5_RCC
|
||||
|
||||
config XLNX_ZYNQMP_ARM
|
||||
bool
|
||||
|
@ -26,27 +26,19 @@
|
||||
#include "qapi/error.h"
|
||||
#include "hw/boards.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/qdev-clock.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "hw/arm/stm32l4x5_soc.h"
|
||||
#include "hw/arm/boot.h"
|
||||
|
||||
/* Main SYSCLK frequency in Hz (80MHz) */
|
||||
#define MAIN_SYSCLK_FREQ_HZ 80000000ULL
|
||||
/* B-L475E-IOT01A implementation is derived from netduinoplus2 */
|
||||
|
||||
static void b_l475e_iot01a_init(MachineState *machine)
|
||||
{
|
||||
const Stm32l4x5SocClass *sc;
|
||||
DeviceState *dev;
|
||||
Clock *sysclk;
|
||||
|
||||
/* This clock doesn't need migration because it is fixed-frequency */
|
||||
sysclk = clock_new(OBJECT(machine), "SYSCLK");
|
||||
clock_set_hz(sysclk, MAIN_SYSCLK_FREQ_HZ);
|
||||
|
||||
dev = qdev_new(TYPE_STM32L4X5XG_SOC);
|
||||
object_property_add_child(OBJECT(machine), "soc", OBJECT(dev));
|
||||
qdev_connect_clock_in(dev, "sysclk", sysclk);
|
||||
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
|
||||
|
||||
sc = STM32L4X5_SOC_GET_CLASS(dev);
|
||||
|
@ -30,6 +30,9 @@
|
||||
#define SEPARATE_DMA_IRQ_MAX 10
|
||||
#define ORGATED_DMA_IRQ_COUNT 4
|
||||
|
||||
/* All three I2C controllers share the same IRQ */
|
||||
#define ORGATED_I2C_IRQ_COUNT 3
|
||||
|
||||
void create_unimp(BCMSocPeripheralBaseState *ps,
|
||||
UnimplementedDeviceState *uds,
|
||||
const char *name, hwaddr ofs, hwaddr size)
|
||||
@ -157,6 +160,19 @@ static void raspi_peripherals_base_init(Object *obj)
|
||||
/* SPI */
|
||||
object_initialize_child(obj, "bcm2835-spi0", &s->spi[0],
|
||||
TYPE_BCM2835_SPI);
|
||||
|
||||
/* I2C */
|
||||
object_initialize_child(obj, "bcm2835-i2c0", &s->i2c[0],
|
||||
TYPE_BCM2835_I2C);
|
||||
object_initialize_child(obj, "bcm2835-i2c1", &s->i2c[1],
|
||||
TYPE_BCM2835_I2C);
|
||||
object_initialize_child(obj, "bcm2835-i2c2", &s->i2c[2],
|
||||
TYPE_BCM2835_I2C);
|
||||
|
||||
object_initialize_child(obj, "orgated-i2c-irq",
|
||||
&s->orgated_i2c_irq, TYPE_OR_IRQ);
|
||||
object_property_set_int(OBJECT(&s->orgated_i2c_irq), "num-lines",
|
||||
ORGATED_I2C_IRQ_COUNT, &error_abort);
|
||||
}
|
||||
|
||||
static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
|
||||
@ -453,14 +469,37 @@ void bcm_soc_peripherals_common_realize(DeviceState *dev, Error **errp)
|
||||
BCM2835_IC_GPU_IRQ,
|
||||
INTERRUPT_SPI));
|
||||
|
||||
/* I2C */
|
||||
for (n = 0; n < 3; n++) {
|
||||
if (!sysbus_realize(SYS_BUS_DEVICE(&s->i2c[n]), errp)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
memory_region_add_subregion(&s->peri_mr, BSC0_OFFSET,
|
||||
sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->i2c[0]), 0));
|
||||
memory_region_add_subregion(&s->peri_mr, BSC1_OFFSET,
|
||||
sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->i2c[1]), 0));
|
||||
memory_region_add_subregion(&s->peri_mr, BSC2_OFFSET,
|
||||
sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->i2c[2]), 0));
|
||||
|
||||
if (!qdev_realize(DEVICE(&s->orgated_i2c_irq), NULL, errp)) {
|
||||
return;
|
||||
}
|
||||
for (n = 0; n < ORGATED_I2C_IRQ_COUNT; n++) {
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c[n]), 0,
|
||||
qdev_get_gpio_in(DEVICE(&s->orgated_i2c_irq), n));
|
||||
}
|
||||
qdev_connect_gpio_out(DEVICE(&s->orgated_i2c_irq), 0,
|
||||
qdev_get_gpio_in_named(DEVICE(&s->ic),
|
||||
BCM2835_IC_GPU_IRQ,
|
||||
INTERRUPT_I2C));
|
||||
|
||||
create_unimp(s, &s->txp, "bcm2835-txp", TXP_OFFSET, 0x1000);
|
||||
create_unimp(s, &s->armtmr, "bcm2835-sp804", ARMCTRL_TIMER0_1_OFFSET, 0x40);
|
||||
create_unimp(s, &s->i2s, "bcm2835-i2s", I2S_OFFSET, 0x100);
|
||||
create_unimp(s, &s->smi, "bcm2835-smi", SMI_OFFSET, 0x100);
|
||||
create_unimp(s, &s->bscsl, "bcm2835-spis", BSC_SL_OFFSET, 0x100);
|
||||
create_unimp(s, &s->i2c[0], "bcm2835-i2c0", BSC0_OFFSET, 0x20);
|
||||
create_unimp(s, &s->i2c[1], "bcm2835-i2c1", BSC1_OFFSET, 0x20);
|
||||
create_unimp(s, &s->i2c[2], "bcm2835-i2c2", BSC2_OFFSET, 0x20);
|
||||
create_unimp(s, &s->otp, "bcm2835-otp", OTP_OFFSET, 0x80);
|
||||
create_unimp(s, &s->dbus, "bcm2835-dbus", DBUS_OFFSET, 0x8000);
|
||||
create_unimp(s, &s->ave0, "bcm2835-ave0", AVE0_OFFSET, 0x8000);
|
||||
|
@ -76,6 +76,8 @@ static const int exti_irq[NUM_EXTI_IRQ] = {
|
||||
-1, -1, -1, -1, /* PVM[1..4] OR gate 1 */
|
||||
78 /* LCD wakeup, Direct */
|
||||
};
|
||||
#define RCC_BASE_ADDRESS 0x40021000
|
||||
#define RCC_IRQ 5
|
||||
|
||||
static const int exti_or_gates_out[NUM_EXTI_OR_GATES] = {
|
||||
23, 40, 63, 1,
|
||||
@ -107,9 +109,7 @@ static void stm32l4x5_soc_initfn(Object *obj)
|
||||
TYPE_OR_IRQ);
|
||||
}
|
||||
object_initialize_child(obj, "syscfg", &s->syscfg, TYPE_STM32L4X5_SYSCFG);
|
||||
|
||||
s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0);
|
||||
s->refclk = qdev_init_clock_in(DEVICE(s), "refclk", NULL, NULL, 0);
|
||||
object_initialize_child(obj, "rcc", &s->rcc, TYPE_STM32L4X5_RCC);
|
||||
}
|
||||
|
||||
static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp)
|
||||
@ -121,30 +121,6 @@ static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp)
|
||||
DeviceState *armv7m;
|
||||
SysBusDevice *busdev;
|
||||
|
||||
/*
|
||||
* We use s->refclk internally and only define it with qdev_init_clock_in()
|
||||
* so it is correctly parented and not leaked on an init/deinit; it is not
|
||||
* intended as an externally exposed clock.
|
||||
*/
|
||||
if (clock_has_source(s->refclk)) {
|
||||
error_setg(errp, "refclk clock must not be wired up by the board code");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!clock_has_source(s->sysclk)) {
|
||||
error_setg(errp, "sysclk clock must be wired up by the board code");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: ideally we should model the SoC RCC and its ability to
|
||||
* change the sysclk frequency and define different sysclk sources.
|
||||
*/
|
||||
|
||||
/* The refclk always runs at frequency HCLK / 8 */
|
||||
clock_set_mul_div(s->refclk, 8, 1);
|
||||
clock_set_source(s->refclk, s->sysclk);
|
||||
|
||||
if (!memory_region_init_rom(&s->flash, OBJECT(dev_soc), "flash",
|
||||
sc->flash_size, errp)) {
|
||||
return;
|
||||
@ -174,8 +150,10 @@ static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp)
|
||||
qdev_prop_set_uint32(armv7m, "num-prio-bits", 4);
|
||||
qdev_prop_set_string(armv7m, "cpu-type", ARM_CPU_TYPE_NAME("cortex-m4"));
|
||||
qdev_prop_set_bit(armv7m, "enable-bitband", true);
|
||||
qdev_connect_clock_in(armv7m, "cpuclk", s->sysclk);
|
||||
qdev_connect_clock_in(armv7m, "refclk", s->refclk);
|
||||
qdev_connect_clock_in(armv7m, "cpuclk",
|
||||
qdev_get_clock_out(DEVICE(&(s->rcc)), "cortex-fclk-out"));
|
||||
qdev_connect_clock_in(armv7m, "refclk",
|
||||
qdev_get_clock_out(DEVICE(&(s->rcc)), "cortex-refclk-out"));
|
||||
object_property_set_link(OBJECT(&s->armv7m), "memory",
|
||||
OBJECT(system_memory), &error_abort);
|
||||
if (!sysbus_realize(SYS_BUS_DEVICE(&s->armv7m), errp)) {
|
||||
@ -244,6 +222,14 @@ static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp)
|
||||
qdev_get_gpio_in(DEVICE(&s->exti), i));
|
||||
}
|
||||
|
||||
/* RCC device */
|
||||
busdev = SYS_BUS_DEVICE(&s->rcc);
|
||||
if (!sysbus_realize(busdev, errp)) {
|
||||
return;
|
||||
}
|
||||
sysbus_mmio_map(busdev, 0, RCC_BASE_ADDRESS);
|
||||
sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m, RCC_IRQ));
|
||||
|
||||
/* APB1 BUS */
|
||||
create_unimplemented_device("TIM2", 0x40000000, 0x400);
|
||||
create_unimplemented_device("TIM3", 0x40000400, 0x400);
|
||||
@ -306,7 +292,6 @@ static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp)
|
||||
create_unimplemented_device("DMA1", 0x40020000, 0x400);
|
||||
create_unimplemented_device("DMA2", 0x40020400, 0x400);
|
||||
/* RESERVED: 0x40020800, 0x800 */
|
||||
create_unimplemented_device("RCC", 0x40021000, 0x400);
|
||||
/* RESERVED: 0x40021400, 0xC00 */
|
||||
create_unimplemented_device("FLASH", 0x40022000, 0x400);
|
||||
/* RESERVED: 0x40022400, 0xC00 */
|
||||
|
110
hw/char/pl011.c
110
hw/char/pl011.c
@ -49,10 +49,14 @@ DeviceState *pl011_create(hwaddr addr, qemu_irq irq, Chardev *chr)
|
||||
}
|
||||
|
||||
/* Flag Register, UARTFR */
|
||||
#define PL011_FLAG_RI 0x100
|
||||
#define PL011_FLAG_TXFE 0x80
|
||||
#define PL011_FLAG_RXFF 0x40
|
||||
#define PL011_FLAG_TXFF 0x20
|
||||
#define PL011_FLAG_RXFE 0x10
|
||||
#define PL011_FLAG_DCD 0x04
|
||||
#define PL011_FLAG_DSR 0x02
|
||||
#define PL011_FLAG_CTS 0x01
|
||||
|
||||
/* Data Register, UARTDR */
|
||||
#define DR_BE (1 << 10)
|
||||
@ -76,6 +80,13 @@ DeviceState *pl011_create(hwaddr addr, qemu_irq irq, Chardev *chr)
|
||||
#define LCR_FEN (1 << 4)
|
||||
#define LCR_BRK (1 << 0)
|
||||
|
||||
/* Control Register, UARTCR */
|
||||
#define CR_OUT2 (1 << 13)
|
||||
#define CR_OUT1 (1 << 12)
|
||||
#define CR_RTS (1 << 11)
|
||||
#define CR_DTR (1 << 10)
|
||||
#define CR_LBE (1 << 7)
|
||||
|
||||
static const unsigned char pl011_id_arm[8] =
|
||||
{ 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
|
||||
static const unsigned char pl011_id_luminary[8] =
|
||||
@ -251,6 +262,89 @@ static void pl011_trace_baudrate_change(const PL011State *s)
|
||||
s->ibrd, s->fbrd);
|
||||
}
|
||||
|
||||
static bool pl011_loopback_enabled(PL011State *s)
|
||||
{
|
||||
return !!(s->cr & CR_LBE);
|
||||
}
|
||||
|
||||
static void pl011_loopback_mdmctrl(PL011State *s)
|
||||
{
|
||||
uint32_t cr, fr, il;
|
||||
|
||||
if (!pl011_loopback_enabled(s)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Loopback software-driven modem control outputs to modem status inputs:
|
||||
* FR.RI <= CR.Out2
|
||||
* FR.DCD <= CR.Out1
|
||||
* FR.CTS <= CR.RTS
|
||||
* FR.DSR <= CR.DTR
|
||||
*
|
||||
* The loopback happens immediately even if this call is triggered
|
||||
* by setting only CR.LBE.
|
||||
*
|
||||
* CTS/RTS updates due to enabled hardware flow controls are not
|
||||
* dealt with here.
|
||||
*/
|
||||
cr = s->cr;
|
||||
fr = s->flags & ~(PL011_FLAG_RI | PL011_FLAG_DCD |
|
||||
PL011_FLAG_DSR | PL011_FLAG_CTS);
|
||||
fr |= (cr & CR_OUT2) ? PL011_FLAG_RI : 0;
|
||||
fr |= (cr & CR_OUT1) ? PL011_FLAG_DCD : 0;
|
||||
fr |= (cr & CR_RTS) ? PL011_FLAG_CTS : 0;
|
||||
fr |= (cr & CR_DTR) ? PL011_FLAG_DSR : 0;
|
||||
|
||||
/* Change interrupts based on updated FR */
|
||||
il = s->int_level & ~(INT_DSR | INT_DCD | INT_CTS | INT_RI);
|
||||
il |= (fr & PL011_FLAG_DSR) ? INT_DSR : 0;
|
||||
il |= (fr & PL011_FLAG_DCD) ? INT_DCD : 0;
|
||||
il |= (fr & PL011_FLAG_CTS) ? INT_CTS : 0;
|
||||
il |= (fr & PL011_FLAG_RI) ? INT_RI : 0;
|
||||
|
||||
s->flags = fr;
|
||||
s->int_level = il;
|
||||
pl011_update(s);
|
||||
}
|
||||
|
||||
static void pl011_put_fifo(void *opaque, uint32_t value);
|
||||
|
||||
static void pl011_loopback_tx(PL011State *s, uint32_t value)
|
||||
{
|
||||
if (!pl011_loopback_enabled(s)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Caveat:
|
||||
*
|
||||
* In real hardware, TX loopback happens at the serial-bit level
|
||||
* and then reassembled by the RX logics back into bytes and placed
|
||||
* into the RX fifo. That is, loopback happens after TX fifo.
|
||||
*
|
||||
* Because the real hardware TX fifo is time-drained at the frame
|
||||
* rate governed by the configured serial format, some loopback
|
||||
* bytes in TX fifo may still be able to get into the RX fifo
|
||||
* that could be full at times while being drained at software
|
||||
* pace.
|
||||
*
|
||||
* In such scenario, the RX draining pace is the major factor
|
||||
* deciding which loopback bytes get into the RX fifo, unless
|
||||
* hardware flow-control is enabled.
|
||||
*
|
||||
* For simplicity, the above described is not emulated.
|
||||
*/
|
||||
pl011_put_fifo(s, value);
|
||||
}
|
||||
|
||||
static void pl011_loopback_break(PL011State *s, int brk_enable)
|
||||
{
|
||||
if (brk_enable) {
|
||||
pl011_loopback_tx(s, DR_BE);
|
||||
}
|
||||
}
|
||||
|
||||
static void pl011_write(void *opaque, hwaddr offset,
|
||||
uint64_t value, unsigned size)
|
||||
{
|
||||
@ -266,6 +360,7 @@ static void pl011_write(void *opaque, hwaddr offset,
|
||||
/* XXX this blocks entire thread. Rewrite to use
|
||||
* qemu_chr_fe_write and background I/O callbacks */
|
||||
qemu_chr_fe_write_all(&s->chr, &ch, 1);
|
||||
pl011_loopback_tx(s, ch);
|
||||
s->int_level |= INT_TX;
|
||||
pl011_update(s);
|
||||
break;
|
||||
@ -295,13 +390,15 @@ static void pl011_write(void *opaque, hwaddr offset,
|
||||
int break_enable = value & LCR_BRK;
|
||||
qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_BREAK,
|
||||
&break_enable);
|
||||
pl011_loopback_break(s, break_enable);
|
||||
}
|
||||
s->lcr = value;
|
||||
pl011_set_read_trigger(s);
|
||||
break;
|
||||
case 12: /* UARTCR */
|
||||
/* ??? Need to implement the enable and loopback bits. */
|
||||
/* ??? Need to implement the enable bit. */
|
||||
s->cr = value;
|
||||
pl011_loopback_mdmctrl(s);
|
||||
break;
|
||||
case 13: /* UARTIFS */
|
||||
s->ifl = value;
|
||||
@ -361,12 +458,21 @@ static void pl011_put_fifo(void *opaque, uint32_t value)
|
||||
|
||||
static void pl011_receive(void *opaque, const uint8_t *buf, int size)
|
||||
{
|
||||
/*
|
||||
* In loopback mode, the RX input signal is internally disconnected
|
||||
* from the entire receiving logics; thus, all inputs are ignored,
|
||||
* and BREAK detection on RX input signal is also not performed.
|
||||
*/
|
||||
if (pl011_loopback_enabled(opaque)) {
|
||||
return;
|
||||
}
|
||||
|
||||
pl011_put_fifo(opaque, *buf);
|
||||
}
|
||||
|
||||
static void pl011_event(void *opaque, QEMUChrEvent event)
|
||||
{
|
||||
if (event == CHR_EVENT_BREAK) {
|
||||
if (event == CHR_EVENT_BREAK && !pl011_loopback_enabled(opaque)) {
|
||||
pl011_put_fifo(opaque, DR_BE);
|
||||
}
|
||||
}
|
||||
|
@ -45,3 +45,7 @@ config PCA954X
|
||||
config PMBUS
|
||||
bool
|
||||
select SMBUS
|
||||
|
||||
config BCM2835_I2C
|
||||
bool
|
||||
select I2C
|
||||
|
282
hw/i2c/bcm2835_i2c.c
Normal file
282
hw/i2c/bcm2835_i2c.c
Normal file
@ -0,0 +1,282 @@
|
||||
/*
|
||||
* Broadcom Serial Controller (BSC)
|
||||
*
|
||||
* Copyright (c) 2024 Rayhan Faizel <rayhan.faizel@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/log.h"
|
||||
#include "hw/i2c/bcm2835_i2c.h"
|
||||
#include "hw/irq.h"
|
||||
#include "migration/vmstate.h"
|
||||
|
||||
static void bcm2835_i2c_update_interrupt(BCM2835I2CState *s)
|
||||
{
|
||||
int do_interrupt = 0;
|
||||
/* Interrupt on RXR (Needs reading) */
|
||||
if (s->c & BCM2835_I2C_C_INTR && s->s & BCM2835_I2C_S_RXR) {
|
||||
do_interrupt = 1;
|
||||
}
|
||||
|
||||
/* Interrupt on TXW (Needs writing) */
|
||||
if (s->c & BCM2835_I2C_C_INTT && s->s & BCM2835_I2C_S_TXW) {
|
||||
do_interrupt = 1;
|
||||
}
|
||||
|
||||
/* Interrupt on DONE (Transfer complete) */
|
||||
if (s->c & BCM2835_I2C_C_INTD && s->s & BCM2835_I2C_S_DONE) {
|
||||
do_interrupt = 1;
|
||||
}
|
||||
qemu_set_irq(s->irq, do_interrupt);
|
||||
}
|
||||
|
||||
static void bcm2835_i2c_begin_transfer(BCM2835I2CState *s)
|
||||
{
|
||||
int direction = s->c & BCM2835_I2C_C_READ;
|
||||
if (i2c_start_transfer(s->bus, s->a, direction)) {
|
||||
s->s |= BCM2835_I2C_S_ERR;
|
||||
}
|
||||
s->s |= BCM2835_I2C_S_TA;
|
||||
|
||||
if (direction) {
|
||||
s->s |= BCM2835_I2C_S_RXR | BCM2835_I2C_S_RXD;
|
||||
} else {
|
||||
s->s |= BCM2835_I2C_S_TXW;
|
||||
}
|
||||
}
|
||||
|
||||
static void bcm2835_i2c_finish_transfer(BCM2835I2CState *s)
|
||||
{
|
||||
/*
|
||||
* STOP is sent when DLEN counts down to zero.
|
||||
*
|
||||
* https://github.com/torvalds/linux/blob/v6.7/drivers/i2c/busses/i2c-bcm2835.c#L223-L261
|
||||
* It is possible to initiate repeated starts on real hardware.
|
||||
* However, this requires sending another ST request before the bytes in
|
||||
* TX FIFO are shifted out.
|
||||
*
|
||||
* This is not emulated currently.
|
||||
*/
|
||||
i2c_end_transfer(s->bus);
|
||||
s->s |= BCM2835_I2C_S_DONE;
|
||||
|
||||
/* Ensure RXD is cleared, otherwise the driver registers an error */
|
||||
s->s &= ~(BCM2835_I2C_S_TA | BCM2835_I2C_S_RXR |
|
||||
BCM2835_I2C_S_TXW | BCM2835_I2C_S_RXD);
|
||||
}
|
||||
|
||||
static uint64_t bcm2835_i2c_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
BCM2835I2CState *s = opaque;
|
||||
uint32_t readval = 0;
|
||||
|
||||
switch (addr) {
|
||||
case BCM2835_I2C_C:
|
||||
readval = s->c;
|
||||
break;
|
||||
case BCM2835_I2C_S:
|
||||
readval = s->s;
|
||||
break;
|
||||
case BCM2835_I2C_DLEN:
|
||||
readval = s->dlen;
|
||||
break;
|
||||
case BCM2835_I2C_A:
|
||||
readval = s->a;
|
||||
break;
|
||||
case BCM2835_I2C_FIFO:
|
||||
/* We receive I2C messages directly instead of using FIFOs */
|
||||
if (s->s & BCM2835_I2C_S_TA) {
|
||||
readval = i2c_recv(s->bus);
|
||||
s->dlen -= 1;
|
||||
|
||||
if (s->dlen == 0) {
|
||||
bcm2835_i2c_finish_transfer(s);
|
||||
}
|
||||
}
|
||||
bcm2835_i2c_update_interrupt(s);
|
||||
break;
|
||||
case BCM2835_I2C_DIV:
|
||||
readval = s->div;
|
||||
break;
|
||||
case BCM2835_I2C_DEL:
|
||||
readval = s->del;
|
||||
break;
|
||||
case BCM2835_I2C_CLKT:
|
||||
readval = s->clkt;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, addr);
|
||||
}
|
||||
|
||||
return readval;
|
||||
}
|
||||
|
||||
static void bcm2835_i2c_write(void *opaque, hwaddr addr,
|
||||
uint64_t value, unsigned int size)
|
||||
{
|
||||
BCM2835I2CState *s = opaque;
|
||||
uint32_t writeval = value;
|
||||
|
||||
switch (addr) {
|
||||
case BCM2835_I2C_C:
|
||||
/* ST is a one-shot operation; it must read back as 0 */
|
||||
s->c = writeval & ~BCM2835_I2C_C_ST;
|
||||
|
||||
/* Start transfer */
|
||||
if (writeval & (BCM2835_I2C_C_ST | BCM2835_I2C_C_I2CEN)) {
|
||||
bcm2835_i2c_begin_transfer(s);
|
||||
/*
|
||||
* Handle special case where transfer starts with zero data length.
|
||||
* Required for zero length i2c quick messages to work.
|
||||
*/
|
||||
if (s->dlen == 0) {
|
||||
bcm2835_i2c_finish_transfer(s);
|
||||
}
|
||||
}
|
||||
|
||||
bcm2835_i2c_update_interrupt(s);
|
||||
break;
|
||||
case BCM2835_I2C_S:
|
||||
if (writeval & BCM2835_I2C_S_DONE && s->s & BCM2835_I2C_S_DONE) {
|
||||
/* When DONE is cleared, DLEN should read last written value. */
|
||||
s->dlen = s->last_dlen;
|
||||
}
|
||||
|
||||
/* Clear DONE, CLKT and ERR by writing 1 */
|
||||
s->s &= ~(writeval & (BCM2835_I2C_S_DONE |
|
||||
BCM2835_I2C_S_ERR | BCM2835_I2C_S_CLKT));
|
||||
break;
|
||||
case BCM2835_I2C_DLEN:
|
||||
s->dlen = writeval;
|
||||
s->last_dlen = writeval;
|
||||
break;
|
||||
case BCM2835_I2C_A:
|
||||
s->a = writeval;
|
||||
break;
|
||||
case BCM2835_I2C_FIFO:
|
||||
/* We send I2C messages directly instead of using FIFOs */
|
||||
if (s->s & BCM2835_I2C_S_TA) {
|
||||
if (s->s & BCM2835_I2C_S_TXD) {
|
||||
if (!i2c_send(s->bus, writeval & 0xff)) {
|
||||
s->dlen -= 1;
|
||||
} else {
|
||||
s->s |= BCM2835_I2C_S_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
if (s->dlen == 0) {
|
||||
bcm2835_i2c_finish_transfer(s);
|
||||
}
|
||||
}
|
||||
bcm2835_i2c_update_interrupt(s);
|
||||
break;
|
||||
case BCM2835_I2C_DIV:
|
||||
s->div = writeval;
|
||||
break;
|
||||
case BCM2835_I2C_DEL:
|
||||
s->del = writeval;
|
||||
break;
|
||||
case BCM2835_I2C_CLKT:
|
||||
s->clkt = writeval;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, addr);
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps bcm2835_i2c_ops = {
|
||||
.read = bcm2835_i2c_read,
|
||||
.write = bcm2835_i2c_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
.valid = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 4,
|
||||
},
|
||||
};
|
||||
|
||||
static void bcm2835_i2c_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
BCM2835I2CState *s = BCM2835_I2C(dev);
|
||||
s->bus = i2c_init_bus(dev, NULL);
|
||||
|
||||
memory_region_init_io(&s->iomem, OBJECT(dev), &bcm2835_i2c_ops, s,
|
||||
TYPE_BCM2835_I2C, 0x24);
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem);
|
||||
sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq);
|
||||
}
|
||||
|
||||
static void bcm2835_i2c_reset(DeviceState *dev)
|
||||
{
|
||||
BCM2835I2CState *s = BCM2835_I2C(dev);
|
||||
|
||||
/* Reset values according to BCM2835 Peripheral Documentation */
|
||||
s->c = 0x0;
|
||||
s->s = BCM2835_I2C_S_TXD | BCM2835_I2C_S_TXE;
|
||||
s->dlen = 0x0;
|
||||
s->a = 0x0;
|
||||
s->div = 0x5dc;
|
||||
s->del = 0x00300030;
|
||||
s->clkt = 0x40;
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_bcm2835_i2c = {
|
||||
.name = TYPE_BCM2835_I2C,
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (const VMStateField[]) {
|
||||
VMSTATE_UINT32(c, BCM2835I2CState),
|
||||
VMSTATE_UINT32(s, BCM2835I2CState),
|
||||
VMSTATE_UINT32(dlen, BCM2835I2CState),
|
||||
VMSTATE_UINT32(a, BCM2835I2CState),
|
||||
VMSTATE_UINT32(div, BCM2835I2CState),
|
||||
VMSTATE_UINT32(del, BCM2835I2CState),
|
||||
VMSTATE_UINT32(clkt, BCM2835I2CState),
|
||||
VMSTATE_UINT32(last_dlen, BCM2835I2CState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void bcm2835_i2c_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->reset = bcm2835_i2c_reset;
|
||||
dc->realize = bcm2835_i2c_realize;
|
||||
dc->vmsd = &vmstate_bcm2835_i2c;
|
||||
}
|
||||
|
||||
static const TypeInfo bcm2835_i2c_info = {
|
||||
.name = TYPE_BCM2835_I2C,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(BCM2835I2CState),
|
||||
.class_init = bcm2835_i2c_class_init,
|
||||
};
|
||||
|
||||
static void bcm2835_i2c_register_types(void)
|
||||
{
|
||||
type_register_static(&bcm2835_i2c_info);
|
||||
}
|
||||
|
||||
type_init(bcm2835_i2c_register_types)
|
@ -17,4 +17,5 @@ i2c_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_i2c.c'))
|
||||
i2c_ss.add(when: 'CONFIG_PPC4XX', if_true: files('ppc4xx_i2c.c'))
|
||||
i2c_ss.add(when: 'CONFIG_PCA954X', if_true: files('i2c_mux_pca954x.c'))
|
||||
i2c_ss.add(when: 'CONFIG_PMBUS', if_true: files('pmbus_device.c'))
|
||||
i2c_ss.add(when: 'CONFIG_BCM2835_I2C', if_true: files('bcm2835_i2c.c'))
|
||||
system_ss.add_all(when: 'CONFIG_I2C', if_true: i2c_ss)
|
||||
|
@ -97,6 +97,9 @@ config STM32L4X5_EXTI
|
||||
config STM32L4X5_SYSCFG
|
||||
bool
|
||||
|
||||
config STM32L4X5_RCC
|
||||
bool
|
||||
|
||||
config MIPS_ITU
|
||||
bool
|
||||
|
||||
|
@ -113,6 +113,7 @@ system_ss.add(when: 'CONFIG_STM32F4XX_SYSCFG', if_true: files('stm32f4xx_syscfg.
|
||||
system_ss.add(when: 'CONFIG_STM32F4XX_EXTI', if_true: files('stm32f4xx_exti.c'))
|
||||
system_ss.add(when: 'CONFIG_STM32L4X5_EXTI', if_true: files('stm32l4x5_exti.c'))
|
||||
system_ss.add(when: 'CONFIG_STM32L4X5_SYSCFG', if_true: files('stm32l4x5_syscfg.c'))
|
||||
system_ss.add(when: 'CONFIG_STM32L4X5_RCC', if_true: files('stm32l4x5_rcc.c'))
|
||||
system_ss.add(when: 'CONFIG_MPS2_FPGAIO', if_true: files('mps2-fpgaio.c'))
|
||||
system_ss.add(when: 'CONFIG_MPS2_SCC', if_true: files('mps2-scc.c'))
|
||||
|
||||
|
1457
hw/misc/stm32l4x5_rcc.c
Normal file
1457
hw/misc/stm32l4x5_rcc.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -174,6 +174,20 @@ stm32l4x5_exti_set_irq(int irq, int level) "Set EXTI: %d to %d"
|
||||
stm32l4x5_exti_read(uint64_t addr, uint64_t data) "reg read: addr: 0x%" PRIx64 " val: 0x%" PRIx64 ""
|
||||
stm32l4x5_exti_write(uint64_t addr, uint64_t data) "reg write: addr: 0x%" PRIx64 " val: 0x%" PRIx64 ""
|
||||
|
||||
# stm32l4x5_rcc.c
|
||||
stm32l4x5_rcc_read(uint64_t addr, uint32_t data) "RCC: Read <0x%" PRIx64 "> -> 0x%" PRIx32
|
||||
stm32l4x5_rcc_write(uint64_t addr, uint32_t data) "RCC: Write <0x%" PRIx64 "> <- 0x%" PRIx32
|
||||
stm32l4x5_rcc_mux_enable(uint32_t mux_id) "RCC: Mux %d enabled"
|
||||
stm32l4x5_rcc_mux_disable(uint32_t mux_id) "RCC: Mux %d disabled"
|
||||
stm32l4x5_rcc_mux_set_factor(uint32_t mux_id, uint32_t old_multiplier, uint32_t new_multiplier, uint32_t old_divider, uint32_t new_divider) "RCC: Mux %d factor changed: multiplier (%u -> %u), divider (%u -> %u)"
|
||||
stm32l4x5_rcc_mux_set_src(uint32_t mux_id, uint32_t old_src, uint32_t new_src) "RCC: Mux %d source changed: from %u to %u"
|
||||
stm32l4x5_rcc_mux_update(uint32_t mux_id, uint32_t src, uint64_t src_freq, uint32_t multiplier, uint32_t divider) "RCC: Mux %d src %d update: src_freq %" PRIu64 " multiplier %" PRIu32 " divider %" PRIu32
|
||||
stm32l4x5_rcc_pll_set_vco_multiplier(uint32_t pll_id, uint32_t old_multiplier, uint32_t new_multiplier) "RCC: PLL %u: vco_multiplier changed (%u -> %u)"
|
||||
stm32l4x5_rcc_pll_channel_enable(uint32_t pll_id, uint32_t channel_id) "RCC: PLL %u, channel %u enabled"
|
||||
stm32l4x5_rcc_pll_channel_disable(uint32_t pll_id, uint32_t channel_id) "RCC: PLL %u, channel %u disabled"
|
||||
stm32l4x5_rcc_pll_set_channel_divider(uint32_t pll_id, uint32_t channel_id, uint32_t old_divider, uint32_t new_divider) "RCC: PLL %u, channel %u: divider changed (%u -> %u)"
|
||||
stm32l4x5_rcc_pll_update(uint32_t pll_id, uint32_t channel_id, uint64_t vco_freq, uint64_t old_freq, uint64_t new_freq) "RCC: PLL %d channel %d update: vco_freq %" PRIu64 " old_freq %" PRIu64 " new_freq %" PRIu64
|
||||
|
||||
# tz-mpc.c
|
||||
tz_mpc_reg_read(uint32_t offset, uint64_t data, unsigned size) "TZ MPC regs read: offset 0x%x data 0x%" PRIx64 " size %u"
|
||||
tz_mpc_reg_write(uint32_t offset, uint64_t data, unsigned size) "TZ MPC regs write: offset 0x%x data 0x%" PRIx64 " size %u"
|
||||
|
@ -357,8 +357,10 @@ static inline int cpu_mmu_index(CPUState *cs, bool ifetch)
|
||||
#define TLB_BSWAP (1 << 0)
|
||||
/* Set if TLB entry contains a watchpoint. */
|
||||
#define TLB_WATCHPOINT (1 << 1)
|
||||
/* Set if TLB entry requires aligned accesses. */
|
||||
#define TLB_CHECK_ALIGNED (1 << 2)
|
||||
|
||||
#define TLB_SLOW_FLAGS_MASK (TLB_BSWAP | TLB_WATCHPOINT)
|
||||
#define TLB_SLOW_FLAGS_MASK (TLB_BSWAP | TLB_WATCHPOINT | TLB_CHECK_ALIGNED)
|
||||
|
||||
/* The two sets of flags must not overlap. */
|
||||
QEMU_BUILD_BUG_ON(TLB_FLAGS_MASK & TLB_SLOW_FLAGS_MASK);
|
||||
|
@ -52,18 +52,6 @@ typedef struct MemTxAttrs {
|
||||
unsigned int memory:1;
|
||||
/* Requester ID (for MSI for example) */
|
||||
unsigned int requester_id:16;
|
||||
/* Invert endianness for this page */
|
||||
unsigned int byte_swap:1;
|
||||
/*
|
||||
* The following are target-specific page-table bits. These are not
|
||||
* related to actual memory transactions at all. However, this structure
|
||||
* is part of the tlb_fill interface, cached in the cputlb structure,
|
||||
* and has unused bits. These fields will be read by target-specific
|
||||
* helpers using env->iotlb[mmu_idx][tlb_index()].attrs.target_tlb_bitN.
|
||||
*/
|
||||
unsigned int target_tlb_bit0 : 1;
|
||||
unsigned int target_tlb_bit1 : 1;
|
||||
unsigned int target_tlb_bit2 : 1;
|
||||
} MemTxAttrs;
|
||||
|
||||
/* Bus masters which don't specify any attributes will get this,
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include "hw/timer/bcm2835_systmr.h"
|
||||
#include "hw/usb/hcd-dwc2.h"
|
||||
#include "hw/ssi/bcm2835_spi.h"
|
||||
#include "hw/i2c/bcm2835_i2c.h"
|
||||
#include "hw/misc/unimp.h"
|
||||
#include "qom/object.h"
|
||||
|
||||
@ -68,7 +69,8 @@ struct BCMSocPeripheralBaseState {
|
||||
BCM2835SDHostState sdhost;
|
||||
UnimplementedDeviceState i2s;
|
||||
BCM2835SPIState spi[1];
|
||||
UnimplementedDeviceState i2c[3];
|
||||
BCM2835I2CState i2c[3];
|
||||
OrIRQState orgated_i2c_irq;
|
||||
UnimplementedDeviceState otp;
|
||||
UnimplementedDeviceState dbus;
|
||||
UnimplementedDeviceState ave0;
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "hw/or-irq.h"
|
||||
#include "hw/misc/stm32l4x5_syscfg.h"
|
||||
#include "hw/misc/stm32l4x5_exti.h"
|
||||
#include "hw/misc/stm32l4x5_rcc.h"
|
||||
#include "qom/object.h"
|
||||
|
||||
#define TYPE_STM32L4X5_SOC "stm32l4x5-soc"
|
||||
@ -47,14 +48,12 @@ struct Stm32l4x5SocState {
|
||||
Stm32l4x5ExtiState exti;
|
||||
OrIRQState exti_or_gates[NUM_EXTI_OR_GATES];
|
||||
Stm32l4x5SyscfgState syscfg;
|
||||
Stm32l4x5RccState rcc;
|
||||
|
||||
MemoryRegion sram1;
|
||||
MemoryRegion sram2;
|
||||
MemoryRegion flash;
|
||||
MemoryRegion flash_alias;
|
||||
|
||||
Clock *sysclk;
|
||||
Clock *refclk;
|
||||
};
|
||||
|
||||
struct Stm32l4x5SocClass {
|
||||
|
@ -230,6 +230,9 @@ typedef struct CPUTLBEntryFull {
|
||||
/* @lg_page_size contains the log2 of the page size. */
|
||||
uint8_t lg_page_size;
|
||||
|
||||
/* Additional tlb flags requested by tlb_fill. */
|
||||
uint8_t tlb_fill_flags;
|
||||
|
||||
/*
|
||||
* Additional tlb flags for use by the slow path. If non-zero,
|
||||
* the corresponding CPUTLBEntry comparator must have TLB_FORCE_SLOW.
|
||||
|
80
include/hw/i2c/bcm2835_i2c.h
Normal file
80
include/hw/i2c/bcm2835_i2c.h
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Broadcom Serial Controller (BSC)
|
||||
*
|
||||
* Copyright (c) 2024 Rayhan Faizel <rayhan.faizel@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/i2c/i2c.h"
|
||||
#include "qom/object.h"
|
||||
|
||||
#define TYPE_BCM2835_I2C "bcm2835-i2c"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(BCM2835I2CState, BCM2835_I2C)
|
||||
|
||||
#define BCM2835_I2C_C 0x0 /* Control */
|
||||
#define BCM2835_I2C_S 0x4 /* Status */
|
||||
#define BCM2835_I2C_DLEN 0x8 /* Data Length */
|
||||
#define BCM2835_I2C_A 0xc /* Slave Address */
|
||||
#define BCM2835_I2C_FIFO 0x10 /* FIFO */
|
||||
#define BCM2835_I2C_DIV 0x14 /* Clock Divider */
|
||||
#define BCM2835_I2C_DEL 0x18 /* Data Delay */
|
||||
#define BCM2835_I2C_CLKT 0x20 /* Clock Stretch Timeout */
|
||||
|
||||
#define BCM2835_I2C_C_I2CEN BIT(15) /* I2C enable */
|
||||
#define BCM2835_I2C_C_INTR BIT(10) /* Interrupt on RXR */
|
||||
#define BCM2835_I2C_C_INTT BIT(9) /* Interrupt on TXW */
|
||||
#define BCM2835_I2C_C_INTD BIT(8) /* Interrupt on DONE */
|
||||
#define BCM2835_I2C_C_ST BIT(7) /* Start transfer */
|
||||
#define BCM2835_I2C_C_CLEAR (BIT(5) | BIT(4)) /* Clear FIFO */
|
||||
#define BCM2835_I2C_C_READ BIT(0) /* I2C read mode */
|
||||
|
||||
#define BCM2835_I2C_S_CLKT BIT(9) /* Clock stretch timeout */
|
||||
#define BCM2835_I2C_S_ERR BIT(8) /* Slave error */
|
||||
#define BCM2835_I2C_S_RXF BIT(7) /* RX FIFO full */
|
||||
#define BCM2835_I2C_S_TXE BIT(6) /* TX FIFO empty */
|
||||
#define BCM2835_I2C_S_RXD BIT(5) /* RX bytes available */
|
||||
#define BCM2835_I2C_S_TXD BIT(4) /* TX space available */
|
||||
#define BCM2835_I2C_S_RXR BIT(3) /* RX FIFO needs reading */
|
||||
#define BCM2835_I2C_S_TXW BIT(2) /* TX FIFO needs writing */
|
||||
#define BCM2835_I2C_S_DONE BIT(1) /* I2C Transfer complete */
|
||||
#define BCM2835_I2C_S_TA BIT(0) /* I2C Transfer active */
|
||||
|
||||
struct BCM2835I2CState {
|
||||
/* <private> */
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
/* <public> */
|
||||
MemoryRegion iomem;
|
||||
I2CBus *bus;
|
||||
qemu_irq irq;
|
||||
|
||||
uint32_t c;
|
||||
uint32_t s;
|
||||
uint32_t dlen;
|
||||
uint32_t a;
|
||||
uint32_t div;
|
||||
uint32_t del;
|
||||
uint32_t clkt;
|
||||
|
||||
uint32_t last_dlen;
|
||||
};
|
239
include/hw/misc/stm32l4x5_rcc.h
Normal file
239
include/hw/misc/stm32l4x5_rcc.h
Normal file
@ -0,0 +1,239 @@
|
||||
/*
|
||||
* STM32L4X5 RCC (Reset and clock control)
|
||||
*
|
||||
* Copyright (c) 2023 Arnaud Minier <arnaud.minier@telecom-paris.fr>
|
||||
* Copyright (c) 2023 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.
|
||||
*
|
||||
* Inspired by the BCM2835 CPRMAN clock manager by Luc Michel.
|
||||
*/
|
||||
|
||||
#ifndef HW_STM32L4X5_RCC_H
|
||||
#define HW_STM32L4X5_RCC_H
|
||||
|
||||
#include "hw/sysbus.h"
|
||||
#include "qom/object.h"
|
||||
|
||||
#define TYPE_STM32L4X5_RCC "stm32l4x5-rcc"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(Stm32l4x5RccState, STM32L4X5_RCC)
|
||||
|
||||
/* In the Stm32l4x5 clock tree, mux have at most 7 sources */
|
||||
#define RCC_NUM_CLOCK_MUX_SRC 7
|
||||
|
||||
typedef enum PllCommonChannels {
|
||||
RCC_PLL_COMMON_CHANNEL_P = 0,
|
||||
RCC_PLL_COMMON_CHANNEL_Q = 1,
|
||||
RCC_PLL_COMMON_CHANNEL_R = 2,
|
||||
|
||||
RCC_NUM_CHANNEL_PLL_OUT = 3
|
||||
} PllCommonChannels;
|
||||
|
||||
/* NB: Prescaler are assimilated to mux with one source and one output */
|
||||
typedef enum RccClockMux {
|
||||
/* Internal muxes that arent't exposed publicly to other peripherals */
|
||||
RCC_CLOCK_MUX_SYSCLK,
|
||||
RCC_CLOCK_MUX_PLL_INPUT,
|
||||
RCC_CLOCK_MUX_HCLK,
|
||||
RCC_CLOCK_MUX_PCLK1,
|
||||
RCC_CLOCK_MUX_PCLK2,
|
||||
RCC_CLOCK_MUX_HSE_OVER_32,
|
||||
RCC_CLOCK_MUX_LCD_AND_RTC_COMMON,
|
||||
|
||||
/* Muxes with a publicly available output */
|
||||
RCC_CLOCK_MUX_CORTEX_REFCLK,
|
||||
RCC_CLOCK_MUX_USART1,
|
||||
RCC_CLOCK_MUX_USART2,
|
||||
RCC_CLOCK_MUX_USART3,
|
||||
RCC_CLOCK_MUX_UART4,
|
||||
RCC_CLOCK_MUX_UART5,
|
||||
RCC_CLOCK_MUX_LPUART1,
|
||||
RCC_CLOCK_MUX_I2C1,
|
||||
RCC_CLOCK_MUX_I2C2,
|
||||
RCC_CLOCK_MUX_I2C3,
|
||||
RCC_CLOCK_MUX_LPTIM1,
|
||||
RCC_CLOCK_MUX_LPTIM2,
|
||||
RCC_CLOCK_MUX_SWPMI1,
|
||||
RCC_CLOCK_MUX_MCO,
|
||||
RCC_CLOCK_MUX_LSCO,
|
||||
RCC_CLOCK_MUX_DFSDM1,
|
||||
RCC_CLOCK_MUX_ADC,
|
||||
RCC_CLOCK_MUX_CLK48,
|
||||
RCC_CLOCK_MUX_SAI1,
|
||||
RCC_CLOCK_MUX_SAI2,
|
||||
|
||||
/*
|
||||
* Mux that have only one input and one output assigned to as peripheral.
|
||||
* They could be direct lines but it is simpler
|
||||
* to use the same logic for all outputs.
|
||||
*/
|
||||
/* - AHB1 */
|
||||
RCC_CLOCK_MUX_TSC,
|
||||
RCC_CLOCK_MUX_CRC,
|
||||
RCC_CLOCK_MUX_FLASH,
|
||||
RCC_CLOCK_MUX_DMA2,
|
||||
RCC_CLOCK_MUX_DMA1,
|
||||
|
||||
/* - AHB2 */
|
||||
RCC_CLOCK_MUX_RNG,
|
||||
RCC_CLOCK_MUX_AES,
|
||||
RCC_CLOCK_MUX_OTGFS,
|
||||
RCC_CLOCK_MUX_GPIOA,
|
||||
RCC_CLOCK_MUX_GPIOB,
|
||||
RCC_CLOCK_MUX_GPIOC,
|
||||
RCC_CLOCK_MUX_GPIOD,
|
||||
RCC_CLOCK_MUX_GPIOE,
|
||||
RCC_CLOCK_MUX_GPIOF,
|
||||
RCC_CLOCK_MUX_GPIOG,
|
||||
RCC_CLOCK_MUX_GPIOH,
|
||||
|
||||
/* - AHB3 */
|
||||
RCC_CLOCK_MUX_QSPI,
|
||||
RCC_CLOCK_MUX_FMC,
|
||||
|
||||
/* - APB1 */
|
||||
RCC_CLOCK_MUX_OPAMP,
|
||||
RCC_CLOCK_MUX_DAC1,
|
||||
RCC_CLOCK_MUX_PWR,
|
||||
RCC_CLOCK_MUX_CAN1,
|
||||
RCC_CLOCK_MUX_SPI3,
|
||||
RCC_CLOCK_MUX_SPI2,
|
||||
RCC_CLOCK_MUX_WWDG,
|
||||
RCC_CLOCK_MUX_LCD,
|
||||
RCC_CLOCK_MUX_TIM7,
|
||||
RCC_CLOCK_MUX_TIM6,
|
||||
RCC_CLOCK_MUX_TIM5,
|
||||
RCC_CLOCK_MUX_TIM4,
|
||||
RCC_CLOCK_MUX_TIM3,
|
||||
RCC_CLOCK_MUX_TIM2,
|
||||
|
||||
/* - APB2 */
|
||||
RCC_CLOCK_MUX_TIM17,
|
||||
RCC_CLOCK_MUX_TIM16,
|
||||
RCC_CLOCK_MUX_TIM15,
|
||||
RCC_CLOCK_MUX_TIM8,
|
||||
RCC_CLOCK_MUX_SPI1,
|
||||
RCC_CLOCK_MUX_TIM1,
|
||||
RCC_CLOCK_MUX_SDMMC1,
|
||||
RCC_CLOCK_MUX_FW,
|
||||
RCC_CLOCK_MUX_SYSCFG,
|
||||
|
||||
/* - BDCR */
|
||||
RCC_CLOCK_MUX_RTC,
|
||||
|
||||
/* - OTHER */
|
||||
RCC_CLOCK_MUX_CORTEX_FCLK,
|
||||
|
||||
RCC_NUM_CLOCK_MUX
|
||||
} RccClockMux;
|
||||
|
||||
typedef enum RccPll {
|
||||
RCC_PLL_PLL,
|
||||
RCC_PLL_PLLSAI1,
|
||||
RCC_PLL_PLLSAI2,
|
||||
|
||||
RCC_NUM_PLL
|
||||
} RccPll;
|
||||
|
||||
typedef struct RccClockMuxState {
|
||||
DeviceState parent_obj;
|
||||
|
||||
RccClockMux id;
|
||||
Clock *srcs[RCC_NUM_CLOCK_MUX_SRC];
|
||||
Clock *out;
|
||||
bool enabled;
|
||||
uint32_t src;
|
||||
uint32_t multiplier;
|
||||
uint32_t divider;
|
||||
|
||||
/*
|
||||
* Used by clock srcs update callback to retrieve both the clock and the
|
||||
* source number.
|
||||
*/
|
||||
struct RccClockMuxState *backref[RCC_NUM_CLOCK_MUX_SRC];
|
||||
} RccClockMuxState;
|
||||
|
||||
typedef struct RccPllState {
|
||||
DeviceState parent_obj;
|
||||
|
||||
RccPll id;
|
||||
Clock *in;
|
||||
uint32_t vco_multiplier;
|
||||
Clock *channels[RCC_NUM_CHANNEL_PLL_OUT];
|
||||
/* Global pll enabled flag */
|
||||
bool enabled;
|
||||
/* 'enabled' refers to the runtime configuration */
|
||||
bool channel_enabled[RCC_NUM_CHANNEL_PLL_OUT];
|
||||
/*
|
||||
* 'exists' refers to the physical configuration
|
||||
* It should only be set at pll initialization.
|
||||
* e.g. pllsai2 doesn't have a Q output.
|
||||
*/
|
||||
bool channel_exists[RCC_NUM_CHANNEL_PLL_OUT];
|
||||
uint32_t channel_divider[RCC_NUM_CHANNEL_PLL_OUT];
|
||||
} RccPllState;
|
||||
|
||||
struct Stm32l4x5RccState {
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
MemoryRegion mmio;
|
||||
|
||||
uint32_t cr;
|
||||
uint32_t icscr;
|
||||
uint32_t cfgr;
|
||||
uint32_t pllcfgr;
|
||||
uint32_t pllsai1cfgr;
|
||||
uint32_t pllsai2cfgr;
|
||||
uint32_t cier;
|
||||
uint32_t cifr;
|
||||
uint32_t ahb1rstr;
|
||||
uint32_t ahb2rstr;
|
||||
uint32_t ahb3rstr;
|
||||
uint32_t apb1rstr1;
|
||||
uint32_t apb1rstr2;
|
||||
uint32_t apb2rstr;
|
||||
uint32_t ahb1enr;
|
||||
uint32_t ahb2enr;
|
||||
uint32_t ahb3enr;
|
||||
uint32_t apb1enr1;
|
||||
uint32_t apb1enr2;
|
||||
uint32_t apb2enr;
|
||||
uint32_t ahb1smenr;
|
||||
uint32_t ahb2smenr;
|
||||
uint32_t ahb3smenr;
|
||||
uint32_t apb1smenr1;
|
||||
uint32_t apb1smenr2;
|
||||
uint32_t apb2smenr;
|
||||
uint32_t ccipr;
|
||||
uint32_t bdcr;
|
||||
uint32_t csr;
|
||||
|
||||
/* Clock sources */
|
||||
Clock *gnd;
|
||||
Clock *hsi16_rc;
|
||||
Clock *msi_rc;
|
||||
Clock *hse;
|
||||
Clock *lsi_rc;
|
||||
Clock *lse_crystal;
|
||||
Clock *sai1_extclk;
|
||||
Clock *sai2_extclk;
|
||||
|
||||
/* PLLs */
|
||||
RccPllState plls[RCC_NUM_PLL];
|
||||
|
||||
/* Muxes ~= outputs */
|
||||
RccClockMuxState clock_muxes[RCC_NUM_CLOCK_MUX];
|
||||
|
||||
qemu_irq irq;
|
||||
uint64_t hse_frequency;
|
||||
uint64_t sai1_extclk_frequency;
|
||||
uint64_t sai2_extclk_frequency;
|
||||
};
|
||||
|
||||
#endif /* HW_STM32L4X5_RCC_H */
|
1042
include/hw/misc/stm32l4x5_rcc_internals.h
Normal file
1042
include/hw/misc/stm32l4x5_rcc_internals.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -202,7 +202,7 @@
|
||||
qatomic_xchg__nocheck(ptr, i); \
|
||||
})
|
||||
|
||||
/* Returns the eventual value, failed or not */
|
||||
/* Returns the old value of '*ptr' (whether the cmpxchg failed or not) */
|
||||
#define qatomic_cmpxchg__nocheck(ptr, old, new) ({ \
|
||||
typeof_strip_qual(*ptr) _old = (old); \
|
||||
(void)__atomic_compare_exchange_n(ptr, &_old, new, false, \
|
||||
|
@ -4129,7 +4129,7 @@ SRST
|
||||
default device is ``vc`` in graphical mode and ``stdio`` in non
|
||||
graphical mode.
|
||||
|
||||
This option can be used several times to simulate up to 4 serial
|
||||
This option can be used several times to simulate multiple serial
|
||||
ports.
|
||||
|
||||
You can use ``-serial none`` to suppress the creation of default
|
||||
|
@ -471,6 +471,16 @@ static bool granule_protection_check(CPUARMState *env, uint64_t paddress,
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool S1_attrs_are_device(uint8_t attrs)
|
||||
{
|
||||
/*
|
||||
* This slightly under-decodes the MAIR_ELx field:
|
||||
* 0b0000dd01 is Device with FEAT_XS, otherwise UNPREDICTABLE;
|
||||
* 0b0000dd1x is UNPREDICTABLE.
|
||||
*/
|
||||
return (attrs & 0xf0) == 0;
|
||||
}
|
||||
|
||||
static bool S2_attrs_are_device(uint64_t hcr, uint8_t attrs)
|
||||
{
|
||||
/*
|
||||
@ -1684,6 +1694,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw,
|
||||
bool aarch64 = arm_el_is_aa64(env, el);
|
||||
uint64_t descriptor, new_descriptor;
|
||||
ARMSecuritySpace out_space;
|
||||
bool device;
|
||||
|
||||
/* TODO: This code does not support shareability levels. */
|
||||
if (aarch64) {
|
||||
@ -2106,6 +2117,12 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw,
|
||||
if (regime_is_stage2(mmu_idx)) {
|
||||
result->cacheattrs.is_s2_format = true;
|
||||
result->cacheattrs.attrs = extract32(attrs, 2, 4);
|
||||
/*
|
||||
* Security state does not really affect HCR_EL2.FWB;
|
||||
* we only need to filter FWB for aa32 or other FEAT.
|
||||
*/
|
||||
device = S2_attrs_are_device(arm_hcr_el2_eff(env),
|
||||
result->cacheattrs.attrs);
|
||||
} else {
|
||||
/* Index into MAIR registers for cache attributes */
|
||||
uint8_t attrindx = extract32(attrs, 2, 3);
|
||||
@ -2118,6 +2135,28 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw,
|
||||
if (aarch64 && cpu_isar_feature(aa64_bti, cpu)) {
|
||||
result->f.extra.arm.guarded = extract64(attrs, 50, 1); /* GP */
|
||||
}
|
||||
device = S1_attrs_are_device(result->cacheattrs.attrs);
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable alignment checks on Device memory.
|
||||
*
|
||||
* Per R_XCHFJ, this check is mis-ordered. The correct ordering
|
||||
* for alignment, permission, and stage 2 faults should be:
|
||||
* - Alignment fault caused by the memory type
|
||||
* - Permission fault
|
||||
* - A stage 2 fault on the memory access
|
||||
* but due to the way the TCG softmmu TLB operates, we will have
|
||||
* implicitly done the permission check and the stage2 lookup in
|
||||
* finding the TLB entry, so the alignment check cannot be done sooner.
|
||||
*
|
||||
* In v7, for a CPU without the Virtualization Extensions this
|
||||
* access is UNPREDICTABLE; we choose to make it take the alignment
|
||||
* fault as is required for a v7VE CPU. (QEMU doesn't emulate any
|
||||
* CPUs with ARM_FEATURE_LPAE but not ARM_FEATURE_V7VE anyway.)
|
||||
*/
|
||||
if (device) {
|
||||
result->f.tlb_fill_flags |= TLB_CHECK_ALIGNED;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -26,6 +26,35 @@ static inline bool fgt_svc(CPUARMState *env, int el)
|
||||
FIELD_EX64(env->cp15.fgt_exec[FGTREG_HFGITR], HFGITR_EL2, SVC_EL1);
|
||||
}
|
||||
|
||||
/* Return true if memory alignment should be enforced. */
|
||||
static bool aprofile_require_alignment(CPUARMState *env, int el, uint64_t sctlr)
|
||||
{
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
return false;
|
||||
#else
|
||||
/* Check the alignment enable bit. */
|
||||
if (sctlr & SCTLR_A) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* If translation is disabled, then the default memory type is
|
||||
* Device(-nGnRnE) instead of Normal, which requires that alignment
|
||||
* be enforced. Since this affects all ram, it is most efficient
|
||||
* to handle this during translation.
|
||||
*/
|
||||
if (sctlr & SCTLR_M) {
|
||||
/* Translation enabled: memory type in PTE via MAIR_ELx. */
|
||||
return false;
|
||||
}
|
||||
if (el < 2 && (arm_hcr_el2_eff(env) & (HCR_DC | HCR_VM))) {
|
||||
/* Stage 2 translation enabled: memory type in PTE. */
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
static CPUARMTBFlags rebuild_hflags_common(CPUARMState *env, int fp_el,
|
||||
ARMMMUIdx mmu_idx,
|
||||
CPUARMTBFlags flags)
|
||||
@ -121,8 +150,9 @@ static CPUARMTBFlags rebuild_hflags_a32(CPUARMState *env, int fp_el,
|
||||
{
|
||||
CPUARMTBFlags flags = {};
|
||||
int el = arm_current_el(env);
|
||||
uint64_t sctlr = arm_sctlr(env, el);
|
||||
|
||||
if (arm_sctlr(env, el) & SCTLR_A) {
|
||||
if (aprofile_require_alignment(env, el, sctlr)) {
|
||||
DP_TBFLAG_ANY(flags, ALIGN_MEM, 1);
|
||||
}
|
||||
|
||||
@ -223,7 +253,7 @@ static CPUARMTBFlags rebuild_hflags_a64(CPUARMState *env, int el, int fp_el,
|
||||
|
||||
sctlr = regime_sctlr(env, stage1);
|
||||
|
||||
if (sctlr & SCTLR_A) {
|
||||
if (aprofile_require_alignment(env, el, sctlr)) {
|
||||
DP_TBFLAG_ANY(flags, ALIGN_MEM, 1);
|
||||
}
|
||||
|
||||
|
@ -900,13 +900,7 @@ static inline void store_reg_from_load(DisasContext *s, int reg, TCGv_i32 var)
|
||||
MemOp pow2_align(unsigned i)
|
||||
{
|
||||
static const MemOp mop_align[] = {
|
||||
0, MO_ALIGN_2, MO_ALIGN_4, MO_ALIGN_8, MO_ALIGN_16,
|
||||
/*
|
||||
* FIXME: TARGET_PAGE_BITS_MIN affects TLB_FLAGS_MASK such
|
||||
* that 256-bit alignment (MO_ALIGN_32) cannot be supported:
|
||||
* see get_alignment_bits(). Enforce only 128-bit alignment for now.
|
||||
*/
|
||||
MO_ALIGN_16
|
||||
0, MO_ALIGN_2, MO_ALIGN_4, MO_ALIGN_8, MO_ALIGN_16, MO_ALIGN_32
|
||||
};
|
||||
g_assert(i < ARRAY_SIZE(mop_align));
|
||||
return mop_align[i];
|
||||
|
@ -580,7 +580,7 @@ static int get_physical_address_data(CPUSPARCState *env, CPUTLBEntryFull *full,
|
||||
int do_fault = 0;
|
||||
|
||||
if (TTE_IS_IE(env->dtlb[i].tte)) {
|
||||
full->attrs.byte_swap = true;
|
||||
full->tlb_fill_flags |= TLB_BSWAP;
|
||||
}
|
||||
|
||||
/* access ok? */
|
||||
|
115
tests/qtest/bcm2835-i2c-test.c
Normal file
115
tests/qtest/bcm2835-i2c-test.c
Normal file
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* QTest testcase for Broadcom Serial Controller (BSC)
|
||||
*
|
||||
* Copyright (c) 2024 Rayhan Faizel <rayhan.faizel@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "libqtest-single.h"
|
||||
|
||||
#include "hw/i2c/bcm2835_i2c.h"
|
||||
#include "hw/sensor/tmp105_regs.h"
|
||||
|
||||
static const uint32_t bsc_base_addrs[] = {
|
||||
0x3f205000, /* I2C0 */
|
||||
0x3f804000, /* I2C1 */
|
||||
0x3f805000, /* I2C2 */
|
||||
};
|
||||
|
||||
static void bcm2835_i2c_init_transfer(uint32_t base_addr, bool read)
|
||||
{
|
||||
/* read flag is bit 0 so we can write it directly */
|
||||
int interrupt = read ? BCM2835_I2C_C_INTR : BCM2835_I2C_C_INTT;
|
||||
|
||||
writel(base_addr + BCM2835_I2C_C,
|
||||
BCM2835_I2C_C_I2CEN | BCM2835_I2C_C_INTD |
|
||||
BCM2835_I2C_C_ST | BCM2835_I2C_C_CLEAR | interrupt | read);
|
||||
}
|
||||
|
||||
static void test_i2c_read_write(gconstpointer data)
|
||||
{
|
||||
uint32_t i2cdata;
|
||||
intptr_t index = (intptr_t) data;
|
||||
uint32_t base_addr = bsc_base_addrs[index];
|
||||
|
||||
/* Write to TMP105 register */
|
||||
writel(base_addr + BCM2835_I2C_A, 0x50);
|
||||
writel(base_addr + BCM2835_I2C_DLEN, 3);
|
||||
|
||||
bcm2835_i2c_init_transfer(base_addr, 0);
|
||||
|
||||
writel(base_addr + BCM2835_I2C_FIFO, TMP105_REG_T_HIGH);
|
||||
writel(base_addr + BCM2835_I2C_FIFO, 0xde);
|
||||
writel(base_addr + BCM2835_I2C_FIFO, 0xad);
|
||||
|
||||
/* Clear flags */
|
||||
writel(base_addr + BCM2835_I2C_S, BCM2835_I2C_S_DONE | BCM2835_I2C_S_ERR |
|
||||
BCM2835_I2C_S_CLKT);
|
||||
|
||||
/* Read from TMP105 register */
|
||||
writel(base_addr + BCM2835_I2C_A, 0x50);
|
||||
writel(base_addr + BCM2835_I2C_DLEN, 1);
|
||||
|
||||
bcm2835_i2c_init_transfer(base_addr, 0);
|
||||
|
||||
writel(base_addr + BCM2835_I2C_FIFO, TMP105_REG_T_HIGH);
|
||||
|
||||
writel(base_addr + BCM2835_I2C_DLEN, 2);
|
||||
bcm2835_i2c_init_transfer(base_addr, 1);
|
||||
|
||||
i2cdata = readl(base_addr + BCM2835_I2C_FIFO);
|
||||
g_assert_cmpint(i2cdata, ==, 0xde);
|
||||
|
||||
i2cdata = readl(base_addr + BCM2835_I2C_FIFO);
|
||||
g_assert_cmpint(i2cdata, ==, 0xad);
|
||||
|
||||
/* Clear flags */
|
||||
writel(base_addr + BCM2835_I2C_S, BCM2835_I2C_S_DONE | BCM2835_I2C_S_ERR |
|
||||
BCM2835_I2C_S_CLKT);
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
g_autofree char *test_name =
|
||||
g_strdup_printf("/bcm2835/bcm2835-i2c%d/read_write", i);
|
||||
qtest_add_data_func(test_name, (void *)(intptr_t) i,
|
||||
test_i2c_read_write);
|
||||
}
|
||||
|
||||
/* Run I2C tests with TMP105 slaves on all three buses */
|
||||
qtest_start("-M raspi3b "
|
||||
"-device tmp105,address=0x50,bus=i2c-bus.0 "
|
||||
"-device tmp105,address=0x50,bus=i2c-bus.1 "
|
||||
"-device tmp105,address=0x50,bus=i2c-bus.2");
|
||||
ret = g_test_run();
|
||||
qtest_end();
|
||||
|
||||
return ret;
|
||||
}
|
@ -203,7 +203,8 @@ qtests_aspeed = \
|
||||
|
||||
qtests_stm32l4x5 = \
|
||||
['stm32l4x5_exti-test',
|
||||
'stm32l4x5_syscfg-test']
|
||||
'stm32l4x5_syscfg-test',
|
||||
'stm32l4x5_rcc-test']
|
||||
|
||||
qtests_arm = \
|
||||
(config_all_devices.has_key('CONFIG_MPS2') ? ['sse-timer-test'] : []) + \
|
||||
@ -230,7 +231,7 @@ qtests_aarch64 = \
|
||||
['tpm-tis-device-test', 'tpm-tis-device-swtpm-test'] : []) + \
|
||||
(config_all_devices.has_key('CONFIG_XLNX_ZYNQMP_ARM') ? ['xlnx-can-test', 'fuzz-xlnx-dp-test'] : []) + \
|
||||
(config_all_devices.has_key('CONFIG_XLNX_VERSAL') ? ['xlnx-canfd-test', 'xlnx-versal-trng-test'] : []) + \
|
||||
(config_all_devices.has_key('CONFIG_RASPI') ? ['bcm2835-dma-test'] : []) + \
|
||||
(config_all_devices.has_key('CONFIG_RASPI') ? ['bcm2835-dma-test', 'bcm2835-i2c-test'] : []) + \
|
||||
(config_all_accel.has_key('CONFIG_TCG') and \
|
||||
config_all_devices.has_key('CONFIG_TPM_TIS_I2C') ? ['tpm-tis-i2c-test'] : []) + \
|
||||
['arm-cpu-features',
|
||||
|
189
tests/qtest/stm32l4x5_rcc-test.c
Normal file
189
tests/qtest/stm32l4x5_rcc-test.c
Normal file
@ -0,0 +1,189 @@
|
||||
/*
|
||||
* QTest testcase for STM32L4x5_RCC
|
||||
*
|
||||
* Copyright (c) 2023 Arnaud Minier <arnaud.minier@telecom-paris.fr>
|
||||
* Copyright (c) 2023 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.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/registerfields.h"
|
||||
#include "libqtest-single.h"
|
||||
#include "hw/misc/stm32l4x5_rcc_internals.h"
|
||||
|
||||
#define RCC_BASE_ADDR 0x40021000
|
||||
#define NVIC_ISER 0xE000E100
|
||||
#define NVIC_ISPR 0xE000E200
|
||||
#define NVIC_ICPR 0xE000E280
|
||||
#define RCC_IRQ 5
|
||||
|
||||
static void enable_nvic_irq(unsigned int n)
|
||||
{
|
||||
writel(NVIC_ISER, 1 << n);
|
||||
}
|
||||
|
||||
static void unpend_nvic_irq(unsigned int n)
|
||||
{
|
||||
writel(NVIC_ICPR, 1 << n);
|
||||
}
|
||||
|
||||
static bool check_nvic_pending(unsigned int n)
|
||||
{
|
||||
return readl(NVIC_ISPR) & (1 << n);
|
||||
}
|
||||
|
||||
static void rcc_writel(unsigned int offset, uint32_t value)
|
||||
{
|
||||
writel(RCC_BASE_ADDR + offset, value);
|
||||
}
|
||||
|
||||
static uint32_t rcc_readl(unsigned int offset)
|
||||
{
|
||||
return readl(RCC_BASE_ADDR + offset);
|
||||
}
|
||||
|
||||
static void test_init_msi(void)
|
||||
{
|
||||
/* MSIRANGE can be set only when MSI is OFF or READY */
|
||||
rcc_writel(A_CR, R_CR_MSION_MASK);
|
||||
/* Wait until MSI is stable */
|
||||
g_assert_true((rcc_readl(A_CR) & R_CR_MSIRDY_MASK) == R_CR_MSIRDY_MASK);
|
||||
/* TODO find a way to test MSI value */
|
||||
}
|
||||
|
||||
static void test_set_msi_as_sysclk(void)
|
||||
{
|
||||
/* Clocking from MSI, in case MSI was not the default source */
|
||||
rcc_writel(A_CFGR, 0);
|
||||
/* Wait until MSI is selected and stable */
|
||||
g_assert_true((rcc_readl(A_CFGR) & R_CFGR_SWS_MASK) == 0);
|
||||
}
|
||||
|
||||
static void test_init_pll(void)
|
||||
{
|
||||
uint32_t value;
|
||||
|
||||
/*
|
||||
* Update PLL and set MSI as the source clock.
|
||||
* PLLM = 1 --> 000
|
||||
* PLLN = 40 --> 40
|
||||
* PPLLR = 2 --> 00
|
||||
* PLLDIV = unused, PLLP = unused (SAI3), PLLQ = unused (48M1)
|
||||
* SRC = MSI --> 01
|
||||
*/
|
||||
rcc_writel(A_PLLCFGR, R_PLLCFGR_PLLREN_MASK |
|
||||
(40 << R_PLLCFGR_PLLN_SHIFT) |
|
||||
(0b01 << R_PLLCFGR_PLLSRC_SHIFT));
|
||||
|
||||
/* PLL activation */
|
||||
value = rcc_readl(A_CR);
|
||||
rcc_writel(A_CR, value | R_CR_PLLON_MASK);
|
||||
|
||||
/* Waiting for PLL lock. */
|
||||
g_assert_true((rcc_readl(A_CR) & R_CR_PLLRDY_MASK) == R_CR_PLLRDY_MASK);
|
||||
|
||||
/* Switches on the PLL clock source */
|
||||
value = rcc_readl(A_CFGR);
|
||||
rcc_writel(A_CFGR, (value & ~R_CFGR_SW_MASK) |
|
||||
(0b11 << R_CFGR_SW_SHIFT));
|
||||
|
||||
/* Wait until SYSCLK is stable. */
|
||||
g_assert_true((rcc_readl(A_CFGR) & R_CFGR_SWS_MASK) ==
|
||||
(0b11 << R_CFGR_SWS_SHIFT));
|
||||
}
|
||||
|
||||
static void test_activate_lse(void)
|
||||
{
|
||||
/* LSE activation, no LSE Bypass */
|
||||
rcc_writel(A_BDCR, R_BDCR_LSEDRV_MASK | R_BDCR_LSEON_MASK);
|
||||
g_assert_true((rcc_readl(A_BDCR) & R_BDCR_LSERDY_MASK) == R_BDCR_LSERDY_MASK);
|
||||
}
|
||||
|
||||
static void test_irq(void)
|
||||
{
|
||||
enable_nvic_irq(RCC_IRQ);
|
||||
|
||||
rcc_writel(A_CIER, R_CIER_LSIRDYIE_MASK);
|
||||
rcc_writel(A_CSR, R_CSR_LSION_MASK);
|
||||
g_assert_true(check_nvic_pending(RCC_IRQ));
|
||||
rcc_writel(A_CICR, R_CICR_LSIRDYC_MASK);
|
||||
unpend_nvic_irq(RCC_IRQ);
|
||||
|
||||
rcc_writel(A_CIER, R_CIER_LSERDYIE_MASK);
|
||||
rcc_writel(A_BDCR, R_BDCR_LSEON_MASK);
|
||||
g_assert_true(check_nvic_pending(RCC_IRQ));
|
||||
rcc_writel(A_CICR, R_CICR_LSERDYC_MASK);
|
||||
unpend_nvic_irq(RCC_IRQ);
|
||||
|
||||
/*
|
||||
* MSI has been enabled by previous tests,
|
||||
* shouln't generate an interruption.
|
||||
*/
|
||||
rcc_writel(A_CIER, R_CIER_MSIRDYIE_MASK);
|
||||
rcc_writel(A_CR, R_CR_MSION_MASK);
|
||||
g_assert_false(check_nvic_pending(RCC_IRQ));
|
||||
|
||||
rcc_writel(A_CIER, R_CIER_HSIRDYIE_MASK);
|
||||
rcc_writel(A_CR, R_CR_HSION_MASK);
|
||||
g_assert_true(check_nvic_pending(RCC_IRQ));
|
||||
rcc_writel(A_CICR, R_CICR_HSIRDYC_MASK);
|
||||
unpend_nvic_irq(RCC_IRQ);
|
||||
|
||||
rcc_writel(A_CIER, R_CIER_HSERDYIE_MASK);
|
||||
rcc_writel(A_CR, R_CR_HSEON_MASK);
|
||||
g_assert_true(check_nvic_pending(RCC_IRQ));
|
||||
rcc_writel(A_CICR, R_CICR_HSERDYC_MASK);
|
||||
unpend_nvic_irq(RCC_IRQ);
|
||||
|
||||
/*
|
||||
* PLL has been enabled by previous tests,
|
||||
* shouln't generate an interruption.
|
||||
*/
|
||||
rcc_writel(A_CIER, R_CIER_PLLRDYIE_MASK);
|
||||
rcc_writel(A_CR, R_CR_PLLON_MASK);
|
||||
g_assert_false(check_nvic_pending(RCC_IRQ));
|
||||
|
||||
rcc_writel(A_CIER, R_CIER_PLLSAI1RDYIE_MASK);
|
||||
rcc_writel(A_CR, R_CR_PLLSAI1ON_MASK);
|
||||
g_assert_true(check_nvic_pending(RCC_IRQ));
|
||||
rcc_writel(A_CICR, R_CICR_PLLSAI1RDYC_MASK);
|
||||
unpend_nvic_irq(RCC_IRQ);
|
||||
|
||||
rcc_writel(A_CIER, R_CIER_PLLSAI2RDYIE_MASK);
|
||||
rcc_writel(A_CR, R_CR_PLLSAI2ON_MASK);
|
||||
g_assert_true(check_nvic_pending(RCC_IRQ));
|
||||
rcc_writel(A_CICR, R_CICR_PLLSAI2RDYC_MASK);
|
||||
unpend_nvic_irq(RCC_IRQ);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
g_test_set_nonfatal_assertions();
|
||||
/*
|
||||
* These test separately that we can enable the plls, change the sysclk,
|
||||
* and enable different devices.
|
||||
* They are dependent on one another.
|
||||
* We assume that all operations that would take some time to have an effect
|
||||
* (e.g. changing the PLL frequency) are done instantaneously.
|
||||
*/
|
||||
qtest_add_func("stm32l4x5/rcc/init_msi", test_init_msi);
|
||||
qtest_add_func("stm32l4x5/rcc/set_msi_as_sysclk",
|
||||
test_set_msi_as_sysclk);
|
||||
qtest_add_func("stm32l4x5/rcc/activate_lse", test_activate_lse);
|
||||
qtest_add_func("stm32l4x5/rcc/init_pll", test_init_pll);
|
||||
|
||||
qtest_add_func("stm32l4x5/rcc/irq", test_irq);
|
||||
|
||||
qtest_start("-machine b-l475e-iot01a");
|
||||
ret = g_test_run();
|
||||
qtest_end();
|
||||
|
||||
return ret;
|
||||
}
|
Loading…
Reference in New Issue
Block a user