NFC: Add support for ese cold reset
If not able to communicate with eSE, secure element HAL service will make ioctl request for cold reset NCI command. Cold reset response will be processed differently based on NFC state. Change-Id: I5e6a1e81ca32f6b90b227cf6b617c873c383f386 Signed-off-by: Bhuvan Varshney <bvarshne@codeaurora.org>
This commit is contained in:
parent
28ad72b24d
commit
dea45e3d54
2 changed files with 225 additions and 27 deletions
|
@ -41,21 +41,9 @@ static const struct of_device_id msm_match_table[] = {
|
|||
|
||||
MODULE_DEVICE_TABLE(of, msm_match_table);
|
||||
|
||||
#define DEV_COUNT 1
|
||||
#define DEVICE_NAME "nq-nci"
|
||||
#define CLASS_NAME "nqx"
|
||||
#define MAX_BUFFER_SIZE (320)
|
||||
#define WAKEUP_SRC_TIMEOUT (2000)
|
||||
#define MAX_RETRY_COUNT 3
|
||||
#define NCI_RESET_CMD_LEN 4
|
||||
#define NCI_RESET_RSP_LEN 4
|
||||
#define NCI_RESET_NTF_LEN 13
|
||||
#define NCI_GET_VERSION_CMD_LEN 8
|
||||
#define NCI_GET_VERSION_RSP_LEN 12
|
||||
#define MAX_IRQ_WAIT_TIME (90) //in ms
|
||||
|
||||
struct nqx_dev {
|
||||
wait_queue_head_t read_wq;
|
||||
wait_queue_head_t cold_reset_read_wq;
|
||||
struct mutex read_mutex;
|
||||
struct mutex dev_ref_mutex;
|
||||
struct i2c_client *client;
|
||||
|
@ -72,10 +60,14 @@ struct nqx_dev {
|
|||
unsigned int ese_gpio;
|
||||
/* NFC VEN pin state powered by Nfc */
|
||||
bool nfc_ven_enabled;
|
||||
/* NFC state reflected from MW */
|
||||
bool nfc_enabled;
|
||||
/* NFC_IRQ state */
|
||||
bool irq_enabled;
|
||||
/* NFC_IRQ wake-up state */
|
||||
bool irq_wake_up;
|
||||
bool cold_reset_rsp_pending;
|
||||
uint8_t cold_reset_status;
|
||||
spinlock_t irq_enabled_lock;
|
||||
unsigned int count_irq;
|
||||
/* NFC_IRQ Count */
|
||||
|
@ -97,6 +89,9 @@ static int nfcc_reboot(struct notifier_block *notifier, unsigned long val,
|
|||
static int nqx_clock_select(struct nqx_dev *nqx_dev);
|
||||
/*clock disable function*/
|
||||
static int nqx_clock_deselect(struct nqx_dev *nqx_dev);
|
||||
static int nqx_standby_write(struct nqx_dev *nqx_dev,
|
||||
const unsigned char *buf, size_t len);
|
||||
|
||||
static struct notifier_block nfcc_notifier = {
|
||||
.notifier_call = nfcc_reboot,
|
||||
.next = NULL,
|
||||
|
@ -159,6 +154,92 @@ static irqreturn_t nqx_dev_irq_handler(int irq, void *dev_id)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int is_data_available_for_read(struct nqx_dev *nqx_dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
nqx_enable_irq(nqx_dev);
|
||||
ret = wait_event_interruptible_timeout(nqx_dev->read_wq,
|
||||
!nqx_dev->irq_enabled, msecs_to_jiffies(MAX_IRQ_WAIT_TIME));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int send_cold_reset_cmd(struct nqx_dev *nqx_dev)
|
||||
{
|
||||
int ret;
|
||||
char *cold_reset_cmd = NULL;
|
||||
|
||||
if (gpio_get_value(nqx_dev->firm_gpio)) {
|
||||
dev_err(&nqx_dev->client->dev, "FW download in-progress\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
if (!gpio_get_value(nqx_dev->en_gpio)) {
|
||||
dev_err(&nqx_dev->client->dev, "VEN LOW - NFCC powered off\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
cold_reset_cmd = kzalloc(COLD_RESET_CMD_LEN, GFP_DMA | GFP_KERNEL);
|
||||
if (!cold_reset_cmd)
|
||||
return -ENOMEM;
|
||||
|
||||
cold_reset_cmd[0] = COLD_RESET_CMD_GID;
|
||||
cold_reset_cmd[1] = COLD_RESET_OID;
|
||||
cold_reset_cmd[2] = COLD_RESET_CMD_PAYLOAD_LEN;
|
||||
|
||||
ret = nqx_standby_write(nqx_dev, cold_reset_cmd, COLD_RESET_CMD_LEN);
|
||||
if (ret < 0) {
|
||||
dev_err(&nqx_dev->client->dev,
|
||||
"%s: write failed after max retry\n", __func__);
|
||||
}
|
||||
kfree(cold_reset_cmd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void read_cold_reset_rsp(struct nqx_dev *nqx_dev, bool isNfcEnabled,
|
||||
char *header)
|
||||
{
|
||||
int ret = -1;
|
||||
char *cold_reset_rsp = NULL;
|
||||
|
||||
cold_reset_rsp = kzalloc(COLD_RESET_RSP_LEN, GFP_DMA | GFP_KERNEL);
|
||||
if (!cold_reset_rsp)
|
||||
return;
|
||||
|
||||
/*
|
||||
* read header also if NFC is disabled
|
||||
* for enable case, will be taken care by nfc_read thread
|
||||
*/
|
||||
if (!isNfcEnabled) {
|
||||
ret = i2c_master_recv(nqx_dev->client, cold_reset_rsp,
|
||||
NCI_HEADER_LEN);
|
||||
if (ret != NCI_HEADER_LEN) {
|
||||
dev_err(&nqx_dev->client->dev,
|
||||
"%s: failure to read cold reset rsp header\n",
|
||||
__func__);
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
memcpy(cold_reset_rsp, header, NCI_HEADER_LEN);
|
||||
}
|
||||
|
||||
if ((NCI_HEADER_LEN + cold_reset_rsp[2]) > COLD_RESET_RSP_LEN) {
|
||||
dev_err(&nqx_dev->client->dev,
|
||||
"%s: - invalid response for cold_reset\n", __func__);
|
||||
ret = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
ret = i2c_master_recv(nqx_dev->client, &cold_reset_rsp[NCI_PAYLOAD_IDX],
|
||||
cold_reset_rsp[2]);
|
||||
if (ret != cold_reset_rsp[2]) {
|
||||
dev_err(&nqx_dev->client->dev,
|
||||
"%s: failure to read cold reset rsp status\n",
|
||||
__func__);
|
||||
goto error;
|
||||
}
|
||||
nqx_dev->cold_reset_status = cold_reset_rsp[NCI_PAYLOAD_IDX];
|
||||
error:
|
||||
kfree(cold_reset_rsp);
|
||||
}
|
||||
|
||||
static ssize_t nfc_read(struct file *filp, char __user *buf,
|
||||
size_t count, loff_t *offset)
|
||||
{
|
||||
|
@ -232,6 +313,24 @@ static ssize_t nfc_read(struct file *filp, char __user *buf,
|
|||
ret = -EIO;
|
||||
goto err;
|
||||
}
|
||||
/* check if it's response of cold reset command
|
||||
* NFC HAL process shouldn't receive this data as
|
||||
* command was sent by eSE HAL
|
||||
*/
|
||||
if (nqx_dev->cold_reset_rsp_pending
|
||||
&& (tmp[0] == COLD_RESET_RSP_GID)
|
||||
&& (tmp[1] == COLD_RESET_OID)) {
|
||||
read_cold_reset_rsp(nqx_dev, true, tmp);
|
||||
nqx_dev->cold_reset_rsp_pending = false;
|
||||
wake_up_interruptible(&nqx_dev->cold_reset_read_wq);
|
||||
mutex_unlock(&nqx_dev->read_mutex);
|
||||
/*
|
||||
* NFC process doesn't know about cold reset command
|
||||
* being sent as it was initiated by eSE process
|
||||
* we shouldn't return any data to NFC process
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
#ifdef NFC_KERNEL_BU
|
||||
dev_dbg(&nqx_dev->client->dev, "%s : NfcNciRx %x %x %x\n",
|
||||
__func__, tmp[0], tmp[1], tmp[2]);
|
||||
|
@ -326,17 +425,19 @@ static int nqx_standby_write(struct nqx_dev *nqx_dev,
|
|||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Power management of the SN100 eSE
|
||||
* eSE and NFCC both are powered using VEN gpio in SN100,
|
||||
* VEN HIGH - eSE and NFCC both are powered on
|
||||
* VEN LOW - eSE and NFCC both are power down
|
||||
*/
|
||||
|
||||
static int sn100_ese_pwr(struct nqx_dev *nqx_dev, unsigned long arg)
|
||||
{
|
||||
int r = -1;
|
||||
|
||||
if (arg == 0) {
|
||||
if (arg == ESE_POWER_ON) {
|
||||
/**
|
||||
* Let's store the NFC VEN pin state
|
||||
* will check stored value in case of eSE power off request,
|
||||
|
@ -355,7 +456,7 @@ static int sn100_ese_pwr(struct nqx_dev *nqx_dev, unsigned long arg)
|
|||
dev_dbg(&nqx_dev->client->dev, "en_gpio already HIGH\n");
|
||||
}
|
||||
r = 0;
|
||||
} else if (arg == 1) {
|
||||
} else if (arg == ESE_POWER_OFF) {
|
||||
if (!nqx_dev->nfc_ven_enabled) {
|
||||
dev_dbg(&nqx_dev->client->dev, "NFC not enabled, disabling en_gpio\n");
|
||||
gpio_set_value(nqx_dev->en_gpio, 0);
|
||||
|
@ -365,7 +466,40 @@ static int sn100_ese_pwr(struct nqx_dev *nqx_dev, unsigned long arg)
|
|||
dev_dbg(&nqx_dev->client->dev, "keep en_gpio high as NFC is enabled\n");
|
||||
}
|
||||
r = 0;
|
||||
} else if (arg == 3) {
|
||||
} else if (arg == ESE_COLD_RESET) {
|
||||
// set default value for status as failure
|
||||
nqx_dev->cold_reset_status = EIO;
|
||||
|
||||
r = send_cold_reset_cmd(nqx_dev);
|
||||
if (r <= 0) {
|
||||
dev_err(&nqx_dev->client->dev,
|
||||
"failed to send cold reset command\n");
|
||||
return nqx_dev->cold_reset_status;
|
||||
}
|
||||
nqx_dev->cold_reset_rsp_pending = true;
|
||||
// check if NFC is enabled
|
||||
if (nqx_dev->nfc_enabled) {
|
||||
/*
|
||||
* nfc_read thread will initiate cold reset response
|
||||
* and it will signal for data available
|
||||
*/
|
||||
wait_event_interruptible(nqx_dev->cold_reset_read_wq,
|
||||
!nqx_dev->cold_reset_rsp_pending);
|
||||
} else {
|
||||
/*
|
||||
* Read data as NFC thread is not active
|
||||
*/
|
||||
r = is_data_available_for_read(nqx_dev);
|
||||
if (r <= 0) {
|
||||
nqx_disable_irq(nqx_dev);
|
||||
nqx_dev->cold_reset_rsp_pending = false;
|
||||
return nqx_dev->cold_reset_status;
|
||||
}
|
||||
read_cold_reset_rsp(nqx_dev, false, NULL);
|
||||
nqx_dev->cold_reset_rsp_pending = false;
|
||||
}
|
||||
r = nqx_dev->cold_reset_status;
|
||||
} else if (arg == ESE_POWER_STATE) {
|
||||
// eSE power state
|
||||
r = gpio_get_value(nqx_dev->en_gpio);
|
||||
}
|
||||
|
@ -556,7 +690,7 @@ int nfc_ioctl_power_states(struct file *filp, unsigned long arg)
|
|||
int r = 0;
|
||||
struct nqx_dev *nqx_dev = filp->private_data;
|
||||
|
||||
if (arg == 0) {
|
||||
if (arg == NFC_POWER_OFF) {
|
||||
/*
|
||||
* We are attempting a hardware reset so let us disable
|
||||
* interrupts to avoid spurious notifications to upper
|
||||
|
@ -590,7 +724,7 @@ int nfc_ioctl_power_states(struct file *filp, unsigned long arg)
|
|||
dev_err(&nqx_dev->client->dev, "unable to disable clock\n");
|
||||
}
|
||||
nqx_dev->nfc_ven_enabled = false;
|
||||
} else if (arg == 1) {
|
||||
} else if (arg == NFC_POWER_ON) {
|
||||
nqx_enable_irq(nqx_dev);
|
||||
dev_dbg(&nqx_dev->client->dev,
|
||||
"gpio_set_value enable: %s: info: %p\n",
|
||||
|
@ -607,7 +741,7 @@ int nfc_ioctl_power_states(struct file *filp, unsigned long arg)
|
|||
dev_err(&nqx_dev->client->dev, "unable to enable clock\n");
|
||||
}
|
||||
nqx_dev->nfc_ven_enabled = true;
|
||||
} else if (arg == 2) {
|
||||
} else if (arg == NFC_FW_DWL_VEN_TOGGLE) {
|
||||
/*
|
||||
* We are switching to Dowload Mode, toggle the enable pin
|
||||
* in order to set the NFCC in the new mode
|
||||
|
@ -629,7 +763,7 @@ int nfc_ioctl_power_states(struct file *filp, unsigned long arg)
|
|||
usleep_range(10000, 10100);
|
||||
gpio_set_value(nqx_dev->en_gpio, 1);
|
||||
usleep_range(10000, 10100);
|
||||
} else if (arg == 4) {
|
||||
} else if (arg == NFC_FW_DWL_HIGH) {
|
||||
/*
|
||||
* Setting firmware download gpio to HIGH for SN100U
|
||||
* before FW download start
|
||||
|
@ -641,7 +775,7 @@ int nfc_ioctl_power_states(struct file *filp, unsigned long arg)
|
|||
} else
|
||||
dev_err(&nqx_dev->client->dev,
|
||||
"firm_gpio is invalid\n");
|
||||
} else if (arg == 6) {
|
||||
} else if (arg == NFC_FW_DWL_LOW) {
|
||||
/*
|
||||
* Setting firmware download gpio to LOW for SN100U
|
||||
* FW download finished
|
||||
|
@ -654,6 +788,16 @@ int nfc_ioctl_power_states(struct file *filp, unsigned long arg)
|
|||
dev_err(&nqx_dev->client->dev,
|
||||
"firm_gpio is invalid\n");
|
||||
}
|
||||
} else if (arg == NFC_ENABLE) {
|
||||
/*
|
||||
* Setting flag true when NFC is enabled
|
||||
*/
|
||||
nqx_dev->nfc_enabled = true;
|
||||
} else if (arg == NFC_DISABLE) {
|
||||
/*
|
||||
* Setting flag false when NFC is disabled
|
||||
*/
|
||||
nqx_dev->nfc_enabled = false;
|
||||
} else {
|
||||
r = -ENOIOCTLCMD;
|
||||
}
|
||||
|
@ -897,9 +1041,8 @@ static int nfcc_hw_check(struct i2c_client *client, struct nqx_dev *nqx_dev)
|
|||
}
|
||||
goto err_nfcc_reset_failed;
|
||||
}
|
||||
nqx_enable_irq(nqx_dev);
|
||||
ret = wait_event_interruptible_timeout(nqx_dev->read_wq,
|
||||
!nqx_dev->irq_enabled, msecs_to_jiffies(MAX_IRQ_WAIT_TIME));
|
||||
|
||||
ret = is_data_available_for_read(nqx_dev);
|
||||
if (ret <= 0) {
|
||||
nqx_disable_irq(nqx_dev);
|
||||
goto err_nfcc_hw_check;
|
||||
|
@ -915,9 +1058,8 @@ static int nfcc_hw_check(struct i2c_client *client, struct nqx_dev *nqx_dev)
|
|||
goto reset_enable_gpio;
|
||||
goto err_nfcc_hw_check;
|
||||
}
|
||||
nqx_enable_irq(nqx_dev);
|
||||
ret = wait_event_interruptible_timeout(nqx_dev->read_wq,
|
||||
!nqx_dev->irq_enabled, msecs_to_jiffies(MAX_IRQ_WAIT_TIME));
|
||||
|
||||
ret = is_data_available_for_read(nqx_dev);
|
||||
if (ret <= 0) {
|
||||
nqx_disable_irq(nqx_dev);
|
||||
goto err_nfcc_hw_check;
|
||||
|
@ -1280,6 +1422,7 @@ static int nqx_probe(struct i2c_client *client,
|
|||
|
||||
/* init mutex and queues */
|
||||
init_waitqueue_head(&nqx_dev->read_wq);
|
||||
init_waitqueue_head(&nqx_dev->cold_reset_read_wq);
|
||||
mutex_init(&nqx_dev->read_mutex);
|
||||
mutex_init(&nqx_dev->dev_ref_mutex);
|
||||
spin_lock_init(&nqx_dev->irq_enabled_lock);
|
||||
|
@ -1362,6 +1505,8 @@ static int nqx_probe(struct i2c_client *client,
|
|||
device_set_wakeup_capable(&client->dev, true);
|
||||
i2c_set_clientdata(client, nqx_dev);
|
||||
nqx_dev->irq_wake_up = false;
|
||||
nqx_dev->cold_reset_rsp_pending = false;
|
||||
nqx_dev->nfc_enabled = false;
|
||||
|
||||
dev_err(&client->dev,
|
||||
"%s: probing NFCC NQxxx exited successfully\n",
|
||||
|
|
|
@ -24,12 +24,65 @@
|
|||
#define SET_EMULATOR_TEST_POINT _IOW(0xE9, 0x05, unsigned int)
|
||||
#define NFCC_INITIAL_CORE_RESET_NTF _IOW(0xE9, 0x10, unsigned int)
|
||||
|
||||
#define DEV_COUNT 1
|
||||
#define DEVICE_NAME "nq-nci"
|
||||
#define CLASS_NAME "nqx"
|
||||
#define MAX_BUFFER_SIZE (320)
|
||||
#define WAKEUP_SRC_TIMEOUT (2000)
|
||||
#define NCI_HEADER_LEN 3
|
||||
#define NCI_PAYLOAD_IDX 3
|
||||
#define MAX_RETRY_COUNT 3
|
||||
#define NCI_RESET_CMD_LEN 4
|
||||
#define NCI_RESET_RSP_LEN 4
|
||||
#define NCI_RESET_NTF_LEN 13
|
||||
#define NCI_GET_VERSION_CMD_LEN 8
|
||||
#define NCI_GET_VERSION_RSP_LEN 12
|
||||
#define MAX_IRQ_WAIT_TIME (90) //in ms
|
||||
#define COLD_RESET_CMD_LEN 3
|
||||
#define COLD_RESET_RSP_LEN 4
|
||||
#define COLD_RESET_CMD_GID 0x2F
|
||||
#define COLD_RESET_CMD_PAYLOAD_LEN 0x00
|
||||
#define COLD_RESET_RSP_GID 0x4F
|
||||
#define COLD_RESET_OID 0x1E
|
||||
|
||||
#define NFC_RX_BUFFER_CNT_START (0x0)
|
||||
#define PAYLOAD_HEADER_LENGTH (0x3)
|
||||
#define PAYLOAD_LENGTH_MAX (256)
|
||||
#define BYTE (0x8)
|
||||
#define NCI_IDENTIFIER (0x10)
|
||||
|
||||
enum ese_ioctl_request {
|
||||
/* eSE POWER ON */
|
||||
ESE_POWER_ON = 0,
|
||||
/* eSE POWER OFF */
|
||||
ESE_POWER_OFF,
|
||||
/* eSE COLD RESET */
|
||||
ESE_COLD_RESET,
|
||||
/* eSE POWER STATE */
|
||||
ESE_POWER_STATE
|
||||
};
|
||||
|
||||
enum nfcc_ioctl_request {
|
||||
/* NFC disable request with VEN LOW */
|
||||
NFC_POWER_OFF = 0,
|
||||
/* NFC enable request with VEN Toggle */
|
||||
NFC_POWER_ON,
|
||||
/* firmware download request with VEN Toggle */
|
||||
NFC_FW_DWL_VEN_TOGGLE,
|
||||
/* ISO reset request */
|
||||
NFC_ISO_RESET,
|
||||
/* request for firmware download gpio HIGH */
|
||||
NFC_FW_DWL_HIGH,
|
||||
/* hard reset request */
|
||||
NFC_HARD_RESET,
|
||||
/* request for firmware download gpio LOW */
|
||||
NFC_FW_DWL_LOW,
|
||||
/* NFC enable without VEN gpio modification */
|
||||
NFC_ENABLE,
|
||||
/* NFC disable without VEN gpio modification */
|
||||
NFC_DISABLE
|
||||
};
|
||||
|
||||
enum nfcc_initial_core_reset_ntf {
|
||||
TIMEDOUT_INITIAL_CORE_RESET_NTF = 0, /* 0*/
|
||||
ARRIVED_INITIAL_CORE_RESET_NTF, /* 1 */
|
||||
|
|
Loading…
Add table
Reference in a new issue