xref: /linux/drivers/resctrl/mpam_fb.c (revision 303bff3637d13f0ecbb280c7cb403470c440a2a6)
10ef7c5ebSAndre Przywara // SPDX-License-Identifier: GPL-2.0
20ef7c5ebSAndre Przywara // Copyright (C) 2024-2026 Arm Ltd.
30ef7c5ebSAndre Przywara 
40ef7c5ebSAndre Przywara #include <linux/arm_mpam.h>
50ef7c5ebSAndre Przywara #include <linux/cleanup.h>
60ef7c5ebSAndre Przywara #include <linux/errno.h>
70ef7c5ebSAndre Przywara #include <linux/mailbox_client.h>
80ef7c5ebSAndre Przywara #include <linux/mutex.h>
90ef7c5ebSAndre Przywara #include <linux/platform_device.h>
100ef7c5ebSAndre Przywara #include <linux/types.h>
110ef7c5ebSAndre Przywara 
120ef7c5ebSAndre Przywara #include <acpi/pcc.h>
130ef7c5ebSAndre Przywara #include <asm/mpam.h>
140ef7c5ebSAndre Przywara 
150ef7c5ebSAndre Przywara #include "mpam_internal.h"
160ef7c5ebSAndre Przywara 
170ef7c5ebSAndre Przywara #define MPAM_MSC_TOKEN_MASK		GENMASK_U32(27, 18)
180ef7c5ebSAndre Przywara #define MPAM_MSC_PROT_ID_MASK		GENMASK_U32(17, 10)
190ef7c5ebSAndre Przywara 
200ef7c5ebSAndre Przywara #define MPAM_FB_PROTOCOL_ID		0x1a
210ef7c5ebSAndre Przywara 
220ef7c5ebSAndre Przywara #define MPAM_PROTOCOL_VERSION_CMD	0x0
23*cb351e3fSAndre Przywara #define MPAM_FB_VERSION_MAJOR_MASK		GENMASK_U32(31, 16)
24*cb351e3fSAndre Przywara #define MPAM_FB_VERSION_MINOR_MASK		GENMASK_U32(15, 0)
25*cb351e3fSAndre Przywara 
260ef7c5ebSAndre Przywara #define MPAM_MSC_READ_CMD		0x4
270ef7c5ebSAndre Przywara #define MPAM_MSC_WRITE_CMD		0x5
280ef7c5ebSAndre Przywara 
290ef7c5ebSAndre Przywara #define MPAM_FB_PROT_HEADER_LEN		sizeof(u32)
30*cb351e3fSAndre Przywara /* The longest message is MPAM_MSC_WRITE, with 4 parameters. */
31*cb351e3fSAndre Przywara #define MPAM_FB_MAX_MSG_SIZE		(4 * sizeof(u32))
320ef7c5ebSAndre Przywara 
330ef7c5ebSAndre Przywara #define MPAM_FB_SUCCESS		 0
340ef7c5ebSAndre Przywara #define MPAM_FB_ERR_NOT_SUPPORTED	-1
350ef7c5ebSAndre Przywara #define MPAM_FB_ERR_INVALID_PARAMETERS	-2
360ef7c5ebSAndre Przywara #define MPAM_FB_ERR_DENIED		-3
370ef7c5ebSAndre Przywara #define MPAM_FB_ERR_NOT_FOUND		-4
380ef7c5ebSAndre Przywara #define MPAM_FB_ERR_OUT_OF_RANGE	-5
390ef7c5ebSAndre Przywara #define MPAM_FB_ERR_BUSY		-6
400ef7c5ebSAndre Przywara #define MPAM_FB_ERR_COMMS_ERROR		-7
410ef7c5ebSAndre Przywara #define MPAM_FB_ERR_GENERIC_ERROR	-8
420ef7c5ebSAndre Przywara #define MPAM_FB_ERR_HW_ERROR		-9
430ef7c5ebSAndre Przywara #define MPAM_FB_ERR_PROTOCOL_ERROR	-10
440ef7c5ebSAndre Przywara #define MPAM_FB_ERR_IN_USE		-11
450ef7c5ebSAndre Przywara 
460ef7c5ebSAndre Przywara static atomic_t mpam_fb_token = ATOMIC_INIT(0);
470ef7c5ebSAndre Przywara 
mpam_fb_to_linux_errno(int mpam_fb_code)480ef7c5ebSAndre Przywara static int mpam_fb_to_linux_errno(int mpam_fb_code)
490ef7c5ebSAndre Przywara {
500ef7c5ebSAndre Przywara 	switch (mpam_fb_code) {
510ef7c5ebSAndre Przywara 	case MPAM_FB_ERR_NOT_SUPPORTED:
520ef7c5ebSAndre Przywara 		return -EOPNOTSUPP;
530ef7c5ebSAndre Przywara 	case MPAM_FB_ERR_INVALID_PARAMETERS:
540ef7c5ebSAndre Przywara 		return -EINVAL;
550ef7c5ebSAndre Przywara 	case MPAM_FB_ERR_NOT_FOUND:
560ef7c5ebSAndre Przywara 		return -ENOENT;
570ef7c5ebSAndre Przywara 	case MPAM_FB_ERR_OUT_OF_RANGE:
580ef7c5ebSAndre Przywara 		return -ERANGE;
590ef7c5ebSAndre Przywara 	case MPAM_FB_ERR_BUSY:
600ef7c5ebSAndre Przywara 		return -EBUSY;
610ef7c5ebSAndre Przywara 	default:
620ef7c5ebSAndre Przywara 		return -EINVAL;
630ef7c5ebSAndre Przywara 	}
640ef7c5ebSAndre Przywara }
650ef7c5ebSAndre Przywara 
mpam_fb_build_version_message(unsigned int token,void __iomem * msg_buf)660ef7c5ebSAndre Przywara static void mpam_fb_build_version_message(unsigned int token,
670ef7c5ebSAndre Przywara 					  void __iomem *msg_buf)
680ef7c5ebSAndre Przywara {
690ef7c5ebSAndre Przywara 	struct acpi_pcct_ext_pcc_shared_memory __iomem *pcc_shmem = msg_buf;
700ef7c5ebSAndre Przywara 
710ef7c5ebSAndre Przywara 	/* .signature is filled by the platform */
720ef7c5ebSAndre Przywara 	writel_relaxed(PCC_CMD_COMPLETION_NOTIFY, &pcc_shmem->flags);
730ef7c5ebSAndre Przywara 	writel_relaxed(MPAM_FB_PROT_HEADER_LEN, &pcc_shmem->length);
740ef7c5ebSAndre Przywara 	writel_relaxed(MPAM_PROTOCOL_VERSION_CMD |
750ef7c5ebSAndre Przywara 		       FIELD_PREP(MPAM_MSC_TOKEN_MASK, token) |
760ef7c5ebSAndre Przywara 		       FIELD_PREP(MPAM_MSC_PROT_ID_MASK, MPAM_FB_PROTOCOL_ID),
770ef7c5ebSAndre Przywara 		       &pcc_shmem->command);
780ef7c5ebSAndre Przywara }
790ef7c5ebSAndre Przywara 
mpam_fb_build_read_message(int msc_fb_id,int reg,unsigned int token,void __iomem * msg_buf)800ef7c5ebSAndre Przywara static void mpam_fb_build_read_message(int msc_fb_id, int reg,
810ef7c5ebSAndre Przywara 				       unsigned int token,
820ef7c5ebSAndre Przywara 				       void __iomem *msg_buf)
830ef7c5ebSAndre Przywara {
840ef7c5ebSAndre Przywara 	struct acpi_pcct_ext_pcc_shared_memory __iomem *pcc_shmem = msg_buf;
850ef7c5ebSAndre Przywara 	struct mpam_fb_read_payload {
860ef7c5ebSAndre Przywara 		u32 msc_fb_id;
870ef7c5ebSAndre Przywara 		u32 flags;
880ef7c5ebSAndre Przywara 		u32 reg_offset;
890ef7c5ebSAndre Przywara 	} __packed __iomem *payload = msg_buf + sizeof(*pcc_shmem);
900ef7c5ebSAndre Przywara 	int msg_size = MPAM_FB_PROT_HEADER_LEN + sizeof(*payload);
910ef7c5ebSAndre Przywara 
920ef7c5ebSAndre Przywara 	/* .signature is filled by the platform */
930ef7c5ebSAndre Przywara 	writel_relaxed(PCC_CMD_COMPLETION_NOTIFY, &pcc_shmem->flags);
940ef7c5ebSAndre Przywara 	writel_relaxed(msg_size, &pcc_shmem->length);
950ef7c5ebSAndre Przywara 	writel_relaxed(MPAM_MSC_READ_CMD |
960ef7c5ebSAndre Przywara 		       FIELD_PREP(MPAM_MSC_TOKEN_MASK, token) |
970ef7c5ebSAndre Przywara 		       FIELD_PREP(MPAM_MSC_PROT_ID_MASK, MPAM_FB_PROTOCOL_ID),
980ef7c5ebSAndre Przywara 		       &pcc_shmem->command);
990ef7c5ebSAndre Przywara 
1000ef7c5ebSAndre Przywara 	writel_relaxed(msc_fb_id, &payload->msc_fb_id);
1010ef7c5ebSAndre Przywara 	writel_relaxed(0, &payload->flags);
1020ef7c5ebSAndre Przywara 	writel_relaxed(reg, &payload->reg_offset);
1030ef7c5ebSAndre Przywara }
1040ef7c5ebSAndre Przywara 
mpam_fb_build_write_message(int msc_fb_id,int reg,u32 val,unsigned int token,void __iomem * msg_buf)1050ef7c5ebSAndre Przywara static void mpam_fb_build_write_message(int msc_fb_id, int reg, u32 val,
1060ef7c5ebSAndre Przywara 					unsigned int token,
1070ef7c5ebSAndre Przywara 					void __iomem *msg_buf)
1080ef7c5ebSAndre Przywara {
1090ef7c5ebSAndre Przywara 	struct acpi_pcct_ext_pcc_shared_memory __iomem *pcc_shmem = msg_buf;
1100ef7c5ebSAndre Przywara 	struct mpam_fb_write_payload {
1110ef7c5ebSAndre Przywara 		u32 msc_fb_id;
1120ef7c5ebSAndre Przywara 		u32 flags;
1130ef7c5ebSAndre Przywara 		u32 reg_offset;
1140ef7c5ebSAndre Przywara 		u32 value;
1150ef7c5ebSAndre Przywara 	} __packed __iomem *payload = msg_buf + sizeof(*pcc_shmem);
1160ef7c5ebSAndre Przywara 	int msg_size = MPAM_FB_PROT_HEADER_LEN + sizeof(*payload);
1170ef7c5ebSAndre Przywara 
1180ef7c5ebSAndre Przywara 	/* .signature is filled by the platform */
1190ef7c5ebSAndre Przywara 	writel_relaxed(PCC_CMD_COMPLETION_NOTIFY, &pcc_shmem->flags);
1200ef7c5ebSAndre Przywara 	writel_relaxed(msg_size, &pcc_shmem->length);
1210ef7c5ebSAndre Przywara 	writel_relaxed(MPAM_MSC_WRITE_CMD |
1220ef7c5ebSAndre Przywara 		       FIELD_PREP(MPAM_MSC_TOKEN_MASK, token) |
1230ef7c5ebSAndre Przywara 		       FIELD_PREP(MPAM_MSC_PROT_ID_MASK, MPAM_FB_PROTOCOL_ID),
1240ef7c5ebSAndre Przywara 		       &pcc_shmem->command);
1250ef7c5ebSAndre Przywara 
1260ef7c5ebSAndre Przywara 	writel_relaxed(msc_fb_id, &payload->msc_fb_id);
1270ef7c5ebSAndre Przywara 	writel_relaxed(0, &payload->flags);
1280ef7c5ebSAndre Przywara 	writel_relaxed(reg, &payload->reg_offset);
1290ef7c5ebSAndre Przywara 	writel_relaxed(val, &payload->value);
1300ef7c5ebSAndre Przywara }
1310ef7c5ebSAndre Przywara 
mpam_fb_send_request(struct mpam_msc * msc,u32 msc_fb_id,u16 reg,u32 * result,int mpam_fb_command)1320ef7c5ebSAndre Przywara static int mpam_fb_send_request(struct mpam_msc *msc, u32 msc_fb_id,
1330ef7c5ebSAndre Przywara 				u16 reg, u32 *result, int mpam_fb_command)
1340ef7c5ebSAndre Przywara {
1350ef7c5ebSAndre Przywara 	unsigned int token = atomic_inc_return(&mpam_fb_token);
1360ef7c5ebSAndre Przywara 	struct acpi_pcct_ext_pcc_shared_memory __iomem *pcc_shmem;
1370ef7c5ebSAndre Przywara 	struct mpam_pcc_chan *pcc_chan;
1380ef7c5ebSAndre Przywara 	struct pcc_mbox_chan *chan;
1390ef7c5ebSAndre Przywara 	void __iomem *payload_ofs;
1400ef7c5ebSAndre Przywara 	int mpam_fb_err = 0;
1410ef7c5ebSAndre Przywara 	u32 status;
1420ef7c5ebSAndre Przywara 	int ret;
1430ef7c5ebSAndre Przywara 
1440ef7c5ebSAndre Przywara 	pcc_chan = msc->pcc_chan;
1450ef7c5ebSAndre Przywara 	if (!pcc_chan)
1460ef7c5ebSAndre Przywara 		return -ENODEV;
1470ef7c5ebSAndre Przywara 
1480ef7c5ebSAndre Przywara 	chan = pcc_chan->pcc_chan;
1490ef7c5ebSAndre Przywara 
1500ef7c5ebSAndre Przywara 	/* prune token to fit into the 10 bits inside the command register */
1510ef7c5ebSAndre Przywara 	token = FIELD_GET(MPAM_MSC_TOKEN_MASK,
1520ef7c5ebSAndre Przywara 			  FIELD_PREP(MPAM_MSC_TOKEN_MASK, token));
1530ef7c5ebSAndre Przywara 
1540ef7c5ebSAndre Przywara 	mutex_lock(&pcc_chan->pcc_chan_lock);
1550ef7c5ebSAndre Przywara 
1560ef7c5ebSAndre Przywara 	switch (mpam_fb_command) {
1570ef7c5ebSAndre Przywara 	case MPAM_PROTOCOL_VERSION_CMD:
1580ef7c5ebSAndre Przywara 		mpam_fb_build_version_message(token, chan->shmem);
1590ef7c5ebSAndre Przywara 		break;
1600ef7c5ebSAndre Przywara 	case MPAM_MSC_READ_CMD:
1610ef7c5ebSAndre Przywara 		mpam_fb_build_read_message(msc_fb_id, reg, token, chan->shmem);
1620ef7c5ebSAndre Przywara 		break;
1630ef7c5ebSAndre Przywara 	case MPAM_MSC_WRITE_CMD:
1640ef7c5ebSAndre Przywara 		mpam_fb_build_write_message(msc_fb_id, reg, *result,
1650ef7c5ebSAndre Przywara 					    token, chan->shmem);
1660ef7c5ebSAndre Przywara 		break;
1670ef7c5ebSAndre Przywara 	default:
1680ef7c5ebSAndre Przywara 		dev_err(&msc->pdev->dev, "unsupported MPAM-Fb command %d\n",
1690ef7c5ebSAndre Przywara 			mpam_fb_command);
1700ef7c5ebSAndre Przywara 		ret = -EINVAL;
1710ef7c5ebSAndre Przywara 		goto out_err;
1720ef7c5ebSAndre Przywara 	}
1730ef7c5ebSAndre Przywara 
1740ef7c5ebSAndre Przywara 	dma_wmb();
1750ef7c5ebSAndre Przywara 	ret = mbox_send_message(chan->mchan, NULL);
1760ef7c5ebSAndre Przywara 	if (ret < 0)
1770ef7c5ebSAndre Przywara 		goto out_err;
1780ef7c5ebSAndre Przywara 
1790ef7c5ebSAndre Przywara 	pcc_shmem = chan->shmem;
1800ef7c5ebSAndre Przywara 	payload_ofs = chan->shmem + sizeof(*pcc_shmem);
1810ef7c5ebSAndre Przywara 	status = readl(&pcc_shmem->command);
1820ef7c5ebSAndre Przywara 	if (FIELD_GET(MPAM_MSC_TOKEN_MASK, status) != token) {
1830ef7c5ebSAndre Przywara 		ret = -ETIMEDOUT;
1840ef7c5ebSAndre Przywara 		goto out_err;
1850ef7c5ebSAndre Przywara 	}
1860ef7c5ebSAndre Przywara 
1870ef7c5ebSAndre Przywara 	mpam_fb_err = readl(payload_ofs + 0x0);
1880ef7c5ebSAndre Przywara 	if (mpam_fb_err < 0) {
1890ef7c5ebSAndre Przywara 		ret = mpam_fb_to_linux_errno(mpam_fb_err);
1900ef7c5ebSAndre Przywara 		goto out_err;
1910ef7c5ebSAndre Przywara 	}
1920ef7c5ebSAndre Przywara 
1930ef7c5ebSAndre Przywara 	if (mpam_fb_command != MPAM_MSC_WRITE_CMD)
1940ef7c5ebSAndre Przywara 		*result = readl(payload_ofs + 0x4);
1950ef7c5ebSAndre Przywara 
1960ef7c5ebSAndre Przywara 	mutex_unlock(&pcc_chan->pcc_chan_lock);
1970ef7c5ebSAndre Przywara 
1980ef7c5ebSAndre Przywara 	return 0;
1990ef7c5ebSAndre Przywara 
2000ef7c5ebSAndre Przywara out_err:
2010ef7c5ebSAndre Przywara 	mutex_unlock(&pcc_chan->pcc_chan_lock);
2020ef7c5ebSAndre Przywara 
2030ef7c5ebSAndre Przywara 	mpam_fb_disable_mpam(ret, mpam_fb_err);
2040ef7c5ebSAndre Przywara 
2050ef7c5ebSAndre Przywara 	/*
2060ef7c5ebSAndre Przywara 	 * TODO: Error codes are filtered in higher layers, return something
2070ef7c5ebSAndre Przywara 	 * that is known there for now, to actually report them.
2080ef7c5ebSAndre Przywara 	 */
2090ef7c5ebSAndre Przywara 	return -EIO;
2100ef7c5ebSAndre Przywara }
2110ef7c5ebSAndre Przywara 
mpam_fb_send_read_request(struct mpam_msc * msc,u16 reg,u32 * result)2120ef7c5ebSAndre Przywara int mpam_fb_send_read_request(struct mpam_msc *msc, u16 reg, u32 *result)
2130ef7c5ebSAndre Przywara {
2140ef7c5ebSAndre Przywara 	return mpam_fb_send_request(msc, msc->fb_id, reg, result,
2150ef7c5ebSAndre Przywara 				    MPAM_MSC_READ_CMD);
2160ef7c5ebSAndre Przywara }
2170ef7c5ebSAndre Przywara 
mpam_fb_send_write_request(struct mpam_msc * msc,u16 reg,u32 value)2180ef7c5ebSAndre Przywara int mpam_fb_send_write_request(struct mpam_msc *msc, u16 reg, u32 value)
2190ef7c5ebSAndre Przywara {
2200ef7c5ebSAndre Przywara 	return mpam_fb_send_request(msc, msc->fb_id, reg, &value,
2210ef7c5ebSAndre Przywara 				    MPAM_MSC_WRITE_CMD);
2220ef7c5ebSAndre Przywara }
223*cb351e3fSAndre Przywara 
224*cb351e3fSAndre Przywara /* We only support MPAM-Fb protocol version 1.x */
mpam_fb_check_protocol_version(struct mpam_msc * msc)225*cb351e3fSAndre Przywara int mpam_fb_check_protocol_version(struct mpam_msc *msc)
226*cb351e3fSAndre Przywara {
227*cb351e3fSAndre Przywara 	u32 version;
228*cb351e3fSAndre Przywara 	int ret;
229*cb351e3fSAndre Przywara 
230*cb351e3fSAndre Przywara 	ret = mpam_fb_send_request(msc, 0, 0, &version,
231*cb351e3fSAndre Przywara 				   MPAM_PROTOCOL_VERSION_CMD);
232*cb351e3fSAndre Przywara 	if (ret)
233*cb351e3fSAndre Przywara 		return ret;
234*cb351e3fSAndre Przywara 
235*cb351e3fSAndre Przywara 	if (FIELD_GET(MPAM_FB_VERSION_MAJOR_MASK, version) != 1) {
236*cb351e3fSAndre Przywara 		pr_err("Incompatible MPAM-Fb protocol version %d.%d\n",
237*cb351e3fSAndre Przywara 		       FIELD_GET(MPAM_FB_VERSION_MAJOR_MASK, version),
238*cb351e3fSAndre Przywara 		       FIELD_GET(MPAM_FB_VERSION_MINOR_MASK, version));
239*cb351e3fSAndre Przywara 
240*cb351e3fSAndre Przywara 		return -EINVAL;
241*cb351e3fSAndre Przywara 	}
242*cb351e3fSAndre Przywara 
243*cb351e3fSAndre Przywara 	return 0;
244*cb351e3fSAndre Przywara }
245*cb351e3fSAndre Przywara 
mpam_fb_check_shared_buffer_size(struct mpam_msc * msc)246*cb351e3fSAndre Przywara int mpam_fb_check_shared_buffer_size(struct mpam_msc *msc)
247*cb351e3fSAndre Przywara {
248*cb351e3fSAndre Przywara 	int min_buffer_size = MPAM_FB_MAX_MSG_SIZE +
249*cb351e3fSAndre Przywara 			      sizeof(struct acpi_pcct_ext_pcc_shared_memory);
250*cb351e3fSAndre Przywara 
251*cb351e3fSAndre Przywara 	if (msc->pcc_chan->pcc_chan->shmem_size < min_buffer_size) {
252*cb351e3fSAndre Przywara 		pr_err("MPAM-Fb PCC channel size too small.\n");
253*cb351e3fSAndre Przywara 
254*cb351e3fSAndre Przywara 		return -ENOMEM;
255*cb351e3fSAndre Przywara 	}
256*cb351e3fSAndre Przywara 
257*cb351e3fSAndre Przywara 	return 0;
258*cb351e3fSAndre Przywara }
259