xref: /linux/drivers/misc/mei/pxp/mei_pxp.c (revision be239684b18e1cdcafcf8c7face4a2f562c745ad)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright © 2020 - 2021 Intel Corporation
4  */
5 
6 /**
7  * DOC: MEI_PXP Client Driver
8  *
9  * The mei_pxp driver acts as a translation layer between PXP
10  * protocol  implementer (I915) and ME FW by translating PXP
11  * negotiation messages to ME FW command payloads and vice versa.
12  */
13 
14 #include <linux/delay.h>
15 #include <linux/module.h>
16 #include <linux/slab.h>
17 #include <linux/mei.h>
18 #include <linux/mei_cl_bus.h>
19 #include <linux/component.h>
20 #include <drm/drm_connector.h>
21 #include <drm/i915_component.h>
22 #include <drm/i915_pxp_tee_interface.h>
23 
24 #include "mei_pxp.h"
25 
26 static inline int mei_pxp_reenable(const struct device *dev, struct mei_cl_device *cldev)
27 {
28 	int ret;
29 
30 	dev_warn(dev, "Trying to reset the channel...\n");
31 	ret = mei_cldev_disable(cldev);
32 	if (ret < 0)
33 		dev_warn(dev, "mei_cldev_disable failed. %d\n", ret);
34 	/*
35 	 * Explicitly ignoring disable failure,
36 	 * enable may fix the states and succeed
37 	 */
38 	ret = mei_cldev_enable(cldev);
39 	if (ret < 0)
40 		dev_err(dev, "mei_cldev_enable failed. %d\n", ret);
41 	return ret;
42 }
43 
44 /**
45  * mei_pxp_send_message() - Sends a PXP message to ME FW.
46  * @dev: device corresponding to the mei_cl_device
47  * @message: a message buffer to send
48  * @size: size of the message
49  * @timeout_ms: timeout in milliseconds, zero means wait indefinitely.
50  *
51  * Returns: 0 on Success, <0 on Failure with the following defined failures.
52  *         -ENODEV: Client was not connected.
53  *                  Caller may attempt to try again immediately.
54  *         -ENOMEM: Internal memory allocation failure experienced.
55  *                  Caller may sleep to allow kernel reclaim before retrying.
56  *         -EINTR : Calling thread received a signal. Caller may choose
57  *                  to abandon with the same thread id.
58  *         -ETIME : Request is timed out.
59  *                  Caller may attempt to try again immediately.
60  */
61 static int
62 mei_pxp_send_message(struct device *dev, const void *message, size_t size, unsigned long timeout_ms)
63 {
64 	struct mei_cl_device *cldev;
65 	ssize_t byte;
66 	int ret;
67 
68 	if (!dev || !message)
69 		return -EINVAL;
70 
71 	cldev = to_mei_cl_device(dev);
72 
73 	byte = mei_cldev_send_timeout(cldev, message, size, timeout_ms);
74 	if (byte < 0) {
75 		dev_dbg(dev, "mei_cldev_send failed. %zd\n", byte);
76 		switch (byte) {
77 		case -ENOMEM:
78 			fallthrough;
79 		case -ENODEV:
80 			fallthrough;
81 		case -ETIME:
82 			ret = mei_pxp_reenable(dev, cldev);
83 			if (ret)
84 				byte = ret;
85 			break;
86 		}
87 		return byte;
88 	}
89 
90 	return 0;
91 }
92 
93 /**
94  * mei_pxp_receive_message() - Receives a PXP message from ME FW.
95  * @dev: device corresponding to the mei_cl_device
96  * @buffer: a message buffer to contain the received message
97  * @size: size of the buffer
98  * @timeout_ms: timeout in milliseconds, zero means wait indefinitely.
99  *
100  * Returns: number of bytes send on Success, <0 on Failure with the following defined failures.
101  *         -ENODEV: Client was not connected.
102  *                  Caller may attempt to try again from send immediately.
103  *         -ENOMEM: Internal memory allocation failure experienced.
104  *                  Caller may sleep to allow kernel reclaim before retrying.
105  *         -EINTR : Calling thread received a signal. Caller will need to repeat calling
106  *                  (with a different owning thread) to retrieve existing unclaimed response
107  *                  (and may discard it).
108  *         -ETIME : Request is timed out.
109  *                  Caller may attempt to try again from send immediately.
110  */
111 static int
112 mei_pxp_receive_message(struct device *dev, void *buffer, size_t size, unsigned long timeout_ms)
113 {
114 	struct mei_cl_device *cldev;
115 	ssize_t byte;
116 	bool retry = false;
117 	int ret;
118 
119 	if (!dev || !buffer)
120 		return -EINVAL;
121 
122 	cldev = to_mei_cl_device(dev);
123 
124 retry:
125 	byte = mei_cldev_recv_timeout(cldev, buffer, size, timeout_ms);
126 	if (byte < 0) {
127 		dev_dbg(dev, "mei_cldev_recv failed. %zd\n", byte);
128 		switch (byte) {
129 		case -ENOMEM:
130 			/* Retry the read when pages are reclaimed */
131 			msleep(20);
132 			if (!retry) {
133 				retry = true;
134 				goto retry;
135 			}
136 			fallthrough;
137 		case -ENODEV:
138 			fallthrough;
139 		case -ETIME:
140 			ret = mei_pxp_reenable(dev, cldev);
141 			if (ret)
142 				byte = ret;
143 			break;
144 		}
145 	}
146 
147 	return byte;
148 }
149 
150 /**
151  * mei_pxp_gsc_command() - sends a gsc command, by sending
152  * a sgl mei message to gsc and receiving reply from gsc
153  *
154  * @dev: device corresponding to the mei_cl_device
155  * @client_id: client id to send the command to
156  * @fence_id: fence id to send the command to
157  * @sg_in: scatter gather list containing addresses for rx message buffer
158  * @total_in_len: total length of data in 'in' sg, can be less than the sum of buffers sizes
159  * @sg_out: scatter gather list containing addresses for tx message buffer
160  *
161  * Return: bytes sent on Success, <0 on Failure
162  */
163 static ssize_t mei_pxp_gsc_command(struct device *dev, u8 client_id, u32 fence_id,
164 				   struct scatterlist *sg_in, size_t total_in_len,
165 				   struct scatterlist *sg_out)
166 {
167 	struct mei_cl_device *cldev;
168 
169 	cldev = to_mei_cl_device(dev);
170 
171 	return mei_cldev_send_gsc_command(cldev, client_id, fence_id, sg_in, total_in_len, sg_out);
172 }
173 
174 static const struct i915_pxp_component_ops mei_pxp_ops = {
175 	.owner = THIS_MODULE,
176 	.send = mei_pxp_send_message,
177 	.recv = mei_pxp_receive_message,
178 	.gsc_command = mei_pxp_gsc_command,
179 };
180 
181 static int mei_component_master_bind(struct device *dev)
182 {
183 	struct mei_cl_device *cldev = to_mei_cl_device(dev);
184 	struct i915_pxp_component *comp_master = mei_cldev_get_drvdata(cldev);
185 	int ret;
186 
187 	comp_master->ops = &mei_pxp_ops;
188 	comp_master->tee_dev = dev;
189 	ret = component_bind_all(dev, comp_master);
190 	if (ret < 0)
191 		return ret;
192 
193 	return 0;
194 }
195 
196 static void mei_component_master_unbind(struct device *dev)
197 {
198 	struct mei_cl_device *cldev = to_mei_cl_device(dev);
199 	struct i915_pxp_component *comp_master = mei_cldev_get_drvdata(cldev);
200 
201 	component_unbind_all(dev, comp_master);
202 }
203 
204 static const struct component_master_ops mei_component_master_ops = {
205 	.bind = mei_component_master_bind,
206 	.unbind = mei_component_master_unbind,
207 };
208 
209 /**
210  * mei_pxp_component_match - compare function for matching mei pxp.
211  *
212  *    The function checks if the driver is i915, the subcomponent is PXP
213  *    and the grand parent of pxp and the parent of i915 are the same
214  *    PCH device.
215  *
216  * @dev: master device
217  * @subcomponent: subcomponent to match (I915_COMPONENT_PXP)
218  * @data: compare data (mei pxp device)
219  *
220  * Return:
221  * * 1 - if components match
222  * * 0 - otherwise
223  */
224 static int mei_pxp_component_match(struct device *dev, int subcomponent,
225 				   void *data)
226 {
227 	struct device *base = data;
228 
229 	if (!dev)
230 		return 0;
231 
232 	if (!dev->driver || strcmp(dev->driver->name, "i915") ||
233 	    subcomponent != I915_COMPONENT_PXP)
234 		return 0;
235 
236 	base = base->parent;
237 	if (!base) /* mei device */
238 		return 0;
239 
240 	base = base->parent; /* pci device */
241 	/* for dgfx */
242 	if (base && dev == base)
243 		return 1;
244 
245 	/* for pch */
246 	dev = dev->parent;
247 	return (base && dev && dev == base);
248 }
249 
250 static int mei_pxp_probe(struct mei_cl_device *cldev,
251 			 const struct mei_cl_device_id *id)
252 {
253 	struct i915_pxp_component *comp_master;
254 	struct component_match *master_match;
255 	int ret;
256 
257 	ret = mei_cldev_enable(cldev);
258 	if (ret < 0) {
259 		dev_err(&cldev->dev, "mei_cldev_enable Failed. %d\n", ret);
260 		goto enable_err_exit;
261 	}
262 
263 	comp_master = kzalloc(sizeof(*comp_master), GFP_KERNEL);
264 	if (!comp_master) {
265 		ret = -ENOMEM;
266 		goto err_exit;
267 	}
268 
269 	master_match = NULL;
270 	component_match_add_typed(&cldev->dev, &master_match,
271 				  mei_pxp_component_match, &cldev->dev);
272 	if (IS_ERR_OR_NULL(master_match)) {
273 		ret = -ENOMEM;
274 		goto err_exit;
275 	}
276 
277 	mei_cldev_set_drvdata(cldev, comp_master);
278 	ret = component_master_add_with_match(&cldev->dev,
279 					      &mei_component_master_ops,
280 					      master_match);
281 	if (ret < 0) {
282 		dev_err(&cldev->dev, "Master comp add failed %d\n", ret);
283 		goto err_exit;
284 	}
285 
286 	return 0;
287 
288 err_exit:
289 	mei_cldev_set_drvdata(cldev, NULL);
290 	kfree(comp_master);
291 	mei_cldev_disable(cldev);
292 enable_err_exit:
293 	return ret;
294 }
295 
296 static void mei_pxp_remove(struct mei_cl_device *cldev)
297 {
298 	struct i915_pxp_component *comp_master = mei_cldev_get_drvdata(cldev);
299 	int ret;
300 
301 	component_master_del(&cldev->dev, &mei_component_master_ops);
302 	kfree(comp_master);
303 	mei_cldev_set_drvdata(cldev, NULL);
304 
305 	ret = mei_cldev_disable(cldev);
306 	if (ret)
307 		dev_warn(&cldev->dev, "mei_cldev_disable() failed\n");
308 }
309 
310 /* fbf6fcf1-96cf-4e2e-a6a6-1bab8cbe36b1 : PAVP GUID*/
311 #define MEI_GUID_PXP UUID_LE(0xfbf6fcf1, 0x96cf, 0x4e2e, 0xA6, \
312 			     0xa6, 0x1b, 0xab, 0x8c, 0xbe, 0x36, 0xb1)
313 
314 static struct mei_cl_device_id mei_pxp_tbl[] = {
315 	{ .uuid = MEI_GUID_PXP, .version = MEI_CL_VERSION_ANY },
316 	{ }
317 };
318 MODULE_DEVICE_TABLE(mei, mei_pxp_tbl);
319 
320 static struct mei_cl_driver mei_pxp_driver = {
321 	.id_table = mei_pxp_tbl,
322 	.name = KBUILD_MODNAME,
323 	.probe = mei_pxp_probe,
324 	.remove	= mei_pxp_remove,
325 };
326 
327 module_mei_cl_driver(mei_pxp_driver);
328 
329 MODULE_AUTHOR("Intel Corporation");
330 MODULE_LICENSE("GPL");
331 MODULE_DESCRIPTION("MEI PXP");
332