|
@@ -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);
|
|
|
}
|
|
|
|