From 59a3455ad0937257a56dbe701204bfe3f39f58cd Mon Sep 17 00:00:00 2001 From: Douglas Reis Date: Tue, 2 Sep 2025 16:30:14 +0100 Subject: [PATCH] [ot] hw/opentitan: ot_usbdev: Create initial usbdev block Signed-off-by: Douglas Reis Signed-off-by: Amaury Pouly --- docs/opentitan/earlgrey.md | 6 + docs/opentitan/usbdev.md | 31 ++ hw/opentitan/Kconfig | 3 + hw/opentitan/meson.build | 1 + hw/opentitan/ot_usbdev.c | 836 +++++++++++++++++++++++++++++++ hw/opentitan/trace-events | 10 + hw/riscv/Kconfig | 1 + hw/riscv/ot_earlgrey.c | 55 +- include/hw/opentitan/ot_usbdev.h | 37 ++ 9 files changed, 971 insertions(+), 9 deletions(-) create mode 100644 docs/opentitan/usbdev.md create mode 100644 hw/opentitan/ot_usbdev.c create mode 100644 include/hw/opentitan/ot_usbdev.h diff --git a/docs/opentitan/earlgrey.md b/docs/opentitan/earlgrey.md index 1ba0b974738bd..727c7b130cdff 100644 --- a/docs/opentitan/earlgrey.md +++ b/docs/opentitan/earlgrey.md @@ -269,6 +269,12 @@ There are two modes to handle address remapping, with different limitations: * `-device ,bus=,address=
` can be used to attach devices at a specific address to one of the three I2C buses. The buses are named `ot-i2c0`, `ot-i2c1`, and `ot-i2c2`. +### USBDEV + +* `-chardev pty,id=usbdev` can be used to connect to the usbdev driver. + +See the [USBDEV documentation](usbdev.md)] for more details. + ## Useful debugging options ### Device log traces diff --git a/docs/opentitan/usbdev.md b/docs/opentitan/usbdev.md new file mode 100644 index 0000000000000..ebde79a68a5f2 --- /dev/null +++ b/docs/opentitan/usbdev.md @@ -0,0 +1,31 @@ +# OpenTitan USBDEV + +**Warning:** the USBDEV driver is still in development and not expected to work at the moment! + +## `usb` Chardev + +The USBDEV driver exposes a chardev with ID `usbdev` which can used to control some aspects of the +emulation. +Once connected, the driver accepts textual commands. +Each command must end with a newline. +The following commands are recognized: +- `vbus_on`: turn on VBUS, see [#VBUS-handling](VBUS handling) for more details. +- `vbus_off`: turn off VBUS, see [#VBUS-handling](VBUS handling) for more details. + +## VBUS handling + +On a real machine, the VBUS sense pin is usually connected to the VBUS connector so +that the chip can detect when a cable is plugged in. For the purpose of emulation, a different +approach needs to be taken. The driver supports two modes of operation which are controlled +by the `vbus-override` property which can be set on the command-line by +`-global ot-usbdev.vbus-override=`. The following modes are supported: + +- `vbus-override=on`: in this mode, the VBUS sense pin is entirely managed over the `usbdev` + chardev. + By default, the sense pin will be set to level low. The `vbus_on` and `vbus_off` commands + can be used to change the value of the sense pin. Note that in this mode, the VBUS sense signal + will be completely independent of the presence of a USB host. +- `vbus-override=off`: in this mode, the `vbus_on` and `vbus_off` commands control a virtual "VBUS + enable" gate. The VBUS sense pin will be reported as level high if and only if the VBUS enable + gate is enabled **and** a USB host is connected to the driver. + By default, the VBUS gate is disabled. diff --git a/hw/opentitan/Kconfig b/hw/opentitan/Kconfig index 1278791b7f8ed..09f245c659a10 100644 --- a/hw/opentitan/Kconfig +++ b/hw/opentitan/Kconfig @@ -191,6 +191,9 @@ config OT_TIMER config OT_UART bool +config OT_USBDEV + bool + config OT_UNIMP bool diff --git a/hw/opentitan/meson.build b/hw/opentitan/meson.build index 6b551af640ede..fff4cd4d3b33c 100644 --- a/hw/opentitan/meson.build +++ b/hw/opentitan/meson.build @@ -57,6 +57,7 @@ system_ss.add(when: 'CONFIG_OT_SRAM_CTRL', if_true: files('ot_sram_ctrl.c')) system_ss.add(when: 'CONFIG_OT_TIMER', if_true: files('ot_timer.c')) system_ss.add(when: 'CONFIG_OT_UART', if_true: files('ot_uart.c')) system_ss.add(when: 'CONFIG_OT_UNIMP', if_true: files('ot_unimp.c')) +system_ss.add(when: 'CONFIG_OT_USBDEV', if_true: files('ot_usbdev.c')) riscv_ss.add(when: 'CONFIG_OT_VMAPPER', if_true: files('ot_vmapper.c')) diff --git a/hw/opentitan/ot_usbdev.c b/hw/opentitan/ot_usbdev.c new file mode 100644 index 0000000000000..667a8e9dcfa13 --- /dev/null +++ b/hw/opentitan/ot_usbdev.c @@ -0,0 +1,836 @@ +/* + * QEMU OpenTitan USBDEV device + * + * Copyright (c) 2025 lowRISC contributors. + * + * Author(s): + * Amaury Pouly + * Douglas Reis + * + * + * 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 "qemu/module.h" +#include "qapi/error.h" +#include "chardev/char-fe.h" +#include "hw/opentitan/ot_alert.h" +#include "hw/opentitan/ot_common.h" +#include "hw/opentitan/ot_usbdev.h" +#include "hw/qdev-properties-system.h" +#include "hw/qdev-properties.h" +#include "hw/registerfields.h" +#include "hw/riscv/ibex_common.h" +#include "hw/riscv/ibex_irq.h" +#include "trace.h" + + +#define USBDEV_PARAM_N_ENDPOINTS 12u +#define USBDEV_PARAM_NUM_ALERTS 1u +#define USBDEV_PARAM_REG_WIDTH 32u + +/* NOLINTNEXTLINE(misc-redundant-expression) */ +static_assert(USBDEV_PARAM_N_ENDPOINTS == 12u, + "This driver only supports 12 endpoints"); + +/* clang-format off */ +REG32(USBDEV_INTR_STATE, 0x0u) + SHARED_FIELD(USBDEV_INTR_PKT_RECEIVED, 0u, 1u) + SHARED_FIELD(USBDEV_INTR_PKT_SENT, 1u, 1u) + SHARED_FIELD(USBDEV_INTR_DISCONNECTED, 2u, 1u) + SHARED_FIELD(USBDEV_INTR_HOST_LOST, 3u, 1u) + SHARED_FIELD(USBDEV_INTR_LINK_RESET, 4u, 1u) + SHARED_FIELD(USBDEV_INTR_LINK_SUSPEND, 5u, 1u) + SHARED_FIELD(USBDEV_INTR_LINK_RESUME, 6u, 1u) + SHARED_FIELD(USBDEV_INTR_AV_OUT_EMPTY, 7u, 1u) + SHARED_FIELD(USBDEV_INTR_RX_FULL, 8u, 1u) + SHARED_FIELD(USBDEV_INTR_AV_OVERFLOW, 9u, 1u) + SHARED_FIELD(USBDEV_INTR_LINK_IN_ERR, 10u, 1u) + SHARED_FIELD(USBDEV_INTR_RX_CRC_ERR, 11u, 1u) + SHARED_FIELD(USBDEV_INTR_RX_PID_ERR, 12u, 1u) + SHARED_FIELD(USBDEV_INTR_RX_BITSTUFF_ERR, 13u, 1u) + SHARED_FIELD(USBDEV_INTR_FRAME, 14u, 1u) + SHARED_FIELD(USBDEV_INTR_POWERED, 15u, 1u) + SHARED_FIELD(USBDEV_INTR_LINK_OUT_ERR, 16u, 1u) + SHARED_FIELD(USBDEV_INTR_AV_SETUP_EMPTY, 17u, 1u) +REG32(USBDEV_INTR_ENABLE, 0x4u) +REG32(USBDEV_INTR_TEST, 0x8u) +REG32(ALERT_TEST, 0xcu) + FIELD(ALERT_TEST, FATAL_FAULT, 0u, 1u) +REG32(USBCTRL, 0x10u) + FIELD(USBCTRL, ENABLE, 0u, 1u) + FIELD(USBCTRL, RESUME_LINK_ACTIVE, 1u, 1u) + FIELD(USBCTRL, DEVICE_ADDRESS, 16u, 7u) +/* + * The following registers use the same layout: + * the i-th bit corresponds to endpoint i. + */ +REG32(EP_OUT_ENABLE, 0x14u) +REG32(EP_IN_ENABLE, 0x18u) +REG32(USBSTAT, 0x1cu) + FIELD(USBSTAT, FRAME, 0u, 11u) + FIELD(USBSTAT, HOST_LOST, 11u, 1u) + FIELD(USBSTAT, LINK_STATE, 12u, 3u) + FIELD(USBSTAT, SENSE, 15u, 1u) + FIELD(USBSTAT, AV_OUT_DEPTH, 16u, 4u) + FIELD(USBSTAT, AV_SETUP_DEPTH, 20u, 3u) + FIELD(USBSTAT, AV_OUT_FULL, 23u, 1u) + FIELD(USBSTAT, RX_DEPTH, 24u, 4u) + FIELD(USBSTAT, AV_SETUP_FULL, 30u, 1u) + FIELD(USBSTAT, RX_EMPTY, 31u, 1u) +REG32(AVOUTBUFFER, 0x20u) + FIELD(AVOUTBUFFER, BUFFER, 0u, 5u) +REG32(AVSETUPBUFFER, 0x24u) + FIELD(AVSETUPBUFFER, BUFFER, 0u, 5u) +REG32(RXFIFO, 0x28u) + FIELD(RXFIFO, BUFFER, 0u, 5u) + FIELD(RXFIFO, SIZE, 8u, 7u) + FIELD(RXFIFO, SETUP, 19u, 1u) + FIELD(RXFIFO, EP, 20u, 4u) +/* + * The following registers use the same layout: + * the i-th bit corresponds to endpoint i. + */ +REG32(RXENABLE_SETUP, 0x2cu) +REG32(RXENABLE_OUT, 0x30u) +REG32(SET_NAK_OUT, 0x34u) +REG32(IN_SENT, 0x38u) +REG32(OUT_STALL, 0x3cu) +REG32(IN_STALL, 0x40u) +/* + * The following is a multi register with + * USBDEV_PARAM_N_ENDPOINTS instances. + */ +REG32(CONFIGIN_0, 0x44u) + SHARED_FIELD(CONFIGIN_BUFFER, 0u, 5u) + SHARED_FIELD(CONFIGIN_SIZE, 8u, 7u) + SHARED_FIELD(CONFIGIN_SENDING, 29u, 1u) + SHARED_FIELD(CONFIGIN_PEND, 30u, 1u) + SHARED_FIELD(CONFIGIN_RDY, 31u, 1u) +REG32(CONFIGIN_1, 0x48u) +REG32(CONFIGIN_2, 0x4cu) +REG32(CONFIGIN_3, 0x50u) +REG32(CONFIGIN_4, 0x54u) +REG32(CONFIGIN_5, 0x58u) +REG32(CONFIGIN_6, 0x5cu) +REG32(CONFIGIN_7, 0x60u) +REG32(CONFIGIN_8, 0x64u) +REG32(CONFIGIN_9, 0x68u) +REG32(CONFIGIN_10, 0x6cu) +REG32(CONFIGIN_11, 0x70u) +/* + * The following registers use the same layout: + * the i-th bit corresponds to endpoint i. + */ +REG32(OUT_ISO, 0x74u) +REG32(IN_ISO, 0x78u) +REG32(OUT_DATA_TOGGLE, 0x7cu) + FIELD(OUT_DATA_TOGGLE, STATUS, 0u, USBDEV_PARAM_N_ENDPOINTS) + FIELD(OUT_DATA_TOGGLE, MASK, 16u, USBDEV_PARAM_N_ENDPOINTS) +REG32(IN_DATA_TOGGLE, 0x80u) + FIELD(IN_DATA_TOGGLE, STATUS, 0u, USBDEV_PARAM_N_ENDPOINTS) + FIELD(IN_DATA_TOGGLE, MASK, 16u, USBDEV_PARAM_N_ENDPOINTS) +REG32(PHY_PINS_SENSE, 0x84u) + FIELD(PHY_PINS_SENSE, RX_DP_I, 0u, 1u) + FIELD(PHY_PINS_SENSE, RX_DN_I, 1u, 1u) + FIELD(PHY_PINS_SENSE, RX_D_I, 2u, 1u) + FIELD(PHY_PINS_SENSE, TX_DP_O, 8u, 1u) + FIELD(PHY_PINS_SENSE, TX_DN_O, 9u, 1u) + FIELD(PHY_PINS_SENSE, TX_D_O, 10u, 1u) + FIELD(PHY_PINS_SENSE, TX_SE0_O, 11u, 1u) + FIELD(PHY_PINS_SENSE, TX_OE_O, USBDEV_PARAM_N_ENDPOINTS, 1u) + FIELD(PHY_PINS_SENSE, PWR_SENSE, 16u, 1u) +REG32(PHY_PINS_DRIVE, 0x88u) + FIELD(PHY_PINS_DRIVE, DP_O, 0u, 1u) + FIELD(PHY_PINS_DRIVE, DN_O, 1u, 1u) + FIELD(PHY_PINS_DRIVE, D_O, 2u, 1u) + FIELD(PHY_PINS_DRIVE, SE0_O, 3u, 1u) + FIELD(PHY_PINS_DRIVE, OE_O, 4u, 1u) + FIELD(PHY_PINS_DRIVE, RX_ENABLE_O, 5u, 1u) + FIELD(PHY_PINS_DRIVE, DP_PULLUP_EN_O, 6u, 1u) + FIELD(PHY_PINS_DRIVE, DN_PULLUP_EN_O, 7u, 1u) + FIELD(PHY_PINS_DRIVE, EN, 16u, 1u) +REG32(PHY_CONFIG, 0x8cu) + FIELD(PHY_CONFIG, USE_DIFF_RCVR, 0u, 1u) + FIELD(PHY_CONFIG, TX_USE_D_SE0, 1u, 1u) + FIELD(PHY_CONFIG, EOP_SINGLE_BIT, 2u, 1u) + FIELD(PHY_CONFIG, PINFLIP, 5u, 1u) + FIELD(PHY_CONFIG, USB_REF_DISABLE, 6u, 1u) + FIELD(PHY_CONFIG, TX_OSC_TEST_MODE, 7u, 1u) +REG32(WAKE_CONTROL, 0x90u) + FIELD(WAKE_CONTROL, SUSPEND_REQ, 0u, 1u) + FIELD(WAKE_CONTROL, WAKE_ACK, 1u, 1u) +REG32(WAKE_EVENTS, 0x94u) + FIELD(WAKE_EVENTS, MODULE_ACTIVE, 0u, 1u) + FIELD(WAKE_EVENTS, DISCONNECTED, 8u, 1u) + FIELD(WAKE_EVENTS, BUS_RESET, 9u, 1u) + FIELD(WAKE_EVENTS, BUS_NOT_IDLE, 10u, 1u) +REG32(FIFO_CTRL, 0x98u) + FIELD(FIFO_CTRL, AVOUT_RST, 0u, 1u) + FIELD(FIFO_CTRL, AVSETUP_RST, 1u, 1u) + FIELD(FIFO_CTRL, RX_RST, 2u, 1u) +REG32(COUNT_OUT, 0x9cu) + FIELD(COUNT_OUT, COUNT, 0u, 8u) + FIELD(COUNT_OUT, DATATOG_OUT, USBDEV_PARAM_N_ENDPOINTS, 1u) + FIELD(COUNT_OUT, DROP_RX, 13u, 1u) + FIELD(COUNT_OUT, DROP_AVOUT, 14u, 1u) + FIELD(COUNT_OUT, IGN_AVSETUP, 15u, 1u) + FIELD(COUNT_OUT, ENDPOINTS, 16u, USBDEV_PARAM_N_ENDPOINTS) + FIELD(COUNT_OUT, RST, 31u, 1u) +REG32(COUNT_IN, 0xa0u) + FIELD(COUNT_IN, COUNT, 0u, 8u) + FIELD(COUNT_IN, NODATA, 13u, 1u) + FIELD(COUNT_IN, NAK, 14u, 1u) + FIELD(COUNT_IN, TIMEOUT, 15u, 1u) + FIELD(COUNT_IN, ENDPOINTS, 16u, USBDEV_PARAM_N_ENDPOINTS) + FIELD(COUNT_IN, RST, 31u, 1u) +REG32(COUNT_NODATA_IN, 0xa4u) + FIELD(COUNT_NODATA_IN, COUNT, 0u, 8u) + FIELD(COUNT_NODATA_IN, ENDPOINTS, 16u, USBDEV_PARAM_N_ENDPOINTS) + FIELD(COUNT_NODATA_IN, RST, 31u, 1u) +REG32(COUNT_ERRORS, 0xa8u) + FIELD(COUNT_ERRORS, COUNT, 0u, 8u) + FIELD(COUNT_ERRORS, PID_INVALID, 27u, 1u) + FIELD(COUNT_ERRORS, BITSTUFF, 28u, 1u) + FIELD(COUNT_ERRORS, CRC16, 29u, 1u) + FIELD(COUNT_ERRORS, CRC5, 30u, 1u) + FIELD(COUNT_ERRORS, RST, 31u, 1u) +/* clang-format on */ + +#define R32_OFF(_r_) ((_r_) / sizeof(uint32_t)) + +#define R_LAST_REG (R_COUNT_ERRORS) +#define REGS_COUNT (R_LAST_REG + 1u) +#define USBDEV_REGS_SIZE (REGS_COUNT * sizeof(uint32_t)) + +typedef enum { + USBDEV_INTR_PKT_RECEIVED, + USBDEV_INTR_PKT_SENT, + USBDEV_INTR_DISCONNECTED, + USBDEV_INTR_HOST_LOST, + USBDEV_INTR_LINK_RESET, + USBDEV_INTR_LINK_SUSPEND, + USBDEV_INTR_LINK_RESUME, + USBDEV_INTR_AV_OUT_EMPTY, + USBDEV_INTR_RX_FULL, + USBDEV_INTR_AV_OVERFLOW, + USBDEV_INTR_LINK_IN_ERR, + USBDEV_INTR_RX_CRC_ERR, + USBDEV_INTR_RX_PID_ERR, + USBDEV_INTR_RX_BITSTUFF_ERR, + USBDEV_INTR_FRAME, + USBDEV_INTR_POWERED, + USBDEV_INTR_LINK_OUT_ERR, + USBDEV_INTR_AV_SETUP_EMPTY, + USBDEV_INTR_NUM, +} OtUsbdevInterrupt; + +#define USBDEV_INTR_RW1C_MASK \ + (USBDEV_INTR_DISCONNECTED_MASK | USBDEV_INTR_HOST_LOST_MASK | \ + USBDEV_INTR_LINK_RESET_MASK | USBDEV_INTR_LINK_SUSPEND_MASK | \ + USBDEV_INTR_LINK_RESUME_MASK | USBDEV_INTR_AV_OVERFLOW_MASK | \ + USBDEV_INTR_LINK_IN_ERR_MASK | USBDEV_INTR_RX_CRC_ERR_MASK | \ + USBDEV_INTR_RX_PID_ERR_MASK | USBDEV_INTR_RX_BITSTUFF_ERR_MASK | \ + USBDEV_INTR_FRAME_MASK | USBDEV_INTR_POWERED_MASK | \ + USBDEV_INTR_LINK_OUT_ERR_MASK) + +#define USBDEV_INTR_MASK \ + (USBDEV_INTR_RW1C_MASK | USBDEV_INTR_PKT_RECEIVED_MASK | \ + USBDEV_INTR_PKT_SENT_MASK | USBDEV_INTR_AV_OUT_EMPTY_MASK | \ + USBDEV_INTR_RX_FULL_MASK | USBDEV_INTR_AV_SETUP_EMPTY_MASK) + +static_assert(USBDEV_INTR_MASK == (1u << USBDEV_INTR_NUM) - 1u, + "Interrupt mask mismatch"); + +#define USBDEV_DEVICE_SIZE 0x1000u +#define USBDEV_REGS_OFFSET 0u +#define USBDEV_BUFFER_OFFSET 0x800u +#define USBDEV_BUFFER_SIZE 0x800u + +/* + * Link state: this is the register field value, which we also use to + * track the state of the link in general. + */ +typedef enum { + /* Link disconnected (no VBUS or no pull-up connected). */ + OT_USBDEV_LINK_STATE_DISCONNECTED = 0, + /* Link powered and connected, but not reset yet. */ + OT_USBDEV_LINK_STATE_POWERED = 1, + /* Link suspended (constant idle/J for > 3 ms), but not reset yet. */ + OT_USBDEV_LINK_STATE_POWERED_SUSP = 2, + /* Link active. */ + OT_USBDEV_LINK_STATE_ACTIVE = 3, + /* + * Link suspended (constant idle for > 3 ms), was active before + * becoming suspended. + */ + OT_USBDEV_LINK_STATE_SUSPENDED = 4, + /* Link active but no SOF has been received since the last reset. */ + OT_USBDEV_LINK_STATE_ACTIVE_NOSOF = 5, + /* + * Link resuming to an active state, + * pending the end of resume signaling. + */ + OT_USBDEV_LINK_STATE_RESUMING = 6, +} OtUsbdevLinkState; + +struct OtUsbdevClass { + SysBusDeviceClass parent_class; + ResettablePhases parent_phases; +}; + +/* Size of the communication buffer for the chardev. */ +#define CMD_BUF_SIZE 32u + +/* NOLINTNEXTLINE(misc-redundant-expression) */ +static_assert(USBDEV_PARAM_NUM_ALERTS == 1u, + "This only supports a single alert (fault)"); + +struct OtUsbdevState { + SysBusDevice parent_obj; + struct { + MemoryRegion main; + MemoryRegion regs; + MemoryRegion buffer; + } mmio; + IbexIRQ irqs[USBDEV_INTR_NUM]; + IbexIRQ alert; + + /* Register content: note that certain read-only fields are not recorded. */ + uint32_t regs[REGS_COUNT]; + /* Link state: this is not recorded in reg. */ + OtUsbdevLinkState link_state; + /* VBUS sense: this is not recorded in reg. */ + bool vbus_sense; + + /* Buffer content */ + uint32_t buffer[USBDEV_BUFFER_SIZE / sizeof(uint32_t)]; + + /* Communication device and buffer. */ + CharBackend chr; + char cmd_buf[CMD_BUF_SIZE]; + unsigned cmd_buf_pos; + + char *ot_id; + /* Link to the clkmgr. */ + DeviceState *clock_src; + /* Name of the USB clock. */ + char *usbclk_name; + /* Name of the AON clock. */ + char *aonclk_name; + /* VBUS override mode. */ + bool vbus_override; +}; + +#define REG_NAME(_reg_) \ + ((((_reg_) <= REGS_COUNT) && REG_NAMES[_reg_]) ? REG_NAMES[_reg_] : "?") + +#define REG_NAME_ENTRY(_reg_) [R_##_reg_] = stringify(_reg_) +static const char *REG_NAMES[REGS_COUNT] = { + /* clang-format off */ + REG_NAME_ENTRY(USBDEV_INTR_STATE), + REG_NAME_ENTRY(USBDEV_INTR_ENABLE), + REG_NAME_ENTRY(USBDEV_INTR_TEST), + REG_NAME_ENTRY(ALERT_TEST), + REG_NAME_ENTRY(USBCTRL), + REG_NAME_ENTRY(EP_OUT_ENABLE), + REG_NAME_ENTRY(EP_IN_ENABLE), + REG_NAME_ENTRY(USBSTAT), + REG_NAME_ENTRY(AVOUTBUFFER), + REG_NAME_ENTRY(AVSETUPBUFFER), + REG_NAME_ENTRY(RXFIFO), + REG_NAME_ENTRY(RXENABLE_SETUP), + REG_NAME_ENTRY(RXENABLE_OUT), + REG_NAME_ENTRY(SET_NAK_OUT), + REG_NAME_ENTRY(IN_SENT), + REG_NAME_ENTRY(OUT_STALL), + REG_NAME_ENTRY(IN_STALL), + REG_NAME_ENTRY(CONFIGIN_0), + REG_NAME_ENTRY(CONFIGIN_1), + REG_NAME_ENTRY(CONFIGIN_2), + REG_NAME_ENTRY(CONFIGIN_3), + REG_NAME_ENTRY(CONFIGIN_4), + REG_NAME_ENTRY(CONFIGIN_5), + REG_NAME_ENTRY(CONFIGIN_6), + REG_NAME_ENTRY(CONFIGIN_7), + REG_NAME_ENTRY(CONFIGIN_8), + REG_NAME_ENTRY(CONFIGIN_9), + REG_NAME_ENTRY(CONFIGIN_10), + REG_NAME_ENTRY(CONFIGIN_11), + REG_NAME_ENTRY(OUT_ISO), + REG_NAME_ENTRY(IN_ISO), + REG_NAME_ENTRY(OUT_DATA_TOGGLE), + REG_NAME_ENTRY(IN_DATA_TOGGLE), + REG_NAME_ENTRY(PHY_PINS_SENSE), + REG_NAME_ENTRY(PHY_PINS_DRIVE), + REG_NAME_ENTRY(PHY_CONFIG), + REG_NAME_ENTRY(WAKE_CONTROL), + REG_NAME_ENTRY(WAKE_EVENTS), + REG_NAME_ENTRY(FIFO_CTRL), + REG_NAME_ENTRY(COUNT_OUT), + REG_NAME_ENTRY(COUNT_IN), + REG_NAME_ENTRY(COUNT_NODATA_IN), + REG_NAME_ENTRY(COUNT_ERRORS), + /* clang-format on */ +}; +#undef REG_NAME_ENTRY + +/* + * State handling + */ +static void ot_usbdev_set_vbus_sense(OtUsbdevState *s, bool sense) +{ + s->vbus_sense = sense; + /* @todo need to update the link state and trigger some events here */ +} + +static void ot_usbdev_update_irqs(OtUsbdevState *s) +{ + uint32_t state_masked = + s->regs[R_USBDEV_INTR_STATE] & s->regs[R_USBDEV_INTR_ENABLE]; + + trace_ot_usbdev_irqs(s->ot_id, s->regs[R_USBDEV_INTR_STATE], + s->regs[R_USBDEV_INTR_ENABLE], state_masked); + + for (unsigned irq_index = 0; irq_index < USBDEV_INTR_NUM; irq_index++) { + bool level = (state_masked & (1U << irq_index)) != 0; + ibex_irq_set(&s->irqs[irq_index], level); + } +} + +static void ot_usbdev_update_alerts(OtUsbdevState *s) +{ + uint32_t level = s->regs[R_ALERT_TEST]; + s->regs[R_ALERT_TEST] = 0u; + + /* @todo add other sources of alerts here */ + + if (level & R_ALERT_TEST_FATAL_FAULT_MASK) { + ibex_irq_set(&s->alert, 1); + ibex_irq_set(&s->alert, 0); + } +} + +/* + * Register read/write handling + */ + +static uint64_t ot_usbdev_read(void *opaque, hwaddr addr, unsigned size) +{ + OtUsbdevState *s = opaque; + (void)size; + uint32_t val32; + + hwaddr reg = R32_OFF(addr); + switch (reg) { + case R_USBDEV_INTR_STATE: + case R_USBDEV_INTR_ENABLE: + val32 = s->regs[reg]; + break; + case R_USBDEV_INTR_TEST: + case R_ALERT_TEST: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s Read to W/O register 0x%02" HWADDR_PRIx " (%s)\n", + __func__, s->ot_id, addr, REG_NAME(reg)); + val32 = 0; + break; + case R_USBSTAT: + /* + * For now, there is no host so we always always operate in + * vbus-override mode. + * @todo Report other statuses. + */ + val32 = FIELD_DP32(0u, USBSTAT, LINK_STATE, s->link_state); + val32 = FIELD_DP32(val32, USBSTAT, SENSE, (uint32_t)s->vbus_sense); + break; + case R_USBCTRL: + case R_EP_OUT_ENABLE: + case R_EP_IN_ENABLE: + case R_AVOUTBUFFER: + case R_AVSETUPBUFFER: + case R_RXFIFO: + case R_RXENABLE_SETUP: + case R_RXENABLE_OUT: + case R_SET_NAK_OUT: + case R_IN_SENT: + case R_OUT_STALL: + case R_IN_STALL: + case R_CONFIGIN_0: + case R_CONFIGIN_1: + case R_CONFIGIN_2: + case R_CONFIGIN_3: + case R_CONFIGIN_4: + case R_CONFIGIN_5: + case R_CONFIGIN_6: + case R_CONFIGIN_7: + case R_CONFIGIN_8: + case R_CONFIGIN_9: + case R_CONFIGIN_10: + case R_CONFIGIN_11: + case R_OUT_ISO: + case R_IN_ISO: + case R_OUT_DATA_TOGGLE: + case R_IN_DATA_TOGGLE: + case R_PHY_PINS_SENSE: + case R_PHY_PINS_DRIVE: + case R_PHY_CONFIG: + case R_WAKE_CONTROL: + case R_WAKE_EVENTS: + case R_FIFO_CTRL: + case R_COUNT_OUT: + case R_COUNT_IN: + case R_COUNT_NODATA_IN: + case R_COUNT_ERRORS: + qemu_log_mask(LOG_UNIMP, "%s: %s %s is not supported\n", __func__, + s->ot_id, REG_NAME(reg)); + val32 = 0; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s Bad offset 0x%" HWADDR_PRIx "\n", + __func__, s->ot_id, addr); + val32 = 0; + break; + } + + uint32_t pc = ibex_get_current_pc(); + trace_ot_usbdev_io_read_out(s->ot_id, (uint32_t)addr, REG_NAME(reg), val32, + pc); + + return (uint64_t)val32; +} + +static void ot_usbdev_write(void *opaque, hwaddr addr, uint64_t val64, + unsigned size) +{ + OtUsbdevState *s = opaque; + (void)size; + uint32_t val32 = (uint32_t)val64; + + hwaddr reg = R32_OFF(addr); + + uint32_t pc = ibex_get_current_pc(); + trace_ot_usbdev_io_write(s->ot_id, (uint32_t)addr, REG_NAME(reg), val32, + pc); + + switch (reg) { + case R_USBDEV_INTR_STATE: + if (val32 & ~USBDEV_INTR_RW1C_MASK) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: write to R/O register 0x%02" HWADDR_PRIx + " (%s) field 0x%08x\n", + __func__, s->ot_id, addr, REG_NAME(reg), + val32 & ~USBDEV_INTR_RW1C_MASK); + } + val32 &= USBDEV_INTR_RW1C_MASK; + s->regs[reg] &= ~val32; + ot_usbdev_update_irqs(s); + break; + case R_USBDEV_INTR_ENABLE: + val32 &= USBDEV_INTR_MASK; + s->regs[reg] = val32; + ot_usbdev_update_irqs(s); + break; + case R_USBDEV_INTR_TEST: + val32 &= USBDEV_INTR_MASK; + s->regs[R_USBDEV_INTR_STATE] |= val32; + ot_usbdev_update_irqs(s); + break; + case R_ALERT_TEST: + val32 &= R_ALERT_TEST_FATAL_FAULT_MASK; + /* Use the register to record alerts sets */ + s->regs[R_ALERT_TEST] = val32; + ot_usbdev_update_alerts(s); + break; + case R_USBSTAT: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: write to R/O register 0x%02" HWADDR_PRIx + " (%s)\n", + __func__, s->ot_id, addr, REG_NAME(reg)); + break; + case R_USBCTRL: + case R_EP_OUT_ENABLE: + case R_EP_IN_ENABLE: + case R_AVOUTBUFFER: + case R_AVSETUPBUFFER: + case R_RXFIFO: + case R_RXENABLE_SETUP: + case R_RXENABLE_OUT: + case R_SET_NAK_OUT: + case R_IN_SENT: + case R_OUT_STALL: + case R_IN_STALL: + case R_CONFIGIN_0: + case R_CONFIGIN_1: + case R_CONFIGIN_2: + case R_CONFIGIN_3: + case R_CONFIGIN_4: + case R_CONFIGIN_5: + case R_CONFIGIN_6: + case R_CONFIGIN_7: + case R_CONFIGIN_8: + case R_CONFIGIN_9: + case R_CONFIGIN_10: + case R_CONFIGIN_11: + case R_OUT_ISO: + case R_IN_ISO: + case R_OUT_DATA_TOGGLE: + case R_IN_DATA_TOGGLE: + case R_PHY_PINS_SENSE: + case R_PHY_PINS_DRIVE: + case R_PHY_CONFIG: + case R_WAKE_CONTROL: + case R_WAKE_EVENTS: + case R_FIFO_CTRL: + case R_COUNT_OUT: + case R_COUNT_IN: + case R_COUNT_NODATA_IN: + case R_COUNT_ERRORS: + qemu_log_mask(LOG_UNIMP, "%s: %s: %s is not supported\n", __func__, + s->ot_id, REG_NAME(reg)); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s Bad offset 0x%" HWADDR_PRIx "\n", + __func__, s->ot_id, addr); + break; + } +} + +static uint64_t ot_usbdev_buffer_read(void *opaque, hwaddr addr, unsigned size) +{ + OtUsbdevState *s = opaque; + (void)size; + + hwaddr word = R32_OFF(addr); + uint32_t val32 = s->buffer[word]; + + uint32_t pc = ibex_get_current_pc(); + trace_ot_usbdev_io_buffer_read_out(s->ot_id, (uint32_t)addr, val32, pc); + + return (uint64_t)val32; +} + +static void ot_usbdev_buffer_write(void *opaque, hwaddr addr, uint64_t val64, + unsigned size) +{ + OtUsbdevState *s = opaque; + (void)size; + uint32_t val32 = val64; + + hwaddr word = R32_OFF(addr); + s->buffer[word] = val32; + + uint32_t pc = ibex_get_current_pc(); + trace_ot_usbdev_io_buffer_write(s->ot_id, (uint32_t)addr, val32, pc); +} + +/* + * Communication device handling + */ + +static int ot_usbdev_chr_can_receive(void *opaque) +{ + OtUsbdevState *s = opaque; + + /* Return remaining size in the buffer. */ + return (int)sizeof(s->cmd_buf) - (int)s->cmd_buf_pos; +} + +/* + * Process a nul-terminated command. A size (which does not count NUL) is + * provided if necessary. The following commands are supported: + * - "vbus_on": set the VBUS sensing signal to 1. + * - "vbus_off": set the VBUS sensing signal to 0. + */ +static void ot_usbdev_chr_process_cmd(OtUsbdevState *s, char *cmd, + size_t cmd_size) +{ + trace_ot_usbdev_chr_process_cmd(s->ot_id, cmd); + + if (strncmp(cmd, "vbus_on", cmd_size) == 0) { + ot_usbdev_set_vbus_sense(s, true); + } else if (strncmp(cmd, "vbus_off", cmd_size) == 0) { + ot_usbdev_set_vbus_sense(s, false); + } else { + qemu_log_mask(LOG_UNIMP, "%s: %s: unsupported command %s\n", __func__, + s->ot_id, cmd); + } +} + +static void ot_usbdev_chr_receive(void *opaque, const uint8_t *buf, int size) +{ + OtUsbdevState *s = opaque; + + if (s->cmd_buf_pos + (unsigned)size > sizeof(s->cmd_buf)) { + error_report("%s: %s: Unexpected chardev receive", __func__, s->ot_id); + return; + } + /* Copy data at the end of the buffer. */ + memcpy(&s->cmd_buf[s->cmd_buf_pos], buf, (size_t)size); + s->cmd_buf_pos += (unsigned)size; + /* Process any line. */ + while (true) { + /* Search end of line, stop if none is found. */ + char *eol = memchr(s->cmd_buf, (int)'\n', s->cmd_buf_pos); + if (!eol) { + break; + } + *eol = 0; + /* + * Process command and then remove it from the buffer + * by shifting everything left. + */ + size_t cmd_size = eol - s->cmd_buf; + ot_usbdev_chr_process_cmd(s, s->cmd_buf, cmd_size); + memmove(s->cmd_buf, eol + 1u, s->cmd_buf_pos - cmd_size - 1u); + s->cmd_buf_pos -= cmd_size - 1u; + } +} + +/* + * QEMU Initialization + */ + +static const MemoryRegionOps ot_usbdev_ops = { + .read = &ot_usbdev_read, + .write = &ot_usbdev_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl.min_access_size = sizeof(uint32_t), + .impl.max_access_size = sizeof(uint32_t), +}; + +static const MemoryRegionOps ot_usbdev_buffer_ops = { + .read = &ot_usbdev_buffer_read, + .write = &ot_usbdev_buffer_write, + .endianness = DEVICE_NATIVE_ENDIAN, + /* @todo The RTL probably supports sub-word reads, implement this */ + .impl.min_access_size = sizeof(uint32_t), + .impl.max_access_size = sizeof(uint32_t), +}; + +static Property ot_usbdev_properties[] = { + DEFINE_PROP_STRING("ot_id", OtUsbdevState, ot_id), + DEFINE_PROP_STRING("clock-name", OtUsbdevState, usbclk_name), + DEFINE_PROP_STRING("clock-name-aon", OtUsbdevState, aonclk_name), + DEFINE_PROP_LINK("clock-src", OtUsbdevState, clock_src, TYPE_DEVICE, + DeviceState *), + /* + * Communication device used to control the USBDEV block. Every command + * must be terminated by a newline. See ot_usbdev_chr_process_cmd for + * the list of supported devices. + */ + DEFINE_PROP_CHR("chardev", OtUsbdevState, chr), + /* VBUS control mode. */ + DEFINE_PROP_BOOL("vbus-override", OtUsbdevState, vbus_override, false), + DEFINE_PROP_END_OF_LIST(), +}; + +static void ot_usbdev_realize(DeviceState *dev, Error **errp) +{ + OtUsbdevState *s = OT_USBDEV(dev); + (void)errp; + + /* @todo: clarify if we need to track events/backend changes. */ + qemu_chr_fe_set_handlers(&s->chr, ot_usbdev_chr_can_receive, + ot_usbdev_chr_receive, NULL, NULL, s, NULL, true); + + g_assert(s->ot_id); + g_assert(s->usbclk_name); + g_assert(s->aonclk_name); +} + +/* + * QEMU reset handling + */ + +static void ot_usbdev_reset_enter(Object *obj, ResetType type) +{ + OtUsbdevClass *c = OT_USBDEV_GET_CLASS(obj); + OtUsbdevState *s = OT_USBDEV(obj); + + trace_ot_usbdev_reset(s->ot_id, "enter"); + + if (c->parent_phases.enter) { + c->parent_phases.enter(obj, type); + } + + /* @todo cancel everything here. */ + + memset(s->regs, 0, sizeof(s->regs)); + s->link_state = OT_USBDEV_LINK_STATE_DISCONNECTED; + + ot_usbdev_update_irqs(s); +} + +static void ot_usbdev_init(Object *obj) +{ + OtUsbdevState *s = OT_USBDEV(obj); + + for (unsigned idx = 0; idx < ARRAY_SIZE(s->irqs); idx++) { + ibex_sysbus_init_irq(obj, &s->irqs[idx]); + } + ibex_qdev_init_irq(obj, &s->alert, OT_DEVICE_ALERT); + + memory_region_init(&s->mmio.main, obj, TYPE_OT_USBDEV ".mmio", + USBDEV_DEVICE_SIZE); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio.main); + memory_region_init_io(&s->mmio.regs, obj, &ot_usbdev_ops, s, + TYPE_OT_USBDEV ".reg", USBDEV_REGS_SIZE); + memory_region_add_subregion(&s->mmio.main, USBDEV_REGS_OFFSET, + &s->mmio.regs); + memory_region_init_io(&s->mmio.buffer, obj, &ot_usbdev_buffer_ops, s, + TYPE_OT_USBDEV ".buffer", USBDEV_BUFFER_SIZE); + memory_region_add_subregion(&s->mmio.main, USBDEV_BUFFER_OFFSET, + &s->mmio.buffer); + + s->vbus_sense = false; +} + +static void ot_usbdev_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + (void)data; + + dc->realize = &ot_usbdev_realize; + device_class_set_props(dc, ot_usbdev_properties); + /* @todo: check what does DEVICE_CATEGORY_USB entail? */ + set_bit(DEVICE_CATEGORY_USB, dc->categories); + + ResettableClass *rc = RESETTABLE_CLASS(klass); + OtUsbdevClass *uc = OT_USBDEV_CLASS(klass); + resettable_class_set_parent_phases(rc, &ot_usbdev_reset_enter, NULL, NULL, + &uc->parent_phases); +} + +static const TypeInfo ot_usbdev_info = { + .name = TYPE_OT_USBDEV, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(OtUsbdevState), + .instance_init = &ot_usbdev_init, + .class_size = sizeof(OtUsbdevClass), + .class_init = &ot_usbdev_class_init, +}; + +static void ot_usbdev_register_types(void) +{ + type_register_static(&ot_usbdev_info); +} + +type_init(ot_usbdev_register_types); diff --git a/hw/opentitan/trace-events b/hw/opentitan/trace-events index 3ecbbd1291cdf..d027dcc882479 100644 --- a/hw/opentitan/trace-events +++ b/hw/opentitan/trace-events @@ -630,6 +630,16 @@ ot_uart_io_read_out(const char *id, uint32_t addr, const char *regname, uint32_t ot_uart_io_write(const char *id, uint32_t addr, const char *regname, uint32_t val, uint32_t pc) "%s: addr=0x%02x (%s), val=0x%x, pc=0x%x" ot_uart_irqs(const char *id, uint32_t active, uint32_t mask, uint32_t eff) "%s: act:0x%08x msk:0x%08x eff:0x%08x" +# ot_usbdev.c + +ot_usbdev_io_read_out(const char *id, uint32_t addr, const char *regname, uint32_t val, uint32_t pc) "%s: addr=0x%02x (%s), val=0x%x, pc=0x%x" +ot_usbdev_io_write(const char *id, uint32_t addr, const char *regname, uint32_t val, uint32_t pc) "%s: addr=0x%02x (%s), val=0x%x, pc=0x%x" +ot_usbdev_io_buffer_read_out(const char *id, uint32_t addr, uint32_t val, uint32_t pc) "%s: addr=0x%04x, val=0x%x, pc=0x%x" +ot_usbdev_io_buffer_write(const char *id, uint32_t addr, uint32_t val, uint32_t pc) "%s: addr=0x%04x, val=0x%x, pc=0x%x" +ot_usbdev_irqs(const char *id, uint32_t active, uint32_t mask, uint32_t eff) "%s: act:0x%08x msk:0x%08x eff:0x%08x" +ot_usbdev_reset(const char *id, const char *stage) "%s: %s" +ot_usbdev_chr_process_cmd(const char *id, const char *cmd) "%s: %s" + # ot_unimp.c ot_unimp_irq(const char *id, unsigned ix, bool lvl) "%s: #%u = %u" diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index ff25e2b5ab4c0..af643ce015408 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -100,6 +100,7 @@ config OT_EARLGREY select OT_SRAM_CTRL select OT_TIMER select OT_UART + select OT_USBDEV select OT_UNIMP select PULP_RV_DM select RISCV_DM diff --git a/hw/riscv/ot_earlgrey.c b/hw/riscv/ot_earlgrey.c index f08837b162d5b..dc555af054a0d 100644 --- a/hw/riscv/ot_earlgrey.c +++ b/hw/riscv/ot_earlgrey.c @@ -70,6 +70,7 @@ #include "hw/opentitan/ot_timer.h" #include "hw/opentitan/ot_uart.h" #include "hw/opentitan/ot_unimp.h" +#include "hw/opentitan/ot_usbdev.h" #include "hw/opentitan/ot_vmapper.h" #include "hw/qdev-properties.h" #include "hw/riscv/dm.h" @@ -102,6 +103,8 @@ static void ot_eg_soc_spi_device_configure( DeviceState *dev, const IbexDeviceDef *def, DeviceState *parent); static void ot_eg_soc_uart_configure(DeviceState *dev, const IbexDeviceDef *def, DeviceState *parent); +static void ot_eg_soc_usbdev_configure( + DeviceState *dev, const IbexDeviceDef *def, DeviceState *parent); /* ------------------------------------------------------------------------ */ /* Constants */ @@ -855,21 +858,41 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { ), }, [OT_EG_SOC_DEV_USBDEV] = { - .type = TYPE_OT_UNIMP, - .cfg = &ibex_unimp_configure, + .type = TYPE_OT_USBDEV, + .cfg = &ot_eg_soc_usbdev_configure, .memmap = MEMMAPENTRIES( { .base = 0x40320000u } ), + .gpio = IBEXGPIOCONNDEFS( + OT_EG_SOC_GPIO_SYSBUS_IRQ(0, PLIC, 135), + OT_EG_SOC_GPIO_SYSBUS_IRQ(1, PLIC, 136), + OT_EG_SOC_GPIO_SYSBUS_IRQ(2, PLIC, 137), + OT_EG_SOC_GPIO_SYSBUS_IRQ(3, PLIC, 138), + OT_EG_SOC_GPIO_SYSBUS_IRQ(4, PLIC, 139), + OT_EG_SOC_GPIO_SYSBUS_IRQ(5, PLIC, 140), + OT_EG_SOC_GPIO_SYSBUS_IRQ(6, PLIC, 141), + OT_EG_SOC_GPIO_SYSBUS_IRQ(7, PLIC, 142), + OT_EG_SOC_GPIO_SYSBUS_IRQ(8, PLIC, 143), + OT_EG_SOC_GPIO_SYSBUS_IRQ(9, PLIC, 144), + OT_EG_SOC_GPIO_SYSBUS_IRQ(10, PLIC, 145), + OT_EG_SOC_GPIO_SYSBUS_IRQ(11, PLIC, 146), + OT_EG_SOC_GPIO_SYSBUS_IRQ(12, PLIC, 147), + OT_EG_SOC_GPIO_SYSBUS_IRQ(13, PLIC, 148), + OT_EG_SOC_GPIO_SYSBUS_IRQ(14, PLIC, 149), + OT_EG_SOC_GPIO_SYSBUS_IRQ(15, PLIC, 150), + OT_EG_SOC_GPIO_SYSBUS_IRQ(16, PLIC, 151), + OT_EG_SOC_GPIO_SYSBUS_IRQ(17, PLIC, 152), + OT_EG_SOC_GPIO_ALERT(0, 21) + /* The VBUS sense pin is handled by a chardev */ + ), + .link = IBEXDEVICELINKDEFS( + OT_EG_SOC_DEVLINK("clock-src", CLKMGR) + ), .prop = IBEXDEVICEPROPDEFS( IBEX_DEV_STRING_PROP(OT_COMMON_DEV_ID, "usbdev"), - IBEX_DEV_UINT_PROP("size", 0x1000u), - IBEX_DEV_UINT_PROP("irq-count", 18u), - IBEX_DEV_UINT_PROP("alert-count", 1u), - IBEX_DEV_BOOL_PROP("warn-once", true) + IBEX_DEV_STRING_PROP("clock-name", "usb"), + IBEX_DEV_STRING_PROP("clock-name-aon", "aon") ), - .gpio = IBEXGPIOCONNDEFS( - OT_EG_SOC_GPIO_ALERT(0, 21) - ) }, [OT_EG_SOC_DEV_PWRMGR] = { .type = TYPE_OT_PWRMGR, @@ -1608,6 +1631,20 @@ static void ot_eg_soc_uart_configure(DeviceState *dev, const IbexDeviceDef *def, qdev_prop_set_chr(dev, "chardev", serial_hd(IBEX_GET_INSTANCE_NUM(def))); } +static void ot_eg_soc_usbdev_configure( + DeviceState *dev, const IbexDeviceDef *def, DeviceState *parent) +{ + (void)parent; + (void)def; + + Chardev *chr; + + chr = ibex_get_chardev_by_id("usbdev"); + if (chr) { + qdev_prop_set_chr(dev, "chardev", chr); + } +} + /* ------------------------------------------------------------------------ */ /* SoC */ /* ------------------------------------------------------------------------ */ diff --git a/include/hw/opentitan/ot_usbdev.h b/include/hw/opentitan/ot_usbdev.h new file mode 100644 index 0000000000000..1b578e83747c8 --- /dev/null +++ b/include/hw/opentitan/ot_usbdev.h @@ -0,0 +1,37 @@ +/* + * QEMU OpenTitan USBDEV device + * + * Copyright (c) 2025 lowRISC contributors. + * + * Author(s): + * Amaury Pouly + * Douglas Reis + * + * + * 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_USBDEV_H +#define HW_OPENTITAN_OT_USBDEV_H + +#include "qom/object.h" + +#define TYPE_OT_USBDEV "ot-usbdev" +OBJECT_DECLARE_TYPE(OtUsbdevState, OtUsbdevClass, OT_USBDEV) + +#endif /* HW_OPENTITAN_OT_USBDEV_H */