Message ID | 86fb4fa7670dcfb45cfcfe0b1a15968e351d7cab.1350677361.git.jbaron@redhat.com |
---|---|
State | New |
Headers | show |
On Fri, Oct 19, 2012 at 04:43:33PM -0400, Jason Baron wrote: > From: Jason Baron <jbaron@redhat.com> > > Lay the groundwork for subsequent ich9 support. > > Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp> > Signed-off-by: Jason Baron <jbaron@redhat.com> Applied with one exception, see below. > --- > hw/acpi_ich9.c | 315 +++++++++++++++++++++++++++++++++++++++++++++++++ > hw/acpi_ich9.h | 47 ++++++++ > hw/i386/Makefile.objs | 1 + > hw/ich9.h | 207 ++++++++++++++++++++++++++++++++ > hw/pci_ids.h | 12 ++ > 5 files changed, 582 insertions(+), 0 deletions(-) > create mode 100644 hw/acpi_ich9.c > create mode 100644 hw/acpi_ich9.h > create mode 100644 hw/ich9.h > > diff --git a/hw/acpi_ich9.c b/hw/acpi_ich9.c > new file mode 100644 > index 0000000..c45921c > --- /dev/null > +++ b/hw/acpi_ich9.c > @@ -0,0 +1,315 @@ > +/* > + * ACPI implementation > + * > + * Copyright (c) 2006 Fabrice Bellard > + * > + * This library is free software; you can redistribute it and/or > + * modify it under the terms of the GNU Lesser General Public > + * License version 2 as published by the Free Software Foundation. > + * > + * This library is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * Lesser General Public License for more details. > + * > + * You should have received a copy of the GNU Lesser General Public > + * License along with this library; if not, see <http://www.gnu.org/licenses/> > + */ > +/* > + * Copyright (c) 2009 Isaku Yamahata <yamahata at valinux co jp> > + * VA Linux Systems Japan K.K. > + * Copyright (C) 2012 Jason Baron <jbaron@redhat.com> > + * > + * This is based on acpi.c. > + */ > +#include "hw.h" > +#include "pc.h" > +#include "pci.h" > +#include "qemu-timer.h" > +#include "sysemu.h" > +#include "acpi.h" > + > +#include "ich9.h" > + > +//#define DEBUG > + > +#ifdef DEBUG > +#define ICH9_DEBUG(fmt, ...) \ > +do { printf("%s "fmt, __func__, ## __VA_ARGS__); } while (0) > +#else > +#define ICH9_DEBUG(fmt, ...) do { } while (0) > +#endif > + > +static void pm_ioport_write_fallback(void *opaque, uint32_t addr, int len, > + uint32_t val); > +static uint32_t pm_ioport_read_fallback(void *opaque, uint32_t addr, int len); > + > +static void pm_update_sci(ICH9LPCPMRegs *pm) > +{ > + int sci_level, pm1a_sts; > + > + pm1a_sts = acpi_pm1_evt_get_sts(&pm->acpi_regs); > + > + sci_level = (((pm1a_sts & pm->acpi_regs.pm1.evt.en) & > + (ACPI_BITMASK_RT_CLOCK_ENABLE | > + ACPI_BITMASK_POWER_BUTTON_ENABLE | > + ACPI_BITMASK_GLOBAL_LOCK_ENABLE | > + ACPI_BITMASK_TIMER_ENABLE)) != 0); > + qemu_set_irq(pm->irq, sci_level); > + > + /* schedule a timer interruption if needed */ > + acpi_pm_tmr_update(&pm->acpi_regs, > + (pm->acpi_regs.pm1.evt.en & ACPI_BITMASK_TIMER_ENABLE) && > + !(pm1a_sts & ACPI_BITMASK_TIMER_STATUS)); > +} > + > +static void ich9_pm_update_sci_fn(ACPIREGS *regs) > +{ > + ICH9LPCPMRegs *pm = container_of(regs, ICH9LPCPMRegs, acpi_regs); > + pm_update_sci(pm); > +} > + > +static void pm_ioport_writeb(void *opaque, uint32_t addr, uint32_t val) > +{ > + ICH9LPCPMRegs *pm = opaque; > + > + switch (addr & ICH9_PMIO_MASK) { > + case ICH9_PMIO_GPE0_STS ... (ICH9_PMIO_GPE0_STS + ICH9_PMIO_GPE0_LEN - 1): > + acpi_gpe_ioport_writeb(&pm->acpi_regs, addr, val); > + break; > + default: > + break; > + } > + > + ICH9_DEBUG("port=0x%04x val=0x%04x\n", addr, val); > +} > + > +static uint32_t pm_ioport_readb(void *opaque, uint32_t addr) > +{ > + ICH9LPCPMRegs *pm = opaque; > + uint32_t val = 0; > + > + switch (addr & ICH9_PMIO_MASK) { > + case ICH9_PMIO_GPE0_STS ... (ICH9_PMIO_GPE0_STS + ICH9_PMIO_GPE0_LEN - 1): > + val = acpi_gpe_ioport_readb(&pm->acpi_regs, addr); > + break; > + default: > + val = 0; > + break; > + } > + ICH9_DEBUG("port=0x%04x val=0x%04x\n", addr, val); > + return val; > +} > + > +static void pm_ioport_writew(void *opaque, uint32_t addr, uint32_t val) > +{ > + ICH9LPCPMRegs *pm = opaque; > + > + switch (addr & ICH9_PMIO_MASK) { > + case ICH9_PMIO_PM1_STS: > + acpi_pm1_evt_write_sts(&pm->acpi_regs, val); > + pm_update_sci(pm); > + break; > + case ICH9_PMIO_PM1_EN: > + pm->acpi_regs.pm1.evt.en = val; > + pm_update_sci(pm); > + break; > + case ICH9_PMIO_PM1_CNT: > + acpi_pm1_cnt_write(&pm->acpi_regs, val, 0); > + break; > + default: > + pm_ioport_write_fallback(opaque, addr, 2, val); > + break; > + } > + ICH9_DEBUG("port=0x%04x val=0x%04x\n", addr, val); > +} > + > +static uint32_t pm_ioport_readw(void *opaque, uint32_t addr) > +{ > + ICH9LPCPMRegs *pm = opaque; > + uint32_t val; > + > + switch (addr & ICH9_PMIO_MASK) { > + case ICH9_PMIO_PM1_STS: > + val = acpi_pm1_evt_get_sts(&pm->acpi_regs); > + break; > + case ICH9_PMIO_PM1_EN: > + val = pm->acpi_regs.pm1.evt.en; > + break; > + case ICH9_PMIO_PM1_CNT: > + val = pm->acpi_regs.pm1.cnt.cnt; > + break; > + default: > + val = pm_ioport_read_fallback(opaque, addr, 2); > + break; > + } > + ICH9_DEBUG("port=0x%04x val=0x%04x\n", addr, val); > + return val; > +} > + > +static void pm_ioport_writel(void *opaque, uint32_t addr, uint32_t val) > +{ > + ICH9LPCPMRegs *pm = opaque; > + > + switch (addr & ICH9_PMIO_MASK) { > + case ICH9_PMIO_SMI_EN: > + pm->smi_en = val; > + break; > + default: > + pm_ioport_write_fallback(opaque, addr, 4, val); > + break; > + } > + ICH9_DEBUG("port=0x%04x val=0x%08x\n", addr, val); > +} > + > +static uint32_t pm_ioport_readl(void *opaque, uint32_t addr) > +{ > + ICH9LPCPMRegs *pm = opaque; > + uint32_t val; > + > + switch (addr & ICH9_PMIO_MASK) { > + case ICH9_PMIO_PM1_TMR: > + val = acpi_pm_tmr_get(&pm->acpi_regs); > + break; > + case ICH9_PMIO_SMI_EN: > + val = pm->smi_en; > + break; > + > + default: > + val = pm_ioport_read_fallback(opaque, addr, 4); > + break; > + } > + ICH9_DEBUG("port=0x%04x val=0x%08x\n", addr, val); > + return val; > +} > + > +static void pm_ioport_write_fallback(void *opaque, uint32_t addr, int len, > + uint32_t val) > + { > + int subsize = (len == 4) ? 2 : 1; > + IOPortWriteFunc *ioport_write = > + (subsize == 2) ? pm_ioport_writew : pm_ioport_writeb; > + > + int i; > + > + for (i = 0; i < len; i += subsize) { > + ioport_write(opaque, addr, val); > + val >>= 8 * subsize; > + } > +} > + > +static uint32_t pm_ioport_read_fallback(void *opaque, uint32_t addr, int len) > +{ > + int subsize = (len == 4) ? 2 : 1; > + IOPortReadFunc *ioport_read = > + (subsize == 2) ? pm_ioport_readw : pm_ioport_readb; > + > + uint32_t val; > + int i; > + > + val = 0; > + for (i = 0; i < len; i += subsize) { > + val <<= 8 * subsize; > + val |= ioport_read(opaque, addr); > + } > + > + return val; > +} > + > +void ich9_pm_iospace_update(ICH9LPCPMRegs *pm, uint32_t pm_io_base) > +{ > + ICH9_DEBUG("to 0x%x\n", pm_io_base); > + > + assert((pm_io_base & ICH9_PMIO_MASK) == 0); > + > + if (pm->pm_io_base != 0) { > + isa_unassign_ioport(pm->pm_io_base, ICH9_PMIO_SIZE); > + } > + > + /* don't map at 0 */ > + if (pm_io_base == 0) { > + return; > + } > + > + register_ioport_write(pm_io_base, ICH9_PMIO_SIZE, 1, pm_ioport_writeb, pm); > + register_ioport_read(pm_io_base, ICH9_PMIO_SIZE, 1, pm_ioport_readb, pm); > + register_ioport_write(pm_io_base, ICH9_PMIO_SIZE, 2, pm_ioport_writew, pm); > + register_ioport_read(pm_io_base, ICH9_PMIO_SIZE, 2, pm_ioport_readw, pm); > + register_ioport_write(pm_io_base, ICH9_PMIO_SIZE, 4, pm_ioport_writel, pm); > + register_ioport_read(pm_io_base, ICH9_PMIO_SIZE, 4, pm_ioport_readl, pm); > + > + pm->pm_io_base = pm_io_base; > + acpi_gpe_blk(&pm->acpi_regs, pm_io_base + ICH9_PMIO_GPE0_STS); > +} > + > +static int ich9_pm_post_load(void *opaque, int version_id) > +{ > + ICH9LPCPMRegs *pm = opaque; > + uint32_t pm_io_base = pm->pm_io_base; > + pm->pm_io_base = 0; > + ich9_pm_iospace_update(pm, pm_io_base); > + return 0; > +} > + > +#define VMSTATE_GPE_ARRAY(_field, _state) \ > + { \ > + .name = (stringify(_field)), \ > + .version_id = 0, \ > + .num = ICH9_PMIO_GPE0_LEN, \ > + .info = &vmstate_info_uint8, \ > + .size = sizeof(uint8_t), \ > + .flags = VMS_ARRAY | VMS_POINTER, \ > + .offset = vmstate_offset_pointer(_state, _field, uint8_t), \ > + } > + > +const VMStateDescription vmstate_ich9_pm = { > + .name = "ich9_pm", > + .version_id = 1, > + .minimum_version_id = 1, > + .minimum_version_id_old = 1, > + .post_load = ich9_pm_post_load, > + .fields = (VMStateField[]) { > + VMSTATE_UINT16(acpi_regs.pm1.evt.sts, ICH9LPCPMRegs), > + VMSTATE_UINT16(acpi_regs.pm1.evt.en, ICH9LPCPMRegs), > + VMSTATE_UINT16(acpi_regs.pm1.cnt.cnt, ICH9LPCPMRegs), > + VMSTATE_TIMER(acpi_regs.tmr.timer, ICH9LPCPMRegs), > + VMSTATE_INT64(acpi_regs.tmr.overflow_time, ICH9LPCPMRegs), > + VMSTATE_GPE_ARRAY(acpi_regs.gpe.sts, ICH9LPCPMRegs), > + VMSTATE_GPE_ARRAY(acpi_regs.gpe.en, ICH9LPCPMRegs), > + VMSTATE_UINT32(smi_en, ICH9LPCPMRegs), > + VMSTATE_UINT32(smi_sts, ICH9LPCPMRegs), > + VMSTATE_END_OF_LIST() > + } > +}; > + > +static void pm_reset(void *opaque) > +{ > + ICH9LPCPMRegs *pm = opaque; > + ich9_pm_iospace_update(pm, 0); > + > + acpi_pm1_evt_reset(&pm->acpi_regs); > + acpi_pm1_cnt_reset(&pm->acpi_regs); > + acpi_pm_tmr_reset(&pm->acpi_regs); > + acpi_gpe_reset(&pm->acpi_regs); > + > + pm_update_sci(pm); > +} > + > +static void pm_powerdown_req(Notifier *n, void *opaque) > +{ > + ICH9LPCPMRegs *pm = container_of(n, ICH9LPCPMRegs, powerdown_notifier); > + > + acpi_pm1_evt_power_down(&pm->acpi_regs); > +} > + > +void ich9_pm_init(ICH9LPCPMRegs *pm, qemu_irq sci_irq, qemu_irq cmos_s3) > +{ > + acpi_pm_tmr_init(&pm->acpi_regs, ich9_pm_update_sci_fn); > + acpi_pm1_cnt_init(&pm->acpi_regs); > + acpi_gpe_init(&pm->acpi_regs, ICH9_PMIO_GPE0_LEN); > + > + pm->irq = sci_irq; > + qemu_register_reset(pm_reset, pm); > + pm->powerdown_notifier.notify = pm_powerdown_req; > + qemu_register_powerdown_notifier(&pm->powerdown_notifier); > +} > diff --git a/hw/acpi_ich9.h b/hw/acpi_ich9.h > new file mode 100644 > index 0000000..180c406 > --- /dev/null > +++ b/hw/acpi_ich9.h > @@ -0,0 +1,47 @@ > +/* > + * QEMU GMCH/ICH9 LPC PM Emulation > + * > + * Copyright (c) 2009 Isaku Yamahata <yamahata at valinux co jp> > + * VA Linux Systems Japan K.K. > + * > + * This library is free software; you can redistribute it and/or > + * modify it under the terms of the GNU Lesser General Public > + * License as published by the Free Software Foundation; either > + * version 2 of the License, or (at your option) any later version. > + * > + * This library is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * Lesser General Public License for more details. > + * > + * You should have received a copy of the GNU Lesser General Public > + * License along with this library; if not, see <http://www.gnu.org/licenses/> > + */ > + > +#ifndef HW_ACPI_ICH9_H > +#define HW_ACPI_ICH9_H > + > +#include "acpi.h" > + > +typedef struct ICH9LPCPMRegs { > + /* > + * In ich9 spec says that pm1_cnt register is 32bit width and > + * that the upper 16bits are reserved and unused. > + * PM1a_CNT_BLK = 2 in FADT so it is defined as uint16_t. > + */ > + ACPIREGS acpi_regs; > + uint32_t smi_en; > + uint32_t smi_sts; > + > + qemu_irq irq; /* SCI */ > + > + uint32_t pm_io_base; > + Notifier powerdown_notifier; > +} ICH9LPCPMRegs; > + > +void ich9_pm_init(ICH9LPCPMRegs *pm, > + qemu_irq sci_irq, qemu_irq cmos_s3_resume); > +void ich9_pm_iospace_update(ICH9LPCPMRegs *pm, uint32_t pm_io_base); > +extern const VMStateDescription vmstate_ich9_pm; > + > +#endif /* HW_ACPI_ICH9_H */ > diff --git a/hw/i386/Makefile.objs b/hw/i386/Makefile.objs > index 2f0c172..ba3744d 100644 > --- a/hw/i386/Makefile.objs > +++ b/hw/i386/Makefile.objs > @@ -7,6 +7,7 @@ obj-y += debugcon.o multiboot.o > obj-y += pc_piix.o > obj-y += pc_sysfw.o > obj-y += pam.o > +obj-y += acpi_ich9.o > obj-$(CONFIG_XEN) += xen_platform.o xen_apic.o > obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen-host-pci-device.o > obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pt.o xen_pt_config_init.o xen_pt_msi.o > diff --git a/hw/ich9.h b/hw/ich9.h > new file mode 100644 > index 0000000..de49135 > --- /dev/null > +++ b/hw/ich9.h > @@ -0,0 +1,207 @@ > +#ifndef HW_ICH9_H > +#define HW_ICH9_H > + > +#include "hw.h" > +#include "range.h" > +#include "isa.h" > +#include "sysbus.h" > +#include "pc.h" > +#include "apm.h" > +#include "ioapic.h" > +#include "pci.h" > +#include "pcie_host.h" > +#include "pci_bridge.h" > +#include "acpi.h" > +#include "acpi_ich9.h" > +#include "pam.h" > +#include "pci_internals.h" > + > +void ich9_lpc_set_irq(void *opaque, int irq_num, int level); > +int ich9_lpc_map_irq(PCIDevice *pci_dev, int intx); > +void ich9_lpc_pm_init(PCIDevice *pci_lpc, qemu_irq cmos_s3); > +PCIBus *ich9_d2pbr_init(PCIBus *bus, int devfn, int sec_bus); > +i2c_bus *ich9_smb_init(PCIBus *bus, int devfn, uint32_t smb_io_base); > + > +#define ICH9_CC_SIZE (16 * 1024) /* 16KB */ > + > +#define TYPE_ICH9_LPC_DEVICE "ICH9 LPC" > +#define ICH9_LPC_DEVICE(obj) \ > + OBJECT_CHECK(ICH9LPCState, (obj), TYPE_ICH9_LPC_DEVICE) > + > +typedef struct ICH9LPCState { > + /* ICH9 LPC PCI to ISA bridge */ > + PCIDevice d; > + > + /* (pci device, intx) -> pirq > + * In real chipset case, the unused slots are never used > + * as ICH9 supports only D25-D32 irq routing. > + * On the other hand in qemu case, any slot/function can be populated > + * via command line option. > + * So fallback interrupt routing for any devices in any slots is necessary. > + */ > + uint8_t irr[PCI_SLOT_MAX][PCI_NUM_PINS]; > + > + APMState apm; > + ICH9LPCPMRegs pm; > + uint32_t sci_level; /* track sci level */ > + > + /* 10.1 Chipset Configuration registers(Memory Space) > + which is pointed by RCBA */ > + uint8_t chip_config[ICH9_CC_SIZE]; > + /* isa bus */ > + ISABus *isa_bus; > + MemoryRegion rbca_mem; > + > + qemu_irq *pic; > + qemu_irq *ioapic; > +} ICH9LPCState; > + > +#define Q35_MASK(bit, ms_bit, ls_bit) \ > +((uint##bit##_t)(((1ULL << ((ms_bit) + 1)) - 1) & ~((1ULL << ls_bit) - 1))) > + > +/* ICH9: Chipset Configuration Registers */ > +#define ICH9_CC_ADDR_MASK (ICH9_CC_SIZE - 1) > + > +#define ICH9_CC > +#define ICH9_CC_D28IP 0x310C > +#define ICH9_CC_D28IP_SHIFT 4 > +#define ICH9_CC_D28IP_MASK 0xf > +#define ICH9_CC_D28IP_DEFAULT 0x00214321 > +#define ICH9_CC_D31IR 0x3140 > +#define ICH9_CC_D30IR 0x3142 > +#define ICH9_CC_D29IR 0x3144 > +#define ICH9_CC_D28IR 0x3146 > +#define ICH9_CC_D27IR 0x3148 > +#define ICH9_CC_D26IR 0x314C > +#define ICH9_CC_D25IR 0x3150 > +#define ICH9_CC_DIR_DEFAULT 0x3210 > +#define ICH9_CC_D30IR_DEFAULT 0x0 > +#define ICH9_CC_DIR_SHIFT 4 > +#define ICH9_CC_DIR_MASK 0x7 > +#define ICH9_CC_OIC 0x31FF > +#define ICH9_CC_OIC_AEN 0x1 > + > +/* D28:F[0-5] */ > +#define ICH9_PCIE_DEV 28 > +#define ICH9_PCIE_FUNC_MAX 6 > + > + > +/* D29:F0 USB UHCI Controller #1 */ > +#define ICH9_USB_UHCI1_DEV 29 > +#define ICH9_USB_UHCI1_FUNC 0 > + > +/* D30:F0 DMI-to-PCI brdige */ > +#define ICH9_D2P_BRIDGE "ICH9 D2P BRIDGE" > +#define ICH9_D2P_BRIDGE_SAVEVM_VERSION 0 > + > +#define ICH9_D2P_BRIDGE_DEV 30 > +#define ICH9_D2P_BRIDGE_FUNC 0 > + > +#define ICH9_D2P_SECONDARY_DEFAULT (256 - 8) > + > +#define ICH9_D2P_A2_REVISION 0x92 > + > + > +/* D31:F1 LPC controller */ > +#define ICH9_A2_LPC "ICH9 A2 LPC" > +#define ICH9_A2_LPC_SAVEVM_VERSION 0 > + > +#define ICH9_LPC_DEV 31 > +#define ICH9_LPC_FUNC 0 > + > +#define ICH9_A2_LPC_REVISION 0x2 > +#define ICH9_LPC_NB_PIRQS 8 /* PCI A-H */ > + > +#define ICH9_LPC_PMBASE 0x40 > +#define ICH9_LPC_PMBASE_BASE_ADDRESS_MASK Q35_MASK(32, 15, 7) > +#define ICH9_LPC_PMBASE_RTE 0x1 > +#define ICH9_LPC_PMBASE_DEFAULT 0x1 > +#define ICH9_LPC_ACPI_CTRL 0x44 > +#define ICH9_LPC_ACPI_CTRL_ACPI_EN 0x80 > +#define ICH9_LPC_ACPI_CTRL_SCI_IRQ_SEL_MASK Q35_MASK(8, 2, 0) > +#define ICH9_LPC_ACPI_CTRL_9 0x0 > +#define ICH9_LPC_ACPI_CTRL_10 0x1 > +#define ICH9_LPC_ACPI_CTRL_11 0x2 > +#define ICH9_LPC_ACPI_CTRL_20 0x4 > +#define ICH9_LPC_ACPI_CTRL_21 0x5 > +#define ICH9_LPC_ACPI_CTRL_DEFAULT 0x0 > + > +#define ICH9_LPC_PIRQA_ROUT 0x60 > +#define ICH9_LPC_PIRQB_ROUT 0x61 > +#define ICH9_LPC_PIRQC_ROUT 0x62 > +#define ICH9_LPC_PIRQD_ROUT 0x63 > + > +#define ICH9_LPC_PIRQE_ROUT 0x68 > +#define ICH9_LPC_PIRQF_ROUT 0x69 > +#define ICH9_LPC_PIRQG_ROUT 0x6a > +#define ICH9_LPC_PIRQH_ROUT 0x6b > + > +#define ICH9_LPC_PIRQ_ROUT_IRQEN 0x80 > +#define ICH9_LPC_PIRQ_ROUT_MASK Q35_MASK(8, 3, 0) > +#define ICH9_LPC_PIRQ_ROUT_DEFAULT 0x80 > + > +#define ICH9_LPC_RCBA 0xf0 > +#define ICH9_LPC_RCBA_BA_MASK Q35_MASK(32, 31, 14) > +#define ICH9_LPC_RCBA_EN 0x1 > +#define ICH9_LPC_RCBA_DEFAULT 0x0 > + > +#define ICH9_LPC_PIC_NUM_PINS 16 > +#define ICH9_LPC_IOAPIC_NUM_PINS 24 > + > +/* D31:F2 SATA Controller #1 */ > +#define ICH9_SATA1_DEV 31 > +#define ICH9_SATA1_FUNC 2 > + > +/* D30:F1 power management I/O registers > + offset from the address ICH9_LPC_PMBASE */ > + > +/* ICH9 LPC PM I/O registers are 128 ports and 128-aligned */ > +#define ICH9_PMIO_SIZE 128 > +#define ICH9_PMIO_MASK (ICH9_PMIO_SIZE - 1) > + > +#define ICH9_PMIO_PM1_STS 0x00 > +#define ICH9_PMIO_PM1_EN 0x02 > +#define ICH9_PMIO_PM1_CNT 0x04 > +#define ICH9_PMIO_PM1_TMR 0x08 > +#define ICH9_PMIO_GPE0_STS 0x20 > +#define ICH9_PMIO_GPE0_EN 0x28 > +#define ICH9_PMIO_GPE0_LEN 16 > +#define ICH9_PMIO_SMI_EN 0x30 > +#define ICH9_PMIO_SMI_EN_APMC_EN (1 << 5) > +#define ICH9_PMIO_SMI_STS 0x34 > + > +/* FADT ACPI_ENABLE/ACPI_DISABLE */ > +#define ICH9_APM_ACPI_ENABLE 0x2 > +#define ICH9_APM_ACPI_DISABLE 0x3 > + > + > +/* D31:F3 SMBus controller */ > +#define ICH9_A2_SMB_REVISION 0x02 > +#define ICH9_SMB_PI 0x00 > + > +#define ICH9_SMB_SMBMBAR0 0x10 > +#define ICH9_SMB_SMBMBAR1 0x14 > +#define ICH9_SMB_SMBM_BAR 0 > +#define ICH9_SMB_SMBM_SIZE (1 << 8) > +#define ICH9_SMB_SMB_BASE 0x20 > +#define ICH9_SMB_SMB_BASE_BAR 4 > +#define ICH9_SMB_SMB_BASE_SIZE (1 << 5) > +#define ICH9_SMB_HOSTC 0x40 > +#define ICH9_SMB_HOSTC_SSRESET ((uint8_t)(1 << 3)) > +#define ICH9_SMB_HOSTC_I2C_EN ((uint8_t)(1 << 2)) > +#define ICH9_SMB_HOSTC_SMB_SMI_EN ((uint8_t)(1 << 1)) > +#define ICH9_SMB_HOSTC_HST_EN ((uint8_t)(1 << 0)) > + > +/* D31:F3 SMBus I/O and memory mapped I/O registers */ > +#define ICH9_SMB_DEV 31 > +#define ICH9_SMB_FUNC 3 > + > +#define ICH9_SMB_HST_STS 0x00 > +#define ICH9_SMB_HST_CNT 0x02 > +#define ICH9_SMB_HST_CMD 0x03 > +#define ICH9_SMB_XMIT_SLVA 0x04 > +#define ICH9_SMB_HST_D0 0x05 > +#define ICH9_SMB_HST_D1 0x06 > +#define ICH9_SMB_HOST_BLOCK_DB 0x07 > + > +#endif /* HW_ICH9_H */ > diff --git a/hw/pci_ids.h b/hw/pci_ids.h > index 26c1d5f..91da67f 100644 > --- a/hw/pci_ids.h > +++ b/hw/pci_ids.h > @@ -36,6 +36,7 @@ > #define PCI_CLASS_BRIDGE_HOST 0x0600 > #define PCI_CLASS_BRIDGE_ISA 0x0601 > #define PCI_CLASS_BRIDGE_PCI 0x0604 > +#define PCI_CLASS_BRDIGE_PCI_INF_SUB 0x01 This bit has several problems: - it's not in linux pci_ids to which we try to stick - name is mistyped - whitespace is damaged As it's unused by this patch I dropped this chunk for now, and applied the rest of the patch. > #define PCI_CLASS_BRIDGE_OTHER 0x0680 > > #define PCI_CLASS_COMMUNICATION_OTHER 0x0780 > @@ -115,6 +116,17 @@ > #define PCI_DEVICE_ID_INTEL_82371AB 0x7111 > #define PCI_DEVICE_ID_INTEL_82371AB_2 0x7112 > #define PCI_DEVICE_ID_INTEL_82371AB_3 0x7113 > + > +#define PCI_DEVICE_ID_INTEL_ICH9_0 0x2910 > +#define PCI_DEVICE_ID_INTEL_ICH9_1 0x2917 > +#define PCI_DEVICE_ID_INTEL_ICH9_2 0x2912 > +#define PCI_DEVICE_ID_INTEL_ICH9_3 0x2913 > +#define PCI_DEVICE_ID_INTEL_ICH9_4 0x2914 > +#define PCI_DEVICE_ID_INTEL_ICH9_5 0x2919 > +#define PCI_DEVICE_ID_INTEL_ICH9_6 0x2930 > +#define PCI_DEVICE_ID_INTEL_ICH9_7 0x2916 > +#define PCI_DEVICE_ID_INTEL_ICH9_8 0x2918 > + > #define PCI_DEVICE_ID_INTEL_82801I_UHCI1 0x2934 > #define PCI_DEVICE_ID_INTEL_82801I_UHCI2 0x2935 > #define PCI_DEVICE_ID_INTEL_82801I_UHCI3 0x2936 > -- > 1.7.1
On Fri, Oct 19, 2012 at 04:43:33PM -0400, Jason Baron wrote: > From: Jason Baron <jbaron@redhat.com> > > Lay the groundwork for subsequent ich9 support. > > Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp> > Signed-off-by: Jason Baron <jbaron@redhat.com> > --- > hw/acpi_ich9.c | 315 +++++++++++++++++++++++++++++++++++++++++++++++++ > hw/acpi_ich9.h | 47 ++++++++ > hw/i386/Makefile.objs | 1 + > hw/ich9.h | 207 ++++++++++++++++++++++++++++++++ > hw/pci_ids.h | 12 ++ > 5 files changed, 582 insertions(+), 0 deletions(-) > create mode 100644 hw/acpi_ich9.c > create mode 100644 hw/acpi_ich9.h > create mode 100644 hw/ich9.h > > diff --git a/hw/acpi_ich9.c b/hw/acpi_ich9.c > new file mode 100644 > index 0000000..c45921c > --- /dev/null > +++ b/hw/acpi_ich9.c > @@ -0,0 +1,315 @@ > +/* > + * ACPI implementation > + * > + * Copyright (c) 2006 Fabrice Bellard > + * > + * This library is free software; you can redistribute it and/or > + * modify it under the terms of the GNU Lesser General Public > + * License version 2 as published by the Free Software Foundation. > + * > + * This library is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * Lesser General Public License for more details. > + * > + * You should have received a copy of the GNU Lesser General Public > + * License along with this library; if not, see <http://www.gnu.org/licenses/> > + */ > +/* > + * Copyright (c) 2009 Isaku Yamahata <yamahata at valinux co jp> > + * VA Linux Systems Japan K.K. > + * Copyright (C) 2012 Jason Baron <jbaron@redhat.com> > + * > + * This is based on acpi.c. > + */ Anthony, acpi.c header says: * Contributions after 2012-01-13 are licensed under the terms of the * GNU GPL, version 2 or (at your option) any later version. I am guessing it would be hard to guess which parts of this patch are original and which are derivative.
On Fri, Oct 19, 2012 at 04:43:33PM -0400, Jason Baron wrote: > From: Jason Baron <jbaron@redhat.com> > > Lay the groundwork for subsequent ich9 support. > > Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp> > Signed-off-by: Jason Baron <jbaron@redhat.com> > --- > hw/acpi_ich9.c | 315 +++++++++++++++++++++++++++++++++++++++++++++++++ > hw/acpi_ich9.h | 47 ++++++++ > hw/i386/Makefile.objs | 1 + > hw/ich9.h | 207 ++++++++++++++++++++++++++++++++ > hw/pci_ids.h | 12 ++ > 5 files changed, 582 insertions(+), 0 deletions(-) > create mode 100644 hw/acpi_ich9.c > create mode 100644 hw/acpi_ich9.h > create mode 100644 hw/ich9.h > > diff --git a/hw/acpi_ich9.c b/hw/acpi_ich9.c > new file mode 100644 > index 0000000..c45921c > --- /dev/null > +++ b/hw/acpi_ich9.c > @@ -0,0 +1,315 @@ > +/* > + * ACPI implementation > + * > + * Copyright (c) 2006 Fabrice Bellard > + * > + * This library is free software; you can redistribute it and/or > + * modify it under the terms of the GNU Lesser General Public > + * License version 2 as published by the Free Software Foundation. > + * > + * This library is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * Lesser General Public License for more details. > + * > + * You should have received a copy of the GNU Lesser General Public > + * License along with this library; if not, see <http://www.gnu.org/licenses/> > + */ > +/* > + * Copyright (c) 2009 Isaku Yamahata <yamahata at valinux co jp> > + * VA Linux Systems Japan K.K. > + * Copyright (C) 2012 Jason Baron <jbaron@redhat.com> > + * > + * This is based on acpi.c. > + */ > +#include "hw.h" > +#include "pc.h" > +#include "pci.h" > +#include "qemu-timer.h" > +#include "sysemu.h" > +#include "acpi.h" > + > +#include "ich9.h" > + > +//#define DEBUG > + > +#ifdef DEBUG > +#define ICH9_DEBUG(fmt, ...) \ > +do { printf("%s "fmt, __func__, ## __VA_ARGS__); } while (0) > +#else > +#define ICH9_DEBUG(fmt, ...) do { } while (0) > +#endif > + > +static void pm_ioport_write_fallback(void *opaque, uint32_t addr, int len, > + uint32_t val); > +static uint32_t pm_ioport_read_fallback(void *opaque, uint32_t addr, int len); > + > +static void pm_update_sci(ICH9LPCPMRegs *pm) > +{ > + int sci_level, pm1a_sts; > + > + pm1a_sts = acpi_pm1_evt_get_sts(&pm->acpi_regs); > + > + sci_level = (((pm1a_sts & pm->acpi_regs.pm1.evt.en) & > + (ACPI_BITMASK_RT_CLOCK_ENABLE | > + ACPI_BITMASK_POWER_BUTTON_ENABLE | > + ACPI_BITMASK_GLOBAL_LOCK_ENABLE | > + ACPI_BITMASK_TIMER_ENABLE)) != 0); > + qemu_set_irq(pm->irq, sci_level); > + > + /* schedule a timer interruption if needed */ > + acpi_pm_tmr_update(&pm->acpi_regs, > + (pm->acpi_regs.pm1.evt.en & ACPI_BITMASK_TIMER_ENABLE) && > + !(pm1a_sts & ACPI_BITMASK_TIMER_STATUS)); > +} > + > +static void ich9_pm_update_sci_fn(ACPIREGS *regs) > +{ > + ICH9LPCPMRegs *pm = container_of(regs, ICH9LPCPMRegs, acpi_regs); > + pm_update_sci(pm); > +} > + > +static void pm_ioport_writeb(void *opaque, uint32_t addr, uint32_t val) > +{ > + ICH9LPCPMRegs *pm = opaque; > + > + switch (addr & ICH9_PMIO_MASK) { > + case ICH9_PMIO_GPE0_STS ... (ICH9_PMIO_GPE0_STS + ICH9_PMIO_GPE0_LEN - 1): > + acpi_gpe_ioport_writeb(&pm->acpi_regs, addr, val); > + break; > + default: > + break; > + } > + > + ICH9_DEBUG("port=0x%04x val=0x%04x\n", addr, val); > +} > + > +static uint32_t pm_ioport_readb(void *opaque, uint32_t addr) > +{ > + ICH9LPCPMRegs *pm = opaque; > + uint32_t val = 0; > + > + switch (addr & ICH9_PMIO_MASK) { > + case ICH9_PMIO_GPE0_STS ... (ICH9_PMIO_GPE0_STS + ICH9_PMIO_GPE0_LEN - 1): > + val = acpi_gpe_ioport_readb(&pm->acpi_regs, addr); > + break; > + default: > + val = 0; > + break; > + } > + ICH9_DEBUG("port=0x%04x val=0x%04x\n", addr, val); > + return val; > +} > + > +static void pm_ioport_writew(void *opaque, uint32_t addr, uint32_t val) > +{ > + ICH9LPCPMRegs *pm = opaque; > + > + switch (addr & ICH9_PMIO_MASK) { > + case ICH9_PMIO_PM1_STS: > + acpi_pm1_evt_write_sts(&pm->acpi_regs, val); > + pm_update_sci(pm); > + break; > + case ICH9_PMIO_PM1_EN: > + pm->acpi_regs.pm1.evt.en = val; > + pm_update_sci(pm); > + break; > + case ICH9_PMIO_PM1_CNT: > + acpi_pm1_cnt_write(&pm->acpi_regs, val, 0); > + break; > + default: > + pm_ioport_write_fallback(opaque, addr, 2, val); > + break; > + } > + ICH9_DEBUG("port=0x%04x val=0x%04x\n", addr, val); > +} > + > +static uint32_t pm_ioport_readw(void *opaque, uint32_t addr) > +{ > + ICH9LPCPMRegs *pm = opaque; > + uint32_t val; > + > + switch (addr & ICH9_PMIO_MASK) { > + case ICH9_PMIO_PM1_STS: > + val = acpi_pm1_evt_get_sts(&pm->acpi_regs); > + break; > + case ICH9_PMIO_PM1_EN: > + val = pm->acpi_regs.pm1.evt.en; > + break; > + case ICH9_PMIO_PM1_CNT: > + val = pm->acpi_regs.pm1.cnt.cnt; > + break; > + default: > + val = pm_ioport_read_fallback(opaque, addr, 2); > + break; > + } > + ICH9_DEBUG("port=0x%04x val=0x%04x\n", addr, val); > + return val; > +} > + > +static void pm_ioport_writel(void *opaque, uint32_t addr, uint32_t val) > +{ > + ICH9LPCPMRegs *pm = opaque; > + > + switch (addr & ICH9_PMIO_MASK) { > + case ICH9_PMIO_SMI_EN: > + pm->smi_en = val; > + break; > + default: > + pm_ioport_write_fallback(opaque, addr, 4, val); > + break; > + } > + ICH9_DEBUG("port=0x%04x val=0x%08x\n", addr, val); > +} > + > +static uint32_t pm_ioport_readl(void *opaque, uint32_t addr) > +{ > + ICH9LPCPMRegs *pm = opaque; > + uint32_t val; > + > + switch (addr & ICH9_PMIO_MASK) { > + case ICH9_PMIO_PM1_TMR: > + val = acpi_pm_tmr_get(&pm->acpi_regs); > + break; > + case ICH9_PMIO_SMI_EN: > + val = pm->smi_en; > + break; > + > + default: > + val = pm_ioport_read_fallback(opaque, addr, 4); > + break; > + } > + ICH9_DEBUG("port=0x%04x val=0x%08x\n", addr, val); > + return val; > +} > + > +static void pm_ioport_write_fallback(void *opaque, uint32_t addr, int len, > + uint32_t val) > + { > + int subsize = (len == 4) ? 2 : 1; > + IOPortWriteFunc *ioport_write = > + (subsize == 2) ? pm_ioport_writew : pm_ioport_writeb; > + > + int i; > + > + for (i = 0; i < len; i += subsize) { > + ioport_write(opaque, addr, val); > + val >>= 8 * subsize; > + } > +} > + > +static uint32_t pm_ioport_read_fallback(void *opaque, uint32_t addr, int len) > +{ > + int subsize = (len == 4) ? 2 : 1; > + IOPortReadFunc *ioport_read = > + (subsize == 2) ? pm_ioport_readw : pm_ioport_readb; > + > + uint32_t val; > + int i; > + > + val = 0; > + for (i = 0; i < len; i += subsize) { > + val <<= 8 * subsize; > + val |= ioport_read(opaque, addr); > + } > + > + return val; > +} > + > +void ich9_pm_iospace_update(ICH9LPCPMRegs *pm, uint32_t pm_io_base) > +{ > + ICH9_DEBUG("to 0x%x\n", pm_io_base); > + > + assert((pm_io_base & ICH9_PMIO_MASK) == 0); > + > + if (pm->pm_io_base != 0) { > + isa_unassign_ioport(pm->pm_io_base, ICH9_PMIO_SIZE); > + } > + > + /* don't map at 0 */ > + if (pm_io_base == 0) { > + return; > + } > + > + register_ioport_write(pm_io_base, ICH9_PMIO_SIZE, 1, pm_ioport_writeb, pm); > + register_ioport_read(pm_io_base, ICH9_PMIO_SIZE, 1, pm_ioport_readb, pm); > + register_ioport_write(pm_io_base, ICH9_PMIO_SIZE, 2, pm_ioport_writew, pm); > + register_ioport_read(pm_io_base, ICH9_PMIO_SIZE, 2, pm_ioport_readw, pm); > + register_ioport_write(pm_io_base, ICH9_PMIO_SIZE, 4, pm_ioport_writel, pm); > + register_ioport_read(pm_io_base, ICH9_PMIO_SIZE, 4, pm_ioport_readl, pm); > + > + pm->pm_io_base = pm_io_base; > + acpi_gpe_blk(&pm->acpi_regs, pm_io_base + ICH9_PMIO_GPE0_STS); > +} > + > +static int ich9_pm_post_load(void *opaque, int version_id) > +{ > + ICH9LPCPMRegs *pm = opaque; > + uint32_t pm_io_base = pm->pm_io_base; > + pm->pm_io_base = 0; > + ich9_pm_iospace_update(pm, pm_io_base); > + return 0; > +} > + > +#define VMSTATE_GPE_ARRAY(_field, _state) \ > + { \ > + .name = (stringify(_field)), \ > + .version_id = 0, \ > + .num = ICH9_PMIO_GPE0_LEN, \ > + .info = &vmstate_info_uint8, \ > + .size = sizeof(uint8_t), \ > + .flags = VMS_ARRAY | VMS_POINTER, \ > + .offset = vmstate_offset_pointer(_state, _field, uint8_t), \ > + } > + > +const VMStateDescription vmstate_ich9_pm = { > + .name = "ich9_pm", > + .version_id = 1, > + .minimum_version_id = 1, > + .minimum_version_id_old = 1, > + .post_load = ich9_pm_post_load, > + .fields = (VMStateField[]) { > + VMSTATE_UINT16(acpi_regs.pm1.evt.sts, ICH9LPCPMRegs), > + VMSTATE_UINT16(acpi_regs.pm1.evt.en, ICH9LPCPMRegs), > + VMSTATE_UINT16(acpi_regs.pm1.cnt.cnt, ICH9LPCPMRegs), > + VMSTATE_TIMER(acpi_regs.tmr.timer, ICH9LPCPMRegs), > + VMSTATE_INT64(acpi_regs.tmr.overflow_time, ICH9LPCPMRegs), > + VMSTATE_GPE_ARRAY(acpi_regs.gpe.sts, ICH9LPCPMRegs), > + VMSTATE_GPE_ARRAY(acpi_regs.gpe.en, ICH9LPCPMRegs), > + VMSTATE_UINT32(smi_en, ICH9LPCPMRegs), > + VMSTATE_UINT32(smi_sts, ICH9LPCPMRegs), > + VMSTATE_END_OF_LIST() > + } > +}; > + > +static void pm_reset(void *opaque) > +{ > + ICH9LPCPMRegs *pm = opaque; > + ich9_pm_iospace_update(pm, 0); > + > + acpi_pm1_evt_reset(&pm->acpi_regs); > + acpi_pm1_cnt_reset(&pm->acpi_regs); > + acpi_pm_tmr_reset(&pm->acpi_regs); > + acpi_gpe_reset(&pm->acpi_regs); > + > + pm_update_sci(pm); > +} > + > +static void pm_powerdown_req(Notifier *n, void *opaque) > +{ > + ICH9LPCPMRegs *pm = container_of(n, ICH9LPCPMRegs, powerdown_notifier); > + > + acpi_pm1_evt_power_down(&pm->acpi_regs); > +} > + > +void ich9_pm_init(ICH9LPCPMRegs *pm, qemu_irq sci_irq, qemu_irq cmos_s3) > +{ > + acpi_pm_tmr_init(&pm->acpi_regs, ich9_pm_update_sci_fn); > + acpi_pm1_cnt_init(&pm->acpi_regs); > + acpi_gpe_init(&pm->acpi_regs, ICH9_PMIO_GPE0_LEN); > + > + pm->irq = sci_irq; > + qemu_register_reset(pm_reset, pm); > + pm->powerdown_notifier.notify = pm_powerdown_req; > + qemu_register_powerdown_notifier(&pm->powerdown_notifier); > +} > diff --git a/hw/acpi_ich9.h b/hw/acpi_ich9.h > new file mode 100644 > index 0000000..180c406 > --- /dev/null > +++ b/hw/acpi_ich9.h > @@ -0,0 +1,47 @@ > +/* > + * QEMU GMCH/ICH9 LPC PM Emulation > + * > + * Copyright (c) 2009 Isaku Yamahata <yamahata at valinux co jp> > + * VA Linux Systems Japan K.K. > + * > + * This library is free software; you can redistribute it and/or > + * modify it under the terms of the GNU Lesser General Public > + * License as published by the Free Software Foundation; either > + * version 2 of the License, or (at your option) any later version. > + * > + * This library is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * Lesser General Public License for more details. > + * > + * You should have received a copy of the GNU Lesser General Public > + * License along with this library; if not, see <http://www.gnu.org/licenses/> > + */ > + > +#ifndef HW_ACPI_ICH9_H > +#define HW_ACPI_ICH9_H > + > +#include "acpi.h" > + > +typedef struct ICH9LPCPMRegs { > + /* > + * In ich9 spec says that pm1_cnt register is 32bit width and > + * that the upper 16bits are reserved and unused. > + * PM1a_CNT_BLK = 2 in FADT so it is defined as uint16_t. > + */ > + ACPIREGS acpi_regs; > + uint32_t smi_en; > + uint32_t smi_sts; > + > + qemu_irq irq; /* SCI */ > + > + uint32_t pm_io_base; > + Notifier powerdown_notifier; > +} ICH9LPCPMRegs; > + > +void ich9_pm_init(ICH9LPCPMRegs *pm, > + qemu_irq sci_irq, qemu_irq cmos_s3_resume); > +void ich9_pm_iospace_update(ICH9LPCPMRegs *pm, uint32_t pm_io_base); > +extern const VMStateDescription vmstate_ich9_pm; > + > +#endif /* HW_ACPI_ICH9_H */ > diff --git a/hw/i386/Makefile.objs b/hw/i386/Makefile.objs > index 2f0c172..ba3744d 100644 > --- a/hw/i386/Makefile.objs > +++ b/hw/i386/Makefile.objs > @@ -7,6 +7,7 @@ obj-y += debugcon.o multiboot.o > obj-y += pc_piix.o > obj-y += pc_sysfw.o > obj-y += pam.o > +obj-y += acpi_ich9.o > obj-$(CONFIG_XEN) += xen_platform.o xen_apic.o > obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen-host-pci-device.o > obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pt.o xen_pt_config_init.o xen_pt_msi.o > diff --git a/hw/ich9.h b/hw/ich9.h > new file mode 100644 > index 0000000..de49135 > --- /dev/null > +++ b/hw/ich9.h > @@ -0,0 +1,207 @@ > +#ifndef HW_ICH9_H > +#define HW_ICH9_H > + > +#include "hw.h" > +#include "range.h" > +#include "isa.h" > +#include "sysbus.h" > +#include "pc.h" > +#include "apm.h" > +#include "ioapic.h" > +#include "pci.h" > +#include "pcie_host.h" > +#include "pci_bridge.h" > +#include "acpi.h" > +#include "acpi_ich9.h" > +#include "pam.h" Here too pam.h is not needed. > +#include "pci_internals.h" > + > +void ich9_lpc_set_irq(void *opaque, int irq_num, int level); > +int ich9_lpc_map_irq(PCIDevice *pci_dev, int intx); > +void ich9_lpc_pm_init(PCIDevice *pci_lpc, qemu_irq cmos_s3); > +PCIBus *ich9_d2pbr_init(PCIBus *bus, int devfn, int sec_bus); > +i2c_bus *ich9_smb_init(PCIBus *bus, int devfn, uint32_t smb_io_base); > + > +#define ICH9_CC_SIZE (16 * 1024) /* 16KB */ > + > +#define TYPE_ICH9_LPC_DEVICE "ICH9 LPC" > +#define ICH9_LPC_DEVICE(obj) \ > + OBJECT_CHECK(ICH9LPCState, (obj), TYPE_ICH9_LPC_DEVICE) > + > +typedef struct ICH9LPCState { > + /* ICH9 LPC PCI to ISA bridge */ > + PCIDevice d; > + > + /* (pci device, intx) -> pirq > + * In real chipset case, the unused slots are never used > + * as ICH9 supports only D25-D32 irq routing. > + * On the other hand in qemu case, any slot/function can be populated > + * via command line option. > + * So fallback interrupt routing for any devices in any slots is necessary. > + */ > + uint8_t irr[PCI_SLOT_MAX][PCI_NUM_PINS]; > + > + APMState apm; > + ICH9LPCPMRegs pm; > + uint32_t sci_level; /* track sci level */ > + > + /* 10.1 Chipset Configuration registers(Memory Space) > + which is pointed by RCBA */ > + uint8_t chip_config[ICH9_CC_SIZE]; > + /* isa bus */ > + ISABus *isa_bus; > + MemoryRegion rbca_mem; > + > + qemu_irq *pic; > + qemu_irq *ioapic; > +} ICH9LPCState; > + > +#define Q35_MASK(bit, ms_bit, ls_bit) \ > +((uint##bit##_t)(((1ULL << ((ms_bit) + 1)) - 1) & ~((1ULL << ls_bit) - 1))) > + > +/* ICH9: Chipset Configuration Registers */ > +#define ICH9_CC_ADDR_MASK (ICH9_CC_SIZE - 1) > + > +#define ICH9_CC > +#define ICH9_CC_D28IP 0x310C > +#define ICH9_CC_D28IP_SHIFT 4 > +#define ICH9_CC_D28IP_MASK 0xf > +#define ICH9_CC_D28IP_DEFAULT 0x00214321 > +#define ICH9_CC_D31IR 0x3140 > +#define ICH9_CC_D30IR 0x3142 > +#define ICH9_CC_D29IR 0x3144 > +#define ICH9_CC_D28IR 0x3146 > +#define ICH9_CC_D27IR 0x3148 > +#define ICH9_CC_D26IR 0x314C > +#define ICH9_CC_D25IR 0x3150 > +#define ICH9_CC_DIR_DEFAULT 0x3210 > +#define ICH9_CC_D30IR_DEFAULT 0x0 > +#define ICH9_CC_DIR_SHIFT 4 > +#define ICH9_CC_DIR_MASK 0x7 > +#define ICH9_CC_OIC 0x31FF > +#define ICH9_CC_OIC_AEN 0x1 > + > +/* D28:F[0-5] */ > +#define ICH9_PCIE_DEV 28 > +#define ICH9_PCIE_FUNC_MAX 6 > + > + > +/* D29:F0 USB UHCI Controller #1 */ > +#define ICH9_USB_UHCI1_DEV 29 > +#define ICH9_USB_UHCI1_FUNC 0 > + > +/* D30:F0 DMI-to-PCI brdige */ > +#define ICH9_D2P_BRIDGE "ICH9 D2P BRIDGE" > +#define ICH9_D2P_BRIDGE_SAVEVM_VERSION 0 > + > +#define ICH9_D2P_BRIDGE_DEV 30 > +#define ICH9_D2P_BRIDGE_FUNC 0 > + > +#define ICH9_D2P_SECONDARY_DEFAULT (256 - 8) > + > +#define ICH9_D2P_A2_REVISION 0x92 > + > + > +/* D31:F1 LPC controller */ > +#define ICH9_A2_LPC "ICH9 A2 LPC" > +#define ICH9_A2_LPC_SAVEVM_VERSION 0 > + > +#define ICH9_LPC_DEV 31 > +#define ICH9_LPC_FUNC 0 > + > +#define ICH9_A2_LPC_REVISION 0x2 > +#define ICH9_LPC_NB_PIRQS 8 /* PCI A-H */ > + > +#define ICH9_LPC_PMBASE 0x40 > +#define ICH9_LPC_PMBASE_BASE_ADDRESS_MASK Q35_MASK(32, 15, 7) > +#define ICH9_LPC_PMBASE_RTE 0x1 > +#define ICH9_LPC_PMBASE_DEFAULT 0x1 > +#define ICH9_LPC_ACPI_CTRL 0x44 > +#define ICH9_LPC_ACPI_CTRL_ACPI_EN 0x80 > +#define ICH9_LPC_ACPI_CTRL_SCI_IRQ_SEL_MASK Q35_MASK(8, 2, 0) > +#define ICH9_LPC_ACPI_CTRL_9 0x0 > +#define ICH9_LPC_ACPI_CTRL_10 0x1 > +#define ICH9_LPC_ACPI_CTRL_11 0x2 > +#define ICH9_LPC_ACPI_CTRL_20 0x4 > +#define ICH9_LPC_ACPI_CTRL_21 0x5 > +#define ICH9_LPC_ACPI_CTRL_DEFAULT 0x0 > + > +#define ICH9_LPC_PIRQA_ROUT 0x60 > +#define ICH9_LPC_PIRQB_ROUT 0x61 > +#define ICH9_LPC_PIRQC_ROUT 0x62 > +#define ICH9_LPC_PIRQD_ROUT 0x63 > + > +#define ICH9_LPC_PIRQE_ROUT 0x68 > +#define ICH9_LPC_PIRQF_ROUT 0x69 > +#define ICH9_LPC_PIRQG_ROUT 0x6a > +#define ICH9_LPC_PIRQH_ROUT 0x6b > + > +#define ICH9_LPC_PIRQ_ROUT_IRQEN 0x80 > +#define ICH9_LPC_PIRQ_ROUT_MASK Q35_MASK(8, 3, 0) > +#define ICH9_LPC_PIRQ_ROUT_DEFAULT 0x80 > + > +#define ICH9_LPC_RCBA 0xf0 > +#define ICH9_LPC_RCBA_BA_MASK Q35_MASK(32, 31, 14) > +#define ICH9_LPC_RCBA_EN 0x1 > +#define ICH9_LPC_RCBA_DEFAULT 0x0 > + > +#define ICH9_LPC_PIC_NUM_PINS 16 > +#define ICH9_LPC_IOAPIC_NUM_PINS 24 > + > +/* D31:F2 SATA Controller #1 */ > +#define ICH9_SATA1_DEV 31 > +#define ICH9_SATA1_FUNC 2 > + > +/* D30:F1 power management I/O registers > + offset from the address ICH9_LPC_PMBASE */ > + > +/* ICH9 LPC PM I/O registers are 128 ports and 128-aligned */ > +#define ICH9_PMIO_SIZE 128 > +#define ICH9_PMIO_MASK (ICH9_PMIO_SIZE - 1) > + > +#define ICH9_PMIO_PM1_STS 0x00 > +#define ICH9_PMIO_PM1_EN 0x02 > +#define ICH9_PMIO_PM1_CNT 0x04 > +#define ICH9_PMIO_PM1_TMR 0x08 > +#define ICH9_PMIO_GPE0_STS 0x20 > +#define ICH9_PMIO_GPE0_EN 0x28 > +#define ICH9_PMIO_GPE0_LEN 16 > +#define ICH9_PMIO_SMI_EN 0x30 > +#define ICH9_PMIO_SMI_EN_APMC_EN (1 << 5) > +#define ICH9_PMIO_SMI_STS 0x34 > + > +/* FADT ACPI_ENABLE/ACPI_DISABLE */ > +#define ICH9_APM_ACPI_ENABLE 0x2 > +#define ICH9_APM_ACPI_DISABLE 0x3 > + > + > +/* D31:F3 SMBus controller */ > +#define ICH9_A2_SMB_REVISION 0x02 > +#define ICH9_SMB_PI 0x00 > + > +#define ICH9_SMB_SMBMBAR0 0x10 > +#define ICH9_SMB_SMBMBAR1 0x14 > +#define ICH9_SMB_SMBM_BAR 0 > +#define ICH9_SMB_SMBM_SIZE (1 << 8) > +#define ICH9_SMB_SMB_BASE 0x20 > +#define ICH9_SMB_SMB_BASE_BAR 4 > +#define ICH9_SMB_SMB_BASE_SIZE (1 << 5) > +#define ICH9_SMB_HOSTC 0x40 > +#define ICH9_SMB_HOSTC_SSRESET ((uint8_t)(1 << 3)) > +#define ICH9_SMB_HOSTC_I2C_EN ((uint8_t)(1 << 2)) > +#define ICH9_SMB_HOSTC_SMB_SMI_EN ((uint8_t)(1 << 1)) > +#define ICH9_SMB_HOSTC_HST_EN ((uint8_t)(1 << 0)) > + > +/* D31:F3 SMBus I/O and memory mapped I/O registers */ > +#define ICH9_SMB_DEV 31 > +#define ICH9_SMB_FUNC 3 > + > +#define ICH9_SMB_HST_STS 0x00 > +#define ICH9_SMB_HST_CNT 0x02 > +#define ICH9_SMB_HST_CMD 0x03 > +#define ICH9_SMB_XMIT_SLVA 0x04 > +#define ICH9_SMB_HST_D0 0x05 > +#define ICH9_SMB_HST_D1 0x06 > +#define ICH9_SMB_HOST_BLOCK_DB 0x07 > + > +#endif /* HW_ICH9_H */ > diff --git a/hw/pci_ids.h b/hw/pci_ids.h > index 26c1d5f..91da67f 100644 > --- a/hw/pci_ids.h > +++ b/hw/pci_ids.h > @@ -36,6 +36,7 @@ > #define PCI_CLASS_BRIDGE_HOST 0x0600 > #define PCI_CLASS_BRIDGE_ISA 0x0601 > #define PCI_CLASS_BRIDGE_PCI 0x0604 > +#define PCI_CLASS_BRDIGE_PCI_INF_SUB 0x01 > #define PCI_CLASS_BRIDGE_OTHER 0x0680 > > #define PCI_CLASS_COMMUNICATION_OTHER 0x0780 > @@ -115,6 +116,17 @@ > #define PCI_DEVICE_ID_INTEL_82371AB 0x7111 > #define PCI_DEVICE_ID_INTEL_82371AB_2 0x7112 > #define PCI_DEVICE_ID_INTEL_82371AB_3 0x7113 > + > +#define PCI_DEVICE_ID_INTEL_ICH9_0 0x2910 > +#define PCI_DEVICE_ID_INTEL_ICH9_1 0x2917 > +#define PCI_DEVICE_ID_INTEL_ICH9_2 0x2912 > +#define PCI_DEVICE_ID_INTEL_ICH9_3 0x2913 > +#define PCI_DEVICE_ID_INTEL_ICH9_4 0x2914 > +#define PCI_DEVICE_ID_INTEL_ICH9_5 0x2919 > +#define PCI_DEVICE_ID_INTEL_ICH9_6 0x2930 > +#define PCI_DEVICE_ID_INTEL_ICH9_7 0x2916 > +#define PCI_DEVICE_ID_INTEL_ICH9_8 0x2918 > + > #define PCI_DEVICE_ID_INTEL_82801I_UHCI1 0x2934 > #define PCI_DEVICE_ID_INTEL_82801I_UHCI2 0x2935 > #define PCI_DEVICE_ID_INTEL_82801I_UHCI3 0x2936 > -- > 1.7.1
diff --git a/hw/acpi_ich9.c b/hw/acpi_ich9.c new file mode 100644 index 0000000..c45921c --- /dev/null +++ b/hw/acpi_ich9.c @@ -0,0 +1,315 @@ +/* + * ACPI implementation + * + * Copyright (c) 2006 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/> + */ +/* + * Copyright (c) 2009 Isaku Yamahata <yamahata at valinux co jp> + * VA Linux Systems Japan K.K. + * Copyright (C) 2012 Jason Baron <jbaron@redhat.com> + * + * This is based on acpi.c. + */ +#include "hw.h" +#include "pc.h" +#include "pci.h" +#include "qemu-timer.h" +#include "sysemu.h" +#include "acpi.h" + +#include "ich9.h" + +//#define DEBUG + +#ifdef DEBUG +#define ICH9_DEBUG(fmt, ...) \ +do { printf("%s "fmt, __func__, ## __VA_ARGS__); } while (0) +#else +#define ICH9_DEBUG(fmt, ...) do { } while (0) +#endif + +static void pm_ioport_write_fallback(void *opaque, uint32_t addr, int len, + uint32_t val); +static uint32_t pm_ioport_read_fallback(void *opaque, uint32_t addr, int len); + +static void pm_update_sci(ICH9LPCPMRegs *pm) +{ + int sci_level, pm1a_sts; + + pm1a_sts = acpi_pm1_evt_get_sts(&pm->acpi_regs); + + sci_level = (((pm1a_sts & pm->acpi_regs.pm1.evt.en) & + (ACPI_BITMASK_RT_CLOCK_ENABLE | + ACPI_BITMASK_POWER_BUTTON_ENABLE | + ACPI_BITMASK_GLOBAL_LOCK_ENABLE | + ACPI_BITMASK_TIMER_ENABLE)) != 0); + qemu_set_irq(pm->irq, sci_level); + + /* schedule a timer interruption if needed */ + acpi_pm_tmr_update(&pm->acpi_regs, + (pm->acpi_regs.pm1.evt.en & ACPI_BITMASK_TIMER_ENABLE) && + !(pm1a_sts & ACPI_BITMASK_TIMER_STATUS)); +} + +static void ich9_pm_update_sci_fn(ACPIREGS *regs) +{ + ICH9LPCPMRegs *pm = container_of(regs, ICH9LPCPMRegs, acpi_regs); + pm_update_sci(pm); +} + +static void pm_ioport_writeb(void *opaque, uint32_t addr, uint32_t val) +{ + ICH9LPCPMRegs *pm = opaque; + + switch (addr & ICH9_PMIO_MASK) { + case ICH9_PMIO_GPE0_STS ... (ICH9_PMIO_GPE0_STS + ICH9_PMIO_GPE0_LEN - 1): + acpi_gpe_ioport_writeb(&pm->acpi_regs, addr, val); + break; + default: + break; + } + + ICH9_DEBUG("port=0x%04x val=0x%04x\n", addr, val); +} + +static uint32_t pm_ioport_readb(void *opaque, uint32_t addr) +{ + ICH9LPCPMRegs *pm = opaque; + uint32_t val = 0; + + switch (addr & ICH9_PMIO_MASK) { + case ICH9_PMIO_GPE0_STS ... (ICH9_PMIO_GPE0_STS + ICH9_PMIO_GPE0_LEN - 1): + val = acpi_gpe_ioport_readb(&pm->acpi_regs, addr); + break; + default: + val = 0; + break; + } + ICH9_DEBUG("port=0x%04x val=0x%04x\n", addr, val); + return val; +} + +static void pm_ioport_writew(void *opaque, uint32_t addr, uint32_t val) +{ + ICH9LPCPMRegs *pm = opaque; + + switch (addr & ICH9_PMIO_MASK) { + case ICH9_PMIO_PM1_STS: + acpi_pm1_evt_write_sts(&pm->acpi_regs, val); + pm_update_sci(pm); + break; + case ICH9_PMIO_PM1_EN: + pm->acpi_regs.pm1.evt.en = val; + pm_update_sci(pm); + break; + case ICH9_PMIO_PM1_CNT: + acpi_pm1_cnt_write(&pm->acpi_regs, val, 0); + break; + default: + pm_ioport_write_fallback(opaque, addr, 2, val); + break; + } + ICH9_DEBUG("port=0x%04x val=0x%04x\n", addr, val); +} + +static uint32_t pm_ioport_readw(void *opaque, uint32_t addr) +{ + ICH9LPCPMRegs *pm = opaque; + uint32_t val; + + switch (addr & ICH9_PMIO_MASK) { + case ICH9_PMIO_PM1_STS: + val = acpi_pm1_evt_get_sts(&pm->acpi_regs); + break; + case ICH9_PMIO_PM1_EN: + val = pm->acpi_regs.pm1.evt.en; + break; + case ICH9_PMIO_PM1_CNT: + val = pm->acpi_regs.pm1.cnt.cnt; + break; + default: + val = pm_ioport_read_fallback(opaque, addr, 2); + break; + } + ICH9_DEBUG("port=0x%04x val=0x%04x\n", addr, val); + return val; +} + +static void pm_ioport_writel(void *opaque, uint32_t addr, uint32_t val) +{ + ICH9LPCPMRegs *pm = opaque; + + switch (addr & ICH9_PMIO_MASK) { + case ICH9_PMIO_SMI_EN: + pm->smi_en = val; + break; + default: + pm_ioport_write_fallback(opaque, addr, 4, val); + break; + } + ICH9_DEBUG("port=0x%04x val=0x%08x\n", addr, val); +} + +static uint32_t pm_ioport_readl(void *opaque, uint32_t addr) +{ + ICH9LPCPMRegs *pm = opaque; + uint32_t val; + + switch (addr & ICH9_PMIO_MASK) { + case ICH9_PMIO_PM1_TMR: + val = acpi_pm_tmr_get(&pm->acpi_regs); + break; + case ICH9_PMIO_SMI_EN: + val = pm->smi_en; + break; + + default: + val = pm_ioport_read_fallback(opaque, addr, 4); + break; + } + ICH9_DEBUG("port=0x%04x val=0x%08x\n", addr, val); + return val; +} + +static void pm_ioport_write_fallback(void *opaque, uint32_t addr, int len, + uint32_t val) + { + int subsize = (len == 4) ? 2 : 1; + IOPortWriteFunc *ioport_write = + (subsize == 2) ? pm_ioport_writew : pm_ioport_writeb; + + int i; + + for (i = 0; i < len; i += subsize) { + ioport_write(opaque, addr, val); + val >>= 8 * subsize; + } +} + +static uint32_t pm_ioport_read_fallback(void *opaque, uint32_t addr, int len) +{ + int subsize = (len == 4) ? 2 : 1; + IOPortReadFunc *ioport_read = + (subsize == 2) ? pm_ioport_readw : pm_ioport_readb; + + uint32_t val; + int i; + + val = 0; + for (i = 0; i < len; i += subsize) { + val <<= 8 * subsize; + val |= ioport_read(opaque, addr); + } + + return val; +} + +void ich9_pm_iospace_update(ICH9LPCPMRegs *pm, uint32_t pm_io_base) +{ + ICH9_DEBUG("to 0x%x\n", pm_io_base); + + assert((pm_io_base & ICH9_PMIO_MASK) == 0); + + if (pm->pm_io_base != 0) { + isa_unassign_ioport(pm->pm_io_base, ICH9_PMIO_SIZE); + } + + /* don't map at 0 */ + if (pm_io_base == 0) { + return; + } + + register_ioport_write(pm_io_base, ICH9_PMIO_SIZE, 1, pm_ioport_writeb, pm); + register_ioport_read(pm_io_base, ICH9_PMIO_SIZE, 1, pm_ioport_readb, pm); + register_ioport_write(pm_io_base, ICH9_PMIO_SIZE, 2, pm_ioport_writew, pm); + register_ioport_read(pm_io_base, ICH9_PMIO_SIZE, 2, pm_ioport_readw, pm); + register_ioport_write(pm_io_base, ICH9_PMIO_SIZE, 4, pm_ioport_writel, pm); + register_ioport_read(pm_io_base, ICH9_PMIO_SIZE, 4, pm_ioport_readl, pm); + + pm->pm_io_base = pm_io_base; + acpi_gpe_blk(&pm->acpi_regs, pm_io_base + ICH9_PMIO_GPE0_STS); +} + +static int ich9_pm_post_load(void *opaque, int version_id) +{ + ICH9LPCPMRegs *pm = opaque; + uint32_t pm_io_base = pm->pm_io_base; + pm->pm_io_base = 0; + ich9_pm_iospace_update(pm, pm_io_base); + return 0; +} + +#define VMSTATE_GPE_ARRAY(_field, _state) \ + { \ + .name = (stringify(_field)), \ + .version_id = 0, \ + .num = ICH9_PMIO_GPE0_LEN, \ + .info = &vmstate_info_uint8, \ + .size = sizeof(uint8_t), \ + .flags = VMS_ARRAY | VMS_POINTER, \ + .offset = vmstate_offset_pointer(_state, _field, uint8_t), \ + } + +const VMStateDescription vmstate_ich9_pm = { + .name = "ich9_pm", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .post_load = ich9_pm_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT16(acpi_regs.pm1.evt.sts, ICH9LPCPMRegs), + VMSTATE_UINT16(acpi_regs.pm1.evt.en, ICH9LPCPMRegs), + VMSTATE_UINT16(acpi_regs.pm1.cnt.cnt, ICH9LPCPMRegs), + VMSTATE_TIMER(acpi_regs.tmr.timer, ICH9LPCPMRegs), + VMSTATE_INT64(acpi_regs.tmr.overflow_time, ICH9LPCPMRegs), + VMSTATE_GPE_ARRAY(acpi_regs.gpe.sts, ICH9LPCPMRegs), + VMSTATE_GPE_ARRAY(acpi_regs.gpe.en, ICH9LPCPMRegs), + VMSTATE_UINT32(smi_en, ICH9LPCPMRegs), + VMSTATE_UINT32(smi_sts, ICH9LPCPMRegs), + VMSTATE_END_OF_LIST() + } +}; + +static void pm_reset(void *opaque) +{ + ICH9LPCPMRegs *pm = opaque; + ich9_pm_iospace_update(pm, 0); + + acpi_pm1_evt_reset(&pm->acpi_regs); + acpi_pm1_cnt_reset(&pm->acpi_regs); + acpi_pm_tmr_reset(&pm->acpi_regs); + acpi_gpe_reset(&pm->acpi_regs); + + pm_update_sci(pm); +} + +static void pm_powerdown_req(Notifier *n, void *opaque) +{ + ICH9LPCPMRegs *pm = container_of(n, ICH9LPCPMRegs, powerdown_notifier); + + acpi_pm1_evt_power_down(&pm->acpi_regs); +} + +void ich9_pm_init(ICH9LPCPMRegs *pm, qemu_irq sci_irq, qemu_irq cmos_s3) +{ + acpi_pm_tmr_init(&pm->acpi_regs, ich9_pm_update_sci_fn); + acpi_pm1_cnt_init(&pm->acpi_regs); + acpi_gpe_init(&pm->acpi_regs, ICH9_PMIO_GPE0_LEN); + + pm->irq = sci_irq; + qemu_register_reset(pm_reset, pm); + pm->powerdown_notifier.notify = pm_powerdown_req; + qemu_register_powerdown_notifier(&pm->powerdown_notifier); +} diff --git a/hw/acpi_ich9.h b/hw/acpi_ich9.h new file mode 100644 index 0000000..180c406 --- /dev/null +++ b/hw/acpi_ich9.h @@ -0,0 +1,47 @@ +/* + * QEMU GMCH/ICH9 LPC PM Emulation + * + * Copyright (c) 2009 Isaku Yamahata <yamahata at valinux co jp> + * VA Linux Systems Japan K.K. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/> + */ + +#ifndef HW_ACPI_ICH9_H +#define HW_ACPI_ICH9_H + +#include "acpi.h" + +typedef struct ICH9LPCPMRegs { + /* + * In ich9 spec says that pm1_cnt register is 32bit width and + * that the upper 16bits are reserved and unused. + * PM1a_CNT_BLK = 2 in FADT so it is defined as uint16_t. + */ + ACPIREGS acpi_regs; + uint32_t smi_en; + uint32_t smi_sts; + + qemu_irq irq; /* SCI */ + + uint32_t pm_io_base; + Notifier powerdown_notifier; +} ICH9LPCPMRegs; + +void ich9_pm_init(ICH9LPCPMRegs *pm, + qemu_irq sci_irq, qemu_irq cmos_s3_resume); +void ich9_pm_iospace_update(ICH9LPCPMRegs *pm, uint32_t pm_io_base); +extern const VMStateDescription vmstate_ich9_pm; + +#endif /* HW_ACPI_ICH9_H */ diff --git a/hw/i386/Makefile.objs b/hw/i386/Makefile.objs index 2f0c172..ba3744d 100644 --- a/hw/i386/Makefile.objs +++ b/hw/i386/Makefile.objs @@ -7,6 +7,7 @@ obj-y += debugcon.o multiboot.o obj-y += pc_piix.o obj-y += pc_sysfw.o obj-y += pam.o +obj-y += acpi_ich9.o obj-$(CONFIG_XEN) += xen_platform.o xen_apic.o obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen-host-pci-device.o obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pt.o xen_pt_config_init.o xen_pt_msi.o diff --git a/hw/ich9.h b/hw/ich9.h new file mode 100644 index 0000000..de49135 --- /dev/null +++ b/hw/ich9.h @@ -0,0 +1,207 @@ +#ifndef HW_ICH9_H +#define HW_ICH9_H + +#include "hw.h" +#include "range.h" +#include "isa.h" +#include "sysbus.h" +#include "pc.h" +#include "apm.h" +#include "ioapic.h" +#include "pci.h" +#include "pcie_host.h" +#include "pci_bridge.h" +#include "acpi.h" +#include "acpi_ich9.h" +#include "pam.h" +#include "pci_internals.h" + +void ich9_lpc_set_irq(void *opaque, int irq_num, int level); +int ich9_lpc_map_irq(PCIDevice *pci_dev, int intx); +void ich9_lpc_pm_init(PCIDevice *pci_lpc, qemu_irq cmos_s3); +PCIBus *ich9_d2pbr_init(PCIBus *bus, int devfn, int sec_bus); +i2c_bus *ich9_smb_init(PCIBus *bus, int devfn, uint32_t smb_io_base); + +#define ICH9_CC_SIZE (16 * 1024) /* 16KB */ + +#define TYPE_ICH9_LPC_DEVICE "ICH9 LPC" +#define ICH9_LPC_DEVICE(obj) \ + OBJECT_CHECK(ICH9LPCState, (obj), TYPE_ICH9_LPC_DEVICE) + +typedef struct ICH9LPCState { + /* ICH9 LPC PCI to ISA bridge */ + PCIDevice d; + + /* (pci device, intx) -> pirq + * In real chipset case, the unused slots are never used + * as ICH9 supports only D25-D32 irq routing. + * On the other hand in qemu case, any slot/function can be populated + * via command line option. + * So fallback interrupt routing for any devices in any slots is necessary. + */ + uint8_t irr[PCI_SLOT_MAX][PCI_NUM_PINS]; + + APMState apm; + ICH9LPCPMRegs pm; + uint32_t sci_level; /* track sci level */ + + /* 10.1 Chipset Configuration registers(Memory Space) + which is pointed by RCBA */ + uint8_t chip_config[ICH9_CC_SIZE]; + /* isa bus */ + ISABus *isa_bus; + MemoryRegion rbca_mem; + + qemu_irq *pic; + qemu_irq *ioapic; +} ICH9LPCState; + +#define Q35_MASK(bit, ms_bit, ls_bit) \ +((uint##bit##_t)(((1ULL << ((ms_bit) + 1)) - 1) & ~((1ULL << ls_bit) - 1))) + +/* ICH9: Chipset Configuration Registers */ +#define ICH9_CC_ADDR_MASK (ICH9_CC_SIZE - 1) + +#define ICH9_CC +#define ICH9_CC_D28IP 0x310C +#define ICH9_CC_D28IP_SHIFT 4 +#define ICH9_CC_D28IP_MASK 0xf +#define ICH9_CC_D28IP_DEFAULT 0x00214321 +#define ICH9_CC_D31IR 0x3140 +#define ICH9_CC_D30IR 0x3142 +#define ICH9_CC_D29IR 0x3144 +#define ICH9_CC_D28IR 0x3146 +#define ICH9_CC_D27IR 0x3148 +#define ICH9_CC_D26IR 0x314C +#define ICH9_CC_D25IR 0x3150 +#define ICH9_CC_DIR_DEFAULT 0x3210 +#define ICH9_CC_D30IR_DEFAULT 0x0 +#define ICH9_CC_DIR_SHIFT 4 +#define ICH9_CC_DIR_MASK 0x7 +#define ICH9_CC_OIC 0x31FF +#define ICH9_CC_OIC_AEN 0x1 + +/* D28:F[0-5] */ +#define ICH9_PCIE_DEV 28 +#define ICH9_PCIE_FUNC_MAX 6 + + +/* D29:F0 USB UHCI Controller #1 */ +#define ICH9_USB_UHCI1_DEV 29 +#define ICH9_USB_UHCI1_FUNC 0 + +/* D30:F0 DMI-to-PCI brdige */ +#define ICH9_D2P_BRIDGE "ICH9 D2P BRIDGE" +#define ICH9_D2P_BRIDGE_SAVEVM_VERSION 0 + +#define ICH9_D2P_BRIDGE_DEV 30 +#define ICH9_D2P_BRIDGE_FUNC 0 + +#define ICH9_D2P_SECONDARY_DEFAULT (256 - 8) + +#define ICH9_D2P_A2_REVISION 0x92 + + +/* D31:F1 LPC controller */ +#define ICH9_A2_LPC "ICH9 A2 LPC" +#define ICH9_A2_LPC_SAVEVM_VERSION 0 + +#define ICH9_LPC_DEV 31 +#define ICH9_LPC_FUNC 0 + +#define ICH9_A2_LPC_REVISION 0x2 +#define ICH9_LPC_NB_PIRQS 8 /* PCI A-H */ + +#define ICH9_LPC_PMBASE 0x40 +#define ICH9_LPC_PMBASE_BASE_ADDRESS_MASK Q35_MASK(32, 15, 7) +#define ICH9_LPC_PMBASE_RTE 0x1 +#define ICH9_LPC_PMBASE_DEFAULT 0x1 +#define ICH9_LPC_ACPI_CTRL 0x44 +#define ICH9_LPC_ACPI_CTRL_ACPI_EN 0x80 +#define ICH9_LPC_ACPI_CTRL_SCI_IRQ_SEL_MASK Q35_MASK(8, 2, 0) +#define ICH9_LPC_ACPI_CTRL_9 0x0 +#define ICH9_LPC_ACPI_CTRL_10 0x1 +#define ICH9_LPC_ACPI_CTRL_11 0x2 +#define ICH9_LPC_ACPI_CTRL_20 0x4 +#define ICH9_LPC_ACPI_CTRL_21 0x5 +#define ICH9_LPC_ACPI_CTRL_DEFAULT 0x0 + +#define ICH9_LPC_PIRQA_ROUT 0x60 +#define ICH9_LPC_PIRQB_ROUT 0x61 +#define ICH9_LPC_PIRQC_ROUT 0x62 +#define ICH9_LPC_PIRQD_ROUT 0x63 + +#define ICH9_LPC_PIRQE_ROUT 0x68 +#define ICH9_LPC_PIRQF_ROUT 0x69 +#define ICH9_LPC_PIRQG_ROUT 0x6a +#define ICH9_LPC_PIRQH_ROUT 0x6b + +#define ICH9_LPC_PIRQ_ROUT_IRQEN 0x80 +#define ICH9_LPC_PIRQ_ROUT_MASK Q35_MASK(8, 3, 0) +#define ICH9_LPC_PIRQ_ROUT_DEFAULT 0x80 + +#define ICH9_LPC_RCBA 0xf0 +#define ICH9_LPC_RCBA_BA_MASK Q35_MASK(32, 31, 14) +#define ICH9_LPC_RCBA_EN 0x1 +#define ICH9_LPC_RCBA_DEFAULT 0x0 + +#define ICH9_LPC_PIC_NUM_PINS 16 +#define ICH9_LPC_IOAPIC_NUM_PINS 24 + +/* D31:F2 SATA Controller #1 */ +#define ICH9_SATA1_DEV 31 +#define ICH9_SATA1_FUNC 2 + +/* D30:F1 power management I/O registers + offset from the address ICH9_LPC_PMBASE */ + +/* ICH9 LPC PM I/O registers are 128 ports and 128-aligned */ +#define ICH9_PMIO_SIZE 128 +#define ICH9_PMIO_MASK (ICH9_PMIO_SIZE - 1) + +#define ICH9_PMIO_PM1_STS 0x00 +#define ICH9_PMIO_PM1_EN 0x02 +#define ICH9_PMIO_PM1_CNT 0x04 +#define ICH9_PMIO_PM1_TMR 0x08 +#define ICH9_PMIO_GPE0_STS 0x20 +#define ICH9_PMIO_GPE0_EN 0x28 +#define ICH9_PMIO_GPE0_LEN 16 +#define ICH9_PMIO_SMI_EN 0x30 +#define ICH9_PMIO_SMI_EN_APMC_EN (1 << 5) +#define ICH9_PMIO_SMI_STS 0x34 + +/* FADT ACPI_ENABLE/ACPI_DISABLE */ +#define ICH9_APM_ACPI_ENABLE 0x2 +#define ICH9_APM_ACPI_DISABLE 0x3 + + +/* D31:F3 SMBus controller */ +#define ICH9_A2_SMB_REVISION 0x02 +#define ICH9_SMB_PI 0x00 + +#define ICH9_SMB_SMBMBAR0 0x10 +#define ICH9_SMB_SMBMBAR1 0x14 +#define ICH9_SMB_SMBM_BAR 0 +#define ICH9_SMB_SMBM_SIZE (1 << 8) +#define ICH9_SMB_SMB_BASE 0x20 +#define ICH9_SMB_SMB_BASE_BAR 4 +#define ICH9_SMB_SMB_BASE_SIZE (1 << 5) +#define ICH9_SMB_HOSTC 0x40 +#define ICH9_SMB_HOSTC_SSRESET ((uint8_t)(1 << 3)) +#define ICH9_SMB_HOSTC_I2C_EN ((uint8_t)(1 << 2)) +#define ICH9_SMB_HOSTC_SMB_SMI_EN ((uint8_t)(1 << 1)) +#define ICH9_SMB_HOSTC_HST_EN ((uint8_t)(1 << 0)) + +/* D31:F3 SMBus I/O and memory mapped I/O registers */ +#define ICH9_SMB_DEV 31 +#define ICH9_SMB_FUNC 3 + +#define ICH9_SMB_HST_STS 0x00 +#define ICH9_SMB_HST_CNT 0x02 +#define ICH9_SMB_HST_CMD 0x03 +#define ICH9_SMB_XMIT_SLVA 0x04 +#define ICH9_SMB_HST_D0 0x05 +#define ICH9_SMB_HST_D1 0x06 +#define ICH9_SMB_HOST_BLOCK_DB 0x07 + +#endif /* HW_ICH9_H */ diff --git a/hw/pci_ids.h b/hw/pci_ids.h index 26c1d5f..91da67f 100644 --- a/hw/pci_ids.h +++ b/hw/pci_ids.h @@ -36,6 +36,7 @@ #define PCI_CLASS_BRIDGE_HOST 0x0600 #define PCI_CLASS_BRIDGE_ISA 0x0601 #define PCI_CLASS_BRIDGE_PCI 0x0604 +#define PCI_CLASS_BRDIGE_PCI_INF_SUB 0x01 #define PCI_CLASS_BRIDGE_OTHER 0x0680 #define PCI_CLASS_COMMUNICATION_OTHER 0x0780 @@ -115,6 +116,17 @@ #define PCI_DEVICE_ID_INTEL_82371AB 0x7111 #define PCI_DEVICE_ID_INTEL_82371AB_2 0x7112 #define PCI_DEVICE_ID_INTEL_82371AB_3 0x7113 + +#define PCI_DEVICE_ID_INTEL_ICH9_0 0x2910 +#define PCI_DEVICE_ID_INTEL_ICH9_1 0x2917 +#define PCI_DEVICE_ID_INTEL_ICH9_2 0x2912 +#define PCI_DEVICE_ID_INTEL_ICH9_3 0x2913 +#define PCI_DEVICE_ID_INTEL_ICH9_4 0x2914 +#define PCI_DEVICE_ID_INTEL_ICH9_5 0x2919 +#define PCI_DEVICE_ID_INTEL_ICH9_6 0x2930 +#define PCI_DEVICE_ID_INTEL_ICH9_7 0x2916 +#define PCI_DEVICE_ID_INTEL_ICH9_8 0x2918 + #define PCI_DEVICE_ID_INTEL_82801I_UHCI1 0x2934 #define PCI_DEVICE_ID_INTEL_82801I_UHCI2 0x2935 #define PCI_DEVICE_ID_INTEL_82801I_UHCI3 0x2936