Skip to content

Commit 30aa1d4

Browse files
committed
[ot] hw/opentitan: ot_uart: Add break interrupt & mocked oversampling
This commit hooks up the QEMU UART RX break event to the `ot_uart` so that the break receive triggers an interrupt. This completely ignores the configured `CTRL.RXBLVL` as we have no notion for "how long" break is being held for. Breaks can be sent to a given CharDev via the QEMU monitor. With the goal of mocking the UART oversampling `VAL` register enough that it can support differentiation between break conditions and not (essentially treating the UART RX like a GPIO strap), we implement logic for outputting either 0x0 or 0xFFFF depending on whether we are "in a break" or not. That is, the `VAL` register will report 16 samples equivalent to the last UART bit received (which is 0 when in a break, and 1 for a stop bit on any valid transmission). We make the assumption here that break is being "held", rather than just being a transient event. This logic is enabled through an `oversample-break` property to make it an opt-in behaviour. To better support flows that use break as a GPIO signal that can be continually asserted, add an additional property that treats incoming break events as a "toggle", such that one break event will cause the UART to enter break until another event tells it to exit break. This is again an opt-in property intended for testing edge-case software that uses UART Break in this way. Signed-off-by: Alex Jones <alex.jones@lowrisc.org>
1 parent 7b28086 commit 30aa1d4

File tree

1 file changed

+46
-9
lines changed

1 file changed

+46
-9
lines changed

hw/opentitan/ot_uart.c

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ struct OtUARTState {
158158
Fifo8 tx_fifo;
159159
Fifo8 rx_fifo;
160160
uint32_t tx_watermark_level;
161+
bool in_break;
161162
guint watch_tag;
162163
unsigned pclk; /* Current input clock */
163164
const char *clock_src_name; /* IRQ name once connected */
@@ -166,6 +167,8 @@ struct OtUARTState {
166167
char *clock_name;
167168
DeviceState *clock_src;
168169
CharBackend chr;
170+
bool oversample_break; /* Should mock break in the oversampled VAL reg? */
171+
bool toggle_break; /* Are incoming breaks temporary or toggled? */
169172
};
170173

171174
struct OtUARTClass {
@@ -256,6 +259,11 @@ static void ot_uart_receive(void *opaque, const uint8_t *buf, int size)
256259
uint32_t rx_watermark_level;
257260
size_t count = MIN(fifo8_num_free(&s->rx_fifo), (size_t)size);
258261

262+
if (size && !s->toggle_break) {
263+
/* no longer breaking, so emulate idle in oversampled VAL register */
264+
s->in_break = false;
265+
}
266+
259267
for (int index = 0; index < size; index++) {
260268
fifo8_push(&s->rx_fifo, buf[index]);
261269
}
@@ -272,6 +280,24 @@ static void ot_uart_receive(void *opaque, const uint8_t *buf, int size)
272280
ot_uart_update_irqs(s);
273281
}
274282

283+
static void ot_uart_event_handler(void *opaque, QEMUChrEvent event)
284+
{
285+
OtUARTState *s = opaque;
286+
287+
if (event == CHR_EVENT_BREAK) {
288+
if (!s->in_break) {
289+
/* ignore CTRL.RXBLVL as we have no notion of break "time" */
290+
s->regs[R_INTR_STATE] |= INTR_RX_BREAK_ERR_MASK;
291+
ot_uart_update_irqs(s);
292+
/* emulate break in the oversampled VAL register */
293+
s->in_break = true;
294+
} else if (s->toggle_break) {
295+
/* emulate toggling break off in the oversampled VAL register */
296+
s->in_break = false;
297+
}
298+
}
299+
}
300+
275301
static uint8_t ot_uart_read_rx_fifo(OtUARTState *s)
276302
{
277303
uint8_t val;
@@ -472,14 +498,19 @@ static uint64_t ot_uart_read(void *opaque, hwaddr addr, unsigned size)
472498
* given our emulated interface, but some software might poll the
473499
* value of this register to determine break conditions.
474500
*
475-
* As such, and given that we always support RXIDLE, default
476-
* to reporting 16 idle high samples (i.e. 0xFFFF) instead.
477-
* This is still not ideal, but at least defaults to showing
478-
* nothing (similar to SW waiting too long before polling) rather
479-
* than essentially showing a break condition.
501+
* As such, default to reporting 16 of the last sample received
502+
* instead. This defaults to 16 idle high samples (as a stop bit is
503+
* always the last received), except for when the `oversample-break`
504+
* property is set and a break condition is received over UART RX,
505+
* where we then show 16 low samples until the next valid UART
506+
* transmission is received (or break is toggled off with the
507+
* `toggle-break` property enabled). This will not be accurate, but
508+
* should be sufficient to support basic software flows that
509+
* essentially use UART break as a strapping mechanism.
480510
*/
481-
val32 = R_VAL_RX_MASK;
482-
qemu_log_mask(LOG_UNIMP, "%s: UART VAL always shows idle\n", __func__);
511+
val32 = (s->in_break && s->oversample_break) ? 0u : UINT16_MAX;
512+
qemu_log_mask(LOG_UNIMP, "%s: VAL only shows idle%s\n", __func__,
513+
(s->oversample_break ? "/break" : ""));
483514
break;
484515
case R_OVRD:
485516
case R_TIMEOUT_CTRL:
@@ -618,6 +649,8 @@ static Property ot_uart_properties[] = {
618649
DEFINE_PROP_STRING("clock-name", OtUARTState, clock_name),
619650
DEFINE_PROP_LINK("clock-src", OtUARTState, clock_src, TYPE_DEVICE,
620651
DeviceState *),
652+
DEFINE_PROP_BOOL("oversample_break", OtUARTState, oversample_break, false),
653+
DEFINE_PROP_BOOL("toggle-break", OtUARTState, toggle_break, false),
621654
DEFINE_PROP_END_OF_LIST(),
622655
};
623656

@@ -626,7 +659,8 @@ static int ot_uart_be_change(void *opaque)
626659
OtUARTState *s = opaque;
627660

628661
qemu_chr_fe_set_handlers(&s->chr, ot_uart_can_receive, ot_uart_receive,
629-
NULL, ot_uart_be_change, s, NULL, true);
662+
ot_uart_event_handler, ot_uart_be_change, s, NULL,
663+
true);
630664

631665
if (s->watch_tag > 0) {
632666
g_source_remove(s->watch_tag);
@@ -686,8 +720,11 @@ static void ot_uart_realize(DeviceState *dev, Error **errp)
686720
fifo8_create(&s->tx_fifo, OT_UART_TX_FIFO_SIZE);
687721
fifo8_create(&s->rx_fifo, OT_UART_RX_FIFO_SIZE);
688722

723+
s->in_break = false;
724+
689725
qemu_chr_fe_set_handlers(&s->chr, ot_uart_can_receive, ot_uart_receive,
690-
NULL, ot_uart_be_change, s, NULL, true);
726+
ot_uart_event_handler, ot_uart_be_change, s, NULL,
727+
true);
691728
}
692729

693730
static void ot_uart_init(Object *obj)

0 commit comments

Comments
 (0)