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