xref: /linux/drivers/crypto/ccp/platform-access.c (revision fd7d598270724cc787982ea48bbe17ad383a8b7f)
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 	/* Store the status in request header for caller to investigate */
122 	cmd_reg = ioread32(cmd);
123 	req->header.status = FIELD_GET(PSP_CMDRESP_STS, cmd_reg);
124 	if (req->header.status) {
125 		ret = -EIO;
126 		goto unlock;
127 	}
128 
129 	print_hex_dump_debug("<-psp ", DUMP_PREFIX_OFFSET, 16, 2, req,
130 			     req->header.payload_size, false);
131 
132 	ret = 0;
133 
134 unlock:
135 	mutex_unlock(&pa_dev->mailbox_mutex);
136 
137 	return ret;
138 }
139 EXPORT_SYMBOL_GPL(psp_send_platform_access_msg);
140 
141 int psp_ring_platform_doorbell(int msg, u32 *result)
142 {
143 	struct psp_device *psp = psp_get_master_device();
144 	struct psp_platform_access_device *pa_dev;
145 	u32 __iomem *button, *cmd;
146 	int ret, val;
147 
148 	if (!psp || !psp->platform_access_data)
149 		return -ENODEV;
150 
151 	pa_dev = psp->platform_access_data;
152 	button = psp->io_regs + pa_dev->vdata->doorbell_button_reg;
153 	cmd = psp->io_regs + pa_dev->vdata->doorbell_cmd_reg;
154 
155 	mutex_lock(&pa_dev->doorbell_mutex);
156 
157 	if (wait_cmd(cmd)) {
158 		dev_err(psp->dev, "doorbell command not done processing\n");
159 		ret = -EBUSY;
160 		goto unlock;
161 	}
162 
163 	iowrite32(FIELD_PREP(DOORBELL_CMDRESP_STS, msg), cmd);
164 	iowrite32(PSP_DRBL_RING, button);
165 
166 	if (wait_cmd(cmd)) {
167 		ret = -ETIMEDOUT;
168 		goto unlock;
169 	}
170 
171 	val = FIELD_GET(DOORBELL_CMDRESP_STS, ioread32(cmd));
172 	if (val) {
173 		if (result)
174 			*result = val;
175 		ret = -EIO;
176 		goto unlock;
177 	}
178 
179 	ret = 0;
180 unlock:
181 	mutex_unlock(&pa_dev->doorbell_mutex);
182 
183 	return ret;
184 }
185 EXPORT_SYMBOL_GPL(psp_ring_platform_doorbell);
186 
187 void platform_access_dev_destroy(struct psp_device *psp)
188 {
189 	struct psp_platform_access_device *pa_dev = psp->platform_access_data;
190 
191 	if (!pa_dev)
192 		return;
193 
194 	mutex_destroy(&pa_dev->mailbox_mutex);
195 	mutex_destroy(&pa_dev->doorbell_mutex);
196 	psp->platform_access_data = NULL;
197 }
198 
199 int platform_access_dev_init(struct psp_device *psp)
200 {
201 	struct device *dev = psp->dev;
202 	struct psp_platform_access_device *pa_dev;
203 
204 	pa_dev = devm_kzalloc(dev, sizeof(*pa_dev), GFP_KERNEL);
205 	if (!pa_dev)
206 		return -ENOMEM;
207 
208 	psp->platform_access_data = pa_dev;
209 	pa_dev->psp = psp;
210 	pa_dev->dev = dev;
211 
212 	pa_dev->vdata = (struct platform_access_vdata *)psp->vdata->platform_access;
213 
214 	mutex_init(&pa_dev->mailbox_mutex);
215 	mutex_init(&pa_dev->doorbell_mutex);
216 
217 	dev_dbg(dev, "platform access enabled\n");
218 
219 	return 0;
220 }
221