xref: /kernel/linux/linux-6.6/drivers/nfc/mei_phy.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2013, Intel Corporation.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * MEI Library for mei bus nfc device access
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/slab.h>
1162306a36Sopenharmony_ci#include <linux/nfc.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include "mei_phy.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_cistruct mei_nfc_hdr {
1662306a36Sopenharmony_ci	u8 cmd;
1762306a36Sopenharmony_ci	u8 status;
1862306a36Sopenharmony_ci	u16 req_id;
1962306a36Sopenharmony_ci	u32 reserved;
2062306a36Sopenharmony_ci	u16 data_size;
2162306a36Sopenharmony_ci} __packed;
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistruct mei_nfc_cmd {
2462306a36Sopenharmony_ci	struct mei_nfc_hdr hdr;
2562306a36Sopenharmony_ci	u8 sub_command;
2662306a36Sopenharmony_ci	u8 data[];
2762306a36Sopenharmony_ci} __packed;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistruct mei_nfc_reply {
3062306a36Sopenharmony_ci	struct mei_nfc_hdr hdr;
3162306a36Sopenharmony_ci	u8 sub_command;
3262306a36Sopenharmony_ci	u8 reply_status;
3362306a36Sopenharmony_ci	u8 data[];
3462306a36Sopenharmony_ci} __packed;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistruct mei_nfc_if_version {
3762306a36Sopenharmony_ci	u8 radio_version_sw[3];
3862306a36Sopenharmony_ci	u8 reserved[3];
3962306a36Sopenharmony_ci	u8 radio_version_hw[3];
4062306a36Sopenharmony_ci	u8 i2c_addr;
4162306a36Sopenharmony_ci	u8 fw_ivn;
4262306a36Sopenharmony_ci	u8 vendor_id;
4362306a36Sopenharmony_ci	u8 radio_type;
4462306a36Sopenharmony_ci} __packed;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistruct mei_nfc_connect {
4762306a36Sopenharmony_ci	u8 fw_ivn;
4862306a36Sopenharmony_ci	u8 vendor_id;
4962306a36Sopenharmony_ci} __packed;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistruct mei_nfc_connect_resp {
5262306a36Sopenharmony_ci	u8 fw_ivn;
5362306a36Sopenharmony_ci	u8 vendor_id;
5462306a36Sopenharmony_ci	u16 me_major;
5562306a36Sopenharmony_ci	u16 me_minor;
5662306a36Sopenharmony_ci	u16 me_hotfix;
5762306a36Sopenharmony_ci	u16 me_build;
5862306a36Sopenharmony_ci} __packed;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci#define MEI_NFC_CMD_MAINTENANCE 0x00
6262306a36Sopenharmony_ci#define MEI_NFC_CMD_HCI_SEND 0x01
6362306a36Sopenharmony_ci#define MEI_NFC_CMD_HCI_RECV 0x02
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci#define MEI_NFC_SUBCMD_CONNECT    0x00
6662306a36Sopenharmony_ci#define MEI_NFC_SUBCMD_IF_VERSION 0x01
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci#define MEI_NFC_MAX_READ (MEI_NFC_HEADER_SIZE + MEI_NFC_MAX_HCI_PAYLOAD)
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci#define MEI_DUMP_SKB_IN(info, skb)				\
7162306a36Sopenharmony_cido {								\
7262306a36Sopenharmony_ci	pr_debug("%s:\n", info);				\
7362306a36Sopenharmony_ci	print_hex_dump_debug("mei in : ", DUMP_PREFIX_OFFSET,	\
7462306a36Sopenharmony_ci			16, 1, (skb)->data, (skb)->len, false);	\
7562306a36Sopenharmony_ci} while (0)
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci#define MEI_DUMP_SKB_OUT(info, skb)				\
7862306a36Sopenharmony_cido {								\
7962306a36Sopenharmony_ci	pr_debug("%s:\n", info);				\
8062306a36Sopenharmony_ci	print_hex_dump_debug("mei out: ", DUMP_PREFIX_OFFSET,	\
8162306a36Sopenharmony_ci			16, 1, (skb)->data, (skb)->len, false);	\
8262306a36Sopenharmony_ci} while (0)
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci#define MEI_DUMP_NFC_HDR(info, _hdr)                                \
8562306a36Sopenharmony_cido {                                                                \
8662306a36Sopenharmony_ci	pr_debug("%s:\n", info);                                    \
8762306a36Sopenharmony_ci	pr_debug("cmd=%02d status=%d req_id=%d rsvd=%d size=%d\n",  \
8862306a36Sopenharmony_ci		 (_hdr)->cmd, (_hdr)->status, (_hdr)->req_id,       \
8962306a36Sopenharmony_ci		 (_hdr)->reserved, (_hdr)->data_size);              \
9062306a36Sopenharmony_ci} while (0)
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic int mei_nfc_if_version(struct nfc_mei_phy *phy)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	struct mei_nfc_cmd cmd;
9662306a36Sopenharmony_ci	struct mei_nfc_reply *reply = NULL;
9762306a36Sopenharmony_ci	struct mei_nfc_if_version *version;
9862306a36Sopenharmony_ci	size_t if_version_length;
9962306a36Sopenharmony_ci	int bytes_recv, r;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	memset(&cmd, 0, sizeof(struct mei_nfc_cmd));
10262306a36Sopenharmony_ci	cmd.hdr.cmd = MEI_NFC_CMD_MAINTENANCE;
10362306a36Sopenharmony_ci	cmd.hdr.data_size = 1;
10462306a36Sopenharmony_ci	cmd.sub_command = MEI_NFC_SUBCMD_IF_VERSION;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	MEI_DUMP_NFC_HDR("version", &cmd.hdr);
10762306a36Sopenharmony_ci	r = mei_cldev_send(phy->cldev, (u8 *)&cmd, sizeof(struct mei_nfc_cmd));
10862306a36Sopenharmony_ci	if (r < 0) {
10962306a36Sopenharmony_ci		pr_err("Could not send IF version cmd\n");
11062306a36Sopenharmony_ci		return r;
11162306a36Sopenharmony_ci	}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	/* to be sure on the stack we alloc memory */
11462306a36Sopenharmony_ci	if_version_length = sizeof(struct mei_nfc_reply) +
11562306a36Sopenharmony_ci		sizeof(struct mei_nfc_if_version);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	reply = kzalloc(if_version_length, GFP_KERNEL);
11862306a36Sopenharmony_ci	if (!reply)
11962306a36Sopenharmony_ci		return -ENOMEM;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	bytes_recv = mei_cldev_recv(phy->cldev, (u8 *)reply, if_version_length);
12262306a36Sopenharmony_ci	if (bytes_recv < 0 || bytes_recv < if_version_length) {
12362306a36Sopenharmony_ci		pr_err("Could not read IF version\n");
12462306a36Sopenharmony_ci		r = -EIO;
12562306a36Sopenharmony_ci		goto err;
12662306a36Sopenharmony_ci	}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	version = (struct mei_nfc_if_version *)reply->data;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	phy->fw_ivn = version->fw_ivn;
13162306a36Sopenharmony_ci	phy->vendor_id = version->vendor_id;
13262306a36Sopenharmony_ci	phy->radio_type = version->radio_type;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_cierr:
13562306a36Sopenharmony_ci	kfree(reply);
13662306a36Sopenharmony_ci	return r;
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistatic int mei_nfc_connect(struct nfc_mei_phy *phy)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	struct mei_nfc_cmd *cmd, *reply;
14262306a36Sopenharmony_ci	struct mei_nfc_connect *connect;
14362306a36Sopenharmony_ci	struct mei_nfc_connect_resp *connect_resp;
14462306a36Sopenharmony_ci	size_t connect_length, connect_resp_length;
14562306a36Sopenharmony_ci	int bytes_recv, r;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	connect_length = sizeof(struct mei_nfc_cmd) +
14862306a36Sopenharmony_ci			sizeof(struct mei_nfc_connect);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	connect_resp_length = sizeof(struct mei_nfc_cmd) +
15162306a36Sopenharmony_ci			sizeof(struct mei_nfc_connect_resp);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	cmd = kzalloc(connect_length, GFP_KERNEL);
15462306a36Sopenharmony_ci	if (!cmd)
15562306a36Sopenharmony_ci		return -ENOMEM;
15662306a36Sopenharmony_ci	connect = (struct mei_nfc_connect *)cmd->data;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	reply = kzalloc(connect_resp_length, GFP_KERNEL);
15962306a36Sopenharmony_ci	if (!reply) {
16062306a36Sopenharmony_ci		kfree(cmd);
16162306a36Sopenharmony_ci		return -ENOMEM;
16262306a36Sopenharmony_ci	}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	connect_resp = (struct mei_nfc_connect_resp *)reply->data;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	cmd->hdr.cmd = MEI_NFC_CMD_MAINTENANCE;
16762306a36Sopenharmony_ci	cmd->hdr.data_size = 3;
16862306a36Sopenharmony_ci	cmd->sub_command = MEI_NFC_SUBCMD_CONNECT;
16962306a36Sopenharmony_ci	connect->fw_ivn = phy->fw_ivn;
17062306a36Sopenharmony_ci	connect->vendor_id = phy->vendor_id;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	MEI_DUMP_NFC_HDR("connect request", &cmd->hdr);
17362306a36Sopenharmony_ci	r = mei_cldev_send(phy->cldev, (u8 *)cmd, connect_length);
17462306a36Sopenharmony_ci	if (r < 0) {
17562306a36Sopenharmony_ci		pr_err("Could not send connect cmd %d\n", r);
17662306a36Sopenharmony_ci		goto err;
17762306a36Sopenharmony_ci	}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	bytes_recv = mei_cldev_recv(phy->cldev, (u8 *)reply,
18062306a36Sopenharmony_ci				    connect_resp_length);
18162306a36Sopenharmony_ci	if (bytes_recv < 0) {
18262306a36Sopenharmony_ci		r = bytes_recv;
18362306a36Sopenharmony_ci		pr_err("Could not read connect response %d\n", r);
18462306a36Sopenharmony_ci		goto err;
18562306a36Sopenharmony_ci	}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	MEI_DUMP_NFC_HDR("connect reply", &reply->hdr);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	pr_info("IVN 0x%x Vendor ID 0x%x\n",
19062306a36Sopenharmony_ci		 connect_resp->fw_ivn, connect_resp->vendor_id);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	pr_info("ME FW %d.%d.%d.%d\n",
19362306a36Sopenharmony_ci		connect_resp->me_major, connect_resp->me_minor,
19462306a36Sopenharmony_ci		connect_resp->me_hotfix, connect_resp->me_build);
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	r = 0;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_cierr:
19962306a36Sopenharmony_ci	kfree(reply);
20062306a36Sopenharmony_ci	kfree(cmd);
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	return r;
20362306a36Sopenharmony_ci}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_cistatic int mei_nfc_send(struct nfc_mei_phy *phy, const u8 *buf, size_t length)
20662306a36Sopenharmony_ci{
20762306a36Sopenharmony_ci	struct mei_nfc_hdr *hdr;
20862306a36Sopenharmony_ci	u8 *mei_buf;
20962306a36Sopenharmony_ci	int err;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	err = -ENOMEM;
21262306a36Sopenharmony_ci	mei_buf = kzalloc(length + MEI_NFC_HEADER_SIZE, GFP_KERNEL);
21362306a36Sopenharmony_ci	if (!mei_buf)
21462306a36Sopenharmony_ci		goto out;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	hdr = (struct mei_nfc_hdr *)mei_buf;
21762306a36Sopenharmony_ci	hdr->cmd = MEI_NFC_CMD_HCI_SEND;
21862306a36Sopenharmony_ci	hdr->status = 0;
21962306a36Sopenharmony_ci	hdr->req_id = phy->req_id;
22062306a36Sopenharmony_ci	hdr->reserved = 0;
22162306a36Sopenharmony_ci	hdr->data_size = length;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	MEI_DUMP_NFC_HDR("send", hdr);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	memcpy(mei_buf + MEI_NFC_HEADER_SIZE, buf, length);
22662306a36Sopenharmony_ci	err = mei_cldev_send(phy->cldev, mei_buf, length + MEI_NFC_HEADER_SIZE);
22762306a36Sopenharmony_ci	if (err < 0)
22862306a36Sopenharmony_ci		goto out;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	if (!wait_event_interruptible_timeout(phy->send_wq,
23162306a36Sopenharmony_ci				phy->recv_req_id == phy->req_id, HZ)) {
23262306a36Sopenharmony_ci		pr_err("NFC MEI command timeout\n");
23362306a36Sopenharmony_ci		err = -ETIME;
23462306a36Sopenharmony_ci	} else {
23562306a36Sopenharmony_ci		phy->req_id++;
23662306a36Sopenharmony_ci	}
23762306a36Sopenharmony_ciout:
23862306a36Sopenharmony_ci	kfree(mei_buf);
23962306a36Sopenharmony_ci	return err;
24062306a36Sopenharmony_ci}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci/*
24362306a36Sopenharmony_ci * Writing a frame must not return the number of written bytes.
24462306a36Sopenharmony_ci * It must return either zero for success, or <0 for error.
24562306a36Sopenharmony_ci * In addition, it must not alter the skb
24662306a36Sopenharmony_ci */
24762306a36Sopenharmony_cistatic int nfc_mei_phy_write(void *phy_id, struct sk_buff *skb)
24862306a36Sopenharmony_ci{
24962306a36Sopenharmony_ci	struct nfc_mei_phy *phy = phy_id;
25062306a36Sopenharmony_ci	int r;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	MEI_DUMP_SKB_OUT("mei frame sent", skb);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	r = mei_nfc_send(phy, skb->data, skb->len);
25562306a36Sopenharmony_ci	if (r > 0)
25662306a36Sopenharmony_ci		r = 0;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	return r;
25962306a36Sopenharmony_ci}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_cistatic int mei_nfc_recv(struct nfc_mei_phy *phy, u8 *buf, size_t length)
26262306a36Sopenharmony_ci{
26362306a36Sopenharmony_ci	struct mei_nfc_hdr *hdr;
26462306a36Sopenharmony_ci	int received_length;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	received_length = mei_cldev_recv(phy->cldev, buf, length);
26762306a36Sopenharmony_ci	if (received_length < 0)
26862306a36Sopenharmony_ci		return received_length;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	hdr = (struct mei_nfc_hdr *) buf;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	MEI_DUMP_NFC_HDR("receive", hdr);
27362306a36Sopenharmony_ci	if (hdr->cmd == MEI_NFC_CMD_HCI_SEND) {
27462306a36Sopenharmony_ci		phy->recv_req_id = hdr->req_id;
27562306a36Sopenharmony_ci		wake_up(&phy->send_wq);
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci		return 0;
27862306a36Sopenharmony_ci	}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	return received_length;
28162306a36Sopenharmony_ci}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_cistatic void nfc_mei_rx_cb(struct mei_cl_device *cldev)
28562306a36Sopenharmony_ci{
28662306a36Sopenharmony_ci	struct nfc_mei_phy *phy = mei_cldev_get_drvdata(cldev);
28762306a36Sopenharmony_ci	struct sk_buff *skb;
28862306a36Sopenharmony_ci	int reply_size;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	if (!phy)
29162306a36Sopenharmony_ci		return;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	if (phy->hard_fault != 0)
29462306a36Sopenharmony_ci		return;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	skb = alloc_skb(MEI_NFC_MAX_READ, GFP_KERNEL);
29762306a36Sopenharmony_ci	if (!skb)
29862306a36Sopenharmony_ci		return;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	reply_size = mei_nfc_recv(phy, skb->data, MEI_NFC_MAX_READ);
30162306a36Sopenharmony_ci	if (reply_size < MEI_NFC_HEADER_SIZE) {
30262306a36Sopenharmony_ci		kfree_skb(skb);
30362306a36Sopenharmony_ci		return;
30462306a36Sopenharmony_ci	}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	skb_put(skb, reply_size);
30762306a36Sopenharmony_ci	skb_pull(skb, MEI_NFC_HEADER_SIZE);
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	MEI_DUMP_SKB_IN("mei frame read", skb);
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	nfc_hci_recv_frame(phy->hdev, skb);
31262306a36Sopenharmony_ci}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_cistatic int nfc_mei_phy_enable(void *phy_id)
31562306a36Sopenharmony_ci{
31662306a36Sopenharmony_ci	int r;
31762306a36Sopenharmony_ci	struct nfc_mei_phy *phy = phy_id;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	if (phy->powered == 1)
32062306a36Sopenharmony_ci		return 0;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	r = mei_cldev_enable(phy->cldev);
32362306a36Sopenharmony_ci	if (r < 0) {
32462306a36Sopenharmony_ci		pr_err("Could not enable device %d\n", r);
32562306a36Sopenharmony_ci		return r;
32662306a36Sopenharmony_ci	}
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	r = mei_nfc_if_version(phy);
32962306a36Sopenharmony_ci	if (r < 0) {
33062306a36Sopenharmony_ci		pr_err("Could not enable device %d\n", r);
33162306a36Sopenharmony_ci		goto err;
33262306a36Sopenharmony_ci	}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	r = mei_nfc_connect(phy);
33562306a36Sopenharmony_ci	if (r < 0) {
33662306a36Sopenharmony_ci		pr_err("Could not connect to device %d\n", r);
33762306a36Sopenharmony_ci		goto err;
33862306a36Sopenharmony_ci	}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	r = mei_cldev_register_rx_cb(phy->cldev, nfc_mei_rx_cb);
34162306a36Sopenharmony_ci	if (r) {
34262306a36Sopenharmony_ci		pr_err("Event cb registration failed %d\n", r);
34362306a36Sopenharmony_ci		goto err;
34462306a36Sopenharmony_ci	}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	phy->powered = 1;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	return 0;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_cierr:
35162306a36Sopenharmony_ci	phy->powered = 0;
35262306a36Sopenharmony_ci	mei_cldev_disable(phy->cldev);
35362306a36Sopenharmony_ci	return r;
35462306a36Sopenharmony_ci}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_cistatic void nfc_mei_phy_disable(void *phy_id)
35762306a36Sopenharmony_ci{
35862306a36Sopenharmony_ci	struct nfc_mei_phy *phy = phy_id;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	mei_cldev_disable(phy->cldev);
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	phy->powered = 0;
36362306a36Sopenharmony_ci}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ciconst struct nfc_phy_ops mei_phy_ops = {
36662306a36Sopenharmony_ci	.write = nfc_mei_phy_write,
36762306a36Sopenharmony_ci	.enable = nfc_mei_phy_enable,
36862306a36Sopenharmony_ci	.disable = nfc_mei_phy_disable,
36962306a36Sopenharmony_ci};
37062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mei_phy_ops);
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_cistruct nfc_mei_phy *nfc_mei_phy_alloc(struct mei_cl_device *cldev)
37362306a36Sopenharmony_ci{
37462306a36Sopenharmony_ci	struct nfc_mei_phy *phy;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	phy = kzalloc(sizeof(struct nfc_mei_phy), GFP_KERNEL);
37762306a36Sopenharmony_ci	if (!phy)
37862306a36Sopenharmony_ci		return NULL;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	phy->cldev = cldev;
38162306a36Sopenharmony_ci	init_waitqueue_head(&phy->send_wq);
38262306a36Sopenharmony_ci	mei_cldev_set_drvdata(cldev, phy);
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	return phy;
38562306a36Sopenharmony_ci}
38662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfc_mei_phy_alloc);
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_civoid nfc_mei_phy_free(struct nfc_mei_phy *phy)
38962306a36Sopenharmony_ci{
39062306a36Sopenharmony_ci	mei_cldev_disable(phy->cldev);
39162306a36Sopenharmony_ci	kfree(phy);
39262306a36Sopenharmony_ci}
39362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfc_mei_phy_free);
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
39662306a36Sopenharmony_ciMODULE_DESCRIPTION("mei bus NFC device interface");
397