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 
23 /* Doorbell shouldn't be ringing */
24 static int check_doorbell(u32 __iomem *doorbell)
25 {
26 	u32 tmp;
27 
28 	return readl_poll_timeout(doorbell, tmp, tmp != 0, 0, PSP_CMD_TIMEOUT_US);
29 }
30 
31 /* Recovery field should be equal 0 to start sending commands */
32 static int check_recovery(u32 __iomem *cmd)
33 {
34 	return FIELD_GET(PSP_CMDRESP_RECOVERY, ioread32(cmd));
35 }
36 
37 static int wait_cmd(u32 __iomem *cmd)
38 {
39 	u32 tmp, expected;
40 
41 	/* Expect mbox_cmd to be cleared and ready bit to be set by PSP */
42 	expected = FIELD_PREP(PSP_CMDRESP_RESP, 1);
43 
44 	/*
45 	 * Check for readiness of PSP mailbox in a tight loop in order to
46 	 * process further as soon as command was consumed.
47 	 */
48 	return readl_poll_timeout(cmd, tmp, (tmp & expected), 0,
49 				  PSP_CMD_TIMEOUT_US);
50 }
51 
52 int psp_check_platform_access_status(void)
53 {
54 	struct psp_device *psp = psp_get_master_device();
55 
56 	if (!psp || !psp->platform_access_data)
57 		return -ENODEV;
58 
59 	return 0;
60 }
61 EXPORT_SYMBOL(psp_check_platform_access_status);
62 
63 int psp_send_platform_access_msg(enum psp_platform_access_msg msg,
64 				 struct psp_request *req)
65 {
66 	struct psp_device *psp = psp_get_master_device();
67 	u32 __iomem *cmd, *lo, *hi;
68 	struct psp_platform_access_device *pa_dev;
69 	phys_addr_t req_addr;
70 	u32 cmd_reg;
71 	int ret;
72 
73 	if (!psp || !psp->platform_access_data)
74 		return -ENODEV;
75 
76 	pa_dev = psp->platform_access_data;
77 	cmd = psp->io_regs + pa_dev->vdata->cmdresp_reg;
78 	lo = psp->io_regs + pa_dev->vdata->cmdbuff_addr_lo_reg;
79 	hi = psp->io_regs + pa_dev->vdata->cmdbuff_addr_hi_reg;
80 
81 	mutex_lock(&pa_dev->mailbox_mutex);
82 
83 	if (check_recovery(cmd)) {
84 		dev_dbg(psp->dev, "platform mailbox is in recovery\n");
85 		ret = -EBUSY;
86 		goto unlock;
87 	}
88 
89 	if (wait_cmd(cmd)) {
90 		dev_dbg(psp->dev, "platform mailbox is not done processing command\n");
91 		ret = -EBUSY;
92 		goto unlock;
93 	}
94 
95 	/*
96 	 * Fill mailbox with address of command-response buffer, which will be
97 	 * used for sending i2c requests as well as reading status returned by
98 	 * PSP. Use physical address of buffer, since PSP will map this region.
99 	 */
100 	req_addr = __psp_pa(req);
101 	iowrite32(lower_32_bits(req_addr), lo);
102 	iowrite32(upper_32_bits(req_addr), hi);
103 
104 	print_hex_dump_debug("->psp ", DUMP_PREFIX_OFFSET, 16, 2, req,
105 			     req->header.payload_size, false);
106 
107 	/* Write command register to trigger processing */
108 	cmd_reg = FIELD_PREP(PSP_CMDRESP_CMD, msg);
109 	iowrite32(cmd_reg, cmd);
110 
111 	if (wait_cmd(cmd)) {
112 		ret = -ETIMEDOUT;
113 		goto unlock;
114 	}
115 
116 	/* Ensure it was triggered by this driver */
117 	if (ioread32(lo) != lower_32_bits(req_addr) ||
118 	    ioread32(hi) != upper_32_bits(req_addr)) {
119 		ret = -EBUSY;
120 		goto unlock;
121 	}
122 
123 	/* Store the status in request header for caller to investigate */
124 	cmd_reg = ioread32(cmd);
125 	req->header.status = FIELD_GET(PSP_CMDRESP_STS, cmd_reg);
126 	if (req->header.status) {
127 		ret = -EIO;
128 		goto unlock;
129 	}
130 
131 	print_hex_dump_debug("<-psp ", DUMP_PREFIX_OFFSET, 16, 2, req,
132 			     req->header.payload_size, false);
133 
134 	ret = 0;
135 
136 unlock:
137 	mutex_unlock(&pa_dev->mailbox_mutex);
138 
139 	return ret;
140 }
141 EXPORT_SYMBOL_GPL(psp_send_platform_access_msg);
142 
143 int psp_ring_platform_doorbell(int msg)
144 {
145 	struct psp_device *psp = psp_get_master_device();
146 	struct psp_platform_access_device *pa_dev;
147 	u32 __iomem *button, *cmd;
148 	int ret, val;
149 
150 	if (!psp || !psp->platform_access_data)
151 		return -ENODEV;
152 
153 	pa_dev = psp->platform_access_data;
154 	button = psp->io_regs + pa_dev->vdata->doorbell_button_reg;
155 	cmd = psp->io_regs + pa_dev->vdata->doorbell_cmd_reg;
156 
157 	mutex_lock(&pa_dev->doorbell_mutex);
158 
159 	if (check_doorbell(button)) {
160 		dev_dbg(psp->dev, "doorbell is not ready\n");
161 		ret = -EBUSY;
162 		goto unlock;
163 	}
164 
165 	if (check_recovery(cmd)) {
166 		dev_dbg(psp->dev, "doorbell command in recovery\n");
167 		ret = -EBUSY;
168 		goto unlock;
169 	}
170 
171 	if (wait_cmd(cmd)) {
172 		dev_dbg(psp->dev, "doorbell command not done processing\n");
173 		ret = -EBUSY;
174 		goto unlock;
175 	}
176 
177 	iowrite32(FIELD_PREP(PSP_DRBL_MSG, msg), cmd);
178 	iowrite32(PSP_DRBL_RING, button);
179 
180 	if (wait_cmd(cmd)) {
181 		ret = -ETIMEDOUT;
182 		goto unlock;
183 	}
184 
185 	val = FIELD_GET(PSP_CMDRESP_STS, ioread32(cmd));
186 	if (val) {
187 		ret = -EIO;
188 		goto unlock;
189 	}
190 
191 	ret = 0;
192 unlock:
193 	mutex_unlock(&pa_dev->doorbell_mutex);
194 
195 	return ret;
196 }
197 EXPORT_SYMBOL_GPL(psp_ring_platform_doorbell);
198 
199 void platform_access_dev_destroy(struct psp_device *psp)
200 {
201 	struct psp_platform_access_device *pa_dev = psp->platform_access_data;
202 
203 	if (!pa_dev)
204 		return;
205 
206 	mutex_destroy(&pa_dev->mailbox_mutex);
207 	mutex_destroy(&pa_dev->doorbell_mutex);
208 	psp->platform_access_data = NULL;
209 }
210 
211 int platform_access_dev_init(struct psp_device *psp)
212 {
213 	struct device *dev = psp->dev;
214 	struct psp_platform_access_device *pa_dev;
215 
216 	pa_dev = devm_kzalloc(dev, sizeof(*pa_dev), GFP_KERNEL);
217 	if (!pa_dev)
218 		return -ENOMEM;
219 
220 	psp->platform_access_data = pa_dev;
221 	pa_dev->psp = psp;
222 	pa_dev->dev = dev;
223 
224 	pa_dev->vdata = (struct platform_access_vdata *)psp->vdata->platform_access;
225 
226 	mutex_init(&pa_dev->mailbox_mutex);
227 	mutex_init(&pa_dev->doorbell_mutex);
228 
229 	dev_dbg(dev, "platform access enabled\n");
230 
231 	return 0;
232 }
233