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>
This commit is contained in:
Brandon Breitenstein
2019-12-19 23:36:47 -08:00
committed by Patrick Georgi
parent 7377cda608
commit 9faab3122e
3 changed files with 146 additions and 1 deletions

View File

@@ -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 */

View File

@@ -2,7 +2,7 @@ ifeq ($(CONFIG_SOC_INTEL_COMMON_BLOCK_PMC),y)
bootblock-y += pmclib.c bootblock-y += pmclib.c
romstage-y += pmclib.c romstage-y += pmclib.c
ramstage-y += pmc.c ramstage-y += pmc.c
ramstage-y += pmclib.c ramstage-y += pmclib.c pmc_ipc.c
smm-y += pmclib.c smm-y += pmclib.c
verstage-y += pmclib.c verstage-y += pmclib.c
postcar-y += pmclib.c postcar-y += pmclib.c

View File

@@ -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;
}