summaryrefslogtreecommitdiff
path: root/hw/char/serial.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/char/serial.c')
-rw-r--r--hw/char/serial.c231
1 files changed, 199 insertions, 32 deletions
diff --git a/hw/char/serial.c b/hw/char/serial.c
index 764e1846c..ebcacdc87 100644
--- a/hw/char/serial.c
+++ b/hw/char/serial.c
@@ -272,6 +272,36 @@ static gboolean serial_xmit(GIOChannel *chan, GIOCondition cond, void *opaque)
}
+/* Setter for FCR.
+ is_load flag means, that value is set while loading VM state
+ and interrupt should not be invoked */
+static void serial_write_fcr(SerialState *s, uint8_t val)
+{
+ /* Set fcr - val only has the bits that are supposed to "stick" */
+ s->fcr = val;
+
+ if (val & UART_FCR_FE) {
+ s->iir |= UART_IIR_FE;
+ /* Set recv_fifo trigger Level */
+ switch (val & 0xC0) {
+ case UART_FCR_ITL_1:
+ s->recv_fifo_itl = 1;
+ break;
+ case UART_FCR_ITL_2:
+ s->recv_fifo_itl = 4;
+ break;
+ case UART_FCR_ITL_3:
+ s->recv_fifo_itl = 8;
+ break;
+ case UART_FCR_ITL_4:
+ s->recv_fifo_itl = 14;
+ break;
+ }
+ } else {
+ s->iir &= ~UART_IIR_FE;
+ }
+}
+
static void serial_ioport_write(void *opaque, hwaddr addr, uint64_t val,
unsigned size)
{
@@ -327,20 +357,16 @@ static void serial_ioport_write(void *opaque, hwaddr addr, uint64_t val,
}
break;
case 2:
- val = val & 0xFF;
-
- if (s->fcr == val)
- break;
-
/* Did the enable/disable flag change? If so, make sure FIFOs get flushed */
- if ((val ^ s->fcr) & UART_FCR_FE)
+ if ((val ^ s->fcr) & UART_FCR_FE) {
val |= UART_FCR_XFR | UART_FCR_RFR;
+ }
/* FIFO clear */
if (val & UART_FCR_RFR) {
timer_del(s->fifo_timeout_timer);
- s->timeout_ipending=0;
+ s->timeout_ipending = 0;
fifo8_reset(&s->recv_fifo);
}
@@ -348,28 +374,7 @@ static void serial_ioport_write(void *opaque, hwaddr addr, uint64_t val,
fifo8_reset(&s->xmit_fifo);
}
- if (val & UART_FCR_FE) {
- s->iir |= UART_IIR_FE;
- /* Set recv_fifo trigger Level */
- switch (val & 0xC0) {
- case UART_FCR_ITL_1:
- s->recv_fifo_itl = 1;
- break;
- case UART_FCR_ITL_2:
- s->recv_fifo_itl = 4;
- break;
- case UART_FCR_ITL_3:
- s->recv_fifo_itl = 8;
- break;
- case UART_FCR_ITL_4:
- s->recv_fifo_itl = 14;
- break;
- }
- } else
- s->iir &= ~UART_IIR_FE;
-
- /* Set fcr - or at least the bits in it that are supposed to "stick" */
- s->fcr = val & 0xC9;
+ serial_write_fcr(s, val & 0xC9);
serial_update_irq(s);
break;
case 3:
@@ -590,6 +595,14 @@ static void serial_pre_save(void *opaque)
s->fcr_vmstate = s->fcr;
}
+static int serial_pre_load(void *opaque)
+{
+ SerialState *s = opaque;
+ s->thr_ipending = -1;
+ s->poll_msl = -1;
+ return 0;
+}
+
static int serial_post_load(void *opaque, int version_id)
{
SerialState *s = opaque;
@@ -597,17 +610,139 @@ static int serial_post_load(void *opaque, int version_id)
if (version_id < 3) {
s->fcr_vmstate = 0;
}
+ if (s->thr_ipending == -1) {
+ s->thr_ipending = ((s->iir & UART_IIR_ID) == UART_IIR_THRI);
+ }
+ s->last_break_enable = (s->lcr >> 6) & 1;
/* Initialize fcr via setter to perform essential side-effects */
- serial_ioport_write(s, 0x02, s->fcr_vmstate, 1);
+ serial_write_fcr(s, s->fcr_vmstate);
serial_update_parameters(s);
return 0;
}
+static bool serial_thr_ipending_needed(void *opaque)
+{
+ SerialState *s = opaque;
+ bool expected_value = ((s->iir & UART_IIR_ID) == UART_IIR_THRI);
+ return s->thr_ipending != expected_value;
+}
+
+const VMStateDescription vmstate_serial_thr_ipending = {
+ .name = "serial/thr_ipending",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_INT32(thr_ipending, SerialState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static bool serial_tsr_needed(void *opaque)
+{
+ SerialState *s = (SerialState *)opaque;
+ return s->tsr_retry != 0;
+}
+
+const VMStateDescription vmstate_serial_tsr = {
+ .name = "serial/tsr",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_INT32(tsr_retry, SerialState),
+ VMSTATE_UINT8(thr, SerialState),
+ VMSTATE_UINT8(tsr, SerialState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static bool serial_recv_fifo_needed(void *opaque)
+{
+ SerialState *s = (SerialState *)opaque;
+ return !fifo8_is_empty(&s->recv_fifo);
+
+}
+
+const VMStateDescription vmstate_serial_recv_fifo = {
+ .name = "serial/recv_fifo",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_STRUCT(recv_fifo, SerialState, 1, vmstate_fifo8, Fifo8),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static bool serial_xmit_fifo_needed(void *opaque)
+{
+ SerialState *s = (SerialState *)opaque;
+ return !fifo8_is_empty(&s->xmit_fifo);
+}
+
+const VMStateDescription vmstate_serial_xmit_fifo = {
+ .name = "serial/xmit_fifo",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_STRUCT(xmit_fifo, SerialState, 1, vmstate_fifo8, Fifo8),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static bool serial_fifo_timeout_timer_needed(void *opaque)
+{
+ SerialState *s = (SerialState *)opaque;
+ return timer_pending(s->fifo_timeout_timer);
+}
+
+const VMStateDescription vmstate_serial_fifo_timeout_timer = {
+ .name = "serial/fifo_timeout_timer",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_TIMER(fifo_timeout_timer, SerialState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static bool serial_timeout_ipending_needed(void *opaque)
+{
+ SerialState *s = (SerialState *)opaque;
+ return s->timeout_ipending != 0;
+}
+
+const VMStateDescription vmstate_serial_timeout_ipending = {
+ .name = "serial/timeout_ipending",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_INT32(timeout_ipending, SerialState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static bool serial_poll_needed(void *opaque)
+{
+ SerialState *s = (SerialState *)opaque;
+ return s->poll_msl >= 0;
+}
+
+const VMStateDescription vmstate_serial_poll = {
+ .name = "serial/poll",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_INT32(poll_msl, SerialState),
+ VMSTATE_TIMER(modem_status_poll, SerialState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
const VMStateDescription vmstate_serial = {
.name = "serial",
.version_id = 3,
.minimum_version_id = 2,
.pre_save = serial_pre_save,
+ .pre_load = serial_pre_load,
.post_load = serial_post_load,
.fields = (VMStateField[]) {
VMSTATE_UINT16_V(divider, SerialState, 2),
@@ -621,6 +756,32 @@ const VMStateDescription vmstate_serial = {
VMSTATE_UINT8(scr, SerialState),
VMSTATE_UINT8_V(fcr_vmstate, SerialState, 3),
VMSTATE_END_OF_LIST()
+ },
+ .subsections = (VMStateSubsection[]) {
+ {
+ .vmsd = &vmstate_serial_thr_ipending,
+ .needed = &serial_thr_ipending_needed,
+ } , {
+ .vmsd = &vmstate_serial_tsr,
+ .needed = &serial_tsr_needed,
+ } , {
+ .vmsd = &vmstate_serial_recv_fifo,
+ .needed = &serial_recv_fifo_needed,
+ } , {
+ .vmsd = &vmstate_serial_xmit_fifo,
+ .needed = &serial_xmit_fifo_needed,
+ } , {
+ .vmsd = &vmstate_serial_fifo_timeout_timer,
+ .needed = &serial_fifo_timeout_timer_needed,
+ } , {
+ .vmsd = &vmstate_serial_timeout_ipending,
+ .needed = &serial_timeout_ipending_needed,
+ } , {
+ .vmsd = &vmstate_serial_poll,
+ .needed = &serial_poll_needed,
+ } , {
+ /* empty */
+ }
}
};
@@ -642,6 +803,10 @@ static void serial_reset(void *opaque)
s->char_transmit_time = (get_ticks_per_sec() / 9600) * 10;
s->poll_msl = 0;
+ s->timeout_ipending = 0;
+ timer_del(s->fifo_timeout_timer);
+ timer_del(s->modem_status_poll);
+
fifo8_reset(&s->recv_fifo);
fifo8_reset(&s->xmit_fifo);
@@ -650,6 +815,9 @@ static void serial_reset(void *opaque)
s->thr_ipending = 0;
s->last_break_enable = 0;
qemu_irq_lower(s->irq);
+
+ serial_update_msl(s);
+ s->msr &= ~UART_MSR_ANY_DELTA;
}
void serial_realize_core(SerialState *s, Error **errp)
@@ -668,6 +836,7 @@ void serial_realize_core(SerialState *s, Error **errp)
serial_event, s);
fifo8_create(&s->recv_fifo, UART_FIFO_LENGTH);
fifo8_create(&s->xmit_fifo, UART_FIFO_LENGTH);
+ serial_reset(s);
}
void serial_exit_core(SerialState *s)
@@ -779,7 +948,5 @@ SerialState *serial_mm_init(MemoryRegion *address_space,
memory_region_init_io(&s->io, NULL, &serial_mm_ops[end], s,
"serial", 8 << it_shift);
memory_region_add_subregion(address_space, base, &s->io);
-
- serial_update_msl(s);
return s;
}