| /* | |
| * -------------------------------------------------------------------- | |
| * Driver for ST NFC Transceiver ST95HF | |
| * -------------------------------------------------------------------- | |
| * Copyright (C) 2015 STMicroelectronics Pvt. Ltd. All rights reserved. | |
| * | |
| * This program is free software; you can redistribute it and/or modify it | |
| * under the terms and conditions of the GNU General Public License, | |
| * version 2, as published by the Free Software Foundation. | |
| * | |
| * This program is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| * GNU General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU General Public License | |
| * along with this program; if not, see <http://www.gnu.org/licenses/>. | |
| */ | |
| #include <linux/err.h> | |
| #include <linux/gpio.h> | |
| #include <linux/init.h> | |
| #include <linux/interrupt.h> | |
| #include <linux/irq.h> | |
| #include <linux/module.h> | |
| #include <linux/netdevice.h> | |
| #include <linux/nfc.h> | |
| #include <linux/of_gpio.h> | |
| #include <linux/of.h> | |
| #include <linux/of_irq.h> | |
| #include <linux/property.h> | |
| #include <linux/regulator/consumer.h> | |
| #include <linux/wait.h> | |
| #include <net/nfc/digital.h> | |
| #include <net/nfc/nfc.h> | |
| #include "spi.h" | |
| /* supported protocols */ | |
| #define ST95HF_SUPPORTED_PROT (NFC_PROTO_ISO14443_MASK | \ | |
| NFC_PROTO_ISO14443_B_MASK | \ | |
| NFC_PROTO_ISO15693_MASK) | |
| /* driver capabilities */ | |
| #define ST95HF_CAPABILITIES NFC_DIGITAL_DRV_CAPS_IN_CRC | |
| /* Command Send Interface */ | |
| /* ST95HF_COMMAND_SEND CMD Ids */ | |
| #define ECHO_CMD 0x55 | |
| #define WRITE_REGISTER_CMD 0x9 | |
| #define PROTOCOL_SELECT_CMD 0x2 | |
| #define SEND_RECEIVE_CMD 0x4 | |
| /* Select protocol codes */ | |
| #define ISO15693_PROTOCOL_CODE 0x1 | |
| #define ISO14443A_PROTOCOL_CODE 0x2 | |
| #define ISO14443B_PROTOCOL_CODE 0x3 | |
| /* | |
| * head room len is 3 | |
| * 1 byte for control byte | |
| * 1 byte for cmd | |
| * 1 byte for size | |
| */ | |
| #define ST95HF_HEADROOM_LEN 3 | |
| /* | |
| * tailroom is 1 for ISO14443A | |
| * and 0 for ISO14443B/ISO15693, | |
| * hence the max value 1 should be | |
| * taken. | |
| */ | |
| #define ST95HF_TAILROOM_LEN 1 | |
| /* Command Response interface */ | |
| #define MAX_RESPONSE_BUFFER_SIZE 280 | |
| #define ECHORESPONSE 0x55 | |
| #define ST95HF_ERR_MASK 0xF | |
| #define ST95HF_TIMEOUT_ERROR 0x87 | |
| #define ST95HF_NFCA_CRC_ERR_MASK 0x20 | |
| #define ST95HF_NFCB_CRC_ERR_MASK 0x01 | |
| /* ST95HF transmission flag values */ | |
| #define TRFLAG_NFCA_SHORT_FRAME 0x07 | |
| #define TRFLAG_NFCA_STD_FRAME 0x08 | |
| #define TRFLAG_NFCA_STD_FRAME_CRC 0x28 | |
| /* Misc defs */ | |
| #define HIGH 1 | |
| #define LOW 0 | |
| #define ISO14443A_RATS_REQ 0xE0 | |
| #define RATS_TB1_PRESENT_MASK 0x20 | |
| #define RATS_TA1_PRESENT_MASK 0x10 | |
| #define TB1_FWI_MASK 0xF0 | |
| #define WTX_REQ_FROM_TAG 0xF2 | |
| #define MAX_CMD_LEN 0x7 | |
| #define MAX_CMD_PARAMS 4 | |
| struct cmd { | |
| int cmd_len; | |
| unsigned char cmd_id; | |
| unsigned char no_cmd_params; | |
| unsigned char cmd_params[MAX_CMD_PARAMS]; | |
| enum req_type req; | |
| }; | |
| struct param_list { | |
| int param_offset; | |
| int new_param_val; | |
| }; | |
| /* | |
| * List of top-level cmds to be used internally by the driver. | |
| * All these commands are build on top of ST95HF basic commands | |
| * such as SEND_RECEIVE_CMD, PROTOCOL_SELECT_CMD, etc. | |
| * These top level cmds are used internally while implementing various ops of | |
| * digital layer/driver probe or extending the digital framework layer for | |
| * features that are not yet implemented there, for example, WTX cmd handling. | |
| */ | |
| enum st95hf_cmd_list { | |
| CMD_ECHO, | |
| CMD_ISO14443A_CONFIG, | |
| CMD_ISO14443A_DEMOGAIN, | |
| CMD_ISO14443B_DEMOGAIN, | |
| CMD_ISO14443A_PROTOCOL_SELECT, | |
| CMD_ISO14443B_PROTOCOL_SELECT, | |
| CMD_WTX_RESPONSE, | |
| CMD_FIELD_OFF, | |
| CMD_ISO15693_PROTOCOL_SELECT, | |
| }; | |
| static const struct cmd cmd_array[] = { | |
| [CMD_ECHO] = { | |
| .cmd_len = 0x2, | |
| .cmd_id = ECHO_CMD, | |
| .no_cmd_params = 0, | |
| .req = SYNC, | |
| }, | |
| [CMD_ISO14443A_CONFIG] = { | |
| .cmd_len = 0x7, | |
| .cmd_id = WRITE_REGISTER_CMD, | |
| .no_cmd_params = 0x4, | |
| .cmd_params = {0x3A, 0x00, 0x5A, 0x04}, | |
| .req = SYNC, | |
| }, | |
| [CMD_ISO14443A_DEMOGAIN] = { | |
| .cmd_len = 0x7, | |
| .cmd_id = WRITE_REGISTER_CMD, | |
| .no_cmd_params = 0x4, | |
| .cmd_params = {0x68, 0x01, 0x01, 0xDF}, | |
| .req = SYNC, | |
| }, | |
| [CMD_ISO14443B_DEMOGAIN] = { | |
| .cmd_len = 0x7, | |
| .cmd_id = WRITE_REGISTER_CMD, | |
| .no_cmd_params = 0x4, | |
| .cmd_params = {0x68, 0x01, 0x01, 0x51}, | |
| .req = SYNC, | |
| }, | |
| [CMD_ISO14443A_PROTOCOL_SELECT] = { | |
| .cmd_len = 0x7, | |
| .cmd_id = PROTOCOL_SELECT_CMD, | |
| .no_cmd_params = 0x4, | |
| .cmd_params = {ISO14443A_PROTOCOL_CODE, 0x00, 0x01, 0xA0}, | |
| .req = SYNC, | |
| }, | |
| [CMD_ISO14443B_PROTOCOL_SELECT] = { | |
| .cmd_len = 0x7, | |
| .cmd_id = PROTOCOL_SELECT_CMD, | |
| .no_cmd_params = 0x4, | |
| .cmd_params = {ISO14443B_PROTOCOL_CODE, 0x01, 0x03, 0xFF}, | |
| .req = SYNC, | |
| }, | |
| [CMD_WTX_RESPONSE] = { | |
| .cmd_len = 0x6, | |
| .cmd_id = SEND_RECEIVE_CMD, | |
| .no_cmd_params = 0x3, | |
| .cmd_params = {0xF2, 0x00, TRFLAG_NFCA_STD_FRAME_CRC}, | |
| .req = ASYNC, | |
| }, | |
| [CMD_FIELD_OFF] = { | |
| .cmd_len = 0x5, | |
| .cmd_id = PROTOCOL_SELECT_CMD, | |
| .no_cmd_params = 0x2, | |
| .cmd_params = {0x0, 0x0}, | |
| .req = SYNC, | |
| }, | |
| [CMD_ISO15693_PROTOCOL_SELECT] = { | |
| .cmd_len = 0x5, | |
| .cmd_id = PROTOCOL_SELECT_CMD, | |
| .no_cmd_params = 0x2, | |
| .cmd_params = {ISO15693_PROTOCOL_CODE, 0x0D}, | |
| .req = SYNC, | |
| }, | |
| }; | |
| /* st95_digital_cmd_complete_arg stores client context */ | |
| struct st95_digital_cmd_complete_arg { | |
| struct sk_buff *skb_resp; | |
| nfc_digital_cmd_complete_t complete_cb; | |
| void *cb_usrarg; | |
| bool rats; | |
| }; | |
| /* | |
| * structure containing ST95HF driver specific data. | |
| * @spicontext: structure containing information required | |
| * for spi communication between st95hf and host. | |
| * @ddev: nfc digital device object. | |
| * @nfcdev: nfc device object. | |
| * @enable_gpio: gpio used to enable st95hf transceiver. | |
| * @complete_cb_arg: structure to store various context information | |
| * that is passed from nfc requesting thread to the threaded ISR. | |
| * @st95hf_supply: regulator "consumer" for NFC device. | |
| * @sendrcv_trflag: last byte of frame send by sendrecv command | |
| * of st95hf. This byte contains transmission flag info. | |
| * @exchange_lock: semaphore used for signaling the st95hf_remove | |
| * function that the last outstanding async nfc request is finished. | |
| * @rm_lock: mutex for ensuring safe access of nfc digital object | |
| * from threaded ISR. Usage of this mutex avoids any race between | |
| * deletion of the object from st95hf_remove() and its access from | |
| * the threaded ISR. | |
| * @nfcdev_free: flag to have the state of nfc device object. | |
| * [alive | died] | |
| * @current_protocol: current nfc protocol. | |
| * @current_rf_tech: current rf technology. | |
| * @fwi: frame waiting index, received in reply of RATS according to | |
| * digital protocol. | |
| */ | |
| struct st95hf_context { | |
| struct st95hf_spi_context spicontext; | |
| struct nfc_digital_dev *ddev; | |
| struct nfc_dev *nfcdev; | |
| unsigned int enable_gpio; | |
| struct st95_digital_cmd_complete_arg complete_cb_arg; | |
| struct regulator *st95hf_supply; | |
| unsigned char sendrcv_trflag; | |
| struct semaphore exchange_lock; | |
| struct mutex rm_lock; | |
| bool nfcdev_free; | |
| u8 current_protocol; | |
| u8 current_rf_tech; | |
| int fwi; | |
| }; | |
| /* | |
| * st95hf_send_recv_cmd() is for sending commands to ST95HF | |
| * that are described in the cmd_array[]. It can optionally | |
| * receive the response if the cmd request is of type | |
| * SYNC. For that to happen caller must pass true to recv_res. | |
| * For ASYNC request, recv_res is ignored and the | |
| * function will never try to receive the response on behalf | |
| * of the caller. | |
| */ | |
| static int st95hf_send_recv_cmd(struct st95hf_context *st95context, | |
| enum st95hf_cmd_list cmd, | |
| int no_modif, | |
| struct param_list *list_array, | |
| bool recv_res) | |
| { | |
| unsigned char spi_cmd_buffer[MAX_CMD_LEN]; | |
| int i, ret; | |
| struct device *dev = &st95context->spicontext.spidev->dev; | |
| if (cmd_array[cmd].cmd_len > MAX_CMD_LEN) | |
| return -EINVAL; | |
| if (cmd_array[cmd].no_cmd_params < no_modif) | |
| return -EINVAL; | |
| if (no_modif && !list_array) | |
| return -EINVAL; | |
| spi_cmd_buffer[0] = ST95HF_COMMAND_SEND; | |
| spi_cmd_buffer[1] = cmd_array[cmd].cmd_id; | |
| spi_cmd_buffer[2] = cmd_array[cmd].no_cmd_params; | |
| memcpy(&spi_cmd_buffer[3], cmd_array[cmd].cmd_params, | |
| spi_cmd_buffer[2]); | |
| for (i = 0; i < no_modif; i++) { | |
| if (list_array[i].param_offset >= cmd_array[cmd].no_cmd_params) | |
| return -EINVAL; | |
| spi_cmd_buffer[3 + list_array[i].param_offset] = | |
| list_array[i].new_param_val; | |
| } | |
| ret = st95hf_spi_send(&st95context->spicontext, | |
| spi_cmd_buffer, | |
| cmd_array[cmd].cmd_len, | |
| cmd_array[cmd].req); | |
| if (ret) { | |
| dev_err(dev, "st95hf_spi_send failed with error %d\n", ret); | |
| return ret; | |
| } | |
| if (cmd_array[cmd].req == SYNC && recv_res) { | |
| unsigned char st95hf_response_arr[2]; | |
| ret = st95hf_spi_recv_response(&st95context->spicontext, | |
| st95hf_response_arr); | |
| if (ret < 0) { | |
| dev_err(dev, "spi error from st95hf_spi_recv_response(), err = 0x%x\n", | |
| ret); | |
| return ret; | |
| } | |
| if (st95hf_response_arr[0]) { | |
| dev_err(dev, "st95hf error from st95hf_spi_recv_response(), err = 0x%x\n", | |
| st95hf_response_arr[0]); | |
| return -EIO; | |
| } | |
| } | |
| return 0; | |
| } | |
| static int st95hf_echo_command(struct st95hf_context *st95context) | |
| { | |
| int result = 0; | |
| unsigned char echo_response; | |
| result = st95hf_send_recv_cmd(st95context, CMD_ECHO, 0, NULL, false); | |
| if (result) | |
| return result; | |
| /* If control reached here, response can be taken */ | |
| result = st95hf_spi_recv_echo_res(&st95context->spicontext, | |
| &echo_response); | |
| if (result) { | |
| dev_err(&st95context->spicontext.spidev->dev, | |
| "err: echo response receieve error = 0x%x\n", result); | |
| return result; | |
| } | |
| if (echo_response == ECHORESPONSE) | |
| return 0; | |
| dev_err(&st95context->spicontext.spidev->dev, "err: echo res is 0x%x\n", | |
| echo_response); | |
| return -EIO; | |
| } | |
| static int secondary_configuration_type4a(struct st95hf_context *stcontext) | |
| { | |
| int result = 0; | |
| struct device *dev = &stcontext->nfcdev->dev; | |
| /* 14443A config setting after select protocol */ | |
| result = st95hf_send_recv_cmd(stcontext, | |
| CMD_ISO14443A_CONFIG, | |
| 0, | |
| NULL, | |
| true); | |
| if (result) { | |
| dev_err(dev, "type a config cmd, err = 0x%x\n", result); | |
| return result; | |
| } | |
| /* 14443A demo gain setting */ | |
| result = st95hf_send_recv_cmd(stcontext, | |
| CMD_ISO14443A_DEMOGAIN, | |
| 0, | |
| NULL, | |
| true); | |
| if (result) | |
| dev_err(dev, "type a demogain cmd, err = 0x%x\n", result); | |
| return result; | |
| } | |
| static int secondary_configuration_type4b(struct st95hf_context *stcontext) | |
| { | |
| int result = 0; | |
| struct device *dev = &stcontext->nfcdev->dev; | |
| result = st95hf_send_recv_cmd(stcontext, | |
| CMD_ISO14443B_DEMOGAIN, | |
| 0, | |
| NULL, | |
| true); | |
| if (result) | |
| dev_err(dev, "type b demogain cmd, err = 0x%x\n", result); | |
| return result; | |
| } | |
| static int st95hf_select_protocol(struct st95hf_context *stcontext, int type) | |
| { | |
| int result = 0; | |
| struct device *dev; | |
| dev = &stcontext->nfcdev->dev; | |
| switch (type) { | |
| case NFC_DIGITAL_RF_TECH_106A: | |
| stcontext->current_rf_tech = NFC_DIGITAL_RF_TECH_106A; | |
| result = st95hf_send_recv_cmd(stcontext, | |
| CMD_ISO14443A_PROTOCOL_SELECT, | |
| 0, | |
| NULL, | |
| true); | |
| if (result) { | |
| dev_err(dev, "protocol sel, err = 0x%x\n", | |
| result); | |
| return result; | |
| } | |
| /* secondary config. for 14443Type 4A after protocol select */ | |
| result = secondary_configuration_type4a(stcontext); | |
| if (result) { | |
| dev_err(dev, "type a secondary config, err = 0x%x\n", | |
| result); | |
| return result; | |
| } | |
| break; | |
| case NFC_DIGITAL_RF_TECH_106B: | |
| stcontext->current_rf_tech = NFC_DIGITAL_RF_TECH_106B; | |
| result = st95hf_send_recv_cmd(stcontext, | |
| CMD_ISO14443B_PROTOCOL_SELECT, | |
| 0, | |
| NULL, | |
| true); | |
| if (result) { | |
| dev_err(dev, "protocol sel send, err = 0x%x\n", | |
| result); | |
| return result; | |
| } | |
| /* | |
| * delay of 5-6 ms is required after select protocol | |
| * command in case of ISO14443 Type B | |
| */ | |
| usleep_range(50000, 60000); | |
| /* secondary config. for 14443Type 4B after protocol select */ | |
| result = secondary_configuration_type4b(stcontext); | |
| if (result) { | |
| dev_err(dev, "type b secondary config, err = 0x%x\n", | |
| result); | |
| return result; | |
| } | |
| break; | |
| case NFC_DIGITAL_RF_TECH_ISO15693: | |
| stcontext->current_rf_tech = NFC_DIGITAL_RF_TECH_ISO15693; | |
| result = st95hf_send_recv_cmd(stcontext, | |
| CMD_ISO15693_PROTOCOL_SELECT, | |
| 0, | |
| NULL, | |
| true); | |
| if (result) { | |
| dev_err(dev, "protocol sel send, err = 0x%x\n", | |
| result); | |
| return result; | |
| } | |
| break; | |
| default: | |
| return -EINVAL; | |
| } | |
| return 0; | |
| } | |
| static void st95hf_send_st95enable_negativepulse(struct st95hf_context *st95con) | |
| { | |
| /* First make irq_in pin high */ | |
| gpio_set_value(st95con->enable_gpio, HIGH); | |
| /* wait for 1 milisecond */ | |
| usleep_range(1000, 2000); | |
| /* Make irq_in pin low */ | |
| gpio_set_value(st95con->enable_gpio, LOW); | |
| /* wait for minimum interrupt pulse to make st95 active */ | |
| usleep_range(1000, 2000); | |
| /* At end make it high */ | |
| gpio_set_value(st95con->enable_gpio, HIGH); | |
| } | |
| /* | |
| * Send a reset sequence over SPI bus (Reset command + wait 3ms + | |
| * negative pulse on st95hf enable gpio | |
| */ | |
| static int st95hf_send_spi_reset_sequence(struct st95hf_context *st95context) | |
| { | |
| int result = 0; | |
| unsigned char reset_cmd = ST95HF_COMMAND_RESET; | |
| result = st95hf_spi_send(&st95context->spicontext, | |
| &reset_cmd, | |
| ST95HF_RESET_CMD_LEN, | |
| ASYNC); | |
| if (result) { | |
| dev_err(&st95context->spicontext.spidev->dev, | |
| "spi reset sequence cmd error = %d", result); | |
| return result; | |
| } | |
| /* wait for 3 milisecond to complete the controller reset process */ | |
| usleep_range(3000, 4000); | |
| /* send negative pulse to make st95hf active */ | |
| st95hf_send_st95enable_negativepulse(st95context); | |
| /* wait for 10 milisecond : HFO setup time */ | |
| usleep_range(10000, 20000); | |
| return result; | |
| } | |
| static int st95hf_por_sequence(struct st95hf_context *st95context) | |
| { | |
| int nth_attempt = 1; | |
| int result; | |
| st95hf_send_st95enable_negativepulse(st95context); | |
| usleep_range(5000, 6000); | |
| do { | |
| /* send an ECHO command and checks ST95HF response */ | |
| result = st95hf_echo_command(st95context); | |
| dev_dbg(&st95context->spicontext.spidev->dev, | |
| "response from echo function = 0x%x, attempt = %d\n", | |
| result, nth_attempt); | |
| if (!result) | |
| return 0; | |
| /* send an pulse on IRQ in case of the chip is on sleep state */ | |
| if (nth_attempt == 2) | |
| st95hf_send_st95enable_negativepulse(st95context); | |
| else | |
| st95hf_send_spi_reset_sequence(st95context); | |
| /* delay of 50 milisecond */ | |
| usleep_range(50000, 51000); | |
| } while (nth_attempt++ < 3); | |
| return -ETIMEDOUT; | |
| } | |
| static int iso14443_config_fdt(struct st95hf_context *st95context, int wtxm) | |
| { | |
| int result = 0; | |
| struct device *dev = &st95context->spicontext.spidev->dev; | |
| struct nfc_digital_dev *nfcddev = st95context->ddev; | |
| unsigned char pp_typeb; | |
| struct param_list new_params[2]; | |
| pp_typeb = cmd_array[CMD_ISO14443B_PROTOCOL_SELECT].cmd_params[2]; | |
| if (nfcddev->curr_protocol == NFC_PROTO_ISO14443 && | |
| st95context->fwi < 4) | |
| st95context->fwi = 4; | |
| new_params[0].param_offset = 2; | |
| if (nfcddev->curr_protocol == NFC_PROTO_ISO14443) | |
| new_params[0].new_param_val = st95context->fwi; | |
| else if (nfcddev->curr_protocol == NFC_PROTO_ISO14443_B) | |
| new_params[0].new_param_val = pp_typeb; | |
| new_params[1].param_offset = 3; | |
| new_params[1].new_param_val = wtxm; | |
| switch (nfcddev->curr_protocol) { | |
| case NFC_PROTO_ISO14443: | |
| result = st95hf_send_recv_cmd(st95context, | |
| CMD_ISO14443A_PROTOCOL_SELECT, | |
| 2, | |
| new_params, | |
| true); | |
| if (result) { | |
| dev_err(dev, "WTX type a sel proto, err = 0x%x\n", | |
| result); | |
| return result; | |
| } | |
| /* secondary config. for 14443Type 4A after protocol select */ | |
| result = secondary_configuration_type4a(st95context); | |
| if (result) { | |
| dev_err(dev, "WTX type a second. config, err = 0x%x\n", | |
| result); | |
| return result; | |
| } | |
| break; | |
| case NFC_PROTO_ISO14443_B: | |
| result = st95hf_send_recv_cmd(st95context, | |
| CMD_ISO14443B_PROTOCOL_SELECT, | |
| 2, | |
| new_params, | |
| true); | |
| if (result) { | |
| dev_err(dev, "WTX type b sel proto, err = 0x%x\n", | |
| result); | |
| return result; | |
| } | |
| /* secondary config. for 14443Type 4B after protocol select */ | |
| result = secondary_configuration_type4b(st95context); | |
| if (result) { | |
| dev_err(dev, "WTX type b second. config, err = 0x%x\n", | |
| result); | |
| return result; | |
| } | |
| break; | |
| default: | |
| return -EINVAL; | |
| } | |
| return 0; | |
| } | |
| static int st95hf_handle_wtx(struct st95hf_context *stcontext, | |
| bool new_wtx, | |
| int wtx_val) | |
| { | |
| int result = 0; | |
| unsigned char val_mm = 0; | |
| struct param_list new_params[1]; | |
| struct nfc_digital_dev *nfcddev = stcontext->ddev; | |
| struct device *dev = &stcontext->nfcdev->dev; | |
| if (new_wtx) { | |
| result = iso14443_config_fdt(stcontext, wtx_val & 0x3f); | |
| if (result) { | |
| dev_err(dev, "Config. setting error on WTX req, err = 0x%x\n", | |
| result); | |
| return result; | |
| } | |
| /* Send response of wtx with ASYNC as no response expected */ | |
| new_params[0].param_offset = 1; | |
| new_params[0].new_param_val = wtx_val; | |
| result = st95hf_send_recv_cmd(stcontext, | |
| CMD_WTX_RESPONSE, | |
| 1, | |
| new_params, | |
| false); | |
| if (result) | |
| dev_err(dev, "WTX response send, err = 0x%x\n", result); | |
| return result; | |
| } | |
| /* if no new wtx, cofigure with default values */ | |
| if (nfcddev->curr_protocol == NFC_PROTO_ISO14443) | |
| val_mm = cmd_array[CMD_ISO14443A_PROTOCOL_SELECT].cmd_params[3]; | |
| else if (nfcddev->curr_protocol == NFC_PROTO_ISO14443_B) | |
| val_mm = cmd_array[CMD_ISO14443B_PROTOCOL_SELECT].cmd_params[3]; | |
| result = iso14443_config_fdt(stcontext, val_mm); | |
| if (result) | |
| dev_err(dev, "Default config. setting error after WTX processing, err = 0x%x\n", | |
| result); | |
| return result; | |
| } | |
| static int st95hf_error_handling(struct st95hf_context *stcontext, | |
| struct sk_buff *skb_resp, | |
| int res_len) | |
| { | |
| int result = 0; | |
| unsigned char error_byte; | |
| struct device *dev = &stcontext->nfcdev->dev; | |
| /* First check ST95HF specific error */ | |
| if (skb_resp->data[0] & ST95HF_ERR_MASK) { | |
| if (skb_resp->data[0] == ST95HF_TIMEOUT_ERROR) | |
| result = -ETIMEDOUT; | |
| else | |
| result = -EIO; | |
| return result; | |
| } | |
| /* Check for CRC err only if CRC is present in the tag response */ | |
| switch (stcontext->current_rf_tech) { | |
| case NFC_DIGITAL_RF_TECH_106A: | |
| if (stcontext->sendrcv_trflag == TRFLAG_NFCA_STD_FRAME_CRC) { | |
| error_byte = skb_resp->data[res_len - 3]; | |
| if (error_byte & ST95HF_NFCA_CRC_ERR_MASK) { | |
| /* CRC error occurred */ | |
| dev_err(dev, "CRC error, byte received = 0x%x\n", | |
| error_byte); | |
| result = -EIO; | |
| } | |
| } | |
| break; | |
| case NFC_DIGITAL_RF_TECH_106B: | |
| case NFC_DIGITAL_RF_TECH_ISO15693: | |
| error_byte = skb_resp->data[res_len - 1]; | |
| if (error_byte & ST95HF_NFCB_CRC_ERR_MASK) { | |
| /* CRC error occurred */ | |
| dev_err(dev, "CRC error, byte received = 0x%x\n", | |
| error_byte); | |
| result = -EIO; | |
| } | |
| break; | |
| } | |
| return result; | |
| } | |
| static int st95hf_response_handler(struct st95hf_context *stcontext, | |
| struct sk_buff *skb_resp, | |
| int res_len) | |
| { | |
| int result = 0; | |
| int skb_len; | |
| unsigned char val_mm; | |
| struct nfc_digital_dev *nfcddev = stcontext->ddev; | |
| struct device *dev = &stcontext->nfcdev->dev; | |
| struct st95_digital_cmd_complete_arg *cb_arg; | |
| cb_arg = &stcontext->complete_cb_arg; | |
| /* Process the response */ | |
| skb_put(skb_resp, res_len); | |
| /* Remove st95 header */ | |
| skb_pull(skb_resp, 2); | |
| skb_len = skb_resp->len; | |
| /* check if it is case of RATS request reply & FWI is present */ | |
| if (nfcddev->curr_protocol == NFC_PROTO_ISO14443 && cb_arg->rats && | |
| (skb_resp->data[1] & RATS_TB1_PRESENT_MASK)) { | |
| if (skb_resp->data[1] & RATS_TA1_PRESENT_MASK) | |
| stcontext->fwi = | |
| (skb_resp->data[3] & TB1_FWI_MASK) >> 4; | |
| else | |
| stcontext->fwi = | |
| (skb_resp->data[2] & TB1_FWI_MASK) >> 4; | |
| val_mm = cmd_array[CMD_ISO14443A_PROTOCOL_SELECT].cmd_params[3]; | |
| result = iso14443_config_fdt(stcontext, val_mm); | |
| if (result) { | |
| dev_err(dev, "error in config_fdt to handle fwi of ATS, error=%d\n", | |
| result); | |
| return result; | |
| } | |
| } | |
| cb_arg->rats = false; | |
| /* Remove CRC bytes only if received frames data has an eod (CRC) */ | |
| switch (stcontext->current_rf_tech) { | |
| case NFC_DIGITAL_RF_TECH_106A: | |
| if (stcontext->sendrcv_trflag == TRFLAG_NFCA_STD_FRAME_CRC) | |
| skb_trim(skb_resp, (skb_len - 5)); | |
| else | |
| skb_trim(skb_resp, (skb_len - 3)); | |
| break; | |
| case NFC_DIGITAL_RF_TECH_106B: | |
| case NFC_DIGITAL_RF_TECH_ISO15693: | |
| skb_trim(skb_resp, (skb_len - 3)); | |
| break; | |
| } | |
| return result; | |
| } | |
| static irqreturn_t st95hf_irq_handler(int irq, void *st95hfcontext) | |
| { | |
| struct st95hf_context *stcontext = | |
| (struct st95hf_context *)st95hfcontext; | |
| if (stcontext->spicontext.req_issync) { | |
| complete(&stcontext->spicontext.done); | |
| stcontext->spicontext.req_issync = false; | |
| return IRQ_HANDLED; | |
| } | |
| return IRQ_WAKE_THREAD; | |
| } | |
| static irqreturn_t st95hf_irq_thread_handler(int irq, void *st95hfcontext) | |
| { | |
| int result = 0; | |
| int res_len; | |
| static bool wtx; | |
| struct device *dev; | |
| struct device *spidevice; | |
| struct nfc_digital_dev *nfcddev; | |
| struct sk_buff *skb_resp; | |
| struct st95hf_context *stcontext = | |
| (struct st95hf_context *)st95hfcontext; | |
| struct st95_digital_cmd_complete_arg *cb_arg; | |
| spidevice = &stcontext->spicontext.spidev->dev; | |
| /* | |
| * check semaphore, if not down() already, then we don't | |
| * know in which context the ISR is called and surely it | |
| * will be a bug. Note that down() of the semaphore is done | |
| * in the corresponding st95hf_in_send_cmd() and then | |
| * only this ISR should be called. ISR will up() the | |
| * semaphore before leaving. Hence when the ISR is called | |
| * the correct behaviour is down_trylock() should always | |
| * return 1 (indicating semaphore cant be taken and hence no | |
| * change in semaphore count). | |
| * If not, then we up() the semaphore and crash on | |
| * a BUG() ! | |
| */ | |
| if (!down_trylock(&stcontext->exchange_lock)) { | |
| up(&stcontext->exchange_lock); | |
| WARN(1, "unknown context in ST95HF ISR"); | |
| return IRQ_NONE; | |
| } | |
| cb_arg = &stcontext->complete_cb_arg; | |
| skb_resp = cb_arg->skb_resp; | |
| mutex_lock(&stcontext->rm_lock); | |
| res_len = st95hf_spi_recv_response(&stcontext->spicontext, | |
| skb_resp->data); | |
| if (res_len < 0) { | |
| dev_err(spidevice, "TISR spi response err = 0x%x\n", res_len); | |
| result = res_len; | |
| goto end; | |
| } | |
| /* if stcontext->nfcdev_free is true, it means remove already ran */ | |
| if (stcontext->nfcdev_free) { | |
| result = -ENODEV; | |
| goto end; | |
| } | |
| dev = &stcontext->nfcdev->dev; | |
| nfcddev = stcontext->ddev; | |
| if (skb_resp->data[2] == WTX_REQ_FROM_TAG) { | |
| /* Request for new FWT from tag */ | |
| result = st95hf_handle_wtx(stcontext, true, skb_resp->data[3]); | |
| if (result) | |
| goto end; | |
| wtx = true; | |
| mutex_unlock(&stcontext->rm_lock); | |
| return IRQ_HANDLED; | |
| } | |
| result = st95hf_error_handling(stcontext, skb_resp, res_len); | |
| if (result) | |
| goto end; | |
| result = st95hf_response_handler(stcontext, skb_resp, res_len); | |
| if (result) | |
| goto end; | |
| /* | |
| * If select protocol is done on wtx req. do select protocol | |
| * again with default values | |
| */ | |
| if (wtx) { | |
| wtx = false; | |
| result = st95hf_handle_wtx(stcontext, false, 0); | |
| if (result) | |
| goto end; | |
| } | |
| /* call digital layer callback */ | |
| cb_arg->complete_cb(stcontext->ddev, cb_arg->cb_usrarg, skb_resp); | |
| /* up the semaphore before returning */ | |
| up(&stcontext->exchange_lock); | |
| mutex_unlock(&stcontext->rm_lock); | |
| return IRQ_HANDLED; | |
| end: | |
| kfree_skb(skb_resp); | |
| wtx = false; | |
| cb_arg->rats = false; | |
| skb_resp = ERR_PTR(result); | |
| /* call of callback with error */ | |
| cb_arg->complete_cb(stcontext->ddev, cb_arg->cb_usrarg, skb_resp); | |
| /* up the semaphore before returning */ | |
| up(&stcontext->exchange_lock); | |
| mutex_unlock(&stcontext->rm_lock); | |
| return IRQ_HANDLED; | |
| } | |
| /* NFC ops functions definition */ | |
| static int st95hf_in_configure_hw(struct nfc_digital_dev *ddev, | |
| int type, | |
| int param) | |
| { | |
| struct st95hf_context *stcontext = nfc_digital_get_drvdata(ddev); | |
| if (type == NFC_DIGITAL_CONFIG_RF_TECH) | |
| return st95hf_select_protocol(stcontext, param); | |
| if (type == NFC_DIGITAL_CONFIG_FRAMING) { | |
| switch (param) { | |
| case NFC_DIGITAL_FRAMING_NFCA_SHORT: | |
| stcontext->sendrcv_trflag = TRFLAG_NFCA_SHORT_FRAME; | |
| break; | |
| case NFC_DIGITAL_FRAMING_NFCA_STANDARD: | |
| stcontext->sendrcv_trflag = TRFLAG_NFCA_STD_FRAME; | |
| break; | |
| case NFC_DIGITAL_FRAMING_NFCA_T4T: | |
| case NFC_DIGITAL_FRAMING_NFCA_NFC_DEP: | |
| case NFC_DIGITAL_FRAMING_NFCA_STANDARD_WITH_CRC_A: | |
| stcontext->sendrcv_trflag = TRFLAG_NFCA_STD_FRAME_CRC; | |
| break; | |
| case NFC_DIGITAL_FRAMING_NFCB: | |
| case NFC_DIGITAL_FRAMING_ISO15693_INVENTORY: | |
| case NFC_DIGITAL_FRAMING_ISO15693_T5T: | |
| break; | |
| } | |
| } | |
| return 0; | |
| } | |
| static int rf_off(struct st95hf_context *stcontext) | |
| { | |
| int rc; | |
| struct device *dev; | |
| dev = &stcontext->nfcdev->dev; | |
| rc = st95hf_send_recv_cmd(stcontext, CMD_FIELD_OFF, 0, NULL, true); | |
| if (rc) | |
| dev_err(dev, "protocol sel send field off, err = 0x%x\n", rc); | |
| return rc; | |
| } | |
| static int st95hf_in_send_cmd(struct nfc_digital_dev *ddev, | |
| struct sk_buff *skb, | |
| u16 timeout, | |
| nfc_digital_cmd_complete_t cb, | |
| void *arg) | |
| { | |
| struct st95hf_context *stcontext = nfc_digital_get_drvdata(ddev); | |
| int rc; | |
| struct sk_buff *skb_resp; | |
| int len_data_to_tag = 0; | |
| skb_resp = nfc_alloc_recv_skb(MAX_RESPONSE_BUFFER_SIZE, GFP_KERNEL); | |
| if (!skb_resp) { | |
| rc = -ENOMEM; | |
| goto error; | |
| } | |
| switch (stcontext->current_rf_tech) { | |
| case NFC_DIGITAL_RF_TECH_106A: | |
| len_data_to_tag = skb->len + 1; | |
| skb_put_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"); | |
| return rc; | |
| } | |
| rc = st95hf_spi_send(&stcontext->spicontext, skb->data, | |
| skb->len, | |
| ASYNC); | |
| if (rc) { | |
| dev_err(&stcontext->nfcdev->dev, | |
| "Error %d trying to perform data_exchange", rc); | |
| /* up the semaphore since ISR will never come in this case */ | |
| up(&stcontext->exchange_lock); | |
| goto free_skb_resp; | |
| } | |
| kfree_skb(skb); | |
| return rc; | |
| free_skb_resp: | |
| kfree_skb(skb_resp); | |
| error: | |
| return rc; | |
| } | |
| /* p2p will be supported in a later release ! */ | |
| static int st95hf_tg_configure_hw(struct nfc_digital_dev *ddev, | |
| int type, | |
| int param) | |
| { | |
| return 0; | |
| } | |
| static int st95hf_tg_send_cmd(struct nfc_digital_dev *ddev, | |
| struct sk_buff *skb, | |
| u16 timeout, | |
| nfc_digital_cmd_complete_t cb, | |
| void *arg) | |
| { | |
| return 0; | |
| } | |
| static int st95hf_tg_listen(struct nfc_digital_dev *ddev, | |
| u16 timeout, | |
| nfc_digital_cmd_complete_t cb, | |
| void *arg) | |
| { | |
| return 0; | |
| } | |
| static int st95hf_tg_get_rf_tech(struct nfc_digital_dev *ddev, u8 *rf_tech) | |
| { | |
| return 0; | |
| } | |
| static int st95hf_switch_rf(struct nfc_digital_dev *ddev, bool on) | |
| { | |
| u8 rf_tech; | |
| struct st95hf_context *stcontext = nfc_digital_get_drvdata(ddev); | |
| rf_tech = ddev->curr_rf_tech; | |
| if (on) | |
| /* switch on RF field */ | |
| return st95hf_select_protocol(stcontext, rf_tech); | |
| /* switch OFF RF field */ | |
| return rf_off(stcontext); | |
| } | |
| /* TODO st95hf_abort_cmd */ | |
| static void st95hf_abort_cmd(struct nfc_digital_dev *ddev) | |
| { | |
| } | |
| static struct nfc_digital_ops st95hf_nfc_digital_ops = { | |
| .in_configure_hw = st95hf_in_configure_hw, | |
| .in_send_cmd = st95hf_in_send_cmd, | |
| .tg_listen = st95hf_tg_listen, | |
| .tg_configure_hw = st95hf_tg_configure_hw, | |
| .tg_send_cmd = st95hf_tg_send_cmd, | |
| .tg_get_rf_tech = st95hf_tg_get_rf_tech, | |
| .switch_rf = st95hf_switch_rf, | |
| .abort_cmd = st95hf_abort_cmd, | |
| }; | |
| static const struct spi_device_id st95hf_id[] = { | |
| { "st95hf", 0 }, | |
| {} | |
| }; | |
| MODULE_DEVICE_TABLE(spi, st95hf_id); | |
| static int st95hf_probe(struct spi_device *nfc_spi_dev) | |
| { | |
| int ret; | |
| struct st95hf_context *st95context; | |
| struct st95hf_spi_context *spicontext; | |
| nfc_info(&nfc_spi_dev->dev, "ST95HF driver probe called.\n"); | |
| st95context = devm_kzalloc(&nfc_spi_dev->dev, | |
| sizeof(struct st95hf_context), | |
| GFP_KERNEL); | |
| if (!st95context) | |
| return -ENOMEM; | |
| spicontext = &st95context->spicontext; | |
| spicontext->spidev = nfc_spi_dev; | |
| st95context->fwi = | |
| cmd_array[CMD_ISO14443A_PROTOCOL_SELECT].cmd_params[2]; | |
| if (device_property_present(&nfc_spi_dev->dev, "st95hfvin")) { | |
| st95context->st95hf_supply = | |
| devm_regulator_get(&nfc_spi_dev->dev, | |
| "st95hfvin"); | |
| if (IS_ERR(st95context->st95hf_supply)) { | |
| dev_err(&nfc_spi_dev->dev, "failed to acquire regulator\n"); | |
| return PTR_ERR(st95context->st95hf_supply); | |
| } | |
| ret = regulator_enable(st95context->st95hf_supply); | |
| if (ret) { | |
| dev_err(&nfc_spi_dev->dev, "failed to enable regulator\n"); | |
| return ret; | |
| } | |
| } | |
| init_completion(&spicontext->done); | |
| mutex_init(&spicontext->spi_lock); | |
| /* | |
| * Store spicontext in spi device object for using it in | |
| * remove function | |
| */ | |
| dev_set_drvdata(&nfc_spi_dev->dev, spicontext); | |
| st95context->enable_gpio = | |
| of_get_named_gpio(nfc_spi_dev->dev.of_node, | |
| "enable-gpio", | |
| 0); | |
| if (!gpio_is_valid(st95context->enable_gpio)) { | |
| dev_err(&nfc_spi_dev->dev, "No valid enable gpio\n"); | |
| ret = st95context->enable_gpio; | |
| goto err_disable_regulator; | |
| } | |
| ret = devm_gpio_request_one(&nfc_spi_dev->dev, st95context->enable_gpio, | |
| GPIOF_DIR_OUT | GPIOF_INIT_HIGH, | |
| "enable_gpio"); | |
| if (ret) | |
| goto err_disable_regulator; | |
| if (nfc_spi_dev->irq > 0) { | |
| if (devm_request_threaded_irq(&nfc_spi_dev->dev, | |
| nfc_spi_dev->irq, | |
| st95hf_irq_handler, | |
| st95hf_irq_thread_handler, | |
| IRQF_TRIGGER_FALLING, | |
| "st95hf", | |
| (void *)st95context) < 0) { | |
| dev_err(&nfc_spi_dev->dev, "err: irq request for st95hf is failed\n"); | |
| ret = -EINVAL; | |
| goto err_disable_regulator; | |
| } | |
| } else { | |
| dev_err(&nfc_spi_dev->dev, "not a valid IRQ associated with ST95HF\n"); | |
| ret = -EINVAL; | |
| goto err_disable_regulator; | |
| } | |
| /* | |
| * First reset SPI to handle warm reset of the system. | |
| * It will put the ST95HF device in Power ON state | |
| * which make the state of device identical to state | |
| * at the time of cold reset of the system. | |
| */ | |
| ret = st95hf_send_spi_reset_sequence(st95context); | |
| if (ret) { | |
| dev_err(&nfc_spi_dev->dev, "err: spi_reset_sequence failed\n"); | |
| goto err_disable_regulator; | |
| } | |
| /* call PowerOnReset sequence of ST95hf to activate it */ | |
| ret = st95hf_por_sequence(st95context); | |
| if (ret) { | |
| dev_err(&nfc_spi_dev->dev, "err: por seq failed for st95hf\n"); | |
| goto err_disable_regulator; | |
| } | |
| /* create NFC dev object and register with NFC Subsystem */ | |
| st95context->ddev = nfc_digital_allocate_device(&st95hf_nfc_digital_ops, | |
| ST95HF_SUPPORTED_PROT, | |
| ST95HF_CAPABILITIES, | |
| ST95HF_HEADROOM_LEN, | |
| ST95HF_TAILROOM_LEN); | |
| if (!st95context->ddev) { | |
| ret = -ENOMEM; | |
| goto err_disable_regulator; | |
| } | |
| st95context->nfcdev = st95context->ddev->nfc_dev; | |
| nfc_digital_set_parent_dev(st95context->ddev, &nfc_spi_dev->dev); | |
| ret = nfc_digital_register_device(st95context->ddev); | |
| if (ret) { | |
| dev_err(&st95context->nfcdev->dev, "st95hf registration failed\n"); | |
| goto err_free_digital_device; | |
| } | |
| /* store st95context in nfc device object */ | |
| nfc_digital_set_drvdata(st95context->ddev, st95context); | |
| sema_init(&st95context->exchange_lock, 1); | |
| mutex_init(&st95context->rm_lock); | |
| return ret; | |
| err_free_digital_device: | |
| nfc_digital_free_device(st95context->ddev); | |
| err_disable_regulator: | |
| if (st95context->st95hf_supply) | |
| regulator_disable(st95context->st95hf_supply); | |
| return ret; | |
| } | |
| static int st95hf_remove(struct spi_device *nfc_spi_dev) | |
| { | |
| int result = 0; | |
| unsigned char reset_cmd = ST95HF_COMMAND_RESET; | |
| struct st95hf_spi_context *spictx = dev_get_drvdata(&nfc_spi_dev->dev); | |
| struct st95hf_context *stcontext = container_of(spictx, | |
| struct st95hf_context, | |
| spicontext); | |
| mutex_lock(&stcontext->rm_lock); | |
| nfc_digital_unregister_device(stcontext->ddev); | |
| nfc_digital_free_device(stcontext->ddev); | |
| stcontext->nfcdev_free = true; | |
| mutex_unlock(&stcontext->rm_lock); | |
| /* if last in_send_cmd's ISR is pending, wait for it to finish */ | |
| result = down_killable(&stcontext->exchange_lock); | |
| if (result == -EINTR) | |
| dev_err(&spictx->spidev->dev, "sleep for semaphore interrupted by signal\n"); | |
| /* next reset the ST95HF controller */ | |
| result = st95hf_spi_send(&stcontext->spicontext, | |
| &reset_cmd, | |
| ST95HF_RESET_CMD_LEN, | |
| ASYNC); | |
| if (result) { | |
| dev_err(&spictx->spidev->dev, | |
| "ST95HF reset failed in remove() err = %d\n", result); | |
| return result; | |
| } | |
| /* wait for 3 ms to complete the controller reset process */ | |
| usleep_range(3000, 4000); | |
| /* disable regulator */ | |
| if (stcontext->st95hf_supply) | |
| regulator_disable(stcontext->st95hf_supply); | |
| return result; | |
| } | |
| /* Register as SPI protocol driver */ | |
| static struct spi_driver st95hf_driver = { | |
| .driver = { | |
| .name = "st95hf", | |
| .owner = THIS_MODULE, | |
| }, | |
| .id_table = st95hf_id, | |
| .probe = st95hf_probe, | |
| .remove = st95hf_remove, | |
| }; | |
| module_spi_driver(st95hf_driver); | |
| MODULE_AUTHOR("Shikha Singh <shikha.singh@st.com>"); | |
| MODULE_DESCRIPTION("ST NFC Transceiver ST95HF driver"); | |
| MODULE_LICENSE("GPL v2"); |