On Tue, Feb 25, 2020 at 5:31 AM Damien Hedde <damien.he...@greensocs.com> wrote: > > Switch the cadence uart to multi-phase reset and add the > reference clock input. > > The input clock frequency is added to the migration structure. > > The reference clock controls the baudrate generation. If it disabled, > any input characters and events are ignored. > > If this clock remains unconnected, the uart behaves as before > (it default to a 50MHz ref clock). > > Signed-off-by: Damien Hedde <damien.he...@greensocs.com>
Reviewed-by: Alistair Francis <alistair.fran...@wdc.com> Alistair > > --- > > v7: > + update ClockIn/ClockOut types > + update due to resettable changes > + use a versioned field instead subsection in vmstate > --- > include/hw/char/cadence_uart.h | 1 + > hw/char/cadence_uart.c | 73 +++++++++++++++++++++++++++++----- > hw/char/trace-events | 3 ++ > 3 files changed, 67 insertions(+), 10 deletions(-) > > diff --git a/include/hw/char/cadence_uart.h b/include/hw/char/cadence_uart.h > index 47cec956c4..2a179a572f 100644 > --- a/include/hw/char/cadence_uart.h > +++ b/include/hw/char/cadence_uart.h > @@ -49,6 +49,7 @@ typedef struct { > CharBackend chr; > qemu_irq irq; > QEMUTimer *fifo_trigger_handle; > + Clock *refclk; > } CadenceUARTState; > > static inline DeviceState *cadence_uart_create(hwaddr addr, > 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); > } > > diff --git a/hw/char/trace-events b/hw/char/trace-events > index 6f938301d9..d20eafd56f 100644 > --- a/hw/char/trace-events > +++ b/hw/char/trace-events > @@ -97,3 +97,6 @@ exynos_uart_wo_read(uint32_t channel, const char *name, > uint32_t reg) "UART%d: T > exynos_uart_rxsize(uint32_t channel, uint32_t size) "UART%d: Rx FIFO size: > %d" > exynos_uart_channel_error(uint32_t channel) "Wrong UART channel number: %d" > exynos_uart_rx_timeout(uint32_t channel, uint32_t stat, uint32_t intsp) > "UART%d: Rx timeout stat=0x%x intsp=0x%x" > + > +# hw/char/cadence_uart.c > +cadence_uart_baudrate(unsigned baudrate) "baudrate %u" > -- > 2.25.1 > >