1191b0700STomas Winkler // SPDX-License-Identifier: GPL-2.0
24912e2feSEric Lapuyade /*
3191b0700STomas Winkler * Copyright (c) 2013, Intel Corporation.
4191b0700STomas Winkler *
54912e2feSEric Lapuyade * MEI Library for mei bus nfc device access
64912e2feSEric Lapuyade */
717936b43SJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
817936b43SJoe Perches
94912e2feSEric Lapuyade #include <linux/module.h>
104912e2feSEric Lapuyade #include <linux/slab.h>
114912e2feSEric Lapuyade #include <linux/nfc.h>
124912e2feSEric Lapuyade
134912e2feSEric Lapuyade #include "mei_phy.h"
144912e2feSEric Lapuyade
151d3ff767STomas Winkler struct mei_nfc_hdr {
161d3ff767STomas Winkler u8 cmd;
17be9b720aSTomas Winkler u8 status;
18be9b720aSTomas Winkler u16 req_id;
19be9b720aSTomas Winkler u32 reserved;
20be9b720aSTomas Winkler u16 data_size;
211d3ff767STomas Winkler } __packed;
221d3ff767STomas Winkler
231d3ff767STomas Winkler struct mei_nfc_cmd {
241d3ff767STomas Winkler struct mei_nfc_hdr hdr;
25be9b720aSTomas Winkler u8 sub_command;
26be9b720aSTomas Winkler u8 data[];
27be9b720aSTomas Winkler } __packed;
28be9b720aSTomas Winkler
29be9b720aSTomas Winkler struct mei_nfc_reply {
301d3ff767STomas Winkler struct mei_nfc_hdr hdr;
31be9b720aSTomas Winkler u8 sub_command;
32be9b720aSTomas Winkler u8 reply_status;
33be9b720aSTomas Winkler u8 data[];
34be9b720aSTomas Winkler } __packed;
35be9b720aSTomas Winkler
36be9b720aSTomas Winkler struct mei_nfc_if_version {
37be9b720aSTomas Winkler u8 radio_version_sw[3];
38be9b720aSTomas Winkler u8 reserved[3];
39be9b720aSTomas Winkler u8 radio_version_hw[3];
40be9b720aSTomas Winkler u8 i2c_addr;
41be9b720aSTomas Winkler u8 fw_ivn;
42be9b720aSTomas Winkler u8 vendor_id;
43be9b720aSTomas Winkler u8 radio_type;
44be9b720aSTomas Winkler } __packed;
45be9b720aSTomas Winkler
46be9b720aSTomas Winkler struct mei_nfc_connect {
47be9b720aSTomas Winkler u8 fw_ivn;
48be9b720aSTomas Winkler u8 vendor_id;
49be9b720aSTomas Winkler } __packed;
50be9b720aSTomas Winkler
51be9b720aSTomas Winkler struct mei_nfc_connect_resp {
52be9b720aSTomas Winkler u8 fw_ivn;
53be9b720aSTomas Winkler u8 vendor_id;
54be9b720aSTomas Winkler u16 me_major;
55be9b720aSTomas Winkler u16 me_minor;
56be9b720aSTomas Winkler u16 me_hotfix;
57be9b720aSTomas Winkler u16 me_build;
58be9b720aSTomas Winkler } __packed;
59be9b720aSTomas Winkler
604912e2feSEric Lapuyade
61be9b720aSTomas Winkler #define MEI_NFC_CMD_MAINTENANCE 0x00
62be9b720aSTomas Winkler #define MEI_NFC_CMD_HCI_SEND 0x01
63be9b720aSTomas Winkler #define MEI_NFC_CMD_HCI_RECV 0x02
64be9b720aSTomas Winkler
65be9b720aSTomas Winkler #define MEI_NFC_SUBCMD_CONNECT 0x00
66be9b720aSTomas Winkler #define MEI_NFC_SUBCMD_IF_VERSION 0x01
67be9b720aSTomas Winkler
684912e2feSEric Lapuyade #define MEI_NFC_MAX_READ (MEI_NFC_HEADER_SIZE + MEI_NFC_MAX_HCI_PAYLOAD)
694912e2feSEric Lapuyade
704912e2feSEric Lapuyade #define MEI_DUMP_SKB_IN(info, skb) \
714912e2feSEric Lapuyade do { \
724912e2feSEric Lapuyade pr_debug("%s:\n", info); \
734912e2feSEric Lapuyade print_hex_dump_debug("mei in : ", DUMP_PREFIX_OFFSET, \
744912e2feSEric Lapuyade 16, 1, (skb)->data, (skb)->len, false); \
754912e2feSEric Lapuyade } while (0)
764912e2feSEric Lapuyade
774912e2feSEric Lapuyade #define MEI_DUMP_SKB_OUT(info, skb) \
784912e2feSEric Lapuyade do { \
794912e2feSEric Lapuyade pr_debug("%s:\n", info); \
804912e2feSEric Lapuyade print_hex_dump_debug("mei out: ", DUMP_PREFIX_OFFSET, \
814912e2feSEric Lapuyade 16, 1, (skb)->data, (skb)->len, false); \
824912e2feSEric Lapuyade } while (0)
834912e2feSEric Lapuyade
841d3ff767STomas Winkler #define MEI_DUMP_NFC_HDR(info, _hdr) \
851d3ff767STomas Winkler do { \
861d3ff767STomas Winkler pr_debug("%s:\n", info); \
871d3ff767STomas Winkler pr_debug("cmd=%02d status=%d req_id=%d rsvd=%d size=%d\n", \
881d3ff767STomas Winkler (_hdr)->cmd, (_hdr)->status, (_hdr)->req_id, \
891d3ff767STomas Winkler (_hdr)->reserved, (_hdr)->data_size); \
901d3ff767STomas Winkler } while (0)
91be9b720aSTomas Winkler
mei_nfc_if_version(struct nfc_mei_phy * phy)92be9b720aSTomas Winkler static int mei_nfc_if_version(struct nfc_mei_phy *phy)
934912e2feSEric Lapuyade {
94be9b720aSTomas Winkler
95be9b720aSTomas Winkler struct mei_nfc_cmd cmd;
96be9b720aSTomas Winkler struct mei_nfc_reply *reply = NULL;
97be9b720aSTomas Winkler struct mei_nfc_if_version *version;
98be9b720aSTomas Winkler size_t if_version_length;
99be9b720aSTomas Winkler int bytes_recv, r;
1004912e2feSEric Lapuyade
101be9b720aSTomas Winkler memset(&cmd, 0, sizeof(struct mei_nfc_cmd));
1021d3ff767STomas Winkler cmd.hdr.cmd = MEI_NFC_CMD_MAINTENANCE;
1031d3ff767STomas Winkler cmd.hdr.data_size = 1;
104be9b720aSTomas Winkler cmd.sub_command = MEI_NFC_SUBCMD_IF_VERSION;
1054912e2feSEric Lapuyade
1061d3ff767STomas Winkler MEI_DUMP_NFC_HDR("version", &cmd.hdr);
107d49dc5e7STomas Winkler r = mei_cldev_send(phy->cldev, (u8 *)&cmd, sizeof(struct mei_nfc_cmd));
1084912e2feSEric Lapuyade if (r < 0) {
109be9b720aSTomas Winkler pr_err("Could not send IF version cmd\n");
1104912e2feSEric Lapuyade return r;
1114912e2feSEric Lapuyade }
1124912e2feSEric Lapuyade
113be9b720aSTomas Winkler /* to be sure on the stack we alloc memory */
114be9b720aSTomas Winkler if_version_length = sizeof(struct mei_nfc_reply) +
115be9b720aSTomas Winkler sizeof(struct mei_nfc_if_version);
11673f3adb9SSamuel Ortiz
117be9b720aSTomas Winkler reply = kzalloc(if_version_length, GFP_KERNEL);
118be9b720aSTomas Winkler if (!reply)
119be9b720aSTomas Winkler return -ENOMEM;
120be9b720aSTomas Winkler
121d49dc5e7STomas Winkler bytes_recv = mei_cldev_recv(phy->cldev, (u8 *)reply, if_version_length);
122582ab27aSAlexander Usyskin if (bytes_recv < 0 || bytes_recv < if_version_length) {
123be9b720aSTomas Winkler pr_err("Could not read IF version\n");
124be9b720aSTomas Winkler r = -EIO;
125be9b720aSTomas Winkler goto err;
126be9b720aSTomas Winkler }
127be9b720aSTomas Winkler
128be9b720aSTomas Winkler version = (struct mei_nfc_if_version *)reply->data;
129be9b720aSTomas Winkler
130be9b720aSTomas Winkler phy->fw_ivn = version->fw_ivn;
131be9b720aSTomas Winkler phy->vendor_id = version->vendor_id;
132be9b720aSTomas Winkler phy->radio_type = version->radio_type;
133be9b720aSTomas Winkler
134be9b720aSTomas Winkler err:
135be9b720aSTomas Winkler kfree(reply);
13673f3adb9SSamuel Ortiz return r;
13773f3adb9SSamuel Ortiz }
13873f3adb9SSamuel Ortiz
mei_nfc_connect(struct nfc_mei_phy * phy)139be9b720aSTomas Winkler static int mei_nfc_connect(struct nfc_mei_phy *phy)
1404912e2feSEric Lapuyade {
141be9b720aSTomas Winkler struct mei_nfc_cmd *cmd, *reply;
142be9b720aSTomas Winkler struct mei_nfc_connect *connect;
143be9b720aSTomas Winkler struct mei_nfc_connect_resp *connect_resp;
144be9b720aSTomas Winkler size_t connect_length, connect_resp_length;
145be9b720aSTomas Winkler int bytes_recv, r;
1464912e2feSEric Lapuyade
147be9b720aSTomas Winkler connect_length = sizeof(struct mei_nfc_cmd) +
148be9b720aSTomas Winkler sizeof(struct mei_nfc_connect);
1494912e2feSEric Lapuyade
150be9b720aSTomas Winkler connect_resp_length = sizeof(struct mei_nfc_cmd) +
151be9b720aSTomas Winkler sizeof(struct mei_nfc_connect_resp);
152be9b720aSTomas Winkler
153be9b720aSTomas Winkler cmd = kzalloc(connect_length, GFP_KERNEL);
154be9b720aSTomas Winkler if (!cmd)
155be9b720aSTomas Winkler return -ENOMEM;
156be9b720aSTomas Winkler connect = (struct mei_nfc_connect *)cmd->data;
157be9b720aSTomas Winkler
158be9b720aSTomas Winkler reply = kzalloc(connect_resp_length, GFP_KERNEL);
159be9b720aSTomas Winkler if (!reply) {
160be9b720aSTomas Winkler kfree(cmd);
161be9b720aSTomas Winkler return -ENOMEM;
1624912e2feSEric Lapuyade }
163be9b720aSTomas Winkler
164be9b720aSTomas Winkler connect_resp = (struct mei_nfc_connect_resp *)reply->data;
165be9b720aSTomas Winkler
1661d3ff767STomas Winkler cmd->hdr.cmd = MEI_NFC_CMD_MAINTENANCE;
1671d3ff767STomas Winkler cmd->hdr.data_size = 3;
168be9b720aSTomas Winkler cmd->sub_command = MEI_NFC_SUBCMD_CONNECT;
169be9b720aSTomas Winkler connect->fw_ivn = phy->fw_ivn;
170be9b720aSTomas Winkler connect->vendor_id = phy->vendor_id;
171be9b720aSTomas Winkler
1721d3ff767STomas Winkler MEI_DUMP_NFC_HDR("connect request", &cmd->hdr);
173d49dc5e7STomas Winkler r = mei_cldev_send(phy->cldev, (u8 *)cmd, connect_length);
174be9b720aSTomas Winkler if (r < 0) {
175be9b720aSTomas Winkler pr_err("Could not send connect cmd %d\n", r);
176be9b720aSTomas Winkler goto err;
177be9b720aSTomas Winkler }
178be9b720aSTomas Winkler
179d49dc5e7STomas Winkler bytes_recv = mei_cldev_recv(phy->cldev, (u8 *)reply,
180d49dc5e7STomas Winkler connect_resp_length);
181be9b720aSTomas Winkler if (bytes_recv < 0) {
182be9b720aSTomas Winkler r = bytes_recv;
183be9b720aSTomas Winkler pr_err("Could not read connect response %d\n", r);
184be9b720aSTomas Winkler goto err;
185be9b720aSTomas Winkler }
186be9b720aSTomas Winkler
1871d3ff767STomas Winkler MEI_DUMP_NFC_HDR("connect reply", &reply->hdr);
1881d3ff767STomas Winkler
189be9b720aSTomas Winkler pr_info("IVN 0x%x Vendor ID 0x%x\n",
190be9b720aSTomas Winkler connect_resp->fw_ivn, connect_resp->vendor_id);
191be9b720aSTomas Winkler
192be9b720aSTomas Winkler pr_info("ME FW %d.%d.%d.%d\n",
193be9b720aSTomas Winkler connect_resp->me_major, connect_resp->me_minor,
194be9b720aSTomas Winkler connect_resp->me_hotfix, connect_resp->me_build);
195be9b720aSTomas Winkler
196be9b720aSTomas Winkler r = 0;
197be9b720aSTomas Winkler
198be9b720aSTomas Winkler err:
199be9b720aSTomas Winkler kfree(reply);
200be9b720aSTomas Winkler kfree(cmd);
201be9b720aSTomas Winkler
202be9b720aSTomas Winkler return r;
203be9b720aSTomas Winkler }
204be9b720aSTomas Winkler
mei_nfc_send(struct nfc_mei_phy * phy,const u8 * buf,size_t length)205*894a6e15SKrzysztof Kozlowski static int mei_nfc_send(struct nfc_mei_phy *phy, const u8 *buf, size_t length)
206be9b720aSTomas Winkler {
2071d3ff767STomas Winkler struct mei_nfc_hdr *hdr;
208be9b720aSTomas Winkler u8 *mei_buf;
209be9b720aSTomas Winkler int err;
210be9b720aSTomas Winkler
211be9b720aSTomas Winkler err = -ENOMEM;
212be9b720aSTomas Winkler mei_buf = kzalloc(length + MEI_NFC_HEADER_SIZE, GFP_KERNEL);
213be9b720aSTomas Winkler if (!mei_buf)
214be9b720aSTomas Winkler goto out;
215be9b720aSTomas Winkler
2161d3ff767STomas Winkler hdr = (struct mei_nfc_hdr *)mei_buf;
217be9b720aSTomas Winkler hdr->cmd = MEI_NFC_CMD_HCI_SEND;
218be9b720aSTomas Winkler hdr->status = 0;
219be9b720aSTomas Winkler hdr->req_id = phy->req_id;
220be9b720aSTomas Winkler hdr->reserved = 0;
221be9b720aSTomas Winkler hdr->data_size = length;
222be9b720aSTomas Winkler
2231d3ff767STomas Winkler MEI_DUMP_NFC_HDR("send", hdr);
2241d3ff767STomas Winkler
225be9b720aSTomas Winkler memcpy(mei_buf + MEI_NFC_HEADER_SIZE, buf, length);
226d49dc5e7STomas Winkler err = mei_cldev_send(phy->cldev, mei_buf, length + MEI_NFC_HEADER_SIZE);
227be9b720aSTomas Winkler if (err < 0)
228be9b720aSTomas Winkler goto out;
229be9b720aSTomas Winkler
230be9b720aSTomas Winkler if (!wait_event_interruptible_timeout(phy->send_wq,
231be9b720aSTomas Winkler phy->recv_req_id == phy->req_id, HZ)) {
232be9b720aSTomas Winkler pr_err("NFC MEI command timeout\n");
233be9b720aSTomas Winkler err = -ETIME;
234be9b720aSTomas Winkler } else {
235be9b720aSTomas Winkler phy->req_id++;
236be9b720aSTomas Winkler }
237be9b720aSTomas Winkler out:
238be9b720aSTomas Winkler kfree(mei_buf);
239be9b720aSTomas Winkler return err;
240be9b720aSTomas Winkler }
2414912e2feSEric Lapuyade
2424912e2feSEric Lapuyade /*
2434912e2feSEric Lapuyade * Writing a frame must not return the number of written bytes.
2444912e2feSEric Lapuyade * It must return either zero for success, or <0 for error.
2454912e2feSEric Lapuyade * In addition, it must not alter the skb
2464912e2feSEric Lapuyade */
nfc_mei_phy_write(void * phy_id,struct sk_buff * skb)2474912e2feSEric Lapuyade static int nfc_mei_phy_write(void *phy_id, struct sk_buff *skb)
2484912e2feSEric Lapuyade {
2494912e2feSEric Lapuyade struct nfc_mei_phy *phy = phy_id;
2504912e2feSEric Lapuyade int r;
2514912e2feSEric Lapuyade
2524912e2feSEric Lapuyade MEI_DUMP_SKB_OUT("mei frame sent", skb);
2534912e2feSEric Lapuyade
254be9b720aSTomas Winkler r = mei_nfc_send(phy, skb->data, skb->len);
2554912e2feSEric Lapuyade if (r > 0)
2564912e2feSEric Lapuyade r = 0;
2574912e2feSEric Lapuyade
2584912e2feSEric Lapuyade return r;
2594912e2feSEric Lapuyade }
2604912e2feSEric Lapuyade
mei_nfc_recv(struct nfc_mei_phy * phy,u8 * buf,size_t length)261be9b720aSTomas Winkler static int mei_nfc_recv(struct nfc_mei_phy *phy, u8 *buf, size_t length)
262be9b720aSTomas Winkler {
2631d3ff767STomas Winkler struct mei_nfc_hdr *hdr;
264be9b720aSTomas Winkler int received_length;
265be9b720aSTomas Winkler
266d49dc5e7STomas Winkler received_length = mei_cldev_recv(phy->cldev, buf, length);
267be9b720aSTomas Winkler if (received_length < 0)
268be9b720aSTomas Winkler return received_length;
269be9b720aSTomas Winkler
2701d3ff767STomas Winkler hdr = (struct mei_nfc_hdr *) buf;
271be9b720aSTomas Winkler
2721d3ff767STomas Winkler MEI_DUMP_NFC_HDR("receive", hdr);
2731d3ff767STomas Winkler if (hdr->cmd == MEI_NFC_CMD_HCI_SEND) {
2741d3ff767STomas Winkler phy->recv_req_id = hdr->req_id;
275be9b720aSTomas Winkler wake_up(&phy->send_wq);
276be9b720aSTomas Winkler
277be9b720aSTomas Winkler return 0;
278be9b720aSTomas Winkler }
279be9b720aSTomas Winkler
280be9b720aSTomas Winkler return received_length;
281be9b720aSTomas Winkler }
282be9b720aSTomas Winkler
283be9b720aSTomas Winkler
nfc_mei_rx_cb(struct mei_cl_device * cldev)2847c7a6077SAlexander Usyskin static void nfc_mei_rx_cb(struct mei_cl_device *cldev)
2854912e2feSEric Lapuyade {
286972cedf6STomas Winkler struct nfc_mei_phy *phy = mei_cldev_get_drvdata(cldev);
2877c7a6077SAlexander Usyskin struct sk_buff *skb;
2887c7a6077SAlexander Usyskin int reply_size;
289972cedf6STomas Winkler
290972cedf6STomas Winkler if (!phy)
291972cedf6STomas Winkler return;
2924912e2feSEric Lapuyade
2934912e2feSEric Lapuyade if (phy->hard_fault != 0)
2944912e2feSEric Lapuyade return;
2954912e2feSEric Lapuyade
2964912e2feSEric Lapuyade skb = alloc_skb(MEI_NFC_MAX_READ, GFP_KERNEL);
2974912e2feSEric Lapuyade if (!skb)
2984912e2feSEric Lapuyade return;
2994912e2feSEric Lapuyade
300be9b720aSTomas Winkler reply_size = mei_nfc_recv(phy, skb->data, MEI_NFC_MAX_READ);
3014912e2feSEric Lapuyade if (reply_size < MEI_NFC_HEADER_SIZE) {
302fe0219ccSSalil Kapur kfree_skb(skb);
3034912e2feSEric Lapuyade return;
3044912e2feSEric Lapuyade }
3054912e2feSEric Lapuyade
3064912e2feSEric Lapuyade skb_put(skb, reply_size);
3074912e2feSEric Lapuyade skb_pull(skb, MEI_NFC_HEADER_SIZE);
3084912e2feSEric Lapuyade
3094912e2feSEric Lapuyade MEI_DUMP_SKB_IN("mei frame read", skb);
3104912e2feSEric Lapuyade
3114912e2feSEric Lapuyade nfc_hci_recv_frame(phy->hdev, skb);
3124912e2feSEric Lapuyade }
313be9b720aSTomas Winkler
nfc_mei_phy_enable(void * phy_id)314be9b720aSTomas Winkler static int nfc_mei_phy_enable(void *phy_id)
315be9b720aSTomas Winkler {
316be9b720aSTomas Winkler int r;
317be9b720aSTomas Winkler struct nfc_mei_phy *phy = phy_id;
318be9b720aSTomas Winkler
319be9b720aSTomas Winkler if (phy->powered == 1)
320be9b720aSTomas Winkler return 0;
321be9b720aSTomas Winkler
322d49dc5e7STomas Winkler r = mei_cldev_enable(phy->cldev);
323be9b720aSTomas Winkler if (r < 0) {
324be9b720aSTomas Winkler pr_err("Could not enable device %d\n", r);
325be9b720aSTomas Winkler return r;
326be9b720aSTomas Winkler }
327be9b720aSTomas Winkler
328be9b720aSTomas Winkler r = mei_nfc_if_version(phy);
329be9b720aSTomas Winkler if (r < 0) {
330be9b720aSTomas Winkler pr_err("Could not enable device %d\n", r);
331be9b720aSTomas Winkler goto err;
332be9b720aSTomas Winkler }
333be9b720aSTomas Winkler
334be9b720aSTomas Winkler r = mei_nfc_connect(phy);
335be9b720aSTomas Winkler if (r < 0) {
336be9b720aSTomas Winkler pr_err("Could not connect to device %d\n", r);
337be9b720aSTomas Winkler goto err;
338be9b720aSTomas Winkler }
339be9b720aSTomas Winkler
3407c7a6077SAlexander Usyskin r = mei_cldev_register_rx_cb(phy->cldev, nfc_mei_rx_cb);
341be9b720aSTomas Winkler if (r) {
342be9b720aSTomas Winkler pr_err("Event cb registration failed %d\n", r);
343be9b720aSTomas Winkler goto err;
344be9b720aSTomas Winkler }
345be9b720aSTomas Winkler
346be9b720aSTomas Winkler phy->powered = 1;
347be9b720aSTomas Winkler
348be9b720aSTomas Winkler return 0;
349be9b720aSTomas Winkler
350be9b720aSTomas Winkler err:
351be9b720aSTomas Winkler phy->powered = 0;
352d49dc5e7STomas Winkler mei_cldev_disable(phy->cldev);
353be9b720aSTomas Winkler return r;
354be9b720aSTomas Winkler }
355be9b720aSTomas Winkler
nfc_mei_phy_disable(void * phy_id)356be9b720aSTomas Winkler static void nfc_mei_phy_disable(void *phy_id)
357be9b720aSTomas Winkler {
358be9b720aSTomas Winkler struct nfc_mei_phy *phy = phy_id;
359be9b720aSTomas Winkler
360d49dc5e7STomas Winkler mei_cldev_disable(phy->cldev);
361be9b720aSTomas Winkler
362be9b720aSTomas Winkler phy->powered = 0;
363be9b720aSTomas Winkler }
3644912e2feSEric Lapuyade
3657a5e98daSKrzysztof Kozlowski const struct nfc_phy_ops mei_phy_ops = {
3664912e2feSEric Lapuyade .write = nfc_mei_phy_write,
3674912e2feSEric Lapuyade .enable = nfc_mei_phy_enable,
3684912e2feSEric Lapuyade .disable = nfc_mei_phy_disable,
3694912e2feSEric Lapuyade };
3704912e2feSEric Lapuyade EXPORT_SYMBOL_GPL(mei_phy_ops);
3714912e2feSEric Lapuyade
nfc_mei_phy_alloc(struct mei_cl_device * cldev)37289391382STomas Winkler struct nfc_mei_phy *nfc_mei_phy_alloc(struct mei_cl_device *cldev)
3734912e2feSEric Lapuyade {
3744912e2feSEric Lapuyade struct nfc_mei_phy *phy;
3754912e2feSEric Lapuyade
3764912e2feSEric Lapuyade phy = kzalloc(sizeof(struct nfc_mei_phy), GFP_KERNEL);
3774912e2feSEric Lapuyade if (!phy)
3784912e2feSEric Lapuyade return NULL;
3794912e2feSEric Lapuyade
38089391382STomas Winkler phy->cldev = cldev;
381be9b720aSTomas Winkler init_waitqueue_head(&phy->send_wq);
382d49dc5e7STomas Winkler mei_cldev_set_drvdata(cldev, phy);
3834912e2feSEric Lapuyade
3844912e2feSEric Lapuyade return phy;
3854912e2feSEric Lapuyade }
3864912e2feSEric Lapuyade EXPORT_SYMBOL_GPL(nfc_mei_phy_alloc);
3874912e2feSEric Lapuyade
nfc_mei_phy_free(struct nfc_mei_phy * phy)3884912e2feSEric Lapuyade void nfc_mei_phy_free(struct nfc_mei_phy *phy)
3894912e2feSEric Lapuyade {
390d49dc5e7STomas Winkler mei_cldev_disable(phy->cldev);
3914912e2feSEric Lapuyade kfree(phy);
3924912e2feSEric Lapuyade }
3934912e2feSEric Lapuyade EXPORT_SYMBOL_GPL(nfc_mei_phy_free);
3944912e2feSEric Lapuyade
3954912e2feSEric Lapuyade MODULE_LICENSE("GPL");
3964912e2feSEric Lapuyade MODULE_DESCRIPTION("mei bus NFC device interface");
397