Commit 6936bfe514bbac7af4b24fad9ed9688b78b5be69
1 parent
7caa33f7
8250: throttle TX-completion IRQs
(Jan Kiszka) git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@4335 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
1 changed file
with
49 additions
and
6 deletions
hw/serial.c
... | ... | @@ -25,6 +25,7 @@ |
25 | 25 | #include "qemu-char.h" |
26 | 26 | #include "isa.h" |
27 | 27 | #include "pc.h" |
28 | +#include "qemu-timer.h" | |
28 | 29 | |
29 | 30 | //#define DEBUG_SERIAL |
30 | 31 | |
... | ... | @@ -73,6 +74,13 @@ |
73 | 74 | #define UART_LSR_OE 0x02 /* Overrun error indicator */ |
74 | 75 | #define UART_LSR_DR 0x01 /* Receiver data ready */ |
75 | 76 | |
77 | +/* | |
78 | + * Delay TX IRQ after sending as much characters as the given interval would | |
79 | + * contain on real hardware. This avoids overloading the guest if it processes | |
80 | + * its output buffer in a loop inside the TX IRQ handler. | |
81 | + */ | |
82 | +#define THROTTLE_TX_INTERVAL 10 /* ms */ | |
83 | + | |
76 | 84 | struct SerialState { |
77 | 85 | uint16_t divider; |
78 | 86 | uint8_t rbr; /* receive register */ |
... | ... | @@ -91,6 +99,8 @@ struct SerialState { |
91 | 99 | int last_break_enable; |
92 | 100 | target_phys_addr_t base; |
93 | 101 | int it_shift; |
102 | + QEMUTimer *tx_timer; | |
103 | + int tx_burst; | |
94 | 104 | }; |
95 | 105 | |
96 | 106 | static void serial_receive_byte(SerialState *s, int ch); |
... | ... | @@ -111,6 +121,28 @@ static void serial_update_irq(SerialState *s) |
111 | 121 | } |
112 | 122 | } |
113 | 123 | |
124 | +static void serial_tx_done(void *opaque) | |
125 | +{ | |
126 | + SerialState *s = opaque; | |
127 | + | |
128 | + if (s->tx_burst < 0) { | |
129 | + uint16_t divider; | |
130 | + | |
131 | + if (s->divider) | |
132 | + divider = s->divider; | |
133 | + else | |
134 | + divider = 1; | |
135 | + | |
136 | + /* We assume 10 bits/char, OK for this purpose. */ | |
137 | + s->tx_burst = THROTTLE_TX_INTERVAL * 1000 / | |
138 | + (1000000 * 10 / (115200 / divider)); | |
139 | + } | |
140 | + s->thr_ipending = 1; | |
141 | + s->lsr |= UART_LSR_THRE; | |
142 | + s->lsr |= UART_LSR_TEMT; | |
143 | + serial_update_irq(s); | |
144 | +} | |
145 | + | |
114 | 146 | static void serial_update_parameters(SerialState *s) |
115 | 147 | { |
116 | 148 | int speed, parity, data_bits, stop_bits; |
... | ... | @@ -166,15 +198,18 @@ static void serial_ioport_write(void *opaque, uint32_t addr, uint32_t val) |
166 | 198 | if (!(s->mcr & UART_MCR_LOOP)) { |
167 | 199 | /* when not in loopback mode, send the char */ |
168 | 200 | qemu_chr_write(s->chr, &ch, 1); |
169 | - } | |
170 | - s->thr_ipending = 1; | |
171 | - s->lsr |= UART_LSR_THRE; | |
172 | - s->lsr |= UART_LSR_TEMT; | |
173 | - serial_update_irq(s); | |
174 | - if (s->mcr & UART_MCR_LOOP) { | |
201 | + } else { | |
175 | 202 | /* in loopback mode, say that we just received a char */ |
176 | 203 | serial_receive_byte(s, ch); |
177 | 204 | } |
205 | + if (s->tx_burst > 0) { | |
206 | + s->tx_burst--; | |
207 | + serial_tx_done(s); | |
208 | + } else if (s->tx_burst == 0) { | |
209 | + s->tx_burst--; | |
210 | + qemu_mod_timer(s->tx_timer, qemu_get_clock(vm_clock) + | |
211 | + ticks_per_sec * THROTTLE_TX_INTERVAL / 1000); | |
212 | + } | |
178 | 213 | } |
179 | 214 | break; |
180 | 215 | case 1: |
... | ... | @@ -387,6 +422,10 @@ SerialState *serial_init(int base, qemu_irq irq, CharDriverState *chr) |
387 | 422 | return NULL; |
388 | 423 | s->irq = irq; |
389 | 424 | |
425 | + s->tx_timer = qemu_new_timer(vm_clock, serial_tx_done, s); | |
426 | + if (!s->tx_timer) | |
427 | + return NULL; | |
428 | + | |
390 | 429 | qemu_register_reset(serial_reset, s); |
391 | 430 | serial_reset(s); |
392 | 431 | |
... | ... | @@ -486,6 +525,10 @@ SerialState *serial_mm_init (target_phys_addr_t base, int it_shift, |
486 | 525 | s->base = base; |
487 | 526 | s->it_shift = it_shift; |
488 | 527 | |
528 | + s->tx_timer = qemu_new_timer(vm_clock, serial_tx_done, s); | |
529 | + if (!s->tx_timer) | |
530 | + return NULL; | |
531 | + | |
489 | 532 | qemu_register_reset(serial_reset, s); |
490 | 533 | serial_reset(s); |
491 | 534 | ... | ... |