[sw, dif_uart] Add API for UART RX timeout control
Add the functions dif_uart_enable_rx_timeout,
dif_uart_disable_rx_timeout and dif_uart_get_rx_timeout to allow the
UART RX timeout to be enabled, disabled and queried as required.
Updates #4277.
Signed-off-by: Michael Munday <mike.munday@lowrisc.org>
diff --git a/sw/device/lib/dif/dif_uart.c b/sw/device/lib/dif/dif_uart.c
index b34fb12..8868348 100644
--- a/sw/device/lib/dif/dif_uart.c
+++ b/sw/device/lib/dif/dif_uart.c
@@ -539,3 +539,51 @@
return kDifUartOk;
}
+
+dif_uart_result_t dif_uart_enable_rx_timeout(const dif_uart_t *uart,
+ uint32_t duration_ticks) {
+ if (uart == NULL || (duration_ticks & ~UART_TIMEOUT_CTRL_VAL_MASK) != 0) {
+ return kDifUartBadArg;
+ }
+
+ uint32_t reg = bitfield_bit32_write(0, UART_TIMEOUT_CTRL_EN_BIT, true);
+ reg =
+ bitfield_field32_write(reg, UART_TIMEOUT_CTRL_VAL_FIELD, duration_ticks);
+ mmio_region_write32(uart->params.base_addr, UART_TIMEOUT_CTRL_REG_OFFSET,
+ reg);
+
+ return kDifUartOk;
+}
+
+dif_uart_result_t dif_uart_disable_rx_timeout(const dif_uart_t *uart) {
+ if (uart == NULL) {
+ return kDifUartBadArg;
+ }
+
+ uint32_t reg = bitfield_bit32_write(0, UART_TIMEOUT_CTRL_EN_BIT, false);
+ reg = bitfield_field32_write(reg, UART_TIMEOUT_CTRL_VAL_FIELD, 0);
+ mmio_region_write32(uart->params.base_addr, UART_TIMEOUT_CTRL_REG_OFFSET,
+ reg);
+
+ return kDifUartOk;
+}
+
+dif_uart_result_t dif_uart_get_rx_timeout(const dif_uart_t *uart,
+ dif_uart_toggle_t *status,
+ uint32_t *duration_ticks) {
+ if (uart == NULL || status == NULL) {
+ return kDifUartBadArg;
+ }
+
+ uint32_t reg =
+ mmio_region_read32(uart->params.base_addr, UART_TIMEOUT_CTRL_REG_OFFSET);
+ *status = bitfield_bit32_read(reg, UART_TIMEOUT_CTRL_EN_BIT)
+ ? kDifUartToggleEnabled
+ : kDifUartToggleDisabled;
+
+ if (duration_ticks != NULL) {
+ *duration_ticks = bitfield_field32_read(reg, UART_TIMEOUT_CTRL_VAL_FIELD);
+ }
+
+ return kDifUartOk;
+}
diff --git a/sw/device/lib/dif/dif_uart.h b/sw/device/lib/dif/dif_uart.h
index bedf408..31f3472 100644
--- a/sw/device/lib/dif/dif_uart.h
+++ b/sw/device/lib/dif/dif_uart.h
@@ -532,6 +532,45 @@
dif_uart_loopback_t loopback,
dif_uart_toggle_t enable);
+/**
+ * Enables the RX timeout with the given duration.
+ *
+ * @param uart A UART handle.
+ * @param duration_ticks RX timeout value in UART bit times (using the baud rate
+ * clock as reference) in the range [0,0xffffff].
+ * @return The result of the operation.
+ */
+DIF_WARN_UNUSED_RESULT
+dif_uart_result_t dif_uart_enable_rx_timeout(const dif_uart_t *uart,
+ uint32_t duration_ticks);
+
+/**
+ * Disables the RX timeout.
+ *
+ * In addition to disabling the RX timeout the timeout duration is reset to 0
+ * ticks.
+ *
+ * @param uart A UART handle.
+ * @return The result of the operation.
+ */
+DIF_WARN_UNUSED_RESULT
+dif_uart_result_t dif_uart_disable_rx_timeout(const dif_uart_t *uart);
+
+/**
+ * Gets the current status of the RX timeout control.
+ *
+ * @param uart A UART handle.
+ * @param[out] status The status of the RX timeout control (enabled or
+ * disabled).
+ * @param[out] duration_ticks RX timeout value in UART bit times (using the baud
+ * rate clock as reference) in the range [0,0xffffff] (optional).
+ * @return The result of the operation.
+ */
+DIF_WARN_UNUSED_RESULT
+dif_uart_result_t dif_uart_get_rx_timeout(const dif_uart_t *uart,
+ dif_uart_toggle_t *status,
+ uint32_t *duration_ticks);
+
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
diff --git a/sw/device/tests/dif/dif_uart_unittest.cc b/sw/device/tests/dif/dif_uart_unittest.cc
index 12909c0..403b103 100644
--- a/sw/device/tests/dif/dif_uart_unittest.cc
+++ b/sw/device/tests/dif/dif_uart_unittest.cc
@@ -653,5 +653,76 @@
kDifUartOk);
}
+class RxTimeoutTest : public UartTest {};
+
+TEST_F(RxTimeoutTest, NullArgs) {
+ EXPECT_EQ(dif_uart_enable_rx_timeout(nullptr, 1), kDifUartBadArg);
+ EXPECT_EQ(dif_uart_disable_rx_timeout(nullptr), kDifUartBadArg);
+
+ uint32_t value; // optional
+ dif_uart_toggle_t status; // mandatory
+ EXPECT_EQ(dif_uart_get_rx_timeout(nullptr, &status, &value), kDifUartBadArg);
+ EXPECT_EQ(dif_uart_get_rx_timeout(&uart_, nullptr, &value), kDifUartBadArg);
+}
+
+TEST_F(RxTimeoutTest, OutOfRange) {
+ // RX timeout value must be in the range [0,0xffffff].
+ EXPECT_EQ(dif_uart_enable_rx_timeout(&uart_, 0x01000000), kDifUartBadArg);
+ EXPECT_EQ(dif_uart_enable_rx_timeout(&uart_, 0xffffffff), kDifUartBadArg);
+}
+
+TEST_F(RxTimeoutTest, Enable) {
+ // Enable RX timeout and set to 0x123.
+ const uint32_t duration = 0x123;
+ EXPECT_WRITE32(UART_TIMEOUT_CTRL_REG_OFFSET,
+ {{UART_TIMEOUT_CTRL_VAL_OFFSET, duration},
+ {UART_TIMEOUT_CTRL_EN_BIT, true}});
+ EXPECT_EQ(dif_uart_enable_rx_timeout(&uart_, duration), kDifUartOk);
+}
+
+TEST_F(RxTimeoutTest, Disable) {
+ // Disable RX timeout.
+ EXPECT_WRITE32(
+ UART_TIMEOUT_CTRL_REG_OFFSET,
+ {{UART_TIMEOUT_CTRL_VAL_OFFSET, 0}, {UART_TIMEOUT_CTRL_EN_BIT, false}});
+ EXPECT_EQ(dif_uart_disable_rx_timeout(&uart_), kDifUartOk);
+}
+
+TEST_F(RxTimeoutTest, GetStatusOnly) {
+ // Enable RX timeout and set to 0x800000.
+ const uint32_t duration = 0x800000;
+ EXPECT_WRITE32(UART_TIMEOUT_CTRL_REG_OFFSET,
+ {{UART_TIMEOUT_CTRL_VAL_OFFSET, duration},
+ {UART_TIMEOUT_CTRL_EN_BIT, true}});
+ EXPECT_EQ(dif_uart_enable_rx_timeout(&uart_, duration), kDifUartOk);
+
+ // Read out status only.
+ dif_uart_toggle_t status = kDifUartToggleDisabled;
+ EXPECT_READ32(UART_TIMEOUT_CTRL_REG_OFFSET,
+ {{UART_TIMEOUT_CTRL_VAL_OFFSET, duration},
+ {UART_TIMEOUT_CTRL_EN_BIT, true}});
+ EXPECT_EQ(dif_uart_get_rx_timeout(&uart_, &status, nullptr), kDifUartOk);
+ EXPECT_EQ(status, kDifUartToggleEnabled);
+}
+
+TEST_F(RxTimeoutTest, GetAll) {
+ // Enable RX timeout and set to 0xf0f0f0.
+ const uint32_t duration = 0xf0f0f0;
+ EXPECT_WRITE32(UART_TIMEOUT_CTRL_REG_OFFSET,
+ {{UART_TIMEOUT_CTRL_VAL_OFFSET, duration},
+ {UART_TIMEOUT_CTRL_EN_BIT, true}});
+ EXPECT_EQ(dif_uart_enable_rx_timeout(&uart_, duration), kDifUartOk);
+
+ // Read out duration and status.
+ uint32_t out = 0;
+ dif_uart_toggle_t status = kDifUartToggleDisabled;
+ EXPECT_READ32(UART_TIMEOUT_CTRL_REG_OFFSET,
+ {{UART_TIMEOUT_CTRL_VAL_OFFSET, duration},
+ {UART_TIMEOUT_CTRL_EN_BIT, true}});
+ EXPECT_EQ(dif_uart_get_rx_timeout(&uart_, &status, &out), kDifUartOk);
+ EXPECT_EQ(status, kDifUartToggleEnabled);
+ EXPECT_EQ(out, duration);
+}
+
} // namespace
} // namespace dif_uart_unittest