xref: /kvmtool/hw/serial.c (revision 46aa8d6918b706e184583c1898b73be090ce66df)
113a7760fSPekka Enberg #include "kvm/early_printk.h"
213a7760fSPekka Enberg 
313a7760fSPekka Enberg #include "kvm/ioport.h"
4*46aa8d69SPekka Enberg #include "kvm/util.h"
513a7760fSPekka Enberg 
6*46aa8d69SPekka Enberg #include <stdbool.h>
713a7760fSPekka Enberg 
8*46aa8d69SPekka Enberg /* Transmitter holding register */
9*46aa8d69SPekka Enberg #define THR             0
103f55f3acSPekka Enberg 
11*46aa8d69SPekka Enberg /* Receive buffer register */
12*46aa8d69SPekka Enberg #define RBR             0
133f55f3acSPekka Enberg 
14*46aa8d69SPekka Enberg /* Divisor latch low byte */
15*46aa8d69SPekka Enberg #define DLL		0
163f55f3acSPekka Enberg 
17*46aa8d69SPekka Enberg /* Divisor latch high byte */
18*46aa8d69SPekka Enberg #define DLM		1
19*46aa8d69SPekka Enberg 
20*46aa8d69SPekka Enberg /* Interrupt enable register */
21*46aa8d69SPekka Enberg #define IER		1
22*46aa8d69SPekka Enberg 
23*46aa8d69SPekka Enberg /* Interrupt identification register */
24*46aa8d69SPekka Enberg #define IIR		2
25*46aa8d69SPekka Enberg 
26*46aa8d69SPekka Enberg /* 16550 FIFO Control Register */
27*46aa8d69SPekka Enberg #define FCR		2
28*46aa8d69SPekka Enberg 
29*46aa8d69SPekka Enberg /* Line control register */
30*46aa8d69SPekka Enberg #define LCR		3
31*46aa8d69SPekka Enberg enum {
32*46aa8d69SPekka Enberg 	DLAB		= 1 << 7,		/* Divisor latch access bit (DLAB) */
33*46aa8d69SPekka Enberg 	/* bit 7 - set break enable */
34*46aa8d69SPekka Enberg 	PM2		= 1 << 5,
35*46aa8d69SPekka Enberg 	PM1		= 1 << 4,
36*46aa8d69SPekka Enberg 	PM0		= 1 << 3,
37*46aa8d69SPekka Enberg 	STB		= 1 << 2,
38*46aa8d69SPekka Enberg 	WLS1		= 1 << 1,
39*46aa8d69SPekka Enberg 	WLS0		= 1 << 0,
40*46aa8d69SPekka Enberg };
41*46aa8d69SPekka Enberg 
42*46aa8d69SPekka Enberg /* Modem control register */
43*46aa8d69SPekka Enberg #define MCR		4
44*46aa8d69SPekka Enberg 
45*46aa8d69SPekka Enberg /* Line status register */
46*46aa8d69SPekka Enberg #define LSR		5
47*46aa8d69SPekka Enberg 
48*46aa8d69SPekka Enberg /* Modem status register */
49*46aa8d69SPekka Enberg #define MSR		6
50*46aa8d69SPekka Enberg 
51*46aa8d69SPekka Enberg /* Scratch register */
52*46aa8d69SPekka Enberg #define SCR		7
53*46aa8d69SPekka Enberg 
54*46aa8d69SPekka Enberg struct serial8250_device {
55*46aa8d69SPekka Enberg 	uint16_t		iobase;
56*46aa8d69SPekka Enberg 	uint8_t			dll;
57*46aa8d69SPekka Enberg 	uint8_t			dlm;
58*46aa8d69SPekka Enberg 	uint8_t			ier;
59*46aa8d69SPekka Enberg 	uint8_t			fcr;
60*46aa8d69SPekka Enberg 	uint8_t			lcr;
61*46aa8d69SPekka Enberg 	uint8_t			mcr;
62*46aa8d69SPekka Enberg 	uint8_t			scr;
63*46aa8d69SPekka Enberg };
64*46aa8d69SPekka Enberg 
65*46aa8d69SPekka Enberg static struct serial8250_device device = {
66*46aa8d69SPekka Enberg 	.iobase			= 0x3f8,	/* ttyS0 */
67*46aa8d69SPekka Enberg };
68*46aa8d69SPekka Enberg 
69*46aa8d69SPekka Enberg static bool serial8250_out(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
7013a7760fSPekka Enberg {
71*46aa8d69SPekka Enberg 	uint16_t offset = port - device.iobase;
72*46aa8d69SPekka Enberg 
73*46aa8d69SPekka Enberg 	if (device.lcr & DLAB) {
74*46aa8d69SPekka Enberg 		switch (offset) {
75*46aa8d69SPekka Enberg 		case DLL:
76*46aa8d69SPekka Enberg 			device.dll		= ioport__read8(data);
77*46aa8d69SPekka Enberg 			break;
78*46aa8d69SPekka Enberg 		case DLM:
79*46aa8d69SPekka Enberg 			device.dlm		= ioport__read8(data);
80*46aa8d69SPekka Enberg 			break;
81*46aa8d69SPekka Enberg 		case FCR:
82*46aa8d69SPekka Enberg 			device.fcr		= ioport__read8(data);
83*46aa8d69SPekka Enberg 			break;
84*46aa8d69SPekka Enberg 		case LCR:
85*46aa8d69SPekka Enberg 			device.lcr		= ioport__read8(data);
86*46aa8d69SPekka Enberg 			break;
87*46aa8d69SPekka Enberg 		default:
88*46aa8d69SPekka Enberg 			return false;
89*46aa8d69SPekka Enberg 		}
90*46aa8d69SPekka Enberg 	} else {
91*46aa8d69SPekka Enberg 		switch (offset) {
92*46aa8d69SPekka Enberg 		case THR: {
9313a7760fSPekka Enberg 			char *p = data;
94f2d8dc88SCyrill Gorcunov 			int i;
9513a7760fSPekka Enberg 
96f2d8dc88SCyrill Gorcunov 			while (count--) {
97f2d8dc88SCyrill Gorcunov 				for (i = 0; i < size; i++)
98b7475544SPekka Enberg 					fprintf(stdout, "%c", *p++);
995b9d0b58SAsias He 			}
100b7475544SPekka Enberg 			fflush(stdout);
101*46aa8d69SPekka Enberg 			break;
102*46aa8d69SPekka Enberg 		}
103*46aa8d69SPekka Enberg 		case IER:
104*46aa8d69SPekka Enberg 			device.ier		= ioport__read8(data);
105*46aa8d69SPekka Enberg 			break;
106*46aa8d69SPekka Enberg 		case FCR:
107*46aa8d69SPekka Enberg 			device.fcr		= ioport__read8(data);
108*46aa8d69SPekka Enberg 			break;
109*46aa8d69SPekka Enberg 		case LCR:
110*46aa8d69SPekka Enberg 			device.lcr		= ioport__read8(data);
111*46aa8d69SPekka Enberg 			break;
112*46aa8d69SPekka Enberg 		case MCR:
113*46aa8d69SPekka Enberg 			device.mcr		= ioport__read8(data);
114*46aa8d69SPekka Enberg 			break;
115*46aa8d69SPekka Enberg 		case SCR:
116*46aa8d69SPekka Enberg 			device.scr		= ioport__read8(data);
117*46aa8d69SPekka Enberg 			break;
118*46aa8d69SPekka Enberg 		default:
119*46aa8d69SPekka Enberg 			return false;
120*46aa8d69SPekka Enberg 		}
121*46aa8d69SPekka Enberg 	}
12213a7760fSPekka Enberg 
12313a7760fSPekka Enberg 	return true;
12413a7760fSPekka Enberg }
12513a7760fSPekka Enberg 
126*46aa8d69SPekka Enberg static bool serial8250_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
12725af6674SPekka Enberg {
128*46aa8d69SPekka Enberg 	uint16_t offset = port - device.iobase;
129*46aa8d69SPekka Enberg 
130*46aa8d69SPekka Enberg 	if (device.lcr & DLAB)
131*46aa8d69SPekka Enberg 		return false;
132*46aa8d69SPekka Enberg 
133*46aa8d69SPekka Enberg 	switch (offset) {
134*46aa8d69SPekka Enberg 	case THR:
135*46aa8d69SPekka Enberg 		ioport__write8(data, 0x00);
136*46aa8d69SPekka Enberg 		break;
137*46aa8d69SPekka Enberg 	case IER:
138*46aa8d69SPekka Enberg 		ioport__write8(data, device.ier);
139*46aa8d69SPekka Enberg 		break;
140*46aa8d69SPekka Enberg 	case IIR:
141*46aa8d69SPekka Enberg 		ioport__write8(data, 0x01); /* no interrupt pending */
142*46aa8d69SPekka Enberg 		break;
143*46aa8d69SPekka Enberg 	case LCR:
144*46aa8d69SPekka Enberg 		ioport__write8(data, device.lcr);
145*46aa8d69SPekka Enberg 		break;
146*46aa8d69SPekka Enberg 	case MCR:
147*46aa8d69SPekka Enberg 		ioport__write8(data, device.mcr);
148*46aa8d69SPekka Enberg 		break;
149*46aa8d69SPekka Enberg 	case LSR:
150*46aa8d69SPekka Enberg 		ioport__write8(data, 0x20); /* XMTRDY */
151*46aa8d69SPekka Enberg 		break;
152*46aa8d69SPekka Enberg 	case MSR:
153*46aa8d69SPekka Enberg 		ioport__write8(data, 0x01); /* clear to send */
154*46aa8d69SPekka Enberg 		break;
155*46aa8d69SPekka Enberg 	case SCR:
156*46aa8d69SPekka Enberg 		ioport__write8(data, device.scr);
157*46aa8d69SPekka Enberg 		break;
158*46aa8d69SPekka Enberg 	default:
159*46aa8d69SPekka Enberg 		return false;
16025af6674SPekka Enberg 	}
16125af6674SPekka Enberg 
16213a7760fSPekka Enberg 	return true;
16313a7760fSPekka Enberg }
16413a7760fSPekka Enberg 
165*46aa8d69SPekka Enberg static struct ioport_operations serial8250_ops = {
166*46aa8d69SPekka Enberg 	.io_in		= serial8250_in,
167*46aa8d69SPekka Enberg 	.io_out		= serial8250_out,
168a93ec68bSPekka Enberg };
169a93ec68bSPekka Enberg 
17013a7760fSPekka Enberg void early_printk__init(void)
17113a7760fSPekka Enberg {
172*46aa8d69SPekka Enberg 	ioport__register(device.iobase, &serial8250_ops, 8);
17313a7760fSPekka Enberg }
174