| /* |
| * This file is part of the coreboot project. |
| * |
| * Copyright (C) 2007-2009 coresystems GmbH |
| * Copyright (C) 2012 The Chromium OS Authors |
| * Copyright (C) 2013 Vladimir Serbinenko |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License as |
| * published by the Free Software Foundation; version 2 of |
| * the License. |
| * |
| * This program 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 General Public License for more details. |
| */ |
| |
| #define __SIMPLE_DEVICE__ |
| |
| #include <types.h> |
| #include <string.h> |
| #include <console/console.h> |
| #include <arch/io.h> |
| #include <arch/acpi.h> |
| #include <device/device.h> |
| #include <device/pci.h> |
| #include <device/pci_ids.h> |
| #include <drivers/intel/gma/intel_bios.h> |
| #include <arch/acpigen.h> |
| #include <cpu/cpu.h> |
| #include <drivers/intel/gma/i915.h> |
| #include <cbmem.h> |
| #include "nehalem.h" |
| |
| unsigned long acpi_fill_mcfg(unsigned long current) |
| { |
| u32 pciexbar = 0; |
| u32 pciexbar_reg; |
| int max_buses; |
| |
| pciexbar_reg = pci_read_config32(PCI_DEV(QUICKPATH_BUS, 0, 1), 0x50); |
| |
| // MMCFG not supported or not enabled. |
| if (!(pciexbar_reg & (1 << 0))) |
| return current; |
| |
| switch ((pciexbar_reg >> 1) & 3) { |
| case 0: // 256MB |
| pciexbar = |
| pciexbar_reg & ((1 << 31) | (1 << 30) | (1 << 29) | |
| (1 << 28)); |
| max_buses = 256; |
| break; |
| case 1: // 128M |
| pciexbar = |
| pciexbar_reg & ((1 << 31) | (1 << 30) | (1 << 29) | |
| (1 << 28) | (1 << 27)); |
| max_buses = 128; |
| break; |
| case 2: // 64M |
| pciexbar = |
| pciexbar_reg & ((1 << 31) | (1 << 30) | (1 << 29) | |
| (1 << 28) | (1 << 27) | (1 << 26)); |
| max_buses = 64; |
| break; |
| default: // RSVD |
| return current; |
| } |
| |
| if (!pciexbar) |
| return current; |
| |
| current += acpi_create_mcfg_mmconfig((acpi_mcfg_mmconfig_t *) current, |
| pciexbar, 0x0, 0x0, max_buses - 1); |
| |
| return current; |
| } |
| |
| static void *get_intel_vbios(void) |
| { |
| /* This should probably be looking at CBFS or we should always |
| * deploy the VBIOS on Intel systems, even if we don't run it |
| * in coreboot (e.g. SeaBIOS only scenarios). |
| */ |
| u8 *vbios = (u8 *) 0xc0000; |
| |
| optionrom_header_t *oprom = (optionrom_header_t *) vbios; |
| optionrom_pcir_t *pcir = (optionrom_pcir_t *) (vbios + |
| oprom->pcir_offset); |
| |
| printk(BIOS_DEBUG, "GET_VBIOS: %x %x %x %x %x\n", |
| oprom->signature, pcir->vendor, pcir->classcode[0], |
| pcir->classcode[1], pcir->classcode[2]); |
| |
| if ((oprom->signature == OPROM_SIGNATURE) && |
| (pcir->vendor == PCI_VENDOR_ID_INTEL) && |
| (pcir->classcode[0] == 0x00) && |
| (pcir->classcode[1] == 0x00) && (pcir->classcode[2] == 0x03)) |
| return (void *)vbios; |
| |
| return NULL; |
| } |
| |
| static int init_opregion_vbt(igd_opregion_t * opregion) |
| { |
| void *vbios; |
| vbios = get_intel_vbios(); |
| if (!vbios) { |
| printk(BIOS_DEBUG, "VBIOS not found.\n"); |
| return 1; |
| } |
| |
| printk(BIOS_DEBUG, " ... VBIOS found at %p\n", vbios); |
| optionrom_header_t *oprom = (optionrom_header_t *) vbios; |
| optionrom_vbt_t *vbt = (optionrom_vbt_t *) (vbios + oprom->vbt_offset); |
| |
| if (read32(vbt->hdr_signature) != VBT_SIGNATURE) { |
| printk(BIOS_DEBUG, "VBT not found!\n"); |
| return 1; |
| } |
| |
| memcpy(opregion->header.vbios_version, vbt->coreblock_biosbuild, 4); |
| memcpy(opregion->vbt.gvd1, vbt, vbt->hdr_vbt_size < 7168 ? |
| vbt->hdr_vbt_size : 7168); |
| |
| return 0; |
| } |
| |
| /* Initialize IGD OpRegion, called from ACPI code */ |
| int init_igd_opregion(igd_opregion_t * opregion) |
| { |
| device_t igd; |
| u16 reg16; |
| |
| memset((void *)opregion, 0, sizeof(igd_opregion_t)); |
| |
| // FIXME if IGD is disabled, we should exit here. |
| |
| memcpy(&opregion->header.signature, IGD_OPREGION_SIGNATURE, |
| sizeof(opregion->header.signature)); |
| |
| /* 8kb */ |
| opregion->header.size = sizeof(igd_opregion_t) / 1024; |
| opregion->header.version = IGD_OPREGION_VERSION; |
| |
| // FIXME We just assume we're mobile for now |
| opregion->header.mailboxes = MAILBOXES_MOBILE; |
| |
| // TODO Initialize Mailbox 1 |
| |
| // TODO Initialize Mailbox 3 |
| opregion->mailbox3.bclp = IGD_BACKLIGHT_BRIGHTNESS; |
| opregion->mailbox3.pfit = IGD_FIELD_VALID | IGD_PFIT_STRETCH; |
| opregion->mailbox3.pcft = 0; // should be (IMON << 1) & 0x3e |
| opregion->mailbox3.cblv = IGD_FIELD_VALID | IGD_INITIAL_BRIGHTNESS; |
| opregion->mailbox3.bclm[0] = IGD_WORD_FIELD_VALID + 0x0000; |
| opregion->mailbox3.bclm[1] = IGD_WORD_FIELD_VALID + 0x0a19; |
| opregion->mailbox3.bclm[2] = IGD_WORD_FIELD_VALID + 0x1433; |
| opregion->mailbox3.bclm[3] = IGD_WORD_FIELD_VALID + 0x1e4c; |
| opregion->mailbox3.bclm[4] = IGD_WORD_FIELD_VALID + 0x2866; |
| opregion->mailbox3.bclm[5] = IGD_WORD_FIELD_VALID + 0x327f; |
| opregion->mailbox3.bclm[6] = IGD_WORD_FIELD_VALID + 0x3c99; |
| opregion->mailbox3.bclm[7] = IGD_WORD_FIELD_VALID + 0x46b2; |
| opregion->mailbox3.bclm[8] = IGD_WORD_FIELD_VALID + 0x50cc; |
| opregion->mailbox3.bclm[9] = IGD_WORD_FIELD_VALID + 0x5ae5; |
| opregion->mailbox3.bclm[10] = IGD_WORD_FIELD_VALID + 0x64ff; |
| |
| init_opregion_vbt(opregion); |
| |
| /* TODO This needs to happen in S3 resume, too. |
| * Maybe it should move to the finalize handler |
| */ |
| igd = PCI_DEV(0, 0x2, 0); |
| |
| pci_write_config32(igd, ASLS, (u32) opregion); |
| reg16 = pci_read_config16(igd, SWSCI); |
| reg16 &= ~(1 << 0); |
| reg16 |= (1 << 15); |
| pci_write_config16(igd, SWSCI, reg16); |
| |
| /* clear dmisci status */ |
| reg16 = inw(DEFAULT_PMBASE + TCO1_STS); |
| reg16 |= DMISCI_STS; // reference code does an &= |
| outw(DEFAULT_PMBASE + TCO1_STS, reg16); |
| |
| /* clear acpi tco status */ |
| outl(DEFAULT_PMBASE + GPE0_STS, TCOSCI_STS); |
| |
| /* enable acpi tco scis */ |
| reg16 = inw(DEFAULT_PMBASE + GPE0_EN); |
| reg16 |= TCOSCI_EN; |
| outw(DEFAULT_PMBASE + GPE0_EN, reg16); |
| |
| return 0; |
| } |
| |
| void *igd_make_opregion(void) |
| { |
| igd_opregion_t *opregion; |
| |
| printk(BIOS_DEBUG, "ACPI: * IGD OpRegion\n"); |
| opregion = cbmem_add(CBMEM_ID_IGD_OPREGION, sizeof (*opregion)); |
| if (opregion) |
| init_igd_opregion(opregion); |
| return opregion; |
| } |