1899fe063SPekka Enberg #include "kvm/8250-serial.h" 213a7760fSPekka Enberg 313a7760fSPekka Enberg #include "kvm/ioport.h" 446aa8d69SPekka Enberg #include "kvm/util.h" 5*e557eef9SPekka Enberg #include "kvm/kvm.h" 613a7760fSPekka Enberg 746aa8d69SPekka Enberg #include <stdbool.h> 813a7760fSPekka Enberg 946aa8d69SPekka Enberg /* Transmitter holding register */ 1046aa8d69SPekka Enberg #define THR 0 113f55f3acSPekka Enberg 1246aa8d69SPekka Enberg /* Receive buffer register */ 1346aa8d69SPekka Enberg #define RBR 0 143f55f3acSPekka Enberg 1546aa8d69SPekka Enberg /* Divisor latch low byte */ 1646aa8d69SPekka Enberg #define DLL 0 173f55f3acSPekka Enberg 1846aa8d69SPekka Enberg /* Divisor latch high byte */ 1946aa8d69SPekka Enberg #define DLM 1 2046aa8d69SPekka Enberg 2146aa8d69SPekka Enberg /* Interrupt enable register */ 2246aa8d69SPekka Enberg #define IER 1 2346aa8d69SPekka Enberg 24*e557eef9SPekka Enberg #define UART_IER_THRI 0x02 25*e557eef9SPekka Enberg 2646aa8d69SPekka Enberg /* Interrupt identification register */ 2746aa8d69SPekka Enberg #define IIR 2 2846aa8d69SPekka Enberg 299a7428ddSPekka Enberg #define UART_IIR_NO_INT 0x01 30900d32c1SPekka Enberg #define UART_IIR_THRI 0x02 319a7428ddSPekka Enberg 3246aa8d69SPekka Enberg /* 16550 FIFO Control Register */ 3346aa8d69SPekka Enberg #define FCR 2 3446aa8d69SPekka Enberg 3546aa8d69SPekka Enberg /* Line control register */ 3646aa8d69SPekka Enberg #define LCR 3 379a7428ddSPekka Enberg 389a7428ddSPekka Enberg #define UART_LCR_DLAB 0x80 3946aa8d69SPekka Enberg 4046aa8d69SPekka Enberg /* Modem control register */ 4146aa8d69SPekka Enberg #define MCR 4 4246aa8d69SPekka Enberg 4346aa8d69SPekka Enberg /* Line status register */ 4446aa8d69SPekka Enberg #define LSR 5 4546aa8d69SPekka Enberg 469a7428ddSPekka Enberg #define UART_LSR_THRE 0x20 479a7428ddSPekka Enberg 4846aa8d69SPekka Enberg /* Modem status register */ 4946aa8d69SPekka Enberg #define MSR 6 5046aa8d69SPekka Enberg 519a7428ddSPekka Enberg #define UART_MSR_CTS 0x10 529a7428ddSPekka Enberg 5346aa8d69SPekka Enberg /* Scratch register */ 5446aa8d69SPekka Enberg #define SCR 7 5546aa8d69SPekka Enberg 5646aa8d69SPekka Enberg struct serial8250_device { 5746aa8d69SPekka Enberg uint16_t iobase; 58*e557eef9SPekka Enberg uint8_t irq; 5946aa8d69SPekka Enberg uint8_t dll; 6046aa8d69SPekka Enberg uint8_t dlm; 61*e557eef9SPekka Enberg uint8_t iir; 6246aa8d69SPekka Enberg uint8_t ier; 6346aa8d69SPekka Enberg uint8_t fcr; 6446aa8d69SPekka Enberg uint8_t lcr; 6546aa8d69SPekka Enberg uint8_t mcr; 6646aa8d69SPekka Enberg uint8_t scr; 6746aa8d69SPekka Enberg }; 6846aa8d69SPekka Enberg 6946aa8d69SPekka Enberg static struct serial8250_device device = { 7046aa8d69SPekka Enberg .iobase = 0x3f8, /* ttyS0 */ 71*e557eef9SPekka Enberg .irq = 4, 72*e557eef9SPekka Enberg 73*e557eef9SPekka Enberg .iir = UART_IIR_NO_INT | UART_IIR_THRI, 7446aa8d69SPekka Enberg }; 7546aa8d69SPekka Enberg 768bb34e0dSPekka Enberg void serial8250__interrupt(struct kvm *self) 778bb34e0dSPekka Enberg { 78*e557eef9SPekka Enberg if (device.ier & UART_IER_THRI) { 79*e557eef9SPekka Enberg device.iir &= ~UART_IIR_NO_INT; 80*e557eef9SPekka Enberg kvm__irq_line(self, device.irq, 1); 81*e557eef9SPekka Enberg } 828bb34e0dSPekka Enberg } 838bb34e0dSPekka Enberg 8446aa8d69SPekka Enberg static bool serial8250_out(struct kvm *self, uint16_t port, void *data, int size, uint32_t count) 8513a7760fSPekka Enberg { 8646aa8d69SPekka Enberg uint16_t offset = port - device.iobase; 8746aa8d69SPekka Enberg 889a7428ddSPekka Enberg if (device.lcr & UART_LCR_DLAB) { 8946aa8d69SPekka Enberg switch (offset) { 9046aa8d69SPekka Enberg case DLL: 9146aa8d69SPekka Enberg device.dll = ioport__read8(data); 9246aa8d69SPekka Enberg break; 9346aa8d69SPekka Enberg case DLM: 9446aa8d69SPekka Enberg device.dlm = ioport__read8(data); 9546aa8d69SPekka Enberg break; 9646aa8d69SPekka Enberg case FCR: 9746aa8d69SPekka Enberg device.fcr = ioport__read8(data); 9846aa8d69SPekka Enberg break; 9946aa8d69SPekka Enberg case LCR: 10046aa8d69SPekka Enberg device.lcr = ioport__read8(data); 10146aa8d69SPekka Enberg break; 10246aa8d69SPekka Enberg default: 10346aa8d69SPekka Enberg return false; 10446aa8d69SPekka Enberg } 10546aa8d69SPekka Enberg } else { 10646aa8d69SPekka Enberg switch (offset) { 10746aa8d69SPekka Enberg case THR: { 10813a7760fSPekka Enberg char *p = data; 109f2d8dc88SCyrill Gorcunov int i; 11013a7760fSPekka Enberg 111f2d8dc88SCyrill Gorcunov while (count--) { 112f2d8dc88SCyrill Gorcunov for (i = 0; i < size; i++) 113b7475544SPekka Enberg fprintf(stdout, "%c", *p++); 1145b9d0b58SAsias He } 115b7475544SPekka Enberg fflush(stdout); 116*e557eef9SPekka Enberg 117*e557eef9SPekka Enberg device.iir |= UART_IIR_NO_INT; 118*e557eef9SPekka Enberg kvm__irq_line(self, device.irq, 0); 119*e557eef9SPekka Enberg 12046aa8d69SPekka Enberg break; 12146aa8d69SPekka Enberg } 12246aa8d69SPekka Enberg case IER: 12346aa8d69SPekka Enberg device.ier = ioport__read8(data); 12446aa8d69SPekka Enberg break; 12546aa8d69SPekka Enberg case FCR: 12646aa8d69SPekka Enberg device.fcr = ioport__read8(data); 12746aa8d69SPekka Enberg break; 12846aa8d69SPekka Enberg case LCR: 12946aa8d69SPekka Enberg device.lcr = ioport__read8(data); 13046aa8d69SPekka Enberg break; 13146aa8d69SPekka Enberg case MCR: 13246aa8d69SPekka Enberg device.mcr = ioport__read8(data); 13346aa8d69SPekka Enberg break; 13446aa8d69SPekka Enberg case SCR: 13546aa8d69SPekka Enberg device.scr = ioport__read8(data); 13646aa8d69SPekka Enberg break; 13746aa8d69SPekka Enberg default: 13846aa8d69SPekka Enberg return false; 13946aa8d69SPekka Enberg } 14046aa8d69SPekka Enberg } 14113a7760fSPekka Enberg 14213a7760fSPekka Enberg return true; 14313a7760fSPekka Enberg } 14413a7760fSPekka Enberg 14546aa8d69SPekka Enberg static bool serial8250_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count) 14625af6674SPekka Enberg { 14746aa8d69SPekka Enberg uint16_t offset = port - device.iobase; 14846aa8d69SPekka Enberg 1499a7428ddSPekka Enberg if (device.lcr & UART_LCR_DLAB) 15046aa8d69SPekka Enberg return false; 15146aa8d69SPekka Enberg 15246aa8d69SPekka Enberg switch (offset) { 15346aa8d69SPekka Enberg case THR: 154900d32c1SPekka Enberg /* TODO: input support */ 15546aa8d69SPekka Enberg break; 15646aa8d69SPekka Enberg case IER: 15746aa8d69SPekka Enberg ioport__write8(data, device.ier); 15846aa8d69SPekka Enberg break; 15946aa8d69SPekka Enberg case IIR: 160*e557eef9SPekka Enberg ioport__write8(data, device.iir); 16146aa8d69SPekka Enberg break; 16246aa8d69SPekka Enberg case LCR: 16346aa8d69SPekka Enberg ioport__write8(data, device.lcr); 16446aa8d69SPekka Enberg break; 16546aa8d69SPekka Enberg case MCR: 16646aa8d69SPekka Enberg ioport__write8(data, device.mcr); 16746aa8d69SPekka Enberg break; 16846aa8d69SPekka Enberg case LSR: 1699a7428ddSPekka Enberg ioport__write8(data, UART_LSR_THRE); 17046aa8d69SPekka Enberg break; 17146aa8d69SPekka Enberg case MSR: 1729a7428ddSPekka Enberg ioport__write8(data, UART_MSR_CTS); 17346aa8d69SPekka Enberg break; 17446aa8d69SPekka Enberg case SCR: 17546aa8d69SPekka Enberg ioport__write8(data, device.scr); 17646aa8d69SPekka Enberg break; 17746aa8d69SPekka Enberg default: 17846aa8d69SPekka Enberg return false; 17925af6674SPekka Enberg } 18025af6674SPekka Enberg 18113a7760fSPekka Enberg return true; 18213a7760fSPekka Enberg } 18313a7760fSPekka Enberg 18446aa8d69SPekka Enberg static struct ioport_operations serial8250_ops = { 18546aa8d69SPekka Enberg .io_in = serial8250_in, 18646aa8d69SPekka Enberg .io_out = serial8250_out, 187a93ec68bSPekka Enberg }; 188a93ec68bSPekka Enberg 189899fe063SPekka Enberg void serial8250__init(void) 19013a7760fSPekka Enberg { 19146aa8d69SPekka Enberg ioport__register(device.iobase, &serial8250_ops, 8); 19213a7760fSPekka Enberg } 193