18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2013, Intel Corporation. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * MEI Library for mei bus nfc device access 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/slab.h> 118c2ecf20Sopenharmony_ci#include <linux/nfc.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include "mei_phy.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_cistruct mei_nfc_hdr { 168c2ecf20Sopenharmony_ci u8 cmd; 178c2ecf20Sopenharmony_ci u8 status; 188c2ecf20Sopenharmony_ci u16 req_id; 198c2ecf20Sopenharmony_ci u32 reserved; 208c2ecf20Sopenharmony_ci u16 data_size; 218c2ecf20Sopenharmony_ci} __packed; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistruct mei_nfc_cmd { 248c2ecf20Sopenharmony_ci struct mei_nfc_hdr hdr; 258c2ecf20Sopenharmony_ci u8 sub_command; 268c2ecf20Sopenharmony_ci u8 data[]; 278c2ecf20Sopenharmony_ci} __packed; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistruct mei_nfc_reply { 308c2ecf20Sopenharmony_ci struct mei_nfc_hdr hdr; 318c2ecf20Sopenharmony_ci u8 sub_command; 328c2ecf20Sopenharmony_ci u8 reply_status; 338c2ecf20Sopenharmony_ci u8 data[]; 348c2ecf20Sopenharmony_ci} __packed; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistruct mei_nfc_if_version { 378c2ecf20Sopenharmony_ci u8 radio_version_sw[3]; 388c2ecf20Sopenharmony_ci u8 reserved[3]; 398c2ecf20Sopenharmony_ci u8 radio_version_hw[3]; 408c2ecf20Sopenharmony_ci u8 i2c_addr; 418c2ecf20Sopenharmony_ci u8 fw_ivn; 428c2ecf20Sopenharmony_ci u8 vendor_id; 438c2ecf20Sopenharmony_ci u8 radio_type; 448c2ecf20Sopenharmony_ci} __packed; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistruct mei_nfc_connect { 478c2ecf20Sopenharmony_ci u8 fw_ivn; 488c2ecf20Sopenharmony_ci u8 vendor_id; 498c2ecf20Sopenharmony_ci} __packed; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistruct mei_nfc_connect_resp { 528c2ecf20Sopenharmony_ci u8 fw_ivn; 538c2ecf20Sopenharmony_ci u8 vendor_id; 548c2ecf20Sopenharmony_ci u16 me_major; 558c2ecf20Sopenharmony_ci u16 me_minor; 568c2ecf20Sopenharmony_ci u16 me_hotfix; 578c2ecf20Sopenharmony_ci u16 me_build; 588c2ecf20Sopenharmony_ci} __packed; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci#define MEI_NFC_CMD_MAINTENANCE 0x00 628c2ecf20Sopenharmony_ci#define MEI_NFC_CMD_HCI_SEND 0x01 638c2ecf20Sopenharmony_ci#define MEI_NFC_CMD_HCI_RECV 0x02 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci#define MEI_NFC_SUBCMD_CONNECT 0x00 668c2ecf20Sopenharmony_ci#define MEI_NFC_SUBCMD_IF_VERSION 0x01 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci#define MEI_NFC_MAX_READ (MEI_NFC_HEADER_SIZE + MEI_NFC_MAX_HCI_PAYLOAD) 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci#define MEI_DUMP_SKB_IN(info, skb) \ 718c2ecf20Sopenharmony_cido { \ 728c2ecf20Sopenharmony_ci pr_debug("%s:\n", info); \ 738c2ecf20Sopenharmony_ci print_hex_dump_debug("mei in : ", DUMP_PREFIX_OFFSET, \ 748c2ecf20Sopenharmony_ci 16, 1, (skb)->data, (skb)->len, false); \ 758c2ecf20Sopenharmony_ci} while (0) 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci#define MEI_DUMP_SKB_OUT(info, skb) \ 788c2ecf20Sopenharmony_cido { \ 798c2ecf20Sopenharmony_ci pr_debug("%s:\n", info); \ 808c2ecf20Sopenharmony_ci print_hex_dump_debug("mei out: ", DUMP_PREFIX_OFFSET, \ 818c2ecf20Sopenharmony_ci 16, 1, (skb)->data, (skb)->len, false); \ 828c2ecf20Sopenharmony_ci} while (0) 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci#define MEI_DUMP_NFC_HDR(info, _hdr) \ 858c2ecf20Sopenharmony_cido { \ 868c2ecf20Sopenharmony_ci pr_debug("%s:\n", info); \ 878c2ecf20Sopenharmony_ci pr_debug("cmd=%02d status=%d req_id=%d rsvd=%d size=%d\n", \ 888c2ecf20Sopenharmony_ci (_hdr)->cmd, (_hdr)->status, (_hdr)->req_id, \ 898c2ecf20Sopenharmony_ci (_hdr)->reserved, (_hdr)->data_size); \ 908c2ecf20Sopenharmony_ci} while (0) 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic int mei_nfc_if_version(struct nfc_mei_phy *phy) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci struct mei_nfc_cmd cmd; 968c2ecf20Sopenharmony_ci struct mei_nfc_reply *reply = NULL; 978c2ecf20Sopenharmony_ci struct mei_nfc_if_version *version; 988c2ecf20Sopenharmony_ci size_t if_version_length; 998c2ecf20Sopenharmony_ci int bytes_recv, r; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci pr_info("%s\n", __func__); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci memset(&cmd, 0, sizeof(struct mei_nfc_cmd)); 1048c2ecf20Sopenharmony_ci cmd.hdr.cmd = MEI_NFC_CMD_MAINTENANCE; 1058c2ecf20Sopenharmony_ci cmd.hdr.data_size = 1; 1068c2ecf20Sopenharmony_ci cmd.sub_command = MEI_NFC_SUBCMD_IF_VERSION; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci MEI_DUMP_NFC_HDR("version", &cmd.hdr); 1098c2ecf20Sopenharmony_ci r = mei_cldev_send(phy->cldev, (u8 *)&cmd, sizeof(struct mei_nfc_cmd)); 1108c2ecf20Sopenharmony_ci if (r < 0) { 1118c2ecf20Sopenharmony_ci pr_err("Could not send IF version cmd\n"); 1128c2ecf20Sopenharmony_ci return r; 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci /* to be sure on the stack we alloc memory */ 1168c2ecf20Sopenharmony_ci if_version_length = sizeof(struct mei_nfc_reply) + 1178c2ecf20Sopenharmony_ci sizeof(struct mei_nfc_if_version); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci reply = kzalloc(if_version_length, GFP_KERNEL); 1208c2ecf20Sopenharmony_ci if (!reply) 1218c2ecf20Sopenharmony_ci return -ENOMEM; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci bytes_recv = mei_cldev_recv(phy->cldev, (u8 *)reply, if_version_length); 1248c2ecf20Sopenharmony_ci if (bytes_recv < 0 || bytes_recv < if_version_length) { 1258c2ecf20Sopenharmony_ci pr_err("Could not read IF version\n"); 1268c2ecf20Sopenharmony_ci r = -EIO; 1278c2ecf20Sopenharmony_ci goto err; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci version = (struct mei_nfc_if_version *)reply->data; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci phy->fw_ivn = version->fw_ivn; 1338c2ecf20Sopenharmony_ci phy->vendor_id = version->vendor_id; 1348c2ecf20Sopenharmony_ci phy->radio_type = version->radio_type; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cierr: 1378c2ecf20Sopenharmony_ci kfree(reply); 1388c2ecf20Sopenharmony_ci return r; 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic int mei_nfc_connect(struct nfc_mei_phy *phy) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci struct mei_nfc_cmd *cmd, *reply; 1448c2ecf20Sopenharmony_ci struct mei_nfc_connect *connect; 1458c2ecf20Sopenharmony_ci struct mei_nfc_connect_resp *connect_resp; 1468c2ecf20Sopenharmony_ci size_t connect_length, connect_resp_length; 1478c2ecf20Sopenharmony_ci int bytes_recv, r; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci pr_info("%s\n", __func__); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci connect_length = sizeof(struct mei_nfc_cmd) + 1528c2ecf20Sopenharmony_ci sizeof(struct mei_nfc_connect); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci connect_resp_length = sizeof(struct mei_nfc_cmd) + 1558c2ecf20Sopenharmony_ci sizeof(struct mei_nfc_connect_resp); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci cmd = kzalloc(connect_length, GFP_KERNEL); 1588c2ecf20Sopenharmony_ci if (!cmd) 1598c2ecf20Sopenharmony_ci return -ENOMEM; 1608c2ecf20Sopenharmony_ci connect = (struct mei_nfc_connect *)cmd->data; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci reply = kzalloc(connect_resp_length, GFP_KERNEL); 1638c2ecf20Sopenharmony_ci if (!reply) { 1648c2ecf20Sopenharmony_ci kfree(cmd); 1658c2ecf20Sopenharmony_ci return -ENOMEM; 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci connect_resp = (struct mei_nfc_connect_resp *)reply->data; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci cmd->hdr.cmd = MEI_NFC_CMD_MAINTENANCE; 1718c2ecf20Sopenharmony_ci cmd->hdr.data_size = 3; 1728c2ecf20Sopenharmony_ci cmd->sub_command = MEI_NFC_SUBCMD_CONNECT; 1738c2ecf20Sopenharmony_ci connect->fw_ivn = phy->fw_ivn; 1748c2ecf20Sopenharmony_ci connect->vendor_id = phy->vendor_id; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci MEI_DUMP_NFC_HDR("connect request", &cmd->hdr); 1778c2ecf20Sopenharmony_ci r = mei_cldev_send(phy->cldev, (u8 *)cmd, connect_length); 1788c2ecf20Sopenharmony_ci if (r < 0) { 1798c2ecf20Sopenharmony_ci pr_err("Could not send connect cmd %d\n", r); 1808c2ecf20Sopenharmony_ci goto err; 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci bytes_recv = mei_cldev_recv(phy->cldev, (u8 *)reply, 1848c2ecf20Sopenharmony_ci connect_resp_length); 1858c2ecf20Sopenharmony_ci if (bytes_recv < 0) { 1868c2ecf20Sopenharmony_ci r = bytes_recv; 1878c2ecf20Sopenharmony_ci pr_err("Could not read connect response %d\n", r); 1888c2ecf20Sopenharmony_ci goto err; 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci MEI_DUMP_NFC_HDR("connect reply", &reply->hdr); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci pr_info("IVN 0x%x Vendor ID 0x%x\n", 1948c2ecf20Sopenharmony_ci connect_resp->fw_ivn, connect_resp->vendor_id); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci pr_info("ME FW %d.%d.%d.%d\n", 1978c2ecf20Sopenharmony_ci connect_resp->me_major, connect_resp->me_minor, 1988c2ecf20Sopenharmony_ci connect_resp->me_hotfix, connect_resp->me_build); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci r = 0; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cierr: 2038c2ecf20Sopenharmony_ci kfree(reply); 2048c2ecf20Sopenharmony_ci kfree(cmd); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci return r; 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic int mei_nfc_send(struct nfc_mei_phy *phy, u8 *buf, size_t length) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci struct mei_nfc_hdr *hdr; 2128c2ecf20Sopenharmony_ci u8 *mei_buf; 2138c2ecf20Sopenharmony_ci int err; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci err = -ENOMEM; 2168c2ecf20Sopenharmony_ci mei_buf = kzalloc(length + MEI_NFC_HEADER_SIZE, GFP_KERNEL); 2178c2ecf20Sopenharmony_ci if (!mei_buf) 2188c2ecf20Sopenharmony_ci goto out; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci hdr = (struct mei_nfc_hdr *)mei_buf; 2218c2ecf20Sopenharmony_ci hdr->cmd = MEI_NFC_CMD_HCI_SEND; 2228c2ecf20Sopenharmony_ci hdr->status = 0; 2238c2ecf20Sopenharmony_ci hdr->req_id = phy->req_id; 2248c2ecf20Sopenharmony_ci hdr->reserved = 0; 2258c2ecf20Sopenharmony_ci hdr->data_size = length; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci MEI_DUMP_NFC_HDR("send", hdr); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci memcpy(mei_buf + MEI_NFC_HEADER_SIZE, buf, length); 2308c2ecf20Sopenharmony_ci err = mei_cldev_send(phy->cldev, mei_buf, length + MEI_NFC_HEADER_SIZE); 2318c2ecf20Sopenharmony_ci if (err < 0) 2328c2ecf20Sopenharmony_ci goto out; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci if (!wait_event_interruptible_timeout(phy->send_wq, 2358c2ecf20Sopenharmony_ci phy->recv_req_id == phy->req_id, HZ)) { 2368c2ecf20Sopenharmony_ci pr_err("NFC MEI command timeout\n"); 2378c2ecf20Sopenharmony_ci err = -ETIME; 2388c2ecf20Sopenharmony_ci } else { 2398c2ecf20Sopenharmony_ci phy->req_id++; 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ciout: 2428c2ecf20Sopenharmony_ci kfree(mei_buf); 2438c2ecf20Sopenharmony_ci return err; 2448c2ecf20Sopenharmony_ci} 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci/* 2478c2ecf20Sopenharmony_ci * Writing a frame must not return the number of written bytes. 2488c2ecf20Sopenharmony_ci * It must return either zero for success, or <0 for error. 2498c2ecf20Sopenharmony_ci * In addition, it must not alter the skb 2508c2ecf20Sopenharmony_ci */ 2518c2ecf20Sopenharmony_cistatic int nfc_mei_phy_write(void *phy_id, struct sk_buff *skb) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci struct nfc_mei_phy *phy = phy_id; 2548c2ecf20Sopenharmony_ci int r; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci MEI_DUMP_SKB_OUT("mei frame sent", skb); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci r = mei_nfc_send(phy, skb->data, skb->len); 2598c2ecf20Sopenharmony_ci if (r > 0) 2608c2ecf20Sopenharmony_ci r = 0; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci return r; 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic int mei_nfc_recv(struct nfc_mei_phy *phy, u8 *buf, size_t length) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci struct mei_nfc_hdr *hdr; 2688c2ecf20Sopenharmony_ci int received_length; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci received_length = mei_cldev_recv(phy->cldev, buf, length); 2718c2ecf20Sopenharmony_ci if (received_length < 0) 2728c2ecf20Sopenharmony_ci return received_length; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci hdr = (struct mei_nfc_hdr *) buf; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci MEI_DUMP_NFC_HDR("receive", hdr); 2778c2ecf20Sopenharmony_ci if (hdr->cmd == MEI_NFC_CMD_HCI_SEND) { 2788c2ecf20Sopenharmony_ci phy->recv_req_id = hdr->req_id; 2798c2ecf20Sopenharmony_ci wake_up(&phy->send_wq); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci return 0; 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci return received_length; 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_cistatic void nfc_mei_rx_cb(struct mei_cl_device *cldev) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci struct nfc_mei_phy *phy = mei_cldev_get_drvdata(cldev); 2918c2ecf20Sopenharmony_ci struct sk_buff *skb; 2928c2ecf20Sopenharmony_ci int reply_size; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci if (!phy) 2958c2ecf20Sopenharmony_ci return; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci if (phy->hard_fault != 0) 2988c2ecf20Sopenharmony_ci return; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci skb = alloc_skb(MEI_NFC_MAX_READ, GFP_KERNEL); 3018c2ecf20Sopenharmony_ci if (!skb) 3028c2ecf20Sopenharmony_ci return; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci reply_size = mei_nfc_recv(phy, skb->data, MEI_NFC_MAX_READ); 3058c2ecf20Sopenharmony_ci if (reply_size < MEI_NFC_HEADER_SIZE) { 3068c2ecf20Sopenharmony_ci kfree_skb(skb); 3078c2ecf20Sopenharmony_ci return; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci skb_put(skb, reply_size); 3118c2ecf20Sopenharmony_ci skb_pull(skb, MEI_NFC_HEADER_SIZE); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci MEI_DUMP_SKB_IN("mei frame read", skb); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci nfc_hci_recv_frame(phy->hdev, skb); 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistatic int nfc_mei_phy_enable(void *phy_id) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci int r; 3218c2ecf20Sopenharmony_ci struct nfc_mei_phy *phy = phy_id; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci pr_info("%s\n", __func__); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci if (phy->powered == 1) 3268c2ecf20Sopenharmony_ci return 0; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci r = mei_cldev_enable(phy->cldev); 3298c2ecf20Sopenharmony_ci if (r < 0) { 3308c2ecf20Sopenharmony_ci pr_err("Could not enable device %d\n", r); 3318c2ecf20Sopenharmony_ci return r; 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci r = mei_nfc_if_version(phy); 3358c2ecf20Sopenharmony_ci if (r < 0) { 3368c2ecf20Sopenharmony_ci pr_err("Could not enable device %d\n", r); 3378c2ecf20Sopenharmony_ci goto err; 3388c2ecf20Sopenharmony_ci } 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci r = mei_nfc_connect(phy); 3418c2ecf20Sopenharmony_ci if (r < 0) { 3428c2ecf20Sopenharmony_ci pr_err("Could not connect to device %d\n", r); 3438c2ecf20Sopenharmony_ci goto err; 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci r = mei_cldev_register_rx_cb(phy->cldev, nfc_mei_rx_cb); 3478c2ecf20Sopenharmony_ci if (r) { 3488c2ecf20Sopenharmony_ci pr_err("Event cb registration failed %d\n", r); 3498c2ecf20Sopenharmony_ci goto err; 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci phy->powered = 1; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci return 0; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_cierr: 3578c2ecf20Sopenharmony_ci phy->powered = 0; 3588c2ecf20Sopenharmony_ci mei_cldev_disable(phy->cldev); 3598c2ecf20Sopenharmony_ci return r; 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_cistatic void nfc_mei_phy_disable(void *phy_id) 3638c2ecf20Sopenharmony_ci{ 3648c2ecf20Sopenharmony_ci struct nfc_mei_phy *phy = phy_id; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci pr_info("%s\n", __func__); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci mei_cldev_disable(phy->cldev); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci phy->powered = 0; 3718c2ecf20Sopenharmony_ci} 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_cistruct nfc_phy_ops mei_phy_ops = { 3748c2ecf20Sopenharmony_ci .write = nfc_mei_phy_write, 3758c2ecf20Sopenharmony_ci .enable = nfc_mei_phy_enable, 3768c2ecf20Sopenharmony_ci .disable = nfc_mei_phy_disable, 3778c2ecf20Sopenharmony_ci}; 3788c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mei_phy_ops); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_cistruct nfc_mei_phy *nfc_mei_phy_alloc(struct mei_cl_device *cldev) 3818c2ecf20Sopenharmony_ci{ 3828c2ecf20Sopenharmony_ci struct nfc_mei_phy *phy; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci phy = kzalloc(sizeof(struct nfc_mei_phy), GFP_KERNEL); 3858c2ecf20Sopenharmony_ci if (!phy) 3868c2ecf20Sopenharmony_ci return NULL; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci phy->cldev = cldev; 3898c2ecf20Sopenharmony_ci init_waitqueue_head(&phy->send_wq); 3908c2ecf20Sopenharmony_ci mei_cldev_set_drvdata(cldev, phy); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci return phy; 3938c2ecf20Sopenharmony_ci} 3948c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nfc_mei_phy_alloc); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_civoid nfc_mei_phy_free(struct nfc_mei_phy *phy) 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci mei_cldev_disable(phy->cldev); 3998c2ecf20Sopenharmony_ci kfree(phy); 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nfc_mei_phy_free); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 4048c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("mei bus NFC device interface"); 405