* [PULL 01/20] hw/i2c: Implement Broadcom Serial Controller (BSC)
2024-03-05 13:52 [PULL 00/20] target-arm queue Peter Maydell
@ 2024-03-05 13:52 ` Peter Maydell
2024-03-05 13:52 ` [PULL 02/20] hw/arm: Connect BSC to BCM2835 board as I2C0, I2C1 and I2C2 Peter Maydell
` (19 subsequent siblings)
20 siblings, 0 replies; 37+ messages in thread
From: Peter Maydell @ 2024-03-05 13:52 UTC (permalink / raw)
To: qemu-devel
From: Rayhan Faizel <rayhan.faizel@gmail.com>
A few deficiencies in the current device model need to be noted.
1. FIFOs are not used. All sends and receives are done directly.
2. Repeated starts are not emulated. Repeated starts can be triggered in real
hardware by sending a new read transfer request in the window time between
transfer active set of write transfer request and done bit set of the same.
Signed-off-by: Rayhan Faizel <rayhan.faizel@gmail.com>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Message-id: 20240224191038.2409945-2-rayhan.faizel@gmail.com
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
docs/system/arm/raspi.rst | 1 +
include/hw/i2c/bcm2835_i2c.h | 80 ++++++++++
hw/i2c/bcm2835_i2c.c | 282 +++++++++++++++++++++++++++++++++++
hw/i2c/Kconfig | 4 +
hw/i2c/meson.build | 1 +
5 files changed, 368 insertions(+)
create mode 100644 include/hw/i2c/bcm2835_i2c.h
create mode 100644 hw/i2c/bcm2835_i2c.c
diff --git a/docs/system/arm/raspi.rst b/docs/system/arm/raspi.rst
index bb417c34241..fbec1da6a1e 100644
--- a/docs/system/arm/raspi.rst
+++ b/docs/system/arm/raspi.rst
@@ -35,6 +35,7 @@ Implemented devices
* MailBox controller (MBOX)
* VideoCore firmware (property)
* Peripheral SPI controller (SPI)
+ * Broadcom Serial Controller (I2C)
Missing devices
---------------
diff --git a/include/hw/i2c/bcm2835_i2c.h b/include/hw/i2c/bcm2835_i2c.h
new file mode 100644
index 00000000000..0a56df4720b
--- /dev/null
+++ b/include/hw/i2c/bcm2835_i2c.h
@@ -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;
+};
diff --git a/hw/i2c/bcm2835_i2c.c b/hw/i2c/bcm2835_i2c.c
new file mode 100644
index 00000000000..20ec46eeabc
--- /dev/null
+++ b/hw/i2c/bcm2835_i2c.c
@@ -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)
diff --git a/hw/i2c/Kconfig b/hw/i2c/Kconfig
index 14886b35dac..596a7a3165a 100644
--- a/hw/i2c/Kconfig
+++ b/hw/i2c/Kconfig
@@ -45,3 +45,7 @@ config PCA954X
config PMBUS
bool
select SMBUS
+
+config BCM2835_I2C
+ bool
+ select I2C
diff --git a/hw/i2c/meson.build b/hw/i2c/meson.build
index b58bc167dbc..c459adcb596 100644
--- a/hw/i2c/meson.build
+++ b/hw/i2c/meson.build
@@ -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)
--
2.34.1
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [PULL 02/20] hw/arm: Connect BSC to BCM2835 board as I2C0, I2C1 and I2C2
2024-03-05 13:52 [PULL 00/20] target-arm queue Peter Maydell
2024-03-05 13:52 ` [PULL 01/20] hw/i2c: Implement Broadcom Serial Controller (BSC) Peter Maydell
@ 2024-03-05 13:52 ` Peter Maydell
2024-03-05 13:52 ` [PULL 03/20] tests/qtest: Add testcase for BCM2835 BSC Peter Maydell
` (18 subsequent siblings)
20 siblings, 0 replies; 37+ messages in thread
From: Peter Maydell @ 2024-03-05 13:52 UTC (permalink / raw)
To: qemu-devel
From: Rayhan Faizel <rayhan.faizel@gmail.com>
BCM2835 has three I2C controllers. All of them share the same interrupt line.
Signed-off-by: Rayhan Faizel <rayhan.faizel@gmail.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Message-id: 20240224191038.2409945-3-rayhan.faizel@gmail.com
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
include/hw/arm/bcm2835_peripherals.h | 4 ++-
hw/arm/bcm2835_peripherals.c | 45 ++++++++++++++++++++++++++--
hw/arm/Kconfig | 1 +
3 files changed, 46 insertions(+), 4 deletions(-)
diff --git a/include/hw/arm/bcm2835_peripherals.h b/include/hw/arm/bcm2835_peripherals.h
index 1fc96218f82..636203baa5a 100644
--- a/include/hw/arm/bcm2835_peripherals.h
+++ b/include/hw/arm/bcm2835_peripherals.h
@@ -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;
diff --git a/hw/arm/bcm2835_peripherals.c b/hw/arm/bcm2835_peripherals.c
index a0bbe76f264..1695d8b453a 100644
--- a/hw/arm/bcm2835_peripherals.c
+++ b/hw/arm/bcm2835_peripherals.c
@@ -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);
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 7caebdd98e1..3c157376844 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -438,6 +438,7 @@ config RASPI
select SDHCI
select USB_DWC2
select BCM2835_SPI
+ select BCM2835_I2C
config STM32F100_SOC
bool
--
2.34.1
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [PULL 03/20] tests/qtest: Add testcase for BCM2835 BSC
2024-03-05 13:52 [PULL 00/20] target-arm queue Peter Maydell
2024-03-05 13:52 ` [PULL 01/20] hw/i2c: Implement Broadcom Serial Controller (BSC) Peter Maydell
2024-03-05 13:52 ` [PULL 02/20] hw/arm: Connect BSC to BCM2835 board as I2C0, I2C1 and I2C2 Peter Maydell
@ 2024-03-05 13:52 ` Peter Maydell
2024-03-05 13:52 ` [PULL 04/20] hw/char/pl011: Add support for loopback Peter Maydell
` (17 subsequent siblings)
20 siblings, 0 replies; 37+ messages in thread
From: Peter Maydell @ 2024-03-05 13:52 UTC (permalink / raw)
To: qemu-devel
From: Rayhan Faizel <rayhan.faizel@gmail.com>
Simple testcase for validating proper operation of read and write for all
three BSC controllers.
Signed-off-by: Rayhan Faizel <rayhan.faizel@gmail.com>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Message-id: 20240224191038.2409945-4-rayhan.faizel@gmail.com
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
tests/qtest/bcm2835-i2c-test.c | 115 +++++++++++++++++++++++++++++++++
tests/qtest/meson.build | 2 +-
2 files changed, 116 insertions(+), 1 deletion(-)
create mode 100644 tests/qtest/bcm2835-i2c-test.c
diff --git a/tests/qtest/bcm2835-i2c-test.c b/tests/qtest/bcm2835-i2c-test.c
new file mode 100644
index 00000000000..513ecce61dc
--- /dev/null
+++ b/tests/qtest/bcm2835-i2c-test.c
@@ -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;
+}
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 6ea77893f50..bdc8bba7a98 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -230,7 +230,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',
--
2.34.1
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [PULL 04/20] hw/char/pl011: Add support for loopback
2024-03-05 13:52 [PULL 00/20] target-arm queue Peter Maydell
` (2 preceding siblings ...)
2024-03-05 13:52 ` [PULL 03/20] tests/qtest: Add testcase for BCM2835 BSC Peter Maydell
@ 2024-03-05 13:52 ` Peter Maydell
2024-03-05 13:52 ` [PULL 05/20] hw/misc/stm32l4x5_rcc: Implement STM32L4x5_RCC skeleton Peter Maydell
` (16 subsequent siblings)
20 siblings, 0 replies; 37+ messages in thread
From: Peter Maydell @ 2024-03-05 13:52 UTC (permalink / raw)
To: qemu-devel
From: Tong Ho <tong.ho@amd.com>
This patch adds loopback for sent characters, sent BREAK,
and modem-control signals.
Loopback of send and modem-control is often used for uart
self tests in real hardware but missing from current pl011
model, resulting in self-test failures when running in QEMU.
This implementation matches what is observed in real pl011
hardware placed in loopback mode:
1. Input characters and BREAK events from serial backend
are ignored, but
2. Both TX characters and BREAK events are still sent to
serial backend, in addition to be looped back to RX.
Signed-off-by: Tong Ho <tong.ho@amd.com>
Signed-off-by: Francisco Iglesias <francisco.iglesias@amd.com>
Message-id: 20240227054855.44204-1-tong.ho@amd.com
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
hw/char/pl011.c | 110 +++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 108 insertions(+), 2 deletions(-)
diff --git a/hw/char/pl011.c b/hw/char/pl011.c
index 855cb82d08d..8753b84a842 100644
--- a/hw/char/pl011.c
+++ b/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);
}
}
--
2.34.1
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [PULL 05/20] hw/misc/stm32l4x5_rcc: Implement STM32L4x5_RCC skeleton
2024-03-05 13:52 [PULL 00/20] target-arm queue Peter Maydell
` (3 preceding siblings ...)
2024-03-05 13:52 ` [PULL 04/20] hw/char/pl011: Add support for loopback Peter Maydell
@ 2024-03-05 13:52 ` Peter Maydell
2024-03-05 13:52 ` [PULL 06/20] hw/misc/stm32l4x5_rcc: Add an internal clock multiplexer object Peter Maydell
` (15 subsequent siblings)
20 siblings, 0 replies; 37+ messages in thread
From: Peter Maydell @ 2024-03-05 13:52 UTC (permalink / raw)
To: qemu-devel
From: Arnaud Minier <arnaud.minier@telecom-paris.fr>
Add the necessary files to add a simple RCC implementation with just
reads from and writes to registers. Also instantiate the RCC in the
STM32L4x5_SoC. It is needed for accurate emulation of all the SoC
clocks and timers.
Signed-off-by: Arnaud Minier <arnaud.minier@telecom-paris.fr>
Signed-off-by: Inès Varhol <ines.varhol@telecom-paris.fr>
Acked-by: Alistair Francis <alistair.francis@wdc.com>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Message-id: 20240303140643.81957-2-arnaud.minier@telecom-paris.fr
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
MAINTAINERS | 5 +-
docs/system/arm/b-l475e-iot01a.rst | 2 +-
include/hw/arm/stm32l4x5_soc.h | 2 +
include/hw/misc/stm32l4x5_rcc.h | 80 ++++
include/hw/misc/stm32l4x5_rcc_internals.h | 286 ++++++++++++++
hw/arm/stm32l4x5_soc.c | 12 +-
hw/misc/stm32l4x5_rcc.c | 446 ++++++++++++++++++++++
hw/arm/Kconfig | 1 +
hw/misc/Kconfig | 3 +
hw/misc/meson.build | 1 +
hw/misc/trace-events | 4 +
11 files changed, 839 insertions(+), 3 deletions(-)
create mode 100644 include/hw/misc/stm32l4x5_rcc.h
create mode 100644 include/hw/misc/stm32l4x5_rcc_internals.h
create mode 100644 hw/misc/stm32l4x5_rcc.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 65dfdc9677e..4183f2f3abb 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -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>
diff --git a/docs/system/arm/b-l475e-iot01a.rst b/docs/system/arm/b-l475e-iot01a.rst
index 1a021b306a6..b857a56ca4e 100644
--- a/docs/system/arm/b-l475e-iot01a.rst
+++ b/docs/system/arm/b-l475e-iot01a.rst
@@ -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)
diff --git a/include/hw/arm/stm32l4x5_soc.h b/include/hw/arm/stm32l4x5_soc.h
index 4f314b7a933..0b4f97e240e 100644
--- a/include/hw/arm/stm32l4x5_soc.h
+++ b/include/hw/arm/stm32l4x5_soc.h
@@ -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,6 +48,7 @@ struct Stm32l4x5SocState {
Stm32l4x5ExtiState exti;
OrIRQState exti_or_gates[NUM_EXTI_OR_GATES];
Stm32l4x5SyscfgState syscfg;
+ Stm32l4x5RccState rcc;
MemoryRegion sram1;
MemoryRegion sram2;
diff --git a/include/hw/misc/stm32l4x5_rcc.h b/include/hw/misc/stm32l4x5_rcc.h
new file mode 100644
index 00000000000..5157e966352
--- /dev/null
+++ b/include/hw/misc/stm32l4x5_rcc.h
@@ -0,0 +1,80 @@
+/*
+ * 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
+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;
+
+ qemu_irq irq;
+ uint64_t hse_frequency;
+ uint64_t sai1_extclk_frequency;
+ uint64_t sai2_extclk_frequency;
+};
+
+#endif /* HW_STM32L4X5_RCC_H */
diff --git a/include/hw/misc/stm32l4x5_rcc_internals.h b/include/hw/misc/stm32l4x5_rcc_internals.h
new file mode 100644
index 00000000000..331ea30db57
--- /dev/null
+++ b/include/hw/misc/stm32l4x5_rcc_internals.h
@@ -0,0 +1,286 @@
+/*
+ * 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 implementation by Luc Michel.
+ */
+
+#ifndef HW_STM32L4X5_RCC_INTERNALS_H
+#define HW_STM32L4X5_RCC_INTERNALS_H
+
+#include "hw/registerfields.h"
+#include "hw/misc/stm32l4x5_rcc.h"
+
+
+/* Register map */
+REG32(CR, 0x00)
+ FIELD(CR, PLLSAI2RDY, 29, 1)
+ FIELD(CR, PLLSAI2ON, 28, 1)
+ FIELD(CR, PLLSAI1RDY, 27, 1)
+ FIELD(CR, PLLSAI1ON, 26, 1)
+ FIELD(CR, PLLRDY, 25, 1)
+ FIELD(CR, PLLON, 24, 1)
+ FIELD(CR, CSSON, 19, 1)
+ FIELD(CR, HSEBYP, 18, 1)
+ FIELD(CR, HSERDY, 17, 1)
+ FIELD(CR, HSEON, 16, 1)
+ FIELD(CR, HSIASFS, 11, 1)
+ FIELD(CR, HSIRDY, 10, 1)
+ FIELD(CR, HSIKERON, 9, 1)
+ FIELD(CR, HSION, 8, 1)
+ FIELD(CR, MSIRANGE, 4, 4)
+ FIELD(CR, MSIRGSEL, 3, 1)
+ FIELD(CR, MSIPLLEN, 2, 1)
+ FIELD(CR, MSIRDY, 1, 1)
+ FIELD(CR, MSION, 0, 1)
+REG32(ICSCR, 0x04)
+ FIELD(ICSCR, HSITRIM, 24, 7)
+ FIELD(ICSCR, HSICAL, 16, 8)
+ FIELD(ICSCR, MSITRIM, 8, 8)
+ FIELD(ICSCR, MSICAL, 0, 8)
+REG32(CFGR, 0x08)
+ FIELD(CFGR, MCOPRE, 28, 3)
+ /* MCOSEL[2:0] only for STM32L475xx/476xx/486xx devices */
+ FIELD(CFGR, MCOSEL, 24, 3)
+ FIELD(CFGR, STOPWUCK, 15, 1)
+ FIELD(CFGR, PPRE2, 11, 3)
+ FIELD(CFGR, PPRE1, 8, 3)
+ FIELD(CFGR, HPRE, 4, 4)
+ FIELD(CFGR, SWS, 2, 2)
+ FIELD(CFGR, SW, 0, 2)
+REG32(PLLCFGR, 0x0C)
+ FIELD(PLLCFGR, PLLPDIV, 27, 5)
+ FIELD(PLLCFGR, PLLR, 25, 2)
+ FIELD(PLLCFGR, PLLREN, 24, 1)
+ FIELD(PLLCFGR, PLLQ, 21, 2)
+ FIELD(PLLCFGR, PLLQEN, 20, 1)
+ FIELD(PLLCFGR, PLLP, 17, 1)
+ FIELD(PLLCFGR, PLLPEN, 16, 1)
+ FIELD(PLLCFGR, PLLN, 8, 7)
+ FIELD(PLLCFGR, PLLM, 4, 3)
+ FIELD(PLLCFGR, PLLSRC, 0, 2)
+REG32(PLLSAI1CFGR, 0x10)
+ FIELD(PLLSAI1CFGR, PLLSAI1PDIV, 27, 5)
+ FIELD(PLLSAI1CFGR, PLLSAI1R, 25, 2)
+ FIELD(PLLSAI1CFGR, PLLSAI1REN, 24, 1)
+ FIELD(PLLSAI1CFGR, PLLSAI1Q, 21, 2)
+ FIELD(PLLSAI1CFGR, PLLSAI1QEN, 20, 1)
+ FIELD(PLLSAI1CFGR, PLLSAI1P, 17, 1)
+ FIELD(PLLSAI1CFGR, PLLSAI1PEN, 16, 1)
+ FIELD(PLLSAI1CFGR, PLLSAI1N, 8, 7)
+REG32(PLLSAI2CFGR, 0x14)
+ FIELD(PLLSAI2CFGR, PLLSAI2PDIV, 27, 5)
+ FIELD(PLLSAI2CFGR, PLLSAI2R, 25, 2)
+ FIELD(PLLSAI2CFGR, PLLSAI2REN, 24, 1)
+ FIELD(PLLSAI2CFGR, PLLSAI2Q, 21, 2)
+ FIELD(PLLSAI2CFGR, PLLSAI2QEN, 20, 1)
+ FIELD(PLLSAI2CFGR, PLLSAI2P, 17, 1)
+ FIELD(PLLSAI2CFGR, PLLSAI2PEN, 16, 1)
+ FIELD(PLLSAI2CFGR, PLLSAI2N, 8, 7)
+REG32(CIER, 0x18)
+ /* HSI48RDYIE: only on STM32L496xx/4A6xx devices */
+ FIELD(CIER, LSECSSIE, 9, 1)
+ FIELD(CIER, PLLSAI2RDYIE, 7, 1)
+ FIELD(CIER, PLLSAI1RDYIE, 6, 1)
+ FIELD(CIER, PLLRDYIE, 5, 1)
+ FIELD(CIER, HSERDYIE, 4, 1)
+ FIELD(CIER, HSIRDYIE, 3, 1)
+ FIELD(CIER, MSIRDYIE, 2, 1)
+ FIELD(CIER, LSERDYIE, 1, 1)
+ FIELD(CIER, LSIRDYIE, 0, 1)
+REG32(CIFR, 0x1C)
+ /* HSI48RDYF: only on STM32L496xx/4A6xx devices */
+ FIELD(CIFR, LSECSSF, 9, 1)
+ FIELD(CIFR, CSSF, 8, 1)
+ FIELD(CIFR, PLLSAI2RDYF, 7, 1)
+ FIELD(CIFR, PLLSAI1RDYF, 6, 1)
+ FIELD(CIFR, PLLRDYF, 5, 1)
+ FIELD(CIFR, HSERDYF, 4, 1)
+ FIELD(CIFR, HSIRDYF, 3, 1)
+ FIELD(CIFR, MSIRDYF, 2, 1)
+ FIELD(CIFR, LSERDYF, 1, 1)
+ FIELD(CIFR, LSIRDYF, 0, 1)
+REG32(CICR, 0x20)
+ /* HSI48RDYC: only on STM32L496xx/4A6xx devices */
+ FIELD(CICR, LSECSSC, 9, 1)
+ FIELD(CICR, CSSC, 8, 1)
+ FIELD(CICR, PLLSAI2RDYC, 7, 1)
+ FIELD(CICR, PLLSAI1RDYC, 6, 1)
+ FIELD(CICR, PLLRDYC, 5, 1)
+ FIELD(CICR, HSERDYC, 4, 1)
+ FIELD(CICR, HSIRDYC, 3, 1)
+ FIELD(CICR, MSIRDYC, 2, 1)
+ FIELD(CICR, LSERDYC, 1, 1)
+ FIELD(CICR, LSIRDYC, 0, 1)
+REG32(AHB1RSTR, 0x28)
+REG32(AHB2RSTR, 0x2C)
+REG32(AHB3RSTR, 0x30)
+REG32(APB1RSTR1, 0x38)
+REG32(APB1RSTR2, 0x3C)
+REG32(APB2RSTR, 0x40)
+REG32(AHB1ENR, 0x48)
+ /* DMA2DEN: reserved for STM32L475xx */
+ FIELD(AHB1ENR, TSCEN, 16, 1)
+ FIELD(AHB1ENR, CRCEN, 12, 1)
+ FIELD(AHB1ENR, FLASHEN, 8, 1)
+ FIELD(AHB1ENR, DMA2EN, 1, 1)
+ FIELD(AHB1ENR, DMA1EN, 0, 1)
+REG32(AHB2ENR, 0x4C)
+ FIELD(AHB2ENR, RNGEN, 18, 1)
+ /* HASHEN: reserved for STM32L475xx */
+ FIELD(AHB2ENR, AESEN, 16, 1)
+ /* DCMIEN: reserved for STM32L475xx */
+ FIELD(AHB2ENR, ADCEN, 13, 1)
+ FIELD(AHB2ENR, OTGFSEN, 12, 1)
+ /* GPIOIEN: reserved for STM32L475xx */
+ FIELD(AHB2ENR, GPIOHEN, 7, 1)
+ FIELD(AHB2ENR, GPIOGEN, 6, 1)
+ FIELD(AHB2ENR, GPIOFEN, 5, 1)
+ FIELD(AHB2ENR, GPIOEEN, 4, 1)
+ FIELD(AHB2ENR, GPIODEN, 3, 1)
+ FIELD(AHB2ENR, GPIOCEN, 2, 1)
+ FIELD(AHB2ENR, GPIOBEN, 1, 1)
+ FIELD(AHB2ENR, GPIOAEN, 0, 1)
+REG32(AHB3ENR, 0x50)
+ FIELD(AHB3ENR, QSPIEN, 8, 1)
+ FIELD(AHB3ENR, FMCEN, 0, 1)
+REG32(APB1ENR1, 0x58)
+ FIELD(APB1ENR1, LPTIM1EN, 31, 1)
+ FIELD(APB1ENR1, OPAMPEN, 30, 1)
+ FIELD(APB1ENR1, DAC1EN, 29, 1)
+ FIELD(APB1ENR1, PWREN, 28, 1)
+ FIELD(APB1ENR1, CAN2EN, 26, 1)
+ FIELD(APB1ENR1, CAN1EN, 25, 1)
+ /* CRSEN: reserved for STM32L475xx */
+ FIELD(APB1ENR1, I2C3EN, 23, 1)
+ FIELD(APB1ENR1, I2C2EN, 22, 1)
+ FIELD(APB1ENR1, I2C1EN, 21, 1)
+ FIELD(APB1ENR1, UART5EN, 20, 1)
+ FIELD(APB1ENR1, UART4EN, 19, 1)
+ FIELD(APB1ENR1, USART3EN, 18, 1)
+ FIELD(APB1ENR1, USART2EN, 17, 1)
+ FIELD(APB1ENR1, SPI3EN, 15, 1)
+ FIELD(APB1ENR1, SPI2EN, 14, 1)
+ FIELD(APB1ENR1, WWDGEN, 11, 1)
+ /* RTCAPBEN: reserved for STM32L475xx */
+ FIELD(APB1ENR1, LCDEN, 9, 1)
+ FIELD(APB1ENR1, TIM7EN, 5, 1)
+ FIELD(APB1ENR1, TIM6EN, 4, 1)
+ FIELD(APB1ENR1, TIM5EN, 3, 1)
+ FIELD(APB1ENR1, TIM4EN, 2, 1)
+ FIELD(APB1ENR1, TIM3EN, 1, 1)
+ FIELD(APB1ENR1, TIM2EN, 0, 1)
+REG32(APB1ENR2, 0x5C)
+ FIELD(APB1ENR2, LPTIM2EN, 5, 1)
+ FIELD(APB1ENR2, SWPMI1EN, 2, 1)
+ /* I2C4EN: reserved for STM32L475xx */
+ FIELD(APB1ENR2, LPUART1EN, 0, 1)
+REG32(APB2ENR, 0x60)
+ FIELD(APB2ENR, DFSDM1EN, 24, 1)
+ FIELD(APB2ENR, SAI2EN, 22, 1)
+ FIELD(APB2ENR, SAI1EN, 21, 1)
+ FIELD(APB2ENR, TIM17EN, 18, 1)
+ FIELD(APB2ENR, TIM16EN, 17, 1)
+ FIELD(APB2ENR, TIM15EN, 16, 1)
+ FIELD(APB2ENR, USART1EN, 14, 1)
+ FIELD(APB2ENR, TIM8EN, 13, 1)
+ FIELD(APB2ENR, SPI1EN, 12, 1)
+ FIELD(APB2ENR, TIM1EN, 11, 1)
+ FIELD(APB2ENR, SDMMC1EN, 10, 1)
+ FIELD(APB2ENR, FWEN, 7, 1)
+ FIELD(APB2ENR, SYSCFGEN, 0, 1)
+REG32(AHB1SMENR, 0x68)
+REG32(AHB2SMENR, 0x6C)
+REG32(AHB3SMENR, 0x70)
+REG32(APB1SMENR1, 0x78)
+REG32(APB1SMENR2, 0x7C)
+REG32(APB2SMENR, 0x80)
+REG32(CCIPR, 0x88)
+ FIELD(CCIPR, DFSDM1SEL, 31, 1)
+ FIELD(CCIPR, SWPMI1SEL, 30, 1)
+ FIELD(CCIPR, ADCSEL, 28, 2)
+ FIELD(CCIPR, CLK48SEL, 26, 2)
+ FIELD(CCIPR, SAI2SEL, 24, 2)
+ FIELD(CCIPR, SAI1SEL, 22, 2)
+ FIELD(CCIPR, LPTIM2SEL, 20, 2)
+ FIELD(CCIPR, LPTIM1SEL, 18, 2)
+ FIELD(CCIPR, I2C3SEL, 16, 2)
+ FIELD(CCIPR, I2C2SEL, 14, 2)
+ FIELD(CCIPR, I2C1SEL, 12, 2)
+ FIELD(CCIPR, LPUART1SEL, 10, 2)
+ FIELD(CCIPR, UART5SEL, 8, 2)
+ FIELD(CCIPR, UART4SEL, 6, 2)
+ FIELD(CCIPR, USART3SEL, 4, 2)
+ FIELD(CCIPR, USART2SEL, 2, 2)
+ FIELD(CCIPR, USART1SEL, 0, 2)
+REG32(BDCR, 0x90)
+ FIELD(BDCR, LSCOSEL, 25, 1)
+ FIELD(BDCR, LSCOEN, 24, 1)
+ FIELD(BDCR, BDRST, 16, 1)
+ FIELD(BDCR, RTCEN, 15, 1)
+ FIELD(BDCR, RTCSEL, 8, 2)
+ FIELD(BDCR, LSECSSD, 6, 1)
+ FIELD(BDCR, LSECSSON, 5, 1)
+ FIELD(BDCR, LSEDRV, 3, 2)
+ FIELD(BDCR, LSEBYP, 2, 1)
+ FIELD(BDCR, LSERDY, 1, 1)
+ FIELD(BDCR, LSEON, 0, 1)
+REG32(CSR, 0x94)
+ FIELD(CSR, LPWRRSTF, 31, 1)
+ FIELD(CSR, WWDGRSTF, 30, 1)
+ FIELD(CSR, IWWGRSTF, 29, 1)
+ FIELD(CSR, SFTRSTF, 28, 1)
+ FIELD(CSR, BORRSTF, 27, 1)
+ FIELD(CSR, PINRSTF, 26, 1)
+ FIELD(CSR, OBLRSTF, 25, 1)
+ FIELD(CSR, FWRSTF, 24, 1)
+ FIELD(CSR, RMVF, 23, 1)
+ FIELD(CSR, MSISRANGE, 8, 4)
+ FIELD(CSR, LSIRDY, 1, 1)
+ FIELD(CSR, LSION, 0, 1)
+/* CRRCR and CCIPR2 registers are present on L496/L4A6 devices only. */
+
+/* Read Only masks to prevent writes in unauthorized bits */
+#define CR_READ_ONLY_MASK (R_CR_PLLSAI2RDY_MASK | \
+ R_CR_PLLSAI1RDY_MASK | \
+ R_CR_PLLRDY_MASK | \
+ R_CR_HSERDY_MASK | \
+ R_CR_HSIRDY_MASK | \
+ R_CR_MSIRDY_MASK)
+#define CR_READ_SET_MASK (R_CR_CSSON_MASK | R_CR_MSIRGSEL_MASK)
+#define ICSCR_READ_ONLY_MASK (R_ICSCR_HSICAL_MASK | R_ICSCR_MSICAL_MASK)
+#define CFGR_READ_ONLY_MASK (R_CFGR_SWS_MASK)
+#define CIFR_READ_ONLY_MASK (R_CIFR_LSECSSF_MASK | \
+ R_CIFR_CSSF_MASK | \
+ R_CIFR_PLLSAI2RDYF_MASK | \
+ R_CIFR_PLLSAI1RDYF_MASK | \
+ R_CIFR_PLLRDYF_MASK | \
+ R_CIFR_HSERDYF_MASK | \
+ R_CIFR_HSIRDYF_MASK | \
+ R_CIFR_MSIRDYF_MASK | \
+ R_CIFR_LSERDYF_MASK | \
+ R_CIFR_LSIRDYF_MASK)
+#define CIFR_IRQ_MASK CIFR_READ_ONLY_MASK
+#define APB2ENR_READ_SET_MASK (R_APB2ENR_FWEN_MASK)
+#define BDCR_READ_ONLY_MASK (R_BDCR_LSECSSD_MASK | R_BDCR_LSERDY_MASK)
+#define CSR_READ_ONLY_MASK (R_CSR_LPWRRSTF_MASK | \
+ R_CSR_WWDGRSTF_MASK | \
+ R_CSR_IWWGRSTF_MASK | \
+ R_CSR_SFTRSTF_MASK | \
+ R_CSR_BORRSTF_MASK | \
+ R_CSR_PINRSTF_MASK | \
+ R_CSR_OBLRSTF_MASK | \
+ R_CSR_FWRSTF_MASK | \
+ R_CSR_LSIRDY_MASK)
+
+#endif /* HW_STM32L4X5_RCC_INTERNALS_H */
diff --git a/hw/arm/stm32l4x5_soc.c b/hw/arm/stm32l4x5_soc.c
index d1786e0da1c..cb147050091 100644
--- a/hw/arm/stm32l4x5_soc.c
+++ b/hw/arm/stm32l4x5_soc.c
@@ -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,6 +109,7 @@ static void stm32l4x5_soc_initfn(Object *obj)
TYPE_OR_IRQ);
}
object_initialize_child(obj, "syscfg", &s->syscfg, TYPE_STM32L4X5_SYSCFG);
+ object_initialize_child(obj, "rcc", &s->rcc, TYPE_STM32L4X5_RCC);
s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0);
s->refclk = qdev_init_clock_in(DEVICE(s), "refclk", NULL, NULL, 0);
@@ -244,6 +247,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 +317,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 */
diff --git a/hw/misc/stm32l4x5_rcc.c b/hw/misc/stm32l4x5_rcc.c
new file mode 100644
index 00000000000..269e50b85a0
--- /dev/null
+++ b/hw/misc/stm32l4x5_rcc.c
@@ -0,0 +1,446 @@
+/*
+ * 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 implementation by Luc Michel.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qemu/timer.h"
+#include "qapi/error.h"
+#include "migration/vmstate.h"
+#include "hw/misc/stm32l4x5_rcc.h"
+#include "hw/misc/stm32l4x5_rcc_internals.h"
+#include "hw/clock.h"
+#include "hw/irq.h"
+#include "hw/qdev-clock.h"
+#include "hw/qdev-properties.h"
+#include "hw/qdev-properties-system.h"
+#include "trace.h"
+
+#define HSE_DEFAULT_FRQ 48000000ULL
+#define HSI_FRQ 16000000ULL
+#define MSI_DEFAULT_FRQ 4000000ULL
+#define LSE_FRQ 32768ULL
+#define LSI_FRQ 32000ULL
+
+static void rcc_update_irq(Stm32l4x5RccState *s)
+{
+ if (s->cifr & CIFR_IRQ_MASK) {
+ qemu_irq_raise(s->irq);
+ } else {
+ qemu_irq_lower(s->irq);
+ }
+}
+
+static void stm32l4x5_rcc_reset_hold(Object *obj)
+{
+ Stm32l4x5RccState *s = STM32L4X5_RCC(obj);
+ s->cr = 0x00000063;
+ /*
+ * Factory-programmed calibration data
+ * From the reference manual: 0x10XX 00XX
+ * Value taken from a real card.
+ */
+ s->icscr = 0x106E0082;
+ s->cfgr = 0x0;
+ s->pllcfgr = 0x00001000;
+ s->pllsai1cfgr = 0x00001000;
+ s->pllsai2cfgr = 0x00001000;
+ s->cier = 0x0;
+ s->cifr = 0x0;
+ s->ahb1rstr = 0x0;
+ s->ahb2rstr = 0x0;
+ s->ahb3rstr = 0x0;
+ s->apb1rstr1 = 0x0;
+ s->apb1rstr2 = 0x0;
+ s->apb2rstr = 0x0;
+ s->ahb1enr = 0x00000100;
+ s->ahb2enr = 0x0;
+ s->ahb3enr = 0x0;
+ s->apb1enr1 = 0x0;
+ s->apb1enr2 = 0x0;
+ s->apb2enr = 0x0;
+ s->ahb1smenr = 0x00011303;
+ s->ahb2smenr = 0x000532FF;
+ s->ahb3smenr = 0x00000101;
+ s->apb1smenr1 = 0xF2FECA3F;
+ s->apb1smenr2 = 0x00000025;
+ s->apb2smenr = 0x01677C01;
+ s->ccipr = 0x0;
+ s->bdcr = 0x0;
+ s->csr = 0x0C000600;
+}
+
+static uint64_t stm32l4x5_rcc_read(void *opaque, hwaddr addr,
+ unsigned int size)
+{
+ Stm32l4x5RccState *s = opaque;
+ uint64_t retvalue = 0;
+
+ switch (addr) {
+ case A_CR:
+ retvalue = s->cr;
+ break;
+ case A_ICSCR:
+ retvalue = s->icscr;
+ break;
+ case A_CFGR:
+ retvalue = s->cfgr;
+ break;
+ case A_PLLCFGR:
+ retvalue = s->pllcfgr;
+ break;
+ case A_PLLSAI1CFGR:
+ retvalue = s->pllsai1cfgr;
+ break;
+ case A_PLLSAI2CFGR:
+ retvalue = s->pllsai2cfgr;
+ break;
+ case A_CIER:
+ retvalue = s->cier;
+ break;
+ case A_CIFR:
+ retvalue = s->cifr;
+ break;
+ case A_CICR:
+ /* CICR is write only, return the reset value = 0 */
+ break;
+ case A_AHB1RSTR:
+ retvalue = s->ahb1rstr;
+ break;
+ case A_AHB2RSTR:
+ retvalue = s->ahb2rstr;
+ break;
+ case A_AHB3RSTR:
+ retvalue = s->ahb3rstr;
+ break;
+ case A_APB1RSTR1:
+ retvalue = s->apb1rstr1;
+ break;
+ case A_APB1RSTR2:
+ retvalue = s->apb1rstr2;
+ break;
+ case A_APB2RSTR:
+ retvalue = s->apb2rstr;
+ break;
+ case A_AHB1ENR:
+ retvalue = s->ahb1enr;
+ break;
+ case A_AHB2ENR:
+ retvalue = s->ahb2enr;
+ break;
+ case A_AHB3ENR:
+ retvalue = s->ahb3enr;
+ break;
+ case A_APB1ENR1:
+ retvalue = s->apb1enr1;
+ break;
+ case A_APB1ENR2:
+ retvalue = s->apb1enr2;
+ break;
+ case A_APB2ENR:
+ retvalue = s->apb2enr;
+ break;
+ case A_AHB1SMENR:
+ retvalue = s->ahb1smenr;
+ break;
+ case A_AHB2SMENR:
+ retvalue = s->ahb2smenr;
+ break;
+ case A_AHB3SMENR:
+ retvalue = s->ahb3smenr;
+ break;
+ case A_APB1SMENR1:
+ retvalue = s->apb1smenr1;
+ break;
+ case A_APB1SMENR2:
+ retvalue = s->apb1smenr2;
+ break;
+ case A_APB2SMENR:
+ retvalue = s->apb2smenr;
+ break;
+ case A_CCIPR:
+ retvalue = s->ccipr;
+ break;
+ case A_BDCR:
+ retvalue = s->bdcr;
+ break;
+ case A_CSR:
+ retvalue = s->csr;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr);
+ break;
+ }
+
+ trace_stm32l4x5_rcc_read(addr, retvalue);
+
+ return retvalue;
+}
+
+static void stm32l4x5_rcc_write(void *opaque, hwaddr addr,
+ uint64_t val64, unsigned int size)
+{
+ Stm32l4x5RccState *s = opaque;
+ const uint32_t value = val64;
+
+ trace_stm32l4x5_rcc_write(addr, value);
+
+ switch (addr) {
+ case A_CR:
+ s->cr = (s->cr & CR_READ_SET_MASK) |
+ (value & (CR_READ_SET_MASK | ~CR_READ_ONLY_MASK));
+ break;
+ case A_ICSCR:
+ s->icscr = value & ~ICSCR_READ_ONLY_MASK;
+ break;
+ case A_CFGR:
+ s->cfgr = value & ~CFGR_READ_ONLY_MASK;
+ break;
+ case A_PLLCFGR:
+ s->pllcfgr = value;
+ break;
+ case A_PLLSAI1CFGR:
+ s->pllsai1cfgr = value;
+ break;
+ case A_PLLSAI2CFGR:
+ s->pllsai2cfgr = value;
+ break;
+ case A_CIER:
+ s->cier = value;
+ break;
+ case A_CIFR:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Write attempt into read-only register (CIFR) 0x%"PRIx32"\n",
+ __func__, value);
+ break;
+ case A_CICR:
+ /* Clear interrupt flags by writing a 1 to the CICR register */
+ s->cifr &= ~value;
+ rcc_update_irq(s);
+ break;
+ /* Reset behaviors are not implemented */
+ case A_AHB1RSTR:
+ s->ahb1rstr = value;
+ break;
+ case A_AHB2RSTR:
+ s->ahb2rstr = value;
+ break;
+ case A_AHB3RSTR:
+ s->ahb3rstr = value;
+ break;
+ case A_APB1RSTR1:
+ s->apb1rstr1 = value;
+ break;
+ case A_APB1RSTR2:
+ s->apb1rstr2 = value;
+ break;
+ case A_APB2RSTR:
+ s->apb2rstr = value;
+ break;
+ case A_AHB1ENR:
+ s->ahb1enr = value;
+ break;
+ case A_AHB2ENR:
+ s->ahb2enr = value;
+ break;
+ case A_AHB3ENR:
+ s->ahb3enr = value;
+ break;
+ case A_APB1ENR1:
+ s->apb1enr1 = value;
+ break;
+ case A_APB1ENR2:
+ s->apb1enr2 = value;
+ break;
+ case A_APB2ENR:
+ s->apb2enr = (s->apb2enr & APB2ENR_READ_SET_MASK) | value;
+ break;
+ /* Behaviors for Sleep and Stop modes are not implemented */
+ case A_AHB1SMENR:
+ s->ahb1smenr = value;
+ break;
+ case A_AHB2SMENR:
+ s->ahb2smenr = value;
+ break;
+ case A_AHB3SMENR:
+ s->ahb3smenr = value;
+ break;
+ case A_APB1SMENR1:
+ s->apb1smenr1 = value;
+ break;
+ case A_APB1SMENR2:
+ s->apb1smenr2 = value;
+ break;
+ case A_APB2SMENR:
+ s->apb2smenr = value;
+ break;
+ case A_CCIPR:
+ s->ccipr = value;
+ break;
+ case A_BDCR:
+ s->bdcr = value & ~BDCR_READ_ONLY_MASK;
+ break;
+ case A_CSR:
+ s->csr = value & ~CSR_READ_ONLY_MASK;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr);
+ }
+}
+
+static const MemoryRegionOps stm32l4x5_rcc_ops = {
+ .read = stm32l4x5_rcc_read,
+ .write = stm32l4x5_rcc_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .max_access_size = 4,
+ .min_access_size = 4,
+ .unaligned = false
+ },
+ .impl = {
+ .max_access_size = 4,
+ .min_access_size = 4,
+ .unaligned = false
+ },
+};
+
+static const ClockPortInitArray stm32l4x5_rcc_clocks = {
+ QDEV_CLOCK_IN(Stm32l4x5RccState, hsi16_rc, NULL, 0),
+ QDEV_CLOCK_IN(Stm32l4x5RccState, msi_rc, NULL, 0),
+ QDEV_CLOCK_IN(Stm32l4x5RccState, hse, NULL, 0),
+ QDEV_CLOCK_IN(Stm32l4x5RccState, lsi_rc, NULL, 0),
+ QDEV_CLOCK_IN(Stm32l4x5RccState, lse_crystal, NULL, 0),
+ QDEV_CLOCK_IN(Stm32l4x5RccState, sai1_extclk, NULL, 0),
+ QDEV_CLOCK_IN(Stm32l4x5RccState, sai2_extclk, NULL, 0),
+ QDEV_CLOCK_END
+};
+
+
+static void stm32l4x5_rcc_init(Object *obj)
+{
+ Stm32l4x5RccState *s = STM32L4X5_RCC(obj);
+
+ sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq);
+
+ memory_region_init_io(&s->mmio, obj, &stm32l4x5_rcc_ops, s,
+ TYPE_STM32L4X5_RCC, 0x400);
+ sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
+
+ qdev_init_clocks(DEVICE(s), stm32l4x5_rcc_clocks);
+
+ s->gnd = clock_new(obj, "gnd");
+}
+
+static const VMStateDescription vmstate_stm32l4x5_rcc = {
+ .name = TYPE_STM32L4X5_RCC,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(cr, Stm32l4x5RccState),
+ VMSTATE_UINT32(icscr, Stm32l4x5RccState),
+ VMSTATE_UINT32(cfgr, Stm32l4x5RccState),
+ VMSTATE_UINT32(pllcfgr, Stm32l4x5RccState),
+ VMSTATE_UINT32(pllsai1cfgr, Stm32l4x5RccState),
+ VMSTATE_UINT32(pllsai2cfgr, Stm32l4x5RccState),
+ VMSTATE_UINT32(cier, Stm32l4x5RccState),
+ VMSTATE_UINT32(cifr, Stm32l4x5RccState),
+ VMSTATE_UINT32(ahb1rstr, Stm32l4x5RccState),
+ VMSTATE_UINT32(ahb2rstr, Stm32l4x5RccState),
+ VMSTATE_UINT32(ahb3rstr, Stm32l4x5RccState),
+ VMSTATE_UINT32(apb1rstr1, Stm32l4x5RccState),
+ VMSTATE_UINT32(apb1rstr2, Stm32l4x5RccState),
+ VMSTATE_UINT32(apb2rstr, Stm32l4x5RccState),
+ VMSTATE_UINT32(ahb1enr, Stm32l4x5RccState),
+ VMSTATE_UINT32(ahb2enr, Stm32l4x5RccState),
+ VMSTATE_UINT32(ahb3enr, Stm32l4x5RccState),
+ VMSTATE_UINT32(apb1enr1, Stm32l4x5RccState),
+ VMSTATE_UINT32(apb1enr2, Stm32l4x5RccState),
+ VMSTATE_UINT32(apb2enr, Stm32l4x5RccState),
+ VMSTATE_UINT32(ahb1smenr, Stm32l4x5RccState),
+ VMSTATE_UINT32(ahb2smenr, Stm32l4x5RccState),
+ VMSTATE_UINT32(ahb3smenr, Stm32l4x5RccState),
+ VMSTATE_UINT32(apb1smenr1, Stm32l4x5RccState),
+ VMSTATE_UINT32(apb1smenr2, Stm32l4x5RccState),
+ VMSTATE_UINT32(apb2smenr, Stm32l4x5RccState),
+ VMSTATE_UINT32(ccipr, Stm32l4x5RccState),
+ VMSTATE_UINT32(bdcr, Stm32l4x5RccState),
+ VMSTATE_UINT32(csr, Stm32l4x5RccState),
+ VMSTATE_CLOCK(hsi16_rc, Stm32l4x5RccState),
+ VMSTATE_CLOCK(msi_rc, Stm32l4x5RccState),
+ VMSTATE_CLOCK(hse, Stm32l4x5RccState),
+ VMSTATE_CLOCK(lsi_rc, Stm32l4x5RccState),
+ VMSTATE_CLOCK(lse_crystal, Stm32l4x5RccState),
+ VMSTATE_CLOCK(sai1_extclk, Stm32l4x5RccState),
+ VMSTATE_CLOCK(sai2_extclk, Stm32l4x5RccState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+
+static void stm32l4x5_rcc_realize(DeviceState *dev, Error **errp)
+{
+ Stm32l4x5RccState *s = STM32L4X5_RCC(dev);
+
+ if (s->hse_frequency < 4000000ULL ||
+ s->hse_frequency > 48000000ULL) {
+ error_setg(errp,
+ "HSE frequency is outside of the allowed [4-48]Mhz range: %" PRIx64 "",
+ s->hse_frequency);
+ return;
+ }
+
+ clock_update_hz(s->msi_rc, MSI_DEFAULT_FRQ);
+ clock_update_hz(s->sai1_extclk, s->sai1_extclk_frequency);
+ clock_update_hz(s->sai2_extclk, s->sai2_extclk_frequency);
+ clock_update(s->gnd, 0);
+}
+
+static Property stm32l4x5_rcc_properties[] = {
+ DEFINE_PROP_UINT64("hse_frequency", Stm32l4x5RccState,
+ hse_frequency, HSE_DEFAULT_FRQ),
+ DEFINE_PROP_UINT64("sai1_extclk_frequency", Stm32l4x5RccState,
+ sai1_extclk_frequency, 0),
+ DEFINE_PROP_UINT64("sai2_extclk_frequency", Stm32l4x5RccState,
+ sai2_extclk_frequency, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void stm32l4x5_rcc_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
+
+
+ rc->phases.hold = stm32l4x5_rcc_reset_hold;
+ device_class_set_props(dc, stm32l4x5_rcc_properties);
+ dc->realize = stm32l4x5_rcc_realize;
+ dc->vmsd = &vmstate_stm32l4x5_rcc;
+}
+
+static const TypeInfo stm32l4x5_rcc_types[] = {
+ {
+ .name = TYPE_STM32L4X5_RCC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(Stm32l4x5RccState),
+ .instance_init = stm32l4x5_rcc_init,
+ .class_init = stm32l4x5_rcc_class_init,
+ }
+};
+
+DEFINE_TYPES(stm32l4x5_rcc_types)
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 3c157376844..d58d820788c 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -475,6 +475,7 @@ config STM32L4X5_SOC
select OR_IRQ
select STM32L4X5_SYSCFG
select STM32L4X5_EXTI
+ select STM32L4X5_RCC
config XLNX_ZYNQMP_ARM
bool
diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index 83ad849b62e..1e08785b832 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -97,6 +97,9 @@ config STM32L4X5_EXTI
config STM32L4X5_SYSCFG
bool
+config STM32L4X5_RCC
+ bool
+
config MIPS_ITU
bool
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index 746686835b0..265b2c26274 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -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'))
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index 5f5bc922223..38169ccbc10 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -174,6 +174,10 @@ 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
+
# 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"
--
2.34.1
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [PULL 06/20] hw/misc/stm32l4x5_rcc: Add an internal clock multiplexer object
2024-03-05 13:52 [PULL 00/20] target-arm queue Peter Maydell
` (4 preceding siblings ...)
2024-03-05 13:52 ` [PULL 05/20] hw/misc/stm32l4x5_rcc: Implement STM32L4x5_RCC skeleton Peter Maydell
@ 2024-03-05 13:52 ` Peter Maydell
2024-03-05 13:52 ` [PULL 07/20] hw/misc/stm32l4x5_rcc: Add an internal PLL Clock object Peter Maydell
` (14 subsequent siblings)
20 siblings, 0 replies; 37+ messages in thread
From: Peter Maydell @ 2024-03-05 13:52 UTC (permalink / raw)
To: qemu-devel
From: Arnaud Minier <arnaud.minier@telecom-paris.fr>
This object is used to represent every multiplexer in the clock tree as
well as every clock output, every presecaler, frequency multiplier, etc.
This allows to use a generic approach for every component of the clock tree
(except the PLLs).
The migration handling is based on hw/misc/zynq_sclr.c.
Three phase reset will be handled in a later commit.
Signed-off-by: Arnaud Minier <arnaud.minier@telecom-paris.fr>
Signed-off-by: Inès Varhol <ines.varhol@telecom-paris.fr>
Acked-by: Alistair Francis <alistair.francis@wdc.com>
Message-id: 20240303140643.81957-3-arnaud.minier@telecom-paris.fr
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
---
include/hw/misc/stm32l4x5_rcc.h | 119 ++++++++++++++++
include/hw/misc/stm32l4x5_rcc_internals.h | 29 ++++
hw/misc/stm32l4x5_rcc.c | 160 ++++++++++++++++++++++
hw/misc/trace-events | 5 +
4 files changed, 313 insertions(+)
diff --git a/include/hw/misc/stm32l4x5_rcc.h b/include/hw/misc/stm32l4x5_rcc.h
index 5157e966352..6719be9fbee 100644
--- a/include/hw/misc/stm32l4x5_rcc.h
+++ b/include/hw/misc/stm32l4x5_rcc.h
@@ -26,6 +26,122 @@ 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
+/* 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 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;
+
struct Stm32l4x5RccState {
SysBusDevice parent_obj;
@@ -71,6 +187,9 @@ struct Stm32l4x5RccState {
Clock *sai1_extclk;
Clock *sai2_extclk;
+ /* Muxes ~= outputs */
+ RccClockMuxState clock_muxes[RCC_NUM_CLOCK_MUX];
+
qemu_irq irq;
uint64_t hse_frequency;
uint64_t sai1_extclk_frequency;
diff --git a/include/hw/misc/stm32l4x5_rcc_internals.h b/include/hw/misc/stm32l4x5_rcc_internals.h
index 331ea30db57..4aa836848b4 100644
--- a/include/hw/misc/stm32l4x5_rcc_internals.h
+++ b/include/hw/misc/stm32l4x5_rcc_internals.h
@@ -21,6 +21,8 @@
#include "hw/registerfields.h"
#include "hw/misc/stm32l4x5_rcc.h"
+#define TYPE_RCC_CLOCK_MUX "stm32l4x5-rcc-clock-mux"
+OBJECT_DECLARE_SIMPLE_TYPE(RccClockMuxState, RCC_CLOCK_MUX)
/* Register map */
REG32(CR, 0x00)
@@ -283,4 +285,31 @@ REG32(CSR, 0x94)
R_CSR_FWRSTF_MASK | \
R_CSR_LSIRDY_MASK)
+typedef enum RccClockMuxSource {
+ RCC_CLOCK_MUX_SRC_GND = 0,
+ RCC_CLOCK_MUX_SRC_HSI,
+ RCC_CLOCK_MUX_SRC_HSE,
+ RCC_CLOCK_MUX_SRC_MSI,
+ RCC_CLOCK_MUX_SRC_LSI,
+ RCC_CLOCK_MUX_SRC_LSE,
+ RCC_CLOCK_MUX_SRC_SAI1_EXTCLK,
+ RCC_CLOCK_MUX_SRC_SAI2_EXTCLK,
+ RCC_CLOCK_MUX_SRC_PLL,
+ RCC_CLOCK_MUX_SRC_PLLSAI1,
+ RCC_CLOCK_MUX_SRC_PLLSAI2,
+ RCC_CLOCK_MUX_SRC_PLLSAI3,
+ RCC_CLOCK_MUX_SRC_PLL48M1,
+ RCC_CLOCK_MUX_SRC_PLL48M2,
+ RCC_CLOCK_MUX_SRC_PLLADC1,
+ RCC_CLOCK_MUX_SRC_PLLADC2,
+ RCC_CLOCK_MUX_SRC_SYSCLK,
+ RCC_CLOCK_MUX_SRC_HCLK,
+ RCC_CLOCK_MUX_SRC_PCLK1,
+ RCC_CLOCK_MUX_SRC_PCLK2,
+ RCC_CLOCK_MUX_SRC_HSE_OVER_32,
+ RCC_CLOCK_MUX_SRC_LCD_AND_RTC_COMMON,
+
+ RCC_CLOCK_MUX_SRC_NUMBER,
+} RccClockMuxSource;
+
#endif /* HW_STM32L4X5_RCC_INTERNALS_H */
diff --git a/hw/misc/stm32l4x5_rcc.c b/hw/misc/stm32l4x5_rcc.c
index 269e50b85a0..ace4083e837 100644
--- a/hw/misc/stm32l4x5_rcc.c
+++ b/hw/misc/stm32l4x5_rcc.c
@@ -36,6 +36,134 @@
#define LSE_FRQ 32768ULL
#define LSI_FRQ 32000ULL
+static void clock_mux_update(RccClockMuxState *mux)
+{
+ uint64_t src_freq;
+ Clock *current_source = mux->srcs[mux->src];
+ uint32_t freq_multiplier = 0;
+ /*
+ * To avoid rounding errors, we use the clock period instead of the
+ * frequency.
+ * This means that the multiplier of the mux becomes the divider of
+ * the clock and the divider of the mux becomes the multiplier of the
+ * clock.
+ */
+ if (mux->enabled && mux->divider) {
+ freq_multiplier = mux->divider;
+ }
+
+ clock_set_mul_div(mux->out, freq_multiplier, mux->multiplier);
+ clock_update(mux->out, clock_get(current_source));
+
+ src_freq = clock_get_hz(current_source);
+ /* TODO: can we simply detect if the config changed so that we reduce log spam ? */
+ trace_stm32l4x5_rcc_mux_update(mux->id, mux->src, src_freq,
+ mux->multiplier, mux->divider);
+}
+
+static void clock_mux_src_update(void *opaque, ClockEvent event)
+{
+ RccClockMuxState **backref = opaque;
+ RccClockMuxState *s = *backref;
+ /*
+ * The backref value is equal to:
+ * s->backref + (sizeof(RccClockMuxState *) * update_src).
+ * By subtracting we can get back the index of the updated clock.
+ */
+ const uint32_t update_src = backref - s->backref;
+ /* Only update if the clock that was updated is the current source */
+ if (update_src == s->src) {
+ clock_mux_update(s);
+ }
+}
+
+static void clock_mux_init(Object *obj)
+{
+ RccClockMuxState *s = RCC_CLOCK_MUX(obj);
+ size_t i;
+
+ for (i = 0; i < RCC_NUM_CLOCK_MUX_SRC; i++) {
+ char *name = g_strdup_printf("srcs[%zu]", i);
+ s->backref[i] = s;
+ s->srcs[i] = qdev_init_clock_in(DEVICE(s), name,
+ clock_mux_src_update,
+ &s->backref[i],
+ ClockUpdate);
+ g_free(name);
+ }
+
+ s->out = qdev_init_clock_out(DEVICE(s), "out");
+}
+
+static void clock_mux_reset_hold(Object *obj)
+{ }
+
+static const VMStateDescription clock_mux_vmstate = {
+ .name = TYPE_RCC_CLOCK_MUX,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(id, RccClockMuxState),
+ VMSTATE_ARRAY_CLOCK(srcs, RccClockMuxState,
+ RCC_NUM_CLOCK_MUX_SRC),
+ VMSTATE_BOOL(enabled, RccClockMuxState),
+ VMSTATE_UINT32(src, RccClockMuxState),
+ VMSTATE_UINT32(multiplier, RccClockMuxState),
+ VMSTATE_UINT32(divider, RccClockMuxState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void clock_mux_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
+
+ rc->phases.hold = clock_mux_reset_hold;
+ dc->vmsd = &clock_mux_vmstate;
+}
+
+static void clock_mux_set_enable(RccClockMuxState *mux, bool enabled)
+{
+ if (mux->enabled == enabled) {
+ return;
+ }
+
+ if (enabled) {
+ trace_stm32l4x5_rcc_mux_enable(mux->id);
+ } else {
+ trace_stm32l4x5_rcc_mux_disable(mux->id);
+ }
+
+ mux->enabled = enabled;
+ clock_mux_update(mux);
+}
+
+static void clock_mux_set_factor(RccClockMuxState *mux,
+ uint32_t multiplier, uint32_t divider)
+{
+ if (mux->multiplier == multiplier && mux->divider == divider) {
+ return;
+ }
+ trace_stm32l4x5_rcc_mux_set_factor(mux->id,
+ mux->multiplier, multiplier, mux->divider, divider);
+
+ mux->multiplier = multiplier;
+ mux->divider = divider;
+ clock_mux_update(mux);
+}
+
+static void clock_mux_set_source(RccClockMuxState *mux, RccClockMuxSource src)
+{
+ if (mux->src == src) {
+ return;
+ }
+
+ trace_stm32l4x5_rcc_mux_set_src(mux->id, mux->src, src);
+ mux->src = src;
+ clock_mux_update(mux);
+}
+
static void rcc_update_irq(Stm32l4x5RccState *s)
{
if (s->cifr & CIFR_IRQ_MASK) {
@@ -335,6 +463,7 @@ static const ClockPortInitArray stm32l4x5_rcc_clocks = {
static void stm32l4x5_rcc_init(Object *obj)
{
Stm32l4x5RccState *s = STM32L4X5_RCC(obj);
+ size_t i;
sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq);
@@ -344,6 +473,14 @@ static void stm32l4x5_rcc_init(Object *obj)
qdev_init_clocks(DEVICE(s), stm32l4x5_rcc_clocks);
+ for (i = 0; i < RCC_NUM_CLOCK_MUX; i++) {
+
+ object_initialize_child(obj, "clock[*]",
+ &s->clock_muxes[i],
+ TYPE_RCC_CLOCK_MUX);
+
+ }
+
s->gnd = clock_new(obj, "gnd");
}
@@ -396,6 +533,7 @@ static const VMStateDescription vmstate_stm32l4x5_rcc = {
static void stm32l4x5_rcc_realize(DeviceState *dev, Error **errp)
{
Stm32l4x5RccState *s = STM32L4X5_RCC(dev);
+ size_t i;
if (s->hse_frequency < 4000000ULL ||
s->hse_frequency > 48000000ULL) {
@@ -405,10 +543,26 @@ static void stm32l4x5_rcc_realize(DeviceState *dev, Error **errp)
return;
}
+ for (i = 0; i < RCC_NUM_CLOCK_MUX; i++) {
+ RccClockMuxState *clock_mux = &s->clock_muxes[i];
+
+ if (!qdev_realize(DEVICE(clock_mux), NULL, errp)) {
+ return;
+ }
+ }
+
clock_update_hz(s->msi_rc, MSI_DEFAULT_FRQ);
clock_update_hz(s->sai1_extclk, s->sai1_extclk_frequency);
clock_update_hz(s->sai2_extclk, s->sai2_extclk_frequency);
clock_update(s->gnd, 0);
+
+ /*
+ * Dummy values to make compilation pass.
+ * Removed in later commits.
+ */
+ clock_mux_set_source(&s->clock_muxes[0], RCC_CLOCK_MUX_SRC_GND);
+ clock_mux_set_enable(&s->clock_muxes[0], true);
+ clock_mux_set_factor(&s->clock_muxes[0], 1, 1);
}
static Property stm32l4x5_rcc_properties[] = {
@@ -440,6 +594,12 @@ static const TypeInfo stm32l4x5_rcc_types[] = {
.instance_size = sizeof(Stm32l4x5RccState),
.instance_init = stm32l4x5_rcc_init,
.class_init = stm32l4x5_rcc_class_init,
+ }, {
+ .name = TYPE_RCC_CLOCK_MUX,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(RccClockMuxState),
+ .instance_init = clock_mux_init,
+ .class_init = clock_mux_class_init,
}
};
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index 38169ccbc10..4b97641475b 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -177,6 +177,11 @@ stm32l4x5_exti_write(uint64_t addr, uint64_t data) "reg write: addr: 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
# 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"
--
2.34.1
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [PULL 07/20] hw/misc/stm32l4x5_rcc: Add an internal PLL Clock object
2024-03-05 13:52 [PULL 00/20] target-arm queue Peter Maydell
` (5 preceding siblings ...)
2024-03-05 13:52 ` [PULL 06/20] hw/misc/stm32l4x5_rcc: Add an internal clock multiplexer object Peter Maydell
@ 2024-03-05 13:52 ` Peter Maydell
2024-03-05 13:52 ` [PULL 08/20] hw/misc/stm32l4x5_rcc: Initialize PLLs and clock multiplexers Peter Maydell
` (13 subsequent siblings)
20 siblings, 0 replies; 37+ messages in thread
From: Peter Maydell @ 2024-03-05 13:52 UTC (permalink / raw)
To: qemu-devel
From: Arnaud Minier <arnaud.minier@telecom-paris.fr>
This object represents the PLLs and their channels. The PLLs allow for a
more fine-grained control of the clocks frequency.
The migration handling is based on hw/misc/zynq_sclr.c.
Three phase reset will be handled in a later commit.
Signed-off-by: Arnaud Minier <arnaud.minier@telecom-paris.fr>
Signed-off-by: Inès Varhol <ines.varhol@telecom-paris.fr>
Message-id: 20240303140643.81957-4-arnaud.minier@telecom-paris.fr
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
---
include/hw/misc/stm32l4x5_rcc.h | 40 +++++
include/hw/misc/stm32l4x5_rcc_internals.h | 22 +++
hw/misc/stm32l4x5_rcc.c | 176 ++++++++++++++++++++++
hw/misc/trace-events | 5 +
4 files changed, 243 insertions(+)
diff --git a/include/hw/misc/stm32l4x5_rcc.h b/include/hw/misc/stm32l4x5_rcc.h
index 6719be9fbee..0fbfba5c40b 100644
--- a/include/hw/misc/stm32l4x5_rcc.h
+++ b/include/hw/misc/stm32l4x5_rcc.h
@@ -26,6 +26,15 @@ 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 */
@@ -124,6 +133,14 @@ typedef enum RccClockMux {
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;
@@ -142,6 +159,26 @@ typedef struct RccClockMuxState {
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;
@@ -187,6 +224,9 @@ struct Stm32l4x5RccState {
Clock *sai1_extclk;
Clock *sai2_extclk;
+ /* PLLs */
+ RccPllState plls[RCC_NUM_PLL];
+
/* Muxes ~= outputs */
RccClockMuxState clock_muxes[RCC_NUM_CLOCK_MUX];
diff --git a/include/hw/misc/stm32l4x5_rcc_internals.h b/include/hw/misc/stm32l4x5_rcc_internals.h
index 4aa836848b4..a9da5e3be7d 100644
--- a/include/hw/misc/stm32l4x5_rcc_internals.h
+++ b/include/hw/misc/stm32l4x5_rcc_internals.h
@@ -22,7 +22,10 @@
#include "hw/misc/stm32l4x5_rcc.h"
#define TYPE_RCC_CLOCK_MUX "stm32l4x5-rcc-clock-mux"
+#define TYPE_RCC_PLL "stm32l4x5-rcc-pll"
+
OBJECT_DECLARE_SIMPLE_TYPE(RccClockMuxState, RCC_CLOCK_MUX)
+OBJECT_DECLARE_SIMPLE_TYPE(RccPllState, RCC_PLL)
/* Register map */
REG32(CR, 0x00)
@@ -285,6 +288,25 @@ REG32(CSR, 0x94)
R_CSR_FWRSTF_MASK | \
R_CSR_LSIRDY_MASK)
+/* Pll Channels */
+enum PllChannels {
+ RCC_PLL_CHANNEL_PLLSAI3CLK = 0,
+ RCC_PLL_CHANNEL_PLL48M1CLK = 1,
+ RCC_PLL_CHANNEL_PLLCLK = 2,
+};
+
+enum PllSai1Channels {
+ RCC_PLLSAI1_CHANNEL_PLLSAI1CLK = 0,
+ RCC_PLLSAI1_CHANNEL_PLL48M2CLK = 1,
+ RCC_PLLSAI1_CHANNEL_PLLADC1CLK = 2,
+};
+
+enum PllSai2Channels {
+ RCC_PLLSAI2_CHANNEL_PLLSAI2CLK = 0,
+ /* No Q channel */
+ RCC_PLLSAI2_CHANNEL_PLLADC2CLK = 2,
+};
+
typedef enum RccClockMuxSource {
RCC_CLOCK_MUX_SRC_GND = 0,
RCC_CLOCK_MUX_SRC_HSI,
diff --git a/hw/misc/stm32l4x5_rcc.c b/hw/misc/stm32l4x5_rcc.c
index ace4083e837..9a9ea6c4702 100644
--- a/hw/misc/stm32l4x5_rcc.c
+++ b/hw/misc/stm32l4x5_rcc.c
@@ -164,6 +164,157 @@ static void clock_mux_set_source(RccClockMuxState *mux, RccClockMuxSource src)
clock_mux_update(mux);
}
+static void pll_update(RccPllState *pll)
+{
+ uint64_t vco_freq, old_channel_freq, channel_freq;
+ int i;
+
+ /* The common PLLM factor is handled by the PLL mux */
+ vco_freq = muldiv64(clock_get_hz(pll->in), pll->vco_multiplier, 1);
+
+ for (i = 0; i < RCC_NUM_CHANNEL_PLL_OUT; i++) {
+ if (!pll->channel_exists[i]) {
+ continue;
+ }
+
+ old_channel_freq = clock_get_hz(pll->channels[i]);
+ if (!pll->enabled ||
+ !pll->channel_enabled[i] ||
+ !pll->channel_divider[i]) {
+ channel_freq = 0;
+ } else {
+ channel_freq = muldiv64(vco_freq,
+ 1,
+ pll->channel_divider[i]);
+ }
+
+ /* No change, early continue to avoid log spam and useless propagation */
+ if (old_channel_freq == channel_freq) {
+ continue;
+ }
+
+ clock_update_hz(pll->channels[i], channel_freq);
+ trace_stm32l4x5_rcc_pll_update(pll->id, i, vco_freq,
+ old_channel_freq, channel_freq);
+ }
+}
+
+static void pll_src_update(void *opaque, ClockEvent event)
+{
+ RccPllState *s = opaque;
+ pll_update(s);
+}
+
+static void pll_init(Object *obj)
+{
+ RccPllState *s = RCC_PLL(obj);
+ size_t i;
+
+ s->in = qdev_init_clock_in(DEVICE(s), "in",
+ pll_src_update, s, ClockUpdate);
+
+ const char *names[] = {
+ "out-p", "out-q", "out-r",
+ };
+
+ for (i = 0; i < RCC_NUM_CHANNEL_PLL_OUT; i++) {
+ s->channels[i] = qdev_init_clock_out(DEVICE(s), names[i]);
+ }
+}
+
+static void pll_reset_hold(Object *obj)
+{ }
+
+static const VMStateDescription pll_vmstate = {
+ .name = TYPE_RCC_PLL,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(id, RccPllState),
+ VMSTATE_CLOCK(in, RccPllState),
+ VMSTATE_ARRAY_CLOCK(channels, RccPllState,
+ RCC_NUM_CHANNEL_PLL_OUT),
+ VMSTATE_BOOL(enabled, RccPllState),
+ VMSTATE_UINT32(vco_multiplier, RccPllState),
+ VMSTATE_BOOL_ARRAY(channel_enabled, RccPllState, RCC_NUM_CHANNEL_PLL_OUT),
+ VMSTATE_BOOL_ARRAY(channel_exists, RccPllState, RCC_NUM_CHANNEL_PLL_OUT),
+ VMSTATE_UINT32_ARRAY(channel_divider, RccPllState, RCC_NUM_CHANNEL_PLL_OUT),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void pll_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
+
+ rc->phases.hold = pll_reset_hold;
+ dc->vmsd = &pll_vmstate;
+}
+
+static void pll_set_vco_multiplier(RccPllState *pll, uint32_t vco_multiplier)
+{
+ if (pll->vco_multiplier == vco_multiplier) {
+ return;
+ }
+
+ if (vco_multiplier < 8 || vco_multiplier > 86) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: VCO multiplier is out of bound (%u) for PLL %u\n",
+ __func__, vco_multiplier, pll->id);
+ return;
+ }
+
+ trace_stm32l4x5_rcc_pll_set_vco_multiplier(pll->id,
+ pll->vco_multiplier, vco_multiplier);
+
+ pll->vco_multiplier = vco_multiplier;
+ pll_update(pll);
+}
+
+static void pll_set_enable(RccPllState *pll, bool enabled)
+{
+ if (pll->enabled == enabled) {
+ return;
+ }
+
+ pll->enabled = enabled;
+ pll_update(pll);
+}
+
+static void pll_set_channel_enable(RccPllState *pll,
+ PllCommonChannels channel,
+ bool enabled)
+{
+ if (pll->channel_enabled[channel] == enabled) {
+ return;
+ }
+
+ if (enabled) {
+ trace_stm32l4x5_rcc_pll_channel_enable(pll->id, channel);
+ } else {
+ trace_stm32l4x5_rcc_pll_channel_disable(pll->id, channel);
+ }
+
+ pll->channel_enabled[channel] = enabled;
+ pll_update(pll);
+}
+
+static void pll_set_channel_divider(RccPllState *pll,
+ PllCommonChannels channel,
+ uint32_t divider)
+{
+ if (pll->channel_divider[channel] == divider) {
+ return;
+ }
+
+ trace_stm32l4x5_rcc_pll_set_channel_divider(pll->id,
+ channel, pll->channel_divider[channel], divider);
+
+ pll->channel_divider[channel] = divider;
+ pll_update(pll);
+}
+
static void rcc_update_irq(Stm32l4x5RccState *s)
{
if (s->cifr & CIFR_IRQ_MASK) {
@@ -473,6 +624,11 @@ static void stm32l4x5_rcc_init(Object *obj)
qdev_init_clocks(DEVICE(s), stm32l4x5_rcc_clocks);
+ for (i = 0; i < RCC_NUM_PLL; i++) {
+ object_initialize_child(obj, "pll[*]",
+ &s->plls[i], TYPE_RCC_PLL);
+ }
+
for (i = 0; i < RCC_NUM_CLOCK_MUX; i++) {
object_initialize_child(obj, "clock[*]",
@@ -543,6 +699,16 @@ static void stm32l4x5_rcc_realize(DeviceState *dev, Error **errp)
return;
}
+ for (i = 0; i < RCC_NUM_PLL; i++) {
+ RccPllState *pll = &s->plls[i];
+
+ clock_set_source(pll->in, s->clock_muxes[RCC_CLOCK_MUX_PLL_INPUT].out);
+
+ if (!qdev_realize(DEVICE(pll), NULL, errp)) {
+ return;
+ }
+ }
+
for (i = 0; i < RCC_NUM_CLOCK_MUX; i++) {
RccClockMuxState *clock_mux = &s->clock_muxes[i];
@@ -563,6 +729,10 @@ static void stm32l4x5_rcc_realize(DeviceState *dev, Error **errp)
clock_mux_set_source(&s->clock_muxes[0], RCC_CLOCK_MUX_SRC_GND);
clock_mux_set_enable(&s->clock_muxes[0], true);
clock_mux_set_factor(&s->clock_muxes[0], 1, 1);
+ pll_set_channel_divider(&s->plls[0], 0, 1);
+ pll_set_enable(&s->plls[0], true);
+ pll_set_channel_enable(&s->plls[0], 0, true);
+ pll_set_vco_multiplier(&s->plls[0], 1);
}
static Property stm32l4x5_rcc_properties[] = {
@@ -600,6 +770,12 @@ static const TypeInfo stm32l4x5_rcc_types[] = {
.instance_size = sizeof(RccClockMuxState),
.instance_init = clock_mux_init,
.class_init = clock_mux_class_init,
+ }, {
+ .name = TYPE_RCC_PLL,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(RccPllState),
+ .instance_init = pll_init,
+ .class_init = pll_class_init,
}
};
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index 4b97641475b..7cab1d5cb50 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -182,6 +182,11 @@ 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"
--
2.34.1
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [PULL 08/20] hw/misc/stm32l4x5_rcc: Initialize PLLs and clock multiplexers
2024-03-05 13:52 [PULL 00/20] target-arm queue Peter Maydell
` (6 preceding siblings ...)
2024-03-05 13:52 ` [PULL 07/20] hw/misc/stm32l4x5_rcc: Add an internal PLL Clock object Peter Maydell
@ 2024-03-05 13:52 ` Peter Maydell
2024-03-05 13:52 ` [PULL 09/20] hw/misc/stm32l4x5_rcc: Handle Register Updates Peter Maydell
` (12 subsequent siblings)
20 siblings, 0 replies; 37+ messages in thread
From: Peter Maydell @ 2024-03-05 13:52 UTC (permalink / raw)
To: qemu-devel
From: Arnaud Minier <arnaud.minier@telecom-paris.fr>
Instantiate the whole clock tree and using the Clock multiplexers and
the PLLs defined in the previous commits. This allows to statically
define the clock tree and easily follow the clock signal from one end to
another.
Also handle three-phase reset now that we have defined a known base
state for every object.
(Reset handling based on hw/misc/zynq_sclr.c)
Signed-off-by: Arnaud Minier <arnaud.minier@telecom-paris.fr>
Signed-off-by: Inès Varhol <ines.varhol@telecom-paris.fr>
Message-id: 20240303140643.81957-5-arnaud.minier@telecom-paris.fr
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
---
include/hw/misc/stm32l4x5_rcc_internals.h | 705 ++++++++++++++++++++++
hw/misc/stm32l4x5_rcc.c | 145 ++++-
2 files changed, 833 insertions(+), 17 deletions(-)
diff --git a/include/hw/misc/stm32l4x5_rcc_internals.h b/include/hw/misc/stm32l4x5_rcc_internals.h
index a9da5e3be7d..ff1c834f694 100644
--- a/include/hw/misc/stm32l4x5_rcc_internals.h
+++ b/include/hw/misc/stm32l4x5_rcc_internals.h
@@ -334,4 +334,709 @@ typedef enum RccClockMuxSource {
RCC_CLOCK_MUX_SRC_NUMBER,
} RccClockMuxSource;
+/* PLL init info */
+typedef struct PllInitInfo {
+ const char *name;
+
+ const char *channel_name[RCC_NUM_CHANNEL_PLL_OUT];
+ bool channel_exists[RCC_NUM_CHANNEL_PLL_OUT];
+ uint32_t default_channel_divider[RCC_NUM_CHANNEL_PLL_OUT];
+
+ RccClockMuxSource src_mapping[RCC_NUM_CLOCK_MUX_SRC];
+} PllInitInfo;
+
+static const PllInitInfo PLL_INIT_INFO[] = {
+ [RCC_PLL_PLL] = {
+ .name = "pll",
+ .channel_name = {
+ "pllsai3clk",
+ "pll48m1clk",
+ "pllclk"
+ },
+ .channel_exists = {
+ true, true, true
+ },
+ /* From PLLCFGR register documentation */
+ .default_channel_divider = {
+ 7, 2, 2
+ }
+ },
+ [RCC_PLL_PLLSAI1] = {
+ .name = "pllsai1",
+ .channel_name = {
+ "pllsai1clk",
+ "pll48m2clk",
+ "plladc1clk"
+ },
+ .channel_exists = {
+ true, true, true
+ },
+ /* From PLLSAI1CFGR register documentation */
+ .default_channel_divider = {
+ 7, 2, 2
+ }
+ },
+ [RCC_PLL_PLLSAI2] = {
+ .name = "pllsai2",
+ .channel_name = {
+ "pllsai2clk",
+ NULL,
+ "plladc2clk"
+ },
+ .channel_exists = {
+ true, false, true
+ },
+ /* From PLLSAI2CFGR register documentation */
+ .default_channel_divider = {
+ 7, 0, 2
+ }
+ }
+};
+
+static inline void set_pll_init_info(RccPllState *pll,
+ RccPll id)
+{
+ int i;
+
+ pll->id = id;
+ pll->vco_multiplier = 1;
+ for (i = 0; i < RCC_NUM_CHANNEL_PLL_OUT; i++) {
+ pll->channel_enabled[i] = false;
+ pll->channel_exists[i] = PLL_INIT_INFO[id].channel_exists[i];
+ pll->channel_divider[i] = PLL_INIT_INFO[id].default_channel_divider[i];
+ }
+}
+
+/* Clock mux init info */
+typedef struct ClockMuxInitInfo {
+ const char *name;
+
+ uint32_t multiplier;
+ uint32_t divider;
+ bool enabled;
+ /* If this is true, the clock will not be exposed outside of the device */
+ bool hidden;
+
+ RccClockMuxSource src_mapping[RCC_NUM_CLOCK_MUX_SRC];
+} ClockMuxInitInfo;
+
+#define FILL_DEFAULT_FACTOR \
+ .multiplier = 1, \
+ .divider = 1
+
+#define FILL_DEFAULT_INIT_ENABLED \
+ FILL_DEFAULT_FACTOR, \
+ .enabled = true
+
+#define FILL_DEFAULT_INIT_DISABLED \
+ FILL_DEFAULT_FACTOR, \
+ .enabled = false
+
+
+static const ClockMuxInitInfo CLOCK_MUX_INIT_INFO[] = {
+ [RCC_CLOCK_MUX_SYSCLK] = {
+ .name = "sysclk",
+ /* Same mapping as: CFGR_SW */
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_MSI,
+ RCC_CLOCK_MUX_SRC_HSI,
+ RCC_CLOCK_MUX_SRC_HSE,
+ RCC_CLOCK_MUX_SRC_PLL,
+ },
+ .hidden = true,
+ FILL_DEFAULT_INIT_ENABLED,
+ },
+ [RCC_CLOCK_MUX_PLL_INPUT] = {
+ .name = "pll-input",
+ /* Same mapping as: PLLCFGR_PLLSRC */
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_MSI,
+ RCC_CLOCK_MUX_SRC_HSI,
+ RCC_CLOCK_MUX_SRC_HSE,
+ },
+ .hidden = true,
+ FILL_DEFAULT_INIT_ENABLED,
+ },
+ [RCC_CLOCK_MUX_HCLK] = {
+ .name = "hclk",
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_SYSCLK,
+ },
+ .hidden = true,
+ FILL_DEFAULT_INIT_ENABLED,
+ },
+ [RCC_CLOCK_MUX_PCLK1] = {
+ .name = "pclk1",
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_HCLK,
+ },
+ .hidden = true,
+ FILL_DEFAULT_INIT_ENABLED,
+ },
+ [RCC_CLOCK_MUX_PCLK2] = {
+ .name = "pclk2",
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_HCLK,
+ },
+ .hidden = true,
+ FILL_DEFAULT_INIT_ENABLED,
+ },
+ [RCC_CLOCK_MUX_HSE_OVER_32] = {
+ .name = "hse-divided-by-32",
+ .multiplier = 1,
+ .divider = 32,
+ .enabled = true,
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_HSE,
+ },
+ .hidden = true,
+ },
+ [RCC_CLOCK_MUX_LCD_AND_RTC_COMMON] = {
+ .name = "lcd-and-rtc-common-mux",
+ /* Same mapping as: BDCR_RTCSEL */
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_GND,
+ RCC_CLOCK_MUX_SRC_LSE,
+ RCC_CLOCK_MUX_SRC_LSI,
+ RCC_CLOCK_MUX_SRC_HSE_OVER_32,
+ },
+ .hidden = true,
+ FILL_DEFAULT_INIT_ENABLED,
+ },
+ /* From now on, muxes with a publicly available output */
+ [RCC_CLOCK_MUX_CORTEX_REFCLK] = {
+ .name = "cortex-refclk",
+ .multiplier = 1,
+ /* REFCLK is always HCLK/8 */
+ .divider = 8,
+ .enabled = true,
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_HCLK,
+ }
+ },
+ [RCC_CLOCK_MUX_USART1] = {
+ .name = "usart1",
+ /* Same mapping as: CCIPR_USART1SEL */
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_PCLK2,
+ RCC_CLOCK_MUX_SRC_SYSCLK,
+ RCC_CLOCK_MUX_SRC_HSI,
+ RCC_CLOCK_MUX_SRC_LSE,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_USART2] = {
+ .name = "usart2",
+ /* Same mapping as: CCIPR_USART2SEL */
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_PCLK1,
+ RCC_CLOCK_MUX_SRC_SYSCLK,
+ RCC_CLOCK_MUX_SRC_HSI,
+ RCC_CLOCK_MUX_SRC_LSE,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_USART3] = {
+ .name = "usart3",
+ /* Same mapping as: CCIPR_USART3SEL */
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_PCLK1,
+ RCC_CLOCK_MUX_SRC_SYSCLK,
+ RCC_CLOCK_MUX_SRC_HSI,
+ RCC_CLOCK_MUX_SRC_LSE,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_UART4] = {
+ .name = "uart4",
+ /* Same mapping as: CCIPR_UART4SEL */
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_PCLK1,
+ RCC_CLOCK_MUX_SRC_SYSCLK,
+ RCC_CLOCK_MUX_SRC_HSI,
+ RCC_CLOCK_MUX_SRC_LSE,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_UART5] = {
+ .name = "uart5",
+ /* Same mapping as: CCIPR_UART5SEL */
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_PCLK1,
+ RCC_CLOCK_MUX_SRC_SYSCLK,
+ RCC_CLOCK_MUX_SRC_HSI,
+ RCC_CLOCK_MUX_SRC_LSE,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_LPUART1] = {
+ .name = "lpuart1",
+ /* Same mapping as: CCIPR_LPUART1SEL */
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_PCLK1,
+ RCC_CLOCK_MUX_SRC_SYSCLK,
+ RCC_CLOCK_MUX_SRC_HSI,
+ RCC_CLOCK_MUX_SRC_LSE,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_I2C1] = {
+ .name = "i2c1",
+ /* Same mapping as: CCIPR_I2C1SEL */
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_PCLK1,
+ RCC_CLOCK_MUX_SRC_SYSCLK,
+ RCC_CLOCK_MUX_SRC_HSI,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_I2C2] = {
+ .name = "i2c2",
+ /* Same mapping as: CCIPR_I2C2SEL */
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_PCLK1,
+ RCC_CLOCK_MUX_SRC_SYSCLK,
+ RCC_CLOCK_MUX_SRC_HSI,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_I2C3] = {
+ .name = "i2c3",
+ /* Same mapping as: CCIPR_I2C3SEL */
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_PCLK1,
+ RCC_CLOCK_MUX_SRC_SYSCLK,
+ RCC_CLOCK_MUX_SRC_HSI,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_LPTIM1] = {
+ .name = "lptim1",
+ /* Same mapping as: CCIPR_LPTIM1SEL */
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_PCLK1,
+ RCC_CLOCK_MUX_SRC_LSI,
+ RCC_CLOCK_MUX_SRC_HSI,
+ RCC_CLOCK_MUX_SRC_LSE,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_LPTIM2] = {
+ .name = "lptim2",
+ /* Same mapping as: CCIPR_LPTIM2SEL */
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_PCLK1,
+ RCC_CLOCK_MUX_SRC_LSI,
+ RCC_CLOCK_MUX_SRC_HSI,
+ RCC_CLOCK_MUX_SRC_LSE,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_SWPMI1] = {
+ .name = "swpmi1",
+ /* Same mapping as: CCIPR_SWPMI1SEL */
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_PCLK1,
+ RCC_CLOCK_MUX_SRC_HSI,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_MCO] = {
+ .name = "mco",
+ /* Same mapping as: CFGR_MCOSEL */
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_SYSCLK,
+ RCC_CLOCK_MUX_SRC_MSI,
+ RCC_CLOCK_MUX_SRC_HSI,
+ RCC_CLOCK_MUX_SRC_HSE,
+ RCC_CLOCK_MUX_SRC_PLL,
+ RCC_CLOCK_MUX_SRC_LSI,
+ RCC_CLOCK_MUX_SRC_LSE,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_LSCO] = {
+ .name = "lsco",
+ /* Same mapping as: BDCR_LSCOSEL */
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_LSI,
+ RCC_CLOCK_MUX_SRC_LSE,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_DFSDM1] = {
+ .name = "dfsdm1",
+ /* Same mapping as: CCIPR_DFSDM1SEL */
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_PCLK2,
+ RCC_CLOCK_MUX_SRC_SYSCLK,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_ADC] = {
+ .name = "adc",
+ /* Same mapping as: CCIPR_ADCSEL */
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_GND,
+ RCC_CLOCK_MUX_SRC_PLLADC1,
+ RCC_CLOCK_MUX_SRC_PLLADC2,
+ RCC_CLOCK_MUX_SRC_SYSCLK,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_CLK48] = {
+ .name = "clk48",
+ /* Same mapping as: CCIPR_CLK48SEL */
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_GND,
+ RCC_CLOCK_MUX_SRC_PLL48M2,
+ RCC_CLOCK_MUX_SRC_PLL48M1,
+ RCC_CLOCK_MUX_SRC_MSI,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_SAI2] = {
+ .name = "sai2",
+ /* Same mapping as: CCIPR_SAI2SEL */
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_PLLSAI1,
+ RCC_CLOCK_MUX_SRC_PLLSAI2,
+ RCC_CLOCK_MUX_SRC_PLLSAI3,
+ RCC_CLOCK_MUX_SRC_SAI2_EXTCLK,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_SAI1] = {
+ .name = "sai1",
+ /* Same mapping as: CCIPR_SAI1SEL */
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_PLLSAI1,
+ RCC_CLOCK_MUX_SRC_PLLSAI2,
+ RCC_CLOCK_MUX_SRC_PLLSAI3,
+ RCC_CLOCK_MUX_SRC_SAI1_EXTCLK,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ /* From now on, these muxes only have one valid source */
+ [RCC_CLOCK_MUX_TSC] = {
+ .name = "tsc",
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_SYSCLK,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_CRC] = {
+ .name = "crc",
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_SYSCLK,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_FLASH] = {
+ .name = "flash",
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_SYSCLK,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_DMA2] = {
+ .name = "dma2",
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_SYSCLK,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_DMA1] = {
+ .name = "dma1",
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_SYSCLK,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_RNG] = {
+ .name = "rng",
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_SYSCLK,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_AES] = {
+ .name = "aes",
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_SYSCLK,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_OTGFS] = {
+ .name = "otgfs",
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_SYSCLK,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_GPIOA] = {
+ .name = "gpioa",
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_SYSCLK,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_GPIOB] = {
+ .name = "gpiob",
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_SYSCLK,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_GPIOC] = {
+ .name = "gpioc",
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_SYSCLK,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_GPIOD] = {
+ .name = "gpiod",
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_SYSCLK,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_GPIOE] = {
+ .name = "gpioe",
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_SYSCLK,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_GPIOF] = {
+ .name = "gpiof",
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_SYSCLK,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_GPIOG] = {
+ .name = "gpiog",
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_SYSCLK,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_GPIOH] = {
+ .name = "gpioh",
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_SYSCLK,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_QSPI] = {
+ .name = "qspi",
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_SYSCLK,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_FMC] = {
+ .name = "fmc",
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_SYSCLK,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_OPAMP] = {
+ .name = "opamp",
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_PCLK1,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_DAC1] = {
+ .name = "dac1",
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_PCLK1,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_PWR] = {
+ .name = "pwr",
+ /*
+ * PWREN is in the APB1ENR1 register,
+ * but PWR uses SYSCLK according to the clock tree.
+ */
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_SYSCLK,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_CAN1] = {
+ .name = "can1",
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_PCLK1,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_SPI3] = {
+ .name = "spi3",
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_PCLK1,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_SPI2] = {
+ .name = "spi2",
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_PCLK1,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_WWDG] = {
+ .name = "wwdg",
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_PCLK1,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_LCD] = {
+ .name = "lcd",
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_LCD_AND_RTC_COMMON,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_TIM7] = {
+ .name = "tim7",
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_PCLK1,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_TIM6] = {
+ .name = "tim6",
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_PCLK1,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_TIM5] = {
+ .name = "tim5",
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_PCLK1,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_TIM4] = {
+ .name = "tim4",
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_PCLK1,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_TIM3] = {
+ .name = "tim3",
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_PCLK1,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_TIM2] = {
+ .name = "tim2",
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_PCLK1,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_TIM17] = {
+ .name = "tim17",
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_PCLK2,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_TIM16] = {
+ .name = "tim16",
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_PCLK2,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_TIM15] = {
+ .name = "tim15",
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_PCLK2,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_TIM8] = {
+ .name = "tim8",
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_PCLK2,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_SPI1] = {
+ .name = "spi1",
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_PCLK2,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_TIM1] = {
+ .name = "tim1",
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_PCLK2,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_SDMMC1] = {
+ .name = "sdmmc1",
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_PCLK2,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_FW] = {
+ .name = "fw",
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_PCLK2,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_SYSCFG] = {
+ .name = "syscfg",
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_PCLK2,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_RTC] = {
+ .name = "rtc",
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_LCD_AND_RTC_COMMON,
+ },
+ FILL_DEFAULT_INIT_DISABLED,
+ },
+ [RCC_CLOCK_MUX_CORTEX_FCLK] = {
+ .name = "cortex-fclk",
+ .src_mapping = {
+ RCC_CLOCK_MUX_SRC_HCLK,
+ },
+ FILL_DEFAULT_INIT_ENABLED,
+ },
+};
+
+static inline void set_clock_mux_init_info(RccClockMuxState *mux,
+ RccClockMux id)
+{
+ mux->id = id;
+ mux->multiplier = CLOCK_MUX_INIT_INFO[id].multiplier;
+ mux->divider = CLOCK_MUX_INIT_INFO[id].divider;
+ mux->enabled = CLOCK_MUX_INIT_INFO[id].enabled;
+ /*
+ * Every peripheral has the first source of their source list as
+ * as their default source.
+ */
+ mux->src = 0;
+}
+
#endif /* HW_STM32L4X5_RCC_INTERNALS_H */
diff --git a/hw/misc/stm32l4x5_rcc.c b/hw/misc/stm32l4x5_rcc.c
index 9a9ea6c4702..083c0ad9ef5 100644
--- a/hw/misc/stm32l4x5_rcc.c
+++ b/hw/misc/stm32l4x5_rcc.c
@@ -36,7 +36,13 @@
#define LSE_FRQ 32768ULL
#define LSI_FRQ 32000ULL
-static void clock_mux_update(RccClockMuxState *mux)
+/*
+ * Function to simply acknowledge and propagate changes in a clock mux
+ * frequency.
+ * `bypass_source` allows to bypass the period of the current source and just
+ * consider it equal to 0. This is useful during the hold phase of reset.
+ */
+static void clock_mux_update(RccClockMuxState *mux, bool bypass_source)
{
uint64_t src_freq;
Clock *current_source = mux->srcs[mux->src];
@@ -48,7 +54,7 @@ static void clock_mux_update(RccClockMuxState *mux)
* the clock and the divider of the mux becomes the multiplier of the
* clock.
*/
- if (mux->enabled && mux->divider) {
+ if (!bypass_source && mux->enabled && mux->divider) {
freq_multiplier = mux->divider;
}
@@ -73,7 +79,7 @@ static void clock_mux_src_update(void *opaque, ClockEvent event)
const uint32_t update_src = backref - s->backref;
/* Only update if the clock that was updated is the current source */
if (update_src == s->src) {
- clock_mux_update(s);
+ clock_mux_update(s, false);
}
}
@@ -95,8 +101,23 @@ static void clock_mux_init(Object *obj)
s->out = qdev_init_clock_out(DEVICE(s), "out");
}
+static void clock_mux_reset_enter(Object *obj, ResetType type)
+{
+ RccClockMuxState *s = RCC_CLOCK_MUX(obj);
+ set_clock_mux_init_info(s, s->id);
+}
+
static void clock_mux_reset_hold(Object *obj)
-{ }
+{
+ RccClockMuxState *s = RCC_CLOCK_MUX(obj);
+ clock_mux_update(s, true);
+}
+
+static void clock_mux_reset_exit(Object *obj)
+{
+ RccClockMuxState *s = RCC_CLOCK_MUX(obj);
+ clock_mux_update(s, false);
+}
static const VMStateDescription clock_mux_vmstate = {
.name = TYPE_RCC_CLOCK_MUX,
@@ -119,7 +140,9 @@ static void clock_mux_class_init(ObjectClass *klass, void *data)
DeviceClass *dc = DEVICE_CLASS(klass);
ResettableClass *rc = RESETTABLE_CLASS(klass);
+ rc->phases.enter = clock_mux_reset_enter;
rc->phases.hold = clock_mux_reset_hold;
+ rc->phases.exit = clock_mux_reset_exit;
dc->vmsd = &clock_mux_vmstate;
}
@@ -136,7 +159,7 @@ static void clock_mux_set_enable(RccClockMuxState *mux, bool enabled)
}
mux->enabled = enabled;
- clock_mux_update(mux);
+ clock_mux_update(mux, false);
}
static void clock_mux_set_factor(RccClockMuxState *mux,
@@ -150,7 +173,7 @@ static void clock_mux_set_factor(RccClockMuxState *mux,
mux->multiplier = multiplier;
mux->divider = divider;
- clock_mux_update(mux);
+ clock_mux_update(mux, false);
}
static void clock_mux_set_source(RccClockMuxState *mux, RccClockMuxSource src)
@@ -161,10 +184,15 @@ static void clock_mux_set_source(RccClockMuxState *mux, RccClockMuxSource src)
trace_stm32l4x5_rcc_mux_set_src(mux->id, mux->src, src);
mux->src = src;
- clock_mux_update(mux);
+ clock_mux_update(mux, false);
}
-static void pll_update(RccPllState *pll)
+/*
+ * Acknowledge and propagate changes in a PLL frequency.
+ * `bypass_source` allows to bypass the period of the current source and just
+ * consider it equal to 0. This is useful during the hold phase of reset.
+ */
+static void pll_update(RccPllState *pll, bool bypass_source)
{
uint64_t vco_freq, old_channel_freq, channel_freq;
int i;
@@ -178,7 +206,8 @@ static void pll_update(RccPllState *pll)
}
old_channel_freq = clock_get_hz(pll->channels[i]);
- if (!pll->enabled ||
+ if (bypass_source ||
+ !pll->enabled ||
!pll->channel_enabled[i] ||
!pll->channel_divider[i]) {
channel_freq = 0;
@@ -202,7 +231,7 @@ static void pll_update(RccPllState *pll)
static void pll_src_update(void *opaque, ClockEvent event)
{
RccPllState *s = opaque;
- pll_update(s);
+ pll_update(s, false);
}
static void pll_init(Object *obj)
@@ -222,8 +251,23 @@ static void pll_init(Object *obj)
}
}
+static void pll_reset_enter(Object *obj, ResetType type)
+{
+ RccPllState *s = RCC_PLL(obj);
+ set_pll_init_info(s, s->id);
+}
+
static void pll_reset_hold(Object *obj)
-{ }
+{
+ RccPllState *s = RCC_PLL(obj);
+ pll_update(s, true);
+}
+
+static void pll_reset_exit(Object *obj)
+{
+ RccPllState *s = RCC_PLL(obj);
+ pll_update(s, false);
+}
static const VMStateDescription pll_vmstate = {
.name = TYPE_RCC_PLL,
@@ -248,7 +292,9 @@ static void pll_class_init(ObjectClass *klass, void *data)
DeviceClass *dc = DEVICE_CLASS(klass);
ResettableClass *rc = RESETTABLE_CLASS(klass);
+ rc->phases.enter = pll_reset_enter;
rc->phases.hold = pll_reset_hold;
+ rc->phases.exit = pll_reset_exit;
dc->vmsd = &pll_vmstate;
}
@@ -269,7 +315,7 @@ static void pll_set_vco_multiplier(RccPllState *pll, uint32_t vco_multiplier)
pll->vco_multiplier, vco_multiplier);
pll->vco_multiplier = vco_multiplier;
- pll_update(pll);
+ pll_update(pll, false);
}
static void pll_set_enable(RccPllState *pll, bool enabled)
@@ -279,7 +325,7 @@ static void pll_set_enable(RccPllState *pll, bool enabled)
}
pll->enabled = enabled;
- pll_update(pll);
+ pll_update(pll, false);
}
static void pll_set_channel_enable(RccPllState *pll,
@@ -297,7 +343,7 @@ static void pll_set_channel_enable(RccPllState *pll,
}
pll->channel_enabled[channel] = enabled;
- pll_update(pll);
+ pll_update(pll, false);
}
static void pll_set_channel_divider(RccPllState *pll,
@@ -312,7 +358,7 @@ static void pll_set_channel_divider(RccPllState *pll,
channel, pll->channel_divider[channel], divider);
pll->channel_divider[channel] = divider;
- pll_update(pll);
+ pll_update(pll, false);
}
static void rcc_update_irq(Stm32l4x5RccState *s)
@@ -625,21 +671,79 @@ static void stm32l4x5_rcc_init(Object *obj)
qdev_init_clocks(DEVICE(s), stm32l4x5_rcc_clocks);
for (i = 0; i < RCC_NUM_PLL; i++) {
- object_initialize_child(obj, "pll[*]",
+ object_initialize_child(obj, PLL_INIT_INFO[i].name,
&s->plls[i], TYPE_RCC_PLL);
+ set_pll_init_info(&s->plls[i], i);
}
for (i = 0; i < RCC_NUM_CLOCK_MUX; i++) {
+ char *alias;
- object_initialize_child(obj, "clock[*]",
+ object_initialize_child(obj, CLOCK_MUX_INIT_INFO[i].name,
&s->clock_muxes[i],
TYPE_RCC_CLOCK_MUX);
+ set_clock_mux_init_info(&s->clock_muxes[i], i);
+ if (!CLOCK_MUX_INIT_INFO[i].hidden) {
+ /* Expose muxes output as RCC outputs */
+ alias = g_strdup_printf("%s-out", CLOCK_MUX_INIT_INFO[i].name);
+ qdev_alias_clock(DEVICE(&s->clock_muxes[i]), "out", DEVICE(obj), alias);
+ g_free(alias);
+ }
}
s->gnd = clock_new(obj, "gnd");
}
+static void connect_mux_sources(Stm32l4x5RccState *s,
+ RccClockMuxState *mux,
+ const RccClockMuxSource *clk_mapping)
+{
+ size_t i;
+
+ Clock * const CLK_SRC_MAPPING[] = {
+ [RCC_CLOCK_MUX_SRC_GND] = s->gnd,
+ [RCC_CLOCK_MUX_SRC_HSI] = s->hsi16_rc,
+ [RCC_CLOCK_MUX_SRC_HSE] = s->hse,
+ [RCC_CLOCK_MUX_SRC_MSI] = s->msi_rc,
+ [RCC_CLOCK_MUX_SRC_LSI] = s->lsi_rc,
+ [RCC_CLOCK_MUX_SRC_LSE] = s->lse_crystal,
+ [RCC_CLOCK_MUX_SRC_SAI1_EXTCLK] = s->sai1_extclk,
+ [RCC_CLOCK_MUX_SRC_SAI2_EXTCLK] = s->sai2_extclk,
+ [RCC_CLOCK_MUX_SRC_PLL] =
+ s->plls[RCC_PLL_PLL].channels[RCC_PLL_CHANNEL_PLLCLK],
+ [RCC_CLOCK_MUX_SRC_PLLSAI1] =
+ s->plls[RCC_PLL_PLLSAI1].channels[RCC_PLLSAI1_CHANNEL_PLLSAI1CLK],
+ [RCC_CLOCK_MUX_SRC_PLLSAI2] =
+ s->plls[RCC_PLL_PLLSAI2].channels[RCC_PLLSAI2_CHANNEL_PLLSAI2CLK],
+ [RCC_CLOCK_MUX_SRC_PLLSAI3] =
+ s->plls[RCC_PLL_PLL].channels[RCC_PLL_CHANNEL_PLLSAI3CLK],
+ [RCC_CLOCK_MUX_SRC_PLL48M1] =
+ s->plls[RCC_PLL_PLL].channels[RCC_PLL_CHANNEL_PLL48M1CLK],
+ [RCC_CLOCK_MUX_SRC_PLL48M2] =
+ s->plls[RCC_PLL_PLLSAI1].channels[RCC_PLLSAI1_CHANNEL_PLL48M2CLK],
+ [RCC_CLOCK_MUX_SRC_PLLADC1] =
+ s->plls[RCC_PLL_PLLSAI1].channels[RCC_PLLSAI1_CHANNEL_PLLADC1CLK],
+ [RCC_CLOCK_MUX_SRC_PLLADC2] =
+ s->plls[RCC_PLL_PLLSAI2] .channels[RCC_PLLSAI2_CHANNEL_PLLADC2CLK],
+ [RCC_CLOCK_MUX_SRC_SYSCLK] = s->clock_muxes[RCC_CLOCK_MUX_SYSCLK].out,
+ [RCC_CLOCK_MUX_SRC_HCLK] = s->clock_muxes[RCC_CLOCK_MUX_HCLK].out,
+ [RCC_CLOCK_MUX_SRC_PCLK1] = s->clock_muxes[RCC_CLOCK_MUX_PCLK1].out,
+ [RCC_CLOCK_MUX_SRC_PCLK2] = s->clock_muxes[RCC_CLOCK_MUX_PCLK2].out,
+ [RCC_CLOCK_MUX_SRC_HSE_OVER_32] = s->clock_muxes[RCC_CLOCK_MUX_HSE_OVER_32].out,
+ [RCC_CLOCK_MUX_SRC_LCD_AND_RTC_COMMON] =
+ s->clock_muxes[RCC_CLOCK_MUX_LCD_AND_RTC_COMMON].out,
+ };
+
+ assert(ARRAY_SIZE(CLK_SRC_MAPPING) == RCC_CLOCK_MUX_SRC_NUMBER);
+
+ for (i = 0; i < RCC_NUM_CLOCK_MUX_SRC; i++) {
+ RccClockMuxSource mapping = clk_mapping[i];
+ clock_set_source(mux->srcs[i], CLK_SRC_MAPPING[mapping]);
+ }
+}
+
+
static const VMStateDescription vmstate_stm32l4x5_rcc = {
.name = TYPE_STM32L4X5_RCC,
.version_id = 1,
@@ -712,11 +816,17 @@ static void stm32l4x5_rcc_realize(DeviceState *dev, Error **errp)
for (i = 0; i < RCC_NUM_CLOCK_MUX; i++) {
RccClockMuxState *clock_mux = &s->clock_muxes[i];
+ connect_mux_sources(s, clock_mux, CLOCK_MUX_INIT_INFO[i].src_mapping);
+
if (!qdev_realize(DEVICE(clock_mux), NULL, errp)) {
return;
}
}
+ /*
+ * Start clocks after everything is connected
+ * to propagate the frequencies along the tree.
+ */
clock_update_hz(s->msi_rc, MSI_DEFAULT_FRQ);
clock_update_hz(s->sai1_extclk, s->sai1_extclk_frequency);
clock_update_hz(s->sai2_extclk, s->sai2_extclk_frequency);
@@ -750,6 +860,7 @@ static void stm32l4x5_rcc_class_init(ObjectClass *klass, void *data)
DeviceClass *dc = DEVICE_CLASS(klass);
ResettableClass *rc = RESETTABLE_CLASS(klass);
+ assert(ARRAY_SIZE(CLOCK_MUX_INIT_INFO) == RCC_NUM_CLOCK_MUX);
rc->phases.hold = stm32l4x5_rcc_reset_hold;
device_class_set_props(dc, stm32l4x5_rcc_properties);
--
2.34.1
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [PULL 09/20] hw/misc/stm32l4x5_rcc: Handle Register Updates
2024-03-05 13:52 [PULL 00/20] target-arm queue Peter Maydell
` (7 preceding siblings ...)
2024-03-05 13:52 ` [PULL 08/20] hw/misc/stm32l4x5_rcc: Initialize PLLs and clock multiplexers Peter Maydell
@ 2024-03-05 13:52 ` Peter Maydell
2024-03-05 13:52 ` [PULL 10/20] hw/misc/stm32l4x5_rcc: Add write protections to CR register Peter Maydell
` (11 subsequent siblings)
20 siblings, 0 replies; 37+ messages in thread
From: Peter Maydell @ 2024-03-05 13:52 UTC (permalink / raw)
To: qemu-devel
From: Arnaud Minier <arnaud.minier@telecom-paris.fr>
Update the RCC state and propagate frequency changes when writing to the
RCC registers. Currently, ICSCR, CIER, the reset registers and the stop
mode registers are not implemented.
Some fields have not been implemented due to uncertainty about
how to handle them (Like the clock security system or bypassing
mecanisms).
Signed-off-by: Arnaud Minier <arnaud.minier@telecom-paris.fr>
Signed-off-by: Inès Varhol <ines.varhol@telecom-paris.fr>
Message-id: 20240303140643.81957-6-arnaud.minier@telecom-paris.fr
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
---
hw/misc/stm32l4x5_rcc.c | 524 +++++++++++++++++++++++++++++++++++++++-
1 file changed, 512 insertions(+), 12 deletions(-)
diff --git a/hw/misc/stm32l4x5_rcc.c b/hw/misc/stm32l4x5_rcc.c
index 083c0ad9ef5..2109f809e2b 100644
--- a/hw/misc/stm32l4x5_rcc.c
+++ b/hw/misc/stm32l4x5_rcc.c
@@ -28,6 +28,7 @@
#include "hw/qdev-clock.h"
#include "hw/qdev-properties.h"
#include "hw/qdev-properties-system.h"
+#include "hw/registerfields.h"
#include "trace.h"
#define HSE_DEFAULT_FRQ 48000000ULL
@@ -363,6 +364,9 @@ static void pll_set_channel_divider(RccPllState *pll,
static void rcc_update_irq(Stm32l4x5RccState *s)
{
+ /*
+ * TODO: Handle LSECSSF and CSSF flags when the CSS is implemented.
+ */
if (s->cifr & CIFR_IRQ_MASK) {
qemu_irq_raise(s->irq);
} else {
@@ -370,6 +374,472 @@ static void rcc_update_irq(Stm32l4x5RccState *s)
}
}
+static void rcc_update_cr_register(Stm32l4x5RccState *s)
+{
+ int val;
+
+ /* PLLSAI2ON and update PLLSAI2RDY */
+ val = FIELD_EX32(s->cr, CR, PLLSAI2ON);
+ pll_set_enable(&s->plls[RCC_PLL_PLLSAI2], val);
+ s->cr = (s->cr & ~R_CR_PLLSAI2RDY_MASK) |
+ (val << R_CR_PLLSAI2RDY_SHIFT);
+ if (s->cier & R_CIER_PLLSAI2RDYIE_MASK) {
+ s->cifr |= R_CIFR_PLLSAI2RDYF_MASK;
+ }
+
+ /* PLLSAI1ON and update PLLSAI1RDY */
+ val = FIELD_EX32(s->cr, CR, PLLSAI1ON);
+ pll_set_enable(&s->plls[RCC_PLL_PLLSAI1], val);
+ s->cr = (s->cr & ~R_CR_PLLSAI1RDY_MASK) |
+ (val << R_CR_PLLSAI1RDY_SHIFT);
+ if (s->cier & R_CIER_PLLSAI1RDYIE_MASK) {
+ s->cifr |= R_CIFR_PLLSAI1RDYF_MASK;
+ }
+
+ /* PLLON and update PLLRDY */
+ val = FIELD_EX32(s->cr, CR, PLLON);
+ pll_set_enable(&s->plls[RCC_PLL_PLL], val);
+ s->cr = (s->cr & ~R_CR_PLLRDY_MASK) |
+ (val << R_CR_PLLRDY_SHIFT);
+ if (s->cier & R_CIER_PLLRDYIE_MASK) {
+ s->cifr |= R_CIFR_PLLRDYF_MASK;
+ }
+
+ /* CSSON: TODO */
+ /* HSEBYP: TODO */
+
+ /* HSEON and update HSERDY */
+ val = FIELD_EX32(s->cr, CR, HSEON);
+ s->cr = (s->cr & ~R_CR_HSERDY_MASK) |
+ (val << R_CR_HSERDY_SHIFT);
+ if (val) {
+ clock_update_hz(s->hse, s->hse_frequency);
+ if (s->cier & R_CIER_HSERDYIE_MASK) {
+ s->cifr |= R_CIFR_HSERDYF_MASK;
+ }
+ } else {
+ clock_update(s->hse, 0);
+ }
+
+ /* HSIAFS: TODO*/
+ /* HSIKERON: TODO*/
+
+ /* HSION and update HSIRDY*/
+ val = FIELD_EX32(s->cr, CR, HSION);
+ s->cr = (s->cr & ~R_CR_HSIRDY_MASK) |
+ (val << R_CR_HSIRDY_SHIFT);
+ if (val) {
+ clock_update_hz(s->hsi16_rc, HSI_FRQ);
+ if (s->cier & R_CIER_HSIRDYIE_MASK) {
+ s->cifr |= R_CIFR_HSIRDYF_MASK;
+ }
+ } else {
+ clock_update(s->hsi16_rc, 0);
+ }
+
+ static const uint32_t msirange[] = {
+ 100000, 200000, 400000, 800000, 1000000, 2000000,
+ 4000000, 8000000, 16000000, 24000000, 32000000, 48000000
+ };
+ /* MSIRANGE and MSIRGSEL */
+ val = FIELD_EX32(s->cr, CR, MSIRGSEL);
+ if (val) {
+ /* MSIRGSEL is set, use the MSIRANGE field */
+ val = FIELD_EX32(s->cr, CR, MSIRANGE);
+ } else {
+ /* MSIRGSEL is not set, use the MSISRANGE field */
+ val = FIELD_EX32(s->csr, CSR, MSISRANGE);
+ }
+
+ if (val < ARRAY_SIZE(msirange)) {
+ clock_update_hz(s->msi_rc, msirange[val]);
+ } else {
+ clock_update_hz(s->msi_rc, MSI_DEFAULT_FRQ);
+ /* TODO: there is a write protection if the value is out of bound,
+ implement that instead of setting the default */
+ }
+
+ /* MSIPLLEN */
+
+ /* MSION and update MSIRDY */
+ val = FIELD_EX32(s->cr, CR, MSION);
+ s->cr = (s->cr & ~R_CR_MSIRDY_MASK) |
+ (val << R_CR_MSIRDY_SHIFT);
+ if (s->cier & R_CIER_MSIRDYIE_MASK) {
+ s->cifr |= R_CIFR_MSIRDYF_MASK;
+ }
+ rcc_update_irq(s);
+}
+
+static void rcc_update_cfgr_register(Stm32l4x5RccState *s)
+{
+ uint32_t val;
+ /* MCOPRE */
+ val = FIELD_EX32(s->cfgr, CFGR, MCOPRE);
+ assert(val <= 0b100);
+ clock_mux_set_factor(&s->clock_muxes[RCC_CLOCK_MUX_MCO],
+ 1, 1 << val);
+
+ /* MCOSEL */
+ val = FIELD_EX32(s->cfgr, CFGR, MCOSEL);
+ assert(val <= 0b111);
+ if (val == 0) {
+ clock_mux_set_enable(&s->clock_muxes[RCC_CLOCK_MUX_MCO], false);
+ } else {
+ clock_mux_set_enable(&s->clock_muxes[RCC_CLOCK_MUX_MCO], true);
+ clock_mux_set_source(&s->clock_muxes[RCC_CLOCK_MUX_MCO],
+ val - 1);
+ }
+
+ /* STOPWUCK */
+ /* TODO */
+
+ /* PPRE2 */
+ val = FIELD_EX32(s->cfgr, CFGR, PPRE2);
+ if (val < 0b100) {
+ clock_mux_set_factor(&s->clock_muxes[RCC_CLOCK_MUX_PCLK2],
+ 1, 1);
+ } else {
+ clock_mux_set_factor(&s->clock_muxes[RCC_CLOCK_MUX_PCLK2],
+ 1, 1 << (val - 0b11));
+ }
+
+ /* PPRE1 */
+ val = FIELD_EX32(s->cfgr, CFGR, PPRE1);
+ if (val < 0b100) {
+ clock_mux_set_factor(&s->clock_muxes[RCC_CLOCK_MUX_PCLK1],
+ 1, 1);
+ } else {
+ clock_mux_set_factor(&s->clock_muxes[RCC_CLOCK_MUX_PCLK1],
+ 1, 1 << (val - 0b11));
+ }
+
+ /* HPRE */
+ val = FIELD_EX32(s->cfgr, CFGR, HPRE);
+ if (val < 0b1000) {
+ clock_mux_set_factor(&s->clock_muxes[RCC_CLOCK_MUX_HCLK],
+ 1, 1);
+ } else {
+ clock_mux_set_factor(&s->clock_muxes[RCC_CLOCK_MUX_HCLK],
+ 1, 1 << (val - 0b111));
+ }
+
+ /* Update SWS */
+ val = FIELD_EX32(s->cfgr, CFGR, SW);
+ clock_mux_set_source(&s->clock_muxes[RCC_CLOCK_MUX_SYSCLK],
+ val);
+ s->cfgr &= ~R_CFGR_SWS_MASK;
+ s->cfgr |= val << R_CFGR_SWS_SHIFT;
+}
+
+static void rcc_update_ahb1enr(Stm32l4x5RccState *s)
+{
+ #define AHB1ENR_SET_ENABLE(_peripheral_name) \
+ clock_mux_set_enable(&s->clock_muxes[RCC_CLOCK_MUX_##_peripheral_name], \
+ FIELD_EX32(s->ahb1enr, AHB1ENR, _peripheral_name##EN))
+
+ /* DMA2DEN: reserved for STM32L475xx */
+ AHB1ENR_SET_ENABLE(TSC);
+ AHB1ENR_SET_ENABLE(CRC);
+ AHB1ENR_SET_ENABLE(FLASH);
+ AHB1ENR_SET_ENABLE(DMA2);
+ AHB1ENR_SET_ENABLE(DMA1);
+
+ #undef AHB1ENR_SET_ENABLE
+}
+
+static void rcc_update_ahb2enr(Stm32l4x5RccState *s)
+{
+ #define AHB2ENR_SET_ENABLE(_peripheral_name) \
+ clock_mux_set_enable(&s->clock_muxes[RCC_CLOCK_MUX_##_peripheral_name], \
+ FIELD_EX32(s->ahb2enr, AHB2ENR, _peripheral_name##EN))
+
+ AHB2ENR_SET_ENABLE(RNG);
+ /* HASHEN: reserved for STM32L475xx */
+ AHB2ENR_SET_ENABLE(AES);
+ /* DCMIEN: reserved for STM32L475xx */
+ AHB2ENR_SET_ENABLE(ADC);
+ AHB2ENR_SET_ENABLE(OTGFS);
+ /* GPIOIEN: reserved for STM32L475xx */
+ AHB2ENR_SET_ENABLE(GPIOA);
+ AHB2ENR_SET_ENABLE(GPIOB);
+ AHB2ENR_SET_ENABLE(GPIOC);
+ AHB2ENR_SET_ENABLE(GPIOD);
+ AHB2ENR_SET_ENABLE(GPIOE);
+ AHB2ENR_SET_ENABLE(GPIOF);
+ AHB2ENR_SET_ENABLE(GPIOG);
+ AHB2ENR_SET_ENABLE(GPIOH);
+
+ #undef AHB2ENR_SET_ENABLE
+}
+
+static void rcc_update_ahb3enr(Stm32l4x5RccState *s)
+{
+ #define AHB3ENR_SET_ENABLE(_peripheral_name) \
+ clock_mux_set_enable(&s->clock_muxes[RCC_CLOCK_MUX_##_peripheral_name], \
+ FIELD_EX32(s->ahb3enr, AHB3ENR, _peripheral_name##EN))
+
+ AHB3ENR_SET_ENABLE(QSPI);
+ AHB3ENR_SET_ENABLE(FMC);
+
+ #undef AHB3ENR_SET_ENABLE
+}
+
+static void rcc_update_apb1enr(Stm32l4x5RccState *s)
+{
+ #define APB1ENR1_SET_ENABLE(_peripheral_name) \
+ clock_mux_set_enable(&s->clock_muxes[RCC_CLOCK_MUX_##_peripheral_name], \
+ FIELD_EX32(s->apb1enr1, APB1ENR1, _peripheral_name##EN))
+ #define APB1ENR2_SET_ENABLE(_peripheral_name) \
+ clock_mux_set_enable(&s->clock_muxes[RCC_CLOCK_MUX_##_peripheral_name], \
+ FIELD_EX32(s->apb1enr2, APB1ENR2, _peripheral_name##EN))
+
+ /* APB1ENR1 */
+ APB1ENR1_SET_ENABLE(LPTIM1);
+ APB1ENR1_SET_ENABLE(OPAMP);
+ APB1ENR1_SET_ENABLE(DAC1);
+ APB1ENR1_SET_ENABLE(PWR);
+ /* CAN2: reserved for STM32L4x5 */
+ APB1ENR1_SET_ENABLE(CAN1);
+ /* CRSEN: reserved for STM32L4x5 */
+ APB1ENR1_SET_ENABLE(I2C3);
+ APB1ENR1_SET_ENABLE(I2C2);
+ APB1ENR1_SET_ENABLE(I2C1);
+ APB1ENR1_SET_ENABLE(UART5);
+ APB1ENR1_SET_ENABLE(UART4);
+ APB1ENR1_SET_ENABLE(USART3);
+ APB1ENR1_SET_ENABLE(USART2);
+ APB1ENR1_SET_ENABLE(SPI3);
+ APB1ENR1_SET_ENABLE(SPI2);
+ APB1ENR1_SET_ENABLE(WWDG);
+ /* RTCAPB: reserved for STM32L4x5 */
+ APB1ENR1_SET_ENABLE(LCD);
+ APB1ENR1_SET_ENABLE(TIM7);
+ APB1ENR1_SET_ENABLE(TIM6);
+ APB1ENR1_SET_ENABLE(TIM5);
+ APB1ENR1_SET_ENABLE(TIM4);
+ APB1ENR1_SET_ENABLE(TIM3);
+ APB1ENR1_SET_ENABLE(TIM2);
+
+ /* APB1ENR2 */
+ APB1ENR2_SET_ENABLE(LPTIM2);
+ APB1ENR2_SET_ENABLE(SWPMI1);
+ /* I2C4EN: reserved for STM32L4x5 */
+ APB1ENR2_SET_ENABLE(LPUART1);
+
+ #undef APB1ENR1_SET_ENABLE
+ #undef APB1ENR2_SET_ENABLE
+}
+
+static void rcc_update_apb2enr(Stm32l4x5RccState *s)
+{
+ #define APB2ENR_SET_ENABLE(_peripheral_name) \
+ clock_mux_set_enable(&s->clock_muxes[RCC_CLOCK_MUX_##_peripheral_name], \
+ FIELD_EX32(s->apb2enr, APB2ENR, _peripheral_name##EN))
+
+ APB2ENR_SET_ENABLE(DFSDM1);
+ APB2ENR_SET_ENABLE(SAI2);
+ APB2ENR_SET_ENABLE(SAI1);
+ APB2ENR_SET_ENABLE(TIM17);
+ APB2ENR_SET_ENABLE(TIM16);
+ APB2ENR_SET_ENABLE(TIM15);
+ APB2ENR_SET_ENABLE(USART1);
+ APB2ENR_SET_ENABLE(TIM8);
+ APB2ENR_SET_ENABLE(SPI1);
+ APB2ENR_SET_ENABLE(TIM1);
+ APB2ENR_SET_ENABLE(SDMMC1);
+ APB2ENR_SET_ENABLE(FW);
+ APB2ENR_SET_ENABLE(SYSCFG);
+
+ #undef APB2ENR_SET_ENABLE
+}
+
+/*
+ * The 3 PLLs share the same register layout
+ * so we can use the same function for all of them
+ * Note: no frequency bounds checking is done here.
+ */
+static void rcc_update_pllsaixcfgr(Stm32l4x5RccState *s, RccPll pll_id)
+{
+ uint32_t reg, val;
+ switch (pll_id) {
+ case RCC_PLL_PLL:
+ reg = s->pllcfgr;
+ break;
+ case RCC_PLL_PLLSAI1:
+ reg = s->pllsai1cfgr;
+ break;
+ case RCC_PLL_PLLSAI2:
+ reg = s->pllsai2cfgr;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Invalid PLL ID: %u\n", __func__, pll_id);
+ return;
+ }
+
+ /* PLLPDIV */
+ val = FIELD_EX32(reg, PLLCFGR, PLLPDIV);
+ /* 1 is a reserved value */
+ if (val == 0) {
+ /* Get PLLP value */
+ val = FIELD_EX32(reg, PLLCFGR, PLLP);
+ pll_set_channel_divider(&s->plls[pll_id], RCC_PLL_COMMON_CHANNEL_P,
+ (val ? 17 : 7));
+ } else if (val > 1) {
+ pll_set_channel_divider(&s->plls[pll_id], RCC_PLL_COMMON_CHANNEL_P,
+ val);
+ }
+
+
+ /* PLLR */
+ val = FIELD_EX32(reg, PLLCFGR, PLLR);
+ pll_set_channel_divider(&s->plls[pll_id], RCC_PLL_COMMON_CHANNEL_R,
+ 2 * (val + 1));
+
+ /* PLLREN */
+ val = FIELD_EX32(reg, PLLCFGR, PLLREN);
+ pll_set_channel_enable(&s->plls[pll_id], RCC_PLL_COMMON_CHANNEL_R, val);
+
+ /* PLLQ */
+ val = FIELD_EX32(reg, PLLCFGR, PLLQ);
+ pll_set_channel_divider(&s->plls[pll_id], RCC_PLL_COMMON_CHANNEL_Q,
+ 2 * (val + 1));
+
+ /* PLLQEN */
+ val = FIELD_EX32(reg, PLLCFGR, PLLQEN);
+ pll_set_channel_enable(&s->plls[pll_id], RCC_PLL_COMMON_CHANNEL_Q, val);
+
+ /* PLLPEN */
+ val = FIELD_EX32(reg, PLLCFGR, PLLPEN);
+ pll_set_channel_enable(&s->plls[pll_id], RCC_PLL_COMMON_CHANNEL_P, val);
+
+ /* PLLN */
+ val = FIELD_EX32(reg, PLLCFGR, PLLN);
+ pll_set_vco_multiplier(&s->plls[pll_id], val);
+}
+
+static void rcc_update_pllcfgr(Stm32l4x5RccState *s)
+{
+ int val;
+
+ /* Use common layout */
+ rcc_update_pllsaixcfgr(s, RCC_PLL_PLL);
+
+ /* Fetch specific fields for pllcfgr */
+
+ /* PLLM */
+ val = FIELD_EX32(s->pllcfgr, PLLCFGR, PLLM);
+ clock_mux_set_factor(&s->clock_muxes[RCC_CLOCK_MUX_PLL_INPUT], 1, (val + 1));
+
+ /* PLLSRC */
+ val = FIELD_EX32(s->pllcfgr, PLLCFGR, PLLSRC);
+ if (val == 0) {
+ clock_mux_set_enable(&s->clock_muxes[RCC_CLOCK_MUX_PLL_INPUT], false);
+ } else {
+ clock_mux_set_source(&s->clock_muxes[RCC_CLOCK_MUX_PLL_INPUT], val - 1);
+ clock_mux_set_enable(&s->clock_muxes[RCC_CLOCK_MUX_PLL_INPUT], true);
+ }
+}
+
+static void rcc_update_ccipr(Stm32l4x5RccState *s)
+{
+ #define CCIPR_SET_SOURCE(_peripheral_name) \
+ clock_mux_set_source(&s->clock_muxes[RCC_CLOCK_MUX_##_peripheral_name], \
+ FIELD_EX32(s->ccipr, CCIPR, _peripheral_name##SEL))
+
+ CCIPR_SET_SOURCE(DFSDM1);
+ CCIPR_SET_SOURCE(SWPMI1);
+ CCIPR_SET_SOURCE(ADC);
+ CCIPR_SET_SOURCE(CLK48);
+ CCIPR_SET_SOURCE(SAI2);
+ CCIPR_SET_SOURCE(SAI1);
+ CCIPR_SET_SOURCE(LPTIM2);
+ CCIPR_SET_SOURCE(LPTIM1);
+ CCIPR_SET_SOURCE(I2C3);
+ CCIPR_SET_SOURCE(I2C2);
+ CCIPR_SET_SOURCE(I2C1);
+ CCIPR_SET_SOURCE(LPUART1);
+ CCIPR_SET_SOURCE(UART5);
+ CCIPR_SET_SOURCE(UART4);
+ CCIPR_SET_SOURCE(USART3);
+ CCIPR_SET_SOURCE(USART2);
+ CCIPR_SET_SOURCE(USART1);
+
+ #undef CCIPR_SET_SOURCE
+}
+
+static void rcc_update_bdcr(Stm32l4x5RccState *s)
+{
+ int val;
+
+ /* LSCOSEL */
+ val = FIELD_EX32(s->bdcr, BDCR, LSCOSEL);
+ clock_mux_set_source(&s->clock_muxes[RCC_CLOCK_MUX_LSCO], val);
+
+ val = FIELD_EX32(s->bdcr, BDCR, LSCOEN);
+ clock_mux_set_enable(&s->clock_muxes[RCC_CLOCK_MUX_LSCO], val);
+
+ /* BDRST */
+ /*
+ * The documentation is not clear if the RTCEN flag disables the RTC and
+ * the LCD common mux or if it only affects the RTC.
+ * As the LCDEN flag exists, we assume here that it only affects the RTC.
+ */
+ val = FIELD_EX32(s->bdcr, BDCR, RTCEN);
+ clock_mux_set_enable(&s->clock_muxes[RCC_CLOCK_MUX_RTC], val);
+ /* LCD and RTC share the same clock */
+ val = FIELD_EX32(s->bdcr, BDCR, RTCSEL);
+ clock_mux_set_source(&s->clock_muxes[RCC_CLOCK_MUX_LCD_AND_RTC_COMMON], val);
+
+ /* LSECSSON */
+ /* LSEDRV[1:0] */
+ /* LSEBYP */
+
+ /* LSEON: Update LSERDY at the same time */
+ val = FIELD_EX32(s->bdcr, BDCR, LSEON);
+ if (val) {
+ clock_update_hz(s->lse_crystal, LSE_FRQ);
+ s->bdcr |= R_BDCR_LSERDY_MASK;
+ if (s->cier & R_CIER_LSERDYIE_MASK) {
+ s->cifr |= R_CIFR_LSERDYF_MASK;
+ }
+ } else {
+ clock_update(s->lse_crystal, 0);
+ s->bdcr &= ~R_BDCR_LSERDY_MASK;
+ }
+
+ rcc_update_irq(s);
+}
+
+static void rcc_update_csr(Stm32l4x5RccState *s)
+{
+ int val;
+
+ /* Reset flags: Not implemented */
+ /* MSISRANGE: Not implemented after reset */
+
+ /* LSION: Update LSIRDY at the same time */
+ val = FIELD_EX32(s->csr, CSR, LSION);
+ if (val) {
+ clock_update_hz(s->lsi_rc, LSI_FRQ);
+ s->csr |= R_CSR_LSIRDY_MASK;
+ if (s->cier & R_CIER_LSIRDYIE_MASK) {
+ s->cifr |= R_CIFR_LSIRDYF_MASK;
+ }
+ } else {
+ /*
+ * TODO: Handle when the LSI is set independently of LSION.
+ * E.g. when the LSI is set by the RTC.
+ * See the reference manual for more details.
+ */
+ clock_update(s->lsi_rc, 0);
+ s->csr &= ~R_CSR_LSIRDY_MASK;
+ }
+
+ rcc_update_irq(s);
+}
+
static void stm32l4x5_rcc_reset_hold(Object *obj)
{
Stm32l4x5RccState *s = STM32L4X5_RCC(obj);
@@ -529,24 +999,33 @@ static void stm32l4x5_rcc_write(void *opaque, hwaddr addr,
case A_CR:
s->cr = (s->cr & CR_READ_SET_MASK) |
(value & (CR_READ_SET_MASK | ~CR_READ_ONLY_MASK));
+ rcc_update_cr_register(s);
break;
case A_ICSCR:
s->icscr = value & ~ICSCR_READ_ONLY_MASK;
+ qemu_log_mask(LOG_UNIMP,
+ "%s: Side-effects not implemented for ICSCR\n", __func__);
break;
case A_CFGR:
s->cfgr = value & ~CFGR_READ_ONLY_MASK;
+ rcc_update_cfgr_register(s);
break;
case A_PLLCFGR:
s->pllcfgr = value;
+ rcc_update_pllcfgr(s);
break;
case A_PLLSAI1CFGR:
s->pllsai1cfgr = value;
+ rcc_update_pllsaixcfgr(s, RCC_PLL_PLLSAI1);
break;
case A_PLLSAI2CFGR:
s->pllsai2cfgr = value;
+ rcc_update_pllsaixcfgr(s, RCC_PLL_PLLSAI2);
break;
case A_CIER:
s->cier = value;
+ qemu_log_mask(LOG_UNIMP,
+ "%s: Side-effects not implemented for CIER\n", __func__);
break;
case A_CIFR:
qemu_log_mask(LOG_GUEST_ERROR,
@@ -561,67 +1040,100 @@ static void stm32l4x5_rcc_write(void *opaque, hwaddr addr,
/* Reset behaviors are not implemented */
case A_AHB1RSTR:
s->ahb1rstr = value;
+ qemu_log_mask(LOG_UNIMP,
+ "%s: Side-effects not implemented for AHB1RSTR\n", __func__);
break;
case A_AHB2RSTR:
s->ahb2rstr = value;
+ qemu_log_mask(LOG_UNIMP,
+ "%s: Side-effects not implemented for AHB2RSTR\n", __func__);
break;
case A_AHB3RSTR:
s->ahb3rstr = value;
+ qemu_log_mask(LOG_UNIMP,
+ "%s: Side-effects not implemented for AHB3RSTR\n", __func__);
break;
case A_APB1RSTR1:
s->apb1rstr1 = value;
+ qemu_log_mask(LOG_UNIMP,
+ "%s: Side-effects not implemented for APB1RSTR1\n", __func__);
break;
case A_APB1RSTR2:
s->apb1rstr2 = value;
+ qemu_log_mask(LOG_UNIMP,
+ "%s: Side-effects not implemented for APB1RSTR2\n", __func__);
break;
case A_APB2RSTR:
s->apb2rstr = value;
+ qemu_log_mask(LOG_UNIMP,
+ "%s: Side-effects not implemented for APB2RSTR\n", __func__);
break;
case A_AHB1ENR:
s->ahb1enr = value;
+ rcc_update_ahb1enr(s);
break;
case A_AHB2ENR:
s->ahb2enr = value;
+ rcc_update_ahb2enr(s);
break;
case A_AHB3ENR:
s->ahb3enr = value;
+ rcc_update_ahb3enr(s);
break;
case A_APB1ENR1:
s->apb1enr1 = value;
+ rcc_update_apb1enr(s);
break;
case A_APB1ENR2:
s->apb1enr2 = value;
+ rcc_update_apb1enr(s);
break;
case A_APB2ENR:
s->apb2enr = (s->apb2enr & APB2ENR_READ_SET_MASK) | value;
+ rcc_update_apb2enr(s);
break;
/* Behaviors for Sleep and Stop modes are not implemented */
case A_AHB1SMENR:
s->ahb1smenr = value;
+ qemu_log_mask(LOG_UNIMP,
+ "%s: Side-effects not implemented for AHB1SMENR\n", __func__);
break;
case A_AHB2SMENR:
s->ahb2smenr = value;
+ qemu_log_mask(LOG_UNIMP,
+ "%s: Side-effects not implemented for AHB2SMENR\n", __func__);
break;
case A_AHB3SMENR:
s->ahb3smenr = value;
+ qemu_log_mask(LOG_UNIMP,
+ "%s: Side-effects not implemented for AHB3SMENR\n", __func__);
break;
case A_APB1SMENR1:
s->apb1smenr1 = value;
+ qemu_log_mask(LOG_UNIMP,
+ "%s: Side-effects not implemented for APB1SMENR1\n", __func__);
break;
case A_APB1SMENR2:
s->apb1smenr2 = value;
+ qemu_log_mask(LOG_UNIMP,
+ "%s: Side-effects not implemented for APB1SMENR2\n", __func__);
break;
case A_APB2SMENR:
s->apb2smenr = value;
+ qemu_log_mask(LOG_UNIMP,
+ "%s: Side-effects not implemented for APB2SMENR\n", __func__);
break;
case A_CCIPR:
s->ccipr = value;
+ rcc_update_ccipr(s);
break;
case A_BDCR:
s->bdcr = value & ~BDCR_READ_ONLY_MASK;
+ rcc_update_bdcr(s);
break;
case A_CSR:
s->csr = value & ~CSR_READ_ONLY_MASK;
+ rcc_update_csr(s);
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
@@ -831,18 +1343,6 @@ static void stm32l4x5_rcc_realize(DeviceState *dev, Error **errp)
clock_update_hz(s->sai1_extclk, s->sai1_extclk_frequency);
clock_update_hz(s->sai2_extclk, s->sai2_extclk_frequency);
clock_update(s->gnd, 0);
-
- /*
- * Dummy values to make compilation pass.
- * Removed in later commits.
- */
- clock_mux_set_source(&s->clock_muxes[0], RCC_CLOCK_MUX_SRC_GND);
- clock_mux_set_enable(&s->clock_muxes[0], true);
- clock_mux_set_factor(&s->clock_muxes[0], 1, 1);
- pll_set_channel_divider(&s->plls[0], 0, 1);
- pll_set_enable(&s->plls[0], true);
- pll_set_channel_enable(&s->plls[0], 0, true);
- pll_set_vco_multiplier(&s->plls[0], 1);
}
static Property stm32l4x5_rcc_properties[] = {
--
2.34.1
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [PULL 10/20] hw/misc/stm32l4x5_rcc: Add write protections to CR register
2024-03-05 13:52 [PULL 00/20] target-arm queue Peter Maydell
` (8 preceding siblings ...)
2024-03-05 13:52 ` [PULL 09/20] hw/misc/stm32l4x5_rcc: Handle Register Updates Peter Maydell
@ 2024-03-05 13:52 ` Peter Maydell
2024-03-05 13:52 ` [PULL 11/20] hw/arm/stm32l4x5_soc.c: Use the RCC Sysclk Peter Maydell
` (10 subsequent siblings)
20 siblings, 0 replies; 37+ messages in thread
From: Peter Maydell @ 2024-03-05 13:52 UTC (permalink / raw)
To: qemu-devel
From: Arnaud Minier <arnaud.minier@telecom-paris.fr>
Add write protections for the fields in the CR register.
PLL configuration write protections (among others) have not
been handled yet. This is planned in a future patch set.
Signed-off-by: Arnaud Minier <arnaud.minier@telecom-paris.fr>
Signed-off-by: Inès Varhol <ines.varhol@telecom-paris.fr>
Message-id: 20240303140643.81957-7-arnaud.minier@telecom-paris.fr
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
---
hw/misc/stm32l4x5_rcc.c | 166 ++++++++++++++++++++++++++++------------
1 file changed, 115 insertions(+), 51 deletions(-)
diff --git a/hw/misc/stm32l4x5_rcc.c b/hw/misc/stm32l4x5_rcc.c
index 2109f809e2b..bc2d63528ba 100644
--- a/hw/misc/stm32l4x5_rcc.c
+++ b/hw/misc/stm32l4x5_rcc.c
@@ -374,9 +374,47 @@ static void rcc_update_irq(Stm32l4x5RccState *s)
}
}
-static void rcc_update_cr_register(Stm32l4x5RccState *s)
+static void rcc_update_msi(Stm32l4x5RccState *s, uint32_t previous_value)
+{
+ uint32_t val;
+
+ static const uint32_t msirange[] = {
+ 100000, 200000, 400000, 800000, 1000000, 2000000,
+ 4000000, 8000000, 16000000, 24000000, 32000000, 48000000
+ };
+ /* MSIRANGE and MSIRGSEL */
+ val = extract32(s->cr, R_CR_MSIRGSEL_SHIFT, R_CR_MSIRGSEL_LENGTH);
+ if (val) {
+ /* MSIRGSEL is set, use the MSIRANGE field */
+ val = extract32(s->cr, R_CR_MSIRANGE_SHIFT, R_CR_MSIRANGE_LENGTH);
+ } else {
+ /* MSIRGSEL is not set, use the MSISRANGE field */
+ val = extract32(s->csr, R_CSR_MSISRANGE_SHIFT, R_CSR_MSISRANGE_LENGTH);
+ }
+
+ if (val < ARRAY_SIZE(msirange)) {
+ clock_update_hz(s->msi_rc, msirange[val]);
+ } else {
+ /*
+ * There is a hardware write protection if the value is out of bound.
+ * Restore the previous value.
+ */
+ s->cr = (s->cr & ~R_CSR_MSISRANGE_MASK) |
+ (previous_value & R_CSR_MSISRANGE_MASK);
+ }
+}
+
+/*
+ * TODO: Add write-protection for all registers:
+ * DONE: CR
+ */
+
+static void rcc_update_cr_register(Stm32l4x5RccState *s, uint32_t previous_value)
{
int val;
+ const RccClockMuxSource current_pll_src =
+ CLOCK_MUX_INIT_INFO[RCC_CLOCK_MUX_PLL_INPUT].src_mapping[
+ s->clock_muxes[RCC_CLOCK_MUX_PLL_INPUT].src];
/* PLLSAI2ON and update PLLSAI2RDY */
val = FIELD_EX32(s->cr, CR, PLLSAI2ON);
@@ -396,77 +434,101 @@ static void rcc_update_cr_register(Stm32l4x5RccState *s)
s->cifr |= R_CIFR_PLLSAI1RDYF_MASK;
}
- /* PLLON and update PLLRDY */
+ /*
+ * PLLON and update PLLRDY
+ * PLLON cannot be reset if the PLL clock is used as the system clock.
+ */
val = FIELD_EX32(s->cr, CR, PLLON);
- pll_set_enable(&s->plls[RCC_PLL_PLL], val);
- s->cr = (s->cr & ~R_CR_PLLRDY_MASK) |
- (val << R_CR_PLLRDY_SHIFT);
- if (s->cier & R_CIER_PLLRDYIE_MASK) {
- s->cifr |= R_CIFR_PLLRDYF_MASK;
+ if (FIELD_EX32(s->cfgr, CFGR, SWS) != 0b11) {
+ pll_set_enable(&s->plls[RCC_PLL_PLL], val);
+ s->cr = (s->cr & ~R_CR_PLLRDY_MASK) |
+ (val << R_CR_PLLRDY_SHIFT);
+ if (s->cier & R_CIER_PLLRDYIE_MASK) {
+ s->cifr |= R_CIFR_PLLRDYF_MASK;
+ }
+ } else {
+ s->cr |= R_CR_PLLON_MASK;
}
/* CSSON: TODO */
/* HSEBYP: TODO */
- /* HSEON and update HSERDY */
+ /*
+ * HSEON and update HSERDY.
+ * HSEON cannot be reset if the HSE oscillator is used directly or
+ * indirectly as the system clock.
+ */
val = FIELD_EX32(s->cr, CR, HSEON);
- s->cr = (s->cr & ~R_CR_HSERDY_MASK) |
- (val << R_CR_HSERDY_SHIFT);
- if (val) {
- clock_update_hz(s->hse, s->hse_frequency);
- if (s->cier & R_CIER_HSERDYIE_MASK) {
- s->cifr |= R_CIFR_HSERDYF_MASK;
+ if (FIELD_EX32(s->cfgr, CFGR, SWS) != 0b10 &&
+ current_pll_src != RCC_CLOCK_MUX_SRC_HSE) {
+ s->cr = (s->cr & ~R_CR_HSERDY_MASK) |
+ (val << R_CR_HSERDY_SHIFT);
+ if (val) {
+ clock_update_hz(s->hse, s->hse_frequency);
+ if (s->cier & R_CIER_HSERDYIE_MASK) {
+ s->cifr |= R_CIFR_HSERDYF_MASK;
+ }
+ } else {
+ clock_update(s->hse, 0);
}
} else {
- clock_update(s->hse, 0);
+ s->cr |= R_CR_HSEON_MASK;
}
/* HSIAFS: TODO*/
/* HSIKERON: TODO*/
- /* HSION and update HSIRDY*/
- val = FIELD_EX32(s->cr, CR, HSION);
- s->cr = (s->cr & ~R_CR_HSIRDY_MASK) |
- (val << R_CR_HSIRDY_SHIFT);
- if (val) {
+ /*
+ * HSION and update HSIRDY
+ * HSION is set by hardware if the HSI16 is used directly
+ * or indirectly as system clock.
+ */
+ if (FIELD_EX32(s->cfgr, CFGR, SWS) == 0b01 ||
+ current_pll_src == RCC_CLOCK_MUX_SRC_HSI) {
+ s->cr |= (R_CR_HSION_MASK | R_CR_HSIRDY_MASK);
clock_update_hz(s->hsi16_rc, HSI_FRQ);
if (s->cier & R_CIER_HSIRDYIE_MASK) {
s->cifr |= R_CIFR_HSIRDYF_MASK;
}
} else {
- clock_update(s->hsi16_rc, 0);
+ val = FIELD_EX32(s->cr, CR, HSION);
+ if (val) {
+ clock_update_hz(s->hsi16_rc, HSI_FRQ);
+ s->cr |= R_CR_HSIRDY_MASK;
+ if (s->cier & R_CIER_HSIRDYIE_MASK) {
+ s->cifr |= R_CIFR_HSIRDYF_MASK;
+ }
+ } else {
+ clock_update(s->hsi16_rc, 0);
+ s->cr &= ~R_CR_HSIRDY_MASK;
+ }
}
- static const uint32_t msirange[] = {
- 100000, 200000, 400000, 800000, 1000000, 2000000,
- 4000000, 8000000, 16000000, 24000000, 32000000, 48000000
- };
- /* MSIRANGE and MSIRGSEL */
- val = FIELD_EX32(s->cr, CR, MSIRGSEL);
- if (val) {
- /* MSIRGSEL is set, use the MSIRANGE field */
- val = FIELD_EX32(s->cr, CR, MSIRANGE);
+ /* MSIPLLEN: TODO */
+
+ /*
+ * MSION and update MSIRDY
+ * Set by hardware when used directly or indirectly as system clock.
+ */
+ if (FIELD_EX32(s->cfgr, CFGR, SWS) == 0b00 ||
+ current_pll_src == RCC_CLOCK_MUX_SRC_MSI) {
+ s->cr |= (R_CR_MSION_MASK | R_CR_MSIRDY_MASK);
+ if (!(previous_value & R_CR_MSION_MASK) && (s->cier & R_CIER_MSIRDYIE_MASK)) {
+ s->cifr |= R_CIFR_MSIRDYF_MASK;
+ }
+ rcc_update_msi(s, previous_value);
} else {
- /* MSIRGSEL is not set, use the MSISRANGE field */
- val = FIELD_EX32(s->csr, CSR, MSISRANGE);
- }
-
- if (val < ARRAY_SIZE(msirange)) {
- clock_update_hz(s->msi_rc, msirange[val]);
- } else {
- clock_update_hz(s->msi_rc, MSI_DEFAULT_FRQ);
- /* TODO: there is a write protection if the value is out of bound,
- implement that instead of setting the default */
- }
-
- /* MSIPLLEN */
-
- /* MSION and update MSIRDY */
- val = FIELD_EX32(s->cr, CR, MSION);
- s->cr = (s->cr & ~R_CR_MSIRDY_MASK) |
- (val << R_CR_MSIRDY_SHIFT);
- if (s->cier & R_CIER_MSIRDYIE_MASK) {
- s->cifr |= R_CIFR_MSIRDYF_MASK;
+ val = FIELD_EX32(s->cr, CR, MSION);
+ if (val) {
+ s->cr |= R_CR_MSIRDY_MASK;
+ rcc_update_msi(s, previous_value);
+ if (s->cier & R_CIER_MSIRDYIE_MASK) {
+ s->cifr |= R_CIFR_MSIRDYF_MASK;
+ }
+ } else {
+ s->cr &= ~R_CR_MSIRDY_MASK;
+ clock_update(s->msi_rc, 0);
+ }
}
rcc_update_irq(s);
}
@@ -991,15 +1053,17 @@ static void stm32l4x5_rcc_write(void *opaque, hwaddr addr,
uint64_t val64, unsigned int size)
{
Stm32l4x5RccState *s = opaque;
+ uint32_t previous_value = 0;
const uint32_t value = val64;
trace_stm32l4x5_rcc_write(addr, value);
switch (addr) {
case A_CR:
+ previous_value = s->cr;
s->cr = (s->cr & CR_READ_SET_MASK) |
(value & (CR_READ_SET_MASK | ~CR_READ_ONLY_MASK));
- rcc_update_cr_register(s);
+ rcc_update_cr_register(s, previous_value);
break;
case A_ICSCR:
s->icscr = value & ~ICSCR_READ_ONLY_MASK;
--
2.34.1
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [PULL 11/20] hw/arm/stm32l4x5_soc.c: Use the RCC Sysclk
2024-03-05 13:52 [PULL 00/20] target-arm queue Peter Maydell
` (9 preceding siblings ...)
2024-03-05 13:52 ` [PULL 10/20] hw/misc/stm32l4x5_rcc: Add write protections to CR register Peter Maydell
@ 2024-03-05 13:52 ` Peter Maydell
2024-03-05 13:52 ` [PULL 12/20] tests/qtest/stm32l4x5_rcc-test.c: Add tests for the STM32L4x5_RCC Peter Maydell
` (9 subsequent siblings)
20 siblings, 0 replies; 37+ messages in thread
From: Peter Maydell @ 2024-03-05 13:52 UTC (permalink / raw)
To: qemu-devel
From: Arnaud Minier <arnaud.minier@telecom-paris.fr>
Now that we can generate reliable clock frequencies from the RCC, remove
the hacky definition of the sysclk in the b_l475e_iot01a initialisation
code and use the correct RCC clock.
Signed-off-by: Arnaud Minier <arnaud.minier@telecom-paris.fr>
Signed-off-by: Inès Varhol <ines.varhol@telecom-paris.fr>
Acked-by: Alistair Francis <alistair.francis@wdc.com>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Message-id: 20240303140643.81957-8-arnaud.minier@telecom-paris.fr
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
include/hw/arm/stm32l4x5_soc.h | 3 ---
hw/arm/b-l475e-iot01a.c | 10 +---------
hw/arm/stm32l4x5_soc.c | 33 ++++-----------------------------
3 files changed, 5 insertions(+), 41 deletions(-)
diff --git a/include/hw/arm/stm32l4x5_soc.h b/include/hw/arm/stm32l4x5_soc.h
index 0b4f97e240e..af67b089efc 100644
--- a/include/hw/arm/stm32l4x5_soc.h
+++ b/include/hw/arm/stm32l4x5_soc.h
@@ -54,9 +54,6 @@ struct Stm32l4x5SocState {
MemoryRegion sram2;
MemoryRegion flash;
MemoryRegion flash_alias;
-
- Clock *sysclk;
- Clock *refclk;
};
struct Stm32l4x5SocClass {
diff --git a/hw/arm/b-l475e-iot01a.c b/hw/arm/b-l475e-iot01a.c
index 6ecde2db15c..d862aa43fc3 100644
--- a/hw/arm/b-l475e-iot01a.c
+++ b/hw/arm/b-l475e-iot01a.c
@@ -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);
diff --git a/hw/arm/stm32l4x5_soc.c b/hw/arm/stm32l4x5_soc.c
index cb147050091..bf9926057be 100644
--- a/hw/arm/stm32l4x5_soc.c
+++ b/hw/arm/stm32l4x5_soc.c
@@ -110,9 +110,6 @@ 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);
-
- s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0);
- s->refclk = qdev_init_clock_in(DEVICE(s), "refclk", NULL, NULL, 0);
}
static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp)
@@ -124,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;
@@ -177,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)) {
--
2.34.1
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [PULL 12/20] tests/qtest/stm32l4x5_rcc-test.c: Add tests for the STM32L4x5_RCC
2024-03-05 13:52 [PULL 00/20] target-arm queue Peter Maydell
` (10 preceding siblings ...)
2024-03-05 13:52 ` [PULL 11/20] hw/arm/stm32l4x5_soc.c: Use the RCC Sysclk Peter Maydell
@ 2024-03-05 13:52 ` Peter Maydell
2024-03-05 13:52 ` [PULL 13/20] target/arm: Support 32-byte alignment in pow2_align Peter Maydell
` (8 subsequent siblings)
20 siblings, 0 replies; 37+ messages in thread
From: Peter Maydell @ 2024-03-05 13:52 UTC (permalink / raw)
To: qemu-devel
From: Arnaud Minier <arnaud.minier@telecom-paris.fr>
Tests:
- the ability to change the sysclk of the device
- the ability to enable/disable/configure the PLLs
- if the clock multiplexers work
- the register flags and the generation of irqs
Signed-off-by: Arnaud Minier <arnaud.minier@telecom-paris.fr>
Signed-off-by: Inès Varhol <ines.varhol@telecom-paris.fr>
Acked-by: Thomas Huth <thuth@redhat.com>
Message-id: 20240303140643.81957-9-arnaud.minier@telecom-paris.fr
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
---
tests/qtest/stm32l4x5_rcc-test.c | 189 +++++++++++++++++++++++++++++++
tests/qtest/meson.build | 3 +-
2 files changed, 191 insertions(+), 1 deletion(-)
create mode 100644 tests/qtest/stm32l4x5_rcc-test.c
diff --git a/tests/qtest/stm32l4x5_rcc-test.c b/tests/qtest/stm32l4x5_rcc-test.c
new file mode 100644
index 00000000000..d927c655d13
--- /dev/null
+++ b/tests/qtest/stm32l4x5_rcc-test.c
@@ -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;
+}
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index bdc8bba7a98..31b9f4ede47 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -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'] : []) + \
--
2.34.1
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [PULL 13/20] target/arm: Support 32-byte alignment in pow2_align
2024-03-05 13:52 [PULL 00/20] target-arm queue Peter Maydell
` (11 preceding siblings ...)
2024-03-05 13:52 ` [PULL 12/20] tests/qtest/stm32l4x5_rcc-test.c: Add tests for the STM32L4x5_RCC Peter Maydell
@ 2024-03-05 13:52 ` Peter Maydell
2024-03-05 13:52 ` [PULL 14/20] exec/memattrs: Remove target_tlb_bit* Peter Maydell
` (7 subsequent siblings)
20 siblings, 0 replies; 37+ messages in thread
From: Peter Maydell @ 2024-03-05 13:52 UTC (permalink / raw)
To: qemu-devel
From: Richard Henderson <richard.henderson@linaro.org>
Now that we have removed TARGET_PAGE_BITS_MIN-6 from
TLB_FLAGS_MASK, we can test for 32-byte alignment.
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Message-id: 20240301204110.656742-2-richard.henderson@linaro.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
target/arm/tcg/translate.c | 8 +-------
1 file changed, 1 insertion(+), 7 deletions(-)
diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c
index f947c62c6be..c8a24706750 100644
--- a/target/arm/tcg/translate.c
+++ b/target/arm/tcg/translate.c
@@ -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];
--
2.34.1
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [PULL 14/20] exec/memattrs: Remove target_tlb_bit*
2024-03-05 13:52 [PULL 00/20] target-arm queue Peter Maydell
` (12 preceding siblings ...)
2024-03-05 13:52 ` [PULL 13/20] target/arm: Support 32-byte alignment in pow2_align Peter Maydell
@ 2024-03-05 13:52 ` Peter Maydell
2024-03-05 13:52 ` [PULL 15/20] accel/tcg: Add tlb_fill_flags to CPUTLBEntryFull Peter Maydell
` (6 subsequent siblings)
20 siblings, 0 replies; 37+ messages in thread
From: Peter Maydell @ 2024-03-05 13:52 UTC (permalink / raw)
To: qemu-devel
From: Richard Henderson <richard.henderson@linaro.org>
These fields are no longer used since 937f224559.
Target specific extensions to the page tables should be done
with TARGET_PAGE_ENTRY_EXTRA.
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Message-id: 20240301204110.656742-3-richard.henderson@linaro.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
include/exec/memattrs.h | 10 ----------
1 file changed, 10 deletions(-)
diff --git a/include/exec/memattrs.h b/include/exec/memattrs.h
index d04170aa27a..afa885f9830 100644
--- a/include/exec/memattrs.h
+++ b/include/exec/memattrs.h
@@ -54,16 +54,6 @@ typedef struct MemTxAttrs {
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,
--
2.34.1
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [PULL 15/20] accel/tcg: Add tlb_fill_flags to CPUTLBEntryFull
2024-03-05 13:52 [PULL 00/20] target-arm queue Peter Maydell
` (13 preceding siblings ...)
2024-03-05 13:52 ` [PULL 14/20] exec/memattrs: Remove target_tlb_bit* Peter Maydell
@ 2024-03-05 13:52 ` Peter Maydell
2024-03-05 13:52 ` [PULL 16/20] accel/tcg: Add TLB_CHECK_ALIGNED Peter Maydell
` (5 subsequent siblings)
20 siblings, 0 replies; 37+ messages in thread
From: Peter Maydell @ 2024-03-05 13:52 UTC (permalink / raw)
To: qemu-devel
From: Richard Henderson <richard.henderson@linaro.org>
Allow the target to set tlb flags to apply to all of the
comparators. Remove MemTxAttrs.byte_swap, as the bit is
not relevant to memory transactions, only the page mapping.
Adjust target/sparc to set TLB_BSWAP directly.
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Message-id: 20240301204110.656742-4-richard.henderson@linaro.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
include/exec/memattrs.h | 2 --
include/hw/core/cpu.h | 3 +++
accel/tcg/cputlb.c | 5 +----
target/sparc/mmu_helper.c | 2 +-
4 files changed, 5 insertions(+), 7 deletions(-)
diff --git a/include/exec/memattrs.h b/include/exec/memattrs.h
index afa885f9830..14cdd8d5824 100644
--- a/include/exec/memattrs.h
+++ b/include/exec/memattrs.h
@@ -52,8 +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;
} MemTxAttrs;
/* Bus masters which don't specify any attributes will get this,
diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h
index d0e345419fc..ec14f74ce5d 100644
--- a/include/hw/core/cpu.h
+++ b/include/hw/core/cpu.h
@@ -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.
diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c
index 6243bcb1791..ac986cb8ea5 100644
--- a/accel/tcg/cputlb.c
+++ b/accel/tcg/cputlb.c
@@ -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);
diff --git a/target/sparc/mmu_helper.c b/target/sparc/mmu_helper.c
index 5170a668bb4..e7b1997d54e 100644
--- a/target/sparc/mmu_helper.c
+++ b/target/sparc/mmu_helper.c
@@ -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? */
--
2.34.1
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [PULL 16/20] accel/tcg: Add TLB_CHECK_ALIGNED
2024-03-05 13:52 [PULL 00/20] target-arm queue Peter Maydell
` (14 preceding siblings ...)
2024-03-05 13:52 ` [PULL 15/20] accel/tcg: Add tlb_fill_flags to CPUTLBEntryFull Peter Maydell
@ 2024-03-05 13:52 ` Peter Maydell
2024-03-05 13:52 ` [PULL 17/20] target/arm: Do memory type alignment check when translation disabled Peter Maydell
` (4 subsequent siblings)
20 siblings, 0 replies; 37+ messages in thread
From: Peter Maydell @ 2024-03-05 13:52 UTC (permalink / raw)
To: qemu-devel
From: Richard Henderson <richard.henderson@linaro.org>
This creates a per-page method for checking of alignment.
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Message-id: 20240301204110.656742-5-richard.henderson@linaro.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
include/exec/cpu-all.h | 4 +++-
accel/tcg/cputlb.c | 30 +++++++++++++++++++++++++++---
2 files changed, 30 insertions(+), 4 deletions(-)
diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h
index bc05dce7abe..1a6510fd3bf 100644
--- a/include/exec/cpu-all.h
+++ b/include/exec/cpu-all.h
@@ -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);
diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c
index ac986cb8ea5..93b1ca810bf 100644
--- a/accel/tcg/cputlb.c
+++ b/accel/tcg/cputlb.c
@@ -1453,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;
}
@@ -1836,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;
}
--
2.34.1
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [PULL 17/20] target/arm: Do memory type alignment check when translation disabled
2024-03-05 13:52 [PULL 00/20] target-arm queue Peter Maydell
` (15 preceding siblings ...)
2024-03-05 13:52 ` [PULL 16/20] accel/tcg: Add TLB_CHECK_ALIGNED Peter Maydell
@ 2024-03-05 13:52 ` Peter Maydell
2024-05-25 13:41 ` Bernhard Beschow
2024-08-28 7:22 ` Michael Tokarev
2024-03-05 13:52 ` [PULL 18/20] target/arm: Do memory type alignment check when translation enabled Peter Maydell
` (3 subsequent siblings)
20 siblings, 2 replies; 37+ messages in thread
From: Peter Maydell @ 2024-03-05 13:52 UTC (permalink / raw)
To: qemu-devel
From: Richard Henderson <richard.henderson@linaro.org>
If translation is disabled, the default memory type is Device, which
requires alignment checking. This is more optimally done early via
the MemOp given to the TCG memory operation.
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Reported-by: Idan Horowitz <idan.horowitz@gmail.com>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Message-id: 20240301204110.656742-6-richard.henderson@linaro.org
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1204
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
target/arm/tcg/hflags.c | 34 ++++++++++++++++++++++++++++++++--
1 file changed, 32 insertions(+), 2 deletions(-)
diff --git a/target/arm/tcg/hflags.c b/target/arm/tcg/hflags.c
index 8e5d35d9227..5da1b0fc1d4 100644
--- a/target/arm/tcg/hflags.c
+++ b/target/arm/tcg/hflags.c
@@ -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);
}
--
2.34.1
^ permalink raw reply related [flat|nested] 37+ messages in thread
* Re: [PULL 17/20] target/arm: Do memory type alignment check when translation disabled
2024-03-05 13:52 ` [PULL 17/20] target/arm: Do memory type alignment check when translation disabled Peter Maydell
@ 2024-05-25 13:41 ` Bernhard Beschow
2024-05-25 20:50 ` Bernhard Beschow
2024-08-09 16:07 ` Peter Maydell
2024-08-28 7:22 ` Michael Tokarev
1 sibling, 2 replies; 37+ messages in thread
From: Bernhard Beschow @ 2024-05-25 13:41 UTC (permalink / raw)
To: qemu-devel, Peter Maydell, Richard Henderson
Am 5. März 2024 13:52:34 UTC schrieb Peter Maydell <peter.maydell@linaro.org>:
>From: Richard Henderson <richard.henderson@linaro.org>
>
>If translation is disabled, the default memory type is Device, which
>requires alignment checking. This is more optimally done early via
>the MemOp given to the TCG memory operation.
>
>Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
>Reported-by: Idan Horowitz <idan.horowitz@gmail.com>
>Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
>Message-id: 20240301204110.656742-6-richard.henderson@linaro.org
>Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1204
>Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
>Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Hi,
This change causes an old 4.14.40 Linux kernel to panic on boot using the sabrelite machine:
[snip]
Alignment trap: init (1) PC=0x76f1e3d4 Instr=0x14913004 Address=0x76f34f3e FSR 0x001
Alignment trap: init (1) PC=0x76f1e3d8 Instr=0x148c3004 Address=0x7e8492bd FSR 0x801
Alignment trap: init (1) PC=0x76f0dab0 Instr=0x6823 Address=0x7e849fbb FSR 0x001
Alignment trap: init (1) PC=0x76f0dab2 Instr=0x6864 Address=0x7e849fbf FSR 0x001
scsi 0:0:0:0: Direct-Access QEMU QEMU HARDDISK 2.5+ PQ: 0 ANSI: 5
fsl-asoc-card sound: ASoC: CODEC DAI sgtl5000 not registered
imx-sgtl5000 sound: ASoC: CODEC DAI sgtl5000 not registered
imx-sgtl5000 sound: snd_soc_register_card failed (-517)
Alignment trap: init (1) PC=0x76eac95a Instr=0xf8dd5015 Address=0x7e849b05 FSR 0x001
Alignment trap: not handling instruction f8dd5015 at [<76eac95a>]
Unhandled fault: alignment exception (0x001) at 0x7e849b05
pgd = 9c59c000
[7e849b05] *pgd=2c552831, *pte=109eb34f, *ppte=109eb83f
Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000007
---[ end Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000007
As you can see, some alignment exceptions are handled by the kernel, the last one isn't. I added some additional printk()'s and traced it down to this location in the kernel: <https://github.com/torvalds/linux/blob/v4.14/arch/arm/mm/alignment.c#L762> which claims that ARMv6++ CPUs can handle up to word-sized unaligned accesses, thus no fixup is needed.
I hope that this will be sufficient for a fix. Let me know if you need any additional information.
Best regards,
Bernhard
>---
> target/arm/tcg/hflags.c | 34 ++++++++++++++++++++++++++++++++--
> 1 file changed, 32 insertions(+), 2 deletions(-)
>
>diff --git a/target/arm/tcg/hflags.c b/target/arm/tcg/hflags.c
>index 8e5d35d9227..5da1b0fc1d4 100644
>--- a/target/arm/tcg/hflags.c
>+++ b/target/arm/tcg/hflags.c
>@@ -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);
> }
>
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PULL 17/20] target/arm: Do memory type alignment check when translation disabled
2024-05-25 13:41 ` Bernhard Beschow
@ 2024-05-25 20:50 ` Bernhard Beschow
2024-05-27 2:36 ` Richard Henderson
2024-08-09 16:07 ` Peter Maydell
1 sibling, 1 reply; 37+ messages in thread
From: Bernhard Beschow @ 2024-05-25 20:50 UTC (permalink / raw)
To: qemu-devel, Peter Maydell, Richard Henderson
Am 25. Mai 2024 13:41:54 UTC schrieb Bernhard Beschow <shentey@gmail.com>:
>
>
>Am 5. März 2024 13:52:34 UTC schrieb Peter Maydell <peter.maydell@linaro.org>:
>>From: Richard Henderson <richard.henderson@linaro.org>
>>
>>If translation is disabled, the default memory type is Device, which
>>requires alignment checking. This is more optimally done early via
>>the MemOp given to the TCG memory operation.
>>
>>Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
>>Reported-by: Idan Horowitz <idan.horowitz@gmail.com>
>>Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
>>Message-id: 20240301204110.656742-6-richard.henderson@linaro.org
>>Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1204
>>Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
>>Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
>
>Hi,
>
>This change causes an old 4.14.40 Linux kernel to panic on boot using the sabrelite machine:
>
>[snip]
>Alignment trap: init (1) PC=0x76f1e3d4 Instr=0x14913004 Address=0x76f34f3e FSR 0x001
>Alignment trap: init (1) PC=0x76f1e3d8 Instr=0x148c3004 Address=0x7e8492bd FSR 0x801
>Alignment trap: init (1) PC=0x76f0dab0 Instr=0x6823 Address=0x7e849fbb FSR 0x001
>Alignment trap: init (1) PC=0x76f0dab2 Instr=0x6864 Address=0x7e849fbf FSR 0x001
>scsi 0:0:0:0: Direct-Access QEMU QEMU HARDDISK 2.5+ PQ: 0 ANSI: 5
>fsl-asoc-card sound: ASoC: CODEC DAI sgtl5000 not registered
>imx-sgtl5000 sound: ASoC: CODEC DAI sgtl5000 not registered
>imx-sgtl5000 sound: snd_soc_register_card failed (-517)
>Alignment trap: init (1) PC=0x76eac95a Instr=0xf8dd5015 Address=0x7e849b05 FSR 0x001
>Alignment trap: not handling instruction f8dd5015 at [<76eac95a>]
>Unhandled fault: alignment exception (0x001) at 0x7e849b05
>pgd = 9c59c000
>[7e849b05] *pgd=2c552831, *pte=109eb34f, *ppte=109eb83f
>Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000007
>
>---[ end Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000007
>
>As you can see, some alignment exceptions are handled by the kernel, the last one isn't. I added some additional printk()'s and traced it down to this location in the kernel: <https://github.com/torvalds/linux/blob/v4.14/arch/arm/mm/alignment.c#L762> which claims that ARMv6++ CPUs can handle up to word-sized unaligned accesses, thus no fixup is needed.
>
>I hope that this will be sufficient for a fix. Let me know if you need any additional information.
I'm performing a direct kernel boot. On real hardware, a bootloader is involved which probably enables unaligned access. This may explain why it works there but not in QEMU any longer.
To fix direct kernel boot, it seems as if the "built-in bootloader" would need to be adapted/extended [1]. Any ideas?
Best regards,
Bernhard
[1] https://stackoverflow.com/questions/68949890/how-does-qemu-emulate-a-kernel-without-a-bootloader
>
>Best regards,
>Bernhard
>
>>---
>> target/arm/tcg/hflags.c | 34 ++++++++++++++++++++++++++++++++--
>> 1 file changed, 32 insertions(+), 2 deletions(-)
>>
>>diff --git a/target/arm/tcg/hflags.c b/target/arm/tcg/hflags.c
>>index 8e5d35d9227..5da1b0fc1d4 100644
>>--- a/target/arm/tcg/hflags.c
>>+++ b/target/arm/tcg/hflags.c
>>@@ -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);
>> }
>>
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PULL 17/20] target/arm: Do memory type alignment check when translation disabled
2024-05-25 20:50 ` Bernhard Beschow
@ 2024-05-27 2:36 ` Richard Henderson
2024-05-27 10:58 ` Peter Maydell
0 siblings, 1 reply; 37+ messages in thread
From: Richard Henderson @ 2024-05-27 2:36 UTC (permalink / raw)
To: Bernhard Beschow, qemu-devel, Peter Maydell
On 5/25/24 13:50, Bernhard Beschow wrote:
>
>
> Am 25. Mai 2024 13:41:54 UTC schrieb Bernhard Beschow <shentey@gmail.com>:
>>
>>
>> Am 5. März 2024 13:52:34 UTC schrieb Peter Maydell <peter.maydell@linaro.org>:
>>> From: Richard Henderson <richard.henderson@linaro.org>
>>>
>>> If translation is disabled, the default memory type is Device, which
>>> requires alignment checking. This is more optimally done early via
>>> the MemOp given to the TCG memory operation.
>>>
>>> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
>>> Reported-by: Idan Horowitz <idan.horowitz@gmail.com>
>>> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
>>> Message-id: 20240301204110.656742-6-richard.henderson@linaro.org
>>> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1204
>>> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
>>> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
>>
>> Hi,
>>
>> This change causes an old 4.14.40 Linux kernel to panic on boot using the sabrelite machine:
>>
>> [snip]
>> Alignment trap: init (1) PC=0x76f1e3d4 Instr=0x14913004 Address=0x76f34f3e FSR 0x001
>> Alignment trap: init (1) PC=0x76f1e3d8 Instr=0x148c3004 Address=0x7e8492bd FSR 0x801
>> Alignment trap: init (1) PC=0x76f0dab0 Instr=0x6823 Address=0x7e849fbb FSR 0x001
>> Alignment trap: init (1) PC=0x76f0dab2 Instr=0x6864 Address=0x7e849fbf FSR 0x001
>> scsi 0:0:0:0: Direct-Access QEMU QEMU HARDDISK 2.5+ PQ: 0 ANSI: 5
>> fsl-asoc-card sound: ASoC: CODEC DAI sgtl5000 not registered
>> imx-sgtl5000 sound: ASoC: CODEC DAI sgtl5000 not registered
>> imx-sgtl5000 sound: snd_soc_register_card failed (-517)
>> Alignment trap: init (1) PC=0x76eac95a Instr=0xf8dd5015 Address=0x7e849b05 FSR 0x001
>> Alignment trap: not handling instruction f8dd5015 at [<76eac95a>]
>> Unhandled fault: alignment exception (0x001) at 0x7e849b05
>> pgd = 9c59c000
>> [7e849b05] *pgd=2c552831, *pte=109eb34f, *ppte=109eb83f
>> Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000007
>>
>> ---[ end Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000007
>>
>> As you can see, some alignment exceptions are handled by the kernel, the last one isn't. I added some additional printk()'s and traced it down to this location in the kernel: <https://github.com/torvalds/linux/blob/v4.14/arch/arm/mm/alignment.c#L762> which claims that ARMv6++ CPUs can handle up to word-sized unaligned accesses, thus no fixup is needed.
>>
>> I hope that this will be sufficient for a fix. Let me know if you need any additional information.
>
> I'm performing a direct kernel boot. On real hardware, a bootloader is involved which probably enables unaligned access. This may explain why it works there but not in QEMU any longer.
>
> To fix direct kernel boot, it seems as if the "built-in bootloader" would need to be adapted/extended [1]. Any ideas?
I strongly suspect a kernel bug. Either mmu disabled or attempting unaligned access on
pages mapped as Device instead of Normal.
r~
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PULL 17/20] target/arm: Do memory type alignment check when translation disabled
2024-05-27 2:36 ` Richard Henderson
@ 2024-05-27 10:58 ` Peter Maydell
2024-05-27 15:29 ` Bernhard Beschow
0 siblings, 1 reply; 37+ messages in thread
From: Peter Maydell @ 2024-05-27 10:58 UTC (permalink / raw)
To: Richard Henderson; +Cc: Bernhard Beschow, qemu-devel
On Mon, 27 May 2024 at 03:36, Richard Henderson
<richard.henderson@linaro.org> wrote:
>
> On 5/25/24 13:50, Bernhard Beschow wrote:
> >
> >
> > Am 25. Mai 2024 13:41:54 UTC schrieb Bernhard Beschow <shentey@gmail.com>:
> >>
> >>
> >> Am 5. März 2024 13:52:34 UTC schrieb Peter Maydell <peter.maydell@linaro.org>:
> >>> From: Richard Henderson <richard.henderson@linaro.org>
> >>>
> >>> If translation is disabled, the default memory type is Device, which
> >>> requires alignment checking. This is more optimally done early via
> >>> the MemOp given to the TCG memory operation.
> >>>
> >>> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
> >>> Reported-by: Idan Horowitz <idan.horowitz@gmail.com>
> >>> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
> >>> Message-id: 20240301204110.656742-6-richard.henderson@linaro.org
> >>> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1204
> >>> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
> >>> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
> >>
> >> Hi,
> >>
> >> This change causes an old 4.14.40 Linux kernel to panic on boot using the sabrelite machine:
> >>
> >> [snip]
> >> Alignment trap: init (1) PC=0x76f1e3d4 Instr=0x14913004 Address=0x76f34f3e FSR 0x001
> >> Alignment trap: init (1) PC=0x76f1e3d8 Instr=0x148c3004 Address=0x7e8492bd FSR 0x801
> >> Alignment trap: init (1) PC=0x76f0dab0 Instr=0x6823 Address=0x7e849fbb FSR 0x001
> >> Alignment trap: init (1) PC=0x76f0dab2 Instr=0x6864 Address=0x7e849fbf FSR 0x001
> >> scsi 0:0:0:0: Direct-Access QEMU QEMU HARDDISK 2.5+ PQ: 0 ANSI: 5
> >> fsl-asoc-card sound: ASoC: CODEC DAI sgtl5000 not registered
> >> imx-sgtl5000 sound: ASoC: CODEC DAI sgtl5000 not registered
> >> imx-sgtl5000 sound: snd_soc_register_card failed (-517)
> >> Alignment trap: init (1) PC=0x76eac95a Instr=0xf8dd5015 Address=0x7e849b05 FSR 0x001
> >> Alignment trap: not handling instruction f8dd5015 at [<76eac95a>]
> >> Unhandled fault: alignment exception (0x001) at 0x7e849b05
> >> pgd = 9c59c000
> >> [7e849b05] *pgd=2c552831, *pte=109eb34f, *ppte=109eb83f
> >> Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000007
> >>
> >> ---[ end Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000007
> >>
> >> As you can see, some alignment exceptions are handled by the kernel, the last one isn't. I added some additional printk()'s and traced it down to this location in the kernel: <https://github.com/torvalds/linux/blob/v4.14/arch/arm/mm/alignment.c#L762> which claims that ARMv6++ CPUs can handle up to word-sized unaligned accesses, thus no fixup is needed.
> >>
> >> I hope that this will be sufficient for a fix. Let me know if you need any additional information.
> >
> > I'm performing a direct kernel boot. On real hardware, a bootloader is involved which probably enables unaligned access. This may explain why it works there but not in QEMU any longer.
> >
> > To fix direct kernel boot, it seems as if the "built-in bootloader" would need to be adapted/extended [1]. Any ideas?
>
> I strongly suspect a kernel bug. Either mmu disabled or attempting unaligned access on
> pages mapped as Device instead of Normal.
The MMU surely must be enabled by this point in guest boot.
This change doesn't affect whether we do alignment checks based
on SCTLR.A being set, so it's not a simple "the bootloader was
supposed to clear that and it didn't" (besides, A=0 means no
checks, so that's the default anyway). So the failure is kind
of weird.
-- PMM
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PULL 17/20] target/arm: Do memory type alignment check when translation disabled
2024-05-27 10:58 ` Peter Maydell
@ 2024-05-27 15:29 ` Bernhard Beschow
2024-05-27 16:20 ` Richard Henderson
0 siblings, 1 reply; 37+ messages in thread
From: Bernhard Beschow @ 2024-05-27 15:29 UTC (permalink / raw)
To: Peter Maydell, Richard Henderson; +Cc: qemu-devel
Am 27. Mai 2024 10:58:54 UTC schrieb Peter Maydell <peter.maydell@linaro.org>:
>On Mon, 27 May 2024 at 03:36, Richard Henderson
><richard.henderson@linaro.org> wrote:
>>
>> On 5/25/24 13:50, Bernhard Beschow wrote:
>> >
>> >
>> > Am 25. Mai 2024 13:41:54 UTC schrieb Bernhard Beschow <shentey@gmail.com>:
>> >>
>> >>
>> >> Am 5. März 2024 13:52:34 UTC schrieb Peter Maydell <peter.maydell@linaro.org>:
>> >>> From: Richard Henderson <richard.henderson@linaro.org>
>> >>>
>> >>> If translation is disabled, the default memory type is Device, which
>> >>> requires alignment checking. This is more optimally done early via
>> >>> the MemOp given to the TCG memory operation.
>> >>>
>> >>> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
>> >>> Reported-by: Idan Horowitz <idan.horowitz@gmail.com>
>> >>> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
>> >>> Message-id: 20240301204110.656742-6-richard.henderson@linaro.org
>> >>> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1204
>> >>> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
>> >>> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
>> >>
>> >> Hi,
>> >>
>> >> This change causes an old 4.14.40 Linux kernel to panic on boot using the sabrelite machine:
>> >>
>> >> [snip]
>> >> Alignment trap: init (1) PC=0x76f1e3d4 Instr=0x14913004 Address=0x76f34f3e FSR 0x001
>> >> Alignment trap: init (1) PC=0x76f1e3d8 Instr=0x148c3004 Address=0x7e8492bd FSR 0x801
>> >> Alignment trap: init (1) PC=0x76f0dab0 Instr=0x6823 Address=0x7e849fbb FSR 0x001
>> >> Alignment trap: init (1) PC=0x76f0dab2 Instr=0x6864 Address=0x7e849fbf FSR 0x001
>> >> scsi 0:0:0:0: Direct-Access QEMU QEMU HARDDISK 2.5+ PQ: 0 ANSI: 5
>> >> fsl-asoc-card sound: ASoC: CODEC DAI sgtl5000 not registered
>> >> imx-sgtl5000 sound: ASoC: CODEC DAI sgtl5000 not registered
>> >> imx-sgtl5000 sound: snd_soc_register_card failed (-517)
>> >> Alignment trap: init (1) PC=0x76eac95a Instr=0xf8dd5015 Address=0x7e849b05 FSR 0x001
>> >> Alignment trap: not handling instruction f8dd5015 at [<76eac95a>]
>> >> Unhandled fault: alignment exception (0x001) at 0x7e849b05
>> >> pgd = 9c59c000
>> >> [7e849b05] *pgd=2c552831, *pte=109eb34f, *ppte=109eb83f
>> >> Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000007
>> >>
>> >> ---[ end Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000007
>> >>
>> >> As you can see, some alignment exceptions are handled by the kernel, the last one isn't. I added some additional printk()'s and traced it down to this location in the kernel: <https://github.com/torvalds/linux/blob/v4.14/arch/arm/mm/alignment.c#L762> which claims that ARMv6++ CPUs can handle up to word-sized unaligned accesses, thus no fixup is needed.
>> >>
>> >> I hope that this will be sufficient for a fix. Let me know if you need any additional information.
>> >
>> > I'm performing a direct kernel boot. On real hardware, a bootloader is involved which probably enables unaligned access. This may explain why it works there but not in QEMU any longer.
>> >
>> > To fix direct kernel boot, it seems as if the "built-in bootloader" would need to be adapted/extended [1]. Any ideas?
>>
>> I strongly suspect a kernel bug. Either mmu disabled or attempting unaligned access on
>> pages mapped as Device instead of Normal.
>
>The MMU surely must be enabled by this point in guest boot.
>This change doesn't affect whether we do alignment checks based
>on SCTLR.A being set, so it's not a simple "the bootloader was
>supposed to clear that and it didn't" (besides, A=0 means no
>checks, so that's the default anyway). So the failure is kind
>of weird.
I think the kernel's output indicates that the MMU is active:
[7e849b05] *pgd=2c552831, *pte=109eb34f, *ppte=109eb83f
AFAIU, the value in brackets is a virtual address while the pte's are physical ones. Furthermore, the `info mtree` QMP command tells that the physical addresses are RAM addresses:
0000000010000000-000000002fffffff (prio 0, ram): sabrelite.ram
So I think we can conclude this to be "normal memory" to speak in ARM terms.
Regarding the Linux kernel, it seems to me that it expects the unaligned accesses (up to word size) to be resolved by the hardware. On ARMv7 it can assume this, because the SCTLR.U bit is always set to 1 [1]. It then seems to only deal with cases which the hardware can't handle. In the case above, the unhandled instruction is (output from execlog plugin):
0, 0x76ecc95a, 0x5015f8dd, "ldr.w r5, [sp, #0x15]"
Note that the correct order of the machine code is
0xf8dd5015. This is not a pattern handled by the kernel, presumably because it expects it to be handled in hardware, hence the "not handling instruction xy" output.
I have the impression that real hardware only traps when the hardware can't handle the unaligned access, and only when the SCTLR.A bit is set.
I'm not an ARM expert, so take this with a grain of salt.
Best regards,
Bernhard
[1] https://developer.arm.com/documentation/ddi0406/cb/Appendixes/ARMv6-Differences/Application-level-memory-support/Alignment
>
>-- PMM
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PULL 17/20] target/arm: Do memory type alignment check when translation disabled
2024-05-27 15:29 ` Bernhard Beschow
@ 2024-05-27 16:20 ` Richard Henderson
2024-05-27 17:49 ` Bernhard Beschow
0 siblings, 1 reply; 37+ messages in thread
From: Richard Henderson @ 2024-05-27 16:20 UTC (permalink / raw)
To: Bernhard Beschow, Peter Maydell; +Cc: qemu-devel
On 5/27/24 08:29, Bernhard Beschow wrote:
> I think the kernel's output indicates that the MMU is active:
>
> [7e849b05] *pgd=2c552831, *pte=109eb34f, *ppte=109eb83f
>
> AFAIU, the value in brackets is a virtual address while the pte's are physical ones. Furthermore, the `info mtree` QMP command tells that the physical addresses are RAM addresses:
>
> 0000000010000000-000000002fffffff (prio 0, ram): sabrelite.ram
>
> So I think we can conclude this to be "normal memory" to speak in ARM terms.
Normal and Device are attributes on the page table entry.
See section G5.7 Memory region attributes in the Arm ARM.
But it's unlikely that the Linux kernel has messed this up, even back in 4.x days.
If you want to make any progress, you'll have to share a test case.
r~
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PULL 17/20] target/arm: Do memory type alignment check when translation disabled
2024-05-27 16:20 ` Richard Henderson
@ 2024-05-27 17:49 ` Bernhard Beschow
2024-07-05 11:46 ` Bernhard Beschow
0 siblings, 1 reply; 37+ messages in thread
From: Bernhard Beschow @ 2024-05-27 17:49 UTC (permalink / raw)
To: Richard Henderson, Peter Maydell; +Cc: qemu-devel
Am 27. Mai 2024 16:20:44 UTC schrieb Richard Henderson <richard.henderson@linaro.org>:
>On 5/27/24 08:29, Bernhard Beschow wrote:
>> I think the kernel's output indicates that the MMU is active:
>>
>> [7e849b05] *pgd=2c552831, *pte=109eb34f, *ppte=109eb83f
>>
>> AFAIU, the value in brackets is a virtual address while the pte's are physical ones. Furthermore, the `info mtree` QMP command tells that the physical addresses are RAM addresses:
>>
>> 0000000010000000-000000002fffffff (prio 0, ram): sabrelite.ram
>>
>> So I think we can conclude this to be "normal memory" to speak in ARM terms.
>
>Normal and Device are attributes on the page table entry.
>See section G5.7 Memory region attributes in the Arm ARM.
>
>But it's unlikely that the Linux kernel has messed this up, even back in 4.x days.
>
>If you want to make any progress, you'll have to share a test case.
It's a proprietary guest, so I need to strip it down first. This may take some time. Thanks for yor feedbak so far!
Best regards,
Bernhard
>
>
>r~
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PULL 17/20] target/arm: Do memory type alignment check when translation disabled
2024-05-27 17:49 ` Bernhard Beschow
@ 2024-07-05 11:46 ` Bernhard Beschow
2024-07-05 17:08 ` Peter Maydell
0 siblings, 1 reply; 37+ messages in thread
From: Bernhard Beschow @ 2024-07-05 11:46 UTC (permalink / raw)
To: Richard Henderson, Peter Maydell; +Cc: qemu-devel
Am 27. Mai 2024 17:49:26 UTC schrieb Bernhard Beschow <shentey@gmail.com>:
>
>
>Am 27. Mai 2024 16:20:44 UTC schrieb Richard Henderson <richard.henderson@linaro.org>:
>>On 5/27/24 08:29, Bernhard Beschow wrote:
>>> I think the kernel's output indicates that the MMU is active:
>>>
>>> [7e849b05] *pgd=2c552831, *pte=109eb34f, *ppte=109eb83f
>>>
>>> AFAIU, the value in brackets is a virtual address while the pte's are physical ones. Furthermore, the `info mtree` QMP command tells that the physical addresses are RAM addresses:
>>>
>>> 0000000010000000-000000002fffffff (prio 0, ram): sabrelite.ram
>>>
>>> So I think we can conclude this to be "normal memory" to speak in ARM terms.
>>
>>Normal and Device are attributes on the page table entry.
>>See section G5.7 Memory region attributes in the Arm ARM.
>>
>>But it's unlikely that the Linux kernel has messed this up, even back in 4.x days.
>>
>>If you want to make any progress, you'll have to share a test case.
>
>It's a proprietary guest, so I need to strip it down first. This may take some time. Thanks for yor feedbak so far!
I finally had some time to look deeper into it. While this patch triggered alignment issues, it is not the culprit. The culprit is that the sabrelite board sets arm_boot_info::secure_boot = true which causes the Linux kernel to run in EL3 mode where hardware alignment fixing is apparently not performed. Setting it to false fixes all problems and the guest boots just fine.
Question: Does it make sense to ignore the secure_boot flag on direct kernel boot? If not, what do you suggest?
Thanks,
Bernhard
>
>Best regards,
>Bernhard
>>
>>
>>r~
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PULL 17/20] target/arm: Do memory type alignment check when translation disabled
2024-07-05 11:46 ` Bernhard Beschow
@ 2024-07-05 17:08 ` Peter Maydell
0 siblings, 0 replies; 37+ messages in thread
From: Peter Maydell @ 2024-07-05 17:08 UTC (permalink / raw)
To: Bernhard Beschow; +Cc: Richard Henderson, qemu-devel
On Fri, 5 Jul 2024 at 12:46, Bernhard Beschow <shentey@gmail.com> wrote:
> Am 27. Mai 2024 17:49:26 UTC schrieb Bernhard Beschow <shentey@gmail.com>:
> >Am 27. Mai 2024 16:20:44 UTC schrieb Richard Henderson <richard.henderson@linaro.org>:
> >>On 5/27/24 08:29, Bernhard Beschow wrote:
> >>> I think the kernel's output indicates that the MMU is active:
> >>>
> >>> [7e849b05] *pgd=2c552831, *pte=109eb34f, *ppte=109eb83f
> >>>
> >>> AFAIU, the value in brackets is a virtual address while the pte's are physical ones. Furthermore, the `info mtree` QMP command tells that the physical addresses are RAM addresses:
> >>>
> >>> 0000000010000000-000000002fffffff (prio 0, ram): sabrelite.ram
> >>>
> >>> So I think we can conclude this to be "normal memory" to speak in ARM terms.
> >>
> >>Normal and Device are attributes on the page table entry.
> >>See section G5.7 Memory region attributes in the Arm ARM.
> >>
> >>But it's unlikely that the Linux kernel has messed this up, even back in 4.x days.
> >>
> >>If you want to make any progress, you'll have to share a test case.
> >
> >It's a proprietary guest, so I need to strip it down first. This may take some time. Thanks for yor feedbak so far!
>
> I finally had some time to look deeper into it. While this patch triggered alignment issues, it is not the culprit. The culprit is that the sabrelite board sets arm_boot_info::secure_boot = true which causes the Linux kernel to run in EL3 mode where hardware alignment fixing is apparently not performed. Setting it to false fixes all problems and the guest boots just fine.
>
> Question: Does it make sense to ignore the secure_boot flag on direct kernel boot? If not, what do you suggest?
The secure_boot flag specifically means "when direct booting
a kernel, do it in Secure SVC, not NonSecure Hyp or NonSecure SVC";
it has no effect on the boot of bios/baremetal images.
It's only supposed to be set on the (very few) boards where that's
how kernels are booted, and kernels that boot on those boards are
supposed to be able to handle being booted that way...
thanks
-- PMM
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PULL 17/20] target/arm: Do memory type alignment check when translation disabled
2024-05-25 13:41 ` Bernhard Beschow
2024-05-25 20:50 ` Bernhard Beschow
@ 2024-08-09 16:07 ` Peter Maydell
1 sibling, 0 replies; 37+ messages in thread
From: Peter Maydell @ 2024-08-09 16:07 UTC (permalink / raw)
To: Bernhard Beschow; +Cc: qemu-devel, Richard Henderson
On Sat, 25 May 2024 at 14:41, Bernhard Beschow <shentey@gmail.com> wrote:
>
>
>
> Am 5. März 2024 13:52:34 UTC schrieb Peter Maydell <peter.maydell@linaro.org>:
> >From: Richard Henderson <richard.henderson@linaro.org>
> >
> >If translation is disabled, the default memory type is Device, which
> >requires alignment checking. This is more optimally done early via
> >the MemOp given to the TCG memory operation.
> >
> >Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
> >Reported-by: Idan Horowitz <idan.horowitz@gmail.com>
> >Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
> >Message-id: 20240301204110.656742-6-richard.henderson@linaro.org
> >Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1204
> >Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
> >Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
>
> Hi,
>
> This change causes an old 4.14.40 Linux kernel to panic on boot using the sabrelite machine:
>
> [snip]
> Alignment trap: init (1) PC=0x76f1e3d4 Instr=0x14913004 Address=0x76f34f3e FSR 0x001
> Alignment trap: init (1) PC=0x76f1e3d8 Instr=0x148c3004 Address=0x7e8492bd FSR 0x801
> Alignment trap: init (1) PC=0x76f0dab0 Instr=0x6823 Address=0x7e849fbb FSR 0x001
> Alignment trap: init (1) PC=0x76f0dab2 Instr=0x6864 Address=0x7e849fbf FSR 0x001
> scsi 0:0:0:0: Direct-Access QEMU QEMU HARDDISK 2.5+ PQ: 0 ANSI: 5
> fsl-asoc-card sound: ASoC: CODEC DAI sgtl5000 not registered
> imx-sgtl5000 sound: ASoC: CODEC DAI sgtl5000 not registered
> imx-sgtl5000 sound: snd_soc_register_card failed (-517)
> Alignment trap: init (1) PC=0x76eac95a Instr=0xf8dd5015 Address=0x7e849b05 FSR 0x001
> Alignment trap: not handling instruction f8dd5015 at [<76eac95a>]
> Unhandled fault: alignment exception (0x001) at 0x7e849b05
> pgd = 9c59c000
> [7e849b05] *pgd=2c552831, *pte=109eb34f, *ppte=109eb83f
> Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000007
>
> ---[ end Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000007
I think this is the same bug as reported in
https://gitlab.com/qemu-project/qemu/-/issues/2326
and which I've just sent a patchset for:
https://patchew.org/QEMU/20240809160430.1144805-1-peter.maydell@linaro.org/
(The problem was that we were looking at the wrong banked
SCTLR when running at Secure EL0.)
thanks
-- PMM
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PULL 17/20] target/arm: Do memory type alignment check when translation disabled
2024-03-05 13:52 ` [PULL 17/20] target/arm: Do memory type alignment check when translation disabled Peter Maydell
2024-05-25 13:41 ` Bernhard Beschow
@ 2024-08-28 7:22 ` Michael Tokarev
2024-08-28 11:07 ` Richard Henderson
2024-08-28 15:51 ` Peter Maydell
1 sibling, 2 replies; 37+ messages in thread
From: Michael Tokarev @ 2024-08-28 7:22 UTC (permalink / raw)
To: Peter Maydell, qemu-devel, Richard Henderson
05.03.2024 16:52, Peter Maydell wrote:
> From: Richard Henderson <richard.henderson@linaro.org>
>
> If translation is disabled, the default memory type is Device, which
> requires alignment checking. This is more optimally done early via
> the MemOp given to the TCG memory operation.
>
> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
> Reported-by: Idan Horowitz <idan.horowitz@gmail.com>
> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
> Message-id: 20240301204110.656742-6-richard.henderson@linaro.org
> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1204
> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Hi!
Apparently this change also breaks picolibc testsuite (between
8.2 and 9.0, bisect points to this commit).
For example:
./qemu-system-arm \
-m 1G \
-chardev stdio,mux=on,id=stdio0 \
-semihosting-config enable=on,chardev=stdio0,arg=program-name \
-monitor none \
-serial none \
-machine none,accel=tcg \
-cpu cortex-a8 \
-device loader,file=/tmp/picolibc-1.8.6/arm-none-eabi/test/printf_scanf_thumb_v7_fp_softfp,cpu-num=0 \
-nographic
(yes, this testsuite uses qemu-system as a substitute of
qemu-user, sort of, (ab)using -device loader)
Before this change:
hello world 1
checking floating point
checking pos args
checking long long
checking c99 formats
(exit code = 0)
After this change:
hello world 1
checking floating point
checking pos args
ARM fault: undef
R0: 0x00000002
R1: 0x00005c90
R2: 0x201ffeac
R3: 0x20200000
R4: 0x00000000
R5: 0x20000004
R6: 0x201ffec4
PC: 0x00000364
Another test from the same picolibc:
timeout 1s ./qemu-system-arm \
-m 1G \
-chardev stdio,mux=on,id=stdio0 \
-semihosting-config enable=on,chardev=stdio0,arg=program-name \
-monitor none \
-serial none \
-machine none,accel=tcg \
-cpu cortex-a7 \
-device loader,file=/tmp/picolibc-1.8.6/arm-none-eabi/newlib/testsuite/newlib.string/tstring_thumb_v7_nofp,cpu-num=0 \
-nographic
This one succeeds immediately before this change, and
just times out (qemu is basically doing nothing, according to
strace) after this commit.
Exactly the same happens up to current qemu master (ie, 9.1-tobe).
So is not https://gitlab.com/qemu-project/qemu/-/issues/2326
and is not fixed by 4c2c0474693229c1f533239bb983495c5427784d
"target/arm: Fix usage of MMU indexes when EL3 is AArch32".
picolibc is built this way:
picolibc-1.8.6$ meson setup . arm-none-eabi \
--prefix=/usr \
-Dc_args='-Wdate-time' \
-Dtests=true \
--cross-file scripts/cross-arm-none-eabi.txt \
-Dspecsdir=/usr/lib/picolibc/arm-none-eabi \
-Dincludedir=lib/picolibc/arm-none-eabi/include \
-Dlibdir=lib/picolibc/arm-none-eabi/lib
Thanks,
/mjt
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PULL 17/20] target/arm: Do memory type alignment check when translation disabled
2024-08-28 7:22 ` Michael Tokarev
@ 2024-08-28 11:07 ` Richard Henderson
2024-08-28 11:27 ` Michael Tokarev
2024-08-28 15:51 ` Peter Maydell
1 sibling, 1 reply; 37+ messages in thread
From: Richard Henderson @ 2024-08-28 11:07 UTC (permalink / raw)
To: Michael Tokarev, Peter Maydell, qemu-devel
On 8/28/24 17:22, Michael Tokarev wrote:
> 05.03.2024 16:52, Peter Maydell wrote:
>> From: Richard Henderson <richard.henderson@linaro.org>
>>
>> If translation is disabled, the default memory type is Device, which
>> requires alignment checking. This is more optimally done early via
>> the MemOp given to the TCG memory operation.
>>
>> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
>> Reported-by: Idan Horowitz <idan.horowitz@gmail.com>
>> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
>> Message-id: 20240301204110.656742-6-richard.henderson@linaro.org
>> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1204
>> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
>> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
>
> Hi!
>
> Apparently this change also breaks picolibc testsuite (between
> 8.2 and 9.0, bisect points to this commit).
>
> For example:
>
> ./qemu-system-arm \
> -m 1G \
> -chardev stdio,mux=on,id=stdio0 \
> -semihosting-config enable=on,chardev=stdio0,arg=program-name \
> -monitor none \
> -serial none \
> -machine none,accel=tcg \
> -cpu cortex-a8 \
> -device loader,file=/tmp/picolibc-1.8.6/arm-none-eabi/test/
> printf_scanf_thumb_v7_fp_softfp,cpu-num=0 \
> -nographic
Almost certainly a duplicate of #2326, fixed in master by
4c2c0474693229c1f533239bb983495c5427784d.
r~
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PULL 17/20] target/arm: Do memory type alignment check when translation disabled
2024-08-28 11:07 ` Richard Henderson
@ 2024-08-28 11:27 ` Michael Tokarev
0 siblings, 0 replies; 37+ messages in thread
From: Michael Tokarev @ 2024-08-28 11:27 UTC (permalink / raw)
To: Richard Henderson, Peter Maydell, qemu-devel
28.08.2024 14:07, Richard Henderson wrote:
> On 8/28/24 17:22, Michael Tokarev wrote:
>> 05.03.2024 16:52, Peter Maydell wrote:
>>> From: Richard Henderson <richard.henderson@linaro.org>
>>>
>>> If translation is disabled, the default memory type is Device, which
>>> requires alignment checking. This is more optimally done early via
>>> the MemOp given to the TCG memory operation.
>>>
>>> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
>>> Reported-by: Idan Horowitz <idan.horowitz@gmail.com>
>>> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
>>> Message-id: 20240301204110.656742-6-richard.henderson@linaro.org
>>> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1204
>>> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
>>> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
>>
>> Hi!
>>
>> Apparently this change also breaks picolibc testsuite (between
>> 8.2 and 9.0, bisect points to this commit).
>>
>> For example:
>>
>> ./qemu-system-arm \
>> -m 1G \
>> -chardev stdio,mux=on,id=stdio0 \
>> -semihosting-config enable=on,chardev=stdio0,arg=program-name \
>> -monitor none \
>> -serial none \
>> -machine none,accel=tcg \
>> -cpu cortex-a8 \
>> -device loader,file=/tmp/picolibc-1.8.6/arm-none-eabi/test/ printf_scanf_thumb_v7_fp_softfp,cpu-num=0 \
>> -nographic
>
> Almost certainly a duplicate of #2326, fixed in master by 4c2c0474693229c1f533239bb983495c5427784d.
Hi Richard!
You can read my email to the end, where I mentioned that this problem
is NOT fixed in current master and by this commit in particular.
Thanks,
/mjt
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PULL 17/20] target/arm: Do memory type alignment check when translation disabled
2024-08-28 7:22 ` Michael Tokarev
2024-08-28 11:07 ` Richard Henderson
@ 2024-08-28 15:51 ` Peter Maydell
2024-08-29 17:25 ` Peter Maydell
1 sibling, 1 reply; 37+ messages in thread
From: Peter Maydell @ 2024-08-28 15:51 UTC (permalink / raw)
To: Michael Tokarev; +Cc: qemu-devel, Richard Henderson
On Wed, 28 Aug 2024 at 08:22, Michael Tokarev <mjt@tls.msk.ru> wrote:
>
> 05.03.2024 16:52, Peter Maydell wrote:
> > From: Richard Henderson <richard.henderson@linaro.org>
> >
> > If translation is disabled, the default memory type is Device, which
> > requires alignment checking. This is more optimally done early via
> > the MemOp given to the TCG memory operation.
> >
> > Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
> > Reported-by: Idan Horowitz <idan.horowitz@gmail.com>
> > Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
> > Message-id: 20240301204110.656742-6-richard.henderson@linaro.org
> > Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1204
> > Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
> > Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
>
> Hi!
>
> Apparently this change also breaks picolibc testsuite (between
> 8.2 and 9.0, bisect points to this commit).
>
> For example:
>
> ./qemu-system-arm \
> -m 1G \
> -chardev stdio,mux=on,id=stdio0 \
> -semihosting-config enable=on,chardev=stdio0,arg=program-name \
> -monitor none \
> -serial none \
> -machine none,accel=tcg \
> -cpu cortex-a8 \
> -device loader,file=/tmp/picolibc-1.8.6/arm-none-eabi/test/printf_scanf_thumb_v7_fp_softfp,cpu-num=0 \
> -nographic
>
> (yes, this testsuite uses qemu-system as a substitute of
> qemu-user, sort of, (ab)using -device loader)
My immediate guess is that this code won't run on real hardware
either -- i.e. that is bare-metal code that was only ever tested
and run on QEMU and was previously relying on the incorrect
behaviour that we didn't enforce the alignment checks that we're
supposed to do when the MMU is off.
We'd need to look at the picolibc test harness and build system
to be sure, but it needs to do one of:
* tell the compiler never to generate nonaligned accesses
* set up at least a simple 1:1 set of page tables and
turn on the MMU before jumping to C code
and my first move would be to check whether it is trying to
do either of those things.
thanks
-- PMM
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PULL 17/20] target/arm: Do memory type alignment check when translation disabled
2024-08-28 15:51 ` Peter Maydell
@ 2024-08-29 17:25 ` Peter Maydell
0 siblings, 0 replies; 37+ messages in thread
From: Peter Maydell @ 2024-08-29 17:25 UTC (permalink / raw)
To: Michael Tokarev; +Cc: qemu-devel, Richard Henderson
On Wed, 28 Aug 2024 at 16:51, Peter Maydell <peter.maydell@linaro.org> wrote:
>
> On Wed, 28 Aug 2024 at 08:22, Michael Tokarev <mjt@tls.msk.ru> wrote:
> >
> > 05.03.2024 16:52, Peter Maydell wrote:
> > > From: Richard Henderson <richard.henderson@linaro.org>
> > >
> > > If translation is disabled, the default memory type is Device, which
> > > requires alignment checking. This is more optimally done early via
> > > the MemOp given to the TCG memory operation.
> > >
> > > Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
> > > Reported-by: Idan Horowitz <idan.horowitz@gmail.com>
> > > Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
> > > Message-id: 20240301204110.656742-6-richard.henderson@linaro.org
> > > Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1204
> > > Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
> > > Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
> >
> > Hi!
> >
> > Apparently this change also breaks picolibc testsuite (between
> > 8.2 and 9.0, bisect points to this commit).
> >
> > For example:
> >
> > ./qemu-system-arm \
> > -m 1G \
> > -chardev stdio,mux=on,id=stdio0 \
> > -semihosting-config enable=on,chardev=stdio0,arg=program-name \
> > -monitor none \
> > -serial none \
> > -machine none,accel=tcg \
> > -cpu cortex-a8 \
> > -device loader,file=/tmp/picolibc-1.8.6/arm-none-eabi/test/printf_scanf_thumb_v7_fp_softfp,cpu-num=0 \
> > -nographic
> >
> > (yes, this testsuite uses qemu-system as a substitute of
> > qemu-user, sort of, (ab)using -device loader)
>
> My immediate guess is that this code won't run on real hardware
> either -- i.e. that is bare-metal code that was only ever tested
> and run on QEMU and was previously relying on the incorrect
> behaviour that we didn't enforce the alignment checks that we're
> supposed to do when the MMU is off.
I had a look at the test binary you kindly provided in
https://gitlab.com/qemu-project/qemu/-/issues/2542
and that confirmed my guess. This binary would never have
worked on real hardware, and it only worked on older QEMU
because we weren't correctly emulating this corner of the
architecture. You need to either use a new enough picolibc
that you can turn on its _PICOCRT_ENABLE_MMU option, or
else make sure everything is built with gcc's -mstrict-align
or similar option to avoid any unaligned loads.
thanks
-- PMM
^ permalink raw reply [flat|nested] 37+ messages in thread
* [PULL 18/20] target/arm: Do memory type alignment check when translation enabled
2024-03-05 13:52 [PULL 00/20] target-arm queue Peter Maydell
` (16 preceding siblings ...)
2024-03-05 13:52 ` [PULL 17/20] target/arm: Do memory type alignment check when translation disabled Peter Maydell
@ 2024-03-05 13:52 ` Peter Maydell
2024-03-05 13:52 ` [PULL 19/20] atomic.h: Reword confusing comment for qatomic_cmpxchg Peter Maydell
` (2 subsequent siblings)
20 siblings, 0 replies; 37+ messages in thread
From: Peter Maydell @ 2024-03-05 13:52 UTC (permalink / raw)
To: qemu-devel
From: Richard Henderson <richard.henderson@linaro.org>
If translation is enabled, and the PTE memory type is Device,
enable checking alignment via TLB_CHECK_ALIGNMENT. While the
check is done later than it should be per the ARM, it's better
than not performing the check at all.
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Message-id: 20240301204110.656742-7-richard.henderson@linaro.org
[PMM: tweaks to comment text]
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
---
target/arm/ptw.c | 39 +++++++++++++++++++++++++++++++++++++++
1 file changed, 39 insertions(+)
diff --git a/target/arm/ptw.c b/target/arm/ptw.c
index ba1a27ca2b5..31ae43f60ed 100644
--- a/target/arm/ptw.c
+++ b/target/arm/ptw.c
@@ -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;
}
/*
--
2.34.1
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [PULL 19/20] atomic.h: Reword confusing comment for qatomic_cmpxchg
2024-03-05 13:52 [PULL 00/20] target-arm queue Peter Maydell
` (17 preceding siblings ...)
2024-03-05 13:52 ` [PULL 18/20] target/arm: Do memory type alignment check when translation enabled Peter Maydell
@ 2024-03-05 13:52 ` Peter Maydell
2024-03-05 13:52 ` [PULL 20/20] qemu-options.hx: Don't claim "-serial" has limit of 4 serial ports Peter Maydell
2024-03-05 15:26 ` [PULL 00/20] target-arm queue Peter Maydell
20 siblings, 0 replies; 37+ messages in thread
From: Peter Maydell @ 2024-03-05 13:52 UTC (permalink / raw)
To: qemu-devel
The qatomic_cmpxchg() and qatomic_cmpxchg__nocheck() macros have
a comment that reads:
Returns the eventual value, failed or not
This is somewhere between cryptic and wrong, since the value actually
returned is the value that was in memory before the cmpxchg. Reword
to match how we describe these macros in atomics.rst.
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
Message-id: 20240223182035.1048541-1-peter.maydell@linaro.org
---
include/qemu/atomic.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/include/qemu/atomic.h b/include/qemu/atomic.h
index f1d3d1702a9..99110abefb3 100644
--- a/include/qemu/atomic.h
+++ b/include/qemu/atomic.h
@@ -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, \
--
2.34.1
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [PULL 20/20] qemu-options.hx: Don't claim "-serial" has limit of 4 serial ports
2024-03-05 13:52 [PULL 00/20] target-arm queue Peter Maydell
` (18 preceding siblings ...)
2024-03-05 13:52 ` [PULL 19/20] atomic.h: Reword confusing comment for qatomic_cmpxchg Peter Maydell
@ 2024-03-05 13:52 ` Peter Maydell
2024-03-05 15:26 ` [PULL 00/20] target-arm queue Peter Maydell
20 siblings, 0 replies; 37+ messages in thread
From: Peter Maydell @ 2024-03-05 13:52 UTC (permalink / raw)
To: qemu-devel
From: Steven Shen <steven.shen@jaguarmicro.com>
Before v2.12, the implementation of serial ports was limited to
a value of MAX_SERIAL_PORTS = 4. We now dynamically allocate
the data structures for serial ports, so this limit is no longer
present, but the documentation for the -serial options still reads:
"This option can be used several times to simulate up to 4 serial ports."
Update to "This option can be used several times to simulate
multiple serial ports." to avoid misleading.
Signed-off-by: Steven Shen <steven.shen@jaguarmicro.com>
Message-id: 20240305013016.2268-1-steven.shen@jaguarmicro.com
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
[PMM: tweaked commit message]
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
qemu-options.hx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/qemu-options.hx b/qemu-options.hx
index 9a47385c157..ac4a30fa834 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -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
--
2.34.1
^ permalink raw reply related [flat|nested] 37+ messages in thread
* Re: [PULL 00/20] target-arm queue
2024-03-05 13:52 [PULL 00/20] target-arm queue Peter Maydell
` (19 preceding siblings ...)
2024-03-05 13:52 ` [PULL 20/20] qemu-options.hx: Don't claim "-serial" has limit of 4 serial ports Peter Maydell
@ 2024-03-05 15:26 ` Peter Maydell
20 siblings, 0 replies; 37+ messages in thread
From: Peter Maydell @ 2024-03-05 15:26 UTC (permalink / raw)
To: qemu-devel
On Tue, 5 Mar 2024 at 13:52, Peter Maydell <peter.maydell@linaro.org> wrote:
>
> The following changes since commit 4eac9dfbd72d346505642fb45ac3141c7eb2c516:
>
> Merge tag 'pull-tcg-20240301' of https://gitlab.com/rth7680/qemu into staging (2024-03-05 09:45:22 +0000)
>
> are available in the Git repository at:
>
> https://git.linaro.org/people/pmaydell/qemu-arm.git tags/pull-target-arm-20240305
>
> for you to fetch changes up to 7558300c53057126514ee0fd5cf629c65ccc20e1:
>
> qemu-options.hx: Don't claim "-serial" has limit of 4 serial ports (2024-03-05 13:22:56 +0000)
>
> ----------------------------------------------------------------
> 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
>
> ----------------------------------------------------------------
Applied, thanks.
Please update the changelog at https://wiki.qemu.org/ChangeLog/9.0
for any user-visible changes.
-- PMM
^ permalink raw reply [flat|nested] 37+ messages in thread