Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions docs/opentitan/ot_eg_pad_ring.md
Original file line number Diff line number Diff line change
@@ -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.
3 changes: 3 additions & 0 deletions hw/opentitan/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions hw/opentitan/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -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'))
Expand Down
206 changes: 206 additions & 0 deletions hw/opentitan/ot_eg_pad_ring.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
/*
* QEMU OpenTitan Earlgrey Pad Ring device
*
* Copyright (c) 2025 lowRISC contributors.
*
* Author(s):
* Emmanuel Blot <eblot@rivosinc.com>
* Alex Jones <alex.jones@lowrisc.org>
*
* 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);
4 changes: 4 additions & 0 deletions hw/opentitan/trace-events
Original file line number Diff line number Diff line change
Expand Up @@ -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]"
Expand Down
2 changes: 2 additions & 0 deletions hw/riscv/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,15 @@ 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
select OT_AST_EG
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
Expand Down
9 changes: 9 additions & 0 deletions hw/riscv/ot_earlgrey.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
74 changes: 74 additions & 0 deletions include/hw/opentitan/ot_eg_pad_ring.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* QEMU OpenTitan Earlgrey Pad Ring Device
*
* Copyright (c) 2025 lowRISC contributors.
*
* Author(s):
* Emmanuel Blot <eblot@rivosinc.com>
* Alex Jones <alex.jones@lowrisc.org>
*
* 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 */
Loading