[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH 08/14] pcibos: initialize q35 chipset.
From: |
Isaku Yamahata |
Subject: |
[Qemu-devel] [PATCH 08/14] pcibos: initialize q35 chipset. |
Date: |
Wed, 30 Sep 2009 19:18:43 +0900 |
teach pcbios q35 chipset to initialize it properly.
Signed-off-by: Isaku Yamahata <address@hidden>
---
rombios32.c | 296 ++++++++++++++++++++++++++++++++++++++++++++++-------------
1 files changed, 233 insertions(+), 63 deletions(-)
diff --git a/rombios32.c b/rombios32.c
index 725f206..f7e906f 100644
--- a/rombios32.c
+++ b/rombios32.c
@@ -449,6 +449,7 @@ uint64_t ram_end;
unsigned long ebda_cur_addr;
#endif
int acpi_enabled;
+uint8_t acpi_enable, acpi_disable;
uint32_t pm_io_base, smb_io_base;
int pm_sci_int;
unsigned long bios_table_cur_addr;
@@ -710,6 +711,24 @@ void smp_probe(void)
#define PCI_DEVICE_ID_INTEL_82371AB 0x7111
#define PCI_DEVICE_ID_INTEL_82371AB_3 0x7113
+#define PCI_DEVICE_ID_INTEL_Q35_MCH 0x29c0
+#define Q35_HOST_BRIDGE_PAM0 0x90
+#define Q35_HOST_BRDIGE_SMRAM 0x9d
+
+#define PCI_DEVICE_ID_INTEL_ICH9_LPC 0x2918
+#define ICH9_LPC_PMBASE 0x40
+#define ICH9_PMBASE_SMI_EN 0x30
+#define ICH9_PMBASE_SMI_EN_APMC_EN (1 << 5)
+#define ICH9_LPC_ACPI_CTRL 0x44
+#define ICH9_LPC_ACPI_CTRL_ACPI_EN 0x80
+#define PCI_DEVICE_ID_INTEL_ICH9_SMBUS 0x2930
+#define ICH9_SMB_SMB_BASE 0x20
+#define ICH9_SMB_HOSTC 0x40
+#define ICH9_SMB_HOSTC_HST_EN 0x01
+
+#define ICH9_ACPI_ENABLE 0x2
+#define ICH9_ACPI_DISABLE 0x3
+
#define PCI_VENDOR_ID_IBM 0x1014
#define PCI_VENDOR_ID_APPLE 0x106b
@@ -726,6 +745,9 @@ static uint32_t pci_bios_prefmem_addr;
/* host irqs corresponding to PCI irqs A-D */
static uint8_t pci_irqs[4] = { 10, 10, 11, 11 };
static PCIDevice i440_pcidev;
+static int i440_found;
+static PCIDevice mch_pcidev;
+static int mch_found;
static void pci_config_writel(PCIDevice *d, uint32_t addr, uint32_t val)
{
@@ -815,7 +837,9 @@ static void find_bios_table_area(void)
return;
}
-static void bios_shadow_init(PCIDevice *d)
+/* i440 and mch have same pam0 register format with different offset */
+static void bios_shadow_init_common(PCIDevice *d, uint32_t pam0_addr,
+ PCIDevice *host_bridge, int *host_found)
{
int v;
@@ -824,43 +848,62 @@ static void bios_shadow_init(PCIDevice *d)
/* remap the BIOS to shadow RAM an keep it read/write while we
are writing tables */
- v = pci_config_readb(d, 0x59);
+ v = pci_config_readb(d, pam0_addr);
v &= 0xcf;
- pci_config_writeb(d, 0x59, v);
+ pci_config_writeb(d, pam0_addr, v);
memcpy((void *)BIOS_TMP_STORAGE, (void *)0x000f0000, 0x10000);
v |= 0x30;
- pci_config_writeb(d, 0x59, v);
+ pci_config_writeb(d, pam0_addr, v);
memcpy((void *)0x000f0000, (void *)BIOS_TMP_STORAGE, 0x10000);
- i440_pcidev = *d;
+ *host_bridge = *d;
+ *host_found = 1;
}
-static void bios_lock_shadow_ram(void)
+static void bios_lock_shadow_ram_common(PCIDevice *d, uint32_t pam0_addr)
{
- PCIDevice *d = &i440_pcidev;
int v;
wbinvd();
- v = pci_config_readb(d, 0x59);
+ v = pci_config_readb(d, pam0_addr);
v = (v & 0x0f) | (0x10);
- pci_config_writeb(d, 0x59, v);
+ pci_config_writeb(d, pam0_addr, v);
}
-static void pci_bios_init_bridges(PCIDevice *d)
+static void i440_bios_shadow_init(PCIDevice *d)
{
- uint16_t vendor_id, device_id;
+ bios_shadow_init_common(d, 0x59, &i440_pcidev, &i440_found);
+}
- vendor_id = pci_config_readw(d, PCI_VENDOR_ID);
- device_id = pci_config_readw(d, PCI_DEVICE_ID);
+static void i440_bios_lock_shadow_ram(PCIDevice *d)
+{
+ bios_lock_shadow_ram_common(d, 0x59);
+}
- if (vendor_id == PCI_VENDOR_ID_INTEL &&
- (device_id == PCI_DEVICE_ID_INTEL_82371SB_0 ||
- device_id == PCI_DEVICE_ID_INTEL_82371AB_0)) {
+static void mch_bios_shadow_init(PCIDevice *d)
+{
+ bios_shadow_init_common(d, Q35_HOST_BRIDGE_PAM0, &mch_pcidev, &mch_found);
+}
+
+static void mch_bios_lock_shadow_ram(PCIDevice *d)
+{
+ bios_lock_shadow_ram_common(d, Q35_HOST_BRIDGE_PAM0);
+}
+
+static void bios_lock_shadow_ram(void)
+{
+ if (i440_found)
+ i440_bios_lock_shadow_ram(&i440_pcidev);
+ if (mch_found)
+ mch_bios_lock_shadow_ram(&mch_pcidev);
+}
+
+/* piix and ich9 have same pirq a-d register format with different offset */
+static void pci_init_isa_bridge(PCIDevice *d, uint32_t addr, const char *name)
+{
int i, irq;
uint8_t elcr[2];
- /* PIIX3/PIIX4 PCI to ISA bridge */
-
elcr[0] = 0x00;
elcr[1] = 0x00;
for(i = 0; i < 4; i++) {
@@ -868,15 +911,37 @@ static void pci_bios_init_bridges(PCIDevice *d)
/* set to trigger level */
elcr[irq >> 3] |= (1 << (irq & 7));
/* activate irq remapping in PIIX */
- pci_config_writeb(d, 0x60 + i, irq);
+ pci_config_writeb(d, addr + i, irq);
}
outb(0x4d0, elcr[0]);
outb(0x4d1, elcr[1]);
- BX_INFO("PIIX3/PIIX4 init: elcr=%02x %02x\n",
- elcr[0], elcr[1]);
- } else if (vendor_id == PCI_VENDOR_ID_INTEL && device_id ==
PCI_DEVICE_ID_INTEL_82441) {
+ BX_INFO("%s init: elcr=%02x %02x\n", name, elcr[0], elcr[1]);
+}
+
+static void pci_bios_init_bridges(PCIDevice *d)
+{
+ uint16_t vendor_id, device_id;
+
+ vendor_id = pci_config_readw(d, PCI_VENDOR_ID);
+ device_id = pci_config_readw(d, PCI_DEVICE_ID);
+
+ if (vendor_id == PCI_VENDOR_ID_INTEL &&
+ (device_id == PCI_DEVICE_ID_INTEL_82371SB_0 ||
+ device_id == PCI_DEVICE_ID_INTEL_82371AB_0)) {
+ /* PIIX3/PIIX4 PCI to ISA bridge */
+ pci_init_isa_bridge(d, 0x60, "PIIX3/PIIX4");
+ } else if (vendor_id == PCI_VENDOR_ID_INTEL &&
+ device_id == PCI_DEVICE_ID_INTEL_ICH9_LPC) {
+ /* ICH9 LPC PCI to ISA bridge */
+ pci_init_isa_bridge(d, 0x60, "ICH9 LPC");
+ } else if (vendor_id == PCI_VENDOR_ID_INTEL &&
+ device_id == PCI_DEVICE_ID_INTEL_82441) {
/* i440 PCI bridge */
- bios_shadow_init(d);
+ i440_bios_shadow_init(d);
+ } else if (vendor_id == PCI_VENDOR_ID_INTEL &&
+ device_id == PCI_DEVICE_ID_INTEL_Q35_MCH) {
+ /* ich9 PCI host bridge */
+ mch_bios_shadow_init(d);
}
}
@@ -884,46 +949,64 @@ extern uint8_t smm_relocation_start, smm_relocation_end;
extern uint8_t smm_code_start, smm_code_end;
#ifdef BX_USE_SMM
-static void smm_init(PCIDevice *d)
+/* i440 and mch have same smram format with different offset
+ * and they have same 0xb2, 0xb3 io port
+ */
+static void smm_init(PCIDevice *d,
+ PCIDevice *host_bridge, uint32_t smram_addr,
+ void (*apmc_en)(PCIDevice *d))
{
- uint32_t value;
+ /* enable the SMM memory window */
+ pci_config_writeb(host_bridge, smram_addr, 0x02 | 0x48);
- /* check if SMM init is already done */
- value = pci_config_readl(d, 0x58);
- if ((value & (1 << 25)) == 0) {
+ /* save original memory content */
+ memcpy((void *)0xa8000, (void *)0x38000, 0x8000);
+
+ /* copy the SMM relocation code */
+ memcpy((void *)0x38000, &smm_relocation_start,
+ &smm_relocation_end - &smm_relocation_start);
- /* enable the SMM memory window */
- pci_config_writeb(&i440_pcidev, 0x72, 0x02 | 0x48);
+ /* enable SMI generation when writing to the APMC register */
+ apmc_en(d);
- /* save original memory content */
- memcpy((void *)0xa8000, (void *)0x38000, 0x8000);
+ /* init APM status port */
+ outb(0xb3, 0x01);
- /* copy the SMM relocation code */
- memcpy((void *)0x38000, &smm_relocation_start,
- &smm_relocation_end - &smm_relocation_start);
+ /* raise an SMI interrupt */
+ outb(0xb2, 0x00);
- /* enable SMI generation when writing to the APMC register */
- pci_config_writel(d, 0x58, value | (1 << 25));
+ /* wait until SMM code executed */
+ while (inb(0xb3) != 0x00);
- /* init APM status port */
- outb(0xb3, 0x01);
+ /* restore original memory content */
+ memcpy((void *)0x38000, (void *)0xa8000, 0x8000);
- /* raise an SMI interrupt */
- outb(0xb2, 0x00);
+ /* copy the SMM code */
+ memcpy((void *)0xa8000, &smm_code_start,
+ &smm_code_end - &smm_code_start);
+ wbinvd();
- /* wait until SMM code executed */
- while (inb(0xb3) != 0x00);
+ /* close the SMM memory window and enable normal SMM */
+ pci_config_writeb(host_bridge, smram_addr, 0x02 | 0x08);
+}
+#endif
- /* restore original memory content */
- memcpy((void *)0x38000, (void *)0xa8000, 0x8000);
+#ifdef BX_USE_SMM
+static void piix_apmc_enable(PCIDevice *d)
+{
+ uint32_t value;
+ value = pci_config_readl(d, 0x58);
+ pci_config_writel(d, 0x58, value | (1 << 25));
+}
- /* copy the SMM code */
- memcpy((void *)0xa8000, &smm_code_start,
- &smm_code_end - &smm_code_start);
- wbinvd();
+static void piix4_smm_init(PCIDevice *d)
+{
+ uint32_t value;
- /* close the SMM memory window and enable normal SMM */
- pci_config_writeb(&i440_pcidev, 0x72, 0x02 | 0x08);
+ /* check if SMM init is already done */
+ value = pci_config_readl(d, 0x58);
+ if ((value & (1 << 25)) == 0) {
+ smm_init(d, &i440_pcidev, 0x72, &piix_apmc_enable);
}
}
#endif
@@ -931,15 +1014,61 @@ static void smm_init(PCIDevice *d)
static void piix4_pm_enable(PCIDevice *d)
{
/* PIIX4 Power Management device (for ACPI) */
- pci_config_writel(d, 0x40, PM_IO_BASE | 1);
+ pci_config_writel(d, 0x40, pm_io_base | 1);
pci_config_writeb(d, 0x80, 0x01); /* enable PM io space */
- pci_config_writel(d, 0x90, SMB_IO_BASE | 1);
+ pci_config_writel(d, 0x90, smb_io_base | 1);
pci_config_writeb(d, 0xd2, 0x09); /* enable SMBus io space */
#ifdef BX_USE_SMM
- smm_init(d);
+ piix4_smm_init(d);
#endif
}
+#ifdef BX_USE_SMM
+static void ich9_apmc_enable(PCIDevice *d)
+{
+ uint32_t value;
+ value = inl(pm_io_base + ICH9_PMBASE_SMI_EN);
+ value |= ICH9_PMBASE_SMI_EN_APMC_EN;
+ outl(pm_io_base + ICH9_PMBASE_SMI_EN, value);
+}
+
+static void ich9_smm_init(PCIDevice *d)
+{
+ uint32_t value;
+
+ /* check if SMM init is already done */
+ value = inl(pm_io_base + ICH9_PMBASE_SMI_EN);
+ if ((value & ICH9_PMBASE_SMI_EN_APMC_EN) == 0) {
+ smm_init(d, &mch_pcidev,
+ Q35_HOST_BRDIGE_SMRAM, &ich9_apmc_enable);
+ }
+}
+#endif
+
+static void ich9_lpc_pm_enable(PCIDevice *d)
+{
+ uint8_t v;
+
+ pci_config_writel(d, ICH9_LPC_PMBASE, pm_io_base | 1);
+
+ v = pci_config_readb(d, ICH9_LPC_ACPI_CTRL);
+ v |= ICH9_LPC_ACPI_CTRL_ACPI_EN;
+ pci_config_writeb(d, ICH9_LPC_ACPI_CTRL, v);
+#ifdef BX_USE_SMM
+ ich9_smm_init(d);
+#endif
+}
+
+static void ich9_smbus_enable(PCIDevice *d)
+{
+ /* map smbus into io space */
+ pci_config_writel(d, ICH9_SMB_SMB_BASE,
+ smb_io_base | 1 /* 1 = io space */);
+
+ /* enable SMBus */
+ pci_config_writeb(d, ICH9_SMB_HOSTC, ICH9_SMB_HOSTC_HST_EN);
+}
+
static void pci_align_addr(uint32_t *paddr, uint32_t size)
{
*paddr = (*paddr + size - 1) & ~(size - 1);
@@ -1052,6 +1181,24 @@ static void pci_bios_init_device(PCIDevice *d)
pm_sci_int = pci_config_readb(d, PCI_INTERRUPT_LINE);
piix4_pm_enable(d);
acpi_enabled = 1;
+ acpi_enable = 0xf1;
+ acpi_disable = 0xf0;
+ } else if (vendor_id == PCI_VENDOR_ID_INTEL &&
+ device_id == PCI_DEVICE_ID_INTEL_ICH9_LPC) {
+ pm_io_base = PM_IO_BASE;
+
+ // acpi sci defaults to 9
+ pci_config_writeb(d, ICH9_LPC_ACPI_CTRL, 0 /* 000b = irq9 */);
+ pci_config_writeb(d, PCI_INTERRUPT_LINE, 9);
+ pm_sci_int = pci_config_readb(d, PCI_INTERRUPT_LINE);
+ ich9_lpc_pm_enable(d);
+ acpi_enabled = 1;
+ acpi_enable = ICH9_ACPI_ENABLE;
+ acpi_disable = ICH9_ACPI_DISABLE;
+ } else if (vendor_id == PCI_VENDOR_ID_INTEL &&
+ device_id == PCI_DEVICE_ID_INTEL_ICH9_SMBUS) {
+ smb_io_base = SMB_IO_BASE;
+ ich9_smbus_enable(d);
}
}
@@ -1789,19 +1936,28 @@ void acpi_bios_init(void)
fadt->model = 1;
fadt->reserved1 = 0;
fadt->sci_int = cpu_to_le16(pm_sci_int);
+ /* both piix4 and ich9 user io port 0xb2 = SMI_CMD_IO_ADDR, 0xb3 */
fadt->smi_cmd = cpu_to_le32(SMI_CMD_IO_ADDR);
- fadt->acpi_enable = 0xf1;
- fadt->acpi_disable = 0xf0;
+ fadt->acpi_enable = acpi_enable;
+ fadt->acpi_disable = acpi_disable;
+
+ /* both piix4 and ich9 have same offset from pm_io_base and length */
fadt->pm1a_evt_blk = cpu_to_le32(pm_io_base);
fadt->pm1a_cnt_blk = cpu_to_le32(pm_io_base + 0x04);
fadt->pm_tmr_blk = cpu_to_le32(pm_io_base + 0x08);
fadt->pm1_evt_len = 4;
fadt->pm1_cnt_len = 2;
fadt->pm_tmr_len = 4;
+
fadt->plvl2_lat = cpu_to_le16(0xfff); // C2 state not supported
fadt->plvl3_lat = cpu_to_le16(0xfff); // C3 state not supported
- fadt->gpe0_blk = cpu_to_le32(0xafe0);
- fadt->gpe0_blk_len = 4;
+ if (i440_found) {
+ fadt->gpe0_blk = cpu_to_le32(0xafe0);
+ fadt->gpe0_blk_len = 4;
+ } else if (mch_found) {
+ fadt->gpe0_blk = cpu_to_le32(pm_io_base + 0x20);
+ fadt->gpe0_blk_len = 0x10;
+ }
/* WBINVD + PROC_C1 + SLP_BUTTON + FIX_RTC */
fadt->flags = cpu_to_le32((1 << 0) | (1 << 2) | (1 << 5) | (1 << 6));
acpi_build_table_header((struct acpi_table_header *)fadt, "FACP",
@@ -2715,18 +2871,24 @@ static uint32_t find_resume_vector(void)
return 0;
}
-static void find_440fx(PCIDevice *d)
+static void find_hostbridge(PCIDevice *d)
{
uint16_t vendor_id, device_id;
vendor_id = pci_config_readw(d, PCI_VENDOR_ID);
device_id = pci_config_readw(d, PCI_DEVICE_ID);
- if (vendor_id == PCI_VENDOR_ID_INTEL && device_id ==
PCI_DEVICE_ID_INTEL_82441)
+ if (vendor_id == PCI_VENDOR_ID_INTEL && device_id ==
PCI_DEVICE_ID_INTEL_82441) {
i440_pcidev = *d;
+ i440_found = 1;
+ }
+ if (vendor_id == PCI_VENDOR_ID_INTEL && device_id ==
PCI_DEVICE_ID_INTEL_Q35_MCH) {
+ mch_pcidev = *d;
+ mch_found = 1;
+ }
}
-static void reinit_piix4_pm(PCIDevice *d)
+static void reinit_pm(PCIDevice *d)
{
uint16_t vendor_id, device_id;
@@ -2735,6 +2897,14 @@ static void reinit_piix4_pm(PCIDevice *d)
if (vendor_id == PCI_VENDOR_ID_INTEL && device_id ==
PCI_DEVICE_ID_INTEL_82371AB_3)
piix4_pm_enable(d);
+
+ if (vendor_id == PCI_VENDOR_ID_INTEL &&
+ device_id == PCI_DEVICE_ID_INTEL_ICH9_LPC)
+ ich9_lpc_pm_enable(d);
+
+ if (vendor_id == PCI_VENDOR_ID_INTEL &&
+ device_id == PCI_DEVICE_ID_INTEL_ICH9_SMBUS)
+ ich9_smbus_enable(d);
}
void rombios32_init(uint32_t *s3_resume_vector, uint8_t *shutdown_flag)
@@ -2764,14 +2934,14 @@ void rombios32_init(uint32_t *s3_resume_vector, uint8_t
*shutdown_flag)
if (*shutdown_flag == 0xfe) {
/* redirect bios read access to RAM */
- pci_for_each_device(find_440fx);
+ pci_for_each_device(find_hostbridge);
bios_lock_shadow_ram(); /* bios is already copied */
*s3_resume_vector = find_resume_vector();
if (!*s3_resume_vector) {
BX_INFO("This is S3 resume but wakeup vector is NULL\n");
} else {
BX_INFO("S3 resume vector %p\n", *s3_resume_vector);
- pci_for_each_device(reinit_piix4_pm);
+ pci_for_each_device(reinit_pm);
}
return;
}
--
1.6.0.2
- [Qemu-devel] [PATCH 00/14] pcbios: support q35 chipset, Isaku Yamahata, 2009/09/30
- [Qemu-devel] [PATCH 13/14] pcbios: change acpi dsdt for q35 chipset., Isaku Yamahata, 2009/09/30
- [Qemu-devel] [PATCH 09/14] pcbios: comment out PCI_FIXED_HOST_BRIDGE for gmch host pci bridge to undef., Isaku Yamahata, 2009/09/30
- [Qemu-devel] [PATCH 06/14] pcbios: rombios32: make pci space assigner preferchable memory aware., Isaku Yamahata, 2009/09/30
- [Qemu-devel] [PATCH 02/14] pcbios: fix makesym.perl, Isaku Yamahata, 2009/09/30
- [Qemu-devel] [PATCH 14/14] pcibos/acpi dsdt: APIC mode support for q35 chipset, Isaku Yamahata, 2009/09/30
- [Qemu-devel] [PATCH 08/14] pcibos: initialize q35 chipset.,
Isaku Yamahata <=
- [Qemu-devel] [PATCH 01/14] pcbios: add generated files to dot gitignore., Isaku Yamahata, 2009/09/30
- [Qemu-devel] [PATCH 03/14] pcbios: remove iasl output file when error., Isaku Yamahata, 2009/09/30
- [Qemu-devel] [PATCH 05/14] pcbios: rombios32: make pci memory space assignment 64bit aware., Isaku Yamahata, 2009/09/30
- [Qemu-devel] [PATCH 12/14] pcbios: make pci bar initialization to be aware of preferchable memory., Isaku Yamahata, 2009/09/30
- [Qemu-devel] [PATCH 07/14] pcbios: enable debug output for debug., Isaku Yamahata, 2009/09/30
- [Qemu-devel] [PATCH 10/14] pcibos: add mcfg entry to ACPI table for q35 pcie., Isaku Yamahata, 2009/09/30
- [Qemu-devel] [PATCH 04/14] pcbios: make set_e820_range() full 64bit aware., Isaku Yamahata, 2009/09/30
- [Qemu-devel] [PATCH 11/14] pcbios: reserve mcfg area by e820 for linux to use mcfg., Isaku Yamahata, 2009/09/30
- [Qemu-devel] Re: [PATCH 00/14] pcbios: support q35 chipset, Anthony Liguori, 2009/09/30