Brandon Breitenstein has uploaded this change for review.

View Change

soc/intel/common/block: Enable PMC IPC driver

In order for USB Type-C devices to be detected prior to loading Kernel
PMC IPC driver API is needed to send IPC commands to the PMC to update
connection/disconnection states.

BUG=b:141608957
BRANCH=none
TEST: built coreboot image and booted to Chrome OS

Change-Id: Ide3528975be23585ce305f6cc909767b96af200f
Signed-off-by: Brandon Breitenstein <brandon.breitenstein@intel.com>
---
M src/soc/intel/common/block/include/intelblocks/pmclib.h
M src/soc/intel/common/block/pmc/pmclib.c
2 files changed, 101 insertions(+), 0 deletions(-)

git pull ssh://review.coreboot.org:29418/coreboot refs/changes/77/42077/1
diff --git a/src/soc/intel/common/block/include/intelblocks/pmclib.h b/src/soc/intel/common/block/include/intelblocks/pmclib.h
index 2b06a50..f7e2c13 100644
--- a/src/soc/intel/common/block/include/intelblocks/pmclib.h
+++ b/src/soc/intel/common/block/include/intelblocks/pmclib.h
@@ -9,6 +9,27 @@
/* Forward declare the power state struct here */
struct chipset_power_state;

+/* pmc_ipc_buffer struct declaration to be used for IPC commands */
+struct pmc_ipc_buffer {
+ uint32_t buf0;
+ uint32_t buf1;
+ uint32_t buf2;
+ uint32_t buf3;
+};
+
+/* pmc_ipc_cmd union declaration to store cmd data for IPC commands */
+union pmc_ipc_cmd {
+ uint32_t cmd_reg;
+ struct {
+ uint32_t cmd:8;
+ uint32_t msi:1;
+ uint32_t res_1:3;
+ uint32_t subcmd:4;
+ uint32_t len:8;
+ uint32_t res_2:8;
+ };
+};
+
/*
* This is implemented as weak function in common pmc lib.
* Clears all power management related registers as the boot
@@ -220,4 +241,10 @@
*/
void pmc_set_power_failure_state(bool target_on);

+/*
+ * Send PMC IPC command
+ */
+enum cb_err pmc_send_ipc_cmd(uint32_t cmd, struct pmc_ipc_buffer *wbuf,
+ struct pmc_ipc_buffer *rbuf);
+
#endif /* SOC_INTEL_COMMON_BLOCK_PMCLIB_H */
diff --git a/src/soc/intel/common/block/pmc/pmclib.c b/src/soc/intel/common/block/pmc/pmclib.c
index 12eb38e..818a36b 100644
--- a/src/soc/intel/common/block/pmc/pmclib.c
+++ b/src/soc/intel/common/block/pmc/pmclib.c
@@ -5,6 +5,7 @@
#include <device/mmio.h>
#include <cbmem.h>
#include <console/console.h>
+#include <delay.h>
#include <halt.h>
#include <intelblocks/pmclib.h>
#include <intelblocks/gpio.h>
@@ -16,6 +17,26 @@
#include <stdint.h>
#include <string.h>
#include <timer.h>
+#include <types.h>
+
+/* define the offsets of all WBUFs */
+#define PMC_IPC_WBUF0 0x80
+#define PMC_IPC_WBUF1 0x84
+#define PMC_IPC_WBUF2 0x88
+#define PMC_IPC_WBUF3 0x8C
+
+/* define the offsets of all RBUFs */
+#define PMC_IPC_RBUF0 0x90
+#define PMC_IPC_RBUF1 0x94
+#define PMC_IPC_RBUF2 0x98
+#define PMC_IPC_RBUF3 0x9C
+
+#define PMC_IPC_CMD_OFFSET 0x0
+#define PMC_IPC_USBC_CMD_ID 0xA7
+#define PMC_IPC_STS_OFFSET 0x4
+#define PMC_IPC_STS_BUSY BIT(0)
+#define PMC_IPC_STS_ERR BIT(1)
+#define PMC_IPC_XFER_TIMEOUT_MS 1000 /* max 1s*/

static struct chipset_power_state power_state;

@@ -574,3 +595,56 @@

pmc_soc_set_afterg3_en(on);
}
+
+static enum cb_err check_ipc_sts(uintptr_t pmcbase)
+{
+ struct stopwatch sw;
+ uint32_t ipcsts;
+
+ stopwatch_init_msecs_expire(&sw, PMC_IPC_XFER_TIMEOUT_MS);
+ do {
+ ipcsts = read32((void *)(pmcbase + PMC_IPC_STS_OFFSET));
+ if (ipcsts & PMC_IPC_STS_ERR)
+ return CB_ERR;
+
+ udelay(1);
+
+ } while (!stopwatch_expired(&sw) && (ipcsts & PMC_IPC_STS_BUSY));
+
+ if (ipcsts & PMC_IPC_STS_BUSY) {
+ printk(BIOS_ERR, "IPC Timeout after %d ms\n", PMC_IPC_XFER_TIMEOUT_MS);
+ return CB_ERR;
+ }
+
+ return CB_SUCCESS;
+}
+
+enum cb_err pmc_send_ipc_cmd(uint32_t cmd, struct pmc_ipc_buffer *wbuf,
+ struct pmc_ipc_buffer *rbuf)
+{
+ uintptr_t pmcbase;
+
+ pmcbase = soc_read_pmc_base();
+
+ /* write the entire WBUF with the new PMC CMD Buffer */
+ write32((void *)(pmcbase + PMC_IPC_WBUF0), wbuf->buf0);
+ write32((void *)(pmcbase + PMC_IPC_WBUF1), wbuf->buf1);
+ write32((void *)(pmcbase + PMC_IPC_WBUF2), wbuf->buf2);
+ write32((void *)(pmcbase + PMC_IPC_WBUF3), wbuf->buf3);
+
+ /* Write the command register with the new command */
+ write32((void *)(pmcbase + PMC_IPC_CMD_OFFSET), cmd);
+
+ if (check_ipc_sts(pmcbase)) {
+ printk(BIOS_ERR, "PMC IPC command failed\n");
+ return CB_ERR;
+ }
+
+ /* get the response from the pmc out buffer */
+ rbuf->buf0 = read32((void *)(pmcbase + PMC_IPC_RBUF0));
+ rbuf->buf1 = read32((void *)(pmcbase + PMC_IPC_RBUF1));
+ rbuf->buf2 = read32((void *)(pmcbase + PMC_IPC_RBUF2));
+ rbuf->buf3 = read32((void *)(pmcbase + PMC_IPC_RBUF3));
+
+ return CB_SUCCESS;
+}

To view, visit change 42077. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: coreboot
Gerrit-Branch: master
Gerrit-Change-Id: Ide3528975be23585ce305f6cc909767b96af200f
Gerrit-Change-Number: 42077
Gerrit-PatchSet: 1
Gerrit-Owner: Brandon Breitenstein <brandon.breitenstein@intel.com>
Gerrit-MessageType: newchange