From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([208.118.235.92]:43531) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TQExS-000580-FC for qemu-devel@nongnu.org; Mon, 22 Oct 2012 06:10:13 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1TQExK-0002PD-0x for qemu-devel@nongnu.org; Mon, 22 Oct 2012 06:10:05 -0400 Received: from mx1.redhat.com ([209.132.183.28]:44710) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TQExJ-0002P0-NE for qemu-devel@nongnu.org; Mon, 22 Oct 2012 06:09:57 -0400 Date: Mon, 22 Oct 2012 13:12:01 +0200 From: "Michael S. Tsirkin" Message-ID: <20121022111201.GC30844@redhat.com> References: <09d1ef33fe429510a94ec4c8a5c40de83764eea7.1350677361.git.jbaron@redhat.com> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <09d1ef33fe429510a94ec4c8a5c40de83764eea7.1350677361.git.jbaron@redhat.com> Subject: Re: [Qemu-devel] [PATCH v3 13/26] ich9: Add the lpc chip List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: Jason Baron Cc: agraf@suse.de, aliguori@us.ibm.com, juzhang@redhat.com, jan.kiszka@siemens.com, qemu-devel@nongnu.org, armbru@redhat.com, blauwirbel@gmail.com, yamahata@valinux.co.jp, alex.williamson@redhat.com, kevin@koconnor.net, avi@redhat.com, mkletzan@redhat.com, pbonzini@redhat.com, lcapitulino@redhat.com, afaerber@suse.de, kraxel@redhat.com On Fri, Oct 19, 2012 at 04:43:34PM -0400, Jason Baron wrote: > From: Jason Baron > > Add support for the ich9 LPC chip. > > Signed-off-by: Isaku Yamahata > Signed-off-by: Jason Baron Applied, thanks. > --- > hw/i386/Makefile.objs | 2 +- > hw/lpc_ich9.c | 523 +++++++++++++++++++++++++++++++++++++++++++++++++ > 2 files changed, 524 insertions(+), 1 deletions(-) > create mode 100644 hw/lpc_ich9.c > > diff --git a/hw/i386/Makefile.objs b/hw/i386/Makefile.objs > index ba3744d..caf8982 100644 > --- a/hw/i386/Makefile.objs > +++ b/hw/i386/Makefile.objs > @@ -7,7 +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-y += acpi_ich9.o lpc_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/lpc_ich9.c b/hw/lpc_ich9.c > new file mode 100644 > index 0000000..d9fd9de > --- /dev/null > +++ b/hw/lpc_ich9.c > @@ -0,0 +1,523 @@ > +/* > + * Copyright (c) 2006 Fabrice Bellard > + * > + * Permission is hereby granted, free of charge, to any person obtaining a copy > + * of this software and associated documentation files (the "Software"), to deal > + * in the Software without restriction, including without limitation the rights > + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell > + * copies of the Software, and to permit persons to whom the Software is > + * furnished to do so, subject to the following conditions: > + * > + * The above copyright notice and this permission notice shall be included in > + * all copies or substantial portions of the Software. > + * > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL > + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, > + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN > + * THE SOFTWARE. > + */ > +/* > + * QEMU ICH9 Emulation > + * > + * Copyright (c) 2009, 2010, 2011 > + * Isaku Yamahata > + * VA Linux Systems Japan K.K. > + * Copyright (C) 2012 Jason Baron > + * > + * This is based on piix_pci.c, but heavily modified. > + * > + * 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 > + */ > + > +#include "qemu-common.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 "ich9.h" > +#include "acpi.h" > +#include "acpi_ich9.h" > +#include "pam.h" > +#include "pci_internals.h" > +#include "exec-memory.h" > + > +static int ich9_lpc_sci_irq(ICH9LPCState *lpc); > + > +/*****************************************************************************/ > +/* ICH9 LPC PCI to ISA bridge */ > + > +static void ich9_lpc_reset(DeviceState *qdev); > + > +/* chipset configuration register > + * to access chipset configuration registers, pci_[sg]et_{byte, word, long} > + * are used. > + * Although it's not pci configuration space, it's little endian as Intel. > + */ > + > +static void ich9_cc_update_ir(uint8_t irr[PCI_NUM_PINS], uint16_t ir) > +{ > + int intx; > + for (intx = 0; intx < PCI_NUM_PINS; intx++) { > + irr[intx] = (ir >> (intx * ICH9_CC_DIR_SHIFT)) & ICH9_CC_DIR_MASK; > + } > +} > + > +static void ich9_cc_update(ICH9LPCState *lpc) > +{ > + int slot; > + int pci_intx; > + > + const int reg_offsets[] = { > + ICH9_CC_D25IR, > + ICH9_CC_D26IR, > + ICH9_CC_D27IR, > + ICH9_CC_D28IR, > + ICH9_CC_D29IR, > + ICH9_CC_D30IR, > + ICH9_CC_D31IR, > + }; > + const int *offset; > + > + /* D{25 - 31}IR, but D30IR is read only to 0. */ > + for (slot = 25, offset = reg_offsets; slot < 32; slot++, offset++) { > + if (slot == 30) { > + continue; > + } > + ich9_cc_update_ir(lpc->irr[slot], > + pci_get_word(lpc->chip_config + *offset)); > + } > + > + /* > + * D30: DMI2PCI bridge > + * It is arbitrarily decided how INTx lines of PCI devicesbehind the bridge > + * are connected to pirq lines. Our choice is PIRQ[E-H]. > + * INT[A-D] are connected to PIRQ[E-H] > + */ > + for (pci_intx = 0; pci_intx < PCI_NUM_PINS; pci_intx++) { > + lpc->irr[30][pci_intx] = pci_intx + 4; > + } > +} > + > +static void ich9_cc_init(ICH9LPCState *lpc) > +{ > + int slot; > + int intx; > + > + /* the default irq routing is arbitrary as long as it matches with > + * acpi irq routing table. > + * The one that is incompatible with piix_pci(= bochs) one is > + * intentionally chosen to let the users know that the different > + * board is used. > + * > + * int[A-D] -> pirq[E-F] > + * avoid pirq A-D because they are used for pci express port > + */ > + for (slot = 0; slot < PCI_SLOT_MAX; slot++) { > + for (intx = 0; intx < PCI_NUM_PINS; intx++) { > + lpc->irr[slot][intx] = (slot + intx) % 4 + 4; > + } > + } > + ich9_cc_update(lpc); > +} > + > +static void ich9_cc_reset(ICH9LPCState *lpc) > +{ > + uint8_t *c = lpc->chip_config; > + > + memset(lpc->chip_config, 0, sizeof(lpc->chip_config)); > + > + pci_set_long(c + ICH9_CC_D31IR, ICH9_CC_DIR_DEFAULT); > + pci_set_long(c + ICH9_CC_D30IR, ICH9_CC_D30IR_DEFAULT); > + pci_set_long(c + ICH9_CC_D29IR, ICH9_CC_DIR_DEFAULT); > + pci_set_long(c + ICH9_CC_D28IR, ICH9_CC_DIR_DEFAULT); > + pci_set_long(c + ICH9_CC_D27IR, ICH9_CC_DIR_DEFAULT); > + pci_set_long(c + ICH9_CC_D26IR, ICH9_CC_DIR_DEFAULT); > + pci_set_long(c + ICH9_CC_D25IR, ICH9_CC_DIR_DEFAULT); > + > + ich9_cc_update(lpc); > +} > + > +static void ich9_cc_addr_len(uint64_t *addr, unsigned *len) > +{ > + *addr &= ICH9_CC_ADDR_MASK; > + if (*addr + *len >= ICH9_CC_SIZE) { > + *len = ICH9_CC_SIZE - *addr; > + } > +} > + > +/* val: little endian */ > +static void ich9_cc_write(void *opaque, target_phys_addr_t addr, > + uint64_t val, unsigned len) > +{ > + ICH9LPCState *lpc = (ICH9LPCState *)opaque; > + > + ich9_cc_addr_len(&addr, &len); > + memcpy(lpc->chip_config + addr, &val, len); > + ich9_cc_update(lpc); > +} > + > +/* return value: little endian */ > +static uint64_t ich9_cc_read(void *opaque, target_phys_addr_t addr, > + unsigned len) > +{ > + ICH9LPCState *lpc = (ICH9LPCState *)opaque; > + > + uint32_t val = 0; > + ich9_cc_addr_len(&addr, &len); > + memcpy(&val, lpc->chip_config + addr, len); > + return val; > +} > + > +/* IRQ routing */ > +/* */ > +static void ich9_lpc_rout(uint8_t pirq_rout, int *pic_irq, int *pic_dis) > +{ > + *pic_irq = pirq_rout & ICH9_LPC_PIRQ_ROUT_MASK; > + *pic_dis = pirq_rout & ICH9_LPC_PIRQ_ROUT_IRQEN; > +} > + > +static void ich9_lpc_pic_irq(ICH9LPCState *lpc, int pirq_num, > + int *pic_irq, int *pic_dis) > +{ > + switch (pirq_num) { > + case 0 ... 3: /* A-D */ > + ich9_lpc_rout(lpc->d.config[ICH9_LPC_PIRQA_ROUT + pirq_num], > + pic_irq, pic_dis); > + return; > + case 4 ... 7: /* E-H */ > + ich9_lpc_rout(lpc->d.config[ICH9_LPC_PIRQE_ROUT + (pirq_num - 4)], > + pic_irq, pic_dis); > + return; > + default: > + break; > + } > + abort(); > +} > + > +/* pic_irq: i8254 irq 0-15 */ > +static void ich9_lpc_update_pic(ICH9LPCState *lpc, int pic_irq) > +{ > + int i, pic_level; > + > + /* The pic level is the logical OR of all the PCI irqs mapped to it */ > + pic_level = 0; > + for (i = 0; i < ICH9_LPC_NB_PIRQS; i++) { > + int tmp_irq; > + int tmp_dis; > + ich9_lpc_pic_irq(lpc, i, &tmp_irq, &tmp_dis); > + if (!tmp_dis && pic_irq == tmp_irq) { > + pic_level |= pci_bus_get_irq_level(lpc->d.bus, i); > + } > + } > + if (pic_irq == ich9_lpc_sci_irq(lpc)) { > + pic_level |= lpc->sci_level; > + } > + > + qemu_set_irq(lpc->pic[pic_irq], pic_level); > +} > + > +/* pirq: pirq[A-H] 0-7*/ > +static void ich9_lpc_update_by_pirq(ICH9LPCState *lpc, int pirq) > +{ > + int pic_irq; > + int pic_dis; > + > + ich9_lpc_pic_irq(lpc, pirq, &pic_irq, &pic_dis); > + assert(pic_irq < ICH9_LPC_PIC_NUM_PINS); > + if (pic_dis) { > + return; > + } > + > + ich9_lpc_update_pic(lpc, pic_irq); > +} > + > +/* APIC mode: GSIx: PIRQ[A-H] -> GSI 16, ... no pirq shares same APIC pins. */ > +static int ich9_pirq_to_gsi(int pirq) > +{ > + return pirq + ICH9_LPC_PIC_NUM_PINS; > +} > + > +static int ich9_gsi_to_pirq(int gsi) > +{ > + return gsi - ICH9_LPC_PIC_NUM_PINS; > +} > + > +static void ich9_lpc_update_apic(ICH9LPCState *lpc, int gsi) > +{ > + int level; > + > + level = pci_bus_get_irq_level(lpc->d.bus, ich9_gsi_to_pirq(gsi)); > + if (gsi == ich9_lpc_sci_irq(lpc)) { > + level |= lpc->sci_level; > + } > + > + qemu_set_irq(lpc->ioapic[gsi], level); > +} > + > +void ich9_lpc_set_irq(void *opaque, int pirq, int level) > +{ > + ICH9LPCState *lpc = opaque; > + > + assert(0 <= pirq); > + assert(pirq < ICH9_LPC_NB_PIRQS); > + > + ich9_lpc_update_apic(lpc, ich9_pirq_to_gsi(pirq)); > + ich9_lpc_update_by_pirq(lpc, pirq); > +} > + > +/* return the pirq number (PIRQ[A-H]:0-7) corresponding to > + * a given device irq pin. > + */ > +int ich9_lpc_map_irq(PCIDevice *pci_dev, int intx) > +{ > + BusState *bus = qdev_get_parent_bus(&pci_dev->qdev); > + PCIBus *pci_bus = PCI_BUS(bus); > + PCIDevice *lpc_pdev = > + pci_bus->devices[PCI_DEVFN(ICH9_LPC_DEV, ICH9_LPC_FUNC)]; > + ICH9LPCState *lpc = ICH9_LPC_DEVICE(lpc_pdev); > + > + return lpc->irr[PCI_SLOT(pci_dev->devfn)][intx]; > +} > + > +static int ich9_lpc_sci_irq(ICH9LPCState *lpc) > +{ > + switch (lpc->d.config[ICH9_LPC_ACPI_CTRL] & > + ICH9_LPC_ACPI_CTRL_SCI_IRQ_SEL_MASK) { > + case ICH9_LPC_ACPI_CTRL_9: > + return 9; > + case ICH9_LPC_ACPI_CTRL_10: > + return 10; > + case ICH9_LPC_ACPI_CTRL_11: > + return 11; > + case ICH9_LPC_ACPI_CTRL_20: > + return 20; > + case ICH9_LPC_ACPI_CTRL_21: > + return 21; > + default: > + /* reserved */ > + break; > + } > + return -1; > +} > + > +static void ich9_set_sci(void *opaque, int irq_num, int level) > +{ > + ICH9LPCState *lpc = opaque; > + int irq; > + > + assert(irq_num == 0); > + level = !!level; > + if (level == lpc->sci_level) { > + return; > + } > + lpc->sci_level = level; > + > + irq = ich9_lpc_sci_irq(lpc); > + if (irq < 0) { > + return; > + } > + > + ich9_lpc_update_apic(lpc, irq); > + if (irq < ICH9_LPC_PIC_NUM_PINS) { > + ich9_lpc_update_pic(lpc, irq); > + } > +} > + > +void ich9_lpc_pm_init(PCIDevice *lpc_pci, qemu_irq cmos_s3) > +{ > + ICH9LPCState *lpc = ICH9_LPC_DEVICE(lpc_pci); > + qemu_irq *sci_irq; > + > + sci_irq = qemu_allocate_irqs(ich9_set_sci, lpc, 1); > + ich9_pm_init(&lpc->pm, sci_irq[0], cmos_s3); > + > + ich9_lpc_reset(&lpc->d.qdev); > +} > + > +/* APM */ > + > +static void ich9_apm_ctrl_changed(uint32_t val, void *arg) > +{ > + ICH9LPCState *lpc = arg; > + > + /* ACPI specs 3.0, 4.7.2.5 */ > + acpi_pm1_cnt_update(&lpc->pm.acpi_regs, > + val == ICH9_APM_ACPI_ENABLE, > + val == ICH9_APM_ACPI_DISABLE); > + > + /* SMI_EN = PMBASE + 30. SMI control and enable register */ > + if (lpc->pm.smi_en & ICH9_PMIO_SMI_EN_APMC_EN) { > + cpu_interrupt(first_cpu, CPU_INTERRUPT_SMI); > + } > +} > + > +/* config:PMBASE */ > +static void > +ich9_lpc_pmbase_update(ICH9LPCState *lpc) > +{ > + uint32_t pm_io_base = pci_get_long(lpc->d.config + ICH9_LPC_PMBASE); > + pm_io_base &= ICH9_LPC_PMBASE_BASE_ADDRESS_MASK; > + > + ich9_pm_iospace_update(&lpc->pm, pm_io_base); > +} > + > +/* config:RBCA */ > +static void ich9_lpc_rcba_update(ICH9LPCState *lpc, uint32_t rbca_old) > +{ > + uint32_t rbca = pci_get_long(lpc->d.config + ICH9_LPC_RCBA); > + > + if (rbca_old & ICH9_LPC_RCBA_EN) { > + memory_region_del_subregion(get_system_memory(), &lpc->rbca_mem); > + } > + if (rbca & ICH9_LPC_RCBA_EN) { > + memory_region_add_subregion_overlap(get_system_memory(), > + rbca & ICH9_LPC_RCBA_BA_MASK, > + &lpc->rbca_mem, 1); > + } > +} > + > +static int ich9_lpc_post_load(void *opaque, int version_id) > +{ > + ICH9LPCState *lpc = opaque; > + > + ich9_lpc_pmbase_update(lpc); > + ich9_lpc_rcba_update(lpc, 0 /* disabled ICH9_LPC_RBCA_EN */); > + return 0; > +} > + > +static void ich9_lpc_config_write(PCIDevice *d, > + uint32_t addr, uint32_t val, int len) > +{ > + ICH9LPCState *lpc = ICH9_LPC_DEVICE(d); > + uint32_t rbca_old = pci_get_long(d->config + ICH9_LPC_RCBA); > + > + pci_default_write_config(d, addr, val, len); > + if (ranges_overlap(addr, len, ICH9_LPC_PMBASE, 4)) { > + ich9_lpc_pmbase_update(lpc); > + } > + if (ranges_overlap(addr, len, ICH9_LPC_RCBA, 4)) { > + ich9_lpc_rcba_update(lpc, rbca_old); > + } > +} > + > +static void ich9_lpc_reset(DeviceState *qdev) > +{ > + PCIDevice *d = PCI_DEVICE(qdev); > + ICH9LPCState *lpc = ICH9_LPC_DEVICE(d); > + uint32_t rbca_old = pci_get_long(d->config + ICH9_LPC_RCBA); > + int i; > + > + for (i = 0; i < 4; i++) { > + pci_set_byte(d->config + ICH9_LPC_PIRQA_ROUT + i, > + ICH9_LPC_PIRQ_ROUT_DEFAULT); > + } > + for (i = 0; i < 4; i++) { > + pci_set_byte(d->config + ICH9_LPC_PIRQE_ROUT + i, > + ICH9_LPC_PIRQ_ROUT_DEFAULT); > + } > + pci_set_byte(d->config + ICH9_LPC_ACPI_CTRL, ICH9_LPC_ACPI_CTRL_DEFAULT); > + > + pci_set_long(d->config + ICH9_LPC_PMBASE, ICH9_LPC_PMBASE_DEFAULT); > + pci_set_long(d->config + ICH9_LPC_RCBA, ICH9_LPC_RCBA_DEFAULT); > + > + ich9_cc_reset(lpc); > + > + ich9_lpc_pmbase_update(lpc); > + ich9_lpc_rcba_update(lpc, rbca_old); > + > + lpc->sci_level = 0; > +} > + > +static const MemoryRegionOps rbca_mmio_ops = { > + .read = ich9_cc_read, > + .write = ich9_cc_write, > + .endianness = DEVICE_LITTLE_ENDIAN, > +}; > + > +static int ich9_lpc_initfn(PCIDevice *d) > +{ > + ICH9LPCState *lpc = ICH9_LPC_DEVICE(d); > + ISABus *isa_bus; > + > + isa_bus = isa_bus_new(&d->qdev, get_system_io()); > + > + pci_set_long(d->wmask + ICH9_LPC_PMBASE, > + ICH9_LPC_PMBASE_BASE_ADDRESS_MASK); > + > + memory_region_init_io(&lpc->rbca_mem, &rbca_mmio_ops, lpc, > + "lpc-rbca-mmio", ICH9_CC_SIZE); > + > + lpc->isa_bus = isa_bus; > + > + ich9_cc_init(lpc); > + apm_init(&lpc->apm, ich9_apm_ctrl_changed, lpc); > + return 0; > +} > + > +static const VMStateDescription vmstate_ich9_lpc = { > + .name = "ICH9LPC", > + .version_id = 1, > + .minimum_version_id = 1, > + .minimum_version_id_old = 1, > + .post_load = ich9_lpc_post_load, > + .fields = (VMStateField[]) { > + VMSTATE_PCI_DEVICE(d, ICH9LPCState), > + VMSTATE_STRUCT(apm, ICH9LPCState, 0, vmstate_apm, APMState), > + VMSTATE_STRUCT(pm, ICH9LPCState, 0, vmstate_ich9_pm, ICH9LPCPMRegs), > + VMSTATE_UINT8_ARRAY(chip_config, ICH9LPCState, ICH9_CC_SIZE), > + VMSTATE_UINT32(sci_level, ICH9LPCState), > + VMSTATE_END_OF_LIST() > + } > +}; > + > +static void ich9_lpc_class_init(ObjectClass *klass, void *data) > +{ > + DeviceClass *dc = DEVICE_CLASS(klass); > + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); > + > + dc->reset = ich9_lpc_reset; > + k->init = ich9_lpc_initfn; > + dc->vmsd = &vmstate_ich9_lpc; > + dc->no_user = 1; > + k->config_write = ich9_lpc_config_write; > + dc->desc = "ICH9 LPC bridge"; > + k->vendor_id = PCI_VENDOR_ID_INTEL; > + k->device_id = PCI_DEVICE_ID_INTEL_ICH9_8; > + k->revision = ICH9_A2_LPC_REVISION; > + k->class_id = PCI_CLASS_BRIDGE_ISA; > + > +} > + > +static const TypeInfo ich9_lpc_info = { > + .name = TYPE_ICH9_LPC_DEVICE, > + .parent = TYPE_PCI_DEVICE, > + .instance_size = sizeof(struct ICH9LPCState), > + .class_init = ich9_lpc_class_init, > +}; > + > +static void ich9_lpc_register(void) > +{ > + type_register_static(&ich9_lpc_info); > +} > + > +type_init(ich9_lpc_register); > -- > 1.7.1