xref: /OK3568_Linux_fs/kernel/drivers/misc/mei/bus-fixup.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Copyright (c) 2013-2020, Intel Corporation. All rights reserved.
4*4882a593Smuzhiyun  * Intel Management Engine Interface (Intel MEI) Linux driver
5*4882a593Smuzhiyun  */
6*4882a593Smuzhiyun 
7*4882a593Smuzhiyun #include <linux/kernel.h>
8*4882a593Smuzhiyun #include <linux/sched.h>
9*4882a593Smuzhiyun #include <linux/module.h>
10*4882a593Smuzhiyun #include <linux/device.h>
11*4882a593Smuzhiyun #include <linux/slab.h>
12*4882a593Smuzhiyun #include <linux/uuid.h>
13*4882a593Smuzhiyun 
14*4882a593Smuzhiyun #include <linux/mei_cl_bus.h>
15*4882a593Smuzhiyun 
16*4882a593Smuzhiyun #include "mei_dev.h"
17*4882a593Smuzhiyun #include "client.h"
18*4882a593Smuzhiyun 
19*4882a593Smuzhiyun #define MEI_UUID_NFC_INFO UUID_LE(0xd2de1625, 0x382d, 0x417d, \
20*4882a593Smuzhiyun 			0x48, 0xa4, 0xef, 0xab, 0xba, 0x8a, 0x12, 0x06)
21*4882a593Smuzhiyun 
22*4882a593Smuzhiyun static const uuid_le mei_nfc_info_guid = MEI_UUID_NFC_INFO;
23*4882a593Smuzhiyun 
24*4882a593Smuzhiyun #define MEI_UUID_NFC_HCI UUID_LE(0x0bb17a78, 0x2a8e, 0x4c50, \
25*4882a593Smuzhiyun 			0x94, 0xd4, 0x50, 0x26, 0x67, 0x23, 0x77, 0x5c)
26*4882a593Smuzhiyun 
27*4882a593Smuzhiyun #define MEI_UUID_WD UUID_LE(0x05B79A6F, 0x4628, 0x4D7F, \
28*4882a593Smuzhiyun 			    0x89, 0x9D, 0xA9, 0x15, 0x14, 0xCB, 0x32, 0xAB)
29*4882a593Smuzhiyun 
30*4882a593Smuzhiyun #define MEI_UUID_MKHIF_FIX UUID_LE(0x55213584, 0x9a29, 0x4916, \
31*4882a593Smuzhiyun 			0xba, 0xdf, 0xf, 0xb7, 0xed, 0x68, 0x2a, 0xeb)
32*4882a593Smuzhiyun 
33*4882a593Smuzhiyun #define MEI_UUID_HDCP UUID_LE(0xB638AB7E, 0x94E2, 0x4EA2, \
34*4882a593Smuzhiyun 			      0xA5, 0x52, 0xD1, 0xC5, 0x4B, 0x62, 0x7F, 0x04)
35*4882a593Smuzhiyun 
36*4882a593Smuzhiyun #define MEI_UUID_ANY NULL_UUID_LE
37*4882a593Smuzhiyun 
38*4882a593Smuzhiyun /**
39*4882a593Smuzhiyun  * number_of_connections - determine whether an client be on the bus
40*4882a593Smuzhiyun  *    according number of connections
41*4882a593Smuzhiyun  *    We support only clients:
42*4882a593Smuzhiyun  *       1. with single connection
43*4882a593Smuzhiyun  *       2. and fixed clients (max_number_of_connections == 0)
44*4882a593Smuzhiyun  *
45*4882a593Smuzhiyun  * @cldev: me clients device
46*4882a593Smuzhiyun  */
number_of_connections(struct mei_cl_device * cldev)47*4882a593Smuzhiyun static void number_of_connections(struct mei_cl_device *cldev)
48*4882a593Smuzhiyun {
49*4882a593Smuzhiyun 	if (cldev->me_cl->props.max_number_of_connections > 1)
50*4882a593Smuzhiyun 		cldev->do_match = 0;
51*4882a593Smuzhiyun }
52*4882a593Smuzhiyun 
53*4882a593Smuzhiyun /**
54*4882a593Smuzhiyun  * blacklist - blacklist a client from the bus
55*4882a593Smuzhiyun  *
56*4882a593Smuzhiyun  * @cldev: me clients device
57*4882a593Smuzhiyun  */
blacklist(struct mei_cl_device * cldev)58*4882a593Smuzhiyun static void blacklist(struct mei_cl_device *cldev)
59*4882a593Smuzhiyun {
60*4882a593Smuzhiyun 	cldev->do_match = 0;
61*4882a593Smuzhiyun }
62*4882a593Smuzhiyun 
63*4882a593Smuzhiyun /**
64*4882a593Smuzhiyun  * whitelist - forcefully whitelist client
65*4882a593Smuzhiyun  *
66*4882a593Smuzhiyun  * @cldev: me clients device
67*4882a593Smuzhiyun  */
whitelist(struct mei_cl_device * cldev)68*4882a593Smuzhiyun static void whitelist(struct mei_cl_device *cldev)
69*4882a593Smuzhiyun {
70*4882a593Smuzhiyun 	cldev->do_match = 1;
71*4882a593Smuzhiyun }
72*4882a593Smuzhiyun 
73*4882a593Smuzhiyun #define OSTYPE_LINUX    2
74*4882a593Smuzhiyun struct mei_os_ver {
75*4882a593Smuzhiyun 	__le16 build;
76*4882a593Smuzhiyun 	__le16 reserved1;
77*4882a593Smuzhiyun 	u8  os_type;
78*4882a593Smuzhiyun 	u8  major;
79*4882a593Smuzhiyun 	u8  minor;
80*4882a593Smuzhiyun 	u8  reserved2;
81*4882a593Smuzhiyun } __packed;
82*4882a593Smuzhiyun 
83*4882a593Smuzhiyun #define MKHI_FEATURE_PTT 0x10
84*4882a593Smuzhiyun 
85*4882a593Smuzhiyun struct mkhi_rule_id {
86*4882a593Smuzhiyun 	__le16 rule_type;
87*4882a593Smuzhiyun 	u8 feature_id;
88*4882a593Smuzhiyun 	u8 reserved;
89*4882a593Smuzhiyun } __packed;
90*4882a593Smuzhiyun 
91*4882a593Smuzhiyun struct mkhi_fwcaps {
92*4882a593Smuzhiyun 	struct mkhi_rule_id id;
93*4882a593Smuzhiyun 	u8 len;
94*4882a593Smuzhiyun 	u8 data[];
95*4882a593Smuzhiyun } __packed;
96*4882a593Smuzhiyun 
97*4882a593Smuzhiyun struct mkhi_fw_ver_block {
98*4882a593Smuzhiyun 	u16 minor;
99*4882a593Smuzhiyun 	u8 major;
100*4882a593Smuzhiyun 	u8 platform;
101*4882a593Smuzhiyun 	u16 buildno;
102*4882a593Smuzhiyun 	u16 hotfix;
103*4882a593Smuzhiyun } __packed;
104*4882a593Smuzhiyun 
105*4882a593Smuzhiyun struct mkhi_fw_ver {
106*4882a593Smuzhiyun 	struct mkhi_fw_ver_block ver[MEI_MAX_FW_VER_BLOCKS];
107*4882a593Smuzhiyun } __packed;
108*4882a593Smuzhiyun 
109*4882a593Smuzhiyun #define MKHI_FWCAPS_GROUP_ID 0x3
110*4882a593Smuzhiyun #define MKHI_FWCAPS_SET_OS_VER_APP_RULE_CMD 6
111*4882a593Smuzhiyun #define MKHI_GEN_GROUP_ID 0xFF
112*4882a593Smuzhiyun #define MKHI_GEN_GET_FW_VERSION_CMD 0x2
113*4882a593Smuzhiyun struct mkhi_msg_hdr {
114*4882a593Smuzhiyun 	u8  group_id;
115*4882a593Smuzhiyun 	u8  command;
116*4882a593Smuzhiyun 	u8  reserved;
117*4882a593Smuzhiyun 	u8  result;
118*4882a593Smuzhiyun } __packed;
119*4882a593Smuzhiyun 
120*4882a593Smuzhiyun struct mkhi_msg {
121*4882a593Smuzhiyun 	struct mkhi_msg_hdr hdr;
122*4882a593Smuzhiyun 	u8 data[];
123*4882a593Smuzhiyun } __packed;
124*4882a593Smuzhiyun 
125*4882a593Smuzhiyun #define MKHI_OSVER_BUF_LEN (sizeof(struct mkhi_msg_hdr) + \
126*4882a593Smuzhiyun 			    sizeof(struct mkhi_fwcaps) + \
127*4882a593Smuzhiyun 			    sizeof(struct mei_os_ver))
mei_osver(struct mei_cl_device * cldev)128*4882a593Smuzhiyun static int mei_osver(struct mei_cl_device *cldev)
129*4882a593Smuzhiyun {
130*4882a593Smuzhiyun 	const size_t size = MKHI_OSVER_BUF_LEN;
131*4882a593Smuzhiyun 	char buf[MKHI_OSVER_BUF_LEN];
132*4882a593Smuzhiyun 	struct mkhi_msg *req;
133*4882a593Smuzhiyun 	struct mkhi_fwcaps *fwcaps;
134*4882a593Smuzhiyun 	struct mei_os_ver *os_ver;
135*4882a593Smuzhiyun 	unsigned int mode = MEI_CL_IO_TX_BLOCKING | MEI_CL_IO_TX_INTERNAL;
136*4882a593Smuzhiyun 
137*4882a593Smuzhiyun 	memset(buf, 0, size);
138*4882a593Smuzhiyun 
139*4882a593Smuzhiyun 	req = (struct mkhi_msg *)buf;
140*4882a593Smuzhiyun 	req->hdr.group_id = MKHI_FWCAPS_GROUP_ID;
141*4882a593Smuzhiyun 	req->hdr.command = MKHI_FWCAPS_SET_OS_VER_APP_RULE_CMD;
142*4882a593Smuzhiyun 
143*4882a593Smuzhiyun 	fwcaps = (struct mkhi_fwcaps *)req->data;
144*4882a593Smuzhiyun 
145*4882a593Smuzhiyun 	fwcaps->id.rule_type = 0x0;
146*4882a593Smuzhiyun 	fwcaps->id.feature_id = MKHI_FEATURE_PTT;
147*4882a593Smuzhiyun 	fwcaps->len = sizeof(*os_ver);
148*4882a593Smuzhiyun 	os_ver = (struct mei_os_ver *)fwcaps->data;
149*4882a593Smuzhiyun 	os_ver->os_type = OSTYPE_LINUX;
150*4882a593Smuzhiyun 
151*4882a593Smuzhiyun 	return __mei_cl_send(cldev->cl, buf, size, mode);
152*4882a593Smuzhiyun }
153*4882a593Smuzhiyun 
154*4882a593Smuzhiyun #define MKHI_FWVER_BUF_LEN (sizeof(struct mkhi_msg_hdr) + \
155*4882a593Smuzhiyun 			    sizeof(struct mkhi_fw_ver))
156*4882a593Smuzhiyun #define MKHI_FWVER_LEN(__num) (sizeof(struct mkhi_msg_hdr) + \
157*4882a593Smuzhiyun 			       sizeof(struct mkhi_fw_ver_block) * (__num))
158*4882a593Smuzhiyun #define MKHI_RCV_TIMEOUT 500 /* receive timeout in msec */
mei_fwver(struct mei_cl_device * cldev)159*4882a593Smuzhiyun static int mei_fwver(struct mei_cl_device *cldev)
160*4882a593Smuzhiyun {
161*4882a593Smuzhiyun 	char buf[MKHI_FWVER_BUF_LEN];
162*4882a593Smuzhiyun 	struct mkhi_msg req;
163*4882a593Smuzhiyun 	struct mkhi_msg *rsp;
164*4882a593Smuzhiyun 	struct mkhi_fw_ver *fwver;
165*4882a593Smuzhiyun 	int bytes_recv, ret, i;
166*4882a593Smuzhiyun 
167*4882a593Smuzhiyun 	memset(buf, 0, sizeof(buf));
168*4882a593Smuzhiyun 
169*4882a593Smuzhiyun 	req.hdr.group_id = MKHI_GEN_GROUP_ID;
170*4882a593Smuzhiyun 	req.hdr.command = MKHI_GEN_GET_FW_VERSION_CMD;
171*4882a593Smuzhiyun 
172*4882a593Smuzhiyun 	ret = __mei_cl_send(cldev->cl, (u8 *)&req, sizeof(req),
173*4882a593Smuzhiyun 			    MEI_CL_IO_TX_BLOCKING);
174*4882a593Smuzhiyun 	if (ret < 0) {
175*4882a593Smuzhiyun 		dev_err(&cldev->dev, "Could not send ReqFWVersion cmd\n");
176*4882a593Smuzhiyun 		return ret;
177*4882a593Smuzhiyun 	}
178*4882a593Smuzhiyun 
179*4882a593Smuzhiyun 	ret = 0;
180*4882a593Smuzhiyun 	bytes_recv = __mei_cl_recv(cldev->cl, buf, sizeof(buf), 0,
181*4882a593Smuzhiyun 				   MKHI_RCV_TIMEOUT);
182*4882a593Smuzhiyun 	if (bytes_recv < 0 || (size_t)bytes_recv < MKHI_FWVER_LEN(1)) {
183*4882a593Smuzhiyun 		/*
184*4882a593Smuzhiyun 		 * Should be at least one version block,
185*4882a593Smuzhiyun 		 * error out if nothing found
186*4882a593Smuzhiyun 		 */
187*4882a593Smuzhiyun 		dev_err(&cldev->dev, "Could not read FW version\n");
188*4882a593Smuzhiyun 		return -EIO;
189*4882a593Smuzhiyun 	}
190*4882a593Smuzhiyun 
191*4882a593Smuzhiyun 	rsp = (struct mkhi_msg *)buf;
192*4882a593Smuzhiyun 	fwver = (struct mkhi_fw_ver *)rsp->data;
193*4882a593Smuzhiyun 	memset(cldev->bus->fw_ver, 0, sizeof(cldev->bus->fw_ver));
194*4882a593Smuzhiyun 	for (i = 0; i < MEI_MAX_FW_VER_BLOCKS; i++) {
195*4882a593Smuzhiyun 		if ((size_t)bytes_recv < MKHI_FWVER_LEN(i + 1))
196*4882a593Smuzhiyun 			break;
197*4882a593Smuzhiyun 		dev_dbg(&cldev->dev, "FW version%d %d:%d.%d.%d.%d\n",
198*4882a593Smuzhiyun 			i, fwver->ver[i].platform,
199*4882a593Smuzhiyun 			fwver->ver[i].major, fwver->ver[i].minor,
200*4882a593Smuzhiyun 			fwver->ver[i].hotfix, fwver->ver[i].buildno);
201*4882a593Smuzhiyun 
202*4882a593Smuzhiyun 		cldev->bus->fw_ver[i].platform = fwver->ver[i].platform;
203*4882a593Smuzhiyun 		cldev->bus->fw_ver[i].major = fwver->ver[i].major;
204*4882a593Smuzhiyun 		cldev->bus->fw_ver[i].minor = fwver->ver[i].minor;
205*4882a593Smuzhiyun 		cldev->bus->fw_ver[i].hotfix = fwver->ver[i].hotfix;
206*4882a593Smuzhiyun 		cldev->bus->fw_ver[i].buildno = fwver->ver[i].buildno;
207*4882a593Smuzhiyun 	}
208*4882a593Smuzhiyun 
209*4882a593Smuzhiyun 	return ret;
210*4882a593Smuzhiyun }
211*4882a593Smuzhiyun 
mei_mkhi_fix(struct mei_cl_device * cldev)212*4882a593Smuzhiyun static void mei_mkhi_fix(struct mei_cl_device *cldev)
213*4882a593Smuzhiyun {
214*4882a593Smuzhiyun 	int ret;
215*4882a593Smuzhiyun 
216*4882a593Smuzhiyun 	/* No need to enable the client if nothing is needed from it */
217*4882a593Smuzhiyun 	if (!cldev->bus->fw_f_fw_ver_supported &&
218*4882a593Smuzhiyun 	    !cldev->bus->hbm_f_os_supported)
219*4882a593Smuzhiyun 		return;
220*4882a593Smuzhiyun 
221*4882a593Smuzhiyun 	ret = mei_cldev_enable(cldev);
222*4882a593Smuzhiyun 	if (ret)
223*4882a593Smuzhiyun 		return;
224*4882a593Smuzhiyun 
225*4882a593Smuzhiyun 	if (cldev->bus->fw_f_fw_ver_supported) {
226*4882a593Smuzhiyun 		ret = mei_fwver(cldev);
227*4882a593Smuzhiyun 		if (ret < 0)
228*4882a593Smuzhiyun 			dev_err(&cldev->dev, "FW version command failed %d\n",
229*4882a593Smuzhiyun 				ret);
230*4882a593Smuzhiyun 	}
231*4882a593Smuzhiyun 
232*4882a593Smuzhiyun 	if (cldev->bus->hbm_f_os_supported) {
233*4882a593Smuzhiyun 		ret = mei_osver(cldev);
234*4882a593Smuzhiyun 		if (ret < 0)
235*4882a593Smuzhiyun 			dev_err(&cldev->dev, "OS version command failed %d\n",
236*4882a593Smuzhiyun 				ret);
237*4882a593Smuzhiyun 	}
238*4882a593Smuzhiyun 	mei_cldev_disable(cldev);
239*4882a593Smuzhiyun }
240*4882a593Smuzhiyun 
241*4882a593Smuzhiyun /**
242*4882a593Smuzhiyun  * mei_wd - wd client on the bus, change protocol version
243*4882a593Smuzhiyun  *   as the API has changed.
244*4882a593Smuzhiyun  *
245*4882a593Smuzhiyun  * @cldev: me clients device
246*4882a593Smuzhiyun  */
247*4882a593Smuzhiyun #if IS_ENABLED(CONFIG_INTEL_MEI_ME)
248*4882a593Smuzhiyun #include <linux/pci.h>
249*4882a593Smuzhiyun #include "hw-me-regs.h"
mei_wd(struct mei_cl_device * cldev)250*4882a593Smuzhiyun static void mei_wd(struct mei_cl_device *cldev)
251*4882a593Smuzhiyun {
252*4882a593Smuzhiyun 	struct pci_dev *pdev = to_pci_dev(cldev->dev.parent);
253*4882a593Smuzhiyun 
254*4882a593Smuzhiyun 	if (pdev->device == MEI_DEV_ID_WPT_LP ||
255*4882a593Smuzhiyun 	    pdev->device == MEI_DEV_ID_SPT ||
256*4882a593Smuzhiyun 	    pdev->device == MEI_DEV_ID_SPT_H)
257*4882a593Smuzhiyun 		cldev->me_cl->props.protocol_version = 0x2;
258*4882a593Smuzhiyun 
259*4882a593Smuzhiyun 	cldev->do_match = 1;
260*4882a593Smuzhiyun }
261*4882a593Smuzhiyun #else
mei_wd(struct mei_cl_device * cldev)262*4882a593Smuzhiyun static inline void mei_wd(struct mei_cl_device *cldev) {}
263*4882a593Smuzhiyun #endif /* CONFIG_INTEL_MEI_ME */
264*4882a593Smuzhiyun 
265*4882a593Smuzhiyun struct mei_nfc_cmd {
266*4882a593Smuzhiyun 	u8 command;
267*4882a593Smuzhiyun 	u8 status;
268*4882a593Smuzhiyun 	u16 req_id;
269*4882a593Smuzhiyun 	u32 reserved;
270*4882a593Smuzhiyun 	u16 data_size;
271*4882a593Smuzhiyun 	u8 sub_command;
272*4882a593Smuzhiyun 	u8 data[];
273*4882a593Smuzhiyun } __packed;
274*4882a593Smuzhiyun 
275*4882a593Smuzhiyun struct mei_nfc_reply {
276*4882a593Smuzhiyun 	u8 command;
277*4882a593Smuzhiyun 	u8 status;
278*4882a593Smuzhiyun 	u16 req_id;
279*4882a593Smuzhiyun 	u32 reserved;
280*4882a593Smuzhiyun 	u16 data_size;
281*4882a593Smuzhiyun 	u8 sub_command;
282*4882a593Smuzhiyun 	u8 reply_status;
283*4882a593Smuzhiyun 	u8 data[];
284*4882a593Smuzhiyun } __packed;
285*4882a593Smuzhiyun 
286*4882a593Smuzhiyun struct mei_nfc_if_version {
287*4882a593Smuzhiyun 	u8 radio_version_sw[3];
288*4882a593Smuzhiyun 	u8 reserved[3];
289*4882a593Smuzhiyun 	u8 radio_version_hw[3];
290*4882a593Smuzhiyun 	u8 i2c_addr;
291*4882a593Smuzhiyun 	u8 fw_ivn;
292*4882a593Smuzhiyun 	u8 vendor_id;
293*4882a593Smuzhiyun 	u8 radio_type;
294*4882a593Smuzhiyun } __packed;
295*4882a593Smuzhiyun 
296*4882a593Smuzhiyun 
297*4882a593Smuzhiyun #define MEI_NFC_CMD_MAINTENANCE 0x00
298*4882a593Smuzhiyun #define MEI_NFC_SUBCMD_IF_VERSION 0x01
299*4882a593Smuzhiyun 
300*4882a593Smuzhiyun /* Vendors */
301*4882a593Smuzhiyun #define MEI_NFC_VENDOR_INSIDE 0x00
302*4882a593Smuzhiyun #define MEI_NFC_VENDOR_NXP    0x01
303*4882a593Smuzhiyun 
304*4882a593Smuzhiyun /* Radio types */
305*4882a593Smuzhiyun #define MEI_NFC_VENDOR_INSIDE_UREAD 0x00
306*4882a593Smuzhiyun #define MEI_NFC_VENDOR_NXP_PN544    0x01
307*4882a593Smuzhiyun 
308*4882a593Smuzhiyun /**
309*4882a593Smuzhiyun  * mei_nfc_if_version - get NFC interface version
310*4882a593Smuzhiyun  *
311*4882a593Smuzhiyun  * @cl: host client (nfc info)
312*4882a593Smuzhiyun  * @ver: NFC interface version to be filled in
313*4882a593Smuzhiyun  *
314*4882a593Smuzhiyun  * Return: 0 on success; < 0 otherwise
315*4882a593Smuzhiyun  */
mei_nfc_if_version(struct mei_cl * cl,struct mei_nfc_if_version * ver)316*4882a593Smuzhiyun static int mei_nfc_if_version(struct mei_cl *cl,
317*4882a593Smuzhiyun 			      struct mei_nfc_if_version *ver)
318*4882a593Smuzhiyun {
319*4882a593Smuzhiyun 	struct mei_device *bus;
320*4882a593Smuzhiyun 	struct mei_nfc_cmd cmd = {
321*4882a593Smuzhiyun 		.command = MEI_NFC_CMD_MAINTENANCE,
322*4882a593Smuzhiyun 		.data_size = 1,
323*4882a593Smuzhiyun 		.sub_command = MEI_NFC_SUBCMD_IF_VERSION,
324*4882a593Smuzhiyun 	};
325*4882a593Smuzhiyun 	struct mei_nfc_reply *reply = NULL;
326*4882a593Smuzhiyun 	size_t if_version_length;
327*4882a593Smuzhiyun 	int bytes_recv, ret;
328*4882a593Smuzhiyun 
329*4882a593Smuzhiyun 	bus = cl->dev;
330*4882a593Smuzhiyun 
331*4882a593Smuzhiyun 	WARN_ON(mutex_is_locked(&bus->device_lock));
332*4882a593Smuzhiyun 
333*4882a593Smuzhiyun 	ret = __mei_cl_send(cl, (u8 *)&cmd, sizeof(cmd), MEI_CL_IO_TX_BLOCKING);
334*4882a593Smuzhiyun 	if (ret < 0) {
335*4882a593Smuzhiyun 		dev_err(bus->dev, "Could not send IF version cmd\n");
336*4882a593Smuzhiyun 		return ret;
337*4882a593Smuzhiyun 	}
338*4882a593Smuzhiyun 
339*4882a593Smuzhiyun 	/* to be sure on the stack we alloc memory */
340*4882a593Smuzhiyun 	if_version_length = sizeof(*reply) + sizeof(*ver);
341*4882a593Smuzhiyun 
342*4882a593Smuzhiyun 	reply = kzalloc(if_version_length, GFP_KERNEL);
343*4882a593Smuzhiyun 	if (!reply)
344*4882a593Smuzhiyun 		return -ENOMEM;
345*4882a593Smuzhiyun 
346*4882a593Smuzhiyun 	ret = 0;
347*4882a593Smuzhiyun 	bytes_recv = __mei_cl_recv(cl, (u8 *)reply, if_version_length, 0, 0);
348*4882a593Smuzhiyun 	if (bytes_recv < 0 || (size_t)bytes_recv < if_version_length) {
349*4882a593Smuzhiyun 		dev_err(bus->dev, "Could not read IF version\n");
350*4882a593Smuzhiyun 		ret = -EIO;
351*4882a593Smuzhiyun 		goto err;
352*4882a593Smuzhiyun 	}
353*4882a593Smuzhiyun 
354*4882a593Smuzhiyun 	memcpy(ver, reply->data, sizeof(*ver));
355*4882a593Smuzhiyun 
356*4882a593Smuzhiyun 	dev_info(bus->dev, "NFC MEI VERSION: IVN 0x%x Vendor ID 0x%x Type 0x%x\n",
357*4882a593Smuzhiyun 		ver->fw_ivn, ver->vendor_id, ver->radio_type);
358*4882a593Smuzhiyun 
359*4882a593Smuzhiyun err:
360*4882a593Smuzhiyun 	kfree(reply);
361*4882a593Smuzhiyun 	return ret;
362*4882a593Smuzhiyun }
363*4882a593Smuzhiyun 
364*4882a593Smuzhiyun /**
365*4882a593Smuzhiyun  * mei_nfc_radio_name - derive nfc radio name from the interface version
366*4882a593Smuzhiyun  *
367*4882a593Smuzhiyun  * @ver: NFC radio version
368*4882a593Smuzhiyun  *
369*4882a593Smuzhiyun  * Return: radio name string
370*4882a593Smuzhiyun  */
mei_nfc_radio_name(struct mei_nfc_if_version * ver)371*4882a593Smuzhiyun static const char *mei_nfc_radio_name(struct mei_nfc_if_version *ver)
372*4882a593Smuzhiyun {
373*4882a593Smuzhiyun 
374*4882a593Smuzhiyun 	if (ver->vendor_id == MEI_NFC_VENDOR_INSIDE) {
375*4882a593Smuzhiyun 		if (ver->radio_type == MEI_NFC_VENDOR_INSIDE_UREAD)
376*4882a593Smuzhiyun 			return "microread";
377*4882a593Smuzhiyun 	}
378*4882a593Smuzhiyun 
379*4882a593Smuzhiyun 	if (ver->vendor_id == MEI_NFC_VENDOR_NXP) {
380*4882a593Smuzhiyun 		if (ver->radio_type == MEI_NFC_VENDOR_NXP_PN544)
381*4882a593Smuzhiyun 			return "pn544";
382*4882a593Smuzhiyun 	}
383*4882a593Smuzhiyun 
384*4882a593Smuzhiyun 	return NULL;
385*4882a593Smuzhiyun }
386*4882a593Smuzhiyun 
387*4882a593Smuzhiyun /**
388*4882a593Smuzhiyun  * mei_nfc - The nfc fixup function. The function retrieves nfc radio
389*4882a593Smuzhiyun  *    name and set is as device attribute so we can load
390*4882a593Smuzhiyun  *    the proper device driver for it
391*4882a593Smuzhiyun  *
392*4882a593Smuzhiyun  * @cldev: me client device (nfc)
393*4882a593Smuzhiyun  */
mei_nfc(struct mei_cl_device * cldev)394*4882a593Smuzhiyun static void mei_nfc(struct mei_cl_device *cldev)
395*4882a593Smuzhiyun {
396*4882a593Smuzhiyun 	struct mei_device *bus;
397*4882a593Smuzhiyun 	struct mei_cl *cl;
398*4882a593Smuzhiyun 	struct mei_me_client *me_cl = NULL;
399*4882a593Smuzhiyun 	struct mei_nfc_if_version ver;
400*4882a593Smuzhiyun 	const char *radio_name = NULL;
401*4882a593Smuzhiyun 	int ret;
402*4882a593Smuzhiyun 
403*4882a593Smuzhiyun 	bus = cldev->bus;
404*4882a593Smuzhiyun 
405*4882a593Smuzhiyun 	mutex_lock(&bus->device_lock);
406*4882a593Smuzhiyun 	/* we need to connect to INFO GUID */
407*4882a593Smuzhiyun 	cl = mei_cl_alloc_linked(bus);
408*4882a593Smuzhiyun 	if (IS_ERR(cl)) {
409*4882a593Smuzhiyun 		ret = PTR_ERR(cl);
410*4882a593Smuzhiyun 		cl = NULL;
411*4882a593Smuzhiyun 		dev_err(bus->dev, "nfc hook alloc failed %d\n", ret);
412*4882a593Smuzhiyun 		goto out;
413*4882a593Smuzhiyun 	}
414*4882a593Smuzhiyun 
415*4882a593Smuzhiyun 	me_cl = mei_me_cl_by_uuid(bus, &mei_nfc_info_guid);
416*4882a593Smuzhiyun 	if (!me_cl) {
417*4882a593Smuzhiyun 		ret = -ENOTTY;
418*4882a593Smuzhiyun 		dev_err(bus->dev, "Cannot find nfc info %d\n", ret);
419*4882a593Smuzhiyun 		goto out;
420*4882a593Smuzhiyun 	}
421*4882a593Smuzhiyun 
422*4882a593Smuzhiyun 	ret = mei_cl_connect(cl, me_cl, NULL);
423*4882a593Smuzhiyun 	if (ret < 0) {
424*4882a593Smuzhiyun 		dev_err(&cldev->dev, "Can't connect to the NFC INFO ME ret = %d\n",
425*4882a593Smuzhiyun 			ret);
426*4882a593Smuzhiyun 		goto out;
427*4882a593Smuzhiyun 	}
428*4882a593Smuzhiyun 
429*4882a593Smuzhiyun 	mutex_unlock(&bus->device_lock);
430*4882a593Smuzhiyun 
431*4882a593Smuzhiyun 	ret = mei_nfc_if_version(cl, &ver);
432*4882a593Smuzhiyun 	if (ret)
433*4882a593Smuzhiyun 		goto disconnect;
434*4882a593Smuzhiyun 
435*4882a593Smuzhiyun 	radio_name = mei_nfc_radio_name(&ver);
436*4882a593Smuzhiyun 
437*4882a593Smuzhiyun 	if (!radio_name) {
438*4882a593Smuzhiyun 		ret = -ENOENT;
439*4882a593Smuzhiyun 		dev_err(&cldev->dev, "Can't get the NFC interface version ret = %d\n",
440*4882a593Smuzhiyun 			ret);
441*4882a593Smuzhiyun 		goto disconnect;
442*4882a593Smuzhiyun 	}
443*4882a593Smuzhiyun 
444*4882a593Smuzhiyun 	dev_dbg(bus->dev, "nfc radio %s\n", radio_name);
445*4882a593Smuzhiyun 	strlcpy(cldev->name, radio_name, sizeof(cldev->name));
446*4882a593Smuzhiyun 
447*4882a593Smuzhiyun disconnect:
448*4882a593Smuzhiyun 	mutex_lock(&bus->device_lock);
449*4882a593Smuzhiyun 	if (mei_cl_disconnect(cl) < 0)
450*4882a593Smuzhiyun 		dev_err(bus->dev, "Can't disconnect the NFC INFO ME\n");
451*4882a593Smuzhiyun 
452*4882a593Smuzhiyun 	mei_cl_flush_queues(cl, NULL);
453*4882a593Smuzhiyun 
454*4882a593Smuzhiyun out:
455*4882a593Smuzhiyun 	mei_cl_unlink(cl);
456*4882a593Smuzhiyun 	mutex_unlock(&bus->device_lock);
457*4882a593Smuzhiyun 	mei_me_cl_put(me_cl);
458*4882a593Smuzhiyun 	kfree(cl);
459*4882a593Smuzhiyun 
460*4882a593Smuzhiyun 	if (ret)
461*4882a593Smuzhiyun 		cldev->do_match = 0;
462*4882a593Smuzhiyun 
463*4882a593Smuzhiyun 	dev_dbg(bus->dev, "end of fixup match = %d\n", cldev->do_match);
464*4882a593Smuzhiyun }
465*4882a593Smuzhiyun 
466*4882a593Smuzhiyun /**
467*4882a593Smuzhiyun  * vt_support - enable on bus clients with vtag support
468*4882a593Smuzhiyun  *
469*4882a593Smuzhiyun  * @cldev: me clients device
470*4882a593Smuzhiyun  */
vt_support(struct mei_cl_device * cldev)471*4882a593Smuzhiyun static void vt_support(struct mei_cl_device *cldev)
472*4882a593Smuzhiyun {
473*4882a593Smuzhiyun 	if (cldev->me_cl->props.vt_supported == 1)
474*4882a593Smuzhiyun 		cldev->do_match = 1;
475*4882a593Smuzhiyun }
476*4882a593Smuzhiyun 
477*4882a593Smuzhiyun #define MEI_FIXUP(_uuid, _hook) { _uuid, _hook }
478*4882a593Smuzhiyun 
479*4882a593Smuzhiyun static struct mei_fixup {
480*4882a593Smuzhiyun 
481*4882a593Smuzhiyun 	const uuid_le uuid;
482*4882a593Smuzhiyun 	void (*hook)(struct mei_cl_device *cldev);
483*4882a593Smuzhiyun } mei_fixups[] = {
484*4882a593Smuzhiyun 	MEI_FIXUP(MEI_UUID_ANY, number_of_connections),
485*4882a593Smuzhiyun 	MEI_FIXUP(MEI_UUID_NFC_INFO, blacklist),
486*4882a593Smuzhiyun 	MEI_FIXUP(MEI_UUID_NFC_HCI, mei_nfc),
487*4882a593Smuzhiyun 	MEI_FIXUP(MEI_UUID_WD, mei_wd),
488*4882a593Smuzhiyun 	MEI_FIXUP(MEI_UUID_MKHIF_FIX, mei_mkhi_fix),
489*4882a593Smuzhiyun 	MEI_FIXUP(MEI_UUID_HDCP, whitelist),
490*4882a593Smuzhiyun 	MEI_FIXUP(MEI_UUID_ANY, vt_support),
491*4882a593Smuzhiyun };
492*4882a593Smuzhiyun 
493*4882a593Smuzhiyun /**
494*4882a593Smuzhiyun  * mei_cldev_fixup - run fixup handlers
495*4882a593Smuzhiyun  *
496*4882a593Smuzhiyun  * @cldev: me client device
497*4882a593Smuzhiyun  */
mei_cl_bus_dev_fixup(struct mei_cl_device * cldev)498*4882a593Smuzhiyun void mei_cl_bus_dev_fixup(struct mei_cl_device *cldev)
499*4882a593Smuzhiyun {
500*4882a593Smuzhiyun 	struct mei_fixup *f;
501*4882a593Smuzhiyun 	const uuid_le *uuid = mei_me_cl_uuid(cldev->me_cl);
502*4882a593Smuzhiyun 	size_t i;
503*4882a593Smuzhiyun 
504*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(mei_fixups); i++) {
505*4882a593Smuzhiyun 
506*4882a593Smuzhiyun 		f = &mei_fixups[i];
507*4882a593Smuzhiyun 		if (uuid_le_cmp(f->uuid, MEI_UUID_ANY) == 0 ||
508*4882a593Smuzhiyun 		    uuid_le_cmp(f->uuid, *uuid) == 0)
509*4882a593Smuzhiyun 			f->hook(cldev);
510*4882a593Smuzhiyun 	}
511*4882a593Smuzhiyun }
512*4882a593Smuzhiyun 
513