diff --git a/nxp/opensource/driver/Android.bp b/nxp/opensource/driver/Android.bp new file mode 100644 index 0000000000..be32d9b7b1 --- /dev/null +++ b/nxp/opensource/driver/Android.bp @@ -0,0 +1,7 @@ +cc_library_headers { + name: "qti_nfc_kernel_headers", + export_include_dirs: [ + "include/uapi/linux/nfc", + ], + vendor_available: true, +} diff --git a/nxp/opensource/driver/Android.mk b/nxp/opensource/driver/Android.mk new file mode 100644 index 0000000000..45262c6e68 --- /dev/null +++ b/nxp/opensource/driver/Android.mk @@ -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 diff --git a/nxp/opensource/driver/BUILD.bazel b/nxp/opensource/driver/BUILD.bazel new file mode 100644 index 0000000000..8079cdcf6c --- /dev/null +++ b/nxp/opensource/driver/BUILD.bazel @@ -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") diff --git a/nxp/opensource/driver/Kbuild b/nxp/opensource/driver/Kbuild new file mode 100644 index 0000000000..1e4a75aad8 --- /dev/null +++ b/nxp/opensource/driver/Kbuild @@ -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 + diff --git a/nxp/opensource/driver/Kconfig b/nxp/opensource/driver/Kconfig new file mode 100644 index 0000000000..facfd412a1 --- /dev/null +++ b/nxp/opensource/driver/Kconfig @@ -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. diff --git a/nxp/opensource/driver/LICENSE b/nxp/opensource/driver/LICENSE new file mode 100644 index 0000000000..8cdb8451d9 --- /dev/null +++ b/nxp/opensource/driver/LICENSE @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 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. + diff --git a/nxp/opensource/driver/Makefile b/nxp/opensource/driver/Makefile new file mode 100644 index 0000000000..2f7bcf4f7e --- /dev/null +++ b/nxp/opensource/driver/Makefile @@ -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 diff --git a/nxp/opensource/driver/README.md b/nxp/opensource/driver/README.md new file mode 100644 index 0000000000..f499907fd4 --- /dev/null +++ b/nxp/opensource/driver/README.md @@ -0,0 +1,2 @@ +# NXPNFC_I2CDriver +NFC I2C Open Source driver diff --git a/nxp/opensource/driver/config/gki_nfc.conf b/nxp/opensource/driver/config/gki_nfc.conf new file mode 100644 index 0000000000..28cd8d20ed --- /dev/null +++ b/nxp/opensource/driver/config/gki_nfc.conf @@ -0,0 +1 @@ +export CONFIG_NXP_NFC_I2C=m diff --git a/nxp/opensource/driver/config/gki_nfc_conf.h b/nxp/opensource/driver/config/gki_nfc_conf.h new file mode 100644 index 0000000000..76869b9838 --- /dev/null +++ b/nxp/opensource/driver/config/gki_nfc_conf.h @@ -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 diff --git a/nxp/opensource/driver/define_modules.bzl b/nxp/opensource/driver/define_modules.bzl new file mode 100644 index 0000000000..37aa03dae9 --- /dev/null +++ b/nxp/opensource/driver/define_modules.bzl @@ -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"}, + ) + diff --git a/nxp/opensource/driver/include/uapi/linux/nfc/nfcinfo.h b/nxp/opensource/driver/include/uapi/linux/nfc/nfcinfo.h new file mode 100644 index 0000000000..aaf78b3928 --- /dev/null +++ b/nxp/opensource/driver/include/uapi/linux/nfc/nfcinfo.h @@ -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 + +#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 diff --git a/nxp/opensource/driver/include/uapi/linux/nfc/sn_uapi.h b/nxp/opensource/driver/include/uapi/linux/nfc/sn_uapi.h new file mode 100644 index 0000000000..cc87bc449e --- /dev/null +++ b/nxp/opensource/driver/include/uapi/linux/nfc/sn_uapi.h @@ -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 + +/* 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 diff --git a/nxp/opensource/driver/nfc/common.c b/nxp/opensource/driver/nfc/common.c new file mode 100644 index 0000000000..c78d6d3a6c --- /dev/null +++ b/nxp/opensource/driver/nfc/common.c @@ -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 +#include +#include +#include +#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; +} diff --git a/nxp/opensource/driver/nfc/common.h b/nxp/opensource/driver/nfc/common.h new file mode 100644 index 0000000000..3c745eb9b7 --- /dev/null +++ b/nxp/opensource/driver/nfc/common.h @@ -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 +#include +#include +#include +#include +#include +#include +#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_ */ diff --git a/nxp/opensource/driver/nfc/common_nxp.c b/nxp/opensource/driver/nfc/common_nxp.c new file mode 100644 index 0000000000..e9ee2f71af --- /dev/null +++ b/nxp/opensource/driver/nfc/common_nxp.c @@ -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; +} diff --git a/nxp/opensource/driver/nfc/common_nxp.h b/nxp/opensource/driver/nfc/common_nxp.h new file mode 100644 index 0000000000..ad3e5aabfa --- /dev/null +++ b/nxp/opensource/driver/nfc/common_nxp.h @@ -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_ diff --git a/nxp/opensource/driver/nfc/common_qcom.c b/nxp/opensource/driver/nfc/common_qcom.c new file mode 100644 index 0000000000..1e35e63096 --- /dev/null +++ b/nxp/opensource/driver/nfc/common_qcom.c @@ -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; +} diff --git a/nxp/opensource/driver/nfc/ese_cold_reset.c b/nxp/opensource/driver/nfc/ese_cold_reset.c new file mode 100644 index 0000000000..1e8a018543 --- /dev/null +++ b/nxp/opensource/driver/nfc/ese_cold_reset.c @@ -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 +#include +#include +#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; +} diff --git a/nxp/opensource/driver/nfc/ese_cold_reset.h b/nxp/opensource/driver/nfc/ese_cold_reset.h new file mode 100644 index 0000000000..4d3b811a70 --- /dev/null +++ b/nxp/opensource/driver/nfc/ese_cold_reset.h @@ -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 + +#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 diff --git a/nxp/opensource/driver/nfc/i2c_drv.c b/nxp/opensource/driver/nfc/i2c_drv.c new file mode 100644 index 0000000000..4bf45e7b74 --- /dev/null +++ b/nxp/opensource/driver/nfc/i2c_drv.c @@ -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 +#include +#include +#include +#include +#ifdef CONFIG_COMPAT +#include +#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"); diff --git a/nxp/opensource/driver/nfc/i2c_drv.h b/nxp/opensource/driver/nfc/i2c_drv.h new file mode 100644 index 0000000000..3809c44fdb --- /dev/null +++ b/nxp/opensource/driver/nfc/i2c_drv.h @@ -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 +#include + +#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_ diff --git a/nxp/opensource/driver/nfc_kernel_dlkm_vendor_board.mk b/nxp/opensource/driver/nfc_kernel_dlkm_vendor_board.mk new file mode 100644 index 0000000000..548400b0ef --- /dev/null +++ b/nxp/opensource/driver/nfc_kernel_dlkm_vendor_board.mk @@ -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 diff --git a/nxp/opensource/driver/nfc_kernel_dlkm_vendor_product.mk b/nxp/opensource/driver/nfc_kernel_dlkm_vendor_product.mk new file mode 100644 index 0000000000..fc1442a8e1 --- /dev/null +++ b/nxp/opensource/driver/nfc_kernel_dlkm_vendor_product.mk @@ -0,0 +1 @@ +PRODUCT_PACKAGES += nxp-nci.ko