diff options
author | Brandon Breitenstein <brandon.breitenstein@intel.com> | 2019-12-19 23:36:47 -0800 |
---|---|---|
committer | Patrick Georgi <pgeorgi@google.com> | 2020-10-14 05:37:17 +0000 |
commit | 9faab3122e13d767bd95dd0887235a368e94d573 (patch) | |
tree | 8a6e0e5af7db5bd178bc68f1b99c7a4587100293 | |
parent | 7377cda6089210068b9d163083e6084439aa3e88 (diff) |
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:151731851
BRANCH=none
TEST=built coreboot image and booted to Chrome OS
Change-Id: Ide3528975be23585ce305f6cc909767b96af200f
Signed-off-by: Brandon Breitenstein <brandon.breitenstein@intel.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/42077
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Tim Wawrzynczak <twawrzynczak@chromium.org>
-rw-r--r-- | src/soc/intel/common/block/include/intelblocks/pmc_ipc.h | 49 | ||||
-rw-r--r-- | src/soc/intel/common/block/pmc/Makefile.inc | 2 | ||||
-rw-r--r-- | src/soc/intel/common/block/pmc/pmc_ipc.c | 96 |
3 files changed, 146 insertions, 1 deletions
diff --git a/src/soc/intel/common/block/include/intelblocks/pmc_ipc.h b/src/soc/intel/common/block/include/intelblocks/pmc_ipc.h new file mode 100644 index 0000000000..0c90cd7df6 --- /dev/null +++ b/src/soc/intel/common/block/include/intelblocks/pmc_ipc.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef SOC_INTEL_COMMON_BLOCK_PMC_IPC_H +#define SOC_INTEL_COMMON_BLOCK_PMC_IPC_H + +#include <types.h> + +#define PMC_IPC_BUF_COUNT 4 + +#define PMC_IPC_CMD_COMMAND_SHIFT 0 +#define PMC_IPC_CMD_COMMAND_MASK 0xff +#define PMC_IPC_CMD_MSI_SHIFT 8 +#define PMC_IPC_CMD_MSI_MASK 0x01 +#define PMC_IPC_CMD_SUB_COMMAND_SHIFT 12 +#define PMC_IPC_CMD_SUB_COMMAND_MASK 0x0f +#define PMC_IPC_CMD_SIZE_SHIFT 16 +#define PMC_IPC_CMD_SIZE_MASK 0xff + +#define PMC_IPC_CMD_FIELD(name, val) \ + (((val) & PMC_IPC_CMD_##name##_MASK << PMC_IPC_CMD_##name##_SHIFT)) + +#define PMC_IPC_CMD_NO_MSI 0 + +/* + * Create the IPC CMD to send to PMC + */ +static inline uint32_t pmc_make_ipc_cmd(uint32_t cmd, uint32_t subcmd, + uint32_t size) +{ + return PMC_IPC_CMD_FIELD(COMMAND, cmd) | + PMC_IPC_CMD_FIELD(SUB_COMMAND, subcmd) | + PMC_IPC_CMD_FIELD(MSI, PMC_IPC_CMD_NO_MSI) | + PMC_IPC_CMD_FIELD(SIZE, size); +} + +/* + * Buffer for holding write and read buffers of IPC commands + */ +struct pmc_ipc_buffer { + uint32_t buf[PMC_IPC_BUF_COUNT]; +}; + +/* + * Send PMC IPC command + */ +enum cb_err pmc_send_ipc_cmd(uint32_t cmd, const struct pmc_ipc_buffer *wbuf, + struct pmc_ipc_buffer *rbuf); + +#endif /* SOC_INTEL_COMMON_BLOCK_PMC_IPC_H */ diff --git a/src/soc/intel/common/block/pmc/Makefile.inc b/src/soc/intel/common/block/pmc/Makefile.inc index 965721714d..796a039aed 100644 --- a/src/soc/intel/common/block/pmc/Makefile.inc +++ b/src/soc/intel/common/block/pmc/Makefile.inc @@ -2,7 +2,7 @@ ifeq ($(CONFIG_SOC_INTEL_COMMON_BLOCK_PMC),y) bootblock-y += pmclib.c romstage-y += pmclib.c ramstage-y += pmc.c -ramstage-y += pmclib.c +ramstage-y += pmclib.c pmc_ipc.c smm-y += pmclib.c verstage-y += pmclib.c postcar-y += pmclib.c diff --git a/src/soc/intel/common/block/pmc/pmc_ipc.c b/src/soc/intel/common/block/pmc/pmc_ipc.c new file mode 100644 index 0000000000..7decf790a9 --- /dev/null +++ b/src/soc/intel/common/block/pmc/pmc_ipc.c @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <device/mmio.h> +#include <console/console.h> +#include <delay.h> +#include <intelblocks/pmclib.h> +#include <intelblocks/pmc_ipc.h> +#include <stdint.h> +#include <timer.h> + +/* + * WBUF register block offset 0x80..0x8f there are 4 consecutive + * 32 bit registers + */ +#define IPC_WBUF0 0x80 + +/* + * RBUF registers block offset 0x90..0x9f there are 4 consecutive + * 32 bit registers + */ +#define IPC_RBUF0 0x90 + +/* + * From Intel 500 Series PCH EDS vol2 s4.4 + */ +#define PMC_IPC_CMD_OFFSET 0x0 +#define PMC_IPC_STS_OFFSET 0x4 +#define PMC_IPC_STS_BUSY BIT(0) +#define PMC_IPC_STS_ERR BIT(1) +#define PMC_IPC_ERR_CODE_SHIFT 16 +#define PMC_IPC_ERR_CODE_MASK 0xff + +#define PMC_IPC_XFER_TIMEOUT_MS (1 * MSECS_PER_SEC) /* max 1s */ +#define IS_IPC_STS_BUSY(status) ((status) & PMC_IPC_STS_BUSY) +#define IPC_STS_HAS_ERROR(status) ((status) & PMC_IPC_STS_ERR) +#define IPC_STS_ERROR_CODE(sts) (((sts) >> PMC_IPC_ERR_CODE_SHIFT & \ + PMC_IPC_ERR_CODE_MASK)) + +static void *pmc_reg(unsigned int pmc_reg_offset) +{ + const uintptr_t pmcbase = soc_read_pmc_base(); + return (void *)(pmcbase + pmc_reg_offset); +} + +static const void *pmc_rbuf(unsigned int ix) +{ + return pmc_reg(IPC_RBUF0 + ix * sizeof(uint32_t)); +} + +static void *pmc_wbuf(unsigned int ix) +{ + return pmc_reg(IPC_WBUF0 + ix * sizeof(uint32_t)); +} + +static int check_ipc_sts(void) +{ + struct stopwatch sw; + uint32_t ipc_sts; + + stopwatch_init_msecs_expire(&sw, PMC_IPC_XFER_TIMEOUT_MS); + do { + ipc_sts = read32(pmc_reg(PMC_IPC_STS_OFFSET)); + if (!(IS_IPC_STS_BUSY(ipc_sts))) { + if (IPC_STS_HAS_ERROR(ipc_sts)) { + printk(BIOS_ERR, "IPC_STS.error_code 0x%x\n", + IPC_STS_ERROR_CODE(ipc_sts)); + return -1; + } + return 0; + } + udelay(50); + + } while (!stopwatch_expired(&sw)); + + printk(BIOS_ERR, "PMC IPC timeout after %u ms\n", PMC_IPC_XFER_TIMEOUT_MS); + return -1; +} + +enum cb_err pmc_send_ipc_cmd(uint32_t cmd, const struct pmc_ipc_buffer *wbuf, + struct pmc_ipc_buffer *rbuf) +{ + for (int i = 0; i < PMC_IPC_BUF_COUNT; ++i) + write32(pmc_wbuf(i), wbuf->buf[i]); + + write32(pmc_reg(PMC_IPC_CMD_OFFSET), cmd); + + if (check_ipc_sts()) { + printk(BIOS_ERR, "PMC IPC command 0x%x failed\n", cmd); + return CB_ERR; + } + + for (int i = 0; i < PMC_IPC_BUF_COUNT; ++i) + rbuf->buf[i] = read32(pmc_rbuf(i)); + + return CB_SUCCESS; +} |