Something went wrong. Try again.
(I'm so sorry!) Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259// SPDX-License-Identifier: GPL-2.0// Copyright (C) 2024-2026 Arm Ltd.
#include <linux/arm_mpam.h>#include <linux/cleanup.h>#include <linux/errno.h>#include <linux/mailbox_client.h>#include <linux/mutex.h>#include <linux/platform_device.h>#include <linux/types.h>
#include <acpi/pcc.h>#include <asm/mpam.h>
#include "mpam_internal.h"
#define MPAM_MSC_TOKEN_MASK GENMASK_U32(27, 18)#define MPAM_MSC_PROT_ID_MASK GENMASK_U32(17, 10)
#define MPAM_FB_PROTOCOL_ID 0x1a
#define MPAM_PROTOCOL_VERSION_CMD 0x0#define MPAM_FB_VERSION_MAJOR_MASK GENMASK_U32(31, 16)#define MPAM_FB_VERSION_MINOR_MASK GENMASK_U32(15, 0)
#define MPAM_MSC_READ_CMD 0x4#define MPAM_MSC_WRITE_CMD 0x5
#define MPAM_FB_PROT_HEADER_LEN sizeof(u32)/* The longest message is MPAM_MSC_WRITE, with 4 parameters. */#define MPAM_FB_MAX_MSG_SIZE (4 * sizeof(u32))
#define MPAM_FB_SUCCESS 0#define MPAM_FB_ERR_NOT_SUPPORTED -1#define MPAM_FB_ERR_INVALID_PARAMETERS -2#define MPAM_FB_ERR_DENIED -3#define MPAM_FB_ERR_NOT_FOUND -4#define MPAM_FB_ERR_OUT_OF_RANGE -5#define MPAM_FB_ERR_BUSY -6#define MPAM_FB_ERR_COMMS_ERROR -7#define MPAM_FB_ERR_GENERIC_ERROR -8#define MPAM_FB_ERR_HW_ERROR -9#define MPAM_FB_ERR_PROTOCOL_ERROR -10#define MPAM_FB_ERR_IN_USE -11
static atomic_t mpam_fb_token = ATOMIC_INIT(0);
static int mpam_fb_to_linux_errno(int mpam_fb_code){ switch (mpam_fb_code) { case MPAM_FB_ERR_NOT_SUPPORTED: return -EOPNOTSUPP; case MPAM_FB_ERR_INVALID_PARAMETERS: return -EINVAL; case MPAM_FB_ERR_NOT_FOUND: return -ENOENT; case MPAM_FB_ERR_OUT_OF_RANGE: return -ERANGE; case MPAM_FB_ERR_BUSY: return -EBUSY; default: return -EINVAL; }}
static void mpam_fb_build_version_message(unsigned int token, void __iomem *msg_buf){ struct acpi_pcct_ext_pcc_shared_memory __iomem *pcc_shmem = msg_buf;
/* .signature is filled by the platform */ writel_relaxed(PCC_CMD_COMPLETION_NOTIFY, &pcc_shmem->flags); writel_relaxed(MPAM_FB_PROT_HEADER_LEN, &pcc_shmem->length); writel_relaxed(MPAM_PROTOCOL_VERSION_CMD | FIELD_PREP(MPAM_MSC_TOKEN_MASK, token) | FIELD_PREP(MPAM_MSC_PROT_ID_MASK, MPAM_FB_PROTOCOL_ID), &pcc_shmem->command);}
static void mpam_fb_build_read_message(int msc_fb_id, int reg, unsigned int token, void __iomem *msg_buf){ struct acpi_pcct_ext_pcc_shared_memory __iomem *pcc_shmem = msg_buf; struct mpam_fb_read_payload { u32 msc_fb_id; u32 flags; u32 reg_offset; } __packed __iomem *payload = msg_buf + sizeof(*pcc_shmem); int msg_size = MPAM_FB_PROT_HEADER_LEN + sizeof(*payload);
/* .signature is filled by the platform */ writel_relaxed(PCC_CMD_COMPLETION_NOTIFY, &pcc_shmem->flags); writel_relaxed(msg_size, &pcc_shmem->length); writel_relaxed(MPAM_MSC_READ_CMD | FIELD_PREP(MPAM_MSC_TOKEN_MASK, token) | FIELD_PREP(MPAM_MSC_PROT_ID_MASK, MPAM_FB_PROTOCOL_ID), &pcc_shmem->command);
writel_relaxed(msc_fb_id, &payload->msc_fb_id); writel_relaxed(0, &payload->flags); writel_relaxed(reg, &payload->reg_offset);}
static void mpam_fb_build_write_message(int msc_fb_id, int reg, u32 val, unsigned int token, void __iomem *msg_buf){ struct acpi_pcct_ext_pcc_shared_memory __iomem *pcc_shmem = msg_buf; struct mpam_fb_write_payload { u32 msc_fb_id; u32 flags; u32 reg_offset; u32 value; } __packed __iomem *payload = msg_buf + sizeof(*pcc_shmem); int msg_size = MPAM_FB_PROT_HEADER_LEN + sizeof(*payload);
/* .signature is filled by the platform */ writel_relaxed(PCC_CMD_COMPLETION_NOTIFY, &pcc_shmem->flags); writel_relaxed(msg_size, &pcc_shmem->length); writel_relaxed(MPAM_MSC_WRITE_CMD | FIELD_PREP(MPAM_MSC_TOKEN_MASK, token) | FIELD_PREP(MPAM_MSC_PROT_ID_MASK, MPAM_FB_PROTOCOL_ID), &pcc_shmem->command);
writel_relaxed(msc_fb_id, &payload->msc_fb_id); writel_relaxed(0, &payload->flags); writel_relaxed(reg, &payload->reg_offset); writel_relaxed(val, &payload->value);}
static int mpam_fb_send_request(struct mpam_msc *msc, u32 msc_fb_id, u16 reg, u32 *result, int mpam_fb_command){ unsigned int token = atomic_inc_return(&mpam_fb_token); struct acpi_pcct_ext_pcc_shared_memory __iomem *pcc_shmem; struct mpam_pcc_chan *pcc_chan; struct pcc_mbox_chan *chan; void __iomem *payload_ofs; int mpam_fb_err = 0; u32 status; int ret;
pcc_chan = msc->pcc_chan; if (!pcc_chan) return -ENODEV;
chan = pcc_chan->pcc_chan;
/* prune token to fit into the 10 bits inside the command register */ token = FIELD_GET(MPAM_MSC_TOKEN_MASK, FIELD_PREP(MPAM_MSC_TOKEN_MASK, token));
mutex_lock(&pcc_chan->pcc_chan_lock);
switch (mpam_fb_command) { case MPAM_PROTOCOL_VERSION_CMD: mpam_fb_build_version_message(token, chan->shmem); break; case MPAM_MSC_READ_CMD: mpam_fb_build_read_message(msc_fb_id, reg, token, chan->shmem); break; case MPAM_MSC_WRITE_CMD: mpam_fb_build_write_message(msc_fb_id, reg, *result, token, chan->shmem); break; default: dev_err(&msc->pdev->dev, "unsupported MPAM-Fb command %d\n", mpam_fb_command); ret = -EINVAL; goto out_err; }
dma_wmb(); ret = mbox_send_message(chan->mchan, NULL); if (ret < 0) goto out_err;
pcc_shmem = chan->shmem; payload_ofs = chan->shmem + sizeof(*pcc_shmem); status = readl(&pcc_shmem->command); if (FIELD_GET(MPAM_MSC_TOKEN_MASK, status) != token) { ret = -ETIMEDOUT; goto out_err; }
mpam_fb_err = readl(payload_ofs + 0x0); if (mpam_fb_err < 0) { ret = mpam_fb_to_linux_errno(mpam_fb_err); goto out_err; }
if (mpam_fb_command != MPAM_MSC_WRITE_CMD) *result = readl(payload_ofs + 0x4);
mutex_unlock(&pcc_chan->pcc_chan_lock);
return 0;
out_err: mutex_unlock(&pcc_chan->pcc_chan_lock);
mpam_fb_disable_mpam(ret, mpam_fb_err);
/* * TODO: Error codes are filtered in higher layers, return something * that is known there for now, to actually report them. */ return -EIO;}
int mpam_fb_send_read_request(struct mpam_msc *msc, u16 reg, u32 *result){ return mpam_fb_send_request(msc, msc->fb_id, reg, result, MPAM_MSC_READ_CMD);}
int mpam_fb_send_write_request(struct mpam_msc *msc, u16 reg, u32 value){ return mpam_fb_send_request(msc, msc->fb_id, reg, &value, MPAM_MSC_WRITE_CMD);}
/* We only support MPAM-Fb protocol version 1.x */int mpam_fb_check_protocol_version(struct mpam_msc *msc){ u32 version; int ret;
ret = mpam_fb_send_request(msc, 0, 0, &version, MPAM_PROTOCOL_VERSION_CMD); if (ret) return ret;
if (FIELD_GET(MPAM_FB_VERSION_MAJOR_MASK, version) != 1) { pr_err("Incompatible MPAM-Fb protocol version %d.%d\n", FIELD_GET(MPAM_FB_VERSION_MAJOR_MASK, version), FIELD_GET(MPAM_FB_VERSION_MINOR_MASK, version));
return -EINVAL; }
return 0;}
int mpam_fb_check_shared_buffer_size(struct mpam_msc *msc){ int min_buffer_size = MPAM_FB_MAX_MSG_SIZE + sizeof(struct acpi_pcct_ext_pcc_shared_memory);
if (msc->pcc_chan->pcc_chan->shmem_size < min_buffer_size) { pr_err("MPAM-Fb PCC channel size too small.\n");
return -ENOMEM; }
return 0;}