[Feature]add MT2731_MP2_MR2_SVN388 baseline version

Change-Id: Ief04314834b31e27effab435d3ca8ba33b499059
diff --git a/src/kernel/linux/v4.14/drivers/nfc/nfcmrvl/Kconfig b/src/kernel/linux/v4.14/drivers/nfc/nfcmrvl/Kconfig
new file mode 100644
index 0000000..670af76
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/nfc/nfcmrvl/Kconfig
@@ -0,0 +1,54 @@
+config NFC_MRVL
+	tristate
+	help
+	  The core driver to support Marvell NFC devices.
+
+	  This driver is required if you want to support
+	  Marvell NFC device 8897.
+
+config NFC_MRVL_USB
+	tristate "Marvell NFC-over-USB driver"
+	depends on NFC_NCI && USB
+	select NFC_MRVL
+	help
+	  Marvell NFC-over-USB driver.
+
+	  This driver provides support for Marvell NFC-over-USB devices:
+          8897.
+
+	  Say Y here to compile support for Marvell NFC-over-USB driver
+	  into the kernel or say M to compile it as module.
+
+config NFC_MRVL_UART
+	tristate "Marvell NFC-over-UART driver"
+	depends on NFC_NCI && NFC_NCI_UART
+	select NFC_MRVL
+	help
+	  Marvell NFC-over-UART driver.
+
+	  This driver provides support for Marvell NFC-over-UART devices
+
+	  Say Y here to compile support for Marvell NFC-over-UART driver
+	  into the kernel or say M to compile it as module.
+
+config NFC_MRVL_I2C
+	tristate "Marvell NFC-over-I2C driver"
+	depends on NFC_MRVL && I2C
+	help
+	  Marvell NFC-over-I2C driver.
+
+	  This driver provides support for Marvell NFC-over-I2C devices.
+
+	  Say Y here to compile support for Marvell NFC-over-I2C driver
+	  into the kernel or say M to compile it as module.
+
+config NFC_MRVL_SPI
+	tristate "Marvell NFC-over-SPI driver"
+	depends on NFC_MRVL && NFC_NCI_SPI
+	help
+	  Marvell NFC-over-SPI driver.
+
+	  This driver provides support for Marvell NFC-over-SPI devices.
+
+	  Say Y here to compile support for Marvell NFC-over-SPI driver
+	  into the kernel or say M to compile it as module.
diff --git a/src/kernel/linux/v4.14/drivers/nfc/nfcmrvl/Makefile b/src/kernel/linux/v4.14/drivers/nfc/nfcmrvl/Makefile
new file mode 100644
index 0000000..e74de0c
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/nfc/nfcmrvl/Makefile
@@ -0,0 +1,19 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for NFCMRVL NCI based NFC driver
+#
+
+nfcmrvl-y += main.o fw_dnld.o
+obj-$(CONFIG_NFC_MRVL) += nfcmrvl.o
+
+nfcmrvl_usb-y += usb.o
+obj-$(CONFIG_NFC_MRVL_USB) += nfcmrvl_usb.o
+
+nfcmrvl_uart-y += uart.o
+obj-$(CONFIG_NFC_MRVL_UART) += nfcmrvl_uart.o
+
+nfcmrvl_i2c-y += i2c.o
+obj-$(CONFIG_NFC_MRVL_I2C) += nfcmrvl_i2c.o
+
+nfcmrvl_spi-y += spi.o
+obj-$(CONFIG_NFC_MRVL_SPI) += nfcmrvl_spi.o
diff --git a/src/kernel/linux/v4.14/drivers/nfc/nfcmrvl/fw_dnld.c b/src/kernel/linux/v4.14/drivers/nfc/nfcmrvl/fw_dnld.c
new file mode 100644
index 0000000..7f8960a
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/nfc/nfcmrvl/fw_dnld.c
@@ -0,0 +1,560 @@
+/*
+ * Marvell NFC driver: Firmware downloader
+ *
+ * Copyright (C) 2015, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available on the worldwide web at
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+#include <linux/module.h>
+#include <asm/unaligned.h>
+#include <linux/firmware.h>
+#include <linux/nfc.h>
+#include <net/nfc/nci.h>
+#include <net/nfc/nci_core.h>
+#include "nfcmrvl.h"
+
+#define FW_DNLD_TIMEOUT			15000
+
+#define NCI_OP_PROPRIETARY_BOOT_CMD	nci_opcode_pack(NCI_GID_PROPRIETARY, \
+							NCI_OP_PROP_BOOT_CMD)
+
+/* FW download states */
+
+enum {
+	STATE_RESET = 0,
+	STATE_INIT,
+	STATE_SET_REF_CLOCK,
+	STATE_SET_HI_CONFIG,
+	STATE_OPEN_LC,
+	STATE_FW_DNLD,
+	STATE_CLOSE_LC,
+	STATE_BOOT
+};
+
+enum {
+	SUBSTATE_WAIT_COMMAND = 0,
+	SUBSTATE_WAIT_ACK_CREDIT,
+	SUBSTATE_WAIT_NACK_CREDIT,
+	SUBSTATE_WAIT_DATA_CREDIT,
+};
+
+/*
+** Patterns for responses
+*/
+
+static const uint8_t nci_pattern_core_reset_ntf[] = {
+	0x60, 0x00, 0x02, 0xA0, 0x01
+};
+
+static const uint8_t nci_pattern_core_init_rsp[] = {
+	0x40, 0x01, 0x11
+};
+
+static const uint8_t nci_pattern_core_set_config_rsp[] = {
+	0x40, 0x02, 0x02, 0x00, 0x00
+};
+
+static const uint8_t nci_pattern_core_conn_create_rsp[] = {
+	0x40, 0x04, 0x04, 0x00
+};
+
+static const uint8_t nci_pattern_core_conn_close_rsp[] = {
+	0x40, 0x05, 0x01, 0x00
+};
+
+static const uint8_t nci_pattern_core_conn_credits_ntf[] = {
+	0x60, 0x06, 0x03, 0x01, NCI_CORE_LC_CONNID_PROP_FW_DL, 0x01
+};
+
+static const uint8_t nci_pattern_proprietary_boot_rsp[] = {
+	0x4F, 0x3A, 0x01, 0x00
+};
+
+static struct sk_buff *alloc_lc_skb(struct nfcmrvl_private *priv, uint8_t plen)
+{
+	struct sk_buff *skb;
+	struct nci_data_hdr *hdr;
+
+	skb = nci_skb_alloc(priv->ndev, (NCI_DATA_HDR_SIZE + plen), GFP_KERNEL);
+	if (!skb) {
+		pr_err("no memory for data\n");
+		return NULL;
+	}
+
+	hdr = skb_put(skb, NCI_DATA_HDR_SIZE);
+	hdr->conn_id = NCI_CORE_LC_CONNID_PROP_FW_DL;
+	hdr->rfu = 0;
+	hdr->plen = plen;
+
+	nci_mt_set((__u8 *)hdr, NCI_MT_DATA_PKT);
+	nci_pbf_set((__u8 *)hdr, NCI_PBF_LAST);
+
+	return skb;
+}
+
+static void fw_dnld_over(struct nfcmrvl_private *priv, u32 error)
+{
+	if (priv->fw_dnld.fw) {
+		release_firmware(priv->fw_dnld.fw);
+		priv->fw_dnld.fw = NULL;
+		priv->fw_dnld.header = NULL;
+		priv->fw_dnld.binary_config = NULL;
+	}
+
+	atomic_set(&priv->ndev->cmd_cnt, 0);
+
+	if (timer_pending(&priv->ndev->cmd_timer))
+		del_timer_sync(&priv->ndev->cmd_timer);
+
+	if (timer_pending(&priv->fw_dnld.timer))
+		del_timer_sync(&priv->fw_dnld.timer);
+
+	nfc_info(priv->dev, "FW loading over (%d)]\n", error);
+
+	if (error != 0) {
+		/* failed, halt the chip to avoid power consumption */
+		nfcmrvl_chip_halt(priv);
+	}
+
+	nfc_fw_download_done(priv->ndev->nfc_dev, priv->fw_dnld.name, error);
+}
+
+static void fw_dnld_timeout(unsigned long arg)
+{
+	struct nfcmrvl_private *priv = (struct nfcmrvl_private *) arg;
+
+	nfc_err(priv->dev, "FW loading timeout");
+	priv->fw_dnld.state = STATE_RESET;
+	fw_dnld_over(priv, -ETIMEDOUT);
+}
+
+static int process_state_reset(struct nfcmrvl_private *priv,
+			       struct sk_buff *skb)
+{
+	if (sizeof(nci_pattern_core_reset_ntf) != skb->len ||
+	    memcmp(skb->data, nci_pattern_core_reset_ntf,
+		   sizeof(nci_pattern_core_reset_ntf)))
+		return -EINVAL;
+
+	nfc_info(priv->dev, "BootROM reset, start fw download\n");
+
+	/* Start FW download state machine */
+	priv->fw_dnld.state = STATE_INIT;
+	nci_send_cmd(priv->ndev, NCI_OP_CORE_INIT_CMD, 0, NULL);
+
+	return 0;
+}
+
+static int process_state_init(struct nfcmrvl_private *priv, struct sk_buff *skb)
+{
+	struct nci_core_set_config_cmd cmd;
+
+	if (sizeof(nci_pattern_core_init_rsp) >= skb->len ||
+	    memcmp(skb->data, nci_pattern_core_init_rsp,
+		   sizeof(nci_pattern_core_init_rsp)))
+		return -EINVAL;
+
+	cmd.num_params = 1;
+	cmd.param.id = NFCMRVL_PROP_REF_CLOCK;
+	cmd.param.len = 4;
+	memcpy(cmd.param.val, &priv->fw_dnld.header->ref_clock, 4);
+
+	nci_send_cmd(priv->ndev, NCI_OP_CORE_SET_CONFIG_CMD, 3 + cmd.param.len,
+		     &cmd);
+
+	priv->fw_dnld.state = STATE_SET_REF_CLOCK;
+	return 0;
+}
+
+static void create_lc(struct nfcmrvl_private *priv)
+{
+	uint8_t param[2] = { NCI_CORE_LC_PROP_FW_DL, 0x0 };
+
+	priv->fw_dnld.state = STATE_OPEN_LC;
+	nci_send_cmd(priv->ndev, NCI_OP_CORE_CONN_CREATE_CMD, 2, param);
+}
+
+static int process_state_set_ref_clock(struct nfcmrvl_private *priv,
+				       struct sk_buff *skb)
+{
+	struct nci_core_set_config_cmd cmd;
+
+	if (sizeof(nci_pattern_core_set_config_rsp) != skb->len ||
+	    memcmp(skb->data, nci_pattern_core_set_config_rsp, skb->len))
+		return -EINVAL;
+
+	cmd.num_params = 1;
+	cmd.param.id = NFCMRVL_PROP_SET_HI_CONFIG;
+
+	switch (priv->phy) {
+	case NFCMRVL_PHY_UART:
+		cmd.param.len = 5;
+		memcpy(cmd.param.val,
+		       &priv->fw_dnld.binary_config->uart.baudrate,
+		       4);
+		cmd.param.val[4] =
+			priv->fw_dnld.binary_config->uart.flow_control;
+		break;
+	case NFCMRVL_PHY_I2C:
+		cmd.param.len = 5;
+		memcpy(cmd.param.val,
+		       &priv->fw_dnld.binary_config->i2c.clk,
+		       4);
+		cmd.param.val[4] = 0;
+		break;
+	case NFCMRVL_PHY_SPI:
+		cmd.param.len = 5;
+		memcpy(cmd.param.val,
+		       &priv->fw_dnld.binary_config->spi.clk,
+		       4);
+		cmd.param.val[4] = 0;
+		break;
+	default:
+		create_lc(priv);
+		return 0;
+	}
+
+	priv->fw_dnld.state = STATE_SET_HI_CONFIG;
+	nci_send_cmd(priv->ndev, NCI_OP_CORE_SET_CONFIG_CMD, 3 + cmd.param.len,
+		     &cmd);
+	return 0;
+}
+
+static int process_state_set_hi_config(struct nfcmrvl_private *priv,
+				       struct sk_buff *skb)
+{
+	if (sizeof(nci_pattern_core_set_config_rsp) != skb->len ||
+	    memcmp(skb->data, nci_pattern_core_set_config_rsp, skb->len))
+		return -EINVAL;
+
+	create_lc(priv);
+	return 0;
+}
+
+static int process_state_open_lc(struct nfcmrvl_private *priv,
+				 struct sk_buff *skb)
+{
+	if (sizeof(nci_pattern_core_conn_create_rsp) >= skb->len ||
+	    memcmp(skb->data, nci_pattern_core_conn_create_rsp,
+		   sizeof(nci_pattern_core_conn_create_rsp)))
+		return -EINVAL;
+
+	priv->fw_dnld.state = STATE_FW_DNLD;
+	priv->fw_dnld.substate = SUBSTATE_WAIT_COMMAND;
+	priv->fw_dnld.offset = priv->fw_dnld.binary_config->offset;
+	return 0;
+}
+
+static int process_state_fw_dnld(struct nfcmrvl_private *priv,
+				 struct sk_buff *skb)
+{
+	uint16_t len;
+	uint16_t comp_len;
+	struct sk_buff *out_skb;
+
+	switch (priv->fw_dnld.substate) {
+	case SUBSTATE_WAIT_COMMAND:
+		/*
+		 * Command format:
+		 * B0..2: NCI header
+		 * B3   : Helper command (0xA5)
+		 * B4..5: le16 data size
+		 * B6..7: le16 data size complement (~)
+		 * B8..N: payload
+		 */
+
+		/* Remove NCI HDR */
+		skb_pull(skb, 3);
+		if (skb->data[0] != HELPER_CMD_PACKET_FORMAT || skb->len != 5) {
+			nfc_err(priv->dev, "bad command");
+			return -EINVAL;
+		}
+		skb_pull(skb, 1);
+		len = get_unaligned_le16(skb->data);
+		skb_pull(skb, 2);
+		comp_len = get_unaligned_le16(skb->data);
+		memcpy(&comp_len, skb->data, 2);
+		skb_pull(skb, 2);
+		if (((~len) & 0xFFFF) != comp_len) {
+			nfc_err(priv->dev, "bad len complement: %x %x %x",
+				len, comp_len, (~len & 0xFFFF));
+			out_skb = alloc_lc_skb(priv, 1);
+			if (!out_skb)
+				return -ENOMEM;
+			skb_put_u8(out_skb, 0xBF);
+			nci_send_frame(priv->ndev, out_skb);
+			priv->fw_dnld.substate = SUBSTATE_WAIT_NACK_CREDIT;
+			return 0;
+		}
+		priv->fw_dnld.chunk_len = len;
+		out_skb = alloc_lc_skb(priv, 1);
+		if (!out_skb)
+			return -ENOMEM;
+		skb_put_u8(out_skb, HELPER_ACK_PACKET_FORMAT);
+		nci_send_frame(priv->ndev, out_skb);
+		priv->fw_dnld.substate = SUBSTATE_WAIT_ACK_CREDIT;
+		break;
+
+	case SUBSTATE_WAIT_ACK_CREDIT:
+		if (sizeof(nci_pattern_core_conn_credits_ntf) != skb->len ||
+		    memcmp(nci_pattern_core_conn_credits_ntf, skb->data,
+			   skb->len)) {
+			nfc_err(priv->dev, "bad packet: waiting for credit");
+			return -EINVAL;
+		}
+		if (priv->fw_dnld.chunk_len == 0) {
+			/* FW Loading is done */
+			uint8_t conn_id = NCI_CORE_LC_CONNID_PROP_FW_DL;
+
+			priv->fw_dnld.state = STATE_CLOSE_LC;
+			nci_send_cmd(priv->ndev, NCI_OP_CORE_CONN_CLOSE_CMD,
+				     1, &conn_id);
+		} else {
+			out_skb = alloc_lc_skb(priv, priv->fw_dnld.chunk_len);
+			if (!out_skb)
+				return -ENOMEM;
+			skb_put_data(out_skb,
+				     ((uint8_t *)priv->fw_dnld.fw->data) + priv->fw_dnld.offset,
+				     priv->fw_dnld.chunk_len);
+			nci_send_frame(priv->ndev, out_skb);
+			priv->fw_dnld.substate = SUBSTATE_WAIT_DATA_CREDIT;
+		}
+		break;
+
+	case SUBSTATE_WAIT_DATA_CREDIT:
+		if (sizeof(nci_pattern_core_conn_credits_ntf) != skb->len ||
+		    memcmp(nci_pattern_core_conn_credits_ntf, skb->data,
+			    skb->len)) {
+			nfc_err(priv->dev, "bad packet: waiting for credit");
+			return -EINVAL;
+		}
+		priv->fw_dnld.offset += priv->fw_dnld.chunk_len;
+		priv->fw_dnld.chunk_len = 0;
+		priv->fw_dnld.substate = SUBSTATE_WAIT_COMMAND;
+		break;
+
+	case SUBSTATE_WAIT_NACK_CREDIT:
+		if (sizeof(nci_pattern_core_conn_credits_ntf) != skb->len ||
+		    memcmp(nci_pattern_core_conn_credits_ntf, skb->data,
+			    skb->len)) {
+			nfc_err(priv->dev, "bad packet: waiting for credit");
+			return -EINVAL;
+		}
+		priv->fw_dnld.substate = SUBSTATE_WAIT_COMMAND;
+		break;
+	}
+	return 0;
+}
+
+static int process_state_close_lc(struct nfcmrvl_private *priv,
+				  struct sk_buff *skb)
+{
+	if (sizeof(nci_pattern_core_conn_close_rsp) != skb->len ||
+	    memcmp(skb->data, nci_pattern_core_conn_close_rsp, skb->len))
+		return -EINVAL;
+
+	priv->fw_dnld.state = STATE_BOOT;
+	nci_send_cmd(priv->ndev, NCI_OP_PROPRIETARY_BOOT_CMD, 0, NULL);
+	return 0;
+}
+
+static int process_state_boot(struct nfcmrvl_private *priv, struct sk_buff *skb)
+{
+	if (sizeof(nci_pattern_proprietary_boot_rsp) != skb->len ||
+	    memcmp(skb->data, nci_pattern_proprietary_boot_rsp, skb->len))
+		return -EINVAL;
+
+	/*
+	 * Update HI config to use the right configuration for the next
+	 * data exchanges.
+	 */
+	priv->if_ops->nci_update_config(priv,
+					&priv->fw_dnld.binary_config->config);
+
+	if (priv->fw_dnld.binary_config == &priv->fw_dnld.header->helper) {
+		/*
+		 * This is the case where an helper was needed and we have
+		 * uploaded it. Now we have to wait the next RESET NTF to start
+		 * FW download.
+		 */
+		priv->fw_dnld.state = STATE_RESET;
+		priv->fw_dnld.binary_config = &priv->fw_dnld.header->firmware;
+		nfc_info(priv->dev, "FW loading: helper loaded");
+	} else {
+		nfc_info(priv->dev, "FW loading: firmware loaded");
+		fw_dnld_over(priv, 0);
+	}
+	return 0;
+}
+
+static void fw_dnld_rx_work(struct work_struct *work)
+{
+	int ret;
+	struct sk_buff *skb;
+	struct nfcmrvl_fw_dnld *fw_dnld = container_of(work,
+						       struct nfcmrvl_fw_dnld,
+						       rx_work);
+	struct nfcmrvl_private *priv = container_of(fw_dnld,
+						    struct nfcmrvl_private,
+						    fw_dnld);
+
+	while ((skb = skb_dequeue(&fw_dnld->rx_q))) {
+		nfc_send_to_raw_sock(priv->ndev->nfc_dev, skb,
+				     RAW_PAYLOAD_NCI, NFC_DIRECTION_RX);
+		switch (fw_dnld->state) {
+		case STATE_RESET:
+			ret = process_state_reset(priv, skb);
+			break;
+		case STATE_INIT:
+			ret = process_state_init(priv, skb);
+			break;
+		case STATE_SET_REF_CLOCK:
+			ret = process_state_set_ref_clock(priv, skb);
+			break;
+		case STATE_SET_HI_CONFIG:
+			ret = process_state_set_hi_config(priv, skb);
+			break;
+		case STATE_OPEN_LC:
+			ret = process_state_open_lc(priv, skb);
+			break;
+		case STATE_FW_DNLD:
+			ret = process_state_fw_dnld(priv, skb);
+			break;
+		case STATE_CLOSE_LC:
+			ret = process_state_close_lc(priv, skb);
+			break;
+		case STATE_BOOT:
+			ret = process_state_boot(priv, skb);
+			break;
+		default:
+			ret = -EFAULT;
+		}
+
+		kfree_skb(skb);
+
+		if (ret != 0) {
+			nfc_err(priv->dev, "FW loading error");
+			fw_dnld_over(priv, ret);
+			break;
+		}
+	}
+}
+
+int	nfcmrvl_fw_dnld_init(struct nfcmrvl_private *priv)
+{
+	char name[32];
+
+	INIT_WORK(&priv->fw_dnld.rx_work, fw_dnld_rx_work);
+	snprintf(name, sizeof(name), "%s_nfcmrvl_fw_dnld_rx_wq",
+		 dev_name(&priv->ndev->nfc_dev->dev));
+	priv->fw_dnld.rx_wq = create_singlethread_workqueue(name);
+	if (!priv->fw_dnld.rx_wq)
+		return -ENOMEM;
+	skb_queue_head_init(&priv->fw_dnld.rx_q);
+	return 0;
+}
+
+void	nfcmrvl_fw_dnld_deinit(struct nfcmrvl_private *priv)
+{
+	destroy_workqueue(priv->fw_dnld.rx_wq);
+}
+
+void	nfcmrvl_fw_dnld_recv_frame(struct nfcmrvl_private *priv,
+				   struct sk_buff *skb)
+{
+	/* Discard command timer */
+	if (timer_pending(&priv->ndev->cmd_timer))
+		del_timer_sync(&priv->ndev->cmd_timer);
+
+	/* Allow next command */
+	atomic_set(&priv->ndev->cmd_cnt, 1);
+
+	/* Queue and trigger rx work */
+	skb_queue_tail(&priv->fw_dnld.rx_q, skb);
+	queue_work(priv->fw_dnld.rx_wq, &priv->fw_dnld.rx_work);
+}
+
+void nfcmrvl_fw_dnld_abort(struct nfcmrvl_private *priv)
+{
+	fw_dnld_over(priv, -EHOSTDOWN);
+}
+
+int nfcmrvl_fw_dnld_start(struct nci_dev *ndev, const char *firmware_name)
+{
+	struct nfcmrvl_private *priv = nci_get_drvdata(ndev);
+	struct nfcmrvl_fw_dnld *fw_dnld = &priv->fw_dnld;
+	int res;
+
+	if (!priv->support_fw_dnld)
+		return -ENOTSUPP;
+
+	if (!firmware_name || !firmware_name[0])
+		return -EINVAL;
+
+	strcpy(fw_dnld->name, firmware_name);
+
+	/*
+	 * Retrieve FW binary file and parse it to initialize FW download
+	 * state machine.
+	 */
+
+	/* Retrieve FW binary */
+	res = request_firmware(&fw_dnld->fw, firmware_name,
+			       &ndev->nfc_dev->dev);
+	if (res < 0) {
+		nfc_err(priv->dev, "failed to retrieve FW %s", firmware_name);
+		return -ENOENT;
+	}
+
+	fw_dnld->header = (const struct nfcmrvl_fw *) priv->fw_dnld.fw->data;
+
+	if (fw_dnld->header->magic != NFCMRVL_FW_MAGIC ||
+	    fw_dnld->header->phy != priv->phy) {
+		nfc_err(priv->dev, "bad firmware binary %s magic=0x%x phy=%d",
+			firmware_name, fw_dnld->header->magic,
+			fw_dnld->header->phy);
+		release_firmware(fw_dnld->fw);
+		fw_dnld->header = NULL;
+		return -EINVAL;
+	}
+
+	if (fw_dnld->header->helper.offset != 0) {
+		nfc_info(priv->dev, "loading helper");
+		fw_dnld->binary_config = &fw_dnld->header->helper;
+	} else {
+		nfc_info(priv->dev, "loading firmware");
+		fw_dnld->binary_config = &fw_dnld->header->firmware;
+	}
+
+	/* Configure a timer for timeout */
+	setup_timer(&priv->fw_dnld.timer, fw_dnld_timeout,
+		    (unsigned long) priv);
+	mod_timer(&priv->fw_dnld.timer,
+		  jiffies + msecs_to_jiffies(FW_DNLD_TIMEOUT));
+
+	/* Ronfigure HI to be sure that it is the bootrom values */
+	priv->if_ops->nci_update_config(priv,
+					&fw_dnld->header->bootrom.config);
+
+	/* Allow first command */
+	atomic_set(&priv->ndev->cmd_cnt, 1);
+
+	/* First, reset the chip */
+	priv->fw_dnld.state = STATE_RESET;
+	nfcmrvl_chip_reset(priv);
+
+	/* Now wait for CORE_RESET_NTF or timeout */
+
+	return 0;
+}
diff --git a/src/kernel/linux/v4.14/drivers/nfc/nfcmrvl/fw_dnld.h b/src/kernel/linux/v4.14/drivers/nfc/nfcmrvl/fw_dnld.h
new file mode 100644
index 0000000..ee4a339
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/nfc/nfcmrvl/fw_dnld.h
@@ -0,0 +1,98 @@
+/**
+ * Marvell NFC driver: Firmware downloader
+ *
+ * Copyright (C) 2015, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available on the worldwide web at
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ **/
+
+#ifndef __NFCMRVL_FW_DNLD_H__
+#define __NFCMRVL_FW_DNLD_H__
+
+#include <linux/workqueue.h>
+
+#define NFCMRVL_FW_MAGIC		0x88888888
+
+#define NCI_OP_PROP_BOOT_CMD		0x3A
+
+#define NCI_CORE_LC_PROP_FW_DL		0xFD
+#define NCI_CORE_LC_CONNID_PROP_FW_DL	0x02
+
+#define HELPER_CMD_ENTRY_POINT		0x04
+#define HELPER_CMD_PACKET_FORMAT	0xA5
+#define HELPER_ACK_PACKET_FORMAT	0x5A
+#define HELPER_RETRY_REQUESTED		(1 << 15)
+
+struct nfcmrvl_private;
+
+struct nfcmrvl_fw_uart_config {
+	uint8_t flow_control;
+	uint32_t baudrate;
+} __packed;
+
+struct nfcmrvl_fw_i2c_config {
+	uint32_t clk;
+} __packed;
+
+struct nfcmrvl_fw_spi_config {
+	uint32_t clk;
+} __packed;
+
+struct nfcmrvl_fw_binary_config {
+	uint32_t offset;
+	union {
+		void *config;
+		struct nfcmrvl_fw_uart_config uart;
+		struct nfcmrvl_fw_i2c_config i2c;
+		struct nfcmrvl_fw_spi_config spi;
+		uint8_t reserved[64];
+	};
+} __packed;
+
+struct nfcmrvl_fw {
+	uint32_t magic;
+	uint32_t ref_clock;
+	uint32_t phy;
+	struct nfcmrvl_fw_binary_config bootrom;
+	struct nfcmrvl_fw_binary_config helper;
+	struct nfcmrvl_fw_binary_config firmware;
+	uint8_t reserved[64];
+} __packed;
+
+struct nfcmrvl_fw_dnld {
+	char name[NFC_FIRMWARE_NAME_MAXSIZE + 1];
+	const struct firmware *fw;
+
+	const struct nfcmrvl_fw *header;
+	const struct nfcmrvl_fw_binary_config *binary_config;
+
+	int state;
+	int substate;
+	int offset;
+	int chunk_len;
+
+	struct workqueue_struct	*rx_wq;
+	struct work_struct rx_work;
+	struct sk_buff_head rx_q;
+
+	struct timer_list timer;
+};
+
+int nfcmrvl_fw_dnld_init(struct nfcmrvl_private *priv);
+void nfcmrvl_fw_dnld_deinit(struct nfcmrvl_private *priv);
+void nfcmrvl_fw_dnld_abort(struct nfcmrvl_private *priv);
+int nfcmrvl_fw_dnld_start(struct nci_dev *ndev, const char *firmware_name);
+void nfcmrvl_fw_dnld_recv_frame(struct nfcmrvl_private *priv,
+				struct sk_buff *skb);
+
+#endif
diff --git a/src/kernel/linux/v4.14/drivers/nfc/nfcmrvl/i2c.c b/src/kernel/linux/v4.14/drivers/nfc/nfcmrvl/i2c.c
new file mode 100644
index 0000000..ffec103
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/nfc/nfcmrvl/i2c.c
@@ -0,0 +1,290 @@
+/**
+ * Marvell NFC-over-I2C driver: I2C interface related functions
+ *
+ * Copyright (C) 2015, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available on the worldwide web at
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ **/
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/pm_runtime.h>
+#include <linux/nfc.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/of_irq.h>
+#include <linux/of_gpio.h>
+#include <net/nfc/nci.h>
+#include <net/nfc/nci_core.h>
+#include "nfcmrvl.h"
+
+struct nfcmrvl_i2c_drv_data {
+	unsigned long flags;
+	struct device *dev;
+	struct i2c_client *i2c;
+	struct nfcmrvl_private *priv;
+};
+
+static int nfcmrvl_i2c_read(struct nfcmrvl_i2c_drv_data *drv_data,
+			    struct sk_buff **skb)
+{
+	int ret;
+	struct nci_ctrl_hdr nci_hdr;
+
+	/* Read NCI header to know the payload size */
+	ret = i2c_master_recv(drv_data->i2c, (u8 *)&nci_hdr, NCI_CTRL_HDR_SIZE);
+	if (ret != NCI_CTRL_HDR_SIZE) {
+		nfc_err(&drv_data->i2c->dev, "cannot read NCI header\n");
+		return -EBADMSG;
+	}
+
+	if (nci_hdr.plen > NCI_MAX_PAYLOAD_SIZE) {
+		nfc_err(&drv_data->i2c->dev, "invalid packet payload size\n");
+		return -EBADMSG;
+	}
+
+	*skb = nci_skb_alloc(drv_data->priv->ndev,
+			     nci_hdr.plen + NCI_CTRL_HDR_SIZE, GFP_KERNEL);
+	if (!*skb)
+		return -ENOMEM;
+
+	/* Copy NCI header into the SKB */
+	skb_put_data(*skb, &nci_hdr, NCI_CTRL_HDR_SIZE);
+
+	if (nci_hdr.plen) {
+		/* Read the NCI payload */
+		ret = i2c_master_recv(drv_data->i2c,
+				      skb_put(*skb, nci_hdr.plen),
+				      nci_hdr.plen);
+
+		if (ret != nci_hdr.plen) {
+			nfc_err(&drv_data->i2c->dev,
+				"Invalid frame payload length: %u (expected %u)\n",
+				ret, nci_hdr.plen);
+			kfree_skb(*skb);
+			return -EBADMSG;
+		}
+	}
+
+	return 0;
+}
+
+static irqreturn_t nfcmrvl_i2c_int_irq_thread_fn(int irq, void *drv_data_ptr)
+{
+	struct nfcmrvl_i2c_drv_data *drv_data = drv_data_ptr;
+	struct sk_buff *skb = NULL;
+	int ret;
+
+	if (!drv_data->priv)
+		return IRQ_HANDLED;
+
+	if (test_bit(NFCMRVL_PHY_ERROR, &drv_data->priv->flags))
+		return IRQ_HANDLED;
+
+	ret = nfcmrvl_i2c_read(drv_data, &skb);
+
+	switch (ret) {
+	case -EREMOTEIO:
+		set_bit(NFCMRVL_PHY_ERROR, &drv_data->priv->flags);
+		break;
+	case -ENOMEM:
+	case -EBADMSG:
+		nfc_err(&drv_data->i2c->dev, "read failed %d\n", ret);
+		break;
+	default:
+		if (nfcmrvl_nci_recv_frame(drv_data->priv, skb) < 0)
+			nfc_err(&drv_data->i2c->dev, "corrupted RX packet\n");
+		break;
+	}
+	return IRQ_HANDLED;
+}
+
+static int nfcmrvl_i2c_nci_open(struct nfcmrvl_private *priv)
+{
+	struct nfcmrvl_i2c_drv_data *drv_data = priv->drv_data;
+
+	if (!drv_data)
+		return -ENODEV;
+
+	return 0;
+}
+
+static int nfcmrvl_i2c_nci_close(struct nfcmrvl_private *priv)
+{
+	return 0;
+}
+
+static int nfcmrvl_i2c_nci_send(struct nfcmrvl_private *priv,
+				struct sk_buff *skb)
+{
+	struct nfcmrvl_i2c_drv_data *drv_data = priv->drv_data;
+	int ret;
+
+	if (test_bit(NFCMRVL_PHY_ERROR, &priv->flags))
+		return -EREMOTEIO;
+
+	ret = i2c_master_send(drv_data->i2c, skb->data, skb->len);
+
+	/* Retry if chip was in standby */
+	if (ret == -EREMOTEIO) {
+		nfc_info(drv_data->dev, "chip may sleep, retry\n");
+		usleep_range(6000, 10000);
+		ret = i2c_master_send(drv_data->i2c, skb->data, skb->len);
+	}
+
+	if (ret >= 0) {
+		if (ret != skb->len) {
+			nfc_err(drv_data->dev,
+				"Invalid length sent: %u (expected %u)\n",
+				ret, skb->len);
+			ret = -EREMOTEIO;
+		} else
+			ret = 0;
+		kfree_skb(skb);
+	}
+
+	return ret;
+}
+
+static void nfcmrvl_i2c_nci_update_config(struct nfcmrvl_private *priv,
+					  const void *param)
+{
+}
+
+static struct nfcmrvl_if_ops i2c_ops = {
+	.nci_open = nfcmrvl_i2c_nci_open,
+	.nci_close = nfcmrvl_i2c_nci_close,
+	.nci_send = nfcmrvl_i2c_nci_send,
+	.nci_update_config = nfcmrvl_i2c_nci_update_config,
+};
+
+static int nfcmrvl_i2c_parse_dt(struct device_node *node,
+				struct nfcmrvl_platform_data *pdata)
+{
+	int ret;
+
+	ret = nfcmrvl_parse_dt(node, pdata);
+	if (ret < 0) {
+		pr_err("Failed to get generic entries\n");
+		return ret;
+	}
+
+	if (of_find_property(node, "i2c-int-falling", NULL))
+		pdata->irq_polarity = IRQF_TRIGGER_FALLING;
+	else
+		pdata->irq_polarity = IRQF_TRIGGER_RISING;
+
+	ret = irq_of_parse_and_map(node, 0);
+	if (ret < 0) {
+		pr_err("Unable to get irq, error: %d\n", ret);
+		return ret;
+	}
+	pdata->irq = ret;
+
+	return 0;
+}
+
+static int nfcmrvl_i2c_probe(struct i2c_client *client,
+			     const struct i2c_device_id *id)
+{
+	struct nfcmrvl_i2c_drv_data *drv_data;
+	struct nfcmrvl_platform_data *pdata;
+	struct nfcmrvl_platform_data config;
+	int ret;
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		nfc_err(&client->dev, "Need I2C_FUNC_I2C\n");
+		return -ENODEV;
+	}
+
+	drv_data = devm_kzalloc(&client->dev, sizeof(*drv_data), GFP_KERNEL);
+	if (!drv_data)
+		return -ENOMEM;
+
+	drv_data->i2c = client;
+	drv_data->dev = &client->dev;
+	drv_data->priv = NULL;
+
+	i2c_set_clientdata(client, drv_data);
+
+	pdata = client->dev.platform_data;
+
+	if (!pdata && client->dev.of_node)
+		if (nfcmrvl_i2c_parse_dt(client->dev.of_node, &config) == 0)
+			pdata = &config;
+
+	if (!pdata)
+		return -EINVAL;
+
+	/* Request the read IRQ */
+	ret = devm_request_threaded_irq(&drv_data->i2c->dev, pdata->irq,
+					NULL, nfcmrvl_i2c_int_irq_thread_fn,
+					pdata->irq_polarity | IRQF_ONESHOT,
+					"nfcmrvl_i2c_int", drv_data);
+	if (ret < 0) {
+		nfc_err(&drv_data->i2c->dev,
+			"Unable to register IRQ handler\n");
+		return ret;
+	}
+
+	drv_data->priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_I2C,
+						  drv_data, &i2c_ops,
+						  &drv_data->i2c->dev, pdata);
+
+	if (IS_ERR(drv_data->priv))
+		return PTR_ERR(drv_data->priv);
+
+	drv_data->priv->support_fw_dnld = true;
+
+	return 0;
+}
+
+static int nfcmrvl_i2c_remove(struct i2c_client *client)
+{
+	struct nfcmrvl_i2c_drv_data *drv_data = i2c_get_clientdata(client);
+
+	nfcmrvl_nci_unregister_dev(drv_data->priv);
+
+	return 0;
+}
+
+
+static const struct of_device_id of_nfcmrvl_i2c_match[] = {
+	{ .compatible = "marvell,nfc-i2c", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, of_nfcmrvl_i2c_match);
+
+static struct i2c_device_id nfcmrvl_i2c_id_table[] = {
+	{ "nfcmrvl_i2c", 0 },
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, nfcmrvl_i2c_id_table);
+
+static struct i2c_driver nfcmrvl_i2c_driver = {
+	.probe = nfcmrvl_i2c_probe,
+	.id_table = nfcmrvl_i2c_id_table,
+	.remove = nfcmrvl_i2c_remove,
+	.driver = {
+		.name		= "nfcmrvl_i2c",
+		.owner		= THIS_MODULE,
+		.of_match_table	= of_match_ptr(of_nfcmrvl_i2c_match),
+	},
+};
+
+module_i2c_driver(nfcmrvl_i2c_driver);
+
+MODULE_AUTHOR("Marvell International Ltd.");
+MODULE_DESCRIPTION("Marvell NFC-over-I2C driver");
+MODULE_LICENSE("GPL v2");
diff --git a/src/kernel/linux/v4.14/drivers/nfc/nfcmrvl/main.c b/src/kernel/linux/v4.14/drivers/nfc/nfcmrvl/main.c
new file mode 100644
index 0000000..529be35
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/nfc/nfcmrvl/main.c
@@ -0,0 +1,287 @@
+/*
+ * Marvell NFC driver: major functions
+ *
+ * Copyright (C) 2014-2015 Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available on the worldwide web at
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/of_gpio.h>
+#include <linux/nfc.h>
+#include <net/nfc/nci.h>
+#include <net/nfc/nci_core.h>
+#include "nfcmrvl.h"
+
+static int nfcmrvl_nci_open(struct nci_dev *ndev)
+{
+	struct nfcmrvl_private *priv = nci_get_drvdata(ndev);
+	int err;
+
+	if (test_and_set_bit(NFCMRVL_NCI_RUNNING, &priv->flags))
+		return 0;
+
+	/* Reset possible fault of previous session */
+	clear_bit(NFCMRVL_PHY_ERROR, &priv->flags);
+
+	err = priv->if_ops->nci_open(priv);
+
+	if (err)
+		clear_bit(NFCMRVL_NCI_RUNNING, &priv->flags);
+
+	return err;
+}
+
+static int nfcmrvl_nci_close(struct nci_dev *ndev)
+{
+	struct nfcmrvl_private *priv = nci_get_drvdata(ndev);
+
+	if (!test_and_clear_bit(NFCMRVL_NCI_RUNNING, &priv->flags))
+		return 0;
+
+	priv->if_ops->nci_close(priv);
+
+	return 0;
+}
+
+static int nfcmrvl_nci_send(struct nci_dev *ndev, struct sk_buff *skb)
+{
+	struct nfcmrvl_private *priv = nci_get_drvdata(ndev);
+
+	nfc_info(priv->dev, "send entry, len %d\n", skb->len);
+
+	skb->dev = (void *)ndev;
+
+	if (priv->config.hci_muxed) {
+		unsigned char *hdr;
+		unsigned char len = skb->len;
+
+		hdr = skb_push(skb, NFCMRVL_HCI_EVENT_HEADER_SIZE);
+		hdr[0] = NFCMRVL_HCI_COMMAND_CODE;
+		hdr[1] = NFCMRVL_HCI_OGF;
+		hdr[2] = NFCMRVL_HCI_OCF;
+		hdr[3] = len;
+	}
+
+	return priv->if_ops->nci_send(priv, skb);
+}
+
+static int nfcmrvl_nci_setup(struct nci_dev *ndev)
+{
+	__u8 val = 1;
+
+	nci_set_config(ndev, NFCMRVL_PB_BAIL_OUT, 1, &val);
+	return 0;
+}
+
+static int nfcmrvl_nci_fw_download(struct nci_dev *ndev,
+				   const char *firmware_name)
+{
+	return nfcmrvl_fw_dnld_start(ndev, firmware_name);
+}
+
+static struct nci_ops nfcmrvl_nci_ops = {
+	.open = nfcmrvl_nci_open,
+	.close = nfcmrvl_nci_close,
+	.send = nfcmrvl_nci_send,
+	.setup = nfcmrvl_nci_setup,
+	.fw_download = nfcmrvl_nci_fw_download,
+};
+
+struct nfcmrvl_private *nfcmrvl_nci_register_dev(enum nfcmrvl_phy phy,
+				void *drv_data,
+				struct nfcmrvl_if_ops *ops,
+				struct device *dev,
+				struct nfcmrvl_platform_data *pdata)
+{
+	struct nfcmrvl_private *priv;
+	int rc;
+	int headroom;
+	int tailroom;
+	u32 protocols;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return ERR_PTR(-ENOMEM);
+
+	priv->drv_data = drv_data;
+	priv->if_ops = ops;
+	priv->dev = dev;
+	priv->phy = phy;
+
+	memcpy(&priv->config, pdata, sizeof(*pdata));
+
+	if (gpio_is_valid(priv->config.reset_n_io)) {
+		rc = gpio_request_one(priv->config.reset_n_io,
+				      GPIOF_OUT_INIT_LOW,
+				      "nfcmrvl_reset_n");
+		if (rc < 0) {
+			priv->config.reset_n_io = -EINVAL;
+			nfc_err(dev, "failed to request reset_n io\n");
+		}
+	}
+
+	if (phy == NFCMRVL_PHY_SPI) {
+		headroom = NCI_SPI_HDR_LEN;
+		tailroom = 1;
+	} else
+		headroom = tailroom = 0;
+
+	if (priv->config.hci_muxed)
+		headroom += NFCMRVL_HCI_EVENT_HEADER_SIZE;
+
+	protocols = NFC_PROTO_JEWEL_MASK
+		| NFC_PROTO_MIFARE_MASK
+		| NFC_PROTO_FELICA_MASK
+		| NFC_PROTO_ISO14443_MASK
+		| NFC_PROTO_ISO14443_B_MASK
+		| NFC_PROTO_ISO15693_MASK
+		| NFC_PROTO_NFC_DEP_MASK;
+
+	priv->ndev = nci_allocate_device(&nfcmrvl_nci_ops, protocols,
+					 headroom, tailroom);
+	if (!priv->ndev) {
+		nfc_err(dev, "nci_allocate_device failed\n");
+		rc = -ENOMEM;
+		goto error_free_gpio;
+	}
+
+	rc = nfcmrvl_fw_dnld_init(priv);
+	if (rc) {
+		nfc_err(dev, "failed to initialize FW download %d\n", rc);
+		goto error_free_dev;
+	}
+
+	nci_set_drvdata(priv->ndev, priv);
+
+	rc = nci_register_device(priv->ndev);
+	if (rc) {
+		nfc_err(dev, "nci_register_device failed %d\n", rc);
+		goto error_fw_dnld_deinit;
+	}
+
+	/* Ensure that controller is powered off */
+	nfcmrvl_chip_halt(priv);
+
+	nfc_info(dev, "registered with nci successfully\n");
+	return priv;
+
+error_fw_dnld_deinit:
+	nfcmrvl_fw_dnld_deinit(priv);
+error_free_dev:
+	nci_free_device(priv->ndev);
+error_free_gpio:
+	if (gpio_is_valid(priv->config.reset_n_io))
+		gpio_free(priv->config.reset_n_io);
+	kfree(priv);
+	return ERR_PTR(rc);
+}
+EXPORT_SYMBOL_GPL(nfcmrvl_nci_register_dev);
+
+void nfcmrvl_nci_unregister_dev(struct nfcmrvl_private *priv)
+{
+	struct nci_dev *ndev = priv->ndev;
+
+	if (priv->ndev->nfc_dev->fw_download_in_progress)
+		nfcmrvl_fw_dnld_abort(priv);
+
+	nfcmrvl_fw_dnld_deinit(priv);
+
+	if (gpio_is_valid(priv->config.reset_n_io))
+		gpio_free(priv->config.reset_n_io);
+
+	nci_unregister_device(ndev);
+	nci_free_device(ndev);
+	kfree(priv);
+}
+EXPORT_SYMBOL_GPL(nfcmrvl_nci_unregister_dev);
+
+int nfcmrvl_nci_recv_frame(struct nfcmrvl_private *priv, struct sk_buff *skb)
+{
+	if (priv->config.hci_muxed) {
+		if (skb->data[0] == NFCMRVL_HCI_EVENT_CODE &&
+		    skb->data[1] == NFCMRVL_HCI_NFC_EVENT_CODE) {
+			/* Data packet, let's extract NCI payload */
+			skb_pull(skb, NFCMRVL_HCI_EVENT_HEADER_SIZE);
+		} else {
+			/* Skip this packet */
+			kfree_skb(skb);
+			return 0;
+		}
+	}
+
+	if (priv->ndev->nfc_dev->fw_download_in_progress) {
+		nfcmrvl_fw_dnld_recv_frame(priv, skb);
+		return 0;
+	}
+
+	if (test_bit(NFCMRVL_NCI_RUNNING, &priv->flags))
+		nci_recv_frame(priv->ndev, skb);
+	else {
+		/* Drop this packet since nobody wants it */
+		kfree_skb(skb);
+		return 0;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(nfcmrvl_nci_recv_frame);
+
+void nfcmrvl_chip_reset(struct nfcmrvl_private *priv)
+{
+	/* Reset possible fault of previous session */
+	clear_bit(NFCMRVL_PHY_ERROR, &priv->flags);
+
+	if (gpio_is_valid(priv->config.reset_n_io)) {
+		nfc_info(priv->dev, "reset the chip\n");
+		gpio_set_value(priv->config.reset_n_io, 0);
+		usleep_range(5000, 10000);
+		gpio_set_value(priv->config.reset_n_io, 1);
+	} else
+		nfc_info(priv->dev, "no reset available on this interface\n");
+}
+
+void nfcmrvl_chip_halt(struct nfcmrvl_private *priv)
+{
+	if (gpio_is_valid(priv->config.reset_n_io))
+		gpio_set_value(priv->config.reset_n_io, 0);
+}
+
+int nfcmrvl_parse_dt(struct device_node *node,
+		     struct nfcmrvl_platform_data *pdata)
+{
+	int reset_n_io;
+
+	reset_n_io = of_get_named_gpio(node, "reset-n-io", 0);
+	if (reset_n_io < 0) {
+		pr_info("no reset-n-io config\n");
+	} else if (!gpio_is_valid(reset_n_io)) {
+		pr_err("invalid reset-n-io GPIO\n");
+		return reset_n_io;
+	}
+	pdata->reset_n_io = reset_n_io;
+
+	if (of_find_property(node, "hci-muxed", NULL))
+		pdata->hci_muxed = 1;
+	else
+		pdata->hci_muxed = 0;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(nfcmrvl_parse_dt);
+
+MODULE_AUTHOR("Marvell International Ltd.");
+MODULE_DESCRIPTION("Marvell NFC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/src/kernel/linux/v4.14/drivers/nfc/nfcmrvl/nfcmrvl.h b/src/kernel/linux/v4.14/drivers/nfc/nfcmrvl/nfcmrvl.h
new file mode 100644
index 0000000..de68ff4
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/nfc/nfcmrvl/nfcmrvl.h
@@ -0,0 +1,117 @@
+/**
+ * Marvell NFC driver
+ *
+ * Copyright (C) 2014-2015, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available on the worldwide web at
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ **/
+
+#ifndef _NFCMRVL_H_
+#define _NFCMRVL_H_
+
+#include <linux/platform_data/nfcmrvl.h>
+
+#include "fw_dnld.h"
+
+/* Define private flags: */
+#define NFCMRVL_NCI_RUNNING			1
+#define NFCMRVL_PHY_ERROR			2
+
+#define NFCMRVL_EXT_COEX_ID			0xE0
+#define NFCMRVL_NOT_ALLOWED_ID			0xE1
+#define NFCMRVL_ACTIVE_ID			0xE2
+#define NFCMRVL_EXT_COEX_ENABLE			1
+#define NFCMRVL_GPIO_PIN_NFC_NOT_ALLOWED	0xA
+#define NFCMRVL_GPIO_PIN_NFC_ACTIVE		0xB
+#define NFCMRVL_NCI_MAX_EVENT_SIZE		260
+
+/*
+** NCI FW Parmaters
+*/
+
+#define NFCMRVL_PB_BAIL_OUT			0x11
+#define NFCMRVL_PROP_REF_CLOCK			0xF0
+#define NFCMRVL_PROP_SET_HI_CONFIG		0xF1
+
+/*
+** HCI defines
+*/
+
+#define NFCMRVL_HCI_EVENT_HEADER_SIZE		0x04
+#define NFCMRVL_HCI_EVENT_CODE			0x04
+#define NFCMRVL_HCI_NFC_EVENT_CODE		0xFF
+#define NFCMRVL_HCI_COMMAND_CODE		0x01
+#define NFCMRVL_HCI_OGF				0x81
+#define NFCMRVL_HCI_OCF				0xFE
+
+enum nfcmrvl_phy {
+	NFCMRVL_PHY_USB		= 0,
+	NFCMRVL_PHY_UART	= 1,
+	NFCMRVL_PHY_I2C		= 2,
+	NFCMRVL_PHY_SPI		= 3,
+};
+
+struct nfcmrvl_private {
+
+	unsigned long flags;
+
+	/* Platform configuration */
+	struct nfcmrvl_platform_data config;
+
+	/* Parent dev */
+	struct nci_dev *ndev;
+
+	/* FW download context */
+	struct nfcmrvl_fw_dnld fw_dnld;
+
+	/* FW download support */
+	bool support_fw_dnld;
+
+	/*
+	** PHY related information
+	*/
+
+	/* PHY driver context */
+	void *drv_data;
+	/* PHY device */
+	struct device *dev;
+	/* PHY type */
+	enum nfcmrvl_phy phy;
+	/* Low level driver ops */
+	struct nfcmrvl_if_ops *if_ops;
+};
+
+struct nfcmrvl_if_ops {
+	int (*nci_open) (struct nfcmrvl_private *priv);
+	int (*nci_close) (struct nfcmrvl_private *priv);
+	int (*nci_send) (struct nfcmrvl_private *priv, struct sk_buff *skb);
+	void (*nci_update_config)(struct nfcmrvl_private *priv,
+				  const void *param);
+};
+
+void nfcmrvl_nci_unregister_dev(struct nfcmrvl_private *priv);
+int nfcmrvl_nci_recv_frame(struct nfcmrvl_private *priv, struct sk_buff *skb);
+struct nfcmrvl_private *nfcmrvl_nci_register_dev(enum nfcmrvl_phy phy,
+				void *drv_data,
+				struct nfcmrvl_if_ops *ops,
+				struct device *dev,
+				struct nfcmrvl_platform_data *pdata);
+
+
+void nfcmrvl_chip_reset(struct nfcmrvl_private *priv);
+void nfcmrvl_chip_halt(struct nfcmrvl_private *priv);
+
+int nfcmrvl_parse_dt(struct device_node *node,
+		     struct nfcmrvl_platform_data *pdata);
+
+#endif
diff --git a/src/kernel/linux/v4.14/drivers/nfc/nfcmrvl/spi.c b/src/kernel/linux/v4.14/drivers/nfc/nfcmrvl/spi.c
new file mode 100644
index 0000000..8e0ddb4
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/nfc/nfcmrvl/spi.c
@@ -0,0 +1,226 @@
+/**
+ * Marvell NFC-over-SPI driver: SPI interface related functions
+ *
+ * Copyright (C) 2015, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available on the worldwide web at
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ **/
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/pm_runtime.h>
+#include <linux/nfc.h>
+#include <linux/gpio.h>
+#include <linux/of_irq.h>
+#include <linux/of_gpio.h>
+#include <net/nfc/nci.h>
+#include <net/nfc/nci_core.h>
+#include <linux/spi/spi.h>
+#include "nfcmrvl.h"
+
+#define SPI_WAIT_HANDSHAKE	1
+
+struct nfcmrvl_spi_drv_data {
+	unsigned long flags;
+	struct spi_device *spi;
+	struct nci_spi *nci_spi;
+	struct completion handshake_completion;
+	struct nfcmrvl_private *priv;
+};
+
+static irqreturn_t nfcmrvl_spi_int_irq_thread_fn(int irq, void *drv_data_ptr)
+{
+	struct nfcmrvl_spi_drv_data *drv_data = drv_data_ptr;
+	struct sk_buff *skb;
+
+	/*
+	 * Special case where we are waiting for SPI_INT deassertion to start a
+	 * transfer.
+	 */
+	if (test_and_clear_bit(SPI_WAIT_HANDSHAKE, &drv_data->flags)) {
+		complete(&drv_data->handshake_completion);
+		return IRQ_HANDLED;
+	}
+
+	/* Normal case, SPI_INT deasserted by slave to trigger a master read */
+
+	skb = nci_spi_read(drv_data->nci_spi);
+	if (!skb) {
+		nfc_err(&drv_data->spi->dev, "failed to read spi packet");
+		return IRQ_HANDLED;
+	}
+
+	if (nfcmrvl_nci_recv_frame(drv_data->priv, skb) < 0)
+		nfc_err(&drv_data->spi->dev, "corrupted RX packet");
+
+	return IRQ_HANDLED;
+}
+
+static int nfcmrvl_spi_nci_open(struct nfcmrvl_private *priv)
+{
+	return 0;
+}
+
+static int nfcmrvl_spi_nci_close(struct nfcmrvl_private *priv)
+{
+	return 0;
+}
+
+static int nfcmrvl_spi_nci_send(struct nfcmrvl_private *priv,
+				struct sk_buff *skb)
+{
+	struct nfcmrvl_spi_drv_data *drv_data = priv->drv_data;
+	int err;
+
+	/* Reinit completion for slave handshake */
+	reinit_completion(&drv_data->handshake_completion);
+	set_bit(SPI_WAIT_HANDSHAKE, &drv_data->flags);
+
+	/*
+	 * Append a dummy byte at the end of SPI frame. This is due to a
+	 * specific DMA implementation in the controller
+	 */
+	skb_put(skb, 1);
+
+	/* Send the SPI packet */
+	err = nci_spi_send(drv_data->nci_spi, &drv_data->handshake_completion,
+			   skb);
+	if (err)
+		nfc_err(priv->dev, "spi_send failed %d", err);
+
+	return err;
+}
+
+static void nfcmrvl_spi_nci_update_config(struct nfcmrvl_private *priv,
+					  const void *param)
+{
+	struct nfcmrvl_spi_drv_data *drv_data = priv->drv_data;
+	const struct nfcmrvl_fw_spi_config *config = param;
+
+	drv_data->nci_spi->xfer_speed_hz = config->clk;
+}
+
+static struct nfcmrvl_if_ops spi_ops = {
+	.nci_open = nfcmrvl_spi_nci_open,
+	.nci_close = nfcmrvl_spi_nci_close,
+	.nci_send = nfcmrvl_spi_nci_send,
+	.nci_update_config = nfcmrvl_spi_nci_update_config,
+};
+
+static int nfcmrvl_spi_parse_dt(struct device_node *node,
+				struct nfcmrvl_platform_data *pdata)
+{
+	int ret;
+
+	ret = nfcmrvl_parse_dt(node, pdata);
+	if (ret < 0) {
+		pr_err("Failed to get generic entries\n");
+		return ret;
+	}
+
+	ret = irq_of_parse_and_map(node, 0);
+	if (ret < 0) {
+		pr_err("Unable to get irq, error: %d\n", ret);
+		return ret;
+	}
+	pdata->irq = ret;
+
+	return 0;
+}
+
+static int nfcmrvl_spi_probe(struct spi_device *spi)
+{
+	struct nfcmrvl_platform_data *pdata;
+	struct nfcmrvl_platform_data config;
+	struct nfcmrvl_spi_drv_data *drv_data;
+	int ret = 0;
+
+	drv_data = devm_kzalloc(&spi->dev, sizeof(*drv_data), GFP_KERNEL);
+	if (!drv_data)
+		return -ENOMEM;
+
+	drv_data->spi = spi;
+	drv_data->priv = NULL;
+	spi_set_drvdata(spi, drv_data);
+
+	pdata = spi->dev.platform_data;
+
+	if (!pdata && spi->dev.of_node)
+		if (nfcmrvl_spi_parse_dt(spi->dev.of_node, &config) == 0)
+			pdata = &config;
+
+	if (!pdata)
+		return -EINVAL;
+
+	ret = devm_request_threaded_irq(&drv_data->spi->dev, pdata->irq,
+					NULL, nfcmrvl_spi_int_irq_thread_fn,
+					IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+					"nfcmrvl_spi_int", drv_data);
+	if (ret < 0) {
+		nfc_err(&drv_data->spi->dev, "Unable to register IRQ handler");
+		return -ENODEV;
+	}
+
+	drv_data->priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_SPI,
+						  drv_data, &spi_ops,
+						  &drv_data->spi->dev,
+						  pdata);
+	if (IS_ERR(drv_data->priv))
+		return PTR_ERR(drv_data->priv);
+
+	drv_data->priv->support_fw_dnld = true;
+
+	drv_data->nci_spi = nci_spi_allocate_spi(drv_data->spi, 0, 10,
+						 drv_data->priv->ndev);
+
+	/* Init completion for slave handshake */
+	init_completion(&drv_data->handshake_completion);
+	return 0;
+}
+
+static int nfcmrvl_spi_remove(struct spi_device *spi)
+{
+	struct nfcmrvl_spi_drv_data *drv_data = spi_get_drvdata(spi);
+
+	nfcmrvl_nci_unregister_dev(drv_data->priv);
+	return 0;
+}
+
+static const struct of_device_id of_nfcmrvl_spi_match[] = {
+	{ .compatible = "marvell,nfc-spi", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, of_nfcmrvl_spi_match);
+
+static const struct spi_device_id nfcmrvl_spi_id_table[] = {
+	{ "nfcmrvl_spi", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(spi, nfcmrvl_spi_id_table);
+
+static struct spi_driver nfcmrvl_spi_driver = {
+	.probe		= nfcmrvl_spi_probe,
+	.remove		= nfcmrvl_spi_remove,
+	.id_table	= nfcmrvl_spi_id_table,
+	.driver		= {
+		.name		= "nfcmrvl_spi",
+		.owner		= THIS_MODULE,
+		.of_match_table	= of_match_ptr(of_nfcmrvl_spi_match),
+	},
+};
+
+module_spi_driver(nfcmrvl_spi_driver);
+
+MODULE_AUTHOR("Marvell International Ltd.");
+MODULE_DESCRIPTION("Marvell NFC-over-SPI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/src/kernel/linux/v4.14/drivers/nfc/nfcmrvl/uart.c b/src/kernel/linux/v4.14/drivers/nfc/nfcmrvl/uart.c
new file mode 100644
index 0000000..e5a622c
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/nfc/nfcmrvl/uart.c
@@ -0,0 +1,235 @@
+/**
+ * Marvell NFC-over-UART driver
+ *
+ * Copyright (C) 2015, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available on the worldwide web at
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/of_gpio.h>
+#include <net/nfc/nci.h>
+#include <net/nfc/nci_core.h>
+#include "nfcmrvl.h"
+
+static unsigned int hci_muxed;
+static unsigned int flow_control;
+static unsigned int break_control;
+static int reset_n_io = -EINVAL;
+
+/*
+** NFCMRVL NCI OPS
+*/
+
+static int nfcmrvl_uart_nci_open(struct nfcmrvl_private *priv)
+{
+	return 0;
+}
+
+static int nfcmrvl_uart_nci_close(struct nfcmrvl_private *priv)
+{
+	return 0;
+}
+
+static int nfcmrvl_uart_nci_send(struct nfcmrvl_private *priv,
+				 struct sk_buff *skb)
+{
+	struct nci_uart *nu = priv->drv_data;
+
+	return nu->ops.send(nu, skb);
+}
+
+static void nfcmrvl_uart_nci_update_config(struct nfcmrvl_private *priv,
+					   const void *param)
+{
+	struct nci_uart *nu = priv->drv_data;
+	const struct nfcmrvl_fw_uart_config *config = param;
+
+	nci_uart_set_config(nu, le32_to_cpu(config->baudrate),
+			    config->flow_control);
+}
+
+static struct nfcmrvl_if_ops uart_ops = {
+	.nci_open = nfcmrvl_uart_nci_open,
+	.nci_close = nfcmrvl_uart_nci_close,
+	.nci_send = nfcmrvl_uart_nci_send,
+	.nci_update_config = nfcmrvl_uart_nci_update_config
+};
+
+static int nfcmrvl_uart_parse_dt(struct device_node *node,
+				 struct nfcmrvl_platform_data *pdata)
+{
+	struct device_node *matched_node;
+	int ret;
+
+	matched_node = of_get_compatible_child(node, "marvell,nfc-uart");
+	if (!matched_node) {
+		matched_node = of_get_compatible_child(node, "mrvl,nfc-uart");
+		if (!matched_node)
+			return -ENODEV;
+	}
+
+	ret = nfcmrvl_parse_dt(matched_node, pdata);
+	if (ret < 0) {
+		pr_err("Failed to get generic entries\n");
+		of_node_put(matched_node);
+		return ret;
+	}
+
+	if (of_find_property(matched_node, "flow-control", NULL))
+		pdata->flow_control = 1;
+	else
+		pdata->flow_control = 0;
+
+	if (of_find_property(matched_node, "break-control", NULL))
+		pdata->break_control = 1;
+	else
+		pdata->break_control = 0;
+
+	of_node_put(matched_node);
+
+	return 0;
+}
+
+/*
+** NCI UART OPS
+*/
+
+static int nfcmrvl_nci_uart_open(struct nci_uart *nu)
+{
+	struct nfcmrvl_private *priv;
+	struct nfcmrvl_platform_data *pdata = NULL;
+	struct nfcmrvl_platform_data config;
+	struct device *dev = nu->tty->dev;
+
+	/*
+	 * Platform data cannot be used here since usually it is already used
+	 * by low level serial driver. We can try to retrieve serial device
+	 * and check if DT entries were added.
+	 */
+
+	if (dev && dev->parent && dev->parent->of_node)
+		if (nfcmrvl_uart_parse_dt(dev->parent->of_node, &config) == 0)
+			pdata = &config;
+
+	if (!pdata) {
+		pr_info("No platform data / DT -> fallback to module params\n");
+		config.hci_muxed = hci_muxed;
+		config.reset_n_io = reset_n_io;
+		config.flow_control = flow_control;
+		config.break_control = break_control;
+		pdata = &config;
+	}
+
+	priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_UART, nu, &uart_ops,
+					dev, pdata);
+	if (IS_ERR(priv))
+		return PTR_ERR(priv);
+
+	priv->support_fw_dnld = true;
+
+	nu->drv_data = priv;
+	nu->ndev = priv->ndev;
+
+	return 0;
+}
+
+static void nfcmrvl_nci_uart_close(struct nci_uart *nu)
+{
+	nfcmrvl_nci_unregister_dev((struct nfcmrvl_private *)nu->drv_data);
+}
+
+static int nfcmrvl_nci_uart_recv(struct nci_uart *nu, struct sk_buff *skb)
+{
+	return nfcmrvl_nci_recv_frame((struct nfcmrvl_private *)nu->drv_data,
+				      skb);
+}
+
+static void nfcmrvl_nci_uart_tx_start(struct nci_uart *nu)
+{
+	struct nfcmrvl_private *priv = (struct nfcmrvl_private *)nu->drv_data;
+
+	if (priv->ndev->nfc_dev->fw_download_in_progress)
+		return;
+
+	/* Remove BREAK to wake up the NFCC */
+	if (priv->config.break_control && nu->tty->ops->break_ctl) {
+		nu->tty->ops->break_ctl(nu->tty, 0);
+		usleep_range(3000, 5000);
+	}
+}
+
+static void nfcmrvl_nci_uart_tx_done(struct nci_uart *nu)
+{
+	struct nfcmrvl_private *priv = (struct nfcmrvl_private *)nu->drv_data;
+
+	if (priv->ndev->nfc_dev->fw_download_in_progress)
+		return;
+
+	/*
+	** To ensure that if the NFCC goes in DEEP SLEEP sate we can wake him
+	** up. we set BREAK. Once we will be ready to send again we will remove
+	** it.
+	*/
+	if (priv->config.break_control && nu->tty->ops->break_ctl) {
+		nu->tty->ops->break_ctl(nu->tty, -1);
+		usleep_range(1000, 3000);
+	}
+}
+
+static struct nci_uart nfcmrvl_nci_uart = {
+	.owner  = THIS_MODULE,
+	.name   = "nfcmrvl_uart",
+	.driver = NCI_UART_DRIVER_MARVELL,
+	.ops	= {
+		.open		= nfcmrvl_nci_uart_open,
+		.close		= nfcmrvl_nci_uart_close,
+		.recv		= nfcmrvl_nci_uart_recv,
+		.tx_start	= nfcmrvl_nci_uart_tx_start,
+		.tx_done	= nfcmrvl_nci_uart_tx_done,
+	}
+};
+
+/*
+** Module init
+*/
+
+static int nfcmrvl_uart_init_module(void)
+{
+	return nci_uart_register(&nfcmrvl_nci_uart);
+}
+
+static void nfcmrvl_uart_exit_module(void)
+{
+	nci_uart_unregister(&nfcmrvl_nci_uart);
+}
+
+module_init(nfcmrvl_uart_init_module);
+module_exit(nfcmrvl_uart_exit_module);
+
+MODULE_AUTHOR("Marvell International Ltd.");
+MODULE_DESCRIPTION("Marvell NFC-over-UART");
+MODULE_LICENSE("GPL v2");
+
+module_param(flow_control, uint, 0);
+MODULE_PARM_DESC(flow_control, "Tell if UART needs flow control at init.");
+
+module_param(break_control, uint, 0);
+MODULE_PARM_DESC(break_control, "Tell if UART driver must drive break signal.");
+
+module_param(hci_muxed, uint, 0);
+MODULE_PARM_DESC(hci_muxed, "Tell if transport is muxed in HCI one.");
+
+module_param(reset_n_io, int, 0);
+MODULE_PARM_DESC(reset_n_io, "GPIO that is wired to RESET_N signal.");
diff --git a/src/kernel/linux/v4.14/drivers/nfc/nfcmrvl/usb.c b/src/kernel/linux/v4.14/drivers/nfc/nfcmrvl/usb.c
new file mode 100644
index 0000000..deb9532
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/nfc/nfcmrvl/usb.c
@@ -0,0 +1,471 @@
+/**
+ * Marvell NFC-over-USB driver: USB interface related functions
+ *
+ * Copyright (C) 2014, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available on the worldwide web at
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ **/
+
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/nfc.h>
+#include <net/nfc/nci.h>
+#include <net/nfc/nci_core.h>
+#include "nfcmrvl.h"
+
+static struct usb_device_id nfcmrvl_table[] = {
+	{ USB_DEVICE_AND_INTERFACE_INFO(0x1286, 0x2046,
+					USB_CLASS_VENDOR_SPEC, 4, 1) },
+	{ }	/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, nfcmrvl_table);
+
+#define NFCMRVL_USB_BULK_RUNNING	1
+#define NFCMRVL_USB_SUSPENDING		2
+
+struct nfcmrvl_usb_drv_data {
+	struct usb_device *udev;
+	struct usb_interface *intf;
+	unsigned long flags;
+	struct work_struct waker;
+	struct usb_anchor tx_anchor;
+	struct usb_anchor bulk_anchor;
+	struct usb_anchor deferred;
+	int tx_in_flight;
+	/* protects tx_in_flight */
+	spinlock_t txlock;
+	struct usb_endpoint_descriptor *bulk_tx_ep;
+	struct usb_endpoint_descriptor *bulk_rx_ep;
+	int suspend_count;
+	struct nfcmrvl_private *priv;
+};
+
+static int nfcmrvl_inc_tx(struct nfcmrvl_usb_drv_data *drv_data)
+{
+	unsigned long flags;
+	int rv;
+
+	spin_lock_irqsave(&drv_data->txlock, flags);
+	rv = test_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
+	if (!rv)
+		drv_data->tx_in_flight++;
+	spin_unlock_irqrestore(&drv_data->txlock, flags);
+
+	return rv;
+}
+
+static void nfcmrvl_bulk_complete(struct urb *urb)
+{
+	struct nfcmrvl_usb_drv_data *drv_data = urb->context;
+	struct sk_buff *skb;
+	int err;
+
+	dev_dbg(&drv_data->udev->dev, "urb %p status %d count %d\n",
+		urb, urb->status, urb->actual_length);
+
+	if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))
+		return;
+
+	if (!urb->status) {
+		skb = nci_skb_alloc(drv_data->priv->ndev, urb->actual_length,
+				    GFP_ATOMIC);
+		if (!skb) {
+			nfc_err(&drv_data->udev->dev, "failed to alloc mem\n");
+		} else {
+			skb_put_data(skb, urb->transfer_buffer,
+				     urb->actual_length);
+			if (nfcmrvl_nci_recv_frame(drv_data->priv, skb) < 0)
+				nfc_err(&drv_data->udev->dev,
+					"corrupted Rx packet\n");
+		}
+	}
+
+	if (!test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags))
+		return;
+
+	usb_anchor_urb(urb, &drv_data->bulk_anchor);
+	usb_mark_last_busy(drv_data->udev);
+
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err) {
+		/* -EPERM: urb is being killed;
+		 * -ENODEV: device got disconnected
+		 */
+		if (err != -EPERM && err != -ENODEV)
+			nfc_err(&drv_data->udev->dev,
+				"urb %p failed to resubmit (%d)\n", urb, -err);
+		usb_unanchor_urb(urb);
+	}
+}
+
+static int
+nfcmrvl_submit_bulk_urb(struct nfcmrvl_usb_drv_data *drv_data, gfp_t mem_flags)
+{
+	struct urb *urb;
+	unsigned char *buf;
+	unsigned int pipe;
+	int err, size = NFCMRVL_NCI_MAX_EVENT_SIZE;
+
+	if (!drv_data->bulk_rx_ep)
+		return -ENODEV;
+
+	urb = usb_alloc_urb(0, mem_flags);
+	if (!urb)
+		return -ENOMEM;
+
+	buf = kmalloc(size, mem_flags);
+	if (!buf) {
+		usb_free_urb(urb);
+		return -ENOMEM;
+	}
+
+	pipe = usb_rcvbulkpipe(drv_data->udev,
+			       drv_data->bulk_rx_ep->bEndpointAddress);
+
+	usb_fill_bulk_urb(urb, drv_data->udev, pipe, buf, size,
+			  nfcmrvl_bulk_complete, drv_data);
+
+	urb->transfer_flags |= URB_FREE_BUFFER;
+
+	usb_mark_last_busy(drv_data->udev);
+	usb_anchor_urb(urb, &drv_data->bulk_anchor);
+
+	err = usb_submit_urb(urb, mem_flags);
+	if (err) {
+		if (err != -EPERM && err != -ENODEV)
+			nfc_err(&drv_data->udev->dev,
+				"urb %p submission failed (%d)\n", urb, -err);
+		usb_unanchor_urb(urb);
+	}
+
+	usb_free_urb(urb);
+
+	return err;
+}
+
+static void nfcmrvl_tx_complete(struct urb *urb)
+{
+	struct sk_buff *skb = urb->context;
+	struct nci_dev *ndev = (struct nci_dev *)skb->dev;
+	struct nfcmrvl_private *priv = nci_get_drvdata(ndev);
+	struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
+
+	nfc_info(priv->dev, "urb %p status %d count %d\n",
+		 urb, urb->status, urb->actual_length);
+
+	spin_lock(&drv_data->txlock);
+	drv_data->tx_in_flight--;
+	spin_unlock(&drv_data->txlock);
+
+	kfree(urb->setup_packet);
+	kfree_skb(skb);
+}
+
+static int nfcmrvl_usb_nci_open(struct nfcmrvl_private *priv)
+{
+	struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
+	int err;
+
+	err = usb_autopm_get_interface(drv_data->intf);
+	if (err)
+		return err;
+
+	drv_data->intf->needs_remote_wakeup = 1;
+
+	err = nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL);
+	if (err)
+		goto failed;
+
+	set_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
+	nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL);
+
+	usb_autopm_put_interface(drv_data->intf);
+	return 0;
+
+failed:
+	usb_autopm_put_interface(drv_data->intf);
+	return err;
+}
+
+static void nfcmrvl_usb_stop_traffic(struct nfcmrvl_usb_drv_data *drv_data)
+{
+	usb_kill_anchored_urbs(&drv_data->bulk_anchor);
+}
+
+static int nfcmrvl_usb_nci_close(struct nfcmrvl_private *priv)
+{
+	struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
+	int err;
+
+	cancel_work_sync(&drv_data->waker);
+
+	clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
+
+	nfcmrvl_usb_stop_traffic(drv_data);
+	usb_kill_anchored_urbs(&drv_data->tx_anchor);
+	err = usb_autopm_get_interface(drv_data->intf);
+	if (err)
+		goto failed;
+
+	drv_data->intf->needs_remote_wakeup = 0;
+	usb_autopm_put_interface(drv_data->intf);
+
+failed:
+	usb_scuttle_anchored_urbs(&drv_data->deferred);
+	return 0;
+}
+
+static int nfcmrvl_usb_nci_send(struct nfcmrvl_private *priv,
+				struct sk_buff *skb)
+{
+	struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
+	struct urb *urb;
+	unsigned int pipe;
+	int err;
+
+	if (!drv_data->bulk_tx_ep)
+		return -ENODEV;
+
+	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	if (!urb)
+		return -ENOMEM;
+
+	pipe = usb_sndbulkpipe(drv_data->udev,
+				drv_data->bulk_tx_ep->bEndpointAddress);
+
+	usb_fill_bulk_urb(urb, drv_data->udev, pipe, skb->data, skb->len,
+			  nfcmrvl_tx_complete, skb);
+
+	err = nfcmrvl_inc_tx(drv_data);
+	if (err) {
+		usb_anchor_urb(urb, &drv_data->deferred);
+		schedule_work(&drv_data->waker);
+		err = 0;
+		goto done;
+	}
+
+	usb_anchor_urb(urb, &drv_data->tx_anchor);
+
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err) {
+		if (err != -EPERM && err != -ENODEV)
+			nfc_err(&drv_data->udev->dev,
+				"urb %p submission failed (%d)\n", urb, -err);
+		kfree(urb->setup_packet);
+		usb_unanchor_urb(urb);
+	} else {
+		usb_mark_last_busy(drv_data->udev);
+	}
+
+done:
+	usb_free_urb(urb);
+	return err;
+}
+
+static struct nfcmrvl_if_ops usb_ops = {
+	.nci_open = nfcmrvl_usb_nci_open,
+	.nci_close = nfcmrvl_usb_nci_close,
+	.nci_send = nfcmrvl_usb_nci_send,
+};
+
+static void nfcmrvl_waker(struct work_struct *work)
+{
+	struct nfcmrvl_usb_drv_data *drv_data =
+			container_of(work, struct nfcmrvl_usb_drv_data, waker);
+	int err;
+
+	err = usb_autopm_get_interface(drv_data->intf);
+	if (err)
+		return;
+
+	usb_autopm_put_interface(drv_data->intf);
+}
+
+static int nfcmrvl_probe(struct usb_interface *intf,
+			 const struct usb_device_id *id)
+{
+	struct usb_endpoint_descriptor *ep_desc;
+	struct nfcmrvl_usb_drv_data *drv_data;
+	struct nfcmrvl_private *priv;
+	int i;
+	struct usb_device *udev = interface_to_usbdev(intf);
+	struct nfcmrvl_platform_data config;
+
+	/* No configuration for USB */
+	memset(&config, 0, sizeof(config));
+	config.reset_n_io = -EINVAL;
+
+	nfc_info(&udev->dev, "intf %p id %p\n", intf, id);
+
+	drv_data = devm_kzalloc(&intf->dev, sizeof(*drv_data), GFP_KERNEL);
+	if (!drv_data)
+		return -ENOMEM;
+
+	for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) {
+		ep_desc = &intf->cur_altsetting->endpoint[i].desc;
+
+		if (!drv_data->bulk_tx_ep &&
+		    usb_endpoint_is_bulk_out(ep_desc)) {
+			drv_data->bulk_tx_ep = ep_desc;
+			continue;
+		}
+
+		if (!drv_data->bulk_rx_ep &&
+		    usb_endpoint_is_bulk_in(ep_desc)) {
+			drv_data->bulk_rx_ep = ep_desc;
+			continue;
+		}
+	}
+
+	if (!drv_data->bulk_tx_ep || !drv_data->bulk_rx_ep)
+		return -ENODEV;
+
+	drv_data->udev = udev;
+	drv_data->intf = intf;
+
+	INIT_WORK(&drv_data->waker, nfcmrvl_waker);
+	spin_lock_init(&drv_data->txlock);
+
+	init_usb_anchor(&drv_data->tx_anchor);
+	init_usb_anchor(&drv_data->bulk_anchor);
+	init_usb_anchor(&drv_data->deferred);
+
+	priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_USB, drv_data, &usb_ops,
+					&intf->dev, &config);
+	if (IS_ERR(priv))
+		return PTR_ERR(priv);
+
+	drv_data->priv = priv;
+	drv_data->priv->support_fw_dnld = false;
+
+	usb_set_intfdata(intf, drv_data);
+
+	return 0;
+}
+
+static void nfcmrvl_disconnect(struct usb_interface *intf)
+{
+	struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf);
+
+	if (!drv_data)
+		return;
+
+	nfc_info(&drv_data->udev->dev, "intf %p\n", intf);
+
+	nfcmrvl_nci_unregister_dev(drv_data->priv);
+
+	usb_set_intfdata(drv_data->intf, NULL);
+}
+
+#ifdef CONFIG_PM
+static int nfcmrvl_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf);
+
+	nfc_info(&drv_data->udev->dev, "intf %p\n", intf);
+
+	if (drv_data->suspend_count++)
+		return 0;
+
+	spin_lock_irq(&drv_data->txlock);
+	if (!(PMSG_IS_AUTO(message) && drv_data->tx_in_flight)) {
+		set_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
+		spin_unlock_irq(&drv_data->txlock);
+	} else {
+		spin_unlock_irq(&drv_data->txlock);
+		drv_data->suspend_count--;
+		return -EBUSY;
+	}
+
+	nfcmrvl_usb_stop_traffic(drv_data);
+	usb_kill_anchored_urbs(&drv_data->tx_anchor);
+
+	return 0;
+}
+
+static void nfcmrvl_play_deferred(struct nfcmrvl_usb_drv_data *drv_data)
+{
+	struct urb *urb;
+	int err;
+
+	while ((urb = usb_get_from_anchor(&drv_data->deferred))) {
+		err = usb_submit_urb(urb, GFP_ATOMIC);
+		if (err)
+			break;
+
+		drv_data->tx_in_flight++;
+	}
+	usb_scuttle_anchored_urbs(&drv_data->deferred);
+}
+
+static int nfcmrvl_resume(struct usb_interface *intf)
+{
+	struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf);
+	int err = 0;
+
+	nfc_info(&drv_data->udev->dev, "intf %p\n", intf);
+
+	if (--drv_data->suspend_count)
+		return 0;
+
+	if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))
+		goto done;
+
+	if (test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags)) {
+		err = nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO);
+		if (err) {
+			clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
+			goto failed;
+		}
+
+		nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO);
+	}
+
+	spin_lock_irq(&drv_data->txlock);
+	nfcmrvl_play_deferred(drv_data);
+	clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
+	spin_unlock_irq(&drv_data->txlock);
+
+	return 0;
+
+failed:
+	usb_scuttle_anchored_urbs(&drv_data->deferred);
+done:
+	spin_lock_irq(&drv_data->txlock);
+	clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
+	spin_unlock_irq(&drv_data->txlock);
+
+	return err;
+}
+#endif
+
+static struct usb_driver nfcmrvl_usb_driver = {
+	.name		= "nfcmrvl",
+	.probe		= nfcmrvl_probe,
+	.disconnect	= nfcmrvl_disconnect,
+#ifdef CONFIG_PM
+	.suspend	= nfcmrvl_suspend,
+	.resume		= nfcmrvl_resume,
+	.reset_resume	= nfcmrvl_resume,
+#endif
+	.id_table	= nfcmrvl_table,
+	.supports_autosuspend = 1,
+	.disable_hub_initiated_lpm = 1,
+	.soft_unbind = 1,
+};
+module_usb_driver(nfcmrvl_usb_driver);
+
+MODULE_AUTHOR("Marvell International Ltd.");
+MODULE_DESCRIPTION("Marvell NFC-over-USB driver");
+MODULE_LICENSE("GPL v2");