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_FB_VERSION_MAJOR_MASK GENMASK_U32(31, 16) 24 #define MPAM_FB_VERSION_MINOR_MASK GENMASK_U32(15, 0) 25 26 #define MPAM_MSC_READ_CMD 0x4 27 #define MPAM_MSC_WRITE_CMD 0x5 28 29 #define MPAM_FB_PROT_HEADER_LEN sizeof(u32) 30 /* The longest message is MPAM_MSC_WRITE, with 4 parameters. */ 31 #define MPAM_FB_MAX_MSG_SIZE (4 * sizeof(u32)) 32 33 #define MPAM_FB_SUCCESS 0 34 #define MPAM_FB_ERR_NOT_SUPPORTED -1 35 #define MPAM_FB_ERR_INVALID_PARAMETERS -2 36 #define MPAM_FB_ERR_DENIED -3 37 #define MPAM_FB_ERR_NOT_FOUND -4 38 #define MPAM_FB_ERR_OUT_OF_RANGE -5 39 #define MPAM_FB_ERR_BUSY -6 40 #define MPAM_FB_ERR_COMMS_ERROR -7 41 #define MPAM_FB_ERR_GENERIC_ERROR -8 42 #define MPAM_FB_ERR_HW_ERROR -9 43 #define MPAM_FB_ERR_PROTOCOL_ERROR -10 44 #define MPAM_FB_ERR_IN_USE -11 45 46 static atomic_t mpam_fb_token = ATOMIC_INIT(0); 47 48 static int mpam_fb_to_linux_errno(int mpam_fb_code) 49 { 50 switch (mpam_fb_code) { 51 case MPAM_FB_ERR_NOT_SUPPORTED: 52 return -EOPNOTSUPP; 53 case MPAM_FB_ERR_INVALID_PARAMETERS: 54 return -EINVAL; 55 case MPAM_FB_ERR_NOT_FOUND: 56 return -ENOENT; 57 case MPAM_FB_ERR_OUT_OF_RANGE: 58 return -ERANGE; 59 case MPAM_FB_ERR_BUSY: 60 return -EBUSY; 61 default: 62 return -EINVAL; 63 } 64 } 65 66 static void mpam_fb_build_version_message(unsigned int token, 67 void __iomem *msg_buf) 68 { 69 struct acpi_pcct_ext_pcc_shared_memory __iomem *pcc_shmem = msg_buf; 70 71 /* .signature is filled by the platform */ 72 writel_relaxed(PCC_CMD_COMPLETION_NOTIFY, &pcc_shmem->flags); 73 writel_relaxed(MPAM_FB_PROT_HEADER_LEN, &pcc_shmem->length); 74 writel_relaxed(MPAM_PROTOCOL_VERSION_CMD | 75 FIELD_PREP(MPAM_MSC_TOKEN_MASK, token) | 76 FIELD_PREP(MPAM_MSC_PROT_ID_MASK, MPAM_FB_PROTOCOL_ID), 77 &pcc_shmem->command); 78 } 79 80 static void mpam_fb_build_read_message(int msc_fb_id, int reg, 81 unsigned int token, 82 void __iomem *msg_buf) 83 { 84 struct acpi_pcct_ext_pcc_shared_memory __iomem *pcc_shmem = msg_buf; 85 struct mpam_fb_read_payload { 86 u32 msc_fb_id; 87 u32 flags; 88 u32 reg_offset; 89 } __packed __iomem *payload = msg_buf + sizeof(*pcc_shmem); 90 int msg_size = MPAM_FB_PROT_HEADER_LEN + sizeof(*payload); 91 92 /* .signature is filled by the platform */ 93 writel_relaxed(PCC_CMD_COMPLETION_NOTIFY, &pcc_shmem->flags); 94 writel_relaxed(msg_size, &pcc_shmem->length); 95 writel_relaxed(MPAM_MSC_READ_CMD | 96 FIELD_PREP(MPAM_MSC_TOKEN_MASK, token) | 97 FIELD_PREP(MPAM_MSC_PROT_ID_MASK, MPAM_FB_PROTOCOL_ID), 98 &pcc_shmem->command); 99 100 writel_relaxed(msc_fb_id, &payload->msc_fb_id); 101 writel_relaxed(0, &payload->flags); 102 writel_relaxed(reg, &payload->reg_offset); 103 } 104 105 static void mpam_fb_build_write_message(int msc_fb_id, int reg, u32 val, 106 unsigned int token, 107 void __iomem *msg_buf) 108 { 109 struct acpi_pcct_ext_pcc_shared_memory __iomem *pcc_shmem = msg_buf; 110 struct mpam_fb_write_payload { 111 u32 msc_fb_id; 112 u32 flags; 113 u32 reg_offset; 114 u32 value; 115 } __packed __iomem *payload = msg_buf + sizeof(*pcc_shmem); 116 int msg_size = MPAM_FB_PROT_HEADER_LEN + sizeof(*payload); 117 118 /* .signature is filled by the platform */ 119 writel_relaxed(PCC_CMD_COMPLETION_NOTIFY, &pcc_shmem->flags); 120 writel_relaxed(msg_size, &pcc_shmem->length); 121 writel_relaxed(MPAM_MSC_WRITE_CMD | 122 FIELD_PREP(MPAM_MSC_TOKEN_MASK, token) | 123 FIELD_PREP(MPAM_MSC_PROT_ID_MASK, MPAM_FB_PROTOCOL_ID), 124 &pcc_shmem->command); 125 126 writel_relaxed(msc_fb_id, &payload->msc_fb_id); 127 writel_relaxed(0, &payload->flags); 128 writel_relaxed(reg, &payload->reg_offset); 129 writel_relaxed(val, &payload->value); 130 } 131 132 static int mpam_fb_send_request(struct mpam_msc *msc, u32 msc_fb_id, 133 u16 reg, u32 *result, int mpam_fb_command) 134 { 135 unsigned int token = atomic_inc_return(&mpam_fb_token); 136 struct acpi_pcct_ext_pcc_shared_memory __iomem *pcc_shmem; 137 struct mpam_pcc_chan *pcc_chan; 138 struct pcc_mbox_chan *chan; 139 void __iomem *payload_ofs; 140 int mpam_fb_err = 0; 141 u32 status; 142 int ret; 143 144 pcc_chan = msc->pcc_chan; 145 if (!pcc_chan) 146 return -ENODEV; 147 148 chan = pcc_chan->pcc_chan; 149 150 /* prune token to fit into the 10 bits inside the command register */ 151 token = FIELD_GET(MPAM_MSC_TOKEN_MASK, 152 FIELD_PREP(MPAM_MSC_TOKEN_MASK, token)); 153 154 mutex_lock(&pcc_chan->pcc_chan_lock); 155 156 switch (mpam_fb_command) { 157 case MPAM_PROTOCOL_VERSION_CMD: 158 mpam_fb_build_version_message(token, chan->shmem); 159 break; 160 case MPAM_MSC_READ_CMD: 161 mpam_fb_build_read_message(msc_fb_id, reg, token, chan->shmem); 162 break; 163 case MPAM_MSC_WRITE_CMD: 164 mpam_fb_build_write_message(msc_fb_id, reg, *result, 165 token, chan->shmem); 166 break; 167 default: 168 dev_err(&msc->pdev->dev, "unsupported MPAM-Fb command %d\n", 169 mpam_fb_command); 170 ret = -EINVAL; 171 goto out_err; 172 } 173 174 dma_wmb(); 175 ret = mbox_send_message(chan->mchan, NULL); 176 if (ret < 0) 177 goto out_err; 178 179 pcc_shmem = chan->shmem; 180 payload_ofs = chan->shmem + sizeof(*pcc_shmem); 181 status = readl(&pcc_shmem->command); 182 if (FIELD_GET(MPAM_MSC_TOKEN_MASK, status) != token) { 183 ret = -ETIMEDOUT; 184 goto out_err; 185 } 186 187 mpam_fb_err = readl(payload_ofs + 0x0); 188 if (mpam_fb_err < 0) { 189 ret = mpam_fb_to_linux_errno(mpam_fb_err); 190 goto out_err; 191 } 192 193 if (mpam_fb_command != MPAM_MSC_WRITE_CMD) 194 *result = readl(payload_ofs + 0x4); 195 196 mutex_unlock(&pcc_chan->pcc_chan_lock); 197 198 return 0; 199 200 out_err: 201 mutex_unlock(&pcc_chan->pcc_chan_lock); 202 203 mpam_fb_disable_mpam(ret, mpam_fb_err); 204 205 /* 206 * TODO: Error codes are filtered in higher layers, return something 207 * that is known there for now, to actually report them. 208 */ 209 return -EIO; 210 } 211 212 int mpam_fb_send_read_request(struct mpam_msc *msc, u16 reg, u32 *result) 213 { 214 return mpam_fb_send_request(msc, msc->fb_id, reg, result, 215 MPAM_MSC_READ_CMD); 216 } 217 218 int mpam_fb_send_write_request(struct mpam_msc *msc, u16 reg, u32 value) 219 { 220 return mpam_fb_send_request(msc, msc->fb_id, reg, &value, 221 MPAM_MSC_WRITE_CMD); 222 } 223 224 /* We only support MPAM-Fb protocol version 1.x */ 225 int mpam_fb_check_protocol_version(struct mpam_msc *msc) 226 { 227 u32 version; 228 int ret; 229 230 ret = mpam_fb_send_request(msc, 0, 0, &version, 231 MPAM_PROTOCOL_VERSION_CMD); 232 if (ret) 233 return ret; 234 235 if (FIELD_GET(MPAM_FB_VERSION_MAJOR_MASK, version) != 1) { 236 pr_err("Incompatible MPAM-Fb protocol version %d.%d\n", 237 FIELD_GET(MPAM_FB_VERSION_MAJOR_MASK, version), 238 FIELD_GET(MPAM_FB_VERSION_MINOR_MASK, version)); 239 240 return -EINVAL; 241 } 242 243 return 0; 244 } 245 246 int mpam_fb_check_shared_buffer_size(struct mpam_msc *msc) 247 { 248 int min_buffer_size = MPAM_FB_MAX_MSG_SIZE + 249 sizeof(struct acpi_pcct_ext_pcc_shared_memory); 250 251 if (msc->pcc_chan->pcc_chan->shmem_size < min_buffer_size) { 252 pr_err("MPAM-Fb PCC channel size too small.\n"); 253 254 return -ENOMEM; 255 } 256 257 return 0; 258 } 259