sb/intel/bd82x6x: Factor out common ME functions

We can now factor out the essentially duplicated ME functions.

We include a .c file to preserve reproducibility. This is needed because
there are two different `mei_base_address` global variables, and we have
to access the same variables in order for builds to be reproducible.

The duplicate global in `me.c` and `me_8.x.c` will be completely gone
once this new `me_common.c` file becomes a standalone compilation unit.
We are wrapping some things in static inline functions, as they won't be
directly accessible anymore after moving to a separate compilation unit.

Tested with BUILD_TIMELESS=1, Asus P8Z77-V LX2 remains identical.

Change-Id: I057809aa039d70c4b5fa9c24fbd26c8f52aca736
Signed-off-by: Angel Pons <th3fanbus@gmail.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/42012
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Arthur Heymans <arthur@aheymans.xyz>
Reviewed-by: Michael Niewöhner
diff --git a/src/southbridge/intel/bd82x6x/me_8.x.c b/src/southbridge/intel/bd82x6x/me_8.x.c
index 88792ba..4d232ed 100644
--- a/src/southbridge/intel/bd82x6x/me_8.x.c
+++ b/src/southbridge/intel/bd82x6x/me_8.x.c
@@ -27,304 +27,8 @@
 #include <vendorcode/google/chromeos/gnvs.h>
 #endif
 
-/* Path that the BIOS should take based on ME state */
-static const char *me_bios_path_values[] __unused = {
-	[ME_NORMAL_BIOS_PATH]		= "Normal",
-	[ME_S3WAKE_BIOS_PATH]		= "S3 Wake",
-	[ME_ERROR_BIOS_PATH]		= "Error",
-	[ME_RECOVERY_BIOS_PATH]		= "Recovery",
-	[ME_DISABLE_BIOS_PATH]		= "Disable",
-	[ME_FIRMWARE_UPDATE_BIOS_PATH]	= "Firmware Update",
-};
-
-/* MMIO base address for MEI interface */
-static u32 *mei_base_address;
-
-static void mei_dump(void *ptr, int dword, int offset, const char *type)
-{
-	struct mei_csr *csr;
-
-	if (!CONFIG(DEBUG_INTEL_ME))
-		return;
-
-	printk(BIOS_SPEW, "%-9s[%02x] : ", type, offset);
-
-	switch (offset) {
-	case MEI_H_CSR:
-	case MEI_ME_CSR_HA:
-		csr = ptr;
-		if (!csr) {
-			printk(BIOS_SPEW, "ERROR: 0x%08x\n", dword);
-			break;
-		}
-		printk(BIOS_SPEW, "cbd=%u cbrp=%02u cbwp=%02u ready=%u "
-		       "reset=%u ig=%u is=%u ie=%u\n", csr->buffer_depth,
-		       csr->buffer_read_ptr, csr->buffer_write_ptr,
-		       csr->ready, csr->reset, csr->interrupt_generate,
-		       csr->interrupt_status, csr->interrupt_enable);
-		break;
-	case MEI_ME_CB_RW:
-	case MEI_H_CB_WW:
-		printk(BIOS_SPEW, "CB: 0x%08x\n", dword);
-		break;
-	default:
-		printk(BIOS_SPEW, "0x%08x\n", offset);
-		break;
-	}
-}
-
-/*
- * ME/MEI access helpers using memcpy to avoid aliasing.
- */
-
-static inline void mei_read_dword_ptr(void *ptr, int offset)
-{
-	u32 dword = read32(mei_base_address + (offset/sizeof(u32)));
-	memcpy(ptr, &dword, sizeof(dword));
-	mei_dump(ptr, dword, offset, "READ");
-}
-
-static inline void mei_write_dword_ptr(void *ptr, int offset)
-{
-	u32 dword = 0;
-	memcpy(&dword, ptr, sizeof(dword));
-	write32(mei_base_address + (offset/sizeof(u32)), dword);
-	mei_dump(ptr, dword, offset, "WRITE");
-}
-
-#ifndef __SIMPLE_DEVICE__
-static inline void pci_read_dword_ptr(struct device *dev, void *ptr, int offset)
-{
-	u32 dword = pci_read_config32(dev, offset);
-	memcpy(ptr, &dword, sizeof(dword));
-	mei_dump(ptr, dword, offset, "PCI READ");
-}
-#endif
-
-static inline void read_host_csr(struct mei_csr *csr)
-{
-	mei_read_dword_ptr(csr, MEI_H_CSR);
-}
-
-static inline void write_host_csr(struct mei_csr *csr)
-{
-	mei_write_dword_ptr(csr, MEI_H_CSR);
-}
-
-static inline void read_me_csr(struct mei_csr *csr)
-{
-	mei_read_dword_ptr(csr, MEI_ME_CSR_HA);
-}
-
-static inline void write_cb(u32 dword)
-{
-	write32(mei_base_address + (MEI_H_CB_WW/sizeof(u32)), dword);
-	mei_dump(NULL, dword, MEI_H_CB_WW, "WRITE");
-}
-
-static inline u32 read_cb(void)
-{
-	u32 dword = read32(mei_base_address + (MEI_ME_CB_RW/sizeof(u32)));
-	mei_dump(NULL, dword, MEI_ME_CB_RW, "READ");
-	return dword;
-}
-
-/* Wait for ME ready bit to be asserted */
-static int mei_wait_for_me_ready(void)
-{
-	struct mei_csr me;
-	unsigned int try = ME_RETRY;
-
-	while (try--) {
-		read_me_csr(&me);
-		if (me.ready)
-			return 0;
-		udelay(ME_DELAY);
-	}
-
-	printk(BIOS_ERR, "ME: failed to become ready\n");
-	return -1;
-}
-
-static void mei_reset(void)
-{
-	struct mei_csr host;
-
-	if (mei_wait_for_me_ready() < 0)
-		return;
-
-	/* Reset host and ME circular buffers for next message */
-	read_host_csr(&host);
-	host.reset = 1;
-	host.interrupt_generate = 1;
-	write_host_csr(&host);
-
-	if (mei_wait_for_me_ready() < 0)
-		return;
-
-	/* Re-init and indicate host is ready */
-	read_host_csr(&host);
-	host.interrupt_generate = 1;
-	host.ready = 1;
-	host.reset = 0;
-	write_host_csr(&host);
-}
-
-static int mei_send_msg(struct mei_header *mei, struct mkhi_header *mkhi,
-			void *req_data)
-{
-	struct mei_csr host;
-	unsigned int ndata, n;
-	u32 *data;
-
-	/* Number of dwords to write, ignoring MKHI */
-	ndata = mei->length >> 2;
-
-	/* Pad non-dword aligned request message length */
-	if (mei->length & 3)
-		ndata++;
-	if (!ndata) {
-		printk(BIOS_DEBUG, "ME: request does not include MKHI\n");
-		return -1;
-	}
-	ndata++; /* Add MEI header */
-
-	/*
-	 * Make sure there is still room left in the circular buffer.
-	 * Reset the buffer pointers if the requested message will not fit.
-	 */
-	read_host_csr(&host);
-	if ((host.buffer_depth - host.buffer_write_ptr) < ndata) {
-		printk(BIOS_ERR, "ME: circular buffer full, resetting...\n");
-		mei_reset();
-		read_host_csr(&host);
-	}
-
-	/*
-	 * This implementation does not handle splitting large messages
-	 * across multiple transactions.  Ensure the requested length
-	 * will fit in the available circular buffer depth.
-	 */
-	if ((host.buffer_depth - host.buffer_write_ptr) < ndata) {
-		printk(BIOS_ERR, "ME: message (%u) too large for buffer (%u)\n",
-		       ndata + 2, host.buffer_depth);
-		return -1;
-	}
-
-	/* Write MEI header */
-	mei_write_dword_ptr(mei, MEI_H_CB_WW);
-	ndata--;
-
-	/* Write MKHI header */
-	mei_write_dword_ptr(mkhi, MEI_H_CB_WW);
-	ndata--;
-
-	/* Write message data */
-	data = req_data;
-	for (n = 0; n < ndata; ++n)
-		write_cb(*data++);
-
-	/* Generate interrupt to the ME */
-	read_host_csr(&host);
-	host.interrupt_generate = 1;
-	write_host_csr(&host);
-
-	/* Make sure ME is ready after sending request data */
-	return mei_wait_for_me_ready();
-}
-
-static int mei_recv_msg(struct mkhi_header *mkhi, void *rsp_data, int rsp_bytes)
-{
-	struct mei_header mei_rsp;
-	struct mkhi_header mkhi_rsp;
-	struct mei_csr me, host;
-	unsigned int ndata, n;
-	unsigned int expected;
-	u32 *data;
-
-	/* Total number of dwords to read from circular buffer */
-	expected = (rsp_bytes + sizeof(mei_rsp) + sizeof(mkhi_rsp)) >> 2;
-	if (rsp_bytes & 3)
-		expected++;
-
-	/*
-	 * The interrupt status bit does not appear to indicate that the
-	 * message has actually been received.  Instead we wait until the
-	 * expected number of dwords are present in the circular buffer.
-	 */
-	for (n = ME_RETRY; n; --n) {
-		read_me_csr(&me);
-		if ((me.buffer_write_ptr - me.buffer_read_ptr) >= expected)
-			break;
-		udelay(ME_DELAY);
-	}
-	if (!n) {
-		printk(BIOS_ERR, "ME: timeout waiting for data: expected "
-		       "%u, available %u\n", expected,
-		       me.buffer_write_ptr - me.buffer_read_ptr);
-		return -1;
-	}
-
-	/* Read and verify MEI response header from the ME */
-	mei_read_dword_ptr(&mei_rsp, MEI_ME_CB_RW);
-	if (!mei_rsp.is_complete) {
-		printk(BIOS_ERR, "ME: response is not complete\n");
-		return -1;
-	}
-
-	/* Handle non-dword responses and expect at least MKHI header */
-	ndata = mei_rsp.length >> 2;
-	if (mei_rsp.length & 3)
-		ndata++;
-	if (ndata != (expected - 1)) {
-		printk(BIOS_ERR, "ME: response is missing data %d != %d\n",
-		       ndata, (expected - 1));
-		return -1;
-	}
-
-	/* Read and verify MKHI response header from the ME */
-	mei_read_dword_ptr(&mkhi_rsp, MEI_ME_CB_RW);
-	if (!mkhi_rsp.is_response ||
-	    mkhi->group_id != mkhi_rsp.group_id ||
-	    mkhi->command != mkhi_rsp.command) {
-		printk(BIOS_ERR, "ME: invalid response, group %u ?= %u, "
-		       "command %u ?= %u, is_response %u\n", mkhi->group_id,
-		       mkhi_rsp.group_id, mkhi->command, mkhi_rsp.command,
-		       mkhi_rsp.is_response);
-		return -1;
-	}
-	ndata--; /* MKHI header has been read */
-
-	/* Make sure caller passed a buffer with enough space */
-	if (ndata != (rsp_bytes >> 2)) {
-		printk(BIOS_ERR, "ME: not enough room in response buffer: "
-		       "%u != %u\n", ndata, rsp_bytes >> 2);
-		return -1;
-	}
-
-	/* Read response data from the circular buffer */
-	data = rsp_data;
-	for (n = 0; n < ndata; ++n)
-		*data++ = read_cb();
-
-	/* Tell the ME that we have consumed the response */
-	read_host_csr(&host);
-	host.interrupt_status = 1;
-	host.interrupt_generate = 1;
-	write_host_csr(&host);
-
-	return mei_wait_for_me_ready();
-}
-
-static inline int mei_sendrecv(struct mei_header *mei, struct mkhi_header *mkhi,
-			       void *req_data, void *rsp_data, int rsp_bytes)
-{
-	if (mei_send_msg(mei, mkhi, req_data) < 0)
-		return -1;
-	if (mei_recv_msg(mkhi, rsp_data, rsp_bytes) < 0)
-		return -1;
-	return 0;
-}
+/* FIXME: For verification purposes only */
+#include "me_common.c"
 
 /* Send END OF POST message to the ME */
 static int __unused mkhi_end_of_post(void)
@@ -431,11 +135,10 @@
 	struct me_hfs hfs;
 	u32 reg32;
 
-	mei_base_address = (void *)
-		(pci_read_config32(PCH_ME_DEV, PCI_BASE_ADDRESS_0) & ~0xf);
+	update_mei_base_address();
 
 	/* S3 path will have hidden this device already */
-	if (!mei_base_address || mei_base_address == (u32 *)0xfffffff0)
+	if (!is_mei_base_address_valid())
 		return;
 
 	/* Make sure ME is in a mode that expects EOP */
@@ -533,91 +236,6 @@
 	return path;
 }
 
-/* Prepare ME for MEI messages */
-static int intel_mei_setup(struct device *dev)
-{
-	struct resource *res;
-	struct mei_csr host;
-
-	/* Find the MMIO base for the ME interface */
-	res = find_resource(dev, PCI_BASE_ADDRESS_0);
-	if (!res || res->base == 0 || res->size == 0) {
-		printk(BIOS_DEBUG, "ME: MEI resource not present!\n");
-		return -1;
-	}
-	mei_base_address = (u32 *)(uintptr_t)res->base;
-
-	/* Ensure Memory and Bus Master bits are set */
-	pci_or_config16(dev, PCI_COMMAND, PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY);
-
-	/* Clean up status for next message */
-	read_host_csr(&host);
-	host.interrupt_generate = 1;
-	host.ready = 1;
-	host.reset = 0;
-	write_host_csr(&host);
-
-	return 0;
-}
-
-#if CONFIG(CHROMEOS)
-#include <vendorcode/google/chromeos/chromeos.h>
-#endif
-
-/* Read the Extend register hash of ME firmware */
-static int intel_me_extend_valid(struct device *dev)
-{
-	struct me_heres status;
-	u32 extend[8] = {0};
-	int i, count = 0;
-
-	pci_read_dword_ptr(dev, &status, PCI_ME_HERES);
-	if (!status.extend_feature_present) {
-		printk(BIOS_ERR, "ME: Extend Feature not present\n");
-		return -1;
-	}
-
-	if (!status.extend_reg_valid) {
-		printk(BIOS_ERR, "ME: Extend Register not valid\n");
-		return -1;
-	}
-
-	switch (status.extend_reg_algorithm) {
-	case PCI_ME_EXT_SHA1:
-		count = 5;
-		printk(BIOS_DEBUG, "ME: Extend SHA-1: ");
-		break;
-	case PCI_ME_EXT_SHA256:
-		count = 8;
-		printk(BIOS_DEBUG, "ME: Extend SHA-256: ");
-		break;
-	default:
-		printk(BIOS_ERR, "ME: Extend Algorithm %d unknown\n",
-		       status.extend_reg_algorithm);
-		return -1;
-	}
-
-	for (i = 0; i < count; ++i) {
-		extend[i] = pci_read_config32(dev, PCI_ME_HER(i));
-		printk(BIOS_DEBUG, "%08x", extend[i]);
-	}
-	printk(BIOS_DEBUG, "\n");
-
-#if CONFIG(CHROMEOS)
-	/* Save hash in NVS for the OS to verify */
-	chromeos_set_me_hash(extend, count);
-#endif
-
-	return 0;
-}
-
-/* Hide the ME virtual PCI devices */
-static void intel_me_hide(struct device *dev)
-{
-	dev->enabled = 0;
-	pch_enable(dev);
-}
-
 static int intel_me_read_mbp(me_bios_payload *mbp_data);
 
 /* Check whether ME is present and do basic init */
@@ -627,7 +245,7 @@
 	me_bios_payload mbp_data;
 
 	/* Do initial setup and determine the BIOS path */
-	printk(BIOS_NOTICE, "ME: BIOS path: %s\n", me_bios_path_values[path]);
+	printk(BIOS_NOTICE, "ME: BIOS path: %s\n", me_get_bios_path_string(path));
 
 	switch (path) {
 	case ME_S3WAKE_BIOS_PATH: