@@ -21,6 +21,7 @@
#include "hw/sysbus.h"
#include "chardev/char-fe.h"
#include "qemu/timer.h"
+#include "hw/clock-port.h"
#define CADENCE_UART_RX_FIFO_SIZE 16
#define CADENCE_UART_TX_FIFO_SIZE 16
@@ -47,6 +48,7 @@ typedef struct {
CharBackend chr;
qemu_irq irq;
QEMUTimer *fifo_trigger_handle;
+ ClockState refclk;
} CadenceUARTState;
static inline DeviceState *cadence_uart_create(hwaddr addr,
@@ -28,6 +28,7 @@
#include "qemu/timer.h"
#include "qemu/log.h"
#include "hw/char/cadence_uart.h"
+#include "trace.h"
#ifdef CADENCE_UART_ERR_DEBUG
#define DB_PRINT(...) do { \
@@ -94,7 +95,7 @@
#define LOCAL_LOOPBACK (0x2 << UART_MR_CHMODE_SH)
#define REMOTE_LOOPBACK (0x3 << UART_MR_CHMODE_SH)
-#define UART_INPUT_CLK 50000000
+#define UART_DEFAULT_REF_CLK (50 * 1000 * 1000)
#define R_CR (0x00/4)
#define R_MR (0x04/4)
@@ -165,15 +166,30 @@ static void uart_send_breaks(CadenceUARTState *s)
&break_enabled);
}
+static unsigned int uart_input_clk(CadenceUARTState *s)
+{
+ return clock_state_get_frequency(&s->refclk);
+}
+
static void uart_parameters_setup(CadenceUARTState *s)
{
QEMUSerialSetParams ssp;
unsigned int baud_rate, packet_size;
baud_rate = (s->r[R_MR] & UART_MR_CLKS) ?
- UART_INPUT_CLK / 8 : UART_INPUT_CLK;
+ uart_input_clk(s) / 8 : uart_input_clk(s);
+ baud_rate /= (s->r[R_BRGR] * (s->r[R_BDIV] + 1));
+ trace_cadence_uart_baudrate(baud_rate);
+
+ ssp.speed = baud_rate;
+ if (ssp.speed == 0) {
+ /*
+ * Avoid division-by-zero below.
+ * TODO: find something better
+ */
+ ssp.speed = 1;
+ }
- ssp.speed = baud_rate / (s->r[R_BRGR] * (s->r[R_BDIV] + 1));
packet_size = 1;
switch (s->r[R_MR] & UART_MR_PAR) {
@@ -337,6 +353,11 @@ static void uart_receive(void *opaque, const uint8_t *buf, int size)
CadenceUARTState *s = opaque;
uint32_t ch_mode = s->r[R_MR] & UART_MR_CHMODE;
+ /* ignore characters when unclocked or reset */
+ if (!clock_state_is_domain_running(&s->refclk)) {
+ return;
+ }
+
if (ch_mode == NORMAL_MODE || ch_mode == ECHO_MODE) {
uart_write_rx_fifo(opaque, buf, size);
}
@@ -350,6 +371,11 @@ static void uart_event(void *opaque, int event)
CadenceUARTState *s = opaque;
uint8_t buf = '\0';
+ /* ignore events when unclocked or reset */
+ if (!clock_state_is_domain_running(&s->refclk)) {
+ return;
+ }
+
if (event == CHR_EVENT_BREAK) {
uart_write_rx_fifo(opaque, &buf, 1);
}
@@ -382,6 +408,14 @@ static void uart_write(void *opaque, hwaddr offset,
{
CadenceUARTState *s = opaque;
+ /* ignore accesses when ref clock is disabled */
+ if (!clock_state_is_domain_running(&s->refclk)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "cadence_uart: Trying to write register 0x%x"
+ " while clock is disabled/reset\n", (unsigned) offset);
+ return;
+ }
+
DB_PRINT(" offset:%x data:%08x\n", (unsigned)offset, (unsigned)value);
offset >>= 2;
if (offset >= CADENCE_UART_R_MAX) {
@@ -440,6 +474,14 @@ static uint64_t uart_read(void *opaque, hwaddr offset,
CadenceUARTState *s = opaque;
uint32_t c = 0;
+ /* ignore accesses when ref clock is disabled */
+ if (!clock_state_is_domain_running(&s->refclk)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "cadence_uart: Trying to read register 0x%x"
+ " while clock is disabled/reset\n", (unsigned) offset);
+ return 0;
+ }
+
offset >>= 2;
if (offset >= CADENCE_UART_R_MAX) {
c = 0;
@@ -488,6 +530,24 @@ static void cadence_uart_realize(DeviceState *dev, Error **errp)
uart_event, NULL, s, NULL, true);
}
+static void cadence_uart_refclk_update(void *opaque, ClockState *clk)
+{
+ CadenceUARTState *s = opaque;
+
+ bool need_reset = clock_state_get_domain_reset(clk) &&
+ !clock_state_get_domain_reset(&s->refclk);
+
+ clock_state_copy(&s->refclk, clk);
+
+ /* reset state if clock domain reset is asserted */
+ if (need_reset) {
+ cadence_uart_reset(DEVICE(s));
+ }
+
+ /* recompute uart's speed on clock change */
+ uart_parameters_setup(s);
+}
+
static void cadence_uart_init(Object *obj)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
@@ -497,6 +557,11 @@ static void cadence_uart_init(Object *obj)
sysbus_init_mmio(sbd, &s->iomem);
sysbus_init_irq(sbd, &s->irq);
+ sysbus_init_bus_interface_clock(sbd, "busclk");
+ qdev_init_clock_in(DEVICE(obj), "refclk", cadence_uart_refclk_update, s);
+ /* initialize the frequency in case the clock remains unconnected */
+ s->refclk.frequency = UART_DEFAULT_REF_CLK;
+
s->char_tx_time = (NANOSECONDS_PER_SECOND / 9600) * 10;
}
@@ -548,7 +613,7 @@ static void cadence_uart_class_init(ObjectClass *klass, void *data)
dc->vmsd = &vmstate_cadence_uart;
dc->reset = cadence_uart_reset;
dc->props = cadence_uart_properties;
- }
+}
static const TypeInfo cadence_uart_info = {
.name = TYPE_CADENCE_UART,
@@ -73,3 +73,6 @@ cmsdk_apb_uart_receive(uint8_t c) "CMSDK APB UART: got character 0x%x from backe
cmsdk_apb_uart_tx_pending(void) "CMSDK APB UART: character send to backend pending"
cmsdk_apb_uart_tx(uint8_t c) "CMSDK APB UART: character 0x%x sent to backend"
cmsdk_apb_uart_set_params(int speed) "CMSDK APB UART: params set to %d 8N1"
+
+# hw/char/cadence_uart.c
+cadence_uart_baudrate(unsigned baudrate) "baudrate %u"