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