diff mbox series

[PATCH-for-5.2,4/4] hw/char/serial: Use the Clock API to feed the UART reference clock

Message ID 20200806130340.17316-5-f4bug@amsat.org
State New
Headers show
Series hw/char/serial: Use the Clock API to feed the UART reference clock | expand

Commit Message

Philippe Mathieu-Daudé Aug. 6, 2020, 1:03 p.m. UTC
In the same chipset, UARTs can be clocked at different rate, or the
input clock can be changed at runtime. The Clock API allow us to
propagate such clock rate change to the device.
Let the SerialState have its reference input clock (called 'rclk')
and if not clock is connected to the device, use the currently provided
frequency, to not modify the current code behavior.

Signed-off-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
---
 include/hw/char/serial.h |  3 +++
 hw/char/serial.c         | 35 ++++++++++++++++++++++++++++-------
 2 files changed, 31 insertions(+), 7 deletions(-)
diff mbox series

Patch

diff --git a/include/hw/char/serial.h b/include/hw/char/serial.h
index 75c71adfd2..1c7a4df2ab 100644
--- a/include/hw/char/serial.h
+++ b/include/hw/char/serial.h
@@ -31,8 +31,10 @@ 
 #include "qemu/fifo8.h"
 #include "chardev/char.h"
 #include "hw/sysbus.h"
+#include "hw/clock.h"
 
 #define UART_FIFO_LENGTH    16      /* 16550A Fifo Length */
+#define UART_CLOCK_DIVISOR  16      /* baudrate is input clock / 16 */
 
 typedef struct SerialState {
     DeviceState parent;
@@ -57,6 +59,7 @@  typedef struct SerialState {
     qemu_irq irq;
     CharBackend chr;
     int last_break_enable;
+    Clock *rclk; /* ReceiverClock */
     uint32_t baudbase;
     uint32_t tsr_retry;
     guint watch_tag;
diff --git a/hw/char/serial.c b/hw/char/serial.c
index 2ddc73f255..701c670fd5 100644
--- a/hw/char/serial.c
+++ b/hw/char/serial.c
@@ -35,6 +35,7 @@ 
 #include "qemu/error-report.h"
 #include "trace.h"
 #include "hw/qdev-properties.h"
+#include "hw/qdev-clock.h"
 
 #define UART_LCR_DLAB	0x80	/* Divisor latch access bit */
 
@@ -921,10 +922,36 @@  static int serial_be_change(void *opaque)
     return 0;
 }
 
+/* Change the main reference oscillator frequency. */
+void serial_set_frequency(SerialState *s, uint32_t frequency)
+{
+    s->baudbase = frequency;
+    serial_update_parameters(s);
+}
+
+static void serial_rclk_update(void *opaque)
+{
+    SerialState *s = opaque;
+
+    serial_set_frequency(s, clock_get_hz(s->rclk) / UART_CLOCK_DIVISOR);
+}
+
+static void serial_init(Object *obj)
+{
+    SerialState *s = SERIAL(obj);
+
+    s->rclk = qdev_init_clock_in(DEVICE(obj), "rclk", serial_rclk_update, s);
+}
+
 static void serial_realize(DeviceState *dev, Error **errp)
 {
     SerialState *s = SERIAL(dev);
 
+    /* initialize the frequency in case the clock remains unconnected */
+    if (!clock_get(s->rclk)) {
+        clock_set_hz(s->rclk, s->baudbase);
+    }
+
     s->modem_status_poll = timer_new_ns(QEMU_CLOCK_VIRTUAL, (QEMUTimerCB *) serial_update_msl, s);
 
     s->fifo_timeout_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, (QEMUTimerCB *) fifo_timeout_int, s);
@@ -955,13 +982,6 @@  static void serial_unrealize(DeviceState *dev)
     qemu_unregister_reset(serial_reset, s);
 }
 
-/* Change the main reference oscillator frequency. */
-void serial_set_frequency(SerialState *s, uint32_t frequency)
-{
-    s->baudbase = frequency;
-    serial_update_parameters(s);
-}
-
 const MemoryRegionOps serial_io_ops = {
     .read = serial_ioport_read,
     .write = serial_ioport_write,
@@ -994,6 +1014,7 @@  static const TypeInfo serial_info = {
     .name = TYPE_SERIAL,
     .parent = TYPE_DEVICE,
     .instance_size = sizeof(SerialState),
+    .instance_init = serial_init,
     .class_init = serial_class_init,
 };