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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
|
// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2024-2026 Arm Ltd.
#include <linux/arm_mpam.h>
#include <linux/cleanup.h>
#include <linux/errno.h>
#include <linux/mailbox_client.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#include <acpi/pcc.h>
#include <asm/mpam.h>
#include "mpam_internal.h"
#define MPAM_MSC_TOKEN_MASK GENMASK_U32(27, 18)
#define MPAM_MSC_PROT_ID_MASK GENMASK_U32(17, 10)
#define MPAM_FB_PROTOCOL_ID 0x1a
#define MPAM_PROTOCOL_VERSION_CMD 0x0
#define MPAM_FB_VERSION_MAJOR_MASK GENMASK_U32(31, 16)
#define MPAM_FB_VERSION_MINOR_MASK GENMASK_U32(15, 0)
#define MPAM_MSC_READ_CMD 0x4
#define MPAM_MSC_WRITE_CMD 0x5
#define MPAM_FB_PROT_HEADER_LEN sizeof(u32)
/* The longest message is MPAM_MSC_WRITE, with 4 parameters. */
#define MPAM_FB_MAX_MSG_SIZE (4 * sizeof(u32))
#define MPAM_FB_SUCCESS 0
#define MPAM_FB_ERR_NOT_SUPPORTED -1
#define MPAM_FB_ERR_INVALID_PARAMETERS -2
#define MPAM_FB_ERR_DENIED -3
#define MPAM_FB_ERR_NOT_FOUND -4
#define MPAM_FB_ERR_OUT_OF_RANGE -5
#define MPAM_FB_ERR_BUSY -6
#define MPAM_FB_ERR_COMMS_ERROR -7
#define MPAM_FB_ERR_GENERIC_ERROR -8
#define MPAM_FB_ERR_HW_ERROR -9
#define MPAM_FB_ERR_PROTOCOL_ERROR -10
#define MPAM_FB_ERR_IN_USE -11
static atomic_t mpam_fb_token = ATOMIC_INIT(0);
static int mpam_fb_to_linux_errno(int mpam_fb_code)
{
switch (mpam_fb_code) {
case MPAM_FB_ERR_NOT_SUPPORTED:
return -EOPNOTSUPP;
case MPAM_FB_ERR_INVALID_PARAMETERS:
return -EINVAL;
case MPAM_FB_ERR_NOT_FOUND:
return -ENOENT;
case MPAM_FB_ERR_OUT_OF_RANGE:
return -ERANGE;
case MPAM_FB_ERR_BUSY:
return -EBUSY;
default:
return -EINVAL;
}
}
static void mpam_fb_build_version_message(unsigned int token,
void __iomem *msg_buf)
{
struct acpi_pcct_ext_pcc_shared_memory __iomem *pcc_shmem = msg_buf;
/* .signature is filled by the platform */
writel_relaxed(PCC_CMD_COMPLETION_NOTIFY, &pcc_shmem->flags);
writel_relaxed(MPAM_FB_PROT_HEADER_LEN, &pcc_shmem->length);
writel_relaxed(MPAM_PROTOCOL_VERSION_CMD |
FIELD_PREP(MPAM_MSC_TOKEN_MASK, token) |
FIELD_PREP(MPAM_MSC_PROT_ID_MASK, MPAM_FB_PROTOCOL_ID),
&pcc_shmem->command);
}
static void mpam_fb_build_read_message(int msc_fb_id, int reg,
unsigned int token,
void __iomem *msg_buf)
{
struct acpi_pcct_ext_pcc_shared_memory __iomem *pcc_shmem = msg_buf;
struct mpam_fb_read_payload {
u32 msc_fb_id;
u32 flags;
u32 reg_offset;
} __packed __iomem *payload = msg_buf + sizeof(*pcc_shmem);
int msg_size = MPAM_FB_PROT_HEADER_LEN + sizeof(*payload);
/* .signature is filled by the platform */
writel_relaxed(PCC_CMD_COMPLETION_NOTIFY, &pcc_shmem->flags);
writel_relaxed(msg_size, &pcc_shmem->length);
writel_relaxed(MPAM_MSC_READ_CMD |
FIELD_PREP(MPAM_MSC_TOKEN_MASK, token) |
FIELD_PREP(MPAM_MSC_PROT_ID_MASK, MPAM_FB_PROTOCOL_ID),
&pcc_shmem->command);
writel_relaxed(msc_fb_id, &payload->msc_fb_id);
writel_relaxed(0, &payload->flags);
writel_relaxed(reg, &payload->reg_offset);
}
static void mpam_fb_build_write_message(int msc_fb_id, int reg, u32 val,
unsigned int token,
void __iomem *msg_buf)
{
struct acpi_pcct_ext_pcc_shared_memory __iomem *pcc_shmem = msg_buf;
struct mpam_fb_write_payload {
u32 msc_fb_id;
u32 flags;
u32 reg_offset;
u32 value;
} __packed __iomem *payload = msg_buf + sizeof(*pcc_shmem);
int msg_size = MPAM_FB_PROT_HEADER_LEN + sizeof(*payload);
/* .signature is filled by the platform */
writel_relaxed(PCC_CMD_COMPLETION_NOTIFY, &pcc_shmem->flags);
writel_relaxed(msg_size, &pcc_shmem->length);
writel_relaxed(MPAM_MSC_WRITE_CMD |
FIELD_PREP(MPAM_MSC_TOKEN_MASK, token) |
FIELD_PREP(MPAM_MSC_PROT_ID_MASK, MPAM_FB_PROTOCOL_ID),
&pcc_shmem->command);
writel_relaxed(msc_fb_id, &payload->msc_fb_id);
writel_relaxed(0, &payload->flags);
writel_relaxed(reg, &payload->reg_offset);
writel_relaxed(val, &payload->value);
}
static int mpam_fb_send_request(struct mpam_msc *msc, u32 msc_fb_id,
u16 reg, u32 *result, int mpam_fb_command)
{
unsigned int token = atomic_inc_return(&mpam_fb_token);
struct acpi_pcct_ext_pcc_shared_memory __iomem *pcc_shmem;
struct mpam_pcc_chan *pcc_chan;
struct pcc_mbox_chan *chan;
void __iomem *payload_ofs;
int mpam_fb_err = 0;
u32 status;
int ret;
pcc_chan = msc->pcc_chan;
if (!pcc_chan)
return -ENODEV;
chan = pcc_chan->pcc_chan;
/* prune token to fit into the 10 bits inside the command register */
token = FIELD_GET(MPAM_MSC_TOKEN_MASK,
FIELD_PREP(MPAM_MSC_TOKEN_MASK, token));
mutex_lock(&pcc_chan->pcc_chan_lock);
switch (mpam_fb_command) {
case MPAM_PROTOCOL_VERSION_CMD:
mpam_fb_build_version_message(token, chan->shmem);
break;
case MPAM_MSC_READ_CMD:
mpam_fb_build_read_message(msc_fb_id, reg, token, chan->shmem);
break;
case MPAM_MSC_WRITE_CMD:
mpam_fb_build_write_message(msc_fb_id, reg, *result,
token, chan->shmem);
break;
default:
dev_err(&msc->pdev->dev, "unsupported MPAM-Fb command %d\n",
mpam_fb_command);
ret = -EINVAL;
goto out_err;
}
dma_wmb();
ret = mbox_send_message(chan->mchan, NULL);
if (ret < 0)
goto out_err;
pcc_shmem = chan->shmem;
payload_ofs = chan->shmem + sizeof(*pcc_shmem);
status = readl(&pcc_shmem->command);
if (FIELD_GET(MPAM_MSC_TOKEN_MASK, status) != token) {
ret = -ETIMEDOUT;
goto out_err;
}
mpam_fb_err = readl(payload_ofs + 0x0);
if (mpam_fb_err < 0) {
ret = mpam_fb_to_linux_errno(mpam_fb_err);
goto out_err;
}
if (mpam_fb_command != MPAM_MSC_WRITE_CMD)
*result = readl(payload_ofs + 0x4);
mutex_unlock(&pcc_chan->pcc_chan_lock);
return 0;
out_err:
mutex_unlock(&pcc_chan->pcc_chan_lock);
mpam_fb_disable_mpam(ret, mpam_fb_err);
/*
* TODO: Error codes are filtered in higher layers, return something
* that is known there for now, to actually report them.
*/
return -EIO;
}
int mpam_fb_send_read_request(struct mpam_msc *msc, u16 reg, u32 *result)
{
return mpam_fb_send_request(msc, msc->fb_id, reg, result,
MPAM_MSC_READ_CMD);
}
int mpam_fb_send_write_request(struct mpam_msc *msc, u16 reg, u32 value)
{
return mpam_fb_send_request(msc, msc->fb_id, reg, &value,
MPAM_MSC_WRITE_CMD);
}
/* We only support MPAM-Fb protocol version 1.x */
int mpam_fb_check_protocol_version(struct mpam_msc *msc)
{
u32 version;
int ret;
ret = mpam_fb_send_request(msc, 0, 0, &version,
MPAM_PROTOCOL_VERSION_CMD);
if (ret)
return ret;
if (FIELD_GET(MPAM_FB_VERSION_MAJOR_MASK, version) != 1) {
pr_err("Incompatible MPAM-Fb protocol version %d.%d\n",
FIELD_GET(MPAM_FB_VERSION_MAJOR_MASK, version),
FIELD_GET(MPAM_FB_VERSION_MINOR_MASK, version));
return -EINVAL;
}
return 0;
}
int mpam_fb_check_shared_buffer_size(struct mpam_msc *msc)
{
int min_buffer_size = MPAM_FB_MAX_MSG_SIZE +
sizeof(struct acpi_pcct_ext_pcc_shared_memory);
if (msc->pcc_chan->pcc_chan->shmem_size < min_buffer_size) {
pr_err("MPAM-Fb PCC channel size too small.\n");
return -ENOMEM;
}
return 0;
}
|