18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/* -------------------------------------------------------------------------
38c2ecf20Sopenharmony_ci * Copyright (C) 2014-2016, Intel Corporation
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * -------------------------------------------------------------------------
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/module.h>
98c2ecf20Sopenharmony_ci#include <linux/nfc.h>
108c2ecf20Sopenharmony_ci#include <linux/i2c.h>
118c2ecf20Sopenharmony_ci#include <linux/delay.h>
128c2ecf20Sopenharmony_ci#include <linux/firmware.h>
138c2ecf20Sopenharmony_ci#include <net/nfc/nci_core.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include "fdp.h"
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#define FDP_OTP_PATCH_NAME			"otp.bin"
188c2ecf20Sopenharmony_ci#define FDP_RAM_PATCH_NAME			"ram.bin"
198c2ecf20Sopenharmony_ci#define FDP_FW_HEADER_SIZE			576
208c2ecf20Sopenharmony_ci#define FDP_FW_UPDATE_SLEEP			1000
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#define NCI_GET_VERSION_TIMEOUT			8000
238c2ecf20Sopenharmony_ci#define NCI_PATCH_REQUEST_TIMEOUT		8000
248c2ecf20Sopenharmony_ci#define FDP_PATCH_CONN_DEST			0xC2
258c2ecf20Sopenharmony_ci#define FDP_PATCH_CONN_PARAM_TYPE		0xA0
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#define NCI_PATCH_TYPE_RAM			0x00
288c2ecf20Sopenharmony_ci#define NCI_PATCH_TYPE_OTP			0x01
298c2ecf20Sopenharmony_ci#define NCI_PATCH_TYPE_EOT			0xFF
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#define NCI_PARAM_ID_FW_RAM_VERSION		0xA0
328c2ecf20Sopenharmony_ci#define NCI_PARAM_ID_FW_OTP_VERSION		0xA1
338c2ecf20Sopenharmony_ci#define NCI_PARAM_ID_OTP_LIMITED_VERSION	0xC5
348c2ecf20Sopenharmony_ci#define NCI_PARAM_ID_KEY_INDEX_ID		0xC6
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#define NCI_GID_PROP				0x0F
378c2ecf20Sopenharmony_ci#define NCI_OP_PROP_PATCH_OID			0x08
388c2ecf20Sopenharmony_ci#define NCI_OP_PROP_SET_PDATA_OID		0x23
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistruct fdp_nci_info {
418c2ecf20Sopenharmony_ci	struct nfc_phy_ops *phy_ops;
428c2ecf20Sopenharmony_ci	struct fdp_i2c_phy *phy;
438c2ecf20Sopenharmony_ci	struct nci_dev *ndev;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	const struct firmware *otp_patch;
468c2ecf20Sopenharmony_ci	const struct firmware *ram_patch;
478c2ecf20Sopenharmony_ci	u32 otp_patch_version;
488c2ecf20Sopenharmony_ci	u32 ram_patch_version;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	u32 otp_version;
518c2ecf20Sopenharmony_ci	u32 ram_version;
528c2ecf20Sopenharmony_ci	u32 limited_otp_version;
538c2ecf20Sopenharmony_ci	u8 key_index;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	u8 *fw_vsc_cfg;
568c2ecf20Sopenharmony_ci	u8 clock_type;
578c2ecf20Sopenharmony_ci	u32 clock_freq;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	atomic_t data_pkt_counter;
608c2ecf20Sopenharmony_ci	void (*data_pkt_counter_cb)(struct nci_dev *ndev);
618c2ecf20Sopenharmony_ci	u8 setup_patch_sent;
628c2ecf20Sopenharmony_ci	u8 setup_patch_ntf;
638c2ecf20Sopenharmony_ci	u8 setup_patch_status;
648c2ecf20Sopenharmony_ci	u8 setup_reset_ntf;
658c2ecf20Sopenharmony_ci	wait_queue_head_t setup_wq;
668c2ecf20Sopenharmony_ci};
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic u8 nci_core_get_config_otp_ram_version[5] = {
698c2ecf20Sopenharmony_ci	0x04,
708c2ecf20Sopenharmony_ci	NCI_PARAM_ID_FW_RAM_VERSION,
718c2ecf20Sopenharmony_ci	NCI_PARAM_ID_FW_OTP_VERSION,
728c2ecf20Sopenharmony_ci	NCI_PARAM_ID_OTP_LIMITED_VERSION,
738c2ecf20Sopenharmony_ci	NCI_PARAM_ID_KEY_INDEX_ID
748c2ecf20Sopenharmony_ci};
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistruct nci_core_get_config_rsp {
778c2ecf20Sopenharmony_ci	u8 status;
788c2ecf20Sopenharmony_ci	u8 count;
798c2ecf20Sopenharmony_ci	u8 data[];
808c2ecf20Sopenharmony_ci};
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic int fdp_nci_create_conn(struct nci_dev *ndev)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	struct fdp_nci_info *info = nci_get_drvdata(ndev);
858c2ecf20Sopenharmony_ci	struct core_conn_create_dest_spec_params param;
868c2ecf20Sopenharmony_ci	int r;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	/* proprietary destination specific paramerer without value */
898c2ecf20Sopenharmony_ci	param.type = FDP_PATCH_CONN_PARAM_TYPE;
908c2ecf20Sopenharmony_ci	param.length = 0x00;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	r = nci_core_conn_create(info->ndev, FDP_PATCH_CONN_DEST, 1,
938c2ecf20Sopenharmony_ci				 sizeof(param), &param);
948c2ecf20Sopenharmony_ci	if (r)
958c2ecf20Sopenharmony_ci		return r;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	return nci_get_conn_info_by_dest_type_params(ndev,
988c2ecf20Sopenharmony_ci						     FDP_PATCH_CONN_DEST, NULL);
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic inline int fdp_nci_get_versions(struct nci_dev *ndev)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	return nci_core_cmd(ndev, NCI_OP_CORE_GET_CONFIG_CMD,
1048c2ecf20Sopenharmony_ci			    sizeof(nci_core_get_config_otp_ram_version),
1058c2ecf20Sopenharmony_ci			    (__u8 *) &nci_core_get_config_otp_ram_version);
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_cistatic inline int fdp_nci_patch_cmd(struct nci_dev *ndev, u8 type)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	return nci_prop_cmd(ndev, NCI_OP_PROP_PATCH_OID, sizeof(type), &type);
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cistatic inline int fdp_nci_set_production_data(struct nci_dev *ndev, u8 len,
1148c2ecf20Sopenharmony_ci					      char *data)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	return nci_prop_cmd(ndev, NCI_OP_PROP_SET_PDATA_OID, len, data);
1178c2ecf20Sopenharmony_ci}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cistatic int fdp_nci_set_clock(struct nci_dev *ndev, u8 clock_type,
1208c2ecf20Sopenharmony_ci			     u32 clock_freq)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	u32 fc = 13560;
1238c2ecf20Sopenharmony_ci	u32 nd, num, delta;
1248c2ecf20Sopenharmony_ci	char data[9];
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	nd = (24 * fc) / clock_freq;
1278c2ecf20Sopenharmony_ci	delta = 24 * fc - nd * clock_freq;
1288c2ecf20Sopenharmony_ci	num = (32768 * delta) / clock_freq;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	data[0] = 0x00;
1318c2ecf20Sopenharmony_ci	data[1] = 0x00;
1328c2ecf20Sopenharmony_ci	data[2] = 0x00;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	data[3] = 0x10;
1358c2ecf20Sopenharmony_ci	data[4] = 0x04;
1368c2ecf20Sopenharmony_ci	data[5] = num & 0xFF;
1378c2ecf20Sopenharmony_ci	data[6] = (num >> 8) & 0xff;
1388c2ecf20Sopenharmony_ci	data[7] = nd;
1398c2ecf20Sopenharmony_ci	data[8] = clock_type;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	return fdp_nci_set_production_data(ndev, 9, data);
1428c2ecf20Sopenharmony_ci}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_cistatic void fdp_nci_send_patch_cb(struct nci_dev *ndev)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	struct fdp_nci_info *info = nci_get_drvdata(ndev);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	info->setup_patch_sent = 1;
1498c2ecf20Sopenharmony_ci	wake_up(&info->setup_wq);
1508c2ecf20Sopenharmony_ci}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci/**
1538c2ecf20Sopenharmony_ci * Register a packet sent counter and a callback
1548c2ecf20Sopenharmony_ci *
1558c2ecf20Sopenharmony_ci * We have no other way of knowing when all firmware packets were sent out
1568c2ecf20Sopenharmony_ci * on the i2c bus. We need to know that in order to close the connection and
1578c2ecf20Sopenharmony_ci * send the patch end message.
1588c2ecf20Sopenharmony_ci */
1598c2ecf20Sopenharmony_cistatic void fdp_nci_set_data_pkt_counter(struct nci_dev *ndev,
1608c2ecf20Sopenharmony_ci				  void (*cb)(struct nci_dev *ndev), int count)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	struct fdp_nci_info *info = nci_get_drvdata(ndev);
1638c2ecf20Sopenharmony_ci	struct device *dev = &info->phy->i2c_dev->dev;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	dev_dbg(dev, "NCI data pkt counter %d\n", count);
1668c2ecf20Sopenharmony_ci	atomic_set(&info->data_pkt_counter, count);
1678c2ecf20Sopenharmony_ci	info->data_pkt_counter_cb = cb;
1688c2ecf20Sopenharmony_ci}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci/**
1718c2ecf20Sopenharmony_ci * The device is expecting a stream of packets. All packets need to
1728c2ecf20Sopenharmony_ci * have the PBF flag set to 0x0 (last packet) even if the firmware
1738c2ecf20Sopenharmony_ci * file is segmented and there are multiple packets. If we give the
1748c2ecf20Sopenharmony_ci * whole firmware to nci_send_data it will segment it and it will set
1758c2ecf20Sopenharmony_ci * the PBF flag to 0x01 so we need to do the segmentation here.
1768c2ecf20Sopenharmony_ci *
1778c2ecf20Sopenharmony_ci * The firmware will be analyzed and applied when we send NCI_OP_PROP_PATCH_CMD
1788c2ecf20Sopenharmony_ci * command with NCI_PATCH_TYPE_EOT parameter. The device will send a
1798c2ecf20Sopenharmony_ci * NFCC_PATCH_NTF packaet and a NCI_OP_CORE_RESET_NTF packet.
1808c2ecf20Sopenharmony_ci */
1818c2ecf20Sopenharmony_cistatic int fdp_nci_send_patch(struct nci_dev *ndev, u8 conn_id, u8 type)
1828c2ecf20Sopenharmony_ci{
1838c2ecf20Sopenharmony_ci	struct fdp_nci_info *info = nci_get_drvdata(ndev);
1848c2ecf20Sopenharmony_ci	const struct firmware *fw;
1858c2ecf20Sopenharmony_ci	struct sk_buff *skb;
1868c2ecf20Sopenharmony_ci	unsigned long len;
1878c2ecf20Sopenharmony_ci	int max_size, payload_size;
1888c2ecf20Sopenharmony_ci	int rc = 0;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	if ((type == NCI_PATCH_TYPE_OTP && !info->otp_patch) ||
1918c2ecf20Sopenharmony_ci	    (type == NCI_PATCH_TYPE_RAM && !info->ram_patch))
1928c2ecf20Sopenharmony_ci		return -EINVAL;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	if (type == NCI_PATCH_TYPE_OTP)
1958c2ecf20Sopenharmony_ci		fw = info->otp_patch;
1968c2ecf20Sopenharmony_ci	else
1978c2ecf20Sopenharmony_ci		fw = info->ram_patch;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	max_size = nci_conn_max_data_pkt_payload_size(ndev, conn_id);
2008c2ecf20Sopenharmony_ci	if (max_size <= 0)
2018c2ecf20Sopenharmony_ci		return -EINVAL;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	len = fw->size;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	fdp_nci_set_data_pkt_counter(ndev, fdp_nci_send_patch_cb,
2068c2ecf20Sopenharmony_ci				     DIV_ROUND_UP(fw->size, max_size));
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	while (len) {
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci		payload_size = min_t(unsigned long, max_size, len);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci		skb = nci_skb_alloc(ndev, (NCI_CTRL_HDR_SIZE + payload_size),
2138c2ecf20Sopenharmony_ci				    GFP_KERNEL);
2148c2ecf20Sopenharmony_ci		if (!skb) {
2158c2ecf20Sopenharmony_ci			fdp_nci_set_data_pkt_counter(ndev, NULL, 0);
2168c2ecf20Sopenharmony_ci			return -ENOMEM;
2178c2ecf20Sopenharmony_ci		}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci		skb_reserve(skb, NCI_CTRL_HDR_SIZE);
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci		skb_put_data(skb, fw->data + (fw->size - len), payload_size);
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci		rc = nci_send_data(ndev, conn_id, skb);
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci		if (rc) {
2278c2ecf20Sopenharmony_ci			fdp_nci_set_data_pkt_counter(ndev, NULL, 0);
2288c2ecf20Sopenharmony_ci			return rc;
2298c2ecf20Sopenharmony_ci		}
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci		len -= payload_size;
2328c2ecf20Sopenharmony_ci	}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	return rc;
2358c2ecf20Sopenharmony_ci}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_cistatic int fdp_nci_open(struct nci_dev *ndev)
2388c2ecf20Sopenharmony_ci{
2398c2ecf20Sopenharmony_ci	int r;
2408c2ecf20Sopenharmony_ci	struct fdp_nci_info *info = nci_get_drvdata(ndev);
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	r = info->phy_ops->enable(info->phy);
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	return r;
2458c2ecf20Sopenharmony_ci}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_cistatic int fdp_nci_close(struct nci_dev *ndev)
2488c2ecf20Sopenharmony_ci{
2498c2ecf20Sopenharmony_ci	return 0;
2508c2ecf20Sopenharmony_ci}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_cistatic int fdp_nci_send(struct nci_dev *ndev, struct sk_buff *skb)
2538c2ecf20Sopenharmony_ci{
2548c2ecf20Sopenharmony_ci	struct fdp_nci_info *info = nci_get_drvdata(ndev);
2558c2ecf20Sopenharmony_ci	int ret;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	if (atomic_dec_and_test(&info->data_pkt_counter))
2588c2ecf20Sopenharmony_ci		info->data_pkt_counter_cb(ndev);
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	ret = info->phy_ops->write(info->phy, skb);
2618c2ecf20Sopenharmony_ci	if (ret < 0) {
2628c2ecf20Sopenharmony_ci		kfree_skb(skb);
2638c2ecf20Sopenharmony_ci		return ret;
2648c2ecf20Sopenharmony_ci	}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	consume_skb(skb);
2678c2ecf20Sopenharmony_ci	return 0;
2688c2ecf20Sopenharmony_ci}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_cistatic int fdp_nci_request_firmware(struct nci_dev *ndev)
2718c2ecf20Sopenharmony_ci{
2728c2ecf20Sopenharmony_ci	struct fdp_nci_info *info = nci_get_drvdata(ndev);
2738c2ecf20Sopenharmony_ci	struct device *dev = &info->phy->i2c_dev->dev;
2748c2ecf20Sopenharmony_ci	u8 *data;
2758c2ecf20Sopenharmony_ci	int r;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	r = request_firmware(&info->ram_patch, FDP_RAM_PATCH_NAME, dev);
2788c2ecf20Sopenharmony_ci	if (r < 0) {
2798c2ecf20Sopenharmony_ci		nfc_err(dev, "RAM patch request error\n");
2808c2ecf20Sopenharmony_ci		goto error;
2818c2ecf20Sopenharmony_ci	}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	data = (u8 *) info->ram_patch->data;
2848c2ecf20Sopenharmony_ci	info->ram_patch_version =
2858c2ecf20Sopenharmony_ci		data[FDP_FW_HEADER_SIZE] |
2868c2ecf20Sopenharmony_ci		(data[FDP_FW_HEADER_SIZE + 1] << 8) |
2878c2ecf20Sopenharmony_ci		(data[FDP_FW_HEADER_SIZE + 2] << 16) |
2888c2ecf20Sopenharmony_ci		(data[FDP_FW_HEADER_SIZE + 3] << 24);
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	dev_dbg(dev, "RAM patch version: %d, size: %d\n",
2918c2ecf20Sopenharmony_ci		  info->ram_patch_version, (int) info->ram_patch->size);
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	r = request_firmware(&info->otp_patch, FDP_OTP_PATCH_NAME, dev);
2958c2ecf20Sopenharmony_ci	if (r < 0) {
2968c2ecf20Sopenharmony_ci		nfc_err(dev, "OTP patch request error\n");
2978c2ecf20Sopenharmony_ci		goto out;
2988c2ecf20Sopenharmony_ci	}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	data = (u8 *) info->otp_patch->data;
3018c2ecf20Sopenharmony_ci	info->otp_patch_version =
3028c2ecf20Sopenharmony_ci		data[FDP_FW_HEADER_SIZE] |
3038c2ecf20Sopenharmony_ci		(data[FDP_FW_HEADER_SIZE + 1] << 8) |
3048c2ecf20Sopenharmony_ci		(data[FDP_FW_HEADER_SIZE+2] << 16) |
3058c2ecf20Sopenharmony_ci		(data[FDP_FW_HEADER_SIZE+3] << 24);
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	dev_dbg(dev, "OTP patch version: %d, size: %d\n",
3088c2ecf20Sopenharmony_ci		 info->otp_patch_version, (int) info->otp_patch->size);
3098c2ecf20Sopenharmony_ciout:
3108c2ecf20Sopenharmony_ci	return 0;
3118c2ecf20Sopenharmony_cierror:
3128c2ecf20Sopenharmony_ci	return r;
3138c2ecf20Sopenharmony_ci}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_cistatic void fdp_nci_release_firmware(struct nci_dev *ndev)
3168c2ecf20Sopenharmony_ci{
3178c2ecf20Sopenharmony_ci	struct fdp_nci_info *info = nci_get_drvdata(ndev);
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	if (info->otp_patch) {
3208c2ecf20Sopenharmony_ci		release_firmware(info->otp_patch);
3218c2ecf20Sopenharmony_ci		info->otp_patch = NULL;
3228c2ecf20Sopenharmony_ci	}
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	if (info->ram_patch) {
3258c2ecf20Sopenharmony_ci		release_firmware(info->ram_patch);
3268c2ecf20Sopenharmony_ci		info->ram_patch = NULL;
3278c2ecf20Sopenharmony_ci	}
3288c2ecf20Sopenharmony_ci}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_cistatic int fdp_nci_patch_otp(struct nci_dev *ndev)
3318c2ecf20Sopenharmony_ci{
3328c2ecf20Sopenharmony_ci	struct fdp_nci_info *info = nci_get_drvdata(ndev);
3338c2ecf20Sopenharmony_ci	struct device *dev = &info->phy->i2c_dev->dev;
3348c2ecf20Sopenharmony_ci	int conn_id;
3358c2ecf20Sopenharmony_ci	int r = 0;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	if (info->otp_version >= info->otp_patch_version)
3388c2ecf20Sopenharmony_ci		goto out;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	info->setup_patch_sent = 0;
3418c2ecf20Sopenharmony_ci	info->setup_reset_ntf = 0;
3428c2ecf20Sopenharmony_ci	info->setup_patch_ntf = 0;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	/* Patch init request */
3458c2ecf20Sopenharmony_ci	r = fdp_nci_patch_cmd(ndev, NCI_PATCH_TYPE_OTP);
3468c2ecf20Sopenharmony_ci	if (r)
3478c2ecf20Sopenharmony_ci		goto out;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	/* Patch data connection creation */
3508c2ecf20Sopenharmony_ci	conn_id = fdp_nci_create_conn(ndev);
3518c2ecf20Sopenharmony_ci	if (conn_id < 0) {
3528c2ecf20Sopenharmony_ci		r = conn_id;
3538c2ecf20Sopenharmony_ci		goto out;
3548c2ecf20Sopenharmony_ci	}
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	/* Send the patch over the data connection */
3578c2ecf20Sopenharmony_ci	r = fdp_nci_send_patch(ndev, conn_id, NCI_PATCH_TYPE_OTP);
3588c2ecf20Sopenharmony_ci	if (r)
3598c2ecf20Sopenharmony_ci		goto out;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	/* Wait for all the packets to be send over i2c */
3628c2ecf20Sopenharmony_ci	wait_event_interruptible(info->setup_wq,
3638c2ecf20Sopenharmony_ci				 info->setup_patch_sent == 1);
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	/* make sure that the NFCC processed the last data packet */
3668c2ecf20Sopenharmony_ci	msleep(FDP_FW_UPDATE_SLEEP);
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	/* Close the data connection */
3698c2ecf20Sopenharmony_ci	r = nci_core_conn_close(info->ndev, conn_id);
3708c2ecf20Sopenharmony_ci	if (r)
3718c2ecf20Sopenharmony_ci		goto out;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	/* Patch finish message */
3748c2ecf20Sopenharmony_ci	if (fdp_nci_patch_cmd(ndev, NCI_PATCH_TYPE_EOT)) {
3758c2ecf20Sopenharmony_ci		nfc_err(dev, "OTP patch error 0x%x\n", r);
3768c2ecf20Sopenharmony_ci		r = -EINVAL;
3778c2ecf20Sopenharmony_ci		goto out;
3788c2ecf20Sopenharmony_ci	}
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	/* If the patch notification didn't arrive yet, wait for it */
3818c2ecf20Sopenharmony_ci	wait_event_interruptible(info->setup_wq, info->setup_patch_ntf);
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	/* Check if the patching was successful */
3848c2ecf20Sopenharmony_ci	r = info->setup_patch_status;
3858c2ecf20Sopenharmony_ci	if (r) {
3868c2ecf20Sopenharmony_ci		nfc_err(dev, "OTP patch error 0x%x\n", r);
3878c2ecf20Sopenharmony_ci		r = -EINVAL;
3888c2ecf20Sopenharmony_ci		goto out;
3898c2ecf20Sopenharmony_ci	}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	/*
3928c2ecf20Sopenharmony_ci	 * We need to wait for the reset notification before we
3938c2ecf20Sopenharmony_ci	 * can continue
3948c2ecf20Sopenharmony_ci	 */
3958c2ecf20Sopenharmony_ci	wait_event_interruptible(info->setup_wq, info->setup_reset_ntf);
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ciout:
3988c2ecf20Sopenharmony_ci	return r;
3998c2ecf20Sopenharmony_ci}
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_cistatic int fdp_nci_patch_ram(struct nci_dev *ndev)
4028c2ecf20Sopenharmony_ci{
4038c2ecf20Sopenharmony_ci	struct fdp_nci_info *info = nci_get_drvdata(ndev);
4048c2ecf20Sopenharmony_ci	struct device *dev = &info->phy->i2c_dev->dev;
4058c2ecf20Sopenharmony_ci	int conn_id;
4068c2ecf20Sopenharmony_ci	int r = 0;
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	if (info->ram_version >= info->ram_patch_version)
4098c2ecf20Sopenharmony_ci		goto out;
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	info->setup_patch_sent = 0;
4128c2ecf20Sopenharmony_ci	info->setup_reset_ntf = 0;
4138c2ecf20Sopenharmony_ci	info->setup_patch_ntf = 0;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	/* Patch init request */
4168c2ecf20Sopenharmony_ci	r = fdp_nci_patch_cmd(ndev, NCI_PATCH_TYPE_RAM);
4178c2ecf20Sopenharmony_ci	if (r)
4188c2ecf20Sopenharmony_ci		goto out;
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	/* Patch data connection creation */
4218c2ecf20Sopenharmony_ci	conn_id = fdp_nci_create_conn(ndev);
4228c2ecf20Sopenharmony_ci	if (conn_id < 0) {
4238c2ecf20Sopenharmony_ci		r = conn_id;
4248c2ecf20Sopenharmony_ci		goto out;
4258c2ecf20Sopenharmony_ci	}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	/* Send the patch over the data connection */
4288c2ecf20Sopenharmony_ci	r = fdp_nci_send_patch(ndev, conn_id, NCI_PATCH_TYPE_RAM);
4298c2ecf20Sopenharmony_ci	if (r)
4308c2ecf20Sopenharmony_ci		goto out;
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	/* Wait for all the packets to be send over i2c */
4338c2ecf20Sopenharmony_ci	wait_event_interruptible(info->setup_wq,
4348c2ecf20Sopenharmony_ci				 info->setup_patch_sent == 1);
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	/* make sure that the NFCC processed the last data packet */
4378c2ecf20Sopenharmony_ci	msleep(FDP_FW_UPDATE_SLEEP);
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	/* Close the data connection */
4408c2ecf20Sopenharmony_ci	r = nci_core_conn_close(info->ndev, conn_id);
4418c2ecf20Sopenharmony_ci	if (r)
4428c2ecf20Sopenharmony_ci		goto out;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	/* Patch finish message */
4458c2ecf20Sopenharmony_ci	if (fdp_nci_patch_cmd(ndev, NCI_PATCH_TYPE_EOT)) {
4468c2ecf20Sopenharmony_ci		nfc_err(dev, "RAM patch error 0x%x\n", r);
4478c2ecf20Sopenharmony_ci		r = -EINVAL;
4488c2ecf20Sopenharmony_ci		goto out;
4498c2ecf20Sopenharmony_ci	}
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	/* If the patch notification didn't arrive yet, wait for it */
4528c2ecf20Sopenharmony_ci	wait_event_interruptible(info->setup_wq, info->setup_patch_ntf);
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	/* Check if the patching was successful */
4558c2ecf20Sopenharmony_ci	r = info->setup_patch_status;
4568c2ecf20Sopenharmony_ci	if (r) {
4578c2ecf20Sopenharmony_ci		nfc_err(dev, "RAM patch error 0x%x\n", r);
4588c2ecf20Sopenharmony_ci		r = -EINVAL;
4598c2ecf20Sopenharmony_ci		goto out;
4608c2ecf20Sopenharmony_ci	}
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	/*
4638c2ecf20Sopenharmony_ci	 * We need to wait for the reset notification before we
4648c2ecf20Sopenharmony_ci	 * can continue
4658c2ecf20Sopenharmony_ci	 */
4668c2ecf20Sopenharmony_ci	wait_event_interruptible(info->setup_wq, info->setup_reset_ntf);
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ciout:
4698c2ecf20Sopenharmony_ci	return r;
4708c2ecf20Sopenharmony_ci}
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_cistatic int fdp_nci_setup(struct nci_dev *ndev)
4738c2ecf20Sopenharmony_ci{
4748c2ecf20Sopenharmony_ci	/* Format: total length followed by an NCI packet */
4758c2ecf20Sopenharmony_ci	struct fdp_nci_info *info = nci_get_drvdata(ndev);
4768c2ecf20Sopenharmony_ci	struct device *dev = &info->phy->i2c_dev->dev;
4778c2ecf20Sopenharmony_ci	int r;
4788c2ecf20Sopenharmony_ci	u8 patched = 0;
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	r = nci_core_init(ndev);
4818c2ecf20Sopenharmony_ci	if (r)
4828c2ecf20Sopenharmony_ci		goto error;
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	/* Get RAM and OTP version */
4858c2ecf20Sopenharmony_ci	r = fdp_nci_get_versions(ndev);
4868c2ecf20Sopenharmony_ci	if (r)
4878c2ecf20Sopenharmony_ci		goto error;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	/* Load firmware from disk */
4908c2ecf20Sopenharmony_ci	r = fdp_nci_request_firmware(ndev);
4918c2ecf20Sopenharmony_ci	if (r)
4928c2ecf20Sopenharmony_ci		goto error;
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	/* Update OTP */
4958c2ecf20Sopenharmony_ci	if (info->otp_version < info->otp_patch_version) {
4968c2ecf20Sopenharmony_ci		r = fdp_nci_patch_otp(ndev);
4978c2ecf20Sopenharmony_ci		if (r)
4988c2ecf20Sopenharmony_ci			goto error;
4998c2ecf20Sopenharmony_ci		patched = 1;
5008c2ecf20Sopenharmony_ci	}
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	/* Update RAM */
5038c2ecf20Sopenharmony_ci	if (info->ram_version < info->ram_patch_version) {
5048c2ecf20Sopenharmony_ci		r = fdp_nci_patch_ram(ndev);
5058c2ecf20Sopenharmony_ci		if (r)
5068c2ecf20Sopenharmony_ci			goto error;
5078c2ecf20Sopenharmony_ci		patched = 1;
5088c2ecf20Sopenharmony_ci	}
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	/* Release the firmware buffers */
5118c2ecf20Sopenharmony_ci	fdp_nci_release_firmware(ndev);
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	/* If a patch was applied the new version is checked */
5148c2ecf20Sopenharmony_ci	if (patched) {
5158c2ecf20Sopenharmony_ci		r = nci_core_init(ndev);
5168c2ecf20Sopenharmony_ci		if (r)
5178c2ecf20Sopenharmony_ci			goto error;
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci		r = fdp_nci_get_versions(ndev);
5208c2ecf20Sopenharmony_ci		if (r)
5218c2ecf20Sopenharmony_ci			goto error;
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci		if (info->otp_version != info->otp_patch_version ||
5248c2ecf20Sopenharmony_ci		    info->ram_version != info->ram_patch_version) {
5258c2ecf20Sopenharmony_ci			nfc_err(dev, "Firmware update failed");
5268c2ecf20Sopenharmony_ci			r = -EINVAL;
5278c2ecf20Sopenharmony_ci			goto error;
5288c2ecf20Sopenharmony_ci		}
5298c2ecf20Sopenharmony_ci	}
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	/*
5328c2ecf20Sopenharmony_ci	 * We initialized the devices but the NFC subsystem expects
5338c2ecf20Sopenharmony_ci	 * it to not be initialized.
5348c2ecf20Sopenharmony_ci	 */
5358c2ecf20Sopenharmony_ci	return nci_core_reset(ndev);
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_cierror:
5388c2ecf20Sopenharmony_ci	fdp_nci_release_firmware(ndev);
5398c2ecf20Sopenharmony_ci	nfc_err(dev, "Setup error %d\n", r);
5408c2ecf20Sopenharmony_ci	return r;
5418c2ecf20Sopenharmony_ci}
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_cistatic int fdp_nci_post_setup(struct nci_dev *ndev)
5448c2ecf20Sopenharmony_ci{
5458c2ecf20Sopenharmony_ci	struct fdp_nci_info *info = nci_get_drvdata(ndev);
5468c2ecf20Sopenharmony_ci	struct device *dev = &info->phy->i2c_dev->dev;
5478c2ecf20Sopenharmony_ci	int r;
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	/* Check if the device has VSC */
5508c2ecf20Sopenharmony_ci	if (info->fw_vsc_cfg && info->fw_vsc_cfg[0]) {
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci		/* Set the vendor specific configuration */
5538c2ecf20Sopenharmony_ci		r = fdp_nci_set_production_data(ndev, info->fw_vsc_cfg[3],
5548c2ecf20Sopenharmony_ci						&info->fw_vsc_cfg[4]);
5558c2ecf20Sopenharmony_ci		if (r) {
5568c2ecf20Sopenharmony_ci			nfc_err(dev, "Vendor specific config set error %d\n",
5578c2ecf20Sopenharmony_ci				r);
5588c2ecf20Sopenharmony_ci			return r;
5598c2ecf20Sopenharmony_ci		}
5608c2ecf20Sopenharmony_ci	}
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	/* Set clock type and frequency */
5638c2ecf20Sopenharmony_ci	r = fdp_nci_set_clock(ndev, info->clock_type, info->clock_freq);
5648c2ecf20Sopenharmony_ci	if (r) {
5658c2ecf20Sopenharmony_ci		nfc_err(dev, "Clock set error %d\n", r);
5668c2ecf20Sopenharmony_ci		return r;
5678c2ecf20Sopenharmony_ci	}
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	/*
5708c2ecf20Sopenharmony_ci	 * In order to apply the VSC FDP needs a reset
5718c2ecf20Sopenharmony_ci	 */
5728c2ecf20Sopenharmony_ci	r = nci_core_reset(ndev);
5738c2ecf20Sopenharmony_ci	if (r)
5748c2ecf20Sopenharmony_ci		return r;
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	/**
5778c2ecf20Sopenharmony_ci	 * The nci core was initialized when post setup was called
5788c2ecf20Sopenharmony_ci	 * so we leave it like that
5798c2ecf20Sopenharmony_ci	 */
5808c2ecf20Sopenharmony_ci	return nci_core_init(ndev);
5818c2ecf20Sopenharmony_ci}
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_cistatic int fdp_nci_core_reset_ntf_packet(struct nci_dev *ndev,
5848c2ecf20Sopenharmony_ci					  struct sk_buff *skb)
5858c2ecf20Sopenharmony_ci{
5868c2ecf20Sopenharmony_ci	struct fdp_nci_info *info = nci_get_drvdata(ndev);
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	info->setup_reset_ntf = 1;
5898c2ecf20Sopenharmony_ci	wake_up(&info->setup_wq);
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	return 0;
5928c2ecf20Sopenharmony_ci}
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_cistatic int fdp_nci_prop_patch_ntf_packet(struct nci_dev *ndev,
5958c2ecf20Sopenharmony_ci					  struct sk_buff *skb)
5968c2ecf20Sopenharmony_ci{
5978c2ecf20Sopenharmony_ci	struct fdp_nci_info *info = nci_get_drvdata(ndev);
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	info->setup_patch_ntf = 1;
6008c2ecf20Sopenharmony_ci	info->setup_patch_status = skb->data[0];
6018c2ecf20Sopenharmony_ci	wake_up(&info->setup_wq);
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci	return 0;
6048c2ecf20Sopenharmony_ci}
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_cistatic int fdp_nci_prop_patch_rsp_packet(struct nci_dev *ndev,
6078c2ecf20Sopenharmony_ci					  struct sk_buff *skb)
6088c2ecf20Sopenharmony_ci{
6098c2ecf20Sopenharmony_ci	struct fdp_nci_info *info = nci_get_drvdata(ndev);
6108c2ecf20Sopenharmony_ci	struct device *dev = &info->phy->i2c_dev->dev;
6118c2ecf20Sopenharmony_ci	u8 status = skb->data[0];
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	dev_dbg(dev, "%s: status 0x%x\n", __func__, status);
6148c2ecf20Sopenharmony_ci	nci_req_complete(ndev, status);
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci	return 0;
6178c2ecf20Sopenharmony_ci}
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_cistatic int fdp_nci_prop_set_production_data_rsp_packet(struct nci_dev *ndev,
6208c2ecf20Sopenharmony_ci							struct sk_buff *skb)
6218c2ecf20Sopenharmony_ci{
6228c2ecf20Sopenharmony_ci	struct fdp_nci_info *info = nci_get_drvdata(ndev);
6238c2ecf20Sopenharmony_ci	struct device *dev = &info->phy->i2c_dev->dev;
6248c2ecf20Sopenharmony_ci	u8 status = skb->data[0];
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci	dev_dbg(dev, "%s: status 0x%x\n", __func__, status);
6278c2ecf20Sopenharmony_ci	nci_req_complete(ndev, status);
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	return 0;
6308c2ecf20Sopenharmony_ci}
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_cistatic int fdp_nci_core_get_config_rsp_packet(struct nci_dev *ndev,
6338c2ecf20Sopenharmony_ci						struct sk_buff *skb)
6348c2ecf20Sopenharmony_ci{
6358c2ecf20Sopenharmony_ci	struct fdp_nci_info *info = nci_get_drvdata(ndev);
6368c2ecf20Sopenharmony_ci	struct device *dev = &info->phy->i2c_dev->dev;
6378c2ecf20Sopenharmony_ci	struct nci_core_get_config_rsp *rsp = (void *) skb->data;
6388c2ecf20Sopenharmony_ci	u8 i, *p;
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	if (rsp->status == NCI_STATUS_OK) {
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci		p = rsp->data;
6438c2ecf20Sopenharmony_ci		for (i = 0; i < 4; i++) {
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci			switch (*p++) {
6468c2ecf20Sopenharmony_ci			case NCI_PARAM_ID_FW_RAM_VERSION:
6478c2ecf20Sopenharmony_ci				p++;
6488c2ecf20Sopenharmony_ci				info->ram_version = le32_to_cpup((__le32 *) p);
6498c2ecf20Sopenharmony_ci				p += 4;
6508c2ecf20Sopenharmony_ci				break;
6518c2ecf20Sopenharmony_ci			case NCI_PARAM_ID_FW_OTP_VERSION:
6528c2ecf20Sopenharmony_ci				p++;
6538c2ecf20Sopenharmony_ci				info->otp_version = le32_to_cpup((__le32 *) p);
6548c2ecf20Sopenharmony_ci				p += 4;
6558c2ecf20Sopenharmony_ci				break;
6568c2ecf20Sopenharmony_ci			case NCI_PARAM_ID_OTP_LIMITED_VERSION:
6578c2ecf20Sopenharmony_ci				p++;
6588c2ecf20Sopenharmony_ci				info->otp_version = le32_to_cpup((__le32 *) p);
6598c2ecf20Sopenharmony_ci				p += 4;
6608c2ecf20Sopenharmony_ci				break;
6618c2ecf20Sopenharmony_ci			case NCI_PARAM_ID_KEY_INDEX_ID:
6628c2ecf20Sopenharmony_ci				p++;
6638c2ecf20Sopenharmony_ci				info->key_index = *p++;
6648c2ecf20Sopenharmony_ci			}
6658c2ecf20Sopenharmony_ci		}
6668c2ecf20Sopenharmony_ci	}
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	dev_dbg(dev, "OTP version %d\n", info->otp_version);
6698c2ecf20Sopenharmony_ci	dev_dbg(dev, "RAM version %d\n", info->ram_version);
6708c2ecf20Sopenharmony_ci	dev_dbg(dev, "key index %d\n", info->key_index);
6718c2ecf20Sopenharmony_ci	dev_dbg(dev, "%s: status 0x%x\n", __func__, rsp->status);
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci	nci_req_complete(ndev, rsp->status);
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci	return 0;
6768c2ecf20Sopenharmony_ci}
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_cistatic struct nci_driver_ops fdp_core_ops[] = {
6798c2ecf20Sopenharmony_ci	{
6808c2ecf20Sopenharmony_ci		.opcode = NCI_OP_CORE_GET_CONFIG_RSP,
6818c2ecf20Sopenharmony_ci		.rsp = fdp_nci_core_get_config_rsp_packet,
6828c2ecf20Sopenharmony_ci	},
6838c2ecf20Sopenharmony_ci	{
6848c2ecf20Sopenharmony_ci		.opcode = NCI_OP_CORE_RESET_NTF,
6858c2ecf20Sopenharmony_ci		.ntf = fdp_nci_core_reset_ntf_packet,
6868c2ecf20Sopenharmony_ci	},
6878c2ecf20Sopenharmony_ci};
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_cistatic struct nci_driver_ops fdp_prop_ops[] = {
6908c2ecf20Sopenharmony_ci	{
6918c2ecf20Sopenharmony_ci		.opcode = nci_opcode_pack(NCI_GID_PROP, NCI_OP_PROP_PATCH_OID),
6928c2ecf20Sopenharmony_ci		.rsp = fdp_nci_prop_patch_rsp_packet,
6938c2ecf20Sopenharmony_ci		.ntf = fdp_nci_prop_patch_ntf_packet,
6948c2ecf20Sopenharmony_ci	},
6958c2ecf20Sopenharmony_ci	{
6968c2ecf20Sopenharmony_ci		.opcode = nci_opcode_pack(NCI_GID_PROP,
6978c2ecf20Sopenharmony_ci					  NCI_OP_PROP_SET_PDATA_OID),
6988c2ecf20Sopenharmony_ci		.rsp = fdp_nci_prop_set_production_data_rsp_packet,
6998c2ecf20Sopenharmony_ci	},
7008c2ecf20Sopenharmony_ci};
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_cistatic struct nci_ops nci_ops = {
7038c2ecf20Sopenharmony_ci	.open = fdp_nci_open,
7048c2ecf20Sopenharmony_ci	.close = fdp_nci_close,
7058c2ecf20Sopenharmony_ci	.send = fdp_nci_send,
7068c2ecf20Sopenharmony_ci	.setup = fdp_nci_setup,
7078c2ecf20Sopenharmony_ci	.post_setup = fdp_nci_post_setup,
7088c2ecf20Sopenharmony_ci	.prop_ops = fdp_prop_ops,
7098c2ecf20Sopenharmony_ci	.n_prop_ops = ARRAY_SIZE(fdp_prop_ops),
7108c2ecf20Sopenharmony_ci	.core_ops = fdp_core_ops,
7118c2ecf20Sopenharmony_ci	.n_core_ops = ARRAY_SIZE(fdp_core_ops),
7128c2ecf20Sopenharmony_ci};
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ciint fdp_nci_probe(struct fdp_i2c_phy *phy, struct nfc_phy_ops *phy_ops,
7158c2ecf20Sopenharmony_ci			struct nci_dev **ndevp, int tx_headroom,
7168c2ecf20Sopenharmony_ci			int tx_tailroom, u8 clock_type, u32 clock_freq,
7178c2ecf20Sopenharmony_ci			u8 *fw_vsc_cfg)
7188c2ecf20Sopenharmony_ci{
7198c2ecf20Sopenharmony_ci	struct device *dev = &phy->i2c_dev->dev;
7208c2ecf20Sopenharmony_ci	struct fdp_nci_info *info;
7218c2ecf20Sopenharmony_ci	struct nci_dev *ndev;
7228c2ecf20Sopenharmony_ci	u32 protocols;
7238c2ecf20Sopenharmony_ci	int r;
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	info = devm_kzalloc(dev, sizeof(struct fdp_nci_info), GFP_KERNEL);
7268c2ecf20Sopenharmony_ci	if (!info)
7278c2ecf20Sopenharmony_ci		return -ENOMEM;
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci	info->phy = phy;
7308c2ecf20Sopenharmony_ci	info->phy_ops = phy_ops;
7318c2ecf20Sopenharmony_ci	info->clock_type = clock_type;
7328c2ecf20Sopenharmony_ci	info->clock_freq = clock_freq;
7338c2ecf20Sopenharmony_ci	info->fw_vsc_cfg = fw_vsc_cfg;
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci	init_waitqueue_head(&info->setup_wq);
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci	protocols = NFC_PROTO_JEWEL_MASK |
7388c2ecf20Sopenharmony_ci		    NFC_PROTO_MIFARE_MASK |
7398c2ecf20Sopenharmony_ci		    NFC_PROTO_FELICA_MASK |
7408c2ecf20Sopenharmony_ci		    NFC_PROTO_ISO14443_MASK |
7418c2ecf20Sopenharmony_ci		    NFC_PROTO_ISO14443_B_MASK |
7428c2ecf20Sopenharmony_ci		    NFC_PROTO_NFC_DEP_MASK |
7438c2ecf20Sopenharmony_ci		    NFC_PROTO_ISO15693_MASK;
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci	ndev = nci_allocate_device(&nci_ops, protocols, tx_headroom,
7468c2ecf20Sopenharmony_ci				   tx_tailroom);
7478c2ecf20Sopenharmony_ci	if (!ndev) {
7488c2ecf20Sopenharmony_ci		nfc_err(dev, "Cannot allocate nfc ndev\n");
7498c2ecf20Sopenharmony_ci		return -ENOMEM;
7508c2ecf20Sopenharmony_ci	}
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	r = nci_register_device(ndev);
7538c2ecf20Sopenharmony_ci	if (r)
7548c2ecf20Sopenharmony_ci		goto err_regdev;
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci	*ndevp = ndev;
7578c2ecf20Sopenharmony_ci	info->ndev = ndev;
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci	nci_set_drvdata(ndev, info);
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci	return 0;
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_cierr_regdev:
7648c2ecf20Sopenharmony_ci	nci_free_device(ndev);
7658c2ecf20Sopenharmony_ci	return r;
7668c2ecf20Sopenharmony_ci}
7678c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fdp_nci_probe);
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_civoid fdp_nci_remove(struct nci_dev *ndev)
7708c2ecf20Sopenharmony_ci{
7718c2ecf20Sopenharmony_ci	nci_unregister_device(ndev);
7728c2ecf20Sopenharmony_ci	nci_free_device(ndev);
7738c2ecf20Sopenharmony_ci}
7748c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fdp_nci_remove);
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
7778c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("NFC NCI driver for Intel Fields Peak NFC controller");
7788c2ecf20Sopenharmony_ciMODULE_AUTHOR("Robert Dolca <robert.dolca@intel.com>");
779