linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 0/9] Adds Intel FieldsPeak NFC solution driver
@ 2015-09-01 14:54 Robert Dolca
  2015-09-01 14:54 ` [PATCH v2 1/9] nfc: nci: Export nci data send API Robert Dolca
                   ` (8 more replies)
  0 siblings, 9 replies; 10+ messages in thread
From: Robert Dolca @ 2015-09-01 14:54 UTC (permalink / raw)
  To: linux-nfc, Lauro Ramos Venancio, Aloisio Almeida Jr, Samuel Ortiz
  Cc: linux-kernel, linux-wireless, netdev, Christophe Ricard, Robert Dolca

These patches add support for Intel's FieldsPeak NFC solution.

Fields Peak complies with the ISO/IEC 14443A/B, 15693, 18092,
and JIS X 6319-4. It is an NCI based controller.

RF Protocols supported:
 - NFC Forum Type 1 Tags (Jewel, Topaz)
 - NFC Forum Type 2 Tags (Mifare UL)
 - NFC Forum Type 3 Tags (FeliCa)
 - NFC Forum Type 4A (ISO/IEC 14443 A-4 106kbps to 848kbps)
 - NFC Forum Type 4B (ISO/IEC 14443 B-4 106kbps to 848kbps)
 - NFCIP in passive and active modes (ISO/IEC 18092 106kbps to 424kbps)
 - B’ (based on ISO/IEC 14443 B-2)
 - iCLASS (based on ISO/IEC 15693-2)
 - Vicinity cards (ISO/IEC 15693-3)
 - Kovio tags (NFC Forum Type 2)

The device can be enumerated using ACPI using the id INT339A.
The 1st GPIO is the IRQ and the 2nd is the RESET pin.

Changes since v1:
 - removed .owner
 - made local functions static
 - replaced nfc_info with dev_dbg
 - make controller setup possible without OTP patch
 - removed the commint that was adding nci_init/reset and start using
   the newly added functions nci_core_init/reset
 - fixed typo and use full controller name instead of abreviation in Kconfig
 - added more info about the controller in the commit message
 - moved NCI_OP_CORE_RESET_NTF, NCI_OP_CORE_GET_CONFIG_RSP and
   NCI_OP_CORE_GET_CONFIG_CMD into the core header files
 - added MODULE_DEVICE_TABLE for ACPI
 - defined FDP_FW_UPDATE_DEST 0xC2
 - release the firmware buffers
 - settings the vendor configuration and clock on post setup
 - platform data enumerations removed (ACPI enumeration left)
 - packet reception intercept logic remove
 - nci: removed "NFC: NCI: Adds NCI init and reset API for drivers"
 - nci: removed nci_send_cmd symbol export
 - nci: removed nci_request_driver and nci_req_complete_driver
 - removed mutex from driver (no need for it)
 - nci: check the setup return code before callig post_setup
 - nci: add function to allow sending core commands from driver
 - nci: Use a separate mutex for nci open and close
 - nci: mutex for: Adds a way to get the new connection ID
 - nci: Allow the driver to set handler for core nci ops

Robert Dolca (9):
  nfc: nci: Exporting nci data send API
  nfc: nci: Add function to get max packet size for conn
  nfc: nci: Define new core opcodes
  nfc: nci: Allow the driver to set handler for core nci ops
  nfc: nci: Do not call post_setup when setup fails
  nfc: nci: Add function to allow sending core commands from driver
  nfc: nci: Use a separate mutex for nci open and close
  nfc: nci: Adds a way to get the new connection ID
  nfc: Add Intel Fields Peak NFC solution driver

 drivers/nfc/Kconfig            |   1 +
 drivers/nfc/Makefile           |   1 +
 drivers/nfc/fdp/Kconfig        |  23 ++
 drivers/nfc/fdp/Makefile       |   9 +
 drivers/nfc/fdp/fdp.c          | 777 +++++++++++++++++++++++++++++++++++++++++
 drivers/nfc/fdp/fdp.h          |  52 +++
 drivers/nfc/fdp/i2c.c          | 416 ++++++++++++++++++++++
 drivers/nfc/st-nci/st-nci_se.c |   2 +-
 include/net/nfc/nci.h          |   7 +
 include/net/nfc/nci_core.h     |  29 +-
 net/nfc/nci/core.c             | 108 +++---
 net/nfc/nci/data.c             |  13 +
 net/nfc/nci/ntf.c              |   6 +-
 net/nfc/nci/rsp.c              |  12 +-
 14 files changed, 1403 insertions(+), 53 deletions(-)
 create mode 100644 drivers/nfc/fdp/Kconfig
 create mode 100644 drivers/nfc/fdp/Makefile
 create mode 100644 drivers/nfc/fdp/fdp.c
 create mode 100644 drivers/nfc/fdp/fdp.h
 create mode 100644 drivers/nfc/fdp/i2c.c

-- 
1.9.1


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH v2 1/9] nfc: nci: Export nci data send API
  2015-09-01 14:54 [PATCH v2 0/9] Adds Intel FieldsPeak NFC solution driver Robert Dolca
@ 2015-09-01 14:54 ` Robert Dolca
  2015-09-01 14:54 ` [PATCH v2 2/9] nfc: nci: Add function to get max packet size for conn Robert Dolca
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Robert Dolca @ 2015-09-01 14:54 UTC (permalink / raw)
  To: linux-nfc, Lauro Ramos Venancio, Aloisio Almeida Jr, Samuel Ortiz
  Cc: linux-kernel, linux-wireless, netdev, Christophe Ricard, Robert Dolca

For the firmware update the driver may use nci_send_data.

Signed-off-by: Robert Dolca <robert.dolca@intel.com>
---
 net/nfc/nci/data.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/net/nfc/nci/data.c b/net/nfc/nci/data.c
index 566466d..83acd18 100644
--- a/net/nfc/nci/data.c
+++ b/net/nfc/nci/data.c
@@ -203,6 +203,7 @@ free_exit:
 exit:
 	return rc;
 }
+EXPORT_SYMBOL(nci_send_data);
 
 /* ----------------- NCI RX Data ----------------- */
 
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v2 2/9] nfc: nci: Add function to get max packet size for conn
  2015-09-01 14:54 [PATCH v2 0/9] Adds Intel FieldsPeak NFC solution driver Robert Dolca
  2015-09-01 14:54 ` [PATCH v2 1/9] nfc: nci: Export nci data send API Robert Dolca
@ 2015-09-01 14:54 ` Robert Dolca
  2015-09-01 14:54 ` [PATCH v2 3/9] nfc: nci: Introduce new core opcodes Robert Dolca
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Robert Dolca @ 2015-09-01 14:54 UTC (permalink / raw)
  To: linux-nfc, Lauro Ramos Venancio, Aloisio Almeida Jr, Samuel Ortiz
  Cc: linux-kernel, linux-wireless, netdev, Christophe Ricard, Robert Dolca

FDP driver needs to send the firmware as regular packets
(not fragmented). The driver should have a way to
get the max packet size for a given connection.

Signed-off-by: Robert Dolca <robert.dolca@intel.com>
---
 include/net/nfc/nci_core.h |  1 +
 net/nfc/nci/data.c         | 12 ++++++++++++
 2 files changed, 13 insertions(+)

diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h
index d0d0f1e..8a4ab1e 100644
--- a/include/net/nfc/nci_core.h
+++ b/include/net/nfc/nci_core.h
@@ -351,6 +351,7 @@ int nci_prop_ntf_packet(struct nci_dev *ndev, __u16 opcode,
 void nci_rx_data_packet(struct nci_dev *ndev, struct sk_buff *skb);
 int nci_send_cmd(struct nci_dev *ndev, __u16 opcode, __u8 plen, void *payload);
 int nci_send_data(struct nci_dev *ndev, __u8 conn_id, struct sk_buff *skb);
+__u8 nci_conn_max_data_pkt_payload_size(struct nci_dev *ndev, __u8 conn_id);
 void nci_data_exchange_complete(struct nci_dev *ndev, struct sk_buff *skb,
 				__u8 conn_id, int err);
 void nci_hci_data_received_cb(void *context, struct sk_buff *skb, int err);
diff --git a/net/nfc/nci/data.c b/net/nfc/nci/data.c
index 83acd18..30cbed7 100644
--- a/net/nfc/nci/data.c
+++ b/net/nfc/nci/data.c
@@ -90,6 +90,18 @@ static inline void nci_push_data_hdr(struct nci_dev *ndev,
 	nci_pbf_set((__u8 *)hdr, pbf);
 }
 
+__u8 nci_conn_max_data_pkt_payload_size(struct nci_dev *ndev, __u8 conn_id)
+{
+	struct nci_conn_info *conn_info;
+
+	conn_info = nci_get_conn_info_by_conn_id(ndev, conn_id);
+	if (!conn_info)
+		return -EPROTO;
+
+	return conn_info->max_pkt_payload_len;
+}
+EXPORT_SYMBOL(nci_conn_max_data_pkt_payload_size);
+
 static int nci_queue_tx_data_frags(struct nci_dev *ndev,
 				   __u8 conn_id,
 				   struct sk_buff *skb) {
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v2 3/9] nfc: nci: Introduce new core opcodes
  2015-09-01 14:54 [PATCH v2 0/9] Adds Intel FieldsPeak NFC solution driver Robert Dolca
  2015-09-01 14:54 ` [PATCH v2 1/9] nfc: nci: Export nci data send API Robert Dolca
  2015-09-01 14:54 ` [PATCH v2 2/9] nfc: nci: Add function to get max packet size for conn Robert Dolca
@ 2015-09-01 14:54 ` Robert Dolca
  2015-09-01 14:54 ` [PATCH v2 4/9] nfc: nci: Allow the driver to set handler for core nci ops Robert Dolca
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Robert Dolca @ 2015-09-01 14:54 UTC (permalink / raw)
  To: linux-nfc, Lauro Ramos Venancio, Aloisio Almeida Jr, Samuel Ortiz
  Cc: linux-kernel, linux-wireless, netdev, Christophe Ricard, Robert Dolca

Add NCI_OP_CORE_GET_CONFIG_CMD, NCI_OP_CORE_GET_CONFIG_RSP
and NCI_OP_CORE_RESET_NTF.

Signed-off-by: Robert Dolca <robert.dolca@intel.com>
---
 include/net/nfc/nci.h | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/include/net/nfc/nci.h b/include/net/nfc/nci.h
index 75d2e18..b495825 100644
--- a/include/net/nfc/nci.h
+++ b/include/net/nfc/nci.h
@@ -315,6 +315,8 @@ struct nci_nfcee_mode_set_cmd {
 	__u8	nfcee_mode;
 } __packed;
 
+#define NCI_OP_CORE_GET_CONFIG_CMD	nci_opcode_pack(NCI_GID_CORE, 0x03)
+
 /* ----------------------- */
 /* ---- NCI Responses ---- */
 /* ----------------------- */
@@ -375,6 +377,9 @@ struct nci_nfcee_discover_rsp {
 } __packed;
 
 #define NCI_OP_NFCEE_MODE_SET_RSP nci_opcode_pack(NCI_GID_NFCEE_MGMT, 0x01)
+
+#define NCI_OP_CORE_GET_CONFIG_RSP	nci_opcode_pack(NCI_GID_CORE, 0x03)
+
 /* --------------------------- */
 /* ---- NCI Notifications ---- */
 /* --------------------------- */
@@ -528,4 +533,6 @@ struct nci_nfcee_discover_ntf {
 	struct nci_nfcee_information_tlv	information_tlv;
 } __packed;
 
+#define NCI_OP_CORE_RESET_NTF		nci_opcode_pack(NCI_GID_CORE, 0x00)
+
 #endif /* __NCI_H */
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v2 4/9] nfc: nci: Allow the driver to set handler for core nci ops
  2015-09-01 14:54 [PATCH v2 0/9] Adds Intel FieldsPeak NFC solution driver Robert Dolca
                   ` (2 preceding siblings ...)
  2015-09-01 14:54 ` [PATCH v2 3/9] nfc: nci: Introduce new core opcodes Robert Dolca
@ 2015-09-01 14:54 ` Robert Dolca
  2015-09-01 14:54 ` [PATCH v2 5/9] nfc: nci: Do not call post_setup when setup fails Robert Dolca
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Robert Dolca @ 2015-09-01 14:54 UTC (permalink / raw)
  To: linux-nfc, Lauro Ramos Venancio, Aloisio Almeida Jr, Samuel Ortiz
  Cc: linux-kernel, linux-wireless, netdev, Christophe Ricard, Robert Dolca

The driver may be required to act when some responses or notifications
arrive. For example the NCI core does not have a handler for
NCI_OP_CORE_GET_CONFIG_RSP. The NFCC can send a config response that has
to be read by the driver and the packet may contain vendor specific data.

The Fields Peak driver needs to take certain actions when a reset
notification arrives (packet also not handled by the nfc core).

The driver handlers do not interfere with the core and they are called
after the core processes the packet.

Signed-off-by: Robert Dolca <robert.dolca@intel.com>
---
 include/net/nfc/nci_core.h | 13 +++++++++----
 net/nfc/nci/core.c         | 43 +++++++++++++++++++++++--------------------
 net/nfc/nci/ntf.c          |  6 +++++-
 net/nfc/nci/rsp.c          |  6 +++++-
 4 files changed, 42 insertions(+), 26 deletions(-)

diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h
index 8a4ab1e..30c1b21 100644
--- a/include/net/nfc/nci_core.h
+++ b/include/net/nfc/nci_core.h
@@ -96,6 +96,9 @@ struct nci_ops {
 
 	struct nci_prop_ops *prop_ops;
 	size_t n_prop_ops;
+
+	struct nci_prop_ops *core_ops;
+	size_t n_core_ops;
 };
 
 #define NCI_MAX_SUPPORTED_RF_INTERFACES		4
@@ -344,10 +347,12 @@ static inline int nci_set_vendor_cmds(struct nci_dev *ndev,
 
 void nci_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb);
 void nci_ntf_packet(struct nci_dev *ndev, struct sk_buff *skb);
-int nci_prop_rsp_packet(struct nci_dev *ndev, __u16 opcode,
-			struct sk_buff *skb);
-int nci_prop_ntf_packet(struct nci_dev *ndev, __u16 opcode,
-			struct sk_buff *skb);
+int nci_op_rsp_packet(struct nci_dev *ndev, __u16 rsp_opcode,
+		      struct sk_buff *skb, struct nci_prop_ops *ops,
+		      size_t n_ops);
+int nci_op_ntf_packet(struct nci_dev *ndev, __u16 ntf_opcode,
+		      struct sk_buff *skb, struct nci_prop_ops *ops,
+		      size_t n_ops);
 void nci_rx_data_packet(struct nci_dev *ndev, struct sk_buff *skb);
 int nci_send_cmd(struct nci_dev *ndev, __u16 opcode, __u8 plen, void *payload);
 int nci_send_data(struct nci_dev *ndev, __u8 conn_id, struct sk_buff *skb);
diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c
index 943889b..e2bd5f4 100644
--- a/net/nfc/nci/core.c
+++ b/net/nfc/nci/core.c
@@ -1228,46 +1228,49 @@ int nci_send_cmd(struct nci_dev *ndev, __u16 opcode, __u8 plen, void *payload)
 }
 
 /* Proprietary commands API */
-static struct nci_prop_ops *prop_cmd_lookup(struct nci_dev *ndev,
-					    __u16 opcode)
+static struct nci_prop_ops *ops_cmd_lookup(struct nci_prop_ops *ops,
+					   size_t n_ops,
+					   __u16 opcode)
 {
 	size_t i;
-	struct nci_prop_ops *prop_op;
+	struct nci_prop_ops *op;
 
-	if (!ndev->ops->prop_ops || !ndev->ops->n_prop_ops)
+	if (!ops || !n_ops)
 		return NULL;
 
-	for (i = 0; i < ndev->ops->n_prop_ops; i++) {
-		prop_op = &ndev->ops->prop_ops[i];
-		if (prop_op->opcode == opcode)
-			return prop_op;
+	for (i = 0; i < n_ops; i++) {
+		op = &ops[i];
+		if (op->opcode == opcode)
+			return op;
 	}
 
 	return NULL;
 }
 
-int nci_prop_rsp_packet(struct nci_dev *ndev, __u16 rsp_opcode,
-			struct sk_buff *skb)
+int nci_op_rsp_packet(struct nci_dev *ndev, __u16 rsp_opcode,
+		      struct sk_buff *skb, struct nci_prop_ops *ops,
+		      size_t n_ops)
 {
-	struct nci_prop_ops *prop_op;
+	struct nci_prop_ops *op;
 
-	prop_op = prop_cmd_lookup(ndev, rsp_opcode);
-	if (!prop_op || !prop_op->rsp)
+	op = ops_cmd_lookup(ops, n_ops, rsp_opcode);
+	if (!op || !op->rsp)
 		return -ENOTSUPP;
 
-	return prop_op->rsp(ndev, skb);
+	return op->rsp(ndev, skb);
 }
 
-int nci_prop_ntf_packet(struct nci_dev *ndev, __u16 ntf_opcode,
-			struct sk_buff *skb)
+int nci_op_ntf_packet(struct nci_dev *ndev, __u16 ntf_opcode,
+		      struct sk_buff *skb, struct nci_prop_ops *ops,
+		      size_t n_ops)
 {
-	struct nci_prop_ops *prop_op;
+	struct nci_prop_ops *op;
 
-	prop_op = prop_cmd_lookup(ndev, ntf_opcode);
-	if (!prop_op || !prop_op->ntf)
+	op = ops_cmd_lookup(ops, n_ops, ntf_opcode);
+	if (!op || !op->ntf)
 		return -ENOTSUPP;
 
-	return prop_op->ntf(ndev, skb);
+	return op->ntf(ndev, skb);
 }
 
 /* ---- NCI TX Data worker thread ---- */
diff --git a/net/nfc/nci/ntf.c b/net/nfc/nci/ntf.c
index 5d1c2e3..8f6ed35 100644
--- a/net/nfc/nci/ntf.c
+++ b/net/nfc/nci/ntf.c
@@ -759,7 +759,9 @@ void nci_ntf_packet(struct nci_dev *ndev, struct sk_buff *skb)
 	skb_pull(skb, NCI_CTRL_HDR_SIZE);
 
 	if (nci_opcode_gid(ntf_opcode) == NCI_GID_PROPRIETARY) {
-		if (nci_prop_ntf_packet(ndev, ntf_opcode, skb)) {
+		if (nci_op_ntf_packet(ndev, ntf_opcode, skb,
+				      ndev->ops->prop_ops,
+				      ndev->ops->n_prop_ops) == -ENOTSUPP) {
 			pr_err("unsupported ntf opcode 0x%x\n",
 			       ntf_opcode);
 		}
@@ -805,6 +807,8 @@ void nci_ntf_packet(struct nci_dev *ndev, struct sk_buff *skb)
 		break;
 	}
 
+	nci_op_ntf_packet(ndev, ntf_opcode, skb, ndev->ops->core_ops,
+			  ndev->ops->n_core_ops);
 end:
 	kfree_skb(skb);
 }
diff --git a/net/nfc/nci/rsp.c b/net/nfc/nci/rsp.c
index 408bd8f..0efab0b 100644
--- a/net/nfc/nci/rsp.c
+++ b/net/nfc/nci/rsp.c
@@ -297,7 +297,9 @@ void nci_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb)
 	skb_pull(skb, NCI_CTRL_HDR_SIZE);
 
 	if (nci_opcode_gid(rsp_opcode) == NCI_GID_PROPRIETARY) {
-		if (nci_prop_rsp_packet(ndev, rsp_opcode, skb) == -ENOTSUPP) {
+		if (nci_op_rsp_packet(ndev, rsp_opcode, skb,
+				      ndev->ops->prop_ops,
+				      ndev->ops->n_prop_ops) == -ENOTSUPP) {
 			pr_err("unsupported rsp opcode 0x%x\n",
 			       rsp_opcode);
 		}
@@ -355,6 +357,8 @@ void nci_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb)
 		break;
 	}
 
+	nci_op_rsp_packet(ndev, rsp_opcode, skb, ndev->ops->core_ops,
+			  ndev->ops->n_core_ops);
 end:
 	kfree_skb(skb);
 
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v2 5/9] nfc: nci: Do not call post_setup when setup fails
  2015-09-01 14:54 [PATCH v2 0/9] Adds Intel FieldsPeak NFC solution driver Robert Dolca
                   ` (3 preceding siblings ...)
  2015-09-01 14:54 ` [PATCH v2 4/9] nfc: nci: Allow the driver to set handler for core nci ops Robert Dolca
@ 2015-09-01 14:54 ` Robert Dolca
  2015-09-01 14:54 ` [PATCH v2 6/9] nfc: nci: Introduce nci_core_cmd Robert Dolca
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Robert Dolca @ 2015-09-01 14:54 UTC (permalink / raw)
  To: linux-nfc, Lauro Ramos Venancio, Aloisio Almeida Jr, Samuel Ortiz
  Cc: linux-kernel, linux-wireless, netdev, Christophe Ricard, Robert Dolca

The driver should know that it can continue with post setup where
setup left off. Being able to execute post_setup when setup fails may
force the developer to keep this state in the driver.

Signed-off-by: Robert Dolca <robert.dolca@intel.com>
---
 net/nfc/nci/core.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c
index e2bd5f4..29607f9 100644
--- a/net/nfc/nci/core.c
+++ b/net/nfc/nci/core.c
@@ -402,9 +402,8 @@ static int nci_open_device(struct nci_dev *ndev)
 				   msecs_to_jiffies(NCI_INIT_TIMEOUT));
 	}
 
-	if (ndev->ops->post_setup) {
+	if (!rc && ndev->ops->post_setup)
 		rc = ndev->ops->post_setup(ndev);
-	}
 
 	if (!rc) {
 		rc = __nci_request(ndev, nci_init_complete_req, 0,
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v2 6/9] nfc: nci: Introduce nci_core_cmd
  2015-09-01 14:54 [PATCH v2 0/9] Adds Intel FieldsPeak NFC solution driver Robert Dolca
                   ` (4 preceding siblings ...)
  2015-09-01 14:54 ` [PATCH v2 5/9] nfc: nci: Do not call post_setup when setup fails Robert Dolca
@ 2015-09-01 14:54 ` Robert Dolca
  2015-09-01 14:54 ` [PATCH v2 7/9] nfc: nci: Use a separate mutex for nci open and close Robert Dolca
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Robert Dolca @ 2015-09-01 14:54 UTC (permalink / raw)
  To: linux-nfc, Lauro Ramos Venancio, Aloisio Almeida Jr, Samuel Ortiz
  Cc: linux-kernel, linux-wireless, netdev, Christophe Ricard, Robert Dolca

This allows sending core commands from the driver. The driver should be
able to send NCI core commands like CORE_GET_CONFIG_CMD.

Signed-off-by: Robert Dolca <robert.dolca@intel.com>
---
 include/net/nfc/nci_core.h |  1 +
 net/nfc/nci/core.c         | 24 +++++++++++++++++++-----
 2 files changed, 20 insertions(+), 5 deletions(-)

diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h
index 30c1b21..d28b7a3 100644
--- a/include/net/nfc/nci_core.h
+++ b/include/net/nfc/nci_core.h
@@ -281,6 +281,7 @@ int nci_request(struct nci_dev *ndev,
 			    unsigned long opt),
 		unsigned long opt, __u32 timeout);
 int nci_prop_cmd(struct nci_dev *ndev, __u8 oid, size_t len, __u8 *payload);
+int nci_core_cmd(struct nci_dev *ndev, __u16 opcode, size_t len, __u8 *payload);
 int nci_core_reset(struct nci_dev *ndev);
 int nci_core_init(struct nci_dev *ndev);
 
diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c
index 29607f9..6f173cd 100644
--- a/net/nfc/nci/core.c
+++ b/net/nfc/nci/core.c
@@ -325,32 +325,46 @@ static void nci_rf_deactivate_req(struct nci_dev *ndev, unsigned long opt)
 		     sizeof(struct nci_rf_deactivate_cmd), &cmd);
 }
 
-struct nci_prop_cmd_param {
+struct nci_cmd_param {
 	__u16 opcode;
 	size_t len;
 	__u8 *payload;
 };
 
-static void nci_prop_cmd_req(struct nci_dev *ndev, unsigned long opt)
+static void nci_generic_req(struct nci_dev *ndev, unsigned long opt)
 {
-	struct nci_prop_cmd_param *param = (struct nci_prop_cmd_param *)opt;
+	struct nci_cmd_param *param =
+		(struct nci_cmd_param *)opt;
 
 	nci_send_cmd(ndev, param->opcode, param->len, param->payload);
 }
 
 int nci_prop_cmd(struct nci_dev *ndev, __u8 oid, size_t len, __u8 *payload)
 {
-	struct nci_prop_cmd_param param;
+	struct nci_cmd_param param;
 
 	param.opcode = nci_opcode_pack(NCI_GID_PROPRIETARY, oid);
 	param.len = len;
 	param.payload = payload;
 
-	return __nci_request(ndev, nci_prop_cmd_req, (unsigned long)&param,
+	return __nci_request(ndev, nci_generic_req, (unsigned long)&param,
 			     msecs_to_jiffies(NCI_CMD_TIMEOUT));
 }
 EXPORT_SYMBOL(nci_prop_cmd);
 
+int nci_core_cmd(struct nci_dev *ndev, __u16 opcode, size_t len, __u8 *payload)
+{
+	struct nci_cmd_param param;
+
+	param.opcode = opcode;
+	param.len = len;
+	param.payload = payload;
+
+	return __nci_request(ndev, nci_generic_req, (unsigned long)&param,
+			     msecs_to_jiffies(NCI_CMD_TIMEOUT));
+}
+EXPORT_SYMBOL(nci_core_cmd);
+
 int nci_core_reset(struct nci_dev *ndev)
 {
 	return __nci_request(ndev, nci_reset_req, 0,
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v2 7/9] nfc: nci: Use a separate mutex for nci open and close
  2015-09-01 14:54 [PATCH v2 0/9] Adds Intel FieldsPeak NFC solution driver Robert Dolca
                   ` (5 preceding siblings ...)
  2015-09-01 14:54 ` [PATCH v2 6/9] nfc: nci: Introduce nci_core_cmd Robert Dolca
@ 2015-09-01 14:54 ` Robert Dolca
  2015-09-01 14:54 ` [PATCH v2 8/9] nfc: nci: Add a parameter to get the new connection id Robert Dolca
  2015-09-01 14:54 ` [PATCH v2 9/9] nfc: Add Intel Fields Peak NFC solution driver Robert Dolca
  8 siblings, 0 replies; 10+ messages in thread
From: Robert Dolca @ 2015-09-01 14:54 UTC (permalink / raw)
  To: linux-nfc, Lauro Ramos Venancio, Aloisio Almeida Jr, Samuel Ortiz
  Cc: linux-kernel, linux-wireless, netdev, Christophe Ricard, Robert Dolca

The functions were using req_lock for serialization. Doing so it
prevented the driver from calling nci_request during setup because it uses
the same mutex.

In the open and close functions __nci_request was replaced with nci_request
because it acquires req_lock.

Add test for NCI_INIT bit in nci_request to allow calling the function
during setup.

Signed-off-by: Robert Dolca <robert.dolca@intel.com>
---
 include/net/nfc/nci_core.h |  3 +++
 net/nfc/nci/core.c         | 30 ++++++++++++++++--------------
 2 files changed, 19 insertions(+), 14 deletions(-)

diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h
index d28b7a3..61dcaf2 100644
--- a/include/net/nfc/nci_core.h
+++ b/include/net/nfc/nci_core.h
@@ -266,6 +266,9 @@ struct nci_dev {
 	/* stored during intf_activated_ntf */
 	__u8 remote_gb[NFC_MAX_GT_LEN];
 	__u8 remote_gb_len;
+
+	/* lock used for setup and other nci_ops serialization */
+	struct mutex op_lock;
 };
 
 /* ----- NCI Devices ----- */
diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c
index 6f173cd..5d61b6e 100644
--- a/net/nfc/nci/core.c
+++ b/net/nfc/nci/core.c
@@ -136,7 +136,8 @@ inline int nci_request(struct nci_dev *ndev,
 {
 	int rc;
 
-	if (!test_bit(NCI_UP, &ndev->flags))
+	if (!test_bit(NCI_UP, &ndev->flags) &&
+	    !test_bit(NCI_INIT, &ndev->flags))
 		return -ENETDOWN;
 
 	/* Serialize all requests */
@@ -383,7 +384,7 @@ static int nci_open_device(struct nci_dev *ndev)
 {
 	int rc = 0;
 
-	mutex_lock(&ndev->req_lock);
+	mutex_lock(&ndev->op_lock);
 
 	if (test_bit(NCI_UP, &ndev->flags)) {
 		rc = -EALREADY;
@@ -403,8 +404,8 @@ static int nci_open_device(struct nci_dev *ndev)
 		rc = ndev->ops->init(ndev);
 
 	if (!rc) {
-		rc = __nci_request(ndev, nci_reset_req, 0,
-				   msecs_to_jiffies(NCI_RESET_TIMEOUT));
+		rc = nci_request(ndev, nci_reset_req, 0,
+				 msecs_to_jiffies(NCI_RESET_TIMEOUT));
 	}
 
 	if (!rc && ndev->ops->setup) {
@@ -412,16 +413,16 @@ static int nci_open_device(struct nci_dev *ndev)
 	}
 
 	if (!rc) {
-		rc = __nci_request(ndev, nci_init_req, 0,
-				   msecs_to_jiffies(NCI_INIT_TIMEOUT));
+		rc = nci_request(ndev, nci_init_req, 0,
+				 msecs_to_jiffies(NCI_INIT_TIMEOUT));
 	}
 
 	if (!rc && ndev->ops->post_setup)
 		rc = ndev->ops->post_setup(ndev);
 
 	if (!rc) {
-		rc = __nci_request(ndev, nci_init_complete_req, 0,
-				   msecs_to_jiffies(NCI_INIT_TIMEOUT));
+		rc = nci_request(ndev, nci_init_complete_req, 0,
+				 msecs_to_jiffies(NCI_INIT_TIMEOUT));
 	}
 
 	clear_bit(NCI_INIT, &ndev->flags);
@@ -441,19 +442,19 @@ static int nci_open_device(struct nci_dev *ndev)
 	}
 
 done:
-	mutex_unlock(&ndev->req_lock);
+	mutex_unlock(&ndev->op_lock);
 	return rc;
 }
 
 static int nci_close_device(struct nci_dev *ndev)
 {
 	nci_req_cancel(ndev, ENODEV);
-	mutex_lock(&ndev->req_lock);
+	mutex_lock(&ndev->op_lock);
 
 	if (!test_and_clear_bit(NCI_UP, &ndev->flags)) {
 		del_timer_sync(&ndev->cmd_timer);
 		del_timer_sync(&ndev->data_timer);
-		mutex_unlock(&ndev->req_lock);
+		mutex_unlock(&ndev->op_lock);
 		return 0;
 	}
 
@@ -470,8 +471,8 @@ static int nci_close_device(struct nci_dev *ndev)
 	atomic_set(&ndev->cmd_cnt, 1);
 
 	set_bit(NCI_INIT, &ndev->flags);
-	__nci_request(ndev, nci_reset_req, 0,
-		      msecs_to_jiffies(NCI_RESET_TIMEOUT));
+	nci_request(ndev, nci_reset_req, 0,
+		    msecs_to_jiffies(NCI_RESET_TIMEOUT));
 
 	/* After this point our queues are empty
 	 * and no works are scheduled.
@@ -488,7 +489,7 @@ static int nci_close_device(struct nci_dev *ndev)
 	/* Clear flags */
 	ndev->flags = 0;
 
-	mutex_unlock(&ndev->req_lock);
+	mutex_unlock(&ndev->op_lock);
 
 	return 0;
 }
@@ -1123,6 +1124,7 @@ int nci_register_device(struct nci_dev *ndev)
 		    (unsigned long) ndev);
 
 	mutex_init(&ndev->req_lock);
+	mutex_init(&ndev->op_lock);
 	INIT_LIST_HEAD(&ndev->conn_info_list);
 
 	rc = nfc_register_device(ndev->nfc_dev);
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v2 8/9] nfc: nci: Add a parameter to get the new connection id
  2015-09-01 14:54 [PATCH v2 0/9] Adds Intel FieldsPeak NFC solution driver Robert Dolca
                   ` (6 preceding siblings ...)
  2015-09-01 14:54 ` [PATCH v2 7/9] nfc: nci: Use a separate mutex for nci open and close Robert Dolca
@ 2015-09-01 14:54 ` Robert Dolca
  2015-09-01 14:54 ` [PATCH v2 9/9] nfc: Add Intel Fields Peak NFC solution driver Robert Dolca
  8 siblings, 0 replies; 10+ messages in thread
From: Robert Dolca @ 2015-09-01 14:54 UTC (permalink / raw)
  To: linux-nfc, Lauro Ramos Venancio, Aloisio Almeida Jr, Samuel Ortiz
  Cc: linux-kernel, linux-wireless, netdev, Christophe Ricard, Robert Dolca

nci_core_conn_create has a new parameter so it can return the id of the
new connection. Also now you can't call nci_core_conn_create
without waiting for the answer from the previous call.

Signed-off-by: Robert Dolca <robert.dolca@intel.com>
---
 drivers/nfc/st-nci/st-nci_se.c |  2 +-
 include/net/nfc/nci_core.h     | 11 ++++++++---
 net/nfc/nci/core.c             | 14 ++++++++++----
 net/nfc/nci/rsp.c              |  6 ++++++
 4 files changed, 25 insertions(+), 8 deletions(-)

diff --git a/drivers/nfc/st-nci/st-nci_se.c b/drivers/nfc/st-nci/st-nci_se.c
index c742ef6..65ba433 100644
--- a/drivers/nfc/st-nci/st-nci_se.c
+++ b/drivers/nfc/st-nci/st-nci_se.c
@@ -524,7 +524,7 @@ static int st_nci_hci_network_init(struct nci_dev *ndev)
 	r = nci_core_conn_create(ndev, NCI_DESTINATION_NFCEE, 1,
 				 sizeof(struct core_conn_create_dest_spec_params) +
 				 sizeof(struct dest_spec_params),
-				 dest_params);
+				 dest_params, NULL);
 	if (r != NCI_STATUS_OK)
 		goto free_dest_params;
 
diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h
index 61dcaf2..880b5cf 100644
--- a/include/net/nfc/nci_core.h
+++ b/include/net/nfc/nci_core.h
@@ -260,6 +260,11 @@ struct nci_dev {
 	/* Save RF Discovery ID or NFCEE ID under conn_create */
 	__u8			cur_id;
 
+	/* Pointer to the address where the next connection ID
+	 * will be stored */
+	__u8			*next_conn_id;
+	__u8			conn_busy;
+
 	/* stored during nci_data_exchange */
 	struct sk_buff		*rx_data_reassembly;
 
@@ -294,9 +299,9 @@ int nci_set_config(struct nci_dev *ndev, __u8 id, size_t len, __u8 *val);
 int nci_nfcee_discover(struct nci_dev *ndev, u8 action);
 int nci_nfcee_mode_set(struct nci_dev *ndev, u8 nfcee_id, u8 nfcee_mode);
 int nci_core_conn_create(struct nci_dev *ndev, u8 destination_type,
-			 u8 number_destination_params,
-			 size_t params_len,
-			 struct core_conn_create_dest_spec_params *params);
+			 u8 number_destination_params, size_t params_len,
+			 struct core_conn_create_dest_spec_params *params,
+			 u8 *conn_id);
 int nci_core_conn_close(struct nci_dev *ndev, u8 conn_id);
 
 struct nci_hci_dev *nci_hci_allocate(struct nci_dev *ndev);
diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c
index 5d61b6e..a9119e1 100644
--- a/net/nfc/nci/core.c
+++ b/net/nfc/nci/core.c
@@ -589,18 +589,22 @@ static void nci_core_conn_create_req(struct nci_dev *ndev, unsigned long opt)
 }
 
 int nci_core_conn_create(struct nci_dev *ndev, u8 destination_type,
-			 u8 number_destination_params,
-			 size_t params_len,
-			 struct core_conn_create_dest_spec_params *params)
+			 u8 number_destination_params, size_t params_len,
+			 struct core_conn_create_dest_spec_params *params,
+			 u8 *conn_id)
 {
 	int r;
 	struct nci_core_conn_create_cmd *cmd;
 	struct core_conn_create_data data;
 
+	mutex_lock(&ndev->req_lock);
+
 	data.length = params_len + sizeof(struct nci_core_conn_create_cmd);
 	cmd = kzalloc(data.length, GFP_KERNEL);
-	if (!cmd)
+	if (!cmd) {
+		mutex_unlock(&ndev->req_lock);
 		return -ENOMEM;
+	}
 
 	cmd->destination_type = destination_type;
 	cmd->number_destination_params = number_destination_params;
@@ -608,10 +612,12 @@ int nci_core_conn_create(struct nci_dev *ndev, u8 destination_type,
 
 	data.cmd = cmd;
 	ndev->cur_id = params->value[DEST_SPEC_PARAMS_ID_INDEX];
+	ndev->next_conn_id = conn_id;
 
 	r = __nci_request(ndev, nci_core_conn_create_req,
 			  (unsigned long)&data,
 			  msecs_to_jiffies(NCI_CMD_TIMEOUT));
+	mutex_unlock(&ndev->req_lock);
 	kfree(cmd);
 	return r;
 }
diff --git a/net/nfc/nci/rsp.c b/net/nfc/nci/rsp.c
index 0efab0b..e83e664 100644
--- a/net/nfc/nci/rsp.c
+++ b/net/nfc/nci/rsp.c
@@ -244,6 +244,12 @@ static void nci_core_conn_create_rsp_packet(struct nci_dev *ndev,
 		conn_info->id = ndev->cur_id;
 		conn_info->conn_id = rsp->conn_id;
 
+		/* Set the conn ID to the address provided by the caller */
+		if (ndev->next_conn_id) {
+			*ndev->next_conn_id = rsp->conn_id;
+			ndev->next_conn_id = NULL;
+		}
+
 		/* Note: data_exchange_cb and data_exchange_cb_context need to
 		 * be specify out of nci_core_conn_create_rsp_packet
 		 */
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v2 9/9] nfc: Add Intel Fields Peak NFC solution driver
  2015-09-01 14:54 [PATCH v2 0/9] Adds Intel FieldsPeak NFC solution driver Robert Dolca
                   ` (7 preceding siblings ...)
  2015-09-01 14:54 ` [PATCH v2 8/9] nfc: nci: Add a parameter to get the new connection id Robert Dolca
@ 2015-09-01 14:54 ` Robert Dolca
  8 siblings, 0 replies; 10+ messages in thread
From: Robert Dolca @ 2015-09-01 14:54 UTC (permalink / raw)
  To: linux-nfc, Lauro Ramos Venancio, Aloisio Almeida Jr, Samuel Ortiz
  Cc: linux-kernel, linux-wireless, netdev, Christophe Ricard, Robert Dolca

Fields Peak complies with the ISO/IEC 14443A/B, 15693, 18092,
and JIS X 6319-4. It is an NCI based controller.

RF Protocols supported:
 - NFC Forum Type 1 Tags (Jewel, Topaz)
 - NFC Forum Type 2 Tags (Mifare UL)
 - NFC Forum Type 3 Tags (FeliCa)
 - NFC Forum Type 4A (ISO/IEC 14443 A-4 106kbps to 848kbps)
 - NFC Forum Type 4B (ISO/IEC 14443 B-4 106kbps to 848kbps)
 - NFCIP in passive and active modes (ISO/IEC 18092 106kbps to 424kbps)
 - B’ (based on ISO/IEC 14443 B-2)
 - iCLASS (based on ISO/IEC 15693-2)
 - Vicinity cards (ISO/IEC 15693-3)
 - Kovio tags (NFC Forum Type 2)

The device can be enumerated using ACPI using the id INT339A.
The 1st GPIO is the IRQ and the 2nd is the RESET pin.

Signed-off-by: Robert Dolca <robert.dolca@intel.com>
---
 drivers/nfc/Kconfig      |   1 +
 drivers/nfc/Makefile     |   1 +
 drivers/nfc/fdp/Kconfig  |  23 ++
 drivers/nfc/fdp/Makefile |   9 +
 drivers/nfc/fdp/fdp.c    | 778 +++++++++++++++++++++++++++++++++++++++++++++++
 drivers/nfc/fdp/fdp.h    |  52 ++++
 drivers/nfc/fdp/i2c.c    | 416 +++++++++++++++++++++++++
 7 files changed, 1280 insertions(+)
 create mode 100644 drivers/nfc/fdp/Kconfig
 create mode 100644 drivers/nfc/fdp/Makefile
 create mode 100644 drivers/nfc/fdp/fdp.c
 create mode 100644 drivers/nfc/fdp/fdp.h
 create mode 100644 drivers/nfc/fdp/i2c.c

diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig
index 6639cd1..0d6003d 100644
--- a/drivers/nfc/Kconfig
+++ b/drivers/nfc/Kconfig
@@ -68,6 +68,7 @@ config NFC_PORT100
 
 	  If unsure, say N.
 
+source "drivers/nfc/fdp/Kconfig"
 source "drivers/nfc/pn544/Kconfig"
 source "drivers/nfc/microread/Kconfig"
 source "drivers/nfc/nfcmrvl/Kconfig"
diff --git a/drivers/nfc/Makefile b/drivers/nfc/Makefile
index 2757fe1..e362141 100644
--- a/drivers/nfc/Makefile
+++ b/drivers/nfc/Makefile
@@ -2,6 +2,7 @@
 # Makefile for nfc devices
 #
 
+obj-$(CONFIG_NFC_FDP)		+= fdp/
 obj-$(CONFIG_NFC_PN544)		+= pn544/
 obj-$(CONFIG_NFC_MICROREAD)	+= microread/
 obj-$(CONFIG_NFC_PN533)		+= pn533.o
diff --git a/drivers/nfc/fdp/Kconfig b/drivers/nfc/fdp/Kconfig
new file mode 100644
index 0000000..fbccd9d
--- /dev/null
+++ b/drivers/nfc/fdp/Kconfig
@@ -0,0 +1,23 @@
+config NFC_FDP
+	tristate "Intel FDP NFC driver"
+	depends on NFC_NCI
+	select CRC_CCITT
+	default n
+	---help---
+	  Intel Fields Peak NFC controller core driver.
+	  This is a driver based on the NCI NFC kernel layers.
+
+	  To compile this driver as a module, choose m here. The module will
+	  be called fdp.
+	  Say N if unsure.
+
+config NFC_FDP_I2C
+	tristate "NFC FDP i2c support"
+	depends on NFC_FDP && I2C
+	---help---
+	  This module adds support for the Intel Fields Peak NFC controller
+	  i2c interface.
+	  Select this if your platform is using the i2c bus.
+
+	  If you choose to build a module, it'll be called fdp_i2c.
+	  Say N if unsure.
diff --git a/drivers/nfc/fdp/Makefile b/drivers/nfc/fdp/Makefile
new file mode 100644
index 0000000..e79d51b
--- /dev/null
+++ b/drivers/nfc/fdp/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for FDP NCI based NFC driver
+#
+
+obj-$(CONFIG_NFC_FDP)     += fdp.o
+obj-$(CONFIG_NFC_FDP_I2C) += fdp_i2c.o
+
+fdp_i2c-objs  = i2c.o
+
diff --git a/drivers/nfc/fdp/fdp.c b/drivers/nfc/fdp/fdp.c
new file mode 100644
index 0000000..5c0ea70
--- /dev/null
+++ b/drivers/nfc/fdp/fdp.c
@@ -0,0 +1,778 @@
+/* -------------------------------------------------------------------------
+ * Copyright (C) 2014-2016, Intel Corporation
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ * ------------------------------------------------------------------------- */
+
+#include <linux/module.h>
+#include <linux/nfc.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <net/nfc/nci_core.h>
+
+#include "fdp.h"
+
+#define FDP_OTP_PATCH_NAME "otp.bin"
+#define FDP_RAM_PATCH_NAME "ram.bin"
+#define FDP_FW_HEADER_SIZE 576
+
+#define NCI_GET_VERSION_TIMEOUT		8000
+#define NCI_PATCH_REQUEST_TIMEOUT	8000
+#define FDP_FW_UPDATE_DEST		0xC2
+
+#define NCI_PATCH_TYPE_RAM			0x00
+#define NCI_PATCH_TYPE_OTP			0x01
+#define NCI_PATCH_TYPE_EOT			0xFF
+
+#define NCI_PARAM_ID_FW_RAM_VERSION		0xA0
+#define NCI_PARAM_ID_FW_OTP_VERSION		0xA1
+#define NCI_PARAM_ID_OTP_LIMITED_VERSION	0xC5
+#define NCI_PARAM_ID_KEY_INDEX_ID		0xC6
+
+#define NCI_GID_PROP				0x0F
+#define NCI_OP_PROP_PATCH_OID			0x08
+#define NCI_OP_PROP_SET_PDATA_OID		0x23
+
+struct fdp_nci_info {
+	struct nfc_phy_ops *phy_ops;
+	struct fdp_i2c_phy *phy;
+	struct nci_dev *ndev;
+
+	enum fdp_state state;
+
+	const struct firmware *otp_patch;
+	const struct firmware *ram_patch;
+	u32 otp_patch_version;
+	u32 ram_patch_version;
+
+	u32 otp_version;
+	u32 ram_version;
+	u32 limited_otp_version;
+	u8 key_index;
+
+	u8 conn_id;
+	atomic_t intercept;
+	atomic_t data_pkt_counter;
+	void (*data_pkt_counter_cb)(struct nci_dev *ndev);
+	u8 setup_patch_sent;
+	u8 setup_patch_ntf;
+	u8 setup_patch_status;
+	u8 setup_reset_ntf;
+	wait_queue_head_t setup_wq;
+};
+
+static u8 nci_core_get_config_otp_ram_version[5] = {
+	0x04,
+	NCI_PARAM_ID_FW_RAM_VERSION,
+	NCI_PARAM_ID_FW_OTP_VERSION,
+	NCI_PARAM_ID_OTP_LIMITED_VERSION,
+	NCI_PARAM_ID_KEY_INDEX_ID
+};
+
+struct nci_core_get_config_rsp {
+	u8 status;
+	u8 count;
+	u8 data[0];
+};
+
+static int fdp_nci_create_conn(struct nci_dev *ndev)
+{
+	struct fdp_nci_info *info = nci_get_drvdata(ndev);
+	struct core_conn_create_dest_spec_params param;
+
+	/* proprietary destination specific paramerer without value */
+	param.type = 0xA0;
+	param.length = 0x00;
+
+	return nci_core_conn_create(info->ndev, FDP_FW_UPDATE_DEST, 1,
+				    sizeof(param), &param, &info->conn_id);
+}
+
+static int fdp_nci_get_versions(struct nci_dev *ndev)
+{
+	return nci_core_cmd(ndev, NCI_OP_CORE_GET_CONFIG_CMD,
+			    sizeof(nci_core_get_config_otp_ram_version),
+			    (__u8 *) &nci_core_get_config_otp_ram_version);
+}
+
+static int fdp_nci_patch_cmd(struct nci_dev *ndev, u8 type)
+{
+	return nci_prop_cmd(ndev, NCI_OP_PROP_PATCH_OID, sizeof(type), &type);
+}
+
+static int fdp_nci_set_production_data(struct nci_dev *ndev, u8 len, char *data)
+{
+	return nci_prop_cmd(ndev, NCI_OP_PROP_SET_PDATA_OID, len, data);
+}
+
+static int fdp_nci_set_clock(struct nci_dev *ndev, u8 clock_type,
+			     u32 clock_freq)
+{
+	u32 fc = 13560;
+	u32 nd, num, delta;
+	char data[9];
+
+	nd = (24 * fc) / clock_freq;
+	delta = 24 * fc - nd * clock_freq;
+	num = (32768 * delta) / clock_freq;
+
+	data[0] = 0x00;
+	data[1] = 0x00;
+	data[2] = 0x00;
+
+	data[3] = 0x10;
+	data[4] = 0x04;
+	data[5] = num & 0xFF;
+	data[6] = (num >> 8) & 0xff;
+	data[7] = nd;
+	data[8] = clock_type;
+
+	return fdp_nci_set_production_data(ndev, 9, data);
+}
+
+static void fdp_nci_send_patch_cb(struct nci_dev *ndev)
+{
+	struct fdp_nci_info *info = nci_get_drvdata(ndev);
+
+	info->setup_patch_sent = 1;
+	wake_up(&info->setup_wq);
+}
+
+/**
+ * Register a packet sent counter and a callback
+ *
+ * We have no other way of knowing when all firmware packets were sent out
+ * on the i2c bus. We need to know that in order to close the connection and
+ * send the patch end message.
+ */
+static void fdp_nci_set_data_pkt_counter(struct nci_dev *ndev,
+				  void (*cb)(struct nci_dev *ndev), int count)
+{
+	struct fdp_nci_info *info = nci_get_drvdata(ndev);
+	struct device *dev = &info->phy->i2c_dev->dev;
+
+	dev_dbg(dev, "NCI data pkt counter %d\n", count);
+	atomic_set(&info->data_pkt_counter, count);
+	info->data_pkt_counter_cb = cb;
+}
+
+/**
+ * The device is expecting a stream of packets. All packets need to
+ * have the PBF flag set to 0x0 (last packet) even if the firmware
+ * file is segmented and there are multiple packets. If we give the
+ * whole firmware to nci_send_data it will segment it and it will set
+ * the PBF flag to 0x01 so we need to do the segmentation here.
+ *
+ * The firmware will be analyzed and applied when we send NCI_OP_PROP_PATCH_CMD
+ * command with NCI_PATCH_TYPE_EOT parameter. The device will send a
+ * NFCC_PATCH_NTF packaet and a NCI_OP_CORE_RESET_NTF packet.
+ */
+static int fdp_nci_send_patch(struct nci_dev *ndev, u8 type)
+{
+	struct fdp_nci_info *info = nci_get_drvdata(ndev);
+	const struct firmware *fw;
+	struct sk_buff *skb;
+	unsigned long len;
+	u8 max_size, payload_size;
+	int rc = 0;
+
+	if ((type == NCI_PATCH_TYPE_OTP && !info->otp_patch) ||
+	    (type == NCI_PATCH_TYPE_RAM && !info->ram_patch))
+		return -EINVAL;
+
+	if (type == NCI_PATCH_TYPE_OTP)
+		fw = info->otp_patch;
+	else
+		fw = info->ram_patch;
+
+	max_size = nci_conn_max_data_pkt_payload_size(ndev, info->conn_id);
+	if (max_size <= 0)
+		return -EINVAL;
+
+	len = fw->size;
+
+	fdp_nci_set_data_pkt_counter(ndev, fdp_nci_send_patch_cb,
+				     DIV_ROUND_UP(fw->size, max_size));
+
+	while (len) {
+
+		payload_size = min_t(unsigned long, (unsigned long) max_size,
+				     len);
+
+		skb = nci_skb_alloc(ndev, (NCI_CTRL_HDR_SIZE + payload_size),
+				    GFP_KERNEL);
+		if (!skb) {
+			fdp_nci_set_data_pkt_counter(ndev, NULL, 0);
+			return -ENOMEM;
+		}
+
+
+		skb_reserve(skb, NCI_CTRL_HDR_SIZE);
+
+		memcpy(skb_put(skb, payload_size), fw->data + (fw->size - len),
+		       payload_size);
+
+		rc = nci_send_data(ndev, info->conn_id, skb);
+
+		if (rc) {
+			fdp_nci_set_data_pkt_counter(ndev, NULL, 0);
+			return rc;
+		}
+
+		len -= payload_size;
+	}
+
+	return rc;
+}
+
+static int fdp_nci_open(struct nci_dev *ndev)
+{
+	int r;
+	struct fdp_nci_info *info = nci_get_drvdata(ndev);
+	struct device *dev = &info->phy->i2c_dev->dev;
+
+	dev_dbg(dev, "%s\n", __func__);
+
+	r = info->phy_ops->enable(info->phy);
+	if (r == 0)
+		info->state = FDP_ST_RESET;
+
+	return r;
+}
+
+static int fdp_nci_close(struct nci_dev *ndev)
+{
+	struct fdp_nci_info *info = nci_get_drvdata(ndev);
+	struct device *dev = &info->phy->i2c_dev->dev;
+
+	dev_dbg(dev, "%s\n", __func__);
+	return 0;
+}
+
+static int fdp_nci_send(struct nci_dev *ndev, struct sk_buff *skb)
+{
+	struct fdp_nci_info *info = nci_get_drvdata(ndev);
+	struct device *dev = &info->phy->i2c_dev->dev;
+
+	dev_dbg(dev, "%s\n", __func__);
+
+	if (atomic_dec_and_test(&info->data_pkt_counter))
+		info->data_pkt_counter_cb(ndev);
+
+	return info->phy_ops->write(info->phy, skb);
+}
+
+int fdp_nci_recv_frame(struct nci_dev *ndev, struct sk_buff *skb)
+{
+	struct fdp_nci_info *info = nci_get_drvdata(ndev);
+	struct device *dev = &info->phy->i2c_dev->dev;
+
+	dev_dbg(dev, "%s\n", __func__);
+	return nci_recv_frame(ndev, skb);
+}
+EXPORT_SYMBOL(fdp_nci_recv_frame);
+
+static int fdp_nci_request_firmware(struct nci_dev *ndev)
+{
+	struct fdp_nci_info *info = nci_get_drvdata(ndev);
+	struct device *dev = &info->phy->i2c_dev->dev;
+	u8 *data;
+	int r;
+
+	r = request_firmware(&info->ram_patch, FDP_RAM_PATCH_NAME, dev);
+	if (r < 0) {
+		nfc_err(dev, "RAM patch request error\n");
+		goto error;
+	}
+
+	data = (u8 *) info->ram_patch->data;
+	info->ram_patch_version =
+		data[FDP_FW_HEADER_SIZE] |
+		(data[FDP_FW_HEADER_SIZE + 1] << 8) |
+		(data[FDP_FW_HEADER_SIZE + 2] << 16) |
+		(data[FDP_FW_HEADER_SIZE + 3] << 24);
+
+	dev_dbg(dev, "RAM patch version: %d, size: %d\n",
+		  info->ram_patch_version, (int) info->ram_patch->size);
+
+
+	r = request_firmware(&info->otp_patch, FDP_OTP_PATCH_NAME, dev);
+	if (r < 0) {
+		nfc_err(dev, "OTP patch request error\n");
+		goto out;
+	}
+
+	data = (u8 *) info->otp_patch->data;
+	info->otp_patch_version =
+		data[FDP_FW_HEADER_SIZE] |
+		(data[FDP_FW_HEADER_SIZE + 1] << 8) |
+		(data[FDP_FW_HEADER_SIZE+2] << 16) |
+		(data[FDP_FW_HEADER_SIZE+3] << 24);
+
+	dev_dbg(dev, "OTP patch version: %d, size: %d\n",
+		 info->otp_patch_version, (int) info->otp_patch->size);
+out:
+	return 0;
+error:
+	return r;
+}
+
+static void fdp_nci_release_firmware(struct nci_dev *ndev)
+{
+	struct fdp_nci_info *info = nci_get_drvdata(ndev);
+
+	if (info->otp_patch) {
+		release_firmware(info->otp_patch);
+		info->otp_patch = NULL;
+	}
+
+	if (info->ram_patch) {
+		release_firmware(info->ram_patch);
+		info->otp_patch = NULL;
+	}
+}
+
+static int fdp_nci_setup(struct nci_dev *ndev)
+{
+	/* Format: total length followed by an NCI packet */
+	struct fdp_nci_info *info = nci_get_drvdata(ndev);
+	struct device *dev = &info->phy->i2c_dev->dev;
+	int r;
+	u8 patched = 0;
+
+	dev_dbg(dev, "%s\n", __func__);
+
+	r = nci_core_init(ndev);
+	if (r)
+		goto error;
+
+	/* Get RAM and OTP version */
+	r = fdp_nci_get_versions(ndev);
+	if (r)
+		goto error;
+
+	/* Load firmware from disk */
+	r = fdp_nci_request_firmware(ndev);
+	if (r)
+		goto error;
+
+	/* Update OTP */
+	if (info->otp_version < info->otp_patch_version) {
+
+		info->setup_patch_sent = 0;
+		info->setup_reset_ntf = 0;
+		info->setup_patch_ntf = 0;
+
+		/* Patch init request */
+		r = fdp_nci_patch_cmd(ndev, NCI_PATCH_TYPE_OTP);
+		if (r)
+			goto error;
+
+		/* Patch data connection creation */
+		r = fdp_nci_create_conn(ndev);
+		if (r)
+			goto error;
+
+		/* Send the patch over the data connection */
+		r = fdp_nci_send_patch(ndev, NCI_PATCH_TYPE_OTP);
+		if (r)
+			goto error;
+
+		/* Wait for all the packets to be send over i2c */
+		wait_event_interruptible(info->setup_wq,
+					 info->setup_patch_sent == 1);
+
+		/* Close the data connection */
+		r = nci_core_conn_close(info->ndev, info->conn_id);
+		if (r)
+			goto error;
+
+		/* Patch finish message */
+		r = fdp_nci_patch_cmd(ndev, NCI_PATCH_TYPE_EOT);
+		if (r) {
+			nfc_err(dev, "OTP patch error 0x%x\n", r);
+			r = -EINVAL;
+			goto error;
+		}
+
+		/* If the patch notification didn't arrive yet, wait for it */
+		wait_event_interruptible(info->setup_wq, info->setup_patch_ntf);
+
+		/* Check if the patching was successful */
+		r = info->setup_patch_status;
+		if (r) {
+			nfc_err(dev, "OTP patch error 0x%x\n", r);
+			r = -EINVAL;
+			goto error;
+		}
+
+		/*
+		 * We need to wait for the reset notification before we
+		 * can continue
+		 */
+		wait_event_interruptible(info->setup_wq, info->setup_reset_ntf);
+
+		patched = 1;
+	}
+
+	/* Update RAM */
+	if (info->ram_version < info->ram_patch_version) {
+
+		info->setup_patch_sent = 0;
+		info->setup_reset_ntf = 0;
+		info->setup_patch_ntf = 0;
+
+		/* Patch init request */
+		r = fdp_nci_patch_cmd(ndev, NCI_PATCH_TYPE_RAM);
+		if (r)
+			goto error;
+
+		/* Patch data connection creation */
+		r = fdp_nci_create_conn(ndev);
+		if (r)
+			goto error;
+
+		/* Send the patch over the data connection */
+		r = fdp_nci_send_patch(ndev, NCI_PATCH_TYPE_RAM);
+		if (r)
+			goto error;
+
+		/* Wait for all the packets to be send over i2c */
+		wait_event_interruptible(info->setup_wq,
+					 info->setup_patch_sent == 1);
+
+		/* Close the data connection */
+		r = nci_core_conn_close(info->ndev, info->conn_id);
+		if (r)
+			goto error;
+
+		/* Patch finish message */
+		if (fdp_nci_patch_cmd(ndev, NCI_PATCH_TYPE_EOT)) {
+			nfc_err(dev, "RAM patch error 0x%x\n", r);
+			r = -EINVAL;
+			goto error;
+		}
+
+		/* If the patch notification didn't arrive yet, wait for it*/
+		wait_event_interruptible(info->setup_wq, info->setup_patch_ntf);
+
+		/* Check if the patching was successful */
+		r = info->setup_patch_status;
+		if (r) {
+			nfc_err(dev, "RAM patch error 0x%x\n", r);
+			r = -EINVAL;
+			goto error;
+		}
+
+		/*
+		 * We need to wait for the reset notification before we
+		 * can continue
+		 */
+		wait_event_interruptible(info->setup_wq, info->setup_reset_ntf);
+
+		patched = 1;
+	}
+
+	/* Release the firmware buffers */
+	fdp_nci_release_firmware(ndev);
+
+	/* If a patch was applied the new version is checked */
+	if (patched) {
+		r = nci_core_init(ndev);
+		if (r)
+			goto error;
+
+		r = fdp_nci_get_versions(ndev);
+		if (r)
+			goto error;
+
+		if (info->otp_version != info->otp_patch_version ||
+		    info->ram_version != info->ram_patch_version) {
+			nfc_err(dev, "Firmware update failed");
+			r = -EINVAL;
+			goto error;
+		}
+	}
+
+	/*
+	 * We initialized the devices but the NFC subsystem expects
+	 * it to not be initialized.
+	 */
+	r = nci_core_reset(ndev);
+	if (r)
+		return r;
+
+	return 0;
+
+error:
+	fdp_nci_release_firmware(ndev);
+	nfc_err(dev, "Setup error %d\n", r);
+	return r;
+}
+
+static int fdp_nci_post_setup(struct nci_dev *ndev)
+{
+	struct fdp_nci_info *info = nci_get_drvdata(ndev);
+	struct device *dev = &info->phy->i2c_dev->dev;
+	char fdp_post_fw_vsc_cfg[] = {  0x1A, 0x2F, 0x23, 0x17,
+					0x00, 0x00, 0x00,
+					0x00, 0x04, 0x04, 0x2B, 0x20, 0xFF,
+					0x09, 0x07, 0xFF, 0xFF, 0xFF, 0xFF,
+					0x02, 0xFF, 0xFF,
+					0x08, 0x03, 0x00, 0xFF, 0xFF };
+	int r;
+
+	/* Check if the device has VSC */
+	if (fdp_post_fw_vsc_cfg[0]) {
+		/* Set the vendor specific configuration */
+		r = fdp_nci_set_production_data(ndev, fdp_post_fw_vsc_cfg[3],
+						&fdp_post_fw_vsc_cfg[4]);
+		if (r) {
+			nfc_err(dev, "Vendor specific config set error %d\n",
+				r);
+			return r;
+		}
+	}
+
+	/* Set clock type and frequency */
+	r = fdp_nci_set_clock(ndev, 0, 26000);
+	if (r) {
+		nfc_err(dev, "Clock set error %d\n", r);
+		return r;
+	}
+
+	/*
+	 * In order to apply the VSC FDP needs a reset
+	 */
+	r = nci_core_reset(ndev);
+	if (r)
+		return r;
+
+	/**
+	 * The nci core was initialized when post setup was called
+	 * so we leave it like that
+	 */
+	r = nci_core_init(ndev);
+	if (r)
+		return r;
+
+	return 0;
+}
+
+static int fdp_nci_core_reset_ntf_packet(struct nci_dev *ndev,
+					  struct sk_buff *skb)
+{
+	struct fdp_nci_info *info = nci_get_drvdata(ndev);
+	struct device *dev = &info->phy->i2c_dev->dev;
+
+	dev_dbg(dev, "%s\n", __func__);
+	info->setup_reset_ntf = 1;
+	wake_up(&info->setup_wq);
+
+	return 0;
+}
+
+static int fdp_nci_prop_patch_ntf_packet(struct nci_dev *ndev,
+					  struct sk_buff *skb)
+{
+	struct fdp_nci_info *info = nci_get_drvdata(ndev);
+	struct device *dev = &info->phy->i2c_dev->dev;
+
+	dev_dbg(dev, "%s\n", __func__);
+	info->setup_patch_ntf = 1;
+	info->setup_patch_status = skb->data[0];
+	wake_up(&info->setup_wq);
+
+	return 0;
+}
+
+static int fdp_nci_prop_patch_rsp_packet(struct nci_dev *ndev,
+					  struct sk_buff *skb)
+{
+	struct fdp_nci_info *info = nci_get_drvdata(ndev);
+	struct device *dev = &info->phy->i2c_dev->dev;
+	u8 status = skb->data[0];
+
+	dev_dbg(dev, "%s: status 0x%x\n", __func__, status);
+	nci_req_complete(ndev, status);
+
+	return 0;
+}
+
+static int fdp_nci_prop_set_production_data_rsp_packet(struct nci_dev *ndev,
+							struct sk_buff *skb)
+{
+	struct fdp_nci_info *info = nci_get_drvdata(ndev);
+	struct device *dev = &info->phy->i2c_dev->dev;
+	u8 status = skb->data[0];
+
+	dev_dbg(dev, "%s: status 0x%x\n", __func__, status);
+	nci_req_complete(ndev, status);
+
+	return 0;
+}
+
+static int fdp_nci_core_get_config_rsp_packet(struct nci_dev *ndev,
+						struct sk_buff *skb)
+{
+	struct fdp_nci_info *info = nci_get_drvdata(ndev);
+	struct device *dev = &info->phy->i2c_dev->dev;
+	struct nci_core_get_config_rsp *rsp = (void *) skb->data;
+	u8 i, *p;
+
+	if (rsp->status == NCI_STATUS_OK) {
+
+		p = rsp->data;
+		for (i = 0; i < 4; i++) {
+
+			switch (*p++) {
+			case NCI_PARAM_ID_FW_RAM_VERSION:
+				p++;
+				info->ram_version = le32_to_cpup((u32 *) p);
+				p += 4;
+				break;
+			case NCI_PARAM_ID_FW_OTP_VERSION:
+				p++;
+				info->otp_version = le32_to_cpup((u32 *) p);
+				p += 4;
+				break;
+			case NCI_PARAM_ID_OTP_LIMITED_VERSION:
+				p++;
+				info->otp_version = le32_to_cpup((u32 *) p);
+				p += 4;
+				break;
+			case NCI_PARAM_ID_KEY_INDEX_ID:
+				p++;
+				info->key_index = *p++;
+			}
+		}
+	}
+
+	dev_dbg(dev, "OTP version %d\n", info->otp_version);
+	dev_dbg(dev, "RAM version %d\n", info->ram_version);
+	dev_dbg(dev, "key index %d\n", info->key_index);
+	dev_dbg(dev, "%s: status 0x%x\n", __func__, rsp->status);
+
+	nci_req_complete(ndev, rsp->status);
+
+	return 0;
+}
+
+struct nci_prop_ops fdp_core_ops[] = {
+	{
+		.opcode = NCI_OP_CORE_GET_CONFIG_RSP,
+		.rsp = fdp_nci_core_get_config_rsp_packet,
+	},
+	{
+		.opcode = NCI_OP_CORE_RESET_NTF,
+		.ntf = fdp_nci_core_reset_ntf_packet,
+	},
+};
+
+struct nci_prop_ops fdp_prop_ops[] = {
+	{
+		.opcode = nci_opcode_pack(NCI_GID_PROP, NCI_OP_PROP_PATCH_OID),
+		.rsp = fdp_nci_prop_patch_rsp_packet,
+		.ntf = fdp_nci_prop_patch_ntf_packet,
+	},
+	{
+		.opcode = nci_opcode_pack(NCI_GID_PROP,
+					  NCI_OP_PROP_SET_PDATA_OID),
+		.rsp = fdp_nci_prop_set_production_data_rsp_packet,
+	},
+};
+
+struct nci_ops nci_ops = {
+	.open = fdp_nci_open,
+	.close = fdp_nci_close,
+	.send = fdp_nci_send,
+	.setup = fdp_nci_setup,
+	.post_setup = fdp_nci_post_setup,
+	.prop_ops = fdp_prop_ops,
+	.n_prop_ops = ARRAY_SIZE(fdp_prop_ops),
+	.core_ops = fdp_core_ops,
+	.n_core_ops = ARRAY_SIZE(fdp_core_ops),
+};
+
+int fdp_nci_probe(struct fdp_i2c_phy *phy, struct nfc_phy_ops *phy_ops,
+			struct nci_dev **ndevp, int tx_headroom,
+			int tx_tailroom)
+{
+	struct device *dev = &phy->i2c_dev->dev;
+	struct fdp_nci_info *info;
+	struct nci_dev *ndev;
+	u32 protocols;
+	int r;
+
+	info = kzalloc(sizeof(struct fdp_nci_info), GFP_KERNEL);
+	if (!info) {
+		r = -ENOMEM;
+		goto err_info_alloc;
+	}
+
+	info->phy = phy;
+	info->phy_ops = phy_ops;
+	info->state = FDP_ST_COLD;
+
+	init_waitqueue_head(&info->setup_wq);
+
+	protocols = NFC_PROTO_JEWEL_MASK |
+		    NFC_PROTO_MIFARE_MASK |
+		    NFC_PROTO_FELICA_MASK |
+		    NFC_PROTO_ISO14443_MASK |
+		    NFC_PROTO_ISO14443_B_MASK |
+		    NFC_PROTO_NFC_DEP_MASK |
+		    NFC_PROTO_ISO15693_MASK;
+
+	ndev = nci_allocate_device(&nci_ops, protocols, tx_headroom,
+				   tx_tailroom);
+	if (!ndev) {
+		nfc_err(dev, "Cannot allocate nfc ndev\n");
+		r = -ENOMEM;
+		goto err_alloc_ndev;
+	}
+
+	r = nci_register_device(ndev);
+	if (r)
+		goto err_regdev;
+
+	*ndevp = ndev;
+	info->ndev = ndev;
+
+	nci_set_drvdata(ndev, info);
+
+	return 0;
+
+err_regdev:
+	nci_free_device(ndev);
+err_alloc_ndev:
+	kfree(info);
+err_info_alloc:
+	return r;
+}
+EXPORT_SYMBOL(fdp_nci_probe);
+
+void fdp_nci_remove(struct nci_dev *ndev)
+{
+	struct fdp_nci_info *info = nci_get_drvdata(ndev);
+	struct device *dev = &info->phy->i2c_dev->dev;
+
+	dev_dbg(dev, "%s\n", __func__);
+
+	nci_unregister_device(ndev);
+	nci_free_device(ndev);
+	kfree(info);
+}
+EXPORT_SYMBOL(fdp_nci_remove);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("NFC NCI driver for Intel Fields Peak NFC controller");
+MODULE_AUTHOR("Robert Dolca <robert.dolca@intel.com>");
diff --git a/drivers/nfc/fdp/fdp.h b/drivers/nfc/fdp/fdp.h
new file mode 100644
index 0000000..84cd4f8
--- /dev/null
+++ b/drivers/nfc/fdp/fdp.h
@@ -0,0 +1,52 @@
+/* -------------------------------------------------------------------------
+ * Copyright (C) 2014-2016, Intel Corporation
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ * ------------------------------------------------------------------------- */
+
+#ifndef __LOCAL_FDP_H_
+#define __LOCAL_FDP_H_
+
+#include <net/nfc/nci_core.h>
+#include <linux/firmware.h>
+
+enum fdp_state {
+	FDP_ST_COLD,
+	FDP_ST_RESET,
+	FDP_ST_READY,
+	FDP_ST_ERROR,
+};
+
+struct fdp_i2c_phy {
+	struct i2c_client *i2c_dev;
+	struct nci_dev *ndev;
+
+	unsigned int gpio_en;
+	unsigned int gpio_irq;
+	unsigned int en_polarity;
+
+	int powered;
+
+	uint16_t next_read_size;
+
+	/*
+	 * < 0 if hardware error occurred (e.g. i2c err)
+	 * and prevents normal operation
+	 */
+	int hard_fault;
+};
+
+int fdp_nci_probe(struct fdp_i2c_phy *phy, struct nfc_phy_ops *phy_ops,
+		  struct nci_dev **ndev, int tx_headroom, int tx_tailroom);
+void fdp_nci_remove(struct nci_dev *ndev);
+int fdp_nci_recv_frame(struct nci_dev *ndev, struct sk_buff *skb);
+
+#endif /* __LOCAL_FDP_H_ */
diff --git a/drivers/nfc/fdp/i2c.c b/drivers/nfc/fdp/i2c.c
new file mode 100644
index 0000000..4b8bd41
--- /dev/null
+++ b/drivers/nfc/fdp/i2c.c
@@ -0,0 +1,416 @@
+/* -------------------------------------------------------------------------
+ * Copyright (C) 2014-2016, Intel Corporation
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ * ------------------------------------------------------------------------- */
+
+#include <linux/module.h>
+#include <linux/acpi.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/nfc.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <net/nfc/nfc.h>
+#include <net/nfc/nci_core.h>
+
+#include "fdp.h"
+
+#define FDP_GPIO_NAME_IRQ "fdp_irq"
+#define FDP_GPIO_NAME_EN  "fdp_en"
+
+#define FDP_FRAME_HEADROOM 2
+#define FDP_FRAME_TAILROOM 1
+
+#define FDP_NCI_I2C_MIN_PAYLOAD 5
+#define FDP_NCI_I2C_MAX_PAYLOAD 25
+
+#define RST_RESET 0
+#define RST_NO_RESET 1
+
+#define FDP_NCI_I2C_DRIVER_NAME "fdp_nci_i2c"
+
+#define fdp_nci_i2c_dump_skb(dev, prefix, skb)				\
+	print_hex_dump(KERN_DEBUG, prefix": ", DUMP_PREFIX_OFFSET,	\
+		       16, 1, (skb)->data, (skb)->len, 0)
+
+static void fdp_nci_i2c_reset(struct fdp_i2c_phy *phy)
+{
+	/* Reset RST/WakeUP for at least 2 micro-second */
+	gpio_set_value_cansleep(phy->gpio_en, RST_RESET);
+	usleep_range(1000, 4000);
+	gpio_set_value_cansleep(phy->gpio_en, RST_NO_RESET);
+	usleep_range(10000, 14000);
+}
+
+static int fdp_nci_i2c_enable(void *phy_id)
+{
+	struct fdp_i2c_phy *phy = phy_id;
+
+	dev_dbg(&phy->i2c_dev->dev, "%s\n", __func__);
+
+	fdp_nci_i2c_reset(phy);
+	phy->powered = 1;
+
+	return 0;
+}
+
+static void fdp_nci_i2c_disable(void *phy_id)
+{
+	struct fdp_i2c_phy *phy = phy_id;
+
+	dev_dbg(&phy->i2c_dev->dev, "%s\n", __func__);
+
+	fdp_nci_i2c_reset(phy);
+	phy->powered = 0;
+}
+
+static void fdp_nci_i2c_add_len_lrc(struct sk_buff *skb)
+{
+	u8 lrc = 0;
+	u16 len, i;
+
+	/* Add length header */
+	len = skb->len;
+	*skb_push(skb, 1) = len & 0xff;
+	*skb_push(skb, 1) = len >> 8;
+
+	/* Compute and add lrc */
+	for (i = 0; i < len + 2; i++)
+		lrc ^= skb->data[i];
+
+	*skb_put(skb, 1) = lrc;
+}
+
+static void fdp_nci_i2c_remove_len_lrc(struct sk_buff *skb)
+{
+	skb_pull(skb, FDP_FRAME_HEADROOM);
+	skb_trim(skb, skb->len - FDP_FRAME_TAILROOM);
+}
+
+static int fdp_nci_i2c_write(void *phy_id, struct sk_buff *skb)
+{
+	struct fdp_i2c_phy *phy = phy_id;
+	struct i2c_client *client = phy->i2c_dev;
+	int r;
+
+	if (phy->hard_fault != 0)
+		return phy->hard_fault;
+
+	fdp_nci_i2c_add_len_lrc(skb);
+	fdp_nci_i2c_dump_skb(&client->dev, "fdp_wr", skb);
+
+	r = i2c_master_send(client, skb->data, skb->len);
+	if (r == -EREMOTEIO) {  /* Retry, chip was in standby */
+		usleep_range(1000, 4000);
+		r = i2c_master_send(client, skb->data, skb->len);
+	}
+
+	if (r < 0 || r != skb->len)
+		dev_dbg(&client->dev, "%s: error err=%d len=%d\n",
+			__func__, r, skb->len);
+
+	if (r >= 0) {
+		if (r != skb->len) {
+			phy->hard_fault = r;
+			r = -EREMOTEIO;
+		} else {
+			r = 0;
+		}
+	}
+
+	fdp_nci_i2c_remove_len_lrc(skb);
+
+	return r;
+}
+
+static struct nfc_phy_ops i2c_phy_ops = {
+	.write = fdp_nci_i2c_write,
+	.enable = fdp_nci_i2c_enable,
+	.disable = fdp_nci_i2c_disable,
+};
+
+static int fdp_nci_i2c_acpi_request_resources(struct i2c_client *client)
+{
+	struct fdp_i2c_phy *phy = i2c_get_clientdata(client);
+	const struct acpi_device_id *id;
+	struct gpio_desc *gpiod_en, *gpiod_irq;
+	struct device *dev;
+	int ret;
+
+	dev_dbg(&client->dev, "%s\n", __func__);
+
+	if (!client)
+		return -EINVAL;
+
+	dev = &client->dev;
+
+	/* Match the struct device against a given list of ACPI IDs */
+	id = acpi_match_device(dev->driver->acpi_match_table, dev);
+	if (!id)
+		return -ENODEV;
+
+	/* Get EN GPIO from ACPI */
+	gpiod_en = devm_gpiod_get_index(dev, FDP_GPIO_NAME_EN, 1);
+	if (IS_ERR(gpiod_en)) {
+		nfc_err(dev, "Unable to get EN GPIO\n");
+		return -ENODEV;
+	}
+
+	phy->gpio_en  = desc_to_gpio(gpiod_en);
+
+	/* Configuration EN GPIO */
+	ret = gpiod_direction_output(gpiod_en, 0);
+	if (ret) {
+		nfc_err(dev, "Fail EN pin direction\n");
+		return ret;
+	}
+
+	/* Get IRQ GPIO */
+	gpiod_irq = devm_gpiod_get_index(dev, FDP_GPIO_NAME_IRQ, 0);
+	if (IS_ERR(gpiod_irq)) {
+		nfc_err(dev,
+			"Unable to get IRQ GPIO\n");
+		return -ENODEV;
+	}
+
+	phy->gpio_irq = desc_to_gpio(gpiod_irq);
+
+	/* Configure IRQ GPIO */
+	ret = gpiod_direction_input(gpiod_irq);
+	if (ret) {
+		nfc_err(dev, "Fail IRQ pin direction\n");
+		return ret;
+	}
+
+	/* Map the pin to an IRQ */
+	ret = gpiod_to_irq(gpiod_irq);
+	if (ret < 0) {
+		nfc_err(dev, "Fail pin IRQ mapping\n");
+		return ret;
+	}
+
+	dev_dbg(dev, "GPIO resource, no:%d irq:%d\n",
+		desc_to_gpio(gpiod_irq), ret);
+	client->irq = ret;
+
+	return 0;
+}
+
+static int fdp_nci_i2c_read(struct fdp_i2c_phy *phy, struct sk_buff **skb)
+{
+	int r, len;
+	u8 tmp[FDP_NCI_I2C_MAX_PAYLOAD], lrc, k;
+	u16 i;
+	struct i2c_client *client = phy->i2c_dev;
+
+	*skb = NULL;
+
+	/* Read the length packet and the data packet */
+	for (k = 0; k < 2; k++) {
+
+		len = phy->next_read_size;
+
+		r = i2c_master_recv(client, tmp, len);
+		if (r != len) {
+			dev_dbg(&client->dev, "%s: i2c recv err: %d\n",
+				__func__, r);
+			goto flush;
+		}
+
+		/* Check packet integruty */
+		for (lrc = i = 0; i < r; i++)
+			lrc ^= tmp[i];
+
+		/*
+		 * LRC check failed. This may due to transmission error or
+		 * desynchronization between driver and FDP. Drop the paquet
+		 * and force resynchronization
+		 */
+		if (lrc) {
+			dev_dbg(&client->dev, "%s: corrupted packet\n",
+				__func__);
+			phy->next_read_size = 5;
+			goto flush;
+		}
+
+		/* Packet that contains a length */
+		if (tmp[0] == 0 && tmp[1] == 0) {
+
+			phy->next_read_size = (tmp[2] << 8) + tmp[3] + 3;
+
+		} else {
+
+			phy->next_read_size = FDP_NCI_I2C_MIN_PAYLOAD;
+
+			*skb = alloc_skb(len, GFP_KERNEL);
+			if (*skb == NULL) {
+				r = -ENOMEM;
+				goto flush;
+			}
+
+			memcpy(skb_put(*skb, len), tmp, len);
+			fdp_nci_i2c_dump_skb(&client->dev, "fdp_rd", *skb);
+
+			fdp_nci_i2c_remove_len_lrc(*skb);
+		}
+	}
+
+	return 0;
+
+flush:
+	/* Flush the remaining data */
+	if (i2c_master_recv(client, tmp, sizeof(tmp)) < 0)
+		r = -EREMOTEIO;
+
+	return r;
+}
+
+static irqreturn_t fdp_nci_i2c_irq_thread_fn(int irq, void *phy_id)
+{
+	struct fdp_i2c_phy *phy = phy_id;
+	struct i2c_client *client;
+	struct sk_buff *skb;
+	int r;
+
+	client = phy->i2c_dev;
+	dev_dbg(&client->dev, "%s\n", __func__);
+
+	if (!phy || irq != phy->i2c_dev->irq) {
+		WARN_ON_ONCE(1);
+		return IRQ_NONE;
+	}
+
+	r = fdp_nci_i2c_read(phy, &skb);
+
+	if (r == -EREMOTEIO)
+		return IRQ_HANDLED;
+	else if (r == -ENOMEM || r == -EBADMSG)
+		return IRQ_HANDLED;
+
+	if (skb != NULL)
+		fdp_nci_recv_frame(phy->ndev, skb);
+
+	return IRQ_HANDLED;
+}
+
+
+static int fdp_nci_i2c_probe(struct i2c_client *client,
+			       const struct i2c_device_id *id)
+{
+	struct fdp_i2c_phy *phy;
+	int r = 0;
+
+	dev_dbg(&client->dev, "%s\n", __func__);
+	dev_dbg(&client->dev, "IRQ: %d\n", client->irq);
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		nfc_err(&client->dev, "No I2C_FUNC_I2C\n");
+		return -ENODEV;
+	}
+
+	phy = devm_kzalloc(&client->dev, sizeof(struct fdp_i2c_phy),
+			   GFP_KERNEL);
+	if (!phy)
+		return -ENOMEM;
+
+	phy->i2c_dev = client;
+	phy->next_read_size = FDP_NCI_I2C_MIN_PAYLOAD;
+	i2c_set_clientdata(client, phy);
+
+	/* Using ACPI */
+	if (ACPI_HANDLE(&client->dev)) {
+
+		r = fdp_nci_i2c_acpi_request_resources(client);
+		if (r) {
+			nfc_err(&client->dev,
+				"Cannot get ACPI data\n");
+			return r;
+		}
+
+	} else {
+		nfc_err(&client->dev, "No platform data\n");
+		return -EINVAL;
+	}
+
+	r = request_threaded_irq(client->irq, NULL, fdp_nci_i2c_irq_thread_fn,
+				 IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+				 FDP_NCI_I2C_DRIVER_NAME, phy);
+
+	if (r < 0) {
+		nfc_err(&client->dev, "Unable to register IRQ handler\n");
+		goto err_rti;
+	}
+
+	r = fdp_nci_probe(phy, &i2c_phy_ops, &phy->ndev,
+				FDP_FRAME_HEADROOM, FDP_FRAME_TAILROOM);
+
+	if (r < 0) {
+		nfc_err(&client->dev, "NCI probing error\n");
+		goto err_nci;
+	}
+
+	dev_dbg(&client->dev, "I2C driver loaded\n");
+	return 0;
+
+err_nci:
+	free_irq(client->irq, phy);
+
+err_rti:
+	gpio_free(phy->gpio_en);
+	return r;
+}
+
+static int fdp_nci_i2c_remove(struct i2c_client *client)
+{
+	struct fdp_i2c_phy *phy = i2c_get_clientdata(client);
+
+	dev_dbg(&client->dev, "%s\n", __func__);
+
+	fdp_nci_remove(phy->ndev);
+
+	if (phy->powered)
+		fdp_nci_i2c_disable(phy);
+
+	free_irq(client->irq, phy);
+	gpio_free(phy->gpio_en);
+
+	return 0;
+}
+
+static struct i2c_device_id fdp_nci_i2c_id_table[] = {
+	{"INT339A", 0},
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, fdp_nci_i2c_id_table);
+
+
+static const struct acpi_device_id fdp_nci_i2c_acpi_match[] = {
+	{"INT339A", 0},
+	{}
+};
+MODULE_DEVICE_TABLE(acpi, fdp_nci_i2c_acpi_match);
+
+static struct i2c_driver fdp_nci_i2c_driver = {
+	.driver = {
+		   .name = FDP_NCI_I2C_DRIVER_NAME,
+		   .acpi_match_table = ACPI_PTR(fdp_nci_i2c_acpi_match),
+		  },
+	.id_table = fdp_nci_i2c_id_table,
+	.probe = fdp_nci_i2c_probe,
+	.remove = fdp_nci_i2c_remove,
+};
+module_i2c_driver(fdp_nci_i2c_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("I2C driver for Intel Fields Peak NFC controller");
+MODULE_AUTHOR("Robert Dolca <robert.dolca@intel.com>");
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2015-09-01 14:59 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-09-01 14:54 [PATCH v2 0/9] Adds Intel FieldsPeak NFC solution driver Robert Dolca
2015-09-01 14:54 ` [PATCH v2 1/9] nfc: nci: Export nci data send API Robert Dolca
2015-09-01 14:54 ` [PATCH v2 2/9] nfc: nci: Add function to get max packet size for conn Robert Dolca
2015-09-01 14:54 ` [PATCH v2 3/9] nfc: nci: Introduce new core opcodes Robert Dolca
2015-09-01 14:54 ` [PATCH v2 4/9] nfc: nci: Allow the driver to set handler for core nci ops Robert Dolca
2015-09-01 14:54 ` [PATCH v2 5/9] nfc: nci: Do not call post_setup when setup fails Robert Dolca
2015-09-01 14:54 ` [PATCH v2 6/9] nfc: nci: Introduce nci_core_cmd Robert Dolca
2015-09-01 14:54 ` [PATCH v2 7/9] nfc: nci: Use a separate mutex for nci open and close Robert Dolca
2015-09-01 14:54 ` [PATCH v2 8/9] nfc: nci: Add a parameter to get the new connection id Robert Dolca
2015-09-01 14:54 ` [PATCH v2 9/9] nfc: Add Intel Fields Peak NFC solution driver Robert Dolca

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).