xref: /linux/drivers/resctrl/mpam_fb.c (revision 0ef7c5eb77c6cf57582bda2b96a845bdd9e8c9d2)
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_MSC_READ_CMD		0x4
24 #define MPAM_MSC_WRITE_CMD		0x5
25 
26 #define MPAM_FB_PROT_HEADER_LEN		sizeof(u32)
27 
28 #define MPAM_FB_SUCCESS		 0
29 #define MPAM_FB_ERR_NOT_SUPPORTED	-1
30 #define MPAM_FB_ERR_INVALID_PARAMETERS	-2
31 #define MPAM_FB_ERR_DENIED		-3
32 #define MPAM_FB_ERR_NOT_FOUND		-4
33 #define MPAM_FB_ERR_OUT_OF_RANGE	-5
34 #define MPAM_FB_ERR_BUSY		-6
35 #define MPAM_FB_ERR_COMMS_ERROR		-7
36 #define MPAM_FB_ERR_GENERIC_ERROR	-8
37 #define MPAM_FB_ERR_HW_ERROR		-9
38 #define MPAM_FB_ERR_PROTOCOL_ERROR	-10
39 #define MPAM_FB_ERR_IN_USE		-11
40 
41 static atomic_t mpam_fb_token = ATOMIC_INIT(0);
42 
43 static int mpam_fb_to_linux_errno(int mpam_fb_code)
44 {
45 	switch (mpam_fb_code) {
46 	case MPAM_FB_ERR_NOT_SUPPORTED:
47 		return -EOPNOTSUPP;
48 	case MPAM_FB_ERR_INVALID_PARAMETERS:
49 		return -EINVAL;
50 	case MPAM_FB_ERR_NOT_FOUND:
51 		return -ENOENT;
52 	case MPAM_FB_ERR_OUT_OF_RANGE:
53 		return -ERANGE;
54 	case MPAM_FB_ERR_BUSY:
55 		return -EBUSY;
56 	default:
57 		return -EINVAL;
58 	}
59 }
60 
61 static void mpam_fb_build_version_message(unsigned int token,
62 					  void __iomem *msg_buf)
63 {
64 	struct acpi_pcct_ext_pcc_shared_memory __iomem *pcc_shmem = msg_buf;
65 
66 	/* .signature is filled by the platform */
67 	writel_relaxed(PCC_CMD_COMPLETION_NOTIFY, &pcc_shmem->flags);
68 	writel_relaxed(MPAM_FB_PROT_HEADER_LEN, &pcc_shmem->length);
69 	writel_relaxed(MPAM_PROTOCOL_VERSION_CMD |
70 		       FIELD_PREP(MPAM_MSC_TOKEN_MASK, token) |
71 		       FIELD_PREP(MPAM_MSC_PROT_ID_MASK, MPAM_FB_PROTOCOL_ID),
72 		       &pcc_shmem->command);
73 }
74 
75 static void mpam_fb_build_read_message(int msc_fb_id, int reg,
76 				       unsigned int token,
77 				       void __iomem *msg_buf)
78 {
79 	struct acpi_pcct_ext_pcc_shared_memory __iomem *pcc_shmem = msg_buf;
80 	struct mpam_fb_read_payload {
81 		u32 msc_fb_id;
82 		u32 flags;
83 		u32 reg_offset;
84 	} __packed __iomem *payload = msg_buf + sizeof(*pcc_shmem);
85 	int msg_size = MPAM_FB_PROT_HEADER_LEN + sizeof(*payload);
86 
87 	/* .signature is filled by the platform */
88 	writel_relaxed(PCC_CMD_COMPLETION_NOTIFY, &pcc_shmem->flags);
89 	writel_relaxed(msg_size, &pcc_shmem->length);
90 	writel_relaxed(MPAM_MSC_READ_CMD |
91 		       FIELD_PREP(MPAM_MSC_TOKEN_MASK, token) |
92 		       FIELD_PREP(MPAM_MSC_PROT_ID_MASK, MPAM_FB_PROTOCOL_ID),
93 		       &pcc_shmem->command);
94 
95 	writel_relaxed(msc_fb_id, &payload->msc_fb_id);
96 	writel_relaxed(0, &payload->flags);
97 	writel_relaxed(reg, &payload->reg_offset);
98 }
99 
100 static void mpam_fb_build_write_message(int msc_fb_id, int reg, u32 val,
101 					unsigned int token,
102 					void __iomem *msg_buf)
103 {
104 	struct acpi_pcct_ext_pcc_shared_memory __iomem *pcc_shmem = msg_buf;
105 	struct mpam_fb_write_payload {
106 		u32 msc_fb_id;
107 		u32 flags;
108 		u32 reg_offset;
109 		u32 value;
110 	} __packed __iomem *payload = msg_buf + sizeof(*pcc_shmem);
111 	int msg_size = MPAM_FB_PROT_HEADER_LEN + sizeof(*payload);
112 
113 	/* .signature is filled by the platform */
114 	writel_relaxed(PCC_CMD_COMPLETION_NOTIFY, &pcc_shmem->flags);
115 	writel_relaxed(msg_size, &pcc_shmem->length);
116 	writel_relaxed(MPAM_MSC_WRITE_CMD |
117 		       FIELD_PREP(MPAM_MSC_TOKEN_MASK, token) |
118 		       FIELD_PREP(MPAM_MSC_PROT_ID_MASK, MPAM_FB_PROTOCOL_ID),
119 		       &pcc_shmem->command);
120 
121 	writel_relaxed(msc_fb_id, &payload->msc_fb_id);
122 	writel_relaxed(0, &payload->flags);
123 	writel_relaxed(reg, &payload->reg_offset);
124 	writel_relaxed(val, &payload->value);
125 }
126 
127 static int mpam_fb_send_request(struct mpam_msc *msc, u32 msc_fb_id,
128 				u16 reg, u32 *result, int mpam_fb_command)
129 {
130 	unsigned int token = atomic_inc_return(&mpam_fb_token);
131 	struct acpi_pcct_ext_pcc_shared_memory __iomem *pcc_shmem;
132 	struct mpam_pcc_chan *pcc_chan;
133 	struct pcc_mbox_chan *chan;
134 	void __iomem *payload_ofs;
135 	int mpam_fb_err = 0;
136 	u32 status;
137 	int ret;
138 
139 	pcc_chan = msc->pcc_chan;
140 	if (!pcc_chan)
141 		return -ENODEV;
142 
143 	chan = pcc_chan->pcc_chan;
144 
145 	/* prune token to fit into the 10 bits inside the command register */
146 	token = FIELD_GET(MPAM_MSC_TOKEN_MASK,
147 			  FIELD_PREP(MPAM_MSC_TOKEN_MASK, token));
148 
149 	mutex_lock(&pcc_chan->pcc_chan_lock);
150 
151 	switch (mpam_fb_command) {
152 	case MPAM_PROTOCOL_VERSION_CMD:
153 		mpam_fb_build_version_message(token, chan->shmem);
154 		break;
155 	case MPAM_MSC_READ_CMD:
156 		mpam_fb_build_read_message(msc_fb_id, reg, token, chan->shmem);
157 		break;
158 	case MPAM_MSC_WRITE_CMD:
159 		mpam_fb_build_write_message(msc_fb_id, reg, *result,
160 					    token, chan->shmem);
161 		break;
162 	default:
163 		dev_err(&msc->pdev->dev, "unsupported MPAM-Fb command %d\n",
164 			mpam_fb_command);
165 		ret = -EINVAL;
166 		goto out_err;
167 	}
168 
169 	dma_wmb();
170 	ret = mbox_send_message(chan->mchan, NULL);
171 	if (ret < 0)
172 		goto out_err;
173 
174 	pcc_shmem = chan->shmem;
175 	payload_ofs = chan->shmem + sizeof(*pcc_shmem);
176 	status = readl(&pcc_shmem->command);
177 	if (FIELD_GET(MPAM_MSC_TOKEN_MASK, status) != token) {
178 		ret = -ETIMEDOUT;
179 		goto out_err;
180 	}
181 
182 	mpam_fb_err = readl(payload_ofs + 0x0);
183 	if (mpam_fb_err < 0) {
184 		ret = mpam_fb_to_linux_errno(mpam_fb_err);
185 		goto out_err;
186 	}
187 
188 	if (mpam_fb_command != MPAM_MSC_WRITE_CMD)
189 		*result = readl(payload_ofs + 0x4);
190 
191 	mutex_unlock(&pcc_chan->pcc_chan_lock);
192 
193 	return 0;
194 
195 out_err:
196 	mutex_unlock(&pcc_chan->pcc_chan_lock);
197 
198 	mpam_fb_disable_mpam(ret, mpam_fb_err);
199 
200 	/*
201 	 * TODO: Error codes are filtered in higher layers, return something
202 	 * that is known there for now, to actually report them.
203 	 */
204 	return -EIO;
205 }
206 
207 int mpam_fb_send_read_request(struct mpam_msc *msc, u16 reg, u32 *result)
208 {
209 	return mpam_fb_send_request(msc, msc->fb_id, reg, result,
210 				    MPAM_MSC_READ_CMD);
211 }
212 
213 int mpam_fb_send_write_request(struct mpam_msc *msc, u16 reg, u32 value)
214 {
215 	return mpam_fb_send_request(msc, msc->fb_id, reg, &value,
216 				    MPAM_MSC_WRITE_CMD);
217 }
218