mirror of
https://github.com/qemu/qemu.git
synced 2024-11-25 11:53:39 +08:00
hw/char/cadence_uart: add clock support
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.hedde@greensocs.com> Reviewed-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-id: 20200406135251.157596-8-damien.hedde@greensocs.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
parent
38867cb7ec
commit
b636db306e
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user