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. 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..2695d53dc9760 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 @@ -80,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, 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 */