ff9105dabc
According to good QOM practice, an object should only deal with objects of its own sub tree. Having devices create an alias on the machine object doesn't respect this good practice. To resolve this, create the alias in the machine's code. Signed-off-by: Bernhard Beschow <shentey@gmail.com> Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org> Acked-by: Daniel Henrique Barboza <danielhb413@gmail.com> Message-Id: <20220901114127.53914-14-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
829 lines
24 KiB
C
829 lines
24 KiB
C
/*
|
|
* 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.
|
|
*
|
|
* Contributions after 2012-01-13 are licensed under the terms of the
|
|
* GNU GPL, version 2 or (at your option) any later version.
|
|
*
|
|
* VT8231 south bridge support and general clean up to allow it
|
|
* Copyright (c) 2018-2020 BALATON Zoltan
|
|
*/
|
|
|
|
#include "qemu/osdep.h"
|
|
#include "hw/isa/vt82c686.h"
|
|
#include "hw/pci/pci.h"
|
|
#include "hw/qdev-properties.h"
|
|
#include "hw/ide/pci.h"
|
|
#include "hw/isa/isa.h"
|
|
#include "hw/isa/superio.h"
|
|
#include "hw/intc/i8259.h"
|
|
#include "hw/irq.h"
|
|
#include "hw/dma/i8257.h"
|
|
#include "hw/usb/hcd-uhci.h"
|
|
#include "hw/timer/i8254.h"
|
|
#include "hw/rtc/mc146818rtc.h"
|
|
#include "migration/vmstate.h"
|
|
#include "hw/isa/apm.h"
|
|
#include "hw/acpi/acpi.h"
|
|
#include "hw/i2c/pm_smbus.h"
|
|
#include "qapi/error.h"
|
|
#include "qemu/log.h"
|
|
#include "qemu/module.h"
|
|
#include "qemu/range.h"
|
|
#include "qemu/timer.h"
|
|
#include "trace.h"
|
|
|
|
#define TYPE_VIA_PM "via-pm"
|
|
OBJECT_DECLARE_SIMPLE_TYPE(ViaPMState, VIA_PM)
|
|
|
|
struct ViaPMState {
|
|
PCIDevice dev;
|
|
MemoryRegion io;
|
|
ACPIREGS ar;
|
|
APMState apm;
|
|
PMSMBus smb;
|
|
};
|
|
|
|
static void pm_io_space_update(ViaPMState *s)
|
|
{
|
|
uint32_t pmbase = pci_get_long(s->dev.config + 0x48) & 0xff80UL;
|
|
|
|
memory_region_transaction_begin();
|
|
memory_region_set_address(&s->io, pmbase);
|
|
memory_region_set_enabled(&s->io, s->dev.config[0x41] & BIT(7));
|
|
memory_region_transaction_commit();
|
|
}
|
|
|
|
static void smb_io_space_update(ViaPMState *s)
|
|
{
|
|
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();
|
|
}
|
|
|
|
static int vmstate_acpi_post_load(void *opaque, int version_id)
|
|
{
|
|
ViaPMState *s = opaque;
|
|
|
|
pm_io_space_update(s);
|
|
smb_io_space_update(s);
|
|
return 0;
|
|
}
|
|
|
|
static const VMStateDescription vmstate_acpi = {
|
|
.name = "vt82c686b_pm",
|
|
.version_id = 1,
|
|
.minimum_version_id = 1,
|
|
.post_load = vmstate_acpi_post_load,
|
|
.fields = (VMStateField[]) {
|
|
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),
|
|
VMSTATE_END_OF_LIST()
|
|
}
|
|
};
|
|
|
|
static void pm_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int len)
|
|
{
|
|
ViaPMState *s = VIA_PM(d);
|
|
|
|
trace_via_pm_write(addr, val, len);
|
|
pci_default_write_config(d, addr, val, len);
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
|
|
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,
|
|
},
|
|
};
|
|
|
|
static void pm_update_sci(ViaPMState *s)
|
|
{
|
|
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);
|
|
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);
|
|
}
|
|
/* 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)
|
|
{
|
|
ViaPMState *s = container_of(ar, ViaPMState, ar);
|
|
pm_update_sci(s);
|
|
}
|
|
|
|
static void via_pm_reset(DeviceState *d)
|
|
{
|
|
ViaPMState *s = VIA_PM(d);
|
|
|
|
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);
|
|
/* SMBus IO base */
|
|
pci_set_long(s->dev.config + 0x90, 1);
|
|
|
|
acpi_pm1_evt_reset(&s->ar);
|
|
acpi_pm1_cnt_reset(&s->ar);
|
|
acpi_pm_tmr_reset(&s->ar);
|
|
pm_update_sci(s);
|
|
|
|
pm_io_space_update(s);
|
|
smb_io_space_update(s);
|
|
}
|
|
|
|
static void via_pm_realize(PCIDevice *dev, Error **errp)
|
|
{
|
|
ViaPMState *s = VIA_PM(dev);
|
|
|
|
pci_set_word(dev->config + PCI_STATUS, PCI_STATUS_FAST_BACK |
|
|
PCI_STATUS_DEVSEL_MEDIUM);
|
|
|
|
pm_smbus_init(DEVICE(s), &s->smb, false);
|
|
memory_region_add_subregion(pci_address_space_io(dev), 0, &s->smb.io);
|
|
memory_region_set_enabled(&s->smb.io, false);
|
|
|
|
apm_init(dev, &s->apm, NULL, s);
|
|
|
|
memory_region_init_io(&s->io, OBJECT(dev), &pm_io_ops, s, "via-pm", 128);
|
|
memory_region_add_subregion(pci_address_space_io(dev), 0, &s->io);
|
|
memory_region_set_enabled(&s->io, false);
|
|
|
|
acpi_pm_tmr_init(&s->ar, pm_tmr_timer, &s->io);
|
|
acpi_pm1_evt_init(&s->ar, pm_tmr_timer, &s->io);
|
|
acpi_pm1_cnt_init(&s->ar, &s->io, false, false, 2, false);
|
|
}
|
|
|
|
typedef struct via_pm_init_info {
|
|
uint16_t device_id;
|
|
} ViaPMInitInfo;
|
|
|
|
static void via_pm_class_init(ObjectClass *klass, void *data)
|
|
{
|
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
|
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
|
ViaPMInitInfo *info = data;
|
|
|
|
k->realize = via_pm_realize;
|
|
k->config_write = pm_write_config;
|
|
k->vendor_id = PCI_VENDOR_ID_VIA;
|
|
k->device_id = info->device_id;
|
|
k->class_id = PCI_CLASS_BRIDGE_OTHER;
|
|
k->revision = 0x40;
|
|
dc->reset = via_pm_reset;
|
|
/* Reason: part of VIA south bridge, does not exist stand alone */
|
|
dc->user_creatable = false;
|
|
dc->vmsd = &vmstate_acpi;
|
|
}
|
|
|
|
static const TypeInfo via_pm_info = {
|
|
.name = TYPE_VIA_PM,
|
|
.parent = TYPE_PCI_DEVICE,
|
|
.instance_size = sizeof(ViaPMState),
|
|
.abstract = true,
|
|
.interfaces = (InterfaceInfo[]) {
|
|
{ INTERFACE_CONVENTIONAL_PCI_DEVICE },
|
|
{ },
|
|
},
|
|
};
|
|
|
|
static const ViaPMInitInfo vt82c686b_pm_init_info = {
|
|
.device_id = PCI_DEVICE_ID_VIA_82C686B_PM,
|
|
};
|
|
|
|
#define TYPE_VT82C686B_PM "vt82c686b-pm"
|
|
|
|
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,
|
|
};
|
|
|
|
#define TYPE_VT8231_PM "vt8231-pm"
|
|
|
|
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,
|
|
};
|
|
|
|
|
|
#define TYPE_VIA_SUPERIO "via-superio"
|
|
OBJECT_DECLARE_SIMPLE_TYPE(ViaSuperIOState, VIA_SUPERIO)
|
|
|
|
struct ViaSuperIOState {
|
|
ISASuperIODevice superio;
|
|
uint8_t regs[0x100];
|
|
const MemoryRegionOps *io_ops;
|
|
MemoryRegion io;
|
|
};
|
|
|
|
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;
|
|
}
|
|
|
|
static void via_superio_class_init(ObjectClass *klass, void *data)
|
|
{
|
|
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"
|
|
|
|
static void vt82c686b_superio_cfg_write(void *opaque, hwaddr addr,
|
|
uint64_t data, unsigned size)
|
|
{
|
|
ViaSuperIOState *sc = opaque;
|
|
uint8_t idx = sc->regs[0];
|
|
|
|
if (addr == 0) { /* config index register */
|
|
sc->regs[0] = data;
|
|
return;
|
|
}
|
|
|
|
/* config data register */
|
|
trace_via_superio_write(idx, data);
|
|
switch (idx) {
|
|
case 0x00 ... 0xdf:
|
|
case 0xe4:
|
|
case 0xe5:
|
|
case 0xe9 ... 0xed:
|
|
case 0xf3:
|
|
case 0xf5:
|
|
case 0xf7:
|
|
case 0xf9 ... 0xfb:
|
|
case 0xfd ... 0xff:
|
|
/* ignore write to read only registers */
|
|
return;
|
|
/* case 0xe6 ... 0xe8: Should set base port of parallel and serial */
|
|
default:
|
|
qemu_log_mask(LOG_UNIMP,
|
|
"via_superio_cfg: unimplemented register 0x%x\n", idx);
|
|
break;
|
|
}
|
|
sc->regs[idx] = data;
|
|
}
|
|
|
|
static const MemoryRegionOps vt82c686b_superio_cfg_ops = {
|
|
.read = via_superio_cfg_read,
|
|
.write = vt82c686b_superio_cfg_write,
|
|
.endianness = DEVICE_NATIVE_ENDIAN,
|
|
.impl = {
|
|
.min_access_size = 1,
|
|
.max_access_size = 1,
|
|
},
|
|
};
|
|
|
|
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)
|
|
{
|
|
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,
|
|
};
|
|
|
|
|
|
#define TYPE_VT8231_SUPERIO "vt8231-superio"
|
|
|
|
static void vt8231_superio_cfg_write(void *opaque, hwaddr addr,
|
|
uint64_t data, unsigned size)
|
|
{
|
|
ViaSuperIOState *sc = opaque;
|
|
uint8_t idx = sc->regs[0];
|
|
|
|
if (addr == 0) { /* config index register */
|
|
sc->regs[0] = data;
|
|
return;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
sc->regs[idx] = data;
|
|
}
|
|
|
|
static const MemoryRegionOps vt8231_superio_cfg_ops = {
|
|
.read = via_superio_cfg_read,
|
|
.write = vt8231_superio_cfg_write,
|
|
.endianness = DEVICE_NATIVE_ENDIAN,
|
|
.impl = {
|
|
.min_access_size = 1,
|
|
.max_access_size = 1,
|
|
},
|
|
};
|
|
|
|
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;
|
|
}
|
|
|
|
static uint16_t vt8231_superio_serial_iobase(ISASuperIODevice *sio,
|
|
uint8_t index)
|
|
{
|
|
return 0x2f8; /* FIXME: This should be settable via registers f2-f4 */
|
|
}
|
|
|
|
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,
|
|
};
|
|
|
|
|
|
#define TYPE_VIA_ISA "via-isa"
|
|
OBJECT_DECLARE_SIMPLE_TYPE(ViaISAState, VIA_ISA)
|
|
|
|
struct ViaISAState {
|
|
PCIDevice dev;
|
|
qemu_irq cpu_intr;
|
|
qemu_irq *isa_irqs;
|
|
ViaSuperIOState via_sio;
|
|
RTCState rtc;
|
|
PCIIDEState ide;
|
|
UHCIState uhci[2];
|
|
ViaPMState pm;
|
|
PCIDevice ac97;
|
|
PCIDevice mc97;
|
|
};
|
|
|
|
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()
|
|
}
|
|
};
|
|
|
|
static void via_isa_init(Object *obj)
|
|
{
|
|
ViaISAState *s = VIA_ISA(obj);
|
|
|
|
object_initialize_child(obj, "rtc", &s->rtc, TYPE_MC146818_RTC);
|
|
object_initialize_child(obj, "ide", &s->ide, TYPE_VIA_IDE);
|
|
object_initialize_child(obj, "uhci1", &s->uhci[0], TYPE_VT82C686B_USB_UHCI);
|
|
object_initialize_child(obj, "uhci2", &s->uhci[1], TYPE_VT82C686B_USB_UHCI);
|
|
object_initialize_child(obj, "ac97", &s->ac97, TYPE_VIA_AC97);
|
|
object_initialize_child(obj, "mc97", &s->mc97, TYPE_VIA_MC97);
|
|
}
|
|
|
|
static const TypeInfo via_isa_info = {
|
|
.name = TYPE_VIA_ISA,
|
|
.parent = TYPE_PCI_DEVICE,
|
|
.instance_size = sizeof(ViaISAState),
|
|
.instance_init = via_isa_init,
|
|
.abstract = true,
|
|
.interfaces = (InterfaceInfo[]) {
|
|
{ INTERFACE_CONVENTIONAL_PCI_DEVICE },
|
|
{ },
|
|
},
|
|
};
|
|
|
|
void via_isa_set_irq(PCIDevice *d, int n, int level)
|
|
{
|
|
ViaISAState *s = VIA_ISA(d);
|
|
qemu_set_irq(s->isa_irqs[n], level);
|
|
}
|
|
|
|
static void via_isa_request_i8259_irq(void *opaque, int irq, int level)
|
|
{
|
|
ViaISAState *s = opaque;
|
|
qemu_set_irq(s->cpu_intr, level);
|
|
}
|
|
|
|
static void via_isa_realize(PCIDevice *d, Error **errp)
|
|
{
|
|
ViaISAState *s = VIA_ISA(d);
|
|
DeviceState *dev = DEVICE(d);
|
|
PCIBus *pci_bus = pci_get_bus(d);
|
|
qemu_irq *isa_irq;
|
|
ISABus *isa_bus;
|
|
int i;
|
|
|
|
qdev_init_gpio_out(dev, &s->cpu_intr, 1);
|
|
isa_irq = qemu_allocate_irqs(via_isa_request_i8259_irq, s, 1);
|
|
isa_bus = isa_bus_new(dev, pci_address_space(d), pci_address_space_io(d),
|
|
errp);
|
|
|
|
if (!isa_bus) {
|
|
return;
|
|
}
|
|
|
|
s->isa_irqs = i8259_init(isa_bus, *isa_irq);
|
|
isa_bus_irqs(isa_bus, s->isa_irqs);
|
|
i8254_pit_init(isa_bus, 0x40, 0, NULL);
|
|
i8257_dma_init(isa_bus, 0);
|
|
|
|
/* 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);
|
|
|
|
for (i = 0; i < PCI_CONFIG_HEADER_SIZE; i++) {
|
|
if (i < PCI_COMMAND || i >= PCI_REVISION_ID) {
|
|
d->wmask[i] = 0;
|
|
}
|
|
}
|
|
|
|
/* Super I/O */
|
|
if (!qdev_realize(DEVICE(&s->via_sio), BUS(isa_bus), errp)) {
|
|
return;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
}
|
|
|
|
/* TYPE_VT82C686B_ISA */
|
|
|
|
static void vt82c686b_write_config(PCIDevice *d, uint32_t addr,
|
|
uint32_t val, int len)
|
|
{
|
|
ViaISAState *s = VIA_ISA(d);
|
|
|
|
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 */
|
|
via_superio_io_enable(&s->via_sio, val & BIT(1));
|
|
}
|
|
}
|
|
|
|
static void vt82c686b_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);
|
|
|
|
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 */
|
|
}
|
|
|
|
static void vt82c686b_init(Object *obj)
|
|
{
|
|
ViaISAState *s = VIA_ISA(obj);
|
|
|
|
object_initialize_child(obj, "sio", &s->via_sio, TYPE_VT82C686B_SUPERIO);
|
|
object_initialize_child(obj, "pm", &s->pm, TYPE_VT82C686B_PM);
|
|
}
|
|
|
|
static void vt82c686b_class_init(ObjectClass *klass, void *data)
|
|
{
|
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
|
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
|
|
|
k->realize = via_isa_realize;
|
|
k->config_write = vt82c686b_write_config;
|
|
k->vendor_id = PCI_VENDOR_ID_VIA;
|
|
k->device_id = PCI_DEVICE_ID_VIA_82C686B_ISA;
|
|
k->class_id = PCI_CLASS_BRIDGE_ISA;
|
|
k->revision = 0x40;
|
|
dc->reset = vt82c686b_isa_reset;
|
|
dc->desc = "ISA bridge";
|
|
dc->vmsd = &vmstate_via;
|
|
/* Reason: part of VIA VT82C686 southbridge, needs to be wired up */
|
|
dc->user_creatable = false;
|
|
}
|
|
|
|
static const TypeInfo vt82c686b_isa_info = {
|
|
.name = TYPE_VT82C686B_ISA,
|
|
.parent = TYPE_VIA_ISA,
|
|
.instance_size = sizeof(ViaISAState),
|
|
.instance_init = vt82c686b_init,
|
|
.class_init = vt82c686b_class_init,
|
|
};
|
|
|
|
/* TYPE_VT8231_ISA */
|
|
|
|
static void vt8231_write_config(PCIDevice *d, uint32_t addr,
|
|
uint32_t val, int len)
|
|
{
|
|
ViaISAState *s = VIA_ISA(d);
|
|
|
|
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 */
|
|
via_superio_io_enable(&s->via_sio, val & BIT(2));
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
pci_conf[0x58] = 0x40; /* Miscellaneous Control 0 */
|
|
pci_conf[0x67] = 0x08; /* Fast IR Config */
|
|
pci_conf[0x6b] = 0x01; /* Fast IR I/O Base */
|
|
}
|
|
|
|
static void vt8231_init(Object *obj)
|
|
{
|
|
ViaISAState *s = VIA_ISA(obj);
|
|
|
|
object_initialize_child(obj, "sio", &s->via_sio, TYPE_VT8231_SUPERIO);
|
|
object_initialize_child(obj, "pm", &s->pm, TYPE_VT8231_PM);
|
|
}
|
|
|
|
static void vt8231_class_init(ObjectClass *klass, void *data)
|
|
{
|
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
|
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
|
|
|
k->realize = via_isa_realize;
|
|
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),
|
|
.instance_init = vt8231_init,
|
|
.class_init = vt8231_class_init,
|
|
};
|
|
|
|
|
|
static void vt82c686b_register_types(void)
|
|
{
|
|
type_register_static(&via_pm_info);
|
|
type_register_static(&vt82c686b_pm_info);
|
|
type_register_static(&vt8231_pm_info);
|
|
type_register_static(&via_superio_info);
|
|
type_register_static(&vt82c686b_superio_info);
|
|
type_register_static(&vt8231_superio_info);
|
|
type_register_static(&via_isa_info);
|
|
type_register_static(&vt82c686b_isa_info);
|
|
type_register_static(&vt8231_isa_info);
|
|
}
|
|
|
|
type_init(vt82c686b_register_types)
|