1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * AMD Platform Security Processor (PSP) Platform Access interface 4 * 5 * Copyright (C) 2023 Advanced Micro Devices, Inc. 6 * 7 * Author: Mario Limonciello <mario.limonciello@amd.com> 8 * 9 * Some of this code is adapted from drivers/i2c/busses/i2c-designware-amdpsp.c 10 * developed by Jan Dabros <jsd@semihalf.com> and Copyright (C) 2022 Google Inc. 11 * 12 */ 13 14 #include <linux/bitfield.h> 15 #include <linux/errno.h> 16 #include <linux/iopoll.h> 17 #include <linux/mutex.h> 18 19 #include "platform-access.h" 20 21 #define PSP_CMD_TIMEOUT_US (500 * USEC_PER_MSEC) 22 #define DOORBELL_CMDRESP_STS GENMASK(7, 0) 23 24 /* Recovery field should be equal 0 to start sending commands */ 25 static int check_recovery(u32 __iomem *cmd) 26 { 27 return FIELD_GET(PSP_CMDRESP_RECOVERY, ioread32(cmd)); 28 } 29 30 static int wait_cmd(u32 __iomem *cmd) 31 { 32 u32 tmp, expected; 33 34 /* Expect mbox_cmd to be cleared and ready bit to be set by PSP */ 35 expected = FIELD_PREP(PSP_CMDRESP_RESP, 1); 36 37 /* 38 * Check for readiness of PSP mailbox in a tight loop in order to 39 * process further as soon as command was consumed. 40 */ 41 return readl_poll_timeout(cmd, tmp, (tmp & expected), 0, 42 PSP_CMD_TIMEOUT_US); 43 } 44 45 int psp_check_platform_access_status(void) 46 { 47 struct psp_device *psp = psp_get_master_device(); 48 49 if (!psp || !psp->platform_access_data) 50 return -ENODEV; 51 52 return 0; 53 } 54 EXPORT_SYMBOL(psp_check_platform_access_status); 55 56 int psp_send_platform_access_msg(enum psp_platform_access_msg msg, 57 struct psp_request *req) 58 { 59 struct psp_device *psp = psp_get_master_device(); 60 u32 __iomem *cmd, *lo, *hi; 61 struct psp_platform_access_device *pa_dev; 62 phys_addr_t req_addr; 63 u32 cmd_reg; 64 int ret; 65 66 if (!psp || !psp->platform_access_data) 67 return -ENODEV; 68 69 pa_dev = psp->platform_access_data; 70 71 if (!pa_dev->vdata->cmdresp_reg || !pa_dev->vdata->cmdbuff_addr_lo_reg || 72 !pa_dev->vdata->cmdbuff_addr_hi_reg) 73 return -ENODEV; 74 75 cmd = psp->io_regs + pa_dev->vdata->cmdresp_reg; 76 lo = psp->io_regs + pa_dev->vdata->cmdbuff_addr_lo_reg; 77 hi = psp->io_regs + pa_dev->vdata->cmdbuff_addr_hi_reg; 78 79 mutex_lock(&pa_dev->mailbox_mutex); 80 81 if (check_recovery(cmd)) { 82 dev_dbg(psp->dev, "platform mailbox is in recovery\n"); 83 ret = -EBUSY; 84 goto unlock; 85 } 86 87 if (wait_cmd(cmd)) { 88 dev_dbg(psp->dev, "platform mailbox is not done processing command\n"); 89 ret = -EBUSY; 90 goto unlock; 91 } 92 93 /* 94 * Fill mailbox with address of command-response buffer, which will be 95 * used for sending i2c requests as well as reading status returned by 96 * PSP. Use physical address of buffer, since PSP will map this region. 97 */ 98 req_addr = __psp_pa(req); 99 iowrite32(lower_32_bits(req_addr), lo); 100 iowrite32(upper_32_bits(req_addr), hi); 101 102 print_hex_dump_debug("->psp ", DUMP_PREFIX_OFFSET, 16, 2, req, 103 req->header.payload_size, false); 104 105 /* Write command register to trigger processing */ 106 cmd_reg = FIELD_PREP(PSP_CMDRESP_CMD, msg); 107 iowrite32(cmd_reg, cmd); 108 109 if (wait_cmd(cmd)) { 110 ret = -ETIMEDOUT; 111 goto unlock; 112 } 113 114 /* Ensure it was triggered by this driver */ 115 if (ioread32(lo) != lower_32_bits(req_addr) || 116 ioread32(hi) != upper_32_bits(req_addr)) { 117 ret = -EBUSY; 118 goto unlock; 119 } 120 121 /* 122 * Read status from PSP. If status is non-zero, it indicates an error 123 * occurred during "processing" of the command. 124 * If status is zero, it indicates the command was "processed" 125 * successfully, but the result of the command is in the payload. 126 * Return both cases to the caller as -EIO to investigate. 127 */ 128 cmd_reg = ioread32(cmd); 129 if (FIELD_GET(PSP_CMDRESP_STS, cmd_reg)) 130 req->header.status = FIELD_GET(PSP_CMDRESP_STS, cmd_reg); 131 if (req->header.status) { 132 ret = -EIO; 133 goto unlock; 134 } 135 136 print_hex_dump_debug("<-psp ", DUMP_PREFIX_OFFSET, 16, 2, req, 137 req->header.payload_size, false); 138 139 ret = 0; 140 141 unlock: 142 mutex_unlock(&pa_dev->mailbox_mutex); 143 144 return ret; 145 } 146 EXPORT_SYMBOL_GPL(psp_send_platform_access_msg); 147 148 int psp_ring_platform_doorbell(int msg, u32 *result) 149 { 150 struct psp_device *psp = psp_get_master_device(); 151 struct psp_platform_access_device *pa_dev; 152 u32 __iomem *button, *cmd; 153 int ret, val; 154 155 if (!psp || !psp->platform_access_data) 156 return -ENODEV; 157 158 pa_dev = psp->platform_access_data; 159 button = psp->io_regs + pa_dev->vdata->doorbell_button_reg; 160 cmd = psp->io_regs + pa_dev->vdata->doorbell_cmd_reg; 161 162 mutex_lock(&pa_dev->doorbell_mutex); 163 164 if (wait_cmd(cmd)) { 165 dev_err(psp->dev, "doorbell command not done processing\n"); 166 ret = -EBUSY; 167 goto unlock; 168 } 169 170 iowrite32(FIELD_PREP(DOORBELL_CMDRESP_STS, msg), cmd); 171 iowrite32(PSP_DRBL_RING, button); 172 173 if (wait_cmd(cmd)) { 174 ret = -ETIMEDOUT; 175 goto unlock; 176 } 177 178 val = FIELD_GET(DOORBELL_CMDRESP_STS, ioread32(cmd)); 179 if (val) { 180 if (result) 181 *result = val; 182 ret = -EIO; 183 goto unlock; 184 } 185 186 ret = 0; 187 unlock: 188 mutex_unlock(&pa_dev->doorbell_mutex); 189 190 return ret; 191 } 192 EXPORT_SYMBOL_GPL(psp_ring_platform_doorbell); 193 194 void platform_access_dev_destroy(struct psp_device *psp) 195 { 196 struct psp_platform_access_device *pa_dev = psp->platform_access_data; 197 198 if (!pa_dev) 199 return; 200 201 mutex_destroy(&pa_dev->mailbox_mutex); 202 mutex_destroy(&pa_dev->doorbell_mutex); 203 psp->platform_access_data = NULL; 204 } 205 206 int platform_access_dev_init(struct psp_device *psp) 207 { 208 struct device *dev = psp->dev; 209 struct psp_platform_access_device *pa_dev; 210 211 pa_dev = devm_kzalloc(dev, sizeof(*pa_dev), GFP_KERNEL); 212 if (!pa_dev) 213 return -ENOMEM; 214 215 psp->platform_access_data = pa_dev; 216 pa_dev->psp = psp; 217 pa_dev->dev = dev; 218 219 pa_dev->vdata = (struct platform_access_vdata *)psp->vdata->platform_access; 220 221 mutex_init(&pa_dev->mailbox_mutex); 222 mutex_init(&pa_dev->doorbell_mutex); 223 224 dev_dbg(dev, "platform access enabled\n"); 225 226 return 0; 227 } 228