summaryrefslogtreecommitdiff
path: root/drivers/nfc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/nfc')
-rw-r--r--drivers/nfc/Kconfig80
-rw-r--r--drivers/nfc/Makefile19
-rw-r--r--drivers/nfc/fdp/Kconfig27
-rw-r--r--drivers/nfc/fdp/Makefile9
-rw-r--r--drivers/nfc/fdp/fdp.c818
-rw-r--r--drivers/nfc/fdp/fdp.h38
-rw-r--r--drivers/nfc/fdp/i2c.c388
-rw-r--r--drivers/nfc/mei_phy.c417
-rw-r--r--drivers/nfc/mei_phy.h52
-rw-r--r--drivers/nfc/microread/Kconfig34
-rw-r--r--drivers/nfc/microread/Makefile10
-rw-r--r--drivers/nfc/microread/i2c.c315
-rw-r--r--drivers/nfc/microread/mei.c94
-rw-r--r--drivers/nfc/microread/microread.c734
-rw-r--r--drivers/nfc/microread/microread.h31
-rw-r--r--drivers/nfc/nfcmrvl/Kconfig62
-rw-r--r--drivers/nfc/nfcmrvl/Makefile18
-rw-r--r--drivers/nfc/nfcmrvl/fw_dnld.c559
-rw-r--r--drivers/nfc/nfcmrvl/fw_dnld.h98
-rw-r--r--drivers/nfc/nfcmrvl/i2c.c290
-rw-r--r--drivers/nfc/nfcmrvl/main.c283
-rw-r--r--drivers/nfc/nfcmrvl/nfcmrvl.h117
-rw-r--r--drivers/nfc/nfcmrvl/spi.c228
-rw-r--r--drivers/nfc/nfcmrvl/uart.c233
-rw-r--r--drivers/nfc/nfcmrvl/usb.c472
-rw-r--r--drivers/nfc/nfcsim.c512
-rw-r--r--drivers/nfc/nfcwilink.c578
-rw-r--r--drivers/nfc/nxp-nci/Kconfig28
-rw-r--r--drivers/nfc/nxp-nci/Makefile9
-rw-r--r--drivers/nfc/nxp-nci/core.c187
-rw-r--r--drivers/nfc/nxp-nci/firmware.c325
-rw-r--r--drivers/nfc/nxp-nci/i2c.c434
-rw-r--r--drivers/nfc/nxp-nci/nxp-nci.h90
-rw-r--r--drivers/nfc/pn533/Kconfig30
-rw-r--r--drivers/nfc/pn533/Makefile9
-rw-r--r--drivers/nfc/pn533/i2c.c281
-rw-r--r--drivers/nfc/pn533/pn533.c2685
-rw-r--r--drivers/nfc/pn533/pn533.h238
-rw-r--r--drivers/nfc/pn533/usb.c594
-rw-r--r--drivers/nfc/pn544/Kconfig35
-rw-r--r--drivers/nfc/pn544/Makefile10
-rw-r--r--drivers/nfc/pn544/i2c.c1120
-rw-r--r--drivers/nfc/pn544/mei.c94
-rw-r--r--drivers/nfc/pn544/pn544.c995
-rw-r--r--drivers/nfc/pn544/pn544.h36
-rw-r--r--drivers/nfc/port100.c1649
-rw-r--r--drivers/nfc/s3fwrn5/Kconfig22
-rw-r--r--drivers/nfc/s3fwrn5/Makefile9
-rw-r--r--drivers/nfc/s3fwrn5/core.c219
-rw-r--r--drivers/nfc/s3fwrn5/firmware.c529
-rw-r--r--drivers/nfc/s3fwrn5/firmware.h111
-rw-r--r--drivers/nfc/s3fwrn5/i2c.c306
-rw-r--r--drivers/nfc/s3fwrn5/nci.c165
-rw-r--r--drivers/nfc/s3fwrn5/nci.h89
-rw-r--r--drivers/nfc/s3fwrn5/s3fwrn5.h99
-rw-r--r--drivers/nfc/st-nci/Kconfig35
-rw-r--r--drivers/nfc/st-nci/Makefile12
-rw-r--r--drivers/nfc/st-nci/core.c187
-rw-r--r--drivers/nfc/st-nci/i2c.c428
-rw-r--r--drivers/nfc/st-nci/ndlc.c316
-rw-r--r--drivers/nfc/st-nci/ndlc.h63
-rw-r--r--drivers/nfc/st-nci/se.c763
-rw-r--r--drivers/nfc/st-nci/spi.c442
-rw-r--r--drivers/nfc/st-nci/st-nci.h153
-rw-r--r--drivers/nfc/st-nci/vendor_cmds.c478
-rw-r--r--drivers/nfc/st21nfca/Kconfig21
-rw-r--r--drivers/nfc/st21nfca/Makefile9
-rw-r--r--drivers/nfc/st21nfca/core.c1049
-rw-r--r--drivers/nfc/st21nfca/dep.c689
-rw-r--r--drivers/nfc/st21nfca/i2c.c735
-rw-r--r--drivers/nfc/st21nfca/se.c427
-rw-r--r--drivers/nfc/st21nfca/st21nfca.h211
-rw-r--r--drivers/nfc/st21nfca/vendor_cmds.c375
-rw-r--r--drivers/nfc/st95hf/Kconfig12
-rw-r--r--drivers/nfc/st95hf/Makefile6
-rw-r--r--drivers/nfc/st95hf/core.c1273
-rw-r--r--drivers/nfc/st95hf/spi.c167
-rw-r--r--drivers/nfc/st95hf/spi.h64
-rw-r--r--drivers/nfc/trf7970a.c2245
79 files changed, 27074 insertions, 0 deletions
diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig
new file mode 100644
index 0000000..9a76549
--- /dev/null
+++ b/drivers/nfc/Kconfig
@@ -0,0 +1,80 @@
+#
+# Near Field Communication (NFC) devices
+#
+
+menu "Near Field Communication (NFC) devices"
+ depends on NFC
+
+config NFC_WILINK
+ depends on !KERNEL_4_8
+ tristate "Texas Instruments NFC WiLink driver"
+ depends on m
+ depends on TI_ST && NFC_NCI
+ help
+ This enables the NFC driver for Texas Instrument's BT/FM/GPS/NFC
+ combo devices. This makes use of shared transport line discipline
+ core driver to communicate with the NFC core of the combo chip.
+
+ Say Y here to compile support for Texas Instrument's NFC WiLink driver
+ into the kernel or say M to compile it as module.
+
+config NFC_TRF7970A
+ depends on !KERNEL_3_4
+ tristate "Texas Instruments TRF7970a NFC driver"
+ depends on m
+ depends on SPI && NFC_DIGITAL
+ help
+ This option enables the NFC driver for Texas Instruments' TRF7970a
+ device. Such device supports 5 different protocols: ISO14443A,
+ ISO14443B, FeLiCa, ISO15693 and ISO18000-3.
+
+ Say Y here to compile support for TRF7970a into the kernel or
+ say M to compile it as a module. The module will be called
+ trf7970a.ko.
+
+config NFC_MEI_PHY
+ depends on !KERNEL_4_10
+ tristate "MEI bus NFC device support"
+ depends on m
+ depends on INTEL_MEI && NFC_HCI
+ help
+ This adds support to use an mei bus nfc device. Select this if you
+ will use an HCI NFC driver for an NFC chip connected behind an
+ Intel's Management Engine chip.
+
+ If unsure, say N.
+
+config NFC_SIM
+ tristate "NFC hardware simulator driver"
+ depends on m
+ depends on NFC_DIGITAL
+ help
+ This driver declares two virtual NFC devices supporting NFC-DEP
+ protocol. An LLCP connection can be established between them and
+ all packets sent from one device is sent back to the other, acting as
+ loopback devices.
+
+ If unsure, say N.
+
+config NFC_PORT100
+ tristate "Sony NFC Port-100 Series USB device support"
+ depends on m
+ depends on USB
+ depends on NFC_DIGITAL
+ help
+ This adds support for Sony Port-100 chip based USB devices such as the
+ RC-S380 dongle.
+
+ If unsure, say N.
+
+source "drivers/nfc/fdp/Kconfig"
+source "drivers/nfc/pn544/Kconfig"
+source "drivers/nfc/pn533/Kconfig"
+source "drivers/nfc/microread/Kconfig"
+source "drivers/nfc/nfcmrvl/Kconfig"
+source "drivers/nfc/st21nfca/Kconfig"
+source "drivers/nfc/st-nci/Kconfig"
+source "drivers/nfc/nxp-nci/Kconfig"
+source "drivers/nfc/s3fwrn5/Kconfig"
+source "drivers/nfc/st95hf/Kconfig"
+endmenu
diff --git a/drivers/nfc/Makefile b/drivers/nfc/Makefile
new file mode 100644
index 0000000..2cb5df6
--- /dev/null
+++ b/drivers/nfc/Makefile
@@ -0,0 +1,19 @@
+#
+# Makefile for nfc devices
+#
+
+obj-$(CPTCFG_NFC_FDP) += fdp/
+obj-$(CPTCFG_NFC_PN544) += pn544/
+obj-$(CPTCFG_NFC_MICROREAD) += microread/
+obj-$(CPTCFG_NFC_PN533) += pn533/
+obj-$(CPTCFG_NFC_WILINK) += nfcwilink.o
+obj-$(CPTCFG_NFC_MEI_PHY) += mei_phy.o
+obj-$(CPTCFG_NFC_SIM) += nfcsim.o
+obj-$(CPTCFG_NFC_PORT100) += port100.o
+obj-$(CPTCFG_NFC_MRVL) += nfcmrvl/
+obj-$(CPTCFG_NFC_TRF7970A) += trf7970a.o
+obj-$(CPTCFG_NFC_ST21NFCA) += st21nfca/
+obj-$(CPTCFG_NFC_ST_NCI) += st-nci/
+obj-$(CPTCFG_NFC_NXP_NCI) += nxp-nci/
+obj-$(CPTCFG_NFC_S3FWRN5) += s3fwrn5/
+obj-$(CPTCFG_NFC_ST95HF) += st95hf/
diff --git a/drivers/nfc/fdp/Kconfig b/drivers/nfc/fdp/Kconfig
new file mode 100644
index 0000000..8ccc834
--- /dev/null
+++ b/drivers/nfc/fdp/Kconfig
@@ -0,0 +1,27 @@
+config NFC_FDP
+ depends on !KERNEL_3_13
+ tristate "Intel FDP NFC driver"
+ depends on m
+ depends on NFC_NCI
+ depends on 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
+ depends on !KERNEL_3_18
+ tristate "NFC FDP i2c support"
+ depends on m
+ 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..295ab43
--- /dev/null
+++ b/drivers/nfc/fdp/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for FDP NCI based NFC driver
+#
+
+obj-$(CPTCFG_NFC_FDP) += fdp.o
+obj-$(CPTCFG_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..7c1eaea
--- /dev/null
+++ b/drivers/nfc/fdp/fdp.c
@@ -0,0 +1,818 @@
+/* -------------------------------------------------------------------------
+ * 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 <linux/firmware.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 FDP_FW_UPDATE_SLEEP 1000
+
+#define NCI_GET_VERSION_TIMEOUT 8000
+#define NCI_PATCH_REQUEST_TIMEOUT 8000
+#define FDP_PATCH_CONN_DEST 0xC2
+#define FDP_PATCH_CONN_PARAM_TYPE 0xA0
+
+#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;
+
+ 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 *fw_vsc_cfg;
+ u8 clock_type;
+ u32 clock_freq;
+
+ 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;
+ int r;
+
+ /* proprietary destination specific paramerer without value */
+ param.type = FDP_PATCH_CONN_PARAM_TYPE;
+ param.length = 0x00;
+
+ r = nci_core_conn_create(info->ndev, FDP_PATCH_CONN_DEST, 1,
+ sizeof(param), &param);
+ if (r)
+ return r;
+
+ return nci_get_conn_info_by_dest_type_params(ndev,
+ FDP_PATCH_CONN_DEST, NULL);
+}
+
+static inline 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 inline 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 inline 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 conn_id, 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, 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, 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);
+
+ 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->ram_patch = NULL;
+ }
+}
+
+static int fdp_nci_patch_otp(struct nci_dev *ndev)
+{
+ struct fdp_nci_info *info = nci_get_drvdata(ndev);
+ struct device *dev = &info->phy->i2c_dev->dev;
+ int conn_id;
+ int r = 0;
+
+ if (info->otp_version >= info->otp_patch_version)
+ goto out;
+
+ 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 out;
+
+ /* Patch data connection creation */
+ conn_id = fdp_nci_create_conn(ndev);
+ if (conn_id < 0) {
+ r = conn_id;
+ goto out;
+ }
+
+ /* Send the patch over the data connection */
+ r = fdp_nci_send_patch(ndev, conn_id, NCI_PATCH_TYPE_OTP);
+ if (r)
+ goto out;
+
+ /* Wait for all the packets to be send over i2c */
+ wait_event_interruptible(info->setup_wq,
+ info->setup_patch_sent == 1);
+
+ /* make sure that the NFCC processed the last data packet */
+ msleep(FDP_FW_UPDATE_SLEEP);
+
+ /* Close the data connection */
+ r = nci_core_conn_close(info->ndev, conn_id);
+ if (r)
+ goto out;
+
+ /* Patch finish message */
+ if (fdp_nci_patch_cmd(ndev, NCI_PATCH_TYPE_EOT)) {
+ nfc_err(dev, "OTP patch error 0x%x\n", r);
+ r = -EINVAL;
+ goto out;
+ }
+
+ /* 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 out;
+ }
+
+ /*
+ * We need to wait for the reset notification before we
+ * can continue
+ */
+ wait_event_interruptible(info->setup_wq, info->setup_reset_ntf);
+
+out:
+ return r;
+}
+
+static int fdp_nci_patch_ram(struct nci_dev *ndev)
+{
+ struct fdp_nci_info *info = nci_get_drvdata(ndev);
+ struct device *dev = &info->phy->i2c_dev->dev;
+ int conn_id;
+ int r = 0;
+
+ if (info->ram_version >= info->ram_patch_version)
+ goto out;
+
+ 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 out;
+
+ /* Patch data connection creation */
+ conn_id = fdp_nci_create_conn(ndev);
+ if (conn_id < 0) {
+ r = conn_id;
+ goto out;
+ }
+
+ /* Send the patch over the data connection */
+ r = fdp_nci_send_patch(ndev, conn_id, NCI_PATCH_TYPE_RAM);
+ if (r)
+ goto out;
+
+ /* Wait for all the packets to be send over i2c */
+ wait_event_interruptible(info->setup_wq,
+ info->setup_patch_sent == 1);
+
+ /* make sure that the NFCC processed the last data packet */
+ msleep(FDP_FW_UPDATE_SLEEP);
+
+ /* Close the data connection */
+ r = nci_core_conn_close(info->ndev, conn_id);
+ if (r)
+ goto out;
+
+ /* 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 out;
+ }
+
+ /* 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 out;
+ }
+
+ /*
+ * We need to wait for the reset notification before we
+ * can continue
+ */
+ wait_event_interruptible(info->setup_wq, info->setup_reset_ntf);
+
+out:
+ return r;
+}
+
+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) {
+ r = fdp_nci_patch_otp(ndev);
+ if (r)
+ goto error;
+ patched = 1;
+ }
+
+ /* Update RAM */
+ if (info->ram_version < info->ram_patch_version) {
+ r = fdp_nci_patch_ram(ndev);
+ if (r)
+ goto error;
+ 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.
+ */
+ return nci_core_reset(ndev);
+
+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;
+ int r;
+
+ /* Check if the device has VSC */
+ if (info->fw_vsc_cfg && info->fw_vsc_cfg[0]) {
+
+ /* Set the vendor specific configuration */
+ r = fdp_nci_set_production_data(ndev, info->fw_vsc_cfg[3],
+ &info->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, info->clock_type, info->clock_freq);
+ 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
+ */
+ return nci_core_init(ndev);
+}
+
+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((__le32 *) p);
+ p += 4;
+ break;
+ case NCI_PARAM_ID_FW_OTP_VERSION:
+ p++;
+ info->otp_version = le32_to_cpup((__le32 *) p);
+ p += 4;
+ break;
+ case NCI_PARAM_ID_OTP_LIMITED_VERSION:
+ p++;
+ info->otp_version = le32_to_cpup((__le32 *) 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;
+}
+
+static struct nci_driver_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,
+ },
+};
+
+static struct nci_driver_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, u8 clock_type, u32 clock_freq,
+ u8 *fw_vsc_cfg)
+{
+ 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->clock_type = clock_type;
+ info->clock_freq = clock_freq;
+ info->fw_vsc_cfg = fw_vsc_cfg;
+
+ 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..0bd36c0
--- /dev/null
+++ b/drivers/nfc/fdp/fdp.h
@@ -0,0 +1,38 @@
+/* -------------------------------------------------------------------------
+ * 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/gpio/consumer.h>
+
+struct fdp_i2c_phy {
+ struct i2c_client *i2c_dev;
+ struct gpio_desc *power_gpio;
+ struct nci_dev *ndev;
+
+ /* < 0 if i2c error occurred */
+ int hard_fault;
+ uint16_t next_read_size;
+};
+
+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,
+ u8 clock_type, u32 clock_freq, u8 *fw_vsc_cfg);
+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..5e797d5
--- /dev/null
+++ b/drivers/nfc/fdp/i2c.c
@@ -0,0 +1,388 @@
+/* -------------------------------------------------------------------------
+ * 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/delay.h>
+#include <linux/gpio/consumer.h>
+#include <net/nfc/nfc.h>
+#include <net/nfc/nci_core.h>
+
+#include "fdp.h"
+
+#define FDP_I2C_DRIVER_NAME "fdp_nci_i2c"
+
+#define FDP_DP_POWER_GPIO_NAME "power"
+#define FDP_DP_CLOCK_TYPE_NAME "clock-type"
+#define FDP_DP_CLOCK_FREQ_NAME "clock-freq"
+#define FDP_DP_FW_VSC_CFG_NAME "fw-vsc-cfg"
+
+#define FDP_FRAME_HEADROOM 2
+#define FDP_FRAME_TAILROOM 1
+
+#define FDP_NCI_I2C_MIN_PAYLOAD 5
+#define FDP_NCI_I2C_MAX_PAYLOAD 261
+
+#define FDP_POWER_OFF 0
+#define FDP_POWER_ON 1
+
+#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 100 micro-second */
+ gpiod_set_value_cansleep(phy->power_gpio, FDP_POWER_OFF);
+ usleep_range(1000, 4000);
+ gpiod_set_value_cansleep(phy->power_gpio, FDP_POWER_ON);
+ 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);
+
+ 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);
+}
+
+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_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 void fdp_nci_i2c_read_device_properties(struct device *dev,
+ u8 *clock_type, u32 *clock_freq,
+ u8 **fw_vsc_cfg)
+{
+ int r;
+ u8 len;
+
+ r = device_property_read_u8(dev, FDP_DP_CLOCK_TYPE_NAME, clock_type);
+ if (r) {
+ dev_dbg(dev, "Using default clock type");
+ *clock_type = 0;
+ }
+
+ r = device_property_read_u32(dev, FDP_DP_CLOCK_FREQ_NAME, clock_freq);
+ if (r) {
+ dev_dbg(dev, "Using default clock frequency\n");
+ *clock_freq = 26000;
+ }
+
+ if (device_property_present(dev, FDP_DP_FW_VSC_CFG_NAME)) {
+ r = device_property_read_u8(dev, FDP_DP_FW_VSC_CFG_NAME,
+ &len);
+
+ if (r || len <= 0)
+ goto vsc_read_err;
+
+ /* Add 1 to the length to inclue the length byte itself */
+ len++;
+
+ *fw_vsc_cfg = devm_kmalloc(dev,
+ len * sizeof(**fw_vsc_cfg),
+ GFP_KERNEL);
+
+ r = device_property_read_u8_array(dev, FDP_DP_FW_VSC_CFG_NAME,
+ *fw_vsc_cfg, len);
+
+ if (r) {
+ devm_kfree(dev, fw_vsc_cfg);
+ goto vsc_read_err;
+ }
+ } else {
+vsc_read_err:
+ dev_dbg(dev, "FW vendor specific commands not present\n");
+ *fw_vsc_cfg = NULL;
+ }
+
+ dev_dbg(dev, "Clock type: %d, clock frequency: %d, VSC: %s",
+ *clock_type, *clock_freq, *fw_vsc_cfg != NULL ? "yes" : "no");
+}
+
+static int fdp_nci_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct fdp_i2c_phy *phy;
+ struct device *dev = &client->dev;
+ u8 *fw_vsc_cfg;
+ u8 clock_type;
+ u32 clock_freq;
+ int r = 0;
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ nfc_err(dev, "No I2C_FUNC_I2C support\n");
+ return -ENODEV;
+ }
+
+ /* Checking if we have an irq */
+ if (client->irq <= 0) {
+ nfc_err(dev, "IRQ not present\n");
+ return -ENODEV;
+ }
+
+ phy = devm_kzalloc(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);
+
+ r = request_threaded_irq(client->irq, NULL, fdp_nci_i2c_irq_thread_fn,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ FDP_I2C_DRIVER_NAME, phy);
+
+ if (r < 0) {
+ nfc_err(&client->dev, "Unable to register IRQ handler\n");
+ return r;
+ }
+
+ /* Requesting the power gpio */
+ phy->power_gpio = devm_gpiod_get(dev, FDP_DP_POWER_GPIO_NAME,
+ GPIOD_OUT_LOW);
+
+ if (IS_ERR(phy->power_gpio)) {
+ nfc_err(dev, "Power GPIO request failed\n");
+ return PTR_ERR(phy->power_gpio);
+ }
+
+ /* read device properties to get the clock and production settings */
+ fdp_nci_i2c_read_device_properties(dev, &clock_type, &clock_freq,
+ &fw_vsc_cfg);
+
+ /* Call the NFC specific probe function */
+ r = fdp_nci_probe(phy, &i2c_phy_ops, &phy->ndev,
+ FDP_FRAME_HEADROOM, FDP_FRAME_TAILROOM,
+ clock_type, clock_freq, fw_vsc_cfg);
+ if (r < 0) {
+ nfc_err(dev, "NCI probing error\n");
+ return r;
+ }
+
+ dev_dbg(dev, "I2C driver loaded\n");
+ return 0;
+}
+
+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);
+ fdp_nci_i2c_disable(phy);
+
+ 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_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>");
diff --git a/drivers/nfc/mei_phy.c b/drivers/nfc/mei_phy.c
new file mode 100644
index 0000000..49bdc2f
--- /dev/null
+++ b/drivers/nfc/mei_phy.c
@@ -0,0 +1,417 @@
+/*
+ * MEI Library for mei bus nfc device access
+ *
+ * Copyright (C) 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/nfc.h>
+
+#include "mei_phy.h"
+
+struct mei_nfc_hdr {
+ u8 cmd;
+ u8 status;
+ u16 req_id;
+ u32 reserved;
+ u16 data_size;
+} __packed;
+
+struct mei_nfc_cmd {
+ struct mei_nfc_hdr hdr;
+ u8 sub_command;
+ u8 data[];
+} __packed;
+
+struct mei_nfc_reply {
+ struct mei_nfc_hdr hdr;
+ u8 sub_command;
+ u8 reply_status;
+ u8 data[];
+} __packed;
+
+struct mei_nfc_if_version {
+ u8 radio_version_sw[3];
+ u8 reserved[3];
+ u8 radio_version_hw[3];
+ u8 i2c_addr;
+ u8 fw_ivn;
+ u8 vendor_id;
+ u8 radio_type;
+} __packed;
+
+struct mei_nfc_connect {
+ u8 fw_ivn;
+ u8 vendor_id;
+} __packed;
+
+struct mei_nfc_connect_resp {
+ u8 fw_ivn;
+ u8 vendor_id;
+ u16 me_major;
+ u16 me_minor;
+ u16 me_hotfix;
+ u16 me_build;
+} __packed;
+
+
+#define MEI_NFC_CMD_MAINTENANCE 0x00
+#define MEI_NFC_CMD_HCI_SEND 0x01
+#define MEI_NFC_CMD_HCI_RECV 0x02
+
+#define MEI_NFC_SUBCMD_CONNECT 0x00
+#define MEI_NFC_SUBCMD_IF_VERSION 0x01
+
+#define MEI_NFC_MAX_READ (MEI_NFC_HEADER_SIZE + MEI_NFC_MAX_HCI_PAYLOAD)
+
+#define MEI_DUMP_SKB_IN(info, skb) \
+do { \
+ pr_debug("%s:\n", info); \
+ print_hex_dump_debug("mei in : ", DUMP_PREFIX_OFFSET, \
+ 16, 1, (skb)->data, (skb)->len, false); \
+} while (0)
+
+#define MEI_DUMP_SKB_OUT(info, skb) \
+do { \
+ pr_debug("%s:\n", info); \
+ print_hex_dump_debug("mei out: ", DUMP_PREFIX_OFFSET, \
+ 16, 1, (skb)->data, (skb)->len, false); \
+} while (0)
+
+#define MEI_DUMP_NFC_HDR(info, _hdr) \
+do { \
+ pr_debug("%s:\n", info); \
+ pr_debug("cmd=%02d status=%d req_id=%d rsvd=%d size=%d\n", \
+ (_hdr)->cmd, (_hdr)->status, (_hdr)->req_id, \
+ (_hdr)->reserved, (_hdr)->data_size); \
+} while (0)
+
+static int mei_nfc_if_version(struct nfc_mei_phy *phy)
+{
+
+ struct mei_nfc_cmd cmd;
+ struct mei_nfc_reply *reply = NULL;
+ struct mei_nfc_if_version *version;
+ size_t if_version_length;
+ int bytes_recv, r;
+
+ pr_info("%s\n", __func__);
+
+ memset(&cmd, 0, sizeof(struct mei_nfc_cmd));
+ cmd.hdr.cmd = MEI_NFC_CMD_MAINTENANCE;
+ cmd.hdr.data_size = 1;
+ cmd.sub_command = MEI_NFC_SUBCMD_IF_VERSION;
+
+ MEI_DUMP_NFC_HDR("version", &cmd.hdr);
+ r = mei_cldev_send(phy->cldev, (u8 *)&cmd, sizeof(struct mei_nfc_cmd));
+ if (r < 0) {
+ pr_err("Could not send IF version cmd\n");
+ return r;
+ }
+
+ /* to be sure on the stack we alloc memory */
+ if_version_length = sizeof(struct mei_nfc_reply) +
+ sizeof(struct mei_nfc_if_version);
+
+ reply = kzalloc(if_version_length, GFP_KERNEL);
+ if (!reply)
+ return -ENOMEM;
+
+ bytes_recv = mei_cldev_recv(phy->cldev, (u8 *)reply, if_version_length);
+ if (bytes_recv < 0 || bytes_recv < if_version_length) {
+ pr_err("Could not read IF version\n");
+ r = -EIO;
+ goto err;
+ }
+
+ version = (struct mei_nfc_if_version *)reply->data;
+
+ phy->fw_ivn = version->fw_ivn;
+ phy->vendor_id = version->vendor_id;
+ phy->radio_type = version->radio_type;
+
+err:
+ kfree(reply);
+ return r;
+}
+
+static int mei_nfc_connect(struct nfc_mei_phy *phy)
+{
+ struct mei_nfc_cmd *cmd, *reply;
+ struct mei_nfc_connect *connect;
+ struct mei_nfc_connect_resp *connect_resp;
+ size_t connect_length, connect_resp_length;
+ int bytes_recv, r;
+
+ pr_info("%s\n", __func__);
+
+ connect_length = sizeof(struct mei_nfc_cmd) +
+ sizeof(struct mei_nfc_connect);
+
+ connect_resp_length = sizeof(struct mei_nfc_cmd) +
+ sizeof(struct mei_nfc_connect_resp);
+
+ cmd = kzalloc(connect_length, GFP_KERNEL);
+ if (!cmd)
+ return -ENOMEM;
+ connect = (struct mei_nfc_connect *)cmd->data;
+
+ reply = kzalloc(connect_resp_length, GFP_KERNEL);
+ if (!reply) {
+ kfree(cmd);
+ return -ENOMEM;
+ }
+
+ connect_resp = (struct mei_nfc_connect_resp *)reply->data;
+
+ cmd->hdr.cmd = MEI_NFC_CMD_MAINTENANCE;
+ cmd->hdr.data_size = 3;
+ cmd->sub_command = MEI_NFC_SUBCMD_CONNECT;
+ connect->fw_ivn = phy->fw_ivn;
+ connect->vendor_id = phy->vendor_id;
+
+ MEI_DUMP_NFC_HDR("connect request", &cmd->hdr);
+ r = mei_cldev_send(phy->cldev, (u8 *)cmd, connect_length);
+ if (r < 0) {
+ pr_err("Could not send connect cmd %d\n", r);
+ goto err;
+ }
+
+ bytes_recv = mei_cldev_recv(phy->cldev, (u8 *)reply,
+ connect_resp_length);
+ if (bytes_recv < 0) {
+ r = bytes_recv;
+ pr_err("Could not read connect response %d\n", r);
+ goto err;
+ }
+
+ MEI_DUMP_NFC_HDR("connect reply", &reply->hdr);
+
+ pr_info("IVN 0x%x Vendor ID 0x%x\n",
+ connect_resp->fw_ivn, connect_resp->vendor_id);
+
+ pr_info("ME FW %d.%d.%d.%d\n",
+ connect_resp->me_major, connect_resp->me_minor,
+ connect_resp->me_hotfix, connect_resp->me_build);
+
+ r = 0;
+
+err:
+ kfree(reply);
+ kfree(cmd);
+
+ return r;
+}
+
+static int mei_nfc_send(struct nfc_mei_phy *phy, u8 *buf, size_t length)
+{
+ struct mei_nfc_hdr *hdr;
+ u8 *mei_buf;
+ int err;
+
+ err = -ENOMEM;
+ mei_buf = kzalloc(length + MEI_NFC_HEADER_SIZE, GFP_KERNEL);
+ if (!mei_buf)
+ goto out;
+
+ hdr = (struct mei_nfc_hdr *)mei_buf;
+ hdr->cmd = MEI_NFC_CMD_HCI_SEND;
+ hdr->status = 0;
+ hdr->req_id = phy->req_id;
+ hdr->reserved = 0;
+ hdr->data_size = length;
+
+ MEI_DUMP_NFC_HDR("send", hdr);
+
+ memcpy(mei_buf + MEI_NFC_HEADER_SIZE, buf, length);
+ err = mei_cldev_send(phy->cldev, mei_buf, length + MEI_NFC_HEADER_SIZE);
+ if (err < 0)
+ goto out;
+
+ if (!wait_event_interruptible_timeout(phy->send_wq,
+ phy->recv_req_id == phy->req_id, HZ)) {
+ pr_err("NFC MEI command timeout\n");
+ err = -ETIME;
+ } else {
+ phy->req_id++;
+ }
+out:
+ kfree(mei_buf);
+ return err;
+}
+
+/*
+ * Writing a frame must not return the number of written bytes.
+ * It must return either zero for success, or <0 for error.
+ * In addition, it must not alter the skb
+ */
+static int nfc_mei_phy_write(void *phy_id, struct sk_buff *skb)
+{
+ struct nfc_mei_phy *phy = phy_id;
+ int r;
+
+ MEI_DUMP_SKB_OUT("mei frame sent", skb);
+
+ r = mei_nfc_send(phy, skb->data, skb->len);
+ if (r > 0)
+ r = 0;
+
+ return r;
+}
+
+static int mei_nfc_recv(struct nfc_mei_phy *phy, u8 *buf, size_t length)
+{
+ struct mei_nfc_hdr *hdr;
+ int received_length;
+
+ received_length = mei_cldev_recv(phy->cldev, buf, length);
+ if (received_length < 0)
+ return received_length;
+
+ hdr = (struct mei_nfc_hdr *) buf;
+
+ MEI_DUMP_NFC_HDR("receive", hdr);
+ if (hdr->cmd == MEI_NFC_CMD_HCI_SEND) {
+ phy->recv_req_id = hdr->req_id;
+ wake_up(&phy->send_wq);
+
+ return 0;
+ }
+
+ return received_length;
+}
+
+
+static void nfc_mei_rx_cb(struct mei_cl_device *cldev)
+{
+ struct nfc_mei_phy *phy = mei_cldev_get_drvdata(cldev);
+ struct sk_buff *skb;
+ int reply_size;
+
+ if (!phy)
+ return;
+
+ if (phy->hard_fault != 0)
+ return;
+
+ skb = alloc_skb(MEI_NFC_MAX_READ, GFP_KERNEL);
+ if (!skb)
+ return;
+
+ reply_size = mei_nfc_recv(phy, skb->data, MEI_NFC_MAX_READ);
+ if (reply_size < MEI_NFC_HEADER_SIZE) {
+ kfree_skb(skb);
+ return;
+ }
+
+ skb_put(skb, reply_size);
+ skb_pull(skb, MEI_NFC_HEADER_SIZE);
+
+ MEI_DUMP_SKB_IN("mei frame read", skb);
+
+ nfc_hci_recv_frame(phy->hdev, skb);
+}
+
+static int nfc_mei_phy_enable(void *phy_id)
+{
+ int r;
+ struct nfc_mei_phy *phy = phy_id;
+
+ pr_info("%s\n", __func__);
+
+ if (phy->powered == 1)
+ return 0;
+
+ r = mei_cldev_enable(phy->cldev);
+ if (r < 0) {
+ pr_err("Could not enable device %d\n", r);
+ return r;
+ }
+
+ r = mei_nfc_if_version(phy);
+ if (r < 0) {
+ pr_err("Could not enable device %d\n", r);
+ goto err;
+ }
+
+ r = mei_nfc_connect(phy);
+ if (r < 0) {
+ pr_err("Could not connect to device %d\n", r);
+ goto err;
+ }
+
+ r = mei_cldev_register_rx_cb(phy->cldev, nfc_mei_rx_cb);
+ if (r) {
+ pr_err("Event cb registration failed %d\n", r);
+ goto err;
+ }
+
+ phy->powered = 1;
+
+ return 0;
+
+err:
+ phy->powered = 0;
+ mei_cldev_disable(phy->cldev);
+ return r;
+}
+
+static void nfc_mei_phy_disable(void *phy_id)
+{
+ struct nfc_mei_phy *phy = phy_id;
+
+ pr_info("%s\n", __func__);
+
+ mei_cldev_disable(phy->cldev);
+
+ phy->powered = 0;
+}
+
+struct nfc_phy_ops mei_phy_ops = {
+ .write = nfc_mei_phy_write,
+ .enable = nfc_mei_phy_enable,
+ .disable = nfc_mei_phy_disable,
+};
+EXPORT_SYMBOL_GPL(mei_phy_ops);
+
+struct nfc_mei_phy *nfc_mei_phy_alloc(struct mei_cl_device *cldev)
+{
+ struct nfc_mei_phy *phy;
+
+ phy = kzalloc(sizeof(struct nfc_mei_phy), GFP_KERNEL);
+ if (!phy)
+ return NULL;
+
+ phy->cldev = cldev;
+ init_waitqueue_head(&phy->send_wq);
+ mei_cldev_set_drvdata(cldev, phy);
+
+ return phy;
+}
+EXPORT_SYMBOL_GPL(nfc_mei_phy_alloc);
+
+void nfc_mei_phy_free(struct nfc_mei_phy *phy)
+{
+ mei_cldev_disable(phy->cldev);
+ kfree(phy);
+}
+EXPORT_SYMBOL_GPL(nfc_mei_phy_free);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("mei bus NFC device interface");
diff --git a/drivers/nfc/mei_phy.h b/drivers/nfc/mei_phy.h
new file mode 100644
index 0000000..acd3a1f
--- /dev/null
+++ b/drivers/nfc/mei_phy.h
@@ -0,0 +1,52 @@
+#ifndef __LOCAL_MEI_PHY_H_
+#define __LOCAL_MEI_PHY_H_
+
+#include <linux/mei_cl_bus.h>
+#include <net/nfc/hci.h>
+#include <linux/uuid.h>
+
+#define MEI_NFC_UUID UUID_LE(0x0bb17a78, 0x2a8e, 0x4c50, \
+ 0x94, 0xd4, 0x50, 0x26, 0x67, 0x23, 0x77, 0x5c)
+#define MEI_NFC_HEADER_SIZE 10
+#define MEI_NFC_MAX_HCI_PAYLOAD 300
+
+/**
+ * struct nfc_mei_phy
+ *
+ * @cldev: mei client device
+ * @hdev: nfc hci device
+
+ * @send_wq: send completion wait queue
+ * @fw_ivn: NFC Interface Version Number
+ * @vendor_id: NFC manufacturer ID
+ * @radio_type: NFC radio type
+ * @reserved: reserved for alignment
+ * @req_id: message counter
+ * @recv_req_id: reception message counter
+ * @powered: the device is in powered state
+ * @hard_fault: < 0 if hardware error occurred
+ * and prevents normal operation.
+ */
+struct nfc_mei_phy {
+ struct mei_cl_device *cldev;
+ struct nfc_hci_dev *hdev;
+
+ wait_queue_head_t send_wq;
+ u8 fw_ivn;
+ u8 vendor_id;
+ u8 radio_type;
+ u8 reserved;
+
+ u16 req_id;
+ u16 recv_req_id;
+
+ int powered;
+ int hard_fault;
+};
+
+extern struct nfc_phy_ops mei_phy_ops;
+
+struct nfc_mei_phy *nfc_mei_phy_alloc(struct mei_cl_device *device);
+void nfc_mei_phy_free(struct nfc_mei_phy *phy);
+
+#endif /* __LOCAL_MEI_PHY_H_ */
diff --git a/drivers/nfc/microread/Kconfig b/drivers/nfc/microread/Kconfig
new file mode 100644
index 0000000..5c5f72a
--- /dev/null
+++ b/drivers/nfc/microread/Kconfig
@@ -0,0 +1,34 @@
+config NFC_MICROREAD
+ tristate
+ depends on m
+ depends on CRC_CCITT
+ ---help---
+ This module contains the main code for Inside Secure microread
+ NFC chipsets. It implements the chipset HCI logic and hooks into
+ the NFC kernel APIs. Physical layers will register against it.
+
+config NFC_MICROREAD_I2C
+ tristate "Inside Secure Microread device support (I2C)"
+ depends on m
+ depends on NFC_HCI && I2C && NFC_SHDLC
+ select NFC_MICROREAD
+ ---help---
+ This module adds support for the i2c interface of adapters using
+ Inside microread chipsets. Select this if your platform is using
+ the i2c bus.
+
+ If you choose to build a module, it'll be called microread_i2c.
+ Say N if unsure.
+
+config NFC_MICROREAD_MEI
+ tristate "Inside Secure Microread device support (MEI)"
+ depends on m
+ depends on NFC_HCI && NFC_MEI_PHY
+ select NFC_MICROREAD
+ ---help---
+ This module adds support for the mei interface of adapters using
+ Inside microread chipsets. Select this if your microread chipset
+ is handled by Intel's Management Engine Interface on your platform.
+
+ If you choose to build a module, it'll be called microread_mei.
+ Say N if unsure.
diff --git a/drivers/nfc/microread/Makefile b/drivers/nfc/microread/Makefile
new file mode 100644
index 0000000..c44077a
--- /dev/null
+++ b/drivers/nfc/microread/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for Microread HCI based NFC driver
+#
+
+microread_i2c-objs = i2c.o
+microread_mei-objs = mei.o
+
+obj-$(CPTCFG_NFC_MICROREAD) += microread.o
+obj-$(CPTCFG_NFC_MICROREAD_I2C) += microread_i2c.o
+obj-$(CPTCFG_NFC_MICROREAD_MEI) += microread_mei.o
diff --git a/drivers/nfc/microread/i2c.c b/drivers/nfc/microread/i2c.c
new file mode 100644
index 0000000..e0e8afd
--- /dev/null
+++ b/drivers/nfc/microread/i2c.c
@@ -0,0 +1,315 @@
+/*
+ * HCI based Driver for Inside Secure microread NFC Chip - i2c layer
+ *
+ * Copyright (C) 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+
+#include <linux/nfc.h>
+#include <net/nfc/hci.h>
+#include <net/nfc/llc.h>
+
+#include "microread.h"
+
+#define MICROREAD_I2C_DRIVER_NAME "microread"
+
+#define MICROREAD_I2C_FRAME_HEADROOM 1
+#define MICROREAD_I2C_FRAME_TAILROOM 1
+
+/* framing in HCI mode */
+#define MICROREAD_I2C_LLC_LEN 1
+#define MICROREAD_I2C_LLC_CRC 1
+#define MICROREAD_I2C_LLC_LEN_CRC (MICROREAD_I2C_LLC_LEN + \
+ MICROREAD_I2C_LLC_CRC)
+#define MICROREAD_I2C_LLC_MIN_SIZE (1 + MICROREAD_I2C_LLC_LEN_CRC)
+#define MICROREAD_I2C_LLC_MAX_PAYLOAD 29
+#define MICROREAD_I2C_LLC_MAX_SIZE (MICROREAD_I2C_LLC_LEN_CRC + 1 + \
+ MICROREAD_I2C_LLC_MAX_PAYLOAD)
+
+struct microread_i2c_phy {
+ struct i2c_client *i2c_dev;
+ struct nfc_hci_dev *hdev;
+
+ int hard_fault; /*
+ * < 0 if hardware error occured (e.g. i2c err)
+ * and prevents normal operation.
+ */
+};
+
+#define I2C_DUMP_SKB(info, skb) \
+do { \
+ pr_debug("%s:\n", info); \
+ print_hex_dump(KERN_DEBUG, "i2c: ", DUMP_PREFIX_OFFSET, \
+ 16, 1, (skb)->data, (skb)->len, 0); \
+} while (0)
+
+static void microread_i2c_add_len_crc(struct sk_buff *skb)
+{
+ int i;
+ u8 crc = 0;
+ int len;
+
+ len = skb->len;
+ *skb_push(skb, 1) = len;
+
+ for (i = 0; i < skb->len; i++)
+ crc = crc ^ skb->data[i];
+
+ *skb_put(skb, 1) = crc;
+}
+
+static void microread_i2c_remove_len_crc(struct sk_buff *skb)
+{
+ skb_pull(skb, MICROREAD_I2C_FRAME_HEADROOM);
+ skb_trim(skb, MICROREAD_I2C_FRAME_TAILROOM);
+}
+
+static int check_crc(struct sk_buff *skb)
+{
+ int i;
+ u8 crc = 0;
+
+ for (i = 0; i < skb->len - 1; i++)
+ crc = crc ^ skb->data[i];
+
+ if (crc != skb->data[skb->len-1]) {
+ pr_err("CRC error 0x%x != 0x%x\n", crc, skb->data[skb->len-1]);
+ pr_info("%s: BAD CRC\n", __func__);
+ return -EPERM;
+ }
+
+ return 0;
+}
+
+static int microread_i2c_enable(void *phy_id)
+{
+ return 0;
+}
+
+static void microread_i2c_disable(void *phy_id)
+{
+ return;
+}
+
+static int microread_i2c_write(void *phy_id, struct sk_buff *skb)
+{
+ int r;
+ struct microread_i2c_phy *phy = phy_id;
+ struct i2c_client *client = phy->i2c_dev;
+
+ if (phy->hard_fault != 0)
+ return phy->hard_fault;
+
+ usleep_range(3000, 6000);
+
+ microread_i2c_add_len_crc(skb);
+
+ I2C_DUMP_SKB("i2c frame written", skb);
+
+ r = i2c_master_send(client, skb->data, skb->len);
+
+ if (r == -EREMOTEIO) { /* Retry, chip was in standby */
+ usleep_range(6000, 10000);
+ r = i2c_master_send(client, skb->data, skb->len);
+ }
+
+ if (r >= 0) {
+ if (r != skb->len)
+ r = -EREMOTEIO;
+ else
+ r = 0;
+ }
+
+ microread_i2c_remove_len_crc(skb);
+
+ return r;
+}
+
+
+static int microread_i2c_read(struct microread_i2c_phy *phy,
+ struct sk_buff **skb)
+{
+ int r;
+ u8 len;
+ u8 tmp[MICROREAD_I2C_LLC_MAX_SIZE - 1];
+ struct i2c_client *client = phy->i2c_dev;
+
+ r = i2c_master_recv(client, &len, 1);
+ if (r != 1) {
+ nfc_err(&client->dev, "cannot read len byte\n");
+ return -EREMOTEIO;
+ }
+
+ if ((len < MICROREAD_I2C_LLC_MIN_SIZE) ||
+ (len > MICROREAD_I2C_LLC_MAX_SIZE)) {
+ nfc_err(&client->dev, "invalid len byte\n");
+ r = -EBADMSG;
+ goto flush;
+ }
+
+ *skb = alloc_skb(1 + len, GFP_KERNEL);
+ if (*skb == NULL) {
+ r = -ENOMEM;
+ goto flush;
+ }
+
+ *skb_put(*skb, 1) = len;
+
+ r = i2c_master_recv(client, skb_put(*skb, len), len);
+ if (r != len) {
+ kfree_skb(*skb);
+ return -EREMOTEIO;
+ }
+
+ I2C_DUMP_SKB("cc frame read", *skb);
+
+ r = check_crc(*skb);
+ if (r != 0) {
+ kfree_skb(*skb);
+ r = -EBADMSG;
+ goto flush;
+ }
+
+ skb_pull(*skb, 1);
+ skb_trim(*skb, (*skb)->len - MICROREAD_I2C_FRAME_TAILROOM);
+
+ usleep_range(3000, 6000);
+
+ return 0;
+
+flush:
+ if (i2c_master_recv(client, tmp, sizeof(tmp)) < 0)
+ r = -EREMOTEIO;
+
+ usleep_range(3000, 6000);
+
+ return r;
+}
+
+static irqreturn_t microread_i2c_irq_thread_fn(int irq, void *phy_id)
+{
+ struct microread_i2c_phy *phy = phy_id;
+ struct sk_buff *skb = NULL;
+ int r;
+
+ if (!phy || irq != phy->i2c_dev->irq) {
+ WARN_ON_ONCE(1);
+ return IRQ_NONE;
+ }
+
+ if (phy->hard_fault != 0)
+ return IRQ_HANDLED;
+
+ r = microread_i2c_read(phy, &skb);
+ if (r == -EREMOTEIO) {
+ phy->hard_fault = r;
+
+ nfc_hci_recv_frame(phy->hdev, NULL);
+
+ return IRQ_HANDLED;
+ } else if ((r == -ENOMEM) || (r == -EBADMSG)) {
+ return IRQ_HANDLED;
+ }
+
+ nfc_hci_recv_frame(phy->hdev, skb);
+
+ return IRQ_HANDLED;
+}
+
+static struct nfc_phy_ops i2c_phy_ops = {
+ .write = microread_i2c_write,
+ .enable = microread_i2c_enable,
+ .disable = microread_i2c_disable,
+};
+
+static int microread_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct microread_i2c_phy *phy;
+ int r;
+
+ dev_dbg(&client->dev, "client %p\n", client);
+
+ phy = devm_kzalloc(&client->dev, sizeof(struct microread_i2c_phy),
+ GFP_KERNEL);
+ if (!phy)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, phy);
+ phy->i2c_dev = client;
+
+ r = request_threaded_irq(client->irq, NULL, microread_i2c_irq_thread_fn,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ MICROREAD_I2C_DRIVER_NAME, phy);
+ if (r) {
+ nfc_err(&client->dev, "Unable to register IRQ handler\n");
+ return r;
+ }
+
+ r = microread_probe(phy, &i2c_phy_ops, LLC_SHDLC_NAME,
+ MICROREAD_I2C_FRAME_HEADROOM,
+ MICROREAD_I2C_FRAME_TAILROOM,
+ MICROREAD_I2C_LLC_MAX_PAYLOAD, &phy->hdev);
+ if (r < 0)
+ goto err_irq;
+
+ nfc_info(&client->dev, "Probed\n");
+
+ return 0;
+
+err_irq:
+ free_irq(client->irq, phy);
+
+ return r;
+}
+
+static int microread_i2c_remove(struct i2c_client *client)
+{
+ struct microread_i2c_phy *phy = i2c_get_clientdata(client);
+
+ microread_remove(phy->hdev);
+
+ free_irq(client->irq, phy);
+
+ return 0;
+}
+
+static struct i2c_device_id microread_i2c_id[] = {
+ { MICROREAD_I2C_DRIVER_NAME, 0},
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, microread_i2c_id);
+
+static struct i2c_driver microread_i2c_driver = {
+ .driver = {
+ .name = MICROREAD_I2C_DRIVER_NAME,
+ },
+ .probe = microread_i2c_probe,
+ .remove = microread_i2c_remove,
+ .id_table = microread_i2c_id,
+};
+
+module_i2c_driver(microread_i2c_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/nfc/microread/mei.c b/drivers/nfc/microread/mei.c
new file mode 100644
index 0000000..5d9e51b
--- /dev/null
+++ b/drivers/nfc/microread/mei.c
@@ -0,0 +1,94 @@
+/*
+ * HCI based Driver for Inside Secure microread NFC Chip
+ *
+ * Copyright (C) 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/nfc.h>
+#include <net/nfc/hci.h>
+#include <net/nfc/llc.h>
+
+#include "../mei_phy.h"
+#include "microread.h"
+
+#define MICROREAD_DRIVER_NAME "microread"
+
+static int microread_mei_probe(struct mei_cl_device *cldev,
+ const struct mei_cl_device_id *id)
+{
+ struct nfc_mei_phy *phy;
+ int r;
+
+ pr_info("Probing NFC microread\n");
+
+ phy = nfc_mei_phy_alloc(cldev);
+ if (!phy) {
+ pr_err("Cannot allocate memory for microread mei phy.\n");
+ return -ENOMEM;
+ }
+
+ r = microread_probe(phy, &mei_phy_ops, LLC_NOP_NAME,
+ MEI_NFC_HEADER_SIZE, 0, MEI_NFC_MAX_HCI_PAYLOAD,
+ &phy->hdev);
+ if (r < 0) {
+ nfc_mei_phy_free(phy);
+
+ return r;
+ }
+
+ return 0;
+}
+
+static int microread_mei_remove(struct mei_cl_device *cldev)
+{
+ struct nfc_mei_phy *phy = mei_cldev_get_drvdata(cldev);
+
+ microread_remove(phy->hdev);
+
+ nfc_mei_phy_free(phy);
+
+ return 0;
+}
+
+static struct mei_cl_device_id microread_mei_tbl[] = {
+#if LINUX_VERSION_IS_GEQ(4,4,0)
+ { MICROREAD_DRIVER_NAME, MEI_NFC_UUID, MEI_CL_VERSION_ANY},
+#elif LINUX_VERSION_IS_GEQ(4,2,0)
+ { MICROREAD_DRIVER_NAME, MEI_NFC_UUID},
+#else
+ { MICROREAD_DRIVER_NAME},
+#endif
+
+ /* required last entry */
+ { }
+};
+MODULE_DEVICE_TABLE(mei, microread_mei_tbl);
+
+static struct mei_cl_driver microread_driver = {
+ .id_table = microread_mei_tbl,
+ .name = MICROREAD_DRIVER_NAME,
+
+ .probe = microread_mei_probe,
+ .remove = microread_mei_remove,
+};
+
+module_mei_cl_driver(microread_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/nfc/microread/microread.c b/drivers/nfc/microread/microread.c
new file mode 100644
index 0000000..f454dc6
--- /dev/null
+++ b/drivers/nfc/microread/microread.c
@@ -0,0 +1,734 @@
+/*
+ * HCI based Driver for Inside Secure microread NFC Chip
+ *
+ * Copyright (C) 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/crc-ccitt.h>
+
+#include <linux/nfc.h>
+#include <net/nfc/nfc.h>
+#include <net/nfc/hci.h>
+#include <net/nfc/llc.h>
+
+#include "microread.h"
+
+/* Proprietary gates, events, commands and registers */
+/* Admin */
+#define MICROREAD_GATE_ID_ADM NFC_HCI_ADMIN_GATE
+#define MICROREAD_GATE_ID_MGT 0x01
+#define MICROREAD_GATE_ID_OS 0x02
+#define MICROREAD_GATE_ID_TESTRF 0x03
+#define MICROREAD_GATE_ID_LOOPBACK NFC_HCI_LOOPBACK_GATE
+#define MICROREAD_GATE_ID_IDT NFC_HCI_ID_MGMT_GATE
+#define MICROREAD_GATE_ID_LMS NFC_HCI_LINK_MGMT_GATE
+
+/* Reader */
+#define MICROREAD_GATE_ID_MREAD_GEN 0x10
+#define MICROREAD_GATE_ID_MREAD_ISO_B NFC_HCI_RF_READER_B_GATE
+#define MICROREAD_GATE_ID_MREAD_NFC_T1 0x12
+#define MICROREAD_GATE_ID_MREAD_ISO_A NFC_HCI_RF_READER_A_GATE
+#define MICROREAD_GATE_ID_MREAD_NFC_T3 0x14
+#define MICROREAD_GATE_ID_MREAD_ISO_15_3 0x15
+#define MICROREAD_GATE_ID_MREAD_ISO_15_2 0x16
+#define MICROREAD_GATE_ID_MREAD_ISO_B_3 0x17
+#define MICROREAD_GATE_ID_MREAD_BPRIME 0x18
+#define MICROREAD_GATE_ID_MREAD_ISO_A_3 0x19
+
+/* Card */
+#define MICROREAD_GATE_ID_MCARD_GEN 0x20
+#define MICROREAD_GATE_ID_MCARD_ISO_B 0x21
+#define MICROREAD_GATE_ID_MCARD_BPRIME 0x22
+#define MICROREAD_GATE_ID_MCARD_ISO_A 0x23
+#define MICROREAD_GATE_ID_MCARD_NFC_T3 0x24
+#define MICROREAD_GATE_ID_MCARD_ISO_15_3 0x25
+#define MICROREAD_GATE_ID_MCARD_ISO_15_2 0x26
+#define MICROREAD_GATE_ID_MCARD_ISO_B_2 0x27
+#define MICROREAD_GATE_ID_MCARD_ISO_CUSTOM 0x28
+#define MICROREAD_GATE_ID_SECURE_ELEMENT 0x2F
+
+/* P2P */
+#define MICROREAD_GATE_ID_P2P_GEN 0x30
+#define MICROREAD_GATE_ID_P2P_TARGET 0x31
+#define MICROREAD_PAR_P2P_TARGET_MODE 0x01
+#define MICROREAD_PAR_P2P_TARGET_GT 0x04
+#define MICROREAD_GATE_ID_P2P_INITIATOR 0x32
+#define MICROREAD_PAR_P2P_INITIATOR_GI 0x01
+#define MICROREAD_PAR_P2P_INITIATOR_GT 0x03
+
+/* Those pipes are created/opened by default in the chip */
+#define MICROREAD_PIPE_ID_LMS 0x00
+#define MICROREAD_PIPE_ID_ADMIN 0x01
+#define MICROREAD_PIPE_ID_MGT 0x02
+#define MICROREAD_PIPE_ID_OS 0x03
+#define MICROREAD_PIPE_ID_HDS_LOOPBACK 0x04
+#define MICROREAD_PIPE_ID_HDS_IDT 0x05
+#define MICROREAD_PIPE_ID_HDS_MCARD_ISO_B 0x08
+#define MICROREAD_PIPE_ID_HDS_MCARD_ISO_BPRIME 0x09
+#define MICROREAD_PIPE_ID_HDS_MCARD_ISO_A 0x0A
+#define MICROREAD_PIPE_ID_HDS_MCARD_ISO_15_3 0x0B
+#define MICROREAD_PIPE_ID_HDS_MCARD_ISO_15_2 0x0C
+#define MICROREAD_PIPE_ID_HDS_MCARD_NFC_T3 0x0D
+#define MICROREAD_PIPE_ID_HDS_MCARD_ISO_B_2 0x0E
+#define MICROREAD_PIPE_ID_HDS_MCARD_CUSTOM 0x0F
+#define MICROREAD_PIPE_ID_HDS_MREAD_ISO_B 0x10
+#define MICROREAD_PIPE_ID_HDS_MREAD_NFC_T1 0x11
+#define MICROREAD_PIPE_ID_HDS_MREAD_ISO_A 0x12
+#define MICROREAD_PIPE_ID_HDS_MREAD_ISO_15_3 0x13
+#define MICROREAD_PIPE_ID_HDS_MREAD_ISO_15_2 0x14
+#define MICROREAD_PIPE_ID_HDS_MREAD_NFC_T3 0x15
+#define MICROREAD_PIPE_ID_HDS_MREAD_ISO_B_3 0x16
+#define MICROREAD_PIPE_ID_HDS_MREAD_BPRIME 0x17
+#define MICROREAD_PIPE_ID_HDS_MREAD_ISO_A_3 0x18
+#define MICROREAD_PIPE_ID_HDS_MREAD_GEN 0x1B
+#define MICROREAD_PIPE_ID_HDS_STACKED_ELEMENT 0x1C
+#define MICROREAD_PIPE_ID_HDS_INSTANCES 0x1D
+#define MICROREAD_PIPE_ID_HDS_TESTRF 0x1E
+#define MICROREAD_PIPE_ID_HDS_P2P_TARGET 0x1F
+#define MICROREAD_PIPE_ID_HDS_P2P_INITIATOR 0x20
+
+/* Events */
+#define MICROREAD_EVT_MREAD_DISCOVERY_OCCURED NFC_HCI_EVT_TARGET_DISCOVERED
+#define MICROREAD_EVT_MREAD_CARD_FOUND 0x3D
+#define MICROREAD_EMCF_A_ATQA 0
+#define MICROREAD_EMCF_A_SAK 2
+#define MICROREAD_EMCF_A_LEN 3
+#define MICROREAD_EMCF_A_UID 4
+#define MICROREAD_EMCF_A3_ATQA 0
+#define MICROREAD_EMCF_A3_SAK 2
+#define MICROREAD_EMCF_A3_LEN 3
+#define MICROREAD_EMCF_A3_UID 4
+#define MICROREAD_EMCF_B_UID 0
+#define MICROREAD_EMCF_T1_ATQA 0
+#define MICROREAD_EMCF_T1_UID 4
+#define MICROREAD_EMCF_T3_UID 0
+#define MICROREAD_EVT_MREAD_DISCOVERY_START NFC_HCI_EVT_READER_REQUESTED
+#define MICROREAD_EVT_MREAD_DISCOVERY_START_SOME 0x3E
+#define MICROREAD_EVT_MREAD_DISCOVERY_STOP NFC_HCI_EVT_END_OPERATION
+#define MICROREAD_EVT_MREAD_SIM_REQUESTS 0x3F
+#define MICROREAD_EVT_MCARD_EXCHANGE NFC_HCI_EVT_TARGET_DISCOVERED
+#define MICROREAD_EVT_P2P_INITIATOR_EXCHANGE_TO_RF 0x20
+#define MICROREAD_EVT_P2P_INITIATOR_EXCHANGE_FROM_RF 0x21
+#define MICROREAD_EVT_MCARD_FIELD_ON 0x11
+#define MICROREAD_EVT_P2P_TARGET_ACTIVATED 0x13
+#define MICROREAD_EVT_P2P_TARGET_DEACTIVATED 0x12
+#define MICROREAD_EVT_MCARD_FIELD_OFF 0x14
+
+/* Commands */
+#define MICROREAD_CMD_MREAD_EXCHANGE 0x10
+#define MICROREAD_CMD_MREAD_SUBSCRIBE 0x3F
+
+/* Hosts IDs */
+#define MICROREAD_ELT_ID_HDS NFC_HCI_TERMINAL_HOST_ID
+#define MICROREAD_ELT_ID_SIM NFC_HCI_UICC_HOST_ID
+#define MICROREAD_ELT_ID_SE1 0x03
+#define MICROREAD_ELT_ID_SE2 0x04
+#define MICROREAD_ELT_ID_SE3 0x05
+
+static struct nfc_hci_gate microread_gates[] = {
+ {MICROREAD_GATE_ID_ADM, MICROREAD_PIPE_ID_ADMIN},
+ {MICROREAD_GATE_ID_LOOPBACK, MICROREAD_PIPE_ID_HDS_LOOPBACK},
+ {MICROREAD_GATE_ID_IDT, MICROREAD_PIPE_ID_HDS_IDT},
+ {MICROREAD_GATE_ID_LMS, MICROREAD_PIPE_ID_LMS},
+ {MICROREAD_GATE_ID_MREAD_ISO_B, MICROREAD_PIPE_ID_HDS_MREAD_ISO_B},
+ {MICROREAD_GATE_ID_MREAD_ISO_A, MICROREAD_PIPE_ID_HDS_MREAD_ISO_A},
+ {MICROREAD_GATE_ID_MREAD_ISO_A_3, MICROREAD_PIPE_ID_HDS_MREAD_ISO_A_3},
+ {MICROREAD_GATE_ID_MGT, MICROREAD_PIPE_ID_MGT},
+ {MICROREAD_GATE_ID_OS, MICROREAD_PIPE_ID_OS},
+ {MICROREAD_GATE_ID_MREAD_NFC_T1, MICROREAD_PIPE_ID_HDS_MREAD_NFC_T1},
+ {MICROREAD_GATE_ID_MREAD_NFC_T3, MICROREAD_PIPE_ID_HDS_MREAD_NFC_T3},
+ {MICROREAD_GATE_ID_P2P_TARGET, MICROREAD_PIPE_ID_HDS_P2P_TARGET},
+ {MICROREAD_GATE_ID_P2P_INITIATOR, MICROREAD_PIPE_ID_HDS_P2P_INITIATOR}
+};
+
+/* Largest headroom needed for outgoing custom commands */
+#define MICROREAD_CMDS_HEADROOM 2
+#define MICROREAD_CMD_TAILROOM 2
+
+struct microread_info {
+ struct nfc_phy_ops *phy_ops;
+ void *phy_id;
+
+ struct nfc_hci_dev *hdev;
+
+ int async_cb_type;
+ data_exchange_cb_t async_cb;
+ void *async_cb_context;
+};
+
+static int microread_open(struct nfc_hci_dev *hdev)
+{
+ struct microread_info *info = nfc_hci_get_clientdata(hdev);
+
+ return info->phy_ops->enable(info->phy_id);
+}
+
+static void microread_close(struct nfc_hci_dev *hdev)
+{
+ struct microread_info *info = nfc_hci_get_clientdata(hdev);
+
+ info->phy_ops->disable(info->phy_id);
+}
+
+static int microread_hci_ready(struct nfc_hci_dev *hdev)
+{
+ int r;
+ u8 param[4];
+
+ param[0] = 0x03;
+ r = nfc_hci_send_cmd(hdev, MICROREAD_GATE_ID_MREAD_ISO_A,
+ MICROREAD_CMD_MREAD_SUBSCRIBE, param, 1, NULL);
+ if (r)
+ return r;
+
+ r = nfc_hci_send_cmd(hdev, MICROREAD_GATE_ID_MREAD_ISO_A_3,
+ MICROREAD_CMD_MREAD_SUBSCRIBE, NULL, 0, NULL);
+ if (r)
+ return r;
+
+ param[0] = 0x00;
+ param[1] = 0x03;
+ param[2] = 0x00;
+ r = nfc_hci_send_cmd(hdev, MICROREAD_GATE_ID_MREAD_ISO_B,
+ MICROREAD_CMD_MREAD_SUBSCRIBE, param, 3, NULL);
+ if (r)
+ return r;
+
+ r = nfc_hci_send_cmd(hdev, MICROREAD_GATE_ID_MREAD_NFC_T1,
+ MICROREAD_CMD_MREAD_SUBSCRIBE, NULL, 0, NULL);
+ if (r)
+ return r;
+
+ param[0] = 0xFF;
+ param[1] = 0xFF;
+ param[2] = 0x00;
+ param[3] = 0x00;
+ r = nfc_hci_send_cmd(hdev, MICROREAD_GATE_ID_MREAD_NFC_T3,
+ MICROREAD_CMD_MREAD_SUBSCRIBE, param, 4, NULL);
+
+ return r;
+}
+
+static int microread_xmit(struct nfc_hci_dev *hdev, struct sk_buff *skb)
+{
+ struct microread_info *info = nfc_hci_get_clientdata(hdev);
+
+ return info->phy_ops->write(info->phy_id, skb);
+}
+
+static int microread_start_poll(struct nfc_hci_dev *hdev,
+ u32 im_protocols, u32 tm_protocols)
+{
+ int r;
+
+ u8 param[2];
+ u8 mode;
+
+ param[0] = 0x00;
+ param[1] = 0x00;
+
+ if (im_protocols & NFC_PROTO_ISO14443_MASK)
+ param[0] |= (1 << 2);
+
+ if (im_protocols & NFC_PROTO_ISO14443_B_MASK)
+ param[0] |= 1;
+
+ if (im_protocols & NFC_PROTO_MIFARE_MASK)
+ param[1] |= 1;
+
+ if (im_protocols & NFC_PROTO_JEWEL_MASK)
+ param[0] |= (1 << 1);
+
+ if (im_protocols & NFC_PROTO_FELICA_MASK)
+ param[0] |= (1 << 5);
+
+ if (im_protocols & NFC_PROTO_NFC_DEP_MASK)
+ param[1] |= (1 << 1);
+
+ if ((im_protocols | tm_protocols) & NFC_PROTO_NFC_DEP_MASK) {
+ hdev->gb = nfc_get_local_general_bytes(hdev->ndev,
+ &hdev->gb_len);
+ if (hdev->gb == NULL || hdev->gb_len == 0) {
+ im_protocols &= ~NFC_PROTO_NFC_DEP_MASK;
+ tm_protocols &= ~NFC_PROTO_NFC_DEP_MASK;
+ }
+ }
+
+ r = nfc_hci_send_event(hdev, MICROREAD_GATE_ID_MREAD_ISO_A,
+ MICROREAD_EVT_MREAD_DISCOVERY_STOP, NULL, 0);
+ if (r)
+ return r;
+
+ mode = 0xff;
+ r = nfc_hci_set_param(hdev, MICROREAD_GATE_ID_P2P_TARGET,
+ MICROREAD_PAR_P2P_TARGET_MODE, &mode, 1);
+ if (r)
+ return r;
+
+ if (im_protocols & NFC_PROTO_NFC_DEP_MASK) {
+ r = nfc_hci_set_param(hdev, MICROREAD_GATE_ID_P2P_INITIATOR,
+ MICROREAD_PAR_P2P_INITIATOR_GI,
+ hdev->gb, hdev->gb_len);
+ if (r)
+ return r;
+ }
+
+ if (tm_protocols & NFC_PROTO_NFC_DEP_MASK) {
+ r = nfc_hci_set_param(hdev, MICROREAD_GATE_ID_P2P_TARGET,
+ MICROREAD_PAR_P2P_TARGET_GT,
+ hdev->gb, hdev->gb_len);
+ if (r)
+ return r;
+
+ mode = 0x02;
+ r = nfc_hci_set_param(hdev, MICROREAD_GATE_ID_P2P_TARGET,
+ MICROREAD_PAR_P2P_TARGET_MODE, &mode, 1);
+ if (r)
+ return r;
+ }
+
+ return nfc_hci_send_event(hdev, MICROREAD_GATE_ID_MREAD_ISO_A,
+ MICROREAD_EVT_MREAD_DISCOVERY_START_SOME,
+ param, 2);
+}
+
+static int microread_dep_link_up(struct nfc_hci_dev *hdev,
+ struct nfc_target *target, u8 comm_mode,
+ u8 *gb, size_t gb_len)
+{
+ struct sk_buff *rgb_skb = NULL;
+ int r;
+
+ r = nfc_hci_get_param(hdev, target->hci_reader_gate,
+ MICROREAD_PAR_P2P_INITIATOR_GT, &rgb_skb);
+ if (r < 0)
+ return r;
+
+ if (rgb_skb->len == 0 || rgb_skb->len > NFC_GB_MAXSIZE) {
+ r = -EPROTO;
+ goto exit;
+ }
+
+ r = nfc_set_remote_general_bytes(hdev->ndev, rgb_skb->data,
+ rgb_skb->len);
+ if (r == 0)
+ r = nfc_dep_link_is_up(hdev->ndev, target->idx, comm_mode,
+ NFC_RF_INITIATOR);
+exit:
+ kfree_skb(rgb_skb);
+
+ return r;
+}
+
+static int microread_dep_link_down(struct nfc_hci_dev *hdev)
+{
+ return nfc_hci_send_event(hdev, MICROREAD_GATE_ID_P2P_INITIATOR,
+ MICROREAD_EVT_MREAD_DISCOVERY_STOP, NULL, 0);
+}
+
+static int microread_target_from_gate(struct nfc_hci_dev *hdev, u8 gate,
+ struct nfc_target *target)
+{
+ switch (gate) {
+ case MICROREAD_GATE_ID_P2P_INITIATOR:
+ target->supported_protocols = NFC_PROTO_NFC_DEP_MASK;
+ break;
+ default:
+ return -EPROTO;
+ }
+
+ return 0;
+}
+
+static int microread_complete_target_discovered(struct nfc_hci_dev *hdev,
+ u8 gate,
+ struct nfc_target *target)
+{
+ return 0;
+}
+
+#define MICROREAD_CB_TYPE_READER_ALL 1
+
+static void microread_im_transceive_cb(void *context, struct sk_buff *skb,
+ int err)
+{
+ struct microread_info *info = context;
+
+ switch (info->async_cb_type) {
+ case MICROREAD_CB_TYPE_READER_ALL:
+ if (err == 0) {
+ if (skb->len == 0) {
+ err = -EPROTO;
+ kfree_skb(skb);
+ info->async_cb(info->async_cb_context, NULL,
+ -EPROTO);
+ return;
+ }
+
+ if (skb->data[skb->len - 1] != 0) {
+ err = nfc_hci_result_to_errno(
+ skb->data[skb->len - 1]);
+ kfree_skb(skb);
+ info->async_cb(info->async_cb_context, NULL,
+ err);
+ return;
+ }
+
+ skb_trim(skb, skb->len - 1); /* RF Error ind. */
+ }
+ info->async_cb(info->async_cb_context, skb, err);
+ break;
+ default:
+ if (err == 0)
+ kfree_skb(skb);
+ break;
+ }
+}
+
+/*
+ * Returns:
+ * <= 0: driver handled the data exchange
+ * 1: driver doesn't especially handle, please do standard processing
+ */
+static int microread_im_transceive(struct nfc_hci_dev *hdev,
+ struct nfc_target *target,
+ struct sk_buff *skb, data_exchange_cb_t cb,
+ void *cb_context)
+{
+ struct microread_info *info = nfc_hci_get_clientdata(hdev);
+ u8 control_bits;
+ u16 crc;
+
+ pr_info("data exchange to gate 0x%x\n", target->hci_reader_gate);
+
+ if (target->hci_reader_gate == MICROREAD_GATE_ID_P2P_INITIATOR) {
+ *skb_push(skb, 1) = 0;
+
+ return nfc_hci_send_event(hdev, target->hci_reader_gate,
+ MICROREAD_EVT_P2P_INITIATOR_EXCHANGE_TO_RF,
+ skb->data, skb->len);
+ }
+
+ switch (target->hci_reader_gate) {
+ case MICROREAD_GATE_ID_MREAD_ISO_A:
+ control_bits = 0xCB;
+ break;
+ case MICROREAD_GATE_ID_MREAD_ISO_A_3:
+ control_bits = 0xCB;
+ break;
+ case MICROREAD_GATE_ID_MREAD_ISO_B:
+ control_bits = 0xCB;
+ break;
+ case MICROREAD_GATE_ID_MREAD_NFC_T1:
+ control_bits = 0x1B;
+
+ crc = crc_ccitt(0xffff, skb->data, skb->len);
+ crc = ~crc;
+ *skb_put(skb, 1) = crc & 0xff;
+ *skb_put(skb, 1) = crc >> 8;
+ break;
+ case MICROREAD_GATE_ID_MREAD_NFC_T3:
+ control_bits = 0xDB;
+ break;
+ default:
+ pr_info("Abort im_transceive to invalid gate 0x%x\n",
+ target->hci_reader_gate);
+ return 1;
+ }
+
+ *skb_push(skb, 1) = control_bits;
+
+ info->async_cb_type = MICROREAD_CB_TYPE_READER_ALL;
+ info->async_cb = cb;
+ info->async_cb_context = cb_context;
+
+ return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate,
+ MICROREAD_CMD_MREAD_EXCHANGE,
+ skb->data, skb->len,
+ microread_im_transceive_cb, info);
+}
+
+static int microread_tm_send(struct nfc_hci_dev *hdev, struct sk_buff *skb)
+{
+ int r;
+
+ r = nfc_hci_send_event(hdev, MICROREAD_GATE_ID_P2P_TARGET,
+ MICROREAD_EVT_MCARD_EXCHANGE,
+ skb->data, skb->len);
+
+ kfree_skb(skb);
+
+ return r;
+}
+
+static void microread_target_discovered(struct nfc_hci_dev *hdev, u8 gate,
+ struct sk_buff *skb)
+{
+ struct nfc_target *targets;
+ int r = 0;
+
+ pr_info("target discovered to gate 0x%x\n", gate);
+
+ targets = kzalloc(sizeof(struct nfc_target), GFP_KERNEL);
+ if (targets == NULL) {
+ r = -ENOMEM;
+ goto exit;
+ }
+
+ targets->hci_reader_gate = gate;
+
+ switch (gate) {
+ case MICROREAD_GATE_ID_MREAD_ISO_A:
+ targets->supported_protocols =
+ nfc_hci_sak_to_protocol(skb->data[MICROREAD_EMCF_A_SAK]);
+ targets->sens_res =
+ be16_to_cpu(*(u16 *)&skb->data[MICROREAD_EMCF_A_ATQA]);
+ targets->sel_res = skb->data[MICROREAD_EMCF_A_SAK];
+ targets->nfcid1_len = skb->data[MICROREAD_EMCF_A_LEN];
+ if (targets->nfcid1_len > sizeof(targets->nfcid1)) {
+ r = -EINVAL;
+ goto exit_free;
+ }
+ memcpy(targets->nfcid1, &skb->data[MICROREAD_EMCF_A_UID],
+ targets->nfcid1_len);
+ break;
+ case MICROREAD_GATE_ID_MREAD_ISO_A_3:
+ targets->supported_protocols =
+ nfc_hci_sak_to_protocol(skb->data[MICROREAD_EMCF_A3_SAK]);
+ targets->sens_res =
+ be16_to_cpu(*(u16 *)&skb->data[MICROREAD_EMCF_A3_ATQA]);
+ targets->sel_res = skb->data[MICROREAD_EMCF_A3_SAK];
+ targets->nfcid1_len = skb->data[MICROREAD_EMCF_A3_LEN];
+ if (targets->nfcid1_len > sizeof(targets->nfcid1)) {
+ r = -EINVAL;
+ goto exit_free;
+ }
+ memcpy(targets->nfcid1, &skb->data[MICROREAD_EMCF_A3_UID],
+ targets->nfcid1_len);
+ break;
+ case MICROREAD_GATE_ID_MREAD_ISO_B:
+ targets->supported_protocols = NFC_PROTO_ISO14443_B_MASK;
+ memcpy(targets->nfcid1, &skb->data[MICROREAD_EMCF_B_UID], 4);
+ targets->nfcid1_len = 4;
+ break;
+ case MICROREAD_GATE_ID_MREAD_NFC_T1:
+ targets->supported_protocols = NFC_PROTO_JEWEL_MASK;
+ targets->sens_res =
+ le16_to_cpu(*(u16 *)&skb->data[MICROREAD_EMCF_T1_ATQA]);
+ memcpy(targets->nfcid1, &skb->data[MICROREAD_EMCF_T1_UID], 4);
+ targets->nfcid1_len = 4;
+ break;
+ case MICROREAD_GATE_ID_MREAD_NFC_T3:
+ targets->supported_protocols = NFC_PROTO_FELICA_MASK;
+ memcpy(targets->nfcid1, &skb->data[MICROREAD_EMCF_T3_UID], 8);
+ targets->nfcid1_len = 8;
+ break;
+ default:
+ pr_info("discard target discovered to gate 0x%x\n", gate);
+ goto exit_free;
+ }
+
+ r = nfc_targets_found(hdev->ndev, targets, 1);
+
+exit_free:
+ kfree(targets);
+
+exit:
+ kfree_skb(skb);
+
+ if (r)
+ pr_err("Failed to handle discovered target err=%d\n", r);
+}
+
+static int microread_event_received(struct nfc_hci_dev *hdev, u8 pipe,
+ u8 event, struct sk_buff *skb)
+{
+ int r;
+ u8 gate = hdev->pipes[pipe].gate;
+ u8 mode;
+
+ pr_info("Microread received event 0x%x to gate 0x%x\n", event, gate);
+
+ switch (event) {
+ case MICROREAD_EVT_MREAD_CARD_FOUND:
+ microread_target_discovered(hdev, gate, skb);
+ return 0;
+
+ case MICROREAD_EVT_P2P_INITIATOR_EXCHANGE_FROM_RF:
+ if (skb->len < 1) {
+ kfree_skb(skb);
+ return -EPROTO;
+ }
+
+ if (skb->data[skb->len - 1]) {
+ kfree_skb(skb);
+ return -EIO;
+ }
+
+ skb_trim(skb, skb->len - 1);
+
+ r = nfc_tm_data_received(hdev->ndev, skb);
+ break;
+
+ case MICROREAD_EVT_MCARD_FIELD_ON:
+ case MICROREAD_EVT_MCARD_FIELD_OFF:
+ kfree_skb(skb);
+ return 0;
+
+ case MICROREAD_EVT_P2P_TARGET_ACTIVATED:
+ r = nfc_tm_activated(hdev->ndev, NFC_PROTO_NFC_DEP_MASK,
+ NFC_COMM_PASSIVE, skb->data,
+ skb->len);
+
+ kfree_skb(skb);
+ break;
+
+ case MICROREAD_EVT_MCARD_EXCHANGE:
+ if (skb->len < 1) {
+ kfree_skb(skb);
+ return -EPROTO;
+ }
+
+ if (skb->data[skb->len-1]) {
+ kfree_skb(skb);
+ return -EIO;
+ }
+
+ skb_trim(skb, skb->len - 1);
+
+ r = nfc_tm_data_received(hdev->ndev, skb);
+ break;
+
+ case MICROREAD_EVT_P2P_TARGET_DEACTIVATED:
+ kfree_skb(skb);
+
+ mode = 0xff;
+ r = nfc_hci_set_param(hdev, MICROREAD_GATE_ID_P2P_TARGET,
+ MICROREAD_PAR_P2P_TARGET_MODE, &mode, 1);
+ if (r)
+ break;
+
+ r = nfc_hci_send_event(hdev, gate,
+ MICROREAD_EVT_MREAD_DISCOVERY_STOP, NULL,
+ 0);
+ break;
+
+ default:
+ return 1;
+ }
+
+ return r;
+}
+
+static struct nfc_hci_ops microread_hci_ops = {
+ .open = microread_open,
+ .close = microread_close,
+ .hci_ready = microread_hci_ready,
+ .xmit = microread_xmit,
+ .start_poll = microread_start_poll,
+ .dep_link_up = microread_dep_link_up,
+ .dep_link_down = microread_dep_link_down,
+ .target_from_gate = microread_target_from_gate,
+ .complete_target_discovered = microread_complete_target_discovered,
+ .im_transceive = microread_im_transceive,
+ .tm_send = microread_tm_send,
+ .check_presence = NULL,
+ .event_received = microread_event_received,
+};
+
+int microread_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name,
+ int phy_headroom, int phy_tailroom, int phy_payload,
+ struct nfc_hci_dev **hdev)
+{
+ struct microread_info *info;
+ unsigned long quirks = 0;
+ u32 protocols;
+ struct nfc_hci_init_data init_data;
+ int r;
+
+ info = kzalloc(sizeof(struct microread_info), GFP_KERNEL);
+ if (!info) {
+ r = -ENOMEM;
+ goto err_info_alloc;
+ }
+
+ info->phy_ops = phy_ops;
+ info->phy_id = phy_id;
+
+ init_data.gate_count = ARRAY_SIZE(microread_gates);
+ memcpy(init_data.gates, microread_gates, sizeof(microread_gates));
+
+ strcpy(init_data.session_id, "MICROREA");
+
+ set_bit(NFC_HCI_QUIRK_SHORT_CLEAR, &quirks);
+
+ 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;
+
+ info->hdev = nfc_hci_allocate_device(&microread_hci_ops, &init_data,
+ quirks, protocols, llc_name,
+ phy_headroom +
+ MICROREAD_CMDS_HEADROOM,
+ phy_tailroom +
+ MICROREAD_CMD_TAILROOM,
+ phy_payload);
+ if (!info->hdev) {
+ pr_err("Cannot allocate nfc hdev\n");
+ r = -ENOMEM;
+ goto err_alloc_hdev;
+ }
+
+ nfc_hci_set_clientdata(info->hdev, info);
+
+ r = nfc_hci_register_device(info->hdev);
+ if (r)
+ goto err_regdev;
+
+ *hdev = info->hdev;
+
+ return 0;
+
+err_regdev:
+ nfc_hci_free_device(info->hdev);
+
+err_alloc_hdev:
+ kfree(info);
+
+err_info_alloc:
+ return r;
+}
+EXPORT_SYMBOL(microread_probe);
+
+void microread_remove(struct nfc_hci_dev *hdev)
+{
+ struct microread_info *info = nfc_hci_get_clientdata(hdev);
+
+ nfc_hci_unregister_device(hdev);
+ nfc_hci_free_device(hdev);
+ kfree(info);
+}
+EXPORT_SYMBOL(microread_remove);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/nfc/microread/microread.h b/drivers/nfc/microread/microread.h
new file mode 100644
index 0000000..f538641
--- /dev/null
+++ b/drivers/nfc/microread/microread.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2011 - 2012 Intel Corporation. All rights reserved.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LOCAL_MICROREAD_H_
+#define __LOCAL_MICROREAD_H_
+
+#include <net/nfc/hci.h>
+
+#define DRIVER_DESC "NFC driver for microread"
+
+int microread_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name,
+ int phy_headroom, int phy_tailroom, int phy_payload,
+ struct nfc_hci_dev **hdev);
+
+void microread_remove(struct nfc_hci_dev *hdev);
+
+#endif /* __LOCAL_MICROREAD_H_ */
diff --git a/drivers/nfc/nfcmrvl/Kconfig b/drivers/nfc/nfcmrvl/Kconfig
new file mode 100644
index 0000000..b40533f
--- /dev/null
+++ b/drivers/nfc/nfcmrvl/Kconfig
@@ -0,0 +1,62 @@
+config NFC_MRVL
+ depends on !KERNEL_3_3
+ tristate
+ depends on m
+ 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
+ depends on !KERNEL_3_3
+ tristate "Marvell NFC-over-USB driver"
+ depends on m
+ 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
+ depends on !KERNEL_3_3
+ tristate "Marvell NFC-over-UART driver"
+ depends on m
+ 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 m
+ 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 m
+ 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/drivers/nfc/nfcmrvl/Makefile b/drivers/nfc/nfcmrvl/Makefile
new file mode 100644
index 0000000..f503220
--- /dev/null
+++ b/drivers/nfc/nfcmrvl/Makefile
@@ -0,0 +1,18 @@
+#
+# Makefile for NFCMRVL NCI based NFC driver
+#
+
+nfcmrvl-y += main.o fw_dnld.o
+obj-$(CPTCFG_NFC_MRVL) += nfcmrvl.o
+
+nfcmrvl_usb-y += usb.o
+obj-$(CPTCFG_NFC_MRVL_USB) += nfcmrvl_usb.o
+
+nfcmrvl_uart-y += uart.o
+obj-$(CPTCFG_NFC_MRVL_UART) += nfcmrvl_uart.o
+
+nfcmrvl_i2c-y += i2c.o
+obj-$(CPTCFG_NFC_MRVL_I2C) += nfcmrvl_i2c.o
+
+nfcmrvl_spi-y += spi.o
+obj-$(CPTCFG_NFC_MRVL_SPI) += nfcmrvl_spi.o
diff --git a/drivers/nfc/nfcmrvl/fw_dnld.c b/drivers/nfc/nfcmrvl/fw_dnld.c
new file mode 100644
index 0000000..f8dcdf4
--- /dev/null
+++ b/drivers/nfc/nfcmrvl/fw_dnld.c
@@ -0,0 +1,559 @@
+/*
+ * 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 <linux/unaligned/access_ok.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 = (struct nci_data_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);
+ memcpy(&len, skb->data, 2);
+ skb_pull(skb, 2);
+ memcpy(&comp_len, skb->data, 2);
+ skb_pull(skb, 2);
+ len = get_unaligned_le16(&len);
+ comp_len = get_unaligned_le16(&comp_len);
+ 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(out_skb, 1) = 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(out_skb, 1) = 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;
+ memcpy(skb_put(out_skb, priv->fw_dnld.chunk_len),
+ ((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->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;
+
+ 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 */
+ if (request_firmware(&fw_dnld->fw, firmware_name, priv->dev) < 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/drivers/nfc/nfcmrvl/fw_dnld.h b/drivers/nfc/nfcmrvl/fw_dnld.h
new file mode 100644
index 0000000..ee4a339
--- /dev/null
+++ b/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/drivers/nfc/nfcmrvl/i2c.c b/drivers/nfc/nfcmrvl/i2c.c
new file mode 100644
index 0000000..78b7aa8
--- /dev/null
+++ b/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 */
+ memcpy(skb_put(*skb, NCI_CTRL_HDR_SIZE), &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/drivers/nfc/nfcmrvl/main.c b/drivers/nfc/nfcmrvl/main.c
new file mode 100644
index 0000000..51c8240
--- /dev/null
+++ b/drivers/nfc/nfcmrvl/main.c
@@ -0,0 +1,283 @@
+/*
+ * 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 = (char *) 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 (priv->config.reset_n_io) {
+ rc = devm_gpio_request_one(dev,
+ priv->config.reset_n_io,
+ GPIOF_OUT_INIT_LOW,
+ "nfcmrvl_reset_n");
+ if (rc < 0)
+ 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;
+ }
+
+ 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_free_dev;
+ }
+
+ /* Ensure that controller is powered off */
+ nfcmrvl_chip_halt(priv);
+
+ rc = nfcmrvl_fw_dnld_init(priv);
+ if (rc) {
+ nfc_err(dev, "failed to initialize FW download %d\n", rc);
+ goto error_free_dev;
+ }
+
+ nfc_info(dev, "registered with nci successfully\n");
+ return priv;
+
+error_free_dev:
+ nci_free_device(priv->ndev);
+error:
+ 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 (priv->config.reset_n_io)
+ devm_gpio_free(priv->dev, 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 (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 (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");
+ reset_n_io = 0;
+ } 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/drivers/nfc/nfcmrvl/nfcmrvl.h b/drivers/nfc/nfcmrvl/nfcmrvl.h
new file mode 100644
index 0000000..de68ff4
--- /dev/null
+++ b/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/drivers/nfc/nfcmrvl/spi.c b/drivers/nfc/nfcmrvl/spi.c
new file mode 100644
index 0000000..a7faa0b
--- /dev/null
+++ b/drivers/nfc/nfcmrvl/spi.c
@@ -0,0 +1,228 @@
+/**
+ * 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 <linux/gpio.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 != 0) {
+ nfc_err(priv->dev, "spi_send failed %d", err);
+ kfree_skb(skb);
+ }
+ 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/drivers/nfc/nfcmrvl/uart.c b/drivers/nfc/nfcmrvl/uart.c
new file mode 100644
index 0000000..83a99e3
--- /dev/null
+++ b/drivers/nfc/nfcmrvl/uart.c
@@ -0,0 +1,233 @@
+/**
+ * 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 unsigned int reset_n_io;
+
+/*
+** 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_find_compatible_node(node, NULL, "marvell,nfc-uart");
+ if (!matched_node) {
+ matched_node = of_find_compatible_node(node, NULL,
+ "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");
+ 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;
+
+ 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;
+
+ /*
+ * 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 (nu->tty->dev->parent && nu->tty->dev->parent->of_node)
+ if (nfcmrvl_uart_parse_dt(nu->tty->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,
+ nu->tty->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, uint, 0);
+MODULE_PARM_DESC(reset_n_io, "GPIO that is wired to RESET_N signal.");
diff --git a/drivers/nfc/nfcmrvl/usb.c b/drivers/nfc/nfcmrvl/usb.c
new file mode 100644
index 0000000..585a0f2
--- /dev/null
+++ b/drivers/nfc/nfcmrvl/usb.c
@@ -0,0 +1,472 @@
+/**
+ * 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 {
+ memcpy(skb_put(skb, urb->actual_length),
+ 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));
+
+ 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,
+ &drv_data->udev->dev, &config);
+ if (IS_ERR(priv))
+ return PTR_ERR(priv);
+
+ drv_data->priv = priv;
+ drv_data->priv->support_fw_dnld = false;
+
+ priv->dev = &drv_data->udev->dev;
+
+ 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");
diff --git a/drivers/nfc/nfcsim.c b/drivers/nfc/nfcsim.c
new file mode 100644
index 0000000..a466e79
--- /dev/null
+++ b/drivers/nfc/nfcsim.c
@@ -0,0 +1,512 @@
+/*
+ * NFC hardware simulation driver
+ * Copyright (c) 2013, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/ctype.h>
+#include <linux/debugfs.h>
+#include <linux/nfc.h>
+#include <net/nfc/nfc.h>
+#include <net/nfc/digital.h>
+
+#define NFCSIM_ERR(d, fmt, args...) nfc_err(&d->nfc_digital_dev->nfc_dev->dev, \
+ "%s: " fmt, __func__, ## args)
+
+#define NFCSIM_DBG(d, fmt, args...) dev_dbg(&d->nfc_digital_dev->nfc_dev->dev, \
+ "%s: " fmt, __func__, ## args)
+
+#define NFCSIM_VERSION "0.2"
+
+#define NFCSIM_MODE_NONE 0
+#define NFCSIM_MODE_INITIATOR 1
+#define NFCSIM_MODE_TARGET 2
+
+#define NFCSIM_CAPABILITIES (NFC_DIGITAL_DRV_CAPS_IN_CRC | \
+ NFC_DIGITAL_DRV_CAPS_TG_CRC)
+
+struct nfcsim {
+ struct nfc_digital_dev *nfc_digital_dev;
+
+ struct work_struct recv_work;
+ struct delayed_work send_work;
+
+ struct nfcsim_link *link_in;
+ struct nfcsim_link *link_out;
+
+ bool up;
+ u8 mode;
+ u8 rf_tech;
+
+ u16 recv_timeout;
+
+ nfc_digital_cmd_complete_t cb;
+ void *arg;
+
+ u8 dropframe;
+};
+
+struct nfcsim_link {
+ struct mutex lock;
+
+ u8 rf_tech;
+ u8 mode;
+
+ u8 shutdown;
+
+ struct sk_buff *skb;
+ wait_queue_head_t recv_wait;
+ u8 cond;
+};
+
+static struct nfcsim_link *nfcsim_link_new(void)
+{
+ struct nfcsim_link *link;
+
+ link = kzalloc(sizeof(struct nfcsim_link), GFP_KERNEL);
+ if (!link)
+ return NULL;
+
+ mutex_init(&link->lock);
+ init_waitqueue_head(&link->recv_wait);
+
+ return link;
+}
+
+static void nfcsim_link_free(struct nfcsim_link *link)
+{
+ dev_kfree_skb(link->skb);
+ kfree(link);
+}
+
+static void nfcsim_link_recv_wake(struct nfcsim_link *link)
+{
+ link->cond = 1;
+ wake_up_interruptible(&link->recv_wait);
+}
+
+static void nfcsim_link_set_skb(struct nfcsim_link *link, struct sk_buff *skb,
+ u8 rf_tech, u8 mode)
+{
+ mutex_lock(&link->lock);
+
+ dev_kfree_skb(link->skb);
+ link->skb = skb;
+ link->rf_tech = rf_tech;
+ link->mode = mode;
+
+ mutex_unlock(&link->lock);
+}
+
+static void nfcsim_link_recv_cancel(struct nfcsim_link *link)
+{
+ mutex_lock(&link->lock);
+
+ link->mode = NFCSIM_MODE_NONE;
+
+ mutex_unlock(&link->lock);
+
+ nfcsim_link_recv_wake(link);
+}
+
+static void nfcsim_link_shutdown(struct nfcsim_link *link)
+{
+ mutex_lock(&link->lock);
+
+ link->shutdown = 1;
+ link->mode = NFCSIM_MODE_NONE;
+
+ mutex_unlock(&link->lock);
+
+ nfcsim_link_recv_wake(link);
+}
+
+static struct sk_buff *nfcsim_link_recv_skb(struct nfcsim_link *link,
+ int timeout, u8 rf_tech, u8 mode)
+{
+ int rc;
+ struct sk_buff *skb;
+
+ rc = wait_event_interruptible_timeout(link->recv_wait,
+ link->cond,
+ msecs_to_jiffies(timeout));
+
+ mutex_lock(&link->lock);
+
+ skb = link->skb;
+ link->skb = NULL;
+
+ if (!rc) {
+ rc = -ETIMEDOUT;
+ goto done;
+ }
+
+ if (!skb || link->rf_tech != rf_tech || link->mode == mode) {
+ rc = -EINVAL;
+ goto done;
+ }
+
+ if (link->shutdown) {
+ rc = -ENODEV;
+ goto done;
+ }
+
+done:
+ mutex_unlock(&link->lock);
+
+ if (rc < 0) {
+ dev_kfree_skb(skb);
+ skb = ERR_PTR(rc);
+ }
+
+ link->cond = 0;
+
+ return skb;
+}
+
+static void nfcsim_send_wq(struct work_struct *work)
+{
+ struct nfcsim *dev = container_of(work, struct nfcsim, send_work.work);
+
+ /*
+ * To effectively send data, the device just wake up its link_out which
+ * is the link_in of the peer device. The exchanged skb has already been
+ * stored in the dev->link_out through nfcsim_link_set_skb().
+ */
+ nfcsim_link_recv_wake(dev->link_out);
+}
+
+static void nfcsim_recv_wq(struct work_struct *work)
+{
+ struct nfcsim *dev = container_of(work, struct nfcsim, recv_work);
+ struct sk_buff *skb;
+
+ skb = nfcsim_link_recv_skb(dev->link_in, dev->recv_timeout,
+ dev->rf_tech, dev->mode);
+
+ if (!dev->up) {
+ NFCSIM_ERR(dev, "Device is down\n");
+
+ if (!IS_ERR(skb))
+ dev_kfree_skb(skb);
+
+ skb = ERR_PTR(-ENODEV);
+ }
+
+ dev->cb(dev->nfc_digital_dev, dev->arg, skb);
+}
+
+static int nfcsim_send(struct nfc_digital_dev *ddev, struct sk_buff *skb,
+ u16 timeout, nfc_digital_cmd_complete_t cb, void *arg)
+{
+ struct nfcsim *dev = nfc_digital_get_drvdata(ddev);
+ u8 delay;
+
+ if (!dev->up) {
+ NFCSIM_ERR(dev, "Device is down\n");
+ return -ENODEV;
+ }
+
+ dev->recv_timeout = timeout;
+ dev->cb = cb;
+ dev->arg = arg;
+
+ schedule_work(&dev->recv_work);
+
+ if (dev->dropframe) {
+ NFCSIM_DBG(dev, "dropping frame (out of %d)\n", dev->dropframe);
+ dev_kfree_skb(skb);
+ dev->dropframe--;
+
+ return 0;
+ }
+
+ if (skb) {
+ nfcsim_link_set_skb(dev->link_out, skb, dev->rf_tech,
+ dev->mode);
+
+ /* Add random delay (between 3 and 10 ms) before sending data */
+ get_random_bytes(&delay, 1);
+ delay = 3 + (delay & 0x07);
+
+ schedule_delayed_work(&dev->send_work, msecs_to_jiffies(delay));
+ }
+
+ return 0;
+}
+
+static void nfcsim_abort_cmd(struct nfc_digital_dev *ddev)
+{
+ struct nfcsim *dev = nfc_digital_get_drvdata(ddev);
+
+ nfcsim_link_recv_cancel(dev->link_in);
+}
+
+static int nfcsim_switch_rf(struct nfc_digital_dev *ddev, bool on)
+{
+ struct nfcsim *dev = nfc_digital_get_drvdata(ddev);
+
+ dev->up = on;
+
+ return 0;
+}
+
+static int nfcsim_in_configure_hw(struct nfc_digital_dev *ddev,
+ int type, int param)
+{
+ struct nfcsim *dev = nfc_digital_get_drvdata(ddev);
+
+ switch (type) {
+ case NFC_DIGITAL_CONFIG_RF_TECH:
+ dev->up = true;
+ dev->mode = NFCSIM_MODE_INITIATOR;
+ dev->rf_tech = param;
+ break;
+
+ case NFC_DIGITAL_CONFIG_FRAMING:
+ break;
+
+ default:
+ NFCSIM_ERR(dev, "Invalid configuration type: %d\n", type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int nfcsim_in_send_cmd(struct nfc_digital_dev *ddev,
+ struct sk_buff *skb, u16 timeout,
+ nfc_digital_cmd_complete_t cb, void *arg)
+{
+ return nfcsim_send(ddev, skb, timeout, cb, arg);
+}
+
+static int nfcsim_tg_configure_hw(struct nfc_digital_dev *ddev,
+ int type, int param)
+{
+ struct nfcsim *dev = nfc_digital_get_drvdata(ddev);
+
+ switch (type) {
+ case NFC_DIGITAL_CONFIG_RF_TECH:
+ dev->up = true;
+ dev->mode = NFCSIM_MODE_TARGET;
+ dev->rf_tech = param;
+ break;
+
+ case NFC_DIGITAL_CONFIG_FRAMING:
+ break;
+
+ default:
+ NFCSIM_ERR(dev, "Invalid configuration type: %d\n", type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int nfcsim_tg_send_cmd(struct nfc_digital_dev *ddev,
+ struct sk_buff *skb, u16 timeout,
+ nfc_digital_cmd_complete_t cb, void *arg)
+{
+ return nfcsim_send(ddev, skb, timeout, cb, arg);
+}
+
+static int nfcsim_tg_listen(struct nfc_digital_dev *ddev, u16 timeout,
+ nfc_digital_cmd_complete_t cb, void *arg)
+{
+ return nfcsim_send(ddev, NULL, timeout, cb, arg);
+}
+
+static struct nfc_digital_ops nfcsim_digital_ops = {
+ .in_configure_hw = nfcsim_in_configure_hw,
+ .in_send_cmd = nfcsim_in_send_cmd,
+
+ .tg_listen = nfcsim_tg_listen,
+ .tg_configure_hw = nfcsim_tg_configure_hw,
+ .tg_send_cmd = nfcsim_tg_send_cmd,
+
+ .abort_cmd = nfcsim_abort_cmd,
+ .switch_rf = nfcsim_switch_rf,
+};
+
+static struct dentry *nfcsim_debugfs_root;
+
+static void nfcsim_debugfs_init(void)
+{
+ nfcsim_debugfs_root = debugfs_create_dir("nfcsim", NULL);
+
+ if (!nfcsim_debugfs_root)
+ pr_err("Could not create debugfs entry\n");
+
+}
+
+static void nfcsim_debugfs_remove(void)
+{
+ debugfs_remove_recursive(nfcsim_debugfs_root);
+}
+
+static void nfcsim_debugfs_init_dev(struct nfcsim *dev)
+{
+ struct dentry *dev_dir;
+ char devname[5]; /* nfcX\0 */
+ u32 idx;
+ int n;
+
+ if (!nfcsim_debugfs_root) {
+ NFCSIM_ERR(dev, "nfcsim debugfs not initialized\n");
+ return;
+ }
+
+ idx = dev->nfc_digital_dev->nfc_dev->idx;
+ n = snprintf(devname, sizeof(devname), "nfc%d", idx);
+ if (n >= sizeof(devname)) {
+ NFCSIM_ERR(dev, "Could not compute dev name for dev %d\n", idx);
+ return;
+ }
+
+ dev_dir = debugfs_create_dir(devname, nfcsim_debugfs_root);
+ if (!dev_dir) {
+ NFCSIM_ERR(dev, "Could not create debugfs entries for nfc%d\n",
+ idx);
+ return;
+ }
+
+ debugfs_create_u8("dropframe", 0664, dev_dir, &dev->dropframe);
+}
+
+static struct nfcsim *nfcsim_device_new(struct nfcsim_link *link_in,
+ struct nfcsim_link *link_out)
+{
+ struct nfcsim *dev;
+ int rc;
+
+ dev = kzalloc(sizeof(struct nfcsim), GFP_KERNEL);
+ if (!dev)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_DELAYED_WORK(&dev->send_work, nfcsim_send_wq);
+ INIT_WORK(&dev->recv_work, nfcsim_recv_wq);
+
+ dev->nfc_digital_dev =
+ nfc_digital_allocate_device(&nfcsim_digital_ops,
+ NFC_PROTO_NFC_DEP_MASK,
+ NFCSIM_CAPABILITIES,
+ 0, 0);
+ if (!dev->nfc_digital_dev) {
+ kfree(dev);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ nfc_digital_set_drvdata(dev->nfc_digital_dev, dev);
+
+ dev->link_in = link_in;
+ dev->link_out = link_out;
+
+ rc = nfc_digital_register_device(dev->nfc_digital_dev);
+ if (rc) {
+ pr_err("Could not register digital device (%d)\n", rc);
+ nfc_digital_free_device(dev->nfc_digital_dev);
+ kfree(dev);
+
+ return ERR_PTR(rc);
+ }
+
+ nfcsim_debugfs_init_dev(dev);
+
+ return dev;
+}
+
+static void nfcsim_device_free(struct nfcsim *dev)
+{
+ nfc_digital_unregister_device(dev->nfc_digital_dev);
+
+ dev->up = false;
+
+ nfcsim_link_shutdown(dev->link_in);
+
+ cancel_delayed_work_sync(&dev->send_work);
+ cancel_work_sync(&dev->recv_work);
+
+ nfc_digital_free_device(dev->nfc_digital_dev);
+
+ kfree(dev);
+}
+
+static struct nfcsim *dev0;
+static struct nfcsim *dev1;
+
+static int __init nfcsim_init(void)
+{
+ struct nfcsim_link *link0, *link1;
+ int rc;
+
+ link0 = nfcsim_link_new();
+ link1 = nfcsim_link_new();
+ if (!link0 || !link1) {
+ rc = -ENOMEM;
+ goto exit_err;
+ }
+
+ nfcsim_debugfs_init();
+
+ dev0 = nfcsim_device_new(link0, link1);
+ if (IS_ERR(dev0)) {
+ rc = PTR_ERR(dev0);
+ goto exit_err;
+ }
+
+ dev1 = nfcsim_device_new(link1, link0);
+ if (IS_ERR(dev1)) {
+ nfcsim_device_free(dev0);
+
+ rc = PTR_ERR(dev1);
+ goto exit_err;
+ }
+
+ pr_info("nfcsim " NFCSIM_VERSION " initialized\n");
+
+ return 0;
+
+exit_err:
+ pr_err("Failed to initialize nfcsim driver (%d)\n", rc);
+
+ nfcsim_link_free(link0);
+ nfcsim_link_free(link1);
+
+ return rc;
+}
+
+static void __exit nfcsim_exit(void)
+{
+ struct nfcsim_link *link0, *link1;
+
+ link0 = dev0->link_in;
+ link1 = dev0->link_out;
+
+ nfcsim_device_free(dev0);
+ nfcsim_device_free(dev1);
+
+ nfcsim_link_free(link0);
+ nfcsim_link_free(link1);
+
+ nfcsim_debugfs_remove();
+}
+
+module_init(nfcsim_init);
+module_exit(nfcsim_exit);
+
+MODULE_DESCRIPTION("NFCSim driver ver " NFCSIM_VERSION);
+MODULE_VERSION(NFCSIM_VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/nfc/nfcwilink.c b/drivers/nfc/nfcwilink.c
new file mode 100644
index 0000000..3fbd18b
--- /dev/null
+++ b/drivers/nfc/nfcwilink.c
@@ -0,0 +1,578 @@
+/*
+ * Texas Instrument's NFC Driver For Shared Transport.
+ *
+ * NFC Driver acts as interface between NCI core and
+ * TI Shared Transport Layer.
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ *
+ * Written by Ilan Elias <ilane@ti.com>
+ *
+ * Acknowledgements:
+ * This file is based on btwilink.c, which was written
+ * by Raja Mani and Pavan Savoy.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/firmware.h>
+#include <linux/nfc.h>
+#include <net/nfc/nci.h>
+#include <net/nfc/nci_core.h>
+#include <linux/ti_wilink_st.h>
+
+#define NFCWILINK_CHNL 12
+#define NFCWILINK_OPCODE 7
+#define NFCWILINK_MAX_FRAME_SIZE 300
+#define NFCWILINK_HDR_LEN 4
+#define NFCWILINK_OFFSET_LEN_IN_HDR 1
+#define NFCWILINK_LEN_SIZE 2
+#define NFCWILINK_REGISTER_TIMEOUT 8000 /* 8 sec */
+#define NFCWILINK_CMD_TIMEOUT 5000 /* 5 sec */
+
+#define BTS_FILE_NAME_MAX_SIZE 40
+#define BTS_FILE_HDR_MAGIC 0x42535442
+#define BTS_FILE_CMD_MAX_LEN 0xff
+#define BTS_FILE_ACTION_TYPE_SEND_CMD 1
+
+#define NCI_VS_NFCC_INFO_CMD_GID 0x2f
+#define NCI_VS_NFCC_INFO_CMD_OID 0x12
+#define NCI_VS_NFCC_INFO_RSP_GID 0x4f
+#define NCI_VS_NFCC_INFO_RSP_OID 0x12
+
+struct nfcwilink_hdr {
+ __u8 chnl;
+ __u8 opcode;
+ __le16 len;
+} __packed;
+
+struct nci_vs_nfcc_info_cmd {
+ __u8 gid;
+ __u8 oid;
+ __u8 plen;
+} __packed;
+
+struct nci_vs_nfcc_info_rsp {
+ __u8 gid;
+ __u8 oid;
+ __u8 plen;
+ __u8 status;
+ __u8 hw_id;
+ __u8 sw_ver_x;
+ __u8 sw_ver_z;
+ __u8 patch_id;
+} __packed;
+
+struct bts_file_hdr {
+ __le32 magic;
+ __le32 ver;
+ __u8 rfu[24];
+ __u8 actions[0];
+} __packed;
+
+struct bts_file_action {
+ __le16 type;
+ __le16 len;
+ __u8 data[0];
+} __packed;
+
+struct nfcwilink {
+ struct platform_device *pdev;
+ struct nci_dev *ndev;
+ unsigned long flags;
+
+ int st_register_cb_status;
+ long (*st_write) (struct sk_buff *);
+
+ struct completion completed;
+
+ struct nci_vs_nfcc_info_rsp nfcc_info;
+};
+
+/* NFCWILINK driver flags */
+enum {
+ NFCWILINK_RUNNING,
+ NFCWILINK_FW_DOWNLOAD,
+};
+
+static int nfcwilink_send(struct nci_dev *ndev, struct sk_buff *skb);
+
+static inline struct sk_buff *nfcwilink_skb_alloc(unsigned int len, gfp_t how)
+{
+ struct sk_buff *skb;
+
+ skb = alloc_skb(len + NFCWILINK_HDR_LEN, how);
+ if (skb)
+ skb_reserve(skb, NFCWILINK_HDR_LEN);
+
+ return skb;
+}
+
+static void nfcwilink_fw_download_receive(struct nfcwilink *drv,
+ struct sk_buff *skb)
+{
+ struct nci_vs_nfcc_info_rsp *rsp = (void *)skb->data;
+
+ /* Detect NCI_VS_NFCC_INFO_RSP and store the result */
+ if ((skb->len > 3) && (rsp->gid == NCI_VS_NFCC_INFO_RSP_GID) &&
+ (rsp->oid == NCI_VS_NFCC_INFO_RSP_OID)) {
+ memcpy(&drv->nfcc_info, rsp,
+ sizeof(struct nci_vs_nfcc_info_rsp));
+ }
+
+ kfree_skb(skb);
+
+ complete(&drv->completed);
+}
+
+static int nfcwilink_get_bts_file_name(struct nfcwilink *drv, char *file_name)
+{
+ struct nci_vs_nfcc_info_cmd *cmd;
+ struct sk_buff *skb;
+ unsigned long comp_ret;
+ int rc;
+
+ skb = nfcwilink_skb_alloc(sizeof(struct nci_vs_nfcc_info_cmd),
+ GFP_KERNEL);
+ if (!skb) {
+ nfc_err(&drv->pdev->dev,
+ "no memory for nci_vs_nfcc_info_cmd\n");
+ return -ENOMEM;
+ }
+
+ cmd = (struct nci_vs_nfcc_info_cmd *)
+ skb_put(skb, sizeof(struct nci_vs_nfcc_info_cmd));
+ cmd->gid = NCI_VS_NFCC_INFO_CMD_GID;
+ cmd->oid = NCI_VS_NFCC_INFO_CMD_OID;
+ cmd->plen = 0;
+
+ drv->nfcc_info.plen = 0;
+
+ rc = nfcwilink_send(drv->ndev, skb);
+ if (rc)
+ return rc;
+
+ comp_ret = wait_for_completion_timeout(&drv->completed,
+ msecs_to_jiffies(NFCWILINK_CMD_TIMEOUT));
+ dev_dbg(&drv->pdev->dev, "wait_for_completion_timeout returned %ld\n",
+ comp_ret);
+ if (comp_ret == 0) {
+ nfc_err(&drv->pdev->dev,
+ "timeout on wait_for_completion_timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ dev_dbg(&drv->pdev->dev, "nci_vs_nfcc_info_rsp: plen %d, status %d\n",
+ drv->nfcc_info.plen, drv->nfcc_info.status);
+
+ if ((drv->nfcc_info.plen != 5) || (drv->nfcc_info.status != 0)) {
+ nfc_err(&drv->pdev->dev, "invalid nci_vs_nfcc_info_rsp\n");
+ return -EINVAL;
+ }
+
+ snprintf(file_name, BTS_FILE_NAME_MAX_SIZE,
+ "TINfcInit_%d.%d.%d.%d.bts",
+ drv->nfcc_info.hw_id,
+ drv->nfcc_info.sw_ver_x,
+ drv->nfcc_info.sw_ver_z,
+ drv->nfcc_info.patch_id);
+
+ nfc_info(&drv->pdev->dev, "nfcwilink FW file name: %s\n", file_name);
+
+ return 0;
+}
+
+static int nfcwilink_send_bts_cmd(struct nfcwilink *drv, __u8 *data, int len)
+{
+ struct nfcwilink_hdr *hdr = (struct nfcwilink_hdr *)data;
+ struct sk_buff *skb;
+ unsigned long comp_ret;
+ int rc;
+
+ /* verify valid cmd for the NFC channel */
+ if ((len <= sizeof(struct nfcwilink_hdr)) ||
+ (len > BTS_FILE_CMD_MAX_LEN) ||
+ (hdr->chnl != NFCWILINK_CHNL) ||
+ (hdr->opcode != NFCWILINK_OPCODE)) {
+ nfc_err(&drv->pdev->dev,
+ "ignoring invalid bts cmd, len %d, chnl %d, opcode %d\n",
+ len, hdr->chnl, hdr->opcode);
+ return 0;
+ }
+
+ /* remove the ST header */
+ len -= sizeof(struct nfcwilink_hdr);
+ data += sizeof(struct nfcwilink_hdr);
+
+ skb = nfcwilink_skb_alloc(len, GFP_KERNEL);
+ if (!skb) {
+ nfc_err(&drv->pdev->dev, "no memory for bts cmd\n");
+ return -ENOMEM;
+ }
+
+ memcpy(skb_put(skb, len), data, len);
+
+ rc = nfcwilink_send(drv->ndev, skb);
+ if (rc)
+ return rc;
+
+ comp_ret = wait_for_completion_timeout(&drv->completed,
+ msecs_to_jiffies(NFCWILINK_CMD_TIMEOUT));
+ dev_dbg(&drv->pdev->dev, "wait_for_completion_timeout returned %ld\n",
+ comp_ret);
+ if (comp_ret == 0) {
+ nfc_err(&drv->pdev->dev,
+ "timeout on wait_for_completion_timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int nfcwilink_download_fw(struct nfcwilink *drv)
+{
+ unsigned char file_name[BTS_FILE_NAME_MAX_SIZE];
+ const struct firmware *fw;
+ __u16 action_type, action_len;
+ __u8 *ptr;
+ int len, rc;
+
+ set_bit(NFCWILINK_FW_DOWNLOAD, &drv->flags);
+
+ rc = nfcwilink_get_bts_file_name(drv, file_name);
+ if (rc)
+ goto exit;
+
+ rc = request_firmware(&fw, file_name, &drv->pdev->dev);
+ if (rc) {
+ nfc_err(&drv->pdev->dev, "request_firmware failed %d\n", rc);
+
+ /* if the file is not found, don't exit with failure */
+ if (rc == -ENOENT)
+ rc = 0;
+
+ goto exit;
+ }
+
+ len = fw->size;
+ ptr = (__u8 *)fw->data;
+
+ if ((len == 0) || (ptr == NULL)) {
+ dev_dbg(&drv->pdev->dev,
+ "request_firmware returned size %d\n", len);
+ goto release_fw;
+ }
+
+ if (__le32_to_cpu(((struct bts_file_hdr *)ptr)->magic) !=
+ BTS_FILE_HDR_MAGIC) {
+ nfc_err(&drv->pdev->dev, "wrong bts magic number\n");
+ rc = -EINVAL;
+ goto release_fw;
+ }
+
+ /* remove the BTS header */
+ len -= sizeof(struct bts_file_hdr);
+ ptr += sizeof(struct bts_file_hdr);
+
+ while (len > 0) {
+ action_type =
+ __le16_to_cpu(((struct bts_file_action *)ptr)->type);
+ action_len =
+ __le16_to_cpu(((struct bts_file_action *)ptr)->len);
+
+ dev_dbg(&drv->pdev->dev, "bts_file_action type %d, len %d\n",
+ action_type, action_len);
+
+ switch (action_type) {
+ case BTS_FILE_ACTION_TYPE_SEND_CMD:
+ rc = nfcwilink_send_bts_cmd(drv,
+ ((struct bts_file_action *)ptr)->data,
+ action_len);
+ if (rc)
+ goto release_fw;
+ break;
+ }
+
+ /* advance to the next action */
+ len -= (sizeof(struct bts_file_action) + action_len);
+ ptr += (sizeof(struct bts_file_action) + action_len);
+ }
+
+release_fw:
+ release_firmware(fw);
+
+exit:
+ clear_bit(NFCWILINK_FW_DOWNLOAD, &drv->flags);
+ return rc;
+}
+
+/* Called by ST when registration is complete */
+static void nfcwilink_register_complete(void *priv_data, int data)
+{
+ struct nfcwilink *drv = priv_data;
+
+ /* store ST registration status */
+ drv->st_register_cb_status = data;
+
+ /* complete the wait in nfc_st_open() */
+ complete(&drv->completed);
+}
+
+/* Called by ST when receive data is available */
+static long nfcwilink_receive(void *priv_data, struct sk_buff *skb)
+{
+ struct nfcwilink *drv = priv_data;
+ int rc;
+
+ if (!skb)
+ return -EFAULT;
+
+ if (!drv) {
+ kfree_skb(skb);
+ return -EFAULT;
+ }
+
+ dev_dbg(&drv->pdev->dev, "receive entry, len %d\n", skb->len);
+
+ /* strip the ST header
+ (apart for the chnl byte, which is not received in the hdr) */
+ skb_pull(skb, (NFCWILINK_HDR_LEN-1));
+
+ if (test_bit(NFCWILINK_FW_DOWNLOAD, &drv->flags)) {
+ nfcwilink_fw_download_receive(drv, skb);
+ return 0;
+ }
+
+ /* Forward skb to NCI core layer */
+ rc = nci_recv_frame(drv->ndev, skb);
+ if (rc < 0) {
+ nfc_err(&drv->pdev->dev, "nci_recv_frame failed %d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+/* protocol structure registered with ST */
+static struct st_proto_s nfcwilink_proto = {
+ .chnl_id = NFCWILINK_CHNL,
+ .max_frame_size = NFCWILINK_MAX_FRAME_SIZE,
+ .hdr_len = (NFCWILINK_HDR_LEN-1), /* not including chnl byte */
+ .offset_len_in_hdr = NFCWILINK_OFFSET_LEN_IN_HDR,
+ .len_size = NFCWILINK_LEN_SIZE,
+ .reserve = 0,
+ .recv = nfcwilink_receive,
+ .reg_complete_cb = nfcwilink_register_complete,
+ .write = NULL,
+};
+
+static int nfcwilink_open(struct nci_dev *ndev)
+{
+ struct nfcwilink *drv = nci_get_drvdata(ndev);
+ unsigned long comp_ret;
+ int rc;
+
+ if (test_and_set_bit(NFCWILINK_RUNNING, &drv->flags)) {
+ rc = -EBUSY;
+ goto exit;
+ }
+
+ nfcwilink_proto.priv_data = drv;
+
+ init_completion(&drv->completed);
+ drv->st_register_cb_status = -EINPROGRESS;
+
+ rc = st_register(&nfcwilink_proto);
+ if (rc < 0) {
+ if (rc == -EINPROGRESS) {
+ comp_ret = wait_for_completion_timeout(
+ &drv->completed,
+ msecs_to_jiffies(NFCWILINK_REGISTER_TIMEOUT));
+
+ dev_dbg(&drv->pdev->dev,
+ "wait_for_completion_timeout returned %ld\n",
+ comp_ret);
+
+ if (comp_ret == 0) {
+ /* timeout */
+ rc = -ETIMEDOUT;
+ goto clear_exit;
+ } else if (drv->st_register_cb_status != 0) {
+ rc = drv->st_register_cb_status;
+ nfc_err(&drv->pdev->dev,
+ "st_register_cb failed %d\n", rc);
+ goto clear_exit;
+ }
+ } else {
+ nfc_err(&drv->pdev->dev, "st_register failed %d\n", rc);
+ goto clear_exit;
+ }
+ }
+
+ /* st_register MUST fill the write callback */
+ BUG_ON(nfcwilink_proto.write == NULL);
+ drv->st_write = nfcwilink_proto.write;
+
+ if (nfcwilink_download_fw(drv)) {
+ nfc_err(&drv->pdev->dev, "nfcwilink_download_fw failed %d\n",
+ rc);
+ /* open should succeed, even if the FW download failed */
+ }
+
+ goto exit;
+
+clear_exit:
+ clear_bit(NFCWILINK_RUNNING, &drv->flags);
+
+exit:
+ return rc;
+}
+
+static int nfcwilink_close(struct nci_dev *ndev)
+{
+ struct nfcwilink *drv = nci_get_drvdata(ndev);
+ int rc;
+
+ if (!test_and_clear_bit(NFCWILINK_RUNNING, &drv->flags))
+ return 0;
+
+ rc = st_unregister(&nfcwilink_proto);
+ if (rc)
+ nfc_err(&drv->pdev->dev, "st_unregister failed %d\n", rc);
+
+ drv->st_write = NULL;
+
+ return rc;
+}
+
+static int nfcwilink_send(struct nci_dev *ndev, struct sk_buff *skb)
+{
+ struct nfcwilink *drv = nci_get_drvdata(ndev);
+ struct nfcwilink_hdr hdr = {NFCWILINK_CHNL, NFCWILINK_OPCODE, 0x0000};
+ long len;
+
+ dev_dbg(&drv->pdev->dev, "send entry, len %d\n", skb->len);
+
+ if (!test_bit(NFCWILINK_RUNNING, &drv->flags)) {
+ kfree_skb(skb);
+ return -EINVAL;
+ }
+
+ /* add the ST hdr to the start of the buffer */
+ hdr.len = cpu_to_le16(skb->len);
+ memcpy(skb_push(skb, NFCWILINK_HDR_LEN), &hdr, NFCWILINK_HDR_LEN);
+
+ /* Insert skb to shared transport layer's transmit queue.
+ * Freeing skb memory is taken care in shared transport layer,
+ * so don't free skb memory here.
+ */
+ len = drv->st_write(skb);
+ if (len < 0) {
+ kfree_skb(skb);
+ nfc_err(&drv->pdev->dev, "st_write failed %ld\n", len);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static struct nci_ops nfcwilink_ops = {
+ .open = nfcwilink_open,
+ .close = nfcwilink_close,
+ .send = nfcwilink_send,
+};
+
+static int nfcwilink_probe(struct platform_device *pdev)
+{
+ struct nfcwilink *drv;
+ int rc;
+ __u32 protocols;
+
+ drv = devm_kzalloc(&pdev->dev, sizeof(struct nfcwilink), GFP_KERNEL);
+ if (!drv) {
+ rc = -ENOMEM;
+ goto exit;
+ }
+
+ drv->pdev = pdev;
+
+ 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;
+
+ drv->ndev = nci_allocate_device(&nfcwilink_ops,
+ protocols,
+ NFCWILINK_HDR_LEN,
+ 0);
+ if (!drv->ndev) {
+ nfc_err(&pdev->dev, "nci_allocate_device failed\n");
+ rc = -ENOMEM;
+ goto exit;
+ }
+
+ nci_set_parent_dev(drv->ndev, &pdev->dev);
+ nci_set_drvdata(drv->ndev, drv);
+
+ rc = nci_register_device(drv->ndev);
+ if (rc < 0) {
+ nfc_err(&pdev->dev, "nci_register_device failed %d\n", rc);
+ goto free_dev_exit;
+ }
+
+ dev_set_drvdata(&pdev->dev, drv);
+
+ goto exit;
+
+free_dev_exit:
+ nci_free_device(drv->ndev);
+
+exit:
+ return rc;
+}
+
+static int nfcwilink_remove(struct platform_device *pdev)
+{
+ struct nfcwilink *drv = dev_get_drvdata(&pdev->dev);
+ struct nci_dev *ndev;
+
+ if (!drv)
+ return -EFAULT;
+
+ ndev = drv->ndev;
+
+ nci_unregister_device(ndev);
+ nci_free_device(ndev);
+
+ return 0;
+}
+
+static struct platform_driver nfcwilink_driver = {
+ .probe = nfcwilink_probe,
+ .remove = nfcwilink_remove,
+ .driver = {
+ .name = "nfcwilink",
+ },
+};
+
+module_platform_driver(nfcwilink_driver);
+
+/* ------ Module Info ------ */
+
+MODULE_AUTHOR("Ilan Elias <ilane@ti.com>");
+MODULE_DESCRIPTION("NFC Driver for TI Shared Transport");
+MODULE_LICENSE("GPL");
diff --git a/drivers/nfc/nxp-nci/Kconfig b/drivers/nfc/nxp-nci/Kconfig
new file mode 100644
index 0000000..208b5a9
--- /dev/null
+++ b/drivers/nfc/nxp-nci/Kconfig
@@ -0,0 +1,28 @@
+config NFC_NXP_NCI
+ tristate "NXP-NCI NFC driver"
+ depends on m
+ depends on NFC_NCI
+ default n
+ ---help---
+ Generic core driver for NXP NCI chips such as the NPC100
+ or PN7150 families.
+ This is a driver based on the NCI NFC kernel layers and
+ will thus not work with NXP libnfc library.
+
+ To compile this driver as a module, choose m here. The module will
+ be called nxp_nci.
+ Say N if unsure.
+
+config NFC_NXP_NCI_I2C
+ depends on !KERNEL_3_17
+ tristate "NXP-NCI I2C support"
+ depends on m
+ depends on NFC_NXP_NCI && I2C
+ ---help---
+ This module adds support for an I2C interface to the NXP NCI
+ chips.
+ Select this if your platform is using the I2C bus.
+
+ To compile this driver as a module, choose m here. The module will
+ be called nxp_nci_i2c.
+ Say Y if unsure.
diff --git a/drivers/nfc/nxp-nci/Makefile b/drivers/nfc/nxp-nci/Makefile
new file mode 100644
index 0000000..1315767
--- /dev/null
+++ b/drivers/nfc/nxp-nci/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for NXP-NCI NFC driver
+#
+
+nxp-nci-objs = core.o firmware.o
+nxp-nci_i2c-objs = i2c.o
+
+obj-$(CPTCFG_NFC_NXP_NCI) += nxp-nci.o
+obj-$(CPTCFG_NFC_NXP_NCI_I2C) += nxp-nci_i2c.o
diff --git a/drivers/nfc/nxp-nci/core.c b/drivers/nfc/nxp-nci/core.c
new file mode 100644
index 0000000..2e4b004
--- /dev/null
+++ b/drivers/nfc/nxp-nci/core.c
@@ -0,0 +1,187 @@
+/*
+ * Generic driver for NXP NCI NFC chips
+ *
+ * Copyright (C) 2014 NXP Semiconductors All rights reserved.
+ *
+ * Authors: Clément Perrochaud <clement.perrochaud@nxp.com>
+ *
+ * Derived from PN544 device driver:
+ * Copyright (C) 2012 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/nfc.h>
+#include <linux/platform_data/nxp-nci.h>
+
+#include <net/nfc/nci_core.h>
+
+#include "nxp-nci.h"
+
+#define NXP_NCI_HDR_LEN 4
+
+#define NXP_NCI_NFC_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)
+
+static int nxp_nci_open(struct nci_dev *ndev)
+{
+ struct nxp_nci_info *info = nci_get_drvdata(ndev);
+ int r = 0;
+
+ mutex_lock(&info->info_lock);
+
+ if (info->mode != NXP_NCI_MODE_COLD) {
+ r = -EBUSY;
+ goto open_exit;
+ }
+
+ if (info->phy_ops->set_mode)
+ r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_NCI);
+
+ info->mode = NXP_NCI_MODE_NCI;
+
+open_exit:
+ mutex_unlock(&info->info_lock);
+ return r;
+}
+
+static int nxp_nci_close(struct nci_dev *ndev)
+{
+ struct nxp_nci_info *info = nci_get_drvdata(ndev);
+ int r = 0;
+
+ mutex_lock(&info->info_lock);
+
+ if (info->phy_ops->set_mode)
+ r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD);
+
+ info->mode = NXP_NCI_MODE_COLD;
+
+ mutex_unlock(&info->info_lock);
+ return r;
+}
+
+static int nxp_nci_send(struct nci_dev *ndev, struct sk_buff *skb)
+{
+ struct nxp_nci_info *info = nci_get_drvdata(ndev);
+ int r;
+
+ if (!info->phy_ops->write) {
+ r = -ENOTSUPP;
+ goto send_exit;
+ }
+
+ if (info->mode != NXP_NCI_MODE_NCI) {
+ r = -EINVAL;
+ goto send_exit;
+ }
+
+ r = info->phy_ops->write(info->phy_id, skb);
+ if (r < 0)
+ kfree_skb(skb);
+
+send_exit:
+ return r;
+}
+
+static struct nci_ops nxp_nci_ops = {
+ .open = nxp_nci_open,
+ .close = nxp_nci_close,
+ .send = nxp_nci_send,
+ .fw_download = nxp_nci_fw_download,
+};
+
+int nxp_nci_probe(void *phy_id, struct device *pdev,
+ const struct nxp_nci_phy_ops *phy_ops,
+ unsigned int max_payload,
+ struct nci_dev **ndev)
+{
+ struct nxp_nci_info *info;
+ int r;
+
+ info = devm_kzalloc(pdev, sizeof(struct nxp_nci_info), GFP_KERNEL);
+ if (!info) {
+ r = -ENOMEM;
+ goto probe_exit;
+ }
+
+ info->phy_id = phy_id;
+ info->pdev = pdev;
+ info->phy_ops = phy_ops;
+ info->max_payload = max_payload;
+ INIT_WORK(&info->fw_info.work, nxp_nci_fw_work);
+ init_completion(&info->fw_info.cmd_completion);
+ mutex_init(&info->info_lock);
+
+ if (info->phy_ops->set_mode) {
+ r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD);
+ if (r < 0)
+ goto probe_exit;
+ }
+
+ info->mode = NXP_NCI_MODE_COLD;
+
+ info->ndev = nci_allocate_device(&nxp_nci_ops, NXP_NCI_NFC_PROTOCOLS,
+ NXP_NCI_HDR_LEN, 0);
+ if (!info->ndev) {
+ r = -ENOMEM;
+ goto probe_exit;
+ }
+
+ nci_set_parent_dev(info->ndev, pdev);
+ nci_set_drvdata(info->ndev, info);
+ r = nci_register_device(info->ndev);
+ if (r < 0)
+ goto probe_exit_free_nci;
+
+ *ndev = info->ndev;
+
+ goto probe_exit;
+
+probe_exit_free_nci:
+ nci_free_device(info->ndev);
+probe_exit:
+ return r;
+}
+EXPORT_SYMBOL(nxp_nci_probe);
+
+void nxp_nci_remove(struct nci_dev *ndev)
+{
+ struct nxp_nci_info *info = nci_get_drvdata(ndev);
+
+ if (info->mode == NXP_NCI_MODE_FW)
+ nxp_nci_fw_work_complete(info, -ESHUTDOWN);
+ cancel_work_sync(&info->fw_info.work);
+
+ mutex_lock(&info->info_lock);
+
+ if (info->phy_ops->set_mode)
+ info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD);
+
+ nci_unregister_device(ndev);
+ nci_free_device(ndev);
+
+ mutex_unlock(&info->info_lock);
+}
+EXPORT_SYMBOL(nxp_nci_remove);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("NXP NCI NFC driver");
+MODULE_AUTHOR("Clément Perrochaud <clement.perrochaud@nxp.com>");
diff --git a/drivers/nfc/nxp-nci/firmware.c b/drivers/nfc/nxp-nci/firmware.c
new file mode 100644
index 0000000..5291797
--- /dev/null
+++ b/drivers/nfc/nxp-nci/firmware.c
@@ -0,0 +1,325 @@
+/*
+ * Generic driver for NXP NCI NFC chips
+ *
+ * Copyright (C) 2014 NXP Semiconductors All rights reserved.
+ *
+ * Author: Clément Perrochaud <clement.perrochaud@nxp.com>
+ *
+ * Derived from PN544 device driver:
+ * Copyright (C) 2012 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/completion.h>
+#include <linux/firmware.h>
+#include <linux/nfc.h>
+#include <linux/unaligned/access_ok.h>
+
+#include "nxp-nci.h"
+
+/* Crypto operations can take up to 30 seconds */
+#define NXP_NCI_FW_ANSWER_TIMEOUT msecs_to_jiffies(30000)
+
+#define NXP_NCI_FW_CMD_RESET 0xF0
+#define NXP_NCI_FW_CMD_GETVERSION 0xF1
+#define NXP_NCI_FW_CMD_CHECKINTEGRITY 0xE0
+#define NXP_NCI_FW_CMD_WRITE 0xC0
+#define NXP_NCI_FW_CMD_READ 0xA2
+#define NXP_NCI_FW_CMD_GETSESSIONSTATE 0xF2
+#define NXP_NCI_FW_CMD_LOG 0xA7
+#define NXP_NCI_FW_CMD_FORCE 0xD0
+#define NXP_NCI_FW_CMD_GET_DIE_ID 0xF4
+
+#define NXP_NCI_FW_CHUNK_FLAG 0x0400
+
+#define NXP_NCI_FW_RESULT_OK 0x00
+#define NXP_NCI_FW_RESULT_INVALID_ADDR 0x01
+#define NXP_NCI_FW_RESULT_GENERIC_ERROR 0x02
+#define NXP_NCI_FW_RESULT_UNKNOWN_CMD 0x0B
+#define NXP_NCI_FW_RESULT_ABORTED_CMD 0x0C
+#define NXP_NCI_FW_RESULT_PLL_ERROR 0x0D
+#define NXP_NCI_FW_RESULT_ADDR_RANGE_OFL_ERROR 0x1E
+#define NXP_NCI_FW_RESULT_BUFFER_OFL_ERROR 0x1F
+#define NXP_NCI_FW_RESULT_MEM_BSY 0x20
+#define NXP_NCI_FW_RESULT_SIGNATURE_ERROR 0x21
+#define NXP_NCI_FW_RESULT_FIRMWARE_VERSION_ERROR 0x24
+#define NXP_NCI_FW_RESULT_PROTOCOL_ERROR 0x28
+#define NXP_NCI_FW_RESULT_SFWU_DEGRADED 0x2A
+#define NXP_NCI_FW_RESULT_PH_STATUS_FIRST_CHUNK 0x2D
+#define NXP_NCI_FW_RESULT_PH_STATUS_NEXT_CHUNK 0x2E
+#define NXP_NCI_FW_RESULT_PH_STATUS_INTERNAL_ERROR_5 0xC5
+
+void nxp_nci_fw_work_complete(struct nxp_nci_info *info, int result)
+{
+ struct nxp_nci_fw_info *fw_info = &info->fw_info;
+ int r;
+
+ if (info->phy_ops->set_mode) {
+ r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD);
+ if (r < 0 && result == 0)
+ result = -r;
+ }
+
+ info->mode = NXP_NCI_MODE_COLD;
+
+ if (fw_info->fw) {
+ release_firmware(fw_info->fw);
+ fw_info->fw = NULL;
+ }
+
+ nfc_fw_download_done(info->ndev->nfc_dev, fw_info->name, (u32) -result);
+}
+
+/* crc_ccitt cannot be used since it is computed MSB first and not LSB first */
+static u16 nxp_nci_fw_crc(u8 const *buffer, size_t len)
+{
+ u16 crc = 0xffff;
+
+ while (len--) {
+ crc = ((crc >> 8) | (crc << 8)) ^ *buffer++;
+ crc ^= (crc & 0xff) >> 4;
+ crc ^= (crc & 0xff) << 12;
+ crc ^= (crc & 0xff) << 5;
+ }
+
+ return crc;
+}
+
+static int nxp_nci_fw_send_chunk(struct nxp_nci_info *info)
+{
+ struct nxp_nci_fw_info *fw_info = &info->fw_info;
+ u16 header, crc;
+ struct sk_buff *skb;
+ size_t chunk_len;
+ size_t remaining_len;
+ int r;
+
+ skb = nci_skb_alloc(info->ndev, info->max_payload, GFP_KERNEL);
+ if (!skb) {
+ r = -ENOMEM;
+ goto chunk_exit;
+ }
+
+ chunk_len = info->max_payload - NXP_NCI_FW_HDR_LEN - NXP_NCI_FW_CRC_LEN;
+ remaining_len = fw_info->frame_size - fw_info->written;
+
+ if (remaining_len > chunk_len) {
+ header = NXP_NCI_FW_CHUNK_FLAG;
+ } else {
+ chunk_len = remaining_len;
+ header = 0x0000;
+ }
+
+ header |= chunk_len & NXP_NCI_FW_FRAME_LEN_MASK;
+ put_unaligned_be16(header, skb_put(skb, NXP_NCI_FW_HDR_LEN));
+
+ memcpy(skb_put(skb, chunk_len), fw_info->data + fw_info->written,
+ chunk_len);
+
+ crc = nxp_nci_fw_crc(skb->data, chunk_len + NXP_NCI_FW_HDR_LEN);
+ put_unaligned_be16(crc, skb_put(skb, NXP_NCI_FW_CRC_LEN));
+
+ r = info->phy_ops->write(info->phy_id, skb);
+ if (r >= 0)
+ r = chunk_len;
+
+ kfree_skb(skb);
+
+chunk_exit:
+ return r;
+}
+
+static int nxp_nci_fw_send(struct nxp_nci_info *info)
+{
+ struct nxp_nci_fw_info *fw_info = &info->fw_info;
+ long completion_rc;
+ int r;
+
+ reinit_completion(&fw_info->cmd_completion);
+
+ if (fw_info->written == 0) {
+ fw_info->frame_size = get_unaligned_be16(fw_info->data) &
+ NXP_NCI_FW_FRAME_LEN_MASK;
+ fw_info->data += NXP_NCI_FW_HDR_LEN;
+ fw_info->size -= NXP_NCI_FW_HDR_LEN;
+ }
+
+ if (fw_info->frame_size > fw_info->size)
+ return -EMSGSIZE;
+
+ r = nxp_nci_fw_send_chunk(info);
+ if (r < 0)
+ return r;
+
+ fw_info->written += r;
+
+ if (*fw_info->data == NXP_NCI_FW_CMD_RESET) {
+ fw_info->cmd_result = 0;
+ if (fw_info->fw)
+ schedule_work(&fw_info->work);
+ } else {
+ completion_rc = wait_for_completion_interruptible_timeout(
+ &fw_info->cmd_completion, NXP_NCI_FW_ANSWER_TIMEOUT);
+ if (completion_rc == 0)
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+void nxp_nci_fw_work(struct work_struct *work)
+{
+ struct nxp_nci_info *info;
+ struct nxp_nci_fw_info *fw_info;
+ int r;
+
+ fw_info = container_of(work, struct nxp_nci_fw_info, work);
+ info = container_of(fw_info, struct nxp_nci_info, fw_info);
+
+ mutex_lock(&info->info_lock);
+
+ r = fw_info->cmd_result;
+ if (r < 0)
+ goto exit_work;
+
+ if (fw_info->written == fw_info->frame_size) {
+ fw_info->data += fw_info->frame_size;
+ fw_info->size -= fw_info->frame_size;
+ fw_info->written = 0;
+ }
+
+ if (fw_info->size > 0)
+ r = nxp_nci_fw_send(info);
+
+exit_work:
+ if (r < 0 || fw_info->size == 0)
+ nxp_nci_fw_work_complete(info, r);
+ mutex_unlock(&info->info_lock);
+}
+
+int nxp_nci_fw_download(struct nci_dev *ndev, const char *firmware_name)
+{
+ struct nxp_nci_info *info = nci_get_drvdata(ndev);
+ struct nxp_nci_fw_info *fw_info = &info->fw_info;
+ int r;
+
+ mutex_lock(&info->info_lock);
+
+ if (!info->phy_ops->set_mode || !info->phy_ops->write) {
+ r = -ENOTSUPP;
+ goto fw_download_exit;
+ }
+
+ if (!firmware_name || firmware_name[0] == '\0') {
+ r = -EINVAL;
+ goto fw_download_exit;
+ }
+
+ strcpy(fw_info->name, firmware_name);
+
+ r = request_firmware(&fw_info->fw, firmware_name,
+ ndev->nfc_dev->dev.parent);
+ if (r < 0)
+ goto fw_download_exit;
+
+ r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_FW);
+ if (r < 0) {
+ release_firmware(fw_info->fw);
+ goto fw_download_exit;
+ }
+
+ info->mode = NXP_NCI_MODE_FW;
+
+ fw_info->data = fw_info->fw->data;
+ fw_info->size = fw_info->fw->size;
+ fw_info->written = 0;
+ fw_info->frame_size = 0;
+ fw_info->cmd_result = 0;
+
+ schedule_work(&fw_info->work);
+
+fw_download_exit:
+ mutex_unlock(&info->info_lock);
+ return r;
+}
+
+static int nxp_nci_fw_read_status(u8 stat)
+{
+ switch (stat) {
+ case NXP_NCI_FW_RESULT_OK:
+ return 0;
+ case NXP_NCI_FW_RESULT_INVALID_ADDR:
+ return -EINVAL;
+ case NXP_NCI_FW_RESULT_UNKNOWN_CMD:
+ return -EINVAL;
+ case NXP_NCI_FW_RESULT_ABORTED_CMD:
+ return -EMSGSIZE;
+ case NXP_NCI_FW_RESULT_ADDR_RANGE_OFL_ERROR:
+ return -EADDRNOTAVAIL;
+ case NXP_NCI_FW_RESULT_BUFFER_OFL_ERROR:
+ return -ENOBUFS;
+ case NXP_NCI_FW_RESULT_MEM_BSY:
+ return -ENOKEY;
+ case NXP_NCI_FW_RESULT_SIGNATURE_ERROR:
+ return -EKEYREJECTED;
+ case NXP_NCI_FW_RESULT_FIRMWARE_VERSION_ERROR:
+ return -EALREADY;
+ case NXP_NCI_FW_RESULT_PROTOCOL_ERROR:
+ return -EPROTO;
+ case NXP_NCI_FW_RESULT_SFWU_DEGRADED:
+ return -EHWPOISON;
+ case NXP_NCI_FW_RESULT_PH_STATUS_FIRST_CHUNK:
+ return 0;
+ case NXP_NCI_FW_RESULT_PH_STATUS_NEXT_CHUNK:
+ return 0;
+ case NXP_NCI_FW_RESULT_PH_STATUS_INTERNAL_ERROR_5:
+ return -EINVAL;
+ default:
+ return -EIO;
+ }
+}
+
+static u16 nxp_nci_fw_check_crc(struct sk_buff *skb)
+{
+ u16 crc, frame_crc;
+ size_t len = skb->len - NXP_NCI_FW_CRC_LEN;
+
+ crc = nxp_nci_fw_crc(skb->data, len);
+ frame_crc = get_unaligned_be16(skb->data + len);
+
+ return (crc ^ frame_crc);
+}
+
+void nxp_nci_fw_recv_frame(struct nci_dev *ndev, struct sk_buff *skb)
+{
+ struct nxp_nci_info *info = nci_get_drvdata(ndev);
+ struct nxp_nci_fw_info *fw_info = &info->fw_info;
+
+ complete(&fw_info->cmd_completion);
+
+ if (skb) {
+ if (nxp_nci_fw_check_crc(skb) != 0x00)
+ fw_info->cmd_result = -EBADMSG;
+ else
+ fw_info->cmd_result = nxp_nci_fw_read_status(
+ *skb_pull(skb, NXP_NCI_FW_HDR_LEN));
+ kfree_skb(skb);
+ } else {
+ fw_info->cmd_result = -EIO;
+ }
+
+ if (fw_info->fw)
+ schedule_work(&fw_info->work);
+}
+EXPORT_SYMBOL(nxp_nci_fw_recv_frame);
diff --git a/drivers/nfc/nxp-nci/i2c.c b/drivers/nfc/nxp-nci/i2c.c
new file mode 100644
index 0000000..36099e5
--- /dev/null
+++ b/drivers/nfc/nxp-nci/i2c.c
@@ -0,0 +1,434 @@
+/*
+ * I2C link layer for the NXP NCI driver
+ *
+ * Copyright (C) 2014 NXP Semiconductors All rights reserved.
+ * Copyright (C) 2012-2015 Intel Corporation. All rights reserved.
+ *
+ * Authors: Clément Perrochaud <clement.perrochaud@nxp.com>
+ * Authors: Oleg Zhurakivskyy <oleg.zhurakivskyy@intel.com>
+ *
+ * Derived from PN544 device driver:
+ * Copyright (C) 2012 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/nfc.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+#include <linux/platform_data/nxp-nci.h>
+#include <linux/unaligned/access_ok.h>
+
+#include <net/nfc/nfc.h>
+
+#include "nxp-nci.h"
+
+#define NXP_NCI_I2C_DRIVER_NAME "nxp-nci_i2c"
+
+#define NXP_NCI_I2C_MAX_PAYLOAD 32
+
+struct nxp_nci_i2c_phy {
+ struct i2c_client *i2c_dev;
+ struct nci_dev *ndev;
+
+ unsigned int gpio_en;
+ unsigned int gpio_fw;
+
+ int hard_fault; /*
+ * < 0 if hardware error occurred (e.g. i2c err)
+ * and prevents normal operation.
+ */
+};
+
+static int nxp_nci_i2c_set_mode(void *phy_id,
+ enum nxp_nci_mode mode)
+{
+ struct nxp_nci_i2c_phy *phy = (struct nxp_nci_i2c_phy *) phy_id;
+
+ gpio_set_value(phy->gpio_fw, (mode == NXP_NCI_MODE_FW) ? 1 : 0);
+ gpio_set_value(phy->gpio_en, (mode != NXP_NCI_MODE_COLD) ? 1 : 0);
+ usleep_range(10000, 15000);
+
+ if (mode == NXP_NCI_MODE_COLD)
+ phy->hard_fault = 0;
+
+ return 0;
+}
+
+static int nxp_nci_i2c_write(void *phy_id, struct sk_buff *skb)
+{
+ int r;
+ struct nxp_nci_i2c_phy *phy = phy_id;
+ struct i2c_client *client = phy->i2c_dev;
+
+ if (phy->hard_fault != 0)
+ return phy->hard_fault;
+
+ r = i2c_master_send(client, skb->data, skb->len);
+ if (r < 0) {
+ /* Retry, chip was in standby */
+ usleep_range(110000, 120000);
+ r = i2c_master_send(client, skb->data, skb->len);
+ }
+
+ if (r < 0) {
+ nfc_err(&client->dev, "Error %d on I2C send\n", r);
+ } else if (r != skb->len) {
+ nfc_err(&client->dev,
+ "Invalid length sent: %u (expected %u)\n",
+ r, skb->len);
+ r = -EREMOTEIO;
+ } else {
+ /* Success but return 0 and not number of bytes */
+ r = 0;
+ }
+
+ return r;
+}
+
+static const struct nxp_nci_phy_ops i2c_phy_ops = {
+ .set_mode = nxp_nci_i2c_set_mode,
+ .write = nxp_nci_i2c_write,
+};
+
+static int nxp_nci_i2c_fw_read(struct nxp_nci_i2c_phy *phy,
+ struct sk_buff **skb)
+{
+ struct i2c_client *client = phy->i2c_dev;
+ u16 header;
+ size_t frame_len;
+ int r;
+
+ r = i2c_master_recv(client, (u8 *) &header, NXP_NCI_FW_HDR_LEN);
+ if (r < 0) {
+ goto fw_read_exit;
+ } else if (r != NXP_NCI_FW_HDR_LEN) {
+ nfc_err(&client->dev, "Incorrect header length: %u\n", r);
+ r = -EBADMSG;
+ goto fw_read_exit;
+ }
+
+ frame_len = (get_unaligned_be16(&header) & NXP_NCI_FW_FRAME_LEN_MASK) +
+ NXP_NCI_FW_CRC_LEN;
+
+ *skb = alloc_skb(NXP_NCI_FW_HDR_LEN + frame_len, GFP_KERNEL);
+ if (*skb == NULL) {
+ r = -ENOMEM;
+ goto fw_read_exit;
+ }
+
+ memcpy(skb_put(*skb, NXP_NCI_FW_HDR_LEN), &header, NXP_NCI_FW_HDR_LEN);
+
+ r = i2c_master_recv(client, skb_put(*skb, frame_len), frame_len);
+ if (r != frame_len) {
+ nfc_err(&client->dev,
+ "Invalid frame length: %u (expected %zu)\n",
+ r, frame_len);
+ r = -EBADMSG;
+ goto fw_read_exit_free_skb;
+ }
+
+ return 0;
+
+fw_read_exit_free_skb:
+ kfree_skb(*skb);
+fw_read_exit:
+ return r;
+}
+
+static int nxp_nci_i2c_nci_read(struct nxp_nci_i2c_phy *phy,
+ struct sk_buff **skb)
+{
+ struct nci_ctrl_hdr header; /* May actually be a data header */
+ struct i2c_client *client = phy->i2c_dev;
+ int r;
+
+ r = i2c_master_recv(client, (u8 *) &header, NCI_CTRL_HDR_SIZE);
+ if (r < 0) {
+ goto nci_read_exit;
+ } else if (r != NCI_CTRL_HDR_SIZE) {
+ nfc_err(&client->dev, "Incorrect header length: %u\n", r);
+ r = -EBADMSG;
+ goto nci_read_exit;
+ }
+
+ *skb = alloc_skb(NCI_CTRL_HDR_SIZE + header.plen, GFP_KERNEL);
+ if (*skb == NULL) {
+ r = -ENOMEM;
+ goto nci_read_exit;
+ }
+
+ memcpy(skb_put(*skb, NCI_CTRL_HDR_SIZE), (void *) &header,
+ NCI_CTRL_HDR_SIZE);
+
+ r = i2c_master_recv(client, skb_put(*skb, header.plen), header.plen);
+ if (r != header.plen) {
+ nfc_err(&client->dev,
+ "Invalid frame payload length: %u (expected %u)\n",
+ r, header.plen);
+ r = -EBADMSG;
+ goto nci_read_exit_free_skb;
+ }
+
+ return 0;
+
+nci_read_exit_free_skb:
+ kfree_skb(*skb);
+nci_read_exit:
+ return r;
+}
+
+static irqreturn_t nxp_nci_i2c_irq_thread_fn(int irq, void *phy_id)
+{
+ struct nxp_nci_i2c_phy *phy = phy_id;
+ struct i2c_client *client;
+ struct nxp_nci_info *info;
+
+ struct sk_buff *skb = NULL;
+ int r = 0;
+
+ if (!phy || !phy->ndev)
+ goto exit_irq_none;
+
+ client = phy->i2c_dev;
+
+ if (!client || irq != client->irq)
+ goto exit_irq_none;
+
+ info = nci_get_drvdata(phy->ndev);
+
+ if (!info)
+ goto exit_irq_none;
+
+ mutex_lock(&info->info_lock);
+
+ if (phy->hard_fault != 0)
+ goto exit_irq_handled;
+
+ switch (info->mode) {
+ case NXP_NCI_MODE_NCI:
+ r = nxp_nci_i2c_nci_read(phy, &skb);
+ break;
+ case NXP_NCI_MODE_FW:
+ r = nxp_nci_i2c_fw_read(phy, &skb);
+ break;
+ case NXP_NCI_MODE_COLD:
+ r = -EREMOTEIO;
+ break;
+ }
+
+ if (r == -EREMOTEIO) {
+ phy->hard_fault = r;
+ skb = NULL;
+ } else if (r < 0) {
+ nfc_err(&client->dev, "Read failed with error %d\n", r);
+ goto exit_irq_handled;
+ }
+
+ switch (info->mode) {
+ case NXP_NCI_MODE_NCI:
+ nci_recv_frame(phy->ndev, skb);
+ break;
+ case NXP_NCI_MODE_FW:
+ nxp_nci_fw_recv_frame(phy->ndev, skb);
+ break;
+ case NXP_NCI_MODE_COLD:
+ break;
+ }
+
+exit_irq_handled:
+ mutex_unlock(&info->info_lock);
+ return IRQ_HANDLED;
+exit_irq_none:
+ WARN_ON_ONCE(1);
+ return IRQ_NONE;
+}
+
+static int nxp_nci_i2c_parse_devtree(struct i2c_client *client)
+{
+ struct nxp_nci_i2c_phy *phy = i2c_get_clientdata(client);
+ struct device_node *pp;
+ int r;
+
+ pp = client->dev.of_node;
+ if (!pp)
+ return -ENODEV;
+
+ r = of_get_named_gpio(pp, "enable-gpios", 0);
+ if (r == -EPROBE_DEFER)
+ r = of_get_named_gpio(pp, "enable-gpios", 0);
+ if (r < 0) {
+ nfc_err(&client->dev, "Failed to get EN gpio, error: %d\n", r);
+ return r;
+ }
+ phy->gpio_en = r;
+
+ r = of_get_named_gpio(pp, "firmware-gpios", 0);
+ if (r == -EPROBE_DEFER)
+ r = of_get_named_gpio(pp, "firmware-gpios", 0);
+ if (r < 0) {
+ nfc_err(&client->dev, "Failed to get FW gpio, error: %d\n", r);
+ return r;
+ }
+ phy->gpio_fw = r;
+
+ return 0;
+}
+
+static int nxp_nci_i2c_acpi_config(struct nxp_nci_i2c_phy *phy)
+{
+ struct i2c_client *client = phy->i2c_dev;
+ struct gpio_desc *gpiod_en, *gpiod_fw;
+
+ gpiod_en = devm_gpiod_get_index(&client->dev, NULL, 2, GPIOD_OUT_LOW);
+ gpiod_fw = devm_gpiod_get_index(&client->dev, NULL, 1, GPIOD_OUT_LOW);
+
+ if (IS_ERR(gpiod_en) || IS_ERR(gpiod_fw)) {
+ nfc_err(&client->dev, "No GPIOs\n");
+ return -EINVAL;
+ }
+
+ phy->gpio_en = desc_to_gpio(gpiod_en);
+ phy->gpio_fw = desc_to_gpio(gpiod_fw);
+
+ return 0;
+}
+
+static int nxp_nci_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct nxp_nci_i2c_phy *phy;
+ struct nxp_nci_nfc_platform_data *pdata;
+ int r;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ nfc_err(&client->dev, "Need I2C_FUNC_I2C\n");
+ r = -ENODEV;
+ goto probe_exit;
+ }
+
+ phy = devm_kzalloc(&client->dev, sizeof(struct nxp_nci_i2c_phy),
+ GFP_KERNEL);
+ if (!phy) {
+ r = -ENOMEM;
+ goto probe_exit;
+ }
+
+ phy->i2c_dev = client;
+ i2c_set_clientdata(client, phy);
+
+ pdata = client->dev.platform_data;
+
+ if (!pdata && client->dev.of_node) {
+ r = nxp_nci_i2c_parse_devtree(client);
+ if (r < 0) {
+ nfc_err(&client->dev, "Failed to get DT data\n");
+ goto probe_exit;
+ }
+ } else if (pdata) {
+ phy->gpio_en = pdata->gpio_en;
+ phy->gpio_fw = pdata->gpio_fw;
+ } else if (ACPI_HANDLE(&client->dev)) {
+ r = nxp_nci_i2c_acpi_config(phy);
+ if (r < 0)
+ goto probe_exit;
+ goto nci_probe;
+ } else {
+ nfc_err(&client->dev, "No platform data\n");
+ r = -EINVAL;
+ goto probe_exit;
+ }
+
+ r = devm_gpio_request_one(&phy->i2c_dev->dev, phy->gpio_en,
+ GPIOF_OUT_INIT_LOW, "nxp_nci_en");
+ if (r < 0)
+ goto probe_exit;
+
+ r = devm_gpio_request_one(&phy->i2c_dev->dev, phy->gpio_fw,
+ GPIOF_OUT_INIT_LOW, "nxp_nci_fw");
+ if (r < 0)
+ goto probe_exit;
+
+nci_probe:
+ r = nxp_nci_probe(phy, &client->dev, &i2c_phy_ops,
+ NXP_NCI_I2C_MAX_PAYLOAD, &phy->ndev);
+ if (r < 0)
+ goto probe_exit;
+
+ r = request_threaded_irq(client->irq, NULL,
+ nxp_nci_i2c_irq_thread_fn,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ NXP_NCI_I2C_DRIVER_NAME, phy);
+ if (r < 0)
+ nfc_err(&client->dev, "Unable to register IRQ handler\n");
+
+probe_exit:
+ return r;
+}
+
+static int nxp_nci_i2c_remove(struct i2c_client *client)
+{
+ struct nxp_nci_i2c_phy *phy = i2c_get_clientdata(client);
+
+ nxp_nci_remove(phy->ndev);
+ free_irq(client->irq, phy);
+
+ return 0;
+}
+
+static struct i2c_device_id nxp_nci_i2c_id_table[] = {
+ {"nxp-nci_i2c", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, nxp_nci_i2c_id_table);
+
+static const struct of_device_id of_nxp_nci_i2c_match[] = {
+ { .compatible = "nxp,nxp-nci-i2c", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, of_nxp_nci_i2c_match);
+
+#ifdef CONFIG_ACPI
+static struct acpi_device_id acpi_id[] = {
+ { "NXP7471" },
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, acpi_id);
+#endif
+
+static struct i2c_driver nxp_nci_i2c_driver = {
+ .driver = {
+ .name = NXP_NCI_I2C_DRIVER_NAME,
+ .acpi_match_table = ACPI_PTR(acpi_id),
+ .of_match_table = of_match_ptr(of_nxp_nci_i2c_match),
+ },
+ .probe = nxp_nci_i2c_probe,
+ .id_table = nxp_nci_i2c_id_table,
+ .remove = nxp_nci_i2c_remove,
+};
+
+module_i2c_driver(nxp_nci_i2c_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("I2C driver for NXP NCI NFC controllers");
+MODULE_AUTHOR("Clément Perrochaud <clement.perrochaud@nxp.com>");
+MODULE_AUTHOR("Oleg Zhurakivskyy <oleg.zhurakivskyy@intel.com>");
diff --git a/drivers/nfc/nxp-nci/nxp-nci.h b/drivers/nfc/nxp-nci/nxp-nci.h
new file mode 100644
index 0000000..20408cb
--- /dev/null
+++ b/drivers/nfc/nxp-nci/nxp-nci.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2014 NXP Semiconductors All rights reserved.
+ *
+ * Authors: Clément Perrochaud <clement.perrochaud@nxp.com>
+ *
+ * Derived from PN544 device driver:
+ * Copyright (C) 2012 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __LOCAL_NXP_NCI_H_
+#define __LOCAL_NXP_NCI_H_
+
+#include <linux/completion.h>
+#include <linux/firmware.h>
+#include <linux/nfc.h>
+#include <linux/platform_data/nxp-nci.h>
+
+#include <net/nfc/nci_core.h>
+
+#define NXP_NCI_FW_HDR_LEN 2
+#define NXP_NCI_FW_CRC_LEN 2
+
+#define NXP_NCI_FW_FRAME_LEN_MASK 0x03FF
+
+enum nxp_nci_mode {
+ NXP_NCI_MODE_COLD,
+ NXP_NCI_MODE_NCI,
+ NXP_NCI_MODE_FW
+};
+
+struct nxp_nci_phy_ops {
+ int (*set_mode)(void *id, enum nxp_nci_mode mode);
+ int (*write)(void *id, struct sk_buff *skb);
+};
+
+struct nxp_nci_fw_info {
+ char name[NFC_FIRMWARE_NAME_MAXSIZE + 1];
+ const struct firmware *fw;
+
+ size_t size;
+ size_t written;
+
+ const u8 *data;
+ size_t frame_size;
+
+ struct work_struct work;
+ struct completion cmd_completion;
+
+ int cmd_result;
+};
+
+struct nxp_nci_info {
+ struct nci_dev *ndev;
+ void *phy_id;
+ struct device *pdev;
+
+ enum nxp_nci_mode mode;
+
+ const struct nxp_nci_phy_ops *phy_ops;
+ unsigned int max_payload;
+
+ struct mutex info_lock;
+
+ struct nxp_nci_fw_info fw_info;
+};
+
+int nxp_nci_fw_download(struct nci_dev *ndev, const char *firmware_name);
+void nxp_nci_fw_work(struct work_struct *work);
+void nxp_nci_fw_recv_frame(struct nci_dev *ndev, struct sk_buff *skb);
+void nxp_nci_fw_work_complete(struct nxp_nci_info *info, int result);
+
+int nxp_nci_probe(void *phy_id, struct device *pdev,
+ const struct nxp_nci_phy_ops *phy_ops,
+ unsigned int max_payload,
+ struct nci_dev **ndev);
+void nxp_nci_remove(struct nci_dev *ndev);
+
+#endif /* __LOCAL_NXP_NCI_H_ */
diff --git a/drivers/nfc/pn533/Kconfig b/drivers/nfc/pn533/Kconfig
new file mode 100644
index 0000000..8516203
--- /dev/null
+++ b/drivers/nfc/pn533/Kconfig
@@ -0,0 +1,30 @@
+config NFC_PN533
+ tristate
+ depends on m
+ help
+ NXP PN533 core driver.
+ This driver provides core functionality for NXP PN533 NFC devices.
+
+config NFC_PN533_USB
+ tristate "NFC PN533 device support (USB)"
+ depends on m
+ depends on USB
+ select NFC_PN533
+ ---help---
+ This module adds support for the NXP pn533 USB interface.
+ Select this if your platform is using the USB bus.
+
+ If you choose to build a module, it'll be called pn533_usb.
+ Say N if unsure.
+
+config NFC_PN533_I2C
+ tristate "NFC PN533 device support (I2C)"
+ depends on m
+ depends on I2C
+ select NFC_PN533
+ ---help---
+ This module adds support for the NXP pn533 I2C interface.
+ Select this if your platform is using the I2C bus.
+
+ If you choose to build a module, it'll be called pn533_i2c.
+ Say N if unsure.
diff --git a/drivers/nfc/pn533/Makefile b/drivers/nfc/pn533/Makefile
new file mode 100644
index 0000000..8460fb4
--- /dev/null
+++ b/drivers/nfc/pn533/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for PN533 NFC driver
+#
+pn533_usb-objs = usb.o
+pn533_i2c-objs = i2c.o
+
+obj-$(CPTCFG_NFC_PN533) += pn533.o
+obj-$(CPTCFG_NFC_PN533_USB) += pn533_usb.o
+obj-$(CPTCFG_NFC_PN533_I2C) += pn533_i2c.o
diff --git a/drivers/nfc/pn533/i2c.c b/drivers/nfc/pn533/i2c.c
new file mode 100644
index 0000000..1dc8924
--- /dev/null
+++ b/drivers/nfc/pn533/i2c.c
@@ -0,0 +1,281 @@
+/*
+ * Driver for NXP PN533 NFC Chip - I2C transport layer
+ *
+ * Copyright (C) 2011 Instituto Nokia de Tecnologia
+ * Copyright (C) 2012-2013 Tieto Poland
+ * Copyright (C) 2016 HALE electronic
+ *
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/nfc.h>
+#include <linux/netdevice.h>
+#include <linux/interrupt.h>
+#include <net/nfc/nfc.h>
+#include "pn533.h"
+
+#define VERSION "0.1"
+
+#define PN533_I2C_DRIVER_NAME "pn533_i2c"
+
+struct pn533_i2c_phy {
+ struct i2c_client *i2c_dev;
+ struct pn533 *priv;
+
+ bool aborted;
+
+ int hard_fault; /*
+ * < 0 if hardware error occurred (e.g. i2c err)
+ * and prevents normal operation.
+ */
+};
+
+static int pn533_i2c_send_ack(struct pn533 *dev, gfp_t flags)
+{
+ struct pn533_i2c_phy *phy = dev->phy;
+ struct i2c_client *client = phy->i2c_dev;
+ u8 ack[6] = {0x00, 0x00, 0xff, 0x00, 0xff, 0x00};
+ /* spec 6.2.1.3: Preamble, SoPC (2), ACK Code (2), Postamble */
+ int rc;
+
+ rc = i2c_master_send(client, ack, 6);
+
+ return rc;
+}
+
+static int pn533_i2c_send_frame(struct pn533 *dev,
+ struct sk_buff *out)
+{
+ struct pn533_i2c_phy *phy = dev->phy;
+ struct i2c_client *client = phy->i2c_dev;
+ int rc;
+
+ if (phy->hard_fault != 0)
+ return phy->hard_fault;
+
+ if (phy->priv == NULL)
+ phy->priv = dev;
+
+ phy->aborted = false;
+
+ print_hex_dump_debug("PN533_i2c TX: ", DUMP_PREFIX_NONE, 16, 1,
+ out->data, out->len, false);
+
+ rc = i2c_master_send(client, out->data, out->len);
+
+ if (rc == -EREMOTEIO) { /* Retry, chip was in power down */
+ usleep_range(6000, 10000);
+ rc = i2c_master_send(client, out->data, out->len);
+ }
+
+ if (rc >= 0) {
+ if (rc != out->len)
+ rc = -EREMOTEIO;
+ else
+ rc = 0;
+ }
+
+ return rc;
+}
+
+static void pn533_i2c_abort_cmd(struct pn533 *dev, gfp_t flags)
+{
+ struct pn533_i2c_phy *phy = dev->phy;
+
+ phy->aborted = true;
+
+ /* An ack will cancel the last issued command */
+ pn533_i2c_send_ack(dev, flags);
+
+ /* schedule cmd_complete_work to finish current command execution */
+ pn533_recv_frame(phy->priv, NULL, -ENOENT);
+}
+
+static int pn533_i2c_read(struct pn533_i2c_phy *phy, struct sk_buff **skb)
+{
+ struct i2c_client *client = phy->i2c_dev;
+ int len = PN533_EXT_FRAME_HEADER_LEN +
+ PN533_STD_FRAME_MAX_PAYLOAD_LEN +
+ PN533_STD_FRAME_TAIL_LEN + 1;
+ int r;
+
+ *skb = alloc_skb(len, GFP_KERNEL);
+ if (*skb == NULL)
+ return -ENOMEM;
+
+ r = i2c_master_recv(client, skb_put(*skb, len), len);
+ if (r != len) {
+ nfc_err(&client->dev, "cannot read. r=%d len=%d\n", r, len);
+ kfree_skb(*skb);
+ return -EREMOTEIO;
+ }
+
+ if (!((*skb)->data[0] & 0x01)) {
+ nfc_err(&client->dev, "READY flag not set");
+ kfree_skb(*skb);
+ return -EBUSY;
+ }
+
+ /* remove READY byte */
+ skb_pull(*skb, 1);
+ /* trim to frame size */
+ skb_trim(*skb, phy->priv->ops->rx_frame_size((*skb)->data));
+
+ return 0;
+}
+
+static irqreturn_t pn533_i2c_irq_thread_fn(int irq, void *data)
+{
+ struct pn533_i2c_phy *phy = data;
+ struct i2c_client *client;
+ struct sk_buff *skb = NULL;
+ int r;
+
+ if (!phy || irq != phy->i2c_dev->irq) {
+ WARN_ON_ONCE(1);
+ return IRQ_NONE;
+ }
+
+ client = phy->i2c_dev;
+ dev_dbg(&client->dev, "IRQ\n");
+
+ if (phy->hard_fault != 0)
+ return IRQ_HANDLED;
+
+ r = pn533_i2c_read(phy, &skb);
+ if (r == -EREMOTEIO) {
+ phy->hard_fault = r;
+
+ pn533_recv_frame(phy->priv, NULL, -EREMOTEIO);
+
+ return IRQ_HANDLED;
+ } else if ((r == -ENOMEM) || (r == -EBADMSG) || (r == -EBUSY)) {
+ return IRQ_HANDLED;
+ }
+
+ if (!phy->aborted)
+ pn533_recv_frame(phy->priv, skb, 0);
+
+ return IRQ_HANDLED;
+}
+
+static struct pn533_phy_ops i2c_phy_ops = {
+ .send_frame = pn533_i2c_send_frame,
+ .send_ack = pn533_i2c_send_ack,
+ .abort_cmd = pn533_i2c_abort_cmd,
+};
+
+
+static int pn533_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct pn533_i2c_phy *phy;
+ struct pn533 *priv;
+ 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, "Need I2C_FUNC_I2C\n");
+ return -ENODEV;
+ }
+
+ phy = devm_kzalloc(&client->dev, sizeof(struct pn533_i2c_phy),
+ GFP_KERNEL);
+ if (!phy)
+ return -ENOMEM;
+
+ phy->i2c_dev = client;
+ i2c_set_clientdata(client, phy);
+
+ r = request_threaded_irq(client->irq, NULL, pn533_i2c_irq_thread_fn,
+ IRQF_TRIGGER_FALLING |
+ IRQF_SHARED | IRQF_ONESHOT,
+ PN533_I2C_DRIVER_NAME, phy);
+
+ if (r < 0)
+ nfc_err(&client->dev, "Unable to register IRQ handler\n");
+
+ priv = pn533_register_device(PN533_DEVICE_PN532,
+ PN533_NO_TYPE_B_PROTOCOLS,
+ PN533_PROTO_REQ_ACK_RESP,
+ phy, &i2c_phy_ops, NULL,
+ &phy->i2c_dev->dev,
+ &client->dev);
+
+ if (IS_ERR(priv)) {
+ r = PTR_ERR(priv);
+ goto err_register;
+ }
+
+ phy->priv = priv;
+
+ return 0;
+
+err_register:
+ free_irq(client->irq, phy);
+
+ return r;
+}
+
+static int pn533_i2c_remove(struct i2c_client *client)
+{
+ struct pn533_i2c_phy *phy = i2c_get_clientdata(client);
+
+ dev_dbg(&client->dev, "%s\n", __func__);
+
+ pn533_unregister_device(phy->priv);
+
+ free_irq(client->irq, phy);
+
+ return 0;
+}
+
+static const struct of_device_id of_pn533_i2c_match[] = {
+ { .compatible = "nxp,pn533-i2c", },
+ { .compatible = "nxp,pn532-i2c", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, of_pn533_i2c_match);
+
+static struct i2c_device_id pn533_i2c_id_table[] = {
+ { PN533_I2C_DRIVER_NAME, 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, pn533_i2c_id_table);
+
+static struct i2c_driver pn533_i2c_driver = {
+ .driver = {
+ .name = PN533_I2C_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(of_pn533_i2c_match),
+ },
+ .probe = pn533_i2c_probe,
+ .id_table = pn533_i2c_id_table,
+ .remove = pn533_i2c_remove,
+};
+
+module_i2c_driver(pn533_i2c_driver);
+
+MODULE_AUTHOR("Michael Thalmeier <michael.thalmeier@hale.at>");
+MODULE_DESCRIPTION("PN533 I2C driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/nfc/pn533/pn533.c b/drivers/nfc/pn533/pn533.c
new file mode 100644
index 0000000..a966c6a
--- /dev/null
+++ b/drivers/nfc/pn533/pn533.c
@@ -0,0 +1,2685 @@
+/*
+ * Driver for NXP PN533 NFC Chip - core functions
+ *
+ * Copyright (C) 2011 Instituto Nokia de Tecnologia
+ * Copyright (C) 2012-2013 Tieto Poland
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/nfc.h>
+#include <linux/netdevice.h>
+#include <net/nfc/nfc.h>
+#include "pn533.h"
+
+#define VERSION "0.3"
+
+/* How much time we spend listening for initiators */
+#define PN533_LISTEN_TIME 2
+/* Delay between each poll frame (ms) */
+#define PN533_POLL_INTERVAL 10
+
+/* structs for pn533 commands */
+
+/* PN533_CMD_GET_FIRMWARE_VERSION */
+struct pn533_fw_version {
+ u8 ic;
+ u8 ver;
+ u8 rev;
+ u8 support;
+};
+
+/* PN533_CMD_RF_CONFIGURATION */
+#define PN533_CFGITEM_RF_FIELD 0x01
+#define PN533_CFGITEM_TIMING 0x02
+#define PN533_CFGITEM_MAX_RETRIES 0x05
+#define PN533_CFGITEM_PASORI 0x82
+
+#define PN533_CFGITEM_RF_FIELD_AUTO_RFCA 0x2
+#define PN533_CFGITEM_RF_FIELD_ON 0x1
+#define PN533_CFGITEM_RF_FIELD_OFF 0x0
+
+#define PN533_CONFIG_TIMING_102 0xb
+#define PN533_CONFIG_TIMING_204 0xc
+#define PN533_CONFIG_TIMING_409 0xd
+#define PN533_CONFIG_TIMING_819 0xe
+
+#define PN533_CONFIG_MAX_RETRIES_NO_RETRY 0x00
+#define PN533_CONFIG_MAX_RETRIES_ENDLESS 0xFF
+
+struct pn533_config_max_retries {
+ u8 mx_rty_atr;
+ u8 mx_rty_psl;
+ u8 mx_rty_passive_act;
+} __packed;
+
+struct pn533_config_timing {
+ u8 rfu;
+ u8 atr_res_timeout;
+ u8 dep_timeout;
+} __packed;
+
+/* PN533_CMD_IN_LIST_PASSIVE_TARGET */
+
+/* felica commands opcode */
+#define PN533_FELICA_OPC_SENSF_REQ 0
+#define PN533_FELICA_OPC_SENSF_RES 1
+/* felica SENSF_REQ parameters */
+#define PN533_FELICA_SENSF_SC_ALL 0xFFFF
+#define PN533_FELICA_SENSF_RC_NO_SYSTEM_CODE 0
+#define PN533_FELICA_SENSF_RC_SYSTEM_CODE 1
+#define PN533_FELICA_SENSF_RC_ADVANCED_PROTOCOL 2
+
+/* type B initiator_data values */
+#define PN533_TYPE_B_AFI_ALL_FAMILIES 0
+#define PN533_TYPE_B_POLL_METHOD_TIMESLOT 0
+#define PN533_TYPE_B_POLL_METHOD_PROBABILISTIC 1
+
+union pn533_cmd_poll_initdata {
+ struct {
+ u8 afi;
+ u8 polling_method;
+ } __packed type_b;
+ struct {
+ u8 opcode;
+ __be16 sc;
+ u8 rc;
+ u8 tsn;
+ } __packed felica;
+};
+
+struct pn533_poll_modulations {
+ struct {
+ u8 maxtg;
+ u8 brty;
+ union pn533_cmd_poll_initdata initiator_data;
+ } __packed data;
+ u8 len;
+};
+
+static const struct pn533_poll_modulations poll_mod[] = {
+ [PN533_POLL_MOD_106KBPS_A] = {
+ .data = {
+ .maxtg = 1,
+ .brty = 0,
+ },
+ .len = 2,
+ },
+ [PN533_POLL_MOD_212KBPS_FELICA] = {
+ .data = {
+ .maxtg = 1,
+ .brty = 1,
+ .initiator_data.felica = {
+ .opcode = PN533_FELICA_OPC_SENSF_REQ,
+ .sc = PN533_FELICA_SENSF_SC_ALL,
+ .rc = PN533_FELICA_SENSF_RC_SYSTEM_CODE,
+ .tsn = 0x03,
+ },
+ },
+ .len = 7,
+ },
+ [PN533_POLL_MOD_424KBPS_FELICA] = {
+ .data = {
+ .maxtg = 1,
+ .brty = 2,
+ .initiator_data.felica = {
+ .opcode = PN533_FELICA_OPC_SENSF_REQ,
+ .sc = PN533_FELICA_SENSF_SC_ALL,
+ .rc = PN533_FELICA_SENSF_RC_SYSTEM_CODE,
+ .tsn = 0x03,
+ },
+ },
+ .len = 7,
+ },
+ [PN533_POLL_MOD_106KBPS_JEWEL] = {
+ .data = {
+ .maxtg = 1,
+ .brty = 4,
+ },
+ .len = 2,
+ },
+ [PN533_POLL_MOD_847KBPS_B] = {
+ .data = {
+ .maxtg = 1,
+ .brty = 8,
+ .initiator_data.type_b = {
+ .afi = PN533_TYPE_B_AFI_ALL_FAMILIES,
+ .polling_method =
+ PN533_TYPE_B_POLL_METHOD_TIMESLOT,
+ },
+ },
+ .len = 3,
+ },
+ [PN533_LISTEN_MOD] = {
+ .len = 0,
+ },
+};
+
+/* PN533_CMD_IN_ATR */
+
+struct pn533_cmd_activate_response {
+ u8 status;
+ u8 nfcid3t[10];
+ u8 didt;
+ u8 bst;
+ u8 brt;
+ u8 to;
+ u8 ppt;
+ /* optional */
+ u8 gt[];
+} __packed;
+
+struct pn533_cmd_jump_dep_response {
+ u8 status;
+ u8 tg;
+ u8 nfcid3t[10];
+ u8 didt;
+ u8 bst;
+ u8 brt;
+ u8 to;
+ u8 ppt;
+ /* optional */
+ u8 gt[];
+} __packed;
+
+
+/* PN533_TG_INIT_AS_TARGET */
+#define PN533_INIT_TARGET_PASSIVE 0x1
+#define PN533_INIT_TARGET_DEP 0x2
+
+#define PN533_INIT_TARGET_RESP_FRAME_MASK 0x3
+#define PN533_INIT_TARGET_RESP_ACTIVE 0x1
+#define PN533_INIT_TARGET_RESP_DEP 0x4
+
+/* The rule: value(high byte) + value(low byte) + checksum = 0 */
+static inline u8 pn533_ext_checksum(u16 value)
+{
+ return ~(u8)(((value & 0xFF00) >> 8) + (u8)(value & 0xFF)) + 1;
+}
+
+/* The rule: value + checksum = 0 */
+static inline u8 pn533_std_checksum(u8 value)
+{
+ return ~value + 1;
+}
+
+/* The rule: sum(data elements) + checksum = 0 */
+static u8 pn533_std_data_checksum(u8 *data, int datalen)
+{
+ u8 sum = 0;
+ int i;
+
+ for (i = 0; i < datalen; i++)
+ sum += data[i];
+
+ return pn533_std_checksum(sum);
+}
+
+static void pn533_std_tx_frame_init(void *_frame, u8 cmd_code)
+{
+ struct pn533_std_frame *frame = _frame;
+
+ frame->preamble = 0;
+ frame->start_frame = cpu_to_be16(PN533_STD_FRAME_SOF);
+ PN533_STD_FRAME_IDENTIFIER(frame) = PN533_STD_FRAME_DIR_OUT;
+ PN533_FRAME_CMD(frame) = cmd_code;
+ frame->datalen = 2;
+}
+
+static void pn533_std_tx_frame_finish(void *_frame)
+{
+ struct pn533_std_frame *frame = _frame;
+
+ frame->datalen_checksum = pn533_std_checksum(frame->datalen);
+
+ PN533_STD_FRAME_CHECKSUM(frame) =
+ pn533_std_data_checksum(frame->data, frame->datalen);
+
+ PN533_STD_FRAME_POSTAMBLE(frame) = 0;
+}
+
+static void pn533_std_tx_update_payload_len(void *_frame, int len)
+{
+ struct pn533_std_frame *frame = _frame;
+
+ frame->datalen += len;
+}
+
+static bool pn533_std_rx_frame_is_valid(void *_frame, struct pn533 *dev)
+{
+ u8 checksum;
+ struct pn533_std_frame *stdf = _frame;
+
+ if (stdf->start_frame != cpu_to_be16(PN533_STD_FRAME_SOF))
+ return false;
+
+ if (likely(!PN533_STD_IS_EXTENDED(stdf))) {
+ /* Standard frame code */
+ dev->ops->rx_header_len = PN533_STD_FRAME_HEADER_LEN;
+
+ checksum = pn533_std_checksum(stdf->datalen);
+ if (checksum != stdf->datalen_checksum)
+ return false;
+
+ checksum = pn533_std_data_checksum(stdf->data, stdf->datalen);
+ if (checksum != PN533_STD_FRAME_CHECKSUM(stdf))
+ return false;
+ } else {
+ /* Extended */
+ struct pn533_ext_frame *eif = _frame;
+
+ dev->ops->rx_header_len = PN533_EXT_FRAME_HEADER_LEN;
+
+ checksum = pn533_ext_checksum(be16_to_cpu(eif->datalen));
+ if (checksum != eif->datalen_checksum)
+ return false;
+
+ /* check data checksum */
+ checksum = pn533_std_data_checksum(eif->data,
+ be16_to_cpu(eif->datalen));
+ if (checksum != PN533_EXT_FRAME_CHECKSUM(eif))
+ return false;
+ }
+
+ return true;
+}
+
+bool pn533_rx_frame_is_ack(void *_frame)
+{
+ struct pn533_std_frame *frame = _frame;
+
+ if (frame->start_frame != cpu_to_be16(PN533_STD_FRAME_SOF))
+ return false;
+
+ if (frame->datalen != 0 || frame->datalen_checksum != 0xFF)
+ return false;
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(pn533_rx_frame_is_ack);
+
+static inline int pn533_std_rx_frame_size(void *frame)
+{
+ struct pn533_std_frame *f = frame;
+
+ /* check for Extended Information frame */
+ if (PN533_STD_IS_EXTENDED(f)) {
+ struct pn533_ext_frame *eif = frame;
+
+ return sizeof(struct pn533_ext_frame)
+ + be16_to_cpu(eif->datalen) + PN533_STD_FRAME_TAIL_LEN;
+ }
+
+ return sizeof(struct pn533_std_frame) + f->datalen +
+ PN533_STD_FRAME_TAIL_LEN;
+}
+
+static u8 pn533_std_get_cmd_code(void *frame)
+{
+ struct pn533_std_frame *f = frame;
+ struct pn533_ext_frame *eif = frame;
+
+ if (PN533_STD_IS_EXTENDED(f))
+ return PN533_FRAME_CMD(eif);
+ else
+ return PN533_FRAME_CMD(f);
+}
+
+bool pn533_rx_frame_is_cmd_response(struct pn533 *dev, void *frame)
+{
+ return (dev->ops->get_cmd_code(frame) ==
+ PN533_CMD_RESPONSE(dev->cmd->code));
+}
+EXPORT_SYMBOL_GPL(pn533_rx_frame_is_cmd_response);
+
+
+static struct pn533_frame_ops pn533_std_frame_ops = {
+ .tx_frame_init = pn533_std_tx_frame_init,
+ .tx_frame_finish = pn533_std_tx_frame_finish,
+ .tx_update_payload_len = pn533_std_tx_update_payload_len,
+ .tx_header_len = PN533_STD_FRAME_HEADER_LEN,
+ .tx_tail_len = PN533_STD_FRAME_TAIL_LEN,
+
+ .rx_is_frame_valid = pn533_std_rx_frame_is_valid,
+ .rx_frame_size = pn533_std_rx_frame_size,
+ .rx_header_len = PN533_STD_FRAME_HEADER_LEN,
+ .rx_tail_len = PN533_STD_FRAME_TAIL_LEN,
+
+ .max_payload_len = PN533_STD_FRAME_MAX_PAYLOAD_LEN,
+ .get_cmd_code = pn533_std_get_cmd_code,
+};
+
+static void pn533_build_cmd_frame(struct pn533 *dev, u8 cmd_code,
+ struct sk_buff *skb)
+{
+ /* payload is already there, just update datalen */
+ int payload_len = skb->len;
+ struct pn533_frame_ops *ops = dev->ops;
+
+
+ skb_push(skb, ops->tx_header_len);
+ skb_put(skb, ops->tx_tail_len);
+
+ ops->tx_frame_init(skb->data, cmd_code);
+ ops->tx_update_payload_len(skb->data, payload_len);
+ ops->tx_frame_finish(skb->data);
+}
+
+static int pn533_send_async_complete(struct pn533 *dev)
+{
+ struct pn533_cmd *cmd = dev->cmd;
+ int status = cmd->status;
+
+ struct sk_buff *req = cmd->req;
+ struct sk_buff *resp = cmd->resp;
+
+ int rc;
+
+ dev_kfree_skb(req);
+
+ if (status < 0) {
+ rc = cmd->complete_cb(dev, cmd->complete_cb_context,
+ ERR_PTR(status));
+ dev_kfree_skb(resp);
+ goto done;
+ }
+
+ skb_pull(resp, dev->ops->rx_header_len);
+ skb_trim(resp, resp->len - dev->ops->rx_tail_len);
+
+ rc = cmd->complete_cb(dev, cmd->complete_cb_context, resp);
+
+done:
+ kfree(cmd);
+ dev->cmd = NULL;
+ return rc;
+}
+
+static int __pn533_send_async(struct pn533 *dev, u8 cmd_code,
+ struct sk_buff *req,
+ pn533_send_async_complete_t complete_cb,
+ void *complete_cb_context)
+{
+ struct pn533_cmd *cmd;
+ int rc = 0;
+
+ dev_dbg(dev->dev, "Sending command 0x%x\n", cmd_code);
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd)
+ return -ENOMEM;
+
+ cmd->code = cmd_code;
+ cmd->req = req;
+ cmd->complete_cb = complete_cb;
+ cmd->complete_cb_context = complete_cb_context;
+
+ pn533_build_cmd_frame(dev, cmd_code, req);
+
+ mutex_lock(&dev->cmd_lock);
+
+ if (!dev->cmd_pending) {
+ rc = dev->phy_ops->send_frame(dev, req);
+ if (rc)
+ goto error;
+
+ dev->cmd_pending = 1;
+ dev->cmd = cmd;
+ goto unlock;
+ }
+
+ dev_dbg(dev->dev, "%s Queueing command 0x%x\n",
+ __func__, cmd_code);
+
+ INIT_LIST_HEAD(&cmd->queue);
+ list_add_tail(&cmd->queue, &dev->cmd_queue);
+
+ goto unlock;
+
+error:
+ kfree(cmd);
+unlock:
+ mutex_unlock(&dev->cmd_lock);
+ return rc;
+}
+
+static int pn533_send_data_async(struct pn533 *dev, u8 cmd_code,
+ struct sk_buff *req,
+ pn533_send_async_complete_t complete_cb,
+ void *complete_cb_context)
+{
+ int rc;
+
+ rc = __pn533_send_async(dev, cmd_code, req, complete_cb,
+ complete_cb_context);
+
+ return rc;
+}
+
+static int pn533_send_cmd_async(struct pn533 *dev, u8 cmd_code,
+ struct sk_buff *req,
+ pn533_send_async_complete_t complete_cb,
+ void *complete_cb_context)
+{
+ int rc;
+
+ rc = __pn533_send_async(dev, cmd_code, req, complete_cb,
+ complete_cb_context);
+
+ return rc;
+}
+
+/*
+ * pn533_send_cmd_direct_async
+ *
+ * The function sends a piority cmd directly to the chip omitting the cmd
+ * queue. It's intended to be used by chaining mechanism of received responses
+ * where the host has to request every single chunk of data before scheduling
+ * next cmd from the queue.
+ */
+static int pn533_send_cmd_direct_async(struct pn533 *dev, u8 cmd_code,
+ struct sk_buff *req,
+ pn533_send_async_complete_t complete_cb,
+ void *complete_cb_context)
+{
+ struct pn533_cmd *cmd;
+ int rc;
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd)
+ return -ENOMEM;
+
+ cmd->code = cmd_code;
+ cmd->req = req;
+ cmd->complete_cb = complete_cb;
+ cmd->complete_cb_context = complete_cb_context;
+
+ pn533_build_cmd_frame(dev, cmd_code, req);
+
+ rc = dev->phy_ops->send_frame(dev, req);
+ if (rc < 0)
+ kfree(cmd);
+ else
+ dev->cmd = cmd;
+
+ return rc;
+}
+
+static void pn533_wq_cmd_complete(struct work_struct *work)
+{
+ struct pn533 *dev = container_of(work, struct pn533, cmd_complete_work);
+ int rc;
+
+ rc = pn533_send_async_complete(dev);
+ if (rc != -EINPROGRESS)
+ queue_work(dev->wq, &dev->cmd_work);
+}
+
+static void pn533_wq_cmd(struct work_struct *work)
+{
+ struct pn533 *dev = container_of(work, struct pn533, cmd_work);
+ struct pn533_cmd *cmd;
+ int rc;
+
+ mutex_lock(&dev->cmd_lock);
+
+ if (list_empty(&dev->cmd_queue)) {
+ dev->cmd_pending = 0;
+ mutex_unlock(&dev->cmd_lock);
+ return;
+ }
+
+ cmd = list_first_entry(&dev->cmd_queue, struct pn533_cmd, queue);
+
+ list_del(&cmd->queue);
+
+ mutex_unlock(&dev->cmd_lock);
+
+ rc = dev->phy_ops->send_frame(dev, cmd->req);
+ if (rc < 0) {
+ dev_kfree_skb(cmd->req);
+ kfree(cmd);
+ return;
+ }
+
+ dev->cmd = cmd;
+}
+
+struct pn533_sync_cmd_response {
+ struct sk_buff *resp;
+ struct completion done;
+};
+
+static int pn533_send_sync_complete(struct pn533 *dev, void *_arg,
+ struct sk_buff *resp)
+{
+ struct pn533_sync_cmd_response *arg = _arg;
+
+ arg->resp = resp;
+ complete(&arg->done);
+
+ return 0;
+}
+
+/* pn533_send_cmd_sync
+ *
+ * Please note the req parameter is freed inside the function to
+ * limit a number of return value interpretations by the caller.
+ *
+ * 1. negative in case of error during TX path -> req should be freed
+ *
+ * 2. negative in case of error during RX path -> req should not be freed
+ * as it's been already freed at the beginning of RX path by
+ * async_complete_cb.
+ *
+ * 3. valid pointer in case of succesfult RX path
+ *
+ * A caller has to check a return value with IS_ERR macro. If the test pass,
+ * the returned pointer is valid.
+ *
+ */
+static struct sk_buff *pn533_send_cmd_sync(struct pn533 *dev, u8 cmd_code,
+ struct sk_buff *req)
+{
+ int rc;
+ struct pn533_sync_cmd_response arg;
+
+ init_completion(&arg.done);
+
+ rc = pn533_send_cmd_async(dev, cmd_code, req,
+ pn533_send_sync_complete, &arg);
+ if (rc) {
+ dev_kfree_skb(req);
+ return ERR_PTR(rc);
+ }
+
+ wait_for_completion(&arg.done);
+
+ return arg.resp;
+}
+
+static struct sk_buff *pn533_alloc_skb(struct pn533 *dev, unsigned int size)
+{
+ struct sk_buff *skb;
+
+ skb = alloc_skb(dev->ops->tx_header_len +
+ size +
+ dev->ops->tx_tail_len, GFP_KERNEL);
+
+ if (skb)
+ skb_reserve(skb, dev->ops->tx_header_len);
+
+ return skb;
+}
+
+struct pn533_target_type_a {
+ __be16 sens_res;
+ u8 sel_res;
+ u8 nfcid_len;
+ u8 nfcid_data[];
+} __packed;
+
+
+#define PN533_TYPE_A_SENS_RES_NFCID1(x) ((u8)((be16_to_cpu(x) & 0x00C0) >> 6))
+#define PN533_TYPE_A_SENS_RES_SSD(x) ((u8)((be16_to_cpu(x) & 0x001F) >> 0))
+#define PN533_TYPE_A_SENS_RES_PLATCONF(x) ((u8)((be16_to_cpu(x) & 0x0F00) >> 8))
+
+#define PN533_TYPE_A_SENS_RES_SSD_JEWEL 0x00
+#define PN533_TYPE_A_SENS_RES_PLATCONF_JEWEL 0x0C
+
+#define PN533_TYPE_A_SEL_PROT(x) (((x) & 0x60) >> 5)
+#define PN533_TYPE_A_SEL_CASCADE(x) (((x) & 0x04) >> 2)
+
+#define PN533_TYPE_A_SEL_PROT_MIFARE 0
+#define PN533_TYPE_A_SEL_PROT_ISO14443 1
+#define PN533_TYPE_A_SEL_PROT_DEP 2
+#define PN533_TYPE_A_SEL_PROT_ISO14443_DEP 3
+
+static bool pn533_target_type_a_is_valid(struct pn533_target_type_a *type_a,
+ int target_data_len)
+{
+ u8 ssd;
+ u8 platconf;
+
+ if (target_data_len < sizeof(struct pn533_target_type_a))
+ return false;
+
+ /*
+ * The length check of nfcid[] and ats[] are not being performed because
+ * the values are not being used
+ */
+
+ /* Requirement 4.6.3.3 from NFC Forum Digital Spec */
+ ssd = PN533_TYPE_A_SENS_RES_SSD(type_a->sens_res);
+ platconf = PN533_TYPE_A_SENS_RES_PLATCONF(type_a->sens_res);
+
+ if ((ssd == PN533_TYPE_A_SENS_RES_SSD_JEWEL &&
+ platconf != PN533_TYPE_A_SENS_RES_PLATCONF_JEWEL) ||
+ (ssd != PN533_TYPE_A_SENS_RES_SSD_JEWEL &&
+ platconf == PN533_TYPE_A_SENS_RES_PLATCONF_JEWEL))
+ return false;
+
+ /* Requirements 4.8.2.1, 4.8.2.3, 4.8.2.5 and 4.8.2.7 from NFC Forum */
+ if (PN533_TYPE_A_SEL_CASCADE(type_a->sel_res) != 0)
+ return false;
+
+ return true;
+}
+
+static int pn533_target_found_type_a(struct nfc_target *nfc_tgt, u8 *tgt_data,
+ int tgt_data_len)
+{
+ struct pn533_target_type_a *tgt_type_a;
+
+ tgt_type_a = (struct pn533_target_type_a *)tgt_data;
+
+ if (!pn533_target_type_a_is_valid(tgt_type_a, tgt_data_len))
+ return -EPROTO;
+
+ switch (PN533_TYPE_A_SEL_PROT(tgt_type_a->sel_res)) {
+ case PN533_TYPE_A_SEL_PROT_MIFARE:
+ nfc_tgt->supported_protocols = NFC_PROTO_MIFARE_MASK;
+ break;
+ case PN533_TYPE_A_SEL_PROT_ISO14443:
+ nfc_tgt->supported_protocols = NFC_PROTO_ISO14443_MASK;
+ break;
+ case PN533_TYPE_A_SEL_PROT_DEP:
+ nfc_tgt->supported_protocols = NFC_PROTO_NFC_DEP_MASK;
+ break;
+ case PN533_TYPE_A_SEL_PROT_ISO14443_DEP:
+ nfc_tgt->supported_protocols = NFC_PROTO_ISO14443_MASK |
+ NFC_PROTO_NFC_DEP_MASK;
+ break;
+ }
+
+ nfc_tgt->sens_res = be16_to_cpu(tgt_type_a->sens_res);
+ nfc_tgt->sel_res = tgt_type_a->sel_res;
+ nfc_tgt->nfcid1_len = tgt_type_a->nfcid_len;
+ memcpy(nfc_tgt->nfcid1, tgt_type_a->nfcid_data, nfc_tgt->nfcid1_len);
+
+ return 0;
+}
+
+struct pn533_target_felica {
+ u8 pol_res;
+ u8 opcode;
+ u8 nfcid2[NFC_NFCID2_MAXSIZE];
+ u8 pad[8];
+ /* optional */
+ u8 syst_code[];
+} __packed;
+
+#define PN533_FELICA_SENSF_NFCID2_DEP_B1 0x01
+#define PN533_FELICA_SENSF_NFCID2_DEP_B2 0xFE
+
+static bool pn533_target_felica_is_valid(struct pn533_target_felica *felica,
+ int target_data_len)
+{
+ if (target_data_len < sizeof(struct pn533_target_felica))
+ return false;
+
+ if (felica->opcode != PN533_FELICA_OPC_SENSF_RES)
+ return false;
+
+ return true;
+}
+
+static int pn533_target_found_felica(struct nfc_target *nfc_tgt, u8 *tgt_data,
+ int tgt_data_len)
+{
+ struct pn533_target_felica *tgt_felica;
+
+ tgt_felica = (struct pn533_target_felica *)tgt_data;
+
+ if (!pn533_target_felica_is_valid(tgt_felica, tgt_data_len))
+ return -EPROTO;
+
+ if ((tgt_felica->nfcid2[0] == PN533_FELICA_SENSF_NFCID2_DEP_B1) &&
+ (tgt_felica->nfcid2[1] == PN533_FELICA_SENSF_NFCID2_DEP_B2))
+ nfc_tgt->supported_protocols = NFC_PROTO_NFC_DEP_MASK;
+ else
+ nfc_tgt->supported_protocols = NFC_PROTO_FELICA_MASK;
+
+ memcpy(nfc_tgt->sensf_res, &tgt_felica->opcode, 9);
+ nfc_tgt->sensf_res_len = 9;
+
+ memcpy(nfc_tgt->nfcid2, tgt_felica->nfcid2, NFC_NFCID2_MAXSIZE);
+ nfc_tgt->nfcid2_len = NFC_NFCID2_MAXSIZE;
+
+ return 0;
+}
+
+struct pn533_target_jewel {
+ __be16 sens_res;
+ u8 jewelid[4];
+} __packed;
+
+static bool pn533_target_jewel_is_valid(struct pn533_target_jewel *jewel,
+ int target_data_len)
+{
+ u8 ssd;
+ u8 platconf;
+
+ if (target_data_len < sizeof(struct pn533_target_jewel))
+ return false;
+
+ /* Requirement 4.6.3.3 from NFC Forum Digital Spec */
+ ssd = PN533_TYPE_A_SENS_RES_SSD(jewel->sens_res);
+ platconf = PN533_TYPE_A_SENS_RES_PLATCONF(jewel->sens_res);
+
+ if ((ssd == PN533_TYPE_A_SENS_RES_SSD_JEWEL &&
+ platconf != PN533_TYPE_A_SENS_RES_PLATCONF_JEWEL) ||
+ (ssd != PN533_TYPE_A_SENS_RES_SSD_JEWEL &&
+ platconf == PN533_TYPE_A_SENS_RES_PLATCONF_JEWEL))
+ return false;
+
+ return true;
+}
+
+static int pn533_target_found_jewel(struct nfc_target *nfc_tgt, u8 *tgt_data,
+ int tgt_data_len)
+{
+ struct pn533_target_jewel *tgt_jewel;
+
+ tgt_jewel = (struct pn533_target_jewel *)tgt_data;
+
+ if (!pn533_target_jewel_is_valid(tgt_jewel, tgt_data_len))
+ return -EPROTO;
+
+ nfc_tgt->supported_protocols = NFC_PROTO_JEWEL_MASK;
+ nfc_tgt->sens_res = be16_to_cpu(tgt_jewel->sens_res);
+ nfc_tgt->nfcid1_len = 4;
+ memcpy(nfc_tgt->nfcid1, tgt_jewel->jewelid, nfc_tgt->nfcid1_len);
+
+ return 0;
+}
+
+struct pn533_type_b_prot_info {
+ u8 bitrate;
+ u8 fsci_type;
+ u8 fwi_adc_fo;
+} __packed;
+
+#define PN533_TYPE_B_PROT_FCSI(x) (((x) & 0xF0) >> 4)
+#define PN533_TYPE_B_PROT_TYPE(x) (((x) & 0x0F) >> 0)
+#define PN533_TYPE_B_PROT_TYPE_RFU_MASK 0x8
+
+struct pn533_type_b_sens_res {
+ u8 opcode;
+ u8 nfcid[4];
+ u8 appdata[4];
+ struct pn533_type_b_prot_info prot_info;
+} __packed;
+
+#define PN533_TYPE_B_OPC_SENSB_RES 0x50
+
+struct pn533_target_type_b {
+ struct pn533_type_b_sens_res sensb_res;
+ u8 attrib_res_len;
+ u8 attrib_res[];
+} __packed;
+
+static bool pn533_target_type_b_is_valid(struct pn533_target_type_b *type_b,
+ int target_data_len)
+{
+ if (target_data_len < sizeof(struct pn533_target_type_b))
+ return false;
+
+ if (type_b->sensb_res.opcode != PN533_TYPE_B_OPC_SENSB_RES)
+ return false;
+
+ if (PN533_TYPE_B_PROT_TYPE(type_b->sensb_res.prot_info.fsci_type) &
+ PN533_TYPE_B_PROT_TYPE_RFU_MASK)
+ return false;
+
+ return true;
+}
+
+static int pn533_target_found_type_b(struct nfc_target *nfc_tgt, u8 *tgt_data,
+ int tgt_data_len)
+{
+ struct pn533_target_type_b *tgt_type_b;
+
+ tgt_type_b = (struct pn533_target_type_b *)tgt_data;
+
+ if (!pn533_target_type_b_is_valid(tgt_type_b, tgt_data_len))
+ return -EPROTO;
+
+ nfc_tgt->supported_protocols = NFC_PROTO_ISO14443_B_MASK;
+
+ return 0;
+}
+
+static void pn533_poll_reset_mod_list(struct pn533 *dev);
+static int pn533_target_found(struct pn533 *dev, u8 tg, u8 *tgdata,
+ int tgdata_len)
+{
+ struct nfc_target nfc_tgt;
+ int rc;
+
+ dev_dbg(dev->dev, "%s: modulation=%d\n",
+ __func__, dev->poll_mod_curr);
+
+ if (tg != 1)
+ return -EPROTO;
+
+ memset(&nfc_tgt, 0, sizeof(struct nfc_target));
+
+ switch (dev->poll_mod_curr) {
+ case PN533_POLL_MOD_106KBPS_A:
+ rc = pn533_target_found_type_a(&nfc_tgt, tgdata, tgdata_len);
+ break;
+ case PN533_POLL_MOD_212KBPS_FELICA:
+ case PN533_POLL_MOD_424KBPS_FELICA:
+ rc = pn533_target_found_felica(&nfc_tgt, tgdata, tgdata_len);
+ break;
+ case PN533_POLL_MOD_106KBPS_JEWEL:
+ rc = pn533_target_found_jewel(&nfc_tgt, tgdata, tgdata_len);
+ break;
+ case PN533_POLL_MOD_847KBPS_B:
+ rc = pn533_target_found_type_b(&nfc_tgt, tgdata, tgdata_len);
+ break;
+ default:
+ nfc_err(dev->dev,
+ "Unknown current poll modulation\n");
+ return -EPROTO;
+ }
+
+ if (rc)
+ return rc;
+
+ if (!(nfc_tgt.supported_protocols & dev->poll_protocols)) {
+ dev_dbg(dev->dev,
+ "The Tg found doesn't have the desired protocol\n");
+ return -EAGAIN;
+ }
+
+ dev_dbg(dev->dev,
+ "Target found - supported protocols: 0x%x\n",
+ nfc_tgt.supported_protocols);
+
+ dev->tgt_available_prots = nfc_tgt.supported_protocols;
+
+ pn533_poll_reset_mod_list(dev);
+ nfc_targets_found(dev->nfc_dev, &nfc_tgt, 1);
+
+ return 0;
+}
+
+static inline void pn533_poll_next_mod(struct pn533 *dev)
+{
+ dev->poll_mod_curr = (dev->poll_mod_curr + 1) % dev->poll_mod_count;
+}
+
+static void pn533_poll_reset_mod_list(struct pn533 *dev)
+{
+ dev->poll_mod_count = 0;
+}
+
+static void pn533_poll_add_mod(struct pn533 *dev, u8 mod_index)
+{
+ dev->poll_mod_active[dev->poll_mod_count] =
+ (struct pn533_poll_modulations *)&poll_mod[mod_index];
+ dev->poll_mod_count++;
+}
+
+static void pn533_poll_create_mod_list(struct pn533 *dev,
+ u32 im_protocols, u32 tm_protocols)
+{
+ pn533_poll_reset_mod_list(dev);
+
+ if ((im_protocols & NFC_PROTO_MIFARE_MASK) ||
+ (im_protocols & NFC_PROTO_ISO14443_MASK) ||
+ (im_protocols & NFC_PROTO_NFC_DEP_MASK))
+ pn533_poll_add_mod(dev, PN533_POLL_MOD_106KBPS_A);
+
+ if (im_protocols & NFC_PROTO_FELICA_MASK ||
+ im_protocols & NFC_PROTO_NFC_DEP_MASK) {
+ pn533_poll_add_mod(dev, PN533_POLL_MOD_212KBPS_FELICA);
+ pn533_poll_add_mod(dev, PN533_POLL_MOD_424KBPS_FELICA);
+ }
+
+ if (im_protocols & NFC_PROTO_JEWEL_MASK)
+ pn533_poll_add_mod(dev, PN533_POLL_MOD_106KBPS_JEWEL);
+
+ if (im_protocols & NFC_PROTO_ISO14443_B_MASK)
+ pn533_poll_add_mod(dev, PN533_POLL_MOD_847KBPS_B);
+
+ if (tm_protocols)
+ pn533_poll_add_mod(dev, PN533_LISTEN_MOD);
+}
+
+static int pn533_start_poll_complete(struct pn533 *dev, struct sk_buff *resp)
+{
+ u8 nbtg, tg, *tgdata;
+ int rc, tgdata_len;
+
+ /* Toggle the DEP polling */
+ if (dev->poll_protocols & NFC_PROTO_NFC_DEP_MASK)
+ dev->poll_dep = 1;
+
+ nbtg = resp->data[0];
+ tg = resp->data[1];
+ tgdata = &resp->data[2];
+ tgdata_len = resp->len - 2; /* nbtg + tg */
+
+ if (nbtg) {
+ rc = pn533_target_found(dev, tg, tgdata, tgdata_len);
+
+ /* We must stop the poll after a valid target found */
+ if (rc == 0)
+ return 0;
+ }
+
+ return -EAGAIN;
+}
+
+static struct sk_buff *pn533_alloc_poll_tg_frame(struct pn533 *dev)
+{
+ struct sk_buff *skb;
+ u8 *felica, *nfcid3, *gb;
+
+ u8 *gbytes = dev->gb;
+ size_t gbytes_len = dev->gb_len;
+
+ u8 felica_params[18] = {0x1, 0xfe, /* DEP */
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, /* random */
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xff, 0xff}; /* System code */
+
+ u8 mifare_params[6] = {0x1, 0x1, /* SENS_RES */
+ 0x0, 0x0, 0x0,
+ 0x40}; /* SEL_RES for DEP */
+
+ unsigned int skb_len = 36 + /*
+ * mode (1), mifare (6),
+ * felica (18), nfcid3 (10), gb_len (1)
+ */
+ gbytes_len +
+ 1; /* len Tk*/
+
+ skb = pn533_alloc_skb(dev, skb_len);
+ if (!skb)
+ return NULL;
+
+ /* DEP support only */
+ *skb_put(skb, 1) = PN533_INIT_TARGET_DEP;
+
+ /* MIFARE params */
+ memcpy(skb_put(skb, 6), mifare_params, 6);
+
+ /* Felica params */
+ felica = skb_put(skb, 18);
+ memcpy(felica, felica_params, 18);
+ get_random_bytes(felica + 2, 6);
+
+ /* NFCID3 */
+ nfcid3 = skb_put(skb, 10);
+ memset(nfcid3, 0, 10);
+ memcpy(nfcid3, felica, 8);
+
+ /* General bytes */
+ *skb_put(skb, 1) = gbytes_len;
+
+ gb = skb_put(skb, gbytes_len);
+ memcpy(gb, gbytes, gbytes_len);
+
+ /* Len Tk */
+ *skb_put(skb, 1) = 0;
+
+ return skb;
+}
+
+static void pn533_wq_tm_mi_recv(struct work_struct *work);
+static struct sk_buff *pn533_build_response(struct pn533 *dev);
+
+static int pn533_tm_get_data_complete(struct pn533 *dev, void *arg,
+ struct sk_buff *resp)
+{
+ struct sk_buff *skb;
+ u8 status, ret, mi;
+ int rc;
+
+ dev_dbg(dev->dev, "%s\n", __func__);
+
+ if (IS_ERR(resp)) {
+ skb_queue_purge(&dev->resp_q);
+ return PTR_ERR(resp);
+ }
+
+ status = resp->data[0];
+
+ ret = status & PN533_CMD_RET_MASK;
+ mi = status & PN533_CMD_MI_MASK;
+
+ skb_pull(resp, sizeof(status));
+
+ if (ret != PN533_CMD_RET_SUCCESS) {
+ rc = -EIO;
+ goto error;
+ }
+
+ skb_queue_tail(&dev->resp_q, resp);
+
+ if (mi) {
+ queue_work(dev->wq, &dev->mi_tm_rx_work);
+ return -EINPROGRESS;
+ }
+
+ skb = pn533_build_response(dev);
+ if (!skb) {
+ rc = -EIO;
+ goto error;
+ }
+
+ return nfc_tm_data_received(dev->nfc_dev, skb);
+
+error:
+ nfc_tm_deactivated(dev->nfc_dev);
+ dev->tgt_mode = 0;
+ skb_queue_purge(&dev->resp_q);
+ dev_kfree_skb(resp);
+
+ return rc;
+}
+
+static void pn533_wq_tm_mi_recv(struct work_struct *work)
+{
+ struct pn533 *dev = container_of(work, struct pn533, mi_tm_rx_work);
+ struct sk_buff *skb;
+ int rc;
+
+ dev_dbg(dev->dev, "%s\n", __func__);
+
+ skb = pn533_alloc_skb(dev, 0);
+ if (!skb)
+ return;
+
+ rc = pn533_send_cmd_direct_async(dev,
+ PN533_CMD_TG_GET_DATA,
+ skb,
+ pn533_tm_get_data_complete,
+ NULL);
+
+ if (rc < 0)
+ dev_kfree_skb(skb);
+}
+
+static int pn533_tm_send_complete(struct pn533 *dev, void *arg,
+ struct sk_buff *resp);
+static void pn533_wq_tm_mi_send(struct work_struct *work)
+{
+ struct pn533 *dev = container_of(work, struct pn533, mi_tm_tx_work);
+ struct sk_buff *skb;
+ int rc;
+
+ dev_dbg(dev->dev, "%s\n", __func__);
+
+ /* Grab the first skb in the queue */
+ skb = skb_dequeue(&dev->fragment_skb);
+ if (skb == NULL) { /* No more data */
+ /* Reset the queue for future use */
+ skb_queue_head_init(&dev->fragment_skb);
+ goto error;
+ }
+
+ /* last entry - remove MI bit */
+ if (skb_queue_len(&dev->fragment_skb) == 0) {
+ rc = pn533_send_cmd_direct_async(dev, PN533_CMD_TG_SET_DATA,
+ skb, pn533_tm_send_complete, NULL);
+ } else
+ rc = pn533_send_cmd_direct_async(dev,
+ PN533_CMD_TG_SET_META_DATA,
+ skb, pn533_tm_send_complete, NULL);
+
+ if (rc == 0) /* success */
+ return;
+
+ dev_err(dev->dev,
+ "Error %d when trying to perform set meta data_exchange", rc);
+
+ dev_kfree_skb(skb);
+
+error:
+ dev->phy_ops->send_ack(dev, GFP_KERNEL);
+ queue_work(dev->wq, &dev->cmd_work);
+}
+
+static void pn533_wq_tg_get_data(struct work_struct *work)
+{
+ struct pn533 *dev = container_of(work, struct pn533, tg_work);
+ struct sk_buff *skb;
+ int rc;
+
+ dev_dbg(dev->dev, "%s\n", __func__);
+
+ skb = pn533_alloc_skb(dev, 0);
+ if (!skb)
+ return;
+
+ rc = pn533_send_data_async(dev, PN533_CMD_TG_GET_DATA, skb,
+ pn533_tm_get_data_complete, NULL);
+
+ if (rc < 0)
+ dev_kfree_skb(skb);
+}
+
+#define ATR_REQ_GB_OFFSET 17
+static int pn533_init_target_complete(struct pn533 *dev, struct sk_buff *resp)
+{
+ u8 mode, *cmd, comm_mode = NFC_COMM_PASSIVE, *gb;
+ size_t gb_len;
+ int rc;
+
+ dev_dbg(dev->dev, "%s\n", __func__);
+
+ if (resp->len < ATR_REQ_GB_OFFSET + 1)
+ return -EINVAL;
+
+ mode = resp->data[0];
+ cmd = &resp->data[1];
+
+ dev_dbg(dev->dev, "Target mode 0x%x len %d\n",
+ mode, resp->len);
+
+ if ((mode & PN533_INIT_TARGET_RESP_FRAME_MASK) ==
+ PN533_INIT_TARGET_RESP_ACTIVE)
+ comm_mode = NFC_COMM_ACTIVE;
+
+ if ((mode & PN533_INIT_TARGET_RESP_DEP) == 0) /* Only DEP supported */
+ return -EOPNOTSUPP;
+
+ gb = cmd + ATR_REQ_GB_OFFSET;
+ gb_len = resp->len - (ATR_REQ_GB_OFFSET + 1);
+
+ rc = nfc_tm_activated(dev->nfc_dev, NFC_PROTO_NFC_DEP_MASK,
+ comm_mode, gb, gb_len);
+ if (rc < 0) {
+ nfc_err(dev->dev,
+ "Error when signaling target activation\n");
+ return rc;
+ }
+
+ dev->tgt_mode = 1;
+ queue_work(dev->wq, &dev->tg_work);
+
+ return 0;
+}
+
+static void pn533_listen_mode_timer(unsigned long data)
+{
+ struct pn533 *dev = (struct pn533 *)data;
+
+ dev_dbg(dev->dev, "Listen mode timeout\n");
+
+ dev->cancel_listen = 1;
+
+ pn533_poll_next_mod(dev);
+
+ queue_delayed_work(dev->wq, &dev->poll_work,
+ msecs_to_jiffies(PN533_POLL_INTERVAL));
+}
+
+static int pn533_rf_complete(struct pn533 *dev, void *arg,
+ struct sk_buff *resp)
+{
+ int rc = 0;
+
+ dev_dbg(dev->dev, "%s\n", __func__);
+
+ if (IS_ERR(resp)) {
+ rc = PTR_ERR(resp);
+
+ nfc_err(dev->dev, "RF setting error %d\n", rc);
+
+ return rc;
+ }
+
+ queue_delayed_work(dev->wq, &dev->poll_work,
+ msecs_to_jiffies(PN533_POLL_INTERVAL));
+
+ dev_kfree_skb(resp);
+ return rc;
+}
+
+static void pn533_wq_rf(struct work_struct *work)
+{
+ struct pn533 *dev = container_of(work, struct pn533, rf_work);
+ struct sk_buff *skb;
+ int rc;
+
+ dev_dbg(dev->dev, "%s\n", __func__);
+
+ skb = pn533_alloc_skb(dev, 2);
+ if (!skb)
+ return;
+
+ *skb_put(skb, 1) = PN533_CFGITEM_RF_FIELD;
+ *skb_put(skb, 1) = PN533_CFGITEM_RF_FIELD_AUTO_RFCA;
+
+ rc = pn533_send_cmd_async(dev, PN533_CMD_RF_CONFIGURATION, skb,
+ pn533_rf_complete, NULL);
+ if (rc < 0) {
+ dev_kfree_skb(skb);
+ nfc_err(dev->dev, "RF setting error %d\n", rc);
+ }
+}
+
+static int pn533_poll_dep_complete(struct pn533 *dev, void *arg,
+ struct sk_buff *resp)
+{
+ struct pn533_cmd_jump_dep_response *rsp;
+ struct nfc_target nfc_target;
+ u8 target_gt_len;
+ int rc;
+
+ if (IS_ERR(resp))
+ return PTR_ERR(resp);
+
+ rsp = (struct pn533_cmd_jump_dep_response *)resp->data;
+
+ rc = rsp->status & PN533_CMD_RET_MASK;
+ if (rc != PN533_CMD_RET_SUCCESS) {
+ /* Not target found, turn radio off */
+ queue_work(dev->wq, &dev->rf_work);
+
+ dev_kfree_skb(resp);
+ return 0;
+ }
+
+ dev_dbg(dev->dev, "Creating new target");
+
+ nfc_target.supported_protocols = NFC_PROTO_NFC_DEP_MASK;
+ nfc_target.nfcid1_len = 10;
+ memcpy(nfc_target.nfcid1, rsp->nfcid3t, nfc_target.nfcid1_len);
+ rc = nfc_targets_found(dev->nfc_dev, &nfc_target, 1);
+ if (rc)
+ goto error;
+
+ dev->tgt_available_prots = 0;
+ dev->tgt_active_prot = NFC_PROTO_NFC_DEP;
+
+ /* ATR_RES general bytes are located at offset 17 */
+ target_gt_len = resp->len - 17;
+ rc = nfc_set_remote_general_bytes(dev->nfc_dev,
+ rsp->gt, target_gt_len);
+ if (!rc) {
+ rc = nfc_dep_link_is_up(dev->nfc_dev,
+ dev->nfc_dev->targets[0].idx,
+ 0, NFC_RF_INITIATOR);
+
+ if (!rc)
+ pn533_poll_reset_mod_list(dev);
+ }
+error:
+ dev_kfree_skb(resp);
+ return rc;
+}
+
+#define PASSIVE_DATA_LEN 5
+static int pn533_poll_dep(struct nfc_dev *nfc_dev)
+{
+ struct pn533 *dev = nfc_get_drvdata(nfc_dev);
+ struct sk_buff *skb;
+ int rc, skb_len;
+ u8 *next, nfcid3[NFC_NFCID3_MAXSIZE];
+ u8 passive_data[PASSIVE_DATA_LEN] = {0x00, 0xff, 0xff, 0x00, 0x3};
+
+ dev_dbg(dev->dev, "%s", __func__);
+
+ if (!dev->gb) {
+ dev->gb = nfc_get_local_general_bytes(nfc_dev, &dev->gb_len);
+
+ if (!dev->gb || !dev->gb_len) {
+ dev->poll_dep = 0;
+ queue_work(dev->wq, &dev->rf_work);
+ }
+ }
+
+ skb_len = 3 + dev->gb_len; /* ActPass + BR + Next */
+ skb_len += PASSIVE_DATA_LEN;
+
+ /* NFCID3 */
+ skb_len += NFC_NFCID3_MAXSIZE;
+ nfcid3[0] = 0x1;
+ nfcid3[1] = 0xfe;
+ get_random_bytes(nfcid3 + 2, 6);
+
+ skb = pn533_alloc_skb(dev, skb_len);
+ if (!skb)
+ return -ENOMEM;
+
+ *skb_put(skb, 1) = 0x01; /* Active */
+ *skb_put(skb, 1) = 0x02; /* 424 kbps */
+
+ next = skb_put(skb, 1); /* Next */
+ *next = 0;
+
+ /* Copy passive data */
+ memcpy(skb_put(skb, PASSIVE_DATA_LEN), passive_data, PASSIVE_DATA_LEN);
+ *next |= 1;
+
+ /* Copy NFCID3 (which is NFCID2 from SENSF_RES) */
+ memcpy(skb_put(skb, NFC_NFCID3_MAXSIZE), nfcid3,
+ NFC_NFCID3_MAXSIZE);
+ *next |= 2;
+
+ memcpy(skb_put(skb, dev->gb_len), dev->gb, dev->gb_len);
+ *next |= 4; /* We have some Gi */
+
+ rc = pn533_send_cmd_async(dev, PN533_CMD_IN_JUMP_FOR_DEP, skb,
+ pn533_poll_dep_complete, NULL);
+
+ if (rc < 0)
+ dev_kfree_skb(skb);
+
+ return rc;
+}
+
+static int pn533_poll_complete(struct pn533 *dev, void *arg,
+ struct sk_buff *resp)
+{
+ struct pn533_poll_modulations *cur_mod;
+ int rc;
+
+ dev_dbg(dev->dev, "%s\n", __func__);
+
+ if (IS_ERR(resp)) {
+ rc = PTR_ERR(resp);
+
+ nfc_err(dev->dev, "%s Poll complete error %d\n",
+ __func__, rc);
+
+ if (rc == -ENOENT) {
+ if (dev->poll_mod_count != 0)
+ return rc;
+ goto stop_poll;
+ } else if (rc < 0) {
+ nfc_err(dev->dev,
+ "Error %d when running poll\n", rc);
+ goto stop_poll;
+ }
+ }
+
+ cur_mod = dev->poll_mod_active[dev->poll_mod_curr];
+
+ if (cur_mod->len == 0) { /* Target mode */
+ del_timer(&dev->listen_timer);
+ rc = pn533_init_target_complete(dev, resp);
+ goto done;
+ }
+
+ /* Initiator mode */
+ rc = pn533_start_poll_complete(dev, resp);
+ if (!rc)
+ goto done;
+
+ if (!dev->poll_mod_count) {
+ dev_dbg(dev->dev, "Polling has been stopped\n");
+ goto done;
+ }
+
+ pn533_poll_next_mod(dev);
+ /* Not target found, turn radio off */
+ queue_work(dev->wq, &dev->rf_work);
+
+done:
+ dev_kfree_skb(resp);
+ return rc;
+
+stop_poll:
+ nfc_err(dev->dev, "Polling operation has been stopped\n");
+
+ pn533_poll_reset_mod_list(dev);
+ dev->poll_protocols = 0;
+ return rc;
+}
+
+static struct sk_buff *pn533_alloc_poll_in_frame(struct pn533 *dev,
+ struct pn533_poll_modulations *mod)
+{
+ struct sk_buff *skb;
+
+ skb = pn533_alloc_skb(dev, mod->len);
+ if (!skb)
+ return NULL;
+
+ memcpy(skb_put(skb, mod->len), &mod->data, mod->len);
+
+ return skb;
+}
+
+static int pn533_send_poll_frame(struct pn533 *dev)
+{
+ struct pn533_poll_modulations *mod;
+ struct sk_buff *skb;
+ int rc;
+ u8 cmd_code;
+
+ mod = dev->poll_mod_active[dev->poll_mod_curr];
+
+ dev_dbg(dev->dev, "%s mod len %d\n",
+ __func__, mod->len);
+
+ if ((dev->poll_protocols & NFC_PROTO_NFC_DEP_MASK) && dev->poll_dep) {
+ dev->poll_dep = 0;
+ return pn533_poll_dep(dev->nfc_dev);
+ }
+
+ if (mod->len == 0) { /* Listen mode */
+ cmd_code = PN533_CMD_TG_INIT_AS_TARGET;
+ skb = pn533_alloc_poll_tg_frame(dev);
+ } else { /* Polling mode */
+ cmd_code = PN533_CMD_IN_LIST_PASSIVE_TARGET;
+ skb = pn533_alloc_poll_in_frame(dev, mod);
+ }
+
+ if (!skb) {
+ nfc_err(dev->dev, "Failed to allocate skb\n");
+ return -ENOMEM;
+ }
+
+ rc = pn533_send_cmd_async(dev, cmd_code, skb, pn533_poll_complete,
+ NULL);
+ if (rc < 0) {
+ dev_kfree_skb(skb);
+ nfc_err(dev->dev, "Polling loop error %d\n", rc);
+ }
+
+ return rc;
+}
+
+static void pn533_wq_poll(struct work_struct *work)
+{
+ struct pn533 *dev = container_of(work, struct pn533, poll_work.work);
+ struct pn533_poll_modulations *cur_mod;
+ int rc;
+
+ cur_mod = dev->poll_mod_active[dev->poll_mod_curr];
+
+ dev_dbg(dev->dev,
+ "%s cancel_listen %d modulation len %d\n",
+ __func__, dev->cancel_listen, cur_mod->len);
+
+ if (dev->cancel_listen == 1) {
+ dev->cancel_listen = 0;
+ dev->phy_ops->abort_cmd(dev, GFP_ATOMIC);
+ }
+
+ rc = pn533_send_poll_frame(dev);
+ if (rc)
+ return;
+
+ if (cur_mod->len == 0 && dev->poll_mod_count > 1)
+ mod_timer(&dev->listen_timer, jiffies + PN533_LISTEN_TIME * HZ);
+}
+
+static int pn533_start_poll(struct nfc_dev *nfc_dev,
+ u32 im_protocols, u32 tm_protocols)
+{
+ struct pn533 *dev = nfc_get_drvdata(nfc_dev);
+ struct pn533_poll_modulations *cur_mod;
+ u8 rand_mod;
+ int rc;
+
+ dev_dbg(dev->dev,
+ "%s: im protocols 0x%x tm protocols 0x%x\n",
+ __func__, im_protocols, tm_protocols);
+
+ if (dev->tgt_active_prot) {
+ nfc_err(dev->dev,
+ "Cannot poll with a target already activated\n");
+ return -EBUSY;
+ }
+
+ if (dev->tgt_mode) {
+ nfc_err(dev->dev,
+ "Cannot poll while already being activated\n");
+ return -EBUSY;
+ }
+
+ if (tm_protocols) {
+ dev->gb = nfc_get_local_general_bytes(nfc_dev, &dev->gb_len);
+ if (dev->gb == NULL)
+ tm_protocols = 0;
+ }
+
+ pn533_poll_create_mod_list(dev, im_protocols, tm_protocols);
+ dev->poll_protocols = im_protocols;
+ dev->listen_protocols = tm_protocols;
+
+ /* Do not always start polling from the same modulation */
+ get_random_bytes(&rand_mod, sizeof(rand_mod));
+ rand_mod %= dev->poll_mod_count;
+ dev->poll_mod_curr = rand_mod;
+
+ cur_mod = dev->poll_mod_active[dev->poll_mod_curr];
+
+ rc = pn533_send_poll_frame(dev);
+
+ /* Start listen timer */
+ if (!rc && cur_mod->len == 0 && dev->poll_mod_count > 1)
+ mod_timer(&dev->listen_timer, jiffies + PN533_LISTEN_TIME * HZ);
+
+ return rc;
+}
+
+static void pn533_stop_poll(struct nfc_dev *nfc_dev)
+{
+ struct pn533 *dev = nfc_get_drvdata(nfc_dev);
+
+ del_timer(&dev->listen_timer);
+
+ if (!dev->poll_mod_count) {
+ dev_dbg(dev->dev,
+ "Polling operation was not running\n");
+ return;
+ }
+
+ dev->phy_ops->abort_cmd(dev, GFP_KERNEL);
+ flush_delayed_work(&dev->poll_work);
+ pn533_poll_reset_mod_list(dev);
+}
+
+static int pn533_activate_target_nfcdep(struct pn533 *dev)
+{
+ struct pn533_cmd_activate_response *rsp;
+ u16 gt_len;
+ int rc;
+ struct sk_buff *skb;
+ struct sk_buff *resp;
+
+ dev_dbg(dev->dev, "%s\n", __func__);
+
+ skb = pn533_alloc_skb(dev, sizeof(u8) * 2); /*TG + Next*/
+ if (!skb)
+ return -ENOMEM;
+
+ *skb_put(skb, sizeof(u8)) = 1; /* TG */
+ *skb_put(skb, sizeof(u8)) = 0; /* Next */
+
+ resp = pn533_send_cmd_sync(dev, PN533_CMD_IN_ATR, skb);
+ if (IS_ERR(resp))
+ return PTR_ERR(resp);
+
+ rsp = (struct pn533_cmd_activate_response *)resp->data;
+ rc = rsp->status & PN533_CMD_RET_MASK;
+ if (rc != PN533_CMD_RET_SUCCESS) {
+ nfc_err(dev->dev,
+ "Target activation failed (error 0x%x)\n", rc);
+ dev_kfree_skb(resp);
+ return -EIO;
+ }
+
+ /* ATR_RES general bytes are located at offset 16 */
+ gt_len = resp->len - 16;
+ rc = nfc_set_remote_general_bytes(dev->nfc_dev, rsp->gt, gt_len);
+
+ dev_kfree_skb(resp);
+ return rc;
+}
+
+static int pn533_activate_target(struct nfc_dev *nfc_dev,
+ struct nfc_target *target, u32 protocol)
+{
+ struct pn533 *dev = nfc_get_drvdata(nfc_dev);
+ int rc;
+
+ dev_dbg(dev->dev, "%s: protocol=%u\n", __func__, protocol);
+
+ if (dev->poll_mod_count) {
+ nfc_err(dev->dev,
+ "Cannot activate while polling\n");
+ return -EBUSY;
+ }
+
+ if (dev->tgt_active_prot) {
+ nfc_err(dev->dev,
+ "There is already an active target\n");
+ return -EBUSY;
+ }
+
+ if (!dev->tgt_available_prots) {
+ nfc_err(dev->dev,
+ "There is no available target to activate\n");
+ return -EINVAL;
+ }
+
+ if (!(dev->tgt_available_prots & (1 << protocol))) {
+ nfc_err(dev->dev,
+ "Target doesn't support requested proto %u\n",
+ protocol);
+ return -EINVAL;
+ }
+
+ if (protocol == NFC_PROTO_NFC_DEP) {
+ rc = pn533_activate_target_nfcdep(dev);
+ if (rc) {
+ nfc_err(dev->dev,
+ "Activating target with DEP failed %d\n", rc);
+ return rc;
+ }
+ }
+
+ dev->tgt_active_prot = protocol;
+ dev->tgt_available_prots = 0;
+
+ return 0;
+}
+
+static int pn533_deactivate_target_complete(struct pn533 *dev, void *arg,
+ struct sk_buff *resp)
+{
+ int rc = 0;
+
+ dev_dbg(dev->dev, "%s\n", __func__);
+
+ if (IS_ERR(resp)) {
+ rc = PTR_ERR(resp);
+
+ nfc_err(dev->dev, "Target release error %d\n", rc);
+
+ return rc;
+ }
+
+ rc = resp->data[0] & PN533_CMD_RET_MASK;
+ if (rc != PN533_CMD_RET_SUCCESS)
+ nfc_err(dev->dev,
+ "Error 0x%x when releasing the target\n", rc);
+
+ dev_kfree_skb(resp);
+ return rc;
+}
+
+static void pn533_deactivate_target(struct nfc_dev *nfc_dev,
+ struct nfc_target *target, u8 mode)
+{
+ struct pn533 *dev = nfc_get_drvdata(nfc_dev);
+ struct sk_buff *skb;
+ int rc;
+
+ dev_dbg(dev->dev, "%s\n", __func__);
+
+ if (!dev->tgt_active_prot) {
+ nfc_err(dev->dev, "There is no active target\n");
+ return;
+ }
+
+ dev->tgt_active_prot = 0;
+ skb_queue_purge(&dev->resp_q);
+
+ skb = pn533_alloc_skb(dev, sizeof(u8));
+ if (!skb)
+ return;
+
+ *skb_put(skb, 1) = 1; /* TG*/
+
+ rc = pn533_send_cmd_async(dev, PN533_CMD_IN_RELEASE, skb,
+ pn533_deactivate_target_complete, NULL);
+ if (rc < 0) {
+ dev_kfree_skb(skb);
+ nfc_err(dev->dev, "Target release error %d\n", rc);
+ }
+}
+
+
+static int pn533_in_dep_link_up_complete(struct pn533 *dev, void *arg,
+ struct sk_buff *resp)
+{
+ struct pn533_cmd_jump_dep_response *rsp;
+ u8 target_gt_len;
+ int rc;
+ u8 active = *(u8 *)arg;
+
+ kfree(arg);
+
+ if (IS_ERR(resp))
+ return PTR_ERR(resp);
+
+ if (dev->tgt_available_prots &&
+ !(dev->tgt_available_prots & (1 << NFC_PROTO_NFC_DEP))) {
+ nfc_err(dev->dev,
+ "The target does not support DEP\n");
+ rc = -EINVAL;
+ goto error;
+ }
+
+ rsp = (struct pn533_cmd_jump_dep_response *)resp->data;
+
+ rc = rsp->status & PN533_CMD_RET_MASK;
+ if (rc != PN533_CMD_RET_SUCCESS) {
+ nfc_err(dev->dev,
+ "Bringing DEP link up failed (error 0x%x)\n", rc);
+ goto error;
+ }
+
+ if (!dev->tgt_available_prots) {
+ struct nfc_target nfc_target;
+
+ dev_dbg(dev->dev, "Creating new target\n");
+
+ nfc_target.supported_protocols = NFC_PROTO_NFC_DEP_MASK;
+ nfc_target.nfcid1_len = 10;
+ memcpy(nfc_target.nfcid1, rsp->nfcid3t, nfc_target.nfcid1_len);
+ rc = nfc_targets_found(dev->nfc_dev, &nfc_target, 1);
+ if (rc)
+ goto error;
+
+ dev->tgt_available_prots = 0;
+ }
+
+ dev->tgt_active_prot = NFC_PROTO_NFC_DEP;
+
+ /* ATR_RES general bytes are located at offset 17 */
+ target_gt_len = resp->len - 17;
+ rc = nfc_set_remote_general_bytes(dev->nfc_dev,
+ rsp->gt, target_gt_len);
+ if (rc == 0)
+ rc = nfc_dep_link_is_up(dev->nfc_dev,
+ dev->nfc_dev->targets[0].idx,
+ !active, NFC_RF_INITIATOR);
+
+error:
+ dev_kfree_skb(resp);
+ return rc;
+}
+
+static int pn533_rf_field(struct nfc_dev *nfc_dev, u8 rf);
+static int pn533_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target,
+ u8 comm_mode, u8 *gb, size_t gb_len)
+{
+ struct pn533 *dev = nfc_get_drvdata(nfc_dev);
+ struct sk_buff *skb;
+ int rc, skb_len;
+ u8 *next, *arg, nfcid3[NFC_NFCID3_MAXSIZE];
+ u8 passive_data[PASSIVE_DATA_LEN] = {0x00, 0xff, 0xff, 0x00, 0x3};
+
+ dev_dbg(dev->dev, "%s\n", __func__);
+
+ if (dev->poll_mod_count) {
+ nfc_err(dev->dev,
+ "Cannot bring the DEP link up while polling\n");
+ return -EBUSY;
+ }
+
+ if (dev->tgt_active_prot) {
+ nfc_err(dev->dev,
+ "There is already an active target\n");
+ return -EBUSY;
+ }
+
+ skb_len = 3 + gb_len; /* ActPass + BR + Next */
+ skb_len += PASSIVE_DATA_LEN;
+
+ /* NFCID3 */
+ skb_len += NFC_NFCID3_MAXSIZE;
+ if (target && !target->nfcid2_len) {
+ nfcid3[0] = 0x1;
+ nfcid3[1] = 0xfe;
+ get_random_bytes(nfcid3 + 2, 6);
+ }
+
+ skb = pn533_alloc_skb(dev, skb_len);
+ if (!skb)
+ return -ENOMEM;
+
+ *skb_put(skb, 1) = !comm_mode; /* ActPass */
+ *skb_put(skb, 1) = 0x02; /* 424 kbps */
+
+ next = skb_put(skb, 1); /* Next */
+ *next = 0;
+
+ /* Copy passive data */
+ memcpy(skb_put(skb, PASSIVE_DATA_LEN), passive_data, PASSIVE_DATA_LEN);
+ *next |= 1;
+
+ /* Copy NFCID3 (which is NFCID2 from SENSF_RES) */
+ if (target && target->nfcid2_len)
+ memcpy(skb_put(skb, NFC_NFCID3_MAXSIZE), target->nfcid2,
+ target->nfcid2_len);
+ else
+ memcpy(skb_put(skb, NFC_NFCID3_MAXSIZE), nfcid3,
+ NFC_NFCID3_MAXSIZE);
+ *next |= 2;
+
+ if (gb != NULL && gb_len > 0) {
+ memcpy(skb_put(skb, gb_len), gb, gb_len);
+ *next |= 4; /* We have some Gi */
+ } else {
+ *next = 0;
+ }
+
+ arg = kmalloc(sizeof(*arg), GFP_KERNEL);
+ if (!arg) {
+ dev_kfree_skb(skb);
+ return -ENOMEM;
+ }
+
+ *arg = !comm_mode;
+
+ pn533_rf_field(dev->nfc_dev, 0);
+
+ rc = pn533_send_cmd_async(dev, PN533_CMD_IN_JUMP_FOR_DEP, skb,
+ pn533_in_dep_link_up_complete, arg);
+
+ if (rc < 0) {
+ dev_kfree_skb(skb);
+ kfree(arg);
+ }
+
+ return rc;
+}
+
+static int pn533_dep_link_down(struct nfc_dev *nfc_dev)
+{
+ struct pn533 *dev = nfc_get_drvdata(nfc_dev);
+
+ dev_dbg(dev->dev, "%s\n", __func__);
+
+ pn533_poll_reset_mod_list(dev);
+
+ if (dev->tgt_mode || dev->tgt_active_prot)
+ dev->phy_ops->abort_cmd(dev, GFP_KERNEL);
+
+ dev->tgt_active_prot = 0;
+ dev->tgt_mode = 0;
+
+ skb_queue_purge(&dev->resp_q);
+
+ return 0;
+}
+
+struct pn533_data_exchange_arg {
+ data_exchange_cb_t cb;
+ void *cb_context;
+};
+
+static struct sk_buff *pn533_build_response(struct pn533 *dev)
+{
+ struct sk_buff *skb, *tmp, *t;
+ unsigned int skb_len = 0, tmp_len = 0;
+
+ dev_dbg(dev->dev, "%s\n", __func__);
+
+ if (skb_queue_empty(&dev->resp_q))
+ return NULL;
+
+ if (skb_queue_len(&dev->resp_q) == 1) {
+ skb = skb_dequeue(&dev->resp_q);
+ goto out;
+ }
+
+ skb_queue_walk_safe(&dev->resp_q, tmp, t)
+ skb_len += tmp->len;
+
+ dev_dbg(dev->dev, "%s total length %d\n",
+ __func__, skb_len);
+
+ skb = alloc_skb(skb_len, GFP_KERNEL);
+ if (skb == NULL)
+ goto out;
+
+ skb_put(skb, skb_len);
+
+ skb_queue_walk_safe(&dev->resp_q, tmp, t) {
+ memcpy(skb->data + tmp_len, tmp->data, tmp->len);
+ tmp_len += tmp->len;
+ }
+
+out:
+ skb_queue_purge(&dev->resp_q);
+
+ return skb;
+}
+
+static int pn533_data_exchange_complete(struct pn533 *dev, void *_arg,
+ struct sk_buff *resp)
+{
+ struct pn533_data_exchange_arg *arg = _arg;
+ struct sk_buff *skb;
+ int rc = 0;
+ u8 status, ret, mi;
+
+ dev_dbg(dev->dev, "%s\n", __func__);
+
+ if (IS_ERR(resp)) {
+ rc = PTR_ERR(resp);
+ goto _error;
+ }
+
+ status = resp->data[0];
+ ret = status & PN533_CMD_RET_MASK;
+ mi = status & PN533_CMD_MI_MASK;
+
+ skb_pull(resp, sizeof(status));
+
+ if (ret != PN533_CMD_RET_SUCCESS) {
+ nfc_err(dev->dev,
+ "Exchanging data failed (error 0x%x)\n", ret);
+ rc = -EIO;
+ goto error;
+ }
+
+ skb_queue_tail(&dev->resp_q, resp);
+
+ if (mi) {
+ dev->cmd_complete_mi_arg = arg;
+ queue_work(dev->wq, &dev->mi_rx_work);
+ return -EINPROGRESS;
+ }
+
+ /* Prepare for the next round */
+ if (skb_queue_len(&dev->fragment_skb) > 0) {
+ dev->cmd_complete_dep_arg = arg;
+ queue_work(dev->wq, &dev->mi_tx_work);
+
+ return -EINPROGRESS;
+ }
+
+ skb = pn533_build_response(dev);
+ if (!skb) {
+ rc = -ENOMEM;
+ goto error;
+ }
+
+ arg->cb(arg->cb_context, skb, 0);
+ kfree(arg);
+ return 0;
+
+error:
+ dev_kfree_skb(resp);
+_error:
+ skb_queue_purge(&dev->resp_q);
+ arg->cb(arg->cb_context, NULL, rc);
+ kfree(arg);
+ return rc;
+}
+
+/*
+ * Receive an incoming pn533 frame. skb contains only header and payload.
+ * If skb == NULL, it is a notification that the link below is dead.
+ */
+void pn533_recv_frame(struct pn533 *dev, struct sk_buff *skb, int status)
+{
+ if (!dev->cmd)
+ goto sched_wq;
+
+ dev->cmd->status = status;
+
+ if (status != 0) {
+ dev_dbg(dev->dev, "%s: Error received: %d\n", __func__, status);
+ goto sched_wq;
+ }
+
+ if (skb == NULL) {
+ pr_err("NULL Frame -> link is dead\n");
+ goto sched_wq;
+ }
+
+ if (pn533_rx_frame_is_ack(skb->data)) {
+ dev_dbg(dev->dev, "%s: Received ACK frame\n", __func__);
+ dev_kfree_skb(skb);
+ return;
+ }
+
+ print_hex_dump_debug("PN533 RX: ", DUMP_PREFIX_NONE, 16, 1, skb->data,
+ dev->ops->rx_frame_size(skb->data), false);
+
+ if (!dev->ops->rx_is_frame_valid(skb->data, dev)) {
+ nfc_err(dev->dev, "Received an invalid frame\n");
+ dev->cmd->status = -EIO;
+ } else if (!pn533_rx_frame_is_cmd_response(dev, skb->data)) {
+ nfc_err(dev->dev, "It it not the response to the last command\n");
+ dev->cmd->status = -EIO;
+ }
+
+ dev->cmd->resp = skb;
+
+sched_wq:
+ queue_work(dev->wq, &dev->cmd_complete_work);
+}
+EXPORT_SYMBOL(pn533_recv_frame);
+
+/* Split the Tx skb into small chunks */
+static int pn533_fill_fragment_skbs(struct pn533 *dev, struct sk_buff *skb)
+{
+ struct sk_buff *frag;
+ int frag_size;
+
+ do {
+ /* Remaining size */
+ if (skb->len > PN533_CMD_DATAFRAME_MAXLEN)
+ frag_size = PN533_CMD_DATAFRAME_MAXLEN;
+ else
+ frag_size = skb->len;
+
+ /* Allocate and reserve */
+ frag = pn533_alloc_skb(dev, frag_size);
+ if (!frag) {
+ skb_queue_purge(&dev->fragment_skb);
+ break;
+ }
+
+ if (!dev->tgt_mode) {
+ /* Reserve the TG/MI byte */
+ skb_reserve(frag, 1);
+
+ /* MI + TG */
+ if (frag_size == PN533_CMD_DATAFRAME_MAXLEN)
+ *skb_push(frag, sizeof(u8)) =
+ (PN533_CMD_MI_MASK | 1);
+ else
+ *skb_push(frag, sizeof(u8)) = 1; /* TG */
+ }
+
+ memcpy(skb_put(frag, frag_size), skb->data, frag_size);
+
+ /* Reduce the size of incoming buffer */
+ skb_pull(skb, frag_size);
+
+ /* Add this to skb_queue */
+ skb_queue_tail(&dev->fragment_skb, frag);
+
+ } while (skb->len > 0);
+
+ dev_kfree_skb(skb);
+
+ return skb_queue_len(&dev->fragment_skb);
+}
+
+static int pn533_transceive(struct nfc_dev *nfc_dev,
+ struct nfc_target *target, struct sk_buff *skb,
+ data_exchange_cb_t cb, void *cb_context)
+{
+ struct pn533 *dev = nfc_get_drvdata(nfc_dev);
+ struct pn533_data_exchange_arg *arg = NULL;
+ int rc;
+
+ dev_dbg(dev->dev, "%s\n", __func__);
+
+ if (!dev->tgt_active_prot) {
+ nfc_err(dev->dev,
+ "Can't exchange data if there is no active target\n");
+ rc = -EINVAL;
+ goto error;
+ }
+
+ arg = kmalloc(sizeof(*arg), GFP_KERNEL);
+ if (!arg) {
+ rc = -ENOMEM;
+ goto error;
+ }
+
+ arg->cb = cb;
+ arg->cb_context = cb_context;
+
+ switch (dev->device_type) {
+ case PN533_DEVICE_PASORI:
+ if (dev->tgt_active_prot == NFC_PROTO_FELICA) {
+ rc = pn533_send_data_async(dev, PN533_CMD_IN_COMM_THRU,
+ skb,
+ pn533_data_exchange_complete,
+ arg);
+
+ break;
+ }
+ default:
+ /* jumbo frame ? */
+ if (skb->len > PN533_CMD_DATAEXCH_DATA_MAXLEN) {
+ rc = pn533_fill_fragment_skbs(dev, skb);
+ if (rc <= 0)
+ goto error;
+
+ skb = skb_dequeue(&dev->fragment_skb);
+ if (!skb) {
+ rc = -EIO;
+ goto error;
+ }
+ } else {
+ *skb_push(skb, sizeof(u8)) = 1; /* TG */
+ }
+
+ rc = pn533_send_data_async(dev, PN533_CMD_IN_DATA_EXCHANGE,
+ skb, pn533_data_exchange_complete,
+ arg);
+
+ break;
+ }
+
+ if (rc < 0) /* rc from send_async */
+ goto error;
+
+ return 0;
+
+error:
+ kfree(arg);
+ dev_kfree_skb(skb);
+ return rc;
+}
+
+static int pn533_tm_send_complete(struct pn533 *dev, void *arg,
+ struct sk_buff *resp)
+{
+ u8 status;
+
+ dev_dbg(dev->dev, "%s\n", __func__);
+
+ if (IS_ERR(resp))
+ return PTR_ERR(resp);
+
+ status = resp->data[0];
+
+ /* Prepare for the next round */
+ if (skb_queue_len(&dev->fragment_skb) > 0) {
+ queue_work(dev->wq, &dev->mi_tm_tx_work);
+ return -EINPROGRESS;
+ }
+ dev_kfree_skb(resp);
+
+ if (status != 0) {
+ nfc_tm_deactivated(dev->nfc_dev);
+
+ dev->tgt_mode = 0;
+
+ return 0;
+ }
+
+ queue_work(dev->wq, &dev->tg_work);
+
+ return 0;
+}
+
+static int pn533_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb)
+{
+ struct pn533 *dev = nfc_get_drvdata(nfc_dev);
+ int rc;
+
+ dev_dbg(dev->dev, "%s\n", __func__);
+
+ /* let's split in multiple chunks if size's too big */
+ if (skb->len > PN533_CMD_DATAEXCH_DATA_MAXLEN) {
+ rc = pn533_fill_fragment_skbs(dev, skb);
+ if (rc <= 0)
+ goto error;
+
+ /* get the first skb */
+ skb = skb_dequeue(&dev->fragment_skb);
+ if (!skb) {
+ rc = -EIO;
+ goto error;
+ }
+
+ rc = pn533_send_data_async(dev, PN533_CMD_TG_SET_META_DATA, skb,
+ pn533_tm_send_complete, NULL);
+ } else {
+ /* Send th skb */
+ rc = pn533_send_data_async(dev, PN533_CMD_TG_SET_DATA, skb,
+ pn533_tm_send_complete, NULL);
+ }
+
+error:
+ if (rc < 0) {
+ dev_kfree_skb(skb);
+ skb_queue_purge(&dev->fragment_skb);
+ }
+
+ return rc;
+}
+
+static void pn533_wq_mi_recv(struct work_struct *work)
+{
+ struct pn533 *dev = container_of(work, struct pn533, mi_rx_work);
+ struct sk_buff *skb;
+ int rc;
+
+ dev_dbg(dev->dev, "%s\n", __func__);
+
+ skb = pn533_alloc_skb(dev, PN533_CMD_DATAEXCH_HEAD_LEN);
+ if (!skb)
+ goto error;
+
+ switch (dev->device_type) {
+ case PN533_DEVICE_PASORI:
+ if (dev->tgt_active_prot == NFC_PROTO_FELICA) {
+ rc = pn533_send_cmd_direct_async(dev,
+ PN533_CMD_IN_COMM_THRU,
+ skb,
+ pn533_data_exchange_complete,
+ dev->cmd_complete_mi_arg);
+
+ break;
+ }
+ default:
+ *skb_put(skb, sizeof(u8)) = 1; /*TG*/
+
+ rc = pn533_send_cmd_direct_async(dev,
+ PN533_CMD_IN_DATA_EXCHANGE,
+ skb,
+ pn533_data_exchange_complete,
+ dev->cmd_complete_mi_arg);
+
+ break;
+ }
+
+ if (rc == 0) /* success */
+ return;
+
+ nfc_err(dev->dev,
+ "Error %d when trying to perform data_exchange\n", rc);
+
+ dev_kfree_skb(skb);
+ kfree(dev->cmd_complete_mi_arg);
+
+error:
+ dev->phy_ops->send_ack(dev, GFP_KERNEL);
+ queue_work(dev->wq, &dev->cmd_work);
+}
+
+static void pn533_wq_mi_send(struct work_struct *work)
+{
+ struct pn533 *dev = container_of(work, struct pn533, mi_tx_work);
+ struct sk_buff *skb;
+ int rc;
+
+ dev_dbg(dev->dev, "%s\n", __func__);
+
+ /* Grab the first skb in the queue */
+ skb = skb_dequeue(&dev->fragment_skb);
+
+ if (skb == NULL) { /* No more data */
+ /* Reset the queue for future use */
+ skb_queue_head_init(&dev->fragment_skb);
+ goto error;
+ }
+
+ switch (dev->device_type) {
+ case PN533_DEVICE_PASORI:
+ if (dev->tgt_active_prot != NFC_PROTO_FELICA) {
+ rc = -EIO;
+ break;
+ }
+
+ rc = pn533_send_cmd_direct_async(dev, PN533_CMD_IN_COMM_THRU,
+ skb,
+ pn533_data_exchange_complete,
+ dev->cmd_complete_dep_arg);
+
+ break;
+
+ default:
+ /* Still some fragments? */
+ rc = pn533_send_cmd_direct_async(dev,
+ PN533_CMD_IN_DATA_EXCHANGE,
+ skb,
+ pn533_data_exchange_complete,
+ dev->cmd_complete_dep_arg);
+
+ break;
+ }
+
+ if (rc == 0) /* success */
+ return;
+
+ nfc_err(dev->dev,
+ "Error %d when trying to perform data_exchange\n", rc);
+
+ dev_kfree_skb(skb);
+ kfree(dev->cmd_complete_dep_arg);
+
+error:
+ dev->phy_ops->send_ack(dev, GFP_KERNEL);
+ queue_work(dev->wq, &dev->cmd_work);
+}
+
+static int pn533_set_configuration(struct pn533 *dev, u8 cfgitem, u8 *cfgdata,
+ u8 cfgdata_len)
+{
+ struct sk_buff *skb;
+ struct sk_buff *resp;
+ int skb_len;
+
+ dev_dbg(dev->dev, "%s\n", __func__);
+
+ skb_len = sizeof(cfgitem) + cfgdata_len; /* cfgitem + cfgdata */
+
+ skb = pn533_alloc_skb(dev, skb_len);
+ if (!skb)
+ return -ENOMEM;
+
+ *skb_put(skb, sizeof(cfgitem)) = cfgitem;
+ memcpy(skb_put(skb, cfgdata_len), cfgdata, cfgdata_len);
+
+ resp = pn533_send_cmd_sync(dev, PN533_CMD_RF_CONFIGURATION, skb);
+ if (IS_ERR(resp))
+ return PTR_ERR(resp);
+
+ dev_kfree_skb(resp);
+ return 0;
+}
+
+static int pn533_get_firmware_version(struct pn533 *dev,
+ struct pn533_fw_version *fv)
+{
+ struct sk_buff *skb;
+ struct sk_buff *resp;
+
+ skb = pn533_alloc_skb(dev, 0);
+ if (!skb)
+ return -ENOMEM;
+
+ resp = pn533_send_cmd_sync(dev, PN533_CMD_GET_FIRMWARE_VERSION, skb);
+ if (IS_ERR(resp))
+ return PTR_ERR(resp);
+
+ fv->ic = resp->data[0];
+ fv->ver = resp->data[1];
+ fv->rev = resp->data[2];
+ fv->support = resp->data[3];
+
+ dev_kfree_skb(resp);
+ return 0;
+}
+
+static int pn533_pasori_fw_reset(struct pn533 *dev)
+{
+ struct sk_buff *skb;
+ struct sk_buff *resp;
+
+ dev_dbg(dev->dev, "%s\n", __func__);
+
+ skb = pn533_alloc_skb(dev, sizeof(u8));
+ if (!skb)
+ return -ENOMEM;
+
+ *skb_put(skb, sizeof(u8)) = 0x1;
+
+ resp = pn533_send_cmd_sync(dev, 0x18, skb);
+ if (IS_ERR(resp))
+ return PTR_ERR(resp);
+
+ dev_kfree_skb(resp);
+
+ return 0;
+}
+
+static int pn533_rf_field(struct nfc_dev *nfc_dev, u8 rf)
+{
+ struct pn533 *dev = nfc_get_drvdata(nfc_dev);
+ u8 rf_field = !!rf;
+ int rc;
+
+ rf_field |= PN533_CFGITEM_RF_FIELD_AUTO_RFCA;
+
+ rc = pn533_set_configuration(dev, PN533_CFGITEM_RF_FIELD,
+ (u8 *)&rf_field, 1);
+ if (rc) {
+ nfc_err(dev->dev, "Error on setting RF field\n");
+ return rc;
+ }
+
+ return rc;
+}
+
+static int pn532_sam_configuration(struct nfc_dev *nfc_dev)
+{
+ struct pn533 *dev = nfc_get_drvdata(nfc_dev);
+ struct sk_buff *skb;
+ struct sk_buff *resp;
+
+ skb = pn533_alloc_skb(dev, 1);
+ if (!skb)
+ return -ENOMEM;
+
+ *skb_put(skb, 1) = 0x01;
+
+ resp = pn533_send_cmd_sync(dev, PN533_CMD_SAM_CONFIGURATION, skb);
+ if (IS_ERR(resp))
+ return PTR_ERR(resp);
+
+ dev_kfree_skb(resp);
+ return 0;
+}
+
+static int pn533_dev_up(struct nfc_dev *nfc_dev)
+{
+ struct pn533 *dev = nfc_get_drvdata(nfc_dev);
+
+ if (dev->device_type == PN533_DEVICE_PN532) {
+ int rc = pn532_sam_configuration(nfc_dev);
+
+ if (rc)
+ return rc;
+ }
+
+ return pn533_rf_field(nfc_dev, 1);
+}
+
+static int pn533_dev_down(struct nfc_dev *nfc_dev)
+{
+ return pn533_rf_field(nfc_dev, 0);
+}
+
+static struct nfc_ops pn533_nfc_ops = {
+ .dev_up = pn533_dev_up,
+ .dev_down = pn533_dev_down,
+ .dep_link_up = pn533_dep_link_up,
+ .dep_link_down = pn533_dep_link_down,
+ .start_poll = pn533_start_poll,
+ .stop_poll = pn533_stop_poll,
+ .activate_target = pn533_activate_target,
+ .deactivate_target = pn533_deactivate_target,
+ .im_transceive = pn533_transceive,
+ .tm_send = pn533_tm_send,
+};
+
+static int pn533_setup(struct pn533 *dev)
+{
+ struct pn533_config_max_retries max_retries;
+ struct pn533_config_timing timing;
+ u8 pasori_cfg[3] = {0x08, 0x01, 0x08};
+ int rc;
+
+ switch (dev->device_type) {
+ case PN533_DEVICE_STD:
+ case PN533_DEVICE_PASORI:
+ case PN533_DEVICE_ACR122U:
+ case PN533_DEVICE_PN532:
+ max_retries.mx_rty_atr = 0x2;
+ max_retries.mx_rty_psl = 0x1;
+ max_retries.mx_rty_passive_act =
+ PN533_CONFIG_MAX_RETRIES_NO_RETRY;
+
+ timing.rfu = PN533_CONFIG_TIMING_102;
+ timing.atr_res_timeout = PN533_CONFIG_TIMING_102;
+ timing.dep_timeout = PN533_CONFIG_TIMING_204;
+
+ break;
+
+ default:
+ nfc_err(dev->dev, "Unknown device type %d\n",
+ dev->device_type);
+ return -EINVAL;
+ }
+
+ rc = pn533_set_configuration(dev, PN533_CFGITEM_MAX_RETRIES,
+ (u8 *)&max_retries, sizeof(max_retries));
+ if (rc) {
+ nfc_err(dev->dev,
+ "Error on setting MAX_RETRIES config\n");
+ return rc;
+ }
+
+
+ rc = pn533_set_configuration(dev, PN533_CFGITEM_TIMING,
+ (u8 *)&timing, sizeof(timing));
+ if (rc) {
+ nfc_err(dev->dev, "Error on setting RF timings\n");
+ return rc;
+ }
+
+ switch (dev->device_type) {
+ case PN533_DEVICE_STD:
+ case PN533_DEVICE_PN532:
+ break;
+
+ case PN533_DEVICE_PASORI:
+ pn533_pasori_fw_reset(dev);
+
+ rc = pn533_set_configuration(dev, PN533_CFGITEM_PASORI,
+ pasori_cfg, 3);
+ if (rc) {
+ nfc_err(dev->dev,
+ "Error while settings PASORI config\n");
+ return rc;
+ }
+
+ pn533_pasori_fw_reset(dev);
+
+ break;
+ }
+
+ return 0;
+}
+
+struct pn533 *pn533_register_device(u32 device_type,
+ u32 protocols,
+ enum pn533_protocol_type protocol_type,
+ void *phy,
+ struct pn533_phy_ops *phy_ops,
+ struct pn533_frame_ops *fops,
+ struct device *dev,
+ struct device *parent)
+{
+ struct pn533_fw_version fw_ver;
+ struct pn533 *priv;
+ int rc = -ENOMEM;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return ERR_PTR(-ENOMEM);
+
+ priv->phy = phy;
+ priv->phy_ops = phy_ops;
+ priv->dev = dev;
+ if (fops != NULL)
+ priv->ops = fops;
+ else
+ priv->ops = &pn533_std_frame_ops;
+
+ priv->protocol_type = protocol_type;
+ priv->device_type = device_type;
+
+ mutex_init(&priv->cmd_lock);
+
+ INIT_WORK(&priv->cmd_work, pn533_wq_cmd);
+ INIT_WORK(&priv->cmd_complete_work, pn533_wq_cmd_complete);
+ INIT_WORK(&priv->mi_rx_work, pn533_wq_mi_recv);
+ INIT_WORK(&priv->mi_tx_work, pn533_wq_mi_send);
+ INIT_WORK(&priv->tg_work, pn533_wq_tg_get_data);
+ INIT_WORK(&priv->mi_tm_rx_work, pn533_wq_tm_mi_recv);
+ INIT_WORK(&priv->mi_tm_tx_work, pn533_wq_tm_mi_send);
+ INIT_DELAYED_WORK(&priv->poll_work, pn533_wq_poll);
+ INIT_WORK(&priv->rf_work, pn533_wq_rf);
+ priv->wq = alloc_ordered_workqueue("pn533", 0);
+ if (priv->wq == NULL)
+ goto error;
+
+ init_timer(&priv->listen_timer);
+ priv->listen_timer.data = (unsigned long) priv;
+ priv->listen_timer.function = pn533_listen_mode_timer;
+
+ skb_queue_head_init(&priv->resp_q);
+ skb_queue_head_init(&priv->fragment_skb);
+
+ INIT_LIST_HEAD(&priv->cmd_queue);
+
+ memset(&fw_ver, 0, sizeof(fw_ver));
+ rc = pn533_get_firmware_version(priv, &fw_ver);
+ if (rc < 0)
+ goto destroy_wq;
+
+ nfc_info(dev, "NXP PN5%02X firmware ver %d.%d now attached\n",
+ fw_ver.ic, fw_ver.ver, fw_ver.rev);
+
+
+ priv->nfc_dev = nfc_allocate_device(&pn533_nfc_ops, protocols,
+ priv->ops->tx_header_len +
+ PN533_CMD_DATAEXCH_HEAD_LEN,
+ priv->ops->tx_tail_len);
+ if (!priv->nfc_dev) {
+ rc = -ENOMEM;
+ goto destroy_wq;
+ }
+
+ nfc_set_parent_dev(priv->nfc_dev, parent);
+ nfc_set_drvdata(priv->nfc_dev, priv);
+
+ rc = nfc_register_device(priv->nfc_dev);
+ if (rc)
+ goto free_nfc_dev;
+
+ rc = pn533_setup(priv);
+ if (rc)
+ goto unregister_nfc_dev;
+
+ return priv;
+
+unregister_nfc_dev:
+ nfc_unregister_device(priv->nfc_dev);
+
+free_nfc_dev:
+ nfc_free_device(priv->nfc_dev);
+
+destroy_wq:
+ destroy_workqueue(priv->wq);
+error:
+ kfree(priv);
+ return ERR_PTR(rc);
+}
+EXPORT_SYMBOL_GPL(pn533_register_device);
+
+void pn533_unregister_device(struct pn533 *priv)
+{
+ struct pn533_cmd *cmd, *n;
+
+ nfc_unregister_device(priv->nfc_dev);
+ nfc_free_device(priv->nfc_dev);
+
+ flush_delayed_work(&priv->poll_work);
+ destroy_workqueue(priv->wq);
+
+ skb_queue_purge(&priv->resp_q);
+
+ del_timer(&priv->listen_timer);
+
+ list_for_each_entry_safe(cmd, n, &priv->cmd_queue, queue) {
+ list_del(&cmd->queue);
+ kfree(cmd);
+ }
+
+ kfree(priv);
+}
+EXPORT_SYMBOL_GPL(pn533_unregister_device);
+
+
+MODULE_AUTHOR("Lauro Ramos Venancio <lauro.venancio@openbossa.org>");
+MODULE_AUTHOR("Aloisio Almeida Jr <aloisio.almeida@openbossa.org>");
+MODULE_AUTHOR("Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>");
+MODULE_DESCRIPTION("PN533 driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/nfc/pn533/pn533.h b/drivers/nfc/pn533/pn533.h
new file mode 100644
index 0000000..553c7d1
--- /dev/null
+++ b/drivers/nfc/pn533/pn533.h
@@ -0,0 +1,238 @@
+/*
+ * Driver for NXP PN533 NFC Chip
+ *
+ * Copyright (C) 2011 Instituto Nokia de Tecnologia
+ * Copyright (C) 2012-2013 Tieto Poland
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define PN533_DEVICE_STD 0x1
+#define PN533_DEVICE_PASORI 0x2
+#define PN533_DEVICE_ACR122U 0x3
+#define PN533_DEVICE_PN532 0x4
+
+#define PN533_ALL_PROTOCOLS (NFC_PROTO_JEWEL_MASK | NFC_PROTO_MIFARE_MASK |\
+ NFC_PROTO_FELICA_MASK | NFC_PROTO_ISO14443_MASK |\
+ NFC_PROTO_NFC_DEP_MASK |\
+ NFC_PROTO_ISO14443_B_MASK)
+
+#define PN533_NO_TYPE_B_PROTOCOLS (NFC_PROTO_JEWEL_MASK | \
+ NFC_PROTO_MIFARE_MASK | \
+ NFC_PROTO_FELICA_MASK | \
+ NFC_PROTO_ISO14443_MASK | \
+ NFC_PROTO_NFC_DEP_MASK)
+
+/* Standard pn533 frame definitions (standard and extended)*/
+#define PN533_STD_FRAME_HEADER_LEN (sizeof(struct pn533_std_frame) \
+ + 2) /* data[0] TFI, data[1] CC */
+#define PN533_STD_FRAME_TAIL_LEN 2 /* data[len] DCS, data[len + 1] postamble*/
+
+#define PN533_EXT_FRAME_HEADER_LEN (sizeof(struct pn533_ext_frame) \
+ + 2) /* data[0] TFI, data[1] CC */
+
+#define PN533_CMD_DATAEXCH_HEAD_LEN 1
+#define PN533_CMD_DATAEXCH_DATA_MAXLEN 262
+#define PN533_CMD_DATAFRAME_MAXLEN 240 /* max data length (send) */
+
+/*
+ * Max extended frame payload len, excluding TFI and CC
+ * which are already in PN533_FRAME_HEADER_LEN.
+ */
+#define PN533_STD_FRAME_MAX_PAYLOAD_LEN 263
+
+
+/* Preamble (1), SoPC (2), ACK Code (2), Postamble (1) */
+#define PN533_STD_FRAME_ACK_SIZE 6
+#define PN533_STD_FRAME_CHECKSUM(f) (f->data[f->datalen])
+#define PN533_STD_FRAME_POSTAMBLE(f) (f->data[f->datalen + 1])
+/* Half start code (3), LEN (4) should be 0xffff for extended frame */
+#define PN533_STD_IS_EXTENDED(hdr) ((hdr)->datalen == 0xFF \
+ && (hdr)->datalen_checksum == 0xFF)
+#define PN533_EXT_FRAME_CHECKSUM(f) (f->data[be16_to_cpu(f->datalen)])
+
+/* start of frame */
+#define PN533_STD_FRAME_SOF 0x00FF
+
+/* standard frame identifier: in/out/error */
+#define PN533_STD_FRAME_IDENTIFIER(f) (f->data[0]) /* TFI */
+#define PN533_STD_FRAME_DIR_OUT 0xD4
+#define PN533_STD_FRAME_DIR_IN 0xD5
+
+/* PN533 Commands */
+#define PN533_FRAME_CMD(f) (f->data[1])
+
+#define PN533_CMD_GET_FIRMWARE_VERSION 0x02
+#define PN533_CMD_SAM_CONFIGURATION 0x14
+#define PN533_CMD_RF_CONFIGURATION 0x32
+#define PN533_CMD_IN_DATA_EXCHANGE 0x40
+#define PN533_CMD_IN_COMM_THRU 0x42
+#define PN533_CMD_IN_LIST_PASSIVE_TARGET 0x4A
+#define PN533_CMD_IN_ATR 0x50
+#define PN533_CMD_IN_RELEASE 0x52
+#define PN533_CMD_IN_JUMP_FOR_DEP 0x56
+
+#define PN533_CMD_TG_INIT_AS_TARGET 0x8c
+#define PN533_CMD_TG_GET_DATA 0x86
+#define PN533_CMD_TG_SET_DATA 0x8e
+#define PN533_CMD_TG_SET_META_DATA 0x94
+#define PN533_CMD_UNDEF 0xff
+
+#define PN533_CMD_RESPONSE(cmd) (cmd + 1)
+
+/* PN533 Return codes */
+#define PN533_CMD_RET_MASK 0x3F
+#define PN533_CMD_MI_MASK 0x40
+#define PN533_CMD_RET_SUCCESS 0x00
+
+
+enum pn533_protocol_type {
+ PN533_PROTO_REQ_ACK_RESP = 0,
+ PN533_PROTO_REQ_RESP
+};
+
+/* Poll modulations */
+enum {
+ PN533_POLL_MOD_106KBPS_A,
+ PN533_POLL_MOD_212KBPS_FELICA,
+ PN533_POLL_MOD_424KBPS_FELICA,
+ PN533_POLL_MOD_106KBPS_JEWEL,
+ PN533_POLL_MOD_847KBPS_B,
+ PN533_LISTEN_MOD,
+
+ __PN533_POLL_MOD_AFTER_LAST,
+};
+#define PN533_POLL_MOD_MAX (__PN533_POLL_MOD_AFTER_LAST - 1)
+
+struct pn533_std_frame {
+ u8 preamble;
+ __be16 start_frame;
+ u8 datalen;
+ u8 datalen_checksum;
+ u8 data[];
+} __packed;
+
+struct pn533_ext_frame { /* Extended Information frame */
+ u8 preamble;
+ __be16 start_frame;
+ __be16 eif_flag; /* fixed to 0xFFFF */
+ __be16 datalen;
+ u8 datalen_checksum;
+ u8 data[];
+} __packed;
+
+struct pn533 {
+ struct nfc_dev *nfc_dev;
+ u32 device_type;
+ enum pn533_protocol_type protocol_type;
+
+ struct sk_buff_head resp_q;
+ struct sk_buff_head fragment_skb;
+
+ struct workqueue_struct *wq;
+ struct work_struct cmd_work;
+ struct work_struct cmd_complete_work;
+ struct delayed_work poll_work;
+ struct work_struct mi_rx_work;
+ struct work_struct mi_tx_work;
+ struct work_struct mi_tm_rx_work;
+ struct work_struct mi_tm_tx_work;
+ struct work_struct tg_work;
+ struct work_struct rf_work;
+
+ struct list_head cmd_queue;
+ struct pn533_cmd *cmd;
+ u8 cmd_pending;
+ struct mutex cmd_lock; /* protects cmd queue */
+
+ void *cmd_complete_mi_arg;
+ void *cmd_complete_dep_arg;
+
+ struct pn533_poll_modulations *poll_mod_active[PN533_POLL_MOD_MAX + 1];
+ u8 poll_mod_count;
+ u8 poll_mod_curr;
+ u8 poll_dep;
+ u32 poll_protocols;
+ u32 listen_protocols;
+ struct timer_list listen_timer;
+ int cancel_listen;
+
+ u8 *gb;
+ size_t gb_len;
+
+ u8 tgt_available_prots;
+ u8 tgt_active_prot;
+ u8 tgt_mode;
+
+ struct pn533_frame_ops *ops;
+
+ struct device *dev;
+ void *phy;
+ struct pn533_phy_ops *phy_ops;
+};
+
+typedef int (*pn533_send_async_complete_t) (struct pn533 *dev, void *arg,
+ struct sk_buff *resp);
+
+struct pn533_cmd {
+ struct list_head queue;
+ u8 code;
+ int status;
+ struct sk_buff *req;
+ struct sk_buff *resp;
+ pn533_send_async_complete_t complete_cb;
+ void *complete_cb_context;
+};
+
+
+struct pn533_frame_ops {
+ void (*tx_frame_init)(void *frame, u8 cmd_code);
+ void (*tx_frame_finish)(void *frame);
+ void (*tx_update_payload_len)(void *frame, int len);
+ int tx_header_len;
+ int tx_tail_len;
+
+ bool (*rx_is_frame_valid)(void *frame, struct pn533 *dev);
+ bool (*rx_frame_is_ack)(void *frame);
+ int (*rx_frame_size)(void *frame);
+ int rx_header_len;
+ int rx_tail_len;
+
+ int max_payload_len;
+ u8 (*get_cmd_code)(void *frame);
+};
+
+
+struct pn533_phy_ops {
+ int (*send_frame)(struct pn533 *priv,
+ struct sk_buff *out);
+ int (*send_ack)(struct pn533 *dev, gfp_t flags);
+ void (*abort_cmd)(struct pn533 *priv, gfp_t flags);
+};
+
+
+struct pn533 *pn533_register_device(u32 device_type,
+ u32 protocols,
+ enum pn533_protocol_type protocol_type,
+ void *phy,
+ struct pn533_phy_ops *phy_ops,
+ struct pn533_frame_ops *fops,
+ struct device *dev,
+ struct device *parent);
+
+void pn533_unregister_device(struct pn533 *priv);
+void pn533_recv_frame(struct pn533 *dev, struct sk_buff *skb, int status);
+
+bool pn533_rx_frame_is_cmd_response(struct pn533 *dev, void *frame);
+bool pn533_rx_frame_is_ack(void *_frame);
diff --git a/drivers/nfc/pn533/usb.c b/drivers/nfc/pn533/usb.c
new file mode 100644
index 0000000..33ed78b
--- /dev/null
+++ b/drivers/nfc/pn533/usb.c
@@ -0,0 +1,594 @@
+/*
+ * Driver for NXP PN533 NFC Chip - USB transport layer
+ *
+ * Copyright (C) 2011 Instituto Nokia de Tecnologia
+ * Copyright (C) 2012-2013 Tieto Poland
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/nfc.h>
+#include <linux/netdevice.h>
+#include <net/nfc/nfc.h>
+#include "pn533.h"
+
+#define VERSION "0.1"
+
+#define PN533_VENDOR_ID 0x4CC
+#define PN533_PRODUCT_ID 0x2533
+
+#define SCM_VENDOR_ID 0x4E6
+#define SCL3711_PRODUCT_ID 0x5591
+
+#define SONY_VENDOR_ID 0x054c
+#define PASORI_PRODUCT_ID 0x02e1
+
+#define ACS_VENDOR_ID 0x072f
+#define ACR122U_PRODUCT_ID 0x2200
+
+static const struct usb_device_id pn533_usb_table[] = {
+ { USB_DEVICE(PN533_VENDOR_ID, PN533_PRODUCT_ID),
+ .driver_info = PN533_DEVICE_STD },
+ { USB_DEVICE(SCM_VENDOR_ID, SCL3711_PRODUCT_ID),
+ .driver_info = PN533_DEVICE_STD },
+ { USB_DEVICE(SONY_VENDOR_ID, PASORI_PRODUCT_ID),
+ .driver_info = PN533_DEVICE_PASORI },
+ { USB_DEVICE(ACS_VENDOR_ID, ACR122U_PRODUCT_ID),
+ .driver_info = PN533_DEVICE_ACR122U },
+ { }
+};
+MODULE_DEVICE_TABLE(usb, pn533_usb_table);
+
+struct pn533_usb_phy {
+ struct usb_device *udev;
+ struct usb_interface *interface;
+
+ struct urb *out_urb;
+ struct urb *in_urb;
+
+ struct pn533 *priv;
+};
+
+static void pn533_recv_response(struct urb *urb)
+{
+ struct pn533_usb_phy *phy = urb->context;
+ struct sk_buff *skb = NULL;
+
+ if (!urb->status) {
+ skb = alloc_skb(urb->actual_length, GFP_KERNEL);
+ if (!skb) {
+ nfc_err(&phy->udev->dev, "failed to alloc memory\n");
+ } else {
+ memcpy(skb_put(skb, urb->actual_length),
+ urb->transfer_buffer, urb->actual_length);
+ }
+ }
+
+ pn533_recv_frame(phy->priv, skb, urb->status);
+}
+
+static int pn533_submit_urb_for_response(struct pn533_usb_phy *phy, gfp_t flags)
+{
+ phy->in_urb->complete = pn533_recv_response;
+
+ return usb_submit_urb(phy->in_urb, flags);
+}
+
+static void pn533_recv_ack(struct urb *urb)
+{
+ struct pn533_usb_phy *phy = urb->context;
+ struct pn533 *priv = phy->priv;
+ struct pn533_cmd *cmd = priv->cmd;
+ struct pn533_std_frame *in_frame;
+ int rc;
+
+ cmd->status = urb->status;
+
+ switch (urb->status) {
+ case 0:
+ break; /* success */
+ case -ECONNRESET:
+ case -ENOENT:
+ dev_dbg(&phy->udev->dev,
+ "The urb has been stopped (status %d)\n",
+ urb->status);
+ goto sched_wq;
+ case -ESHUTDOWN:
+ default:
+ nfc_err(&phy->udev->dev,
+ "Urb failure (status %d)\n", urb->status);
+ goto sched_wq;
+ }
+
+ in_frame = phy->in_urb->transfer_buffer;
+
+ if (!pn533_rx_frame_is_ack(in_frame)) {
+ nfc_err(&phy->udev->dev, "Received an invalid ack\n");
+ cmd->status = -EIO;
+ goto sched_wq;
+ }
+
+ rc = pn533_submit_urb_for_response(phy, GFP_ATOMIC);
+ if (rc) {
+ nfc_err(&phy->udev->dev,
+ "usb_submit_urb failed with result %d\n", rc);
+ cmd->status = rc;
+ goto sched_wq;
+ }
+
+ return;
+
+sched_wq:
+ queue_work(priv->wq, &priv->cmd_complete_work);
+}
+
+static int pn533_submit_urb_for_ack(struct pn533_usb_phy *phy, gfp_t flags)
+{
+ phy->in_urb->complete = pn533_recv_ack;
+
+ return usb_submit_urb(phy->in_urb, flags);
+}
+
+static int pn533_usb_send_ack(struct pn533 *dev, gfp_t flags)
+{
+ struct pn533_usb_phy *phy = dev->phy;
+ u8 ack[6] = {0x00, 0x00, 0xff, 0x00, 0xff, 0x00};
+ /* spec 7.1.1.3: Preamble, SoPC (2), ACK Code (2), Postamble */
+ int rc;
+
+ phy->out_urb->transfer_buffer = ack;
+ phy->out_urb->transfer_buffer_length = sizeof(ack);
+ rc = usb_submit_urb(phy->out_urb, flags);
+
+ return rc;
+}
+
+static int pn533_usb_send_frame(struct pn533 *dev,
+ struct sk_buff *out)
+{
+ struct pn533_usb_phy *phy = dev->phy;
+ int rc;
+
+ if (phy->priv == NULL)
+ phy->priv = dev;
+
+ phy->out_urb->transfer_buffer = out->data;
+ phy->out_urb->transfer_buffer_length = out->len;
+
+ print_hex_dump_debug("PN533 TX: ", DUMP_PREFIX_NONE, 16, 1,
+ out->data, out->len, false);
+
+ rc = usb_submit_urb(phy->out_urb, GFP_KERNEL);
+ if (rc)
+ return rc;
+
+ if (dev->protocol_type == PN533_PROTO_REQ_RESP) {
+ /* request for response for sent packet directly */
+ rc = pn533_submit_urb_for_response(phy, GFP_ATOMIC);
+ if (rc)
+ goto error;
+ } else if (dev->protocol_type == PN533_PROTO_REQ_ACK_RESP) {
+ /* request for ACK if that's the case */
+ rc = pn533_submit_urb_for_ack(phy, GFP_KERNEL);
+ if (rc)
+ goto error;
+ }
+
+ return 0;
+
+error:
+ usb_unlink_urb(phy->out_urb);
+ return rc;
+}
+
+static void pn533_usb_abort_cmd(struct pn533 *dev, gfp_t flags)
+{
+ struct pn533_usb_phy *phy = dev->phy;
+
+ /* ACR122U does not support any command which aborts last
+ * issued command i.e. as ACK for standard PN533. Additionally,
+ * it behaves stange, sending broken or incorrect responses,
+ * when we cancel urb before the chip will send response.
+ */
+ if (dev->device_type == PN533_DEVICE_ACR122U)
+ return;
+
+ /* An ack will cancel the last issued command */
+ pn533_usb_send_ack(dev, flags);
+
+ /* cancel the urb request */
+ usb_kill_urb(phy->in_urb);
+}
+
+/* ACR122 specific structs and fucntions */
+
+/* ACS ACR122 pn533 frame definitions */
+#define PN533_ACR122_TX_FRAME_HEADER_LEN (sizeof(struct pn533_acr122_tx_frame) \
+ + 2)
+#define PN533_ACR122_TX_FRAME_TAIL_LEN 0
+#define PN533_ACR122_RX_FRAME_HEADER_LEN (sizeof(struct pn533_acr122_rx_frame) \
+ + 2)
+#define PN533_ACR122_RX_FRAME_TAIL_LEN 2
+#define PN533_ACR122_FRAME_MAX_PAYLOAD_LEN PN533_STD_FRAME_MAX_PAYLOAD_LEN
+
+/* CCID messages types */
+#define PN533_ACR122_PC_TO_RDR_ICCPOWERON 0x62
+#define PN533_ACR122_PC_TO_RDR_ESCAPE 0x6B
+
+#define PN533_ACR122_RDR_TO_PC_ESCAPE 0x83
+
+
+struct pn533_acr122_ccid_hdr {
+ u8 type;
+ u32 datalen;
+ u8 slot;
+ u8 seq;
+
+ /*
+ * 3 msg specific bytes or status, error and 1 specific
+ * byte for reposnse msg
+ */
+ u8 params[3];
+ u8 data[]; /* payload */
+} __packed;
+
+struct pn533_acr122_apdu_hdr {
+ u8 class;
+ u8 ins;
+ u8 p1;
+ u8 p2;
+} __packed;
+
+struct pn533_acr122_tx_frame {
+ struct pn533_acr122_ccid_hdr ccid;
+ struct pn533_acr122_apdu_hdr apdu;
+ u8 datalen;
+ u8 data[]; /* pn533 frame: TFI ... */
+} __packed;
+
+struct pn533_acr122_rx_frame {
+ struct pn533_acr122_ccid_hdr ccid;
+ u8 data[]; /* pn533 frame : TFI ... */
+} __packed;
+
+static void pn533_acr122_tx_frame_init(void *_frame, u8 cmd_code)
+{
+ struct pn533_acr122_tx_frame *frame = _frame;
+
+ frame->ccid.type = PN533_ACR122_PC_TO_RDR_ESCAPE;
+ /* sizeof(apdu_hdr) + sizeof(datalen) */
+ frame->ccid.datalen = sizeof(frame->apdu) + 1;
+ frame->ccid.slot = 0;
+ frame->ccid.seq = 0;
+ frame->ccid.params[0] = 0;
+ frame->ccid.params[1] = 0;
+ frame->ccid.params[2] = 0;
+
+ frame->data[0] = PN533_STD_FRAME_DIR_OUT;
+ frame->data[1] = cmd_code;
+ frame->datalen = 2; /* data[0] + data[1] */
+
+ frame->apdu.class = 0xFF;
+ frame->apdu.ins = 0;
+ frame->apdu.p1 = 0;
+ frame->apdu.p2 = 0;
+}
+
+static void pn533_acr122_tx_frame_finish(void *_frame)
+{
+ struct pn533_acr122_tx_frame *frame = _frame;
+
+ frame->ccid.datalen += frame->datalen;
+}
+
+static void pn533_acr122_tx_update_payload_len(void *_frame, int len)
+{
+ struct pn533_acr122_tx_frame *frame = _frame;
+
+ frame->datalen += len;
+}
+
+static bool pn533_acr122_is_rx_frame_valid(void *_frame, struct pn533 *dev)
+{
+ struct pn533_acr122_rx_frame *frame = _frame;
+
+ if (frame->ccid.type != 0x83)
+ return false;
+
+ if (!frame->ccid.datalen)
+ return false;
+
+ if (frame->data[frame->ccid.datalen - 2] == 0x63)
+ return false;
+
+ return true;
+}
+
+static int pn533_acr122_rx_frame_size(void *frame)
+{
+ struct pn533_acr122_rx_frame *f = frame;
+
+ /* f->ccid.datalen already includes tail length */
+ return sizeof(struct pn533_acr122_rx_frame) + f->ccid.datalen;
+}
+
+static u8 pn533_acr122_get_cmd_code(void *frame)
+{
+ struct pn533_acr122_rx_frame *f = frame;
+
+ return PN533_FRAME_CMD(f);
+}
+
+static struct pn533_frame_ops pn533_acr122_frame_ops = {
+ .tx_frame_init = pn533_acr122_tx_frame_init,
+ .tx_frame_finish = pn533_acr122_tx_frame_finish,
+ .tx_update_payload_len = pn533_acr122_tx_update_payload_len,
+ .tx_header_len = PN533_ACR122_TX_FRAME_HEADER_LEN,
+ .tx_tail_len = PN533_ACR122_TX_FRAME_TAIL_LEN,
+
+ .rx_is_frame_valid = pn533_acr122_is_rx_frame_valid,
+ .rx_header_len = PN533_ACR122_RX_FRAME_HEADER_LEN,
+ .rx_tail_len = PN533_ACR122_RX_FRAME_TAIL_LEN,
+ .rx_frame_size = pn533_acr122_rx_frame_size,
+
+ .max_payload_len = PN533_ACR122_FRAME_MAX_PAYLOAD_LEN,
+ .get_cmd_code = pn533_acr122_get_cmd_code,
+};
+
+struct pn533_acr122_poweron_rdr_arg {
+ int rc;
+ struct completion done;
+};
+
+static void pn533_acr122_poweron_rdr_resp(struct urb *urb)
+{
+ struct pn533_acr122_poweron_rdr_arg *arg = urb->context;
+
+ dev_dbg(&urb->dev->dev, "%s\n", __func__);
+
+ print_hex_dump_debug("ACR122 RX: ", DUMP_PREFIX_NONE, 16, 1,
+ urb->transfer_buffer, urb->transfer_buffer_length,
+ false);
+
+ arg->rc = urb->status;
+ complete(&arg->done);
+}
+
+static int pn533_acr122_poweron_rdr(struct pn533_usb_phy *phy)
+{
+ /* Power on th reader (CCID cmd) */
+ u8 cmd[10] = {PN533_ACR122_PC_TO_RDR_ICCPOWERON,
+ 0, 0, 0, 0, 0, 0, 3, 0, 0};
+ int rc;
+ void *cntx;
+ struct pn533_acr122_poweron_rdr_arg arg;
+
+ dev_dbg(&phy->udev->dev, "%s\n", __func__);
+
+ init_completion(&arg.done);
+ cntx = phy->in_urb->context; /* backup context */
+
+ phy->in_urb->complete = pn533_acr122_poweron_rdr_resp;
+ phy->in_urb->context = &arg;
+
+ phy->out_urb->transfer_buffer = cmd;
+ phy->out_urb->transfer_buffer_length = sizeof(cmd);
+
+ print_hex_dump_debug("ACR122 TX: ", DUMP_PREFIX_NONE, 16, 1,
+ cmd, sizeof(cmd), false);
+
+ rc = usb_submit_urb(phy->out_urb, GFP_KERNEL);
+ if (rc) {
+ nfc_err(&phy->udev->dev,
+ "Reader power on cmd error %d\n", rc);
+ return rc;
+ }
+
+ rc = usb_submit_urb(phy->in_urb, GFP_KERNEL);
+ if (rc) {
+ nfc_err(&phy->udev->dev,
+ "Can't submit reader poweron cmd response %d\n", rc);
+ return rc;
+ }
+
+ wait_for_completion(&arg.done);
+ phy->in_urb->context = cntx; /* restore context */
+
+ return arg.rc;
+}
+
+static void pn533_send_complete(struct urb *urb)
+{
+ struct pn533_usb_phy *phy = urb->context;
+
+ switch (urb->status) {
+ case 0:
+ break; /* success */
+ case -ECONNRESET:
+ case -ENOENT:
+ dev_dbg(&phy->udev->dev,
+ "The urb has been stopped (status %d)\n",
+ urb->status);
+ break;
+ case -ESHUTDOWN:
+ default:
+ nfc_err(&phy->udev->dev,
+ "Urb failure (status %d)\n",
+ urb->status);
+ }
+}
+
+static struct pn533_phy_ops usb_phy_ops = {
+ .send_frame = pn533_usb_send_frame,
+ .send_ack = pn533_usb_send_ack,
+ .abort_cmd = pn533_usb_abort_cmd,
+};
+
+static int pn533_usb_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ struct pn533 *priv;
+ struct pn533_usb_phy *phy;
+ struct usb_host_interface *iface_desc;
+ struct usb_endpoint_descriptor *endpoint;
+ int in_endpoint = 0;
+ int out_endpoint = 0;
+ int rc = -ENOMEM;
+ int i;
+ u32 protocols;
+ enum pn533_protocol_type protocol_type = PN533_PROTO_REQ_ACK_RESP;
+ struct pn533_frame_ops *fops = NULL;
+ unsigned char *in_buf;
+ int in_buf_len = PN533_EXT_FRAME_HEADER_LEN +
+ PN533_STD_FRAME_MAX_PAYLOAD_LEN +
+ PN533_STD_FRAME_TAIL_LEN;
+
+ phy = devm_kzalloc(&interface->dev, sizeof(*phy), GFP_KERNEL);
+ if (!phy)
+ return -ENOMEM;
+
+ in_buf = kzalloc(in_buf_len, GFP_KERNEL);
+ if (!in_buf)
+ return -ENOMEM;
+
+ phy->udev = usb_get_dev(interface_to_usbdev(interface));
+ phy->interface = interface;
+
+ iface_desc = interface->cur_altsetting;
+ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+ endpoint = &iface_desc->endpoint[i].desc;
+
+ if (!in_endpoint && usb_endpoint_is_bulk_in(endpoint))
+ in_endpoint = endpoint->bEndpointAddress;
+
+ if (!out_endpoint && usb_endpoint_is_bulk_out(endpoint))
+ out_endpoint = endpoint->bEndpointAddress;
+ }
+
+ if (!in_endpoint || !out_endpoint) {
+ nfc_err(&interface->dev,
+ "Could not find bulk-in or bulk-out endpoint\n");
+ rc = -ENODEV;
+ goto error;
+ }
+
+ phy->in_urb = usb_alloc_urb(0, GFP_KERNEL);
+ phy->out_urb = usb_alloc_urb(0, GFP_KERNEL);
+
+ if (!phy->in_urb || !phy->out_urb)
+ goto error;
+
+ usb_fill_bulk_urb(phy->in_urb, phy->udev,
+ usb_rcvbulkpipe(phy->udev, in_endpoint),
+ in_buf, in_buf_len, NULL, phy);
+
+ usb_fill_bulk_urb(phy->out_urb, phy->udev,
+ usb_sndbulkpipe(phy->udev, out_endpoint),
+ NULL, 0, pn533_send_complete, phy);
+
+
+ switch (id->driver_info) {
+ case PN533_DEVICE_STD:
+ protocols = PN533_ALL_PROTOCOLS;
+ break;
+
+ case PN533_DEVICE_PASORI:
+ protocols = PN533_NO_TYPE_B_PROTOCOLS;
+ break;
+
+ case PN533_DEVICE_ACR122U:
+ protocols = PN533_NO_TYPE_B_PROTOCOLS;
+ fops = &pn533_acr122_frame_ops;
+ protocol_type = PN533_PROTO_REQ_RESP,
+
+ rc = pn533_acr122_poweron_rdr(phy);
+ if (rc < 0) {
+ nfc_err(&interface->dev,
+ "Couldn't poweron the reader (error %d)\n", rc);
+ goto error;
+ }
+ break;
+
+ default:
+ nfc_err(&interface->dev, "Unknown device type %lu\n",
+ id->driver_info);
+ rc = -EINVAL;
+ goto error;
+ }
+
+ priv = pn533_register_device(id->driver_info, protocols, protocol_type,
+ phy, &usb_phy_ops, fops,
+ &phy->udev->dev, &interface->dev);
+
+ if (IS_ERR(priv)) {
+ rc = PTR_ERR(priv);
+ goto error;
+ }
+
+ phy->priv = priv;
+
+ usb_set_intfdata(interface, phy);
+
+ return 0;
+
+error:
+ usb_free_urb(phy->in_urb);
+ usb_free_urb(phy->out_urb);
+ usb_put_dev(phy->udev);
+ kfree(in_buf);
+
+ return rc;
+}
+
+static void pn533_usb_disconnect(struct usb_interface *interface)
+{
+ struct pn533_usb_phy *phy = usb_get_intfdata(interface);
+
+ if (!phy)
+ return;
+
+ pn533_unregister_device(phy->priv);
+
+ usb_set_intfdata(interface, NULL);
+
+ usb_kill_urb(phy->in_urb);
+ usb_kill_urb(phy->out_urb);
+
+ kfree(phy->in_urb->transfer_buffer);
+ usb_free_urb(phy->in_urb);
+ usb_free_urb(phy->out_urb);
+
+ nfc_info(&interface->dev, "NXP PN533 NFC device disconnected\n");
+}
+
+static struct usb_driver pn533_usb_driver = {
+ .name = "pn533_usb",
+ .probe = pn533_usb_probe,
+ .disconnect = pn533_usb_disconnect,
+ .id_table = pn533_usb_table,
+};
+
+module_usb_driver(pn533_usb_driver);
+
+MODULE_AUTHOR("Lauro Ramos Venancio <lauro.venancio@openbossa.org>");
+MODULE_AUTHOR("Aloisio Almeida Jr <aloisio.almeida@openbossa.org>");
+MODULE_AUTHOR("Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>");
+MODULE_DESCRIPTION("PN533 USB driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/nfc/pn544/Kconfig b/drivers/nfc/pn544/Kconfig
new file mode 100644
index 0000000..d8d6a24
--- /dev/null
+++ b/drivers/nfc/pn544/Kconfig
@@ -0,0 +1,35 @@
+config NFC_PN544
+ tristate
+ depends on m
+ depends on CRC_CCITT
+ ---help---
+ NXP PN544 core driver.
+ This is a driver based on the HCI NFC kernel layers and
+ will thus not work with NXP libnfc library.
+
+config NFC_PN544_I2C
+ depends on !KERNEL_3_17
+ depends on !KERNEL_3_13
+ tristate "NXP PN544 device support (I2C)"
+ depends on m
+ depends on NFC_HCI && I2C && NFC_SHDLC
+ select NFC_PN544
+ ---help---
+ This module adds support for the NXP pn544 i2c interface.
+ Select this if your platform is using the i2c bus.
+
+ If you choose to build a module, it'll be called pn544_i2c.
+ Say N if unsure.
+
+config NFC_PN544_MEI
+ tristate "NXP PN544 device support (MEI)"
+ depends on m
+ depends on NFC_HCI && NFC_MEI_PHY
+ select NFC_PN544
+ ---help---
+ This module adds support for the mei interface of adapters using
+ NXP pn544 chipsets. Select this if your pn544 chipset
+ is handled by Intel's Management Engine Interface on your platform.
+
+ If you choose to build a module, it'll be called pn544_mei.
+ Say N if unsure.
diff --git a/drivers/nfc/pn544/Makefile b/drivers/nfc/pn544/Makefile
new file mode 100644
index 0000000..c12e83c
--- /dev/null
+++ b/drivers/nfc/pn544/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for PN544 HCI based NFC driver
+#
+
+pn544_i2c-objs = i2c.o
+pn544_mei-objs = mei.o
+
+obj-$(CPTCFG_NFC_PN544) += pn544.o
+obj-$(CPTCFG_NFC_PN544_I2C) += pn544_i2c.o
+obj-$(CPTCFG_NFC_PN544_MEI) += pn544_mei.o
diff --git a/drivers/nfc/pn544/i2c.c b/drivers/nfc/pn544/i2c.c
new file mode 100644
index 0000000..f837c39
--- /dev/null
+++ b/drivers/nfc/pn544/i2c.c
@@ -0,0 +1,1120 @@
+/*
+ * I2C Link Layer for PN544 HCI based Driver
+ *
+ * Copyright (C) 2012 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/crc-ccitt.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+#include <linux/acpi.h>
+#include <linux/miscdevice.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/nfc.h>
+#include <linux/firmware.h>
+#include <linux/gpio/consumer.h>
+#include <linux/platform_data/pn544.h>
+#include <asm/unaligned.h>
+
+#include <net/nfc/hci.h>
+#include <net/nfc/llc.h>
+#include <net/nfc/nfc.h>
+
+#include "pn544.h"
+
+#define PN544_I2C_FRAME_HEADROOM 1
+#define PN544_I2C_FRAME_TAILROOM 2
+
+/* GPIO names */
+#define PN544_GPIO_NAME_IRQ "pn544_irq"
+#define PN544_GPIO_NAME_FW "pn544_fw"
+#define PN544_GPIO_NAME_EN "pn544_en"
+
+/* framing in HCI mode */
+#define PN544_HCI_I2C_LLC_LEN 1
+#define PN544_HCI_I2C_LLC_CRC 2
+#define PN544_HCI_I2C_LLC_LEN_CRC (PN544_HCI_I2C_LLC_LEN + \
+ PN544_HCI_I2C_LLC_CRC)
+#define PN544_HCI_I2C_LLC_MIN_SIZE (1 + PN544_HCI_I2C_LLC_LEN_CRC)
+#define PN544_HCI_I2C_LLC_MAX_PAYLOAD 29
+#define PN544_HCI_I2C_LLC_MAX_SIZE (PN544_HCI_I2C_LLC_LEN_CRC + 1 + \
+ PN544_HCI_I2C_LLC_MAX_PAYLOAD)
+
+static struct i2c_device_id pn544_hci_i2c_id_table[] = {
+ {"pn544", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, pn544_hci_i2c_id_table);
+
+static const struct acpi_device_id pn544_hci_i2c_acpi_match[] = {
+ {"NXP5440", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(acpi, pn544_hci_i2c_acpi_match);
+
+#define PN544_HCI_I2C_DRIVER_NAME "pn544_hci_i2c"
+
+/*
+ * Exposed through the 4 most significant bytes
+ * from the HCI SW_VERSION first byte, a.k.a.
+ * SW RomLib.
+ */
+#define PN544_HW_VARIANT_C2 0xa
+#define PN544_HW_VARIANT_C3 0xb
+
+#define PN544_FW_CMD_RESET 0x01
+#define PN544_FW_CMD_WRITE 0x08
+#define PN544_FW_CMD_CHECK 0x06
+#define PN544_FW_CMD_SECURE_WRITE 0x0C
+#define PN544_FW_CMD_SECURE_CHUNK_WRITE 0x0D
+
+struct pn544_i2c_fw_frame_write {
+ u8 cmd;
+ u16 be_length;
+ u8 be_dest_addr[3];
+ u16 be_datalen;
+ u8 data[];
+} __packed;
+
+struct pn544_i2c_fw_frame_check {
+ u8 cmd;
+ u16 be_length;
+ u8 be_start_addr[3];
+ u16 be_datalen;
+ u16 be_crc;
+} __packed;
+
+struct pn544_i2c_fw_frame_response {
+ u8 status;
+ u16 be_length;
+} __packed;
+
+struct pn544_i2c_fw_blob {
+ u32 be_size;
+ u32 be_destaddr;
+ u8 data[];
+};
+
+struct pn544_i2c_fw_secure_frame {
+ u8 cmd;
+ u16 be_datalen;
+ u8 data[];
+} __packed;
+
+struct pn544_i2c_fw_secure_blob {
+ u64 header;
+ u8 data[];
+};
+
+#define PN544_FW_CMD_RESULT_TIMEOUT 0x01
+#define PN544_FW_CMD_RESULT_BAD_CRC 0x02
+#define PN544_FW_CMD_RESULT_ACCESS_DENIED 0x08
+#define PN544_FW_CMD_RESULT_PROTOCOL_ERROR 0x0B
+#define PN544_FW_CMD_RESULT_INVALID_PARAMETER 0x11
+#define PN544_FW_CMD_RESULT_UNSUPPORTED_COMMAND 0x13
+#define PN544_FW_CMD_RESULT_INVALID_LENGTH 0x18
+#define PN544_FW_CMD_RESULT_CRYPTOGRAPHIC_ERROR 0x19
+#define PN544_FW_CMD_RESULT_VERSION_CONDITIONS_ERROR 0x1D
+#define PN544_FW_CMD_RESULT_MEMORY_ERROR 0x20
+#define PN544_FW_CMD_RESULT_CHUNK_OK 0x21
+#define PN544_FW_CMD_RESULT_WRITE_FAILED 0x74
+#define PN544_FW_CMD_RESULT_COMMAND_REJECTED 0xE0
+#define PN544_FW_CMD_RESULT_CHUNK_ERROR 0xE6
+
+#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
+
+#define PN544_FW_WRITE_BUFFER_MAX_LEN 0x9f7
+#define PN544_FW_I2C_MAX_PAYLOAD PN544_HCI_I2C_LLC_MAX_SIZE
+#define PN544_FW_I2C_WRITE_FRAME_HEADER_LEN 8
+#define PN544_FW_I2C_WRITE_DATA_MAX_LEN MIN((PN544_FW_I2C_MAX_PAYLOAD -\
+ PN544_FW_I2C_WRITE_FRAME_HEADER_LEN),\
+ PN544_FW_WRITE_BUFFER_MAX_LEN)
+#define PN544_FW_SECURE_CHUNK_WRITE_HEADER_LEN 3
+#define PN544_FW_SECURE_CHUNK_WRITE_DATA_MAX_LEN (PN544_FW_I2C_MAX_PAYLOAD -\
+ PN544_FW_SECURE_CHUNK_WRITE_HEADER_LEN)
+#define PN544_FW_SECURE_FRAME_HEADER_LEN 3
+#define PN544_FW_SECURE_BLOB_HEADER_LEN 8
+
+#define FW_WORK_STATE_IDLE 1
+#define FW_WORK_STATE_START 2
+#define FW_WORK_STATE_WAIT_WRITE_ANSWER 3
+#define FW_WORK_STATE_WAIT_CHECK_ANSWER 4
+#define FW_WORK_STATE_WAIT_SECURE_WRITE_ANSWER 5
+
+struct pn544_i2c_phy {
+ struct i2c_client *i2c_dev;
+ struct nfc_hci_dev *hdev;
+
+ unsigned int gpio_en;
+ unsigned int gpio_fw;
+ unsigned int en_polarity;
+
+ u8 hw_variant;
+
+ struct work_struct fw_work;
+ int fw_work_state;
+ char firmware_name[NFC_FIRMWARE_NAME_MAXSIZE + 1];
+ const struct firmware *fw;
+ u32 fw_blob_dest_addr;
+ size_t fw_blob_size;
+ const u8 *fw_blob_data;
+ size_t fw_written;
+ size_t fw_size;
+
+ int fw_cmd_result;
+
+ int powered;
+ int run_mode;
+
+ int hard_fault; /*
+ * < 0 if hardware error occured (e.g. i2c err)
+ * and prevents normal operation.
+ */
+};
+
+#define I2C_DUMP_SKB(info, skb) \
+do { \
+ pr_debug("%s:\n", info); \
+ print_hex_dump(KERN_DEBUG, "i2c: ", DUMP_PREFIX_OFFSET, \
+ 16, 1, (skb)->data, (skb)->len, 0); \
+} while (0)
+
+static void pn544_hci_i2c_platform_init(struct pn544_i2c_phy *phy)
+{
+ int polarity, retry, ret;
+ char rset_cmd[] = { 0x05, 0xF9, 0x04, 0x00, 0xC3, 0xE5 };
+ int count = sizeof(rset_cmd);
+
+ nfc_info(&phy->i2c_dev->dev, "Detecting nfc_en polarity\n");
+
+ /* Disable fw download */
+ gpio_set_value_cansleep(phy->gpio_fw, 0);
+
+ for (polarity = 0; polarity < 2; polarity++) {
+ phy->en_polarity = polarity;
+ retry = 3;
+ while (retry--) {
+ /* power off */
+ gpio_set_value_cansleep(phy->gpio_en,
+ !phy->en_polarity);
+ usleep_range(10000, 15000);
+
+ /* power on */
+ gpio_set_value_cansleep(phy->gpio_en, phy->en_polarity);
+ usleep_range(10000, 15000);
+
+ /* send reset */
+ dev_dbg(&phy->i2c_dev->dev, "Sending reset cmd\n");
+ ret = i2c_master_send(phy->i2c_dev, rset_cmd, count);
+ if (ret == count) {
+ nfc_info(&phy->i2c_dev->dev,
+ "nfc_en polarity : active %s\n",
+ (polarity == 0 ? "low" : "high"));
+ goto out;
+ }
+ }
+ }
+
+ nfc_err(&phy->i2c_dev->dev,
+ "Could not detect nfc_en polarity, fallback to active high\n");
+
+out:
+ gpio_set_value_cansleep(phy->gpio_en, !phy->en_polarity);
+}
+
+static void pn544_hci_i2c_enable_mode(struct pn544_i2c_phy *phy, int run_mode)
+{
+ gpio_set_value_cansleep(phy->gpio_fw,
+ run_mode == PN544_FW_MODE ? 1 : 0);
+ gpio_set_value_cansleep(phy->gpio_en, phy->en_polarity);
+ usleep_range(10000, 15000);
+
+ phy->run_mode = run_mode;
+}
+
+static int pn544_hci_i2c_enable(void *phy_id)
+{
+ struct pn544_i2c_phy *phy = phy_id;
+
+ pr_info("%s\n", __func__);
+
+ pn544_hci_i2c_enable_mode(phy, PN544_HCI_MODE);
+
+ phy->powered = 1;
+
+ return 0;
+}
+
+static void pn544_hci_i2c_disable(void *phy_id)
+{
+ struct pn544_i2c_phy *phy = phy_id;
+
+ gpio_set_value_cansleep(phy->gpio_fw, 0);
+ gpio_set_value_cansleep(phy->gpio_en, !phy->en_polarity);
+ usleep_range(10000, 15000);
+
+ gpio_set_value_cansleep(phy->gpio_en, phy->en_polarity);
+ usleep_range(10000, 15000);
+
+ gpio_set_value_cansleep(phy->gpio_en, !phy->en_polarity);
+ usleep_range(10000, 15000);
+
+ phy->powered = 0;
+}
+
+static void pn544_hci_i2c_add_len_crc(struct sk_buff *skb)
+{
+ u16 crc;
+ int len;
+
+ len = skb->len + 2;
+ *skb_push(skb, 1) = len;
+
+ crc = crc_ccitt(0xffff, skb->data, skb->len);
+ crc = ~crc;
+ *skb_put(skb, 1) = crc & 0xff;
+ *skb_put(skb, 1) = crc >> 8;
+}
+
+static void pn544_hci_i2c_remove_len_crc(struct sk_buff *skb)
+{
+ skb_pull(skb, PN544_I2C_FRAME_HEADROOM);
+ skb_trim(skb, PN544_I2C_FRAME_TAILROOM);
+}
+
+/*
+ * Writing a frame must not return the number of written bytes.
+ * It must return either zero for success, or <0 for error.
+ * In addition, it must not alter the skb
+ */
+static int pn544_hci_i2c_write(void *phy_id, struct sk_buff *skb)
+{
+ int r;
+ struct pn544_i2c_phy *phy = phy_id;
+ struct i2c_client *client = phy->i2c_dev;
+
+ if (phy->hard_fault != 0)
+ return phy->hard_fault;
+
+ usleep_range(3000, 6000);
+
+ pn544_hci_i2c_add_len_crc(skb);
+
+ I2C_DUMP_SKB("i2c frame written", skb);
+
+ r = i2c_master_send(client, skb->data, skb->len);
+
+ if (r == -EREMOTEIO) { /* Retry, chip was in standby */
+ usleep_range(6000, 10000);
+ r = i2c_master_send(client, skb->data, skb->len);
+ }
+
+ if (r >= 0) {
+ if (r != skb->len)
+ r = -EREMOTEIO;
+ else
+ r = 0;
+ }
+
+ pn544_hci_i2c_remove_len_crc(skb);
+
+ return r;
+}
+
+static int check_crc(u8 *buf, int buflen)
+{
+ int len;
+ u16 crc;
+
+ len = buf[0] + 1;
+ crc = crc_ccitt(0xffff, buf, len - 2);
+ crc = ~crc;
+
+ if (buf[len - 2] != (crc & 0xff) || buf[len - 1] != (crc >> 8)) {
+ pr_err("CRC error 0x%x != 0x%x 0x%x\n",
+ crc, buf[len - 1], buf[len - 2]);
+ pr_info("%s: BAD CRC\n", __func__);
+ print_hex_dump(KERN_DEBUG, "crc: ", DUMP_PREFIX_NONE,
+ 16, 2, buf, buflen, false);
+ return -EPERM;
+ }
+ return 0;
+}
+
+/*
+ * Reads an shdlc frame and returns it in a newly allocated sk_buff. Guarantees
+ * that i2c bus will be flushed and that next read will start on a new frame.
+ * returned skb contains only LLC header and payload.
+ * returns:
+ * -EREMOTEIO : i2c read error (fatal)
+ * -EBADMSG : frame was incorrect and discarded
+ * -ENOMEM : cannot allocate skb, frame dropped
+ */
+static int pn544_hci_i2c_read(struct pn544_i2c_phy *phy, struct sk_buff **skb)
+{
+ int r;
+ u8 len;
+ u8 tmp[PN544_HCI_I2C_LLC_MAX_SIZE - 1];
+ struct i2c_client *client = phy->i2c_dev;
+
+ r = i2c_master_recv(client, &len, 1);
+ if (r != 1) {
+ nfc_err(&client->dev, "cannot read len byte\n");
+ return -EREMOTEIO;
+ }
+
+ if ((len < (PN544_HCI_I2C_LLC_MIN_SIZE - 1)) ||
+ (len > (PN544_HCI_I2C_LLC_MAX_SIZE - 1))) {
+ nfc_err(&client->dev, "invalid len byte\n");
+ r = -EBADMSG;
+ goto flush;
+ }
+
+ *skb = alloc_skb(1 + len, GFP_KERNEL);
+ if (*skb == NULL) {
+ r = -ENOMEM;
+ goto flush;
+ }
+
+ *skb_put(*skb, 1) = len;
+
+ r = i2c_master_recv(client, skb_put(*skb, len), len);
+ if (r != len) {
+ kfree_skb(*skb);
+ return -EREMOTEIO;
+ }
+
+ I2C_DUMP_SKB("i2c frame read", *skb);
+
+ r = check_crc((*skb)->data, (*skb)->len);
+ if (r != 0) {
+ kfree_skb(*skb);
+ r = -EBADMSG;
+ goto flush;
+ }
+
+ skb_pull(*skb, 1);
+ skb_trim(*skb, (*skb)->len - 2);
+
+ usleep_range(3000, 6000);
+
+ return 0;
+
+flush:
+ if (i2c_master_recv(client, tmp, sizeof(tmp)) < 0)
+ r = -EREMOTEIO;
+
+ usleep_range(3000, 6000);
+
+ return r;
+}
+
+static int pn544_hci_i2c_fw_read_status(struct pn544_i2c_phy *phy)
+{
+ int r;
+ struct pn544_i2c_fw_frame_response response;
+ struct i2c_client *client = phy->i2c_dev;
+
+ r = i2c_master_recv(client, (char *) &response, sizeof(response));
+ if (r != sizeof(response)) {
+ nfc_err(&client->dev, "cannot read fw status\n");
+ return -EIO;
+ }
+
+ usleep_range(3000, 6000);
+
+ switch (response.status) {
+ case 0:
+ return 0;
+ case PN544_FW_CMD_RESULT_CHUNK_OK:
+ return response.status;
+ case PN544_FW_CMD_RESULT_TIMEOUT:
+ return -ETIMEDOUT;
+ case PN544_FW_CMD_RESULT_BAD_CRC:
+ return -ENODATA;
+ case PN544_FW_CMD_RESULT_ACCESS_DENIED:
+ return -EACCES;
+ case PN544_FW_CMD_RESULT_PROTOCOL_ERROR:
+ return -EPROTO;
+ case PN544_FW_CMD_RESULT_INVALID_PARAMETER:
+ return -EINVAL;
+ case PN544_FW_CMD_RESULT_UNSUPPORTED_COMMAND:
+ return -ENOTSUPP;
+ case PN544_FW_CMD_RESULT_INVALID_LENGTH:
+ return -EBADMSG;
+ case PN544_FW_CMD_RESULT_CRYPTOGRAPHIC_ERROR:
+ return -ENOKEY;
+ case PN544_FW_CMD_RESULT_VERSION_CONDITIONS_ERROR:
+ return -EINVAL;
+ case PN544_FW_CMD_RESULT_MEMORY_ERROR:
+ return -ENOMEM;
+ case PN544_FW_CMD_RESULT_COMMAND_REJECTED:
+ return -EACCES;
+ case PN544_FW_CMD_RESULT_WRITE_FAILED:
+ case PN544_FW_CMD_RESULT_CHUNK_ERROR:
+ return -EIO;
+ default:
+ return -EIO;
+ }
+}
+
+/*
+ * Reads an shdlc frame from the chip. This is not as straightforward as it
+ * seems. There are cases where we could loose the frame start synchronization.
+ * The frame format is len-data-crc, and corruption can occur anywhere while
+ * transiting on i2c bus, such that we could read an invalid len.
+ * In order to recover synchronization with the next frame, we must be sure
+ * to read the real amount of data without using the len byte. We do this by
+ * assuming the following:
+ * - the chip will always present only one single complete frame on the bus
+ * before triggering the interrupt
+ * - the chip will not present a new frame until we have completely read
+ * the previous one (or until we have handled the interrupt).
+ * The tricky case is when we read a corrupted len that is less than the real
+ * len. We must detect this here in order to determine that we need to flush
+ * the bus. This is the reason why we check the crc here.
+ */
+static irqreturn_t pn544_hci_i2c_irq_thread_fn(int irq, void *phy_id)
+{
+ struct pn544_i2c_phy *phy = phy_id;
+ struct i2c_client *client;
+ struct sk_buff *skb = NULL;
+ int r;
+
+ if (!phy || irq != phy->i2c_dev->irq) {
+ WARN_ON_ONCE(1);
+ return IRQ_NONE;
+ }
+
+ client = phy->i2c_dev;
+ dev_dbg(&client->dev, "IRQ\n");
+
+ if (phy->hard_fault != 0)
+ return IRQ_HANDLED;
+
+ if (phy->run_mode == PN544_FW_MODE) {
+ phy->fw_cmd_result = pn544_hci_i2c_fw_read_status(phy);
+ schedule_work(&phy->fw_work);
+ } else {
+ r = pn544_hci_i2c_read(phy, &skb);
+ if (r == -EREMOTEIO) {
+ phy->hard_fault = r;
+
+ nfc_hci_recv_frame(phy->hdev, NULL);
+
+ return IRQ_HANDLED;
+ } else if ((r == -ENOMEM) || (r == -EBADMSG)) {
+ return IRQ_HANDLED;
+ }
+
+ nfc_hci_recv_frame(phy->hdev, skb);
+ }
+ return IRQ_HANDLED;
+}
+
+static struct nfc_phy_ops i2c_phy_ops = {
+ .write = pn544_hci_i2c_write,
+ .enable = pn544_hci_i2c_enable,
+ .disable = pn544_hci_i2c_disable,
+};
+
+static int pn544_hci_i2c_fw_download(void *phy_id, const char *firmware_name,
+ u8 hw_variant)
+{
+ struct pn544_i2c_phy *phy = phy_id;
+
+ pr_info("Starting Firmware Download (%s)\n", firmware_name);
+
+ strcpy(phy->firmware_name, firmware_name);
+
+ phy->hw_variant = hw_variant;
+ phy->fw_work_state = FW_WORK_STATE_START;
+
+ schedule_work(&phy->fw_work);
+
+ return 0;
+}
+
+static void pn544_hci_i2c_fw_work_complete(struct pn544_i2c_phy *phy,
+ int result)
+{
+ pr_info("Firmware Download Complete, result=%d\n", result);
+
+ pn544_hci_i2c_disable(phy);
+
+ phy->fw_work_state = FW_WORK_STATE_IDLE;
+
+ if (phy->fw) {
+ release_firmware(phy->fw);
+ phy->fw = NULL;
+ }
+
+ nfc_fw_download_done(phy->hdev->ndev, phy->firmware_name, (u32) -result);
+}
+
+static int pn544_hci_i2c_fw_write_cmd(struct i2c_client *client, u32 dest_addr,
+ const u8 *data, u16 datalen)
+{
+ u8 frame[PN544_FW_I2C_MAX_PAYLOAD];
+ struct pn544_i2c_fw_frame_write *framep;
+ u16 params_len;
+ int framelen;
+ int r;
+
+ if (datalen > PN544_FW_I2C_WRITE_DATA_MAX_LEN)
+ datalen = PN544_FW_I2C_WRITE_DATA_MAX_LEN;
+
+ framep = (struct pn544_i2c_fw_frame_write *) frame;
+
+ params_len = sizeof(framep->be_dest_addr) +
+ sizeof(framep->be_datalen) + datalen;
+ framelen = params_len + sizeof(framep->cmd) +
+ sizeof(framep->be_length);
+
+ framep->cmd = PN544_FW_CMD_WRITE;
+
+ put_unaligned_be16(params_len, &framep->be_length);
+
+ framep->be_dest_addr[0] = (dest_addr & 0xff0000) >> 16;
+ framep->be_dest_addr[1] = (dest_addr & 0xff00) >> 8;
+ framep->be_dest_addr[2] = dest_addr & 0xff;
+
+ put_unaligned_be16(datalen, &framep->be_datalen);
+
+ memcpy(framep->data, data, datalen);
+
+ r = i2c_master_send(client, frame, framelen);
+
+ if (r == framelen)
+ return datalen;
+ else if (r < 0)
+ return r;
+ else
+ return -EIO;
+}
+
+static int pn544_hci_i2c_fw_check_cmd(struct i2c_client *client, u32 start_addr,
+ const u8 *data, u16 datalen)
+{
+ struct pn544_i2c_fw_frame_check frame;
+ int r;
+ u16 crc;
+
+ /* calculate local crc for the data we want to check */
+ crc = crc_ccitt(0xffff, data, datalen);
+
+ frame.cmd = PN544_FW_CMD_CHECK;
+
+ put_unaligned_be16(sizeof(frame.be_start_addr) +
+ sizeof(frame.be_datalen) + sizeof(frame.be_crc),
+ &frame.be_length);
+
+ /* tell the chip the memory region to which our crc applies */
+ frame.be_start_addr[0] = (start_addr & 0xff0000) >> 16;
+ frame.be_start_addr[1] = (start_addr & 0xff00) >> 8;
+ frame.be_start_addr[2] = start_addr & 0xff;
+
+ put_unaligned_be16(datalen, &frame.be_datalen);
+
+ /*
+ * and give our local crc. Chip will calculate its own crc for the
+ * region and compare with ours.
+ */
+ put_unaligned_be16(crc, &frame.be_crc);
+
+ r = i2c_master_send(client, (const char *) &frame, sizeof(frame));
+
+ if (r == sizeof(frame))
+ return 0;
+ else if (r < 0)
+ return r;
+ else
+ return -EIO;
+}
+
+static int pn544_hci_i2c_fw_write_chunk(struct pn544_i2c_phy *phy)
+{
+ int r;
+
+ r = pn544_hci_i2c_fw_write_cmd(phy->i2c_dev,
+ phy->fw_blob_dest_addr + phy->fw_written,
+ phy->fw_blob_data + phy->fw_written,
+ phy->fw_blob_size - phy->fw_written);
+ if (r < 0)
+ return r;
+
+ phy->fw_written += r;
+ phy->fw_work_state = FW_WORK_STATE_WAIT_WRITE_ANSWER;
+
+ return 0;
+}
+
+static int pn544_hci_i2c_fw_secure_write_frame_cmd(struct pn544_i2c_phy *phy,
+ const u8 *data, u16 datalen)
+{
+ u8 buf[PN544_FW_I2C_MAX_PAYLOAD];
+ struct pn544_i2c_fw_secure_frame *chunk;
+ int chunklen;
+ int r;
+
+ if (datalen > PN544_FW_SECURE_CHUNK_WRITE_DATA_MAX_LEN)
+ datalen = PN544_FW_SECURE_CHUNK_WRITE_DATA_MAX_LEN;
+
+ chunk = (struct pn544_i2c_fw_secure_frame *) buf;
+
+ chunk->cmd = PN544_FW_CMD_SECURE_CHUNK_WRITE;
+
+ put_unaligned_be16(datalen, &chunk->be_datalen);
+
+ memcpy(chunk->data, data, datalen);
+
+ chunklen = sizeof(chunk->cmd) + sizeof(chunk->be_datalen) + datalen;
+
+ r = i2c_master_send(phy->i2c_dev, buf, chunklen);
+
+ if (r == chunklen)
+ return datalen;
+ else if (r < 0)
+ return r;
+ else
+ return -EIO;
+
+}
+
+static int pn544_hci_i2c_fw_secure_write_frame(struct pn544_i2c_phy *phy)
+{
+ struct pn544_i2c_fw_secure_frame *framep;
+ int r;
+
+ framep = (struct pn544_i2c_fw_secure_frame *) phy->fw_blob_data;
+ if (phy->fw_written == 0)
+ phy->fw_blob_size = get_unaligned_be16(&framep->be_datalen)
+ + PN544_FW_SECURE_FRAME_HEADER_LEN;
+
+ /* Only secure write command can be chunked*/
+ if (phy->fw_blob_size > PN544_FW_I2C_MAX_PAYLOAD &&
+ framep->cmd != PN544_FW_CMD_SECURE_WRITE)
+ return -EINVAL;
+
+ /* The firmware also have other commands, we just send them directly */
+ if (phy->fw_blob_size < PN544_FW_I2C_MAX_PAYLOAD) {
+ r = i2c_master_send(phy->i2c_dev,
+ (const char *) phy->fw_blob_data, phy->fw_blob_size);
+
+ if (r == phy->fw_blob_size)
+ goto exit;
+ else if (r < 0)
+ return r;
+ else
+ return -EIO;
+ }
+
+ r = pn544_hci_i2c_fw_secure_write_frame_cmd(phy,
+ phy->fw_blob_data + phy->fw_written,
+ phy->fw_blob_size - phy->fw_written);
+ if (r < 0)
+ return r;
+
+exit:
+ phy->fw_written += r;
+ phy->fw_work_state = FW_WORK_STATE_WAIT_SECURE_WRITE_ANSWER;
+
+ /* SW reset command will not trig any response from PN544 */
+ if (framep->cmd == PN544_FW_CMD_RESET) {
+ pn544_hci_i2c_enable_mode(phy, PN544_FW_MODE);
+ phy->fw_cmd_result = 0;
+ schedule_work(&phy->fw_work);
+ }
+
+ return 0;
+}
+
+static void pn544_hci_i2c_fw_work(struct work_struct *work)
+{
+ struct pn544_i2c_phy *phy = container_of(work, struct pn544_i2c_phy,
+ fw_work);
+ int r;
+ struct pn544_i2c_fw_blob *blob;
+ struct pn544_i2c_fw_secure_blob *secure_blob;
+
+ switch (phy->fw_work_state) {
+ case FW_WORK_STATE_START:
+ pn544_hci_i2c_enable_mode(phy, PN544_FW_MODE);
+
+ r = request_firmware(&phy->fw, phy->firmware_name,
+ &phy->i2c_dev->dev);
+ if (r < 0)
+ goto exit_state_start;
+
+ phy->fw_written = 0;
+
+ switch (phy->hw_variant) {
+ case PN544_HW_VARIANT_C2:
+ blob = (struct pn544_i2c_fw_blob *) phy->fw->data;
+ phy->fw_blob_size = get_unaligned_be32(&blob->be_size);
+ phy->fw_blob_dest_addr = get_unaligned_be32(
+ &blob->be_destaddr);
+ phy->fw_blob_data = blob->data;
+
+ r = pn544_hci_i2c_fw_write_chunk(phy);
+ break;
+ case PN544_HW_VARIANT_C3:
+ secure_blob = (struct pn544_i2c_fw_secure_blob *)
+ phy->fw->data;
+ phy->fw_blob_data = secure_blob->data;
+ phy->fw_size = phy->fw->size;
+ r = pn544_hci_i2c_fw_secure_write_frame(phy);
+ break;
+ default:
+ r = -ENOTSUPP;
+ break;
+ }
+
+exit_state_start:
+ if (r < 0)
+ pn544_hci_i2c_fw_work_complete(phy, r);
+ break;
+
+ case FW_WORK_STATE_WAIT_WRITE_ANSWER:
+ r = phy->fw_cmd_result;
+ if (r < 0)
+ goto exit_state_wait_write_answer;
+
+ if (phy->fw_written == phy->fw_blob_size) {
+ r = pn544_hci_i2c_fw_check_cmd(phy->i2c_dev,
+ phy->fw_blob_dest_addr,
+ phy->fw_blob_data,
+ phy->fw_blob_size);
+ if (r < 0)
+ goto exit_state_wait_write_answer;
+ phy->fw_work_state = FW_WORK_STATE_WAIT_CHECK_ANSWER;
+ break;
+ }
+
+ r = pn544_hci_i2c_fw_write_chunk(phy);
+
+exit_state_wait_write_answer:
+ if (r < 0)
+ pn544_hci_i2c_fw_work_complete(phy, r);
+ break;
+
+ case FW_WORK_STATE_WAIT_CHECK_ANSWER:
+ r = phy->fw_cmd_result;
+ if (r < 0)
+ goto exit_state_wait_check_answer;
+
+ blob = (struct pn544_i2c_fw_blob *) (phy->fw_blob_data +
+ phy->fw_blob_size);
+ phy->fw_blob_size = get_unaligned_be32(&blob->be_size);
+ if (phy->fw_blob_size != 0) {
+ phy->fw_blob_dest_addr =
+ get_unaligned_be32(&blob->be_destaddr);
+ phy->fw_blob_data = blob->data;
+
+ phy->fw_written = 0;
+ r = pn544_hci_i2c_fw_write_chunk(phy);
+ }
+
+exit_state_wait_check_answer:
+ if (r < 0 || phy->fw_blob_size == 0)
+ pn544_hci_i2c_fw_work_complete(phy, r);
+ break;
+
+ case FW_WORK_STATE_WAIT_SECURE_WRITE_ANSWER:
+ r = phy->fw_cmd_result;
+ if (r < 0)
+ goto exit_state_wait_secure_write_answer;
+
+ if (r == PN544_FW_CMD_RESULT_CHUNK_OK) {
+ r = pn544_hci_i2c_fw_secure_write_frame(phy);
+ goto exit_state_wait_secure_write_answer;
+ }
+
+ if (phy->fw_written == phy->fw_blob_size) {
+ secure_blob = (struct pn544_i2c_fw_secure_blob *)
+ (phy->fw_blob_data + phy->fw_blob_size);
+ phy->fw_size -= phy->fw_blob_size +
+ PN544_FW_SECURE_BLOB_HEADER_LEN;
+ if (phy->fw_size >= PN544_FW_SECURE_BLOB_HEADER_LEN
+ + PN544_FW_SECURE_FRAME_HEADER_LEN) {
+ phy->fw_blob_data = secure_blob->data;
+
+ phy->fw_written = 0;
+ r = pn544_hci_i2c_fw_secure_write_frame(phy);
+ }
+ }
+
+exit_state_wait_secure_write_answer:
+ if (r < 0 || phy->fw_size == 0)
+ pn544_hci_i2c_fw_work_complete(phy, r);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static int pn544_hci_i2c_acpi_request_resources(struct i2c_client *client)
+{
+ struct pn544_i2c_phy *phy = i2c_get_clientdata(client);
+ struct gpio_desc *gpiod_en, *gpiod_fw;
+ struct device *dev = &client->dev;
+
+ /* Get EN GPIO from ACPI */
+ gpiod_en = devm_gpiod_get_index(dev, PN544_GPIO_NAME_EN, 1,
+ GPIOD_OUT_LOW);
+ if (IS_ERR(gpiod_en)) {
+ nfc_err(dev, "Unable to get EN GPIO\n");
+ return -ENODEV;
+ }
+
+ phy->gpio_en = desc_to_gpio(gpiod_en);
+
+ /* Get FW GPIO from ACPI */
+ gpiod_fw = devm_gpiod_get_index(dev, PN544_GPIO_NAME_FW, 2,
+ GPIOD_OUT_LOW);
+ if (IS_ERR(gpiod_fw)) {
+ nfc_err(dev, "Unable to get FW GPIO\n");
+ return -ENODEV;
+ }
+
+ phy->gpio_fw = desc_to_gpio(gpiod_fw);
+
+ return 0;
+}
+
+static int pn544_hci_i2c_of_request_resources(struct i2c_client *client)
+{
+ struct pn544_i2c_phy *phy = i2c_get_clientdata(client);
+ struct device_node *pp;
+ int ret;
+
+ pp = client->dev.of_node;
+ if (!pp) {
+ ret = -ENODEV;
+ goto err_dt;
+ }
+
+ /* Obtention of EN GPIO from device tree */
+ ret = of_get_named_gpio(pp, "enable-gpios", 0);
+ if (ret < 0) {
+ if (ret != -EPROBE_DEFER)
+ nfc_err(&client->dev,
+ "Failed to get EN gpio, error: %d\n", ret);
+ goto err_dt;
+ }
+ phy->gpio_en = ret;
+
+ /* Configuration of EN GPIO */
+ ret = gpio_request(phy->gpio_en, PN544_GPIO_NAME_EN);
+ if (ret) {
+ nfc_err(&client->dev, "Fail EN pin\n");
+ goto err_dt;
+ }
+ ret = gpio_direction_output(phy->gpio_en, 0);
+ if (ret) {
+ nfc_err(&client->dev, "Fail EN pin direction\n");
+ goto err_gpio_en;
+ }
+
+ /* Obtention of FW GPIO from device tree */
+ ret = of_get_named_gpio(pp, "firmware-gpios", 0);
+ if (ret < 0) {
+ if (ret != -EPROBE_DEFER)
+ nfc_err(&client->dev,
+ "Failed to get FW gpio, error: %d\n", ret);
+ goto err_gpio_en;
+ }
+ phy->gpio_fw = ret;
+
+ /* Configuration of FW GPIO */
+ ret = gpio_request(phy->gpio_fw, PN544_GPIO_NAME_FW);
+ if (ret) {
+ nfc_err(&client->dev, "Fail FW pin\n");
+ goto err_gpio_en;
+ }
+ ret = gpio_direction_output(phy->gpio_fw, 0);
+ if (ret) {
+ nfc_err(&client->dev, "Fail FW pin direction\n");
+ goto err_gpio_fw;
+ }
+
+ return 0;
+
+err_gpio_fw:
+ gpio_free(phy->gpio_fw);
+err_gpio_en:
+ gpio_free(phy->gpio_en);
+err_dt:
+ return ret;
+}
+
+static int pn544_hci_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct pn544_i2c_phy *phy;
+ struct pn544_nfc_platform_data *pdata;
+ 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, "Need I2C_FUNC_I2C\n");
+ return -ENODEV;
+ }
+
+ phy = devm_kzalloc(&client->dev, sizeof(struct pn544_i2c_phy),
+ GFP_KERNEL);
+ if (!phy)
+ return -ENOMEM;
+
+ INIT_WORK(&phy->fw_work, pn544_hci_i2c_fw_work);
+ phy->fw_work_state = FW_WORK_STATE_IDLE;
+
+ phy->i2c_dev = client;
+ i2c_set_clientdata(client, phy);
+
+ pdata = client->dev.platform_data;
+
+ /* No platform data, using device tree. */
+ if (!pdata && client->dev.of_node) {
+ r = pn544_hci_i2c_of_request_resources(client);
+ if (r) {
+ nfc_err(&client->dev, "No DT data\n");
+ return r;
+ }
+ /* Using platform data. */
+ } else if (pdata) {
+
+ if (pdata->request_resources == NULL) {
+ nfc_err(&client->dev, "request_resources() missing\n");
+ return -EINVAL;
+ }
+
+ r = pdata->request_resources(client);
+ if (r) {
+ nfc_err(&client->dev,
+ "Cannot get platform resources\n");
+ return r;
+ }
+
+ phy->gpio_en = pdata->get_gpio(NFC_GPIO_ENABLE);
+ phy->gpio_fw = pdata->get_gpio(NFC_GPIO_FW_RESET);
+ /* Using ACPI */
+ } else if (ACPI_HANDLE(&client->dev)) {
+ r = pn544_hci_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;
+ }
+
+ pn544_hci_i2c_platform_init(phy);
+
+ r = request_threaded_irq(client->irq, NULL, pn544_hci_i2c_irq_thread_fn,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ PN544_HCI_I2C_DRIVER_NAME, phy);
+ if (r < 0) {
+ nfc_err(&client->dev, "Unable to register IRQ handler\n");
+ goto err_rti;
+ }
+
+ r = pn544_hci_probe(phy, &i2c_phy_ops, LLC_SHDLC_NAME,
+ PN544_I2C_FRAME_HEADROOM, PN544_I2C_FRAME_TAILROOM,
+ PN544_HCI_I2C_LLC_MAX_PAYLOAD,
+ pn544_hci_i2c_fw_download, &phy->hdev);
+ if (r < 0)
+ goto err_hci;
+
+ return 0;
+
+err_hci:
+ free_irq(client->irq, phy);
+
+err_rti:
+ if (!pdata) {
+ gpio_free(phy->gpio_en);
+ gpio_free(phy->gpio_fw);
+ } else if (pdata->free_resources) {
+ pdata->free_resources();
+ }
+
+ return r;
+}
+
+static int pn544_hci_i2c_remove(struct i2c_client *client)
+{
+ struct pn544_i2c_phy *phy = i2c_get_clientdata(client);
+ struct pn544_nfc_platform_data *pdata = client->dev.platform_data;
+
+ dev_dbg(&client->dev, "%s\n", __func__);
+
+ cancel_work_sync(&phy->fw_work);
+ if (phy->fw_work_state != FW_WORK_STATE_IDLE)
+ pn544_hci_i2c_fw_work_complete(phy, -ENODEV);
+
+ pn544_hci_remove(phy->hdev);
+
+ if (phy->powered)
+ pn544_hci_i2c_disable(phy);
+
+ free_irq(client->irq, phy);
+
+ /* No platform data, GPIOs have been requested by this driver */
+ if (!pdata) {
+ gpio_free(phy->gpio_en);
+ gpio_free(phy->gpio_fw);
+ /* Using platform data */
+ } else if (pdata->free_resources) {
+ pdata->free_resources();
+ }
+
+ return 0;
+}
+
+static const struct of_device_id of_pn544_i2c_match[] = {
+ { .compatible = "nxp,pn544-i2c", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, of_pn544_i2c_match);
+
+static struct i2c_driver pn544_hci_i2c_driver = {
+ .driver = {
+ .name = PN544_HCI_I2C_DRIVER_NAME,
+ .of_match_table = of_match_ptr(of_pn544_i2c_match),
+ .acpi_match_table = ACPI_PTR(pn544_hci_i2c_acpi_match),
+ },
+ .probe = pn544_hci_i2c_probe,
+ .id_table = pn544_hci_i2c_id_table,
+ .remove = pn544_hci_i2c_remove,
+};
+
+module_i2c_driver(pn544_hci_i2c_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/nfc/pn544/mei.c b/drivers/nfc/pn544/mei.c
new file mode 100644
index 0000000..e4e0350
--- /dev/null
+++ b/drivers/nfc/pn544/mei.c
@@ -0,0 +1,94 @@
+/*
+ * HCI based Driver for NXP pn544 NFC Chip
+ *
+ * Copyright (C) 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/nfc.h>
+#include <net/nfc/hci.h>
+#include <net/nfc/llc.h>
+
+#include "../mei_phy.h"
+#include "pn544.h"
+
+#define PN544_DRIVER_NAME "pn544"
+
+static int pn544_mei_probe(struct mei_cl_device *cldev,
+ const struct mei_cl_device_id *id)
+{
+ struct nfc_mei_phy *phy;
+ int r;
+
+ pr_info("Probing NFC pn544\n");
+
+ phy = nfc_mei_phy_alloc(cldev);
+ if (!phy) {
+ pr_err("Cannot allocate memory for pn544 mei phy.\n");
+ return -ENOMEM;
+ }
+
+ r = pn544_hci_probe(phy, &mei_phy_ops, LLC_NOP_NAME,
+ MEI_NFC_HEADER_SIZE, 0, MEI_NFC_MAX_HCI_PAYLOAD,
+ NULL, &phy->hdev);
+ if (r < 0) {
+ nfc_mei_phy_free(phy);
+
+ return r;
+ }
+
+ return 0;
+}
+
+static int pn544_mei_remove(struct mei_cl_device *cldev)
+{
+ struct nfc_mei_phy *phy = mei_cldev_get_drvdata(cldev);
+
+ pr_info("Removing pn544\n");
+
+ pn544_hci_remove(phy->hdev);
+
+ nfc_mei_phy_free(phy);
+
+ return 0;
+}
+
+static struct mei_cl_device_id pn544_mei_tbl[] = {
+#if LINUX_VERSION_IS_GEQ(4,4,0)
+ { PN544_DRIVER_NAME, MEI_NFC_UUID, MEI_CL_VERSION_ANY},
+#elif LINUX_VERSION_IS_GEQ(4,2,0)
+ { PN544_DRIVER_NAME, MEI_NFC_UUID},
+#else
+ { PN544_DRIVER_NAME},
+#endif
+
+ /* required last entry */
+ { }
+};
+MODULE_DEVICE_TABLE(mei, pn544_mei_tbl);
+
+static struct mei_cl_driver pn544_driver = {
+ .id_table = pn544_mei_tbl,
+ .name = PN544_DRIVER_NAME,
+
+ .probe = pn544_mei_probe,
+ .remove = pn544_mei_remove,
+};
+
+module_mei_cl_driver(pn544_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/nfc/pn544/pn544.c b/drivers/nfc/pn544/pn544.c
new file mode 100644
index 0000000..12e819d
--- /dev/null
+++ b/drivers/nfc/pn544/pn544.c
@@ -0,0 +1,995 @@
+/*
+ * HCI based Driver for NXP PN544 NFC Chip
+ *
+ * Copyright (C) 2012 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+
+#include <linux/nfc.h>
+#include <net/nfc/hci.h>
+#include <net/nfc/llc.h>
+
+#include "pn544.h"
+
+/* Timing restrictions (ms) */
+#define PN544_HCI_RESETVEN_TIME 30
+
+enum pn544_state {
+ PN544_ST_COLD,
+ PN544_ST_FW_READY,
+ PN544_ST_READY,
+};
+
+#define FULL_VERSION_LEN 11
+
+/* Proprietary commands */
+#define PN544_WRITE 0x3f
+#define PN544_TEST_SWP 0x21
+
+/* Proprietary gates, events, commands and registers */
+
+/* NFC_HCI_RF_READER_A_GATE additional registers and commands */
+#define PN544_RF_READER_A_AUTO_ACTIVATION 0x10
+#define PN544_RF_READER_A_CMD_CONTINUE_ACTIVATION 0x12
+#define PN544_MIFARE_CMD 0x21
+
+/* Commands that apply to all RF readers */
+#define PN544_RF_READER_CMD_PRESENCE_CHECK 0x30
+#define PN544_RF_READER_CMD_ACTIVATE_NEXT 0x32
+
+/* NFC_HCI_ID_MGMT_GATE additional registers */
+#define PN544_ID_MGMT_FULL_VERSION_SW 0x10
+
+#define PN544_RF_READER_ISO15693_GATE 0x12
+
+#define PN544_RF_READER_F_GATE 0x14
+#define PN544_FELICA_ID 0x04
+#define PN544_FELICA_RAW 0x20
+
+#define PN544_RF_READER_JEWEL_GATE 0x15
+#define PN544_JEWEL_RAW_CMD 0x23
+
+#define PN544_RF_READER_NFCIP1_INITIATOR_GATE 0x30
+#define PN544_RF_READER_NFCIP1_TARGET_GATE 0x31
+
+#define PN544_SYS_MGMT_GATE 0x90
+#define PN544_SYS_MGMT_INFO_NOTIFICATION 0x02
+
+#define PN544_POLLING_LOOP_MGMT_GATE 0x94
+#define PN544_DEP_MODE 0x01
+#define PN544_DEP_ATR_REQ 0x02
+#define PN544_DEP_ATR_RES 0x03
+#define PN544_DEP_MERGE 0x0D
+#define PN544_PL_RDPHASES 0x06
+#define PN544_PL_EMULATION 0x07
+#define PN544_PL_NFCT_DEACTIVATED 0x09
+
+#define PN544_SWP_MGMT_GATE 0xA0
+#define PN544_SWP_DEFAULT_MODE 0x01
+
+#define PN544_NFC_WI_MGMT_GATE 0xA1
+#define PN544_NFC_ESE_DEFAULT_MODE 0x01
+
+#define PN544_HCI_EVT_SND_DATA 0x01
+#define PN544_HCI_EVT_ACTIVATED 0x02
+#define PN544_HCI_EVT_DEACTIVATED 0x03
+#define PN544_HCI_EVT_RCV_DATA 0x04
+#define PN544_HCI_EVT_CONTINUE_MI 0x05
+#define PN544_HCI_EVT_SWITCH_MODE 0x03
+
+#define PN544_HCI_CMD_ATTREQUEST 0x12
+#define PN544_HCI_CMD_CONTINUE_ACTIVATION 0x13
+
+static struct nfc_hci_gate pn544_gates[] = {
+ {NFC_HCI_ADMIN_GATE, NFC_HCI_INVALID_PIPE},
+ {NFC_HCI_LOOPBACK_GATE, NFC_HCI_INVALID_PIPE},
+ {NFC_HCI_ID_MGMT_GATE, NFC_HCI_INVALID_PIPE},
+ {NFC_HCI_LINK_MGMT_GATE, NFC_HCI_INVALID_PIPE},
+ {NFC_HCI_RF_READER_B_GATE, NFC_HCI_INVALID_PIPE},
+ {NFC_HCI_RF_READER_A_GATE, NFC_HCI_INVALID_PIPE},
+ {PN544_SYS_MGMT_GATE, NFC_HCI_INVALID_PIPE},
+ {PN544_SWP_MGMT_GATE, NFC_HCI_INVALID_PIPE},
+ {PN544_POLLING_LOOP_MGMT_GATE, NFC_HCI_INVALID_PIPE},
+ {PN544_NFC_WI_MGMT_GATE, NFC_HCI_INVALID_PIPE},
+ {PN544_RF_READER_F_GATE, NFC_HCI_INVALID_PIPE},
+ {PN544_RF_READER_JEWEL_GATE, NFC_HCI_INVALID_PIPE},
+ {PN544_RF_READER_ISO15693_GATE, NFC_HCI_INVALID_PIPE},
+ {PN544_RF_READER_NFCIP1_INITIATOR_GATE, NFC_HCI_INVALID_PIPE},
+ {PN544_RF_READER_NFCIP1_TARGET_GATE, NFC_HCI_INVALID_PIPE}
+};
+
+/* Largest headroom needed for outgoing custom commands */
+#define PN544_CMDS_HEADROOM 2
+
+struct pn544_hci_info {
+ struct nfc_phy_ops *phy_ops;
+ void *phy_id;
+
+ struct nfc_hci_dev *hdev;
+
+ enum pn544_state state;
+
+ struct mutex info_lock;
+
+ int async_cb_type;
+ data_exchange_cb_t async_cb;
+ void *async_cb_context;
+
+ fw_download_t fw_download;
+};
+
+static int pn544_hci_open(struct nfc_hci_dev *hdev)
+{
+ struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev);
+ int r = 0;
+
+ mutex_lock(&info->info_lock);
+
+ if (info->state != PN544_ST_COLD) {
+ r = -EBUSY;
+ goto out;
+ }
+
+ r = info->phy_ops->enable(info->phy_id);
+
+ if (r == 0)
+ info->state = PN544_ST_READY;
+
+out:
+ mutex_unlock(&info->info_lock);
+ return r;
+}
+
+static void pn544_hci_close(struct nfc_hci_dev *hdev)
+{
+ struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+ mutex_lock(&info->info_lock);
+
+ if (info->state == PN544_ST_COLD)
+ goto out;
+
+ info->phy_ops->disable(info->phy_id);
+
+ info->state = PN544_ST_COLD;
+
+out:
+ mutex_unlock(&info->info_lock);
+}
+
+static int pn544_hci_ready(struct nfc_hci_dev *hdev)
+{
+ struct sk_buff *skb;
+ static struct hw_config {
+ u8 adr[2];
+ u8 value;
+ } hw_config[] = {
+ {{0x9f, 0x9a}, 0x00},
+
+ {{0x98, 0x10}, 0xbc},
+
+ {{0x9e, 0x71}, 0x00},
+
+ {{0x98, 0x09}, 0x00},
+
+ {{0x9e, 0xb4}, 0x00},
+
+ {{0x9c, 0x01}, 0x08},
+
+ {{0x9e, 0xaa}, 0x01},
+
+ {{0x9b, 0xd1}, 0x17},
+ {{0x9b, 0xd2}, 0x58},
+ {{0x9b, 0xd3}, 0x10},
+ {{0x9b, 0xd4}, 0x47},
+ {{0x9b, 0xd5}, 0x0c},
+ {{0x9b, 0xd6}, 0x37},
+ {{0x9b, 0xdd}, 0x33},
+
+ {{0x9b, 0x84}, 0x00},
+ {{0x99, 0x81}, 0x79},
+ {{0x99, 0x31}, 0x79},
+
+ {{0x98, 0x00}, 0x3f},
+
+ {{0x9f, 0x09}, 0x02},
+
+ {{0x9f, 0x0a}, 0x05},
+
+ {{0x9e, 0xd1}, 0xa1},
+ {{0x99, 0x23}, 0x01},
+
+ {{0x9e, 0x74}, 0x00},
+ {{0x9e, 0x90}, 0x00},
+ {{0x9f, 0x28}, 0x10},
+
+ {{0x9f, 0x35}, 0x04},
+
+ {{0x9f, 0x36}, 0x11},
+
+ {{0x9c, 0x31}, 0x00},
+
+ {{0x9c, 0x32}, 0x00},
+
+ {{0x9c, 0x19}, 0x0a},
+
+ {{0x9c, 0x1a}, 0x0a},
+
+ {{0x9c, 0x0c}, 0x00},
+
+ {{0x9c, 0x0d}, 0x00},
+
+ {{0x9c, 0x12}, 0x00},
+
+ {{0x9c, 0x13}, 0x00},
+
+ {{0x98, 0xa2}, 0x09},
+
+ {{0x98, 0x93}, 0x00},
+
+ {{0x98, 0x7d}, 0x08},
+ {{0x98, 0x7e}, 0x00},
+ {{0x9f, 0xc8}, 0x00},
+ };
+ struct hw_config *p = hw_config;
+ int count = ARRAY_SIZE(hw_config);
+ struct sk_buff *res_skb;
+ u8 param[4];
+ int r;
+
+ param[0] = 0;
+ while (count--) {
+ param[1] = p->adr[0];
+ param[2] = p->adr[1];
+ param[3] = p->value;
+
+ r = nfc_hci_send_cmd(hdev, PN544_SYS_MGMT_GATE, PN544_WRITE,
+ param, 4, &res_skb);
+ if (r < 0)
+ return r;
+
+ if (res_skb->len != 1) {
+ kfree_skb(res_skb);
+ return -EPROTO;
+ }
+
+ if (res_skb->data[0] != p->value) {
+ kfree_skb(res_skb);
+ return -EIO;
+ }
+
+ kfree_skb(res_skb);
+
+ p++;
+ }
+
+ param[0] = NFC_HCI_UICC_HOST_ID;
+ r = nfc_hci_set_param(hdev, NFC_HCI_ADMIN_GATE,
+ NFC_HCI_ADMIN_WHITELIST, param, 1);
+ if (r < 0)
+ return r;
+
+ param[0] = 0x3d;
+ r = nfc_hci_set_param(hdev, PN544_SYS_MGMT_GATE,
+ PN544_SYS_MGMT_INFO_NOTIFICATION, param, 1);
+ if (r < 0)
+ return r;
+
+ param[0] = 0x0;
+ r = nfc_hci_set_param(hdev, NFC_HCI_RF_READER_A_GATE,
+ PN544_RF_READER_A_AUTO_ACTIVATION, param, 1);
+ if (r < 0)
+ return r;
+
+ r = nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE,
+ NFC_HCI_EVT_END_OPERATION, NULL, 0);
+ if (r < 0)
+ return r;
+
+ param[0] = 0x1;
+ r = nfc_hci_set_param(hdev, PN544_POLLING_LOOP_MGMT_GATE,
+ PN544_PL_NFCT_DEACTIVATED, param, 1);
+ if (r < 0)
+ return r;
+
+ param[0] = 0x0;
+ r = nfc_hci_set_param(hdev, PN544_POLLING_LOOP_MGMT_GATE,
+ PN544_PL_RDPHASES, param, 1);
+ if (r < 0)
+ return r;
+
+ r = nfc_hci_get_param(hdev, NFC_HCI_ID_MGMT_GATE,
+ PN544_ID_MGMT_FULL_VERSION_SW, &skb);
+ if (r < 0)
+ return r;
+
+ if (skb->len != FULL_VERSION_LEN) {
+ kfree_skb(skb);
+ return -EINVAL;
+ }
+
+ print_hex_dump(KERN_DEBUG, "FULL VERSION SOFTWARE INFO: ",
+ DUMP_PREFIX_NONE, 16, 1,
+ skb->data, FULL_VERSION_LEN, false);
+
+ kfree_skb(skb);
+
+ return 0;
+}
+
+static int pn544_hci_xmit(struct nfc_hci_dev *hdev, struct sk_buff *skb)
+{
+ struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+ return info->phy_ops->write(info->phy_id, skb);
+}
+
+static int pn544_hci_start_poll(struct nfc_hci_dev *hdev,
+ u32 im_protocols, u32 tm_protocols)
+{
+ u8 phases = 0;
+ int r;
+ u8 duration[2];
+ u8 activated;
+ u8 i_mode = 0x3f; /* Enable all supported modes */
+ u8 t_mode = 0x0f;
+ u8 t_merge = 0x01; /* Enable merge by default */
+
+ pr_info(DRIVER_DESC ": %s protocols 0x%x 0x%x\n",
+ __func__, im_protocols, tm_protocols);
+
+ r = nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE,
+ NFC_HCI_EVT_END_OPERATION, NULL, 0);
+ if (r < 0)
+ return r;
+
+ duration[0] = 0x18;
+ duration[1] = 0x6a;
+ r = nfc_hci_set_param(hdev, PN544_POLLING_LOOP_MGMT_GATE,
+ PN544_PL_EMULATION, duration, 2);
+ if (r < 0)
+ return r;
+
+ activated = 0;
+ r = nfc_hci_set_param(hdev, PN544_POLLING_LOOP_MGMT_GATE,
+ PN544_PL_NFCT_DEACTIVATED, &activated, 1);
+ if (r < 0)
+ return r;
+
+ if (im_protocols & (NFC_PROTO_ISO14443_MASK | NFC_PROTO_MIFARE_MASK |
+ NFC_PROTO_JEWEL_MASK))
+ phases |= 1; /* Type A */
+ if (im_protocols & NFC_PROTO_FELICA_MASK) {
+ phases |= (1 << 2); /* Type F 212 */
+ phases |= (1 << 3); /* Type F 424 */
+ }
+
+ phases |= (1 << 5); /* NFC active */
+
+ r = nfc_hci_set_param(hdev, PN544_POLLING_LOOP_MGMT_GATE,
+ PN544_PL_RDPHASES, &phases, 1);
+ if (r < 0)
+ return r;
+
+ if ((im_protocols | tm_protocols) & NFC_PROTO_NFC_DEP_MASK) {
+ hdev->gb = nfc_get_local_general_bytes(hdev->ndev,
+ &hdev->gb_len);
+ pr_debug("generate local bytes %p\n", hdev->gb);
+ if (hdev->gb == NULL || hdev->gb_len == 0) {
+ im_protocols &= ~NFC_PROTO_NFC_DEP_MASK;
+ tm_protocols &= ~NFC_PROTO_NFC_DEP_MASK;
+ }
+ }
+
+ if (im_protocols & NFC_PROTO_NFC_DEP_MASK) {
+ r = nfc_hci_send_event(hdev,
+ PN544_RF_READER_NFCIP1_INITIATOR_GATE,
+ NFC_HCI_EVT_END_OPERATION, NULL, 0);
+ if (r < 0)
+ return r;
+
+ r = nfc_hci_set_param(hdev,
+ PN544_RF_READER_NFCIP1_INITIATOR_GATE,
+ PN544_DEP_MODE, &i_mode, 1);
+ if (r < 0)
+ return r;
+
+ r = nfc_hci_set_param(hdev,
+ PN544_RF_READER_NFCIP1_INITIATOR_GATE,
+ PN544_DEP_ATR_REQ, hdev->gb, hdev->gb_len);
+ if (r < 0)
+ return r;
+
+ r = nfc_hci_send_event(hdev,
+ PN544_RF_READER_NFCIP1_INITIATOR_GATE,
+ NFC_HCI_EVT_READER_REQUESTED, NULL, 0);
+ if (r < 0)
+ nfc_hci_send_event(hdev,
+ PN544_RF_READER_NFCIP1_INITIATOR_GATE,
+ NFC_HCI_EVT_END_OPERATION, NULL, 0);
+ }
+
+ if (tm_protocols & NFC_PROTO_NFC_DEP_MASK) {
+ r = nfc_hci_set_param(hdev, PN544_RF_READER_NFCIP1_TARGET_GATE,
+ PN544_DEP_MODE, &t_mode, 1);
+ if (r < 0)
+ return r;
+
+ r = nfc_hci_set_param(hdev, PN544_RF_READER_NFCIP1_TARGET_GATE,
+ PN544_DEP_ATR_RES, hdev->gb, hdev->gb_len);
+ if (r < 0)
+ return r;
+
+ r = nfc_hci_set_param(hdev, PN544_RF_READER_NFCIP1_TARGET_GATE,
+ PN544_DEP_MERGE, &t_merge, 1);
+ if (r < 0)
+ return r;
+ }
+
+ r = nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE,
+ NFC_HCI_EVT_READER_REQUESTED, NULL, 0);
+ if (r < 0)
+ nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE,
+ NFC_HCI_EVT_END_OPERATION, NULL, 0);
+
+ return r;
+}
+
+static int pn544_hci_dep_link_up(struct nfc_hci_dev *hdev,
+ struct nfc_target *target, u8 comm_mode,
+ u8 *gb, size_t gb_len)
+{
+ struct sk_buff *rgb_skb = NULL;
+ int r;
+
+ r = nfc_hci_get_param(hdev, target->hci_reader_gate,
+ PN544_DEP_ATR_RES, &rgb_skb);
+ if (r < 0)
+ return r;
+
+ if (rgb_skb->len == 0 || rgb_skb->len > NFC_GB_MAXSIZE) {
+ r = -EPROTO;
+ goto exit;
+ }
+ print_hex_dump(KERN_DEBUG, "remote gb: ", DUMP_PREFIX_OFFSET,
+ 16, 1, rgb_skb->data, rgb_skb->len, true);
+
+ r = nfc_set_remote_general_bytes(hdev->ndev, rgb_skb->data,
+ rgb_skb->len);
+
+ if (r == 0)
+ r = nfc_dep_link_is_up(hdev->ndev, target->idx, comm_mode,
+ NFC_RF_INITIATOR);
+exit:
+ kfree_skb(rgb_skb);
+ return r;
+}
+
+static int pn544_hci_dep_link_down(struct nfc_hci_dev *hdev)
+{
+
+ return nfc_hci_send_event(hdev, PN544_RF_READER_NFCIP1_INITIATOR_GATE,
+ NFC_HCI_EVT_END_OPERATION, NULL, 0);
+}
+
+static int pn544_hci_target_from_gate(struct nfc_hci_dev *hdev, u8 gate,
+ struct nfc_target *target)
+{
+ switch (gate) {
+ case PN544_RF_READER_F_GATE:
+ target->supported_protocols = NFC_PROTO_FELICA_MASK;
+ break;
+ case PN544_RF_READER_JEWEL_GATE:
+ target->supported_protocols = NFC_PROTO_JEWEL_MASK;
+ target->sens_res = 0x0c00;
+ break;
+ case PN544_RF_READER_NFCIP1_INITIATOR_GATE:
+ target->supported_protocols = NFC_PROTO_NFC_DEP_MASK;
+ break;
+ default:
+ return -EPROTO;
+ }
+
+ return 0;
+}
+
+static int pn544_hci_complete_target_discovered(struct nfc_hci_dev *hdev,
+ u8 gate,
+ struct nfc_target *target)
+{
+ struct sk_buff *uid_skb;
+ int r = 0;
+
+ if (gate == PN544_RF_READER_NFCIP1_INITIATOR_GATE)
+ return r;
+
+ if (target->supported_protocols & NFC_PROTO_NFC_DEP_MASK) {
+ r = nfc_hci_send_cmd(hdev,
+ PN544_RF_READER_NFCIP1_INITIATOR_GATE,
+ PN544_HCI_CMD_CONTINUE_ACTIVATION, NULL, 0, NULL);
+ if (r < 0)
+ return r;
+
+ target->hci_reader_gate = PN544_RF_READER_NFCIP1_INITIATOR_GATE;
+ } else if (target->supported_protocols & NFC_PROTO_MIFARE_MASK) {
+ if (target->nfcid1_len != 4 && target->nfcid1_len != 7 &&
+ target->nfcid1_len != 10)
+ return -EPROTO;
+
+ r = nfc_hci_send_cmd(hdev, NFC_HCI_RF_READER_A_GATE,
+ PN544_RF_READER_CMD_ACTIVATE_NEXT,
+ target->nfcid1, target->nfcid1_len, NULL);
+ } else if (target->supported_protocols & NFC_PROTO_FELICA_MASK) {
+ r = nfc_hci_get_param(hdev, PN544_RF_READER_F_GATE,
+ PN544_FELICA_ID, &uid_skb);
+ if (r < 0)
+ return r;
+
+ if (uid_skb->len != 8) {
+ kfree_skb(uid_skb);
+ return -EPROTO;
+ }
+
+ /* Type F NFC-DEP IDm has prefix 0x01FE */
+ if ((uid_skb->data[0] == 0x01) && (uid_skb->data[1] == 0xfe)) {
+ kfree_skb(uid_skb);
+ r = nfc_hci_send_cmd(hdev,
+ PN544_RF_READER_NFCIP1_INITIATOR_GATE,
+ PN544_HCI_CMD_CONTINUE_ACTIVATION,
+ NULL, 0, NULL);
+ if (r < 0)
+ return r;
+
+ target->supported_protocols = NFC_PROTO_NFC_DEP_MASK;
+ target->hci_reader_gate =
+ PN544_RF_READER_NFCIP1_INITIATOR_GATE;
+ } else {
+ r = nfc_hci_send_cmd(hdev, PN544_RF_READER_F_GATE,
+ PN544_RF_READER_CMD_ACTIVATE_NEXT,
+ uid_skb->data, uid_skb->len, NULL);
+ kfree_skb(uid_skb);
+ }
+ } else if (target->supported_protocols & NFC_PROTO_ISO14443_MASK) {
+ /*
+ * TODO: maybe other ISO 14443 require some kind of continue
+ * activation, but for now we've seen only this one below.
+ */
+ if (target->sens_res == 0x4403) /* Type 4 Mifare DESFire */
+ r = nfc_hci_send_cmd(hdev, NFC_HCI_RF_READER_A_GATE,
+ PN544_RF_READER_A_CMD_CONTINUE_ACTIVATION,
+ NULL, 0, NULL);
+ }
+
+ return r;
+}
+
+#define PN544_CB_TYPE_READER_F 1
+
+static void pn544_hci_data_exchange_cb(void *context, struct sk_buff *skb,
+ int err)
+{
+ struct pn544_hci_info *info = context;
+
+ switch (info->async_cb_type) {
+ case PN544_CB_TYPE_READER_F:
+ if (err == 0)
+ skb_pull(skb, 1);
+ info->async_cb(info->async_cb_context, skb, err);
+ break;
+ default:
+ if (err == 0)
+ kfree_skb(skb);
+ break;
+ }
+}
+
+#define MIFARE_CMD_AUTH_KEY_A 0x60
+#define MIFARE_CMD_AUTH_KEY_B 0x61
+#define MIFARE_CMD_HEADER 2
+#define MIFARE_UID_LEN 4
+#define MIFARE_KEY_LEN 6
+#define MIFARE_CMD_LEN 12
+/*
+ * Returns:
+ * <= 0: driver handled the data exchange
+ * 1: driver doesn't especially handle, please do standard processing
+ */
+static int pn544_hci_im_transceive(struct nfc_hci_dev *hdev,
+ struct nfc_target *target,
+ struct sk_buff *skb, data_exchange_cb_t cb,
+ void *cb_context)
+{
+ struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+ pr_info(DRIVER_DESC ": %s for gate=%d\n", __func__,
+ target->hci_reader_gate);
+
+ switch (target->hci_reader_gate) {
+ case NFC_HCI_RF_READER_A_GATE:
+ if (target->supported_protocols & NFC_PROTO_MIFARE_MASK) {
+ /*
+ * It seems that pn544 is inverting key and UID for
+ * MIFARE authentication commands.
+ */
+ if (skb->len == MIFARE_CMD_LEN &&
+ (skb->data[0] == MIFARE_CMD_AUTH_KEY_A ||
+ skb->data[0] == MIFARE_CMD_AUTH_KEY_B)) {
+ u8 uid[MIFARE_UID_LEN];
+ u8 *data = skb->data + MIFARE_CMD_HEADER;
+
+ memcpy(uid, data + MIFARE_KEY_LEN,
+ MIFARE_UID_LEN);
+ memmove(data + MIFARE_UID_LEN, data,
+ MIFARE_KEY_LEN);
+ memcpy(data, uid, MIFARE_UID_LEN);
+ }
+
+ return nfc_hci_send_cmd_async(hdev,
+ target->hci_reader_gate,
+ PN544_MIFARE_CMD,
+ skb->data, skb->len,
+ cb, cb_context);
+ } else
+ return 1;
+ case PN544_RF_READER_F_GATE:
+ *skb_push(skb, 1) = 0;
+ *skb_push(skb, 1) = 0;
+
+ info->async_cb_type = PN544_CB_TYPE_READER_F;
+ info->async_cb = cb;
+ info->async_cb_context = cb_context;
+
+ return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate,
+ PN544_FELICA_RAW, skb->data,
+ skb->len,
+ pn544_hci_data_exchange_cb, info);
+ case PN544_RF_READER_JEWEL_GATE:
+ return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate,
+ PN544_JEWEL_RAW_CMD, skb->data,
+ skb->len, cb, cb_context);
+ case PN544_RF_READER_NFCIP1_INITIATOR_GATE:
+ *skb_push(skb, 1) = 0;
+
+ return nfc_hci_send_event(hdev, target->hci_reader_gate,
+ PN544_HCI_EVT_SND_DATA, skb->data,
+ skb->len);
+ default:
+ return 1;
+ }
+}
+
+static int pn544_hci_tm_send(struct nfc_hci_dev *hdev, struct sk_buff *skb)
+{
+ int r;
+
+ /* Set default false for multiple information chaining */
+ *skb_push(skb, 1) = 0;
+
+ r = nfc_hci_send_event(hdev, PN544_RF_READER_NFCIP1_TARGET_GATE,
+ PN544_HCI_EVT_SND_DATA, skb->data, skb->len);
+
+ kfree_skb(skb);
+
+ return r;
+}
+
+static int pn544_hci_check_presence(struct nfc_hci_dev *hdev,
+ struct nfc_target *target)
+{
+ pr_debug("supported protocol %d\b", target->supported_protocols);
+ if (target->supported_protocols & (NFC_PROTO_ISO14443_MASK |
+ NFC_PROTO_ISO14443_B_MASK)) {
+ return nfc_hci_send_cmd(hdev, target->hci_reader_gate,
+ PN544_RF_READER_CMD_PRESENCE_CHECK,
+ NULL, 0, NULL);
+ } else if (target->supported_protocols & NFC_PROTO_MIFARE_MASK) {
+ if (target->nfcid1_len != 4 && target->nfcid1_len != 7 &&
+ target->nfcid1_len != 10)
+ return -EOPNOTSUPP;
+
+ return nfc_hci_send_cmd(hdev, NFC_HCI_RF_READER_A_GATE,
+ PN544_RF_READER_CMD_ACTIVATE_NEXT,
+ target->nfcid1, target->nfcid1_len, NULL);
+ } else if (target->supported_protocols & (NFC_PROTO_JEWEL_MASK |
+ NFC_PROTO_FELICA_MASK)) {
+ return -EOPNOTSUPP;
+ } else if (target->supported_protocols & NFC_PROTO_NFC_DEP_MASK) {
+ return nfc_hci_send_cmd(hdev, target->hci_reader_gate,
+ PN544_HCI_CMD_ATTREQUEST,
+ NULL, 0, NULL);
+ }
+
+ return 0;
+}
+
+/*
+ * Returns:
+ * <= 0: driver handled the event, skb consumed
+ * 1: driver does not handle the event, please do standard processing
+ */
+static int pn544_hci_event_received(struct nfc_hci_dev *hdev, u8 pipe, u8 event,
+ struct sk_buff *skb)
+{
+ struct sk_buff *rgb_skb = NULL;
+ u8 gate = hdev->pipes[pipe].gate;
+ int r;
+
+ pr_debug("hci event %d\n", event);
+ switch (event) {
+ case PN544_HCI_EVT_ACTIVATED:
+ if (gate == PN544_RF_READER_NFCIP1_INITIATOR_GATE) {
+ r = nfc_hci_target_discovered(hdev, gate);
+ } else if (gate == PN544_RF_READER_NFCIP1_TARGET_GATE) {
+ r = nfc_hci_get_param(hdev, gate, PN544_DEP_ATR_REQ,
+ &rgb_skb);
+ if (r < 0)
+ goto exit;
+
+ r = nfc_tm_activated(hdev->ndev, NFC_PROTO_NFC_DEP_MASK,
+ NFC_COMM_PASSIVE, rgb_skb->data,
+ rgb_skb->len);
+
+ kfree_skb(rgb_skb);
+ } else {
+ r = -EINVAL;
+ }
+ break;
+ case PN544_HCI_EVT_DEACTIVATED:
+ r = nfc_hci_send_event(hdev, gate, NFC_HCI_EVT_END_OPERATION,
+ NULL, 0);
+ break;
+ case PN544_HCI_EVT_RCV_DATA:
+ if (skb->len < 2) {
+ r = -EPROTO;
+ goto exit;
+ }
+
+ if (skb->data[0] != 0) {
+ pr_debug("data0 %d\n", skb->data[0]);
+ r = -EPROTO;
+ goto exit;
+ }
+
+ skb_pull(skb, 2);
+ return nfc_tm_data_received(hdev->ndev, skb);
+ default:
+ return 1;
+ }
+
+exit:
+ kfree_skb(skb);
+
+ return r;
+}
+
+static int pn544_hci_fw_download(struct nfc_hci_dev *hdev,
+ const char *firmware_name)
+{
+ struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+ if (info->fw_download == NULL)
+ return -ENOTSUPP;
+
+ return info->fw_download(info->phy_id, firmware_name, hdev->sw_romlib);
+}
+
+static int pn544_hci_discover_se(struct nfc_hci_dev *hdev)
+{
+ u32 se_idx = 0;
+ u8 ese_mode = 0x01; /* Default mode */
+ struct sk_buff *res_skb;
+ int r;
+
+ r = nfc_hci_send_cmd(hdev, PN544_SYS_MGMT_GATE, PN544_TEST_SWP,
+ NULL, 0, &res_skb);
+
+ if (r == 0) {
+ if (res_skb->len == 2 && res_skb->data[0] == 0x00)
+ nfc_add_se(hdev->ndev, se_idx++, NFC_SE_UICC);
+
+ kfree_skb(res_skb);
+ }
+
+ r = nfc_hci_send_event(hdev, PN544_NFC_WI_MGMT_GATE,
+ PN544_HCI_EVT_SWITCH_MODE,
+ &ese_mode, 1);
+ if (r == 0)
+ nfc_add_se(hdev->ndev, se_idx++, NFC_SE_EMBEDDED);
+
+ return !se_idx;
+}
+
+#define PN544_SE_MODE_OFF 0x00
+#define PN544_SE_MODE_ON 0x01
+static int pn544_hci_enable_se(struct nfc_hci_dev *hdev, u32 se_idx)
+{
+ struct nfc_se *se;
+ u8 enable = PN544_SE_MODE_ON;
+ static struct uicc_gatelist {
+ u8 head;
+ u8 adr[2];
+ u8 value;
+ } uicc_gatelist[] = {
+ {0x00, {0x9e, 0xd9}, 0x23},
+ {0x00, {0x9e, 0xda}, 0x21},
+ {0x00, {0x9e, 0xdb}, 0x22},
+ {0x00, {0x9e, 0xdc}, 0x24},
+ };
+ struct uicc_gatelist *p = uicc_gatelist;
+ int count = ARRAY_SIZE(uicc_gatelist);
+ struct sk_buff *res_skb;
+ int r;
+
+ se = nfc_find_se(hdev->ndev, se_idx);
+
+ switch (se->type) {
+ case NFC_SE_UICC:
+ while (count--) {
+ r = nfc_hci_send_cmd(hdev, PN544_SYS_MGMT_GATE,
+ PN544_WRITE, (u8 *)p, 4, &res_skb);
+ if (r < 0)
+ return r;
+
+ if (res_skb->len != 1) {
+ kfree_skb(res_skb);
+ return -EPROTO;
+ }
+
+ if (res_skb->data[0] != p->value) {
+ kfree_skb(res_skb);
+ return -EIO;
+ }
+
+ kfree_skb(res_skb);
+
+ p++;
+ }
+
+ return nfc_hci_set_param(hdev, PN544_SWP_MGMT_GATE,
+ PN544_SWP_DEFAULT_MODE, &enable, 1);
+ case NFC_SE_EMBEDDED:
+ return nfc_hci_set_param(hdev, PN544_NFC_WI_MGMT_GATE,
+ PN544_NFC_ESE_DEFAULT_MODE, &enable, 1);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int pn544_hci_disable_se(struct nfc_hci_dev *hdev, u32 se_idx)
+{
+ struct nfc_se *se;
+ u8 disable = PN544_SE_MODE_OFF;
+
+ se = nfc_find_se(hdev->ndev, se_idx);
+
+ switch (se->type) {
+ case NFC_SE_UICC:
+ return nfc_hci_set_param(hdev, PN544_SWP_MGMT_GATE,
+ PN544_SWP_DEFAULT_MODE, &disable, 1);
+ case NFC_SE_EMBEDDED:
+ return nfc_hci_set_param(hdev, PN544_NFC_WI_MGMT_GATE,
+ PN544_NFC_ESE_DEFAULT_MODE, &disable, 1);
+ default:
+ return -EINVAL;
+ }
+}
+
+static struct nfc_hci_ops pn544_hci_ops = {
+ .open = pn544_hci_open,
+ .close = pn544_hci_close,
+ .hci_ready = pn544_hci_ready,
+ .xmit = pn544_hci_xmit,
+ .start_poll = pn544_hci_start_poll,
+ .dep_link_up = pn544_hci_dep_link_up,
+ .dep_link_down = pn544_hci_dep_link_down,
+ .target_from_gate = pn544_hci_target_from_gate,
+ .complete_target_discovered = pn544_hci_complete_target_discovered,
+ .im_transceive = pn544_hci_im_transceive,
+ .tm_send = pn544_hci_tm_send,
+ .check_presence = pn544_hci_check_presence,
+ .event_received = pn544_hci_event_received,
+ .fw_download = pn544_hci_fw_download,
+ .discover_se = pn544_hci_discover_se,
+ .enable_se = pn544_hci_enable_se,
+ .disable_se = pn544_hci_disable_se,
+};
+
+int pn544_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name,
+ int phy_headroom, int phy_tailroom, int phy_payload,
+ fw_download_t fw_download, struct nfc_hci_dev **hdev)
+{
+ struct pn544_hci_info *info;
+ u32 protocols;
+ struct nfc_hci_init_data init_data;
+ int r;
+
+ info = kzalloc(sizeof(struct pn544_hci_info), GFP_KERNEL);
+ if (!info) {
+ r = -ENOMEM;
+ goto err_info_alloc;
+ }
+
+ info->phy_ops = phy_ops;
+ info->phy_id = phy_id;
+ info->fw_download = fw_download;
+ info->state = PN544_ST_COLD;
+ mutex_init(&info->info_lock);
+
+ init_data.gate_count = ARRAY_SIZE(pn544_gates);
+
+ memcpy(init_data.gates, pn544_gates, sizeof(pn544_gates));
+
+ /*
+ * TODO: Session id must include the driver name + some bus addr
+ * persistent info to discriminate 2 identical chips
+ */
+ strcpy(init_data.session_id, "ID544HCI");
+
+ 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;
+
+ info->hdev = nfc_hci_allocate_device(&pn544_hci_ops, &init_data, 0,
+ protocols, llc_name,
+ phy_headroom + PN544_CMDS_HEADROOM,
+ phy_tailroom, phy_payload);
+ if (!info->hdev) {
+ pr_err("Cannot allocate nfc hdev\n");
+ r = -ENOMEM;
+ goto err_alloc_hdev;
+ }
+
+ nfc_hci_set_clientdata(info->hdev, info);
+
+ r = nfc_hci_register_device(info->hdev);
+ if (r)
+ goto err_regdev;
+
+ *hdev = info->hdev;
+
+ return 0;
+
+err_regdev:
+ nfc_hci_free_device(info->hdev);
+
+err_alloc_hdev:
+ kfree(info);
+
+err_info_alloc:
+ return r;
+}
+EXPORT_SYMBOL(pn544_hci_probe);
+
+void pn544_hci_remove(struct nfc_hci_dev *hdev)
+{
+ struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+ nfc_hci_unregister_device(hdev);
+ nfc_hci_free_device(hdev);
+ kfree(info);
+}
+EXPORT_SYMBOL(pn544_hci_remove);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/nfc/pn544/pn544.h b/drivers/nfc/pn544/pn544.h
new file mode 100644
index 0000000..2aa9233
--- /dev/null
+++ b/drivers/nfc/pn544/pn544.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2011 - 2012 Intel Corporation. All rights reserved.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LOCAL_PN544_H_
+#define __LOCAL_PN544_H_
+
+#include <net/nfc/hci.h>
+
+#define DRIVER_DESC "HCI NFC driver for PN544"
+
+#define PN544_HCI_MODE 0
+#define PN544_FW_MODE 1
+
+typedef int (*fw_download_t)(void *context, const char *firmware_name,
+ u8 hw_variant);
+
+int pn544_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name,
+ int phy_headroom, int phy_tailroom, int phy_payload,
+ fw_download_t fw_download, struct nfc_hci_dev **hdev);
+void pn544_hci_remove(struct nfc_hci_dev *hdev);
+
+#endif /* __LOCAL_PN544_H_ */
diff --git a/drivers/nfc/port100.c b/drivers/nfc/port100.c
new file mode 100644
index 0000000..2b2330b
--- /dev/null
+++ b/drivers/nfc/port100.c
@@ -0,0 +1,1649 @@
+/*
+ * Sony NFC Port-100 Series driver
+ * Copyright (c) 2013, Intel Corporation.
+ *
+ * Partly based/Inspired by Stephen Tiedemann's nfcpy
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/usb.h>
+#include <net/nfc/digital.h>
+
+#define VERSION "0.1"
+
+#define SONY_VENDOR_ID 0x054c
+#define RCS380_PRODUCT_ID 0x06c1
+
+#define PORT100_PROTOCOLS (NFC_PROTO_JEWEL_MASK | \
+ NFC_PROTO_MIFARE_MASK | \
+ NFC_PROTO_FELICA_MASK | \
+ NFC_PROTO_NFC_DEP_MASK | \
+ NFC_PROTO_ISO14443_MASK | \
+ NFC_PROTO_ISO14443_B_MASK)
+
+#define PORT100_CAPABILITIES (NFC_DIGITAL_DRV_CAPS_IN_CRC | \
+ NFC_DIGITAL_DRV_CAPS_TG_CRC)
+
+/* Standard port100 frame definitions */
+#define PORT100_FRAME_HEADER_LEN (sizeof(struct port100_frame) \
+ + 2) /* data[0] CC, data[1] SCC */
+#define PORT100_FRAME_TAIL_LEN 2 /* data[len] DCS, data[len + 1] postamble*/
+
+#define PORT100_COMM_RF_HEAD_MAX_LEN (sizeof(struct port100_tg_comm_rf_cmd))
+
+/*
+ * Max extended frame payload len, excluding CC and SCC
+ * which are already in PORT100_FRAME_HEADER_LEN.
+ */
+#define PORT100_FRAME_MAX_PAYLOAD_LEN 1001
+
+#define PORT100_FRAME_ACK_SIZE 6 /* Preamble (1), SoPC (2), ACK Code (2),
+ Postamble (1) */
+static u8 ack_frame[PORT100_FRAME_ACK_SIZE] = {
+ 0x00, 0x00, 0xff, 0x00, 0xff, 0x00
+};
+
+#define PORT100_FRAME_CHECKSUM(f) (f->data[le16_to_cpu(f->datalen)])
+#define PORT100_FRAME_POSTAMBLE(f) (f->data[le16_to_cpu(f->datalen) + 1])
+
+/* start of frame */
+#define PORT100_FRAME_SOF 0x00FF
+#define PORT100_FRAME_EXT 0xFFFF
+#define PORT100_FRAME_ACK 0x00FF
+
+/* Port-100 command: in or out */
+#define PORT100_FRAME_DIRECTION(f) (f->data[0]) /* CC */
+#define PORT100_FRAME_DIR_OUT 0xD6
+#define PORT100_FRAME_DIR_IN 0xD7
+
+/* Port-100 sub-command */
+#define PORT100_FRAME_CMD(f) (f->data[1]) /* SCC */
+
+#define PORT100_CMD_GET_FIRMWARE_VERSION 0x20
+#define PORT100_CMD_GET_COMMAND_TYPE 0x28
+#define PORT100_CMD_SET_COMMAND_TYPE 0x2A
+
+#define PORT100_CMD_IN_SET_RF 0x00
+#define PORT100_CMD_IN_SET_PROTOCOL 0x02
+#define PORT100_CMD_IN_COMM_RF 0x04
+
+#define PORT100_CMD_TG_SET_RF 0x40
+#define PORT100_CMD_TG_SET_PROTOCOL 0x42
+#define PORT100_CMD_TG_SET_RF_OFF 0x46
+#define PORT100_CMD_TG_COMM_RF 0x48
+
+#define PORT100_CMD_SWITCH_RF 0x06
+
+#define PORT100_CMD_RESPONSE(cmd) (cmd + 1)
+
+#define PORT100_CMD_TYPE_IS_SUPPORTED(mask, cmd_type) \
+ ((mask) & (0x01 << (cmd_type)))
+#define PORT100_CMD_TYPE_0 0
+#define PORT100_CMD_TYPE_1 1
+
+#define PORT100_CMD_STATUS_OK 0x00
+#define PORT100_CMD_STATUS_TIMEOUT 0x80
+
+#define PORT100_MDAA_TGT_HAS_BEEN_ACTIVATED_MASK 0x01
+#define PORT100_MDAA_TGT_WAS_ACTIVATED_MASK 0x02
+
+struct port100;
+
+typedef void (*port100_send_async_complete_t)(struct port100 *dev, void *arg,
+ struct sk_buff *resp);
+
+/**
+ * Setting sets structure for in_set_rf command
+ *
+ * @in_*_set_number: Represent the entry indexes in the port-100 RF Base Table.
+ * This table contains multiple RF setting sets required for RF
+ * communication.
+ *
+ * @in_*_comm_type: Theses fields set the communication type to be used.
+ */
+struct port100_in_rf_setting {
+ u8 in_send_set_number;
+ u8 in_send_comm_type;
+ u8 in_recv_set_number;
+ u8 in_recv_comm_type;
+} __packed;
+
+#define PORT100_COMM_TYPE_IN_212F 0x01
+#define PORT100_COMM_TYPE_IN_424F 0x02
+#define PORT100_COMM_TYPE_IN_106A 0x03
+#define PORT100_COMM_TYPE_IN_106B 0x07
+
+static const struct port100_in_rf_setting in_rf_settings[] = {
+ [NFC_DIGITAL_RF_TECH_212F] = {
+ .in_send_set_number = 1,
+ .in_send_comm_type = PORT100_COMM_TYPE_IN_212F,
+ .in_recv_set_number = 15,
+ .in_recv_comm_type = PORT100_COMM_TYPE_IN_212F,
+ },
+ [NFC_DIGITAL_RF_TECH_424F] = {
+ .in_send_set_number = 1,
+ .in_send_comm_type = PORT100_COMM_TYPE_IN_424F,
+ .in_recv_set_number = 15,
+ .in_recv_comm_type = PORT100_COMM_TYPE_IN_424F,
+ },
+ [NFC_DIGITAL_RF_TECH_106A] = {
+ .in_send_set_number = 2,
+ .in_send_comm_type = PORT100_COMM_TYPE_IN_106A,
+ .in_recv_set_number = 15,
+ .in_recv_comm_type = PORT100_COMM_TYPE_IN_106A,
+ },
+ [NFC_DIGITAL_RF_TECH_106B] = {
+ .in_send_set_number = 3,
+ .in_send_comm_type = PORT100_COMM_TYPE_IN_106B,
+ .in_recv_set_number = 15,
+ .in_recv_comm_type = PORT100_COMM_TYPE_IN_106B,
+ },
+ /* Ensures the array has NFC_DIGITAL_RF_TECH_LAST elements */
+ [NFC_DIGITAL_RF_TECH_LAST] = { 0 },
+};
+
+/**
+ * Setting sets structure for tg_set_rf command
+ *
+ * @tg_set_number: Represents the entry index in the port-100 RF Base Table.
+ * This table contains multiple RF setting sets required for RF
+ * communication. this field is used for both send and receive
+ * settings.
+ *
+ * @tg_comm_type: Sets the communication type to be used to send and receive
+ * data.
+ */
+struct port100_tg_rf_setting {
+ u8 tg_set_number;
+ u8 tg_comm_type;
+} __packed;
+
+#define PORT100_COMM_TYPE_TG_106A 0x0B
+#define PORT100_COMM_TYPE_TG_212F 0x0C
+#define PORT100_COMM_TYPE_TG_424F 0x0D
+
+static const struct port100_tg_rf_setting tg_rf_settings[] = {
+ [NFC_DIGITAL_RF_TECH_106A] = {
+ .tg_set_number = 8,
+ .tg_comm_type = PORT100_COMM_TYPE_TG_106A,
+ },
+ [NFC_DIGITAL_RF_TECH_212F] = {
+ .tg_set_number = 8,
+ .tg_comm_type = PORT100_COMM_TYPE_TG_212F,
+ },
+ [NFC_DIGITAL_RF_TECH_424F] = {
+ .tg_set_number = 8,
+ .tg_comm_type = PORT100_COMM_TYPE_TG_424F,
+ },
+ /* Ensures the array has NFC_DIGITAL_RF_TECH_LAST elements */
+ [NFC_DIGITAL_RF_TECH_LAST] = { 0 },
+
+};
+
+#define PORT100_IN_PROT_INITIAL_GUARD_TIME 0x00
+#define PORT100_IN_PROT_ADD_CRC 0x01
+#define PORT100_IN_PROT_CHECK_CRC 0x02
+#define PORT100_IN_PROT_MULTI_CARD 0x03
+#define PORT100_IN_PROT_ADD_PARITY 0x04
+#define PORT100_IN_PROT_CHECK_PARITY 0x05
+#define PORT100_IN_PROT_BITWISE_AC_RECV_MODE 0x06
+#define PORT100_IN_PROT_VALID_BIT_NUMBER 0x07
+#define PORT100_IN_PROT_CRYPTO1 0x08
+#define PORT100_IN_PROT_ADD_SOF 0x09
+#define PORT100_IN_PROT_CHECK_SOF 0x0A
+#define PORT100_IN_PROT_ADD_EOF 0x0B
+#define PORT100_IN_PROT_CHECK_EOF 0x0C
+#define PORT100_IN_PROT_DEAF_TIME 0x0E
+#define PORT100_IN_PROT_CRM 0x0F
+#define PORT100_IN_PROT_CRM_MIN_LEN 0x10
+#define PORT100_IN_PROT_T1_TAG_FRAME 0x11
+#define PORT100_IN_PROT_RFCA 0x12
+#define PORT100_IN_PROT_GUARD_TIME_AT_INITIATOR 0x13
+#define PORT100_IN_PROT_END 0x14
+
+#define PORT100_IN_MAX_NUM_PROTOCOLS 19
+
+#define PORT100_TG_PROT_TU 0x00
+#define PORT100_TG_PROT_RF_OFF 0x01
+#define PORT100_TG_PROT_CRM 0x02
+#define PORT100_TG_PROT_END 0x03
+
+#define PORT100_TG_MAX_NUM_PROTOCOLS 3
+
+struct port100_protocol {
+ u8 number;
+ u8 value;
+} __packed;
+
+static struct port100_protocol
+in_protocols[][PORT100_IN_MAX_NUM_PROTOCOLS + 1] = {
+ [NFC_DIGITAL_FRAMING_NFCA_SHORT] = {
+ { PORT100_IN_PROT_INITIAL_GUARD_TIME, 6 },
+ { PORT100_IN_PROT_ADD_CRC, 0 },
+ { PORT100_IN_PROT_CHECK_CRC, 0 },
+ { PORT100_IN_PROT_MULTI_CARD, 0 },
+ { PORT100_IN_PROT_ADD_PARITY, 0 },
+ { PORT100_IN_PROT_CHECK_PARITY, 1 },
+ { PORT100_IN_PROT_BITWISE_AC_RECV_MODE, 0 },
+ { PORT100_IN_PROT_VALID_BIT_NUMBER, 7 },
+ { PORT100_IN_PROT_CRYPTO1, 0 },
+ { PORT100_IN_PROT_ADD_SOF, 0 },
+ { PORT100_IN_PROT_CHECK_SOF, 0 },
+ { PORT100_IN_PROT_ADD_EOF, 0 },
+ { PORT100_IN_PROT_CHECK_EOF, 0 },
+ { PORT100_IN_PROT_DEAF_TIME, 4 },
+ { PORT100_IN_PROT_CRM, 0 },
+ { PORT100_IN_PROT_CRM_MIN_LEN, 0 },
+ { PORT100_IN_PROT_T1_TAG_FRAME, 0 },
+ { PORT100_IN_PROT_RFCA, 0 },
+ { PORT100_IN_PROT_GUARD_TIME_AT_INITIATOR, 6 },
+ { PORT100_IN_PROT_END, 0 },
+ },
+ [NFC_DIGITAL_FRAMING_NFCA_STANDARD] = {
+ { PORT100_IN_PROT_INITIAL_GUARD_TIME, 6 },
+ { PORT100_IN_PROT_ADD_CRC, 0 },
+ { PORT100_IN_PROT_CHECK_CRC, 0 },
+ { PORT100_IN_PROT_MULTI_CARD, 0 },
+ { PORT100_IN_PROT_ADD_PARITY, 1 },
+ { PORT100_IN_PROT_CHECK_PARITY, 1 },
+ { PORT100_IN_PROT_BITWISE_AC_RECV_MODE, 0 },
+ { PORT100_IN_PROT_VALID_BIT_NUMBER, 8 },
+ { PORT100_IN_PROT_CRYPTO1, 0 },
+ { PORT100_IN_PROT_ADD_SOF, 0 },
+ { PORT100_IN_PROT_CHECK_SOF, 0 },
+ { PORT100_IN_PROT_ADD_EOF, 0 },
+ { PORT100_IN_PROT_CHECK_EOF, 0 },
+ { PORT100_IN_PROT_DEAF_TIME, 4 },
+ { PORT100_IN_PROT_CRM, 0 },
+ { PORT100_IN_PROT_CRM_MIN_LEN, 0 },
+ { PORT100_IN_PROT_T1_TAG_FRAME, 0 },
+ { PORT100_IN_PROT_RFCA, 0 },
+ { PORT100_IN_PROT_GUARD_TIME_AT_INITIATOR, 6 },
+ { PORT100_IN_PROT_END, 0 },
+ },
+ [NFC_DIGITAL_FRAMING_NFCA_STANDARD_WITH_CRC_A] = {
+ { PORT100_IN_PROT_INITIAL_GUARD_TIME, 6 },
+ { PORT100_IN_PROT_ADD_CRC, 1 },
+ { PORT100_IN_PROT_CHECK_CRC, 1 },
+ { PORT100_IN_PROT_MULTI_CARD, 0 },
+ { PORT100_IN_PROT_ADD_PARITY, 1 },
+ { PORT100_IN_PROT_CHECK_PARITY, 1 },
+ { PORT100_IN_PROT_BITWISE_AC_RECV_MODE, 0 },
+ { PORT100_IN_PROT_VALID_BIT_NUMBER, 8 },
+ { PORT100_IN_PROT_CRYPTO1, 0 },
+ { PORT100_IN_PROT_ADD_SOF, 0 },
+ { PORT100_IN_PROT_CHECK_SOF, 0 },
+ { PORT100_IN_PROT_ADD_EOF, 0 },
+ { PORT100_IN_PROT_CHECK_EOF, 0 },
+ { PORT100_IN_PROT_DEAF_TIME, 4 },
+ { PORT100_IN_PROT_CRM, 0 },
+ { PORT100_IN_PROT_CRM_MIN_LEN, 0 },
+ { PORT100_IN_PROT_T1_TAG_FRAME, 0 },
+ { PORT100_IN_PROT_RFCA, 0 },
+ { PORT100_IN_PROT_GUARD_TIME_AT_INITIATOR, 6 },
+ { PORT100_IN_PROT_END, 0 },
+ },
+ [NFC_DIGITAL_FRAMING_NFCA_T1T] = {
+ /* nfc_digital_framing_nfca_short */
+ { PORT100_IN_PROT_ADD_CRC, 2 },
+ { PORT100_IN_PROT_CHECK_CRC, 2 },
+ { PORT100_IN_PROT_VALID_BIT_NUMBER, 8 },
+ { PORT100_IN_PROT_T1_TAG_FRAME, 2 },
+ { PORT100_IN_PROT_END, 0 },
+ },
+ [NFC_DIGITAL_FRAMING_NFCA_T2T] = {
+ /* nfc_digital_framing_nfca_standard */
+ { PORT100_IN_PROT_ADD_CRC, 1 },
+ { PORT100_IN_PROT_CHECK_CRC, 0 },
+ { PORT100_IN_PROT_END, 0 },
+ },
+ [NFC_DIGITAL_FRAMING_NFCA_T4T] = {
+ /* nfc_digital_framing_nfca_standard_with_crc_a */
+ { PORT100_IN_PROT_END, 0 },
+ },
+ [NFC_DIGITAL_FRAMING_NFCA_NFC_DEP] = {
+ /* nfc_digital_framing_nfca_standard */
+ { PORT100_IN_PROT_END, 0 },
+ },
+ [NFC_DIGITAL_FRAMING_NFCF] = {
+ { PORT100_IN_PROT_INITIAL_GUARD_TIME, 18 },
+ { PORT100_IN_PROT_ADD_CRC, 1 },
+ { PORT100_IN_PROT_CHECK_CRC, 1 },
+ { PORT100_IN_PROT_MULTI_CARD, 0 },
+ { PORT100_IN_PROT_ADD_PARITY, 0 },
+ { PORT100_IN_PROT_CHECK_PARITY, 0 },
+ { PORT100_IN_PROT_BITWISE_AC_RECV_MODE, 0 },
+ { PORT100_IN_PROT_VALID_BIT_NUMBER, 8 },
+ { PORT100_IN_PROT_CRYPTO1, 0 },
+ { PORT100_IN_PROT_ADD_SOF, 0 },
+ { PORT100_IN_PROT_CHECK_SOF, 0 },
+ { PORT100_IN_PROT_ADD_EOF, 0 },
+ { PORT100_IN_PROT_CHECK_EOF, 0 },
+ { PORT100_IN_PROT_DEAF_TIME, 4 },
+ { PORT100_IN_PROT_CRM, 0 },
+ { PORT100_IN_PROT_CRM_MIN_LEN, 0 },
+ { PORT100_IN_PROT_T1_TAG_FRAME, 0 },
+ { PORT100_IN_PROT_RFCA, 0 },
+ { PORT100_IN_PROT_GUARD_TIME_AT_INITIATOR, 6 },
+ { PORT100_IN_PROT_END, 0 },
+ },
+ [NFC_DIGITAL_FRAMING_NFCF_T3T] = {
+ /* nfc_digital_framing_nfcf */
+ { PORT100_IN_PROT_END, 0 },
+ },
+ [NFC_DIGITAL_FRAMING_NFCF_NFC_DEP] = {
+ /* nfc_digital_framing_nfcf */
+ { PORT100_IN_PROT_INITIAL_GUARD_TIME, 18 },
+ { PORT100_IN_PROT_ADD_CRC, 1 },
+ { PORT100_IN_PROT_CHECK_CRC, 1 },
+ { PORT100_IN_PROT_MULTI_CARD, 0 },
+ { PORT100_IN_PROT_ADD_PARITY, 0 },
+ { PORT100_IN_PROT_CHECK_PARITY, 0 },
+ { PORT100_IN_PROT_BITWISE_AC_RECV_MODE, 0 },
+ { PORT100_IN_PROT_VALID_BIT_NUMBER, 8 },
+ { PORT100_IN_PROT_CRYPTO1, 0 },
+ { PORT100_IN_PROT_ADD_SOF, 0 },
+ { PORT100_IN_PROT_CHECK_SOF, 0 },
+ { PORT100_IN_PROT_ADD_EOF, 0 },
+ { PORT100_IN_PROT_CHECK_EOF, 0 },
+ { PORT100_IN_PROT_DEAF_TIME, 4 },
+ { PORT100_IN_PROT_CRM, 0 },
+ { PORT100_IN_PROT_CRM_MIN_LEN, 0 },
+ { PORT100_IN_PROT_T1_TAG_FRAME, 0 },
+ { PORT100_IN_PROT_RFCA, 0 },
+ { PORT100_IN_PROT_GUARD_TIME_AT_INITIATOR, 6 },
+ { PORT100_IN_PROT_END, 0 },
+ },
+ [NFC_DIGITAL_FRAMING_NFC_DEP_ACTIVATED] = {
+ { PORT100_IN_PROT_END, 0 },
+ },
+ [NFC_DIGITAL_FRAMING_NFCB] = {
+ { PORT100_IN_PROT_INITIAL_GUARD_TIME, 20 },
+ { PORT100_IN_PROT_ADD_CRC, 1 },
+ { PORT100_IN_PROT_CHECK_CRC, 1 },
+ { PORT100_IN_PROT_MULTI_CARD, 0 },
+ { PORT100_IN_PROT_ADD_PARITY, 0 },
+ { PORT100_IN_PROT_CHECK_PARITY, 0 },
+ { PORT100_IN_PROT_BITWISE_AC_RECV_MODE, 0 },
+ { PORT100_IN_PROT_VALID_BIT_NUMBER, 8 },
+ { PORT100_IN_PROT_CRYPTO1, 0 },
+ { PORT100_IN_PROT_ADD_SOF, 1 },
+ { PORT100_IN_PROT_CHECK_SOF, 1 },
+ { PORT100_IN_PROT_ADD_EOF, 1 },
+ { PORT100_IN_PROT_CHECK_EOF, 1 },
+ { PORT100_IN_PROT_DEAF_TIME, 4 },
+ { PORT100_IN_PROT_CRM, 0 },
+ { PORT100_IN_PROT_CRM_MIN_LEN, 0 },
+ { PORT100_IN_PROT_T1_TAG_FRAME, 0 },
+ { PORT100_IN_PROT_RFCA, 0 },
+ { PORT100_IN_PROT_GUARD_TIME_AT_INITIATOR, 6 },
+ { PORT100_IN_PROT_END, 0 },
+ },
+ [NFC_DIGITAL_FRAMING_NFCB_T4T] = {
+ /* nfc_digital_framing_nfcb */
+ { PORT100_IN_PROT_END, 0 },
+ },
+ /* Ensures the array has NFC_DIGITAL_FRAMING_LAST elements */
+ [NFC_DIGITAL_FRAMING_LAST] = {
+ { PORT100_IN_PROT_END, 0 },
+ },
+};
+
+static struct port100_protocol
+tg_protocols[][PORT100_TG_MAX_NUM_PROTOCOLS + 1] = {
+ [NFC_DIGITAL_FRAMING_NFCA_SHORT] = {
+ { PORT100_TG_PROT_END, 0 },
+ },
+ [NFC_DIGITAL_FRAMING_NFCA_STANDARD] = {
+ { PORT100_TG_PROT_END, 0 },
+ },
+ [NFC_DIGITAL_FRAMING_NFCA_STANDARD_WITH_CRC_A] = {
+ { PORT100_TG_PROT_END, 0 },
+ },
+ [NFC_DIGITAL_FRAMING_NFCA_T1T] = {
+ { PORT100_TG_PROT_END, 0 },
+ },
+ [NFC_DIGITAL_FRAMING_NFCA_T2T] = {
+ { PORT100_TG_PROT_END, 0 },
+ },
+ [NFC_DIGITAL_FRAMING_NFCA_NFC_DEP] = {
+ { PORT100_TG_PROT_TU, 1 },
+ { PORT100_TG_PROT_RF_OFF, 0 },
+ { PORT100_TG_PROT_CRM, 7 },
+ { PORT100_TG_PROT_END, 0 },
+ },
+ [NFC_DIGITAL_FRAMING_NFCF] = {
+ { PORT100_TG_PROT_END, 0 },
+ },
+ [NFC_DIGITAL_FRAMING_NFCF_T3T] = {
+ { PORT100_TG_PROT_END, 0 },
+ },
+ [NFC_DIGITAL_FRAMING_NFCF_NFC_DEP] = {
+ { PORT100_TG_PROT_TU, 1 },
+ { PORT100_TG_PROT_RF_OFF, 0 },
+ { PORT100_TG_PROT_CRM, 7 },
+ { PORT100_TG_PROT_END, 0 },
+ },
+ [NFC_DIGITAL_FRAMING_NFC_DEP_ACTIVATED] = {
+ { PORT100_TG_PROT_RF_OFF, 1 },
+ { PORT100_TG_PROT_END, 0 },
+ },
+ /* Ensures the array has NFC_DIGITAL_FRAMING_LAST elements */
+ [NFC_DIGITAL_FRAMING_LAST] = {
+ { PORT100_TG_PROT_END, 0 },
+ },
+};
+
+struct port100 {
+ struct nfc_digital_dev *nfc_digital_dev;
+
+ int skb_headroom;
+ int skb_tailroom;
+
+ struct usb_device *udev;
+ struct usb_interface *interface;
+
+ struct urb *out_urb;
+ struct urb *in_urb;
+
+ /* This mutex protects the out_urb and avoids to submit a new command
+ * through port100_send_frame_async() while the previous one is being
+ * canceled through port100_abort_cmd().
+ */
+ struct mutex out_urb_lock;
+
+ struct work_struct cmd_complete_work;
+
+ u8 cmd_type;
+
+ /* The digital stack serializes commands to be sent. There is no need
+ * for any queuing/locking mechanism at driver level.
+ */
+ struct port100_cmd *cmd;
+
+ bool cmd_cancel;
+ struct completion cmd_cancel_done;
+};
+
+struct port100_cmd {
+ u8 code;
+ int status;
+ struct sk_buff *req;
+ struct sk_buff *resp;
+ int resp_len;
+ port100_send_async_complete_t complete_cb;
+ void *complete_cb_context;
+};
+
+struct port100_frame {
+ u8 preamble;
+ __be16 start_frame;
+ __be16 extended_frame;
+ __le16 datalen;
+ u8 datalen_checksum;
+ u8 data[];
+} __packed;
+
+struct port100_ack_frame {
+ u8 preamble;
+ __be16 start_frame;
+ __be16 ack_frame;
+ u8 postambule;
+} __packed;
+
+struct port100_cb_arg {
+ nfc_digital_cmd_complete_t complete_cb;
+ void *complete_arg;
+ u8 mdaa;
+};
+
+struct port100_tg_comm_rf_cmd {
+ __le16 guard_time;
+ __le16 send_timeout;
+ u8 mdaa;
+ u8 nfca_param[6];
+ u8 nfcf_param[18];
+ u8 mf_halted;
+ u8 arae_flag;
+ __le16 recv_timeout;
+ u8 data[];
+} __packed;
+
+struct port100_tg_comm_rf_res {
+ u8 comm_type;
+ u8 ar_status;
+ u8 target_activated;
+ __le32 status;
+ u8 data[];
+} __packed;
+
+/* The rule: value + checksum = 0 */
+static inline u8 port100_checksum(u16 value)
+{
+ return ~(((u8 *)&value)[0] + ((u8 *)&value)[1]) + 1;
+}
+
+/* The rule: sum(data elements) + checksum = 0 */
+static u8 port100_data_checksum(u8 *data, int datalen)
+{
+ u8 sum = 0;
+ int i;
+
+ for (i = 0; i < datalen; i++)
+ sum += data[i];
+
+ return port100_checksum(sum);
+}
+
+static void port100_tx_frame_init(void *_frame, u8 cmd_code)
+{
+ struct port100_frame *frame = _frame;
+
+ frame->preamble = 0;
+ frame->start_frame = cpu_to_be16(PORT100_FRAME_SOF);
+ frame->extended_frame = cpu_to_be16(PORT100_FRAME_EXT);
+ PORT100_FRAME_DIRECTION(frame) = PORT100_FRAME_DIR_OUT;
+ PORT100_FRAME_CMD(frame) = cmd_code;
+ frame->datalen = cpu_to_le16(2);
+}
+
+static void port100_tx_frame_finish(void *_frame)
+{
+ struct port100_frame *frame = _frame;
+
+ frame->datalen_checksum = port100_checksum(le16_to_cpu(frame->datalen));
+
+ PORT100_FRAME_CHECKSUM(frame) =
+ port100_data_checksum(frame->data, le16_to_cpu(frame->datalen));
+
+ PORT100_FRAME_POSTAMBLE(frame) = 0;
+}
+
+static void port100_tx_update_payload_len(void *_frame, int len)
+{
+ struct port100_frame *frame = _frame;
+
+ frame->datalen = cpu_to_le16(le16_to_cpu(frame->datalen) + len);
+}
+
+static bool port100_rx_frame_is_valid(void *_frame)
+{
+ u8 checksum;
+ struct port100_frame *frame = _frame;
+
+ if (frame->start_frame != cpu_to_be16(PORT100_FRAME_SOF) ||
+ frame->extended_frame != cpu_to_be16(PORT100_FRAME_EXT))
+ return false;
+
+ checksum = port100_checksum(le16_to_cpu(frame->datalen));
+ if (checksum != frame->datalen_checksum)
+ return false;
+
+ checksum = port100_data_checksum(frame->data,
+ le16_to_cpu(frame->datalen));
+ if (checksum != PORT100_FRAME_CHECKSUM(frame))
+ return false;
+
+ return true;
+}
+
+static bool port100_rx_frame_is_ack(struct port100_ack_frame *frame)
+{
+ return (frame->start_frame == cpu_to_be16(PORT100_FRAME_SOF) &&
+ frame->ack_frame == cpu_to_be16(PORT100_FRAME_ACK));
+}
+
+static inline int port100_rx_frame_size(void *frame)
+{
+ struct port100_frame *f = frame;
+
+ return sizeof(struct port100_frame) + le16_to_cpu(f->datalen) +
+ PORT100_FRAME_TAIL_LEN;
+}
+
+static bool port100_rx_frame_is_cmd_response(struct port100 *dev, void *frame)
+{
+ struct port100_frame *f = frame;
+
+ return (PORT100_FRAME_CMD(f) == PORT100_CMD_RESPONSE(dev->cmd->code));
+}
+
+static void port100_recv_response(struct urb *urb)
+{
+ struct port100 *dev = urb->context;
+ struct port100_cmd *cmd = dev->cmd;
+ u8 *in_frame;
+
+ cmd->status = urb->status;
+
+ switch (urb->status) {
+ case 0:
+ break; /* success */
+ case -ECONNRESET:
+ case -ENOENT:
+ nfc_err(&dev->interface->dev,
+ "The urb has been canceled (status %d)\n", urb->status);
+ goto sched_wq;
+ case -ESHUTDOWN:
+ default:
+ nfc_err(&dev->interface->dev, "Urb failure (status %d)\n",
+ urb->status);
+ goto sched_wq;
+ }
+
+ in_frame = dev->in_urb->transfer_buffer;
+
+ if (!port100_rx_frame_is_valid(in_frame)) {
+ nfc_err(&dev->interface->dev, "Received an invalid frame\n");
+ cmd->status = -EIO;
+ goto sched_wq;
+ }
+
+ print_hex_dump_debug("PORT100 RX: ", DUMP_PREFIX_NONE, 16, 1, in_frame,
+ port100_rx_frame_size(in_frame), false);
+
+ if (!port100_rx_frame_is_cmd_response(dev, in_frame)) {
+ nfc_err(&dev->interface->dev,
+ "It's not the response to the last command\n");
+ cmd->status = -EIO;
+ goto sched_wq;
+ }
+
+sched_wq:
+ schedule_work(&dev->cmd_complete_work);
+}
+
+static int port100_submit_urb_for_response(struct port100 *dev, gfp_t flags)
+{
+ dev->in_urb->complete = port100_recv_response;
+
+ return usb_submit_urb(dev->in_urb, flags);
+}
+
+static void port100_recv_ack(struct urb *urb)
+{
+ struct port100 *dev = urb->context;
+ struct port100_cmd *cmd = dev->cmd;
+ struct port100_ack_frame *in_frame;
+ int rc;
+
+ cmd->status = urb->status;
+
+ switch (urb->status) {
+ case 0:
+ break; /* success */
+ case -ECONNRESET:
+ case -ENOENT:
+ nfc_err(&dev->interface->dev,
+ "The urb has been stopped (status %d)\n", urb->status);
+ goto sched_wq;
+ case -ESHUTDOWN:
+ default:
+ nfc_err(&dev->interface->dev, "Urb failure (status %d)\n",
+ urb->status);
+ goto sched_wq;
+ }
+
+ in_frame = dev->in_urb->transfer_buffer;
+
+ if (!port100_rx_frame_is_ack(in_frame)) {
+ nfc_err(&dev->interface->dev, "Received an invalid ack\n");
+ cmd->status = -EIO;
+ goto sched_wq;
+ }
+
+ rc = port100_submit_urb_for_response(dev, GFP_ATOMIC);
+ if (rc) {
+ nfc_err(&dev->interface->dev,
+ "usb_submit_urb failed with result %d\n", rc);
+ cmd->status = rc;
+ goto sched_wq;
+ }
+
+ return;
+
+sched_wq:
+ schedule_work(&dev->cmd_complete_work);
+}
+
+static int port100_submit_urb_for_ack(struct port100 *dev, gfp_t flags)
+{
+ dev->in_urb->complete = port100_recv_ack;
+
+ return usb_submit_urb(dev->in_urb, flags);
+}
+
+static int port100_send_ack(struct port100 *dev)
+{
+ int rc;
+
+ mutex_lock(&dev->out_urb_lock);
+
+ init_completion(&dev->cmd_cancel_done);
+
+ usb_kill_urb(dev->out_urb);
+
+ dev->out_urb->transfer_buffer = ack_frame;
+ dev->out_urb->transfer_buffer_length = sizeof(ack_frame);
+ rc = usb_submit_urb(dev->out_urb, GFP_KERNEL);
+
+ /* Set the cmd_cancel flag only if the URB has been successfully
+ * submitted. It will be reset by the out URB completion callback
+ * port100_send_complete().
+ */
+ dev->cmd_cancel = !rc;
+
+ mutex_unlock(&dev->out_urb_lock);
+
+ if (!rc)
+ wait_for_completion(&dev->cmd_cancel_done);
+
+ return rc;
+}
+
+static int port100_send_frame_async(struct port100 *dev, struct sk_buff *out,
+ struct sk_buff *in, int in_len)
+{
+ int rc;
+
+ mutex_lock(&dev->out_urb_lock);
+
+ /* A command cancel frame as been sent through dev->out_urb. Don't try
+ * to submit a new one.
+ */
+ if (dev->cmd_cancel) {
+ rc = -EAGAIN;
+ goto exit;
+ }
+
+ dev->out_urb->transfer_buffer = out->data;
+ dev->out_urb->transfer_buffer_length = out->len;
+
+ dev->in_urb->transfer_buffer = in->data;
+ dev->in_urb->transfer_buffer_length = in_len;
+
+ print_hex_dump_debug("PORT100 TX: ", DUMP_PREFIX_NONE, 16, 1,
+ out->data, out->len, false);
+
+ rc = usb_submit_urb(dev->out_urb, GFP_KERNEL);
+ if (rc)
+ goto exit;
+
+ rc = port100_submit_urb_for_ack(dev, GFP_KERNEL);
+ if (rc)
+ usb_unlink_urb(dev->out_urb);
+
+exit:
+ mutex_unlock(&dev->out_urb_lock);
+
+ return rc;
+}
+
+static void port100_build_cmd_frame(struct port100 *dev, u8 cmd_code,
+ struct sk_buff *skb)
+{
+ /* payload is already there, just update datalen */
+ int payload_len = skb->len;
+
+ skb_push(skb, PORT100_FRAME_HEADER_LEN);
+ skb_put(skb, PORT100_FRAME_TAIL_LEN);
+
+ port100_tx_frame_init(skb->data, cmd_code);
+ port100_tx_update_payload_len(skb->data, payload_len);
+ port100_tx_frame_finish(skb->data);
+}
+
+static void port100_send_async_complete(struct port100 *dev)
+{
+ struct port100_cmd *cmd = dev->cmd;
+ int status = cmd->status;
+
+ struct sk_buff *req = cmd->req;
+ struct sk_buff *resp = cmd->resp;
+
+ dev_kfree_skb(req);
+
+ dev->cmd = NULL;
+
+ if (status < 0) {
+ cmd->complete_cb(dev, cmd->complete_cb_context,
+ ERR_PTR(status));
+ dev_kfree_skb(resp);
+ goto done;
+ }
+
+ skb_put(resp, port100_rx_frame_size(resp->data));
+ skb_pull(resp, PORT100_FRAME_HEADER_LEN);
+ skb_trim(resp, resp->len - PORT100_FRAME_TAIL_LEN);
+
+ cmd->complete_cb(dev, cmd->complete_cb_context, resp);
+
+done:
+ kfree(cmd);
+}
+
+static int port100_send_cmd_async(struct port100 *dev, u8 cmd_code,
+ struct sk_buff *req,
+ port100_send_async_complete_t complete_cb,
+ void *complete_cb_context)
+{
+ struct port100_cmd *cmd;
+ struct sk_buff *resp;
+ int rc;
+ int resp_len = PORT100_FRAME_HEADER_LEN +
+ PORT100_FRAME_MAX_PAYLOAD_LEN +
+ PORT100_FRAME_TAIL_LEN;
+
+ if (dev->cmd) {
+ nfc_err(&dev->interface->dev,
+ "A command is still in process\n");
+ return -EBUSY;
+ }
+
+ resp = alloc_skb(resp_len, GFP_KERNEL);
+ if (!resp)
+ return -ENOMEM;
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd) {
+ dev_kfree_skb(resp);
+ return -ENOMEM;
+ }
+
+ cmd->code = cmd_code;
+ cmd->req = req;
+ cmd->resp = resp;
+ cmd->resp_len = resp_len;
+ cmd->complete_cb = complete_cb;
+ cmd->complete_cb_context = complete_cb_context;
+
+ port100_build_cmd_frame(dev, cmd_code, req);
+
+ dev->cmd = cmd;
+
+ rc = port100_send_frame_async(dev, req, resp, resp_len);
+ if (rc) {
+ kfree(cmd);
+ dev_kfree_skb(resp);
+ dev->cmd = NULL;
+ }
+
+ return rc;
+}
+
+struct port100_sync_cmd_response {
+ struct sk_buff *resp;
+ struct completion done;
+};
+
+static void port100_wq_cmd_complete(struct work_struct *work)
+{
+ struct port100 *dev = container_of(work, struct port100,
+ cmd_complete_work);
+
+ port100_send_async_complete(dev);
+}
+
+static void port100_send_sync_complete(struct port100 *dev, void *_arg,
+ struct sk_buff *resp)
+{
+ struct port100_sync_cmd_response *arg = _arg;
+
+ arg->resp = resp;
+ complete(&arg->done);
+}
+
+static struct sk_buff *port100_send_cmd_sync(struct port100 *dev, u8 cmd_code,
+ struct sk_buff *req)
+{
+ int rc;
+ struct port100_sync_cmd_response arg;
+
+ init_completion(&arg.done);
+
+ rc = port100_send_cmd_async(dev, cmd_code, req,
+ port100_send_sync_complete, &arg);
+ if (rc) {
+ dev_kfree_skb(req);
+ return ERR_PTR(rc);
+ }
+
+ wait_for_completion(&arg.done);
+
+ return arg.resp;
+}
+
+static void port100_send_complete(struct urb *urb)
+{
+ struct port100 *dev = urb->context;
+
+ if (dev->cmd_cancel) {
+ dev->cmd_cancel = false;
+ complete(&dev->cmd_cancel_done);
+ }
+
+ switch (urb->status) {
+ case 0:
+ break; /* success */
+ case -ECONNRESET:
+ case -ENOENT:
+ nfc_err(&dev->interface->dev,
+ "The urb has been stopped (status %d)\n", urb->status);
+ break;
+ case -ESHUTDOWN:
+ default:
+ nfc_err(&dev->interface->dev, "Urb failure (status %d)\n",
+ urb->status);
+ }
+}
+
+static void port100_abort_cmd(struct nfc_digital_dev *ddev)
+{
+ struct port100 *dev = nfc_digital_get_drvdata(ddev);
+
+ /* An ack will cancel the last issued command */
+ port100_send_ack(dev);
+
+ /* cancel the urb request */
+ usb_kill_urb(dev->in_urb);
+}
+
+static struct sk_buff *port100_alloc_skb(struct port100 *dev, unsigned int size)
+{
+ struct sk_buff *skb;
+
+ skb = alloc_skb(dev->skb_headroom + dev->skb_tailroom + size,
+ GFP_KERNEL);
+ if (skb)
+ skb_reserve(skb, dev->skb_headroom);
+
+ return skb;
+}
+
+static int port100_set_command_type(struct port100 *dev, u8 command_type)
+{
+ struct sk_buff *skb;
+ struct sk_buff *resp;
+ int rc;
+
+ skb = port100_alloc_skb(dev, 1);
+ if (!skb)
+ return -ENOMEM;
+
+ *skb_put(skb, sizeof(u8)) = command_type;
+
+ resp = port100_send_cmd_sync(dev, PORT100_CMD_SET_COMMAND_TYPE, skb);
+ if (IS_ERR(resp))
+ return PTR_ERR(resp);
+
+ rc = resp->data[0];
+
+ dev_kfree_skb(resp);
+
+ return rc;
+}
+
+static u64 port100_get_command_type_mask(struct port100 *dev)
+{
+ struct sk_buff *skb;
+ struct sk_buff *resp;
+ u64 mask;
+
+ skb = port100_alloc_skb(dev, 0);
+ if (!skb)
+ return -ENOMEM;
+
+ resp = port100_send_cmd_sync(dev, PORT100_CMD_GET_COMMAND_TYPE, skb);
+ if (IS_ERR(resp))
+ return PTR_ERR(resp);
+
+ if (resp->len < 8)
+ mask = 0;
+ else
+ mask = be64_to_cpu(*(__be64 *)resp->data);
+
+ dev_kfree_skb(resp);
+
+ return mask;
+}
+
+static u16 port100_get_firmware_version(struct port100 *dev)
+{
+ struct sk_buff *skb;
+ struct sk_buff *resp;
+ u16 fw_ver;
+
+ skb = port100_alloc_skb(dev, 0);
+ if (!skb)
+ return 0;
+
+ resp = port100_send_cmd_sync(dev, PORT100_CMD_GET_FIRMWARE_VERSION,
+ skb);
+ if (IS_ERR(resp))
+ return 0;
+
+ fw_ver = le16_to_cpu(*(__le16 *)resp->data);
+
+ dev_kfree_skb(resp);
+
+ return fw_ver;
+}
+
+static int port100_switch_rf(struct nfc_digital_dev *ddev, bool on)
+{
+ struct port100 *dev = nfc_digital_get_drvdata(ddev);
+ struct sk_buff *skb, *resp;
+
+ skb = port100_alloc_skb(dev, 1);
+ if (!skb)
+ return -ENOMEM;
+
+ *skb_put(skb, 1) = on ? 1 : 0;
+
+ /* Cancel the last command if the device is being switched off */
+ if (!on)
+ port100_abort_cmd(ddev);
+
+ resp = port100_send_cmd_sync(dev, PORT100_CMD_SWITCH_RF, skb);
+
+ if (IS_ERR(resp))
+ return PTR_ERR(resp);
+
+ dev_kfree_skb(resp);
+
+ return 0;
+}
+
+static int port100_in_set_rf(struct nfc_digital_dev *ddev, u8 rf)
+{
+ struct port100 *dev = nfc_digital_get_drvdata(ddev);
+ struct sk_buff *skb;
+ struct sk_buff *resp;
+ int rc;
+
+ if (rf >= NFC_DIGITAL_RF_TECH_LAST)
+ return -EINVAL;
+
+ skb = port100_alloc_skb(dev, sizeof(struct port100_in_rf_setting));
+ if (!skb)
+ return -ENOMEM;
+
+ memcpy(skb_put(skb, sizeof(struct port100_in_rf_setting)),
+ &in_rf_settings[rf],
+ sizeof(struct port100_in_rf_setting));
+
+ resp = port100_send_cmd_sync(dev, PORT100_CMD_IN_SET_RF, skb);
+
+ if (IS_ERR(resp))
+ return PTR_ERR(resp);
+
+ rc = resp->data[0];
+
+ dev_kfree_skb(resp);
+
+ return rc;
+}
+
+static int port100_in_set_framing(struct nfc_digital_dev *ddev, int param)
+{
+ struct port100 *dev = nfc_digital_get_drvdata(ddev);
+ struct port100_protocol *protocols;
+ struct sk_buff *skb;
+ struct sk_buff *resp;
+ int num_protocols;
+ size_t size;
+ int rc;
+
+ if (param >= NFC_DIGITAL_FRAMING_LAST)
+ return -EINVAL;
+
+ protocols = in_protocols[param];
+
+ num_protocols = 0;
+ while (protocols[num_protocols].number != PORT100_IN_PROT_END)
+ num_protocols++;
+
+ if (!num_protocols)
+ return 0;
+
+ size = sizeof(struct port100_protocol) * num_protocols;
+
+ skb = port100_alloc_skb(dev, size);
+ if (!skb)
+ return -ENOMEM;
+
+ memcpy(skb_put(skb, size), protocols, size);
+
+ resp = port100_send_cmd_sync(dev, PORT100_CMD_IN_SET_PROTOCOL, skb);
+
+ if (IS_ERR(resp))
+ return PTR_ERR(resp);
+
+ rc = resp->data[0];
+
+ dev_kfree_skb(resp);
+
+ return rc;
+}
+
+static int port100_in_configure_hw(struct nfc_digital_dev *ddev, int type,
+ int param)
+{
+ if (type == NFC_DIGITAL_CONFIG_RF_TECH)
+ return port100_in_set_rf(ddev, param);
+
+ if (type == NFC_DIGITAL_CONFIG_FRAMING)
+ return port100_in_set_framing(ddev, param);
+
+ return -EINVAL;
+}
+
+static void port100_in_comm_rf_complete(struct port100 *dev, void *arg,
+ struct sk_buff *resp)
+{
+ struct port100_cb_arg *cb_arg = arg;
+ nfc_digital_cmd_complete_t cb = cb_arg->complete_cb;
+ u32 status;
+ int rc;
+
+ if (IS_ERR(resp)) {
+ rc = PTR_ERR(resp);
+ goto exit;
+ }
+
+ if (resp->len < 4) {
+ nfc_err(&dev->interface->dev,
+ "Invalid packet length received\n");
+ rc = -EIO;
+ goto error;
+ }
+
+ status = le32_to_cpu(*(__le32 *)resp->data);
+
+ skb_pull(resp, sizeof(u32));
+
+ if (status == PORT100_CMD_STATUS_TIMEOUT) {
+ rc = -ETIMEDOUT;
+ goto error;
+ }
+
+ if (status != PORT100_CMD_STATUS_OK) {
+ nfc_err(&dev->interface->dev,
+ "in_comm_rf failed with status 0x%08x\n", status);
+ rc = -EIO;
+ goto error;
+ }
+
+ /* Remove collision bits byte */
+ skb_pull(resp, 1);
+
+ goto exit;
+
+error:
+ kfree_skb(resp);
+ resp = ERR_PTR(rc);
+
+exit:
+ cb(dev->nfc_digital_dev, cb_arg->complete_arg, resp);
+
+ kfree(cb_arg);
+}
+
+static int port100_in_send_cmd(struct nfc_digital_dev *ddev,
+ struct sk_buff *skb, u16 _timeout,
+ nfc_digital_cmd_complete_t cb, void *arg)
+{
+ struct port100 *dev = nfc_digital_get_drvdata(ddev);
+ struct port100_cb_arg *cb_arg;
+ __le16 timeout;
+
+ cb_arg = kzalloc(sizeof(struct port100_cb_arg), GFP_KERNEL);
+ if (!cb_arg)
+ return -ENOMEM;
+
+ cb_arg->complete_cb = cb;
+ cb_arg->complete_arg = arg;
+
+ timeout = cpu_to_le16(_timeout * 10);
+
+ memcpy(skb_push(skb, sizeof(__le16)), &timeout, sizeof(__le16));
+
+ return port100_send_cmd_async(dev, PORT100_CMD_IN_COMM_RF, skb,
+ port100_in_comm_rf_complete, cb_arg);
+}
+
+static int port100_tg_set_rf(struct nfc_digital_dev *ddev, u8 rf)
+{
+ struct port100 *dev = nfc_digital_get_drvdata(ddev);
+ struct sk_buff *skb;
+ struct sk_buff *resp;
+ int rc;
+
+ if (rf >= NFC_DIGITAL_RF_TECH_LAST)
+ return -EINVAL;
+
+ skb = port100_alloc_skb(dev, sizeof(struct port100_tg_rf_setting));
+ if (!skb)
+ return -ENOMEM;
+
+ memcpy(skb_put(skb, sizeof(struct port100_tg_rf_setting)),
+ &tg_rf_settings[rf],
+ sizeof(struct port100_tg_rf_setting));
+
+ resp = port100_send_cmd_sync(dev, PORT100_CMD_TG_SET_RF, skb);
+
+ if (IS_ERR(resp))
+ return PTR_ERR(resp);
+
+ rc = resp->data[0];
+
+ dev_kfree_skb(resp);
+
+ return rc;
+}
+
+static int port100_tg_set_framing(struct nfc_digital_dev *ddev, int param)
+{
+ struct port100 *dev = nfc_digital_get_drvdata(ddev);
+ struct port100_protocol *protocols;
+ struct sk_buff *skb;
+ struct sk_buff *resp;
+ int rc;
+ int num_protocols;
+ size_t size;
+
+ if (param >= NFC_DIGITAL_FRAMING_LAST)
+ return -EINVAL;
+
+ protocols = tg_protocols[param];
+
+ num_protocols = 0;
+ while (protocols[num_protocols].number != PORT100_TG_PROT_END)
+ num_protocols++;
+
+ if (!num_protocols)
+ return 0;
+
+ size = sizeof(struct port100_protocol) * num_protocols;
+
+ skb = port100_alloc_skb(dev, size);
+ if (!skb)
+ return -ENOMEM;
+
+ memcpy(skb_put(skb, size), protocols, size);
+
+ resp = port100_send_cmd_sync(dev, PORT100_CMD_TG_SET_PROTOCOL, skb);
+
+ if (IS_ERR(resp))
+ return PTR_ERR(resp);
+
+ rc = resp->data[0];
+
+ dev_kfree_skb(resp);
+
+ return rc;
+}
+
+static int port100_tg_configure_hw(struct nfc_digital_dev *ddev, int type,
+ int param)
+{
+ if (type == NFC_DIGITAL_CONFIG_RF_TECH)
+ return port100_tg_set_rf(ddev, param);
+
+ if (type == NFC_DIGITAL_CONFIG_FRAMING)
+ return port100_tg_set_framing(ddev, param);
+
+ return -EINVAL;
+}
+
+static bool port100_tg_target_activated(struct port100 *dev, u8 tgt_activated)
+{
+ u8 mask;
+
+ switch (dev->cmd_type) {
+ case PORT100_CMD_TYPE_0:
+ mask = PORT100_MDAA_TGT_HAS_BEEN_ACTIVATED_MASK;
+ break;
+ case PORT100_CMD_TYPE_1:
+ mask = PORT100_MDAA_TGT_HAS_BEEN_ACTIVATED_MASK |
+ PORT100_MDAA_TGT_WAS_ACTIVATED_MASK;
+ break;
+ default:
+ nfc_err(&dev->interface->dev, "Unknown command type\n");
+ return false;
+ }
+
+ return ((tgt_activated & mask) == mask);
+}
+
+static void port100_tg_comm_rf_complete(struct port100 *dev, void *arg,
+ struct sk_buff *resp)
+{
+ u32 status;
+ struct port100_cb_arg *cb_arg = arg;
+ nfc_digital_cmd_complete_t cb = cb_arg->complete_cb;
+ struct port100_tg_comm_rf_res *hdr;
+
+ if (IS_ERR(resp))
+ goto exit;
+
+ hdr = (struct port100_tg_comm_rf_res *)resp->data;
+
+ status = le32_to_cpu(hdr->status);
+
+ if (cb_arg->mdaa &&
+ !port100_tg_target_activated(dev, hdr->target_activated)) {
+ kfree_skb(resp);
+ resp = ERR_PTR(-ETIMEDOUT);
+
+ goto exit;
+ }
+
+ skb_pull(resp, sizeof(struct port100_tg_comm_rf_res));
+
+ if (status != PORT100_CMD_STATUS_OK) {
+ kfree_skb(resp);
+
+ if (status == PORT100_CMD_STATUS_TIMEOUT)
+ resp = ERR_PTR(-ETIMEDOUT);
+ else
+ resp = ERR_PTR(-EIO);
+ }
+
+exit:
+ cb(dev->nfc_digital_dev, cb_arg->complete_arg, resp);
+
+ kfree(cb_arg);
+}
+
+static int port100_tg_send_cmd(struct nfc_digital_dev *ddev,
+ struct sk_buff *skb, u16 timeout,
+ nfc_digital_cmd_complete_t cb, void *arg)
+{
+ struct port100 *dev = nfc_digital_get_drvdata(ddev);
+ struct port100_tg_comm_rf_cmd *hdr;
+ struct port100_cb_arg *cb_arg;
+
+ cb_arg = kzalloc(sizeof(struct port100_cb_arg), GFP_KERNEL);
+ if (!cb_arg)
+ return -ENOMEM;
+
+ cb_arg->complete_cb = cb;
+ cb_arg->complete_arg = arg;
+
+ skb_push(skb, sizeof(struct port100_tg_comm_rf_cmd));
+
+ hdr = (struct port100_tg_comm_rf_cmd *)skb->data;
+
+ memset(hdr, 0, sizeof(struct port100_tg_comm_rf_cmd));
+ hdr->guard_time = cpu_to_le16(500);
+ hdr->send_timeout = cpu_to_le16(0xFFFF);
+ hdr->recv_timeout = cpu_to_le16(timeout);
+
+ return port100_send_cmd_async(dev, PORT100_CMD_TG_COMM_RF, skb,
+ port100_tg_comm_rf_complete, cb_arg);
+}
+
+static int port100_listen_mdaa(struct nfc_digital_dev *ddev,
+ struct digital_tg_mdaa_params *params,
+ u16 timeout,
+ nfc_digital_cmd_complete_t cb, void *arg)
+{
+ struct port100 *dev = nfc_digital_get_drvdata(ddev);
+ struct port100_tg_comm_rf_cmd *hdr;
+ struct port100_cb_arg *cb_arg;
+ struct sk_buff *skb;
+ int rc;
+
+ rc = port100_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_RF_TECH,
+ NFC_DIGITAL_RF_TECH_106A);
+ if (rc)
+ return rc;
+
+ rc = port100_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING,
+ NFC_DIGITAL_FRAMING_NFCA_NFC_DEP);
+ if (rc)
+ return rc;
+
+ cb_arg = kzalloc(sizeof(struct port100_cb_arg), GFP_KERNEL);
+ if (!cb_arg)
+ return -ENOMEM;
+
+ cb_arg->complete_cb = cb;
+ cb_arg->complete_arg = arg;
+ cb_arg->mdaa = 1;
+
+ skb = port100_alloc_skb(dev, 0);
+ if (!skb) {
+ kfree(cb_arg);
+ return -ENOMEM;
+ }
+
+ skb_push(skb, sizeof(struct port100_tg_comm_rf_cmd));
+ hdr = (struct port100_tg_comm_rf_cmd *)skb->data;
+
+ memset(hdr, 0, sizeof(struct port100_tg_comm_rf_cmd));
+
+ hdr->guard_time = 0;
+ hdr->send_timeout = cpu_to_le16(0xFFFF);
+ hdr->mdaa = 1;
+ hdr->nfca_param[0] = (params->sens_res >> 8) & 0xFF;
+ hdr->nfca_param[1] = params->sens_res & 0xFF;
+ memcpy(hdr->nfca_param + 2, params->nfcid1, 3);
+ hdr->nfca_param[5] = params->sel_res;
+ memcpy(hdr->nfcf_param, params->nfcid2, 8);
+ hdr->nfcf_param[16] = (params->sc >> 8) & 0xFF;
+ hdr->nfcf_param[17] = params->sc & 0xFF;
+ hdr->recv_timeout = cpu_to_le16(timeout);
+
+ return port100_send_cmd_async(dev, PORT100_CMD_TG_COMM_RF, skb,
+ port100_tg_comm_rf_complete, cb_arg);
+}
+
+static int port100_listen(struct nfc_digital_dev *ddev, u16 timeout,
+ nfc_digital_cmd_complete_t cb, void *arg)
+{
+ struct port100 *dev = nfc_digital_get_drvdata(ddev);
+ struct sk_buff *skb;
+
+ skb = port100_alloc_skb(dev, 0);
+ if (!skb)
+ return -ENOMEM;
+
+ return port100_tg_send_cmd(ddev, skb, timeout, cb, arg);
+}
+
+static struct nfc_digital_ops port100_digital_ops = {
+ .in_configure_hw = port100_in_configure_hw,
+ .in_send_cmd = port100_in_send_cmd,
+
+ .tg_listen_mdaa = port100_listen_mdaa,
+ .tg_listen = port100_listen,
+ .tg_configure_hw = port100_tg_configure_hw,
+ .tg_send_cmd = port100_tg_send_cmd,
+
+ .switch_rf = port100_switch_rf,
+ .abort_cmd = port100_abort_cmd,
+};
+
+static const struct usb_device_id port100_table[] = {
+ { USB_DEVICE(SONY_VENDOR_ID, RCS380_PRODUCT_ID), },
+ { }
+};
+MODULE_DEVICE_TABLE(usb, port100_table);
+
+static int port100_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ struct port100 *dev;
+ int rc;
+ struct usb_host_interface *iface_desc;
+ struct usb_endpoint_descriptor *endpoint;
+ int in_endpoint;
+ int out_endpoint;
+ u16 fw_version;
+ u64 cmd_type_mask;
+ int i;
+
+ dev = devm_kzalloc(&interface->dev, sizeof(struct port100), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ mutex_init(&dev->out_urb_lock);
+ dev->udev = usb_get_dev(interface_to_usbdev(interface));
+ dev->interface = interface;
+ usb_set_intfdata(interface, dev);
+
+ in_endpoint = out_endpoint = 0;
+ iface_desc = interface->cur_altsetting;
+ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+ endpoint = &iface_desc->endpoint[i].desc;
+
+ if (!in_endpoint && usb_endpoint_is_bulk_in(endpoint))
+ in_endpoint = endpoint->bEndpointAddress;
+
+ if (!out_endpoint && usb_endpoint_is_bulk_out(endpoint))
+ out_endpoint = endpoint->bEndpointAddress;
+ }
+
+ if (!in_endpoint || !out_endpoint) {
+ nfc_err(&interface->dev,
+ "Could not find bulk-in or bulk-out endpoint\n");
+ rc = -ENODEV;
+ goto error;
+ }
+
+ dev->in_urb = usb_alloc_urb(0, GFP_KERNEL);
+ dev->out_urb = usb_alloc_urb(0, GFP_KERNEL);
+
+ if (!dev->in_urb || !dev->out_urb) {
+ nfc_err(&interface->dev, "Could not allocate USB URBs\n");
+ rc = -ENOMEM;
+ goto error;
+ }
+
+ usb_fill_bulk_urb(dev->in_urb, dev->udev,
+ usb_rcvbulkpipe(dev->udev, in_endpoint),
+ NULL, 0, NULL, dev);
+ usb_fill_bulk_urb(dev->out_urb, dev->udev,
+ usb_sndbulkpipe(dev->udev, out_endpoint),
+ NULL, 0, port100_send_complete, dev);
+
+ dev->skb_headroom = PORT100_FRAME_HEADER_LEN +
+ PORT100_COMM_RF_HEAD_MAX_LEN;
+ dev->skb_tailroom = PORT100_FRAME_TAIL_LEN;
+
+ INIT_WORK(&dev->cmd_complete_work, port100_wq_cmd_complete);
+
+ /* The first thing to do with the Port-100 is to set the command type
+ * to be used. If supported we use command type 1. 0 otherwise.
+ */
+ cmd_type_mask = port100_get_command_type_mask(dev);
+ if (!cmd_type_mask) {
+ nfc_err(&interface->dev,
+ "Could not get supported command types\n");
+ rc = -ENODEV;
+ goto error;
+ }
+
+ if (PORT100_CMD_TYPE_IS_SUPPORTED(cmd_type_mask, PORT100_CMD_TYPE_1))
+ dev->cmd_type = PORT100_CMD_TYPE_1;
+ else
+ dev->cmd_type = PORT100_CMD_TYPE_0;
+
+ rc = port100_set_command_type(dev, dev->cmd_type);
+ if (rc) {
+ nfc_err(&interface->dev,
+ "The device does not support command type %u\n",
+ dev->cmd_type);
+ goto error;
+ }
+
+ fw_version = port100_get_firmware_version(dev);
+ if (!fw_version)
+ nfc_err(&interface->dev,
+ "Could not get device firmware version\n");
+
+ nfc_info(&interface->dev,
+ "Sony NFC Port-100 Series attached (firmware v%x.%02x)\n",
+ (fw_version & 0xFF00) >> 8, fw_version & 0xFF);
+
+ dev->nfc_digital_dev = nfc_digital_allocate_device(&port100_digital_ops,
+ PORT100_PROTOCOLS,
+ PORT100_CAPABILITIES,
+ dev->skb_headroom,
+ dev->skb_tailroom);
+ if (!dev->nfc_digital_dev) {
+ nfc_err(&interface->dev,
+ "Could not allocate nfc_digital_dev\n");
+ rc = -ENOMEM;
+ goto error;
+ }
+
+ nfc_digital_set_parent_dev(dev->nfc_digital_dev, &interface->dev);
+ nfc_digital_set_drvdata(dev->nfc_digital_dev, dev);
+
+ rc = nfc_digital_register_device(dev->nfc_digital_dev);
+ if (rc) {
+ nfc_err(&interface->dev,
+ "Could not register digital device\n");
+ goto free_nfc_dev;
+ }
+
+ return 0;
+
+free_nfc_dev:
+ nfc_digital_free_device(dev->nfc_digital_dev);
+
+error:
+ usb_free_urb(dev->in_urb);
+ usb_free_urb(dev->out_urb);
+ usb_put_dev(dev->udev);
+
+ return rc;
+}
+
+static void port100_disconnect(struct usb_interface *interface)
+{
+ struct port100 *dev;
+
+ dev = usb_get_intfdata(interface);
+ usb_set_intfdata(interface, NULL);
+
+ nfc_digital_unregister_device(dev->nfc_digital_dev);
+ nfc_digital_free_device(dev->nfc_digital_dev);
+
+ usb_kill_urb(dev->in_urb);
+ usb_kill_urb(dev->out_urb);
+
+ usb_free_urb(dev->in_urb);
+ usb_free_urb(dev->out_urb);
+ usb_put_dev(dev->udev);
+
+ kfree(dev->cmd);
+
+ nfc_info(&interface->dev, "Sony Port-100 NFC device disconnected\n");
+}
+
+static struct usb_driver port100_driver = {
+ .name = "port100",
+ .probe = port100_probe,
+ .disconnect = port100_disconnect,
+ .id_table = port100_table,
+};
+
+module_usb_driver(port100_driver);
+
+MODULE_DESCRIPTION("NFC Port-100 series usb driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/nfc/s3fwrn5/Kconfig b/drivers/nfc/s3fwrn5/Kconfig
new file mode 100644
index 0000000..47e31db
--- /dev/null
+++ b/drivers/nfc/s3fwrn5/Kconfig
@@ -0,0 +1,22 @@
+config NFC_S3FWRN5
+ tristate
+ depends on m
+ depends on CRYPTO
+ ---help---
+ Core driver for Samsung S3FWRN5 NFC chip. Contains core utilities
+ of chip. It's intended to be used by PHYs to avoid duplicating lots
+ of common code.
+
+config NFC_S3FWRN5_I2C
+ tristate "Samsung S3FWRN5 I2C support"
+ depends on m
+ depends on NFC_NCI && I2C
+ select NFC_S3FWRN5
+ default n
+ ---help---
+ This module adds support for an I2C interface to the S3FWRN5 chip.
+ Select this if your platform is using the I2C bus.
+
+ To compile this driver as a module, choose m here. The module will
+ be called s3fwrn5_i2c.ko.
+ Say N if unsure.
diff --git a/drivers/nfc/s3fwrn5/Makefile b/drivers/nfc/s3fwrn5/Makefile
new file mode 100644
index 0000000..aa698d4
--- /dev/null
+++ b/drivers/nfc/s3fwrn5/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for Samsung S3FWRN5 NFC driver
+#
+
+s3fwrn5-objs = core.o firmware.o nci.o
+s3fwrn5_i2c-objs = i2c.o
+
+obj-$(CPTCFG_NFC_S3FWRN5) += s3fwrn5.o
+obj-$(CPTCFG_NFC_S3FWRN5_I2C) += s3fwrn5_i2c.o
diff --git a/drivers/nfc/s3fwrn5/core.c b/drivers/nfc/s3fwrn5/core.c
new file mode 100644
index 0000000..9d9c8d5
--- /dev/null
+++ b/drivers/nfc/s3fwrn5/core.c
@@ -0,0 +1,219 @@
+/*
+ * NCI based driver for Samsung S3FWRN5 NFC chip
+ *
+ * Copyright (C) 2015 Samsung Electrnoics
+ * Robert Baldyga <r.baldyga@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/module.h>
+#include <net/nfc/nci_core.h>
+
+#include "s3fwrn5.h"
+#include "firmware.h"
+#include "nci.h"
+
+#define S3FWRN5_NFC_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)
+
+static int s3fwrn5_firmware_update(struct s3fwrn5_info *info)
+{
+ bool need_update;
+ int ret;
+
+ s3fwrn5_fw_init(&info->fw_info, "sec_s3fwrn5_firmware.bin");
+
+ /* Update firmware */
+
+ s3fwrn5_set_wake(info, false);
+ s3fwrn5_set_mode(info, S3FWRN5_MODE_FW);
+
+ ret = s3fwrn5_fw_setup(&info->fw_info);
+ if (ret < 0)
+ return ret;
+
+ need_update = s3fwrn5_fw_check_version(&info->fw_info,
+ info->ndev->manufact_specific_info);
+ if (!need_update)
+ goto out;
+
+ dev_info(&info->ndev->nfc_dev->dev, "Detected new firmware version\n");
+
+ ret = s3fwrn5_fw_download(&info->fw_info);
+ if (ret < 0)
+ goto out;
+
+ /* Update RF configuration */
+
+ s3fwrn5_set_mode(info, S3FWRN5_MODE_NCI);
+
+ s3fwrn5_set_wake(info, true);
+ ret = s3fwrn5_nci_rf_configure(info, "sec_s3fwrn5_rfreg.bin");
+ s3fwrn5_set_wake(info, false);
+
+out:
+ s3fwrn5_set_mode(info, S3FWRN5_MODE_COLD);
+ s3fwrn5_fw_cleanup(&info->fw_info);
+ return ret;
+}
+
+static int s3fwrn5_nci_open(struct nci_dev *ndev)
+{
+ struct s3fwrn5_info *info = nci_get_drvdata(ndev);
+
+ if (s3fwrn5_get_mode(info) != S3FWRN5_MODE_COLD)
+ return -EBUSY;
+
+ s3fwrn5_set_mode(info, S3FWRN5_MODE_NCI);
+ s3fwrn5_set_wake(info, true);
+
+ return 0;
+}
+
+static int s3fwrn5_nci_close(struct nci_dev *ndev)
+{
+ struct s3fwrn5_info *info = nci_get_drvdata(ndev);
+
+ s3fwrn5_set_wake(info, false);
+ s3fwrn5_set_mode(info, S3FWRN5_MODE_COLD);
+
+ return 0;
+}
+
+static int s3fwrn5_nci_send(struct nci_dev *ndev, struct sk_buff *skb)
+{
+ struct s3fwrn5_info *info = nci_get_drvdata(ndev);
+ int ret;
+
+ mutex_lock(&info->mutex);
+
+ if (s3fwrn5_get_mode(info) != S3FWRN5_MODE_NCI) {
+ mutex_unlock(&info->mutex);
+ return -EINVAL;
+ }
+
+ ret = s3fwrn5_write(info, skb);
+ if (ret < 0)
+ kfree_skb(skb);
+
+ mutex_unlock(&info->mutex);
+ return ret;
+}
+
+static int s3fwrn5_nci_post_setup(struct nci_dev *ndev)
+{
+ struct s3fwrn5_info *info = nci_get_drvdata(ndev);
+ int ret;
+
+ ret = s3fwrn5_firmware_update(info);
+ if (ret < 0)
+ goto out;
+
+ /* NCI core reset */
+
+ s3fwrn5_set_mode(info, S3FWRN5_MODE_NCI);
+ s3fwrn5_set_wake(info, true);
+
+ ret = nci_core_reset(info->ndev);
+ if (ret < 0)
+ goto out;
+
+ ret = nci_core_init(info->ndev);
+
+out:
+ return ret;
+}
+
+static struct nci_ops s3fwrn5_nci_ops = {
+ .open = s3fwrn5_nci_open,
+ .close = s3fwrn5_nci_close,
+ .send = s3fwrn5_nci_send,
+ .post_setup = s3fwrn5_nci_post_setup,
+};
+
+int s3fwrn5_probe(struct nci_dev **ndev, void *phy_id, struct device *pdev,
+ const struct s3fwrn5_phy_ops *phy_ops, unsigned int max_payload)
+{
+ struct s3fwrn5_info *info;
+ int ret;
+
+ info = devm_kzalloc(pdev, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->phy_id = phy_id;
+ info->pdev = pdev;
+ info->phy_ops = phy_ops;
+ info->max_payload = max_payload;
+ mutex_init(&info->mutex);
+
+ s3fwrn5_set_mode(info, S3FWRN5_MODE_COLD);
+
+ s3fwrn5_nci_get_prop_ops(&s3fwrn5_nci_ops.prop_ops,
+ &s3fwrn5_nci_ops.n_prop_ops);
+
+ info->ndev = nci_allocate_device(&s3fwrn5_nci_ops,
+ S3FWRN5_NFC_PROTOCOLS, 0, 0);
+ if (!info->ndev)
+ return -ENOMEM;
+
+ nci_set_parent_dev(info->ndev, pdev);
+ nci_set_drvdata(info->ndev, info);
+
+ ret = nci_register_device(info->ndev);
+ if (ret < 0) {
+ nci_free_device(info->ndev);
+ return ret;
+ }
+
+ info->fw_info.ndev = info->ndev;
+
+ *ndev = info->ndev;
+
+ return ret;
+}
+EXPORT_SYMBOL(s3fwrn5_probe);
+
+void s3fwrn5_remove(struct nci_dev *ndev)
+{
+ struct s3fwrn5_info *info = nci_get_drvdata(ndev);
+
+ s3fwrn5_set_mode(info, S3FWRN5_MODE_COLD);
+
+ nci_unregister_device(ndev);
+ nci_free_device(ndev);
+}
+EXPORT_SYMBOL(s3fwrn5_remove);
+
+int s3fwrn5_recv_frame(struct nci_dev *ndev, struct sk_buff *skb,
+ enum s3fwrn5_mode mode)
+{
+ switch (mode) {
+ case S3FWRN5_MODE_NCI:
+ return nci_recv_frame(ndev, skb);
+ case S3FWRN5_MODE_FW:
+ return s3fwrn5_fw_recv_frame(ndev, skb);
+ default:
+ return -ENODEV;
+ }
+}
+EXPORT_SYMBOL(s3fwrn5_recv_frame);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Samsung S3FWRN5 NFC driver");
+MODULE_AUTHOR("Robert Baldyga <r.baldyga@samsung.com>");
diff --git a/drivers/nfc/s3fwrn5/firmware.c b/drivers/nfc/s3fwrn5/firmware.c
new file mode 100644
index 0000000..5f97da1
--- /dev/null
+++ b/drivers/nfc/s3fwrn5/firmware.c
@@ -0,0 +1,529 @@
+/*
+ * NCI based driver for Samsung S3FWRN5 NFC chip
+ *
+ * Copyright (C) 2015 Samsung Electrnoics
+ * Robert Baldyga <r.baldyga@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/completion.h>
+#include <linux/firmware.h>
+#include <crypto/hash.h>
+#include <crypto/sha.h>
+
+#include "s3fwrn5.h"
+#include "firmware.h"
+
+struct s3fwrn5_fw_version {
+ __u8 major;
+ __u8 build1;
+ __u8 build2;
+ __u8 target;
+};
+
+static int s3fwrn5_fw_send_msg(struct s3fwrn5_fw_info *fw_info,
+ struct sk_buff *msg, struct sk_buff **rsp)
+{
+ struct s3fwrn5_info *info =
+ container_of(fw_info, struct s3fwrn5_info, fw_info);
+ long ret;
+
+ reinit_completion(&fw_info->completion);
+
+ ret = s3fwrn5_write(info, msg);
+ if (ret < 0)
+ return ret;
+
+ ret = wait_for_completion_interruptible_timeout(
+ &fw_info->completion, msecs_to_jiffies(1000));
+ if (ret < 0)
+ return ret;
+ else if (ret == 0)
+ return -ENXIO;
+
+ if (!fw_info->rsp)
+ return -EINVAL;
+
+ *rsp = fw_info->rsp;
+ fw_info->rsp = NULL;
+
+ return 0;
+}
+
+static int s3fwrn5_fw_prep_msg(struct s3fwrn5_fw_info *fw_info,
+ struct sk_buff **msg, u8 type, u8 code, const void *data, u16 len)
+{
+ struct s3fwrn5_fw_header hdr;
+ struct sk_buff *skb;
+
+ hdr.type = type | fw_info->parity;
+ fw_info->parity ^= 0x80;
+ hdr.code = code;
+ hdr.len = len;
+
+ skb = alloc_skb(S3FWRN5_FW_HDR_SIZE + len, GFP_KERNEL);
+ if (!skb)
+ return -ENOMEM;
+
+ memcpy(skb_put(skb, S3FWRN5_FW_HDR_SIZE), &hdr, S3FWRN5_FW_HDR_SIZE);
+ if (len)
+ memcpy(skb_put(skb, len), data, len);
+
+ *msg = skb;
+
+ return 0;
+}
+
+static int s3fwrn5_fw_get_bootinfo(struct s3fwrn5_fw_info *fw_info,
+ struct s3fwrn5_fw_cmd_get_bootinfo_rsp *bootinfo)
+{
+ struct sk_buff *msg, *rsp = NULL;
+ struct s3fwrn5_fw_header *hdr;
+ int ret;
+
+ /* Send GET_BOOTINFO command */
+
+ ret = s3fwrn5_fw_prep_msg(fw_info, &msg, S3FWRN5_FW_MSG_CMD,
+ S3FWRN5_FW_CMD_GET_BOOTINFO, NULL, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = s3fwrn5_fw_send_msg(fw_info, msg, &rsp);
+ kfree_skb(msg);
+ if (ret < 0)
+ return ret;
+
+ hdr = (struct s3fwrn5_fw_header *) rsp->data;
+ if (hdr->code != S3FWRN5_FW_RET_SUCCESS) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ memcpy(bootinfo, rsp->data + S3FWRN5_FW_HDR_SIZE, 10);
+
+out:
+ kfree_skb(rsp);
+ return ret;
+}
+
+static int s3fwrn5_fw_enter_update_mode(struct s3fwrn5_fw_info *fw_info,
+ const void *hash_data, u16 hash_size,
+ const void *sig_data, u16 sig_size)
+{
+ struct s3fwrn5_fw_cmd_enter_updatemode args;
+ struct sk_buff *msg, *rsp = NULL;
+ struct s3fwrn5_fw_header *hdr;
+ int ret;
+
+ /* Send ENTER_UPDATE_MODE command */
+
+ args.hashcode_size = hash_size;
+ args.signature_size = sig_size;
+
+ ret = s3fwrn5_fw_prep_msg(fw_info, &msg, S3FWRN5_FW_MSG_CMD,
+ S3FWRN5_FW_CMD_ENTER_UPDATE_MODE, &args, sizeof(args));
+ if (ret < 0)
+ return ret;
+
+ ret = s3fwrn5_fw_send_msg(fw_info, msg, &rsp);
+ kfree_skb(msg);
+ if (ret < 0)
+ return ret;
+
+ hdr = (struct s3fwrn5_fw_header *) rsp->data;
+ if (hdr->code != S3FWRN5_FW_RET_SUCCESS) {
+ ret = -EPROTO;
+ goto out;
+ }
+
+ kfree_skb(rsp);
+
+ /* Send hashcode data */
+
+ ret = s3fwrn5_fw_prep_msg(fw_info, &msg, S3FWRN5_FW_MSG_DATA, 0,
+ hash_data, hash_size);
+ if (ret < 0)
+ return ret;
+
+ ret = s3fwrn5_fw_send_msg(fw_info, msg, &rsp);
+ kfree_skb(msg);
+ if (ret < 0)
+ return ret;
+
+ hdr = (struct s3fwrn5_fw_header *) rsp->data;
+ if (hdr->code != S3FWRN5_FW_RET_SUCCESS) {
+ ret = -EPROTO;
+ goto out;
+ }
+
+ kfree_skb(rsp);
+
+ /* Send signature data */
+
+ ret = s3fwrn5_fw_prep_msg(fw_info, &msg, S3FWRN5_FW_MSG_DATA, 0,
+ sig_data, sig_size);
+ if (ret < 0)
+ return ret;
+
+ ret = s3fwrn5_fw_send_msg(fw_info, msg, &rsp);
+ kfree_skb(msg);
+ if (ret < 0)
+ return ret;
+
+ hdr = (struct s3fwrn5_fw_header *) rsp->data;
+ if (hdr->code != S3FWRN5_FW_RET_SUCCESS)
+ ret = -EPROTO;
+
+out:
+ kfree_skb(rsp);
+ return ret;
+}
+
+static int s3fwrn5_fw_update_sector(struct s3fwrn5_fw_info *fw_info,
+ u32 base_addr, const void *data)
+{
+ struct s3fwrn5_fw_cmd_update_sector args;
+ struct sk_buff *msg, *rsp = NULL;
+ struct s3fwrn5_fw_header *hdr;
+ int ret, i;
+
+ /* Send UPDATE_SECTOR command */
+
+ args.base_address = base_addr;
+
+ ret = s3fwrn5_fw_prep_msg(fw_info, &msg, S3FWRN5_FW_MSG_CMD,
+ S3FWRN5_FW_CMD_UPDATE_SECTOR, &args, sizeof(args));
+ if (ret < 0)
+ return ret;
+
+ ret = s3fwrn5_fw_send_msg(fw_info, msg, &rsp);
+ kfree_skb(msg);
+ if (ret < 0)
+ return ret;
+
+ hdr = (struct s3fwrn5_fw_header *) rsp->data;
+ if (hdr->code != S3FWRN5_FW_RET_SUCCESS) {
+ ret = -EPROTO;
+ goto err;
+ }
+
+ kfree_skb(rsp);
+
+ /* Send data split into 256-byte packets */
+
+ for (i = 0; i < 16; ++i) {
+ ret = s3fwrn5_fw_prep_msg(fw_info, &msg,
+ S3FWRN5_FW_MSG_DATA, 0, data+256*i, 256);
+ if (ret < 0)
+ break;
+
+ ret = s3fwrn5_fw_send_msg(fw_info, msg, &rsp);
+ kfree_skb(msg);
+ if (ret < 0)
+ break;
+
+ hdr = (struct s3fwrn5_fw_header *) rsp->data;
+ if (hdr->code != S3FWRN5_FW_RET_SUCCESS) {
+ ret = -EPROTO;
+ goto err;
+ }
+
+ kfree_skb(rsp);
+ }
+
+ return ret;
+
+err:
+ kfree_skb(rsp);
+ return ret;
+}
+
+static int s3fwrn5_fw_complete_update_mode(struct s3fwrn5_fw_info *fw_info)
+{
+ struct sk_buff *msg, *rsp = NULL;
+ struct s3fwrn5_fw_header *hdr;
+ int ret;
+
+ /* Send COMPLETE_UPDATE_MODE command */
+
+ ret = s3fwrn5_fw_prep_msg(fw_info, &msg, S3FWRN5_FW_MSG_CMD,
+ S3FWRN5_FW_CMD_COMPLETE_UPDATE_MODE, NULL, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = s3fwrn5_fw_send_msg(fw_info, msg, &rsp);
+ kfree_skb(msg);
+ if (ret < 0)
+ return ret;
+
+ hdr = (struct s3fwrn5_fw_header *) rsp->data;
+ if (hdr->code != S3FWRN5_FW_RET_SUCCESS)
+ ret = -EPROTO;
+
+ kfree_skb(rsp);
+
+ return ret;
+}
+
+/*
+ * Firmware header stucture:
+ *
+ * 0x00 - 0x0B : Date and time string (w/o NUL termination)
+ * 0x10 - 0x13 : Firmware version
+ * 0x14 - 0x17 : Signature address
+ * 0x18 - 0x1B : Signature size
+ * 0x1C - 0x1F : Firmware image address
+ * 0x20 - 0x23 : Firmware sectors count
+ * 0x24 - 0x27 : Custom signature address
+ * 0x28 - 0x2B : Custom signature size
+ */
+
+#define S3FWRN5_FW_IMAGE_HEADER_SIZE 44
+
+static int s3fwrn5_fw_request_firmware(struct s3fwrn5_fw_info *fw_info)
+{
+ struct s3fwrn5_fw_image *fw = &fw_info->fw;
+ u32 sig_off;
+ u32 image_off;
+ u32 custom_sig_off;
+ int ret;
+
+ ret = request_firmware(&fw->fw, fw_info->fw_name,
+ &fw_info->ndev->nfc_dev->dev);
+ if (ret < 0)
+ return ret;
+
+ if (fw->fw->size < S3FWRN5_FW_IMAGE_HEADER_SIZE)
+ return -EINVAL;
+
+ memcpy(fw->date, fw->fw->data + 0x00, 12);
+ fw->date[12] = '\0';
+
+ memcpy(&fw->version, fw->fw->data + 0x10, 4);
+
+ memcpy(&sig_off, fw->fw->data + 0x14, 4);
+ fw->sig = fw->fw->data + sig_off;
+ memcpy(&fw->sig_size, fw->fw->data + 0x18, 4);
+
+ memcpy(&image_off, fw->fw->data + 0x1C, 4);
+ fw->image = fw->fw->data + image_off;
+ memcpy(&fw->image_sectors, fw->fw->data + 0x20, 4);
+
+ memcpy(&custom_sig_off, fw->fw->data + 0x24, 4);
+ fw->custom_sig = fw->fw->data + custom_sig_off;
+ memcpy(&fw->custom_sig_size, fw->fw->data + 0x28, 4);
+
+ return 0;
+}
+
+static void s3fwrn5_fw_release_firmware(struct s3fwrn5_fw_info *fw_info)
+{
+ release_firmware(fw_info->fw.fw);
+}
+
+static int s3fwrn5_fw_get_base_addr(
+ struct s3fwrn5_fw_cmd_get_bootinfo_rsp *bootinfo, u32 *base_addr)
+{
+ int i;
+ struct {
+ u8 version[4];
+ u32 base_addr;
+ } match[] = {
+ {{0x05, 0x00, 0x00, 0x00}, 0x00005000},
+ {{0x05, 0x00, 0x00, 0x01}, 0x00003000},
+ {{0x05, 0x00, 0x00, 0x02}, 0x00003000},
+ {{0x05, 0x00, 0x00, 0x03}, 0x00003000},
+ {{0x05, 0x00, 0x00, 0x05}, 0x00003000}
+ };
+
+ for (i = 0; i < ARRAY_SIZE(match); ++i)
+ if (bootinfo->hw_version[0] == match[i].version[0] &&
+ bootinfo->hw_version[1] == match[i].version[1] &&
+ bootinfo->hw_version[3] == match[i].version[3]) {
+ *base_addr = match[i].base_addr;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static inline bool
+s3fwrn5_fw_is_custom(struct s3fwrn5_fw_cmd_get_bootinfo_rsp *bootinfo)
+{
+ return !!bootinfo->hw_version[2];
+}
+
+int s3fwrn5_fw_setup(struct s3fwrn5_fw_info *fw_info)
+{
+ struct s3fwrn5_fw_cmd_get_bootinfo_rsp bootinfo;
+ int ret;
+
+ /* Get firmware data */
+
+ ret = s3fwrn5_fw_request_firmware(fw_info);
+ if (ret < 0) {
+ dev_err(&fw_info->ndev->nfc_dev->dev,
+ "Failed to get fw file, ret=%02x\n", ret);
+ return ret;
+ }
+
+ /* Get bootloader info */
+
+ ret = s3fwrn5_fw_get_bootinfo(fw_info, &bootinfo);
+ if (ret < 0) {
+ dev_err(&fw_info->ndev->nfc_dev->dev,
+ "Failed to get bootinfo, ret=%02x\n", ret);
+ goto err;
+ }
+
+ /* Match hardware version to obtain firmware base address */
+
+ ret = s3fwrn5_fw_get_base_addr(&bootinfo, &fw_info->base_addr);
+ if (ret < 0) {
+ dev_err(&fw_info->ndev->nfc_dev->dev,
+ "Unknown hardware version\n");
+ goto err;
+ }
+
+ fw_info->sector_size = bootinfo.sector_size;
+
+ fw_info->sig_size = s3fwrn5_fw_is_custom(&bootinfo) ?
+ fw_info->fw.custom_sig_size : fw_info->fw.sig_size;
+ fw_info->sig = s3fwrn5_fw_is_custom(&bootinfo) ?
+ fw_info->fw.custom_sig : fw_info->fw.sig;
+
+ return 0;
+
+err:
+ s3fwrn5_fw_release_firmware(fw_info);
+ return ret;
+}
+
+bool s3fwrn5_fw_check_version(struct s3fwrn5_fw_info *fw_info, u32 version)
+{
+ struct s3fwrn5_fw_version *new = (void *) &fw_info->fw.version;
+ struct s3fwrn5_fw_version *old = (void *) &version;
+
+ if (new->major > old->major)
+ return true;
+ if (new->build1 > old->build1)
+ return true;
+ if (new->build2 > old->build2)
+ return true;
+
+ return false;
+}
+
+int s3fwrn5_fw_download(struct s3fwrn5_fw_info *fw_info)
+{
+ struct s3fwrn5_fw_image *fw = &fw_info->fw;
+ u8 hash_data[SHA1_DIGEST_SIZE];
+ struct crypto_shash *tfm;
+ u32 image_size, off;
+ int ret;
+
+ image_size = fw_info->sector_size * fw->image_sectors;
+
+ /* Compute SHA of firmware data */
+
+ tfm = crypto_alloc_shash("sha1", 0, 0);
+ if (IS_ERR(tfm)) {
+ ret = PTR_ERR(tfm);
+ dev_err(&fw_info->ndev->nfc_dev->dev,
+ "Cannot allocate shash (code=%d)\n", ret);
+ goto out;
+ }
+
+ {
+ SHASH_DESC_ON_STACK(desc, tfm);
+
+ desc->tfm = tfm;
+ desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+
+ ret = crypto_shash_digest(desc, fw->image, image_size,
+ hash_data);
+ shash_desc_zero(desc);
+ }
+
+ crypto_free_shash(tfm);
+ if (ret) {
+ dev_err(&fw_info->ndev->nfc_dev->dev,
+ "Cannot compute hash (code=%d)\n", ret);
+ goto out;
+ }
+
+ /* Firmware update process */
+
+ dev_info(&fw_info->ndev->nfc_dev->dev,
+ "Firmware update: %s\n", fw_info->fw_name);
+
+ ret = s3fwrn5_fw_enter_update_mode(fw_info, hash_data,
+ SHA1_DIGEST_SIZE, fw_info->sig, fw_info->sig_size);
+ if (ret < 0) {
+ dev_err(&fw_info->ndev->nfc_dev->dev,
+ "Unable to enter update mode\n");
+ goto out;
+ }
+
+ for (off = 0; off < image_size; off += fw_info->sector_size) {
+ ret = s3fwrn5_fw_update_sector(fw_info,
+ fw_info->base_addr + off, fw->image + off);
+ if (ret < 0) {
+ dev_err(&fw_info->ndev->nfc_dev->dev,
+ "Firmware update error (code=%d)\n", ret);
+ goto out;
+ }
+ }
+
+ ret = s3fwrn5_fw_complete_update_mode(fw_info);
+ if (ret < 0) {
+ dev_err(&fw_info->ndev->nfc_dev->dev,
+ "Unable to complete update mode\n");
+ goto out;
+ }
+
+ dev_info(&fw_info->ndev->nfc_dev->dev,
+ "Firmware update: success\n");
+
+out:
+ return ret;
+}
+
+void s3fwrn5_fw_init(struct s3fwrn5_fw_info *fw_info, const char *fw_name)
+{
+ fw_info->parity = 0x00;
+ fw_info->rsp = NULL;
+ fw_info->fw.fw = NULL;
+ strcpy(fw_info->fw_name, fw_name);
+ init_completion(&fw_info->completion);
+}
+
+void s3fwrn5_fw_cleanup(struct s3fwrn5_fw_info *fw_info)
+{
+ s3fwrn5_fw_release_firmware(fw_info);
+}
+
+int s3fwrn5_fw_recv_frame(struct nci_dev *ndev, struct sk_buff *skb)
+{
+ struct s3fwrn5_info *info = nci_get_drvdata(ndev);
+ struct s3fwrn5_fw_info *fw_info = &info->fw_info;
+
+ BUG_ON(fw_info->rsp);
+
+ fw_info->rsp = skb;
+
+ complete(&fw_info->completion);
+
+ return 0;
+}
diff --git a/drivers/nfc/s3fwrn5/firmware.h b/drivers/nfc/s3fwrn5/firmware.h
new file mode 100644
index 0000000..1ec0647
--- /dev/null
+++ b/drivers/nfc/s3fwrn5/firmware.h
@@ -0,0 +1,111 @@
+/*
+ * NCI based driver for Samsung S3FWRN5 NFC chip
+ *
+ * Copyright (C) 2015 Samsung Electrnoics
+ * Robert Baldyga <r.baldyga@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LOCAL_S3FWRN5_FIRMWARE_H_
+#define __LOCAL_S3FWRN5_FIRMWARE_H_
+
+/* FW Message Types */
+#define S3FWRN5_FW_MSG_CMD 0x00
+#define S3FWRN5_FW_MSG_RSP 0x01
+#define S3FWRN5_FW_MSG_DATA 0x02
+
+/* FW Return Codes */
+#define S3FWRN5_FW_RET_SUCCESS 0x00
+#define S3FWRN5_FW_RET_MESSAGE_TYPE_INVALID 0x01
+#define S3FWRN5_FW_RET_COMMAND_INVALID 0x02
+#define S3FWRN5_FW_RET_PAGE_DATA_OVERFLOW 0x03
+#define S3FWRN5_FW_RET_SECT_DATA_OVERFLOW 0x04
+#define S3FWRN5_FW_RET_AUTHENTICATION_FAIL 0x05
+#define S3FWRN5_FW_RET_FLASH_OPERATION_FAIL 0x06
+#define S3FWRN5_FW_RET_ADDRESS_OUT_OF_RANGE 0x07
+#define S3FWRN5_FW_RET_PARAMETER_INVALID 0x08
+
+/* ---- FW Packet structures ---- */
+#define S3FWRN5_FW_HDR_SIZE 4
+
+struct s3fwrn5_fw_header {
+ __u8 type;
+ __u8 code;
+ __u16 len;
+};
+
+#define S3FWRN5_FW_CMD_RESET 0x00
+
+#define S3FWRN5_FW_CMD_GET_BOOTINFO 0x01
+
+struct s3fwrn5_fw_cmd_get_bootinfo_rsp {
+ __u8 hw_version[4];
+ __u16 sector_size;
+ __u16 page_size;
+ __u16 frame_max_size;
+ __u16 hw_buffer_size;
+};
+
+#define S3FWRN5_FW_CMD_ENTER_UPDATE_MODE 0x02
+
+struct s3fwrn5_fw_cmd_enter_updatemode {
+ __u16 hashcode_size;
+ __u16 signature_size;
+};
+
+#define S3FWRN5_FW_CMD_UPDATE_SECTOR 0x04
+
+struct s3fwrn5_fw_cmd_update_sector {
+ __u32 base_address;
+};
+
+#define S3FWRN5_FW_CMD_COMPLETE_UPDATE_MODE 0x05
+
+struct s3fwrn5_fw_image {
+ const struct firmware *fw;
+
+ char date[13];
+ u32 version;
+ const void *sig;
+ u32 sig_size;
+ const void *image;
+ u32 image_sectors;
+ const void *custom_sig;
+ u32 custom_sig_size;
+};
+
+struct s3fwrn5_fw_info {
+ struct nci_dev *ndev;
+ struct s3fwrn5_fw_image fw;
+ char fw_name[NFC_FIRMWARE_NAME_MAXSIZE + 1];
+
+ const void *sig;
+ u32 sig_size;
+ u32 sector_size;
+ u32 base_addr;
+
+ struct completion completion;
+ struct sk_buff *rsp;
+ char parity;
+};
+
+void s3fwrn5_fw_init(struct s3fwrn5_fw_info *fw_info, const char *fw_name);
+int s3fwrn5_fw_setup(struct s3fwrn5_fw_info *fw_info);
+bool s3fwrn5_fw_check_version(struct s3fwrn5_fw_info *fw_info, u32 version);
+int s3fwrn5_fw_download(struct s3fwrn5_fw_info *fw_info);
+void s3fwrn5_fw_cleanup(struct s3fwrn5_fw_info *fw_info);
+
+int s3fwrn5_fw_recv_frame(struct nci_dev *ndev, struct sk_buff *skb);
+
+#endif /* __LOCAL_S3FWRN5_FIRMWARE_H_ */
diff --git a/drivers/nfc/s3fwrn5/i2c.c b/drivers/nfc/s3fwrn5/i2c.c
new file mode 100644
index 0000000..3ed0adf
--- /dev/null
+++ b/drivers/nfc/s3fwrn5/i2c.c
@@ -0,0 +1,306 @@
+/*
+ * I2C Link Layer for Samsung S3FWRN5 NCI based Driver
+ *
+ * Copyright (C) 2015 Samsung Electrnoics
+ * Robert Baldyga <r.baldyga@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+#include <linux/module.h>
+
+#include <net/nfc/nfc.h>
+
+#include "s3fwrn5.h"
+
+#define S3FWRN5_I2C_DRIVER_NAME "s3fwrn5_i2c"
+
+#define S3FWRN5_I2C_MAX_PAYLOAD 32
+#define S3FWRN5_EN_WAIT_TIME 150
+
+struct s3fwrn5_i2c_phy {
+ struct i2c_client *i2c_dev;
+ struct nci_dev *ndev;
+
+ unsigned int gpio_en;
+ unsigned int gpio_fw_wake;
+
+ struct mutex mutex;
+
+ enum s3fwrn5_mode mode;
+ unsigned int irq_skip:1;
+};
+
+static void s3fwrn5_i2c_set_wake(void *phy_id, bool wake)
+{
+ struct s3fwrn5_i2c_phy *phy = phy_id;
+
+ mutex_lock(&phy->mutex);
+ gpio_set_value(phy->gpio_fw_wake, wake);
+ msleep(S3FWRN5_EN_WAIT_TIME/2);
+ mutex_unlock(&phy->mutex);
+}
+
+static void s3fwrn5_i2c_set_mode(void *phy_id, enum s3fwrn5_mode mode)
+{
+ struct s3fwrn5_i2c_phy *phy = phy_id;
+
+ mutex_lock(&phy->mutex);
+
+ if (phy->mode == mode)
+ goto out;
+
+ phy->mode = mode;
+
+ gpio_set_value(phy->gpio_en, 1);
+ gpio_set_value(phy->gpio_fw_wake, 0);
+ if (mode == S3FWRN5_MODE_FW)
+ gpio_set_value(phy->gpio_fw_wake, 1);
+
+ if (mode != S3FWRN5_MODE_COLD) {
+ msleep(S3FWRN5_EN_WAIT_TIME);
+ gpio_set_value(phy->gpio_en, 0);
+ msleep(S3FWRN5_EN_WAIT_TIME/2);
+ }
+
+ phy->irq_skip = true;
+
+out:
+ mutex_unlock(&phy->mutex);
+}
+
+static enum s3fwrn5_mode s3fwrn5_i2c_get_mode(void *phy_id)
+{
+ struct s3fwrn5_i2c_phy *phy = phy_id;
+ enum s3fwrn5_mode mode;
+
+ mutex_lock(&phy->mutex);
+
+ mode = phy->mode;
+
+ mutex_unlock(&phy->mutex);
+
+ return mode;
+}
+
+static int s3fwrn5_i2c_write(void *phy_id, struct sk_buff *skb)
+{
+ struct s3fwrn5_i2c_phy *phy = phy_id;
+ int ret;
+
+ mutex_lock(&phy->mutex);
+
+ phy->irq_skip = false;
+
+ ret = i2c_master_send(phy->i2c_dev, skb->data, skb->len);
+ if (ret == -EREMOTEIO) {
+ /* Retry, chip was in standby */
+ usleep_range(110000, 120000);
+ ret = i2c_master_send(phy->i2c_dev, skb->data, skb->len);
+ }
+
+ mutex_unlock(&phy->mutex);
+
+ if (ret < 0)
+ return ret;
+
+ if (ret != skb->len)
+ return -EREMOTEIO;
+
+ return 0;
+}
+
+static const struct s3fwrn5_phy_ops i2c_phy_ops = {
+ .set_wake = s3fwrn5_i2c_set_wake,
+ .set_mode = s3fwrn5_i2c_set_mode,
+ .get_mode = s3fwrn5_i2c_get_mode,
+ .write = s3fwrn5_i2c_write,
+};
+
+static int s3fwrn5_i2c_read(struct s3fwrn5_i2c_phy *phy)
+{
+ struct sk_buff *skb;
+ size_t hdr_size;
+ size_t data_len;
+ char hdr[4];
+ int ret;
+
+ hdr_size = (phy->mode == S3FWRN5_MODE_NCI) ?
+ NCI_CTRL_HDR_SIZE : S3FWRN5_FW_HDR_SIZE;
+ ret = i2c_master_recv(phy->i2c_dev, hdr, hdr_size);
+ if (ret < 0)
+ return ret;
+
+ if (ret < hdr_size)
+ return -EBADMSG;
+
+ data_len = (phy->mode == S3FWRN5_MODE_NCI) ?
+ ((struct nci_ctrl_hdr *)hdr)->plen :
+ ((struct s3fwrn5_fw_header *)hdr)->len;
+
+ skb = alloc_skb(hdr_size + data_len, GFP_KERNEL);
+ if (!skb)
+ return -ENOMEM;
+
+ memcpy(skb_put(skb, hdr_size), hdr, hdr_size);
+
+ if (data_len == 0)
+ goto out;
+
+ ret = i2c_master_recv(phy->i2c_dev, skb_put(skb, data_len), data_len);
+ if (ret != data_len) {
+ kfree_skb(skb);
+ return -EBADMSG;
+ }
+
+out:
+ return s3fwrn5_recv_frame(phy->ndev, skb, phy->mode);
+}
+
+static irqreturn_t s3fwrn5_i2c_irq_thread_fn(int irq, void *phy_id)
+{
+ struct s3fwrn5_i2c_phy *phy = phy_id;
+ int ret = 0;
+
+ if (!phy || !phy->ndev) {
+ WARN_ON_ONCE(1);
+ return IRQ_NONE;
+ }
+
+ mutex_lock(&phy->mutex);
+
+ if (phy->irq_skip)
+ goto out;
+
+ switch (phy->mode) {
+ case S3FWRN5_MODE_NCI:
+ case S3FWRN5_MODE_FW:
+ ret = s3fwrn5_i2c_read(phy);
+ break;
+ case S3FWRN5_MODE_COLD:
+ ret = -EREMOTEIO;
+ break;
+ }
+
+out:
+ mutex_unlock(&phy->mutex);
+
+ return IRQ_HANDLED;
+}
+
+static int s3fwrn5_i2c_parse_dt(struct i2c_client *client)
+{
+ struct s3fwrn5_i2c_phy *phy = i2c_get_clientdata(client);
+ struct device_node *np = client->dev.of_node;
+
+ if (!np)
+ return -ENODEV;
+
+ phy->gpio_en = of_get_named_gpio(np, "s3fwrn5,en-gpios", 0);
+ if (!gpio_is_valid(phy->gpio_en))
+ return -ENODEV;
+
+ phy->gpio_fw_wake = of_get_named_gpio(np, "s3fwrn5,fw-gpios", 0);
+ if (!gpio_is_valid(phy->gpio_fw_wake))
+ return -ENODEV;
+
+ return 0;
+}
+
+static int s3fwrn5_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct s3fwrn5_i2c_phy *phy;
+ int ret;
+
+ phy = devm_kzalloc(&client->dev, sizeof(*phy), GFP_KERNEL);
+ if (!phy)
+ return -ENOMEM;
+
+ mutex_init(&phy->mutex);
+ phy->mode = S3FWRN5_MODE_COLD;
+ phy->irq_skip = true;
+
+ phy->i2c_dev = client;
+ i2c_set_clientdata(client, phy);
+
+ ret = s3fwrn5_i2c_parse_dt(client);
+ if (ret < 0)
+ return ret;
+
+ ret = devm_gpio_request_one(&phy->i2c_dev->dev, phy->gpio_en,
+ GPIOF_OUT_INIT_HIGH, "s3fwrn5_en");
+ if (ret < 0)
+ return ret;
+
+ ret = devm_gpio_request_one(&phy->i2c_dev->dev, phy->gpio_fw_wake,
+ GPIOF_OUT_INIT_LOW, "s3fwrn5_fw_wake");
+ if (ret < 0)
+ return ret;
+
+ ret = s3fwrn5_probe(&phy->ndev, phy, &phy->i2c_dev->dev, &i2c_phy_ops,
+ S3FWRN5_I2C_MAX_PAYLOAD);
+ if (ret < 0)
+ return ret;
+
+ ret = devm_request_threaded_irq(&client->dev, phy->i2c_dev->irq, NULL,
+ s3fwrn5_i2c_irq_thread_fn, IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ S3FWRN5_I2C_DRIVER_NAME, phy);
+ if (ret)
+ s3fwrn5_remove(phy->ndev);
+
+ return ret;
+}
+
+static int s3fwrn5_i2c_remove(struct i2c_client *client)
+{
+ struct s3fwrn5_i2c_phy *phy = i2c_get_clientdata(client);
+
+ s3fwrn5_remove(phy->ndev);
+
+ return 0;
+}
+
+static struct i2c_device_id s3fwrn5_i2c_id_table[] = {
+ {S3FWRN5_I2C_DRIVER_NAME, 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, s3fwrn5_i2c_id_table);
+
+static const struct of_device_id of_s3fwrn5_i2c_match[] = {
+ { .compatible = "samsung,s3fwrn5-i2c", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, of_s3fwrn5_i2c_match);
+
+static struct i2c_driver s3fwrn5_i2c_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = S3FWRN5_I2C_DRIVER_NAME,
+ .of_match_table = of_match_ptr(of_s3fwrn5_i2c_match),
+ },
+ .probe = s3fwrn5_i2c_probe,
+ .remove = s3fwrn5_i2c_remove,
+ .id_table = s3fwrn5_i2c_id_table,
+};
+
+module_i2c_driver(s3fwrn5_i2c_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("I2C driver for Samsung S3FWRN5");
+MODULE_AUTHOR("Robert Baldyga <r.baldyga@samsung.com>");
diff --git a/drivers/nfc/s3fwrn5/nci.c b/drivers/nfc/s3fwrn5/nci.c
new file mode 100644
index 0000000..075e4e8
--- /dev/null
+++ b/drivers/nfc/s3fwrn5/nci.c
@@ -0,0 +1,165 @@
+/*
+ * NCI based driver for Samsung S3FWRN5 NFC chip
+ *
+ * Copyright (C) 2015 Samsung Electrnoics
+ * Robert Baldyga <r.baldyga@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/completion.h>
+#include <linux/firmware.h>
+
+#include "s3fwrn5.h"
+#include "nci.h"
+
+static int s3fwrn5_nci_prop_rsp(struct nci_dev *ndev, struct sk_buff *skb)
+{
+ __u8 status = skb->data[0];
+
+ nci_req_complete(ndev, status);
+ return 0;
+}
+
+static struct nci_driver_ops s3fwrn5_nci_prop_ops[] = {
+ {
+ .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY,
+ NCI_PROP_AGAIN),
+ .rsp = s3fwrn5_nci_prop_rsp,
+ },
+ {
+ .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY,
+ NCI_PROP_GET_RFREG),
+ .rsp = s3fwrn5_nci_prop_rsp,
+ },
+ {
+ .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY,
+ NCI_PROP_SET_RFREG),
+ .rsp = s3fwrn5_nci_prop_rsp,
+ },
+ {
+ .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY,
+ NCI_PROP_GET_RFREG_VER),
+ .rsp = s3fwrn5_nci_prop_rsp,
+ },
+ {
+ .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY,
+ NCI_PROP_SET_RFREG_VER),
+ .rsp = s3fwrn5_nci_prop_rsp,
+ },
+ {
+ .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY,
+ NCI_PROP_START_RFREG),
+ .rsp = s3fwrn5_nci_prop_rsp,
+ },
+ {
+ .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY,
+ NCI_PROP_STOP_RFREG),
+ .rsp = s3fwrn5_nci_prop_rsp,
+ },
+ {
+ .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY,
+ NCI_PROP_FW_CFG),
+ .rsp = s3fwrn5_nci_prop_rsp,
+ },
+ {
+ .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY,
+ NCI_PROP_WR_RESET),
+ .rsp = s3fwrn5_nci_prop_rsp,
+ },
+};
+
+void s3fwrn5_nci_get_prop_ops(struct nci_driver_ops **ops, size_t *n)
+{
+ *ops = s3fwrn5_nci_prop_ops;
+ *n = ARRAY_SIZE(s3fwrn5_nci_prop_ops);
+}
+
+#define S3FWRN5_RFREG_SECTION_SIZE 252
+
+int s3fwrn5_nci_rf_configure(struct s3fwrn5_info *info, const char *fw_name)
+{
+ const struct firmware *fw;
+ struct nci_prop_fw_cfg_cmd fw_cfg;
+ struct nci_prop_set_rfreg_cmd set_rfreg;
+ struct nci_prop_stop_rfreg_cmd stop_rfreg;
+ u32 checksum;
+ int i, len;
+ int ret;
+
+ ret = request_firmware(&fw, fw_name, &info->ndev->nfc_dev->dev);
+ if (ret < 0)
+ return ret;
+
+ /* Compute rfreg checksum */
+
+ checksum = 0;
+ for (i = 0; i < fw->size; i += 4)
+ checksum += *((u32 *)(fw->data+i));
+
+ /* Set default clock configuration for external crystal */
+
+ fw_cfg.clk_type = 0x01;
+ fw_cfg.clk_speed = 0xff;
+ fw_cfg.clk_req = 0xff;
+ ret = nci_prop_cmd(info->ndev, NCI_PROP_FW_CFG,
+ sizeof(fw_cfg), (__u8 *)&fw_cfg);
+ if (ret < 0)
+ goto out;
+
+ /* Start rfreg configuration */
+
+ dev_info(&info->ndev->nfc_dev->dev,
+ "rfreg configuration update: %s\n", fw_name);
+
+ ret = nci_prop_cmd(info->ndev, NCI_PROP_START_RFREG, 0, NULL);
+ if (ret < 0) {
+ dev_err(&info->ndev->nfc_dev->dev,
+ "Unable to start rfreg update\n");
+ goto out;
+ }
+
+ /* Update rfreg */
+
+ set_rfreg.index = 0;
+ for (i = 0; i < fw->size; i += S3FWRN5_RFREG_SECTION_SIZE) {
+ len = (fw->size - i < S3FWRN5_RFREG_SECTION_SIZE) ?
+ (fw->size - i) : S3FWRN5_RFREG_SECTION_SIZE;
+ memcpy(set_rfreg.data, fw->data+i, len);
+ ret = nci_prop_cmd(info->ndev, NCI_PROP_SET_RFREG,
+ len+1, (__u8 *)&set_rfreg);
+ if (ret < 0) {
+ dev_err(&info->ndev->nfc_dev->dev,
+ "rfreg update error (code=%d)\n", ret);
+ goto out;
+ }
+ set_rfreg.index++;
+ }
+
+ /* Finish rfreg configuration */
+
+ stop_rfreg.checksum = checksum & 0xffff;
+ ret = nci_prop_cmd(info->ndev, NCI_PROP_STOP_RFREG,
+ sizeof(stop_rfreg), (__u8 *)&stop_rfreg);
+ if (ret < 0) {
+ dev_err(&info->ndev->nfc_dev->dev,
+ "Unable to stop rfreg update\n");
+ goto out;
+ }
+
+ dev_info(&info->ndev->nfc_dev->dev,
+ "rfreg configuration update: success\n");
+out:
+ release_firmware(fw);
+ return ret;
+}
diff --git a/drivers/nfc/s3fwrn5/nci.h b/drivers/nfc/s3fwrn5/nci.h
new file mode 100644
index 0000000..60c7fb5
--- /dev/null
+++ b/drivers/nfc/s3fwrn5/nci.h
@@ -0,0 +1,89 @@
+/*
+ * NCI based driver for Samsung S3FWRN5 NFC chip
+ *
+ * Copyright (C) 2015 Samsung Electrnoics
+ * Robert Baldyga <r.baldyga@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LOCAL_S3FWRN5_NCI_H_
+#define __LOCAL_S3FWRN5_NCI_H_
+
+#include "s3fwrn5.h"
+
+#define NCI_PROP_AGAIN 0x01
+
+#define NCI_PROP_GET_RFREG 0x21
+#define NCI_PROP_SET_RFREG 0x22
+
+struct nci_prop_set_rfreg_cmd {
+ __u8 index;
+ __u8 data[252];
+};
+
+struct nci_prop_set_rfreg_rsp {
+ __u8 status;
+};
+
+#define NCI_PROP_GET_RFREG_VER 0x24
+
+struct nci_prop_get_rfreg_ver_rsp {
+ __u8 status;
+ __u8 data[8];
+};
+
+#define NCI_PROP_SET_RFREG_VER 0x25
+
+struct nci_prop_set_rfreg_ver_cmd {
+ __u8 data[8];
+};
+
+struct nci_prop_set_rfreg_ver_rsp {
+ __u8 status;
+};
+
+#define NCI_PROP_START_RFREG 0x26
+
+struct nci_prop_start_rfreg_rsp {
+ __u8 status;
+};
+
+#define NCI_PROP_STOP_RFREG 0x27
+
+struct nci_prop_stop_rfreg_cmd {
+ __u16 checksum;
+};
+
+struct nci_prop_stop_rfreg_rsp {
+ __u8 status;
+};
+
+#define NCI_PROP_FW_CFG 0x28
+
+struct nci_prop_fw_cfg_cmd {
+ __u8 clk_type;
+ __u8 clk_speed;
+ __u8 clk_req;
+};
+
+struct nci_prop_fw_cfg_rsp {
+ __u8 status;
+};
+
+#define NCI_PROP_WR_RESET 0x2f
+
+void s3fwrn5_nci_get_prop_ops(struct nci_driver_ops **ops, size_t *n);
+int s3fwrn5_nci_rf_configure(struct s3fwrn5_info *info, const char *fw_name);
+
+#endif /* __LOCAL_S3FWRN5_NCI_H_ */
diff --git a/drivers/nfc/s3fwrn5/s3fwrn5.h b/drivers/nfc/s3fwrn5/s3fwrn5.h
new file mode 100644
index 0000000..7d5e516
--- /dev/null
+++ b/drivers/nfc/s3fwrn5/s3fwrn5.h
@@ -0,0 +1,99 @@
+/*
+ * NCI based driver for Samsung S3FWRN5 NFC chip
+ *
+ * Copyright (C) 2015 Samsung Electrnoics
+ * Robert Baldyga <r.baldyga@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LOCAL_S3FWRN5_H_
+#define __LOCAL_S3FWRN5_H_
+
+#include <linux/nfc.h>
+
+#include <net/nfc/nci_core.h>
+
+#include "firmware.h"
+
+enum s3fwrn5_mode {
+ S3FWRN5_MODE_COLD,
+ S3FWRN5_MODE_NCI,
+ S3FWRN5_MODE_FW,
+};
+
+struct s3fwrn5_phy_ops {
+ void (*set_wake)(void *id, bool sleep);
+ void (*set_mode)(void *id, enum s3fwrn5_mode);
+ enum s3fwrn5_mode (*get_mode)(void *id);
+ int (*write)(void *id, struct sk_buff *skb);
+};
+
+struct s3fwrn5_info {
+ struct nci_dev *ndev;
+ void *phy_id;
+ struct device *pdev;
+
+ const struct s3fwrn5_phy_ops *phy_ops;
+ unsigned int max_payload;
+
+ struct s3fwrn5_fw_info fw_info;
+
+ struct mutex mutex;
+};
+
+static inline int s3fwrn5_set_mode(struct s3fwrn5_info *info,
+ enum s3fwrn5_mode mode)
+{
+ if (!info->phy_ops->set_mode)
+ return -ENOTSUPP;
+
+ info->phy_ops->set_mode(info->phy_id, mode);
+
+ return 0;
+}
+
+static inline enum s3fwrn5_mode s3fwrn5_get_mode(struct s3fwrn5_info *info)
+{
+ if (!info->phy_ops->get_mode)
+ return -ENOTSUPP;
+
+ return info->phy_ops->get_mode(info->phy_id);
+}
+
+static inline int s3fwrn5_set_wake(struct s3fwrn5_info *info, bool wake)
+{
+ if (!info->phy_ops->set_wake)
+ return -ENOTSUPP;
+
+ info->phy_ops->set_wake(info->phy_id, wake);
+
+ return 0;
+}
+
+static inline int s3fwrn5_write(struct s3fwrn5_info *info, struct sk_buff *skb)
+{
+ if (!info->phy_ops->write)
+ return -ENOTSUPP;
+
+ return info->phy_ops->write(info->phy_id, skb);
+}
+
+int s3fwrn5_probe(struct nci_dev **ndev, void *phy_id, struct device *pdev,
+ const struct s3fwrn5_phy_ops *phy_ops, unsigned int max_payload);
+void s3fwrn5_remove(struct nci_dev *ndev);
+
+int s3fwrn5_recv_frame(struct nci_dev *ndev, struct sk_buff *skb,
+ enum s3fwrn5_mode mode);
+
+#endif /* __LOCAL_S3FWRN5_H_ */
diff --git a/drivers/nfc/st-nci/Kconfig b/drivers/nfc/st-nci/Kconfig
new file mode 100644
index 0000000..84d1da2
--- /dev/null
+++ b/drivers/nfc/st-nci/Kconfig
@@ -0,0 +1,35 @@
+config NFC_ST_NCI
+ tristate
+ depends on m
+ ---help---
+ STMicroelectronics NFC NCI chips core driver. It implements the chipset
+ NCI logic and hooks into the NFC kernel APIs. Physical layers will
+ register against it.
+
+config NFC_ST_NCI_I2C
+ depends on !KERNEL_3_18
+ tristate "STMicroelectronics ST NCI NFC driver (I2C)"
+ depends on m
+ depends on NFC_NCI && I2C
+ select NFC_ST_NCI
+ ---help---
+ This module adds support for an I2C interface to the
+ STMicroelectronics NFC NCI chips familly.
+ Select this if your platform is using the i2c bus.
+
+ If you choose to build a module, it'll be called st-nci_i2c.
+ Say N if unsure.
+
+config NFC_ST_NCI_SPI
+ depends on !KERNEL_3_18
+ tristate "STMicroelectronics ST NCI NFC driver (SPI)"
+ depends on m
+ depends on NFC_NCI && SPI
+ select NFC_ST_NCI
+ ---help---
+ This module adds support for an SPI interface to the
+ STMicroelectronics NFC NCI chips familly.
+ Select this if your platform is using the spi bus.
+
+ If you choose to build a module, it'll be called st-nci_spi.
+ Say N if unsure.
diff --git a/drivers/nfc/st-nci/Makefile b/drivers/nfc/st-nci/Makefile
new file mode 100644
index 0000000..c354d64
--- /dev/null
+++ b/drivers/nfc/st-nci/Makefile
@@ -0,0 +1,12 @@
+#
+# Makefile for ST_NCI NCI based NFC driver
+#
+
+st-nci-objs = ndlc.o core.o se.o vendor_cmds.o
+obj-$(CPTCFG_NFC_ST_NCI) += st-nci.o
+
+st-nci_i2c-objs = i2c.o
+obj-$(CPTCFG_NFC_ST_NCI_I2C) += st-nci_i2c.o
+
+st-nci_spi-objs = spi.o
+obj-$(CPTCFG_NFC_ST_NCI_SPI) += st-nci_spi.o
diff --git a/drivers/nfc/st-nci/core.c b/drivers/nfc/st-nci/core.c
new file mode 100644
index 0000000..c693128
--- /dev/null
+++ b/drivers/nfc/st-nci/core.c
@@ -0,0 +1,187 @@
+/*
+ * NCI based Driver for STMicroelectronics NFC Chip
+ *
+ * Copyright (C) 2014-2015 STMicroelectronics SAS. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/module.h>
+#include <linux/nfc.h>
+#include <net/nfc/nci.h>
+#include <net/nfc/nci_core.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+
+#include "st-nci.h"
+
+#define DRIVER_DESC "NCI NFC driver for ST_NCI"
+
+#define ST_NCI1_X_PROPRIETARY_ISO15693 0x83
+
+static int st_nci_init(struct nci_dev *ndev)
+{
+ struct nci_mode_set_cmd cmd;
+
+ cmd.cmd_type = ST_NCI_SET_NFC_MODE;
+ cmd.mode = 1;
+
+ return nci_prop_cmd(ndev, ST_NCI_CORE_PROP,
+ sizeof(struct nci_mode_set_cmd), (__u8 *)&cmd);
+}
+
+static int st_nci_open(struct nci_dev *ndev)
+{
+ struct st_nci_info *info = nci_get_drvdata(ndev);
+ int r;
+
+ if (test_and_set_bit(ST_NCI_RUNNING, &info->flags))
+ return 0;
+
+ r = ndlc_open(info->ndlc);
+ if (r)
+ clear_bit(ST_NCI_RUNNING, &info->flags);
+
+ return r;
+}
+
+static int st_nci_close(struct nci_dev *ndev)
+{
+ struct st_nci_info *info = nci_get_drvdata(ndev);
+
+ if (!test_bit(ST_NCI_RUNNING, &info->flags))
+ return 0;
+
+ ndlc_close(info->ndlc);
+
+ clear_bit(ST_NCI_RUNNING, &info->flags);
+
+ return 0;
+}
+
+static int st_nci_send(struct nci_dev *ndev, struct sk_buff *skb)
+{
+ struct st_nci_info *info = nci_get_drvdata(ndev);
+
+ skb->dev = (void *)ndev;
+
+ if (!test_bit(ST_NCI_RUNNING, &info->flags))
+ return -EBUSY;
+
+ return ndlc_send(info->ndlc, skb);
+}
+
+static __u32 st_nci_get_rfprotocol(struct nci_dev *ndev,
+ __u8 rf_protocol)
+{
+ return rf_protocol == ST_NCI1_X_PROPRIETARY_ISO15693 ?
+ NFC_PROTO_ISO15693_MASK : 0;
+}
+
+static int st_nci_prop_rsp_packet(struct nci_dev *ndev,
+ struct sk_buff *skb)
+{
+ __u8 status = skb->data[0];
+
+ nci_req_complete(ndev, status);
+ return 0;
+}
+
+static struct nci_driver_ops st_nci_prop_ops[] = {
+ {
+ .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY,
+ ST_NCI_CORE_PROP),
+ .rsp = st_nci_prop_rsp_packet,
+ },
+};
+
+static struct nci_ops st_nci_ops = {
+ .init = st_nci_init,
+ .open = st_nci_open,
+ .close = st_nci_close,
+ .send = st_nci_send,
+ .get_rfprotocol = st_nci_get_rfprotocol,
+ .discover_se = st_nci_discover_se,
+ .enable_se = st_nci_enable_se,
+ .disable_se = st_nci_disable_se,
+ .se_io = st_nci_se_io,
+ .hci_load_session = st_nci_hci_load_session,
+ .hci_event_received = st_nci_hci_event_received,
+ .hci_cmd_received = st_nci_hci_cmd_received,
+ .prop_ops = st_nci_prop_ops,
+ .n_prop_ops = ARRAY_SIZE(st_nci_prop_ops),
+};
+
+int st_nci_probe(struct llt_ndlc *ndlc, int phy_headroom,
+ int phy_tailroom, struct st_nci_se_status *se_status)
+{
+ struct st_nci_info *info;
+ int r;
+ u32 protocols;
+
+ info = devm_kzalloc(ndlc->dev,
+ sizeof(struct st_nci_info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ 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;
+
+ ndlc->ndev = nci_allocate_device(&st_nci_ops, protocols,
+ phy_headroom, phy_tailroom);
+ if (!ndlc->ndev) {
+ pr_err("Cannot allocate nfc ndev\n");
+ return -ENOMEM;
+ }
+ info->ndlc = ndlc;
+
+ nci_set_drvdata(ndlc->ndev, info);
+
+ r = st_nci_vendor_cmds_init(ndlc->ndev);
+ if (r) {
+ pr_err("Cannot register proprietary vendor cmds\n");
+ goto err_reg_dev;
+ }
+
+ r = nci_register_device(ndlc->ndev);
+ if (r) {
+ pr_err("Cannot register nfc device to nci core\n");
+ goto err_reg_dev;
+ }
+
+ return st_nci_se_init(ndlc->ndev, se_status);
+
+err_reg_dev:
+ nci_free_device(ndlc->ndev);
+ return r;
+}
+EXPORT_SYMBOL_GPL(st_nci_probe);
+
+void st_nci_remove(struct nci_dev *ndev)
+{
+ struct st_nci_info *info = nci_get_drvdata(ndev);
+
+ ndlc_close(info->ndlc);
+
+ nci_unregister_device(ndev);
+ nci_free_device(ndev);
+}
+EXPORT_SYMBOL_GPL(st_nci_remove);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/nfc/st-nci/i2c.c b/drivers/nfc/st-nci/i2c.c
new file mode 100644
index 0000000..9dfae0e
--- /dev/null
+++ b/drivers/nfc/st-nci/i2c.c
@@ -0,0 +1,428 @@
+/*
+ * I2C Link Layer for ST NCI NFC controller familly based Driver
+ * Copyright (C) 2014-2015 STMicroelectronics SAS. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of_irq.h>
+#include <linux/of_gpio.h>
+#include <linux/acpi.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/nfc.h>
+#include <linux/platform_data/st-nci.h>
+
+#include "st-nci.h"
+
+#define DRIVER_DESC "NCI NFC driver for ST_NCI"
+
+/* ndlc header */
+#define ST_NCI_FRAME_HEADROOM 1
+#define ST_NCI_FRAME_TAILROOM 0
+
+#define ST_NCI_I2C_MIN_SIZE 4 /* PCB(1) + NCI Packet header(3) */
+#define ST_NCI_I2C_MAX_SIZE 250 /* req 4.2.1 */
+
+#define ST_NCI_I2C_DRIVER_NAME "st_nci_i2c"
+
+#define ST_NCI_GPIO_NAME_RESET "reset"
+
+struct st_nci_i2c_phy {
+ struct i2c_client *i2c_dev;
+ struct llt_ndlc *ndlc;
+
+ bool irq_active;
+
+ unsigned int gpio_reset;
+ unsigned int irq_polarity;
+
+ struct st_nci_se_status se_status;
+};
+
+static int st_nci_i2c_enable(void *phy_id)
+{
+ struct st_nci_i2c_phy *phy = phy_id;
+
+ gpio_set_value(phy->gpio_reset, 0);
+ usleep_range(10000, 15000);
+ gpio_set_value(phy->gpio_reset, 1);
+ usleep_range(80000, 85000);
+
+ if (phy->ndlc->powered == 0 && phy->irq_active == 0) {
+ enable_irq(phy->i2c_dev->irq);
+ phy->irq_active = true;
+ }
+
+ return 0;
+}
+
+static void st_nci_i2c_disable(void *phy_id)
+{
+ struct st_nci_i2c_phy *phy = phy_id;
+
+ disable_irq_nosync(phy->i2c_dev->irq);
+ phy->irq_active = false;
+}
+
+/*
+ * Writing a frame must not return the number of written bytes.
+ * It must return either zero for success, or <0 for error.
+ * In addition, it must not alter the skb
+ */
+static int st_nci_i2c_write(void *phy_id, struct sk_buff *skb)
+{
+ int r = -1;
+ struct st_nci_i2c_phy *phy = phy_id;
+ struct i2c_client *client = phy->i2c_dev;
+
+ if (phy->ndlc->hard_fault != 0)
+ return phy->ndlc->hard_fault;
+
+ r = i2c_master_send(client, skb->data, skb->len);
+ if (r < 0) { /* Retry, chip was in standby */
+ usleep_range(1000, 4000);
+ r = i2c_master_send(client, skb->data, skb->len);
+ }
+
+ if (r >= 0) {
+ if (r != skb->len)
+ r = -EREMOTEIO;
+ else
+ r = 0;
+ }
+
+ return r;
+}
+
+/*
+ * Reads an ndlc frame and returns it in a newly allocated sk_buff.
+ * returns:
+ * 0 : if received frame is complete
+ * -EREMOTEIO : i2c read error (fatal)
+ * -EBADMSG : frame was incorrect and discarded
+ * -ENOMEM : cannot allocate skb, frame dropped
+ */
+static int st_nci_i2c_read(struct st_nci_i2c_phy *phy,
+ struct sk_buff **skb)
+{
+ int r;
+ u8 len;
+ u8 buf[ST_NCI_I2C_MAX_SIZE];
+ struct i2c_client *client = phy->i2c_dev;
+
+ r = i2c_master_recv(client, buf, ST_NCI_I2C_MIN_SIZE);
+ if (r < 0) { /* Retry, chip was in standby */
+ usleep_range(1000, 4000);
+ r = i2c_master_recv(client, buf, ST_NCI_I2C_MIN_SIZE);
+ }
+
+ if (r != ST_NCI_I2C_MIN_SIZE)
+ return -EREMOTEIO;
+
+ len = be16_to_cpu(*(__be16 *) (buf + 2));
+ if (len > ST_NCI_I2C_MAX_SIZE) {
+ nfc_err(&client->dev, "invalid frame len\n");
+ return -EBADMSG;
+ }
+
+ *skb = alloc_skb(ST_NCI_I2C_MIN_SIZE + len, GFP_KERNEL);
+ if (*skb == NULL)
+ return -ENOMEM;
+
+ skb_reserve(*skb, ST_NCI_I2C_MIN_SIZE);
+ skb_put(*skb, ST_NCI_I2C_MIN_SIZE);
+ memcpy((*skb)->data, buf, ST_NCI_I2C_MIN_SIZE);
+
+ if (!len)
+ return 0;
+
+ r = i2c_master_recv(client, buf, len);
+ if (r != len) {
+ kfree_skb(*skb);
+ return -EREMOTEIO;
+ }
+
+ skb_put(*skb, len);
+ memcpy((*skb)->data + ST_NCI_I2C_MIN_SIZE, buf, len);
+
+ return 0;
+}
+
+/*
+ * Reads an ndlc frame from the chip.
+ *
+ * On ST_NCI, IRQ goes in idle state when read starts.
+ */
+static irqreturn_t st_nci_irq_thread_fn(int irq, void *phy_id)
+{
+ struct st_nci_i2c_phy *phy = phy_id;
+ struct i2c_client *client;
+ struct sk_buff *skb = NULL;
+ int r;
+
+ if (!phy || !phy->ndlc || irq != phy->i2c_dev->irq) {
+ WARN_ON_ONCE(1);
+ return IRQ_NONE;
+ }
+
+ client = phy->i2c_dev;
+ dev_dbg(&client->dev, "IRQ\n");
+
+ if (phy->ndlc->hard_fault)
+ return IRQ_HANDLED;
+
+ if (!phy->ndlc->powered) {
+ st_nci_i2c_disable(phy);
+ return IRQ_HANDLED;
+ }
+
+ r = st_nci_i2c_read(phy, &skb);
+ if (r == -EREMOTEIO || r == -ENOMEM || r == -EBADMSG)
+ return IRQ_HANDLED;
+
+ ndlc_recv(phy->ndlc, skb);
+
+ return IRQ_HANDLED;
+}
+
+static struct nfc_phy_ops i2c_phy_ops = {
+ .write = st_nci_i2c_write,
+ .enable = st_nci_i2c_enable,
+ .disable = st_nci_i2c_disable,
+};
+
+static int st_nci_i2c_acpi_request_resources(struct i2c_client *client)
+{
+ struct st_nci_i2c_phy *phy = i2c_get_clientdata(client);
+ struct gpio_desc *gpiod_reset;
+ struct device *dev = &client->dev;
+ u8 tmp;
+
+ /* Get RESET GPIO from ACPI */
+ gpiod_reset = devm_gpiod_get_index(dev, ST_NCI_GPIO_NAME_RESET, 1,
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(gpiod_reset)) {
+ nfc_err(dev, "Unable to get RESET GPIO\n");
+ return -ENODEV;
+ }
+
+ phy->gpio_reset = desc_to_gpio(gpiod_reset);
+
+ phy->irq_polarity = irq_get_trigger_type(client->irq);
+
+ phy->se_status.is_ese_present = false;
+ phy->se_status.is_uicc_present = false;
+
+ if (device_property_present(dev, "ese-present")) {
+ device_property_read_u8(dev, "ese-present", &tmp);
+ phy->se_status.is_ese_present = tmp;
+ }
+
+ if (device_property_present(dev, "uicc-present")) {
+ device_property_read_u8(dev, "uicc-present", &tmp);
+ phy->se_status.is_uicc_present = tmp;
+ }
+
+ return 0;
+}
+
+static int st_nci_i2c_of_request_resources(struct i2c_client *client)
+{
+ struct st_nci_i2c_phy *phy = i2c_get_clientdata(client);
+ struct device_node *pp;
+ int gpio;
+ int r;
+
+ pp = client->dev.of_node;
+ if (!pp)
+ return -ENODEV;
+
+ /* Get GPIO from device tree */
+ gpio = of_get_named_gpio(pp, "reset-gpios", 0);
+ if (gpio < 0) {
+ nfc_err(&client->dev,
+ "Failed to retrieve reset-gpios from device tree\n");
+ return gpio;
+ }
+
+ /* GPIO request and configuration */
+ r = devm_gpio_request_one(&client->dev, gpio,
+ GPIOF_OUT_INIT_HIGH, ST_NCI_GPIO_NAME_RESET);
+ if (r) {
+ nfc_err(&client->dev, "Failed to request reset pin\n");
+ return r;
+ }
+ phy->gpio_reset = gpio;
+
+ phy->irq_polarity = irq_get_trigger_type(client->irq);
+
+ phy->se_status.is_ese_present =
+ of_property_read_bool(pp, "ese-present");
+ phy->se_status.is_uicc_present =
+ of_property_read_bool(pp, "uicc-present");
+
+ return 0;
+}
+
+static int st_nci_i2c_request_resources(struct i2c_client *client)
+{
+ struct st_nci_nfc_platform_data *pdata;
+ struct st_nci_i2c_phy *phy = i2c_get_clientdata(client);
+ int r;
+
+ pdata = client->dev.platform_data;
+ if (pdata == NULL) {
+ nfc_err(&client->dev, "No platform data\n");
+ return -EINVAL;
+ }
+
+ /* store for later use */
+ phy->gpio_reset = pdata->gpio_reset;
+ phy->irq_polarity = pdata->irq_polarity;
+
+ r = devm_gpio_request_one(&client->dev,
+ phy->gpio_reset, GPIOF_OUT_INIT_HIGH,
+ ST_NCI_GPIO_NAME_RESET);
+ if (r) {
+ pr_err("%s : reset gpio_request failed\n", __FILE__);
+ return r;
+ }
+
+ phy->se_status.is_ese_present = pdata->is_ese_present;
+ phy->se_status.is_uicc_present = pdata->is_uicc_present;
+
+ return 0;
+}
+
+static int st_nci_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct st_nci_i2c_phy *phy;
+ struct st_nci_nfc_platform_data *pdata;
+ int r;
+
+ 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, "Need I2C_FUNC_I2C\n");
+ return -ENODEV;
+ }
+
+ phy = devm_kzalloc(&client->dev, sizeof(struct st_nci_i2c_phy),
+ GFP_KERNEL);
+ if (!phy)
+ return -ENOMEM;
+
+ phy->i2c_dev = client;
+
+ i2c_set_clientdata(client, phy);
+
+ pdata = client->dev.platform_data;
+ if (!pdata && client->dev.of_node) {
+ r = st_nci_i2c_of_request_resources(client);
+ if (r) {
+ nfc_err(&client->dev, "No platform data\n");
+ return r;
+ }
+ } else if (pdata) {
+ r = st_nci_i2c_request_resources(client);
+ if (r) {
+ nfc_err(&client->dev,
+ "Cannot get platform resources\n");
+ return r;
+ }
+ } else if (ACPI_HANDLE(&client->dev)) {
+ r = st_nci_i2c_acpi_request_resources(client);
+ if (r) {
+ nfc_err(&client->dev, "Cannot get ACPI data\n");
+ return r;
+ }
+ } else {
+ nfc_err(&client->dev,
+ "st_nci platform resources not available\n");
+ return -ENODEV;
+ }
+
+ r = ndlc_probe(phy, &i2c_phy_ops, &client->dev,
+ ST_NCI_FRAME_HEADROOM, ST_NCI_FRAME_TAILROOM,
+ &phy->ndlc, &phy->se_status);
+ if (r < 0) {
+ nfc_err(&client->dev, "Unable to register ndlc layer\n");
+ return r;
+ }
+
+ phy->irq_active = true;
+ r = devm_request_threaded_irq(&client->dev, client->irq, NULL,
+ st_nci_irq_thread_fn,
+ phy->irq_polarity | IRQF_ONESHOT,
+ ST_NCI_DRIVER_NAME, phy);
+ if (r < 0)
+ nfc_err(&client->dev, "Unable to register IRQ handler\n");
+
+ return r;
+}
+
+static int st_nci_i2c_remove(struct i2c_client *client)
+{
+ struct st_nci_i2c_phy *phy = i2c_get_clientdata(client);
+
+ dev_dbg(&client->dev, "%s\n", __func__);
+
+ ndlc_remove(phy->ndlc);
+
+ return 0;
+}
+
+static struct i2c_device_id st_nci_i2c_id_table[] = {
+ {ST_NCI_DRIVER_NAME, 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, st_nci_i2c_id_table);
+
+static const struct acpi_device_id st_nci_i2c_acpi_match[] = {
+ {"SMO2101"},
+ {"SMO2102"},
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, st_nci_i2c_acpi_match);
+
+static const struct of_device_id of_st_nci_i2c_match[] = {
+ { .compatible = "st,st21nfcb-i2c", },
+ { .compatible = "st,st21nfcb_i2c", },
+ { .compatible = "st,st21nfcc-i2c", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, of_st_nci_i2c_match);
+
+static struct i2c_driver st_nci_i2c_driver = {
+ .driver = {
+ .name = ST_NCI_I2C_DRIVER_NAME,
+ .of_match_table = of_match_ptr(of_st_nci_i2c_match),
+ .acpi_match_table = ACPI_PTR(st_nci_i2c_acpi_match),
+ },
+ .probe = st_nci_i2c_probe,
+ .id_table = st_nci_i2c_id_table,
+ .remove = st_nci_i2c_remove,
+};
+module_i2c_driver(st_nci_i2c_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/nfc/st-nci/ndlc.c b/drivers/nfc/st-nci/ndlc.c
new file mode 100644
index 0000000..50880d7
--- /dev/null
+++ b/drivers/nfc/st-nci/ndlc.c
@@ -0,0 +1,316 @@
+/*
+ * Low Level Transport (NDLC) Driver for STMicroelectronics NFC Chip
+ *
+ * Copyright (C) 2014-2015 STMicroelectronics SAS. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/sched.h>
+#include <net/nfc/nci_core.h>
+
+#include "st-nci.h"
+
+#define NDLC_TIMER_T1 100
+#define NDLC_TIMER_T1_WAIT 400
+#define NDLC_TIMER_T2 1200
+
+#define PCB_TYPE_DATAFRAME 0x80
+#define PCB_TYPE_SUPERVISOR 0xc0
+#define PCB_TYPE_MASK PCB_TYPE_SUPERVISOR
+
+#define PCB_SYNC_ACK 0x20
+#define PCB_SYNC_NACK 0x10
+#define PCB_SYNC_WAIT 0x30
+#define PCB_SYNC_NOINFO 0x00
+#define PCB_SYNC_MASK PCB_SYNC_WAIT
+
+#define PCB_DATAFRAME_RETRANSMIT_YES 0x00
+#define PCB_DATAFRAME_RETRANSMIT_NO 0x04
+#define PCB_DATAFRAME_RETRANSMIT_MASK PCB_DATAFRAME_RETRANSMIT_NO
+
+#define PCB_SUPERVISOR_RETRANSMIT_YES 0x00
+#define PCB_SUPERVISOR_RETRANSMIT_NO 0x02
+#define PCB_SUPERVISOR_RETRANSMIT_MASK PCB_SUPERVISOR_RETRANSMIT_NO
+
+#define PCB_FRAME_CRC_INFO_PRESENT 0x08
+#define PCB_FRAME_CRC_INFO_NOTPRESENT 0x00
+#define PCB_FRAME_CRC_INFO_MASK PCB_FRAME_CRC_INFO_PRESENT
+
+#define NDLC_DUMP_SKB(info, skb) \
+do { \
+ pr_debug("%s:\n", info); \
+ print_hex_dump(KERN_DEBUG, "ndlc: ", DUMP_PREFIX_OFFSET, \
+ 16, 1, skb->data, skb->len, 0); \
+} while (0)
+
+int ndlc_open(struct llt_ndlc *ndlc)
+{
+ /* toggle reset pin */
+ ndlc->ops->enable(ndlc->phy_id);
+ ndlc->powered = 1;
+ return 0;
+}
+EXPORT_SYMBOL(ndlc_open);
+
+void ndlc_close(struct llt_ndlc *ndlc)
+{
+ struct nci_mode_set_cmd cmd;
+
+ cmd.cmd_type = ST_NCI_SET_NFC_MODE;
+ cmd.mode = 0;
+
+ /* toggle reset pin */
+ ndlc->ops->enable(ndlc->phy_id);
+
+ nci_prop_cmd(ndlc->ndev, ST_NCI_CORE_PROP,
+ sizeof(struct nci_mode_set_cmd), (__u8 *)&cmd);
+
+ ndlc->powered = 0;
+ ndlc->ops->disable(ndlc->phy_id);
+}
+EXPORT_SYMBOL(ndlc_close);
+
+int ndlc_send(struct llt_ndlc *ndlc, struct sk_buff *skb)
+{
+ /* add ndlc header */
+ u8 pcb = PCB_TYPE_DATAFRAME | PCB_DATAFRAME_RETRANSMIT_NO |
+ PCB_FRAME_CRC_INFO_NOTPRESENT;
+
+ *skb_push(skb, 1) = pcb;
+ skb_queue_tail(&ndlc->send_q, skb);
+
+ schedule_work(&ndlc->sm_work);
+
+ return 0;
+}
+EXPORT_SYMBOL(ndlc_send);
+
+static void llt_ndlc_send_queue(struct llt_ndlc *ndlc)
+{
+ struct sk_buff *skb;
+ int r;
+ unsigned long time_sent;
+
+ if (ndlc->send_q.qlen)
+ pr_debug("sendQlen=%d unackQlen=%d\n",
+ ndlc->send_q.qlen, ndlc->ack_pending_q.qlen);
+
+ while (ndlc->send_q.qlen) {
+ skb = skb_dequeue(&ndlc->send_q);
+ NDLC_DUMP_SKB("ndlc frame written", skb);
+ r = ndlc->ops->write(ndlc->phy_id, skb);
+ if (r < 0) {
+ ndlc->hard_fault = r;
+ break;
+ }
+ time_sent = jiffies;
+ *(unsigned long *)skb->cb = time_sent;
+
+ skb_queue_tail(&ndlc->ack_pending_q, skb);
+
+ /* start timer t1 for ndlc aknowledge */
+ ndlc->t1_active = true;
+ mod_timer(&ndlc->t1_timer, time_sent +
+ msecs_to_jiffies(NDLC_TIMER_T1));
+ /* start timer t2 for chip availability */
+ ndlc->t2_active = true;
+ mod_timer(&ndlc->t2_timer, time_sent +
+ msecs_to_jiffies(NDLC_TIMER_T2));
+ }
+}
+
+static void llt_ndlc_requeue_data_pending(struct llt_ndlc *ndlc)
+{
+ struct sk_buff *skb;
+ u8 pcb;
+
+ while ((skb = skb_dequeue_tail(&ndlc->ack_pending_q))) {
+ pcb = skb->data[0];
+ switch (pcb & PCB_TYPE_MASK) {
+ case PCB_TYPE_SUPERVISOR:
+ skb->data[0] = (pcb & ~PCB_SUPERVISOR_RETRANSMIT_MASK) |
+ PCB_SUPERVISOR_RETRANSMIT_YES;
+ break;
+ case PCB_TYPE_DATAFRAME:
+ skb->data[0] = (pcb & ~PCB_DATAFRAME_RETRANSMIT_MASK) |
+ PCB_DATAFRAME_RETRANSMIT_YES;
+ break;
+ default:
+ pr_err("UNKNOWN Packet Control Byte=%d\n", pcb);
+ kfree_skb(skb);
+ continue;
+ }
+ skb_queue_head(&ndlc->send_q, skb);
+ }
+}
+
+static void llt_ndlc_rcv_queue(struct llt_ndlc *ndlc)
+{
+ struct sk_buff *skb;
+ u8 pcb;
+ unsigned long time_sent;
+
+ if (ndlc->rcv_q.qlen)
+ pr_debug("rcvQlen=%d\n", ndlc->rcv_q.qlen);
+
+ while ((skb = skb_dequeue(&ndlc->rcv_q)) != NULL) {
+ pcb = skb->data[0];
+ skb_pull(skb, 1);
+ if ((pcb & PCB_TYPE_MASK) == PCB_TYPE_SUPERVISOR) {
+ switch (pcb & PCB_SYNC_MASK) {
+ case PCB_SYNC_ACK:
+ skb = skb_dequeue(&ndlc->ack_pending_q);
+ kfree_skb(skb);
+ del_timer_sync(&ndlc->t1_timer);
+ del_timer_sync(&ndlc->t2_timer);
+ ndlc->t2_active = false;
+ ndlc->t1_active = false;
+ break;
+ case PCB_SYNC_NACK:
+ llt_ndlc_requeue_data_pending(ndlc);
+ llt_ndlc_send_queue(ndlc);
+ /* start timer t1 for ndlc aknowledge */
+ time_sent = jiffies;
+ ndlc->t1_active = true;
+ mod_timer(&ndlc->t1_timer, time_sent +
+ msecs_to_jiffies(NDLC_TIMER_T1));
+ break;
+ case PCB_SYNC_WAIT:
+ time_sent = jiffies;
+ ndlc->t1_active = true;
+ mod_timer(&ndlc->t1_timer, time_sent +
+ msecs_to_jiffies(NDLC_TIMER_T1_WAIT));
+ break;
+ default:
+ kfree_skb(skb);
+ break;
+ }
+ } else if ((pcb & PCB_TYPE_MASK) == PCB_TYPE_DATAFRAME) {
+ nci_recv_frame(ndlc->ndev, skb);
+ } else {
+ kfree_skb(skb);
+ }
+ }
+}
+
+static void llt_ndlc_sm_work(struct work_struct *work)
+{
+ struct llt_ndlc *ndlc = container_of(work, struct llt_ndlc, sm_work);
+
+ llt_ndlc_send_queue(ndlc);
+ llt_ndlc_rcv_queue(ndlc);
+
+ if (ndlc->t1_active && timer_pending(&ndlc->t1_timer) == 0) {
+ pr_debug
+ ("Handle T1(recv SUPERVISOR) elapsed (T1 now inactive)\n");
+ ndlc->t1_active = false;
+
+ llt_ndlc_requeue_data_pending(ndlc);
+ llt_ndlc_send_queue(ndlc);
+ }
+
+ if (ndlc->t2_active && timer_pending(&ndlc->t2_timer) == 0) {
+ pr_debug("Handle T2(recv DATA) elapsed (T2 now inactive)\n");
+ ndlc->t2_active = false;
+ ndlc->t1_active = false;
+ del_timer_sync(&ndlc->t1_timer);
+ del_timer_sync(&ndlc->t2_timer);
+ ndlc_close(ndlc);
+ ndlc->hard_fault = -EREMOTEIO;
+ }
+}
+
+void ndlc_recv(struct llt_ndlc *ndlc, struct sk_buff *skb)
+{
+ if (skb == NULL) {
+ pr_err("NULL Frame -> link is dead\n");
+ ndlc->hard_fault = -EREMOTEIO;
+ ndlc_close(ndlc);
+ } else {
+ NDLC_DUMP_SKB("incoming frame", skb);
+ skb_queue_tail(&ndlc->rcv_q, skb);
+ }
+
+ schedule_work(&ndlc->sm_work);
+}
+EXPORT_SYMBOL(ndlc_recv);
+
+static void ndlc_t1_timeout(unsigned long data)
+{
+ struct llt_ndlc *ndlc = (struct llt_ndlc *)data;
+
+ pr_debug("\n");
+
+ schedule_work(&ndlc->sm_work);
+}
+
+static void ndlc_t2_timeout(unsigned long data)
+{
+ struct llt_ndlc *ndlc = (struct llt_ndlc *)data;
+
+ pr_debug("\n");
+
+ schedule_work(&ndlc->sm_work);
+}
+
+int ndlc_probe(void *phy_id, struct nfc_phy_ops *phy_ops, struct device *dev,
+ int phy_headroom, int phy_tailroom, struct llt_ndlc **ndlc_id,
+ struct st_nci_se_status *se_status)
+{
+ struct llt_ndlc *ndlc;
+
+ ndlc = devm_kzalloc(dev, sizeof(struct llt_ndlc), GFP_KERNEL);
+ if (!ndlc)
+ return -ENOMEM;
+
+ ndlc->ops = phy_ops;
+ ndlc->phy_id = phy_id;
+ ndlc->dev = dev;
+ ndlc->powered = 0;
+
+ *ndlc_id = ndlc;
+
+ /* initialize timers */
+ init_timer(&ndlc->t1_timer);
+ ndlc->t1_timer.data = (unsigned long)ndlc;
+ ndlc->t1_timer.function = ndlc_t1_timeout;
+
+ init_timer(&ndlc->t2_timer);
+ ndlc->t2_timer.data = (unsigned long)ndlc;
+ ndlc->t2_timer.function = ndlc_t2_timeout;
+
+ skb_queue_head_init(&ndlc->rcv_q);
+ skb_queue_head_init(&ndlc->send_q);
+ skb_queue_head_init(&ndlc->ack_pending_q);
+
+ INIT_WORK(&ndlc->sm_work, llt_ndlc_sm_work);
+
+ return st_nci_probe(ndlc, phy_headroom, phy_tailroom, se_status);
+}
+EXPORT_SYMBOL(ndlc_probe);
+
+void ndlc_remove(struct llt_ndlc *ndlc)
+{
+ st_nci_remove(ndlc->ndev);
+
+ /* cancel timers */
+ del_timer_sync(&ndlc->t1_timer);
+ del_timer_sync(&ndlc->t2_timer);
+ ndlc->t2_active = false;
+ ndlc->t1_active = false;
+
+ skb_queue_purge(&ndlc->rcv_q);
+ skb_queue_purge(&ndlc->send_q);
+}
+EXPORT_SYMBOL(ndlc_remove);
diff --git a/drivers/nfc/st-nci/ndlc.h b/drivers/nfc/st-nci/ndlc.h
new file mode 100644
index 0000000..bdf78ff
--- /dev/null
+++ b/drivers/nfc/st-nci/ndlc.h
@@ -0,0 +1,63 @@
+/*
+ * NCI based Driver for STMicroelectronics NFC Chip
+ *
+ * Copyright (C) 2014-2015 STMicroelectronics SAS. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LOCAL_NDLC_H_
+#define __LOCAL_NDLC_H_
+
+#include <linux/skbuff.h>
+#include <net/nfc/nfc.h>
+
+struct st_nci_se_status;
+
+/* Low Level Transport description */
+struct llt_ndlc {
+ struct nci_dev *ndev;
+ struct nfc_phy_ops *ops;
+ void *phy_id;
+
+ struct timer_list t1_timer;
+ bool t1_active;
+
+ struct timer_list t2_timer;
+ bool t2_active;
+
+ struct sk_buff_head rcv_q;
+ struct sk_buff_head send_q;
+ struct sk_buff_head ack_pending_q;
+
+ struct work_struct sm_work;
+
+ struct device *dev;
+
+ /*
+ * < 0 if hardware error occurred
+ * and prevents normal operation.
+ */
+ int hard_fault;
+ int powered;
+};
+
+int ndlc_open(struct llt_ndlc *ndlc);
+void ndlc_close(struct llt_ndlc *ndlc);
+int ndlc_send(struct llt_ndlc *ndlc, struct sk_buff *skb);
+void ndlc_recv(struct llt_ndlc *ndlc, struct sk_buff *skb);
+int ndlc_probe(void *phy_id, struct nfc_phy_ops *phy_ops, struct device *dev,
+ int phy_headroom, int phy_tailroom, struct llt_ndlc **ndlc_id,
+ struct st_nci_se_status *se_status);
+void ndlc_remove(struct llt_ndlc *ndlc);
+#endif /* __LOCAL_NDLC_H__ */
diff --git a/drivers/nfc/st-nci/se.c b/drivers/nfc/st-nci/se.c
new file mode 100644
index 0000000..56f2112
--- /dev/null
+++ b/drivers/nfc/st-nci/se.c
@@ -0,0 +1,763 @@
+/*
+ * Secure Element driver for STMicroelectronics NFC NCI chip
+ *
+ * Copyright (C) 2014-2015 STMicroelectronics SAS. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/module.h>
+#include <linux/nfc.h>
+#include <linux/delay.h>
+#include <net/nfc/nci.h>
+#include <net/nfc/nci_core.h>
+
+#include "st-nci.h"
+
+struct st_nci_pipe_info {
+ u8 pipe_state;
+ u8 src_host_id;
+ u8 src_gate_id;
+ u8 dst_host_id;
+ u8 dst_gate_id;
+} __packed;
+
+/* Hosts */
+#define ST_NCI_HOST_CONTROLLER_ID 0x00
+#define ST_NCI_TERMINAL_HOST_ID 0x01
+#define ST_NCI_UICC_HOST_ID 0x02
+#define ST_NCI_ESE_HOST_ID 0xc0
+
+/* Gates */
+#define ST_NCI_APDU_READER_GATE 0xf0
+#define ST_NCI_CONNECTIVITY_GATE 0x41
+
+/* Pipes */
+#define ST_NCI_DEVICE_MGNT_PIPE 0x02
+
+/* Connectivity pipe only */
+#define ST_NCI_SE_COUNT_PIPE_UICC 0x01
+/* Connectivity + APDU Reader pipe */
+#define ST_NCI_SE_COUNT_PIPE_EMBEDDED 0x02
+
+#define ST_NCI_SE_TO_HOT_PLUG 1000 /* msecs */
+#define ST_NCI_SE_TO_PIPES 2000
+
+#define ST_NCI_EVT_HOT_PLUG_IS_INHIBITED(x) (x->data[0] & 0x80)
+
+#define NCI_HCI_APDU_PARAM_ATR 0x01
+#define NCI_HCI_ADMIN_PARAM_SESSION_IDENTITY 0x01
+#define NCI_HCI_ADMIN_PARAM_WHITELIST 0x03
+#define NCI_HCI_ADMIN_PARAM_HOST_LIST 0x04
+
+#define ST_NCI_EVT_SE_HARD_RESET 0x20
+#define ST_NCI_EVT_TRANSMIT_DATA 0x10
+#define ST_NCI_EVT_WTX_REQUEST 0x11
+#define ST_NCI_EVT_SE_SOFT_RESET 0x11
+#define ST_NCI_EVT_SE_END_OF_APDU_TRANSFER 0x21
+#define ST_NCI_EVT_HOT_PLUG 0x03
+
+#define ST_NCI_SE_MODE_OFF 0x00
+#define ST_NCI_SE_MODE_ON 0x01
+
+#define ST_NCI_EVT_CONNECTIVITY 0x10
+#define ST_NCI_EVT_TRANSACTION 0x12
+
+#define ST_NCI_DM_GETINFO 0x13
+#define ST_NCI_DM_GETINFO_PIPE_LIST 0x02
+#define ST_NCI_DM_GETINFO_PIPE_INFO 0x01
+#define ST_NCI_DM_PIPE_CREATED 0x02
+#define ST_NCI_DM_PIPE_OPEN 0x04
+#define ST_NCI_DM_RF_ACTIVE 0x80
+#define ST_NCI_DM_DISCONNECT 0x30
+
+#define ST_NCI_DM_IS_PIPE_OPEN(p) \
+ ((p & 0x0f) == (ST_NCI_DM_PIPE_CREATED | ST_NCI_DM_PIPE_OPEN))
+
+#define ST_NCI_ATR_DEFAULT_BWI 0x04
+
+/*
+ * WT = 2^BWI/10[s], convert into msecs and add a secure
+ * room by increasing by 2 this timeout
+ */
+#define ST_NCI_BWI_TO_TIMEOUT(x) ((1 << x) * 200)
+#define ST_NCI_ATR_GET_Y_FROM_TD(x) (x >> 4)
+
+/* If TA is present bit 0 is set */
+#define ST_NCI_ATR_TA_PRESENT(x) (x & 0x01)
+/* If TB is present bit 1 is set */
+#define ST_NCI_ATR_TB_PRESENT(x) (x & 0x02)
+
+#define ST_NCI_NUM_DEVICES 256
+
+static DECLARE_BITMAP(dev_mask, ST_NCI_NUM_DEVICES);
+
+/* Here are the mandatory pipe for st_nci */
+static struct nci_hci_gate st_nci_gates[] = {
+ {NCI_HCI_ADMIN_GATE, NCI_HCI_ADMIN_PIPE,
+ ST_NCI_HOST_CONTROLLER_ID},
+ {NCI_HCI_LINK_MGMT_GATE, NCI_HCI_LINK_MGMT_PIPE,
+ ST_NCI_HOST_CONTROLLER_ID},
+ {ST_NCI_DEVICE_MGNT_GATE, ST_NCI_DEVICE_MGNT_PIPE,
+ ST_NCI_HOST_CONTROLLER_ID},
+
+ {NCI_HCI_IDENTITY_MGMT_GATE, NCI_HCI_INVALID_PIPE,
+ ST_NCI_HOST_CONTROLLER_ID},
+
+ /* Secure element pipes are created by secure element host */
+ {ST_NCI_CONNECTIVITY_GATE, NCI_HCI_DO_NOT_OPEN_PIPE,
+ ST_NCI_HOST_CONTROLLER_ID},
+ {ST_NCI_APDU_READER_GATE, NCI_HCI_DO_NOT_OPEN_PIPE,
+ ST_NCI_HOST_CONTROLLER_ID},
+};
+
+static u8 st_nci_se_get_bwi(struct nci_dev *ndev)
+{
+ int i;
+ u8 td;
+ struct st_nci_info *info = nci_get_drvdata(ndev);
+
+ /* Bits 8 to 5 of the first TB for T=1 encode BWI from zero to nine */
+ for (i = 1; i < ST_NCI_ESE_MAX_LENGTH; i++) {
+ td = ST_NCI_ATR_GET_Y_FROM_TD(info->se_info.atr[i]);
+ if (ST_NCI_ATR_TA_PRESENT(td))
+ i++;
+ if (ST_NCI_ATR_TB_PRESENT(td)) {
+ i++;
+ return info->se_info.atr[i] >> 4;
+ }
+ }
+ return ST_NCI_ATR_DEFAULT_BWI;
+}
+
+static void st_nci_se_get_atr(struct nci_dev *ndev)
+{
+ struct st_nci_info *info = nci_get_drvdata(ndev);
+ int r;
+ struct sk_buff *skb;
+
+ r = nci_hci_get_param(ndev, ST_NCI_APDU_READER_GATE,
+ NCI_HCI_APDU_PARAM_ATR, &skb);
+ if (r < 0)
+ return;
+
+ if (skb->len <= ST_NCI_ESE_MAX_LENGTH) {
+ memcpy(info->se_info.atr, skb->data, skb->len);
+
+ info->se_info.wt_timeout =
+ ST_NCI_BWI_TO_TIMEOUT(st_nci_se_get_bwi(ndev));
+ }
+ kfree_skb(skb);
+}
+
+int st_nci_hci_load_session(struct nci_dev *ndev)
+{
+ int i, j, r;
+ struct sk_buff *skb_pipe_list, *skb_pipe_info;
+ struct st_nci_pipe_info *dm_pipe_info;
+ u8 pipe_list[] = { ST_NCI_DM_GETINFO_PIPE_LIST,
+ ST_NCI_TERMINAL_HOST_ID};
+ u8 pipe_info[] = { ST_NCI_DM_GETINFO_PIPE_INFO,
+ ST_NCI_TERMINAL_HOST_ID, 0};
+
+ /* On ST_NCI device pipes number are dynamics
+ * If pipes are already created, hci_dev_up will fail.
+ * Doing a clear all pipe is a bad idea because:
+ * - It does useless EEPROM cycling
+ * - It might cause issue for secure elements support
+ * (such as removing connectivity or APDU reader pipe)
+ * A better approach on ST_NCI is to:
+ * - get a pipe list for each host.
+ * (eg: ST_NCI_HOST_CONTROLLER_ID for now).
+ * (TODO Later on UICC HOST and eSE HOST)
+ * - get pipe information
+ * - match retrieved pipe list in st_nci_gates
+ * ST_NCI_DEVICE_MGNT_GATE is a proprietary gate
+ * with ST_NCI_DEVICE_MGNT_PIPE.
+ * Pipe can be closed and need to be open.
+ */
+ r = nci_hci_connect_gate(ndev, ST_NCI_HOST_CONTROLLER_ID,
+ ST_NCI_DEVICE_MGNT_GATE,
+ ST_NCI_DEVICE_MGNT_PIPE);
+ if (r < 0)
+ return r;
+
+ /* Get pipe list */
+ r = nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
+ ST_NCI_DM_GETINFO, pipe_list, sizeof(pipe_list),
+ &skb_pipe_list);
+ if (r < 0)
+ return r;
+
+ /* Complete the existing gate_pipe table */
+ for (i = 0; i < skb_pipe_list->len; i++) {
+ pipe_info[2] = skb_pipe_list->data[i];
+ r = nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
+ ST_NCI_DM_GETINFO, pipe_info,
+ sizeof(pipe_info), &skb_pipe_info);
+
+ if (r)
+ continue;
+
+ /*
+ * Match pipe ID and gate ID
+ * Output format from ST21NFC_DM_GETINFO is:
+ * - pipe state (1byte)
+ * - source hid (1byte)
+ * - source gid (1byte)
+ * - destination hid (1byte)
+ * - destination gid (1byte)
+ */
+ dm_pipe_info = (struct st_nci_pipe_info *)skb_pipe_info->data;
+ if (dm_pipe_info->dst_gate_id == ST_NCI_APDU_READER_GATE &&
+ dm_pipe_info->src_host_id == ST_NCI_UICC_HOST_ID) {
+ pr_err("Unexpected apdu_reader pipe on host %x\n",
+ dm_pipe_info->src_host_id);
+ kfree_skb(skb_pipe_info);
+ continue;
+ }
+
+ for (j = 3; (j < ARRAY_SIZE(st_nci_gates)) &&
+ (st_nci_gates[j].gate != dm_pipe_info->dst_gate_id); j++)
+ ;
+
+ if (j < ARRAY_SIZE(st_nci_gates) &&
+ st_nci_gates[j].gate == dm_pipe_info->dst_gate_id &&
+ ST_NCI_DM_IS_PIPE_OPEN(dm_pipe_info->pipe_state)) {
+ ndev->hci_dev->init_data.gates[j].pipe = pipe_info[2];
+
+ ndev->hci_dev->gate2pipe[st_nci_gates[j].gate] =
+ pipe_info[2];
+ ndev->hci_dev->pipes[pipe_info[2]].gate =
+ st_nci_gates[j].gate;
+ ndev->hci_dev->pipes[pipe_info[2]].host =
+ dm_pipe_info->src_host_id;
+ }
+ kfree_skb(skb_pipe_info);
+ }
+
+ /*
+ * 3 gates have a well known pipe ID. Only NCI_HCI_LINK_MGMT_GATE
+ * is not yet open at this stage.
+ */
+ r = nci_hci_connect_gate(ndev, ST_NCI_HOST_CONTROLLER_ID,
+ NCI_HCI_LINK_MGMT_GATE,
+ NCI_HCI_LINK_MGMT_PIPE);
+
+ kfree_skb(skb_pipe_list);
+ return r;
+}
+EXPORT_SYMBOL_GPL(st_nci_hci_load_session);
+
+static void st_nci_hci_admin_event_received(struct nci_dev *ndev,
+ u8 event, struct sk_buff *skb)
+{
+ struct st_nci_info *info = nci_get_drvdata(ndev);
+
+ switch (event) {
+ case ST_NCI_EVT_HOT_PLUG:
+ if (info->se_info.se_active) {
+ if (!ST_NCI_EVT_HOT_PLUG_IS_INHIBITED(skb)) {
+ del_timer_sync(&info->se_info.se_active_timer);
+ info->se_info.se_active = false;
+ complete(&info->se_info.req_completion);
+ } else {
+ mod_timer(&info->se_info.se_active_timer,
+ jiffies +
+ msecs_to_jiffies(ST_NCI_SE_TO_PIPES));
+ }
+ }
+ break;
+ default:
+ nfc_err(&ndev->nfc_dev->dev, "Unexpected event on admin gate\n");
+ }
+}
+
+static int st_nci_hci_apdu_reader_event_received(struct nci_dev *ndev,
+ u8 event,
+ struct sk_buff *skb)
+{
+ int r = 0;
+ struct st_nci_info *info = nci_get_drvdata(ndev);
+
+ pr_debug("apdu reader gate event: %x\n", event);
+
+ switch (event) {
+ case ST_NCI_EVT_TRANSMIT_DATA:
+ del_timer_sync(&info->se_info.bwi_timer);
+ info->se_info.bwi_active = false;
+ info->se_info.cb(info->se_info.cb_context,
+ skb->data, skb->len, 0);
+ break;
+ case ST_NCI_EVT_WTX_REQUEST:
+ mod_timer(&info->se_info.bwi_timer, jiffies +
+ msecs_to_jiffies(info->se_info.wt_timeout));
+ break;
+ default:
+ nfc_err(&ndev->nfc_dev->dev, "Unexpected event on apdu reader gate\n");
+ return 1;
+ }
+
+ kfree_skb(skb);
+ return r;
+}
+
+/*
+ * Returns:
+ * <= 0: driver handled the event, skb consumed
+ * 1: driver does not handle the event, please do standard processing
+ */
+static int st_nci_hci_connectivity_event_received(struct nci_dev *ndev,
+ u8 host, u8 event,
+ struct sk_buff *skb)
+{
+ int r = 0;
+ struct device *dev = &ndev->nfc_dev->dev;
+ struct nfc_evt_transaction *transaction;
+
+ pr_debug("connectivity gate event: %x\n", event);
+
+ switch (event) {
+ case ST_NCI_EVT_CONNECTIVITY:
+ r = nfc_se_connectivity(ndev->nfc_dev, host);
+ break;
+ case ST_NCI_EVT_TRANSACTION:
+ /* According to specification etsi 102 622
+ * 11.2.2.4 EVT_TRANSACTION Table 52
+ * Description Tag Length
+ * AID 81 5 to 16
+ * PARAMETERS 82 0 to 255
+ */
+ if (skb->len < NFC_MIN_AID_LENGTH + 2 &&
+ skb->data[0] != NFC_EVT_TRANSACTION_AID_TAG)
+ return -EPROTO;
+
+ transaction = (struct nfc_evt_transaction *)devm_kzalloc(dev,
+ skb->len - 2, GFP_KERNEL);
+
+ transaction->aid_len = skb->data[1];
+ memcpy(transaction->aid, &skb->data[2], transaction->aid_len);
+
+ /* Check next byte is PARAMETERS tag (82) */
+ if (skb->data[transaction->aid_len + 2] !=
+ NFC_EVT_TRANSACTION_PARAMS_TAG)
+ return -EPROTO;
+
+ transaction->params_len = skb->data[transaction->aid_len + 3];
+ memcpy(transaction->params, skb->data +
+ transaction->aid_len + 4, transaction->params_len);
+
+ r = nfc_se_transaction(ndev->nfc_dev, host, transaction);
+ break;
+ default:
+ nfc_err(&ndev->nfc_dev->dev, "Unexpected event on connectivity gate\n");
+ return 1;
+ }
+ kfree_skb(skb);
+ return r;
+}
+
+void st_nci_hci_event_received(struct nci_dev *ndev, u8 pipe,
+ u8 event, struct sk_buff *skb)
+{
+ u8 gate = ndev->hci_dev->pipes[pipe].gate;
+ u8 host = ndev->hci_dev->pipes[pipe].host;
+
+ switch (gate) {
+ case NCI_HCI_ADMIN_GATE:
+ st_nci_hci_admin_event_received(ndev, event, skb);
+ break;
+ case ST_NCI_APDU_READER_GATE:
+ st_nci_hci_apdu_reader_event_received(ndev, event, skb);
+ break;
+ case ST_NCI_CONNECTIVITY_GATE:
+ st_nci_hci_connectivity_event_received(ndev, host, event, skb);
+ break;
+ }
+}
+EXPORT_SYMBOL_GPL(st_nci_hci_event_received);
+
+void st_nci_hci_cmd_received(struct nci_dev *ndev, u8 pipe, u8 cmd,
+ struct sk_buff *skb)
+{
+ struct st_nci_info *info = nci_get_drvdata(ndev);
+ u8 gate = ndev->hci_dev->pipes[pipe].gate;
+
+ pr_debug("cmd: %x\n", cmd);
+
+ switch (cmd) {
+ case NCI_HCI_ANY_OPEN_PIPE:
+ if (gate != ST_NCI_APDU_READER_GATE &&
+ ndev->hci_dev->pipes[pipe].host != ST_NCI_UICC_HOST_ID)
+ ndev->hci_dev->count_pipes++;
+
+ if (ndev->hci_dev->count_pipes ==
+ ndev->hci_dev->expected_pipes) {
+ del_timer_sync(&info->se_info.se_active_timer);
+ info->se_info.se_active = false;
+ ndev->hci_dev->count_pipes = 0;
+ complete(&info->se_info.req_completion);
+ }
+ break;
+ }
+}
+EXPORT_SYMBOL_GPL(st_nci_hci_cmd_received);
+
+static int st_nci_control_se(struct nci_dev *ndev, u8 se_idx,
+ u8 state)
+{
+ struct st_nci_info *info = nci_get_drvdata(ndev);
+ int r, i;
+ struct sk_buff *sk_host_list;
+ u8 host_id;
+
+ switch (se_idx) {
+ case ST_NCI_UICC_HOST_ID:
+ ndev->hci_dev->count_pipes = 0;
+ ndev->hci_dev->expected_pipes = ST_NCI_SE_COUNT_PIPE_UICC;
+ break;
+ case ST_NCI_ESE_HOST_ID:
+ ndev->hci_dev->count_pipes = 0;
+ ndev->hci_dev->expected_pipes = ST_NCI_SE_COUNT_PIPE_EMBEDDED;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /*
+ * Wait for an EVT_HOT_PLUG in order to
+ * retrieve a relevant host list.
+ */
+ reinit_completion(&info->se_info.req_completion);
+ r = nci_nfcee_mode_set(ndev, se_idx, state);
+ if (r != NCI_STATUS_OK)
+ return r;
+
+ mod_timer(&info->se_info.se_active_timer, jiffies +
+ msecs_to_jiffies(ST_NCI_SE_TO_HOT_PLUG));
+ info->se_info.se_active = true;
+
+ /* Ignore return value and check in any case the host_list */
+ wait_for_completion_interruptible(&info->se_info.req_completion);
+
+ /* There might be some "collision" after receiving a HOT_PLUG event
+ * This may cause the CLF to not answer to the next hci command.
+ * There is no possible synchronization to prevent this.
+ * Adding a small delay is the only way to solve the issue.
+ */
+ if (info->se_info.se_status->is_ese_present &&
+ info->se_info.se_status->is_uicc_present)
+ usleep_range(15000, 20000);
+
+ r = nci_hci_get_param(ndev, NCI_HCI_ADMIN_GATE,
+ NCI_HCI_ADMIN_PARAM_HOST_LIST, &sk_host_list);
+ if (r != NCI_HCI_ANY_OK)
+ return r;
+
+ for (i = 0; i < sk_host_list->len &&
+ sk_host_list->data[i] != se_idx; i++)
+ ;
+ host_id = sk_host_list->data[i];
+ kfree_skb(sk_host_list);
+ if (state == ST_NCI_SE_MODE_ON && host_id == se_idx)
+ return se_idx;
+ else if (state == ST_NCI_SE_MODE_OFF && host_id != se_idx)
+ return se_idx;
+
+ return -1;
+}
+
+int st_nci_disable_se(struct nci_dev *ndev, u32 se_idx)
+{
+ int r;
+
+ pr_debug("st_nci_disable_se\n");
+
+ /*
+ * According to upper layer, se_idx == NFC_SE_UICC when
+ * info->se_info.se_status->is_uicc_enable is true should never happen
+ * Same for eSE.
+ */
+ r = st_nci_control_se(ndev, se_idx, ST_NCI_SE_MODE_OFF);
+ if (r < 0) {
+ /* Do best effort to release SWP */
+ if (se_idx == NFC_SE_EMBEDDED) {
+ r = nci_hci_send_event(ndev, ST_NCI_APDU_READER_GATE,
+ ST_NCI_EVT_SE_END_OF_APDU_TRANSFER,
+ NULL, 0);
+ }
+ return r;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(st_nci_disable_se);
+
+int st_nci_enable_se(struct nci_dev *ndev, u32 se_idx)
+{
+ int r;
+
+ pr_debug("st_nci_enable_se\n");
+
+ /*
+ * According to upper layer, se_idx == NFC_SE_UICC when
+ * info->se_info.se_status->is_uicc_enable is true should never happen.
+ * Same for eSE.
+ */
+ r = st_nci_control_se(ndev, se_idx, ST_NCI_SE_MODE_ON);
+ if (r == ST_NCI_ESE_HOST_ID) {
+ st_nci_se_get_atr(ndev);
+ r = nci_hci_send_event(ndev, ST_NCI_APDU_READER_GATE,
+ ST_NCI_EVT_SE_SOFT_RESET, NULL, 0);
+ }
+
+ if (r < 0) {
+ /*
+ * The activation procedure failed, the secure element
+ * is not connected. Remove from the list.
+ */
+ nfc_remove_se(ndev->nfc_dev, se_idx);
+ return r;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(st_nci_enable_se);
+
+static int st_nci_hci_network_init(struct nci_dev *ndev)
+{
+ struct st_nci_info *info = nci_get_drvdata(ndev);
+ struct core_conn_create_dest_spec_params *dest_params;
+ struct dest_spec_params spec_params;
+ struct nci_conn_info *conn_info;
+ int r, dev_num;
+
+ dest_params =
+ kzalloc(sizeof(struct core_conn_create_dest_spec_params) +
+ sizeof(struct dest_spec_params), GFP_KERNEL);
+ if (dest_params == NULL) {
+ r = -ENOMEM;
+ goto exit;
+ }
+
+ dest_params->type = NCI_DESTINATION_SPECIFIC_PARAM_NFCEE_TYPE;
+ dest_params->length = sizeof(struct dest_spec_params);
+ spec_params.id = ndev->hci_dev->nfcee_id;
+ spec_params.protocol = NCI_NFCEE_INTERFACE_HCI_ACCESS;
+ memcpy(dest_params->value, &spec_params,
+ sizeof(struct dest_spec_params));
+ 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);
+ if (r != NCI_STATUS_OK)
+ goto free_dest_params;
+
+ conn_info = ndev->hci_dev->conn_info;
+ if (!conn_info)
+ goto free_dest_params;
+
+ ndev->hci_dev->init_data.gate_count = ARRAY_SIZE(st_nci_gates);
+ memcpy(ndev->hci_dev->init_data.gates, st_nci_gates,
+ sizeof(st_nci_gates));
+
+ /*
+ * Session id must include the driver name + i2c bus addr
+ * persistent info to discriminate 2 identical chips
+ */
+ dev_num = find_first_zero_bit(dev_mask, ST_NCI_NUM_DEVICES);
+ if (dev_num >= ST_NCI_NUM_DEVICES) {
+ r = -ENODEV;
+ goto free_dest_params;
+ }
+
+ scnprintf(ndev->hci_dev->init_data.session_id,
+ sizeof(ndev->hci_dev->init_data.session_id),
+ "%s%2x", "ST21BH", dev_num);
+
+ r = nci_hci_dev_session_init(ndev);
+ if (r != NCI_HCI_ANY_OK)
+ goto free_dest_params;
+
+ /*
+ * In factory mode, we prevent secure elements activation
+ * by disabling nfcee on the current HCI connection id.
+ * HCI will be used here only for proprietary commands.
+ */
+ if (test_bit(ST_NCI_FACTORY_MODE, &info->flags))
+ r = nci_nfcee_mode_set(ndev,
+ ndev->hci_dev->conn_info->dest_params->id,
+ NCI_NFCEE_DISABLE);
+ else
+ r = nci_nfcee_mode_set(ndev,
+ ndev->hci_dev->conn_info->dest_params->id,
+ NCI_NFCEE_ENABLE);
+
+free_dest_params:
+ kfree(dest_params);
+
+exit:
+ return r;
+}
+
+int st_nci_discover_se(struct nci_dev *ndev)
+{
+ u8 white_list[2];
+ int r, wl_size = 0;
+ int se_count = 0;
+ struct st_nci_info *info = nci_get_drvdata(ndev);
+
+ pr_debug("st_nci_discover_se\n");
+
+ r = st_nci_hci_network_init(ndev);
+ if (r != 0)
+ return r;
+
+ if (test_bit(ST_NCI_FACTORY_MODE, &info->flags))
+ return 0;
+
+ if (info->se_info.se_status->is_uicc_present)
+ white_list[wl_size++] = ST_NCI_UICC_HOST_ID;
+ if (info->se_info.se_status->is_ese_present)
+ white_list[wl_size++] = ST_NCI_ESE_HOST_ID;
+
+ if (wl_size) {
+ r = nci_hci_set_param(ndev, NCI_HCI_ADMIN_GATE,
+ NCI_HCI_ADMIN_PARAM_WHITELIST,
+ white_list, wl_size);
+ if (r != NCI_HCI_ANY_OK)
+ return r;
+ }
+
+ if (info->se_info.se_status->is_uicc_present) {
+ nfc_add_se(ndev->nfc_dev, ST_NCI_UICC_HOST_ID, NFC_SE_UICC);
+ se_count++;
+ }
+
+ if (info->se_info.se_status->is_ese_present) {
+ nfc_add_se(ndev->nfc_dev, ST_NCI_ESE_HOST_ID, NFC_SE_EMBEDDED);
+ se_count++;
+ }
+
+ return !se_count;
+}
+EXPORT_SYMBOL_GPL(st_nci_discover_se);
+
+int st_nci_se_io(struct nci_dev *ndev, u32 se_idx,
+ u8 *apdu, size_t apdu_length,
+ se_io_cb_t cb, void *cb_context)
+{
+ struct st_nci_info *info = nci_get_drvdata(ndev);
+
+ pr_debug("\n");
+
+ switch (se_idx) {
+ case ST_NCI_ESE_HOST_ID:
+ info->se_info.cb = cb;
+ info->se_info.cb_context = cb_context;
+ mod_timer(&info->se_info.bwi_timer, jiffies +
+ msecs_to_jiffies(info->se_info.wt_timeout));
+ info->se_info.bwi_active = true;
+ return nci_hci_send_event(ndev, ST_NCI_APDU_READER_GATE,
+ ST_NCI_EVT_TRANSMIT_DATA, apdu,
+ apdu_length);
+ default:
+ return -ENODEV;
+ }
+}
+EXPORT_SYMBOL(st_nci_se_io);
+
+static void st_nci_se_wt_timeout(unsigned long data)
+{
+ /*
+ * No answer from the secure element
+ * within the defined timeout.
+ * Let's send a reset request as recovery procedure.
+ * According to the situation, we first try to send a software reset
+ * to the secure element. If the next command is still not
+ * answering in time, we send to the CLF a secure element hardware
+ * reset request.
+ */
+ /* hardware reset managed through VCC_UICC_OUT power supply */
+ u8 param = 0x01;
+ struct st_nci_info *info = (struct st_nci_info *) data;
+
+ pr_debug("\n");
+
+ info->se_info.bwi_active = false;
+
+ if (!info->se_info.xch_error) {
+ info->se_info.xch_error = true;
+ nci_hci_send_event(info->ndlc->ndev, ST_NCI_APDU_READER_GATE,
+ ST_NCI_EVT_SE_SOFT_RESET, NULL, 0);
+ } else {
+ info->se_info.xch_error = false;
+ nci_hci_send_event(info->ndlc->ndev, ST_NCI_DEVICE_MGNT_GATE,
+ ST_NCI_EVT_SE_HARD_RESET, &param, 1);
+ }
+ info->se_info.cb(info->se_info.cb_context, NULL, 0, -ETIME);
+}
+
+static void st_nci_se_activation_timeout(unsigned long data)
+{
+ struct st_nci_info *info = (struct st_nci_info *) data;
+
+ pr_debug("\n");
+
+ info->se_info.se_active = false;
+
+ complete(&info->se_info.req_completion);
+}
+
+int st_nci_se_init(struct nci_dev *ndev, struct st_nci_se_status *se_status)
+{
+ struct st_nci_info *info = nci_get_drvdata(ndev);
+
+ init_completion(&info->se_info.req_completion);
+ /* initialize timers */
+ init_timer(&info->se_info.bwi_timer);
+ info->se_info.bwi_timer.data = (unsigned long)info;
+ info->se_info.bwi_timer.function = st_nci_se_wt_timeout;
+ info->se_info.bwi_active = false;
+
+ init_timer(&info->se_info.se_active_timer);
+ info->se_info.se_active_timer.data = (unsigned long)info;
+ info->se_info.se_active_timer.function =
+ st_nci_se_activation_timeout;
+ info->se_info.se_active = false;
+
+ info->se_info.xch_error = false;
+
+ info->se_info.wt_timeout =
+ ST_NCI_BWI_TO_TIMEOUT(ST_NCI_ATR_DEFAULT_BWI);
+
+ info->se_info.se_status = se_status;
+
+ return 0;
+}
+EXPORT_SYMBOL(st_nci_se_init);
+
+void st_nci_se_deinit(struct nci_dev *ndev)
+{
+ struct st_nci_info *info = nci_get_drvdata(ndev);
+
+ if (info->se_info.bwi_active)
+ del_timer_sync(&info->se_info.bwi_timer);
+ if (info->se_info.se_active)
+ del_timer_sync(&info->se_info.se_active_timer);
+
+ info->se_info.se_active = false;
+ info->se_info.bwi_active = false;
+}
+EXPORT_SYMBOL(st_nci_se_deinit);
+
diff --git a/drivers/nfc/st-nci/spi.c b/drivers/nfc/st-nci/spi.c
new file mode 100644
index 0000000..6493e17
--- /dev/null
+++ b/drivers/nfc/st-nci/spi.c
@@ -0,0 +1,442 @@
+/*
+ * SPI Link Layer for ST NCI based Driver
+ * Copyright (C) 2014-2015 STMicroelectronics SAS. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_gpio.h>
+#include <linux/acpi.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/nfc.h>
+#include <net/nfc/nci.h>
+#include <linux/platform_data/st-nci.h>
+
+#include "st-nci.h"
+
+#define DRIVER_DESC "NCI NFC driver for ST_NCI"
+
+/* ndlc header */
+#define ST_NCI_FRAME_HEADROOM 1
+#define ST_NCI_FRAME_TAILROOM 0
+
+#define ST_NCI_SPI_MIN_SIZE 4 /* PCB(1) + NCI Packet header(3) */
+#define ST_NCI_SPI_MAX_SIZE 250 /* req 4.2.1 */
+
+#define ST_NCI_SPI_DRIVER_NAME "st_nci_spi"
+
+#define ST_NCI_GPIO_NAME_RESET "reset"
+
+struct st_nci_spi_phy {
+ struct spi_device *spi_dev;
+ struct llt_ndlc *ndlc;
+
+ bool irq_active;
+
+ unsigned int gpio_reset;
+ unsigned int irq_polarity;
+
+ struct st_nci_se_status se_status;
+};
+
+static int st_nci_spi_enable(void *phy_id)
+{
+ struct st_nci_spi_phy *phy = phy_id;
+
+ gpio_set_value(phy->gpio_reset, 0);
+ usleep_range(10000, 15000);
+ gpio_set_value(phy->gpio_reset, 1);
+ usleep_range(80000, 85000);
+
+ if (phy->ndlc->powered == 0 && phy->irq_active == 0) {
+ enable_irq(phy->spi_dev->irq);
+ phy->irq_active = true;
+ }
+
+ return 0;
+}
+
+static void st_nci_spi_disable(void *phy_id)
+{
+ struct st_nci_spi_phy *phy = phy_id;
+
+ disable_irq_nosync(phy->spi_dev->irq);
+ phy->irq_active = false;
+}
+
+/*
+ * Writing a frame must not return the number of written bytes.
+ * It must return either zero for success, or <0 for error.
+ * In addition, it must not alter the skb
+ */
+static int st_nci_spi_write(void *phy_id, struct sk_buff *skb)
+{
+ int r;
+ struct st_nci_spi_phy *phy = phy_id;
+ struct spi_device *dev = phy->spi_dev;
+ struct sk_buff *skb_rx;
+ u8 buf[ST_NCI_SPI_MAX_SIZE + NCI_DATA_HDR_SIZE +
+ ST_NCI_FRAME_HEADROOM + ST_NCI_FRAME_TAILROOM];
+ struct spi_transfer spi_xfer = {
+ .tx_buf = skb->data,
+ .rx_buf = buf,
+ .len = skb->len,
+ };
+
+ if (phy->ndlc->hard_fault != 0)
+ return phy->ndlc->hard_fault;
+
+ r = spi_sync_transfer(dev, &spi_xfer, 1);
+ /*
+ * We may have received some valuable data on miso line.
+ * Send them back in the ndlc state machine.
+ */
+ if (!r) {
+ skb_rx = alloc_skb(skb->len, GFP_KERNEL);
+ if (!skb_rx) {
+ r = -ENOMEM;
+ goto exit;
+ }
+
+ skb_put(skb_rx, skb->len);
+ memcpy(skb_rx->data, buf, skb->len);
+ ndlc_recv(phy->ndlc, skb_rx);
+ }
+
+exit:
+ return r;
+}
+
+/*
+ * Reads an ndlc frame and returns it in a newly allocated sk_buff.
+ * returns:
+ * 0 : if received frame is complete
+ * -EREMOTEIO : i2c read error (fatal)
+ * -EBADMSG : frame was incorrect and discarded
+ * -ENOMEM : cannot allocate skb, frame dropped
+ */
+static int st_nci_spi_read(struct st_nci_spi_phy *phy,
+ struct sk_buff **skb)
+{
+ int r;
+ u8 len;
+ u8 buf[ST_NCI_SPI_MAX_SIZE];
+ struct spi_device *dev = phy->spi_dev;
+ struct spi_transfer spi_xfer = {
+ .rx_buf = buf,
+ .len = ST_NCI_SPI_MIN_SIZE,
+ };
+
+ r = spi_sync_transfer(dev, &spi_xfer, 1);
+ if (r < 0)
+ return -EREMOTEIO;
+
+ len = be16_to_cpu(*(__be16 *) (buf + 2));
+ if (len > ST_NCI_SPI_MAX_SIZE) {
+ nfc_err(&dev->dev, "invalid frame len\n");
+ phy->ndlc->hard_fault = 1;
+ return -EBADMSG;
+ }
+
+ *skb = alloc_skb(ST_NCI_SPI_MIN_SIZE + len, GFP_KERNEL);
+ if (*skb == NULL)
+ return -ENOMEM;
+
+ skb_reserve(*skb, ST_NCI_SPI_MIN_SIZE);
+ skb_put(*skb, ST_NCI_SPI_MIN_SIZE);
+ memcpy((*skb)->data, buf, ST_NCI_SPI_MIN_SIZE);
+
+ if (!len)
+ return 0;
+
+ spi_xfer.len = len;
+ r = spi_sync_transfer(dev, &spi_xfer, 1);
+ if (r < 0) {
+ kfree_skb(*skb);
+ return -EREMOTEIO;
+ }
+
+ skb_put(*skb, len);
+ memcpy((*skb)->data + ST_NCI_SPI_MIN_SIZE, buf, len);
+
+ return 0;
+}
+
+/*
+ * Reads an ndlc frame from the chip.
+ *
+ * On ST21NFCB, IRQ goes in idle state when read starts.
+ */
+static irqreturn_t st_nci_irq_thread_fn(int irq, void *phy_id)
+{
+ struct st_nci_spi_phy *phy = phy_id;
+ struct spi_device *dev;
+ struct sk_buff *skb = NULL;
+ int r;
+
+ if (!phy || !phy->ndlc || irq != phy->spi_dev->irq) {
+ WARN_ON_ONCE(1);
+ return IRQ_NONE;
+ }
+
+ dev = phy->spi_dev;
+ dev_dbg(&dev->dev, "IRQ\n");
+
+ if (phy->ndlc->hard_fault)
+ return IRQ_HANDLED;
+
+ if (!phy->ndlc->powered) {
+ st_nci_spi_disable(phy);
+ return IRQ_HANDLED;
+ }
+
+ r = st_nci_spi_read(phy, &skb);
+ if (r == -EREMOTEIO || r == -ENOMEM || r == -EBADMSG)
+ return IRQ_HANDLED;
+
+ ndlc_recv(phy->ndlc, skb);
+
+ return IRQ_HANDLED;
+}
+
+static struct nfc_phy_ops spi_phy_ops = {
+ .write = st_nci_spi_write,
+ .enable = st_nci_spi_enable,
+ .disable = st_nci_spi_disable,
+};
+
+static int st_nci_spi_acpi_request_resources(struct spi_device *spi_dev)
+{
+ struct st_nci_spi_phy *phy = spi_get_drvdata(spi_dev);
+ struct gpio_desc *gpiod_reset;
+ struct device *dev = &spi_dev->dev;
+ u8 tmp;
+
+ /* Get RESET GPIO from ACPI */
+ gpiod_reset = devm_gpiod_get_index(dev, ST_NCI_GPIO_NAME_RESET, 1,
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(gpiod_reset)) {
+ nfc_err(dev, "Unable to get RESET GPIO\n");
+ return -ENODEV;
+ }
+
+ phy->gpio_reset = desc_to_gpio(gpiod_reset);
+
+ phy->irq_polarity = irq_get_trigger_type(spi_dev->irq);
+
+ phy->se_status.is_ese_present = false;
+ phy->se_status.is_uicc_present = false;
+
+ if (device_property_present(dev, "ese-present")) {
+ device_property_read_u8(dev, "ese-present", &tmp);
+ tmp = phy->se_status.is_ese_present;
+ }
+
+ if (device_property_present(dev, "uicc-present")) {
+ device_property_read_u8(dev, "uicc-present", &tmp);
+ tmp = phy->se_status.is_uicc_present;
+ }
+
+ return 0;
+}
+
+static int st_nci_spi_of_request_resources(struct spi_device *dev)
+{
+ struct st_nci_spi_phy *phy = spi_get_drvdata(dev);
+ struct device_node *pp;
+ int gpio;
+ int r;
+
+ pp = dev->dev.of_node;
+ if (!pp)
+ return -ENODEV;
+
+ /* Get GPIO from device tree */
+ gpio = of_get_named_gpio(pp, "reset-gpios", 0);
+ if (gpio < 0) {
+ nfc_err(&dev->dev,
+ "Failed to retrieve reset-gpios from device tree\n");
+ return gpio;
+ }
+
+ /* GPIO request and configuration */
+ r = devm_gpio_request_one(&dev->dev, gpio,
+ GPIOF_OUT_INIT_HIGH, ST_NCI_GPIO_NAME_RESET);
+ if (r) {
+ nfc_err(&dev->dev, "Failed to request reset pin\n");
+ return r;
+ }
+ phy->gpio_reset = gpio;
+
+ phy->irq_polarity = irq_get_trigger_type(dev->irq);
+
+ phy->se_status.is_ese_present =
+ of_property_read_bool(pp, "ese-present");
+ phy->se_status.is_uicc_present =
+ of_property_read_bool(pp, "uicc-present");
+
+ return 0;
+}
+
+static int st_nci_spi_request_resources(struct spi_device *dev)
+{
+ struct st_nci_nfc_platform_data *pdata;
+ struct st_nci_spi_phy *phy = spi_get_drvdata(dev);
+ int r;
+
+ pdata = dev->dev.platform_data;
+ if (pdata == NULL) {
+ nfc_err(&dev->dev, "No platform data\n");
+ return -EINVAL;
+ }
+
+ /* store for later use */
+ phy->gpio_reset = pdata->gpio_reset;
+ phy->irq_polarity = pdata->irq_polarity;
+
+ r = devm_gpio_request_one(&dev->dev,
+ phy->gpio_reset, GPIOF_OUT_INIT_HIGH,
+ ST_NCI_GPIO_NAME_RESET);
+ if (r) {
+ pr_err("%s : reset gpio_request failed\n", __FILE__);
+ return r;
+ }
+
+ phy->se_status.is_ese_present = pdata->is_ese_present;
+ phy->se_status.is_uicc_present = pdata->is_uicc_present;
+
+ return 0;
+}
+
+static int st_nci_spi_probe(struct spi_device *dev)
+{
+ struct st_nci_spi_phy *phy;
+ struct st_nci_nfc_platform_data *pdata;
+ int r;
+
+ dev_dbg(&dev->dev, "%s\n", __func__);
+ dev_dbg(&dev->dev, "IRQ: %d\n", dev->irq);
+
+ /* Check SPI platform functionnalities */
+ if (!dev) {
+ pr_debug("%s: dev is NULL. Device is not accessible.\n",
+ __func__);
+ return -ENODEV;
+ }
+
+ phy = devm_kzalloc(&dev->dev, sizeof(struct st_nci_spi_phy),
+ GFP_KERNEL);
+ if (!phy)
+ return -ENOMEM;
+
+ phy->spi_dev = dev;
+
+ spi_set_drvdata(dev, phy);
+
+ pdata = dev->dev.platform_data;
+ if (!pdata && dev->dev.of_node) {
+ r = st_nci_spi_of_request_resources(dev);
+ if (r) {
+ nfc_err(&dev->dev, "No platform data\n");
+ return r;
+ }
+ } else if (pdata) {
+ r = st_nci_spi_request_resources(dev);
+ if (r) {
+ nfc_err(&dev->dev,
+ "Cannot get platform resources\n");
+ return r;
+ }
+ } else if (ACPI_HANDLE(&dev->dev)) {
+ r = st_nci_spi_acpi_request_resources(dev);
+ if (r) {
+ nfc_err(&dev->dev, "Cannot get ACPI data\n");
+ return r;
+ }
+ } else {
+ nfc_err(&dev->dev,
+ "st_nci platform resources not available\n");
+ return -ENODEV;
+ }
+
+ r = ndlc_probe(phy, &spi_phy_ops, &dev->dev,
+ ST_NCI_FRAME_HEADROOM, ST_NCI_FRAME_TAILROOM,
+ &phy->ndlc, &phy->se_status);
+ if (r < 0) {
+ nfc_err(&dev->dev, "Unable to register ndlc layer\n");
+ return r;
+ }
+
+ phy->irq_active = true;
+ r = devm_request_threaded_irq(&dev->dev, dev->irq, NULL,
+ st_nci_irq_thread_fn,
+ phy->irq_polarity | IRQF_ONESHOT,
+ ST_NCI_SPI_DRIVER_NAME, phy);
+ if (r < 0)
+ nfc_err(&dev->dev, "Unable to register IRQ handler\n");
+
+ return r;
+}
+
+static int st_nci_spi_remove(struct spi_device *dev)
+{
+ struct st_nci_spi_phy *phy = spi_get_drvdata(dev);
+
+ dev_dbg(&dev->dev, "%s\n", __func__);
+
+ ndlc_remove(phy->ndlc);
+
+ return 0;
+}
+
+static struct spi_device_id st_nci_spi_id_table[] = {
+ {ST_NCI_SPI_DRIVER_NAME, 0},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, st_nci_spi_id_table);
+
+static const struct acpi_device_id st_nci_spi_acpi_match[] = {
+ {"SMO2101", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, st_nci_spi_acpi_match);
+
+static const struct of_device_id of_st_nci_spi_match[] = {
+ { .compatible = "st,st21nfcb-spi", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, of_st_nci_spi_match);
+
+static struct spi_driver st_nci_spi_driver = {
+ .driver = {
+ .name = ST_NCI_SPI_DRIVER_NAME,
+ .of_match_table = of_match_ptr(of_st_nci_spi_match),
+ .acpi_match_table = ACPI_PTR(st_nci_spi_acpi_match),
+ },
+ .probe = st_nci_spi_probe,
+ .id_table = st_nci_spi_id_table,
+ .remove = st_nci_spi_remove,
+};
+module_spi_driver(st_nci_spi_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/nfc/st-nci/st-nci.h b/drivers/nfc/st-nci/st-nci.h
new file mode 100644
index 0000000..afaf138
--- /dev/null
+++ b/drivers/nfc/st-nci/st-nci.h
@@ -0,0 +1,153 @@
+/*
+ * NCI based Driver for STMicroelectronics NFC Chip
+ *
+ * Copyright (C) 2014 STMicroelectronics SAS. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LOCAL_ST_NCI_H_
+#define __LOCAL_ST_NCI_H_
+
+#include "ndlc.h"
+
+/* Define private flags: */
+#define ST_NCI_RUNNING 1
+
+#define ST_NCI_CORE_PROP 0x01
+#define ST_NCI_SET_NFC_MODE 0x02
+
+/*
+ * ref ISO7816-3 chap 8.1. the initial character TS is followed by a
+ * sequence of at most 32 characters.
+ */
+#define ST_NCI_ESE_MAX_LENGTH 33
+
+#define ST_NCI_DEVICE_MGNT_GATE 0x01
+
+#define ST_NCI_VENDOR_OUI 0x0080E1 /* STMicroelectronics */
+#define ST_NCI_FACTORY_MODE 2
+
+struct nci_mode_set_cmd {
+ u8 cmd_type;
+ u8 mode;
+} __packed;
+
+struct nci_mode_set_rsp {
+ u8 status;
+} __packed;
+
+struct st_nci_se_status {
+ bool is_ese_present;
+ bool is_uicc_present;
+};
+
+struct st_nci_se_info {
+ struct st_nci_se_status *se_status;
+ u8 atr[ST_NCI_ESE_MAX_LENGTH];
+ struct completion req_completion;
+
+ struct timer_list bwi_timer;
+ int wt_timeout; /* in msecs */
+ bool bwi_active;
+
+ struct timer_list se_active_timer;
+ bool se_active;
+
+ bool xch_error;
+
+ se_io_cb_t cb;
+ void *cb_context;
+};
+
+/**
+ * enum nfc_vendor_cmds - supported nfc vendor commands
+ *
+ * @FACTORY_MODE: Allow to set the driver into a mode where no secure element
+ * are activated. It does not consider any NFC_ATTR_VENDOR_DATA.
+ * @HCI_CLEAR_ALL_PIPES: Allow to execute a HCI clear all pipes command.
+ * It does not consider any NFC_ATTR_VENDOR_DATA.
+ * @HCI_DM_PUT_DATA: Allow to configure specific CLF registry as for example
+ * RF trimmings or low level drivers configurations (I2C, SPI, SWP).
+ * @HCI_DM_UPDATE_AID: Allow to configure an AID routing into the CLF routing
+ * table following RF technology, CLF mode or protocol.
+ * @HCI_DM_GET_INFO: Allow to retrieve CLF information.
+ * @HCI_DM_GET_DATA: Allow to retrieve CLF configurable data such as low
+ * level drivers configurations or RF trimmings.
+ * @HCI_DM_DIRECT_LOAD: Allow to load a firmware into the CLF. A complete
+ * packet can be more than 8KB.
+ * @HCI_DM_RESET: Allow to run a CLF reset in order to "commit" CLF
+ * configuration changes without CLF power off.
+ * @HCI_GET_PARAM: Allow to retrieve an HCI CLF parameter (for example the
+ * white list).
+ * @HCI_DM_FIELD_GENERATOR: Allow to generate different kind of RF
+ * technology. When using this command to anti-collision is done.
+ * @LOOPBACK: Allow to echo a command and test the Dh to CLF connectivity.
+ * @HCI_DM_VDC_MEASUREMENT_VALUE: Allow to measure the field applied on the
+ * CLF antenna. A value between 0 and 0x0f is returned. 0 is maximum.
+ * @HCI_DM_FWUPD_START: Allow to put CLF into firmware update mode. It is a
+ * specific CLF command as there is no GPIO for this.
+ * @HCI_DM_FWUPD_END: Allow to complete firmware update.
+ * @HCI_DM_VDC_VALUE_COMPARISON: Allow to compare the field applied on the
+ * CLF antenna to a reference value.
+ * @MANUFACTURER_SPECIFIC: Allow to retrieve manufacturer specific data
+ * received during a NCI_CORE_INIT_CMD.
+ */
+enum nfc_vendor_cmds {
+ FACTORY_MODE,
+ HCI_CLEAR_ALL_PIPES,
+ HCI_DM_PUT_DATA,
+ HCI_DM_UPDATE_AID,
+ HCI_DM_GET_INFO,
+ HCI_DM_GET_DATA,
+ HCI_DM_DIRECT_LOAD,
+ HCI_DM_RESET,
+ HCI_GET_PARAM,
+ HCI_DM_FIELD_GENERATOR,
+ LOOPBACK,
+ HCI_DM_FWUPD_START,
+ HCI_DM_FWUPD_END,
+ HCI_DM_VDC_MEASUREMENT_VALUE,
+ HCI_DM_VDC_VALUE_COMPARISON,
+ MANUFACTURER_SPECIFIC,
+};
+
+struct st_nci_info {
+ struct llt_ndlc *ndlc;
+ unsigned long flags;
+
+ struct st_nci_se_info se_info;
+};
+
+void st_nci_remove(struct nci_dev *ndev);
+int st_nci_probe(struct llt_ndlc *ndlc, int phy_headroom,
+ int phy_tailroom, struct st_nci_se_status *se_status);
+
+int st_nci_se_init(struct nci_dev *ndev, struct st_nci_se_status *se_status);
+void st_nci_se_deinit(struct nci_dev *ndev);
+
+int st_nci_discover_se(struct nci_dev *ndev);
+int st_nci_enable_se(struct nci_dev *ndev, u32 se_idx);
+int st_nci_disable_se(struct nci_dev *ndev, u32 se_idx);
+int st_nci_se_io(struct nci_dev *ndev, u32 se_idx,
+ u8 *apdu, size_t apdu_length,
+ se_io_cb_t cb, void *cb_context);
+int st_nci_hci_load_session(struct nci_dev *ndev);
+void st_nci_hci_event_received(struct nci_dev *ndev, u8 pipe,
+ u8 event, struct sk_buff *skb);
+void st_nci_hci_cmd_received(struct nci_dev *ndev, u8 pipe, u8 cmd,
+ struct sk_buff *skb);
+
+int st_nci_vendor_cmds_init(struct nci_dev *ndev);
+
+#endif /* __LOCAL_ST_NCI_H_ */
diff --git a/drivers/nfc/st-nci/vendor_cmds.c b/drivers/nfc/st-nci/vendor_cmds.c
new file mode 100644
index 0000000..1a836c7
--- /dev/null
+++ b/drivers/nfc/st-nci/vendor_cmds.c
@@ -0,0 +1,478 @@
+/*
+ * Proprietary commands extension for STMicroelectronics NFC NCI Chip
+ *
+ * Copyright (C) 2014-2015 STMicroelectronics SAS. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <net/genetlink.h>
+#include <linux/module.h>
+#include <linux/nfc.h>
+#include <linux/delay.h>
+#include <net/nfc/nci_core.h>
+
+#include "st-nci.h"
+
+#define ST_NCI_HCI_DM_GETDATA 0x10
+#define ST_NCI_HCI_DM_PUTDATA 0x11
+#define ST_NCI_HCI_DM_LOAD 0x12
+#define ST_NCI_HCI_DM_GETINFO 0x13
+#define ST_NCI_HCI_DM_FWUPD_START 0x14
+#define ST_NCI_HCI_DM_FWUPD_STOP 0x15
+#define ST_NCI_HCI_DM_UPDATE_AID 0x20
+#define ST_NCI_HCI_DM_RESET 0x3e
+
+#define ST_NCI_HCI_DM_FIELD_GENERATOR 0x32
+#define ST_NCI_HCI_DM_VDC_MEASUREMENT_VALUE 0x33
+#define ST_NCI_HCI_DM_VDC_VALUE_COMPARISON 0x34
+
+#define ST_NCI_FACTORY_MODE_ON 1
+#define ST_NCI_FACTORY_MODE_OFF 0
+
+#define ST_NCI_EVT_POST_DATA 0x02
+
+struct get_param_data {
+ u8 gate;
+ u8 data;
+} __packed;
+
+static int st_nci_factory_mode(struct nfc_dev *dev, void *data,
+ size_t data_len)
+{
+ struct nci_dev *ndev = nfc_get_drvdata(dev);
+ struct st_nci_info *info = nci_get_drvdata(ndev);
+
+ if (data_len != 1)
+ return -EINVAL;
+
+ pr_debug("factory mode: %x\n", ((u8 *)data)[0]);
+
+ switch (((u8 *)data)[0]) {
+ case ST_NCI_FACTORY_MODE_ON:
+ test_and_set_bit(ST_NCI_FACTORY_MODE, &info->flags);
+ break;
+ case ST_NCI_FACTORY_MODE_OFF:
+ clear_bit(ST_NCI_FACTORY_MODE, &info->flags);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int st_nci_hci_clear_all_pipes(struct nfc_dev *dev, void *data,
+ size_t data_len)
+{
+ struct nci_dev *ndev = nfc_get_drvdata(dev);
+
+ return nci_hci_clear_all_pipes(ndev);
+}
+
+static int st_nci_hci_dm_put_data(struct nfc_dev *dev, void *data,
+ size_t data_len)
+{
+ struct nci_dev *ndev = nfc_get_drvdata(dev);
+
+ return nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
+ ST_NCI_HCI_DM_PUTDATA, data,
+ data_len, NULL);
+}
+
+static int st_nci_hci_dm_update_aid(struct nfc_dev *dev, void *data,
+ size_t data_len)
+{
+ struct nci_dev *ndev = nfc_get_drvdata(dev);
+
+ return nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
+ ST_NCI_HCI_DM_UPDATE_AID, data, data_len, NULL);
+}
+
+static int st_nci_hci_dm_get_info(struct nfc_dev *dev, void *data,
+ size_t data_len)
+{
+ int r;
+ struct sk_buff *msg, *skb;
+ struct nci_dev *ndev = nfc_get_drvdata(dev);
+
+ r = nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE, ST_NCI_HCI_DM_GETINFO,
+ data, data_len, &skb);
+ if (r)
+ goto exit;
+
+ msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI,
+ HCI_DM_GET_INFO, skb->len);
+ if (!msg) {
+ r = -ENOMEM;
+ goto free_skb;
+ }
+
+ if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
+ kfree_skb(msg);
+ r = -ENOBUFS;
+ goto free_skb;
+ }
+
+ r = nfc_vendor_cmd_reply(msg);
+
+free_skb:
+ kfree_skb(skb);
+exit:
+ return r;
+}
+
+static int st_nci_hci_dm_get_data(struct nfc_dev *dev, void *data,
+ size_t data_len)
+{
+ int r;
+ struct sk_buff *msg, *skb;
+ struct nci_dev *ndev = nfc_get_drvdata(dev);
+
+ r = nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE, ST_NCI_HCI_DM_GETDATA,
+ data, data_len, &skb);
+ if (r)
+ goto exit;
+
+ msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI,
+ HCI_DM_GET_DATA, skb->len);
+ if (!msg) {
+ r = -ENOMEM;
+ goto free_skb;
+ }
+
+ if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
+ kfree_skb(msg);
+ r = -ENOBUFS;
+ goto free_skb;
+ }
+
+ r = nfc_vendor_cmd_reply(msg);
+
+free_skb:
+ kfree_skb(skb);
+exit:
+ return r;
+}
+
+static int st_nci_hci_dm_fwupd_start(struct nfc_dev *dev, void *data,
+ size_t data_len)
+{
+ int r;
+ struct nci_dev *ndev = nfc_get_drvdata(dev);
+
+ dev->fw_download_in_progress = true;
+ r = nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
+ ST_NCI_HCI_DM_FWUPD_START, data, data_len, NULL);
+ if (r)
+ dev->fw_download_in_progress = false;
+
+ return r;
+}
+
+static int st_nci_hci_dm_fwupd_end(struct nfc_dev *dev, void *data,
+ size_t data_len)
+{
+ struct nci_dev *ndev = nfc_get_drvdata(dev);
+
+ return nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
+ ST_NCI_HCI_DM_FWUPD_STOP, data, data_len, NULL);
+}
+
+static int st_nci_hci_dm_direct_load(struct nfc_dev *dev, void *data,
+ size_t data_len)
+{
+ struct nci_dev *ndev = nfc_get_drvdata(dev);
+
+ if (dev->fw_download_in_progress) {
+ dev->fw_download_in_progress = false;
+ return nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
+ ST_NCI_HCI_DM_LOAD, data, data_len, NULL);
+ }
+ return -EPROTO;
+}
+
+static int st_nci_hci_dm_reset(struct nfc_dev *dev, void *data,
+ size_t data_len)
+{
+ struct nci_dev *ndev = nfc_get_drvdata(dev);
+
+ nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
+ ST_NCI_HCI_DM_RESET, data, data_len, NULL);
+ msleep(200);
+
+ return 0;
+}
+
+static int st_nci_hci_get_param(struct nfc_dev *dev, void *data,
+ size_t data_len)
+{
+ int r;
+ struct sk_buff *msg, *skb;
+ struct nci_dev *ndev = nfc_get_drvdata(dev);
+ struct get_param_data *param = (struct get_param_data *)data;
+
+ if (data_len < sizeof(struct get_param_data))
+ return -EPROTO;
+
+ r = nci_hci_get_param(ndev, param->gate, param->data, &skb);
+ if (r)
+ goto exit;
+
+ msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI,
+ HCI_GET_PARAM, skb->len);
+ if (!msg) {
+ r = -ENOMEM;
+ goto free_skb;
+ }
+
+ if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
+ kfree_skb(msg);
+ r = -ENOBUFS;
+ goto free_skb;
+ }
+
+ r = nfc_vendor_cmd_reply(msg);
+
+free_skb:
+ kfree_skb(skb);
+exit:
+ return r;
+}
+
+static int st_nci_hci_dm_field_generator(struct nfc_dev *dev, void *data,
+ size_t data_len)
+{
+ struct nci_dev *ndev = nfc_get_drvdata(dev);
+
+ return nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
+ ST_NCI_HCI_DM_FIELD_GENERATOR, data, data_len, NULL);
+}
+
+static int st_nci_hci_dm_vdc_measurement_value(struct nfc_dev *dev, void *data,
+ size_t data_len)
+{
+ int r;
+ struct sk_buff *msg, *skb;
+ struct nci_dev *ndev = nfc_get_drvdata(dev);
+
+ if (data_len != 4)
+ return -EPROTO;
+
+ r = nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
+ ST_NCI_HCI_DM_VDC_MEASUREMENT_VALUE,
+ data, data_len, &skb);
+ if (r)
+ goto exit;
+
+ msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI,
+ HCI_DM_VDC_MEASUREMENT_VALUE, skb->len);
+ if (!msg) {
+ r = -ENOMEM;
+ goto free_skb;
+ }
+
+ if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
+ kfree_skb(msg);
+ r = -ENOBUFS;
+ goto free_skb;
+ }
+
+ r = nfc_vendor_cmd_reply(msg);
+
+free_skb:
+ kfree_skb(skb);
+exit:
+ return r;
+}
+
+static int st_nci_hci_dm_vdc_value_comparison(struct nfc_dev *dev, void *data,
+ size_t data_len)
+{
+ int r;
+ struct sk_buff *msg, *skb;
+ struct nci_dev *ndev = nfc_get_drvdata(dev);
+
+ if (data_len != 2)
+ return -EPROTO;
+
+ r = nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
+ ST_NCI_HCI_DM_VDC_VALUE_COMPARISON,
+ data, data_len, &skb);
+ if (r)
+ goto exit;
+
+ msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI,
+ HCI_DM_VDC_VALUE_COMPARISON, skb->len);
+ if (!msg) {
+ r = -ENOMEM;
+ goto free_skb;
+ }
+
+ if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
+ kfree_skb(msg);
+ r = -ENOBUFS;
+ goto free_skb;
+ }
+
+ r = nfc_vendor_cmd_reply(msg);
+
+free_skb:
+ kfree_skb(skb);
+exit:
+ return r;
+}
+
+static int st_nci_loopback(struct nfc_dev *dev, void *data,
+ size_t data_len)
+{
+ int r;
+ struct sk_buff *msg, *skb;
+ struct nci_dev *ndev = nfc_get_drvdata(dev);
+
+ if (data_len <= 0)
+ return -EPROTO;
+
+ r = nci_nfcc_loopback(ndev, data, data_len, &skb);
+ if (r < 0)
+ return r;
+
+ msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI,
+ LOOPBACK, skb->len);
+ if (!msg) {
+ r = -ENOMEM;
+ goto free_skb;
+ }
+
+ if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
+ kfree_skb(msg);
+ r = -ENOBUFS;
+ goto free_skb;
+ }
+
+ r = nfc_vendor_cmd_reply(msg);
+free_skb:
+ kfree_skb(skb);
+ return r;
+}
+
+static int st_nci_manufacturer_specific(struct nfc_dev *dev, void *data,
+ size_t data_len)
+{
+ struct sk_buff *msg;
+ struct nci_dev *ndev = nfc_get_drvdata(dev);
+
+ msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI,
+ MANUFACTURER_SPECIFIC,
+ sizeof(ndev->manufact_specific_info));
+ if (!msg)
+ return -ENOMEM;
+
+ if (nla_put(msg, NFC_ATTR_VENDOR_DATA, sizeof(ndev->manufact_specific_info),
+ &ndev->manufact_specific_info)) {
+ kfree_skb(msg);
+ return -ENOBUFS;
+ }
+
+ return nfc_vendor_cmd_reply(msg);
+}
+
+static struct nfc_vendor_cmd st_nci_vendor_cmds[] = {
+ {
+ .vendor_id = ST_NCI_VENDOR_OUI,
+ .subcmd = FACTORY_MODE,
+ .doit = st_nci_factory_mode,
+ },
+ {
+ .vendor_id = ST_NCI_VENDOR_OUI,
+ .subcmd = HCI_CLEAR_ALL_PIPES,
+ .doit = st_nci_hci_clear_all_pipes,
+ },
+ {
+ .vendor_id = ST_NCI_VENDOR_OUI,
+ .subcmd = HCI_DM_PUT_DATA,
+ .doit = st_nci_hci_dm_put_data,
+ },
+ {
+ .vendor_id = ST_NCI_VENDOR_OUI,
+ .subcmd = HCI_DM_UPDATE_AID,
+ .doit = st_nci_hci_dm_update_aid,
+ },
+ {
+ .vendor_id = ST_NCI_VENDOR_OUI,
+ .subcmd = HCI_DM_GET_INFO,
+ .doit = st_nci_hci_dm_get_info,
+ },
+ {
+ .vendor_id = ST_NCI_VENDOR_OUI,
+ .subcmd = HCI_DM_GET_DATA,
+ .doit = st_nci_hci_dm_get_data,
+ },
+ {
+ .vendor_id = ST_NCI_VENDOR_OUI,
+ .subcmd = HCI_DM_DIRECT_LOAD,
+ .doit = st_nci_hci_dm_direct_load,
+ },
+ {
+ .vendor_id = ST_NCI_VENDOR_OUI,
+ .subcmd = HCI_DM_RESET,
+ .doit = st_nci_hci_dm_reset,
+ },
+ {
+ .vendor_id = ST_NCI_VENDOR_OUI,
+ .subcmd = HCI_GET_PARAM,
+ .doit = st_nci_hci_get_param,
+ },
+ {
+ .vendor_id = ST_NCI_VENDOR_OUI,
+ .subcmd = HCI_DM_FIELD_GENERATOR,
+ .doit = st_nci_hci_dm_field_generator,
+ },
+ {
+ .vendor_id = ST_NCI_VENDOR_OUI,
+ .subcmd = HCI_DM_FWUPD_START,
+ .doit = st_nci_hci_dm_fwupd_start,
+ },
+ {
+ .vendor_id = ST_NCI_VENDOR_OUI,
+ .subcmd = HCI_DM_FWUPD_END,
+ .doit = st_nci_hci_dm_fwupd_end,
+ },
+ {
+ .vendor_id = ST_NCI_VENDOR_OUI,
+ .subcmd = LOOPBACK,
+ .doit = st_nci_loopback,
+ },
+ {
+ .vendor_id = ST_NCI_VENDOR_OUI,
+ .subcmd = HCI_DM_VDC_MEASUREMENT_VALUE,
+ .doit = st_nci_hci_dm_vdc_measurement_value,
+ },
+ {
+ .vendor_id = ST_NCI_VENDOR_OUI,
+ .subcmd = HCI_DM_VDC_VALUE_COMPARISON,
+ .doit = st_nci_hci_dm_vdc_value_comparison,
+ },
+ {
+ .vendor_id = ST_NCI_VENDOR_OUI,
+ .subcmd = MANUFACTURER_SPECIFIC,
+ .doit = st_nci_manufacturer_specific,
+ },
+};
+
+int st_nci_vendor_cmds_init(struct nci_dev *ndev)
+{
+ return nfc_set_vendor_cmds(ndev->nfc_dev, st_nci_vendor_cmds,
+ sizeof(st_nci_vendor_cmds));
+}
+EXPORT_SYMBOL(st_nci_vendor_cmds_init);
diff --git a/drivers/nfc/st21nfca/Kconfig b/drivers/nfc/st21nfca/Kconfig
new file mode 100644
index 0000000..e86e47a
--- /dev/null
+++ b/drivers/nfc/st21nfca/Kconfig
@@ -0,0 +1,21 @@
+config NFC_ST21NFCA
+ tristate
+ depends on m
+ depends on CRC_CCITT
+ ---help---
+ STMicroelectronics ST21NFCA core driver. It implements the chipset
+ HCI logic and hooks into the NFC kernel APIs. Physical layers will
+ register against it.
+
+config NFC_ST21NFCA_I2C
+ depends on !KERNEL_3_18
+ tristate "STMicroelectronics ST21NFCA NFC driver (I2C)"
+ depends on m
+ depends on NFC_HCI && I2C && NFC_SHDLC
+ select NFC_ST21NFCA
+ ---help---
+ This module adds support for the STMicroelectronics st21nfca i2c interface.
+ Select this if your platform is using the i2c bus.
+
+ If you choose to build a module, it'll be called st21nfca_i2c.
+ Say N if unsure.
diff --git a/drivers/nfc/st21nfca/Makefile b/drivers/nfc/st21nfca/Makefile
new file mode 100644
index 0000000..f76b5ad
--- /dev/null
+++ b/drivers/nfc/st21nfca/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for ST21NFCA HCI based NFC driver
+#
+
+st21nfca_hci-objs = core.o dep.o se.o vendor_cmds.o
+obj-$(CPTCFG_NFC_ST21NFCA) += st21nfca_hci.o
+
+st21nfca_i2c-objs = i2c.o
+obj-$(CPTCFG_NFC_ST21NFCA_I2C) += st21nfca_i2c.o
diff --git a/drivers/nfc/st21nfca/core.c b/drivers/nfc/st21nfca/core.c
new file mode 100644
index 0000000..dacb916
--- /dev/null
+++ b/drivers/nfc/st21nfca/core.c
@@ -0,0 +1,1049 @@
+/*
+ * HCI based Driver for STMicroelectronics NFC Chip
+ *
+ * Copyright (C) 2014 STMicroelectronics SAS. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/module.h>
+#include <linux/nfc.h>
+#include <net/nfc/hci.h>
+#include <net/nfc/llc.h>
+
+#include "st21nfca.h"
+
+#define DRIVER_DESC "HCI NFC driver for ST21NFCA"
+
+#define FULL_VERSION_LEN 3
+
+/* Proprietary gates, events, commands and registers */
+
+/* Commands that apply to all RF readers */
+#define ST21NFCA_RF_READER_CMD_PRESENCE_CHECK 0x30
+
+#define ST21NFCA_RF_READER_ISO15693_GATE 0x12
+#define ST21NFCA_RF_READER_ISO15693_INVENTORY 0x01
+
+/*
+ * Reader gate for communication with contact-less cards using Type A
+ * protocol ISO14443-3 but not compliant with ISO14443-4
+ */
+#define ST21NFCA_RF_READER_14443_3_A_GATE 0x15
+#define ST21NFCA_RF_READER_14443_3_A_UID 0x02
+#define ST21NFCA_RF_READER_14443_3_A_ATQA 0x03
+#define ST21NFCA_RF_READER_14443_3_A_SAK 0x04
+
+#define ST21NFCA_RF_READER_F_DATARATE 0x01
+#define ST21NFCA_RF_READER_F_DATARATE_106 0x01
+#define ST21NFCA_RF_READER_F_DATARATE_212 0x02
+#define ST21NFCA_RF_READER_F_DATARATE_424 0x04
+#define ST21NFCA_RF_READER_F_POL_REQ 0x02
+#define ST21NFCA_RF_READER_F_POL_REQ_DEFAULT 0xffff0000
+#define ST21NFCA_RF_READER_F_NFCID2 0x03
+#define ST21NFCA_RF_READER_F_NFCID1 0x04
+
+#define ST21NFCA_RF_CARD_F_MODE 0x01
+#define ST21NFCA_RF_CARD_F_NFCID2_LIST 0x04
+#define ST21NFCA_RF_CARD_F_NFCID1 0x05
+#define ST21NFCA_RF_CARD_F_SENS_RES 0x06
+#define ST21NFCA_RF_CARD_F_SEL_RES 0x07
+#define ST21NFCA_RF_CARD_F_DATARATE 0x08
+#define ST21NFCA_RF_CARD_F_DATARATE_212_424 0x01
+
+#define ST21NFCA_DEVICE_MGNT_PIPE 0x02
+
+#define ST21NFCA_DM_GETINFO 0x13
+#define ST21NFCA_DM_GETINFO_PIPE_LIST 0x02
+#define ST21NFCA_DM_GETINFO_PIPE_INFO 0x01
+#define ST21NFCA_DM_PIPE_CREATED 0x02
+#define ST21NFCA_DM_PIPE_OPEN 0x04
+#define ST21NFCA_DM_RF_ACTIVE 0x80
+#define ST21NFCA_DM_DISCONNECT 0x30
+
+#define ST21NFCA_DM_IS_PIPE_OPEN(p) \
+ ((p & 0x0f) == (ST21NFCA_DM_PIPE_CREATED | ST21NFCA_DM_PIPE_OPEN))
+
+#define ST21NFCA_NFC_MODE 0x03 /* NFC_MODE parameter*/
+
+#define ST21NFCA_EVT_HOT_PLUG 0x03
+#define ST21NFCA_EVT_HOT_PLUG_IS_INHIBITED(x) (x->data[0] & 0x80)
+
+#define ST21NFCA_SE_TO_PIPES 2000
+
+static DECLARE_BITMAP(dev_mask, ST21NFCA_NUM_DEVICES);
+
+static struct nfc_hci_gate st21nfca_gates[] = {
+ {NFC_HCI_ADMIN_GATE, NFC_HCI_ADMIN_PIPE},
+ {NFC_HCI_LINK_MGMT_GATE, NFC_HCI_LINK_MGMT_PIPE},
+ {ST21NFCA_DEVICE_MGNT_GATE, ST21NFCA_DEVICE_MGNT_PIPE},
+
+ {NFC_HCI_LOOPBACK_GATE, NFC_HCI_INVALID_PIPE},
+ {NFC_HCI_ID_MGMT_GATE, NFC_HCI_INVALID_PIPE},
+ {NFC_HCI_RF_READER_B_GATE, NFC_HCI_INVALID_PIPE},
+ {NFC_HCI_RF_READER_A_GATE, NFC_HCI_INVALID_PIPE},
+ {ST21NFCA_RF_READER_F_GATE, NFC_HCI_INVALID_PIPE},
+ {ST21NFCA_RF_READER_14443_3_A_GATE, NFC_HCI_INVALID_PIPE},
+ {ST21NFCA_RF_READER_ISO15693_GATE, NFC_HCI_INVALID_PIPE},
+ {ST21NFCA_RF_CARD_F_GATE, NFC_HCI_INVALID_PIPE},
+
+ /* Secure element pipes are created by secure element host */
+ {ST21NFCA_CONNECTIVITY_GATE, NFC_HCI_DO_NOT_CREATE_PIPE},
+ {ST21NFCA_APDU_READER_GATE, NFC_HCI_DO_NOT_CREATE_PIPE},
+};
+
+struct st21nfca_pipe_info {
+ u8 pipe_state;
+ u8 src_host_id;
+ u8 src_gate_id;
+ u8 dst_host_id;
+ u8 dst_gate_id;
+} __packed;
+
+/* Largest headroom needed for outgoing custom commands */
+#define ST21NFCA_CMDS_HEADROOM 7
+
+static int st21nfca_hci_load_session(struct nfc_hci_dev *hdev)
+{
+ int i, j, r;
+ struct sk_buff *skb_pipe_list, *skb_pipe_info;
+ struct st21nfca_pipe_info *info;
+
+ u8 pipe_list[] = { ST21NFCA_DM_GETINFO_PIPE_LIST,
+ NFC_HCI_TERMINAL_HOST_ID
+ };
+ u8 pipe_info[] = { ST21NFCA_DM_GETINFO_PIPE_INFO,
+ NFC_HCI_TERMINAL_HOST_ID, 0
+ };
+
+ /* On ST21NFCA device pipes number are dynamics
+ * A maximum of 16 pipes can be created at the same time
+ * If pipes are already created, hci_dev_up will fail.
+ * Doing a clear all pipe is a bad idea because:
+ * - It does useless EEPROM cycling
+ * - It might cause issue for secure elements support
+ * (such as removing connectivity or APDU reader pipe)
+ * A better approach on ST21NFCA is to:
+ * - get a pipe list for each host.
+ * (eg: NFC_HCI_HOST_CONTROLLER_ID for now).
+ * (TODO Later on UICC HOST and eSE HOST)
+ * - get pipe information
+ * - match retrieved pipe list in st21nfca_gates
+ * ST21NFCA_DEVICE_MGNT_GATE is a proprietary gate
+ * with ST21NFCA_DEVICE_MGNT_PIPE.
+ * Pipe can be closed and need to be open.
+ */
+ r = nfc_hci_connect_gate(hdev, NFC_HCI_HOST_CONTROLLER_ID,
+ ST21NFCA_DEVICE_MGNT_GATE,
+ ST21NFCA_DEVICE_MGNT_PIPE);
+ if (r < 0)
+ return r;
+
+ /* Get pipe list */
+ r = nfc_hci_send_cmd(hdev, ST21NFCA_DEVICE_MGNT_GATE,
+ ST21NFCA_DM_GETINFO, pipe_list, sizeof(pipe_list),
+ &skb_pipe_list);
+ if (r < 0)
+ return r;
+
+ /* Complete the existing gate_pipe table */
+ for (i = 0; i < skb_pipe_list->len; i++) {
+ pipe_info[2] = skb_pipe_list->data[i];
+ r = nfc_hci_send_cmd(hdev, ST21NFCA_DEVICE_MGNT_GATE,
+ ST21NFCA_DM_GETINFO, pipe_info,
+ sizeof(pipe_info), &skb_pipe_info);
+ if (r)
+ continue;
+
+ /*
+ * Match pipe ID and gate ID
+ * Output format from ST21NFC_DM_GETINFO is:
+ * - pipe state (1byte)
+ * - source hid (1byte)
+ * - source gid (1byte)
+ * - destination hid (1byte)
+ * - destination gid (1byte)
+ */
+ info = (struct st21nfca_pipe_info *) skb_pipe_info->data;
+ if (info->dst_gate_id == ST21NFCA_APDU_READER_GATE &&
+ info->src_host_id == NFC_HCI_UICC_HOST_ID) {
+ pr_err("Unexpected apdu_reader pipe on host %x\n",
+ info->src_host_id);
+ kfree_skb(skb_pipe_info);
+ continue;
+ }
+
+ for (j = 3; (j < ARRAY_SIZE(st21nfca_gates)) &&
+ (st21nfca_gates[j].gate != info->dst_gate_id) ; j++)
+ ;
+
+ if (j < ARRAY_SIZE(st21nfca_gates) &&
+ st21nfca_gates[j].gate == info->dst_gate_id &&
+ ST21NFCA_DM_IS_PIPE_OPEN(info->pipe_state)) {
+ hdev->init_data.gates[j].pipe = pipe_info[2];
+
+ hdev->gate2pipe[st21nfca_gates[j].gate] =
+ pipe_info[2];
+ hdev->pipes[pipe_info[2]].gate =
+ st21nfca_gates[j].gate;
+ hdev->pipes[pipe_info[2]].dest_host =
+ info->src_host_id;
+ }
+ kfree_skb(skb_pipe_info);
+ }
+
+ /*
+ * 3 gates have a well known pipe ID. Only NFC_HCI_LINK_MGMT_GATE
+ * is not yet open at this stage.
+ */
+ r = nfc_hci_connect_gate(hdev, NFC_HCI_HOST_CONTROLLER_ID,
+ NFC_HCI_LINK_MGMT_GATE,
+ NFC_HCI_LINK_MGMT_PIPE);
+
+ kfree_skb(skb_pipe_list);
+ return r;
+}
+
+static int st21nfca_hci_open(struct nfc_hci_dev *hdev)
+{
+ struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+ int r;
+
+ mutex_lock(&info->info_lock);
+
+ if (info->state != ST21NFCA_ST_COLD) {
+ r = -EBUSY;
+ goto out;
+ }
+
+ r = info->phy_ops->enable(info->phy_id);
+
+ if (r == 0)
+ info->state = ST21NFCA_ST_READY;
+
+out:
+ mutex_unlock(&info->info_lock);
+ return r;
+}
+
+static void st21nfca_hci_close(struct nfc_hci_dev *hdev)
+{
+ struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+ mutex_lock(&info->info_lock);
+
+ if (info->state == ST21NFCA_ST_COLD)
+ goto out;
+
+ info->phy_ops->disable(info->phy_id);
+ info->state = ST21NFCA_ST_COLD;
+
+out:
+ mutex_unlock(&info->info_lock);
+}
+
+static int st21nfca_hci_ready(struct nfc_hci_dev *hdev)
+{
+ struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+ struct sk_buff *skb;
+
+ u8 param;
+ u8 white_list[2];
+ int wl_size = 0;
+ int r;
+
+ if (info->se_status->is_uicc_present)
+ white_list[wl_size++] = NFC_HCI_UICC_HOST_ID;
+ if (info->se_status->is_ese_present)
+ white_list[wl_size++] = ST21NFCA_ESE_HOST_ID;
+
+ if (wl_size) {
+ r = nfc_hci_set_param(hdev, NFC_HCI_ADMIN_GATE,
+ NFC_HCI_ADMIN_WHITELIST,
+ (u8 *) &white_list, wl_size);
+ if (r < 0)
+ return r;
+ }
+
+ /* Set NFC_MODE in device management gate to enable */
+ r = nfc_hci_get_param(hdev, ST21NFCA_DEVICE_MGNT_GATE,
+ ST21NFCA_NFC_MODE, &skb);
+ if (r < 0)
+ return r;
+
+ param = skb->data[0];
+ kfree_skb(skb);
+ if (param == 0) {
+ param = 1;
+
+ r = nfc_hci_set_param(hdev, ST21NFCA_DEVICE_MGNT_GATE,
+ ST21NFCA_NFC_MODE, &param, 1);
+ if (r < 0)
+ return r;
+ }
+
+ r = nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE,
+ NFC_HCI_EVT_END_OPERATION, NULL, 0);
+ if (r < 0)
+ return r;
+
+ r = nfc_hci_get_param(hdev, NFC_HCI_ID_MGMT_GATE,
+ NFC_HCI_ID_MGMT_VERSION_SW, &skb);
+ if (r < 0)
+ return r;
+
+ if (skb->len != FULL_VERSION_LEN) {
+ kfree_skb(skb);
+ return -EINVAL;
+ }
+
+ print_hex_dump(KERN_DEBUG, "FULL VERSION SOFTWARE INFO: ",
+ DUMP_PREFIX_NONE, 16, 1,
+ skb->data, FULL_VERSION_LEN, false);
+
+ kfree_skb(skb);
+
+ return 0;
+}
+
+static int st21nfca_hci_xmit(struct nfc_hci_dev *hdev, struct sk_buff *skb)
+{
+ struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+ return info->phy_ops->write(info->phy_id, skb);
+}
+
+static int st21nfca_hci_start_poll(struct nfc_hci_dev *hdev,
+ u32 im_protocols, u32 tm_protocols)
+{
+ int r;
+ u32 pol_req;
+ u8 param[19];
+ struct sk_buff *datarate_skb;
+
+ pr_info(DRIVER_DESC ": %s protocols 0x%x 0x%x\n",
+ __func__, im_protocols, tm_protocols);
+
+ r = nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE,
+ NFC_HCI_EVT_END_OPERATION, NULL, 0);
+ if (r < 0)
+ return r;
+ if (im_protocols) {
+ /*
+ * enable polling according to im_protocols & tm_protocols
+ * - CLOSE pipe according to im_protocols & tm_protocols
+ */
+ if ((NFC_HCI_RF_READER_B_GATE & im_protocols) == 0) {
+ r = nfc_hci_disconnect_gate(hdev,
+ NFC_HCI_RF_READER_B_GATE);
+ if (r < 0)
+ return r;
+ }
+
+ if ((NFC_HCI_RF_READER_A_GATE & im_protocols) == 0) {
+ r = nfc_hci_disconnect_gate(hdev,
+ NFC_HCI_RF_READER_A_GATE);
+ if (r < 0)
+ return r;
+ }
+
+ if ((ST21NFCA_RF_READER_F_GATE & im_protocols) == 0) {
+ r = nfc_hci_disconnect_gate(hdev,
+ ST21NFCA_RF_READER_F_GATE);
+ if (r < 0)
+ return r;
+ } else {
+ hdev->gb = nfc_get_local_general_bytes(hdev->ndev,
+ &hdev->gb_len);
+
+ if (hdev->gb == NULL || hdev->gb_len == 0) {
+ im_protocols &= ~NFC_PROTO_NFC_DEP_MASK;
+ tm_protocols &= ~NFC_PROTO_NFC_DEP_MASK;
+ }
+
+ param[0] = ST21NFCA_RF_READER_F_DATARATE_106 |
+ ST21NFCA_RF_READER_F_DATARATE_212 |
+ ST21NFCA_RF_READER_F_DATARATE_424;
+ r = nfc_hci_set_param(hdev, ST21NFCA_RF_READER_F_GATE,
+ ST21NFCA_RF_READER_F_DATARATE,
+ param, 1);
+ if (r < 0)
+ return r;
+
+ pol_req = be32_to_cpu((__force __be32)
+ ST21NFCA_RF_READER_F_POL_REQ_DEFAULT);
+ r = nfc_hci_set_param(hdev, ST21NFCA_RF_READER_F_GATE,
+ ST21NFCA_RF_READER_F_POL_REQ,
+ (u8 *) &pol_req, 4);
+ if (r < 0)
+ return r;
+ }
+
+ if ((ST21NFCA_RF_READER_14443_3_A_GATE & im_protocols) == 0) {
+ r = nfc_hci_disconnect_gate(hdev,
+ ST21NFCA_RF_READER_14443_3_A_GATE);
+ if (r < 0)
+ return r;
+ }
+
+ if ((ST21NFCA_RF_READER_ISO15693_GATE & im_protocols) == 0) {
+ r = nfc_hci_disconnect_gate(hdev,
+ ST21NFCA_RF_READER_ISO15693_GATE);
+ if (r < 0)
+ return r;
+ }
+
+ r = nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE,
+ NFC_HCI_EVT_READER_REQUESTED, NULL, 0);
+ if (r < 0)
+ nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE,
+ NFC_HCI_EVT_END_OPERATION, NULL, 0);
+ }
+
+ if (tm_protocols & NFC_PROTO_NFC_DEP_MASK) {
+ r = nfc_hci_get_param(hdev, ST21NFCA_RF_CARD_F_GATE,
+ ST21NFCA_RF_CARD_F_DATARATE,
+ &datarate_skb);
+ if (r < 0)
+ return r;
+
+ /* Configure the maximum supported datarate to 424Kbps */
+ if (datarate_skb->len > 0 &&
+ datarate_skb->data[0] !=
+ ST21NFCA_RF_CARD_F_DATARATE_212_424) {
+ param[0] = ST21NFCA_RF_CARD_F_DATARATE_212_424;
+ r = nfc_hci_set_param(hdev, ST21NFCA_RF_CARD_F_GATE,
+ ST21NFCA_RF_CARD_F_DATARATE,
+ param, 1);
+ if (r < 0) {
+ kfree_skb(datarate_skb);
+ return r;
+ }
+ }
+ kfree_skb(datarate_skb);
+
+ /*
+ * Configure sens_res
+ *
+ * NFC Forum Digital Spec Table 7:
+ * NFCID1 size: triple (10 bytes)
+ */
+ param[0] = 0x00;
+ param[1] = 0x08;
+ r = nfc_hci_set_param(hdev, ST21NFCA_RF_CARD_F_GATE,
+ ST21NFCA_RF_CARD_F_SENS_RES, param, 2);
+ if (r < 0)
+ return r;
+
+ /*
+ * Configure sel_res
+ *
+ * NFC Forum Digistal Spec Table 17:
+ * b3 set to 0b (value b7-b6):
+ * - 10b: Configured for NFC-DEP Protocol
+ */
+ param[0] = 0x40;
+ r = nfc_hci_set_param(hdev, ST21NFCA_RF_CARD_F_GATE,
+ ST21NFCA_RF_CARD_F_SEL_RES, param, 1);
+ if (r < 0)
+ return r;
+
+ /* Configure NFCID1 Random uid */
+ r = nfc_hci_set_param(hdev, ST21NFCA_RF_CARD_F_GATE,
+ ST21NFCA_RF_CARD_F_NFCID1, NULL, 0);
+ if (r < 0)
+ return r;
+
+ /* Configure NFCID2_LIST */
+ /* System Code */
+ param[0] = 0x00;
+ param[1] = 0x00;
+ /* NFCID2 */
+ param[2] = 0x01;
+ param[3] = 0xfe;
+ param[4] = 'S';
+ param[5] = 'T';
+ param[6] = 'M';
+ param[7] = 'i';
+ param[8] = 'c';
+ param[9] = 'r';
+ /* 8 byte Pad bytes used for polling respone frame */
+
+ /*
+ * Configuration byte:
+ * - bit 0: define the default NFCID2 entry used when the
+ * system code is equal to 'FFFF'
+ * - bit 1: use a random value for lowest 6 bytes of
+ * NFCID2 value
+ * - bit 2: ignore polling request frame if request code
+ * is equal to '01'
+ * - Other bits are RFU
+ */
+ param[18] = 0x01;
+ r = nfc_hci_set_param(hdev, ST21NFCA_RF_CARD_F_GATE,
+ ST21NFCA_RF_CARD_F_NFCID2_LIST, param,
+ 19);
+ if (r < 0)
+ return r;
+
+ param[0] = 0x02;
+ r = nfc_hci_set_param(hdev, ST21NFCA_RF_CARD_F_GATE,
+ ST21NFCA_RF_CARD_F_MODE, param, 1);
+ }
+
+ return r;
+}
+
+static void st21nfca_hci_stop_poll(struct nfc_hci_dev *hdev)
+{
+ nfc_hci_send_cmd(hdev, ST21NFCA_DEVICE_MGNT_GATE,
+ ST21NFCA_DM_DISCONNECT, NULL, 0, NULL);
+}
+
+static int st21nfca_get_iso14443_3_atqa(struct nfc_hci_dev *hdev, u16 *atqa)
+{
+ int r;
+ struct sk_buff *atqa_skb = NULL;
+
+ r = nfc_hci_get_param(hdev, ST21NFCA_RF_READER_14443_3_A_GATE,
+ ST21NFCA_RF_READER_14443_3_A_ATQA, &atqa_skb);
+ if (r < 0)
+ goto exit;
+
+ if (atqa_skb->len != 2) {
+ r = -EPROTO;
+ goto exit;
+ }
+
+ *atqa = be16_to_cpu(*(__be16 *) atqa_skb->data);
+
+exit:
+ kfree_skb(atqa_skb);
+ return r;
+}
+
+static int st21nfca_get_iso14443_3_sak(struct nfc_hci_dev *hdev, u8 *sak)
+{
+ int r;
+ struct sk_buff *sak_skb = NULL;
+
+ r = nfc_hci_get_param(hdev, ST21NFCA_RF_READER_14443_3_A_GATE,
+ ST21NFCA_RF_READER_14443_3_A_SAK, &sak_skb);
+ if (r < 0)
+ goto exit;
+
+ if (sak_skb->len != 1) {
+ r = -EPROTO;
+ goto exit;
+ }
+
+ *sak = sak_skb->data[0];
+
+exit:
+ kfree_skb(sak_skb);
+ return r;
+}
+
+static int st21nfca_get_iso14443_3_uid(struct nfc_hci_dev *hdev, u8 *uid,
+ int *len)
+{
+ int r;
+ struct sk_buff *uid_skb = NULL;
+
+ r = nfc_hci_get_param(hdev, ST21NFCA_RF_READER_14443_3_A_GATE,
+ ST21NFCA_RF_READER_14443_3_A_UID, &uid_skb);
+ if (r < 0)
+ goto exit;
+
+ if (uid_skb->len == 0 || uid_skb->len > NFC_NFCID1_MAXSIZE) {
+ r = -EPROTO;
+ goto exit;
+ }
+
+ memcpy(uid, uid_skb->data, uid_skb->len);
+ *len = uid_skb->len;
+exit:
+ kfree_skb(uid_skb);
+ return r;
+}
+
+static int st21nfca_get_iso15693_inventory(struct nfc_hci_dev *hdev,
+ struct nfc_target *target)
+{
+ int r;
+ struct sk_buff *inventory_skb = NULL;
+
+ r = nfc_hci_get_param(hdev, ST21NFCA_RF_READER_ISO15693_GATE,
+ ST21NFCA_RF_READER_ISO15693_INVENTORY,
+ &inventory_skb);
+ if (r < 0)
+ goto exit;
+
+ skb_pull(inventory_skb, 2);
+
+ if (inventory_skb->len == 0 ||
+ inventory_skb->len > NFC_ISO15693_UID_MAXSIZE) {
+ r = -EPROTO;
+ goto exit;
+ }
+
+ memcpy(target->iso15693_uid, inventory_skb->data, inventory_skb->len);
+ target->iso15693_dsfid = inventory_skb->data[1];
+ target->is_iso15693 = 1;
+exit:
+ kfree_skb(inventory_skb);
+ return r;
+}
+
+static int st21nfca_hci_dep_link_up(struct nfc_hci_dev *hdev,
+ struct nfc_target *target, u8 comm_mode,
+ u8 *gb, size_t gb_len)
+{
+ struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+ info->dep_info.idx = target->idx;
+ return st21nfca_im_send_atr_req(hdev, gb, gb_len);
+}
+
+static int st21nfca_hci_dep_link_down(struct nfc_hci_dev *hdev)
+{
+ struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+ info->state = ST21NFCA_ST_READY;
+
+ return nfc_hci_send_cmd(hdev, ST21NFCA_DEVICE_MGNT_GATE,
+ ST21NFCA_DM_DISCONNECT, NULL, 0, NULL);
+}
+
+static int st21nfca_hci_target_from_gate(struct nfc_hci_dev *hdev, u8 gate,
+ struct nfc_target *target)
+{
+ int r, len;
+ u16 atqa;
+ u8 sak;
+ u8 uid[NFC_NFCID1_MAXSIZE];
+
+ switch (gate) {
+ case ST21NFCA_RF_READER_F_GATE:
+ target->supported_protocols = NFC_PROTO_FELICA_MASK;
+ break;
+ case ST21NFCA_RF_READER_14443_3_A_GATE:
+ /* ISO14443-3 type 1 or 2 tags */
+ r = st21nfca_get_iso14443_3_atqa(hdev, &atqa);
+ if (r < 0)
+ return r;
+ if (atqa == 0x000c) {
+ target->supported_protocols = NFC_PROTO_JEWEL_MASK;
+ target->sens_res = 0x0c00;
+ } else {
+ r = st21nfca_get_iso14443_3_sak(hdev, &sak);
+ if (r < 0)
+ return r;
+
+ r = st21nfca_get_iso14443_3_uid(hdev, uid, &len);
+ if (r < 0)
+ return r;
+
+ target->supported_protocols =
+ nfc_hci_sak_to_protocol(sak);
+ if (target->supported_protocols == 0xffffffff)
+ return -EPROTO;
+
+ target->sens_res = atqa;
+ target->sel_res = sak;
+ memcpy(target->nfcid1, uid, len);
+ target->nfcid1_len = len;
+ }
+
+ break;
+ case ST21NFCA_RF_READER_ISO15693_GATE:
+ target->supported_protocols = NFC_PROTO_ISO15693_MASK;
+ r = st21nfca_get_iso15693_inventory(hdev, target);
+ if (r < 0)
+ return r;
+ break;
+ default:
+ return -EPROTO;
+ }
+
+ return 0;
+}
+
+static int st21nfca_hci_complete_target_discovered(struct nfc_hci_dev *hdev,
+ u8 gate,
+ struct nfc_target *target)
+{
+ int r;
+ struct sk_buff *nfcid_skb = NULL;
+
+ if (gate == ST21NFCA_RF_READER_F_GATE) {
+ r = nfc_hci_get_param(hdev, ST21NFCA_RF_READER_F_GATE,
+ ST21NFCA_RF_READER_F_NFCID2, &nfcid_skb);
+ if (r < 0)
+ goto exit;
+
+ if (nfcid_skb->len > NFC_SENSF_RES_MAXSIZE) {
+ r = -EPROTO;
+ goto exit;
+ }
+
+ /*
+ * - After the recepton of polling response for type F frame
+ * at 212 or 424 Kbit/s, NFCID2 registry parameters will be
+ * updated.
+ * - After the reception of SEL_RES with NFCIP-1 compliant bit
+ * set for type A frame NFCID1 will be updated
+ */
+ if (nfcid_skb->len > 0) {
+ /* P2P in type F */
+ memcpy(target->sensf_res, nfcid_skb->data,
+ nfcid_skb->len);
+ target->sensf_res_len = nfcid_skb->len;
+ /* NFC Forum Digital Protocol Table 44 */
+ if (target->sensf_res[0] == 0x01 &&
+ target->sensf_res[1] == 0xfe)
+ target->supported_protocols =
+ NFC_PROTO_NFC_DEP_MASK;
+ else
+ target->supported_protocols =
+ NFC_PROTO_FELICA_MASK;
+ } else {
+ kfree_skb(nfcid_skb);
+ /* P2P in type A */
+ r = nfc_hci_get_param(hdev, ST21NFCA_RF_READER_F_GATE,
+ ST21NFCA_RF_READER_F_NFCID1,
+ &nfcid_skb);
+ if (r < 0)
+ goto exit;
+
+ if (nfcid_skb->len > NFC_NFCID1_MAXSIZE) {
+ r = -EPROTO;
+ goto exit;
+ }
+ memcpy(target->sensf_res, nfcid_skb->data,
+ nfcid_skb->len);
+ target->sensf_res_len = nfcid_skb->len;
+ target->supported_protocols = NFC_PROTO_NFC_DEP_MASK;
+ }
+ target->hci_reader_gate = ST21NFCA_RF_READER_F_GATE;
+ }
+ r = 1;
+exit:
+ kfree_skb(nfcid_skb);
+ return r;
+}
+
+#define ST21NFCA_CB_TYPE_READER_ISO15693 1
+static void st21nfca_hci_data_exchange_cb(void *context, struct sk_buff *skb,
+ int err)
+{
+ struct st21nfca_hci_info *info = context;
+
+ switch (info->async_cb_type) {
+ case ST21NFCA_CB_TYPE_READER_ISO15693:
+ if (err == 0)
+ skb_trim(skb, skb->len - 1);
+ info->async_cb(info->async_cb_context, skb, err);
+ break;
+ default:
+ if (err == 0)
+ kfree_skb(skb);
+ break;
+ }
+}
+
+/*
+ * Returns:
+ * <= 0: driver handled the data exchange
+ * 1: driver doesn't especially handle, please do standard processing
+ */
+static int st21nfca_hci_im_transceive(struct nfc_hci_dev *hdev,
+ struct nfc_target *target,
+ struct sk_buff *skb,
+ data_exchange_cb_t cb, void *cb_context)
+{
+ struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+ pr_info(DRIVER_DESC ": %s for gate=%d len=%d\n", __func__,
+ target->hci_reader_gate, skb->len);
+
+ switch (target->hci_reader_gate) {
+ case ST21NFCA_RF_READER_F_GATE:
+ if (target->supported_protocols == NFC_PROTO_NFC_DEP_MASK)
+ return st21nfca_im_send_dep_req(hdev, skb);
+
+ *skb_push(skb, 1) = 0x1a;
+ return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate,
+ ST21NFCA_WR_XCHG_DATA, skb->data,
+ skb->len, cb, cb_context);
+ case ST21NFCA_RF_READER_14443_3_A_GATE:
+ *skb_push(skb, 1) = 0x1a; /* CTR, see spec:10.2.2.1 */
+
+ return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate,
+ ST21NFCA_WR_XCHG_DATA, skb->data,
+ skb->len, cb, cb_context);
+ case ST21NFCA_RF_READER_ISO15693_GATE:
+ info->async_cb_type = ST21NFCA_CB_TYPE_READER_ISO15693;
+ info->async_cb = cb;
+ info->async_cb_context = cb_context;
+
+ *skb_push(skb, 1) = 0x17;
+
+ return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate,
+ ST21NFCA_WR_XCHG_DATA, skb->data,
+ skb->len,
+ st21nfca_hci_data_exchange_cb,
+ info);
+ break;
+ default:
+ return 1;
+ }
+}
+
+static int st21nfca_hci_tm_send(struct nfc_hci_dev *hdev, struct sk_buff *skb)
+{
+ return st21nfca_tm_send_dep_res(hdev, skb);
+}
+
+static int st21nfca_hci_check_presence(struct nfc_hci_dev *hdev,
+ struct nfc_target *target)
+{
+ u8 fwi = 0x11;
+
+ switch (target->hci_reader_gate) {
+ case NFC_HCI_RF_READER_A_GATE:
+ case NFC_HCI_RF_READER_B_GATE:
+ /*
+ * PRESENCE_CHECK on those gates is available
+ * However, the answer to this command is taking 3 * fwi
+ * if the card is no present.
+ * Instead, we send an empty I-Frame with a very short
+ * configurable fwi ~604µs.
+ */
+ return nfc_hci_send_cmd(hdev, target->hci_reader_gate,
+ ST21NFCA_WR_XCHG_DATA, &fwi, 1, NULL);
+ case ST21NFCA_RF_READER_14443_3_A_GATE:
+ return nfc_hci_send_cmd(hdev, target->hci_reader_gate,
+ ST21NFCA_RF_READER_CMD_PRESENCE_CHECK,
+ NULL, 0, NULL);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static void st21nfca_hci_cmd_received(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd,
+ struct sk_buff *skb)
+{
+ struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+ u8 gate = hdev->pipes[pipe].gate;
+
+ pr_debug("cmd: %x\n", cmd);
+
+ switch (cmd) {
+ case NFC_HCI_ANY_OPEN_PIPE:
+ if (gate != ST21NFCA_APDU_READER_GATE &&
+ hdev->pipes[pipe].dest_host != NFC_HCI_UICC_HOST_ID)
+ info->se_info.count_pipes++;
+
+ if (info->se_info.count_pipes == info->se_info.expected_pipes) {
+ del_timer_sync(&info->se_info.se_active_timer);
+ info->se_info.se_active = false;
+ info->se_info.count_pipes = 0;
+ complete(&info->se_info.req_completion);
+ }
+ break;
+ }
+}
+
+static int st21nfca_admin_event_received(struct nfc_hci_dev *hdev, u8 event,
+ struct sk_buff *skb)
+{
+ struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+ pr_debug("admin event: %x\n", event);
+
+ switch (event) {
+ case ST21NFCA_EVT_HOT_PLUG:
+ if (info->se_info.se_active) {
+ if (!ST21NFCA_EVT_HOT_PLUG_IS_INHIBITED(skb)) {
+ del_timer_sync(&info->se_info.se_active_timer);
+ info->se_info.se_active = false;
+ complete(&info->se_info.req_completion);
+ } else {
+ mod_timer(&info->se_info.se_active_timer,
+ jiffies +
+ msecs_to_jiffies(ST21NFCA_SE_TO_PIPES));
+ }
+ }
+ break;
+ default:
+ nfc_err(&hdev->ndev->dev, "Unexpected event on admin gate\n");
+ }
+ kfree_skb(skb);
+ return 0;
+}
+
+/*
+ * Returns:
+ * <= 0: driver handled the event, skb consumed
+ * 1: driver does not handle the event, please do standard processing
+ */
+static int st21nfca_hci_event_received(struct nfc_hci_dev *hdev, u8 pipe,
+ u8 event, struct sk_buff *skb)
+{
+ u8 gate = hdev->pipes[pipe].gate;
+ u8 host = hdev->pipes[pipe].dest_host;
+
+ pr_debug("hci event: %d gate: %x\n", event, gate);
+
+ switch (gate) {
+ case NFC_HCI_ADMIN_GATE:
+ return st21nfca_admin_event_received(hdev, event, skb);
+ case ST21NFCA_RF_CARD_F_GATE:
+ return st21nfca_dep_event_received(hdev, event, skb);
+ case ST21NFCA_CONNECTIVITY_GATE:
+ return st21nfca_connectivity_event_received(hdev, host,
+ event, skb);
+ case ST21NFCA_APDU_READER_GATE:
+ return st21nfca_apdu_reader_event_received(hdev, event, skb);
+ case NFC_HCI_LOOPBACK_GATE:
+ return st21nfca_hci_loopback_event_received(hdev, event, skb);
+ default:
+ return 1;
+ }
+}
+
+static struct nfc_hci_ops st21nfca_hci_ops = {
+ .open = st21nfca_hci_open,
+ .close = st21nfca_hci_close,
+ .load_session = st21nfca_hci_load_session,
+ .hci_ready = st21nfca_hci_ready,
+ .xmit = st21nfca_hci_xmit,
+ .start_poll = st21nfca_hci_start_poll,
+ .stop_poll = st21nfca_hci_stop_poll,
+ .dep_link_up = st21nfca_hci_dep_link_up,
+ .dep_link_down = st21nfca_hci_dep_link_down,
+ .target_from_gate = st21nfca_hci_target_from_gate,
+ .complete_target_discovered = st21nfca_hci_complete_target_discovered,
+ .im_transceive = st21nfca_hci_im_transceive,
+ .tm_send = st21nfca_hci_tm_send,
+ .check_presence = st21nfca_hci_check_presence,
+ .event_received = st21nfca_hci_event_received,
+ .cmd_received = st21nfca_hci_cmd_received,
+ .discover_se = st21nfca_hci_discover_se,
+ .enable_se = st21nfca_hci_enable_se,
+ .disable_se = st21nfca_hci_disable_se,
+ .se_io = st21nfca_hci_se_io,
+};
+
+int st21nfca_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops,
+ char *llc_name, int phy_headroom, int phy_tailroom,
+ int phy_payload, struct nfc_hci_dev **hdev,
+ struct st21nfca_se_status *se_status)
+{
+ struct st21nfca_hci_info *info;
+ int r = 0;
+ int dev_num;
+ u32 protocols;
+ struct nfc_hci_init_data init_data;
+ unsigned long quirks = 0;
+
+ info = kzalloc(sizeof(struct st21nfca_hci_info), GFP_KERNEL);
+ if (!info) {
+ r = -ENOMEM;
+ goto err_alloc_hdev;
+ }
+
+ info->phy_ops = phy_ops;
+ info->phy_id = phy_id;
+ info->state = ST21NFCA_ST_COLD;
+ mutex_init(&info->info_lock);
+
+ init_data.gate_count = ARRAY_SIZE(st21nfca_gates);
+
+ memcpy(init_data.gates, st21nfca_gates, sizeof(st21nfca_gates));
+
+ /*
+ * Session id must include the driver name + i2c bus addr
+ * persistent info to discriminate 2 identical chips
+ */
+ dev_num = find_first_zero_bit(dev_mask, ST21NFCA_NUM_DEVICES);
+ if (dev_num >= ST21NFCA_NUM_DEVICES)
+ return -ENODEV;
+
+ set_bit(dev_num, dev_mask);
+
+ scnprintf(init_data.session_id, sizeof(init_data.session_id), "%s%2x",
+ "ST21AH", dev_num);
+
+ 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;
+
+ set_bit(NFC_HCI_QUIRK_SHORT_CLEAR, &quirks);
+
+ info->hdev =
+ nfc_hci_allocate_device(&st21nfca_hci_ops, &init_data, quirks,
+ protocols, llc_name,
+ phy_headroom + ST21NFCA_CMDS_HEADROOM,
+ phy_tailroom, phy_payload);
+
+ if (!info->hdev) {
+ pr_err("Cannot allocate nfc hdev.\n");
+ r = -ENOMEM;
+ goto err_alloc_hdev;
+ }
+
+ info->se_status = se_status;
+
+ nfc_hci_set_clientdata(info->hdev, info);
+
+ r = nfc_hci_register_device(info->hdev);
+ if (r)
+ goto err_regdev;
+
+ *hdev = info->hdev;
+ st21nfca_dep_init(info->hdev);
+ st21nfca_se_init(info->hdev);
+ st21nfca_vendor_cmds_init(info->hdev);
+
+ return 0;
+
+err_regdev:
+ nfc_hci_free_device(info->hdev);
+
+err_alloc_hdev:
+ kfree(info);
+
+ return r;
+}
+EXPORT_SYMBOL(st21nfca_hci_probe);
+
+void st21nfca_hci_remove(struct nfc_hci_dev *hdev)
+{
+ struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+ st21nfca_dep_deinit(hdev);
+ st21nfca_se_deinit(hdev);
+ nfc_hci_unregister_device(hdev);
+ nfc_hci_free_device(hdev);
+ kfree(info);
+}
+EXPORT_SYMBOL(st21nfca_hci_remove);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/nfc/st21nfca/dep.c b/drivers/nfc/st21nfca/dep.c
new file mode 100644
index 0000000..798a32b
--- /dev/null
+++ b/drivers/nfc/st21nfca/dep.c
@@ -0,0 +1,689 @@
+/*
+ * Copyright (C) 2014 STMicroelectronics SAS. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <net/nfc/hci.h>
+
+#include "st21nfca.h"
+
+#define ST21NFCA_NFCIP1_INITIATOR 0x00
+#define ST21NFCA_NFCIP1_REQ 0xd4
+#define ST21NFCA_NFCIP1_RES 0xd5
+#define ST21NFCA_NFCIP1_ATR_REQ 0x00
+#define ST21NFCA_NFCIP1_ATR_RES 0x01
+#define ST21NFCA_NFCIP1_PSL_REQ 0x04
+#define ST21NFCA_NFCIP1_PSL_RES 0x05
+#define ST21NFCA_NFCIP1_DEP_REQ 0x06
+#define ST21NFCA_NFCIP1_DEP_RES 0x07
+
+#define ST21NFCA_NFC_DEP_PFB_PNI(pfb) ((pfb) & 0x03)
+#define ST21NFCA_NFC_DEP_PFB_TYPE(pfb) ((pfb) & 0xE0)
+#define ST21NFCA_NFC_DEP_PFB_IS_TIMEOUT(pfb) \
+ ((pfb) & ST21NFCA_NFC_DEP_PFB_TIMEOUT_BIT)
+#define ST21NFCA_NFC_DEP_DID_BIT_SET(pfb) ((pfb) & 0x04)
+#define ST21NFCA_NFC_DEP_NAD_BIT_SET(pfb) ((pfb) & 0x08)
+#define ST21NFCA_NFC_DEP_PFB_TIMEOUT_BIT 0x10
+
+#define ST21NFCA_NFC_DEP_PFB_IS_TIMEOUT(pfb) \
+ ((pfb) & ST21NFCA_NFC_DEP_PFB_TIMEOUT_BIT)
+
+#define ST21NFCA_NFC_DEP_PFB_I_PDU 0x00
+#define ST21NFCA_NFC_DEP_PFB_ACK_NACK_PDU 0x40
+#define ST21NFCA_NFC_DEP_PFB_SUPERVISOR_PDU 0x80
+
+#define ST21NFCA_ATR_REQ_MIN_SIZE 17
+#define ST21NFCA_ATR_REQ_MAX_SIZE 65
+#define ST21NFCA_LR_BITS_PAYLOAD_SIZE_254B 0x30
+#define ST21NFCA_GB_BIT 0x02
+
+#define ST21NFCA_EVT_SEND_DATA 0x10
+#define ST21NFCA_EVT_FIELD_ON 0x11
+#define ST21NFCA_EVT_CARD_DEACTIVATED 0x12
+#define ST21NFCA_EVT_CARD_ACTIVATED 0x13
+#define ST21NFCA_EVT_FIELD_OFF 0x14
+
+#define ST21NFCA_EVT_CARD_F_BITRATE 0x16
+#define ST21NFCA_EVT_READER_F_BITRATE 0x13
+#define ST21NFCA_PSL_REQ_SEND_SPEED(brs) (brs & 0x38)
+#define ST21NFCA_PSL_REQ_RECV_SPEED(brs) (brs & 0x07)
+#define ST21NFCA_PP2LRI(pp) ((pp & 0x30) >> 4)
+#define ST21NFCA_CARD_BITRATE_212 0x01
+#define ST21NFCA_CARD_BITRATE_424 0x02
+
+#define ST21NFCA_DEFAULT_TIMEOUT 0x0a
+
+
+#define PROTOCOL_ERR(req) pr_err("%d: ST21NFCA Protocol error: %s\n", \
+ __LINE__, req)
+
+struct st21nfca_atr_req {
+ u8 length;
+ u8 cmd0;
+ u8 cmd1;
+ u8 nfcid3[NFC_NFCID3_MAXSIZE];
+ u8 did;
+ u8 bsi;
+ u8 bri;
+ u8 ppi;
+ u8 gbi[0];
+} __packed;
+
+struct st21nfca_atr_res {
+ u8 length;
+ u8 cmd0;
+ u8 cmd1;
+ u8 nfcid3[NFC_NFCID3_MAXSIZE];
+ u8 did;
+ u8 bsi;
+ u8 bri;
+ u8 to;
+ u8 ppi;
+ u8 gbi[0];
+} __packed;
+
+struct st21nfca_psl_req {
+ u8 length;
+ u8 cmd0;
+ u8 cmd1;
+ u8 did;
+ u8 brs;
+ u8 fsl;
+} __packed;
+
+struct st21nfca_psl_res {
+ u8 length;
+ u8 cmd0;
+ u8 cmd1;
+ u8 did;
+} __packed;
+
+struct st21nfca_dep_req_res {
+ u8 length;
+ u8 cmd0;
+ u8 cmd1;
+ u8 pfb;
+ u8 did;
+ u8 nad;
+} __packed;
+
+static void st21nfca_tx_work(struct work_struct *work)
+{
+ struct st21nfca_hci_info *info = container_of(work,
+ struct st21nfca_hci_info,
+ dep_info.tx_work);
+
+ struct nfc_dev *dev;
+ struct sk_buff *skb;
+
+ if (info) {
+ dev = info->hdev->ndev;
+ skb = info->dep_info.tx_pending;
+
+ device_lock(&dev->dev);
+
+ nfc_hci_send_cmd_async(info->hdev, ST21NFCA_RF_READER_F_GATE,
+ ST21NFCA_WR_XCHG_DATA, skb->data, skb->len,
+ info->async_cb, info);
+ device_unlock(&dev->dev);
+ kfree_skb(skb);
+ }
+}
+
+static void st21nfca_im_send_pdu(struct st21nfca_hci_info *info,
+ struct sk_buff *skb)
+{
+ info->dep_info.tx_pending = skb;
+ schedule_work(&info->dep_info.tx_work);
+}
+
+static int st21nfca_tm_send_atr_res(struct nfc_hci_dev *hdev,
+ struct st21nfca_atr_req *atr_req)
+{
+ struct st21nfca_atr_res *atr_res;
+ struct sk_buff *skb;
+ size_t gb_len;
+ int r;
+ struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+ gb_len = atr_req->length - sizeof(struct st21nfca_atr_req);
+ skb = alloc_skb(atr_req->length + 1, GFP_KERNEL);
+ if (!skb)
+ return -ENOMEM;
+
+ skb_put(skb, sizeof(struct st21nfca_atr_res));
+
+ atr_res = (struct st21nfca_atr_res *)skb->data;
+ memset(atr_res, 0, sizeof(struct st21nfca_atr_res));
+
+ atr_res->length = atr_req->length + 1;
+ atr_res->cmd0 = ST21NFCA_NFCIP1_RES;
+ atr_res->cmd1 = ST21NFCA_NFCIP1_ATR_RES;
+
+ memcpy(atr_res->nfcid3, atr_req->nfcid3, 6);
+ atr_res->bsi = 0x00;
+ atr_res->bri = 0x00;
+ atr_res->to = ST21NFCA_DEFAULT_TIMEOUT;
+ atr_res->ppi = ST21NFCA_LR_BITS_PAYLOAD_SIZE_254B;
+
+ if (gb_len) {
+ skb_put(skb, gb_len);
+
+ atr_res->ppi |= ST21NFCA_GB_BIT;
+ memcpy(atr_res->gbi, atr_req->gbi, gb_len);
+ r = nfc_set_remote_general_bytes(hdev->ndev, atr_res->gbi,
+ gb_len);
+ if (r < 0)
+ return r;
+ }
+
+ info->dep_info.curr_nfc_dep_pni = 0;
+
+ r = nfc_hci_send_event(hdev, ST21NFCA_RF_CARD_F_GATE,
+ ST21NFCA_EVT_SEND_DATA, skb->data, skb->len);
+ kfree_skb(skb);
+ return r;
+}
+
+static int st21nfca_tm_recv_atr_req(struct nfc_hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ struct st21nfca_atr_req *atr_req;
+ size_t gb_len;
+ int r;
+
+ skb_trim(skb, skb->len - 1);
+
+ if (!skb->len) {
+ r = -EIO;
+ goto exit;
+ }
+
+ if (skb->len < ST21NFCA_ATR_REQ_MIN_SIZE) {
+ r = -EPROTO;
+ goto exit;
+ }
+
+ atr_req = (struct st21nfca_atr_req *)skb->data;
+
+ if (atr_req->length < sizeof(struct st21nfca_atr_req)) {
+ r = -EPROTO;
+ goto exit;
+ }
+
+ r = st21nfca_tm_send_atr_res(hdev, atr_req);
+ if (r)
+ goto exit;
+
+ gb_len = skb->len - sizeof(struct st21nfca_atr_req);
+
+ r = nfc_tm_activated(hdev->ndev, NFC_PROTO_NFC_DEP_MASK,
+ NFC_COMM_PASSIVE, atr_req->gbi, gb_len);
+ if (r)
+ goto exit;
+
+ r = 0;
+
+exit:
+ return r;
+}
+
+static int st21nfca_tm_send_psl_res(struct nfc_hci_dev *hdev,
+ struct st21nfca_psl_req *psl_req)
+{
+ struct st21nfca_psl_res *psl_res;
+ struct sk_buff *skb;
+ u8 bitrate[2] = {0, 0};
+ int r;
+
+ skb = alloc_skb(sizeof(struct st21nfca_psl_res), GFP_KERNEL);
+ if (!skb)
+ return -ENOMEM;
+ skb_put(skb, sizeof(struct st21nfca_psl_res));
+
+ psl_res = (struct st21nfca_psl_res *)skb->data;
+
+ psl_res->length = sizeof(struct st21nfca_psl_res);
+ psl_res->cmd0 = ST21NFCA_NFCIP1_RES;
+ psl_res->cmd1 = ST21NFCA_NFCIP1_PSL_RES;
+ psl_res->did = psl_req->did;
+
+ r = nfc_hci_send_event(hdev, ST21NFCA_RF_CARD_F_GATE,
+ ST21NFCA_EVT_SEND_DATA, skb->data, skb->len);
+ if (r < 0)
+ goto error;
+
+ /*
+ * ST21NFCA only support P2P passive.
+ * PSL_REQ BRS value != 0 has only a meaning to
+ * change technology to type F.
+ * We change to BITRATE 424Kbits.
+ * In other case switch to BITRATE 106Kbits.
+ */
+ if (ST21NFCA_PSL_REQ_SEND_SPEED(psl_req->brs) &&
+ ST21NFCA_PSL_REQ_RECV_SPEED(psl_req->brs)) {
+ bitrate[0] = ST21NFCA_CARD_BITRATE_424;
+ bitrate[1] = ST21NFCA_CARD_BITRATE_424;
+ }
+
+ /* Send an event to change bitrate change event to card f */
+ r = nfc_hci_send_event(hdev, ST21NFCA_RF_CARD_F_GATE,
+ ST21NFCA_EVT_CARD_F_BITRATE, bitrate, 2);
+error:
+ kfree_skb(skb);
+ return r;
+}
+
+static int st21nfca_tm_recv_psl_req(struct nfc_hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ struct st21nfca_psl_req *psl_req;
+ int r;
+
+ skb_trim(skb, skb->len - 1);
+
+ if (!skb->len) {
+ r = -EIO;
+ goto exit;
+ }
+
+ psl_req = (struct st21nfca_psl_req *)skb->data;
+
+ if (skb->len < sizeof(struct st21nfca_psl_req)) {
+ r = -EIO;
+ goto exit;
+ }
+
+ r = st21nfca_tm_send_psl_res(hdev, psl_req);
+exit:
+ return r;
+}
+
+int st21nfca_tm_send_dep_res(struct nfc_hci_dev *hdev, struct sk_buff *skb)
+{
+ int r;
+ struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+ *skb_push(skb, 1) = info->dep_info.curr_nfc_dep_pni;
+ *skb_push(skb, 1) = ST21NFCA_NFCIP1_DEP_RES;
+ *skb_push(skb, 1) = ST21NFCA_NFCIP1_RES;
+ *skb_push(skb, 1) = skb->len;
+
+ r = nfc_hci_send_event(hdev, ST21NFCA_RF_CARD_F_GATE,
+ ST21NFCA_EVT_SEND_DATA, skb->data, skb->len);
+ kfree_skb(skb);
+
+ return r;
+}
+EXPORT_SYMBOL(st21nfca_tm_send_dep_res);
+
+static int st21nfca_tm_recv_dep_req(struct nfc_hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ struct st21nfca_dep_req_res *dep_req;
+ u8 size;
+ int r;
+ struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+ skb_trim(skb, skb->len - 1);
+
+ size = 4;
+
+ dep_req = (struct st21nfca_dep_req_res *)skb->data;
+ if (skb->len < size) {
+ r = -EIO;
+ goto exit;
+ }
+
+ if (ST21NFCA_NFC_DEP_DID_BIT_SET(dep_req->pfb))
+ size++;
+ if (ST21NFCA_NFC_DEP_NAD_BIT_SET(dep_req->pfb))
+ size++;
+
+ if (skb->len < size) {
+ r = -EIO;
+ goto exit;
+ }
+
+ /* Receiving DEP_REQ - Decoding */
+ switch (ST21NFCA_NFC_DEP_PFB_TYPE(dep_req->pfb)) {
+ case ST21NFCA_NFC_DEP_PFB_I_PDU:
+ info->dep_info.curr_nfc_dep_pni =
+ ST21NFCA_NFC_DEP_PFB_PNI(dep_req->pfb);
+ break;
+ case ST21NFCA_NFC_DEP_PFB_ACK_NACK_PDU:
+ pr_err("Received a ACK/NACK PDU\n");
+ break;
+ case ST21NFCA_NFC_DEP_PFB_SUPERVISOR_PDU:
+ pr_err("Received a SUPERVISOR PDU\n");
+ break;
+ }
+
+ skb_pull(skb, size);
+
+ return nfc_tm_data_received(hdev->ndev, skb);
+exit:
+ return r;
+}
+
+static int st21nfca_tm_event_send_data(struct nfc_hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ u8 cmd0, cmd1;
+ int r;
+
+ cmd0 = skb->data[1];
+ switch (cmd0) {
+ case ST21NFCA_NFCIP1_REQ:
+ cmd1 = skb->data[2];
+ switch (cmd1) {
+ case ST21NFCA_NFCIP1_ATR_REQ:
+ r = st21nfca_tm_recv_atr_req(hdev, skb);
+ break;
+ case ST21NFCA_NFCIP1_PSL_REQ:
+ r = st21nfca_tm_recv_psl_req(hdev, skb);
+ break;
+ case ST21NFCA_NFCIP1_DEP_REQ:
+ r = st21nfca_tm_recv_dep_req(hdev, skb);
+ break;
+ default:
+ return 1;
+ }
+ default:
+ return 1;
+ }
+ return r;
+}
+
+/*
+ * Returns:
+ * <= 0: driver handled the event, skb consumed
+ * 1: driver does not handle the event, please do standard processing
+ */
+int st21nfca_dep_event_received(struct nfc_hci_dev *hdev,
+ u8 event, struct sk_buff *skb)
+{
+ int r = 0;
+ struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+ pr_debug("dep event: %d\n", event);
+
+ switch (event) {
+ case ST21NFCA_EVT_CARD_ACTIVATED:
+ info->dep_info.curr_nfc_dep_pni = 0;
+ break;
+ case ST21NFCA_EVT_CARD_DEACTIVATED:
+ break;
+ case ST21NFCA_EVT_FIELD_ON:
+ break;
+ case ST21NFCA_EVT_FIELD_OFF:
+ break;
+ case ST21NFCA_EVT_SEND_DATA:
+ r = st21nfca_tm_event_send_data(hdev, skb);
+ if (r < 0)
+ return r;
+ return 0;
+ default:
+ nfc_err(&hdev->ndev->dev, "Unexpected event on card f gate\n");
+ return 1;
+ }
+ kfree_skb(skb);
+ return r;
+}
+EXPORT_SYMBOL(st21nfca_dep_event_received);
+
+static void st21nfca_im_send_psl_req(struct nfc_hci_dev *hdev, u8 did, u8 bsi,
+ u8 bri, u8 lri)
+{
+ struct sk_buff *skb;
+ struct st21nfca_psl_req *psl_req;
+ struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+ skb =
+ alloc_skb(sizeof(struct st21nfca_psl_req) + 1, GFP_KERNEL);
+ if (!skb)
+ return;
+ skb_reserve(skb, 1);
+
+ skb_put(skb, sizeof(struct st21nfca_psl_req));
+ psl_req = (struct st21nfca_psl_req *) skb->data;
+
+ psl_req->length = sizeof(struct st21nfca_psl_req);
+ psl_req->cmd0 = ST21NFCA_NFCIP1_REQ;
+ psl_req->cmd1 = ST21NFCA_NFCIP1_PSL_REQ;
+ psl_req->did = did;
+ psl_req->brs = (0x30 & bsi << 4) | (bri & 0x03);
+ psl_req->fsl = lri;
+
+ *skb_push(skb, 1) = info->dep_info.to | 0x10;
+
+ st21nfca_im_send_pdu(info, skb);
+}
+
+#define ST21NFCA_CB_TYPE_READER_F 1
+static void st21nfca_im_recv_atr_res_cb(void *context, struct sk_buff *skb,
+ int err)
+{
+ struct st21nfca_hci_info *info = context;
+ struct st21nfca_atr_res *atr_res;
+ int r;
+
+ if (err != 0)
+ return;
+
+ if (!skb)
+ return;
+
+ switch (info->async_cb_type) {
+ case ST21NFCA_CB_TYPE_READER_F:
+ skb_trim(skb, skb->len - 1);
+ atr_res = (struct st21nfca_atr_res *)skb->data;
+ r = nfc_set_remote_general_bytes(info->hdev->ndev,
+ atr_res->gbi,
+ skb->len - sizeof(struct st21nfca_atr_res));
+ if (r < 0)
+ return;
+
+ if (atr_res->to >= 0x0e)
+ info->dep_info.to = 0x0e;
+ else
+ info->dep_info.to = atr_res->to + 1;
+
+ info->dep_info.to |= 0x10;
+
+ r = nfc_dep_link_is_up(info->hdev->ndev, info->dep_info.idx,
+ NFC_COMM_PASSIVE, NFC_RF_INITIATOR);
+ if (r < 0)
+ return;
+
+ info->dep_info.curr_nfc_dep_pni = 0;
+ if (ST21NFCA_PP2LRI(atr_res->ppi) != info->dep_info.lri)
+ st21nfca_im_send_psl_req(info->hdev, atr_res->did,
+ atr_res->bsi, atr_res->bri,
+ ST21NFCA_PP2LRI(atr_res->ppi));
+ break;
+ default:
+ kfree_skb(skb);
+ break;
+ }
+}
+
+int st21nfca_im_send_atr_req(struct nfc_hci_dev *hdev, u8 *gb, size_t gb_len)
+{
+ struct sk_buff *skb;
+ struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+ struct st21nfca_atr_req *atr_req;
+ struct nfc_target *target;
+ uint size;
+
+ info->dep_info.to = ST21NFCA_DEFAULT_TIMEOUT;
+ size = ST21NFCA_ATR_REQ_MIN_SIZE + gb_len;
+ if (size > ST21NFCA_ATR_REQ_MAX_SIZE) {
+ PROTOCOL_ERR("14.6.1.1");
+ return -EINVAL;
+ }
+
+ skb =
+ alloc_skb(sizeof(struct st21nfca_atr_req) + gb_len + 1, GFP_KERNEL);
+ if (!skb)
+ return -ENOMEM;
+
+ skb_reserve(skb, 1);
+
+ skb_put(skb, sizeof(struct st21nfca_atr_req));
+
+ atr_req = (struct st21nfca_atr_req *)skb->data;
+ memset(atr_req, 0, sizeof(struct st21nfca_atr_req));
+
+ atr_req->cmd0 = ST21NFCA_NFCIP1_REQ;
+ atr_req->cmd1 = ST21NFCA_NFCIP1_ATR_REQ;
+ memset(atr_req->nfcid3, 0, NFC_NFCID3_MAXSIZE);
+ target = hdev->ndev->targets;
+
+ if (target->sensf_res_len > 0)
+ memcpy(atr_req->nfcid3, target->sensf_res,
+ target->sensf_res_len);
+ else
+ get_random_bytes(atr_req->nfcid3, NFC_NFCID3_MAXSIZE);
+
+ atr_req->did = 0x0;
+
+ atr_req->bsi = 0x00;
+ atr_req->bri = 0x00;
+ atr_req->ppi = ST21NFCA_LR_BITS_PAYLOAD_SIZE_254B;
+ if (gb_len) {
+ atr_req->ppi |= ST21NFCA_GB_BIT;
+ memcpy(skb_put(skb, gb_len), gb, gb_len);
+ }
+ atr_req->length = sizeof(struct st21nfca_atr_req) + hdev->gb_len;
+
+ *skb_push(skb, 1) = info->dep_info.to | 0x10; /* timeout */
+
+ info->async_cb_type = ST21NFCA_CB_TYPE_READER_F;
+ info->async_cb_context = info;
+ info->async_cb = st21nfca_im_recv_atr_res_cb;
+ info->dep_info.bri = atr_req->bri;
+ info->dep_info.bsi = atr_req->bsi;
+ info->dep_info.lri = ST21NFCA_PP2LRI(atr_req->ppi);
+
+ return nfc_hci_send_cmd_async(hdev, ST21NFCA_RF_READER_F_GATE,
+ ST21NFCA_WR_XCHG_DATA, skb->data,
+ skb->len, info->async_cb, info);
+}
+EXPORT_SYMBOL(st21nfca_im_send_atr_req);
+
+static void st21nfca_im_recv_dep_res_cb(void *context, struct sk_buff *skb,
+ int err)
+{
+ struct st21nfca_hci_info *info = context;
+ struct st21nfca_dep_req_res *dep_res;
+
+ int size;
+
+ if (err != 0)
+ return;
+
+ if (!skb)
+ return;
+
+ switch (info->async_cb_type) {
+ case ST21NFCA_CB_TYPE_READER_F:
+ dep_res = (struct st21nfca_dep_req_res *)skb->data;
+
+ size = 3;
+ if (skb->len < size)
+ goto exit;
+
+ if (ST21NFCA_NFC_DEP_DID_BIT_SET(dep_res->pfb))
+ size++;
+ if (ST21NFCA_NFC_DEP_NAD_BIT_SET(dep_res->pfb))
+ size++;
+
+ if (skb->len < size)
+ goto exit;
+
+ skb_trim(skb, skb->len - 1);
+
+ /* Receiving DEP_REQ - Decoding */
+ switch (ST21NFCA_NFC_DEP_PFB_TYPE(dep_res->pfb)) {
+ case ST21NFCA_NFC_DEP_PFB_ACK_NACK_PDU:
+ pr_err("Received a ACK/NACK PDU\n");
+ case ST21NFCA_NFC_DEP_PFB_I_PDU:
+ info->dep_info.curr_nfc_dep_pni =
+ ST21NFCA_NFC_DEP_PFB_PNI(dep_res->pfb + 1);
+ size++;
+ skb_pull(skb, size);
+ nfc_tm_data_received(info->hdev->ndev, skb);
+ break;
+ case ST21NFCA_NFC_DEP_PFB_SUPERVISOR_PDU:
+ pr_err("Received a SUPERVISOR PDU\n");
+ skb_pull(skb, size);
+ *skb_push(skb, 1) = ST21NFCA_NFCIP1_DEP_REQ;
+ *skb_push(skb, 1) = ST21NFCA_NFCIP1_REQ;
+ *skb_push(skb, 1) = skb->len;
+ *skb_push(skb, 1) = info->dep_info.to | 0x10;
+
+ st21nfca_im_send_pdu(info, skb);
+ break;
+ }
+
+ return;
+ default:
+ break;
+ }
+
+exit:
+ kfree_skb(skb);
+}
+
+int st21nfca_im_send_dep_req(struct nfc_hci_dev *hdev, struct sk_buff *skb)
+{
+ struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+ info->async_cb_type = ST21NFCA_CB_TYPE_READER_F;
+ info->async_cb_context = info;
+ info->async_cb = st21nfca_im_recv_dep_res_cb;
+
+ *skb_push(skb, 1) = info->dep_info.curr_nfc_dep_pni;
+ *skb_push(skb, 1) = ST21NFCA_NFCIP1_DEP_REQ;
+ *skb_push(skb, 1) = ST21NFCA_NFCIP1_REQ;
+ *skb_push(skb, 1) = skb->len;
+
+ *skb_push(skb, 1) = info->dep_info.to | 0x10;
+
+ return nfc_hci_send_cmd_async(hdev, ST21NFCA_RF_READER_F_GATE,
+ ST21NFCA_WR_XCHG_DATA,
+ skb->data, skb->len,
+ info->async_cb, info);
+}
+EXPORT_SYMBOL(st21nfca_im_send_dep_req);
+
+void st21nfca_dep_init(struct nfc_hci_dev *hdev)
+{
+ struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+ INIT_WORK(&info->dep_info.tx_work, st21nfca_tx_work);
+ info->dep_info.curr_nfc_dep_pni = 0;
+ info->dep_info.idx = 0;
+ info->dep_info.to = ST21NFCA_DEFAULT_TIMEOUT;
+}
+EXPORT_SYMBOL(st21nfca_dep_init);
+
+void st21nfca_dep_deinit(struct nfc_hci_dev *hdev)
+{
+ struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+ cancel_work_sync(&info->dep_info.tx_work);
+}
+EXPORT_SYMBOL(st21nfca_dep_deinit);
diff --git a/drivers/nfc/st21nfca/i2c.c b/drivers/nfc/st21nfca/i2c.c
new file mode 100644
index 0000000..5a82f55
--- /dev/null
+++ b/drivers/nfc/st21nfca/i2c.c
@@ -0,0 +1,735 @@
+/*
+ * I2C Link Layer for ST21NFCA HCI based Driver
+ * Copyright (C) 2014 STMicroelectronics SAS. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/crc-ccitt.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of_irq.h>
+#include <linux/of_gpio.h>
+#include <linux/acpi.h>
+#include <linux/miscdevice.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/nfc.h>
+#include <linux/firmware.h>
+#include <linux/platform_data/st21nfca.h>
+#include <asm/unaligned.h>
+
+#include <net/nfc/hci.h>
+#include <net/nfc/llc.h>
+#include <net/nfc/nfc.h>
+
+#include "st21nfca.h"
+
+/*
+ * Every frame starts with ST21NFCA_SOF_EOF and ends with ST21NFCA_SOF_EOF.
+ * Because ST21NFCA_SOF_EOF is a possible data value, there is a mecanism
+ * called byte stuffing has been introduced.
+ *
+ * if byte == ST21NFCA_SOF_EOF or ST21NFCA_ESCAPE_BYTE_STUFFING
+ * - insert ST21NFCA_ESCAPE_BYTE_STUFFING (escape byte)
+ * - xor byte with ST21NFCA_BYTE_STUFFING_MASK
+ */
+#define ST21NFCA_SOF_EOF 0x7e
+#define ST21NFCA_BYTE_STUFFING_MASK 0x20
+#define ST21NFCA_ESCAPE_BYTE_STUFFING 0x7d
+
+/* SOF + 00 */
+#define ST21NFCA_FRAME_HEADROOM 2
+
+/* 2 bytes crc + EOF */
+#define ST21NFCA_FRAME_TAILROOM 3
+#define IS_START_OF_FRAME(buf) (buf[0] == ST21NFCA_SOF_EOF && \
+ buf[1] == 0)
+
+#define ST21NFCA_HCI_I2C_DRIVER_NAME "st21nfca_hci_i2c"
+
+#define ST21NFCA_GPIO_NAME_EN "enable"
+
+struct st21nfca_i2c_phy {
+ struct i2c_client *i2c_dev;
+ struct nfc_hci_dev *hdev;
+
+ unsigned int gpio_ena;
+ unsigned int irq_polarity;
+
+ struct st21nfca_se_status se_status;
+
+ struct sk_buff *pending_skb;
+ int current_read_len;
+ /*
+ * crc might have fail because i2c macro
+ * is disable due to other interface activity
+ */
+ int crc_trials;
+
+ int powered;
+ int run_mode;
+
+ /*
+ * < 0 if hardware error occured (e.g. i2c err)
+ * and prevents normal operation.
+ */
+ int hard_fault;
+ struct mutex phy_lock;
+};
+
+static u8 len_seq[] = { 16, 24, 12, 29 };
+static u16 wait_tab[] = { 2, 3, 5, 15, 20, 40};
+
+#define I2C_DUMP_SKB(info, skb) \
+do { \
+ pr_debug("%s:\n", info); \
+ print_hex_dump(KERN_DEBUG, "i2c: ", DUMP_PREFIX_OFFSET, \
+ 16, 1, (skb)->data, (skb)->len, 0); \
+} while (0)
+
+/*
+ * In order to get the CLF in a known state we generate an internal reboot
+ * using a proprietary command.
+ * Once the reboot is completed, we expect to receive a ST21NFCA_SOF_EOF
+ * fill buffer.
+ */
+static int st21nfca_hci_platform_init(struct st21nfca_i2c_phy *phy)
+{
+ u16 wait_reboot[] = { 50, 300, 1000 };
+ char reboot_cmd[] = { 0x7E, 0x66, 0x48, 0xF6, 0x7E };
+ u8 tmp[ST21NFCA_HCI_LLC_MAX_SIZE];
+ int i, r = -1;
+
+ for (i = 0; i < ARRAY_SIZE(wait_reboot) && r < 0; i++) {
+ r = i2c_master_send(phy->i2c_dev, reboot_cmd,
+ sizeof(reboot_cmd));
+ if (r < 0)
+ msleep(wait_reboot[i]);
+ }
+ if (r < 0)
+ return r;
+
+ /* CLF is spending about 20ms to do an internal reboot */
+ msleep(20);
+ r = -1;
+ for (i = 0; i < ARRAY_SIZE(wait_reboot) && r < 0; i++) {
+ r = i2c_master_recv(phy->i2c_dev, tmp,
+ ST21NFCA_HCI_LLC_MAX_SIZE);
+ if (r < 0)
+ msleep(wait_reboot[i]);
+ }
+ if (r < 0)
+ return r;
+
+ for (i = 0; i < ST21NFCA_HCI_LLC_MAX_SIZE &&
+ tmp[i] == ST21NFCA_SOF_EOF; i++)
+ ;
+
+ if (r != ST21NFCA_HCI_LLC_MAX_SIZE)
+ return -ENODEV;
+
+ usleep_range(1000, 1500);
+ return 0;
+}
+
+static int st21nfca_hci_i2c_enable(void *phy_id)
+{
+ struct st21nfca_i2c_phy *phy = phy_id;
+
+ gpio_set_value(phy->gpio_ena, 1);
+ phy->powered = 1;
+ phy->run_mode = ST21NFCA_HCI_MODE;
+
+ usleep_range(10000, 15000);
+
+ return 0;
+}
+
+static void st21nfca_hci_i2c_disable(void *phy_id)
+{
+ struct st21nfca_i2c_phy *phy = phy_id;
+
+ gpio_set_value(phy->gpio_ena, 0);
+
+ phy->powered = 0;
+}
+
+static void st21nfca_hci_add_len_crc(struct sk_buff *skb)
+{
+ u16 crc;
+ u8 tmp;
+
+ *skb_push(skb, 1) = 0;
+
+ crc = crc_ccitt(0xffff, skb->data, skb->len);
+ crc = ~crc;
+
+ tmp = crc & 0x00ff;
+ *skb_put(skb, 1) = tmp;
+
+ tmp = (crc >> 8) & 0x00ff;
+ *skb_put(skb, 1) = tmp;
+}
+
+static void st21nfca_hci_remove_len_crc(struct sk_buff *skb)
+{
+ skb_pull(skb, ST21NFCA_FRAME_HEADROOM);
+ skb_trim(skb, skb->len - ST21NFCA_FRAME_TAILROOM);
+}
+
+/*
+ * Writing a frame must not return the number of written bytes.
+ * It must return either zero for success, or <0 for error.
+ * In addition, it must not alter the skb
+ */
+static int st21nfca_hci_i2c_write(void *phy_id, struct sk_buff *skb)
+{
+ int r = -1, i, j;
+ struct st21nfca_i2c_phy *phy = phy_id;
+ struct i2c_client *client = phy->i2c_dev;
+ u8 tmp[ST21NFCA_HCI_LLC_MAX_SIZE * 2];
+
+ I2C_DUMP_SKB("st21nfca_hci_i2c_write", skb);
+
+ if (phy->hard_fault != 0)
+ return phy->hard_fault;
+
+ /*
+ * Compute CRC before byte stuffing computation on frame
+ * Note st21nfca_hci_add_len_crc is doing a byte stuffing
+ * on its own value
+ */
+ st21nfca_hci_add_len_crc(skb);
+
+ /* add ST21NFCA_SOF_EOF on tail */
+ *skb_put(skb, 1) = ST21NFCA_SOF_EOF;
+ /* add ST21NFCA_SOF_EOF on head */
+ *skb_push(skb, 1) = ST21NFCA_SOF_EOF;
+
+ /*
+ * Compute byte stuffing
+ * if byte == ST21NFCA_SOF_EOF or ST21NFCA_ESCAPE_BYTE_STUFFING
+ * insert ST21NFCA_ESCAPE_BYTE_STUFFING (escape byte)
+ * xor byte with ST21NFCA_BYTE_STUFFING_MASK
+ */
+ tmp[0] = skb->data[0];
+ for (i = 1, j = 1; i < skb->len - 1; i++, j++) {
+ if (skb->data[i] == ST21NFCA_SOF_EOF
+ || skb->data[i] == ST21NFCA_ESCAPE_BYTE_STUFFING) {
+ tmp[j] = ST21NFCA_ESCAPE_BYTE_STUFFING;
+ j++;
+ tmp[j] = skb->data[i] ^ ST21NFCA_BYTE_STUFFING_MASK;
+ } else {
+ tmp[j] = skb->data[i];
+ }
+ }
+ tmp[j] = skb->data[i];
+ j++;
+
+ /*
+ * Manage sleep mode
+ * Try 3 times to send data with delay between each
+ */
+ mutex_lock(&phy->phy_lock);
+ for (i = 0; i < ARRAY_SIZE(wait_tab) && r < 0; i++) {
+ r = i2c_master_send(client, tmp, j);
+ if (r < 0)
+ msleep(wait_tab[i]);
+ }
+ mutex_unlock(&phy->phy_lock);
+
+ if (r >= 0) {
+ if (r != j)
+ r = -EREMOTEIO;
+ else
+ r = 0;
+ }
+
+ st21nfca_hci_remove_len_crc(skb);
+
+ return r;
+}
+
+static int get_frame_size(u8 *buf, int buflen)
+{
+ int len = 0;
+
+ if (buf[len + 1] == ST21NFCA_SOF_EOF)
+ return 0;
+
+ for (len = 1; len < buflen && buf[len] != ST21NFCA_SOF_EOF; len++)
+ ;
+
+ return len;
+}
+
+static int check_crc(u8 *buf, int buflen)
+{
+ u16 crc;
+
+ crc = crc_ccitt(0xffff, buf, buflen - 2);
+ crc = ~crc;
+
+ if (buf[buflen - 2] != (crc & 0xff) || buf[buflen - 1] != (crc >> 8)) {
+ pr_err(ST21NFCA_HCI_DRIVER_NAME
+ ": CRC error 0x%x != 0x%x 0x%x\n", crc, buf[buflen - 1],
+ buf[buflen - 2]);
+
+ pr_info(DRIVER_DESC ": %s : BAD CRC\n", __func__);
+ print_hex_dump(KERN_DEBUG, "crc: ", DUMP_PREFIX_NONE,
+ 16, 2, buf, buflen, false);
+ return -EPERM;
+ }
+ return 0;
+}
+
+/*
+ * Prepare received data for upper layer.
+ * Received data include byte stuffing, crc and sof/eof
+ * which is not usable by hci part.
+ * returns:
+ * frame size without sof/eof, header and byte stuffing
+ * -EBADMSG : frame was incorrect and discarded
+ */
+static int st21nfca_hci_i2c_repack(struct sk_buff *skb)
+{
+ int i, j, r, size;
+
+ if (skb->len < 1 || (skb->len > 1 && skb->data[1] != 0))
+ return -EBADMSG;
+
+ size = get_frame_size(skb->data, skb->len);
+ if (size > 0) {
+ skb_trim(skb, size);
+ /* remove ST21NFCA byte stuffing for upper layer */
+ for (i = 1, j = 0; i < skb->len; i++) {
+ if (skb->data[i + j] ==
+ (u8) ST21NFCA_ESCAPE_BYTE_STUFFING) {
+ skb->data[i] = skb->data[i + j + 1]
+ | ST21NFCA_BYTE_STUFFING_MASK;
+ i++;
+ j++;
+ }
+ skb->data[i] = skb->data[i + j];
+ }
+ /* remove byte stuffing useless byte */
+ skb_trim(skb, i - j);
+ /* remove ST21NFCA_SOF_EOF from head */
+ skb_pull(skb, 1);
+
+ r = check_crc(skb->data, skb->len);
+ if (r != 0) {
+ i = 0;
+ return -EBADMSG;
+ }
+
+ /* remove headbyte */
+ skb_pull(skb, 1);
+ /* remove crc. Byte Stuffing is already removed here */
+ skb_trim(skb, skb->len - 2);
+ return skb->len;
+ }
+ return 0;
+}
+
+/*
+ * Reads an shdlc frame and returns it in a newly allocated sk_buff. Guarantees
+ * that i2c bus will be flushed and that next read will start on a new frame.
+ * returned skb contains only LLC header and payload.
+ * returns:
+ * frame size : if received frame is complete (find ST21NFCA_SOF_EOF at
+ * end of read)
+ * -EAGAIN : if received frame is incomplete (not find ST21NFCA_SOF_EOF
+ * at end of read)
+ * -EREMOTEIO : i2c read error (fatal)
+ * -EBADMSG : frame was incorrect and discarded
+ * (value returned from st21nfca_hci_i2c_repack)
+ * -EIO : if no ST21NFCA_SOF_EOF is found after reaching
+ * the read length end sequence
+ */
+static int st21nfca_hci_i2c_read(struct st21nfca_i2c_phy *phy,
+ struct sk_buff *skb)
+{
+ int r, i;
+ u8 len;
+ u8 buf[ST21NFCA_HCI_LLC_MAX_PAYLOAD];
+ struct i2c_client *client = phy->i2c_dev;
+
+ if (phy->current_read_len < ARRAY_SIZE(len_seq)) {
+ len = len_seq[phy->current_read_len];
+
+ /*
+ * Add retry mecanism
+ * Operation on I2C interface may fail in case of operation on
+ * RF or SWP interface
+ */
+ r = 0;
+ mutex_lock(&phy->phy_lock);
+ for (i = 0; i < ARRAY_SIZE(wait_tab) && r <= 0; i++) {
+ r = i2c_master_recv(client, buf, len);
+ if (r < 0)
+ msleep(wait_tab[i]);
+ }
+ mutex_unlock(&phy->phy_lock);
+
+ if (r != len) {
+ phy->current_read_len = 0;
+ return -EREMOTEIO;
+ }
+
+ /*
+ * The first read sequence does not start with SOF.
+ * Data is corrupeted so we drop it.
+ */
+ if (!phy->current_read_len && !IS_START_OF_FRAME(buf)) {
+ skb_trim(skb, 0);
+ phy->current_read_len = 0;
+ return -EIO;
+ } else if (phy->current_read_len && IS_START_OF_FRAME(buf)) {
+ /*
+ * Previous frame transmission was interrupted and
+ * the frame got repeated.
+ * Received frame start with ST21NFCA_SOF_EOF + 00.
+ */
+ skb_trim(skb, 0);
+ phy->current_read_len = 0;
+ }
+
+ memcpy(skb_put(skb, len), buf, len);
+
+ if (skb->data[skb->len - 1] == ST21NFCA_SOF_EOF) {
+ phy->current_read_len = 0;
+ return st21nfca_hci_i2c_repack(skb);
+ }
+ phy->current_read_len++;
+ return -EAGAIN;
+ }
+ return -EIO;
+}
+
+/*
+ * Reads an shdlc frame from the chip. This is not as straightforward as it
+ * seems. The frame format is data-crc, and corruption can occur anywhere
+ * while transiting on i2c bus, such that we could read an invalid data.
+ * The tricky case is when we read a corrupted data or crc. We must detect
+ * this here in order to determine that data can be transmitted to the hci
+ * core. This is the reason why we check the crc here.
+ * The CLF will repeat a frame until we send a RR on that frame.
+ *
+ * On ST21NFCA, IRQ goes in idle when read starts. As no size information are
+ * available in the incoming data, other IRQ might come. Every IRQ will trigger
+ * a read sequence with different length and will fill the current frame.
+ * The reception is complete once we reach a ST21NFCA_SOF_EOF.
+ */
+static irqreturn_t st21nfca_hci_irq_thread_fn(int irq, void *phy_id)
+{
+ struct st21nfca_i2c_phy *phy = phy_id;
+ struct i2c_client *client;
+
+ int r;
+
+ if (!phy || irq != phy->i2c_dev->irq) {
+ WARN_ON_ONCE(1);
+ return IRQ_NONE;
+ }
+
+ client = phy->i2c_dev;
+ dev_dbg(&client->dev, "IRQ\n");
+
+ if (phy->hard_fault != 0)
+ return IRQ_HANDLED;
+
+ r = st21nfca_hci_i2c_read(phy, phy->pending_skb);
+ if (r == -EREMOTEIO) {
+ phy->hard_fault = r;
+
+ nfc_hci_recv_frame(phy->hdev, NULL);
+
+ return IRQ_HANDLED;
+ } else if (r == -EAGAIN || r == -EIO) {
+ return IRQ_HANDLED;
+ } else if (r == -EBADMSG && phy->crc_trials < ARRAY_SIZE(wait_tab)) {
+ /*
+ * With ST21NFCA, only one interface (I2C, RF or SWP)
+ * may be active at a time.
+ * Having incorrect crc is usually due to i2c macrocell
+ * deactivation in the middle of a transmission.
+ * It may generate corrupted data on i2c.
+ * We give sometime to get i2c back.
+ * The complete frame will be repeated.
+ */
+ msleep(wait_tab[phy->crc_trials]);
+ phy->crc_trials++;
+ phy->current_read_len = 0;
+ kfree_skb(phy->pending_skb);
+ } else if (r > 0) {
+ /*
+ * We succeeded to read data from the CLF and
+ * data is valid.
+ * Reset counter.
+ */
+ nfc_hci_recv_frame(phy->hdev, phy->pending_skb);
+ phy->crc_trials = 0;
+ } else {
+ kfree_skb(phy->pending_skb);
+ }
+
+ phy->pending_skb = alloc_skb(ST21NFCA_HCI_LLC_MAX_SIZE * 2, GFP_KERNEL);
+ if (phy->pending_skb == NULL) {
+ phy->hard_fault = -ENOMEM;
+ nfc_hci_recv_frame(phy->hdev, NULL);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static struct nfc_phy_ops i2c_phy_ops = {
+ .write = st21nfca_hci_i2c_write,
+ .enable = st21nfca_hci_i2c_enable,
+ .disable = st21nfca_hci_i2c_disable,
+};
+
+static int st21nfca_hci_i2c_acpi_request_resources(struct i2c_client *client)
+{
+ struct st21nfca_i2c_phy *phy = i2c_get_clientdata(client);
+ struct gpio_desc *gpiod_ena;
+ struct device *dev = &client->dev;
+ u8 tmp;
+
+ /* Get EN GPIO from ACPI */
+ gpiod_ena = devm_gpiod_get_index(dev, ST21NFCA_GPIO_NAME_EN, 1,
+ GPIOD_OUT_LOW);
+ if (!IS_ERR(gpiod_ena)) {
+ nfc_err(dev, "Unable to get ENABLE GPIO\n");
+ return -ENODEV;
+ }
+
+ phy->gpio_ena = desc_to_gpio(gpiod_ena);
+
+ phy->irq_polarity = irq_get_trigger_type(client->irq);
+
+ phy->se_status.is_ese_present = false;
+ phy->se_status.is_uicc_present = false;
+
+ if (device_property_present(dev, "ese-present")) {
+ device_property_read_u8(dev, "ese-present", &tmp);
+ phy->se_status.is_ese_present = tmp;
+ }
+
+ if (device_property_present(dev, "uicc-present")) {
+ device_property_read_u8(dev, "uicc-present", &tmp);
+ phy->se_status.is_uicc_present = tmp;
+ }
+
+ return 0;
+}
+
+static int st21nfca_hci_i2c_of_request_resources(struct i2c_client *client)
+{
+ struct st21nfca_i2c_phy *phy = i2c_get_clientdata(client);
+ struct device_node *pp;
+ int gpio;
+ int r;
+
+ pp = client->dev.of_node;
+ if (!pp)
+ return -ENODEV;
+
+ /* Get GPIO from device tree */
+ gpio = of_get_named_gpio(pp, "enable-gpios", 0);
+ if (gpio < 0) {
+ nfc_err(&client->dev, "Failed to retrieve enable-gpios from device tree\n");
+ return gpio;
+ }
+
+ /* GPIO request and configuration */
+ r = devm_gpio_request_one(&client->dev, gpio, GPIOF_OUT_INIT_HIGH,
+ ST21NFCA_GPIO_NAME_EN);
+ if (r) {
+ nfc_err(&client->dev, "Failed to request enable pin\n");
+ return r;
+ }
+
+ phy->gpio_ena = gpio;
+
+ phy->irq_polarity = irq_get_trigger_type(client->irq);
+
+ phy->se_status.is_ese_present =
+ of_property_read_bool(pp, "ese-present");
+ phy->se_status.is_uicc_present =
+ of_property_read_bool(pp, "uicc-present");
+
+ return 0;
+}
+
+static int st21nfca_hci_i2c_request_resources(struct i2c_client *client)
+{
+ struct st21nfca_nfc_platform_data *pdata;
+ struct st21nfca_i2c_phy *phy = i2c_get_clientdata(client);
+ int r;
+
+ pdata = client->dev.platform_data;
+ if (pdata == NULL) {
+ nfc_err(&client->dev, "No platform data\n");
+ return -EINVAL;
+ }
+
+ /* store for later use */
+ phy->gpio_ena = pdata->gpio_ena;
+ phy->irq_polarity = pdata->irq_polarity;
+
+ if (phy->gpio_ena > 0) {
+ r = devm_gpio_request_one(&client->dev, phy->gpio_ena,
+ GPIOF_OUT_INIT_HIGH,
+ ST21NFCA_GPIO_NAME_EN);
+ if (r) {
+ pr_err("%s : ena gpio_request failed\n", __FILE__);
+ return r;
+ }
+ }
+
+ phy->se_status.is_ese_present = pdata->is_ese_present;
+ phy->se_status.is_uicc_present = pdata->is_uicc_present;
+
+ return 0;
+}
+
+static int st21nfca_hci_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct st21nfca_i2c_phy *phy;
+ struct st21nfca_nfc_platform_data *pdata;
+ int r;
+
+ 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, "Need I2C_FUNC_I2C\n");
+ return -ENODEV;
+ }
+
+ phy = devm_kzalloc(&client->dev, sizeof(struct st21nfca_i2c_phy),
+ GFP_KERNEL);
+ if (!phy)
+ return -ENOMEM;
+
+ phy->i2c_dev = client;
+ phy->pending_skb = alloc_skb(ST21NFCA_HCI_LLC_MAX_SIZE * 2, GFP_KERNEL);
+ if (phy->pending_skb == NULL)
+ return -ENOMEM;
+
+ phy->current_read_len = 0;
+ phy->crc_trials = 0;
+ mutex_init(&phy->phy_lock);
+ i2c_set_clientdata(client, phy);
+
+ pdata = client->dev.platform_data;
+ if (!pdata && client->dev.of_node) {
+ r = st21nfca_hci_i2c_of_request_resources(client);
+ if (r) {
+ nfc_err(&client->dev, "No platform data\n");
+ return r;
+ }
+ } else if (pdata) {
+ r = st21nfca_hci_i2c_request_resources(client);
+ if (r) {
+ nfc_err(&client->dev, "Cannot get platform resources\n");
+ return r;
+ }
+ } else if (ACPI_HANDLE(&client->dev)) {
+ r = st21nfca_hci_i2c_acpi_request_resources(client);
+ if (r) {
+ nfc_err(&client->dev, "Cannot get ACPI data\n");
+ return r;
+ }
+ } else {
+ nfc_err(&client->dev, "st21nfca platform resources not available\n");
+ return -ENODEV;
+ }
+
+ r = st21nfca_hci_platform_init(phy);
+ if (r < 0) {
+ nfc_err(&client->dev, "Unable to reboot st21nfca\n");
+ return r;
+ }
+
+ r = devm_request_threaded_irq(&client->dev, client->irq, NULL,
+ st21nfca_hci_irq_thread_fn,
+ phy->irq_polarity | IRQF_ONESHOT,
+ ST21NFCA_HCI_DRIVER_NAME, phy);
+ if (r < 0) {
+ nfc_err(&client->dev, "Unable to register IRQ handler\n");
+ return r;
+ }
+
+ return st21nfca_hci_probe(phy, &i2c_phy_ops, LLC_SHDLC_NAME,
+ ST21NFCA_FRAME_HEADROOM,
+ ST21NFCA_FRAME_TAILROOM,
+ ST21NFCA_HCI_LLC_MAX_PAYLOAD,
+ &phy->hdev,
+ &phy->se_status);
+}
+
+static int st21nfca_hci_i2c_remove(struct i2c_client *client)
+{
+ struct st21nfca_i2c_phy *phy = i2c_get_clientdata(client);
+
+ dev_dbg(&client->dev, "%s\n", __func__);
+
+ st21nfca_hci_remove(phy->hdev);
+
+ if (phy->powered)
+ st21nfca_hci_i2c_disable(phy);
+
+ return 0;
+}
+
+static struct i2c_device_id st21nfca_hci_i2c_id_table[] = {
+ {ST21NFCA_HCI_DRIVER_NAME, 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, st21nfca_hci_i2c_id_table);
+
+static const struct acpi_device_id st21nfca_hci_i2c_acpi_match[] = {
+ {"SMO2100", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, st21nfca_hci_i2c_acpi_match);
+
+static const struct of_device_id of_st21nfca_i2c_match[] = {
+ { .compatible = "st,st21nfca-i2c", },
+ { .compatible = "st,st21nfca_i2c", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, of_st21nfca_i2c_match);
+
+static struct i2c_driver st21nfca_hci_i2c_driver = {
+ .driver = {
+ .name = ST21NFCA_HCI_I2C_DRIVER_NAME,
+ .of_match_table = of_match_ptr(of_st21nfca_i2c_match),
+ .acpi_match_table = ACPI_PTR(st21nfca_hci_i2c_acpi_match),
+ },
+ .probe = st21nfca_hci_i2c_probe,
+ .id_table = st21nfca_hci_i2c_id_table,
+ .remove = st21nfca_hci_i2c_remove,
+};
+module_i2c_driver(st21nfca_hci_i2c_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/nfc/st21nfca/se.c b/drivers/nfc/st21nfca/se.c
new file mode 100644
index 0000000..3a98563
--- /dev/null
+++ b/drivers/nfc/st21nfca/se.c
@@ -0,0 +1,427 @@
+/*
+ * Copyright (C) 2014 STMicroelectronics SAS. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <net/nfc/hci.h>
+
+#include "st21nfca.h"
+
+#define ST21NFCA_EVT_UICC_ACTIVATE 0x10
+#define ST21NFCA_EVT_UICC_DEACTIVATE 0x13
+#define ST21NFCA_EVT_SE_HARD_RESET 0x20
+#define ST21NFCA_EVT_SE_SOFT_RESET 0x11
+#define ST21NFCA_EVT_SE_END_OF_APDU_TRANSFER 0x21
+#define ST21NFCA_EVT_SE_ACTIVATE 0x22
+#define ST21NFCA_EVT_SE_DEACTIVATE 0x23
+
+#define ST21NFCA_EVT_TRANSMIT_DATA 0x10
+#define ST21NFCA_EVT_WTX_REQUEST 0x11
+
+#define ST21NFCA_EVT_CONNECTIVITY 0x10
+#define ST21NFCA_EVT_TRANSACTION 0x12
+
+#define ST21NFCA_SE_TO_HOT_PLUG 1000
+/* Connectivity pipe only */
+#define ST21NFCA_SE_COUNT_PIPE_UICC 0x01
+/* Connectivity + APDU Reader pipe */
+#define ST21NFCA_SE_COUNT_PIPE_EMBEDDED 0x02
+
+#define ST21NFCA_SE_MODE_OFF 0x00
+#define ST21NFCA_SE_MODE_ON 0x01
+
+#define ST21NFCA_PARAM_ATR 0x01
+#define ST21NFCA_ATR_DEFAULT_BWI 0x04
+
+/*
+ * WT = 2^BWI/10[s], convert into msecs and add a secure
+ * room by increasing by 2 this timeout
+ */
+#define ST21NFCA_BWI_TO_TIMEOUT(x) ((1 << x) * 200)
+#define ST21NFCA_ATR_GET_Y_FROM_TD(x) (x >> 4)
+
+/* If TA is present bit 0 is set */
+#define ST21NFCA_ATR_TA_PRESENT(x) (x & 0x01)
+/* If TB is present bit 1 is set */
+#define ST21NFCA_ATR_TB_PRESENT(x) (x & 0x02)
+
+static u8 st21nfca_se_get_bwi(struct nfc_hci_dev *hdev)
+{
+ int i;
+ u8 td;
+ struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+ /* Bits 8 to 5 of the first TB for T=1 encode BWI from zero to nine */
+ for (i = 1; i < ST21NFCA_ESE_MAX_LENGTH; i++) {
+ td = ST21NFCA_ATR_GET_Y_FROM_TD(info->se_info.atr[i]);
+ if (ST21NFCA_ATR_TA_PRESENT(td))
+ i++;
+ if (ST21NFCA_ATR_TB_PRESENT(td)) {
+ i++;
+ return info->se_info.atr[i] >> 4;
+ }
+ }
+ return ST21NFCA_ATR_DEFAULT_BWI;
+}
+
+static void st21nfca_se_get_atr(struct nfc_hci_dev *hdev)
+{
+ int r;
+ struct sk_buff *skb;
+ struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+ r = nfc_hci_get_param(hdev, ST21NFCA_APDU_READER_GATE,
+ ST21NFCA_PARAM_ATR, &skb);
+ if (r < 0)
+ return;
+
+ if (skb->len <= ST21NFCA_ESE_MAX_LENGTH) {
+ memcpy(info->se_info.atr, skb->data, skb->len);
+ info->se_info.wt_timeout =
+ ST21NFCA_BWI_TO_TIMEOUT(st21nfca_se_get_bwi(hdev));
+ }
+ kfree_skb(skb);
+}
+
+static int st21nfca_hci_control_se(struct nfc_hci_dev *hdev, u32 se_idx,
+ u8 state)
+{
+ struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+ int r, i;
+ struct sk_buff *sk_host_list;
+ u8 se_event, host_id;
+
+ switch (se_idx) {
+ case NFC_HCI_UICC_HOST_ID:
+ se_event = (state == ST21NFCA_SE_MODE_ON ?
+ ST21NFCA_EVT_UICC_ACTIVATE :
+ ST21NFCA_EVT_UICC_DEACTIVATE);
+
+ info->se_info.count_pipes = 0;
+ info->se_info.expected_pipes = ST21NFCA_SE_COUNT_PIPE_UICC;
+ break;
+ case ST21NFCA_ESE_HOST_ID:
+ se_event = (state == ST21NFCA_SE_MODE_ON ?
+ ST21NFCA_EVT_SE_ACTIVATE :
+ ST21NFCA_EVT_SE_DEACTIVATE);
+
+ info->se_info.count_pipes = 0;
+ info->se_info.expected_pipes = ST21NFCA_SE_COUNT_PIPE_EMBEDDED;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /*
+ * Wait for an EVT_HOT_PLUG in order to
+ * retrieve a relevant host list.
+ */
+ reinit_completion(&info->se_info.req_completion);
+ r = nfc_hci_send_event(hdev, ST21NFCA_DEVICE_MGNT_GATE, se_event,
+ NULL, 0);
+ if (r < 0)
+ return r;
+
+ mod_timer(&info->se_info.se_active_timer, jiffies +
+ msecs_to_jiffies(ST21NFCA_SE_TO_HOT_PLUG));
+ info->se_info.se_active = true;
+
+ /* Ignore return value and check in any case the host_list */
+ wait_for_completion_interruptible(&info->se_info.req_completion);
+
+ r = nfc_hci_get_param(hdev, NFC_HCI_ADMIN_GATE,
+ NFC_HCI_ADMIN_HOST_LIST,
+ &sk_host_list);
+ if (r < 0)
+ return r;
+
+ for (i = 0; i < sk_host_list->len &&
+ sk_host_list->data[i] != se_idx; i++)
+ ;
+ host_id = sk_host_list->data[i];
+ kfree_skb(sk_host_list);
+
+ if (state == ST21NFCA_SE_MODE_ON && host_id == se_idx)
+ return se_idx;
+ else if (state == ST21NFCA_SE_MODE_OFF && host_id != se_idx)
+ return se_idx;
+
+ return -1;
+}
+
+int st21nfca_hci_discover_se(struct nfc_hci_dev *hdev)
+{
+ struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+ int se_count = 0;
+
+ if (test_bit(ST21NFCA_FACTORY_MODE, &hdev->quirks))
+ return 0;
+
+ if (info->se_status->is_uicc_present) {
+ nfc_add_se(hdev->ndev, NFC_HCI_UICC_HOST_ID, NFC_SE_UICC);
+ se_count++;
+ }
+
+ if (info->se_status->is_ese_present) {
+ nfc_add_se(hdev->ndev, ST21NFCA_ESE_HOST_ID, NFC_SE_EMBEDDED);
+ se_count++;
+ }
+
+ return !se_count;
+}
+EXPORT_SYMBOL(st21nfca_hci_discover_se);
+
+int st21nfca_hci_enable_se(struct nfc_hci_dev *hdev, u32 se_idx)
+{
+ int r;
+
+ /*
+ * According to upper layer, se_idx == NFC_SE_UICC when
+ * info->se_status->is_uicc_enable is true should never happen.
+ * Same for eSE.
+ */
+ r = st21nfca_hci_control_se(hdev, se_idx, ST21NFCA_SE_MODE_ON);
+ if (r == ST21NFCA_ESE_HOST_ID) {
+ st21nfca_se_get_atr(hdev);
+ r = nfc_hci_send_event(hdev, ST21NFCA_APDU_READER_GATE,
+ ST21NFCA_EVT_SE_SOFT_RESET, NULL, 0);
+ if (r < 0)
+ return r;
+ } else if (r < 0) {
+ /*
+ * The activation tentative failed, the secure element
+ * is not connected. Remove from the list.
+ */
+ nfc_remove_se(hdev->ndev, se_idx);
+ return r;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(st21nfca_hci_enable_se);
+
+int st21nfca_hci_disable_se(struct nfc_hci_dev *hdev, u32 se_idx)
+{
+ int r;
+
+ /*
+ * According to upper layer, se_idx == NFC_SE_UICC when
+ * info->se_status->is_uicc_enable is true should never happen
+ * Same for eSE.
+ */
+ r = st21nfca_hci_control_se(hdev, se_idx, ST21NFCA_SE_MODE_OFF);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+EXPORT_SYMBOL(st21nfca_hci_disable_se);
+
+int st21nfca_hci_se_io(struct nfc_hci_dev *hdev, u32 se_idx,
+ u8 *apdu, size_t apdu_length,
+ se_io_cb_t cb, void *cb_context)
+{
+ struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+ pr_debug("se_io %x\n", se_idx);
+
+ switch (se_idx) {
+ case ST21NFCA_ESE_HOST_ID:
+ info->se_info.cb = cb;
+ info->se_info.cb_context = cb_context;
+ mod_timer(&info->se_info.bwi_timer, jiffies +
+ msecs_to_jiffies(info->se_info.wt_timeout));
+ info->se_info.bwi_active = true;
+ return nfc_hci_send_event(hdev, ST21NFCA_APDU_READER_GATE,
+ ST21NFCA_EVT_TRANSMIT_DATA,
+ apdu, apdu_length);
+ default:
+ return -ENODEV;
+ }
+}
+EXPORT_SYMBOL(st21nfca_hci_se_io);
+
+static void st21nfca_se_wt_timeout(unsigned long data)
+{
+ /*
+ * No answer from the secure element
+ * within the defined timeout.
+ * Let's send a reset request as recovery procedure.
+ * According to the situation, we first try to send a software reset
+ * to the secure element. If the next command is still not
+ * answering in time, we send to the CLF a secure element hardware
+ * reset request.
+ */
+ /* hardware reset managed through VCC_UICC_OUT power supply */
+ u8 param = 0x01;
+ struct st21nfca_hci_info *info = (struct st21nfca_hci_info *) data;
+
+ pr_debug("\n");
+
+ info->se_info.bwi_active = false;
+
+ if (!info->se_info.xch_error) {
+ info->se_info.xch_error = true;
+ nfc_hci_send_event(info->hdev, ST21NFCA_APDU_READER_GATE,
+ ST21NFCA_EVT_SE_SOFT_RESET, NULL, 0);
+ } else {
+ info->se_info.xch_error = false;
+ nfc_hci_send_event(info->hdev, ST21NFCA_DEVICE_MGNT_GATE,
+ ST21NFCA_EVT_SE_HARD_RESET, &param, 1);
+ }
+ info->se_info.cb(info->se_info.cb_context, NULL, 0, -ETIME);
+}
+
+static void st21nfca_se_activation_timeout(unsigned long data)
+{
+ struct st21nfca_hci_info *info = (struct st21nfca_hci_info *) data;
+
+ pr_debug("\n");
+
+ info->se_info.se_active = false;
+
+ complete(&info->se_info.req_completion);
+}
+
+/*
+ * Returns:
+ * <= 0: driver handled the event, skb consumed
+ * 1: driver does not handle the event, please do standard processing
+ */
+int st21nfca_connectivity_event_received(struct nfc_hci_dev *hdev, u8 host,
+ u8 event, struct sk_buff *skb)
+{
+ int r = 0;
+ struct device *dev = &hdev->ndev->dev;
+ struct nfc_evt_transaction *transaction;
+
+ pr_debug("connectivity gate event: %x\n", event);
+
+ switch (event) {
+ case ST21NFCA_EVT_CONNECTIVITY:
+ r = nfc_se_connectivity(hdev->ndev, host);
+ break;
+ case ST21NFCA_EVT_TRANSACTION:
+ /*
+ * According to specification etsi 102 622
+ * 11.2.2.4 EVT_TRANSACTION Table 52
+ * Description Tag Length
+ * AID 81 5 to 16
+ * PARAMETERS 82 0 to 255
+ */
+ if (skb->len < NFC_MIN_AID_LENGTH + 2 &&
+ skb->data[0] != NFC_EVT_TRANSACTION_AID_TAG)
+ return -EPROTO;
+
+ transaction = (struct nfc_evt_transaction *)devm_kzalloc(dev,
+ skb->len - 2, GFP_KERNEL);
+
+ transaction->aid_len = skb->data[1];
+ memcpy(transaction->aid, &skb->data[2],
+ transaction->aid_len);
+
+ /* Check next byte is PARAMETERS tag (82) */
+ if (skb->data[transaction->aid_len + 2] !=
+ NFC_EVT_TRANSACTION_PARAMS_TAG)
+ return -EPROTO;
+
+ transaction->params_len = skb->data[transaction->aid_len + 3];
+ memcpy(transaction->params, skb->data +
+ transaction->aid_len + 4, transaction->params_len);
+
+ r = nfc_se_transaction(hdev->ndev, host, transaction);
+ break;
+ default:
+ nfc_err(&hdev->ndev->dev, "Unexpected event on connectivity gate\n");
+ return 1;
+ }
+ kfree_skb(skb);
+ return r;
+}
+EXPORT_SYMBOL(st21nfca_connectivity_event_received);
+
+int st21nfca_apdu_reader_event_received(struct nfc_hci_dev *hdev,
+ u8 event, struct sk_buff *skb)
+{
+ int r = 0;
+ struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+ pr_debug("apdu reader gate event: %x\n", event);
+
+ switch (event) {
+ case ST21NFCA_EVT_TRANSMIT_DATA:
+ del_timer_sync(&info->se_info.bwi_timer);
+ info->se_info.bwi_active = false;
+ r = nfc_hci_send_event(hdev, ST21NFCA_DEVICE_MGNT_GATE,
+ ST21NFCA_EVT_SE_END_OF_APDU_TRANSFER, NULL, 0);
+ if (r < 0)
+ goto exit;
+
+ info->se_info.cb(info->se_info.cb_context,
+ skb->data, skb->len, 0);
+ break;
+ case ST21NFCA_EVT_WTX_REQUEST:
+ mod_timer(&info->se_info.bwi_timer, jiffies +
+ msecs_to_jiffies(info->se_info.wt_timeout));
+ break;
+ default:
+ nfc_err(&hdev->ndev->dev, "Unexpected event on apdu reader gate\n");
+ return 1;
+ }
+
+exit:
+ kfree_skb(skb);
+ return r;
+}
+EXPORT_SYMBOL(st21nfca_apdu_reader_event_received);
+
+void st21nfca_se_init(struct nfc_hci_dev *hdev)
+{
+ struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+ init_completion(&info->se_info.req_completion);
+ /* initialize timers */
+ init_timer(&info->se_info.bwi_timer);
+ info->se_info.bwi_timer.data = (unsigned long)info;
+ info->se_info.bwi_timer.function = st21nfca_se_wt_timeout;
+ info->se_info.bwi_active = false;
+
+ init_timer(&info->se_info.se_active_timer);
+ info->se_info.se_active_timer.data = (unsigned long)info;
+ info->se_info.se_active_timer.function = st21nfca_se_activation_timeout;
+ info->se_info.se_active = false;
+
+ info->se_info.count_pipes = 0;
+ info->se_info.expected_pipes = 0;
+
+ info->se_info.xch_error = false;
+
+ info->se_info.wt_timeout =
+ ST21NFCA_BWI_TO_TIMEOUT(ST21NFCA_ATR_DEFAULT_BWI);
+}
+EXPORT_SYMBOL(st21nfca_se_init);
+
+void st21nfca_se_deinit(struct nfc_hci_dev *hdev)
+{
+ struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+ if (info->se_info.bwi_active)
+ del_timer_sync(&info->se_info.bwi_timer);
+ if (info->se_info.se_active)
+ del_timer_sync(&info->se_info.se_active_timer);
+
+ info->se_info.bwi_active = false;
+ info->se_info.se_active = false;
+}
+EXPORT_SYMBOL(st21nfca_se_deinit);
diff --git a/drivers/nfc/st21nfca/st21nfca.h b/drivers/nfc/st21nfca/st21nfca.h
new file mode 100644
index 0000000..94ffb05
--- /dev/null
+++ b/drivers/nfc/st21nfca/st21nfca.h
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2014 STMicroelectronics SAS. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LOCAL_ST21NFCA_H_
+#define __LOCAL_ST21NFCA_H_
+
+#include <net/nfc/hci.h>
+#include <linux/skbuff.h>
+#include <linux/workqueue.h>
+
+#define HCI_MODE 0
+
+/* framing in HCI mode */
+#define ST21NFCA_SOF_EOF_LEN 2
+
+/* Almost every time value is 0 */
+#define ST21NFCA_HCI_LLC_LEN 1
+
+/* Size in worst case :
+ * In normal case CRC len = 2 but byte stuffing
+ * may appear in case one CRC byte = ST21NFCA_SOF_EOF
+ */
+#define ST21NFCA_HCI_LLC_CRC 4
+
+#define ST21NFCA_HCI_LLC_LEN_CRC (ST21NFCA_SOF_EOF_LEN + \
+ ST21NFCA_HCI_LLC_LEN + \
+ ST21NFCA_HCI_LLC_CRC)
+#define ST21NFCA_HCI_LLC_MIN_SIZE (1 + ST21NFCA_HCI_LLC_LEN_CRC)
+
+/* Worst case when adding byte stuffing between each byte */
+#define ST21NFCA_HCI_LLC_MAX_PAYLOAD 29
+#define ST21NFCA_HCI_LLC_MAX_SIZE (ST21NFCA_HCI_LLC_LEN_CRC + 1 + \
+ ST21NFCA_HCI_LLC_MAX_PAYLOAD)
+
+/* Reader RF commands */
+#define ST21NFCA_WR_XCHG_DATA 0x10
+
+#define ST21NFCA_DEVICE_MGNT_GATE 0x01
+#define ST21NFCA_RF_READER_F_GATE 0x14
+#define ST21NFCA_RF_CARD_F_GATE 0x24
+#define ST21NFCA_APDU_READER_GATE 0xf0
+#define ST21NFCA_CONNECTIVITY_GATE 0x41
+
+/*
+ * ref ISO7816-3 chap 8.1. the initial character TS is followed by a
+ * sequence of at most 32 characters.
+ */
+#define ST21NFCA_ESE_MAX_LENGTH 33
+#define ST21NFCA_ESE_HOST_ID 0xc0
+
+#define DRIVER_DESC "HCI NFC driver for ST21NFCA"
+
+#define ST21NFCA_HCI_MODE 0
+#define ST21NFCA_NUM_DEVICES 256
+
+#define ST21NFCA_VENDOR_OUI 0x0080E1 /* STMicroelectronics */
+#define ST21NFCA_FACTORY_MODE 2
+
+struct st21nfca_se_status {
+ bool is_ese_present;
+ bool is_uicc_present;
+};
+
+enum st21nfca_state {
+ ST21NFCA_ST_COLD,
+ ST21NFCA_ST_READY,
+};
+
+/**
+ * enum nfc_vendor_cmds - supported nfc vendor commands
+ *
+ * @FACTORY_MODE: Allow to set the driver into a mode where no secure element
+ * are activated. It does not consider any NFC_ATTR_VENDOR_DATA.
+ * @HCI_CLEAR_ALL_PIPES: Allow to execute a HCI clear all pipes command.
+ * It does not consider any NFC_ATTR_VENDOR_DATA.
+ * @HCI_DM_PUT_DATA: Allow to configure specific CLF registry as for example
+ * RF trimmings or low level drivers configurations (I2C, SPI, SWP).
+ * @HCI_DM_UPDATE_AID: Allow to configure an AID routing into the CLF routing
+ * table following RF technology, CLF mode or protocol.
+ * @HCI_DM_GET_INFO: Allow to retrieve CLF information.
+ * @HCI_DM_GET_DATA: Allow to retrieve CLF configurable data such as low
+ * level drivers configurations or RF trimmings.
+ * @HCI_DM_LOAD: Allow to load a firmware into the CLF. A complete
+ * packet can be more than 8KB.
+ * @HCI_DM_RESET: Allow to run a CLF reset in order to "commit" CLF
+ * configuration changes without CLF power off.
+ * @HCI_GET_PARAM: Allow to retrieve an HCI CLF parameter (for example the
+ * white list).
+ * @HCI_DM_FIELD_GENERATOR: Allow to generate different kind of RF
+ * technology. When using this command to anti-collision is done.
+ * @HCI_LOOPBACK: Allow to echo a command and test the Dh to CLF
+ * connectivity.
+ */
+enum nfc_vendor_cmds {
+ FACTORY_MODE,
+ HCI_CLEAR_ALL_PIPES,
+ HCI_DM_PUT_DATA,
+ HCI_DM_UPDATE_AID,
+ HCI_DM_GET_INFO,
+ HCI_DM_GET_DATA,
+ HCI_DM_LOAD,
+ HCI_DM_RESET,
+ HCI_GET_PARAM,
+ HCI_DM_FIELD_GENERATOR,
+ HCI_LOOPBACK,
+};
+
+struct st21nfca_vendor_info {
+ struct completion req_completion;
+ struct sk_buff *rx_skb;
+};
+
+struct st21nfca_dep_info {
+ struct sk_buff *tx_pending;
+ struct work_struct tx_work;
+ u8 curr_nfc_dep_pni;
+ u32 idx;
+ u8 to;
+ u8 did;
+ u8 bsi;
+ u8 bri;
+ u8 lri;
+} __packed;
+
+struct st21nfca_se_info {
+ u8 atr[ST21NFCA_ESE_MAX_LENGTH];
+ struct completion req_completion;
+
+ struct timer_list bwi_timer;
+ int wt_timeout; /* in msecs */
+ bool bwi_active;
+
+ struct timer_list se_active_timer;
+ bool se_active;
+ int expected_pipes;
+ int count_pipes;
+
+ bool xch_error;
+
+ se_io_cb_t cb;
+ void *cb_context;
+};
+
+struct st21nfca_hci_info {
+ struct nfc_phy_ops *phy_ops;
+ void *phy_id;
+
+ struct nfc_hci_dev *hdev;
+ struct st21nfca_se_status *se_status;
+
+ enum st21nfca_state state;
+
+ struct mutex info_lock;
+
+ int async_cb_type;
+ data_exchange_cb_t async_cb;
+ void *async_cb_context;
+
+ struct st21nfca_dep_info dep_info;
+ struct st21nfca_se_info se_info;
+ struct st21nfca_vendor_info vendor_info;
+};
+
+int st21nfca_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops,
+ char *llc_name, int phy_headroom, int phy_tailroom,
+ int phy_payload, struct nfc_hci_dev **hdev,
+ struct st21nfca_se_status *se_status);
+void st21nfca_hci_remove(struct nfc_hci_dev *hdev);
+
+int st21nfca_dep_event_received(struct nfc_hci_dev *hdev,
+ u8 event, struct sk_buff *skb);
+int st21nfca_tm_send_dep_res(struct nfc_hci_dev *hdev, struct sk_buff *skb);
+
+int st21nfca_im_send_atr_req(struct nfc_hci_dev *hdev, u8 *gb, size_t gb_len);
+int st21nfca_im_send_dep_req(struct nfc_hci_dev *hdev, struct sk_buff *skb);
+void st21nfca_dep_init(struct nfc_hci_dev *hdev);
+void st21nfca_dep_deinit(struct nfc_hci_dev *hdev);
+
+int st21nfca_connectivity_event_received(struct nfc_hci_dev *hdev, u8 host,
+ u8 event, struct sk_buff *skb);
+int st21nfca_apdu_reader_event_received(struct nfc_hci_dev *hdev,
+ u8 event, struct sk_buff *skb);
+
+int st21nfca_hci_discover_se(struct nfc_hci_dev *hdev);
+int st21nfca_hci_enable_se(struct nfc_hci_dev *hdev, u32 se_idx);
+int st21nfca_hci_disable_se(struct nfc_hci_dev *hdev, u32 se_idx);
+int st21nfca_hci_se_io(struct nfc_hci_dev *hdev, u32 se_idx,
+ u8 *apdu, size_t apdu_length,
+ se_io_cb_t cb, void *cb_context);
+
+void st21nfca_se_init(struct nfc_hci_dev *hdev);
+void st21nfca_se_deinit(struct nfc_hci_dev *hdev);
+
+int st21nfca_hci_loopback_event_received(struct nfc_hci_dev *ndev, u8 event,
+ struct sk_buff *skb);
+int st21nfca_vendor_cmds_init(struct nfc_hci_dev *ndev);
+
+#endif /* __LOCAL_ST21NFCA_H_ */
diff --git a/drivers/nfc/st21nfca/vendor_cmds.c b/drivers/nfc/st21nfca/vendor_cmds.c
new file mode 100644
index 0000000..ab765e5
--- /dev/null
+++ b/drivers/nfc/st21nfca/vendor_cmds.c
@@ -0,0 +1,375 @@
+/*
+ * Proprietary commands extension for STMicroelectronics NFC Chip
+ *
+ * Copyright (C) 2014-2015 STMicroelectronics SAS. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <net/genetlink.h>
+#include <linux/module.h>
+#include <linux/nfc.h>
+#include <net/nfc/hci.h>
+#include <net/nfc/llc.h>
+
+#include "st21nfca.h"
+
+#define ST21NFCA_HCI_DM_GETDATA 0x10
+#define ST21NFCA_HCI_DM_PUTDATA 0x11
+#define ST21NFCA_HCI_DM_LOAD 0x12
+#define ST21NFCA_HCI_DM_GETINFO 0x13
+#define ST21NFCA_HCI_DM_UPDATE_AID 0x20
+#define ST21NFCA_HCI_DM_RESET 0x3e
+
+#define ST21NFCA_HCI_DM_FIELD_GENERATOR 0x32
+
+#define ST21NFCA_FACTORY_MODE_ON 1
+#define ST21NFCA_FACTORY_MODE_OFF 0
+
+#define ST21NFCA_EVT_POST_DATA 0x02
+
+struct get_param_data {
+ u8 gate;
+ u8 data;
+} __packed;
+
+static int st21nfca_factory_mode(struct nfc_dev *dev, void *data,
+ size_t data_len)
+{
+ struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
+
+ if (data_len != 1)
+ return -EINVAL;
+
+ pr_debug("factory mode: %x\n", ((u8 *)data)[0]);
+
+ switch (((u8 *)data)[0]) {
+ case ST21NFCA_FACTORY_MODE_ON:
+ test_and_set_bit(ST21NFCA_FACTORY_MODE, &hdev->quirks);
+ break;
+ case ST21NFCA_FACTORY_MODE_OFF:
+ clear_bit(ST21NFCA_FACTORY_MODE, &hdev->quirks);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int st21nfca_hci_clear_all_pipes(struct nfc_dev *dev, void *data,
+ size_t data_len)
+{
+ struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
+
+ return nfc_hci_disconnect_all_gates(hdev);
+}
+
+static int st21nfca_hci_dm_put_data(struct nfc_dev *dev, void *data,
+ size_t data_len)
+{
+ struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
+
+ return nfc_hci_send_cmd(hdev, ST21NFCA_DEVICE_MGNT_GATE,
+ ST21NFCA_HCI_DM_PUTDATA, data,
+ data_len, NULL);
+}
+
+static int st21nfca_hci_dm_update_aid(struct nfc_dev *dev, void *data,
+ size_t data_len)
+{
+ struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
+
+ return nfc_hci_send_cmd(hdev, ST21NFCA_DEVICE_MGNT_GATE,
+ ST21NFCA_HCI_DM_UPDATE_AID, data, data_len, NULL);
+}
+
+static int st21nfca_hci_dm_get_info(struct nfc_dev *dev, void *data,
+ size_t data_len)
+{
+ int r;
+ struct sk_buff *msg, *skb;
+ struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
+
+ r = nfc_hci_send_cmd(hdev,
+ ST21NFCA_DEVICE_MGNT_GATE,
+ ST21NFCA_HCI_DM_GETINFO,
+ data, data_len, &skb);
+ if (r)
+ goto exit;
+
+ msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST21NFCA_VENDOR_OUI,
+ HCI_DM_GET_INFO, skb->len);
+ if (!msg) {
+ r = -ENOMEM;
+ goto free_skb;
+ }
+
+ if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
+ kfree_skb(msg);
+ r = -ENOBUFS;
+ goto free_skb;
+ }
+
+ r = nfc_vendor_cmd_reply(msg);
+
+free_skb:
+ kfree_skb(skb);
+exit:
+ return r;
+}
+
+static int st21nfca_hci_dm_get_data(struct nfc_dev *dev, void *data,
+ size_t data_len)
+{
+ int r;
+ struct sk_buff *msg, *skb;
+ struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
+
+ r = nfc_hci_send_cmd(hdev,
+ ST21NFCA_DEVICE_MGNT_GATE,
+ ST21NFCA_HCI_DM_GETDATA,
+ data, data_len, &skb);
+ if (r)
+ goto exit;
+
+ msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST21NFCA_VENDOR_OUI,
+ HCI_DM_GET_DATA, skb->len);
+ if (!msg) {
+ r = -ENOMEM;
+ goto free_skb;
+ }
+
+ if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
+ kfree_skb(msg);
+ r = -ENOBUFS;
+ goto free_skb;
+ }
+
+ r = nfc_vendor_cmd_reply(msg);
+
+free_skb:
+ kfree_skb(skb);
+exit:
+ return r;
+}
+
+static int st21nfca_hci_dm_load(struct nfc_dev *dev, void *data,
+ size_t data_len)
+{
+ struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
+
+ return nfc_hci_send_cmd(hdev, ST21NFCA_DEVICE_MGNT_GATE,
+ ST21NFCA_HCI_DM_LOAD, data, data_len, NULL);
+}
+
+static int st21nfca_hci_dm_reset(struct nfc_dev *dev, void *data,
+ size_t data_len)
+{
+ int r;
+ struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
+
+ r = nfc_hci_send_cmd_async(hdev, ST21NFCA_DEVICE_MGNT_GATE,
+ ST21NFCA_HCI_DM_RESET, data, data_len, NULL, NULL);
+ if (r < 0)
+ return r;
+
+ r = nfc_llc_stop(hdev->llc);
+ if (r < 0)
+ return r;
+
+ return nfc_llc_start(hdev->llc);
+}
+
+static int st21nfca_hci_get_param(struct nfc_dev *dev, void *data,
+ size_t data_len)
+{
+ int r;
+ struct sk_buff *msg, *skb;
+ struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
+ struct get_param_data *param = (struct get_param_data *)data;
+
+ if (data_len < sizeof(struct get_param_data))
+ return -EPROTO;
+
+ r = nfc_hci_get_param(hdev, param->gate, param->data, &skb);
+ if (r)
+ goto exit;
+
+ msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST21NFCA_VENDOR_OUI,
+ HCI_GET_PARAM, skb->len);
+ if (!msg) {
+ r = -ENOMEM;
+ goto free_skb;
+ }
+
+ if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
+ kfree_skb(msg);
+ r = -ENOBUFS;
+ goto free_skb;
+ }
+
+ r = nfc_vendor_cmd_reply(msg);
+
+free_skb:
+ kfree_skb(skb);
+exit:
+ return r;
+}
+
+static int st21nfca_hci_dm_field_generator(struct nfc_dev *dev, void *data,
+ size_t data_len)
+{
+ struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
+
+ return nfc_hci_send_cmd(hdev,
+ ST21NFCA_DEVICE_MGNT_GATE,
+ ST21NFCA_HCI_DM_FIELD_GENERATOR,
+ data, data_len, NULL);
+}
+
+int st21nfca_hci_loopback_event_received(struct nfc_hci_dev *hdev, u8 event,
+ struct sk_buff *skb)
+{
+ struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+ switch (event) {
+ case ST21NFCA_EVT_POST_DATA:
+ info->vendor_info.rx_skb = skb;
+ break;
+ default:
+ nfc_err(&hdev->ndev->dev, "Unexpected event on loopback gate\n");
+ }
+ complete(&info->vendor_info.req_completion);
+ return 0;
+}
+EXPORT_SYMBOL(st21nfca_hci_loopback_event_received);
+
+static int st21nfca_hci_loopback(struct nfc_dev *dev, void *data,
+ size_t data_len)
+{
+ int r;
+ struct sk_buff *msg;
+ struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
+ struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+ if (data_len <= 0)
+ return -EPROTO;
+
+ reinit_completion(&info->vendor_info.req_completion);
+ info->vendor_info.rx_skb = NULL;
+
+ r = nfc_hci_send_event(hdev, NFC_HCI_LOOPBACK_GATE,
+ ST21NFCA_EVT_POST_DATA, data, data_len);
+ if (r < 0) {
+ r = -EPROTO;
+ goto exit;
+ }
+
+ wait_for_completion_interruptible(&info->vendor_info.req_completion);
+ if (!info->vendor_info.rx_skb ||
+ info->vendor_info.rx_skb->len != data_len) {
+ r = -EPROTO;
+ goto exit;
+ }
+
+ msg = nfc_vendor_cmd_alloc_reply_skb(hdev->ndev,
+ ST21NFCA_VENDOR_OUI,
+ HCI_LOOPBACK,
+ info->vendor_info.rx_skb->len);
+ if (!msg) {
+ r = -ENOMEM;
+ goto free_skb;
+ }
+
+ if (nla_put(msg, NFC_ATTR_VENDOR_DATA, info->vendor_info.rx_skb->len,
+ info->vendor_info.rx_skb->data)) {
+ kfree_skb(msg);
+ r = -ENOBUFS;
+ goto free_skb;
+ }
+
+ r = nfc_vendor_cmd_reply(msg);
+free_skb:
+ kfree_skb(info->vendor_info.rx_skb);
+exit:
+ return r;
+}
+
+static struct nfc_vendor_cmd st21nfca_vendor_cmds[] = {
+ {
+ .vendor_id = ST21NFCA_VENDOR_OUI,
+ .subcmd = FACTORY_MODE,
+ .doit = st21nfca_factory_mode,
+ },
+ {
+ .vendor_id = ST21NFCA_VENDOR_OUI,
+ .subcmd = HCI_CLEAR_ALL_PIPES,
+ .doit = st21nfca_hci_clear_all_pipes,
+ },
+ {
+ .vendor_id = ST21NFCA_VENDOR_OUI,
+ .subcmd = HCI_DM_PUT_DATA,
+ .doit = st21nfca_hci_dm_put_data,
+ },
+ {
+ .vendor_id = ST21NFCA_VENDOR_OUI,
+ .subcmd = HCI_DM_UPDATE_AID,
+ .doit = st21nfca_hci_dm_update_aid,
+ },
+ {
+ .vendor_id = ST21NFCA_VENDOR_OUI,
+ .subcmd = HCI_DM_GET_INFO,
+ .doit = st21nfca_hci_dm_get_info,
+ },
+ {
+ .vendor_id = ST21NFCA_VENDOR_OUI,
+ .subcmd = HCI_DM_GET_DATA,
+ .doit = st21nfca_hci_dm_get_data,
+ },
+ {
+ .vendor_id = ST21NFCA_VENDOR_OUI,
+ .subcmd = HCI_DM_LOAD,
+ .doit = st21nfca_hci_dm_load,
+ },
+ {
+ .vendor_id = ST21NFCA_VENDOR_OUI,
+ .subcmd = HCI_DM_RESET,
+ .doit = st21nfca_hci_dm_reset,
+ },
+ {
+ .vendor_id = ST21NFCA_VENDOR_OUI,
+ .subcmd = HCI_GET_PARAM,
+ .doit = st21nfca_hci_get_param,
+ },
+ {
+ .vendor_id = ST21NFCA_VENDOR_OUI,
+ .subcmd = HCI_DM_FIELD_GENERATOR,
+ .doit = st21nfca_hci_dm_field_generator,
+ },
+ {
+ .vendor_id = ST21NFCA_VENDOR_OUI,
+ .subcmd = HCI_LOOPBACK,
+ .doit = st21nfca_hci_loopback,
+ },
+};
+
+int st21nfca_vendor_cmds_init(struct nfc_hci_dev *hdev)
+{
+ struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+ init_completion(&info->vendor_info.req_completion);
+ return nfc_set_vendor_cmds(hdev->ndev, st21nfca_vendor_cmds,
+ sizeof(st21nfca_vendor_cmds));
+}
+EXPORT_SYMBOL(st21nfca_vendor_cmds_init);
diff --git a/drivers/nfc/st95hf/Kconfig b/drivers/nfc/st95hf/Kconfig
new file mode 100644
index 0000000..6bcede0
--- /dev/null
+++ b/drivers/nfc/st95hf/Kconfig
@@ -0,0 +1,12 @@
+config NFC_ST95HF
+ depends on !KERNEL_3_18
+ tristate "ST95HF NFC Transceiver driver"
+ depends on m
+ depends on SPI && NFC_DIGITAL
+ help
+ This enables the ST NFC driver for ST95HF NFC transceiver.
+ This makes use of SPI framework to communicate with transceiver
+ and registered with NFC digital core to support Linux NFC framework.
+
+ Say Y here to compile support for ST NFC transceiver ST95HF
+ linux driver into the kernel or say M to compile it as module.
diff --git a/drivers/nfc/st95hf/Makefile b/drivers/nfc/st95hf/Makefile
new file mode 100644
index 0000000..6ac2da9
--- /dev/null
+++ b/drivers/nfc/st95hf/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for STMicroelectronics NFC transceiver ST95HF
+#
+
+obj-$(CPTCFG_NFC_ST95HF) += st95hf.o
+st95hf-objs := spi.o core.o
diff --git a/drivers/nfc/st95hf/core.c b/drivers/nfc/st95hf/core.c
new file mode 100644
index 0000000..c2840e4
--- /dev/null
+++ b/drivers/nfc/st95hf/core.c
@@ -0,0 +1,1273 @@
+/*
+ * --------------------------------------------------------------------
+ * Driver for ST NFC Transceiver ST95HF
+ * --------------------------------------------------------------------
+ * Copyright (C) 2015 STMicroelectronics Pvt. Ltd. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/nfc.h>
+#include <linux/of_gpio.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+#include <linux/wait.h>
+#include <net/nfc/digital.h>
+#include <net/nfc/nfc.h>
+
+#include "spi.h"
+
+/* supported protocols */
+#define ST95HF_SUPPORTED_PROT (NFC_PROTO_ISO14443_MASK | \
+ NFC_PROTO_ISO14443_B_MASK | \
+ NFC_PROTO_ISO15693_MASK)
+/* driver capabilities */
+#define ST95HF_CAPABILITIES NFC_DIGITAL_DRV_CAPS_IN_CRC
+
+/* Command Send Interface */
+/* ST95HF_COMMAND_SEND CMD Ids */
+#define ECHO_CMD 0x55
+#define WRITE_REGISTER_CMD 0x9
+#define PROTOCOL_SELECT_CMD 0x2
+#define SEND_RECEIVE_CMD 0x4
+
+/* Select protocol codes */
+#define ISO15693_PROTOCOL_CODE 0x1
+#define ISO14443A_PROTOCOL_CODE 0x2
+#define ISO14443B_PROTOCOL_CODE 0x3
+
+/*
+ * head room len is 3
+ * 1 byte for control byte
+ * 1 byte for cmd
+ * 1 byte for size
+ */
+#define ST95HF_HEADROOM_LEN 3
+
+/*
+ * tailroom is 1 for ISO14443A
+ * and 0 for ISO14443B/ISO15693,
+ * hence the max value 1 should be
+ * taken.
+ */
+#define ST95HF_TAILROOM_LEN 1
+
+/* Command Response interface */
+#define MAX_RESPONSE_BUFFER_SIZE 280
+#define ECHORESPONSE 0x55
+#define ST95HF_ERR_MASK 0xF
+#define ST95HF_TIMEOUT_ERROR 0x87
+#define ST95HF_NFCA_CRC_ERR_MASK 0x20
+#define ST95HF_NFCB_CRC_ERR_MASK 0x01
+
+/* ST95HF transmission flag values */
+#define TRFLAG_NFCA_SHORT_FRAME 0x07
+#define TRFLAG_NFCA_STD_FRAME 0x08
+#define TRFLAG_NFCA_STD_FRAME_CRC 0x28
+
+/* Misc defs */
+#define HIGH 1
+#define LOW 0
+#define ISO14443A_RATS_REQ 0xE0
+#define RATS_TB1_PRESENT_MASK 0x20
+#define RATS_TA1_PRESENT_MASK 0x10
+#define TB1_FWI_MASK 0xF0
+#define WTX_REQ_FROM_TAG 0xF2
+
+#define MAX_CMD_LEN 0x7
+
+#define MAX_CMD_PARAMS 4
+struct cmd {
+ int cmd_len;
+ unsigned char cmd_id;
+ unsigned char no_cmd_params;
+ unsigned char cmd_params[MAX_CMD_PARAMS];
+ enum req_type req;
+};
+
+struct param_list {
+ int param_offset;
+ int new_param_val;
+};
+
+/*
+ * List of top-level cmds to be used internally by the driver.
+ * All these commands are build on top of ST95HF basic commands
+ * such as SEND_RECEIVE_CMD, PROTOCOL_SELECT_CMD, etc.
+ * These top level cmds are used internally while implementing various ops of
+ * digital layer/driver probe or extending the digital framework layer for
+ * features that are not yet implemented there, for example, WTX cmd handling.
+ */
+enum st95hf_cmd_list {
+ CMD_ECHO,
+ CMD_ISO14443A_CONFIG,
+ CMD_ISO14443A_DEMOGAIN,
+ CMD_ISO14443B_DEMOGAIN,
+ CMD_ISO14443A_PROTOCOL_SELECT,
+ CMD_ISO14443B_PROTOCOL_SELECT,
+ CMD_WTX_RESPONSE,
+ CMD_FIELD_OFF,
+ CMD_ISO15693_PROTOCOL_SELECT,
+};
+
+static const struct cmd cmd_array[] = {
+ [CMD_ECHO] = {
+ .cmd_len = 0x2,
+ .cmd_id = ECHO_CMD,
+ .no_cmd_params = 0,
+ .req = SYNC,
+ },
+ [CMD_ISO14443A_CONFIG] = {
+ .cmd_len = 0x7,
+ .cmd_id = WRITE_REGISTER_CMD,
+ .no_cmd_params = 0x4,
+ .cmd_params = {0x3A, 0x00, 0x5A, 0x04},
+ .req = SYNC,
+ },
+ [CMD_ISO14443A_DEMOGAIN] = {
+ .cmd_len = 0x7,
+ .cmd_id = WRITE_REGISTER_CMD,
+ .no_cmd_params = 0x4,
+ .cmd_params = {0x68, 0x01, 0x01, 0xDF},
+ .req = SYNC,
+ },
+ [CMD_ISO14443B_DEMOGAIN] = {
+ .cmd_len = 0x7,
+ .cmd_id = WRITE_REGISTER_CMD,
+ .no_cmd_params = 0x4,
+ .cmd_params = {0x68, 0x01, 0x01, 0x51},
+ .req = SYNC,
+ },
+ [CMD_ISO14443A_PROTOCOL_SELECT] = {
+ .cmd_len = 0x7,
+ .cmd_id = PROTOCOL_SELECT_CMD,
+ .no_cmd_params = 0x4,
+ .cmd_params = {ISO14443A_PROTOCOL_CODE, 0x00, 0x01, 0xA0},
+ .req = SYNC,
+ },
+ [CMD_ISO14443B_PROTOCOL_SELECT] = {
+ .cmd_len = 0x7,
+ .cmd_id = PROTOCOL_SELECT_CMD,
+ .no_cmd_params = 0x4,
+ .cmd_params = {ISO14443B_PROTOCOL_CODE, 0x01, 0x03, 0xFF},
+ .req = SYNC,
+ },
+ [CMD_WTX_RESPONSE] = {
+ .cmd_len = 0x6,
+ .cmd_id = SEND_RECEIVE_CMD,
+ .no_cmd_params = 0x3,
+ .cmd_params = {0xF2, 0x00, TRFLAG_NFCA_STD_FRAME_CRC},
+ .req = ASYNC,
+ },
+ [CMD_FIELD_OFF] = {
+ .cmd_len = 0x5,
+ .cmd_id = PROTOCOL_SELECT_CMD,
+ .no_cmd_params = 0x2,
+ .cmd_params = {0x0, 0x0},
+ .req = SYNC,
+ },
+ [CMD_ISO15693_PROTOCOL_SELECT] = {
+ .cmd_len = 0x5,
+ .cmd_id = PROTOCOL_SELECT_CMD,
+ .no_cmd_params = 0x2,
+ .cmd_params = {ISO15693_PROTOCOL_CODE, 0x0D},
+ .req = SYNC,
+ },
+};
+
+/* st95_digital_cmd_complete_arg stores client context */
+struct st95_digital_cmd_complete_arg {
+ struct sk_buff *skb_resp;
+ nfc_digital_cmd_complete_t complete_cb;
+ void *cb_usrarg;
+ bool rats;
+};
+
+/*
+ * structure containing ST95HF driver specific data.
+ * @spicontext: structure containing information required
+ * for spi communication between st95hf and host.
+ * @ddev: nfc digital device object.
+ * @nfcdev: nfc device object.
+ * @enable_gpio: gpio used to enable st95hf transceiver.
+ * @complete_cb_arg: structure to store various context information
+ * that is passed from nfc requesting thread to the threaded ISR.
+ * @st95hf_supply: regulator "consumer" for NFC device.
+ * @sendrcv_trflag: last byte of frame send by sendrecv command
+ * of st95hf. This byte contains transmission flag info.
+ * @exchange_lock: semaphore used for signaling the st95hf_remove
+ * function that the last outstanding async nfc request is finished.
+ * @rm_lock: mutex for ensuring safe access of nfc digital object
+ * from threaded ISR. Usage of this mutex avoids any race between
+ * deletion of the object from st95hf_remove() and its access from
+ * the threaded ISR.
+ * @nfcdev_free: flag to have the state of nfc device object.
+ * [alive | died]
+ * @current_protocol: current nfc protocol.
+ * @current_rf_tech: current rf technology.
+ * @fwi: frame waiting index, received in reply of RATS according to
+ * digital protocol.
+ */
+struct st95hf_context {
+ struct st95hf_spi_context spicontext;
+ struct nfc_digital_dev *ddev;
+ struct nfc_dev *nfcdev;
+ unsigned int enable_gpio;
+ struct st95_digital_cmd_complete_arg complete_cb_arg;
+ struct regulator *st95hf_supply;
+ unsigned char sendrcv_trflag;
+ struct semaphore exchange_lock;
+ struct mutex rm_lock;
+ bool nfcdev_free;
+ u8 current_protocol;
+ u8 current_rf_tech;
+ int fwi;
+};
+
+/*
+ * st95hf_send_recv_cmd() is for sending commands to ST95HF
+ * that are described in the cmd_array[]. It can optionally
+ * receive the response if the cmd request is of type
+ * SYNC. For that to happen caller must pass true to recv_res.
+ * For ASYNC request, recv_res is ignored and the
+ * function will never try to receive the response on behalf
+ * of the caller.
+ */
+static int st95hf_send_recv_cmd(struct st95hf_context *st95context,
+ enum st95hf_cmd_list cmd,
+ int no_modif,
+ struct param_list *list_array,
+ bool recv_res)
+{
+ unsigned char spi_cmd_buffer[MAX_CMD_LEN];
+ int i, ret;
+ struct device *dev = &st95context->spicontext.spidev->dev;
+
+ if (cmd_array[cmd].cmd_len > MAX_CMD_LEN)
+ return -EINVAL;
+ if (cmd_array[cmd].no_cmd_params < no_modif)
+ return -EINVAL;
+ if (no_modif && !list_array)
+ return -EINVAL;
+
+ spi_cmd_buffer[0] = ST95HF_COMMAND_SEND;
+ spi_cmd_buffer[1] = cmd_array[cmd].cmd_id;
+ spi_cmd_buffer[2] = cmd_array[cmd].no_cmd_params;
+
+ memcpy(&spi_cmd_buffer[3], cmd_array[cmd].cmd_params,
+ spi_cmd_buffer[2]);
+
+ for (i = 0; i < no_modif; i++) {
+ if (list_array[i].param_offset >= cmd_array[cmd].no_cmd_params)
+ return -EINVAL;
+ spi_cmd_buffer[3 + list_array[i].param_offset] =
+ list_array[i].new_param_val;
+ }
+
+ ret = st95hf_spi_send(&st95context->spicontext,
+ spi_cmd_buffer,
+ cmd_array[cmd].cmd_len,
+ cmd_array[cmd].req);
+ if (ret) {
+ dev_err(dev, "st95hf_spi_send failed with error %d\n", ret);
+ return ret;
+ }
+
+ if (cmd_array[cmd].req == SYNC && recv_res) {
+ unsigned char st95hf_response_arr[2];
+
+ ret = st95hf_spi_recv_response(&st95context->spicontext,
+ st95hf_response_arr);
+ if (ret < 0) {
+ dev_err(dev, "spi error from st95hf_spi_recv_response(), err = 0x%x\n",
+ ret);
+ return ret;
+ }
+
+ if (st95hf_response_arr[0]) {
+ dev_err(dev, "st95hf error from st95hf_spi_recv_response(), err = 0x%x\n",
+ st95hf_response_arr[0]);
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
+static int st95hf_echo_command(struct st95hf_context *st95context)
+{
+ int result = 0;
+ unsigned char echo_response;
+
+ result = st95hf_send_recv_cmd(st95context, CMD_ECHO, 0, NULL, false);
+ if (result)
+ return result;
+
+ /* If control reached here, response can be taken */
+ result = st95hf_spi_recv_echo_res(&st95context->spicontext,
+ &echo_response);
+ if (result) {
+ dev_err(&st95context->spicontext.spidev->dev,
+ "err: echo response receieve error = 0x%x\n", result);
+ return result;
+ }
+
+ if (echo_response == ECHORESPONSE)
+ return 0;
+
+ dev_err(&st95context->spicontext.spidev->dev, "err: echo res is 0x%x\n",
+ echo_response);
+
+ return -EIO;
+}
+
+static int secondary_configuration_type4a(struct st95hf_context *stcontext)
+{
+ int result = 0;
+ struct device *dev = &stcontext->nfcdev->dev;
+
+ /* 14443A config setting after select protocol */
+ result = st95hf_send_recv_cmd(stcontext,
+ CMD_ISO14443A_CONFIG,
+ 0,
+ NULL,
+ true);
+ if (result) {
+ dev_err(dev, "type a config cmd, err = 0x%x\n", result);
+ return result;
+ }
+
+ /* 14443A demo gain setting */
+ result = st95hf_send_recv_cmd(stcontext,
+ CMD_ISO14443A_DEMOGAIN,
+ 0,
+ NULL,
+ true);
+ if (result)
+ dev_err(dev, "type a demogain cmd, err = 0x%x\n", result);
+
+ return result;
+}
+
+static int secondary_configuration_type4b(struct st95hf_context *stcontext)
+{
+ int result = 0;
+ struct device *dev = &stcontext->nfcdev->dev;
+
+ result = st95hf_send_recv_cmd(stcontext,
+ CMD_ISO14443B_DEMOGAIN,
+ 0,
+ NULL,
+ true);
+ if (result)
+ dev_err(dev, "type b demogain cmd, err = 0x%x\n", result);
+
+ return result;
+}
+
+static int st95hf_select_protocol(struct st95hf_context *stcontext, int type)
+{
+ int result = 0;
+ struct device *dev;
+
+ dev = &stcontext->nfcdev->dev;
+
+ switch (type) {
+ case NFC_DIGITAL_RF_TECH_106A:
+ stcontext->current_rf_tech = NFC_DIGITAL_RF_TECH_106A;
+ result = st95hf_send_recv_cmd(stcontext,
+ CMD_ISO14443A_PROTOCOL_SELECT,
+ 0,
+ NULL,
+ true);
+ if (result) {
+ dev_err(dev, "protocol sel, err = 0x%x\n",
+ result);
+ return result;
+ }
+
+ /* secondary config. for 14443Type 4A after protocol select */
+ result = secondary_configuration_type4a(stcontext);
+ if (result) {
+ dev_err(dev, "type a secondary config, err = 0x%x\n",
+ result);
+ return result;
+ }
+ break;
+ case NFC_DIGITAL_RF_TECH_106B:
+ stcontext->current_rf_tech = NFC_DIGITAL_RF_TECH_106B;
+ result = st95hf_send_recv_cmd(stcontext,
+ CMD_ISO14443B_PROTOCOL_SELECT,
+ 0,
+ NULL,
+ true);
+ if (result) {
+ dev_err(dev, "protocol sel send, err = 0x%x\n",
+ result);
+ return result;
+ }
+
+ /*
+ * delay of 5-6 ms is required after select protocol
+ * command in case of ISO14443 Type B
+ */
+ usleep_range(50000, 60000);
+
+ /* secondary config. for 14443Type 4B after protocol select */
+ result = secondary_configuration_type4b(stcontext);
+ if (result) {
+ dev_err(dev, "type b secondary config, err = 0x%x\n",
+ result);
+ return result;
+ }
+ break;
+ case NFC_DIGITAL_RF_TECH_ISO15693:
+ stcontext->current_rf_tech = NFC_DIGITAL_RF_TECH_ISO15693;
+ result = st95hf_send_recv_cmd(stcontext,
+ CMD_ISO15693_PROTOCOL_SELECT,
+ 0,
+ NULL,
+ true);
+ if (result) {
+ dev_err(dev, "protocol sel send, err = 0x%x\n",
+ result);
+ return result;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void st95hf_send_st95enable_negativepulse(struct st95hf_context *st95con)
+{
+ /* First make irq_in pin high */
+ gpio_set_value(st95con->enable_gpio, HIGH);
+
+ /* wait for 1 milisecond */
+ usleep_range(1000, 2000);
+
+ /* Make irq_in pin low */
+ gpio_set_value(st95con->enable_gpio, LOW);
+
+ /* wait for minimum interrupt pulse to make st95 active */
+ usleep_range(1000, 2000);
+
+ /* At end make it high */
+ gpio_set_value(st95con->enable_gpio, HIGH);
+}
+
+/*
+ * Send a reset sequence over SPI bus (Reset command + wait 3ms +
+ * negative pulse on st95hf enable gpio
+ */
+static int st95hf_send_spi_reset_sequence(struct st95hf_context *st95context)
+{
+ int result = 0;
+ unsigned char reset_cmd = ST95HF_COMMAND_RESET;
+
+ result = st95hf_spi_send(&st95context->spicontext,
+ &reset_cmd,
+ ST95HF_RESET_CMD_LEN,
+ ASYNC);
+ if (result) {
+ dev_err(&st95context->spicontext.spidev->dev,
+ "spi reset sequence cmd error = %d", result);
+ return result;
+ }
+
+ /* wait for 3 milisecond to complete the controller reset process */
+ usleep_range(3000, 4000);
+
+ /* send negative pulse to make st95hf active */
+ st95hf_send_st95enable_negativepulse(st95context);
+
+ /* wait for 10 milisecond : HFO setup time */
+ usleep_range(10000, 20000);
+
+ return result;
+}
+
+static int st95hf_por_sequence(struct st95hf_context *st95context)
+{
+ int nth_attempt = 1;
+ int result;
+
+ st95hf_send_st95enable_negativepulse(st95context);
+
+ usleep_range(5000, 6000);
+ do {
+ /* send an ECHO command and checks ST95HF response */
+ result = st95hf_echo_command(st95context);
+
+ dev_dbg(&st95context->spicontext.spidev->dev,
+ "response from echo function = 0x%x, attempt = %d\n",
+ result, nth_attempt);
+
+ if (!result)
+ return 0;
+
+ /* send an pulse on IRQ in case of the chip is on sleep state */
+ if (nth_attempt == 2)
+ st95hf_send_st95enable_negativepulse(st95context);
+ else
+ st95hf_send_spi_reset_sequence(st95context);
+
+ /* delay of 50 milisecond */
+ usleep_range(50000, 51000);
+ } while (nth_attempt++ < 3);
+
+ return -ETIMEDOUT;
+}
+
+static int iso14443_config_fdt(struct st95hf_context *st95context, int wtxm)
+{
+ int result = 0;
+ struct device *dev = &st95context->spicontext.spidev->dev;
+ struct nfc_digital_dev *nfcddev = st95context->ddev;
+ unsigned char pp_typeb;
+ struct param_list new_params[2];
+
+ pp_typeb = cmd_array[CMD_ISO14443B_PROTOCOL_SELECT].cmd_params[2];
+
+ if (nfcddev->curr_protocol == NFC_PROTO_ISO14443 &&
+ st95context->fwi < 4)
+ st95context->fwi = 4;
+
+ new_params[0].param_offset = 2;
+ if (nfcddev->curr_protocol == NFC_PROTO_ISO14443)
+ new_params[0].new_param_val = st95context->fwi;
+ else if (nfcddev->curr_protocol == NFC_PROTO_ISO14443_B)
+ new_params[0].new_param_val = pp_typeb;
+
+ new_params[1].param_offset = 3;
+ new_params[1].new_param_val = wtxm;
+
+ switch (nfcddev->curr_protocol) {
+ case NFC_PROTO_ISO14443:
+ result = st95hf_send_recv_cmd(st95context,
+ CMD_ISO14443A_PROTOCOL_SELECT,
+ 2,
+ new_params,
+ true);
+ if (result) {
+ dev_err(dev, "WTX type a sel proto, err = 0x%x\n",
+ result);
+ return result;
+ }
+
+ /* secondary config. for 14443Type 4A after protocol select */
+ result = secondary_configuration_type4a(st95context);
+ if (result) {
+ dev_err(dev, "WTX type a second. config, err = 0x%x\n",
+ result);
+ return result;
+ }
+ break;
+ case NFC_PROTO_ISO14443_B:
+ result = st95hf_send_recv_cmd(st95context,
+ CMD_ISO14443B_PROTOCOL_SELECT,
+ 2,
+ new_params,
+ true);
+ if (result) {
+ dev_err(dev, "WTX type b sel proto, err = 0x%x\n",
+ result);
+ return result;
+ }
+
+ /* secondary config. for 14443Type 4B after protocol select */
+ result = secondary_configuration_type4b(st95context);
+ if (result) {
+ dev_err(dev, "WTX type b second. config, err = 0x%x\n",
+ result);
+ return result;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int st95hf_handle_wtx(struct st95hf_context *stcontext,
+ bool new_wtx,
+ int wtx_val)
+{
+ int result = 0;
+ unsigned char val_mm = 0;
+ struct param_list new_params[1];
+ struct nfc_digital_dev *nfcddev = stcontext->ddev;
+ struct device *dev = &stcontext->nfcdev->dev;
+
+ if (new_wtx) {
+ result = iso14443_config_fdt(stcontext, wtx_val & 0x3f);
+ if (result) {
+ dev_err(dev, "Config. setting error on WTX req, err = 0x%x\n",
+ result);
+ return result;
+ }
+
+ /* Send response of wtx with ASYNC as no response expected */
+ new_params[0].param_offset = 1;
+ new_params[0].new_param_val = wtx_val;
+
+ result = st95hf_send_recv_cmd(stcontext,
+ CMD_WTX_RESPONSE,
+ 1,
+ new_params,
+ false);
+ if (result)
+ dev_err(dev, "WTX response send, err = 0x%x\n", result);
+ return result;
+ }
+
+ /* if no new wtx, cofigure with default values */
+ if (nfcddev->curr_protocol == NFC_PROTO_ISO14443)
+ val_mm = cmd_array[CMD_ISO14443A_PROTOCOL_SELECT].cmd_params[3];
+ else if (nfcddev->curr_protocol == NFC_PROTO_ISO14443_B)
+ val_mm = cmd_array[CMD_ISO14443B_PROTOCOL_SELECT].cmd_params[3];
+
+ result = iso14443_config_fdt(stcontext, val_mm);
+ if (result)
+ dev_err(dev, "Default config. setting error after WTX processing, err = 0x%x\n",
+ result);
+
+ return result;
+}
+
+static int st95hf_error_handling(struct st95hf_context *stcontext,
+ struct sk_buff *skb_resp,
+ int res_len)
+{
+ int result = 0;
+ unsigned char error_byte;
+ struct device *dev = &stcontext->nfcdev->dev;
+
+ /* First check ST95HF specific error */
+ if (skb_resp->data[0] & ST95HF_ERR_MASK) {
+ if (skb_resp->data[0] == ST95HF_TIMEOUT_ERROR)
+ result = -ETIMEDOUT;
+ else
+ result = -EIO;
+ return result;
+ }
+
+ /* Check for CRC err only if CRC is present in the tag response */
+ switch (stcontext->current_rf_tech) {
+ case NFC_DIGITAL_RF_TECH_106A:
+ if (stcontext->sendrcv_trflag == TRFLAG_NFCA_STD_FRAME_CRC) {
+ error_byte = skb_resp->data[res_len - 3];
+ if (error_byte & ST95HF_NFCA_CRC_ERR_MASK) {
+ /* CRC error occurred */
+ dev_err(dev, "CRC error, byte received = 0x%x\n",
+ error_byte);
+ result = -EIO;
+ }
+ }
+ break;
+ case NFC_DIGITAL_RF_TECH_106B:
+ case NFC_DIGITAL_RF_TECH_ISO15693:
+ error_byte = skb_resp->data[res_len - 1];
+ if (error_byte & ST95HF_NFCB_CRC_ERR_MASK) {
+ /* CRC error occurred */
+ dev_err(dev, "CRC error, byte received = 0x%x\n",
+ error_byte);
+ result = -EIO;
+ }
+ break;
+ }
+
+ return result;
+}
+
+static int st95hf_response_handler(struct st95hf_context *stcontext,
+ struct sk_buff *skb_resp,
+ int res_len)
+{
+ int result = 0;
+ int skb_len;
+ unsigned char val_mm;
+ struct nfc_digital_dev *nfcddev = stcontext->ddev;
+ struct device *dev = &stcontext->nfcdev->dev;
+ struct st95_digital_cmd_complete_arg *cb_arg;
+
+ cb_arg = &stcontext->complete_cb_arg;
+
+ /* Process the response */
+ skb_put(skb_resp, res_len);
+
+ /* Remove st95 header */
+ skb_pull(skb_resp, 2);
+
+ skb_len = skb_resp->len;
+
+ /* check if it is case of RATS request reply & FWI is present */
+ if (nfcddev->curr_protocol == NFC_PROTO_ISO14443 && cb_arg->rats &&
+ (skb_resp->data[1] & RATS_TB1_PRESENT_MASK)) {
+ if (skb_resp->data[1] & RATS_TA1_PRESENT_MASK)
+ stcontext->fwi =
+ (skb_resp->data[3] & TB1_FWI_MASK) >> 4;
+ else
+ stcontext->fwi =
+ (skb_resp->data[2] & TB1_FWI_MASK) >> 4;
+
+ val_mm = cmd_array[CMD_ISO14443A_PROTOCOL_SELECT].cmd_params[3];
+
+ result = iso14443_config_fdt(stcontext, val_mm);
+ if (result) {
+ dev_err(dev, "error in config_fdt to handle fwi of ATS, error=%d\n",
+ result);
+ return result;
+ }
+ }
+ cb_arg->rats = false;
+
+ /* Remove CRC bytes only if received frames data has an eod (CRC) */
+ switch (stcontext->current_rf_tech) {
+ case NFC_DIGITAL_RF_TECH_106A:
+ if (stcontext->sendrcv_trflag == TRFLAG_NFCA_STD_FRAME_CRC)
+ skb_trim(skb_resp, (skb_len - 5));
+ else
+ skb_trim(skb_resp, (skb_len - 3));
+ break;
+ case NFC_DIGITAL_RF_TECH_106B:
+ case NFC_DIGITAL_RF_TECH_ISO15693:
+ skb_trim(skb_resp, (skb_len - 3));
+ break;
+ }
+
+ return result;
+}
+
+static irqreturn_t st95hf_irq_handler(int irq, void *st95hfcontext)
+{
+ struct st95hf_context *stcontext =
+ (struct st95hf_context *)st95hfcontext;
+
+ if (stcontext->spicontext.req_issync) {
+ complete(&stcontext->spicontext.done);
+ stcontext->spicontext.req_issync = false;
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t st95hf_irq_thread_handler(int irq, void *st95hfcontext)
+{
+ int result = 0;
+ int res_len;
+ static bool wtx;
+ struct device *dev;
+ struct device *spidevice;
+ struct nfc_digital_dev *nfcddev;
+ struct sk_buff *skb_resp;
+ struct st95hf_context *stcontext =
+ (struct st95hf_context *)st95hfcontext;
+ struct st95_digital_cmd_complete_arg *cb_arg;
+
+ spidevice = &stcontext->spicontext.spidev->dev;
+
+ /*
+ * check semaphore, if not down() already, then we don't
+ * know in which context the ISR is called and surely it
+ * will be a bug. Note that down() of the semaphore is done
+ * in the corresponding st95hf_in_send_cmd() and then
+ * only this ISR should be called. ISR will up() the
+ * semaphore before leaving. Hence when the ISR is called
+ * the correct behaviour is down_trylock() should always
+ * return 1 (indicating semaphore cant be taken and hence no
+ * change in semaphore count).
+ * If not, then we up() the semaphore and crash on
+ * a BUG() !
+ */
+ if (!down_trylock(&stcontext->exchange_lock)) {
+ up(&stcontext->exchange_lock);
+ WARN(1, "unknown context in ST95HF ISR");
+ return IRQ_NONE;
+ }
+
+ cb_arg = &stcontext->complete_cb_arg;
+ skb_resp = cb_arg->skb_resp;
+
+ mutex_lock(&stcontext->rm_lock);
+ res_len = st95hf_spi_recv_response(&stcontext->spicontext,
+ skb_resp->data);
+ if (res_len < 0) {
+ dev_err(spidevice, "TISR spi response err = 0x%x\n", res_len);
+ result = res_len;
+ goto end;
+ }
+
+ /* if stcontext->nfcdev_free is true, it means remove already ran */
+ if (stcontext->nfcdev_free) {
+ result = -ENODEV;
+ goto end;
+ }
+
+ dev = &stcontext->nfcdev->dev;
+ nfcddev = stcontext->ddev;
+ if (skb_resp->data[2] == WTX_REQ_FROM_TAG) {
+ /* Request for new FWT from tag */
+ result = st95hf_handle_wtx(stcontext, true, skb_resp->data[3]);
+ if (result)
+ goto end;
+
+ wtx = true;
+ mutex_unlock(&stcontext->rm_lock);
+ return IRQ_HANDLED;
+ }
+
+ result = st95hf_error_handling(stcontext, skb_resp, res_len);
+ if (result)
+ goto end;
+
+ result = st95hf_response_handler(stcontext, skb_resp, res_len);
+ if (result)
+ goto end;
+
+ /*
+ * If select protocol is done on wtx req. do select protocol
+ * again with default values
+ */
+ if (wtx) {
+ wtx = false;
+ result = st95hf_handle_wtx(stcontext, false, 0);
+ if (result)
+ goto end;
+ }
+
+ /* call digital layer callback */
+ cb_arg->complete_cb(stcontext->ddev, cb_arg->cb_usrarg, skb_resp);
+
+ /* up the semaphore before returning */
+ up(&stcontext->exchange_lock);
+ mutex_unlock(&stcontext->rm_lock);
+
+ return IRQ_HANDLED;
+
+end:
+ kfree_skb(skb_resp);
+ wtx = false;
+ cb_arg->rats = false;
+ skb_resp = ERR_PTR(result);
+ /* call of callback with error */
+ cb_arg->complete_cb(stcontext->ddev, cb_arg->cb_usrarg, skb_resp);
+ /* up the semaphore before returning */
+ up(&stcontext->exchange_lock);
+ mutex_unlock(&stcontext->rm_lock);
+ return IRQ_HANDLED;
+}
+
+/* NFC ops functions definition */
+static int st95hf_in_configure_hw(struct nfc_digital_dev *ddev,
+ int type,
+ int param)
+{
+ struct st95hf_context *stcontext = nfc_digital_get_drvdata(ddev);
+
+ if (type == NFC_DIGITAL_CONFIG_RF_TECH)
+ return st95hf_select_protocol(stcontext, param);
+
+ if (type == NFC_DIGITAL_CONFIG_FRAMING) {
+ switch (param) {
+ case NFC_DIGITAL_FRAMING_NFCA_SHORT:
+ stcontext->sendrcv_trflag = TRFLAG_NFCA_SHORT_FRAME;
+ break;
+ case NFC_DIGITAL_FRAMING_NFCA_STANDARD:
+ stcontext->sendrcv_trflag = TRFLAG_NFCA_STD_FRAME;
+ break;
+ case NFC_DIGITAL_FRAMING_NFCA_T4T:
+ case NFC_DIGITAL_FRAMING_NFCA_NFC_DEP:
+ case NFC_DIGITAL_FRAMING_NFCA_STANDARD_WITH_CRC_A:
+ stcontext->sendrcv_trflag = TRFLAG_NFCA_STD_FRAME_CRC;
+ break;
+ case NFC_DIGITAL_FRAMING_NFCB:
+ case NFC_DIGITAL_FRAMING_ISO15693_INVENTORY:
+ case NFC_DIGITAL_FRAMING_ISO15693_T5T:
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int rf_off(struct st95hf_context *stcontext)
+{
+ int rc;
+ struct device *dev;
+
+ dev = &stcontext->nfcdev->dev;
+
+ rc = st95hf_send_recv_cmd(stcontext, CMD_FIELD_OFF, 0, NULL, true);
+ if (rc)
+ dev_err(dev, "protocol sel send field off, err = 0x%x\n", rc);
+
+ return rc;
+}
+
+static int st95hf_in_send_cmd(struct nfc_digital_dev *ddev,
+ struct sk_buff *skb,
+ u16 timeout,
+ nfc_digital_cmd_complete_t cb,
+ void *arg)
+{
+ struct st95hf_context *stcontext = nfc_digital_get_drvdata(ddev);
+ int rc;
+ struct sk_buff *skb_resp;
+ int len_data_to_tag = 0;
+
+ skb_resp = nfc_alloc_recv_skb(MAX_RESPONSE_BUFFER_SIZE, GFP_KERNEL);
+ if (!skb_resp) {
+ rc = -ENOMEM;
+ goto error;
+ }
+
+ switch (stcontext->current_rf_tech) {
+ case NFC_DIGITAL_RF_TECH_106A:
+ len_data_to_tag = skb->len + 1;
+ *skb_put(skb, 1) = stcontext->sendrcv_trflag;
+ break;
+ case NFC_DIGITAL_RF_TECH_106B:
+ case NFC_DIGITAL_RF_TECH_ISO15693:
+ len_data_to_tag = skb->len;
+ break;
+ default:
+ rc = -EINVAL;
+ goto free_skb_resp;
+ }
+
+ skb_push(skb, 3);
+ skb->data[0] = ST95HF_COMMAND_SEND;
+ skb->data[1] = SEND_RECEIVE_CMD;
+ skb->data[2] = len_data_to_tag;
+
+ stcontext->complete_cb_arg.skb_resp = skb_resp;
+ stcontext->complete_cb_arg.cb_usrarg = arg;
+ stcontext->complete_cb_arg.complete_cb = cb;
+
+ if ((skb->data[3] == ISO14443A_RATS_REQ) &&
+ ddev->curr_protocol == NFC_PROTO_ISO14443)
+ stcontext->complete_cb_arg.rats = true;
+
+ /*
+ * down the semaphore to indicate to remove func that an
+ * ISR is pending, note that it will not block here in any case.
+ * If found blocked, it is a BUG!
+ */
+ rc = down_killable(&stcontext->exchange_lock);
+ if (rc) {
+ WARN(1, "Semaphore is not found up in st95hf_in_send_cmd\n");
+ return rc;
+ }
+
+ rc = st95hf_spi_send(&stcontext->spicontext, skb->data,
+ skb->len,
+ ASYNC);
+ if (rc) {
+ dev_err(&stcontext->nfcdev->dev,
+ "Error %d trying to perform data_exchange", rc);
+ /* up the semaphore since ISR will never come in this case */
+ up(&stcontext->exchange_lock);
+ goto free_skb_resp;
+ }
+
+ kfree_skb(skb);
+
+ return rc;
+
+free_skb_resp:
+ kfree_skb(skb_resp);
+error:
+ return rc;
+}
+
+/* p2p will be supported in a later release ! */
+static int st95hf_tg_configure_hw(struct nfc_digital_dev *ddev,
+ int type,
+ int param)
+{
+ return 0;
+}
+
+static int st95hf_tg_send_cmd(struct nfc_digital_dev *ddev,
+ struct sk_buff *skb,
+ u16 timeout,
+ nfc_digital_cmd_complete_t cb,
+ void *arg)
+{
+ return 0;
+}
+
+static int st95hf_tg_listen(struct nfc_digital_dev *ddev,
+ u16 timeout,
+ nfc_digital_cmd_complete_t cb,
+ void *arg)
+{
+ return 0;
+}
+
+static int st95hf_tg_get_rf_tech(struct nfc_digital_dev *ddev, u8 *rf_tech)
+{
+ return 0;
+}
+
+static int st95hf_switch_rf(struct nfc_digital_dev *ddev, bool on)
+{
+ u8 rf_tech;
+ struct st95hf_context *stcontext = nfc_digital_get_drvdata(ddev);
+
+ rf_tech = ddev->curr_rf_tech;
+
+ if (on)
+ /* switch on RF field */
+ return st95hf_select_protocol(stcontext, rf_tech);
+
+ /* switch OFF RF field */
+ return rf_off(stcontext);
+}
+
+/* TODO st95hf_abort_cmd */
+static void st95hf_abort_cmd(struct nfc_digital_dev *ddev)
+{
+}
+
+static struct nfc_digital_ops st95hf_nfc_digital_ops = {
+ .in_configure_hw = st95hf_in_configure_hw,
+ .in_send_cmd = st95hf_in_send_cmd,
+
+ .tg_listen = st95hf_tg_listen,
+ .tg_configure_hw = st95hf_tg_configure_hw,
+ .tg_send_cmd = st95hf_tg_send_cmd,
+ .tg_get_rf_tech = st95hf_tg_get_rf_tech,
+
+ .switch_rf = st95hf_switch_rf,
+ .abort_cmd = st95hf_abort_cmd,
+};
+
+static const struct spi_device_id st95hf_id[] = {
+ { "st95hf", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, st95hf_id);
+
+static int st95hf_probe(struct spi_device *nfc_spi_dev)
+{
+ int ret;
+
+ struct st95hf_context *st95context;
+ struct st95hf_spi_context *spicontext;
+
+ nfc_info(&nfc_spi_dev->dev, "ST95HF driver probe called.\n");
+
+ st95context = devm_kzalloc(&nfc_spi_dev->dev,
+ sizeof(struct st95hf_context),
+ GFP_KERNEL);
+ if (!st95context)
+ return -ENOMEM;
+
+ spicontext = &st95context->spicontext;
+
+ spicontext->spidev = nfc_spi_dev;
+
+ st95context->fwi =
+ cmd_array[CMD_ISO14443A_PROTOCOL_SELECT].cmd_params[2];
+
+ if (device_property_present(&nfc_spi_dev->dev, "st95hfvin")) {
+ st95context->st95hf_supply =
+ devm_regulator_get(&nfc_spi_dev->dev,
+ "st95hfvin");
+ if (IS_ERR(st95context->st95hf_supply)) {
+ dev_err(&nfc_spi_dev->dev, "failed to acquire regulator\n");
+ return PTR_ERR(st95context->st95hf_supply);
+ }
+
+ ret = regulator_enable(st95context->st95hf_supply);
+ if (ret) {
+ dev_err(&nfc_spi_dev->dev, "failed to enable regulator\n");
+ return ret;
+ }
+ }
+
+ init_completion(&spicontext->done);
+ mutex_init(&spicontext->spi_lock);
+
+ /*
+ * Store spicontext in spi device object for using it in
+ * remove function
+ */
+ dev_set_drvdata(&nfc_spi_dev->dev, spicontext);
+
+ st95context->enable_gpio =
+ of_get_named_gpio(nfc_spi_dev->dev.of_node,
+ "enable-gpio",
+ 0);
+ if (!gpio_is_valid(st95context->enable_gpio)) {
+ dev_err(&nfc_spi_dev->dev, "No valid enable gpio\n");
+ ret = st95context->enable_gpio;
+ goto err_disable_regulator;
+ }
+
+ ret = devm_gpio_request_one(&nfc_spi_dev->dev, st95context->enable_gpio,
+ GPIOF_DIR_OUT | GPIOF_INIT_HIGH,
+ "enable_gpio");
+ if (ret)
+ goto err_disable_regulator;
+
+ if (nfc_spi_dev->irq > 0) {
+ if (devm_request_threaded_irq(&nfc_spi_dev->dev,
+ nfc_spi_dev->irq,
+ st95hf_irq_handler,
+ st95hf_irq_thread_handler,
+ IRQF_TRIGGER_FALLING,
+ "st95hf",
+ (void *)st95context) < 0) {
+ dev_err(&nfc_spi_dev->dev, "err: irq request for st95hf is failed\n");
+ ret = -EINVAL;
+ goto err_disable_regulator;
+ }
+ } else {
+ dev_err(&nfc_spi_dev->dev, "not a valid IRQ associated with ST95HF\n");
+ ret = -EINVAL;
+ goto err_disable_regulator;
+ }
+
+ /*
+ * First reset SPI to handle warm reset of the system.
+ * It will put the ST95HF device in Power ON state
+ * which make the state of device identical to state
+ * at the time of cold reset of the system.
+ */
+ ret = st95hf_send_spi_reset_sequence(st95context);
+ if (ret) {
+ dev_err(&nfc_spi_dev->dev, "err: spi_reset_sequence failed\n");
+ goto err_disable_regulator;
+ }
+
+ /* call PowerOnReset sequence of ST95hf to activate it */
+ ret = st95hf_por_sequence(st95context);
+ if (ret) {
+ dev_err(&nfc_spi_dev->dev, "err: por seq failed for st95hf\n");
+ goto err_disable_regulator;
+ }
+
+ /* create NFC dev object and register with NFC Subsystem */
+ st95context->ddev = nfc_digital_allocate_device(&st95hf_nfc_digital_ops,
+ ST95HF_SUPPORTED_PROT,
+ ST95HF_CAPABILITIES,
+ ST95HF_HEADROOM_LEN,
+ ST95HF_TAILROOM_LEN);
+ if (!st95context->ddev) {
+ ret = -ENOMEM;
+ goto err_disable_regulator;
+ }
+
+ st95context->nfcdev = st95context->ddev->nfc_dev;
+ nfc_digital_set_parent_dev(st95context->ddev, &nfc_spi_dev->dev);
+
+ ret = nfc_digital_register_device(st95context->ddev);
+ if (ret) {
+ dev_err(&st95context->nfcdev->dev, "st95hf registration failed\n");
+ goto err_free_digital_device;
+ }
+
+ /* store st95context in nfc device object */
+ nfc_digital_set_drvdata(st95context->ddev, st95context);
+
+ sema_init(&st95context->exchange_lock, 1);
+ mutex_init(&st95context->rm_lock);
+
+ return ret;
+
+err_free_digital_device:
+ nfc_digital_free_device(st95context->ddev);
+err_disable_regulator:
+ if (st95context->st95hf_supply)
+ regulator_disable(st95context->st95hf_supply);
+
+ return ret;
+}
+
+static int st95hf_remove(struct spi_device *nfc_spi_dev)
+{
+ int result = 0;
+ unsigned char reset_cmd = ST95HF_COMMAND_RESET;
+ struct st95hf_spi_context *spictx = dev_get_drvdata(&nfc_spi_dev->dev);
+
+ struct st95hf_context *stcontext = container_of(spictx,
+ struct st95hf_context,
+ spicontext);
+
+ mutex_lock(&stcontext->rm_lock);
+
+ nfc_digital_unregister_device(stcontext->ddev);
+ nfc_digital_free_device(stcontext->ddev);
+ stcontext->nfcdev_free = true;
+
+ mutex_unlock(&stcontext->rm_lock);
+
+ /* if last in_send_cmd's ISR is pending, wait for it to finish */
+ result = down_killable(&stcontext->exchange_lock);
+ if (result == -EINTR)
+ dev_err(&spictx->spidev->dev, "sleep for semaphore interrupted by signal\n");
+
+ /* next reset the ST95HF controller */
+ result = st95hf_spi_send(&stcontext->spicontext,
+ &reset_cmd,
+ ST95HF_RESET_CMD_LEN,
+ ASYNC);
+ if (result) {
+ dev_err(&spictx->spidev->dev,
+ "ST95HF reset failed in remove() err = %d\n", result);
+ return result;
+ }
+
+ /* wait for 3 ms to complete the controller reset process */
+ usleep_range(3000, 4000);
+
+ /* disable regulator */
+ if (stcontext->st95hf_supply)
+ regulator_disable(stcontext->st95hf_supply);
+
+ return result;
+}
+
+/* Register as SPI protocol driver */
+static struct spi_driver st95hf_driver = {
+ .driver = {
+ .name = "st95hf",
+ .owner = THIS_MODULE,
+ },
+ .id_table = st95hf_id,
+ .probe = st95hf_probe,
+ .remove = st95hf_remove,
+};
+
+module_spi_driver(st95hf_driver);
+
+MODULE_AUTHOR("Shikha Singh <shikha.singh@st.com>");
+MODULE_DESCRIPTION("ST NFC Transceiver ST95HF driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/nfc/st95hf/spi.c b/drivers/nfc/st95hf/spi.c
new file mode 100644
index 0000000..e2d3bbc
--- /dev/null
+++ b/drivers/nfc/st95hf/spi.c
@@ -0,0 +1,167 @@
+/*
+ * ----------------------------------------------------------------------------
+ * drivers/nfc/st95hf/spi.c function definitions for SPI communication
+ * ----------------------------------------------------------------------------
+ * Copyright (C) 2015 STMicroelectronics Pvt. Ltd. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "spi.h"
+
+/* Function to send user provided buffer to ST95HF through SPI */
+int st95hf_spi_send(struct st95hf_spi_context *spicontext,
+ unsigned char *buffertx,
+ int datalen,
+ enum req_type reqtype)
+{
+ struct spi_message m;
+ int result = 0;
+ struct spi_device *spidev = spicontext->spidev;
+ struct spi_transfer tx_transfer = {
+ .tx_buf = buffertx,
+ .len = datalen,
+ };
+
+ mutex_lock(&spicontext->spi_lock);
+
+ if (reqtype == SYNC) {
+ spicontext->req_issync = true;
+ reinit_completion(&spicontext->done);
+ } else {
+ spicontext->req_issync = false;
+ }
+
+ spi_message_init(&m);
+ spi_message_add_tail(&tx_transfer, &m);
+
+ result = spi_sync(spidev, &m);
+ if (result) {
+ dev_err(&spidev->dev, "error: sending cmd to st95hf using SPI = %d\n",
+ result);
+ mutex_unlock(&spicontext->spi_lock);
+ return result;
+ }
+
+ /* return for asynchronous or no-wait case */
+ if (reqtype == ASYNC) {
+ mutex_unlock(&spicontext->spi_lock);
+ return 0;
+ }
+
+ result = wait_for_completion_timeout(&spicontext->done,
+ msecs_to_jiffies(1000));
+ /* check for timeout or success */
+ if (!result) {
+ dev_err(&spidev->dev, "error: response not ready timeout\n");
+ result = -ETIMEDOUT;
+ } else {
+ result = 0;
+ }
+
+ mutex_unlock(&spicontext->spi_lock);
+
+ return result;
+}
+EXPORT_SYMBOL_GPL(st95hf_spi_send);
+
+/* Function to Receive command Response */
+int st95hf_spi_recv_response(struct st95hf_spi_context *spicontext,
+ unsigned char *receivebuff)
+{
+ int len = 0;
+ struct spi_transfer tx_takedata;
+ struct spi_message m;
+ struct spi_device *spidev = spicontext->spidev;
+ unsigned char readdata_cmd = ST95HF_COMMAND_RECEIVE;
+ struct spi_transfer t[2] = {
+ {.tx_buf = &readdata_cmd, .len = 1,},
+ {.rx_buf = receivebuff, .len = 2, .cs_change = 1,},
+ };
+
+ int ret = 0;
+
+ memset(&tx_takedata, 0x0, sizeof(struct spi_transfer));
+
+ mutex_lock(&spicontext->spi_lock);
+
+ /* First spi transfer to know the length of valid data */
+ spi_message_init(&m);
+ spi_message_add_tail(&t[0], &m);
+ spi_message_add_tail(&t[1], &m);
+
+ ret = spi_sync(spidev, &m);
+ if (ret) {
+ dev_err(&spidev->dev, "spi_recv_resp, data length error = %d\n",
+ ret);
+ mutex_unlock(&spicontext->spi_lock);
+ return ret;
+ }
+
+ /* As 2 bytes are already read */
+ len = 2;
+
+ /* Support of long frame */
+ if (receivebuff[0] & 0x60)
+ len += (((receivebuff[0] & 0x60) >> 5) << 8) | receivebuff[1];
+ else
+ len += receivebuff[1];
+
+ /* Now make a transfer to read only relevant bytes */
+ tx_takedata.rx_buf = &receivebuff[2];
+ tx_takedata.len = len - 2;
+
+ spi_message_init(&m);
+ spi_message_add_tail(&tx_takedata, &m);
+
+ ret = spi_sync(spidev, &m);
+
+ mutex_unlock(&spicontext->spi_lock);
+ if (ret) {
+ dev_err(&spidev->dev, "spi_recv_resp, data read error = %d\n",
+ ret);
+ return ret;
+ }
+
+ return len;
+}
+EXPORT_SYMBOL_GPL(st95hf_spi_recv_response);
+
+int st95hf_spi_recv_echo_res(struct st95hf_spi_context *spicontext,
+ unsigned char *receivebuff)
+{
+ unsigned char readdata_cmd = ST95HF_COMMAND_RECEIVE;
+ struct spi_transfer t[2] = {
+ {.tx_buf = &readdata_cmd, .len = 1,},
+ {.rx_buf = receivebuff, .len = 1,},
+ };
+ struct spi_message m;
+ struct spi_device *spidev = spicontext->spidev;
+ int ret = 0;
+
+ mutex_lock(&spicontext->spi_lock);
+
+ spi_message_init(&m);
+ spi_message_add_tail(&t[0], &m);
+ spi_message_add_tail(&t[1], &m);
+ ret = spi_sync(spidev, &m);
+
+ mutex_unlock(&spicontext->spi_lock);
+
+ if (ret)
+ dev_err(&spidev->dev, "recv_echo_res, data read error = %d\n",
+ ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(st95hf_spi_recv_echo_res);
diff --git a/drivers/nfc/st95hf/spi.h b/drivers/nfc/st95hf/spi.h
new file mode 100644
index 0000000..552d220
--- /dev/null
+++ b/drivers/nfc/st95hf/spi.h
@@ -0,0 +1,64 @@
+/*
+ * ---------------------------------------------------------------------------
+ * drivers/nfc/st95hf/spi.h functions declarations for SPI communication
+ * ---------------------------------------------------------------------------
+ * Copyright (C) 2015 STMicroelectronics – All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LINUX_ST95HF_SPI_H
+#define __LINUX_ST95HF_SPI_H
+
+#include <linux/spi/spi.h>
+
+/* Basic ST95HF SPI CMDs */
+#define ST95HF_COMMAND_SEND 0x0
+#define ST95HF_COMMAND_RESET 0x1
+#define ST95HF_COMMAND_RECEIVE 0x2
+
+#define ST95HF_RESET_CMD_LEN 0x1
+
+/*
+ * structure to contain st95hf spi communication specific information.
+ * @req_issync: true for synchronous calls.
+ * @spidev: st95hf spi device object.
+ * @done: completion structure to wait for st95hf response
+ * for synchronous calls.
+ * @spi_lock: mutex to allow only one spi transfer at a time.
+ */
+struct st95hf_spi_context {
+ bool req_issync;
+ struct spi_device *spidev;
+ struct completion done;
+ struct mutex spi_lock;
+};
+
+/* flag to differentiate synchronous & asynchronous spi request */
+enum req_type {
+ SYNC,
+ ASYNC,
+};
+
+int st95hf_spi_send(struct st95hf_spi_context *spicontext,
+ unsigned char *buffertx,
+ int datalen,
+ enum req_type reqtype);
+
+int st95hf_spi_recv_response(struct st95hf_spi_context *spicontext,
+ unsigned char *receivebuff);
+
+int st95hf_spi_recv_echo_res(struct st95hf_spi_context *spicontext,
+ unsigned char *receivebuff);
+
+#endif
diff --git a/drivers/nfc/trf7970a.c b/drivers/nfc/trf7970a.c
new file mode 100644
index 0000000..26c9dbb
--- /dev/null
+++ b/drivers/nfc/trf7970a.c
@@ -0,0 +1,2245 @@
+/*
+ * TI TRF7970a RFID/NFC Transceiver Driver
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Author: Erick Macias <emacias@ti.com>
+ * Author: Felipe Balbi <balbi@ti.com>
+ * Author: Mark A. Greer <mgreer@animalcreek.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 of
+ * the License as published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/netdevice.h>
+#include <linux/interrupt.h>
+#include <linux/pm_runtime.h>
+#include <linux/nfc.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/regulator/consumer.h>
+
+#include <net/nfc/nfc.h>
+#include <net/nfc/digital.h>
+
+/* There are 3 ways the host can communicate with the trf7970a:
+ * parallel mode, SPI with Slave Select (SS) mode, and SPI without
+ * SS mode. The driver only supports the two SPI modes.
+ *
+ * The trf7970a is very timing sensitive and the VIN, EN2, and EN
+ * pins must asserted in that order and with specific delays in between.
+ * The delays used in the driver were provided by TI and have been
+ * confirmed to work with this driver. There is a bug with the current
+ * version of the trf7970a that requires that EN2 remain low no matter
+ * what. If it goes high, it will generate an RF field even when in
+ * passive target mode. TI has indicated that the chip will work okay
+ * when EN2 is left low. The 'en2-rf-quirk' device tree property
+ * indicates that trf7970a currently being used has the erratum and
+ * that EN2 must be kept low.
+ *
+ * Timeouts are implemented using the delayed workqueue kernel facility.
+ * Timeouts are required so things don't hang when there is no response
+ * from the trf7970a (or tag). Using this mechanism creates a race with
+ * interrupts, however. That is, an interrupt and a timeout could occur
+ * closely enough together that one is blocked by the mutex while the other
+ * executes. When the timeout handler executes first and blocks the
+ * interrupt handler, it will eventually set the state to IDLE so the
+ * interrupt handler will check the state and exit with no harm done.
+ * When the interrupt handler executes first and blocks the timeout handler,
+ * the cancel_delayed_work() call will know that it didn't cancel the
+ * work item (i.e., timeout) and will return zero. That return code is
+ * used by the timer handler to indicate that it should ignore the timeout
+ * once its unblocked.
+ *
+ * Aborting an active command isn't as simple as it seems because the only
+ * way to abort a command that's already been sent to the tag is so turn
+ * off power to the tag. If we do that, though, we'd have to go through
+ * the entire anticollision procedure again but the digital layer doesn't
+ * support that. So, if an abort is received before trf7970a_send_cmd()
+ * has sent the command to the tag, it simply returns -ECANCELED. If the
+ * command has already been sent to the tag, then the driver continues
+ * normally and recieves the response data (or error) but just before
+ * sending the data upstream, it frees the rx_skb and sends -ECANCELED
+ * upstream instead. If the command failed, that error will be sent
+ * upstream.
+ *
+ * When recieving data from a tag and the interrupt status register has
+ * only the SRX bit set, it means that all of the data has been received
+ * (once what's in the fifo has been read). However, depending on timing
+ * an interrupt status with only the SRX bit set may not be recived. In
+ * those cases, the timeout mechanism is used to wait 20 ms in case more
+ * data arrives. After 20 ms, it is assumed that all of the data has been
+ * received and the accumulated rx data is sent upstream. The
+ * 'TRF7970A_ST_WAIT_FOR_RX_DATA_CONT' state is used for this purpose
+ * (i.e., it indicates that some data has been received but we're not sure
+ * if there is more coming so a timeout in this state means all data has
+ * been received and there isn't an error). The delay is 20 ms since delays
+ * of ~16 ms have been observed during testing.
+ *
+ * When transmitting a frame larger than the FIFO size (127 bytes), the
+ * driver will wait 20 ms for the FIFO to drain past the low-watermark
+ * and generate an interrupt. The low-watermark set to 32 bytes so the
+ * interrupt should fire after 127 - 32 = 95 bytes have been sent. At
+ * the lowest possible bit rate (6.62 kbps for 15693), it will take up
+ * to ~14.35 ms so 20 ms is used for the timeout.
+ *
+ * Type 2 write and sector select commands respond with a 4-bit ACK or NACK.
+ * Having only 4 bits in the FIFO won't normally generate an interrupt so
+ * driver enables the '4_bit_RX' bit of the Special Functions register 1
+ * to cause an interrupt in that case. Leaving that bit for a read command
+ * messes up the data returned so it is only enabled when the framing is
+ * 'NFC_DIGITAL_FRAMING_NFCA_T2T' and the command is not a read command.
+ * Unfortunately, that means that the driver has to peek into tx frames
+ * when the framing is 'NFC_DIGITAL_FRAMING_NFCA_T2T'. This is done by
+ * the trf7970a_per_cmd_config() routine.
+ *
+ * ISO/IEC 15693 frames specify whether to use single or double sub-carrier
+ * frequencies and whether to use low or high data rates in the flags byte
+ * of the frame. This means that the driver has to peek at all 15693 frames
+ * to determine what speed to set the communication to. In addition, write
+ * and lock commands use the OPTION flag to indicate that an EOF must be
+ * sent to the tag before it will send its response. So the driver has to
+ * examine all frames for that reason too.
+ *
+ * It is unclear how long to wait before sending the EOF. According to the
+ * Note under Table 1-1 in section 1.6 of
+ * http://www.ti.com/lit/ug/scbu011/scbu011.pdf, that wait should be at least
+ * 10 ms for TI Tag-it HF-I tags; however testing has shown that is not long
+ * enough so 20 ms is used. So the timer is set to 40 ms - 20 ms to drain
+ * up to 127 bytes in the FIFO at the lowest bit rate plus another 20 ms to
+ * ensure the wait is long enough before sending the EOF. This seems to work
+ * reliably.
+ */
+
+#define TRF7970A_SUPPORTED_PROTOCOLS \
+ (NFC_PROTO_MIFARE_MASK | NFC_PROTO_ISO14443_MASK | \
+ NFC_PROTO_ISO14443_B_MASK | NFC_PROTO_FELICA_MASK | \
+ NFC_PROTO_ISO15693_MASK | NFC_PROTO_NFC_DEP_MASK)
+
+#define TRF7970A_AUTOSUSPEND_DELAY 30000 /* 30 seconds */
+
+#define TRF7970A_RX_SKB_ALLOC_SIZE 256
+
+#define TRF7970A_FIFO_SIZE 127
+
+/* TX length is 3 nibbles long ==> 4KB - 1 bytes max */
+#define TRF7970A_TX_MAX (4096 - 1)
+
+#define TRF7970A_WAIT_FOR_TX_IRQ 20
+#define TRF7970A_WAIT_FOR_RX_DATA_TIMEOUT 20
+#define TRF7970A_WAIT_FOR_FIFO_DRAIN_TIMEOUT 20
+#define TRF7970A_WAIT_TO_ISSUE_ISO15693_EOF 40
+
+/* Guard times for various RF technologies (in us) */
+#define TRF7970A_GUARD_TIME_NFCA 5000
+#define TRF7970A_GUARD_TIME_NFCB 5000
+#define TRF7970A_GUARD_TIME_NFCF 20000
+#define TRF7970A_GUARD_TIME_15693 1000
+
+/* Quirks */
+/* Erratum: When reading IRQ Status register on trf7970a, we must issue a
+ * read continuous command for IRQ Status and Collision Position registers.
+ */
+#define TRF7970A_QUIRK_IRQ_STATUS_READ BIT(0)
+#define TRF7970A_QUIRK_EN2_MUST_STAY_LOW BIT(1)
+#define TRF7970A_QUIRK_T5T_RMB_EXTRA_BYTE BIT(2)
+
+/* Direct commands */
+#define TRF7970A_CMD_IDLE 0x00
+#define TRF7970A_CMD_SOFT_INIT 0x03
+#define TRF7970A_CMD_RF_COLLISION 0x04
+#define TRF7970A_CMD_RF_COLLISION_RESPONSE_N 0x05
+#define TRF7970A_CMD_RF_COLLISION_RESPONSE_0 0x06
+#define TRF7970A_CMD_FIFO_RESET 0x0f
+#define TRF7970A_CMD_TRANSMIT_NO_CRC 0x10
+#define TRF7970A_CMD_TRANSMIT 0x11
+#define TRF7970A_CMD_DELAY_TRANSMIT_NO_CRC 0x12
+#define TRF7970A_CMD_DELAY_TRANSMIT 0x13
+#define TRF7970A_CMD_EOF 0x14
+#define TRF7970A_CMD_CLOSE_SLOT 0x15
+#define TRF7970A_CMD_BLOCK_RX 0x16
+#define TRF7970A_CMD_ENABLE_RX 0x17
+#define TRF7970A_CMD_TEST_INT_RF 0x18
+#define TRF7970A_CMD_TEST_EXT_RF 0x19
+#define TRF7970A_CMD_RX_GAIN_ADJUST 0x1a
+
+/* Bits determining whether its a direct command or register R/W,
+ * whether to use a continuous SPI transaction or not, and the actual
+ * direct cmd opcode or regster address.
+ */
+#define TRF7970A_CMD_BIT_CTRL BIT(7)
+#define TRF7970A_CMD_BIT_RW BIT(6)
+#define TRF7970A_CMD_BIT_CONTINUOUS BIT(5)
+#define TRF7970A_CMD_BIT_OPCODE(opcode) ((opcode) & 0x1f)
+
+/* Registers addresses */
+#define TRF7970A_CHIP_STATUS_CTRL 0x00
+#define TRF7970A_ISO_CTRL 0x01
+#define TRF7970A_ISO14443B_TX_OPTIONS 0x02
+#define TRF7970A_ISO14443A_HIGH_BITRATE_OPTIONS 0x03
+#define TRF7970A_TX_TIMER_SETTING_H_BYTE 0x04
+#define TRF7970A_TX_TIMER_SETTING_L_BYTE 0x05
+#define TRF7970A_TX_PULSE_LENGTH_CTRL 0x06
+#define TRF7970A_RX_NO_RESPONSE_WAIT 0x07
+#define TRF7970A_RX_WAIT_TIME 0x08
+#define TRF7970A_MODULATOR_SYS_CLK_CTRL 0x09
+#define TRF7970A_RX_SPECIAL_SETTINGS 0x0a
+#define TRF7970A_REG_IO_CTRL 0x0b
+#define TRF7970A_IRQ_STATUS 0x0c
+#define TRF7970A_COLLISION_IRQ_MASK 0x0d
+#define TRF7970A_COLLISION_POSITION 0x0e
+#define TRF7970A_RSSI_OSC_STATUS 0x0f
+#define TRF7970A_SPECIAL_FCN_REG1 0x10
+#define TRF7970A_SPECIAL_FCN_REG2 0x11
+#define TRF7970A_RAM1 0x12
+#define TRF7970A_RAM2 0x13
+#define TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS 0x14
+#define TRF7970A_NFC_LOW_FIELD_LEVEL 0x16
+#define TRF7970A_NFCID1 0x17
+#define TRF7970A_NFC_TARGET_LEVEL 0x18
+#define TRF79070A_NFC_TARGET_PROTOCOL 0x19
+#define TRF7970A_TEST_REGISTER1 0x1a
+#define TRF7970A_TEST_REGISTER2 0x1b
+#define TRF7970A_FIFO_STATUS 0x1c
+#define TRF7970A_TX_LENGTH_BYTE1 0x1d
+#define TRF7970A_TX_LENGTH_BYTE2 0x1e
+#define TRF7970A_FIFO_IO_REGISTER 0x1f
+
+/* Chip Status Control Register Bits */
+#define TRF7970A_CHIP_STATUS_VRS5_3 BIT(0)
+#define TRF7970A_CHIP_STATUS_REC_ON BIT(1)
+#define TRF7970A_CHIP_STATUS_AGC_ON BIT(2)
+#define TRF7970A_CHIP_STATUS_PM_ON BIT(3)
+#define TRF7970A_CHIP_STATUS_RF_PWR BIT(4)
+#define TRF7970A_CHIP_STATUS_RF_ON BIT(5)
+#define TRF7970A_CHIP_STATUS_DIRECT BIT(6)
+#define TRF7970A_CHIP_STATUS_STBY BIT(7)
+
+/* ISO Control Register Bits */
+#define TRF7970A_ISO_CTRL_15693_SGL_1OF4_662 0x00
+#define TRF7970A_ISO_CTRL_15693_SGL_1OF256_662 0x01
+#define TRF7970A_ISO_CTRL_15693_SGL_1OF4_2648 0x02
+#define TRF7970A_ISO_CTRL_15693_SGL_1OF256_2648 0x03
+#define TRF7970A_ISO_CTRL_15693_DBL_1OF4_667a 0x04
+#define TRF7970A_ISO_CTRL_15693_DBL_1OF256_667 0x05
+#define TRF7970A_ISO_CTRL_15693_DBL_1OF4_2669 0x06
+#define TRF7970A_ISO_CTRL_15693_DBL_1OF256_2669 0x07
+#define TRF7970A_ISO_CTRL_14443A_106 0x08
+#define TRF7970A_ISO_CTRL_14443A_212 0x09
+#define TRF7970A_ISO_CTRL_14443A_424 0x0a
+#define TRF7970A_ISO_CTRL_14443A_848 0x0b
+#define TRF7970A_ISO_CTRL_14443B_106 0x0c
+#define TRF7970A_ISO_CTRL_14443B_212 0x0d
+#define TRF7970A_ISO_CTRL_14443B_424 0x0e
+#define TRF7970A_ISO_CTRL_14443B_848 0x0f
+#define TRF7970A_ISO_CTRL_FELICA_212 0x1a
+#define TRF7970A_ISO_CTRL_FELICA_424 0x1b
+#define TRF7970A_ISO_CTRL_NFC_NFCA_106 0x01
+#define TRF7970A_ISO_CTRL_NFC_NFCF_212 0x02
+#define TRF7970A_ISO_CTRL_NFC_NFCF_424 0x03
+#define TRF7970A_ISO_CTRL_NFC_CE_14443A 0x00
+#define TRF7970A_ISO_CTRL_NFC_CE_14443B 0x01
+#define TRF7970A_ISO_CTRL_NFC_CE BIT(2)
+#define TRF7970A_ISO_CTRL_NFC_ACTIVE BIT(3)
+#define TRF7970A_ISO_CTRL_NFC_INITIATOR BIT(4)
+#define TRF7970A_ISO_CTRL_NFC_NFC_CE_MODE BIT(5)
+#define TRF7970A_ISO_CTRL_RFID BIT(5)
+#define TRF7970A_ISO_CTRL_DIR_MODE BIT(6)
+#define TRF7970A_ISO_CTRL_RX_CRC_N BIT(7) /* true == No CRC */
+
+#define TRF7970A_ISO_CTRL_RFID_SPEED_MASK 0x1f
+
+/* Modulator and SYS_CLK Control Register Bits */
+#define TRF7970A_MODULATOR_DEPTH(n) ((n) & 0x7)
+#define TRF7970A_MODULATOR_DEPTH_ASK10 (TRF7970A_MODULATOR_DEPTH(0))
+#define TRF7970A_MODULATOR_DEPTH_OOK (TRF7970A_MODULATOR_DEPTH(1))
+#define TRF7970A_MODULATOR_DEPTH_ASK7 (TRF7970A_MODULATOR_DEPTH(2))
+#define TRF7970A_MODULATOR_DEPTH_ASK8_5 (TRF7970A_MODULATOR_DEPTH(3))
+#define TRF7970A_MODULATOR_DEPTH_ASK13 (TRF7970A_MODULATOR_DEPTH(4))
+#define TRF7970A_MODULATOR_DEPTH_ASK16 (TRF7970A_MODULATOR_DEPTH(5))
+#define TRF7970A_MODULATOR_DEPTH_ASK22 (TRF7970A_MODULATOR_DEPTH(6))
+#define TRF7970A_MODULATOR_DEPTH_ASK30 (TRF7970A_MODULATOR_DEPTH(7))
+#define TRF7970A_MODULATOR_EN_ANA BIT(3)
+#define TRF7970A_MODULATOR_CLK(n) (((n) & 0x3) << 4)
+#define TRF7970A_MODULATOR_CLK_DISABLED (TRF7970A_MODULATOR_CLK(0))
+#define TRF7970A_MODULATOR_CLK_3_6 (TRF7970A_MODULATOR_CLK(1))
+#define TRF7970A_MODULATOR_CLK_6_13 (TRF7970A_MODULATOR_CLK(2))
+#define TRF7970A_MODULATOR_CLK_13_27 (TRF7970A_MODULATOR_CLK(3))
+#define TRF7970A_MODULATOR_EN_OOK BIT(6)
+#define TRF7970A_MODULATOR_27MHZ BIT(7)
+
+#define TRF7970A_RX_SPECIAL_SETTINGS_NO_LIM BIT(0)
+#define TRF7970A_RX_SPECIAL_SETTINGS_AGCR BIT(1)
+#define TRF7970A_RX_SPECIAL_SETTINGS_GD_0DB (0x0 << 2)
+#define TRF7970A_RX_SPECIAL_SETTINGS_GD_5DB (0x1 << 2)
+#define TRF7970A_RX_SPECIAL_SETTINGS_GD_10DB (0x2 << 2)
+#define TRF7970A_RX_SPECIAL_SETTINGS_GD_15DB (0x3 << 2)
+#define TRF7970A_RX_SPECIAL_SETTINGS_HBT BIT(4)
+#define TRF7970A_RX_SPECIAL_SETTINGS_M848 BIT(5)
+#define TRF7970A_RX_SPECIAL_SETTINGS_C424 BIT(6)
+#define TRF7970A_RX_SPECIAL_SETTINGS_C212 BIT(7)
+
+#define TRF7970A_REG_IO_CTRL_VRS(v) ((v) & 0x07)
+#define TRF7970A_REG_IO_CTRL_IO_LOW BIT(5)
+#define TRF7970A_REG_IO_CTRL_EN_EXT_PA BIT(6)
+#define TRF7970A_REG_IO_CTRL_AUTO_REG BIT(7)
+
+/* IRQ Status Register Bits */
+#define TRF7970A_IRQ_STATUS_NORESP BIT(0) /* ISO15693 only */
+#define TRF7970A_IRQ_STATUS_NFC_COL_ERROR BIT(0)
+#define TRF7970A_IRQ_STATUS_COL BIT(1)
+#define TRF7970A_IRQ_STATUS_FRAMING_EOF_ERROR BIT(2)
+#define TRF7970A_IRQ_STATUS_NFC_RF BIT(2)
+#define TRF7970A_IRQ_STATUS_PARITY_ERROR BIT(3)
+#define TRF7970A_IRQ_STATUS_NFC_SDD BIT(3)
+#define TRF7970A_IRQ_STATUS_CRC_ERROR BIT(4)
+#define TRF7970A_IRQ_STATUS_NFC_PROTO_ERROR BIT(4)
+#define TRF7970A_IRQ_STATUS_FIFO BIT(5)
+#define TRF7970A_IRQ_STATUS_SRX BIT(6)
+#define TRF7970A_IRQ_STATUS_TX BIT(7)
+
+#define TRF7970A_IRQ_STATUS_ERROR \
+ (TRF7970A_IRQ_STATUS_COL | \
+ TRF7970A_IRQ_STATUS_FRAMING_EOF_ERROR | \
+ TRF7970A_IRQ_STATUS_PARITY_ERROR | \
+ TRF7970A_IRQ_STATUS_CRC_ERROR)
+
+#define TRF7970A_RSSI_OSC_STATUS_RSSI_MASK (BIT(2) | BIT(1) | BIT(0))
+#define TRF7970A_RSSI_OSC_STATUS_RSSI_X_MASK (BIT(5) | BIT(4) | BIT(3))
+#define TRF7970A_RSSI_OSC_STATUS_RSSI_OSC_OK BIT(6)
+
+#define TRF7970A_SPECIAL_FCN_REG1_COL_7_6 BIT(0)
+#define TRF7970A_SPECIAL_FCN_REG1_14_ANTICOLL BIT(1)
+#define TRF7970A_SPECIAL_FCN_REG1_4_BIT_RX BIT(2)
+#define TRF7970A_SPECIAL_FCN_REG1_SP_DIR_MODE BIT(3)
+#define TRF7970A_SPECIAL_FCN_REG1_NEXT_SLOT_37US BIT(4)
+#define TRF7970A_SPECIAL_FCN_REG1_PAR43 BIT(5)
+
+#define TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS_WLH_124 (0x0 << 2)
+#define TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS_WLH_120 (0x1 << 2)
+#define TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS_WLH_112 (0x2 << 2)
+#define TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS_WLH_96 (0x3 << 2)
+#define TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS_WLL_4 0x0
+#define TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS_WLL_8 0x1
+#define TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS_WLL_16 0x2
+#define TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS_WLL_32 0x3
+
+#define TRF7970A_NFC_LOW_FIELD_LEVEL_RFDET(v) ((v) & 0x07)
+#define TRF7970A_NFC_LOW_FIELD_LEVEL_CLEX_DIS BIT(7)
+
+#define TRF7970A_NFC_TARGET_LEVEL_RFDET(v) ((v) & 0x07)
+#define TRF7970A_NFC_TARGET_LEVEL_HI_RF BIT(3)
+#define TRF7970A_NFC_TARGET_LEVEL_SDD_EN BIT(5)
+#define TRF7970A_NFC_TARGET_LEVEL_LD_S_4BYTES (0x0 << 6)
+#define TRF7970A_NFC_TARGET_LEVEL_LD_S_7BYTES (0x1 << 6)
+#define TRF7970A_NFC_TARGET_LEVEL_LD_S_10BYTES (0x2 << 6)
+
+#define TRF79070A_NFC_TARGET_PROTOCOL_NFCBR_106 BIT(0)
+#define TRF79070A_NFC_TARGET_PROTOCOL_NFCBR_212 BIT(1)
+#define TRF79070A_NFC_TARGET_PROTOCOL_NFCBR_424 (BIT(0) | BIT(1))
+#define TRF79070A_NFC_TARGET_PROTOCOL_PAS_14443B BIT(2)
+#define TRF79070A_NFC_TARGET_PROTOCOL_PAS_106 BIT(3)
+#define TRF79070A_NFC_TARGET_PROTOCOL_FELICA BIT(4)
+#define TRF79070A_NFC_TARGET_PROTOCOL_RF_L BIT(6)
+#define TRF79070A_NFC_TARGET_PROTOCOL_RF_H BIT(7)
+
+#define TRF79070A_NFC_TARGET_PROTOCOL_106A \
+ (TRF79070A_NFC_TARGET_PROTOCOL_RF_H | \
+ TRF79070A_NFC_TARGET_PROTOCOL_RF_L | \
+ TRF79070A_NFC_TARGET_PROTOCOL_PAS_106 | \
+ TRF79070A_NFC_TARGET_PROTOCOL_NFCBR_106)
+
+#define TRF79070A_NFC_TARGET_PROTOCOL_106B \
+ (TRF79070A_NFC_TARGET_PROTOCOL_RF_H | \
+ TRF79070A_NFC_TARGET_PROTOCOL_RF_L | \
+ TRF79070A_NFC_TARGET_PROTOCOL_PAS_14443B | \
+ TRF79070A_NFC_TARGET_PROTOCOL_NFCBR_106)
+
+#define TRF79070A_NFC_TARGET_PROTOCOL_212F \
+ (TRF79070A_NFC_TARGET_PROTOCOL_RF_H | \
+ TRF79070A_NFC_TARGET_PROTOCOL_RF_L | \
+ TRF79070A_NFC_TARGET_PROTOCOL_FELICA | \
+ TRF79070A_NFC_TARGET_PROTOCOL_NFCBR_212)
+
+#define TRF79070A_NFC_TARGET_PROTOCOL_424F \
+ (TRF79070A_NFC_TARGET_PROTOCOL_RF_H | \
+ TRF79070A_NFC_TARGET_PROTOCOL_RF_L | \
+ TRF79070A_NFC_TARGET_PROTOCOL_FELICA | \
+ TRF79070A_NFC_TARGET_PROTOCOL_NFCBR_424)
+
+#define TRF7970A_FIFO_STATUS_OVERFLOW BIT(7)
+
+/* NFC (ISO/IEC 14443A) Type 2 Tag commands */
+#define NFC_T2T_CMD_READ 0x30
+
+/* ISO 15693 commands codes */
+#define ISO15693_CMD_INVENTORY 0x01
+#define ISO15693_CMD_READ_SINGLE_BLOCK 0x20
+#define ISO15693_CMD_WRITE_SINGLE_BLOCK 0x21
+#define ISO15693_CMD_LOCK_BLOCK 0x22
+#define ISO15693_CMD_READ_MULTIPLE_BLOCK 0x23
+#define ISO15693_CMD_WRITE_MULTIPLE_BLOCK 0x24
+#define ISO15693_CMD_SELECT 0x25
+#define ISO15693_CMD_RESET_TO_READY 0x26
+#define ISO15693_CMD_WRITE_AFI 0x27
+#define ISO15693_CMD_LOCK_AFI 0x28
+#define ISO15693_CMD_WRITE_DSFID 0x29
+#define ISO15693_CMD_LOCK_DSFID 0x2a
+#define ISO15693_CMD_GET_SYSTEM_INFO 0x2b
+#define ISO15693_CMD_GET_MULTIPLE_BLOCK_SECURITY_STATUS 0x2c
+
+/* ISO 15693 request and response flags */
+#define ISO15693_REQ_FLAG_SUB_CARRIER BIT(0)
+#define ISO15693_REQ_FLAG_DATA_RATE BIT(1)
+#define ISO15693_REQ_FLAG_INVENTORY BIT(2)
+#define ISO15693_REQ_FLAG_PROTOCOL_EXT BIT(3)
+#define ISO15693_REQ_FLAG_SELECT BIT(4)
+#define ISO15693_REQ_FLAG_AFI BIT(4)
+#define ISO15693_REQ_FLAG_ADDRESS BIT(5)
+#define ISO15693_REQ_FLAG_NB_SLOTS BIT(5)
+#define ISO15693_REQ_FLAG_OPTION BIT(6)
+
+#define ISO15693_REQ_FLAG_SPEED_MASK \
+ (ISO15693_REQ_FLAG_SUB_CARRIER | ISO15693_REQ_FLAG_DATA_RATE)
+
+enum trf7970a_state {
+ TRF7970A_ST_PWR_OFF,
+ TRF7970A_ST_RF_OFF,
+ TRF7970A_ST_IDLE,
+ TRF7970A_ST_IDLE_RX_BLOCKED,
+ TRF7970A_ST_WAIT_FOR_TX_FIFO,
+ TRF7970A_ST_WAIT_FOR_RX_DATA,
+ TRF7970A_ST_WAIT_FOR_RX_DATA_CONT,
+ TRF7970A_ST_WAIT_TO_ISSUE_EOF,
+ TRF7970A_ST_LISTENING,
+ TRF7970A_ST_LISTENING_MD,
+ TRF7970A_ST_MAX
+};
+
+struct trf7970a {
+ enum trf7970a_state state;
+ struct device *dev;
+ struct spi_device *spi;
+ struct regulator *regulator;
+ struct nfc_digital_dev *ddev;
+ u32 quirks;
+ bool is_initiator;
+ bool aborting;
+ struct sk_buff *tx_skb;
+ struct sk_buff *rx_skb;
+ nfc_digital_cmd_complete_t cb;
+ void *cb_arg;
+ u8 chip_status_ctrl;
+ u8 iso_ctrl;
+ u8 iso_ctrl_tech;
+ u8 modulator_sys_clk_ctrl;
+ u8 special_fcn_reg1;
+ unsigned int guard_time;
+ int technology;
+ int framing;
+ u8 md_rf_tech;
+ u8 tx_cmd;
+ bool issue_eof;
+ bool adjust_resp_len;
+ int en2_gpio;
+ int en_gpio;
+ struct mutex lock;
+ unsigned int timeout;
+ bool ignore_timeout;
+ struct delayed_work timeout_work;
+};
+
+
+static int trf7970a_cmd(struct trf7970a *trf, u8 opcode)
+{
+ u8 cmd = TRF7970A_CMD_BIT_CTRL | TRF7970A_CMD_BIT_OPCODE(opcode);
+ int ret;
+
+ dev_dbg(trf->dev, "cmd: 0x%x\n", cmd);
+
+ ret = spi_write(trf->spi, &cmd, 1);
+ if (ret)
+ dev_err(trf->dev, "%s - cmd: 0x%x, ret: %d\n", __func__, cmd,
+ ret);
+ return ret;
+}
+
+static int trf7970a_read(struct trf7970a *trf, u8 reg, u8 *val)
+{
+ u8 addr = TRF7970A_CMD_BIT_RW | reg;
+ int ret;
+
+ ret = spi_write_then_read(trf->spi, &addr, 1, val, 1);
+ if (ret)
+ dev_err(trf->dev, "%s - addr: 0x%x, ret: %d\n", __func__, addr,
+ ret);
+
+ dev_dbg(trf->dev, "read(0x%x): 0x%x\n", addr, *val);
+
+ return ret;
+}
+
+static int trf7970a_read_cont(struct trf7970a *trf, u8 reg, u8 *buf, size_t len)
+{
+ u8 addr = reg | TRF7970A_CMD_BIT_RW | TRF7970A_CMD_BIT_CONTINUOUS;
+ struct spi_transfer t[2];
+ struct spi_message m;
+ int ret;
+
+ dev_dbg(trf->dev, "read_cont(0x%x, %zd)\n", addr, len);
+
+ spi_message_init(&m);
+
+ memset(&t, 0, sizeof(t));
+
+ t[0].tx_buf = &addr;
+ t[0].len = sizeof(addr);
+ spi_message_add_tail(&t[0], &m);
+
+ t[1].rx_buf = buf;
+ t[1].len = len;
+ spi_message_add_tail(&t[1], &m);
+
+ ret = spi_sync(trf->spi, &m);
+ if (ret)
+ dev_err(trf->dev, "%s - addr: 0x%x, ret: %d\n", __func__, addr,
+ ret);
+ return ret;
+}
+
+static int trf7970a_write(struct trf7970a *trf, u8 reg, u8 val)
+{
+ u8 buf[2] = { reg, val };
+ int ret;
+
+ dev_dbg(trf->dev, "write(0x%x): 0x%x\n", reg, val);
+
+ ret = spi_write(trf->spi, buf, 2);
+ if (ret)
+ dev_err(trf->dev, "%s - write: 0x%x 0x%x, ret: %d\n", __func__,
+ buf[0], buf[1], ret);
+
+ return ret;
+}
+
+static int trf7970a_read_irqstatus(struct trf7970a *trf, u8 *status)
+{
+ int ret;
+ u8 buf[2];
+ u8 addr;
+
+ addr = TRF7970A_IRQ_STATUS | TRF7970A_CMD_BIT_RW;
+
+ if (trf->quirks & TRF7970A_QUIRK_IRQ_STATUS_READ) {
+ addr |= TRF7970A_CMD_BIT_CONTINUOUS;
+ ret = spi_write_then_read(trf->spi, &addr, 1, buf, 2);
+ } else {
+ ret = spi_write_then_read(trf->spi, &addr, 1, buf, 1);
+ }
+
+ if (ret)
+ dev_err(trf->dev, "%s - irqstatus: Status read failed: %d\n",
+ __func__, ret);
+ else
+ *status = buf[0];
+
+ return ret;
+}
+
+static int trf7970a_read_target_proto(struct trf7970a *trf, u8 *target_proto)
+{
+ int ret;
+ u8 buf[2];
+ u8 addr;
+
+ addr = TRF79070A_NFC_TARGET_PROTOCOL | TRF7970A_CMD_BIT_RW |
+ TRF7970A_CMD_BIT_CONTINUOUS;
+
+ ret = spi_write_then_read(trf->spi, &addr, 1, buf, 2);
+ if (ret)
+ dev_err(trf->dev, "%s - target_proto: Read failed: %d\n",
+ __func__, ret);
+ else
+ *target_proto = buf[0];
+
+ return ret;
+}
+
+static int trf7970a_mode_detect(struct trf7970a *trf, u8 *rf_tech)
+{
+ int ret;
+ u8 target_proto, tech;
+
+ ret = trf7970a_read_target_proto(trf, &target_proto);
+ if (ret)
+ return ret;
+
+ switch (target_proto) {
+ case TRF79070A_NFC_TARGET_PROTOCOL_106A:
+ tech = NFC_DIGITAL_RF_TECH_106A;
+ break;
+ case TRF79070A_NFC_TARGET_PROTOCOL_106B:
+ tech = NFC_DIGITAL_RF_TECH_106B;
+ break;
+ case TRF79070A_NFC_TARGET_PROTOCOL_212F:
+ tech = NFC_DIGITAL_RF_TECH_212F;
+ break;
+ case TRF79070A_NFC_TARGET_PROTOCOL_424F:
+ tech = NFC_DIGITAL_RF_TECH_424F;
+ break;
+ default:
+ dev_dbg(trf->dev, "%s - mode_detect: target_proto: 0x%x\n",
+ __func__, target_proto);
+ return -EIO;
+ }
+
+ *rf_tech = tech;
+
+ return ret;
+}
+
+static void trf7970a_send_upstream(struct trf7970a *trf)
+{
+ dev_kfree_skb_any(trf->tx_skb);
+ trf->tx_skb = NULL;
+
+ if (trf->rx_skb && !IS_ERR(trf->rx_skb) && !trf->aborting)
+ print_hex_dump_debug("trf7970a rx data: ", DUMP_PREFIX_NONE,
+ 16, 1, trf->rx_skb->data, trf->rx_skb->len,
+ false);
+
+ trf->state = TRF7970A_ST_IDLE;
+
+ if (trf->aborting) {
+ dev_dbg(trf->dev, "Abort process complete\n");
+
+ if (!IS_ERR(trf->rx_skb)) {
+ kfree_skb(trf->rx_skb);
+ trf->rx_skb = ERR_PTR(-ECANCELED);
+ }
+
+ trf->aborting = false;
+ }
+
+ if (trf->adjust_resp_len) {
+ if (trf->rx_skb)
+ skb_trim(trf->rx_skb, trf->rx_skb->len - 1);
+
+ trf->adjust_resp_len = false;
+ }
+
+ trf->cb(trf->ddev, trf->cb_arg, trf->rx_skb);
+
+ trf->rx_skb = NULL;
+}
+
+static void trf7970a_send_err_upstream(struct trf7970a *trf, int errno)
+{
+ dev_dbg(trf->dev, "Error - state: %d, errno: %d\n", trf->state, errno);
+
+ cancel_delayed_work(&trf->timeout_work);
+
+ kfree_skb(trf->rx_skb);
+ trf->rx_skb = ERR_PTR(errno);
+
+ trf7970a_send_upstream(trf);
+}
+
+static int trf7970a_transmit(struct trf7970a *trf, struct sk_buff *skb,
+ unsigned int len, u8 *prefix, unsigned int prefix_len)
+{
+ struct spi_transfer t[2];
+ struct spi_message m;
+ unsigned int timeout;
+ int ret;
+
+ print_hex_dump_debug("trf7970a tx data: ", DUMP_PREFIX_NONE,
+ 16, 1, skb->data, len, false);
+
+ spi_message_init(&m);
+
+ memset(&t, 0, sizeof(t));
+
+ t[0].tx_buf = prefix;
+ t[0].len = prefix_len;
+ spi_message_add_tail(&t[0], &m);
+
+ t[1].tx_buf = skb->data;
+ t[1].len = len;
+ spi_message_add_tail(&t[1], &m);
+
+ ret = spi_sync(trf->spi, &m);
+ if (ret) {
+ dev_err(trf->dev, "%s - Can't send tx data: %d\n", __func__,
+ ret);
+ return ret;
+ }
+
+ skb_pull(skb, len);
+
+ if (skb->len > 0) {
+ trf->state = TRF7970A_ST_WAIT_FOR_TX_FIFO;
+ timeout = TRF7970A_WAIT_FOR_FIFO_DRAIN_TIMEOUT;
+ } else {
+ if (trf->issue_eof) {
+ trf->state = TRF7970A_ST_WAIT_TO_ISSUE_EOF;
+ timeout = TRF7970A_WAIT_TO_ISSUE_ISO15693_EOF;
+ } else {
+ trf->state = TRF7970A_ST_WAIT_FOR_RX_DATA;
+
+ if (!trf->timeout)
+ timeout = TRF7970A_WAIT_FOR_TX_IRQ;
+ else
+ timeout = trf->timeout;
+ }
+ }
+
+ dev_dbg(trf->dev, "Setting timeout for %d ms, state: %d\n", timeout,
+ trf->state);
+
+ schedule_delayed_work(&trf->timeout_work, msecs_to_jiffies(timeout));
+
+ return 0;
+}
+
+static void trf7970a_fill_fifo(struct trf7970a *trf)
+{
+ struct sk_buff *skb = trf->tx_skb;
+ unsigned int len;
+ int ret;
+ u8 fifo_bytes;
+ u8 prefix;
+
+ ret = trf7970a_read(trf, TRF7970A_FIFO_STATUS, &fifo_bytes);
+ if (ret) {
+ trf7970a_send_err_upstream(trf, ret);
+ return;
+ }
+
+ dev_dbg(trf->dev, "Filling FIFO - fifo_bytes: 0x%x\n", fifo_bytes);
+
+ fifo_bytes &= ~TRF7970A_FIFO_STATUS_OVERFLOW;
+
+ /* Calculate how much more data can be written to the fifo */
+ len = TRF7970A_FIFO_SIZE - fifo_bytes;
+ if (!len) {
+ schedule_delayed_work(&trf->timeout_work,
+ msecs_to_jiffies(TRF7970A_WAIT_FOR_FIFO_DRAIN_TIMEOUT));
+ return;
+ }
+
+ len = min(skb->len, len);
+
+ prefix = TRF7970A_CMD_BIT_CONTINUOUS | TRF7970A_FIFO_IO_REGISTER;
+
+ ret = trf7970a_transmit(trf, skb, len, &prefix, sizeof(prefix));
+ if (ret)
+ trf7970a_send_err_upstream(trf, ret);
+}
+
+static void trf7970a_drain_fifo(struct trf7970a *trf, u8 status)
+{
+ struct sk_buff *skb = trf->rx_skb;
+ int ret;
+ u8 fifo_bytes;
+
+ if (status & TRF7970A_IRQ_STATUS_ERROR) {
+ trf7970a_send_err_upstream(trf, -EIO);
+ return;
+ }
+
+ ret = trf7970a_read(trf, TRF7970A_FIFO_STATUS, &fifo_bytes);
+ if (ret) {
+ trf7970a_send_err_upstream(trf, ret);
+ return;
+ }
+
+ dev_dbg(trf->dev, "Draining FIFO - fifo_bytes: 0x%x\n", fifo_bytes);
+
+ fifo_bytes &= ~TRF7970A_FIFO_STATUS_OVERFLOW;
+
+ if (!fifo_bytes)
+ goto no_rx_data;
+
+ if (fifo_bytes > skb_tailroom(skb)) {
+ skb = skb_copy_expand(skb, skb_headroom(skb),
+ max_t(int, fifo_bytes,
+ TRF7970A_RX_SKB_ALLOC_SIZE),
+ GFP_KERNEL);
+ if (!skb) {
+ trf7970a_send_err_upstream(trf, -ENOMEM);
+ return;
+ }
+
+ kfree_skb(trf->rx_skb);
+ trf->rx_skb = skb;
+ }
+
+ ret = trf7970a_read_cont(trf, TRF7970A_FIFO_IO_REGISTER,
+ skb_put(skb, fifo_bytes), fifo_bytes);
+ if (ret) {
+ trf7970a_send_err_upstream(trf, ret);
+ return;
+ }
+
+ /* If received Type 2 ACK/NACK, shift right 4 bits and pass up */
+ if ((trf->framing == NFC_DIGITAL_FRAMING_NFCA_T2T) && (skb->len == 1) &&
+ (trf->special_fcn_reg1 ==
+ TRF7970A_SPECIAL_FCN_REG1_4_BIT_RX)) {
+ skb->data[0] >>= 4;
+ status = TRF7970A_IRQ_STATUS_SRX;
+ } else {
+ trf->state = TRF7970A_ST_WAIT_FOR_RX_DATA_CONT;
+
+ ret = trf7970a_read(trf, TRF7970A_FIFO_STATUS, &fifo_bytes);
+ if (ret) {
+ trf7970a_send_err_upstream(trf, ret);
+ return;
+ }
+
+ fifo_bytes &= ~TRF7970A_FIFO_STATUS_OVERFLOW;
+
+ /* If there are bytes in the FIFO, set status to '0' so
+ * the if stmt below doesn't fire and the driver will wait
+ * for the trf7970a to generate another RX interrupt.
+ */
+ if (fifo_bytes)
+ status = 0;
+ }
+
+no_rx_data:
+ if (status == TRF7970A_IRQ_STATUS_SRX) { /* Receive complete */
+ trf7970a_send_upstream(trf);
+ return;
+ }
+
+ dev_dbg(trf->dev, "Setting timeout for %d ms\n",
+ TRF7970A_WAIT_FOR_RX_DATA_TIMEOUT);
+
+ schedule_delayed_work(&trf->timeout_work,
+ msecs_to_jiffies(TRF7970A_WAIT_FOR_RX_DATA_TIMEOUT));
+}
+
+static irqreturn_t trf7970a_irq(int irq, void *dev_id)
+{
+ struct trf7970a *trf = dev_id;
+ int ret;
+ u8 status, fifo_bytes, iso_ctrl;
+
+ mutex_lock(&trf->lock);
+
+ if (trf->state == TRF7970A_ST_RF_OFF) {
+ mutex_unlock(&trf->lock);
+ return IRQ_NONE;
+ }
+
+ ret = trf7970a_read_irqstatus(trf, &status);
+ if (ret) {
+ mutex_unlock(&trf->lock);
+ return IRQ_NONE;
+ }
+
+ dev_dbg(trf->dev, "IRQ - state: %d, status: 0x%x\n", trf->state,
+ status);
+
+ if (!status) {
+ mutex_unlock(&trf->lock);
+ return IRQ_NONE;
+ }
+
+ switch (trf->state) {
+ case TRF7970A_ST_IDLE:
+ case TRF7970A_ST_IDLE_RX_BLOCKED:
+ /* If initiator and getting interrupts caused by RF noise,
+ * turn off the receiver to avoid unnecessary interrupts.
+ * It will be turned back on in trf7970a_send_cmd() when
+ * the next command is issued.
+ */
+ if (trf->is_initiator && (status & TRF7970A_IRQ_STATUS_ERROR)) {
+ trf7970a_cmd(trf, TRF7970A_CMD_BLOCK_RX);
+ trf->state = TRF7970A_ST_IDLE_RX_BLOCKED;
+ }
+
+ trf7970a_cmd(trf, TRF7970A_CMD_FIFO_RESET);
+ break;
+ case TRF7970A_ST_WAIT_FOR_TX_FIFO:
+ if (status & TRF7970A_IRQ_STATUS_TX) {
+ trf->ignore_timeout =
+ !cancel_delayed_work(&trf->timeout_work);
+ trf7970a_fill_fifo(trf);
+ } else {
+ trf7970a_send_err_upstream(trf, -EIO);
+ }
+ break;
+ case TRF7970A_ST_WAIT_FOR_RX_DATA:
+ case TRF7970A_ST_WAIT_FOR_RX_DATA_CONT:
+ if (status & TRF7970A_IRQ_STATUS_SRX) {
+ trf->ignore_timeout =
+ !cancel_delayed_work(&trf->timeout_work);
+ trf7970a_drain_fifo(trf, status);
+ } else if (status & TRF7970A_IRQ_STATUS_FIFO) {
+ ret = trf7970a_read(trf, TRF7970A_FIFO_STATUS,
+ &fifo_bytes);
+
+ fifo_bytes &= ~TRF7970A_FIFO_STATUS_OVERFLOW;
+
+ if (ret)
+ trf7970a_send_err_upstream(trf, ret);
+ else if (!fifo_bytes)
+ trf7970a_cmd(trf, TRF7970A_CMD_FIFO_RESET);
+ } else if ((status == TRF7970A_IRQ_STATUS_TX) ||
+ (!trf->is_initiator &&
+ (status == (TRF7970A_IRQ_STATUS_TX |
+ TRF7970A_IRQ_STATUS_NFC_RF)))) {
+ trf7970a_cmd(trf, TRF7970A_CMD_FIFO_RESET);
+
+ if (!trf->timeout) {
+ trf->ignore_timeout = !cancel_delayed_work(
+ &trf->timeout_work);
+ trf->rx_skb = ERR_PTR(0);
+ trf7970a_send_upstream(trf);
+ break;
+ }
+
+ if (trf->is_initiator)
+ break;
+
+ iso_ctrl = trf->iso_ctrl;
+
+ switch (trf->framing) {
+ case NFC_DIGITAL_FRAMING_NFCA_STANDARD:
+ trf->tx_cmd = TRF7970A_CMD_TRANSMIT_NO_CRC;
+ iso_ctrl |= TRF7970A_ISO_CTRL_RX_CRC_N;
+ trf->iso_ctrl = 0xff; /* Force ISO_CTRL write */
+ break;
+ case NFC_DIGITAL_FRAMING_NFCA_STANDARD_WITH_CRC_A:
+ trf->tx_cmd = TRF7970A_CMD_TRANSMIT;
+ iso_ctrl &= ~TRF7970A_ISO_CTRL_RX_CRC_N;
+ trf->iso_ctrl = 0xff; /* Force ISO_CTRL write */
+ break;
+ case NFC_DIGITAL_FRAMING_NFCA_ANTICOL_COMPLETE:
+ ret = trf7970a_write(trf,
+ TRF7970A_SPECIAL_FCN_REG1,
+ TRF7970A_SPECIAL_FCN_REG1_14_ANTICOLL);
+ if (ret)
+ goto err_unlock_exit;
+
+ trf->special_fcn_reg1 =
+ TRF7970A_SPECIAL_FCN_REG1_14_ANTICOLL;
+ break;
+ default:
+ break;
+ }
+
+ if (iso_ctrl != trf->iso_ctrl) {
+ ret = trf7970a_write(trf, TRF7970A_ISO_CTRL,
+ iso_ctrl);
+ if (ret)
+ goto err_unlock_exit;
+
+ trf->iso_ctrl = iso_ctrl;
+ }
+ } else {
+ trf7970a_send_err_upstream(trf, -EIO);
+ }
+ break;
+ case TRF7970A_ST_WAIT_TO_ISSUE_EOF:
+ if (status != TRF7970A_IRQ_STATUS_TX)
+ trf7970a_send_err_upstream(trf, -EIO);
+ break;
+ case TRF7970A_ST_LISTENING:
+ if (status & TRF7970A_IRQ_STATUS_SRX) {
+ trf->ignore_timeout =
+ !cancel_delayed_work(&trf->timeout_work);
+ trf7970a_drain_fifo(trf, status);
+ } else if (!(status & TRF7970A_IRQ_STATUS_NFC_RF)) {
+ trf7970a_send_err_upstream(trf, -EIO);
+ }
+ break;
+ case TRF7970A_ST_LISTENING_MD:
+ if (status & TRF7970A_IRQ_STATUS_SRX) {
+ trf->ignore_timeout =
+ !cancel_delayed_work(&trf->timeout_work);
+
+ ret = trf7970a_mode_detect(trf, &trf->md_rf_tech);
+ if (ret) {
+ trf7970a_send_err_upstream(trf, ret);
+ } else {
+ trf->state = TRF7970A_ST_LISTENING;
+ trf7970a_drain_fifo(trf, status);
+ }
+ } else if (!(status & TRF7970A_IRQ_STATUS_NFC_RF)) {
+ trf7970a_send_err_upstream(trf, -EIO);
+ }
+ break;
+ default:
+ dev_err(trf->dev, "%s - Driver in invalid state: %d\n",
+ __func__, trf->state);
+ }
+
+err_unlock_exit:
+ mutex_unlock(&trf->lock);
+ return IRQ_HANDLED;
+}
+
+static void trf7970a_issue_eof(struct trf7970a *trf)
+{
+ int ret;
+
+ dev_dbg(trf->dev, "Issuing EOF\n");
+
+ ret = trf7970a_cmd(trf, TRF7970A_CMD_FIFO_RESET);
+ if (ret)
+ trf7970a_send_err_upstream(trf, ret);
+
+ ret = trf7970a_cmd(trf, TRF7970A_CMD_EOF);
+ if (ret)
+ trf7970a_send_err_upstream(trf, ret);
+
+ trf->state = TRF7970A_ST_WAIT_FOR_RX_DATA;
+
+ dev_dbg(trf->dev, "Setting timeout for %d ms, state: %d\n",
+ trf->timeout, trf->state);
+
+ schedule_delayed_work(&trf->timeout_work,
+ msecs_to_jiffies(trf->timeout));
+}
+
+static void trf7970a_timeout_work_handler(struct work_struct *work)
+{
+ struct trf7970a *trf = container_of(work, struct trf7970a,
+ timeout_work.work);
+
+ dev_dbg(trf->dev, "Timeout - state: %d, ignore_timeout: %d\n",
+ trf->state, trf->ignore_timeout);
+
+ mutex_lock(&trf->lock);
+
+ if (trf->ignore_timeout)
+ trf->ignore_timeout = false;
+ else if (trf->state == TRF7970A_ST_WAIT_FOR_RX_DATA_CONT)
+ trf7970a_drain_fifo(trf, TRF7970A_IRQ_STATUS_SRX);
+ else if (trf->state == TRF7970A_ST_WAIT_TO_ISSUE_EOF)
+ trf7970a_issue_eof(trf);
+ else
+ trf7970a_send_err_upstream(trf, -ETIMEDOUT);
+
+ mutex_unlock(&trf->lock);
+}
+
+static int trf7970a_init(struct trf7970a *trf)
+{
+ int ret;
+
+ dev_dbg(trf->dev, "Initializing device - state: %d\n", trf->state);
+
+ ret = trf7970a_cmd(trf, TRF7970A_CMD_SOFT_INIT);
+ if (ret)
+ goto err_out;
+
+ ret = trf7970a_cmd(trf, TRF7970A_CMD_IDLE);
+ if (ret)
+ goto err_out;
+
+ ret = trf7970a_write(trf, TRF7970A_NFC_TARGET_LEVEL, 0);
+ if (ret)
+ goto err_out;
+
+ usleep_range(1000, 2000);
+
+ trf->chip_status_ctrl &= ~TRF7970A_CHIP_STATUS_RF_ON;
+
+ ret = trf7970a_write(trf, TRF7970A_MODULATOR_SYS_CLK_CTRL, 0);
+ if (ret)
+ goto err_out;
+
+ trf->modulator_sys_clk_ctrl = 0;
+
+ ret = trf7970a_write(trf, TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS,
+ TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS_WLH_96 |
+ TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS_WLL_32);
+ if (ret)
+ goto err_out;
+
+ ret = trf7970a_write(trf, TRF7970A_SPECIAL_FCN_REG1, 0);
+ if (ret)
+ goto err_out;
+
+ trf->special_fcn_reg1 = 0;
+
+ trf->iso_ctrl = 0xff;
+ return 0;
+
+err_out:
+ dev_dbg(trf->dev, "Couldn't init device: %d\n", ret);
+ return ret;
+}
+
+static void trf7970a_switch_rf_off(struct trf7970a *trf)
+{
+ if ((trf->state == TRF7970A_ST_PWR_OFF) ||
+ (trf->state == TRF7970A_ST_RF_OFF))
+ return;
+
+ dev_dbg(trf->dev, "Switching rf off\n");
+
+ trf->chip_status_ctrl &= ~TRF7970A_CHIP_STATUS_RF_ON;
+
+ trf7970a_write(trf, TRF7970A_CHIP_STATUS_CTRL, trf->chip_status_ctrl);
+
+ trf->aborting = false;
+ trf->state = TRF7970A_ST_RF_OFF;
+
+ pm_runtime_mark_last_busy(trf->dev);
+ pm_runtime_put_autosuspend(trf->dev);
+}
+
+static int trf7970a_switch_rf_on(struct trf7970a *trf)
+{
+ int ret;
+
+ dev_dbg(trf->dev, "Switching rf on\n");
+
+ pm_runtime_get_sync(trf->dev);
+
+ if (trf->state != TRF7970A_ST_RF_OFF) { /* Power on, RF off */
+ dev_err(trf->dev, "%s - Incorrect state: %d\n", __func__,
+ trf->state);
+ return -EINVAL;
+ }
+
+ ret = trf7970a_init(trf);
+ if (ret) {
+ dev_err(trf->dev, "%s - Can't initialize: %d\n", __func__, ret);
+ return ret;
+ }
+
+ trf->state = TRF7970A_ST_IDLE;
+
+ return 0;
+}
+
+static int trf7970a_switch_rf(struct nfc_digital_dev *ddev, bool on)
+{
+ struct trf7970a *trf = nfc_digital_get_drvdata(ddev);
+ int ret = 0;
+
+ dev_dbg(trf->dev, "Switching RF - state: %d, on: %d\n", trf->state, on);
+
+ mutex_lock(&trf->lock);
+
+ if (on) {
+ switch (trf->state) {
+ case TRF7970A_ST_PWR_OFF:
+ case TRF7970A_ST_RF_OFF:
+ ret = trf7970a_switch_rf_on(trf);
+ break;
+ case TRF7970A_ST_IDLE:
+ case TRF7970A_ST_IDLE_RX_BLOCKED:
+ break;
+ default:
+ dev_err(trf->dev, "%s - Invalid request: %d %d\n",
+ __func__, trf->state, on);
+ trf7970a_switch_rf_off(trf);
+ ret = -EINVAL;
+ }
+ } else {
+ switch (trf->state) {
+ case TRF7970A_ST_PWR_OFF:
+ case TRF7970A_ST_RF_OFF:
+ break;
+ default:
+ dev_err(trf->dev, "%s - Invalid request: %d %d\n",
+ __func__, trf->state, on);
+ ret = -EINVAL;
+ /* FALLTHROUGH */
+ case TRF7970A_ST_IDLE:
+ case TRF7970A_ST_IDLE_RX_BLOCKED:
+ case TRF7970A_ST_WAIT_FOR_RX_DATA:
+ case TRF7970A_ST_WAIT_FOR_RX_DATA_CONT:
+ trf7970a_switch_rf_off(trf);
+ }
+ }
+
+ mutex_unlock(&trf->lock);
+ return ret;
+}
+
+static int trf7970a_in_config_rf_tech(struct trf7970a *trf, int tech)
+{
+ int ret = 0;
+
+ dev_dbg(trf->dev, "rf technology: %d\n", tech);
+
+ switch (tech) {
+ case NFC_DIGITAL_RF_TECH_106A:
+ trf->iso_ctrl_tech = TRF7970A_ISO_CTRL_14443A_106;
+ trf->modulator_sys_clk_ctrl = TRF7970A_MODULATOR_DEPTH_OOK;
+ trf->guard_time = TRF7970A_GUARD_TIME_NFCA;
+ break;
+ case NFC_DIGITAL_RF_TECH_106B:
+ trf->iso_ctrl_tech = TRF7970A_ISO_CTRL_14443B_106;
+ trf->modulator_sys_clk_ctrl = TRF7970A_MODULATOR_DEPTH_ASK10;
+ trf->guard_time = TRF7970A_GUARD_TIME_NFCB;
+ break;
+ case NFC_DIGITAL_RF_TECH_212F:
+ trf->iso_ctrl_tech = TRF7970A_ISO_CTRL_FELICA_212;
+ trf->modulator_sys_clk_ctrl = TRF7970A_MODULATOR_DEPTH_ASK10;
+ trf->guard_time = TRF7970A_GUARD_TIME_NFCF;
+ break;
+ case NFC_DIGITAL_RF_TECH_424F:
+ trf->iso_ctrl_tech = TRF7970A_ISO_CTRL_FELICA_424;
+ trf->modulator_sys_clk_ctrl = TRF7970A_MODULATOR_DEPTH_ASK10;
+ trf->guard_time = TRF7970A_GUARD_TIME_NFCF;
+ break;
+ case NFC_DIGITAL_RF_TECH_ISO15693:
+ trf->iso_ctrl_tech = TRF7970A_ISO_CTRL_15693_SGL_1OF4_2648;
+ trf->modulator_sys_clk_ctrl = TRF7970A_MODULATOR_DEPTH_OOK;
+ trf->guard_time = TRF7970A_GUARD_TIME_15693;
+ break;
+ default:
+ dev_dbg(trf->dev, "Unsupported rf technology: %d\n", tech);
+ return -EINVAL;
+ }
+
+ trf->technology = tech;
+
+ /* If in initiator mode and not changing the RF tech due to a
+ * PSL sequence (indicated by 'trf->iso_ctrl == 0xff' from
+ * trf7970a_init()), clear the NFC Target Detection Level register
+ * due to erratum.
+ */
+ if (trf->iso_ctrl == 0xff)
+ ret = trf7970a_write(trf, TRF7970A_NFC_TARGET_LEVEL, 0);
+
+ return ret;
+}
+
+static int trf7970a_is_rf_field(struct trf7970a *trf, bool *is_rf_field)
+{
+ int ret;
+ u8 rssi;
+
+ ret = trf7970a_write(trf, TRF7970A_CHIP_STATUS_CTRL,
+ trf->chip_status_ctrl | TRF7970A_CHIP_STATUS_REC_ON);
+ if (ret)
+ return ret;
+
+ ret = trf7970a_cmd(trf, TRF7970A_CMD_TEST_EXT_RF);
+ if (ret)
+ return ret;
+
+ usleep_range(50, 60);
+
+ ret = trf7970a_read(trf, TRF7970A_RSSI_OSC_STATUS, &rssi);
+ if (ret)
+ return ret;
+
+ ret = trf7970a_write(trf, TRF7970A_CHIP_STATUS_CTRL,
+ trf->chip_status_ctrl);
+ if (ret)
+ return ret;
+
+ if (rssi & TRF7970A_RSSI_OSC_STATUS_RSSI_MASK)
+ *is_rf_field = true;
+ else
+ *is_rf_field = false;
+
+ return 0;
+}
+
+static int trf7970a_in_config_framing(struct trf7970a *trf, int framing)
+{
+ u8 iso_ctrl = trf->iso_ctrl_tech;
+ bool is_rf_field = false;
+ int ret;
+
+ dev_dbg(trf->dev, "framing: %d\n", framing);
+
+ switch (framing) {
+ case NFC_DIGITAL_FRAMING_NFCA_SHORT:
+ case NFC_DIGITAL_FRAMING_NFCA_STANDARD:
+ trf->tx_cmd = TRF7970A_CMD_TRANSMIT_NO_CRC;
+ iso_ctrl |= TRF7970A_ISO_CTRL_RX_CRC_N;
+ break;
+ case NFC_DIGITAL_FRAMING_NFCA_STANDARD_WITH_CRC_A:
+ case NFC_DIGITAL_FRAMING_NFCA_T4T:
+ case NFC_DIGITAL_FRAMING_NFCB:
+ case NFC_DIGITAL_FRAMING_NFCB_T4T:
+ case NFC_DIGITAL_FRAMING_NFCF:
+ case NFC_DIGITAL_FRAMING_NFCF_T3T:
+ case NFC_DIGITAL_FRAMING_ISO15693_INVENTORY:
+ case NFC_DIGITAL_FRAMING_ISO15693_T5T:
+ case NFC_DIGITAL_FRAMING_NFCA_NFC_DEP:
+ case NFC_DIGITAL_FRAMING_NFCF_NFC_DEP:
+ trf->tx_cmd = TRF7970A_CMD_TRANSMIT;
+ iso_ctrl &= ~TRF7970A_ISO_CTRL_RX_CRC_N;
+ break;
+ case NFC_DIGITAL_FRAMING_NFCA_T2T:
+ trf->tx_cmd = TRF7970A_CMD_TRANSMIT;
+ iso_ctrl |= TRF7970A_ISO_CTRL_RX_CRC_N;
+ break;
+ default:
+ dev_dbg(trf->dev, "Unsupported Framing: %d\n", framing);
+ return -EINVAL;
+ }
+
+ trf->framing = framing;
+
+ if (!(trf->chip_status_ctrl & TRF7970A_CHIP_STATUS_RF_ON)) {
+ ret = trf7970a_is_rf_field(trf, &is_rf_field);
+ if (ret)
+ return ret;
+
+ if (is_rf_field)
+ return -EBUSY;
+ }
+
+ if (iso_ctrl != trf->iso_ctrl) {
+ ret = trf7970a_write(trf, TRF7970A_ISO_CTRL, iso_ctrl);
+ if (ret)
+ return ret;
+
+ trf->iso_ctrl = iso_ctrl;
+
+ ret = trf7970a_write(trf, TRF7970A_MODULATOR_SYS_CLK_CTRL,
+ trf->modulator_sys_clk_ctrl);
+ if (ret)
+ return ret;
+ }
+
+ if (!(trf->chip_status_ctrl & TRF7970A_CHIP_STATUS_RF_ON)) {
+ ret = trf7970a_write(trf, TRF7970A_CHIP_STATUS_CTRL,
+ trf->chip_status_ctrl |
+ TRF7970A_CHIP_STATUS_RF_ON);
+ if (ret)
+ return ret;
+
+ trf->chip_status_ctrl |= TRF7970A_CHIP_STATUS_RF_ON;
+
+ usleep_range(trf->guard_time, trf->guard_time + 1000);
+ }
+
+ return 0;
+}
+
+static int trf7970a_in_configure_hw(struct nfc_digital_dev *ddev, int type,
+ int param)
+{
+ struct trf7970a *trf = nfc_digital_get_drvdata(ddev);
+ int ret;
+
+ dev_dbg(trf->dev, "Configure hw - type: %d, param: %d\n", type, param);
+
+ mutex_lock(&trf->lock);
+
+ trf->is_initiator = true;
+
+ if ((trf->state == TRF7970A_ST_PWR_OFF) ||
+ (trf->state == TRF7970A_ST_RF_OFF)) {
+ ret = trf7970a_switch_rf_on(trf);
+ if (ret)
+ goto err_unlock;
+ }
+
+ switch (type) {
+ case NFC_DIGITAL_CONFIG_RF_TECH:
+ ret = trf7970a_in_config_rf_tech(trf, param);
+ break;
+ case NFC_DIGITAL_CONFIG_FRAMING:
+ ret = trf7970a_in_config_framing(trf, param);
+ break;
+ default:
+ dev_dbg(trf->dev, "Unknown type: %d\n", type);
+ ret = -EINVAL;
+ }
+
+err_unlock:
+ mutex_unlock(&trf->lock);
+ return ret;
+}
+
+static int trf7970a_is_iso15693_write_or_lock(u8 cmd)
+{
+ switch (cmd) {
+ case ISO15693_CMD_WRITE_SINGLE_BLOCK:
+ case ISO15693_CMD_LOCK_BLOCK:
+ case ISO15693_CMD_WRITE_MULTIPLE_BLOCK:
+ case ISO15693_CMD_WRITE_AFI:
+ case ISO15693_CMD_LOCK_AFI:
+ case ISO15693_CMD_WRITE_DSFID:
+ case ISO15693_CMD_LOCK_DSFID:
+ return 1;
+ break;
+ default:
+ return 0;
+ }
+}
+
+static int trf7970a_per_cmd_config(struct trf7970a *trf, struct sk_buff *skb)
+{
+ u8 *req = skb->data;
+ u8 special_fcn_reg1, iso_ctrl;
+ int ret;
+
+ trf->issue_eof = false;
+
+ /* When issuing Type 2 read command, make sure the '4_bit_RX' bit in
+ * special functions register 1 is cleared; otherwise, its a write or
+ * sector select command and '4_bit_RX' must be set.
+ *
+ * When issuing an ISO 15693 command, inspect the flags byte to see
+ * what speed to use. Also, remember if the OPTION flag is set on
+ * a Type 5 write or lock command so the driver will know that it
+ * has to send an EOF in order to get a response.
+ */
+ if ((trf->technology == NFC_DIGITAL_RF_TECH_106A) &&
+ (trf->framing == NFC_DIGITAL_FRAMING_NFCA_T2T)) {
+ if (req[0] == NFC_T2T_CMD_READ)
+ special_fcn_reg1 = 0;
+ else
+ special_fcn_reg1 = TRF7970A_SPECIAL_FCN_REG1_4_BIT_RX;
+
+ if (special_fcn_reg1 != trf->special_fcn_reg1) {
+ ret = trf7970a_write(trf, TRF7970A_SPECIAL_FCN_REG1,
+ special_fcn_reg1);
+ if (ret)
+ return ret;
+
+ trf->special_fcn_reg1 = special_fcn_reg1;
+ }
+ } else if (trf->technology == NFC_DIGITAL_RF_TECH_ISO15693) {
+ iso_ctrl = trf->iso_ctrl & ~TRF7970A_ISO_CTRL_RFID_SPEED_MASK;
+
+ switch (req[0] & ISO15693_REQ_FLAG_SPEED_MASK) {
+ case 0x00:
+ iso_ctrl |= TRF7970A_ISO_CTRL_15693_SGL_1OF4_662;
+ break;
+ case ISO15693_REQ_FLAG_SUB_CARRIER:
+ iso_ctrl |= TRF7970A_ISO_CTRL_15693_DBL_1OF4_667a;
+ break;
+ case ISO15693_REQ_FLAG_DATA_RATE:
+ iso_ctrl |= TRF7970A_ISO_CTRL_15693_SGL_1OF4_2648;
+ break;
+ case (ISO15693_REQ_FLAG_SUB_CARRIER |
+ ISO15693_REQ_FLAG_DATA_RATE):
+ iso_ctrl |= TRF7970A_ISO_CTRL_15693_DBL_1OF4_2669;
+ break;
+ }
+
+ if (iso_ctrl != trf->iso_ctrl) {
+ ret = trf7970a_write(trf, TRF7970A_ISO_CTRL, iso_ctrl);
+ if (ret)
+ return ret;
+
+ trf->iso_ctrl = iso_ctrl;
+ }
+
+ if (trf->framing == NFC_DIGITAL_FRAMING_ISO15693_T5T) {
+ if (trf7970a_is_iso15693_write_or_lock(req[1]) &&
+ (req[0] & ISO15693_REQ_FLAG_OPTION))
+ trf->issue_eof = true;
+ else if ((trf->quirks &
+ TRF7970A_QUIRK_T5T_RMB_EXTRA_BYTE) &&
+ (req[1] == ISO15693_CMD_READ_MULTIPLE_BLOCK))
+ trf->adjust_resp_len = true;
+ }
+ }
+
+ return 0;
+}
+
+static int trf7970a_send_cmd(struct nfc_digital_dev *ddev,
+ struct sk_buff *skb, u16 timeout,
+ nfc_digital_cmd_complete_t cb, void *arg)
+{
+ struct trf7970a *trf = nfc_digital_get_drvdata(ddev);
+ u8 prefix[5];
+ unsigned int len;
+ int ret;
+ u8 status;
+
+ dev_dbg(trf->dev, "New request - state: %d, timeout: %d ms, len: %d\n",
+ trf->state, timeout, skb->len);
+
+ if (skb->len > TRF7970A_TX_MAX)
+ return -EINVAL;
+
+ mutex_lock(&trf->lock);
+
+ if ((trf->state != TRF7970A_ST_IDLE) &&
+ (trf->state != TRF7970A_ST_IDLE_RX_BLOCKED)) {
+ dev_err(trf->dev, "%s - Bogus state: %d\n", __func__,
+ trf->state);
+ ret = -EIO;
+ goto out_err;
+ }
+
+ if (trf->aborting) {
+ dev_dbg(trf->dev, "Abort process complete\n");
+ trf->aborting = false;
+ ret = -ECANCELED;
+ goto out_err;
+ }
+
+ if (timeout) {
+ trf->rx_skb = nfc_alloc_recv_skb(TRF7970A_RX_SKB_ALLOC_SIZE,
+ GFP_KERNEL);
+ if (!trf->rx_skb) {
+ dev_dbg(trf->dev, "Can't alloc rx_skb\n");
+ ret = -ENOMEM;
+ goto out_err;
+ }
+ }
+
+ if (trf->state == TRF7970A_ST_IDLE_RX_BLOCKED) {
+ ret = trf7970a_cmd(trf, TRF7970A_CMD_ENABLE_RX);
+ if (ret)
+ goto out_err;
+
+ trf->state = TRF7970A_ST_IDLE;
+ }
+
+ if (trf->is_initiator) {
+ ret = trf7970a_per_cmd_config(trf, skb);
+ if (ret)
+ goto out_err;
+ }
+
+ trf->ddev = ddev;
+ trf->tx_skb = skb;
+ trf->cb = cb;
+ trf->cb_arg = arg;
+ trf->timeout = timeout;
+ trf->ignore_timeout = false;
+
+ len = skb->len;
+
+ /* TX data must be prefixed with a FIFO reset cmd, a cmd that depends
+ * on what the current framing is, the address of the TX length byte 1
+ * register (0x1d), and the 2 byte length of the data to be transmitted.
+ * That totals 5 bytes.
+ */
+ prefix[0] = TRF7970A_CMD_BIT_CTRL |
+ TRF7970A_CMD_BIT_OPCODE(TRF7970A_CMD_FIFO_RESET);
+ prefix[1] = TRF7970A_CMD_BIT_CTRL |
+ TRF7970A_CMD_BIT_OPCODE(trf->tx_cmd);
+ prefix[2] = TRF7970A_CMD_BIT_CONTINUOUS | TRF7970A_TX_LENGTH_BYTE1;
+
+ if (trf->framing == NFC_DIGITAL_FRAMING_NFCA_SHORT) {
+ prefix[3] = 0x00;
+ prefix[4] = 0x0f; /* 7 bits */
+ } else {
+ prefix[3] = (len & 0xf00) >> 4;
+ prefix[3] |= ((len & 0xf0) >> 4);
+ prefix[4] = ((len & 0x0f) << 4);
+ }
+
+ len = min_t(int, skb->len, TRF7970A_FIFO_SIZE);
+
+ /* Clear possible spurious interrupt */
+ ret = trf7970a_read_irqstatus(trf, &status);
+ if (ret)
+ goto out_err;
+
+ ret = trf7970a_transmit(trf, skb, len, prefix, sizeof(prefix));
+ if (ret) {
+ kfree_skb(trf->rx_skb);
+ trf->rx_skb = NULL;
+ }
+
+out_err:
+ mutex_unlock(&trf->lock);
+ return ret;
+}
+
+static int trf7970a_tg_config_rf_tech(struct trf7970a *trf, int tech)
+{
+ int ret = 0;
+
+ dev_dbg(trf->dev, "rf technology: %d\n", tech);
+
+ switch (tech) {
+ case NFC_DIGITAL_RF_TECH_106A:
+ trf->iso_ctrl_tech = TRF7970A_ISO_CTRL_NFC_NFC_CE_MODE |
+ TRF7970A_ISO_CTRL_NFC_CE |
+ TRF7970A_ISO_CTRL_NFC_CE_14443A;
+ trf->modulator_sys_clk_ctrl = TRF7970A_MODULATOR_DEPTH_OOK;
+ break;
+ case NFC_DIGITAL_RF_TECH_212F:
+ trf->iso_ctrl_tech = TRF7970A_ISO_CTRL_NFC_NFC_CE_MODE |
+ TRF7970A_ISO_CTRL_NFC_NFCF_212;
+ trf->modulator_sys_clk_ctrl = TRF7970A_MODULATOR_DEPTH_ASK10;
+ break;
+ case NFC_DIGITAL_RF_TECH_424F:
+ trf->iso_ctrl_tech = TRF7970A_ISO_CTRL_NFC_NFC_CE_MODE |
+ TRF7970A_ISO_CTRL_NFC_NFCF_424;
+ trf->modulator_sys_clk_ctrl = TRF7970A_MODULATOR_DEPTH_ASK10;
+ break;
+ default:
+ dev_dbg(trf->dev, "Unsupported rf technology: %d\n", tech);
+ return -EINVAL;
+ }
+
+ trf->technology = tech;
+
+ /* Normally we write the ISO_CTRL register in
+ * trf7970a_tg_config_framing() because the framing can change
+ * the value written. However, when sending a PSL RES,
+ * digital_tg_send_psl_res_complete() doesn't call
+ * trf7970a_tg_config_framing() so we must write the register
+ * here.
+ */
+ if ((trf->framing == NFC_DIGITAL_FRAMING_NFC_DEP_ACTIVATED) &&
+ (trf->iso_ctrl_tech != trf->iso_ctrl)) {
+ ret = trf7970a_write(trf, TRF7970A_ISO_CTRL,
+ trf->iso_ctrl_tech);
+
+ trf->iso_ctrl = trf->iso_ctrl_tech;
+ }
+
+ return ret;
+}
+
+/* Since this is a target routine, several of the framing calls are
+ * made between receiving the request and sending the response so they
+ * should take effect until after the response is sent. This is accomplished
+ * by skipping the ISO_CTRL register write here and doing it in the interrupt
+ * handler.
+ */
+static int trf7970a_tg_config_framing(struct trf7970a *trf, int framing)
+{
+ u8 iso_ctrl = trf->iso_ctrl_tech;
+ int ret;
+
+ dev_dbg(trf->dev, "framing: %d\n", framing);
+
+ switch (framing) {
+ case NFC_DIGITAL_FRAMING_NFCA_NFC_DEP:
+ trf->tx_cmd = TRF7970A_CMD_TRANSMIT_NO_CRC;
+ iso_ctrl |= TRF7970A_ISO_CTRL_RX_CRC_N;
+ break;
+ case NFC_DIGITAL_FRAMING_NFCA_STANDARD:
+ case NFC_DIGITAL_FRAMING_NFCA_STANDARD_WITH_CRC_A:
+ case NFC_DIGITAL_FRAMING_NFCA_ANTICOL_COMPLETE:
+ /* These ones are applied in the interrupt handler */
+ iso_ctrl = trf->iso_ctrl; /* Don't write to ISO_CTRL yet */
+ break;
+ case NFC_DIGITAL_FRAMING_NFCF_NFC_DEP:
+ trf->tx_cmd = TRF7970A_CMD_TRANSMIT;
+ iso_ctrl &= ~TRF7970A_ISO_CTRL_RX_CRC_N;
+ break;
+ case NFC_DIGITAL_FRAMING_NFC_DEP_ACTIVATED:
+ trf->tx_cmd = TRF7970A_CMD_TRANSMIT;
+ iso_ctrl &= ~TRF7970A_ISO_CTRL_RX_CRC_N;
+ break;
+ default:
+ dev_dbg(trf->dev, "Unsupported Framing: %d\n", framing);
+ return -EINVAL;
+ }
+
+ trf->framing = framing;
+
+ if (iso_ctrl != trf->iso_ctrl) {
+ ret = trf7970a_write(trf, TRF7970A_ISO_CTRL, iso_ctrl);
+ if (ret)
+ return ret;
+
+ trf->iso_ctrl = iso_ctrl;
+
+ ret = trf7970a_write(trf, TRF7970A_MODULATOR_SYS_CLK_CTRL,
+ trf->modulator_sys_clk_ctrl);
+ if (ret)
+ return ret;
+ }
+
+ if (!(trf->chip_status_ctrl & TRF7970A_CHIP_STATUS_RF_ON)) {
+ ret = trf7970a_write(trf, TRF7970A_CHIP_STATUS_CTRL,
+ trf->chip_status_ctrl |
+ TRF7970A_CHIP_STATUS_RF_ON);
+ if (ret)
+ return ret;
+
+ trf->chip_status_ctrl |= TRF7970A_CHIP_STATUS_RF_ON;
+ }
+
+ return 0;
+}
+
+static int trf7970a_tg_configure_hw(struct nfc_digital_dev *ddev, int type,
+ int param)
+{
+ struct trf7970a *trf = nfc_digital_get_drvdata(ddev);
+ int ret;
+
+ dev_dbg(trf->dev, "Configure hw - type: %d, param: %d\n", type, param);
+
+ mutex_lock(&trf->lock);
+
+ trf->is_initiator = false;
+
+ if ((trf->state == TRF7970A_ST_PWR_OFF) ||
+ (trf->state == TRF7970A_ST_RF_OFF)) {
+ ret = trf7970a_switch_rf_on(trf);
+ if (ret)
+ goto err_unlock;
+ }
+
+ switch (type) {
+ case NFC_DIGITAL_CONFIG_RF_TECH:
+ ret = trf7970a_tg_config_rf_tech(trf, param);
+ break;
+ case NFC_DIGITAL_CONFIG_FRAMING:
+ ret = trf7970a_tg_config_framing(trf, param);
+ break;
+ default:
+ dev_dbg(trf->dev, "Unknown type: %d\n", type);
+ ret = -EINVAL;
+ }
+
+err_unlock:
+ mutex_unlock(&trf->lock);
+ return ret;
+}
+
+static int _trf7970a_tg_listen(struct nfc_digital_dev *ddev, u16 timeout,
+ nfc_digital_cmd_complete_t cb, void *arg, bool mode_detect)
+{
+ struct trf7970a *trf = nfc_digital_get_drvdata(ddev);
+ int ret;
+
+ mutex_lock(&trf->lock);
+
+ if ((trf->state != TRF7970A_ST_IDLE) &&
+ (trf->state != TRF7970A_ST_IDLE_RX_BLOCKED)) {
+ dev_err(trf->dev, "%s - Bogus state: %d\n", __func__,
+ trf->state);
+ ret = -EIO;
+ goto out_err;
+ }
+
+ if (trf->aborting) {
+ dev_dbg(trf->dev, "Abort process complete\n");
+ trf->aborting = false;
+ ret = -ECANCELED;
+ goto out_err;
+ }
+
+ trf->rx_skb = nfc_alloc_recv_skb(TRF7970A_RX_SKB_ALLOC_SIZE,
+ GFP_KERNEL);
+ if (!trf->rx_skb) {
+ dev_dbg(trf->dev, "Can't alloc rx_skb\n");
+ ret = -ENOMEM;
+ goto out_err;
+ }
+
+ ret = trf7970a_write(trf, TRF7970A_RX_SPECIAL_SETTINGS,
+ TRF7970A_RX_SPECIAL_SETTINGS_HBT |
+ TRF7970A_RX_SPECIAL_SETTINGS_M848 |
+ TRF7970A_RX_SPECIAL_SETTINGS_C424 |
+ TRF7970A_RX_SPECIAL_SETTINGS_C212);
+ if (ret)
+ goto out_err;
+
+ ret = trf7970a_write(trf, TRF7970A_REG_IO_CTRL,
+ TRF7970A_REG_IO_CTRL_VRS(0x1));
+ if (ret)
+ goto out_err;
+
+ ret = trf7970a_write(trf, TRF7970A_NFC_LOW_FIELD_LEVEL,
+ TRF7970A_NFC_LOW_FIELD_LEVEL_RFDET(0x3));
+ if (ret)
+ goto out_err;
+
+ ret = trf7970a_write(trf, TRF7970A_NFC_TARGET_LEVEL,
+ TRF7970A_NFC_TARGET_LEVEL_RFDET(0x7));
+ if (ret)
+ goto out_err;
+
+ trf->ddev = ddev;
+ trf->cb = cb;
+ trf->cb_arg = arg;
+ trf->timeout = timeout;
+ trf->ignore_timeout = false;
+
+ ret = trf7970a_cmd(trf, TRF7970A_CMD_ENABLE_RX);
+ if (ret)
+ goto out_err;
+
+ trf->state = mode_detect ? TRF7970A_ST_LISTENING_MD :
+ TRF7970A_ST_LISTENING;
+
+ schedule_delayed_work(&trf->timeout_work, msecs_to_jiffies(timeout));
+
+out_err:
+ mutex_unlock(&trf->lock);
+ return ret;
+}
+
+static int trf7970a_tg_listen(struct nfc_digital_dev *ddev, u16 timeout,
+ nfc_digital_cmd_complete_t cb, void *arg)
+{
+ struct trf7970a *trf = nfc_digital_get_drvdata(ddev);
+
+ dev_dbg(trf->dev, "Listen - state: %d, timeout: %d ms\n",
+ trf->state, timeout);
+
+ return _trf7970a_tg_listen(ddev, timeout, cb, arg, false);
+}
+
+static int trf7970a_tg_listen_md(struct nfc_digital_dev *ddev,
+ u16 timeout, nfc_digital_cmd_complete_t cb, void *arg)
+{
+ struct trf7970a *trf = nfc_digital_get_drvdata(ddev);
+ int ret;
+
+ dev_dbg(trf->dev, "Listen MD - state: %d, timeout: %d ms\n",
+ trf->state, timeout);
+
+ ret = trf7970a_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_RF_TECH,
+ NFC_DIGITAL_RF_TECH_106A);
+ if (ret)
+ return ret;
+
+ ret = trf7970a_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING,
+ NFC_DIGITAL_FRAMING_NFCA_NFC_DEP);
+ if (ret)
+ return ret;
+
+ return _trf7970a_tg_listen(ddev, timeout, cb, arg, true);
+}
+
+static int trf7970a_tg_get_rf_tech(struct nfc_digital_dev *ddev, u8 *rf_tech)
+{
+ struct trf7970a *trf = nfc_digital_get_drvdata(ddev);
+
+ dev_dbg(trf->dev, "Get RF Tech - state: %d, rf_tech: %d\n",
+ trf->state, trf->md_rf_tech);
+
+ *rf_tech = trf->md_rf_tech;
+
+ return 0;
+}
+
+static void trf7970a_abort_cmd(struct nfc_digital_dev *ddev)
+{
+ struct trf7970a *trf = nfc_digital_get_drvdata(ddev);
+
+ dev_dbg(trf->dev, "Abort process initiated\n");
+
+ mutex_lock(&trf->lock);
+
+ switch (trf->state) {
+ case TRF7970A_ST_WAIT_FOR_TX_FIFO:
+ case TRF7970A_ST_WAIT_FOR_RX_DATA:
+ case TRF7970A_ST_WAIT_FOR_RX_DATA_CONT:
+ case TRF7970A_ST_WAIT_TO_ISSUE_EOF:
+ trf->aborting = true;
+ break;
+ case TRF7970A_ST_LISTENING:
+ trf->ignore_timeout = !cancel_delayed_work(&trf->timeout_work);
+ trf7970a_send_err_upstream(trf, -ECANCELED);
+ dev_dbg(trf->dev, "Abort process complete\n");
+ break;
+ default:
+ break;
+ }
+
+ mutex_unlock(&trf->lock);
+}
+
+static struct nfc_digital_ops trf7970a_nfc_ops = {
+ .in_configure_hw = trf7970a_in_configure_hw,
+ .in_send_cmd = trf7970a_send_cmd,
+ .tg_configure_hw = trf7970a_tg_configure_hw,
+ .tg_send_cmd = trf7970a_send_cmd,
+ .tg_listen = trf7970a_tg_listen,
+ .tg_listen_md = trf7970a_tg_listen_md,
+ .tg_get_rf_tech = trf7970a_tg_get_rf_tech,
+ .switch_rf = trf7970a_switch_rf,
+ .abort_cmd = trf7970a_abort_cmd,
+};
+
+static int trf7970a_power_up(struct trf7970a *trf)
+{
+ int ret;
+
+ dev_dbg(trf->dev, "Powering up - state: %d\n", trf->state);
+
+ if (trf->state != TRF7970A_ST_PWR_OFF)
+ return 0;
+
+ ret = regulator_enable(trf->regulator);
+ if (ret) {
+ dev_err(trf->dev, "%s - Can't enable VIN: %d\n", __func__, ret);
+ return ret;
+ }
+
+ usleep_range(5000, 6000);
+
+ if (!(trf->quirks & TRF7970A_QUIRK_EN2_MUST_STAY_LOW)) {
+ gpio_set_value(trf->en2_gpio, 1);
+ usleep_range(1000, 2000);
+ }
+
+ gpio_set_value(trf->en_gpio, 1);
+
+ usleep_range(20000, 21000);
+
+ trf->state = TRF7970A_ST_RF_OFF;
+
+ return 0;
+}
+
+static int trf7970a_power_down(struct trf7970a *trf)
+{
+ int ret;
+
+ dev_dbg(trf->dev, "Powering down - state: %d\n", trf->state);
+
+ if (trf->state == TRF7970A_ST_PWR_OFF)
+ return 0;
+
+ if (trf->state != TRF7970A_ST_RF_OFF) {
+ dev_dbg(trf->dev, "Can't power down - not RF_OFF state (%d)\n",
+ trf->state);
+ return -EBUSY;
+ }
+
+ gpio_set_value(trf->en_gpio, 0);
+ gpio_set_value(trf->en2_gpio, 0);
+
+ ret = regulator_disable(trf->regulator);
+ if (ret)
+ dev_err(trf->dev, "%s - Can't disable VIN: %d\n", __func__,
+ ret);
+
+ trf->state = TRF7970A_ST_PWR_OFF;
+
+ return ret;
+}
+
+static int trf7970a_startup(struct trf7970a *trf)
+{
+ int ret;
+
+ ret = trf7970a_power_up(trf);
+ if (ret)
+ return ret;
+
+ pm_runtime_set_active(trf->dev);
+ pm_runtime_enable(trf->dev);
+ pm_runtime_mark_last_busy(trf->dev);
+
+ return 0;
+}
+
+static void trf7970a_shutdown(struct trf7970a *trf)
+{
+ switch (trf->state) {
+ case TRF7970A_ST_WAIT_FOR_TX_FIFO:
+ case TRF7970A_ST_WAIT_FOR_RX_DATA:
+ case TRF7970A_ST_WAIT_FOR_RX_DATA_CONT:
+ case TRF7970A_ST_WAIT_TO_ISSUE_EOF:
+ case TRF7970A_ST_LISTENING:
+ trf7970a_send_err_upstream(trf, -ECANCELED);
+ /* FALLTHROUGH */
+ case TRF7970A_ST_IDLE:
+ case TRF7970A_ST_IDLE_RX_BLOCKED:
+ trf7970a_switch_rf_off(trf);
+ break;
+ default:
+ break;
+ }
+
+ pm_runtime_disable(trf->dev);
+ pm_runtime_set_suspended(trf->dev);
+
+ trf7970a_power_down(trf);
+}
+
+static int trf7970a_get_autosuspend_delay(struct device_node *np)
+{
+ int autosuspend_delay, ret;
+
+ ret = of_property_read_u32(np, "autosuspend-delay", &autosuspend_delay);
+ if (ret)
+ autosuspend_delay = TRF7970A_AUTOSUSPEND_DELAY;
+
+ return autosuspend_delay;
+}
+
+static int trf7970a_get_vin_voltage_override(struct device_node *np,
+ u32 *vin_uvolts)
+{
+ return of_property_read_u32(np, "vin-voltage-override", vin_uvolts);
+}
+
+static int trf7970a_probe(struct spi_device *spi)
+{
+ struct device_node *np = spi->dev.of_node;
+ struct trf7970a *trf;
+ int uvolts, autosuspend_delay, ret;
+
+ if (!np) {
+ dev_err(&spi->dev, "No Device Tree entry\n");
+ return -EINVAL;
+ }
+
+ trf = devm_kzalloc(&spi->dev, sizeof(*trf), GFP_KERNEL);
+ if (!trf)
+ return -ENOMEM;
+
+ trf->state = TRF7970A_ST_PWR_OFF;
+ trf->dev = &spi->dev;
+ trf->spi = spi;
+
+ spi->mode = SPI_MODE_1;
+ spi->bits_per_word = 8;
+
+ ret = spi_setup(spi);
+ if (ret < 0) {
+ dev_err(trf->dev, "Can't set up SPI Communication\n");
+ return ret;
+ }
+
+ if (of_property_read_bool(np, "t5t-rmb-extra-byte-quirk"))
+ trf->quirks |= TRF7970A_QUIRK_T5T_RMB_EXTRA_BYTE;
+
+ if (of_property_read_bool(np, "irq-status-read-quirk"))
+ trf->quirks |= TRF7970A_QUIRK_IRQ_STATUS_READ;
+
+ /* There are two enable pins - both must be present */
+ trf->en_gpio = of_get_named_gpio(np, "ti,enable-gpios", 0);
+ if (!gpio_is_valid(trf->en_gpio)) {
+ dev_err(trf->dev, "No EN GPIO property\n");
+ return trf->en_gpio;
+ }
+
+ ret = devm_gpio_request_one(trf->dev, trf->en_gpio,
+ GPIOF_DIR_OUT | GPIOF_INIT_LOW, "trf7970a EN");
+ if (ret) {
+ dev_err(trf->dev, "Can't request EN GPIO: %d\n", ret);
+ return ret;
+ }
+
+ trf->en2_gpio = of_get_named_gpio(np, "ti,enable-gpios", 1);
+ if (!gpio_is_valid(trf->en2_gpio)) {
+ dev_err(trf->dev, "No EN2 GPIO property\n");
+ return trf->en2_gpio;
+ }
+
+ ret = devm_gpio_request_one(trf->dev, trf->en2_gpio,
+ GPIOF_DIR_OUT | GPIOF_INIT_LOW, "trf7970a EN2");
+ if (ret) {
+ dev_err(trf->dev, "Can't request EN2 GPIO: %d\n", ret);
+ return ret;
+ }
+
+ if (of_property_read_bool(np, "en2-rf-quirk"))
+ trf->quirks |= TRF7970A_QUIRK_EN2_MUST_STAY_LOW;
+
+ ret = devm_request_threaded_irq(trf->dev, spi->irq, NULL,
+ trf7970a_irq, IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ "trf7970a", trf);
+ if (ret) {
+ dev_err(trf->dev, "Can't request IRQ#%d: %d\n", spi->irq, ret);
+ return ret;
+ }
+
+ mutex_init(&trf->lock);
+ INIT_DELAYED_WORK(&trf->timeout_work, trf7970a_timeout_work_handler);
+
+ trf->regulator = devm_regulator_get(&spi->dev, "vin");
+ if (IS_ERR(trf->regulator)) {
+ ret = PTR_ERR(trf->regulator);
+ dev_err(trf->dev, "Can't get VIN regulator: %d\n", ret);
+ goto err_destroy_lock;
+ }
+
+ ret = regulator_enable(trf->regulator);
+ if (ret) {
+ dev_err(trf->dev, "Can't enable VIN: %d\n", ret);
+ goto err_destroy_lock;
+ }
+
+ ret = trf7970a_get_vin_voltage_override(np, &uvolts);
+ if (ret)
+ uvolts = regulator_get_voltage(trf->regulator);
+
+ if (uvolts > 4000000)
+ trf->chip_status_ctrl = TRF7970A_CHIP_STATUS_VRS5_3;
+
+ trf->ddev = nfc_digital_allocate_device(&trf7970a_nfc_ops,
+ TRF7970A_SUPPORTED_PROTOCOLS,
+ NFC_DIGITAL_DRV_CAPS_IN_CRC |
+ NFC_DIGITAL_DRV_CAPS_TG_CRC, 0, 0);
+ if (!trf->ddev) {
+ dev_err(trf->dev, "Can't allocate NFC digital device\n");
+ ret = -ENOMEM;
+ goto err_disable_regulator;
+ }
+
+ nfc_digital_set_parent_dev(trf->ddev, trf->dev);
+ nfc_digital_set_drvdata(trf->ddev, trf);
+ spi_set_drvdata(spi, trf);
+
+ autosuspend_delay = trf7970a_get_autosuspend_delay(np);
+
+ pm_runtime_set_autosuspend_delay(trf->dev, autosuspend_delay);
+ pm_runtime_use_autosuspend(trf->dev);
+
+ ret = trf7970a_startup(trf);
+ if (ret)
+ goto err_free_ddev;
+
+ ret = nfc_digital_register_device(trf->ddev);
+ if (ret) {
+ dev_err(trf->dev, "Can't register NFC digital device: %d\n",
+ ret);
+ goto err_shutdown;
+ }
+
+ return 0;
+
+err_shutdown:
+ trf7970a_shutdown(trf);
+err_free_ddev:
+ nfc_digital_free_device(trf->ddev);
+err_disable_regulator:
+ regulator_disable(trf->regulator);
+err_destroy_lock:
+ mutex_destroy(&trf->lock);
+ return ret;
+}
+
+static int trf7970a_remove(struct spi_device *spi)
+{
+ struct trf7970a *trf = spi_get_drvdata(spi);
+
+ mutex_lock(&trf->lock);
+
+ trf7970a_shutdown(trf);
+
+ mutex_unlock(&trf->lock);
+
+ nfc_digital_unregister_device(trf->ddev);
+ nfc_digital_free_device(trf->ddev);
+
+ regulator_disable(trf->regulator);
+
+ mutex_destroy(&trf->lock);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int trf7970a_suspend(struct device *dev)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct trf7970a *trf = spi_get_drvdata(spi);
+
+ dev_dbg(dev, "Suspend\n");
+
+ mutex_lock(&trf->lock);
+
+ trf7970a_shutdown(trf);
+
+ mutex_unlock(&trf->lock);
+
+ return 0;
+}
+
+static int trf7970a_resume(struct device *dev)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct trf7970a *trf = spi_get_drvdata(spi);
+ int ret;
+
+ dev_dbg(dev, "Resume\n");
+
+ mutex_lock(&trf->lock);
+
+ ret = trf7970a_startup(trf);
+
+ mutex_unlock(&trf->lock);
+
+ return ret;
+}
+#endif
+
+#ifdef CONFIG_PM
+static int trf7970a_pm_runtime_suspend(struct device *dev)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct trf7970a *trf = spi_get_drvdata(spi);
+ int ret;
+
+ dev_dbg(dev, "Runtime suspend\n");
+
+ mutex_lock(&trf->lock);
+
+ ret = trf7970a_power_down(trf);
+
+ mutex_unlock(&trf->lock);
+
+ return ret;
+}
+
+static int trf7970a_pm_runtime_resume(struct device *dev)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct trf7970a *trf = spi_get_drvdata(spi);
+ int ret;
+
+ dev_dbg(dev, "Runtime resume\n");
+
+ ret = trf7970a_power_up(trf);
+ if (!ret)
+ pm_runtime_mark_last_busy(dev);
+
+ return ret;
+}
+#endif
+
+static const struct dev_pm_ops trf7970a_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(trf7970a_suspend, trf7970a_resume)
+ SET_RUNTIME_PM_OPS(trf7970a_pm_runtime_suspend,
+ trf7970a_pm_runtime_resume, NULL)
+};
+
+static const struct of_device_id trf7970a_of_match[] = {
+ { .compatible = "ti,trf7970a", },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, trf7970a_of_match);
+
+static const struct spi_device_id trf7970a_id_table[] = {
+ { "trf7970a", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, trf7970a_id_table);
+
+static struct spi_driver trf7970a_spi_driver = {
+ .probe = trf7970a_probe,
+ .remove = trf7970a_remove,
+ .id_table = trf7970a_id_table,
+ .driver = {
+ .name = "trf7970a",
+ .of_match_table = of_match_ptr(trf7970a_of_match),
+ .pm = &trf7970a_pm_ops,
+ },
+};
+
+module_spi_driver(trf7970a_spi_driver);
+
+MODULE_AUTHOR("Mark A. Greer <mgreer@animalcreek.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("TI trf7970a RFID/NFC Transceiver Driver");