aboutsummaryrefslogtreecommitdiff
path: root/src/soc/intel/common/block/pmc/pmc_ipc.c
blob: 7decf790a9e7d021bcc2c4aa4b4df98dca4d9008 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
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;
}