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 /src/soc/intel/common/block/pmc/pmc_ipc.c | |
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>
Diffstat (limited to 'src/soc/intel/common/block/pmc/pmc_ipc.c')
-rw-r--r-- | src/soc/intel/common/block/pmc/pmc_ipc.c | 96 |
1 files changed, 96 insertions, 0 deletions
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; +} |