summaryrefslogtreecommitdiffstats
path: root/hw/char/cadence_uart.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/char/cadence_uart.c')
-rw-r--r--hw/char/cadence_uart.c73
1 files changed, 63 insertions, 10 deletions
diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c
index 22e47972f1..e196906c92 100644
--- a/hw/char/cadence_uart.c
+++ b/hw/char/cadence_uart.c
@@ -31,6 +31,8 @@
#include "qemu/module.h"
#include "hw/char/cadence_uart.h"
#include "hw/irq.h"
+#include "hw/qdev-clock.h"
+#include "trace.h"
#ifdef CADENCE_UART_ERR_DEBUG
#define DB_PRINT(...) do { \
@@ -97,7 +99,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)
@@ -171,12 +173,15 @@ static void uart_send_breaks(CadenceUARTState *s)
static void uart_parameters_setup(CadenceUARTState *s)
{
QEMUSerialSetParams ssp;
- unsigned int baud_rate, packet_size;
+ unsigned int baud_rate, packet_size, input_clk;
+ input_clk = clock_get_hz(s->refclk);
- baud_rate = (s->r[R_MR] & UART_MR_CLKS) ?
- UART_INPUT_CLK / 8 : UART_INPUT_CLK;
+ baud_rate = (s->r[R_MR] & UART_MR_CLKS) ? input_clk / 8 : input_clk;
+ baud_rate /= (s->r[R_BRGR] * (s->r[R_BDIV] + 1));
+ trace_cadence_uart_baudrate(baud_rate);
+
+ ssp.speed = baud_rate;
- ssp.speed = baud_rate / (s->r[R_BRGR] * (s->r[R_BDIV] + 1));
packet_size = 1;
switch (s->r[R_MR] & UART_MR_PAR) {
@@ -215,6 +220,13 @@ static void uart_parameters_setup(CadenceUARTState *s)
}
packet_size += ssp.data_bits + ssp.stop_bits;
+ if (ssp.speed == 0) {
+ /*
+ * Avoid division-by-zero below.
+ * TODO: find something better
+ */
+ ssp.speed = 1;
+ }
s->char_tx_time = (NANOSECONDS_PER_SECOND / ssp.speed) * packet_size;
qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
}
@@ -340,6 +352,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 in reset */
+ if (!clock_is_enabled(s->refclk) || device_is_in_reset(DEVICE(s))) {
+ return;
+ }
+
if (ch_mode == NORMAL_MODE || ch_mode == ECHO_MODE) {
uart_write_rx_fifo(opaque, buf, size);
}
@@ -353,6 +370,11 @@ static void uart_event(void *opaque, QEMUChrEvent event)
CadenceUARTState *s = opaque;
uint8_t buf = '\0';
+ /* ignore characters when unclocked or in reset */
+ if (!clock_is_enabled(s->refclk) || device_is_in_reset(DEVICE(s))) {
+ return;
+ }
+
if (event == CHR_EVENT_BREAK) {
uart_write_rx_fifo(opaque, &buf, 1);
}
@@ -462,9 +484,9 @@ static const MemoryRegionOps uart_ops = {
.endianness = DEVICE_NATIVE_ENDIAN,
};
-static void cadence_uart_reset(DeviceState *dev)
+static void cadence_uart_reset_init(Object *obj, ResetType type)
{
- CadenceUARTState *s = CADENCE_UART(dev);
+ CadenceUARTState *s = CADENCE_UART(obj);
s->r[R_CR] = 0x00000128;
s->r[R_IMR] = 0;
@@ -473,6 +495,11 @@ static void cadence_uart_reset(DeviceState *dev)
s->r[R_BRGR] = 0x0000028B;
s->r[R_BDIV] = 0x0000000F;
s->r[R_TTRIG] = 0x00000020;
+}
+
+static void cadence_uart_reset_hold(Object *obj)
+{
+ CadenceUARTState *s = CADENCE_UART(obj);
uart_rx_reset(s);
uart_tx_reset(s);
@@ -491,6 +518,14 @@ static void cadence_uart_realize(DeviceState *dev, Error **errp)
uart_event, NULL, s, NULL, true);
}
+static void cadence_uart_refclk_update(void *opaque)
+{
+ CadenceUARTState *s = opaque;
+
+ /* recompute uart's speed on clock change */
+ uart_parameters_setup(s);
+}
+
static void cadence_uart_init(Object *obj)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
@@ -500,9 +535,23 @@ static void cadence_uart_init(Object *obj)
sysbus_init_mmio(sbd, &s->iomem);
sysbus_init_irq(sbd, &s->irq);
+ s->refclk = qdev_init_clock_in(DEVICE(obj), "refclk",
+ cadence_uart_refclk_update, s);
+ /* initialize the frequency in case the clock remains unconnected */
+ clock_set_hz(s->refclk, UART_DEFAULT_REF_CLK);
+
s->char_tx_time = (NANOSECONDS_PER_SECOND / 9600) * 10;
}
+static int cadence_uart_pre_load(void *opaque)
+{
+ CadenceUARTState *s = opaque;
+
+ /* the frequency will be overriden if the refclk field is present */
+ clock_set_hz(s->refclk, UART_DEFAULT_REF_CLK);
+ return 0;
+}
+
static int cadence_uart_post_load(void *opaque, int version_id)
{
CadenceUARTState *s = opaque;
@@ -521,8 +570,9 @@ static int cadence_uart_post_load(void *opaque, int version_id)
static const VMStateDescription vmstate_cadence_uart = {
.name = "cadence_uart",
- .version_id = 2,
+ .version_id = 3,
.minimum_version_id = 2,
+ .pre_load = cadence_uart_pre_load,
.post_load = cadence_uart_post_load,
.fields = (VMStateField[]) {
VMSTATE_UINT32_ARRAY(r, CadenceUARTState, CADENCE_UART_R_MAX),
@@ -534,8 +584,9 @@ static const VMStateDescription vmstate_cadence_uart = {
VMSTATE_UINT32(tx_count, CadenceUARTState),
VMSTATE_UINT32(rx_wpos, CadenceUARTState),
VMSTATE_TIMER_PTR(fifo_trigger_handle, CadenceUARTState),
+ VMSTATE_CLOCK_V(refclk, CadenceUARTState, 3),
VMSTATE_END_OF_LIST()
- }
+ },
};
static Property cadence_uart_properties[] = {
@@ -546,10 +597,12 @@ static Property cadence_uart_properties[] = {
static void cadence_uart_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
dc->realize = cadence_uart_realize;
dc->vmsd = &vmstate_cadence_uart;
- dc->reset = cadence_uart_reset;
+ rc->phases.enter = cadence_uart_reset_init;
+ rc->phases.hold = cadence_uart_reset_hold;
device_class_set_props(dc, cadence_uart_properties);
}