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