linux/drivers/nfc/st95hf/core.c

// SPDX-License-Identifier: GPL-2.0-only
/*
 * --------------------------------------------------------------------
 * Driver for ST NFC Transceiver ST95HF
 * --------------------------------------------------------------------
 * Copyright (C) 2015 STMicroelectronics Pvt. Ltd. All rights reserved.
 */

#include <linux/err.h>
#include <linux/gpio/consumer.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.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_gpiod: 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;
	struct gpio_desc *enable_gpiod;
	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 receive 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 */
	gpiod_set_value(st95con->enable_gpiod, HIGH);

	/* wait for 1 milisecond */
	usleep_range(1000, 2000);

	/* Make irq_in pin low */
	gpiod_set_value(st95con->enable_gpiod, LOW);

	/* wait for minimum interrupt pulse to make st95 active */
	usleep_range(1000, 2000);

	/* At end make it high */
	gpiod_set_value(st95con->enable_gpiod, 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 *spidevice;
	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;
	}

	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)
		return -ENOMEM;

	switch (stcontext->current_rf_tech) {
	case NFC_DIGITAL_RF_TECH_106A:
		len_data_to_tag = skb->len + 1;
		skb_put_u8(skb, 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");
		goto free_skb_resp;
	}

	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);
	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 const 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 const struct of_device_id st95hf_spi_of_match[] __maybe_unused = {
	{ .compatible = "st,st95hf" },
	{},
};
MODULE_DEVICE_TABLE(of, st95hf_spi_of_match);

static int st95hf_probe(struct spi_device *nfc_spi_dev)
{
	struct device *dev = &nfc_spi_dev->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_gpiod = devm_gpiod_get(dev, "enable", GPIOD_OUT_HIGH);
	if (IS_ERR(st95context->enable_gpiod)) {
		ret = PTR_ERR(st95context->enable_gpiod);
		dev_err(&nfc_spi_dev->dev, "No valid enable gpio\n");
		goto err_disable_regulator;
	}

	ret = gpiod_set_consumer_name(st95context->enable_gpiod, "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 void 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);

	/* 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);
}

/* Register as SPI protocol driver */
static struct spi_driver st95hf_driver = {
	.driver = {
		.name = "st95hf",
		.of_match_table = of_match_ptr(st95hf_spi_of_match),
	},
	.id_table = st95hf_id,
	.probe = st95hf_probe,
	.remove = st95hf_remove,
};

module_spi_driver(st95hf_driver);

MODULE_AUTHOR("Shikha Singh <[email protected]>");
MODULE_DESCRIPTION("ST NFC Transceiver ST95HF driver");
MODULE_LICENSE("GPL v2");