coreboot-kgpe-d16/src/southbridge/intel/i82801dx/lpc.c
Patrick Georgi a73b93157f tree: drop last paragraph of GPL copyright header
It encourages users from writing to the FSF without giving an address.
Linux also prefers to drop that and their checkpatch.pl (that we
imported) looks out for that.

This is the result of util/scripts/no-fsf-addresses.sh with no further
editing.

Change-Id: Ie96faea295fe001911d77dbc51e9a6789558fbd6
Signed-off-by: Patrick Georgi <pgeorgi@chromium.org>
Reviewed-on: http://review.coreboot.org/11888
Tested-by: build bot (Jenkins)
Reviewed-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
Reviewed-by: Ronald G. Minnich <rminnich@gmail.com>
2015-10-31 21:37:39 +01:00

351 lines
9.4 KiB
C

/*
* This file is part of the coreboot project.
*
* Copyright (C) 2003 Linux Networx
* Copyright (C) 2004 SuSE Linux AG
* Copyright (C) 2004 Tyan Computer
* Copyright (C) 2010 Joseph Smith <joe@settoplinux.org>
*
* 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.
*/
#include <console/console.h>
#include <device/device.h>
#include <device/pci.h>
#include <device/pci_ids.h>
#include <device/pci_ops.h>
#include <pc80/mc146818rtc.h>
#include <pc80/isa-dma.h>
#include <arch/io.h>
#include <arch/ioapic.h>
#include "i82801dx.h"
#define NMI_OFF 0
typedef struct southbridge_intel_i82801dx_config config_t;
/**
* Enable ACPI I/O range.
*
* @param dev PCI device with ACPI and PM BAR's
*/
static void i82801dx_enable_acpi(struct device *dev)
{
/* Set ACPI base address (I/O space). */
pci_write_config32(dev, PMBASE, (PMBASE_ADDR | 1));
/* Enable ACPI I/O range decode and ACPI power management. */
pci_write_config8(dev, ACPI_CNTL, ACPI_EN);
}
/**
* Set miscellanous static southbridge features.
*
* @param dev PCI device with I/O APIC control registers
*/
static void i82801dx_enable_ioapic(struct device *dev)
{
u32 reg32;
reg32 = pci_read_config32(dev, GEN_CNTL);
reg32 |= (1 << 13); /* Coprocessor error enable (COPR_ERR_EN) */
reg32 |= (3 << 7); /* IOAPIC enable (APIC_EN) */
reg32 |= (1 << 2); /* DMA collection buffer enable (DCB_EN) */
reg32 |= (1 << 1); /* Delayed transaction enable (DTE) */
pci_write_config32(dev, GEN_CNTL, reg32);
printk(BIOS_DEBUG, "IOAPIC Southbridge enabled %x\n", reg32);
set_ioapic_id(VIO_APIC_VADDR, 0x02);
/*
* Select Boot Configuration register (0x03) and
* use Processor System Bus (0x01) to deliver interrupts.
*/
io_apic_write(VIO_APIC_VADDR, 0x03, 0x01);
}
static void i82801dx_enable_serial_irqs(struct device *dev)
{
/* Set packet length and toggle silent mode bit. */
pci_write_config8(dev, SERIRQ_CNTL,
(1 << 7) | (1 << 6) | ((21 - 17) << 2) | (0 << 0));
pci_write_config8(dev, SERIRQ_CNTL,
(1 << 7) | (0 << 6) | ((21 - 17) << 2) | (0 << 0));
}
static void i82801dx_pirq_init(device_t dev)
{
/* Get the chip configuration */
config_t *config = dev->chip_info;
pci_write_config8(dev, PIRQA_ROUT, config->pirqa_routing);
pci_write_config8(dev, PIRQB_ROUT, config->pirqb_routing);
pci_write_config8(dev, PIRQC_ROUT, config->pirqc_routing);
pci_write_config8(dev, PIRQD_ROUT, config->pirqd_routing);
pci_write_config8(dev, PIRQE_ROUT, config->pirqe_routing);
pci_write_config8(dev, PIRQF_ROUT, config->pirqf_routing);
pci_write_config8(dev, PIRQG_ROUT, config->pirqg_routing);
pci_write_config8(dev, PIRQH_ROUT, config->pirqh_routing);
}
static void i82801dx_power_options(device_t dev)
{
u8 reg8;
u16 reg16, pmbase;
u32 reg32;
const char *state;
int pwr_on = CONFIG_MAINBOARD_POWER_ON_AFTER_POWER_FAIL;
int nmi_option;
/* Which state do we want to goto after g3 (power restored)?
* 0 == S0 Full On
* 1 == S5 Soft Off
*
* If the option is not existent (Laptops), use MAINBOARD_POWER_ON.
*/
pwr_on = MAINBOARD_POWER_ON;
get_option(&pwr_on, "power_on_after_fail");
reg8 = pci_read_config8(dev, GEN_PMCON_3);
reg8 &= 0xfe;
switch (pwr_on) {
case MAINBOARD_POWER_OFF:
reg8 |= 1;
state = "off";
break;
case MAINBOARD_POWER_ON:
reg8 &= ~1;
state = "on";
break;
case MAINBOARD_POWER_KEEP:
reg8 &= ~1;
state = "state keep";
break;
default:
state = "undefined";
}
reg8 &= ~(1 << 3); /* minimum asssertion is 1 to 2 RTCCLK */
pci_write_config8(dev, GEN_PMCON_3, reg8);
printk(BIOS_INFO, "Set power %s after power failure.\n", state);
/* Set up NMI on errors. */
reg8 = inb(0x61);
reg8 &= 0x0f; /* Higher Nibble must be 0 */
reg8 &= ~(1 << 3); /* IOCHK# NMI Enable */
// reg8 &= ~(1 << 2); /* PCI SERR# Enable */
reg8 |= (1 << 2); /* PCI SERR# Disable for now */
outb(reg8, 0x61);
reg8 = inb(0x70);
nmi_option = NMI_OFF;
get_option(&nmi_option, "nmi");
if (nmi_option) {
printk(BIOS_INFO, "NMI sources enabled.\n");
reg8 &= ~(1 << 7); /* Set NMI. */
} else {
printk(BIOS_INFO, "NMI sources disabled.\n");
reg8 |= ( 1 << 7); /* Disable NMI. */
}
outb(reg8, 0x70);
/* Set SMI# rate down and enable CPU_SLP# */
reg16 = pci_read_config16(dev, GEN_PMCON_1);
reg16 &= ~(3 << 0); // SMI# rate 1 minute
reg16 |= (1 << 5); // CPUSLP_EN Desktop only
pci_write_config16(dev, GEN_PMCON_1, reg16);
pmbase = pci_read_config16(dev, 0x40) & 0xfffe;
/* Set up power management block and determine sleep mode */
reg32 = inl(pmbase + 0x04); // PM1_CNT
reg32 &= ~(7 << 10); // SLP_TYP
reg32 |= (1 << 0); // SCI_EN
outl(reg32, pmbase + 0x04);
}
static void gpio_init(device_t dev)
{
/* This should be done in romstage.c already */
pci_write_config32(dev, GPIO_BASE, (GPIOBASE_ADDR | 1));
pci_write_config8(dev, GPIO_CNTL, 0x10);
}
static void i82801dx_rtc_init(struct device *dev)
{
u8 reg8;
u32 reg32;
int rtc_failed;
reg8 = pci_read_config8(dev, GEN_PMCON_3);
rtc_failed = reg8 & RTC_BATTERY_DEAD;
if (rtc_failed) {
reg8 &= ~(1 << 1); /* Preserve the power fail state. */
pci_write_config8(dev, GEN_PMCON_3, reg8);
}
reg32 = pci_read_config32(dev, GEN_STS);
rtc_failed |= reg32 & (1 << 2);
cmos_init(rtc_failed);
/* Enable access to the upper 128 byte bank of CMOS RAM. */
pci_write_config8(dev, RTC_CONF, 0x04);
}
static void i82801dx_lpc_route_dma(struct device *dev, u8 mask)
{
u16 reg16;
int i;
reg16 = pci_read_config16(dev, PCI_DMA_CFG);
reg16 &= 0x300;
for (i = 0; i < 8; i++) {
if (i == 4)
continue;
reg16 |= ((mask & (1 << i)) ? 3 : 1) << (i * 2);
}
pci_write_config16(dev, PCI_DMA_CFG, reg16);
}
static void i82801dx_lpc_decode_en(device_t dev)
{
/* Decode 0x3F8-0x3FF (COM1) for COMA port, 0x2F8-0x2FF (COM2) for COMB.
* LPT decode defaults to 0x378-0x37F and 0x778-0x77F.
* Floppy decode defaults to 0x3F0-0x3F5, 0x3F7.
* We also need to set the value for LPC I/F Enables Register.
*/
pci_write_config8(dev, COM_DEC, 0x10);
pci_write_config16(dev, LPC_EN, 0x300F);
}
/* ICH4 does not mention HPET in the docs, but
* all ICH3 and ICH4 do have HPETs built in.
*/
static void enable_hpet(struct device *dev)
{
u32 reg32, hpet, val;
/* Set HPET base address and enable it */
printk(BIOS_DEBUG, "Enabling HPET at 0x%x\n", CONFIG_HPET_ADDRESS);
reg32 = pci_read_config32(dev, GEN_CNTL);
/*
* Bit 17 is HPET enable bit.
* Bit 16:15 control the HPET base address.
*/
reg32 &= ~(3 << 15); /* Clear it */
hpet = CONFIG_HPET_ADDRESS >> 12;
hpet &= 0x3;
reg32 |= (hpet << 15);
reg32 |= (1 << 17); /* Enable HPET. */
pci_write_config32(dev, GEN_CNTL, reg32);
/* Check to see whether it took */
reg32 = pci_read_config32(dev, GEN_CNTL);
val = reg32 >> 15;
val &= 0x7;
if ((val & 0x4) && (hpet == (val & 0x3))) {
printk(BIOS_INFO, "HPET enabled at 0x%x\n", CONFIG_HPET_ADDRESS);
} else {
printk(BIOS_WARNING, "HPET was not enabled correctly\n");
reg32 &= ~(1 << 17); /* Clear Enable */
pci_write_config32(dev, GEN_CNTL, reg32);
}
}
static void lpc_init(struct device *dev)
{
/* Set the value for PCI command register. */
pci_write_config16(dev, PCI_COMMAND, 0x000f);
i82801dx_enable_acpi(dev);
/* IO APIC initialization. */
i82801dx_enable_ioapic(dev);
i82801dx_enable_serial_irqs(dev);
/* Setup the PIRQ. */
i82801dx_pirq_init(dev);
/* Setup power options. */
i82801dx_power_options(dev);
/* Set the state of the GPIO lines. */
gpio_init(dev);
/* Initialize the real time clock. */
i82801dx_rtc_init(dev);
/* Route DMA. */
i82801dx_lpc_route_dma(dev, 0xff);
/* Initialize ISA DMA. */
isa_dma_init();
/* Setup decode ports and LPC I/F enables. */
i82801dx_lpc_decode_en(dev);
/* Initialize the High Precision Event Timers */
enable_hpet(dev);
}
static void i82801dx_lpc_read_resources(device_t dev)
{
struct resource *res;
/* Get the normal PCI resources of this device. */
pci_dev_read_resources(dev);
/* Add an extra subtractive resource for both memory and I/O. */
res = new_resource(dev, IOINDEX_SUBTRACTIVE(0, 0));
res->base = 0;
res->size = 0x1000;
res->flags = IORESOURCE_IO | IORESOURCE_SUBTRACTIVE |
IORESOURCE_ASSIGNED | IORESOURCE_FIXED;
res = new_resource(dev, IOINDEX_SUBTRACTIVE(1, 0));
res->base = 0xff800000;
res->size = 0x00800000; /* 8 MB for flash */
res->flags = IORESOURCE_MEM | IORESOURCE_SUBTRACTIVE |
IORESOURCE_ASSIGNED | IORESOURCE_FIXED;
res = new_resource(dev, 3); /* IOAPIC */
res->base = IO_APIC_ADDR;
res->size = 0x00001000;
res->flags = IORESOURCE_MEM | IORESOURCE_ASSIGNED | IORESOURCE_FIXED;
}
static struct device_operations lpc_ops = {
.read_resources = i82801dx_lpc_read_resources,
.set_resources = pci_dev_set_resources,
.enable_resources = pci_dev_enable_resources,
.init = lpc_init,
.scan_bus = scan_lpc_bus,
.enable = i82801dx_enable,
};
/* 82801DB/DBL */
static const struct pci_driver lpc_driver_db __pci_driver = {
.ops = &lpc_ops,
.vendor = PCI_VENDOR_ID_INTEL,
.device = PCI_DEVICE_ID_INTEL_82801DB_LPC,
};
/* 82801DBM */
static const struct pci_driver lpc_driver_dbm __pci_driver = {
.ops = &lpc_ops,
.vendor = PCI_VENDOR_ID_INTEL,
.device = PCI_DEVICE_ID_INTEL_82801DBM_LPC,
};