xref: /linux/drivers/resctrl/mpam_fb.c (revision 5415b256badbe1681183e85f42462fa01cf09c83)
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 
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 
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 
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 
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 
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 
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 
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 */
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 
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