From c5c57db171691fabeccd1643a12a97d237c1e33f Mon Sep 17 00:00:00 2001 From: Alex Jones Date: Thu, 20 Nov 2025 16:35:27 +0000 Subject: [PATCH 1/3] [ot] hw/opentitan: ot_eg_pad_ring: Add initial stubbed Earlgrey Padring Only contains the Power-on Reset (PoR) pad for now. While the PoR egress IRQ is maintained, a separate PoR IRQ is introduced for linking with the Rstmgr to allow to correctly specify the reset reason. This is hardcoded with a separate dedicated signal for now to avoid overcomplicating the interface, especially given that refactoring will likely be needed anyhow for MIO pads for pinmux. Also adds TODOs detailing the remaining steps to support the missing features from Earlgrey's pad ring (in loose detail). Signed-off-by: Alex Jones --- hw/opentitan/Kconfig | 3 + hw/opentitan/meson.build | 1 + hw/opentitan/ot_eg_pad_ring.c | 206 ++++++++++++++++++++++++++ hw/opentitan/trace-events | 4 + hw/riscv/Kconfig | 1 + include/hw/opentitan/ot_eg_pad_ring.h | 74 +++++++++ 6 files changed, 289 insertions(+) create mode 100644 hw/opentitan/ot_eg_pad_ring.c create mode 100644 include/hw/opentitan/ot_eg_pad_ring.h diff --git a/hw/opentitan/Kconfig b/hw/opentitan/Kconfig index 8c1f5c6ae25eb..3a98d8e0cb931 100644 --- a/hw/opentitan/Kconfig +++ b/hw/opentitan/Kconfig @@ -53,6 +53,9 @@ config OT_DMA config OT_EDN bool +config OT_EG_PAD_RING + bool + config OT_ENTROPY_SRC select OT_NOISE_SRC select OT_OTP_IF diff --git a/hw/opentitan/meson.build b/hw/opentitan/meson.build index 48fb5d9e4cccf..2d557ac037508 100644 --- a/hw/opentitan/meson.build +++ b/hw/opentitan/meson.build @@ -18,6 +18,7 @@ system_ss.add(when: 'CONFIG_OT_DEV_PROXY', if_true: files('ot_dev_proxy.c')) system_ss.add(when: 'CONFIG_OT_DM_TL', if_true: files('ot_dm_tl.c')) system_ss.add(when: 'CONFIG_OT_DMA', if_true: [files('ot_dma.c'), libtomcrypt_dep]) system_ss.add(when: 'CONFIG_OT_EDN', if_true: files('ot_edn.c')) +system_ss.add(when: 'CONFIG_OT_EG_PAD_RING', if_true: files('ot_eg_pad_ring.c')) system_ss.add(when: 'CONFIG_OT_ENTROPY_SRC', if_true: [files('ot_entropy_src.c'), libtomcrypt_dep]) system_ss.add(when: 'CONFIG_OT_FLASH', if_true: files('ot_flash.c')) system_ss.add(when: 'CONFIG_OT_GPIO_DJ', if_true: files('ot_gpio_dj.c')) diff --git a/hw/opentitan/ot_eg_pad_ring.c b/hw/opentitan/ot_eg_pad_ring.c new file mode 100644 index 0000000000000..a5524563d9599 --- /dev/null +++ b/hw/opentitan/ot_eg_pad_ring.c @@ -0,0 +1,206 @@ +/* + * QEMU OpenTitan Earlgrey Pad Ring device + * + * Copyright (c) 2025 lowRISC contributors. + * + * Author(s): + * Emmanuel Blot + * Alex Jones + * + * 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. + * + * This device currently only implements the Power-on-Reset (POR) pad, and does + * not support any of the Muxed IO pads. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qapi/error.h" +#include "hw/opentitan/ot_common.h" +#include "hw/opentitan/ot_eg_pad_ring.h" +#include "hw/opentitan/ot_rstmgr.h" +#include "hw/qdev-properties.h" +#include "hw/riscv/ibex_gpio.h" +#include "hw/riscv/ibex_irq.h" +#include "trace.h" + +struct OtEgPadRingState { + DeviceState parent_obj; + + IbexIRQ outputs[OT_EG_PAD_RING_PAD_COUNT]; + IbexIRQ por; /* Separate PoR IRQ to send the correct rstmgr reset req */ + + ibex_gpio default_levels[OT_EG_PAD_RING_PAD_COUNT]; + + /* properties */ + char *ot_id; +}; + +struct OtEgPadRingClass { + SysBusDeviceClass parent_class; + ResettablePhases parent_phases; +}; + +static const ibex_gpio DEFAULT_LEVELS[OT_EG_PAD_RING_PAD_COUNT] = { + [OT_EG_PAD_RING_PAD_POR_N] = IBEX_GPIO_PULL_UP, +}; + +#define PAD_NAME_ENTRY(_pad_) [OT_EG_PAD_RING_PAD_##_pad_] = stringify(_pad_) +static const char *PAD_NAMES[] = { + /* clang-format off */ + PAD_NAME_ENTRY(POR_N), + /* clang-format on */ +}; +#define PAD_NAME(_pad_) \ + ((((_pad_) < ARRAY_SIZE(PAD_NAMES)) && PAD_NAMES[_pad_]) ? \ + PAD_NAMES[_pad_] : \ + "?") + +static void ot_eg_pad_ring_por_update(OtEgPadRingState *s) +{ + int level = ibex_irq_get_level(&s->outputs[OT_EG_PAD_RING_PAD_POR_N]); + bool blevel = ibex_gpio_level(level); + + /** + * @todo: The current implementation directly invokes a rstmgr reset req + * on a falling edge on the PoR pad. In reality, this pin can be held high + * which should hold the device in reset, but this is not currently + * supported via the rstmgr interface. Hence, if the host wishes to rely on + * holding the device in reset, it must stop the VM when setting PoR high & + * later resume when setting it low again. + */ + if (ibex_gpio_is_hiz(level) || blevel) { + return; + } + + ibex_irq_set(&s->por, OT_RSTMGR_RESET_POR); +} + +static Property ot_eg_pad_ring_properties[] = { + DEFINE_PROP_STRING(OT_COMMON_DEV_ID, OtEgPadRingState, ot_id), + DEFINE_PROP_END_OF_LIST(), +}; + +static void ot_eg_pad_ring_reset_enter(Object *obj, ResetType type) +{ + OtEgPadRingClass *c = OT_EG_PAD_RING_GET_CLASS(obj); + OtEgPadRingState *s = OT_EG_PAD_RING(obj); + + trace_ot_eg_pad_ring_reset(s->ot_id, "enter"); + + if (c->parent_phases.enter) { + c->parent_phases.enter(obj, type); + } + + for (unsigned ix = 0; ix < OT_EG_PAD_RING_PAD_COUNT; ix++) { + /* POR may cause and thus should be maintained through reset */ + if (ix != OT_EG_PAD_RING_PAD_POR_N) { + /* current pad outputs should be cleared (Hi-Z) upon reset */ + ibex_irq_set(&s->outputs[ix], IBEX_GPIO_HIZ); + } + } + + /* Reset the dedicated PoR signal */ + ibex_irq_set(&s->por, 0); +} + +static void ot_eg_pad_ring_reset_exit(Object *obj, ResetType type) +{ + OtEgPadRingClass *c = OT_EG_PAD_RING_GET_CLASS(obj); + OtEgPadRingState *s = OT_EG_PAD_RING(obj); + + trace_ot_eg_pad_ring_reset(s->ot_id, "exit"); + + if (c->parent_phases.exit) { + c->parent_phases.exit(obj, type); + } + + for (unsigned ix = 0; ix < OT_EG_PAD_RING_PAD_COUNT; ix++) { + trace_ot_eg_pad_ring_reset_value(s->ot_id, PAD_NAME(ix), + ibex_gpio_repr(s->default_levels[ix])); + ibex_irq_set(&s->outputs[ix], s->default_levels[ix]); + } + + /* Handle special cases where dedicated output signals are used */ + ot_eg_pad_ring_por_update(s); +} + +static void ot_eg_pad_ring_realize(DeviceState *dev, Error **errp) +{ + OtEgPadRingState *s = OT_EG_PAD_RING(dev); + (void)errp; + + ibex_qdev_init_irqs_default(OBJECT(dev), s->outputs, + OT_EG_PAD_RING_PAD_EGRESS, + OT_EG_PAD_RING_PAD_COUNT, -1); +} + +static void ot_eg_pad_ring_init(Object *obj) +{ + OtEgPadRingState *s = OT_EG_PAD_RING(obj); + + ibex_qdev_init_irq(obj, &s->por, OT_EG_PAD_RING_POR_REQ); + + for (unsigned ix = 0; ix < OT_EG_PAD_RING_PAD_COUNT; ix++) { + gchar *pad_name = g_ascii_strdown(PAD_NAME(ix), -1); + s->default_levels[ix] = DEFAULT_LEVELS[ix]; + object_property_add_ibex_gpio(obj, pad_name, &s->default_levels[ix]); + g_free(pad_name); + } +} + +static void ot_eg_pad_ring_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + (void)data; + + dc->realize = &ot_eg_pad_ring_realize; + device_class_set_props(dc, ot_eg_pad_ring_properties); + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + + /* + * Implement the resettable interface to ensure the package configuration is + * forwarded to the PinMux/GPIO on each reset sequence (this is not yet used + * in Earlgrey, but is added for future-proofing). + * The enter stage fully reinitializes the package config to HiZ default + * values, and the exit stage pushes the actual package configuration to the + * connected pinmux/IOs. + */ + ResettableClass *rc = RESETTABLE_CLASS(dc); + OtEgPadRingClass *pc = OT_EG_PAD_RING_CLASS(klass); + resettable_class_set_parent_phases(rc, &ot_eg_pad_ring_reset_enter, NULL, + &ot_eg_pad_ring_reset_exit, + &pc->parent_phases); +} + +static const TypeInfo ot_eg_pad_ring_info = { + .name = TYPE_OT_EG_PAD_RING, + .parent = TYPE_DEVICE, + .instance_size = sizeof(OtEgPadRingState), + .instance_init = &ot_eg_pad_ring_init, + .class_init = &ot_eg_pad_ring_class_init, + .class_size = sizeof(OtEgPadRingClass) +}; + +static void ot_eg_pad_ring_register_types(void) +{ + type_register_static(&ot_eg_pad_ring_info); +} + +type_init(ot_eg_pad_ring_register_types); diff --git a/hw/opentitan/trace-events b/hw/opentitan/trace-events index 3ed8b02e2d044..afe5cffa12063 100644 --- a/hw/opentitan/trace-events +++ b/hw/opentitan/trace-events @@ -176,6 +176,10 @@ ot_edn_schedule(unsigned appid, const char *cause) "a#%u %s" ot_edn_update_genbits_ready(unsigned appid, unsigned rem, unsigned fslot, bool accept) "a#%u rem packet %u, free slot %u, accept? %u" ot_edn_xinfo(unsigned appid, const char *func, int line, const char *msg, uint32_t value) "a#%u %s:%d %s 0x%08x" +# ot_eg_pad_ring.c +ot_eg_pad_ring_reset(const char *id, const char *stage) "%s: %s" +ot_eg_pad_ring_reset_value(const char *id, const char *pad, char lvl) "%s: pad %s {%c}" + # ot_entropy_src.c ot_entropy_src_available(const char *id, const char *state, int st) "%s: entropy source is ready in [%s:%u]" diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index 4eb7e6b76b07c..cfcdfb763277e 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -73,6 +73,7 @@ config OT_EARLGREY select IBEX select IBEX_CLOCK_SRC select IBEX_COMMON + select IBEX_GPIO select OT_AES select OT_ALERT select OT_AON_TIMER diff --git a/include/hw/opentitan/ot_eg_pad_ring.h b/include/hw/opentitan/ot_eg_pad_ring.h new file mode 100644 index 0000000000000..73ba85d321fd0 --- /dev/null +++ b/include/hw/opentitan/ot_eg_pad_ring.h @@ -0,0 +1,74 @@ +/* + * QEMU OpenTitan Earlgrey Pad Ring Device + * + * Copyright (c) 2025 lowRISC contributors. + * + * Author(s): + * Emmanuel Blot + * Alex Jones + * + * 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. + */ + +#ifndef HW_OPENTITAN_OT_EG_PAD_RING_H +#define HW_OPENTITAN_OT_EG_PAD_RING_H + +#include "qom/object.h" + +#define TYPE_OT_EG_PAD_RING "ot-eg-pad-ring" +OBJECT_DECLARE_TYPE(OtEgPadRingState, OtEgPadRingClass, OT_EG_PAD_RING) + +/* Pad output signals */ +#define OT_EG_PAD_RING_PAD_EGRESS TYPE_OT_EG_PAD_RING "-egress" + +/* Separate output signal to map the PoR GPIO to the correct reset request */ +#define OT_EG_PAD_RING_POR_REQ TYPE_OT_EG_PAD_RING "-por-req" + +/* Exposed I/O on external pads */ +typedef enum { + /* + * USB (N & P), SPI Host & SPI Device (Csb, Sck, Sd0-3) currently go direct + * through their respective CharDevs. Their DIOs are thus excluded unless + * it is determined they are needed/useful for emulating their devices. + * + * @todo: IOR8/IOR9 are used by the System Reset Controller (not currently + * emulated) as outputs for the `ec_rst_l` & `flash_wp_l` signals. These + * should be added here if it is determined they should be emulated. + * + * Flash testing and voltage signal pads (flash_test_mode{0,1} and + * flash_test_volt) are not needed for emulation. The same is true of the + * OTP external voltage pad. + * + * @todo: There are also 47 MIO pads that should be added here: + * - IOR0-7 and IOR10-13, + * - IOC0-12, + * - IOB0-12, and + * - IOA0-8. + * These pads should be connected to Earlgrey's Pinmux, and then the GPIO + * CharDev logic should be updated/moved so that the host talks to GPIO + * via the Pad Ring (going through Pinmux) instead of bypassing Pinmux to + * talk to GPIO directly. When this is done, because the pad states will + * not be reset, the work-around in Earlgrey's GPIO to retain values + * across reset (see commit c232d98) should be removed also. + */ + OT_EG_PAD_RING_PAD_POR_N, /* Power-on-Reset (negated) */ + OT_EG_PAD_RING_PAD_COUNT, +} OtEgPadRingPad; + +#endif /* HW_OPENTITAN_OT_EG_PAD_RING_H */ From 590601342e33a45544b913ebdbae0bd3a98f76a5 Mon Sep 17 00:00:00 2001 From: Alex Jones Date: Thu, 20 Nov 2025 16:39:09 +0000 Subject: [PATCH 2/3] [ot] hw/riscv: ot_earlgrey: Add Pad Ring Connect the dedicated PoR signal to the rstmgr here. Signed-off-by: Alex Jones --- hw/riscv/Kconfig | 1 + hw/riscv/ot_earlgrey.c | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index cfcdfb763277e..2695d53dc9760 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -81,6 +81,7 @@ config OT_EARLGREY select OT_CLKMGR select OT_CSRNG select OT_EDN + select OT_EG_PAD_RING select OT_ENTROPY_SRC select OT_FLASH select OT_GPIO_EG diff --git a/hw/riscv/ot_earlgrey.c b/hw/riscv/ot_earlgrey.c index 6418d22fe3685..75c5a6415d5ff 100644 --- a/hw/riscv/ot_earlgrey.c +++ b/hw/riscv/ot_earlgrey.c @@ -46,6 +46,7 @@ #include "hw/opentitan/ot_common.h" #include "hw/opentitan/ot_csrng.h" #include "hw/opentitan/ot_edn.h" +#include "hw/opentitan/ot_eg_pad_ring.h" #include "hw/opentitan/ot_entropy_src.h" #include "hw/opentitan/ot_flash.h" #include "hw/opentitan/ot_gpio_eg.h" @@ -138,6 +139,7 @@ enum OtEGSocDevice { OT_EG_SOC_DEV_OTBN, OT_EG_SOC_DEV_OTP_CTRL, OT_EG_SOC_DEV_OTP_BACKEND, + OT_EG_SOC_DEV_PAD_RING, OT_EG_SOC_DEV_PATTGEN, OT_EG_SOC_DEV_PINMUX, OT_EG_SOC_DEV_PLIC, @@ -668,6 +670,13 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { OT_EG_SOC_DEVLINK("clock-src", CLKMGR) ) }, + [OT_EG_SOC_DEV_PAD_RING] = { + .type = TYPE_OT_EG_PAD_RING, + .gpio = IBEXGPIOCONNDEFS( + OT_EG_SOC_SIGNAL(OT_EG_PAD_RING_POR_REQ, 0, RSTMGR, + OT_RSTMGR_RST_REQ, 0) + ), + }, [OT_EG_SOC_DEV_PATTGEN] = { .type = TYPE_OT_UNIMP, .cfg = &ibex_unimp_configure, From 34b35dcbf4a78807ccbaaf514a494238d47cbf52 Mon Sep 17 00:00:00 2001 From: Alex Jones Date: Thu, 20 Nov 2025 17:00:39 +0000 Subject: [PATCH 3/3] [ot] docs/opentitan: ot_eg_pad_ring.md: Add docs Add initial pad ring documentation (only for the PoR signal, and how to use it, for now). Signed-off-by: Alex Jones --- docs/opentitan/ot_eg_pad_ring.md | 33 ++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/opentitan/ot_eg_pad_ring.md diff --git a/docs/opentitan/ot_eg_pad_ring.md b/docs/opentitan/ot_eg_pad_ring.md new file mode 100644 index 0000000000000..8e7301a8a58dd --- /dev/null +++ b/docs/opentitan/ot_eg_pad_ring.md @@ -0,0 +1,33 @@ +# OpenTitan Earl Grey Pad Ring + +## Power-on Reset (POR) + +The Earl Grey Pad Ring currently only supports the negated Power-on Reset (PoR) pad, which can +be signalled by setting the `por_n` property and resetting the VM to perform a Power-on Reset. +This can for example be done with the QEMU Monitor via the following command sequence: +``` +> qom-set ot-eg-pad-ring.0 por_n low +> system_reset +> qom-set ot-eg-pad-ring.0 por_n high +``` + +Equivalent QMP JSON commands can also be used. + +Note that the current implementation directly invokes a reset request on any reset where a falling +edge is detected (i.e. the reset strapping is asserted), and it is not well supported to "hold" the +device in reset. If it is desired to emulate this time, you should stop and resume the VM for +for the duration of the reset, e.g.: +``` +/* Asserting the POR signal */ +> stop +> qom-set ot-eg-pad-ring.0 por_n low +> system_reset +/* ... wait for the duration of the reset ... */ +/* De-asserting the POR signal */ +> qom-set ot-eg-pad-ring.0 por_n high +> cont +``` + +## MIO Pads + +Currently, Earl Grey's MIO pads are not connected in the Pad Ring / Pinmux.