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