Add 'nxp/opensource/driver/' from commit 'c6f0de7127de042241c6f2ac7c60c5deb77d7d85'

git-subtree-dir: nxp/opensource/driver
git-subtree-mainline: 47018f8d6d
git-subtree-split: c6f0de7127
Change-Id:
repo: https://git.codelinaro.org/clo/la/platform/vendor/nxp/opensource/driver
tag: LA.VENDOR.14.3.0.r1-17300-lanai.QSSI15.0
This commit is contained in:
David Wronek 2024-10-06 16:43:44 +02:00
commit 99ab089c55
24 changed files with 3556 additions and 0 deletions

View File

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

View File

@ -0,0 +1,25 @@
# Android makefile for nfc kernel modules
# Path to DLKM make scripts
ifeq ($(strip $(NFC_DLKM_ENABLED)),true)
DLKM_DIR := $(TOP)/device/qcom/common/dlkm
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
ifeq ($(TARGET_ENABLE_PERIPHERAL_CONTROL), true)
LOCAL_CFLAGS := -DNFC_SECURE_PERIPHERAL_ENABLED
KBUILD_OPTIONS += KBUILD_EXTRA_SYMBOLS=$(PWD)/$(call intermediates-dir-for,DLKM,sec-module-symvers)/Module.symvers
ifeq ($(TARGET_KERNEL_DLKM_SECURE_MSM_OVERRIDE), true)
LOCAL_REQUIRED_MODULES := sec-module-symvers
LOCAL_ADDITIONAL_DEPENDENCIES += $(call intermediates-dir-for,DLKM,sec-module-symvers)/Module.symvers
endif
endif
LOCAL_MODULE := nxp-nci.ko
LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT)
LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*)
LOCAL_MODULE_DDK_BUILD := true
include $(DLKM_DIR)/Build_external_kernelmodule.mk
endif

View File

@ -0,0 +1,14 @@
load("//build/kernel/kleaf:kernel.bzl", "ddk_headers")
load(":define_modules.bzl", "define_modules")
define_modules("pineapple", "consolidate")
define_modules("pineapple", "gki")
define_modules("blair", "consolidate")
define_modules("blair", "gki")
define_modules("pitti", "consolidate")
define_modules("pitti", "gki")
define_modules("volcano", "consolidate")
define_modules("volcano", "gki")

View File

@ -0,0 +1,21 @@
#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
LINUXINCLUDE += -I$(NFC_ROOT)/../../../qcom/opensource/securemsm-kernel/smcinvoke/
LINUXINCLUDE += -I$(NFC_ROOT)/../../../qcom/opensource/securemsm-kernel/linux/
LINUXINCLUDE += -I$(NFC_ROOT)/../../../qcom/opensource/securemsm-kernel/include/linux/
obj-$(CONFIG_NXP_NFC_I2C) += nxp-nci.o
#source files
nxp-nci-objs += nfc/ese_cold_reset.o \
nfc/common.o \
nfc/common_nxp.o \
nfc/common_qcom.o \
nfc/i2c_drv.o

View File

@ -0,0 +1,13 @@
#
# 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

@ -0,0 +1,340 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc., <http://fsf.org/>
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
{description}
Copyright (C) {year} {fullname}
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.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
{signature of Ty Coon}, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

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

View File

@ -0,0 +1,2 @@
# NXPNFC_I2CDriver
NFC I2C Open Source driver

View File

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

View File

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

View File

@ -0,0 +1,46 @@
load("//build/kernel/kleaf:kernel.bzl", "ddk_module")
load("//build/bazel_common_rules/dist:dist.bzl", "copy_to_dist_dir")
def define_modules(target, variant):
tv = "{}_{}".format(target, variant)
copts = []
deps = ["//msm-kernel:all_headers"]
if target == "pineapple":
copts.append("-DNFC_SECURE_PERIPHERAL_ENABLED")
deps += ["//vendor/qcom/opensource/securemsm-kernel:smcinvoke_kernel_headers",
"//vendor/qcom/opensource/securemsm-kernel:{}_smcinvoke_dlkm".format(tv)
]
ddk_module(
name = "{}_nxp-nci".format(tv),
out = "nxp-nci.ko",
srcs = ["nfc/common.c",
"nfc/common_nxp.c",
"nfc/common_qcom.c",
"nfc/ese_cold_reset.c",
"nfc/i2c_drv.c",
"nfc/common.h",
"nfc/common_nxp.h",
"nfc/ese_cold_reset.h",
"nfc/i2c_drv.h"
],
hdrs = ["include/uapi/linux/nfc/nfcinfo.h",
"include/uapi/linux/nfc/sn_uapi.h"],
includes = [".", "linux", "nfc", "include/uapi/linux/nfc"],
copts = copts,
deps = deps,
kernel_build= "//msm-kernel:{}".format(tv),
visibility = ["//visibility:public"]
)
copy_to_dist_dir(
name = "{}_nxp-nci_dist".format(tv),
data = [":{}_nxp-nci".format(tv)],
dist_dir = "out/target/product/{}/dlkm/lib/modules/".format(target),
flat = True,
wipe_dist_dir = False,
allow_duplicate_filenames = False,
mode_overrides = {"**/*": "644"},
)

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,40 @@
/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
/******************************************************************************
* Copyright (C) 2015, The Linux Foundation. All rights reserved.
* Copyright (C) 2019-2022 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.
*
******************************************************************************/
/*
* Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
****************************************************************************/
#ifndef _UAPI_SN_UAPI_H_
#define _UAPI_SN_UAPI_H_
#include <linux/ioctl.h>
/* Ioctls
* The type should be aligned with MW HAL definitions
*/
#define NFC_MAGIC (0xE9)
#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 NFC_SET_RESET_READ_PENDING _IOW(NFC_MAGIC, 0x04, uint32_t)
#define NFC_GET_GPIO_STATUS _IOR(NFC_MAGIC, 0x05, uint32_t)
#define NFC_SECURE_ZONE _IOW(NFC_MAGIC, 0x0A, uint32_t)
#define ESE_COLD_RESET _IOWR(NFCC_MAGIC, 0x08, struct ese_ioctl_arg)
#endif

View File

@ -0,0 +1,862 @@
/******************************************************************************
* Copyright (C) 2015, The Linux Foundation. All rights reserved.
* Copyright (C) 2019-2022 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-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*
*****************************************************************************/
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/delay.h>
#include <linux/pinctrl/qcom-pinctrl.h>
#include "common.h"
bool secure_peripheral_not_found = true;
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("NxpDrv: %s: nfc of_node NULL\n", __func__);
return -EINVAL;
}
nfc_gpio->irq = -EINVAL;
nfc_gpio->dwl_req = -EINVAL;
nfc_gpio->ven = -EINVAL;
nfc_gpio->clkreq = -EINVAL;
/* irq 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("NxpDrv: %s: irq gpio invalid %d\n", __func__,
nfc_gpio->irq);
return nfc_gpio->irq;
}
pr_info("NxpDrv: %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("NxpDrv: %s: ven gpio invalid %d\n", __func__, nfc_gpio->ven);
return nfc_gpio->ven;
}
/* some products like sn220 does not required fw dwl pin */
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("NxpDrv: %s: dwl_req gpio invalid %d\n", __func__,
nfc_gpio->dwl_req);
}
/* Read clock request gpio configuration if MGPIO configurations are not preasent */
if (of_property_read_string(np, DTS_CLKSRC_GPIO_STR, &nfc_configs->clk_src_name)) {
nfc_configs->clk_pin_voting = false;
} else
nfc_configs->clk_pin_voting = true;
/* Read clkreq GPIO pin number from DTSI */
nfc_gpio->clkreq = of_get_named_gpio(np, DTS_CLKREQ_GPIO_STR, 0);
if (!gpio_is_valid(nfc_gpio->clkreq)) {
dev_err(dev, "NxpDrv: clkreq gpio invalid %d\n", nfc_gpio->clkreq);
return -EINVAL;
}
#ifdef NFC_SECURE_PERIPHERAL_ENABLED
/* Read DTS_SZONE_STR to check secure zone support */
if (of_property_read_string(np, DTS_SZONE_STR, &nfc_configs->szone)) {
nfc_configs->CNSS_NFC_HW_SECURE_ENABLE = false;
}else
nfc_configs->CNSS_NFC_HW_SECURE_ENABLE = true;
#endif
pr_info("NxpDrv: %s: irq %d, ven %d, dwl %d, clkreq %d \n", __func__, nfc_gpio->irq, 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, "NxpDrv: 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, "NxpDrv: error reading NFC current value\n");
// set default as per datasheet
ldo->max_current = NFC_CURRENT_MAX;
}
return 0;
}
void set_valid_gpio(int gpio, int value)
{
if (gpio_is_valid(gpio)) {
pr_debug("NxpDrv: %s: gpio %d value %d\n", __func__, gpio, value);
gpio_set_value(gpio, value);
/* hardware dependent delay */
usleep_range(NFC_GPIO_SET_WAIT_TIME_US,
NFC_GPIO_SET_WAIT_TIME_US + 100);
}
}
int get_valid_gpio(int gpio)
{
int value = -EINVAL;
if (gpio_is_valid(gpio)) {
value = gpio_get_value(gpio);
pr_debug("NxpDrv: %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("NxpDrv: %s: value %d\n", __func__, value);
#ifdef NFC_SECURE_PERIPHERAL_ENABLED
if(secure_peripheral_not_found)
{
/*secure peripheral feature is not enabled*/
gpio_set_value(nfc_gpio->ven, value);
}
else
{
/*secure peripheral feature is enabled*/
if(!nfc_hw_secure_check())
gpio_set_value(nfc_gpio->ven, value);
}
#else
gpio_set_value(nfc_gpio->ven, value);
#endif
/* hardware dependent delay */
usleep_range(NFC_GPIO_SET_WAIT_TIME_US,
NFC_GPIO_SET_WAIT_TIME_US + 100);
}
}
int configure_gpio(unsigned int gpio, int flag)
{
int ret;
pr_debug("NxpDrv: %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("NxpDrv: %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("NxpDrv: %s: nfc o/p gpio %d level %d\n", __func__,
gpio, gpio_get_value(gpio));
} else {
ret = gpio_direction_input(gpio);
pr_debug("NxpDrv: %s: nfc i/p gpio %d\n", __func__, gpio);
}
if (ret) {
pr_err("NxpDrv: %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("NxpDrv: %s: unable to set irq [%d]\n", __func__,
gpio);
gpio_free(gpio);
return ret;
}
pr_debug("NxpDrv: %s: gpio_to_irq successful [%d]\n", __func__,
gpio);
return ret;
}
} else {
pr_err("NxpDrv: %s: invalid gpio\n", __func__);
ret = -EINVAL;
}
return ret;
}
void gpio_free_all(struct nfc_dev *nfc_dev)
{
struct platform_gpio *nfc_gpio = &nfc_dev->configs.gpio;
if (gpio_is_valid(nfc_gpio->clkreq))
gpio_free(nfc_gpio->clkreq);
if (gpio_is_valid(nfc_gpio->dwl_req))
gpio_free(nfc_gpio->dwl_req);
if (gpio_is_valid(nfc_gpio->irq))
gpio_free(nfc_gpio->irq);
if (gpio_is_valid(nfc_gpio->ven))
gpio_free(nfc_gpio->ven);
}
void nfc_misc_unregister(struct nfc_dev *nfc_dev, int count)
{
pr_debug("NxpDrv: %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("NxpDrv: %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("NxpDrv: %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("NxpDrv: %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("NxpDrv: %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_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;
}
/**
* nfc_gpio_info() - gets the status of nfc gpio pins and encodes into a byte.
* @nfc_dev: nfc device data structure
* @arg: userspace buffer
*
* Encoding can be done in following manner
* 1) map the gpio value into INVALID(-2), SET(1), RESET(0).
* 2) mask the first 2 bits of gpio.
* 3) left shift the 2 bits as multiple of 2.
* 4) multiply factor can be defined as position of gpio pin in struct platform_gpio
*
* Return: -EFAULT, if unable to copy the data from kernel space to userspace, 0
* if Success(or no issue)
*/
static int nfc_gpio_info(struct nfc_dev *nfc_dev, unsigned long arg)
{
unsigned int gpios_status = 0;
int value = 0;
int gpio_no = 0;
int i;
int ret = 0;
struct platform_gpio *nfc_gpio = &nfc_dev->configs.gpio;
for (i = 0; i < sizeof(struct platform_gpio) / sizeof(unsigned int);
i++) {
gpio_no = *((unsigned int *)nfc_gpio + i);
value = get_valid_gpio(gpio_no);
if (value < 0)
value = -2;
gpios_status |= (value & GPIO_STATUS_MASK_BITS)<<(GPIO_POS_SHIFT_VAL*i);
}
ret = copy_to_user((uint32_t *) arg, &gpios_status, sizeof(value));
if (ret < 0) {
pr_err("NxpDrv: %s : Unable to copy data from kernel space to user space", __func__);
return -EFAULT;
}
return 0;
}
/**
* 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 common.h for args
*
* Return: -ENOIOCTLCMD if arg is not supported, 0 if Success(or no issue)
* and error ret code otherwise
*/
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;
nfc_dev->nfc_state = NFC_STATE_NCI;
} 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;
nfc_dev->nfc_state = NFC_STATE_NCI;
} 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("NxpDrv: 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("NxpDrv: %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("NxpDrv: 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) {
if (nfc_dev->configs.clk_pin_voting) {
/* Enabling nfc clock */
ret = nfc_clock_select(nfc_dev);
if (ret)
pr_err("%s unable to select clock\n", __func__);
}
/* Setting flag true when NFC is enabled */
nfc_dev->cold_reset.is_nfc_enabled = true;
} else if (arg == NFC_DISABLE) {
if (nfc_dev->configs.clk_pin_voting) {
/* Disabling nfc clock */
ret = nfc_clock_deselect(nfc_dev);
if (ret)
pr_err("%s unable to disable clock\n", __func__);
}
/* Setting flag true when NFC is disabled */
nfc_dev->cold_reset.is_nfc_enabled = false;
} else {
pr_err("NxpDrv: %s: bad arg %lu\n", __func__, arg);
ret = -ENOIOCTLCMD;
}
return ret;
}
#ifdef CONFIG_COMPAT
/**
* nfc_dev_compat_ioctl - used to set or get data from upper layer.
* @pfile file node for opened device.
* @cmd ioctl type from upper layer.
* @arg ioctl arg from upper layer.
*
* NFC and ESE Device power control, based on the argument value
*
* Return: -ENOIOCTLCMD if arg is not supported
* 0 if Success(or no issue)
* 0 or 1 in case of arg is ESE_GET_PWR/ESE_POWER_STATE
* and error ret code otherwise
*/
long nfc_dev_compat_ioctl(struct file *pfile, unsigned int cmd,
unsigned long arg)
{
int ret = 0;
arg = (compat_u64) arg;
pr_debug("NxpDrv: %s: cmd = %x arg = %zx\n", __func__, cmd, arg);
ret = nfc_dev_ioctl(pfile, cmd, arg);
return ret;
}
#endif
/**
* nfc_post_init() - Configuraing Ven GPIO and hardware check
* @nfc_dev: nfc device data structure
*
* Configure GPIOs post notification from TZ, ensuring it's a non-secure zone.
*
* Return: 0 if Success(or no issue) and error ret code otherwise
*/
int nfc_post_init(struct nfc_dev *nfc_dev)
{
int ret=0;
unsigned int clkreq_gpio = 0;
static int post_init_success;
struct platform_configs nfc_configs;
struct platform_gpio *nfc_gpio;
if(post_init_success)
return 0;
if (!nfc_dev)
return -ENODEV;
memcpy(&nfc_configs, &nfc_dev->configs, sizeof(struct platform_configs));
nfc_gpio = &nfc_configs.gpio;
ret = configure_gpio(nfc_gpio->ven, GPIO_OUTPUT);
if (ret) {
pr_err("NxpDrv: %s: unable to request nfc reset gpio [%d]\n",
__func__, nfc_gpio->ven);
return ret;
}
ret = configure_gpio(nfc_gpio->dwl_req, GPIO_OUTPUT);
if (ret) {
pr_err("NxpDrv: %s: unable to request nfc firm downl gpio [%d]\n",
__func__, nfc_gpio->dwl_req);
}
ret = configure_gpio(nfc_gpio->clkreq, GPIO_INPUT);
if (ret) {
pr_err("NxpDrv: %s: unable to request nfc clkreq gpio [%d]\n",
__func__, nfc_gpio->clkreq);
}
/* Read clkreq GPIO number from device tree*/
ret = of_property_read_u32_index(nfc_dev->i2c_dev.client->dev.of_node,
DTS_CLKREQ_GPIO_STR, 1, &clkreq_gpio);
if (ret < 0) {
pr_err("NxpDrv: %s Failed to read clkreq gipo number, ret: %d\n",
__func__, ret);
return ret;
}
/* configure clkreq GPIO as wakeup capable */
ret = msm_gpio_mpm_wake_set(clkreq_gpio, true);
if (ret < 0) {
pr_err("NxpDrv: %s clkreq gpio %d as wakeup capable failed, ret: %d\n",
__func__, clkreq_gpio, ret);
return ret;
}
ret = nfcc_hw_check(nfc_dev);
if (ret || nfc_dev->nfc_state == NFC_STATE_UNKNOWN) {
pr_err("NxpDrv: nfc hw check failed ret %d\n", ret);
gpio_free(nfc_gpio->dwl_req);
gpio_free(nfc_gpio->ven);
return ret;
}
#ifdef NFC_SECURE_PERIPHERAL_ENABLED
/*Initialising sempahore to disbale NFC Ven GPIO only after eSE is power off flag is set */
if (nfc_dev->configs.CNSS_NFC_HW_SECURE_ENABLE == true) {
sema_init(&sem_eSE_pwr_off,0);
}
#endif
post_init_success = 1;
pr_info("NxpDrv: %s success\n", __func__);
return 0;
}
#ifdef NFC_SECURE_PERIPHERAL_ENABLED
/**
* nfc_hw_secure_check() - Checks the NFC secure zone status
*
* Queries the TZ secure libraries if NFC is in secure zone statue or not.
*
* Return: 0 if FEATURE_NOT_SUPPORTED or PERIPHERAL_NOT_FOUND or nfc_sec_state = 2(non-secure zone) and
* return 1 if nfc_sec_state = 1(secure zone) or error otherwise
*/
bool nfc_hw_secure_check(void)
{
struct Object client_env;
struct Object app_object;
u32 nfc_uid = HW_NFC_UID;
union ObjectArg obj_arg[2] = {{{0, 0}}};
int ret;
bool retstat = 1;
u8 nfc_sec_state = 0;
/* get rootObj */
ret = get_client_env_object(&client_env);
if (ret) {
pr_err("NxpDrv: Failed to get client_env_object, ret: %d\n", ret);
return retstat;
}
ret = IClientEnv_open(client_env, HW_STATE_UID, &app_object);
if (ret) {
pr_debug("NxpDrv: Failed to get app_object, ret: %d\n", ret);
if (ret == FEATURE_NOT_SUPPORTED) {
retstat = 0; /* Do not Assert */
pr_debug("NxpDrv: Secure HW feature not supported\n");
}
goto exit_release_clientenv;
}
obj_arg[0].b = (struct ObjectBuf) {&nfc_uid, sizeof(u32)};
obj_arg[1].b = (struct ObjectBuf) {&nfc_sec_state, sizeof(u8)};
ret = Object_invoke(app_object, HW_OP_GET_STATE, obj_arg,
ObjectCounts_pack(1, 1, 0, 0));
pr_info("NxpDrv: TZ ret: %d nfc_sec_state: %d\n", ret, nfc_sec_state);
if (ret) {
if (ret == PERIPHERAL_NOT_FOUND) {
retstat = 0; /* Do not Assert */
pr_debug("NxpDrv: Secure HW mode is not updated. Peripheral not found\n");
}
goto exit_release_app_obj;
}
secure_peripheral_not_found = false;
/* Refer peripheral state utilities for different states of NFC peripherals */
if (nfc_sec_state == 1) {
/*Secure Zone*/
retstat = 1;
} else {
/*Non-Secure Zone*/
retstat = 0;
}
exit_release_app_obj:
Object_release(app_object);
exit_release_clientenv:
Object_release(client_env);
return retstat;
}
/**
* nfc_dynamic_protection_ioctl() - dynamic protection control
* @nfc_dev: nfc device data structure
* @sec_zone_trans: mode that we want to move to
* If sec_zone_trans = 1; transition from non-secure zone to secure zone
* If sec_zone_trans = 0; transition from secure zone to non - secure zone
*
* nfc periheral dynamic protection control. Depending on the sec_zone_trans value, device moves to
* secure zone and non-secure zone
*
* Return: -ENOIOCTLCMD if sec_zone_trans val is not supported, 0 if Success(or no issue)
* and error ret code otherwise
*/
int nfc_dynamic_protection_ioctl(struct nfc_dev *nfc_dev, unsigned long sec_zone_trans)
{
int ret = 0;
static int init_flag=1;
struct platform_gpio *nfc_gpio = &nfc_dev->configs.gpio;
if(sec_zone_trans == 1) {
/*check NFC is disabled, only then set Ven GPIO low*/
if(nfc_dev->cold_reset.is_nfc_enabled == false) {
pr_debug("NxpDrv: %s: value %d\n", __func__, gpio_get_value(nfc_gpio->ven));
chk_eSE_pwr_off = 1;
/*check if eSE is active, if yes, wait max of 1sec, until it's inactive */
if(nfc_dev->is_ese_session_active == true) {
if(down_timeout(&sem_eSE_pwr_off, msecs_to_jiffies(1000))) {
/*waited for 1sec yet eSE not turned off, so, ignoring eSE power off*/
pr_info("NxpDrv: Forcefull shutdown of eSE\n");
}
}
ret = nfc_ioctl_power_states(nfc_dev, 0);
/*set driver as secure zone, such that no ioctl calls are allowed*/
nfc_dev->secure_zone = true;
pr_info("NxpDrv: Driver Secure flag set successful\n");
} else {
ret = -1;
}
}
else if(sec_zone_trans == 0) {
chk_eSE_pwr_off = 0;
nfc_dev->secure_zone = false;
if(init_flag) {
/*Initialize once,only during the first non-secure entry*/
ret = nfc_post_init(nfc_dev);
if(ret == 0)
init_flag=0;
}
else {
if(!gpio_get_value(nfc_gpio->ven))
ret = nfc_ioctl_power_states(nfc_dev, 1);
}
pr_info("NxpDrv: Func Driver Secure flag clear successful\n");
} else {
pr_info("NxpDrv: INVALID ARG\n");
ret = -ENOIOCTLCMD;
}
return ret;
}
#endif
/**
* nfc_dev_ioctl - used to set or get data from upper layer.
* @pfile file node for opened device.
* @cmd ioctl type from upper layer.
* @arg ioctl arg from upper layer.
*
* NFC and ESE Device power control, based on the argument value
*
* Return: -ENOIOCTLCMD if arg is not supported
* 0 if Success(or no issue)
* 0 or 1 in case of arg is ESE_GET_PWR/ESE_POWER_STATE
* and error ret code otherwise
*/
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;
#ifdef NFC_SECURE_PERIPHERAL_ENABLED
if( nfc_dev->configs.CNSS_NFC_HW_SECURE_ENABLE == true) {
/*Avoiding ioctl call in secure zone*/
if(nfc_dev->secure_zone) {
if(cmd!=NFC_SECURE_ZONE) {
pr_debug("NxpDrv: nfc_dev_ioctl failed\n");
return -1;
}
}
}
#endif
pr_debug("NxpDrv: %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 NFC_SET_RESET_READ_PENDING:
if (arg == NFC_SET_READ_PENDING) {
nfc_dev->cold_reset.is_nfc_read_pending = true;
/* Set default NFC state as NCI for Nfc read pending request */
nfc_dev->nfc_state = NFC_STATE_NCI;
} else if (arg == NFC_RESET_READ_PENDING) {
nfc_dev->cold_reset.is_nfc_read_pending = false;
} else {
ret = -EINVAL;
}
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 NFC_GET_GPIO_STATUS:
ret = nfc_gpio_info(nfc_dev, arg);
break;
case NFCC_GET_INFO:
ret = nfc_ioctl_nfcc_info(pfile, arg);
break;
case ESE_COLD_RESET:
pr_debug("NxpDrv: nfc ese cold reset ioctl\n");
ret = ese_cold_reset_ioctl(nfc_dev, arg);
break;
#ifdef NFC_SECURE_PERIPHERAL_ENABLED
case NFC_SECURE_ZONE:
if( nfc_dev->configs.CNSS_NFC_HW_SECURE_ENABLE == true) {
ret = nfc_dynamic_protection_ioctl(nfc_dev, arg);
}
break;
#endif
default:
pr_err("NxpDrv: %s: bad cmd %lu\n", __func__, arg);
ret = -ENOIOCTLCMD;
}
return ret;
}
int nfc_dev_open(struct inode *inode, struct file *filp)
{
struct nfc_dev *nfc_dev = NULL;
nfc_dev = container_of(inode->i_cdev, struct nfc_dev, c_dev);
if (!nfc_dev)
return -ENODEV;
pr_debug("NxpDrv: %s: %d, %d\n", __func__, imajor(inode), iminor(inode));
/* Set flag to block freezer fake signal if not set already.
* Without this Signal being set, Driver is trying to do a read
* which is causing the delay in moving to Hibernate Mode.
*/
if (!(current->flags & PF_NOFREEZE)) {
current->flags |= PF_NOFREEZE;
pr_debug("NxpDrv: %s: current->flags 0x%x. \n", __func__, current->flags);
}
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_flush(struct file *pfile, fl_owner_t id)
{
struct nfc_dev *nfc_dev = pfile->private_data;
if (!nfc_dev)
return -ENODEV;
/*
* release blocked user thread waiting for pending read during close
*/
if (!mutex_trylock(&nfc_dev->read_mutex)) {
nfc_dev->release_read = true;
nfc_dev->nfc_disable_intr(nfc_dev);
wake_up(&nfc_dev->read_wq);
pr_debug("NxpDrv: %s: waiting for release of blocked read\n", __func__);
mutex_lock(&nfc_dev->read_mutex);
nfc_dev->release_read = false;
} else {
pr_debug("NxpDrv: %s: read thread already released\n", __func__);
}
mutex_unlock(&nfc_dev->read_mutex);
return 0;
}
int nfc_dev_close(struct inode *inode, struct file *filp)
{
struct nfc_dev *nfc_dev = NULL;
nfc_dev = container_of(inode->i_cdev, struct nfc_dev, c_dev);
if (!nfc_dev)
return -ENODEV;
pr_debug("NxpDrv: %s: %d, %d\n", __func__, imajor(inode), iminor(inode));
/* unset the flag to restore to previous state */
if (current->flags & PF_NOFREEZE) {
current->flags &= ~PF_NOFREEZE;
pr_debug("NxpDrv: %s: current->flags 0x%x. \n", __func__, current->flags);
}
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 validate_nfc_state_nci(struct nfc_dev *nfc_dev)
{
struct platform_gpio *nfc_gpio = &nfc_dev->configs.gpio;
if(!nfc_dev->secure_zone) {
if (!gpio_get_value(nfc_gpio->ven)) {
pr_err("NxpDrv: %s: ven low - nfcc powered off\n", __func__);
return -ENODEV;
}
}
if (get_valid_gpio(nfc_gpio->dwl_req) == 1) {
pr_err("NxpDrv: %s: fw download in-progress\n", __func__);
return -EBUSY;
}
if (nfc_dev->nfc_state != NFC_STATE_NCI) {
pr_err("NxpDrv: %s: fw download state\n", __func__);
return -EBUSY;
}
return 0;
}

View File

@ -0,0 +1,334 @@
/******************************************************************************
* Copyright (C) 2015, The Linux Foundation. All rights reserved.
* Copyright (C) 2019-2022 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-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*****************************************************************************/
#ifndef _COMMON_H_
#define _COMMON_H_
#include <linux/cdev.h>
#include <linux/of_gpio.h>
#include <linux/delay.h>
#include <linux/ipc_logging.h>
#include <linux/clk.h>
#include <nfcinfo.h>
#include <sn_uapi.h>
#include "i2c_drv.h"
#include "ese_cold_reset.h"
#ifdef NFC_SECURE_PERIPHERAL_ENABLED
/*secure library headers*/
#include "smcinvoke_object.h"
#include "IClientEnv.h"
#endif
/* Max device count for this driver */
#define DEV_COUNT 1
/* i2c 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_CMD (0x20)
#define NCI_RSP (0x40)
#define NCI_NTF (0x60)
#define NCI_HDR_LEN (3)
#define NCI_HDR_IDX (0)
#define DL_CMD 0x00
#define DL_PAYLOAD_BYTE_ZERO 0x00
#define NCI_HDR_OID_IDX (1)
#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 DL_HDR_LEN (2)
#define DL_CRC_LEN (2)
#define NCI_RSP_PKT_TYPE (0x40)
#define MAX_NCI_PAYLOAD_LEN (255)
#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_BUFFER_SIZE (DL_HDR_LEN + DL_CRC_LEN + \
MAX_DL_PAYLOAD_LEN)
/* Retry count for normal write */
#define NO_RETRY (1)
/* Maximum retry count for standby writes */
#define MAX_RETRY_COUNT (3)
#define MAX_WRITE_IRQ_COUNT (5)
#define MAX_IRQ_WAIT_TIME (90)
#define WAKEUP_SRC_TIMEOUT (100)
/* command response timeout */
#define NCI_CMD_RSP_TIMEOUT_MS (2000)
/* Time to wait for NFCC to be ready again after any change in the GPIO */
#define NFC_GPIO_SET_WAIT_TIME_US (10000)
/* Time to wait before retrying writes */
#define WRITE_RETRY_WAIT_TIME_US (3000)
/* Time to wait before retrying read for some specific usecases */
#define READ_RETRY_WAIT_TIME_US (3500)
#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 DTS_SZONE_STR "qcom,sn-szone"
#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
/* Each GPIO occupies consecutive two bits */
#define GPIO_POS_SHIFT_VAL 2
/* Two bits to indicate GPIO status (Invalid(-2), Set(1) or Reset(0)) */
#define GPIO_STATUS_MASK_BITS 3
#ifdef NFC_SECURE_PERIPHERAL_ENABLED
//NFC ID for registration with secure libraries
#define HW_STATE_UID 0x108
#define HW_OP_GET_STATE 1
#define HW_NFC_UID 0x506
#define FEATURE_NOT_SUPPORTED 12
#define PERIPHERAL_NOT_FOUND 10
#endif
#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)
#ifdef NFC_SECURE_PERIPHERAL_ENABLED
static struct semaphore sem_eSE_pwr_off;
static bool chk_eSE_pwr_off;
#endif
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,
};
enum nfc_read_pending {
NFC_RESET_READ_PENDING,
NFC_SET_READ_PENDING,
};
/* 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;
const char *clk_src_name;
/* NFC_CLK pin voting state */
bool clk_pin_voting;
const char *szone;
#ifdef NFC_SECURE_PERIPHERAL_ENABLED
bool CNSS_NFC_HW_SECURE_ENABLE;
#endif
};
/* 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;
bool release_read;
union {
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;
/*secure zone state*/
bool secure_zone;
/* CLK control */
bool clk_run;
struct clk *s_clk;
void *ipcl;
/* function pointers for the common i2c functionality */
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_flush(struct file *pfile, fl_owner_t id);
int nfc_dev_close(struct inode *inode, struct file *filp);
long nfc_dev_compat_ioctl(struct file *pfile, unsigned int cmd,
unsigned long arg);
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);
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);
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 nfc_clock_select(struct nfc_dev *nfc_dev);
int nfc_clock_deselect(struct nfc_dev *nfc_dev);
int nfc_post_init(struct nfc_dev *nfc_dev);
int nfc_dynamic_protection_ioctl(struct nfc_dev *nfc_dev, unsigned long sec_zone_trans);
bool nfc_hw_secure_check(void);
#endif /* _COMMON_H_ */

View File

@ -0,0 +1,333 @@
// 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("NxpDrv: %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("NxpDrv: %s: - nfc get version cmd error ret %d\n", __func__, ret);
goto err;
}
memset(rsp, 0x00, DL_GET_VERSION_RSP_LEN_2);
pr_debug("NxpDrv: %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("NxpDrv: %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("NxpDrv: %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("NxpDrv: %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("NxpDrv: %s: - nfc get session cmd error ret %d\n", __func__, ret);
goto err;
}
memset(rsp, 0x00, DL_GET_SESSION_STATE_RSP_LEN);
pr_debug("NxpDrv: %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("NxpDrv: %s: - nfc get session rsp error ret %d\n", __func__, ret);
goto err;
}
if (rsp[0] != FW_MSG_CMD_RSP) {
pr_err("NxpDrv: %s: - nfc invalid get session state rsp\n", __func__);
goto err;
}
pr_debug("NxpDrv: 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("NxpDrv: %s NFCC booted in FW teared state\n", __func__);
nfc_state = NFC_STATE_FW_TEARED;
} else {
pr_info("NxpDrv: %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("NxpDrv: %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("NxpDrv: %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("NxpDrv: %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("NxpDrv: %s: - nfc nci core reset rsp error ret %d\n", __func__, ret);
goto err_disable_intr;
}
pr_debug("NxpDrv: %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("NxpDrv: %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("NxpDrv: %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("NxpDrv: %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("NxpDrv: %s nfc devices structure is null\n", __func__);
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("NxpDrv: %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("NxpDrv: %s: - NFCC FW Teared State\n", __func__);
break;
case NFC_STATE_FW_DWL:
case NFC_STATE_NCI:
break;
case NFC_STATE_UNKNOWN:
default:
ret = -ENXIO;
pr_err("NxpDrv: %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;
}

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_

View File

@ -0,0 +1,204 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
*
***************************************************************************/
/*
* Copyright (c) 2022-2024 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("NxpDrv: 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("NxpDrv: %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("NxpDrv: %s: set load failed\n", __func__);
return ret;
}
ret = regulator_enable(nfc_dev->reg);
if (ret < 0)
pr_err("NxpDrv: %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("NxpDrv: %s: regulator_get failed, ret = %d\n",
__func__, ret);
return ret;
}
} else {
nfc_dev->reg = NULL;
pr_err("NxpDrv: %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("NxpDrv: %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("NxpDrv: %s: regulator already disabled\n", __func__);
return -EINVAL;
}
ret = regulator_disable(nfc_dev->reg);
if (ret < 0) {
pr_err("NxpDrv: %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("NxpDrv: %s: set voltage failed\n", __func__);
return ret;
}
ret = regulator_set_load(nfc_dev->reg, 0);
if (ret < 0)
pr_err("NxpDrv: %s: set load failed\n", __func__);
return ret;
}
/*
* Routine to enable clock.
* this routine can be extended to select from multiple
* sources based on clk name.
*/
int nfc_clock_select(struct nfc_dev *nfc_dev)
{
int r = 0;
nfc_dev->s_clk = clk_get(&nfc_dev->i2c_dev.client->dev, "nfc_ref_clk");
if (IS_ERR(nfc_dev->s_clk))
return PTR_ERR(nfc_dev->s_clk);
if (!nfc_dev->clk_run)
r = clk_prepare_enable(nfc_dev->s_clk);
if (r)
return r;
nfc_dev->clk_run = true;
return r;
}
/*
* Routine to disable clocks
*/
int nfc_clock_deselect(struct nfc_dev *nfc_dev)
{
int r = -EINVAL;
if (nfc_dev->s_clk != NULL) {
if (nfc_dev->clk_run) {
clk_disable_unprepare(nfc_dev->s_clk);
nfc_dev->clk_run = false;
}
return 0;
}
return r;
}

View File

@ -0,0 +1,407 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
*
***************************************************************************/
/*
* Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
*
***************************************************************************/
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/uaccess.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;
#ifdef NFC_SECURE_PERIPHERAL_ENABLED
if (nfc_dev->configs.CNSS_NFC_HW_SECURE_ENABLE == true) {
if(chk_eSE_pwr_off)
up(&sem_eSE_pwr_off);
}
#endif
} 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.
* @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;
}
if (nfc_dev->cold_reset.cmd_buf == NULL)
return -EFAULT;
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;
if (cold_rst->rsp_len < COLD_RESET_RSP_LEN) {
dev_err(nfc_dev->nfc_device,
"%s: received cold reset rsp buffer length is invalid \n",
__func__);
return -EINVAL;
}
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) ||
(!cold_rst->cmd_buf) ||
(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_MS);
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;
struct ese_cold_reset_arg *cold_reset_arg = NULL;
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;
}
cold_reset_arg = kzalloc(sizeof(struct ese_cold_reset_arg), GFP_KERNEL);
if (!cold_reset_arg)
return -ENOMEM;
mutex_lock(&nfc_dev->write_mutex);
ret = copy_struct_from_user(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 (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 &&
(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",
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 (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;
}
fallthrough;
case ESE_COLD_RESET_PROTECT_DIS:
if (nfc_dev->cold_reset.is_crp_en &&
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 (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__,
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 (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 = cold_reset_arg->src;
} else if (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", cold_reset_arg->sub_cmd);
ret = nfc_dev->cold_reset.status;
err:
if (nfc_dev->cold_reset.cmd_buf != NULL) {
kfree(nfc_dev->cold_reset.cmd_buf);
nfc_dev->cold_reset.cmd_buf = NULL;
}
if (cold_reset_arg != NULL) {
kfree(cold_reset_arg);
cold_reset_arg = NULL;
}
mutex_unlock(&nfc_dev->write_mutex);
return ret;
}

View File

@ -0,0 +1,81 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. 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
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;
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;
bool is_nfc_read_pending;
};
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

View File

@ -0,0 +1,609 @@
/******************************************************************************
* Copyright (C) 2015, The Linux Foundation. All rights reserved.
* Copyright (C) 2013-2022 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.
*
****************************************************************************/
/*
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*
****************************************************************************/
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/uaccess.h>
#include <linux/gpio.h>
#ifdef CONFIG_COMPAT
#include <linux/compat.h>
#endif
#include "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("NxpDrv: %s: reading %zu bytes.\n", __func__, count);
if (timeout > NCI_CMD_RSP_TIMEOUT_MS)
timeout = NCI_CMD_RSP_TIMEOUT_MS;
if (count > MAX_NCI_BUFFER_SIZE)
count = MAX_NCI_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("NxpDrv: %s: timeout error\n",
__func__);
goto err;
}
} else {
ret = wait_event_interruptible(
nfc_dev->read_wq,
!i2c_dev->irq_enabled);
if (ret) {
pr_err("NxpDrv: %s: err wakeup of wq\n",
__func__);
goto err;
}
}
}
i2c_disable_irq(nfc_dev);
if (gpio_get_value(nfc_gpio->irq))
break;
if(!nfc_dev->secure_zone) {
if (!gpio_get_value(nfc_gpio->ven)) {
pr_info("NxpDrv: %s: releasing read\n", __func__);
ret = -EIO;
goto err;
}
}
/*
* NFC service wanted to close the driver so,
* release the calling reader thread asap.
*
* This can happen in case of nfc node close call from
* eSE HAL in that case the NFC HAL reader thread
* will again call read system call
*/
if (nfc_dev->release_read) {
pr_debug("NxpDrv: %s: releasing read\n", __func__);
return 0;
}
pr_warn("NxpDrv: %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("NxpDrv: %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 sent by 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);
struct platform_gpio *nfc_gpio = &nfc_dev->configs.gpio;
if (count > MAX_DL_BUFFER_SIZE)
count = MAX_DL_BUFFER_SIZE;
pr_debug("NxpDrv: %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
* This is to avoid any packet corruption during read, when
* the host cmds resets NFCC during any parallel read operation
*/
for (retry_cnt = 1; retry_cnt <= MAX_WRITE_IRQ_COUNT; retry_cnt++) {
if (gpio_get_value(nfc_gpio->irq)) {
pr_warn("NxpDrv: %s: irq high during write, wait\n", __func__);
usleep_range(WRITE_RETRY_WAIT_TIME_US,
WRITE_RETRY_WAIT_TIME_US + 100);
} else {
break;
}
if (retry_cnt == MAX_WRITE_IRQ_COUNT &&
gpio_get_value(nfc_gpio->irq)) {
pr_warn("NxpDrv: %s: allow after maximum wait\n", __func__);
}
}
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("NxpDrv: %s: write failed ret(%d), maybe in standby\n",
__func__, ret);
usleep_range(WRITE_RETRY_WAIT_TIME_US,
WRITE_RETRY_WAIT_TIME_US + 100);
} else if (ret != count) {
pr_err("NxpDrv: %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;
struct nfc_dev *nfc_dev = (struct nfc_dev *)filp->private_data;
if (!nfc_dev) {
pr_err("NxpDrv: %s: device doesn't exist anymore\n", __func__);
return -ENODEV;
}
mutex_lock(&nfc_dev->read_mutex);
if (count > MAX_NCI_BUFFER_SIZE)
count = MAX_NCI_BUFFER_SIZE;
if (filp->f_flags & O_NONBLOCK) {
ret = i2c_master_recv(nfc_dev->i2c_dev.client, nfc_dev->read_kbuf, count);
pr_debug("NxpDrv: %s: NONBLOCK read ret = %d\n", __func__, ret);
} else {
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("NxpDrv: %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) {
pr_err("NxpDrv: %s: device doesn't exist anymore\n", __func__);
return -ENODEV;
}
mutex_lock(&nfc_dev->write_mutex);
if (copy_from_user(nfc_dev->write_kbuf, buf, count)) {
pr_err("NxpDrv: %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,
.flush = nfc_dev_flush,
.release = nfc_dev_close,
.unlocked_ioctl = nfc_dev_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = nfc_dev_compat_ioctl,
#endif
};
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 = NULL;
struct platform_gpio *nfc_gpio = NULL;
pr_debug("NxpDrv: %s: enter\n", __func__);
nfc_dev = kzalloc(sizeof(struct nfc_dev), GFP_KERNEL);
if (nfc_dev == NULL) {
ret = -ENOMEM;
goto err;
}
nfc_configs = &nfc_dev->configs;
nfc_gpio = &nfc_configs->gpio;
/* retrieve details of gpios from dt */
ret = nfc_parse_dt(&client->dev,nfc_configs, PLATFORM_IF_I2C);
if (ret) {
pr_err("NxpDrv: %s: failed to parse dt\n", __func__);
goto err_free_nfc_dev;
}
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
pr_err("NxpDrv: %s: need I2C_FUNC_I2C\n", __func__);
ret = -ENODEV;
goto err_free_nfc_dev;
}
nfc_dev->read_kbuf = kzalloc(MAX_NCI_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;
ret = configure_gpio(nfc_gpio->irq, GPIO_IRQ);
if (ret <= 0) {
pr_err("NxpDrv: %s: unable to request nfc irq gpio [%d]\n", __func__,
nfc_gpio->irq);
goto err_free_gpio;
}
client->irq = ret;
/* 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("NxpDrv: %s: nfc_misc_register failed\n", __func__);
goto err_mutex_destroy;
}
/* interrupt initializations */
pr_info("NxpDrv: %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("NxpDrv: %s: request_irq failed\n", __func__);
goto err_nfc_misc_unregister;
}
i2c_disable_irq(nfc_dev);
ret = nfc_ldo_config(&client->dev, nfc_dev);
if (ret) {
pr_err("NxpDrv: LDO config failed\n");
goto err_ldo_config_failed;
}
#ifdef NFC_SECURE_PERIPHERAL_ENABLED
if( nfc_dev->configs.CNSS_NFC_HW_SECURE_ENABLE == true) {
/*Check NFC Secure Zone status*/
if(!nfc_hw_secure_check()) {
nfc_post_init(nfc_dev);
nfc_dev->secure_zone = false;
}
else {
nfc_dev->secure_zone = true;
}
pr_info("NxpDrv: %s:nfc secure_zone = %s", __func__, nfc_dev->secure_zone ? "true" : "false");
}else {
nfc_post_init(nfc_dev);
}
#else
nfc_dev->secure_zone = false;
nfc_post_init(nfc_dev);
#endif
if (nfc_dev->configs.clk_pin_voting)
nfc_dev->clk_run = false;
else
nfc_dev->clk_run = true;
device_init_wakeup(&client->dev, true);
i2c_set_clientdata(client, nfc_dev);
i2c_dev->irq_wake_up = false;
nfc_dev->is_ese_session_active = false;
pr_info("NxpDrv: %s: probing nfc i2c success\n", __func__);
return 0;
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);
err_free_gpio:
gpio_free_all(nfc_dev);
kfree(nfc_dev->write_kbuf);
err_free_read_kbuf:
kfree(nfc_dev->read_kbuf);
err_free_nfc_dev:
kfree(nfc_dev);
err:
pr_err("NxpDrv: %s: probing not successful, check hardware\n", __func__);
return ret;
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0))
void nfc_i2c_dev_remove(struct i2c_client *client)
#else
int nfc_i2c_dev_remove(struct i2c_client *client)
#endif
{
struct nfc_dev *nfc_dev = NULL;
pr_info("NxpDrv: %s: remove device\n", __func__);
nfc_dev = i2c_get_clientdata(client);
if (!nfc_dev) {
pr_err("NxpDrv: %s: device doesn't exist anymore\n", __func__);
#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0))
return -ENODEV;
#endif
}
if (nfc_dev->dev_ref_count > 0) {
pr_err("NxpDrv: %s: device already in use\n", __func__);
#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0))
return -EBUSY;
#endif
}
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);
gpio_free_all(nfc_dev);
kfree(nfc_dev->read_kbuf);
kfree(nfc_dev->write_kbuf);
kfree(nfc_dev);
#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0))
return 0;
#endif
}
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("NxpDrv: %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;
}
pr_debug("NxpDrv: %s: irq_wake_up = %d", __func__, i2c_dev->irq_wake_up);
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("NxpDrv: %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;
}
pr_debug("NxpDrv: %s: irq_wake_up = %d", __func__, i2c_dev->irq_wake_up);
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;
pr_info("NxpDrv: %s: Loading NXP NFC I2C driver\n", __func__);
ret = i2c_add_driver(&nfc_i2c_dev_driver);
if (ret != 0)
pr_err("NxpDrv: %s: NFC I2C add driver error ret %d\n", __func__, ret);
return ret;
}
module_init(nfc_i2c_dev_init);
static void __exit nfc_i2c_dev_exit(void)
{
pr_info("NxpDrv: %s: Unloading NXP NFC I2C driver\n", __func__);
i2c_del_driver(&nfc_i2c_dev_driver);
}
module_exit(nfc_i2c_dev_exit);
MODULE_DESCRIPTION("NXP NFC I2C driver");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,66 @@
/******************************************************************************
* Copyright (C) 2015, 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 _I2C_DRV_H_
#define _I2C_DRV_H_
#include <linux/i2c.h>
#include <linux/version.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);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0))
void nfc_i2c_dev_remove(struct i2c_client *client);
#else
int nfc_i2c_dev_remove(struct i2c_client *client);
#endif
int nfc_i2c_dev_suspend(struct device *device);
int nfc_i2c_dev_resume(struct device *device);
#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

@ -0,0 +1,22 @@
# Build NFC kernel driver
NFC_DLKM_ENABLED := false
########## Check and set local DLKM flag based on system-wide global flags ##########
ifeq ($(TARGET_KERNEL_DLKM_DISABLE), true)
ifeq ($(TARGET_KERNEL_DLKM_NFC_OVERRIDE), true)
NFC_DLKM_ENABLED := true
endif
else
NFC_DLKM_ENABLED := true
endif
########## Build kernel module based on local DLKM flag status ##########
ifeq ($(NFC_DLKM_ENABLED), true)
ifeq ($(call is-board-platform-in-list, pineapple blair pitti volcano),true)
BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/nxp-nci.ko
endif
endif
ifeq ($(call is-board-platform-in-list, blair pitti volcano),true)
TARGET_ENABLE_PERIPHERAL_CONTROL := false
endif

View File

@ -0,0 +1 @@
PRODUCT_PACKAGES += nxp-nci.ko