From ad394ccfc5da5fb73ca244c57e5ce573b02c8e7d Mon Sep 17 00:00:00 2001 From: hanwckf Date: Wed, 21 Dec 2022 16:37:27 +0800 Subject: [PATCH] package: replace conninfra bin blob with netgear wax220 gpl code source: https://www.downloads.netgear.com/files/GPL/WAX220-V1.0.1.2-gpl-src.zip --- ...t7981_conninfra_20220425-bbf588-obj.tar.xz | Bin 155628 -> 0 bytes dl/mt7986_conninfra-bbf588-obj.tar.xz | Bin 158888 -> 0 bytes package/mtk/drivers/conninfra/Makefile | 9 - package/mtk/drivers/conninfra/src/Makefile | 66 + package/mtk/drivers/conninfra/src/NOTICE | 202 ++ .../conninfra/src/base/include/msg_thread.h | 141 ++ .../drivers/conninfra/src/base/include/osal.h | 399 ++++ .../drivers/conninfra/src/base/include/ring.h | 95 + .../drivers/conninfra/src/base/msg_thread.c | 716 ++++++ package/mtk/drivers/conninfra/src/base/osal.c | 1595 +++++++++++++ package/mtk/drivers/conninfra/src/base/ring.c | 150 ++ .../conninfra/src/core/conninfra_core.c | 1244 ++++++++++ .../src/core/include/conninfra_core.h | 193 ++ .../drivers/conninfra/src/include/conninfra.h | 216 ++ .../conninfra/src/platform/consys_hw.c | 663 ++++++ .../src/platform/consys_hw_plat_data.c | 39 + .../conninfra/src/platform/consys_reg_mng.c | 148 ++ .../drivers/conninfra/src/platform/emi_mng.c | 158 ++ .../src/platform/include/consys_hw.h | 255 ++ .../src/platform/include/consys_reg_base.h | 28 + .../src/platform/include/consys_reg_mng.h | 57 + .../src/platform/include/consys_reg_util.h | 174 ++ .../conninfra/src/platform/include/emi_mng.h | 99 + .../conninfra/src/platform/include/plat_def.h | 31 + .../conninfra/src/platform/include/pmic_mng.h | 110 + .../src/platform/mt7981/include/mt7981.h | 59 + .../mt7981/include/mt7981_consys_reg.h | 132 ++ .../mt7981/include/mt7981_consys_reg_offset.h | 243 ++ .../src/platform/mt7981/include/mt7981_emi.h | 72 + .../src/platform/mt7981/include/mt7981_pmic.h | 72 + .../src/platform/mt7981/include/mt7981_pos.h | 63 + .../conninfra/src/platform/mt7981/mt7981.c | 162 ++ .../src/platform/mt7981/mt7981_consys_reg.c | 172 ++ .../src/platform/mt7981/mt7981_emi.c | 50 + .../src/platform/mt7981/mt7981_pmic.c | 95 + .../src/platform/mt7981/mt7981_pos.c | 1715 ++++++++++++++ .../src/platform/mt7986/include/mt7986.h | 59 + .../mt7986/include/mt7986_consys_reg.h | 132 ++ .../mt7986/include/mt7986_consys_reg_offset.h | 243 ++ .../src/platform/mt7986/include/mt7986_emi.h | 72 + .../src/platform/mt7986/include/mt7986_pmic.h | 72 + .../src/platform/mt7986/include/mt7986_pos.h | 64 + .../conninfra/src/platform/mt7986/mt7986.c | 163 ++ .../src/platform/mt7986/mt7986_consys_reg.c | 172 ++ .../src/platform/mt7986/mt7986_emi.c | 50 + .../src/platform/mt7986/mt7986_pmic.c | 95 + .../src/platform/mt7986/mt7986_pos.c | 2043 +++++++++++++++++ .../drivers/conninfra/src/platform/pmic_mng.c | 145 ++ .../mtk/drivers/conninfra/src/src/conninfra.c | 263 +++ .../drivers/conninfra/src/src/conninfra_dev.c | 344 +++ 50 files changed, 13531 insertions(+), 9 deletions(-) delete mode 100644 dl/mt7981_conninfra_20220425-bbf588-obj.tar.xz delete mode 100644 dl/mt7986_conninfra-bbf588-obj.tar.xz create mode 100644 package/mtk/drivers/conninfra/src/Makefile create mode 100644 package/mtk/drivers/conninfra/src/NOTICE create mode 100644 package/mtk/drivers/conninfra/src/base/include/msg_thread.h create mode 100644 package/mtk/drivers/conninfra/src/base/include/osal.h create mode 100644 package/mtk/drivers/conninfra/src/base/include/ring.h create mode 100644 package/mtk/drivers/conninfra/src/base/msg_thread.c create mode 100644 package/mtk/drivers/conninfra/src/base/osal.c create mode 100644 package/mtk/drivers/conninfra/src/base/ring.c create mode 100644 package/mtk/drivers/conninfra/src/core/conninfra_core.c create mode 100644 package/mtk/drivers/conninfra/src/core/include/conninfra_core.h create mode 100644 package/mtk/drivers/conninfra/src/include/conninfra.h create mode 100644 package/mtk/drivers/conninfra/src/platform/consys_hw.c create mode 100644 package/mtk/drivers/conninfra/src/platform/consys_hw_plat_data.c create mode 100644 package/mtk/drivers/conninfra/src/platform/consys_reg_mng.c create mode 100644 package/mtk/drivers/conninfra/src/platform/emi_mng.c create mode 100644 package/mtk/drivers/conninfra/src/platform/include/consys_hw.h create mode 100644 package/mtk/drivers/conninfra/src/platform/include/consys_reg_base.h create mode 100644 package/mtk/drivers/conninfra/src/platform/include/consys_reg_mng.h create mode 100644 package/mtk/drivers/conninfra/src/platform/include/consys_reg_util.h create mode 100644 package/mtk/drivers/conninfra/src/platform/include/emi_mng.h create mode 100644 package/mtk/drivers/conninfra/src/platform/include/plat_def.h create mode 100644 package/mtk/drivers/conninfra/src/platform/include/pmic_mng.h create mode 100644 package/mtk/drivers/conninfra/src/platform/mt7981/include/mt7981.h create mode 100644 package/mtk/drivers/conninfra/src/platform/mt7981/include/mt7981_consys_reg.h create mode 100644 package/mtk/drivers/conninfra/src/platform/mt7981/include/mt7981_consys_reg_offset.h create mode 100644 package/mtk/drivers/conninfra/src/platform/mt7981/include/mt7981_emi.h create mode 100644 package/mtk/drivers/conninfra/src/platform/mt7981/include/mt7981_pmic.h create mode 100644 package/mtk/drivers/conninfra/src/platform/mt7981/include/mt7981_pos.h create mode 100644 package/mtk/drivers/conninfra/src/platform/mt7981/mt7981.c create mode 100644 package/mtk/drivers/conninfra/src/platform/mt7981/mt7981_consys_reg.c create mode 100644 package/mtk/drivers/conninfra/src/platform/mt7981/mt7981_emi.c create mode 100644 package/mtk/drivers/conninfra/src/platform/mt7981/mt7981_pmic.c create mode 100644 package/mtk/drivers/conninfra/src/platform/mt7981/mt7981_pos.c create mode 100644 package/mtk/drivers/conninfra/src/platform/mt7986/include/mt7986.h create mode 100644 package/mtk/drivers/conninfra/src/platform/mt7986/include/mt7986_consys_reg.h create mode 100644 package/mtk/drivers/conninfra/src/platform/mt7986/include/mt7986_consys_reg_offset.h create mode 100644 package/mtk/drivers/conninfra/src/platform/mt7986/include/mt7986_emi.h create mode 100644 package/mtk/drivers/conninfra/src/platform/mt7986/include/mt7986_pmic.h create mode 100644 package/mtk/drivers/conninfra/src/platform/mt7986/include/mt7986_pos.h create mode 100644 package/mtk/drivers/conninfra/src/platform/mt7986/mt7986.c create mode 100644 package/mtk/drivers/conninfra/src/platform/mt7986/mt7986_consys_reg.c create mode 100644 package/mtk/drivers/conninfra/src/platform/mt7986/mt7986_emi.c create mode 100644 package/mtk/drivers/conninfra/src/platform/mt7986/mt7986_pmic.c create mode 100644 package/mtk/drivers/conninfra/src/platform/mt7986/mt7986_pos.c create mode 100644 package/mtk/drivers/conninfra/src/platform/pmic_mng.c create mode 100644 package/mtk/drivers/conninfra/src/src/conninfra.c create mode 100644 package/mtk/drivers/conninfra/src/src/conninfra_dev.c diff --git a/dl/mt7981_conninfra_20220425-bbf588-obj.tar.xz b/dl/mt7981_conninfra_20220425-bbf588-obj.tar.xz deleted file mode 100644 index 842f956244d7120ce480d0ae5024d541dc9513de..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 155628 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 diff --git a/dl/mt7986_conninfra-bbf588-obj.tar.xz b/dl/mt7986_conninfra-bbf588-obj.tar.xz deleted file mode 100644 index ada92e5616a2b82bfffa1594b35df1f993458596..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 158888 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 bPNnCeA( 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 diff --git a/package/mtk/drivers/conninfra/Makefile b/package/mtk/drivers/conninfra/Makefile index 8b731ae804..17db624d49 100644 --- a/package/mtk/drivers/conninfra/Makefile +++ b/package/mtk/drivers/conninfra/Makefile @@ -9,15 +9,6 @@ include $(TOPDIR)/rules.mk include $(INCLUDE_DIR)/kernel.mk PKG_NAME:=conninfra - -ifeq ($(CONFIG_MTK_CONNINFRA_APSOC_MT7981),y) -PKG_SOURCE:=mt7981_conninfra_20220425-bbf588-obj.tar.xz -else -ifeq ($(CONFIG_MTK_CONNINFRA_APSOC_MT7986),y) -PKG_SOURCE:=mt7986_conninfra-bbf588-obj.tar.xz -endif -endif - PKG_VERSION:=bbf588 PKG_BUILD_DIR:=$(KERNEL_BUILD_DIR)/$(PKG_NAME) PKG_MAINTAINER:=Kun-Ze Syue diff --git a/package/mtk/drivers/conninfra/src/Makefile b/package/mtk/drivers/conninfra/src/Makefile new file mode 100644 index 0000000000..b0a81db708 --- /dev/null +++ b/package/mtk/drivers/conninfra/src/Makefile @@ -0,0 +1,66 @@ +############################################################################### +# Necessary Check + +#ifeq ($(AUTOCONF_H),) +# $(error AUTOCONF_H is not defined) +#endif + +#ccflags-y += -imacros $(AUTOCONF_H) + +# Force build fail on modpost warning +KBUILD_MODPOST_FAIL_ON_WARNINGS := y + +############################################################################### +ccflags-y += -Werror +ccflags-y += -Wno-error=format +ccflags-y += -Wno-error=format-extra-args +ccflags-y += -Wframe-larger-than=1024 + +############################################################################### +MODULE_NAME := conninfra +obj-m += $(MODULE_NAME).o + +############################################################################### +# common_main +############################################################################### +ccflags-y += \ + -I$(SUBDIRS)/include \ + -I$(SUBDIRS)/base/include \ + -I$(SUBDIRS)/core/include \ + -I$(SUBDIRS)/conf/include \ + -I$(SUBDIRS)/platform/include + +$(MODULE_NAME)-objs += base/ring.o +$(MODULE_NAME)-objs += base/osal.o +$(MODULE_NAME)-objs += base/msg_thread.o +$(MODULE_NAME)-objs += core/conninfra_core.o +$(MODULE_NAME)-objs += src/conninfra_dev.o +$(MODULE_NAME)-objs += src/conninfra.o +$(MODULE_NAME)-objs += platform/consys_hw.o +$(MODULE_NAME)-objs += platform/consys_hw_plat_data.o +$(MODULE_NAME)-objs += platform/pmic_mng.o +$(MODULE_NAME)-objs += platform/emi_mng.o +$(MODULE_NAME)-objs += platform/consys_reg_mng.o + +# By Plaftfrom +# MT7986 +ifeq ($(CONFIG_MTK_CONNINFRA_APSOC_MT7986),y) +ccflags-y += \ + -I$(SUBDIRS)/platform/mt7986/include -DCONNINFRA_APSOC_MT7986 +$(MODULE_NAME)-objs += platform/mt7986/mt7986.o +$(MODULE_NAME)-objs += platform/mt7986/mt7986_pmic.o +$(MODULE_NAME)-objs += platform/mt7986/mt7986_consys_reg.o +$(MODULE_NAME)-objs += platform/mt7986/mt7986_pos.o +$(MODULE_NAME)-objs += platform/mt7986/mt7986_emi.o +endif + +# MT7981 +ifeq ($(CONFIG_MTK_CONNINFRA_APSOC_MT7981),y) +ccflags-y += \ + -I$(SUBDIRS)/platform/mt7981/include -DCONNINFRA_APSOC_MT7981 +$(MODULE_NAME)-objs += platform/mt7981/mt7981.o +$(MODULE_NAME)-objs += platform/mt7981/mt7981_pmic.o +$(MODULE_NAME)-objs += platform/mt7981/mt7981_consys_reg.o +$(MODULE_NAME)-objs += platform/mt7981/mt7981_pos.o +$(MODULE_NAME)-objs += platform/mt7981/mt7981_emi.o +endif \ No newline at end of file diff --git a/package/mtk/drivers/conninfra/src/NOTICE b/package/mtk/drivers/conninfra/src/NOTICE new file mode 100644 index 0000000000..52c1cace2c --- /dev/null +++ b/package/mtk/drivers/conninfra/src/NOTICE @@ -0,0 +1,202 @@ +The GNU General Public License (GPL) + +Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. +59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Library 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. + +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 + + + + diff --git a/package/mtk/drivers/conninfra/src/base/include/msg_thread.h b/package/mtk/drivers/conninfra/src/base/include/msg_thread.h new file mode 100644 index 0000000000..6cb462821d --- /dev/null +++ b/package/mtk/drivers/conninfra/src/base/include/msg_thread.h @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2016 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 http://www.gnu.org/licenses/gpl-2.0.html for more details. + */ +/*! \file +* \brief Declaration of library functions +* +* Any definitions in this file will be shared among GLUE Layer and internal Driver Stack. +*/ + +#ifndef _BASE_MSG_THREAD_H_ +#define _BASE_MSG_THREAD_H_ + +#include "osal.h" + +/******************************************************************************* +* C O M P I L E R F L A G S +******************************************************************************** +*/ + +/******************************************************************************* +* M A C R O S +******************************************************************************** +*/ + + +#define MSG_THREAD_OP_DATA_SIZE 8 +#define MSG_THREAD_OP_BUF_SIZE 64 + +/******************************************************************************* +* E X T E R N A L R E F E R E N C E S +******************************************************************************** +*/ + +/******************************************************************************* +* C O N S T A N T S +******************************************************************************** +*/ + +/******************************************************************************* +* D A T A T Y P E S +******************************************************************************** +*/ + +struct msg_op_data { + unsigned int op_id; /* Event ID */ + unsigned int info_bit; /* Reserved */ + size_t op_data[MSG_THREAD_OP_DATA_SIZE]; /* OP Data */ +}; + +struct msg_op { + struct msg_op_data op; + OSAL_SIGNAL signal; + int result; + atomic_t ref_count; +}; + + +struct msg_op_q { + OSAL_SLEEPABLE_LOCK lock; + unsigned int write; + unsigned int read; + unsigned int size; + struct msg_op *queue[MSG_THREAD_OP_BUF_SIZE]; +}; + +typedef OSAL_OP_DAT msg_evt_op; +typedef int(*msg_opid_func) (struct msg_op_data *); + +struct msg_thread_ctx { + OSAL_THREAD thread; /* core thread */ + OSAL_EVENT evt; + + struct msg_op_q free_op_q; /* free op queue */ + struct msg_op_q active_op_q; /* active op queue */ + struct msg_op op_q_inst[MSG_THREAD_OP_BUF_SIZE]; /* real op instances */ + struct msg_op *cur_op; /* current op */ + + int op_func_size; + const msg_opid_func *op_func; + + struct osal_op_history op_history; +}; + + +/******************************************************************************* +* P U B L I C D A T A +******************************************************************************** +*/ + +#define MSG_OP_TIMEOUT 20000 + +int msg_thread_init(struct msg_thread_ctx *ctx, const char *name, + const msg_opid_func *func, int op_size); +int msg_thread_deinit(struct msg_thread_ctx *ctx); + +/* timeout: + * 0: default value (by MSG_OP_TIMEOUT define) + * >0: cutom timeout (ms) + */ +int msg_thread_send(struct msg_thread_ctx *ctx, int opid); +int msg_thread_send_1(struct msg_thread_ctx *ctx, int opid, + size_t param1); +int msg_thread_send_2(struct msg_thread_ctx *ctx, int opid, + size_t param1, size_t param2); + +int msg_thread_send_wait(struct msg_thread_ctx *ctx, int opid, + int timeout); +int msg_thread_send_wait_1(struct msg_thread_ctx *ctx, int opid, + int timeout, size_t param1); +int msg_thread_send_wait_2(struct msg_thread_ctx *ctx, int opid, + int timeout, size_t param1, size_t param2); +int msg_thread_send_wait_3(struct msg_thread_ctx *ctx, int opid, int timeout, size_t param1, + size_t param2,size_t param3); + +int msg_thread_dump(struct msg_thread_ctx *ctx); + +/******************************************************************************* +* P R I V A T E D A T A +******************************************************************************** +*/ + +/******************************************************************************* +* F U N C T I O N D E C L A R A T I O N S +******************************************************************************** +*/ + +/******************************************************************************* +* F U N C T I O N S +******************************************************************************** +*/ + +#endif /* _BASE_MSG_THREAD_H_ */ diff --git a/package/mtk/drivers/conninfra/src/base/include/osal.h b/package/mtk/drivers/conninfra/src/base/include/osal.h new file mode 100644 index 0000000000..2eb2a753ad --- /dev/null +++ b/package/mtk/drivers/conninfra/src/base/include/osal.h @@ -0,0 +1,399 @@ +/* + * Copyright (C) 2016 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 http://www.gnu.org/licenses/gpl-2.0.html for more details. + */ + + +/*! \file + * \brief Declaration of library functions + * Any definitions in this file will be shared among GLUE Layer and internal Driver Stack. + */ + +#ifndef _OSAL_H_ +#define _OSAL_H_ + +#include +#include +#include +#include +#include "ring.h" +/******************************************************************************* +* C O M P I L E R F L A G S +******************************************************************************** +*/ + +/******************************************************************************* +* M A C R O S +******************************************************************************** +*/ + +#define OS_BIT_OPS_SUPPORT 1 + +#ifndef MTK_CONN_BOOL_TRUE +#define MTK_CONN_BOOL_FALSE ((MTK_CONN_BOOL) 0) +#define MTK_CONN_BOOL_TRUE ((MTK_CONN_BOOL) 1) +#endif + +#define _osal_inline_ inline + +#define MAX_THREAD_NAME_LEN 16 +#define MAX_HISTORY_NAME_LEN 16 +#define OSAL_OP_BUF_SIZE 64 + + +#if (defined(CONFIG_MTK_GMO_RAM_OPTIMIZE) && !defined(CONFIG_MTK_ENG_BUILD)) +#define OSAL_OP_DATA_SIZE 8 +#else +#define OSAL_OP_DATA_SIZE 32 +#endif + +#define DBG_LOG_STR_SIZE 256 + +#define osal_sizeof(x) sizeof(x) + +#define osal_array_size(x) ARRAY_SIZE(x) + +#ifndef NAME_MAX +#define NAME_MAX 256 +#endif + +#define WMT_OP_BIT(x) (0x1UL << x) +#define WMT_OP_HIF_BIT WMT_OP_BIT(0) + +#define GET_BIT_MASK(value, mask) ((value) & (mask)) +#define SET_BIT_MASK(pdest, value, mask) (*(pdest) = (GET_BIT_MASK(*(pdest), ~(mask)) | GET_BIT_MASK(value, mask))) +#define GET_BIT_RANGE(data, end, begin) ((data) & GENMASK(end, begin)) +#define SET_BIT_RANGE(pdest, data, end, begin) (SET_BIT_MASK(pdest, data, GENMASK(end, begin))) + +#define RB_LATEST(prb) ((prb)->write - 1) +#define RB_SIZE(prb) ((prb)->size) +#define RB_MASK(prb) (RB_SIZE(prb) - 1) +#define RB_COUNT(prb) ((prb)->write - (prb)->read) +#define RB_FULL(prb) (RB_COUNT(prb) >= RB_SIZE(prb)) +#define RB_EMPTY(prb) ((prb)->write == (prb)->read) + +#define RB_INIT(prb, qsize) \ +do { \ + (prb)->read = (prb)->write = 0; \ + (prb)->size = (qsize); \ +} while (0) + +#define RB_PUT(prb, value) \ +do { \ + if (!RB_FULL(prb)) { \ + (prb)->queue[(prb)->write & RB_MASK(prb)] = value; \ + ++((prb)->write); \ + } \ + else { \ + pr_warn("RB is full!"); \ + } \ +} while (0) + +#define RB_GET(prb, value) \ +do { \ + if (!RB_EMPTY(prb)) { \ + value = (prb)->queue[(prb)->read & RB_MASK(prb)]; \ + ++((prb)->read); \ + if (RB_EMPTY(prb)) { \ + (prb)->read = (prb)->write = 0; \ + } \ + } \ + else { \ + value = NULL; \ + pr_warn("RB is empty!"); \ + } \ +} while (0) + +#define RB_GET_LATEST(prb, value) \ +do { \ + if (!RB_EMPTY(prb)) { \ + value = (prb)->queue[RB_LATEST(prb) & RB_MASK(prb)]; \ + if (RB_EMPTY(prb)) { \ + (prb)->read = (prb)->write = 0; \ + } \ + } \ + else { \ + value = NULL; \ + } \ +} while (0) + + + +/******************************************************************************* +* E X T E R N A L R E F E R E N C E S +******************************************************************************** +*/ + +/******************************************************************************* +* C O N S T A N T S +******************************************************************************** +*/ + +/******************************************************************************* +* D A T A T Y P E S +******************************************************************************** +*/ + +typedef int MTK_CONN_BOOL; + +typedef int(*P_COND) (void *); + +typedef struct _OSAL_UNSLEEPABLE_LOCK_ { + spinlock_t lock; + unsigned long flag; +} OSAL_UNSLEEPABLE_LOCK, *P_OSAL_UNSLEEPABLE_LOCK; + +typedef struct _OSAL_SLEEPABLE_LOCK_ { + struct mutex lock; +} OSAL_SLEEPABLE_LOCK, *P_OSAL_SLEEPABLE_LOCK; + +typedef struct _OSAL_SIGNAL_ { + struct completion comp; + unsigned int timeoutValue; + unsigned int timeoutExtension; /* max number of timeout caused by thread not able to acquire CPU */ +} OSAL_SIGNAL, *P_OSAL_SIGNAL; + +typedef struct _OSAL_EVENT_ { + wait_queue_head_t waitQueue; + unsigned int timeoutValue; + int waitFlag; + +} OSAL_EVENT, *P_OSAL_EVENT; + +/* Data collected from sched_entity and sched_statistics */ +typedef struct _OSAL_THREAD_SCHEDSTATS_ { + unsigned long long time; /* when marked: the profiling start time(ms), when unmarked: total duration(ms) */ + unsigned long long exec; /* time spent in exec (sum_exec_runtime) */ + unsigned long long runnable; /* time spent in run-queue while not being scheduled (wait_sum) */ + unsigned long long iowait; /* time spent waiting for I/O (iowait_sum) */ +} OSAL_THREAD_SCHEDSTATS, *P_OSAL_THREAD_SCHEDSTATS; + +typedef struct _OSAL_THREAD_ { + struct task_struct *pThread; + void *pThreadFunc; + void *pThreadData; + char threadName[MAX_THREAD_NAME_LEN]; +} OSAL_THREAD, *P_OSAL_THREAD; + + +typedef struct _OSAL_FIFO_ { + /*fifo definition */ + void *pFifoBody; + spinlock_t fifoSpinlock; + /*fifo operations */ + int (*FifoInit)(struct _OSAL_FIFO_ *pFifo, unsigned char *buf, unsigned int); + int (*FifoDeInit)(struct _OSAL_FIFO_ *pFifo); + int (*FifoReset)(struct _OSAL_FIFO_ *pFifo); + int (*FifoSz)(struct _OSAL_FIFO_ *pFifo); + int (*FifoAvailSz)(struct _OSAL_FIFO_ *pFifo); + int (*FifoLen)(struct _OSAL_FIFO_ *pFifo); + int (*FifoIsEmpty)(struct _OSAL_FIFO_ *pFifo); + int (*FifoIsFull)(struct _OSAL_FIFO_ *pFifo); + int (*FifoDataIn)(struct _OSAL_FIFO_ *pFifo, const void *buf, unsigned int len); + int (*FifoDataOut)(struct _OSAL_FIFO_ *pFifo, void *buf, unsigned int len); +} OSAL_FIFO, *P_OSAL_FIFO; + +typedef struct firmware osal_firmware; + +typedef struct _OSAL_OP_DAT { + unsigned int opId; /* Event ID */ + unsigned int u4InfoBit; /* Reserved */ + size_t au4OpData[OSAL_OP_DATA_SIZE]; /* OP Data */ +} OSAL_OP_DAT, *P_OSAL_OP_DAT; + +typedef struct _OSAL_LXOP_ { + OSAL_OP_DAT op; + OSAL_SIGNAL signal; + int result; + atomic_t ref_count; +} OSAL_OP, *P_OSAL_OP; + +typedef struct _OSAL_LXOP_Q { + OSAL_SLEEPABLE_LOCK sLock; + unsigned int write; + unsigned int read; + unsigned int size; + P_OSAL_OP queue[OSAL_OP_BUF_SIZE]; +} OSAL_OP_Q, *P_OSAL_OP_Q; + +typedef struct _OSAL_BIT_OP_VAR_ { + unsigned long data; + OSAL_UNSLEEPABLE_LOCK opLock; +} OSAL_BIT_OP_VAR, *P_OSAL_BIT_OP_VAR; + +typedef unsigned int (*P_OSAL_EVENT_CHECKER) (P_OSAL_THREAD pThread); + +struct osal_op_history_entry { + void *opbuf_address; + unsigned int op_id; + unsigned int opbuf_ref_count; + unsigned int op_info_bit; + size_t param_0; + size_t param_1; + size_t param_2; + size_t param_3; + unsigned long long ts; + unsigned long usec; +}; + +struct osal_op_history { + struct ring ring_buffer; + struct osal_op_history_entry *queue; + spinlock_t lock; + struct ring dump_ring_buffer; + struct work_struct dump_work; + unsigned char name[MAX_HISTORY_NAME_LEN]; +}; + +/******************************************************************************* +* P U B L I C D A T A +******************************************************************************** +*/ + +/******************************************************************************* +* P R I V A T E D A T A +******************************************************************************** +*/ + +/******************************************************************************* +* F U N C T I O N D E C L A R A T I O N S +******************************************************************************** +*/ +void FlashRead(char *name,unsigned char *value, unsigned long offset, unsigned long size); +void FlashWrite(char *name, unsigned char *p, unsigned long a, unsigned long b); + +unsigned int osal_strlen(const char *str); +int osal_strcmp(const char *dst, const char *src); +int osal_strncmp(const char *dst, const char *src, unsigned int len); +char *osal_strcpy(char *dst, const char *src); +char *osal_strncpy(char *dst, const char *src, unsigned int len); +char *osal_strcat(char *dst, const char *src); +char *osal_strncat(char *dst, const char *src, unsigned int len); +char *osal_strchr(const char *str, unsigned char c); +char *osal_strsep(char **str, const char *c); +int osal_strtol(const char *str, unsigned int adecimal, long *res); +char *osal_strstr(char *str1, const char *str2); +char *osal_strnstr(char *str1, const char *str2, int n); + +void osal_bug_on(unsigned int val); + +int osal_snprintf(char *buf, unsigned int len, const char *fmt, ...); + +int osal_sprintf(char *str, const char *format, ...); +void *osal_malloc(unsigned int size); +void osal_free(const void *dst); +void *osal_memset(void *buf, int i, unsigned int len); +void *osal_memcpy(void *dst, const void *src, unsigned int len); +void osal_memcpy_fromio(void *dst, const void *src, unsigned int len); +void osal_memcpy_toio(void *dst, const void *src, unsigned int len); +int osal_memcmp(const void *buf1, const void *buf2, unsigned int len); + +int osal_sleep_ms(unsigned int ms); +int osal_udelay(unsigned int us); +int osal_usleep_range(unsigned long min, unsigned long max); + +int osal_fifo_init(P_OSAL_FIFO pFifo, unsigned char *buffer, unsigned int size); +void osal_fifo_deinit(P_OSAL_FIFO pFifo); +int osal_fifo_reset(P_OSAL_FIFO pFifo); +unsigned int osal_fifo_in(P_OSAL_FIFO pFifo, unsigned char *buffer, + unsigned int size); +unsigned int osal_fifo_out(P_OSAL_FIFO pFifo, unsigned char *buffer, + unsigned int size); +unsigned int osal_fifo_len(P_OSAL_FIFO pFifo); +unsigned int osal_fifo_sz(P_OSAL_FIFO pFifo); +unsigned int osal_fifo_avail(P_OSAL_FIFO pFifo); +unsigned int osal_fifo_is_empty(P_OSAL_FIFO pFifo); +unsigned int osal_fifo_is_full(P_OSAL_FIFO pFifo); + +#if defined(CONFIG_PROVE_LOCKING) +#define osal_unsleepable_lock_init(l) { spin_lock_init(&((l)->lock)); } +#else +int osal_unsleepable_lock_init(P_OSAL_UNSLEEPABLE_LOCK); +#endif +int osal_lock_unsleepable_lock(P_OSAL_UNSLEEPABLE_LOCK); +int osal_unlock_unsleepable_lock(P_OSAL_UNSLEEPABLE_LOCK); +int osal_unsleepable_lock_deinit(P_OSAL_UNSLEEPABLE_LOCK); + +#if defined(CONFIG_PROVE_LOCKING) +#define osal_sleepable_lock_init(l) { mutex_init(&((l)->lock)); } +#else +int osal_sleepable_lock_init(P_OSAL_SLEEPABLE_LOCK); +#endif +int osal_lock_sleepable_lock(P_OSAL_SLEEPABLE_LOCK); +int osal_unlock_sleepable_lock(P_OSAL_SLEEPABLE_LOCK); +int osal_trylock_sleepable_lock(P_OSAL_SLEEPABLE_LOCK); +int osal_sleepable_lock_deinit(P_OSAL_SLEEPABLE_LOCK); + +int osal_signal_init(P_OSAL_SIGNAL); +int osal_wait_for_signal(P_OSAL_SIGNAL); +int osal_wait_for_signal_timeout(P_OSAL_SIGNAL, P_OSAL_THREAD); +int osal_raise_signal(P_OSAL_SIGNAL); +int osal_signal_active_state(P_OSAL_SIGNAL pSignal); +int osal_signal_deinit(P_OSAL_SIGNAL); + +int osal_event_init(P_OSAL_EVENT); +int osal_wait_for_event(P_OSAL_EVENT, P_COND, void*); +int osal_wait_for_event_timeout(P_OSAL_EVENT, P_COND, void*); +extern int osal_trigger_event(P_OSAL_EVENT); + +int osal_event_deinit(P_OSAL_EVENT); +long osal_wait_for_event_bit_set(P_OSAL_EVENT pEvent, unsigned long *pState, unsigned int bitOffset); +long osal_wait_for_event_bit_clr(P_OSAL_EVENT pEvent, unsigned long *pState, unsigned int bitOffset); + +int osal_thread_create(P_OSAL_THREAD); +int osal_thread_run(P_OSAL_THREAD); +int osal_thread_should_stop(P_OSAL_THREAD); +int osal_thread_stop(P_OSAL_THREAD); +/*int osal_thread_wait_for_event(P_OSAL_THREAD, P_OSAL_EVENT);*/ +int osal_thread_wait_for_event(P_OSAL_THREAD, P_OSAL_EVENT, P_OSAL_EVENT_CHECKER); +/*check pOsalLxOp and OSAL_THREAD_SHOULD_STOP*/ +int osal_thread_destroy(P_OSAL_THREAD); +int osal_thread_sched_mark(P_OSAL_THREAD, P_OSAL_THREAD_SCHEDSTATS schedstats); +int osal_thread_sched_unmark(P_OSAL_THREAD pThread, P_OSAL_THREAD_SCHEDSTATS schedstats); + +int osal_clear_bit(unsigned int bitOffset, P_OSAL_BIT_OP_VAR pData); +int osal_set_bit(unsigned int bitOffset, P_OSAL_BIT_OP_VAR pData); +int osal_test_bit(unsigned int bitOffset, P_OSAL_BIT_OP_VAR pData); +int osal_test_and_clear_bit(unsigned int bitOffset, P_OSAL_BIT_OP_VAR pData); +int osal_test_and_set_bit(unsigned int bitOffset, P_OSAL_BIT_OP_VAR pData); + +int osal_gettimeofday(int *sec, int *usec); +int osal_gettimeofday2(struct timeval *tv); + +//int osal_printtimeofday(const unsigned char *prefix); +void osal_get_local_time(unsigned long long *sec, unsigned long *nsec); +unsigned long long osal_elapsed_us(unsigned long long ts, unsigned long usec); + +void osal_buffer_dump(const unsigned char *buf, const unsigned char *title, unsigned int len, unsigned int limit); +void osal_buffer_dump_data(const unsigned int *buf, const unsigned char *title, const unsigned int len, const unsigned int limit, + const int flag); + +unsigned int osal_op_get_id(P_OSAL_OP pOp); +MTK_CONN_BOOL osal_op_is_wait_for_signal(P_OSAL_OP pOp); +void osal_op_raise_signal(P_OSAL_OP pOp, int result); +void osal_set_op_result(P_OSAL_OP pOp, int result); +void osal_opq_dump(const char *qName, P_OSAL_OP_Q pOpQ); +void osal_opq_dump_locked(const char *qName, P_OSAL_OP_Q pOpQ); +MTK_CONN_BOOL osal_opq_has_op(P_OSAL_OP_Q pOpQ, P_OSAL_OP pOp); + +int osal_ftrace_print(const char *str, ...); +int osal_ftrace_print_ctrl(int flag); + +void osal_dump_thread_state(const unsigned char *name); +void osal_op_history_init(struct osal_op_history *log_history, int queue_size); +void osal_op_history_save(struct osal_op_history *log_history, P_OSAL_OP pOp); +void osal_op_history_print(struct osal_op_history *log_history, char *name); +/******************************************************************************* +* F U N C T I O N S +******************************************************************************** +*/ + +#endif /* _OSAL_H_ */ diff --git a/package/mtk/drivers/conninfra/src/base/include/ring.h b/package/mtk/drivers/conninfra/src/base/include/ring.h new file mode 100644 index 0000000000..81168ce866 --- /dev/null +++ b/package/mtk/drivers/conninfra/src/base/include/ring.h @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2016 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 http://www.gnu.org/licenses/gpl-2.0.html for more details. + */ +#ifndef _RING_H_ +#define _RING_H_ + +struct ring { + /* addr where ring buffer starts */ + void *base; + /* addr storing the next writable pos, guaranteed to be >= read except when write overflow, but it's ok. */ + unsigned int write; + /* addr storing the next readable pos, except when read == write as buffer empty */ + unsigned int read; + /* must be power of 2 */ + unsigned int max_size; +}; + +struct ring_segment { + /* addr points into ring buffer for read/write */ + void *ring_pt; + /* size to read/write */ + unsigned int sz; + /* pos in external data buffer to read/write */ + unsigned int data_pos; + /* the size to be read/write after this segment completed */ + unsigned int remain; +}; + +void ring_init(void *base, unsigned int max_size, unsigned int read, + unsigned int write, struct ring *ring); +unsigned int ring_read_prepare(unsigned int sz, struct ring_segment *seg, struct ring *ring); +#define ring_read_all_prepare(seg, ring) ring_read_prepare((ring)->max_size, seg, ring) +unsigned int ring_write_prepare(unsigned int sz, struct ring_segment *seg, struct ring *ring); +unsigned int ring_overwrite_prepare(unsigned int sz, + struct ring_segment *seg, struct ring *ring); + +/* making sure max_size is power of 2 */ +#define RING_VALIDATE_SIZE(max_size) WARN_ON(!max_size || (max_size & (max_size - 1))) + +#define RING_EMPTY(ring) ((ring)->read == (ring)->write) +/* equation works even when write overflow */ +#define RING_SIZE(ring) ((ring)->write - (ring)->read) +#define RING_FULL(ring) (RING_SIZE(ring) == (ring)->max_size) +#define RING_WRITE_REMAIN_SIZE(ring) ((ring)->max_size - RING_SIZE(ring)) + +#define RING_READ_FOR_EACH(_sz, _seg, _ring) \ + for (ring_read_prepare(_sz, &(_seg), _ring), \ + _ring_segment_prepare((_ring)->read, &(_seg), (_ring)); \ + (_seg).sz > 0; \ + _ring_read_commit(&(_seg), (_ring)), _ring_segment_prepare((_ring)->read, \ + &(_seg), (_ring))) + +#define RING_READ_ALL_FOR_EACH(seg, ring) RING_READ_FOR_EACH((ring)->max_size, seg, ring) + +#define RING_READ_FOR_EACH_ITEM(_sz, _seg, _ring) \ + for (ring_read_prepare(_sz, &(_seg), _ring), \ + _ring_segment_prepare_item((_ring)->read, &(_seg), (_ring)); \ + (_seg).sz > 0; \ + _ring_read_commit(&(_seg), (_ring)), _ring_segment_prepare_item((_ring)->read, \ + &(_seg), (_ring))) + +#define RING_WRITE_FOR_EACH(_sz, _seg, _ring) \ + for (ring_write_prepare(_sz, &(_seg), _ring),\ + _ring_segment_prepare((_ring)->write, &(_seg), (_ring)); \ + (_seg).sz > 0; \ + _ring_write_commit(&(_seg), (_ring)), _ring_segment_prepare((_ring)->write, \ + &(_seg), (_ring))) + +#define RING_OVERWRITE_FOR_EACH(_sz, _seg, _ring) \ + for (ring_overwrite_prepare(_sz, &(_seg), _ring), \ + _ring_segment_prepare((_ring)->write, &(_seg), (_ring)); \ + (_seg).sz > 0; \ + _ring_write_commit(&(_seg), (_ring)), _ring_segment_prepare((_ring)->write, \ + &(_seg), (_ring))) + +void ring_dump(const char *title, struct ring *ring); +void ring_dump_segment(const char *title, struct ring_segment *seg); + + +/* ring Buffer Internal API */ +void _ring_segment_prepare(unsigned int from, struct ring_segment *seg, struct ring *ring); +void _ring_segment_prepare_item(unsigned int from, struct ring_segment *seg, struct ring *ring); +void _ring_read_commit(struct ring_segment *seg, struct ring *ring); +void _ring_write_commit(struct ring_segment *seg, struct ring *ring); + +#endif diff --git a/package/mtk/drivers/conninfra/src/base/msg_thread.c b/package/mtk/drivers/conninfra/src/base/msg_thread.c new file mode 100644 index 0000000000..4a9c0cba4a --- /dev/null +++ b/package/mtk/drivers/conninfra/src/base/msg_thread.c @@ -0,0 +1,716 @@ +/* + * Copyright (C) 2016 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 http://www.gnu.org/licenses/gpl-2.0.html for more details. + */ +/*! \file +* \brief Declaration of library functions +* +* Any definitions in this file will be shared among GLUE Layer and internal Driver Stack. +*/ + +#define pr_fmt(fmt) KBUILD_MODNAME "@(%s:%d) " fmt, __func__, __LINE__ +#include +#include "msg_thread.h" + +/******************************************************************************* +* C O M P I L E R F L A G S +******************************************************************************** +*/ + +/******************************************************************************* +* M A C R O S +******************************************************************************** +*/ + +/******************************************************************************* +* E X T E R N A L R E F E R E N C E S +******************************************************************************** +*/ + +/******************************************************************************* +* C O N S T A N T S +******************************************************************************** +*/ + +/******************************************************************************* +* D A T A T Y P E S +******************************************************************************** +*/ + +/******************************************************************************* +* F U N C T I O N D E C L A R A T I O N S +******************************************************************************** +*/ +static void msg_opq_dump(const char *qName, struct msg_op_q *op_q); +static void _msg_opq_dump(const char *qName, struct msg_op_q *op_q); + +/******************************************************************************* +* P U B L I C D A T A +******************************************************************************** +*/ + +/******************************************************************************* +* P R I V A T E D A T A +******************************************************************************** +*/ + +/******************************************************************************* +* F U N C T I O N S +******************************************************************************** +*/ + +#define MSG_OP_SIZE(prb) ((prb)->size) +#define MSG_OP_MASK(prb) (MSG_OP_SIZE(prb) - 1) +#define MSG_OP_COUNT(prb) ((prb)->write - (prb)->read) +#define MSG_OP_FULL(prb) (MSG_OP_COUNT(prb) >= MSG_OP_SIZE(prb)) +#define MSG_OP_EMPTY(prb) ((prb)->write == (prb)->read) + +#define MSG_OP_INIT(prb, qsize) \ +do { \ + (prb)->read = (prb)->write = 0; \ + (prb)->size = (qsize); \ +} while (0) + +#define MSG_OP_PUT(prb, value) \ +do { \ + if (!MSG_OP_FULL(prb)) { \ + (prb)->queue[(prb)->write & MSG_OP_MASK(prb)] = value; \ + ++((prb)->write); \ + } \ + else { \ + pr_warn("Message queue is full.\n"); \ + } \ +} while (0) + +#define MSG_OP_GET(prb, value) \ +do { \ + if (!MSG_OP_EMPTY(prb)) { \ + value = (prb)->queue[(prb)->read & MSG_OP_MASK(prb)]; \ + ++((prb)->read); \ + if (MSG_OP_EMPTY(prb)) { \ + (prb)->read = (prb)->write = 0; \ + } \ + } \ + else { \ + value = NULL; \ + pr_warn("Message queue is empty.\n"); \ + } \ +} while (0) + + + +#if defined(CONFIG_MTK_ENG_BUILD) || defined(CONFIG_MT_ENG_BUILD) +static bool msg_evt_opq_has_op(struct msg_op_q *op_q, struct msg_op *op) +{ + unsigned int rd; + unsigned int wt; + struct msg_op *tmp_op; + + rd = op_q->read; + wt = op_q->write; + + while (rd != wt) { + tmp_op = op_q->queue[rd & MSG_OP_MASK(op_q)]; + if (op == tmp_op) + return true; + rd++; + } + return false; +} +#endif + +/* + * Utility functions + */ +static int msg_evt_put_op_to_q(struct msg_op_q *op_q, struct msg_op *op) +{ + int ret; + + if (!op_q || !op) { + pr_err("invalid input param: pOpQ(0x%p), pLxOp(0x%p)\n", op_q, op); + return -1; + } + + ret = osal_lock_sleepable_lock(&op_q->lock); + if (ret) { + pr_warn("osal_lock_sleepable_lock iRet(%d)\n", ret); + return -2; + } + +#if defined(CONFIG_MTK_ENG_BUILD) || defined(CONFIG_MT_ENG_BUILD) + if (msg_evt_opq_has_op(op_q, op)) { + pr_err("Op(%p) already exists in queue(%p)\n", op, op_q); + ret = -3; + } +#endif + + /* acquire lock success */ + if (!MSG_OP_FULL(op_q)) + MSG_OP_PUT(op_q, op); + else { + pr_warn("MSG_OP_FULL(%p -> %p)\n", op, op_q); + ret = -4; + } + + osal_unlock_sleepable_lock(&op_q->lock); + + if (ret) { + //osal_opq_dump("FreeOpQ", &g_conninfra_ctx.rFreeOpQ); + //osal_opq_dump("ActiveOpQ", &g_conninfra_ctx.rActiveOpQ); + return ret; + } + return 0; +} + + +/* + * Utility functions + */ +static struct msg_op *msg_evt_get_op_from_q(struct msg_op_q *op_q) +{ + struct msg_op *op; + int ret; + + if (op_q == NULL) { + pr_err("pOpQ = NULL\n"); + return NULL; + } + + ret = osal_lock_sleepable_lock(&op_q->lock); + if (ret) { + pr_err("osal_lock_sleepable_lock iRet(%d)\n", ret); + return NULL; + } + + /* acquire lock success */ + MSG_OP_GET(op_q, op); + osal_unlock_sleepable_lock(&op_q->lock); + + if (op == NULL) { + pr_warn("MSG_OP_GET(%p) return NULL\n", op_q); + //osal_opq_dump("FreeOpQ", &g_conninfra_ctx.rFreeOpQ); + //osal_opq_dump("ActiveOpQ", &g_conninfra_ctx.rActiveOpQ); + } + + return op; +} + +static void _msg_opq_dump(const char *qName, struct msg_op_q *op_q) +{ + /* Line format: + * [LogicalIdx(PhysicalIdx)]Address:OpId(Ref)(Result)-Info-OpData0,OpData1,OpData2,OpData3,OpData5_ + * [LogicalIdx] max 10+2=12 chars (decimal) + * (PhysicalIdx) max 10+2=12 chars (decimal) + * Address: max 16+1=17 chars (hex) + * OpId max 10 chars (decimal) + * (Ref) max 2+2=4 chars (should only be 1 digit, reserve 2 in case of negative number) + * (Result) max 11+2=13 chars (signed decimal) + * -Info- max 8+2=10 chars (hex) + * OpData, max 16+1=17 chars (hex) + */ +#define OPQ_DUMP_OP_PER_LINE 1 +#define OPQ_DUMP_OPDATA_PER_OP 6 +#define OPQ_DUMP_OP_BUF_SIZE (12 + 12 + 17 + 10 + 4 + 13 + 10 + (17 * (OPQ_DUMP_OPDATA_PER_OP)) + 1) +#define OPQ_DUMP_LINE_BUF_SIZE ((OPQ_DUMP_OP_BUF_SIZE * OPQ_DUMP_OP_PER_LINE) + 1) + unsigned int rd; + unsigned int wt; + unsigned int idx = 0; + unsigned int op_data_idx; + unsigned int buf_idx; + int printed; + struct msg_op *op; + char buf[OPQ_DUMP_LINE_BUF_SIZE]; + + rd = op_q->read; + wt = op_q->write; + + pr_info("%s(%p), sz:%u/%u, rd:%u, wt:%u\n", + qName, op_q, RB_COUNT(op_q), RB_SIZE(op_q), rd, wt); + while (rd != wt && idx < RB_SIZE(op_q)) { + buf_idx = idx % OPQ_DUMP_OP_PER_LINE; + op = op_q->queue[rd & RB_MASK(op_q)]; + + if (buf_idx == 0) { + printed = 0; + buf[0] = 0; + } + + if (op) { + printed += snprintf(buf + printed, OPQ_DUMP_LINE_BUF_SIZE - printed, + "[%u(%u)]%p:%u(%d)(%d)-%u-", + idx, + (rd & RB_MASK(op_q)), + op, + op->op.op_id, + atomic_read(&op->ref_count), + op->result, + op->op.info_bit); + for (op_data_idx = 0; op_data_idx < OPQ_DUMP_OPDATA_PER_OP; op_data_idx++) + printed += snprintf(buf + printed, OPQ_DUMP_LINE_BUF_SIZE - printed, + "%zx,", op->op.op_data[op_data_idx]); + buf[printed-1] = ' '; + } else { + printed += snprintf(buf + printed, OPQ_DUMP_LINE_BUF_SIZE - printed, + "[%u(%u)]%p ", idx, (rd & RB_MASK(op_q)), op); + } + buf[printed++] = ' '; + + if (buf_idx == OPQ_DUMP_OP_PER_LINE - 1 || rd == wt - 1) { + buf[printed - 1] = 0; + pr_info("%s\n", buf); + } + rd++; + idx++; + } +} + +void msg_opq_dump(const char *qName, struct msg_op_q *op_q) +{ + int err; + + err = osal_lock_sleepable_lock(&op_q->lock); + if (err) { + pr_info("Failed to lock queue (%d)\n", err); + return; + } + + _msg_opq_dump(qName, op_q); + + osal_unlock_sleepable_lock(&op_q->lock); +} + +/* + * msg_evt_thread API + */ + +int msg_evt_put_op_to_free_queue(struct msg_thread_ctx *ctx, struct msg_op *op) +{ + if (msg_evt_put_op_to_q(&ctx->free_op_q, op)) + return -1; + return 0; +} + + +struct msg_op *msg_evt_get_free_op(struct msg_thread_ctx *ctx) +{ + struct msg_op *op = NULL; + + if (ctx == NULL) { + pr_warn("ctx is null.\n"); + return op; + } + op = msg_evt_get_op_from_q(&ctx->free_op_q); + if (op) + osal_memset(op, 0, osal_sizeof(struct msg_op)); + return op; +} + +int msg_evt_put_op_to_active(struct msg_thread_ctx *ctx, struct msg_op *op) +{ + P_OSAL_SIGNAL signal = NULL; + int wait_ret = -1; + int ret = 0; + + do { + if (!ctx || !op) { + pr_err("msg_thread_ctx(0x%p), op(0x%p)\n", ctx, op); + break; + } + + signal = &op->signal; + if (signal->timeoutValue) { + op->result = -9; + osal_signal_init(signal); + atomic_set(&op->ref_count, 1); + } else + atomic_set(&op->ref_count, 0); + + /* Increment ref_count by 1 as wmtd thread will hold a reference also, + * this must be done here instead of on target thread, because + * target thread might not be scheduled until a much later time, + * allowing current thread to decrement ref_count at the end of function, + * putting op back to free queue before target thread has a chance to process. + */ + atomic_inc(&op->ref_count); + + /* put to active Q */ + ret = msg_evt_put_op_to_q(&ctx->active_op_q, op); + if (ret) { + pr_warn("put to active queue fail\n"); + atomic_dec(&op->ref_count); + break; + } + + /* wake up conninfra_cored */ + osal_trigger_event(&ctx->evt); + + if (signal->timeoutValue == 0) { + //ret = -1; + /* Not set timeout, don't wait */ + /* pr_info("[%s] timeout is zero", __func__);*/ + break; + } + + /* check result */ + wait_ret = osal_wait_for_signal_timeout(signal, &ctx->thread); + /*pr_info("osal_wait_for_signal_timeout:%d result=[%d]\n", + wait_ret, op->result);*/ + + if (wait_ret == 0) + pr_warn("opId(%d) completion timeout\n", op->op.op_id); + else if (op->result) + pr_info("opId(%d) result:%d\n", + op->op.op_id, op->result); + + /* op completes, check result */ + ret = op->result; + } while (0); + + if (op != NULL && signal != NULL && signal->timeoutValue && + atomic_dec_and_test(&op->ref_count)) { + /* put Op back to freeQ */ + msg_evt_put_op_to_free_queue(ctx, op); + } + + return ret; +} + + +int msg_thread_send(struct msg_thread_ctx *ctx, int opid) +{ + return msg_thread_send_2(ctx, opid, 0, 0); +} + +int msg_thread_send_1(struct msg_thread_ctx *ctx, int opid, + size_t param1) +{ + return msg_thread_send_2(ctx, opid, param1, 0); +} + +int msg_thread_send_2(struct msg_thread_ctx *ctx, int opid, + size_t param1, size_t param2) +{ + struct msg_op *op = NULL; + P_OSAL_SIGNAL signal; + int ret; + + op = msg_evt_get_free_op(ctx); + if (!op) { + pr_err("[%s] can't get free op\n", __func__); + return -1; + } + op->op.op_id = opid; + op->op.op_data[0] = param1; + op->op.op_data[1] = param2; + + signal = &op->signal; + //signal->timeoutValue = timeout > 0 ? timeout : MSG_OP_TIMEOUT; + signal->timeoutValue = 0; + ret = msg_evt_put_op_to_active(ctx, op); + + return ret; +} + +int msg_thread_send_wait(struct msg_thread_ctx *ctx, int opid, + int timeout) +{ + return msg_thread_send_wait_3(ctx, opid, timeout, 0, 0, 0); +} + +int msg_thread_send_wait_1(struct msg_thread_ctx *ctx, + int opid, int timeout, + size_t param1) +{ + return msg_thread_send_wait_3(ctx, opid, timeout, param1, 0, 0); +} + + +int msg_thread_send_wait_2(struct msg_thread_ctx *ctx, + int opid, int timeout, + size_t param1, + size_t param2) +{ + return msg_thread_send_wait_3(ctx, opid, timeout, param1, param2, 0); +} + +int msg_thread_send_wait_3(struct msg_thread_ctx *ctx, + int opid, int timeout, + size_t param1, + size_t param2, + size_t param3) +{ + struct msg_op *op = NULL; + P_OSAL_SIGNAL signal; + int ret; + + op = msg_evt_get_free_op(ctx); + if (!op) { + pr_err("can't get free op for 0x%x\n", opid); + return -1; + } + op->op.op_id = opid; + op->op.op_data[0] = param1; + op->op.op_data[1] = param2; + op->op.op_data[2] = param3; + + signal = &op->signal; + signal->timeoutValue = timeout > 0 ? timeout : MSG_OP_TIMEOUT; + ret = msg_evt_put_op_to_active(ctx, op); + return ret; + +} + +void msg_op_history_save(struct osal_op_history *log_history, struct msg_op *op) +{ + struct osal_op_history_entry *entry = NULL; + struct ring_segment seg; + int index; + unsigned long long sec = 0; + unsigned long usec = 0; + unsigned long flags; + + if (log_history->queue == NULL) + return; + + osal_get_local_time(&sec, &usec); + + spin_lock_irqsave(&(log_history->lock), flags); + RING_OVERWRITE_FOR_EACH(1, seg, &log_history->ring_buffer) { + index = seg.ring_pt - log_history->ring_buffer.base; + entry = &log_history->queue[index]; + } + + if (entry == NULL) { + pr_info("Entry is null, size %d\n", + RING_SIZE(&log_history->ring_buffer)); + spin_unlock_irqrestore(&(log_history->lock), flags); + return; + } + + entry->opbuf_address = op; + entry->op_id = op->op.op_id; + entry->opbuf_ref_count = atomic_read(&op->ref_count); + entry->op_info_bit = op->op.info_bit; + entry->param_0 = op->op.op_data[0]; + entry->param_1 = op->op.op_data[1]; + entry->param_2 = op->op.op_data[2]; + entry->param_3 = op->op.op_data[3]; + entry->ts = sec; + entry->usec = usec; + spin_unlock_irqrestore(&(log_history->lock), flags); +} + +unsigned int msg_evt_wait_event_checker(P_OSAL_THREAD thread) +{ + struct msg_thread_ctx *ctx = NULL; + + if (thread) { + ctx = (struct msg_thread_ctx *) (thread->pThreadData); + return !MSG_OP_EMPTY(&ctx->active_op_q); + } + return 0; +} + +int msg_evt_set_current_op(struct msg_thread_ctx *ctx, struct msg_op *op) +{ + ctx->cur_op = op; + return 0; +} + +int msg_evt_opid_handler(struct msg_thread_ctx *ctx, struct msg_op_data *op) +{ + int opid, ret; + + /*sanity check */ + if (op == NULL) { + pr_warn("null op\n"); + return -1; + } + if (ctx == NULL) { + pr_warn("null evt thread ctx\n"); + return -2; + } + + opid = op->op_id; + + if (opid >= ctx->op_func_size) { + pr_err("msg_evt_thread invalid OPID(%d)\n", opid); + return -3; + } + + if (ctx->op_func[opid] == NULL) { + pr_err("null handler (%d)\n", opid); + return -4; + } + ret = (*(ctx->op_func[opid])) (op); + return ret; +} + +static int msg_evt_thread(void *pvData) +{ + struct msg_thread_ctx *ctx = (struct msg_thread_ctx *)pvData; + P_OSAL_EVENT evt = NULL; + struct msg_op *op; + int ret; + + if (ctx == NULL) { + pr_err("msg_evt_thread (NULL)\n"); + return -1; + } + + evt = &(ctx->evt); + + for (;;) { + op = NULL; + evt->timeoutValue = 0; + + osal_thread_wait_for_event(&ctx->thread, evt, msg_evt_wait_event_checker); + + if (osal_thread_should_stop(&ctx->thread)) { + pr_info("msg_evt_thread thread should stop now...\n"); + /* TODO: clean up active opQ */ + break; + } + /* get Op from activeQ */ + op = msg_evt_get_op_from_q(&ctx->active_op_q); + if (!op) { + pr_warn("get op from activeQ fail\n"); + continue; + } + + /* TODO: save op history */ + msg_op_history_save(&ctx->op_history, op); + msg_evt_set_current_op(ctx, op); + ret = msg_evt_opid_handler(ctx, &op->op); + msg_evt_set_current_op(ctx, NULL); + + if (ret) + pr_warn("opid (0x%x) failed, ret(%d)\n", + op->op.op_id, ret); + + if (atomic_dec_and_test(&op->ref_count)) { + /* msg_evt_free_op(ctx) */ + msg_evt_put_op_to_free_queue(ctx, op); + } else if (op->signal.timeoutValue) { + op->result = ret; + osal_raise_signal(&op->signal); + } + } + + pr_debug("msg evt thread exists\n"); + return 0; +} + +int msg_thread_dump(struct msg_thread_ctx *ctx) +{ + P_OSAL_THREAD p_thread; + struct msg_op *cur_op; + + if (ctx == NULL) { + pr_err("get NULL input\n"); + return 0; + } + p_thread = &ctx->thread; + pr_info("Dump msg_thread_ctx: %s\n", p_thread->threadName); + cur_op = ctx->cur_op; + if (cur_op) { + pr_info("cur_op: %x(%x)-%zx,%zx,%zx,%zx\n", + cur_op->op.op_id, cur_op->op.info_bit, + cur_op->op.op_data[0], cur_op->op.op_data[1], + cur_op->op.op_data[2], cur_op->op.op_data[3]); + } + osal_dump_thread_state(p_thread->threadName); + osal_op_history_print(&(ctx->op_history), p_thread->threadName); + msg_opq_dump("ActiveOpQ", &ctx->active_op_q); + + return 0; +} + +int msg_thread_init(struct msg_thread_ctx *ctx, + const char *thread_name, const msg_opid_func *funcs, + int op_size) +{ + int r = 0, i; + P_OSAL_THREAD p_thread; + + osal_memset(ctx, 0, sizeof(struct msg_thread_ctx)); + + ctx->op_func = funcs; + ctx->op_func_size = op_size; + + /* init thread inst */ + p_thread = &ctx->thread; + + osal_strncpy(p_thread->threadName, thread_name, + sizeof(p_thread->threadName)); + + p_thread->pThreadData = (void *) ctx; + p_thread->pThreadFunc = (void *) msg_evt_thread; + r = osal_thread_create(p_thread); + + if (r) { + pr_err("osal_thread_create(0x%p) fail(%d)\n", p_thread, r); + return -1; + } + + osal_event_init(&ctx->evt); + osal_sleepable_lock_init(&ctx->active_op_q.lock); + osal_sleepable_lock_init(&ctx->free_op_q.lock); + + /* Initialize op queue */ + MSG_OP_INIT(&ctx->free_op_q, MSG_THREAD_OP_BUF_SIZE); + MSG_OP_INIT(&ctx->active_op_q, MSG_THREAD_OP_BUF_SIZE); + + /* Put all to free Q */ + for (i = 0; i < MSG_THREAD_OP_BUF_SIZE; i++) { + osal_signal_init(&(ctx->op_q_inst[i].signal)); + msg_evt_put_op_to_free_queue(ctx, &(ctx->op_q_inst[i])); + } + + osal_op_history_init(&ctx->op_history, 16); + + r = osal_thread_run(p_thread); + if (r) { + pr_err("osal_thread_run(evt_thread 0x%p) fail(%d)\n", + p_thread, r); + return -2; + } + return r; +} + +int msg_thread_deinit(struct msg_thread_ctx *ctx) +{ + int r, i; + P_OSAL_THREAD p_thraed = &ctx->thread; + + r = osal_thread_stop(p_thraed); + if (r) { + pr_err("osal_thread_stop(0x%p) fail(%d)\n", p_thraed, r); + return -1; + } + + for (i = 0; i < MSG_THREAD_OP_BUF_SIZE; i++) + osal_signal_deinit(&(ctx->op_q_inst[i].signal)); + + osal_sleepable_lock_deinit(&ctx->free_op_q.lock); + osal_sleepable_lock_deinit(&ctx->active_op_q.lock); + + r = osal_thread_destroy(p_thraed); + if (r) { + pr_err("osal_thread_stop(0x%p) fail(%d)\n", p_thraed, r); + return -2; + } + + osal_memset(ctx, 0, sizeof(struct msg_thread_ctx)); + + pr_debug("[%s] DONE\n", __func__); + return 0; +} diff --git a/package/mtk/drivers/conninfra/src/base/osal.c b/package/mtk/drivers/conninfra/src/base/osal.c new file mode 100644 index 0000000000..3cdacfd601 --- /dev/null +++ b/package/mtk/drivers/conninfra/src/base/osal.c @@ -0,0 +1,1595 @@ +/* + * Copyright (C) 2016 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 http://www.gnu.org/licenses/gpl-2.0.html for more details. + */ + +/*! \file + * \brief Declaration of library functions + * Any definitions in this file will be shared among GLUE Layer and internal Driver Stack. + */ + +/******************************************************************************* +* C O M P I L E R F L A G S +******************************************************************************** +*/ +#define pr_fmt(fmt) KBUILD_MODNAME "@(%s:%d) " fmt, __func__, __LINE__ +/******************************************************************************* +* M A C R O S +******************************************************************************** +*/ + +/******************************************************************************* +* E X T E R N A L R E F E R E N C E S +******************************************************************************** +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_MTD +#include +#include +#include +#include +#endif /* CONFIG_MTD */ +#include "osal.h" + +/******************************************************************************* +* C O N S T A N T S +******************************************************************************** +*/ + +#define TO_STR(_x) #_x +#define BIN_TO_STR(_x) TO_STR(_x) +/******************************************************************************* +* D A T A T Y P E S +******************************************************************************** +*/ + +/******************************************************************************* +* P U B L I C D A T A +******************************************************************************** +*/ + +/******************************************************************************* +* P R I V A T E D A T A +******************************************************************************** +*/ + +int ftrace_flag = 1; +/******************************************************************************* +* F U N C T I O N D E C L A R A T I O N S +******************************************************************************** +*/ + +/******************************************************************************* +* F U N C T I O N S +******************************************************************************** +*/ + + +int get_default_bin_image_file(char *path) +{ + if(osal_strlen(BIN_TO_STR(EEPROM_NAME))) + osal_sprintf(path, "/lib/firmware/%s", BIN_TO_STR(EEPROM_NAME)); + else + osal_sprintf(path, "/lib/firmware/e2p"); + + pr_info("Use default BIN from:%s.\n", path); + + return 0; +} + +void FlashRead(char *name, unsigned char *value, unsigned long offset, unsigned long size) +{ +#ifdef CONFIG_MTD + int ret; + size_t rdlen; + struct mtd_info *mtd; + + mtd = get_mtd_device_nm(name); + if (IS_ERR(mtd)) { + pr_err("Can't get mtd device!\n"); + return; + } else { + ret = mtd_read(mtd, offset, size, &rdlen, value); + if (rdlen != size) { + pr_err("mtd_read: rdlen is not equal to size!\n"); + } + put_mtd_device(mtd); + } +#endif /* CONFIG_WIFI_MTD */ +} + +void FlashWrite(char *name, unsigned char *p, unsigned long a, unsigned long b) +{ + return; +} + +/*string operations*/ +unsigned int osal_strlen(const char *str) +{ + return strlen(str); +} + +int osal_strcmp(const char *dst, const char *src) +{ + return strcmp(dst, src); +} + +int osal_strncmp(const char *dst, const char *src, unsigned int len) +{ + return strncmp(dst, src, len); +} + +char *osal_strcpy(char *dst, const char *src) +{ + return strncpy(dst, src, strlen(src)+1); +} + +char *osal_strncpy(char *dst, const char *src, unsigned int len) +{ + return strncpy(dst, src, len); +} + +char *osal_strcat(char *dst, const char *src) +{ + return strncat(dst, src, strlen(src)); +} + +char *osal_strncat(char *dst, const char *src, unsigned int len) +{ + return strncat(dst, src, len); +} + +char *osal_strchr(const char *str, unsigned char c) +{ + return strchr(str, c); +} + +char *osal_strsep(char **str, const char *c) +{ + return strsep(str, c); +} + +int osal_strtol(const char *str, unsigned int adecimal, long *res) +{ + if (sizeof(long) == 4) + return kstrtou32(str, adecimal, (unsigned int *) res); + else + return kstrtol(str, adecimal, res); +} + +char *osal_strstr(char *str1, const char *str2) +{ + return strstr(str1, str2); +} + +char *osal_strnstr(char *str1, const char *str2, int n) +{ + return strnstr(str1, str2, n); +} + +void osal_bug_on(unsigned int val) +{ + WARN_ON(val); +} + +int osal_snprintf(char *buf, unsigned int len, const char *fmt, ...) +{ + int iRet = 0; + va_list args; + + /*va_start(args, fmt); */ + va_start(args, fmt); + /*iRet = snprintf(buf, len, fmt, args); */ + iRet = vsnprintf(buf, len, fmt, args); + va_end(args); + + return iRet; +} + +int osal_sprintf(char *str, const char *format, ...) +{ + int iRet = 0; + va_list args; + + va_start(args, format); + iRet = vsnprintf(str, DBG_LOG_STR_SIZE, format, args); + va_end(args); + + return iRet; +} + +void *osal_malloc(unsigned int size) +{ + return vmalloc(size); +} + +void osal_free(const void *dst) +{ + vfree(dst); +} + +void *osal_memset(void *buf, int i, unsigned int len) +{ + return memset(buf, i, len); +} + +void *osal_memcpy(void *dst, const void *src, unsigned int len) +{ + return memcpy(dst, src, len); +} + +void osal_memcpy_fromio(void *dst, const void *src, unsigned int len) +{ + return memcpy_fromio(dst, src, len); +} + +void osal_memcpy_toio(void *dst, const void *src, unsigned int len) +{ + return memcpy_toio(dst, src, len); +} + +int osal_memcmp(const void *buf1, const void *buf2, unsigned int len) +{ + return memcmp(buf1, buf2, len); +} + +void osal_dump_thread_state(const unsigned char *name) +{ + //TODO + return; +} + +static inline bool __osal_is_valid_thread(P_OSAL_THREAD pThread) +{ + if ((pThread) && !IS_ERR_OR_NULL(pThread->pThread)) + return true; + else + return false; +} + +/* + * OSAL layer Thread Opeartion related APIs + * + */ +int osal_thread_create(P_OSAL_THREAD pThread) +{ + struct task_struct *task; + + if (!pThread) + return -1; + + task = kthread_create(pThread->pThreadFunc, + pThread->pThreadData, pThread->threadName); + if (IS_ERR(task)) { + pr_err("[%s] create %s thread fail\n", __func__, pThread->threadName); + return -1; + } + + pThread->pThread = task; + return 0; +} + +int osal_thread_run(P_OSAL_THREAD pThread) +{ + if (__osal_is_valid_thread(pThread)) { + wake_up_process(pThread->pThread); + return 0; + } else { + return -1; + } +} + +int osal_thread_stop(P_OSAL_THREAD pThread) +{ + int iRet; + + if (__osal_is_valid_thread(pThread)) { + iRet = kthread_stop(pThread->pThread); + pThread->pThread = NULL; + return iRet; + } + return -1; +} + +int osal_thread_should_stop(P_OSAL_THREAD pThread) +{ + if (__osal_is_valid_thread(pThread)) + return kthread_should_stop(); + else + return 1; + +} + +int osal_thread_wait_for_event(P_OSAL_THREAD pThread, + P_OSAL_EVENT pEvent, P_OSAL_EVENT_CHECKER pChecker) +{ + /* P_DEV_WMT pDevWmt;*/ + + if (__osal_is_valid_thread(pThread) && (pEvent) && (pChecker)) { + return wait_event_interruptible(pEvent->waitQueue, ( + osal_thread_should_stop(pThread) + || (*pChecker) (pThread))); + } + return -1; +} + +int osal_thread_destroy(P_OSAL_THREAD pThread) +{ + if (__osal_is_valid_thread(pThread)) { + kthread_stop(pThread->pThread); + pThread->pThread = NULL; + } + return 0; +} + +/* + * osal_thread_sched_retrieve + * Retrieve thread's current scheduling statistics and stored in output "sched". + * Return value: + * 0 : Schedstats successfully retrieved + * -1 : Kernel's schedstats feature not enabled + * -2 : pThread not yet initialized or sched is a NULL pointer + */ +static int osal_thread_sched_retrieve(P_OSAL_THREAD pThread, + P_OSAL_THREAD_SCHEDSTATS sched) +{ +#ifdef CONFIG_SCHEDSTATS + struct sched_entity se; + unsigned long long sec; + unsigned long usec; + + if (!sched) + return -2; + + /* always clear sched to simplify error handling at caller side */ + memset(sched, 0, sizeof(OSAL_THREAD_SCHEDSTATS)); + + if (!__osal_is_valid_thread(pThread)) + return -2; + + memcpy(&se, &pThread->pThread->se, sizeof(struct sched_entity)); + osal_get_local_time(&sec, &usec); + + sched->time = sec*1000 + usec/1000; + sched->exec = se.sum_exec_runtime; + sched->runnable = se.statistics.wait_sum; + sched->iowait = se.statistics.iowait_sum; + + return 0; +#else + /* always clear sched to simplify error handling at caller side */ + if (sched) + memset(sched, 0, sizeof(OSAL_THREAD_SCHEDSTATS)); + return -1; +#endif +} + +/* + * osal_thread_sched_mark + * Record the thread's current schedstats and stored + * in output "schedstats" parameter for profiling at + * later time. + * Return value: + * 0 : Schedstats successfully recorded + * -1 : Kernel's schedstats feature not enabled + * -2 : pThread not yet initialized or invalid parameters + */ +int osal_thread_sched_mark(P_OSAL_THREAD pThread, + P_OSAL_THREAD_SCHEDSTATS schedstats) +{ + return osal_thread_sched_retrieve(pThread, schedstats); +} + +/* + * osal_thread_sched_unmark + * Calculate scheduling statistics against the previously marked point. + * The result will be filled back into the schedstats output parameter. + * Return value: + * 0 : Schedstats successfully calculated + * -1 : Kernel's schedstats feature not enabled + * -2 : pThread not yet initialized or invalid parameters + */ +int osal_thread_sched_unmark(P_OSAL_THREAD pThread, + P_OSAL_THREAD_SCHEDSTATS schedstats) +{ + int ret; + OSAL_THREAD_SCHEDSTATS sched_now; + + if (unlikely(!schedstats)) { + ret = -2; + } else { + ret = osal_thread_sched_retrieve(pThread, &sched_now); + if (ret == 0) { + schedstats->time = sched_now.time - schedstats->time; + schedstats->exec = sched_now.exec - schedstats->exec; + schedstats->runnable = + sched_now.runnable - schedstats->runnable; + schedstats->iowait = + sched_now.iowait - schedstats->iowait; + } + } + return ret; +} + +/* + * OSAL layer Signal Opeartion related APIs + * initialization + * wait for signal + * wait for signal timerout + * raise signal + * destroy a signal + * + */ + +int osal_signal_init(P_OSAL_SIGNAL pSignal) +{ + if (pSignal) { + init_completion(&pSignal->comp); + return 0; + } else { + return -1; + } +} + +int osal_wait_for_signal(P_OSAL_SIGNAL pSignal) +{ + if (pSignal) { + wait_for_completion_interruptible(&pSignal->comp); + return 0; + } else { + return -1; + } +} + +/* + * osal_wait_for_signal_timeout + * + * Wait for a signal to be triggered by the corresponding thread, within the + * expected timeout specified by the signal's timeoutValue. + * When the pThread parameter is specified, the thread's scheduling ability is + * considered, the timeout will be extended when thread cannot acquire CPU + * resource, and will only extend for a number of times specified by the + * signal's timeoutExtension should the situation continues. + * + * Return value: + * 0 : timeout + * >0 : signal triggered + */ +int osal_wait_for_signal_timeout(P_OSAL_SIGNAL pSignal, P_OSAL_THREAD pThread) +{ + OSAL_THREAD_SCHEDSTATS schedstats; + int waitRet; + + /* [ChangeFeature][George] gps driver may be closed by -ERESTARTSYS. + * Avoid using *interruptible" version in order to complete our jobs, + * such as function off gracefully. + */ + if (!pThread || !pThread->pThread) + return wait_for_completion_timeout(&pSignal->comp, + msecs_to_jiffies(pSignal->timeoutValue)); + + do { + osal_thread_sched_mark(pThread, &schedstats); + waitRet = wait_for_completion_timeout(&pSignal->comp, + msecs_to_jiffies(pSignal->timeoutValue)); + osal_thread_sched_unmark(pThread, &schedstats); + + if (waitRet > 0) + break; + + if (schedstats.runnable > schedstats.exec) { + pr_err( + "[E]%s:wait completion timeout, %s cannot get CPU, extension(%d), show backtrace:\n", + __func__, + pThread->threadName, + pSignal->timeoutExtension); + } else { + pr_err( + "[E]%s:wait completion timeout, show %s backtrace:\n", + __func__, + pThread->threadName); + pSignal->timeoutExtension = 0; + } + pr_err( + "[E]%s:\tduration:%llums, sched(x%llu/r%llu/i%llu)\n", + __func__, + schedstats.time, + schedstats.exec, + schedstats.runnable, + schedstats.iowait); + /* + * no need to disginguish combo or A/D die projects + * osal_dump_thread_state will just return if target + * thread does not exist + */ + osal_dump_thread_state("mtk_wmtd"); + osal_dump_thread_state("mtk_wmtd_worker"); + osal_dump_thread_state("btif_rxd"); + osal_dump_thread_state("mtk_stp_psm"); + osal_dump_thread_state("mtk_stp_btm"); + osal_dump_thread_state("stp_sdio_tx_rx"); + } while (pSignal->timeoutExtension--); + return waitRet; +} + +int osal_raise_signal(P_OSAL_SIGNAL pSignal) +{ + if (pSignal) { + complete(&pSignal->comp); + return 0; + } else + return -1; +} + +int osal_signal_active_state(P_OSAL_SIGNAL pSignal) +{ + if (pSignal) + return pSignal->timeoutValue; + else + return -1; +} + +int osal_signal_deinit(P_OSAL_SIGNAL pSignal) +{ + if (pSignal) { + pSignal->timeoutValue = 0; + return 0; + } else + return -1; +} + +/* + * OSAL layer Event Opeartion related APIs + * initialization + * wait for signal + * wait for signal timerout + * raise signal + * destroy a signal + * + */ + +int osal_event_init(P_OSAL_EVENT pEvent) +{ + if (pEvent) { + init_waitqueue_head(&pEvent->waitQueue); + return 0; + } + return -1; +} + +int osal_trigger_event(P_OSAL_EVENT pEvent) +{ + int ret = 0; + + if (pEvent) { + wake_up_interruptible(&pEvent->waitQueue); + return ret; + } + return -1; +} + +int osal_wait_for_event(P_OSAL_EVENT pEvent, + int (*condition)(void *), void *cond_pa) +{ + if (pEvent) + return wait_event_interruptible(pEvent->waitQueue, + condition(cond_pa)); + else + return -1; +} + +int osal_wait_for_event_timeout(P_OSAL_EVENT pEvent, + int (*condition)(void *), void *cond_pa) +{ + if (pEvent) + return wait_event_interruptible_timeout(pEvent->waitQueue, + condition(cond_pa), + msecs_to_jiffies(pEvent->timeoutValue)); + return -1; +} + + +int osal_event_deinit(P_OSAL_EVENT pEvent) +{ + return 0; +} + +long osal_wait_for_event_bit_set(P_OSAL_EVENT pEvent, + unsigned long *pState, unsigned int bitOffset) +{ + unsigned int ms = 0; + + if (pEvent) { + ms = pEvent->timeoutValue; + if (ms != 0) + return wait_event_interruptible_timeout( + pEvent->waitQueue, + test_bit(bitOffset, pState), + msecs_to_jiffies(ms)); + else + return wait_event_interruptible(pEvent->waitQueue, + test_bit(bitOffset, pState)); + } else + return -1; + +} + +long osal_wait_for_event_bit_clr(P_OSAL_EVENT pEvent, + unsigned long *pState, unsigned int bitOffset) +{ + unsigned int ms = 0; + + if (pEvent) { + ms = pEvent->timeoutValue; + if (ms != 0) + return wait_event_interruptible_timeout( + pEvent->waitQueue, + !test_bit(bitOffset, pState), + msecs_to_jiffies(ms)); + else + return wait_event_interruptible(pEvent->waitQueue, + !test_bit(bitOffset, pState)); + } else + return -1; +} + +/* + * bit test and set/clear operations APIs + */ +#if OS_BIT_OPS_SUPPORT +#define osal_bit_op_lock(x) +#define osal_bit_op_unlock(x) +#else + +int osal_bit_op_lock(P_OSAL_UNSLEEPABLE_LOCK pLock) +{ + + return 0; +} + +int osal_bit_op_unlock(P_OSAL_UNSLEEPABLE_LOCK pLock) +{ + + return 0; +} +#endif +int osal_clear_bit(unsigned int bitOffset, P_OSAL_BIT_OP_VAR pData) +{ + osal_bit_op_lock(&(pData->opLock)); + clear_bit(bitOffset, &pData->data); + osal_bit_op_unlock(&(pData->opLock)); + return 0; +} + +int osal_set_bit(unsigned int bitOffset, P_OSAL_BIT_OP_VAR pData) +{ + osal_bit_op_lock(&(pData->opLock)); + set_bit(bitOffset, &pData->data); + osal_bit_op_unlock(&(pData->opLock)); + return 0; +} + +int osal_test_bit(unsigned int bitOffset, P_OSAL_BIT_OP_VAR pData) +{ + unsigned int iRet = 0; + + osal_bit_op_lock(&(pData->opLock)); + iRet = test_bit(bitOffset, &pData->data); + osal_bit_op_unlock(&(pData->opLock)); + return iRet; +} + +int osal_test_and_clear_bit(unsigned int bitOffset, P_OSAL_BIT_OP_VAR pData) +{ + unsigned int iRet = 0; + + osal_bit_op_lock(&(pData->opLock)); + iRet = test_and_clear_bit(bitOffset, &pData->data); + osal_bit_op_unlock(&(pData->opLock)); + return iRet; + +} + +int osal_test_and_set_bit(unsigned int bitOffset, P_OSAL_BIT_OP_VAR pData) +{ + unsigned int iRet = 0; + + osal_bit_op_lock(&(pData->opLock)); + iRet = test_and_set_bit(bitOffset, &pData->data); + osal_bit_op_unlock(&(pData->opLock)); + return iRet; +} + +int _osal_fifo_init(OSAL_FIFO *pFifo, unsigned char *buf, unsigned int size) +{ + struct kfifo *fifo = NULL; + int ret = -1; + + if (!pFifo) { + pr_err("pFifo must be !NULL\n"); + return -1; + } + if (pFifo->pFifoBody) { + pr_err("pFifo->pFifoBody must be NULL\n"); + pr_err("pFifo(0x%p), pFifo->pFifoBody(0x%p)\n", + pFifo, pFifo->pFifoBody); + return -1; + } + fifo = kzalloc(sizeof(struct kfifo), GFP_ATOMIC); + if (!buf) { + /*fifo's buffer is not ready, we allocate automatically */ + ret = kfifo_alloc(fifo, size, /*GFP_KERNEL */ GFP_ATOMIC); + } else { + if (is_power_of_2(size)) { + kfifo_init(fifo, buf, size); + ret = 0; + } else { + kfifo_free(fifo); + fifo = NULL; + ret = -1; + } + } + + pFifo->pFifoBody = fifo; + return (ret < 0) ? (-1) : (0); +} + +int _osal_fifo_deinit(OSAL_FIFO *pFifo) +{ + struct kfifo *fifo = NULL; + + if (!pFifo || !pFifo->pFifoBody) { + pr_err("%s:pFifo = NULL or pFifo->pFifoBody = NULL, error\n", + __func__); + return -1; + } + + fifo = (struct kfifo *)pFifo->pFifoBody; + + if (fifo) + kfifo_free(fifo); + + return 0; +} + +int _osal_fifo_size(OSAL_FIFO *pFifo) +{ + struct kfifo *fifo = NULL; + int ret = 0; + + if (!pFifo || !pFifo->pFifoBody) { + pr_err("%s:pFifo = NULL or pFifo->pFifoBody = NULL, error\n", + __func__); + return -1; + } + + fifo = (struct kfifo *)pFifo->pFifoBody; + + if (fifo) + ret = kfifo_size(fifo); + + return ret; +} + +/*returns unused bytes in fifo*/ +int _osal_fifo_avail_size(OSAL_FIFO *pFifo) +{ + struct kfifo *fifo = NULL; + int ret = 0; + + if (!pFifo || !pFifo->pFifoBody) { + pr_err("%s:pFifo = NULL or pFifo->pFifoBody = NULL, error\n", + __func__); + return -1; + } + + fifo = (struct kfifo *)pFifo->pFifoBody; + + if (fifo) + ret = kfifo_avail(fifo); + + return ret; +} + +/*returns used bytes in fifo*/ +int _osal_fifo_len(OSAL_FIFO *pFifo) +{ + struct kfifo *fifo = NULL; + int ret = 0; + + if (!pFifo || !pFifo->pFifoBody) { + pr_err("%s:pFifo = NULL or pFifo->pFifoBody = NULL, error\n", + __func__); + return -1; + } + + fifo = (struct kfifo *)pFifo->pFifoBody; + + if (fifo) + ret = kfifo_len(fifo); + + return ret; +} + +int _osal_fifo_is_empty(OSAL_FIFO *pFifo) +{ + struct kfifo *fifo = NULL; + int ret = 0; + + if (!pFifo || !pFifo->pFifoBody) { + pr_err("%s:pFifo = NULL or pFifo->pFifoBody = NULL, error\n", + __func__); + return -1; + } + + fifo = (struct kfifo *)pFifo->pFifoBody; + + if (fifo) + ret = kfifo_is_empty(fifo); + + return ret; +} + +int _osal_fifo_is_full(OSAL_FIFO *pFifo) +{ + struct kfifo *fifo = NULL; + int ret = 0; + + if (!pFifo || !pFifo->pFifoBody) { + pr_err("%s:pFifo = NULL or pFifo->pFifoBody = NULL, error\n", + __func__); + return -1; + } + + fifo = (struct kfifo *)pFifo->pFifoBody; + + if (fifo) + ret = kfifo_is_full(fifo); + + return ret; +} + +int _osal_fifo_data_in(OSAL_FIFO *pFifo, const void *buf, unsigned int len) +{ + struct kfifo *fifo = NULL; + int ret = 0; + + if (!pFifo || !pFifo->pFifoBody) { + pr_err("%s:pFifo = NULL or pFifo->pFifoBody = NULL, error\n", + __func__); + return -1; + } + + fifo = (struct kfifo *)pFifo->pFifoBody; + + if (fifo && buf && (len <= _osal_fifo_avail_size(pFifo))) { + ret = kfifo_in(fifo, buf, len); + } else { + pr_err("%s: kfifo_in, error, len = %d, _osal_fifo_avail_size = %d, buf=%p\n", + __func__, len, _osal_fifo_avail_size(pFifo), buf); + + ret = 0; + } + + return ret; +} + +int _osal_fifo_data_out(OSAL_FIFO *pFifo, void *buf, unsigned int len) +{ + struct kfifo *fifo = NULL; + int ret = 0; + + if (!pFifo || !pFifo->pFifoBody) { + pr_err("%s:pFifo = NULL or pFifo->pFifoBody = NULL, error\n" + , __func__); + return -1; + } + + fifo = (struct kfifo *)pFifo->pFifoBody; + + if (fifo && buf && (len <= _osal_fifo_len(pFifo))) { + ret = kfifo_out(fifo, buf, len); + } else { + pr_err("%s: kfifo_out, error, len = %d, osal_fifo_len = %d, buf=%p\n", + __func__, len, _osal_fifo_len(pFifo), buf); + + ret = 0; + } + + return ret; +} + +int _osal_fifo_reset(OSAL_FIFO *pFifo) +{ + struct kfifo *fifo = NULL; + + if (!pFifo || !pFifo->pFifoBody) { + pr_err("%s:pFifo = NULL or pFifo->pFifoBody = NULL, error\n", + __func__); + return -1; + } + + fifo = (struct kfifo *)pFifo->pFifoBody; + + if (fifo) + kfifo_reset(fifo); + + return 0; +} + +int osal_fifo_init(P_OSAL_FIFO pFifo, unsigned char *buffer, unsigned int size) +{ + if (!pFifo) { + pr_err("%s:pFifo = NULL, error\n", __func__); + return -1; + } + + pFifo->FifoInit = _osal_fifo_init; + pFifo->FifoDeInit = _osal_fifo_deinit; + pFifo->FifoSz = _osal_fifo_size; + pFifo->FifoAvailSz = _osal_fifo_avail_size; + pFifo->FifoLen = _osal_fifo_len; + pFifo->FifoIsEmpty = _osal_fifo_is_empty; + pFifo->FifoIsFull = _osal_fifo_is_full; + pFifo->FifoDataIn = _osal_fifo_data_in; + pFifo->FifoDataOut = _osal_fifo_data_out; + pFifo->FifoReset = _osal_fifo_reset; + + if (pFifo->pFifoBody != NULL) { + pr_err("%s:Because pFifo room is avialable, we clear the room and allocate them again.\n", __func__); + pFifo->FifoDeInit(pFifo->pFifoBody); + pFifo->pFifoBody = NULL; + } + + pFifo->FifoInit(pFifo, buffer, size); + + return 0; +} + +void osal_fifo_deinit(P_OSAL_FIFO pFifo) +{ + if (pFifo) + pFifo->FifoDeInit(pFifo); + else { + pr_err("%s:pFifo = NULL, error\n", __func__); + return; + } + kfree(pFifo->pFifoBody); +} + +int osal_fifo_reset(P_OSAL_FIFO pFifo) +{ + int ret = -1; + + if (pFifo) { + ret = pFifo->FifoReset(pFifo); + } else { + pr_err("%s:pFifo = NULL, error\n", __func__); + ret = -1; + } + return ret; +} + +unsigned int osal_fifo_in(P_OSAL_FIFO pFifo, + unsigned char *buffer, unsigned int size) +{ + unsigned int ret = 0; + + if (pFifo) { + ret = pFifo->FifoDataIn(pFifo, buffer, size); + } else { + pr_err("%s:pFifo = NULL, error\n", __func__); + ret = 0; + } + + return ret; +} + +unsigned int osal_fifo_out(P_OSAL_FIFO pFifo, + unsigned char *buffer, unsigned int size) +{ + unsigned int ret = 0; + + if (pFifo) { + ret = pFifo->FifoDataOut(pFifo, buffer, size); + } else { + pr_err("%s:pFifo = NULL, error\n", __func__); + ret = 0; + } + + return ret; +} + +unsigned int osal_fifo_len(P_OSAL_FIFO pFifo) +{ + unsigned int ret = 0; + + if (pFifo) { + ret = pFifo->FifoLen(pFifo); + } else { + pr_err("%s:pFifo = NULL, error\n", __func__); + ret = 0; + } + + return ret; +} + +unsigned int osal_fifo_sz(P_OSAL_FIFO pFifo) +{ + unsigned int ret = 0; + + if (pFifo) { + ret = pFifo->FifoSz(pFifo); + } else { + pr_err("%s:pFifo = NULL, error\n", __func__); + ret = 0; + } + + return ret; +} + +unsigned int osal_fifo_avail(P_OSAL_FIFO pFifo) +{ + unsigned int ret = 0; + + if (pFifo) { + ret = pFifo->FifoAvailSz(pFifo); + } else { + pr_err("%s:pFifo = NULL, error\n", __func__); + ret = 0; + } + + return ret; +} + +unsigned int osal_fifo_is_empty(P_OSAL_FIFO pFifo) +{ + unsigned int ret = 0; + + if (pFifo) { + ret = pFifo->FifoIsEmpty(pFifo); + } else { + pr_err("%s:pFifo = NULL, error\n", __func__); + ret = 0; + } + + return ret; +} + +unsigned int osal_fifo_is_full(P_OSAL_FIFO pFifo) +{ + unsigned int ret = 0; + + if (pFifo) { + ret = pFifo->FifoIsFull(pFifo); + } else { + pr_err("%s:pFifo = NULL, error\n", __func__); + ret = 0; + } + return ret; +} + +/* + * sleepable lock operations APIs + * init + * lock + * unlock + * destroy + * + */ +#if !defined(CONFIG_PROVE_LOCKING) +int osal_unsleepable_lock_init(P_OSAL_UNSLEEPABLE_LOCK pUSL) +{ + spin_lock_init(&(pUSL->lock)); + return 0; +} +#endif + +int osal_lock_unsleepable_lock(P_OSAL_UNSLEEPABLE_LOCK pUSL) +{ + spin_lock_irqsave(&(pUSL->lock), pUSL->flag); + return 0; +} + +int osal_unlock_unsleepable_lock(P_OSAL_UNSLEEPABLE_LOCK pUSL) +{ + spin_unlock_irqrestore(&(pUSL->lock), pUSL->flag); + return 0; +} + +int osal_unsleepable_lock_deinit(P_OSAL_UNSLEEPABLE_LOCK pUSL) +{ + return 0; +} + +/* + * unsleepable operations APIs + * init + * lock + * unlock + * destroy + * + */ + +#if !defined(CONFIG_PROVE_LOCKING) +int osal_sleepable_lock_init(P_OSAL_SLEEPABLE_LOCK pSL) +{ + mutex_init(&pSL->lock); + return 0; +} +#endif + +int osal_lock_sleepable_lock(P_OSAL_SLEEPABLE_LOCK pSL) +{ + return mutex_lock_killable(&pSL->lock); +} + +int osal_unlock_sleepable_lock(P_OSAL_SLEEPABLE_LOCK pSL) +{ + mutex_unlock(&pSL->lock); + return 0; +} + +int osal_trylock_sleepable_lock(P_OSAL_SLEEPABLE_LOCK pSL) +{ + return mutex_trylock(&pSL->lock); +} + +int osal_sleepable_lock_deinit(P_OSAL_SLEEPABLE_LOCK pSL) +{ + mutex_destroy(&pSL->lock); + return 0; +} + +int osal_sleep_ms(unsigned int ms) +{ + msleep(ms); + return 0; +} + +int osal_udelay(unsigned int us) +{ + udelay(us); + return 0; +} + +int osal_usleep_range(unsigned long min, unsigned long max) +{ + usleep_range(min, max); + return 0; +} + +int osal_gettimeofday(int *sec, int *usec) +{ + int ret = 0; + struct timespec64 now; + + ktime_get_real_ts64(&now); + + if (sec != NULL) + *sec = now.tv_sec; + else + ret = -1; + + if (usec != NULL) + *usec = (now.tv_nsec / 1000); + else + ret = -1; + + return ret; +} + +int osal_gettimeofday2(struct timeval *tv) +{ + int ret = 0; + struct timespec64 now; + + if (tv == NULL) + return -1; + + ktime_get_real_ts64(&now); + tv->tv_sec = now.tv_sec; + tv->tv_usec = (now.tv_nsec / 1000); + return ret; +} + +void osal_get_local_time(unsigned long long *sec, unsigned long *nsec) +{ + if (sec != NULL && nsec != NULL) { + *sec = local_clock(); + *nsec = do_div(*sec, 1000000000)/1000; + } else + pr_err("The input parameters error when get local time\n"); +} + +unsigned long long osal_elapsed_us(unsigned long long ts, unsigned long usec) +{ + unsigned long long current_ts = 0; + unsigned long current_usec = 0; + + osal_get_local_time(¤t_ts, ¤t_usec); + return (current_ts*1000000 + current_usec) - (ts*1000000 + usec); +} + +void osal_buffer_dump(const unsigned char *buf, + const unsigned char *title, const unsigned int len, + const unsigned int limit) +{ + int k; + unsigned int dump_len; + char str[DBG_LOG_STR_SIZE] = {""}; + int strlen = 0; + + pr_info("[%s] len=%d, limit=%d, start dump\n", title, len, limit); + + dump_len = ((limit != 0) && (len > limit)) ? limit : len; + for (k = 0; k < dump_len; k++) { + if ((k+1) % 16 != 0) { + strlen += osal_snprintf(str + strlen, DBG_LOG_STR_SIZE - strlen, + "%02x ", buf[k]); + } else { + strlen += osal_snprintf(str + strlen, DBG_LOG_STR_SIZE - strlen, + "%02x ", buf[k]); + + pr_info("%s", str); + strlen = 0; + } + } + if (k % 16 != 0) + pr_info("%s\n", str); + + pr_info("end of dump\n"); +} + +void osal_buffer_dump_data(const unsigned int *buf, + const unsigned char *title, const unsigned int len, + const unsigned int limit, + const int flag) +{ + int k; + unsigned int dump_len; + char str[DBG_LOG_STR_SIZE] = {""}; + int strlen = 0; + + dump_len = ((limit != 0) && (len > limit)) ? limit : len; + for (k = 0; k < dump_len; k++) { + if (((k+1) % 8 != 0) && (k < (dump_len - 1))) { + strlen += osal_snprintf(str + strlen, DBG_LOG_STR_SIZE - strlen, + "0x%08x,", buf[k]); + } else { + strlen += osal_snprintf(str + strlen, DBG_LOG_STR_SIZE - strlen, + "0x%08x,", buf[k]); + if (flag) + osal_ftrace_print("%s%s", title, str); + else + pr_info("%s%s", title, str); + strlen = 0; + } + } + if (k % 8 != 0) { + if (flag) + osal_ftrace_print("%s%s", title, str); + else + pr_info("%s%s", title, str); + } +} + +unsigned int osal_op_get_id(P_OSAL_OP pOp) +{ + return (pOp) ? pOp->op.opId : 0xFFFFFFFF; +} + +MTK_CONN_BOOL osal_op_is_wait_for_signal(P_OSAL_OP pOp) +{ + return (pOp && pOp->signal.timeoutValue) + ? MTK_CONN_BOOL_TRUE : MTK_CONN_BOOL_FALSE; +} + +void osal_op_raise_signal(P_OSAL_OP pOp, int result) +{ + if (pOp) { + pOp->result = result; + osal_raise_signal(&pOp->signal); + } +} + +int osal_ftrace_print(const char *str, ...) +{ +#ifdef CONFIG_TRACING + va_list args; + char tempString[DBG_LOG_STR_SIZE]; + + if (ftrace_flag) { + va_start(args, str); + vsnprintf(tempString, DBG_LOG_STR_SIZE, str, args); + va_end(args); + + trace_printk("%s\n", tempString); + } +#endif + return 0; +} + +int osal_ftrace_print_ctrl(int flag) +{ +#ifdef CONFIG_TRACING + if (flag) + ftrace_flag = 1; + else + ftrace_flag = 0; +#endif + return 0; +} + +void osal_set_op_result(P_OSAL_OP pOp, int result) +{ + if (pOp) + pOp->result = result; + +} + +static void _osal_opq_dump(const char *qName, P_OSAL_OP_Q pOpQ) +{ + /* Line format: + * [LogicalIdx(PhysicalIdx)]Address:OpId(Ref)(Result)-Info-OpData0,OpData1,OpData2,OpData3,OpData5_ + * [LogicalIdx] max 10+2=12 chars (decimal) + * (PhysicalIdx) max 10+2=12 chars (decimal) + * Address: max 16+1=17 chars (hex) + * OpId max 10 chars (decimal) + * (Ref) max 2+2=4 chars (should only be 1 digit, reserve 2 in case of negative number) + * (Result) max 11+2=13 chars (signed decimal) + * -Info- max 8+2=10 chars (hex) + * OpData, max 16+1=17 chars (hex) + */ +#define OPQ_DUMP_OP_PER_LINE 1 +#define OPQ_DUMP_OPDATA_PER_OP 6 +#define OPQ_DUMP_OP_BUF_SIZE (12 + 12 + 17 + 10 + 4 + 13 + 10 + (17 * (OPQ_DUMP_OPDATA_PER_OP)) + 1) +#define OPQ_DUMP_LINE_BUF_SIZE ((OPQ_DUMP_OP_BUF_SIZE * OPQ_DUMP_OP_PER_LINE) + 1) + unsigned int rd; + unsigned int wt; + unsigned int idx = 0; + unsigned int opDataIdx; + unsigned int idxInBuf; + int printed; + P_OSAL_OP op; + char buf[OPQ_DUMP_LINE_BUF_SIZE]; + + rd = pOpQ->read; + wt = pOpQ->write; + + pr_info("%s(%p), sz:%u/%u, rd:%u, wt:%u\n", + qName, pOpQ, RB_COUNT(pOpQ), RB_SIZE(pOpQ), rd, wt); + while (rd != wt && idx < RB_SIZE(pOpQ)) { + idxInBuf = idx % OPQ_DUMP_OP_PER_LINE; + op = pOpQ->queue[rd & RB_MASK(pOpQ)]; + + if (idxInBuf == 0) { + printed = 0; + buf[0] = 0; + } + + if (op) { + printed += snprintf(buf + printed, OPQ_DUMP_LINE_BUF_SIZE - printed, + "[%u(%u)]%p:%u(%d)(%d)-%u-", + idx, + (rd & RB_MASK(pOpQ)), + op, + op->op.opId, + atomic_read(&op->ref_count), + op->result, + op->op.u4InfoBit); + for (opDataIdx = 0; opDataIdx < OPQ_DUMP_OPDATA_PER_OP; opDataIdx++) + printed += snprintf(buf + printed, OPQ_DUMP_LINE_BUF_SIZE - printed, + "%zx,", op->op.au4OpData[opDataIdx]); + buf[printed-1] = ' '; + } else { + printed += snprintf(buf + printed, OPQ_DUMP_LINE_BUF_SIZE - printed, + "[%u(%u)]%p ", idx, (rd & RB_MASK(pOpQ)), op); + } + buf[printed++] = ' '; + + if (idxInBuf == OPQ_DUMP_OP_PER_LINE - 1 || rd == wt - 1) { + buf[printed - 1] = 0; + pr_info("%s\n", buf); + } + rd++; + idx++; + } +} + +void osal_opq_dump(const char *qName, P_OSAL_OP_Q pOpQ) +{ + int err; + + err = osal_lock_sleepable_lock(&pOpQ->sLock); + if (err) { + pr_info("Failed to lock queue (%d)\n", err); + return; + } + + _osal_opq_dump(qName, pOpQ); + + osal_unlock_sleepable_lock(&pOpQ->sLock); +} + +void osal_opq_dump_locked(const char *qName, P_OSAL_OP_Q pOpQ) +{ + _osal_opq_dump(qName, pOpQ); +} + +MTK_CONN_BOOL osal_opq_has_op(P_OSAL_OP_Q pOpQ, P_OSAL_OP pOp) +{ + unsigned int rd; + unsigned int wt; + P_OSAL_OP op; + + rd = pOpQ->read; + wt = pOpQ->write; + + while (rd != wt) { + op = pOpQ->queue[rd & RB_MASK(pOpQ)]; + if (op == pOp) + return MTK_CONN_BOOL_TRUE; + rd++; + } + return MTK_CONN_BOOL_FALSE; +} + +static void osal_op_history_print_work(struct work_struct *work) +{ + struct osal_op_history *log_history + = container_of(work, struct osal_op_history, dump_work); + struct ring *ring_buffer = &log_history->dump_ring_buffer; + struct ring_segment seg; + struct osal_op_history_entry *queue = ring_buffer->base; + struct osal_op_history_entry *entry; + int index = 0; + + if (queue == NULL) { + pr_info("queue shouldn't be NULL, %s", log_history->name); + return; + } + + if (RING_EMPTY(ring_buffer)) + pr_info("History of %s is empty.\n", log_history->name); + + RING_READ_FOR_EACH_ITEM(RING_SIZE(ring_buffer), seg, ring_buffer) { + index = seg.ring_pt - ring_buffer->base; + entry = &queue[index]; + pr_info("(%llu.%06lu) %s: pOp(%p):%u(%d)-%x-%zx,%zx,%zx,%zx\n", + entry->ts, + entry->usec, + log_history->name, + entry->opbuf_address, + entry->op_id, + entry->opbuf_ref_count, + entry->op_info_bit, + entry->param_0, + entry->param_1, + entry->param_2, + entry->param_3); + } + kfree(queue); + ring_buffer->base = NULL; +} + +void osal_op_history_init(struct osal_op_history *log_history, int queue_size) +{ + int size = queue_size * sizeof(struct osal_op_history_entry); + + spin_lock_init(&(log_history->lock)); + + log_history->queue = kzalloc(size, GFP_ATOMIC); + if (log_history->queue == NULL) + return; + + /* queue_size must be power of 2 */ + ring_init( + &log_history->queue, + queue_size, + 0, + 0, + &log_history->ring_buffer); + + INIT_WORK(&log_history->dump_work, osal_op_history_print_work); +} + +void osal_op_history_print(struct osal_op_history *log_history, char *name) +{ + struct osal_op_history_entry *queue; + struct ring *ring_buffer, *dump_ring_buffer; + int queue_size; + unsigned long flags; + struct work_struct *work = &log_history->dump_work; + spinlock_t *lock = &(log_history->lock); + + if (log_history->queue == NULL) { + pr_info("Queue is NULL, name: %s\n", name); + return; + } + + ring_buffer = &log_history->ring_buffer; + queue_size = sizeof(struct osal_op_history_entry) + * RING_SIZE(ring_buffer); + + /* Allocate memory before getting lock to save time of holding lock */ + queue = kmalloc(queue_size, GFP_KERNEL); + if (queue == NULL) + return; + + dump_ring_buffer = &log_history->dump_ring_buffer; + + spin_lock_irqsave(lock, flags); + if (dump_ring_buffer->base != NULL) { + spin_unlock_irqrestore(lock, flags); + kfree(queue); + pr_info("print is ongoing: %s\n", name); + return; + } + + osal_snprintf(log_history->name, sizeof(log_history->name), "%s", name); + osal_memcpy(queue, log_history->queue, queue_size); + osal_memcpy(dump_ring_buffer, ring_buffer, sizeof(struct ring)); + /* assign value to base after memory copy */ + dump_ring_buffer->base = queue; + spin_unlock_irqrestore(lock, flags); + schedule_work(work); +} + +void osal_op_history_save(struct osal_op_history *log_history, P_OSAL_OP pOp) +{ + struct osal_op_history_entry *entry = NULL; + struct ring_segment seg; + int index; + unsigned long long sec = 0; + unsigned long usec = 0; + unsigned long flags; + + if (log_history->queue == NULL) + return; + + osal_get_local_time(&sec, &usec); + + spin_lock_irqsave(&(log_history->lock), flags); + RING_OVERWRITE_FOR_EACH(1, seg, &log_history->ring_buffer) { + index = seg.ring_pt - log_history->ring_buffer.base; + entry = &log_history->queue[index]; + } + + if (entry == NULL) { + pr_info("Entry is null, size %d\n", + RING_SIZE(&log_history->ring_buffer)); + spin_unlock_irqrestore(&(log_history->lock), flags); + return; + } + + entry->opbuf_address = pOp; + entry->op_id = pOp->op.opId; + entry->opbuf_ref_count = atomic_read(&pOp->ref_count); + entry->op_info_bit = pOp->op.u4InfoBit; + entry->param_0 = pOp->op.au4OpData[0]; + entry->param_1 = pOp->op.au4OpData[1]; + entry->param_2 = pOp->op.au4OpData[2]; + entry->param_3 = pOp->op.au4OpData[3]; + entry->ts = sec; + entry->usec = usec; + spin_unlock_irqrestore(&(log_history->lock), flags); +} + diff --git a/package/mtk/drivers/conninfra/src/base/ring.c b/package/mtk/drivers/conninfra/src/base/ring.c new file mode 100644 index 0000000000..24cad3b7f9 --- /dev/null +++ b/package/mtk/drivers/conninfra/src/base/ring.c @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2016 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 http://www.gnu.org/licenses/gpl-2.0.html for more details. + */ +#include "ring.h" +#include +#include +#include + + +void ring_init(void *base, unsigned int max_size, unsigned int read, + unsigned int write, struct ring *ring) +{ + WARN_ON(!base); + + /* making sure max_size is power of 2 */ + WARN_ON(!max_size || (max_size & (max_size - 1))); + + /* making sure write largger than read */ + WARN_ON(read > write); + + ring->base = base; + ring->read = read; + ring->write = write; + ring->max_size = max_size; +} + +void ring_dump(const char *title, struct ring *ring) +{ + pr_info("[%s] ring:{write=%d, read=%d, max_size=%d}\n", + title, ring->write, ring->read, ring->max_size); +} + +void ring_dump_segment(const char *title, struct ring_segment *seg) +{ + pr_info("[%s] seg:{ring_pt=0x%p, data_pos=%d, sz=%d, remain=%d}\n", + title, seg->ring_pt, seg->data_pos, + seg->sz, seg->remain); +} + +/* + * Function prepares the ring_segment and + * returns the number of valid bytes for read. + */ +unsigned int ring_read_prepare(unsigned int sz, + struct ring_segment *seg, + struct ring *ring) +{ + unsigned int wt = ring->write; + unsigned int rd = ring->read; + + memset(seg, 0, sizeof(struct ring_segment)); + if (sz > wt - rd) + sz = wt - rd; + seg->remain = sz; + /* ring_dump(__func__, ring); */ + /* ring_dump_segment(__func__, seg); */ + return seg->remain; +} + +/* + * Function prepares the ring_segment and + * returns the number of bytes available for write. + */ +unsigned int ring_write_prepare(unsigned int sz, + struct ring_segment *seg, + struct ring *ring) +{ + unsigned int wt = ring->write; + unsigned int rd = ring->read; + + memset(seg, 0, sizeof(struct ring_segment)); + if (sz > ring->max_size - (wt - rd)) + sz = ring->max_size - (wt - rd); + seg->remain = sz; + /* ring_dump(__func__, ring); */ + /* ring_dump_segment(__func__, seg); */ + return seg->remain; +} + +unsigned int ring_overwrite_prepare(unsigned int sz, struct ring_segment *seg, + struct ring *ring) +{ + unsigned int wt = ring->write; + unsigned int rd = ring->read; + + memset(seg, 0, sizeof(struct ring_segment)); + if (sz > ring->max_size - (wt - rd)) + ring->read += sz - (ring->max_size - (wt - rd)); + seg->remain = sz; + /* ring_dump(__func__, ring); */ + /* ring_dump_segment(__func__, seg); */ + return seg->remain; +} + +void __ring_segment_prepare(unsigned int from, unsigned int sz, + struct ring_segment *seg, + struct ring *ring) +{ + unsigned int ring_pos = from & (ring->max_size - 1); + + seg->ring_pt = ring->base + ring_pos; + seg->data_pos = (seg->sz ? seg->data_pos + seg->sz : 0); + if (ring_pos + sz <= ring->max_size) + seg->sz = sz; + else + seg->sz = ring->max_size - ring_pos; + seg->remain -= seg->sz; + /* ring_dump(__func__, ring); */ + /* ring_dump_segment(__func__, seg); */ +} + +void _ring_segment_prepare(unsigned int from, + struct ring_segment *seg, + struct ring *ring) +{ + __ring_segment_prepare(from, seg->remain, seg, ring); +} + +void _ring_segment_prepare_item(unsigned int from, + struct ring_segment *seg, + struct ring *ring) +{ + unsigned int size; + + size = (seg->remain ? 1 : 0); + __ring_segment_prepare(from, size, seg, ring); +} + +void _ring_read_commit(struct ring_segment *seg, struct ring *ring) +{ + ring->read += seg->sz; + /* ring_dump(__func__, ring); */ + /* ring_dump_segment(__func__, seg); */ +} +void _ring_write_commit(struct ring_segment *seg, struct ring *ring) +{ + ring->write += seg->sz; + /* ring_dump(__func__, ring); */ + /* ring_dump_segment(__func__, seg); */ +} + diff --git a/package/mtk/drivers/conninfra/src/core/conninfra_core.c b/package/mtk/drivers/conninfra/src/core/conninfra_core.c new file mode 100644 index 0000000000..0f0a771309 --- /dev/null +++ b/package/mtk/drivers/conninfra/src/core/conninfra_core.c @@ -0,0 +1,1244 @@ +/* + * Copyright (C) 2016 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 http://www.gnu.org/licenses/gpl-2.0.html for more details. + */ +/*! \file +* \brief Declaration of library functions +* +* Any definitions in this file will be shared among GLUE Layer and internal Driver Stack. +*/ + +#define pr_fmt(fmt) KBUILD_MODNAME "@(%s:%d) " fmt, __func__, __LINE__ + +#include "consys_hw.h" +#include "conninfra_core.h" +#include "msg_thread.h" +#include "consys_reg_mng.h" + +/******************************************************************************* +* C O M P I L E R F L A G S +******************************************************************************** +*/ + +/******************************************************************************* +* M A C R O S +******************************************************************************** +*/ +#define CONNINFRA_EVENT_TIMEOUT 3000 +#define CONNINFRA_RESET_TIMEOUT 500 + +/******************************************************************************* +* E X T E R N A L R E F E R E N C E S +******************************************************************************** +*/ +#include + +/******************************************************************************* +* C O N S T A N T S +******************************************************************************** +*/ + +/******************************************************************************* +* D A T A T Y P E S +******************************************************************************** +*/ + +/******************************************************************************* +* F U N C T I O N D E C L A R A T I O N S +******************************************************************************** +*/ + +static int opfunc_power_on(struct msg_op_data *op); +static int opfunc_power_off(struct msg_op_data *op); +static int opfunc_chip_rst(struct msg_op_data *op); +static int opfunc_rfspi_read(struct msg_op_data *op); +static int opfunc_rfspi_write(struct msg_op_data *op); +static int opfunc_adie_top_ck_en_on(struct msg_op_data *op); +static int opfunc_adie_top_ck_en_off(struct msg_op_data *op); +static int opfunc_spi_clock_switch(struct msg_op_data *op); +static int opfunc_force_conninfra_wakeup(struct msg_op_data *op); +static int opfunc_force_conninfra_sleep(struct msg_op_data *op); +static int opfunc_dump_power_state(struct msg_op_data *op); +static int opfunc_subdrv_pre_reset(struct msg_op_data *op); +static int opfunc_subdrv_post_reset(struct msg_op_data *op); +static void _conninfra_core_update_rst_status(enum chip_rst_status status); + +/******************************************************************************* +* P U B L I C D A T A +******************************************************************************** +*/ + +struct conninfra_ctx g_conninfra_ctx; + +/******************************************************************************* +* P R I V A T E D A T A +******************************************************************************** +*/ +static const msg_opid_func conninfra_core_opfunc[] = { + [CONNINFRA_OPID_PWR_ON] = opfunc_power_on, + [CONNINFRA_OPID_PWR_OFF] = opfunc_power_off, + [CONNINFRA_OPID_RFSPI_READ] = opfunc_rfspi_read, + [CONNINFRA_OPID_RFSPI_WRITE] = opfunc_rfspi_write, + [CONNINFRA_OPID_ADIE_TOP_CK_EN_ON] = opfunc_adie_top_ck_en_on, + [CONNINFRA_OPID_ADIE_TOP_CK_EN_OFF] = opfunc_adie_top_ck_en_off, + [CONNINFRA_OPID_SPI_CLOCK_SWITCH] = opfunc_spi_clock_switch, + [CONNINFRA_OPID_FORCE_CONNINFRA_WAKUP] = opfunc_force_conninfra_wakeup, + [CONNINFRA_OPID_FORCE_CONNINFRA_SLEEP] = opfunc_force_conninfra_sleep, + [CONNINFRA_OPID_DUMP_POWER_STATE] = opfunc_dump_power_state, +}; + +static const msg_opid_func conninfra_core_cb_opfunc[] = { + [CONNINFRA_CB_OPID_CHIP_RST] = opfunc_chip_rst, +}; + + +/* subsys ops */ +static char *drv_thread_name[] = { + [CONNDRV_TYPE_BT] = "sub_bt_thrd", + [CONNDRV_TYPE_FM] = "sub_fm_thrd", + [CONNDRV_TYPE_GPS] = "sub_gps_thrd", + [CONNDRV_TYPE_WIFI] = "sub_wifi_thrd", + [CONNDRV_TYPE_CONNINFRA] = "sub_conninfra_thrd", +}; + +static char *drv_name[] = { + [CONNDRV_TYPE_BT] = "BT", + [CONNDRV_TYPE_FM] = "FM", + [CONNDRV_TYPE_GPS] = "GPS", + [CONNDRV_TYPE_WIFI] = "WIFI", + [CONNDRV_TYPE_CONNINFRA] = "CONNINFRA", +}; + +typedef enum { + INFRA_SUBDRV_OPID_PRE_RESET = 0, + INFRA_SUBDRV_OPID_POST_RESET = 1, + INFRA_SUBDRV_OPID_MAX +} infra_subdrv_op; + + +static const msg_opid_func infra_subdrv_opfunc[] = { + [INFRA_SUBDRV_OPID_PRE_RESET] = opfunc_subdrv_pre_reset, + [INFRA_SUBDRV_OPID_POST_RESET] = opfunc_subdrv_post_reset, +}; + +/******************************************************************************* +* F U N C T I O N S +******************************************************************************** +*/ + +static void reset_chip_rst_trg_data(void) +{ + g_conninfra_ctx.trg_drv = CONNDRV_TYPE_MAX; + memset(g_conninfra_ctx.trg_reason, '\0', CHIP_RST_REASON_MAX_LEN); +} + +static unsigned long timeval_to_ms(struct timeval *begin, struct timeval *end) +{ + unsigned long time_diff; + + time_diff = (end->tv_sec - begin->tv_sec) * 1000; + time_diff += (end->tv_usec - begin->tv_usec) / 1000; + + return time_diff; +} + +static unsigned int opfunc_get_current_status(void) +{ + unsigned int ret = 0; + unsigned int i; + + for (i = 0; i < CONNDRV_TYPE_MAX; i++) { + ret |= (g_conninfra_ctx.drv_inst[i].drv_status << i); + } + + return ret; +} + +static void opfunc_vcn_control_internal(unsigned int drv_type, bool on) +{ + /* VCNx enable */ + switch (drv_type) { + case CONNDRV_TYPE_BT: + consys_hw_bt_power_ctl(on); + break; + case CONNDRV_TYPE_FM: + consys_hw_fm_power_ctl(on); + break; + case CONNDRV_TYPE_GPS: + consys_hw_gps_power_ctl(on); + break; + case CONNDRV_TYPE_WIFI: + consys_hw_wifi_power_ctl(on); + break; + case CONNDRV_TYPE_CONNINFRA: + break; + default: + pr_err("Wrong parameter: drv_type(%d)\n", drv_type); + break; + } +} + +static int opfunc_power_on_internal(unsigned int drv_type) +{ + int ret; + struct conninfra_ctx *infra_ctx = &g_conninfra_ctx; + + /* Check abnormal type */ + if (drv_type >= CONNDRV_TYPE_MAX) { + pr_err("abnormal Fun(%d)\n", drv_type); + return -EINVAL; + } + + /* Check abnormal state */ + if ((g_conninfra_ctx.drv_inst[drv_type].drv_status < DRV_STS_POWER_OFF) + || (g_conninfra_ctx.drv_inst[drv_type].drv_status >= DRV_STS_MAX)) { + pr_err("func(%d) status[0x%x] abnormal\n", drv_type, + g_conninfra_ctx.drv_inst[drv_type].drv_status); + return -EINVAL; + } + + ret = osal_lock_sleepable_lock(&infra_ctx->core_lock); + if (ret) { + pr_err("core_lock fail!!\n"); + return ret; + } + + /* check if func already on */ + if (g_conninfra_ctx.drv_inst[drv_type].drv_status == DRV_STS_POWER_ON) { + pr_warn("func(%d) already on\n", drv_type); + osal_unlock_sleepable_lock(&infra_ctx->core_lock); + return 0; + } + + ret = consys_hw_pwr_on(opfunc_get_current_status(), drv_type); + if (ret) { + pr_err("Conninfra power on fail. drv(%d) ret=(%d)\n", + drv_type, ret); + osal_unlock_sleepable_lock(&infra_ctx->core_lock); + return -3; + } + + /* POWER ON SEQUENCE */ + g_conninfra_ctx.infra_drv_status = DRV_STS_POWER_ON; + g_conninfra_ctx.drv_inst[drv_type].drv_status = DRV_STS_POWER_ON; + + /* VCNx enable */ + opfunc_vcn_control_internal(drv_type, true); + + pr_info("[Conninfra Pwr On] BT=[%d] FM=[%d] GPS=[%d] WF=[%d] CONNINFRA=[%d]\n", + infra_ctx->drv_inst[CONNDRV_TYPE_BT].drv_status, + infra_ctx->drv_inst[CONNDRV_TYPE_FM].drv_status, + infra_ctx->drv_inst[CONNDRV_TYPE_GPS].drv_status, + infra_ctx->drv_inst[CONNDRV_TYPE_WIFI].drv_status, + infra_ctx->drv_inst[CONNDRV_TYPE_CONNINFRA].drv_status); + + osal_unlock_sleepable_lock(&infra_ctx->core_lock); + + return 0; +} + +static int opfunc_power_on(struct msg_op_data *op) +{ + unsigned int drv_type = op->op_data[0]; + + return opfunc_power_on_internal(drv_type); +} + +static int opfunc_power_off_internal(unsigned int drv_type) +{ + int i, ret; + bool try_power_off = true; + struct conninfra_ctx *infra_ctx = &g_conninfra_ctx; + unsigned int curr_status = opfunc_get_current_status(); + + /* Check abnormal type */ + if (drv_type >= CONNDRV_TYPE_MAX) { + pr_err("abnormal Fun(%d)\n", drv_type); + return -EINVAL; + } + + ret = osal_lock_sleepable_lock(&infra_ctx->core_lock); + if (ret) { + pr_err("core_lock fail!!\n"); + return ret; + } + + /* Check abnormal state */ + if ((g_conninfra_ctx.drv_inst[drv_type].drv_status < DRV_STS_POWER_OFF) + || (g_conninfra_ctx.drv_inst[drv_type].drv_status >= DRV_STS_MAX)) { + pr_err("func(%d) status[0x%x] abnormal\n", drv_type, + g_conninfra_ctx.drv_inst[drv_type].drv_status); + osal_unlock_sleepable_lock(&infra_ctx->core_lock); + return -2; + } + + /* Special case for force power off */ + if (drv_type == CONNDRV_TYPE_CONNINFRA) { + if (g_conninfra_ctx.infra_drv_status == DRV_STS_POWER_OFF) { + pr_warn("Connsys already off, do nothing for force off\n"); + return 0; + } + /* Turn off subsys VCN and update record */ + for (i = 0; i < CONNDRV_TYPE_MAX; i++) { + if (g_conninfra_ctx.drv_inst[i].drv_status == DRV_STS_POWER_ON) { + opfunc_vcn_control_internal(i, false); + g_conninfra_ctx.drv_inst[i].drv_status = DRV_STS_POWER_OFF; + } + } + /* POWER OFF SEQUENCE */ + ret = consys_hw_pwr_off(0, drv_type); + /* For force power off operation, ignore err code */ + if (ret) + pr_err("Force power off fail. ret=%d\n", ret); + try_power_off = true; + } else { + /* check if func already off */ + if (g_conninfra_ctx.drv_inst[drv_type].drv_status + == DRV_STS_POWER_OFF) { + pr_warn("func(%d) already off\n", drv_type); + osal_unlock_sleepable_lock(&infra_ctx->core_lock); + return 0; + } + /* VCNx disable */ + opfunc_vcn_control_internal(drv_type, false); + g_conninfra_ctx.drv_inst[drv_type].drv_status = DRV_STS_POWER_OFF; + /* is there subsys on ? */ + for (i = 0; i < CONNDRV_TYPE_MAX; i++) + if (g_conninfra_ctx.drv_inst[i].drv_status == DRV_STS_POWER_ON) + try_power_off = false; + + /* POWER OFF SEQUENCE */ + ret = consys_hw_pwr_off(curr_status, drv_type); + if (ret) { + pr_err("Conninfra power on fail. drv(%d) ret=(%d)\n", + drv_type, ret); + osal_unlock_sleepable_lock(&infra_ctx->core_lock); + return -3; + } + } + + if (try_power_off) + g_conninfra_ctx.infra_drv_status = DRV_STS_POWER_OFF; + + pr_info("[Conninfra Pwr Off] Conninfra=[%d] BT=[%d] FM=[%d] GPS=[%d] WF=[%d]\n", + infra_ctx->infra_drv_status, + infra_ctx->drv_inst[CONNDRV_TYPE_BT].drv_status, + infra_ctx->drv_inst[CONNDRV_TYPE_FM].drv_status, + infra_ctx->drv_inst[CONNDRV_TYPE_GPS].drv_status, + infra_ctx->drv_inst[CONNDRV_TYPE_WIFI].drv_status); + + osal_unlock_sleepable_lock(&infra_ctx->core_lock); + return 0; +} + +static int opfunc_power_off(struct msg_op_data *op) +{ + unsigned int drv_type = op->op_data[0]; + + return opfunc_power_off_internal(drv_type); +} + +static int opfunc_chip_rst(struct msg_op_data *op) +{ + int i, ret, cur_rst_state; + struct subsys_drv_inst *drv_inst; + unsigned int drv_pwr_state[CONNDRV_TYPE_MAX]; + const unsigned int subdrv_all_done = (0x1 << CONNDRV_TYPE_MAX) - 1; + struct timeval pre_begin, pre_end, reset_end, done_end; + + if (g_conninfra_ctx.infra_drv_status == DRV_STS_POWER_OFF) { + pr_info("No subsys on, just return\n"); + _conninfra_core_update_rst_status(CHIP_RST_NONE); + return 0; + } + + osal_gettimeofday2(&pre_begin); + + atomic_set(&g_conninfra_ctx.rst_state, 0); + sema_init(&g_conninfra_ctx.rst_sema, 1); + + _conninfra_core_update_rst_status(CHIP_RST_PRE_CB); + + /* pre */ + for (i = 0; i < CONNDRV_TYPE_MAX; i++) { + drv_inst = &g_conninfra_ctx.drv_inst[i]; + drv_pwr_state[i] = drv_inst->drv_status; + pr_info("subsys %d is %d\n", i, drv_inst->drv_status); + ret = msg_thread_send_1(&drv_inst->msg_ctx, INFRA_SUBDRV_OPID_PRE_RESET, i); + } + + pr_info("[chip_rst] pre vvvvvvvvvvvvv\n"); + while (atomic_read(&g_conninfra_ctx.rst_state) != subdrv_all_done) { + ret = down_timeout(&g_conninfra_ctx.rst_sema, msecs_to_jiffies(CONNINFRA_RESET_TIMEOUT)); + pr_info("sema ret=[%d]\n", ret); + if (ret == 0) + continue; + cur_rst_state = atomic_read(&g_conninfra_ctx.rst_state); + pr_info("cur_rst state =[%d]\n", cur_rst_state); + for (i = 0; i < CONNDRV_TYPE_MAX; i++) { + if ((cur_rst_state & (0x1 << i)) == 0) { + pr_info("[chip_rst] [%s] pre-callback is not back\n", drv_thread_name[i]); + } + } + } + + _conninfra_core_update_rst_status(CHIP_RST_RESET); + + osal_gettimeofday2(&pre_end); + + pr_info("[chip_rst] reset ++++++++++++\n"); + /*******************************************************/ + /* reset */ + /* call consys_hw */ + /*******************************************************/ + /* Special power-off function, turn off connsys directly */ + ret = opfunc_power_off_internal(CONNDRV_TYPE_CONNINFRA); + pr_info("Force conninfra power off, ret=%d\n", ret); + pr_info("conninfra status should be power off. Status=%d\n", g_conninfra_ctx.infra_drv_status); + + /* Turn on subsys */ + for (i = 0; i < CONNDRV_TYPE_MAX; i++) { + if (drv_pwr_state[i]) { + ret = opfunc_power_on_internal(i); + pr_info("Call subsys(%d) power on ret=%d\n", i, ret); + } + } + pr_info("conninfra status should be power on. Status=%d\n", g_conninfra_ctx.infra_drv_status); + + pr_info("[chip_rst] reset --------------\n"); + + _conninfra_core_update_rst_status(CHIP_RST_POST_CB); + + osal_gettimeofday2(&reset_end); + + /* post */ + atomic_set(&g_conninfra_ctx.rst_state, 0); + sema_init(&g_conninfra_ctx.rst_sema, 1); + for (i = 0; i < CONNDRV_TYPE_MAX; i++) { + drv_inst = &g_conninfra_ctx.drv_inst[i]; + ret = msg_thread_send_1(&drv_inst->msg_ctx, INFRA_SUBDRV_OPID_POST_RESET, i); + } + + while (atomic_read(&g_conninfra_ctx.rst_state) != subdrv_all_done) { + ret = down_timeout(&g_conninfra_ctx.rst_sema, msecs_to_jiffies(CONNINFRA_RESET_TIMEOUT)); + if (ret == 0) + continue; + cur_rst_state = atomic_read(&g_conninfra_ctx.rst_state); + for (i = 0; i < CONNDRV_TYPE_MAX; i++) { + if ((cur_rst_state & (0x1 << i)) == 0) { + pr_info("[chip_rst] [%s] post-callback is not back\n", drv_thread_name[i]); + } + } + } + pr_info("[chip_rst] post ^^^^^^^^^^^^^^\n"); + + reset_chip_rst_trg_data(); + //_conninfra_core_update_rst_status(CHIP_RST_DONE); + _conninfra_core_update_rst_status(CHIP_RST_NONE); + osal_gettimeofday2(&done_end); + + pr_info("[chip_rst] summary pre=[%lu] reset=[%lu] post=[%lu]\n", + timeval_to_ms(&pre_begin, &pre_end), + timeval_to_ms(&pre_end, &reset_end), + timeval_to_ms(&reset_end, &done_end)); + + return 0; +} + +static int opfunc_rfspi_read(struct msg_op_data *op) +{ + int ret = 0; + unsigned int data = 0; + unsigned int* data_pt = (unsigned int*)op->op_data[2]; + + ret = osal_lock_sleepable_lock(&g_conninfra_ctx.core_lock); + if (ret) { + pr_err("core_lock fail!!\n"); + return CONNINFRA_SPI_OP_FAIL; + } + + if (g_conninfra_ctx.infra_drv_status != DRV_STS_POWER_ON) { + pr_err("Connsys didn't power on\n"); + ret = CONNINFRA_SPI_OP_FAIL; + goto err; + } + + if (consys_hw_reg_readable() == 0) { + pr_err("connsys reg not readable\n"); + ret = CONNINFRA_SPI_OP_FAIL; + goto err; + } + + /* DO read spi */ + ret = consys_hw_spi_read(op->op_data[0], op->op_data[1], &data); + if (data_pt) + *(data_pt) = data; +err: + osal_unlock_sleepable_lock(&g_conninfra_ctx.core_lock); + return ret; +} + +static int opfunc_rfspi_write(struct msg_op_data *op) +{ + int ret = 0; + + ret = osal_lock_sleepable_lock(&g_conninfra_ctx.core_lock); + if (ret) { + pr_err("core_lock fail!!\n"); + return CONNINFRA_SPI_OP_FAIL; + } + + if (g_conninfra_ctx.infra_drv_status != DRV_STS_POWER_ON) { + pr_err("Connsys didn't power on\n"); + ret = CONNINFRA_SPI_OP_FAIL; + goto err; + } + + if (consys_hw_reg_readable() == 0) { + pr_err("connsys reg not readable\n"); + ret = CONNINFRA_SPI_OP_FAIL; + goto err; + } + + /* DO spi write */ + ret = consys_hw_spi_write(op->op_data[0], op->op_data[1], op->op_data[2]); +err: + osal_unlock_sleepable_lock(&g_conninfra_ctx.core_lock); + return ret; +} + +static int opfunc_adie_top_ck_en_on(struct msg_op_data *op) +{ + int ret = 0; + unsigned int type = op->op_data[0]; + + if (type >= CONNDRV_TYPE_MAX) { + pr_err("wrong parameter %d\n", type); + return -EINVAL; + } + + ret = osal_lock_sleepable_lock(&g_conninfra_ctx.core_lock); + if (ret) { + pr_err("core_lock fail!!\n"); + ret = -1; + goto err; + } + + if (g_conninfra_ctx.infra_drv_status != DRV_STS_POWER_ON) { + pr_err("Connsys didn't power on\n"); + ret = -2; + goto err; + } + + ret = consys_hw_adie_top_ck_en_on(type); + +err: + osal_unlock_sleepable_lock(&g_conninfra_ctx.core_lock); + return ret; +} + + +static int opfunc_adie_top_ck_en_off(struct msg_op_data *op) +{ + int ret = 0; + unsigned int type = op->op_data[0]; + + if (type >= CONNDRV_TYPE_MAX) { + pr_err("wrong parameter %d\n", type); + return -EINVAL; + } + + ret = osal_lock_sleepable_lock(&g_conninfra_ctx.core_lock); + if (ret) { + pr_err("core_lock fail!!\n"); + ret = -1; + goto err; + } + if (g_conninfra_ctx.infra_drv_status != DRV_STS_POWER_ON) { + pr_err("Connsys didn't power on\n"); + ret = -2; + goto err; + } + + ret = consys_hw_adie_top_ck_en_off(type); +err: + osal_unlock_sleepable_lock(&g_conninfra_ctx.core_lock); + return ret; +} + +static int opfunc_spi_clock_switch(struct msg_op_data *op) +{ + int ret = 0; + unsigned int type = op->op_data[0]; + + if (type >= CONNSYS_SPI_SPEED_MAX) { + pr_err("wrong parameter %d\n", type); + return -EINVAL; + } + + ret = osal_lock_sleepable_lock(&g_conninfra_ctx.core_lock); + if (ret) { + pr_err("core_lock fail!!\n"); + ret = -2; + goto err; + } + if (g_conninfra_ctx.infra_drv_status != DRV_STS_POWER_ON) { + pr_err("Connsys didn't power on\n"); + ret = -2; + goto err; + } + + ret = consys_hw_spi_clock_switch(type); +err: + osal_unlock_sleepable_lock(&g_conninfra_ctx.core_lock); + return ret; +} + +static int opfunc_force_conninfra_wakeup(struct msg_op_data *op) +{ + int ret; + struct conninfra_ctx *infra_ctx = &g_conninfra_ctx; + + ret = osal_lock_sleepable_lock(&infra_ctx->core_lock); + if (ret) { + pr_err("core_lock fail!!\n"); + return ret; + } + + /* check if conninfra already on */ + if (g_conninfra_ctx.infra_drv_status != DRV_STS_POWER_ON) { + ret = -1; + goto err; + } + + ret = consys_hw_force_conninfra_wakeup(); + if (ret) + pr_err("force conninfra wakeup fail\n"); + +err: + osal_unlock_sleepable_lock(&infra_ctx->core_lock); + return ret; +} + +static int opfunc_force_conninfra_sleep(struct msg_op_data *op) +{ + int ret; + struct conninfra_ctx *infra_ctx = &g_conninfra_ctx; + + ret = osal_lock_sleepable_lock(&infra_ctx->core_lock); + if (ret) { + pr_err("core_lock fail!!\n"); + return ret; + } + + /* check if conninfra already on */ + if (g_conninfra_ctx.infra_drv_status != DRV_STS_POWER_ON) { + ret = -1; + goto err; + } + + ret = consys_hw_force_conninfra_sleep(); + if (ret) + pr_err("force conninfra sleep fail\n"); + +err: + osal_unlock_sleepable_lock(&infra_ctx->core_lock); + return ret; +} + + +static int opfunc_dump_power_state(struct msg_op_data *op) +{ + int ret; + struct conninfra_ctx *infra_ctx = &g_conninfra_ctx; + + ret = osal_lock_sleepable_lock(&infra_ctx->core_lock); + if (ret) { + pr_err("core_lock fail!!\n"); + return ret; + } + + /* check if conninfra already on */ + if (g_conninfra_ctx.infra_drv_status != DRV_STS_POWER_ON) { + ret = -1; + goto err; + } + + ret = consys_hw_dump_power_state(); + if (ret) + pr_err("dump power state fail\n"); + +err: + osal_unlock_sleepable_lock(&infra_ctx->core_lock); + return ret; + +} + +static int opfunc_subdrv_pre_reset(struct msg_op_data *op) +{ + int ret, cur_rst_state; + unsigned int drv_type = op->op_data[0]; + struct subsys_drv_inst *drv_inst; + + + /* TODO: should be locked, to avoid cb was reset */ + drv_inst = &g_conninfra_ctx.drv_inst[drv_type]; + if (/*drv_inst->drv_status == DRV_ST_POWER_ON &&*/ + drv_inst->ops_cb.rst_cb.pre_whole_chip_rst) { + + ret = drv_inst->ops_cb.rst_cb.pre_whole_chip_rst(g_conninfra_ctx.trg_drv, g_conninfra_ctx.trg_reason); + if (ret) + pr_err("[%s] fail [%d]\n", __func__, ret); + } + + atomic_add(0x1 << drv_type, &g_conninfra_ctx.rst_state); + cur_rst_state = atomic_read(&g_conninfra_ctx.rst_state); + + pr_info("[%s] rst_state=[%d]\n", drv_thread_name[drv_type], cur_rst_state); + + up(&g_conninfra_ctx.rst_sema); + return 0; +} + +static int opfunc_subdrv_post_reset(struct msg_op_data *op) +{ + int ret; + unsigned int drv_type = op->op_data[0]; + struct subsys_drv_inst *drv_inst; + + /* TODO: should be locked, to avoid cb was reset */ + drv_inst = &g_conninfra_ctx.drv_inst[drv_type]; + if (/*drv_inst->drv_status == DRV_ST_POWER_ON &&*/ + drv_inst->ops_cb.rst_cb.post_whole_chip_rst) { + ret = drv_inst->ops_cb.rst_cb.post_whole_chip_rst(); + if (ret) + pr_warn("[%s] fail [%d]\n", __func__, ret); + } + + atomic_add(0x1 << drv_type, &g_conninfra_ctx.rst_state); + up(&g_conninfra_ctx.rst_sema); + return 0; +} + +/* + * CONNINFRA API + */ +int conninfra_core_power_on(enum consys_drv_type type) +{ + int ret = 0; + struct conninfra_ctx *infra_ctx = &g_conninfra_ctx; + + ret = msg_thread_send_wait_1(&infra_ctx->msg_ctx, + CONNINFRA_OPID_PWR_ON, 0, type); + if (ret) { + pr_err("[%s] fail, ret = %d\n", __func__, ret); + return -1; + } + return 0; +} + +int conninfra_core_power_off(enum consys_drv_type type) +{ + int ret = 0; + struct conninfra_ctx *infra_ctx = &g_conninfra_ctx; + + ret = msg_thread_send_wait_1(&infra_ctx->msg_ctx, + CONNINFRA_OPID_PWR_OFF, 0, type); + if (ret) { + pr_err("[%s] send msg fail, ret = %d\n", __func__, ret); + return -1; + } + return 0; +} + +int conninfra_core_reg_readable(void) +{ + int ret = 0, rst_status; + unsigned long flag; + struct conninfra_ctx *infra_ctx = &g_conninfra_ctx; + + + /* check if in reseting, can not read */ + spin_lock_irqsave(&g_conninfra_ctx.rst_lock, flag); + rst_status = g_conninfra_ctx.rst_status; + spin_unlock_irqrestore(&g_conninfra_ctx.rst_lock, flag); + + if (rst_status >= CHIP_RST_RESET && + rst_status < CHIP_RST_POST_CB) + return 0; + + ret = osal_lock_sleepable_lock(&infra_ctx->core_lock); + if (ret) { + pr_err("core_lock fail!!\n"); + return 0; + } + + if (infra_ctx->infra_drv_status == DRV_STS_POWER_ON) + ret = consys_hw_reg_readable(); + osal_unlock_sleepable_lock(&infra_ctx->core_lock); + + return ret; +} + +int conninfra_core_reg_readable_no_lock(void) +{ + int rst_status; + unsigned long flag; + + /* check if in reseting, can not read */ + spin_lock_irqsave(&g_conninfra_ctx.rst_lock, flag); + rst_status = g_conninfra_ctx.rst_status; + spin_unlock_irqrestore(&g_conninfra_ctx.rst_lock, flag); + + if (rst_status >= CHIP_RST_RESET && + rst_status < CHIP_RST_POST_CB) + return 0; + + return consys_hw_reg_readable(); +} + +int conninfra_core_is_bus_hang(void) +{ + int ret = 0; + struct conninfra_ctx *infra_ctx = &g_conninfra_ctx; + + ret = osal_lock_sleepable_lock(&infra_ctx->core_lock); + if (ret) { + pr_err("core_lock fail!!\n"); + return 0; + } + + if (infra_ctx->infra_drv_status == DRV_STS_POWER_ON) + ret = consys_hw_is_bus_hang(); + osal_unlock_sleepable_lock(&infra_ctx->core_lock); + + return ret; + +} + +int conninfra_core_is_consys_reg(phys_addr_t addr) +{ + return consys_hw_is_connsys_reg(addr); +} + +int conninfra_core_reg_read(unsigned long address, unsigned int *value, unsigned int mask) +{ + int ret = 0; + struct conninfra_ctx *infra_ctx = &g_conninfra_ctx; + + ret = osal_lock_sleepable_lock(&infra_ctx->core_lock); + if (ret) { + pr_err("core_lock fail!!\n"); + return 0; + } + + if (infra_ctx->infra_drv_status == DRV_STS_POWER_ON) { + if (consys_reg_mng_is_host_csr(address)) + ret = consys_reg_mng_reg_read(address, value, mask); + else if (consys_hw_reg_readable()) + ret = consys_reg_mng_reg_read(address, value, mask); + else + pr_info("CR (%lx) is not readable\n", address); + } else + pr_info("CR (%lx) cannot read. conninfra is off\n", address); + + osal_unlock_sleepable_lock(&infra_ctx->core_lock); + return ret; +} + +int conninfra_core_reg_write(unsigned long address, unsigned int value, unsigned int mask) +{ + int ret = 0; + struct conninfra_ctx *infra_ctx = &g_conninfra_ctx; + + ret = osal_lock_sleepable_lock(&infra_ctx->core_lock); + if (ret) { + pr_err("core_lock fail!!\n"); + return 0; + } + + if (infra_ctx->infra_drv_status == DRV_STS_POWER_ON) { + if (consys_reg_mng_is_host_csr(address)) + ret = consys_reg_mng_reg_write(address, value, mask); + else if (consys_hw_reg_readable()) + ret = consys_reg_mng_reg_write(address, value, mask); + else + pr_info("CR (%p) is not readable\n", (void*)address); + } else + pr_info("CR (%p) cannot read. conninfra is off\n", (void*)address); + + osal_unlock_sleepable_lock(&infra_ctx->core_lock); + return ret; + +} + +int conninfra_core_lock_rst(void) +{ + struct conninfra_ctx *infra_ctx = &g_conninfra_ctx; + int ret = 0; + unsigned long flag; + + spin_lock_irqsave(&infra_ctx->rst_lock, flag); + + ret = infra_ctx->rst_status; + if (infra_ctx->rst_status > CHIP_RST_NONE && + infra_ctx->rst_status < CHIP_RST_DONE) { + /* do nothing */ + } else { + infra_ctx->rst_status = CHIP_RST_START; + } + spin_unlock_irqrestore(&infra_ctx->rst_lock, flag); + + pr_info("[%s] ret=[%d]\n", __func__, ret); + return ret; +} + +int conninfra_core_unlock_rst(void) +{ + unsigned long flag; + struct conninfra_ctx *infra_ctx = &g_conninfra_ctx; + + spin_lock_irqsave(&infra_ctx->rst_lock, flag); + infra_ctx->rst_status = CHIP_RST_NONE; + spin_unlock_irqrestore(&infra_ctx->rst_lock, flag); + return 0; +} + +int conninfra_core_trg_chip_rst(enum consys_drv_type drv, char *reason) +{ + int ret = 0; + struct conninfra_ctx *infra_ctx = &g_conninfra_ctx; + + infra_ctx->trg_drv = drv; + snprintf(infra_ctx->trg_reason, CHIP_RST_REASON_MAX_LEN, "%s", reason); + ret = msg_thread_send_1(&infra_ctx->cb_ctx, + CONNINFRA_CB_OPID_CHIP_RST, drv); + if (ret) { + pr_err("send msg fail, ret = %d\n", ret); + return -1; + } + pr_info("trg_reset DONE!\n"); + return 0; +} + +static inline char* conninfra_core_spi_subsys_string(enum sys_spi_subsystem subsystem) +{ + static char* subsys_name[] = { + "SYS_SPI_WF1", + "SYS_SPI_WF", + "SYS_SPI_BT", + "SYS_SPI_FM", + "SYS_SPI_GPS", + "SYS_SPI_TOP", + "SYS_SPI_WF2", + "SYS_SPI_WF3", + "SYS_SPI_2ND_ADIE_WF1", + "SYS_SPI_2ND_ADIE_WF", + "SYS_SPI_2ND_ADIE_BT", + "SYS_SPI_2ND_ADIE_FM", + "SYS_SPI_2ND_ADIE_GPS", + "SYS_SPI_2ND_ADIE_TOP", + "SYS_SPI_2ND_ADIE_WF2", + "SYS_SPI_2ND_ADIE_WF3", + "SYS_SPI_MAX" + }; + return subsys_name[subsystem]; +} + +int conninfra_core_spi_read(enum sys_spi_subsystem subsystem, unsigned int addr, unsigned int *data) +{ + int ret = 0; + struct conninfra_ctx *infra_ctx = &g_conninfra_ctx; + size_t data_ptr = (size_t)data; + + ret = msg_thread_send_wait_3(&infra_ctx->msg_ctx, CONNINFRA_OPID_RFSPI_READ, 0, + subsystem, addr, data_ptr); + if (ret) { + pr_err("failed (ret = %d). subsystem=%s addr=%x\n", + ret, conninfra_core_spi_subsys_string(subsystem), addr); + return CONNINFRA_SPI_OP_FAIL; + } + return 0; +} + +int conninfra_core_spi_write(enum sys_spi_subsystem subsystem, unsigned int addr, unsigned int data) +{ + int ret; + ret = msg_thread_send_wait_3(&(g_conninfra_ctx.msg_ctx), CONNINFRA_OPID_RFSPI_WRITE, 0, + subsystem, addr, data); + if (ret) { + pr_err("failed (ret = %d). subsystem=%s addr=0x%x data=%d\n", + ret, conninfra_core_spi_subsys_string(subsystem), addr, data); + return CONNINFRA_SPI_OP_FAIL; + } + return 0; +} + +int conninfra_core_adie_top_ck_en_on(enum consys_drv_type type) +{ + int ret = 0; + struct conninfra_ctx *infra_ctx = &g_conninfra_ctx; + + ret = msg_thread_send_wait_1(&infra_ctx->msg_ctx, + CONNINFRA_OPID_ADIE_TOP_CK_EN_ON, 0, type); + if (ret) { + pr_err("fail, ret = %d\n", ret); + return -1; + } + return 0; +} + +int conninfra_core_adie_top_ck_en_off(enum consys_drv_type type) +{ + int ret = 0; + struct conninfra_ctx *infra_ctx = &g_conninfra_ctx; + + ret = msg_thread_send_wait_1(&infra_ctx->msg_ctx, + CONNINFRA_OPID_ADIE_TOP_CK_EN_OFF, 0, type); + if (ret) { + pr_err("fail, ret = %d\n", ret); + return -1; + } + return 0; +} + +int conninfra_core_force_conninfra_wakeup(void) +{ + int ret = 0; + struct conninfra_ctx *infra_ctx = &g_conninfra_ctx; + + /* if in conninfra_cored thread */ + if (current == infra_ctx->msg_ctx.thread.pThread) + return opfunc_force_conninfra_wakeup(NULL); + + ret = msg_thread_send_wait(&infra_ctx->msg_ctx, + CONNINFRA_OPID_FORCE_CONNINFRA_WAKUP, 0); + if (ret) { + pr_err("fail, ret = %d\n", ret); + return -1; + } + return 0; +} + +int conninfra_core_force_conninfra_sleep(void) +{ + int ret = 0; + struct conninfra_ctx *infra_ctx = &g_conninfra_ctx; + + /* if in conninfra_cored thread */ + if (current == infra_ctx->msg_ctx.thread.pThread) + return opfunc_force_conninfra_sleep(NULL); + + ret = msg_thread_send_wait(&infra_ctx->msg_ctx, + CONNINFRA_OPID_FORCE_CONNINFRA_SLEEP, 0); + if (ret) { + pr_err("fail, ret = %d\n", ret); + return -1; + } + return 0; +} + +int conninfra_core_spi_clock_switch(enum connsys_spi_speed_type type) +{ + int ret = 0; + struct conninfra_ctx *infra_ctx = &g_conninfra_ctx; + + ret = msg_thread_send_wait_1(&infra_ctx->msg_ctx, + CONNINFRA_OPID_SPI_CLOCK_SWITCH, 0, type); + if (ret) { + pr_err("fail, ret = %d\n", ret); + return -1; + } + return 0; +} + +int conninfra_core_subsys_ops_reg(enum consys_drv_type type, + struct sub_drv_ops_cb *cb) +{ + unsigned long flag; + struct subsys_drv_inst *drv_inst; + int ret = 0; + + spin_lock_irqsave(&g_conninfra_ctx.infra_lock, flag); + drv_inst = &g_conninfra_ctx.drv_inst[type]; + memcpy(&g_conninfra_ctx.drv_inst[type].ops_cb, cb, sizeof(struct sub_drv_ops_cb)); + spin_unlock_irqrestore(&g_conninfra_ctx.infra_lock, flag); + + pr_info("[pre_cal] type=[%s] cb rst=[%p][%p]\n", + drv_name[type], cb->rst_cb.pre_whole_chip_rst, cb->rst_cb.post_whole_chip_rst); + + return ret; +} + +int conninfra_core_subsys_ops_unreg(enum consys_drv_type type) +{ + unsigned long flag; + + spin_lock_irqsave(&g_conninfra_ctx.infra_lock, flag); + memset(&g_conninfra_ctx.drv_inst[type].ops_cb, 0, + sizeof(struct sub_drv_ops_cb)); + spin_unlock_irqrestore(&g_conninfra_ctx.infra_lock, flag); + + return 0; +} + +static void _conninfra_core_update_rst_status(enum chip_rst_status status) +{ + unsigned long flag; + + spin_lock_irqsave(&g_conninfra_ctx.rst_lock, flag); + g_conninfra_ctx.rst_status = status; + spin_unlock_irqrestore(&g_conninfra_ctx.rst_lock, flag); +} + + +int conninfra_core_is_rst_locking(void) +{ + unsigned long flag; + int ret = 0; + + spin_lock_irqsave(&g_conninfra_ctx.rst_lock, flag); + + if (g_conninfra_ctx.rst_status > CHIP_RST_NONE && + g_conninfra_ctx.rst_status < CHIP_RST_POST_CB) + ret = 1; + spin_unlock_irqrestore(&g_conninfra_ctx.rst_lock, flag); + return ret; +} + +int conninfra_core_dump_power_state(void) +{ + int ret = 0; + struct conninfra_ctx *infra_ctx = &g_conninfra_ctx; + + ret = msg_thread_send(&infra_ctx->msg_ctx, + CONNINFRA_OPID_DUMP_POWER_STATE); + if (ret) { + pr_err("fail, ret = %d\n", ret); + return -1; + } + + return 0; + +} + +int conninfra_core_pmic_event_cb(unsigned int id, unsigned int event) +{ + struct conninfra_ctx *infra_ctx = &g_conninfra_ctx; + int ret; + + if (conninfra_core_is_rst_locking()) { + return 0; + } + + ret = osal_lock_sleepable_lock(&infra_ctx->core_lock); + if (ret) { + pr_err("core_lock fail!!\n"); + return 0; + } + + if (infra_ctx->infra_drv_status == DRV_STS_POWER_ON) + consys_hw_pmic_event_cb(id, event); + + osal_unlock_sleepable_lock(&infra_ctx->core_lock); + + return 0; +} + +int conninfra_core_debug_dump(void) +{ + struct conninfra_ctx *infra_ctx = &g_conninfra_ctx; + int ret = -1; + unsigned int i; + + ret = osal_lock_sleepable_lock(&infra_ctx->core_lock); + if (ret) { + pr_err("core_lock fail, ret=%d\n", ret); + return -1; + } + + msg_thread_dump(&infra_ctx->msg_ctx); + msg_thread_dump(&infra_ctx->cb_ctx); + for (i = 0; i < CONNDRV_TYPE_MAX; i++) { + msg_thread_dump(&(infra_ctx->drv_inst[i].msg_ctx)); + } + + osal_unlock_sleepable_lock(&infra_ctx->core_lock); + + return ret; +} + +int conninfra_core_init(void) +{ + int ret = 0, i; + struct conninfra_ctx *infra_ctx = &g_conninfra_ctx; + + osal_memset(&g_conninfra_ctx, 0, sizeof(g_conninfra_ctx)); + + reset_chip_rst_trg_data(); + + spin_lock_init(&infra_ctx->infra_lock); + osal_sleepable_lock_init(&infra_ctx->core_lock); + spin_lock_init(&infra_ctx->rst_lock); + + + ret = msg_thread_init(&infra_ctx->msg_ctx, "conninfra_cored", + conninfra_core_opfunc, CONNINFRA_OPID_MAX); + if (ret) { + pr_err("msg_thread init fail(%d)\n", ret); + return -1; + } + + ret = msg_thread_init(&infra_ctx->cb_ctx, "conninfra_cb", + conninfra_core_cb_opfunc, CONNINFRA_CB_OPID_MAX); + if (ret) { + pr_err("callback msg thread init fail(%d)\n", ret); + return -1; + } + + /* init subsys drv state */ + for (i = 0; i < CONNDRV_TYPE_MAX; i++) { + ret += msg_thread_init(&infra_ctx->drv_inst[i].msg_ctx, + drv_thread_name[i], infra_subdrv_opfunc, + INFRA_SUBDRV_OPID_MAX); + } + + if (ret) { + pr_err("subsys callback thread init fail.\n"); + return -1; + } + + return ret; +} + +int conninfra_core_deinit(void) +{ + int ret, i; + struct conninfra_ctx *infra_ctx = &g_conninfra_ctx; + + for (i = 0; i < CONNDRV_TYPE_MAX; i++) { + ret = msg_thread_deinit(&infra_ctx->drv_inst[i].msg_ctx); + if (ret) + pr_warn("subdrv [%d] msg_thread deinit fail (%d)\n", + i, ret); + } + + ret = msg_thread_deinit(&infra_ctx->msg_ctx); + if (ret) { + pr_err("msg_thread_deinit fail(%d)\n", ret); + return -1; + } + + osal_sleepable_lock_deinit(&infra_ctx->core_lock); + + return 0; +} + diff --git a/package/mtk/drivers/conninfra/src/core/include/conninfra_core.h b/package/mtk/drivers/conninfra/src/core/include/conninfra_core.h new file mode 100644 index 0000000000..555c8de8a2 --- /dev/null +++ b/package/mtk/drivers/conninfra/src/core/include/conninfra_core.h @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2016 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 http://www.gnu.org/licenses/gpl-2.0.html for more details. + */ +/*! \file +* \brief Declaration of library functions +* +* Any definitions in this file will be shared among GLUE Layer and internal Driver Stack. +*/ + +#ifndef _CONNINFRA_CORE_H_ +#define _CONNINFRA_CORE_H_ + +#include +#include +#include +#include + +#include "osal.h" +#include "msg_thread.h" +#include "conninfra.h" + +/******************************************************************************* +* C O M P I L E R F L A G S +******************************************************************************** +*/ + +/******************************************************************************* +* M A C R O S +******************************************************************************** +*/ + +#define CHIP_RST_REASON_MAX_LEN 128 + +/******************************************************************************* +* E X T E R N A L R E F E R E N C E S +******************************************************************************** +*/ + +/******************************************************************************* +* C O N S T A N T S +******************************************************************************** +*/ + +/******************************************************************************* +* D A T A T Y P E S +******************************************************************************** +*/ +typedef enum _ENUM_DRV_STS_ { + DRV_STS_POWER_OFF = 0, /* initial state */ + DRV_STS_POWER_ON = 1, /* powered on */ + DRV_STS_MAX +} ENUM_DRV_STS, *P_ENUM_DRV_STS; + +enum chip_rst_status { + CHIP_RST_NONE = 0, + CHIP_RST_START = 1, + CHIP_RST_PRE_CB = 2, + CHIP_RST_RESET = 3, + CHIP_RST_POST_CB = 4, + CHIP_RST_DONE = 5 +}; + +struct subsys_drv_inst { + ENUM_DRV_STS drv_status; /* Controlled driver status */ + unsigned int rst_state; + struct sub_drv_ops_cb ops_cb; + struct msg_thread_ctx msg_ctx; +}; + +/* + * state of conninfra + * + */ +struct conninfra_ctx { + ENUM_DRV_STS infra_drv_status; + + struct subsys_drv_inst drv_inst[CONNDRV_TYPE_MAX]; + /*struct spinlock infra_lock;*/ + spinlock_t infra_lock; + + OSAL_SLEEPABLE_LOCK core_lock; + + /* chip reset */ + enum chip_rst_status rst_status; + spinlock_t rst_lock; + + struct semaphore rst_sema; + atomic_t rst_state; + enum consys_drv_type trg_drv; + char trg_reason[CHIP_RST_REASON_MAX_LEN]; + + struct msg_thread_ctx msg_ctx; + struct msg_thread_ctx cb_ctx; + + unsigned int hw_ver; + unsigned int fw_ver; + unsigned int ip_ver; +}; + +//typedef enum _ENUM_CONNINFRA_CORE_OPID_T { +typedef enum { + CONNINFRA_OPID_PWR_ON = 0, + CONNINFRA_OPID_PWR_OFF = 1, + CONNINFRA_OPID_RFSPI_READ = 2, + CONNINFRA_OPID_RFSPI_WRITE = 3, + CONNINFRA_OPID_ADIE_TOP_CK_EN_ON = 4, + CONNINFRA_OPID_ADIE_TOP_CK_EN_OFF = 5, + CONNINFRA_OPID_SPI_CLOCK_SWITCH = 6, + CONNINFRA_OPID_FORCE_CONNINFRA_WAKUP = 7, + CONNINFRA_OPID_FORCE_CONNINFRA_SLEEP = 8, + CONNINFRA_OPID_DUMP_POWER_STATE = 9, + CONNINFRA_OPID_MAX +} conninfra_core_opid; + +/* For the operation which may callback subsys driver */ +typedef enum { + CONNINFRA_CB_OPID_CHIP_RST = 0, + CONNINFRA_CB_OPID_MAX +} conninfra_core_cb_opid; + +/******************************************************************************* +* P U B L I C D A T A +******************************************************************************** +*/ + +/******************************************************************************* +* P R I V A T E D A T A +******************************************************************************** +*/ + +/******************************************************************************* +* F U N C T I O N D E C L A R A T I O N S +******************************************************************************** +*/ + +extern int conninfra_core_init(void); +extern int conninfra_core_deinit(void); + +int conninfra_core_power_on(enum consys_drv_type type); +int conninfra_core_power_off(enum consys_drv_type type); + +int conninfra_core_lock_rst(void); +int conninfra_core_unlock_rst(void); +int conninfra_core_trg_chip_rst(enum consys_drv_type drv, char *reason); + +int conninfra_core_subsys_ops_reg(enum consys_drv_type type, struct sub_drv_ops_cb *cb); +int conninfra_core_subsys_ops_unreg(enum consys_drv_type type); + +/* reg control */ +/* NOTE: NOT thread-safe + * return value + * 1 : Yes, 0: NO + */ +int conninfra_core_reg_readable(void); +int conninfra_core_reg_readable_no_lock(void); +int conninfra_core_is_bus_hang(void); + +int conninfra_core_is_consys_reg(phys_addr_t addr); +int conninfra_core_reg_read(unsigned long address, unsigned int *value, unsigned int mask); +int conninfra_core_reg_write(unsigned long address, unsigned int value, unsigned int mask); + +int conninfra_core_is_rst_locking(void); + +int conninfra_core_spi_read(enum sys_spi_subsystem subsystem, unsigned int addr, unsigned int *data); +int conninfra_core_spi_write(enum sys_spi_subsystem subsystem, unsigned int addr, unsigned int data); + +int conninfra_core_adie_top_ck_en_on(enum consys_drv_type type); +int conninfra_core_adie_top_ck_en_off(enum consys_drv_type type); + +int conninfra_core_force_conninfra_wakeup(void); +int conninfra_core_force_conninfra_sleep(void); + +int conninfra_core_spi_clock_switch(enum connsys_spi_speed_type type); + +int conninfra_core_dump_power_state(void); +int conninfra_core_pmic_event_cb(unsigned int, unsigned int); +int conninfra_core_debug_dump(void); + +/******************************************************************************* +* F U N C T I O N S +******************************************************************************** +*/ + +#endif /* _CONNINFRA_CORE_H_ */ diff --git a/package/mtk/drivers/conninfra/src/include/conninfra.h b/package/mtk/drivers/conninfra/src/include/conninfra.h new file mode 100644 index 0000000000..b8577bdec3 --- /dev/null +++ b/package/mtk/drivers/conninfra/src/include/conninfra.h @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2016 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 http://www.gnu.org/licenses/gpl-2.0.html for more details. + */ + +/*! \file +* \brief Declaration of library functions +* +* Any definitions in this file will be shared among GLUE Layer and internal Driver Stack. +*/ + +#ifndef _CONNINFRA_H_ +#define _CONNINFRA_H_ + + +/******************************************************************************* +* C O M P I L E R F L A G S +******************************************************************************** +*/ + +/******************************************************************************* +* M A C R O S +******************************************************************************** +*/ +#define AIDE_NUM_MAX 2 +/******************************************************************************* +* C O N S T A N T S +******************************************************************************** +*/ + +/******************************************************************************* +* D A T A T Y P E S +******************************************************************************** +*/ +enum consys_drv_type { + CONNDRV_TYPE_BT = 0, + CONNDRV_TYPE_FM = 1, + CONNDRV_TYPE_GPS = 2, + CONNDRV_TYPE_WIFI = 3, + CONNDRV_TYPE_CONNINFRA = 4, + CONNDRV_TYPE_MAX +}; + +/* HW-specific, need sync with FW. DO NOT MODIFY */ +enum sys_spi_subsystem +{ + SYS_SPI_WF1 = 0x00, + SYS_SPI_WF = 0x01, + SYS_SPI_BT = 0x02, + SYS_SPI_FM = 0x03, + SYS_SPI_GPS = 0x04, + SYS_SPI_TOP = 0x05, + SYS_SPI_WF2 = 0x06, + SYS_SPI_WF3 = 0x07, + SYS_SPI_2ND_ADIE_WF1 = 0x10, + SYS_SPI_2ND_ADIE_WF = 0x11, + SYS_SPI_2ND_ADIE_BT = 0x12, + SYS_SPI_2ND_ADIE_FM = 0x13, + SYS_SPI_2ND_ADIE_GPS = 0x14, + SYS_SPI_2ND_ADIE_TOP = 0x15, + SYS_SPI_2ND_ADIE_WF2 = 0x16, + SYS_SPI_2ND_ADIE_WF3 = 0x17, + SYS_SPI_MAX +}; + +enum connsys_spi_speed_type { + CONNSYS_SPI_SPEED_26M, + CONNSYS_SPI_SPEED_64M, + CONNSYS_SPI_SPEED_MAX +}; + +/* Conninfra driver allocate EMI for FW and WFDAM + * (FW includes: BT, WIFI and their MCU) + * +-----------+ + + * | | | + * | FW | | + * | | | + * +-----------+ v + * | | + * | | FW_WFDMA + * | | ^ + * | WFDMA | | + * | | | + * | | | + * +-----------+ + + * + * MCIF region is provided by MD + * +-----------+ + * | | + * | | + * | MCIF | + * | | + * +-----------+ + */ +enum connsys_emi_type +{ + CONNSYS_EMI_FW = 0, + CONNSYS_EMI_MAX, +}; + +#define CONNINFRA_SPI_OP_FAIL 0x1 + +#define CONNINFRA_CB_RET_CAL_PASS_POWER_OFF 0x0 +#define CONNINFRA_CB_RET_CAL_PASS_POWER_ON 0x2 +#define CONNINFRA_CB_RET_CAL_FAIL_POWER_OFF 0x1 +#define CONNINFRA_CB_RET_CAL_FAIL_POWER_ON 0x3 + +#define CONNINFRA_BUS_CLOCK_WPLL 0x1 +#define CONNINFRA_BUS_CLOCK_ALL (CONNINFRA_BUS_CLOCK_WPLL) + +/* bus hang error define */ +#define CONNINFRA_INFRA_BUS_HANG 0x1 +#define CONNINFRA_AP2CONN_RX_SLP_PROT_ERR 0x2 +#define CONNINFRA_AP2CONN_TX_SLP_PROT_ERR 0x4 +#define CONNINFRA_AP2CONN_CLK_ERR 0x8 +#define CONNINFRA_INFRA_BUS_HANG_IRQ 0x10 + +#define CONNINFRA_ERR_RST_ONGOING -0x7788 +#define CONNINFRA_ERR_WAKEUP_FAIL -0x5566 +/******************************************************************************* +* E X T E R N A L R E F E R E N C E S +******************************************************************************** +*/ + +/******************************************************************************* +* P U B L I C D A T A +******************************************************************************** +*/ + +/******************************************************************************* +* P R I V A T E D A T A +******************************************************************************** +*/ + +/******************************************************************************* +* F U N C T I O N D E C L A R A T I O N S +******************************************************************************** +*/ + +/* SPI clock switch */ +int conninfra_spi_clock_switch(enum connsys_spi_speed_type type); + +/* A-die top_ck_en control, only for MT6885 */ +int conninfra_adie_top_ck_en_on(enum consys_drv_type type); +int conninfra_adie_top_ck_en_off(enum consys_drv_type type); + +/* RFSPI */ +int conninfra_spi_read(enum sys_spi_subsystem subsystem, unsigned int addr, unsigned int *data); +int conninfra_spi_write(enum sys_spi_subsystem subsystem, unsigned int addr, unsigned int data); + +/* EMI */ +void conninfra_get_emi_phy_addr(enum connsys_emi_type type, phys_addr_t* base, unsigned int *size); + +/* power on/off */ +int conninfra_pwr_on(enum consys_drv_type drv_type); +int conninfra_pwr_off(enum consys_drv_type drv_type); + +/* To setup config relative data, ex: debug flag */ +void conninfra_config_setup(void); + +/* + * 0 : NO hang + * > 0 : HANG!! + * CONNINFRA_ERR_RST_ONGOING: whole chip reset is ongoing + */ +int conninfra_is_bus_hang(void); + +/* chip reset +* return: +* <0: error +* =0: triggered +* =1: ongoing +*/ +int conninfra_trigger_whole_chip_rst(enum consys_drv_type drv, char *reason); + +int conninfra_debug_dump(void); + +struct whole_chip_rst_cb { + int (*pre_whole_chip_rst)(enum consys_drv_type drv, char *reason); + int (*post_whole_chip_rst)(void); +}; + +/* driver state query */ + +/* VCN control */ + +/* Thermal */ + +/* Config */ + +/* semaphore */ + +/* calibration */ + +struct sub_drv_ops_cb { + /* chip reset */ + struct whole_chip_rst_cb rst_cb; +}; + +int conninfra_sub_drv_ops_register(enum consys_drv_type drv_type, struct sub_drv_ops_cb *cb); +int conninfra_sub_drv_ops_unregister(enum consys_drv_type drv_type); + +/******************************************************************************* +* F U N C T I O N S +******************************************************************************** +*/ + +#endif /* _CONNINFRA_H_ */ diff --git a/package/mtk/drivers/conninfra/src/platform/consys_hw.c b/package/mtk/drivers/conninfra/src/platform/consys_hw.c new file mode 100644 index 0000000000..7f8e02e223 --- /dev/null +++ b/package/mtk/drivers/conninfra/src/platform/consys_hw.c @@ -0,0 +1,663 @@ +/* + * Copyright (C) 2016 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 http://www.gnu.org/licenses/gpl-2.0.html for more details. + */ +/*! \file +* \brief Declaration of library functions +* +* Any definitions in this file will be shared among GLUE Layer and internal Driver Stack. +*/ + +#define pr_fmt(fmt) KBUILD_MODNAME "@(%s:%d) " fmt, __func__, __LINE__ + + +#include +#include +#include + +#include "osal.h" +#include "consys_hw.h" +#include "emi_mng.h" +#include "pmic_mng.h" +#include "consys_reg_mng.h" + +/******************************************************************************* +* C O M P I L E R F L A G S +******************************************************************************** +*/ + +/******************************************************************************* +* M A C R O S +******************************************************************************** +*/ + + +/******************************************************************************* +* E X T E R N A L R E F E R E N C E S +******************************************************************************** +*/ + +/******************************************************************************* +* C O N S T A N T S +******************************************************************************** +*/ + +/******************************************************************************* +* D A T A T Y P E S +******************************************************************************** +*/ + +/******************************************************************************* +* F U N C T I O N D E C L A R A T I O N S +******************************************************************************** +*/ + +static int mtk_conninfra_probe(struct platform_device *pdev); +static int mtk_conninfra_remove(struct platform_device *pdev); +static int mtk_conninfra_suspend(struct platform_device *pdev, pm_message_t state); +static int mtk_conninfra_resume(struct platform_device *pdev); + +static int consys_hw_init(struct platform_device *pdev); +static int consys_hw_deinit(void); +static int _consys_hw_conninfra_wakeup(void); +static int _consys_hw_conninfra_sleep(void); + +/******************************************************************************* +* P U B L I C D A T A +******************************************************************************** +*/ + +extern const struct of_device_id apconninfra_of_ids[]; + +static struct platform_driver mtk_conninfra_dev_drv = { + .probe = mtk_conninfra_probe, + .remove = mtk_conninfra_remove, + .suspend = mtk_conninfra_suspend, + .resume = mtk_conninfra_resume, + .driver = { + .name = "mtk_conninfra", + .owner = THIS_MODULE, + .of_match_table = apconninfra_of_ids, + }, +}; + + +struct consys_hw_env conn_hw_env[AIDE_NUM_MAX]; + +const struct consys_hw_ops_struct *consys_hw_ops; +struct platform_device *g_pdev; + +int g_conninfra_wakeup_ref_cnt; + +struct work_struct ap_resume_work; + +struct conninfra_dev_cb *g_conninfra_dev_cb; +const struct conninfra_plat_data *g_conninfra_plat_data = NULL; + +/******************************************************************************* +* P R I V A T E D A T A +******************************************************************************** +*/ + +/******************************************************************************* +* F U N C T I O N S +******************************************************************************** +*/ +struct platform_device *get_consys_device(void) +{ + return g_pdev; +} + +int consys_hw_get_clock_schematic(void) +{ + if (consys_hw_ops->consys_plt_co_clock_type) + return consys_hw_ops->consys_plt_co_clock_type(); + else + pr_err("consys_hw_ops->consys_co_clock_type not supported\n"); + + return -1; +} + +unsigned int consys_hw_chipid_get(void) +{ + if (g_conninfra_plat_data && g_conninfra_plat_data->chip_id) + return g_conninfra_plat_data->chip_id; + else if (consys_hw_ops->consys_plt_soc_chipid_get) + return consys_hw_ops->consys_plt_soc_chipid_get(); + else + pr_err("consys_plt_soc_chipid_get not supported\n"); + + return 0; +} + +unsigned int consys_hw_get_hw_ver(void) +{ + if (consys_hw_ops->consys_plt_get_hw_ver) + return consys_hw_ops->consys_plt_get_hw_ver(); + return 0; +} + + +int consys_hw_reg_readable(void) +{ + return consys_reg_mng_reg_readable(); +} + +int consys_hw_is_connsys_reg(phys_addr_t addr) +{ + return consys_reg_mng_is_connsys_reg(addr); +} + +int consys_hw_is_bus_hang(void) +{ + return consys_reg_mng_is_bus_hang(); +} + +int consys_hw_dump_bus_status(void) +{ + return consys_reg_mng_dump_bus_status(); +} + +int consys_hw_dump_cpupcr(enum conn_dump_cpupcr_type dump_type, int times, unsigned long interval_us) +{ + return consys_reg_mng_dump_cpupcr(dump_type, times, interval_us); +} + +int consys_hw_pwr_on(unsigned int curr_status, unsigned int on_radio) +{ + //unsigned int next_status = (curr_status | (0x1 << on_radio)); + + /* first power on */ + if (curr_status == 0) { + /* POS PART 0: + * Set PMIC to turn on the power that AFE WBG circuit in D-die, + * OSC or crystal component, and A-die need. + */ + if (consys_hw_ops->consys_plt_xtal_ctrl_fast_mode) + consys_hw_ops->consys_plt_xtal_ctrl_fast_mode(); + + if (consys_hw_ops->consys_plt_connsys_sw_reset_ctrl) + consys_hw_ops->consys_plt_connsys_sw_reset_ctrl(false); + + /* POS PART 1: + * 1. Pinmux setting + * 2. Turn on MTCMOS + * 3. Enable AHB bus + */ + if (consys_hw_ops->consys_plt_set_if_pinmux) + consys_hw_ops->consys_plt_set_if_pinmux(true); + + udelay(500); + + if (consys_hw_ops->consys_plt_tx_rx_bus_slp_prot_ctrl) + consys_hw_ops->consys_plt_tx_rx_bus_slp_prot_ctrl(true); + + if (consys_hw_ops->consys_plt_polling_consys_chipid) + consys_hw_ops->consys_plt_polling_consys_chipid(); + + /* POS PART 2: + * 1. Set connsys EMI mapping + * 2. d_die_cfg + * 3. spi_master_cfg + * 4. a_die_cfg + * 5. afe_wbg_cal + * 6. patch default value + * 7. CONN_INFRA low power setting (srcclken wait time, mtcmos HW ctl...) + */ + if (consys_hw_ops->consys_plt_bus_clock_ctrl) + consys_hw_ops->consys_plt_bus_clock_ctrl(on_radio, CONNINFRA_BUS_CLOCK_ALL); + + emi_mng_set_remapping_reg(); + emi_mng_set_region_protection(); + + if (consys_hw_ops->consys_plt_d_die_cfg) + consys_hw_ops->consys_plt_d_die_cfg(); + + if (consys_hw_ops->consys_plt_conninfra_sysram_hw_ctrl) + consys_hw_ops->consys_plt_conninfra_sysram_hw_ctrl(); + + if (consys_hw_ops->consys_plt_spi_master_cfg) + consys_hw_ops->consys_plt_spi_master_cfg(); + +#ifndef CONFIG_FPGA_EARLY_PORTING + if (consys_hw_ops->consys_plt_adie_type_check) + consys_hw_ops->consys_plt_adie_type_check(); + + if (consys_hw_ops->consys_plt_a_die_cfg) + consys_hw_ops->consys_plt_a_die_cfg(); +#endif + + if (consys_hw_ops->consys_plt_afe_wbg_cal) + consys_hw_ops->consys_plt_afe_wbg_cal(); + + if (consys_hw_ops->consys_plt_subsys_pll_initial) + consys_hw_ops->consys_plt_subsys_pll_initial(); + + if (consys_hw_ops->consys_plt_osc_legacy_mode) + consys_hw_ops->consys_plt_osc_legacy_mode(); + +#ifndef CONFIG_FPGA_EARLY_PORTING + if (consys_hw_ops->consys_plt_top_pwr_ctrl) + consys_hw_ops->consys_plt_top_pwr_ctrl(); +#endif + + if (consys_hw_ops->consys_plt_conn_infra_bus_timeout) + consys_hw_ops->consys_plt_conn_infra_bus_timeout(); + + if (consys_hw_ops->consys_plt_clkgen_wpll_hw_ctrl) + consys_hw_ops->consys_plt_clkgen_wpll_hw_ctrl(); + + /* POS PART 3: + * 1. A-die low power setting + * 2. bgfsys power on(BT/GPS on) + */ + consys_hw_force_conninfra_wakeup(); +#ifndef CONFIG_FPGA_EARLY_PORTING + consys_hw_adie_top_ck_en_on(on_radio); + //consys_hw_adie_top_ck_en_off(on_radio); +#endif + consys_hw_force_conninfra_sleep(); + }else { + switch (on_radio) { + case CONNDRV_TYPE_WIFI: + /* Power on WFSYS PART 0: + * 1. wake up conn_infra + * 2. turn on MTCMOS power switch of "wfsys_top_on" and "wfsys_top_off" circuit in D-die ("wfsys_top_off" is turned on by "wfsys_top_on" automatically) + * 3. enable AHB bus(WF2conn/conn2WF) + * 4. downlad CONNSYS EMI code + * 5. patch default value + */ + if (consys_hw_ops->consys_plt_conninfra_wf_wakeup) + consys_hw_ops->consys_plt_conninfra_wf_wakeup(); + + if (consys_hw_ops->consys_plt_conn_wmcpu_sw_reset) + consys_hw_ops->consys_plt_conn_wmcpu_sw_reset(true); + + if (consys_hw_ops->consys_plt_wf_bus_slp_prot_ctrl) + consys_hw_ops->consys_plt_wf_bus_slp_prot_ctrl(false); + + if (consys_hw_ops->consys_plt_wfsys_top_on_ctrl) + consys_hw_ops->consys_plt_wfsys_top_on_ctrl(true); + + if (consys_hw_ops->consys_plt_wfsys_bus_slp_prot_check) + consys_hw_ops->consys_plt_wfsys_bus_slp_prot_check(true); + + if (consys_hw_ops->consys_plt_wfsys_bus_timeout_ctrl) + consys_hw_ops->consys_plt_wfsys_bus_timeout_ctrl(); + + if (consys_hw_ops->consys_plt_conn_wmcpu_sw_reset) + consys_hw_ops->consys_plt_conn_wmcpu_sw_reset(false); + +#ifndef CONFIG_FPGA_EARLY_PORTING + if (consys_hw_ops->consys_plt_conn_wmcpu_idle_loop_check) + consys_hw_ops->consys_plt_conn_wmcpu_idle_loop_check(); + + if (consys_hw_ops->consys_plt_adie_type_cfg) + consys_hw_ops->consys_plt_adie_type_cfg(); +#endif + /* No sleep requiremenct for rebb AP */ +#if 0 + if (consys_hw_ops->consys_plt_conninfra_wf_sleep) + consys_hw_ops->consys_plt_conninfra_wf_sleep(); +#endif + break; + + default: + pr_err("Not support type now (on_radio = %d)\n", on_radio); + break; + } + } + + return 0; +} + +int consys_hw_pwr_off(unsigned int curr_status, unsigned int off_radio) +{ + //int ret = 0; + unsigned int next_status = curr_status & ~(0x1 << off_radio); + + if (next_status == 0) { + pr_info("Last pwoer off: %d\n", off_radio); + + /* Power off CONNSYS PART 0: + * 1. A-die low power setting + */ + consys_hw_force_conninfra_wakeup(); +#ifndef CONFIG_FPGA_EARLY_PORTING + //consys_hw_adie_top_ck_en_off(off_radio); +#endif + consys_hw_force_conninfra_sleep(); + + /* Power off CONNSYS PART 1: + * 1. disable AXI bus + * 2. turn off MTCMOS power switch of "conn_top_on" and "conn_top_off" circuit in D-die + */ + if (consys_hw_ops->consys_plt_tx_rx_bus_slp_prot_ctrl) + consys_hw_ops->consys_plt_tx_rx_bus_slp_prot_ctrl(false); + + if (consys_hw_ops->consys_plt_connsys_sw_reset_ctrl) + consys_hw_ops->consys_plt_connsys_sw_reset_ctrl(true); + + udelay(1); + } else { + switch (off_radio) { + case CONNDRV_TYPE_WIFI: + /* Power off WFSYS PART 1: + * 1. disable AXI bus(wf2conn/conn2wf) + * 2. turn off MTCMOS power switch of "wf_top_on" and "wf_top_off" circuit in D-die ("wf_top_off" is turned off by "wf_top_on" automatically) + */ + if (consys_hw_ops->consys_plt_conninfra_wf_wakeup) + consys_hw_ops->consys_plt_conninfra_wf_wakeup(); + + if (consys_hw_ops->consys_plt_wf_bus_slp_prot_ctrl) + consys_hw_ops->consys_plt_wf_bus_slp_prot_ctrl(true); + + if (consys_hw_ops->consys_plt_wfsys_bus_slp_prot_check) + consys_hw_ops->consys_plt_wfsys_bus_slp_prot_check(false); + + if (consys_hw_ops->consys_plt_wpll_ctrl) + consys_hw_ops->consys_plt_wpll_ctrl(false); + + if (consys_hw_ops->consys_plt_wfsys_top_on_ctrl) + consys_hw_ops->consys_plt_wfsys_top_on_ctrl(false); + + if (consys_hw_ops->consys_plt_wpll_ctrl) + consys_hw_ops->consys_plt_wpll_ctrl(true); + + consys_hw_adie_top_ck_en_off(off_radio); + + if (consys_hw_ops->consys_plt_conninfra_wf_req_clr) + consys_hw_ops->consys_plt_conninfra_wf_req_clr(); + + if (consys_hw_ops->consys_plt_conninfra_wf_sleep) + consys_hw_ops->consys_plt_conninfra_wf_sleep(); + break; + + default: + consys_hw_force_conninfra_wakeup(); + consys_hw_adie_top_ck_en_off(off_radio); + consys_hw_force_conninfra_sleep(); + break; + } + } + + return 0; +} + +int consys_hw_wifi_power_ctl(unsigned int enable) +{ + return pmic_mng_wifi_power_ctrl(enable); +} + +int consys_hw_bt_power_ctl(unsigned int enable) +{ + return pmic_mng_bt_power_ctrl(enable); +} + +int consys_hw_gps_power_ctl(unsigned int enable) +{ + return pmic_mng_gps_power_ctrl(enable); +} + +int consys_hw_fm_power_ctl(unsigned int enable) +{ + return pmic_mng_fm_power_ctrl(enable); +} + +int consys_hw_dump_power_state(void) +{ + if (consys_hw_ops && consys_hw_ops->consys_plt_power_state) + consys_hw_ops->consys_plt_power_state(); + return 0; +} + +int consys_hw_spi_read(enum sys_spi_subsystem subsystem, unsigned int addr, unsigned int *data) +{ + if (consys_hw_ops->consys_plt_spi_read) + return consys_hw_ops->consys_plt_spi_read(subsystem, addr, data); + return -1; +} + +int consys_hw_spi_write(enum sys_spi_subsystem subsystem, unsigned int addr, unsigned int data) +{ + if (consys_hw_ops->consys_plt_spi_write) + return consys_hw_ops->consys_plt_spi_write(subsystem, addr, data); + return -1; +} + +int consys_hw_adie_top_ck_en_on(enum consys_drv_type type) +{ + if (consys_hw_ops->consys_plt_adie_top_ck_en_on_off_ctrl) + return consys_hw_ops->consys_plt_adie_top_ck_en_on_off_ctrl(type, 1); + return -1; +} + +int consys_hw_adie_top_ck_en_off(enum consys_drv_type type) +{ + if (consys_hw_ops->consys_plt_adie_top_ck_en_on_off_ctrl) + return consys_hw_ops->consys_plt_adie_top_ck_en_on_off_ctrl(type, 0); + return -1; +} + + +static int _consys_hw_conninfra_wakeup(void) +{ + int ref = g_conninfra_wakeup_ref_cnt; + bool wakeup = false, ret; + + if (consys_hw_ops->consys_plt_conninfra_wakeup) { + if (g_conninfra_wakeup_ref_cnt == 0) { + ret = consys_hw_ops->consys_plt_conninfra_wakeup(); + if (ret) { + pr_err("wakeup fail!! ret=[%d]\n", ret); + return ret; + } + wakeup = true; + } + + g_conninfra_wakeup_ref_cnt++; + } + + pr_info("conninfra_wakeup refcnt=[%d]->[%d] %s\n", + ref, g_conninfra_wakeup_ref_cnt, (wakeup ? "wakeup!!" : "")); + + return 0; +} + +static int _consys_hw_conninfra_sleep(void) +{ + int ref = g_conninfra_wakeup_ref_cnt; + bool sleep = false; + + if (consys_hw_ops->consys_plt_conninfra_sleep && + --g_conninfra_wakeup_ref_cnt == 0) { + sleep = true; + consys_hw_ops->consys_plt_conninfra_sleep(); + } + + if (g_conninfra_wakeup_ref_cnt < 0) + g_conninfra_wakeup_ref_cnt = 0; + + pr_info("conninfra_sleep refcnt=[%d]->[%d] %s\n", + ref, g_conninfra_wakeup_ref_cnt, (sleep ? "sleep!!" : "")); + + return 0; +} + +int consys_hw_force_conninfra_wakeup(void) +{ + return _consys_hw_conninfra_wakeup(); +} + +int consys_hw_force_conninfra_sleep(void) +{ + return _consys_hw_conninfra_sleep(); +} + +int consys_hw_spi_clock_switch(enum connsys_spi_speed_type type) +{ + if (consys_hw_ops->consys_plt_spi_clock_switch) + return consys_hw_ops->consys_plt_spi_clock_switch(type); + return -1; +} + +int consys_hw_pmic_event_cb(unsigned int id, unsigned int event) +{ + pmic_mng_event_cb(id, event); + return 0; +} + +int mtk_conninfra_probe(struct platform_device *pdev) +{ + int ret = -1; + + if (pdev) + g_pdev = pdev; + else { + pr_err("pdev is NULL\n"); + return -1; + } + + g_conninfra_plat_data = (const struct conninfra_plat_data*)of_device_get_match_data(&pdev->dev); + if (g_conninfra_plat_data == NULL) { + pr_err("Get platform data fail.\n"); + return -2; + } + + if (consys_hw_ops == NULL) + consys_hw_ops = (const struct consys_hw_ops_struct*)g_conninfra_plat_data->hw_ops; + if (consys_hw_ops == NULL) { + pr_err("Get HW op fail\n"); + return -3; + } + + /* Read device node */ + if (consys_reg_mng_init(pdev, g_conninfra_plat_data) != 0) { + pr_err("consys_plt_read_reg_from_dts fail\n"); + return -4; + } + + if (consys_hw_ops->consys_plt_clk_get_from_dts) { + if (consys_hw_ops->consys_plt_clk_get_from_dts(pdev) != 0) { + pr_err("consys_plt_clk_get_from_dts fail\n"); + return -5; + } + } + + /* HW operation init */ + if (consys_hw_init(pdev) != 0) { + pr_err("consys_hw_init fail\n"); + return -6; + } + + /* emi mng init */ + ret = emi_mng_init(pdev, g_conninfra_plat_data); + if (ret) { + pr_err("emi_mng init fail, %d\n", ret); + return -7; + } + + ret = pmic_mng_init(pdev, g_conninfra_dev_cb, g_conninfra_plat_data); + if (ret) { + pr_err("pmic_mng init fail, %d\n", ret); + return -8; + } + + return ret; +} + +int mtk_conninfra_remove(struct platform_device *pdev) +{ + int ret; + + ret = pmic_mng_deinit(); + pr_info("pmic_mng_deinit ret=%d\n", ret); + + ret = emi_mng_deinit(); + pr_info("emi_mng_deinit ret=%d\n", ret); + + if (consys_hw_ops->consys_plt_clk_detach) + consys_hw_ops->consys_plt_clk_detach(); + else + pr_err("consys_plt_clk_detach is null\n"); + + ret = consys_reg_mng_deinit(); + pr_info("consys_reg_mng_deinit ret=%d\n", ret); + + ret = consys_hw_deinit(); + pr_info("consys_hw_deinit ret=%d\n", ret); + + if (g_pdev) + g_pdev = NULL; + + return 0; +} + +int mtk_conninfra_suspend(struct platform_device *pdev, pm_message_t state) +{ + return 0; +} + +int mtk_conninfra_resume(struct platform_device *pdev) +{ + /* suspend callback is in atomic context, use schedule work to execute STEP */ + + schedule_work(&ap_resume_work); + return 0; +} + +static void consys_hw_ap_resume_handler(struct work_struct *work) +{ + if (g_conninfra_dev_cb && g_conninfra_dev_cb->conninfra_resume_cb) + (*g_conninfra_dev_cb->conninfra_resume_cb)(); +} + +int consys_hw_init(struct platform_device *pdev) +{ + int iRet = 0; + + if (consys_hw_ops->consys_plt_hw_init) + iRet = consys_hw_ops->consys_plt_hw_init(); + + return iRet; +} + +int consys_hw_deinit(void) +{ + return 0; +} + +int mtk_conninfra_drv_init(struct conninfra_dev_cb *dev_cb) +{ + int iRet = 0; + + g_conninfra_dev_cb = dev_cb; + + pr_info("Before platform_driver_register\n"); + + iRet = platform_driver_register(&mtk_conninfra_dev_drv); + if (iRet) + pr_err("Conninfra platform driver registered failed(%d)\n", iRet); + + pr_info("After platform_driver_register\n"); + + INIT_WORK(&ap_resume_work, consys_hw_ap_resume_handler); + + return iRet; +} + +int mtk_conninfra_drv_deinit(void) +{ + platform_driver_unregister(&mtk_conninfra_dev_drv); + g_conninfra_dev_cb = NULL; + return 0; +} + diff --git a/package/mtk/drivers/conninfra/src/platform/consys_hw_plat_data.c b/package/mtk/drivers/conninfra/src/platform/consys_hw_plat_data.c new file mode 100644 index 0000000000..1841b535d1 --- /dev/null +++ b/package/mtk/drivers/conninfra/src/platform/consys_hw_plat_data.c @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019 MediaTek Inc. + */ +/******************************************************************************* +* E X T E R N A L R E F E R E N C E S +******************************************************************************** +*/ +#include + +#include "consys_hw.h" + +/******************************************************************************* +* P U B L I C D A T A +******************************************************************************** +*/ + +/* Platform data */ +#ifdef CONNINFRA_APSOC_MT7986 +extern struct conninfra_plat_data mt7986_plat_data; +const struct of_device_id apconninfra_of_ids[] = { + { + .compatible = "mediatek,mt7986-consys", + .data = (void*)&mt7986_plat_data, + }, + {} +}; +#endif + +#ifdef CONNINFRA_APSOC_MT7981 +extern struct conninfra_plat_data mt7981_plat_data; +const struct of_device_id apconninfra_of_ids[] = { + { + .compatible = "mediatek,mt7981-consys", + .data = (void*)&mt7981_plat_data, + }, + {} +}; +#endif diff --git a/package/mtk/drivers/conninfra/src/platform/consys_reg_mng.c b/package/mtk/drivers/conninfra/src/platform/consys_reg_mng.c new file mode 100644 index 0000000000..e92a99d7c1 --- /dev/null +++ b/package/mtk/drivers/conninfra/src/platform/consys_reg_mng.c @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2016 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 http://www.gnu.org/licenses/gpl-2.0.html for more details. + */ +/*! \file +* \brief Declaration of library functions +* +* Any definitions in this file will be shared among GLUE Layer and internal Driver Stack. +*/ + +#define pr_fmt(fmt) KBUILD_MODNAME "@(%s:%d) " fmt, __func__, __LINE__ + +#include "consys_hw.h" +#include "consys_reg_mng.h" +#include "consys_reg_util.h" + +const struct consys_reg_mng_ops* g_consys_reg_ops = NULL; + +int consys_reg_mng_reg_readable(void) +{ + if (g_consys_reg_ops && + g_consys_reg_ops->consys_reg_mng_check_reable) + return g_consys_reg_ops->consys_reg_mng_check_reable(); + + return -1; +} + +int consys_reg_mng_is_connsys_reg(phys_addr_t addr) +{ + if (g_consys_reg_ops && + g_consys_reg_ops->consys_reg_mng_is_consys_reg) + return g_consys_reg_ops->consys_reg_mng_is_consys_reg(addr); + + return -1; +} + + +int consys_reg_mng_is_bus_hang(void) +{ + if (g_consys_reg_ops && + g_consys_reg_ops->consys_reg_mng_is_bus_hang) + return g_consys_reg_ops->consys_reg_mng_is_bus_hang(); + + return -1; +} + +int consys_reg_mng_dump_bus_status(void) +{ + if (g_consys_reg_ops && + g_consys_reg_ops->consys_reg_mng_dump_bus_status) + return g_consys_reg_ops->consys_reg_mng_dump_bus_status(); + + return -1; +} + +int consys_reg_mng_dump_conninfra_status(void) +{ + if (g_consys_reg_ops && + g_consys_reg_ops->consys_reg_mng_dump_conninfra_status) + return g_consys_reg_ops->consys_reg_mng_dump_conninfra_status(); + + return -1; +} + +int consys_reg_mng_dump_cpupcr(enum conn_dump_cpupcr_type dump_type, int times, unsigned long interval_us) +{ + if (g_consys_reg_ops && + g_consys_reg_ops->consys_reg_mng_dump_cpupcr) + return g_consys_reg_ops->consys_reg_mng_dump_cpupcr(dump_type, times, interval_us); + + return -1; +} + +int consys_reg_mng_init(struct platform_device *pdev, const struct conninfra_plat_data* plat_data) +{ + int ret = 0; + if (g_consys_reg_ops == NULL) + g_consys_reg_ops = (const struct consys_reg_mng_ops*)plat_data->reg_ops; + + if (g_consys_reg_ops && + g_consys_reg_ops->consys_reg_mng_init) + ret = g_consys_reg_ops->consys_reg_mng_init(pdev); + else + ret = EFAULT; + + return ret; +} + +int consys_reg_mng_deinit(void) +{ + if (g_consys_reg_ops&& + g_consys_reg_ops->consys_reg_mng_deinit) + g_consys_reg_ops->consys_reg_mng_deinit(); + + return 0; +} + +int consys_reg_mng_reg_read(unsigned long addr, unsigned int *value, unsigned int mask) +{ + void __iomem *vir_addr = NULL; + + vir_addr = ioremap_nocache(addr, 0x100); + if (!vir_addr) { + pr_err("ioremap fail\n"); + return -1; + } + + *value = (unsigned int)CONSYS_REG_READ(vir_addr) & mask; + + pr_info("[%x] mask=[%x]\n", *value, mask); + + iounmap(vir_addr); + return 0; +} + +int consys_reg_mng_reg_write(unsigned long addr, unsigned int value, unsigned int mask) +{ + void __iomem *vir_addr = NULL; + + vir_addr = ioremap_nocache(addr, 0x100); + if (!vir_addr) { + pr_err("ioremap fail\n"); + return -1; + } + + CONSYS_REG_WRITE_MASK(vir_addr, value, mask); + + iounmap(vir_addr); + return 0; +} + + +int consys_reg_mng_is_host_csr(unsigned long addr) +{ + if (g_consys_reg_ops && + g_consys_reg_ops->consys_reg_mng_is_host_csr) + return g_consys_reg_ops->consys_reg_mng_is_host_csr(addr); + + return -1; +} diff --git a/package/mtk/drivers/conninfra/src/platform/emi_mng.c b/package/mtk/drivers/conninfra/src/platform/emi_mng.c new file mode 100644 index 0000000000..15a9028f1f --- /dev/null +++ b/package/mtk/drivers/conninfra/src/platform/emi_mng.c @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2016 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 http://www.gnu.org/licenses/gpl-2.0.html for more details. + */ +/*! \file +* \brief Declaration of library functions +* +* Any definitions in this file will be shared among GLUE Layer and internal Driver Stack. +*/ + +#include +#include +#include +#include +#include "osal.h" + +#include "consys_hw.h" +#include "emi_mng.h" + +/******************************************************************************* +* C O M P I L E R F L A G S +******************************************************************************** +*/ + +/******************************************************************************* +* M A C R O S +******************************************************************************** +*/ + + +/******************************************************************************* +* E X T E R N A L R E F E R E N C E S +******************************************************************************** +*/ + +/******************************************************************************* +* C O N S T A N T S +******************************************************************************** +*/ + +/******************************************************************************* +* D A T A T Y P E S +******************************************************************************** +*/ + +/******************************************************************************* +* F U N C T I O N D E C L A R A T I O N S +******************************************************************************** +*/ + +/******************************************************************************* +* P U B L I C D A T A +******************************************************************************** +*/ + +unsigned long long gConEmiSize = 0; +phys_addr_t gConEmiPhyBase = 0x0; + +const struct consys_platform_emi_ops* consys_platform_emi_ops = NULL; + +struct consys_emi_addr_info connsys_emi_addr_info = { + .emi_ap_phy_base = 0, + .emi_ap_phy_size = 0, + .fw_emi_size = 0, +}; + +/******************************************************************************* +* P R I V A T E D A T A +******************************************************************************** +*/ + +/******************************************************************************* +* F U N C T I O N S +******************************************************************************** +*/ + +int emi_mng_set_region_protection(void) +{ + if (consys_platform_emi_ops && + consys_platform_emi_ops->consys_ic_emi_set_region_protection) + return consys_platform_emi_ops->consys_ic_emi_set_region_protection(); + + return -1; +} + +int emi_mng_set_remapping_reg(void) +{ + if (consys_platform_emi_ops && + consys_platform_emi_ops->consys_ic_emi_set_remapping_reg) + return consys_platform_emi_ops->consys_ic_emi_set_remapping_reg(); + + return -1; +} + +struct consys_emi_addr_info* emi_mng_get_phy_addr(void) +{ + return &connsys_emi_addr_info; +} + +int emi_mng_init(struct platform_device *pdev, const struct conninfra_plat_data* plat_data) +{ + unsigned int fw_emi_size = 0; + +#ifdef CONFIG_CONNINFRA_EMI_SUPPORT + struct device_node *np; + struct reserved_mem *rmem; + + np = of_parse_phandle(pdev->dev.of_node, "memory-region", 0); + if (!np) { + pr_info("[%s] memory region not found.\n", __func__); + return -1; + } + + rmem = of_reserved_mem_lookup(np); + if (!rmem) { + pr_info("[%s] no memory-region\n", __func__); + return -1; + } else { + gConEmiPhyBase = rmem->base; + gConEmiSize = rmem->size; + } +#else + pr_info("Conninfra not support EMI reservation for %04x\n", plat_data->chip_id); +#endif /* CONFIG_CONNINFRA_EMI_SUPPORT */ + + if (consys_platform_emi_ops == NULL) { + consys_platform_emi_ops = (const struct consys_platform_emi_ops*)plat_data->platform_emi_ops; + } + + if (consys_platform_emi_ops && consys_platform_emi_ops->consys_ic_emi_get_fw_emi_size) + fw_emi_size = consys_platform_emi_ops->consys_ic_emi_get_fw_emi_size(); + + pr_info("[emi_mng_init] gConEmiPhyBase = [0x%llx] size = [0x%llx] fw size = [0x%x] ops=[%p]\n", + gConEmiPhyBase, gConEmiSize, fw_emi_size, consys_platform_emi_ops); + + if (gConEmiPhyBase) { + connsys_emi_addr_info.emi_ap_phy_base = gConEmiPhyBase; + connsys_emi_addr_info.emi_ap_phy_size = gConEmiSize; + connsys_emi_addr_info.fw_emi_size = fw_emi_size; + } else { + pr_err("consys emi memory address gConEmiPhyBase invalid\n"); + } + + return 0; +} + +int emi_mng_deinit(void) +{ + return 0; +} diff --git a/package/mtk/drivers/conninfra/src/platform/include/consys_hw.h b/package/mtk/drivers/conninfra/src/platform/include/consys_hw.h new file mode 100644 index 0000000000..ab2ea8839c --- /dev/null +++ b/package/mtk/drivers/conninfra/src/platform/include/consys_hw.h @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2016 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 http://www.gnu.org/licenses/gpl-2.0.html for more details. + */ +/*! \file +* \brief Declaration of library functions +* +* Any definitions in this file will be shared among GLUE Layer and internal Driver Stack. +*/ + +#ifndef _PLATFORM_CONSYS_HW_H_ +#define _PLATFORM_CONSYS_HW_H_ + +#include +#include "conninfra.h" + +/******************************************************************************* +* C O M P I L E R F L A G S +******************************************************************************** +*/ + +/******************************************************************************* +* M A C R O S +******************************************************************************** +*/ + +#define CONN_SEMA_GET_SUCCESS 0 +#define CONN_SEMA_GET_FAIL 1 + +#define CONN_SEMA_TIMEOUT (1*1000) /* 1ms */ + +/******************************************************************************* +* E X T E R N A L R E F E R E N C E S +******************************************************************************** +*/ + +/******************************************************************************* +* C O N S T A N T S +******************************************************************************** +*/ + +/******************************************************************************* +* D A T A T Y P E S +******************************************************************************** +*/ + +struct conninfra_dev_cb { + int (*conninfra_suspend_cb) (void); + int (*conninfra_resume_cb) (void); + int (*conninfra_pmic_event_notifier) (unsigned int, unsigned int); +}; + +typedef int(*CONSYS_PLT_HW_INIT)(void); +typedef int(*CONSYS_PLT_XTAL_CTRL_FAST_MODE)(void); +typedef int(*CONSYS_PLT_CONNSYS_SW_RESET_CTRL)(bool bassert); +typedef void(*CONSYS_PLT_SET_IF_PINMUX)(bool enable); +typedef int(*CONSYS_PLT_TX_RX_BUS_SLP_PROT_CTRL)(bool enable); +typedef int(*CONSYS_PLT_POLLING_CONSYS_CHIPID)(void); +typedef int(*CONSYS_PLT_BUS_CLOCK_CTRL)(enum consys_drv_type drv_type, unsigned int bus_clock); +typedef int(*CONSYS_PLT_D_DIE_CFG)(void); +typedef int(*CONSYS_PLT_CONNINFRA_SYSRAM_HW_CTRL)(void); +typedef int(*CONSYS_PLT_SPI_MASTER_CFG)(void); +typedef int(*CONSYS_PLT_A_DIE_CFG)(void); +typedef int(*CONSYS_PLT_AFE_WBG_CAL)(void); +typedef int(*CONSYS_PLT_SUBSYS_PLL_INITIAL)(void); +typedef int(*CONSYS_PLT_OSC_LEGACY_MODE)(void); +typedef int(*CONSYS_PLT_TOP_PWR_CTRL)(void); +typedef int(*CONSYS_PLT_CONN_INFRA_BUS_TIMEOUT)(void); +typedef int(*CONSYS_PLT_CLKGEN_WPLL_HW_CTRL)(void); +typedef int(*CONSYS_PLT_CONNINFRA_TOP_WAKEUP) (void); +typedef int(*CONSYS_PLT_CONNINFRA_TOP_SLEEP) (void); +typedef int(*CONSYS_PLT_ADIE_TOP_CK_EN_ON_OFF_CTRL)(enum consys_drv_type type, unsigned char on); +typedef int(*CONSYS_PLT_CONNINFRA_WF_WAKEUP) (void); +typedef int(*CONSYS_PLT_CONNINFRA_WF_SLEEP) (void); +typedef int(*CONSYS_PLT_CONN_WMCPU_SW_RESET) (bool bassert); +typedef int(*CONSYS_PLT_WF_BUS_SLP_PROT_CTRL)(bool enable); +typedef int(*CONSYS_PLT_WFSYS_TOP_ON_CTRL) (bool enable); +typedef int(*CONSYS_PLT_WFSYS_BUS_SLP_PROT_CHECK)(bool enable); +typedef int(*CONSYS_PLT_WFSYS_BUS_TIMEOUT_CTRL) (void); +typedef int(*CONSYS_PLT_CONN_WMCPU_IDLE_LOOP_CHECK) (void); +typedef int(*CONSYS_PLT_WPLL_CTRL)(bool enable); +typedef int(*CONSYS_PLT_CONNINFRA_WF_REQ_CLR) (void); +typedef int(*CONSYS_PLT_CLK_GET_FROM_DTS) (struct platform_device *pdev); +typedef int(*CONSYS_PLT_CLK_DETACH) (void); +typedef int(*CONSYS_PLT_CO_CLOCK_TYPE) (void); +typedef unsigned int(*CONSYS_PLT_SOC_CHIPID_GET) (void); +typedef unsigned int(*CONSYS_PLT_GET_HW_VER)(void); +typedef int(*CONSYS_PLT_SPI_READ)(enum sys_spi_subsystem subsystem, unsigned int addr, unsigned int *data); +typedef int(*CONSYS_PLT_SPI_WRITE)(enum sys_spi_subsystem subsystem, unsigned int addr, unsigned int data); +typedef int(*CONSYS_PLT_SPI_CLOCK_SWITCH)(enum connsys_spi_speed_type type); +typedef int(*CONSYS_PLT_POWER_STATE)(void); +typedef int(*CONSYS_PLT_AIDE_TYPE_CHECK)(void); +typedef int(*CONSYS_PLT_AIDE_TYPE_CFG)(void); + +struct consys_hw_ops_struct { + /* HW init */ + CONSYS_PLT_HW_INIT consys_plt_hw_init; + + /* Power on/off CONNSYS PART (by Conn_infra Driver) */ + CONSYS_PLT_XTAL_CTRL_FAST_MODE consys_plt_xtal_ctrl_fast_mode; + CONSYS_PLT_CONNSYS_SW_RESET_CTRL consys_plt_connsys_sw_reset_ctrl; + CONSYS_PLT_SET_IF_PINMUX consys_plt_set_if_pinmux; + CONSYS_PLT_TX_RX_BUS_SLP_PROT_CTRL consys_plt_tx_rx_bus_slp_prot_ctrl; + CONSYS_PLT_POLLING_CONSYS_CHIPID consys_plt_polling_consys_chipid; + CONSYS_PLT_BUS_CLOCK_CTRL consys_plt_bus_clock_ctrl; + CONSYS_PLT_D_DIE_CFG consys_plt_d_die_cfg; + CONSYS_PLT_CONNINFRA_SYSRAM_HW_CTRL consys_plt_conninfra_sysram_hw_ctrl; + CONSYS_PLT_SPI_MASTER_CFG consys_plt_spi_master_cfg; + CONSYS_PLT_A_DIE_CFG consys_plt_a_die_cfg; + CONSYS_PLT_AFE_WBG_CAL consys_plt_afe_wbg_cal; + CONSYS_PLT_SUBSYS_PLL_INITIAL consys_plt_subsys_pll_initial; + CONSYS_PLT_OSC_LEGACY_MODE consys_plt_osc_legacy_mode; + CONSYS_PLT_TOP_PWR_CTRL consys_plt_top_pwr_ctrl; + CONSYS_PLT_CONN_INFRA_BUS_TIMEOUT consys_plt_conn_infra_bus_timeout; + CONSYS_PLT_CLKGEN_WPLL_HW_CTRL consys_plt_clkgen_wpll_hw_ctrl; + CONSYS_PLT_CONNINFRA_TOP_WAKEUP consys_plt_conninfra_wakeup; + CONSYS_PLT_CONNINFRA_TOP_SLEEP consys_plt_conninfra_sleep; + CONSYS_PLT_ADIE_TOP_CK_EN_ON_OFF_CTRL consys_plt_adie_top_ck_en_on_off_ctrl; + CONSYS_PLT_WPLL_CTRL consys_plt_wpll_ctrl; + + /* Power on/off WFSYS PART 0 (by WF Driver) */ + CONSYS_PLT_CONNINFRA_WF_WAKEUP consys_plt_conninfra_wf_wakeup; + CONSYS_PLT_CONNINFRA_WF_SLEEP consys_plt_conninfra_wf_sleep; + CONSYS_PLT_CONN_WMCPU_SW_RESET consys_plt_conn_wmcpu_sw_reset; + CONSYS_PLT_WF_BUS_SLP_PROT_CTRL consys_plt_wf_bus_slp_prot_ctrl; + CONSYS_PLT_WFSYS_TOP_ON_CTRL consys_plt_wfsys_top_on_ctrl; + CONSYS_PLT_WFSYS_BUS_SLP_PROT_CHECK consys_plt_wfsys_bus_slp_prot_check; + CONSYS_PLT_WFSYS_BUS_TIMEOUT_CTRL consys_plt_wfsys_bus_timeout_ctrl; + CONSYS_PLT_CONN_WMCPU_IDLE_LOOP_CHECK consys_plt_conn_wmcpu_idle_loop_check; + CONSYS_PLT_CONNINFRA_WF_REQ_CLR consys_plt_conninfra_wf_req_clr; + + /* load from dts */ + CONSYS_PLT_CLK_GET_FROM_DTS consys_plt_clk_get_from_dts; + CONSYS_PLT_CLK_DETACH consys_plt_clk_detach; + + /* clock */ + CONSYS_PLT_CO_CLOCK_TYPE consys_plt_co_clock_type; + + CONSYS_PLT_SOC_CHIPID_GET consys_plt_soc_chipid_get; + + /* debug */ + CONSYS_PLT_GET_HW_VER consys_plt_get_hw_ver; + + /* For SPI operation */ + CONSYS_PLT_SPI_READ consys_plt_spi_read; + CONSYS_PLT_SPI_WRITE consys_plt_spi_write; + + /* For SPI clock switch */ + CONSYS_PLT_SPI_CLOCK_SWITCH consys_plt_spi_clock_switch; + + /* power state */ + CONSYS_PLT_POWER_STATE consys_plt_power_state; + + /* others */ + CONSYS_PLT_AIDE_TYPE_CHECK consys_plt_adie_type_check; + CONSYS_PLT_AIDE_TYPE_CFG consys_plt_adie_type_cfg; +}; + +struct consys_hw_env { + bool valid; + unsigned int adie_hw_version; + unsigned int adie_id; + int is_rc_mode; +}; + +struct conninfra_plat_data { + const unsigned int chip_id; + const void* hw_ops; + const void* reg_ops; + const void* platform_emi_ops; + const void* platform_pmic_ops; +}; + +extern struct consys_hw_env conn_hw_env[AIDE_NUM_MAX]; +extern struct consys_base_addr conn_reg; +/******************************************************************************* +* P U B L I C D A T A +******************************************************************************** +*/ + +/******************************************************************************* +* P R I V A T E D A T A +******************************************************************************** +*/ + +/******************************************************************************* +* F U N C T I O N D E C L A R A T I O N S +******************************************************************************** +*/ +int mtk_conninfra_drv_init(struct conninfra_dev_cb *dev_cb); +int mtk_conninfra_drv_deinit(void); + +int consys_hw_pwr_on(unsigned int curr_status, unsigned int on_radio); +int consys_hw_pwr_off(unsigned int curr_status, unsigned int off_radio); + +int consys_hw_wifi_power_ctl(unsigned int enable); +int consys_hw_bt_power_ctl(unsigned int enable); +int consys_hw_gps_power_ctl(unsigned int enable); +int consys_hw_fm_power_ctl(unsigned int enable); +int consys_hw_pmic_event_cb(unsigned int id, unsigned int event); + +unsigned int consys_hw_chipid_get(void); + +int consys_hw_get_clock_schematic(void); +unsigned int consys_hw_get_hw_ver(void); + +/******************************************************************************* +* tempoary for STEP +******************************************************************************** +*/ +/* + * return + * 1 : can read + * 0 : can't read + * -1: not consys register + */ +int consys_hw_reg_readable(void); +int consys_hw_is_connsys_reg(phys_addr_t addr); +/* + * 0 means NO hang + * > 0 means hang!! + */ +int consys_hw_is_bus_hang(void); +int consys_hw_dump_bus_status(void); + +int consys_hw_spi_read(enum sys_spi_subsystem subsystem, unsigned int addr, unsigned int *data); +int consys_hw_spi_write(enum sys_spi_subsystem subsystem, unsigned int addr, unsigned int data); + +int consys_hw_adie_top_ck_en_on(enum consys_drv_type type); +int consys_hw_adie_top_ck_en_off(enum consys_drv_type type); + +/* NOTE: debug only*/ +int consys_hw_force_conninfra_wakeup(void); +int consys_hw_force_conninfra_sleep(void); + +int consys_hw_spi_clock_switch(enum connsys_spi_speed_type type); + +struct platform_device *get_consys_device(void); +struct consys_base_addr *get_conn_reg_base_addr(void); + +int consys_hw_dump_power_state(void); +/******************************************************************************* +* F U N C T I O N S +******************************************************************************** +*/ + +#endif /* _PLATFORM_CONSYS_HW_H_ */ diff --git a/package/mtk/drivers/conninfra/src/platform/include/consys_reg_base.h b/package/mtk/drivers/conninfra/src/platform/include/consys_reg_base.h new file mode 100644 index 0000000000..b16dc6217b --- /dev/null +++ b/package/mtk/drivers/conninfra/src/platform/include/consys_reg_base.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2016 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 http://www.gnu.org/licenses/gpl-2.0.html for more details. + */ +/*! \file +* \brief Declaration of library functions +* +* Any definitions in this file will be shared among GLUE Layer and internal Driver Stack. +*/ + +#ifndef _PLATFORM_CONSYS_REG_BASE_H_ +#define _PLATFORM_CONSYS_REG_BASE_H_ + +struct consys_reg_base_addr { + unsigned long phy_addr; + unsigned long long size; + unsigned long vir_addr; +}; + +#endif /* _PLATFORM_CONSYS_REG_BASE_H_ */ diff --git a/package/mtk/drivers/conninfra/src/platform/include/consys_reg_mng.h b/package/mtk/drivers/conninfra/src/platform/include/consys_reg_mng.h new file mode 100644 index 0000000000..57f08af9a2 --- /dev/null +++ b/package/mtk/drivers/conninfra/src/platform/include/consys_reg_mng.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2016 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 http://www.gnu.org/licenses/gpl-2.0.html for more details. + */ +/*! \file +* \brief Declaration of library functions +* +* Any definitions in this file will be shared among GLUE Layer and internal Driver Stack. +*/ + +#ifndef _PLATFORM_CONSYS_REG_MNG_H_ +#define _PLATFORM_CONSYS_REG_MNG_H_ + +#include + +#include "consys_hw.h" + +enum conn_dump_cpupcr_type +{ + CONN_DUMP_CPUPCR_TYPE_BT = 1, + CONN_DUMP_CPUPCR_TYPE_WF = 2, + CONN_DUMP_CPUPCR_TYPE_ALL = 3, +}; + +struct consys_reg_mng_ops { + int(*consys_reg_mng_init) (struct platform_device *pdev); + int(*consys_reg_mng_deinit) (void); + int(*consys_reg_mng_check_reable) (void); + int(*consys_reg_mng_is_consys_reg) (unsigned int addr); + int(*consys_reg_mng_is_bus_hang) (void); + int(*consys_reg_mng_dump_bus_status) (void); + int(*consys_reg_mng_dump_conninfra_status) (void); + int(*consys_reg_mng_dump_cpupcr) (enum conn_dump_cpupcr_type, int times, unsigned long interval_us); + int(*consys_reg_mng_is_host_csr) (unsigned long addr); +}; + +int consys_reg_mng_init(struct platform_device *pdev, const struct conninfra_plat_data* plat_data); +int consys_reg_mng_deinit(void); +int consys_reg_mng_reg_readable(void); +int consys_reg_mng_is_connsys_reg(phys_addr_t addr); +int consys_reg_mng_reg_read(unsigned long addr, unsigned int *value, unsigned int mask); +int consys_reg_mng_reg_write(unsigned long addr, unsigned int value, unsigned int mask); +int consys_reg_mng_is_bus_hang(void); +int consys_reg_mng_dump_bus_status(void); +int consys_reg_mng_dump_conninfra_status(void); +int consys_reg_mng_dump_cpupcr(enum conn_dump_cpupcr_type dump_type, int times, unsigned long interval_us); +int consys_reg_mng_is_host_csr(unsigned long addr); + +#endif /* _PLATFORM_CONSYS_REG_MNG_H_ */ diff --git a/package/mtk/drivers/conninfra/src/platform/include/consys_reg_util.h b/package/mtk/drivers/conninfra/src/platform/include/consys_reg_util.h new file mode 100644 index 0000000000..65ddc05bdb --- /dev/null +++ b/package/mtk/drivers/conninfra/src/platform/include/consys_reg_util.h @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2016 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 http://www.gnu.org/licenses/gpl-2.0.html for more details. + */ +/*! \file +* \brief Declaration of library functions +* +* Any definitions in this file will be shared among GLUE Layer and internal Driver Stack. +*/ + +#ifndef _PLATFORM_CONSYS_REG_UTIL_H_ +#define _PLATFORM_CONSYS_REG_UTIL_H_ + +/******************************************************************************* +* C O M P I L E R F L A G S +******************************************************************************** +*/ + +/******************************************************************************* +* M A C R O S +******************************************************************************** +*/ +/* platform dependent */ +#include "plat_def.h" + +#ifndef BIT +#define BIT(x) (1<<(x)) +#endif + +#define KBYTE (1024*sizeof(char)) +#ifndef GENMASK +#define GENMASK(h, l) \ + (((~0UL) - (1UL << (l)) + 1) & (~0UL >> (BITS_PER_LONG - 1 - (h)))) +#endif + +#define GET_BIT_MASK(value, mask) ((value) & (mask)) +#define SET_BIT_MASK(pdest, value, mask) (*(pdest) = (GET_BIT_MASK(*(pdest), ~(mask)) | GET_BIT_MASK(value, mask))) +#define GET_BIT_RANGE(data, end, begin) ((data) & GENMASK(end, begin)) +#define SET_BIT_RANGE(pdest, data, end, begin) (SET_BIT_MASK(pdest, data, GENMASK(end, begin))) + +#define CONSYS_SET_BIT(REG, BITVAL) (*((volatile unsigned int *)(REG)) |= ((unsigned int)(BITVAL))) +#define CONSYS_CLR_BIT(REG, BITVAL) ((*(volatile unsigned int *)(REG)) &= ~((unsigned int)(BITVAL))) +#define CONSYS_CLR_BIT_WITH_KEY(REG, BITVAL, KEY) {\ + unsigned int val = (*(volatile unsigned int *)(REG)); \ + val &= ~((unsigned int)(BITVAL)); \ + val |= ((unsigned int)(KEY)); \ + (*(volatile unsigned int *)(REG)) = val;\ +} +#define CONSYS_REG_READ(addr) (*((volatile unsigned int *)(addr))) +#define CONSYS_REG_READ_BIT(addr, BITVAL) (*((volatile unsigned int *)(addr)) & ((unsigned int)(BITVAL))) +#define CONSYS_REG_WRITE(addr, data) mt_reg_sync_writel(data, addr) +#define CONSYS_REG_WRITE_RANGE(reg, data, end, begin) {\ + unsigned int val = CONSYS_REG_READ(reg); \ + SET_BIT_RANGE(&val, data, end, begin); \ + CONSYS_REG_WRITE(reg, val); \ +} +#define CONSYS_REG_WRITE_MASK(reg, data, mask) {\ + unsigned int val = CONSYS_REG_READ(reg); \ + SET_BIT_MASK(&val, data, mask); \ + CONSYS_REG_WRITE(reg, val); \ +} + +/* + * Write value with value_offset bits of right shift and size bits, + * to the reg_offset-th bit of address reg + * value -----------XXXXXXXXXXXX------------------- + * |<--size-->|<--value_offset-->| + * reg -------------OOOOOOOOOOOO----------------- + * |<--size-->|<--reg_offset-->| + * result -------------XXXXXXXXXXXX----------------- + */ +#define CONSYS_REG_WRITE_OFFSET_RANGE(reg, value, reg_offset, value_offset, size) ({\ + unsigned int data = (value) >> (value_offset); \ + data = GET_BIT_RANGE(data, size, 0); \ + data = data << (reg_offset); \ + CONSYS_REG_WRITE_RANGE(reg, data, ((reg_offset) + ((size) - 1)), reg_offset); \ +}) + +#define CONSYS_REG_WRITE_BIT(reg, offset, val) CONSYS_REG_WRITE_OFFSET_RANGE(reg, ((val) & 1), offset, 0, 1) + +#define CONSYS_REG_BIT_POLLING(addr, bit_index, exp_val, loop, delay, success) {\ + unsigned int polling_count = 0; \ + unsigned int reg_value = 0; \ + success = 0; \ + reg_value = (CONSYS_REG_READ_BIT(addr, (0x1 << bit_index)) >> bit_index); \ + while (reg_value != exp_val) { \ + if (polling_count > loop) { \ + success = -1; \ + break; \ + } \ + reg_value = (CONSYS_REG_READ_BIT(addr, (0x1 << bit_index)) >> bit_index); \ + udelay(delay); \ + polling_count++; \ + } \ +} + +#define CONSYS_REG_POLLING_LARGER_OR_EQUAL(addr, mask, bit_index, exp_val, loop, delay, success) {\ + unsigned int polling_count = 0; \ + unsigned int reg_value = 0; \ + success = 0; \ + reg_value = ((CONSYS_REG_READ(addr) & mask) >> bit_index); \ + while (reg_value < exp_val) { \ + if (polling_count > loop) { \ + success = -1; \ + break; \ + } \ + reg_value = ((CONSYS_REG_READ(addr) & mask) >> bit_index); \ + udelay(delay); \ + polling_count++; \ + } \ +} + +#define CONSYS_REG_POLLING_EQUAL(addr, mask, bit_index, exp_val, loop, delay, success) {\ + unsigned int polling_count = 0; \ + unsigned int reg_value = 0; \ + success = 0; \ + reg_value = ((CONSYS_REG_READ(addr) & mask) >> bit_index); \ + while (reg_value != exp_val) { \ + if (polling_count > loop) { \ + success = -1; \ + break; \ + } \ + reg_value = ((CONSYS_REG_READ(addr) & mask) >> bit_index); \ + udelay(delay); \ + polling_count++; \ + } \ +} + +/******************************************************************************* +* E X T E R N A L R E F E R E N C E S +******************************************************************************** +*/ + +/******************************************************************************* +* C O N S T A N T S +******************************************************************************** +*/ + +/******************************************************************************* +* D A T A T Y P E S +******************************************************************************** +*/ + + +/******************************************************************************* +* P U B L I C D A T A +******************************************************************************** +*/ + +/******************************************************************************* +* P R I V A T E D A T A +******************************************************************************** +*/ + +/******************************************************************************* +* F U N C T I O N D E C L A R A T I O N S +******************************************************************************** +*/ + + +/******************************************************************************* +* F U N C T I O N S +******************************************************************************** +*/ + +#endif /* _PLATFORM_CONSYS_REG_UTIL_H_ */ diff --git a/package/mtk/drivers/conninfra/src/platform/include/emi_mng.h b/package/mtk/drivers/conninfra/src/platform/include/emi_mng.h new file mode 100644 index 0000000000..93e619e632 --- /dev/null +++ b/package/mtk/drivers/conninfra/src/platform/include/emi_mng.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2016 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 http://www.gnu.org/licenses/gpl-2.0.html for more details. + */ +/*! \file +* \brief Declaration of library functions +* +* Any definitions in this file will be shared among GLUE Layer and internal Driver Stack. +*/ + +#ifndef _PLATFORM_EMI_MNG_H_ +#define _PLATFORM_EMI_MNG_H_ + +#include +#include +#include "osal.h" + +#include "consys_hw.h" + +/******************************************************************************* +* C O M P I L E R F L A G S +******************************************************************************** +*/ + +/******************************************************************************* +* M A C R O S +******************************************************************************** +*/ + + +/******************************************************************************* +* E X T E R N A L R E F E R E N C E S +******************************************************************************** +*/ + +/******************************************************************************* +* C O N S T A N T S +******************************************************************************** +*/ + +/******************************************************************************* +* D A T A T Y P E S +******************************************************************************** +*/ + +struct consys_emi_addr_info { + /* This include BT/WF FW and WFDMA */ + phys_addr_t emi_ap_phy_base; + unsigned int emi_ap_phy_size; + unsigned int fw_emi_size; +}; + +typedef int(*CONSYS_IC_EMI_SET_REGION_PROTECTION) (void); +typedef int(*CONSYS_IC_EMI_SET_REMAPPING_REG) (void); +typedef unsigned int (*CONSYS_IC_GET_FW_EMI_SIZE)(void); + +struct consys_platform_emi_ops { + CONSYS_IC_EMI_SET_REGION_PROTECTION consys_ic_emi_set_region_protection; + CONSYS_IC_EMI_SET_REMAPPING_REG consys_ic_emi_set_remapping_reg; + CONSYS_IC_GET_FW_EMI_SIZE consys_ic_emi_get_fw_emi_size; +}; + +/******************************************************************************* +* P U B L I C D A T A +******************************************************************************** +*/ + +/******************************************************************************* +* P R I V A T E D A T A +******************************************************************************** +*/ + +/******************************************************************************* +* F U N C T I O N D E C L A R A T I O N S +******************************************************************************** +*/ + +int emi_mng_init(struct platform_device *pdev, const struct conninfra_plat_data* plat_data); +int emi_mng_deinit(void); + +int emi_mng_set_region_protection(void); +int emi_mng_set_remapping_reg(void); +struct consys_emi_addr_info* emi_mng_get_phy_addr(void); + +/******************************************************************************* +* F U N C T I O N S +******************************************************************************** +*/ + +#endif /* _PLATFORM_EMI_MNG_H_ */ + diff --git a/package/mtk/drivers/conninfra/src/platform/include/plat_def.h b/package/mtk/drivers/conninfra/src/platform/include/plat_def.h new file mode 100644 index 0000000000..7c2a4f62b9 --- /dev/null +++ b/package/mtk/drivers/conninfra/src/platform/include/plat_def.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2016 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 http://www.gnu.org/licenses/gpl-2.0.html for more details. + */ +/*! \file +* \brief Declaration of library functions +* +* Any definitions in this file will be shared among GLUE Layer and internal Driver Stack. +*/ + +#ifndef _PLATFORM_DEF_H_ +#define _PLATFORM_DEF_H_ + +#include +#include + +#define mt_reg_sync_writel(v, a) \ + do { \ + writel((v), (void __force __iomem *)((a))); \ + mb(); \ + } while (0) + +#endif /* _PLATFORM_DEF_H_ */ diff --git a/package/mtk/drivers/conninfra/src/platform/include/pmic_mng.h b/package/mtk/drivers/conninfra/src/platform/include/pmic_mng.h new file mode 100644 index 0000000000..8ee3243732 --- /dev/null +++ b/package/mtk/drivers/conninfra/src/platform/include/pmic_mng.h @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2016 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 http://www.gnu.org/licenses/gpl-2.0.html for more details. + */ +/*! \file +* \brief Declaration of library functions +* +* Any definitions in this file will be shared among GLUE Layer and internal Driver Stack. +*/ + +#ifndef _PLATFORM_PMIC_MNG_H_ +#define _PLATFORM_PMIC_MNG_H_ + +#include + +#include "consys_hw.h" + +/******************************************************************************* +* C O M P I L E R F L A G S +******************************************************************************** +*/ + +/******************************************************************************* +* M A C R O S +******************************************************************************** +*/ + + +/******************************************************************************* +* E X T E R N A L R E F E R E N C E S +******************************************************************************** +*/ + +/******************************************************************************* +* C O N S T A N T S +******************************************************************************** +*/ + +/******************************************************************************* +* D A T A T Y P E S +******************************************************************************** +*/ + +typedef int(*CONSYS_PMIC_GET_FROM_DTS) ( + struct platform_device *pdev, + struct conninfra_dev_cb* dev_cb); + +typedef int(*CONSYS_PMIC_COMMON_POWER_CTRL) (unsigned int enable); + +typedef int(*CONSYS_PMIC_WIFI_POWER_CTRL) (unsigned int enable); +typedef int(*CONSYS_PMIC_BT_POWER_CTRL) (unsigned int enable); +typedef int(*CONSYS_PMIC_GPS_POWER_CTRL) (unsigned int enable); +typedef int(*CONSYS_PMIC_FM_POWER_CTRL) (unsigned int enable); +typedef int(*CONSYS_PMIC_EVENT_NOTIFIER) (unsigned int id, unsigned int event); + +struct consys_platform_pmic_ops { + CONSYS_PMIC_GET_FROM_DTS consys_pmic_get_from_dts; + /* vcn 18 */ + CONSYS_PMIC_COMMON_POWER_CTRL consys_pmic_common_power_ctrl; + CONSYS_PMIC_WIFI_POWER_CTRL consys_pmic_wifi_power_ctrl; + CONSYS_PMIC_BT_POWER_CTRL consys_pmic_bt_power_ctrl; + CONSYS_PMIC_GPS_POWER_CTRL consys_pmic_gps_power_ctrl; + CONSYS_PMIC_FM_POWER_CTRL consys_pmic_fm_power_ctrl; + CONSYS_PMIC_EVENT_NOTIFIER consys_pmic_event_notifier; +}; + + + +/******************************************************************************* +* P U B L I C D A T A +******************************************************************************** +*/ + +/******************************************************************************* +* P R I V A T E D A T A +******************************************************************************** +*/ + +/******************************************************************************* +* F U N C T I O N D E C L A R A T I O N S +******************************************************************************** +*/ + +int pmic_mng_init( + struct platform_device *pdev, + struct conninfra_dev_cb* dev_cb, + const struct conninfra_plat_data* plat_data); +int pmic_mng_deinit(void); + +int pmic_mng_common_power_ctrl(unsigned int enable); +int pmic_mng_wifi_power_ctrl(unsigned int enable); +int pmic_mng_bt_power_ctrl(unsigned int enable); +int pmic_mng_gps_power_ctrl(unsigned int enable); +int pmic_mng_fm_power_ctrl(unsigned int enable); +int pmic_mng_event_cb(unsigned int id, unsigned int event); + +/******************************************************************************* +* F U N C T I O N S +******************************************************************************** +*/ + +#endif /* _PLATFORM_PMIC_MNG_H_ */ diff --git a/package/mtk/drivers/conninfra/src/platform/mt7981/include/mt7981.h b/package/mtk/drivers/conninfra/src/platform/mt7981/include/mt7981.h new file mode 100644 index 0000000000..48b1e2786d --- /dev/null +++ b/package/mtk/drivers/conninfra/src/platform/mt7981/include/mt7981.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2016 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 http://www.gnu.org/licenses/gpl-2.0.html for more details. + */ +/*! \file +* \brief Declaration of library functions +* +* Any definitions in this file will be shared among GLUE Layer and internal Driver Stack. +*/ + +#ifndef _PLATFORM_MT7981_H_ +#define _PLATFORM_MT7981_H_ + +enum +{ + ADIE_TYPE_NONE = 0, + ADIE_TYPE_ONE, + ADIE_TYPE_TWO, + ADIE_TYPE_NUM_MAX +}; + +enum conn_semaphore_type +{ + CONN_SEMA_CHIP_POWER_ON_INDEX = 0, + CONN_SEMA_CALIBRATION_INDEX = 1, + CONN_SEMA_FW_DL_INDEX = 2, + CONN_SEMA_CLOCK_SWITCH_INDEX = 3, + CONN_SEMA_CCIF_INDEX = 4, + CONN_SEMA_COEX_INDEX = 5, + CONN_SEMA_USB_EP0_INDEX = 6, + CONN_SEMA_USB_SHARED_INFO_INDEX = 7, + CONN_SEMA_USB_SUSPEND_INDEX = 8, + CONN_SEMA_USB_RESUME_INDEX = 9, + CONN_SEMA_PCIE_INDEX = 10, + CONN_SEMA_RFSPI_INDEX = 11, + CONN_SEMA_EFUSE_INDEX = 12, + CONN_SEMA_THERMAL_INDEX = 13, + CONN_SEMA_FLASH_INDEX = 14, + CONN_SEMA_DEBUG_INDEX = 15, + CONN_SEMA_WIFI_LP_INDEX = 16, + CONN_SEMA_PATCH_DL_INDEX = 17, + CONN_SEMA_SHARED_VAR_INDEX = 18, + CONN_SEMA_CONN_INFRA_COMMON_SYSRAM_INDEX = 19, + CONN_SEMA_NUM_MAX = 32 /* can't be omitted */ +}; + +unsigned int consys_soc_chipid_get(void); +unsigned int consys_get_hw_ver(void); + + +#endif /* _PLATFORM_MT7981_H_ */ diff --git a/package/mtk/drivers/conninfra/src/platform/mt7981/include/mt7981_consys_reg.h b/package/mtk/drivers/conninfra/src/platform/mt7981/include/mt7981_consys_reg.h new file mode 100644 index 0000000000..ffa8b5c398 --- /dev/null +++ b/package/mtk/drivers/conninfra/src/platform/mt7981/include/mt7981_consys_reg.h @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2016 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 http://www.gnu.org/licenses/gpl-2.0.html for more details. + */ +/*! \file +* \brief Declaration of library functions +* +* Any definitions in this file will be shared among GLUE Layer and internal Driver Stack. +*/ + +#ifndef _PLATFORM_MT7981_CONSYS_REG_H_ +#define _PLATFORM_MT7981_CONSYS_REG_H_ + +#include "consys_reg_base.h" +/******************************************************************************* +* C O M P I L E R F L A G S +******************************************************************************** +*/ + +/******************************************************************************* +* M A C R O S +******************************************************************************** +*/ + + +/******************************************************************************* +* E X T E R N A L R E F E R E N C E S +******************************************************************************** +*/ + +/******************************************************************************* +* C O N S T A N T S +******************************************************************************** +*/ + +/******************************************************************************* +* D A T A T Y P E S +******************************************************************************** +*/ +enum consys_base_addr_index { + TOP_MISC_BASE = 0, /* top_misc */ + TOPRGU_BASE = 1, /* TOPRGU */ + GPIO_BASE = 2, /* GPIO */ + IOCFG_TM_BASE = 3, /* IOCFG_TM */ + IOCFG_LT_BASE = 4, /* IOCFG_LT */ + INFRACFG_AO_BASE = 5, /* infracfg_ao_auto_gen_reg */ + CONN_INFRA_CFG_BASE = 6, /* conn_infra_cfg */ + CONN_INFRA_SYSRAM_BASE = 7, /* conn_infra_sysram */ + CONN_INFRA_CLKGEN_ON_TOP_BASE = 8, /* conn_infra_clkgen_on_top */ + CONN_HOST_CSR_TOP_BASE = 9, /* conn_host_csr_top */ + CONN_INFRA_BUS_CR_BASE = 10, /* conn_infra_bus_cr */ + CONN_INFRA_RGU_BASE = 11, /* conn_infra_rgu */ + CONN_WT_SLP_CTL_REG_BASE = 12, /* conn_wt_slp_ctl_reg */ + INST2_CONN_WT_SLP_CTL_REG_BASE = 13, /* Inst2_conn_wt_slp_ctl_reg */ + CONN_RF_SPI_MST_REG_BASE = 14, /* conn_rf_spi_mst_reg */ + INST2_CONN_RF_SPI_MST_REG_BASE = 15, /* Inst2_conn_rf_spi_mst_reg */ + CONN_SEMAPHORE_BASE = 16, /* conn_semaphore */ + CONN_AFE_CTL_BASE = 17, /* conn_afe_ctl */ + CONN_AFE_CTL_2ND_BASE = 18, /* conn_afe_ctl_2nd */ + WF_TOP_SLPPROT_ON_BASE = 19, /* wf_top_slpprot_on by remapping to 0x81020000 */ + WF_TOP_CFG_BASE = 20, /* wf_top_cfg by remapping to 0x80020000 */ + WF_MCU_CONFIG_LS_BASE = 21, /* wf_mcu_confg_ls by remapping to 0x88000000 */ + WF_MCU_BUS_CR_BASE = 22, /* wf_mcu_bus_cr by remapping to 0x830C0XXX */ + WF_MCUSYS_INFRA_BUS_FULL_U_DEBUG_CTRL_AO_BASE = 23, /* wf_mcusys_infra_bus_full_u_debug_ctrl_ao by remapping to 0x810F0000 */ + WF_TOP_CFG_ON_BASE = 24, /* wf_top_cfg_on by remapping to 0x81021000 */ + CONSYS_BASE_ADDR_MAX +}; + +struct consys_base_addr { + struct consys_reg_base_addr reg_base_addr[CONSYS_BASE_ADDR_MAX]; +}; + +extern struct consys_base_addr conn_reg; + +#define REG_TOP_MISC_ADDR conn_reg.reg_base_addr[TOP_MISC_BASE].vir_addr +#define REG_TOP_RGU_ADDR conn_reg.reg_base_addr[TOPRGU_BASE].vir_addr +#define REG_GPIO_BASE_ADDR conn_reg.reg_base_addr[GPIO_BASE].vir_addr +#define REG_IOCFG_TM_ADDR conn_reg.reg_base_addr[IOCFG_TM_BASE].vir_addr +#define REG_IOCFG_LT_ADDR conn_reg.reg_base_addr[IOCFG_LT_BASE].vir_addr +#define REG_INFRACFG_AO_ADDR conn_reg.reg_base_addr[INFRACFG_AO_BASE].vir_addr +#define REG_CONN_INFRA_CFG_ADDR conn_reg.reg_base_addr[CONN_INFRA_CFG_BASE].vir_addr +#define REG_CONN_INFRA_SYSRAM_ADDR conn_reg.reg_base_addr[CONN_INFRA_SYSRAM_BASE].vir_addr +#define REG_CONN_INFRA_CLKGEN_ON_TOP_ADDR conn_reg.reg_base_addr[CONN_INFRA_CLKGEN_ON_TOP_BASE].vir_addr +#define REG_CONN_HOST_CSR_TOP_ADDR conn_reg.reg_base_addr[CONN_HOST_CSR_TOP_BASE].vir_addr +#define REG_CONN_INFRA_BUS_CR_ADDR conn_reg.reg_base_addr[CONN_INFRA_BUS_CR_BASE].vir_addr +#define REG_CONN_INFRA_RGU_ADDR conn_reg.reg_base_addr[CONN_INFRA_RGU_BASE].vir_addr +#define REG_CONN_WT_SLP_CTL_REG_ADDR conn_reg.reg_base_addr[CONN_WT_SLP_CTL_REG_BASE].vir_addr +#define REG_INST2_CONN_WT_SLP_CTL_REG_ADDR conn_reg.reg_base_addr[INST2_CONN_WT_SLP_CTL_REG_BASE].vir_addr +#define REG_CONN_RF_SPI_MST_REG_ADDR conn_reg.reg_base_addr[CONN_RF_SPI_MST_REG_BASE].vir_addr +#define REG_INST2_CONN_RF_SPI_MST_REG_ADDR conn_reg.reg_base_addr[INST2_CONN_RF_SPI_MST_REG_BASE].vir_addr +#define REG_CONN_SEMAPHORE_ADDR conn_reg.reg_base_addr[CONN_SEMAPHORE_BASE].vir_addr +#define REG_CONN_AFE_CTL_ADDR conn_reg.reg_base_addr[CONN_AFE_CTL_BASE].vir_addr +#define REG_CONN_AFE_CTL_2ND_ADDR conn_reg.reg_base_addr[CONN_AFE_CTL_2ND_BASE].vir_addr +#define REG_WF_TOP_SLPPROT_ON_ADDR conn_reg.reg_base_addr[WF_TOP_SLPPROT_ON_BASE].vir_addr +#define REG_WF_TOP_CFG_ADDR conn_reg.reg_base_addr[WF_TOP_CFG_BASE].vir_addr +#define REG_WF_MCU_CONFIG_LS_ADDR conn_reg.reg_base_addr[WF_MCU_CONFIG_LS_BASE].vir_addr +#define REG_WF_MCU_BUS_CR_ADDR conn_reg.reg_base_addr[WF_MCU_BUS_CR_BASE].vir_addr +#define REG_WF_MCUSYS_INFRA_BUS_FULL_U_DEBUG_CTRL_AO_ADDR conn_reg.reg_base_addr[WF_MCUSYS_INFRA_BUS_FULL_U_DEBUG_CTRL_AO_BASE].vir_addr +#define REG_WF_TOP_CFG_ON_ADDR conn_reg.reg_base_addr[WF_TOP_CFG_ON_BASE].vir_addr + +/******************************************************************************* +* P U B L I C D A T A +******************************************************************************** +*/ + +/******************************************************************************* +* P R I V A T E D A T A +******************************************************************************** +*/ + +/******************************************************************************* +* F U N C T I O N D E C L A R A T I O N S +******************************************************************************** +*/ + + +/******************************************************************************* +* F U N C T I O N S +******************************************************************************** +*/ + +struct consys_base_addr* get_conn_reg_base_addr(void); + +#endif /* _PLATFORM_MT7981_CONSYS_REG_H_ */ diff --git a/package/mtk/drivers/conninfra/src/platform/mt7981/include/mt7981_consys_reg_offset.h b/package/mtk/drivers/conninfra/src/platform/mt7981/include/mt7981_consys_reg_offset.h new file mode 100644 index 0000000000..bd50361d04 --- /dev/null +++ b/package/mtk/drivers/conninfra/src/platform/mt7981/include/mt7981_consys_reg_offset.h @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2016 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 http://www.gnu.org/licenses/gpl-2.0.html for more details. + */ +/*! \file +* \brief Declaration of library functions +* +* Any definitions in this file will be shared among GLUE Layer and internal Driver Stack. +*/ + +#ifndef _PLATFORM_MT7981_CONSYS_REG_OFFSET_H_ +#define _PLATFORM_MT7981_CONSYS_REG_OFFSET_H_ + +/**********************************************************************/ +/* Base: TOP_MISC (0x11D1_0000) */ +/**********************************************************************/ +#define CONNSYS_MISC 0x0114 +#define TOP_MISC_RSRV_ALL1_3 0x021C + + +/**********************************************************************/ +/* Base: TOP RGU (0x1001_C000) */ +/**********************************************************************/ +#define TOP_RGU_WDT_SWSYSRST 0x0018 + +/**********************************************************************/ +/* Base: GPIO (0x11D0_0000) */ +/**********************************************************************/ +#define GPIO_MODE5 0x0350 +#define GPIO_MODE6 0x0360 +#define GPIO_MODE7 0x0370 + +/**********************************************************************/ +/* Base: IOCFG_TM (0x11F0_0000) */ +/**********************************************************************/ +#define IOCFG_TM_DRV_CFG0 0x0000 +#define IOCFG_TM_DRV_CFG1 0x0010 + +/**********************************************************************/ +/* Base: IOCFG_LT (0x11F1_0000) */ +/**********************************************************************/ +#define IOCFG_LT_DRV_CFG0 0x0000 +#define IOCFG_LT_DRV_CFG1 0x0010 + +/**********************************************************************/ +/* Base: INFRACFG_AO (0x1000_3000) */ +/**********************************************************************/ +#define CONN2AP_GALS_SLPPROT 0x00D0 +#define AP2CONN_GALS_SLPPROT 0x00D4 + +/**********************************************************************/ +/* Base: CONN_INFRA_CFG (0x1800_1000) */ +/**********************************************************************/ +#define CONN_INFRA_CFG_IP_VERSION 0x0000 +#define EFUSE 0x0020 +#define ADIE_CTL 0x0030 +#define CONN_INFRA_CFG_PWRCTRL0 0x0200 +#define CONN_INFRA_CFG_RC_CTL_0 0x0380 +#define OSC_CTL_0 0x0300 +#define EMI_CTL_WF 0x0414 +#define CONN_INFRA_WF_SLP_CTRL 0x0540 +#define CONN_INFRA_WF_SLP_STATUS 0x0544 + +/**********************************************************************/ +/* Base: CONN_INFRA_SYSRAM (0x1805_0000) */ +/**********************************************************************/ +#define SYSRAM_BASE_ADDR 0x0000 + +/**********************************************************************/ +/* Base: CONN_INFRA_CLKGEN_ON_TOP (0x1800_9000) */ +/**********************************************************************/ +#define CKGEN_BUS_WPLL_DIV_1 0x0008 +#define CKGEN_BUS_WPLL_DIV_2 0x000C +#define CKGEN_RFSPI_WPLL_DIV 0x0040 +#define CKGEN_BUS 0x0A00 + +/**********************************************************************/ +/* Base: CONN_HOST_CSR_TOP (0x1806_0000) */ +/**********************************************************************/ +#define CONN_INFRA_WAKEPU_TOP 0x01A0 +#define CONN_INFRA_WAKEPU_WF 0x01A4 +#define CONN2AP_REMAP_MCU_EMI 0x01C4 +#define CONN2AP_REMAP_WF_PERI 0x01D4 +#define CONN2AP_RSVD_PERI_REGION1 0x01D8 +#define DBG_DUMMY_3 0x02CC + +/**********************************************************************/ +/* Base: CONN_INFRA_BUS_CR (0x1800_E000) */ +/**********************************************************************/ +#define CONN_INFRA_BUS_OFF_TIMEOUT_CTRL 0x0300 +#define CONN_INFRA_BUS_ON_TIMEOUT_CTRL 0x031C +#define CONN2AP_EMI_PATH_ADDR_START 0x0360 +#define CONN2AP_EMI_PATH_ADDR_END 0x0364 + +/**********************************************************************/ +/* Base: CONN_INFRA_RGU (0x1800_0000) */ +/**********************************************************************/ +#define WFSYS_ON_TOP_PWR_CTL 0x0010 +#define BGFYS_ON_TOP_PWR_CTL 0x0020 +#define SYSRAM_HWCTL_PDN 0x0050 +#define SYSRAM_HWCTL_SLP 0x0054 +#define WFSYS_CPU_SW_RST_B 0x0120 + +/**********************************************************************/ +/* Base: CONN_WT_SLP_CTL_REG (0x1800_5000) */ +/* Base: INST2_CONN_WT_SLP_CTL_REG (0x1808_5000) */ +/**********************************************************************/ +#define WB_WF_CK_ADDR 0x0070 +#define WB_WF_WAKE_ADDR 0x0074 +#define WB_WF_ZPS_ADDR 0x0078 +#define WB_TOP_CK_ADDR 0x0084 +#define WB_WF_B0_CMD_ADDR 0x008C +#define WB_WF_B1_CMD_ADDR 0x0090 +#define WB_SLP_TOP_CK_0 0x0120 +#define WB_SLP_TOP_CK_1 0x0124 + +/**********************************************************************/ +/* Base: CONN_RF_SPI_MST_REG (0x1800_4000) */ +/* Base: INST2_CONN_RF_SPI_MST_REG (0x1808_4000) */ +/**********************************************************************/ +#define SPI_STA 0x0000 +#define SPI_WF_ADDR 0x0010 +#define SPI_WF_WDAT 0x0014 +#define SPI_WF_RDAT 0x0018 +#define SPI_BT_ADDR 0x0020 +#define SPI_BT_WDAT 0x0024 +#define SPI_BT_RDAT 0x0028 +#define SPI_FM_ADDR 0x0030 +#define SPI_FM_WDAT 0x0034 +#define SPI_FM_RDAT 0x0038 +#define SPI_GPS_ADDR 0x0040 +#define SPI_GPS_WDAT 0x0044 +#define SPI_GPS_RDAT 0x0048 +#define SPI_TOP_ADDR 0x0050 +#define SPI_TOP_WDAT 0x0054 +#define SPI_TOP_RDAT 0x0058 + +/**********************************************************************/ +/* Base: CONN_SEMAPHORE_BASE (0x1807_0000) */ +/**********************************************************************/ +#define CONN_SEMA00_M2_OWN_STA 0x2000 +#define CONN_SEMA00_M2_OWN_REL 0x2200 +#define CONN_SEMA_OWN_BY_M0_STA_REP 0x0400 +#define CONN_SEMA_OWN_BY_M1_STA_REP 0x1400 +#define CONN_SEMA_OWN_BY_M2_STA_REP 0x2400 +#define CONN_SEMA_OWN_BY_M3_STA_REP 0x3400 +#define CONN_SEMA_OWN_BY_M4_STA_REP 0x4400 +#define CONN_SEMA_OWN_BY_M5_STA_REP 0x5400 +#define CONN_SEMA_OWN_BY_M6_STA_REP 0x6400 +#define CONN_SEMA_OWN_BY_M7_STA_REP 0x7400 + +/**********************************************************************/ +/* Base: CONN_AFE_CTL_BASE (0x1800_3000) */ +/* Base: CONN_AFE_CTL_2ND_BASE (0x1808_3000) */ +/**********************************************************************/ +#define RG_DIG_EN_01 0x0000 +#define RG_DIG_EN_02 0x0004 +#define RG_DIG_EN_03 0x0008 +#define RG_DIG_TOP_01 0x000C +#define RG_PLL_STB_TIME 0x00F4 + +/**********************************************************************/ +/* Base: WF_TOP_SLPPROT_ON_BASE (0x8102_0000 remap to 0x184C_0000) */ +/**********************************************************************/ +#define WF_TOP_SLPPROT_ON_STATUS_READ 0x300C + +/**********************************************************************/ +/* Base: WF_TOP_CFG_BASE (0x8002_0000 remap to 0x184B_0000) */ +/**********************************************************************/ +#define WF_TOP_CFG_IP_VERSION 0x0010 + +/**********************************************************************/ +/* Base: WF_MCU_CONFIG_LS_BASE (0x8800_0000 remap to 0x184F_0000) */ +/**********************************************************************/ +#define BUSHANGCR 0x0440 + +/**********************************************************************/ +/* Base: WF_MCU_BUS_CR_BASE (0x830C_0XXX remap to 0x1840_0XXX) */ +/**********************************************************************/ +#define AP2WF_REMAP_1 0x0120 + +/**********************************************************************/ +/* Base: WF_MCUSYS_INFRA_BUS_FULL_U_DEBUG_CTRL_AO_BASE (0x810F_0000 remap to 0x1850_0000) */ +/**********************************************************************/ +#define WF_MCUSYS_INFRA_BUS_FULL_U_DEBUG_CTRL_AO_WFMCU_PWA_CTRL0 0x0000 + +/**********************************************************************/ +/* Base: WF_TOP_CFG_ON_BASE (0x8102_1000 remap to 0x184C_0000) */ +/**********************************************************************/ +#define ROMCODE_INDEX 0x1604 + +/**********************************************************************/ +/* A-die CR */ +/**********************************************************************/ +#define ATOP_CHIP_ID 0x02C +#define ATOP_TOP_CLK_EN 0xA00 +#define ATOP_RG_ENCAL_WBTAC_IF_SW 0x070 +#define ATOP_RG_WRI_CK_SELECT 0x4AC +#define ATOP_EFUSE_CTRL_1 0x108 +#define ATOP_EFUSE_CTRL_2 0x148 +#define ATOP_EFUSE_CTRL_3 0x14C +#define ATOP_EFUSE_CTRL_4 0x15C +#define ATOP_EFUSE_RDATA0 0x130 +#define ATOP_EFUSE_RDATA1 0x134 +#define ATOP_EFUSE_RDATA2 0x138 +#define ATOP_EFUSE_RDATA3 0x13C +#define ATOP_RG_EFUSE_CFG5 0x144 +#define ATOP_THADC_ANALOG 0x3A6 +#define ATOP_THADC_SLOP 0x3A7 +#define ATOP_RG_TOP_THADC_BG 0x034 +#define ATOP_RG_TOP_THADC_00 0x038 + +#define ATOP_XTAL_TRIM_FLOW 0x3AC +#define ATOP_XTAL_CR_C1_SEL_AXM_80M_OSC 0x390 +#define ATOP_XTAL_CR_C1_SEL_AXM_40M_OSC 0x391 +#define ATOP_XTAL_CR_C1_SEL_AXM_TRIM1_80M_OSC 0x398 +#define ATOP_XTAL_CR_C1_SEL_AXM_TRIM1_40M_OSC 0x399 +#define ATOP_RG_STRAP_PIN_IN 0x4FC +#define ATOP_RG_XO_01 0x65C +#define ATOP_RG_XO_03 0x664 + + +#define ATOP_7975_XTAL_CALIBRATION 0x3A1 +#define ATOP_7975_XTAL_TRIM2_COMPENSATION 0x3A2 +#define ATOP_7975_XTAL_TRIM3_COMPENSATION 0x3A3 +#define ATOP_7975_XTAL_TRIM4_COMPENSATION 0x3A4 +#define ATOP_7975_XTAL_TRIM_FLOW 0x3A5 +#define ATOP_7975_CR_C1_C2_A94 0xA94 +#define ATOP_7975_CR_C1_C2_A18 0xA18 +#define ATOP_7975_CR_C1_C2_A84 0xA84 +#define ATOP_7975_CR_C1_C2_AA4 0xAA4 + + + +#endif /* _PLATFORM_MT7981_CONSYS_REG_OFFSET_H_ */ diff --git a/package/mtk/drivers/conninfra/src/platform/mt7981/include/mt7981_emi.h b/package/mtk/drivers/conninfra/src/platform/mt7981/include/mt7981_emi.h new file mode 100644 index 0000000000..6d395748ba --- /dev/null +++ b/package/mtk/drivers/conninfra/src/platform/mt7981/include/mt7981_emi.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2016 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 http://www.gnu.org/licenses/gpl-2.0.html for more details. + */ +/*! \file +* \brief Declaration of library functions +* +* Any definitions in this file will be shared among GLUE Layer and internal Driver Stack. +*/ + +#ifndef _PLATFORM_MT7981_EMI_H_ +#define _PLATFORM_MT7981_EMI_H_ + +#include "osal.h" +#include "emi_mng.h" +/******************************************************************************* +* C O M P I L E R F L A G S +******************************************************************************** +*/ + +/******************************************************************************* +* M A C R O S +******************************************************************************** +*/ + +/******************************************************************************* +* E X T E R N A L R E F E R E N C E S +******************************************************************************** +*/ + +/******************************************************************************* +* C O N S T A N T S +******************************************************************************** +*/ + +/******************************************************************************* +* D A T A T Y P E S +******************************************************************************** +*/ + + +/******************************************************************************* +* P U B L I C D A T A +******************************************************************************** +*/ + +/******************************************************************************* +* P R I V A T E D A T A +******************************************************************************** +*/ + +/******************************************************************************* +* F U N C T I O N D E C L A R A T I O N S +******************************************************************************** +*/ + +struct consys_platform_emi_ops* get_consys_platform_emi_ops(void); + +/******************************************************************************* +* F U N C T I O N S +******************************************************************************** +*/ + +#endif /* _PLATFORM_MT7981_EMI_H_ */ diff --git a/package/mtk/drivers/conninfra/src/platform/mt7981/include/mt7981_pmic.h b/package/mtk/drivers/conninfra/src/platform/mt7981/include/mt7981_pmic.h new file mode 100644 index 0000000000..8f597918d5 --- /dev/null +++ b/package/mtk/drivers/conninfra/src/platform/mt7981/include/mt7981_pmic.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2016 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 http://www.gnu.org/licenses/gpl-2.0.html for more details. + */ +/*! \file +* \brief Declaration of library functions +* +* Any definitions in this file will be shared among GLUE Layer and internal Driver Stack. +*/ + +#ifndef _PLATFORM_MT7981_PMIC_H_ +#define _PLATFORM_MT7981_PMIC_H_ + +#include "osal.h" +#include "pmic_mng.h" +/******************************************************************************* +* C O M P I L E R F L A G S +******************************************************************************** +*/ + +/******************************************************************************* +* M A C R O S +******************************************************************************** +*/ + +/******************************************************************************* +* E X T E R N A L R E F E R E N C E S +******************************************************************************** +*/ + +/******************************************************************************* +* C O N S T A N T S +******************************************************************************** +*/ + +/******************************************************************************* +* D A T A T Y P E S +******************************************************************************** +*/ + + +/******************************************************************************* +* P U B L I C D A T A +******************************************************************************** +*/ + +/******************************************************************************* +* P R I V A T E D A T A +******************************************************************************** +*/ + +/******************************************************************************* +* F U N C T I O N D E C L A R A T I O N S +******************************************************************************** +*/ + +/******************************************************************************* +* F U N C T I O N S +******************************************************************************** +*/ + +struct consys_platform_pmic_ops* get_consys_platform_pmic_ops(void); + +#endif /* _PLATFORM_MT7981_PMIC_H_ */ diff --git a/package/mtk/drivers/conninfra/src/platform/mt7981/include/mt7981_pos.h b/package/mtk/drivers/conninfra/src/platform/mt7981/include/mt7981_pos.h new file mode 100644 index 0000000000..997e68454b --- /dev/null +++ b/package/mtk/drivers/conninfra/src/platform/mt7981/include/mt7981_pos.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2016 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 http://www.gnu.org/licenses/gpl-2.0.html for more details. + */ +/*! \file +* \brief Declaration of library functions +* +* Any definitions in this file will be shared among GLUE Layer and internal Driver Stack. +*/ + +#ifndef _PLATFORM_MT7981_POS_H_ +#define _PLATFORM_MT7981_POS_H_ + +int consys_plt_hw_init(void); +int consys_xtal_ctrl_fast_mode(void); +int consys_sw_reset_ctrl(bool bassert); +int consys_tx_rx_bus_slp_prot_ctrl(bool enable); +void consys_set_if_pinmux(bool enable); +int consys_polling_chipid(void); +int consys_plt_adie_type_cfg(void); +int consys_bus_clock_ctrl(enum consys_drv_type drv_type, unsigned int bus_clock); +int consys_emi_set_remapping_reg(void); +int consys_emi_set_region_protection(void); +int connsys_d_die_cfg(void); +int connsys_conninfra_sysram_hw_ctrl(void); +int connsys_spi_master_cfg(void); +int consys_sema_acquire_timeout(unsigned int index, unsigned int usec); +void consys_sema_release(unsigned int index); +int consys_spi_read(enum sys_spi_subsystem subsystem, unsigned int addr, unsigned int *data); +int consys_spi_write(enum sys_spi_subsystem subsystem, unsigned int addr, unsigned int data); +int consys_spi_write_offset_range(enum sys_spi_subsystem subsystem, unsigned int addr, unsigned int value, + unsigned int reg_offset, unsigned int value_offset, unsigned int size); +int connsys_a_die_cfg(void); +int connsys_afe_wbg_cal(void); +int connsys_subsys_pll_initial(void); +int connsys_osc_legacy_mode(void); +int connsys_top_pwr_ctrl(void); +int connsys_conn_infra_bus_timeout(void); +int connsys_clkgen_wpll_hw_ctrl(void); +int consys_conninfra_top_wakeup(void); +int consys_conninfra_top_sleep(void); +int consys_adie_top_ck_en_on_off_ctrl(enum consys_drv_type type, unsigned char on); +int consys_conninfra_wf_wakeup(void); +int consys_conninfra_wf_sleep(void); +int consys_conn_wmcpu_sw_reset(bool bassert); +int consys_wf_bus_slp_prot_ctrl(bool enable); +int consys_wfsys_top_on_ctrl(bool enable); +int consys_wfsys_bus_slp_prot_check(bool enable); +int consys_wfsys_bus_timeout_ctrl(void); +int consys_wmcpu_idle_loop_check(void); +int consys_wpll_ctrl(bool enable); +int consys_conninfra_wf_req_clr(void); + + +#endif /* _PLATFORM_MT7981_POS_H_ */ diff --git a/package/mtk/drivers/conninfra/src/platform/mt7981/mt7981.c b/package/mtk/drivers/conninfra/src/platform/mt7981/mt7981.c new file mode 100644 index 0000000000..ac00ebc381 --- /dev/null +++ b/package/mtk/drivers/conninfra/src/platform/mt7981/mt7981.c @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2016 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 http://www.gnu.org/licenses/gpl-2.0.html for more details. + */ +/*! \file +* \brief Declaration of library functions +* +* Any definitions in this file will be shared among GLUE Layer and internal Driver Stack. +*/ + +#define pr_fmt(fmt) KBUILD_MODNAME "@(%s:%d) " fmt, __func__, __LINE__ + +#include +#include +#include +#include +#include +#include +#include + +#include "osal.h" +#include "conninfra.h" +#include "consys_hw.h" +#include "consys_reg_mng.h" +#include "consys_reg_util.h" +#include "mt7981.h" +#include "mt7981_pos.h" +#include "emi_mng.h" +#include "mt7981_consys_reg.h" +#include "mt7981_consys_reg_offset.h" + +/******************************************************************************* +* C O M P I L E R F L A G S +******************************************************************************** +*/ + +/******************************************************************************* +* M A C R O S +******************************************************************************** +*/ +#define PLATFORM_SOC_CHIP 0x7981 +#define CONN_IP_VER 0x02090000 + +/******************************************************************************* +* E X T E R N A L R E F E R E N C E S +******************************************************************************** +*/ + +/******************************************************************************* +* C O N S T A N T S +******************************************************************************** +*/ + +/******************************************************************************* +* D A T A T Y P E S +******************************************************************************** +*/ + +/******************************************************************************* +* F U N C T I O N D E C L A R A T I O N S +******************************************************************************** +*/ + +/******************************************************************************* +* P U B L I C D A T A +******************************************************************************** +*/ +struct consys_hw_ops_struct g_consys_hw_ops_mt7981 = { + /* HW init */ + .consys_plt_hw_init = consys_plt_hw_init, + + /* POS */ + .consys_plt_xtal_ctrl_fast_mode = consys_xtal_ctrl_fast_mode, + .consys_plt_connsys_sw_reset_ctrl = consys_sw_reset_ctrl, + .consys_plt_set_if_pinmux = consys_set_if_pinmux, + .consys_plt_tx_rx_bus_slp_prot_ctrl = consys_tx_rx_bus_slp_prot_ctrl, + .consys_plt_polling_consys_chipid = consys_polling_chipid, + .consys_plt_bus_clock_ctrl = consys_bus_clock_ctrl, + .consys_plt_d_die_cfg = connsys_d_die_cfg, + .consys_plt_conninfra_sysram_hw_ctrl = connsys_conninfra_sysram_hw_ctrl, + .consys_plt_spi_master_cfg = connsys_spi_master_cfg, + .consys_plt_a_die_cfg = connsys_a_die_cfg, + .consys_plt_afe_wbg_cal = connsys_afe_wbg_cal, + .consys_plt_subsys_pll_initial = connsys_subsys_pll_initial, + .consys_plt_osc_legacy_mode = connsys_osc_legacy_mode, + .consys_plt_top_pwr_ctrl = connsys_top_pwr_ctrl, + .consys_plt_conn_infra_bus_timeout = connsys_conn_infra_bus_timeout, + .consys_plt_clkgen_wpll_hw_ctrl = connsys_clkgen_wpll_hw_ctrl, + .consys_plt_conninfra_wakeup = consys_conninfra_top_wakeup, + .consys_plt_conninfra_sleep = consys_conninfra_top_sleep, + .consys_plt_adie_top_ck_en_on_off_ctrl = consys_adie_top_ck_en_on_off_ctrl, + .consys_plt_conninfra_wf_wakeup = consys_conninfra_wf_wakeup, + .consys_plt_conninfra_wf_sleep = consys_conninfra_wf_sleep, + .consys_plt_conn_wmcpu_sw_reset = consys_conn_wmcpu_sw_reset, + .consys_plt_wf_bus_slp_prot_ctrl = consys_wf_bus_slp_prot_ctrl, + .consys_plt_wfsys_top_on_ctrl = consys_wfsys_top_on_ctrl, + .consys_plt_wfsys_bus_slp_prot_check = consys_wfsys_bus_slp_prot_check, + .consys_plt_wfsys_bus_timeout_ctrl = consys_wfsys_bus_timeout_ctrl, + .consys_plt_conn_wmcpu_idle_loop_check = consys_wmcpu_idle_loop_check, + .consys_plt_wpll_ctrl = consys_wpll_ctrl, + .consys_plt_conninfra_wf_req_clr = consys_conninfra_wf_req_clr, + + /* load from dts */ + /* TODO: mtcmos should move to a independent module */ + .consys_plt_clk_get_from_dts = NULL, + .consys_plt_clk_detach = NULL, + + /* clock */ + .consys_plt_soc_chipid_get = consys_soc_chipid_get, + + /* debug */ + .consys_plt_get_hw_ver = consys_get_hw_ver, + .consys_plt_spi_read = consys_spi_read, + .consys_plt_spi_write = consys_spi_write, + .consys_plt_spi_clock_switch = NULL, + .consys_plt_power_state = NULL, + + /* others */ + .consys_plt_adie_type_cfg = consys_plt_adie_type_cfg, +}; + +/* For mt7981 */ +extern struct consys_hw_ops_struct g_consys_hw_ops_mt7981; +extern struct consys_reg_mng_ops g_dev_consys_reg_ops_mt7981; +extern struct consys_platform_emi_ops g_consys_platform_emi_ops_mt7981; +extern struct consys_platform_pmic_ops g_consys_platform_pmic_ops_mt7981; + +const struct conninfra_plat_data mt7981_plat_data = { + .chip_id = PLATFORM_SOC_CHIP, + .hw_ops = &g_consys_hw_ops_mt7981, + .reg_ops = &g_dev_consys_reg_ops_mt7981, + .platform_emi_ops = &g_consys_platform_emi_ops_mt7981, + .platform_pmic_ops = &g_consys_platform_pmic_ops_mt7981, +}; + +/******************************************************************************* +* P R I V A T E D A T A +******************************************************************************** +*/ + +/******************************************************************************* +* F U N C T I O N S +******************************************************************************** +*/ +unsigned int consys_soc_chipid_get(void) +{ + return PLATFORM_SOC_CHIP; +} + +unsigned int consys_get_hw_ver(void) +{ + return CONN_IP_VER; +} + diff --git a/package/mtk/drivers/conninfra/src/platform/mt7981/mt7981_consys_reg.c b/package/mtk/drivers/conninfra/src/platform/mt7981/mt7981_consys_reg.c new file mode 100644 index 0000000000..fddcd1b148 --- /dev/null +++ b/package/mtk/drivers/conninfra/src/platform/mt7981/mt7981_consys_reg.c @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2016 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 http://www.gnu.org/licenses/gpl-2.0.html for more details. + */ +/*! \file +* \brief Declaration of library functions +* +* Any definitions in this file will be shared among GLUE Layer and internal Driver Stack. +*/ + +#include +#include +#include +#include +#include +#include +#include "consys_reg_mng.h" +#include "mt7981_consys_reg.h" +#include "mt7981_consys_reg_offset.h" +#include "consys_hw.h" +#include "consys_reg_util.h" + +#define CFG_REG_LOAD_FROM_DTS_CTRL 0 + +static int consys_reg_init(struct platform_device *pdev); +static int consys_reg_deinit(void); + +struct consys_base_addr conn_reg = { + .reg_base_addr[TOP_MISC_BASE] = {0x11D10000, 0x1000, 0}, + .reg_base_addr[TOPRGU_BASE] = {0x1001C000, 0x1000, 0}, + .reg_base_addr[GPIO_BASE] = {0x11D00000, 0x1000, 0}, + .reg_base_addr[IOCFG_TM_BASE] = {0x11F00000, 0x1000, 0}, + .reg_base_addr[IOCFG_LT_BASE] = {0x11F10000, 0x1000, 0}, + .reg_base_addr[INFRACFG_AO_BASE] = {0x10003000, 0x1000, 0}, + .reg_base_addr[CONN_INFRA_CFG_BASE] = {0x18001000, 0x1000, 0}, + .reg_base_addr[CONN_INFRA_SYSRAM_BASE] = {0x18050000, 0x1000, 0}, + .reg_base_addr[CONN_INFRA_CLKGEN_ON_TOP_BASE] = {0x18009000, 0x1000, 0}, + .reg_base_addr[CONN_HOST_CSR_TOP_BASE] = {0x18060000, 0x1000, 0}, + .reg_base_addr[CONN_INFRA_BUS_CR_BASE] = {0x1800E000, 0x1000, 0}, + .reg_base_addr[CONN_INFRA_RGU_BASE] = {0x18000000, 0x1000, 0}, + .reg_base_addr[CONN_WT_SLP_CTL_REG_BASE] = {0x18005000, 0x1000, 0}, + .reg_base_addr[INST2_CONN_WT_SLP_CTL_REG_BASE] = {0x18085000, 0x1000, 0}, + .reg_base_addr[CONN_RF_SPI_MST_REG_BASE] = {0x18004000, 0x1000, 0}, + .reg_base_addr[INST2_CONN_RF_SPI_MST_REG_BASE] = {0x18084000, 0x1000, 0}, + .reg_base_addr[CONN_SEMAPHORE_BASE] = {0x18070000, 0x10000, 0}, + .reg_base_addr[CONN_AFE_CTL_BASE] = {0x18003000, 0x1000, 0}, + .reg_base_addr[CONN_AFE_CTL_2ND_BASE] = {0x18083000, 0x1000, 0}, + .reg_base_addr[WF_TOP_SLPPROT_ON_BASE] = {0x184C0000, 0x10000, 0}, + .reg_base_addr[WF_TOP_CFG_BASE] = {0x184B0000, 0x1000, 0}, + .reg_base_addr[WF_MCU_CONFIG_LS_BASE] = {0x184F0000, 0x1000, 0}, + .reg_base_addr[WF_MCU_BUS_CR_BASE] = {0x18400000, 0x1000, 0}, + .reg_base_addr[WF_MCUSYS_INFRA_BUS_FULL_U_DEBUG_CTRL_AO_BASE] = {0x18500000, 0x1000, 0}, + .reg_base_addr[WF_TOP_CFG_ON_BASE] = {0x184C0000, 0x10000, 0}, +}; + +const char* consys_base_addr_index_to_str[CONSYS_BASE_ADDR_MAX] = { + "TOP_MISC_BASE", + "TOPRGU_BASE", + "GPIO_BASE", + "IOCFG_TR_BASE", + "IOCFG_TL_BASE", + "INFRACFG_AO_BASE", + "CONN_INFRA_CFG_BASE", + "CONN_INFRA_SYSRAM_BASE", + "CONN_INFRA_CLKGEN_ON_TOP_BASE", + "CONN_HOST_CSR_TOP_BASE", + "CONN_INFRA_BUS_CR_BASE", + "CONN_INFRA_RGU_BASE", + "CONN_WT_SLP_CTL_REG_BASE", + "INST2_CONN_WT_SLP_CTL_REG_BASE", + "CONN_RF_SPI_MST_REG_BASE", + "INST2_CONN_RF_SPI_MST_REG_BASE", + "CONN_SEMAPHORE_BASE", + "CONN_AFE_CTL_BASE", + "CONN_AFE_CTL_2ND_BASE", + "WF_TOP_SLPPROT_ON_BASE", + "WF_TOP_CFG_BASE", + "WF_MCU_CONFIG_LS_BASE", + "WF_MCU_BUS_CR_BASE", + "WF_MCUSYS_INFRA_BUS_FULL_U_DEBUG_CTRL_AO_BASE", + "WF_TOP_CFG_ON_BASE" +}; + +struct consys_reg_mng_ops g_dev_consys_reg_ops_mt7981 = { + .consys_reg_mng_init = consys_reg_init, + .consys_reg_mng_deinit = consys_reg_deinit, + .consys_reg_mng_check_reable = NULL, + .consys_reg_mng_is_consys_reg = NULL, + .consys_reg_mng_is_bus_hang = NULL, + .consys_reg_mng_dump_bus_status = NULL, + .consys_reg_mng_dump_conninfra_status = NULL, + .consys_reg_mng_dump_cpupcr = NULL, + .consys_reg_mng_is_host_csr = NULL, +}; + +struct consys_base_addr* get_conn_reg_base_addr() +{ + return &conn_reg; +} + +static int consys_reg_init(struct platform_device *pdev) +{ + int ret = -1; + struct device_node *node = NULL; + struct consys_reg_base_addr *base_addr = NULL; + int i = 0; + + node = pdev->dev.of_node; + if (node) { +#if (CFG_REG_LOAD_FROM_DTS_CTRL == 1) + struct resource res; + int flag; + + for (i = 0; i < CONSYS_BASE_ADDR_MAX; i++) { + base_addr = &conn_reg.reg_base_addr[i]; + ret = of_address_to_resource(node, i, &res); + if (ret) { + pr_err("Get Reg Index(%d-%s) failed\n", i, consys_base_addr_index_to_str[i]); + continue; + } + base_addr->phy_addr = res.start; + base_addr->vir_addr = (unsigned long)of_iomap(node, i); + of_get_address(node, i, &(base_addr->size), &flag); +#if 0 + pr_info("Get Index(%d-%s) phy_addr(0x%zx) vir_addr=(0x%zx) size=(0x%zx)\n", + i, consys_base_addr_index_to_str[i], base_addr->phy_addr, + base_addr->vir_addr, base_addr->size); +#endif + } +#else + for (i = 0; i < CONSYS_BASE_ADDR_MAX; i++) { + base_addr = &conn_reg.reg_base_addr[i]; + if (base_addr->vir_addr == 0) + base_addr->vir_addr = (unsigned long)ioremap(base_addr->phy_addr, base_addr->size); + + pr_info("Get Index(%d-%s) phy_addr(0x%zx) vir_addr=(0x%zx) size=(0x%zx)\n", + i, consys_base_addr_index_to_str[i], base_addr->phy_addr, + base_addr->vir_addr, base_addr->size); + } +#endif + } else { + pr_err("[%s] can't find CONSYS compatible node\n", __func__); + return ret; + } + + return 0; +} + +static int consys_reg_deinit(void) +{ + int i = 0; + + for (i = 0; i < CONSYS_BASE_ADDR_MAX; i++) { + if (conn_reg.reg_base_addr[i].vir_addr) { + pr_info("[%d] Unmap %s (0x%zx)\n", i, consys_base_addr_index_to_str[i], + conn_reg.reg_base_addr[i].vir_addr); + iounmap((void __iomem*)conn_reg.reg_base_addr[i].vir_addr); + conn_reg.reg_base_addr[i].vir_addr = 0; + } + } + + return 0; +} + diff --git a/package/mtk/drivers/conninfra/src/platform/mt7981/mt7981_emi.c b/package/mtk/drivers/conninfra/src/platform/mt7981/mt7981_emi.c new file mode 100644 index 0000000000..c291b0e8c0 --- /dev/null +++ b/package/mtk/drivers/conninfra/src/platform/mt7981/mt7981_emi.c @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2016 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 http://www.gnu.org/licenses/gpl-2.0.html for more details. + */ +/*! \file +* \brief Declaration of library functions +* +* Any definitions in this file will be shared among GLUE Layer and internal Driver Stack. +*/ + +#define pr_fmt(fmt) KBUILD_MODNAME "@(%s:%d) " fmt, __func__, __LINE__ + +#include +#include +#include +#include "mt7981_emi.h" +#include "mt7981.h" +#include "mt7981_consys_reg.h" +#include "consys_hw.h" +#include "consys_reg_util.h" +#include "mt7981_pos.h" + +/******************************************************************************* +* P U B L I C D A T A +******************************************************************************** +*/ +unsigned int consys_emi_get_fw_emi_size(void) +{ + return 0x100000; +} + +struct consys_platform_emi_ops g_consys_platform_emi_ops_mt7981 = { + .consys_ic_emi_set_region_protection = consys_emi_set_region_protection, + .consys_ic_emi_set_remapping_reg = consys_emi_set_remapping_reg, + .consys_ic_emi_get_fw_emi_size = consys_emi_get_fw_emi_size, +}; + +struct consys_platform_emi_ops* get_consys_platform_emi_ops(void) +{ + return &g_consys_platform_emi_ops_mt7981; +} + diff --git a/package/mtk/drivers/conninfra/src/platform/mt7981/mt7981_pmic.c b/package/mtk/drivers/conninfra/src/platform/mt7981/mt7981_pmic.c new file mode 100644 index 0000000000..c46da4c845 --- /dev/null +++ b/package/mtk/drivers/conninfra/src/platform/mt7981/mt7981_pmic.c @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2016 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 http://www.gnu.org/licenses/gpl-2.0.html for more details. + */ +/*! \file +* \brief Declaration of library functions +* +* Any definitions in this file will be shared among GLUE Layer and internal Driver Stack. +*/ + +#define pr_fmt(fmt) KBUILD_MODNAME "@(%s:%d) " fmt, __func__, __LINE__ + +#include +#include +#include +#include +#include + +#include "consys_hw.h" +#include "consys_reg_util.h" +#include "osal.h" +#include "mt7981_pmic.h" +#include "mt7981_pos.h" +#include "mt7981_consys_reg.h" +#include "mt7981_consys_reg_offset.h" + +/******************************************************************************* +* C O M P I L E R F L A G S +******************************************************************************** +*/ + +/******************************************************************************* +* M A C R O S +******************************************************************************** +*/ + + +/******************************************************************************* +* E X T E R N A L R E F E R E N C E S +******************************************************************************** +*/ + +/******************************************************************************* +* C O N S T A N T S +******************************************************************************** +*/ + +/******************************************************************************* +* D A T A T Y P E S +******************************************************************************** +*/ + +/******************************************************************************* +* F U N C T I O N D E C L A R A T I O N S +******************************************************************************** +*/ + +/******************************************************************************* +* P U B L I C D A T A +******************************************************************************** +*/ + +struct consys_platform_pmic_ops g_consys_platform_pmic_ops_mt7981 = { + .consys_pmic_get_from_dts = NULL, + .consys_pmic_common_power_ctrl = NULL, + .consys_pmic_wifi_power_ctrl = NULL, + .consys_pmic_bt_power_ctrl = NULL, + .consys_pmic_gps_power_ctrl = NULL, + .consys_pmic_fm_power_ctrl = NULL, + .consys_pmic_event_notifier = NULL, +}; + +/******************************************************************************* +* P R I V A T E D A T A +******************************************************************************** +*/ + +/******************************************************************************* +* F U N C T I O N S +******************************************************************************** +*/ + +struct consys_platform_pmic_ops* get_consys_platform_pmic_ops(void) +{ + return &g_consys_platform_pmic_ops_mt7981; +} + diff --git a/package/mtk/drivers/conninfra/src/platform/mt7981/mt7981_pos.c b/package/mtk/drivers/conninfra/src/platform/mt7981/mt7981_pos.c new file mode 100644 index 0000000000..4050b66278 --- /dev/null +++ b/package/mtk/drivers/conninfra/src/platform/mt7981/mt7981_pos.c @@ -0,0 +1,1715 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019 MediaTek Inc. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME "@(%s:%d) " fmt, __func__, __LINE__ + +#include +#include "plat_def.h" +#include "consys_reg_util.h" +#include "consys_reg_mng.h" +#include "mt7981_consys_reg.h" +#include "mt7981_consys_reg_offset.h" +#include "mt7981_pos.h" +#include "mt7981.h" +#include "mt7981_emi.h" + + + +/******************************************************************************* +* M A C R O S +******************************************************************************** +*/ +#define MTD_WIFI_NM "Factory" +#define EEPROM_CHIPID_OFFSET 0x0 + +#define EEPROM_BAND0_STREAM_OFFSET 0x190 +#define EEPROM_BAND0_STREAM_TX_MASK 0x7 +#define EEPROM_BAND0_STREAM_TX_BIT_OFFSET 0 +#define EEPROM_BAND0_STREAM_RX_MASK 0x7 +#define EEPROM_BAND0_STREAM_RX_BIT_OFFSET 3 + +#define _TO_STR(_x) #_x +#define TO_STR(_x) _TO_STR(_x) +#define RED(_text) "\033[1;31m"_text"\033[0m" +#define GRN(_text) "\033[1;32m"_text"\033[0m" + +/******************************************************************************* +* D A T A T Y P E S +******************************************************************************** +*/ +bool one_adie_dbdc = false; +bool adie_7976 = false; +unsigned int adie_cfg_type = ADIE_TYPE_NONE; + +struct spi_op { + unsigned int busy_cr; + unsigned int polling_bit; + unsigned int addr_cr; + unsigned int read_addr_format; + unsigned int write_addr_format; + unsigned int write_data_cr; + unsigned int read_data_cr; + unsigned int read_data_mask; +}; + +/******************************************************************************* +* C O N S T A N T S +******************************************************************************** +*/ +const static char* g_spi_system_name[SYS_SPI_MAX] = { + "SYS_SPI_WF1", + "SYS_SPI_WF", + "SYS_SPI_BT", + "SYS_SPI_FM", + "SYS_SPI_GPS", + "SYS_SPI_TOP", + "SYS_SPI_WF2", + "SYS_SPI_WF3", +}; + +static const struct spi_op spi_op_array[SYS_SPI_MAX] = { + /* SYS_SPI_WF1 */ + { + SPI_STA, 1, SPI_WF_ADDR, 0x00001000, 0x00000000, + SPI_WF_WDAT, SPI_WF_RDAT, 0xFFFFFFFF + }, + /* SYS_SPI_WF */ + { + SPI_STA, 1, SPI_WF_ADDR, 0x00003000, 0x00002000, + SPI_WF_WDAT, SPI_WF_RDAT, 0xFFFFFFFF + }, + /* SYS_SPI_BT */ + { + SPI_STA, 2, SPI_BT_ADDR, 0x00005000, 0x00004000, + SPI_BT_WDAT, SPI_BT_RDAT, 0xFFFFFFFF + }, + /* SYS_SPI_FM */ + { + SPI_STA, 3, SPI_FM_ADDR, 0x00007000, 0x00006000, + SPI_FM_WDAT, SPI_FM_RDAT, 0x0000FFFF + }, + /* SYS_SPI_GPS */ + { + SPI_STA, 4, SPI_GPS_ADDR, 0x00009000, 0x00008000, + SPI_GPS_WDAT, SPI_GPS_RDAT, 0x0000FFFF + }, + /* SYS_SPI_TOP */ + { + SPI_STA, 5, SPI_TOP_ADDR, 0x0000B000, 0x0000A000, + SPI_TOP_WDAT, SPI_TOP_RDAT, 0xFFFFFFFF + }, + /* SYS_SPI_WF2 */ + { + SPI_STA, 1, SPI_WF_ADDR, 0x0000D000, 0x0000C000, + SPI_WF_WDAT, SPI_WF_RDAT, 0xFFFFFFFF + }, + /* SYS_SPI_WF3 */ + { + SPI_STA, 1, SPI_WF_ADDR, 0x0000F000, 0x0000E000, + SPI_WF_WDAT, SPI_WF_RDAT, 0xFFFFFFFF + }, +}; + +/******************************************************************************* +* F U N C T I O N D E C L A R A T I O N S +******************************************************************************** +*/ +bool _is_flash_content_valid(void) +{ + unsigned short eeFlashId = 0; + + FlashRead(MTD_WIFI_NM, (unsigned char*)&eeFlashId, EEPROM_CHIPID_OFFSET, sizeof(eeFlashId)); + + if (eeFlashId == consys_soc_chipid_get()) + return true; + else + return false; +} + +int _consys_check_adie_cfg(void) +{ + int ret = 0; + unsigned int hw_adie_type = 0; + unsigned int i = 0; + bool found = false; + + for (i = 0; i < AIDE_NUM_MAX; i++) { + if (conn_hw_env[i].valid) { + hw_adie_type = conn_hw_env[i].adie_id; + found = true; + break; + } + } + + if (found) { + printk(GRN("Adie Type: 0x%x"), hw_adie_type); + } else { + printk(RED("No Adie found!!!")); + ret = -1; + } + + return ret; +} + +int consys_plt_hw_init(void) +{ + /* Cheetah only has a-die 7976 and one-adie-dbdc */ + adie_7976 = true; + one_adie_dbdc = true; + adie_cfg_type = ADIE_TYPE_ONE; + pr_info("adie_cfg_type = %d, one_adie_dbdc = %d\n", adie_cfg_type, one_adie_dbdc); + return 0; +} + +int consys_xtal_ctrl_fast_mode(void) +{ + /* Setting fast mode to xtal control */ + CONSYS_SET_BIT(REG_TOP_MISC_ADDR + CONNSYS_MISC, (0x1 << 3)); + return 0; +} + +int consys_sw_reset_ctrl(bool bassert) +{ + /* Release CONNSYS software reset */ + if (bassert) { + CONSYS_REG_WRITE_MASK( + REG_TOP_RGU_ADDR + TOP_RGU_WDT_SWSYSRST, + 0x88800000, 0xff800000); + } else { + /* de-assert CONNSYS S/W reset */ + CONSYS_REG_WRITE_MASK( + REG_TOP_RGU_ADDR + TOP_RGU_WDT_SWSYSRST, + 0x88000000, 0xff800000); + } + + return 0; +} + +void consys_set_if_pinmux(bool enable) +{ + if (enable) { + /* One_Adie_DB + set pinmux for the interface between D-die and A-die (Aux1) + PAD_WF0_TOP_CLK(GPIO43) 0x0350[14:12] + PAD_WF0_TOP_DATA(GPIO44) 0x0350[18:16] + PAD_WF0_HB1(GPIO45) 0x0350[22:20] + PAD_WF0_HB2(GPIO46) 0x0350[26:24] + PAD_WF0_HB3(GPIO47) 0x0350[30:28] + PAD_WF0_HB4(GPIO48) 0x0360[2:0] + PAD_WF0_HB0(GPIO49) 0x0360[6:4] + PAD_WF0_HB0_B(GPIO50) 0x0360[10:8] + PAD_WF0_HB5(GPIO51) 0x0360[14:12] + PAD_WF0_HB6(GPIO52) 0x0360[18:16] + PAD_WF0_HB7(GPIO53) 0x0360[22:20] + PAD_WF0_HB8(GPIO54) 0x0360[26:24] + PAD_WF0_HB9(GPIO55) 0x0360[30:28] + PAD_WF0_HB10(GPIO56) 0x0370[2:0] + */ + CONSYS_REG_WRITE_MASK(REG_GPIO_BASE_ADDR + GPIO_MODE5, 0x11111000, 0x77777000); + CONSYS_REG_WRITE_MASK(REG_GPIO_BASE_ADDR + GPIO_MODE6, 0x11111111, 0x77777777); + CONSYS_REG_WRITE_MASK(REG_GPIO_BASE_ADDR + GPIO_MODE7, 0x00000001, 0x00000007); + + /* Set pinmux driving to 4mA */ + CONSYS_REG_WRITE_MASK(REG_IOCFG_TM_ADDR + IOCFG_TM_DRV_CFG0, 0x49249, 0x1FFFFF); + CONSYS_REG_WRITE_MASK(REG_IOCFG_LT_ADDR + IOCFG_LT_DRV_CFG0, 0x1249240, 0x7FFFFC0); + } +} + +int consys_tx_rx_bus_slp_prot_ctrl(bool enable) +{ + int check; + + if (enable) { + /* conn2ap/ap2conn slpprot disable */ + /* Turn off AP2CONN AHB RX bus sleep protect */ + CONSYS_REG_WRITE_MASK(REG_INFRACFG_AO_ADDR + AP2CONN_GALS_SLPPROT, 0x0, 0x10000); + CONSYS_REG_BIT_POLLING(REG_INFRACFG_AO_ADDR + AP2CONN_GALS_SLPPROT, + 24, 0x0, 100, 500, check); + if (check != 0) + pr_err("Polling AP2CONN AHB RX bus sleep protect turn off fail! CR Value = 0x%08x\n", + CONSYS_REG_READ(REG_INFRACFG_AO_ADDR + AP2CONN_GALS_SLPPROT)); + + /* Turn off AP2CONN AHB TX bus sleep protect */ + CONSYS_REG_WRITE_MASK(REG_INFRACFG_AO_ADDR + AP2CONN_GALS_SLPPROT, 0x0, 0x1); + CONSYS_REG_BIT_POLLING(REG_INFRACFG_AO_ADDR + AP2CONN_GALS_SLPPROT, + 4, 0x0, 100, 500, check); + if (check != 0) + pr_err("Polling AP2CONN AHB TX bus sleep protect turn off fail! CR Value = 0x%08x\n", + CONSYS_REG_READ(REG_INFRACFG_AO_ADDR + AP2CONN_GALS_SLPPROT)); + + /* Turn off CONN2AP AXI RX bus sleep protect */ + CONSYS_REG_WRITE_MASK(REG_INFRACFG_AO_ADDR + CONN2AP_GALS_SLPPROT, 0x0, 0x10000); + /* Turn off CONN2AP AXI TX bus sleep protect */ + CONSYS_REG_WRITE_MASK(REG_INFRACFG_AO_ADDR + CONN2AP_GALS_SLPPROT, 0x0, 0x1); + + /* Wait 900us (apply this for CONNSYS XO clock ready) */ + udelay(900); + } else { + /* Turn on AP2CONN AHB TX bus sleep protect */ + CONSYS_REG_WRITE_MASK(REG_INFRACFG_AO_ADDR + AP2CONN_GALS_SLPPROT, 0x1, 0x1); + CONSYS_REG_BIT_POLLING(REG_INFRACFG_AO_ADDR + AP2CONN_GALS_SLPPROT, + 4, 0x1, 100, 500, check); + if (check != 1) + pr_err("Polling AP2CONN AHB TX bus sleep protect turn on fail! CR Value = 0x%08x\n", + CONSYS_REG_READ(REG_INFRACFG_AO_ADDR + AP2CONN_GALS_SLPPROT)); + + /* Turn on AP2CONN AHB RX bus sleep protec */ + CONSYS_REG_WRITE_MASK(REG_INFRACFG_AO_ADDR + AP2CONN_GALS_SLPPROT, 0x1, 0x10000); + CONSYS_REG_BIT_POLLING(REG_INFRACFG_AO_ADDR + AP2CONN_GALS_SLPPROT, + 24, 0x1, 100, 500, check); + if (check !=1) + pr_err("Polling AP2CONN AHB RX bus sleep protect turn on fail! CR Value = 0x%08x\n", + CONSYS_REG_READ(REG_INFRACFG_AO_ADDR + AP2CONN_GALS_SLPPROT)); + + /* Turn on CONN2AP AXI TX bus sleep protect */ + CONSYS_REG_WRITE_MASK(REG_INFRACFG_AO_ADDR + CONN2AP_GALS_SLPPROT, 0x1, 0x1); + CONSYS_REG_BIT_POLLING(REG_INFRACFG_AO_ADDR + CONN2AP_GALS_SLPPROT, + 4, 0x1, 100, 500, check); + if (check != 1) + pr_err("Polling CONN2AP AXI TX bus sleep protect turn on fail! CR Value = 0x%08x\n", + CONSYS_REG_READ(REG_INFRACFG_AO_ADDR + CONN2AP_GALS_SLPPROT)); + + /* Turn on CONN2AP AXI RX bus sleep protect */ + CONSYS_REG_WRITE_MASK(REG_INFRACFG_AO_ADDR + CONN2AP_GALS_SLPPROT, 0x1, 0x10000); + CONSYS_REG_BIT_POLLING(REG_INFRACFG_AO_ADDR + CONN2AP_GALS_SLPPROT, + 24, 0x1, 100, 500, check); + if (check != 1) + pr_err("Polling CONN2AP AXI RX bus sleep protect turn on fail! CR Value = 0x%08x\n", + CONSYS_REG_READ(REG_INFRACFG_AO_ADDR + CONN2AP_GALS_SLPPROT)); + + /* wait 1us*/ + udelay(1); + } + + return 0; +} + +int _consys_polling_chipid_int(unsigned int retry, unsigned int sleep_ms) +{ + unsigned int count = retry + 1; + unsigned int consys_hw_ver = consys_get_hw_ver(); + unsigned int hw_ver = 0; + + while (--count > 0) { + hw_ver = CONSYS_REG_READ(REG_CONN_INFRA_CFG_ADDR + CONN_INFRA_CFG_IP_VERSION); + if ((hw_ver >= consys_hw_ver) && (hw_ver != 0xdeadfeed)) + break; + msleep(sleep_ms); + } + + if (count == 0) { + pr_err("Read CONNSYS HW IP version fail. Expect 0x%x but get 0x%x\n", consys_hw_ver, hw_ver); + return -1; + } else { + pr_info("Read CONNSYS HW IP version successfully! (0x%08x)\n", hw_ver); + } + + return 0; +} + +int consys_polling_chipid(void) +{ + return _consys_polling_chipid_int(10, 1); +} + +int consys_bus_clock_ctrl(enum consys_drv_type drv_type, unsigned int bus_clock) +{ + /* Cheetah doesn't need to do anything according to DE's excel */ + return 0; +} + +int consys_emi_set_remapping_reg(void) +{ + struct consys_emi_addr_info *addr_info = emi_mng_get_phy_addr(); + + /* 0x1806_01C4[19:0], ap_emi_base[19:0] = TBD (related to emi) + 0x1806_01D4[19:0], wf_ap_peri_base[19:0] = 0x0_1100 (un-related to emi) + */ + if (addr_info->emi_ap_phy_base != 0) + CONSYS_REG_WRITE_OFFSET_RANGE(REG_CONN_HOST_CSR_TOP_ADDR + CONN2AP_REMAP_MCU_EMI, + addr_info->emi_ap_phy_base, 0, 16, 20); + + CONSYS_REG_WRITE_OFFSET_RANGE(REG_CONN_HOST_CSR_TOP_ADDR + CONN2AP_REMAP_WF_PERI, + 0x300D0000, 0, 16, 20); + + CONSYS_REG_WRITE_OFFSET_RANGE(REG_CONN_HOST_CSR_TOP_ADDR + CONN2AP_RSVD_PERI_REGION1, + 0x11F20000, 0, 16, 20); + + return 0; +} + +int consys_emi_set_region_protection(void) +{ + struct consys_emi_addr_info *addr_info = emi_mng_get_phy_addr(); + + /* set infra top emi address range */ + if (addr_info->emi_ap_phy_base != 0) { + CONSYS_REG_WRITE(REG_CONN_INFRA_BUS_CR_ADDR + CONN2AP_EMI_PATH_ADDR_START, + addr_info->emi_ap_phy_base); + + if (addr_info->emi_ap_phy_size != 0) + CONSYS_REG_WRITE(REG_CONN_INFRA_BUS_CR_ADDR + CONN2AP_EMI_PATH_ADDR_END, + addr_info->emi_ap_phy_base + addr_info->emi_ap_phy_size); + } + + return 0; +} + +int connsys_d_die_cfg(void) +{ + unsigned int efuse; + + efuse = CONSYS_REG_READ(REG_CONN_INFRA_CFG_ADDR + EFUSE); + pr_info("D-die efuse: 0x%08x\n", efuse); + + return 0; +} + +int connsys_conninfra_sysram_hw_ctrl(void) +{ + /* conn_infra sysram hw control setting -> disable hw power down */ + CONSYS_REG_WRITE(REG_CONN_INFRA_RGU_ADDR + SYSRAM_HWCTL_PDN, 0x0); + + /* conn_infra sysram hw control setting -> enable hw sleep */ + CONSYS_REG_WRITE(REG_CONN_INFRA_RGU_ADDR + SYSRAM_HWCTL_SLP, 0x1); + + return 0; +} + +int connsys_spi_master_cfg(void) +{ + /* wt_slp CR for A-die ck_en/wake_en control */ + /* + RFSPI #0 RFSPI #1 + WF_CK_ADDR 0x18005070[11:0] 0x18085070[11:0] 0xA04 + WF_B1_CK_ADDR 0x18005070[27:16] 0x18085070[27:16] 0xAF4 + WF_WAKE_ADDR 0x18005074[11:0] 0x18085074[11:0] 0x090 + WF_B1_WAKE_ADDR 0x18005074[27:16] 0x18085074[27:16] 0x0A0 + WF_ZPS_ADDR 0x18005078[11:0] 0x18085078[11:0] 0x08C + WF_B1_ZPS_ADDR 0x18005078[27:16] 0x18085078[27:16] 0x09C + TOP_CK_ADDR 0x18005084[11:0] 0x18085084[11:0] 0xA00 + WF_B0_CMD_ADDR 0x1800508c[11:0] 0x1808508c[11:0] 0x0F0 + WF_B1_CMD_ADDR 0x18005090[11:0] 0x18085090[11:0] 0x0F4 + */ + CONSYS_REG_WRITE_MASK(REG_CONN_WT_SLP_CTL_REG_ADDR + WB_WF_CK_ADDR, 0xAF40A04, 0xFFF0FFF); + CONSYS_REG_WRITE_MASK(REG_CONN_WT_SLP_CTL_REG_ADDR + WB_WF_WAKE_ADDR, 0x0A00090, 0xFFF0FFF); + CONSYS_REG_WRITE_MASK(REG_CONN_WT_SLP_CTL_REG_ADDR + WB_WF_ZPS_ADDR, 0x09C008C, 0xFFF0FFF); + CONSYS_REG_WRITE_MASK(REG_CONN_WT_SLP_CTL_REG_ADDR + WB_TOP_CK_ADDR, 0xA00, 0xFFF); + CONSYS_REG_WRITE_MASK(REG_CONN_WT_SLP_CTL_REG_ADDR + WB_WF_B0_CMD_ADDR, 0x0F0, 0xFFF); + CONSYS_REG_WRITE_MASK(REG_CONN_WT_SLP_CTL_REG_ADDR + WB_WF_B1_CMD_ADDR, 0x0F4, 0xFFF); + + /* Cheetah doesn't need to configure RFSPI#1 */ + + return 0; +} + +static int consys_spi_read_nolock(enum sys_spi_subsystem subsystem, unsigned int addr, unsigned int *data) +{ + int check = 0; + unsigned long rf_spi_addr = 0; + const struct spi_op *op = NULL; + unsigned char adie_idx = ((subsystem & 0xF0) >> 4); //0: one adie, 1: two adie + unsigned char subsystem_idx = (subsystem & 0xF); + + if (!data) { + pr_err("invalid data ptr\n"); + return CONNINFRA_SPI_OP_FAIL; + } + + op = &spi_op_array[subsystem_idx]; + if (adie_idx != 0) + rf_spi_addr = REG_INST2_CONN_RF_SPI_MST_REG_ADDR; + else + rf_spi_addr = REG_CONN_RF_SPI_MST_REG_ADDR; + + /* Read action: + * 1. Polling busy_cr[polling_bit] should be 0 + * 2. Write addr_cr with data being {read_addr_format | addr[11:0]} + * 3. Trigger SPI by writing write_data_cr as 0 + * 4. Polling busy_cr[polling_bit] as 0 + * 5. Read data_cr[data_mask] + */ + + CONSYS_REG_BIT_POLLING(rf_spi_addr + op->busy_cr, op->polling_bit, 0, 100, 500, check); + if (check != 0) { + pr_err("[%d][STEP1] polling 0x%08lx bit %d fail. Value=0x%08x\n", + subsystem, rf_spi_addr + op->busy_cr, op->polling_bit, + CONSYS_REG_READ(rf_spi_addr + op->busy_cr)); + return CONNINFRA_SPI_OP_FAIL; + } + + CONSYS_REG_WRITE(rf_spi_addr + op->addr_cr, (op->read_addr_format | addr)); + CONSYS_REG_WRITE(rf_spi_addr + op->write_data_cr, 0); + + CONSYS_REG_BIT_POLLING(rf_spi_addr + op->busy_cr, op->polling_bit, 0, 100, 500, check); + if (check != 0) { + pr_err("[%d][STEP4] polling 0x%08lx bit %d fail. Value=0x%08x\n", + subsystem, rf_spi_addr + op->busy_cr, + op->polling_bit, CONSYS_REG_READ(rf_spi_addr + op->busy_cr)); + return CONNINFRA_SPI_OP_FAIL; + } + + check = CONSYS_REG_READ_BIT(rf_spi_addr + op->read_data_cr, op->read_data_mask); + *data = check; + + return 0; +} + +static int consys_spi_write_nolock(enum sys_spi_subsystem subsystem, unsigned int addr, unsigned int data) +{ + int check = 0; + unsigned long rf_spi_addr = 0; + const struct spi_op *op = NULL; + unsigned char adie_idx = ((subsystem & 0xF0) >> 4); //0: one adie, 1: two adie + unsigned char subsystem_idx = (subsystem & 0xF); + + op = &spi_op_array[subsystem_idx]; + if (adie_idx != 0) + rf_spi_addr = REG_INST2_CONN_RF_SPI_MST_REG_ADDR; + else + rf_spi_addr = REG_CONN_RF_SPI_MST_REG_ADDR; + + /* Write action: + * 1. Wait busy_cr[polling_bit] as 0 + * 2. Write addr_cr with data being {write_addr_format | addr[11:0] + * 3. Write write_data_cr ad data + * 4. Wait busy_cr[polling_bit] as 0 + */ + + CONSYS_REG_BIT_POLLING(rf_spi_addr + op->busy_cr, op->polling_bit, 0, 100, 500, check); + if (check != 0) { + pr_err("[%d][STEP1] polling 0x%08lx bit %d fail. Value=0x%08x\n", + subsystem, rf_spi_addr + op->busy_cr, + op->polling_bit, CONSYS_REG_READ(rf_spi_addr + op->busy_cr)); + return CONNINFRA_SPI_OP_FAIL; + } + + CONSYS_REG_WRITE(rf_spi_addr + op->addr_cr, (op->write_addr_format | addr)); + CONSYS_REG_WRITE(rf_spi_addr + op->write_data_cr, data); + + check = 0; + CONSYS_REG_BIT_POLLING(rf_spi_addr + op->busy_cr, op->polling_bit, 0, 100, 500, check); + if (check != 0) { + pr_err("[%d][STEP4] polling 0x%08lx bit %d fail. Value=0x%08x\n", + subsystem, rf_spi_addr + op->busy_cr, + op->polling_bit, CONSYS_REG_READ(rf_spi_addr + op->busy_cr)); + return CONNINFRA_SPI_OP_FAIL; + } + + pr_info("addr=0x%04x, val=0x%08x\n", addr, data); + + return 0; +} + +static int consys_sema_acquire(enum conn_semaphore_type index) +{ + if (CONSYS_REG_READ_BIT((REG_CONN_SEMAPHORE_ADDR + CONN_SEMA00_M2_OWN_STA + index*4), 0x1) == 0x1) { + return CONN_SEMA_GET_SUCCESS; + } else { + return CONN_SEMA_GET_FAIL; + } +} + +int consys_sema_acquire_timeout(unsigned int index, unsigned int usec) +{ + int i; + + if (index >= CONN_SEMA_NUM_MAX) { + pr_err("wrong index: %d\n", index); + return CONN_SEMA_GET_FAIL; + } + + for (i = 0; i < usec; i++) { + if (consys_sema_acquire(index) == CONN_SEMA_GET_SUCCESS) { + return CONN_SEMA_GET_SUCCESS; + } + udelay(1); + } + pr_err("Get semaphore 0x%x timeout, dump status:\n", index); + pr_err("M0:[0x%x] M1:[0x%x] M2:[0x%x] M3:[0x%x] M4:[0x%x] M5:[0x%x] M6:[0x%x] M7:[0x%x]\n", + CONSYS_REG_READ(REG_CONN_SEMAPHORE_ADDR + CONN_SEMA_OWN_BY_M0_STA_REP), + CONSYS_REG_READ(REG_CONN_SEMAPHORE_ADDR + CONN_SEMA_OWN_BY_M1_STA_REP), + CONSYS_REG_READ(REG_CONN_SEMAPHORE_ADDR + CONN_SEMA_OWN_BY_M2_STA_REP), + CONSYS_REG_READ(REG_CONN_SEMAPHORE_ADDR + CONN_SEMA_OWN_BY_M3_STA_REP), + CONSYS_REG_READ(REG_CONN_SEMAPHORE_ADDR + CONN_SEMA_OWN_BY_M4_STA_REP), + CONSYS_REG_READ(REG_CONN_SEMAPHORE_ADDR + CONN_SEMA_OWN_BY_M5_STA_REP), + CONSYS_REG_READ(REG_CONN_SEMAPHORE_ADDR + CONN_SEMA_OWN_BY_M6_STA_REP), + CONSYS_REG_READ(REG_CONN_SEMAPHORE_ADDR + CONN_SEMA_OWN_BY_M7_STA_REP)); + + return CONN_SEMA_GET_FAIL; +} + +void consys_sema_release(unsigned int index) +{ + if (index >= CONN_SEMA_NUM_MAX) { + pr_err("wrong index: %d\n", index); + return; + } + + CONSYS_REG_WRITE((REG_CONN_SEMAPHORE_ADDR + CONN_SEMA00_M2_OWN_REL + index*4), 0x1); +} + +int consys_spi_read(enum sys_spi_subsystem subsystem, unsigned int addr, unsigned int *data) +{ + int ret; + + /* Get semaphore before read */ + if (consys_sema_acquire_timeout(CONN_SEMA_RFSPI_INDEX, CONN_SEMA_TIMEOUT) == CONN_SEMA_GET_FAIL) { + pr_err("[SPI READ] Require semaphore fail\n"); + return CONNINFRA_SPI_OP_FAIL; + } + + ret = consys_spi_read_nolock(subsystem, addr, data); + + consys_sema_release(CONN_SEMA_RFSPI_INDEX); + + return ret; +} + +int consys_spi_write(enum sys_spi_subsystem subsystem, unsigned int addr, unsigned int data) +{ + int ret; + + /* Get semaphore before read */ + if (consys_sema_acquire_timeout(CONN_SEMA_RFSPI_INDEX, CONN_SEMA_TIMEOUT) == CONN_SEMA_GET_FAIL) { + pr_err("[SPI WRITE] Require semaphore fail\n"); + return CONNINFRA_SPI_OP_FAIL; + } + + ret = consys_spi_write_nolock(subsystem, addr, data); + + consys_sema_release(CONN_SEMA_RFSPI_INDEX); + return ret; +} + +static void consys_spi_write_offset_range_nolock( + enum sys_spi_subsystem subsystem, unsigned int addr, unsigned int value, + unsigned int reg_offset, unsigned int value_offset, unsigned int size) +{ + unsigned int data = 0, data2; + unsigned int reg_mask; + int ret; + + pr_info("[%s] addr=0x%04x value=0x%08x reg_offset=%d value_offset=%d size=%d\n", + g_spi_system_name[subsystem], addr, value, reg_offset, value_offset, size); + + value = (value >> value_offset); + value = GET_BIT_RANGE(value, size, 0); + value = (value << reg_offset); + ret = consys_spi_read_nolock(subsystem, addr, &data); + if (ret) { + pr_err("[%s] Get 0x%08x error, ret=%d\n", + g_spi_system_name[subsystem], addr, ret); + return; + } + + reg_mask = GENMASK(reg_offset + size - 1, reg_offset); + data2 = data & (~reg_mask); + data2 = (data2 | value); + consys_spi_write_nolock(subsystem, addr, data2); + + pr_info("[%s] Write CR:0x%08x from 0x%08x to 0x%08x\n", + g_spi_system_name[subsystem], addr, data, data2); +} + +int consys_spi_write_offset_range( + enum sys_spi_subsystem subsystem, unsigned int addr, unsigned int value, + unsigned int reg_offset, unsigned int value_offset, unsigned int size) +{ + if (consys_sema_acquire_timeout(CONN_SEMA_RFSPI_INDEX, CONN_SEMA_TIMEOUT) == CONN_SEMA_GET_FAIL) { + pr_err("[SPI READ] Require semaphore fail\n"); + return CONNINFRA_SPI_OP_FAIL; + } + consys_spi_write_offset_range_nolock(subsystem, addr, value, reg_offset, value_offset, size); + + consys_sema_release(CONN_SEMA_RFSPI_INDEX); + + return 0; +} + +/***************************************************************************** +* FUNCTION +* connsys_a_die_efuse_read +* DESCRIPTION +* Read a-die efuse +* PARAMETERS +* efuse_addr: read address +* RETURNS +* int +* 0: fail, efuse is invalid +* 1: success, efuse is valid +*****************************************************************************/ +static int connsys_a_die_efuse_read_nolock( + enum sys_spi_subsystem subsystem, unsigned int efuse_ctrl, unsigned int efuse_addr, + unsigned int *data0, unsigned int *data1, unsigned int *data2, unsigned int *data3) +{ + int ret = 0; + int retry = 0; + int ret0, ret1, ret2, ret3; + unsigned int efuse_block_sel; + + if (data0 == NULL || data1 == NULL || data2 == NULL || data3 == NULL) { + pr_err("invalid parameter (%p, %p, %p, %p)\n", + data0, data1, data2, data3); + return 0; + } + + switch (efuse_ctrl) { + case ATOP_EFUSE_CTRL_1: + efuse_block_sel = 0x1; + break; + + case ATOP_EFUSE_CTRL_2: + efuse_block_sel = 0x2; + break; + + case ATOP_EFUSE_CTRL_3: + efuse_block_sel = 0x4; + break; + + case ATOP_EFUSE_CTRL_4: + efuse_block_sel = 0x8; + break; + + default: + pr_err("Not support for efuse block No. = %d\n", efuse_ctrl); + return 0; + break; + } + + /* select Efuse block */ + consys_spi_write_nolock(subsystem, ATOP_RG_EFUSE_CFG5, efuse_block_sel); + + /* Efuse control clear, clear Status /trigger + * Address: ATOP EFUSE_CTRL_write_efsrom_kick_and_read_kick_busy_flag (0x108[30]) + * Data: 1'b0 + * Action: TOPSPI_WR + */ + consys_spi_read_nolock(subsystem, efuse_ctrl, &ret); + ret &= ~(0x1 << 30); + consys_spi_write_nolock(subsystem, efuse_ctrl, ret); + + /* Efuse Read 1st 16byte + * Address: + * ATOP EFUSE_CTRL_efsrom_mode (0x108[7:6]) = 2'b00 + * ATOP EFUSE_CTRL_efsrom_ain (0x108[25:16]) = efuse_addr (0) + * ATOP EFUSE_CTRL_write_efsrom_kick_and_read_kick_busy_flag (0x108[30]) = 1'b1 + * Action: TOPSPI_WR + */ + consys_spi_read_nolock(subsystem, efuse_ctrl, &ret); + ret &= ~(0x43FF00C0); + ret |= (0x1 << 30); + ret |= ((efuse_addr << 16) & 0x3FF0000); + consys_spi_write_nolock(subsystem, efuse_ctrl, ret); + + /* Polling EFUSE busy = low + * (each polling interval is "30us" and polling timeout is 2ms) + * Address: + * ATOP EFUSE_CTRL_write_efsrom_kick_and_read_kick_busy_flag (0x108[30]) = 1'b0 + * Action: TOPSPI_Polling + */ + consys_spi_read_nolock(subsystem, efuse_ctrl, &ret); + while ((ret & (0x1 << 30)) != 0 && retry < 70) { + retry++; + udelay(30); + consys_spi_read_nolock(subsystem, efuse_ctrl, &ret); + } + if ((ret & (0x1 << 30)) != 0) { + pr_err("EFUSE busy, retry failed(%d)\n", retry); + } + + /* Check efuse_valid & return + * Address: ATOP EFUSE_CTRL_csri_efsrom_dout_vld_sync_1_ (0x108[29]) + * Action: TOPSPI_RD + */ + /* if (efuse_valid == 1'b1) + * Read Efuse Data to global var + */ + consys_spi_read_nolock(subsystem, efuse_ctrl, &ret); + if (((ret & (0x1 << 29)) >> 29) == 1) { + ret0 = consys_spi_read_nolock(subsystem, ATOP_EFUSE_RDATA0, data0); + ret1 = consys_spi_read_nolock(subsystem, ATOP_EFUSE_RDATA1, data1); + ret2 = consys_spi_read_nolock(subsystem, ATOP_EFUSE_RDATA2, data2); + ret3 = consys_spi_read_nolock(subsystem, ATOP_EFUSE_RDATA3, data3); + + pr_info("efuse = [0x%08x, 0x%08x, 0x%08x, 0x%08x]\n", *data0, *data1, *data2, *data3); + if (ret0 || ret1 || ret2 || ret3) + pr_err("efuse read error: [%d, %d, %d, %d]\n", ret0, ret1, ret2, ret3); + ret = 1; + } else { + pr_err("EFUSE is invalid\n"); + ret = 0; + } + + return ret; +} + +static int _connsys_a_die_thermal_cal(enum sys_spi_subsystem subsystem) +{ + int efuse_valid = 0; + unsigned int efuse0 = 0, efuse1 = 0, efuse2 = 0, efuse3 = 0; + + /* thernal efuse data in 7976&7975 in EFUSE2 */ + efuse_valid = connsys_a_die_efuse_read_nolock(subsystem, ATOP_EFUSE_CTRL_2, ATOP_THADC_ANALOG, + &efuse0, &efuse1, &efuse2, &efuse3); + //if (efuse_valid) { + if ((efuse0 & (0x1 << 7))) { + consys_spi_write_offset_range_nolock(subsystem, ATOP_RG_TOP_THADC_BG, efuse0, 12, 3, 4); + consys_spi_write_offset_range_nolock(subsystem, ATOP_RG_TOP_THADC_00, efuse0, 23, 0, 3); + } + //} + + efuse_valid = connsys_a_die_efuse_read_nolock(subsystem, ATOP_EFUSE_CTRL_2, ATOP_THADC_SLOP, + &efuse0, &efuse1, &efuse2, &efuse3); + //if (efuse_valid) { + if((efuse0 & (0x1 << 7))) { + consys_spi_write_offset_range_nolock(subsystem, ATOP_RG_TOP_THADC_00, efuse0, 26, 5, 2); + } + //} + + return 0; +} + +static int _connsys_a_die_xtal_trim_7976(enum sys_spi_subsystem subsystem) +{ + unsigned int efuse0 = 0, efuse1 = 0, efuse2 = 0, efuse3 = 0; + int c1c2_trim_result_ax_80m = 0, c1c2_trim_result_ax_40m = 0; + unsigned int cbtop_strap_rdata = 0, xtal_strap_mode = 0, adie_rdata = 0, value = 0; + + connsys_a_die_efuse_read_nolock(subsystem, ATOP_EFUSE_CTRL_2, ATOP_XTAL_TRIM_FLOW, + &efuse0, &efuse1, &efuse2, &efuse3); + if ((efuse0 & (0x1 < 1))) { + /* C1C2 80M AX */ + connsys_a_die_efuse_read_nolock(subsystem, ATOP_EFUSE_CTRL_2, ATOP_XTAL_CR_C1_SEL_AXM_80M_OSC, + &efuse0, &efuse1, &efuse2, &efuse3); + if ((efuse0 & (0x1 < 7)) == 0) { + c1c2_trim_result_ax_80m = 64; + } else { + c1c2_trim_result_ax_80m = (efuse0 & 0x7F); + connsys_a_die_efuse_read_nolock(subsystem, ATOP_EFUSE_CTRL_2, ATOP_XTAL_CR_C1_SEL_AXM_TRIM1_80M_OSC, + &efuse0, &efuse1, &efuse2, &efuse3); + if ((efuse0 & (0x1 < 7)) == 1) { + if ((efuse0 & (0x1 < 6)) == 0) { + c1c2_trim_result_ax_80m += c1c2_trim_result_ax_80m + (efuse0 & 0x3F); + } else { + c1c2_trim_result_ax_80m = c1c2_trim_result_ax_80m - (efuse0 & 0x3F); + } + + if (c1c2_trim_result_ax_80m > 127) + c1c2_trim_result_ax_80m = 127; + else if (c1c2_trim_result_ax_80m < 0) + c1c2_trim_result_ax_80m = 0; + } + } + + /* C1C2 40M AX */ + connsys_a_die_efuse_read_nolock(subsystem, ATOP_EFUSE_CTRL_2, ATOP_XTAL_CR_C1_SEL_AXM_40M_OSC, + &efuse0, &efuse1, &efuse2, &efuse3); + if ((efuse0 & (0x1 < 7)) == 0) { + c1c2_trim_result_ax_40m = 64; + } else { + c1c2_trim_result_ax_40m = (efuse0 & 0x7F); + connsys_a_die_efuse_read_nolock(subsystem, ATOP_EFUSE_CTRL_2, ATOP_XTAL_CR_C1_SEL_AXM_TRIM1_40M_OSC, + &efuse0, &efuse1, &efuse2, &efuse3); + if ((efuse0 & (0x1 < 7)) == 1) { + if ((efuse0 & (0x1 < 6)) == 0) { + c1c2_trim_result_ax_40m += c1c2_trim_result_ax_40m + (efuse0 & 0x3F); + } else { + c1c2_trim_result_ax_40m = c1c2_trim_result_ax_40m - (efuse0 & 0x3F); + } + + if (c1c2_trim_result_ax_40m > 127) + c1c2_trim_result_ax_40m = 127; + else if (c1c2_trim_result_ax_40m < 0) + c1c2_trim_result_ax_40m = 0; + } + } + + /* Update trim value to C1 and C2 */ + consys_spi_read_nolock(subsystem, ATOP_RG_STRAP_PIN_IN, &cbtop_strap_rdata); + xtal_strap_mode = ((cbtop_strap_rdata & 0x70) >> 4); + if ((xtal_strap_mode == 0x0) || (xtal_strap_mode == 0x2)) { //80m osc + /* C1 */ + consys_spi_read_nolock(subsystem, 0x654, &adie_rdata); + value = (adie_rdata & 0xFFFFFF) | ((c1c2_trim_result_ax_80m & 0xFF) << 24); + consys_spi_write_nolock(subsystem, 0x654, value); + + /* C2 */ + consys_spi_read_nolock(subsystem, 0x658, &adie_rdata); + value = (adie_rdata & 0xFFFFFF) | ((c1c2_trim_result_ax_80m & 0xFF) << 24); + consys_spi_write_nolock(subsystem, 0x658, value); + } else if ((xtal_strap_mode == 0x3) || (xtal_strap_mode == 0x4) || (xtal_strap_mode == 0x6)) { //40m osc + /* C1 */ + consys_spi_read_nolock(subsystem, 0x654, &adie_rdata); + value = (adie_rdata & 0xFF00FFFF) | ((c1c2_trim_result_ax_40m & 0xFF) << 16); + consys_spi_write_nolock(subsystem, 0x654, value); + + /* C2 */ + consys_spi_read_nolock(subsystem, 0x658, &adie_rdata); + value = (adie_rdata & 0xFF00FFFF) | ((c1c2_trim_result_ax_40m & 0xFF) << 16); + consys_spi_write_nolock(subsystem, 0x658, value); + } + } + + return 0; +} + +static int _connsys_a_die_sw_cntl(enum sys_spi_subsystem subsystem, unsigned char adie_idx) +{ + if (conn_hw_env[adie_idx].valid && (conn_hw_env[adie_idx].adie_id == 0x7976)) { + if ((conn_hw_env[adie_idx].adie_hw_version == 0x8A00) + || (conn_hw_env[adie_idx].adie_hw_version == 0x8A10) + || (conn_hw_env[adie_idx].adie_hw_version == 0x8B00)){ + consys_spi_write_nolock(subsystem, ATOP_RG_TOP_THADC_00, 0x4A563B00); + consys_spi_write_nolock(subsystem, ATOP_RG_XO_01, 0x1D59080F); + consys_spi_write_nolock(subsystem, ATOP_RG_XO_03, 0x34C00FE0); + } else { + consys_spi_write_nolock(subsystem, ATOP_RG_TOP_THADC_00, 0x4A563B00); + consys_spi_write_nolock(subsystem, ATOP_RG_XO_01, 0x1959C80F); + consys_spi_write_nolock(subsystem, ATOP_RG_XO_03, 0x34D00FE0); + } + } + + return 0; +} + +int _connsys_a_die_cfg_7976(unsigned char adie_idx) +{ + int check; + unsigned int adie_chip_id = 0x0; + unsigned char subsystem = 0; + + if (adie_idx == 1) + subsystem = SYS_SPI_2ND_ADIE_TOP; + else + subsystem = SYS_SPI_TOP; + + /* release D Die to A Die Digital reset_b */ + if (adie_idx == 1) + CONSYS_SET_BIT(REG_CONN_INFRA_CFG_ADDR + ADIE_CTL, 0x4); + else + CONSYS_SET_BIT(REG_CONN_INFRA_CFG_ADDR + ADIE_CTL, 0x1); + + /* Get semaphore once */ + if (consys_sema_acquire_timeout(CONN_SEMA_RFSPI_INDEX, CONN_SEMA_TIMEOUT) == CONN_SEMA_GET_FAIL) { + pr_err("Require semaphore fail\n"); + return -1; + } + + /* read a-die ID */ + check = consys_spi_read_nolock(subsystem, ATOP_CHIP_ID, &adie_chip_id); + if (check) { + /* Release semaphore */ + consys_sema_release(CONN_SEMA_RFSPI_INDEX); + pr_err("Get ATOP_CHIP_ID fail, check=%d\n", check); + return -1; + } + + if (adie_idx < AIDE_NUM_MAX && ((adie_chip_id & 0xFFFF0000) != 0)) { + conn_hw_env[adie_idx].valid = true; + conn_hw_env[adie_idx].adie_hw_version = (adie_chip_id & 0xFFFF); + conn_hw_env[adie_idx].adie_id = ((adie_chip_id & 0xFFFF0000) >> 16); + conn_hw_env[adie_idx].is_rc_mode = 0; + + pr_info("adie_idx[%d], A-die CHIP ID = 0x%x, HW Version = 0x%x\n", + adie_idx, conn_hw_env[adie_idx].adie_id, conn_hw_env[adie_idx].adie_hw_version); + } + + /* enable TOPDIG CK */ + check = consys_spi_write_nolock(subsystem, ATOP_TOP_CLK_EN, 0xFFFFFFFF); + + /* config WRI CK select */ + if (one_adie_dbdc) + check = consys_spi_write_nolock(subsystem, ATOP_RG_WRI_CK_SELECT, 0x1C); + + /* Thermal Cal (TOP) */ + _connsys_a_die_thermal_cal(subsystem); + + /* XTAL TRIM */ + _connsys_a_die_xtal_trim_7976(subsystem); + + /* SW control part */ + _connsys_a_die_sw_cntl(subsystem, adie_idx); + + /* Release semaphore */ + consys_sema_release(CONN_SEMA_RFSPI_INDEX); + + return 0; +} + +static int _connsys_a_die_xtal_trim_7975(enum sys_spi_subsystem subsystem) +{ + unsigned int efuse0 = 0, efuse1 = 0, efuse2 = 0, efuse3 = 0; + unsigned int trim_result = 0, value = 0; + int ret = 0; + + ret = connsys_a_die_efuse_read_nolock(subsystem, ATOP_EFUSE_CTRL_2, ATOP_7975_XTAL_TRIM_FLOW, + &efuse0, &efuse1, &efuse2, &efuse3); + if (((efuse0 & 0x1) == 0) || (ret == 0)) + return 0; + + connsys_a_die_efuse_read_nolock(subsystem, ATOP_EFUSE_CTRL_2, ATOP_7975_XTAL_CALIBRATION, + &efuse0, &efuse1, &efuse2, &efuse3); + if ((efuse0 & (0x1 << 7))) { + trim_result = (efuse0 & 0x7F); + trim_result = (trim_result & 0x7F); + } + + connsys_a_die_efuse_read_nolock(subsystem, ATOP_EFUSE_CTRL_2, ATOP_7975_XTAL_TRIM2_COMPENSATION, + &efuse0, &efuse1, &efuse2, &efuse3); + if ((efuse0 & (0x1 << 7))){ + if ((efuse0 & (0x1 << 6))) + trim_result -= (efuse0 & 0x3F); + else + trim_result += (efuse0 & 0x3F); + trim_result = (trim_result & 0x7F); + } + + connsys_a_die_efuse_read_nolock(subsystem, ATOP_EFUSE_CTRL_2, ATOP_7975_XTAL_TRIM3_COMPENSATION, + &efuse0, &efuse1, &efuse2, &efuse3); + if ((efuse0 & (0x1 << 7))){ + if ((efuse0 & (0x1 << 6))) + trim_result -= (efuse0 & 0x3F); + else + trim_result += (efuse0 & 0x3F); + trim_result = (trim_result & 0x7F); + } + + connsys_a_die_efuse_read_nolock(subsystem, ATOP_EFUSE_CTRL_2, ATOP_7975_XTAL_TRIM4_COMPENSATION, + &efuse0, &efuse1, &efuse2, &efuse3); + if ((efuse0 & (0x1 << 7))){ + if ((efuse0 & (0x1 << 6))) + trim_result -= (efuse0 & 0x3F); + else + trim_result += (efuse0 & 0x3F); + trim_result = (trim_result & 0x7F); + } + + /* Update Trim Value to C1 and C2*/ + /* Step 1 */ + consys_spi_read_nolock(subsystem, ATOP_7975_CR_C1_C2_A94, &value); + value = ((value & 0xf8080fff) | ((trim_result << 20) | (trim_result << 12))); + consys_spi_write_nolock(subsystem, ATOP_7975_CR_C1_C2_A94, value); + + /* Step 2 */ + consys_spi_read_nolock(subsystem, ATOP_7975_CR_C1_C2_A18, &value); + if(value & (1<<29)){ + consys_spi_read_nolock(subsystem, ATOP_7975_CR_C1_C2_A84, &value); + value = (value & 0x7fffffff); + consys_spi_write_nolock(subsystem, ATOP_7975_CR_C1_C2_A84, value); + } + + /* Step 3 */ + consys_spi_read_nolock(subsystem, ATOP_7975_CR_C1_C2_AA4, &value); + value = ((value & 0xfffeffff) | 0x10000); + consys_spi_write_nolock(subsystem, ATOP_7975_CR_C1_C2_AA4, value); + + return 0; +} + + +static int _connsys_a_die_form_patch_7975(enum sys_spi_subsystem subsystem) +{ + pr_info("Form 7975 adie Patch\n"); + + /* disable CAL LDO and fine tune RFDIG LDO, 20191218 */ + consys_spi_write_nolock(subsystem, 0x348, 0x00000002); + + /* disable CAL LDO and fine tune RFDIG LDO, 20191218 */ + consys_spi_write_nolock(subsystem, 0x378, 0x00000002); + + /* disable CAL LDO and fine tune RFDIG LDO, 20191218 */ + consys_spi_write_nolock(subsystem, 0x3A8, 0x00000002); + + /* disable CAL LDO and fine tune RFDIG LDO, 20191218 */ + consys_spi_write_nolock(subsystem, 0x3D8, 0x00000002); + + /* set CKA driving and filter */ + consys_spi_write_nolock(subsystem, 0xA1C, 0x30000AAA); + + /* set CKB LDO to 1.4V */ + consys_spi_write_nolock(subsystem, 0xA84, 0x8470008A); + + /* turn on SX0 LTBUF */ + consys_spi_write_nolock(subsystem, 0x074, 0x00000007); + + /* CK_BUF_SW_EN=1 (all buf in manual mode.) */ + consys_spi_write_nolock(subsystem, 0xAA4, 0x01001FC0); + + /* BT mode/WF normal mode 32'h=00000005 */ + consys_spi_write_nolock(subsystem, 0x070, 0x00000005); + + /* BG thermal sensor offset update */ + consys_spi_write_nolock(subsystem, 0x344, 0x00000088); + + /* BG thermal sensor offset update */ + consys_spi_write_nolock(subsystem, 0x374, 0x00000088); + + /* BG thermal sensor offset update */ + consys_spi_write_nolock(subsystem, 0x3A4, 0x00000088); + + /* BG thermal sensor offset update */ + consys_spi_write_nolock(subsystem, 0x3D4, 0x00000088); + + /* set WCON VDD IPTAT to "0000" */ + consys_spi_write_nolock(subsystem, 0xA80, 0x44D07000); + + /* change back LTBUF SX3 drving to default value, 20191113 */ + consys_spi_write_nolock(subsystem, 0xA88, 0x3900AAAA); + + /* SM input cap off */ + consys_spi_write_nolock(subsystem, 0x2C4, 0x00000000); + + + return 0; +} + +int _connsys_a_die_cfg_7975(unsigned char adie_idx) +{ + int check; + unsigned int adie_chip_id = 0x0; + unsigned char subsystem = 0; + + if (adie_idx == 1) + subsystem = SYS_SPI_2ND_ADIE_TOP; + else + subsystem = SYS_SPI_TOP; + + /* release D Die to A Die Digital reset_b */ + if (adie_idx == 1) + CONSYS_SET_BIT(REG_CONN_INFRA_CFG_ADDR + ADIE_CTL, 0x4); + else + CONSYS_SET_BIT(REG_CONN_INFRA_CFG_ADDR + ADIE_CTL, 0x1); + + /* Get semaphore once */ + if (consys_sema_acquire_timeout(CONN_SEMA_RFSPI_INDEX, CONN_SEMA_TIMEOUT) == CONN_SEMA_GET_FAIL) { + pr_err("Require semaphore fail\n"); + return -1; + } + + /* read a-die ID */ + check = consys_spi_read_nolock(subsystem, ATOP_CHIP_ID, &adie_chip_id); + if (check) { + /* Release semaphore */ + consys_sema_release(CONN_SEMA_RFSPI_INDEX); + pr_err("Get ATOP_CHIP_ID fail, check=%d\n", check); + return -1; + } + + if (adie_idx < AIDE_NUM_MAX) { + conn_hw_env[adie_idx].valid = true; + conn_hw_env[adie_idx].adie_hw_version = (adie_chip_id & 0xFFFF); + conn_hw_env[adie_idx].adie_id = ((adie_chip_id & 0xFFFF0000) >> 16); + conn_hw_env[adie_idx].is_rc_mode = 0; + + pr_info("adie_idx[%d], A-die CHIP ID = 0x%x, HW Version = 0x%x\n", + adie_idx, conn_hw_env[adie_idx].adie_id, conn_hw_env[adie_idx].adie_hw_version); + } + + /* enable TOPDIG CK */ + check = consys_spi_write_nolock(subsystem, ATOP_TOP_CLK_EN, 0xFFFFFFFF); + + /* Thermal Cal (TOP) */ + _connsys_a_die_thermal_cal(subsystem); + + /* XTAL TRIM */ + _connsys_a_die_xtal_trim_7975(subsystem); + + /* Form Harrier E2 Patch */ + _connsys_a_die_form_patch_7975(subsystem); + + /* Release semaphore */ + consys_sema_release(CONN_SEMA_RFSPI_INDEX); + + return 0; +} + +int connsys_a_die_cfg(void) +{ + int ret; + memset(&conn_hw_env, 0, sizeof(conn_hw_env)); + + if (adie_7976){ + if (adie_cfg_type == ADIE_TYPE_TWO) { + ret = _connsys_a_die_cfg_7976(0); + ret = _connsys_a_die_cfg_7976(1); + } else { + if (one_adie_dbdc) { + ret = _connsys_a_die_cfg_7976(0); + } else { + ret = _connsys_a_die_cfg_7976(1); + } + } + } else { + if (adie_cfg_type == ADIE_TYPE_TWO) { + ret = _connsys_a_die_cfg_7975(0); + ret = _connsys_a_die_cfg_7975(1); + } else { + if (!one_adie_dbdc) + ret = _connsys_a_die_cfg_7975(1); + } + } + + return ret; +} + +int _connsys_afe_wbg_cal_7976(unsigned char wbg_idx, unsigned char rfspi_idx) +{ + int check; + unsigned long afe_ctl_addr = 0; + unsigned char subsystem = 0; + + if ((wbg_idx == 0) && (rfspi_idx == 0)) { + afe_ctl_addr = REG_CONN_AFE_CTL_ADDR; + subsystem = SYS_SPI_TOP; + } else if ((wbg_idx == 1) && (rfspi_idx == 0)) { + afe_ctl_addr = REG_CONN_AFE_CTL_2ND_ADDR; + subsystem = SYS_SPI_TOP; + } else if ((wbg_idx == 1) && (rfspi_idx == 1)) { + afe_ctl_addr = REG_CONN_AFE_CTL_2ND_ADDR; + subsystem = SYS_SPI_2ND_ADIE_TOP; + } else { + pr_err("Not support for this combination (wbg_idx=%d, rfspi_idx=%d)\n", + wbg_idx, rfspi_idx); + return -1; + } + + /* Get semaphore once */ + if (consys_sema_acquire_timeout(CONN_SEMA_RFSPI_INDEX, CONN_SEMA_TIMEOUT) == CONN_SEMA_GET_FAIL) { + pr_err("Require semaphore fail\n"); + return -1; + } + + /* set WF_PAD to HighZ */ + check = consys_spi_write_nolock(subsystem, ATOP_RG_ENCAL_WBTAC_IF_SW, 0x88888005); + + /* AFE WBG CAL SEQ1 (RC calibration) */ + CONSYS_SET_BIT(afe_ctl_addr + RG_DIG_EN_01, 0x1); + udelay(60); + CONSYS_CLR_BIT(afe_ctl_addr + RG_DIG_EN_01, 0x1); + + /* AFE WBG CAL SEQ2 (TX calibration) */ + CONSYS_SET_BIT(afe_ctl_addr + RG_DIG_EN_03, (0x1 << 21)); + udelay(30); + CONSYS_SET_BIT(afe_ctl_addr + RG_DIG_EN_03, (0x1 << 20)); + udelay(60); + CONSYS_SET_BIT(afe_ctl_addr + RG_DIG_EN_01, 0x203E0000); + udelay(800); + CONSYS_CLR_BIT(afe_ctl_addr + RG_DIG_EN_01, 0x203E0000); + CONSYS_CLR_BIT(afe_ctl_addr + RG_DIG_EN_03, 0x300000); + + /* disable WF_PAD to HighZ */ + check = consys_spi_write_nolock(subsystem, ATOP_RG_ENCAL_WBTAC_IF_SW, 0x00000005); + + /* Release semaphore */ + consys_sema_release(CONN_SEMA_RFSPI_INDEX); + + return 0; +} + +int _connsys_afe_wbg_cal_7975(unsigned char wbg_idx, unsigned char rfspi_idx) +{ + int check; + unsigned long afe_ctl_addr = 0; + unsigned char subsystem = 0; + + if ((wbg_idx == 0) && (rfspi_idx == 0)) { + afe_ctl_addr = REG_CONN_AFE_CTL_ADDR; + subsystem = SYS_SPI_TOP; + } else if ((wbg_idx == 1) && (rfspi_idx == 0)) { + afe_ctl_addr = REG_CONN_AFE_CTL_2ND_ADDR; + subsystem = SYS_SPI_TOP; + } else if ((wbg_idx == 1) && (rfspi_idx == 1)) { + afe_ctl_addr = REG_CONN_AFE_CTL_2ND_ADDR; + subsystem = SYS_SPI_2ND_ADIE_TOP; + } else { + pr_err("Not support for this combination (wbg_idx=%d, rfspi_idx=%d)\n", + wbg_idx, rfspi_idx); + return -1; + } + + /* Get semaphore once */ + if (consys_sema_acquire_timeout(CONN_SEMA_RFSPI_INDEX, CONN_SEMA_TIMEOUT) == CONN_SEMA_GET_FAIL) { + pr_err("Require semaphore fail\n"); + return -1; + } + + /* set WF_PAD to HighZ */ + check = consys_spi_write_nolock(subsystem, ATOP_RG_ENCAL_WBTAC_IF_SW, 0x80000000); + + /* AFE WBG CAL SEQ1 (RC calibration) */ + CONSYS_SET_BIT(afe_ctl_addr + RG_DIG_EN_01, 0x1); + udelay(60); + CONSYS_CLR_BIT(afe_ctl_addr + RG_DIG_EN_01, 0x1); + + /* AFE WBG CAL SEQ2 (TX calibration) */ + CONSYS_SET_BIT(afe_ctl_addr + RG_DIG_EN_03, (0x1 << 21)); + udelay(30); + CONSYS_SET_BIT(afe_ctl_addr + RG_DIG_EN_03, (0x1 << 20)); + udelay(60); + CONSYS_SET_BIT(afe_ctl_addr + RG_DIG_EN_01, 0x3E0000); + udelay(800); + CONSYS_CLR_BIT(afe_ctl_addr + RG_DIG_EN_01, 0x3E0000); + CONSYS_CLR_BIT(afe_ctl_addr + RG_DIG_EN_03, 0x300000); + + /* disable WF_PAD to HighZ */ + check = consys_spi_write_nolock(subsystem, ATOP_RG_ENCAL_WBTAC_IF_SW, 0x00000005); + + /* Release semaphore */ + consys_sema_release(CONN_SEMA_RFSPI_INDEX); + + return 0; +} + +int connsys_afe_wbg_cal(void) +{ + int ret; + + if (adie_7976){ + if (adie_cfg_type == ADIE_TYPE_TWO) { + ret = _connsys_afe_wbg_cal_7976(0, 0); + ret = _connsys_afe_wbg_cal_7976(1, 1); + } else { + if (one_adie_dbdc) { + ret = _connsys_afe_wbg_cal_7976(0, 0); + } else { + ret = _connsys_afe_wbg_cal_7976(1, 1); + } + } + } else { + if (adie_cfg_type == ADIE_TYPE_TWO) { + ret = _connsys_afe_wbg_cal_7975(0, 0); + ret = _connsys_afe_wbg_cal_7975(1, 1); + } else { + if (!one_adie_dbdc) + ret = _connsys_afe_wbg_cal_7975(1, 1); + } + } + + return ret; +} + +int _connsys_subsys_pll_initial(unsigned char wbg_idx) +{ + unsigned long afe_ctl_addr = 0; + + if (wbg_idx == 0) { + afe_ctl_addr = REG_CONN_AFE_CTL_ADDR; + } else if (wbg_idx == 1) { + afe_ctl_addr = REG_CONN_AFE_CTL_2ND_ADDR; + } else { + pr_err("Not support for this wbg index (wbg_idx=%d)\n", wbg_idx); + return -1; + } + + /* SWITCH(xtal_freq) + CASE SYS_XTAL_40000K + */ + /* set BPLL stable time = 30us (value = 30 * 1000 *1.01 / 25ns) */ + CONSYS_REG_WRITE_RANGE(afe_ctl_addr + RG_PLL_STB_TIME, (0x4BC << 16), 30, 16); + /* set WPLL stable time = 50us (value = 50 * 1000 *1.01 / 25ns) */ + CONSYS_REG_WRITE_RANGE(afe_ctl_addr + RG_PLL_STB_TIME, 0x7E4, 14, 0); + /* BT pll_en will turn on BPLL only (may change in different XTAL option) */ + CONSYS_REG_WRITE_RANGE(afe_ctl_addr + RG_DIG_EN_02, (0x1 << 6), 7, 6); + /* WF pll_en will turn on WPLL only (may change in different XTAL option) */ + CONSYS_REG_WRITE_RANGE(afe_ctl_addr + RG_DIG_EN_02, 0x2, 1, 0); + /* MCU pll_en will turn on BPLL (may change in different XTAL option) */ + CONSYS_REG_WRITE_RANGE(afe_ctl_addr + RG_DIG_EN_02, (0x1 << 2), 3, 2); + /* MCU pll_en will turn on BPLL + WPLL (may change in different XTAL option) */ + CONSYS_REG_WRITE_RANGE(afe_ctl_addr + RG_DIG_EN_02, (0x2 << 16), 17, 16); + /* CONN_INFRA CLKGEN WPLL AND BPLL REQUEST */ + CONSYS_REG_WRITE_RANGE(afe_ctl_addr + RG_DIG_TOP_01, (0x9 << 15), 18, 15); + + return 0; +} + +int connsys_subsys_pll_initial(void) +{ + int ret; + + ret = _connsys_subsys_pll_initial(0); + + return ret; +} + +int connsys_osc_legacy_mode(void) +{ + /* disable conn_top rc osc_ctrl_top */ + CONSYS_CLR_BIT(REG_CONN_INFRA_CFG_ADDR + CONN_INFRA_CFG_RC_CTL_0, 0x80); + CONSYS_REG_WRITE_RANGE(REG_CONN_INFRA_CFG_ADDR + OSC_CTL_0, 0x80706, 23, 0); + + return 0; +} + +int connsys_top_pwr_ctrl(void) +{ + /* prevent subsys from power on/of in a short time interval */ + CONSYS_CLR_BIT_WITH_KEY(REG_CONN_INFRA_RGU_ADDR + BGFYS_ON_TOP_PWR_CTL, 0x40, 0x42540000); + CONSYS_CLR_BIT_WITH_KEY(REG_CONN_INFRA_RGU_ADDR + WFSYS_ON_TOP_PWR_CTL, 0x40, 0x57460000); + + return 0; +} + +int connsys_conn_infra_bus_timeout(void) +{ + /* set conn_infra_off bus timeout */ + CONSYS_REG_WRITE_RANGE(REG_CONN_INFRA_BUS_CR_ADDR + CONN_INFRA_BUS_OFF_TIMEOUT_CTRL, (0x2 << 7), 14, 7); + /* enable conn_infra off bus timeout feature */ + CONSYS_REG_WRITE_RANGE(REG_CONN_INFRA_BUS_CR_ADDR + CONN_INFRA_BUS_OFF_TIMEOUT_CTRL, 0xF, 3, 0); + + /* set conn_infra_on bus timeout */ + CONSYS_REG_WRITE_RANGE(REG_CONN_INFRA_BUS_CR_ADDR + CONN_INFRA_BUS_ON_TIMEOUT_CTRL, (0xC << 7), 14, 7); + /* enable conn_infra_on bus timeout feature */ + CONSYS_REG_WRITE_RANGE(REG_CONN_INFRA_BUS_CR_ADDR + CONN_INFRA_BUS_ON_TIMEOUT_CTRL, 0xF, 3, 0); + + return 0; +} + +int connsys_clkgen_wpll_hw_ctrl(void) +{ + /* set hclk_div_1 with wpll div sel */ + CONSYS_REG_WRITE_MASK(REG_CONN_INFRA_CLKGEN_ON_TOP_ADDR + CKGEN_BUS_WPLL_DIV_1, 0x4, 0xFC); + + /* set hclk_div_2 with wpll div sel */ + CONSYS_REG_WRITE_MASK(REG_CONN_INFRA_CLKGEN_ON_TOP_ADDR + CKGEN_BUS_WPLL_DIV_2, 0x4, 0xFC); + +#ifndef CONFIG_FPGA_EARLY_PORTING + /* enable conn_infra bus wpll div_1 */ + CONSYS_SET_BIT(REG_CONN_INFRA_CLKGEN_ON_TOP_ADDR + CKGEN_BUS_WPLL_DIV_1, 0x1); + + /* enable conn_infra bus wpll div_2 */ + CONSYS_SET_BIT(REG_CONN_INFRA_CLKGEN_ON_TOP_ADDR + CKGEN_BUS_WPLL_DIV_2, 0x1); +#endif + +#ifndef CONFIG_FPGA_EARLY_PORTING + /* set rfspi wpll div sel + enable rfspis wpll div + */ + CONSYS_REG_WRITE_MASK(REG_CONN_INFRA_CLKGEN_ON_TOP_ADDR + CKGEN_RFSPI_WPLL_DIV, 0x21, 0xFD); +#else + /* set rfspi wpll div sel */ + CONSYS_REG_WRITE_MASK(REG_CONN_INFRA_CLKGEN_ON_TOP_ADDR + CKGEN_RFSPI_WPLL_DIV, 0x20, 0xFC); +#endif + + /* disable conn_infra bus clock sw control ==> conn_infra bus clock hw control */ + CONSYS_CLR_BIT(REG_CONN_INFRA_CLKGEN_ON_TOP_ADDR + CKGEN_BUS, 0x800000); + + /* Conn_infra HW_CONTROL => conn_infra enter dsleep mode */ + CONSYS_SET_BIT(REG_CONN_INFRA_CFG_ADDR + CONN_INFRA_CFG_PWRCTRL0, 0x1); + + return 0; +} + +int consys_conninfra_top_wakeup(void) +{ + /* wake up conn_infra */ + CONSYS_SET_BIT(REG_CONN_HOST_CSR_TOP_ADDR + CONN_INFRA_WAKEPU_TOP, 0x1); + + /* Wait 900us (apply this for CONNSYS XO clock ready) */ + udelay(900); + + /* Check CONNSYS version ID + * (polling "10 times" for specific project code and each polling interval is "1ms") + */ + if (consys_polling_chipid() != 0) { + pr_err("Polling chip id fail\n"); + return -1; + } + + return 0; +} + +int consys_conninfra_top_sleep(void) +{ + /* release conn_infra force on */ + CONSYS_CLR_BIT(REG_CONN_HOST_CSR_TOP_ADDR + CONN_INFRA_WAKEPU_TOP, 0x1); + + return 0; +} + +int _consys_adie_top_ck_en_on_off_ctrl(unsigned char rfspi_idx, enum consys_drv_type type, unsigned char on) +{ + int check = 0; + unsigned long slp_ctl_addr = 0; + + if (rfspi_idx == 1) + slp_ctl_addr = REG_INST2_CONN_WT_SLP_CTL_REG_ADDR; + else + slp_ctl_addr = REG_CONN_WT_SLP_CTL_REG_ADDR; + + if (type == CONNDRV_TYPE_CONNINFRA) { + if (on) + CONSYS_SET_BIT(slp_ctl_addr + WB_SLP_TOP_CK_0, 0x1); + else + CONSYS_CLR_BIT(slp_ctl_addr + WB_SLP_TOP_CK_0, 0x1); + CONSYS_REG_BIT_POLLING(slp_ctl_addr + WB_SLP_TOP_CK_0, 1, 0, 100, 500, check); + if (check == -1) + pr_err("[type=%d][on=%d] op= fail\n", type, on); + } else if (type == CONNDRV_TYPE_WIFI) { + if (on) + CONSYS_SET_BIT(slp_ctl_addr + WB_SLP_TOP_CK_1, 0x1); + else + CONSYS_CLR_BIT(slp_ctl_addr + WB_SLP_TOP_CK_1, 0x1); + CONSYS_REG_BIT_POLLING(slp_ctl_addr + WB_SLP_TOP_CK_1, 1, 0, 100, 500, check); + if (check == -1) + pr_err("[type=%d][on=%d] op= fail\n", type, on); + } else { + pr_err("Not support for this consys drv type = %d\n", type); + return -1; + } + + return 0; +} + +int consys_adie_top_ck_en_on_off_ctrl(enum consys_drv_type type, unsigned char on) +{ + int ret; + + if (consys_sema_acquire_timeout(CONN_SEMA_CONN_INFRA_COMMON_SYSRAM_INDEX, CONN_SEMA_TIMEOUT) == CONN_SEMA_GET_FAIL) { + pr_err("[type=%d] acquire semaphore (%d) timeout\n", + type, CONN_SEMA_CONN_INFRA_COMMON_SYSRAM_INDEX); + return -1; + } + + if (adie_cfg_type == ADIE_TYPE_TWO) { + ret = _consys_adie_top_ck_en_on_off_ctrl(0, type, on); + ret = _consys_adie_top_ck_en_on_off_ctrl(1, type, on); + } else { + if (one_adie_dbdc) { + ret = _consys_adie_top_ck_en_on_off_ctrl(0, type, on); + } else { + ret = _consys_adie_top_ck_en_on_off_ctrl(1, type, on); + } + } + + consys_sema_release(CONN_SEMA_CONN_INFRA_COMMON_SYSRAM_INDEX); + + return ret; +} + +int consys_conninfra_wf_wakeup(void) +{ + /* wake up conn_infra */ + CONSYS_SET_BIT(REG_CONN_HOST_CSR_TOP_ADDR + CONN_INFRA_WAKEPU_WF, 0x1); + + /* Wait 900us (apply this for CONNSYS XO clock ready) */ + udelay(900); + + /* Check CONNSYS version ID + * (polling "10 times" for specific project code and each polling interval is "1ms") + */ + if (consys_polling_chipid() != 0) { + pr_err("Polling chip id fail\n"); + return -1; + } + + return 0; +} + +int consys_conninfra_wf_sleep(void) +{ + CONSYS_CLR_BIT(REG_CONN_HOST_CSR_TOP_ADDR + CONN_INFRA_WAKEPU_WF, 0x1); + + return 0; +} + +int consys_conn_wmcpu_sw_reset(bool bassert) +{ + if (bassert) + CONSYS_CLR_BIT(REG_CONN_INFRA_RGU_ADDR + WFSYS_CPU_SW_RST_B, 0x1); + else + CONSYS_SET_BIT(REG_CONN_INFRA_RGU_ADDR + WFSYS_CPU_SW_RST_B, 0x1); + + return 0; +} + +int consys_wf_bus_slp_prot_ctrl(bool enable) +{ + /* Turn on/off "conn_infra to wfsys"/wfsys to conn_infra/wfdma2conn" bus sleep protect */ + + if (enable) + CONSYS_SET_BIT(REG_CONN_INFRA_CFG_ADDR + CONN_INFRA_WF_SLP_CTRL, 0x1); + else + CONSYS_CLR_BIT(REG_CONN_INFRA_CFG_ADDR + CONN_INFRA_WF_SLP_CTRL, 0x1); + + return 0; +} + +int consys_wfsys_top_on_ctrl(bool enable) +{ + int check = 0; + + if (enable) { + /* turn on wfsys_top_on */ + CONSYS_SET_BIT(REG_CONN_INFRA_RGU_ADDR + WFSYS_ON_TOP_PWR_CTL, 0x57460080); + + /* polling wfsys_rgu_off_hreset_rst_b */ + CONSYS_REG_BIT_POLLING(REG_CONN_HOST_CSR_TOP_ADDR + DBG_DUMMY_3, 30, 1, 100, 500, check); + if (check == -1) + pr_err("[%d] polling wfsys_rgu_off_hreset_rst_b fail\n", enable); + } else { + /* turn off wfsys_top_on */ + CONSYS_CLR_BIT_WITH_KEY(REG_CONN_INFRA_RGU_ADDR + WFSYS_ON_TOP_PWR_CTL, 0x80, 0x57460000); + + /* polling wfsys_rgu_off_hreset_rst_b */ + CONSYS_REG_BIT_POLLING(REG_CONN_HOST_CSR_TOP_ADDR + DBG_DUMMY_3, 30, 0, 100, 500, check); + if (check == -1) + pr_err("[%d] polling wfsys_rgu_off_hreset_rst_b fail\n", enable); + } + + return 0; +} + +int consys_wfsys_bus_slp_prot_check(bool enable) +{ + int check = 0; + + if (enable) { + /* check "conn_infra to wfsys"/wfsys to conn_infra" bus sleep protect turn off */ + CONSYS_REG_BIT_POLLING(REG_CONN_INFRA_CFG_ADDR + CONN_INFRA_WF_SLP_STATUS, 29, 0, 100, 500, check); + if (check == -1) + pr_err("[bit %d] check conn_infra to wfsys or wfsys to conn_infra bus sleep protect turn off fail\n", 29); + + CONSYS_REG_BIT_POLLING(REG_CONN_INFRA_CFG_ADDR + CONN_INFRA_WF_SLP_STATUS, 31, 0, 100, 500, check); + if (check == -1) + pr_err("[bit %d] check conn_infra to wfsys or wfsys to conn_infra bus sleep protect turn off fail\n", 31); + + /* check WFDMA2CONN AXI TX bus sleep protect turn off */ + CONSYS_REG_BIT_POLLING(REG_WF_TOP_SLPPROT_ON_ADDR + WF_TOP_SLPPROT_ON_STATUS_READ, 23, 0, 100, 500, check); + if (check == -1) + pr_err("check WFDMA2CONN AXI TX bus sleep protect turn off fail\n"); + + /* check WFDMA2CONN AXI RX bus sleep protect turn off */ + CONSYS_REG_BIT_POLLING(REG_WF_TOP_SLPPROT_ON_ADDR + WF_TOP_SLPPROT_ON_STATUS_READ, 21, 0, 100, 500, check); + if (check == -1) + pr_err("check WFDMA2CONN AXI RX bus sleep protect turn off fail\n"); + + /* check WFSYS version ID */ + CONSYS_REG_POLLING_LARGER_OR_EQUAL(REG_WF_TOP_CFG_ADDR + WF_TOP_CFG_IP_VERSION, 0xFFFFFFFF, 0, 0x02060000, 10, 500, check); + if (check == -1) + pr_err("check WFSYS version ID fail\n"); + } else { + /* check WFDMA2CONN AXI RX bus sleep protect turn on */ + CONSYS_REG_BIT_POLLING(REG_CONN_INFRA_CFG_ADDR + CONN_INFRA_WF_SLP_STATUS, 25, 1, 100, 500, check); + if (check == -1) + pr_err("check WFDMA2CONN AXI RX bus sleep protect turn on fail\n"); + + /* check "conn_infra to wfsys"/wfsys to conn_infra" bus sleep protect turn on */ + CONSYS_REG_BIT_POLLING(REG_CONN_INFRA_CFG_ADDR + CONN_INFRA_WF_SLP_STATUS, 29, 1, 100, 500, check); + if (check == -1) + pr_err("[bit %d] check conn_infra to wfsys or wfsys to conn_infra bus sleep protect turn on fail\n", 29); + + CONSYS_REG_BIT_POLLING(REG_CONN_INFRA_CFG_ADDR + CONN_INFRA_WF_SLP_STATUS, 31, 1, 100, 500, check); + if (check == -1) + pr_err("[bit %d] check conn_infra to wfsys or wfsys to conn_infra bus sleep protect turn on fail\n", 31); + } + + return 0; +} + +int consys_wfsys_bus_timeout_ctrl(void) +{ + /* set wfsys bus timeout value (ahb apb timeout) */ + CONSYS_REG_WRITE_MASK(REG_WF_MCU_CONFIG_LS_ADDR + BUSHANGCR, 0x1, 0xFF); + + /* enable wfsys bus timeout (ahb apb timeout) */ + CONSYS_SET_BIT(REG_WF_MCU_CONFIG_LS_ADDR + BUSHANGCR, 0x90000000); + + /* set conn2wf remapping window to wf debug_ctrl_ao CR */ + CONSYS_REG_WRITE(REG_WF_MCU_BUS_CR_ADDR + AP2WF_REMAP_1, 0x810F0000); + + /* set wfsys bus timeout value (debug ctrl ao) */ + CONSYS_REG_WRITE_MASK(REG_WF_MCUSYS_INFRA_BUS_FULL_U_DEBUG_CTRL_AO_ADDR + WF_MCUSYS_INFRA_BUS_FULL_U_DEBUG_CTRL_AO_WFMCU_PWA_CTRL0, + 0x03AA0000, 0xFFFF0000); + + /* enable wfsys bus timeout (debug ctrl ao) */ + CONSYS_SET_BIT(REG_WF_MCUSYS_INFRA_BUS_FULL_U_DEBUG_CTRL_AO_ADDR + WF_MCUSYS_INFRA_BUS_FULL_U_DEBUG_CTRL_AO_WFMCU_PWA_CTRL0, 0xC); + + return 0; +} + +int consys_wmcpu_idle_loop_check(void) +{ + int check = 0; + + /* check CONNSYS power-on completion */ + CONSYS_REG_POLLING_EQUAL(REG_WF_TOP_CFG_ON_ADDR + ROMCODE_INDEX, 0xFFFFFFFF, 0, 0x1D1E, 5000, 1000, check); + if (check == -1) + pr_err("check CONNSYS power-on completion fail\n"); + + return 0; +} + +void _consys_check_sku_cfg(void) +{ + unsigned int hw_sku_type = 0; + + if (one_adie_dbdc) { + if (adie_cfg_type == ADIE_TYPE_ONE) + hw_sku_type = 3000; + } + + if (hw_sku_type) + printk(GRN("SKU Type: %d"), hw_sku_type); + else + printk(GRN("Unknown SKU Type\n")); +} + +int consys_plt_adie_type_cfg(void) +{ + /* + If One_Adie_DB: + then TOP_MISC_CR (0x11D1_021C[31:28]) = 0x7 && 0x18050000 = 0x7 + */ + + if (one_adie_dbdc) { + if (adie_cfg_type == ADIE_TYPE_ONE) { + CONSYS_REG_WRITE_MASK(REG_TOP_MISC_ADDR + TOP_MISC_RSRV_ALL1_3, 0x70000000, 0xF0000000); + CONSYS_REG_WRITE(REG_CONN_INFRA_SYSRAM_ADDR + SYSRAM_BASE_ADDR, 0x7); + } + } + + if (_consys_check_adie_cfg() == 0) + _consys_check_sku_cfg(); + + return 0; +} + +int consys_wpll_ctrl(bool enable) +{ + if (enable) { + /* turn back wpll setting in conn_afe_ctl by setting wpll initial control to 2'b10 */ + CONSYS_REG_WRITE_MASK(REG_CONN_AFE_CTL_ADDR + RG_DIG_EN_02, 0x20002, 0x30003); + } else { + /* Don't need below code check anymore due to new design */ +#if 0 + int check = 0; + /* trun off wpll enable in conn_afe_ctl by setting wpll initial control to 2'b00 */ + CONSYS_REG_WRITE_MASK(REG_CONN_AFE_CTL_ADDR + RG_DIG_EN_02, 0x0, 0x30003); + + /* polling conn_infra bus to non-wpll case */ + CONSYS_REG_POLLING_EQUAL(REG_CONN_INFRA_CLKGEN_ON_TOP_ADDR + CKGEN_BUS, 0x7800, 11, 0x0, 5000, 1000, check); + if (check == -1) + pr_err("polling conn_infra bus to non-wpll case fail\n"); +#endif + } + return 0; +} + +int consys_conninfra_wf_req_clr(void) +{ + /* clear wf_emi_req */ + CONSYS_SET_BIT(REG_CONN_INFRA_CFG_ADDR + EMI_CTL_WF, 0x1); + CONSYS_CLR_BIT(REG_CONN_INFRA_CFG_ADDR + EMI_CTL_WF, 0x1); + + /* clear wf_infra_req */ + CONSYS_SET_BIT(REG_CONN_INFRA_CFG_ADDR + EMI_CTL_WF, 0x20); + CONSYS_CLR_BIT(REG_CONN_INFRA_CFG_ADDR + EMI_CTL_WF, 0x20); + + return 0; +} + diff --git a/package/mtk/drivers/conninfra/src/platform/mt7986/include/mt7986.h b/package/mtk/drivers/conninfra/src/platform/mt7986/include/mt7986.h new file mode 100644 index 0000000000..a8dc0a2a34 --- /dev/null +++ b/package/mtk/drivers/conninfra/src/platform/mt7986/include/mt7986.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2016 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 http://www.gnu.org/licenses/gpl-2.0.html for more details. + */ +/*! \file +* \brief Declaration of library functions +* +* Any definitions in this file will be shared among GLUE Layer and internal Driver Stack. +*/ + +#ifndef _PLATFORM_MT7986_H_ +#define _PLATFORM_MT7986_H_ + +enum +{ + ADIE_TYPE_NONE = 0, + ADIE_TYPE_ONE, + ADIE_TYPE_TWO, + ADIE_TYPE_NUM_MAX +}; + +enum conn_semaphore_type +{ + CONN_SEMA_CHIP_POWER_ON_INDEX = 0, + CONN_SEMA_CALIBRATION_INDEX = 1, + CONN_SEMA_FW_DL_INDEX = 2, + CONN_SEMA_CLOCK_SWITCH_INDEX = 3, + CONN_SEMA_CCIF_INDEX = 4, + CONN_SEMA_COEX_INDEX = 5, + CONN_SEMA_USB_EP0_INDEX = 6, + CONN_SEMA_USB_SHARED_INFO_INDEX = 7, + CONN_SEMA_USB_SUSPEND_INDEX = 8, + CONN_SEMA_USB_RESUME_INDEX = 9, + CONN_SEMA_PCIE_INDEX = 10, + CONN_SEMA_RFSPI_INDEX = 11, + CONN_SEMA_EFUSE_INDEX = 12, + CONN_SEMA_THERMAL_INDEX = 13, + CONN_SEMA_FLASH_INDEX = 14, + CONN_SEMA_DEBUG_INDEX = 15, + CONN_SEMA_WIFI_LP_INDEX = 16, + CONN_SEMA_PATCH_DL_INDEX = 17, + CONN_SEMA_SHARED_VAR_INDEX = 18, + CONN_SEMA_CONN_INFRA_COMMON_SYSRAM_INDEX = 19, + CONN_SEMA_NUM_MAX = 32 /* can't be omitted */ +}; + +unsigned int consys_soc_chipid_get(void); +unsigned int consys_get_hw_ver(void); + + +#endif /* _PLATFORM_MT7986_H_ */ diff --git a/package/mtk/drivers/conninfra/src/platform/mt7986/include/mt7986_consys_reg.h b/package/mtk/drivers/conninfra/src/platform/mt7986/include/mt7986_consys_reg.h new file mode 100644 index 0000000000..475e6f06d2 --- /dev/null +++ b/package/mtk/drivers/conninfra/src/platform/mt7986/include/mt7986_consys_reg.h @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2016 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 http://www.gnu.org/licenses/gpl-2.0.html for more details. + */ +/*! \file +* \brief Declaration of library functions +* +* Any definitions in this file will be shared among GLUE Layer and internal Driver Stack. +*/ + +#ifndef _PLATFORM_MT7986_CONSYS_REG_H_ +#define _PLATFORM_MT7986_CONSYS_REG_H_ + +#include "consys_reg_base.h" +/******************************************************************************* +* C O M P I L E R F L A G S +******************************************************************************** +*/ + +/******************************************************************************* +* M A C R O S +******************************************************************************** +*/ + + +/******************************************************************************* +* E X T E R N A L R E F E R E N C E S +******************************************************************************** +*/ + +/******************************************************************************* +* C O N S T A N T S +******************************************************************************** +*/ + +/******************************************************************************* +* D A T A T Y P E S +******************************************************************************** +*/ +enum consys_base_addr_index { + TOP_MISC_BASE = 0, /* top_misc */ + TOPRGU_BASE = 1, /* TOPRGU */ + GPIO_BASE = 2, /* GPIO */ + IOCFG_TR_BASE = 3, /* IOCFG_TR */ + IOCFG_TL_BASE = 4, /* IOCFG_TL */ + INFRACFG_AO_BASE = 5, /* infracfg_ao_auto_gen_reg */ + CONN_INFRA_CFG_BASE = 6, /* conn_infra_cfg */ + CONN_INFRA_SYSRAM_BASE = 7, /* conn_infra_sysram */ + CONN_INFRA_CLKGEN_ON_TOP_BASE = 8, /* conn_infra_clkgen_on_top */ + CONN_HOST_CSR_TOP_BASE = 9, /* conn_host_csr_top */ + CONN_INFRA_BUS_CR_BASE = 10, /* conn_infra_bus_cr */ + CONN_INFRA_RGU_BASE = 11, /* conn_infra_rgu */ + CONN_WT_SLP_CTL_REG_BASE = 12, /* conn_wt_slp_ctl_reg */ + INST2_CONN_WT_SLP_CTL_REG_BASE = 13, /* Inst2_conn_wt_slp_ctl_reg */ + CONN_RF_SPI_MST_REG_BASE = 14, /* conn_rf_spi_mst_reg */ + INST2_CONN_RF_SPI_MST_REG_BASE = 15, /* Inst2_conn_rf_spi_mst_reg */ + CONN_SEMAPHORE_BASE = 16, /* conn_semaphore */ + CONN_AFE_CTL_BASE = 17, /* conn_afe_ctl */ + CONN_AFE_CTL_2ND_BASE = 18, /* conn_afe_ctl_2nd */ + WF_TOP_SLPPROT_ON_BASE = 19, /* wf_top_slpprot_on by remapping to 0x81020000 */ + WF_TOP_CFG_BASE = 20, /* wf_top_cfg by remapping to 0x80020000 */ + WF_MCU_CONFIG_LS_BASE = 21, /* wf_mcu_confg_ls by remapping to 0x88000000 */ + WF_MCU_BUS_CR_BASE = 22, /* wf_mcu_bus_cr by remapping to 0x830C0XXX */ + WF_MCUSYS_INFRA_BUS_FULL_U_DEBUG_CTRL_AO_BASE = 23, /* wf_mcusys_infra_bus_full_u_debug_ctrl_ao by remapping to 0x810F0000 */ + WF_TOP_CFG_ON_BASE = 24, /* wf_top_cfg_on by remapping to 0x81021000 */ + CONSYS_BASE_ADDR_MAX +}; + +struct consys_base_addr { + struct consys_reg_base_addr reg_base_addr[CONSYS_BASE_ADDR_MAX]; +}; + +extern struct consys_base_addr conn_reg; + +#define REG_TOP_MISC_ADDR conn_reg.reg_base_addr[TOP_MISC_BASE].vir_addr +#define REG_TOP_RGU_ADDR conn_reg.reg_base_addr[TOPRGU_BASE].vir_addr +#define REG_GPIO_BASE_ADDR conn_reg.reg_base_addr[GPIO_BASE].vir_addr +#define REG_IOCFG_TR_ADDR conn_reg.reg_base_addr[IOCFG_TR_BASE].vir_addr +#define REG_IOCFG_TL_ADDR conn_reg.reg_base_addr[IOCFG_TL_BASE].vir_addr +#define REG_INFRACFG_AO_ADDR conn_reg.reg_base_addr[INFRACFG_AO_BASE].vir_addr +#define REG_CONN_INFRA_CFG_ADDR conn_reg.reg_base_addr[CONN_INFRA_CFG_BASE].vir_addr +#define REG_CONN_INFRA_SYSRAM_ADDR conn_reg.reg_base_addr[CONN_INFRA_SYSRAM_BASE].vir_addr +#define REG_CONN_INFRA_CLKGEN_ON_TOP_ADDR conn_reg.reg_base_addr[CONN_INFRA_CLKGEN_ON_TOP_BASE].vir_addr +#define REG_CONN_HOST_CSR_TOP_ADDR conn_reg.reg_base_addr[CONN_HOST_CSR_TOP_BASE].vir_addr +#define REG_CONN_INFRA_BUS_CR_ADDR conn_reg.reg_base_addr[CONN_INFRA_BUS_CR_BASE].vir_addr +#define REG_CONN_INFRA_RGU_ADDR conn_reg.reg_base_addr[CONN_INFRA_RGU_BASE].vir_addr +#define REG_CONN_WT_SLP_CTL_REG_ADDR conn_reg.reg_base_addr[CONN_WT_SLP_CTL_REG_BASE].vir_addr +#define REG_INST2_CONN_WT_SLP_CTL_REG_ADDR conn_reg.reg_base_addr[INST2_CONN_WT_SLP_CTL_REG_BASE].vir_addr +#define REG_CONN_RF_SPI_MST_REG_ADDR conn_reg.reg_base_addr[CONN_RF_SPI_MST_REG_BASE].vir_addr +#define REG_INST2_CONN_RF_SPI_MST_REG_ADDR conn_reg.reg_base_addr[INST2_CONN_RF_SPI_MST_REG_BASE].vir_addr +#define REG_CONN_SEMAPHORE_ADDR conn_reg.reg_base_addr[CONN_SEMAPHORE_BASE].vir_addr +#define REG_CONN_AFE_CTL_ADDR conn_reg.reg_base_addr[CONN_AFE_CTL_BASE].vir_addr +#define REG_CONN_AFE_CTL_2ND_ADDR conn_reg.reg_base_addr[CONN_AFE_CTL_2ND_BASE].vir_addr +#define REG_WF_TOP_SLPPROT_ON_ADDR conn_reg.reg_base_addr[WF_TOP_SLPPROT_ON_BASE].vir_addr +#define REG_WF_TOP_CFG_ADDR conn_reg.reg_base_addr[WF_TOP_CFG_BASE].vir_addr +#define REG_WF_MCU_CONFIG_LS_ADDR conn_reg.reg_base_addr[WF_MCU_CONFIG_LS_BASE].vir_addr +#define REG_WF_MCU_BUS_CR_ADDR conn_reg.reg_base_addr[WF_MCU_BUS_CR_BASE].vir_addr +#define REG_WF_MCUSYS_INFRA_BUS_FULL_U_DEBUG_CTRL_AO_ADDR conn_reg.reg_base_addr[WF_MCUSYS_INFRA_BUS_FULL_U_DEBUG_CTRL_AO_BASE].vir_addr +#define REG_WF_TOP_CFG_ON_ADDR conn_reg.reg_base_addr[WF_TOP_CFG_ON_BASE].vir_addr + +/******************************************************************************* +* P U B L I C D A T A +******************************************************************************** +*/ + +/******************************************************************************* +* P R I V A T E D A T A +******************************************************************************** +*/ + +/******************************************************************************* +* F U N C T I O N D E C L A R A T I O N S +******************************************************************************** +*/ + + +/******************************************************************************* +* F U N C T I O N S +******************************************************************************** +*/ + +struct consys_base_addr* get_conn_reg_base_addr(void); + +#endif /* _PLATFORM_MT7986_CONSYS_REG_H_ */ diff --git a/package/mtk/drivers/conninfra/src/platform/mt7986/include/mt7986_consys_reg_offset.h b/package/mtk/drivers/conninfra/src/platform/mt7986/include/mt7986_consys_reg_offset.h new file mode 100644 index 0000000000..19e4a855e3 --- /dev/null +++ b/package/mtk/drivers/conninfra/src/platform/mt7986/include/mt7986_consys_reg_offset.h @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2016 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 http://www.gnu.org/licenses/gpl-2.0.html for more details. + */ +/*! \file +* \brief Declaration of library functions +* +* Any definitions in this file will be shared among GLUE Layer and internal Driver Stack. +*/ + +#ifndef _PLATFORM_MT7986_CONSYS_REG_OFFSET_H_ +#define _PLATFORM_MT7986_CONSYS_REG_OFFSET_H_ + +/**********************************************************************/ +/* Base: TOP_MISC (0x11D1_0000) */ +/**********************************************************************/ +#define CONNSYS_MISC 0x0114 +#define TOP_MISC_RSRV_ALL1_3 0x021C + + +/**********************************************************************/ +/* Base: TOP RGU (0x1001_C000) */ +/**********************************************************************/ +#define TOP_RGU_WDT_SWSYSRST 0x0018 + +/**********************************************************************/ +/* Base: GPIO (0x1001_F000) */ +/**********************************************************************/ +#define GPIO_MODE9 0x0390 +#define GPIO_MODE10 0x03A0 +#define GPIO_MODE11 0x03B0 +#define GPIO_MODE12 0x03C0 + +/**********************************************************************/ +/* Base: IOCFG_TR (0x11F0_0000) */ +/**********************************************************************/ +#define IOCFG_TR_DRV_CFG0 0x0000 +#define IOCFG_TR_DRV_CFG1 0x0010 + +/**********************************************************************/ +/* Base: IOCFG_TL (0x11F1_0000) */ +/**********************************************************************/ +#define IOCFG_TL_DRV_CFG0 0x0000 +#define IOCFG_TL_DRV_CFG1 0x0010 + +/**********************************************************************/ +/* Base: INFRACFG_AO (0x1000_3000) */ +/**********************************************************************/ +#define CONN2AP_GALS_SLPPROT 0x00D0 +#define AP2CONN_GALS_SLPPROT 0x00D4 + +/**********************************************************************/ +/* Base: CONN_INFRA_CFG (0x1800_1000) */ +/**********************************************************************/ +#define CONN_INFRA_CFG_IP_VERSION 0x0000 +#define EFUSE 0x0020 +#define ADIE_CTL 0x0030 +#define CONN_INFRA_CFG_PWRCTRL0 0x0200 +#define CONN_INFRA_CFG_RC_CTL_0 0x0380 +#define OSC_CTL_0 0x0300 +#define EMI_CTL_WF 0x0414 +#define CONN_INFRA_WF_SLP_CTRL 0x0540 +#define CONN_INFRA_WF_SLP_STATUS 0x0544 + +/**********************************************************************/ +/* Base: CONN_INFRA_SYSRAM (0x1805_0000) */ +/**********************************************************************/ +#define SYSRAM_BASE_ADDR 0x0000 + +/**********************************************************************/ +/* Base: CONN_INFRA_CLKGEN_ON_TOP (0x1800_9000) */ +/**********************************************************************/ +#define CKGEN_BUS_WPLL_DIV_1 0x0008 +#define CKGEN_BUS_WPLL_DIV_2 0x000C +#define CKGEN_RFSPI_WPLL_DIV 0x0040 +#define CKGEN_BUS 0x0A00 + +/**********************************************************************/ +/* Base: CONN_HOST_CSR_TOP (0x1806_0000) */ +/**********************************************************************/ +#define CONN_INFRA_WAKEPU_TOP 0x01A0 +#define CONN_INFRA_WAKEPU_WF 0x01A4 +#define CONN2AP_REMAP_MCU_EMI 0x01C4 +#define CONN2AP_REMAP_WF_PERI 0x01D4 +#define DBG_DUMMY_3 0x02CC + +/**********************************************************************/ +/* Base: CONN_INFRA_BUS_CR (0x1800_E000) */ +/**********************************************************************/ +#define CONN_INFRA_BUS_OFF_TIMEOUT_CTRL 0x0300 +#define CONN_INFRA_BUS_ON_TIMEOUT_CTRL 0x031C +#define CONN2AP_EMI_PATH_ADDR_START 0x0360 +#define CONN2AP_EMI_PATH_ADDR_END 0x0364 + +/**********************************************************************/ +/* Base: CONN_INFRA_RGU (0x1800_0000) */ +/**********************************************************************/ +#define WFSYS_ON_TOP_PWR_CTL 0x0010 +#define BGFYS_ON_TOP_PWR_CTL 0x0020 +#define SYSRAM_HWCTL_PDN 0x0050 +#define SYSRAM_HWCTL_SLP 0x0054 +#define WFSYS_CPU_SW_RST_B 0x0120 + +/**********************************************************************/ +/* Base: CONN_WT_SLP_CTL_REG (0x1800_5000) */ +/* Base: INST2_CONN_WT_SLP_CTL_REG (0x1808_5000) */ +/**********************************************************************/ +#define WB_WF_CK_ADDR 0x0070 +#define WB_WF_WAKE_ADDR 0x0074 +#define WB_WF_ZPS_ADDR 0x0078 +#define WB_TOP_CK_ADDR 0x0084 +#define WB_WF_B0_CMD_ADDR 0x008C +#define WB_WF_B1_CMD_ADDR 0x0090 +#define WB_SLP_TOP_CK_0 0x0120 +#define WB_SLP_TOP_CK_1 0x0124 + +/**********************************************************************/ +/* Base: CONN_RF_SPI_MST_REG (0x1800_4000) */ +/* Base: INST2_CONN_RF_SPI_MST_REG (0x1808_4000) */ +/**********************************************************************/ +#define SPI_STA 0x0000 +#define SPI_WF_ADDR 0x0010 +#define SPI_WF_WDAT 0x0014 +#define SPI_WF_RDAT 0x0018 +#define SPI_BT_ADDR 0x0020 +#define SPI_BT_WDAT 0x0024 +#define SPI_BT_RDAT 0x0028 +#define SPI_FM_ADDR 0x0030 +#define SPI_FM_WDAT 0x0034 +#define SPI_FM_RDAT 0x0038 +#define SPI_GPS_ADDR 0x0040 +#define SPI_GPS_WDAT 0x0044 +#define SPI_GPS_RDAT 0x0048 +#define SPI_TOP_ADDR 0x0050 +#define SPI_TOP_WDAT 0x0054 +#define SPI_TOP_RDAT 0x0058 + +/**********************************************************************/ +/* Base: CONN_SEMAPHORE_BASE (0x1807_0000) */ +/**********************************************************************/ +#define CONN_SEMA00_M2_OWN_STA 0x2000 +#define CONN_SEMA00_M2_OWN_REL 0x2200 +#define CONN_SEMA_OWN_BY_M0_STA_REP 0x0400 +#define CONN_SEMA_OWN_BY_M1_STA_REP 0x1400 +#define CONN_SEMA_OWN_BY_M2_STA_REP 0x2400 +#define CONN_SEMA_OWN_BY_M3_STA_REP 0x3400 +#define CONN_SEMA_OWN_BY_M4_STA_REP 0x4400 +#define CONN_SEMA_OWN_BY_M5_STA_REP 0x5400 +#define CONN_SEMA_OWN_BY_M6_STA_REP 0x6400 +#define CONN_SEMA_OWN_BY_M7_STA_REP 0x7400 + +/**********************************************************************/ +/* Base: CONN_AFE_CTL_BASE (0x1800_3000) */ +/* Base: CONN_AFE_CTL_2ND_BASE (0x1808_3000) */ +/**********************************************************************/ +#define RG_DIG_EN_01 0x0000 +#define RG_DIG_EN_02 0x0004 +#define RG_DIG_EN_03 0x0008 +#define RG_DIG_TOP_01 0x000C +#define RG_PLL_STB_TIME 0x00F4 + +/**********************************************************************/ +/* Base: WF_TOP_SLPPROT_ON_BASE (0x8102_0000 remap to 0x184C_0000) */ +/**********************************************************************/ +#define WF_TOP_SLPPROT_ON_STATUS_READ 0x300C + +/**********************************************************************/ +/* Base: WF_TOP_CFG_BASE (0x8002_0000 remap to 0x184B_0000) */ +/**********************************************************************/ +#define WF_TOP_CFG_IP_VERSION 0x0010 + +/**********************************************************************/ +/* Base: WF_MCU_CONFIG_LS_BASE (0x8800_0000 remap to 0x184F_0000) */ +/**********************************************************************/ +#define BUSHANGCR 0x0440 + +/**********************************************************************/ +/* Base: WF_MCU_BUS_CR_BASE (0x830C_0XXX remap to 0x1840_0XXX) */ +/**********************************************************************/ +#define AP2WF_REMAP_1 0x0120 + +/**********************************************************************/ +/* Base: WF_MCUSYS_INFRA_BUS_FULL_U_DEBUG_CTRL_AO_BASE (0x810F_0000 remap to 0x1850_0000) */ +/**********************************************************************/ +#define WF_MCUSYS_INFRA_BUS_FULL_U_DEBUG_CTRL_AO_WFMCU_PWA_CTRL0 0x0000 + +/**********************************************************************/ +/* Base: WF_TOP_CFG_ON_BASE (0x8102_1000 remap to 0x184C_0000) */ +/**********************************************************************/ +#define ROMCODE_INDEX 0x1604 + +/**********************************************************************/ +/* A-die CR */ +/**********************************************************************/ +#define ATOP_CHIP_ID 0x02C +#define ATOP_TOP_CLK_EN 0xA00 +#define ATOP_RG_ENCAL_WBTAC_IF_SW 0x070 +#define ATOP_RG_WRI_CK_SELECT 0x4AC +#define ATOP_EFUSE_CTRL_1 0x108 +#define ATOP_EFUSE_CTRL_2 0x148 +#define ATOP_EFUSE_CTRL_3 0x14C +#define ATOP_EFUSE_CTRL_4 0x15C +#define ATOP_EFUSE_RDATA0 0x130 +#define ATOP_EFUSE_RDATA1 0x134 +#define ATOP_EFUSE_RDATA2 0x138 +#define ATOP_EFUSE_RDATA3 0x13C +#define ATOP_RG_EFUSE_CFG5 0x144 +#define ATOP_THADC_ANALOG 0x3A6 +#define ATOP_THADC_SLOP 0x3A7 +#define ATOP_RG_TOP_THADC_BG 0x034 +#define ATOP_RG_TOP_THADC_00 0x038 + +#define ATOP_XTAL_TRIM_FLOW 0x3AC +#define ATOP_XTAL_CR_C1_SEL_AXM_80M_OSC 0x390 +#define ATOP_XTAL_CR_C1_SEL_AXM_40M_OSC 0x391 +#define ATOP_XTAL_CR_C1_SEL_AXM_TRIM1_80M_OSC 0x398 +#define ATOP_XTAL_CR_C1_SEL_AXM_TRIM1_40M_OSC 0x399 +#define ATOP_RG_STRAP_PIN_IN 0x4FC +#define ATOP_RG_XO_01 0x65C +#define ATOP_RG_XO_03 0x664 + + +#define ATOP_7975_XTAL_CALIBRATION 0x3A1 +#define ATOP_7975_XTAL_TRIM2_COMPENSATION 0x3A2 +#define ATOP_7975_XTAL_TRIM3_COMPENSATION 0x3A3 +#define ATOP_7975_XTAL_TRIM4_COMPENSATION 0x3A4 +#define ATOP_7975_XTAL_TRIM_FLOW 0x3A5 +#define ATOP_7975_CR_C1_C2_A94 0xA94 +#define ATOP_7975_CR_C1_C2_A18 0xA18 +#define ATOP_7975_CR_C1_C2_A84 0xA84 +#define ATOP_7975_CR_C1_C2_AA4 0xAA4 +#define ATOP_7975_CO_CLK 0xA1C + + +#endif /* _PLATFORM_MT7986_CONSYS_REG_OFFSET_H_ */ diff --git a/package/mtk/drivers/conninfra/src/platform/mt7986/include/mt7986_emi.h b/package/mtk/drivers/conninfra/src/platform/mt7986/include/mt7986_emi.h new file mode 100644 index 0000000000..663a4a0879 --- /dev/null +++ b/package/mtk/drivers/conninfra/src/platform/mt7986/include/mt7986_emi.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2016 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 http://www.gnu.org/licenses/gpl-2.0.html for more details. + */ +/*! \file +* \brief Declaration of library functions +* +* Any definitions in this file will be shared among GLUE Layer and internal Driver Stack. +*/ + +#ifndef _PLATFORM_MT7986_EMI_H_ +#define _PLATFORM_MT7986_EMI_H_ + +#include "osal.h" +#include "emi_mng.h" +/******************************************************************************* +* C O M P I L E R F L A G S +******************************************************************************** +*/ + +/******************************************************************************* +* M A C R O S +******************************************************************************** +*/ + +/******************************************************************************* +* E X T E R N A L R E F E R E N C E S +******************************************************************************** +*/ + +/******************************************************************************* +* C O N S T A N T S +******************************************************************************** +*/ + +/******************************************************************************* +* D A T A T Y P E S +******************************************************************************** +*/ + + +/******************************************************************************* +* P U B L I C D A T A +******************************************************************************** +*/ + +/******************************************************************************* +* P R I V A T E D A T A +******************************************************************************** +*/ + +/******************************************************************************* +* F U N C T I O N D E C L A R A T I O N S +******************************************************************************** +*/ + +struct consys_platform_emi_ops* get_consys_platform_emi_ops(void); + +/******************************************************************************* +* F U N C T I O N S +******************************************************************************** +*/ + +#endif /* _PLATFORM_MT7986_EMI_H_ */ diff --git a/package/mtk/drivers/conninfra/src/platform/mt7986/include/mt7986_pmic.h b/package/mtk/drivers/conninfra/src/platform/mt7986/include/mt7986_pmic.h new file mode 100644 index 0000000000..165816d835 --- /dev/null +++ b/package/mtk/drivers/conninfra/src/platform/mt7986/include/mt7986_pmic.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2016 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 http://www.gnu.org/licenses/gpl-2.0.html for more details. + */ +/*! \file +* \brief Declaration of library functions +* +* Any definitions in this file will be shared among GLUE Layer and internal Driver Stack. +*/ + +#ifndef _PLATFORM_MT7986_PMIC_H_ +#define _PLATFORM_MT7986_PMIC_H_ + +#include "osal.h" +#include "pmic_mng.h" +/******************************************************************************* +* C O M P I L E R F L A G S +******************************************************************************** +*/ + +/******************************************************************************* +* M A C R O S +******************************************************************************** +*/ + +/******************************************************************************* +* E X T E R N A L R E F E R E N C E S +******************************************************************************** +*/ + +/******************************************************************************* +* C O N S T A N T S +******************************************************************************** +*/ + +/******************************************************************************* +* D A T A T Y P E S +******************************************************************************** +*/ + + +/******************************************************************************* +* P U B L I C D A T A +******************************************************************************** +*/ + +/******************************************************************************* +* P R I V A T E D A T A +******************************************************************************** +*/ + +/******************************************************************************* +* F U N C T I O N D E C L A R A T I O N S +******************************************************************************** +*/ + +/******************************************************************************* +* F U N C T I O N S +******************************************************************************** +*/ + +struct consys_platform_pmic_ops* get_consys_platform_pmic_ops(void); + +#endif /* _PLATFORM_MT7986_PMIC_H_ */ diff --git a/package/mtk/drivers/conninfra/src/platform/mt7986/include/mt7986_pos.h b/package/mtk/drivers/conninfra/src/platform/mt7986/include/mt7986_pos.h new file mode 100644 index 0000000000..fc182c5430 --- /dev/null +++ b/package/mtk/drivers/conninfra/src/platform/mt7986/include/mt7986_pos.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2016 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 http://www.gnu.org/licenses/gpl-2.0.html for more details. + */ +/*! \file +* \brief Declaration of library functions +* +* Any definitions in this file will be shared among GLUE Layer and internal Driver Stack. +*/ + +#ifndef _PLATFORM_MT7986_POS_H_ +#define _PLATFORM_MT7986_POS_H_ + +int consys_plt_hw_init(void); +int consys_xtal_ctrl_fast_mode(void); +int consys_sw_reset_ctrl(bool bassert); +int consys_tx_rx_bus_slp_prot_ctrl(bool enable); +void consys_set_if_pinmux(bool enable); +int consys_polling_chipid(void); +int consys_plt_adie_type_check(void); +int consys_plt_adie_type_cfg(void); +int consys_bus_clock_ctrl(enum consys_drv_type drv_type, unsigned int bus_clock); +int consys_emi_set_remapping_reg(void); +int consys_emi_set_region_protection(void); +int connsys_d_die_cfg(void); +int connsys_conninfra_sysram_hw_ctrl(void); +int connsys_spi_master_cfg(void); +int consys_sema_acquire_timeout(unsigned int index, unsigned int usec); +void consys_sema_release(unsigned int index); +int consys_spi_read(enum sys_spi_subsystem subsystem, unsigned int addr, unsigned int *data); +int consys_spi_write(enum sys_spi_subsystem subsystem, unsigned int addr, unsigned int data); +int consys_spi_write_offset_range(enum sys_spi_subsystem subsystem, unsigned int addr, unsigned int value, + unsigned int reg_offset, unsigned int value_offset, unsigned int size); +int connsys_a_die_cfg(void); +int connsys_afe_wbg_cal(void); +int connsys_subsys_pll_initial(void); +int connsys_osc_legacy_mode(void); +int connsys_top_pwr_ctrl(void); +int connsys_conn_infra_bus_timeout(void); +int connsys_clkgen_wpll_hw_ctrl(void); +int consys_conninfra_top_wakeup(void); +int consys_conninfra_top_sleep(void); +int consys_adie_top_ck_en_on_off_ctrl(enum consys_drv_type type, unsigned char on); +int consys_conninfra_wf_wakeup(void); +int consys_conninfra_wf_sleep(void); +int consys_conn_wmcpu_sw_reset(bool bassert); +int consys_wf_bus_slp_prot_ctrl(bool enable); +int consys_wfsys_top_on_ctrl(bool enable); +int consys_wfsys_bus_slp_prot_check(bool enable); +int consys_wfsys_bus_timeout_ctrl(void); +int consys_wmcpu_idle_loop_check(void); +int consys_wpll_ctrl(bool enable); +int consys_conninfra_wf_req_clr(void); + + +#endif /* _PLATFORM_MT7986_POS_H_ */ diff --git a/package/mtk/drivers/conninfra/src/platform/mt7986/mt7986.c b/package/mtk/drivers/conninfra/src/platform/mt7986/mt7986.c new file mode 100644 index 0000000000..9329eda795 --- /dev/null +++ b/package/mtk/drivers/conninfra/src/platform/mt7986/mt7986.c @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2016 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 http://www.gnu.org/licenses/gpl-2.0.html for more details. + */ +/*! \file +* \brief Declaration of library functions +* +* Any definitions in this file will be shared among GLUE Layer and internal Driver Stack. +*/ + +#define pr_fmt(fmt) KBUILD_MODNAME "@(%s:%d) " fmt, __func__, __LINE__ + +#include +#include +#include +#include +#include +#include +#include + +#include "osal.h" +#include "conninfra.h" +#include "consys_hw.h" +#include "consys_reg_mng.h" +#include "consys_reg_util.h" +#include "mt7986.h" +#include "mt7986_pos.h" +#include "emi_mng.h" +#include "mt7986_consys_reg.h" +#include "mt7986_consys_reg_offset.h" + +/******************************************************************************* +* C O M P I L E R F L A G S +******************************************************************************** +*/ + +/******************************************************************************* +* M A C R O S +******************************************************************************** +*/ +#define PLATFORM_SOC_CHIP 0x7986 +#define CONN_IP_VER 0x02070000 + +/******************************************************************************* +* E X T E R N A L R E F E R E N C E S +******************************************************************************** +*/ + +/******************************************************************************* +* C O N S T A N T S +******************************************************************************** +*/ + +/******************************************************************************* +* D A T A T Y P E S +******************************************************************************** +*/ + +/******************************************************************************* +* F U N C T I O N D E C L A R A T I O N S +******************************************************************************** +*/ + +/******************************************************************************* +* P U B L I C D A T A +******************************************************************************** +*/ +struct consys_hw_ops_struct g_consys_hw_ops_mt7986 = { + /* HW init */ + .consys_plt_hw_init = consys_plt_hw_init, + + /* POS */ + .consys_plt_xtal_ctrl_fast_mode = consys_xtal_ctrl_fast_mode, + .consys_plt_connsys_sw_reset_ctrl = consys_sw_reset_ctrl, + .consys_plt_set_if_pinmux = consys_set_if_pinmux, + .consys_plt_tx_rx_bus_slp_prot_ctrl = consys_tx_rx_bus_slp_prot_ctrl, + .consys_plt_polling_consys_chipid = consys_polling_chipid, + .consys_plt_bus_clock_ctrl = consys_bus_clock_ctrl, + .consys_plt_d_die_cfg = connsys_d_die_cfg, + .consys_plt_conninfra_sysram_hw_ctrl = connsys_conninfra_sysram_hw_ctrl, + .consys_plt_spi_master_cfg = connsys_spi_master_cfg, + .consys_plt_a_die_cfg = connsys_a_die_cfg, + .consys_plt_afe_wbg_cal = connsys_afe_wbg_cal, + .consys_plt_subsys_pll_initial = connsys_subsys_pll_initial, + .consys_plt_osc_legacy_mode = connsys_osc_legacy_mode, + .consys_plt_top_pwr_ctrl = connsys_top_pwr_ctrl, + .consys_plt_conn_infra_bus_timeout = connsys_conn_infra_bus_timeout, + .consys_plt_clkgen_wpll_hw_ctrl = connsys_clkgen_wpll_hw_ctrl, + .consys_plt_conninfra_wakeup = consys_conninfra_top_wakeup, + .consys_plt_conninfra_sleep = consys_conninfra_top_sleep, + .consys_plt_adie_top_ck_en_on_off_ctrl = consys_adie_top_ck_en_on_off_ctrl, + .consys_plt_conninfra_wf_wakeup = consys_conninfra_wf_wakeup, + .consys_plt_conninfra_wf_sleep = consys_conninfra_wf_sleep, + .consys_plt_conn_wmcpu_sw_reset = consys_conn_wmcpu_sw_reset, + .consys_plt_wf_bus_slp_prot_ctrl = consys_wf_bus_slp_prot_ctrl, + .consys_plt_wfsys_top_on_ctrl = consys_wfsys_top_on_ctrl, + .consys_plt_wfsys_bus_slp_prot_check = consys_wfsys_bus_slp_prot_check, + .consys_plt_wfsys_bus_timeout_ctrl = consys_wfsys_bus_timeout_ctrl, + .consys_plt_conn_wmcpu_idle_loop_check = consys_wmcpu_idle_loop_check, + .consys_plt_wpll_ctrl = consys_wpll_ctrl, + .consys_plt_conninfra_wf_req_clr = consys_conninfra_wf_req_clr, + + /* load from dts */ + /* TODO: mtcmos should move to a independent module */ + .consys_plt_clk_get_from_dts = NULL, + .consys_plt_clk_detach = NULL, + + /* clock */ + .consys_plt_soc_chipid_get = consys_soc_chipid_get, + + /* debug */ + .consys_plt_get_hw_ver = consys_get_hw_ver, + .consys_plt_spi_read = consys_spi_read, + .consys_plt_spi_write = consys_spi_write, + .consys_plt_spi_clock_switch = NULL, + .consys_plt_power_state = NULL, + + /* others */ + .consys_plt_adie_type_check = consys_plt_adie_type_check, + .consys_plt_adie_type_cfg = consys_plt_adie_type_cfg, +}; + +/* For mt7986 */ +extern struct consys_hw_ops_struct g_consys_hw_ops_mt7986; +extern struct consys_reg_mng_ops g_dev_consys_reg_ops_mt7986; +extern struct consys_platform_emi_ops g_consys_platform_emi_ops_mt7986; +extern struct consys_platform_pmic_ops g_consys_platform_pmic_ops_mt7986; + +const struct conninfra_plat_data mt7986_plat_data = { + .chip_id = PLATFORM_SOC_CHIP, + .hw_ops = &g_consys_hw_ops_mt7986, + .reg_ops = &g_dev_consys_reg_ops_mt7986, + .platform_emi_ops = &g_consys_platform_emi_ops_mt7986, + .platform_pmic_ops = &g_consys_platform_pmic_ops_mt7986, +}; + +/******************************************************************************* +* P R I V A T E D A T A +******************************************************************************** +*/ + +/******************************************************************************* +* F U N C T I O N S +******************************************************************************** +*/ +unsigned int consys_soc_chipid_get(void) +{ + return PLATFORM_SOC_CHIP; +} + +unsigned int consys_get_hw_ver(void) +{ + return CONN_IP_VER; +} + diff --git a/package/mtk/drivers/conninfra/src/platform/mt7986/mt7986_consys_reg.c b/package/mtk/drivers/conninfra/src/platform/mt7986/mt7986_consys_reg.c new file mode 100644 index 0000000000..290cc9acef --- /dev/null +++ b/package/mtk/drivers/conninfra/src/platform/mt7986/mt7986_consys_reg.c @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2016 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 http://www.gnu.org/licenses/gpl-2.0.html for more details. + */ +/*! \file +* \brief Declaration of library functions +* +* Any definitions in this file will be shared among GLUE Layer and internal Driver Stack. +*/ + +#include +#include +#include +#include +#include +#include +#include "consys_reg_mng.h" +#include "mt7986_consys_reg.h" +#include "mt7986_consys_reg_offset.h" +#include "consys_hw.h" +#include "consys_reg_util.h" + +#define CFG_REG_LOAD_FROM_DTS_CTRL 0 + +static int consys_reg_init(struct platform_device *pdev); +static int consys_reg_deinit(void); + +struct consys_base_addr conn_reg = { + .reg_base_addr[TOP_MISC_BASE] = {0x11D10000, 0x1000, 0}, + .reg_base_addr[TOPRGU_BASE] = {0x1001C000, 0x1000, 0}, + .reg_base_addr[GPIO_BASE] = {0x1001F000, 0x1000, 0}, + .reg_base_addr[IOCFG_TR_BASE] = {0x11F00000, 0x1000, 0}, + .reg_base_addr[IOCFG_TL_BASE] = {0x11F10000, 0x1000, 0}, + .reg_base_addr[INFRACFG_AO_BASE] = {0x10003000, 0x1000, 0}, + .reg_base_addr[CONN_INFRA_CFG_BASE] = {0x18001000, 0x1000, 0}, + .reg_base_addr[CONN_INFRA_SYSRAM_BASE] = {0x18050000, 0x1000, 0}, + .reg_base_addr[CONN_INFRA_CLKGEN_ON_TOP_BASE] = {0x18009000, 0x1000, 0}, + .reg_base_addr[CONN_HOST_CSR_TOP_BASE] = {0x18060000, 0x1000, 0}, + .reg_base_addr[CONN_INFRA_BUS_CR_BASE] = {0x1800E000, 0x1000, 0}, + .reg_base_addr[CONN_INFRA_RGU_BASE] = {0x18000000, 0x1000, 0}, + .reg_base_addr[CONN_WT_SLP_CTL_REG_BASE] = {0x18005000, 0x1000, 0}, + .reg_base_addr[INST2_CONN_WT_SLP_CTL_REG_BASE] = {0x18085000, 0x1000, 0}, + .reg_base_addr[CONN_RF_SPI_MST_REG_BASE] = {0x18004000, 0x1000, 0}, + .reg_base_addr[INST2_CONN_RF_SPI_MST_REG_BASE] = {0x18084000, 0x1000, 0}, + .reg_base_addr[CONN_SEMAPHORE_BASE] = {0x18070000, 0x10000, 0}, + .reg_base_addr[CONN_AFE_CTL_BASE] = {0x18003000, 0x1000, 0}, + .reg_base_addr[CONN_AFE_CTL_2ND_BASE] = {0x18083000, 0x1000, 0}, + .reg_base_addr[WF_TOP_SLPPROT_ON_BASE] = {0x184C0000, 0x10000, 0}, + .reg_base_addr[WF_TOP_CFG_BASE] = {0x184B0000, 0x1000, 0}, + .reg_base_addr[WF_MCU_CONFIG_LS_BASE] = {0x184F0000, 0x1000, 0}, + .reg_base_addr[WF_MCU_BUS_CR_BASE] = {0x18400000, 0x1000, 0}, + .reg_base_addr[WF_MCUSYS_INFRA_BUS_FULL_U_DEBUG_CTRL_AO_BASE] = {0x18500000, 0x1000, 0}, + .reg_base_addr[WF_TOP_CFG_ON_BASE] = {0x184C0000, 0x10000, 0}, +}; + +const char* consys_base_addr_index_to_str[CONSYS_BASE_ADDR_MAX] = { + "TOP_MISC_BASE", + "TOPRGU_BASE", + "GPIO_BASE", + "IOCFG_TR_BASE", + "IOCFG_TL_BASE", + "INFRACFG_AO_BASE", + "CONN_INFRA_CFG_BASE", + "CONN_INFRA_SYSRAM_BASE", + "CONN_INFRA_CLKGEN_ON_TOP_BASE", + "CONN_HOST_CSR_TOP_BASE", + "CONN_INFRA_BUS_CR_BASE", + "CONN_INFRA_RGU_BASE", + "CONN_WT_SLP_CTL_REG_BASE", + "INST2_CONN_WT_SLP_CTL_REG_BASE", + "CONN_RF_SPI_MST_REG_BASE", + "INST2_CONN_RF_SPI_MST_REG_BASE", + "CONN_SEMAPHORE_BASE", + "CONN_AFE_CTL_BASE", + "CONN_AFE_CTL_2ND_BASE", + "WF_TOP_SLPPROT_ON_BASE", + "WF_TOP_CFG_BASE", + "WF_MCU_CONFIG_LS_BASE", + "WF_MCU_BUS_CR_BASE", + "WF_MCUSYS_INFRA_BUS_FULL_U_DEBUG_CTRL_AO_BASE", + "WF_TOP_CFG_ON_BASE" +}; + +struct consys_reg_mng_ops g_dev_consys_reg_ops_mt7986 = { + .consys_reg_mng_init = consys_reg_init, + .consys_reg_mng_deinit = consys_reg_deinit, + .consys_reg_mng_check_reable = NULL, + .consys_reg_mng_is_consys_reg = NULL, + .consys_reg_mng_is_bus_hang = NULL, + .consys_reg_mng_dump_bus_status = NULL, + .consys_reg_mng_dump_conninfra_status = NULL, + .consys_reg_mng_dump_cpupcr = NULL, + .consys_reg_mng_is_host_csr = NULL, +}; + +struct consys_base_addr* get_conn_reg_base_addr() +{ + return &conn_reg; +} + +static int consys_reg_init(struct platform_device *pdev) +{ + int ret = -1; + struct device_node *node = NULL; + struct consys_reg_base_addr *base_addr = NULL; + int i = 0; + + node = pdev->dev.of_node; + if (node) { +#if (CFG_REG_LOAD_FROM_DTS_CTRL == 1) + struct resource res; + int flag; + + for (i = 0; i < CONSYS_BASE_ADDR_MAX; i++) { + base_addr = &conn_reg.reg_base_addr[i]; + ret = of_address_to_resource(node, i, &res); + if (ret) { + pr_err("Get Reg Index(%d-%s) failed\n", i, consys_base_addr_index_to_str[i]); + continue; + } + base_addr->phy_addr = res.start; + base_addr->vir_addr = (unsigned long)of_iomap(node, i); + of_get_address(node, i, &(base_addr->size), &flag); +#if 0 + pr_info("Get Index(%d-%s) phy_addr(0x%zx) vir_addr=(0x%zx) size=(0x%zx)\n", + i, consys_base_addr_index_to_str[i], base_addr->phy_addr, + base_addr->vir_addr, base_addr->size); +#endif + } +#else + for (i = 0; i < CONSYS_BASE_ADDR_MAX; i++) { + base_addr = &conn_reg.reg_base_addr[i]; + if (base_addr->vir_addr == 0) + base_addr->vir_addr = (unsigned long)ioremap(base_addr->phy_addr, base_addr->size); + + pr_info("Get Index(%d-%s) phy_addr(0x%zx) vir_addr=(0x%zx) size=(0x%zx)\n", + i, consys_base_addr_index_to_str[i], base_addr->phy_addr, + base_addr->vir_addr, base_addr->size); + } +#endif + } else { + pr_err("[%s] can't find CONSYS compatible node\n", __func__); + return ret; + } + + return 0; +} + +static int consys_reg_deinit(void) +{ + int i = 0; + + for (i = 0; i < CONSYS_BASE_ADDR_MAX; i++) { + if (conn_reg.reg_base_addr[i].vir_addr) { + pr_info("[%d] Unmap %s (0x%zx)\n", i, consys_base_addr_index_to_str[i], + conn_reg.reg_base_addr[i].vir_addr); + iounmap((void __iomem*)conn_reg.reg_base_addr[i].vir_addr); + conn_reg.reg_base_addr[i].vir_addr = 0; + } + } + + return 0; +} + diff --git a/package/mtk/drivers/conninfra/src/platform/mt7986/mt7986_emi.c b/package/mtk/drivers/conninfra/src/platform/mt7986/mt7986_emi.c new file mode 100644 index 0000000000..754174ec72 --- /dev/null +++ b/package/mtk/drivers/conninfra/src/platform/mt7986/mt7986_emi.c @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2016 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 http://www.gnu.org/licenses/gpl-2.0.html for more details. + */ +/*! \file +* \brief Declaration of library functions +* +* Any definitions in this file will be shared among GLUE Layer and internal Driver Stack. +*/ + +#define pr_fmt(fmt) KBUILD_MODNAME "@(%s:%d) " fmt, __func__, __LINE__ + +#include +#include +#include +#include "mt7986_emi.h" +#include "mt7986.h" +#include "mt7986_consys_reg.h" +#include "consys_hw.h" +#include "consys_reg_util.h" +#include "mt7986_pos.h" + +/******************************************************************************* +* P U B L I C D A T A +******************************************************************************** +*/ +unsigned int consys_emi_get_fw_emi_size(void) +{ + return 0x100000; +} + +struct consys_platform_emi_ops g_consys_platform_emi_ops_mt7986 = { + .consys_ic_emi_set_region_protection = consys_emi_set_region_protection, + .consys_ic_emi_set_remapping_reg = consys_emi_set_remapping_reg, + .consys_ic_emi_get_fw_emi_size = consys_emi_get_fw_emi_size, +}; + +struct consys_platform_emi_ops* get_consys_platform_emi_ops(void) +{ + return &g_consys_platform_emi_ops_mt7986; +} + diff --git a/package/mtk/drivers/conninfra/src/platform/mt7986/mt7986_pmic.c b/package/mtk/drivers/conninfra/src/platform/mt7986/mt7986_pmic.c new file mode 100644 index 0000000000..d2576b67a1 --- /dev/null +++ b/package/mtk/drivers/conninfra/src/platform/mt7986/mt7986_pmic.c @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2016 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 http://www.gnu.org/licenses/gpl-2.0.html for more details. + */ +/*! \file +* \brief Declaration of library functions +* +* Any definitions in this file will be shared among GLUE Layer and internal Driver Stack. +*/ + +#define pr_fmt(fmt) KBUILD_MODNAME "@(%s:%d) " fmt, __func__, __LINE__ + +#include +#include +#include +#include +#include + +#include "consys_hw.h" +#include "consys_reg_util.h" +#include "osal.h" +#include "mt7986_pmic.h" +#include "mt7986_pos.h" +#include "mt7986_consys_reg.h" +#include "mt7986_consys_reg_offset.h" + +/******************************************************************************* +* C O M P I L E R F L A G S +******************************************************************************** +*/ + +/******************************************************************************* +* M A C R O S +******************************************************************************** +*/ + + +/******************************************************************************* +* E X T E R N A L R E F E R E N C E S +******************************************************************************** +*/ + +/******************************************************************************* +* C O N S T A N T S +******************************************************************************** +*/ + +/******************************************************************************* +* D A T A T Y P E S +******************************************************************************** +*/ + +/******************************************************************************* +* F U N C T I O N D E C L A R A T I O N S +******************************************************************************** +*/ + +/******************************************************************************* +* P U B L I C D A T A +******************************************************************************** +*/ + +struct consys_platform_pmic_ops g_consys_platform_pmic_ops_mt7986 = { + .consys_pmic_get_from_dts = NULL, + .consys_pmic_common_power_ctrl = NULL, + .consys_pmic_wifi_power_ctrl = NULL, + .consys_pmic_bt_power_ctrl = NULL, + .consys_pmic_gps_power_ctrl = NULL, + .consys_pmic_fm_power_ctrl = NULL, + .consys_pmic_event_notifier = NULL, +}; + +/******************************************************************************* +* P R I V A T E D A T A +******************************************************************************** +*/ + +/******************************************************************************* +* F U N C T I O N S +******************************************************************************** +*/ + +struct consys_platform_pmic_ops* get_consys_platform_pmic_ops(void) +{ + return &g_consys_platform_pmic_ops_mt7986; +} + diff --git a/package/mtk/drivers/conninfra/src/platform/mt7986/mt7986_pos.c b/package/mtk/drivers/conninfra/src/platform/mt7986/mt7986_pos.c new file mode 100644 index 0000000000..c439395ab0 --- /dev/null +++ b/package/mtk/drivers/conninfra/src/platform/mt7986/mt7986_pos.c @@ -0,0 +1,2043 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019 MediaTek Inc. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME "@(%s:%d) " fmt, __func__, __LINE__ + +#include +#include "plat_def.h" +#include "consys_reg_util.h" +#include "consys_reg_mng.h" +#include "mt7986_consys_reg.h" +#include "mt7986_consys_reg_offset.h" +#include "mt7986_pos.h" +#include "mt7986.h" +#include "mt7986_emi.h" + + +/******************************************************************************* +* M A C R O S +******************************************************************************** +*/ +#define MTD_WIFI_NM "Factory" +#define EEPROM_CHIPID_OFFSET 0x0 + +#define EEPROM_BAND0_STREAM_OFFSET 0x190 +#define EEPROM_BAND0_STREAM_TX_MASK 0x7 +#define EEPROM_BAND0_STREAM_TX_BIT_OFFSET 0 +#define EEPROM_BAND0_STREAM_RX_MASK 0x7 +#define EEPROM_BAND0_STREAM_RX_BIT_OFFSET 3 +#define EEPROM_LNA_PA_SELECT_OFFSET 0x197 +#define EEPROM_LNA_PA_SELECT_BAND0_MASK 0x3 +#define EEPROM_LNA_PA_SELECT_BAND0_BIT_OFFSET 2 +#define EEPROM_LNA_PA_SELECT_BAND1_MASK 0x3 +#define EEPROM_LNA_PA_SELECT_BAND1_BIT_OFFSET 4 + + +#define _TO_STR(_x) #_x +#define TO_STR(_x) _TO_STR(_x) +#define RED(_text) "\033[1;31m"_text"\033[0m" +#define GRN(_text) "\033[1;32m"_text"\033[0m" + +/******************************************************************************* +* D A T A T Y P E S +******************************************************************************** +*/ +bool EEPROM_content_valid = false; +bool one_adie_dbdc = false; +unsigned int adie_cfg_type = ADIE_TYPE_NONE; +unsigned int tx_stream = 0; +unsigned int rx_stream = 0; +unsigned int band0_pa_type = 0; +unsigned int band1_pa_type = 0; + +enum LNA_PA_TYPE { + iPAiLNA = 0x0, /* 2b'00 */ + iPAeLNA = 0x1, /* 2b'01 */ + ePAiLNA = 0x2, /* 2b'10 */ + ePAeLNA = 0x3 /* 2b'11 */ +}; + + +struct spi_op { + unsigned int busy_cr; + unsigned int polling_bit; + unsigned int addr_cr; + unsigned int read_addr_format; + unsigned int write_addr_format; + unsigned int write_data_cr; + unsigned int read_data_cr; + unsigned int read_data_mask; +}; + +/******************************************************************************* +* C O N S T A N T S +******************************************************************************** +*/ +const static char* g_spi_system_name[SYS_SPI_MAX] = { + "SYS_SPI_WF1", + "SYS_SPI_WF", + "SYS_SPI_BT", + "SYS_SPI_FM", + "SYS_SPI_GPS", + "SYS_SPI_TOP", + "SYS_SPI_WF2", + "SYS_SPI_WF3", +}; + +static const struct spi_op spi_op_array[SYS_SPI_MAX] = { + /* SYS_SPI_WF1 */ + { + SPI_STA, 1, SPI_WF_ADDR, 0x00001000, 0x00000000, + SPI_WF_WDAT, SPI_WF_RDAT, 0xFFFFFFFF + }, + /* SYS_SPI_WF */ + { + SPI_STA, 1, SPI_WF_ADDR, 0x00003000, 0x00002000, + SPI_WF_WDAT, SPI_WF_RDAT, 0xFFFFFFFF + }, + /* SYS_SPI_BT */ + { + SPI_STA, 2, SPI_BT_ADDR, 0x00005000, 0x00004000, + SPI_BT_WDAT, SPI_BT_RDAT, 0xFFFFFFFF + }, + /* SYS_SPI_FM */ + { + SPI_STA, 3, SPI_FM_ADDR, 0x00007000, 0x00006000, + SPI_FM_WDAT, SPI_FM_RDAT, 0x0000FFFF + }, + /* SYS_SPI_GPS */ + { + SPI_STA, 4, SPI_GPS_ADDR, 0x00009000, 0x00008000, + SPI_GPS_WDAT, SPI_GPS_RDAT, 0x0000FFFF + }, + /* SYS_SPI_TOP */ + { + SPI_STA, 5, SPI_TOP_ADDR, 0x0000B000, 0x0000A000, + SPI_TOP_WDAT, SPI_TOP_RDAT, 0xFFFFFFFF + }, + /* SYS_SPI_WF2 */ + { + SPI_STA, 1, SPI_WF_ADDR, 0x0000D000, 0x0000C000, + SPI_WF_WDAT, SPI_WF_RDAT, 0xFFFFFFFF + }, + /* SYS_SPI_WF3 */ + { + SPI_STA, 1, SPI_WF_ADDR, 0x0000F000, 0x0000E000, + SPI_WF_WDAT, SPI_WF_RDAT, 0xFFFFFFFF + }, +}; + +/******************************************************************************* +* F U N C T I O N D E C L A R A T I O N S +******************************************************************************** +*/ +bool _is_flash_content_valid(void) +{ + unsigned short eeFlashId = 0; + + FlashRead(MTD_WIFI_NM, (unsigned char*)&eeFlashId, EEPROM_CHIPID_OFFSET, sizeof(eeFlashId)); + if (eeFlashId == consys_soc_chipid_get()) { + EEPROM_content_valid = true; + printk(GRN("eeFlashId: 0x%x, EEPROM_content_valid = %d"), eeFlashId, EEPROM_content_valid); + return true; + } else { + printk(RED("eeFlashId(0x%x) isn't match with 0x%x!"), eeFlashId, consys_soc_chipid_get()); + return false; + } +} + +int _consys_check_adie_cfg(void) +{ + int ret = 0; + unsigned int hw_adie_type = 0; + unsigned int i = 0; + bool found = false; + + for (i = 0; i < AIDE_NUM_MAX; i++) { + if (conn_hw_env[i].valid) { + hw_adie_type = conn_hw_env[i].adie_id; + found = true; + break; + } + } + + if (found) { + printk(GRN("Adie Type: 0x%x"), hw_adie_type); + } else { + printk(RED("No Adie found!!!")); + ret = -1; + } + + return ret; +} + +void _consys_check_sku_cfg(void) +{ + unsigned int hw_sku_type; + + if (!one_adie_dbdc) { + if (adie_cfg_type == ADIE_TYPE_TWO) + hw_sku_type = 6000; + else + hw_sku_type = 7800; + + printk(GRN("SKU Type: %d"), hw_sku_type); + } else { + printk(GRN("SKU Type: One_Adie_DBDC")); + } +} + +int consys_plt_hw_init(void) +{ + unsigned int value = 0; + + value = CONSYS_REG_READ_BIT(REG_TOP_MISC_ADDR + TOP_MISC_RSRV_ALL1_3, (0x1 << 2)); + if (value != 0) + one_adie_dbdc = true; + else + one_adie_dbdc = false; + + if (_is_flash_content_valid()) { + FlashRead(MTD_WIFI_NM, (unsigned char*)&value, EEPROM_BAND0_STREAM_OFFSET, sizeof(value)); + tx_stream = ((value >> EEPROM_BAND0_STREAM_TX_BIT_OFFSET) & EEPROM_BAND0_STREAM_TX_MASK); + rx_stream = ((value >> EEPROM_BAND0_STREAM_RX_BIT_OFFSET) & EEPROM_BAND0_STREAM_RX_MASK); + + FlashRead(MTD_WIFI_NM, (unsigned char*)&value, EEPROM_LNA_PA_SELECT_OFFSET, sizeof(value)); + band0_pa_type = ((value >> EEPROM_LNA_PA_SELECT_BAND0_BIT_OFFSET) & EEPROM_LNA_PA_SELECT_BAND0_MASK); + band1_pa_type = ((value >> EEPROM_LNA_PA_SELECT_BAND1_BIT_OFFSET) & EEPROM_LNA_PA_SELECT_BAND1_MASK); + + if (one_adie_dbdc) { + adie_cfg_type = ADIE_TYPE_ONE; + } else { + if (tx_stream >= 2) { + adie_cfg_type = ADIE_TYPE_TWO; + } else { + adie_cfg_type = ADIE_TYPE_ONE; + } + } + pr_info("tx_stream = 0x%x, rx_stream = 0x%x, band0_pa_type = 0x%x, band1_pa_type = 0x%x\n", + tx_stream, rx_stream, band0_pa_type, band1_pa_type); + } else { + if (one_adie_dbdc) { + adie_cfg_type = ADIE_TYPE_ONE; + } else { + adie_cfg_type = ADIE_TYPE_TWO; + printk(RED("Flash content is empty, so use AX6000 configuration by default!")); + } + } + pr_info("adie_cfg_type = %d, one_adie_dbdc = %d\n", adie_cfg_type, one_adie_dbdc); + + return 0; +} + +int consys_xtal_ctrl_fast_mode(void) +{ + /* Setting fast mode to xtal control */ + CONSYS_SET_BIT(REG_TOP_MISC_ADDR + CONNSYS_MISC, (0x1 << 3)); + return 0; +} + +int consys_sw_reset_ctrl(bool bassert) +{ + /* Release CONNSYS software reset */ + if (bassert) { + CONSYS_REG_WRITE_MASK( + REG_TOP_RGU_ADDR + TOP_RGU_WDT_SWSYSRST, + 0x88800000, 0xff800000); + } else { + /* de-assert CONNSYS S/W reset */ + CONSYS_REG_WRITE_MASK( + REG_TOP_RGU_ADDR + TOP_RGU_WDT_SWSYSRST, + 0x88000000, 0xff800000); + } + + return 0; +} + +void consys_set_if_pinmux(bool enable) +{ + if (enable) { + /* Set PAD_WF*_HB* to GPIO mode by default. (Aux0) */ + CONSYS_REG_WRITE_MASK(REG_GPIO_BASE_ADDR + GPIO_MODE9, 0x0, 0x77777700); + CONSYS_REG_WRITE_MASK(REG_GPIO_BASE_ADDR + GPIO_MODE10, 0x0, 0x777777); + CONSYS_REG_WRITE_MASK(REG_GPIO_BASE_ADDR + GPIO_MODE11, 0x0, 0x77777000); + CONSYS_REG_WRITE_MASK(REG_GPIO_BASE_ADDR + GPIO_MODE12, 0x0, 0x77770); + + if (adie_cfg_type == ADIE_TYPE_TWO) { + /* Two_Adie + set pinmux for the interface between D-die and A-die (Aux1) + PAD_WF0_HB1(GPIO74) 0x0390[10:8] + PAD_WF0_HB2(GPIO75) 0x0390[14:12] + PAD_WF0_HB3(GPIO76) 0x0390[18:16] + PAD_WF0_HB4(GPIO77) 0x0390[22:20] + PAD_WF0_HB0(GPIO78) 0x0390[26:24] + PAD_WF0_HB5(GPIO80) 0x03A0[2:0] + PAD_WF0_HB6(GPIO81) 0x03A0[6:4] + PAD_WF0_HB7(GPIO82) 0x03A0[10:8] + PAD_WF0_HB8(GPIO83) 0x03A0[14:12] + PAD_WF1_HB1(GPIO91) 0x03B0[14:12] + PAD_WF1_HB2(GPIO92) 0x03B0[18:16] + PAD_WF1_HB3(GPIO93) 0x03B0[22:20] + PAD_WF1_HB4(GPIO94) 0x03B0[26:24] + PAD_WF1_HB0(GPIO95) 0x03B0[30:28] + PAD_WF1_HB5(GPIO97) 0x03C0[6:4] + PAD_WF1_HB6(GPIO98) 0x03C0[10:8] + PAD_WF1_HB7(GPIO99) 0x03C0[14:12] + PAD_WF1_HB8(GPIO100) 0x03C0[18:16] + */ + CONSYS_REG_WRITE_MASK(REG_GPIO_BASE_ADDR + GPIO_MODE9, 0x1111100, 0x7777700); + CONSYS_REG_WRITE_MASK(REG_GPIO_BASE_ADDR + GPIO_MODE10, 0x1111, 0x7777); + CONSYS_REG_WRITE_MASK(REG_GPIO_BASE_ADDR + GPIO_MODE11, 0x11111000, 0x77777000); + CONSYS_REG_WRITE_MASK(REG_GPIO_BASE_ADDR + GPIO_MODE12, 0x11110, 0x77770); + } else { + if (one_adie_dbdc) { + /* One_Adie_DB + set pinmux for the interface between D-die and A-die (Aux2) + PAD_WF0_HB1(GPIO74) 0x0390[10:8] + PAD_WF0_HB2(GPIO75) 0x0390[14:12] + PAD_WF0_HB3(GPIO76) 0x0390[18:16] + PAD_WF0_HB4(GPIO77) 0x0390[22:20] + PAD_WF0_HB0(GPIO78) 0x0390[26:24] + PAD_WF0_HB0_B(GPIO79) 0x0390[30:28] + PAD_WF0_HB5(GPIO80) 0x03A0[2:0] + PAD_WF0_HB6(GPIO81) 0x03A0[6:4] + PAD_WF0_HB7(GPIO82) 0x03A0[10:8] + PAD_WF0_HB8(GPIO83) 0x03A0[14:12] + PAD_WF0_HB9(GPIO84) 0x03A0[18:16] + PAD_WF0_HB10(GPIO85) 0x03A0[22:20] + */ + CONSYS_REG_WRITE_MASK(REG_GPIO_BASE_ADDR + GPIO_MODE9, 0x22222200, 0x77777700); + CONSYS_REG_WRITE_MASK(REG_GPIO_BASE_ADDR + GPIO_MODE10, 0x222222, 0x777777); + } else { + /* One_Adie_SB + set pinmux for the interface between D-die and A-die (Aux1) + PAD_WF1_HB1(GPIO91) 0x03B0[14:12] + PAD_WF1_HB2(GPIO92) 0x03B0[18:16] + PAD_WF1_HB3(GPIO93) 0x03B0[22:20] + PAD_WF1_HB4(GPIO94) 0x03B0[26:24] + PAD_WF1_HB0(GPIO95) 0x03B0[30:28] + PAD_WF1_HB5(GPIO97) 0x03C0[6:4] + PAD_WF1_HB6(GPIO98) 0x03C0[10:8] + PAD_WF1_HB7(GPIO99) 0x03C0[14:12] + PAD_WF1_HB8(GPIO100) 0x03C0[18:16] + */ + CONSYS_REG_WRITE_MASK(REG_GPIO_BASE_ADDR + GPIO_MODE11, 0x11111000, 0x77777000); + CONSYS_REG_WRITE_MASK(REG_GPIO_BASE_ADDR + GPIO_MODE12, 0x11110, 0x77770); + } + } + /* Set pinmux driving to 4mA + 2mA: [000] + 4mA: [001] + 6mA: [010] + 8mA: [011] + 10mA: [100] + 12mA: [101] + 14mA: [110] + 16mA: [111] + PAD_WF0_HB1 0x0000[17:15] + PAD_WF0_HB2 0x0000[20:18] + PAD_WF0_HB3 0x0000[23:21] + PAD_WF0_HB4 0x0000[26:24] + PAD_WF0_HB0 0x0000[8:6] + PAD_WF0_HB0_B 0x0000[11:9] + PAD_WF0_HB5 0x0000[29:27] + PAD_WF0_HB6 0x0010[2:0] + PAD_WF0_HB7 0x0010[5:3] + PAD_WF0_HB8 0x0010[8:6] + PAD_WF0_HB9 0x0010[11:9] + PAD_WF0_HB10 0x0000[14:12] + PAD_WF0_TOP_CLK 0x0010[14:12] + PAD_WF0_TOP_DATA 0x0010[17:15] + + PAD_WF1_HB1 0x0000[14:12] + PAD_WF1_HB2 0x0000[17:15] + PAD_WF1_HB3 0x0000[20:18] + PAD_WF1_HB4 0x0000[23:21] + PAD_WF1_HB0 0x0000[8:6] + PAD_WF1_HB5 0x0000[26:24] + PAD_WF1_HB6 0x0000[29:27] + PAD_WF1_HB7 0x0010[2:0] + PAD_WF1_HB8 0x0010[5:3] + PAD_WF1_TOP_CLK 0x0010[8:6] + PAD_WF1_TOP_DATA 0x0010[11:9] + */ + CONSYS_REG_WRITE_MASK(REG_IOCFG_TR_ADDR + IOCFG_TR_DRV_CFG0, 0x9249240, 0x3FFFFFC0); + CONSYS_REG_WRITE_MASK(REG_IOCFG_TR_ADDR + IOCFG_TR_DRV_CFG1, 0x9249, 0x3FFF); + CONSYS_REG_WRITE_MASK(REG_IOCFG_TL_ADDR + IOCFG_TL_DRV_CFG0, 0x9249040, 0x3FFFF1C0); + CONSYS_REG_WRITE_MASK(REG_IOCFG_TL_ADDR + IOCFG_TL_DRV_CFG1, 0x249, 0xFFF); + } +} + +int consys_tx_rx_bus_slp_prot_ctrl(bool enable) +{ + int check; + + if (enable) { + /* conn2ap/ap2conn slpprot disable */ + /* Turn off AP2CONN AHB RX bus sleep protect */ + CONSYS_REG_WRITE_MASK(REG_INFRACFG_AO_ADDR + AP2CONN_GALS_SLPPROT, 0x0, 0x10000); + CONSYS_REG_BIT_POLLING(REG_INFRACFG_AO_ADDR + AP2CONN_GALS_SLPPROT, + 24, 0x0, 100, 500, check); + if (check != 0) + pr_err("Polling AP2CONN AHB RX bus sleep protect turn off fail! CR Value = 0x%08x\n", + CONSYS_REG_READ(REG_INFRACFG_AO_ADDR + AP2CONN_GALS_SLPPROT)); + + /* Turn off AP2CONN AHB TX bus sleep protect */ + CONSYS_REG_WRITE_MASK(REG_INFRACFG_AO_ADDR + AP2CONN_GALS_SLPPROT, 0x0, 0x1); + CONSYS_REG_BIT_POLLING(REG_INFRACFG_AO_ADDR + AP2CONN_GALS_SLPPROT, + 4, 0x0, 100, 500, check); + if (check != 0) + pr_err("Polling AP2CONN AHB TX bus sleep protect turn off fail! CR Value = 0x%08x\n", + CONSYS_REG_READ(REG_INFRACFG_AO_ADDR + AP2CONN_GALS_SLPPROT)); + + /* Turn off CONN2AP AXI RX bus sleep protect */ + CONSYS_REG_WRITE_MASK(REG_INFRACFG_AO_ADDR + CONN2AP_GALS_SLPPROT, 0x0, 0x10000); + /* Turn off CONN2AP AXI TX bus sleep protect */ + CONSYS_REG_WRITE_MASK(REG_INFRACFG_AO_ADDR + CONN2AP_GALS_SLPPROT, 0x0, 0x1); + + /* Wait 900us (apply this for CONNSYS XO clock ready) */ + udelay(900); + } else { + /* Turn on AP2CONN AHB TX bus sleep protect */ + CONSYS_REG_WRITE_MASK(REG_INFRACFG_AO_ADDR + AP2CONN_GALS_SLPPROT, 0x1, 0x1); + CONSYS_REG_BIT_POLLING(REG_INFRACFG_AO_ADDR + AP2CONN_GALS_SLPPROT, + 4, 0x1, 100, 500, check); + if (check != 1) + pr_err("Polling AP2CONN AHB TX bus sleep protect turn on fail! CR Value = 0x%08x\n", + CONSYS_REG_READ(REG_INFRACFG_AO_ADDR + AP2CONN_GALS_SLPPROT)); + + /* Turn on AP2CONN AHB RX bus sleep protec */ + CONSYS_REG_WRITE_MASK(REG_INFRACFG_AO_ADDR + AP2CONN_GALS_SLPPROT, 0x1, 0x10000); + CONSYS_REG_BIT_POLLING(REG_INFRACFG_AO_ADDR + AP2CONN_GALS_SLPPROT, + 24, 0x1, 100, 500, check); + if (check !=1) + pr_err("Polling AP2CONN AHB RX bus sleep protect turn on fail! CR Value = 0x%08x\n", + CONSYS_REG_READ(REG_INFRACFG_AO_ADDR + AP2CONN_GALS_SLPPROT)); + + /* Turn on CONN2AP AXI TX bus sleep protect */ + CONSYS_REG_WRITE_MASK(REG_INFRACFG_AO_ADDR + CONN2AP_GALS_SLPPROT, 0x1, 0x1); + CONSYS_REG_BIT_POLLING(REG_INFRACFG_AO_ADDR + CONN2AP_GALS_SLPPROT, + 4, 0x1, 100, 500, check); + if (check != 1) + pr_err("Polling CONN2AP AXI TX bus sleep protect turn on fail! CR Value = 0x%08x\n", + CONSYS_REG_READ(REG_INFRACFG_AO_ADDR + CONN2AP_GALS_SLPPROT)); + + /* Turn on CONN2AP AXI RX bus sleep protect */ + CONSYS_REG_WRITE_MASK(REG_INFRACFG_AO_ADDR + CONN2AP_GALS_SLPPROT, 0x1, 0x10000); + CONSYS_REG_BIT_POLLING(REG_INFRACFG_AO_ADDR + CONN2AP_GALS_SLPPROT, + 24, 0x1, 100, 500, check); + if (check != 1) + pr_err("Polling CONN2AP AXI RX bus sleep protect turn on fail! CR Value = 0x%08x\n", + CONSYS_REG_READ(REG_INFRACFG_AO_ADDR + CONN2AP_GALS_SLPPROT)); + + /* wait 1us*/ + udelay(1); + } + + return 0; +} + +int _consys_polling_chipid_int(unsigned int retry, unsigned int sleep_ms) +{ + unsigned int count = retry + 1; + unsigned int consys_hw_ver = consys_get_hw_ver(); + unsigned int hw_ver = 0; + + while (--count > 0) { + hw_ver = CONSYS_REG_READ(REG_CONN_INFRA_CFG_ADDR + CONN_INFRA_CFG_IP_VERSION); + if ((hw_ver >= consys_hw_ver) && (hw_ver != 0xdeadfeed)) + break; + msleep(sleep_ms); + } + + if (count == 0) { + pr_err("Read CONNSYS HW IP version fail. Expect 0x%x but get 0x%x\n", consys_hw_ver, hw_ver); + return -1; + } else { + pr_info("Read CONNSYS HW IP version successfully! (0x%08x)\n", hw_ver); + } + + return 0; +} + +int consys_polling_chipid(void) +{ + return _consys_polling_chipid_int(10, 1); +} + +int consys_bus_clock_ctrl(enum consys_drv_type drv_type, unsigned int bus_clock) +{ + static unsigned int conninfra_bus_clock_wpll_state = 0; + unsigned int wpll_state = conninfra_bus_clock_wpll_state; + bool wpll_switch = false; + + /* switch conn_infra bus clock pll ready check to pll-1 */ + if (bus_clock & CONNINFRA_BUS_CLOCK_WPLL) { + if (conninfra_bus_clock_wpll_state == 0) { + CONSYS_SET_BIT(REG_CONN_INFRA_CLKGEN_ON_TOP_ADDR + CKGEN_BUS, (0x1 << 29)); + wpll_switch = true; + } + conninfra_bus_clock_wpll_state |= (0x1 << drv_type); + } + pr_info("drv=[%d] conninfra_bus_clock_wpll=[%u]->[%u] %s\n", + drv_type, wpll_state, conninfra_bus_clock_wpll_state, (wpll_switch ? "enable" : "")); + + return 0; +} + +int consys_emi_set_remapping_reg(void) +{ + struct consys_emi_addr_info *addr_info = emi_mng_get_phy_addr(); + + /* 0x1806_01C4[19:0], ap_emi_base[19:0] = TBD (related to emi) + 0x1806_01D4[19:0], wf_ap_peri_base[19:0] = 0x0_1100 (un-related to emi) + */ + if (addr_info->emi_ap_phy_base != 0) + CONSYS_REG_WRITE_OFFSET_RANGE(REG_CONN_HOST_CSR_TOP_ADDR + CONN2AP_REMAP_MCU_EMI, + addr_info->emi_ap_phy_base, 0, 16, 20); + /* + CONSYS_REG_WRITE_OFFSET_RANGE(REG_CONN_HOST_CSR_TOP_ADDR + CONN2AP_REMAP_WF_PERI, + 0x300D0000, 0, 16, 20); + */ + + return 0; +} + +int consys_emi_set_region_protection(void) +{ + struct consys_emi_addr_info *addr_info = emi_mng_get_phy_addr(); + + /* set infra top emi address range */ + if (addr_info->emi_ap_phy_base != 0) { + CONSYS_REG_WRITE(REG_CONN_INFRA_BUS_CR_ADDR + CONN2AP_EMI_PATH_ADDR_START, + addr_info->emi_ap_phy_base); + + if (addr_info->emi_ap_phy_size != 0) + CONSYS_REG_WRITE(REG_CONN_INFRA_BUS_CR_ADDR + CONN2AP_EMI_PATH_ADDR_END, + addr_info->emi_ap_phy_base + addr_info->emi_ap_phy_size); + } + + return 0; +} + +int connsys_d_die_cfg(void) +{ + unsigned int efuse; + + efuse = CONSYS_REG_READ(REG_CONN_INFRA_CFG_ADDR + EFUSE); + pr_info("D-die efuse: 0x%08x\n", efuse); + + return 0; +} + +int connsys_conninfra_sysram_hw_ctrl(void) +{ + /* conn_infra sysram hw control setting -> disable hw power down */ + CONSYS_REG_WRITE(REG_CONN_INFRA_RGU_ADDR + SYSRAM_HWCTL_PDN, 0x0); + + /* conn_infra sysram hw control setting -> enable hw sleep */ + CONSYS_REG_WRITE(REG_CONN_INFRA_RGU_ADDR + SYSRAM_HWCTL_SLP, 0x1); + + return 0; +} + +int connsys_spi_master_cfg(void) +{ + /* wt_slp CR for A-die ck_en/wake_en control */ + /* + RFSPI #0 RFSPI #1 + WF_CK_ADDR 0x18005070[11:0] 0x18085070[11:0] 0xA04 + WF_B1_CK_ADDR 0x18005070[27:16] 0x18085070[27:16] 0xAF4 + WF_WAKE_ADDR 0x18005074[11:0] 0x18085074[11:0] 0x090 + WF_B1_WAKE_ADDR 0x18005074[27:16] 0x18085074[27:16] 0x0A0 + WF_ZPS_ADDR 0x18005078[11:0] 0x18085078[11:0] 0x08C + WF_B1_ZPS_ADDR 0x18005078[27:16] 0x18085078[27:16] 0x09C + TOP_CK_ADDR 0x18005084[11:0] 0x18085084[11:0] 0xA00 + WF_B0_CMD_ADDR 0x1800508c[11:0] 0x1808508c[11:0] 0x0F0 + WF_B1_CMD_ADDR 0x18005090[11:0] 0x18085090[11:0] 0x0F4 + */ + CONSYS_REG_WRITE_MASK(REG_CONN_WT_SLP_CTL_REG_ADDR + WB_WF_CK_ADDR, 0xAF40A04, 0xFFF0FFF); + CONSYS_REG_WRITE_MASK(REG_CONN_WT_SLP_CTL_REG_ADDR + WB_WF_WAKE_ADDR, 0x0A00090, 0xFFF0FFF); + CONSYS_REG_WRITE_MASK(REG_CONN_WT_SLP_CTL_REG_ADDR + WB_WF_ZPS_ADDR, 0x09C008C, 0xFFF0FFF); + CONSYS_REG_WRITE_MASK(REG_CONN_WT_SLP_CTL_REG_ADDR + WB_TOP_CK_ADDR, 0xA00, 0xFFF); + CONSYS_REG_WRITE_MASK(REG_CONN_WT_SLP_CTL_REG_ADDR + WB_WF_B0_CMD_ADDR, 0x0F0, 0xFFF); + CONSYS_REG_WRITE_MASK(REG_CONN_WT_SLP_CTL_REG_ADDR + WB_WF_B1_CMD_ADDR, 0x0F4, 0xFFF); + CONSYS_REG_WRITE_MASK(REG_INST2_CONN_WT_SLP_CTL_REG_ADDR + WB_WF_CK_ADDR, 0xAF40A04, 0xFFF0FFF); + CONSYS_REG_WRITE_MASK(REG_INST2_CONN_WT_SLP_CTL_REG_ADDR + WB_WF_WAKE_ADDR, 0x0A00090, 0xFFF0FFF); + CONSYS_REG_WRITE_MASK(REG_INST2_CONN_WT_SLP_CTL_REG_ADDR + WB_WF_ZPS_ADDR, 0x09C008C, 0xFFF0FFF); + CONSYS_REG_WRITE_MASK(REG_INST2_CONN_WT_SLP_CTL_REG_ADDR + WB_TOP_CK_ADDR, 0xA00, 0xFFF); + CONSYS_REG_WRITE_MASK(REG_INST2_CONN_WT_SLP_CTL_REG_ADDR + WB_WF_B0_CMD_ADDR, 0x0F0, 0xFFF); + CONSYS_REG_WRITE_MASK(REG_INST2_CONN_WT_SLP_CTL_REG_ADDR + WB_WF_B1_CMD_ADDR, 0x0F4, 0xFFF); + + return 0; +} + +static int consys_spi_read_nolock(enum sys_spi_subsystem subsystem, unsigned int addr, unsigned int *data) +{ + int check = 0; + unsigned long rf_spi_addr = 0; + const struct spi_op *op = NULL; + unsigned char adie_idx = ((subsystem & 0xF0) >> 4); //0: one adie, 1: two adie + unsigned char subsystem_idx = (subsystem & 0xF); + + if (!data) { + pr_err("invalid data ptr\n"); + return CONNINFRA_SPI_OP_FAIL; + } + + op = &spi_op_array[subsystem_idx]; + if (adie_idx != 0) + rf_spi_addr = REG_INST2_CONN_RF_SPI_MST_REG_ADDR; + else + rf_spi_addr = REG_CONN_RF_SPI_MST_REG_ADDR; + + /* Read action: + * 1. Polling busy_cr[polling_bit] should be 0 + * 2. Write addr_cr with data being {read_addr_format | addr[11:0]} + * 3. Trigger SPI by writing write_data_cr as 0 + * 4. Polling busy_cr[polling_bit] as 0 + * 5. Read data_cr[data_mask] + */ + + CONSYS_REG_BIT_POLLING(rf_spi_addr + op->busy_cr, op->polling_bit, 0, 100, 500, check); + if (check != 0) { + pr_err("[%d][STEP1] polling 0x%08lx bit %d fail. Value=0x%08x\n", + subsystem, rf_spi_addr + op->busy_cr, op->polling_bit, + CONSYS_REG_READ(rf_spi_addr + op->busy_cr)); + return CONNINFRA_SPI_OP_FAIL; + } + + CONSYS_REG_WRITE(rf_spi_addr + op->addr_cr, (op->read_addr_format | addr)); + CONSYS_REG_WRITE(rf_spi_addr + op->write_data_cr, 0); + + CONSYS_REG_BIT_POLLING(rf_spi_addr + op->busy_cr, op->polling_bit, 0, 100, 500, check); + if (check != 0) { + pr_err("[%d][STEP4] polling 0x%08lx bit %d fail. Value=0x%08x\n", + subsystem, rf_spi_addr + op->busy_cr, + op->polling_bit, CONSYS_REG_READ(rf_spi_addr + op->busy_cr)); + return CONNINFRA_SPI_OP_FAIL; + } + + check = CONSYS_REG_READ_BIT(rf_spi_addr + op->read_data_cr, op->read_data_mask); + *data = check; + + return 0; +} + +static int consys_spi_write_nolock(enum sys_spi_subsystem subsystem, unsigned int addr, unsigned int data) +{ + int check = 0; + unsigned long rf_spi_addr = 0; + const struct spi_op *op = NULL; + unsigned char adie_idx = ((subsystem & 0xF0) >> 4); //0: one adie, 1: two adie + unsigned char subsystem_idx = (subsystem & 0xF); + + op = &spi_op_array[subsystem_idx]; + if (adie_idx != 0) + rf_spi_addr = REG_INST2_CONN_RF_SPI_MST_REG_ADDR; + else + rf_spi_addr = REG_CONN_RF_SPI_MST_REG_ADDR; + + /* Write action: + * 1. Wait busy_cr[polling_bit] as 0 + * 2. Write addr_cr with data being {write_addr_format | addr[11:0] + * 3. Write write_data_cr ad data + * 4. Wait busy_cr[polling_bit] as 0 + */ + + CONSYS_REG_BIT_POLLING(rf_spi_addr + op->busy_cr, op->polling_bit, 0, 100, 500, check); + if (check != 0) { + pr_err("[%d][STEP1] polling 0x%08lx bit %d fail. Value=0x%08x\n", + subsystem, rf_spi_addr + op->busy_cr, + op->polling_bit, CONSYS_REG_READ(rf_spi_addr + op->busy_cr)); + return CONNINFRA_SPI_OP_FAIL; + } + + CONSYS_REG_WRITE(rf_spi_addr + op->addr_cr, (op->write_addr_format | addr)); + CONSYS_REG_WRITE(rf_spi_addr + op->write_data_cr, data); + + check = 0; + CONSYS_REG_BIT_POLLING(rf_spi_addr + op->busy_cr, op->polling_bit, 0, 100, 500, check); + if (check != 0) { + pr_err("[%d][STEP4] polling 0x%08lx bit %d fail. Value=0x%08x\n", + subsystem, rf_spi_addr + op->busy_cr, + op->polling_bit, CONSYS_REG_READ(rf_spi_addr + op->busy_cr)); + return CONNINFRA_SPI_OP_FAIL; + } + + pr_info("addr = 0x%04x, val = 0x%08x\n", addr, data); + + return 0; +} + +static int consys_sema_acquire(enum conn_semaphore_type index) +{ + if (CONSYS_REG_READ_BIT((REG_CONN_SEMAPHORE_ADDR + CONN_SEMA00_M2_OWN_STA + index*4), 0x1) == 0x1) { + return CONN_SEMA_GET_SUCCESS; + } else { + return CONN_SEMA_GET_FAIL; + } +} + +int consys_sema_acquire_timeout(unsigned int index, unsigned int usec) +{ + int i; + + if (index >= CONN_SEMA_NUM_MAX) { + pr_err("wrong index: %d\n", index); + return CONN_SEMA_GET_FAIL; + } + + for (i = 0; i < usec; i++) { + if (consys_sema_acquire(index) == CONN_SEMA_GET_SUCCESS) { + return CONN_SEMA_GET_SUCCESS; + } + udelay(1); + } + pr_err("Get semaphore 0x%x timeout, dump status:\n", index); + pr_err("M0:[0x%x] M1:[0x%x] M2:[0x%x] M3:[0x%x] M4:[0x%x] M5:[0x%x] M6:[0x%x] M7:[0x%x]\n", + CONSYS_REG_READ(REG_CONN_SEMAPHORE_ADDR + CONN_SEMA_OWN_BY_M0_STA_REP), + CONSYS_REG_READ(REG_CONN_SEMAPHORE_ADDR + CONN_SEMA_OWN_BY_M1_STA_REP), + CONSYS_REG_READ(REG_CONN_SEMAPHORE_ADDR + CONN_SEMA_OWN_BY_M2_STA_REP), + CONSYS_REG_READ(REG_CONN_SEMAPHORE_ADDR + CONN_SEMA_OWN_BY_M3_STA_REP), + CONSYS_REG_READ(REG_CONN_SEMAPHORE_ADDR + CONN_SEMA_OWN_BY_M4_STA_REP), + CONSYS_REG_READ(REG_CONN_SEMAPHORE_ADDR + CONN_SEMA_OWN_BY_M5_STA_REP), + CONSYS_REG_READ(REG_CONN_SEMAPHORE_ADDR + CONN_SEMA_OWN_BY_M6_STA_REP), + CONSYS_REG_READ(REG_CONN_SEMAPHORE_ADDR + CONN_SEMA_OWN_BY_M7_STA_REP)); + + return CONN_SEMA_GET_FAIL; +} + +void consys_sema_release(unsigned int index) +{ + if (index >= CONN_SEMA_NUM_MAX) { + pr_err("wrong index: %d\n", index); + return; + } + + CONSYS_REG_WRITE((REG_CONN_SEMAPHORE_ADDR + CONN_SEMA00_M2_OWN_REL + index*4), 0x1); +} + +int consys_spi_read(enum sys_spi_subsystem subsystem, unsigned int addr, unsigned int *data) +{ + int ret; + + /* Get semaphore before read */ + if (consys_sema_acquire_timeout(CONN_SEMA_RFSPI_INDEX, CONN_SEMA_TIMEOUT) == CONN_SEMA_GET_FAIL) { + pr_err("[SPI READ] Require semaphore fail\n"); + return CONNINFRA_SPI_OP_FAIL; + } + + ret = consys_spi_read_nolock(subsystem, addr, data); + + consys_sema_release(CONN_SEMA_RFSPI_INDEX); + + return ret; +} + +int consys_spi_write(enum sys_spi_subsystem subsystem, unsigned int addr, unsigned int data) +{ + int ret; + + /* Get semaphore before read */ + if (consys_sema_acquire_timeout(CONN_SEMA_RFSPI_INDEX, CONN_SEMA_TIMEOUT) == CONN_SEMA_GET_FAIL) { + pr_err("[SPI WRITE] Require semaphore fail\n"); + return CONNINFRA_SPI_OP_FAIL; + } + + ret = consys_spi_write_nolock(subsystem, addr, data); + + consys_sema_release(CONN_SEMA_RFSPI_INDEX); + return ret; +} + +static void consys_spi_write_offset_range_nolock( + enum sys_spi_subsystem subsystem, unsigned int addr, unsigned int value, + unsigned int reg_offset, unsigned int value_offset, unsigned int size) +{ + unsigned int data = 0, data2; + unsigned int reg_mask; + int ret; + + pr_info("[%s] addr=0x%04x value=0x%08x reg_offset=%d value_offset=%d size=%d\n", + g_spi_system_name[subsystem], addr, value, reg_offset, value_offset, size); + + value = (value >> value_offset); + value = GET_BIT_RANGE(value, size, 0); + value = (value << reg_offset); + ret = consys_spi_read_nolock(subsystem, addr, &data); + if (ret) { + pr_err("[%s] Get 0x%08x error, ret=%d\n", + g_spi_system_name[subsystem], addr, ret); + return; + } + + reg_mask = GENMASK(reg_offset + size - 1, reg_offset); + data2 = data & (~reg_mask); + data2 = (data2 | value); + consys_spi_write_nolock(subsystem, addr, data2); + + pr_info("[%s] Write CR:0x%08x from 0x%08x to 0x%08x\n", + g_spi_system_name[subsystem], addr, data, data2); +} + +int consys_spi_write_offset_range( + enum sys_spi_subsystem subsystem, unsigned int addr, unsigned int value, + unsigned int reg_offset, unsigned int value_offset, unsigned int size) +{ + if (consys_sema_acquire_timeout(CONN_SEMA_RFSPI_INDEX, CONN_SEMA_TIMEOUT) == CONN_SEMA_GET_FAIL) { + pr_err("[SPI READ] Require semaphore fail\n"); + return CONNINFRA_SPI_OP_FAIL; + } + consys_spi_write_offset_range_nolock(subsystem, addr, value, reg_offset, value_offset, size); + + consys_sema_release(CONN_SEMA_RFSPI_INDEX); + + return 0; +} + +/***************************************************************************** +* FUNCTION +* connsys_a_die_efuse_read +* DESCRIPTION +* Read a-die efuse +* PARAMETERS +* efuse_addr: read address +* RETURNS +* int +* 0: fail, efuse is invalid +* 1: success, efuse is valid +*****************************************************************************/ +static int connsys_a_die_efuse_read_nolock( + enum sys_spi_subsystem subsystem, unsigned int efuse_ctrl, unsigned int efuse_addr, + unsigned int *data0, unsigned int *data1, unsigned int *data2, unsigned int *data3) +{ + int ret = 0; + int retry = 0; + int ret0, ret1, ret2, ret3; + unsigned int efuse_block_sel; + + if (data0 == NULL || data1 == NULL || data2 == NULL || data3 == NULL) { + pr_err("invalid parameter (%p, %p, %p, %p)\n", + data0, data1, data2, data3); + return 0; + } + + switch (efuse_ctrl) { + case ATOP_EFUSE_CTRL_1: + efuse_block_sel = 0x1; + break; + + case ATOP_EFUSE_CTRL_2: + efuse_block_sel = 0x2; + break; + + case ATOP_EFUSE_CTRL_3: + efuse_block_sel = 0x4; + break; + + case ATOP_EFUSE_CTRL_4: + efuse_block_sel = 0x8; + break; + + default: + pr_err("No support for efuse block No. = %d\n", efuse_ctrl); + return 0; + break; + } + + /* select Efuse block */ + consys_spi_write_nolock(subsystem, ATOP_RG_EFUSE_CFG5, efuse_block_sel); + + /* Efuse control clear, clear Status /trigger + * Address: ATOP EFUSE_CTRL_write_efsrom_kick_and_read_kick_busy_flag (0x108[30]) + * Data: 1'b0 + * Action: TOPSPI_WR + */ + consys_spi_read_nolock(subsystem, efuse_ctrl, &ret); + ret &= ~(0x1 << 30); + consys_spi_write_nolock(subsystem, efuse_ctrl, ret); + + /* Efuse Read 1st 16byte + * Address: + * ATOP EFUSE_CTRL_efsrom_mode (0x108[7:6]) = 2'b00 + * ATOP EFUSE_CTRL_efsrom_ain (0x108[25:16]) = efuse_addr (0) + * ATOP EFUSE_CTRL_write_efsrom_kick_and_read_kick_busy_flag (0x108[30]) = 1'b1 + * Action: TOPSPI_WR + */ + consys_spi_read_nolock(subsystem, efuse_ctrl, &ret); + ret &= ~(0x43FF00C0); + ret |= (0x1 << 30); + ret |= ((efuse_addr << 16) & 0x3FF0000); + consys_spi_write_nolock(subsystem, efuse_ctrl, ret); + + /* Polling EFUSE busy = low + * (each polling interval is "30us" and polling timeout is 2ms) + * Address: + * ATOP EFUSE_CTRL_write_efsrom_kick_and_read_kick_busy_flag (0x108[30]) = 1'b0 + * Action: TOPSPI_Polling + */ + consys_spi_read_nolock(subsystem, efuse_ctrl, &ret); + while ((ret & (0x1 << 30)) != 0 && retry < 70) { + retry++; + udelay(30); + consys_spi_read_nolock(subsystem, efuse_ctrl, &ret); + } + if ((ret & (0x1 << 30)) != 0) { + pr_err("EFUSE busy, retry failed(%d)\n", retry); + } + + /* Check efuse_valid & return + * Address: ATOP EFUSE_CTRL_csri_efsrom_dout_vld_sync_1_ (0x108[29]) + * Action: TOPSPI_RD + */ + /* if (efuse_valid == 1'b1) + * Read Efuse Data to global var + */ + consys_spi_read_nolock(subsystem, efuse_ctrl, &ret); + if (((ret & (0x1 << 29)) >> 29) == 1) { + ret0 = consys_spi_read_nolock(subsystem, ATOP_EFUSE_RDATA0, data0); + ret1 = consys_spi_read_nolock(subsystem, ATOP_EFUSE_RDATA1, data1); + ret2 = consys_spi_read_nolock(subsystem, ATOP_EFUSE_RDATA2, data2); + ret3 = consys_spi_read_nolock(subsystem, ATOP_EFUSE_RDATA3, data3); + + pr_info("efuse = [0x%08x, 0x%08x, 0x%08x, 0x%08x]\n", *data0, *data1, *data2, *data3); + if (ret0 || ret1 || ret2 || ret3) + pr_err("efuse read error: [%d, %d, %d, %d]\n", ret0, ret1, ret2, ret3); + ret = 1; + } else { + pr_err("EFUSE is invalid\n"); + ret = 0; + } + + return ret; +} + +static int _connsys_a_die_thermal_cal(enum sys_spi_subsystem subsystem) +{ + int efuse_valid = 0; + unsigned int efuse0 = 0, efuse1 = 0, efuse2 = 0, efuse3 = 0; + + /* thernal efuse data in 7976&7975 in EFUSE2 */ + efuse_valid = connsys_a_die_efuse_read_nolock(subsystem, ATOP_EFUSE_CTRL_2, ATOP_THADC_ANALOG, + &efuse0, &efuse1, &efuse2, &efuse3); + //if (efuse_valid) { + if ((efuse0 & (0x1 << 7))) { + consys_spi_write_offset_range_nolock(subsystem, ATOP_RG_TOP_THADC_BG, efuse0, 12, 3, 4); + consys_spi_write_offset_range_nolock(subsystem, ATOP_RG_TOP_THADC_00, efuse0, 23, 0, 3); + } + //} + + efuse_valid = connsys_a_die_efuse_read_nolock(subsystem, ATOP_EFUSE_CTRL_2, ATOP_THADC_SLOP, + &efuse0, &efuse1, &efuse2, &efuse3); + //if (efuse_valid) { + if((efuse0 & (0x1 << 7))) { + consys_spi_write_offset_range_nolock(subsystem, ATOP_RG_TOP_THADC_00, efuse0, 26, 5, 2); + } + //} + + return 0; +} + +static int _connsys_a_die_xtal_trim_7976(enum sys_spi_subsystem subsystem) +{ + unsigned int efuse0 = 0, efuse1 = 0, efuse2 = 0, efuse3 = 0; + int c1c2_trim_result_ax_80m = 0, c1c2_trim_result_ax_40m = 0; + unsigned int cbtop_strap_rdata = 0, xtal_strap_mode = 0, adie_rdata = 0, value = 0; + + connsys_a_die_efuse_read_nolock(subsystem, ATOP_EFUSE_CTRL_2, ATOP_XTAL_TRIM_FLOW, + &efuse0, &efuse1, &efuse2, &efuse3); + if ((efuse0 & (0x1 < 1))) { + /* C1C2 80M AX */ + connsys_a_die_efuse_read_nolock(subsystem, ATOP_EFUSE_CTRL_2, ATOP_XTAL_CR_C1_SEL_AXM_80M_OSC, + &efuse0, &efuse1, &efuse2, &efuse3); + if ((efuse0 & (0x1 < 7)) == 0) { + c1c2_trim_result_ax_80m = 64; + } else { + c1c2_trim_result_ax_80m = (efuse0 & 0x7F); + connsys_a_die_efuse_read_nolock(subsystem, ATOP_EFUSE_CTRL_2, ATOP_XTAL_CR_C1_SEL_AXM_TRIM1_80M_OSC, + &efuse0, &efuse1, &efuse2, &efuse3); + if ((efuse0 & (0x1 < 7)) == 1) { + if ((efuse0 & (0x1 < 6)) == 0) { + c1c2_trim_result_ax_80m = c1c2_trim_result_ax_80m + (efuse0 & 0x3F); + } else { + c1c2_trim_result_ax_80m = c1c2_trim_result_ax_80m - (efuse0 & 0x3F); + } + + if (c1c2_trim_result_ax_80m > 127) + c1c2_trim_result_ax_80m = 127; + else if (c1c2_trim_result_ax_80m < 0) + c1c2_trim_result_ax_80m = 0; + } + } + + /* C1C2 40M AX */ + connsys_a_die_efuse_read_nolock(subsystem, ATOP_EFUSE_CTRL_2, ATOP_XTAL_CR_C1_SEL_AXM_40M_OSC, + &efuse0, &efuse1, &efuse2, &efuse3); + if ((efuse0 & (0x1 < 7)) == 0) { + c1c2_trim_result_ax_40m = 64; + } else { + c1c2_trim_result_ax_40m = (efuse0 & 0x7F); + connsys_a_die_efuse_read_nolock(subsystem, ATOP_EFUSE_CTRL_2, ATOP_XTAL_CR_C1_SEL_AXM_TRIM1_40M_OSC, + &efuse0, &efuse1, &efuse2, &efuse3); + if ((efuse0 & (0x1 < 7)) == 1) { + if ((efuse0 & (0x1 < 6)) == 0) { + c1c2_trim_result_ax_40m = c1c2_trim_result_ax_40m + (efuse0 & 0x3F); + } else { + c1c2_trim_result_ax_40m = c1c2_trim_result_ax_40m - (efuse0 & 0x3F); + } + + if (c1c2_trim_result_ax_40m > 127) + c1c2_trim_result_ax_40m = 127; + else if (c1c2_trim_result_ax_40m < 0) + c1c2_trim_result_ax_40m = 0; + } + } + + /* Update trim value to C1 and C2 */ + consys_spi_read_nolock(subsystem, ATOP_RG_STRAP_PIN_IN, &cbtop_strap_rdata); + xtal_strap_mode = ((cbtop_strap_rdata & 0x70) >> 4); + if ((xtal_strap_mode == 0x0) || (xtal_strap_mode == 0x2)) { //80m osc + /* C1 */ + consys_spi_read_nolock(subsystem, 0x654, &adie_rdata); + value = (adie_rdata & 0xFFFFFF) | ((c1c2_trim_result_ax_80m & 0xFF) << 24); + consys_spi_write_nolock(subsystem, 0x654, value); + + /* C2 */ + consys_spi_read_nolock(subsystem, 0x658, &adie_rdata); + value = (adie_rdata & 0xFFFFFF) | ((c1c2_trim_result_ax_80m & 0xFF) << 24); + consys_spi_write_nolock(subsystem, 0x658, value); + } else if ((xtal_strap_mode == 0x3) || (xtal_strap_mode == 0x4) || (xtal_strap_mode == 0x6)) { //40m osc + /* C1 */ + consys_spi_read_nolock(subsystem, 0x654, &adie_rdata); + value = (adie_rdata & 0xFF00FFFF) | ((c1c2_trim_result_ax_40m & 0xFF) << 16); + consys_spi_write_nolock(subsystem, 0x654, value); + + /* C2 */ + consys_spi_read_nolock(subsystem, 0x658, &adie_rdata); + value = (adie_rdata & 0xFF00FFFF) | ((c1c2_trim_result_ax_40m & 0xFF) << 16); + consys_spi_write_nolock(subsystem, 0x658, value); + } + } + + return 0; +} + +static int _connsys_a_die_sw_cntl(enum sys_spi_subsystem subsystem, unsigned char adie_idx) +{ + if (conn_hw_env[adie_idx].valid && (conn_hw_env[adie_idx].adie_id == 0x7976)) { + if ((conn_hw_env[adie_idx].adie_hw_version == 0x8A00) + || (conn_hw_env[adie_idx].adie_hw_version == 0x8A10) + || (conn_hw_env[adie_idx].adie_hw_version == 0x8B00)){ + consys_spi_write_nolock(subsystem, ATOP_RG_TOP_THADC_00, 0x4A563B00); + consys_spi_write_nolock(subsystem, ATOP_RG_XO_01, 0x1D59080F); + consys_spi_write_nolock(subsystem, ATOP_RG_XO_03, 0x34C00FE0); + } else { + consys_spi_write_nolock(subsystem, ATOP_RG_TOP_THADC_00, 0x4A563B00); + consys_spi_write_nolock(subsystem, ATOP_RG_XO_01, 0x1959F80F); + consys_spi_write_nolock(subsystem, ATOP_RG_XO_03, 0x34D00FE0); + } + } + + return 0; +} + +int _connsys_a_die_cfg_7976(unsigned char adie_idx) +{ + int check; + unsigned int adie_chip_id = 0x0; + unsigned char subsystem = 0; + + if (adie_idx == 1) + subsystem = SYS_SPI_2ND_ADIE_TOP; + else + subsystem = SYS_SPI_TOP; + + /* release D Die to A Die Digital reset_b */ + if (adie_idx == 1) + CONSYS_SET_BIT(REG_CONN_INFRA_CFG_ADDR + ADIE_CTL, 0x4); + else + CONSYS_SET_BIT(REG_CONN_INFRA_CFG_ADDR + ADIE_CTL, 0x1); + + /* Get semaphore once */ + if (consys_sema_acquire_timeout(CONN_SEMA_RFSPI_INDEX, CONN_SEMA_TIMEOUT) == CONN_SEMA_GET_FAIL) { + pr_err("Require semaphore fail\n"); + return -1; + } + + /* read a-die ID */ + check = consys_spi_read_nolock(subsystem, ATOP_CHIP_ID, &adie_chip_id); + if (check) { + /* Release semaphore */ + consys_sema_release(CONN_SEMA_RFSPI_INDEX); + pr_err("Get ATOP_CHIP_ID fail, check = %d\n", check); + return -1; + } + + /* enable TOPDIG CK */ + check = consys_spi_write_nolock(subsystem, ATOP_TOP_CLK_EN, 0xFFFFFFFF); + + /* config WRI CK select */ + if (one_adie_dbdc) + check = consys_spi_write_nolock(subsystem, ATOP_RG_WRI_CK_SELECT, 0x1C); + + /* Thermal Cal (TOP) */ + _connsys_a_die_thermal_cal(subsystem); + + /* XTAL TRIM */ + _connsys_a_die_xtal_trim_7976(subsystem); + + /* SW control part */ + _connsys_a_die_sw_cntl(subsystem, adie_idx); + + /* Release semaphore */ + consys_sema_release(CONN_SEMA_RFSPI_INDEX); + + return 0; +} + +static int _connsys_a_die_xtal_trim_7975(enum sys_spi_subsystem subsystem) +{ + unsigned int efuse0 = 0, efuse1 = 0, efuse2 = 0, efuse3 = 0; + unsigned int trim_result = 0, value = 0; + int ret = 0; + + ret = connsys_a_die_efuse_read_nolock(subsystem, ATOP_EFUSE_CTRL_2, ATOP_7975_XTAL_TRIM_FLOW, + &efuse0, &efuse1, &efuse2, &efuse3); + if (((efuse0 & 0x1) == 0) || (ret == 0)) + return 0; + + connsys_a_die_efuse_read_nolock(subsystem, ATOP_EFUSE_CTRL_2, ATOP_7975_XTAL_CALIBRATION, + &efuse0, &efuse1, &efuse2, &efuse3); + if ((efuse0 & (0x1 << 7))) { + trim_result = (efuse0 & 0x7F); + trim_result = (trim_result & 0x7F); + } + + connsys_a_die_efuse_read_nolock(subsystem, ATOP_EFUSE_CTRL_2, ATOP_7975_XTAL_TRIM2_COMPENSATION, + &efuse0, &efuse1, &efuse2, &efuse3); + if ((efuse0 & (0x1 << 7))){ + if ((efuse0 & (0x1 << 6))) + trim_result -= (efuse0 & 0x3F); + else + trim_result += (efuse0 & 0x3F); + trim_result = (trim_result & 0x7F); + } + + connsys_a_die_efuse_read_nolock(subsystem, ATOP_EFUSE_CTRL_2, ATOP_7975_XTAL_TRIM3_COMPENSATION, + &efuse0, &efuse1, &efuse2, &efuse3); + if ((efuse0 & (0x1 << 7))){ + if ((efuse0 & (0x1 << 6))) + trim_result -= (efuse0 & 0x3F); + else + trim_result += (efuse0 & 0x3F); + trim_result = (trim_result & 0x7F); + } + + connsys_a_die_efuse_read_nolock(subsystem, ATOP_EFUSE_CTRL_2, ATOP_7975_XTAL_TRIM4_COMPENSATION, + &efuse0, &efuse1, &efuse2, &efuse3); + if ((efuse0 & (0x1 << 7))){ + if ((efuse0 & (0x1 << 6))) + trim_result -= (efuse0 & 0x3F); + else + trim_result += (efuse0 & 0x3F); + trim_result = (trim_result & 0x7F); + } + + /* Update Trim Value to C1 and C2*/ + /* Step 1 */ + consys_spi_read_nolock(subsystem, ATOP_7975_CR_C1_C2_A94, &value); + value = ((value & 0xf8080fff) | ((trim_result << 20) | (trim_result << 12))); + consys_spi_write_nolock(subsystem, ATOP_7975_CR_C1_C2_A94, value); + + /* Step 2 */ + consys_spi_read_nolock(subsystem, ATOP_7975_CR_C1_C2_A18, &value); + if(value & (1<<29)){ + consys_spi_read_nolock(subsystem, ATOP_7975_CR_C1_C2_A84, &value); + value = (value & 0x7fffffff); + consys_spi_write_nolock(subsystem, ATOP_7975_CR_C1_C2_A84, value); + } + + /* Step 3 */ + consys_spi_read_nolock(subsystem, ATOP_7975_CR_C1_C2_AA4, &value); + value = ((value & 0xfffeffff) | 0x10000); + consys_spi_write_nolock(subsystem, ATOP_7975_CR_C1_C2_AA4, value); + + return 0; +} + + +static int _connsys_a_die_form_patch_7975(enum sys_spi_subsystem subsystem) +{ + pr_info("Form 7975 adie Patch\n"); + + /* disable CAL LDO and fine tune RFDIG LDO, 20191218 */ + consys_spi_write_nolock(subsystem, 0x348, 0x00000002); + + /* disable CAL LDO and fine tune RFDIG LDO, 20191218 */ + consys_spi_write_nolock(subsystem, 0x378, 0x00000002); + + /* disable CAL LDO and fine tune RFDIG LDO, 20191218 */ + consys_spi_write_nolock(subsystem, 0x3A8, 0x00000002); + + /* disable CAL LDO and fine tune RFDIG LDO, 20191218 */ + consys_spi_write_nolock(subsystem, 0x3D8, 0x00000002); + + /* set CKA driving and filter */ + consys_spi_write_nolock(subsystem, 0xA1C, 0x30000AAA); + + /* set CKB LDO to 1.4V */ + consys_spi_write_nolock(subsystem, 0xA84, 0x8470008A); + + /* turn on SX0 LTBUF */ + consys_spi_write_nolock(subsystem, 0x074, 0x00000002); + + /* CK_BUF_SW_EN=1 (all buf in manual mode.) */ + consys_spi_write_nolock(subsystem, 0xAA4, 0x01001FC0); + + /* BT mode/WF normal mode 32?™h=00000005 */ + consys_spi_write_nolock(subsystem, 0x070, 0x00000005); + + /* BG thermal sensor offset update */ + consys_spi_write_nolock(subsystem, 0x344, 0x00000088); + + /* BG thermal sensor offset update */ + consys_spi_write_nolock(subsystem, 0x374, 0x00000088); + + /* BG thermal sensor offset update */ + consys_spi_write_nolock(subsystem, 0x3A4, 0x00000088); + + /* BG thermal sensor offset update */ + consys_spi_write_nolock(subsystem, 0x3D4, 0x00000088); + + /* set WCON VDD IPTAT to "0000" */ + consys_spi_write_nolock(subsystem, 0xA80, 0x44D07000); + + /* change back LTBUF SX3 drving to default value, 20191113 */ + consys_spi_write_nolock(subsystem, 0xA88, 0x3900AAAA); + + /* SM input cap off */ + consys_spi_write_nolock(subsystem, 0x2C4, 0x00000000); + + /* set CKB driving and filter */ + consys_spi_write_nolock(subsystem, 0x2C8, 0x00000072); + + return 0; +} + +int _connsys_a_die_cfg_7975(unsigned char adie_idx) +{ + int check; + unsigned int adie_chip_id = 0x0; + unsigned int value = 0x0; + unsigned char subsystem = 0; + + if (adie_idx == 1) + subsystem = SYS_SPI_2ND_ADIE_TOP; + else + subsystem = SYS_SPI_TOP; + + /* release D Die to A Die Digital reset_b */ + if (adie_idx == 1) + CONSYS_SET_BIT(REG_CONN_INFRA_CFG_ADDR + ADIE_CTL, 0x4); + else + CONSYS_SET_BIT(REG_CONN_INFRA_CFG_ADDR + ADIE_CTL, 0x1); + + /* Get semaphore once */ + if (consys_sema_acquire_timeout(CONN_SEMA_RFSPI_INDEX, CONN_SEMA_TIMEOUT) == CONN_SEMA_GET_FAIL) { + pr_err("Require semaphore fail\n"); + return -1; + } + + /* read a-die ID */ + check = consys_spi_read_nolock(subsystem, ATOP_CHIP_ID, &adie_chip_id); + if (check) { + /* Release semaphore */ + consys_sema_release(CONN_SEMA_RFSPI_INDEX); + pr_err("Get ATOP_CHIP_ID fail, check = %d\n", check); + return -1; + } + + /* enable TOPDIG CK */ + check = consys_spi_write_nolock(subsystem, ATOP_TOP_CLK_EN, 0xFFFFFFFF); + + /* Disable XO_OUT_B */ + check = consys_spi_read_nolock(subsystem, ATOP_7975_CO_CLK, &value); + check = consys_spi_write_nolock(subsystem, ATOP_7975_CO_CLK, value | 0x02); + + /* Thermal Cal (TOP) */ + _connsys_a_die_thermal_cal(subsystem); + + /* XTAL TRIM */ + _connsys_a_die_xtal_trim_7975(subsystem); + + /* Form Harrier E2 Patch */ + _connsys_a_die_form_patch_7975(subsystem); + + /* Release semaphore */ + consys_sema_release(CONN_SEMA_RFSPI_INDEX); + + return 0; +} + +int connsys_a_die_cfg(void) +{ + int ret = 0; + unsigned int i; + + if (one_adie_dbdc) { + /* use adie_idx = 0 */ + if (conn_hw_env[0].valid) { + if (conn_hw_env[0].adie_id == 0x7976) { + ret = _connsys_a_die_cfg_7976(0); + } else if (conn_hw_env[0].adie_id == 0x7975) { + printk(RED("%s: Error(%d): No support!!!"), __func__, __LINE__); + } else { + printk(RED("%s: Error(%d): Unknown Adie type!!!"), __func__, __LINE__); + return -1; + } + } + } else { + for (i = 0; i < AIDE_NUM_MAX; i++) { + if (conn_hw_env[i].valid) { + if (conn_hw_env[i].adie_id == 0x7976) { + ret = _connsys_a_die_cfg_7976(i); + } else if (conn_hw_env[i].adie_id == 0x7975) { + ret = _connsys_a_die_cfg_7975(i); + } else { + printk(RED("%s: Error(%d): Unknown Adie type!!!"), __func__, __LINE__); + return -1; + } + } + } + } + + return ret; +} + +int _connsys_afe_wbg_cal_7976(unsigned char wbg_idx, unsigned char rfspi_idx) +{ + int check; + unsigned long afe_ctl_addr = 0; + unsigned char subsystem = 0; + + if ((wbg_idx == 0) && (rfspi_idx == 0)) { + afe_ctl_addr = REG_CONN_AFE_CTL_ADDR; + subsystem = SYS_SPI_TOP; + } else if ((wbg_idx == 1) && (rfspi_idx == 0)) { + afe_ctl_addr = REG_CONN_AFE_CTL_2ND_ADDR; + subsystem = SYS_SPI_TOP; + } else if ((wbg_idx == 1) && (rfspi_idx == 1)) { + afe_ctl_addr = REG_CONN_AFE_CTL_2ND_ADDR; + subsystem = SYS_SPI_2ND_ADIE_TOP; + } else { + pr_err("No support for this combination (wbg_idx = %d, rfspi_idx = %d)\n", + wbg_idx, rfspi_idx); + return -1; + } + + /* Get semaphore once */ + if (consys_sema_acquire_timeout(CONN_SEMA_RFSPI_INDEX, CONN_SEMA_TIMEOUT) == CONN_SEMA_GET_FAIL) { + pr_err("Require semaphore fail\n"); + return -1; + } + + /* set WF_PAD to HighZ */ + check = consys_spi_write_nolock(subsystem, ATOP_RG_ENCAL_WBTAC_IF_SW, 0x88888005); + + /* AFE WBG CAL SEQ1 (RC calibration) */ + CONSYS_SET_BIT(afe_ctl_addr + RG_DIG_EN_01, 0x1); + udelay(60); + CONSYS_CLR_BIT(afe_ctl_addr + RG_DIG_EN_01, 0x1); + + /* AFE WBG CAL SEQ2 (TX calibration) */ + CONSYS_SET_BIT(afe_ctl_addr + RG_DIG_EN_03, (0x1 << 21)); + udelay(30); + CONSYS_SET_BIT(afe_ctl_addr + RG_DIG_EN_03, (0x1 << 20)); + udelay(60); + CONSYS_SET_BIT(afe_ctl_addr + RG_DIG_EN_01, 0x3E0000); + udelay(800); + CONSYS_CLR_BIT(afe_ctl_addr + RG_DIG_EN_01, 0x3E0000); + CONSYS_CLR_BIT(afe_ctl_addr + RG_DIG_EN_03, 0x300000); + + /* disable WF_PAD to HighZ */ + check = consys_spi_write_nolock(subsystem, ATOP_RG_ENCAL_WBTAC_IF_SW, 0x00000005); + + /* Release semaphore */ + consys_sema_release(CONN_SEMA_RFSPI_INDEX); + + return 0; +} + +int _connsys_afe_wbg_cal_7975(unsigned char wbg_idx, unsigned char rfspi_idx) +{ + int check; + unsigned long afe_ctl_addr = 0; + unsigned char subsystem = 0; + + if ((wbg_idx == 0) && (rfspi_idx == 0)) { + afe_ctl_addr = REG_CONN_AFE_CTL_ADDR; + subsystem = SYS_SPI_TOP; + } else if ((wbg_idx == 1) && (rfspi_idx == 0)) { + afe_ctl_addr = REG_CONN_AFE_CTL_2ND_ADDR; + subsystem = SYS_SPI_TOP; + } else if ((wbg_idx == 1) && (rfspi_idx == 1)) { + afe_ctl_addr = REG_CONN_AFE_CTL_2ND_ADDR; + subsystem = SYS_SPI_2ND_ADIE_TOP; + } else { + pr_err("No support for this combination (wbg_idx = %d, rfspi_idx = %d)\n", + wbg_idx, rfspi_idx); + return -1; + } + + /* Get semaphore once */ + if (consys_sema_acquire_timeout(CONN_SEMA_RFSPI_INDEX, CONN_SEMA_TIMEOUT) == CONN_SEMA_GET_FAIL) { + pr_err("Require semaphore fail\n"); + return -1; + } + + /* set WF_PAD to HighZ */ + check = consys_spi_write_nolock(subsystem, ATOP_RG_ENCAL_WBTAC_IF_SW, 0x80000000); + + /* AFE WBG CAL SEQ1 (RC calibration) */ + CONSYS_SET_BIT(afe_ctl_addr + RG_DIG_EN_01, 0x1); + udelay(60); + CONSYS_CLR_BIT(afe_ctl_addr + RG_DIG_EN_01, 0x1); + + /* AFE WBG CAL SEQ2 (TX calibration) */ + CONSYS_SET_BIT(afe_ctl_addr + RG_DIG_EN_03, (0x1 << 21)); + udelay(30); + CONSYS_SET_BIT(afe_ctl_addr + RG_DIG_EN_03, (0x1 << 20)); + udelay(60); + CONSYS_SET_BIT(afe_ctl_addr + RG_DIG_EN_01, 0x3E0000); + udelay(800); + CONSYS_CLR_BIT(afe_ctl_addr + RG_DIG_EN_01, 0x3E0000); + CONSYS_CLR_BIT(afe_ctl_addr + RG_DIG_EN_03, 0x300000); + + /* disable WF_PAD to HighZ */ + check = consys_spi_write_nolock(subsystem, ATOP_RG_ENCAL_WBTAC_IF_SW, 0x00000005); + + /* Release semaphore */ + consys_sema_release(CONN_SEMA_RFSPI_INDEX); + + return 0; +} + +int connsys_afe_wbg_cal(void) +{ + int ret; + unsigned int i; + + if (adie_cfg_type == ADIE_TYPE_TWO) { + for (i = 0; i < AIDE_NUM_MAX; i++) { + if (conn_hw_env[i].valid) { + if (conn_hw_env[i].adie_id == 0x7976) { + ret = _connsys_afe_wbg_cal_7976(i, i); + } else if (conn_hw_env[i].adie_id == 0x7975) { + ret = _connsys_afe_wbg_cal_7975(i, i); + } else { + printk(RED("%s: Error(%d): Unknown Adie type!!!"), __func__, __LINE__); + return -1; + } + } + } + } else { + if (one_adie_dbdc) { + /* use adie_idx = 0 */ + if (conn_hw_env[0].valid) { + if (conn_hw_env[0].adie_id == 0x7976) { + ret = _connsys_afe_wbg_cal_7976(0, 0); + ret = _connsys_afe_wbg_cal_7976(1, 0); + } else if (conn_hw_env[0].adie_id == 0x7975) { + printk(RED("%s: Error(%d): No support!!!"), __func__, __LINE__); + } else { + printk(RED("%s: Error(%d): Unknown Adie type!!!"), __func__, __LINE__); + return -1; + } + } + } else { + /* use adie_idx = 1 */ + if (conn_hw_env[1].valid) { + if (conn_hw_env[1].adie_id == 0x7976) { + ret = _connsys_afe_wbg_cal_7976(1, 1); + } else if (conn_hw_env[1].adie_id == 0x7975) { + ret = _connsys_afe_wbg_cal_7975(1, 1); + } else { + printk(RED("%s: Error(%d): Unknown Adie type!!!"), __func__, __LINE__); + return -1; + } + } + } + } + + return ret; +} + +int _connsys_subsys_pll_initial(unsigned char wbg_idx) +{ + unsigned long afe_ctl_addr = 0; + + if (wbg_idx == 0) { + afe_ctl_addr = REG_CONN_AFE_CTL_ADDR; + } else if (wbg_idx == 1) { + afe_ctl_addr = REG_CONN_AFE_CTL_2ND_ADDR; + } else { + pr_err("Not support for this wbg index (wbg_idx=%d)\n", wbg_idx); + return -1; + } + + /* SWITCH(xtal_freq) + CASE SYS_XTAL_40000K + */ + /* set BPLL stable time = 30us (value = 30 * 1000 *1.01 / 25ns) */ + CONSYS_REG_WRITE_RANGE(afe_ctl_addr + RG_PLL_STB_TIME, (0x4BC << 16), 30, 16); + /* set WPLL stable time = 50us (value = 50 * 1000 *1.01 / 25ns) */ + CONSYS_REG_WRITE_RANGE(afe_ctl_addr + RG_PLL_STB_TIME, 0x7E4, 14, 0); + /* BT pll_en will turn on BPLL only (may change in different XTAL option) */ + CONSYS_REG_WRITE_RANGE(afe_ctl_addr + RG_DIG_EN_02, (0x1 << 6), 7, 6); + /* WF pll_en will turn on WPLL only (may change in different XTAL option) */ + CONSYS_REG_WRITE_RANGE(afe_ctl_addr + RG_DIG_EN_02, 0x2, 1, 0); + /* MCU pll_en will turn on BPLL (may change in different XTAL option) */ + CONSYS_REG_WRITE_RANGE(afe_ctl_addr + RG_DIG_EN_02, (0x1 << 2), 3, 2); + /* MCU pll_en will turn on BPLL + WPLL (may change in different XTAL option) */ + CONSYS_REG_WRITE_RANGE(afe_ctl_addr + RG_DIG_EN_02, (0x2 << 16), 17, 16); + /* CONN_INFRA CLKGEN WPLL AND BPLL REQUEST */ + CONSYS_REG_WRITE_RANGE(afe_ctl_addr + RG_DIG_TOP_01, (0x9 << 15), 18, 15); + + return 0; +} + +int connsys_subsys_pll_initial(void) +{ + int ret; + + ret = _connsys_subsys_pll_initial(0); + ret = _connsys_subsys_pll_initial(1); + + return ret; +} + +int connsys_osc_legacy_mode(void) +{ + /* disable conn_top rc osc_ctrl_top */ + CONSYS_CLR_BIT(REG_CONN_INFRA_CFG_ADDR + CONN_INFRA_CFG_RC_CTL_0, 0x80); + CONSYS_REG_WRITE_RANGE(REG_CONN_INFRA_CFG_ADDR + OSC_CTL_0, 0x80706, 23, 0); + + return 0; +} + +int connsys_top_pwr_ctrl(void) +{ + /* prevent subsys from power on/of in a short time interval */ + CONSYS_CLR_BIT_WITH_KEY(REG_CONN_INFRA_RGU_ADDR + BGFYS_ON_TOP_PWR_CTL, 0x40, 0x42540000); + CONSYS_CLR_BIT_WITH_KEY(REG_CONN_INFRA_RGU_ADDR + WFSYS_ON_TOP_PWR_CTL, 0x40, 0x57460000); + + return 0; +} + +int connsys_conn_infra_bus_timeout(void) +{ + /* set conn_infra_off bus timeout */ + CONSYS_REG_WRITE_RANGE(REG_CONN_INFRA_BUS_CR_ADDR + CONN_INFRA_BUS_OFF_TIMEOUT_CTRL, (0x2 << 7), 14, 7); + /* enable conn_infra off bus timeout feature */ + CONSYS_REG_WRITE_RANGE(REG_CONN_INFRA_BUS_CR_ADDR + CONN_INFRA_BUS_OFF_TIMEOUT_CTRL, 0xF, 3, 0); + + /* set conn_infra_on bus timeout */ + CONSYS_REG_WRITE_RANGE(REG_CONN_INFRA_BUS_CR_ADDR + CONN_INFRA_BUS_ON_TIMEOUT_CTRL, (0xC << 7), 14, 7); + /* enable conn_infra_on bus timeout feature */ + CONSYS_REG_WRITE_RANGE(REG_CONN_INFRA_BUS_CR_ADDR + CONN_INFRA_BUS_ON_TIMEOUT_CTRL, 0xF, 3, 0); + + return 0; +} + +int connsys_clkgen_wpll_hw_ctrl(void) +{ + /* set hclk_div_1 with wpll div sel */ + CONSYS_REG_WRITE_MASK(REG_CONN_INFRA_CLKGEN_ON_TOP_ADDR + CKGEN_BUS_WPLL_DIV_1, 0x4, 0xFC); + + /* set hclk_div_2 with wpll div sel */ + CONSYS_REG_WRITE_MASK(REG_CONN_INFRA_CLKGEN_ON_TOP_ADDR + CKGEN_BUS_WPLL_DIV_2, 0x4, 0xFC); + +#ifndef CONFIG_FPGA_EARLY_PORTING + /* enable conn_infra bus wpll div_1 */ + CONSYS_SET_BIT(REG_CONN_INFRA_CLKGEN_ON_TOP_ADDR + CKGEN_BUS_WPLL_DIV_1, 0x1); + + /* enable conn_infra bus wpll div_2 */ + CONSYS_SET_BIT(REG_CONN_INFRA_CLKGEN_ON_TOP_ADDR + CKGEN_BUS_WPLL_DIV_2, 0x1); +#endif + +#ifndef CONFIG_FPGA_EARLY_PORTING + /* set rfspi wpll div sel + enable rfspis wpll div + */ + CONSYS_REG_WRITE_MASK(REG_CONN_INFRA_CLKGEN_ON_TOP_ADDR + CKGEN_RFSPI_WPLL_DIV, 0x21, 0xFD); +#else + /* set rfspi wpll div sel */ + CONSYS_REG_WRITE_MASK(REG_CONN_INFRA_CLKGEN_ON_TOP_ADDR + CKGEN_RFSPI_WPLL_DIV, 0x20, 0xFC); +#endif + + /* disable conn_infra bus clock sw control ==> conn_infra bus clock hw control */ + CONSYS_CLR_BIT(REG_CONN_INFRA_CLKGEN_ON_TOP_ADDR + CKGEN_BUS, 0x800000); + + /* Conn_infra HW_CONTROL => conn_infra enter dsleep mode */ + CONSYS_SET_BIT(REG_CONN_INFRA_CFG_ADDR + CONN_INFRA_CFG_PWRCTRL0, 0x1); + + return 0; +} + +int consys_conninfra_top_wakeup(void) +{ + /* wake up conn_infra */ + CONSYS_SET_BIT(REG_CONN_HOST_CSR_TOP_ADDR + CONN_INFRA_WAKEPU_TOP, 0x1); + + /* Wait 900us (apply this for CONNSYS XO clock ready) */ + udelay(900); + + /* Check CONNSYS version ID + * (polling "10 times" for specific project code and each polling interval is "1ms") + */ + if (consys_polling_chipid() != 0) { + pr_err("Polling chip id fail\n"); + return -1; + } + + return 0; +} + +int consys_conninfra_top_sleep(void) +{ + /* release conn_infra force on */ + CONSYS_CLR_BIT(REG_CONN_HOST_CSR_TOP_ADDR + CONN_INFRA_WAKEPU_TOP, 0x1); + + return 0; +} + +int _consys_adie_top_ck_en_on_off_ctrl(unsigned char rfspi_idx, enum consys_drv_type type, unsigned char on) +{ + int check = 0; + unsigned long slp_ctl_addr = 0; + + if (rfspi_idx == 1) + slp_ctl_addr = REG_INST2_CONN_WT_SLP_CTL_REG_ADDR; + else + slp_ctl_addr = REG_CONN_WT_SLP_CTL_REG_ADDR; + + if (type == CONNDRV_TYPE_CONNINFRA) { + if (on) + CONSYS_SET_BIT(slp_ctl_addr + WB_SLP_TOP_CK_0, 0x1); + else + CONSYS_CLR_BIT(slp_ctl_addr + WB_SLP_TOP_CK_0, 0x1); + CONSYS_REG_BIT_POLLING(slp_ctl_addr + WB_SLP_TOP_CK_0, 1, 0, 100, 500, check); + if (check == -1) + pr_err("[type=%d][on=%d] op fail\n", type, on); + } else if (type == CONNDRV_TYPE_WIFI) { + if (on) + CONSYS_SET_BIT(slp_ctl_addr + WB_SLP_TOP_CK_1, 0x1); + else + CONSYS_CLR_BIT(slp_ctl_addr + WB_SLP_TOP_CK_1, 0x1); + CONSYS_REG_BIT_POLLING(slp_ctl_addr + WB_SLP_TOP_CK_1, 1, 0, 100, 500, check); + if (check == -1) + pr_err("[type=%d][on=%d] op fail\n", type, on); + } else { + pr_err("Not support for this consys drv type = %d\n", type); + return -1; + } + + return 0; +} + +int consys_adie_top_ck_en_on_off_ctrl(enum consys_drv_type type, unsigned char on) +{ + int ret; + + if (consys_sema_acquire_timeout(CONN_SEMA_CONN_INFRA_COMMON_SYSRAM_INDEX, CONN_SEMA_TIMEOUT) == CONN_SEMA_GET_FAIL) { + pr_err("[type=%d] acquire semaphore (%d) timeout\n", + type, CONN_SEMA_CONN_INFRA_COMMON_SYSRAM_INDEX); + return -1; + } + + if (adie_cfg_type == ADIE_TYPE_TWO) { + ret = _consys_adie_top_ck_en_on_off_ctrl(0, type, on); + ret = _consys_adie_top_ck_en_on_off_ctrl(1, type, on); + } else { + if (one_adie_dbdc) { + ret = _consys_adie_top_ck_en_on_off_ctrl(0, type, on); + } else { + ret = _consys_adie_top_ck_en_on_off_ctrl(1, type, on); + } + } + + consys_sema_release(CONN_SEMA_CONN_INFRA_COMMON_SYSRAM_INDEX); + + return ret; +} + +bool _is_wmcpu_run_allow(void) +{ + if (EEPROM_content_valid) { + if (one_adie_dbdc) { + if ((band0_pa_type == iPAiLNA) || + (band0_pa_type == iPAeLNA) || + (band1_pa_type == iPAiLNA) || + (band1_pa_type == iPAeLNA)) { + if ((conn_hw_env[0].valid && + (conn_hw_env[0].adie_id == 0x7976) && + (conn_hw_env[0].adie_hw_version == 0x8A20))) { + printk(RED("Wrong EEPROM PA type for this sku!")); + return false; + } + } + } else { + if (adie_cfg_type == ADIE_TYPE_TWO) { + if ((band0_pa_type == iPAiLNA) || + (band0_pa_type == iPAeLNA) || + (band1_pa_type == iPAiLNA) || + (band1_pa_type == iPAeLNA)) { + if ((conn_hw_env[0].valid && (conn_hw_env[0].adie_id == 0x7976)) && + (conn_hw_env[1].valid && (conn_hw_env[1].adie_id == 0x7976))) { + printk(RED("Wrong EEPROM PA type for this sku!")); + return false; + } + } + } + } + } + + return true; +} + +int consys_conninfra_wf_wakeup(void) +{ + /* wake up conn_infra */ + CONSYS_SET_BIT(REG_CONN_HOST_CSR_TOP_ADDR + CONN_INFRA_WAKEPU_WF, 0x1); + + /* Wait 900us (apply this for CONNSYS XO clock ready) */ + udelay(900); + + /* Check CONNSYS version ID + * (polling "10 times" for specific project code and each polling interval is "1ms") + */ + if (consys_polling_chipid() != 0) { + pr_err("Polling chip id fail\n"); + return -1; + } + + return 0; +} + +int consys_conninfra_wf_sleep(void) +{ + CONSYS_CLR_BIT(REG_CONN_HOST_CSR_TOP_ADDR + CONN_INFRA_WAKEPU_WF, 0x1); + + return 0; +} + +int consys_conn_wmcpu_sw_reset(bool bassert) +{ + if (bassert) { + CONSYS_CLR_BIT(REG_CONN_INFRA_RGU_ADDR + WFSYS_CPU_SW_RST_B, 0x1); + } else { + if (_is_wmcpu_run_allow()) + CONSYS_SET_BIT(REG_CONN_INFRA_RGU_ADDR + WFSYS_CPU_SW_RST_B, 0x1); + } + + return 0; +} + +int consys_wf_bus_slp_prot_ctrl(bool enable) +{ + /* Turn on/off "conn_infra to wfsys"/wfsys to conn_infra/wfdma2conn" bus sleep protect */ + + if (enable) + CONSYS_SET_BIT(REG_CONN_INFRA_CFG_ADDR + CONN_INFRA_WF_SLP_CTRL, 0x1); + else + CONSYS_CLR_BIT(REG_CONN_INFRA_CFG_ADDR + CONN_INFRA_WF_SLP_CTRL, 0x1); + + return 0; +} + +int consys_wfsys_top_on_ctrl(bool enable) +{ + int check = 0; + + if (enable) { + /* turn on wfsys_top_on */ + CONSYS_SET_BIT(REG_CONN_INFRA_RGU_ADDR + WFSYS_ON_TOP_PWR_CTL, 0x57460080); + + /* polling wfsys_rgu_off_hreset_rst_b */ + CONSYS_REG_BIT_POLLING(REG_CONN_HOST_CSR_TOP_ADDR + DBG_DUMMY_3, 30, 1, 100, 500, check); + if (check == -1) + pr_err("[%d] polling wfsys_rgu_off_hreset_rst_b fail\n", enable); + } else { + /* turn off wfsys_top_on */ + CONSYS_CLR_BIT_WITH_KEY(REG_CONN_INFRA_RGU_ADDR + WFSYS_ON_TOP_PWR_CTL, 0x80, 0x57460000); + + /* polling wfsys_rgu_off_hreset_rst_b */ + CONSYS_REG_BIT_POLLING(REG_CONN_HOST_CSR_TOP_ADDR + DBG_DUMMY_3, 30, 0, 100, 500, check); + if (check == -1) + pr_err("[%d] polling wfsys_rgu_off_hreset_rst_b fail\n", enable); + } + + return 0; +} + +int consys_wfsys_bus_slp_prot_check(bool enable) +{ + int check = 0; + + if (enable) { + /* check "conn_infra to wfsys"/wfsys to conn_infra" bus sleep protect turn off */ + CONSYS_REG_BIT_POLLING(REG_CONN_INFRA_CFG_ADDR + CONN_INFRA_WF_SLP_STATUS, 29, 0, 100, 500, check); + if (check == -1) + pr_err("[bit %d] check conn_infra to wfsys or wfsys to conn_infra bus sleep protect turn off fail\n", 29); + + CONSYS_REG_BIT_POLLING(REG_CONN_INFRA_CFG_ADDR + CONN_INFRA_WF_SLP_STATUS, 31, 0, 100, 500, check); + if (check == -1) + pr_err("[bit %d] check conn_infra to wfsys or wfsys to conn_infra bus sleep protect turn off fail\n", 31); + + /* check WFDMA2CONN AXI TX bus sleep protect turn off */ + CONSYS_REG_BIT_POLLING(REG_WF_TOP_SLPPROT_ON_ADDR + WF_TOP_SLPPROT_ON_STATUS_READ, 23, 0, 100, 500, check); + if (check == -1) + pr_err("check WFDMA2CONN AXI TX bus sleep protect turn off fail\n"); + + /* check WFDMA2CONN AXI RX bus sleep protect turn off */ + CONSYS_REG_BIT_POLLING(REG_WF_TOP_SLPPROT_ON_ADDR + WF_TOP_SLPPROT_ON_STATUS_READ, 21, 0, 100, 500, check); + if (check == -1) + pr_err("check WFDMA2CONN AXI RX bus sleep protect turn off fail\n"); + + /* check WFSYS version ID */ + CONSYS_REG_POLLING_LARGER_OR_EQUAL(REG_WF_TOP_CFG_ADDR + WF_TOP_CFG_IP_VERSION, 0xFFFFFFFF, 0, 0x02060000, 10, 500, check); + if (check == -1) + pr_err("check WFSYS version ID fail\n"); + } else { + /* check WFDMA2CONN AXI RX bus sleep protect turn on */ + CONSYS_REG_BIT_POLLING(REG_CONN_INFRA_CFG_ADDR + CONN_INFRA_WF_SLP_STATUS, 25, 1, 100, 500, check); + if (check == -1) + pr_err("check WFDMA2CONN AXI RX bus sleep protect turn on fail\n"); + + /* check "conn_infra to wfsys"/wfsys to conn_infra" bus sleep protect turn on */ + CONSYS_REG_BIT_POLLING(REG_CONN_INFRA_CFG_ADDR + CONN_INFRA_WF_SLP_STATUS, 29, 1, 100, 500, check); + if (check == -1) + pr_err("[bit %d] check conn_infra to wfsys or wfsys to conn_infra bus sleep protect turn on fail\n", 29); + + CONSYS_REG_BIT_POLLING(REG_CONN_INFRA_CFG_ADDR + CONN_INFRA_WF_SLP_STATUS, 31, 1, 100, 500, check); + if (check == -1) + pr_err("[bit %d] check conn_infra to wfsys or wfsys to conn_infra bus sleep protect turn on fail\n", 31); + } + + return 0; +} + +int consys_wfsys_bus_timeout_ctrl(void) +{ + /* set wfsys bus timeout value (ahb apb timeout) */ + CONSYS_REG_WRITE_MASK(REG_WF_MCU_CONFIG_LS_ADDR + BUSHANGCR, 0x1, 0xFF); + + /* enable wfsys bus timeout (ahb apb timeout) */ + CONSYS_SET_BIT(REG_WF_MCU_CONFIG_LS_ADDR + BUSHANGCR, 0x90000000); + + /* set conn2wf remapping window to wf debug_ctrl_ao CR */ + CONSYS_REG_WRITE(REG_WF_MCU_BUS_CR_ADDR + AP2WF_REMAP_1, 0x810F0000); + + /* set wfsys bus timeout value (debug ctrl ao) */ + CONSYS_REG_WRITE_MASK(REG_WF_MCUSYS_INFRA_BUS_FULL_U_DEBUG_CTRL_AO_ADDR + WF_MCUSYS_INFRA_BUS_FULL_U_DEBUG_CTRL_AO_WFMCU_PWA_CTRL0, + 0x03AA0000, 0xFFFF0000); + + /* enable wfsys bus timeout (debug ctrl ao) */ + CONSYS_SET_BIT(REG_WF_MCUSYS_INFRA_BUS_FULL_U_DEBUG_CTRL_AO_ADDR + WF_MCUSYS_INFRA_BUS_FULL_U_DEBUG_CTRL_AO_WFMCU_PWA_CTRL0, 0xC); + + return 0; +} + +int consys_wmcpu_idle_loop_check(void) +{ + int check = 0; + + /* check CONNSYS power-on completion */ + CONSYS_REG_POLLING_EQUAL(REG_WF_TOP_CFG_ON_ADDR + ROMCODE_INDEX, 0xFFFFFFFF, 0, 0x1D1E, 5000, 1000, check); + if (check == -1) + pr_err("check CONNSYS power-on completion fail\n"); + + return 0; +} + +int _connsys_a_die_chip_id_confirm(unsigned char adie_idx) +{ + int check; + unsigned int adie_chip_id = 0x0; + unsigned char subsystem = 0; + + if (adie_idx == 1) + subsystem = SYS_SPI_2ND_ADIE_TOP; + else + subsystem = SYS_SPI_TOP; + + /* release D Die to A Die Digital reset_b if need */ + if (adie_idx == 1) { + if (CONSYS_REG_READ_BIT(REG_CONN_INFRA_CFG_ADDR + ADIE_CTL, 0x4) == 0) + CONSYS_SET_BIT(REG_CONN_INFRA_CFG_ADDR + ADIE_CTL, 0x4); + } else { + if (CONSYS_REG_READ_BIT(REG_CONN_INFRA_CFG_ADDR + ADIE_CTL, 0x1) == 0) + CONSYS_SET_BIT(REG_CONN_INFRA_CFG_ADDR + ADIE_CTL, 0x1); + } + + /* Get semaphore once */ + if (consys_sema_acquire_timeout(CONN_SEMA_RFSPI_INDEX, CONN_SEMA_TIMEOUT) == CONN_SEMA_GET_FAIL) { + pr_err("Require semaphore fail\n"); + return -1; + } + + /* read a-die ID */ + check = consys_spi_read_nolock(subsystem, ATOP_CHIP_ID, &adie_chip_id); + if (check) { + /* Release semaphore */ + consys_sema_release(CONN_SEMA_RFSPI_INDEX); + pr_err("adie_idx[%d]: Get ATOP_CHIP_ID fail, check = %d\n", adie_idx, check); + return -1; + } + + /* Release semaphore */ + consys_sema_release(CONN_SEMA_RFSPI_INDEX); + + if (adie_idx < AIDE_NUM_MAX && (adie_chip_id & 0xFFFF0000) != 0) { + conn_hw_env[adie_idx].valid = true; + conn_hw_env[adie_idx].adie_hw_version = (adie_chip_id & 0xFFFF); + conn_hw_env[adie_idx].adie_id = ((adie_chip_id & 0xFFFF0000) >> 16); + conn_hw_env[adie_idx].is_rc_mode = 0; + + pr_info("adie_idx[%d]: A-die CHIP ID = 0x%x, HW Version = 0x%x\n", + adie_idx, conn_hw_env[adie_idx].adie_id, conn_hw_env[adie_idx].adie_hw_version); + } + + return 0; +} + +int consys_plt_adie_type_check(void) +{ + int ret = 0; + + memset(&conn_hw_env, 0, sizeof(conn_hw_env)); + + if (adie_cfg_type == ADIE_TYPE_TWO) { + ret = _connsys_a_die_chip_id_confirm(0); + ret = _connsys_a_die_chip_id_confirm(1); + if (!(conn_hw_env[0].valid && conn_hw_env[1].valid)){ + adie_cfg_type = ADIE_TYPE_ONE; + printk(RED("%s: Change adie type to one adie"), __func__); + } + } else { + if (one_adie_dbdc) + ret = _connsys_a_die_chip_id_confirm(0); + else + ret = _connsys_a_die_chip_id_confirm(1); + } + + return ret; +} + +int consys_plt_adie_type_cfg(void) +{ + /* + a. Check if One Adie DBDC mode from GPIO8 (0x11D1_021C[2]) + b. To read from falsh EEPROM to decide stream number for several skus + c. + 1. If Strap_GPIO8 == 0 && Read EEPROM Stream Num. + case 1: 2G: 0T0R => One_Adie_SB_AX7800 + then + case 1: A-die is 7976 => + TOP_MISC_CR (0x11D1_021C[31:28]) = 0xA && 0x18050000 = 0xA + case 2: A-die is 7975 => + TOP_MISC_CR (0x11D1_021C[31:28]) = 0x8 && 0x18050000 = 0x8 + 2. If Strap_GPIO8 == 0 && Read EEPROM Stream Num. + case 1: 2G: 2T4R => Two_Adie_SB_AX5400 + case 2: 2G: 4T4R => Two_Adie_SB_AX6000 + then + case 1: A-die is 7976 => + TOP_MISC_CR (0x11D1_021C[31:28]) = 0xF && 0x18050000 = 0xF + case 2: A-die is 7975 => + TOP_MISC_CR (0x11D1_021C[31:28]) = 0xD && 0x18050000 = 0xD + 3. If Strap_GPIO8 == 1 && Read EEPROM Stream Num. + case 1: 2G: 2T2R => One_Adie_DB + then TOP_MISC_CR (0x11D1_021C[31:28]) = 0x7 && 0x18050000 = 0x7 + */ + + if (one_adie_dbdc) { + if (adie_cfg_type == ADIE_TYPE_ONE) { + if ((conn_hw_env[0].valid && (conn_hw_env[0].adie_id == 0x7976))) { + CONSYS_REG_WRITE_MASK(REG_TOP_MISC_ADDR + TOP_MISC_RSRV_ALL1_3, 0x70000000, 0xF0000000); + CONSYS_REG_WRITE(REG_CONN_INFRA_SYSRAM_ADDR + SYSRAM_BASE_ADDR, 0x7); + } else { + printk(RED("%s: Error(%d): Unknown sku type!!!"), __func__, __LINE__); + } + } + } else { + if (adie_cfg_type == ADIE_TYPE_TWO) { + if ((conn_hw_env[0].valid && (conn_hw_env[0].adie_id == 0x7976)) && + (conn_hw_env[1].valid && (conn_hw_env[1].adie_id == 0x7976))) { + CONSYS_REG_WRITE_MASK(REG_TOP_MISC_ADDR + TOP_MISC_RSRV_ALL1_3, 0xF0000000, 0xF0000000); + CONSYS_REG_WRITE(REG_CONN_INFRA_SYSRAM_ADDR + SYSRAM_BASE_ADDR, 0xF); + } else if ((conn_hw_env[0].valid && (conn_hw_env[0].adie_id == 0x7975)) && + (conn_hw_env[1].valid && (conn_hw_env[1].adie_id == 0x7975))) { + CONSYS_REG_WRITE_MASK(REG_TOP_MISC_ADDR + TOP_MISC_RSRV_ALL1_3, 0xD0000000, 0xF0000000); + CONSYS_REG_WRITE(REG_CONN_INFRA_SYSRAM_ADDR + SYSRAM_BASE_ADDR, 0xD); + } else { + printk(RED("%s: Error(%d): Unknown sku type!!!"), __func__, __LINE__); + } + } else { + if ((conn_hw_env[0].valid && (conn_hw_env[0].adie_id == 0x7976)) || + (conn_hw_env[1].valid && (conn_hw_env[1].adie_id == 0x7976))) { + CONSYS_REG_WRITE_MASK(REG_TOP_MISC_ADDR + TOP_MISC_RSRV_ALL1_3, 0xA0000000, 0xF0000000); + CONSYS_REG_WRITE(REG_CONN_INFRA_SYSRAM_ADDR + SYSRAM_BASE_ADDR, 0xA); + } else if ((conn_hw_env[0].valid && (conn_hw_env[0].adie_id == 0x7975)) || + (conn_hw_env[1].valid && (conn_hw_env[1].adie_id == 0x7975))) { + CONSYS_REG_WRITE_MASK(REG_TOP_MISC_ADDR + TOP_MISC_RSRV_ALL1_3, 0x80000000, 0xF0000000); + CONSYS_REG_WRITE(REG_CONN_INFRA_SYSRAM_ADDR + SYSRAM_BASE_ADDR, 0x8); + } else { + printk(RED("%s: Error(%d): Unknown sku type!!!"), __func__, __LINE__); + } + } + } + + if (_consys_check_adie_cfg() == 0) + _consys_check_sku_cfg(); + + return 0; +} + +int consys_wpll_ctrl(bool enable) +{ + if (enable) { + /* turn back wpll setting in conn_afe_ctl by setting wpll initial control to 2'b10 */ + CONSYS_REG_WRITE_MASK(REG_CONN_AFE_CTL_ADDR + RG_DIG_EN_02, 0x20002, 0x30003); + } else { + /* Don't need below code check anymore due to new design */ +#if 0 + int check = 0; + /* trun off wpll enable in conn_afe_ctl by setting wpll initial control to 2'b00 */ + CONSYS_REG_WRITE_MASK(REG_CONN_AFE_CTL_ADDR + RG_DIG_EN_02, 0x0, 0x30003); + + /* polling conn_infra bus to non-wpll case */ + CONSYS_REG_POLLING_EQUAL(REG_CONN_INFRA_CLKGEN_ON_TOP_ADDR + CKGEN_BUS, 0x7800, 11, 0x0, 5000, 1000, check); + if (check == -1) + pr_err("polling conn_infra bus to non-wpll case fail\n"); +#endif + } + + return 0; +} + +int consys_conninfra_wf_req_clr(void) +{ + /* clear wf_emi_req */ + CONSYS_SET_BIT(REG_CONN_INFRA_CFG_ADDR + EMI_CTL_WF, 0x1); + CONSYS_CLR_BIT(REG_CONN_INFRA_CFG_ADDR + EMI_CTL_WF, 0x1); + + /* clear wf_infra_req */ + CONSYS_SET_BIT(REG_CONN_INFRA_CFG_ADDR + EMI_CTL_WF, 0x20); + CONSYS_CLR_BIT(REG_CONN_INFRA_CFG_ADDR + EMI_CTL_WF, 0x20); + + return 0; +} + diff --git a/package/mtk/drivers/conninfra/src/platform/pmic_mng.c b/package/mtk/drivers/conninfra/src/platform/pmic_mng.c new file mode 100644 index 0000000000..0e8d034ec6 --- /dev/null +++ b/package/mtk/drivers/conninfra/src/platform/pmic_mng.c @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2016 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 http://www.gnu.org/licenses/gpl-2.0.html for more details. + */ +/*! \file +* \brief Declaration of library functions +* +* Any definitions in this file will be shared among GLUE Layer and internal Driver Stack. +*/ + +#include "consys_hw.h" +#include "pmic_mng.h" +#include "osal.h" +/******************************************************************************* +* C O M P I L E R F L A G S +******************************************************************************** +*/ + +/******************************************************************************* +* M A C R O S +******************************************************************************** +*/ + + +/******************************************************************************* +* E X T E R N A L R E F E R E N C E S +******************************************************************************** +*/ + +/******************************************************************************* +* C O N S T A N T S +******************************************************************************** +*/ + +/******************************************************************************* +* D A T A T Y P E S +******************************************************************************** +*/ + +/******************************************************************************* +* F U N C T I O N D E C L A R A T I O N S +******************************************************************************** +*/ + +/******************************************************************************* +* P U B L I C D A T A +******************************************************************************** +*/ + +const struct consys_platform_pmic_ops* consys_platform_pmic_ops = NULL; + +/******************************************************************************* +* P R I V A T E D A T A +******************************************************************************** +*/ + +/******************************************************************************* +* F U N C T I O N S +******************************************************************************** +*/ + +int pmic_mng_init( + struct platform_device *pdev, + struct conninfra_dev_cb* dev_cb, + const struct conninfra_plat_data* plat_data) +{ + if (consys_platform_pmic_ops == NULL) { + consys_platform_pmic_ops = (const struct consys_platform_pmic_ops*)plat_data->platform_pmic_ops; + } + + if (consys_platform_pmic_ops && consys_platform_pmic_ops->consys_pmic_get_from_dts) + consys_platform_pmic_ops->consys_pmic_get_from_dts(pdev, dev_cb); + + return 0; +} + +int pmic_mng_deinit(void) +{ + return 0; +} + +int pmic_mng_common_power_ctrl(unsigned int enable) +{ + int ret = 0; + + if (consys_platform_pmic_ops && + consys_platform_pmic_ops->consys_pmic_common_power_ctrl) + ret = consys_platform_pmic_ops->consys_pmic_common_power_ctrl(enable); + + return ret; +} + +int pmic_mng_wifi_power_ctrl(unsigned int enable) +{ + int ret = 0; + if (consys_platform_pmic_ops && + consys_platform_pmic_ops->consys_pmic_wifi_power_ctrl) + ret = consys_platform_pmic_ops->consys_pmic_wifi_power_ctrl(enable); + return ret; + +} + +int pmic_mng_bt_power_ctrl(unsigned int enable) +{ + int ret = 0; + if (consys_platform_pmic_ops && + consys_platform_pmic_ops->consys_pmic_bt_power_ctrl) + ret = consys_platform_pmic_ops->consys_pmic_bt_power_ctrl(enable); + return ret; +} + +int pmic_mng_gps_power_ctrl(unsigned int enable) +{ + int ret = 0; + if (consys_platform_pmic_ops && + consys_platform_pmic_ops->consys_pmic_gps_power_ctrl) + ret = consys_platform_pmic_ops->consys_pmic_gps_power_ctrl(enable); + return ret; +} + +int pmic_mng_fm_power_ctrl(unsigned int enable) +{ + int ret = 0; + if (consys_platform_pmic_ops && + consys_platform_pmic_ops->consys_pmic_fm_power_ctrl) + ret = consys_platform_pmic_ops->consys_pmic_fm_power_ctrl(enable); + return ret; +} + + +int pmic_mng_event_cb(unsigned int id, unsigned int event) +{ + if (consys_platform_pmic_ops && + consys_platform_pmic_ops->consys_pmic_event_notifier) + consys_platform_pmic_ops->consys_pmic_event_notifier(id, event); + return 0; +} diff --git a/package/mtk/drivers/conninfra/src/src/conninfra.c b/package/mtk/drivers/conninfra/src/src/conninfra.c new file mode 100644 index 0000000000..4a48679f38 --- /dev/null +++ b/package/mtk/drivers/conninfra/src/src/conninfra.c @@ -0,0 +1,263 @@ +/* + * Copyright (C) 2016 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 http://www.gnu.org/licenses/gpl-2.0.html for more details. + */ +/*! \file +* \brief Declaration of library functions +* +* Any definitions in this file will be shared among GLUE Layer and internal Driver Stack. +*/ + +#define pr_fmt(fmt) KBUILD_MODNAME "@(%s:%d) " fmt, __func__, __LINE__ + +#include +#include +#include +#include +#include +#include +#include +#include "conninfra.h" +#include "emi_mng.h" +#include "conninfra_core.h" +#include "consys_hw.h" + +/******************************************************************************* +* C O M P I L E R F L A G S +******************************************************************************** +*/ + +/******************************************************************************* +* M A C R O S +******************************************************************************** +*/ + + +/******************************************************************************* +* E X T E R N A L R E F E R E N C E S +******************************************************************************** +*/ + +/******************************************************************************* +* C O N S T A N T S +******************************************************************************** +*/ + +/******************************************************************************* +* D A T A T Y P E S +******************************************************************************** +*/ + +/******************************************************************************* +* F U N C T I O N D E C L A R A T I O N S +******************************************************************************** +*/ + +/******************************************************************************* +* P U B L I C D A T A +******************************************************************************** +*/ + +/******************************************************************************* +* P R I V A T E D A T A +******************************************************************************** +*/ + +#define CONNINFRA_RST_RATE_LIMIT 0 + +#if CONNINFRA_RST_RATE_LIMIT +DEFINE_RATELIMIT_STATE(g_rs, HZ, 1); + +#define DUMP_LOG() if (__ratelimit(&g_rs)) \ + pr_info("rst is ongoing\n") + +#else +#define DUMP_LOG() +#endif + +struct conninfra_rst_data { + enum consys_drv_type drv; + char *reason; +}; + +struct conninfra_rst_data rst_data; + +void conninfra_get_emi_phy_addr(enum connsys_emi_type type, phys_addr_t* base, unsigned int *size) +{ + struct consys_emi_addr_info* addr_info = emi_mng_get_phy_addr(); + + switch (type) { + case CONNSYS_EMI_FW: + if (base) + *base = addr_info->emi_ap_phy_base; + + if (size) + *size = addr_info->fw_emi_size; + break; + + default: + pr_err("Wrong EMI type: %d\n", type); + if (base) + *base = 0x0; + + if (size) + *size = 0; + break; + } +} +EXPORT_SYMBOL(conninfra_get_emi_phy_addr); + +int conninfra_pwr_on(enum consys_drv_type drv_type) +{ + pr_info("[%s] drv=[%d]\n", __func__, drv_type); + + if (conninfra_core_is_rst_locking()) { + DUMP_LOG(); + return CONNINFRA_ERR_RST_ONGOING; + } + + return conninfra_core_power_on(drv_type); +} +EXPORT_SYMBOL(conninfra_pwr_on); + +int conninfra_pwr_off(enum consys_drv_type drv_type) +{ + if (conninfra_core_is_rst_locking()) { + DUMP_LOG(); + return CONNINFRA_ERR_RST_ONGOING; + } + + return conninfra_core_power_off(drv_type); +} +EXPORT_SYMBOL(conninfra_pwr_off); + +int conninfra_is_bus_hang(void) +{ + if (conninfra_core_is_rst_locking()) { + DUMP_LOG(); + return CONNINFRA_ERR_RST_ONGOING; + } + return conninfra_core_is_bus_hang(); +} +EXPORT_SYMBOL(conninfra_is_bus_hang); + +int conninfra_trigger_whole_chip_rst(enum consys_drv_type who, char *reason) +{ + /* use schedule worker to trigger ??? */ + /* so that function can be returned immediately */ + int r; + + r = conninfra_core_lock_rst(); + if (r >= CHIP_RST_START) { + /* reset is ongoing */ + pr_warn("r=[%d] chip rst is ongoing\n", r); + return 1; + } + pr_info("rst lock [%d] [%d] reason=%s\n", r, who, reason); + + conninfra_core_trg_chip_rst(who, reason); + + return 0; +} +EXPORT_SYMBOL(conninfra_trigger_whole_chip_rst); + +int conninfra_sub_drv_ops_register(enum consys_drv_type type, + struct sub_drv_ops_cb *cb) +{ + /* type validation */ + if (type < 0 || type >= CONNDRV_TYPE_MAX) { + pr_err("incorrect drv type [%d]\n", type); + return -EINVAL; + } + pr_info("----\n"); + conninfra_core_subsys_ops_reg(type, cb); + return 0; +} +EXPORT_SYMBOL(conninfra_sub_drv_ops_register); + +int conninfra_sub_drv_ops_unregister(enum consys_drv_type type) +{ + /* type validation */ + if (type < 0 || type >= CONNDRV_TYPE_MAX) { + pr_err("[%s] incorrect drv type [%d]\n", __func__, type); + return -EINVAL; + } + pr_info("----\n"); + conninfra_core_subsys_ops_unreg(type); + return 0; +} +EXPORT_SYMBOL(conninfra_sub_drv_ops_unregister); + +int conninfra_spi_read(enum sys_spi_subsystem subsystem, unsigned int addr, unsigned int *data) +{ + if (conninfra_core_is_rst_locking()) { + DUMP_LOG(); + return CONNINFRA_ERR_RST_ONGOING; + } + if (subsystem >= SYS_SPI_MAX) { + pr_err("wrong subsys %d\n", subsystem); + return -EINVAL; + } + conninfra_core_spi_read(subsystem, addr, data); + return 0; +} +EXPORT_SYMBOL(conninfra_spi_read); + +int conninfra_spi_write(enum sys_spi_subsystem subsystem, unsigned int addr, unsigned int data) +{ + if (conninfra_core_is_rst_locking()) { + DUMP_LOG(); + return CONNINFRA_ERR_RST_ONGOING; + } + + if (subsystem >= SYS_SPI_MAX) { + pr_err("wrong subsys %d\n", subsystem); + return -EINVAL; + } + conninfra_core_spi_write(subsystem, addr, data); + return 0; +} +EXPORT_SYMBOL(conninfra_spi_write); + +int conninfra_adie_top_ck_en_on(enum consys_drv_type type) +{ + if (conninfra_core_is_rst_locking()) { + DUMP_LOG(); + return CONNINFRA_ERR_RST_ONGOING; + } + + return conninfra_core_adie_top_ck_en_on(type); +} +EXPORT_SYMBOL(conninfra_adie_top_ck_en_on); + +int conninfra_adie_top_ck_en_off(enum consys_drv_type type) +{ + if (conninfra_core_is_rst_locking()) { + DUMP_LOG(); + return CONNINFRA_ERR_RST_ONGOING; + } + + return conninfra_core_adie_top_ck_en_off(type); +} +EXPORT_SYMBOL(conninfra_adie_top_ck_en_off); + +int conninfra_spi_clock_switch(enum connsys_spi_speed_type type) +{ + return conninfra_core_spi_clock_switch(type); +} +EXPORT_SYMBOL(conninfra_spi_clock_switch); + +int conninfra_debug_dump(void) +{ + return conninfra_core_debug_dump(); + +} +EXPORT_SYMBOL(conninfra_debug_dump); diff --git a/package/mtk/drivers/conninfra/src/src/conninfra_dev.c b/package/mtk/drivers/conninfra/src/src/conninfra_dev.c new file mode 100644 index 0000000000..0f525dd339 --- /dev/null +++ b/package/mtk/drivers/conninfra/src/src/conninfra_dev.c @@ -0,0 +1,344 @@ +/* + * Copyright (C) 2016 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 http://www.gnu.org/licenses/gpl-2.0.html for more details. + */ +/*! \file +* \brief Declaration of library functions +* +* Any definitions in this file will be shared among GLUE Layer and internal Driver Stack. +*/ + +#include +#include +#include +#include +#include +#include +#include "conninfra.h" +#include "conninfra_core.h" +#include "consys_hw.h" +#include "emi_mng.h" + +/******************************************************************************* +* C O M P I L E R F L A G S +******************************************************************************** +*/ + +/******************************************************************************* +* M A C R O S +******************************************************************************** +*/ + +#define CONNINFRA_DEV_MAJOR 164 +#define CONNINFRA_DEV_NUM 1 +#define CONNINFRA_DRVIER_NAME "conninfra_drv" +#define CONNINFRA_DEVICE_NAME "conninfra_dev" + +#define CONNINFRA_DEV_IOC_MAGIC 0xc2 +#define CONNINFRA_IOCTL_GET_CHIP_ID _IOR(CONNINFRA_DEV_IOC_MAGIC, 0, int) + +#define CONNINFRA_DEV_INIT_TO_MS (2 * 1000) + +/******************************************************************************* +* E X T E R N A L R E F E R E N C E S +******************************************************************************** +*/ + +/******************************************************************************* +* C O N S T A N T S +******************************************************************************** +*/ + +enum conninfra_init_status { + CONNINFRA_INIT_NOT_START, + CONNINFRA_INIT_START, + CONNINFRA_INIT_DONE, +}; +static int g_conninfra_init_status = CONNINFRA_INIT_NOT_START; +static wait_queue_head_t g_conninfra_init_wq; + +/******************************************************************************* +* D A T A T Y P E S +******************************************************************************** +*/ + +/******************************************************************************* +* F U N C T I O N D E C L A R A T I O N S +******************************************************************************** +*/ +static int conninfra_dev_open(struct inode *inode, struct file *file); +static int conninfra_dev_close(struct inode *inode, struct file *file); +static ssize_t conninfra_dev_read(struct file *filp, char __user *buf, + size_t count, loff_t *f_pos); +static ssize_t conninfra_dev_write(struct file *filp, + const char __user *buf, size_t count, + loff_t *f_pos); +static long conninfra_dev_unlocked_ioctl( + struct file *filp, unsigned int cmd, unsigned long arg); +#ifdef CONFIG_COMPAT +static long conninfra_dev_compat_ioctl( + struct file *filp, unsigned int cmd, unsigned long arg); +#endif /* CONFIG_COMPAT */ + +static int conninfra_dev_suspend_cb(void); +static int conninfra_dev_resume_cb(void); +static int conninfra_dev_pmic_event_cb(unsigned int, unsigned int); +/******************************************************************************* +* P U B L I C D A T A +******************************************************************************** +*/ + +/******************************************************************************* +* P R I V A T E D A T A +******************************************************************************** +*/ + +struct class *pConninfraClass; +struct device *pConninfraDev; +static struct cdev gConninfraCdev; + +const struct file_operations gConninfraDevFops = { + .open = conninfra_dev_open, + .release = conninfra_dev_close, + .read = conninfra_dev_read, + .write = conninfra_dev_write, + .unlocked_ioctl = conninfra_dev_unlocked_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = conninfra_dev_compat_ioctl, +#endif /* CONFIG_COMPAT */ +}; + +static int gConnInfraMajor = CONNINFRA_DEV_MAJOR; + +/* screen on/off notification */ + +static struct conninfra_dev_cb gConninfraDevCb = { + .conninfra_suspend_cb = conninfra_dev_suspend_cb, + .conninfra_resume_cb = conninfra_dev_resume_cb, + .conninfra_pmic_event_notifier = conninfra_dev_pmic_event_cb, +}; + +/******************************************************************************* +* F U N C T I O N S +******************************************************************************** +*/ + +int conninfra_dev_open(struct inode *inode, struct file *file) +{ + static DEFINE_RATELIMIT_STATE(_rs, HZ, 1); + + if (!wait_event_timeout(g_conninfra_init_wq, g_conninfra_init_status == CONNINFRA_INIT_DONE, + msecs_to_jiffies(CONNINFRA_DEV_INIT_TO_MS))) { + if (__ratelimit(&_rs)) { + pr_warn("wait_event_timeout (%d)ms,(%lu)jiffies,return -EIO\n", + CONNINFRA_DEV_INIT_TO_MS, msecs_to_jiffies(CONNINFRA_DEV_INIT_TO_MS)); + } + return -EIO; + } + + pr_info("open major %d minor %d (pid %d)\n", + imajor(inode), iminor(inode), current->pid); + + return 0; +} + +int conninfra_dev_close(struct inode *inode, struct file *file) +{ + pr_info("close major %d minor %d (pid %d)\n", + imajor(inode), iminor(inode), current->pid); + + return 0; +} + +ssize_t conninfra_dev_read(struct file *filp, char __user *buf, + size_t count, loff_t *f_pos) +{ + return 0; +} + +ssize_t conninfra_dev_write(struct file *filp, + const char __user *buf, size_t count, loff_t *f_pos) +{ + return 0; +} + +static long conninfra_dev_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + int retval = 0; + + pr_info("[%s] cmd (%d),arg(%ld)\n", __func__, cmd, arg); + switch (cmd) { + case CONNINFRA_IOCTL_GET_CHIP_ID: + retval = consys_hw_chipid_get(); + break; + } + return retval; + +} + +#ifdef CONFIG_COMPAT +static long conninfra_dev_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + long ret; + + pr_info("[%s] cmd (%d)\n", __func__, cmd); + ret = conninfra_dev_unlocked_ioctl(filp, cmd, arg); + return ret; +} +#endif /* CONFIG_COMPAT */ + +static int conninfra_dev_suspend_cb(void) +{ + return 0; +} + +static int conninfra_dev_resume_cb(void) +{ + conninfra_core_dump_power_state(); + return 0; +} + +static int conninfra_dev_pmic_event_cb(unsigned int id, unsigned int event) +{ + conninfra_core_pmic_event_cb(id, event); + + return 0; +} + +/************************************************************************/ + +static int conninfra_dev_init(void) +{ + dev_t devID = MKDEV(gConnInfraMajor, 0); + int cdevErr = -1; + int iret = 0; + + g_conninfra_init_status = CONNINFRA_INIT_START; + init_waitqueue_head((wait_queue_head_t *)&g_conninfra_init_wq); + + iret = register_chrdev_region(devID, CONNINFRA_DEV_NUM, CONNINFRA_DRVIER_NAME); + if (iret) { + pr_err("fail to register chrdev\n"); + g_conninfra_init_status = CONNINFRA_INIT_NOT_START; + return -1; + } + + cdev_init(&gConninfraCdev, &gConninfraDevFops); + gConninfraCdev.owner = THIS_MODULE; + + cdevErr = cdev_add(&gConninfraCdev, devID, CONNINFRA_DEV_NUM); + if (cdevErr) { + pr_err("cdev_add() fails (%d)\n", cdevErr); + goto err1; + } + + pConninfraClass = class_create(THIS_MODULE, CONNINFRA_DEVICE_NAME); + if (IS_ERR(pConninfraClass)) { + pr_err("class create fail, error code(%ld)\n", PTR_ERR(pConninfraClass)); + goto err1; + } + + pConninfraDev = device_create(pConninfraClass, NULL, devID, NULL, CONNINFRA_DEVICE_NAME); + if (IS_ERR(pConninfraDev)) { + pr_err("device create fail, error code(%ld)\n", PTR_ERR(pConninfraDev)); + goto err2; + } + + iret = mtk_conninfra_drv_init(&gConninfraDevCb); + if (iret) { + pr_err("init consys_hw fail, ret = %d\n", iret); + g_conninfra_init_status = CONNINFRA_INIT_NOT_START; + return -2; + } + + iret = conninfra_core_init(); + if (iret) { + pr_err("conninfra init fail\n"); + g_conninfra_init_status = CONNINFRA_INIT_NOT_START; + return -3; + } + + pr_info("ConnInfra Dev: init (%d)\n", iret); + g_conninfra_init_status = CONNINFRA_INIT_DONE; + +#ifdef CONFIG_CONNINFRA_AUTO_UP + iret = conninfra_core_power_on(CONNDRV_TYPE_CONNINFRA); + if (iret) { + pr_err("conninfra auto load power on fail\n"); + return -4; + } +#endif /* CONFIG_CONNINFRA_AUTO_UP */ + + return 0; + +err2: + + pr_err("[conninfra_dev_init] err2\n"); + if (pConninfraClass) { + class_destroy(pConninfraClass); + pConninfraClass = NULL; + } +err1: + pr_err("[conninfra_dev_init] err1\n"); + if (cdevErr == 0) + cdev_del(&gConninfraCdev); + + if (iret == 0) { + unregister_chrdev_region(devID, CONNINFRA_DEV_NUM); + gConnInfraMajor = -1; + } + + g_conninfra_init_status = CONNINFRA_INIT_NOT_START; + return -2; +} + +static void conninfra_dev_deinit(void) +{ + dev_t dev = MKDEV(gConnInfraMajor, 0); + int iret = 0; + +#ifdef CONFIG_CONNINFRA_AUTO_UP + iret = conninfra_core_power_off(CONNDRV_TYPE_CONNINFRA); + if (iret) { + pr_err("conninfra auto load power off fail\n"); + } +#endif /* CONFIG_CONNINFRA_AUTO_UP */ + + g_conninfra_init_status = CONNINFRA_INIT_NOT_START; + + iret = conninfra_core_deinit(); + + iret = mtk_conninfra_drv_deinit(); + + if (pConninfraDev) { + device_destroy(pConninfraClass, dev); + pConninfraDev = NULL; + } + + if (pConninfraClass) { + class_destroy(pConninfraClass); + pConninfraClass = NULL; + } + + cdev_del(&gConninfraCdev); + unregister_chrdev_region(dev, CONNINFRA_DEV_NUM); + + pr_info("ConnInfra: platform init (%d)\n", iret); +} + +module_init(conninfra_dev_init); +module_exit(conninfra_dev_deinit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Willy.Yu @ CTD/SE5/CS5"); + +module_param(gConnInfraMajor, uint, 0644); +