diff options
author | Stefan Reinauer <stepan@coresystems.de> | 2010-03-01 08:34:19 +0000 |
---|---|---|
committer | Stefan Reinauer <stepan@openbios.org> | 2010-03-01 08:34:19 +0000 |
commit | 800379f7aa07ca54898faa2c51e6f41ea5b228df (patch) | |
tree | 0f01be5464706eb68d9490df0463a3d9d25e2574 /src | |
parent | 75bf053fd65bd962fe7a144eb4956f47d9e43d35 (diff) | |
download | coreboot-800379f7aa07ca54898faa2c51e6f41ea5b228df.tar.xz |
This patch implements MBI (modular bios interface) support to the i830 chipset.
This is needed on the IP1000T to get VGA output. The VGA option rom will ask
through an SMI for hardware specifics (in form of a VBT, video bios table)
which the SMI handler copies into the VGA option rom.
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@5177 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1
Diffstat (limited to 'src')
30 files changed, 2120 insertions, 87 deletions
diff --git a/src/Kconfig b/src/Kconfig index dfb2d71a4c..0fae6216dd 100644 --- a/src/Kconfig +++ b/src/Kconfig @@ -447,6 +447,66 @@ config FALLBACK_VGA_BIOS_ID the "0x" prefix) and 3230 specifies the PCI device ID of the video card (also in hex, without "0x" prefix). +config INTEL_MBI + bool "Add an MBI image" + depends on NORTHBRIDGE_INTEL_I82830 + help + Select this option if you have an Intel MBI image that you would + like to add to your ROM. + + You will be able to specify the location and file name of the + image later. + +config FALLBACK_MBI_FILE + string "Intel MBI path and filename" + depends on INTEL_MBI + default "mbi.bin" + help + The path and filename of the file to use as VGA BIOS. + +endmenu + +menu "Bootsplash" + depends on PCI_OPTION_ROM_RUN_YABEL + +config BOOTSPLASH + prompt "Show graphical bootsplash" + bool + depends on PCI_OPTION_ROM_RUN_YABEL + help + This option shows a graphical bootsplash screen. The grapics are + loaded from the CBFS file bootsplash.jpg. + +config FALLBACK_BOOTSPLASH_FILE + string "Bootsplash path and filename" + depends on BOOTSPLASH + default "bootsplash.jpg" + help + The path and filename of the file to use as graphical bootsplash + screen. The file format has to be jpg. + +# TODO: Turn this into a "choice". +config FRAMEBUFFER_VESA_MODE + prompt "VESA framebuffer video mode" + hex + default 0x117 + depends on BOOTSPLASH + help + This option sets the resolution used for the coreboot framebuffer and + bootsplash screen. Set to 0x117 for 1024x768x16. A diligent soul will + some day make this a "choice". + +config COREBOOT_KEEP_FRAMEBUFFER + prompt "Keep VESA framebuffer" + bool + depends on BOOTSPLASH + help + This option keeps the framebuffer mode set after coreboot finishes + execution. If this option is enabled, coreboot will pass a + framebuffer entry in its coreboot table and the payload will need a + framebuffer driver. If this option is disabled, coreboot will switch + back to text mode before handing control to a payload. + endmenu menu "Debugging" diff --git a/src/arch/i386/Makefile.inc b/src/arch/i386/Makefile.inc index e24bd1b421..a16972ca93 100644 --- a/src/arch/i386/Makefile.inc +++ b/src/arch/i386/Makefile.inc @@ -28,6 +28,14 @@ ifeq ($(CONFIG_VGA_BIOS),y) @printf " VGABIOS $(CONFIG_FALLBACK_VGA_BIOS_FILE) $(CONFIG_FALLBACK_VGA_BIOS_ID)\n" $(CBFSTOOL) $(obj)/coreboot.rom add $(CONFIG_FALLBACK_VGA_BIOS_FILE) "pci$(CONFIG_FALLBACK_VGA_BIOS_ID).rom" optionrom endif +ifeq ($(CONFIG_INTEL_MBI),y) + @printf " MBI $(CONFIG_FALLBACK_MBI_FILE)\n" + $(CBFSTOOL) $(obj)/coreboot.rom add $(CONFIG_FALLBACK_MBI_FILE) mbi.bin mbi +endif +ifeq ($(CONFIG_BOOTSPLASH),y) + @printf " BOOTSPLASH $(CONFIG_FALLBACK_BOOTSPLASH_FILE)\n" + $(CBFSTOOL) $(obj)/coreboot.rom add $(CONFIG_FALLBACK_BOOTSPLASH_FILE) bootsplash.jpg bootsplash +endif @printf " CBFSPRINT $(subst $(obj)/,,$(@))\n\n" $(CBFSTOOL) $(obj)/coreboot.rom print diff --git a/src/devices/Kconfig b/src/devices/Kconfig index 08e532d12c..5e13e4e393 100644 --- a/src/devices/Kconfig +++ b/src/devices/Kconfig @@ -167,36 +167,6 @@ config YABEL_DIRECTHW they can still access all devices in the system. Enable this option for a good compromise between security and speed. -config BOOTSPLASH - prompt "Show graphical bootsplash" - bool - depends on PCI_OPTION_ROM_RUN_YABEL - help - This option shows a graphical bootsplash screen. The grapics are - loaded from the CBFS file bootsplash.jpg. - -# TODO: Turn this into a "choice". -config FRAMEBUFFER_VESA_MODE - prompt "VESA framebuffer video mode" - hex - default 0x117 - depends on BOOTSPLASH - help - This option sets the resolution used for the coreboot framebuffer and - bootsplash screen. Set to 0x117 for 1024x768x16. A diligent soul will - some day make this a "choice". - -config COREBOOT_KEEP_FRAMEBUFFER - prompt "Keep VESA framebuffer" - bool - depends on BOOTSPLASH - help - This option keeps the framebuffer mode set after coreboot finishes - execution. If this option is enabled, coreboot will pass a - framebuffer entry in its coreboot table and the payload will need a - framebuffer driver. If this option is disabled, coreboot will switch - back to text mode before handing control to a payload. - config CONSOLE_VGA_MULTI bool default n diff --git a/src/include/cbfs.h b/src/include/cbfs.h index 38f18a4610..d169c28799 100644 --- a/src/include/cbfs.h +++ b/src/include/cbfs.h @@ -63,9 +63,14 @@ Users are welcome to use any other value for their components */ -#define CBFS_TYPE_STAGE 0x10 -#define CBFS_TYPE_PAYLOAD 0x20 -#define CBFS_TYPE_OPTIONROM 0x30 +#define CBFS_TYPE_STAGE 0x10 +#define CBFS_TYPE_PAYLOAD 0x20 +#define CBFS_TYPE_OPTIONROM 0x30 +#define CBFS_TYPE_BOOTSPLASH 0x40 +#define CBFS_TYPE_RAW 0x50 +#define CBFS_TYPE_VSA 0x51 +#define CBFS_TYPE_MBI 0x52 +#define CBFS_TYPE_MICROCODE 0x53 /** this is the master cbfs header - it need to be located somewhere in the bootblock. Where it @@ -164,11 +169,8 @@ int cbfs_execute_stage(const char *name); void * cbfs_get_file(const char *name); void *cbfs_load_optionrom(u16 vendor, u16 device, void * dest); int run_address(void *f); -int cbfs_decompress(int algo, void *src, void *dst, int len); -struct cbfs_stage *cbfs_find_file(const char *name, int type); -int cbfs_check_magic(struct cbfs_file *file); -struct cbfs_header *cbfs_master_header(void); struct cbfs_file *cbfs_find(const char *name); +void *cbfs_find_file(const char *name, int type); void cbfs_and_run_core(const char *filename, unsigned int ebp); #endif diff --git a/src/lib/Makefile.inc b/src/lib/Makefile.inc index cbff717c32..95c10cf011 100644 --- a/src/lib/Makefile.inc +++ b/src/lib/Makefile.inc @@ -31,3 +31,5 @@ obj-$(CONFIG_BOOTSPLASH) += jpeg.o ifdef POST_EVALUATION $(obj)/lib/version.o :: $(obj)/build.h endif + +smmobj-y += memcpy.o diff --git a/src/lib/cbfs.c b/src/lib/cbfs.c index 0bb6e838fb..e694952f39 100644 --- a/src/lib/cbfs.c +++ b/src/lib/cbfs.c @@ -24,7 +24,16 @@ #include <lib.h> #include <arch/byteorder.h> -int cbfs_decompress(int algo, void *src, void *dst, int len) + +/** + * Decompression wrapper for CBFS + * @param algo + * @param src + * @param dst + * @param len + * @return 0 on success, -1 on failure + */ +static int cbfs_decompress(int algo, void *src, void *dst, int len) { switch(algo) { case CBFS_COMPRESS_NONE: @@ -44,12 +53,12 @@ int cbfs_decompress(int algo, void *src, void *dst, int len) } } -int cbfs_check_magic(struct cbfs_file *file) +static int cbfs_check_magic(struct cbfs_file *file) { return !strcmp(file->magic, CBFS_FILE_MAGIC) ? 1 : 0; } -struct cbfs_header *cbfs_master_header(void) +static struct cbfs_header *cbfs_master_header(void) { struct cbfs_header *header; @@ -103,7 +112,7 @@ struct cbfs_file *cbfs_find(const char *name) } } -struct cbfs_stage *cbfs_find_file(const char *name, int type) +void *cbfs_find_file(const char *name, int type) { struct cbfs_file *file = cbfs_find(name); @@ -123,7 +132,7 @@ struct cbfs_stage *cbfs_find_file(const char *name, int type) return (void *) CBFS_SUBHEADER(file); } -static int tohex4(unsigned int c) +static inline int tohex4(unsigned int c) { return (c<=9)?(c+'0'):(c-10+'a'); } @@ -205,11 +214,6 @@ void * cbfs_load_stage(const char *name) return (void *) entry; } -void * cbfs_get_file(const char *name) -{ - return (void *) cbfs_find(name); -} - int cbfs_execute_stage(const char *name) { struct cbfs_stage *stage = (struct cbfs_stage *) @@ -233,7 +237,7 @@ int cbfs_execute_stage(const char *name) * run_address is passed the address of a function taking no parameters and * jumps to it, returning the result. * @param f the address to call as a function. - * returns value returned by the function. + * @return value returned by the function. */ int run_address(void *f) diff --git a/src/mainboard/rca/rm4100/Kconfig b/src/mainboard/rca/rm4100/Kconfig index cdca002ea3..1654128b03 100644 --- a/src/mainboard/rca/rm4100/Kconfig +++ b/src/mainboard/rca/rm4100/Kconfig @@ -9,6 +9,7 @@ config BOARD_RCA_RM4100 select HAVE_PIRQ_TABLE select UDELAY_TSC select BOARD_ROMSIZE_KB_512 + select HAVE_SMI_HANDLER config MAINBOARD_DIR string diff --git a/src/mainboard/rca/rm4100/Makefile.inc b/src/mainboard/rca/rm4100/Makefile.inc index 5688262e62..38a5a61fdd 100644 --- a/src/mainboard/rca/rm4100/Makefile.inc +++ b/src/mainboard/rca/rm4100/Makefile.inc @@ -1 +1,4 @@ ROMCCFLAGS=-mcpu=p3 -O + +smmobj-$(CONFIG_HAVE_SMI_HANDLER) += mainboard_smi.o + diff --git a/src/mainboard/rca/rm4100/devicetree.cb b/src/mainboard/rca/rm4100/devicetree.cb index 2b04ad7d3a..4dff3bea53 100644 --- a/src/mainboard/rca/rm4100/devicetree.cb +++ b/src/mainboard/rca/rm4100/devicetree.cb @@ -1,4 +1,9 @@ chip northbridge/intel/i82830 # Northbridge + device apic_cluster 0 on # APIC cluster + chip cpu/intel/socket_PGA370 # Mobile Celeron Micro-FCBGA Socket 479 + device apic 0 on end # APIC + end + end device pci_domain 0 on # PCI domain device pci 0.0 on end # Host bridge device pci 2.0 on end # VGA (Intel 82830 CGC) @@ -19,9 +24,7 @@ chip northbridge/intel/i82830 # Northbridge device pci 1d.1 on end # USB UHCI Controller #2 device pci 1d.2 on end # USB UHCI Controller #3 device pci 1d.7 on end # USB2 EHCI Controller - device pci 1e.0 on # PCI bridge - device pci 08.0 on end # Intel 82801DB PRO/100 VE Ethernet - end + device pci 1e.0 on end # PCI bridge device pci 1f.0 on # ISA/LPC bridge chip superio/smsc/smscsuperio # Super I/O device pnp 2e.0 off # Floppy @@ -61,10 +64,5 @@ chip northbridge/intel/i82830 # Northbridge device pci 1f.6 on end # AC'97 modem end end - device apic_cluster 0 on # APIC cluster - chip cpu/intel/socket_PGA370 # Mobile Celeron Micro-FCBGA Socket 479 - device apic 0 on end # APIC - end - end end diff --git a/src/mainboard/rca/rm4100/mainboard.c b/src/mainboard/rca/rm4100/mainboard.c index cb907496e1..0324266a8d 100644 --- a/src/mainboard/rca/rm4100/mainboard.c +++ b/src/mainboard/rca/rm4100/mainboard.c @@ -21,6 +21,18 @@ #include <device/device.h> #include "chip.h" +static void mainboard_init(device_t dev) +{ + // TODO Switch parport LEDs again +} + +static void mainboard_enable(device_t dev) +{ + // TODO Switch parport LEDs + dev->ops->init = mainboard_init; +} + struct chip_operations mainboard_ops = { + .enable_dev = mainboard_enable, CHIP_NAME("RCA RM4100 Mainboard") }; diff --git a/src/mainboard/rca/rm4100/mainboard_smi.c b/src/mainboard/rca/rm4100/mainboard_smi.c new file mode 100644 index 0000000000..3e242359ee --- /dev/null +++ b/src/mainboard/rca/rm4100/mainboard_smi.c @@ -0,0 +1,30 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2008-2009 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> + +int mainboard_io_trap_handler(int smif) +{ + printk_debug("MAINBOARD IO TRAP HANDLER!\n"); + return 1; +} diff --git a/src/mainboard/rca/rm4100/romstage.c b/src/mainboard/rca/rm4100/romstage.c index cf7464442d..8b70d9ffc7 100644 --- a/src/mainboard/rca/rm4100/romstage.c +++ b/src/mainboard/rca/rm4100/romstage.c @@ -45,6 +45,7 @@ #define SERIAL_DEV PNP_DEV(0x2e, SMSCSUPERIO_SP1) #include "southbridge/intel/i82801dx/i82801dx_early_smbus.c" +#include "southbridge/intel/i82801dx/i82801dx_tco_timer.c" /** * The onboard 64MB PC133 memory does not have a SPD EEPROM so the @@ -102,11 +103,12 @@ static void mb_early_setup(void) static void main(unsigned long bist) { - if (bist == 0) + if (bist == 0) { early_mtrr_init(); if (memory_initialized()) { hard_reset(); } + } /* Set southbridge and superio gpios */ mb_gpio_init(); @@ -118,6 +120,9 @@ static void main(unsigned long bist) /* Halt if there was a built in self test failure. */ report_bist_failure(bist); + /* disable TCO timers */ + i82801dx_halt_tco_timer(); + /* Setup mainboard specific registers */ mb_early_setup(); diff --git a/src/mainboard/thomson/ip1000/Kconfig b/src/mainboard/thomson/ip1000/Kconfig index b78a20e388..48ab245fd7 100644 --- a/src/mainboard/thomson/ip1000/Kconfig +++ b/src/mainboard/thomson/ip1000/Kconfig @@ -9,6 +9,7 @@ config BOARD_THOMSON_IP1000 select HAVE_PIRQ_TABLE select UDELAY_TSC select BOARD_ROMSIZE_KB_512 + select HAVE_SMI_HANDLER config MAINBOARD_DIR string diff --git a/src/mainboard/thomson/ip1000/Makefile.inc b/src/mainboard/thomson/ip1000/Makefile.inc index 5688262e62..38a5a61fdd 100644 --- a/src/mainboard/thomson/ip1000/Makefile.inc +++ b/src/mainboard/thomson/ip1000/Makefile.inc @@ -1 +1,4 @@ ROMCCFLAGS=-mcpu=p3 -O + +smmobj-$(CONFIG_HAVE_SMI_HANDLER) += mainboard_smi.o + diff --git a/src/mainboard/thomson/ip1000/devicetree.cb b/src/mainboard/thomson/ip1000/devicetree.cb index f38c1c3e67..a3ee26cc59 100644 --- a/src/mainboard/thomson/ip1000/devicetree.cb +++ b/src/mainboard/thomson/ip1000/devicetree.cb @@ -1,4 +1,10 @@ chip northbridge/intel/i82830 # Northbridge + device apic_cluster 0 on # APIC cluster + chip cpu/intel/socket_PGA370 # Low Voltage PIII Micro-FCBGA Socket 479 + device apic 0 on end # APIC + end + end + device pci_domain 0 on # PCI domain device pci 0.0 on end # Host bridge device pci 2.0 on end # VGA (Intel 82830 CGC) @@ -19,9 +25,7 @@ chip northbridge/intel/i82830 # Northbridge device pci 1d.1 on end # USB UHCI Controller #2 device pci 1d.2 on end # USB UHCI Controller #3 device pci 1d.7 on end # USB2 EHCI Controller - device pci 1e.0 on # PCI bridge - device pci 08.0 on end # Intel 82801DB PRO/100 VE Ethernet - end + device pci 1e.0 on end # PCI bridge device pci 1f.0 on # ISA/LPC bridge chip superio/smsc/smscsuperio # Super I/O device pnp 2e.0 off # Floppy @@ -61,10 +65,5 @@ chip northbridge/intel/i82830 # Northbridge device pci 1f.6 off end # AC'97 modem end end - device apic_cluster 0 on # APIC cluster - chip cpu/intel/socket_PGA370 # Low Voltage PIII Micro-FCBGA Socket 479 - device apic 0 on end # APIC - end - end end diff --git a/src/mainboard/thomson/ip1000/mainboard.c b/src/mainboard/thomson/ip1000/mainboard.c index c6b4cdf738..daa6b7eb64 100644 --- a/src/mainboard/thomson/ip1000/mainboard.c +++ b/src/mainboard/thomson/ip1000/mainboard.c @@ -21,6 +21,18 @@ #include <device/device.h> #include "chip.h" +static void mainboard_init(device_t dev) +{ + // TODO Switch parport LEDs again +} + +static void mainboard_enable(device_t dev) +{ + // TODO Switch parport LEDs + dev->ops->init = mainboard_init; +} + struct chip_operations mainboard_ops = { + .enable_dev = mainboard_enable, CHIP_NAME("THOMSON IP1000 Mainboard") }; diff --git a/src/mainboard/thomson/ip1000/mainboard_smi.c b/src/mainboard/thomson/ip1000/mainboard_smi.c new file mode 100644 index 0000000000..3e242359ee --- /dev/null +++ b/src/mainboard/thomson/ip1000/mainboard_smi.c @@ -0,0 +1,30 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2008-2009 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> + +int mainboard_io_trap_handler(int smif) +{ + printk_debug("MAINBOARD IO TRAP HANDLER!\n"); + return 1; +} diff --git a/src/mainboard/thomson/ip1000/romstage.c b/src/mainboard/thomson/ip1000/romstage.c index cf7464442d..8b70d9ffc7 100644 --- a/src/mainboard/thomson/ip1000/romstage.c +++ b/src/mainboard/thomson/ip1000/romstage.c @@ -45,6 +45,7 @@ #define SERIAL_DEV PNP_DEV(0x2e, SMSCSUPERIO_SP1) #include "southbridge/intel/i82801dx/i82801dx_early_smbus.c" +#include "southbridge/intel/i82801dx/i82801dx_tco_timer.c" /** * The onboard 64MB PC133 memory does not have a SPD EEPROM so the @@ -102,11 +103,12 @@ static void mb_early_setup(void) static void main(unsigned long bist) { - if (bist == 0) + if (bist == 0) { early_mtrr_init(); if (memory_initialized()) { hard_reset(); } + } /* Set southbridge and superio gpios */ mb_gpio_init(); @@ -118,6 +120,9 @@ static void main(unsigned long bist) /* Halt if there was a built in self test failure. */ report_bist_failure(bist); + /* disable TCO timers */ + i82801dx_halt_tco_timer(); + /* Setup mainboard specific registers */ mb_early_setup(); diff --git a/src/northbridge/intel/i82830/Makefile.inc b/src/northbridge/intel/i82830/Makefile.inc index 3ebb8a5aaf..57dedfde73 100644 --- a/src/northbridge/intel/i82830/Makefile.inc +++ b/src/northbridge/intel/i82830/Makefile.inc @@ -1,2 +1,4 @@ driver-y += northbridge.o driver-y += vga.o + +smmobj-$(CONFIG_HAVE_SMI_HANDLER) += i82830_smihandler.o diff --git a/src/northbridge/intel/i82830/i82830_smihandler.c b/src/northbridge/intel/i82830/i82830_smihandler.c new file mode 100644 index 0000000000..ae5d5e2872 --- /dev/null +++ b/src/northbridge/intel/i82830/i82830_smihandler.c @@ -0,0 +1,374 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2010 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 <types.h> +#include <string.h> +#include <arch/io.h> +#include <arch/romcc_io.h> +#include <console/console.h> +#include <cpu/x86/cache.h> +#include <cpu/x86/smm.h> +#include <device/pci_def.h> +#include "i82830.h" + +extern unsigned char *mbi; +extern u32 mbi_len; + +#define DEBUG_SMI + +/* If YABEL is enabled and it's not running at 0x00000000, we have to add some + * offset to all our mbi object memory accesses + */ +#if defined(CONFIG_PCI_OPTION_ROM_RUN_YABEL) && !defined(CONFIG_YABEL_DIRECTHW) +#define OBJ_OFFSET CONFIG_YABEL_VIRTMEM_LOCATION +#else +#define OBJ_OFFSET 0x00000 +#endif + +/* I830M */ +#define SMRAM 0x90 +#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)) + + +typedef struct { + u32 mhid; + u32 function; + u32 retsts; + u32 rfu; +} __attribute__((packed)) banner_id_t; + +#define MSH_OK 0x0000 +#define MSH_OK_RESTART 0x0001 +#define MSH_FWH_ERR 0x00ff +#define MSH_IF_BAD_ID 0x0100 +#define MSH_IF_BAD_FUNC 0x0101 +#define MSH_IF_MBI_CORRUPT 0x0102 +#define MSH_IF_BAD_HANDLE 0x0103 +#define MSH_ALRDY_ATCHED 0x0104 +#define MSH_NOT_ATCHED 0x0105 +#define MSH_IF 0x0106 +#define MSH_IF_INVADDR 0x0107 +#define MSH_IF_UKN_TYPE 0x0108 +#define MSH_IF_NOT_FOUND 0x0109 +#define MSH_IF_NO_KEY 0x010a +#define MSH_IF_BUF_SIZE 0x010b +#define MSH_IF_NOT_PENDING 0x010c + +static void +dump(u8 * addr, u32 len) +{ + printk_debug("\n%s(%p, %x):\n", __func__, addr, len); + while (len) { + unsigned int tmpCnt = len; + unsigned char x; + if (tmpCnt > 8) + tmpCnt = 8; + printk_debug("\n%p: ", addr); + // print hex + while (tmpCnt--) { + x = *addr++; + printk_debug("%02x ", x); + } + tmpCnt = len; + if (tmpCnt > 8) + tmpCnt = 8; + len -= tmpCnt; + //reset addr ptr to print ascii + addr = addr - tmpCnt; + // print ascii + while (tmpCnt--) { + x = *addr++; + if ((x < 32) || (x >= 127)) { + //non-printable char + x = '.'; + } + printk_debug("%c", x); + } + } + printk_debug("\n"); +} + +typedef struct { + banner_id_t banner; + u16 versionmajor; + u16 versionminor; + u32 smicombuffersize; +} __attribute__((packed)) version_t; + +typedef struct { + u16 header_id; + u16 attributes; + u16 size; + u8 name_len; + u8 reserved; + u32 type; + u32 header_ext; + u8 name[0]; +} __attribute__((packed)) mbi_header_t; + +typedef struct { + banner_id_t banner; + u64 handle; + u32 objnum; + mbi_header_t header; +} __attribute__((packed)) obj_header_t; + +typedef struct { + banner_id_t banner; + u64 handle; + u32 objnum; + u32 start; + u32 numbytes; + u32 buflen; + u32 buffer; +} __attribute__((packed)) get_object_t; + +static void mbi_call(u8 subf, banner_id_t *banner_id) +{ + // printk_debug("MBI\n"); + // printk_debug("|- sub function %x\n", subf); + // printk_debug("|- banner id @ %x\n", (u32)banner_id); + // printk_debug("| |- mhid %x\n", banner_id->mhid); + // printk_debug("| |- function %x\n", banner_id->function); + // printk_debug("| |- return status %x\n", banner_id->retsts); + // printk_debug("| |- rfu %x\n", banner_id->rfu); + + switch(banner_id->function) { + case 0x0001: { + version_t *version; + printk_debug("|- MBI_QueryInterface\n"); + version = (version_t *)banner_id; + version->banner.retsts = MSH_OK; + version->versionmajor=1; + version->versionminor=3; + version->smicombuffersize=0x1000; + break; + } + case 0x0002: + printk_debug("|- MBI_Attach\n"); + printk_debug("| |- Not Implemented!\n"); + break; + case 0x0003: + printk_debug("|- MBI_Detach\n"); + printk_debug("| |- Not Implemented!\n"); + break; + case 0x0201: { + obj_header_t *obj_header = (obj_header_t *)banner_id; + mbi_header_t *mbi_header = NULL; + printk_debug("|- MBI_GetObjectHeader\n"); + printk_debug("| |- objnum = %d\n", obj_header->objnum); + + int i, count=0; + obj_header->banner.retsts = MSH_IF_NOT_FOUND; + + for (i=0; i< mbi_len;) { + int len; + + if (!(mbi[i] == 0xf0 && mbi [i+1] == 0xf6)) { + i+=16; + continue; + } + + mbi_header = (mbi_header_t *)&mbi[i]; + len = ALIGN((mbi_header->size * 16) + sizeof(mbi_header) + mbi_header->name_len, 16); + + if (obj_header->objnum == count) { + int headerlen = ALIGN(sizeof(mbi_header) + mbi_header->name_len + 15, 16); + // printk_debug("| |- headerlen = %d\n", headerlen); + memcpy(&obj_header->header, mbi_header, headerlen); + obj_header->banner.retsts = MSH_OK; + printk_debug("| |- MBI module '"); + int j; + for (j=0; j < mbi_header->name_len && mbi_header->name[j]; j++) + printk_debug("%c", mbi_header->name[j]); + printk_debug("' found.\n", obj_header->objnum); + // dump(banner_id, sizeof(obj_header_t) + 16); + break; + } + i += len; + count++; + } + if (obj_header->banner.retsts == MSH_IF_NOT_FOUND) + printk_debug("| |- MBI object #%d not found.\n", obj_header->objnum); + break; + } + case 0x0203: { + get_object_t *getobj = (get_object_t *)banner_id; + mbi_header_t *mbi_header = NULL; + printk_debug("|- MBI_GetObject\n"); + // printk_debug("| |- handle = %016lx\n", getobj->handle); + printk_debug("| |- objnum = %d\n", getobj->objnum); + printk_debug("| |- start = %x\n", getobj->start); + printk_debug("| |- numbytes = %x\n", getobj->numbytes); + printk_debug("| |- buflen = %x\n", getobj->buflen); + printk_debug("| |- buffer = %x\n", getobj->buffer); + + int i, count=0; + getobj->banner.retsts = MSH_IF_NOT_FOUND; + + for (i=0; i< mbi_len;) { + int len; + + if (!(mbi[i] == 0xf0 && mbi [i+1] == 0xf6)) { + i+=16; + continue; + } + + mbi_header = (mbi_header_t *)&mbi[i]; + len = ALIGN((mbi_header->size * 16) + sizeof(mbi_header) + mbi_header->name_len, 16); + + if (getobj->objnum == count) { + printk_debug("| |- len = %x\n", len); + memcpy((void *)(getobj->buffer + OBJ_OFFSET), + ((char *)mbi_header) + 0x20 , (len > getobj->buflen ? getobj->buflen : len)); + + getobj->banner.retsts = MSH_OK; + //dump(banner_id, sizeof(getobj) + len); + break; + } + i += len; + count++; + } + if (getobj->banner.retsts == MSH_IF_NOT_FOUND) + printk_debug("MBI module %d not found.\n", getobj->objnum); + break; + } + default: + printk_debug("|- function %x\n", banner_id->function); + printk_debug("| |- Unknown Function!\n"); + break; + } + printk_debug("\n"); + //dump(banner_id, 0x20); +} + +#define SMI_IFC_SUCCESS 1 +#define SMI_IFC_FAILURE_GENERIC 0 +#define SMI_IFC_FAILURE_INVALID 2 +#define SMI_IFC_FAILURE_CRITICAL 4 +#define SMI_IFC_FAILURE_NONCRITICAL 6 + +#define PC10 0x10 +#define PC11 0x11 +#define PC12 0x12 +#define PC13 0x13 + +void smi_interface_call(void) +{ + u32 mmio; + mmio = pci_read_config32(PCI_DEV(0, 0x02, 0), 0x14); + // mmio &= 0xfff80000; + // printk_debug("mmio=%x\n", mmio); + + u16 swsmi; + swsmi=pci_read_config16(PCI_DEV(0, 0x02, 0), 0xe0); + + if (!(swsmi & 1)) + return; + + swsmi &= ~(1 << 0); // clear SMI toggle + + switch ((swsmi>>1) & 0xf) { + case 0: + printk_debug("Interface Function Presence Test.\n"); + swsmi = 0; + swsmi &= ~(7 << 5); // Exit: Result + swsmi |= (SMI_IFC_SUCCESS << 5); + swsmi &= 0xff; + swsmi |= (PC13 << 8); + pci_write_config16(PCI_DEV(0, 0x02, 0), 0xe0, swsmi); + // pathetic + write32(mmio + 0x71428, 0x494e5443); + return; + case 4: + printk_debug("Get BIOS Data.\n"); + printk_debug("swsmi=%04x\n", swsmi); + break; + case 5: + printk_debug("Call MBI Functions.\n"); + mbi_call(swsmi >> 8, (banner_id_t *)((readl(mmio + 0x71428) & 0x000fffff) + OBJ_OFFSET) ); + // swsmi = 0x0000; + swsmi &= ~(7 << 5); // Exit: Result + swsmi |= (SMI_IFC_SUCCESS << 5); + pci_write_config16(PCI_DEV(0, 0x02, 0), 0xe0, swsmi); + return; + case 6: + printk_debug("System BIOS Callbacks.\n"); + printk_debug("swsmi=%04x\n", swsmi); + break; + default: + printk_debug("Unknown SMI interface call %04x\n", swsmi); + break; + } + + swsmi &= ~(7 << 5); // Exit: Result + swsmi |= (SMI_IFC_FAILURE_CRITICAL << 7); + pci_write_config16(PCI_DEV(0, 0x02, 0), 0xe0, swsmi); +} + +/** + * @brief read and clear ERRSTS + * @return ERRSTS register + */ +static u16 reset_err_status(void) +{ + u16 reg16; + + reg16 = pci_read_config16(PCI_DEV(0, 0x00, 0), ERRSTS); + /* set status bits are cleared by writing 1 to them */ + pci_write_config16(PCI_DEV(0, 0x00, 0), ERRSTS, reg16); + + return reg16; +} + +static void dump_err_status(u32 errsts) +{ + printk_debug("ERRSTS: "); + if (errsts & (1 << 12)) printk_debug("MBI "); + if (errsts & (1 << 9)) printk_debug("LCKF "); + if (errsts & (1 << 8)) printk_debug("DTF "); + if (errsts & (1 << 5)) printk_debug("UNSC "); + if (errsts & (1 << 4)) printk_debug("OOGF "); + if (errsts & (1 << 3)) printk_debug("IAAF "); + if (errsts & (1 << 2)) printk_debug("ITTEF "); + printk_debug("\n"); +} + +void northbridge_smi_handler(unsigned int node, smm_state_save_area_t *state_save) +{ + u16 errsts; + + /* We need to clear the SMI status registers, or we won't see what's + * happening in the following calls. + */ + errsts = reset_err_status(); + if (errsts & (1 << 12)) { + smi_interface_call(); + } else { + if (errsts) + dump_err_status(errsts); + } + +} diff --git a/src/northbridge/intel/i82830/northbridge.c b/src/northbridge/intel/i82830/northbridge.c index e482db6cf6..af9663df09 100644 --- a/src/northbridge/intel/i82830/northbridge.c +++ b/src/northbridge/intel/i82830/northbridge.c @@ -116,7 +116,7 @@ static void pci_domain_set_resources(device_t dev) */ tomk = ((unsigned long)pci_read_config8(mc_dev, DRB + 3)) << 15; tomk -= igd_memory; - printk_debug("Setting RAM size to %ld\n", tomk); + printk_debug("Memory detected: %ldKB RAM\n", tomk); /* Compute the top of low memory. */ tolmk = pci_tolm >> 10; diff --git a/src/northbridge/intel/i82830/raminit.c b/src/northbridge/intel/i82830/raminit.c index 9cb194ec22..2b747158c6 100644 --- a/src/northbridge/intel/i82830/raminit.c +++ b/src/northbridge/intel/i82830/raminit.c @@ -536,6 +536,7 @@ static void northbridge_set_registers(void) value = pci_read_config16(NORTHBRIDGE, GCC1); value |= igd_memory << 4; + value |= 1; // 64MB aperture pci_write_config16(NORTHBRIDGE, GCC1, value); PRINT_DEBUG("Initial northbridge registers have been set.\r\n"); diff --git a/src/northbridge/intel/i82830/vga.c b/src/northbridge/intel/i82830/vga.c index e9cfdac0c6..8c1cac0a84 100644 --- a/src/northbridge/intel/i82830/vga.c +++ b/src/northbridge/intel/i82830/vga.c @@ -24,13 +24,67 @@ #include <device/device.h> #include <device/pci.h> #include <device/pci_ids.h> +#include <cbfs.h> +#include <x86emu/x86emu.h> -static void vga_init(device_t dev) { - +static void vga_init(device_t dev) +{ printk_info("Starting Graphics Initialization\n"); + struct cbfs_file *file = cbfs_find("mbi.bin"); + void *mbi = NULL; + unsigned int mbi_len = 0; + + if (file) { + if (ntohl(file->type) != CBFS_TYPE_MBI) { + printk_info( "CBFS: MBI binary is of type %x instead of" + "type %x\n", file->type, CBFS_TYPE_MBI); + } else { + mbi = (void *) CBFS_SUBHEADER(file); + mbi_len = file->len; + } + } else { + printk_info( "Could not find MBI.\n"); + } + + if (mbi && mbi_len) { + /* The GDT or coreboot table is going to live here. But + * a long time after we relocated the GNVS, so this is + * not troublesome. + */ + *(u32 *)0x500 = (u32)mbi; + *(u32 *)0x504 = (u32)mbi_len; + outb(0xeb, 0xb2); + } + pci_dev_init(dev); printk_info("Graphics Initialization Complete\n"); - /* Future TV-OUT code will be called from here. */ + + /* Enable TV-Out */ +#if defined(CONFIG_PCI_OPTION_ROM_RUN_YABEL) && CONFIG_PCI_OPTION_ROM_RUN_YABEL +#define PIPE_A_CRT (1 << 0) +#define PIPE_A_LFP (1 << 1) +#define PIPE_A_TV (1 << 3) +#define PIPE_B_CRT (1 << 8) +#define PIPE_B_TV (1 << 10) + printk_debug("Enabling TV-Out\n"); + void runInt10(void); + M.x86.R_AX = 0x5f64; + M.x86.R_BX = 0x0001; // Set Display Device, force execution + M.x86.R_CX = PIPE_A_CRT | PIPE_A_TV; + // M.x86.R_CX = PIPE_B_TV; + runInt10(); + switch (M.x86.R_AX) { + case 0x005f: + printk_debug("... failed.\n"); + break; + case 0x015f: + printk_debug("... ok.\n"); + break; + default: + printk_debug("... not supported.\n"); + break; + } +#endif } static const struct device_operations vga_operations = { diff --git a/src/southbridge/intel/i82801dx/Kconfig b/src/southbridge/intel/i82801dx/Kconfig index 6a35691b5d..62da5cf856 100644 --- a/src/southbridge/intel/i82801dx/Kconfig +++ b/src/southbridge/intel/i82801dx/Kconfig @@ -1,2 +1,3 @@ config SOUTHBRIDGE_INTEL_I82801DX bool + select IOAPIC diff --git a/src/southbridge/intel/i82801dx/Makefile.inc b/src/southbridge/intel/i82801dx/Makefile.inc index 7167e1d391..562e80afe0 100644 --- a/src/southbridge/intel/i82801dx/Makefile.inc +++ b/src/southbridge/intel/i82801dx/Makefile.inc @@ -7,3 +7,6 @@ driver-y += i82801dx_ac97.o #driver-y += i82801dx_nic.o #driver-y += i82801dx_pci.o obj-y += i82801dx_reset.o + +obj-$(CONFIG_HAVE_SMI_HANDLER) += i82801dx_smi.o +smmobj-$(CONFIG_HAVE_SMI_HANDLER) += i82801dx_smihandler.o diff --git a/src/southbridge/intel/i82801dx/i82801dx_ac97.c b/src/southbridge/intel/i82801dx/i82801dx_ac97.c index 752449865d..10b100beee 100644 --- a/src/southbridge/intel/i82801dx/i82801dx_ac97.c +++ b/src/southbridge/intel/i82801dx/i82801dx_ac97.c @@ -1,41 +1,285 @@ /* - * (C) 2003 Linux Networx + * This file is part of the coreboot project. + * + * Copyright (C) 2008-2009 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 <console/console.h> #include <device/device.h> #include <device/pci.h> #include <device/pci_ids.h> -#include <device/pci_ops.h> +#include <arch/io.h> +#include <delay.h> #include "i82801dx.h" +#define NAMBAR 0x10 +#define MASTER_VOL 0x02 +#define PAGING 0x24 +#define EXT_AUDIO 0x28 +#define FUNC_SEL 0x66 +#define INFO_IO 0x68 +#define CONNECTOR 0x6a +#define VENDOR_ID1 0x7c +#define VENDOR_ID2 0x7e +#define SEC_VENDOR_ID1 0xfc +#define SEC_VENDOR_ID2 0xfe + +#define NABMBAR 0x14 +#define GLOB_CNT 0x2c +#define GLOB_STA 0x30 +#define CAS 0x34 + +#define MMBAR 0x10 +#define EXT_MODEM_ID1 0x3c +#define EXT_MODEM_ID2 0xbc + +#define MBAR 0x14 +#define SEC_CODEC 0x40 + + +/* FIXME. This table is probably mainboard specific */ +static u16 ac97_function[16*2][4] = { + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) } +}; + +static u16 nabmbar; +static u16 nambar; + +static int ac97_semaphore(void) +{ + int timeout; + u8 reg8; + + timeout = 0xffff; + do { + reg8 = inb(nabmbar + CAS); + timeout--; + } while ((reg8 & 1) && timeout); + if (! timeout) { + printk_debug("Timeout!\n"); + } + + return (!timeout); +} + +static void init_cnr(void) +{ + // TODO +} + +static void program_sigid(struct device *dev, u32 id) +{ + pci_write_config32(dev, 0x2c, id); +} + +static void ac97_audio_init(struct device *dev) +{ + u16 reg16; + u32 reg32; + int i; + + printk_debug("Initializing AC'97 Audio.\n"); + + /* top 16 bits are zero, so don't read them */ + nabmbar = pci_read_config16(dev, NABMBAR) & 0xfffe; + nambar = pci_read_config16(dev, NAMBAR) & 0xfffe; + + reg16 = inw(nabmbar + GLOB_CNT); + reg16 |= (1 << 1); /* Remove AC_RESET# */ + outw(reg16, nabmbar + GLOB_CNT); + + /* Wait 600ms. Ouch. */ + udelay(600 * 1000); + + init_cnr(); + + /* Detect Primary AC'97 Codec */ + reg32 = inl(nabmbar + GLOB_STA); + if ((reg32 & ((1 << 28) | (1 << 9) | (1 << 8))) == 0) { + /* Primary Codec not found */ + printk_debug("No primary codec. Disabling AC'97 Audio.\n"); + return; + } + + ac97_semaphore(); + + /* Detect if codec is programmable */ + outw(0x8000, nambar + MASTER_VOL); + ac97_semaphore(); + if (inw(nambar + MASTER_VOL) != 0x8000) { + printk_debug("Codec not programmable. Disabling AC'97 Audio.\n"); + return; + } + + /* Program Vendor IDs */ + reg32 = inw(nambar + VENDOR_ID1); + reg32 <<= 16; + reg32 |= (u16)inw(nambar + VENDOR_ID2); + + program_sigid(dev, reg32); -static struct device_operations ac97audio_ops = { + /* Is Codec AC'97 2.3 compliant? */ + reg16 = inw(nambar + EXT_AUDIO); + /* [11:10] = 10b -> AC'97 2.3 */ + if ((reg16 & 0x0c00) != 0x0800) { + /* No 2.3 Codec. We're done */ + return; + } + + /* Select Page 1 */ + reg16 = inw(nambar + PAGING); + reg16 &= 0xfff0; + reg16 |= 0x0001; + outw(reg16, nambar + PAGING); + + for (i = 0x0a * 2; i > 0; i--) { + outw(i, nambar + FUNC_SEL); + + /* Function could not be selected. Next one */ + if (inw(nambar + FUNC_SEL) != i) + continue; + + reg16 = inw(nambar + INFO_IO); + + /* Function Information present? */ + if (!(reg16 & (1 << 0))) + continue; + + /* Function Information valid? */ + if (!(reg16 & (1 << 4))) + continue; + + /* Program Buffer Delay [9:5] */ + reg16 &= 0x03e0; + reg16 |= ac97_function[i][0]; + + /* Program Gain [15:11] */ + reg16 |= ac97_function[i][1]; + + /* Program Inversion [10] */ + reg16 |= ac97_function[i][2]; + + outw(reg16, nambar + INFO_IO); + + /* Program Connector / Jack Location */ + reg16 = inw(nambar + CONNECTOR); + reg16 &= 0x1fff; + reg16 |= ac97_function[i][3]; + outw(reg16, nambar + CONNECTOR); + } +} + +static void ac97_modem_init(struct device *dev) +{ + u16 reg16; + u32 reg32; + u16 mmbar, mbar; + + mmbar = pci_read_config16(dev, MMBAR) & 0xfffe; + mbar = pci_read_config16(dev, MBAR) & 0xfffe; + + reg16 = inw(mmbar + EXT_MODEM_ID1); + if ((reg16 & 0xc000) != 0xc000 ) { + if (reg16 & (1 << 0)) { + reg32 = inw(mmbar + VENDOR_ID2); + reg32 <<= 16; + reg32 |= (u16)inw(mmbar + VENDOR_ID1); + program_sigid(dev, reg32); + return; + } + } + + /* Secondary codec? */ + reg16 = inw(mbar + SEC_CODEC); + if ((reg16 & (1 << 9)) == 0) + return; + + reg16 = inw(mmbar + EXT_MODEM_ID2); + if ((reg16 & 0xc000) == 0x4000) { + if (reg16 & (1 << 0)) { + reg32 = inw(mmbar + SEC_VENDOR_ID2); + reg32 <<= 16; + reg32 |= (u16)inw(mmbar + SEC_VENDOR_ID1); + program_sigid(dev, reg32); + return; + } + } +} + +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, .enable = i82801dx_enable, - .init = 0, + .init = ac97_audio_init, .scan_bus = 0, }; -static const struct pci_driver ac97audio_driver __pci_driver = { - .ops = &ac97audio_ops, - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_82801DBM_AC97_AUDIO, -}; - - -static struct device_operations ac97modem_ops = { +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, .enable = i82801dx_enable, - .init = 0, + .init = ac97_modem_init, .scan_bus = 0, }; -static const struct pci_driver ac97modem_driver __pci_driver = { - .ops = &ac97modem_ops, - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_82801DBM_AC97_MODEM, +/* 82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) */ +static const struct pci_driver i82801db_ac97_audio __pci_driver = { + .ops = &ac97_audio_ops, + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_82801DB_AC97_AUDIO, +}; + +static const struct pci_driver i82801db_ac97_modem __pci_driver = { + .ops = &ac97_modem_ops, + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_82801DB_AC97_MODEM, }; + + diff --git a/src/southbridge/intel/i82801dx/i82801dx_nvs.h b/src/southbridge/intel/i82801dx/i82801dx_nvs.h new file mode 100644 index 0000000000..03f8de74ea --- /dev/null +++ b/src/southbridge/intel/i82801dx/i82801dx_nvs.h @@ -0,0 +1,138 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2008-2009 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 { + /* Miscellaneous */ + u16 osys; /* 0x00 - Operating System */ + u8 smif; /* 0x02 - SMI function call ("TRAP") */ + u8 prm0; /* 0x03 - SMI function call parameter */ + u8 prm1; /* 0x04 - SMI function call parameter */ + u8 scif; /* 0x05 - SCI function call (via _L00) */ + u8 prm2; /* 0x06 - SCI function call parameter */ + u8 prm3; /* 0x07 - SCI function call parameter */ + u8 lckf; /* 0x08 - Global Lock function for EC */ + u8 prm4; /* 0x09 - Lock function parameter */ + u8 prm5; /* 0x0a - Lock function parameter */ + u32 p80d; /* 0x0b - Debug port (IO 0x80) value */ + u8 lids; /* 0x0f - LID state (open = 1) */ + u8 pwrs; /* 0x10 - Power state (AC = 1) */ + u8 dbgs; /* 0x11 - Debug state */ + u8 linx; /* 0x12 - Linux OS */ + u8 dckn; /* 0x13 - PCIe docking state */ + /* Thermal policy */ + u8 actt; /* 0x14 - active trip point */ + u8 psvt; /* 0x15 - passive trip point */ + u8 tc1v; /* 0x16 - passive trip point TC1 */ + u8 tc2v; /* 0x17 - passive trip point TC2 */ + u8 tspv; /* 0x18 - passive trip point TSP */ + u8 crtt; /* 0x19 - critical trip point */ + u8 dtse; /* 0x1a - Digital Thermal Sensor enable */ + u8 dts1; /* 0x1b - DT sensor 1 */ + u8 dts2; /* 0x1c - DT sensor 2 */ + u8 rsvd2; + /* Battery Support */ + u8 bnum; /* 0x1e - number of batteries */ + u8 b0sc, b1sc, b2sc; /* 0x1f-0x21 - stored capacity */ + u8 b0ss, b1ss, b2ss; /* 0x22-0x24 - stored status */ + u8 rsvd3[3]; + /* Processor Identification */ + u8 apic; /* 0x28 - APIC enabled */ + u8 mpen; /* 0x29 - MP capable/enabled */ + u8 pcp0; /* 0x2a - PDC CPU/CORE 0 */ + u8 pcp1; /* 0x2b - PDC CPU/CORE 1 */ + u8 ppcm; /* 0x2c - Max. PPC state */ + u8 rsvd4[5]; + /* Super I/O & CMOS config */ + u8 natp; /* 0x32 - SIO type */ + u8 cmap; /* 0x33 - */ + u8 cmbp; /* 0x34 - */ + u8 lptp; /* 0x35 - LPT port */ + u8 fdcp; /* 0x36 - Floppy Disk Controller */ + u8 rfdv; /* 0x37 - */ + u8 hotk; /* 0x38 - Hot Key */ + u8 rtcf; + u8 util; + u8 acin; + /* Integrated Graphics Device */ + u8 igds; /* 0x3c - IGD state */ + u8 tlst; /* 0x3d - Display Toggle List Pointer */ + u8 cadl; /* 0x3e - currently attached devices */ + u8 padl; /* 0x3f - previously attached devices */ + u16 cste; /* 0x40 - current display state */ + u16 nste; /* 0x42 - next display state */ + u16 sste; /* 0x44 - set display state */ + u8 ndid; /* 0x46 - number of device ids */ + u32 did[5]; /* 0x47 - 5b device id 1..5 */ + u8 rsvd5[0x9]; + /* Backlight Control */ + u8 blcs; /* 0x64 - Backlight Control possible */ + u8 brtl; + u8 odds; + u8 rsvd6[0x7]; + /* Ambient Light Sensors*/ + u8 alse; /* 0x6e - ALS enable */ + u8 alaf; + u8 llow; + u8 lhih; + u8 rsvd7[0x6]; + /* EMA */ + u8 emae; /* 0x78 - EMA enable */ + u16 emap; + u16 emal; + u8 rsvd8[0x5]; + /* MEF */ + u8 mefe; /* 0x82 - MEF enable */ + u8 rsvd9[0x9]; + /* TPM support */ + u8 tpmp; /* 0x8c - TPM */ + u8 tpme; + u8 rsvd10[8]; + /* SATA */ + u8 gtf0[7]; /* 0x96 - GTF task file buffer for port 0 */ + u8 gtf1[7]; + u8 gtf2[7]; + u8 idem; + u8 idet; + u8 rsvd11[7]; + /* IGD OpRegion (not implemented yet) */ + u32 aslb; /* 0xb4 - IGD OpRegion Base Address */ + u8 ibtt; + u8 ipat; + u8 itvf; + u8 itvm; + u8 ipsc; + u8 iblc; + u8 ibia; + u8 issc; + u8 i409; + u8 i509; + u8 i609; + u8 i709; + u8 idmm; + u8 idms; + u8 if1e; + u8 hvco; + u32 nxd[8]; + u8 rsvd12[8]; + /* Mainboard specific */ + u8 dock; /* 0xf0 - Docking Status */ + u8 bten; + u8 rsvd13[14]; +} __attribute__((packed)) global_nvs_t; + diff --git a/src/southbridge/intel/i82801dx/i82801dx_smi.c b/src/southbridge/intel/i82801dx/i82801dx_smi.c new file mode 100644 index 0000000000..b934dcf2bd --- /dev/null +++ b/src/southbridge/intel/i82801dx/i82801dx_smi.c @@ -0,0 +1,365 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2008-2009 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 "i82801dx.h" + +extern unsigned char smm[]; +extern unsigned int smm_len; + +/* I945 */ +#define SMRAM 0x90 +#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)) + +/* While we read PMBASE dynamically in case it changed, let's + * initialize it with a sane value + */ +static u16 pmbase = PMBASE_ADDR; + +/** + * @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 ALT_GP_SMI_STS + * @return ALT_GP_SMI_STS register + */ +static u16 reset_alt_gp_smi_status(void) +{ + u16 reg16; + + reg16 = inl(pmbase + ALT_GP_SMI_STS); + /* set status bits are cleared by writing 1 to them */ + outl(reg16, pmbase + ALT_GP_SMI_STS); + + return reg16; +} + +static void dump_alt_gp_smi_status(u16 alt_gp_smi_sts) +{ + int i; + printk_debug("ALT_GP_SMI_STS: "); + for (i=15; i<= 0; i--) { + if (alt_gp_smi_sts & (1 << i)) printk_debug("GPI%d ", (i-16)); + } + 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; + u16 pm1_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_alt_gp_smi_status(reset_alt_gp_smi_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) + */ + + smi_en = 0; /* reset SMI enables */ + +#if 0 + smi_en |= LEGACY_USB2_EN | LEGACY_USB_EN; +#endif + smi_en |= TCO_EN; + smi_en |= APMC_EN; +#if DEBUG_PERIODIC_SMIS + /* Set DEBUG_PERIODIC_SMIS in i82801gx.h to debug using + * periodic SMIs. + */ + smi_en |= PERIODIC_EN; +#endif + smi_en |= SLP_SMI_EN; + smi_en |= BIOS_EN; + + /* The following need to be on for SMIs to happen */ + smi_en |= EOS | GBL_SMI_EN; + + outl(smi_en, pmbase + SMI_EN); + + pm1_en = 0; + pm1_en |= PWRBTN_EN; + pm1_en |= GBL_EN; + outw(pm1_en, pmbase + PM1_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) +{ + // FIXME is this a race condition? + smm_relocate(); + smm_install(); + + // We're done. Make sure SMIs can happen! + smi_set_eos(); +} + +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); +} + +void smm_setup_structures(void *gnvs, void *tcg, void *smi1) +{ + /* The GDT or coreboot table is going to live here. But a long time + * after we relocated the GNVS, so this is not troublesome. + */ + *(u32 *)0x500 = (u32)gnvs; + *(u32 *)0x504 = (u32)tcg; + *(u32 *)0x508 = (u32)smi1; + outb(0xea, 0xb2); +} diff --git a/src/southbridge/intel/i82801dx/i82801dx_smihandler.c b/src/southbridge/intel/i82801dx/i82801dx_smihandler.c new file mode 100644 index 0000000000..9994429a2f --- /dev/null +++ b/src/southbridge/intel/i82801dx/i82801dx_smihandler.c @@ -0,0 +1,669 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2008-2009 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 <types.h> +#include <arch/io.h> +#include <arch/romcc_io.h> +#include <console/console.h> +#include <cpu/x86/cache.h> +#include <cpu/x86/smm.h> +#include <device/pci_def.h> +#include "i82801dx.h" + +#define DEBUG_SMI + +#define APM_CNT 0xb2 +#define CST_CONTROL 0x85 +#define PST_CONTROL 0x80 +#define ACPI_DISABLE 0x1e +#define ACPI_ENABLE 0xe1 +#define GNVS_UPDATE 0xea +#define MBI_UPDATE 0xeb +#define APM_STS 0xb3 + +/* I830M */ +#define SMRAM 0x90 +#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)) + +#include "i82801dx_nvs.h" + +/* While we read PMBASE dynamically in case it changed, let's + * initialize it with a sane value + */ +u16 pmbase = PMBASE_ADDR; +u8 smm_initialized = 0; + +unsigned char *mbi = NULL; +u32 mbi_len; +u8 mbi_initialized = 0; + +/* GNVS needs to be updated by an 0xEA PM Trap (B2) after it has been located + * by coreboot. + */ +global_nvs_t *gnvs = (global_nvs_t *)0x0; +void *tcg = (void *)0x0; +void *smi1 = (void *)0x0; + +/** + * @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_spew("PM1_STS: "); + if (pm1_sts & (1 << 15)) printk_spew("WAK "); + if (pm1_sts & (1 << 14)) printk_spew("PCIEXPWAK "); + if (pm1_sts & (1 << 11)) printk_spew("PRBTNOR "); + if (pm1_sts & (1 << 10)) printk_spew("RTC "); + if (pm1_sts & (1 << 8)) printk_spew("PWRBTN "); + if (pm1_sts & (1 << 5)) printk_spew("GBL "); + if (pm1_sts & (1 << 4)) printk_spew("BM "); + if (pm1_sts & (1 << 0)) printk_spew("TMROF "); + printk_spew("\n"); + int reg16 = inw(pmbase + PM1_EN); + printk_spew("PM1_EN: %x\n", reg16); +} + +/** + * @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"); +} + +/* 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" + +int southbridge_io_trap_handler(int smif) +{ + switch (smif) { + case 0x32: + printk_debug("OS Init\n"); + /* gnvs->smif: + * On success, the IO Trap Handler returns 0 + * On failure, the IO Trap Handler returns a value != 0 + */ + gnvs->smif = 0; + return 1; /* IO trap handled */ + } + + /* Not handled */ + return 0; +} + +/** + * @brief Set the EOS bit + */ +void southbridge_smi_set_eos(void) +{ + u8 reg8; + + reg8 = inb(pmbase + SMI_EN); + reg8 |= EOS; + outb(reg8, pmbase + SMI_EN); +} + +static void busmaster_disable_on_bus(int bus) +{ + int slot, func; + unsigned int val; + unsigned char hdr; + + for (slot = 0; slot < 0x20; slot++) { + for (func = 0; func < 8; func++) { + u32 reg32; + device_t dev = PCI_DEV(bus, slot, func); + + val = pci_read_config32(dev, PCI_VENDOR_ID); + + if (val == 0xffffffff || val == 0x00000000 || + val == 0x0000ffff || val == 0xffff0000) + continue; + + /* Disable Bus Mastering for this one device */ + reg32 = pci_read_config32(dev, PCI_COMMAND); + reg32 &= ~PCI_COMMAND_MASTER; + pci_write_config32(dev, PCI_COMMAND, reg32); + + /* If this is a bridge, then follow it. */ + hdr = pci_read_config8(dev, PCI_HEADER_TYPE); + hdr &= 0x7f; + if (hdr == PCI_HEADER_TYPE_BRIDGE || + hdr == PCI_HEADER_TYPE_CARDBUS) { + unsigned int buses; + buses = pci_read_config32(dev, PCI_PRIMARY_BUS); + busmaster_disable_on_bus((buses >> 8) & 0xff); + } + } + } +} + + +static void southbridge_smi_sleep(unsigned int node, smm_state_save_area_t *state_save) +{ + u8 reg8; + u32 reg32; + u8 slp_typ; + /* FIXME: the power state on boot should be read from + * CMOS or even better from GNVS. Right now it's hard + * coded at compile time. + */ + u8 s5pwr = CONFIG_MAINBOARD_POWER_ON_AFTER_POWER_FAIL; + + /* First, disable further SMIs */ + reg8 = inb(pmbase + SMI_EN); + reg8 &= ~SLP_SMI_EN; + outb(reg8, pmbase + SMI_EN); + + /* Figure out SLP_TYP */ + reg32 = inl(pmbase + PM1_CNT); + printk_spew("SMI#: SLP = 0x%08x\n", reg32); + slp_typ = (reg32 >> 10) & 7; + + /* Next, do the deed. + */ + + switch (slp_typ) { + case 0: printk_debug("SMI#: Entering S0 (On)\n"); break; + case 1: printk_debug("SMI#: Entering S1 (Assert STPCLK#)\n"); break; + case 5: + printk_debug("SMI#: Entering S3 (Suspend-To-RAM)\n"); + /* Invalidate the cache before going to S3 */ + wbinvd(); + break; + case 6: printk_debug("SMI#: Entering S4 (Suspend-To-Disk)\n"); break; + case 7: + printk_debug("SMI#: Entering S5 (Soft Power off)\n"); + + outl(0, pmbase + GPE0_EN); + + /* Should we keep the power state after a power loss? + * In case the setting is "ON" or "OFF" we don't have + * to do anything. But if it's "KEEP" we have to switch + * to "OFF" before entering S5. + */ + if (s5pwr == MAINBOARD_POWER_KEEP) { + reg8 = pci_read_config8(PCI_DEV(0, 0x1f, 0), GEN_PMCON_3); + reg8 |= 1; + pci_write_config8(PCI_DEV(0, 0x1f, 0), GEN_PMCON_3, reg8); + } + + /* also iterates over all bridges on bus 0 */ + busmaster_disable_on_bus(0); + break; + default: printk_debug("SMI#: ERROR: SLP_TYP reserved\n"); break; + } + + /* Write back to the SLP register to cause the originally intended + * event again. We need to set BIT13 (SLP_EN) though to make the + * sleep happen. + */ + outl(reg32 | SLP_EN, pmbase + PM1_CNT); + + /* In most sleep states, the code flow of this function ends at + * the line above. However, if we entered sleep state S1 and wake + * up again, we will continue to execute code in this function. + */ + reg32 = inl(pmbase + PM1_CNT); + if (reg32 & SCI_EN) { + /* The OS is not an ACPI OS, so we set the state to S0 */ + reg32 &= ~(SLP_EN | SLP_TYP); + outl(reg32, pmbase + PM1_CNT); + } +} + +static void southbridge_smi_apmc(unsigned int node, smm_state_save_area_t *state_save) +{ + u32 pmctrl; + u8 reg8; + + /* Emulate B2 register as the FADT / Linux expects it */ + + reg8 = inb(APM_CNT); + switch (reg8) { + case CST_CONTROL: + /* Calling this function seems to cause + * some kind of race condition in Linux + * and causes a kernel oops + */ + printk_debug("C-state control\n"); + break; + case PST_CONTROL: + /* Calling this function seems to cause + * some kind of race condition in Linux + * and causes a kernel oops + */ + printk_debug("P-state control\n"); + break; + case ACPI_DISABLE: + pmctrl = inl(pmbase + PM1_CNT); + pmctrl &= ~SCI_EN; + outl(pmctrl, pmbase + PM1_CNT); + printk_debug("SMI#: ACPI disabled.\n"); + break; + case ACPI_ENABLE: + pmctrl = inl(pmbase + PM1_CNT); + pmctrl |= SCI_EN; + outl(pmctrl, pmbase + PM1_CNT); + printk_debug("SMI#: ACPI enabled.\n"); + break; + case GNVS_UPDATE: + if (smm_initialized) { + printk_debug("SMI#: SMM structures already initialized!\n"); + return; + } + gnvs = *(global_nvs_t **)0x500; + tcg = *(void **)0x504; + smi1 = *(void **)0x508; + smm_initialized = 1; + printk_debug("SMI#: Setting up structures to %p, %p, %p\n", gnvs, tcg, smi1); + break; + case MBI_UPDATE: // FIXME + if (mbi_initialized) { + printk_debug("SMI#: mbi already registered!\n"); + return; + } + mbi = *(void **)0x500; + mbi_len = *(u32 *)0x504; + mbi_initialized = 1; + printk_debug("SMI#: Registered MBI at %p (%d bytes)\n", mbi, mbi_len); + break; + + default: + printk_debug("SMI#: Unknown function APM_CNT=%02x\n", reg8); + } +} + +static void southbridge_smi_pm1(unsigned int node, smm_state_save_area_t *state_save) +{ + u16 pm1_sts; + + pm1_sts = reset_pm1_status(); + dump_pm1_status(pm1_sts); + + /* While OSPM is not active, poweroff immediately + * on a power button event. + */ + if (pm1_sts & PWRBTN_STS) { + // power button pressed + u32 reg32; + reg32 = (7 << 10) | (1 << 13); + outl(reg32, pmbase + PM1_CNT); + } +} + +static void southbridge_smi_gpe0(unsigned int node, smm_state_save_area_t *state_save) +{ + u32 gpe0_sts; + + gpe0_sts = reset_gpe0_status(); + dump_gpe0_status(gpe0_sts); +} + +void __attribute__((weak)) mainboard_smi_gpi(u16 gpi_sts); + +static void southbridge_smi_gpi(unsigned int node, smm_state_save_area_t *state_save) +{ + u16 reg16; + reg16 = inw(pmbase + ALT_GP_SMI_STS); + outl(reg16, pmbase + ALT_GP_SMI_STS); + + reg16 &= inw(pmbase + ALT_GP_SMI_EN); + + if (mainboard_smi_gpi) { + mainboard_smi_gpi(reg16); + } else { + if (reg16) + printk_debug("GPI (mask %04x)\n",reg16); + } +} + +static void southbridge_smi_mc(unsigned int node, smm_state_save_area_t *state_save) +{ + u32 reg32; + + reg32 = inl(pmbase + SMI_EN); + + /* Are periodic SMIs enabled? */ + if ((reg32 & MCSMI_EN) == 0) + return; + + printk_debug("Microcontroller SMI.\n"); +} + + + +static void southbridge_smi_tco(unsigned int node, smm_state_save_area_t *state_save) +{ + u32 tco_sts; + + tco_sts = reset_tco_status(); + + /* Any TCO event? */ + if (!tco_sts) + return; + + if (tco_sts & (1 << 8)) { // BIOSWR + u8 bios_cntl; + + bios_cntl = pci_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"); + pci_write_config32(PCI_DEV(0, 0x1f, 0), 0xdc, (bios_cntl & ~1)); + } /* No else for now? */ + } else if (tco_sts & (1 << 3)) { /* TIMEOUT */ + /* Handle TCO timeout */ + printk_debug("TCO Timeout.\n"); + } else if (!tco_sts) { + dump_tco_status(tco_sts); + } +} + +static void southbridge_smi_periodic(unsigned int node, smm_state_save_area_t *state_save) +{ + u32 reg32; + + reg32 = inl(pmbase + SMI_EN); + + /* Are periodic SMIs enabled? */ + if ((reg32 & PERIODIC_EN) == 0) + return; + + printk_debug("Periodic SMI.\n"); +} + +static void southbridge_smi_monitor(unsigned int node, smm_state_save_area_t *state_save) +{ +#define IOTRAP(x) (trap_sts & (1 << x)) +#if 0 + u32 trap_sts, trap_cycle; + u32 data, mask = 0; + int i; + + trap_sts = RCBA32(0x1e00); // TRSR - Trap Status Register + RCBA32(0x1e00) = trap_sts; // Clear trap(s) in TRSR + + trap_cycle = RCBA32(0x1e10); + for (i=16; i<20; i++) { + if (trap_cycle & (1 << i)) + mask |= (0xff << ((i - 16) << 2)); + } + + + /* IOTRAP(3) SMI function call */ + if (IOTRAP(3)) { + if (gnvs && gnvs->smif) + io_trap_handler(gnvs->smif); // call function smif + return; + } + + /* IOTRAP(2) currently unused + * IOTRAP(1) currently unused */ + + /* IOTRAP(0) SMIC */ + if (IOTRAP(0)) { + if (!(trap_cycle & (1 << 24))) { // It's a write + printk_debug("SMI1 command\n"); + data = RCBA32(0x1e18); + data &= mask; + // if (smi1) + // southbridge_smi_command(data); + // return; + } + // Fall through to debug + } + + printk_debug(" trapped io address = 0x%x\n", trap_cycle & 0xfffc); + for (i=0; i < 4; i++) if(IOTRAP(i)) printk_debug(" TRAP = %d\n", i); + printk_debug(" AHBE = %x\n", (trap_cycle >> 16) & 0xf); + printk_debug(" MASK = 0x%08x\n", mask); + printk_debug(" read/write: %s\n", (trap_cycle & (1 << 24)) ? "read" : "write"); + + if (!(trap_cycle & (1 << 24))) { + /* Write Cycle */ + data = RCBA32(0x1e18); + printk_debug(" iotrap written data = 0x%08x\n", data); + } +#endif +#undef IOTRAP +} + +typedef void (*smi_handler)(unsigned int node, + smm_state_save_area_t *state_save); + +smi_handler southbridge_smi[32] = { + NULL, // [0] reserved + NULL, // [1] reserved + NULL, // [2] BIOS_STS + NULL, // [3] LEGACY_USB_STS + southbridge_smi_sleep, // [4] SLP_SMI_STS + southbridge_smi_apmc, // [5] APM_STS + NULL, // [6] SWSMI_TMR_STS + NULL, // [7] reserved + southbridge_smi_pm1, // [8] PM1_STS + southbridge_smi_gpe0, // [9] GPE0_STS + southbridge_smi_gpi, // [10] GPI_STS + southbridge_smi_mc, // [11] MCSMI_STS + NULL, // [12] DEVMON_STS + southbridge_smi_tco, // [13] TCO_STS + southbridge_smi_periodic, // [14] PERIODIC_STS + NULL, // [15] SERIRQ_SMI_STS + NULL, // [16] SMBUS_SMI_STS + NULL, // [17] LEGACY_USB2_STS + NULL, // [18] INTEL_USB2_STS + NULL, // [19] reserved + NULL, // [20] PCI_EXP_SMI_STS + southbridge_smi_monitor, // [21] MONITOR_STS + NULL, // [22] reserved + NULL, // [23] reserved + NULL, // [24] reserved + NULL, // [25] EL_SMI_STS + NULL, // [26] SPI_STS + NULL, // [27] reserved + NULL, // [28] reserved + NULL, // [29] reserved + NULL, // [30] reserved + NULL // [31] reserved +}; + +/** + * @brief Interrupt handler for SMI# + * + * @param smm_revision revision of the smm state save map + */ + +void southbridge_smi_handler(unsigned int node, smm_state_save_area_t *state_save) +{ + int i, dump = 0; + u32 smi_sts; + + /* Update global variable pmbase */ + pmbase = pci_read_config16(PCI_DEV(0, 0x1f, 0), 0x40) & 0xfffc; + + /* 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(); + + /* Filter all non-enabled SMI events */ + // FIXME Double check, this clears MONITOR + // smi_sts &= inl(pmbase + SMI_EN); + + /* Call SMI sub handler for each of the status bits */ + for (i = 0; i < 31; i++) { + if (smi_sts & (1 << i)) { + if (southbridge_smi[i]) + southbridge_smi[i](node, state_save); + else { + printk_debug("SMI_STS[%d] occured, but no " + "handler available.\n", i); + dump = 1; + } + } + } + + if(dump) { + dump_smi_status(smi_sts); + } + +} diff --git a/src/southbridge/intel/i82801dx/i82801dx_tco_timer.c b/src/southbridge/intel/i82801dx/i82801dx_tco_timer.c new file mode 100644 index 0000000000..ea5485f799 --- /dev/null +++ b/src/southbridge/intel/i82801dx/i82801dx_tco_timer.c @@ -0,0 +1,37 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2008 Joseph Smith <joe@settoplinux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 + * + */ + +static void i82801dx_halt_tco_timer(void) +{ + device_t dev; + uint16_t halt_tco_timer; + + /* Set the LPC device statically. */ + dev = PCI_DEV(0x0, 0x1f, 0x0); + + /* Temporarily set ACPI base address (I/O space). */ + pci_write_config32(dev, PMBASE, (PMBASE_ADDR | 1)); + + /* Enable ACPI I/O. */ + pci_write_config8(dev, ACPI_CNTL, 0x10); + + /* Halt the TCO timer, preventing SMI and automatic reboot */ + outw(inw(PMBASE_ADDR + TCOBASE + TCO1_CNT) | (1 << 11), PMBASE_ADDR + TCOBASE + TCO1_CNT); +} |