summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEvgeny Zinoviev <me@ch1p.io>2021-02-07 01:49:30 +0300
committerEvgeny Zinoviev <me@ch1p.io>2021-02-07 01:49:30 +0300
commitee835aed102f4651f30234eda1110fa106575fdd (patch)
treee3368029dfe7b14b77892b6f48dbd0d11614057a
parent8f96216188ae440be6af63af650e26f05a86fd81 (diff)
parentb01b13866a6049ac81bd11861a29c3f4e591f951 (diff)
Merge branch 'me-disable' into mbp101_medisable_1
-rw-r--r--src/mainboard/lenovo/l520/cmos.default1
-rw-r--r--src/mainboard/lenovo/l520/cmos.layout6
-rw-r--r--src/mainboard/lenovo/t420/cmos.default1
-rw-r--r--src/mainboard/lenovo/t420/cmos.layout6
-rw-r--r--src/mainboard/lenovo/t420s/cmos.default1
-rw-r--r--src/mainboard/lenovo/t420s/cmos.layout6
-rw-r--r--src/mainboard/lenovo/t430/cmos.default1
-rw-r--r--src/mainboard/lenovo/t430/cmos.layout6
-rw-r--r--src/mainboard/lenovo/t430s/cmos.default1
-rw-r--r--src/mainboard/lenovo/t430s/cmos.layout6
-rw-r--r--src/mainboard/lenovo/t520/cmos.default1
-rw-r--r--src/mainboard/lenovo/t520/cmos.layout6
-rw-r--r--src/mainboard/lenovo/t530/cmos.default1
-rw-r--r--src/mainboard/lenovo/t530/cmos.layout6
-rw-r--r--src/mainboard/lenovo/x220/cmos.default1
-rw-r--r--src/mainboard/lenovo/x220/cmos.layout6
-rw-r--r--src/mainboard/lenovo/x230/cmos.default1
-rw-r--r--src/mainboard/lenovo/x230/cmos.layout6
-rw-r--r--src/southbridge/intel/bd82x6x/Kconfig1
-rw-r--r--src/southbridge/intel/bd82x6x/early_me.c17
-rw-r--r--src/southbridge/intel/bd82x6x/early_me_mrc.c17
-rw-r--r--src/southbridge/intel/bd82x6x/me.c62
-rw-r--r--src/southbridge/intel/bd82x6x/me.h20
-rw-r--r--src/southbridge/intel/bd82x6x/me_8.x.c65
-rw-r--r--src/southbridge/intel/bd82x6x/me_common.c75
-rw-r--r--src/southbridge/intel/common/Kconfig3
-rw-r--r--src/southbridge/intel/common/Makefile.inc2
-rw-r--r--src/southbridge/intel/common/me.c31
-rw-r--r--src/southbridge/intel/common/me.h10
-rw-r--r--src/southbridge/intel/ibexpeak/Kconfig1
30 files changed, 322 insertions, 45 deletions
diff --git a/src/mainboard/lenovo/l520/cmos.default b/src/mainboard/lenovo/l520/cmos.default
index 979f132863..681c40e78b 100644
--- a/src/mainboard/lenovo/l520/cmos.default
+++ b/src/mainboard/lenovo/l520/cmos.default
@@ -14,3 +14,4 @@ sticky_fn=Disable
trackpoint=Enable
backlight=Both
usb_always_on=Disable
+me_state=Normal
diff --git a/src/mainboard/lenovo/l520/cmos.layout b/src/mainboard/lenovo/l520/cmos.layout
index e96915d2d1..a3f5308aa4 100644
--- a/src/mainboard/lenovo/l520/cmos.layout
+++ b/src/mainboard/lenovo/l520/cmos.layout
@@ -34,7 +34,9 @@ entries
421 1 e 9 sata_mode
422 2 e 10 backlight
-# coreboot config options: cpu
+# coreboot config options: ME
+424 1 e 13 me_state
+425 2 h 0 me_state_prev
# coreboot config options: northbridge
432 3 e 11 gfx_uma_size
@@ -89,6 +91,8 @@ enumerations
12 0 Disable
12 1 AC and battery
12 2 AC only
+13 0 Normal
+13 1 Disabled
# -----------------------------------------------------------------
checksums
diff --git a/src/mainboard/lenovo/t420/cmos.default b/src/mainboard/lenovo/t420/cmos.default
index 467f965829..8244071b8a 100644
--- a/src/mainboard/lenovo/t420/cmos.default
+++ b/src/mainboard/lenovo/t420/cmos.default
@@ -14,3 +14,4 @@ sticky_fn=Disable
trackpoint=Enable
hybrid_graphics_mode=Integrated Only
usb_always_on=Disable
+me_state=Normal
diff --git a/src/mainboard/lenovo/t420/cmos.layout b/src/mainboard/lenovo/t420/cmos.layout
index e1d15be56b..daf569c0af 100644
--- a/src/mainboard/lenovo/t420/cmos.layout
+++ b/src/mainboard/lenovo/t420/cmos.layout
@@ -34,7 +34,9 @@ entries
421 1 e 9 sata_mode
422 2 e 13 usb_always_on
-# coreboot config options: cpu
+# coreboot config options: ME
+424 1 e 14 me_state
+425 2 h 0 me_state_prev
# coreboot config options: northbridge
432 3 e 11 gfx_uma_size
@@ -97,6 +99,8 @@ enumerations
13 0 Disable
13 1 AC and battery
13 2 AC only
+14 0 Normal
+14 1 Disabled
# -----------------------------------------------------------------
checksums
diff --git a/src/mainboard/lenovo/t420s/cmos.default b/src/mainboard/lenovo/t420s/cmos.default
index 467f965829..8244071b8a 100644
--- a/src/mainboard/lenovo/t420s/cmos.default
+++ b/src/mainboard/lenovo/t420s/cmos.default
@@ -14,3 +14,4 @@ sticky_fn=Disable
trackpoint=Enable
hybrid_graphics_mode=Integrated Only
usb_always_on=Disable
+me_state=Normal
diff --git a/src/mainboard/lenovo/t420s/cmos.layout b/src/mainboard/lenovo/t420s/cmos.layout
index e1d15be56b..daf569c0af 100644
--- a/src/mainboard/lenovo/t420s/cmos.layout
+++ b/src/mainboard/lenovo/t420s/cmos.layout
@@ -34,7 +34,9 @@ entries
421 1 e 9 sata_mode
422 2 e 13 usb_always_on
-# coreboot config options: cpu
+# coreboot config options: ME
+424 1 e 14 me_state
+425 2 h 0 me_state_prev
# coreboot config options: northbridge
432 3 e 11 gfx_uma_size
@@ -97,6 +99,8 @@ enumerations
13 0 Disable
13 1 AC and battery
13 2 AC only
+14 0 Normal
+14 1 Disabled
# -----------------------------------------------------------------
checksums
diff --git a/src/mainboard/lenovo/t430/cmos.default b/src/mainboard/lenovo/t430/cmos.default
index e65869b332..26795fe5cf 100644
--- a/src/mainboard/lenovo/t430/cmos.default
+++ b/src/mainboard/lenovo/t430/cmos.default
@@ -15,3 +15,4 @@ trackpoint=Enable
backlight=Both
usb_always_on=Disable
hybrid_graphics_mode=Integrated Only
+me_state=Normal
diff --git a/src/mainboard/lenovo/t430/cmos.layout b/src/mainboard/lenovo/t430/cmos.layout
index dd51c36854..3e48df5584 100644
--- a/src/mainboard/lenovo/t430/cmos.layout
+++ b/src/mainboard/lenovo/t430/cmos.layout
@@ -34,7 +34,9 @@ entries
421 1 e 9 sata_mode
422 2 e 10 backlight
-# coreboot config options: cpu
+# coreboot config options: ME
+424 1 e 14 me_state
+425 2 h 0 me_state_prev
# coreboot config options: northbridge
432 3 e 11 gfx_uma_size
@@ -96,6 +98,8 @@ enumerations
13 0 Disable
13 1 AC and battery
13 2 AC only
+14 0 Normal
+14 1 Disabled
# -----------------------------------------------------------------
checksums
diff --git a/src/mainboard/lenovo/t430s/cmos.default b/src/mainboard/lenovo/t430s/cmos.default
index a30c90dc1f..52dbf70377 100644
--- a/src/mainboard/lenovo/t430s/cmos.default
+++ b/src/mainboard/lenovo/t430s/cmos.default
@@ -16,3 +16,4 @@ backlight=Both
enable_dual_graphics=Disable
usb_always_on=Disable
f1_to_f12_as_primary=Enable
+me_state=Normal
diff --git a/src/mainboard/lenovo/t430s/cmos.layout b/src/mainboard/lenovo/t430s/cmos.layout
index 02c1ea78df..14a21eb84e 100644
--- a/src/mainboard/lenovo/t430s/cmos.layout
+++ b/src/mainboard/lenovo/t430s/cmos.layout
@@ -35,7 +35,9 @@ entries
422 2 e 10 backlight
424 1 e 1 f1_to_f12_as_primary
-# coreboot config options: cpu
+# coreboot config options: ME
+425 1 e 13 me_state
+426 2 h 0 me_state_prev
# coreboot config options: northbridge
432 3 e 11 gfx_uma_size
@@ -94,6 +96,8 @@ enumerations
12 0 Disable
12 1 AC and battery
12 2 AC only
+13 0 Normal
+13 1 Disabled
# -----------------------------------------------------------------
checksums
diff --git a/src/mainboard/lenovo/t520/cmos.default b/src/mainboard/lenovo/t520/cmos.default
index 6e61dae340..cf79b391e2 100644
--- a/src/mainboard/lenovo/t520/cmos.default
+++ b/src/mainboard/lenovo/t520/cmos.default
@@ -15,3 +15,4 @@ trackpoint=Enable
backlight=Both
hybrid_graphics_mode=Integrated Only
usb_always_on=Disable
+me_state=Normal
diff --git a/src/mainboard/lenovo/t520/cmos.layout b/src/mainboard/lenovo/t520/cmos.layout
index dd51c36854..3e48df5584 100644
--- a/src/mainboard/lenovo/t520/cmos.layout
+++ b/src/mainboard/lenovo/t520/cmos.layout
@@ -34,7 +34,9 @@ entries
421 1 e 9 sata_mode
422 2 e 10 backlight
-# coreboot config options: cpu
+# coreboot config options: ME
+424 1 e 14 me_state
+425 2 h 0 me_state_prev
# coreboot config options: northbridge
432 3 e 11 gfx_uma_size
@@ -96,6 +98,8 @@ enumerations
13 0 Disable
13 1 AC and battery
13 2 AC only
+14 0 Normal
+14 1 Disabled
# -----------------------------------------------------------------
checksums
diff --git a/src/mainboard/lenovo/t530/cmos.default b/src/mainboard/lenovo/t530/cmos.default
index 6e61dae340..cf79b391e2 100644
--- a/src/mainboard/lenovo/t530/cmos.default
+++ b/src/mainboard/lenovo/t530/cmos.default
@@ -15,3 +15,4 @@ trackpoint=Enable
backlight=Both
hybrid_graphics_mode=Integrated Only
usb_always_on=Disable
+me_state=Normal
diff --git a/src/mainboard/lenovo/t530/cmos.layout b/src/mainboard/lenovo/t530/cmos.layout
index 6cd8ac066b..d109a61b4e 100644
--- a/src/mainboard/lenovo/t530/cmos.layout
+++ b/src/mainboard/lenovo/t530/cmos.layout
@@ -34,7 +34,9 @@ entries
421 1 e 9 sata_mode
422 2 e 10 backlight
-# coreboot config options: cpu
+# coreboot config options: ME
+424 1 e 14 me_state
+425 2 h 0 me_state_prev
# coreboot config options: northbridge
432 3 e 11 gfx_uma_size
@@ -97,6 +99,8 @@ enumerations
13 0 Disable
13 1 AC and battery
13 2 AC only
+14 0 Normal
+14 1 Disabled
# -----------------------------------------------------------------
checksums
diff --git a/src/mainboard/lenovo/x220/cmos.default b/src/mainboard/lenovo/x220/cmos.default
index 42720a27bd..6d1d57a795 100644
--- a/src/mainboard/lenovo/x220/cmos.default
+++ b/src/mainboard/lenovo/x220/cmos.default
@@ -13,3 +13,4 @@ usb_always_on=Disable
fn_ctrl_swap=Disable
sticky_fn=Disable
trackpoint=Enable
+me_state=Normal
diff --git a/src/mainboard/lenovo/x220/cmos.layout b/src/mainboard/lenovo/x220/cmos.layout
index f152b2982a..c63ed8c22c 100644
--- a/src/mainboard/lenovo/x220/cmos.layout
+++ b/src/mainboard/lenovo/x220/cmos.layout
@@ -34,7 +34,9 @@ entries
421 1 e 9 sata_mode
422 2 e 12 usb_always_on
-# coreboot config options: cpu
+# coreboot config options: ME
+424 1 e 13 me_state
+425 2 h 0 me_state_prev
# coreboot config options: northbridge
432 3 e 11 gfx_uma_size
@@ -92,6 +94,8 @@ enumerations
12 0 Disable
12 1 AC and battery
12 2 AC only
+13 0 Normal
+13 1 Disabled
# -----------------------------------------------------------------
checksums
diff --git a/src/mainboard/lenovo/x230/cmos.default b/src/mainboard/lenovo/x230/cmos.default
index 5c19d0f4a5..7314066c2b 100644
--- a/src/mainboard/lenovo/x230/cmos.default
+++ b/src/mainboard/lenovo/x230/cmos.default
@@ -15,3 +15,4 @@ trackpoint=Enable
backlight=Both
usb_always_on=Disable
f1_to_f12_as_primary=Enable
+me_state=Normal
diff --git a/src/mainboard/lenovo/x230/cmos.layout b/src/mainboard/lenovo/x230/cmos.layout
index 89891bf0b0..22110189a1 100644
--- a/src/mainboard/lenovo/x230/cmos.layout
+++ b/src/mainboard/lenovo/x230/cmos.layout
@@ -35,7 +35,9 @@ entries
422 2 e 10 backlight
424 1 e 1 f1_to_f12_as_primary
-# coreboot config options: cpu
+# coreboot config options: ME
+425 1 e 13 me_state
+426 2 h 0 me_state_prev
# coreboot config options: northbridge
432 3 e 11 gfx_uma_size
@@ -94,6 +96,8 @@ enumerations
12 0 Disable
12 1 AC and battery
12 2 AC only
+13 0 Normal
+13 1 Disabled
# -----------------------------------------------------------------
checksums
diff --git a/src/southbridge/intel/bd82x6x/Kconfig b/src/southbridge/intel/bd82x6x/Kconfig
index e3ad885cb4..812b6c052e 100644
--- a/src/southbridge/intel/bd82x6x/Kconfig
+++ b/src/southbridge/intel/bd82x6x/Kconfig
@@ -18,6 +18,7 @@ config SOUTH_BRIDGE_OPTIONS
select SOUTHBRIDGE_INTEL_COMMON_SMBUS
select SOUTHBRIDGE_INTEL_COMMON_EARLY_SMBUS
select SOUTHBRIDGE_INTEL_COMMON_SPI_ICH9
+ select SOUTHBRIDGE_INTEL_COMMON_ME
select SOUTHBRIDGE_INTEL_COMMON_PMCLIB
select SOUTHBRIDGE_INTEL_COMMON_PMBASE
select SOUTHBRIDGE_INTEL_COMMON_RTC
diff --git a/src/southbridge/intel/bd82x6x/early_me.c b/src/southbridge/intel/bd82x6x/early_me.c
index 6320d2ea9f..09e5f39c9c 100644
--- a/src/southbridge/intel/bd82x6x/early_me.c
+++ b/src/southbridge/intel/bd82x6x/early_me.c
@@ -7,6 +7,7 @@
#include <delay.h>
#include <device/pci_def.h>
#include <halt.h>
+#include <southbridge/intel/common/me.h>
#include <string.h>
#include <timestamp.h>
#include "me.h"
@@ -91,22 +92,6 @@ int intel_early_me_uma_size(void)
return 0;
}
-static inline void set_global_reset(int enable)
-{
- u32 etr3 = pci_read_config32(PCH_LPC_DEV, ETR3);
-
- /* Clear CF9 Without Resume Well Reset Enable */
- etr3 &= ~ETR3_CWORWRE;
-
- /* CF9GR indicates a Global Reset */
- if (enable)
- etr3 |= ETR3_CF9GR;
- else
- etr3 &= ~ETR3_CF9GR;
-
- pci_write_config32(PCH_LPC_DEV, ETR3, etr3);
-}
-
int intel_early_me_init_done(u8 status)
{
u8 reset, errorcode, opmode;
diff --git a/src/southbridge/intel/bd82x6x/early_me_mrc.c b/src/southbridge/intel/bd82x6x/early_me_mrc.c
index 0b11fd0e81..180e466bd4 100644
--- a/src/southbridge/intel/bd82x6x/early_me_mrc.c
+++ b/src/southbridge/intel/bd82x6x/early_me_mrc.c
@@ -6,6 +6,7 @@
#include <delay.h>
#include <device/pci_def.h>
#include <halt.h>
+#include <southbridge/intel/common/me.h>
#include <string.h>
#include "me.h"
#include "pch.h"
@@ -96,22 +97,6 @@ int intel_early_me_uma_size(void)
return 0;
}
-static inline void set_global_reset(int enable)
-{
- u32 etr3 = pci_read_config32(PCH_LPC_DEV, ETR3);
-
- /* Clear CF9 Without Resume Well Reset Enable */
- etr3 &= ~ETR3_CWORWRE;
-
- /* CF9GR indicates a Global Reset */
- if (enable)
- etr3 |= ETR3_CF9GR;
- else
- etr3 &= ~ETR3_CF9GR;
-
- pci_write_config32(PCH_LPC_DEV, ETR3, etr3);
-}
-
int intel_early_me_init_done(u8 status)
{
u8 reset;
diff --git a/src/southbridge/intel/bd82x6x/me.c b/src/southbridge/intel/bd82x6x/me.c
index fe2a37c849..c522d77a11 100644
--- a/src/southbridge/intel/bd82x6x/me.c
+++ b/src/southbridge/intel/bd82x6x/me.c
@@ -9,6 +9,7 @@
*/
#include <acpi/acpi.h>
+#include <cf9_reset.h>
#include <device/mmio.h>
#include <device/device.h>
#include <device/pci.h>
@@ -19,6 +20,9 @@
#include <string.h>
#include <delay.h>
#include <elog.h>
+#include <halt.h>
+#include <option.h>
+#include <southbridge/intel/common/me.h>
#include "me.h"
#include "pch.h"
@@ -248,13 +252,20 @@ static me_bios_path intel_me_path(struct device *dev)
static void intel_me_init(struct device *dev)
{
me_bios_path path = intel_me_path(dev);
+ u8 me_state = 0, me_state_prev = 0;
+ bool need_reset = false;
+ struct me_hfs hfs;
/* Do initial setup and determine the BIOS path */
printk(BIOS_NOTICE, "ME: BIOS path: %s\n", me_get_bios_path_string(path));
+ get_option(&me_state, "me_state");
+ get_option(&me_state_prev, "me_state_prev");
+
+ printk(BIOS_DEBUG, "ME: me_state=%u, me_state_prev=%u\n", me_state, me_state_prev);
+
switch (path) {
case ME_S3WAKE_BIOS_PATH:
- case ME_DISABLE_BIOS_PATH:
#if CONFIG(HIDE_MEI_ON_ERROR)
case ME_ERROR_BIOS_PATH:
#endif
@@ -277,12 +288,49 @@ static void intel_me_init(struct device *dev)
mkhi_get_fwcaps();
}
+ /* Put ME in Software Temporary Disable Mode, if needed */
+ if (me_state == CMOS_ME_STATE_DISABLED
+ && CMOS_ME_STATE(me_state_prev) == CMOS_ME_STATE_NORMAL) {
+ printk(BIOS_INFO, "ME: disabling ME\n");
+ if (enter_soft_temp_disable()) {
+ enter_soft_temp_disable_wait();
+ need_reset = true;
+ } else {
+ printk(BIOS_ERR, "ME: failed to enter Soft Temporary Disable mode\n");
+ }
+
+ break;
+ }
+
/*
* Leave the ME unlocked in this path.
* It will be locked via SMI command later.
*/
break;
+ case ME_DISABLE_BIOS_PATH:
+ /* Bring ME out of Soft Temporary Disable mode, if needed */
+ pci_read_dword_ptr(dev, &hfs, PCI_ME_HFS);
+ if (hfs.operation_mode == ME_HFS_MODE_DIS
+ && me_state == CMOS_ME_STATE_NORMAL
+ && (CMOS_ME_STATE(me_state_prev) == CMOS_ME_STATE_DISABLED
+ || !CMOS_ME_CHANGED(me_state_prev))) {
+ printk(BIOS_INFO, "ME: re-enabling ME\n");
+
+ exit_soft_temp_disable(dev);
+ exit_soft_temp_disable_wait(dev);
+
+ /*
+ * ME starts loading firmware immediately after writing to H_GS,
+ * but Lenovo BIOS performs a reboot after bringing ME back to
+ * Normal mode. Assume that global reset is needed.
+ */
+ need_reset = true;
+ } else {
+ intel_me_hide(dev);
+ }
+ break;
+
#if !CONFIG(HIDE_MEI_ON_ERROR)
case ME_ERROR_BIOS_PATH:
#endif
@@ -290,6 +338,18 @@ static void intel_me_init(struct device *dev)
case ME_FIRMWARE_UPDATE_BIOS_PATH:
break;
}
+
+ /* To avoid boot loops if ME fails to get back from disabled mode,
+ set the 'changed' bit here. */
+ if (me_state != CMOS_ME_STATE(me_state_prev) || need_reset) {
+ u8 new_state = me_state | CMOS_ME_STATE_CHANGED;
+ set_option("me_state_prev", &new_state);
+ }
+
+ if (need_reset) {
+ set_global_reset(true);
+ full_reset();
+ }
}
static struct device_operations device_ops = {
diff --git a/src/southbridge/intel/bd82x6x/me.h b/src/southbridge/intel/bd82x6x/me.h
index 014fc1d9ea..d99c452669 100644
--- a/src/southbridge/intel/bd82x6x/me.h
+++ b/src/southbridge/intel/bd82x6x/me.h
@@ -171,6 +171,22 @@ struct mei_header {
#define MKHI_GLOBAL_RESET 0x0b
#define MKHI_FWCAPS_GET_RULE 0x02
+#define MKHI_FWCAPS_SET_RULE 0x03
+
+#define MKHI_DISABLE_RULE_ID 0x06
+
+#define CMOS_ME_STATE(state) ((state) & 0x1)
+#define CMOS_ME_CHANGED(state) (((state) & 0x2) >> 1)
+#define CMOS_ME_STATE_NORMAL 0
+#define CMOS_ME_STATE_DISABLED 1
+#define CMOS_ME_STATE_CHANGED 2
+
+#define ME_ENABLE_TIMEOUT 20000
+
+struct me_disable {
+ u32 rule_id;
+ u16 data;
+} __packed;
#define MKHI_MDES_ENABLE 0x09
@@ -228,6 +244,10 @@ void mei_write_dword_ptr(void *ptr, int offset);
#ifndef __SIMPLE_DEVICE__
void pci_read_dword_ptr(struct device *dev, void *ptr, int offset);
+bool enter_soft_temp_disable(void);
+void enter_soft_temp_disable_wait(void);
+void exit_soft_temp_disable(struct device *dev);
+void exit_soft_temp_disable_wait(struct device *dev);
#endif
void read_host_csr(struct mei_csr *csr);
diff --git a/src/southbridge/intel/bd82x6x/me_8.x.c b/src/southbridge/intel/bd82x6x/me_8.x.c
index f5a39ecfa6..78c71aa368 100644
--- a/src/southbridge/intel/bd82x6x/me_8.x.c
+++ b/src/southbridge/intel/bd82x6x/me_8.x.c
@@ -9,6 +9,7 @@
*/
#include <acpi/acpi.h>
+#include <cf9_reset.h>
#include <device/mmio.h>
#include <device/device.h>
#include <device/pci.h>
@@ -19,6 +20,9 @@
#include <string.h>
#include <delay.h>
#include <elog.h>
+#include <halt.h>
+#include <option.h>
+#include <southbridge/intel/common/me.h>
#include "me.h"
#include "pch.h"
@@ -206,8 +210,7 @@ static me_bios_path intel_me_path(struct device *dev)
/* Check if the MBP is ready */
if (!gmes.mbp_rdy) {
- printk(BIOS_CRIT, "%s: mbp is not ready!\n",
- __func__);
+ printk(BIOS_CRIT, "%s: mbp is not ready!\n", __func__);
path = ME_ERROR_BIOS_PATH;
}
@@ -236,13 +239,20 @@ static void intel_me_init(struct device *dev)
{
me_bios_path path = intel_me_path(dev);
me_bios_payload mbp_data;
+ u8 me_state = 0, me_state_prev = 0;
+ bool need_reset = false;
+ struct me_hfs hfs;
/* Do initial setup and determine the BIOS path */
printk(BIOS_NOTICE, "ME: BIOS path: %s\n", me_get_bios_path_string(path));
+ get_option(&me_state, "me_state");
+ get_option(&me_state_prev, "me_state_prev");
+
+ printk(BIOS_DEBUG, "ME: me_state=%u, me_state_prev=%u\n", me_state, me_state_prev);
+
switch (path) {
case ME_S3WAKE_BIOS_PATH:
- case ME_DISABLE_BIOS_PATH:
#if CONFIG(HIDE_MEI_ON_ERROR)
case ME_ERROR_BIOS_PATH:
#endif
@@ -266,12 +276,49 @@ static void intel_me_init(struct device *dev)
me_print_fwcaps(&mbp_data.fw_caps_sku);
}
+ /* Put ME in Software Temporary Disable Mode, if needed */
+ if (me_state == CMOS_ME_STATE_DISABLED
+ && CMOS_ME_STATE(me_state_prev) == CMOS_ME_STATE_NORMAL) {
+ printk(BIOS_INFO, "ME: disabling ME\n");
+ if (enter_soft_temp_disable()) {
+ enter_soft_temp_disable_wait();
+ need_reset = true;
+ } else {
+ printk(BIOS_ERR, "ME: failed to enter Soft Temporary Disable mode\n");
+ }
+
+ break;
+ }
+
/*
* Leave the ME unlocked in this path.
* It will be locked via SMI command later.
*/
break;
+ case ME_DISABLE_BIOS_PATH:
+ /* Bring ME out of Soft Temporary Disable mode, if needed */
+ pci_read_dword_ptr(dev, &hfs, PCI_ME_HFS);
+ if (hfs.operation_mode == ME_HFS_MODE_DIS
+ && me_state == CMOS_ME_STATE_NORMAL
+ && (CMOS_ME_STATE(me_state_prev) == CMOS_ME_STATE_DISABLED
+ || !CMOS_ME_CHANGED(me_state_prev))) {
+ printk(BIOS_INFO, "ME: re-enabling ME\n");
+
+ exit_soft_temp_disable(dev);
+ exit_soft_temp_disable_wait(dev);
+
+ /*
+ * ME starts loading firmware immediately after writing to H_GS,
+ * but Lenovo BIOS performs a reboot after bringing ME back to
+ * Normal mode. Assume that global reset is needed.
+ */
+ need_reset = true;
+ } else {
+ intel_me_hide(dev);
+ }
+ break;
+
#if !CONFIG(HIDE_MEI_ON_ERROR)
case ME_ERROR_BIOS_PATH:
#endif
@@ -279,6 +326,18 @@ static void intel_me_init(struct device *dev)
case ME_FIRMWARE_UPDATE_BIOS_PATH:
break;
}
+
+ /* To avoid boot loops if ME fails to get back from disabled mode,
+ set the 'changed' bit here. */
+ if (me_state != CMOS_ME_STATE(me_state_prev) || need_reset) {
+ u8 new_state = me_state | CMOS_ME_STATE_CHANGED;
+ set_option("me_state_prev", &new_state);
+ }
+
+ if (need_reset) {
+ set_global_reset(true);
+ full_reset();
+ }
}
static struct device_operations device_ops = {
diff --git a/src/southbridge/intel/bd82x6x/me_common.c b/src/southbridge/intel/bd82x6x/me_common.c
index 422c091001..e229956607 100644
--- a/src/southbridge/intel/bd82x6x/me_common.c
+++ b/src/southbridge/intel/bd82x6x/me_common.c
@@ -11,6 +11,7 @@
#include <string.h>
#include <delay.h>
#include <halt.h>
+#include <timer.h>
#include "me.h"
#include "pch.h"
@@ -417,4 +418,78 @@ void intel_me_hide(struct device *dev)
pch_enable(dev);
}
+bool enter_soft_temp_disable(void)
+{
+ /* The binary sequence for the disable command was found by PT in some vendor BIOS */
+ struct me_disable message = {
+ .rule_id = MKHI_DISABLE_RULE_ID,
+ .data = 0x01,
+ };
+ struct mkhi_header mkhi = {
+ .group_id = MKHI_GROUP_ID_FWCAPS,
+ .command = MKHI_FWCAPS_SET_RULE,
+ };
+ struct mei_header mei = {
+ .is_complete = 1,
+ .length = sizeof(mkhi) + sizeof(message),
+ .host_address = MEI_HOST_ADDRESS,
+ .client_address = MEI_ADDRESS_MKHI,
+ };
+ u32 resp;
+
+ if (mei_sendrecv(&mei, &mkhi, &message, &resp, sizeof(resp)) < 0
+ || resp != MKHI_DISABLE_RULE_ID) {
+ printk(BIOS_WARNING, "ME: disable command failed\n");
+ return false;
+ }
+
+ return true;
+}
+
+void enter_soft_temp_disable_wait(void)
+{
+ /*
+ * TODO: Find smarter way to determine when we're ready to reboot.
+ *
+ * There has to be some bit in some register, or something, that indicates that ME has
+ * finished doing its thing and we're ready to reboot.
+ *
+ * It was not found yet, though, and waiting for a response after the disable command is
+ * not enough. If we reboot too early, ME will not be disabled on next boot. For now,
+ * let's just wait for 1 second here.
+ */
+ mdelay(1000);
+}
+
+void exit_soft_temp_disable(struct device *dev)
+{
+ /* To bring ME out of Soft Temporary Disable Mode, host writes 0x20000000 to H_GS */
+ pci_write_config32(dev, PCI_ME_H_GS, 0x2 << 28);
+}
+
+void exit_soft_temp_disable_wait(struct device *dev)
+{
+ struct me_hfs hfs;
+ struct stopwatch sw;
+
+ stopwatch_init_msecs_expire(&sw, ME_ENABLE_TIMEOUT);
+
+ /**
+ * Wait for fw_init_complete. Check every 50 ms, give up after 20 sec.
+ * This is what vendor BIOS does. Usually it takes 1.5 seconds or so.
+ */
+ do {
+ mdelay(50);
+ pci_read_dword_ptr(dev, &hfs, PCI_ME_HFS);
+ if (hfs.fw_init_complete)
+ break;
+ } while (!stopwatch_expired(&sw));
+
+ if (!hfs.fw_init_complete)
+ printk(BIOS_ERR, "ME: giving up on waiting for fw_init_complete\n");
+ else
+ printk(BIOS_NOTICE, "ME: took %lums to complete initialization\n",
+ stopwatch_duration_msecs(&sw));
+}
+
#endif
diff --git a/src/southbridge/intel/common/Kconfig b/src/southbridge/intel/common/Kconfig
index 1bdefd4b93..5d7a4ee784 100644
--- a/src/southbridge/intel/common/Kconfig
+++ b/src/southbridge/intel/common/Kconfig
@@ -15,6 +15,9 @@ config SOUTHBRIDGE_INTEL_COMMON_PMBASE
config SOUTHBRIDGE_INTEL_COMMON_GPIO
def_bool n
+config SOUTHBRIDGE_INTEL_COMMON_ME
+ def_bool n
+
config SOUTHBRIDGE_INTEL_COMMON_HPET
def_bool n
diff --git a/src/southbridge/intel/common/Makefile.inc b/src/southbridge/intel/common/Makefile.inc
index f11ffa6aef..adacc25a4b 100644
--- a/src/southbridge/intel/common/Makefile.inc
+++ b/src/southbridge/intel/common/Makefile.inc
@@ -7,6 +7,8 @@ all-$(CONFIG_SOUTHBRIDGE_INTEL_COMMON_RESET) += reset.c
all-$(CONFIG_SOUTHBRIDGE_INTEL_COMMON_HPET) += hpet.c
+all-$(CONFIG_SOUTHBRIDGE_INTEL_COMMON_ME) += me.c
+
romstage-$(CONFIG_SOUTHBRIDGE_INTEL_COMMON_EARLY_SMBUS) += early_smbus.c
romstage-$(CONFIG_SOUTHBRIDGE_INTEL_COMMON_SMBUS) += smbus.c
diff --git a/src/southbridge/intel/common/me.c b/src/southbridge/intel/common/me.c
new file mode 100644
index 0000000000..d49508748a
--- /dev/null
+++ b/src/southbridge/intel/common/me.c
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#define __SIMPLE_DEVICE__
+
+#include <device/pci_ops.h>
+#include <types.h>
+
+#include "me.h"
+
+#define PCH_LPC_DEV PCI_DEV(0, 0x1f, 0)
+
+#define ETR3 0xac
+#define ETR3_CWORWRE (1 << 18)
+#define ETR3_CF9GR (1 << 20)
+#define ETR3_CF9LOCK (1 << 31)
+
+void set_global_reset(const bool enable)
+{
+ u32 etr3 = pci_read_config32(PCH_LPC_DEV, ETR3);
+
+ /* Clear CF9 Without Resume Well Reset Enable */
+ etr3 &= ~ETR3_CWORWRE;
+
+ /* CF9GR indicates a Global Reset */
+ if (enable)
+ etr3 |= ETR3_CF9GR;
+ else
+ etr3 &= ~ETR3_CF9GR;
+
+ pci_write_config32(PCH_LPC_DEV, ETR3, etr3);
+}
diff --git a/src/southbridge/intel/common/me.h b/src/southbridge/intel/common/me.h
new file mode 100644
index 0000000000..81c1b47bc9
--- /dev/null
+++ b/src/southbridge/intel/common/me.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef SOUTHBRIDGE_INTEL_COMMON_HPET_H
+#define SOUTHBRIDGE_INTEL_COMMON_HPET_H
+
+#include <types.h>
+
+void set_global_reset(const bool enable);
+
+#endif /* SOUTHBRIDGE_INTEL_COMMON_HPET_H */
diff --git a/src/southbridge/intel/ibexpeak/Kconfig b/src/southbridge/intel/ibexpeak/Kconfig
index 34ae2f112a..18f77703a4 100644
--- a/src/southbridge/intel/ibexpeak/Kconfig
+++ b/src/southbridge/intel/ibexpeak/Kconfig
@@ -21,6 +21,7 @@ config SOUTH_BRIDGE_OPTIONS
select SOUTHBRIDGE_INTEL_COMMON_EARLY_SMBUS
select SOUTHBRIDGE_INTEL_COMMON_SPI_ICH9
select SOUTHBRIDGE_INTEL_COMMON_SMM
+ select SOUTHBRIDGE_INTEL_COMMON_ME
select SOUTHBRIDGE_INTEL_COMMON_PMCLIB
select SOUTHBRIDGE_INTEL_COMMON_PMBASE
select SOUTHBRIDGE_INTEL_COMMON_RTC