2010-06-29 04:49:29 +02:00
|
|
|
/*
|
|
|
|
* VT82C686B south bridge support
|
|
|
|
*
|
|
|
|
* Copyright (c) 2008 yajin (yajin@vm-kernel.org)
|
|
|
|
* Copyright (c) 2009 chenming (chenming@rdc.faw.com.cn)
|
|
|
|
* Copyright (c) 2010 Huacai Chen (zltjiangshi@gmail.com)
|
|
|
|
* This code is licensed under the GNU GPL v2.
|
2012-01-13 17:44:23 +01:00
|
|
|
*
|
|
|
|
* Contributions after 2012-01-13 are licensed under the terms of the
|
|
|
|
* GNU GPL, version 2 or (at your option) any later version.
|
2021-03-25 14:50:39 +01:00
|
|
|
*
|
|
|
|
* VT8231 south bridge support and general clean up to allow it
|
|
|
|
* Copyright (c) 2018-2020 BALATON Zoltan
|
2010-06-29 04:49:29 +02:00
|
|
|
*/
|
|
|
|
|
2016-01-26 19:17:30 +01:00
|
|
|
#include "qemu/osdep.h"
|
2013-02-05 17:06:20 +01:00
|
|
|
#include "hw/isa/vt82c686.h"
|
2013-02-04 15:40:22 +01:00
|
|
|
#include "hw/pci/pci.h"
|
2019-08-12 07:23:51 +02:00
|
|
|
#include "hw/qdev-properties.h"
|
2022-06-13 19:24:55 +02:00
|
|
|
#include "hw/ide/pci.h"
|
2013-02-05 17:06:20 +01:00
|
|
|
#include "hw/isa/isa.h"
|
2018-03-08 23:39:40 +01:00
|
|
|
#include "hw/isa/superio.h"
|
2021-01-09 21:16:36 +01:00
|
|
|
#include "hw/intc/i8259.h"
|
|
|
|
#include "hw/irq.h"
|
|
|
|
#include "hw/dma/i8257.h"
|
2022-09-01 13:41:22 +02:00
|
|
|
#include "hw/usb/hcd-uhci.h"
|
2021-01-09 21:16:36 +01:00
|
|
|
#include "hw/timer/i8254.h"
|
|
|
|
#include "hw/rtc/mc146818rtc.h"
|
2019-08-12 07:23:45 +02:00
|
|
|
#include "migration/vmstate.h"
|
2013-02-05 17:06:20 +01:00
|
|
|
#include "hw/isa/apm.h"
|
|
|
|
#include "hw/acpi/acpi.h"
|
|
|
|
#include "hw/i2c/pm_smbus.h"
|
pci: Convert uses of pci_create() etc. with Coccinelle
Replace
dev = pci_create(bus, type_name);
...
qdev_init_nofail(dev);
by
dev = pci_new(type_name);
...
pci_realize_and_unref(dev, bus, &error_fatal);
and similarly for pci_create_multifunction().
Recent commit "qdev: New qdev_new(), qdev_realize(), etc." explains
why.
Coccinelle script:
@@
expression dev, bus, expr;
expression list args;
@@
- dev = pci_create(bus, args);
+ dev = pci_new(args);
... when != dev = expr
- qdev_init_nofail(&dev->qdev);
+ pci_realize_and_unref(dev, bus, &error_fatal);
@@
expression dev, bus, expr;
expression list args;
expression d;
@@
- dev = pci_create(bus, args);
+ dev = pci_new(args);
(
d = &dev->qdev;
|
d = DEVICE(dev);
)
... when != dev = expr
- qdev_init_nofail(d);
+ pci_realize_and_unref(dev, bus, &error_fatal);
@@
expression dev, bus, expr;
expression list args;
@@
- dev = pci_create(bus, args);
+ dev = pci_new(args);
... when != dev = expr
- qdev_init_nofail(DEVICE(dev));
+ pci_realize_and_unref(dev, bus, &error_fatal);
@@
expression dev, bus, expr;
expression list args;
@@
- dev = DEVICE(pci_create(bus, args));
+ PCIDevice *pci_dev; // TODO move
+ pci_dev = pci_new(args);
+ dev = DEVICE(pci_dev);
... when != dev = expr
- qdev_init_nofail(dev);
+ pci_realize_and_unref(pci_dev, bus, &error_fatal);
@@
expression dev, bus, expr;
expression list args;
@@
- dev = pci_create_multifunction(bus, args);
+ dev = pci_new_multifunction(args);
... when != dev = expr
- qdev_init_nofail(&dev->qdev);
+ pci_realize_and_unref(dev, bus, &error_fatal);
@@
expression bus, expr;
expression list args;
identifier dev;
@@
- PCIDevice *dev = pci_create_multifunction(bus, args);
+ PCIDevice *dev = pci_new_multifunction(args);
... when != dev = expr
- qdev_init_nofail(&dev->qdev);
+ pci_realize_and_unref(dev, bus, &error_fatal);
@@
expression dev, bus, expr;
expression list args;
@@
- dev = pci_create_multifunction(bus, args);
+ dev = pci_new_multifunction(args);
... when != dev = expr
- qdev_init_nofail(DEVICE(dev));
+ pci_realize_and_unref(dev, bus, &error_fatal);
Missing #include "qapi/error.h" added manually, whitespace changes
minimized manually, @pci_dev declarations moved manually.
Cc: Michael S. Tsirkin <mst@redhat.com>
Cc: Marcel Apfelbaum <marcel.apfelbaum@gmail.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Message-Id: <20200610053247.1583243-16-armbru@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
2020-06-10 07:32:04 +02:00
|
|
|
#include "qapi/error.h"
|
2021-01-09 21:16:36 +01:00
|
|
|
#include "qemu/log.h"
|
2019-05-23 16:35:07 +02:00
|
|
|
#include "qemu/module.h"
|
2021-01-09 21:16:36 +01:00
|
|
|
#include "qemu/range.h"
|
2012-12-17 18:20:00 +01:00
|
|
|
#include "qemu/timer.h"
|
2021-01-02 11:43:35 +01:00
|
|
|
#include "trace.h"
|
2010-06-29 04:49:29 +02:00
|
|
|
|
2021-01-09 21:16:36 +01:00
|
|
|
#define TYPE_VIA_PM "via-pm"
|
|
|
|
OBJECT_DECLARE_SIMPLE_TYPE(ViaPMState, VIA_PM)
|
2010-06-29 04:49:29 +02:00
|
|
|
|
2021-01-09 21:16:36 +01:00
|
|
|
struct ViaPMState {
|
2010-06-29 04:49:29 +02:00
|
|
|
PCIDevice dev;
|
2012-11-23 08:29:27 +01:00
|
|
|
MemoryRegion io;
|
2012-02-23 13:45:16 +01:00
|
|
|
ACPIREGS ar;
|
2010-06-29 04:49:29 +02:00
|
|
|
APMState apm;
|
|
|
|
PMSMBus smb;
|
2020-09-03 22:43:22 +02:00
|
|
|
};
|
2010-06-29 04:49:29 +02:00
|
|
|
|
2021-01-09 21:16:36 +01:00
|
|
|
static void pm_io_space_update(ViaPMState *s)
|
2010-06-29 04:49:29 +02:00
|
|
|
{
|
2021-01-09 21:16:36 +01:00
|
|
|
uint32_t pmbase = pci_get_long(s->dev.config + 0x48) & 0xff80UL;
|
2010-06-29 04:49:29 +02:00
|
|
|
|
2012-11-23 08:29:27 +01:00
|
|
|
memory_region_transaction_begin();
|
2021-01-09 21:16:36 +01:00
|
|
|
memory_region_set_address(&s->io, pmbase);
|
|
|
|
memory_region_set_enabled(&s->io, s->dev.config[0x41] & BIT(7));
|
2012-11-23 08:29:27 +01:00
|
|
|
memory_region_transaction_commit();
|
2010-06-29 04:49:29 +02:00
|
|
|
}
|
|
|
|
|
2021-01-09 21:16:36 +01:00
|
|
|
static void smb_io_space_update(ViaPMState *s)
|
2021-01-09 21:16:36 +01:00
|
|
|
{
|
|
|
|
uint32_t smbase = pci_get_long(s->dev.config + 0x90) & 0xfff0UL;
|
|
|
|
|
|
|
|
memory_region_transaction_begin();
|
|
|
|
memory_region_set_address(&s->smb.io, smbase);
|
|
|
|
memory_region_set_enabled(&s->smb.io, s->dev.config[0xd2] & BIT(0));
|
|
|
|
memory_region_transaction_commit();
|
|
|
|
}
|
|
|
|
|
2010-06-29 04:49:29 +02:00
|
|
|
static int vmstate_acpi_post_load(void *opaque, int version_id)
|
|
|
|
{
|
2021-01-09 21:16:36 +01:00
|
|
|
ViaPMState *s = opaque;
|
2010-06-29 04:49:29 +02:00
|
|
|
|
|
|
|
pm_io_space_update(s);
|
2021-01-09 21:16:36 +01:00
|
|
|
smb_io_space_update(s);
|
2010-06-29 04:49:29 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const VMStateDescription vmstate_acpi = {
|
|
|
|
.name = "vt82c686b_pm",
|
|
|
|
.version_id = 1,
|
|
|
|
.minimum_version_id = 1,
|
|
|
|
.post_load = vmstate_acpi_post_load,
|
2014-04-16 15:32:32 +02:00
|
|
|
.fields = (VMStateField[]) {
|
2021-01-09 21:16:36 +01:00
|
|
|
VMSTATE_PCI_DEVICE(dev, ViaPMState),
|
|
|
|
VMSTATE_UINT16(ar.pm1.evt.sts, ViaPMState),
|
|
|
|
VMSTATE_UINT16(ar.pm1.evt.en, ViaPMState),
|
|
|
|
VMSTATE_UINT16(ar.pm1.cnt.cnt, ViaPMState),
|
|
|
|
VMSTATE_STRUCT(apm, ViaPMState, 0, vmstate_apm, APMState),
|
|
|
|
VMSTATE_TIMER_PTR(ar.tmr.timer, ViaPMState),
|
|
|
|
VMSTATE_INT64(ar.tmr.overflow_time, ViaPMState),
|
2010-06-29 04:49:29 +02:00
|
|
|
VMSTATE_END_OF_LIST()
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-01-09 21:16:36 +01:00
|
|
|
static void pm_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int len)
|
|
|
|
{
|
2021-01-09 21:16:36 +01:00
|
|
|
ViaPMState *s = VIA_PM(d);
|
2021-01-09 21:16:36 +01:00
|
|
|
|
2021-01-09 21:16:36 +01:00
|
|
|
trace_via_pm_write(addr, val, len);
|
|
|
|
pci_default_write_config(d, addr, val, len);
|
2021-01-09 21:16:36 +01:00
|
|
|
if (ranges_overlap(addr, len, 0x48, 4)) {
|
|
|
|
uint32_t v = pci_get_long(s->dev.config + 0x48);
|
|
|
|
pci_set_long(s->dev.config + 0x48, (v & 0xff80UL) | 1);
|
|
|
|
}
|
|
|
|
if (range_covers_byte(addr, len, 0x41)) {
|
|
|
|
pm_io_space_update(s);
|
|
|
|
}
|
2021-01-09 21:16:36 +01:00
|
|
|
if (ranges_overlap(addr, len, 0x90, 4)) {
|
|
|
|
uint32_t v = pci_get_long(s->dev.config + 0x90);
|
|
|
|
pci_set_long(s->dev.config + 0x90, (v & 0xfff0UL) | 1);
|
|
|
|
}
|
|
|
|
if (range_covers_byte(addr, len, 0xd2)) {
|
|
|
|
s->dev.config[0xd2] &= 0xf;
|
|
|
|
smb_io_space_update(s);
|
|
|
|
}
|
2021-01-09 21:16:36 +01:00
|
|
|
}
|
|
|
|
|
2021-01-09 21:16:36 +01:00
|
|
|
static void pm_io_write(void *op, hwaddr addr, uint64_t data, unsigned size)
|
|
|
|
{
|
|
|
|
trace_via_pm_io_write(addr, data, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint64_t pm_io_read(void *op, hwaddr addr, unsigned size)
|
|
|
|
{
|
|
|
|
trace_via_pm_io_read(addr, 0, size);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const MemoryRegionOps pm_io_ops = {
|
|
|
|
.read = pm_io_read,
|
|
|
|
.write = pm_io_write,
|
|
|
|
.endianness = DEVICE_NATIVE_ENDIAN,
|
|
|
|
.impl = {
|
|
|
|
.min_access_size = 1,
|
|
|
|
.max_access_size = 1,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2021-01-09 21:16:36 +01:00
|
|
|
static void pm_update_sci(ViaPMState *s)
|
2021-01-09 21:16:36 +01:00
|
|
|
{
|
|
|
|
int sci_level, pmsts;
|
|
|
|
|
|
|
|
pmsts = acpi_pm1_evt_get_sts(&s->ar);
|
|
|
|
sci_level = (((pmsts & s->ar.pm1.evt.en) &
|
|
|
|
(ACPI_BITMASK_RT_CLOCK_ENABLE |
|
|
|
|
ACPI_BITMASK_POWER_BUTTON_ENABLE |
|
|
|
|
ACPI_BITMASK_GLOBAL_LOCK_ENABLE |
|
|
|
|
ACPI_BITMASK_TIMER_ENABLE)) != 0);
|
2021-03-23 21:52:25 +01:00
|
|
|
if (pci_get_byte(s->dev.config + PCI_INTERRUPT_PIN)) {
|
|
|
|
/*
|
|
|
|
* FIXME:
|
|
|
|
* Fix device model that realizes this PM device and remove
|
|
|
|
* this work around.
|
|
|
|
* The device model should wire SCI and setup
|
|
|
|
* PCI_INTERRUPT_PIN properly.
|
|
|
|
* If PIN# = 0(interrupt pin isn't used), don't raise SCI as
|
|
|
|
* work around.
|
|
|
|
*/
|
|
|
|
pci_set_irq(&s->dev, sci_level);
|
|
|
|
}
|
2021-01-09 21:16:36 +01:00
|
|
|
/* schedule a timer interruption if needed */
|
|
|
|
acpi_pm_tmr_update(&s->ar, (s->ar.pm1.evt.en & ACPI_BITMASK_TIMER_ENABLE) &&
|
|
|
|
!(pmsts & ACPI_BITMASK_TIMER_STATUS));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pm_tmr_timer(ACPIREGS *ar)
|
|
|
|
{
|
2021-01-09 21:16:36 +01:00
|
|
|
ViaPMState *s = container_of(ar, ViaPMState, ar);
|
2021-01-09 21:16:36 +01:00
|
|
|
pm_update_sci(s);
|
|
|
|
}
|
|
|
|
|
2021-01-09 21:16:36 +01:00
|
|
|
static void via_pm_reset(DeviceState *d)
|
2021-01-09 21:16:36 +01:00
|
|
|
{
|
2021-01-09 21:16:36 +01:00
|
|
|
ViaPMState *s = VIA_PM(d);
|
2021-01-09 21:16:36 +01:00
|
|
|
|
2021-01-09 21:16:36 +01:00
|
|
|
memset(s->dev.config + PCI_CONFIG_HEADER_SIZE, 0,
|
|
|
|
PCI_CONFIG_SPACE_SIZE - PCI_CONFIG_HEADER_SIZE);
|
|
|
|
/* Power Management IO base */
|
|
|
|
pci_set_long(s->dev.config + 0x48, 1);
|
2021-01-09 21:16:36 +01:00
|
|
|
/* SMBus IO base */
|
|
|
|
pci_set_long(s->dev.config + 0x90, 1);
|
|
|
|
|
2021-03-23 21:52:26 +01:00
|
|
|
acpi_pm1_evt_reset(&s->ar);
|
|
|
|
acpi_pm1_cnt_reset(&s->ar);
|
|
|
|
acpi_pm_tmr_reset(&s->ar);
|
|
|
|
pm_update_sci(s);
|
|
|
|
|
2021-01-09 21:16:36 +01:00
|
|
|
pm_io_space_update(s);
|
2021-01-09 21:16:36 +01:00
|
|
|
smb_io_space_update(s);
|
|
|
|
}
|
|
|
|
|
2021-01-09 21:16:36 +01:00
|
|
|
static void via_pm_realize(PCIDevice *dev, Error **errp)
|
2010-06-29 04:49:29 +02:00
|
|
|
{
|
2021-01-09 21:16:36 +01:00
|
|
|
ViaPMState *s = VIA_PM(dev);
|
2010-06-29 04:49:29 +02:00
|
|
|
|
2021-01-09 21:16:36 +01:00
|
|
|
pci_set_word(dev->config + PCI_STATUS, PCI_STATUS_FAST_BACK |
|
2010-06-29 04:49:29 +02:00
|
|
|
PCI_STATUS_DEVSEL_MEDIUM);
|
|
|
|
|
2019-05-28 18:40:17 +02:00
|
|
|
pm_smbus_init(DEVICE(s), &s->smb, false);
|
2021-01-09 21:16:36 +01:00
|
|
|
memory_region_add_subregion(pci_address_space_io(dev), 0, &s->smb.io);
|
|
|
|
memory_region_set_enabled(&s->smb.io, false);
|
2010-06-29 04:49:29 +02:00
|
|
|
|
2012-09-19 13:50:03 +02:00
|
|
|
apm_init(dev, &s->apm, NULL, s);
|
2010-06-29 04:49:29 +02:00
|
|
|
|
2021-01-09 21:16:36 +01:00
|
|
|
memory_region_init_io(&s->io, OBJECT(dev), &pm_io_ops, s, "via-pm", 128);
|
2021-01-09 21:16:36 +01:00
|
|
|
memory_region_add_subregion(pci_address_space_io(dev), 0, &s->io);
|
2012-11-23 08:29:27 +01:00
|
|
|
memory_region_set_enabled(&s->io, false);
|
2010-06-29 04:49:29 +02:00
|
|
|
|
2012-11-22 12:12:30 +01:00
|
|
|
acpi_pm_tmr_init(&s->ar, pm_tmr_timer, &s->io);
|
2012-11-22 13:25:10 +01:00
|
|
|
acpi_pm1_evt_init(&s->ar, pm_tmr_timer, &s->io);
|
2021-02-18 06:51:12 +01:00
|
|
|
acpi_pm1_cnt_init(&s->ar, &s->io, false, false, 2, false);
|
2010-06-29 04:49:29 +02:00
|
|
|
}
|
|
|
|
|
2021-01-09 21:16:36 +01:00
|
|
|
typedef struct via_pm_init_info {
|
|
|
|
uint16_t device_id;
|
|
|
|
} ViaPMInitInfo;
|
|
|
|
|
2011-12-04 19:22:06 +01:00
|
|
|
static void via_pm_class_init(ObjectClass *klass, void *data)
|
|
|
|
{
|
2011-12-08 04:34:16 +01:00
|
|
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
2011-12-04 19:22:06 +01:00
|
|
|
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
2021-01-09 21:16:36 +01:00
|
|
|
ViaPMInitInfo *info = data;
|
2011-12-04 19:22:06 +01:00
|
|
|
|
2021-01-09 21:16:36 +01:00
|
|
|
k->realize = via_pm_realize;
|
2011-12-04 19:22:06 +01:00
|
|
|
k->config_write = pm_write_config;
|
|
|
|
k->vendor_id = PCI_VENDOR_ID_VIA;
|
2021-01-09 21:16:36 +01:00
|
|
|
k->device_id = info->device_id;
|
2011-12-04 19:22:06 +01:00
|
|
|
k->class_id = PCI_CLASS_BRIDGE_OTHER;
|
|
|
|
k->revision = 0x40;
|
2021-01-09 21:16:36 +01:00
|
|
|
dc->reset = via_pm_reset;
|
2021-01-09 21:16:36 +01:00
|
|
|
/* Reason: part of VIA south bridge, does not exist stand alone */
|
|
|
|
dc->user_creatable = false;
|
2011-12-08 04:34:16 +01:00
|
|
|
dc->vmsd = &vmstate_acpi;
|
2011-12-04 19:22:06 +01:00
|
|
|
}
|
|
|
|
|
2013-01-10 16:19:07 +01:00
|
|
|
static const TypeInfo via_pm_info = {
|
2021-01-09 21:16:36 +01:00
|
|
|
.name = TYPE_VIA_PM,
|
2011-12-08 04:34:16 +01:00
|
|
|
.parent = TYPE_PCI_DEVICE,
|
2021-01-09 21:16:36 +01:00
|
|
|
.instance_size = sizeof(ViaPMState),
|
|
|
|
.abstract = true,
|
2017-09-27 21:56:34 +02:00
|
|
|
.interfaces = (InterfaceInfo[]) {
|
|
|
|
{ INTERFACE_CONVENTIONAL_PCI_DEVICE },
|
|
|
|
{ },
|
|
|
|
},
|
2010-06-29 04:49:29 +02:00
|
|
|
};
|
|
|
|
|
2021-01-09 21:16:36 +01:00
|
|
|
static const ViaPMInitInfo vt82c686b_pm_init_info = {
|
|
|
|
.device_id = PCI_DEVICE_ID_VIA_82C686B_PM,
|
|
|
|
};
|
|
|
|
|
2022-09-01 13:41:23 +02:00
|
|
|
#define TYPE_VT82C686B_PM "vt82c686b-pm"
|
|
|
|
|
2021-01-09 21:16:36 +01:00
|
|
|
static const TypeInfo vt82c686b_pm_info = {
|
|
|
|
.name = TYPE_VT82C686B_PM,
|
|
|
|
.parent = TYPE_VIA_PM,
|
|
|
|
.class_init = via_pm_class_init,
|
|
|
|
.class_data = (void *)&vt82c686b_pm_init_info,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const ViaPMInitInfo vt8231_pm_init_info = {
|
|
|
|
.device_id = PCI_DEVICE_ID_VIA_8231_PM,
|
|
|
|
};
|
|
|
|
|
2022-09-01 13:41:23 +02:00
|
|
|
#define TYPE_VT8231_PM "vt8231-pm"
|
|
|
|
|
2021-01-09 21:16:36 +01:00
|
|
|
static const TypeInfo vt8231_pm_info = {
|
|
|
|
.name = TYPE_VT8231_PM,
|
|
|
|
.parent = TYPE_VIA_PM,
|
|
|
|
.class_init = via_pm_class_init,
|
|
|
|
.class_data = (void *)&vt8231_pm_init_info,
|
|
|
|
};
|
|
|
|
|
2021-01-09 21:16:36 +01:00
|
|
|
|
2021-03-25 14:50:39 +01:00
|
|
|
#define TYPE_VIA_SUPERIO "via-superio"
|
|
|
|
OBJECT_DECLARE_SIMPLE_TYPE(ViaSuperIOState, VIA_SUPERIO)
|
|
|
|
|
|
|
|
struct ViaSuperIOState {
|
|
|
|
ISASuperIODevice superio;
|
2021-01-09 21:16:36 +01:00
|
|
|
uint8_t regs[0x100];
|
2021-03-25 14:50:39 +01:00
|
|
|
const MemoryRegionOps *io_ops;
|
2021-01-09 21:16:36 +01:00
|
|
|
MemoryRegion io;
|
2021-03-25 14:50:39 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
static inline void via_superio_io_enable(ViaSuperIOState *s, bool enable)
|
|
|
|
{
|
|
|
|
memory_region_set_enabled(&s->io, enable);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void via_superio_realize(DeviceState *d, Error **errp)
|
|
|
|
{
|
|
|
|
ViaSuperIOState *s = VIA_SUPERIO(d);
|
|
|
|
ISASuperIOClass *ic = ISA_SUPERIO_GET_CLASS(s);
|
|
|
|
Error *local_err = NULL;
|
|
|
|
|
|
|
|
assert(s->io_ops);
|
|
|
|
ic->parent_realize(d, &local_err);
|
|
|
|
if (local_err) {
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
memory_region_init_io(&s->io, OBJECT(d), s->io_ops, s, "via-superio", 2);
|
|
|
|
memory_region_set_enabled(&s->io, false);
|
|
|
|
/* The floppy also uses 0x3f0 and 0x3f1 but this seems to work anyway */
|
|
|
|
memory_region_add_subregion(isa_address_space_io(ISA_DEVICE(s)), 0x3f0,
|
|
|
|
&s->io);
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint64_t via_superio_cfg_read(void *opaque, hwaddr addr, unsigned size)
|
|
|
|
{
|
|
|
|
ViaSuperIOState *sc = opaque;
|
|
|
|
uint8_t idx = sc->regs[0];
|
|
|
|
uint8_t val = sc->regs[idx];
|
|
|
|
|
|
|
|
if (addr == 0) {
|
|
|
|
return idx;
|
|
|
|
}
|
|
|
|
if (addr == 1 && idx == 0) {
|
|
|
|
val = 0; /* reading reg 0 where we store index value */
|
|
|
|
}
|
|
|
|
trace_via_superio_read(idx, val);
|
|
|
|
return val;
|
|
|
|
}
|
2021-01-09 21:16:36 +01:00
|
|
|
|
2021-03-25 14:50:39 +01:00
|
|
|
static void via_superio_class_init(ObjectClass *klass, void *data)
|
2021-01-09 21:16:36 +01:00
|
|
|
{
|
2021-03-25 14:50:39 +01:00
|
|
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
|
|
|
ISASuperIOClass *sc = ISA_SUPERIO_CLASS(klass);
|
|
|
|
|
|
|
|
sc->parent_realize = dc->realize;
|
|
|
|
dc->realize = via_superio_realize;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const TypeInfo via_superio_info = {
|
|
|
|
.name = TYPE_VIA_SUPERIO,
|
|
|
|
.parent = TYPE_ISA_SUPERIO,
|
|
|
|
.instance_size = sizeof(ViaSuperIOState),
|
|
|
|
.class_size = sizeof(ISASuperIOClass),
|
|
|
|
.class_init = via_superio_class_init,
|
|
|
|
.abstract = true,
|
|
|
|
};
|
|
|
|
|
|
|
|
#define TYPE_VT82C686B_SUPERIO "vt82c686b-superio"
|
2021-01-09 21:16:36 +01:00
|
|
|
|
2021-03-25 14:50:39 +01:00
|
|
|
static void vt82c686b_superio_cfg_write(void *opaque, hwaddr addr,
|
|
|
|
uint64_t data, unsigned size)
|
2021-01-09 21:16:36 +01:00
|
|
|
{
|
2021-03-25 14:50:39 +01:00
|
|
|
ViaSuperIOState *sc = opaque;
|
2021-01-09 21:16:36 +01:00
|
|
|
uint8_t idx = sc->regs[0];
|
2021-01-09 21:16:36 +01:00
|
|
|
|
2021-01-09 21:16:36 +01:00
|
|
|
if (addr == 0) { /* config index register */
|
|
|
|
sc->regs[0] = data;
|
2021-01-09 21:16:36 +01:00
|
|
|
return;
|
|
|
|
}
|
2021-01-09 21:16:36 +01:00
|
|
|
|
|
|
|
/* config data register */
|
|
|
|
trace_via_superio_write(idx, data);
|
2021-01-09 21:16:36 +01:00
|
|
|
switch (idx) {
|
|
|
|
case 0x00 ... 0xdf:
|
|
|
|
case 0xe4:
|
|
|
|
case 0xe5:
|
|
|
|
case 0xe9 ... 0xed:
|
|
|
|
case 0xf3:
|
|
|
|
case 0xf5:
|
|
|
|
case 0xf7:
|
|
|
|
case 0xf9 ... 0xfb:
|
|
|
|
case 0xfd ... 0xff:
|
2021-01-09 21:16:36 +01:00
|
|
|
/* ignore write to read only registers */
|
|
|
|
return;
|
2021-01-09 21:16:36 +01:00
|
|
|
/* case 0xe6 ... 0xe8: Should set base port of parallel and serial */
|
|
|
|
default:
|
2021-01-09 21:16:36 +01:00
|
|
|
qemu_log_mask(LOG_UNIMP,
|
|
|
|
"via_superio_cfg: unimplemented register 0x%x\n", idx);
|
2021-01-09 21:16:36 +01:00
|
|
|
break;
|
|
|
|
}
|
2021-01-09 21:16:36 +01:00
|
|
|
sc->regs[idx] = data;
|
2021-01-09 21:16:36 +01:00
|
|
|
}
|
|
|
|
|
2021-03-25 14:50:39 +01:00
|
|
|
static const MemoryRegionOps vt82c686b_superio_cfg_ops = {
|
|
|
|
.read = via_superio_cfg_read,
|
|
|
|
.write = vt82c686b_superio_cfg_write,
|
2021-01-09 21:16:36 +01:00
|
|
|
.endianness = DEVICE_NATIVE_ENDIAN,
|
|
|
|
.impl = {
|
|
|
|
.min_access_size = 1,
|
|
|
|
.max_access_size = 1,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2021-03-25 14:50:39 +01:00
|
|
|
static void vt82c686b_superio_reset(DeviceState *dev)
|
|
|
|
{
|
|
|
|
ViaSuperIOState *s = VIA_SUPERIO(dev);
|
|
|
|
|
|
|
|
memset(s->regs, 0, sizeof(s->regs));
|
|
|
|
/* Device ID */
|
|
|
|
vt82c686b_superio_cfg_write(s, 0, 0xe0, 1);
|
|
|
|
vt82c686b_superio_cfg_write(s, 1, 0x3c, 1);
|
|
|
|
/* Function select - all disabled */
|
|
|
|
vt82c686b_superio_cfg_write(s, 0, 0xe2, 1);
|
|
|
|
vt82c686b_superio_cfg_write(s, 1, 0x03, 1);
|
|
|
|
/* Floppy ctrl base addr 0x3f0-7 */
|
|
|
|
vt82c686b_superio_cfg_write(s, 0, 0xe3, 1);
|
|
|
|
vt82c686b_superio_cfg_write(s, 1, 0xfc, 1);
|
|
|
|
/* Parallel port base addr 0x378-f */
|
|
|
|
vt82c686b_superio_cfg_write(s, 0, 0xe6, 1);
|
|
|
|
vt82c686b_superio_cfg_write(s, 1, 0xde, 1);
|
|
|
|
/* Serial port 1 base addr 0x3f8-f */
|
|
|
|
vt82c686b_superio_cfg_write(s, 0, 0xe7, 1);
|
|
|
|
vt82c686b_superio_cfg_write(s, 1, 0xfe, 1);
|
|
|
|
/* Serial port 2 base addr 0x2f8-f */
|
|
|
|
vt82c686b_superio_cfg_write(s, 0, 0xe8, 1);
|
|
|
|
vt82c686b_superio_cfg_write(s, 1, 0xbe, 1);
|
|
|
|
|
|
|
|
vt82c686b_superio_cfg_write(s, 0, 0, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void vt82c686b_superio_init(Object *obj)
|
|
|
|
{
|
|
|
|
VIA_SUPERIO(obj)->io_ops = &vt82c686b_superio_cfg_ops;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void vt82c686b_superio_class_init(ObjectClass *klass, void *data)
|
2021-01-09 21:16:36 +01:00
|
|
|
{
|
2021-03-25 14:50:39 +01:00
|
|
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
|
|
|
ISASuperIOClass *sc = ISA_SUPERIO_CLASS(klass);
|
|
|
|
|
|
|
|
dc->reset = vt82c686b_superio_reset;
|
|
|
|
sc->serial.count = 2;
|
|
|
|
sc->parallel.count = 1;
|
|
|
|
sc->ide.count = 0; /* emulated by via-ide */
|
|
|
|
sc->floppy.count = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const TypeInfo vt82c686b_superio_info = {
|
|
|
|
.name = TYPE_VT82C686B_SUPERIO,
|
|
|
|
.parent = TYPE_VIA_SUPERIO,
|
|
|
|
.instance_size = sizeof(ViaSuperIOState),
|
|
|
|
.instance_init = vt82c686b_superio_init,
|
|
|
|
.class_size = sizeof(ISASuperIOClass),
|
|
|
|
.class_init = vt82c686b_superio_class_init,
|
|
|
|
};
|
|
|
|
|
2021-01-09 21:16:36 +01:00
|
|
|
|
2021-03-25 14:50:39 +01:00
|
|
|
#define TYPE_VT8231_SUPERIO "vt8231-superio"
|
|
|
|
|
|
|
|
static void vt8231_superio_cfg_write(void *opaque, hwaddr addr,
|
|
|
|
uint64_t data, unsigned size)
|
|
|
|
{
|
|
|
|
ViaSuperIOState *sc = opaque;
|
2021-01-09 21:16:36 +01:00
|
|
|
uint8_t idx = sc->regs[0];
|
2021-01-09 21:16:36 +01:00
|
|
|
|
2021-03-25 14:50:39 +01:00
|
|
|
if (addr == 0) { /* config index register */
|
|
|
|
sc->regs[0] = data;
|
|
|
|
return;
|
2021-01-09 21:16:36 +01:00
|
|
|
}
|
2021-03-25 14:50:39 +01:00
|
|
|
|
|
|
|
/* config data register */
|
|
|
|
trace_via_superio_write(idx, data);
|
|
|
|
switch (idx) {
|
|
|
|
case 0x00 ... 0xdf:
|
|
|
|
case 0xe7 ... 0xef:
|
|
|
|
case 0xf0 ... 0xf1:
|
|
|
|
case 0xf5:
|
|
|
|
case 0xf8:
|
|
|
|
case 0xfd:
|
|
|
|
/* ignore write to read only registers */
|
|
|
|
return;
|
|
|
|
default:
|
|
|
|
qemu_log_mask(LOG_UNIMP,
|
|
|
|
"via_superio_cfg: unimplemented register 0x%x\n", idx);
|
|
|
|
break;
|
2021-01-09 21:16:36 +01:00
|
|
|
}
|
2021-03-25 14:50:39 +01:00
|
|
|
sc->regs[idx] = data;
|
2021-01-09 21:16:36 +01:00
|
|
|
}
|
|
|
|
|
2021-03-25 14:50:39 +01:00
|
|
|
static const MemoryRegionOps vt8231_superio_cfg_ops = {
|
|
|
|
.read = via_superio_cfg_read,
|
|
|
|
.write = vt8231_superio_cfg_write,
|
2021-01-09 21:16:36 +01:00
|
|
|
.endianness = DEVICE_NATIVE_ENDIAN,
|
|
|
|
.impl = {
|
|
|
|
.min_access_size = 1,
|
|
|
|
.max_access_size = 1,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2021-03-25 14:50:39 +01:00
|
|
|
static void vt8231_superio_reset(DeviceState *dev)
|
|
|
|
{
|
|
|
|
ViaSuperIOState *s = VIA_SUPERIO(dev);
|
|
|
|
|
|
|
|
memset(s->regs, 0, sizeof(s->regs));
|
|
|
|
/* Device ID */
|
|
|
|
s->regs[0xf0] = 0x3c;
|
|
|
|
/* Device revision */
|
|
|
|
s->regs[0xf1] = 0x01;
|
|
|
|
/* Function select - all disabled */
|
|
|
|
vt8231_superio_cfg_write(s, 0, 0xf2, 1);
|
|
|
|
vt8231_superio_cfg_write(s, 1, 0x03, 1);
|
|
|
|
/* Serial port base addr */
|
|
|
|
vt8231_superio_cfg_write(s, 0, 0xf4, 1);
|
|
|
|
vt8231_superio_cfg_write(s, 1, 0xfe, 1);
|
|
|
|
/* Parallel port base addr */
|
|
|
|
vt8231_superio_cfg_write(s, 0, 0xf6, 1);
|
|
|
|
vt8231_superio_cfg_write(s, 1, 0xde, 1);
|
|
|
|
/* Floppy ctrl base addr */
|
|
|
|
vt8231_superio_cfg_write(s, 0, 0xf7, 1);
|
|
|
|
vt8231_superio_cfg_write(s, 1, 0xfc, 1);
|
|
|
|
|
|
|
|
vt8231_superio_cfg_write(s, 0, 0, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void vt8231_superio_init(Object *obj)
|
|
|
|
{
|
|
|
|
VIA_SUPERIO(obj)->io_ops = &vt8231_superio_cfg_ops;
|
|
|
|
}
|
2021-01-09 21:16:36 +01:00
|
|
|
|
2021-03-25 14:50:39 +01:00
|
|
|
static uint16_t vt8231_superio_serial_iobase(ISASuperIODevice *sio,
|
|
|
|
uint8_t index)
|
|
|
|
{
|
|
|
|
return 0x2f8; /* FIXME: This should be settable via registers f2-f4 */
|
|
|
|
}
|
2021-01-09 21:16:36 +01:00
|
|
|
|
2021-03-25 14:50:39 +01:00
|
|
|
static void vt8231_superio_class_init(ObjectClass *klass, void *data)
|
|
|
|
{
|
|
|
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
|
|
|
ISASuperIOClass *sc = ISA_SUPERIO_CLASS(klass);
|
|
|
|
|
|
|
|
dc->reset = vt8231_superio_reset;
|
|
|
|
sc->serial.count = 1;
|
|
|
|
sc->serial.get_iobase = vt8231_superio_serial_iobase;
|
|
|
|
sc->parallel.count = 1;
|
|
|
|
sc->ide.count = 0; /* emulated by via-ide */
|
|
|
|
sc->floppy.count = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const TypeInfo vt8231_superio_info = {
|
|
|
|
.name = TYPE_VT8231_SUPERIO,
|
|
|
|
.parent = TYPE_VIA_SUPERIO,
|
|
|
|
.instance_size = sizeof(ViaSuperIOState),
|
|
|
|
.instance_init = vt8231_superio_init,
|
|
|
|
.class_size = sizeof(ISASuperIOClass),
|
|
|
|
.class_init = vt8231_superio_class_init,
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2021-03-25 14:50:39 +01:00
|
|
|
#define TYPE_VIA_ISA "via-isa"
|
|
|
|
OBJECT_DECLARE_SIMPLE_TYPE(ViaISAState, VIA_ISA)
|
2021-01-09 21:16:36 +01:00
|
|
|
|
2021-03-25 14:50:39 +01:00
|
|
|
struct ViaISAState {
|
2021-01-09 21:16:36 +01:00
|
|
|
PCIDevice dev;
|
2021-01-09 21:16:36 +01:00
|
|
|
qemu_irq cpu_intr;
|
2023-02-09 10:38:42 +01:00
|
|
|
qemu_irq *isa_irqs_in;
|
2022-09-01 13:41:15 +02:00
|
|
|
ViaSuperIOState via_sio;
|
2023-02-11 00:17:03 +01:00
|
|
|
MC146818RtcState rtc;
|
2022-06-13 19:24:55 +02:00
|
|
|
PCIIDEState ide;
|
2022-09-01 13:41:22 +02:00
|
|
|
UHCIState uhci[2];
|
2022-09-01 13:41:23 +02:00
|
|
|
ViaPMState pm;
|
2022-01-23 21:40:42 +01:00
|
|
|
ViaAC97State ac97;
|
2022-09-01 13:41:24 +02:00
|
|
|
PCIDevice mc97;
|
2021-01-09 21:16:36 +01:00
|
|
|
};
|
|
|
|
|
2021-03-25 14:50:39 +01:00
|
|
|
static const VMStateDescription vmstate_via = {
|
|
|
|
.name = "via-isa",
|
|
|
|
.version_id = 1,
|
|
|
|
.minimum_version_id = 1,
|
|
|
|
.fields = (VMStateField[]) {
|
|
|
|
VMSTATE_PCI_DEVICE(dev, ViaISAState),
|
|
|
|
VMSTATE_END_OF_LIST()
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-06-13 19:24:55 +02:00
|
|
|
static void via_isa_init(Object *obj)
|
|
|
|
{
|
|
|
|
ViaISAState *s = VIA_ISA(obj);
|
|
|
|
|
2022-09-01 13:41:26 +02:00
|
|
|
object_initialize_child(obj, "rtc", &s->rtc, TYPE_MC146818_RTC);
|
2022-06-13 19:24:55 +02:00
|
|
|
object_initialize_child(obj, "ide", &s->ide, TYPE_VIA_IDE);
|
2022-09-01 13:41:22 +02:00
|
|
|
object_initialize_child(obj, "uhci1", &s->uhci[0], TYPE_VT82C686B_USB_UHCI);
|
|
|
|
object_initialize_child(obj, "uhci2", &s->uhci[1], TYPE_VT82C686B_USB_UHCI);
|
2022-09-01 13:41:24 +02:00
|
|
|
object_initialize_child(obj, "ac97", &s->ac97, TYPE_VIA_AC97);
|
|
|
|
object_initialize_child(obj, "mc97", &s->mc97, TYPE_VIA_MC97);
|
2022-06-13 19:24:55 +02:00
|
|
|
}
|
|
|
|
|
2021-03-25 14:50:39 +01:00
|
|
|
static const TypeInfo via_isa_info = {
|
|
|
|
.name = TYPE_VIA_ISA,
|
|
|
|
.parent = TYPE_PCI_DEVICE,
|
|
|
|
.instance_size = sizeof(ViaISAState),
|
2022-06-13 19:24:55 +02:00
|
|
|
.instance_init = via_isa_init,
|
2021-03-25 14:50:39 +01:00
|
|
|
.abstract = true,
|
|
|
|
.interfaces = (InterfaceInfo[]) {
|
|
|
|
{ INTERFACE_CONVENTIONAL_PCI_DEVICE },
|
|
|
|
{ },
|
|
|
|
},
|
2021-01-09 21:16:36 +01:00
|
|
|
};
|
|
|
|
|
Revert "hw/isa/vt82c686: Remove intermediate IRQ forwarder"
To be 'usable', QDev objects (which are QOM objects) must be
1/ initialized (at this point their properties can be modified), then
2/ realized (properties are consumed).
Some devices (objects) might depend on other devices. When creating
the 'QOM composition tree', parent objects can't be 'realized' until
all their children are. We might also have circular dependencies.
A common circular dependency occurs with IRQs. Device (A) has an
output IRQ wired to device (B), and device (B) has one to device (A).
When (A) is realized and connects its IRQ to an unrealized (B), the
IRQ handler on (B) is not yet created. QEMU pass IRQ between objects
as pointer. When (A) poll (B)'s IRQ, it is NULL. Later (B) is realized
and its IRQ pointers are populated, but (A) keeps a reference to a
NULL pointer.
A common pattern to bypass this circular limitation is to use 'proxy'
objects. Proxy (P) is created (and realized) before (A) and (B). Then
(A) and (B) can be created in different order, it doesn't matter: (P)
pointers are already populated.
Commit bb98e0f59cde ("hw/isa/vt82c686: Remove intermediate IRQ
forwarder") neglected the QOM/QDev circular dependency issue, and
removed the 'proxy' between the southbridge, its PCI functions and the
interrupt controller, resulting in PCI functions wiring output IRQs to
'NULL', leading to guest failures (IRQ never delivered) [1] [2].
Since we are entering feature freeze, it is safer to revert the
offending patch until we figure a way to strengthen our APIs.
[1] https://lore.kernel.org/qemu-devel/928a8552-ab62-9e6c-a492-d6453e338b9d@redhat.com/
[2] https://lore.kernel.org/qemu-devel/cover.1677628524.git.balaton@eik.bme.hu/
Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu>
Tested-by: Rene Engel <ReneEngel80@emailn.de>
Reviewed-by: Daniel Henrique Barboza <danielhb413@gmail.com>
Message-Id: <cdfb3c5a42e505450f6803124f27856434c5b298.1677628524.git.balaton@eik.bme.hu>
[PMD: Reworded description]
Inspired-by: Bernhard Beschow <shentey@gmail.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
2023-03-01 01:17:08 +01:00
|
|
|
static void via_isa_request_i8259_irq(void *opaque, int irq, int level)
|
|
|
|
{
|
|
|
|
ViaISAState *s = opaque;
|
|
|
|
qemu_set_irq(s->cpu_intr, level);
|
|
|
|
}
|
|
|
|
|
2023-02-16 21:21:35 +01:00
|
|
|
static int via_isa_get_pci_irq(const ViaISAState *s, int irq_num)
|
|
|
|
{
|
|
|
|
switch (irq_num) {
|
|
|
|
case 0:
|
|
|
|
return s->dev.config[0x55] >> 4;
|
|
|
|
case 1:
|
|
|
|
return s->dev.config[0x56] & 0xf;
|
|
|
|
case 2:
|
|
|
|
return s->dev.config[0x56] >> 4;
|
|
|
|
case 3:
|
|
|
|
return s->dev.config[0x57] >> 4;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void via_isa_set_pci_irq(void *opaque, int irq_num, int level)
|
|
|
|
{
|
|
|
|
ViaISAState *s = opaque;
|
|
|
|
PCIBus *bus = pci_get_bus(&s->dev);
|
|
|
|
int i, pic_level, pic_irq = via_isa_get_pci_irq(s, irq_num);
|
|
|
|
|
|
|
|
/* IRQ 0: disabled, IRQ 2,8,13: reserved */
|
|
|
|
if (!pic_irq) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (unlikely(pic_irq == 2 || pic_irq == 8 || pic_irq == 13)) {
|
|
|
|
qemu_log_mask(LOG_GUEST_ERROR, "Invalid ISA IRQ routing");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The pic level is the logical OR of all the PCI irqs mapped to it. */
|
|
|
|
pic_level = 0;
|
|
|
|
for (i = 0; i < PCI_NUM_PINS; i++) {
|
|
|
|
if (pic_irq == via_isa_get_pci_irq(s, i)) {
|
|
|
|
pic_level |= pci_bus_get_irq_level(bus, i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Now we change the pic irq level according to the via irq mappings. */
|
|
|
|
qemu_set_irq(s->isa_irqs_in[pic_irq], pic_level);
|
|
|
|
}
|
|
|
|
|
2021-10-15 03:06:20 +02:00
|
|
|
static void via_isa_realize(PCIDevice *d, Error **errp)
|
|
|
|
{
|
|
|
|
ViaISAState *s = VIA_ISA(d);
|
|
|
|
DeviceState *dev = DEVICE(d);
|
2022-06-13 19:24:55 +02:00
|
|
|
PCIBus *pci_bus = pci_get_bus(d);
|
Revert "hw/isa/vt82c686: Remove intermediate IRQ forwarder"
To be 'usable', QDev objects (which are QOM objects) must be
1/ initialized (at this point their properties can be modified), then
2/ realized (properties are consumed).
Some devices (objects) might depend on other devices. When creating
the 'QOM composition tree', parent objects can't be 'realized' until
all their children are. We might also have circular dependencies.
A common circular dependency occurs with IRQs. Device (A) has an
output IRQ wired to device (B), and device (B) has one to device (A).
When (A) is realized and connects its IRQ to an unrealized (B), the
IRQ handler on (B) is not yet created. QEMU pass IRQ between objects
as pointer. When (A) poll (B)'s IRQ, it is NULL. Later (B) is realized
and its IRQ pointers are populated, but (A) keeps a reference to a
NULL pointer.
A common pattern to bypass this circular limitation is to use 'proxy'
objects. Proxy (P) is created (and realized) before (A) and (B). Then
(A) and (B) can be created in different order, it doesn't matter: (P)
pointers are already populated.
Commit bb98e0f59cde ("hw/isa/vt82c686: Remove intermediate IRQ
forwarder") neglected the QOM/QDev circular dependency issue, and
removed the 'proxy' between the southbridge, its PCI functions and the
interrupt controller, resulting in PCI functions wiring output IRQs to
'NULL', leading to guest failures (IRQ never delivered) [1] [2].
Since we are entering feature freeze, it is safer to revert the
offending patch until we figure a way to strengthen our APIs.
[1] https://lore.kernel.org/qemu-devel/928a8552-ab62-9e6c-a492-d6453e338b9d@redhat.com/
[2] https://lore.kernel.org/qemu-devel/cover.1677628524.git.balaton@eik.bme.hu/
Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu>
Tested-by: Rene Engel <ReneEngel80@emailn.de>
Reviewed-by: Daniel Henrique Barboza <danielhb413@gmail.com>
Message-Id: <cdfb3c5a42e505450f6803124f27856434c5b298.1677628524.git.balaton@eik.bme.hu>
[PMD: Reworded description]
Inspired-by: Bernhard Beschow <shentey@gmail.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
2023-03-01 01:17:08 +01:00
|
|
|
qemu_irq *isa_irq;
|
2022-09-01 13:41:16 +02:00
|
|
|
ISABus *isa_bus;
|
2021-10-15 03:06:20 +02:00
|
|
|
int i;
|
|
|
|
|
|
|
|
qdev_init_gpio_out(dev, &s->cpu_intr, 1);
|
Revert "hw/isa/vt82c686: Remove intermediate IRQ forwarder"
To be 'usable', QDev objects (which are QOM objects) must be
1/ initialized (at this point their properties can be modified), then
2/ realized (properties are consumed).
Some devices (objects) might depend on other devices. When creating
the 'QOM composition tree', parent objects can't be 'realized' until
all their children are. We might also have circular dependencies.
A common circular dependency occurs with IRQs. Device (A) has an
output IRQ wired to device (B), and device (B) has one to device (A).
When (A) is realized and connects its IRQ to an unrealized (B), the
IRQ handler on (B) is not yet created. QEMU pass IRQ between objects
as pointer. When (A) poll (B)'s IRQ, it is NULL. Later (B) is realized
and its IRQ pointers are populated, but (A) keeps a reference to a
NULL pointer.
A common pattern to bypass this circular limitation is to use 'proxy'
objects. Proxy (P) is created (and realized) before (A) and (B). Then
(A) and (B) can be created in different order, it doesn't matter: (P)
pointers are already populated.
Commit bb98e0f59cde ("hw/isa/vt82c686: Remove intermediate IRQ
forwarder") neglected the QOM/QDev circular dependency issue, and
removed the 'proxy' between the southbridge, its PCI functions and the
interrupt controller, resulting in PCI functions wiring output IRQs to
'NULL', leading to guest failures (IRQ never delivered) [1] [2].
Since we are entering feature freeze, it is safer to revert the
offending patch until we figure a way to strengthen our APIs.
[1] https://lore.kernel.org/qemu-devel/928a8552-ab62-9e6c-a492-d6453e338b9d@redhat.com/
[2] https://lore.kernel.org/qemu-devel/cover.1677628524.git.balaton@eik.bme.hu/
Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu>
Tested-by: Rene Engel <ReneEngel80@emailn.de>
Reviewed-by: Daniel Henrique Barboza <danielhb413@gmail.com>
Message-Id: <cdfb3c5a42e505450f6803124f27856434c5b298.1677628524.git.balaton@eik.bme.hu>
[PMD: Reworded description]
Inspired-by: Bernhard Beschow <shentey@gmail.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
2023-03-01 01:17:08 +01:00
|
|
|
isa_irq = qemu_allocate_irqs(via_isa_request_i8259_irq, s, 1);
|
2022-09-01 13:41:17 +02:00
|
|
|
isa_bus = isa_bus_new(dev, pci_address_space(d), pci_address_space_io(d),
|
2022-09-01 13:41:18 +02:00
|
|
|
errp);
|
|
|
|
|
|
|
|
if (!isa_bus) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
Revert "hw/isa/vt82c686: Remove intermediate IRQ forwarder"
To be 'usable', QDev objects (which are QOM objects) must be
1/ initialized (at this point their properties can be modified), then
2/ realized (properties are consumed).
Some devices (objects) might depend on other devices. When creating
the 'QOM composition tree', parent objects can't be 'realized' until
all their children are. We might also have circular dependencies.
A common circular dependency occurs with IRQs. Device (A) has an
output IRQ wired to device (B), and device (B) has one to device (A).
When (A) is realized and connects its IRQ to an unrealized (B), the
IRQ handler on (B) is not yet created. QEMU pass IRQ between objects
as pointer. When (A) poll (B)'s IRQ, it is NULL. Later (B) is realized
and its IRQ pointers are populated, but (A) keeps a reference to a
NULL pointer.
A common pattern to bypass this circular limitation is to use 'proxy'
objects. Proxy (P) is created (and realized) before (A) and (B). Then
(A) and (B) can be created in different order, it doesn't matter: (P)
pointers are already populated.
Commit bb98e0f59cde ("hw/isa/vt82c686: Remove intermediate IRQ
forwarder") neglected the QOM/QDev circular dependency issue, and
removed the 'proxy' between the southbridge, its PCI functions and the
interrupt controller, resulting in PCI functions wiring output IRQs to
'NULL', leading to guest failures (IRQ never delivered) [1] [2].
Since we are entering feature freeze, it is safer to revert the
offending patch until we figure a way to strengthen our APIs.
[1] https://lore.kernel.org/qemu-devel/928a8552-ab62-9e6c-a492-d6453e338b9d@redhat.com/
[2] https://lore.kernel.org/qemu-devel/cover.1677628524.git.balaton@eik.bme.hu/
Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu>
Tested-by: Rene Engel <ReneEngel80@emailn.de>
Reviewed-by: Daniel Henrique Barboza <danielhb413@gmail.com>
Message-Id: <cdfb3c5a42e505450f6803124f27856434c5b298.1677628524.git.balaton@eik.bme.hu>
[PMD: Reworded description]
Inspired-by: Bernhard Beschow <shentey@gmail.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
2023-03-01 01:17:08 +01:00
|
|
|
s->isa_irqs_in = i8259_init(isa_bus, *isa_irq);
|
2023-02-09 13:32:18 +01:00
|
|
|
isa_bus_register_input_irqs(isa_bus, s->isa_irqs_in);
|
2022-09-01 13:41:16 +02:00
|
|
|
i8254_pit_init(isa_bus, 0x40, 0, NULL);
|
|
|
|
i8257_dma_init(isa_bus, 0);
|
2022-09-01 13:41:26 +02:00
|
|
|
|
2023-02-16 21:21:35 +01:00
|
|
|
qdev_init_gpio_in_named(dev, via_isa_set_pci_irq, "pirq", PCI_NUM_PINS);
|
|
|
|
|
2022-09-01 13:41:26 +02:00
|
|
|
/* RTC */
|
|
|
|
qdev_prop_set_int32(DEVICE(&s->rtc), "base_year", 2000);
|
|
|
|
if (!qdev_realize(DEVICE(&s->rtc), BUS(isa_bus), errp)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
isa_connect_gpio_out(ISA_DEVICE(&s->rtc), 0, s->rtc.isairq);
|
2021-10-15 03:06:20 +02:00
|
|
|
|
|
|
|
for (i = 0; i < PCI_CONFIG_HEADER_SIZE; i++) {
|
|
|
|
if (i < PCI_COMMAND || i >= PCI_REVISION_ID) {
|
|
|
|
d->wmask[i] = 0;
|
|
|
|
}
|
|
|
|
}
|
2022-09-01 13:41:15 +02:00
|
|
|
|
|
|
|
/* Super I/O */
|
2022-09-01 13:41:16 +02:00
|
|
|
if (!qdev_realize(DEVICE(&s->via_sio), BUS(isa_bus), errp)) {
|
2022-09-01 13:41:15 +02:00
|
|
|
return;
|
|
|
|
}
|
2022-06-13 19:24:55 +02:00
|
|
|
|
|
|
|
/* Function 1: IDE */
|
|
|
|
qdev_prop_set_int32(DEVICE(&s->ide), "addr", d->devfn + 1);
|
|
|
|
if (!qdev_realize(DEVICE(&s->ide), BUS(pci_bus), errp)) {
|
|
|
|
return;
|
|
|
|
}
|
2023-05-31 23:10:38 +02:00
|
|
|
for (i = 0; i < 2; i++) {
|
|
|
|
qdev_connect_gpio_out_named(DEVICE(&s->ide), "isa-irq", i,
|
|
|
|
s->isa_irqs_in[14 + i]);
|
|
|
|
}
|
2022-09-01 13:41:22 +02:00
|
|
|
|
|
|
|
/* Functions 2-3: USB Ports */
|
|
|
|
for (i = 0; i < ARRAY_SIZE(s->uhci); i++) {
|
|
|
|
qdev_prop_set_int32(DEVICE(&s->uhci[i]), "addr", d->devfn + 2 + i);
|
|
|
|
if (!qdev_realize(DEVICE(&s->uhci[i]), BUS(pci_bus), errp)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2022-09-01 13:41:23 +02:00
|
|
|
|
|
|
|
/* Function 4: Power Management */
|
|
|
|
qdev_prop_set_int32(DEVICE(&s->pm), "addr", d->devfn + 4);
|
|
|
|
if (!qdev_realize(DEVICE(&s->pm), BUS(pci_bus), errp)) {
|
|
|
|
return;
|
|
|
|
}
|
2022-09-01 13:41:24 +02:00
|
|
|
|
|
|
|
/* Function 5: AC97 Audio */
|
|
|
|
qdev_prop_set_int32(DEVICE(&s->ac97), "addr", d->devfn + 5);
|
|
|
|
if (!qdev_realize(DEVICE(&s->ac97), BUS(pci_bus), errp)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Function 6: MC97 Modem */
|
|
|
|
qdev_prop_set_int32(DEVICE(&s->mc97), "addr", d->devfn + 6);
|
|
|
|
if (!qdev_realize(DEVICE(&s->mc97), BUS(pci_bus), errp)) {
|
|
|
|
return;
|
|
|
|
}
|
2021-10-15 03:06:20 +02:00
|
|
|
}
|
|
|
|
|
2021-03-25 14:50:39 +01:00
|
|
|
/* TYPE_VT82C686B_ISA */
|
|
|
|
|
2021-01-09 21:16:36 +01:00
|
|
|
static void vt82c686b_write_config(PCIDevice *d, uint32_t addr,
|
|
|
|
uint32_t val, int len)
|
|
|
|
{
|
2021-03-25 14:50:39 +01:00
|
|
|
ViaISAState *s = VIA_ISA(d);
|
2021-01-09 21:16:36 +01:00
|
|
|
|
|
|
|
trace_via_isa_write(addr, val, len);
|
|
|
|
pci_default_write_config(d, addr, val, len);
|
|
|
|
if (addr == 0x85) {
|
|
|
|
/* BIT(1): enable or disable superio config io ports */
|
2022-09-01 13:41:15 +02:00
|
|
|
via_superio_io_enable(&s->via_sio, val & BIT(1));
|
2021-01-09 21:16:36 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void vt82c686b_isa_reset(DeviceState *dev)
|
|
|
|
{
|
2021-03-25 14:50:39 +01:00
|
|
|
ViaISAState *s = VIA_ISA(dev);
|
2021-01-09 21:16:36 +01:00
|
|
|
uint8_t *pci_conf = s->dev.config;
|
|
|
|
|
|
|
|
pci_set_long(pci_conf + PCI_CAPABILITY_LIST, 0x000000c0);
|
|
|
|
pci_set_word(pci_conf + PCI_COMMAND, PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
|
|
|
|
PCI_COMMAND_MASTER | PCI_COMMAND_SPECIAL);
|
|
|
|
pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_DEVSEL_MEDIUM);
|
|
|
|
|
|
|
|
pci_conf[0x48] = 0x01; /* Miscellaneous Control 3 */
|
|
|
|
pci_conf[0x4a] = 0x04; /* IDE interrupt Routing */
|
|
|
|
pci_conf[0x4f] = 0x03; /* DMA/Master Mem Access Control 3 */
|
|
|
|
pci_conf[0x50] = 0x2d; /* PnP DMA Request Control */
|
|
|
|
pci_conf[0x59] = 0x04;
|
|
|
|
pci_conf[0x5a] = 0x04; /* KBC/RTC Control*/
|
|
|
|
pci_conf[0x5f] = 0x04;
|
|
|
|
pci_conf[0x77] = 0x10; /* GPIO Control 1/2/3/4 */
|
|
|
|
}
|
|
|
|
|
2022-09-01 13:41:15 +02:00
|
|
|
static void vt82c686b_init(Object *obj)
|
2010-06-29 04:49:29 +02:00
|
|
|
{
|
2022-09-01 13:41:15 +02:00
|
|
|
ViaISAState *s = VIA_ISA(obj);
|
2010-06-29 04:49:29 +02:00
|
|
|
|
2022-09-01 13:41:15 +02:00
|
|
|
object_initialize_child(obj, "sio", &s->via_sio, TYPE_VT82C686B_SUPERIO);
|
2022-09-01 13:41:23 +02:00
|
|
|
object_initialize_child(obj, "pm", &s->pm, TYPE_VT82C686B_PM);
|
2010-06-29 04:49:29 +02:00
|
|
|
}
|
|
|
|
|
2021-03-25 14:50:39 +01:00
|
|
|
static void vt82c686b_class_init(ObjectClass *klass, void *data)
|
2011-12-04 19:22:06 +01:00
|
|
|
{
|
2011-12-08 04:34:16 +01:00
|
|
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
2011-12-04 19:22:06 +01:00
|
|
|
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
|
|
|
|
2022-09-01 13:41:15 +02:00
|
|
|
k->realize = via_isa_realize;
|
2011-12-04 19:22:06 +01:00
|
|
|
k->config_write = vt82c686b_write_config;
|
|
|
|
k->vendor_id = PCI_VENDOR_ID_VIA;
|
2021-03-25 14:50:39 +01:00
|
|
|
k->device_id = PCI_DEVICE_ID_VIA_82C686B_ISA;
|
2011-12-04 19:22:06 +01:00
|
|
|
k->class_id = PCI_CLASS_BRIDGE_ISA;
|
|
|
|
k->revision = 0x40;
|
2019-10-10 15:15:25 +02:00
|
|
|
dc->reset = vt82c686b_isa_reset;
|
2011-12-08 04:34:16 +01:00
|
|
|
dc->desc = "ISA bridge";
|
|
|
|
dc->vmsd = &vmstate_via;
|
2021-03-25 14:50:39 +01:00
|
|
|
/* Reason: part of VIA VT82C686 southbridge, needs to be wired up */
|
2017-05-03 22:35:44 +02:00
|
|
|
dc->user_creatable = false;
|
2011-12-04 19:22:06 +01:00
|
|
|
}
|
|
|
|
|
2021-03-25 14:50:39 +01:00
|
|
|
static const TypeInfo vt82c686b_isa_info = {
|
2021-01-02 11:43:35 +01:00
|
|
|
.name = TYPE_VT82C686B_ISA,
|
2021-03-25 14:50:39 +01:00
|
|
|
.parent = TYPE_VIA_ISA,
|
|
|
|
.instance_size = sizeof(ViaISAState),
|
2022-09-01 13:41:15 +02:00
|
|
|
.instance_init = vt82c686b_init,
|
2021-03-25 14:50:39 +01:00
|
|
|
.class_init = vt82c686b_class_init,
|
2010-06-29 04:49:29 +02:00
|
|
|
};
|
|
|
|
|
2021-03-25 14:50:39 +01:00
|
|
|
/* TYPE_VT8231_ISA */
|
2021-01-09 21:16:36 +01:00
|
|
|
|
2021-03-25 14:50:39 +01:00
|
|
|
static void vt8231_write_config(PCIDevice *d, uint32_t addr,
|
|
|
|
uint32_t val, int len)
|
2018-03-08 23:39:40 +01:00
|
|
|
{
|
2021-03-25 14:50:39 +01:00
|
|
|
ViaISAState *s = VIA_ISA(d);
|
2018-03-08 23:39:40 +01:00
|
|
|
|
2021-03-25 14:50:39 +01:00
|
|
|
trace_via_isa_write(addr, val, len);
|
|
|
|
pci_default_write_config(d, addr, val, len);
|
|
|
|
if (addr == 0x50) {
|
|
|
|
/* BIT(2): enable or disable superio config io ports */
|
2022-09-01 13:41:15 +02:00
|
|
|
via_superio_io_enable(&s->via_sio, val & BIT(2));
|
2021-03-25 14:50:39 +01:00
|
|
|
}
|
2018-03-08 23:39:40 +01:00
|
|
|
}
|
|
|
|
|
2021-03-25 14:50:39 +01:00
|
|
|
static void vt8231_isa_reset(DeviceState *dev)
|
|
|
|
{
|
|
|
|
ViaISAState *s = VIA_ISA(dev);
|
|
|
|
uint8_t *pci_conf = s->dev.config;
|
|
|
|
|
|
|
|
pci_set_long(pci_conf + PCI_CAPABILITY_LIST, 0x000000c0);
|
|
|
|
pci_set_word(pci_conf + PCI_COMMAND, PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
|
|
|
|
PCI_COMMAND_MASTER | PCI_COMMAND_SPECIAL);
|
|
|
|
pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_DEVSEL_MEDIUM);
|
|
|
|
|
2023-05-31 23:10:38 +02:00
|
|
|
pci_conf[0x4c] = 0x04; /* IDE interrupt Routing */
|
2021-03-25 14:50:39 +01:00
|
|
|
pci_conf[0x58] = 0x40; /* Miscellaneous Control 0 */
|
|
|
|
pci_conf[0x67] = 0x08; /* Fast IR Config */
|
|
|
|
pci_conf[0x6b] = 0x01; /* Fast IR I/O Base */
|
|
|
|
}
|
|
|
|
|
2022-09-01 13:41:15 +02:00
|
|
|
static void vt8231_init(Object *obj)
|
2021-03-25 14:50:39 +01:00
|
|
|
{
|
2022-09-01 13:41:15 +02:00
|
|
|
ViaISAState *s = VIA_ISA(obj);
|
2021-03-25 14:50:39 +01:00
|
|
|
|
2022-09-01 13:41:15 +02:00
|
|
|
object_initialize_child(obj, "sio", &s->via_sio, TYPE_VT8231_SUPERIO);
|
2022-09-01 13:41:23 +02:00
|
|
|
object_initialize_child(obj, "pm", &s->pm, TYPE_VT8231_PM);
|
2021-03-25 14:50:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void vt8231_class_init(ObjectClass *klass, void *data)
|
|
|
|
{
|
|
|
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
|
|
|
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
|
|
|
|
2022-09-01 13:41:15 +02:00
|
|
|
k->realize = via_isa_realize;
|
2021-03-25 14:50:39 +01:00
|
|
|
k->config_write = vt8231_write_config;
|
|
|
|
k->vendor_id = PCI_VENDOR_ID_VIA;
|
|
|
|
k->device_id = PCI_DEVICE_ID_VIA_8231_ISA;
|
|
|
|
k->class_id = PCI_CLASS_BRIDGE_ISA;
|
|
|
|
k->revision = 0x10;
|
|
|
|
dc->reset = vt8231_isa_reset;
|
|
|
|
dc->desc = "ISA bridge";
|
|
|
|
dc->vmsd = &vmstate_via;
|
|
|
|
/* Reason: part of VIA VT8231 southbridge, needs to be wired up */
|
|
|
|
dc->user_creatable = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const TypeInfo vt8231_isa_info = {
|
|
|
|
.name = TYPE_VT8231_ISA,
|
|
|
|
.parent = TYPE_VIA_ISA,
|
|
|
|
.instance_size = sizeof(ViaISAState),
|
2022-09-01 13:41:15 +02:00
|
|
|
.instance_init = vt8231_init,
|
2021-03-25 14:50:39 +01:00
|
|
|
.class_init = vt8231_class_init,
|
2018-03-08 23:39:40 +01:00
|
|
|
};
|
|
|
|
|
2021-01-09 21:16:36 +01:00
|
|
|
|
2012-02-09 15:20:55 +01:00
|
|
|
static void vt82c686b_register_types(void)
|
2010-06-29 04:49:29 +02:00
|
|
|
{
|
2012-02-09 15:20:55 +01:00
|
|
|
type_register_static(&via_pm_info);
|
2021-01-09 21:16:36 +01:00
|
|
|
type_register_static(&vt82c686b_pm_info);
|
|
|
|
type_register_static(&vt8231_pm_info);
|
2021-01-09 21:16:36 +01:00
|
|
|
type_register_static(&via_superio_info);
|
2021-03-25 14:50:39 +01:00
|
|
|
type_register_static(&vt82c686b_superio_info);
|
2021-03-25 14:50:39 +01:00
|
|
|
type_register_static(&vt8231_superio_info);
|
2021-03-25 14:50:39 +01:00
|
|
|
type_register_static(&via_isa_info);
|
|
|
|
type_register_static(&vt82c686b_isa_info);
|
2021-03-25 14:50:39 +01:00
|
|
|
type_register_static(&vt8231_isa_info);
|
2010-06-29 04:49:29 +02:00
|
|
|
}
|
2012-02-09 15:20:55 +01:00
|
|
|
|
|
|
|
type_init(vt82c686b_register_types)
|