xref: /openbmc/linux/drivers/misc/mei/bus-fixup.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
19fff0425STomas Winkler // SPDX-License-Identifier: GPL-2.0
2a0a785c4STomas Winkler /*
339e1655cSVitaly Lubart  * Copyright (c) 2013-2023, Intel Corporation. All rights reserved.
4a0a785c4STomas Winkler  * Intel Management Engine Interface (Intel MEI) Linux driver
5a0a785c4STomas Winkler  */
6a0a785c4STomas Winkler 
7a0a785c4STomas Winkler #include <linux/kernel.h>
8a0a785c4STomas Winkler #include <linux/sched.h>
9a0a785c4STomas Winkler #include <linux/module.h>
10a0a785c4STomas Winkler #include <linux/device.h>
11a0a785c4STomas Winkler #include <linux/slab.h>
12a0a785c4STomas Winkler 
131fb1ea0dSAndy Shevchenko #include <linux/mei.h>
14a0a785c4STomas Winkler #include <linux/mei_cl_bus.h>
15a0a785c4STomas Winkler 
16a0a785c4STomas Winkler #include "mei_dev.h"
17a0a785c4STomas Winkler #include "client.h"
187d88a258SVitaly Lubart #include "mkhi.h"
19a0a785c4STomas Winkler 
205b6dca29STomas Winkler #define MEI_UUID_NFC_INFO UUID_LE(0xd2de1625, 0x382d, 0x417d, \
215b6dca29STomas Winkler 			0x48, 0xa4, 0xef, 0xab, 0xba, 0x8a, 0x12, 0x06)
225b6dca29STomas Winkler 
236009595aSTomas Winkler static const uuid_le mei_nfc_info_guid = MEI_UUID_NFC_INFO;
246009595aSTomas Winkler 
256009595aSTomas Winkler #define MEI_UUID_NFC_HCI UUID_LE(0x0bb17a78, 0x2a8e, 0x4c50, \
266009595aSTomas Winkler 			0x94, 0xd4, 0x50, 0x26, 0x67, 0x23, 0x77, 0x5c)
276009595aSTomas Winkler 
28e97cdb30STomas Winkler #define MEI_UUID_WD UUID_LE(0x05B79A6F, 0x4628, 0x4D7F, \
29e97cdb30STomas Winkler 			    0x89, 0x9D, 0xA9, 0x15, 0x14, 0xCB, 0x32, 0xAB)
30e97cdb30STomas Winkler 
317279b238SAlexander Usyskin #define MEI_UUID_MKHIF_FIX UUID_LE(0x55213584, 0x9a29, 0x4916, \
327279b238SAlexander Usyskin 			0xba, 0xdf, 0xf, 0xb7, 0xed, 0x68, 0x2a, 0xeb)
337279b238SAlexander Usyskin 
341bc22fc5SAlexander Usyskin #define MEI_UUID_IGSC_MKHI UUID_LE(0xE2C2AFA2, 0x3817, 0x4D19, \
351bc22fc5SAlexander Usyskin 			0x9D, 0x95, 0x06, 0xB1, 0x6B, 0x58, 0x8A, 0x5D)
361bc22fc5SAlexander Usyskin 
371bc22fc5SAlexander Usyskin #define MEI_UUID_IGSC_MKHI_FIX UUID_LE(0x46E0C1FB, 0xA546, 0x414F, \
381bc22fc5SAlexander Usyskin 			0x91, 0x70, 0xB7, 0xF4, 0x6D, 0x57, 0xB4, 0xAD)
391bc22fc5SAlexander Usyskin 
40d1e204e8STomas Winkler #define MEI_UUID_HDCP UUID_LE(0xB638AB7E, 0x94E2, 0x4EA2, \
41d1e204e8STomas Winkler 			      0xA5, 0x52, 0xD1, 0xC5, 0x4B, 0x62, 0x7F, 0x04)
42d1e204e8STomas Winkler 
43ee64ed81STomas Winkler #define MEI_UUID_PAVP UUID_LE(0xfbf6fcf1, 0x96cf, 0x4e2e, 0xA6, \
44ee64ed81STomas Winkler 			      0xa6, 0x1b, 0xab, 0x8c, 0xbe, 0x36, 0xb1)
45ee64ed81STomas Winkler 
4671ce7891STomas Winkler #define MEI_UUID_ANY NULL_UUID_LE
4771ce7891STomas Winkler 
485b6dca29STomas Winkler /**
49dd070a16STomas Winkler  * number_of_connections - determine whether an client be on the bus
50dd070a16STomas Winkler  *    according number of connections
51dd070a16STomas Winkler  *    We support only clients:
52dd070a16STomas Winkler  *       1. with single connection
53dd070a16STomas Winkler  *       2. and fixed clients (max_number_of_connections == 0)
54dd070a16STomas Winkler  *
55dd070a16STomas Winkler  * @cldev: me clients device
56dd070a16STomas Winkler  */
number_of_connections(struct mei_cl_device * cldev)57dd070a16STomas Winkler static void number_of_connections(struct mei_cl_device *cldev)
58dd070a16STomas Winkler {
59dd070a16STomas Winkler 	if (cldev->me_cl->props.max_number_of_connections > 1)
60dd070a16STomas Winkler 		cldev->do_match = 0;
61dd070a16STomas Winkler }
62dd070a16STomas Winkler 
63dd070a16STomas Winkler /**
645b6dca29STomas Winkler  * blacklist - blacklist a client from the bus
655b6dca29STomas Winkler  *
665b6dca29STomas Winkler  * @cldev: me clients device
675b6dca29STomas Winkler  */
blacklist(struct mei_cl_device * cldev)685b6dca29STomas Winkler static void blacklist(struct mei_cl_device *cldev)
695b6dca29STomas Winkler {
705b6dca29STomas Winkler 	cldev->do_match = 0;
715b6dca29STomas Winkler }
725b6dca29STomas Winkler 
73d1e204e8STomas Winkler /**
74d1e204e8STomas Winkler  * whitelist - forcefully whitelist client
75d1e204e8STomas Winkler  *
76d1e204e8STomas Winkler  * @cldev: me clients device
77d1e204e8STomas Winkler  */
whitelist(struct mei_cl_device * cldev)78d1e204e8STomas Winkler static void whitelist(struct mei_cl_device *cldev)
79d1e204e8STomas Winkler {
80d1e204e8STomas Winkler 	cldev->do_match = 1;
81d1e204e8STomas Winkler }
82d1e204e8STomas Winkler 
837279b238SAlexander Usyskin #define OSTYPE_LINUX    2
847279b238SAlexander Usyskin struct mei_os_ver {
857279b238SAlexander Usyskin 	__le16 build;
867279b238SAlexander Usyskin 	__le16 reserved1;
877279b238SAlexander Usyskin 	u8  os_type;
887279b238SAlexander Usyskin 	u8  major;
897279b238SAlexander Usyskin 	u8  minor;
907279b238SAlexander Usyskin 	u8  reserved2;
917279b238SAlexander Usyskin } __packed;
927279b238SAlexander Usyskin 
933cfaeb33SAlexander Usyskin struct mkhi_fw_ver_block {
943cfaeb33SAlexander Usyskin 	u16 minor;
953cfaeb33SAlexander Usyskin 	u8 major;
963cfaeb33SAlexander Usyskin 	u8 platform;
973cfaeb33SAlexander Usyskin 	u16 buildno;
983cfaeb33SAlexander Usyskin 	u16 hotfix;
993cfaeb33SAlexander Usyskin } __packed;
1003cfaeb33SAlexander Usyskin 
1013cfaeb33SAlexander Usyskin struct mkhi_fw_ver {
1023cfaeb33SAlexander Usyskin 	struct mkhi_fw_ver_block ver[MEI_MAX_FW_VER_BLOCKS];
1033cfaeb33SAlexander Usyskin } __packed;
1043cfaeb33SAlexander Usyskin 
105dd04eeccSTomas Winkler #define MKHI_OSVER_BUF_LEN (sizeof(struct mkhi_msg_hdr) + \
106dd04eeccSTomas Winkler 			    sizeof(struct mkhi_fwcaps) + \
107dd04eeccSTomas Winkler 			    sizeof(struct mei_os_ver))
mei_osver(struct mei_cl_device * cldev)1087279b238SAlexander Usyskin static int mei_osver(struct mei_cl_device *cldev)
1097279b238SAlexander Usyskin {
110dd04eeccSTomas Winkler 	const size_t size = MKHI_OSVER_BUF_LEN;
1110f2c1f80SAlexander Usyskin 	u8 buf[MKHI_OSVER_BUF_LEN];
1127279b238SAlexander Usyskin 	struct mkhi_msg *req;
1137279b238SAlexander Usyskin 	struct mkhi_fwcaps *fwcaps;
1147279b238SAlexander Usyskin 	struct mei_os_ver *os_ver;
1157279b238SAlexander Usyskin 	unsigned int mode = MEI_CL_IO_TX_BLOCKING | MEI_CL_IO_TX_INTERNAL;
1167279b238SAlexander Usyskin 
1177279b238SAlexander Usyskin 	memset(buf, 0, size);
1187279b238SAlexander Usyskin 
1197279b238SAlexander Usyskin 	req = (struct mkhi_msg *)buf;
1207279b238SAlexander Usyskin 	req->hdr.group_id = MKHI_FWCAPS_GROUP_ID;
1217279b238SAlexander Usyskin 	req->hdr.command = MKHI_FWCAPS_SET_OS_VER_APP_RULE_CMD;
1227279b238SAlexander Usyskin 
1237279b238SAlexander Usyskin 	fwcaps = (struct mkhi_fwcaps *)req->data;
1247279b238SAlexander Usyskin 
1257279b238SAlexander Usyskin 	fwcaps->id.rule_type = 0x0;
1267279b238SAlexander Usyskin 	fwcaps->id.feature_id = MKHI_FEATURE_PTT;
1277279b238SAlexander Usyskin 	fwcaps->len = sizeof(*os_ver);
1287279b238SAlexander Usyskin 	os_ver = (struct mei_os_ver *)fwcaps->data;
1297279b238SAlexander Usyskin 	os_ver->os_type = OSTYPE_LINUX;
1307279b238SAlexander Usyskin 
13185261c1fSAlexander Usyskin 	return __mei_cl_send(cldev->cl, buf, size, 0, mode);
1327279b238SAlexander Usyskin }
1337279b238SAlexander Usyskin 
1343cfaeb33SAlexander Usyskin #define MKHI_FWVER_BUF_LEN (sizeof(struct mkhi_msg_hdr) + \
1353cfaeb33SAlexander Usyskin 			    sizeof(struct mkhi_fw_ver))
1363cfaeb33SAlexander Usyskin #define MKHI_FWVER_LEN(__num) (sizeof(struct mkhi_msg_hdr) + \
1373cfaeb33SAlexander Usyskin 			       sizeof(struct mkhi_fw_ver_block) * (__num))
mei_fwver(struct mei_cl_device * cldev)1383cfaeb33SAlexander Usyskin static int mei_fwver(struct mei_cl_device *cldev)
1393cfaeb33SAlexander Usyskin {
1400f2c1f80SAlexander Usyskin 	u8 buf[MKHI_FWVER_BUF_LEN];
14171ae5255STomas Winkler 	struct mkhi_msg req;
14271ae5255STomas Winkler 	struct mkhi_msg *rsp;
1433cfaeb33SAlexander Usyskin 	struct mkhi_fw_ver *fwver;
1443cfaeb33SAlexander Usyskin 	int bytes_recv, ret, i;
1453cfaeb33SAlexander Usyskin 
1463cfaeb33SAlexander Usyskin 	memset(buf, 0, sizeof(buf));
1473cfaeb33SAlexander Usyskin 
14871ae5255STomas Winkler 	req.hdr.group_id = MKHI_GEN_GROUP_ID;
14971ae5255STomas Winkler 	req.hdr.command = MKHI_GEN_GET_FW_VERSION_CMD;
1503cfaeb33SAlexander Usyskin 
15185261c1fSAlexander Usyskin 	ret = __mei_cl_send(cldev->cl, (u8 *)&req, sizeof(req), 0,
1523cfaeb33SAlexander Usyskin 			    MEI_CL_IO_TX_BLOCKING);
1533cfaeb33SAlexander Usyskin 	if (ret < 0) {
15439e1655cSVitaly Lubart 		dev_info(&cldev->dev, "Could not send ReqFWVersion cmd ret = %d\n", ret);
1553cfaeb33SAlexander Usyskin 		return ret;
1563cfaeb33SAlexander Usyskin 	}
1573cfaeb33SAlexander Usyskin 
1583cfaeb33SAlexander Usyskin 	ret = 0;
15985261c1fSAlexander Usyskin 	bytes_recv = __mei_cl_recv(cldev->cl, buf, sizeof(buf), NULL, 0,
16095953618SAlexander Usyskin 				   cldev->bus->timeouts.mkhi_recv);
1617f3838fbSTomas Winkler 	if (bytes_recv < 0 || (size_t)bytes_recv < MKHI_FWVER_LEN(1)) {
1623cfaeb33SAlexander Usyskin 		/*
1633cfaeb33SAlexander Usyskin 		 * Should be at least one version block,
1643cfaeb33SAlexander Usyskin 		 * error out if nothing found
1653cfaeb33SAlexander Usyskin 		 */
16639e1655cSVitaly Lubart 		dev_info(&cldev->dev, "Could not read FW version ret = %d\n", bytes_recv);
1673cfaeb33SAlexander Usyskin 		return -EIO;
1683cfaeb33SAlexander Usyskin 	}
1693cfaeb33SAlexander Usyskin 
17071ae5255STomas Winkler 	rsp = (struct mkhi_msg *)buf;
17171ae5255STomas Winkler 	fwver = (struct mkhi_fw_ver *)rsp->data;
1723cfaeb33SAlexander Usyskin 	memset(cldev->bus->fw_ver, 0, sizeof(cldev->bus->fw_ver));
1733cfaeb33SAlexander Usyskin 	for (i = 0; i < MEI_MAX_FW_VER_BLOCKS; i++) {
1747f3838fbSTomas Winkler 		if ((size_t)bytes_recv < MKHI_FWVER_LEN(i + 1))
1753cfaeb33SAlexander Usyskin 			break;
1763cfaeb33SAlexander Usyskin 		dev_dbg(&cldev->dev, "FW version%d %d:%d.%d.%d.%d\n",
1773cfaeb33SAlexander Usyskin 			i, fwver->ver[i].platform,
1783cfaeb33SAlexander Usyskin 			fwver->ver[i].major, fwver->ver[i].minor,
1793cfaeb33SAlexander Usyskin 			fwver->ver[i].hotfix, fwver->ver[i].buildno);
1803cfaeb33SAlexander Usyskin 
1813cfaeb33SAlexander Usyskin 		cldev->bus->fw_ver[i].platform = fwver->ver[i].platform;
1823cfaeb33SAlexander Usyskin 		cldev->bus->fw_ver[i].major = fwver->ver[i].major;
1833cfaeb33SAlexander Usyskin 		cldev->bus->fw_ver[i].minor = fwver->ver[i].minor;
1843cfaeb33SAlexander Usyskin 		cldev->bus->fw_ver[i].hotfix = fwver->ver[i].hotfix;
1853cfaeb33SAlexander Usyskin 		cldev->bus->fw_ver[i].buildno = fwver->ver[i].buildno;
1863cfaeb33SAlexander Usyskin 	}
187*6549b2b7SAlexander Usyskin 	cldev->bus->fw_ver_received = 1;
1883cfaeb33SAlexander Usyskin 
1893cfaeb33SAlexander Usyskin 	return ret;
1903cfaeb33SAlexander Usyskin }
1913cfaeb33SAlexander Usyskin 
19283f47eeaSAlexander Usyskin #define GFX_MEMORY_READY_TIMEOUT 200 /* timeout in milliseconds */
19383f47eeaSAlexander Usyskin 
mei_gfx_memory_ready(struct mei_cl_device * cldev)194342e4c7eSTomas Winkler static int mei_gfx_memory_ready(struct mei_cl_device *cldev)
195342e4c7eSTomas Winkler {
196342e4c7eSTomas Winkler 	struct mkhi_gfx_mem_ready req = {0};
19783f47eeaSAlexander Usyskin 	unsigned int mode = MEI_CL_IO_TX_INTERNAL | MEI_CL_IO_TX_BLOCKING;
198342e4c7eSTomas Winkler 
199342e4c7eSTomas Winkler 	req.hdr.group_id = MKHI_GROUP_ID_GFX;
200342e4c7eSTomas Winkler 	req.hdr.command = MKHI_GFX_MEMORY_READY_CMD_REQ;
201342e4c7eSTomas Winkler 	req.flags = MKHI_GFX_MEM_READY_PXP_ALLOWED;
202342e4c7eSTomas Winkler 
203342e4c7eSTomas Winkler 	dev_dbg(&cldev->dev, "Sending memory ready command\n");
20483f47eeaSAlexander Usyskin 	return __mei_cl_send_timeout(cldev->cl, (u8 *)&req, sizeof(req), 0,
20583f47eeaSAlexander Usyskin 				     mode, GFX_MEMORY_READY_TIMEOUT);
206342e4c7eSTomas Winkler }
207342e4c7eSTomas Winkler 
mei_mkhi_fix(struct mei_cl_device * cldev)2087279b238SAlexander Usyskin static void mei_mkhi_fix(struct mei_cl_device *cldev)
2097279b238SAlexander Usyskin {
2107279b238SAlexander Usyskin 	int ret;
2117279b238SAlexander Usyskin 
212f8204f0dSAlexander Usyskin 	/* No need to enable the client if nothing is needed from it */
213f8204f0dSAlexander Usyskin 	if (!cldev->bus->fw_f_fw_ver_supported &&
214f8204f0dSAlexander Usyskin 	    !cldev->bus->hbm_f_os_supported)
215f8204f0dSAlexander Usyskin 		return;
216f8204f0dSAlexander Usyskin 
2177279b238SAlexander Usyskin 	ret = mei_cldev_enable(cldev);
2187279b238SAlexander Usyskin 	if (ret)
2197279b238SAlexander Usyskin 		return;
2207279b238SAlexander Usyskin 
221f8204f0dSAlexander Usyskin 	if (cldev->bus->fw_f_fw_ver_supported) {
2223cfaeb33SAlexander Usyskin 		ret = mei_fwver(cldev);
2233cfaeb33SAlexander Usyskin 		if (ret < 0)
22439e1655cSVitaly Lubart 			dev_info(&cldev->dev, "FW version command failed %d\n",
225f8204f0dSAlexander Usyskin 				 ret);
226f8204f0dSAlexander Usyskin 	}
2273cfaeb33SAlexander Usyskin 
2283cfaeb33SAlexander Usyskin 	if (cldev->bus->hbm_f_os_supported) {
2297279b238SAlexander Usyskin 		ret = mei_osver(cldev);
230c6240cacSAlexander Usyskin 		if (ret < 0)
23139e1655cSVitaly Lubart 			dev_info(&cldev->dev, "OS version command failed %d\n",
2323cfaeb33SAlexander Usyskin 				 ret);
2333cfaeb33SAlexander Usyskin 	}
2347279b238SAlexander Usyskin 	mei_cldev_disable(cldev);
2357279b238SAlexander Usyskin }
2367279b238SAlexander Usyskin 
mei_gsc_mkhi_ver(struct mei_cl_device * cldev)2371bc22fc5SAlexander Usyskin static void mei_gsc_mkhi_ver(struct mei_cl_device *cldev)
2381bc22fc5SAlexander Usyskin {
2391bc22fc5SAlexander Usyskin 	int ret;
2401bc22fc5SAlexander Usyskin 
241*6549b2b7SAlexander Usyskin 	/*
242*6549b2b7SAlexander Usyskin 	 * No need to enable the client if nothing is needed from it.
243*6549b2b7SAlexander Usyskin 	 * No need to fill in version if it is already filled in by the fix address client.
244*6549b2b7SAlexander Usyskin 	 */
245*6549b2b7SAlexander Usyskin 	if (!cldev->bus->fw_f_fw_ver_supported || cldev->bus->fw_ver_received)
2461bc22fc5SAlexander Usyskin 		return;
2471bc22fc5SAlexander Usyskin 
2481bc22fc5SAlexander Usyskin 	ret = mei_cldev_enable(cldev);
2491bc22fc5SAlexander Usyskin 	if (ret)
2501bc22fc5SAlexander Usyskin 		return;
2511bc22fc5SAlexander Usyskin 
2521bc22fc5SAlexander Usyskin 	ret = mei_fwver(cldev);
2531bc22fc5SAlexander Usyskin 	if (ret < 0)
25439e1655cSVitaly Lubart 		dev_info(&cldev->dev, "FW version command failed %d\n", ret);
2551bc22fc5SAlexander Usyskin 	mei_cldev_disable(cldev);
2561bc22fc5SAlexander Usyskin }
257342e4c7eSTomas Winkler 
mei_gsc_mkhi_fix_ver(struct mei_cl_device * cldev)258342e4c7eSTomas Winkler static void mei_gsc_mkhi_fix_ver(struct mei_cl_device *cldev)
259342e4c7eSTomas Winkler {
260342e4c7eSTomas Winkler 	int ret;
261342e4c7eSTomas Winkler 
262342e4c7eSTomas Winkler 	/* No need to enable the client if nothing is needed from it */
263342e4c7eSTomas Winkler 	if (!cldev->bus->fw_f_fw_ver_supported &&
264342e4c7eSTomas Winkler 	    cldev->bus->pxp_mode != MEI_DEV_PXP_INIT)
265342e4c7eSTomas Winkler 		return;
266342e4c7eSTomas Winkler 
267342e4c7eSTomas Winkler 	ret = mei_cldev_enable(cldev);
268342e4c7eSTomas Winkler 	if (ret)
269342e4c7eSTomas Winkler 		return;
270342e4c7eSTomas Winkler 
271342e4c7eSTomas Winkler 	if (cldev->bus->pxp_mode == MEI_DEV_PXP_INIT) {
272342e4c7eSTomas Winkler 		ret = mei_gfx_memory_ready(cldev);
2730ef77698SAlexander Usyskin 		if (ret < 0) {
274342e4c7eSTomas Winkler 			dev_err(&cldev->dev, "memory ready command failed %d\n", ret);
2750ef77698SAlexander Usyskin 		} else {
276342e4c7eSTomas Winkler 			dev_dbg(&cldev->dev, "memory ready command sent\n");
277342e4c7eSTomas Winkler 			cldev->bus->pxp_mode = MEI_DEV_PXP_SETUP;
2780ef77698SAlexander Usyskin 		}
2790ef77698SAlexander Usyskin 		/* we go to reset after that */
280342e4c7eSTomas Winkler 		goto out;
281342e4c7eSTomas Winkler 	}
282342e4c7eSTomas Winkler 
283342e4c7eSTomas Winkler 	ret = mei_fwver(cldev);
284342e4c7eSTomas Winkler 	if (ret < 0)
28539e1655cSVitaly Lubart 		dev_info(&cldev->dev, "FW version command failed %d\n",
286342e4c7eSTomas Winkler 			 ret);
287342e4c7eSTomas Winkler out:
288342e4c7eSTomas Winkler 	mei_cldev_disable(cldev);
289342e4c7eSTomas Winkler }
290342e4c7eSTomas Winkler 
291e97cdb30STomas Winkler /**
292e97cdb30STomas Winkler  * mei_wd - wd client on the bus, change protocol version
293e97cdb30STomas Winkler  *   as the API has changed.
294e97cdb30STomas Winkler  *
295e97cdb30STomas Winkler  * @cldev: me clients device
296e97cdb30STomas Winkler  */
297e97cdb30STomas Winkler #if IS_ENABLED(CONFIG_INTEL_MEI_ME)
298e97cdb30STomas Winkler #include <linux/pci.h>
299e97cdb30STomas Winkler #include "hw-me-regs.h"
mei_wd(struct mei_cl_device * cldev)300e97cdb30STomas Winkler static void mei_wd(struct mei_cl_device *cldev)
301e97cdb30STomas Winkler {
302e97cdb30STomas Winkler 	struct pci_dev *pdev = to_pci_dev(cldev->dev.parent);
303e97cdb30STomas Winkler 
304e97cdb30STomas Winkler 	if (pdev->device == MEI_DEV_ID_WPT_LP ||
305e97cdb30STomas Winkler 	    pdev->device == MEI_DEV_ID_SPT ||
306e97cdb30STomas Winkler 	    pdev->device == MEI_DEV_ID_SPT_H)
307e97cdb30STomas Winkler 		cldev->me_cl->props.protocol_version = 0x2;
308e97cdb30STomas Winkler 
309e97cdb30STomas Winkler 	cldev->do_match = 1;
310e97cdb30STomas Winkler }
311e97cdb30STomas Winkler #else
mei_wd(struct mei_cl_device * cldev)312e97cdb30STomas Winkler static inline void mei_wd(struct mei_cl_device *cldev) {}
313e97cdb30STomas Winkler #endif /* CONFIG_INTEL_MEI_ME */
314e97cdb30STomas Winkler 
315a0a785c4STomas Winkler struct mei_nfc_cmd {
316a0a785c4STomas Winkler 	u8 command;
317a0a785c4STomas Winkler 	u8 status;
318a0a785c4STomas Winkler 	u16 req_id;
319a0a785c4STomas Winkler 	u32 reserved;
320a0a785c4STomas Winkler 	u16 data_size;
321a0a785c4STomas Winkler 	u8 sub_command;
322a0a785c4STomas Winkler 	u8 data[];
323a0a785c4STomas Winkler } __packed;
324a0a785c4STomas Winkler 
325a0a785c4STomas Winkler struct mei_nfc_reply {
326a0a785c4STomas Winkler 	u8 command;
327a0a785c4STomas Winkler 	u8 status;
328a0a785c4STomas Winkler 	u16 req_id;
329a0a785c4STomas Winkler 	u32 reserved;
330a0a785c4STomas Winkler 	u16 data_size;
331a0a785c4STomas Winkler 	u8 sub_command;
332a0a785c4STomas Winkler 	u8 reply_status;
333a0a785c4STomas Winkler 	u8 data[];
334a0a785c4STomas Winkler } __packed;
335a0a785c4STomas Winkler 
336a0a785c4STomas Winkler struct mei_nfc_if_version {
337a0a785c4STomas Winkler 	u8 radio_version_sw[3];
338a0a785c4STomas Winkler 	u8 reserved[3];
339a0a785c4STomas Winkler 	u8 radio_version_hw[3];
340a0a785c4STomas Winkler 	u8 i2c_addr;
341a0a785c4STomas Winkler 	u8 fw_ivn;
342a0a785c4STomas Winkler 	u8 vendor_id;
343a0a785c4STomas Winkler 	u8 radio_type;
344a0a785c4STomas Winkler } __packed;
345a0a785c4STomas Winkler 
346a0a785c4STomas Winkler 
347a0a785c4STomas Winkler #define MEI_NFC_CMD_MAINTENANCE 0x00
348a0a785c4STomas Winkler #define MEI_NFC_SUBCMD_IF_VERSION 0x01
349a0a785c4STomas Winkler 
350a0a785c4STomas Winkler /* Vendors */
351a0a785c4STomas Winkler #define MEI_NFC_VENDOR_INSIDE 0x00
352a0a785c4STomas Winkler #define MEI_NFC_VENDOR_NXP    0x01
353a0a785c4STomas Winkler 
354a0a785c4STomas Winkler /* Radio types */
355a0a785c4STomas Winkler #define MEI_NFC_VENDOR_INSIDE_UREAD 0x00
356a0a785c4STomas Winkler #define MEI_NFC_VENDOR_NXP_PN544    0x01
357a0a785c4STomas Winkler 
358b39910c2STomas Winkler /**
359b39910c2STomas Winkler  * mei_nfc_if_version - get NFC interface version
360b39910c2STomas Winkler  *
361b39910c2STomas Winkler  * @cl: host client (nfc info)
362b39910c2STomas Winkler  * @ver: NFC interface version to be filled in
363b39910c2STomas Winkler  *
364b39910c2STomas Winkler  * Return: 0 on success; < 0 otherwise
365b39910c2STomas Winkler  */
mei_nfc_if_version(struct mei_cl * cl,struct mei_nfc_if_version * ver)366b39910c2STomas Winkler static int mei_nfc_if_version(struct mei_cl *cl,
367b39910c2STomas Winkler 			      struct mei_nfc_if_version *ver)
368a0a785c4STomas Winkler {
369a0a785c4STomas Winkler 	struct mei_device *bus;
370b39910c2STomas Winkler 	struct mei_nfc_cmd cmd = {
371b39910c2STomas Winkler 		.command = MEI_NFC_CMD_MAINTENANCE,
372b39910c2STomas Winkler 		.data_size = 1,
373b39910c2STomas Winkler 		.sub_command = MEI_NFC_SUBCMD_IF_VERSION,
374b39910c2STomas Winkler 	};
375a0a785c4STomas Winkler 	struct mei_nfc_reply *reply = NULL;
376a0a785c4STomas Winkler 	size_t if_version_length;
37785261c1fSAlexander Usyskin 	u8 vtag;
378a0a785c4STomas Winkler 	int bytes_recv, ret;
379a0a785c4STomas Winkler 
380a0a785c4STomas Winkler 	bus = cl->dev;
381a0a785c4STomas Winkler 
382b39910c2STomas Winkler 	WARN_ON(mutex_is_locked(&bus->device_lock));
383a0a785c4STomas Winkler 
38485261c1fSAlexander Usyskin 	ret = __mei_cl_send(cl, (u8 *)&cmd, sizeof(cmd), 0,
38585261c1fSAlexander Usyskin 			    MEI_CL_IO_TX_BLOCKING);
386a0a785c4STomas Winkler 	if (ret < 0) {
3874b8659e2SAlexander Usyskin 		dev_err(bus->dev, "Could not send IF version cmd ret = %d\n", ret);
388a0a785c4STomas Winkler 		return ret;
389a0a785c4STomas Winkler 	}
390a0a785c4STomas Winkler 
391a0a785c4STomas Winkler 	/* to be sure on the stack we alloc memory */
39271ae5255STomas Winkler 	if_version_length = sizeof(*reply) + sizeof(*ver);
393a0a785c4STomas Winkler 
394a0a785c4STomas Winkler 	reply = kzalloc(if_version_length, GFP_KERNEL);
395a0a785c4STomas Winkler 	if (!reply)
396a0a785c4STomas Winkler 		return -ENOMEM;
397a0a785c4STomas Winkler 
398b39910c2STomas Winkler 	ret = 0;
39985261c1fSAlexander Usyskin 	bytes_recv = __mei_cl_recv(cl, (u8 *)reply, if_version_length, &vtag,
40085261c1fSAlexander Usyskin 				   0, 0);
4017f3838fbSTomas Winkler 	if (bytes_recv < 0 || (size_t)bytes_recv < if_version_length) {
4024b8659e2SAlexander Usyskin 		dev_err(bus->dev, "Could not read IF version ret = %d\n", bytes_recv);
403a0a785c4STomas Winkler 		ret = -EIO;
404a0a785c4STomas Winkler 		goto err;
405a0a785c4STomas Winkler 	}
406a0a785c4STomas Winkler 
40771ae5255STomas Winkler 	memcpy(ver, reply->data, sizeof(*ver));
408a0a785c4STomas Winkler 
409b39910c2STomas Winkler 	dev_info(bus->dev, "NFC MEI VERSION: IVN 0x%x Vendor ID 0x%x Type 0x%x\n",
410b39910c2STomas Winkler 		 ver->fw_ivn, ver->vendor_id, ver->radio_type);
411a0a785c4STomas Winkler 
412a0a785c4STomas Winkler err:
413a0a785c4STomas Winkler 	kfree(reply);
414a0a785c4STomas Winkler 	return ret;
415a0a785c4STomas Winkler }
416a0a785c4STomas Winkler 
417b39910c2STomas Winkler /**
418b39910c2STomas Winkler  * mei_nfc_radio_name - derive nfc radio name from the interface version
419b39910c2STomas Winkler  *
420b39910c2STomas Winkler  * @ver: NFC radio version
421b39910c2STomas Winkler  *
422b39910c2STomas Winkler  * Return: radio name string
423b39910c2STomas Winkler  */
mei_nfc_radio_name(struct mei_nfc_if_version * ver)424b39910c2STomas Winkler static const char *mei_nfc_radio_name(struct mei_nfc_if_version *ver)
425b39910c2STomas Winkler {
426b39910c2STomas Winkler 
427b39910c2STomas Winkler 	if (ver->vendor_id == MEI_NFC_VENDOR_INSIDE) {
428b39910c2STomas Winkler 		if (ver->radio_type == MEI_NFC_VENDOR_INSIDE_UREAD)
429b39910c2STomas Winkler 			return "microread";
430b39910c2STomas Winkler 	}
431b39910c2STomas Winkler 
432b39910c2STomas Winkler 	if (ver->vendor_id == MEI_NFC_VENDOR_NXP) {
433b39910c2STomas Winkler 		if (ver->radio_type == MEI_NFC_VENDOR_NXP_PN544)
434b39910c2STomas Winkler 			return "pn544";
435b39910c2STomas Winkler 	}
436b39910c2STomas Winkler 
437b39910c2STomas Winkler 	return NULL;
438b39910c2STomas Winkler }
439b39910c2STomas Winkler 
4406009595aSTomas Winkler /**
4416009595aSTomas Winkler  * mei_nfc - The nfc fixup function. The function retrieves nfc radio
4426009595aSTomas Winkler  *    name and set is as device attribute so we can load
4436009595aSTomas Winkler  *    the proper device driver for it
4446009595aSTomas Winkler  *
4456009595aSTomas Winkler  * @cldev: me client device (nfc)
4466009595aSTomas Winkler  */
mei_nfc(struct mei_cl_device * cldev)4476009595aSTomas Winkler static void mei_nfc(struct mei_cl_device *cldev)
448a0a785c4STomas Winkler {
449a0a785c4STomas Winkler 	struct mei_device *bus;
4506009595aSTomas Winkler 	struct mei_cl *cl;
4516009595aSTomas Winkler 	struct mei_me_client *me_cl = NULL;
4526009595aSTomas Winkler 	struct mei_nfc_if_version ver;
4536009595aSTomas Winkler 	const char *radio_name = NULL;
454a0a785c4STomas Winkler 	int ret;
455a0a785c4STomas Winkler 
4566009595aSTomas Winkler 	bus = cldev->bus;
457a0a785c4STomas Winkler 
4586009595aSTomas Winkler 	mutex_lock(&bus->device_lock);
4596009595aSTomas Winkler 	/* we need to connect to INFO GUID */
4607851e008SAlexander Usyskin 	cl = mei_cl_alloc_linked(bus);
461a0a785c4STomas Winkler 	if (IS_ERR(cl)) {
462a0a785c4STomas Winkler 		ret = PTR_ERR(cl);
4636009595aSTomas Winkler 		cl = NULL;
4646009595aSTomas Winkler 		dev_err(bus->dev, "nfc hook alloc failed %d\n", ret);
4656009595aSTomas Winkler 		goto out;
466a0a785c4STomas Winkler 	}
467a0a785c4STomas Winkler 
4686009595aSTomas Winkler 	me_cl = mei_me_cl_by_uuid(bus, &mei_nfc_info_guid);
4696009595aSTomas Winkler 	if (!me_cl) {
4706009595aSTomas Winkler 		ret = -ENOTTY;
4716009595aSTomas Winkler 		dev_err(bus->dev, "Cannot find nfc info %d\n", ret);
4726009595aSTomas Winkler 		goto out;
473a0a785c4STomas Winkler 	}
474a0a785c4STomas Winkler 
4756009595aSTomas Winkler 	ret = mei_cl_connect(cl, me_cl, NULL);
4766009595aSTomas Winkler 	if (ret < 0) {
4776009595aSTomas Winkler 		dev_err(&cldev->dev, "Can't connect to the NFC INFO ME ret = %d\n",
4786009595aSTomas Winkler 			ret);
4796009595aSTomas Winkler 		goto out;
4806009595aSTomas Winkler 	}
481a0a785c4STomas Winkler 
482a0a785c4STomas Winkler 	mutex_unlock(&bus->device_lock);
4836009595aSTomas Winkler 
4846009595aSTomas Winkler 	ret = mei_nfc_if_version(cl, &ver);
4856009595aSTomas Winkler 	if (ret)
4866009595aSTomas Winkler 		goto disconnect;
4876009595aSTomas Winkler 
4886009595aSTomas Winkler 	radio_name = mei_nfc_radio_name(&ver);
4896009595aSTomas Winkler 
4906009595aSTomas Winkler 	if (!radio_name) {
4916009595aSTomas Winkler 		ret = -ENOENT;
4926009595aSTomas Winkler 		dev_err(&cldev->dev, "Can't get the NFC interface version ret = %d\n",
4936009595aSTomas Winkler 			ret);
4946009595aSTomas Winkler 		goto disconnect;
4956009595aSTomas Winkler 	}
4966009595aSTomas Winkler 
4976009595aSTomas Winkler 	dev_dbg(bus->dev, "nfc radio %s\n", radio_name);
4985192e395SWolfram Sang 	strscpy(cldev->name, radio_name, sizeof(cldev->name));
4996009595aSTomas Winkler 
5006009595aSTomas Winkler disconnect:
5016009595aSTomas Winkler 	mutex_lock(&bus->device_lock);
5026009595aSTomas Winkler 	if (mei_cl_disconnect(cl) < 0)
5036009595aSTomas Winkler 		dev_err(bus->dev, "Can't disconnect the NFC INFO ME\n");
5046009595aSTomas Winkler 
5056009595aSTomas Winkler 	mei_cl_flush_queues(cl, NULL);
5066009595aSTomas Winkler 
5076009595aSTomas Winkler out:
5086009595aSTomas Winkler 	mei_cl_unlink(cl);
5096009595aSTomas Winkler 	mutex_unlock(&bus->device_lock);
5106009595aSTomas Winkler 	mei_me_cl_put(me_cl);
5116009595aSTomas Winkler 	kfree(cl);
5126009595aSTomas Winkler 
5136009595aSTomas Winkler 	if (ret)
5146009595aSTomas Winkler 		cldev->do_match = 0;
5156009595aSTomas Winkler 
5166009595aSTomas Winkler 	dev_dbg(bus->dev, "end of fixup match = %d\n", cldev->do_match);
517a0a785c4STomas Winkler }
518a0a785c4STomas Winkler 
519224ae607SAlexander Usyskin /**
520224ae607SAlexander Usyskin  * vt_support - enable on bus clients with vtag support
521224ae607SAlexander Usyskin  *
522224ae607SAlexander Usyskin  * @cldev: me clients device
523224ae607SAlexander Usyskin  */
vt_support(struct mei_cl_device * cldev)524224ae607SAlexander Usyskin static void vt_support(struct mei_cl_device *cldev)
525224ae607SAlexander Usyskin {
526224ae607SAlexander Usyskin 	if (cldev->me_cl->props.vt_supported == 1)
527224ae607SAlexander Usyskin 		cldev->do_match = 1;
528224ae607SAlexander Usyskin }
529224ae607SAlexander Usyskin 
530342e4c7eSTomas Winkler /**
531342e4c7eSTomas Winkler  * pxp_is_ready - enable bus client if pxp is ready
532342e4c7eSTomas Winkler  *
533342e4c7eSTomas Winkler  * @cldev: me clients device
534342e4c7eSTomas Winkler  */
pxp_is_ready(struct mei_cl_device * cldev)535342e4c7eSTomas Winkler static void pxp_is_ready(struct mei_cl_device *cldev)
536342e4c7eSTomas Winkler {
537342e4c7eSTomas Winkler 	struct mei_device *bus = cldev->bus;
538342e4c7eSTomas Winkler 
539342e4c7eSTomas Winkler 	switch (bus->pxp_mode) {
540342e4c7eSTomas Winkler 	case MEI_DEV_PXP_READY:
541342e4c7eSTomas Winkler 	case MEI_DEV_PXP_DEFAULT:
542342e4c7eSTomas Winkler 		cldev->do_match = 1;
543342e4c7eSTomas Winkler 	break;
544342e4c7eSTomas Winkler 	default:
545342e4c7eSTomas Winkler 		cldev->do_match = 0;
546342e4c7eSTomas Winkler 	break;
547342e4c7eSTomas Winkler 	}
548342e4c7eSTomas Winkler }
549342e4c7eSTomas Winkler 
55071ce7891STomas Winkler #define MEI_FIXUP(_uuid, _hook) { _uuid, _hook }
55171ce7891STomas Winkler 
55271ce7891STomas Winkler static struct mei_fixup {
55371ce7891STomas Winkler 
55471ce7891STomas Winkler 	const uuid_le uuid;
55571ce7891STomas Winkler 	void (*hook)(struct mei_cl_device *cldev);
5565b6dca29STomas Winkler } mei_fixups[] = {
557dd070a16STomas Winkler 	MEI_FIXUP(MEI_UUID_ANY, number_of_connections),
5585b6dca29STomas Winkler 	MEI_FIXUP(MEI_UUID_NFC_INFO, blacklist),
5596009595aSTomas Winkler 	MEI_FIXUP(MEI_UUID_NFC_HCI, mei_nfc),
560e97cdb30STomas Winkler 	MEI_FIXUP(MEI_UUID_WD, mei_wd),
5617279b238SAlexander Usyskin 	MEI_FIXUP(MEI_UUID_MKHIF_FIX, mei_mkhi_fix),
562342e4c7eSTomas Winkler 	MEI_FIXUP(MEI_UUID_IGSC_MKHI_FIX, mei_gsc_mkhi_fix_ver),
563*6549b2b7SAlexander Usyskin 	MEI_FIXUP(MEI_UUID_IGSC_MKHI, mei_gsc_mkhi_ver),
564d1e204e8STomas Winkler 	MEI_FIXUP(MEI_UUID_HDCP, whitelist),
565224ae607SAlexander Usyskin 	MEI_FIXUP(MEI_UUID_ANY, vt_support),
566342e4c7eSTomas Winkler 	MEI_FIXUP(MEI_UUID_PAVP, pxp_is_ready),
5675b6dca29STomas Winkler };
56871ce7891STomas Winkler 
56971ce7891STomas Winkler /**
57009f8c33aSTamar Mashiah  * mei_cl_bus_dev_fixup - run fixup handlers
57171ce7891STomas Winkler  *
57271ce7891STomas Winkler  * @cldev: me client device
57371ce7891STomas Winkler  */
mei_cl_bus_dev_fixup(struct mei_cl_device * cldev)574ae48d74dSTomas Winkler void mei_cl_bus_dev_fixup(struct mei_cl_device *cldev)
57571ce7891STomas Winkler {
57671ce7891STomas Winkler 	struct mei_fixup *f;
57771ce7891STomas Winkler 	const uuid_le *uuid = mei_me_cl_uuid(cldev->me_cl);
57844c98df0STomas Winkler 	size_t i;
57971ce7891STomas Winkler 
58071ce7891STomas Winkler 	for (i = 0; i < ARRAY_SIZE(mei_fixups); i++) {
58171ce7891STomas Winkler 
58271ce7891STomas Winkler 		f = &mei_fixups[i];
58371ce7891STomas Winkler 		if (uuid_le_cmp(f->uuid, MEI_UUID_ANY) == 0 ||
58471ce7891STomas Winkler 		    uuid_le_cmp(f->uuid, *uuid) == 0)
58571ce7891STomas Winkler 			f->hook(cldev);
58671ce7891STomas Winkler 	}
58771ce7891STomas Winkler }
588a0a785c4STomas Winkler 
589