NFC: driver: Fix compilation issues on NFC driver

Modified code to fix compilation issues on NFC driver
for MW version 12.10.00 and retained QC specific code.

Change-Id: I9eeba3f6534a3c67c3b32b6361b4ef5d42965265
This commit is contained in:
Tapas Dey
2022-01-06 18:10:46 +05:30
parent 9f6bae5ac6
commit a6c862cb8a
25 changed files with 930 additions and 2534 deletions

View File

@@ -7,7 +7,7 @@ LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS) include $(CLEAR_VARS)
LOCAL_MODULE := nfc_i2c.ko LOCAL_MODULE := nxp-nci.ko
LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT)
LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*)

10
Kbuild
View File

@@ -6,10 +6,12 @@ LINUXINCLUDE += -I$(NFC_ROOT)/include/uapi/linux/nfc
LINUXINCLUDE += -include $(NFC_ROOT)/config/gki_nfc_conf.h LINUXINCLUDE += -include $(NFC_ROOT)/config/gki_nfc_conf.h
obj-$(CONFIG_NFC_QTI_I2C) += nfc_i2c.o obj-$(CONFIG_NXP_NFC_I2C) += nxp-nci.o
#source files #source files
nfc_i2c-objs += qti/ese_cold_reset.o \ nxp-nci-objs += nfc/ese_cold_reset.o \
qti/nfc_common.o \ nfc/common.o \
qti/nfc_i2c_drv.o nfc/common_nxp.o \
nfc/common_qcom.o \
nfc/i2c_drv.o

19
Kconfig
View File

@@ -1,10 +1,13 @@
menuconfig NFC_QTI_I2C #
tristate "QTI NCI based NFC I2C Slave Driver for SNxxx" # near field communication driver configuration
depends on I2C #
help
This enables the NFC driver for SNxxx based devices.
This is for I2C connected version. NCI protocol logic
resides in the usermode and it has no other NFC dependencies.
If unsure, say N. config NXP_NFC_I2C
tristate "NXP NCI based NFC I2C Slave Driver for SNxxx"
depends on I2C
help
This enables the NFC driver for SNxxx based devices.
This is for I2C connected version. NCI protocol logic
resides in the usermode and it has no other NFC dependencies.
If unsure, say N.

View File

@@ -1 +1 @@
export CONFIG_NFC_QTI_I2C=m export CONFIG_NXP_NFC_I2C=m

View File

@@ -1,6 +1,11 @@
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* /*
* Copyright (c) 2021, The Linux Foundation. All rights reserved. * Copyright (c) 2021, The Linux Foundation. All rights reserved.
*/ *
***************************************************************************/
/*
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
*
***************************************************************************/
#define CONFIG_NFC_QTI_I2C 1 #define CONFIG_NXP_NFC_I2C 1

View File

@@ -1,6 +0,0 @@
obj-$(CONFIG_NXP_NFC_I2C) := pn553_i2c.o
pn553_i2c-y := common.o \
common_ese.o \
i2c_drv.o

View File

@@ -1,13 +0,0 @@
#
# near field communication driver configuration
#
config NXP_NFC_I2C
tristate "NXP NCI based NFC I2C Slave Driver for SNxxx"
depends on I2C
help
This enables the NFC driver for SNxxx based devices.
This is for I2C connected version. NCI protocol logic
resides in the usermode and it has no other NFC dependencies.
If unsure, say N.

View File

@@ -1,10 +0,0 @@
KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build
all:
$(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS)
modules_install:
$(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install
clean:
$(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean

View File

@@ -17,17 +17,23 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* *
******************************************************************************/ ******************************************************************************/
/*
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
*
*****************************************************************************/
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/of_gpio.h> #include <linux/of_gpio.h>
#include <linux/delay.h> #include <linux/delay.h>
#include "common_ese.h" #include "common.h"
int nfc_parse_dt(struct device *dev, struct platform_configs *nfc_configs, int nfc_parse_dt(struct device *dev, struct platform_configs *nfc_configs,
uint8_t interface) uint8_t interface)
{ {
int ret;
struct device_node *np = dev->of_node; struct device_node *np = dev->of_node;
struct platform_gpio *nfc_gpio = &nfc_configs->gpio; struct platform_gpio *nfc_gpio = &nfc_configs->gpio;
struct platform_ldo *ldo = &nfc_configs->ldo;
if (!np) { if (!np) {
pr_err("%s: nfc of_node NULL\n", __func__); pr_err("%s: nfc of_node NULL\n", __func__);
@@ -42,7 +48,7 @@ int nfc_parse_dt(struct device *dev, struct platform_configs *nfc_configs,
if (interface == PLATFORM_IF_I2C) { if (interface == PLATFORM_IF_I2C) {
nfc_gpio->irq = of_get_named_gpio(np, DTS_IRQ_GPIO_STR, 0); nfc_gpio->irq = of_get_named_gpio(np, DTS_IRQ_GPIO_STR, 0);
if ((!gpio_is_valid(nfc_gpio->irq))) { if ((!gpio_is_valid(nfc_gpio->irq))) {
pr_err("%s: irq gpio invalid %d\n", __func__, pr_err("%s: nfc irq gpio invalid %d\n", __func__,
nfc_gpio->irq); nfc_gpio->irq);
return -EINVAL; return -EINVAL;
} }
@@ -50,17 +56,38 @@ int nfc_parse_dt(struct device *dev, struct platform_configs *nfc_configs,
} }
nfc_gpio->ven = of_get_named_gpio(np, DTS_VEN_GPIO_STR, 0); nfc_gpio->ven = of_get_named_gpio(np, DTS_VEN_GPIO_STR, 0);
if ((!gpio_is_valid(nfc_gpio->ven))) { if ((!gpio_is_valid(nfc_gpio->ven))) {
pr_err("%s: ven gpio invalid %d\n", __func__, nfc_gpio->ven); pr_err("%s: nfc ven gpio invalid %d\n", __func__, nfc_gpio->ven);
return -EINVAL; return -EINVAL;
} }
/* some products like sn220 does not required fw dwl pin */ /* some products like sn220 does not required fw dwl pin */
nfc_gpio->dwl_req = of_get_named_gpio(np, DTS_FWDN_GPIO_STR, 0); nfc_gpio->dwl_req = of_get_named_gpio(np, DTS_FWDN_GPIO_STR, 0);
/* not returning failure for dwl gpio as it is optional for sn220 */
if ((!gpio_is_valid(nfc_gpio->dwl_req))) if ((!gpio_is_valid(nfc_gpio->dwl_req)))
pr_warn("%s: dwl_req gpio invalid %d\n", __func__, pr_warn("%s: nfc dwl_req gpio invalid %d\n", __func__,
nfc_gpio->dwl_req); nfc_gpio->dwl_req);
pr_info("%s: %d, %d, %d\n", __func__, nfc_gpio->irq, nfc_gpio->ven, pr_info("%s: irq %d, ven %d, dwl %d\n", __func__, nfc_gpio->irq, nfc_gpio->ven,
nfc_gpio->dwl_req); nfc_gpio->dwl_req);
/* optional property */
ret = of_property_read_u32_array(np, NFC_LDO_VOL_DT_NAME,
(u32 *) ldo->vdd_levels,
ARRAY_SIZE(ldo->vdd_levels));
if (ret) {
dev_err(dev, "error reading NFC VDDIO min and max value\n");
// set default as per datasheet
ldo->vdd_levels[0] = NFC_VDDIO_MIN;
ldo->vdd_levels[1] = NFC_VDDIO_MAX;
}
/* optional property */
ret = of_property_read_u32(np, NFC_LDO_CUR_DT_NAME, &ldo->max_current);
if (ret) {
dev_err(dev, "error reading NFC current value\n");
// set default as per datasheet
ldo->max_current = NFC_CURRENT_MAX;
}
return 0; return 0;
} }
@@ -92,9 +119,6 @@ void gpio_set_ven(struct nfc_dev *nfc_dev, int value)
if (gpio_get_value(nfc_gpio->ven) != value) { if (gpio_get_value(nfc_gpio->ven) != value) {
pr_debug("%s: value %d\n", __func__, value); pr_debug("%s: value %d\n", __func__, value);
/* reset on change in level from high to low */
if (value)
ese_cold_reset_release(nfc_dev);
gpio_set_value(nfc_gpio->ven, value); gpio_set_value(nfc_gpio->ven, value);
/* hardware dependent delay */ /* hardware dependent delay */
@@ -168,10 +192,13 @@ void gpio_free_all(struct nfc_dev *nfc_dev)
void nfc_misc_unregister(struct nfc_dev *nfc_dev, int count) void nfc_misc_unregister(struct nfc_dev *nfc_dev, int count)
{ {
pr_debug("%s: entry\n", __func__); pr_debug("%s: entry\n", __func__);
kfree(nfc_dev->kbuf);
device_destroy(nfc_dev->nfc_class, nfc_dev->devno); device_destroy(nfc_dev->nfc_class, nfc_dev->devno);
cdev_del(&nfc_dev->c_dev); cdev_del(&nfc_dev->c_dev);
class_destroy(nfc_dev->nfc_class); class_destroy(nfc_dev->nfc_class);
unregister_chrdev_region(nfc_dev->devno, count); unregister_chrdev_region(nfc_dev->devno, count);
if (nfc_dev->ipcl)
ipc_log_context_destroy(nfc_dev->ipcl);
} }
int nfc_misc_register(struct nfc_dev *nfc_dev, int nfc_misc_register(struct nfc_dev *nfc_dev,
@@ -213,6 +240,23 @@ int nfc_misc_register(struct nfc_dev *nfc_dev,
unregister_chrdev_region(nfc_dev->devno, count); unregister_chrdev_region(nfc_dev->devno, count);
return ret; return ret;
} }
nfc_dev->ipcl = ipc_log_context_create(NUM_OF_IPC_LOG_PAGES,
dev_name(nfc_dev->nfc_device), 0);
nfc_dev->kbuflen = MAX_NCI_BUFFER_SIZE;
nfc_dev->kbuf = kzalloc(MAX_NCI_BUFFER_SIZE, GFP_KERNEL | GFP_DMA);
if (!nfc_dev->kbuf) {
nfc_misc_unregister(nfc_dev, count);
return -ENOMEM;
}
nfc_dev->cold_reset.rsp_pending = false;
nfc_dev->cold_reset.is_nfc_enabled = false;
nfc_dev->cold_reset.is_crp_en = false;
nfc_dev->cold_reset.last_src_ese_prot = ESE_COLD_RESET_ORIGIN_NONE;
init_waitqueue_head(&nfc_dev->cold_reset.read_wq);
return 0; return 0;
} }
@@ -264,6 +308,7 @@ static int nfc_ioctl_power_states(struct nfc_dev *nfc_dev, unsigned long arg)
* Setting firmware download gpio to HIGH * Setting firmware download gpio to HIGH
* before FW download start * before FW download start
*/ */
pr_debug("set fw gpio high\n");
set_valid_gpio(nfc_gpio->dwl_req, 1); set_valid_gpio(nfc_gpio->dwl_req, 1);
nfc_dev->nfc_state = NFC_STATE_FW_DWL; nfc_dev->nfc_state = NFC_STATE_FW_DWL;
@@ -272,15 +317,29 @@ static int nfc_ioctl_power_states(struct nfc_dev *nfc_dev, unsigned long arg)
gpio_set_ven(nfc_dev, 0); gpio_set_ven(nfc_dev, 0);
gpio_set_ven(nfc_dev, 1); gpio_set_ven(nfc_dev, 1);
nfc_dev->nfc_enable_intr(nfc_dev); nfc_dev->nfc_enable_intr(nfc_dev);
pr_info("%s VEN forced reset done\n", __func__);
} else if (arg == NFC_FW_DWL_LOW) { } else if (arg == NFC_FW_DWL_LOW) {
/* /*
* Setting firmware download gpio to LOW * Setting firmware download gpio to LOW
* FW download finished * FW download finished
*/ */
pr_debug("set fw gpio LOW\n");
set_valid_gpio(nfc_gpio->dwl_req, 0); set_valid_gpio(nfc_gpio->dwl_req, 0);
nfc_dev->nfc_state = NFC_STATE_NCI; nfc_dev->nfc_state = NFC_STATE_NCI;
} else {
pr_err("%s: bad arg %lu\n", __func__, arg); } else if (arg == NFC_ENABLE) {
/*
* Setting flag true when NFC is enabled
*/
nfc_dev->cold_reset.is_nfc_enabled = true;
} else if (arg == NFC_DISABLE) {
/*
* Setting flag true when NFC is disabled
*/
nfc_dev->cold_reset.is_nfc_enabled = false;
} else {
pr_err("%s bad arg %lu\n", __func__, arg);
ret = -ENOIOCTLCMD; ret = -ENOIOCTLCMD;
} }
return ret; return ret;
@@ -343,10 +402,17 @@ long nfc_dev_ioctl(struct file *pfile, unsigned int cmd, unsigned long arg)
case ESE_GET_PWR: case ESE_GET_PWR:
ret = nfc_ese_pwr(nfc_dev, ESE_POWER_STATE); ret = nfc_ese_pwr(nfc_dev, ESE_POWER_STATE);
break; break;
case NFCC_GET_INFO:
ret = nfc_ioctl_nfcc_info(pfile, arg);
break;
case ESE_COLD_RESET:
pr_debug("nfc ese cold reset ioctl\n");
ret = ese_cold_reset_ioctl(nfc_dev, arg);
break;
default: default:
pr_err("%s: bad cmd %lu\n", __func__, arg); pr_err("%s: bad cmd %lu\n", __func__, arg);
ret = -ENOIOCTLCMD; ret = -ENOIOCTLCMD;
}; }
return ret; return ret;
} }
@@ -413,14 +479,7 @@ int nfc_dev_close(struct inode *inode, struct file *filp)
} }
if (nfc_dev->dev_ref_count > 0) if (nfc_dev->dev_ref_count > 0)
nfc_dev->dev_ref_count = nfc_dev->dev_ref_count - 1; nfc_dev->dev_ref_count = nfc_dev->dev_ref_count - 1;
else {
/*
* Use "ESE_RST_PROT_DIS" as argument
* if eSE calls flow is via NFC driver
* i.e. direct calls from SPI HAL to NFC driver
*/
nfc_ese_pwr(nfc_dev, ESE_RST_PROT_DIS_NFC);
}
filp->private_data = NULL; filp->private_data = NULL;
mutex_unlock(&nfc_dev->dev_ref_mutex); mutex_unlock(&nfc_dev->dev_ref_mutex);

View File

@@ -17,41 +17,68 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* *
******************************************************************************/ ******************************************************************************/
/*
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
*
*****************************************************************************/
#ifndef _COMMON_H_ #ifndef _COMMON_H_
#define _COMMON_H_ #define _COMMON_H_
#include <linux/cdev.h> #include <linux/cdev.h>
#include <linux/of_gpio.h>
#include <linux/delay.h>
#include <linux/ipc_logging.h>
#include <nfcinfo.h>
#include "i2c_drv.h" #include "i2c_drv.h"
#include "ese_cold_reset.h"
/* Max device count for this driver */ /* Max device count for this driver */
#define DEV_COUNT 1 #define DEV_COUNT 1
/* i2c device class */ /* i2c device class */
#define CLASS_NAME "nfc" #define CLASS_NAME "qti-nfc"
/* NFC character device name, this will be in /dev/ */ /* NFC character device name, this will be in /dev/ */
#define NFC_CHAR_DEV_NAME "pn553" #define NFC_CHAR_DEV_NAME "nq-nci"
/* NCI packet details */ /* NCI packet details */
#define NCI_CMD (0x20) #define NCI_CMD (0x20)
#define NCI_RSP (0x40) #define NCI_RSP (0x40)
#define NCI_NTF (0x60)
#define NCI_HDR_LEN (3) #define NCI_HDR_LEN (3)
#define NCI_HDR_IDX (0) #define NCI_HDR_IDX (0)
#define DL_CMD 0x00
#define DL_PAYLOAD_BYTE_ZERO 0x00
#define NCI_HDR_OID_IDX (1) #define NCI_HDR_OID_IDX (1)
#define NCI_PAYLOAD_IDX (3) #define NCI_PAYLOAD_IDX (3)
#define NCI_PAYLOAD_LEN_IDX (2) #define NCI_PAYLOAD_LEN_IDX (2)
/* FW DNLD packet details */ /*Time to wait for first NCI rest response*/
#define NCI_RESET_RESP_READ_DELAY (10000) // 10ms
#define NCI_RESET_RESP_TIMEOUT (500) // 500ms
// FW DNLD packet details
#define FW_MSG_CMD_RSP 0x00
#define DL_HDR_LEN (2) #define DL_HDR_LEN (2)
#define FW_PAYLOAD_LEN_IDX 1
#define DL_CRC_LEN (2) #define DL_CRC_LEN (2)
#define NCI_RSP_PKT_TYPE (0x40)
#define FW_MIN_PAYLOAD_LEN 4
#define MIN_NFC_DL_FRAME_SIZE 3
#define GET_VERSION_CMD_LEN 8
#define GET_SESSION_STATE_CMD_LEN 8
#define MAX_NCI_PAYLOAD_LEN (255) #define MAX_NCI_PAYLOAD_LEN (255)
#define MAX_NCI_BUFFER_SIZE (NCI_HDR_LEN + MAX_NCI_PAYLOAD_LEN) #define MAX_NCI_BUFFER_SIZE (NCI_HDR_LEN + MAX_NCI_PAYLOAD_LEN)
/*
* From MW 11.04 buffer size increased to support
* frame size of 554 in FW download mode
* Frame len(2) + Frame Header(6) + DATA(512) + HASH(32) + CRC(2) + RFU(4)
*/
#define MAX_DL_PAYLOAD_LEN (550) #define MAX_DL_PAYLOAD_LEN (550)
#define MAX_DL_BUFFER_SIZE (DL_HDR_LEN + DL_CRC_LEN + \ #define MAX_DL_BUFFER_SIZE (DL_HDR_LEN + DL_CRC_LEN + \
MAX_DL_PAYLOAD_LEN) MAX_DL_PAYLOAD_LEN)
/* Retry count for normal write */ /* Retry count for normal write */
#define NO_RETRY (1) #define NO_RETRY (1)
/* Maximum retry count for standby writes */ /* Maximum retry count for standby writes */
@@ -64,6 +91,8 @@
#define NCI_CMD_RSP_TIMEOUT_MS (2000) #define NCI_CMD_RSP_TIMEOUT_MS (2000)
/* Time to wait for NFCC to be ready again after any change in the GPIO */ /* Time to wait for NFCC to be ready again after any change in the GPIO */
#define NFC_GPIO_SET_WAIT_TIME_US (10000) #define NFC_GPIO_SET_WAIT_TIME_US (10000)
/*Time to wait after soft reset via any NCI/DL cmd*/
#define NFC_SOFT_RESET_WAIT_TIME_USEC (5000)
/* Time to wait for IRQ low during write 5*3ms */ /* Time to wait for IRQ low during write 5*3ms */
#define NFC_WRITE_IRQ_WAIT_TIME_US (3000) #define NFC_WRITE_IRQ_WAIT_TIME_US (3000)
/* Time to wait before retrying i2c/I3C writes */ /* Time to wait before retrying i2c/I3C writes */
@@ -72,15 +101,51 @@
#define READ_RETRY_WAIT_TIME_US (3500) #define READ_RETRY_WAIT_TIME_US (3500)
#define NFC_MAGIC (0xE9) #define NFC_MAGIC (0xE9)
/* Ioctls */ // Ioctls
/* The type should be aligned with MW HAL definitions */ // The type should be aligned with MW HAL definitions
#define NFC_SET_PWR _IOW(NFC_MAGIC, 0x01, uint32_t)
#define ESE_SET_PWR _IOW(NFC_MAGIC, 0x02, uint32_t)
#define ESE_GET_PWR _IOR(NFC_MAGIC, 0x03, uint32_t)
#define DTS_IRQ_GPIO_STR "nxp,pn544-irq" #define NFC_SET_PWR _IOW(NFC_MAGIC, 0x01, unsigned int)
#define DTS_VEN_GPIO_STR "nxp,pn544-ven" #define ESE_SET_PWR _IOW(NFC_MAGIC, 0x02, unsigned int)
#define DTS_FWDN_GPIO_STR "nxp,pn544-fw-dwnld" #define ESE_GET_PWR _IOR(NFC_MAGIC, 0x03, unsigned int)
#define DTS_IRQ_GPIO_STR "qcom,sn-irq"
#define DTS_VEN_GPIO_STR "qcom,sn-ven"
#define DTS_FWDN_GPIO_STR "qcom,sn-firm"
#define NFC_LDO_SUPPLY_DT_NAME "qcom,sn-vdd-1p8"
#define NFC_LDO_SUPPLY_NAME "qcom,sn-vdd-1p8-supply"
#define NFC_LDO_VOL_DT_NAME "qcom,sn-vdd-1p8-voltage"
#define NFC_LDO_CUR_DT_NAME "qcom,sn-vdd-1p8-current"
//as per SN1x0 datasheet
#define NFC_VDDIO_MIN 1650000 //in uV
#define NFC_VDDIO_MAX 1950000 //in uV
#define NFC_CURRENT_MAX 157000 //in uA
#define NUM_OF_IPC_LOG_PAGES (2)
#define PKT_MAX_LEN (4) // no of max bytes to print for cmd/resp
#define GET_IPCLOG_MAX_PKT_LEN(c) ((c > PKT_MAX_LEN) ? PKT_MAX_LEN : c)
#define NFCLOG_IPC(nfc_dev, log_to_dmesg, x...) \
do { \
ipc_log_string(nfc_dev->ipcl, x); \
if (log_to_dmesg) { \
if (nfc_dev->nfc_device) \
dev_err((nfc_dev->nfc_device), x); \
else \
pr_err(x); \
} \
} while (0)
enum ese_ioctl_request {
/* eSE POWER ON */
ESE_POWER_ON = 0,
/* eSE POWER OFF */
ESE_POWER_OFF,
/* eSE POWER STATE */
ESE_POWER_STATE
};
enum nfcc_ioctl_request { enum nfcc_ioctl_request {
/* NFC disable request with VEN LOW */ /* NFC disable request with VEN LOW */
@@ -97,6 +162,10 @@ enum nfcc_ioctl_request {
NFC_VEN_FORCED_HARD_RESET, NFC_VEN_FORCED_HARD_RESET,
/* request for firmware download gpio LOW */ /* request for firmware download gpio LOW */
NFC_FW_DWL_LOW, NFC_FW_DWL_LOW,
/* NFC enable without VEN gpio modification */
NFC_ENABLE,
/* NFC disable without VEN gpio modification */
NFC_DISABLE,
}; };
/* nfc platform interface type */ /* nfc platform interface type */
@@ -142,21 +211,18 @@ struct platform_gpio {
unsigned int dwl_req; unsigned int dwl_req;
}; };
// NFC LDO entries from DT
struct platform_ldo {
int vdd_levels[2];
int max_current;
};
/* NFC Struct to get all the required configs from DTS */ /* NFC Struct to get all the required configs from DTS */
struct platform_configs { struct platform_configs {
struct platform_gpio gpio; struct platform_gpio gpio;
struct platform_ldo ldo;
}; };
/* cold reset Features specific Parameters */
struct cold_reset {
bool rsp_pending; /* cmd rsp pending status */
bool in_progress; /* for cold reset when gurad timer in progress */
bool reset_protection; /* reset protection enabled/disabled */
uint8_t status; /* status from response buffer */
uint8_t rst_prot_src; /* reset protection source (SPI, NFC) */
struct timer_list timer;
wait_queue_head_t read_wq;
};
/* Device specific structure */ /* Device specific structure */
struct nfc_dev { struct nfc_dev {
@@ -177,12 +243,25 @@ struct nfc_dev {
uint8_t nfc_state; uint8_t nfc_state;
/* NFC VEN pin state */ /* NFC VEN pin state */
bool nfc_ven_enabled; bool nfc_ven_enabled;
/* current firmware major version */
uint8_t fw_major_version;
bool is_vreg_enabled;
bool is_ese_session_active;
bool release_read; bool release_read;
union { union {
struct i2c_dev i2c_dev; struct i2c_dev i2c_dev;
}; };
struct platform_configs configs; struct platform_configs configs;
struct cold_reset cold_reset; struct cold_reset cold_reset;
struct regulator *reg;
/* read buffer*/
size_t kbuflen;
u8 *kbuf;
union nqx_uinfo nqx_info;
void *ipcl;
/* function pointers for the common i2c functionality */ /* function pointers for the common i2c functionality */
int (*nfc_read)(struct nfc_dev *dev, char *buf, size_t count, int (*nfc_read)(struct nfc_dev *dev, char *buf, size_t count,
@@ -207,6 +286,14 @@ int nfc_misc_register(struct nfc_dev *nfc_dev,
void nfc_misc_unregister(struct nfc_dev *nfc_dev, int count); void nfc_misc_unregister(struct nfc_dev *nfc_dev, int count);
int configure_gpio(unsigned int gpio, int flag); int configure_gpio(unsigned int gpio, int flag);
void gpio_set_ven(struct nfc_dev *nfc_dev, int value); void gpio_set_ven(struct nfc_dev *nfc_dev, int value);
void set_valid_gpio(int gpio, int value);
int nfcc_hw_check(struct nfc_dev *nfc_dev);
unsigned int nfc_ioctl_nfcc_info(struct file *, unsigned long);
void gpio_free_all(struct nfc_dev *nfc_dev); void gpio_free_all(struct nfc_dev *nfc_dev);
int nfc_ldo_config(struct device *dev, struct nfc_dev *nfc_dev);
int nfc_ldo_vote(struct nfc_dev *nfc_dev);
int nfc_ese_pwr(struct nfc_dev *nfc_dev, unsigned long arg);
int nfc_ldo_unvote(struct nfc_dev *nfc_dev);
int is_nfc_data_available_for_read(struct nfc_dev *nfc_dev);
int validate_nfc_state_nci(struct nfc_dev *nfc_dev); int validate_nfc_state_nci(struct nfc_dev *nfc_dev);
#endif /* _COMMON_H_ */ #endif /* _COMMON_H_ */

View File

@@ -1,361 +0,0 @@
/******************************************************************************
* Copyright (C) 2020-2021 NXP
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
******************************************************************************/
#include <linux/jiffies.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include "common_ese.h"
static void cold_reset_gaurd_timer_callback(struct timer_list *t)
{
struct cold_reset *cold_reset = from_timer(cold_reset, t, timer);
pr_debug("%s: entry\n", __func__);
cold_reset->in_progress = false;
}
static long start_cold_reset_guard_timer(struct cold_reset *cold_reset)
{
long ret = -EINVAL;
if (timer_pending(&cold_reset->timer) == 1) {
pr_debug("%s: delete pending timer\n", __func__);
/* delete timer if already pending */
del_timer(&cold_reset->timer);
}
cold_reset->in_progress = true;
timer_setup(&cold_reset->timer, cold_reset_gaurd_timer_callback, 0);
ret = mod_timer(&cold_reset->timer,
jiffies + msecs_to_jiffies(ESE_CLD_RST_GUARD_TIME_MS));
return ret;
}
static int send_cold_reset_protection_cmd(struct nfc_dev *nfc_dev,
bool requestType)
{
int ret = 0;
int cmd_length = 0;
uint8_t *cmd = nfc_dev->write_kbuf;
struct cold_reset *cold_reset = &nfc_dev->cold_reset;
*cmd++ = NCI_PROP_MSG_CMD;
if (requestType) { /* reset protection */
*cmd++ = RST_PROT_OID;
*cmd++ = RST_PROT_PAYLOAD_SIZE;
*cmd++ = (!cold_reset->reset_protection) ? 1 : 0;
} else { /* cold reset */
*cmd++ = CLD_RST_OID;
*cmd++ = CLD_RST_PAYLOAD_SIZE;
}
cmd_length = cmd - nfc_dev->write_kbuf;
ret = nfc_dev->nfc_write(nfc_dev, nfc_dev->write_kbuf, cmd_length,
MAX_RETRY_COUNT);
if (ret != cmd_length) {
ret = -EIO;
pr_err("%s: nfc_write returned %d\n", __func__, ret);
goto exit;
}
cmd = nfc_dev->write_kbuf;
if (requestType)
pr_debug(" %s: NxpNciX: %d > 0x%02x%02x%02x%02x\n", __func__,
ret, cmd[NCI_HDR_IDX], cmd[NCI_HDR_OID_IDX],
cmd[NCI_PAYLOAD_LEN_IDX], cmd[NCI_PAYLOAD_IDX]);
else
pr_debug(" %s: NxpNciX: %d > 0x%02x%02x%02x\n", __func__, ret,
cmd[NCI_HDR_IDX], cmd[NCI_HDR_OID_IDX],
cmd[NCI_PAYLOAD_LEN_IDX]);
exit:
return ret;
}
void wakeup_on_prop_rsp(struct nfc_dev *nfc_dev, uint8_t *buf)
{
struct cold_reset *cold_reset = &nfc_dev->cold_reset;
cold_reset->status = -EIO;
if ((NCI_HDR_LEN + buf[NCI_PAYLOAD_LEN_IDX]) != NCI_PROP_MSG_RSP_LEN)
pr_err("%s: invalid response for cold_reset/protection\n",
__func__);
else
cold_reset->status = buf[NCI_PAYLOAD_IDX];
pr_debug(" %s: NxpNciR 0x%02x%02x%02x%02x\n", __func__,
buf[NCI_HDR_IDX], buf[NCI_HDR_OID_IDX],
buf[NCI_PAYLOAD_LEN_IDX], buf[NCI_PAYLOAD_IDX]);
cold_reset->rsp_pending = false;
wake_up_interruptible(&cold_reset->read_wq);
}
static int validate_cold_reset_protection_request(struct cold_reset *cold_reset,
unsigned long arg)
{
int ret = 0;
if (!cold_reset->reset_protection) {
if (IS_RST_PROT_EN_REQ(arg) && IS_SRC_VALID_PROT(arg)) {
pr_debug("%s: reset protection enable\n", __func__);
} else if (IS_CLD_RST_REQ(arg) && IS_SRC_VALID(arg)) {
pr_debug("%s: cold reset\n", __func__);
} else if (IS_RST_PROT_DIS_REQ(arg) && IS_SRC_VALID_PROT(arg)) {
pr_debug("%s: reset protection already disable\n",
__func__);
ret = -EINVAL;
} else {
pr_err("%s: operation not permitted\n", __func__);
ret = -EPERM;
}
} else {
if (IS_RST_PROT_DIS_REQ(arg) &&
IS_SRC(arg, cold_reset->rst_prot_src)) {
pr_debug("%s: disable reset protection from same src\n",
__func__);
} else if (IS_CLD_RST_REQ(arg) &&
IS_SRC(arg, cold_reset->rst_prot_src)) {
pr_debug("%s: cold reset from same source\n", __func__);
} else if (IS_RST_PROT_EN_REQ(arg) &&
IS_SRC(arg, cold_reset->rst_prot_src)) {
pr_debug("%s: enable reset protection from same src\n",
__func__);
} else {
pr_err("%s: operation not permitted\n", __func__);
ret = -EPERM;
}
}
return ret;
}
static int perform_cold_reset_protection(struct nfc_dev *nfc_dev,
unsigned long arg)
{
int ret = 0;
int timeout = 0;
char *rsp = nfc_dev->read_kbuf;
struct cold_reset *cold_reset = &nfc_dev->cold_reset;
/* check if NFCC not in the FW download or hard reset state */
ret = validate_nfc_state_nci(nfc_dev);
if (ret < 0) {
pr_err("%s: invalid cmd\n", __func__);
return ret;
}
/* check if NFCC not in the FW download or hard reset state */
ret = validate_cold_reset_protection_request(cold_reset, arg);
if (ret < 0) {
pr_err("%s: invalid cmd\n", __func__);
goto err;
}
/* check if cold reset already in progress */
if (IS_CLD_RST_REQ(arg) && cold_reset->in_progress) {
pr_err("%s: cold reset already in progress\n", __func__);
ret = -EBUSY;
goto err;
}
/* enable interrupt if not enabled incase when devnode not opened by HAL */
nfc_dev->nfc_enable_intr(nfc_dev);
mutex_lock(&nfc_dev->write_mutex);
/* write api has 15ms maximum wait to clear any pending read before */
cold_reset->status = -EIO;
cold_reset->rsp_pending = true;
ret = send_cold_reset_protection_cmd(nfc_dev, IS_RST_PROT_REQ(arg));
if (ret < 0) {
mutex_unlock(&nfc_dev->write_mutex);
cold_reset->rsp_pending = false;
pr_err("%s: failed to send cold reset/protection cmd\n",
__func__);
goto err;
}
ret = 0;
/* start the cold reset guard timer */
if (IS_CLD_RST_REQ(arg)) {
/* Guard timer not needed when OSU over NFC */
if (!(cold_reset->reset_protection && IS_SRC_NFC(arg))) {
ret = start_cold_reset_guard_timer(cold_reset);
if (ret) {
mutex_unlock(&nfc_dev->write_mutex);
pr_err("%s: error in mod_timer\n", __func__);
goto err;
}
}
}
timeout = NCI_CMD_RSP_TIMEOUT_MS;
do {
/* call read api directly if reader thread is not blocked */
if (mutex_trylock(&nfc_dev->read_mutex)) {
pr_debug("%s: reader thread not pending\n", __func__);
ret = nfc_dev->nfc_read(nfc_dev, rsp, 3,
timeout);
mutex_unlock(&nfc_dev->read_mutex);
if (!ret)
break;
usleep_range(READ_RETRY_WAIT_TIME_US,
READ_RETRY_WAIT_TIME_US + 500);
/* Read pending response form the HAL service */
} else if (!wait_event_interruptible_timeout(
cold_reset->read_wq,
cold_reset->rsp_pending == false,
msecs_to_jiffies(timeout))) {
pr_err("%s: cold reset/prot response timeout\n", __func__);
ret = -EAGAIN;
}
} while (ret == -ERESTARTSYS || ret == -EFAULT);
mutex_unlock(&nfc_dev->write_mutex);
timeout = ESE_CLD_RST_REBOOT_GUARD_TIME_MS;
if (ret == 0) { /* success case */
ret = cold_reset->status;
if (IS_RST_PROT_REQ(arg)) {
cold_reset->reset_protection = IS_RST_PROT_EN_REQ(arg);
cold_reset->rst_prot_src = IS_RST_PROT_EN_REQ(arg) ?
GET_SRC(arg) :
SRC_NONE;
/* wait for reboot guard timer */
} else if (wait_event_interruptible_timeout(
cold_reset->read_wq, true,
msecs_to_jiffies(timeout)) == 0) {
pr_info("%s: reboot guard timer timeout\n", __func__);
}
}
err:
mutex_unlock(&nfc_dev->dev_ref_mutex);
return ret;
}
/**
* nfc_ese_pwr() - power control for ese
* @nfc_dev: nfc device data structure
* @arg: mode that we want to move to
*
* Device power control. Depending on the arg value, device moves to
* different states, refer common_ese.h for args
*
* Return: -ENOIOCTLCMD if arg is not supported
* 0 if Success(or no issue)
* 0 or 1 in case of arg is ESE_POWER_STATE
* and error ret code otherwise
*/
int nfc_ese_pwr(struct nfc_dev *nfc_dev, unsigned long arg)
{
int ret = 0;
struct platform_gpio *nfc_gpio = &nfc_dev->configs.gpio;
if (arg == ESE_POWER_ON) {
/*
* Let's store the NFC VEN pin state
* will check stored value in case of eSE power off request,
* to find out if NFC MW also sent request to set VEN HIGH
* VEN state will remain HIGH if NFC is enabled otherwise
* it will be set as LOW
*/
nfc_dev->nfc_ven_enabled = gpio_get_value(nfc_gpio->ven);
if (!nfc_dev->nfc_ven_enabled) {
pr_debug("%s: ese hal service setting ven high\n",
__func__);
gpio_set_ven(nfc_dev, 1);
} else {
pr_debug("%s: ven already high\n", __func__);
}
} else if (arg == ESE_POWER_OFF) {
if (!nfc_dev->nfc_ven_enabled) {
pr_debug("%s: nfc not enabled, disabling ven\n",
__func__);
gpio_set_ven(nfc_dev, 0);
} else {
pr_debug("%s: keep ven high as nfc is enabled\n",
__func__);
}
} else if (arg == ESE_POWER_STATE) {
/* eSE get power state */
ret = gpio_get_value(nfc_gpio->ven);
} else if (IS_CLD_RST_REQ(arg) || IS_RST_PROT_REQ(arg)) {
ret = perform_cold_reset_protection(nfc_dev, arg);
} else {
pr_err("%s: bad arg %lu\n", __func__, arg);
ret = -ENOIOCTLCMD;
}
return ret;
}
EXPORT_SYMBOL(nfc_ese_pwr);
#define ESE_LEGACY_INTERFACE
#ifdef ESE_LEGACY_INTERFACE
static struct nfc_dev *nfc_dev_legacy;
/******************************************************************************
* perform_ese_cold_reset() - It shall be called by others driver(not nfc/ese)
* to perform cold reset only
* @arg: request of cold reset from other drivers should be ESE_CLD_RST_OTHER
*
* Returns:- 0 in case of success and negative values in case of failure
*****************************************************************************/
int perform_ese_cold_reset(unsigned long arg)
{
int ret = 0;
if (nfc_dev_legacy) {
if (IS_CLD_RST_REQ(arg) && IS_SRC_OTHER(arg)) {
ret = nfc_ese_pwr(nfc_dev_legacy, arg);
} else {
pr_err("%s: operation not permitted\n", __func__);
return -EPERM;
}
}
pr_debug("%s: arg = %d ret = %lu\n", __func__, arg, ret);
return ret;
}
EXPORT_SYMBOL(perform_ese_cold_reset);
#endif /* ESE_LEGACY_INTERFACE */
void ese_cold_reset_release(struct nfc_dev *nfc_dev)
{
struct cold_reset *cold_reset = &nfc_dev->cold_reset;
cold_reset->rsp_pending = false;
cold_reset->in_progress = false;
if (timer_pending(&cold_reset->timer) == 1)
del_timer(&cold_reset->timer);
}
void common_ese_init(struct nfc_dev *nfc_dev)
{
struct cold_reset *cold_reset = &nfc_dev->cold_reset;
cold_reset->reset_protection = false;
cold_reset->rst_prot_src = SRC_NONE;
init_waitqueue_head(&cold_reset->read_wq);
ese_cold_reset_release(nfc_dev);
#ifdef ESE_LEGACY_INTERFACE
nfc_dev_legacy = nfc_dev;
#endif /* ESE_LEGACY_INTERFACE */
}
void common_ese_exit(struct nfc_dev *nfc_dev)
{
#ifdef ESE_LEGACY_INTERFACE
nfc_dev_legacy = NULL;
#endif /* ESE_LEGACY_INTERFACE */
}

View File

@@ -1,99 +0,0 @@
/******************************************************************************
* Copyright (C) 2020-2021 NXP
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
******************************************************************************/
#ifndef _COMMON_ESE_H_
#define _COMMON_ESE_H_
#include "common.h"
/* nci prop msg 1st byte */
#define NCI_PROP_MSG_GID 0x0F
#define NCI_PROP_MSG_CMD (NCI_CMD | NCI_PROP_MSG_GID)
#define NCI_PROP_MSG_RSP (NCI_RSP | NCI_PROP_MSG_GID)
/* nci prop msg 2nd byte */
#define CLD_RST_OID 0x1E
#define RST_PROT_OID 0x1F
/* nci prop msg 3rd byte */
#define CLD_RST_PAYLOAD_SIZE 0x00
#define RST_PROT_PAYLOAD_SIZE 0x01
/* nci prop msg response length */
#define NCI_PROP_MSG_RSP_LEN 0x04
/* cold reset guard time to allow back to back cold reset after some time */
#define ESE_CLD_RST_GUARD_TIME_MS (3000)
/* guard time to reboot after reset */
#define ESE_CLD_RST_REBOOT_GUARD_TIME_MS (50)
/* sources of reset protection and cold reset */
enum reset_source {
SRC_SPI = 0,
SRC_NFC = 0x10,
SRC_OTHER = 0x20,
SRC_NONE = 0x80,
};
enum ese_ioctl_request {
ESE_POWER_ON = 0, /* eSE POWER ON */
ESE_POWER_OFF, /* eSE POWER OFF */
ESE_POWER_STATE, /* eSE GET POWER STATE */
/* ese reset requests from eSE service/hal/driver */
ESE_CLD_RST, /* eSE COLD RESET */
ESE_RST_PROT_EN, /* eSE RESET PROTECTION ENABLE */
ESE_RST_PROT_DIS, /* eSE RESET PROTECTION DISABLE */
/* similar ese reset requests from nfc service/hal/driver */
ESE_CLD_RST_NFC = ESE_CLD_RST | SRC_NFC,
ESE_RST_PROT_EN_NFC = ESE_RST_PROT_EN | SRC_NFC,
ESE_RST_PROT_DIS_NFC = ESE_RST_PROT_DIS | SRC_NFC,
/* similar ese reset requests from other service/hal/driver */
ESE_CLD_RST_OTHER = ESE_CLD_RST | SRC_OTHER,
};
#define GET_SRC(arg) (arg & 0xF0)
#define IS_SRC(arg, src) (GET_SRC(arg) == src)
#define IS_SRC_SPI(arg) IS_SRC(arg, SRC_SPI)
#define IS_SRC_NFC(arg) IS_SRC(arg, SRC_NFC)
#define IS_SRC_OTHER(arg) IS_SRC(arg, SRC_OTHER)
#define IS_SRC_VALID(arg) (IS_SRC_SPI(arg) || \
IS_SRC_NFC(arg) || \
IS_SRC_OTHER(arg))
#define IS_SRC_VALID_PROT(arg) (IS_SRC_SPI(arg) || \
IS_SRC_NFC(arg))
#define IS_RST(arg, type) ((arg & 0xF) == type)
#define IS_CLD_RST_REQ(arg) IS_RST(arg, ESE_CLD_RST)
#define IS_RST_PROT_EN_REQ(arg) IS_RST(arg, ESE_RST_PROT_EN)
#define IS_RST_PROT_DIS_REQ(arg) IS_RST(arg, ESE_RST_PROT_DIS)
#define IS_RST_PROT_REQ(arg) (IS_RST_PROT_EN_REQ(arg) || \
IS_RST_PROT_DIS_REQ(arg))
/* This macro evaluates to 1 if prop cmd response is received */
#define IS_PROP_CMD_RSP(buf) ((buf[0] == NCI_PROP_MSG_RSP) && \
((buf[1] == CLD_RST_OID) || \
(buf[1] == RST_PROT_OID)))
void wakeup_on_prop_rsp(struct nfc_dev *nfc_dev, uint8_t *buf);
int nfc_ese_pwr(struct nfc_dev *nfc_dev, unsigned long arg);
void ese_cold_reset_release(struct nfc_dev *nfc_dev);
void common_ese_init(struct nfc_dev *nfc_dev);
void common_ese_exit(struct nfc_dev *nfc_dev);
#endif /* _COMMON_ESE_H_ */

332
nfc/common_nxp.c Normal file
View File

@@ -0,0 +1,332 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2019-2021 NXP
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
******************************************************************************/
/*
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
*
******************************************************************************/
#include "common.h"
#include "common_nxp.h"
/**
* get_nfcc_chip_type_dl() - get chip type in fw download command;
* @nfc_dev: nfc device data structure
*
* Perform get version command and determine chip
* type from response.
*
* @Return: enum chip_types value
*
*/
static enum chip_types get_nfcc_chip_type_dl(struct nfc_dev *nfc_dev)
{
int ret = 0;
uint8_t *cmd = nfc_dev->write_kbuf;
uint8_t *rsp = nfc_dev->read_kbuf;
enum chip_types chip_type = CHIP_UNKNOWN;
*cmd++ = DL_CMD;
*cmd++ = DL_GET_VERSION_CMD_PAYLOAD_LEN;
*cmd++ = DL_GET_VERSION_CMD_ID;
*cmd++ = DL_PAYLOAD_BYTE_ZERO;
*cmd++ = DL_PAYLOAD_BYTE_ZERO;
*cmd++ = DL_PAYLOAD_BYTE_ZERO;
*cmd++ = DL_GET_VERSION_CMD_CRC_1;
*cmd++ = DL_GET_VERSION_CMD_CRC_2;
pr_debug("%s:Sending GET_VERSION cmd of size = %d\n", __func__, DL_GET_VERSION_CMD_LEN);
ret = nfc_dev->nfc_write(nfc_dev, nfc_dev->write_kbuf, DL_GET_VERSION_CMD_LEN,
MAX_RETRY_COUNT);
if (ret <= 0) {
pr_err("%s: - nfc get version cmd error ret %d\n", __func__, ret);
goto err;
}
memset(rsp, 0x00, DL_GET_VERSION_RSP_LEN_2);
pr_debug("%s:Reading response of GET_VERSION cmd\n", __func__);
ret = nfc_dev->nfc_read(nfc_dev, rsp, DL_GET_VERSION_RSP_LEN_2, NCI_CMD_RSP_TIMEOUT_MS);
if (ret <= 0) {
pr_err("%s: - nfc get version rsp error ret %d\n", __func__, ret);
goto err;
}
if (rsp[0] == FW_MSG_CMD_RSP && ret >= DL_GET_VERSION_RSP_LEN_2) {
nfc_dev->fw_major_version = rsp[FW_MAJOR_VER_OFFSET];
if (rsp[FW_ROM_CODE_VER_OFFSET] == SN1XX_ROM_VER &&
rsp[FW_MAJOR_VER_OFFSET] == SN1xx_MAJOR_VER)
chip_type = CHIP_SN1XX;
else if (rsp[FW_ROM_CODE_VER_OFFSET] == SN220_ROM_VER &&
rsp[FW_MAJOR_VER_OFFSET] == SN220_MAJOR_VER)
chip_type = CHIP_SN220;
pr_debug("%s:NFC Chip Type 0x%02x Rom Version 0x%02x FW Minor 0x%02x Major 0x%02x\n",
__func__, rsp[GET_VERSION_RSP_CHIP_TYPE_OFFSET],
rsp[FW_ROM_CODE_VER_OFFSET],
rsp[GET_VERSION_RSP_MINOR_VERSION_OFFSET],
rsp[FW_MAJOR_VER_OFFSET]);
nfc_dev->nqx_info.info.chip_type = rsp[GET_VERSION_RSP_CHIP_TYPE_OFFSET];
nfc_dev->nqx_info.info.rom_version = rsp[FW_ROM_CODE_VER_OFFSET];
nfc_dev->nqx_info.info.fw_minor = rsp[GET_VERSION_RSP_MINOR_VERSION_OFFSET];
nfc_dev->nqx_info.info.fw_major = rsp[FW_MAJOR_VER_OFFSET];
}
err:
return chip_type;
}
/**
* get_nfcc_session_state_dl() - gets the session state
* @nfc_dev: nfc device data structure
*
* Performs get session command and determine
* the nfcc state based on session status.
*
* @Return nfcc state based on session status.
* NFC_STATE_FW_TEARED if sessionis not closed
* NFC_STATE_FW_DWL if session closed
* NFC_STATE_UNKNOWN in error cases.
*/
enum nfc_state_flags get_nfcc_session_state_dl(struct nfc_dev *nfc_dev)
{
int ret = 0;
uint8_t *cmd = nfc_dev->write_kbuf;
uint8_t *rsp = nfc_dev->read_kbuf;
enum nfc_state_flags nfc_state = NFC_STATE_UNKNOWN;
*cmd++ = DL_CMD;
*cmd++ = DL_GET_SESSION_STATE_CMD_PAYLOAD_LEN;
*cmd++ = DL_GET_SESSION_CMD_ID;
*cmd++ = DL_PAYLOAD_BYTE_ZERO;
*cmd++ = DL_PAYLOAD_BYTE_ZERO;
*cmd++ = DL_PAYLOAD_BYTE_ZERO;
*cmd++ = DL_GET_SESSION_CMD_CRC_1;
*cmd++ = DL_GET_SESSION_CMD_CRC_2;
pr_debug("%s:Sending GET_SESSION_STATE cmd of size = %d\n", __func__,
DL_GET_SESSION_STATE_CMD_LEN);
ret = nfc_dev->nfc_write(nfc_dev, nfc_dev->write_kbuf, DL_GET_SESSION_STATE_CMD_LEN,
MAX_RETRY_COUNT);
if (ret <= 0) {
pr_err("%s: - nfc get session cmd error ret %d\n", __func__, ret);
goto err;
}
memset(rsp, 0x00, DL_GET_SESSION_STATE_RSP_LEN);
pr_debug("%s:Reading response of GET_SESSION_STATE cmd\n", __func__);
ret = nfc_dev->nfc_read(nfc_dev, rsp, DL_GET_SESSION_STATE_RSP_LEN, NCI_CMD_RSP_TIMEOUT_MS);
if (ret <= 0) {
pr_err("%s: - nfc get session rsp error ret %d\n", __func__, ret);
goto err;
}
if (rsp[0] != FW_MSG_CMD_RSP) {
pr_err("%s: - nfc invalid get session state rsp\n", __func__);
goto err;
}
pr_debug("Response bytes are %02x%02x%02x%02x%02x%02x%02x%02x\n",
rsp[0], rsp[1], rsp[2], rsp[3], rsp[4], rsp[5], rsp[6], rsp[7]);
/*verify fw in non-teared state */
if (rsp[GET_SESSION_STS_OFF] != NFCC_SESSION_STS_CLOSED) {
pr_err("%s NFCC booted in FW teared state\n", __func__);
nfc_state = NFC_STATE_FW_TEARED;
} else {
pr_info("%s NFCC booted in FW DN mode\n", __func__);
nfc_state = NFC_STATE_FW_DWL;
}
err:
return nfc_state;
}
/**
* get_nfcc_chip_type() - get nfcc chip type in nci mode.
* @nfc_dev: nfc device data structure.
*
* Function to perform nci core reset and extract
* chip type from the response.
*
* @Return: enum chip_types value
*
*/
static enum chip_types get_nfcc_chip_type(struct nfc_dev *nfc_dev)
{
int ret = 0;
uint8_t major_version = 0;
uint8_t rom_version = 0;
uint8_t *cmd = nfc_dev->write_kbuf;
uint8_t *rsp = nfc_dev->read_kbuf;
enum chip_types chip_type = CHIP_UNKNOWN;
*cmd++ = NCI_CMD;
*cmd++ = NCI_CORE_RESET_CMD_OID;
*cmd++ = NCI_CORE_RESET_CMD_PAYLOAD_LEN;
*cmd++ = NCI_CORE_RESET_KEEP_CONFIG;
pr_debug("%s:Sending NCI Core Reset cmd of size = %d\n", __func__, NCI_RESET_CMD_LEN);
ret = nfc_dev->nfc_write(nfc_dev, nfc_dev->write_kbuf, NCI_RESET_CMD_LEN, NO_RETRY);
if (ret <= 0) {
pr_err("%s: - nfc nci core reset cmd error ret %d\n", __func__, ret);
goto err;
}
/* to flush out debug NTF this delay is required */
usleep_range(NCI_RESET_RESP_READ_DELAY, NCI_RESET_RESP_READ_DELAY + 100);
nfc_dev->nfc_enable_intr(nfc_dev);
memset(rsp, 0x00, NCI_RESET_RSP_LEN);
pr_debug("%s:Reading NCI Core Reset rsp\n", __func__);
ret = nfc_dev->nfc_read(nfc_dev, rsp, NCI_RESET_RSP_LEN, NCI_CMD_RSP_TIMEOUT_MS);
if (ret <= 0) {
pr_err("%s: - nfc nci core reset rsp error ret %d\n", __func__, ret);
goto err_disable_intr;
}
pr_debug(" %s: nci core reset response 0x%02x%02x%02x%02x\n",
__func__, rsp[0], rsp[1], rsp[2], rsp[3]);
if (rsp[0] != NCI_RSP) {
/* reset response failed response*/
pr_err("%s invalid nci core reset response\n", __func__);
goto err_disable_intr;
}
memset(rsp, 0x00, NCI_RESET_NTF_LEN);
/* read nci rest response ntf */
ret = nfc_dev->nfc_read(nfc_dev, rsp, NCI_RESET_NTF_LEN, NCI_CMD_RSP_TIMEOUT_MS);
if (ret <= 0) {
pr_err("%s - nfc nci rest rsp ntf error status %d\n", __func__, ret);
goto err_disable_intr;
}
if (rsp[0] == NCI_NTF) {
/* read version info from NCI Reset Notification */
rom_version = rsp[NCI_HDR_LEN + rsp[NCI_PAYLOAD_LEN_IDX] - 3];
major_version = rsp[NCI_HDR_LEN + rsp[NCI_PAYLOAD_LEN_IDX] - 2];
/* determine chip type based on version info */
if (rom_version == SN1XX_ROM_VER && major_version == SN1xx_MAJOR_VER)
chip_type = CHIP_SN1XX;
else if (rom_version == SN220_ROM_VER && major_version == SN220_MAJOR_VER)
chip_type = CHIP_SN220;
pr_debug(" %s:NCI Core Reset ntf 0x%02x%02x%02x%02x\n",
__func__, rsp[0], rsp[1], rsp[2], rsp[3]);
nfc_dev->nqx_info.info.chip_type = rsp[NCI_HDR_LEN + rsp[NCI_PAYLOAD_LEN_IDX] -
NFC_CHIP_TYPE_OFF];
nfc_dev->nqx_info.info.rom_version = rom_version;
nfc_dev->nqx_info.info.fw_major = major_version;
nfc_dev->nqx_info.info.fw_minor = rsp[NCI_HDR_LEN + rsp[NCI_PAYLOAD_LEN_IDX] -
NFC_FW_MINOR_OFF];
}
err_disable_intr:
nfc_dev->nfc_disable_intr(nfc_dev);
err:
return chip_type;
}
/**
* validate_download_gpio() - validate download gpio.
* @nfc_dev: nfc_dev device data structure.
* @chip_type: chip type of the platform.
*
* Validates dwnld gpio should configured for supported and
* should not be configured for unsupported platform.
*
* @Return: true if gpio validation successful ortherwise
* false if validation fails.
*/
static bool validate_download_gpio(struct nfc_dev *nfc_dev, enum chip_types chip_type)
{
bool status = false;
struct platform_gpio *nfc_gpio;
if (nfc_dev == NULL) {
pr_err("%s nfc devices structure is null\n");
return status;
}
nfc_gpio = &nfc_dev->configs.gpio;
if (chip_type == CHIP_SN1XX) {
/* gpio should be configured for SN1xx */
status = gpio_is_valid(nfc_gpio->dwl_req);
} else if (chip_type == CHIP_SN220) {
/* gpio should not be configured for SN220 */
set_valid_gpio(nfc_gpio->dwl_req, 0);
gpio_free(nfc_gpio->dwl_req);
nfc_gpio->dwl_req = -EINVAL;
status = true;
}
return status;
}
/* Check for availability of NFC controller hardware */
int nfcc_hw_check(struct nfc_dev *nfc_dev)
{
int ret = 0;
enum nfc_state_flags nfc_state = NFC_STATE_UNKNOWN;
enum chip_types chip_type = CHIP_UNKNOWN;
struct platform_gpio *nfc_gpio = &nfc_dev->configs.gpio;
/*get fw version in nci mode*/
gpio_set_ven(nfc_dev, 1);
gpio_set_ven(nfc_dev, 0);
gpio_set_ven(nfc_dev, 1);
chip_type = get_nfcc_chip_type(nfc_dev);
/*get fw version in fw dwl mode*/
if (chip_type == CHIP_UNKNOWN) {
nfc_dev->nfc_enable_intr(nfc_dev);
/*Chip is unknown, initially assume with fw dwl pin enabled*/
set_valid_gpio(nfc_gpio->dwl_req, 1);
gpio_set_ven(nfc_dev, 0);
gpio_set_ven(nfc_dev, 1);
chip_type = get_nfcc_chip_type_dl(nfc_dev);
/*get the state of nfcc normal/teared in fw dwl mode*/
} else {
nfc_state = NFC_STATE_NCI;
}
/*validate gpio config required as per the chip*/
if (!validate_download_gpio(nfc_dev, chip_type)) {
pr_info("%s gpio validation fail\n", __func__);
ret = -ENXIO;
goto err;
}
/*check whether the NFCC is in FW DN or Teared state*/
if (nfc_state != NFC_STATE_NCI)
nfc_state = get_nfcc_session_state_dl(nfc_dev);
/*nfcc state specific operations */
switch (nfc_state) {
case NFC_STATE_FW_TEARED:
pr_warn("%s: - NFCC FW Teared State\n", __func__);
case NFC_STATE_FW_DWL:
case NFC_STATE_NCI:
break;
case NFC_STATE_UNKNOWN:
default:
ret = -ENXIO;
pr_err("%s: - NFCC HW not available\n", __func__);
goto err;
}
nfc_dev->nfc_state = nfc_state;
err:
nfc_dev->nfc_disable_intr(nfc_dev);
set_valid_gpio(nfc_gpio->dwl_req, 0);
gpio_set_ven(nfc_dev, 0);
gpio_set_ven(nfc_dev, 1);
nfc_dev->nfc_ven_enabled = true;
return ret;
}

77
nfc/common_nxp.h Normal file
View File

@@ -0,0 +1,77 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2019-2021 NXP
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
******************************************************************************/
/*
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
*
******************************************************************************/
#ifndef _COMMON_QCOM_H_
#define _COMMON_QCOM_H_
#define NCI_RESET_CMD_LEN (4)
#define NCI_RESET_RSP_LEN (4)
#define NCI_CORE_RESET_CMD_OID (0x0)
#define NCI_CORE_RESET_CMD_PAYLOAD_LEN (0x1)
#define NCI_CORE_RESET_KEEP_CONFIG (0x0)
#define NCI_RESET_NTF_LEN (13)
/*command response timeout*/
#define NCI_CMD_RSP_TIMEOUT_MS (2000) //2s
#define SN1XX_ROM_VER 0x01
#define SN1xx_MAJOR_VER 0x10
#define SN220_ROM_VER 0x01
#define SN220_MAJOR_VER 0x01
#define FW_ROM_CODE_VER_OFFSET 4
#define FW_MAJOR_VER_OFFSET 7
#define GET_VERSION_RSP_CHIP_TYPE_OFFSET 3
#define GET_VERSION_RSP_MINOR_VERSION_OFFSET 6
#define DL_GET_VERSION_CMD_LEN (8)
#define DL_GET_VERSION_RSP_LEN_1 (12) /* SN110 */
#define DL_GET_VERSION_RSP_LEN_2 (20) /* SN220 */
#define DL_GET_VERSION_CMD_PAYLOAD_LEN (4)
#define DL_GET_VERSION_CMD_ID (0xF1)
#define DL_GET_VERSION_CMD_CRC_1 (0x6E)
#define DL_GET_VERSION_CMD_CRC_2 (0xEF)
#define DL_RESET_CMD_LEN (8)
#define DL_GET_SESSION_STATE_CMD_LEN (8)
#define DL_GET_SESSION_STATE_RSP_LEN (8)
#define DL_GET_SESSION_STATE_CMD_PAYLOAD_LEN (4)
#define DL_GET_SESSION_CMD_ID (0xF2)
#define DL_GET_SESSION_CMD_CRC_1 (0xF5)
#define DL_GET_SESSION_CMD_CRC_2 (0x33)
#define GET_SESSION_STS_OFF (3)
#define NFCC_SESSION_STS_CLOSED (0x0)
/* Below offsets should be subtracted from NCI header length + payload length */
#define NFC_CHIP_TYPE_OFF (4)
#define NFC_FW_MINOR_OFF (1)
enum chip_types {
CHIP_SN1XX = 0x01,
CHIP_SN220 = 0x02,
CHIP_UNKNOWN = 0xFF,
};
#endif //_COMMON_QCOM_H_

163
nfc/common_qcom.c Normal file
View File

@@ -0,0 +1,163 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
*
***************************************************************************/
/*
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
*
***************************************************************************/
#include "common.h"
/*
* Inside nfc_ioctl_nfcc_info
*
* @brief nfc_ioctl_nfcc_info
*
* Check the NFC Chipset and firmware version details
*/
unsigned int nfc_ioctl_nfcc_info(struct file *filp, unsigned long arg)
{
unsigned int r = 0;
struct nfc_dev *nfc_dev = filp->private_data;
r = nfc_dev->nqx_info.i;
pr_debug("nfc : %s r = 0x%x\n", __func__, r);
return r;
}
/*
* Inside is_nfc_data_available_for_read
*
* @nfc_dev: nfc device data structure
*
* Checks if the data is available for reading
* on waiting queue.
*
* @Return: status value
*
*/
int is_nfc_data_available_for_read(struct nfc_dev *nfc_dev)
{
int ret;
nfc_dev->nfc_enable_intr(nfc_dev);
ret = wait_event_interruptible_timeout(nfc_dev->read_wq,
!nfc_dev->i2c_dev.irq_enabled,
msecs_to_jiffies(MAX_IRQ_WAIT_TIME));
return ret;
}
/**
* nfc_ldo_vote()
* @nfc_dev: NFC device containing regulator handle
*
* LDO voting based on voltage and current entries in DT
*
* Return: 0 on success and -ve on failure
*/
int nfc_ldo_vote(struct nfc_dev *nfc_dev)
{
int ret;
ret = regulator_set_voltage(nfc_dev->reg,
nfc_dev->configs.ldo.vdd_levels[0],
nfc_dev->configs.ldo.vdd_levels[1]);
if (ret < 0) {
pr_err("%s: set voltage failed\n", __func__);
return ret;
}
/* pass expected current from NFC in uA */
ret = regulator_set_load(nfc_dev->reg, nfc_dev->configs.ldo.max_current);
if (ret < 0) {
pr_err("%s: set load failed\n", __func__);
return ret;
}
ret = regulator_enable(nfc_dev->reg);
if (ret < 0)
pr_err("%s: regulator_enable failed\n", __func__);
else
nfc_dev->is_vreg_enabled = true;
return ret;
}
/**
* nfc_ldo_config()
* @dev: device instance to read DT entry
* @nfc_dev: NFC device containing regulator handle
*
* Configure LDO if entry is present in DT file otherwise
* return with success as it's optional
*
* Return: 0 on success and -ve on failure
*/
int nfc_ldo_config(struct device *dev, struct nfc_dev *nfc_dev)
{
int ret;
if (of_get_property(dev->of_node, NFC_LDO_SUPPLY_NAME, NULL)) {
// Get the regulator handle
nfc_dev->reg = regulator_get(dev, NFC_LDO_SUPPLY_DT_NAME);
if (IS_ERR(nfc_dev->reg)) {
ret = PTR_ERR(nfc_dev->reg);
nfc_dev->reg = NULL;
pr_err("%s: regulator_get failed, ret = %d\n",
__func__, ret);
return ret;
}
} else {
nfc_dev->reg = NULL;
pr_err("%s: regulator entry not present\n", __func__);
// return success as it's optional to configure LDO
return 0;
}
// LDO config supported by platform DT
ret = nfc_ldo_vote(nfc_dev);
if (ret < 0) {
pr_err("%s: LDO voting failed, ret = %d\n", __func__, ret);
regulator_put(nfc_dev->reg);
}
return ret;
}
/**
* nfc_ldo_unvote()
* @nfc_dev: NFC device containing regulator handle
*
* set voltage and load to zero and disable regulator
*
* Return: 0 on success and -ve on failure
*/
int nfc_ldo_unvote(struct nfc_dev *nfc_dev)
{
int ret;
if (!nfc_dev->is_vreg_enabled) {
pr_err("%s: regulator already disabled\n", __func__);
return -EINVAL;
}
ret = regulator_disable(nfc_dev->reg);
if (ret < 0) {
pr_err("%s: regulator_disable failed\n", __func__);
return ret;
}
nfc_dev->is_vreg_enabled = false;
ret = regulator_set_voltage(nfc_dev->reg, 0, NFC_VDDIO_MAX);
if (ret < 0) {
pr_err("%s: set voltage failed\n", __func__);
return ret;
}
ret = regulator_set_load(nfc_dev->reg, 0);
if (ret < 0)
pr_err("%s: set load failed\n", __func__);
return ret;
}

View File

@@ -1,12 +1,61 @@
// SPDX-License-Identifier: GPL-2.0-only // SPDX-License-Identifier: GPL-2.0-only
/* /*
* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
*/ *
***************************************************************************/
/*
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
*
***************************************************************************/
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include "nfc_common.h" #include "common.h"
/*
* Power management of the eSE
* eSE and NFCC both are powered using VEN gpio,
* VEN HIGH - eSE and NFCC both are powered on
* VEN LOW - eSE and NFCC both are power down
*/
int nfc_ese_pwr(struct nfc_dev *nfc_dev, unsigned long arg)
{
int ret = 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,
* to find out if NFC MW also sent request to set VEN HIGH
* VEN state will remain HIGH if NFC is enabled otherwise
* it will be set as LOW
*/
nfc_dev->nfc_ven_enabled = gpio_get_value(nfc_dev->configs.gpio.ven);
if (!nfc_dev->nfc_ven_enabled) {
pr_debug("eSE HAL service setting ven HIGH\n");
gpio_set_ven(nfc_dev, 1);
} else {
pr_debug("ven already HIGH\n");
}
nfc_dev->is_ese_session_active = true;
} else if (arg == ESE_POWER_OFF) {
if (!nfc_dev->nfc_ven_enabled) {
pr_debug("NFC not enabled, disabling ven\n");
gpio_set_ven(nfc_dev, 0);
} else {
pr_debug("keep ven high as NFC is enabled\n");
}
nfc_dev->is_ese_session_active = false;
} else if (arg == ESE_POWER_STATE) {
/* get VEN gpio state for eSE, as eSE also enabled through same GPIO */
ret = gpio_get_value(nfc_dev->configs.gpio.ven);
} else {
pr_err("%s bad arg %lu\n", __func__, arg);
ret = -ENOIOCTLCMD;
}
return ret;
}
/** /**
* send_ese_cmd() - Send eSE command to NFC controller. * send_ese_cmd() - Send eSE command to NFC controller.
@@ -106,7 +155,7 @@ int read_cold_reset_rsp(struct nfc_dev *nfc_dev, char *header)
ret = nfc_dev->nfc_read(nfc_dev, ret = nfc_dev->nfc_read(nfc_dev,
&rsp_buf[NCI_PAYLOAD_IDX], &rsp_buf[NCI_PAYLOAD_IDX],
rsp_buf[NCI_PAYLOAD_LEN_IDX], rsp_buf[NCI_PAYLOAD_LEN_IDX],
NCI_CMD_RSP_TIMEOUT); NCI_CMD_RSP_TIMEOUT_MS);
if (ret <= 0) { if (ret <= 0) {
dev_err(nfc_dev->nfc_device, dev_err(nfc_dev->nfc_device,

View File

@@ -33,7 +33,11 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/ ****************************************************************************/
/*
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
*
****************************************************************************/
#include <linux/module.h> #include <linux/module.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
@@ -43,7 +47,7 @@
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT
#include <linux/compat.h> #include <linux/compat.h>
#endif #endif
#include "common_ese.h" #include "common.h"
/** /**
* i2c_disable_irq() * i2c_disable_irq()
@@ -108,6 +112,8 @@ int i2c_read(struct nfc_dev *nfc_dev, char *buf, size_t count, int timeout)
int ret; int ret;
struct i2c_dev *i2c_dev = &nfc_dev->i2c_dev; struct i2c_dev *i2c_dev = &nfc_dev->i2c_dev;
struct platform_gpio *nfc_gpio = &nfc_dev->configs.gpio; struct platform_gpio *nfc_gpio = &nfc_dev->configs.gpio;
uint16_t i = 0;
uint16_t disp_len = GET_IPCLOG_MAX_PKT_LEN(count);
pr_debug("%s: reading %zu bytes.\n", __func__, count); pr_debug("%s: reading %zu bytes.\n", __func__, count);
@@ -174,34 +180,34 @@ int i2c_read(struct nfc_dev *nfc_dev, char *buf, size_t count, int timeout)
memset(buf, 0x00, count); memset(buf, 0x00, count);
/* Read data */ /* Read data */
ret = i2c_master_recv(nfc_dev->i2c_dev.client, buf, count); ret = i2c_master_recv(nfc_dev->i2c_dev.client, buf, count);
NFCLOG_IPC(nfc_dev, false, "%s of %d bytes, ret %d", __func__, count,
ret);
if (ret <= 0) { if (ret <= 0) {
pr_err("%s: returned %d\n", __func__, ret); pr_err("%s: returned %d\n", __func__, ret);
goto err; goto err;
} }
for (i = 0; i < disp_len; i++)
NFCLOG_IPC(nfc_dev, false, " %02x", buf[i]);
/* check if it's response of cold reset command /* check if it's response of cold reset command
* NFC HAL process shouldn't receive this data as * NFC HAL process shouldn't receive this data as
* command was sent by driver * command was sent by esepowermanager
*/ */
if (nfc_dev->cold_reset.rsp_pending) { if (nfc_dev->cold_reset.rsp_pending && nfc_dev->cold_reset.cmd_buf
if (IS_PROP_CMD_RSP(buf)) { && (buf[0] == PROP_NCI_RSP_GID)
/* Read data */ && (buf[1] == nfc_dev->cold_reset.cmd_buf[1])) {
ret = i2c_master_recv(nfc_dev->i2c_dev.client, read_cold_reset_rsp(nfc_dev, buf);
&buf[NCI_PAYLOAD_IDX], nfc_dev->cold_reset.rsp_pending = false;
buf[NCI_PAYLOAD_LEN_IDX]); wake_up_interruptible(&nfc_dev->cold_reset.read_wq);
if (ret <= 0) { /*
pr_err("%s: error reading cold rst/prot rsp\n", * NFC process doesn't know about cold reset command
__func__); * being sent as it was initiated by eSE process
goto err; * we shouldn't return any data to NFC process
} */
wakeup_on_prop_rsp(nfc_dev, buf); return 0;
/*
* 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;
}
} }
err: err:
return ret; return ret;
} }
@@ -211,12 +217,18 @@ int i2c_write(struct nfc_dev *nfc_dev, const char *buf, size_t count,
{ {
int ret = -EINVAL; int ret = -EINVAL;
int retry_cnt; int retry_cnt;
uint16_t i = 0;
uint16_t disp_len = GET_IPCLOG_MAX_PKT_LEN(count);
struct platform_gpio *nfc_gpio = &nfc_dev->configs.gpio; struct platform_gpio *nfc_gpio = &nfc_dev->configs.gpio;
if (count > MAX_DL_BUFFER_SIZE) if (count > MAX_DL_BUFFER_SIZE)
count = MAX_DL_BUFFER_SIZE; count = MAX_DL_BUFFER_SIZE;
pr_debug("%s: writing %zu bytes.\n", __func__, count); pr_debug("%s: writing %zu bytes.\n", __func__, count);
NFCLOG_IPC(nfc_dev, false, "%s sending %d B", __func__, count);
for (i = 0; i < disp_len; i++)
NFCLOG_IPC(nfc_dev, false, " %02x", buf[i]);
/* /*
* Wait for any pending read for max 15ms before write * Wait for any pending read for max 15ms before write
* This is to avoid any packet corruption during read, when * This is to avoid any packet corruption during read, when
@@ -238,6 +250,7 @@ int i2c_write(struct nfc_dev *nfc_dev, const char *buf, size_t count,
for (retry_cnt = 1; retry_cnt <= max_retry_cnt; retry_cnt++) { for (retry_cnt = 1; retry_cnt <= max_retry_cnt; retry_cnt++) {
ret = i2c_master_send(nfc_dev->i2c_dev.client, buf, count); ret = i2c_master_send(nfc_dev->i2c_dev.client, buf, count);
NFCLOG_IPC(nfc_dev, false, "%s ret %d", __func__, ret);
if (ret <= 0) { if (ret <= 0) {
pr_warn("%s: write failed ret(%d), maybe in standby\n", pr_warn("%s: write failed ret(%d), maybe in standby\n",
__func__, ret); __func__, ret);
@@ -391,7 +404,6 @@ int nfc_i2c_dev_probe(struct i2c_client *client, const struct i2c_device_id *id)
mutex_init(&nfc_dev->write_mutex); mutex_init(&nfc_dev->write_mutex);
mutex_init(&nfc_dev->dev_ref_mutex); mutex_init(&nfc_dev->dev_ref_mutex);
spin_lock_init(&i2c_dev->irq_enabled_lock); spin_lock_init(&i2c_dev->irq_enabled_lock);
common_ese_init(nfc_dev);
ret = nfc_misc_register(nfc_dev, &nfc_i2c_dev_fops, DEV_COUNT, ret = nfc_misc_register(nfc_dev, &nfc_i2c_dev_fops, DEV_COUNT,
NFC_CHAR_DEV_NAME, CLASS_NAME); NFC_CHAR_DEV_NAME, CLASS_NAME);
if (ret) { if (ret) {
@@ -408,15 +420,35 @@ int nfc_i2c_dev_probe(struct i2c_client *client, const struct i2c_device_id *id)
goto err_nfc_misc_unregister; goto err_nfc_misc_unregister;
} }
i2c_disable_irq(nfc_dev); i2c_disable_irq(nfc_dev);
ret = nfc_ldo_config(&client->dev, nfc_dev);
if (ret) {
pr_err("LDO config failed\n");
goto err_ldo_config_failed;
}
ret = nfcc_hw_check(nfc_dev);
if (ret || nfc_dev->nfc_state == NFC_STATE_UNKNOWN) {
pr_err("nfc hw check failed ret %d\n", ret);
goto err_nfcc_hw_check;
}
gpio_set_ven(nfc_dev, 1); gpio_set_ven(nfc_dev, 1);
gpio_set_ven(nfc_dev, 0); gpio_set_ven(nfc_dev, 0);
gpio_set_ven(nfc_dev, 1); gpio_set_ven(nfc_dev, 1);
device_init_wakeup(&client->dev, true); device_init_wakeup(&client->dev, true);
i2c_set_clientdata(client, nfc_dev); i2c_set_clientdata(client, nfc_dev);
i2c_dev->irq_wake_up = false; i2c_dev->irq_wake_up = false;
nfc_dev->is_ese_session_active = false;
pr_info("%s: probing nfc i2c successfully\n", __func__); pr_info("%s: probing nfc i2c successfully\n", __func__);
return 0; return 0;
err_nfcc_hw_check:
if (nfc_dev->reg) {
nfc_ldo_unvote(nfc_dev);
regulator_put(nfc_dev->reg);
}
err_ldo_config_failed:
free_irq(client->irq, nfc_dev);
err_nfc_misc_unregister: err_nfc_misc_unregister:
nfc_misc_unregister(nfc_dev, DEV_COUNT); nfc_misc_unregister(nfc_dev, DEV_COUNT);
err_mutex_destroy: err_mutex_destroy:
@@ -452,9 +484,19 @@ int nfc_i2c_dev_remove(struct i2c_client *client)
pr_err("%s: device already in use\n", __func__); pr_err("%s: device already in use\n", __func__);
return -EBUSY; return -EBUSY;
} }
gpio_set_value(nfc_dev->configs.gpio.ven, 0);
// HW dependent delay before LDO goes into LPM mode
usleep_range(10000, 10100);
if (nfc_dev->reg) {
nfc_ldo_unvote(nfc_dev);
regulator_put(nfc_dev->reg);
}
device_init_wakeup(&client->dev, false); device_init_wakeup(&client->dev, false);
free_irq(client->irq, nfc_dev); free_irq(client->irq, nfc_dev);
nfc_misc_unregister(nfc_dev, DEV_COUNT); nfc_misc_unregister(nfc_dev, DEV_COUNT);
mutex_destroy(&nfc_dev->dev_ref_mutex);
mutex_destroy(&nfc_dev->read_mutex); mutex_destroy(&nfc_dev->read_mutex);
mutex_destroy(&nfc_dev->write_mutex); mutex_destroy(&nfc_dev->write_mutex);
gpio_free_all(nfc_dev); gpio_free_all(nfc_dev);
@@ -475,6 +517,9 @@ int nfc_i2c_dev_suspend(struct device *device)
} }
i2c_dev = &nfc_dev->i2c_dev; i2c_dev = &nfc_dev->i2c_dev;
NFCLOG_IPC(nfc_dev, false, "%s: irq_enabled = %d", __func__,
i2c_dev->irq_enabled);
if (device_may_wakeup(&client->dev) && i2c_dev->irq_enabled) { if (device_may_wakeup(&client->dev) && i2c_dev->irq_enabled) {
if (!enable_irq_wake(client->irq)) if (!enable_irq_wake(client->irq))
i2c_dev->irq_wake_up = true; i2c_dev->irq_wake_up = true;
@@ -494,6 +539,9 @@ int nfc_i2c_dev_resume(struct device *device)
} }
i2c_dev = &nfc_dev->i2c_dev; i2c_dev = &nfc_dev->i2c_dev;
NFCLOG_IPC(nfc_dev, false, "%s: irq_wake_up = %d", __func__,
i2c_dev->irq_wake_up);
if (device_may_wakeup(&client->dev) && i2c_dev->irq_wake_up) { if (device_may_wakeup(&client->dev) && i2c_dev->irq_wake_up) {
if (!disable_irq_wake(client->irq)) if (!disable_irq_wake(client->irq))
i2c_dev->irq_wake_up = false; i2c_dev->irq_wake_up = false;

View File

@@ -17,14 +17,19 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* *
******************************************************************************/ ******************************************************************************/
/*
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
*
******************************************************************************/
#ifndef _I2C_DRV_H_ #ifndef _I2C_DRV_H_
#define _I2C_DRV_H_ #define _I2C_DRV_H_
#include <linux/i2c.h> #include <linux/i2c.h>
/* kept same as dts */ #define NFC_I2C_DRV_STR "qcom,sn-nci" /*kept same as dts */
#define NFC_I2C_DRV_STR "nxp,pn544" #define NFC_I2C_DEV_ID "sn-i2c"
#define NFC_I2C_DEV_ID "pn553"
struct nfc_dev;
/* Interface specific parameters */ /* Interface specific parameters */
struct i2c_dev { struct i2c_dev {
@@ -43,4 +48,14 @@ int nfc_i2c_dev_remove(struct i2c_client *client);
int nfc_i2c_dev_suspend(struct device *device); int nfc_i2c_dev_suspend(struct device *device);
int nfc_i2c_dev_resume(struct device *device); int nfc_i2c_dev_resume(struct device *device);
#endif /* _I2C_DRV_H_ */ #if IS_ENABLED(CONFIG_NXP_NFC_I2C)
int i2c_enable_irq(struct nfc_dev *dev);
int i2c_disable_irq(struct nfc_dev *dev);
int i2c_write(struct nfc_dev *dev, const char *buf, size_t count,
int max_retry_cnt);
int i2c_read(struct nfc_dev *dev, char *buf, size_t count, int timeout);
#endif
#endif //_I2C_DRV_H_

View File

@@ -1,4 +1,4 @@
# Build NFC kernel driver # Build NFC kernel driver
ifeq ($(call is-board-platform-in-list, kalama),true) ifeq ($(call is-board-platform-in-list, kalama),true)
BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/nfc_i2c.ko BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/nxp-nci.ko
endif endif

View File

@@ -1 +1 @@
PRODUCT_PACKAGES += nfc_i2c.ko PRODUCT_PACKAGES += nxp-nci.ko

View File

@@ -1,951 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2019-2021 NXP
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
******************************************************************************/
#include <linux/of_gpio.h>
#include <linux/of_device.h>
#include <linux/delay.h>
#include "nfc_common.h"
int nfc_parse_dt(struct device *dev, struct platform_configs *nfc_configs,
uint8_t interface)
{
int ret;
struct device_node *np = dev->of_node;
struct platform_gpio *nfc_gpio = &nfc_configs->gpio;
struct platform_ldo *ldo = &nfc_configs->ldo;
if (!np) {
pr_err("nfc of_node NULL\n");
return -EINVAL;
}
nfc_gpio->irq = -EINVAL;
nfc_gpio->dwl_req = -EINVAL;
nfc_gpio->ven = -EINVAL;
nfc_gpio->clkreq = -EINVAL;
/* required for i2c based chips only */
if (interface == PLATFORM_IF_I2C) {
nfc_gpio->irq = of_get_named_gpio(np, DTS_IRQ_GPIO_STR, 0);
if ((!gpio_is_valid(nfc_gpio->irq))) {
pr_err("nfc irq gpio invalid %d\n", nfc_gpio->irq);
return -EINVAL;
}
pr_info("%s: irq %d\n", __func__, nfc_gpio->irq);
}
nfc_gpio->ven = of_get_named_gpio(np, DTS_VEN_GPIO_STR, 0);
if ((!gpio_is_valid(nfc_gpio->ven))) {
pr_err("nfc ven gpio invalid %d\n", nfc_gpio->ven);
return -EINVAL;
}
nfc_gpio->dwl_req = of_get_named_gpio(np, DTS_FWDN_GPIO_STR, 0);
/* not returning failure for dwl gpio as it is optional for sn220 */
if ((!gpio_is_valid(nfc_gpio->dwl_req)))
pr_warn("nfc dwl_req gpio invalid %d\n", nfc_gpio->dwl_req);
nfc_gpio->clkreq = of_get_named_gpio(np, DTS_CLKREQ_GPIO_STR, 0);
if (!gpio_is_valid(nfc_gpio->clkreq)) {
dev_err(dev, "clkreq gpio invalid %d\n", nfc_gpio->dwl_req);
return -EINVAL;
}
pr_info("%s: ven %d, dwl req %d, clkreq %d\n", __func__,
nfc_gpio->ven, nfc_gpio->dwl_req, nfc_gpio->clkreq);
// optional property
ret = of_property_read_u32_array(np, NFC_LDO_VOL_DT_NAME,
(u32 *) ldo->vdd_levels,
ARRAY_SIZE(ldo->vdd_levels));
if (ret) {
dev_err(dev, "error reading NFC VDDIO min and max value\n");
// set default as per datasheet
ldo->vdd_levels[0] = NFC_VDDIO_MIN;
ldo->vdd_levels[1] = NFC_VDDIO_MAX;
}
// optional property
ret = of_property_read_u32(np, NFC_LDO_CUR_DT_NAME, &ldo->max_current);
if (ret) {
dev_err(dev, "error reading NFC current value\n");
// set default as per datasheet
ldo->max_current = NFC_CURRENT_MAX;
}
return 0;
}
/**
* nfc_ldo_vote()
* @nfc_dev: NFC device containing regulator handle
*
* LDO voting based on voltage and current entries in DT
*
* Return: 0 on success and -ve on failure
*/
int nfc_ldo_vote(struct nfc_dev *nfc_dev)
{
int ret;
ret = regulator_set_voltage(nfc_dev->reg,
nfc_dev->configs.ldo.vdd_levels[0],
nfc_dev->configs.ldo.vdd_levels[1]);
if (ret < 0) {
pr_err("%s: set voltage failed\n", __func__);
return ret;
}
/* pass expected current from NFC in uA */
ret = regulator_set_load(nfc_dev->reg, nfc_dev->configs.ldo.max_current);
if (ret < 0) {
pr_err("%s: set load failed\n", __func__);
return ret;
}
ret = regulator_enable(nfc_dev->reg);
if (ret < 0)
pr_err("%s: regulator_enable failed\n", __func__);
else
nfc_dev->is_vreg_enabled = true;
return ret;
}
/**
* nfc_ldo_config()
* @dev: device instance to read DT entry
* @nfc_dev: NFC device containing regulator handle
*
* Configure LDO if entry is present in DT file otherwise
* return with success as it's optional
*
* Return: 0 on success and -ve on failure
*/
int nfc_ldo_config(struct device *dev, struct nfc_dev *nfc_dev)
{
int ret;
if (of_get_property(dev->of_node, NFC_LDO_SUPPLY_NAME, NULL)) {
// Get the regulator handle
nfc_dev->reg = regulator_get(dev, NFC_LDO_SUPPLY_DT_NAME);
if (IS_ERR(nfc_dev->reg)) {
ret = PTR_ERR(nfc_dev->reg);
nfc_dev->reg = NULL;
pr_err("%s: regulator_get failed, ret = %d\n",
__func__, ret);
return ret;
}
} else {
nfc_dev->reg = NULL;
pr_err("%s: regulator entry not present\n", __func__);
// return success as it's optional to configure LDO
return 0;
}
// LDO config supported by platform DT
ret = nfc_ldo_vote(nfc_dev);
if (ret < 0) {
pr_err("%s: LDO voting failed, ret = %d\n", __func__, ret);
regulator_put(nfc_dev->reg);
}
return ret;
}
/**
* nfc_ldo_unvote()
* @nfc_dev: NFC device containing regulator handle
*
* set voltage and load to zero and disable regulator
*
* Return: 0 on success and -ve on failure
*/
int nfc_ldo_unvote(struct nfc_dev *nfc_dev)
{
int ret;
if (!nfc_dev->is_vreg_enabled) {
pr_err("%s: regulator already disabled\n", __func__);
return -EINVAL;
}
ret = regulator_disable(nfc_dev->reg);
if (ret < 0) {
pr_err("%s: regulator_disable failed\n", __func__);
return ret;
}
nfc_dev->is_vreg_enabled = false;
ret = regulator_set_voltage(nfc_dev->reg, 0, NFC_VDDIO_MAX);
if (ret < 0) {
pr_err("%s: set voltage failed\n", __func__);
return ret;
}
ret = regulator_set_load(nfc_dev->reg, 0);
if (ret < 0)
pr_err("%s: set load failed\n", __func__);
return ret;
}
void set_valid_gpio(int gpio, int value)
{
if (gpio_is_valid(gpio)) {
pr_debug("%s gpio %d value %d\n", __func__, gpio, value);
gpio_set_value(gpio, value);
/* hardware dependent delay */
usleep_range(NFC_GPIO_SET_WAIT_TIME_USEC,
NFC_GPIO_SET_WAIT_TIME_USEC + 100);
}
}
int get_valid_gpio(int gpio)
{
int value = -EINVAL;
if (gpio_is_valid(gpio)) {
value = gpio_get_value(gpio);
pr_debug("%s gpio %d value %d\n", __func__, gpio, value);
}
return value;
}
void gpio_set_ven(struct nfc_dev *nfc_dev, int value)
{
struct platform_gpio *nfc_gpio = &nfc_dev->configs.gpio;
if (gpio_get_value(nfc_gpio->ven) != value) {
pr_debug("%s: value %d\n", __func__, value);
gpio_set_value(nfc_gpio->ven, value);
/* hardware dependent delay */
usleep_range(NFC_GPIO_SET_WAIT_TIME_USEC,
NFC_GPIO_SET_WAIT_TIME_USEC + 100);
}
}
int configure_gpio(unsigned int gpio, int flag)
{
int ret;
pr_debug("%s: nfc gpio [%d] flag [%01x]\n", __func__, gpio, flag);
if (gpio_is_valid(gpio)) {
ret = gpio_request(gpio, "nfc_gpio");
if (ret) {
pr_err("%s: unable to request nfc gpio [%d]\n",
__func__, gpio);
return ret;
}
/* set direction and value for output pin */
if (flag & GPIO_OUTPUT) {
ret = gpio_direction_output(gpio, (GPIO_HIGH & flag));
pr_debug("nfc o/p gpio %d level %d\n", gpio, gpio_get_value(gpio));
} else {
ret = gpio_direction_input(gpio);
pr_debug("nfc i/p gpio %d\n", gpio);
}
if (ret) {
pr_err
("%s: unable to set direction for nfc gpio [%d]\n",
__func__, gpio);
gpio_free(gpio);
return ret;
}
// Consider value as control for input IRQ pin
if (flag & GPIO_IRQ) {
ret = gpio_to_irq(gpio);
if (ret < 0) {
pr_err("%s: unable to set irq for nfc gpio [%d]\n",
__func__, gpio);
gpio_free(gpio);
return ret;
}
pr_debug
("%s: gpio_to_irq successful [%d]\n",
__func__, gpio);
return ret;
}
} else {
pr_err("%s: invalid gpio\n", __func__);
ret = -EINVAL;
}
return ret;
}
void nfc_misc_unregister(struct nfc_dev *nfc_dev, int count)
{
pr_debug("%s: entry\n", __func__);
kfree(nfc_dev->kbuf);
device_destroy(nfc_dev->nfc_class, nfc_dev->devno);
cdev_del(&nfc_dev->c_dev);
class_destroy(nfc_dev->nfc_class);
unregister_chrdev_region(nfc_dev->devno, count);
if (nfc_dev->ipcl)
ipc_log_context_destroy(nfc_dev->ipcl);
}
int nfc_misc_register(struct nfc_dev *nfc_dev,
const struct file_operations *nfc_fops, int count,
char *devname, char *classname)
{
int ret = 0;
ret = alloc_chrdev_region(&nfc_dev->devno, 0, count, devname);
if (ret < 0) {
pr_err("%s: failed to alloc chrdev region ret %d\n",
__func__, ret);
return ret;
}
nfc_dev->nfc_class = class_create(THIS_MODULE, classname);
if (IS_ERR(nfc_dev->nfc_class)) {
ret = PTR_ERR(nfc_dev->nfc_class);
pr_err("%s: failed to register device class ret %d\n",
__func__, ret);
unregister_chrdev_region(nfc_dev->devno, count);
return ret;
}
cdev_init(&nfc_dev->c_dev, nfc_fops);
ret = cdev_add(&nfc_dev->c_dev, nfc_dev->devno, count);
if (ret < 0) {
pr_err("%s: failed to add cdev ret %d\n", __func__, ret);
class_destroy(nfc_dev->nfc_class);
unregister_chrdev_region(nfc_dev->devno, count);
return ret;
}
nfc_dev->nfc_device = device_create(nfc_dev->nfc_class, NULL,
nfc_dev->devno, nfc_dev, devname);
if (IS_ERR(nfc_dev->nfc_device)) {
ret = PTR_ERR(nfc_dev->nfc_device);
pr_err("%s: failed to create the device ret %d\n",
__func__, ret);
cdev_del(&nfc_dev->c_dev);
class_destroy(nfc_dev->nfc_class);
unregister_chrdev_region(nfc_dev->devno, count);
return ret;
}
nfc_dev->ipcl = ipc_log_context_create(NUM_OF_IPC_LOG_PAGES,
dev_name(nfc_dev->nfc_device), 0);
nfc_dev->kbuflen = MAX_BUFFER_SIZE;
nfc_dev->kbuf = kzalloc(MAX_BUFFER_SIZE, GFP_KERNEL | GFP_DMA);
if (!nfc_dev->kbuf) {
nfc_misc_unregister(nfc_dev, count);
return -ENOMEM;
}
nfc_dev->cold_reset.rsp_pending = false;
nfc_dev->cold_reset.is_nfc_enabled = false;
nfc_dev->cold_reset.is_crp_en = false;
nfc_dev->cold_reset.last_src_ese_prot = ESE_COLD_RESET_ORIGIN_NONE;
init_waitqueue_head(&nfc_dev->cold_reset.read_wq);
return 0;
}
/*
* Power management of the eSE
* eSE and NFCC both are powered using VEN gpio,
* VEN HIGH - eSE and NFCC both are powered on
* VEN LOW - eSE and NFCC both are power down
*/
int nfc_ese_pwr(struct nfc_dev *nfc_dev, unsigned long arg)
{
int ret = 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,
* to find out if NFC MW also sent request to set VEN HIGH
* VEN state will remain HIGH if NFC is enabled otherwise
* it will be set as LOW
*/
nfc_dev->nfc_ven_enabled = gpio_get_value(nfc_dev->configs.gpio.ven);
if (!nfc_dev->nfc_ven_enabled) {
pr_debug("eSE HAL service setting ven HIGH\n");
gpio_set_ven(nfc_dev, 1);
} else {
pr_debug("ven already HIGH\n");
}
nfc_dev->is_ese_session_active = true;
} else if (arg == ESE_POWER_OFF) {
if (!nfc_dev->nfc_ven_enabled) {
pr_debug("NFC not enabled, disabling ven\n");
gpio_set_ven(nfc_dev, 0);
} else {
pr_debug("keep ven high as NFC is enabled\n");
}
nfc_dev->is_ese_session_active = false;
} else if (arg == ESE_POWER_STATE) {
/* get VEN gpio state for eSE, as eSE also enabled through same GPIO */
ret = gpio_get_value(nfc_dev->configs.gpio.ven);
} else {
pr_err("%s bad arg %lu\n", __func__, arg);
ret = -ENOIOCTLCMD;
}
return ret;
}
/*
* nfc_ioctl_power_states() - power control
* @nfc_dev: nfc device data structure
* @arg: mode that we want to move to
*
* Device power control. Depending on the arg value, device moves to
* different states, refer nfcc_ioctl_request in nfc_common.h for args
*
* Return: -ENOIOCTLCMD if arg is not supported, 0 in any other case
*/
static int nfc_ioctl_power_states(struct nfc_dev *nfc_dev, unsigned long arg)
{
int ret = 0;
struct platform_gpio *nfc_gpio = &nfc_dev->configs.gpio;
if (arg == NFC_POWER_OFF) {
/*
* We are attempting a hardware reset so let us disable
* interrupts to avoid spurious notifications to upper
* layers.
*/
nfc_dev->nfc_disable_intr(nfc_dev);
set_valid_gpio(nfc_gpio->dwl_req, 0);
gpio_set_ven(nfc_dev, 0);
nfc_dev->nfc_ven_enabled = false;
} else if (arg == NFC_POWER_ON) {
nfc_dev->nfc_enable_intr(nfc_dev);
set_valid_gpio(nfc_gpio->dwl_req, 0);
gpio_set_ven(nfc_dev, 1);
nfc_dev->nfc_ven_enabled = true;
} else if (arg == NFC_FW_DWL_VEN_TOGGLE) {
/*
* We are switching to download Mode, toggle the enable pin
* in order to set the NFCC in the new mode
*/
nfc_dev->nfc_disable_intr(nfc_dev);
set_valid_gpio(nfc_gpio->dwl_req, 1);
nfc_dev->nfc_state = NFC_STATE_FW_DWL;
gpio_set_ven(nfc_dev, 0);
gpio_set_ven(nfc_dev, 1);
nfc_dev->nfc_enable_intr(nfc_dev);
} else if (arg == NFC_FW_DWL_HIGH) {
/*
* Setting firmware download gpio to HIGH
* before FW download start
*/
pr_debug("set fw gpio high\n");
set_valid_gpio(nfc_gpio->dwl_req, 1);
nfc_dev->nfc_state = NFC_STATE_FW_DWL;
} else if (arg == NFC_VEN_FORCED_HARD_RESET) {
nfc_dev->nfc_disable_intr(nfc_dev);
gpio_set_ven(nfc_dev, 0);
gpio_set_ven(nfc_dev, 1);
nfc_dev->nfc_enable_intr(nfc_dev);
pr_info("%s VEN forced reset done\n", __func__);
} else if (arg == NFC_FW_DWL_LOW) {
/*
* Setting firmware download gpio to LOW
* FW download finished
*/
pr_debug("set fw gpio LOW\n");
set_valid_gpio(nfc_gpio->dwl_req, 0);
nfc_dev->nfc_state = NFC_STATE_NCI;
} else if (arg == NFC_ENABLE) {
/*
* Setting flag true when NFC is enabled
*/
nfc_dev->cold_reset.is_nfc_enabled = true;
} else if (arg == NFC_DISABLE) {
/*
* Setting flag true when NFC is disabled
*/
nfc_dev->cold_reset.is_nfc_enabled = false;
} else {
pr_err("%s bad arg %lu\n", __func__, arg);
ret = -ENOIOCTLCMD;
}
return ret;
}
/*
* Inside nfc_ioctl_nfcc_info
*
* @brief nfc_ioctl_nfcc_info
*
* Check the NFC Chipset and firmware version details
*/
unsigned int nfc_ioctl_nfcc_info(struct file *filp, unsigned long arg)
{
unsigned int r = 0;
struct nfc_dev *nfc_dev = filp->private_data;
r = nfc_dev->nqx_info.i;
pr_debug("nfc : %s r = 0x%x\n", __func__, r);
return r;
}
/** @brief IOCTL function to be used to set or get data from upper layer.
*
* @param pfile fil node for opened device.
* @cmd IOCTL type from upper layer.
* @arg IOCTL arg from upper layer.
*
* @return 0 on success, error code for failures.
*/
long nfc_dev_ioctl(struct file *pfile, unsigned int cmd, unsigned long arg)
{
int ret = 0;
struct nfc_dev *nfc_dev = pfile->private_data;
if (!nfc_dev)
return -ENODEV;
pr_debug("%s cmd = %x arg = %zx\n", __func__, cmd, arg);
switch (cmd) {
case NFC_SET_PWR:
ret = nfc_ioctl_power_states(nfc_dev, arg);
break;
case ESE_SET_PWR:
ret = nfc_ese_pwr(nfc_dev, arg);
break;
case ESE_GET_PWR:
ret = nfc_ese_pwr(nfc_dev, ESE_POWER_STATE);
break;
case NFCC_GET_INFO:
ret = nfc_ioctl_nfcc_info(pfile, arg);
break;
case NFC_GET_PLATFORM_TYPE:
ret = nfc_dev->interface;
break;
case ESE_COLD_RESET:
pr_debug("nfc ese cold reset ioctl\n");
ret = ese_cold_reset_ioctl(nfc_dev, arg);
break;
case NFC_GET_IRQ_STATE:
ret = gpio_get_value(nfc_dev->configs.gpio.irq);
break;
default:
pr_err("%s Unsupported ioctl cmd 0x%x, arg %lu\n",
__func__, cmd, arg);
ret = -ENOIOCTLCMD;
}
return ret;
}
int nfc_dev_open(struct inode *inode, struct file *filp)
{
struct nfc_dev *nfc_dev = container_of(inode->i_cdev,
struct nfc_dev, c_dev);
if (!nfc_dev)
return -ENODEV;
pr_debug("%s: %d, %d\n", __func__, imajor(inode), iminor(inode));
mutex_lock(&nfc_dev->dev_ref_mutex);
filp->private_data = nfc_dev;
if (nfc_dev->dev_ref_count == 0) {
set_valid_gpio(nfc_dev->configs.gpio.dwl_req, 0);
nfc_dev->nfc_enable_intr(nfc_dev);
}
nfc_dev->dev_ref_count = nfc_dev->dev_ref_count + 1;
mutex_unlock(&nfc_dev->dev_ref_mutex);
return 0;
}
int nfc_dev_close(struct inode *inode, struct file *filp)
{
struct nfc_dev *nfc_dev = container_of(inode->i_cdev,
struct nfc_dev, c_dev);
if (!nfc_dev)
return -ENODEV;
pr_debug("%s: %d, %d\n", __func__, imajor(inode), iminor(inode));
mutex_lock(&nfc_dev->dev_ref_mutex);
if (nfc_dev->dev_ref_count == 1) {
nfc_dev->nfc_disable_intr(nfc_dev);
set_valid_gpio(nfc_dev->configs.gpio.dwl_req, 0);
}
if (nfc_dev->dev_ref_count > 0)
nfc_dev->dev_ref_count = nfc_dev->dev_ref_count - 1;
filp->private_data = NULL;
mutex_unlock(&nfc_dev->dev_ref_mutex);
return 0;
}
int is_nfc_data_available_for_read(struct nfc_dev *nfc_dev)
{
int ret;
nfc_dev->nfc_enable_intr(nfc_dev);
ret = wait_event_interruptible_timeout(nfc_dev->read_wq,
!nfc_dev->i2c_dev.irq_enabled,
msecs_to_jiffies(MAX_IRQ_WAIT_TIME));
return ret;
}
/**
* get_nfcc_chip_type_dl() - get chip type in fw download command;
* @nfc_dev: nfc device data structure
*
* Perform get version command and determine chip
* type from response.
*
* @Return: enum chip_types value
*
*/
static enum chip_types get_nfcc_chip_type_dl(struct nfc_dev *nfc_dev)
{
int ret = 0;
uint8_t *cmd = nfc_dev->write_kbuf;
uint8_t *rsp = nfc_dev->read_kbuf;
enum chip_types chip_type = CHIP_UNKNOWN;
*cmd++ = DL_CMD;
*cmd++ = DL_GET_VERSION_CMD_PAYLOAD_LEN;
*cmd++ = DL_GET_VERSION_CMD_ID;
*cmd++ = DL_PAYLOAD_BYTE_ZERO;
*cmd++ = DL_PAYLOAD_BYTE_ZERO;
*cmd++ = DL_PAYLOAD_BYTE_ZERO;
*cmd++ = DL_GET_VERSION_CMD_CRC_1;
*cmd++ = DL_GET_VERSION_CMD_CRC_2;
pr_debug("%s:Sending GET_VERSION cmd of size = %d\n", __func__, DL_GET_VERSION_CMD_LEN);
ret = nfc_dev->nfc_write(nfc_dev, nfc_dev->write_kbuf, DL_GET_VERSION_CMD_LEN,
MAX_RETRY_COUNT);
if (ret <= 0) {
pr_err("%s: - nfc get version cmd error ret %d\n", __func__, ret);
goto err;
}
memset(rsp, 0x00, DL_GET_VERSION_RSP_LEN_2);
pr_debug("%s:Reading response of GET_VERSION cmd\n", __func__);
ret = nfc_dev->nfc_read(nfc_dev, rsp, DL_GET_VERSION_RSP_LEN_2, NCI_CMD_RSP_TIMEOUT);
if (ret <= 0) {
pr_err("%s: - nfc get version rsp error ret %d\n", __func__, ret);
goto err;
}
if (rsp[0] == FW_MSG_CMD_RSP && ret >= DL_GET_VERSION_RSP_LEN_2) {
nfc_dev->fw_major_version = rsp[FW_MAJOR_VER_OFFSET];
if (rsp[FW_ROM_CODE_VER_OFFSET] == SN1XX_ROM_VER &&
rsp[FW_MAJOR_VER_OFFSET] == SN1xx_MAJOR_VER)
chip_type = CHIP_SN1XX;
else if (rsp[FW_ROM_CODE_VER_OFFSET] == SN220_ROM_VER &&
rsp[FW_MAJOR_VER_OFFSET] == SN220_MAJOR_VER)
chip_type = CHIP_SN220;
pr_debug("%s:NFC Chip Type 0x%02x Rom Version 0x%02x FW Minor 0x%02x Major 0x%02x\n",
__func__, rsp[GET_VERSION_RSP_CHIP_TYPE_OFFSET],
rsp[FW_ROM_CODE_VER_OFFSET],
rsp[GET_VERSION_RSP_MINOR_VERSION_OFFSET],
rsp[FW_MAJOR_VER_OFFSET]);
nfc_dev->nqx_info.info.chip_type = rsp[GET_VERSION_RSP_CHIP_TYPE_OFFSET];
nfc_dev->nqx_info.info.rom_version = rsp[FW_ROM_CODE_VER_OFFSET];
nfc_dev->nqx_info.info.fw_minor = rsp[GET_VERSION_RSP_MINOR_VERSION_OFFSET];
nfc_dev->nqx_info.info.fw_major = rsp[FW_MAJOR_VER_OFFSET];
}
err:
return chip_type;
}
/**
* get_nfcc_session_state_dl() - gets the session state
* @nfc_dev: nfc device data structure
*
* Performs get session command and determine
* the nfcc state based on session status.
*
* @Return nfcc state based on session status.
* NFC_STATE_FW_TEARED if sessionis not closed
* NFC_STATE_FW_DWL if session closed
* NFC_STATE_UNKNOWN in error cases.
*/
enum nfc_state_flags get_nfcc_session_state_dl(struct nfc_dev *nfc_dev)
{
int ret = 0;
uint8_t *cmd = nfc_dev->write_kbuf;
uint8_t *rsp = nfc_dev->read_kbuf;
enum nfc_state_flags nfc_state = NFC_STATE_UNKNOWN;
*cmd++ = DL_CMD;
*cmd++ = DL_GET_SESSION_STATE_CMD_PAYLOAD_LEN;
*cmd++ = DL_GET_SESSION_CMD_ID;
*cmd++ = DL_PAYLOAD_BYTE_ZERO;
*cmd++ = DL_PAYLOAD_BYTE_ZERO;
*cmd++ = DL_PAYLOAD_BYTE_ZERO;
*cmd++ = DL_GET_SESSION_CMD_CRC_1;
*cmd++ = DL_GET_SESSION_CMD_CRC_2;
pr_debug("%s:Sending GET_SESSION_STATE cmd of size = %d\n", __func__,
DL_GET_SESSION_STATE_CMD_LEN);
ret = nfc_dev->nfc_write(nfc_dev, nfc_dev->write_kbuf, DL_GET_SESSION_STATE_CMD_LEN,
MAX_RETRY_COUNT);
if (ret <= 0) {
pr_err("%s: - nfc get session cmd error ret %d\n", __func__, ret);
goto err;
}
memset(rsp, 0x00, DL_GET_SESSION_STATE_RSP_LEN);
pr_debug("%s:Reading response of GET_SESSION_STATE cmd\n", __func__);
ret = nfc_dev->nfc_read(nfc_dev, rsp, DL_GET_SESSION_STATE_RSP_LEN, NCI_CMD_RSP_TIMEOUT);
if (ret <= 0) {
pr_err("%s: - nfc get session rsp error ret %d\n", __func__, ret);
goto err;
}
if (rsp[0] != FW_MSG_CMD_RSP) {
pr_err("%s: - nfc invalid get session state rsp\n", __func__);
goto err;
}
pr_debug("Response bytes are %02x%02x%02x%02x%02x%02x%02x%02x\n",
rsp[0], rsp[1], rsp[2], rsp[3], rsp[4], rsp[5], rsp[6], rsp[7]);
/*verify fw in non-teared state */
if (rsp[GET_SESSION_STS_OFF] != NFCC_SESSION_STS_CLOSED) {
pr_err("%s NFCC booted in FW teared state\n", __func__);
nfc_state = NFC_STATE_FW_TEARED;
} else {
pr_info("%s NFCC booted in FW DN mode\n", __func__);
nfc_state = NFC_STATE_FW_DWL;
}
err:
return nfc_state;
}
/**
* get_nfcc_chip_type() - get nfcc chip type in nci mode.
* @nfc_dev: nfc device data structure.
*
* Function to perform nci core reset and extract
* chip type from the response.
*
* @Return: enum chip_types value
*
*/
static enum chip_types get_nfcc_chip_type(struct nfc_dev *nfc_dev)
{
int ret = 0;
uint8_t major_version = 0;
uint8_t rom_version = 0;
uint8_t *cmd = nfc_dev->write_kbuf;
uint8_t *rsp = nfc_dev->read_kbuf;
enum chip_types chip_type = CHIP_UNKNOWN;
*cmd++ = NCI_MSG_CMD;
*cmd++ = NCI_CORE_RESET_CMD_OID;
*cmd++ = NCI_CORE_RESET_CMD_PAYLOAD_LEN;
*cmd++ = NCI_CORE_RESET_KEEP_CONFIG;
pr_debug("%s:Sending NCI Core Reset cmd of size = %d\n", __func__, NCI_RESET_CMD_LEN);
ret = nfc_dev->nfc_write(nfc_dev, nfc_dev->write_kbuf, NCI_RESET_CMD_LEN, NO_RETRY);
if (ret <= 0) {
pr_err("%s: - nfc nci core reset cmd error ret %d\n", __func__, ret);
goto err;
}
/* to flush out debug NTF this delay is required */
usleep_range(NCI_RESET_RESP_READ_DELAY, NCI_RESET_RESP_READ_DELAY + 100);
nfc_dev->nfc_enable_intr(nfc_dev);
memset(rsp, 0x00, NCI_RESET_RSP_LEN);
pr_debug("%s:Reading NCI Core Reset rsp\n", __func__);
ret = nfc_dev->nfc_read(nfc_dev, rsp, NCI_RESET_RSP_LEN, NCI_CMD_RSP_TIMEOUT);
if (ret <= 0) {
pr_err("%s: - nfc nci core reset rsp error ret %d\n", __func__, ret);
goto err_disable_intr;
}
pr_debug(" %s: nci core reset response 0x%02x%02x%02x%02x\n",
__func__, rsp[0], rsp[1], rsp[2], rsp[3]);
if (rsp[0] != NCI_MSG_RSP) {
/* reset response failed response*/
pr_err("%s invalid nci core reset response\n", __func__);
goto err_disable_intr;
}
memset(rsp, 0x00, NCI_RESET_NTF_LEN);
/* read nci rest response ntf */
ret = nfc_dev->nfc_read(nfc_dev, rsp, NCI_RESET_NTF_LEN, NCI_CMD_RSP_TIMEOUT);
if (ret <= 0) {
pr_err("%s - nfc nci rest rsp ntf error status %d\n", __func__, ret);
goto err_disable_intr;
}
if (rsp[0] == NCI_MSG_NTF) {
/* read version info from NCI Reset Notification */
rom_version = rsp[NCI_HDR_LEN + rsp[NCI_PAYLOAD_LEN_IDX] - 3];
major_version = rsp[NCI_HDR_LEN + rsp[NCI_PAYLOAD_LEN_IDX] - 2];
/* determine chip type based on version info */
if (rom_version == SN1XX_ROM_VER && major_version == SN1xx_MAJOR_VER)
chip_type = CHIP_SN1XX;
else if (rom_version == SN220_ROM_VER && major_version == SN220_MAJOR_VER)
chip_type = CHIP_SN220;
pr_debug(" %s:NCI Core Reset ntf 0x%02x%02x%02x%02x\n",
__func__, rsp[0], rsp[1], rsp[2], rsp[3]);
nfc_dev->nqx_info.info.chip_type = rsp[NCI_HDR_LEN + rsp[NCI_PAYLOAD_LEN_IDX] -
NFC_CHIP_TYPE_OFF];
nfc_dev->nqx_info.info.rom_version = rom_version;
nfc_dev->nqx_info.info.fw_major = major_version;
nfc_dev->nqx_info.info.fw_minor = rsp[NCI_HDR_LEN + rsp[NCI_PAYLOAD_LEN_IDX] -
NFC_FW_MINOR_OFF];
}
err_disable_intr:
nfc_dev->nfc_disable_intr(nfc_dev);
err:
return chip_type;
}
/**
* validate_download_gpio() - validate download gpio.
* @nfc_dev: nfc_dev device data structure.
* @chip_type: chip type of the platform.
*
* Validates dwnld gpio should configured for supported and
* should not be configured for unsupported platform.
*
* @Return: true if gpio validation successful ortherwise
* false if validation fails.
*/
static bool validate_download_gpio(struct nfc_dev *nfc_dev, enum chip_types chip_type)
{
bool status = false;
struct platform_gpio *nfc_gpio;
if (nfc_dev == NULL) {
pr_err("%s nfc devices structure is null\n");
return status;
}
nfc_gpio = &nfc_dev->configs.gpio;
if (chip_type == CHIP_SN1XX) {
/* gpio should be configured for SN1xx */
status = gpio_is_valid(nfc_gpio->dwl_req);
} else if (chip_type == CHIP_SN220) {
/* gpio should not be configured for SN220 */
set_valid_gpio(nfc_gpio->dwl_req, 0);
gpio_free(nfc_gpio->dwl_req);
nfc_gpio->dwl_req = -EINVAL;
status = true;
}
return status;
}
/* Check for availability of NFC controller hardware */
int nfcc_hw_check(struct nfc_dev *nfc_dev)
{
int ret = 0;
enum nfc_state_flags nfc_state = NFC_STATE_UNKNOWN;
enum chip_types chip_type = CHIP_UNKNOWN;
struct platform_gpio *nfc_gpio = &nfc_dev->configs.gpio;
/*get fw version in nci mode*/
gpio_set_ven(nfc_dev, 1);
gpio_set_ven(nfc_dev, 0);
gpio_set_ven(nfc_dev, 1);
chip_type = get_nfcc_chip_type(nfc_dev);
/*get fw version in fw dwl mode*/
if (chip_type == CHIP_UNKNOWN) {
nfc_dev->nfc_enable_intr(nfc_dev);
/*Chip is unknown, initially assume with fw dwl pin enabled*/
set_valid_gpio(nfc_gpio->dwl_req, 1);
gpio_set_ven(nfc_dev, 0);
gpio_set_ven(nfc_dev, 1);
chip_type = get_nfcc_chip_type_dl(nfc_dev);
/*get the state of nfcc normal/teared in fw dwl mode*/
} else {
nfc_state = NFC_STATE_NCI;
}
/*validate gpio config required as per the chip*/
if (!validate_download_gpio(nfc_dev, chip_type)) {
pr_info("%s gpio validation fail\n", __func__);
ret = -ENXIO;
goto err;
}
/*check whether the NFCC is in FW DN or Teared state*/
if (nfc_state != NFC_STATE_NCI)
nfc_state = get_nfcc_session_state_dl(nfc_dev);
/*nfcc state specific operations */
switch (nfc_state) {
case NFC_STATE_FW_TEARED:
pr_warn("%s: - NFCC FW Teared State\n", __func__);
case NFC_STATE_FW_DWL:
case NFC_STATE_NCI:
break;
case NFC_STATE_UNKNOWN:
default:
ret = -ENXIO;
pr_err("%s: - NFCC HW not available\n", __func__);
goto err;
}
nfc_dev->nfc_state = nfc_state;
err:
nfc_dev->nfc_disable_intr(nfc_dev);
set_valid_gpio(nfc_gpio->dwl_req, 0);
gpio_set_ven(nfc_dev, 0);
gpio_set_ven(nfc_dev, 1);
nfc_dev->nfc_ven_enabled = true;
return ret;
}
int validate_nfc_state_nci(struct nfc_dev *nfc_dev)
{
struct platform_gpio *nfc_gpio = &nfc_dev->configs.gpio;
if (!gpio_get_value(nfc_gpio->ven)) {
pr_err("VEN LOW - NFCC powered off\n");
return -ENODEV;
}
if (get_valid_gpio(nfc_gpio->dwl_req) == 1) {
pr_err("FW download in-progress\n");
return -EBUSY;
}
if (nfc_dev->nfc_state != NFC_STATE_NCI) {
pr_err("FW download state\n");
return -EBUSY;
}
return 0;
}

View File

@@ -1,346 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2019-2021 NXP
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
******************************************************************************/
#ifndef _NFC_COMMON_H_
#define _NFC_COMMON_H_
#include <linux/types.h>
#include <linux/version.h>
#include <linux/semaphore.h>
#include <linux/completion.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/spinlock.h>
#include <linux/gpio.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <nfcinfo.h>
#include <linux/regulator/consumer.h>
#include <linux/ipc_logging.h>
#include "nfc_i2c_drv.h"
#include "ese_cold_reset.h"
// Max device count for this driver
#define DEV_COUNT 1
// NFC device class
#define CLASS_NAME "qti-nfc"
// NFC character device name, this will be in /dev/
#define NFC_CHAR_DEV_NAME "nq-nci"
// NCI packet details
#define NCI_MSG_CMD 0x20
#define NCI_MSG_RSP 0x40
#define NCI_MSG_NTF 0x60
#define DL_CMD 0x00
#define DL_PAYLOAD_BYTE_ZERO 0x00
#define NCI_HDR_LEN 3
#define NCI_PAYLOAD_IDX 3
#define NCI_PAYLOAD_LEN_IDX 2
/*Time to wait for first NCI rest response*/
#define NCI_RESET_RESP_READ_DELAY (10000) // 10ms
#define NCI_RESET_RESP_TIMEOUT (500) // 500ms
// FW DNLD packet details
#define FW_MSG_CMD_RSP 0x00
#define FW_HDR_LEN 2
#define FW_PAYLOAD_LEN_IDX 1
#define FW_CRC_LEN 2
#define NCI_RSP_PKT_TYPE (0x40)
#define FW_MIN_PAYLOAD_LEN 4
#define MIN_NFC_DL_FRAME_SIZE 3
#define NCI_RESET_CMD_LEN (4)
#define NCI_RESET_RSP_LEN (4)
#define NCI_CORE_RESET_CMD_OID (0x0)
#define NCI_CORE_RESET_CMD_PAYLOAD_LEN (0x1)
#define NCI_CORE_RESET_KEEP_CONFIG (0x0)
#define NCI_RESET_NTF_LEN (13)
#define SN1XX_ROM_VER 0x01
#define SN1xx_MAJOR_VER 0x10
#define SN220_ROM_VER 0x01
#define SN220_MAJOR_VER 0x01
#define FW_ROM_CODE_VER_OFFSET 4
#define FW_MAJOR_VER_OFFSET 7
#define GET_VERSION_RSP_CHIP_TYPE_OFFSET 3
#define GET_VERSION_RSP_MINOR_VERSION_OFFSET 6
#define DL_GET_VERSION_CMD_LEN (8)
#define DL_GET_VERSION_RSP_LEN_1 (12) /* SN110 */
#define DL_GET_VERSION_RSP_LEN_2 (20) /* SN220 */
#define DL_GET_VERSION_CMD_PAYLOAD_LEN (4)
#define DL_GET_VERSION_CMD_ID (0xF1)
#define DL_GET_VERSION_CMD_CRC_1 (0x6E)
#define DL_GET_VERSION_CMD_CRC_2 (0xEF)
#define DL_RESET_CMD_LEN (8)
#define DL_GET_SESSION_STATE_CMD_LEN (8)
#define DL_GET_SESSION_STATE_RSP_LEN (8)
#define DL_GET_SESSION_STATE_CMD_PAYLOAD_LEN (4)
#define DL_GET_SESSION_CMD_ID (0xF2)
#define DL_GET_SESSION_CMD_CRC_1 (0xF5)
#define DL_GET_SESSION_CMD_CRC_2 (0x33)
#define GET_SESSION_STS_OFF (3)
#define NFCC_SESSION_STS_CLOSED (0x0)
/* Below offsets should be subtracted from NCI header length + payload length */
#define NFC_CHIP_TYPE_OFF (4)
#define NFC_FW_MINOR_OFF (1)
#define GET_VERSION_CMD_LEN 8
#define GET_SESSION_STATE_CMD_LEN 8
#define MAX_NCI_PAYLOAD_LEN (255)
#define MAX_BUFFER_SIZE (NCI_HDR_LEN + MAX_NCI_PAYLOAD_LEN)
/*
* From MW 11.04 buffer size increased to support
* frame size of 554 in FW download mode
* Frame len(2) + Frame Header(6) + DATA(512) + HASH(32) + CRC(2) + RFU(4)
*/
#define MAX_DL_PAYLOAD_LEN (550)
#define MAX_DL_BUFFER_SIZE (FW_HDR_LEN + FW_CRC_LEN + MAX_DL_PAYLOAD_LEN)
// Maximum retry count for standby writes
#define MAX_RETRY_COUNT (3)
// Retry count for normal write
#define NO_RETRY (1)
#define MAX_IRQ_WAIT_TIME (90)
#define WAKEUP_SRC_TIMEOUT (2000)
/*command response timeout*/
#define NCI_CMD_RSP_TIMEOUT (2000) //2s
/*Time to wait for NFCC to be ready again after any change in the GPIO*/
#define NFC_GPIO_SET_WAIT_TIME_USEC (10000)
/*Time to wait after soft reset via any NCI/DL cmd*/
#define NFC_SOFT_RESET_WAIT_TIME_USEC (5000)
/*Time to wait before retrying i2c writes*/
#define WRITE_RETRY_WAIT_TIME_USEC (1000)
/*Time to wait before retrying read for some specific usecases*/
#define READ_RETRY_WAIT_TIME_USEC (3500)
#define NFC_MAGIC 0xE9
// Ioctls
// The type should be aligned with MW HAL definitions
#define NFC_SET_PWR _IOW(NFC_MAGIC, 0x01, unsigned int)
#define ESE_SET_PWR _IOW(NFC_MAGIC, 0x02, unsigned int)
#define ESE_GET_PWR _IOR(NFC_MAGIC, 0x03, unsigned int)
#define NFC_GET_PLATFORM_TYPE _IO(NFC_MAGIC, 0x04)
/* NFC HAL can call this ioctl to get the current IRQ state */
#define NFC_GET_IRQ_STATE _IO(NFC_MAGIC, 0x06)
#define DTS_IRQ_GPIO_STR "qcom,sn-irq"
#define DTS_VEN_GPIO_STR "qcom,sn-ven"
#define DTS_FWDN_GPIO_STR "qcom,sn-firm"
#define DTS_CLKREQ_GPIO_STR "qcom,sn-clkreq"
#define DTS_CLKSRC_GPIO_STR "qcom,clk-src"
#define NFC_LDO_SUPPLY_DT_NAME "qcom,sn-vdd-1p8"
#define NFC_LDO_SUPPLY_NAME "qcom,sn-vdd-1p8-supply"
#define NFC_LDO_VOL_DT_NAME "qcom,sn-vdd-1p8-voltage"
#define NFC_LDO_CUR_DT_NAME "qcom,sn-vdd-1p8-current"
//as per SN1x0 datasheet
#define NFC_VDDIO_MIN 1650000 //in uV
#define NFC_VDDIO_MAX 1950000 //in uV
#define NFC_CURRENT_MAX 157000 //in uA
#define NUM_OF_IPC_LOG_PAGES (2)
#define PKT_MAX_LEN (4) // no of max bytes to print for cmd/resp
#define GET_IPCLOG_MAX_PKT_LEN(c) ((c > PKT_MAX_LEN) ? PKT_MAX_LEN : c)
#define NFCLOG_IPC(nfc_dev, log_to_dmesg, x...) \
do { \
ipc_log_string(nfc_dev->ipcl, x); \
if (log_to_dmesg) { \
if (nfc_dev->nfc_device) \
dev_err((nfc_dev->nfc_device), x); \
else \
pr_err(x); \
} \
} while (0)
enum ese_ioctl_request {
/* eSE POWER ON */
ESE_POWER_ON = 0,
/* eSE POWER OFF */
ESE_POWER_OFF,
/* 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,
/* VEN hard reset request */
NFC_VEN_FORCED_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,
};
/*nfc platform interface type*/
enum interface_flags {
/*I2C physical IF for NFCC */
PLATFORM_IF_I2C = 0,
};
/*nfc state flags*/
enum nfc_state_flags {
/*nfc in unknown state */
NFC_STATE_UNKNOWN = 0,
/*nfc in download mode */
NFC_STATE_FW_DWL = 0x1,
/*nfc booted in NCI mode */
NFC_STATE_NCI = 0x2,
/*nfc booted in Fw teared mode */
NFC_STATE_FW_TEARED = 0x4,
};
/*
* Power state for IBI handing, mainly needed to defer the IBI handling
* for the IBI received in suspend state to do it later in resume call
*/
enum pm_state_flags {
PM_STATE_NORMAL = 0,
PM_STATE_SUSPEND,
PM_STATE_IBI_BEFORE_RESUME,
};
/* Enum for GPIO values*/
enum gpio_values {
GPIO_INPUT = 0x0,
GPIO_OUTPUT = 0x1,
GPIO_HIGH = 0x2,
GPIO_OUTPUT_HIGH = 0x3,
GPIO_IRQ = 0x4,
};
// NFC GPIO variables
struct platform_gpio {
unsigned int irq;
unsigned int ven;
unsigned int clkreq;
unsigned int dwl_req;
};
// NFC LDO entries from DT
struct platform_ldo {
int vdd_levels[2];
int max_current;
};
// NFC Struct to get all the required configs from DTS
struct platform_configs {
struct platform_gpio gpio;
struct platform_ldo ldo;
};
enum chip_types {
CHIP_SN1XX = 0x01,
CHIP_SN220 = 0x02,
CHIP_UNKNOWN = 0xFF,
};
/* Device specific structure */
struct nfc_dev {
wait_queue_head_t read_wq;
struct mutex read_mutex;
struct mutex write_mutex;
uint8_t *read_kbuf;
uint8_t *write_kbuf;
struct mutex dev_ref_mutex;
unsigned int dev_ref_count;
struct class *nfc_class;
struct device *nfc_device;
struct cdev c_dev;
dev_t devno;
/* Interface flag */
uint8_t interface;
/* nfc state flags */
uint8_t nfc_state;
/* NFC VEN pin state */
bool nfc_ven_enabled;
/* current firmware major version */
uint8_t fw_major_version;
bool is_vreg_enabled;
bool is_ese_session_active;
struct i2c_dev i2c_dev;
struct platform_configs configs;
struct cold_reset cold_reset;
struct regulator *reg;
/* read buffer*/
size_t kbuflen;
u8 *kbuf;
union nqx_uinfo nqx_info;
void *ipcl;
int (*nfc_read)(struct nfc_dev *dev, char *buf, size_t count, int timeout);
int (*nfc_write)(struct nfc_dev *dev, const char *buf, const size_t count,
int max_retry_cnt);
int (*nfc_enable_intr)(struct nfc_dev *dev);
int (*nfc_disable_intr)(struct nfc_dev *dev);
};
int nfc_dev_open(struct inode *inode, struct file *filp);
int nfc_dev_close(struct inode *inode, struct file *filp);
long nfc_dev_ioctl(struct file *pfile, unsigned int cmd, unsigned long arg);
int nfc_parse_dt(struct device *dev, struct platform_configs *nfc_configs,
uint8_t interface);
int nfc_misc_register(struct nfc_dev *nfc_dev,
const struct file_operations *nfc_fops, int count, char *devname,
char *classname);
void nfc_misc_unregister(struct nfc_dev *nfc_dev, int count);
int configure_gpio(unsigned int gpio, int flag);
void gpio_set_ven(struct nfc_dev *nfc_dev, int value);
int nfcc_hw_check(struct nfc_dev *nfc_dev);
int nfc_ldo_config(struct device *dev, struct nfc_dev *nfc_dev);
int nfc_ldo_vote(struct nfc_dev *nfc_dev);
int nfc_ldo_unvote(struct nfc_dev *nfc_dev);
int is_nfc_data_available_for_read(struct nfc_dev *nfc_dev);
int validate_nfc_state_nci(struct nfc_dev *nfc_dev);
void set_nfcc_state_from_rsp(struct nfc_dev *dev, const char *buf,
const int count);
void enable_dwnld_mode(struct nfc_dev *nfc_dev, bool value);
#endif //_NFC_COMMON_H_

View File

@@ -1,577 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2013-2021 NXP
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
******************************************************************************/
/*
* Copyright (C) 2010 Trusted Logic S.A.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "nfc_common.h"
/**
* i2c_disable_irq()
*
* Check if interrupt is disabled or not
* and disable interrupt
*
* Return: int
*/
int i2c_disable_irq(struct nfc_dev *dev)
{
unsigned long flags;
spin_lock_irqsave(&dev->i2c_dev.irq_enabled_lock, flags);
if (dev->i2c_dev.irq_enabled) {
disable_irq_nosync(dev->i2c_dev.client->irq);
dev->i2c_dev.irq_enabled = false;
}
spin_unlock_irqrestore(&dev->i2c_dev.irq_enabled_lock, flags);
return 0;
}
/**
* i2c_enable_irq()
*
* Check if interrupt is enabled or not
* and enable interrupt
*
* Return: int
*/
int i2c_enable_irq(struct nfc_dev *dev)
{
unsigned long flags;
spin_lock_irqsave(&dev->i2c_dev.irq_enabled_lock, flags);
if (!dev->i2c_dev.irq_enabled) {
dev->i2c_dev.irq_enabled = true;
enable_irq(dev->i2c_dev.client->irq);
}
spin_unlock_irqrestore(&dev->i2c_dev.irq_enabled_lock, flags);
return 0;
}
static irqreturn_t i2c_irq_handler(int irq, void *dev_id)
{
struct nfc_dev *nfc_dev = dev_id;
struct i2c_dev *i2c_dev = &nfc_dev->i2c_dev;
if (device_may_wakeup(&i2c_dev->client->dev))
pm_wakeup_event(&i2c_dev->client->dev, WAKEUP_SRC_TIMEOUT);
i2c_disable_irq(nfc_dev);
wake_up(&nfc_dev->read_wq);
return IRQ_HANDLED;
}
int i2c_read(struct nfc_dev *nfc_dev, char *buf, size_t count, int timeout)
{
int ret;
struct i2c_dev *i2c_dev = &nfc_dev->i2c_dev;
struct platform_gpio *nfc_gpio = &nfc_dev->configs.gpio;
uint16_t i = 0;
uint16_t disp_len = GET_IPCLOG_MAX_PKT_LEN(count);
pr_debug("%s : reading %zu bytes.\n", __func__, count);
if (timeout > NCI_CMD_RSP_TIMEOUT)
timeout = NCI_CMD_RSP_TIMEOUT;
if (count > MAX_BUFFER_SIZE)
count = MAX_BUFFER_SIZE;
if (!gpio_get_value(nfc_gpio->irq)) {
while (1) {
ret = 0;
if (!i2c_dev->irq_enabled) {
i2c_dev->irq_enabled = true;
enable_irq(i2c_dev->client->irq);
}
if (!gpio_get_value(nfc_gpio->irq)) {
if (timeout) {
ret = wait_event_interruptible_timeout(nfc_dev->read_wq,
!i2c_dev->irq_enabled, msecs_to_jiffies(timeout));
if (ret <= 0) {
pr_err("%s timeout/error in read\n", __func__);
goto err;
}
} else {
ret = wait_event_interruptible(nfc_dev->read_wq,
!i2c_dev->irq_enabled);
if (ret) {
pr_err("%s error wakeup of read wq\n", __func__);
ret = -EINTR;
goto err;
}
}
}
i2c_disable_irq(nfc_dev);
if (gpio_get_value(nfc_gpio->irq))
break;
if (!gpio_get_value(nfc_gpio->ven)) {
pr_info("%s: releasing read\n", __func__);
ret = -EIO;
goto err;
}
pr_warn("%s: spurious interrupt detected\n", __func__);
}
}
memset(buf, 0x00, count);
/* Read data */
ret = i2c_master_recv(nfc_dev->i2c_dev.client, buf, count);
NFCLOG_IPC(nfc_dev, false, "%s of %d bytes, ret %d", __func__, count,
ret);
if (ret <= 0) {
pr_err("%s: returned %d\n", __func__, ret);
goto err;
}
for (i = 0; i < disp_len; i++)
NFCLOG_IPC(nfc_dev, false, " %02x", buf[i]);
/* check if it's response of cold reset command
* NFC HAL process shouldn't receive this data as
* command was esepowermanager
*/
if (nfc_dev->cold_reset.rsp_pending && nfc_dev->cold_reset.cmd_buf
&& (buf[0] == PROP_NCI_RSP_GID)
&& (buf[1] == nfc_dev->cold_reset.cmd_buf[1])) {
read_cold_reset_rsp(nfc_dev, buf);
nfc_dev->cold_reset.rsp_pending = false;
wake_up_interruptible(&nfc_dev->cold_reset.read_wq);
/*
* 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;
}
err:
return ret;
}
int i2c_write(struct nfc_dev *nfc_dev, const char *buf, size_t count,
int max_retry_cnt)
{
int ret = -EINVAL;
int retry_cnt;
uint16_t i = 0;
uint16_t disp_len = GET_IPCLOG_MAX_PKT_LEN(count);
if (count > MAX_DL_BUFFER_SIZE)
count = MAX_DL_BUFFER_SIZE;
pr_debug("%s : writing %zu bytes.\n", __func__, count);
NFCLOG_IPC(nfc_dev, false, "%s sending %d B", __func__, count);
for (i = 0; i < disp_len; i++)
NFCLOG_IPC(nfc_dev, false, " %02x", buf[i]);
for (retry_cnt = 1; retry_cnt <= max_retry_cnt; retry_cnt++) {
ret = i2c_master_send(nfc_dev->i2c_dev.client, buf, count);
NFCLOG_IPC(nfc_dev, false, "%s ret %d", __func__, ret);
if (ret <= 0) {
pr_warn("%s: write failed ret %d, Maybe in Standby Mode - Retry(%d)\n",
__func__, ret, retry_cnt);
usleep_range(WRITE_RETRY_WAIT_TIME_USEC,
WRITE_RETRY_WAIT_TIME_USEC + 100);
} else if (ret != count) {
pr_err("%s: failed to write %d\n", __func__, ret);
ret = -EIO;
} else if (ret == count)
break;
}
return ret;
}
ssize_t nfc_i2c_dev_read(struct file *filp, char __user *buf,
size_t count, loff_t *offset)
{
int ret = 0;
struct nfc_dev *nfc_dev = (struct nfc_dev *)filp->private_data;
if (filp->f_flags & O_NONBLOCK) {
pr_err(":f_flag has O_NONBLOCK. EAGAIN\n");
return -EAGAIN;
}
mutex_lock(&nfc_dev->read_mutex);
ret = i2c_read(nfc_dev, nfc_dev->read_kbuf, count, 0);
if (ret > 0) {
if (copy_to_user(buf, nfc_dev->read_kbuf, ret)) {
pr_warn("%s : failed to copy to user space\n", __func__);
ret = -EFAULT;
}
}
mutex_unlock(&nfc_dev->read_mutex);
return ret;
}
ssize_t nfc_i2c_dev_write(struct file *filp, const char __user *buf,
size_t count, loff_t *offset)
{
int ret;
struct nfc_dev *nfc_dev = (struct nfc_dev *)filp->private_data;
if (count > MAX_DL_BUFFER_SIZE)
count = MAX_DL_BUFFER_SIZE;
if (!nfc_dev)
return -ENODEV;
mutex_lock(&nfc_dev->write_mutex);
if (copy_from_user(nfc_dev->write_kbuf, buf, count)) {
pr_err("%s : failed to copy from user space\n", __func__);
mutex_unlock(&nfc_dev->write_mutex);
return -EFAULT;
}
ret = i2c_write(nfc_dev, nfc_dev->write_kbuf, count, NO_RETRY);
mutex_unlock(&nfc_dev->write_mutex);
return ret;
}
static const struct file_operations nfc_i2c_dev_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = nfc_i2c_dev_read,
.write = nfc_i2c_dev_write,
.open = nfc_dev_open,
.release = nfc_dev_close,
.unlocked_ioctl = nfc_dev_ioctl,
};
int nfc_i2c_dev_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int ret = 0;
struct nfc_dev *nfc_dev = NULL;
struct i2c_dev *i2c_dev = NULL;
struct platform_configs nfc_configs;
struct platform_gpio *nfc_gpio = &nfc_configs.gpio;
pr_debug("%s: enter\n", __func__);
//retrieve details of gpios from dt
ret = nfc_parse_dt(&client->dev, &nfc_configs, PLATFORM_IF_I2C);
if (ret) {
pr_err("%s : failed to parse dt\n", __func__);
goto err;
}
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
pr_err("%s : need I2C_FUNC_I2C\n", __func__);
ret = -ENODEV;
goto err;
}
nfc_dev = kzalloc(sizeof(struct nfc_dev), GFP_KERNEL);
if (nfc_dev == NULL) {
ret = -ENOMEM;
goto err;
}
nfc_dev->read_kbuf = kzalloc(MAX_BUFFER_SIZE, GFP_DMA | GFP_KERNEL);
if (!nfc_dev->read_kbuf) {
ret = -ENOMEM;
goto err_free_nfc_dev;
}
nfc_dev->write_kbuf = kzalloc(MAX_DL_BUFFER_SIZE, GFP_DMA | GFP_KERNEL);
if (!nfc_dev->write_kbuf) {
ret = -ENOMEM;
goto err_free_read_kbuf;
}
nfc_dev->interface = PLATFORM_IF_I2C;
nfc_dev->nfc_state = NFC_STATE_NCI;
nfc_dev->i2c_dev.client = client;
i2c_dev = &nfc_dev->i2c_dev;
nfc_dev->nfc_read = i2c_read;
nfc_dev->nfc_write = i2c_write;
nfc_dev->nfc_enable_intr = i2c_enable_irq;
nfc_dev->nfc_disable_intr = i2c_disable_irq;
nfc_dev->fw_major_version = 0;
ret = configure_gpio(nfc_gpio->ven, GPIO_OUTPUT);
if (ret) {
pr_err("%s: unable to request nfc reset gpio [%d]\n",
__func__, nfc_gpio->ven);
goto err_free_write_kbuf;
}
ret = configure_gpio(nfc_gpio->irq, GPIO_IRQ);
if (ret <= 0) {
pr_err("%s: unable to request nfc irq gpio [%d]\n",
__func__, nfc_gpio->irq);
goto err_free_ven;
}
client->irq = ret;
ret = configure_gpio(nfc_gpio->dwl_req, GPIO_OUTPUT);
if (ret) {
pr_err("%s: unable to request nfc firm downl gpio [%d]\n",
__func__, nfc_gpio->dwl_req);
//not returning failure here as dwl gpio is a optional gpio for sn220
}
ret = configure_gpio(nfc_gpio->clkreq, GPIO_INPUT);
if (ret) {
pr_err("%s: unable to request nfc clkreq gpio [%d]\n",
__func__, nfc_gpio->clkreq);
goto err_free_dwl_req;
}
/*copy the retrieved gpio details from DT */
memcpy(&nfc_dev->configs, &nfc_configs, sizeof(struct platform_configs));
/* init mutex and queues */
init_waitqueue_head(&nfc_dev->read_wq);
mutex_init(&nfc_dev->read_mutex);
mutex_init(&nfc_dev->write_mutex);
mutex_init(&nfc_dev->dev_ref_mutex);
spin_lock_init(&i2c_dev->irq_enabled_lock);
ret = nfc_misc_register(nfc_dev, &nfc_i2c_dev_fops, DEV_COUNT,
NFC_CHAR_DEV_NAME, CLASS_NAME);
if (ret) {
pr_err("%s: nfc_misc_register failed\n", __func__);
goto err_mutex_destroy;
}
/* interrupt initializations */
pr_info("%s : requesting IRQ %d\n", __func__, client->irq);
i2c_dev->irq_enabled = true;
ret = request_irq(client->irq, i2c_irq_handler,
IRQF_TRIGGER_HIGH, client->name, nfc_dev);
if (ret) {
pr_err("%s: request_irq failed\n", __func__);
goto err_nfc_misc_unregister;
}
i2c_disable_irq(nfc_dev);
i2c_set_clientdata(client, nfc_dev);
ret = nfc_ldo_config(&client->dev, nfc_dev);
if (ret) {
pr_err("LDO config failed\n");
goto err_ldo_config_failed;
}
ret = nfcc_hw_check(nfc_dev);
if (ret || nfc_dev->nfc_state == NFC_STATE_UNKNOWN) {
pr_err("nfc hw check failed ret %d\n", ret);
goto err_nfcc_hw_check;
}
device_init_wakeup(&client->dev, true);
i2c_dev->irq_wake_up = false;
nfc_dev->is_ese_session_active = false;
pr_info("%s success\n", __func__);
return 0;
err_nfcc_hw_check:
if (nfc_dev->reg) {
nfc_ldo_unvote(nfc_dev);
regulator_put(nfc_dev->reg);
}
err_ldo_config_failed:
free_irq(client->irq, nfc_dev);
err_nfc_misc_unregister:
nfc_misc_unregister(nfc_dev, DEV_COUNT);
err_mutex_destroy:
mutex_destroy(&nfc_dev->dev_ref_mutex);
mutex_destroy(&nfc_dev->read_mutex);
mutex_destroy(&nfc_dev->write_mutex);
gpio_free(nfc_gpio->clkreq);
err_free_dwl_req:
if (gpio_is_valid(nfc_gpio->dwl_req))
gpio_free(nfc_gpio->dwl_req);
gpio_free(nfc_gpio->irq);
err_free_ven:
gpio_free(nfc_gpio->ven);
err_free_write_kbuf:
kfree(nfc_dev->write_kbuf);
err_free_read_kbuf:
kfree(nfc_dev->read_kbuf);
err_free_nfc_dev:
kfree(nfc_dev);
err:
pr_err("%s: failed\n", __func__);
return ret;
}
int nfc_i2c_dev_remove(struct i2c_client *client)
{
int ret = 0;
struct nfc_dev *nfc_dev = NULL;
pr_info("%s: remove device\n", __func__);
nfc_dev = i2c_get_clientdata(client);
if (!nfc_dev) {
pr_err("%s: device doesn't exist anymore\n", __func__);
ret = -ENODEV;
return ret;
}
if (nfc_dev->dev_ref_count > 0) {
pr_err("%s: device already in use\n", __func__);
return -EBUSY;
}
gpio_set_value(nfc_dev->configs.gpio.ven, 0);
// HW dependent delay before LDO goes into LPM mode
usleep_range(10000, 10100);
if (nfc_dev->reg) {
nfc_ldo_unvote(nfc_dev);
regulator_put(nfc_dev->reg);
}
device_init_wakeup(&client->dev, false);
free_irq(client->irq, nfc_dev);
nfc_misc_unregister(nfc_dev, DEV_COUNT);
mutex_destroy(&nfc_dev->dev_ref_mutex);
mutex_destroy(&nfc_dev->read_mutex);
mutex_destroy(&nfc_dev->write_mutex);
if (gpio_is_valid(nfc_dev->configs.gpio.clkreq))
gpio_free(nfc_dev->configs.gpio.clkreq);
if (gpio_is_valid(nfc_dev->configs.gpio.dwl_req))
gpio_free(nfc_dev->configs.gpio.dwl_req);
if (gpio_is_valid(nfc_dev->configs.gpio.irq))
gpio_free(nfc_dev->configs.gpio.irq);
if (gpio_is_valid(nfc_dev->configs.gpio.ven))
gpio_free(nfc_dev->configs.gpio.ven);
kfree(nfc_dev->read_kbuf);
kfree(nfc_dev->write_kbuf);
kfree(nfc_dev);
return ret;
}
int nfc_i2c_dev_suspend(struct device *device)
{
struct i2c_client *client = to_i2c_client(device);
struct nfc_dev *nfc_dev = i2c_get_clientdata(client);
struct i2c_dev *i2c_dev = NULL;
if (!nfc_dev) {
pr_err("%s: device doesn't exist anymore\n", __func__);
return -ENODEV;
}
i2c_dev = &nfc_dev->i2c_dev;
NFCLOG_IPC(nfc_dev, false, "%s: irq_enabled = %d", __func__,
i2c_dev->irq_enabled);
if (device_may_wakeup(&client->dev) && i2c_dev->irq_enabled) {
if (!enable_irq_wake(client->irq))
i2c_dev->irq_wake_up = true;
}
return 0;
}
int nfc_i2c_dev_resume(struct device *device)
{
struct i2c_client *client = to_i2c_client(device);
struct nfc_dev *nfc_dev = i2c_get_clientdata(client);
struct i2c_dev *i2c_dev = NULL;
if (!nfc_dev) {
pr_err("%s: device doesn't exist anymore\n", __func__);
return -ENODEV;
}
i2c_dev = &nfc_dev->i2c_dev;
NFCLOG_IPC(nfc_dev, false, "%s: irq_wake_up = %d", __func__,
i2c_dev->irq_wake_up);
if (device_may_wakeup(&client->dev) && i2c_dev->irq_wake_up) {
if (!disable_irq_wake(client->irq))
i2c_dev->irq_wake_up = false;
}
return 0;
}
static const struct i2c_device_id nfc_i2c_dev_id[] = {
{NFC_I2C_DEV_ID, 0},
{}
};
static const struct of_device_id nfc_i2c_dev_match_table[] = {
{.compatible = NFC_I2C_DRV_STR,},
{}
};
static const struct dev_pm_ops nfc_i2c_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(nfc_i2c_dev_suspend, nfc_i2c_dev_resume)
};
static struct i2c_driver nfc_i2c_dev_driver = {
.id_table = nfc_i2c_dev_id,
.probe = nfc_i2c_dev_probe,
.remove = nfc_i2c_dev_remove,
.driver = {
.name = NFC_I2C_DRV_STR,
.pm = &nfc_i2c_dev_pm_ops,
.of_match_table = nfc_i2c_dev_match_table,
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
};
MODULE_DEVICE_TABLE(of, nfc_i2c_dev_match_table);
static int __init nfc_i2c_dev_init(void)
{
int ret = 0;
ret = i2c_add_driver(&nfc_i2c_dev_driver);
if (ret != 0)
pr_err("NFC I2C add driver error ret %d\n", ret);
return ret;
}
module_init(nfc_i2c_dev_init);
static void __exit nfc_i2c_dev_exit(void)
{
pr_debug("Unloading NFC I2C driver\n");
i2c_del_driver(&nfc_i2c_dev_driver);
}
module_exit(nfc_i2c_dev_exit);
MODULE_DESCRIPTION("QTI NFC I2C driver");
MODULE_LICENSE("GPL v2");

View File

@@ -1,81 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2019-2021 NXP
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
******************************************************************************/
#ifndef _NFC_I2C_DRV_H_
#define _NFC_I2C_DRV_H_
#include <linux/i2c.h>
#define NFC_I2C_DRV_STR "qcom,sn-nci" /*kept same as dts */
#define NFC_I2C_DEV_ID "sn-i2c"
struct nfc_dev;
//Interface specific parameters
struct i2c_dev {
struct i2c_client *client;
/*IRQ parameters */
bool irq_enabled;
spinlock_t irq_enabled_lock;
/* NFC_IRQ wake-up state */
bool irq_wake_up;
};
long nfc_i2c_dev_ioctl(struct file *pfile, unsigned int cmd, unsigned long arg);
int nfc_i2c_dev_probe(struct i2c_client *client,
const struct i2c_device_id *id);
int nfc_i2c_dev_remove(struct i2c_client *client);
int nfc_i2c_dev_suspend(struct device *device);
int nfc_i2c_dev_resume(struct device *device);
#if IS_ENABLED(CONFIG_NFC_QTI_I2C)
int i2c_enable_irq(struct nfc_dev *dev);
int i2c_disable_irq(struct nfc_dev *dev);
int i2c_write(struct nfc_dev *dev, const char *buf, size_t count,
int max_retry_cnt);
int i2c_read(struct nfc_dev *dev, char *buf, size_t count, int timeout);
#else
static inline int i2c_enable_irq(struct nfc_dev *dev)
{
return -ENXIO;
}
static inline int i2c_disable_irq(struct nfc_dev *dev)
{
return -ENXIO;
}
static inline int i2c_write(struct nfc_dev *dev, const char *buf,
size_t count, int max_retry_cnt)
{
return -ENXIO;
}
static inline int i2c_read(struct nfc_dev *dev, char *buf, size_t count, int timeout)
{
return -ENXIO;
}
#endif
#endif //_NFC_I2C_DRV_H_