17ccc4f4eSMario Limonciello // SPDX-License-Identifier: GPL-2.0
27ccc4f4eSMario Limonciello /*
37ccc4f4eSMario Limonciello * AMD Platform Security Processor (PSP) Platform Access interface
47ccc4f4eSMario Limonciello *
57ccc4f4eSMario Limonciello * Copyright (C) 2023 Advanced Micro Devices, Inc.
67ccc4f4eSMario Limonciello *
77ccc4f4eSMario Limonciello * Author: Mario Limonciello <mario.limonciello@amd.com>
87ccc4f4eSMario Limonciello *
97ccc4f4eSMario Limonciello * Some of this code is adapted from drivers/i2c/busses/i2c-designware-amdpsp.c
107ccc4f4eSMario Limonciello * developed by Jan Dabros <jsd@semihalf.com> and Copyright (C) 2022 Google Inc.
117ccc4f4eSMario Limonciello *
127ccc4f4eSMario Limonciello */
137ccc4f4eSMario Limonciello
147ccc4f4eSMario Limonciello #include <linux/bitfield.h>
157ccc4f4eSMario Limonciello #include <linux/errno.h>
167ccc4f4eSMario Limonciello #include <linux/iopoll.h>
177ccc4f4eSMario Limonciello #include <linux/mutex.h>
187ccc4f4eSMario Limonciello
197ccc4f4eSMario Limonciello #include "platform-access.h"
207ccc4f4eSMario Limonciello
217ccc4f4eSMario Limonciello #define PSP_CMD_TIMEOUT_US (500 * USEC_PER_MSEC)
226699e143SMario Limonciello #define DOORBELL_CMDRESP_STS GENMASK(7, 0)
237ccc4f4eSMario Limonciello
247ccc4f4eSMario Limonciello /* Recovery field should be equal 0 to start sending commands */
check_recovery(u32 __iomem * cmd)257ccc4f4eSMario Limonciello static int check_recovery(u32 __iomem *cmd)
267ccc4f4eSMario Limonciello {
277ccc4f4eSMario Limonciello return FIELD_GET(PSP_CMDRESP_RECOVERY, ioread32(cmd));
287ccc4f4eSMario Limonciello }
297ccc4f4eSMario Limonciello
wait_cmd(u32 __iomem * cmd)307ccc4f4eSMario Limonciello static int wait_cmd(u32 __iomem *cmd)
317ccc4f4eSMario Limonciello {
327ccc4f4eSMario Limonciello u32 tmp, expected;
337ccc4f4eSMario Limonciello
347ccc4f4eSMario Limonciello /* Expect mbox_cmd to be cleared and ready bit to be set by PSP */
357ccc4f4eSMario Limonciello expected = FIELD_PREP(PSP_CMDRESP_RESP, 1);
367ccc4f4eSMario Limonciello
377ccc4f4eSMario Limonciello /*
387ccc4f4eSMario Limonciello * Check for readiness of PSP mailbox in a tight loop in order to
397ccc4f4eSMario Limonciello * process further as soon as command was consumed.
407ccc4f4eSMario Limonciello */
417ccc4f4eSMario Limonciello return readl_poll_timeout(cmd, tmp, (tmp & expected), 0,
427ccc4f4eSMario Limonciello PSP_CMD_TIMEOUT_US);
437ccc4f4eSMario Limonciello }
447ccc4f4eSMario Limonciello
psp_check_platform_access_status(void)457ccc4f4eSMario Limonciello int psp_check_platform_access_status(void)
467ccc4f4eSMario Limonciello {
477ccc4f4eSMario Limonciello struct psp_device *psp = psp_get_master_device();
487ccc4f4eSMario Limonciello
497ccc4f4eSMario Limonciello if (!psp || !psp->platform_access_data)
507ccc4f4eSMario Limonciello return -ENODEV;
517ccc4f4eSMario Limonciello
527ccc4f4eSMario Limonciello return 0;
537ccc4f4eSMario Limonciello }
547ccc4f4eSMario Limonciello EXPORT_SYMBOL(psp_check_platform_access_status);
557ccc4f4eSMario Limonciello
psp_send_platform_access_msg(enum psp_platform_access_msg msg,struct psp_request * req)567ccc4f4eSMario Limonciello int psp_send_platform_access_msg(enum psp_platform_access_msg msg,
577ccc4f4eSMario Limonciello struct psp_request *req)
587ccc4f4eSMario Limonciello {
597ccc4f4eSMario Limonciello struct psp_device *psp = psp_get_master_device();
607ccc4f4eSMario Limonciello u32 __iomem *cmd, *lo, *hi;
617ccc4f4eSMario Limonciello struct psp_platform_access_device *pa_dev;
627ccc4f4eSMario Limonciello phys_addr_t req_addr;
637ccc4f4eSMario Limonciello u32 cmd_reg;
647ccc4f4eSMario Limonciello int ret;
657ccc4f4eSMario Limonciello
667ccc4f4eSMario Limonciello if (!psp || !psp->platform_access_data)
677ccc4f4eSMario Limonciello return -ENODEV;
687ccc4f4eSMario Limonciello
697ccc4f4eSMario Limonciello pa_dev = psp->platform_access_data;
70dd536cb9SMario Limonciello
71dd536cb9SMario Limonciello if (!pa_dev->vdata->cmdresp_reg || !pa_dev->vdata->cmdbuff_addr_lo_reg ||
72dd536cb9SMario Limonciello !pa_dev->vdata->cmdbuff_addr_hi_reg)
73dd536cb9SMario Limonciello return -ENODEV;
74dd536cb9SMario Limonciello
757ccc4f4eSMario Limonciello cmd = psp->io_regs + pa_dev->vdata->cmdresp_reg;
767ccc4f4eSMario Limonciello lo = psp->io_regs + pa_dev->vdata->cmdbuff_addr_lo_reg;
777ccc4f4eSMario Limonciello hi = psp->io_regs + pa_dev->vdata->cmdbuff_addr_hi_reg;
787ccc4f4eSMario Limonciello
797ccc4f4eSMario Limonciello mutex_lock(&pa_dev->mailbox_mutex);
807ccc4f4eSMario Limonciello
817ccc4f4eSMario Limonciello if (check_recovery(cmd)) {
827ccc4f4eSMario Limonciello dev_dbg(psp->dev, "platform mailbox is in recovery\n");
837ccc4f4eSMario Limonciello ret = -EBUSY;
847ccc4f4eSMario Limonciello goto unlock;
857ccc4f4eSMario Limonciello }
867ccc4f4eSMario Limonciello
877ccc4f4eSMario Limonciello if (wait_cmd(cmd)) {
887ccc4f4eSMario Limonciello dev_dbg(psp->dev, "platform mailbox is not done processing command\n");
897ccc4f4eSMario Limonciello ret = -EBUSY;
907ccc4f4eSMario Limonciello goto unlock;
917ccc4f4eSMario Limonciello }
927ccc4f4eSMario Limonciello
937ccc4f4eSMario Limonciello /*
947ccc4f4eSMario Limonciello * Fill mailbox with address of command-response buffer, which will be
957ccc4f4eSMario Limonciello * used for sending i2c requests as well as reading status returned by
967ccc4f4eSMario Limonciello * PSP. Use physical address of buffer, since PSP will map this region.
977ccc4f4eSMario Limonciello */
987ccc4f4eSMario Limonciello req_addr = __psp_pa(req);
997ccc4f4eSMario Limonciello iowrite32(lower_32_bits(req_addr), lo);
1007ccc4f4eSMario Limonciello iowrite32(upper_32_bits(req_addr), hi);
1017ccc4f4eSMario Limonciello
1027ccc4f4eSMario Limonciello print_hex_dump_debug("->psp ", DUMP_PREFIX_OFFSET, 16, 2, req,
1037ccc4f4eSMario Limonciello req->header.payload_size, false);
1047ccc4f4eSMario Limonciello
1057ccc4f4eSMario Limonciello /* Write command register to trigger processing */
1067ccc4f4eSMario Limonciello cmd_reg = FIELD_PREP(PSP_CMDRESP_CMD, msg);
1077ccc4f4eSMario Limonciello iowrite32(cmd_reg, cmd);
1087ccc4f4eSMario Limonciello
1097ccc4f4eSMario Limonciello if (wait_cmd(cmd)) {
1107ccc4f4eSMario Limonciello ret = -ETIMEDOUT;
1117ccc4f4eSMario Limonciello goto unlock;
1127ccc4f4eSMario Limonciello }
1137ccc4f4eSMario Limonciello
1147ccc4f4eSMario Limonciello /* Ensure it was triggered by this driver */
1157ccc4f4eSMario Limonciello if (ioread32(lo) != lower_32_bits(req_addr) ||
1167ccc4f4eSMario Limonciello ioread32(hi) != upper_32_bits(req_addr)) {
1177ccc4f4eSMario Limonciello ret = -EBUSY;
1187ccc4f4eSMario Limonciello goto unlock;
1197ccc4f4eSMario Limonciello }
1207ccc4f4eSMario Limonciello
121*6a28ba59SMario Limonciello /*
122*6a28ba59SMario Limonciello * Read status from PSP. If status is non-zero, it indicates an error
123*6a28ba59SMario Limonciello * occurred during "processing" of the command.
124*6a28ba59SMario Limonciello * If status is zero, it indicates the command was "processed"
125*6a28ba59SMario Limonciello * successfully, but the result of the command is in the payload.
126*6a28ba59SMario Limonciello * Return both cases to the caller as -EIO to investigate.
127*6a28ba59SMario Limonciello */
1287ccc4f4eSMario Limonciello cmd_reg = ioread32(cmd);
129*6a28ba59SMario Limonciello if (FIELD_GET(PSP_CMDRESP_STS, cmd_reg))
1307ccc4f4eSMario Limonciello req->header.status = FIELD_GET(PSP_CMDRESP_STS, cmd_reg);
1317ccc4f4eSMario Limonciello if (req->header.status) {
1327ccc4f4eSMario Limonciello ret = -EIO;
1337ccc4f4eSMario Limonciello goto unlock;
1347ccc4f4eSMario Limonciello }
1357ccc4f4eSMario Limonciello
1367ccc4f4eSMario Limonciello print_hex_dump_debug("<-psp ", DUMP_PREFIX_OFFSET, 16, 2, req,
1377ccc4f4eSMario Limonciello req->header.payload_size, false);
1387ccc4f4eSMario Limonciello
1397ccc4f4eSMario Limonciello ret = 0;
1407ccc4f4eSMario Limonciello
1417ccc4f4eSMario Limonciello unlock:
1427ccc4f4eSMario Limonciello mutex_unlock(&pa_dev->mailbox_mutex);
1437ccc4f4eSMario Limonciello
1447ccc4f4eSMario Limonciello return ret;
1457ccc4f4eSMario Limonciello }
1467ccc4f4eSMario Limonciello EXPORT_SYMBOL_GPL(psp_send_platform_access_msg);
1477ccc4f4eSMario Limonciello
psp_ring_platform_doorbell(int msg,u32 * result)148a19c61b0SMario Limonciello int psp_ring_platform_doorbell(int msg, u32 *result)
149d5812571SMario Limonciello {
150d5812571SMario Limonciello struct psp_device *psp = psp_get_master_device();
151d5812571SMario Limonciello struct psp_platform_access_device *pa_dev;
152d5812571SMario Limonciello u32 __iomem *button, *cmd;
153d5812571SMario Limonciello int ret, val;
154d5812571SMario Limonciello
155d5812571SMario Limonciello if (!psp || !psp->platform_access_data)
156d5812571SMario Limonciello return -ENODEV;
157d5812571SMario Limonciello
158d5812571SMario Limonciello pa_dev = psp->platform_access_data;
159d5812571SMario Limonciello button = psp->io_regs + pa_dev->vdata->doorbell_button_reg;
160d5812571SMario Limonciello cmd = psp->io_regs + pa_dev->vdata->doorbell_cmd_reg;
161d5812571SMario Limonciello
162d5812571SMario Limonciello mutex_lock(&pa_dev->doorbell_mutex);
163d5812571SMario Limonciello
164d5812571SMario Limonciello if (wait_cmd(cmd)) {
165e0358dedSMario Limonciello dev_err(psp->dev, "doorbell command not done processing\n");
166d5812571SMario Limonciello ret = -EBUSY;
167d5812571SMario Limonciello goto unlock;
168d5812571SMario Limonciello }
169d5812571SMario Limonciello
1706699e143SMario Limonciello iowrite32(FIELD_PREP(DOORBELL_CMDRESP_STS, msg), cmd);
171d5812571SMario Limonciello iowrite32(PSP_DRBL_RING, button);
172d5812571SMario Limonciello
173d5812571SMario Limonciello if (wait_cmd(cmd)) {
174d5812571SMario Limonciello ret = -ETIMEDOUT;
175d5812571SMario Limonciello goto unlock;
176d5812571SMario Limonciello }
177d5812571SMario Limonciello
1786699e143SMario Limonciello val = FIELD_GET(DOORBELL_CMDRESP_STS, ioread32(cmd));
179d5812571SMario Limonciello if (val) {
180a19c61b0SMario Limonciello if (result)
181a19c61b0SMario Limonciello *result = val;
182d5812571SMario Limonciello ret = -EIO;
183d5812571SMario Limonciello goto unlock;
184d5812571SMario Limonciello }
185d5812571SMario Limonciello
186d5812571SMario Limonciello ret = 0;
187d5812571SMario Limonciello unlock:
188d5812571SMario Limonciello mutex_unlock(&pa_dev->doorbell_mutex);
189d5812571SMario Limonciello
190d5812571SMario Limonciello return ret;
191d5812571SMario Limonciello }
192d5812571SMario Limonciello EXPORT_SYMBOL_GPL(psp_ring_platform_doorbell);
193d5812571SMario Limonciello
platform_access_dev_destroy(struct psp_device * psp)1947ccc4f4eSMario Limonciello void platform_access_dev_destroy(struct psp_device *psp)
1957ccc4f4eSMario Limonciello {
1967ccc4f4eSMario Limonciello struct psp_platform_access_device *pa_dev = psp->platform_access_data;
1977ccc4f4eSMario Limonciello
1987ccc4f4eSMario Limonciello if (!pa_dev)
1997ccc4f4eSMario Limonciello return;
2007ccc4f4eSMario Limonciello
2017ccc4f4eSMario Limonciello mutex_destroy(&pa_dev->mailbox_mutex);
202d5812571SMario Limonciello mutex_destroy(&pa_dev->doorbell_mutex);
2037ccc4f4eSMario Limonciello psp->platform_access_data = NULL;
2047ccc4f4eSMario Limonciello }
2057ccc4f4eSMario Limonciello
platform_access_dev_init(struct psp_device * psp)2067ccc4f4eSMario Limonciello int platform_access_dev_init(struct psp_device *psp)
2077ccc4f4eSMario Limonciello {
2087ccc4f4eSMario Limonciello struct device *dev = psp->dev;
2097ccc4f4eSMario Limonciello struct psp_platform_access_device *pa_dev;
2107ccc4f4eSMario Limonciello
2117ccc4f4eSMario Limonciello pa_dev = devm_kzalloc(dev, sizeof(*pa_dev), GFP_KERNEL);
2127ccc4f4eSMario Limonciello if (!pa_dev)
2137ccc4f4eSMario Limonciello return -ENOMEM;
2147ccc4f4eSMario Limonciello
2157ccc4f4eSMario Limonciello psp->platform_access_data = pa_dev;
2167ccc4f4eSMario Limonciello pa_dev->psp = psp;
2177ccc4f4eSMario Limonciello pa_dev->dev = dev;
2187ccc4f4eSMario Limonciello
2197ccc4f4eSMario Limonciello pa_dev->vdata = (struct platform_access_vdata *)psp->vdata->platform_access;
2207ccc4f4eSMario Limonciello
2217ccc4f4eSMario Limonciello mutex_init(&pa_dev->mailbox_mutex);
222d5812571SMario Limonciello mutex_init(&pa_dev->doorbell_mutex);
2237ccc4f4eSMario Limonciello
2247ccc4f4eSMario Limonciello dev_dbg(dev, "platform access enabled\n");
2257ccc4f4eSMario Limonciello
2267ccc4f4eSMario Limonciello return 0;
2277ccc4f4eSMario Limonciello }
228