Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a06347c0 authored by Robert Dolca's avatar Robert Dolca Committed by Samuel Ortiz
Browse files

NFC: Add Intel Fields Peak NFC solution driver



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

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

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

Signed-off-by: default avatarRobert Dolca <robert.dolca@intel.com>
Signed-off-by: default avatarSamuel Ortiz <sameo@linux.intel.com>
parent 85b9ce9a
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -68,6 +68,7 @@ config NFC_PORT100

	  If unsure, say N.

source "drivers/nfc/fdp/Kconfig"
source "drivers/nfc/pn544/Kconfig"
source "drivers/nfc/microread/Kconfig"
source "drivers/nfc/nfcmrvl/Kconfig"
+1 −0
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@
# Makefile for nfc devices
#

obj-$(CONFIG_NFC_FDP)		+= fdp/
obj-$(CONFIG_NFC_PN544)		+= pn544/
obj-$(CONFIG_NFC_MICROREAD)	+= microread/
obj-$(CONFIG_NFC_PN533)		+= pn533.o
+23 −0
Original line number Diff line number Diff line
config NFC_FDP
	tristate "Intel FDP NFC driver"
	depends on NFC_NCI
	select CRC_CCITT
	default n
	---help---
	  Intel Fields Peak NFC controller core driver.
	  This is a driver based on the NCI NFC kernel layers.

	  To compile this driver as a module, choose m here. The module will
	  be called fdp.
	  Say N if unsure.

config NFC_FDP_I2C
	tristate "NFC FDP i2c support"
	depends on NFC_FDP && I2C
	---help---
	  This module adds support for the Intel Fields Peak NFC controller
	  i2c interface.
	  Select this if your platform is using the i2c bus.

	  If you choose to build a module, it'll be called fdp_i2c.
	  Say N if unsure.
+9 −0
Original line number Diff line number Diff line
#
# Makefile for FDP NCI based NFC driver
#

obj-$(CONFIG_NFC_FDP)     += fdp.o
obj-$(CONFIG_NFC_FDP_I2C) += fdp_i2c.o

fdp_i2c-objs  = i2c.o

drivers/nfc/fdp/fdp.c

0 → 100644
+817 −0
Original line number Diff line number Diff line
/* -------------------------------------------------------------------------
 * 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_id(ndev, 0);
}

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->otp_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;
	u8 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;
	u8 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>");
Loading