* [PATCH 0/7] NS 87560 SuperIO chip
@ 2026-09-17 21:33 Helge Deller
2026-09-17 21:33 ` [PATCH 1/7] hw/pci/pci_ids: Add PCI vendor ID and IDs for NS chips Helge Deller
` (6 more replies)
0 siblings, 7 replies; 8+ messages in thread
From: Helge Deller @ 2026-09-17 21:33 UTC (permalink / raw)
To: qemu-devel
Cc: Michael S. Tsirkin, Denis V. Lunev, Helge Deller, John Snow,
Richard Henderson, Paolo Bonzini, Abizer Lokhandwala, qemu-block
From: Helge Deller <deller@gmx.de>
This series adds the emulation of a "NS 87560 SuperIO chip".
The SuperIO chip includes a floppy controller, two serial ports, one parallel
port, an IDE controller and one OHCI USB port.
This National Semiconductor SuperIO chip was commonly used in many HP PA-RISC
B- and C-class workstations, as in the HP C3700 machine which is being
emulated by QEMU already. The last patch wires up this SuperIO driver to the
existing C3700 machine emulation.
This driver was developed during the last few months by Abizer Lokhandwala
<abizerlokhandwalastd10@gmail.com> and mentored and reviewed by me.
Originally Abizer applied for the SCSI TAPE emulation program which I offered
for the Google-Summer-of-Code 2026 program, but since the TAPE project was
dropped by Google, he instead stepped up to develop this driver - even without
receiving sponsoring by Google.
So, I'm very grateful that he nevertheless invested his spare time to develop
this driver!
I've already reviewed the patches off-list, but it would be great if
other people could review the code as well.
Helge
Abizer Lokhandwala (6):
hw/isa/pc87560: Add main isa code for NS 87560 SuperIO chip
hw/ide/pc87560-ide: Add IDE emulation of NS 87560 SuperIO chip
hw/usb/hcd-ohci: Add big endian and consistency check support
hw/usb/pc87560-ohci: Add USB OHCI emulation of NS 87560 SuperIO chip
hw/pc87560: Wire up emulation of NS 87560 SuperIO chip
hw/hppa: Add NS 87560 SuperIO chip to HP C3700 machine
Helge Deller (1):
hw/pci/pci_ids: Add PCI vendor ID and IDs for NS chips
MAINTAINERS | 4 +
hw/hppa/machine.c | 41 +++-
hw/ide/meson.build | 1 +
hw/ide/pc87560-ide.c | 358 +++++++++++++++++++++++++++
hw/ide/trace-events | 4 +
hw/isa/Kconfig | 11 +
hw/isa/meson.build | 1 +
hw/isa/pc87560.c | 510 +++++++++++++++++++++++++++++++++++++++
hw/usb/hcd-ohci.c | 35 +++
hw/usb/hcd-ohci.h | 5 +
hw/usb/meson.build | 1 +
hw/usb/pc87560-ohci.c | 355 +++++++++++++++++++++++++++
include/hw/isa/pc87560.h | 96 ++++++++
include/hw/pci/pci_ids.h | 5 +
14 files changed, 1420 insertions(+), 7 deletions(-)
create mode 100644 hw/ide/pc87560-ide.c
create mode 100644 hw/isa/pc87560.c
create mode 100644 hw/usb/pc87560-ohci.c
create mode 100644 include/hw/isa/pc87560.h
--
2.54.0
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 1/7] hw/pci/pci_ids: Add PCI vendor ID and IDs for NS chips
2026-09-17 21:33 [PATCH 0/7] NS 87560 SuperIO chip Helge Deller
@ 2026-09-17 21:33 ` Helge Deller
2026-09-17 21:33 ` [PATCH 2/7] hw/isa/pc87560: Add main isa code for NS 87560 SuperIO chip Helge Deller
` (5 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Helge Deller @ 2026-09-17 21:33 UTC (permalink / raw)
To: qemu-devel
Cc: Michael S. Tsirkin, Denis V. Lunev, Helge Deller, John Snow,
Richard Henderson, Paolo Bonzini, Abizer Lokhandwala, qemu-block
From: Helge Deller <deller@gmx.de>
Some HP B- and C-class workstations used a PCI SuperIO chip from National
Semiconductor for USB, serial, parallel and FDC. Add the relevant PCI IDs, so
that the QEMU driver can reuse the constants.
Signed-off-by: Helge Deller <deller@gmx.de>
---
include/hw/pci/pci_ids.h | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/include/hw/pci/pci_ids.h b/include/hw/pci/pci_ids.h
index 16034aaa2c..477609ed62 100644
--- a/include/hw/pci/pci_ids.h
+++ b/include/hw/pci/pci_ids.h
@@ -168,6 +168,11 @@
#define PCI_DEVICE_ID_LSI_SAS1078 0x0060
#define PCI_DEVICE_ID_LSI_SAS0079 0x0079
+#define PCI_VENDOR_ID_NS 0x100b
+#define PCI_DEVICE_ID_NS_87415 0x0002
+#define PCI_DEVICE_ID_NS_87560_LIO 0x000e
+#define PCI_DEVICE_ID_NS_87560_USB 0x0012
+
#define PCI_VENDOR_ID_DEC 0x1011
#define PCI_DEVICE_ID_DEC_21143 0x0019
--
2.54.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 2/7] hw/isa/pc87560: Add main isa code for NS 87560 SuperIO chip
2026-09-17 21:33 [PATCH 0/7] NS 87560 SuperIO chip Helge Deller
2026-09-17 21:33 ` [PATCH 1/7] hw/pci/pci_ids: Add PCI vendor ID and IDs for NS chips Helge Deller
@ 2026-09-17 21:33 ` Helge Deller
2026-09-17 21:33 ` [PATCH 3/7] hw/ide/pc87560-ide: Add IDE emulation of " Helge Deller
` (4 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Helge Deller @ 2026-09-17 21:33 UTC (permalink / raw)
To: qemu-devel
Cc: Michael S. Tsirkin, Denis V. Lunev, Helge Deller, John Snow,
Richard Henderson, Paolo Bonzini, Abizer Lokhandwala, qemu-block
From: Abizer Lokhandwala <abizerlokhandwalastd10@gmail.com>
Add emulation of the core components (PCI function 1 aka I/O
Peripherals) of the National Semiconductor SuperIO chip which was
commonly used in HP PA-RISC B- and C-class workstations. This includes
the PIC interrupt controller, the floppy controller, serial and parallel
ports.
Signed-off-by: Abizer Lokhandwala <abizerlokhandwalastd10@gmail.com>
Signed-off-by: Helge Deller <deller@gmx.de>
---
MAINTAINERS | 2 +
hw/isa/Kconfig | 11 +
hw/isa/pc87560.c | 510 +++++++++++++++++++++++++++++++++++++++
include/hw/isa/pc87560.h | 96 ++++++++
4 files changed, 619 insertions(+)
create mode 100644 hw/isa/pc87560.c
create mode 100644 include/hw/isa/pc87560.h
diff --git a/MAINTAINERS b/MAINTAINERS
index e9b68c51f1..e3b1e80ad6 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1355,6 +1355,8 @@ F: include/hw/pci-host/dino.h
F: pc-bios/hppa-firmware.img
F: roms/seabios-hppa/
F: tests/functional/hppa/
+F: hw/isa/pc87560.c
+F: include/hw/isa/pc87560.h
Hexagon Machines
----------------
diff --git a/hw/isa/Kconfig b/hw/isa/Kconfig
index 73c6470805..23d327e88c 100644
--- a/hw/isa/Kconfig
+++ b/hw/isa/Kconfig
@@ -67,6 +67,17 @@ config SMC37C669
bool
select ISA_SUPERIO
+config PC87560
+ bool
+ default y if PCI_DEVICES
+ depends on PCI
+ depends on HPPA
+ select ISA_BUS
+ select FDC_ISA
+ select SERIAL_ISA
+ select IDE_PCI
+ select USB_OHCI
+
config LPC_ICH9
bool
# For historical reasons, SuperIO devices are created in the board
diff --git a/hw/isa/pc87560.c b/hw/isa/pc87560.c
new file mode 100644
index 0000000000..c13c54deb2
--- /dev/null
+++ b/hw/isa/pc87560.c
@@ -0,0 +1,510 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * NS PC87560 SuperI/O — ISA emulation
+ *
+ * Copyright (c) 2026 Abizer abizerlokhandwalastd10@gmail.com
+ */
+#include "qemu/osdep.h"
+#include "hw/pci/pci.h"
+#include "hw/pci/pci_device.h"
+#include "qapi/error.h"
+#include "qemu/log.h"
+#include "migration/vmstate.h"
+#include "qemu/module.h"
+#include "hw/core/irq.h"
+#include "hw/core/qdev-properties.h"
+#include "hw/core/qdev-properties-system.h"
+#include "hw/char/serial.h"
+#include "hw/char/parallel.h"
+#include "hw/block/fdc-internal.h"
+#include "hw/isa/pc87560.h"
+
+static uint64_t pc87560_pp_read(void *opaque, hwaddr addr, unsigned size)
+{
+ ParallelState *s = opaque;
+ switch (addr) {
+ case 0: return s->datar;
+ case 1: return s->status;
+ case 2: return s->control;
+ default:
+ qemu_log_mask(LOG_UNIMP,
+ "pc87560-pp: read offset 0x%" HWADDR_PRIx "\n", addr);
+ return 0xFF;
+ }
+}
+
+static int pic_highest_priority(int priority_base, uint8_t mask)
+{
+ int i;
+ for (i = 1; i <= 8; i++) {
+ int irq = (priority_base + i) & 7;
+ if (mask & (1 << irq)) {
+ return irq;
+ }
+ }
+ return -1;
+}
+
+static void pic_update_irq(PC87560SuperioState *s)
+{
+ int base = s->pic.priority_base;
+ int first_irr = pic_highest_priority(base, s->pic.irr & ~s->pic.imr);
+ int first_isr = pic_highest_priority(base, s->pic.isr);
+ int pending = 0;
+
+ if (first_irr != -1) {
+ if (first_isr == -1) {
+ pending = 1;
+ } else if (first_irr != first_isr) {
+ int irr_lvl = (first_irr - s->pic.priority_base - 1) & 7;
+ int isr_lvl = (first_isr - s->pic.priority_base - 1) & 7;
+ if (irr_lvl < isr_lvl) {
+ pending = 1;
+ }
+ }
+ }
+
+ if (pending != s->pic.irq_level) {
+ s->pic.irq_level = pending;
+ qemu_set_irq(s->parent_irq, pending);
+ }
+}
+
+static uint64_t pic1_read(void *opaque, hwaddr addr, unsigned size)
+{
+ PC87560SuperioState *s = opaque;
+ if (addr == 0) {
+ if (s->pic.poll_mode) {
+ s->pic.poll_mode = false;
+ uint8_t pending_mask = s->pic.irr & ~s->pic.imr;
+ int irq = pic_highest_priority(s->pic.priority_base, pending_mask);
+ if (irq == -1) {
+ pic_update_irq(s);
+ return 0x00;
+ }
+ s->pic.isr |= (1 << irq);
+ pic_update_irq(s);
+ return 0x80 | irq;
+ }
+ return s->pic.read_isr ? s->pic.isr : s->pic.irr;
+ }
+ return s->pic.imr;
+}
+
+static void pic1_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+ PC87560SuperioState *s = opaque;
+ uint8_t v = (uint8_t)val;
+
+ if (addr == 1) {
+ if (s->pic.init_phase > 0) {
+ if (s->pic.init_phase == 3) {
+ s->pic.init_phase = 0;
+ } else {
+ s->pic.init_phase++;
+ }
+ } else {
+ s->pic.imr = v;
+ pic_update_irq(s);
+ }
+ return;
+ }
+
+ if (v & 0x10) {
+ s->pic.init_phase = 1;
+ s->pic.imr = 0xFF;
+ s->pic.irr = 0;
+ s->pic.isr = 0;
+ s->pic.poll_mode = false;
+ s->pic.priority_base = 7;
+ } else if (v & 0x08) {
+ if (v & 0x04) {
+ s->pic.poll_mode = true;
+ }
+ if (v & 0x02) {
+ s->pic.read_isr = (v & 0x01);
+ }
+ } else {
+ uint8_t cmd = (v >> 5) & 0x07;
+ uint8_t irq = v & 0x07;
+ switch (cmd) {
+ case 1:
+ case 5: {
+ int serviced = pic_highest_priority(s->pic.priority_base,
+ s->pic.isr);
+ if (serviced != -1) {
+ s->pic.isr &= ~(1 << serviced);
+ if (cmd == 5) {
+ s->pic.priority_base = serviced;
+ }
+ }
+ break;
+ }
+ case 3:
+ case 7:
+ s->pic.isr &= ~(1 << irq);
+ if (cmd == 7) {
+ s->pic.priority_base = irq;
+ }
+ break;
+ case 6:
+ s->pic.priority_base = irq;
+ break;
+ }
+ pic_update_irq(s);
+ }
+}
+
+static const MemoryRegionOps pc87560_pic1_ops = {
+ .read = pic1_read,
+ .write = pic1_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid.min_access_size = 1,
+ .valid.max_access_size = 1,
+};
+
+static void pc87560_pic_irq_in(void *opaque, int irq_n, int level)
+{
+ PC87560SuperioState *s = opaque;
+ if (level) {
+ s->pic.irr |= (1 << irq_n);
+ } else {
+ s->pic.irr &= ~(1 << irq_n);
+ }
+ pic_update_irq(s);
+}
+
+
+static void pc87560_pp_write(void *opaque, hwaddr addr, uint64_t val,
+ unsigned size)
+{
+ ParallelState *s = opaque;
+ switch (addr) {
+ case 0:
+ s->dataw = (uint8_t)val;
+ if (qemu_chr_fe_backend_connected(&s->chr)) {
+ unsigned char ch = (uint8_t)val;
+ qemu_chr_fe_write_all(&s->chr, &ch, 1);
+ }
+ break;
+ case 2:
+ s->control = (uint8_t)(val & 0x1F);
+ if (!(s->control & 0x10)) {
+ qemu_irq_lower(s->irq);
+ }
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP,
+ "pc87560-pp: write offset 0x%" HWADDR_PRIx "\n", addr);
+ }
+}
+static const MemoryRegionOps pc87560_pp_ops = {
+ .read = pc87560_pp_read,
+ .write = pc87560_pp_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid.min_access_size = 1,
+ .valid.max_access_size = 1,
+};
+
+/*
+ * Some features on the chip are disabled by default. If a guest
+ * reads or writes one of those registers, respond via this stub
+ * instead of faulting.
+ */
+static uint64_t pc87560_stub_read(void *opaque, hwaddr addr, unsigned size)
+{
+ qemu_log_mask(LOG_UNIMP,
+ "pc87560-superio: stub read 0x%" HWADDR_PRIx "\n", addr);
+ return 0xFF;
+}
+static void pc87560_stub_write(void *opaque, hwaddr addr, uint64_t val,
+ unsigned size)
+{
+ qemu_log_mask(LOG_UNIMP,
+ "pc87560-superio: stub write 0x%" HWADDR_PRIx
+ " = 0x%" PRIx64 "\n", addr, val);
+}
+
+static const MemoryRegionOps pc87560_stub_ops = {
+ .read = pc87560_stub_read,
+ .write = pc87560_stub_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+
+static uint64_t pc87560_fdc_read(void *opaque, hwaddr addr, unsigned size)
+{
+ return fdctrl_read(opaque, (uint32_t)addr);
+}
+
+static void pc87560_fdc_write(void *opaque, hwaddr addr, uint64_t val,
+ unsigned size)
+{
+ fdctrl_write(opaque, (uint32_t)addr, (uint32_t)val);
+}
+
+static const MemoryRegionOps pc87560_fdc_ops = {
+ .read = pc87560_fdc_read,
+ .write = pc87560_fdc_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid.min_access_size = 1,
+ .valid.max_access_size = 1,
+};
+
+static void pc87560_map_region(PC87560SuperioState *s, MemoryRegion *mr,
+ bool *mapped, hwaddr base)
+{
+ MemoryRegion *io = pci_address_space_io(PCI_DEVICE(s));
+ if (*mapped) {
+ memory_region_del_subregion(io, mr);
+ *mapped = false;
+ }
+ if (base) {
+ memory_region_add_subregion(io, base, mr);
+ *mapped = true;
+ }
+}
+
+static void pc87560_apply_funcen(PC87560SuperioState *s)
+{
+ PCIDevice *pci = PCI_DEVICE(s);
+ uint16_t en = pci_get_word(pci->config + REG_FUNCEN);
+
+ pc87560_map_region(s, &s->fdc_io, &s->fdc_mapped, (en & FUNCEN_FDC)
+ ? (pci_get_long(pci->config + REG_FDCBAR) & ~(hwaddr)0x7) : 0);
+
+ pc87560_map_region(s, &s->sp1_io, &s->sp1_mapped, (en & FUNCEN_SP1)
+ ? (pci_get_long(pci->config + REG_SP1BAR) & ~(hwaddr)0x7) : 0);
+
+ pc87560_map_region(s, &s->sp2_io, &s->sp2_mapped, (en & FUNCEN_SP2)
+ ? (pci_get_long(pci->config + REG_SP2BAR) & ~(hwaddr)0x7) : 0);
+
+ pc87560_map_region(s, &s->pp_io, &s->pp_mapped, (en & FUNCEN_PP)
+ ? (pci_get_long(pci->config + REG_PPBAR) & ~(hwaddr)0x7) : 0);
+
+ pc87560_map_region(s, &s->kbc_io, &s->kbc_mapped, (en & FUNCEN_KBC)
+ ? (pci_get_long(pci->config + REG_KBCBAR) & ~(hwaddr)0x7) : 0);
+}
+
+static void pc87560_superio_config_write(PCIDevice *pci,
+ uint32_t addr, uint32_t val, int len)
+{
+ PC87560SuperioState *s = PC87560_Superio(pci);
+ uint16_t funcen;
+
+ pci_default_write_config(pci, addr, val, len);
+ funcen = pci_get_word(pci->config + REG_FUNCEN);
+
+ if (ranges_overlap(addr, len, REG_FUNCEN, 2)) {
+ pc87560_apply_funcen(s);
+ }
+
+ if (ranges_overlap(addr, len, REG_FDCBAR, 4)) {
+ if (funcen & FUNCEN_FDC) {
+ pc87560_map_region(s, &s->fdc_io, &s->fdc_mapped,
+ pci_get_long(pci->config + REG_FDCBAR) & ~(hwaddr)0x7);
+ }
+ }
+
+ if (ranges_overlap(addr, len, REG_SP1BAR, 4)) {
+ if (funcen & FUNCEN_SP1) {
+ pc87560_map_region(s, &s->sp1_io, &s->sp1_mapped,
+ pci_get_long(pci->config + REG_SP1BAR) & ~(hwaddr)0x7);
+ }
+ }
+
+ if (ranges_overlap(addr, len, REG_SP2BAR, 4)) {
+ if (funcen & FUNCEN_SP2) {
+ pc87560_map_region(s, &s->sp2_io, &s->sp2_mapped,
+ pci_get_long(pci->config + REG_SP2BAR) & ~(hwaddr)0x7);
+ }
+ }
+
+ if (ranges_overlap(addr, len, REG_PPBAR, 4)) {
+ if (funcen & FUNCEN_PP) {
+ pc87560_map_region(s, &s->pp_io, &s->pp_mapped,
+ pci_get_long(pci->config + REG_PPBAR) & ~(hwaddr)0x7);
+ }
+ }
+
+ if (ranges_overlap(addr, len, REG_KBCBAR, 4)) {
+ if (funcen & FUNCEN_KBC) {
+ pc87560_map_region(s, &s->kbc_io, &s->kbc_mapped,
+ pci_get_long(pci->config + REG_KBCBAR) & ~(hwaddr)0x7);
+ }
+ }
+
+ if (ranges_overlap(addr, len, REG_ACPIBAR, 4)) {
+ uint32_t ab = pci_get_long(pci->config + REG_ACPIBAR);
+ pc87560_map_region(s, &s->acpi_io, &s->acpi_mapped,
+ (ab & 1) ? (ab & ~(hwaddr)0x1F) : 0);
+ }
+
+ if (ranges_overlap(addr, len, REG_PMBAR, 4)) {
+ uint32_t pb = pci_get_long(pci->config + REG_PMBAR);
+ pc87560_map_region(s, &s->pm_io, &s->pm_mapped,
+ (pb & 1) ? (pb & ~(hwaddr)0xFF) : 0);
+ }
+
+ if (ranges_overlap(addr, len, REG_RSVD_CFG, 1)) {
+ if ((pci->config[REG_RSVD_CFG] & 0x0F) != 0x01) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "pc87560-superio: offset 0x7E bits[3:0] must be 0x01\n");
+ }
+ }
+}
+
+static void pc87560_superio_realize(PCIDevice *pci, Error **errp)
+{
+ PC87560SuperioState *s = PC87560_Superio(pci);
+ MemoryRegion *io = pci_address_space_io(pci);
+
+ pci_set_word(pci->config + REG_FUNCEN, FUNCEN_DEFAULT);
+ pci->config[REG_RSVD_CFG] = 0x01;
+ pci->config[REG_DMA_ROUTE1] = 0x67;
+ pci->config[REG_PPDID] = 0x10;
+
+ pci_set_long(pci->config + REG_KBCBAR, 0x00000060); /* not usable */
+ pci_set_long(pci->config + REG_ACPIBAR, 0x00004001);
+ pci_set_long(pci->config + REG_PMBAR, 0xFFFFFF01);
+ pci_set_long(pci->config + REG_FDCBAR, 0x000003F0); /* not usable */
+ pci_set_long(pci->config + REG_SP1BAR, 0x000003F8);
+ pci_set_long(pci->config + REG_SP2BAR, 0x000002F8);
+ pci_set_long(pci->config + REG_PPBAR, 0x00000378);
+
+
+ pci_set_long(pci->wmask + REG_KBCBAR, 0xFFFFFFF8); /* not usable */
+ pci_set_long(pci->wmask + REG_ACPIBAR, 0xFFFFFFE1);
+ pci_set_long(pci->wmask + REG_PMBAR, 0xFFFFFF01);
+ pci_set_long(pci->wmask + REG_FDCBAR, 0xFFFFFFF8); /* not usable */
+ pci_set_long(pci->wmask + REG_SP1BAR, 0xFFFFFFF8);
+ pci_set_long(pci->wmask + REG_SP2BAR, 0xFFFFFFF8);
+ pci_set_long(pci->wmask + REG_PPBAR, 0xFFFFFFF8);
+
+ pci->config[PCI_INTERRUPT_PIN] = 0x01;
+
+ qdev_init_gpio_out_named(DEVICE(pci), &s->parent_irq, "pic-out", 1);
+ qdev_init_gpio_in_named(DEVICE(pci), pc87560_pic_irq_in, "pic-irq", 8);
+
+ memory_region_init_io(&s->pic1_io, OBJECT(s), &pc87560_pic1_ops,
+ s, "pc87560-pic1", 2);
+ memory_region_add_subregion(io, IC_PIC1, &s->pic1_io);
+
+ s->serial[0].irq = qemu_allocate_irq(pc87560_pic_irq_in, s, 3);
+ if (!qdev_realize(DEVICE(&s->serial[0]), NULL, errp)) {
+ return;
+ }
+ memory_region_init_io(&s->sp1_io, OBJECT(s), &serial_io_ops,
+ &s->serial[0], "pc87560-sp1", 8);
+ memory_region_add_subregion(io, 0x3F8, &s->sp1_io);
+ s->sp1_mapped = true;
+
+ s->serial[1].irq = qemu_allocate_irq(pc87560_pic_irq_in, s, 4);
+ if (!qdev_realize(DEVICE(&s->serial[1]), NULL, errp)) {
+ return;
+ }
+ memory_region_init_io(&s->sp2_io, OBJECT(s), &serial_io_ops,
+ &s->serial[1], "pc87560-sp2", 8);
+ memory_region_add_subregion(io, 0x2F8, &s->sp2_io);
+ s->sp2_mapped = true;
+
+ s->pp.irq = qemu_allocate_irq(pc87560_pic_irq_in, s, 5);
+ memory_region_init_io(&s->pp_io, OBJECT(s), &pc87560_pp_ops,
+ &s->pp, "pc87560-pp", 8);
+ memory_region_add_subregion(io, 0x378, &s->pp_io);
+ s->pp_mapped = true;
+ s->pic.irq_level = -1;
+ s->pic.priority_base = 7;
+ s->fdc.irq = qemu_allocate_irq(pc87560_pic_irq_in, s, 6);
+ s->fdc.dma_chann = -1;
+ s->fdc.dma = NULL;
+ s->fdc.fallback = FLOPPY_DRIVE_TYPE_144;
+ fdctrl_realize_common(DEVICE(pci), &s->fdc, errp);
+ if (*errp) {
+ return;
+ }
+ memory_region_init_io(&s->fdc_io, OBJECT(s), &pc87560_fdc_ops,
+ &s->fdc, "pc87560-fdc", 8);
+ memory_region_add_subregion(io, 0x3F0, &s->fdc_io);
+ s->fdc_mapped = true;
+
+ memory_region_init_io(&s->kbc_io, OBJECT(s), &pc87560_stub_ops,
+ s, "pc87560-kbc", 8);
+ memory_region_add_subregion(io, 0x60, &s->kbc_io);
+ s->kbc_mapped = true;
+
+ memory_region_init_io(&s->acpi_io, OBJECT(s), &pc87560_stub_ops,
+ s, "pc87560-acpi", 32);
+ memory_region_add_subregion(io, 0x4000, &s->acpi_io);
+ s->acpi_mapped = true;
+
+ memory_region_init_io(&s->pm_io, OBJECT(s), &pc87560_stub_ops,
+ s, "pc87560-pm", 256);
+}
+
+static void pc87560_superio_instance_init(Object *obj)
+{
+ PC87560SuperioState *s = PC87560_Superio(obj);
+
+ object_initialize_child(obj, "serial[*]", &s->serial[0], TYPE_SERIAL);
+ object_initialize_child(obj, "serial[*]", &s->serial[1], TYPE_SERIAL);
+}
+
+static const VMStateDescription vmstate_pc87560_superio = {
+ .name = "pc87560-superio",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (const VMStateField[]) {
+ VMSTATE_PCI_DEVICE(parent_obj, PC87560SuperioState),
+ VMSTATE_STRUCT_ARRAY(serial, PC87560SuperioState, 2, 0,
+ vmstate_serial, SerialState),
+ VMSTATE_STRUCT(fdc, PC87560SuperioState, 0, vmstate_fdc, FDCtrl),
+ VMSTATE_END_OF_LIST()
+ },
+};
+
+static const Property pc87560_superio_properties[] = {
+ DEFINE_PROP_CHR("serial0", PC87560SuperioState, serial[0].chr),
+ DEFINE_PROP_CHR("serial1", PC87560SuperioState, serial[1].chr),
+ DEFINE_PROP_CHR("parallel", PC87560SuperioState, pp.chr),
+};
+
+static void pc87560_superio_class_init(ObjectClass *oc, const void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(oc);
+
+ k->realize = pc87560_superio_realize;
+ k->config_write = pc87560_superio_config_write;
+ k->vendor_id = PCI_VENDOR_ID_NS;
+ k->device_id = PCI_DEVICE_ID_NS_87560_LIO;
+ k->class_id = 0x0680;
+ k->revision = 0x01;
+ k->subsystem_vendor_id = PCI_VENDOR_ID_HP;
+ k->subsystem_id = 0x10A7;
+
+ dc->desc = "NS PC87560 SuperI/O Function 1 (I/O Peripherals)";
+ dc->vmsd = &vmstate_pc87560_superio;
+ dc->user_creatable = false;
+ device_class_set_props(dc, pc87560_superio_properties);
+ set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
+}
+
+static const TypeInfo pc87560_superio_info = {
+ .name = TYPE_PC87560_Superio,
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(PC87560SuperioState),
+ .instance_init = pc87560_superio_instance_init,
+ .class_init = pc87560_superio_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { INTERFACE_CONVENTIONAL_PCI_DEVICE },
+ { },
+ },
+};
+
+static void pc87560_superio_register_types(void)
+{
+ type_register_static(&pc87560_superio_info);
+}
+
+type_init(pc87560_superio_register_types)
diff --git a/include/hw/isa/pc87560.h b/include/hw/isa/pc87560.h
new file mode 100644
index 0000000000..72e317cc7b
--- /dev/null
+++ b/include/hw/isa/pc87560.h
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef HW_ISA_PC87560_H
+#define HW_ISA_PC87560_H
+
+#define TYPE_PC87560_Superio "pc87560-superio"
+OBJECT_DECLARE_SIMPLE_TYPE(PC87560SuperioState, PC87560_Superio)
+
+#define REG_FXBAR0 0x40
+#define REG_FXBAR1 0x44
+#define REG_FXBAR2 0x48
+#define REG_DDMA_BAR 0x4C
+#define REG_FIR_BAR 0x50
+#define REG_SYSCFG 0x58
+#define REG_FUNCEN 0x5A
+#define REG_SIOCFG 0x5C
+#define REG_SPECFEN 0x5D
+#define REG_PPDID 0x5E
+#define REG_PPMODE 0x5F
+#define REG_DMA_ROUTE4 0x61
+#define REG_ROMCFG 0x62
+#define REG_DMA_ROUTE1 0x63
+#define REG_DMA_ROUTE2 0x64
+#define REG_DMA_ROUTE3 0x65
+#define REG_DMA_CHAN 0x66
+#define REG_IRQ_TRIG 0x67
+#define REG_IRQ_ROUTE1 0x69
+#define REG_ARBCTL 0x72
+#define REG_FX_MASK2 0x74
+#define REG_FX_TIMING 0x76
+#define REG_FXCTL 0x78
+#define REG_GPIO_CFG 0x79
+#define REG_GPIO_DIR 0x7A
+#define REG_SIRQ_CTL 0x7B
+#define REG_SIRQ_EN 0x7C
+#define REG_RSVD_CFG 0x7E
+#define REG_AUDIOCS_EN 0x7F
+#define REG_FXMEM_CTL1 0x80
+#define REG_KBCBAR 0x84
+#define REG_ACPIBAR 0x88
+#define REG_PMBAR 0x8C
+#define REG_FDCBAR 0x90
+#define REG_SP1BAR 0x94
+#define REG_SP2BAR 0x98
+#define REG_PPBAR 0x9C
+#define REG_TEST_CTL 0xFF
+
+#define FUNCEN_FDC (1 << 0)
+#define FUNCEN_SP1 (1 << 1)
+#define FUNCEN_SP2 (1 << 3)
+#define FUNCEN_PP (1 << 5)
+#define FUNCEN_KBC (1 << 13)
+
+#define FUNCEN_DEFAULT 0xFFBF
+
+#define IC_PIC1 0x20
+
+typedef struct {
+ uint8_t imr; /* interrupt mask register (OCW1) */
+ uint8_t irr; /* interrupt request register */
+ uint8_t isr; /* in-service register */
+ bool poll_mode; /* OCW3 POLL bit set */
+ bool read_isr; /* OCW3: true=ISR, false=IRR */
+ int init_phase;/* 0=normal, 1-3=ICW2/3/4 expected */
+ int priority_base;
+ int irq_level;
+} Pic8259;
+
+struct PC87560SuperioState {
+ PCIDevice parent_obj;
+
+ qemu_irq parent_irq;
+
+ SerialState serial[2];
+ ParallelState pp;
+ FDCtrl fdc;
+
+ MemoryRegion fdc_io;
+ MemoryRegion sp1_io;
+ MemoryRegion sp2_io;
+ MemoryRegion pp_io;
+ MemoryRegion kbc_io;
+ MemoryRegion acpi_io;
+ MemoryRegion pm_io;
+ MemoryRegion pic1_io;
+
+ bool fdc_mapped;
+ bool sp1_mapped;
+ bool sp2_mapped;
+ bool pp_mapped;
+ bool kbc_mapped;
+ bool acpi_mapped;
+ bool pm_mapped;
+ Pic8259 pic;
+};
+
+#endif
--
2.54.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 3/7] hw/ide/pc87560-ide: Add IDE emulation of NS 87560 SuperIO chip
2026-09-17 21:33 [PATCH 0/7] NS 87560 SuperIO chip Helge Deller
2026-09-17 21:33 ` [PATCH 1/7] hw/pci/pci_ids: Add PCI vendor ID and IDs for NS chips Helge Deller
2026-09-17 21:33 ` [PATCH 2/7] hw/isa/pc87560: Add main isa code for NS 87560 SuperIO chip Helge Deller
@ 2026-09-17 21:33 ` Helge Deller
2026-09-17 21:33 ` [PATCH 4/7] hw/usb/hcd-ohci: Add big endian and consistency check support Helge Deller
` (3 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Helge Deller @ 2026-09-17 21:33 UTC (permalink / raw)
To: qemu-devel
Cc: Michael S. Tsirkin, Denis V. Lunev, Helge Deller, John Snow,
Richard Henderson, Paolo Bonzini, Abizer Lokhandwala, qemu-block
From: Abizer Lokhandwala <abizerlokhandwalastd10@gmail.com>
Add emulation of the IDE ports of the NS SuperIO chip.
Signed-off-by: Abizer Lokhandwala <abizerlokhandwalastd10@gmail.com>
Signed-off-by: Helge Deller <deller@gmx.de>
---
MAINTAINERS | 1 +
hw/ide/pc87560-ide.c | 358 +++++++++++++++++++++++++++++++++++++++++++
hw/ide/trace-events | 4 +
3 files changed, 363 insertions(+)
create mode 100644 hw/ide/pc87560-ide.c
diff --git a/MAINTAINERS b/MAINTAINERS
index e3b1e80ad6..2da2f00ff3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1356,6 +1356,7 @@ F: pc-bios/hppa-firmware.img
F: roms/seabios-hppa/
F: tests/functional/hppa/
F: hw/isa/pc87560.c
+F: hw/ide/pc87560-ide.c
F: include/hw/isa/pc87560.h
Hexagon Machines
diff --git a/hw/ide/pc87560-ide.c b/hw/ide/pc87560-ide.c
new file mode 100644
index 0000000000..879b5f1234
--- /dev/null
+++ b/hw/ide/pc87560-ide.c
@@ -0,0 +1,358 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * NS PC87560 SuperI/O — IDE emulation
+ *
+ * Copyright (c) 2026 Abizer abizerlokhandwalastd10@gmail.com
+ */
+
+#include "qemu/osdep.h"
+#include "hw/pci/pci.h"
+#include "migration/vmstate.h"
+#include "qemu/module.h"
+#include "hw/isa/isa.h"
+#include "hw/core/irq.h"
+#include "hw/ide/pci.h"
+#include "ide-internal.h"
+#include "trace.h"
+
+#define PC87415_IDE_CHANNELS 2
+
+/* PCI configuration space */
+#define PC87415_CTRL 0x40
+#define PC87415_WRITE_BUFFER_STATUS 0x43
+
+/* CTRL register, byte 1 (PCI config offset 0x41) */
+#define PC87415_CTRL_CH1_INT_MASK BIT(0)
+#define PC87415_CTRL_CH2_INT_MASK BIT(1)
+
+/* PCI programming interface */
+#define PC87415_PIF_CH1_PROGRAMMABLE BIT(1)
+#define PC87415_PIF_CH2_PROGRAMMABLE BIT(3)
+#define PC87415_PIF_MASTER_IDE BIT(7)
+
+#define PC87415_PIF_DEFAULT \
+ (PC87415_PIF_CH1_PROGRAMMABLE | \
+ PC87415_PIF_CH2_PROGRAMMABLE | \
+ PC87415_PIF_MASTER_IDE)
+
+/* Per-device timing registers */
+#define PC87415_CH1_DEV1_READ_TIMING 0x44
+#define PC87415_CH1_DEV1_WRITE_TIMING 0x45
+#define PC87415_CH1_DEV2_READ_TIMING 0x48
+#define PC87415_CH1_DEV2_WRITE_TIMING 0x49
+#define PC87415_CH2_DEV1_READ_TIMING 0x4c
+#define PC87415_CH2_DEV1_WRITE_TIMING 0x4d
+#define PC87415_CH2_DEV2_READ_TIMING 0x50
+#define PC87415_CH2_DEV2_WRITE_TIMING 0x51
+
+#define PC87415_CMD_TIMING 0x54
+#define PC87415_SECTOR_SIZE 0x55
+
+/* PCI BARs */
+#define PC87415_BAR_CH1_DATA 0
+#define PC87415_BAR_CH1_CONTROL 1
+#define PC87415_BAR_CH2_DATA 2
+#define PC87415_BAR_CH2_CONTROL 3
+#define PC87415_BAR_BMDMA 4
+
+/* Bus-master IDE registers */
+#define PC87415_BMDMA_CMD 0x00
+#define PC87415_BMDMA_STATUS 0x02
+#define PC87415_BMDMA_PRD 0x04
+#define PC87415_BMDMA_CHANNEL_SIZE 0x08
+#define PC87415_BMDMA_BAR_SIZE 0x10
+
+/*
+ * Bus-master command register:
+ * bit 0 = start/stop
+ * bit 3 = read/write
+ */
+#define PC87415_BMDMA_CMD_START BIT(0)
+#define PC87415_BMDMA_CMD_WRITE BIT(3)
+#define PC87415_BMDMA_CMD_MASK \
+ (PC87415_BMDMA_CMD_START | PC87415_BMDMA_CMD_WRITE)
+
+/*
+ * Bus-master status register:
+ * bit 1 = error
+ * bit 2 = interrupt
+ */
+#define PC87415_BMDMA_STATUS_ERROR BIT(1)
+#define PC87415_BMDMA_STATUS_INT BIT(2)
+
+static qemu_irq pc87560_ide_irq_out;
+static void pc87560_update_irq(PCIDevice *pd);
+
+static uint64_t bmdma_read(void *opaque, hwaddr addr, unsigned size)
+{
+ BMDMAState *bm = opaque;
+ uint32_t val;
+
+ if (size != 1) {
+ return ((uint64_t)1 << (size * 8)) - 1;
+ }
+
+ switch (addr & 3) {
+ case 0:
+ val = bm->cmd;
+ break;
+ case 2:
+ val = bm->status;
+ break;
+ default:
+ val = 0xff;
+ break;
+ }
+ return val;
+}
+
+static void bmdma_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+ BMDMAState *bm = opaque;
+ PCIDevice *pd = PCI_DEVICE(bm->bus->qbus.parent);
+
+ if (size != 1) {
+ return;
+ }
+
+ switch (addr & 3) {
+ case 0: {
+ /*
+ * NS87415 erratum: writing the command register also clears
+ * the error and interrupt status bits.
+ */
+ uint8_t status_clear = val & (PC87415_BMDMA_STATUS_INT |
+ PC87415_BMDMA_STATUS_ERROR);
+ uint8_t real_cmd = val & PC87415_BMDMA_CMD_MASK;
+ /* only START(0) and WRITE(3) are real cmd bits */
+
+ bm->status &= ~status_clear;
+
+ if (real_cmd != (bm->cmd & 0x09)) {
+ bmdma_cmd_writeb(bm, real_cmd);
+ }
+
+ pc87560_update_irq(pd);
+ break;
+ }
+ case 2:
+ bmdma_status_writeb(bm, val);
+ pc87560_update_irq(pd);
+ break;
+ default:
+ break;
+ }
+}
+
+static void pc87560_update_irq(PCIDevice *pd)
+{
+ PCIIDEState *d = PCI_IDE(pd);
+ uint8_t ctrl1 = pd->config[PC87415_CTRL + 1];
+ int level = 0;
+
+ if (!(ctrl1 & PC87415_CTRL_CH1_INT_MASK)) {
+ level |= !!(d->bmdma[0].status & BM_STATUS_INT);
+ }
+
+ if (!(ctrl1 & PC87415_CTRL_CH2_INT_MASK)) {
+ level |= !!(d->bmdma[1].status & BM_STATUS_INT);
+ }
+
+ qemu_set_irq(pc87560_ide_irq_out, level);
+}
+
+static const MemoryRegionOps pc87560_bmdma_ops = {
+ .read = bmdma_read,
+ .write = bmdma_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void bmdma_setup_bar(PCIIDEState *d)
+{
+ unsigned int i;
+
+ memory_region_init(&d->bmdma_bar, OBJECT(d), "pc87415-bmdma",
+ PC87415_BMDMA_BAR_SIZE);
+
+ for (i = 0; i < PC87415_IDE_CHANNELS; i++) {
+ BMDMAState *bm = &d->bmdma[i];
+
+ memory_region_init_io(&bm->extra_io, OBJECT(d),
+ &pc87560_bmdma_ops, bm,
+ "pc87415-bmdma-cmd-status",
+ 4);
+ memory_region_add_subregion(&d->bmdma_bar,
+ i * PC87415_BMDMA_CHANNEL_SIZE,
+ &bm->extra_io);
+
+ memory_region_init_io(&bm->addr_ioport, OBJECT(d),
+ &bmdma_addr_ioport_ops, bm,
+ "pc87415-bmdma-prd",
+ 4);
+ memory_region_add_subregion(&d->bmdma_bar,
+ i * PC87415_BMDMA_CHANNEL_SIZE +
+ PC87415_BMDMA_PRD,
+ &bm->addr_ioport);
+ }
+}
+
+
+static void pc87560_set_irq(void *opaque, int channel, int level)
+{
+ PCIIDEState *d = opaque;
+ PCIDevice *pd = PCI_DEVICE(d);
+
+ if (level) {
+ d->bmdma[channel].status |= BM_STATUS_INT;
+ }
+ pc87560_update_irq(pd);
+}
+
+
+static void pc87560_reset(Object *dev, ResetType type)
+{
+ PCIIDEState *d = PCI_IDE(dev);
+ unsigned int i;
+
+ for (i = 0; i < 2; i++) {
+ ide_bus_reset(&d->bus[i], IDE_RESET_HARDWARE);
+ }
+}
+
+
+static void pc87560_pci_config_write(PCIDevice *d, uint32_t addr, uint32_t val,
+ int len)
+{
+ pci_default_write_config(d, addr, val, len);
+
+ if (ranges_overlap(addr, len, PC87415_CTRL + 1, 1)) {
+ pc87560_update_irq(d);
+ }
+}
+
+static void pci_pc87560_ide_realize(PCIDevice *dev, Error **errp)
+{
+
+ PCIIDEState *d = PCI_IDE(dev);
+ DeviceState *ds = DEVICE(dev);
+ uint8_t *pci_conf = dev->config;
+ int i;
+
+ dev->cap_present |= QEMU_PCI_CAP_MULTIFUNCTION;
+ pci_conf[PCI_HEADER_TYPE] |= PCI_HEADER_TYPE_MULTI_FUNCTION;
+
+ pci_conf[PCI_CLASS_PROG] = 0x8a;
+ dev->wmask[PCI_CLASS_PROG] = 0xff;
+
+ pci_conf[PCI_INTERRUPT_PIN] = 0x01;
+ qdev_init_gpio_out(ds, &pc87560_ide_irq_out, 1);
+
+ memset(&dev->wmask[PC87415_CTRL], 0xff, 3);
+
+ dev->wmask[PC87415_CH1_DEV1_READ_TIMING] = 0xff;
+ dev->wmask[PC87415_CH1_DEV1_WRITE_TIMING] = 0xff;
+
+ dev->wmask[PC87415_CH1_DEV2_READ_TIMING] = 0xff;
+ dev->wmask[PC87415_CH1_DEV2_WRITE_TIMING] = 0xff;
+
+ dev->wmask[PC87415_CH2_DEV1_READ_TIMING] = 0xff;
+ dev->wmask[PC87415_CH2_DEV1_WRITE_TIMING] = 0xff;
+
+ dev->wmask[PC87415_CH2_DEV2_READ_TIMING] = 0xff;
+ dev->wmask[PC87415_CH2_DEV2_WRITE_TIMING] = 0xff;
+
+ dev->wmask[PC87415_CMD_TIMING] = 0xff;
+ dev->wmask[PC87415_SECTOR_SIZE] = 0xff;
+
+ pci_conf[PC87415_CTRL + 0] = 0x00;
+ pci_conf[PC87415_CTRL + 1] = 0x00;
+ pci_conf[PC87415_CTRL + 2] = 0x00;
+
+ pci_conf[PC87415_CH1_DEV1_READ_TIMING] = 0x85;
+ pci_conf[PC87415_CH1_DEV1_WRITE_TIMING] = 0x85;
+ pci_conf[PC87415_CH1_DEV2_READ_TIMING] = 0x85;
+ pci_conf[PC87415_CH1_DEV2_WRITE_TIMING] = 0x85;
+ pci_conf[PC87415_CH2_DEV1_READ_TIMING] = 0x85;
+ pci_conf[PC87415_CH2_DEV1_WRITE_TIMING] = 0x85;
+ pci_conf[PC87415_CH2_DEV2_READ_TIMING] = 0x85;
+ pci_conf[PC87415_CH2_DEV2_WRITE_TIMING] = 0x85;
+
+ pci_conf[PC87415_CMD_TIMING] = 0xB7;
+ pci_conf[PC87415_SECTOR_SIZE] = 0xEE;
+
+ memory_region_init_io(&d->data_bar[0], OBJECT(d), &pci_ide_data_le_ops,
+ &d->bus[0], "pc87560-data0", 8);
+ pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &d->data_bar[0]);
+
+ memory_region_init_io(&d->cmd_bar[0], OBJECT(d), &pci_ide_cmd_le_ops,
+ &d->bus[0], "pc87560-cmd0", 4);
+ pci_register_bar(dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &d->cmd_bar[0]);
+
+ memory_region_init_io(&d->data_bar[1], OBJECT(d), &pci_ide_data_le_ops,
+ &d->bus[1], "pc87560-data1", 8);
+ pci_register_bar(dev, 2, PCI_BASE_ADDRESS_SPACE_IO, &d->data_bar[1]);
+
+ memory_region_init_io(&d->cmd_bar[1], OBJECT(d), &pci_ide_cmd_le_ops,
+ &d->bus[1], "pc87560-cmd1", 4);
+ pci_register_bar(dev, 3, PCI_BASE_ADDRESS_SPACE_IO, &d->cmd_bar[1]);
+
+ bmdma_setup_bar(d);
+ pci_register_bar(dev, 4, PCI_BASE_ADDRESS_SPACE_IO, &d->bmdma_bar);
+
+ qdev_init_gpio_in(ds, pc87560_set_irq, 2);
+ for (i = 0; i < 2; i++) {
+ ide_bus_init(&d->bus[i], sizeof(d->bus[i]), ds, i, 2);
+ ide_bus_init_output_irq(&d->bus[i], qdev_get_gpio_in(ds, i));
+
+ bmdma_init(&d->bus[i], &d->bmdma[i], d);
+ d->bmdma[i].bus = &d->bus[i];
+ ide_bus_register_restart_cb(&d->bus[i]);
+ }
+}
+
+static void pci_pc87560_ide_exitfn(PCIDevice *dev)
+{
+ PCIIDEState *d = PCI_IDE(dev);
+ unsigned int i;
+
+ for (i = 0; i < 2; ++i) {
+ memory_region_del_subregion(&d->bmdma_bar, &d->bmdma[i].extra_io);
+ memory_region_del_subregion(&d->bmdma_bar, &d->bmdma[i].addr_ioport);
+ }
+}
+
+
+static void pc87560_ide_class_init(ObjectClass *klass, const void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
+
+ rc->phases.hold = pc87560_reset;
+ dc->vmsd = &vmstate_ide_pci;
+ dc->user_creatable = false;
+ k->realize = pci_pc87560_ide_realize;
+ k->exit = pci_pc87560_ide_exitfn;
+ k->vendor_id = PCI_VENDOR_ID_NS;
+ k->device_id = PCI_DEVICE_ID_NS_87415;
+ k->revision = 0x02;
+ k->subsystem_vendor_id = PCI_VENDOR_ID_HP;
+ k->subsystem_id = 0x10A7;
+ k->class_id = PCI_CLASS_STORAGE_IDE;
+ k->config_write = pc87560_pci_config_write;
+ set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
+}
+
+
+static const TypeInfo pc87560_ide_info = {
+ .name = "pc87560-ide",
+ .parent = TYPE_PCI_IDE,
+ .class_init = pc87560_ide_class_init,
+};
+
+static void pc87560_ide_register_types(void)
+{
+ type_register_static(&pc87560_ide_info);
+}
+
+type_init(pc87560_ide_register_types)
diff --git a/hw/ide/trace-events b/hw/ide/trace-events
index 3ab5e7bd1d..c44eec6c58 100644
--- a/hw/ide/trace-events
+++ b/hw/ide/trace-events
@@ -27,6 +27,10 @@ ide_dma_cb(void *s, int64_t sector_num, int n, const char *dma) "IDEState %p; se
bmdma_read_cmd646(uint64_t addr, uint32_t val) "bmdma: readb 0x%"PRIx64" : 0x%02x"
bmdma_write_cmd646(uint64_t addr, uint64_t val) "bmdma: writeb 0x%"PRIx64" : 0x%02"PRIx64
+# hw/ide/pc87560.c
+bmdma_read_pc87560(uint64_t addr, uint32_t val) "bmdma: readb 0x%"PRIx64" : 0x%02x"
+bmdma_write_pc87560(uint64_t addr, uint64_t val) "bmdma: writeb 0x%"PRIx64" : 0x%02"PRIx64
+
# pci.c
bmdma_reset(void) ""
bmdma_cmd_writeb(uint32_t val) "val: 0x%08x"
--
2.54.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 4/7] hw/usb/hcd-ohci: Add big endian and consistency check support
2026-09-17 21:33 [PATCH 0/7] NS 87560 SuperIO chip Helge Deller
` (2 preceding siblings ...)
2026-09-17 21:33 ` [PATCH 3/7] hw/ide/pc87560-ide: Add IDE emulation of " Helge Deller
@ 2026-09-17 21:33 ` Helge Deller
2026-09-17 21:33 ` [PATCH 5/7] hw/usb/pc87560-ohci: Add USB OHCI emulation of NS 87560 SuperIO chip Helge Deller
` (2 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Helge Deller @ 2026-09-17 21:33 UTC (permalink / raw)
To: qemu-devel
Cc: Michael S. Tsirkin, Denis V. Lunev, Helge Deller, John Snow,
Richard Henderson, Paolo Bonzini, Abizer Lokhandwala, qemu-block
From: Abizer Lokhandwala <abizerlokhandwalastd10@gmail.com>
The OHCI controller allows running in big endian mode. If this mode is enabled, swap the
buffer contents before accessing them since we by default run OHCI in little endian mode.
Additionally add the architected consistency check which halts OHCI on errors.
Signed-off-by: Abizer Lokhandwala <abizerlokhandwalastd10@gmail.com>
Signed-off-by: Helge Deller <deller@gmx.de>
---
hw/usb/hcd-ohci.c | 35 +++++++++++++++++++++++++++++++++++
hw/usb/hcd-ohci.h | 5 +++++
2 files changed, 40 insertions(+)
diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c
index 23990115ab..5e5fe41f47 100644
--- a/hw/usb/hcd-ohci.c
+++ b/hw/usb/hcd-ohci.c
@@ -74,6 +74,7 @@ struct ohci_hcca {
/* Flags in the head field of an Endpoint Descriptor. */
#define OHCI_ED_H 1
#define OHCI_ED_C 2
+#define OHCI_ED_HEAD_RSVD (1 << 2 | 1 << 3)
/* Bitfields for the first word of a Transfer Descriptor. */
#define OHCI_TD_R (1 << 18)
@@ -371,6 +372,8 @@ void ohci_hard_reset(OHCIState *ohci)
ohci_soft_reset(ohci);
ohci->ctl = 0;
ohci_roothub_reset(ohci);
+ ohci->big_endian = false;
+ ohci->consistency_check = false;
}
/* Get an array of dwords from main memory */
@@ -867,6 +870,18 @@ static void ohci_td_pkt(const char *msg, const uint8_t *buf, size_t len)
}
}
+static void ohci_bswap_buf(uint8_t *buf, size_t len)
+{
+ uint32_t w;
+ while (len >= sizeof(w)) {
+ memcpy(&w, buf, sizeof(w));
+ w = bswap32(w);
+ memcpy(buf, &w, sizeof(w));
+ buf += sizeof(w);
+ len -= sizeof(w);
+ }
+}
+
/*
* Service a transport descriptor.
* Returns nonzero to terminate processing of this endpoint.
@@ -975,6 +990,9 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
DMA_DIRECTION_TO_DEVICE)) {
ohci_die(ohci);
}
+ if (ohci->big_endian) {
+ ohci_bswap_buf(ohci->usb_buf, pktlen);
+ }
}
}
}
@@ -1024,6 +1042,9 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
if (ret >= 0) {
if (dir == OHCI_TD_DIR_IN) {
+ if (ohci->big_endian) {
+ ohci_bswap_buf(ohci->usb_buf, ret);
+ }
if (ohci_copy_td(ohci, &td, ohci->usb_buf, ret,
DMA_DIRECTION_FROM_DEVICE)) {
ohci_die(ohci);
@@ -1108,6 +1129,13 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
ohci->done_count = i;
}
exit_no_retire:
+ if (ohci->media_error) {
+ int cc = OHCI_BM(td.flags, TD_CC);
+ /* only these 2 errors will ever actually be possible in qemu */
+ if (cc == OHCI_CC_DEVICENOTRESPONDING || cc == OHCI_CC_UNDEXPETEDPID) {
+ ohci->media_error(ohci);
+ }
+ }
if (ohci_put_td(ohci, addr, &td)) {
ohci_die(ohci);
return 1;
@@ -1136,6 +1164,13 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head)
ohci_die(ohci);
return 0;
}
+ if (ohci->consistency_check && (ed.head & OHCI_ED_HEAD_RSVD)) {
+ if (ohci->descriptor_error) {
+ ohci->descriptor_error(ohci);
+ }
+ ohci_die(ohci);
+ return 0;
+ }
next_ed = ed.next & OHCI_DPTR_MASK;
diff --git a/hw/usb/hcd-ohci.h b/hw/usb/hcd-ohci.h
index 243cd1f46a..d079f8d24e 100644
--- a/hw/usb/hcd-ohci.h
+++ b/hw/usb/hcd-ohci.h
@@ -93,6 +93,11 @@ struct OHCIState {
bool async_complete;
void (*ohci_die)(OHCIState *ohci);
+ void (*media_error)(OHCIState *ohci);
+ void (*descriptor_error)(OHCIState *ohci);
+
+ bool big_endian;
+ bool consistency_check;
};
#define TYPE_SYSBUS_OHCI "sysbus-ohci"
--
2.54.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 5/7] hw/usb/pc87560-ohci: Add USB OHCI emulation of NS 87560 SuperIO chip
2026-09-17 21:33 [PATCH 0/7] NS 87560 SuperIO chip Helge Deller
` (3 preceding siblings ...)
2026-09-17 21:33 ` [PATCH 4/7] hw/usb/hcd-ohci: Add big endian and consistency check support Helge Deller
@ 2026-09-17 21:33 ` Helge Deller
2026-09-17 21:33 ` [PATCH 6/7] hw/pc87560: Wire up " Helge Deller
2026-09-17 21:34 ` [PATCH 7/7] hw/hppa: Add NS 87560 SuperIO chip to HP C3700 machine Helge Deller
6 siblings, 0 replies; 8+ messages in thread
From: Helge Deller @ 2026-09-17 21:33 UTC (permalink / raw)
To: qemu-devel
Cc: Michael S. Tsirkin, Denis V. Lunev, Helge Deller, John Snow,
Richard Henderson, Paolo Bonzini, Abizer Lokhandwala, qemu-block
From: Abizer Lokhandwala <abizerlokhandwalastd10@gmail.com>
Add emulation of the USB OHCI portion of the NS SuperIO chip.
Signed-off-by: Abizer Lokhandwala <abizerlokhandwalastd10@gmail.com>
Signed-off-by: Helge Deller <deller@gmx.de>
---
MAINTAINERS | 1 +
hw/usb/pc87560-ohci.c | 355 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 356 insertions(+)
create mode 100644 hw/usb/pc87560-ohci.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 2da2f00ff3..e8c7030dec 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1357,6 +1357,7 @@ F: roms/seabios-hppa/
F: tests/functional/hppa/
F: hw/isa/pc87560.c
F: hw/ide/pc87560-ide.c
+F: hw/usb/pc87560-ohci.c
F: include/hw/isa/pc87560.h
Hexagon Machines
diff --git a/hw/usb/pc87560-ohci.c b/hw/usb/pc87560-ohci.c
new file mode 100644
index 0000000000..cc00f6e936
--- /dev/null
+++ b/hw/usb/pc87560-ohci.c
@@ -0,0 +1,355 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * NS PC87560 SuperI/O — USB OHCI emulation
+ *
+ * Copyright (c) 2026 Abizer abizerlokhandwalastd10@gmail.com
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/timer.h"
+#include "hw/usb/usb.h"
+#include "migration/vmstate.h"
+#include "hw/pci/pci_device.h"
+#include "hw/core/sysbus.h"
+#include "hw/core/irq.h"
+#include "hw/core/qdev-properties.h"
+#include "trace.h"
+#include "hcd-ohci.h"
+#include "qom/object.h"
+
+#define TYPE_NSC_PCI_OHCI "pc87560-ohci"
+OBJECT_DECLARE_SIMPLE_TYPE(OHCIPCIState, NSC_PCI_OHCI)
+
+struct OHCIPCIState {
+
+ PCIDevice parent_obj;
+ OHCIState state;
+ MemoryRegion nsc_bar;
+ char *masterbus;
+ uint32_t num_ports;
+ uint32_t firstport;
+ qemu_irq irq_out[1];
+
+ /* NSC (NUSSBAR) vendor-specific register state */
+ uint32_t nsc_control;
+ uint32_t nsc_status;
+};
+
+#define NSC_CONTROL 0x00
+#define NSC_COMMAND_STATUS 0x04
+#define NSC_PORT_RX_STATUS 0x08
+#define NSC_ROOT_HUB_STATUS 0x34
+#define NSC_CTL_BIG_ENDIAN (1U << 0)
+#define NSC_CTL_CCE (1U << 1) /* Consistency Check Enable */
+#define NSC_CTL_WORDCOUNT (0x3fU << 2) /* bits 7:2 */
+#define NSC_CTL_MODE (0x3U << 8) /* bits 9:8 */
+#define NSC_CTL_LIMIT_CONTROL (1U << 16)
+#define NSC_CTL_RISE_TIME (0xffU << 24) /* bits 31:24 */
+#define NSC_CTL_WRMASK (NSC_CTL_BIG_ENDIAN | NSC_CTL_CCE | NSC_CTL_WORDCOUNT |\
+ NSC_CTL_MODE | NSC_CTL_LIMIT_CONTROL | \
+ NSC_CTL_RISE_TIME)
+#define NSC_CONTROL_RESET 0x90000014U
+#define NSC_CS_MEDIA_ERROR 0x00000fffU /* bits 11:0, 12-bit counter */
+#define NSC_CS_MEDIA_ERR_OVFL (1U << 16)
+#define NSC_CS_UE_PCI (1U << 17)
+#define NSC_CS_UE_DESCRIPTOR (1U << 18)
+#define NSC_CS_UE_INTERNAL (1U << 19)
+#define NSC_CS_SCHEDULE_ERROR (1U << 20)
+#define NSC_CS_PHY_OVERRUN (1U << 21)
+#define NSC_CS_W1C_MASK (NSC_CS_MEDIA_ERR_OVFL | NSC_CS_UE_PCI |\
+ NSC_CS_UE_DESCRIPTOR | NSC_CS_UE_INTERNAL |\
+ NSC_CS_SCHEDULE_ERROR | NSC_CS_PHY_OVERRUN)
+#define NSC_OHCI_INTR_SO (1U << 0)
+#define NSC_RHPORT_CCS (1U << 0) /* CurrentConnectStatus */
+#define NSC_RHPORT_PES (1U << 1) /* PortEnableStatus */
+#define NSC_RHPORT_PSS (1U << 2) /* PortSuspendStatus */
+#define NSC_RHPORT_PRS (1U << 4) /* PortResetStatus */
+#define NSC_RHPORT_PPS (1U << 8) /* PortPowerStatus */
+
+static uint32_t nsc_hub_port_state(OHCIState *s, int port)
+{
+ uint32_t ctrl = s->rhport[port].ctrl;
+
+ if (!(ctrl & NSC_RHPORT_PPS)) {
+ return 0x8; /* POWER OFF */
+ }
+ if (!(ctrl & NSC_RHPORT_CCS)) {
+ return 0xa; /* DISCONNECTED */
+ }
+ if (ctrl & NSC_RHPORT_PRS) {
+ return 0x2; /* RESET */
+ }
+ if (ctrl & NSC_RHPORT_PSS) {
+ return 0xc; /* SUSPENDED */
+ }
+ if (ctrl & NSC_RHPORT_PES) {
+ return 0xe; /* ENABLED, Idle */
+ }
+ return 0xb; /* DISABLED */
+}
+
+static uint32_t nsc_root_hub_status(OHCIPCIState *ohci)
+{
+ OHCIState *s = &ohci->state;
+ uint32_t val = 0;
+
+ if (s->num_ports >= 1) {
+ val |= nsc_hub_port_state(s, 0); /* Port 1 Status[3:0] */
+ }
+ if (s->num_ports >= 2) {
+ val |= nsc_hub_port_state(s, 1) << 8; /* Port 2 Status[3:0] */
+ }
+ return val;
+}
+
+static void nsc_ohci_media_error(OHCIState *ohci)
+{
+ OHCIPCIState *dev = container_of(ohci, OHCIPCIState, state);
+ uint32_t count = dev->nsc_status & NSC_CS_MEDIA_ERROR;
+
+ if (count == NSC_CS_MEDIA_ERROR) {
+ dev->nsc_status |= NSC_CS_MEDIA_ERR_OVFL;
+ } else {
+ dev->nsc_status = (dev->nsc_status & ~NSC_CS_MEDIA_ERROR) | (count + 1);
+ }
+}
+
+static void nsc_ohci_descriptor_error(OHCIState *ohci)
+{
+ OHCIPCIState *dev = container_of(ohci, OHCIPCIState, state);
+ dev->nsc_status |= NSC_CS_UE_DESCRIPTOR;
+}
+
+static uint64_t nsc_usb_read(void *opaque, hwaddr addr, unsigned size)
+{
+ OHCIPCIState *ohci = opaque;
+ uint32_t val;
+
+ switch (addr & ~3) {
+ case NSC_CONTROL:
+ val = ohci->nsc_control;
+ break;
+ case NSC_COMMAND_STATUS:
+ val = ohci->nsc_status;
+ if (ohci->state.intr_status & NSC_OHCI_INTR_SO) {
+ val |= NSC_CS_SCHEDULE_ERROR;
+ }
+ break;
+ case NSC_PORT_RX_STATUS:
+ val = 0;
+ break;
+ case NSC_ROOT_HUB_STATUS:
+ val = nsc_root_hub_status(ohci);
+ break;
+ default:
+ val = 0;
+ break;
+ }
+
+ val >>= (addr & 3) * 8;
+ if (size < 4) {
+ val &= (1U << (size * 8)) - 1;
+ }
+ return val;
+}
+
+static void pc87560_usb_irq_relay(void *opaque, int n, int level)
+{
+ OHCIPCIState *ohci = opaque;
+ qemu_set_irq(ohci->irq_out[0], level);
+}
+
+static void nsc_usb_write(void *opaque, hwaddr addr, uint64_t val,
+ unsigned size)
+{
+ OHCIPCIState *ohci = opaque;
+ uint32_t shift = (addr & 3) * 8;
+ uint32_t lane = (size < 4) ? (((1U << (size * 8)) - 1) << shift)
+ : 0xffffffffU;
+ uint32_t wval = ((uint32_t)val << shift) & lane;
+
+ switch (addr & ~3) {
+ case NSC_CONTROL:
+ ohci->nsc_control = (ohci->nsc_control & ~lane) |
+ (wval & NSC_CTL_WRMASK);
+ ohci->state.big_endian = (ohci->nsc_control & NSC_CTL_BIG_ENDIAN) != 0;
+ ohci->state.consistency_check = (ohci->nsc_control & NSC_CTL_CCE) != 0;
+ break;
+
+ case NSC_COMMAND_STATUS:
+ if (wval & NSC_CS_MEDIA_ERROR) {
+ ohci->nsc_status &= ~NSC_CS_MEDIA_ERROR;
+ }
+ ohci->nsc_status &= ~(wval & NSC_CS_W1C_MASK);
+ if (wval & NSC_CS_SCHEDULE_ERROR) {
+ ohci->state.intr_status &= ~NSC_OHCI_INTR_SO;
+ }
+ break;
+
+ case NSC_PORT_RX_STATUS:
+ case NSC_ROOT_HUB_STATUS:
+ break;
+
+ default:
+ break;
+ }
+}
+
+static const MemoryRegionOps nsc_usb_ops = {
+ .read = nsc_usb_read,
+ .write = nsc_usb_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 1,
+ .max_access_size = 4,
+ },
+};
+
+static void ohci_pci_die(struct OHCIState *ohci)
+{
+ OHCIPCIState *dev = container_of(ohci, OHCIPCIState, state);
+
+ ohci_sysbus_die(ohci);
+
+ dev->nsc_status |= NSC_CS_UE_INTERNAL;
+
+ pci_set_word(dev->parent_obj.config + PCI_STATUS,
+ PCI_STATUS_DETECTED_PARITY);
+}
+
+static uint32_t pc87560_ohci_config_read(PCIDevice *pci, uint32_t addr, int len)
+{
+ uint32_t val = pci_default_read_config(pci, addr, len);
+ return val;
+}
+
+static void pc87560_ohci_config_write(PCIDevice *pci, uint32_t addr,
+ uint32_t val, int len)
+{
+ pci_default_write_config(pci, addr, val, len);
+}
+
+static void usb_ohci_realize_pci(PCIDevice *dev, Error **errp)
+{
+ Error *err = NULL;
+ OHCIPCIState *ohci = NSC_PCI_OHCI(dev);
+
+ dev->config[PCI_CLASS_PROG] = 0x10;
+ dev->wmask[PCI_CLASS_PROG] = 0xff;
+ dev->config[PCI_INTERRUPT_PIN] = 0x01;
+
+ qdev_init_gpio_out(DEVICE(dev), ohci->irq_out, 1);
+ ohci->state.irq = qemu_allocate_irq(pc87560_usb_irq_relay, ohci, 0);
+
+ usb_ohci_init(&ohci->state, DEVICE(dev), ohci->num_ports, 0,
+ ohci->masterbus, ohci->firstport,
+ pci_get_address_space(dev), ohci_pci_die, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ ohci->state.media_error = nsc_ohci_media_error;
+ ohci->state.descriptor_error = nsc_ohci_descriptor_error;
+
+ pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &ohci->state.mem);
+
+ memory_region_init_io(&ohci->nsc_bar, OBJECT(dev), &nsc_usb_ops, ohci,
+ "nsc-usb", 0x1000);
+ pci_register_bar(dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY, &ohci->nsc_bar);
+}
+
+static void usb_ohci_exit(PCIDevice *dev)
+{
+ OHCIPCIState *ohci = NSC_PCI_OHCI(dev);
+ OHCIState *s = &ohci->state;
+
+ trace_usb_ohci_exit(s->name);
+ ohci_bus_stop(s);
+
+ if (s->async_td) {
+ usb_cancel_packet(&s->usb_packet);
+ s->async_td = 0;
+ }
+ ohci_stop_endpoints(s);
+
+ if (!ohci->masterbus) {
+ usb_bus_release(&s->bus);
+ }
+
+ timer_free(s->eof_timer);
+}
+
+static void usb_ohci_reset_pci(DeviceState *d)
+{
+ PCIDevice *dev = PCI_DEVICE(d);
+ OHCIPCIState *ohci = NSC_PCI_OHCI(dev);
+ OHCIState *s = &ohci->state;
+
+ ohci_hard_reset(s);
+
+ ohci->nsc_control = NSC_CONTROL_RESET;
+ ohci->nsc_status = 0;
+}
+
+static const Property ohci_pci_properties[] = {
+ DEFINE_PROP_STRING("masterbus", OHCIPCIState, masterbus),
+ DEFINE_PROP_UINT32("num-ports", OHCIPCIState, num_ports, 3),
+ DEFINE_PROP_UINT32("firstport", OHCIPCIState, firstport, 0),
+};
+
+static const VMStateDescription vmstate_ohci = {
+ .name = "pc87560-ohci",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (const VMStateField[]) {
+ VMSTATE_PCI_DEVICE(parent_obj, OHCIPCIState),
+ VMSTATE_STRUCT(state, OHCIPCIState, 1, vmstate_ohci_state, OHCIState),
+ VMSTATE_UINT32(nsc_control, OHCIPCIState),
+ VMSTATE_UINT32(nsc_status, OHCIPCIState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void ohci_pci_class_init(ObjectClass *klass, const void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ k->realize = usb_ohci_realize_pci;
+ k->exit = usb_ohci_exit;
+ k->config_read = pc87560_ohci_config_read;
+ k->config_write = pc87560_ohci_config_write;
+ k->vendor_id = PCI_VENDOR_ID_NS;
+ k->device_id = PCI_DEVICE_ID_NS_87560_USB;
+ k->revision = 0x01;
+ k->class_id = PCI_CLASS_SERIAL_USB;
+ k->subsystem_vendor_id = PCI_VENDOR_ID_HP;
+ k->subsystem_id = 0x10A7;
+
+ set_bit(DEVICE_CATEGORY_USB, dc->categories);
+ dc->desc = "National Semiconductor USB Controller";
+ device_class_set_props(dc, ohci_pci_properties);
+ dc->hotpluggable = false;
+ dc->user_creatable = false;
+ dc->vmsd = &vmstate_ohci;
+ device_class_set_legacy_reset(dc, usb_ohci_reset_pci);
+}
+
+static const TypeInfo ohci_pci_info = {
+ .name = TYPE_NSC_PCI_OHCI,
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(OHCIPCIState),
+ .class_init = ohci_pci_class_init,
+ .interfaces = (const InterfaceInfo[]) {
+ { INTERFACE_CONVENTIONAL_PCI_DEVICE },
+ { },
+ },
+};
+
+static void ohci_pci_register_types(void)
+{
+ type_register_static(&ohci_pci_info);
+}
+
+type_init(ohci_pci_register_types)
--
2.54.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 6/7] hw/pc87560: Wire up emulation of NS 87560 SuperIO chip
2026-09-17 21:33 [PATCH 0/7] NS 87560 SuperIO chip Helge Deller
` (4 preceding siblings ...)
2026-09-17 21:33 ` [PATCH 5/7] hw/usb/pc87560-ohci: Add USB OHCI emulation of NS 87560 SuperIO chip Helge Deller
@ 2026-09-17 21:33 ` Helge Deller
2026-09-17 21:34 ` [PATCH 7/7] hw/hppa: Add NS 87560 SuperIO chip to HP C3700 machine Helge Deller
6 siblings, 0 replies; 8+ messages in thread
From: Helge Deller @ 2026-09-17 21:33 UTC (permalink / raw)
To: qemu-devel
Cc: Michael S. Tsirkin, Denis V. Lunev, Helge Deller, John Snow,
Richard Henderson, Paolo Bonzini, Abizer Lokhandwala, qemu-block
From: Abizer Lokhandwala <abizerlokhandwalastd10@gmail.com>
Put everything together and add the meson code to enable build of the IDE-,
USB- and core portions of the NS SuperIO chip.
Signed-off-by: Abizer Lokhandwala <abizerlokhandwalastd10@gmail.com>
Signed-off-by: Helge Deller <deller@gmx.de>
---
hw/ide/meson.build | 1 +
hw/isa/meson.build | 1 +
hw/usb/meson.build | 1 +
3 files changed, 3 insertions(+)
diff --git a/hw/ide/meson.build b/hw/ide/meson.build
index ddd7066040..be82ee8974 100644
--- a/hw/ide/meson.build
+++ b/hw/ide/meson.build
@@ -14,3 +14,4 @@ system_ss.add(when: 'CONFIG_IDE_PCI', if_true: files('pci.c'))
system_ss.add(when: 'CONFIG_IDE_PIIX', if_true: files('piix.c', 'ioport.c'))
system_ss.add(when: 'CONFIG_IDE_SII3112', if_true: files('sii3112.c'))
system_ss.add(when: 'CONFIG_IDE_VIA', if_true: files('via.c'))
+system_ss.add(when: 'CONFIG_PC87560', if_true: files('pc87560-ide.c'))
diff --git a/hw/isa/meson.build b/hw/isa/meson.build
index 3219282217..1a150e7069 100644
--- a/hw/isa/meson.build
+++ b/hw/isa/meson.build
@@ -8,4 +8,5 @@ system_ss.add(when: 'CONFIG_PIIX', if_true: files('piix.c'))
system_ss.add(when: 'CONFIG_SMC37C669', if_true: files('smc37c669-superio.c'))
system_ss.add(when: 'CONFIG_VT82C686', if_true: files('vt82c686.c'))
+specific_ss.add(when: 'CONFIG_PC87560', if_true: files('pc87560.c'))
specific_ss.add(when: 'CONFIG_LPC_ICH9', if_true: files('lpc_ich9.c'))
diff --git a/hw/usb/meson.build b/hw/usb/meson.build
index d4ba60a91c..429dcd6a24 100644
--- a/hw/usb/meson.build
+++ b/hw/usb/meson.build
@@ -45,6 +45,7 @@ system_ss.add(when: 'CONFIG_USB_STORAGE_UAS', if_true: files('dev-uas.c'))
system_ss.add(when: 'CONFIG_USB_AUDIO', if_true: files('dev-audio.c'))
system_ss.add(when: 'CONFIG_USB_SERIAL', if_true: files('dev-serial.c'))
system_ss.add(when: 'CONFIG_USB_NETWORK', if_true: files('dev-network.c'))
+system_ss.add(when: 'CONFIG_PC87560', if_true: files('pc87560-ohci.c'))
if host_os != 'windows'
system_ss.add(when: 'CONFIG_USB_STORAGE_MTP', if_true: files('dev-mtp.c'))
endif
--
2.54.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 7/7] hw/hppa: Add NS 87560 SuperIO chip to HP C3700 machine
2026-09-17 21:33 [PATCH 0/7] NS 87560 SuperIO chip Helge Deller
` (5 preceding siblings ...)
2026-09-17 21:33 ` [PATCH 6/7] hw/pc87560: Wire up " Helge Deller
@ 2026-09-17 21:34 ` Helge Deller
6 siblings, 0 replies; 8+ messages in thread
From: Helge Deller @ 2026-09-17 21:34 UTC (permalink / raw)
To: qemu-devel
Cc: Michael S. Tsirkin, Denis V. Lunev, Helge Deller, John Snow,
Richard Henderson, Paolo Bonzini, Abizer Lokhandwala, qemu-block
From: Abizer Lokhandwala <abizerlokhandwalastd10@gmail.com>
Due to the lack of the emulated NS 87560 SuperIO chip, we used until now the
emulated serial ports of a Diva GSP management board as a temporary
replacement. Since the NS SuperIO chip emulation was just added in the
previous commits, switch over to use it which allows us to emulate the physical
hardware more closely.
Signed-off-by: Abizer Lokhandwala <abizerlokhandwalastd10@gmail.com>
Signed-off-by: Helge Deller <deller@gmx.de>
---
hw/hppa/machine.c | 41 ++++++++++++++++++++++++++++++++++-------
1 file changed, 34 insertions(+), 7 deletions(-)
diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c
index 98931481b2..96133f09ba 100644
--- a/hw/hppa/machine.c
+++ b/hw/hppa/machine.c
@@ -394,7 +394,7 @@ static TranslateFn *machine_HP_common_init_cpus(MachineState *machine)
* Last creation step: Add NICs, graphics & load firmware
*/
static void machine_HP_common_init_tail(MachineState *machine, PCIBus *pci_bus,
- TranslateFn *translate)
+ TranslateFn *translate, bool create_diva)
{
const char *kernel_filename = machine->kernel_filename;
MachineClass *mc = MACHINE_GET_CLASS(machine);
@@ -426,7 +426,7 @@ static void machine_HP_common_init_tail(MachineState *machine, PCIBus *pci_bus,
pci_init_nic_devices(pci_bus, mc->default_nic);
}
- if (pci_bus && hppa_is_pa20(&cpu[0]->env)) {
+ if (pci_bus && hppa_is_pa20(&cpu[0]->env) && create_diva) {
/* BMC board: HP Diva GSP PCI card */
pci_dev = pci_new_multifunction(PCI_DEVFN(2, 0), "diva-gsp");
if (!lasi_dev) {
@@ -442,8 +442,9 @@ static void machine_HP_common_init_tail(MachineState *machine, PCIBus *pci_bus,
/* create USB OHCI controller for USB keyboard & mouse on Astro machines */
if (!lasi_dev && machine->enable_graphics && defaults_enabled()) {
USBBus *usb_bus;
-
- pci_create_simple(pci_bus, -1, "pci-ohci");
+ if (create_diva) {
+ pci_create_simple(pci_bus, -1, "pci-ohci");
+ }
usb_bus = USB_BUS(object_resolve_type_unambiguous(TYPE_USB_BUS,
&error_abort));
usb_create_simple(usb_bus, "usb-kbd");
@@ -647,7 +648,7 @@ static void machine_HP_715_init(MachineState *machine)
}
/* Add NICs, graphics & load firmware */
- machine_HP_common_init_tail(machine, NULL, translate);
+ machine_HP_common_init_tail(machine, NULL, translate, false);
}
/*
@@ -726,7 +727,7 @@ static void machine_HP_B160L_init(MachineState *machine)
}
/* Add NICs, graphics & load firmware */
- machine_HP_common_init_tail(machine, pci_bus, translate);
+ machine_HP_common_init_tail(machine, pci_bus, translate, false);
}
static AstroState *astro_init(void)
@@ -771,6 +772,32 @@ static void machine_HP_C3700_init(MachineState *machine)
pci_bus = PCI_BUS(qdev_get_child_bus(DEVICE(astro->elroy[0]), "pci"));
assert(pci_bus);
+ /* The C3700 has a SuperIO chip, while the A400 hasn't. */
+ MachineClass *mc = MACHINE_GET_CLASS(machine);
+ bool is_C3700 = strcmp(mc->name, "C3700") == 0;
+ if (is_C3700) {
+ PCIDevice *ide_pdev = pci_create_simple(pci_bus, PCI_DEVFN(2, 0),
+ "pc87560-ide");
+ PCIDevice *usb_pdev = pci_create_simple(pci_bus, PCI_DEVFN(2, 2),
+ "pc87560-ohci");
+ PCIDevice *sio_pdev = pci_new(PCI_DEVFN(2, 1), "pc87560-superio");
+ if (serial_hd(0)) {
+ qdev_prop_set_chr(DEVICE(sio_pdev), "serial0", serial_hd(0));
+ }
+ if (serial_hd(1)) {
+ qdev_prop_set_chr(DEVICE(sio_pdev), "serial1", serial_hd(1));
+ }
+ pci_realize_and_unref(sio_pdev, pci_bus, &error_fatal);
+
+ qdev_connect_gpio_out(DEVICE(ide_pdev), 0,
+ qdev_get_gpio_in_named(DEVICE(sio_pdev), "pic-irq", 7));
+ qdev_connect_gpio_out(DEVICE(usb_pdev), 0,
+ qdev_get_gpio_in_named(DEVICE(sio_pdev), "pic-irq", 1));
+
+ qdev_connect_gpio_out_named(DEVICE(sio_pdev), "pic-out", 0,
+ pci_allocate_irq(usb_pdev));
+ }
+
/* SCSI disk setup. */
if (drive_get_max_bus(IF_SCSI) >= 0) {
DeviceState *dev = DEVICE(pci_create_simple(pci_bus, -1, "lsi53c895a"));
@@ -778,7 +805,7 @@ static void machine_HP_C3700_init(MachineState *machine)
}
/* Add NICs, graphics & load firmware */
- machine_HP_common_init_tail(machine, pci_bus, translate);
+ machine_HP_common_init_tail(machine, pci_bus, translate, !is_C3700);
}
/*
--
2.54.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-09-17 21:36 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-17 21:33 [PATCH 0/7] NS 87560 SuperIO chip Helge Deller
2026-09-17 21:33 ` [PATCH 1/7] hw/pci/pci_ids: Add PCI vendor ID and IDs for NS chips Helge Deller
2026-09-17 21:33 ` [PATCH 2/7] hw/isa/pc87560: Add main isa code for NS 87560 SuperIO chip Helge Deller
2026-09-17 21:33 ` [PATCH 3/7] hw/ide/pc87560-ide: Add IDE emulation of " Helge Deller
2026-09-17 21:33 ` [PATCH 4/7] hw/usb/hcd-ohci: Add big endian and consistency check support Helge Deller
2026-09-17 21:33 ` [PATCH 5/7] hw/usb/pc87560-ohci: Add USB OHCI emulation of NS 87560 SuperIO chip Helge Deller
2026-09-17 21:33 ` [PATCH 6/7] hw/pc87560: Wire up " Helge Deller
2026-09-17 21:34 ` [PATCH 7/7] hw/hppa: Add NS 87560 SuperIO chip to HP C3700 machine Helge Deller
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.