Brandon Breitenstein has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/42077 )
Change subject: soc/intel/common/block: Enable PMC IPC driver ......................................................................
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; +}