summaryrefslogtreecommitdiff
path: root/src/southbridge/amd
diff options
context:
space:
mode:
authorMartin Roth <martin@se-eng.com>2012-12-05 16:07:11 -0700
committerMarc Jones <marcj303@gmail.com>2012-12-12 22:35:03 +0100
commite899e518d88f2f48927fba9006d02ea9e1e5a797 (patch)
treecc93e167283c3f5b3ab82725765bab058f5f6086 /src/southbridge/amd
parenta17fd056d4b4fb373f7821ccca74a80de14fc618 (diff)
SB800: Add IMC ROM and fan control.
Add configuration for AMD's IMC ROM and fan registers for cimx/sb800 platforms. - Allows user to add the IMC rom to the build and to configure the location of the "signature" between the allowed positions. - Allows for no fan control, manual setup of SB800 Fan registers, or setup of the IMC fan configuration registers. - Register configuration is done through devicetree.cb. No files need to be added for new platform configuration. - Initial setup is for Persimmon, but may be extended to any cimx/sb800 platform. Change-Id: Ib06408d794988cbb29eed6adbeeadea8b2629bae Signed-off-by: Martin Roth <martin@se-eng.com> Reviewed-on: http://review.coreboot.org/1977 Tested-by: build bot (Jenkins) Reviewed-by: Patrick Georgi <patrick@georgi-clan.de> Reviewed-by: Marc Jones <marcj303@gmail.com>
Diffstat (limited to 'src/southbridge/amd')
-rw-r--r--src/southbridge/amd/cimx/sb800/Kconfig95
-rw-r--r--src/southbridge/amd/cimx/sb800/Makefile.inc40
-rw-r--r--src/southbridge/amd/cimx/sb800/SBPLATFORM.h4
-rw-r--r--src/southbridge/amd/cimx/sb800/cfg.c1
-rw-r--r--src/southbridge/amd/cimx/sb800/chip.h198
-rw-r--r--src/southbridge/amd/cimx/sb800/fan.c311
-rw-r--r--src/southbridge/amd/cimx/sb800/fan.h152
-rw-r--r--src/southbridge/amd/cimx/sb800/late.c7
8 files changed, 807 insertions, 1 deletions
diff --git a/src/southbridge/amd/cimx/sb800/Kconfig b/src/southbridge/amd/cimx/sb800/Kconfig
index 4ac2094d4d..2461d97fa4 100644
--- a/src/southbridge/amd/cimx/sb800/Kconfig
+++ b/src/southbridge/amd/cimx/sb800/Kconfig
@@ -130,5 +130,100 @@ config S3_VOLATILE_POS
For a system with S3 feature, the BIOS needs to save some data to
non-volitile storage at cold boot stage.
+config SB800_IMC_FWM
+ bool "Add IMC firmware"
+ default n
+ help
+ Add SB800 / Hudson 1 IMC Firmware to support the onboard fan control.
+ Please contact AMD to obtain the related firmware.
+
+if SB800_IMC_FWM
+
+config SB800_IMC_FWM_FILE
+ string "IMC firmware path and filename"
+ default "3rdparty/southbridge/amd/sb800/imc.bin"
+
+choice
+ prompt "SB800 Firmware ROM Position"
+
+config SB800_FWM_AT_FFFA0000
+ bool "0xFFFA0000"
+ help
+ The IMC and GEC ROMs requires a 'signature' located at one of several
+ fixed locations in memory. The location used shouldn't matter, just
+ select an area that doesn't conflict with anything else.
+
+config SB800_FWM_AT_FFF20000
+ bool "0xFFF20000"
+ help
+ The IMC and GEC ROMs requires a 'signature' located at one of several
+ fixed locations in memory. The location used shouldn't matter, just
+ select an area that doesn't conflict with anything else.
+
+config SB800_FWM_AT_FFE20000
+ depends on BOARD_ROMSIZE_KB_8192 || BOARD_ROMSIZE_KB_4096 || BOARD_ROMSIZE_KB_2048
+ bool "0xFFE20000"
+ help
+ The IMC and GEC ROMs requires a 'signature' located at one of several
+ fixed locations in memory. The location used shouldn't matter, just
+ select an area that doesn't conflict with anything else.
+
+config SB800_FWM_AT_FFC20000
+ depends on BOARD_ROMSIZE_KB_8192 || BOARD_ROMSIZE_KB_4096
+ bool "0xFFC20000"
+ help
+ The IMC and GEC ROMs requires a 'signature' located at one of several
+ fixed locations in memory. The location used shouldn't matter, just
+ select an area that doesn't conflict with anything else.
+
+config SB800_FWM_AT_FF820000
+ depends on BOARD_ROMSIZE_KB_8192
+ bool "0xFF820000"
+ help
+ The IMC and GEC ROMs requires a 'signature' located at one of several
+ fixed locations in memory. The location used shouldn't matter, just
+ select an area that doesn't conflict with anything else.
+
+endchoice
+
+config SB800_FWM_POSITION
+ hex
+ default 0xFFFA0000 if SB800_FWM_AT_FFFA0000
+ default 0xFFF20000 if SB800_FWM_AT_FFF20000
+ default 0xFFE20000 if SB800_FWM_AT_FFE20000
+ default 0xFFC20000 if SB800_FWM_AT_FFC20000
+ default 0xFF820000 if SB800_FWM_AT_FFE20000
+
+endif #SB800_IMC_FWM
+
+choice
+ prompt "Fan Control"
+ default SB800_NO_FAN_CONTROL
+ help
+ Select the method of SB800 fan control to be used. None would be
+ for either fixed maximum speed fans connected to the SB800 or for
+ an external chip controlling the fan speeds. Manual control sets
+ up the SB800 fan control registers. IMC fan control uses the SB800
+ IMC to actively control the fan speeds.
+
+config SB800_NO_FAN_CONTROL
+ bool "None"
+ help
+ No SB800 Fan control - Do not set up the SB800 fan control registers.
+
+config SB800_MANUAL_FAN_CONTROL
+ bool "Manual"
+ help
+ Configure the SB800 fan control registers in devicetree.cb.
+
+config SB800_IMC_FAN_CONTROL
+ bool "IMC Based"
+ depends on SB800_IMC_FWM
+ help
+ Set up the SB800 to use the IMC based Fan controller. This requires
+ the IMC rom from AMD. Configure the registers in devicetree.cb.
+
+endchoice
+
endif #SOUTHBRIDGE_AMD_CIMX_SB800
diff --git a/src/southbridge/amd/cimx/sb800/Makefile.inc b/src/southbridge/amd/cimx/sb800/Makefile.inc
index bea976356a..064cbc7122 100644
--- a/src/southbridge/amd/cimx/sb800/Makefile.inc
+++ b/src/southbridge/amd/cimx/sb800/Makefile.inc
@@ -27,6 +27,8 @@ romstage-y += smbus.c
ramstage-y += cfg.c
ramstage-y += late.c
+ramstage-$(CONFIG_SB800_MANUAL_FAN_CONTROL) += fan.c
+ramstage-$(CONFIG_SB800_IMC_FAN_CONTROL) += fan.c
ramstage-$(CONFIG_HAVE_ACPI_RESUME) += spi.c
ramstage-$(CONFIG_GENERATE_ACPI_TABLES) += fadt.c
@@ -53,3 +55,41 @@ ifeq ($(CONFIG_SB800_SATA_RAID), y)
raid/misc.bin-position := $(CONFIG_RAID_MISC_ROM_POSITION)
raid/misc.bin-type := raw
endif
+
+ifeq ($(CONFIG_SB800_IMC_FWM), y)
+
+# ROMSIG At ROMBASE + 0x20000:
+# +-----------+---------------+----------------+------------+
+# |0x55AA55AA |EC ROM Address |GEC ROM Address | |
+# +-----------+---------------+----------------+------------+
+# EC ROM should be 64K aligned.
+SB800_FWM_POSITION=$(shell printf %u $(CONFIG_SB800_FWM_POSITION))
+#assume the cbfs header is less than 128 bytes.
+ROMSIG_SIZE=16
+
+SB800_IMC_POSITION_UNALIGN=$(shell echo $(SB800_FWM_POSITION) $(ROMSIG_SIZE) 128 65535 | awk '{print $$1 + $$2 + $$3 + $$4}')
+SB800_IMC_POSITION=$(shell echo $(SB800_IMC_POSITION_UNALIGN) | awk '{print $$1 - $$1 % 65536}')
+
+$(obj)/coreboot_SB800_romsig.bin: \
+ $(call strip_quotes, $(CONFIG_SB800_IMC_FWM_FILE)) \
+ $(obj)/config.h \
+ $(obj)/mainboard/$(MAINBOARDDIR)/static.c
+ echo " SB800 FW $@"
+ for fwm in 1437226410 \
+ $(SB800_IMC_POSITION) \
+ 0 \
+ 0 ; do \
+ echo $$fwm | LC_ALL=C awk '{printf ("%c%c%c%c", $$1 % 256, int($$1/256) % 256, int($$1/65536) % 256, int($$1/16777216));}'; \
+ done > $@
+
+cbfs-files-y += SB800/fwm
+SB800/fwm-file := $(obj)/coreboot_SB800_romsig.bin
+SB800/fwm-position := $(SB800_FWM_POSITION)
+SB800/fwm-type := raw
+
+cbfs-files-y += SB800/imc
+SB800/imc-file := $(call strip_quotes, $(CONFIG_SB800_IMC_FWM_FILE))
+SB800/imc-position := $(SB800_IMC_POSITION)
+SB800/imc-type := raw
+
+endif
diff --git a/src/southbridge/amd/cimx/sb800/SBPLATFORM.h b/src/southbridge/amd/cimx/sb800/SBPLATFORM.h
index 22d7724890..1966eb469c 100644
--- a/src/southbridge/amd/cimx/sb800/SBPLATFORM.h
+++ b/src/southbridge/amd/cimx/sb800/SBPLATFORM.h
@@ -57,6 +57,10 @@ typedef union _PCI_ADDR {
#endif
#define FIXUP_PTR(ptr) ptr
+#if CONFIG_SB800_IMC_FWM
+ #define IMC_ENABLE_OVER_WRITE 0x01
+#endif
+
#include <console/console.h>
#include "AmdSbLib.h"
#include "Amd.h"
diff --git a/src/southbridge/amd/cimx/sb800/cfg.c b/src/southbridge/amd/cimx/sb800/cfg.c
index 71fea672d5..ebc9918890 100644
--- a/src/southbridge/amd/cimx/sb800/cfg.c
+++ b/src/southbridge/amd/cimx/sb800/cfg.c
@@ -112,6 +112,7 @@ void sb800_cimx_config(AMDSBCFG *sb_config)
sb_config->BuildParameters.PCIBSsid = PCIB_SSID;
sb_config->BuildParameters.SpreadSpectrumType = Spread_Spectrum_Type;
sb_config->BuildParameters.HpetBase = HPET_BASE_ADDRESS;
+ sb_config->BuildParameters.ImcEnableOverWrite = IMC_ENABLE_OVER_WRITE;
/* General */
sb_config->SpreadSpectrum = SPREAD_SPECTRUM;
diff --git a/src/southbridge/amd/cimx/sb800/chip.h b/src/southbridge/amd/cimx/sb800/chip.h
index 7fc7b88c76..4742c6fa0c 100644
--- a/src/southbridge/amd/cimx/sb800/chip.h
+++ b/src/southbridge/amd/cimx/sb800/chip.h
@@ -19,6 +19,7 @@
#ifndef _CIMX_SB800_CHIP_H_
#define _CIMX_SB800_CHIP_H_
+#include "fan.h" /* include for #defines used in devicetree.cb */
/*
* configuration set in mainboard/devicetree.cb
@@ -35,6 +36,201 @@ struct southbridge_amd_cimx_sb800_config
{
u32 boot_switch_sata_ide : 1;
u8 gpp_configuration;
-};
+ /*
+ * SB800 IMC and fan control
+ */
+
+ u16 imc_port_address;
+
+ u32 fan0_enabled : 1;
+ u32 fan1_enabled : 1;
+ u32 fan2_enabled : 1;
+ u32 fan3_enabled : 1;
+ u32 fan4_enabled : 1;
+ u32 imc_fan_zone0_enabled : 1;
+ u32 imc_fan_zone1_enabled : 1;
+ u32 imc_fan_zone2_enabled : 1;
+ u32 imc_fan_zone3_enabled : 1;
+ u32 imc_tempin0_enabled : 1;
+ u32 imc_tempin1_enabled : 1;
+ u32 imc_tempin2_enabled : 1;
+ u32 imc_tempin3_enabled : 1;
+
+ union {
+ struct {
+ u8 fan0_control_reg_value;
+ u8 fan0_frequency_reg_value;
+ u8 fan0_low_duty_reg_value;
+ u8 fan0_med_duty_reg_value;
+ u8 fan0_multiplier_reg_value;
+ u8 fan0_low_temp_lo_reg_value;
+ u8 fan0_low_temp_hi_reg_value;
+ u8 fan0_med_temp_lo_reg_value;
+ u8 fan0_med_temp_hi_reg_value;
+ u8 fan0_high_temp_lo_reg_value;
+ u8 fan0_high_temp_hi_reg_value;
+ u8 fan0_linear_range_reg_value;
+ u8 fan0_linear_hold_reg_value;
+ };
+ u8 fan0_config_vals[FAN_REGISTER_COUNT];
+ };
+
+ union {
+ struct {
+ u8 fan1_control_reg_value;
+ u8 fan1_frequency_reg_value;
+ u8 fan1_low_duty_reg_value;
+ u8 fan1_med_duty_reg_value;
+ u8 fan1_multiplier_reg_value;
+ u8 fan1_low_temp_lo_reg_value;
+ u8 fan1_low_temp_hi_reg_value;
+ u8 fan1_med_temp_lo_reg_value;
+ u8 fan1_med_temp_hi_reg_value;
+ u8 fan1_high_temp_lo_reg_value;
+ u8 fan1_high_temp_hi_reg_value;
+ u8 fan1_linear_range_reg_value;
+ u8 fan1_linear_hold_reg_value;
+ };
+ u8 fan1_config_vals[FAN_REGISTER_COUNT];
+ };
+
+ union {
+ struct {
+ u8 fan2_control_reg_value;
+ u8 fan2_frequency_reg_value;
+ u8 fan2_low_duty_reg_value;
+ u8 fan2_med_duty_reg_value;
+ u8 fan2_multiplier_reg_value;
+ u8 fan2_low_temp_lo_reg_value;
+ u8 fan2_low_temp_hi_reg_value;
+ u8 fan2_med_temp_lo_reg_value;
+ u8 fan2_med_temp_hi_reg_value;
+ u8 fan2_high_temp_lo_reg_value;
+ u8 fan2_high_temp_hi_reg_value;
+ u8 fan2_linear_range_reg_value;
+ u8 fan2_linear_hold_reg_value;
+ };
+ u8 fan2_config_vals[FAN_REGISTER_COUNT];
+ };
+
+ union {
+ struct {
+ u8 fan3_control_reg_value;
+ u8 fan3_frequency_reg_value;
+ u8 fan3_low_duty_reg_value;
+ u8 fan3_med_duty_reg_value;
+ u8 fan3_multiplier_reg_value;
+ u8 fan3_low_temp_lo_reg_value;
+ u8 fan3_low_temp_hi_reg_value;
+ u8 fan3_med_temp_lo_reg_value;
+ u8 fan3_med_temp_hi_reg_value;
+ u8 fan3_high_temp_lo_reg_value;
+ u8 fan3_high_temp_hi_reg_value;
+ u8 fan3_linear_range_reg_value;
+ u8 fan3_linear_hold_reg_value;
+ };
+ u8 fan3_config_vals[FAN_REGISTER_COUNT];
+ };
+
+ union {
+ struct {
+ u8 fan4_control_reg_value;
+ u8 fan4_frequency_reg_value;
+ u8 fan4_low_duty_reg_value;
+ u8 fan4_med_duty_reg_value;
+ u8 fan4_multiplier_reg_value;
+ u8 fan4_low_temp_lo_reg_value;
+ u8 fan4_low_temp_hi_reg_value;
+ u8 fan4_med_temp_lo_reg_value;
+ u8 fan4_med_temp_hi_reg_value;
+ u8 fan4_high_temp_lo_reg_value;
+ u8 fan4_high_temp_hi_reg_value;
+ u8 fan4_linear_range_reg_value;
+ u8 fan4_linear_hold_reg_value;
+ };
+ u8 fan4_config_vals[FAN_REGISTER_COUNT];
+ };
+
+ union {
+ struct {
+ u8 imc_zone0_mode1;
+ u8 imc_zone0_mode2;
+ u8 imc_zone0_temp_offset;
+ u8 imc_zone0_hysteresis;
+ u8 imc_zone0_smbus_addr;
+ u8 imc_zone0_smbus_num;
+ u8 imc_zone0_pwm_step;
+ u8 imc_zone0_ramping;
+ };
+ u8 imc_zone0_config_vals[IMC_FAN_CONFIG_COUNT];
+ };
+ u8 imc_zone0_thresholds[IMC_FAN_THRESHOLD_COUNT];
+ u8 imc_zone0_fanspeeds[IMC_FAN_SPEED_COUNT];
+
+ union {
+ struct {
+ u8 imc_zone1_mode1;
+ u8 imc_zone1_mode2;
+ u8 imc_zone1_temp_offset;
+ u8 imc_zone1_hysteresis;
+ u8 imc_zone1_smbus_addr;
+ u8 imc_zone1_smbus_num;
+ u8 imc_zone1_pwm_step;
+ u8 imc_zone1_ramping;
+ };
+ u8 imc_zone1_config_vals[IMC_FAN_CONFIG_COUNT];
+ };
+ u8 imc_zone1_thresholds[IMC_FAN_THRESHOLD_COUNT];
+ u8 imc_zone1_fanspeeds[IMC_FAN_SPEED_COUNT];
+
+ union {
+ struct {
+ u8 imc_zone2_mode1;
+ u8 imc_zone2_mode2;
+ u8 imc_zone2_temp_offset;
+ u8 imc_zone2_hysteresis;
+ u8 imc_zone2_smbus_addr;
+ u8 imc_zone2_smbus_num;
+ u8 imc_zone2_pwm_step;
+ u8 imc_zone2_ramping;
+ };
+ u8 imc_zone2_config_vals[IMC_FAN_CONFIG_COUNT];
+ };
+ u8 imc_zone2_thresholds[IMC_FAN_THRESHOLD_COUNT];
+ u8 imc_zone2_fanspeeds[IMC_FAN_SPEED_COUNT];
+
+ union {
+ struct {
+ u8 imc_zone3_mode1;
+ u8 imc_zone3_mode2;
+ u8 imc_zone3_temp_offset;
+ u8 imc_zone3_hysteresis;
+ u8 imc_zone3_smbus_addr;
+ u8 imc_zone3_smbus_num;
+ u8 imc_zone3_pwm_step;
+ u8 imc_zone3_ramping;
+ };
+ u8 imc_zone3_config_vals[IMC_FAN_CONFIG_COUNT];
+ };
+ u8 imc_zone3_thresholds[IMC_FAN_THRESHOLD_COUNT];
+ u8 imc_zone3_fanspeeds[IMC_FAN_SPEED_COUNT];
+
+ u32 imc_tempin0_at;
+ u32 imc_tempin0_ct;
+ u8 imc_tempin0_tuning_param;
+
+ u32 imc_tempin1_at;
+ u32 imc_tempin1_ct;
+ u8 imc_tempin1_tuning_param;
+
+ u32 imc_tempin2_at;
+ u32 imc_tempin2_ct;
+ u8 imc_tempin2_tuning_param;
+
+ u32 imc_tempin3_at;
+ u32 imc_tempin3_ct;
+ u8 imc_tempin3_tuning_param;
+
+};
#endif /* _CIMX_SB800_CHIP_H_ */
diff --git a/src/southbridge/amd/cimx/sb800/fan.c b/src/southbridge/amd/cimx/sb800/fan.c
new file mode 100644
index 0000000000..115da6541a
--- /dev/null
+++ b/src/southbridge/amd/cimx/sb800/fan.c
@@ -0,0 +1,311 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2012 Sage Electronic Engineering, LLC
+ *
+ * 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 <southbridge/amd/cimx/cimx_util.h>
+#include <device/device.h> /* device_t */
+#include <device/pci.h> /* device_operations */
+#include "SBPLATFORM.h"
+#include "sb_cimx.h"
+#include "chip.h" /* struct southbridge_amd_cimx_sb800_config */
+#include "fan.h"
+
+void init_sb800_MANUAL_fans(device_t dev)
+{
+ int i;
+ struct southbridge_amd_cimx_sb800_config *sb_chip =
+ (struct southbridge_amd_cimx_sb800_config *)(dev->chip_info);
+
+ /* Init Fan 0 */
+ if (sb_chip->fan0_enabled)
+ for (i = 0; i < FAN_REGISTER_COUNT; i++)
+ pm2_iowrite(FAN_0_OFFSET + i, sb_chip->fan0_config_vals[i]);
+
+ /* Init Fan 1 */
+ if (sb_chip->fan1_enabled)
+ for (i = 0; i < FAN_REGISTER_COUNT; i++)
+ pm2_iowrite(FAN_1_OFFSET + i, sb_chip->fan1_config_vals[i]);
+
+ /* Init Fan 2 */
+ if (sb_chip->fan2_enabled)
+ for (i = 0; i < FAN_REGISTER_COUNT; i++)
+ pm2_iowrite(FAN_2_OFFSET + i, sb_chip->fan2_config_vals[i]);
+
+ /* Init Fan 3 */
+ if (sb_chip->fan3_enabled)
+ for (i = 0; i < FAN_REGISTER_COUNT; i++)
+ pm2_iowrite(FAN_3_OFFSET + i, sb_chip->fan3_config_vals[i]);
+
+ /* Init Fan 4 */
+ if (sb_chip->fan4_enabled)
+ for (i = 0; i < FAN_REGISTER_COUNT; i++)
+ pm2_iowrite(FAN_4_OFFSET + i, sb_chip->fan4_config_vals[i]);
+
+}
+
+void init_sb800_IMC_fans(device_t dev)
+{
+
+ AMDSBCFG sb_config;
+ unsigned char *message_ptr;
+ int i;
+ struct southbridge_amd_cimx_sb800_config *sb_chip =
+ (struct southbridge_amd_cimx_sb800_config *)(dev->chip_info);
+
+ /*
+ * The default I/O address of the IMC configuration register index
+ * port is 0x6E. Change the IMC Config port I/O Address if it
+ * conflicts with other components in the system.
+ *
+ * Device 20, Function 3, Reg 0xA4
+ * [0]: if 1, the address specified in IMC_PortAddress is used.
+ * [15:1] IMC_PortAddress bits 15:1 (0x17 - address 0x2E )
+ */
+
+ pci_write_config16(dev, 0xA4, sb_chip->imc_port_address | 0x01);
+
+
+ /*
+ * Do an initial manual setup of the fans for things like polarity
+ * and frequency.
+ */
+ init_sb800_MANUAL_fans(dev);
+
+ /*
+ * FLAG for Func 81/83/85/89 support (1=On,0=Off)
+ * Bit0-3 = Func 81 Zone0-Zone3
+ * Bit4-7 = Func 83 Zone0-Zone3
+ * Bit8-11 = Func 85 Zone0-Zone3
+ * Bit12-15 = Func 89 Tempin Channel0-Channel3
+ */
+ sb_config.Pecstruct.IMCFUNSupportBitMap = 0;
+
+/*
+ ********** Zone 0 **********
+ */
+if (sb_chip->imc_fan_zone0_enabled) {
+
+ sb_config.Pecstruct.IMCFUNSupportBitMap |= IMC_ENABLE_ZONE0;
+
+ /* EC LDN9 function 81 zone 0 */
+ sb_config.Pecstruct.MSGFun81zone0MSGREG0 = 0x00;
+ sb_config.Pecstruct.MSGFun81zone0MSGREG1 = IMC_ZONE0;
+ message_ptr = &sb_config.Pecstruct.MSGFun81zone0MSGREG2;
+ for (i = 0; i < IMC_FAN_CONFIG_COUNT ; i++ )
+ *(message_ptr + i) = sb_chip->imc_zone0_config_vals[i];
+
+ /* EC LDN9 function 83 zone 0 - Temperature Thresholds */
+ sb_config.Pecstruct.MSGFun83zone0MSGREG0 = 0x00;
+ sb_config.Pecstruct.MSGFun83zone0MSGREG1 = IMC_ZONE0;
+ sb_config.Pecstruct.MSGFun83zone0MSGREGB = 0x00;
+ message_ptr = &sb_config.Pecstruct.MSGFun83zone0MSGREG2;
+ for (i = 0; i < IMC_FAN_THRESHOLD_COUNT ; i++ )
+ *(message_ptr + i) = sb_chip->imc_zone0_thresholds[i];
+
+ /*EC LDN9 function 85 zone 0 - Fan Speeds */
+ sb_config.Pecstruct.MSGFun85zone0MSGREG0 = 0x00;
+ sb_config.Pecstruct.MSGFun85zone0MSGREG1 = IMC_ZONE0;
+ message_ptr = &sb_config.Pecstruct.MSGFun85zone0MSGREG2;
+ for (i = 0; i < IMC_FAN_SPEED_COUNT ; i++ )
+ *(message_ptr + i) = sb_chip->imc_zone0_fanspeeds[i];
+
+}
+
+/*
+ ********** Zone 1 **********
+ */
+if (sb_chip->imc_fan_zone1_enabled) {
+
+ sb_config.Pecstruct.IMCFUNSupportBitMap |= IMC_ENABLE_ZONE1;
+
+ /* EC LDN9 function 81 zone 1 */
+ sb_config.Pecstruct.MSGFun81zone1MSGREG0 = 0x00;
+ sb_config.Pecstruct.MSGFun81zone1MSGREG1 = IMC_ZONE1;
+ message_ptr = &sb_config.Pecstruct.MSGFun81zone1MSGREG2;
+ for (i = 0; i < IMC_FAN_CONFIG_COUNT ; i++ )
+ *(message_ptr + i) = sb_chip->imc_zone1_config_vals[i];
+
+ /* EC LDN9 function 83 zone 1 - Temperature Thresholds */
+ sb_config.Pecstruct.MSGFun83zone1MSGREG0 = 0x00;
+ sb_config.Pecstruct.MSGFun83zone1MSGREG1 = IMC_ZONE1;
+ sb_config.Pecstruct.MSGFun83zone1MSGREGB = 0x00;
+ message_ptr = &sb_config.Pecstruct.MSGFun83zone1MSGREG2;
+ for (i = 0; i < IMC_FAN_THRESHOLD_COUNT ; i++ )
+ *(message_ptr + i) = sb_chip->imc_zone1_thresholds[i];
+
+ /* EC LDN9 function 85 zone 1 - Fan Speeds */
+ sb_config.Pecstruct.MSGFun85zone1MSGREG0 = 0x00;
+ sb_config.Pecstruct.MSGFun85zone1MSGREG1 = IMC_ZONE1;
+ message_ptr = &sb_config.Pecstruct.MSGFun85zone1MSGREG2;
+ for (i = 0; i < IMC_FAN_SPEED_COUNT ; i++ )
+ *(message_ptr + i) = sb_chip->imc_zone1_fanspeeds[i];
+
+}
+
+
+/*
+ ********** Zone 2 **********
+ */
+if (sb_chip->imc_fan_zone2_enabled) {
+
+ sb_config.Pecstruct.IMCFUNSupportBitMap |= IMC_ENABLE_ZONE2;
+
+ /* EC LDN9 function 81 zone 2 */
+ sb_config.Pecstruct.MSGFun81zone2MSGREG0 = 0x00;
+ sb_config.Pecstruct.MSGFun81zone2MSGREG1 = IMC_ZONE2;
+ message_ptr = &sb_config.Pecstruct.MSGFun81zone2MSGREG2;
+ for (i = 0; i < IMC_FAN_CONFIG_COUNT ; i++ )
+ *(message_ptr + i) = sb_chip->imc_zone2_config_vals[i];
+
+ /* EC LDN9 function 83 zone 2 */
+ sb_config.Pecstruct.MSGFun83zone2MSGREG0 = 0x00;
+ sb_config.Pecstruct.MSGFun83zone2MSGREG1 = IMC_ZONE2;
+ sb_config.Pecstruct.MSGFun83zone2MSGREGB = 0x00;
+ message_ptr = &sb_config.Pecstruct.MSGFun83zone2MSGREG2;
+ for (i = 0; i < IMC_FAN_THRESHOLD_COUNT ; i++ )
+ *(message_ptr + i) = sb_chip->imc_zone2_thresholds[i];
+
+ /* EC LDN9 function 85 zone 2 */
+ sb_config.Pecstruct.MSGFun85zone2MSGREG0 = 0x00;
+ sb_config.Pecstruct.MSGFun85zone2MSGREG1 = IMC_ZONE2;
+ message_ptr = &sb_config.Pecstruct.MSGFun85zone2MSGREG2;
+ for (i = 0; i < IMC_FAN_SPEED_COUNT ; i++ )
+ *(message_ptr + i) = sb_chip->imc_zone2_fanspeeds[i];
+
+}
+
+/*
+ ********** Zone 3 **********
+ */
+
+if (sb_chip->imc_fan_zone3_enabled) {
+
+ sb_config.Pecstruct.IMCFUNSupportBitMap |= IMC_ENABLE_ZONE3;
+
+ /* EC LDN9 function 81 zone 3 */
+ sb_config.Pecstruct.MSGFun81zone3MSGREG0 = 0x00;
+ sb_config.Pecstruct.MSGFun81zone3MSGREG1 = IMC_ZONE3;
+ message_ptr = &sb_config.Pecstruct.MSGFun81zone3MSGREG2;
+ for (i = 0; i < IMC_FAN_CONFIG_COUNT ; i++ )
+ *(message_ptr + i) = sb_chip->imc_zone3_config_vals[i];
+
+ /* EC LDN9 function 83 zone 3 */
+ sb_config.Pecstruct.MSGFun83zone3MSGREG0 = 0x00;
+ sb_config.Pecstruct.MSGFun83zone3MSGREG1 = IMC_ZONE3;
+ sb_config.Pecstruct.MSGFun83zone3MSGREGB = 0x00;
+ message_ptr = &sb_config.Pecstruct.MSGFun83zone3MSGREG2;
+ for (i = 0; i < IMC_FAN_THRESHOLD_COUNT ; i++ )
+ *(message_ptr + i) = sb_chip->imc_zone3_thresholds[i];
+
+ /* EC LDN9 function 85 zone 3 */
+ sb_config.Pecstruct.MSGFun85zone3MSGREG0 = 0x00;
+ sb_config.Pecstruct.MSGFun85zone3MSGREG1 = IMC_ZONE3;
+ message_ptr = &sb_config.Pecstruct.MSGFun85zone3MSGREG2;
+ for (i = 0; i < IMC_FAN_SPEED_COUNT ; i++ )
+ *(message_ptr + i) = sb_chip->imc_zone3_fanspeeds[i];
+
+}
+
+ /*
+ * EC LDN9 funtion 89 - Set HWM TEMPIN Temperature Calculation Parameters
+ * This function provides the critical parameters of the HWM TempIn
+ * sensors, IMC would not perform temperature measurement using those
+ * sensors until the parameters are provided.
+ */
+
+if (sb_chip->imc_tempin0_enabled) {
+
+ sb_config.Pecstruct.IMCFUNSupportBitMap |= IMC_ENABLE_TEMPIN0;
+
+ /* EC LDN9 funtion 89 TEMPIN channel 0 */
+ sb_config.Pecstruct.MSGFun89zone0MSGREG0 = 0x00;
+ sb_config.Pecstruct.MSGFun89zone0MSGREG1 = 0x00;
+ sb_config.Pecstruct.MSGFun89zone0MSGREG2 = ( sb_chip->imc_tempin0_at & 0xff);
+ sb_config.Pecstruct.MSGFun89zone0MSGREG3 = ((sb_chip->imc_tempin0_at >> 8) & 0xff);
+ sb_config.Pecstruct.MSGFun89zone0MSGREG4 = ((sb_chip->imc_tempin0_at >> 16) & 0xff);
+ sb_config.Pecstruct.MSGFun89zone0MSGREG5 = ((sb_chip->imc_tempin0_at >> 24) & 0xff);
+ sb_config.Pecstruct.MSGFun89zone0MSGREG6 = ( sb_chip->imc_tempin0_ct & 0xff);
+ sb_config.Pecstruct.MSGFun89zone0MSGREG7 = ((sb_chip->imc_tempin0_ct >> 8) & 0xff);
+ sb_config.Pecstruct.MSGFun89zone0MSGREG8 = ((sb_chip->imc_tempin0_ct >> 16) & 0xff);
+ sb_config.Pecstruct.MSGFun89zone0MSGREG9 = ((sb_chip->imc_tempin0_ct >> 24) & 0xff);
+ sb_config.Pecstruct.MSGFun89zone0MSGREGA = sb_chip->imc_tempin0_tuning_param;
+}
+
+if (sb_chip->imc_tempin1_enabled) {
+
+ sb_config.Pecstruct.IMCFUNSupportBitMap |= IMC_ENABLE_TEMPIN1;
+
+ /* EC LDN9 funtion 89 TEMPIN channel 1 */
+ sb_config.Pecstruct.MSGFun89zone1MSGREG0 = 0x00;
+ sb_config.Pecstruct.MSGFun89zone1MSGREG1 = 0x01;
+ sb_config.Pecstruct.MSGFun89zone1MSGREG2 = ( sb_chip->imc_tempin1_at & 0xff);
+ sb_config.Pecstruct.MSGFun89zone1MSGREG3 = ((sb_chip->imc_tempin1_at >> 8) & 0xff);
+ sb_config.Pecstruct.MSGFun89zone1MSGREG4 = ((sb_chip->imc_tempin1_at >> 16) & 0xff);
+ sb_config.Pecstruct.MSGFun89zone1MSGREG5 = ((sb_chip->imc_tempin1_at >> 24) & 0xff);
+ sb_config.Pecstruct.MSGFun89zone1MSGREG6 = ( sb_chip->imc_tempin1_ct & 0xff);
+ sb_config.Pecstruct.MSGFun89zone1MSGREG7 = ((sb_chip->imc_tempin1_ct >> 8) & 0xff);
+ sb_config.Pecstruct.MSGFun89zone1MSGREG8 = ((sb_chip->imc_tempin1_ct >> 16) & 0xff);
+ sb_config.Pecstruct.MSGFun89zone1MSGREG9 = ((sb_chip->imc_tempin1_ct >> 24) & 0xff);
+ sb_config.Pecstruct.MSGFun89zone1MSGREGA = sb_chip->imc_tempin1_tuning_param;
+}
+
+if (sb_chip->imc_tempin2_enabled) {
+
+ sb_config.Pecstruct.IMCFUNSupportBitMap |= IMC_ENABLE_TEMPIN2;
+
+ /* EC LDN9 funtion 89 TEMPIN channel 2 */
+ sb_config.Pecstruct.MSGFun89zone2MSGREG0 = 0x00;
+ sb_config.Pecstruct.MSGFun89zone2MSGREG1 = 0x02;
+ sb_config.Pecstruct.MSGFun89zone2MSGREG2 = ( sb_chip->imc_tempin2_at & 0xff);
+ sb_config.Pecstruct.MSGFun89zone2MSGREG3 = ((sb_chip->imc_tempin2_at >> 8) & 0xff);
+ sb_config.Pecstruct.MSGFun89zone2MSGREG4 = ((sb_chip->imc_tempin2_at >> 16) & 0xff);
+ sb_config.Pecstruct.MSGFun89zone2MSGREG5 = ((sb_chip->imc_tempin2_at >> 24) & 0xff);
+ sb_config.Pecstruct.MSGFun89zone2MSGREG6 = ( sb_chip->imc_tempin2_ct & 0xff);
+ sb_config.Pecstruct.MSGFun89zone2MSGREG7 = ((sb_chip->imc_tempin2_ct >> 8) & 0xff);
+ sb_config.Pecstruct.MSGFun89zone2MSGREG8 = ((sb_chip->imc_tempin2_ct >> 16) & 0xff);
+ sb_config.Pecstruct.MSGFun89zone2MSGREG9 = ((sb_chip->imc_tempin2_ct >> 24) & 0xff);
+ sb_config.Pecstruct.MSGFun89zone2MSGREGA = sb_chip->imc_tempin2_tuning_param;
+}
+
+if (sb_chip->imc_tempin3_enabled) {
+
+ sb_config.Pecstruct.IMCFUNSupportBitMap |= IMC_ENABLE_TEMPIN3;
+
+ /* EC LDN9 funtion 89 TEMPIN channel 3 */
+ sb_config.Pecstruct.MSGFun89zone3MSGREG0 = 0x00;
+ sb_config.Pecstruct.MSGFun89zone3MSGREG1 = 0x03;
+ sb_config.Pecstruct.MSGFun89zone3MSGREG2 = ( sb_chip->imc_tempin3_at & 0xff);
+ sb_config.Pecstruct.MSGFun89zone3MSGREG3 = ((sb_chip->imc_tempin3_at >> 8) & 0xff);
+ sb_config.Pecstruct.MSGFun89zone3MSGREG4 = ((sb_chip->imc_tempin3_at >> 16) & 0xff);
+ sb_config.Pecstruct.MSGFun89zone3MSGREG5 = ((sb_chip->imc_tempin3_at >> 24) & 0xff);
+ sb_config.Pecstruct.MSGFun89zone3MSGREG6 = ( sb_chip->imc_tempin3_ct & 0xff);
+ sb_config.Pecstruct.MSGFun89zone3MSGREG7 = ((sb_chip->imc_tempin3_ct >> 8) & 0xff);
+ sb_config.Pecstruct.MSGFun89zone3MSGREG8 = ((sb_chip->imc_tempin3_ct >> 16) & 0xff);
+ sb_config.Pecstruct.MSGFun89zone3MSGREG9 = ((sb_chip->imc_tempin3_ct >> 24) & 0xff);
+ sb_config.Pecstruct.MSGFun89zone3MSGREGA = sb_chip->imc_tempin3_tuning_param;
+}
+
+ /* Set up the sb_config structure for the fan control initialization */
+ sb_config.StdHeader.Func = SB_EC_FANCONTROL;
+
+ AmdSbDispatcher(&sb_config);
+
+ return;
+}
+
+
diff --git a/src/southbridge/amd/cimx/sb800/fan.h b/src/southbridge/amd/cimx/sb800/fan.h
new file mode 100644
index 0000000000..a0fc04ee25
--- /dev/null
+++ b/src/southbridge/amd/cimx/sb800/fan.h
@@ -0,0 +1,152 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2012 Sage Electronic Engineering, LLC
+ *
+ * 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
+ */
+
+#ifndef _SB800_FAN_H_
+#define _SB800_FAN_H_
+
+#ifndef __PRE_RAM__
+void init_sb800_IMC_fans(device_t dev);
+void init_sb800_MANUAL_fans(device_t dev);
+#endif
+
+/* Fan Register Definitions */
+#define FAN_0_OFFSET 0x00
+#define FAN_1_OFFSET 0x10
+#define FAN_2_OFFSET 0x20
+#define FAN_3_OFFSET 0x30
+#define FAN_4_OFFSET 0x40
+
+#define FAN_INPUT_CONTROL_REG 0x00
+#define FAN_CONTROL_REG 0x01
+#define FAN_FREQUENCY_REG 0x02
+#define FAN_LOW_DUTY_REG 0x03
+#define FAN_MED_DUTY_REG 0x04
+#define FAN_MULTIPLIER_REG 0x05
+#define FAN_LOW_TEMP_LO_REG 0x06
+#define FAN_LOW_TEMP_HI_REG 0x07
+#define FAN_MED_TEMP_LO_REG 0x08
+#define FAN_MED_TEMP_HI_REG 0x09
+#define FAN_HIGH_TEMP_LO_REG 0x0A
+#define FAN_HIGH_TEMP_HI_REG 0x0B
+#define FAN_LINEAR_RANGE_REG 0x0C
+#define FAN_LINEAR_HOLD_REG 0x0D
+
+/* FanXInputControl Definitions */
+#define FAN_INPUT_INTERNAL_DIODE 0
+#define FAN_INPUT_TEMP0 1
+#define FAN_INPUT_TEMP1 2
+#define FAN_INPUT_TEMP2 3
+#define FAN_INPUT_TEMP3 4
+#define FAN_INPUT_TEMP0_FILTER 5
+#define FAN_INPUT_ZERO 6
+#define FAN_INPUT_DISABLED 7
+
+/* FanXControl Definitions */
+#define FAN_AUTOMODE (1 << 0)
+#define FAN_LINEARMODE (1 << 1)
+#define FAN_STEPMODE 0 /* ~(1 << 1) */
+#define FAN_POLARITY_HIGH (1 << 2)
+#define FAN_POLARITY_LOW 0 /*~(1 << 2) */
+
+/* FanXLowDuty Definitions */
+#define FAN_POLARITY_HIGH_MAX_SPEED 0xff
+#define FAN_POLARITY_LOW_MAX_SPEED 0x00
+
+/* FanXFreq Definitions */
+/* Typically, fans run at 25KHz */
+#define FREQ_28KHZ 0x0
+#define FREQ_25KHZ 0x1
+#define FREQ_23KHZ 0x2
+#define FREQ_21KHZ 0x3
+#define FREQ_29KHZ 0x4
+#define FREQ_18KHZ 0x5
+/* Any value > 05h and < F7: Freq = 1/(FreqDiv * 2048 * 15ns) */
+#define FREQ_100HZ 0xF7
+#define FREQ_87HZ 0xF8
+#define FREQ_58HZ 0xF9
+#define FREQ_44HZ 0xFA
+#define FREQ_35HZ 0xFB
+#define FREQ_29HZ 0xFC
+#define FREQ_22HZ 0xFD
+#define FREQ_14HZ 0xFE
+#define FREQ_11HZ 0xFF
+
+
+/* IMC Fan Control Definitions */
+#define IMC_MODE1_FAN_ENABLED ( 1 << 0 )
+#define IMC_MODE1_FAN_IMC_CONTROLLED ( 1 << 2 )
+#define IMC_MODE1_FAN_LINEAR_MODE ( 1 << 4 )
+#define IMC_MODE1_FAN_STEP_MODE 0 /* ~( 1 << 4 ) */
+#define IMC_MODE1_NO_FANOUT 0 /* ~( 7 << 5 ) */
+#define IMC_MODE1_FANOUT0 ( 1 << 5 )
+#define IMC_MODE1_FANOUT1 ( 2 << 5 )
+#define IMC_MODE1_FANOUT2 ( 3 << 5 )
+#define IMC_MODE1_FANOUT3 ( 4 << 5 )
+#define IMC_MODE1_FANOUT4 ( 5 << 5 )
+
+#define IMC_MODE2_TEMPIN_NONE 0 /* ~( 7 << 0) */
+#define IMC_MODE2_TEMPIN_0 1
+#define IMC_MODE2_TEMPIN_1 2
+#define IMC_MODE2_TEMPIN_2 3
+#define IMC_MODE2_TEMPIN_3 4
+#define IMC_MODE2_INT_TEMPIN 5
+#define IMC_MODE2_TEMPIN_SB_TSI 6
+#define IMC_MODE2_TEMPIN_OTHER 7
+#define IMC_MODE2_FANIN_NONE 0 /* ~ (7 << 3) */
+#define IMC_MODE2_FANIN0 ( 1 << 3 )
+#define IMC_MODE2_FANIN1 ( 2 << 3 )
+#define IMC_MODE2_FANIN2 ( 3 << 3 )
+#define IMC_MODE2_FANIN3 ( 4 << 3 )
+#define IMC_MODE2_FANIN4 ( 5 << 3 )
+#define IMC_MODE2_TEMP_AVERAGING_ENABLED ( 1 << 6 )
+#define IMC_MODE2_TEMP_AVERAGING_DISABLED 0 /* ~( 1 << 6 ) */
+
+#define IMC_TEMP_SENSOR_ON_SMBUS_0 0
+#define IMC_TEMP_SENSOR_ON_SMBUS_2 1
+#define IMC_TEMP_SENSOR_ON_SMBUS_3 2
+#define IMC_TEMP_SENSOR_ON_SMBUS_4 3
+
+#define IMC_ZONE0 0
+#define IMC_ZONE1 1
+#define IMC_ZONE2 2
+#define IMC_ZONE3 3
+#define IMC_ZONE4 4
+
+#define IMC_TEMPIN_TUNING_DEFAULT_MODE 0
+#define IMC_TEMPIN_TUNING_HIGH_CURRENT_RATIO 1
+#define IMC_TEMPIN_TUNING_HIGH_CURRENT 2
+#define IMC_TEMPIN_TUNING_DISABLE_FILTERING ( 1 << 2 )
+
+/* IMCFUNSupportBitMap - Zone enable values */
+#define IMC_ENABLE_ZONE0 0x111
+#define IMC_ENABLE_ZONE1 0x222
+#define IMC_ENABLE_ZONE2 0x333
+#define IMC_ENABLE_ZONE3 0x444
+#define IMC_ENABLE_TEMPIN0 ( 1 << 12 )
+#define IMC_ENABLE_TEMPIN1 ( 1 << 13 )
+#define IMC_ENABLE_TEMPIN2 ( 1 << 14 )
+#define IMC_ENABLE_TEMPIN3 ( 1 << 15 )
+
+/* Array size settings */
+#define IMC_FAN_THRESHOLD_COUNT 9
+#define IMC_FAN_SPEED_COUNT 8
+#define IMC_FAN_CONFIG_COUNT 8
+#define FAN_REGISTER_COUNT 15
+
+#endif
diff --git a/src/southbridge/amd/cimx/sb800/late.c b/src/southbridge/amd/cimx/sb800/late.c
index 6067e39471..ef3a34b796 100644
--- a/src/southbridge/amd/cimx/sb800/late.c
+++ b/src/southbridge/amd/cimx/sb800/late.c
@@ -32,6 +32,7 @@
#include "chip.h" /* struct southbridge_amd_cimx_sb800_config */
#include "sb_cimx.h" /* AMD CIMX wrapper entries */
#include "smbus.h"
+#include "fan.h"
/*implement in mainboard.c*/
void set_pcie_reset(void);
@@ -418,6 +419,12 @@ static void sb800_enable(device_t dev)
case (0x14 << 3) | 3: /* 0:14:3 LPC */
+ /* Initialize the fans */
+#if CONFIG_SB800_IMC_FAN_CONTROL
+ init_sb800_IMC_fans(dev);
+#elif CONFIG_SB800_MANUAL_FAN_CONTROL
+ init_sb800_MANUAL_fans(dev);
+#endif
break;
case (0x14 << 3) | 4: /* 0:14:4 PCI */