NFC: FR72092, To move NFC module to vendor techpackage

As a part FR72092 requirement, moved the NFC driver
module out of kernel tree to be compiled as vendor techpackage.

Change-Id: I177d81782a7059bf6b9b4556b384737708c43348
This commit is contained in:
Tapas Dey
2021-11-17 12:00:44 +05:30
parent a63df4ba2f
commit e89ae5f94a
16 changed files with 2470 additions and 0 deletions

7
Android.bp Normal file
View File

@@ -0,0 +1,7 @@
cc_library_headers {
name: "qti_nfc_kernel_headers",
export_include_dirs: [
"include/uapi/linux/nfc",
],
vendor_available: true,
}

14
Android.mk Normal file
View File

@@ -0,0 +1,14 @@
# Android makefile for nfc kernel modules
# Path to DLKM make scripts
DLKM_DIR := $(TOP)/device/qcom/common/dlkm
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := nfc_i2c.ko
LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT)
LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*)
include $(DLKM_DIR)/Build_external_kernelmodule.mk

15
Kbuild Normal file
View File

@@ -0,0 +1,15 @@
#Makefile for qti nfc drivers
include $(NFC_ROOT)/config/gki_nfc.conf
LINUXINCLUDE += -I$(NFC_ROOT)/include/uapi/linux/nfc
LINUXINCLUDE += -include $(NFC_ROOT)/config/gki_nfc_conf.h
obj-$(CONFIG_NFC_QTI_I2C) += nfc_i2c.o
#source files
nfc_i2c-objs += qti/ese_cold_reset.o \
qti/nfc_common.o \
qti/nfc_i2c_drv.o

10
Kconfig Normal file
View File

@@ -0,0 +1,10 @@
menuconfig NFC_QTI_I2C
tristate "QTI 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.

14
Makefile Normal file
View File

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

1
config/gki_nfc.conf Normal file
View File

@@ -0,0 +1 @@
export CONFIG_NFC_QTI_I2C=m

6
config/gki_nfc_conf.h Normal file
View File

@@ -0,0 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
*/
#define CONFIG_NFC_QTI_I2C 1

View File

@@ -0,0 +1,26 @@
/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
/*
* Copyright (c) 2018-2020 The Linux Foundation. All rights reserved.
*/
#ifndef _UAPI_NFCINFO_H_
#define _UAPI_NFCINFO_H_
#include <linux/ioctl.h>
#define NFCC_MAGIC 0xE9
#define NFCC_GET_INFO _IOW(NFCC_MAGIC, 0x09, unsigned int)
struct nqx_devinfo {
unsigned char chip_type;
unsigned char rom_version;
unsigned char fw_major;
unsigned char fw_minor;
};
union nqx_uinfo {
unsigned int i;
struct nqx_devinfo info;
};
#endif

View File

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

View File

@@ -0,0 +1 @@
PRODUCT_PACKAGES += nfc_i2c.ko

336
qti/ese_cold_reset.c Normal file
View File

@@ -0,0 +1,336 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
*/
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/uaccess.h>
#include "nfc_common.h"
/**
* send_ese_cmd() - Send eSE command to NFC controller.
* @nfc_dev: NFC device handle.
*
* Return: 0 on pass and negative value on failure.
*/
static int send_ese_cmd(struct nfc_dev *nfc_dev)
{
int ret;
if (nfc_dev->nfc_state == NFC_STATE_FW_DWL) {
dev_err(nfc_dev->nfc_device,
"cannot send ese cmd as FW download is in-progress\n");
return -EBUSY;
}
if (!gpio_get_value(nfc_dev->configs.gpio.ven)) {
dev_err(nfc_dev->nfc_device,
"cannot send ese cmd as NFCC powered off\n");
return -ENODEV;
}
ret = nfc_dev->nfc_write(nfc_dev, nfc_dev->cold_reset.cmd_buf,
nfc_dev->cold_reset.cmd_len,
MAX_RETRY_COUNT);
if (ret <= 0)
dev_err(nfc_dev->nfc_device,
"%s: write failed after max retry, ret %d\n",
__func__, ret);
return ret;
}
/**
* read_cold_reset_rsp() - Read response of the cold reset command.
* @nfc_dev: NFC device handle.
* @header: Pointer to NCI header if it is already read.
*
* Return: 0 on pass and negative value on failure.
*/
int read_cold_reset_rsp(struct nfc_dev *nfc_dev, char *header)
{
int ret = -EPERM;
struct cold_reset *cold_rst = &nfc_dev->cold_reset;
char *rsp_buf = NULL;
rsp_buf = kzalloc(cold_rst->rsp_len, GFP_DMA | GFP_KERNEL);
if (!rsp_buf)
return -ENOMEM;
/*
* read header if NFC is disabled
* for enable case, header is read by nfc read thread(for i2c)
*/
if ((!cold_rst->is_nfc_enabled) &&
(nfc_dev->interface == PLATFORM_IF_I2C)) {
ret = i2c_master_recv(nfc_dev->i2c_dev.client, rsp_buf, NCI_HDR_LEN);
if (ret <= 0) {
dev_err(nfc_dev->nfc_device,
"%s: failure to read cold reset rsp header\n",
__func__);
ret = -EIO;
goto error;
}
/*
* return failure, if packet is not a response packet or
* if response's OID doesn't match with the CMD's OID
*/
if (!(rsp_buf[0] & NCI_RSP_PKT_TYPE) ||
(rsp_buf[1] != cold_rst->cmd_buf[1])) {
dev_err(nfc_dev->nfc_device,
"%s: - invalid cold reset response 0x%x 0x%x\n",
__func__, rsp_buf[0], rsp_buf[1]);
ret = -EINVAL;
goto error;
}
} else if (header) {
memcpy(rsp_buf, header, NCI_HDR_LEN);
} else {
dev_err(nfc_dev->nfc_device,
"%s: - invalid or NULL header\n", __func__);
ret = -EINVAL;
goto error;
}
if ((NCI_HDR_LEN + rsp_buf[NCI_PAYLOAD_LEN_IDX]) >
cold_rst->rsp_len) {
dev_err(nfc_dev->nfc_device,
"%s: - no space for cold_reset resp\n", __func__);
ret = -ENOMEM;
goto error;
}
if (nfc_dev->interface == PLATFORM_IF_I2C) {
ret = nfc_dev->nfc_read(nfc_dev,
&rsp_buf[NCI_PAYLOAD_IDX],
rsp_buf[NCI_PAYLOAD_LEN_IDX],
NCI_CMD_RSP_TIMEOUT);
if (ret <= 0) {
dev_err(nfc_dev->nfc_device,
"%s: failure to read cold reset rsp payload\n",
__func__);
ret = -EIO;
goto error;
}
ret = cold_rst->status = rsp_buf[NCI_PAYLOAD_IDX];
pr_debug("nfc ese rsp hdr 0x%x 0x%x 0x%x, payload byte0 0x%x\n",
rsp_buf[0], rsp_buf[1], rsp_buf[2], rsp_buf[3]);
}
error:
kfree(rsp_buf);
return ret;
}
/**
* ese_cold_reset_ioctl() - This function handles the eSE cold reset ioctls.
* @nfc_dev: NFC device handle.
* @arg: ioctl argument.
*
* Return: 0 on pass and negative value on failure.
*/
int ese_cold_reset_ioctl(struct nfc_dev *nfc_dev, unsigned long arg)
{
int ret;
struct ese_ioctl_arg ioctl_arg;
if (!arg) {
dev_err(nfc_dev->nfc_device, "arg is invalid\n");
return -EINVAL;
}
ret = copy_from_user((void *)&ioctl_arg, (const void *)arg,
sizeof(ioctl_arg));
if (ret) {
dev_err(nfc_dev->nfc_device,
"ese ioctl arg copy from user failed\n");
return -EFAULT;
}
nfc_dev->cold_reset.arg = kzalloc(sizeof(struct ese_cold_reset_arg),
GFP_KERNEL);
if (!nfc_dev->cold_reset.arg)
return -ENOMEM;
ret = copy_struct_from_user(nfc_dev->cold_reset.arg,
sizeof(struct ese_cold_reset_arg),
u64_to_user_ptr(ioctl_arg.buf),
sizeof(struct ese_cold_reset_arg));
if (ret) {
dev_err(nfc_dev->nfc_device,
"ese ioctl arg buffer copy from user failed\n");
ret = -EFAULT;
goto err;
}
switch (nfc_dev->cold_reset.arg->sub_cmd) {
case ESE_COLD_RESET_DO:
/*
* cold reset allowed during protection enable, only if the
* source is same as the one which enabled protection.
*/
if (nfc_dev->cold_reset.is_crp_en &&
(nfc_dev->cold_reset.arg->src !=
nfc_dev->cold_reset.last_src_ese_prot)) {
dev_err(nfc_dev->nfc_device,
"cold reset from %d denied, protection is on\n",
nfc_dev->cold_reset.arg->src);
ret = -EACCES;
goto err;
}
nfc_dev->cold_reset.cmd_buf = kzalloc(COLD_RESET_CMD_LEN,
GFP_DMA | GFP_KERNEL);
if (!nfc_dev->cold_reset.cmd_buf) {
ret = -ENOMEM;
goto err;
}
nfc_dev->cold_reset.cmd_buf[0] = PROP_NCI_CMD_GID;
nfc_dev->cold_reset.cmd_buf[1] = COLD_RESET_OID;
nfc_dev->cold_reset.cmd_buf[2] = COLD_RESET_CMD_PL_LEN;
nfc_dev->cold_reset.cmd_len = NCI_HDR_LEN +
COLD_RESET_CMD_PL_LEN;
nfc_dev->cold_reset.rsp_len = COLD_RESET_RSP_LEN;
break;
case ESE_COLD_RESET_PROTECT_EN:
if (nfc_dev->cold_reset.is_crp_en) {
if (nfc_dev->cold_reset.arg->src !=
nfc_dev->cold_reset.last_src_ese_prot) {
dev_err(nfc_dev->nfc_device,
"ese protection enable denied\n");
ret = -EACCES;
goto err;
}
pr_warn("ese protection already enabled\n");
ret = 0;
/* free buffers and exit with pass */
goto err;
}
case ESE_COLD_RESET_PROTECT_DIS:
if (nfc_dev->cold_reset.is_crp_en &&
nfc_dev->cold_reset.arg->src !=
nfc_dev->cold_reset.last_src_ese_prot) {
pr_err("ese cold reset protection disable denied\n");
ret = -EACCES;
goto err;
}
nfc_dev->cold_reset.cmd_buf = kzalloc(COLD_RESET_PROT_CMD_LEN,
GFP_DMA | GFP_KERNEL);
if (!nfc_dev->cold_reset.cmd_buf) {
ret = -ENOMEM;
goto err;
}
nfc_dev->cold_reset.cmd_buf[0] = PROP_NCI_CMD_GID;
nfc_dev->cold_reset.cmd_buf[1] = COLD_RESET_PROT_OID;
nfc_dev->cold_reset.cmd_buf[2] = COLD_RESET_PROT_CMD_PL_LEN;
nfc_dev->cold_reset.cmd_len = NCI_HDR_LEN +
COLD_RESET_PROT_CMD_PL_LEN;
nfc_dev->cold_reset.rsp_len = COLD_RESET_PROT_RSP_LEN;
if (nfc_dev->cold_reset.arg->sub_cmd ==
ESE_COLD_RESET_PROTECT_EN)
nfc_dev->cold_reset.cmd_buf[3] = 0x1;
else
nfc_dev->cold_reset.cmd_buf[3] = 0x0;
break;
default:
pr_err("%s invalid ese ioctl sub cmd %d\n", __func__,
nfc_dev->cold_reset.arg->sub_cmd);
ret = -ENOIOCTLCMD;
goto err;
}
pr_debug("nfc ese cmd hdr 0x%x 0x%x 0x%x\n",
nfc_dev->cold_reset.cmd_buf[0],
nfc_dev->cold_reset.cmd_buf[1],
nfc_dev->cold_reset.cmd_buf[2]);
ret = send_ese_cmd(nfc_dev);
if (ret <= 0) {
pr_err("failed to send ese command\n");
goto err;
}
nfc_dev->cold_reset.rsp_pending = true;
/* check if NFC is enabled */
if (nfc_dev->cold_reset.is_nfc_enabled) {
/*
* nfc_read thread will initiate cold reset response
* and it will signal for data available
*/
wait_event_interruptible(nfc_dev->cold_reset.read_wq,
!nfc_dev->cold_reset.rsp_pending);
} else {
/*
* Read data as NFC read thread is not active
*/
if (nfc_dev->interface == PLATFORM_IF_I2C) {
ret = is_nfc_data_available_for_read(nfc_dev);
if (ret <= 0) {
nfc_dev->nfc_disable_intr(nfc_dev);
nfc_dev->cold_reset.rsp_pending = false;
goto err;
}
ret = read_cold_reset_rsp(nfc_dev, NULL);
nfc_dev->cold_reset.rsp_pending = false;
if (ret < 0) {
pr_err("%s rsp read err\n", __func__);
goto err;
}
} else {
/*
* Enable intr as it is disabled when NFC is in disable
* state
*/
nfc_dev->nfc_enable_intr(nfc_dev);
wait_event_interruptible(
nfc_dev->cold_reset.read_wq,
!nfc_dev->cold_reset.rsp_pending);
}
nfc_dev->nfc_disable_intr(nfc_dev);
}
if (nfc_dev->cold_reset.arg->sub_cmd == ESE_COLD_RESET_PROTECT_EN) {
nfc_dev->cold_reset.is_crp_en = true;
nfc_dev->cold_reset.last_src_ese_prot =
nfc_dev->cold_reset.arg->src;
} else if (nfc_dev->cold_reset.arg->sub_cmd ==
ESE_COLD_RESET_PROTECT_DIS) {
nfc_dev->cold_reset.is_crp_en = false;
nfc_dev->cold_reset.last_src_ese_prot =
ESE_COLD_RESET_ORIGIN_NONE;
} else
pr_debug("ese cmd is %d\n", nfc_dev->cold_reset.arg->sub_cmd);
ret = nfc_dev->cold_reset.status;
err:
kfree(nfc_dev->cold_reset.cmd_buf);
kfree(nfc_dev->cold_reset.arg);
nfc_dev->cold_reset.arg = NULL;
nfc_dev->cold_reset.cmd_buf = NULL;
return ret;
}

81
qti/ese_cold_reset.h Normal file
View File

@@ -0,0 +1,81 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
*/
#ifndef __ESE_COLD_RESET_H
#define __ESE_COLD_RESET_H
#include <nfcinfo.h>
#define MAX_BUFF_SIZE 264
/* ESE_COLD_RESET MACROS */
#define COLD_RESET_CMD_LEN 3
#define COLD_RESET_RSP_LEN 4
#define COLD_RESET_PROT_CMD_LEN 4
#define COLD_RESET_PROT_RSP_LEN 4
#define PROP_NCI_CMD_GID 0x2F
#define COLD_RESET_CMD_PL_LEN 0x00
#define COLD_RESET_PROT_CMD_PL_LEN 0x01
#define PROP_NCI_RSP_GID 0x4F
#define COLD_RESET_OID 0x1E
#define COLD_RESET_PROT_OID 0x1F
#define ESE_COLD_RESET _IOWR(NFCC_MAGIC, 0x08, struct ese_ioctl_arg)
enum ese_ioctl_arg_type {
ESE_ARG_TYPE_COLD_RESET = 0,
};
/* ESE_COLD_RESET ioctl origin, max 4 are supported */
enum ese_cold_reset_origin {
ESE_COLD_RESET_ORIGIN_ESE = 0,
ESE_COLD_RESET_ORIGIN_NFC,
ESE_COLD_RESET_ORIGIN_OTHER = 0x20,
ESE_COLD_RESET_ORIGIN_NONE = 0xFF,
};
/* ESE_COLD_RESET ioctl sub commands, max 8 are supported */
enum ese_cold_reset_sub_cmd {
ESE_COLD_RESET_DO = 0,
ESE_COLD_RESET_PROTECT_EN,
ESE_COLD_RESET_PROTECT_DIS,
};
/* Data passed in buf of ese cold reset ioctl */
struct ese_cold_reset_arg {
__u8 src;
__u8 sub_cmd;
__u16 rfu;
};
/* Argument buffer passed to ese ioctl */
struct ese_ioctl_arg {
__u64 buf;
__u32 buf_size;
__u8 type;
};
/* Features specific Parameters */
struct cold_reset {
wait_queue_head_t read_wq;
char *cmd_buf;
struct ese_cold_reset_arg *arg;
uint16_t cmd_len;
uint16_t rsp_len;
/* Source of last ese protection command */
uint8_t last_src_ese_prot;
uint8_t status;
/* Is cold reset protection enabled */
bool is_crp_en;
bool rsp_pending;
/* Is NFC enabled from UI */
bool is_nfc_enabled;
};
struct nfc_dev;
int ese_cold_reset_ioctl(struct nfc_dev *nfc_dev, unsigned long arg);
int read_cold_reset_rsp(struct nfc_dev *nfc_dev, char *header);
#endif

951
qti/nfc_common.c Normal file
View File

@@ -0,0 +1,951 @@
// 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;
}

346
qti/nfc_common.h Normal file
View File

@@ -0,0 +1,346 @@
/* 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_

577
qti/nfc_i2c_drv.c Normal file
View File

@@ -0,0 +1,577 @@
// 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");

81
qti/nfc_i2c_drv.h Normal file
View File

@@ -0,0 +1,81 @@
/* 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_