summaryrefslogtreecommitdiff
path: root/arch/arm/mach-zynqmp/pmu_ipc.c
blob: d8858ea3ff99cd3768d01bb8bcb7e133642663cb (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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
// SPDX-License-Identifier: GPL-2.0+
/*
 * Inter-Processor Communication with the Platform Management Unit (PMU)
 * firmware.
 *
 * (C) Copyright 2019 Luca Ceresoli
 * Luca Ceresoli <luca@lucaceresoli.net>
 */

#include <common.h>
#include <asm/io.h>
#include <asm/arch/sys_proto.h>

/* IPI bitmasks, register base and register offsets */
#define IPI_BIT_MASK_APU      0x00001
#define IPI_BIT_MASK_PMU0     0x10000
#define IPI_REG_BASE_APU      0xFF300000
#define IPI_REG_BASE_PMU0     0xFF330000
#define IPI_REG_OFFSET_TRIG   0x00
#define IPI_REG_OFFSET_OBR    0x04

/* IPI mailbox buffer offsets */
#define IPI_BUF_BASE_APU               0xFF990400
#define IPI_BUF_OFFSET_TARGET_PMU      0x1C0
#define IPI_BUF_OFFSET_REQ             0x00
#define IPI_BUF_OFFSET_RESP            0x20

#define PMUFW_PAYLOAD_ARG_CNT          8

/* PMUFW commands */
#define PMUFW_CMD_SET_CONFIGURATION    2

static void pmu_ipc_send_request(const u32 *req, size_t req_len)
{
	u32 *mbx = (u32 *)(IPI_BUF_BASE_APU +
			   IPI_BUF_OFFSET_TARGET_PMU +
			   IPI_BUF_OFFSET_REQ);
	size_t i;

	for (i = 0; i < req_len; i++)
		writel(req[i], &mbx[i]);
}

static void pmu_ipc_read_response(unsigned int *value, size_t count)
{
	u32 *mbx = (u32 *)(IPI_BUF_BASE_APU +
			   IPI_BUF_OFFSET_TARGET_PMU +
			   IPI_BUF_OFFSET_RESP);
	size_t i;

	for (i = 0; i < count; i++)
		value[i] = readl(&mbx[i]);
}

/**
 * Send request to PMU and get the response.
 *
 * @req:        Request buffer. Byte 0 is the API ID, other bytes are optional
 *              parameters.
 * @req_len:    Request length in number of 32-bit words.
 * @res:        Response buffer. Byte 0 is the error code, other bytes are
 *              optional parameters. Optional, if @res_maxlen==0 the parameters
 *              will not be read.
 * @res_maxlen: Space allocated for the response in number of 32-bit words.
 *
 * @return Error code returned by the PMU (i.e. the first word of the response)
 */
static int pmu_ipc_request(const u32 *req, size_t req_len,
			   u32 *res, size_t res_maxlen)
{
	u32 status;

	if (req_len > PMUFW_PAYLOAD_ARG_CNT ||
	    res_maxlen > PMUFW_PAYLOAD_ARG_CNT)
		return -EINVAL;

	pmu_ipc_send_request(req, req_len);

	/* Raise Inter-Processor Interrupt to PMU and wait for response */
	writel(IPI_BIT_MASK_PMU0, IPI_REG_BASE_APU + IPI_REG_OFFSET_TRIG);
	do {
		status = readl(IPI_REG_BASE_APU + IPI_REG_OFFSET_OBR);
	} while (status & IPI_BIT_MASK_PMU0);

	pmu_ipc_read_response(res, res_maxlen);

	return 0;
}

/**
 * Send a configuration object to the PMU firmware.
 *
 * @cfg_obj: Pointer to the configuration object
 * @size:    Size of @cfg_obj in bytes
 */
void zynqmp_pmufw_load_config_object(const void *cfg_obj, size_t size)
{
	const u32 request[] = {
		PMUFW_CMD_SET_CONFIGURATION,
		(u32)((u64)cfg_obj)
	};
	u32 response;
	int err;

	printf("Loading PMUFW cfg obj (%ld bytes)\n", size);

	err = pmu_ipc_request(request,  ARRAY_SIZE(request), &response, 1);
	if (err)
		panic("Cannot load PMUFW configuration object (%d)\n", err);
	if (response != 0)
		panic("PMUFW returned 0x%08x status!\n", response);
}