1*d09ecd8cSThomas Huth /* 2*d09ecd8cSThomas Huth * QEMU 16550A multi UART emulation 3*d09ecd8cSThomas Huth * 4*d09ecd8cSThomas Huth * SPDX-License-Identifier: MIT 5*d09ecd8cSThomas Huth * 6*d09ecd8cSThomas Huth * Copyright (c) 2003-2004 Fabrice Bellard 7*d09ecd8cSThomas Huth * Copyright (c) 2008 Citrix Systems, Inc. 8*d09ecd8cSThomas Huth * 9*d09ecd8cSThomas Huth * Permission is hereby granted, free of charge, to any person obtaining a copy 10*d09ecd8cSThomas Huth * of this software and associated documentation files (the "Software"), to deal 11*d09ecd8cSThomas Huth * in the Software without restriction, including without limitation the rights 12*d09ecd8cSThomas Huth * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13*d09ecd8cSThomas Huth * copies of the Software, and to permit persons to whom the Software is 14*d09ecd8cSThomas Huth * furnished to do so, subject to the following conditions: 15*d09ecd8cSThomas Huth * 16*d09ecd8cSThomas Huth * The above copyright notice and this permission notice shall be included in 17*d09ecd8cSThomas Huth * all copies or substantial portions of the Software. 18*d09ecd8cSThomas Huth * 19*d09ecd8cSThomas Huth * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20*d09ecd8cSThomas Huth * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21*d09ecd8cSThomas Huth * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 22*d09ecd8cSThomas Huth * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23*d09ecd8cSThomas Huth * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24*d09ecd8cSThomas Huth * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25*d09ecd8cSThomas Huth * THE SOFTWARE. 26*d09ecd8cSThomas Huth */ 27*d09ecd8cSThomas Huth 28*d09ecd8cSThomas Huth /* see docs/specs/pci-serial.txt */ 29*d09ecd8cSThomas Huth 30*d09ecd8cSThomas Huth #include "qemu/osdep.h" 31*d09ecd8cSThomas Huth #include "qapi/error.h" 32*d09ecd8cSThomas Huth #include "hw/char/serial.h" 33*d09ecd8cSThomas Huth #include "hw/pci/pci.h" 34*d09ecd8cSThomas Huth 35*d09ecd8cSThomas Huth #define PCI_SERIAL_MAX_PORTS 4 36*d09ecd8cSThomas Huth 37*d09ecd8cSThomas Huth typedef struct PCIMultiSerialState { 38*d09ecd8cSThomas Huth PCIDevice dev; 39*d09ecd8cSThomas Huth MemoryRegion iobar; 40*d09ecd8cSThomas Huth uint32_t ports; 41*d09ecd8cSThomas Huth char *name[PCI_SERIAL_MAX_PORTS]; 42*d09ecd8cSThomas Huth SerialState state[PCI_SERIAL_MAX_PORTS]; 43*d09ecd8cSThomas Huth uint32_t level[PCI_SERIAL_MAX_PORTS]; 44*d09ecd8cSThomas Huth qemu_irq *irqs; 45*d09ecd8cSThomas Huth uint8_t prog_if; 46*d09ecd8cSThomas Huth } PCIMultiSerialState; 47*d09ecd8cSThomas Huth 48*d09ecd8cSThomas Huth static void multi_serial_pci_exit(PCIDevice *dev) 49*d09ecd8cSThomas Huth { 50*d09ecd8cSThomas Huth PCIMultiSerialState *pci = DO_UPCAST(PCIMultiSerialState, dev, dev); 51*d09ecd8cSThomas Huth SerialState *s; 52*d09ecd8cSThomas Huth int i; 53*d09ecd8cSThomas Huth 54*d09ecd8cSThomas Huth for (i = 0; i < pci->ports; i++) { 55*d09ecd8cSThomas Huth s = pci->state + i; 56*d09ecd8cSThomas Huth serial_exit_core(s); 57*d09ecd8cSThomas Huth memory_region_del_subregion(&pci->iobar, &s->io); 58*d09ecd8cSThomas Huth g_free(pci->name[i]); 59*d09ecd8cSThomas Huth } 60*d09ecd8cSThomas Huth qemu_free_irqs(pci->irqs, pci->ports); 61*d09ecd8cSThomas Huth } 62*d09ecd8cSThomas Huth 63*d09ecd8cSThomas Huth static void multi_serial_irq_mux(void *opaque, int n, int level) 64*d09ecd8cSThomas Huth { 65*d09ecd8cSThomas Huth PCIMultiSerialState *pci = opaque; 66*d09ecd8cSThomas Huth int i, pending = 0; 67*d09ecd8cSThomas Huth 68*d09ecd8cSThomas Huth pci->level[n] = level; 69*d09ecd8cSThomas Huth for (i = 0; i < pci->ports; i++) { 70*d09ecd8cSThomas Huth if (pci->level[i]) { 71*d09ecd8cSThomas Huth pending = 1; 72*d09ecd8cSThomas Huth } 73*d09ecd8cSThomas Huth } 74*d09ecd8cSThomas Huth pci_set_irq(&pci->dev, pending); 75*d09ecd8cSThomas Huth } 76*d09ecd8cSThomas Huth 77*d09ecd8cSThomas Huth static void multi_serial_pci_realize(PCIDevice *dev, Error **errp) 78*d09ecd8cSThomas Huth { 79*d09ecd8cSThomas Huth PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev); 80*d09ecd8cSThomas Huth PCIMultiSerialState *pci = DO_UPCAST(PCIMultiSerialState, dev, dev); 81*d09ecd8cSThomas Huth SerialState *s; 82*d09ecd8cSThomas Huth Error *err = NULL; 83*d09ecd8cSThomas Huth int i, nr_ports = 0; 84*d09ecd8cSThomas Huth 85*d09ecd8cSThomas Huth switch (pc->device_id) { 86*d09ecd8cSThomas Huth case 0x0003: 87*d09ecd8cSThomas Huth nr_ports = 2; 88*d09ecd8cSThomas Huth break; 89*d09ecd8cSThomas Huth case 0x0004: 90*d09ecd8cSThomas Huth nr_ports = 4; 91*d09ecd8cSThomas Huth break; 92*d09ecd8cSThomas Huth } 93*d09ecd8cSThomas Huth assert(nr_ports > 0); 94*d09ecd8cSThomas Huth assert(nr_ports <= PCI_SERIAL_MAX_PORTS); 95*d09ecd8cSThomas Huth 96*d09ecd8cSThomas Huth pci->dev.config[PCI_CLASS_PROG] = pci->prog_if; 97*d09ecd8cSThomas Huth pci->dev.config[PCI_INTERRUPT_PIN] = 0x01; 98*d09ecd8cSThomas Huth memory_region_init(&pci->iobar, OBJECT(pci), "multiserial", 8 * nr_ports); 99*d09ecd8cSThomas Huth pci_register_bar(&pci->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &pci->iobar); 100*d09ecd8cSThomas Huth pci->irqs = qemu_allocate_irqs(multi_serial_irq_mux, pci, 101*d09ecd8cSThomas Huth nr_ports); 102*d09ecd8cSThomas Huth 103*d09ecd8cSThomas Huth for (i = 0; i < nr_ports; i++) { 104*d09ecd8cSThomas Huth s = pci->state + i; 105*d09ecd8cSThomas Huth s->baudbase = 115200; 106*d09ecd8cSThomas Huth serial_realize_core(s, &err); 107*d09ecd8cSThomas Huth if (err != NULL) { 108*d09ecd8cSThomas Huth error_propagate(errp, err); 109*d09ecd8cSThomas Huth multi_serial_pci_exit(dev); 110*d09ecd8cSThomas Huth return; 111*d09ecd8cSThomas Huth } 112*d09ecd8cSThomas Huth s->irq = pci->irqs[i]; 113*d09ecd8cSThomas Huth pci->name[i] = g_strdup_printf("uart #%d", i + 1); 114*d09ecd8cSThomas Huth memory_region_init_io(&s->io, OBJECT(pci), &serial_io_ops, s, 115*d09ecd8cSThomas Huth pci->name[i], 8); 116*d09ecd8cSThomas Huth memory_region_add_subregion(&pci->iobar, 8 * i, &s->io); 117*d09ecd8cSThomas Huth pci->ports++; 118*d09ecd8cSThomas Huth } 119*d09ecd8cSThomas Huth } 120*d09ecd8cSThomas Huth 121*d09ecd8cSThomas Huth static const VMStateDescription vmstate_pci_multi_serial = { 122*d09ecd8cSThomas Huth .name = "pci-serial-multi", 123*d09ecd8cSThomas Huth .version_id = 1, 124*d09ecd8cSThomas Huth .minimum_version_id = 1, 125*d09ecd8cSThomas Huth .fields = (VMStateField[]) { 126*d09ecd8cSThomas Huth VMSTATE_PCI_DEVICE(dev, PCIMultiSerialState), 127*d09ecd8cSThomas Huth VMSTATE_STRUCT_ARRAY(state, PCIMultiSerialState, PCI_SERIAL_MAX_PORTS, 128*d09ecd8cSThomas Huth 0, vmstate_serial, SerialState), 129*d09ecd8cSThomas Huth VMSTATE_UINT32_ARRAY(level, PCIMultiSerialState, PCI_SERIAL_MAX_PORTS), 130*d09ecd8cSThomas Huth VMSTATE_END_OF_LIST() 131*d09ecd8cSThomas Huth } 132*d09ecd8cSThomas Huth }; 133*d09ecd8cSThomas Huth 134*d09ecd8cSThomas Huth static Property multi_2x_serial_pci_properties[] = { 135*d09ecd8cSThomas Huth DEFINE_PROP_CHR("chardev1", PCIMultiSerialState, state[0].chr), 136*d09ecd8cSThomas Huth DEFINE_PROP_CHR("chardev2", PCIMultiSerialState, state[1].chr), 137*d09ecd8cSThomas Huth DEFINE_PROP_UINT8("prog_if", PCIMultiSerialState, prog_if, 0x02), 138*d09ecd8cSThomas Huth DEFINE_PROP_END_OF_LIST(), 139*d09ecd8cSThomas Huth }; 140*d09ecd8cSThomas Huth 141*d09ecd8cSThomas Huth static Property multi_4x_serial_pci_properties[] = { 142*d09ecd8cSThomas Huth DEFINE_PROP_CHR("chardev1", PCIMultiSerialState, state[0].chr), 143*d09ecd8cSThomas Huth DEFINE_PROP_CHR("chardev2", PCIMultiSerialState, state[1].chr), 144*d09ecd8cSThomas Huth DEFINE_PROP_CHR("chardev3", PCIMultiSerialState, state[2].chr), 145*d09ecd8cSThomas Huth DEFINE_PROP_CHR("chardev4", PCIMultiSerialState, state[3].chr), 146*d09ecd8cSThomas Huth DEFINE_PROP_UINT8("prog_if", PCIMultiSerialState, prog_if, 0x02), 147*d09ecd8cSThomas Huth DEFINE_PROP_END_OF_LIST(), 148*d09ecd8cSThomas Huth }; 149*d09ecd8cSThomas Huth 150*d09ecd8cSThomas Huth static void multi_2x_serial_pci_class_initfn(ObjectClass *klass, void *data) 151*d09ecd8cSThomas Huth { 152*d09ecd8cSThomas Huth DeviceClass *dc = DEVICE_CLASS(klass); 153*d09ecd8cSThomas Huth PCIDeviceClass *pc = PCI_DEVICE_CLASS(klass); 154*d09ecd8cSThomas Huth pc->realize = multi_serial_pci_realize; 155*d09ecd8cSThomas Huth pc->exit = multi_serial_pci_exit; 156*d09ecd8cSThomas Huth pc->vendor_id = PCI_VENDOR_ID_REDHAT; 157*d09ecd8cSThomas Huth pc->device_id = PCI_DEVICE_ID_REDHAT_SERIAL2; 158*d09ecd8cSThomas Huth pc->revision = 1; 159*d09ecd8cSThomas Huth pc->class_id = PCI_CLASS_COMMUNICATION_SERIAL; 160*d09ecd8cSThomas Huth dc->vmsd = &vmstate_pci_multi_serial; 161*d09ecd8cSThomas Huth dc->props = multi_2x_serial_pci_properties; 162*d09ecd8cSThomas Huth set_bit(DEVICE_CATEGORY_INPUT, dc->categories); 163*d09ecd8cSThomas Huth } 164*d09ecd8cSThomas Huth 165*d09ecd8cSThomas Huth static void multi_4x_serial_pci_class_initfn(ObjectClass *klass, void *data) 166*d09ecd8cSThomas Huth { 167*d09ecd8cSThomas Huth DeviceClass *dc = DEVICE_CLASS(klass); 168*d09ecd8cSThomas Huth PCIDeviceClass *pc = PCI_DEVICE_CLASS(klass); 169*d09ecd8cSThomas Huth pc->realize = multi_serial_pci_realize; 170*d09ecd8cSThomas Huth pc->exit = multi_serial_pci_exit; 171*d09ecd8cSThomas Huth pc->vendor_id = PCI_VENDOR_ID_REDHAT; 172*d09ecd8cSThomas Huth pc->device_id = PCI_DEVICE_ID_REDHAT_SERIAL4; 173*d09ecd8cSThomas Huth pc->revision = 1; 174*d09ecd8cSThomas Huth pc->class_id = PCI_CLASS_COMMUNICATION_SERIAL; 175*d09ecd8cSThomas Huth dc->vmsd = &vmstate_pci_multi_serial; 176*d09ecd8cSThomas Huth dc->props = multi_4x_serial_pci_properties; 177*d09ecd8cSThomas Huth set_bit(DEVICE_CATEGORY_INPUT, dc->categories); 178*d09ecd8cSThomas Huth } 179*d09ecd8cSThomas Huth 180*d09ecd8cSThomas Huth static const TypeInfo multi_2x_serial_pci_info = { 181*d09ecd8cSThomas Huth .name = "pci-serial-2x", 182*d09ecd8cSThomas Huth .parent = TYPE_PCI_DEVICE, 183*d09ecd8cSThomas Huth .instance_size = sizeof(PCIMultiSerialState), 184*d09ecd8cSThomas Huth .class_init = multi_2x_serial_pci_class_initfn, 185*d09ecd8cSThomas Huth .interfaces = (InterfaceInfo[]) { 186*d09ecd8cSThomas Huth { INTERFACE_CONVENTIONAL_PCI_DEVICE }, 187*d09ecd8cSThomas Huth { }, 188*d09ecd8cSThomas Huth }, 189*d09ecd8cSThomas Huth }; 190*d09ecd8cSThomas Huth 191*d09ecd8cSThomas Huth static const TypeInfo multi_4x_serial_pci_info = { 192*d09ecd8cSThomas Huth .name = "pci-serial-4x", 193*d09ecd8cSThomas Huth .parent = TYPE_PCI_DEVICE, 194*d09ecd8cSThomas Huth .instance_size = sizeof(PCIMultiSerialState), 195*d09ecd8cSThomas Huth .class_init = multi_4x_serial_pci_class_initfn, 196*d09ecd8cSThomas Huth .interfaces = (InterfaceInfo[]) { 197*d09ecd8cSThomas Huth { INTERFACE_CONVENTIONAL_PCI_DEVICE }, 198*d09ecd8cSThomas Huth { }, 199*d09ecd8cSThomas Huth }, 200*d09ecd8cSThomas Huth }; 201*d09ecd8cSThomas Huth 202*d09ecd8cSThomas Huth static void multi_serial_pci_register_types(void) 203*d09ecd8cSThomas Huth { 204*d09ecd8cSThomas Huth type_register_static(&multi_2x_serial_pci_info); 205*d09ecd8cSThomas Huth type_register_static(&multi_4x_serial_pci_info); 206*d09ecd8cSThomas Huth } 207*d09ecd8cSThomas Huth 208*d09ecd8cSThomas Huth type_init(multi_serial_pci_register_types) 209