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.c b/src/southbridge/intel/bd82x6x/me.c
index 1c56e5e..3a1ba55 100644
--- a/src/southbridge/intel/bd82x6x/me.c
+++ b/src/southbridge/intel/bd82x6x/me.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)
@@ -438,11 +142,10 @@
 	struct me_hfs hfs;
 	u32 reg32;
 
-	mei_base_address = (u32 *)
-		(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 */
@@ -548,94 +251,13 @@
 	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;
-}
-
-/* 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);
-}
-
 /* Check whether ME is present and do basic init */
 static void intel_me_init(struct device *dev)
 {
 	me_bios_path path = intel_me_path(dev);
 
 	/* 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:
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:
diff --git a/src/southbridge/intel/bd82x6x/me_common.c b/src/southbridge/intel/bd82x6x/me_common.c
new file mode 100644
index 0000000..8e38171
--- /dev/null
+++ b/src/southbridge/intel/bd82x6x/me_common.c
@@ -0,0 +1,419 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <acpi/acpi.h>
+#include <device/mmio.h>
+#include <device/device.h>
+#include <device/pci.h>
+#include <device/pci_ops.h>
+#include <console/console.h>
+#include <device/pci_ids.h>
+#include <device/pci_def.h>
+#include <string.h>
+#include <delay.h>
+#include <halt.h>
+
+#include "me.h"
+#include "pch.h"
+
+/* 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",
+};
+
+static inline const char *const me_get_bios_path_string(int path)
+{
+	return me_bios_path_values[path];
+}
+
+/* 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;
+}
+
+#ifdef __SIMPLE_DEVICE__
+
+static inline void update_mei_base_address(void)
+{
+	mei_base_address = (u32 *)(pci_read_config32(PCH_ME_DEV, PCI_BASE_ADDRESS_0) & ~0xf);
+}
+
+static inline bool is_mei_base_address_valid(void)
+{
+	return mei_base_address && mei_base_address != (u32 *)0xfffffff0;
+}
+
+#else
+
+/* 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);
+}
+
+#endif