hw/char/pl011: add a clock input

Add a clock input to the PL011 UART so we can compute the current baud
rate and trace it. This is intended for developers who wish to use QEMU
to e.g. debug their firmware or to figure out the baud rate configured
by an unknown/closed source binary.

Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
Signed-off-by: Luc Michel <luc@lmichel.fr>
Tested-by: Guenter Roeck <linux@roeck-us.net>
Tested-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Luc Michel 2020-10-10 15:57:58 +02:00 committed by Peter Maydell
parent 83ad469547
commit aac63e0e6e
3 changed files with 47 additions and 0 deletions

View file

@ -22,6 +22,7 @@
#include "hw/char/pl011.h"
#include "hw/irq.h"
#include "hw/sysbus.h"
#include "hw/qdev-clock.h"
#include "migration/vmstate.h"
#include "chardev/char-fe.h"
#include "qemu/log.h"
@ -169,6 +170,25 @@ static void pl011_set_read_trigger(PL011State *s)
s->read_trigger = 1;
}
static unsigned int pl011_get_baudrate(const PL011State *s)
{
uint64_t clk;
if (s->fbrd == 0) {
return 0;
}
clk = clock_get_hz(s->clk);
return (clk / ((s->ibrd << 6) + s->fbrd)) << 2;
}
static void pl011_trace_baudrate_change(const PL011State *s)
{
trace_pl011_baudrate_change(pl011_get_baudrate(s),
clock_get_hz(s->clk),
s->ibrd, s->fbrd);
}
static void pl011_write(void *opaque, hwaddr offset,
uint64_t value, unsigned size)
{
@ -198,9 +218,11 @@ static void pl011_write(void *opaque, hwaddr offset,
break;
case 9: /* UARTIBRD */
s->ibrd = value;
pl011_trace_baudrate_change(s);
break;
case 10: /* UARTFBRD */
s->fbrd = value;
pl011_trace_baudrate_change(s);
break;
case 11: /* UARTLCR_H */
/* Reset the FIFO state on FIFO enable or disable */
@ -286,12 +308,29 @@ static void pl011_event(void *opaque, QEMUChrEvent event)
pl011_put_fifo(opaque, 0x400);
}
static void pl011_clock_update(void *opaque)
{
PL011State *s = PL011(opaque);
pl011_trace_baudrate_change(s);
}
static const MemoryRegionOps pl011_ops = {
.read = pl011_read,
.write = pl011_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static const VMStateDescription vmstate_pl011_clock = {
.name = "pl011/clock",
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_CLOCK(clk, PL011State),
VMSTATE_END_OF_LIST()
}
};
static const VMStateDescription vmstate_pl011 = {
.name = "pl011",
.version_id = 2,
@ -314,6 +353,10 @@ static const VMStateDescription vmstate_pl011 = {
VMSTATE_INT32(read_count, PL011State),
VMSTATE_INT32(read_trigger, PL011State),
VMSTATE_END_OF_LIST()
},
.subsections = (const VMStateDescription * []) {
&vmstate_pl011_clock,
NULL
}
};
@ -334,6 +377,8 @@ static void pl011_init(Object *obj)
sysbus_init_irq(sbd, &s->irq[i]);
}
s->clk = qdev_init_clock_in(DEVICE(obj), "clk", pl011_clock_update, s);
s->read_trigger = 1;
s->ifl = 0x12;
s->cr = 0x300;