Support for the Intel ICH7 southbridge.
This includes an early SMI handler. Signed-off-by: Stefan Reinauer <stepan@coresystems.de> Acked-by: Ronald G. Minnich <rminnich@gmail.com> git-svn-id: svn://svn.coreboot.org/coreboot/trunk@3701 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1
This commit is contained in:
parent
b70d1993a4
commit
debb11fc1f
|
@ -856,6 +856,11 @@ define HAVE_HARD_RESET
|
||||||
export used
|
export used
|
||||||
comment "Have hard reset"
|
comment "Have hard reset"
|
||||||
end
|
end
|
||||||
|
define HAVE_SMI_HANDLER
|
||||||
|
default none
|
||||||
|
export used
|
||||||
|
comment "Set, if the board needs an SMI handler"
|
||||||
|
end
|
||||||
define MEMORY_HOLE
|
define MEMORY_HOLE
|
||||||
default none
|
default none
|
||||||
export used
|
export used
|
||||||
|
|
|
@ -0,0 +1,253 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the coreboot project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008 coresystems GmbH
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
typedef uint64_t u64;
|
||||||
|
|
||||||
|
/* AMD64 SMM State-Save Area
|
||||||
|
* starts @ 0x7e00
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
u16 es_selector;
|
||||||
|
u16 es_attributes;
|
||||||
|
u32 es_limit;
|
||||||
|
u64 es_base;
|
||||||
|
|
||||||
|
u16 cs_selector;
|
||||||
|
u16 cs_attributcs;
|
||||||
|
u32 cs_limit;
|
||||||
|
u64 cs_base;
|
||||||
|
|
||||||
|
u16 ss_selector;
|
||||||
|
u16 ss_attributss;
|
||||||
|
u32 ss_limit;
|
||||||
|
u64 ss_base;
|
||||||
|
|
||||||
|
u16 ds_selector;
|
||||||
|
u16 ds_attributds;
|
||||||
|
u32 ds_limit;
|
||||||
|
u64 ds_base;
|
||||||
|
|
||||||
|
u16 fs_selector;
|
||||||
|
u16 fs_attributfs;
|
||||||
|
u32 fs_limit;
|
||||||
|
u64 fs_base;
|
||||||
|
|
||||||
|
u16 gs_selector;
|
||||||
|
u16 gs_attributgs;
|
||||||
|
u32 gs_limit;
|
||||||
|
u64 gs_base;
|
||||||
|
|
||||||
|
u8 reserved0[4];
|
||||||
|
u16 gdtr_limit;
|
||||||
|
u8 reserved1[2];
|
||||||
|
u64 gdtr_base;
|
||||||
|
|
||||||
|
u16 ldtr_selector;
|
||||||
|
u16 ldtr_attributes;
|
||||||
|
u32 ldtr_limit;
|
||||||
|
u64 ldtr_base;
|
||||||
|
|
||||||
|
u8 reserved2[4];
|
||||||
|
u16 idtr_limit;
|
||||||
|
u8 reserved3[2];
|
||||||
|
u64 idtr_base;
|
||||||
|
|
||||||
|
u16 tr_selector;
|
||||||
|
u16 tr_attributes;
|
||||||
|
u32 tr_limit;
|
||||||
|
u64 tr_base;
|
||||||
|
|
||||||
|
u8 reserved4[40];
|
||||||
|
|
||||||
|
u8 io_restart;
|
||||||
|
u8 autohalt_restart;
|
||||||
|
|
||||||
|
u8 reserved5[6];
|
||||||
|
|
||||||
|
u64 efer;
|
||||||
|
|
||||||
|
u8 reserved6[36];
|
||||||
|
|
||||||
|
u32 smm_revision;
|
||||||
|
u32 smbase;
|
||||||
|
|
||||||
|
u8 reserved7[68];
|
||||||
|
|
||||||
|
u64 cr4;
|
||||||
|
u64 cr3;
|
||||||
|
u64 cr0;
|
||||||
|
u64 dr7;
|
||||||
|
u64 dr6;
|
||||||
|
|
||||||
|
u64 rflags;
|
||||||
|
u64 rip;
|
||||||
|
u64 r15;
|
||||||
|
u64 r14;
|
||||||
|
u64 r13;
|
||||||
|
u64 r12;
|
||||||
|
u64 r11;
|
||||||
|
u64 r10;
|
||||||
|
u64 r9;
|
||||||
|
u64 r8;
|
||||||
|
|
||||||
|
u64 rdi;
|
||||||
|
u64 rsi;
|
||||||
|
u64 rpb;
|
||||||
|
u64 rsp;
|
||||||
|
u64 rbx;
|
||||||
|
u64 rdx;
|
||||||
|
u64 rcx;
|
||||||
|
u64 rax;
|
||||||
|
} __attribute__((packed)) amd64_smm_state_save_area_t;
|
||||||
|
|
||||||
|
|
||||||
|
/* Intel Core 2 (EM64T) SMM State-Save Area
|
||||||
|
* starts @ 0x7d00
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
u8 reserved0[208];
|
||||||
|
|
||||||
|
u32 gdtr_upper_base;
|
||||||
|
u32 ldtr_upper_base;
|
||||||
|
u32 idtr_upper_base;
|
||||||
|
|
||||||
|
u8 reserved1[4];
|
||||||
|
|
||||||
|
u64 io_rdi;
|
||||||
|
u64 io_rip;
|
||||||
|
u64 io_rcx;
|
||||||
|
u64 io_rsi;
|
||||||
|
u64 cr4;
|
||||||
|
|
||||||
|
u8 reserved2[68];
|
||||||
|
|
||||||
|
u64 gdtr_base;
|
||||||
|
u64 idtr_base;
|
||||||
|
u64 ldtr_base;
|
||||||
|
|
||||||
|
u8 reserved3[84];
|
||||||
|
|
||||||
|
u32 smm_revision;
|
||||||
|
u32 smbase;
|
||||||
|
|
||||||
|
u16 io_restart;
|
||||||
|
u16 autohalt_restart;
|
||||||
|
|
||||||
|
u8 reserved4[24];
|
||||||
|
|
||||||
|
u64 r15;
|
||||||
|
u64 r14;
|
||||||
|
u64 r13;
|
||||||
|
u64 r12;
|
||||||
|
u64 r11;
|
||||||
|
u64 r10;
|
||||||
|
u64 r9;
|
||||||
|
u64 r8;
|
||||||
|
|
||||||
|
u64 rax;
|
||||||
|
u64 rcx;
|
||||||
|
u64 rdx;
|
||||||
|
u64 rbx;
|
||||||
|
|
||||||
|
u64 rsp;
|
||||||
|
u64 rbp;
|
||||||
|
u64 rsi;
|
||||||
|
u64 rdi;
|
||||||
|
|
||||||
|
|
||||||
|
u64 io_mem_addr;
|
||||||
|
u32 io_misc_info;
|
||||||
|
|
||||||
|
u32 es_sel;
|
||||||
|
u32 cs_sel;
|
||||||
|
u32 ss_sel;
|
||||||
|
u32 ds_sel;
|
||||||
|
u32 fs_sel;
|
||||||
|
u32 gs_sel;
|
||||||
|
|
||||||
|
u32 ldtr_sel;
|
||||||
|
u32 tr_sel;
|
||||||
|
|
||||||
|
u64 dr7;
|
||||||
|
u64 dr6;
|
||||||
|
u64 rip;
|
||||||
|
u64 efer;
|
||||||
|
u64 rflags;
|
||||||
|
|
||||||
|
u64 cr3;
|
||||||
|
u64 cr0;
|
||||||
|
} __attribute__((packed)) em64t_smm_state_save_area_t;
|
||||||
|
|
||||||
|
|
||||||
|
/* Legacy x86 SMM State-Save Area
|
||||||
|
* starts @ 0x7e00
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
u8 reserved0[248];
|
||||||
|
u32 smbase;
|
||||||
|
u32 smm_revision;
|
||||||
|
u16 io_restart;
|
||||||
|
u16 autohalt_restart;
|
||||||
|
u8 reserved1[132];
|
||||||
|
u32 gdtbase;
|
||||||
|
u8 reserved2[8];
|
||||||
|
u32 idtbase;
|
||||||
|
u8 reserved3[16];
|
||||||
|
u32 es;
|
||||||
|
u32 cs;
|
||||||
|
u32 ss;
|
||||||
|
u32 ds;
|
||||||
|
u32 fs;
|
||||||
|
u32 gs;
|
||||||
|
u32 ldtbase;
|
||||||
|
u32 tr;
|
||||||
|
u32 dr7;
|
||||||
|
u32 dr6;
|
||||||
|
u32 eax;
|
||||||
|
u32 ecx;
|
||||||
|
u32 edx;
|
||||||
|
u32 ebx;
|
||||||
|
u32 esp;
|
||||||
|
u32 ebp;
|
||||||
|
u32 esi;
|
||||||
|
u32 edi;
|
||||||
|
u32 eip;
|
||||||
|
u32 eflags;
|
||||||
|
u32 cr3;
|
||||||
|
u32 cr0;
|
||||||
|
} __attribute__((packed)) legacy_smm_state_save_area_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
AMD64,
|
||||||
|
EM64T,
|
||||||
|
LEGACY
|
||||||
|
} save_state_type_t;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
save_state_type_t type;
|
||||||
|
union {
|
||||||
|
amd64_smm_state_save_area_t *amd64_state_save;
|
||||||
|
em64t_smm_state_save_area_t *em64t_state_save;
|
||||||
|
legacy_smm_state_save_area_t *legacy_state_save;
|
||||||
|
};
|
||||||
|
} smm_state_save_area_t;
|
||||||
|
|
|
@ -71,7 +71,7 @@ struct device {
|
||||||
struct device_path path;
|
struct device_path path;
|
||||||
unsigned vendor;
|
unsigned vendor;
|
||||||
unsigned device;
|
unsigned device;
|
||||||
unsigned int class; /* 3 bytes: (base,sub,prog-if) */
|
unsigned int class; /* 3 bytes: (base, sub, prog-if) */
|
||||||
unsigned int hdr_type; /* PCI header type */
|
unsigned int hdr_type; /* PCI header type */
|
||||||
unsigned int enabled : 1; /* set if we should enable the device */
|
unsigned int enabled : 1; /* set if we should enable the device */
|
||||||
unsigned int initialized : 1; /* set if we have initialized the device */
|
unsigned int initialized : 1; /* set if we have initialized the device */
|
||||||
|
@ -85,7 +85,7 @@ struct device {
|
||||||
struct resource resource[MAX_RESOURCES];
|
struct resource resource[MAX_RESOURCES];
|
||||||
unsigned int resources;
|
unsigned int resources;
|
||||||
|
|
||||||
/* link are (down sream) buses attached to the device, usually a leaf
|
/* link are (down stream) buses attached to the device, usually a leaf
|
||||||
* device with no children have 0 buses attached and a bridge has 1 bus
|
* device with no children have 0 buses attached and a bridge has 1 bus
|
||||||
*/
|
*/
|
||||||
struct bus link[MAX_LINKS];
|
struct bus link[MAX_LINKS];
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
##
|
||||||
|
## This file is part of the coreboot project.
|
||||||
|
##
|
||||||
|
## Copyright (C) 2008 coresystems GmbH
|
||||||
|
##
|
||||||
|
## 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; either version 2 of the License, or
|
||||||
|
## (at your option) any later version.
|
||||||
|
##
|
||||||
|
## 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.
|
||||||
|
##
|
||||||
|
## You should have received a copy of the GNU General Public License
|
||||||
|
## along with this program; if not, write to the Free Software
|
||||||
|
## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
##
|
||||||
|
|
||||||
|
config chip.h
|
||||||
|
driver i82801gx.o
|
||||||
|
driver i82801gx_ac97.o
|
||||||
|
driver i82801gx_ide.o
|
||||||
|
driver i82801gx_lpc.o
|
||||||
|
driver i82801gx_nic.o
|
||||||
|
driver i82801gx_pci.o
|
||||||
|
driver i82801gx_pcie.o
|
||||||
|
driver i82801gx_sata.o
|
||||||
|
driver i82801gx_usb.o
|
||||||
|
driver i82801gx_usb_ehci.o
|
||||||
|
|
||||||
|
object i82801gx_reset.o
|
||||||
|
object i82801gx_watchdog.o
|
||||||
|
|
||||||
|
object i82801gx_smi.o
|
||||||
|
object smmrelocate.S
|
||||||
|
|
||||||
|
makerule smmhandler.o
|
||||||
|
depends "$(TOP)/src/southbridge/intel/i82801gx/smmhandler.S"
|
||||||
|
action "@$(CC) -c $(CPU_OPT) $(CPPFLAGS) $(CFLAGS) -o $@ $<"
|
||||||
|
end
|
||||||
|
|
||||||
|
makerule smihandler.o
|
||||||
|
depends "$(TOP)/src/southbridge/intel/i82801gx/smihandler.c"
|
||||||
|
action "@$(CC) -c $(CPU_OPT) $(CPPFLAGS) $(CFLAGS) -o $@ $<"
|
||||||
|
end
|
||||||
|
|
||||||
|
makerule smm.o
|
||||||
|
depends "smmhandler.o smihandler.o printk.o vtxprintf.o $(LIBGCC_FILE_NAME)"
|
||||||
|
action "$(CC) $(DISTRO_LFLAGS) -nostdlib -r -o $@ smmhandler.o smihandler.o printk.o vtxprintf.o $(LIBGCC_FILE_NAME)"
|
||||||
|
end
|
||||||
|
|
||||||
|
makerule smm
|
||||||
|
depends "smm.o $(TOP)/src/southbridge/intel/i82801gx/smm.ld ldoptions"
|
||||||
|
action "$(CC) $(DISTRO_LFLAGS) -nostdlib -nostartfiles -static -o smm.elf -T $(TOP)/src/southbridge/intel/i82801gx/smm.ld smm.o"
|
||||||
|
action "$(CROSS_COMPILE)nm -n smm.elf | sort > smm.map"
|
||||||
|
action "$(OBJCOPY) -O binary smm.elf smm"
|
||||||
|
end
|
||||||
|
|
||||||
|
makerule smm_bin.c
|
||||||
|
depends "smm"
|
||||||
|
action "(echo 'unsigned char smm[] = {'; od -vtx1 smm | sed -e 's,^[0-9]* *,,' -e 's:[0-9a-f][0-9a-f] :0x&,:g' -e 's:[0-9a-f][0-9a-f]$$:0x&,:'; echo '}; unsigned int smm_len = '; wc -c smm |awk '{print $$1;}' ; echo ';') > smm_bin.c"
|
||||||
|
end
|
||||||
|
|
||||||
|
object ./smm_bin.o
|
|
@ -0,0 +1,35 @@
|
||||||
|
Non-automatic IO-Addresses
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
The following dynamic IO BARs are used on the ICH7 for the Kontron Default BIOS:
|
||||||
|
|
||||||
|
GPIOBASE 0x480 (64 bytes)
|
||||||
|
PMBASE 0x800 (128 bytes)
|
||||||
|
SMBASE 0x400 (32 bytes)
|
||||||
|
HWMON 0xa00 (??)
|
||||||
|
|
||||||
|
The following dynamic IO BARs are used on the ICH7 for the Getac Default BIOS:
|
||||||
|
|
||||||
|
GPIOBASE 0x1180 (64 bytes)
|
||||||
|
PMBASE 0x1000 (128 bytes)
|
||||||
|
SMBASE 0x18e0 (32 bytes)
|
||||||
|
|
||||||
|
The Getac also needs an IO Trapped area of 0x0C bytes (defaults to 0x800)
|
||||||
|
|
||||||
|
coreboot:
|
||||||
|
GPIOBASE 0x480 (64 bytes)
|
||||||
|
PMBASE 0x500 (128 bytes)
|
||||||
|
SMBASE 0x400 (32 bytes)
|
||||||
|
HWMON 0xa00 (??)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
NOTE: Coreboot sets the SMBASE to 0xf00 in auto.c. But it gets relocated
|
||||||
|
in stage2 because its a "normal BAR" (to 0x2080 in one case here).
|
||||||
|
|
||||||
|
This is not unhealthy but at least confusing. We should provide a method to
|
||||||
|
nail down certain resources for stage2.
|
||||||
|
|
||||||
|
For a list of static I/O space allocation look at 6.3.1 of the ICH7 Family
|
||||||
|
Datasheet.
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the coreboot project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008 coresystems GmbH
|
||||||
|
*
|
||||||
|
* 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SOUTHBRIDGE_INTEL_I82801GX_CHIP_H
|
||||||
|
#define SOUTHBRIDGE_INTEL_I82801GX_CHIP_H
|
||||||
|
|
||||||
|
struct southbridge_intel_i82801gx_config {
|
||||||
|
uint32_t ide_legacy_combined;
|
||||||
|
uint32_t ide_enable_primary;
|
||||||
|
uint32_t ide_enable_secondary;
|
||||||
|
uint32_t sata_ahci;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern struct chip_operations southbridge_intel_i82801gx_ops;
|
||||||
|
|
||||||
|
#endif /* SOUTHBRIDGE_INTEL_I82801GX_CHIP_H */
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the coreboot project.
|
||||||
|
*
|
||||||
|
* 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "i82801gx.h"
|
||||||
|
|
||||||
|
#define RTC_FAILED (1 <<2)
|
||||||
|
#define GEN_PMCON_3 0xa4
|
||||||
|
|
||||||
|
static void check_cmos_failed(void)
|
||||||
|
{
|
||||||
|
u8 byte;
|
||||||
|
byte = pci_read_config8(PCI_DEV(0, 0x1f, 0), GEN_PMCON_3);
|
||||||
|
if (byte & RTC_FAILED) {
|
||||||
|
// clear bit 1 and bit 2
|
||||||
|
byte = cmos_read(RTC_BOOT_BYTE);
|
||||||
|
byte &= 0x0c;
|
||||||
|
byte |= MAX_REBOOT_CNT << 4;
|
||||||
|
cmos_write(byte, RTC_BOOT_BYTE);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the coreboot project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008 coresystems GmbH
|
||||||
|
*
|
||||||
|
* 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <console/console.h>
|
||||||
|
#include <device/device.h>
|
||||||
|
#include <device/pci.h>
|
||||||
|
#include "i82801gx.h"
|
||||||
|
|
||||||
|
void i82801gx_enable(device_t dev)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
struct chip_operations southbridge_intel_i82801gx_ops = {
|
||||||
|
CHIP_NAME("Intel ICH7/ICH7-M (82801Gx) Series Southbridge")
|
||||||
|
.enable_dev = i82801gx_enable,
|
||||||
|
};
|
|
@ -0,0 +1,122 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the coreboot project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008 coresystems GmbH
|
||||||
|
*
|
||||||
|
* 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SOUTHBRIDGE_INTEL_I82801GX_I82801GX_H
|
||||||
|
#define SOUTHBRIDGE_INTEL_I82801GX_I82801GX_H
|
||||||
|
|
||||||
|
#include "chip.h"
|
||||||
|
extern void i82801gx_enable(device_t dev);
|
||||||
|
|
||||||
|
#define PCI_DMA_CFG 0x90
|
||||||
|
#define SERIRQ_CNTL 0x64
|
||||||
|
#define GEN_CNTL 0xd0
|
||||||
|
#define GEN_STS 0xd4
|
||||||
|
#define RTC_CONF 0xd8
|
||||||
|
#define GEN_PMCON_1 0xa0
|
||||||
|
#define GEN_PMCON_2 0xa2
|
||||||
|
#define GEN_PMCON_3 0xa4
|
||||||
|
|
||||||
|
#define PMBASE 0x40
|
||||||
|
#define ACPI_CNTL 0x44
|
||||||
|
#define BIOS_CNTL 0x4E
|
||||||
|
#define GPIO_BASE 0x48 /* LPC GPIO Base Address Register */
|
||||||
|
#define GPIO_CNTL 0x4C /* LPC GPIO Control Register */
|
||||||
|
|
||||||
|
#define PIRQA_ROUT 0x60
|
||||||
|
#define PIRQB_ROUT 0x61
|
||||||
|
#define PIRQC_ROUT 0x62
|
||||||
|
#define PIRQD_ROUT 0x63
|
||||||
|
#define PIRQE_ROUT 0x68
|
||||||
|
#define PIRQF_ROUT 0x69
|
||||||
|
#define PIRQG_ROUT 0x6A
|
||||||
|
#define PIRQH_ROUT 0x6B
|
||||||
|
|
||||||
|
#define FUNC_DIS 0xF2
|
||||||
|
|
||||||
|
#define LPC_IO_DEC 0x80 /* IO Decode Ranges Register */
|
||||||
|
#define LPC_EN 0x82 /* LPC IF Enables Register */
|
||||||
|
|
||||||
|
#define SBUS_NUM 0x19
|
||||||
|
#define SUB_BUS_NUM 0x1A
|
||||||
|
#define SMLT 0x1B
|
||||||
|
#define IOBASE 0x1C
|
||||||
|
#define IOLIM 0x1D
|
||||||
|
#define MEMBASE 0x20
|
||||||
|
#define MEMLIM 0x22
|
||||||
|
#define CNF 0x50
|
||||||
|
#define MTT 0x70
|
||||||
|
#define PCI_MAST_STS 0x82
|
||||||
|
|
||||||
|
/* GEN_PMCON_3 bits */
|
||||||
|
#define RTC_BATTERY_DEAD (1 << 2)
|
||||||
|
#define RTC_POWER_FAILED (1 << 1)
|
||||||
|
#define SLEEP_AFTER_POWER_FAIL (1 << 0)
|
||||||
|
|
||||||
|
/* PCI Configuration Space (D31:F1) */
|
||||||
|
#define INTR_LN 0x3c
|
||||||
|
#define IDE_TIM_PRI 0x40 /* IDE timings, primary */
|
||||||
|
#define IDE_DECODE_ENABLE (1 << 15)
|
||||||
|
#define IDE_SITRE (1 << 14)
|
||||||
|
#define IDE_TIM_SEC 0x42 /* IDE timings, secondary */
|
||||||
|
#define IDE_CONFIG 0x54 /* IDE I/O Configuration Register */
|
||||||
|
#define SIG_MODE_NORMAL (0 << 16)
|
||||||
|
#define SIG_MODE_TRISTATE (1 << 16)
|
||||||
|
#define SIG_MODE_DRIVELOW (2 << 16)
|
||||||
|
#define FAST_PCB1 (1 << 13)
|
||||||
|
#define FAST_PCB0 (1 << 12)
|
||||||
|
#define PCB1 (1 << 1)
|
||||||
|
#define PCB0 (1 << 0)
|
||||||
|
|
||||||
|
/* PCI Configuration Space (D31:F3) */
|
||||||
|
#define SMB_BASE 0x20
|
||||||
|
#define HOSTC 0x40
|
||||||
|
|
||||||
|
/* HOSTC bits */
|
||||||
|
#define I2C_EN (1 << 2)
|
||||||
|
#define SMB_SMI_EN (1 << 1)
|
||||||
|
#define HST_EN (1 << 0)
|
||||||
|
|
||||||
|
/* SMBus I/O bits.
|
||||||
|
* TODO: It does not matter where we put the SMBus IO base, as long as we keep
|
||||||
|
* consistent and don't interfere with other devices. Stage2 will relocate
|
||||||
|
* this anyways. But it's a general problem we have not solved in the brightest
|
||||||
|
* possible way.
|
||||||
|
*/
|
||||||
|
/* #define SMBUS_IO_BASE 0x1000 */
|
||||||
|
#define SMBUS_IO_BASE 0x0400
|
||||||
|
|
||||||
|
#define SMBHSTSTAT 0x0
|
||||||
|
#define SMBHSTCTL 0x2
|
||||||
|
#define SMBHSTCMD 0x3
|
||||||
|
#define SMBXMITADD 0x4
|
||||||
|
#define SMBHSTDAT0 0x5
|
||||||
|
#define SMBHSTDAT1 0x6
|
||||||
|
#define SMBBLKDAT 0x7
|
||||||
|
#define SMBTRNSADD 0x9
|
||||||
|
#define SMBSLVDATA 0xa
|
||||||
|
#define SMLINK_PIN_CTL 0xe
|
||||||
|
#define SMBUS_PIN_CTL 0xf
|
||||||
|
|
||||||
|
#define SMBUS_TIMEOUT (10 * 1000 * 100)
|
||||||
|
|
||||||
|
/* HPET, if present */
|
||||||
|
#define HPET_ADDR 0xfed0000
|
||||||
|
|
||||||
|
#endif /* SOUTHBRIDGE_INTEL_I82801GX_I82801GX_H */
|
|
@ -0,0 +1,71 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the coreboot project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008 coresystems GmbH
|
||||||
|
*
|
||||||
|
* 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <console/console.h>
|
||||||
|
#include <device/device.h>
|
||||||
|
#include <device/pci.h>
|
||||||
|
#include <device/pci_ids.h>
|
||||||
|
#include "i82801gx.h"
|
||||||
|
|
||||||
|
static void ac97_audio_init(struct device *dev)
|
||||||
|
{
|
||||||
|
// XXX init AC97 codecs.
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ac97_modem_init(struct device *dev)
|
||||||
|
{
|
||||||
|
// XXX init modem?
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct device_operations ac97_audio_ops = {
|
||||||
|
.read_resources = pci_dev_read_resources,
|
||||||
|
.set_resources = pci_dev_set_resources,
|
||||||
|
.enable_resources = pci_dev_enable_resources,
|
||||||
|
.init = ac97_audio_init,
|
||||||
|
.scan_bus = 0,
|
||||||
|
.enable = i82801gx_enable,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct device_operations ac97_modem_ops = {
|
||||||
|
.read_resources = pci_dev_read_resources,
|
||||||
|
.set_resources = pci_dev_set_resources,
|
||||||
|
.enable_resources = pci_dev_enable_resources,
|
||||||
|
.init = ac97_modem_init,
|
||||||
|
.scan_bus = 0,
|
||||||
|
.enable = i82801gx_enable,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* 82801GB/GR/GDH/GBM/GHM (ICH7/ICH7R/ICH7DH/ICH7-M/ICH7-M DH) */
|
||||||
|
/* Note: 82801GU (ICH7-U) doesn't have AC97 audio/modem. */
|
||||||
|
|
||||||
|
static const struct pci_driver i82801gb_ac97_audio __pci_driver = {
|
||||||
|
.ops = &ac97_audio_ops,
|
||||||
|
.vendor = PCI_VENDOR_ID_INTEL,
|
||||||
|
.device = PCI_DEVICE_ID_INTEL_82801GB_AC97_AUDIO,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct pci_driver i82801gb_ac97_modem __pci_driver = {
|
||||||
|
.ops = &ac97_modem_ops,
|
||||||
|
.vendor = PCI_VENDOR_ID_INTEL,
|
||||||
|
.device = PCI_DEVICE_ID_INTEL_82801GB_AC97_MODEM,
|
||||||
|
};
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the coreboot project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008 coresystems GmbH
|
||||||
|
*
|
||||||
|
* 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <device/pci_ids.h>
|
||||||
|
#include "i82801gx.h"
|
||||||
|
#include "i82801gx_smbus.h"
|
||||||
|
|
||||||
|
static void enable_smbus(void)
|
||||||
|
{
|
||||||
|
device_t dev;
|
||||||
|
|
||||||
|
/* Set the SMBus device statically. */
|
||||||
|
dev = PCI_DEV(0x0, 0x1f, 0x3);
|
||||||
|
|
||||||
|
/* Check to make sure we've got the right device. */
|
||||||
|
if (pci_read_config16(dev, 0x2) != 0x27da) {
|
||||||
|
die("SMBus controller not found!");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set SMBus I/O base. */
|
||||||
|
pci_write_config32(dev, SMB_BASE,
|
||||||
|
SMBUS_IO_BASE | PCI_BASE_ADDRESS_SPACE_IO);
|
||||||
|
|
||||||
|
/* Set SMBus enable. */
|
||||||
|
pci_write_config8(dev, HOSTC, HST_EN);
|
||||||
|
|
||||||
|
/* Set SMBus I/O space enable. */
|
||||||
|
pci_write_config16(dev, PCI_COMMAND, PCI_COMMAND_IO);
|
||||||
|
|
||||||
|
/* Disable interrupt generation. */
|
||||||
|
outb(0, SMBUS_IO_BASE + SMBHSTCTL);
|
||||||
|
|
||||||
|
/* Clear any lingering errors, so transactions can run. */
|
||||||
|
outb(inb(SMBUS_IO_BASE + SMBHSTSTAT), SMBUS_IO_BASE + SMBHSTSTAT);
|
||||||
|
print_debug("SMBus controller enabled.\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int smbus_read_byte(unsigned device, unsigned address)
|
||||||
|
{
|
||||||
|
return do_smbus_read_byte(device, address);
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the coreboot project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008 coresystems GmbH
|
||||||
|
*
|
||||||
|
* 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <console/console.h>
|
||||||
|
#include <device/device.h>
|
||||||
|
#include <device/pci.h>
|
||||||
|
#include <device/pci_ids.h>
|
||||||
|
#include "i82801gx.h"
|
||||||
|
|
||||||
|
typedef struct southbridge_intel_i82801gx_config config_t;
|
||||||
|
|
||||||
|
static void ide_init(struct device *dev)
|
||||||
|
{
|
||||||
|
u16 ideTimingConfig;
|
||||||
|
u32 reg32;
|
||||||
|
|
||||||
|
/* Get the chip configuration */
|
||||||
|
config_t *config = dev->chip_info;
|
||||||
|
|
||||||
|
int enable_primary = config->ide_enable_primary;
|
||||||
|
int enable_secondary = config->ide_enable_secondary;
|
||||||
|
|
||||||
|
reg32 = pci_read_config32(dev, PCI_COMMAND);
|
||||||
|
pci_write_config32(dev, PCI_COMMAND, reg32 | PCI_COMMAND_IO | PCI_COMMAND_MASTER);
|
||||||
|
|
||||||
|
/* Native Capable, but not enabled. */
|
||||||
|
pci_write_config8(dev, 0x09, 0x8a);
|
||||||
|
|
||||||
|
ideTimingConfig = pci_read_config16(dev, IDE_TIM_PRI);
|
||||||
|
ideTimingConfig &= ~IDE_DECODE_ENABLE;
|
||||||
|
ideTimingConfig |= IDE_SITRE;
|
||||||
|
if (enable_primary) {
|
||||||
|
/* Enable primary IDE interface. */
|
||||||
|
ideTimingConfig |= IDE_DECODE_ENABLE;
|
||||||
|
ideTimingConfig |= (2 << 12); // ISP = 3 clocks
|
||||||
|
ideTimingConfig |= (3 << 8); // RCT = 1 clock
|
||||||
|
ideTimingConfig |= (1 << 1); // IE0
|
||||||
|
ideTimingConfig |= (1 << 0); // TIME0
|
||||||
|
printk_debug("IDE0 ");
|
||||||
|
}
|
||||||
|
pci_write_config16(dev, IDE_TIM_PRI, ideTimingConfig);
|
||||||
|
|
||||||
|
ideTimingConfig = pci_read_config16(dev, IDE_TIM_SEC);
|
||||||
|
ideTimingConfig &= ~IDE_DECODE_ENABLE;
|
||||||
|
ideTimingConfig |= IDE_SITRE;
|
||||||
|
if (enable_secondary) {
|
||||||
|
/* Enable secondary IDE interface. */
|
||||||
|
ideTimingConfig |= IDE_DECODE_ENABLE;
|
||||||
|
ideTimingConfig |= (2 << 12); // ISP = 3 clocks
|
||||||
|
ideTimingConfig |= (3 << 8); // RCT = 1 clock
|
||||||
|
ideTimingConfig |= (1 << 1); // IE0
|
||||||
|
ideTimingConfig |= (1 << 0); // TIME0
|
||||||
|
printk_debug("IDE1 ");
|
||||||
|
}
|
||||||
|
pci_write_config16(dev, IDE_TIM_SEC, ideTimingConfig);
|
||||||
|
|
||||||
|
/* Set IDE I/O Configuration */
|
||||||
|
if (enable_secondary)
|
||||||
|
reg32 = SIG_MODE_NORMAL | FAST_PCB1 | FAST_PCB0 | PCB1 | PCB0;
|
||||||
|
else
|
||||||
|
reg32 = SIG_MODE_NORMAL | FAST_PCB1 | PCB1;
|
||||||
|
pci_write_config32(dev, IDE_CONFIG, reg32);
|
||||||
|
|
||||||
|
/* Set Interrupt Line */
|
||||||
|
/* Interrupt Pin is set by D31IP.PIP */
|
||||||
|
pci_write_config32(dev, INTR_LN, 0xff); /* Int 15 */
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct device_operations ide_ops = {
|
||||||
|
.read_resources = pci_dev_read_resources,
|
||||||
|
.set_resources = pci_dev_set_resources,
|
||||||
|
.enable_resources = pci_dev_enable_resources,
|
||||||
|
.init = ide_init,
|
||||||
|
.scan_bus = 0,
|
||||||
|
.enable = i82801gx_enable,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* 82801Gx */
|
||||||
|
static const struct pci_driver i82801gx_ide __pci_driver = {
|
||||||
|
.ops = &ide_ops,
|
||||||
|
.vendor = PCI_VENDOR_ID_INTEL,
|
||||||
|
.device = 0x27df,
|
||||||
|
};
|
|
@ -0,0 +1,361 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the coreboot project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008 coresystems GmbH
|
||||||
|
*
|
||||||
|
* 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <console/console.h>
|
||||||
|
#include <device/device.h>
|
||||||
|
#include <device/pci.h>
|
||||||
|
#include <device/pci_ids.h>
|
||||||
|
#include <pc80/mc146818rtc.h>
|
||||||
|
#include <pc80/isa-dma.h>
|
||||||
|
#include <arch/io.h>
|
||||||
|
#include "i82801gx.h"
|
||||||
|
|
||||||
|
#include "../../../northbridge/intel/i945/ich7.h"
|
||||||
|
|
||||||
|
#define MAINBOARD_POWER_OFF 0
|
||||||
|
#define MAINBOARD_POWER_ON 1
|
||||||
|
|
||||||
|
#ifndef MAINBOARD_POWER_ON_AFTER_FAIL
|
||||||
|
#define MAINBOARD_POWER_ON_AFTER_FAIL MAINBOARD_POWER_ON
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define NMI_OFF 0
|
||||||
|
|
||||||
|
/* PIRQ[n]_ROUT[3:0] - PIRQ Routing Control
|
||||||
|
* 0x00 - 0000 = Reserved
|
||||||
|
* 0x01 - 0001 = Reserved
|
||||||
|
* 0x02 - 0010 = Reserved
|
||||||
|
* 0x03 - 0011 = IRQ3
|
||||||
|
* 0x04 - 0100 = IRQ4
|
||||||
|
* 0x05 - 0101 = IRQ5
|
||||||
|
* 0x06 - 0110 = IRQ6
|
||||||
|
* 0x07 - 0111 = IRQ7
|
||||||
|
* 0x08 - 1000 = Reserved
|
||||||
|
* 0x09 - 1001 = IRQ9
|
||||||
|
* 0x0A - 1010 = IRQ10
|
||||||
|
* 0x0B - 1011 = IRQ11
|
||||||
|
* 0x0C - 1100 = IRQ12
|
||||||
|
* 0x0D - 1101 = Reserved
|
||||||
|
* 0x0E - 1110 = IRQ14
|
||||||
|
* 0x0F - 1111 = IRQ15
|
||||||
|
* PIRQ[n]_ROUT[7] - PIRQ Routing Control
|
||||||
|
* 0x80 - The PIRQ is not routed.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define PIRQA 0x03
|
||||||
|
#define PIRQB 0x05
|
||||||
|
#define PIRQC 0x06
|
||||||
|
#define PIRQD 0x07
|
||||||
|
#define PIRQE 0x09
|
||||||
|
#define PIRQF 0x0A
|
||||||
|
#define PIRQG 0x0B
|
||||||
|
#define PIRQH 0x0C
|
||||||
|
|
||||||
|
static void i82801gx_enable_apic(struct device *dev)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
u32 reg32;
|
||||||
|
volatile u32 *ioapic_index = (volatile u32 *)0xfec00000;
|
||||||
|
volatile u32 *ioapic_data = (volatile u32 *)0xfec00010;
|
||||||
|
|
||||||
|
/* Enable ACPI I/O and power management. */
|
||||||
|
pci_write_config8(dev, ACPI_CNTL, 0x80);
|
||||||
|
|
||||||
|
*ioapic_index = 0;
|
||||||
|
*ioapic_data = (1 << 25);
|
||||||
|
|
||||||
|
*ioapic_index = 0;
|
||||||
|
reg32 = *ioapic_data;
|
||||||
|
printk_debug("Southbridge APIC ID = %x\n", (reg32 >> 24) & 0x0f);
|
||||||
|
if (reg32 != (1 << 25))
|
||||||
|
die("APIC Error\n");
|
||||||
|
|
||||||
|
printk_spew("Dumping IOAPIC registers\n");
|
||||||
|
for (i=0; i<3; i++) {
|
||||||
|
*ioapic_index = i;
|
||||||
|
printk_spew(" reg 0x%04x:", i);
|
||||||
|
reg32 = *ioapic_data;
|
||||||
|
printk_spew(" 0x%08x\n", reg32);
|
||||||
|
}
|
||||||
|
|
||||||
|
*ioapic_index = 3; /* Select Boot Configuration register. */
|
||||||
|
*ioapic_data = 1; /* Use Processor System Bus to deliver interrupts. */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void i82801gx_enable_serial_irqs(struct device *dev)
|
||||||
|
{
|
||||||
|
/* Set packet length and toggle silent mode bit for one frame. */
|
||||||
|
pci_write_config8(dev, SERIRQ_CNTL,
|
||||||
|
(1 << 7) | (1 << 6) | ((21 - 17) << 2) | (0 << 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void i82801gx_pirq_init(device_t dev)
|
||||||
|
{
|
||||||
|
pci_write_config8(dev, PIRQA_ROUT, 0x85);
|
||||||
|
pci_write_config8(dev, PIRQB_ROUT, 0x87);
|
||||||
|
pci_write_config8(dev, PIRQC_ROUT, 0x86);
|
||||||
|
pci_write_config8(dev, PIRQD_ROUT, 0x87);
|
||||||
|
|
||||||
|
pci_write_config8(dev, PIRQE_ROUT, 0x80);
|
||||||
|
pci_write_config8(dev, PIRQF_ROUT, 0x80);
|
||||||
|
pci_write_config8(dev, PIRQG_ROUT, 0x80);
|
||||||
|
pci_write_config8(dev, PIRQH_ROUT, 0x85);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void i82801gx_power_options(device_t dev)
|
||||||
|
{
|
||||||
|
u8 reg8;
|
||||||
|
u16 reg16;
|
||||||
|
|
||||||
|
int pwr_on=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
|
||||||
|
*/
|
||||||
|
get_option(&pwr_on, "power_on_after_fail");
|
||||||
|
reg8 = pci_read_config8(dev, GEN_PMCON_3);
|
||||||
|
reg8 &= 0xfe;
|
||||||
|
if (pwr_on) {
|
||||||
|
reg8 &= ~1;
|
||||||
|
} else {
|
||||||
|
reg8 |= 1;
|
||||||
|
}
|
||||||
|
reg8 |= (3 << 4); /* avoid #S4 assertions */
|
||||||
|
|
||||||
|
pci_write_config8(dev, GEN_PMCON_3, reg8);
|
||||||
|
printk_info("Set power %s after power failure.\n", pwr_on ? "on" : "off");
|
||||||
|
|
||||||
|
/* 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_info ("NMI sources enabled.\n");
|
||||||
|
reg8 &= ~(1 << 7); /* Set NMI. */
|
||||||
|
} else {
|
||||||
|
printk_info ("NMI sources disabled.\n");
|
||||||
|
reg8 |= ( 1 << 7); /* Can't mask NMI from PCI-E and NMI_NOW */
|
||||||
|
}
|
||||||
|
outb(reg8, 0x70);
|
||||||
|
|
||||||
|
// Enable CPU_SLP# and Intel Speedstep, set SMI# rate down
|
||||||
|
reg16 = pci_read_config16(dev, GEN_PMCON_1);
|
||||||
|
reg16 &= ~3;
|
||||||
|
reg16 |= (1 << 3) | (1 << 5) | (1 << 10);
|
||||||
|
pci_write_config16(dev, GEN_PMCON_1, reg16);
|
||||||
|
|
||||||
|
// Set GPIO13 to SCI (?)
|
||||||
|
// This might be board specific
|
||||||
|
pci_write_config32(dev, 0xb8, 0x08000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void i82801gx_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 &= ~RTC_BATTERY_DEAD;
|
||||||
|
pci_write_config8(dev, GEN_PMCON_3, reg8);
|
||||||
|
}
|
||||||
|
printk_debug("rtc_failed = 0x%x\n", rtc_failed);
|
||||||
|
|
||||||
|
rtc_init(rtc_failed);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void enable_hpet(struct device *dev)
|
||||||
|
{
|
||||||
|
u32 reg32;
|
||||||
|
u32 code = (0 & 0x3);
|
||||||
|
|
||||||
|
reg32 = pci_read_config32(dev, GEN_CNTL);
|
||||||
|
reg32 |= (1 << 17); /* Enable HPET. */
|
||||||
|
/*
|
||||||
|
* Bits [16:15] Memory Address Range
|
||||||
|
* 00 FED0_0000h - FED0_03FFh
|
||||||
|
* 01 FED0_1000h - FED0_13FFh
|
||||||
|
* 10 FED0_2000h - FED0_23FFh
|
||||||
|
* 11 FED0_3000h - FED0_33FFh
|
||||||
|
*/
|
||||||
|
reg32 &= ~(3 << 15); /* Clear it */
|
||||||
|
reg32 |= (code << 15);
|
||||||
|
/* TODO: reg32 is never written to anywhere? */
|
||||||
|
printk_debug("Enabling HPET @0x%x\n", HPET_ADDR | (code << 12));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void i82801gx_lock_smm(struct device *dev)
|
||||||
|
{
|
||||||
|
void smm_lock(void);
|
||||||
|
u8 reg8;
|
||||||
|
|
||||||
|
#if ENABLE_ACPI_MODE_IN_COREBOOT
|
||||||
|
printk_debug("Enabling ACPI via APMC:\n");
|
||||||
|
outb(0xe1, 0xb2); // Enable ACPI mode
|
||||||
|
printk_debug("done.\n");
|
||||||
|
#else
|
||||||
|
printk_debug("Disabling ACPI via APMC:\n");
|
||||||
|
outb(0x1e, 0xb2); // Disable ACPI mode
|
||||||
|
printk_debug("done.\n");
|
||||||
|
#endif
|
||||||
|
/* Don't allow evil boot loaders, kernels, or
|
||||||
|
* userspace applications to deceive us:
|
||||||
|
*/
|
||||||
|
smm_lock();
|
||||||
|
|
||||||
|
#if TEST_SMM_FLASH_LOCKDOWN
|
||||||
|
/* Now try this: */
|
||||||
|
printk_debug("Locking BIOS to RO... ");
|
||||||
|
reg8 = pci_read_config8(dev, 0xdc); /* BIOS_CNTL */
|
||||||
|
printk_debug(" BLE: %s; BWE: %s\n", (reg8&2)?"on":"off",
|
||||||
|
(reg8&1)?"rw":"ro");
|
||||||
|
reg8 &= ~(1 << 0); /* clear BIOSWE */
|
||||||
|
pci_write_config8(dev, 0xdc, reg8);
|
||||||
|
reg8 |= (1 << 1); /* set BLE */
|
||||||
|
pci_write_config8(dev, 0xdc, reg8);
|
||||||
|
printk_debug("ok.\n");
|
||||||
|
reg8 = pci_read_config8(dev, 0xdc); /* BIOS_CNTL */
|
||||||
|
printk_debug(" BLE: %s; BWE: %s\n", (reg8&2)?"on":"off",
|
||||||
|
(reg8&1)?"rw":"ro");
|
||||||
|
|
||||||
|
printk_debug("Writing:\n");
|
||||||
|
*(volatile u8 *)0xfff00000 = 0x00;
|
||||||
|
printk_debug("Testing:\n");
|
||||||
|
reg8 |= (1 << 0); /* set BIOSWE */
|
||||||
|
pci_write_config8(dev, 0xdc, reg8);
|
||||||
|
|
||||||
|
reg8 = pci_read_config8(dev, 0xdc); /* BIOS_CNTL */
|
||||||
|
printk_debug(" BLE: %s; BWE: %s\n", (reg8&2)?"on":"off",
|
||||||
|
(reg8&1)?"rw":"ro");
|
||||||
|
printk_debug("Done.\n");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lpc_init(struct device *dev)
|
||||||
|
{
|
||||||
|
printk_debug("i82801gx: lpc_init\n");
|
||||||
|
|
||||||
|
/* Set the value for PCI command register. */
|
||||||
|
pci_write_config16(dev, PCI_COMMAND, 0x000f);
|
||||||
|
|
||||||
|
/* IO APIC initialization. */
|
||||||
|
i82801gx_enable_apic(dev);
|
||||||
|
|
||||||
|
i82801gx_enable_serial_irqs(dev);
|
||||||
|
|
||||||
|
/* Setup the PIRQ. */
|
||||||
|
i82801gx_pirq_init(dev);
|
||||||
|
|
||||||
|
/* Setup power options. */
|
||||||
|
i82801gx_power_options(dev);
|
||||||
|
|
||||||
|
/* Set the state of the GPIO lines. */
|
||||||
|
//gpio_init(dev);
|
||||||
|
|
||||||
|
/* Initialize the real time clock. */
|
||||||
|
i82801gx_rtc_init(dev);
|
||||||
|
|
||||||
|
/* Initialize ISA DMA. */
|
||||||
|
isa_dma_init();
|
||||||
|
|
||||||
|
/* Initialize the High Precision Event Timers, if present. */
|
||||||
|
enable_hpet(dev);
|
||||||
|
|
||||||
|
setup_i8259();
|
||||||
|
|
||||||
|
i82801gx_lock_smm(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void i82801gx_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->flags =
|
||||||
|
IORESOURCE_IO | IORESOURCE_SUBTRACTIVE | IORESOURCE_ASSIGNED;
|
||||||
|
|
||||||
|
res = new_resource(dev, IOINDEX_SUBTRACTIVE(1, 0));
|
||||||
|
res->flags =
|
||||||
|
IORESOURCE_MEM | IORESOURCE_SUBTRACTIVE | IORESOURCE_ASSIGNED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void i82801gx_lpc_enable_resources(device_t dev)
|
||||||
|
{
|
||||||
|
pci_dev_enable_resources(dev);
|
||||||
|
enable_childrens_resources(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_subsystem(device_t dev, unsigned vendor, unsigned device)
|
||||||
|
{
|
||||||
|
printk_debug("Setting LPC bridge subsystem ID\n");
|
||||||
|
pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID,
|
||||||
|
pci_read_config32(dev, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct pci_operations pci_ops = {
|
||||||
|
.set_subsystem = set_subsystem,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct device_operations device_ops = {
|
||||||
|
.read_resources = i82801gx_lpc_read_resources,
|
||||||
|
.set_resources = pci_dev_set_resources,
|
||||||
|
.enable_resources = i82801gx_lpc_enable_resources,
|
||||||
|
.init = lpc_init,
|
||||||
|
.scan_bus = scan_static_bus,
|
||||||
|
.enable = i82801gx_enable,
|
||||||
|
.ops_pci = &pci_ops,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ICH7 / ICH7R */
|
||||||
|
static const struct pci_driver ich7_ich7r_lpc __pci_driver = {
|
||||||
|
.ops = &device_ops,
|
||||||
|
.vendor = PCI_VENDOR_ID_INTEL,
|
||||||
|
.device = 0x27b8,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ICH7M / ICH7U */
|
||||||
|
static const struct pci_driver ich7m_ich7u_lpc __pci_driver = {
|
||||||
|
.ops = &device_ops,
|
||||||
|
.vendor = PCI_VENDOR_ID_INTEL,
|
||||||
|
.device = 0x27b9,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ICH7M DH */
|
||||||
|
static const struct pci_driver ich7m_dh_lpc __pci_driver = {
|
||||||
|
.ops = &device_ops,
|
||||||
|
.vendor = PCI_VENDOR_ID_INTEL,
|
||||||
|
.device = 0x27bd,
|
||||||
|
};
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the coreboot project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008 coresystems GmbH
|
||||||
|
*
|
||||||
|
* 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* This code should work for all ICH* southbridges with a NIC. */
|
||||||
|
|
||||||
|
#include <console/console.h>
|
||||||
|
#include <device/device.h>
|
||||||
|
#include <device/pci.h>
|
||||||
|
#include <device/pci_ids.h>
|
||||||
|
|
||||||
|
static void nic_init(struct device *dev)
|
||||||
|
{
|
||||||
|
/* Nothing yet */
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct device_operations nic_ops = {
|
||||||
|
.read_resources = pci_dev_read_resources,
|
||||||
|
.set_resources = pci_dev_set_resources,
|
||||||
|
.enable_resources = pci_dev_enable_resources,
|
||||||
|
.init = nic_init,
|
||||||
|
.scan_bus = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* 82801GB/GR/GDH/GBM/GHM (ICH7/ICH7R/ICH7DH/ICH7-M/ICH7-M DH) */
|
||||||
|
/* Note: 82801GU (ICH7-U) doesn't have a NIC. */
|
||||||
|
/* PCI ID loaded from EEPROM. If EEPROM is 0, 0x27dc is used */
|
||||||
|
static const struct pci_driver i82801gb_nic __pci_driver = {
|
||||||
|
.ops = &nic_ops,
|
||||||
|
.vendor = PCI_VENDOR_ID_INTEL,
|
||||||
|
.device = PCI_DEVICE_ID_INTEL_82801GB_LAN,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the coreboot project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008 coresystems GmbH
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
u16 osys;
|
||||||
|
u8 smif;
|
||||||
|
u8 prm0;
|
||||||
|
u8 prm1;
|
||||||
|
u8 scif;
|
||||||
|
u8 prm2;
|
||||||
|
u8 prm3;
|
||||||
|
u8 lckf;
|
||||||
|
u8 prm4;
|
||||||
|
u8 prm5;
|
||||||
|
u32 p80d;
|
||||||
|
u8 lids;
|
||||||
|
u8 pwrs;
|
||||||
|
u8 dbgs;
|
||||||
|
u8 linxs;
|
||||||
|
u8 rsvd;
|
||||||
|
u8 actt;
|
||||||
|
u8 psvt;
|
||||||
|
u8 tc1v;
|
||||||
|
u8 tc2v;
|
||||||
|
u8 tspv;
|
||||||
|
u8 crtt;
|
||||||
|
u8 dtse;
|
||||||
|
u8 dts1;
|
||||||
|
u8 dts2;
|
||||||
|
u8 rsvd2;
|
||||||
|
u8 bnum;
|
||||||
|
u8 b0sc, b1sc, b2sc;
|
||||||
|
u8 b0ss, b1ss, b2ss;
|
||||||
|
u8 rsvd3[3];
|
||||||
|
u8 apic;
|
||||||
|
u8 mpen;
|
||||||
|
u8 bten;
|
||||||
|
u8 ppcm;
|
||||||
|
u8 pcp0;
|
||||||
|
u8 pcp1;
|
||||||
|
u8 rsvd4[4];
|
||||||
|
u8 natp;
|
||||||
|
u8 cmap;
|
||||||
|
u8 cmbp;
|
||||||
|
u8 lptp;
|
||||||
|
u8 fdcp;
|
||||||
|
u8 rfdv;
|
||||||
|
u8 hotk;
|
||||||
|
u8 rtcf;
|
||||||
|
u8 util;
|
||||||
|
u8 acin;
|
||||||
|
u8 igds;
|
||||||
|
u8 tlst;
|
||||||
|
u8 cadl;
|
||||||
|
u8 padl;
|
||||||
|
u16 cste;
|
||||||
|
u16 pste;
|
||||||
|
u16 nste;
|
||||||
|
u16 sste;
|
||||||
|
u8 ndid;
|
||||||
|
u32 did1;
|
||||||
|
u32 did2;
|
||||||
|
u32 did3;
|
||||||
|
u32 did4;
|
||||||
|
u32 did5;
|
||||||
|
u8 rsvd5[0xb];
|
||||||
|
u8 brtl;
|
||||||
|
u8 odds;
|
||||||
|
u8 alse;
|
||||||
|
u8 alaf;
|
||||||
|
u8 llow;
|
||||||
|
u8 lhih;
|
||||||
|
u8 rsvd6;
|
||||||
|
u8 emae;
|
||||||
|
u16 emap;
|
||||||
|
u16 emal;
|
||||||
|
u8 rsvd7;
|
||||||
|
u8 mefe;
|
||||||
|
u8 igps;
|
||||||
|
u8 rsvd8[2];
|
||||||
|
u8 tpmp;
|
||||||
|
u8 tpme;
|
||||||
|
u8 rsvd9[8];
|
||||||
|
u8 gtf0[7];
|
||||||
|
u8 gtf2[7];
|
||||||
|
u8 idem;
|
||||||
|
u8 idet;
|
||||||
|
u8 dock;
|
||||||
|
} global_nvs_t;
|
||||||
|
|
|
@ -0,0 +1,141 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the coreboot project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008 coresystems GmbH
|
||||||
|
*
|
||||||
|
* 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <console/console.h>
|
||||||
|
#include <device/device.h>
|
||||||
|
#include <device/pci.h>
|
||||||
|
#include <device/pci_ids.h>
|
||||||
|
|
||||||
|
static void pci_init(struct device *dev)
|
||||||
|
{
|
||||||
|
u16 reg16;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
/* Commented out for now because it will break on some machines. */
|
||||||
|
/* Set latency timer to 32. */
|
||||||
|
pci_write_config16(dev, 0x1b, 0x20);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* disable parity error response */
|
||||||
|
reg16 = pci_read_config16(dev, 0x3e);
|
||||||
|
reg16 &= ~(1 << 0);
|
||||||
|
pci_write_config16(dev, 0x3e, reg16);
|
||||||
|
|
||||||
|
/* Clear errors in status registers */
|
||||||
|
reg16 = pci_read_config16(dev, 0x06);
|
||||||
|
reg16 |= 0xf900;
|
||||||
|
pci_write_config16(dev, 0x06, reg16);
|
||||||
|
|
||||||
|
reg16 = pci_read_config16(dev, 0x1e);
|
||||||
|
reg16 |= 0xf900;
|
||||||
|
pci_write_config16(dev, 0x1e, reg16);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ich_pci_dev_enable_resources(struct device *dev)
|
||||||
|
{
|
||||||
|
const struct pci_operations *ops;
|
||||||
|
uint16_t command;
|
||||||
|
|
||||||
|
/* Set the subsystem vendor and device id for mainboard devices */
|
||||||
|
ops = ops_pci(dev);
|
||||||
|
if (dev->on_mainboard && ops && ops->set_subsystem) {
|
||||||
|
printk_debug("%s subsystem <- %02x/%02x\n",
|
||||||
|
dev_path(dev),
|
||||||
|
MAINBOARD_PCI_SUBSYSTEM_VENDOR_ID,
|
||||||
|
MAINBOARD_PCI_SUBSYSTEM_DEVICE_ID);
|
||||||
|
ops->set_subsystem(dev,
|
||||||
|
MAINBOARD_PCI_SUBSYSTEM_VENDOR_ID,
|
||||||
|
MAINBOARD_PCI_SUBSYSTEM_DEVICE_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
/* If we write to PCI_COMMAND, on some systems
|
||||||
|
* this will cause the ROM and APICs not being visible
|
||||||
|
* anymore.
|
||||||
|
*/
|
||||||
|
command = pci_read_config16(dev, PCI_COMMAND);
|
||||||
|
command |= dev->command;
|
||||||
|
printk_debug("%s cmd <- %02x\n", dev_path(dev), command);
|
||||||
|
pci_write_config16(dev, PCI_COMMAND, command);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ich_pci_bus_enable_resources(struct device *dev)
|
||||||
|
{
|
||||||
|
uint16_t ctrl;
|
||||||
|
/* enable IO in command register if there is VGA card
|
||||||
|
* connected with (even it does not claim IO resource)
|
||||||
|
*/
|
||||||
|
if (dev->link[0].bridge_ctrl & PCI_BRIDGE_CTL_VGA)
|
||||||
|
dev->command |= PCI_COMMAND_IO;
|
||||||
|
ctrl = pci_read_config16(dev, PCI_BRIDGE_CONTROL);
|
||||||
|
ctrl |= dev->link[0].bridge_ctrl;
|
||||||
|
ctrl |= (PCI_BRIDGE_CTL_PARITY + PCI_BRIDGE_CTL_SERR); /* error check */
|
||||||
|
printk_debug("%s bridge ctrl <- %04x\n", dev_path(dev), ctrl);
|
||||||
|
pci_write_config16(dev, PCI_BRIDGE_CONTROL, ctrl);
|
||||||
|
|
||||||
|
/* This is the reason we need our own pci_bus_enable_resources */
|
||||||
|
ich_pci_dev_enable_resources(dev);
|
||||||
|
|
||||||
|
enable_childrens_resources(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_subsystem(device_t dev, unsigned vendor, unsigned device)
|
||||||
|
{
|
||||||
|
#if 0
|
||||||
|
/* Currently disabled because it causes a "BAR 9" memory resource
|
||||||
|
* conflict:
|
||||||
|
*/
|
||||||
|
u32 pci_id;
|
||||||
|
|
||||||
|
printk_debug("Setting PCI bridge subsystem ID\n");
|
||||||
|
pci_id = pci_read_config32(dev, 0);
|
||||||
|
pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID, pci_id );
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct pci_operations pci_ops = {
|
||||||
|
.set_subsystem = set_subsystem,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct device_operations device_ops = {
|
||||||
|
.read_resources = pci_bus_read_resources,
|
||||||
|
.set_resources = pci_dev_set_resources,
|
||||||
|
.enable_resources = ich_pci_bus_enable_resources,
|
||||||
|
.init = pci_init,
|
||||||
|
.scan_bus = pci_scan_bridge,
|
||||||
|
.ops_pci = &pci_ops,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* Desktop */
|
||||||
|
static const struct pci_driver i82801g_pci __pci_driver = {
|
||||||
|
.ops = &device_ops,
|
||||||
|
.vendor = 0x8086,
|
||||||
|
.device = 0x244e,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Mobile / Ultra Mobile */
|
||||||
|
static const struct pci_driver i82801gmu_pci __pci_driver = {
|
||||||
|
.ops = &device_ops,
|
||||||
|
.vendor = 0x8086,
|
||||||
|
.device = 0x2448,
|
||||||
|
};
|
||||||
|
|
|
@ -0,0 +1,126 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the coreboot project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008 coresystems GmbH
|
||||||
|
*
|
||||||
|
* 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <console/console.h>
|
||||||
|
#include <device/device.h>
|
||||||
|
#include <device/pci.h>
|
||||||
|
#include <device/pci_ids.h>
|
||||||
|
|
||||||
|
static void pci_init(struct device *dev)
|
||||||
|
{
|
||||||
|
u16 reg16;
|
||||||
|
u32 reg32;
|
||||||
|
|
||||||
|
printk_debug("Initializing ICH7 PCIe bridge.\n");
|
||||||
|
#if 0
|
||||||
|
// When the latency of the PCIe(!) bridge is set to 0x20
|
||||||
|
// all devices on the secondary bus of the PCI(!) bridge
|
||||||
|
// suddenly vanish. If you know why, please explain here.
|
||||||
|
|
||||||
|
/* Set latency timer to 32. */
|
||||||
|
pci_write_config16(dev, 0x1b, 0x20);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* disable parity error response */
|
||||||
|
reg16 = pci_read_config16(dev, 0x3e);
|
||||||
|
reg16 &= ~(1 << 0);
|
||||||
|
pci_write_config16(dev, 0x3e, reg16);
|
||||||
|
|
||||||
|
/* Clear errors in status registers */
|
||||||
|
reg16 = pci_read_config16(dev, 0x06);
|
||||||
|
reg16 |= 0xf900;
|
||||||
|
pci_write_config16(dev, 0x06, reg16);
|
||||||
|
|
||||||
|
reg16 = pci_read_config16(dev, 0x1e);
|
||||||
|
reg16 |= 0xf900;
|
||||||
|
pci_write_config16(dev, 0x1e, reg16);
|
||||||
|
|
||||||
|
reg32 = pci_read_config32(dev, 0x20);
|
||||||
|
printk_debug(" MBL = 0x%08x\n", reg32);
|
||||||
|
reg32 = pci_read_config32(dev, 0x24);
|
||||||
|
printk_debug(" PMBL = 0x%08x\n", reg32);
|
||||||
|
reg32 = pci_read_config32(dev, 0x28);
|
||||||
|
printk_debug(" PMBU32 = 0x%08x\n", reg32);
|
||||||
|
reg32 = pci_read_config32(dev, 0x2c);
|
||||||
|
printk_debug(" PMLU32 = 0x%08x\n", reg32);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_subsystem(device_t dev, unsigned vendor, unsigned device)
|
||||||
|
{
|
||||||
|
u32 pci_id;
|
||||||
|
|
||||||
|
printk_debug("Setting PCIe bridge subsystem ID.\n");
|
||||||
|
pci_id = pci_read_config32(dev, 0);
|
||||||
|
pci_write_config32(dev, 0x94, pci_id );
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct pci_operations pci_ops = {
|
||||||
|
.set_subsystem = set_subsystem,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct device_operations device_ops = {
|
||||||
|
.read_resources = pci_bus_read_resources,
|
||||||
|
.set_resources = pci_dev_set_resources,
|
||||||
|
.enable_resources = pci_bus_enable_resources,
|
||||||
|
.init = pci_init,
|
||||||
|
.scan_bus = pci_scan_bridge,
|
||||||
|
.ops_pci = &pci_ops,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct pci_driver i82801gx_pcie_port1 __pci_driver = {
|
||||||
|
.ops = &device_ops,
|
||||||
|
.vendor = 0x8086,
|
||||||
|
.device = 0x27d0,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct pci_driver i82801gx_pcie_port2 __pci_driver = {
|
||||||
|
.ops = &device_ops,
|
||||||
|
.vendor = 0x8086,
|
||||||
|
.device = 0x27d2,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct pci_driver i82801gx_pcie_port3 __pci_driver = {
|
||||||
|
.ops = &device_ops,
|
||||||
|
.vendor = 0x8086,
|
||||||
|
.device = 0x27d4,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct pci_driver i82801gx_pcie_port4 __pci_driver = {
|
||||||
|
.ops = &device_ops,
|
||||||
|
.vendor = 0x8086,
|
||||||
|
.device = 0x27d6,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct pci_driver i82801gx_pcie_port5 __pci_driver = {
|
||||||
|
.ops = &device_ops,
|
||||||
|
.vendor = 0x8086,
|
||||||
|
.device = 0x27e0,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct pci_driver i82801gx_pcie_port6 __pci_driver = {
|
||||||
|
.ops = &device_ops,
|
||||||
|
.vendor = 0x8086,
|
||||||
|
.device = 0x27e2,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the coreboot project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008 coresystems GmbH
|
||||||
|
*
|
||||||
|
* 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <arch/io.h>
|
||||||
|
|
||||||
|
void hard_reset(void)
|
||||||
|
{
|
||||||
|
/* Try rebooting through port 0xcf9. */
|
||||||
|
outb((1 << 2) | (1 << 1), 0xcf9);
|
||||||
|
}
|
|
@ -0,0 +1,190 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the coreboot project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008 coresystems GmbH
|
||||||
|
*
|
||||||
|
* 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <console/console.h>
|
||||||
|
#include <device/device.h>
|
||||||
|
#include <device/pci.h>
|
||||||
|
#include <device/pci_ids.h>
|
||||||
|
#include "i82801gx.h"
|
||||||
|
|
||||||
|
typedef struct southbridge_intel_i82801gx_config config_t;
|
||||||
|
|
||||||
|
static void sata_init(struct device *dev)
|
||||||
|
{
|
||||||
|
u32 reg32;
|
||||||
|
/* Get the chip configuration */
|
||||||
|
config_t *config = dev->chip_info;
|
||||||
|
|
||||||
|
printk_debug("i82801gx_sata: initializing...\n");
|
||||||
|
/* SATA configuration */
|
||||||
|
|
||||||
|
/* Enable BARs */
|
||||||
|
pci_write_config16(dev, 0x04, 0x0007);
|
||||||
|
|
||||||
|
if (config->ide_legacy_combined) {
|
||||||
|
pci_write_config8(dev, 0x09, 0x80);
|
||||||
|
|
||||||
|
/* Set timings */
|
||||||
|
pci_write_config16(dev, IDE_TIM_PRI, 0x8000);
|
||||||
|
pci_write_config16(dev, IDE_TIM_SEC, 0xa307);
|
||||||
|
|
||||||
|
/* Sync DMA */
|
||||||
|
pci_write_config16(dev, 0x48, 0x0004);
|
||||||
|
pci_write_config16(dev, 0x4a, 0x0200);
|
||||||
|
|
||||||
|
/* Combine IDE - SATA configuration */
|
||||||
|
pci_write_config8(dev, 0x90, 0x02);
|
||||||
|
|
||||||
|
/* Port 0 & 1 enable */
|
||||||
|
pci_write_config8(dev, 0x92, 0x0f);
|
||||||
|
|
||||||
|
/* SATA Initialization register */
|
||||||
|
pci_write_config32(dev, 0x94, 0x40000180);
|
||||||
|
} else if(config->sata_ahci) {
|
||||||
|
/* Allow both Legacy and Native mode */
|
||||||
|
pci_write_config8(dev, 0x09, 0x8f);
|
||||||
|
|
||||||
|
/* Set Interrupt Line */
|
||||||
|
/* Interrupt Pin is set by D31IP.PIP */
|
||||||
|
pci_write_config8(dev, INTR_LN, 0x0a);
|
||||||
|
|
||||||
|
/* Set timings */
|
||||||
|
pci_write_config16(dev, IDE_TIM_PRI, 0xa307);
|
||||||
|
pci_write_config16(dev, IDE_TIM_SEC, 0x8000);
|
||||||
|
|
||||||
|
/* Sync DMA */
|
||||||
|
pci_write_config16(dev, 0x48, 0x0001);
|
||||||
|
pci_write_config16(dev, 0x4a, 0x0001);
|
||||||
|
|
||||||
|
/* Set IDE I/O Configuration */
|
||||||
|
reg32 = SIG_MODE_NORMAL | FAST_PCB1 | FAST_PCB0 | PCB1 | PCB0;
|
||||||
|
pci_write_config32(dev, IDE_CONFIG, reg32);
|
||||||
|
|
||||||
|
/* Set Sata Controller Mode. */
|
||||||
|
pci_write_config8(dev, 0x90, 0xc0); // WTF - Reserved?
|
||||||
|
|
||||||
|
/* Port 0 & 1 enable */
|
||||||
|
pci_write_config8(dev, 0x92, 0x0f);
|
||||||
|
|
||||||
|
/* SATA Initialization register */
|
||||||
|
pci_write_config32(dev, 0x94, 0x1a000180);
|
||||||
|
} else {
|
||||||
|
/* Native mode capable on both primary and secondary (0xa)
|
||||||
|
* or'ed with enabled (0x50) = 0xf
|
||||||
|
*/
|
||||||
|
pci_write_config8(dev, 0x09, 0x8f);
|
||||||
|
|
||||||
|
/* Set Interrupt Line */
|
||||||
|
/* Interrupt Pin is set by D31IP.PIP */
|
||||||
|
pci_write_config8(dev, INTR_LN, 0xff);
|
||||||
|
|
||||||
|
/* Set timings */
|
||||||
|
pci_write_config16(dev, IDE_TIM_PRI, 0xa307);
|
||||||
|
pci_write_config16(dev, IDE_TIM_SEC, 0xe303);
|
||||||
|
|
||||||
|
/* Sync DMA */
|
||||||
|
pci_write_config16(dev, 0x48, 0x0005);
|
||||||
|
pci_write_config16(dev, 0x4a, 0x0201);
|
||||||
|
|
||||||
|
/* Set IDE I/O Configuration */
|
||||||
|
reg32 = SIG_MODE_NORMAL | FAST_PCB1 | FAST_PCB0 | PCB1 | PCB0;
|
||||||
|
pci_write_config32(dev, IDE_CONFIG, reg32);
|
||||||
|
|
||||||
|
/* Set Sata Controller Mode. */
|
||||||
|
pci_write_config8(dev, 0x90, 0x02);
|
||||||
|
|
||||||
|
/* Port 0 & 1 enable XXX */
|
||||||
|
pci_write_config8(dev, 0x92, 0x15);
|
||||||
|
|
||||||
|
/* SATA Initialization register */
|
||||||
|
pci_write_config32(dev, 0x94, 0x1a000180);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* All configurations need this SATA initialization sequence */
|
||||||
|
pci_write_config8(dev, 0xa0, 0x40);
|
||||||
|
pci_write_config8(dev, 0xa6, 0x22);
|
||||||
|
pci_write_config8(dev, 0xa0, 0x78);
|
||||||
|
pci_write_config8(dev, 0xa6, 0x22);
|
||||||
|
pci_write_config8(dev, 0xa0, 0x88);
|
||||||
|
reg32 = pci_read_config32(dev, 0xa4);
|
||||||
|
reg32 &= 0xc0c0c0c0;
|
||||||
|
reg32 |= 0x1b1b1212;
|
||||||
|
pci_write_config32(dev, 0xa4, reg32);
|
||||||
|
pci_write_config8(dev, 0xa0, 0x8c);
|
||||||
|
reg32 = pci_read_config32(dev, 0xa4);
|
||||||
|
reg32 &= 0xc0c0ff00;
|
||||||
|
reg32 |= 0x121200aa;
|
||||||
|
pci_write_config32(dev, 0xa4, reg32);
|
||||||
|
pci_write_config8(dev, 0xa0, 0x00);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct device_operations sata_ops = {
|
||||||
|
.read_resources = pci_dev_read_resources,
|
||||||
|
.set_resources = pci_dev_set_resources,
|
||||||
|
.enable_resources = pci_dev_enable_resources,
|
||||||
|
.init = sata_init,
|
||||||
|
.scan_bus = 0,
|
||||||
|
.enable = i82801gx_enable,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Desktop Non-AHCI and Non-RAID Mode */
|
||||||
|
static const struct pci_driver i82801gx_sata_normal_driver __pci_driver = {
|
||||||
|
.ops = &sata_ops,
|
||||||
|
.vendor = PCI_VENDOR_ID_INTEL,
|
||||||
|
.device = 0x27c0,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* NOTE: Any of the below are not properly supported yet */
|
||||||
|
|
||||||
|
/* Desktop AHCI Mode */
|
||||||
|
static const struct pci_driver i82801gx_sata_ahci_driver __pci_driver = {
|
||||||
|
.ops = &sata_ops,
|
||||||
|
.vendor = PCI_VENDOR_ID_INTEL,
|
||||||
|
.device = 0x27c1,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Desktop RAID mode */
|
||||||
|
static const struct pci_driver i82801gx_sata_raid_driver __pci_driver = {
|
||||||
|
.ops = &sata_ops,
|
||||||
|
.vendor = PCI_VENDOR_ID_INTEL,
|
||||||
|
.device = 0x27c3,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Mobile Non-AHCI and Non-RAID Mode */
|
||||||
|
static const struct pci_driver i82801gx_sata_mobile_normal_driver __pci_driver = {
|
||||||
|
.ops = &sata_ops,
|
||||||
|
.vendor = PCI_VENDOR_ID_INTEL,
|
||||||
|
.device = 0x27c4,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Mobile AHCI Mode */
|
||||||
|
static const struct pci_driver i82801gx_sata_mobile_ahci_driver __pci_driver = {
|
||||||
|
.ops = &sata_ops,
|
||||||
|
.vendor = PCI_VENDOR_ID_INTEL,
|
||||||
|
.device = 0x27c5,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ICH7M DH Raid Mode */
|
||||||
|
static const struct pci_driver i82801gx_sata_ich7dh_raid_driver __pci_driver = {
|
||||||
|
.ops = &sata_ops,
|
||||||
|
.vendor = PCI_VENDOR_ID_INTEL,
|
||||||
|
.device = 0x27c6,
|
||||||
|
};
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the coreboot project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008 coresystems GmbH
|
||||||
|
*
|
||||||
|
* 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <smbus.h>
|
||||||
|
#include <pci.h>
|
||||||
|
#include <arch/io.h>
|
||||||
|
#include "i82801gx.h"
|
||||||
|
#include "i82801_smbus.h"
|
||||||
|
|
||||||
|
static int smbus_read_byte(struct bus *bus, device_t dev, u8 address)
|
||||||
|
{
|
||||||
|
u16 device;
|
||||||
|
struct resource *res;
|
||||||
|
|
||||||
|
device = dev->path.u.i2c.device;
|
||||||
|
res = find_resource(bus->dev, 0x20);
|
||||||
|
|
||||||
|
return do_smbus_read_byte(res->base, device, address);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct smbus_bus_operations lops_smbus_bus = {
|
||||||
|
.read_byte = smbus_read_byte,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct device_operations smbus_ops = {
|
||||||
|
.read_resources = pci_dev_read_resources,
|
||||||
|
.set_resources = pci_dev_set_resources,
|
||||||
|
.enable_resources = pci_dev_enable_resources,
|
||||||
|
.init = 0,
|
||||||
|
.scan_bus = scan_static_bus,
|
||||||
|
.enable = i82801gx_enable,
|
||||||
|
.ops_smbus_bus = &lops_smbus_bus,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* 82801GB/GR/GDH/GBM/GHM/GU (ICH7/ICH7R/ICH7DH/ICH7-M/ICH7-M DH/ICH7-U) */
|
||||||
|
static const struct pci_driver i82801gx_smbus __pci_driver = {
|
||||||
|
.ops = &smbus_ops,
|
||||||
|
.vendor = PCI_VENDOR_ID_INTEL,
|
||||||
|
.device = PCI_DEVICE_ID_INTEL_82801GB_SMB,
|
||||||
|
};
|
||||||
|
|
|
@ -0,0 +1,187 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the coreboot project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2005 Yinghai Lu <yinghailu@gmail.com>
|
||||||
|
*
|
||||||
|
* 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <device/smbus_def.h>
|
||||||
|
|
||||||
|
static void smbus_delay(void)
|
||||||
|
{
|
||||||
|
inb(0x80);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int smbus_wait_until_ready(void)
|
||||||
|
{
|
||||||
|
unsigned loops = SMBUS_TIMEOUT;
|
||||||
|
unsigned char byte;
|
||||||
|
do {
|
||||||
|
smbus_delay();
|
||||||
|
if (--loops == 0)
|
||||||
|
break;
|
||||||
|
byte = inb(SMBUS_IO_BASE + SMBHSTSTAT);
|
||||||
|
} while (byte & 1);
|
||||||
|
return loops ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int smbus_wait_until_done(void)
|
||||||
|
{
|
||||||
|
unsigned loops = SMBUS_TIMEOUT;
|
||||||
|
unsigned char byte;
|
||||||
|
do {
|
||||||
|
smbus_delay();
|
||||||
|
if (--loops == 0)
|
||||||
|
break;
|
||||||
|
byte = inb(SMBUS_IO_BASE + SMBHSTSTAT);
|
||||||
|
} while ((byte & 1) || (byte & ~((1 << 6) | (1 << 0))) == 0);
|
||||||
|
return loops ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int smbus_wait_until_blk_done(void)
|
||||||
|
{
|
||||||
|
unsigned loops = SMBUS_TIMEOUT;
|
||||||
|
unsigned char byte;
|
||||||
|
do {
|
||||||
|
smbus_delay();
|
||||||
|
if (--loops == 0)
|
||||||
|
break;
|
||||||
|
byte = inb(SMBUS_IO_BASE + SMBHSTSTAT);
|
||||||
|
} while ((byte & (1 << 7)) == 0);
|
||||||
|
return loops ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_smbus_read_byte(unsigned device, unsigned address)
|
||||||
|
{
|
||||||
|
unsigned char global_status_register;
|
||||||
|
unsigned char byte;
|
||||||
|
|
||||||
|
if (smbus_wait_until_ready() < 0) {
|
||||||
|
return SMBUS_WAIT_UNTIL_READY_TIMEOUT;
|
||||||
|
}
|
||||||
|
/* Setup transaction */
|
||||||
|
/* Disable interrupts */
|
||||||
|
outb(inb(SMBUS_IO_BASE + SMBHSTCTL) & (~1), SMBUS_IO_BASE + SMBHSTCTL);
|
||||||
|
/* Set the device I'm talking too */
|
||||||
|
outb(((device & 0x7f) << 1) | 1, SMBUS_IO_BASE + SMBXMITADD);
|
||||||
|
/* Set the command/address... */
|
||||||
|
outb(address & 0xff, SMBUS_IO_BASE + SMBHSTCMD);
|
||||||
|
/* Set up for a byte data read */
|
||||||
|
outb((inb(SMBUS_IO_BASE + SMBHSTCTL) & 0xe3) | (0x2 << 2),
|
||||||
|
(SMBUS_IO_BASE + SMBHSTCTL));
|
||||||
|
/* Clear any lingering errors, so the transaction will run */
|
||||||
|
outb(inb(SMBUS_IO_BASE + SMBHSTSTAT), SMBUS_IO_BASE + SMBHSTSTAT);
|
||||||
|
|
||||||
|
/* Clear the data byte... */
|
||||||
|
outb(0, SMBUS_IO_BASE + SMBHSTDAT0);
|
||||||
|
|
||||||
|
/* Start the command */
|
||||||
|
outb((inb(SMBUS_IO_BASE + SMBHSTCTL) | 0x40),
|
||||||
|
SMBUS_IO_BASE + SMBHSTCTL);
|
||||||
|
|
||||||
|
/* Poll for transaction completion */
|
||||||
|
if (smbus_wait_until_done() < 0) {
|
||||||
|
return SMBUS_WAIT_UNTIL_DONE_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
global_status_register = inb(SMBUS_IO_BASE + SMBHSTSTAT);
|
||||||
|
|
||||||
|
/* Ignore the "In Use" status... */
|
||||||
|
global_status_register &= ~(3 << 5);
|
||||||
|
|
||||||
|
/* Read results of transaction */
|
||||||
|
byte = inb(SMBUS_IO_BASE + SMBHSTDAT0);
|
||||||
|
if (global_status_register != (1 << 1)) {
|
||||||
|
return SMBUS_ERROR;
|
||||||
|
}
|
||||||
|
return byte;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This function is neither used nor tested by me (Corey Osgood), the author
|
||||||
|
(Yinghai) probably tested/used it on i82801er */
|
||||||
|
static int do_smbus_write_block(unsigned device, unsigned length, unsigned cmd,
|
||||||
|
unsigned data1, unsigned data2)
|
||||||
|
{
|
||||||
|
unsigned char byte;
|
||||||
|
unsigned char stat;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
#if CONFIG_USE_PRINTK_IN_CAR
|
||||||
|
printk_err("Untested smbus_write_block called\r\n");
|
||||||
|
#else
|
||||||
|
print_err("Untested smbus_write_block called\r\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Clear the PM timeout flags, SECOND_TO_STS */
|
||||||
|
outw(inw(0x0400 + 0x66), 0x0400 + 0x66);
|
||||||
|
|
||||||
|
if (smbus_wait_until_ready() < 0) {
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Setup transaction */
|
||||||
|
/* Obtain ownership */
|
||||||
|
outb(inb(SMBUS_IO_BASE + SMBHSTSTAT), SMBUS_IO_BASE + SMBHSTSTAT);
|
||||||
|
for (stat = 0; (stat & 0x40) == 0;) {
|
||||||
|
stat = inb(SMBUS_IO_BASE + SMBHSTSTAT);
|
||||||
|
}
|
||||||
|
/* Clear the done bit */
|
||||||
|
outb(0x80, SMBUS_IO_BASE + SMBHSTSTAT);
|
||||||
|
/* Disable interrupts */
|
||||||
|
outb(inb(SMBUS_IO_BASE + SMBHSTCTL) & (~1), SMBUS_IO_BASE + SMBHSTCTL);
|
||||||
|
|
||||||
|
/* Set the device I'm talking too */
|
||||||
|
outb(((device & 0x7f) << 1), SMBUS_IO_BASE + SMBXMITADD);
|
||||||
|
|
||||||
|
/* Set the command address */
|
||||||
|
outb(cmd & 0xff, SMBUS_IO_BASE + SMBHSTCMD);
|
||||||
|
|
||||||
|
/* Set the block length */
|
||||||
|
outb(length & 0xff, SMBUS_IO_BASE + SMBHSTDAT0);
|
||||||
|
|
||||||
|
/* Try sending out the first byte of data here */
|
||||||
|
byte = (data1 >> (0)) & 0x0ff;
|
||||||
|
outb(byte, SMBUS_IO_BASE + SMBBLKDAT);
|
||||||
|
/* Issue a block write command */
|
||||||
|
outb((inb(SMBUS_IO_BASE + SMBHSTCTL) & 0xe3) | (0x5 << 2) | 0x40,
|
||||||
|
SMBUS_IO_BASE + SMBHSTCTL);
|
||||||
|
|
||||||
|
for (i = 0; i < length; i++) {
|
||||||
|
|
||||||
|
/* Poll for transaction completion */
|
||||||
|
if (smbus_wait_until_blk_done() < 0) {
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Load the next byte */
|
||||||
|
if (i > 3)
|
||||||
|
byte = (data2 >> (i % 4)) & 0x0ff;
|
||||||
|
else
|
||||||
|
byte = (data1 >> (i)) & 0x0ff;
|
||||||
|
outb(byte, SMBUS_IO_BASE + SMBBLKDAT);
|
||||||
|
|
||||||
|
/* Clear the done bit */
|
||||||
|
outb(inb(SMBUS_IO_BASE + SMBHSTSTAT),
|
||||||
|
SMBUS_IO_BASE + SMBHSTSTAT);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if CONFIG_USE_PRINTK_IN_CAR
|
||||||
|
printk_debug("SMBUS Block complete\r\n");
|
||||||
|
#else
|
||||||
|
print_debug("SMBUS Block complete\r\n");
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,335 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the coreboot project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008 coresystems GmbH
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <device/device.h>
|
||||||
|
#include <device/pci.h>
|
||||||
|
#include <console/console.h>
|
||||||
|
#include <arch/io.h>
|
||||||
|
#include <cpu/x86/cache.h>
|
||||||
|
#include <cpu/x86/smm.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "chip.h"
|
||||||
|
|
||||||
|
// Future TODO: Move to i82801gx directory
|
||||||
|
#include "../../../northbridge/intel/i945/ich7.h"
|
||||||
|
|
||||||
|
extern unsigned char smm[];
|
||||||
|
extern unsigned int smm_len;
|
||||||
|
|
||||||
|
/* I945 */
|
||||||
|
#define SMRAM 0x9d
|
||||||
|
#define D_OPEN (1 << 6)
|
||||||
|
#define D_CLS (1 << 5)
|
||||||
|
#define D_LCK (1 << 4)
|
||||||
|
#define G_SMRAME (1 << 3)
|
||||||
|
#define C_BASE_SEG ((0 << 2) | (1 << 1) | (0 << 0))
|
||||||
|
|
||||||
|
/* ICH7 */
|
||||||
|
#define PM1_STS 0x00
|
||||||
|
#define PM1_EN 0x02
|
||||||
|
#define PM1_CNT 0x04
|
||||||
|
#define PM1_TMR 0x08
|
||||||
|
#define PROC_CNT 0x10
|
||||||
|
#define LV2 0x14
|
||||||
|
#define LV3 0x15
|
||||||
|
#define LV4 0x16
|
||||||
|
#define PM2_CNT 0x20 // mobile only
|
||||||
|
#define GPE0_STS 0x28
|
||||||
|
#define GPE0_EN 0x2c
|
||||||
|
#define SMI_EN 0x30
|
||||||
|
#define EL_SMI_EN (1 << 25) // Intel Quick Resume Technology
|
||||||
|
#define INTEL_USB2_EN (1 << 18) // Intel-Specific USB2 SMI logic
|
||||||
|
#define LEGACY_USB2_EN (1 << 17) // Legacy USB2 SMI logic
|
||||||
|
#define PERIODIC_EN (1 << 14) // SMI on PERIODIC_STS in SMI_STS
|
||||||
|
#define TCO_EN (1 << 13) // Enable TCO Logic (BIOSWE et al)
|
||||||
|
#define MCSMI_EN (1 << 11) // Trap microcontroller range access
|
||||||
|
#define BIOS_RLS (1 << 7) // asserts SCI on bit set
|
||||||
|
#define SWSMI_TMR_EN (1 << 6) // start software smi timer on bit set
|
||||||
|
#define APMC_EN (1 << 5) // Writes to APM_CNT cause SMI#
|
||||||
|
#define SLP_SMI_EN (1 << 4) // Write to SLP_EN in PM1_CNT asserts SMI#
|
||||||
|
#define LEGACY_USB_EN (1 << 3) // Legacy USB circuit SMI logic
|
||||||
|
#define BIOS_EN (1 << 2) // Assert SMI# on setting GBL_RLS bit
|
||||||
|
#define EOS (1 << 1) // End of SMI (deassert SMI#)
|
||||||
|
#define GBL_SMI_EN (1 << 0) // SMI# generation at all?
|
||||||
|
#define SMI_STS 0x34
|
||||||
|
#define ALT_GP_SMI_EN 0x38
|
||||||
|
#define ALT_GP_SMI_STS 0x3a
|
||||||
|
#define GPE_CNTL 0x42
|
||||||
|
#define DEVACT_STS 0x44
|
||||||
|
#define SS_CNT 0x50
|
||||||
|
#define C3_RES 0x54
|
||||||
|
|
||||||
|
/* While we read PMBASE dynamically in case it changed, let's
|
||||||
|
* initialize it with a sane value
|
||||||
|
*/
|
||||||
|
static u16 pmbase = DEFAULT_PMBASE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief read and clear PM1_STS
|
||||||
|
* @return PM1_STS register
|
||||||
|
*/
|
||||||
|
static u16 reset_pm1_status(void)
|
||||||
|
{
|
||||||
|
u16 reg16;
|
||||||
|
|
||||||
|
reg16 = inw(pmbase + PM1_STS);
|
||||||
|
/* set status bits are cleared by writing 1 to them */
|
||||||
|
outw(reg16, pmbase + PM1_STS);
|
||||||
|
|
||||||
|
return reg16;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_pm1_status(u16 pm1_sts)
|
||||||
|
{
|
||||||
|
printk_debug("PM1_STS: ");
|
||||||
|
if (pm1_sts & (1 << 15)) printk_debug("WAK ");
|
||||||
|
if (pm1_sts & (1 << 14)) printk_debug("PCIEXPWAK ");
|
||||||
|
if (pm1_sts & (1 << 11)) printk_debug("PRBTNOR ");
|
||||||
|
if (pm1_sts & (1 << 10)) printk_debug("RTC ");
|
||||||
|
if (pm1_sts & (1 << 8)) printk_debug("PWRBTN ");
|
||||||
|
if (pm1_sts & (1 << 5)) printk_debug("GBL ");
|
||||||
|
if (pm1_sts & (1 << 4)) printk_debug("BM ");
|
||||||
|
if (pm1_sts & (1 << 0)) printk_debug("TMROF ");
|
||||||
|
printk_debug("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief read and clear SMI_STS
|
||||||
|
* @return SMI_STS register
|
||||||
|
*/
|
||||||
|
static u32 reset_smi_status(void)
|
||||||
|
{
|
||||||
|
u32 reg32;
|
||||||
|
|
||||||
|
reg32 = inl(pmbase + SMI_STS);
|
||||||
|
/* set status bits are cleared by writing 1 to them */
|
||||||
|
outl(reg32, pmbase + SMI_STS);
|
||||||
|
|
||||||
|
return reg32;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_smi_status(u32 smi_sts)
|
||||||
|
{
|
||||||
|
printk_debug("SMI_STS: ");
|
||||||
|
if (smi_sts & (1 << 26)) printk_debug("SPI ");
|
||||||
|
if (smi_sts & (1 << 25)) printk_debug("EL_SMI ");
|
||||||
|
if (smi_sts & (1 << 21)) printk_debug("MONITOR ");
|
||||||
|
if (smi_sts & (1 << 20)) printk_debug("PCI_EXP_SMI ");
|
||||||
|
if (smi_sts & (1 << 18)) printk_debug("INTEL_USB2 ");
|
||||||
|
if (smi_sts & (1 << 17)) printk_debug("LEGACY_USB2 ");
|
||||||
|
if (smi_sts & (1 << 16)) printk_debug("SMBUS_SMI ");
|
||||||
|
if (smi_sts & (1 << 15)) printk_debug("SERIRQ_SMI ");
|
||||||
|
if (smi_sts & (1 << 14)) printk_debug("PERIODIC ");
|
||||||
|
if (smi_sts & (1 << 13)) printk_debug("TCO ");
|
||||||
|
if (smi_sts & (1 << 12)) printk_debug("DEVMON ");
|
||||||
|
if (smi_sts & (1 << 11)) printk_debug("MCSMI ");
|
||||||
|
if (smi_sts & (1 << 10)) printk_debug("GPI ");
|
||||||
|
if (smi_sts & (1 << 9)) printk_debug("GPE0 ");
|
||||||
|
if (smi_sts & (1 << 8)) printk_debug("PM1 ");
|
||||||
|
if (smi_sts & (1 << 6)) printk_debug("SWSMI_TMR ");
|
||||||
|
if (smi_sts & (1 << 5)) printk_debug("APM ");
|
||||||
|
if (smi_sts & (1 << 4)) printk_debug("SLP_SMI ");
|
||||||
|
if (smi_sts & (1 << 3)) printk_debug("LEGACY_USB ");
|
||||||
|
if (smi_sts & (1 << 2)) printk_debug("BIOS ");
|
||||||
|
printk_debug("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief read and clear GPE0_STS
|
||||||
|
* @return GPE0_STS register
|
||||||
|
*/
|
||||||
|
static u32 reset_gpe0_status(void)
|
||||||
|
{
|
||||||
|
u32 reg32;
|
||||||
|
|
||||||
|
reg32 = inl(pmbase + GPE0_STS);
|
||||||
|
/* set status bits are cleared by writing 1 to them */
|
||||||
|
outl(reg32, pmbase + GPE0_STS);
|
||||||
|
|
||||||
|
return reg32;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_gpe0_status(u32 gpe0_sts)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
printk_debug("GPE0_STS: ");
|
||||||
|
for (i=31; i<= 16; i--) {
|
||||||
|
if (gpe0_sts & (1 << i)) printk_debug("GPIO%d ", (i-16));
|
||||||
|
}
|
||||||
|
if (gpe0_sts & (1 << 14)) printk_debug("USB4 ");
|
||||||
|
if (gpe0_sts & (1 << 13)) printk_debug("PME_B0 ");
|
||||||
|
if (gpe0_sts & (1 << 12)) printk_debug("USB3 ");
|
||||||
|
if (gpe0_sts & (1 << 11)) printk_debug("PME ");
|
||||||
|
if (gpe0_sts & (1 << 10)) printk_debug("EL_SCI/BATLOW ");
|
||||||
|
if (gpe0_sts & (1 << 9)) printk_debug("PCI_EXP ");
|
||||||
|
if (gpe0_sts & (1 << 8)) printk_debug("RI ");
|
||||||
|
if (gpe0_sts & (1 << 7)) printk_debug("SMB_WAK ");
|
||||||
|
if (gpe0_sts & (1 << 6)) printk_debug("TCO_SCI ");
|
||||||
|
if (gpe0_sts & (1 << 5)) printk_debug("AC97 ");
|
||||||
|
if (gpe0_sts & (1 << 4)) printk_debug("USB2 ");
|
||||||
|
if (gpe0_sts & (1 << 3)) printk_debug("USB1 ");
|
||||||
|
if (gpe0_sts & (1 << 2)) printk_debug("HOT_PLUG ");
|
||||||
|
if (gpe0_sts & (1 << 0)) printk_debug("THRM ");
|
||||||
|
printk_debug("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief read and clear TCOx_STS
|
||||||
|
* @return TCOx_STS registers
|
||||||
|
*/
|
||||||
|
static u32 reset_tco_status(void)
|
||||||
|
{
|
||||||
|
u32 tcobase = pmbase + 0x60;
|
||||||
|
u32 reg32;
|
||||||
|
|
||||||
|
reg32 = inl(tcobase + 0x04);
|
||||||
|
/* set status bits are cleared by writing 1 to them */
|
||||||
|
outl(reg32 & ~(1<<18), tcobase + 0x04); // Don't clear BOOT_STS before SECOND_TO_STS
|
||||||
|
if (reg32 & (1 << 18))
|
||||||
|
outl(reg32 & (1<<18), tcobase + 0x04); // clear BOOT_STS
|
||||||
|
|
||||||
|
return reg32;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void dump_tco_status(u32 tco_sts)
|
||||||
|
{
|
||||||
|
printk_debug("TCO_STS: ");
|
||||||
|
if (tco_sts & (1 << 20)) printk_debug("SMLINK_SLV ");
|
||||||
|
if (tco_sts & (1 << 18)) printk_debug("BOOT ");
|
||||||
|
if (tco_sts & (1 << 17)) printk_debug("SECOND_TO ");
|
||||||
|
if (tco_sts & (1 << 16)) printk_debug("INTRD_DET ");
|
||||||
|
if (tco_sts & (1 << 12)) printk_debug("DMISERR ");
|
||||||
|
if (tco_sts & (1 << 10)) printk_debug("DMISMI ");
|
||||||
|
if (tco_sts & (1 << 9)) printk_debug("DMISCI ");
|
||||||
|
if (tco_sts & (1 << 8)) printk_debug("BIOSWR ");
|
||||||
|
if (tco_sts & (1 << 7)) printk_debug("NEWCENTURY ");
|
||||||
|
if (tco_sts & (1 << 3)) printk_debug("TIMEOUT ");
|
||||||
|
if (tco_sts & (1 << 2)) printk_debug("TCO_INT ");
|
||||||
|
if (tco_sts & (1 << 1)) printk_debug("SW_TCO ");
|
||||||
|
if (tco_sts & (1 << 0)) printk_debug("NMI2SMI ");
|
||||||
|
printk_debug("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the EOS bit
|
||||||
|
*/
|
||||||
|
static void smi_set_eos(void)
|
||||||
|
{
|
||||||
|
u8 reg8;
|
||||||
|
|
||||||
|
reg8 = inb(pmbase + SMI_EN);
|
||||||
|
reg8 |= EOS;
|
||||||
|
outb(reg8, pmbase + SMI_EN);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern uint8_t smm_relocation_start, smm_relocation_end;
|
||||||
|
|
||||||
|
void smm_relocate(void)
|
||||||
|
{
|
||||||
|
u32 smi_en;
|
||||||
|
|
||||||
|
printk_debug("Initializing SMM handler...");
|
||||||
|
|
||||||
|
pmbase = pci_read_config16(dev_find_slot(0, PCI_DEVFN(0x1f, 0)), 0x40) & 0xfffc;
|
||||||
|
printk_spew(" ... pmbase = 0x%04x\n", pmbase);
|
||||||
|
|
||||||
|
smi_en = inl(pmbase + SMI_EN);
|
||||||
|
if (smi_en & APMC_EN) {
|
||||||
|
printk_info("SMI# handler already enabled?\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* copy the SMM relocation code */
|
||||||
|
memcpy((void *)0x38000, &smm_relocation_start,
|
||||||
|
&smm_relocation_end - &smm_relocation_start);
|
||||||
|
|
||||||
|
printk_debug("\n");
|
||||||
|
dump_smi_status(reset_smi_status());
|
||||||
|
dump_pm1_status(reset_pm1_status());
|
||||||
|
dump_gpe0_status(reset_gpe0_status());
|
||||||
|
dump_tco_status(reset_tco_status());
|
||||||
|
|
||||||
|
/* Enable SMI generation:
|
||||||
|
* - on TCO events
|
||||||
|
* - on APMC writes (io 0xb2)
|
||||||
|
* - on writes to SLP_EN (sleep states)
|
||||||
|
* - on writes to GBL_RLS (bios commands)
|
||||||
|
* No SMIs:
|
||||||
|
* - on microcontroller writes (io 0x62/0x66)
|
||||||
|
*/
|
||||||
|
outl(smi_en | (TCO_EN | APMC_EN | SLP_SMI_EN | BIOS_EN |
|
||||||
|
EOS | GBL_SMI_EN), pmbase + SMI_EN);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* There are several methods of raising a controlled SMI# via
|
||||||
|
* software, among them:
|
||||||
|
* - Writes to io 0xb2 (APMC)
|
||||||
|
* - Writes to the Local Apic ICR with Delivery mode SMI.
|
||||||
|
*
|
||||||
|
* Using the local apic is a bit more tricky. According to
|
||||||
|
* AMD Family 11 Processor BKDG no destination shorthand must be
|
||||||
|
* used.
|
||||||
|
* The whole SMM initialization is quite a bit hardware specific, so
|
||||||
|
* I'm not too worried about the better of the methods at the moment
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* raise an SMI interrupt */
|
||||||
|
printk_spew(" ... raise SMI#\n");
|
||||||
|
outb(0x00, 0xb2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void smm_install(void)
|
||||||
|
{
|
||||||
|
/* enable the SMM memory window */
|
||||||
|
pci_write_config8(dev_find_slot(0, PCI_DEVFN(0, 0)), SMRAM,
|
||||||
|
D_OPEN | G_SMRAME | C_BASE_SEG);
|
||||||
|
|
||||||
|
/* copy the real SMM handler */
|
||||||
|
memcpy((void *)0xa0000, smm, smm_len);
|
||||||
|
wbinvd();
|
||||||
|
|
||||||
|
/* close the SMM memory window and enable normal SMM */
|
||||||
|
pci_write_config8(dev_find_slot(0, PCI_DEVFN(0, 0)), SMRAM,
|
||||||
|
G_SMRAME | C_BASE_SEG);
|
||||||
|
}
|
||||||
|
|
||||||
|
void smm_init(void)
|
||||||
|
{
|
||||||
|
smm_relocate();
|
||||||
|
smm_install();
|
||||||
|
}
|
||||||
|
|
||||||
|
void smm_lock(void)
|
||||||
|
{
|
||||||
|
/* LOCK the SMM memory window and enable normal SMM.
|
||||||
|
* After running this function, only a full reset can
|
||||||
|
* make the SMM registers writable again.
|
||||||
|
*/
|
||||||
|
printk_debug("Locking SMM.\n");
|
||||||
|
pci_write_config8(dev_find_slot(0, PCI_DEVFN(0, 0)), SMRAM,
|
||||||
|
D_LCK | G_SMRAME | C_BASE_SEG);
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the coreboot project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008 coresystems GmbH
|
||||||
|
*
|
||||||
|
* 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <console/console.h>
|
||||||
|
#include <device/device.h>
|
||||||
|
#include <device/pci.h>
|
||||||
|
#include <device/pci_ids.h>
|
||||||
|
#include "i82801gx.h"
|
||||||
|
|
||||||
|
static void usb_init(struct device *dev)
|
||||||
|
{
|
||||||
|
u32 reg32;
|
||||||
|
u8 reg8;
|
||||||
|
|
||||||
|
/* USB Specification says the device must be Bus Master */
|
||||||
|
printk_debug("UHCI: Setting up controller.. ");
|
||||||
|
|
||||||
|
reg32 = pci_read_config32(dev, PCI_COMMAND);
|
||||||
|
pci_write_config32(dev, PCI_COMMAND, reg32 | PCI_COMMAND_MASTER);
|
||||||
|
|
||||||
|
reg8 = pci_read_config8(dev, 0xca);
|
||||||
|
reg8 |= (1 << 0);
|
||||||
|
pci_write_config8(dev, 0xca, reg8);
|
||||||
|
|
||||||
|
printk_debug("done.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct device_operations usb_ops = {
|
||||||
|
.read_resources = pci_dev_read_resources,
|
||||||
|
.set_resources = pci_dev_set_resources,
|
||||||
|
.enable_resources = pci_dev_enable_resources,
|
||||||
|
.init = usb_init,
|
||||||
|
.scan_bus = 0,
|
||||||
|
.enable = i82801gx_enable,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* 82801GB/GR/GDH/GBM/GHM/GU (ICH7/ICH7R/ICH7DH/ICH7-M/ICH7-M DH/ICH7-U) */
|
||||||
|
static const struct pci_driver i82801gb_usb1 __pci_driver = {
|
||||||
|
.ops = &usb_ops,
|
||||||
|
.vendor = PCI_VENDOR_ID_INTEL,
|
||||||
|
.device = PCI_DEVICE_ID_INTEL_82801GB_USB1,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct pci_driver i82801gb_usb2 __pci_driver = {
|
||||||
|
.ops = &usb_ops,
|
||||||
|
.vendor = PCI_VENDOR_ID_INTEL,
|
||||||
|
.device = PCI_DEVICE_ID_INTEL_82801GB_USB2,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct pci_driver i82801gb_usb3 __pci_driver = {
|
||||||
|
.ops = &usb_ops,
|
||||||
|
.vendor = PCI_VENDOR_ID_INTEL,
|
||||||
|
.device = PCI_DEVICE_ID_INTEL_82801GB_USB3,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct pci_driver i82801gb_usb4 __pci_driver = {
|
||||||
|
.ops = &usb_ops,
|
||||||
|
.vendor = PCI_VENDOR_ID_INTEL,
|
||||||
|
.device = PCI_DEVICE_ID_INTEL_82801GB_USB4,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the coreboot project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008 coresystems GmbH
|
||||||
|
*
|
||||||
|
* 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <console/console.h>
|
||||||
|
#include <device/device.h>
|
||||||
|
#include <device/pci.h>
|
||||||
|
#include <device/pci_ids.h>
|
||||||
|
#include "i82801gx.h"
|
||||||
|
|
||||||
|
static void usb_ehci_init(struct device *dev)
|
||||||
|
{
|
||||||
|
u32 reg32;
|
||||||
|
|
||||||
|
printk_debug("EHCI: Setting up controller.. ");
|
||||||
|
reg32 = pci_read_config32(dev, PCI_COMMAND);
|
||||||
|
pci_write_config32(dev, PCI_COMMAND, reg32 | PCI_COMMAND_MASTER);
|
||||||
|
|
||||||
|
reg32 = pci_read_config32(dev, 0xdc);
|
||||||
|
reg32 |= (1 << 31) | (1 << 27);
|
||||||
|
pci_write_config32(dev, 0xdc, reg32);
|
||||||
|
|
||||||
|
reg32 = pci_read_config32(dev, 0xfc);
|
||||||
|
reg32 &= ~(3 << 2);
|
||||||
|
reg32 |= (2 << 2) | (1 << 29) | (1 << 17);
|
||||||
|
pci_write_config32(dev, 0xfc, reg32);
|
||||||
|
|
||||||
|
printk_debug("done.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void usb_ehci_set_subsystem(device_t dev, unsigned vendor,
|
||||||
|
unsigned device)
|
||||||
|
{
|
||||||
|
u8 access_cntl;
|
||||||
|
|
||||||
|
access_cntl = pci_read_config8(dev, 0x80);
|
||||||
|
|
||||||
|
/* Enable writes to protected registers. */
|
||||||
|
pci_write_config8(dev, 0x80, access_cntl | 1);
|
||||||
|
|
||||||
|
/* Write the subsystem vendor and device ID. */
|
||||||
|
pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID,
|
||||||
|
((device & 0xffff) << 16) | (vendor & 0xffff));
|
||||||
|
|
||||||
|
/* Restore protection. */
|
||||||
|
pci_write_config8(dev, 0x80, access_cntl);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct pci_operations lops_pci = {
|
||||||
|
.set_subsystem = &usb_ehci_set_subsystem,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct device_operations usb_ehci_ops = {
|
||||||
|
.read_resources = pci_dev_read_resources,
|
||||||
|
.set_resources = pci_dev_set_resources,
|
||||||
|
.enable_resources = pci_dev_enable_resources,
|
||||||
|
.init = usb_ehci_init,
|
||||||
|
.scan_bus = 0,
|
||||||
|
.enable = i82801gx_enable,
|
||||||
|
.ops_pci = &lops_pci,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* 82801Gx */
|
||||||
|
static const struct pci_driver i82801ex_usb_ehci __pci_driver = {
|
||||||
|
.ops = &usb_ehci_ops,
|
||||||
|
.vendor = PCI_VENDOR_ID_INTEL,
|
||||||
|
.device = 0x27cc,
|
||||||
|
};
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the coreboot project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008 coresystems GmbH
|
||||||
|
*
|
||||||
|
* 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <console/console.h>
|
||||||
|
#include <arch/io.h>
|
||||||
|
#include <device/device.h>
|
||||||
|
#include <device/pci.h>
|
||||||
|
|
||||||
|
void watchdog_off(void)
|
||||||
|
{
|
||||||
|
device_t dev;
|
||||||
|
unsigned long value, base;
|
||||||
|
|
||||||
|
/* Turn off the ICH7 watchdog. */
|
||||||
|
dev = dev_find_slot(0, PCI_DEVFN(0x1f, 0));
|
||||||
|
|
||||||
|
/* Enable I/O space. */
|
||||||
|
value = pci_read_config16(dev, 0x04);
|
||||||
|
value |= (1 << 10);
|
||||||
|
pci_write_config16(dev, 0x04, value);
|
||||||
|
|
||||||
|
/* Get TCO base. */
|
||||||
|
base = (pci_read_config32(dev, 0x40) & 0x0fffe) + 0x60;
|
||||||
|
|
||||||
|
/* Disable the watchdog timer. */
|
||||||
|
value = inw(base + 0x08);
|
||||||
|
value |= 1 << 11;
|
||||||
|
outw(value, base + 0x08);
|
||||||
|
|
||||||
|
/* Clear TCO timeout status. */
|
||||||
|
outw(0x0008, base + 0x04);
|
||||||
|
outw(0x0002, base + 0x06);
|
||||||
|
|
||||||
|
printk_debug("ICH7 watchdog disabled\r\n");
|
||||||
|
}
|
|
@ -0,0 +1,551 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the coreboot project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008 coresystems GmbH
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <arch/io.h>
|
||||||
|
#include <arch/romcc_io.h>
|
||||||
|
#include <console/console.h>
|
||||||
|
#include <cpu/x86/cache.h>
|
||||||
|
#include <cpu/x86/smm.h>
|
||||||
|
#include "chip.h"
|
||||||
|
|
||||||
|
// Future TODO: Move to i82801gx directory
|
||||||
|
#include "../../../northbridge/intel/i945/ich7.h"
|
||||||
|
|
||||||
|
#define DEBUG_SMI
|
||||||
|
|
||||||
|
#define ACPI_DISABLE 0x1e
|
||||||
|
#define ACPI_ENABLE 0xe1
|
||||||
|
|
||||||
|
/* I945 */
|
||||||
|
#define SMRAM 0x9d
|
||||||
|
#define D_OPEN (1 << 6)
|
||||||
|
#define D_CLS (1 << 5)
|
||||||
|
#define D_LCK (1 << 4)
|
||||||
|
#define G_SMRANE (1 << 3)
|
||||||
|
#define C_BASE_SEG ((0 << 2) | (1 << 1) | (0 << 0))
|
||||||
|
|
||||||
|
/* ICH7 */
|
||||||
|
#define PM1_STS 0x00
|
||||||
|
#define PM1_EN 0x02
|
||||||
|
#define PM1_CNT 0x04
|
||||||
|
#define PM1_TMR 0x08
|
||||||
|
#define PROC_CNT 0x10
|
||||||
|
#define LV2 0x14
|
||||||
|
#define LV3 0x15
|
||||||
|
#define LV4 0x16
|
||||||
|
#define PM2_CNT 0x20 // mobile only
|
||||||
|
#define GPE0_STS 0x28
|
||||||
|
#define GPE0_EN 0x2c
|
||||||
|
#define SMI_EN 0x30
|
||||||
|
#define EL_SMI_EN (1 << 25) // Intel Quick Resume Technology
|
||||||
|
#define INTEL_USB2_EN (1 << 18) // Intel-Specific USB2 SMI logic
|
||||||
|
#define LEGACY_USB2_EN (1 << 17) // Legacy USB2 SMI logic
|
||||||
|
#define PERIODIC_EN (1 << 14) // SMI on PERIODIC_STS in SMI_STS
|
||||||
|
#define TCO_EN (1 << 13) // Enable TCO Logic (BIOSWE et al)
|
||||||
|
#define MCSMI_EN (1 << 11) // Trap microcontroller range access
|
||||||
|
#define BIOS_RLS (1 << 7) // asserts SCI on bit set
|
||||||
|
#define SWSMI_TMR_EN (1 << 6) // start software smi timer on bit set
|
||||||
|
#define APMC_EN (1 << 5) // Writes to APM_CNT cause SMI#
|
||||||
|
#define SLP_SMI_EN (1 << 4) // Write to SLP_EN in PM1_CNT asserts SMI#
|
||||||
|
#define LEGACY_USB_EN (1 << 3) // Legacy USB circuit SMI logic
|
||||||
|
#define BIOS_EN (1 << 2) // Assert SMI# on setting GBL_RLS bit
|
||||||
|
#define EOS (1 << 1) // End of SMI (deassert SMI#)
|
||||||
|
#define GBL_SMI_EN (1 << 0) // SMI# generation at all?
|
||||||
|
#define SMI_STS 0x34
|
||||||
|
#define ALT_GP_SMI_EN 0x38
|
||||||
|
#define ALT_GP_SMI_STS 0x3a
|
||||||
|
#define GPE_CNTL 0x42
|
||||||
|
#define DEVACT_STS 0x44
|
||||||
|
#define SS_CNT 0x50
|
||||||
|
#define C3_RES 0x54
|
||||||
|
|
||||||
|
#include "i82801gx_nvs.h"
|
||||||
|
|
||||||
|
/* While we read PMBASE dynamically in case it changed, let's
|
||||||
|
* initialize it with a sane value
|
||||||
|
*/
|
||||||
|
static u16 pmbase = DEFAULT_PMBASE;
|
||||||
|
|
||||||
|
typedef enum { SMI_LOCKED, SMI_UNLOCKED } smi_semaphore;
|
||||||
|
|
||||||
|
/* SMI multiprocessing semaphore */
|
||||||
|
static volatile smi_semaphore smi_handler_status = SMI_UNLOCKED;
|
||||||
|
|
||||||
|
static int smi_obtain_lock(void)
|
||||||
|
{
|
||||||
|
u8 ret = SMI_LOCKED;
|
||||||
|
|
||||||
|
asm volatile (
|
||||||
|
"movb %2, %%al\n"
|
||||||
|
"xchgb %%al, %1\n"
|
||||||
|
"movb %%al, %0\n"
|
||||||
|
: "=g" (ret), "=m" (smi_handler_status)
|
||||||
|
: "g" (SMI_LOCKED)
|
||||||
|
: "eax"
|
||||||
|
);
|
||||||
|
|
||||||
|
return (ret == SMI_UNLOCKED);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void smi_release_lock(void)
|
||||||
|
{
|
||||||
|
asm volatile (
|
||||||
|
"movb %1, %%al\n"
|
||||||
|
"xchgb %%al, %0\n"
|
||||||
|
: "=m" (smi_handler_status)
|
||||||
|
: "g" (SMI_UNLOCKED)
|
||||||
|
: "eax"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define LAPIC_ID 0xfee00020
|
||||||
|
static inline __attribute__((always_inline)) unsigned long nodeid(void)
|
||||||
|
{
|
||||||
|
return (*((volatile unsigned long *)(LAPIC_ID)) >> 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief read and clear PM1_STS
|
||||||
|
* @return PM1_STS register
|
||||||
|
*/
|
||||||
|
static u16 reset_pm1_status(void)
|
||||||
|
{
|
||||||
|
u16 reg16;
|
||||||
|
|
||||||
|
reg16 = inw(pmbase + PM1_STS);
|
||||||
|
/* set status bits are cleared by writing 1 to them */
|
||||||
|
outw(reg16, pmbase + PM1_STS);
|
||||||
|
|
||||||
|
return reg16;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_pm1_status(u16 pm1_sts)
|
||||||
|
{
|
||||||
|
printk_debug("PM1_STS: ");
|
||||||
|
if (pm1_sts & (1 << 15)) printk_debug("WAK ");
|
||||||
|
if (pm1_sts & (1 << 14)) printk_debug("PCIEXPWAK ");
|
||||||
|
if (pm1_sts & (1 << 11)) printk_debug("PRBTNOR ");
|
||||||
|
if (pm1_sts & (1 << 10)) printk_debug("RTC ");
|
||||||
|
if (pm1_sts & (1 << 8)) printk_debug("PWRBTN ");
|
||||||
|
if (pm1_sts & (1 << 5)) printk_debug("GBL ");
|
||||||
|
if (pm1_sts & (1 << 4)) printk_debug("BM ");
|
||||||
|
if (pm1_sts & (1 << 0)) printk_debug("TMROF ");
|
||||||
|
printk_debug("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief read and clear SMI_STS
|
||||||
|
* @return SMI_STS register
|
||||||
|
*/
|
||||||
|
static u32 reset_smi_status(void)
|
||||||
|
{
|
||||||
|
u32 reg32;
|
||||||
|
|
||||||
|
reg32 = inl(pmbase + SMI_STS);
|
||||||
|
/* set status bits are cleared by writing 1 to them */
|
||||||
|
outl(reg32, pmbase + SMI_STS);
|
||||||
|
|
||||||
|
return reg32;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_smi_status(u32 smi_sts)
|
||||||
|
{
|
||||||
|
printk_debug("SMI_STS: ");
|
||||||
|
if (smi_sts & (1 << 26)) printk_debug("SPI ");
|
||||||
|
if (smi_sts & (1 << 25)) printk_debug("EL_SMI ");
|
||||||
|
if (smi_sts & (1 << 21)) printk_debug("MONITOR ");
|
||||||
|
if (smi_sts & (1 << 20)) printk_debug("PCI_EXP_SMI ");
|
||||||
|
if (smi_sts & (1 << 18)) printk_debug("INTEL_USB2 ");
|
||||||
|
if (smi_sts & (1 << 17)) printk_debug("LEGACY_USB2 ");
|
||||||
|
if (smi_sts & (1 << 16)) printk_debug("SMBUS_SMI ");
|
||||||
|
if (smi_sts & (1 << 15)) printk_debug("SERIRQ_SMI ");
|
||||||
|
if (smi_sts & (1 << 14)) printk_debug("PERIODIC ");
|
||||||
|
if (smi_sts & (1 << 13)) printk_debug("TCO ");
|
||||||
|
if (smi_sts & (1 << 12)) printk_debug("DEVMON ");
|
||||||
|
if (smi_sts & (1 << 11)) printk_debug("MCSMI ");
|
||||||
|
if (smi_sts & (1 << 10)) printk_debug("GPI ");
|
||||||
|
if (smi_sts & (1 << 9)) printk_debug("GPE0 ");
|
||||||
|
if (smi_sts & (1 << 8)) printk_debug("PM1 ");
|
||||||
|
if (smi_sts & (1 << 6)) printk_debug("SWSMI_TMR ");
|
||||||
|
if (smi_sts & (1 << 5)) printk_debug("APM ");
|
||||||
|
if (smi_sts & (1 << 4)) printk_debug("SLP_SMI ");
|
||||||
|
if (smi_sts & (1 << 3)) printk_debug("LEGACY_USB ");
|
||||||
|
if (smi_sts & (1 << 2)) printk_debug("BIOS ");
|
||||||
|
printk_debug("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief read and clear GPE0_STS
|
||||||
|
* @return GPE0_STS register
|
||||||
|
*/
|
||||||
|
static u32 reset_gpe0_status(void)
|
||||||
|
{
|
||||||
|
u32 reg32;
|
||||||
|
|
||||||
|
reg32 = inl(pmbase + GPE0_STS);
|
||||||
|
/* set status bits are cleared by writing 1 to them */
|
||||||
|
outl(reg32, pmbase + GPE0_STS);
|
||||||
|
|
||||||
|
return reg32;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_gpe0_status(u32 gpe0_sts)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
printk_debug("GPE0_STS: ");
|
||||||
|
for (i=31; i<= 16; i--) {
|
||||||
|
if (gpe0_sts & (1 << i)) printk_debug("GPIO%d ", (i-16));
|
||||||
|
}
|
||||||
|
if (gpe0_sts & (1 << 14)) printk_debug("USB4 ");
|
||||||
|
if (gpe0_sts & (1 << 13)) printk_debug("PME_B0 ");
|
||||||
|
if (gpe0_sts & (1 << 12)) printk_debug("USB3 ");
|
||||||
|
if (gpe0_sts & (1 << 11)) printk_debug("PME ");
|
||||||
|
if (gpe0_sts & (1 << 10)) printk_debug("EL_SCI/BATLOW ");
|
||||||
|
if (gpe0_sts & (1 << 9)) printk_debug("PCI_EXP ");
|
||||||
|
if (gpe0_sts & (1 << 8)) printk_debug("RI ");
|
||||||
|
if (gpe0_sts & (1 << 7)) printk_debug("SMB_WAK ");
|
||||||
|
if (gpe0_sts & (1 << 6)) printk_debug("TCO_SCI ");
|
||||||
|
if (gpe0_sts & (1 << 5)) printk_debug("AC97 ");
|
||||||
|
if (gpe0_sts & (1 << 4)) printk_debug("USB2 ");
|
||||||
|
if (gpe0_sts & (1 << 3)) printk_debug("USB1 ");
|
||||||
|
if (gpe0_sts & (1 << 2)) printk_debug("HOT_PLUG ");
|
||||||
|
if (gpe0_sts & (1 << 0)) printk_debug("THRM ");
|
||||||
|
printk_debug("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief read and clear TCOx_STS
|
||||||
|
* @return TCOx_STS registers
|
||||||
|
*/
|
||||||
|
static u32 reset_tco_status(void)
|
||||||
|
{
|
||||||
|
u32 tcobase = pmbase + 0x60;
|
||||||
|
u32 reg32;
|
||||||
|
|
||||||
|
reg32 = inl(tcobase + 0x04);
|
||||||
|
/* set status bits are cleared by writing 1 to them */
|
||||||
|
outl(reg32 & ~(1<<18), tcobase + 0x04); // Don't clear BOOT_STS before SECOND_TO_STS
|
||||||
|
if (reg32 & (1 << 18))
|
||||||
|
outl(reg32 & (1<<18), tcobase + 0x04); // clear BOOT_STS
|
||||||
|
|
||||||
|
return reg32;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void dump_tco_status(u32 tco_sts)
|
||||||
|
{
|
||||||
|
printk_debug("TCO_STS: ");
|
||||||
|
if (tco_sts & (1 << 20)) printk_debug("SMLINK_SLV ");
|
||||||
|
if (tco_sts & (1 << 18)) printk_debug("BOOT ");
|
||||||
|
if (tco_sts & (1 << 17)) printk_debug("SECOND_TO ");
|
||||||
|
if (tco_sts & (1 << 16)) printk_debug("INTRD_DET ");
|
||||||
|
if (tco_sts & (1 << 12)) printk_debug("DMISERR ");
|
||||||
|
if (tco_sts & (1 << 10)) printk_debug("DMISMI ");
|
||||||
|
if (tco_sts & (1 << 9)) printk_debug("DMISCI ");
|
||||||
|
if (tco_sts & (1 << 8)) printk_debug("BIOSWR ");
|
||||||
|
if (tco_sts & (1 << 7)) printk_debug("NEWCENTURY ");
|
||||||
|
if (tco_sts & (1 << 3)) printk_debug("TIMEOUT ");
|
||||||
|
if (tco_sts & (1 << 2)) printk_debug("TCO_INT ");
|
||||||
|
if (tco_sts & (1 << 1)) printk_debug("SW_TCO ");
|
||||||
|
if (tco_sts & (1 << 0)) printk_debug("NMI2SMI ");
|
||||||
|
printk_debug("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ********************* smi_util ************************* */
|
||||||
|
|
||||||
|
/* Data */
|
||||||
|
#define UART_RBR 0x00
|
||||||
|
#define UART_TBR 0x00
|
||||||
|
|
||||||
|
/* Control */
|
||||||
|
#define UART_IER 0x01
|
||||||
|
#define UART_IIR 0x02
|
||||||
|
#define UART_FCR 0x02
|
||||||
|
#define UART_LCR 0x03
|
||||||
|
#define UART_MCR 0x04
|
||||||
|
#define UART_DLL 0x00
|
||||||
|
#define UART_DLM 0x01
|
||||||
|
|
||||||
|
/* Status */
|
||||||
|
#define UART_LSR 0x05
|
||||||
|
#define UART_MSR 0x06
|
||||||
|
#define UART_SCR 0x07
|
||||||
|
|
||||||
|
static int uart_can_tx_byte(void)
|
||||||
|
{
|
||||||
|
return inb(TTYS0_BASE + UART_LSR) & 0x20;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void uart_wait_to_tx_byte(void)
|
||||||
|
{
|
||||||
|
while(!uart_can_tx_byte())
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void uart_wait_until_sent(void)
|
||||||
|
{
|
||||||
|
while(!(inb(TTYS0_BASE + UART_LSR) & 0x40))
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void uart_tx_byte(unsigned char data)
|
||||||
|
{
|
||||||
|
uart_wait_to_tx_byte();
|
||||||
|
outb(data, TTYS0_BASE + UART_TBR);
|
||||||
|
/* Make certain the data clears the fifos */
|
||||||
|
uart_wait_until_sent();
|
||||||
|
}
|
||||||
|
|
||||||
|
void console_tx_flush(void)
|
||||||
|
{
|
||||||
|
uart_wait_to_tx_byte();
|
||||||
|
}
|
||||||
|
|
||||||
|
void console_tx_byte(unsigned char byte)
|
||||||
|
{
|
||||||
|
if (byte == '\n')
|
||||||
|
uart_tx_byte('\r');
|
||||||
|
uart_tx_byte(byte);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We are using PCIe accesses for now
|
||||||
|
* 1. the chipset can do it
|
||||||
|
* 2. we don't need to worry about how we leave 0xcf8/0xcfc behind
|
||||||
|
*/
|
||||||
|
#include "../../../northbridge/intel/i945/pcie_config.c"
|
||||||
|
|
||||||
|
/* ********************* smi_util ************************* */
|
||||||
|
|
||||||
|
|
||||||
|
void io_trap_handler(int smif)
|
||||||
|
{
|
||||||
|
u8 reg8;
|
||||||
|
global_nvs_t *gnvs = (global_nvs_t *)0xc00;
|
||||||
|
|
||||||
|
printk_debug("SMI function trap 0x%x: ", smif);
|
||||||
|
|
||||||
|
|
||||||
|
switch (smif) {
|
||||||
|
case 0x32:
|
||||||
|
printk_debug("OS Init\n");
|
||||||
|
break;
|
||||||
|
case 0xd6:
|
||||||
|
printk_debug("Get Brightness\n");
|
||||||
|
outb(0x17, 0x66);
|
||||||
|
reg8 = inb(0x62);
|
||||||
|
gnvs->brtl = reg8;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printk_debug("Unknown function\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* On success, the IO Trap Handler returns 0
|
||||||
|
* On failure, the IO Trap Handler returns a value != 0
|
||||||
|
*
|
||||||
|
* For now, we force the return value to 0 and log all traps to
|
||||||
|
* see what's going on.
|
||||||
|
*/
|
||||||
|
//gnvs->smif = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the EOS bit
|
||||||
|
*/
|
||||||
|
static void smi_set_eos(void)
|
||||||
|
{
|
||||||
|
u8 reg8;
|
||||||
|
|
||||||
|
reg8 = inb(pmbase + SMI_EN);
|
||||||
|
reg8 |= EOS;
|
||||||
|
outb(reg8, pmbase + SMI_EN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Interrupt handler for SMI#
|
||||||
|
*
|
||||||
|
* @param smm_revision revision of the smm state save map
|
||||||
|
*/
|
||||||
|
|
||||||
|
void smi_handler(u32 smm_revision)
|
||||||
|
{
|
||||||
|
u8 reg8;
|
||||||
|
u16 pmctrl;
|
||||||
|
u16 pm1_sts;
|
||||||
|
u32 smi_sts, gpe0_sts, tco_sts;
|
||||||
|
unsigned int node;
|
||||||
|
smm_state_save_area_t state_save;
|
||||||
|
|
||||||
|
/* Are we ok to execute the handler? */
|
||||||
|
if (!smi_obtain_lock())
|
||||||
|
return;
|
||||||
|
|
||||||
|
node=nodeid();
|
||||||
|
|
||||||
|
#ifdef DEBUG_SMI
|
||||||
|
console_loglevel = DEFAULT_CONSOLE_LOGLEVEL;
|
||||||
|
#else
|
||||||
|
console_loglevel = 1;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
printk_debug("\nSMI# #%d\n", node);
|
||||||
|
|
||||||
|
switch (smm_revision) {
|
||||||
|
case 0x00030007:
|
||||||
|
state_save.type = LEGACY;
|
||||||
|
state_save.legacy_state_save = (legacy_smm_state_save_area_t *)
|
||||||
|
(0xa8000 + 0x7e00 - (node * 0x400));
|
||||||
|
break;
|
||||||
|
case 0x00030100:
|
||||||
|
state_save.type = EM64T;
|
||||||
|
state_save.em64t_state_save = (em64t_smm_state_save_area_t *)
|
||||||
|
(0xa8000 + 0x7d00 - (node * 0x400));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printk_debug("smm_revision: 0x%08x\n", smm_revision);
|
||||||
|
printk_debug("SMI# not supported on your CPU\n");
|
||||||
|
/* Don't release lock, so no further SMI will happen,
|
||||||
|
* if we don't handle it anyways.
|
||||||
|
*/
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pmbase = pcie_read_config16(PCI_DEV(0, 0x1f, 0), 0x40) & 0xfffc;
|
||||||
|
printk_spew("SMI#: pmbase = 0x%04x\n", pmbase);
|
||||||
|
|
||||||
|
/* We need to clear the SMI status registers, or we won't see what's
|
||||||
|
* happening in the following calls.
|
||||||
|
*/
|
||||||
|
smi_sts = reset_smi_status();
|
||||||
|
dump_smi_status(smi_sts);
|
||||||
|
|
||||||
|
if (smi_sts & (1 << 21)) { // MONITOR
|
||||||
|
global_nvs_t *gnvs = (global_nvs_t *)0xc00;
|
||||||
|
int i;
|
||||||
|
u32 reg32;
|
||||||
|
|
||||||
|
reg32 = RCBA32(0x1e00); // TRSR - Trap Status Register
|
||||||
|
#if 0
|
||||||
|
/* Comment in for some useful debug */
|
||||||
|
for (i=0; i<4; i++) {
|
||||||
|
if (reg32 & (1 << i)) {
|
||||||
|
printk_debug(" io trap #%d\n", i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
RCBA32(0x1e00) = reg32; // TRSR
|
||||||
|
|
||||||
|
reg32 = RCBA32(0x1e10);
|
||||||
|
|
||||||
|
if ((reg32 & 0xfffc) != 0x808) {
|
||||||
|
printk_debug(" trapped io address = 0x%x\n", reg32 & 0xfffc);
|
||||||
|
printk_debug(" AHBE = %x\n", (reg32 >> 16) & 0xf);
|
||||||
|
printk_debug(" read/write: %s\n", (reg32 & (1 << 24)) ? "read" :
|
||||||
|
"write");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(reg32 & (1 << 24))) {
|
||||||
|
/* Write Cycle */
|
||||||
|
reg32 = RCBA32(0x1e18);
|
||||||
|
printk_debug(" iotrap written data = 0x%08x\n", reg32);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gnvs->smif)
|
||||||
|
io_trap_handler(gnvs->smif); // call function smif
|
||||||
|
}
|
||||||
|
|
||||||
|
if (smi_sts & (1 << 13)) { // TCO
|
||||||
|
tco_sts = reset_tco_status();
|
||||||
|
dump_tco_status(tco_sts);
|
||||||
|
|
||||||
|
if (tco_sts & (1 << 8)) { // BIOSWR
|
||||||
|
u8 bios_cntl;
|
||||||
|
|
||||||
|
bios_cntl = pcie_read_config16(PCI_DEV(0, 0x1f, 0), 0xdc);
|
||||||
|
|
||||||
|
if (bios_cntl & 1) {
|
||||||
|
/* BWE is RW, so the SMI was caused by a
|
||||||
|
* write to BWE, not by a write to the BIOS
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* This is the place where we notice someone
|
||||||
|
* is trying to tinker with the BIOS. We are
|
||||||
|
* trying to be nice and just ignore it. A more
|
||||||
|
* resolute answer would be to power down the
|
||||||
|
* box.
|
||||||
|
*/
|
||||||
|
printk_debug("Switching back to RO\n");
|
||||||
|
pcie_write_config32(PCI_DEV(0, 0x1f, 0), 0xdc, (bios_cntl & ~1));
|
||||||
|
} /* No else for now? */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (smi_sts & (1 << 8)) { // PM1
|
||||||
|
pm1_sts = reset_pm1_status();
|
||||||
|
dump_pm1_status(pm1_sts);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (smi_sts & (1 << 9)) { // GPE0
|
||||||
|
gpe0_sts = reset_gpe0_status();
|
||||||
|
dump_gpe0_status(gpe0_sts);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (smi_sts & (1 << 5)) { // APM
|
||||||
|
/* Emulate B2 register as the FADT / Linux expects it */
|
||||||
|
|
||||||
|
reg8 = inb(0xb2);
|
||||||
|
switch (reg8) {
|
||||||
|
case ACPI_DISABLE:
|
||||||
|
pmctrl = inw(pmbase + 0x04);
|
||||||
|
pmctrl |= (1 << 0);
|
||||||
|
outw(pmctrl, pmbase + 0x04);
|
||||||
|
printk_debug("SMI#: ACPI disabled.\n");
|
||||||
|
break;
|
||||||
|
case ACPI_ENABLE:
|
||||||
|
pmctrl = inw(pmbase + 0x04);
|
||||||
|
pmctrl &= ~(1 << 0);
|
||||||
|
outw(pmctrl, pmbase + 0x04);
|
||||||
|
printk_debug("SMI#: ACPI enabled.\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (smi_sts & (1 << 4)) { // SLP_SMI
|
||||||
|
u32 reg32;
|
||||||
|
reg32 = inl(pmbase + 0x04);
|
||||||
|
printk_debug("SMI#: SLP = 0x%08x\n");
|
||||||
|
printk_debug("SMI#: Powering off.\n");
|
||||||
|
outl((6 << 10), pmbase + 0x04);
|
||||||
|
outl((1 << 13) | (6 << 10), pmbase + 0x04);
|
||||||
|
printk_debug("....\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
smi_release_lock();
|
||||||
|
|
||||||
|
/* De-assert SMI# signal to allow another SMI */
|
||||||
|
smi_set_eos();
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
|
||||||
|
/* Maximum number of CPUs/cores */
|
||||||
|
CPUS = 4;
|
||||||
|
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
/* This is the actual SMM handler.
|
||||||
|
*
|
||||||
|
* We just put code, rodata, data and bss all in a row.
|
||||||
|
*/
|
||||||
|
. = 0xa0000;
|
||||||
|
.handler (.): {
|
||||||
|
/* Assembler stub */
|
||||||
|
*(.handler)
|
||||||
|
|
||||||
|
/* C code of the SMM handler */
|
||||||
|
*(.text);
|
||||||
|
*(.text.*);
|
||||||
|
|
||||||
|
/* C read-only data of the SMM handler */
|
||||||
|
. = ALIGN(16);
|
||||||
|
*(.rodata)
|
||||||
|
*(.rodata.*)
|
||||||
|
|
||||||
|
/* C read-write data of the SMM handler */
|
||||||
|
. = ALIGN(4);
|
||||||
|
*(.data)
|
||||||
|
|
||||||
|
/* C uninitialized data of the SMM handler */
|
||||||
|
. = ALIGN(4);
|
||||||
|
*(.bss)
|
||||||
|
*(.sbss)
|
||||||
|
|
||||||
|
/* What is this? */
|
||||||
|
*(COMMON)
|
||||||
|
. = ALIGN(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We are using the ASEG interleaved to stuff the SMM handlers
|
||||||
|
* for all CPU cores in there. The jump table redirects the execution
|
||||||
|
* to the actual SMM handler
|
||||||
|
*/
|
||||||
|
. = 0xa8000 - (( CPUS - 1) * 0x400);
|
||||||
|
.jumptable : {
|
||||||
|
*(.jumptable)
|
||||||
|
}
|
||||||
|
|
||||||
|
/DISCARD/ : {
|
||||||
|
*(.comment)
|
||||||
|
*(.note)
|
||||||
|
*(.note.*)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,201 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the coreboot project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008 coresystems GmbH
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* NOTE: This handler assumes the SMM window goes from 0xa0000
|
||||||
|
* to 0xaffff. In fact, at least on Intel Core CPUs (i945 chipset)
|
||||||
|
* the SMM window is 128K big, covering 0xa0000 to 0xbffff.
|
||||||
|
* So there is a lot of potential for growth in here. Let's stick
|
||||||
|
* to 64k if we can though.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* +--------------------------------+ 0xaffff
|
||||||
|
* | Save State Map Node 0 |
|
||||||
|
* | Save State Map Node 1 |
|
||||||
|
* | Save State Map Node 2 |
|
||||||
|
* | Save State Map Node 3 |
|
||||||
|
* | ... |
|
||||||
|
* +--------------------------------+ 0xaf000
|
||||||
|
* | |
|
||||||
|
* | |
|
||||||
|
* | |
|
||||||
|
* +--------------------------------+ 0xa8400
|
||||||
|
* | SMM Entry Node 0 (+ stack) |
|
||||||
|
* +--------------------------------+ 0xa8000
|
||||||
|
* | SMM Entry Node 1 (+ stack) |
|
||||||
|
* | SMM Entry Node 2 (+ stack) |
|
||||||
|
* | SMM Entry Node 3 (+ stack) |
|
||||||
|
* | ... |
|
||||||
|
* +--------------------------------+ 0xa7400
|
||||||
|
* | |
|
||||||
|
* | SMM Handler |
|
||||||
|
* | |
|
||||||
|
* +--------------------------------+ 0xa0000
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <arch/asm.h>
|
||||||
|
|
||||||
|
#define LAPIC_ID 0xfee00020
|
||||||
|
|
||||||
|
/* SMM_HANDLER_OFFSET is the 16bit offset within the ASEG
|
||||||
|
* at which smm_handler_start lives. At the moment the handler
|
||||||
|
* lives right at 0xa0000, so the offset is 0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define SMM_HANDLER_OFFSET 0x0000
|
||||||
|
|
||||||
|
/* initially SMM is some sort of real mode. Let gcc know
|
||||||
|
* how to treat the SMM handler stub
|
||||||
|
*/
|
||||||
|
|
||||||
|
.section ".handler", "a", @progbits
|
||||||
|
|
||||||
|
.code16
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SMM code to enable protected mode and jump to the
|
||||||
|
* C-written function void smi_handler(u32 smm_revision)
|
||||||
|
*
|
||||||
|
* All the bad magic is not all that bad after all.
|
||||||
|
*/
|
||||||
|
smm_handler_start:
|
||||||
|
movw $(smm_gdtptr16 - smm_handler_start + SMM_HANDLER_OFFSET), %bx
|
||||||
|
data32 lgdt %cs:(%bx)
|
||||||
|
|
||||||
|
movl %cr0, %eax
|
||||||
|
andl $0x7FFAFFD1, %eax /* PG,AM,WP,NE,TS,EM,MP = 0 */
|
||||||
|
orl $0x60000001, %eax /* CD, NW, PE = 1 */
|
||||||
|
movl %eax, %cr0
|
||||||
|
|
||||||
|
/* Enable protected mode */
|
||||||
|
data32 ljmp $0x08, $1f
|
||||||
|
|
||||||
|
.code32
|
||||||
|
1:
|
||||||
|
/* Use flat data segment */
|
||||||
|
movw $0x10, %ax
|
||||||
|
movw %ax, %ds
|
||||||
|
movw %ax, %es
|
||||||
|
movw %ax, %ss
|
||||||
|
movw %ax, %fs
|
||||||
|
movw %ax, %gs
|
||||||
|
|
||||||
|
/* Get this CPU's LAPIC ID */
|
||||||
|
movl $LAPIC_ID, %esi
|
||||||
|
movl (%esi), %ecx
|
||||||
|
shr $24, %ecx
|
||||||
|
|
||||||
|
/* calculate stack offset by multiplying the APIC ID
|
||||||
|
* by 1024 (0x400), and save that offset in ebp.
|
||||||
|
*/
|
||||||
|
shl $10, %ecx
|
||||||
|
movl %ecx, %ebp
|
||||||
|
|
||||||
|
/* We put the stack for each core right above
|
||||||
|
* its SMM entry point. Core 0 starts at 0xa8000,
|
||||||
|
* we spare 0x10 bytes for the jump to be sure.
|
||||||
|
*/
|
||||||
|
movl $0xa8010, %eax
|
||||||
|
subl %ecx, %eax /* subtract offset, see above */
|
||||||
|
movl %eax, %ebx /* Save bottom of stack in ebx */
|
||||||
|
|
||||||
|
#define SMM_STACK_SIZE (0x400 - 0x10)
|
||||||
|
/* clear stack */
|
||||||
|
cld
|
||||||
|
movl %eax, %edi
|
||||||
|
movl $(SMM_STACK_SIZE >> 2), %ecx
|
||||||
|
xorl %eax, %eax
|
||||||
|
rep stosl
|
||||||
|
|
||||||
|
/* set new stack */
|
||||||
|
addl $SMM_STACK_SIZE, %ebx
|
||||||
|
movl %ebx, %esp
|
||||||
|
|
||||||
|
/* Get SMM revision */
|
||||||
|
movl $0xa8000 + 0x7efc, %ebx /* core 0 address */
|
||||||
|
subl %ebp, %ebx /* subtract core X offset */
|
||||||
|
movl (%ebx), %eax
|
||||||
|
pushl %eax
|
||||||
|
|
||||||
|
/* Call 32bit C handler */
|
||||||
|
call smi_handler
|
||||||
|
|
||||||
|
/* To return, just do rsm. It will "clean up" protected mode */
|
||||||
|
rsm
|
||||||
|
|
||||||
|
.code16
|
||||||
|
|
||||||
|
.align 4, 0xff
|
||||||
|
|
||||||
|
smm_gdtptr16:
|
||||||
|
.word smm_gdt_end - smm_gdt - 1
|
||||||
|
.long smm_gdt - smm_handler_start + 0xa0000 + SMM_HANDLER_OFFSET
|
||||||
|
|
||||||
|
.code32
|
||||||
|
|
||||||
|
smm_gdt:
|
||||||
|
/* The first GDT entry can not be used. Keep it zero */
|
||||||
|
.long 0x00000000, 0x00000000
|
||||||
|
|
||||||
|
/* gdt selector 0x08, flat code segment */
|
||||||
|
.word 0xffff, 0x0000
|
||||||
|
.byte 0x00, 0x9b, 0xcf, 0x00 /* G=1 and 0x0f, 4GB limit */
|
||||||
|
|
||||||
|
/* gdt selector 0x10, flat data segment */
|
||||||
|
.word 0xffff, 0x0000
|
||||||
|
.byte 0x00, 0x93, 0xcf, 0x00
|
||||||
|
|
||||||
|
smm_gdt_end:
|
||||||
|
|
||||||
|
|
||||||
|
.section ".jumptable", "a", @progbits
|
||||||
|
|
||||||
|
/* This is the SMM jump table. All cores use the same SMM handler
|
||||||
|
* for simplicity. But SMM Entry needs to be different due to the
|
||||||
|
* save state area. The jump table makes sure all CPUs jump into the
|
||||||
|
* real handler on SMM entry.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* This code currently supports up to 4 CPU cores. If more than 4 CPU cores
|
||||||
|
* shall be used, below table has to be updated, as well as smm.ld
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* GNU AS/LD will always generate code that assumes CS is 0xa000. In reality
|
||||||
|
* CS will be set to SMM_BASE[19:4] though. Knowing that the smm handler is the
|
||||||
|
* first thing in the ASEG, we do a far jump here, to set CS to 0xa000.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.code16
|
||||||
|
jumptable:
|
||||||
|
/* core 3 */
|
||||||
|
ljmp $0xa000, $SMM_HANDLER_OFFSET
|
||||||
|
.align 1024, 0x00
|
||||||
|
/* core 2 */
|
||||||
|
ljmp $0xa000, $SMM_HANDLER_OFFSET
|
||||||
|
.align 1024, 0x00
|
||||||
|
/* core 1 */
|
||||||
|
ljmp $0xa000, $SMM_HANDLER_OFFSET
|
||||||
|
.align 1024, 0x00
|
||||||
|
/* core 0 */
|
||||||
|
ljmp $0xa000, $SMM_HANDLER_OFFSET
|
||||||
|
.align 1024, 0x00
|
||||||
|
|
|
@ -0,0 +1,168 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the coreboot project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008 coresystems GmbH
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <arch/asm.h>
|
||||||
|
#include "../../../../src/northbridge/intel/i945/ich7.h"
|
||||||
|
|
||||||
|
#undef DEBUG_SMM_RELOCATION
|
||||||
|
//#define DEBUG_SMM_RELOCATION
|
||||||
|
|
||||||
|
#define LAPIC_ID 0xfee00020
|
||||||
|
|
||||||
|
.global smm_relocation_start
|
||||||
|
.global smm_relocation_end
|
||||||
|
|
||||||
|
/* initially SMM is some sort of real mode. */
|
||||||
|
.code16
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This trampoline code relocates SMBASE to 0xa0000 - ( lapicid * 0x400 )
|
||||||
|
*
|
||||||
|
* Why 0x400? It is a safe value to cover the save state area per CPU. On
|
||||||
|
* current AMD CPUs this area is _documented_ to be 0x200 bytes. On Intel
|
||||||
|
* Core 2 CPUs the _documented_ parts of the save state area is 48 bytes
|
||||||
|
* bigger, effectively sizing our data structures 0x300 bytes.
|
||||||
|
*
|
||||||
|
* LAPICID SMBASE SMM Entry SAVE STATE
|
||||||
|
* 0 0xa0000 0xa8000 0xafd00
|
||||||
|
* 1 0x9fc00 0xa7c00 0xaf900
|
||||||
|
* 2 0x9f800 0xa7800 0xaf500
|
||||||
|
* 3 0x9f400 0xa7400 0xaf100
|
||||||
|
* 4 0x9f000 0xa7000 0xaed00
|
||||||
|
* 5 0x9ec00 0xa6c00 0xae900
|
||||||
|
* 6 0x9e800 0xa6800 0xae500
|
||||||
|
* 7 0x9e400 0xa6400 0xae100
|
||||||
|
* 8 0x9e000 0xa6000 0xadd00
|
||||||
|
* 9 0x9dc00 0xa5c00 0xad900
|
||||||
|
* 10 0x9d800 0xa5800 0xad500
|
||||||
|
* 11 0x9d400 0xa5400 0xad100
|
||||||
|
* 12 0x9d000 0xa5000 0xacd00
|
||||||
|
* 13 0x9cc00 0xa4c00 0xac900
|
||||||
|
* 14 0x9c800 0xa4800 0xac500
|
||||||
|
* 15 0x9c400 0xa4400 0xac100
|
||||||
|
* . . . .
|
||||||
|
* . . . .
|
||||||
|
* . . . .
|
||||||
|
* 31 0x98400 0xa0400 0xa8100
|
||||||
|
*
|
||||||
|
* With 32 cores, the SMM handler would need to fit between
|
||||||
|
* 0xa0000-0xa0400 and the stub plus stack would need to go
|
||||||
|
* at 0xa8000-0xa8100 (example for core 0). That is not enough.
|
||||||
|
*
|
||||||
|
* This means we're basically limited to 16 cpu cores before
|
||||||
|
* we need to use the TSEG/HSEG for the actual SMM handler plus stack.
|
||||||
|
* When we exceed 32 cores, we also need to put SMBASE to TSEG/HSEG.
|
||||||
|
*
|
||||||
|
* If we figure out the documented values above are safe to use,
|
||||||
|
* we could pack the structure above even more, so we could use the
|
||||||
|
* scheme to pack save state areas for 63 AMD CPUs or 58 Intel CPUs
|
||||||
|
* in the ASEG.
|
||||||
|
*
|
||||||
|
* Note: Some versions of Pentium M need their SMBASE aligned to 32k.
|
||||||
|
* On those the above only works for up to 2 cores. But for now we only
|
||||||
|
* care fore Core (2) Duo/Solo
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
smm_relocation_start:
|
||||||
|
/* Check revision to see if AMD64 style SMM_BASE
|
||||||
|
* Intel Core Solo/Duo: 0x30007
|
||||||
|
* Intel Core2 Solo/Duo: 0x30100
|
||||||
|
* AMD64: 0x3XX64
|
||||||
|
* This check does not make much sense, unless someone ports
|
||||||
|
* SMI handling to AMD64 CPUs.
|
||||||
|
*/
|
||||||
|
|
||||||
|
mov $0x38000 + 0x7efc, %ebx
|
||||||
|
addr32 mov (%ebx), %al
|
||||||
|
cmp $0x64, %al
|
||||||
|
je 1f
|
||||||
|
|
||||||
|
mov $0x38000 + 0x7ef8, %ebx
|
||||||
|
jmp smm_relocate
|
||||||
|
1:
|
||||||
|
mov $0x38000 + 0x7f00, %ebx
|
||||||
|
|
||||||
|
smm_relocate:
|
||||||
|
/* Get this CPU's LAPIC ID */
|
||||||
|
movl $LAPIC_ID, %esi
|
||||||
|
addr32 movl (%esi), %ecx
|
||||||
|
shr $24, %ecx
|
||||||
|
|
||||||
|
/* calculate offset by multiplying the
|
||||||
|
* apic ID by 1024 (0x400)
|
||||||
|
*/
|
||||||
|
movl %ecx, %edx
|
||||||
|
shl $10, %edx
|
||||||
|
|
||||||
|
movl $0xa0000, %eax
|
||||||
|
subl %edx, %eax /* subtract offset, see above */
|
||||||
|
|
||||||
|
addr32 movl %eax, (%ebx)
|
||||||
|
|
||||||
|
|
||||||
|
/* The next section of code is hardware specific */
|
||||||
|
|
||||||
|
/* Clear SMI status */
|
||||||
|
movw $(DEFAULT_PMBASE + 0x34), %dx
|
||||||
|
inw %dx, %ax
|
||||||
|
outw %ax, %dx
|
||||||
|
|
||||||
|
/* Clear PM1 status */
|
||||||
|
movw $(DEFAULT_PMBASE + 0x00), %dx
|
||||||
|
inw %dx, %ax
|
||||||
|
outw %ax, %dx
|
||||||
|
|
||||||
|
/* Set EOS bit so other SMIs can occur */
|
||||||
|
movw $(DEFAULT_PMBASE + 0x30), %dx
|
||||||
|
inl %dx, %eax
|
||||||
|
orl $(1 << 1), %eax
|
||||||
|
outl %eax, %dx
|
||||||
|
|
||||||
|
/* End of hardware specific section. */
|
||||||
|
#ifdef DEBUG_SMM_RELOCATION
|
||||||
|
/* print [SMM-x] so we can determine if CPUx went to SMM */
|
||||||
|
movw $TTYS0_BASE, %dx
|
||||||
|
mov $'[', %al
|
||||||
|
outb %al, %dx
|
||||||
|
mov $'S', %al
|
||||||
|
outb %al, %dx
|
||||||
|
mov $'M', %al
|
||||||
|
outb %al, %dx
|
||||||
|
outb %al, %dx
|
||||||
|
movb $'-', %al
|
||||||
|
outb %al, %dx
|
||||||
|
/* calculate ascii of cpu number. More than 9 cores? -> FIXME */
|
||||||
|
movb %cl, %al
|
||||||
|
addb $'0', %al
|
||||||
|
outb %al, %dx
|
||||||
|
mov $']', %al
|
||||||
|
outb %al, %dx
|
||||||
|
mov $'\r', %al
|
||||||
|
outb %al, %dx
|
||||||
|
mov $'\n', %al
|
||||||
|
outb %al, %dx
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* That's it. return */
|
||||||
|
rsm
|
||||||
|
smm_relocation_end:
|
||||||
|
|
Loading…
Reference in New Issue