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