xref: /openbmc/linux/drivers/crypto/ccp/platform-access.c (revision 5ee9cd065836e5934710ca35653bce7905add20b)
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