1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (C) 2024-2026 Arm Ltd.
3
4 #include <linux/arm_mpam.h>
5 #include <linux/cleanup.h>
6 #include <linux/errno.h>
7 #include <linux/mailbox_client.h>
8 #include <linux/mutex.h>
9 #include <linux/platform_device.h>
10 #include <linux/types.h>
11
12 #include <acpi/pcc.h>
13 #include <asm/mpam.h>
14
15 #include "mpam_internal.h"
16
17 #define MPAM_MSC_TOKEN_MASK GENMASK_U32(27, 18)
18 #define MPAM_MSC_PROT_ID_MASK GENMASK_U32(17, 10)
19
20 #define MPAM_FB_PROTOCOL_ID 0x1a
21
22 #define MPAM_PROTOCOL_VERSION_CMD 0x0
23 #define MPAM_FB_VERSION_MAJOR_MASK GENMASK_U32(31, 16)
24 #define MPAM_FB_VERSION_MINOR_MASK GENMASK_U32(15, 0)
25
26 #define MPAM_MSC_READ_CMD 0x4
27 #define MPAM_MSC_WRITE_CMD 0x5
28
29 #define MPAM_FB_PROT_HEADER_LEN sizeof(u32)
30 /* The longest message is MPAM_MSC_WRITE, with 4 parameters. */
31 #define MPAM_FB_MAX_MSG_SIZE (4 * sizeof(u32))
32
33 #define MPAM_FB_SUCCESS 0
34 #define MPAM_FB_ERR_NOT_SUPPORTED -1
35 #define MPAM_FB_ERR_INVALID_PARAMETERS -2
36 #define MPAM_FB_ERR_DENIED -3
37 #define MPAM_FB_ERR_NOT_FOUND -4
38 #define MPAM_FB_ERR_OUT_OF_RANGE -5
39 #define MPAM_FB_ERR_BUSY -6
40 #define MPAM_FB_ERR_COMMS_ERROR -7
41 #define MPAM_FB_ERR_GENERIC_ERROR -8
42 #define MPAM_FB_ERR_HW_ERROR -9
43 #define MPAM_FB_ERR_PROTOCOL_ERROR -10
44 #define MPAM_FB_ERR_IN_USE -11
45
46 static atomic_t mpam_fb_token = ATOMIC_INIT(0);
47
mpam_fb_to_linux_errno(int mpam_fb_code)48 static int mpam_fb_to_linux_errno(int mpam_fb_code)
49 {
50 switch (mpam_fb_code) {
51 case MPAM_FB_ERR_NOT_SUPPORTED:
52 return -EOPNOTSUPP;
53 case MPAM_FB_ERR_INVALID_PARAMETERS:
54 return -EINVAL;
55 case MPAM_FB_ERR_NOT_FOUND:
56 return -ENOENT;
57 case MPAM_FB_ERR_OUT_OF_RANGE:
58 return -ERANGE;
59 case MPAM_FB_ERR_BUSY:
60 return -EBUSY;
61 default:
62 return -EINVAL;
63 }
64 }
65
mpam_fb_build_version_message(unsigned int token,void __iomem * msg_buf)66 static void mpam_fb_build_version_message(unsigned int token,
67 void __iomem *msg_buf)
68 {
69 struct acpi_pcct_ext_pcc_shared_memory __iomem *pcc_shmem = msg_buf;
70
71 /* .signature is filled by the platform */
72 writel_relaxed(PCC_CMD_COMPLETION_NOTIFY, &pcc_shmem->flags);
73 writel_relaxed(MPAM_FB_PROT_HEADER_LEN, &pcc_shmem->length);
74 writel_relaxed(MPAM_PROTOCOL_VERSION_CMD |
75 FIELD_PREP(MPAM_MSC_TOKEN_MASK, token) |
76 FIELD_PREP(MPAM_MSC_PROT_ID_MASK, MPAM_FB_PROTOCOL_ID),
77 &pcc_shmem->command);
78 }
79
mpam_fb_build_read_message(int msc_fb_id,int reg,unsigned int token,void __iomem * msg_buf)80 static void mpam_fb_build_read_message(int msc_fb_id, int reg,
81 unsigned int token,
82 void __iomem *msg_buf)
83 {
84 struct acpi_pcct_ext_pcc_shared_memory __iomem *pcc_shmem = msg_buf;
85 struct mpam_fb_read_payload {
86 u32 msc_fb_id;
87 u32 flags;
88 u32 reg_offset;
89 } __packed __iomem *payload = msg_buf + sizeof(*pcc_shmem);
90 int msg_size = MPAM_FB_PROT_HEADER_LEN + sizeof(*payload);
91
92 /* .signature is filled by the platform */
93 writel_relaxed(PCC_CMD_COMPLETION_NOTIFY, &pcc_shmem->flags);
94 writel_relaxed(msg_size, &pcc_shmem->length);
95 writel_relaxed(MPAM_MSC_READ_CMD |
96 FIELD_PREP(MPAM_MSC_TOKEN_MASK, token) |
97 FIELD_PREP(MPAM_MSC_PROT_ID_MASK, MPAM_FB_PROTOCOL_ID),
98 &pcc_shmem->command);
99
100 writel_relaxed(msc_fb_id, &payload->msc_fb_id);
101 writel_relaxed(0, &payload->flags);
102 writel_relaxed(reg, &payload->reg_offset);
103 }
104
mpam_fb_build_write_message(int msc_fb_id,int reg,u32 val,unsigned int token,void __iomem * msg_buf)105 static void mpam_fb_build_write_message(int msc_fb_id, int reg, u32 val,
106 unsigned int token,
107 void __iomem *msg_buf)
108 {
109 struct acpi_pcct_ext_pcc_shared_memory __iomem *pcc_shmem = msg_buf;
110 struct mpam_fb_write_payload {
111 u32 msc_fb_id;
112 u32 flags;
113 u32 reg_offset;
114 u32 value;
115 } __packed __iomem *payload = msg_buf + sizeof(*pcc_shmem);
116 int msg_size = MPAM_FB_PROT_HEADER_LEN + sizeof(*payload);
117
118 /* .signature is filled by the platform */
119 writel_relaxed(PCC_CMD_COMPLETION_NOTIFY, &pcc_shmem->flags);
120 writel_relaxed(msg_size, &pcc_shmem->length);
121 writel_relaxed(MPAM_MSC_WRITE_CMD |
122 FIELD_PREP(MPAM_MSC_TOKEN_MASK, token) |
123 FIELD_PREP(MPAM_MSC_PROT_ID_MASK, MPAM_FB_PROTOCOL_ID),
124 &pcc_shmem->command);
125
126 writel_relaxed(msc_fb_id, &payload->msc_fb_id);
127 writel_relaxed(0, &payload->flags);
128 writel_relaxed(reg, &payload->reg_offset);
129 writel_relaxed(val, &payload->value);
130 }
131
mpam_fb_send_request(struct mpam_msc * msc,u32 msc_fb_id,u16 reg,u32 * result,int mpam_fb_command)132 static int mpam_fb_send_request(struct mpam_msc *msc, u32 msc_fb_id,
133 u16 reg, u32 *result, int mpam_fb_command)
134 {
135 unsigned int token = atomic_inc_return(&mpam_fb_token);
136 struct acpi_pcct_ext_pcc_shared_memory __iomem *pcc_shmem;
137 struct mpam_pcc_chan *pcc_chan;
138 struct pcc_mbox_chan *chan;
139 void __iomem *payload_ofs;
140 int mpam_fb_err = 0;
141 u32 status;
142 int ret;
143
144 pcc_chan = msc->pcc_chan;
145 if (!pcc_chan)
146 return -ENODEV;
147
148 chan = pcc_chan->pcc_chan;
149
150 /* prune token to fit into the 10 bits inside the command register */
151 token = FIELD_GET(MPAM_MSC_TOKEN_MASK,
152 FIELD_PREP(MPAM_MSC_TOKEN_MASK, token));
153
154 mutex_lock(&pcc_chan->pcc_chan_lock);
155
156 switch (mpam_fb_command) {
157 case MPAM_PROTOCOL_VERSION_CMD:
158 mpam_fb_build_version_message(token, chan->shmem);
159 break;
160 case MPAM_MSC_READ_CMD:
161 mpam_fb_build_read_message(msc_fb_id, reg, token, chan->shmem);
162 break;
163 case MPAM_MSC_WRITE_CMD:
164 mpam_fb_build_write_message(msc_fb_id, reg, *result,
165 token, chan->shmem);
166 break;
167 default:
168 dev_err(&msc->pdev->dev, "unsupported MPAM-Fb command %d\n",
169 mpam_fb_command);
170 ret = -EINVAL;
171 goto out_err;
172 }
173
174 dma_wmb();
175 ret = mbox_send_message(chan->mchan, NULL);
176 if (ret < 0)
177 goto out_err;
178
179 pcc_shmem = chan->shmem;
180 payload_ofs = chan->shmem + sizeof(*pcc_shmem);
181 status = readl(&pcc_shmem->command);
182 if (FIELD_GET(MPAM_MSC_TOKEN_MASK, status) != token) {
183 ret = -ETIMEDOUT;
184 goto out_err;
185 }
186
187 mpam_fb_err = readl(payload_ofs + 0x0);
188 if (mpam_fb_err < 0) {
189 ret = mpam_fb_to_linux_errno(mpam_fb_err);
190 goto out_err;
191 }
192
193 if (mpam_fb_command != MPAM_MSC_WRITE_CMD)
194 *result = readl(payload_ofs + 0x4);
195
196 mutex_unlock(&pcc_chan->pcc_chan_lock);
197
198 return 0;
199
200 out_err:
201 mutex_unlock(&pcc_chan->pcc_chan_lock);
202
203 mpam_fb_disable_mpam(ret, mpam_fb_err);
204
205 /*
206 * TODO: Error codes are filtered in higher layers, return something
207 * that is known there for now, to actually report them.
208 */
209 return -EIO;
210 }
211
mpam_fb_send_read_request(struct mpam_msc * msc,u16 reg,u32 * result)212 int mpam_fb_send_read_request(struct mpam_msc *msc, u16 reg, u32 *result)
213 {
214 return mpam_fb_send_request(msc, msc->fb_id, reg, result,
215 MPAM_MSC_READ_CMD);
216 }
217
mpam_fb_send_write_request(struct mpam_msc * msc,u16 reg,u32 value)218 int mpam_fb_send_write_request(struct mpam_msc *msc, u16 reg, u32 value)
219 {
220 return mpam_fb_send_request(msc, msc->fb_id, reg, &value,
221 MPAM_MSC_WRITE_CMD);
222 }
223
224 /* We only support MPAM-Fb protocol version 1.x */
mpam_fb_check_protocol_version(struct mpam_msc * msc)225 int mpam_fb_check_protocol_version(struct mpam_msc *msc)
226 {
227 u32 version;
228 int ret;
229
230 ret = mpam_fb_send_request(msc, 0, 0, &version,
231 MPAM_PROTOCOL_VERSION_CMD);
232 if (ret)
233 return ret;
234
235 if (FIELD_GET(MPAM_FB_VERSION_MAJOR_MASK, version) != 1) {
236 pr_err("Incompatible MPAM-Fb protocol version %d.%d\n",
237 FIELD_GET(MPAM_FB_VERSION_MAJOR_MASK, version),
238 FIELD_GET(MPAM_FB_VERSION_MINOR_MASK, version));
239
240 return -EINVAL;
241 }
242
243 return 0;
244 }
245
mpam_fb_check_shared_buffer_size(struct mpam_msc * msc)246 int mpam_fb_check_shared_buffer_size(struct mpam_msc *msc)
247 {
248 int min_buffer_size = MPAM_FB_MAX_MSG_SIZE +
249 sizeof(struct acpi_pcct_ext_pcc_shared_memory);
250
251 if (msc->pcc_chan->pcc_chan->shmem_size < min_buffer_size) {
252 pr_err("MPAM-Fb PCC channel size too small.\n");
253
254 return -ENOMEM;
255 }
256
257 return 0;
258 }
259