diff options
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); +} |