xref: /kvmtool/hw/serial.c (revision 206c41f433bbbb5763851a2f6df928a66f71193e)
1899fe063SPekka Enberg #include "kvm/8250-serial.h"
213a7760fSPekka Enberg 
38eb47d29SSasha Levin #include "kvm/read-write.h"
413a7760fSPekka Enberg #include "kvm/ioport.h"
54ef0f4d6SPekka Enberg #include "kvm/mutex.h"
646aa8d69SPekka Enberg #include "kvm/util.h"
705d1a2a6SAsias He #include "kvm/term.h"
8e557eef9SPekka Enberg #include "kvm/kvm.h"
913a7760fSPekka Enberg 
103fdf659dSSasha Levin #include <linux/types.h>
114e49b05bSCyrill Gorcunov #include <linux/serial_reg.h>
124e49b05bSCyrill Gorcunov 
132932c9ebSPekka Enberg #include <pthread.h>
1413a7760fSPekka Enberg 
15d4b02a37SThomas Gleixner /*
16d4b02a37SThomas Gleixner  * This fakes a U6_16550A. The fifo len needs to be 64 as the kernel
17d4b02a37SThomas Gleixner  * expects that for autodetection.
18d4b02a37SThomas Gleixner  */
19d4b02a37SThomas Gleixner #define FIFO_LEN		64
20d4b02a37SThomas Gleixner #define FIFO_MASK		(FIFO_LEN - 1)
21d4b02a37SThomas Gleixner 
22d4b02a37SThomas Gleixner #define UART_IIR_TYPE_BITS	0xc0
23d4b02a37SThomas Gleixner 
2446aa8d69SPekka Enberg struct serial8250_device {
25d3476f7dSSasha Levin 	struct mutex		mutex;
261add4b76SSasha Levin 	u8			id;
272932c9ebSPekka Enberg 
283fdf659dSSasha Levin 	u16			iobase;
293fdf659dSSasha Levin 	u8			irq;
30f6b8ccc1SThomas Gleixner 	u8			irq_state;
318dfae8beSThomas Gleixner 	int			txcnt;
32d4b02a37SThomas Gleixner 	int			rxcnt;
33d4b02a37SThomas Gleixner 	int			rxdone;
34d4b02a37SThomas Gleixner 	char			txbuf[FIFO_LEN];
35d4b02a37SThomas Gleixner 	char			rxbuf[FIFO_LEN];
3676b4a122SPekka Enberg 
373fdf659dSSasha Levin 	u8			dll;
383fdf659dSSasha Levin 	u8			dlm;
393fdf659dSSasha Levin 	u8			iir;
403fdf659dSSasha Levin 	u8			ier;
413fdf659dSSasha Levin 	u8			fcr;
423fdf659dSSasha Levin 	u8			lcr;
433fdf659dSSasha Levin 	u8			mcr;
443fdf659dSSasha Levin 	u8			lsr;
453fdf659dSSasha Levin 	u8			msr;
463fdf659dSSasha Levin 	u8			scr;
4746aa8d69SPekka Enberg };
4846aa8d69SPekka Enberg 
49f3efa592SLiming Wang #define SERIAL_REGS_SETTING \
50f3efa592SLiming Wang 	.iir			= UART_IIR_NO_INT, \
51f3efa592SLiming Wang 	.lsr			= UART_LSR_TEMT | UART_LSR_THRE, \
52f3efa592SLiming Wang 	.msr			= UART_MSR_DCD | UART_MSR_DSR | UART_MSR_CTS, \
53f3efa592SLiming Wang 	.mcr			= UART_MCR_OUT2,
54f3efa592SLiming Wang 
55e62c18deSPekka Enberg static struct serial8250_device devices[] = {
56c6a69c61SPekka Enberg 	/* ttyS0 */
57c6a69c61SPekka Enberg 	[0]	= {
58d3476f7dSSasha Levin 		.mutex			= MUTEX_INITIALIZER,
592932c9ebSPekka Enberg 
601add4b76SSasha Levin 		.id			= 0,
61c6a69c61SPekka Enberg 		.iobase			= 0x3f8,
62e557eef9SPekka Enberg 		.irq			= 4,
63e557eef9SPekka Enberg 
64f3efa592SLiming Wang 		SERIAL_REGS_SETTING
65c6a69c61SPekka Enberg 	},
66e62c18deSPekka Enberg 	/* ttyS1 */
67e62c18deSPekka Enberg 	[1]	= {
68d3476f7dSSasha Levin 		.mutex			= MUTEX_INITIALIZER,
692932c9ebSPekka Enberg 
701add4b76SSasha Levin 		.id			= 1,
71e62c18deSPekka Enberg 		.iobase			= 0x2f8,
72e62c18deSPekka Enberg 		.irq			= 3,
73133bedc1SPekka Enberg 
74f3efa592SLiming Wang 		SERIAL_REGS_SETTING
75e62c18deSPekka Enberg 	},
76e62c18deSPekka Enberg 	/* ttyS2 */
77e62c18deSPekka Enberg 	[2]	= {
78d3476f7dSSasha Levin 		.mutex			= MUTEX_INITIALIZER,
792932c9ebSPekka Enberg 
801add4b76SSasha Levin 		.id			= 2,
81e62c18deSPekka Enberg 		.iobase			= 0x3e8,
82e62c18deSPekka Enberg 		.irq			= 4,
83133bedc1SPekka Enberg 
84f3efa592SLiming Wang 		SERIAL_REGS_SETTING
85e62c18deSPekka Enberg 	},
86bf459c83SPekka Enberg 	/* ttyS3 */
87bf459c83SPekka Enberg 	[3]	= {
88d3476f7dSSasha Levin 		.mutex			= MUTEX_INITIALIZER,
89bf459c83SPekka Enberg 
901add4b76SSasha Levin 		.id			= 3,
91bf459c83SPekka Enberg 		.iobase			= 0x2e8,
92bf459c83SPekka Enberg 		.irq			= 3,
93bf459c83SPekka Enberg 
94f3efa592SLiming Wang 		SERIAL_REGS_SETTING
95bf459c83SPekka Enberg 	},
9646aa8d69SPekka Enberg };
9746aa8d69SPekka Enberg 
982651ea58SSasha Levin static void serial8250_flush_tx(struct kvm *kvm, struct serial8250_device *dev)
99d4b02a37SThomas Gleixner {
100d4b02a37SThomas Gleixner 	dev->lsr |= UART_LSR_TEMT | UART_LSR_THRE;
101d4b02a37SThomas Gleixner 
102d4b02a37SThomas Gleixner 	if (dev->txcnt) {
1032651ea58SSasha Levin 		term_putc(dev->txbuf, dev->txcnt, dev->id);
104d4b02a37SThomas Gleixner 		dev->txcnt = 0;
105d4b02a37SThomas Gleixner 	}
106d4b02a37SThomas Gleixner }
107d4b02a37SThomas Gleixner 
108f6b8ccc1SThomas Gleixner static void serial8250_update_irq(struct kvm *kvm, struct serial8250_device *dev)
109f6b8ccc1SThomas Gleixner {
110f6b8ccc1SThomas Gleixner 	u8 iir = 0;
111f6b8ccc1SThomas Gleixner 
112d4b02a37SThomas Gleixner 	/* Handle clear rx */
113d4b02a37SThomas Gleixner 	if (dev->lcr & UART_FCR_CLEAR_RCVR) {
114d4b02a37SThomas Gleixner 		dev->lcr &= ~UART_FCR_CLEAR_RCVR;
115d4b02a37SThomas Gleixner 		dev->rxcnt = dev->rxdone = 0;
116d4b02a37SThomas Gleixner 		dev->lsr &= ~UART_LSR_DR;
117d4b02a37SThomas Gleixner 	}
118d4b02a37SThomas Gleixner 
119d4b02a37SThomas Gleixner 	/* Handle clear tx */
120d4b02a37SThomas Gleixner 	if (dev->lcr & UART_FCR_CLEAR_XMIT) {
121d4b02a37SThomas Gleixner 		dev->lcr &= ~UART_FCR_CLEAR_XMIT;
122d4b02a37SThomas Gleixner 		dev->txcnt = 0;
123d4b02a37SThomas Gleixner 		dev->lsr |= UART_LSR_TEMT | UART_LSR_THRE;
124d4b02a37SThomas Gleixner 	}
125d4b02a37SThomas Gleixner 
126f6b8ccc1SThomas Gleixner 	/* Data ready and rcv interrupt enabled ? */
127f6b8ccc1SThomas Gleixner 	if ((dev->ier & UART_IER_RDI) && (dev->lsr & UART_LSR_DR))
128f6b8ccc1SThomas Gleixner 		iir |= UART_IIR_RDI;
129f6b8ccc1SThomas Gleixner 
130f6b8ccc1SThomas Gleixner 	/* Transmitter empty and interrupt enabled ? */
131f6b8ccc1SThomas Gleixner 	if ((dev->ier & UART_IER_THRI) && (dev->lsr & UART_LSR_TEMT))
132f6b8ccc1SThomas Gleixner 		iir |= UART_IIR_THRI;
133f6b8ccc1SThomas Gleixner 
134f6b8ccc1SThomas Gleixner 	/* Now update the irq line, if necessary */
135f6b8ccc1SThomas Gleixner 	if (!iir) {
136f6b8ccc1SThomas Gleixner 		dev->iir = UART_IIR_NO_INT;
137f6b8ccc1SThomas Gleixner 		if (dev->irq_state)
138f6b8ccc1SThomas Gleixner 			kvm__irq_line(kvm, dev->irq, 0);
139f6b8ccc1SThomas Gleixner 	} else {
140f6b8ccc1SThomas Gleixner 		dev->iir = iir;
141f6b8ccc1SThomas Gleixner 		if (!dev->irq_state)
142f6b8ccc1SThomas Gleixner 			kvm__irq_line(kvm, dev->irq, 1);
143f6b8ccc1SThomas Gleixner 	}
144f6b8ccc1SThomas Gleixner 	dev->irq_state = iir;
1458dfae8beSThomas Gleixner 
1468dfae8beSThomas Gleixner 	/*
1478dfae8beSThomas Gleixner 	 * If the kernel disabled the tx interrupt, we know that there
1488dfae8beSThomas Gleixner 	 * is nothing more to transmit, so we can reset our tx logic
1498dfae8beSThomas Gleixner 	 * here.
1508dfae8beSThomas Gleixner 	 */
151d4b02a37SThomas Gleixner 	if (!(dev->ier & UART_IER_THRI))
1522651ea58SSasha Levin 		serial8250_flush_tx(kvm, dev);
153f6b8ccc1SThomas Gleixner }
154f6b8ccc1SThomas Gleixner 
155a428f72eSPekka Enberg #define SYSRQ_PENDING_NONE		0
156a428f72eSPekka Enberg 
157a428f72eSPekka Enberg static int sysrq_pending;
158a428f72eSPekka Enberg 
15943835ac9SSasha Levin static void serial8250__sysrq(struct kvm *kvm, struct serial8250_device *dev)
160a428f72eSPekka Enberg {
161a428f72eSPekka Enberg 	dev->lsr |= UART_LSR_DR | UART_LSR_BI;
162226e727bSSasha Levin 	dev->rxbuf[dev->rxcnt++] = sysrq_pending;
163a428f72eSPekka Enberg 	sysrq_pending	= SYSRQ_PENDING_NONE;
164a428f72eSPekka Enberg }
165a428f72eSPekka Enberg 
166d4b02a37SThomas Gleixner static void serial8250__receive(struct kvm *kvm, struct serial8250_device *dev,
167d4b02a37SThomas Gleixner 				bool handle_sysrq)
168251cf9a6SPekka Enberg {
169251cf9a6SPekka Enberg 	int c;
170251cf9a6SPekka Enberg 
1718dfae8beSThomas Gleixner 	/*
172d4b02a37SThomas Gleixner 	 * If the guest transmitted a full fifo, we clear the
1738dfae8beSThomas Gleixner 	 * TEMT/THRE bits to let the kernel escape from the 8250
1748dfae8beSThomas Gleixner 	 * interrupt handler. We come here only once a ms, so that
175d4b02a37SThomas Gleixner 	 * should give the kernel the desired pause. That also flushes
176d4b02a37SThomas Gleixner 	 * the tx fifo to the terminal.
1778dfae8beSThomas Gleixner 	 */
1782651ea58SSasha Levin 	serial8250_flush_tx(kvm, dev);
1798dfae8beSThomas Gleixner 
180d4b02a37SThomas Gleixner 	if (dev->mcr & UART_MCR_LOOP)
181db34045cSPekka Enberg 		return;
182db34045cSPekka Enberg 
183d4b02a37SThomas Gleixner 	if ((dev->lsr & UART_LSR_DR) || dev->rxcnt)
184d4b02a37SThomas Gleixner 		return;
185d4b02a37SThomas Gleixner 
186d4b02a37SThomas Gleixner 	if (handle_sysrq && sysrq_pending) {
18743835ac9SSasha Levin 		serial8250__sysrq(kvm, dev);
188a428f72eSPekka Enberg 		return;
189a428f72eSPekka Enberg 	}
190a428f72eSPekka Enberg 
1912651ea58SSasha Levin 	if (kvm->cfg.active_console != CONSOLE_8250)
1922651ea58SSasha Levin 		return;
1932651ea58SSasha Levin 
1942651ea58SSasha Levin 	while (term_readable(dev->id) &&
195d4b02a37SThomas Gleixner 	       dev->rxcnt < FIFO_LEN) {
196251cf9a6SPekka Enberg 
1974346fd8fSSasha Levin 		c = term_getc(kvm, dev->id);
19805d1a2a6SAsias He 
199251cf9a6SPekka Enberg 		if (c < 0)
200d4b02a37SThomas Gleixner 			break;
201d4b02a37SThomas Gleixner 		dev->rxbuf[dev->rxcnt++] = c;
202251cf9a6SPekka Enberg 		dev->lsr |= UART_LSR_DR;
203251cf9a6SPekka Enberg 	}
204d4b02a37SThomas Gleixner }
205251cf9a6SPekka Enberg 
206f6b8ccc1SThomas Gleixner void serial8250__update_consoles(struct kvm *kvm)
2078bb34e0dSPekka Enberg {
208479de16fSCyrill Gorcunov 	unsigned int i;
2091add4b76SSasha Levin 
210479de16fSCyrill Gorcunov 	for (i = 0; i < ARRAY_SIZE(devices); i++) {
2111add4b76SSasha Levin 		struct serial8250_device *dev = &devices[i];
212934c193bSPekka Enberg 
2134ef0f4d6SPekka Enberg 		mutex_lock(&dev->mutex);
2142932c9ebSPekka Enberg 
215d4b02a37SThomas Gleixner 		/* Restrict sysrq injection to the first port */
216d4b02a37SThomas Gleixner 		serial8250__receive(kvm, dev, i == 0);
217251cf9a6SPekka Enberg 
218f6b8ccc1SThomas Gleixner 		serial8250_update_irq(kvm, dev);
2192932c9ebSPekka Enberg 
2204ef0f4d6SPekka Enberg 		mutex_unlock(&dev->mutex);
2218bb34e0dSPekka Enberg 	}
2221add4b76SSasha Levin }
2238bb34e0dSPekka Enberg 
224226e727bSSasha Levin void serial8250__inject_sysrq(struct kvm *kvm, char sysrq)
225a428f72eSPekka Enberg {
226226e727bSSasha Levin 	sysrq_pending = sysrq;
227a428f72eSPekka Enberg }
228a428f72eSPekka Enberg 
2293fdf659dSSasha Levin static struct serial8250_device *find_device(u16 port)
230c6a69c61SPekka Enberg {
231c6a69c61SPekka Enberg 	unsigned int i;
232c6a69c61SPekka Enberg 
233c6a69c61SPekka Enberg 	for (i = 0; i < ARRAY_SIZE(devices); i++) {
234c6a69c61SPekka Enberg 		struct serial8250_device *dev = &devices[i];
235c6a69c61SPekka Enberg 
236c6a69c61SPekka Enberg 		if (dev->iobase == (port & ~0x7))
237c6a69c61SPekka Enberg 			return dev;
238c6a69c61SPekka Enberg 	}
239c6a69c61SPekka Enberg 	return NULL;
240c6a69c61SPekka Enberg }
241c6a69c61SPekka Enberg 
242d4b02a37SThomas Gleixner static bool serial8250_out(struct ioport *ioport, struct kvm *kvm, u16 port,
243d4b02a37SThomas Gleixner 			   void *data, int size)
24413a7760fSPekka Enberg {
245c6a69c61SPekka Enberg 	struct serial8250_device *dev;
2463fdf659dSSasha Levin 	u16 offset;
2472932c9ebSPekka Enberg 	bool ret = true;
248d4b02a37SThomas Gleixner 	char *addr = data;
24946aa8d69SPekka Enberg 
250c6a69c61SPekka Enberg 	dev = find_device(port);
251c6a69c61SPekka Enberg 	if (!dev)
252c6a69c61SPekka Enberg 		return false;
253c6a69c61SPekka Enberg 
2544ef0f4d6SPekka Enberg 	mutex_lock(&dev->mutex);
2552932c9ebSPekka Enberg 
256c6a69c61SPekka Enberg 	offset = port - dev->iobase;
257c6a69c61SPekka Enberg 
25846aa8d69SPekka Enberg 	switch (offset) {
259c59fa0c4SThomas Gleixner 	case UART_TX:
260d4b02a37SThomas Gleixner 		if (dev->lcr & UART_LCR_DLAB) {
261c59fa0c4SThomas Gleixner 			dev->dll = ioport__read8(data);
262d4b02a37SThomas Gleixner 			break;
263d4b02a37SThomas Gleixner 		}
264d4b02a37SThomas Gleixner 
265d4b02a37SThomas Gleixner 		/* Loopback mode */
266d4b02a37SThomas Gleixner 		if (dev->mcr & UART_MCR_LOOP) {
267d4b02a37SThomas Gleixner 			if (dev->rxcnt < FIFO_LEN) {
268d4b02a37SThomas Gleixner 				dev->rxbuf[dev->rxcnt++] = *addr;
269d4b02a37SThomas Gleixner 				dev->lsr |= UART_LSR_DR;
270d4b02a37SThomas Gleixner 			}
271d4b02a37SThomas Gleixner 			break;
272d4b02a37SThomas Gleixner 		}
273d4b02a37SThomas Gleixner 
274d4b02a37SThomas Gleixner 		if (dev->txcnt < FIFO_LEN) {
275d4b02a37SThomas Gleixner 			dev->txbuf[dev->txcnt++] = *addr;
276d4b02a37SThomas Gleixner 			dev->lsr &= ~UART_LSR_TEMT;
277d4b02a37SThomas Gleixner 			if (dev->txcnt == FIFO_LEN / 2)
278d4b02a37SThomas Gleixner 				dev->lsr &= ~UART_LSR_THRE;
279d4b02a37SThomas Gleixner 		} else {
280d4b02a37SThomas Gleixner 			/* Should never happpen */
281d4b02a37SThomas Gleixner 			dev->lsr &= ~(UART_LSR_TEMT | UART_LSR_THRE);
28246aa8d69SPekka Enberg 		}
283369c01c0SPekka Enberg 		break;
2844e49b05bSCyrill Gorcunov 	case UART_IER:
285f6b8ccc1SThomas Gleixner 		if (!(dev->lcr & UART_LCR_DLAB))
286d4b02a37SThomas Gleixner 			dev->ier = ioport__read8(data) & 0x0f;
287f6b8ccc1SThomas Gleixner 		else
288c59fa0c4SThomas Gleixner 			dev->dlm = ioport__read8(data);
289c59fa0c4SThomas Gleixner 		break;
290c59fa0c4SThomas Gleixner 	case UART_FCR:
291c59fa0c4SThomas Gleixner 		dev->fcr = ioport__read8(data);
29246aa8d69SPekka Enberg 		break;
293369c01c0SPekka Enberg 	case UART_LCR:
294369c01c0SPekka Enberg 		dev->lcr = ioport__read8(data);
295369c01c0SPekka Enberg 		break;
296369c01c0SPekka Enberg 	case UART_MCR:
297369c01c0SPekka Enberg 		dev->mcr = ioport__read8(data);
298369c01c0SPekka Enberg 		break;
299369c01c0SPekka Enberg 	case UART_LSR:
300369c01c0SPekka Enberg 		/* Factory test */
301369c01c0SPekka Enberg 		break;
302369c01c0SPekka Enberg 	case UART_MSR:
303369c01c0SPekka Enberg 		/* Not used */
304369c01c0SPekka Enberg 		break;
305369c01c0SPekka Enberg 	case UART_SCR:
306369c01c0SPekka Enberg 		dev->scr = ioport__read8(data);
307369c01c0SPekka Enberg 		break;
308369c01c0SPekka Enberg 	default:
3092932c9ebSPekka Enberg 		ret = false;
310d2ea115dSThomas Gleixner 		break;
31146aa8d69SPekka Enberg 	}
3122932c9ebSPekka Enberg 
313f6b8ccc1SThomas Gleixner 	serial8250_update_irq(kvm, dev);
314f6b8ccc1SThomas Gleixner 
3154ef0f4d6SPekka Enberg 	mutex_unlock(&dev->mutex);
3162932c9ebSPekka Enberg 
3172932c9ebSPekka Enberg 	return ret;
31813a7760fSPekka Enberg }
31913a7760fSPekka Enberg 
320d4b02a37SThomas Gleixner static void serial8250_rx(struct serial8250_device *dev, void *data)
321d4b02a37SThomas Gleixner {
322d4b02a37SThomas Gleixner 	if (dev->rxdone == dev->rxcnt)
323d4b02a37SThomas Gleixner 		return;
324d4b02a37SThomas Gleixner 
325d4b02a37SThomas Gleixner 	/* Break issued ? */
326d4b02a37SThomas Gleixner 	if (dev->lsr & UART_LSR_BI) {
327d4b02a37SThomas Gleixner 		dev->lsr &= ~UART_LSR_BI;
328d4b02a37SThomas Gleixner 		ioport__write8(data, 0);
329d4b02a37SThomas Gleixner 		return;
330d4b02a37SThomas Gleixner 	}
331d4b02a37SThomas Gleixner 
332d4b02a37SThomas Gleixner 	ioport__write8(data, dev->rxbuf[dev->rxdone++]);
333d4b02a37SThomas Gleixner 	if (dev->rxcnt == dev->rxdone) {
334d4b02a37SThomas Gleixner 		dev->lsr &= ~UART_LSR_DR;
335d4b02a37SThomas Gleixner 		dev->rxcnt = dev->rxdone = 0;
336d4b02a37SThomas Gleixner 	}
337d4b02a37SThomas Gleixner }
338d4b02a37SThomas Gleixner 
339c9f6a037SXiao Guangrong static bool serial8250_in(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size)
34025af6674SPekka Enberg {
341c6a69c61SPekka Enberg 	struct serial8250_device *dev;
3423fdf659dSSasha Levin 	u16 offset;
3432932c9ebSPekka Enberg 	bool ret = true;
34446aa8d69SPekka Enberg 
345c6a69c61SPekka Enberg 	dev = find_device(port);
346c6a69c61SPekka Enberg 	if (!dev)
347c6a69c61SPekka Enberg 		return false;
348c6a69c61SPekka Enberg 
3494ef0f4d6SPekka Enberg 	mutex_lock(&dev->mutex);
3502932c9ebSPekka Enberg 
351c6a69c61SPekka Enberg 	offset = port - dev->iobase;
352c6a69c61SPekka Enberg 
35346aa8d69SPekka Enberg 	switch (offset) {
354251cf9a6SPekka Enberg 	case UART_RX:
355d4b02a37SThomas Gleixner 		if (dev->lcr & UART_LCR_DLAB)
356c59fa0c4SThomas Gleixner 			ioport__write8(data, dev->dll);
357d4b02a37SThomas Gleixner 		else
358d4b02a37SThomas Gleixner 			serial8250_rx(dev, data);
359369c01c0SPekka Enberg 		break;
360c59fa0c4SThomas Gleixner 	case UART_IER:
361c59fa0c4SThomas Gleixner 		if (dev->lcr & UART_LCR_DLAB)
362c59fa0c4SThomas Gleixner 			ioport__write8(data, dev->dlm);
363c59fa0c4SThomas Gleixner 		else
364c59fa0c4SThomas Gleixner 			ioport__write8(data, dev->ier);
365c59fa0c4SThomas Gleixner 		break;
366f6b8ccc1SThomas Gleixner 	case UART_IIR:
367d4b02a37SThomas Gleixner 		ioport__write8(data, dev->iir | UART_IIR_TYPE_BITS);
36846aa8d69SPekka Enberg 		break;
3694e49b05bSCyrill Gorcunov 	case UART_LCR:
370c6a69c61SPekka Enberg 		ioport__write8(data, dev->lcr);
37146aa8d69SPekka Enberg 		break;
3724e49b05bSCyrill Gorcunov 	case UART_MCR:
373c6a69c61SPekka Enberg 		ioport__write8(data, dev->mcr);
37446aa8d69SPekka Enberg 		break;
3754e49b05bSCyrill Gorcunov 	case UART_LSR:
376c6a69c61SPekka Enberg 		ioport__write8(data, dev->lsr);
37746aa8d69SPekka Enberg 		break;
3784e49b05bSCyrill Gorcunov 	case UART_MSR:
379369c01c0SPekka Enberg 		ioport__write8(data, dev->msr);
38046aa8d69SPekka Enberg 		break;
3814e49b05bSCyrill Gorcunov 	case UART_SCR:
382369c01c0SPekka Enberg 		ioport__write8(data, dev->scr);
38346aa8d69SPekka Enberg 		break;
384369c01c0SPekka Enberg 	default:
3852932c9ebSPekka Enberg 		ret = false;
386c59fa0c4SThomas Gleixner 		break;
38725af6674SPekka Enberg 	}
388f6b8ccc1SThomas Gleixner 
389f6b8ccc1SThomas Gleixner 	serial8250_update_irq(kvm, dev);
390f6b8ccc1SThomas Gleixner 
3914ef0f4d6SPekka Enberg 	mutex_unlock(&dev->mutex);
3922932c9ebSPekka Enberg 
3932932c9ebSPekka Enberg 	return ret;
39413a7760fSPekka Enberg }
39513a7760fSPekka Enberg 
39646aa8d69SPekka Enberg static struct ioport_operations serial8250_ops = {
39746aa8d69SPekka Enberg 	.io_in		= serial8250_in,
39846aa8d69SPekka Enberg 	.io_out		= serial8250_out,
399a93ec68bSPekka Enberg };
400a93ec68bSPekka Enberg 
40120715a22SSasha Levin static int serial8250__device_init(struct kvm *kvm, struct serial8250_device *dev)
402bc4b0ffeSPekka Enberg {
40320715a22SSasha Levin 	int r;
40420715a22SSasha Levin 
405*206c41f4SWill Deacon 	ioport__map_irq(&dev->irq);
4064346fd8fSSasha Levin 	r = ioport__register(kvm, dev->iobase, &serial8250_ops, 8, NULL);
407bc4b0ffeSPekka Enberg 	kvm__irq_line(kvm, dev->irq, 0);
40820715a22SSasha Levin 
40920715a22SSasha Levin 	return r;
410bc4b0ffeSPekka Enberg }
411bc4b0ffeSPekka Enberg 
41220715a22SSasha Levin int serial8250__init(struct kvm *kvm)
41313a7760fSPekka Enberg {
41420715a22SSasha Levin 	unsigned int i, j;
41520715a22SSasha Levin 	int r = 0;
416c6a69c61SPekka Enberg 
417c6a69c61SPekka Enberg 	for (i = 0; i < ARRAY_SIZE(devices); i++) {
418c6a69c61SPekka Enberg 		struct serial8250_device *dev = &devices[i];
419c6a69c61SPekka Enberg 
42020715a22SSasha Levin 		r = serial8250__device_init(kvm, dev);
42120715a22SSasha Levin 		if (r < 0)
42220715a22SSasha Levin 			goto cleanup;
423c6a69c61SPekka Enberg 	}
42420715a22SSasha Levin 
42520715a22SSasha Levin 	return r;
42620715a22SSasha Levin cleanup:
42720715a22SSasha Levin 	for (j = 0; j <= i; j++) {
42820715a22SSasha Levin 		struct serial8250_device *dev = &devices[j];
42920715a22SSasha Levin 
4304346fd8fSSasha Levin 		ioport__unregister(kvm, dev->iobase);
43120715a22SSasha Levin 	}
43220715a22SSasha Levin 
43320715a22SSasha Levin 	return r;
43420715a22SSasha Levin }
43549a8afd1SSasha Levin dev_init(serial8250__init);
43620715a22SSasha Levin 
43720715a22SSasha Levin int serial8250__exit(struct kvm *kvm)
43820715a22SSasha Levin {
43920715a22SSasha Levin 	unsigned int i;
44020715a22SSasha Levin 	int r;
44120715a22SSasha Levin 
44220715a22SSasha Levin 	for (i = 0; i < ARRAY_SIZE(devices); i++) {
44320715a22SSasha Levin 		struct serial8250_device *dev = &devices[i];
44420715a22SSasha Levin 
4454346fd8fSSasha Levin 		r = ioport__unregister(kvm, dev->iobase);
44620715a22SSasha Levin 		if (r < 0)
44720715a22SSasha Levin 			return r;
44820715a22SSasha Levin 	}
44920715a22SSasha Levin 
45020715a22SSasha Levin 	return 0;
45113a7760fSPekka Enberg }
45249a8afd1SSasha Levin dev_exit(serial8250__exit);
453