summaryrefslogtreecommitdiff
path: root/src/soc/intel/skylake/me.c
diff options
context:
space:
mode:
authorSubrata Banik <subrata.banik@intel.com>2016-08-19 12:33:42 +0530
committerAaron Durbin <adurbin@chromium.org>2016-10-16 02:50:26 +0200
commitff8bf410d9596ac82b183cb62e2d8990c550ee4a (patch)
tree30ff1c100dcb1a4e0a91db6eb3e3a5aa6e185ba1 /src/soc/intel/skylake/me.c
parent29f8708fca820797a088c7e81eb830fc61b21d28 (diff)
soc/intel/skylake: Implement Global Reset MEI message
As per ME BWG, there are two mechanism to generate a Global Reset (resets both host and Intel ME), one is through CF9h IO write of 6h or Eh with "CF9h Global Reset" (CF9GR) bit set, PMC PCI offset ACh[20]. Another is to issue the Global Reset MEI message. Because any attempts to cause global reset without synchronizing the two sides might cause unwanted side effects, such as unwritten flash data that will get destroyed if the host were to cause a global reset without informing Intel ME firmware, the recommended method is to send a Global Reset MEI message when the following conditions are met: The PCH chipset firmware just needs to complete the Intel ME Interface #1 initialization and check the Intel ME HFSTS state if Intel ME is not in ERROR state and is accepting MEI commands then firmware should be able to use Global Reset MEI message to trigger global reset. Furthermore, if Intel ME is in ERROR state, BIOS can use I/O 0xCF9 write of 0x06 or 0x0E command with PCH ETR3 register bit [20] to perform the global reset. BUG=none BRANCH=none TEST=Verified Global Reset MEI message is able to perform platform global issue in ME good state. Change-Id: If326a137eeadaa695668b76b84c510e12c546024 Signed-off-by: Subrata Banik <subrata.banik@intel.com> Signed-off-by: Rizwan Qureshi <rizwan.qureshi@intel.com> Reviewed-on: https://review.coreboot.org/16902 Tested-by: build bot (Jenkins) Reviewed-by: Aaron Durbin <adurbin@chromium.org>
Diffstat (limited to 'src/soc/intel/skylake/me.c')
-rw-r--r--src/soc/intel/skylake/me.c683
1 files changed, 683 insertions, 0 deletions
diff --git a/src/soc/intel/skylake/me.c b/src/soc/intel/skylake/me.c
new file mode 100644
index 0000000000..2f94123374
--- /dev/null
+++ b/src/soc/intel/skylake/me.c
@@ -0,0 +1,683 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2016 Intel Corporation.
+ *
+ * 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.
+ */
+
+#include <arch/io.h>
+#include <commonlib/helpers.h>
+#include <console/console.h>
+#include <device/pci.h>
+#include <device/pci_def.h>
+#include <device/pci_ids.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <soc/iomap.h>
+#include <soc/pci_devs.h>
+#include <soc/me.h>
+#include <delay.h>
+#include <timer.h>
+
+static inline u32 me_read_config32(int offset)
+{
+ return pci_read_config32(PCH_DEV_ME, offset);
+}
+
+static inline void me_write_config32(int offset, u32 value)
+{
+ pci_write_config32(PCH_DEV_ME, offset, value);
+}
+
+static inline u32 me_read_mmio32(int offset)
+{
+ return read32((void *)(HECI1_BASE_ADDRESS + offset));
+}
+
+static inline void me_write_mmio32(u16 offset, u32 value)
+{
+ write32((void *)(HECI1_BASE_ADDRESS + offset), value);
+}
+
+/* HFSTS1[3:0] Current Working State Values */
+static const char *me_cws_values[] = {
+ [ME_HFS_CWS_RESET] = "Reset",
+ [ME_HFS_CWS_INIT] = "Initializing",
+ [ME_HFS_CWS_REC] = "Recovery",
+ [3] = "Unknown (3)",
+ [4] = "Unknown (4)",
+ [ME_HFS_CWS_NORMAL] = "Normal",
+ [ME_HFS_CWS_WAIT] = "Platform Disable Wait",
+ [ME_HFS_CWS_TRANS] = "OP State Transition",
+ [ME_HFS_CWS_INVALID] = "Invalid CPU Plugged In",
+ [9] = "Unknown (9)",
+ [10] = "Unknown (10)",
+ [11] = "Unknown (11)",
+ [12] = "Unknown (12)",
+ [13] = "Unknown (13)",
+ [14] = "Unknown (14)",
+ [15] = "Unknown (15)",
+};
+
+/* HFSTS1[8:6] Current Operation State Values */
+static const char *me_opstate_values[] = {
+ [ME_HFS_STATE_PREBOOT] = "Preboot",
+ [ME_HFS_STATE_M0_UMA] = "M0 with UMA",
+ [ME_HFS_STATE_M3] = "M3 without UMA",
+ [ME_HFS_STATE_M0] = "M0 without UMA",
+ [ME_HFS_STATE_BRINGUP] = "Bring up",
+ [ME_HFS_STATE_ERROR] = "M0 without UMA but with error"
+};
+
+/* HFSTS1[19:16] Current Operation Mode Values */
+static const char *me_opmode_values[] = {
+ [ME_HFS_MODE_NORMAL] = "Normal",
+ [ME_HFS_MODE_DEBUG] = "Debug",
+ [ME_HFS_MODE_DIS] = "Soft Temporary Disable",
+ [ME_HFS_MODE_OVER_JMPR] = "Security Override via Jumper",
+ [ME_HFS_MODE_OVER_MEI] = "Security Override via MEI Message"
+};
+
+/* HFSTS1[15:12] Error Code Values */
+static const char *me_error_values[] = {
+ [ME_HFS_ERROR_NONE] = "No Error",
+ [ME_HFS_ERROR_UNCAT] = "Uncategorized Failure",
+ [ME_HFS_ERROR_IMAGE] = "Image Failure",
+ [ME_HFS_ERROR_DEBUG] = "Debug Failure"
+};
+
+/* HFSTS2[31:28] ME Progress Code */
+static const char *me_progress_values[] = {
+ [ME_HFS2_PHASE_ROM] = "ROM Phase",
+ [1] = "Unknown (1)",
+ [ME_HFS2_PHASE_UKERNEL] = "uKernel Phase",
+ [ME_HFS2_PHASE_BUP] = "BUP Phase",
+ [4] = "Unknown (4)",
+ [5] = "Unknown (5)",
+ [ME_HFS2_PHASE_HOST_COMM] = "Host Communication",
+ [7] = "Unknown (7)",
+ [8] = "Unknown (8)"
+};
+
+/* HFSTS2[27:24] Power Management Event */
+static const char *me_pmevent_values[] = {
+ [ME_HFS2_PMEVENT_CLEAN_MOFF_MX_WAKE] =
+ "Clean Moff->Mx wake",
+ [ME_HFS2_PMEVENT_MOFF_MX_WAKE_ERROR] =
+ "Moff->Mx wake after an error",
+ [ME_HFS2_PMEVENT_CLEAN_GLOBAL_RESET] =
+ "Clean global reset",
+ [ME_HFS2_PMEVENT_CLEAN_GLOBAL_RESET_ERROR] =
+ "Global reset after an error",
+ [ME_HFS2_PMEVENT_CLEAN_ME_RESET] =
+ "Clean Intel ME reset",
+ [ME_HFS2_PMEVENT_ME_RESET_EXCEPTION] =
+ "Intel ME reset due to exception",
+ [ME_HFS2_PMEVENT_PSEUDO_ME_RESET] =
+ "Pseudo-global reset",
+ [ME_HFS2_PMEVENT_CM0_CM3] =
+ "CM0->CM3",
+ [ME_HFS2_PMEVENT_CM3_CM0] =
+ "CM3->CM0",
+ [ME_HFS2_PMEVENT_NON_PWR_CYCLE_RESET] =
+ "Non-power cycle reset",
+ [ME_HFS2_PMEVENT_PWR_CYCLE_RESET_M3] =
+ "Power cycle reset through M3",
+ [ME_HFS2_PMEVENT_PWR_CYCLE_RESET_MOFF] =
+ "Power cycle reset through Moff",
+ [ME_HFS2_PMEVENT_CMX_CMOFF] =
+ "Cx/Mx->Cx/Moff",
+ [ME_HFS2_PMEVENT_CM0_CM0PG] =
+ "CM0->CM0PG",
+ [ME_HFS2_PMEVENT_CM3_CM3PG] =
+ "CM3->CM3PG",
+ [ME_HFS2_PMEVENT_CM0PG_CM0] =
+ "CM0PG->CM0"
+
+};
+
+/* Progress Code 0 states */
+static const char *me_progress_rom_values[] = {
+ [ME_HFS2_STATE_ROM_BEGIN] = "BEGIN",
+ [ME_HFS2_STATE_ROM_DISABLE] = "DISABLE"
+};
+
+/* Progress Code 1 states */
+static const char *me_progress_bup_values[] = {
+ [ME_HFS2_STATE_BUP_INIT] =
+ "Initialization starts",
+ [ME_HFS2_STATE_BUP_DIS_HOST_WAKE] =
+ "Disable the host wake event",
+ [ME_HFS2_STATE_BUP_CG_ENABLE] =
+ "Enabling CG for cset",
+ [ME_HFS2_STATE_BUP_PM_HND_EN] =
+ "Enabling PM handshaking",
+ [ME_HFS2_STATE_BUP_FLOW_DET] =
+ "Flow determination start process",
+ [ME_HFS2_STATE_BUP_PMC_PATCHING] =
+ "PMC Patching process",
+ [ME_HFS2_STATE_BUP_GET_FLASH_VSCC] =
+ "Get VSCC params",
+ [ME_HFS2_STATE_BUP_SET_FLASH_VSCC] =
+ "Set VSCC params",
+ [ME_HFS2_STATE_BUP_VSCC_ERR] =
+ "Error reading/matching the VSCC table in the descriptor",
+ [ME_HFS2_STATE_BUP_EFSS_INIT] =
+ "Initialize EFFS",
+ [ME_HFS2_STATE_BUP_CHECK_STRAP] =
+ "Check to see if straps say ME DISABLED",
+ [ME_HFS2_STATE_BUP_PWR_OK_TIMEOUT] =
+ "Timeout waiting for PWROK",
+ [ME_HFS2_STATE_BUP_STRAP_DIS] =
+ "EFFS says ME disabled",
+ [ME_HFS2_STATE_BUP_MANUF_OVRD_STRAP] =
+ "Possibly handle BUP manufacturing override strap",
+ [ME_HFS2_STATE_BUP_M3] =
+ "Bringup in M3",
+ [ME_HFS2_STATE_BUP_M0] =
+ "Bringup in M0",
+ [ME_HFS2_STATE_BUP_FLOW_DET_ERR] =
+ "Flow detection error",
+ [ME_HFS2_STATE_BUP_M3_CLK_ERR] =
+ "M3 clock switching error",
+ [ME_HFS2_STATE_BUP_CPU_RESET_DID_TIMEOUT_MEM_MISSING] =
+ "Host error - CPU reset timeout, DID timeout, memory missing",
+ [ME_HFS2_STATE_BUP_M3_KERN_LOAD] =
+ "M3 kernel load",
+ [ME_HFS2_STATE_BUP_T32_MISSING] =
+ "T34 missing - cannot program ICC",
+ [ME_HFS2_STATE_BUP_WAIT_DID] =
+ "Waiting for DID BIOS message",
+ [ME_HFS2_STATE_BUP_WAIT_DID_FAIL] =
+ "Waiting for DID BIOS message failure",
+ [ME_HFS2_STATE_BUP_DID_NO_FAIL] =
+ "DID reported no error",
+ [ME_HFS2_STATE_BUP_ENABLE_UMA] =
+ "Enabling UMA",
+ [ME_HFS2_STATE_BUP_ENABLE_UMA_ERR] =
+ "Enabling UMA error",
+ [ME_HFS2_STATE_BUP_SEND_DID_ACK] =
+ "Sending DID Ack to BIOS",
+ [ME_HFS2_STATE_BUP_SEND_DID_ACK_ERR] =
+ "Sending DID Ack to BIOS error",
+ [ME_HFS2_STATE_BUP_M0_CLK] =
+ "Switching clocks in M0",
+ [ME_HFS2_STATE_BUP_M0_CLK_ERR] =
+ "Switching clocks in M0 error",
+ [ME_HFS2_STATE_BUP_TEMP_DIS] =
+ "ME in temp disable",
+ [ME_HFS2_STATE_BUP_M0_KERN_LOAD] =
+ "M0 kernel load",
+};
+
+void intel_me_status(void)
+{
+ union me_hfs hfs;
+ union me_hfs2 hfs2;
+ union me_hfs3 hfs3;
+
+ hfs.data = me_read_config32(PCI_ME_HFSTS1);
+ hfs2.data = me_read_config32(PCI_ME_HFSTS2);
+ hfs3.data = me_read_config32(PCI_ME_HFSTS3);
+
+ /* Check Current States */
+ printk(BIOS_DEBUG, "ME: FW Partition Table : %s\n",
+ hfs.fields.fpt_bad ? "BAD" : "OK");
+ printk(BIOS_DEBUG, "ME: Bringup Loader Failure : %s\n",
+ hfs.fields.ft_bup_ld_flr ? "YES" : "NO");
+ printk(BIOS_DEBUG, "ME: Firmware Init Complete : %s\n",
+ hfs.fields.fw_init_complete ? "YES" : "NO");
+ printk(BIOS_DEBUG, "ME: Manufacturing Mode : %s\n",
+ hfs.fields.mfg_mode ? "YES" : "NO");
+ printk(BIOS_DEBUG, "ME: Boot Options Present : %s\n",
+ hfs.fields.boot_options_present ? "YES" : "NO");
+ printk(BIOS_DEBUG, "ME: Update In Progress : %s\n",
+ hfs.fields.update_in_progress ? "YES" : "NO");
+ printk(BIOS_DEBUG, "ME: D3 Support : %s\n",
+ hfs.fields.d3_support_valid ? "YES" : "NO");
+ printk(BIOS_DEBUG, "ME: D0i3 Support : %s\n",
+ hfs.fields.d0i3_support_valid ? "YES" : "NO");
+ printk(BIOS_DEBUG, "ME: Low Power State Enabled : %s\n",
+ hfs2.fields.low_power_state ? "YES" : "NO");
+ printk(BIOS_DEBUG, "ME: Power Gated : %s\n",
+ hfs2.fields.power_gating_ind ? "YES" : "NO");
+ printk(BIOS_DEBUG, "ME: CPU Replaced : %s\n",
+ hfs2.fields.cpu_replaced_sts ? "YES" : "NO");
+ printk(BIOS_DEBUG, "ME: CPU Replacement Valid : %s\n",
+ hfs2.fields.cpu_replaced_valid ? "YES" : "NO");
+ printk(BIOS_DEBUG, "ME: Current Working State : %s\n",
+ me_cws_values[hfs.fields.working_state]);
+ printk(BIOS_DEBUG, "ME: Current Operation State : %s\n",
+ me_opstate_values[hfs.fields.operation_state]);
+ printk(BIOS_DEBUG, "ME: Current Operation Mode : %s\n",
+ me_opmode_values[hfs.fields.operation_mode]);
+ printk(BIOS_DEBUG, "ME: Error Code : %s\n",
+ me_error_values[hfs.fields.error_code]);
+ printk(BIOS_DEBUG, "ME: Progress Phase : %s\n",
+ me_progress_values[hfs2.fields.progress_code]);
+ printk(BIOS_DEBUG, "ME: Power Management Event : %s\n",
+ me_pmevent_values[hfs2.fields.current_pmevent]);
+
+ printk(BIOS_DEBUG, "ME: Progress Phase State : ");
+ switch (hfs2.fields.progress_code) {
+ case ME_HFS2_PHASE_ROM: /* ROM Phase */
+ printk(BIOS_DEBUG, "%s",
+ me_progress_rom_values[hfs2.fields.current_state]);
+ break;
+
+ case ME_HFS2_PHASE_UKERNEL: /* uKernel Phase */
+ printk(BIOS_DEBUG, "0x%02x", hfs2.fields.current_state);
+ break;
+
+ case ME_HFS2_PHASE_BUP: /* Bringup Phase */
+ if (hfs2.fields.current_state < ARRAY_SIZE(me_progress_bup_values)
+ && me_progress_bup_values[hfs2.fields.current_state])
+ printk(BIOS_DEBUG, "%s",
+ me_progress_bup_values[hfs2.fields.current_state]);
+ else
+ printk(BIOS_DEBUG, "0x%02x", hfs2.fields.current_state);
+ break;
+
+ case ME_HFS2_PHASE_HOST_COMM: /* Host Communication Phase */
+ if (!hfs2.fields.current_state)
+ printk(BIOS_DEBUG, "Host communication established");
+ else
+ printk(BIOS_DEBUG, "0x%02x", hfs2.fields.current_state);
+ break;
+
+ default:
+ printk(BIOS_DEBUG, "Unknown phase: 0x%02x state: 0x%02x",
+ hfs2.fields.progress_code, hfs2.fields.current_state);
+ }
+ printk(BIOS_DEBUG, "\n");
+
+ /* Power Down Mitigation Status */
+ printk(BIOS_DEBUG, "ME: Power Down Mitigation : %s\n",
+ hfs3.fields.power_down_mitigation ? "YES" : "NO");
+
+ if (hfs3.fields.power_down_mitigation) {
+ printk(BIOS_INFO, "ME: PD Mitigation State : ");
+ if (hfs3.fields.encrypt_key_override == 1 &&
+ hfs3.fields.encrypt_key_check == 0 &&
+ hfs3.fields.pch_config_change == 0)
+ printk(BIOS_INFO, "Normal Operation");
+ else if (hfs3.fields.encrypt_key_override == 1 &&
+ hfs3.fields.encrypt_key_check == 1 &&
+ hfs3.fields.pch_config_change == 0)
+ printk(BIOS_INFO, "Issue Detected and Recovered");
+ else
+ printk(BIOS_INFO, "Issue Detected but not Recovered");
+ printk(BIOS_INFO, "\n");
+
+ printk(BIOS_DEBUG, "ME: Encryption Key Override : %s\n",
+ hfs3.fields.encrypt_key_override ? "Workaround Applied" :
+ "Unable to override");
+ printk(BIOS_DEBUG, "ME: Encryption Key Check : %s\n",
+ hfs3.fields.encrypt_key_check ? "FAIL" : "PASS");
+ printk(BIOS_DEBUG, "ME: PCH Configuration Info : %s\n",
+ hfs3.fields.pch_config_change ? "Changed" : "No Change");
+
+ printk(BIOS_DEBUG, "ME: Firmware SKU : ");
+ switch (hfs3.fields.fw_sku) {
+ case ME_HFS3_FW_SKU_CONSUMER:
+ printk(BIOS_DEBUG, "Consumer\n");
+ break;
+ case ME_HFS3_FW_SKU_CORPORATE:
+ printk(BIOS_DEBUG, "Corporate\n");
+ break;
+ default:
+ printk(BIOS_DEBUG, "Unknown (0x%x)\n", hfs3.fields.fw_sku);
+ }
+ }
+}
+
+/*
+* Aligning a byte length to length in dwords.
+*/
+static u32 get_dword_length(u32 byte_length)
+{
+ return ALIGN_UP(byte_length, sizeof(uint32_t)) / sizeof(uint32_t);
+}
+
+/*
+* Get remaining message count in dword from circular buffer based on
+* write and read offset.
+*/
+static u32 get_cb_msg_count(u32 data)
+{
+ u8 read_offset = data >> 8;
+ u8 write_offset = data >> 16;
+
+ return get_dword_length(write_offset - read_offset);
+}
+
+static int wait_heci_ready(void)
+{
+ struct stopwatch sw;
+ int timeout = 0;
+ union me_csr csr;
+
+ stopwatch_init_msecs_expire(&sw, HECI_TIMEOUT);
+ while (1) {
+ do {
+ csr.data = me_read_mmio32(MMIO_ME_CSR);
+ if (csr.fields.host_ready)
+ return 0;
+ } while (!(timeout = stopwatch_expired(&sw)));
+
+ printk(BIOS_ERR, "ME_RDY bit is not set after 15 sec");
+ return -1;
+ }
+}
+
+static int wait_heci_cb_avail(u32 len)
+{
+ struct stopwatch sw;
+ union host_csr csr;
+
+ csr.data = me_read_mmio32(MMIO_HOST_CSR);
+ /*
+ * if timeout has happened, return failure as
+ * the circular buffer is not empty
+ */
+ stopwatch_init_msecs_expire(&sw, HECI_SEND_TIMEOUT);
+ /* Must have room for message and message header */
+ while (len > (get_dword_length(csr.fields.me_cir_depth) -
+ get_cb_msg_count(csr.data))) {
+ if (stopwatch_expired(&sw)) {
+ printk(BIOS_ERR,
+ "Circular Buffer never emptied within 5 sec");
+ return -1;
+ }
+ /* wait before trying again */
+ udelay(HECI_DELAY);
+ /* read HOST_CSR for next iteration */
+ csr.data = me_read_mmio32(MMIO_HOST_CSR);
+ }
+ return 0;
+}
+
+static int send_heci_packet(union mei_header *head, u32 len, u32 *payload)
+{
+ int sts;
+ int index;
+ union me_csr csr;
+ union host_csr hcsr;
+
+ /*
+ * wait until there is sufficient room in CB
+ */
+ sts = wait_heci_cb_avail(len + 1);
+ if (sts != 0)
+ return -1;
+
+ /* Write message header */
+ me_write_mmio32(MMIO_ME_CB_WW, head->data);
+
+ /* Write message body */
+ for (index = 0; index < len; index++)
+ me_write_mmio32(MMIO_ME_CB_WW, payload[index]);
+
+ /* Set Interrupt Generate bit */
+ hcsr.data = me_read_mmio32(MMIO_HOST_CSR);
+ hcsr.fields.int_gen = 1;
+ me_write_mmio32(MMIO_HOST_CSR, hcsr.data);
+
+ /* Check if ME Ready bit is set, if set to 0 then return fatal error */
+ csr.data = me_read_mmio32(MMIO_ME_CSR);
+ if (csr.fields.host_ready)
+ return 0;
+ else
+ return -1;
+}
+
+static int recv_heci_packet(union mei_header *head, u32 *packet,
+ u32 *packet_size)
+{
+ union me_csr csr;
+ union host_csr hcsr;
+ int rec_msg = 0;
+ struct stopwatch sw;
+ u32 length, index;
+
+ /* Clear Interrupt Status bit */
+ hcsr.data = me_read_mmio32(MMIO_HOST_CSR);
+ hcsr.fields.int_sts = 1;
+ me_write_mmio32(MMIO_HOST_CSR, hcsr.data);
+
+ /* Check if circular buffer overflow
+ * if yes then return fatal error
+ */
+ csr.data = me_read_mmio32(MMIO_ME_CSR);
+ if (get_cb_msg_count(csr.data) >
+ get_dword_length(csr.fields.me_cir_buff))
+ return -1;
+ /*
+ * if timeout has happened, return failure as
+ * the circular buffer is not empty
+ */
+ stopwatch_init_msecs_expire(&sw, HECI_READ_TIMEOUT);
+ /* go until we got message pkt */
+ do {
+ if (stopwatch_expired(&sw)) {
+ printk(BIOS_ERR,
+ "Circular Buffer not filled within 5 sec");
+ *packet_size = 0;
+ return -1;
+ }
+ csr.data = me_read_mmio32(MMIO_ME_CSR);
+ /* Read one message from HECI buffer */
+ if (get_cb_msg_count(csr.data) > 0) {
+ head->data = me_read_mmio32(MMIO_ME_CB_RW);
+ /* calculate the message length in dword */
+ length = get_dword_length(head->fields.length);
+ if (head->fields.length == 0) {
+ *packet_size = 0;
+ goto SET_IG;
+ }
+ /* Make sure, we have enough space to catch all */
+ if (head->fields.length <= *packet_size) {
+ csr.data = me_read_mmio32(MMIO_ME_CSR);
+ /* get complete message into circular buffer */
+ while (length > get_cb_msg_count(csr.data)) {
+ udelay(HECI_DELAY);
+ csr.data = me_read_mmio32(MMIO_ME_CSR);
+ }
+ /* here is the message */
+ for (index = 0; index < length; index++)
+ packet[index] = me_read_mmio32(MMIO_ME_CB_RW);
+
+ rec_msg = 1;
+ *packet_size = head->fields.length;
+ } else {
+ /* Too small buffer */
+ *packet_size = 0;
+ return -1;
+ }
+ }
+ } while (!rec_msg);
+
+ /*
+ * Check if ME Ready bit is set, if set to 0 then return fatal error
+ * because ME might have reset during transaction and we might have
+ * read a junk data from CB
+ */
+ csr.data = me_read_mmio32(MMIO_ME_CSR);
+ if (!(csr.fields.host_ready))
+ return -1;
+SET_IG:
+ /* Set Interrupt Generate bit */
+ hcsr.data = me_read_mmio32(MMIO_HOST_CSR);
+ hcsr.fields.int_gen = 1;
+ me_write_mmio32(MMIO_HOST_CSR, hcsr.data);
+ return 0;
+}
+
+static int
+send_heci_message(void *msg, int len, u8 hostaddress, u8 clientaddress)
+{
+ u8 retry;
+ int status = -1;
+ u32 cir_buff_depth;
+ union host_csr csr;
+ union mei_header head;
+ int cur = 0;
+ u32 slength, rlength;
+
+ for (retry = 0; retry < MAX_HECI_MESSAGE; retry++) {
+ if (wait_heci_ready() != 0)
+ continue;
+ /* HECI is ready */
+ csr.data = me_read_mmio32(MMIO_HOST_CSR);
+ cir_buff_depth = csr.fields.me_cir_depth;
+ head.fields.client_address = clientaddress;
+ head.fields.host_address = hostaddress;
+ while (len > cur) {
+ rlength = get_dword_length(len - cur);
+ /*
+ * Set the message complete bit if this is last packet
+ * in message needs to be "less than" to account for
+ * the header OR needs to be exact equal to CB depth
+ */
+ if (rlength <= cir_buff_depth)
+ head.fields.is_complete = 1;
+ else
+ head.fields.is_complete = 0;
+ /*
+ * calculate length for message header
+ * header length = smaller of CB buffer or
+ * remaining message
+ */
+ slength = ((cir_buff_depth <= rlength)
+ ? ((cir_buff_depth - 1) * 4)
+ : (len - cur));
+ head.fields.length = slength;
+ head.fields.reserved = 0;
+ /*
+ * send the current packet
+ * (cur should be treated as index for message)
+ */
+ status = send_heci_packet(&head,
+ get_dword_length(head.fields.length), msg);
+ if (status != 0)
+ break;
+ /* update the length information */
+ cur += slength;
+ msg += cur;
+ }
+ if (!status)
+ break;
+ }
+ return status;
+}
+
+static int
+recv_heci_message(void *message, u32 * message_size)
+{
+ union mei_header head;
+ int cur = 0;
+ u8 retry;
+ int status = -1;
+ int msg_complete = 0;
+ u32 pkt_buff;
+
+ for (retry = 0; retry < MAX_HECI_MESSAGE; retry++) {
+ if (wait_heci_ready() != 0)
+ continue;
+ /* HECI is ready */
+ while ((cur < *message_size) && (msg_complete == 0)) {
+ pkt_buff = *message_size - cur;
+ status = recv_heci_packet(&head, message + (cur >> 2),
+ &pkt_buff);
+ if (status == -1) {
+ *message_size = 0;
+ break;
+ }
+ msg_complete = head.fields.is_complete;
+ if (pkt_buff == 0) {
+ /* if not in middle of msg and msg complete bit
+ * is set then this is a valid zero length msg
+ */
+ if ((cur == 0) && (msg_complete == 1))
+ status = 0;
+ else
+ status = -1;
+ *message_size = 0;
+ break;
+ }
+ cur += pkt_buff;
+ }
+ if (!status) {
+ *message_size = cur;
+ break;
+ }
+ }
+ return status;
+}
+
+static int send_heci_reset_message(void)
+{
+ int status;
+ struct reset_reply {
+ u8 group_id;
+ u8 command;
+ u8 reserved;
+ u8 result;
+ } __attribute__ ((packed)) reply;
+ struct reset_message {
+ u8 group_id;
+ u8 cmd;
+ u8 reserved;
+ u8 result;
+ u8 req_origin;
+ u8 reset_type;
+ } __attribute__ ((packed));
+ struct reset_message msg = {
+ .cmd = MKHI_GLOBAL_RESET,
+ .req_origin = GR_ORIGIN_BIOS_POST,
+ .reset_type = GLOBAL_RST_TYPE
+ };
+ u32 reply_size;
+
+ status= send_heci_message(&msg, sizeof(msg),
+ BIOS_HOST_ADD, HECI_MKHI_ADD);
+ if (status != 0)
+ return -1;
+
+ reply_size = sizeof(reply);
+ if (recv_heci_message(&reply, &reply_size) == -1)
+ return -1;
+ /* get reply result from HECI MSG */
+ if (reply.result != 0) {
+ printk(BIOS_DEBUG, "%s: Exit with Failure\n", __func__);
+ return -1;
+ } else {
+ printk(BIOS_DEBUG, "%s: Exit with Success\n", __func__);
+ return 0;
+ }
+}
+
+int send_global_reset(void)
+{
+ int status = -1;
+ union me_hfs hfs;
+
+ /* Check ME operating mode */
+ hfs.data = me_read_config32(PCI_ME_HFSTS1);
+ if (hfs.fields.operation_mode)
+ goto ret;
+
+ /* ME should be in Normal Mode for this command */
+ status = send_heci_reset_message();
+ret:
+ return status;
+}