root/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 <shikha.singh@st.com>");
MODULE_DESCRIPTION("ST NFC Transceiver ST95HF driver");
MODULE_LICENSE("GPL v2");