diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/.gitignore b/packages/platforms/accton/x86-64/dcg8510_32d/.gitignore new file mode 100644 index 0000000000..d40103f364 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/.gitignore @@ -0,0 +1,2 @@ +*x86*64*accton*dcg8510*32d*.mk +onlpdump.mk diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/Makefile b/packages/platforms/accton/x86-64/dcg8510_32d/Makefile new file mode 100644 index 0000000000..dc1e7b86f0 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/Makefile @@ -0,0 +1 @@ +include $(ONL)/make/pkg.mk diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/Makefile b/packages/platforms/accton/x86-64/dcg8510_32d/modules/Makefile new file mode 100644 index 0000000000..502e772a7b --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/Makefile @@ -0,0 +1 @@ +include $(ONL)/make/pkg.mk diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/PKG.yml b/packages/platforms/accton/x86-64/dcg8510_32d/modules/PKG.yml new file mode 100644 index 0000000000..f4cab49e50 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/PKG.yml @@ -0,0 +1 @@ +!include $ONL_TEMPLATES/platform-modules.yml ARCH=amd64 VENDOR=accton BASENAME=x86-64-accton-dcg8510-32d KERNELS="onl-kernel-4.19-lts-x86-64-all:amd64" diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/.gitignore b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/.gitignore new file mode 100644 index 0000000000..a65b41774a --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/.gitignore @@ -0,0 +1 @@ +lib diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/COPYING b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/COPYING new file mode 100644 index 0000000000..d159169d10 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/Makefile b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/Makefile new file mode 100644 index 0000000000..3f116fc959 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/Makefile @@ -0,0 +1,6 @@ +KERNELS := onl-kernel-4.19-lts-x86-64-all:amd64 +KMODULES := src +VENDOR := accton +BASENAME := x86-64-accton-dcg8510-32d +ARCH := x86_64 +include $(ONL)/make/kmodule.mk diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/Makefile b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/Makefile new file mode 100644 index 0000000000..9fef92c8bd --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/Makefile @@ -0,0 +1,46 @@ +############################ +# Makefile for s3ip driver # +############################ +obj-m += switch_mb_driver.o +obj-m += switch_fpga_driver.o +obj-m += switch_cpld_driver.o +obj-m += switch_led_driver.o +obj-m += switch_fan_driver.o +obj-m += switch_transceiver_driver.o +obj-m += switch_psu_driver.o +obj-m += switch_sensor_driver.o +obj-m += switch_wdt_driver.o +obj-m += switch_avs_driver.o +obj-m += switch_bmc_driver.o + +obj-m += s3ip/switch_s3ip_switch.o +obj-m += s3ip/switch_s3ip_syseeprom.o +obj-m += s3ip/switch_s3ip_fpga.o +obj-m += s3ip/switch_s3ip_cpld.o +obj-m += s3ip/switch_s3ip_sysled.o +obj-m += s3ip/switch_s3ip_fan.o +obj-m += s3ip/switch_s3ip_transceiver.o +obj-m += s3ip/switch_s3ip_watchdog.o +obj-m += s3ip/switch_s3ip_psu.o +obj-m += s3ip/switch_s3ip_sensor.o +obj-m += s3ip/switch_s3ip_avs.o +obj-m += s3ip/switch_s3ip_bmc.o + +obj-m += third_party/switch_system_fpga.o +#obj-m += third_party/switch_intel-spi.o +obj-m += third_party/switch_optoe.o +obj-m += third_party/switch_coretemp.o +obj-m += third_party/switch_i2c_cpld.o +obj-m += third_party/switch_system_cpld.o +obj-m += third_party/switch_lm75.o +obj-m += third_party/switch_at24.o +obj-m += third_party/i2c-imc.o +obj-m += third_party/sysfs_ipmi.o +#obj-m += third_party/intel-spi-platform.o +obj-m += third_party/mp2975.o +obj-m += third_party/switch_pmbus_core.o +obj-m += third_party/bel-pfe.o + +subdir-ccflags-y := -I$(src) +subdir-ccflags-y += -I$(src)/third_party + diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/s3ip/Makefile b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/s3ip/Makefile new file mode 100644 index 0000000000..66b5c5162a --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/s3ip/Makefile @@ -0,0 +1,12 @@ +obj-m += switch_s3ip_switch.o +obj-m += switch_s3ip_syseeprom.o +obj-m += switch_s3ip_fpga.o +obj-m += switch_s3ip_cpld.o +obj-m += switch_s3ip_sysled.o +obj-m += switch_s3ip_fan.o +obj-m += switch_s3ip_transceiver.o +obj-m += switch_s3ip_watchdog.o +obj-m += switch_s3ip_psu.o +obj-m += switch_s3ip_sensor.o +obj-m += switch_s3ip_avs.o +obj-m += switch_s3ip_bmc.o diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/s3ip/switch_s3ip_avs.c b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/s3ip/switch_s3ip_avs.c new file mode 100644 index 0000000000..aac2410759 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/s3ip/switch_s3ip_avs.c @@ -0,0 +1,342 @@ +#include +#include +#include +#include +#include + +#include "pmbus.h" +#include "switch_avs_driver.h" + +#define SWITCH_S3IP_AVS_VERSION "0.0.0.1" + +unsigned int loglevel = 0; +struct avs_drivers_t *cb_func = NULL; + +struct avs_attribute{ + struct attribute attr; + ssize_t (*show)(struct kobject *obj, struct avs_attribute *attr, char *buf); + ssize_t (*store)(struct kobject *obj, struct avs_attribute *attr, const char *buf, size_t count); +}; + +/* For s3ip */ +extern struct kobject *switch_kobj; +static struct kobject *avs_kobj; +static struct kobject *avs_index_kobj[TOTAL_FMEA_AVS_NUM]; + +enum avs_attrs { + LOGLEVEL, + WORK_STATUS, + CURRENT_STATUS, + PMBUS_STATUS, + NUM_AVS_ATTR, +}; + +int get_avs_index(struct kobject *kobj) +{ + int retval=0; + unsigned int avs_index; + char avs_index_str[2] = {0}; +#ifdef C11_ANNEX_K + if(memcpy_s(avs_index_str, 2, (kobject_name(kobj)+3), 1) != 0) + { + return -ENOMEM; + } +#else + memcpy(avs_index_str, (kobject_name(kobj)+3), 1); +#endif + retval = kstrtoint(avs_index_str, 10, &avs_index); + if(retval == 0) + { + AVS_DEBUG("avs_index:%d \n", avs_index); + } + else + { + AVS_DEBUG("Error:%d, avs_index:%s \n", retval, avs_index_str); + return -1; + } + + return avs_index; +} + +static ssize_t s3ip_get_loglevel(struct kobject *kobj, struct avs_attribute *attr, char *buf) +{ +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", loglevel); +#else + return sprintf(buf, "%d\n", loglevel); +#endif +} + +static ssize_t s3ip_set_loglevel(struct kobject *kobj, struct avs_attribute *attr, const char *buf, size_t count) +{ + int retval=0; + long lev; + + if(cb_func == NULL) + return -1; + + retval = kstrtol(buf, 0, &lev); + if(retval == 0) + { + AVS_DEBUG("lev:%ld \n", lev); + } + else + { + AVS_DEBUG("Error:%d, lev:%s \n", retval, buf); + return -1; + } + + loglevel = (unsigned int)lev; + + cb_func->set_loglevel(lev); + + return count; +} + +static ssize_t s3ip_fmea_get_work_status(struct kobject *kobj, struct avs_attribute *attr, char *buf) +{ + int retval=0; + int avs_index; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + avs_index = get_avs_index(kobj); + if(avs_index < 0) + { + AVS_DEBUG("Get avs index failed.\n"); + ERROR_RETURN_NA(-EINVAL); + } + + retval = cb_func->fmea_get_work_status(avs_index, buf, "s3ip"); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { + return retval; + } +} + +static ssize_t s3ip_fmea_get_current_status(struct kobject *kobj, struct avs_attribute *attr, char *buf) +{ + int retval=0; + int avs_index; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + avs_index = get_avs_index(kobj); + if(avs_index < 0) + { + AVS_DEBUG("Get avs index failed.\n"); + ERROR_RETURN_NA(-EINVAL); + } + + retval = cb_func->fmea_get_current_status(avs_index, buf, "s3ip"); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { + return retval; + } +} + +static ssize_t s3ip_fmea_get_pmbus_status(struct kobject *kobj, struct avs_attribute *attr, char *buf) +{ + int retval=0; + int avs_index; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + avs_index = get_avs_index(kobj); + if(avs_index < 0) + { + AVS_DEBUG("Get avs index failed.\n"); + ERROR_RETURN_NA(-EINVAL); + } + + retval = cb_func->fmea_get_pmbus_status(avs_index, buf, "s3ip"); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { + return retval; + } +} + +static struct avs_attribute avs_attr[NUM_AVS_ATTR] = { + [LOGLEVEL] = {{.name = "loglevel", .mode = S_IRUGO | S_IWUSR}, s3ip_get_loglevel, s3ip_set_loglevel}, + [WORK_STATUS] = {{.name = "work_status", .mode = S_IRUGO}, s3ip_fmea_get_work_status, NULL}, + [CURRENT_STATUS] = {{.name = "current_status", .mode = S_IRUGO}, s3ip_fmea_get_current_status, NULL}, + [PMBUS_STATUS] = {{.name = "pmbus_status", .mode = S_IRUGO}, s3ip_fmea_get_pmbus_status, NULL}, +}; + +void s3ip_avs_drivers_register(struct avs_drivers_t *pfunc) +{ + cb_func = pfunc; + + return; +} +EXPORT_SYMBOL_GPL(s3ip_avs_drivers_register); + +void s3ip_avs_drivers_unregister(void) +{ + cb_func = NULL; + + return; +} +EXPORT_SYMBOL_GPL(s3ip_avs_drivers_unregister); + +static int __init switch_avs_init(void) +{ + int err=0; + int retval=0; + int i; + int avs_index; + char *avs_index_str; + + avs_index_str = (char *)kzalloc(3*sizeof(char), GFP_KERNEL); + if (!avs_index_str) + { + AVS_DEBUG( "Fail to alloc avs_index_str memory\n"); + return -ENOMEM; + } + + /* For s3ip */ + avs_kobj = kobject_create_and_add("avs", switch_kobj); + if(!avs_kobj) + { + AVS_DEBUG( "Failed to create 'avs'\n"); + err = -ENOMEM; + goto sysfs_create_kobject_avs_failed; + } + + for(i = 0; i < WORK_STATUS ; i++) + { + AVS_DEBUG( "sysfs_create_file /avs/%s\n", avs_attr[i].attr.name); + retval = sysfs_create_file(avs_kobj, &avs_attr[i].attr); + if(retval) + { + printk(KERN_ERR "Failed to create file '%s'\n", avs_attr[i].attr.name); + err = -retval; + goto sysfs_create_s3ip_attr_loglevel_failed; + } + } + + for(avs_index = 0; avs_index < TOTAL_FMEA_AVS_NUM; avs_index++) + { +#ifdef C11_ANNEX_K + if(sprintf_s(avs_index_str, 8, "avs%d", avs_index) < 0) +#else + if(sprintf(avs_index_str, "avs%d", avs_index) < 0) +#endif + { + err = -ENOMEM; + goto sysfs_create_kobject_avs_index_failed; + } + avs_index_kobj[avs_index] = kobject_create_and_add(avs_index_str, avs_kobj); + if(!avs_index_kobj[avs_index]) + { + AVS_DEBUG( "Failed to create %s\n", avs_index_str); + err = -ENOMEM; + goto sysfs_create_kobject_avs_index_failed; + } + + for(i = WORK_STATUS; i < NUM_AVS_ATTR ; i++) + { + AVS_DEBUG( "sysfs_create_file /avs/%s\n", avs_attr[i].attr.name); + retval = sysfs_create_file(avs_index_kobj[avs_index], &avs_attr[i].attr); + if(retval) + { + printk(KERN_ERR "Failed to create file '%s'\n", avs_attr[i].attr.name); + err = -retval; + goto sysfs_create_s3ip_attr_failed; + } + } + } + + kfree(avs_index_str); + + return 0; + +sysfs_create_s3ip_attr_failed: +sysfs_create_kobject_avs_index_failed: + for(avs_index = 0; avs_index < TOTAL_FMEA_AVS_NUM; avs_index++) + { + if(avs_index_kobj[avs_index]) + { + for(i = WORK_STATUS; i < NUM_AVS_ATTR ; i++) + sysfs_remove_file(avs_index_kobj[avs_index], &avs_attr[i].attr); + + kobject_put(avs_index_kobj[avs_index]); + avs_index_kobj[avs_index] = NULL; + } + } + + for(i = 0; i < WORK_STATUS ; i++) + sysfs_remove_file(avs_kobj, &avs_attr[i].attr); + +sysfs_create_s3ip_attr_loglevel_failed: + if(avs_kobj) + { + kobject_put(avs_kobj); + avs_kobj = NULL; + } + +sysfs_create_kobject_avs_failed: + kfree(avs_index_str); + + return err; +} + +static void __exit switch_avs_exit(void) +{ + int i; + int avs_index; + + /* For s3ip */ + for(avs_index = 0; avs_index < TOTAL_FMEA_AVS_NUM; avs_index++) + { + if(avs_index_kobj[avs_index]) + { + for(i = WORK_STATUS; i < NUM_AVS_ATTR ; i++) + sysfs_remove_file(avs_index_kobj[avs_index], &avs_attr[i].attr); + + kobject_put(avs_index_kobj[avs_index]); + avs_index_kobj[avs_index] = NULL; + } + } + + for(i = 0; i < WORK_STATUS ; i++) + sysfs_remove_file(avs_kobj, &avs_attr[i].attr); + + if(avs_kobj) + { + kobject_put(avs_kobj); + avs_kobj = NULL; + } + + cb_func = NULL; + + return; +} + +MODULE_DESCRIPTION("S3IP Switch S3IP AVS Driver"); +MODULE_VERSION(SWITCH_S3IP_AVS_VERSION); +MODULE_LICENSE("GPL"); + +module_init(switch_avs_init); +module_exit(switch_avs_exit); diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/s3ip/switch_s3ip_bmc.c b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/s3ip/switch_s3ip_bmc.c new file mode 100644 index 0000000000..c69888f6db --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/s3ip/switch_s3ip_bmc.c @@ -0,0 +1,172 @@ +#include +#include +#include +#include +#include + +#include "pmbus.h" +#include "switch_bmc_driver.h" + +#define SWITCH_S3IP_BMC_VERSION "0.0.0.1" + +unsigned int loglevel = 0; +struct bmc_drivers_t *cb_func = NULL; + +struct bmc_attribute{ + struct attribute attr; + ssize_t (*show)(struct kobject *obj, struct bmc_attribute *attr, char *buf); + ssize_t (*store)(struct kobject *obj, struct bmc_attribute *attr, const char *buf, size_t count); +}; + +/* For s3ip */ +extern struct kobject *switch_kobj; +static struct kobject *bmc_kobj; + +enum bmc_attrs { + LOGLEVEL, + STATUS, + NUM_BMC_ATTR, +}; + +static ssize_t s3ip_get_loglevel(struct kobject *kobj, struct bmc_attribute *attr, char *buf) +{ +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", loglevel); +#else + return sprintf(buf, "%d\n", loglevel); +#endif +} + +static ssize_t s3ip_set_loglevel(struct kobject *kobj, struct bmc_attribute *attr, const char *buf, size_t count) +{ + int retval=0; + long lev; + + if(cb_func == NULL) + return -1; + + retval = kstrtol(buf, 0, &lev); + if(retval == 0) + { + BMC_DEBUG("lev:%ld \n", lev); + } + else + { + BMC_DEBUG("Error:%d, lev:%s \n", retval, buf); + return -1; + } + + loglevel = (unsigned int)lev; + + cb_func->set_loglevel(lev); + + return count; +} + +static ssize_t s3ip_get_status(struct kobject *kobj, struct bmc_attribute *attr, char *buf) +{ + int retval=0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + retval = cb_func->get_status(buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { + return retval; + } +} + +static struct bmc_attribute bmc_attr[NUM_BMC_ATTR] = { + [LOGLEVEL] = {{.name = "loglevel", .mode = S_IRUGO | S_IWUSR}, s3ip_get_loglevel, s3ip_set_loglevel}, + [STATUS] = {{.name = "status", .mode = S_IRUGO}, s3ip_get_status, NULL}, +}; + +void s3ip_bmc_drivers_register(struct bmc_drivers_t *pfunc) +{ + cb_func = pfunc; + + return; +} +EXPORT_SYMBOL_GPL(s3ip_bmc_drivers_register); + +void s3ip_bmc_drivers_unregister(void) +{ + cb_func = NULL; + + return; +} +EXPORT_SYMBOL_GPL(s3ip_bmc_drivers_unregister); + +static int __init switch_bmc_init(void) +{ + int err=0; + int retval=0; + int i; + + /* For s3ip */ + bmc_kobj = kobject_create_and_add("bmc", switch_kobj); + if(!bmc_kobj) + { + BMC_DEBUG( "Failed to create 'bmc'\n"); + return -ENOMEM; + } + + for(i = 0; i < NUM_BMC_ATTR ; i++) + { + BMC_DEBUG( "sysfs_create_file /bmc/%s\n", bmc_attr[i].attr.name); + retval = sysfs_create_file(bmc_kobj, &bmc_attr[i].attr); + if(retval) + { + printk(KERN_ERR "Failed to create file '%s'\n", bmc_attr[i].attr.name); + err = -retval; + goto sysfs_create_s3ip_bmc_attr_failed; + } + } + + return 0; + +sysfs_create_s3ip_bmc_attr_failed: + for(i = 0; i < NUM_BMC_ATTR ; i++) + sysfs_remove_file(bmc_kobj, &bmc_attr[i].attr); + + if(bmc_kobj) + { + kobject_put(bmc_kobj); + bmc_kobj = NULL; + } + + return err; +} + +static void __exit switch_bmc_exit(void) +{ + int i; + + /* For s3ip */ + for(i = 0; i < NUM_BMC_ATTR ; i++) + sysfs_remove_file(bmc_kobj, &bmc_attr[i].attr); + + if(bmc_kobj) + { + kobject_put(bmc_kobj); + bmc_kobj = NULL; + } + + cb_func = NULL; + + return; +} + +MODULE_DESCRIPTION("S3IP Switch S3IP BMC Driver"); +MODULE_VERSION(SWITCH_S3IP_BMC_VERSION); +MODULE_LICENSE("GPL"); + +module_init(switch_bmc_init); +module_exit(switch_bmc_exit); diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/s3ip/switch_s3ip_cpld.c b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/s3ip/switch_s3ip_cpld.c new file mode 100644 index 0000000000..ccee062141 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/s3ip/switch_s3ip_cpld.c @@ -0,0 +1,789 @@ +#include +#include +#include +#include + +#include "switch_cpld_driver.h" + +#define SWITCH_S3IP_CPLD_VERSION "0.0.0.1" + +unsigned int loglevel = 0; + +struct cpld_attribute{ + struct attribute attr; + ssize_t (*show)(struct kobject *obj, struct cpld_attribute *attr, char *buf); + ssize_t (*store)(struct kobject *obj, struct cpld_attribute *attr, const char *buf, size_t count); +}; + +struct cpld_drivers_t *cb_func = NULL; + +/* For s3ip */ +extern struct kobject *switch_kobj; +static struct kobject *cpld_kobj; +static struct kobject *cpld_index_kobj[CPLD_TOTAL_NUM]; + +static struct kobject *cpld_fmea_kobj[CPLD_TOTAL_NUM][5]; + +static char *cpld_fmea_kobj_name[CPLD_TOTAL_NUM][5] = { + {"battery", "clock", "corrosion", "voltage"}, + {"battery", "clock", "corrosion", "voltage"}, + {"battery", "clock", "corrosion", "voltage"}, + {"battery", "clock", "corrosion", "voltage"} +}; + +static bool cpld_is_support_reset[CPLD_TOTAL_NUM] = { + true, true, true, true +}; + +static bool cpld_is_support_interrupt[CPLD_TOTAL_NUM] = { + true, true, true, true +}; + +enum cpld_attrs { + DEBUG, + LOGLEVEL, + REBOOT_CAUSE, + NUM, + ALIAS, + TYPE, + HW_VERSION, + BOARD_VERSION, + REG_TEST, + NUM_CPLD_ATTR, +}; + +enum cpld_fmea_attrs { + RESET, + INTERRUPT, + STATUS, + NUM_CPLD_FMEA_ATTR, +}; + +int get_cpld_index(struct kobject *kobj) +{ + int retval=0; + unsigned int cpld_index; + char cpld_index_str[2] = {0}; +#ifdef C11_ANNEX_K + if(memcpy_s(cpld_index_str, 2, (kobject_name(kobj)+4), 1) != 0) + { + return -ENOMEM; + } +#else + memcpy(cpld_index_str, (kobject_name(kobj)+4), 1); +#endif + retval = kstrtoint(cpld_index_str, 10, &cpld_index); + if(retval == 0) + { + CPLD_DEBUG("cpld_index:%d \n", cpld_index); + } + else + { + CPLD_DEBUG("Error:%d, cpld_index:%s \n", retval, cpld_index_str); + return -1; + } + + return cpld_index; +} + +int get_cpld_index_from_fmea_kobj(struct kobject *kobj) +{ + int retval=0; + unsigned int cpld_index; + char cpld_index_str[2] = {0}; +#ifdef C11_ANNEX_K + if(memcpy_s(cpld_index_str, 2, (kobject_name(kobj->parent)+4), 1) != 0) + { + return -ENOMEM; + } +#else + memcpy(cpld_index_str, (kobject_name(kobj->parent)+4), 1); +#endif + + retval = kstrtoint(cpld_index_str, 10, &cpld_index); + if(retval == 0) + { + CPLD_DEBUG("cpld_index:%d \n", cpld_index); + } + else + { + CPLD_DEBUG("Error:%d, cpld_index:%s \n", retval, cpld_index_str); + return -1; + } + + return cpld_index; +} + +static ssize_t s3ip_debug_help(struct kobject *kobj, struct cpld_attribute *attr, char *buf) +{ + int retval=0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + retval = cb_func->debug_help(buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { + return retval; + } +} + +static ssize_t s3ip_debug(struct kobject *kobj, struct cpld_attribute *attr, const char *buf, size_t count) +{ + return count; +} + +static ssize_t s3ip_get_loglevel(struct kobject *kobj, struct cpld_attribute *attr, char *buf) +{ +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", loglevel); +#else + return sprintf(buf, "%d\n", loglevel); +#endif +} + +static ssize_t s3ip_set_loglevel(struct kobject *kobj, struct cpld_attribute *attr, const char *buf, size_t count) +{ + int retval=0; + long lev; + + if(cb_func == NULL) + return -1; + + retval = kstrtol(buf, 0, &lev); + if(retval == 0) + { + CPLD_DEBUG("lev:%ld \n", lev); + } + else + { + CPLD_DEBUG("Error:%d, lev:%s \n", retval, buf); + return -1; + } + + loglevel = (unsigned int)lev; + + cb_func->set_loglevel(lev); + + return count; +} + +static ssize_t s3ip_get_reboot_cause(struct kobject *kobj, struct cpld_attribute *attr, char *buf) +{ + int retval=0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + retval = cb_func->get_reboot_cause(buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { + return retval; + } +} + +static ssize_t s3ip_get_num(struct kobject *kobj, struct cpld_attribute *attr, char *buf) +{ +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", CPLD_TOTAL_NUM); +#else + return sprintf(buf, "%d\n", CPLD_TOTAL_NUM); +#endif +} + +static ssize_t s3ip_get_alias(struct kobject *kobj, struct cpld_attribute *attr, char *buf) +{ + int retval=0; + int cpld_index; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + cpld_index = get_cpld_index(kobj); + if(cpld_index < 0) + { + CPLD_DEBUG("Get cpld index failed.\n"); + ERROR_RETURN_NA(-1); + } + + retval = cb_func->get_alias(cpld_index, buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { + return retval; + } +} + +static ssize_t s3ip_get_type(struct kobject *kobj, struct cpld_attribute *attr, char *buf) +{ + int retval=0; + int cpld_index; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + cpld_index = get_cpld_index(kobj); + if(cpld_index < 0) + { + CPLD_DEBUG("Get cpld index failed.\n"); + ERROR_RETURN_NA(-1); + } + + retval = cb_func->get_type(cpld_index, buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { + return retval; + } +} + +static ssize_t s3ip_get_hw_version(struct kobject *kobj, struct cpld_attribute *attr, char *buf) +{ + int retval=0; + int cpld_index; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + cpld_index = get_cpld_index(kobj); + if(cpld_index < 0) + { + CPLD_DEBUG("Get cpld index failed.\n"); + ERROR_RETURN_NA(-1); + } + + retval = cb_func->get_hw_version(cpld_index, buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { + return retval; + } +} + +static ssize_t s3ip_get_board_version(struct kobject *kobj, struct cpld_attribute *attr, char *buf) +{ + int retval=0; + int cpld_index; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + cpld_index = get_cpld_index(kobj); + if(cpld_index < 0) + { + CPLD_DEBUG("Get cpld index failed.\n"); + ERROR_RETURN_NA(-1); + } + + retval = cb_func->get_board_version(cpld_index, buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { + return retval; + } +} + +static ssize_t s3ip_get_reg_test(struct kobject *kobj, struct cpld_attribute *attr, char *buf) +{ + int retval=0; + int cpld_index; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + cpld_index = get_cpld_index(kobj); + if(cpld_index < 0) + { + CPLD_DEBUG("Get cpld index failed.\n"); + ERROR_RETURN_NA(-1); + } + + retval = cb_func->get_fmea_selftest_status(cpld_index, buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { + return retval; + } +} + +static ssize_t s3ip_set_reg_test(struct kobject *kobj, struct cpld_attribute *attr, const char *buf, size_t count) +{ + int retval=0; + int cpld_index; + int input_value; + unsigned char data; + + if((cb_func == NULL)||(cb_func->set_reg_test == NULL)) + { + return -EIO; + } + + cpld_index = get_cpld_index(kobj); + if(cpld_index < 0) + { + CPLD_DEBUG("Get cpld index failed.\n"); + return -EINVAL; + } + + retval = kstrtoint(buf, 0, &input_value); + if(retval == 0) + { + data = input_value; + CPLD_DEBUG("cpld%d reg_test data:%d \n", cpld_index, data); + } + else + { + CPLD_DEBUG("Error:%d, cpld%d reg_test data:%s\n", retval, cpld_index, buf); + return -EIO; + } + + retval = cb_func->set_reg_test(cpld_index, data); + if(retval < 0) + { + CPLD_DEBUG("Set cpld%d reg_test failed.\n", cpld_index); + return -EIO; + } + + return count; +} + +static ssize_t s3ip_get_fmea_status(struct kobject *kobj, struct cpld_attribute *attr, char *buf) +{ + int len=0; + int cpld_index; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + cpld_index = get_cpld_index_from_fmea_kobj(kobj); + if(cpld_index < 0) + { + CPLD_DEBUG("Get cpld index failed.\n"); + ERROR_RETURN_NA(-1); + } + + if(!strcmp(kobject_name(kobj), "corrosion")) + { + len = cb_func->get_fmea_corrosion_status(cpld_index-1, buf); + } + else if(!strcmp(kobject_name(kobj), "voltage")) + { + len = cb_func->get_fmea_voltage_status(cpld_index-1, buf); + } + else if(!strcmp(kobject_name(kobj), "clock")) + { + len = cb_func->get_fmea_clock_status(cpld_index-1, buf); + } + else if(!strcmp(kobject_name(kobj), "battery")) + { + len = cb_func->get_fmea_battery_status(cpld_index-1, buf); + } + else; + + return len; +} + +static ssize_t s3ip_get_fmea_reset(struct kobject *kobj, struct cpld_attribute *attr, char *buf) +{ + int cpld_index; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + cpld_index = get_cpld_index(kobj); + if(cpld_index < 0) + { + CPLD_DEBUG("Get cpld index failed.\n"); + ERROR_RETURN_NA(-1); + } + + return cb_func->get_fmea_reset(cpld_index-1, buf); +} + +static ssize_t s3ip_get_fmea_interrupt(struct kobject *kobj, struct cpld_attribute *attr, char *buf) +{ + int cpld_index; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + cpld_index = get_cpld_index(kobj); + if(cpld_index < 0) + { + CPLD_DEBUG("Get cpld index failed.\n"); + ERROR_RETURN_NA(-1); + } + + return cb_func->get_fmea_interrupt(cpld_index-1, buf); +} + +static ssize_t s3ip_set_fmea_reset(struct kobject *kobj, struct cpld_attribute *attr, const char *buf, size_t count) +{ + int retval=0; + int reset; + int cpld_index; + + if(cb_func == NULL) + { + return -ENXIO; + } + + retval = kstrtoint(buf, 10, &reset); + if(retval == 0) + { + CPLD_DEBUG("reset:%d \n", reset); + } + else + { + CPLD_DEBUG("Error:%d, reset:%s \n", retval, buf); + return -ENXIO; + } + + cpld_index = get_cpld_index(kobj); + if(cpld_index < 0) + { + CPLD_DEBUG("Get cpld index failed.\n"); + return -ENXIO; + } + + retval = cb_func->set_fmea_reset(cpld_index, reset); + if(retval == false) + { + CPLD_DEBUG("Set CPLD reset failed.\n"); + return -ENXIO; + } + + return count; +} + +static struct cpld_attribute cpld_attr[NUM_CPLD_ATTR] = { + [DEBUG] = {{.name = "debug", .mode = S_IRUGO | S_IWUSR}, s3ip_debug_help, s3ip_debug}, + [LOGLEVEL] = {{.name = "loglevel", .mode = S_IRUGO | S_IWUSR}, s3ip_get_loglevel, s3ip_set_loglevel}, + [REBOOT_CAUSE] = {{.name = "reboot_cause", .mode = S_IRUGO}, s3ip_get_reboot_cause, NULL}, + [NUM] = {{.name = "num", .mode = S_IRUGO}, s3ip_get_num, NULL}, + [ALIAS] = {{.name = "alias", .mode = S_IRUGO}, s3ip_get_alias, NULL}, + [TYPE] = {{.name = "type", .mode = S_IRUGO}, s3ip_get_type, NULL}, + [HW_VERSION] = {{.name = "firmware_version", .mode = S_IRUGO}, s3ip_get_hw_version, NULL}, + [BOARD_VERSION] = {{.name = "board_version", .mode = S_IRUGO}, s3ip_get_board_version, NULL}, + [REG_TEST] = {{.name = "reg_test", .mode = S_IRUGO | S_IWUSR}, s3ip_get_reg_test, s3ip_set_reg_test}, +}; + +static struct cpld_attribute cpld_fmea_attr[NUM_CPLD_FMEA_ATTR] = { + [RESET] = {{.name = "reset", .mode = S_IRUGO | S_IWUSR}, s3ip_get_fmea_reset, s3ip_set_fmea_reset}, + [INTERRUPT] = {{.name = "interrupt", .mode = S_IRUGO}, s3ip_get_fmea_interrupt, NULL}, + [STATUS] = {{.name = "status", .mode = S_IRUGO}, s3ip_get_fmea_status, NULL}, +}; + +void s3ip_cpld_drivers_register(struct cpld_drivers_t *pfunc) +{ + cb_func = pfunc; + + return; +} +EXPORT_SYMBOL_GPL(s3ip_cpld_drivers_register); + +void s3ip_cpld_drivers_unregister(void) +{ + cb_func = NULL; + + return; +} +EXPORT_SYMBOL_GPL(s3ip_cpld_drivers_unregister); + +static int __init switch_cpld_init(void) +{ + int err=0; + int retval=0; + int i; + int cpld_index; + char *cpld_index_str; + int fmea_kobj_num; + int fmea_kobj_index; + + cpld_index_str = (char *)kzalloc(7*sizeof(char), GFP_KERNEL); + if(!cpld_index_str) + { + CPLD_DEBUG( "Fail to alloc cpld_index_str memory\n"); + return -ENOMEM; + } + + /* For s3ip */ + cpld_kobj = kobject_create_and_add("cpld", switch_kobj); + if(!cpld_kobj) + { + CPLD_DEBUG( "Failed to create 'cpld'\n"); + err = -ENOMEM; + goto sysfs_create_kobject_cpld_failed; + } + + for(i = DEBUG; i <= NUM; i++) + { + CPLD_DEBUG( "sysfs_create_file /cpld/%s\n", cpld_attr[i].attr.name); + retval = sysfs_create_file(cpld_kobj, &cpld_attr[i].attr); + if(retval) + { + printk(KERN_ERR "Failed to create file '%s'\n", cpld_attr[i].attr.name); + err = -retval; + goto sysfs_create_s3ip_attr_failed; + } + } + + for(cpld_index = 0; cpld_index < CPLD_TOTAL_NUM; cpld_index++) + { +#ifdef C11_ANNEX_K + if(sprintf_s(cpld_index_str, 7, "cpld%d", cpld_index+1) < 0) +#else + if(sprintf(cpld_index_str, "cpld%d", cpld_index+1) < 0) +#endif + { + err = -ENOMEM; + goto sysfs_create_kobject_switch_cpld_index_failed; + } + cpld_index_kobj[cpld_index] = kobject_create_and_add(cpld_index_str, cpld_kobj); + if(!cpld_index_kobj[cpld_index]) + { + CPLD_DEBUG( "Failed to create 'cpld%d'\n", cpld_index+1); + err = -ENOMEM; + goto sysfs_create_kobject_switch_cpld_index_failed; + } + + for(i = ALIAS; i < NUM_CPLD_ATTR; i++) + { + CPLD_DEBUG( "sysfs_create_file /cpld/cpld%d/%s\n", cpld_index+1, cpld_attr[i].attr.name); + retval = sysfs_create_file(cpld_index_kobj[cpld_index], &cpld_attr[i].attr); + if(retval) + { + printk(KERN_ERR "Failed to create file '%s'\n", cpld_attr[i].attr.name); + err = -retval; + goto sysfs_create_s3ip_cpld_attr_failed; + } + } + + fmea_kobj_num = sizeof(cpld_fmea_kobj_name[cpld_index])/sizeof(cpld_fmea_kobj_name[cpld_index][0]); + for(fmea_kobj_index = 0; fmea_kobj_index < fmea_kobj_num; fmea_kobj_index++) + { + if(cpld_fmea_kobj_name[cpld_index][fmea_kobj_index] == NULL) + continue; + + cpld_fmea_kobj[cpld_index][fmea_kobj_index] = kobject_create_and_add(cpld_fmea_kobj_name[cpld_index][fmea_kobj_index], cpld_index_kobj[cpld_index]); + if(!cpld_fmea_kobj[cpld_index][fmea_kobj_index]) + { + CPLD_DEBUG( "Failed to create '%s'\n", cpld_fmea_kobj_name[cpld_index][fmea_kobj_index]); + err = -ENOMEM; + goto sysfs_create_s3ip_cpld_fmea_kobj_failed; + } + + for(i = STATUS; i < NUM_CPLD_FMEA_ATTR; i++) + { + CPLD_DEBUG( "sysfs_create_file /cpld/cpld%d/%s/%s\n", cpld_index, cpld_fmea_kobj_name[cpld_index][fmea_kobj_index], cpld_fmea_attr[i].attr.name); + retval = sysfs_create_file(cpld_fmea_kobj[cpld_index][fmea_kobj_index], &cpld_fmea_attr[i].attr); + if(retval) + { + printk(KERN_ERR "Failed to create file '%s'\n", cpld_fmea_attr[i].attr.name); + err = -retval; + goto sysfs_create_s3ip_cpld_fmea_attr_failed; + } + } + } + + if(cpld_is_support_reset[cpld_index]) + { + for(i = RESET; i < INTERRUPT; i++) + { + CPLD_DEBUG( "sysfs_create_file /cpld/cpld%d/%s\n", cpld_index, cpld_fmea_attr[i].attr.name); + retval = sysfs_create_file(cpld_index_kobj[cpld_index], &cpld_fmea_attr[i].attr); + if(retval) + { + printk(KERN_ERR "Failed to create file '%s'\n", cpld_fmea_attr[i].attr.name); + err = -retval; + goto sysfs_create_s3ip_fmea_attr_reset_failed; + } + } + } + + if(cpld_is_support_interrupt[cpld_index]) + { + for(i = INTERRUPT; i < STATUS; i++) + { + CPLD_DEBUG( "sysfs_create_file /cpld/cpld%d/%s\n", cpld_index, cpld_fmea_attr[i].attr.name); + retval = sysfs_create_file(cpld_index_kobj[cpld_index], &cpld_fmea_attr[i].attr); + if(retval) + { + printk(KERN_ERR "Failed to create file '%s'\n", cpld_fmea_attr[i].attr.name); + err = -retval; + goto sysfs_create_s3ip_fmea_attr_reset_failed; + } + } + } + } + + kfree(cpld_index_str); + + return 0; + +sysfs_create_s3ip_fmea_attr_reset_failed: +sysfs_create_s3ip_cpld_fmea_attr_failed: +sysfs_create_s3ip_cpld_fmea_kobj_failed: +sysfs_create_s3ip_cpld_attr_failed: +sysfs_create_kobject_switch_cpld_index_failed: + for(i = DEBUG; i <= NUM; i++) + sysfs_remove_file(cpld_kobj, &cpld_attr[i].attr); + + for(cpld_index = 0; cpld_index < CPLD_TOTAL_NUM; cpld_index++) + { + if(cpld_index_kobj[cpld_index]) + { + for(i = ALIAS; i < NUM_CPLD_ATTR; i++) + sysfs_remove_file(cpld_index_kobj[cpld_index], &cpld_attr[i].attr); + + fmea_kobj_num = sizeof(cpld_fmea_kobj_name[cpld_index])/sizeof(cpld_fmea_kobj_name[cpld_index][0]); + for(fmea_kobj_index = 0; fmea_kobj_index < fmea_kobj_num; fmea_kobj_index++) + { + if(cpld_fmea_kobj[cpld_index][fmea_kobj_index]) + { + for(i = STATUS; i < NUM_CPLD_FMEA_ATTR; i++) + sysfs_remove_file(cpld_fmea_kobj[cpld_index][fmea_kobj_index], &cpld_fmea_attr[i].attr); + + kobject_put(cpld_fmea_kobj[cpld_index][fmea_kobj_index]); + cpld_fmea_kobj[cpld_index][fmea_kobj_index] = NULL; + } + } + + if(cpld_is_support_reset[cpld_index]) + { + for(i = RESET; i < INTERRUPT; i++) + sysfs_remove_file(cpld_index_kobj[cpld_index], &cpld_fmea_attr[i].attr); + } + + if(cpld_is_support_interrupt[cpld_index]) + { + for(i = INTERRUPT; i < STATUS; i++) + sysfs_remove_file(cpld_index_kobj[cpld_index], &cpld_fmea_attr[i].attr); + } + + kobject_put(cpld_index_kobj[cpld_index]); + cpld_index_kobj[cpld_index] = NULL; + } + } + +sysfs_create_s3ip_attr_failed: + if(cpld_kobj) + { + kobject_put(cpld_kobj); + cpld_kobj = NULL; + } + +sysfs_create_kobject_cpld_failed: + kfree(cpld_index_str); + + return err; +} + +static void __exit switch_cpld_exit(void) +{ + int i; + int cpld_index; + int fmea_kobj_num; + int fmea_kobj_index; + + /* For s3ip */ + for(i = DEBUG; i <= NUM; i++) + sysfs_remove_file(cpld_kobj, &cpld_attr[i].attr); + + for(cpld_index = 0; cpld_index < CPLD_TOTAL_NUM; cpld_index++) + { + if(cpld_index_kobj[cpld_index]) + { + for(i = ALIAS; i < NUM_CPLD_ATTR; i++) + sysfs_remove_file(cpld_index_kobj[cpld_index], &cpld_attr[i].attr); + + fmea_kobj_num = sizeof(cpld_fmea_kobj_name[cpld_index])/sizeof(cpld_fmea_kobj_name[cpld_index][0]); + for(fmea_kobj_index = 0; fmea_kobj_index < fmea_kobj_num; fmea_kobj_index++) + { + if(cpld_fmea_kobj[cpld_index][fmea_kobj_index]) + { + for(i = STATUS; i < NUM_CPLD_FMEA_ATTR; i++) + sysfs_remove_file(cpld_fmea_kobj[cpld_index][fmea_kobj_index], &cpld_fmea_attr[i].attr); + + kobject_put(cpld_fmea_kobj[cpld_index][fmea_kobj_index]); + cpld_fmea_kobj[cpld_index][fmea_kobj_index] = NULL; + } + } + + if(cpld_is_support_reset[cpld_index]) + { + for(i = RESET; i < INTERRUPT; i++) + sysfs_remove_file(cpld_index_kobj[cpld_index], &cpld_fmea_attr[i].attr); + } + + if(cpld_is_support_interrupt[cpld_index]) + { + for(i = INTERRUPT; i < STATUS; i++) + sysfs_remove_file(cpld_index_kobj[cpld_index], &cpld_fmea_attr[i].attr); + } + + kobject_put(cpld_index_kobj[cpld_index]); + cpld_index_kobj[cpld_index] = NULL; + } + } + + if(cpld_kobj) + { + kobject_put(cpld_kobj); + cpld_kobj = NULL; + } + + cb_func = NULL; + + return; +} + +MODULE_DESCRIPTION("Switch S3IP CPLD Driver"); +MODULE_VERSION(SWITCH_S3IP_CPLD_VERSION); +MODULE_LICENSE("GPL"); + +module_init(switch_cpld_init); +module_exit(switch_cpld_exit); diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/s3ip/switch_s3ip_fan.c b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/s3ip/switch_s3ip_fan.c new file mode 100644 index 0000000000..de49eb90ab --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/s3ip/switch_s3ip_fan.c @@ -0,0 +1,1050 @@ +#include +#include +#include +#include + +#include "switch_fan_driver.h" +#include "switch_led_driver.h" + +#define SWITCH_S3IP_CPLD_VERSION "0.0.0.1" + +unsigned int loglevel = 0; + +struct fan_attribute{ + struct attribute attr; + ssize_t (*show)(struct kobject *obj, struct fan_attribute *attr, char *buf); + ssize_t (*store)(struct kobject *obj, struct fan_attribute *attr, const char *buf, size_t count); +}; + +struct fan_drivers_t *cb_func = NULL; + +/* For s3ip */ +extern struct kobject *switch_kobj; +static struct kobject *fan_kobj; +static struct kobject *fan_index_kobj[MAX_FAN_NUM]; +static struct kobject *motor_index_kobj[MAX_FAN_NUM][MAX_MOTOR_NUM]; + +enum fan_attrs { + DEBUG, + LOGLEVEL, + NUM, + MODEL_NAME, + SERIAL_NUMBER, + VENDOR, + PART_NUMBER, + HARDWARE_VERSION, + NUM_MOTORS, + STATUS, + LED_STATUS, + HI_DIRECTION, + HI_RATIO, + NUM_FAN_ATTR, +}; + +enum motor_attrs { + SPEED, + SPEED_TOLERANCE, + SPEED_MAX, + SPEED_MIN, + SPEED_TARGET, + RATIO, + DIRECTION, + NUM_MOTOR_ATTR, +}; + +int get_fan_index(struct kobject *kobj) +{ + int retval=0; + unsigned int fan_index; + char fan_index_str[2] = {0}; +#ifdef C11_ANNEX_K + if(memcpy_s(fan_index_str, 2, (kobject_name(kobj)+3), 1) != 0) + { + return -ENOMEM; + } +#else + memcpy(fan_index_str, (kobject_name(kobj)+3), 1); +#endif + + retval = kstrtoint(fan_index_str, 10, &fan_index); + if(retval == 0) + { + FAN_DEBUG("fan_index:%d \n", fan_index); + } + else + { + FAN_DEBUG("Error:%d, fan_index:%s \n", retval, fan_index_str); + return -1; + } + + return fan_index; +} + +int get_fan_index_from_motor_kobj(struct kobject *kobj) +{ + int retval=0; + unsigned int fan_index; + char fan_index_str[2] = {0}; + +#ifdef C11_ANNEX_K + if(memcpy_s(fan_index_str, 2, (kobject_name(kobj->parent)+3), 1) != 0) + { + return -ENOMEM; + } +#else + memcpy(fan_index_str, (kobject_name(kobj->parent)+3), 1); +#endif + + retval = kstrtoint(fan_index_str, 10, &fan_index); + if(retval == 0) + { + FAN_DEBUG("fan_index:%d \n", fan_index); + } + else + { + FAN_DEBUG("Error:%d, fan_index:%s \n", retval, fan_index_str); + return -1; + } + + return fan_index; +} + +int get_motor_index(struct kobject *kobj) +{ + int retval=0; + unsigned int motor_index; + char motor_index_str[2] = {0}; + +#ifdef C11_ANNEX_K + if(memcpy_s(motor_index_str, 2, (kobject_name(kobj)+5), 1) != 0) + { + return -ENOMEM; + } +#else + memcpy(motor_index_str, (kobject_name(kobj)+5), 1); +#endif + + retval = kstrtoint(motor_index_str, 10, &motor_index); + if(retval == 0) + { + FAN_DEBUG("motor_index:%d \n", motor_index); + } + else + { + FAN_DEBUG("Error:%d, motor_index:%s \n", retval, motor_index_str); + return -1; + } + + return motor_index; +} + +void set_fan_led(int index, unsigned short val) +{ + return; +} + +static ssize_t s3ip_debug_help(struct kobject *kobj, struct fan_attribute *attr, char *buf) +{ + int retval=0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + retval = cb_func->debug_help(buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { + return retval; + } +} + +static ssize_t s3ip_debug(struct kobject *kobj, struct fan_attribute *attr, const char *buf, size_t count) +{ + return count; +} + +static ssize_t s3ip_get_loglevel(struct kobject *kobj, struct fan_attribute *attr, char *buf) +{ + +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", loglevel); +#else + return sprintf(buf, "%d\n", loglevel); +#endif +} + +static ssize_t s3ip_set_loglevel(struct kobject *kobj, struct fan_attribute *attr, const char *buf, size_t count) +{ + int retval=0; + long lev; + + if(cb_func == NULL) + return -1; + + retval = kstrtol(buf, 0, &lev); + if(retval == 0) + { + FAN_DEBUG("lev:%ld \n", lev); + } + else + { + FAN_DEBUG("Error:%d, lev:%s \n", retval, buf); + return -1; + } + + loglevel = (unsigned int)lev; + + cb_func->set_loglevel(lev); + + return count; +} + +static ssize_t s3ip_get_num(struct kobject *kobj, struct fan_attribute *attr, char *buf) +{ +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", MAX_FAN_NUM); +#else + return sprintf(buf, "%d\n", MAX_FAN_NUM); +#endif + +} + +static ssize_t s3ip_get_model_name(struct kobject *kobj, struct fan_attribute *attr, char *buf) +{ + int retval=0; + int fan_index; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + fan_index = get_fan_index(kobj); + if(fan_index < 0) + { + FAN_DEBUG("Get fan index failed.\n"); + ERROR_RETURN_NA(-1); + } + + retval = cb_func->get_model_name(fan_index, buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { + return retval; + } +} + +static ssize_t s3ip_get_serial_number(struct kobject *kobj, struct fan_attribute *attr, char *buf) +{ + int retval=0; + int fan_index; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + fan_index = get_fan_index(kobj); + if(fan_index < 0) + { + FAN_DEBUG("Get fan index failed.\n"); + ERROR_RETURN_NA(-1); + } + + retval = cb_func->get_sn(fan_index, buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { + return retval; + } +} + +static ssize_t s3ip_get_vendor(struct kobject *kobj, struct fan_attribute *attr, char *buf) +{ + int retval=0; + int fan_index; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + fan_index = get_fan_index(kobj); + if(fan_index < 0) + { + FAN_DEBUG("Get fan index failed.\n"); + ERROR_RETURN_NA(-1); + } + + retval = cb_func->get_vendor(fan_index, buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { + return retval; + } +} + +static ssize_t s3ip_get_part_number(struct kobject *kobj, struct fan_attribute *attr, char *buf) +{ + int retval=0; + int fan_index; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + fan_index = get_fan_index(kobj); + if(fan_index < 0) + { + FAN_DEBUG("Get fan index failed.\n"); + ERROR_RETURN_NA(-1); + } + + retval = cb_func->get_part_number(fan_index, buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { + return retval; + } +} + +static ssize_t s3ip_get_hardware_version(struct kobject *kobj, struct fan_attribute *attr, char *buf) +{ + int retval=0; + int fan_index; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + fan_index = get_fan_index(kobj); + if(fan_index < 0) + { + FAN_DEBUG("Get fan index failed.\n"); + ERROR_RETURN_NA(-1); + } + + retval = cb_func->get_hw_version(fan_index, buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { + return retval; + } +} + +static ssize_t s3ip_get_num_motors(struct kobject *kobj, struct fan_attribute *attr, char *buf) +{ +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", MAX_MOTOR_NUM); +#else + return sprintf(buf, "%d\n", MAX_MOTOR_NUM); +#endif + +} + +static ssize_t s3ip_get_status(struct kobject *kobj, struct fan_attribute *attr, char *buf) +{ + int fan_index; + unsigned int status=0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + fan_index = get_fan_index(kobj); + if(fan_index < 0) + { + FAN_DEBUG("Get fan index failed.\n"); + ERROR_RETURN_NA(-1); + } + + status = cb_func->get_status(fan_index); + if(status < 0) + { + ERROR_RETURN_NA(status); + } +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", status); +#else + return sprintf(buf, "%d\n", status); +#endif + +} + +ssize_t s3ip_get_led_status(struct kobject *kobj, struct fan_attribute *attr, char *buf) +{ + int retval=0; + unsigned int val; + int fan_index; + + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + fan_index = get_fan_index(kobj); + if(fan_index < 0) + { + FAN_DEBUG("Get fan index failed.\n"); + ERROR_RETURN_NA(-1); + } + + retval = cb_func->get_led_status(fan_index, &val); + if(retval < 0) + { + FAN_DEBUG("Get fan%d led status failed.\n", fan_index); + ERROR_RETURN_NA(-1); + } +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", val); +#else + return sprintf(buf, "%d\n", val); +#endif + +} +EXPORT_SYMBOL_GPL(s3ip_get_led_status); + +static ssize_t s3ip_set_led_status(struct kobject *kobj, struct fan_attribute *attr, const char *buf, size_t count) +{ + int retval=0; + int fan_index; + int led_status; + + if(cb_func == NULL) + { + return -EIO; + } + + fan_index = get_fan_index(kobj); + if(fan_index < 0) + { + FAN_DEBUG("Get fan index failed.\n"); + return -ENXIO; + } + + retval = kstrtoint(buf, 10, &led_status); + + if(retval == 0) + { + FAN_DEBUG("led_status:%d \n", led_status); + } + else + { + FAN_DEBUG("Error:%d, led_status:%s \n", retval, buf); + return -EIO; + } + + retval = cb_func->set_led_status(fan_index, led_status); + if(retval != 0) + { + FAN_DEBUG("Set fan %d led_status failed.\n", fan_index); + return -EIO; + } + + return count; +} + +static ssize_t s3ip_get_speed(struct kobject *kobj, struct fan_attribute *attr, char *buf) +{ + int retval=0; + int fan_index, motor_index; + unsigned int speed; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + fan_index = get_fan_index_from_motor_kobj(kobj); + if(fan_index < 0) + { + FAN_DEBUG("Get fan index failed.\n"); + ERROR_RETURN_NA(-EINVAL); + } + + motor_index = get_motor_index(kobj); + if(motor_index < 0) + { + FAN_DEBUG("Get motor index failed.\n"); + ERROR_RETURN_NA(-1); + } + + retval = cb_func->get_speed(fan_index, motor_index-1, &speed); + if(retval < 0) + { + FAN_DEBUG("Get fan%d motor%d speed failed.\n", fan_index, motor_index); + ERROR_RETURN_NA(-1); + } +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", speed); +#else + return sprintf(buf, "%d\n", speed); +#endif + +} + +static ssize_t s3ip_get_speed_tolerance(struct kobject *kobj, struct fan_attribute *attr, char *buf) +{ + int retval=0; + int fan_index, motor_index; + unsigned int speed_tolerance=0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + fan_index = get_fan_index_from_motor_kobj(kobj); + if(fan_index < 0) + { + FAN_DEBUG("Get fan index failed.\n"); + ERROR_RETURN_NA(-1); + } + + motor_index = get_motor_index(kobj); + if(motor_index < 0) + { + FAN_DEBUG("Get motor index failed.\n"); + ERROR_RETURN_NA(-1); + } + + retval = cb_func->get_speed_tolerance(fan_index, motor_index, &speed_tolerance); + if(retval < 0) + { + FAN_DEBUG("Get fan%d motor%d speed tolerance failed.\n", fan_index, motor_index); + ERROR_RETURN_NA(-1); + } +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", speed_tolerance); +#else + return sprintf(buf, "%d\n", speed_tolerance); +#endif + +} + +static ssize_t s3ip_get_speed_max(struct kobject *kobj, struct fan_attribute *attr, char *buf) +{ + int retval=0; + int fan_index, motor_index; + unsigned int speed; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + fan_index = get_fan_index_from_motor_kobj(kobj); + if(fan_index < 0) + { + FAN_DEBUG("Get fan index failed.\n"); + ERROR_RETURN_NA(-EINVAL); + } + + motor_index = get_motor_index(kobj); + if(motor_index < 0) + { + FAN_DEBUG("Get motor index failed.\n"); + ERROR_RETURN_NA(-1); + } + + retval = cb_func->get_speed_max(fan_index, motor_index, &speed); + if(retval < 0) + { + FAN_DEBUG("Get fan%d motor%d speed failed.\n", fan_index, motor_index); + ERROR_RETURN_NA(-1); + } +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", speed); +#else + return sprintf(buf, "%d\n", speed); +#endif + +} + +static ssize_t s3ip_get_speed_min(struct kobject *kobj, struct fan_attribute *attr, char *buf) +{ + int retval=0; + int fan_index, motor_index; + unsigned int speed; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + fan_index = get_fan_index_from_motor_kobj(kobj); + if(fan_index < 0) + { + FAN_DEBUG("Get fan index failed.\n"); + ERROR_RETURN_NA(-EINVAL); + } + + motor_index = get_motor_index(kobj); + if(motor_index < 0) + { + FAN_DEBUG("Get motor index failed.\n"); + ERROR_RETURN_NA(-1); + } + + retval = cb_func->get_speed_min(fan_index, motor_index, &speed); + if(retval < 0) + { + FAN_DEBUG("Get fan%d motor%d speed failed.\n", fan_index, motor_index); + ERROR_RETURN_NA(-1); + } +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", speed); +#else + return sprintf(buf, "%d\n", speed); +#endif + +} + +static ssize_t s3ip_get_speed_target(struct kobject *kobj, struct fan_attribute *attr, char *buf) +{ + int retval=0; + int fan_index, motor_index; + unsigned int speed_target; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + fan_index = get_fan_index_from_motor_kobj(kobj); + if(fan_index < 0) + { + FAN_DEBUG("Get fan index failed.\n"); + ERROR_RETURN_NA(-1); + } + + motor_index = get_motor_index(kobj); + if(motor_index < 0) + { + FAN_DEBUG("Get motor index failed.\n"); + ERROR_RETURN_NA(-1); + } + + retval = cb_func->get_speed_target(fan_index, motor_index, &speed_target); + if(retval < 0) + { + FAN_DEBUG("Get fan%d motor%d speed target failed.\n", fan_index, motor_index); + ERROR_RETURN_NA(-1); + } +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", speed_target); +#else + return sprintf(buf, "%d\n", speed_target); +#endif + +} + +static ssize_t s3ip_get_ratio(struct kobject *kobj, struct fan_attribute *attr, char *buf) +{ + int retval=0; + int fan_index; + int pwm; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + if(strstr(kobject_name(kobj), "fan")) // /sys/switch/fan/fan* + { + fan_index = get_fan_index(kobj); + if(fan_index < 0) + { + FAN_DEBUG("Get fan index failed.\n"); + ERROR_RETURN_NA(-EINVAL); + } + } + else // /sys/switch/fan/fan*/motor/* + { + fan_index = get_fan_index_from_motor_kobj(kobj); + if(fan_index < 0) + { + FAN_DEBUG("Get fan index failed.\n"); + ERROR_RETURN_NA(-EINVAL); + } + } + + retval = cb_func->get_pwm(fan_index, &pwm); + if(retval < 0) + { + FAN_DEBUG("Get fan %d pwm failed.\n", fan_index); + ERROR_RETURN_NA(-1); + } +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", pwm); +#else + return sprintf(buf, "%d\n", pwm); +#endif + +} + +static ssize_t s3ip_set_ratio(struct kobject *kobj, struct fan_attribute *attr, const char *buf, size_t count) +{ + int retval=0; + int fan_index; + int pwm; + + if(cb_func == NULL) + { + return -EIO; + } + + // /sys/switch/fan/fan*/motor/* + fan_index = get_fan_index_from_motor_kobj(kobj); + if(fan_index < 0) + { + FAN_DEBUG("Get fan index failed.\n"); + return -ENXIO; + } + + retval = kstrtoint(buf, 10, &pwm); + if(retval == 0) + { + FAN_DEBUG("pwm:%d \n", pwm); + } + else + { + FAN_DEBUG("Error:%d, pwm:%s \n", retval, buf); + return -EIO; + } + + retval = cb_func->set_pwm(fan_index, pwm); + if(retval < 0) + { + FAN_DEBUG("Get fan %d pwm failed.\n", fan_index); + return -EIO; + } + + return count; +} + +static ssize_t s3ip_get_direction(struct kobject *kobj, struct fan_attribute *attr, char *buf) +{ + int retval=0; + int fan_index; + int direction; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + if(strstr(kobject_name(kobj), "fan")) // /sys/switch/fan/fan* + { + fan_index = get_fan_index(kobj); + if(fan_index < 0) + { + FAN_DEBUG("Get fan index failed.\n"); + ERROR_RETURN_NA(-EINVAL); + } + } + else // /sys/switch/fan/fan*/motor/* + { + fan_index = get_fan_index_from_motor_kobj(kobj); + if(fan_index < 0) + { + FAN_DEBUG("Get fan index failed.\n"); + ERROR_RETURN_NA(-EINVAL); + } + } + + retval = cb_func->get_wind(fan_index, &direction); + if(retval < 0) + { + FAN_DEBUG("Get fan %d direction failed.\n", fan_index); + ERROR_RETURN_NA(-EINVAL); + } +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", direction); +#else + return sprintf(buf, "%d\n", direction); +#endif + +} + +static struct fan_attribute fan_attr[NUM_FAN_ATTR] = { + [DEBUG] = {{.name = "debug", .mode = S_IRUGO | S_IWUSR}, s3ip_debug_help, s3ip_debug}, + [LOGLEVEL] = {{.name = "loglevel", .mode = S_IRUGO | S_IWUSR}, s3ip_get_loglevel, s3ip_set_loglevel}, + [NUM] = {{.name = "num", .mode = S_IRUGO}, s3ip_get_num, NULL}, + [MODEL_NAME] = {{.name = "model_name", .mode = S_IRUGO}, s3ip_get_model_name, NULL}, + [SERIAL_NUMBER] = {{.name = "serial_number", .mode = S_IRUGO}, s3ip_get_serial_number, NULL}, + [VENDOR] = {{.name = "vendor", .mode = S_IRUGO}, s3ip_get_vendor, NULL}, + [PART_NUMBER] = {{.name = "part_number", .mode = S_IRUGO}, s3ip_get_part_number, NULL}, + [HARDWARE_VERSION] = {{.name = "hardware_version", .mode = S_IRUGO}, s3ip_get_hardware_version, NULL}, + [NUM_MOTORS] = {{.name = "num_motors", .mode = S_IRUGO}, s3ip_get_num_motors, NULL}, + [STATUS] = {{.name = "status", .mode = S_IRUGO}, s3ip_get_status, NULL}, + [LED_STATUS] = {{.name = "led_status", .mode = S_IRUGO | S_IWUSR}, s3ip_get_led_status, s3ip_set_led_status}, + [HI_DIRECTION] = {{.name = "direction", .mode = S_IRUGO}, s3ip_get_direction, NULL}, + [HI_RATIO] = {{.name = "ratio", .mode = S_IRUGO | S_IWUSR}, s3ip_get_ratio, s3ip_set_ratio}, +}; + +static struct fan_attribute motor_attr[NUM_MOTOR_ATTR] = { + [SPEED] = {{.name = "speed", .mode = S_IRUGO}, s3ip_get_speed, NULL}, + [SPEED_TOLERANCE] = {{.name = "speed_tolerance", .mode = S_IRUGO}, s3ip_get_speed_tolerance, NULL}, + [SPEED_MAX] = {{.name = "speed_max", .mode = S_IRUGO}, s3ip_get_speed_max, NULL}, + [SPEED_MIN] = {{.name = "speed_min", .mode = S_IRUGO}, s3ip_get_speed_min, NULL}, + [SPEED_TARGET] = {{.name = "speed_target", .mode = S_IRUGO}, s3ip_get_speed_target, NULL}, + [RATIO] = {{.name = "ratio", .mode = S_IRUGO | S_IWUSR}, s3ip_get_ratio, s3ip_set_ratio}, + [DIRECTION] = {{.name = "direction", .mode = S_IRUGO}, s3ip_get_direction, NULL}, +}; + +void s3ip_fan_drivers_register(struct fan_drivers_t *pfunc) +{ + cb_func = pfunc; + + return; +} +EXPORT_SYMBOL_GPL(s3ip_fan_drivers_register); + +void s3ip_fan_drivers_unregister(void) +{ + cb_func = NULL; + + return; +} +EXPORT_SYMBOL_GPL(s3ip_fan_drivers_unregister); + +static int __init switch_fan_init(void) +{ + int err=0; + int retval=0; + int i; + int fan_index, motor_index; + char *fan_index_str; + char *motor_index_str; + + fan_index_str = (char *)kzalloc(6*sizeof(char), GFP_KERNEL); + if (!fan_index_str) + { + FAN_DEBUG( "Fail to alloc fan_index_str memory\n"); + return -ENOMEM; + } + + motor_index_str = (char *)kzalloc(8*sizeof(char), GFP_KERNEL); + if (!motor_index_str) + { + FAN_DEBUG( "Fail to alloc motor_index_str memory\n"); + kfree(motor_index_str); + return -ENOMEM; + } + + /* For s3ip */ + fan_kobj = kobject_create_and_add("fan", switch_kobj); + if(!fan_kobj) + { + FAN_DEBUG( "Failed to create 'fan'\n"); + err = -ENOMEM; + goto sysfs_create_kobject_fan_failed; + } + + for(i=0; i<=NUM; i++) + { + FAN_DEBUG( "sysfs_create_file /fan/%s\n", fan_attr[i].attr.name); + retval = sysfs_create_file(fan_kobj, &fan_attr[i].attr); + if(retval) + { + FAN_DEBUG( "Failed to create file '%s'\n", fan_attr[i].attr.name); + err = -retval; + goto sysfs_create_s3ip_fan_attr_failed; + } + } + + for(fan_index=0; fan_index +#include +#include +#include + +#include "switch_fpga_driver.h" + +#define ERROR_DEBUG(...) printk(KERN_ALERT __VA_ARGS__) + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE (!FALSE) +#endif + +#define SWITCH_S3IP_FPGA_VERSION "0.0.0.1" + +unsigned int loglevel = 0; + +struct fpga_attribute{ + struct attribute attr; + ssize_t (*show)(struct kobject *obj, struct fpga_attribute *attr, char *buf); + ssize_t (*store)(struct kobject *obj, struct fpga_attribute *attr, const char *buf, size_t count); +}; + +struct fpga_drivers_t *cb_func = NULL; + +/* For s3ip */ +extern struct kobject *switch_kobj; +static struct kobject *fpga_kobj; +static struct kobject *fpga_index_kobj[FPGA_TOTAL_NUM]; + +enum fpga_attrs { + DEBUG, + LOGLEVEL, + NUM, + ALIAS, + TYPE, + HW_VERSION, + BOARD_VERSION, + REG_TEST, + NUM_FPGA_ATTR, +}; + +int get_fpga_index(struct kobject *kobj) +{ + int retval=0; + unsigned int fpga_index; + char fpga_index_str[2] = {0}; +#ifdef C11_ANNEX_K + if(memcpy_s(fpga_index_str, 2, (kobject_name(kobj)+4), 1) != 0) + { + return -ENOMEM; + } +#else + memcpy(fpga_index_str, (kobject_name(kobj)+4), 1); +#endif + retval = kstrtoint(fpga_index_str, 10, &fpga_index); + if(retval == 0) + { + FPGA_DEBUG("fpag_index:%d \n", fpga_index); + } + else + { + FPGA_DEBUG("Error:%d, fpga_index:%s \n", retval, fpga_index_str); + return -1; + } + + return fpga_index; +} + + +static ssize_t s3ip_debug_help(struct kobject *kobj, struct fpga_attribute *attr, char *buf) +{ + int retval=0; + + if((cb_func == NULL)||(cb_func->debug_help == NULL)) + { + ERROR_RETURN_NA(-1); + } + + retval = cb_func->debug_help(buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { + return retval; + } +} + +static ssize_t s3ip_debug(struct kobject *kobj, struct fpga_attribute *attr, const char *buf, size_t count) +{ + return count; +} + +static ssize_t s3ip_get_loglevel(struct kobject *kobj, struct fpga_attribute *attr, char *buf) +{ +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", loglevel); +#else + return sprintf(buf, "%d\n", loglevel); +#endif +} + +static ssize_t s3ip_set_loglevel(struct kobject *kobj, struct fpga_attribute *attr, const char *buf, size_t count) +{ + int retval=0; + long lev; + + if((cb_func == NULL)||(cb_func->set_loglevel == NULL)) + return -1; + + retval = kstrtol(buf, 0, &lev); + if(retval == 0) + { + FPGA_DEBUG("lev:%ld \n", lev); + } + else + { + FPGA_DEBUG("Error:%d, lev:%s \n", retval, buf); + return -1; + } + + loglevel = (unsigned int)lev; + + cb_func->set_loglevel(lev); + + return count; +} + +static ssize_t s3ip_get_num(struct kobject *kobj, struct fpga_attribute *attr, char *buf) +{ +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", FPGA_TOTAL_NUM); +#else + return sprintf(buf, "%d\n", FPGA_TOTAL_NUM); +#endif +} + +static ssize_t s3ip_get_alias(struct kobject *kobj, struct fpga_attribute *attr, char *buf) +{ + int retval=0; + int fpga_index; + + if((cb_func == NULL)||(cb_func->get_alias == NULL)) + { + ERROR_RETURN_NA(-1); + } + + fpga_index = get_fpga_index(kobj); + if(fpga_index < 0) + { + FPGA_DEBUG("Get fpga index failed.\n"); + ERROR_RETURN_NA(-1); + } + + retval = cb_func->get_alias(fpga_index, buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { + return retval; + } +} + +static ssize_t s3ip_get_type(struct kobject *kobj, struct fpga_attribute *attr, char *buf) +{ + int retval=0; + int fpga_index; + + if((cb_func == NULL)||(cb_func->get_type == NULL)) + { + ERROR_RETURN_NA(-1); + } + + fpga_index = get_fpga_index(kobj); + if(fpga_index < 0) + { + FPGA_DEBUG("Get fpga index failed.\n"); + ERROR_RETURN_NA(-1); + } + + retval = cb_func->get_type(fpga_index, buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { + return retval; + } +} + +static ssize_t s3ip_get_hw_version(struct kobject *kobj, struct fpga_attribute *attr, char *buf) +{ + int retval=0; + int fpga_index; + + if((cb_func == NULL)||(cb_func->get_hw_version == NULL)) + { + ERROR_RETURN_NA(-1); + } + + fpga_index = get_fpga_index(kobj); + if(fpga_index < 0) + { + FPGA_DEBUG("Get fpga index failed.\n"); + ERROR_RETURN_NA(-1); + } + + retval = cb_func->get_hw_version(fpga_index, buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { + return retval; + } +} + +static ssize_t s3ip_get_board_version(struct kobject *kobj, struct fpga_attribute *attr, char *buf) +{ + int retval=0; + int fpga_index; + + if((cb_func == NULL)||(cb_func->get_board_version == NULL)) + { + ERROR_RETURN_NA(-1); + } + + fpga_index = get_fpga_index(kobj); + if(fpga_index < 0) + { + FPGA_DEBUG("Get fpga index failed.\n"); + ERROR_RETURN_NA(-1); + } + + retval = cb_func->get_board_version(fpga_index, buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { + return retval; + } +} + +static ssize_t s3ip_get_reg_test(struct kobject *kobj, struct fpga_attribute *attr, char *buf) +{ + int retval=0; + int fpga_index; + + if((cb_func == NULL)||(cb_func->get_reg_test == NULL)) + { + ERROR_RETURN_NA(-1); + } + + fpga_index = get_fpga_index(kobj); + if(fpga_index < 0) + { + FPGA_DEBUG("Get fpga index failed.\n"); + ERROR_RETURN_NA(-1); + } + + retval = cb_func->get_reg_test(fpga_index, buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { + return retval; + } +} + +static ssize_t s3ip_set_reg_test(struct kobject *kobj, struct fpga_attribute *attr, const char *buf, size_t count) +{ + int retval=0; + int fpga_index; + long data; + + if((cb_func == NULL)||(cb_func->set_reg_test == NULL)) + { + return -EIO; + } + + fpga_index = get_fpga_index(kobj); + if(fpga_index < 0) + { + FPGA_DEBUG("Get fpga index failed.\n"); + return -EINVAL; + } + + retval = kstrtol(buf, 0, &data); + if(retval == 0) + { + FPGA_DEBUG("fpga%d reg_test data:%ld \n", fpga_index, data); + } + else + { + FPGA_DEBUG("Error:%d, fpga%d reg_test data:%s\n", retval, fpga_index, buf); + return -EIO; + } + + retval = cb_func->set_reg_test(fpga_index, data); + if(retval < 0) + { + FPGA_DEBUG("Set fpga%d reg_test failed.\n", fpga_index); + return -EIO; + } + + return count; +} + +static struct fpga_attribute fpga_attr[NUM_FPGA_ATTR] = { + [DEBUG] = {{.name = "debug", .mode = S_IRUGO | S_IWUSR}, s3ip_debug_help, s3ip_debug}, + [LOGLEVEL] = {{.name = "loglevel", .mode = S_IRUGO | S_IWUSR}, s3ip_get_loglevel, s3ip_set_loglevel}, + [NUM] = {{.name = "num", .mode = S_IRUGO}, s3ip_get_num, NULL}, + [ALIAS] = {{.name = "alias", .mode = S_IRUGO}, s3ip_get_alias, NULL}, + [TYPE] = {{.name = "type", .mode = S_IRUGO}, s3ip_get_type, NULL}, + [HW_VERSION] = {{.name = "firmware_version", .mode = S_IRUGO}, s3ip_get_hw_version, NULL}, + [BOARD_VERSION] = {{.name = "board_version", .mode = S_IRUGO}, s3ip_get_board_version, NULL}, + [REG_TEST] = {{.name = "reg_test", .mode = S_IRUGO | S_IWUSR}, s3ip_get_reg_test, s3ip_set_reg_test}, +}; + + + +void s3ip_fpga_drivers_register(struct fpga_drivers_t *pfunc) +{ + cb_func = pfunc; + return; +} +EXPORT_SYMBOL_GPL(s3ip_fpga_drivers_register); + +void s3ip_fpga_drivers_unregister(void) +{ + cb_func = NULL; + return; +} +EXPORT_SYMBOL_GPL(s3ip_fpga_drivers_unregister); + +static int __init switch_fpga_init(void) +{ + int fpga_index; + char *fpga_index_str; + int retval = 0; + int err=0; + int i; + + fpga_index_str = (char *)kzalloc(7*sizeof(char), GFP_KERNEL); + if(!fpga_index_str) + { + FPGA_DEBUG("Fail to alloc fpga_index_str memory\n"); + return -ENOMEM; + } + + /* For s3ip */ + fpga_kobj = kobject_create_and_add("fpga", switch_kobj); + if(!fpga_kobj) + { + FPGA_DEBUG("Failed to create 'fpga_kobj'\n"); + err = -ENOMEM; + goto sysfs_create_kobject_fpga_failed; + } + + for(i = DEBUG; i <= NUM; i++) + { + FPGA_DEBUG("sysfs_create_file /fpga/%s\n", fpga_attr[i].attr.name); + retval = sysfs_create_file(fpga_kobj, &fpga_attr[i].attr); + if(retval) + { + printk(KERN_ERR "Failed to create file '%s'\n", fpga_attr[i].attr.name); + err = -retval; + goto sysfs_create_s3ip_fpga_attr_failed; + } + } + + for(fpga_index = 0; fpga_index < FPGA_TOTAL_NUM; fpga_index++) + { +#ifdef C11_ANNEX_K + if(sprintf_s(fpga_index_str, 7, "fpga%d", fpga_index+1) < 0) +#else + if(sprintf(fpga_index_str, "fpga%d", fpga_index+1) < 0) +#endif + { + err = -ENOMEM; + goto sysfs_create_kobject_fpga_index_failed; + } + + fpga_index_kobj[fpga_index] = kobject_create_and_add(fpga_index_str, fpga_kobj); + if(!fpga_index_kobj[fpga_index]) + { + FPGA_DEBUG("Failed to create 'fpga%d'\n", fpga_index+1); + err = -ENOMEM; + goto sysfs_create_kobject_fpga_index_failed; + } + + for(i = ALIAS; i < NUM_FPGA_ATTR; i++) + { + FPGA_DEBUG( "sysfs_create_file /fpga/fpga%d/%s\n", fpga_index+1, fpga_attr[i].attr.name); + retval = sysfs_create_file(fpga_index_kobj[fpga_index], &fpga_attr[i].attr); + if(retval) + { + printk(KERN_ERR "Failed to create file '%s'\n", fpga_attr[i].attr.name); + err = -retval; + goto sysfs_create_s3ip_fpga_index_attr_failed; + } + } + } + + return 0; + +sysfs_create_s3ip_fpga_index_attr_failed: +sysfs_create_kobject_fpga_index_failed: + for(fpga_index = 0; fpga_index < FPGA_TOTAL_NUM; fpga_index++) + { + if(fpga_index_kobj[fpga_index]) + { + for(i = ALIAS; i < NUM_FPGA_ATTR; i++) + sysfs_remove_file(fpga_index_kobj[fpga_index], &fpga_attr[i].attr); + + kobject_put(fpga_index_kobj[fpga_index]); + fpga_index_kobj[fpga_index] = NULL; + } + } + +sysfs_create_s3ip_fpga_attr_failed: + for(i = DEBUG; i <= NUM; i++) + sysfs_remove_file(fpga_kobj, &fpga_attr[i].attr); + + kobject_put(fpga_kobj); + fpga_kobj = NULL; + +sysfs_create_kobject_fpga_failed: + kfree(fpga_index_str); + + return err; +} + +static void __exit switch_fpga_exit(void) +{ + int i; + int fpga_index; + + /* For s3ip */ + for(i = DEBUG; i <= NUM; i++) + sysfs_remove_file(fpga_kobj, &fpga_attr[i].attr); + + for(fpga_index = 0; fpga_index < FPGA_TOTAL_NUM; fpga_index++) + { + if(fpga_index_kobj[fpga_index]) + { + for(i = ALIAS; i < NUM_FPGA_ATTR; i++) + sysfs_remove_file(fpga_index_kobj[fpga_index], &fpga_attr[i].attr); + + kobject_put(fpga_index_kobj[fpga_index]); + fpga_index_kobj[fpga_index] = NULL; + } + } + + if(fpga_kobj) + { + kobject_put(fpga_kobj); + fpga_kobj = NULL; + } + + cb_func = NULL; + + return; +} + +MODULE_DESCRIPTION("Switch S3IP fpga Driver"); +MODULE_VERSION(SWITCH_S3IP_FPGA_VERSION); +MODULE_LICENSE("GPL"); + +module_init(switch_fpga_init); +module_exit(switch_fpga_exit); diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/s3ip/switch_s3ip_psu.c b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/s3ip/switch_s3ip_psu.c new file mode 100644 index 0000000000..327457821f --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/s3ip/switch_s3ip_psu.c @@ -0,0 +1,1539 @@ +#include +#include +#include +#include + +#include "switch_psu_driver.h" +#include "switch_led_driver.h" + +#define SWITCH_S3IP_PSU_VERSION "0.0.0.1" + +unsigned int loglevel = 0; + +struct psu_attribute{ + struct attribute attr; + ssize_t (*show)(struct kobject *obj, struct psu_attribute *attr, char *buf); + ssize_t (*store)(struct kobject *obj, struct psu_attribute *attr, const char *buf, size_t count); +}; + +struct psu_drivers_t *cb_func = NULL; + + +/* For s3ip */ +extern struct kobject *switch_kobj; +static struct kobject *psu_kobj; +static struct kobject *psu_index_kobj[PSU_TOTAL_NUM]; +static struct kobject *temp_index_kobj[PSU_TOTAL_NUM][PSU_TOTAL_TEMP_NUM]; + +enum psu_attrs { + DEBUG, + LOGLEVEL, + PSU_NUM, + PSU_MODEL_NAME, + PSU_SERIAL_NUMBER, + PSU_DATE, + PSU_VENDOR, + PSU_HARDWARE_VERSION, + PSU_ALARM, + PSU_ALARM_THRESHOLD_CURR, + PSU_ALARM_THRESHOLD_VOL, + PSU_PART_NUMBER, + PSU_MAX_OUTPUT_POWER, + PSU_IN_CURR, + PSU_IN_VOL, + PSU_IN_POWER, + PSU_IN_STATUS, + PSU_OUT_CURR, + PSU_OUT_VOL, + PSU_OUT_POWER, + PSU_OUT_STATUS, + PSU_PRESENT, + PSU_LED_STATUS, + PSU_TYPE, + PSU_NUM_TEMP_SENSORS, + PSU_FAN_SPEED, + PSU_FAN_RATIO, + DIRECTION, + NUM_PSU_ATTR, +}; + +enum temp_attrs { + TEMP_ALIAS, + TEMP_TYPE, + TEMP_MAX, + TEMP_MAX_HYST, + TEMP_MIN, + TEMP_INPUT, + NUM_TEMP_ATTR, +}; + +int get_psu_index(struct kobject *kobj) +{ + int retval=0; + unsigned int psu_index; + char psu_index_str[2] = {0}; + +#ifdef C11_ANNEX_K + if(memcpy_s(psu_index_str, 2, (kobject_name(kobj)+sizeof(PSU_NAME_STRING)-1), 2) != 0) + { + return -ENOMEM; + } +#else + memcpy(psu_index_str, (kobject_name(kobj)+sizeof(PSU_NAME_STRING)-1), 2); +#endif + + retval = kstrtoint(psu_index_str, 10, &psu_index); + if(retval == 0) + { + PSU_DEBUG("psu_index:%d \n", psu_index); + } + else + { + PSU_ERR("Error:%d, psu_index:%s \n", retval, psu_index_str); + return -EINVAL; + } + + if(!(psu_index <= PSU_TOTAL_NUM)) + return -EINVAL; + + return psu_index; +} + +int get_psu_temp_index(struct kobject *kobj) +{ + int retval=0; + unsigned int psu_temp_index; + char psu_temp_index_str[2] = {0}; + +#ifdef C11_ANNEX_K + if(memcpy_s(psu_temp_index_str, 2, (kobject_name(kobj)+sizeof(TEMP_NAME_STRING)-1), 2) != 0) + { + return -ENOMEM; + } +#else + memcpy(psu_temp_index_str, (kobject_name(kobj)+sizeof(TEMP_NAME_STRING)-1), 2); +#endif + + retval = kstrtoint(psu_temp_index_str, 10, &psu_temp_index); + if(retval == 0) + { + PSU_DEBUG("psu_temp_index:%d \n", psu_temp_index); + } + else + { + PSU_ERR("Error:%d, psu_temp_index:%s \n", retval, psu_temp_index_str); + return -EINVAL; + } + + if((psu_temp_index <= 0) && (psu_temp_index > PSU_TOTAL_TEMP_NUM)) + return -EINVAL; + + return psu_temp_index; +} + +static ssize_t s3ip_debug_help(struct kobject *kobj, struct psu_attribute *attr, char *buf) +{ + int retval=0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + retval = cb_func->debug_help(buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { + return retval; + } +} + +static ssize_t s3ip_debug(struct kobject *kobj, struct psu_attribute *attr, const char *buf, size_t count) +{ + return count; +} + +static ssize_t s3ip_get_loglevel(struct kobject *kobj, struct psu_attribute *attr, char *buf) +{ +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", loglevel); +#else + return sprintf(buf, "%d\n", loglevel); +#endif +} + +static ssize_t s3ip_set_loglevel(struct kobject *kobj, struct psu_attribute *attr, const char *buf, size_t count) +{ + int retval=0; + long lev; + + if(cb_func == NULL) + return -1; + + retval = kstrtol(buf, 0, &lev); + if(retval == 0) + { + PSU_DEBUG("lev:%ld \n", lev); + } + else + { + PSU_ERR("Error:%d, lev:%s \n", retval, buf); + return -1; + } + + loglevel = (unsigned int)lev; + + cb_func->set_loglevel(lev); + + return count; +} + +static ssize_t s3ip_psu_get_num(struct kobject *kobj, struct psu_attribute *attr, char *buf) +{ +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", PSU_TOTAL_NUM); +#else + return sprintf(buf, "%d\n", PSU_TOTAL_NUM); +#endif + +} + +static ssize_t s3ip_psu_get_direction(struct kobject *kobj, struct psu_attribute *attr, char *buf) +{ +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", PSU_FAN_DIRECT_B2F); +#else + return sprintf(buf, "%d\n", PSU_FAN_DIRECT_B2F); +#endif + +} + +static ssize_t s3ip_psu_get_psu_model_name(struct kobject *kobj, struct psu_attribute *attr, char *buf) +{ + int retval=0; + char str_buff[PSU_MAX_STR_BUFF_LENGTH] = {0}; + int psu_index = 0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + psu_index = get_psu_index(kobj); + if(psu_index < 0) + { + PSU_ERR("Get psu index failed.\n"); + ERROR_RETURN_NA(-EINVAL); + } + + retval = cb_func->get_mfr_info(psu_index, PMBUS_MFR_MODEL, str_buff); + + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%s\n", str_buff); +#else + return sprintf(buf, "%s\n", str_buff); +#endif + + } +} + +static ssize_t s3ip_psu_get_serial_number(struct kobject *kobj, struct psu_attribute *attr, char *buf) +{ + int retval=0; + char str_buff[PSU_MAX_STR_BUFF_LENGTH] = {0}; + int psu_index = 0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + psu_index = get_psu_index(kobj); + if(psu_index < 0) + { + PSU_ERR("Get psu index failed.\n"); + ERROR_RETURN_NA(-EINVAL); + } + + retval = cb_func->get_mfr_info(psu_index, PMBUS_MFR_SERIAL, str_buff); + + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%s\n", str_buff); +#else + return sprintf(buf, "%s\n", str_buff); +#endif + + } +} + +static ssize_t s3ip_psu_get_date(struct kobject *kobj, struct psu_attribute *attr, char *buf) +{ + int retval=0; + char str_buff[PSU_MAX_STR_BUFF_LENGTH] = {0}; + int psu_index = 0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + psu_index = get_psu_index(kobj); + if(psu_index < 0) + { + PSU_ERR("Get psu index failed.\n"); + ERROR_RETURN_NA(-EINVAL); + } + + retval = cb_func->get_mfr_info(psu_index, PMBUS_MFR_DATE, str_buff); + + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%s\n", str_buff); +#else + return sprintf(buf, "%s\n", str_buff); +#endif + + } +} + +static ssize_t s3ip_psu_get_vendor(struct kobject *kobj, struct psu_attribute *attr, char *buf) +{ + int retval=0; + char str_buff[PSU_MAX_STR_BUFF_LENGTH] = {0}; + int psu_index = 0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + psu_index = get_psu_index(kobj); + if(psu_index < 0) + { + PSU_ERR("Get psu index failed.\n"); + ERROR_RETURN_NA(-EINVAL); + } + + retval = cb_func->get_mfr_info(psu_index, PMBUS_MFR_ID, str_buff); + + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%s\n", str_buff); +#else + return sprintf(buf, "%s\n", str_buff); +#endif + + } +} + +static ssize_t s3ip_psu_get_hardware_version(struct kobject *kobj, struct psu_attribute *attr, char *buf) +{ + int retval=0; + char str_buff[PSU_MAX_STR_BUFF_LENGTH] = {0}; + int psu_index = 0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + psu_index = get_psu_index(kobj); + if(psu_index < 0) + { + PSU_ERR("Get psu index failed.\n"); + ERROR_RETURN_NA(-EINVAL); + } + + retval = cb_func->get_mfr_info(psu_index, PMBUS_MFR_REVISION, str_buff); + + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%s\n", str_buff); +#else + return sprintf(buf, "%s\n", str_buff); +#endif + + } +} + +static ssize_t s3ip_psu_get_alarm(struct kobject *kobj, struct psu_attribute *attr, char *buf) +{ + int psu_index = 0; + unsigned int alarm_value = 0; + + int ret; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + psu_index = get_psu_index(kobj); + if(psu_index < 0) + { + PSU_ERR("Get psu index failed.\n"); + ERROR_RETURN_NA(-EINVAL); + } + + ret = cb_func->get_psu_pok_alarm(psu_index-1, &alarm_value); + if(ret < 0) + { + ERROR_RETURN_NA(ret); + } +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "0x%x\n", alarm_value); +#else + return sprintf(buf, "0x%x\n", alarm_value); +#endif + +} + +static ssize_t s3ip_psu_get_alarm_threshold_curr(struct kobject *kobj, struct psu_attribute *attr, char *buf) +{ + int retval=0; + long threshold_curr = 0; + int psu_index = 0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + psu_index = get_psu_index(kobj); + if(psu_index < 0) + { + PSU_ERR("Get psu index failed.\n"); + ERROR_RETURN_NA(-EINVAL); + } + + retval = cb_func->get_alarm_threshold_curr(psu_index, &threshold_curr); + + if(retval == false) + { + ERROR_RETURN_NA(retval); + } + else + { +#ifdef C11_ANNEX_K + return sprintf_s(buf, PSU_MAX_STR_BUFF_LENGTH, "%ld\n", threshold_curr); +#else + return sprintf(buf, "%ld\n", threshold_curr); +#endif + + } +} + +static ssize_t s3ip_psu_get_alarm_threshold_vol(struct kobject *kobj, struct psu_attribute *attr, char *buf) +{ + int retval=0; + long threshold_vol = 0; + int psu_index = 0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + psu_index = get_psu_index(kobj); + if(psu_index < 0) + { + PSU_ERR("Get psu index failed.\n"); + ERROR_RETURN_NA(-EINVAL); + } + + retval = cb_func->get_alarm_threshold_vol(psu_index, &threshold_vol); + + if(retval == false) + { + ERROR_RETURN_NA(retval); + } + else + { +#ifdef C11_ANNEX_K + return sprintf_s(buf, PSU_MAX_STR_BUFF_LENGTH, "%ld\n", threshold_vol); +#else + return sprintf(buf, "%ld\n", threshold_vol); +#endif + + } +} + +static ssize_t s3ip_psu_get_part_number(struct kobject *kobj, struct psu_attribute *attr, char *buf) +{ + int retval=0; + char str_buff[PSU_MAX_STR_BUFF_LENGTH] = {0}; + int psu_index = 0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + psu_index = get_psu_index(kobj); + if(psu_index < 0) + { + PSU_ERR("Get psu index failed.\n"); + ERROR_RETURN_NA(-EINVAL); + } + + retval = cb_func->get_mfr_info(psu_index, PMBUS_PART_NUM, str_buff); + + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%s\n", str_buff); +#else + return sprintf(buf, "%s\n", str_buff); +#endif + + } +} + +static ssize_t s3ip_psu_get_max_output_power(struct kobject *kobj, struct psu_attribute *attr, char *buf) +{ + int retval=0; + long max_output_power = 0; + int psu_index = 0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + psu_index = get_psu_index(kobj); + if(psu_index < 0) + { + PSU_ERR("Get psu index failed.\n"); + ERROR_RETURN_NA(-EINVAL); + } + + retval = cb_func->get_max_output_power(psu_index, &max_output_power); + + if(retval == false) + { + ERROR_RETURN_NA(retval); + } + else + { +#ifdef C11_ANNEX_K + return sprintf_s(buf, PSU_MAX_STR_BUFF_LENGTH, "%ld\n", max_output_power); +#else + return sprintf(buf, "%ld\n", max_output_power); +#endif + + } +} + +static ssize_t s3ip_psu_get_in_curr(struct kobject *kobj, struct psu_attribute *attr, char *buf) +{ + int retval=0; + char str_buff[PSU_MAX_STR_BUFF_LENGTH] = {0}; + int psu_index = 0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + psu_index = get_psu_index(kobj); + if(psu_index < 0) + { + PSU_ERR("Get psu index failed.\n"); + ERROR_RETURN_NA(-EINVAL); + } + + retval = cb_func->get_mfr_info(psu_index, PMBUS_READ_IIN, str_buff); + + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { +#ifdef C11_ANNEX_K + return sprintf_s(buf, PSU_MAX_STR_BUFF_LENGTH, "%s\n", str_buff); +#else + return sprintf(buf, "%s\n", str_buff); +#endif + + } +} + +static ssize_t s3ip_psu_get_in_vol(struct kobject *kobj, struct psu_attribute *attr, char *buf) +{ + int retval=0; + char str_buff[PSU_MAX_STR_BUFF_LENGTH] = {0}; + int psu_index = 0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + psu_index = get_psu_index(kobj); + if(psu_index < 0) + { + PSU_ERR("Get psu index failed.\n"); + ERROR_RETURN_NA(-EINVAL); + } + + retval = cb_func->get_mfr_info(psu_index, PMBUS_READ_VIN, str_buff); + + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { +#ifdef C11_ANNEX_K + return sprintf_s(buf, PSU_MAX_STR_BUFF_LENGTH, "%s\n", str_buff); +#else + return sprintf(buf, "%s\n", str_buff); +#endif + + } +} + +static ssize_t s3ip_psu_get_in_power(struct kobject *kobj, struct psu_attribute *attr, char *buf) +{ + int retval=0; + char str_buff[PSU_MAX_STR_BUFF_LENGTH] = {0}; + int psu_index = 0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + psu_index = get_psu_index(kobj); + if(psu_index < 0) + { + PSU_ERR("Get psu index failed.\n"); + ERROR_RETURN_NA(-EINVAL); + } + + retval = cb_func->get_mfr_info(psu_index, PMBUS_READ_PIN, str_buff); + + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { +#ifdef C11_ANNEX_K + return sprintf_s(buf, PSU_MAX_STR_BUFF_LENGTH, "%s\n", str_buff); +#else + return sprintf(buf, "%s\n", str_buff); +#endif + + } +} + +static ssize_t s3ip_psu_get_in_status(struct kobject *kobj, struct psu_attribute *attr, char *buf) +{ + int retval=0; + int psu_index = 0; + unsigned int value = 0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + psu_index = get_psu_index(kobj); + if(psu_index < 0) + { + PSU_ERR("Get psu index failed.\n"); + ERROR_RETURN_NA(-EINVAL); + } + + retval = cb_func->get_psu_out_pok(psu_index-1, &value); + + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } +#ifdef C11_ANNEX_K + /* IPOK return 1; else return 0 */ + return sprintf_s(buf, PAGE_SIZE, "%d\n", value ? 1 : 0); +#else + return sprintf(buf, "%d\n", value ? 1 : 0); +#endif + +} + +static ssize_t s3ip_psu_get_out_curr(struct kobject *kobj, struct psu_attribute *attr, char *buf) +{ + int retval=0; + char str_buff[PSU_MAX_STR_BUFF_LENGTH] = {0}; + int psu_index = 0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + psu_index = get_psu_index(kobj); + if(psu_index < 0) + { + PSU_ERR("Get psu index failed.\n"); + ERROR_RETURN_NA(-EINVAL); + } + + retval = cb_func->get_mfr_info(psu_index, PMBUS_READ_IOUT, str_buff); + + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { +#ifdef C11_ANNEX_K + return sprintf_s(buf, PSU_MAX_STR_BUFF_LENGTH, "%s\n", str_buff); +#else + return sprintf(buf, "%s\n", str_buff); +#endif + + } +} + +static ssize_t s3ip_psu_get_out_vol(struct kobject *kobj, struct psu_attribute *attr, char *buf) +{ + int retval=0; + char str_buff[PSU_MAX_STR_BUFF_LENGTH] = {0}; + int psu_index = 0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + psu_index = get_psu_index(kobj); + if(psu_index < 0) + { + PSU_ERR("Get psu index failed.\n"); + ERROR_RETURN_NA(-EINVAL); + } + + retval = cb_func->get_mfr_info(psu_index, PMBUS_READ_VOUT, str_buff); + + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { +#ifdef C11_ANNEX_K + return sprintf_s(buf, PSU_MAX_STR_BUFF_LENGTH, "%s\n", str_buff); +#else + return sprintf(buf, "%s\n", str_buff); +#endif + + } +} + +static ssize_t s3ip_psu_get_out_power(struct kobject *kobj, struct psu_attribute *attr, char *buf) +{ + int retval=0; + char str_buff[PSU_MAX_STR_BUFF_LENGTH] = {0}; + int psu_index = 0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + psu_index = get_psu_index(kobj); + if(psu_index < 0) + { + PSU_ERR("Get psu index failed.\n"); + ERROR_RETURN_NA(-EINVAL); + } + + retval = cb_func->get_mfr_info(psu_index, PMBUS_READ_POUT, str_buff); + + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { +#ifdef C11_ANNEX_K + return sprintf_s(buf, PSU_MAX_STR_BUFF_LENGTH, "%s\n", str_buff); +#else + return sprintf(buf, "%s\n", str_buff); +#endif + + } +} + +static ssize_t s3ip_psu_get_out_status(struct kobject *kobj, struct psu_attribute *attr, char *buf) +{ + int retval=0; + int psu_index = 0; + unsigned int value = 0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + psu_index = get_psu_index(kobj); + if(psu_index < 0) + { + PSU_ERR("Get psu index failed.\n"); + ERROR_RETURN_NA(-EINVAL); + } + + retval = cb_func->get_psu_out_pok(psu_index-1, &value); + + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } +#ifdef C11_ANNEX_K + /* OPOK return 1; else return 0 */ + return sprintf_s(buf, PAGE_SIZE, "%d\n", value ? 1 : 0); +#else + return sprintf(buf, "%d\n", value ? 1 : 0); +#endif + +} + +static ssize_t s3ip_psu_get_present(struct kobject *kobj, struct psu_attribute *attr, char *buf) +{ + int retval=0; + int psu_index = 0; + unsigned int value = 0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + psu_index = get_psu_index(kobj); + if(psu_index < 0) + { + PSU_ERR("Get psu index failed.\n"); + ERROR_RETURN_NA(-EINVAL); + } + + retval = cb_func->get_present(psu_index-1, &value); + + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } +#ifdef C11_ANNEX_K + /* Present return 1; else return 0 */ + return sprintf_s(buf, PAGE_SIZE, "%d\n", value ? 1 : 0); +#else + return sprintf(buf, "%d\n", value ? 1 : 0); +#endif + +} + +static ssize_t s3ip_psu_get_led_status(struct kobject *kobj, struct psu_attribute *attr, char *buf) +{ + ERROR_RETURN_NA(-EINVAL); +} + +static ssize_t s3ip_psu_get_type(struct kobject *kobj, struct psu_attribute *attr, char *buf) +{ +#ifdef C11_ANNEX_K + // PSU only support AC, alway return 0: AC. + return sprintf_s(buf, PAGE_SIZE, "0\n"); +#else + return sprintf(buf, "0\n"); +#endif + +} + +static ssize_t s3ip_psu_get_num_temp_sensors(struct kobject *kobj, struct psu_attribute *attr, char *buf) +{ + int psu_index = 0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + psu_index = get_psu_index(kobj); + if(psu_index < 0) + { + PSU_ERR("Get psu index failed.\n"); + ERROR_RETURN_NA(-EINVAL); + } + + return cb_func->get_num_temp_sensors(psu_index, buf); +} + +static ssize_t s3ip_psu_get_fan_speed(struct kobject *kobj, struct psu_attribute *attr, char *buf) +{ + int retval=0; + char str_buff[PSU_MAX_STR_BUFF_LENGTH] = {0}; + int psu_index = 0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + psu_index = get_psu_index(kobj); + if(psu_index < 0) + { + PSU_ERR("Get psu index failed.\n"); + ERROR_RETURN_NA(-EINVAL); + } + + retval = cb_func->get_mfr_info(psu_index, PMBUS_READ_FAN_SPEED_1, str_buff); + + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { +#ifdef C11_ANNEX_K + return sprintf_s(buf, PSU_MAX_STR_BUFF_LENGTH, "%s\n", str_buff); +#else + return sprintf(buf, "%s\n", str_buff); +#endif + + } +} + +static ssize_t s3ip_psu_get_fan_ratio(struct kobject *kobj, struct psu_attribute *attr, char *buf) +{ + int retval=0; + char str_buff[PSU_MAX_STR_BUFF_LENGTH] = {0}; + int psu_index = 0; + unsigned int fan_speed; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + psu_index = get_psu_index(kobj); + if(psu_index < 0) + { + PSU_ERR("Get psu index failed.\n"); + ERROR_RETURN_NA(-EINVAL); + } + + retval = cb_func->get_mfr_info(psu_index, PMBUS_READ_FAN_SPEED_1, str_buff); + + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { +#ifdef C11_ANNEX_K + if (sscanf_s(str_buff, "%d", &fan_speed) < 0) +#else + if (sscanf(str_buff, "%d", &fan_speed) < 0) +#endif + { + ERROR_RETURN_NA(-EINVAL); + } +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", (fan_speed*100)/PSU_FAN_SPEED_MAX_RPM); +#else + return sprintf(buf, "%d\n", (fan_speed*100)/PSU_FAN_SPEED_MAX_RPM); +#endif + + } +} + +static ssize_t s3ip_psu_set_fan_ratio(struct kobject *kobj, struct psu_attribute *attr, const char *buf, size_t count) +{ + int psu_index = 0; + + if(cb_func == NULL) + { + return -EIO; + } + + psu_index = get_psu_index(kobj); + if(psu_index < 0) + { + PSU_ERR("Get psu index failed.\n"); + return -EINVAL; + } + + /* PSU FAN speed manually control isn't supported. */ + return -EPERM; +} + +static ssize_t s3ip_get_psu_temp_alias(struct kobject *kobj, struct psu_attribute *attr, char *buf) +{ + int psu_index = 0; + int psu_temp_index = 0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + psu_index = get_psu_index(kobj->parent); + if(psu_index < 0) + { + PSU_ERR("Get psu index failed.\n"); + ERROR_RETURN_NA(-EINVAL); + } + + psu_temp_index = get_psu_temp_index(kobj); + if(psu_temp_index < 0) + { + PSU_ERR("Get psu temp index failed.\n"); + ERROR_RETURN_NA(-EINVAL); + } + + return cb_func->get_psu_temp_alias(psu_index, psu_temp_index, buf); +} + +static ssize_t s3ip_get_psu_temp_type(struct kobject *kobj, struct psu_attribute *attr, char *buf) +{ + int psu_index = 0; + int psu_temp_index = 0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + psu_index = get_psu_index(kobj->parent); + if(psu_index < 0) + { + PSU_ERR("Get psu index failed.\n"); + ERROR_RETURN_NA(-EINVAL); + } + + psu_temp_index = get_psu_temp_index(kobj); + if(psu_temp_index < 0) + { + PSU_ERR("Get psu temp index failed.\n"); + ERROR_RETURN_NA(-EINVAL); + } + + return cb_func->get_psu_temp_type(psu_index, psu_temp_index, buf); +} + +static ssize_t s3ip_get_psu_temp_max(struct kobject *kobj, struct psu_attribute *attr, char *buf) +{ + int psu_index = 0; + int psu_temp_index = 0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + psu_index = get_psu_index(kobj->parent); + if(psu_index < 0) + { + PSU_ERR("Get psu index failed.\n"); + ERROR_RETURN_NA(-EINVAL); + } + + psu_temp_index = get_psu_temp_index(kobj); + if(psu_temp_index < 0) + { + PSU_ERR("Get psu temp index failed.\n"); + ERROR_RETURN_NA(-EINVAL); + } + + return cb_func->get_psu_temp_max(psu_index, psu_temp_index, buf); +} + +static ssize_t s3ip_set_psu_temp_max(struct kobject *kobj, struct psu_attribute *attr, const char *buf, size_t count) +{ + int retval=0; + int psu_index; + int psu_temp_index = 0; + int temp_max; + + if(cb_func == NULL) + { + return -EIO; + } + + psu_index = get_psu_index(kobj->parent); + if(psu_index < 0) + { + PSU_ERR("Get psu index failed.\n"); + return -EINVAL; + } + + psu_temp_index = get_psu_temp_index(kobj); + if(psu_temp_index < 0) + { + PSU_ERR("Get psu temp index failed.\n"); + return -EINVAL; + } + + retval = kstrtoint(buf, 10, &temp_max); + if(retval == 0) + { + PSU_DEBUG("temp_max:%d \n", temp_max); + } + else + { + PSU_ERR("Error:%d, temp_max:%d \n", retval, temp_max); + return -EIO; + } + + retval = cb_func->set_psu_temp_max(psu_index, psu_temp_index, temp_max); + if(retval < 0) + { + PSU_ERR("Set psu %d temp %d temp_max failed.\n", psu_index, psu_temp_index); + return -EIO; + } + + return count; +} + +static ssize_t s3ip_get_psu_temp_max_hyst(struct kobject *kobj, struct psu_attribute *attr, char *buf) +{ + int psu_index = 0; + int psu_temp_index = 0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + psu_index = get_psu_index(kobj->parent); + if(psu_index < 0) + { + PSU_ERR("Get psu index failed.\n"); + ERROR_RETURN_NA(-EINVAL); + } + + psu_temp_index = get_psu_temp_index(kobj); + if(psu_temp_index < 0) + { + PSU_ERR("Get psu temp index failed.\n"); + ERROR_RETURN_NA(-EINVAL); + } + + return cb_func->get_psu_temp_max_hyst(psu_index, psu_temp_index, buf); +} + +static ssize_t s3ip_get_psu_temp_min(struct kobject *kobj, struct psu_attribute *attr, char *buf) +{ + int psu_index = 0; + int psu_temp_index = 0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + psu_index = get_psu_index(kobj->parent); + if(psu_index < 0) + { + PSU_ERR("Get psu index failed.\n"); + ERROR_RETURN_NA(-EINVAL); + } + + psu_temp_index = get_psu_temp_index(kobj); + if(psu_temp_index < 0) + { + PSU_ERR("Get psu temp index failed.\n"); + ERROR_RETURN_NA(-EINVAL); + } + + return cb_func->get_psu_temp_min(psu_index, psu_temp_index, buf); +} + +static ssize_t s3ip_set_psu_temp_min(struct kobject *kobj, struct psu_attribute *attr, const char *buf, size_t count) +{ + int retval=0; + int psu_index; + int psu_temp_index = 0; + int temp_min; + + if(cb_func == NULL) + { + return -EIO; + } + + psu_index = get_psu_index(kobj->parent); + if(psu_index < 0) + { + PSU_ERR("Get psu index failed.\n"); + return -EINVAL; + } + + psu_temp_index = get_psu_temp_index(kobj); + if(psu_temp_index < 0) + { + PSU_ERR("Get psu temp index failed.\n"); + return -EINVAL; + } + + retval = kstrtoint(buf, 10, &temp_min); + if(retval == 0) + { + PSU_DEBUG("temp_min:%d \n", temp_min); + } + else + { + PSU_ERR("Error:%d, temp_min:%d \n", retval, temp_min); + return -EIO; + } + + retval = cb_func->set_psu_temp_min(psu_index, psu_temp_index, temp_min); + if(retval < 0) + { + PSU_ERR("Set psu %d temp %d temp_min failed.\n", psu_index, psu_temp_index); + return -EIO; + } + + return count; +} + +static ssize_t s3ip_get_psu_temp_input(struct kobject *kobj, struct psu_attribute *attr, char *buf) +{ + int retval=0; + int psu_index = 0; + int psu_temp_index = 0; + long temp_input = 0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + psu_index = get_psu_index(kobj->parent); + if(psu_index < 0) + { + PSU_ERR("Get psu index failed.\n"); + ERROR_RETURN_NA(-EINVAL); + } + + psu_temp_index = get_psu_temp_index(kobj); + if(psu_temp_index < 0) + { + PSU_ERR("Get psu temp index failed.\n"); + ERROR_RETURN_NA(-EINVAL); + } + + retval = cb_func->get_temp_input(psu_index, psu_temp_index, &temp_input); + if(retval < 0) + { + PSU_ERR("Get temp input failed.\n"); + ERROR_RETURN_NA(-1); + } +#ifdef C11_ANNEX_K + return sprintf_s(buf, PSU_MAX_STR_BUFF_LENGTH, "%s%ld\n", ((temp_input < 0) ? "-":""), abs(temp_input)); +#else + return sprintf(buf, "%s%ld\n", ((temp_input < 0) ? "-":""), abs(temp_input)); +#endif + +} + +static struct psu_attribute psu_attr[NUM_PSU_ATTR] = { + [DEBUG] = {{.name = "debug", .mode = S_IRUGO | S_IWUSR}, s3ip_debug_help, s3ip_debug}, + [LOGLEVEL] = {{.name = "loglevel", .mode = S_IRUGO | S_IWUSR}, s3ip_get_loglevel, s3ip_set_loglevel}, + [PSU_NUM] = {{.name = "num", .mode = S_IRUGO}, s3ip_psu_get_num, NULL}, + [PSU_MODEL_NAME] = {{.name = "model_name", .mode = S_IRUGO}, s3ip_psu_get_psu_model_name, NULL}, + [PSU_SERIAL_NUMBER] = {{.name = "serial_number", .mode = S_IRUGO}, s3ip_psu_get_serial_number, NULL}, + [PSU_DATE] = {{.name = "date", .mode = S_IRUGO}, s3ip_psu_get_date, NULL}, + [PSU_VENDOR] = {{.name = "vendor", .mode = S_IRUGO}, s3ip_psu_get_vendor, NULL}, + [PSU_HARDWARE_VERSION] = {{.name = "hardware_version", .mode = S_IRUGO}, s3ip_psu_get_hardware_version, NULL}, + [PSU_ALARM] = {{.name = "alarm", .mode = S_IRUGO}, s3ip_psu_get_alarm, NULL}, + [PSU_ALARM_THRESHOLD_CURR] = {{.name = "alarm_threshold_curr", .mode = S_IRUGO}, s3ip_psu_get_alarm_threshold_curr, NULL}, + [PSU_ALARM_THRESHOLD_VOL] = {{.name = "alarm_threshold_vol", .mode = S_IRUGO}, s3ip_psu_get_alarm_threshold_vol, NULL}, + [PSU_PART_NUMBER] = {{.name = "part_number", .mode = S_IRUGO}, s3ip_psu_get_part_number, NULL}, + [PSU_MAX_OUTPUT_POWER] = {{.name = "max_output_power", .mode = S_IRUGO}, s3ip_psu_get_max_output_power, NULL}, + [PSU_IN_CURR] = {{.name = "in_curr", .mode = S_IRUGO}, s3ip_psu_get_in_curr, NULL}, + [PSU_IN_VOL] = {{.name = "in_vol", .mode = S_IRUGO}, s3ip_psu_get_in_vol, NULL}, + [PSU_IN_POWER] = {{.name = "in_power", .mode = S_IRUGO}, s3ip_psu_get_in_power, NULL}, + [PSU_IN_STATUS] = {{.name = "in_status", .mode = S_IRUGO}, s3ip_psu_get_in_status, NULL}, + [PSU_OUT_CURR] = {{.name = "out_curr", .mode = S_IRUGO}, s3ip_psu_get_out_curr, NULL}, + [PSU_OUT_VOL] = {{.name = "out_vol", .mode = S_IRUGO}, s3ip_psu_get_out_vol, NULL}, + [PSU_OUT_POWER] = {{.name = "out_power", .mode = S_IRUGO}, s3ip_psu_get_out_power, NULL}, + [PSU_OUT_STATUS] = {{.name = "out_status", .mode = S_IRUGO}, s3ip_psu_get_out_status, NULL}, + [PSU_PRESENT] = {{.name = "present", .mode = S_IRUGO}, s3ip_psu_get_present, NULL}, + [PSU_LED_STATUS] = {{.name = "led_status", .mode = S_IRUGO}, s3ip_psu_get_led_status, NULL}, + [PSU_TYPE] = {{.name = "type", .mode = S_IRUGO}, s3ip_psu_get_type, NULL}, + [PSU_NUM_TEMP_SENSORS] = {{.name = "num_temp_sensors", .mode = S_IRUGO}, s3ip_psu_get_num_temp_sensors, NULL}, + [PSU_FAN_SPEED] = {{.name = "fan_speed", .mode = S_IRUGO}, s3ip_psu_get_fan_speed, NULL}, + [PSU_FAN_RATIO] = {{.name = "fan_ratio", .mode = S_IRUGO | S_IWUSR}, s3ip_psu_get_fan_ratio, s3ip_psu_set_fan_ratio}, + [DIRECTION] = {{.name = "direction", .mode = S_IRUGO}, s3ip_psu_get_direction, NULL}, +}; + +static struct psu_attribute temp_attr[NUM_TEMP_ATTR] = { + [TEMP_ALIAS] = {{.name = "temp_alias", .mode = S_IRUGO}, s3ip_get_psu_temp_alias, NULL}, + [TEMP_TYPE] = {{.name = "temp_type", .mode = S_IRUGO}, s3ip_get_psu_temp_type, NULL}, + [TEMP_MAX] = {{.name = "temp_max", .mode = S_IRUGO | S_IWUSR}, s3ip_get_psu_temp_max, s3ip_set_psu_temp_max}, + [TEMP_MAX_HYST] = {{.name = "temp_max_hyst", .mode = S_IRUGO}, s3ip_get_psu_temp_max_hyst, NULL}, + [TEMP_MIN] = {{.name = "temp_min", .mode = S_IRUGO | S_IWUSR}, s3ip_get_psu_temp_min, s3ip_set_psu_temp_min}, + [TEMP_INPUT] = {{.name = "temp_input", .mode = S_IRUGO}, s3ip_get_psu_temp_input, NULL}, +}; + +void s3ip_psu_drivers_register(struct psu_drivers_t *pfunc) +{ + cb_func = pfunc; + + return; +} +EXPORT_SYMBOL_GPL(s3ip_psu_drivers_register); + +void s3ip_psu_drivers_unregister(void) +{ + cb_func = NULL; + + return; +} +EXPORT_SYMBOL_GPL(s3ip_psu_drivers_unregister); + +static int __init switch_psu_init(void) +{ + int err=0; + int retval=0; + int i; + int psu_index, temp_index; + char *psu_index_str, *temp_index_str; + + psu_index_str = (char *)kzalloc(sizeof(PSU_NAME_STRING)+3, GFP_KERNEL); + if (!psu_index_str) + { + PSU_ERR("Fail to alloc psu_index_str memory\n"); + return -ENOMEM; + } + + temp_index_str = (char *)kzalloc(7*sizeof(char), GFP_KERNEL); + if (!temp_index_str) + { + PSU_ERR("Fail to alloc temp_index_str memory\n"); + err = -ENOMEM; + goto drv_allocate_temp_index_str_failed; + } + + /* For s3ip */ + psu_kobj = kobject_create_and_add(PSU_NAME_STRING, switch_kobj); + if(!psu_kobj) + { + PSU_ERR("Failed to create 'psu'\n"); + err = -ENOMEM; + goto sysfs_create_kobject_psu_failed; + } + + for(i=0; i <= PSU_NUM; i++) + { + PSU_DEBUG("sysfs_create_file /psu/%s\n", psu_attr[i].attr.name); + retval = sysfs_create_file(psu_kobj, &psu_attr[i].attr); + if(retval) + { + PSU_ERR("Failed to create file '%s'\n", psu_attr[i].attr.name); + err = -retval; + goto sysfs_create_s3ip_attr_failed; + } + } + + for(psu_index=0; psu_index +#include +#include +#include + +#include "switch_sensor_driver.h" + +#define SWITCH_S3IP_SENSOR_VERSION "0.0.0.1" + +unsigned int loglevel = 0; +struct sensor_drivers_t *cb_func = NULL; + +struct sensor_attribute{ + struct attribute attr; + ssize_t (*show)(struct kobject *obj, struct sensor_attribute *attr, char *buf); + ssize_t (*store)(struct kobject *obj, struct sensor_attribute *attr, const char *buf, size_t count); +}; + +/* For s3ip */ +extern struct kobject *switch_kobj; +static struct kobject *sensor_kobj; +static struct kobject *temp_index_kobj[TEMP_TOTAL_NUM]; +static struct kobject *vol_index_kobj[VOL_TOTAL_NUM]; +static struct kobject *curr_index_kobj[CURR_TOTAL_NUM]; + +enum sensor_attrs { + DEBUG, + LOGLEVEL, + NUM_TEMP_SENSORS, + NUM_VOL_SENSORS, + NUM_CURR_SENSORS, + TEMP_ALIAS, + TEMP_TYPE, + TEMP_MAX, + TEMP_HYST, + TEMP_MIN, + TEMP_INPUT, + VOL_ALIAS, + VOL_TYPE, + VOL_MAX, + VOL_MIN, + VOL_INPUT, + RANGE, + VOL_NOMINAL_VALUE, + CURR_ALIAS, + CURR_TYPE, + CURR_MAX, + CURR_MIN, + CURR_INPUT, + NUM_SENSOR_ATTR, +}; + +int get_temp_index(struct kobject *kobj) +{ + int retval=0; + unsigned int temp_index; + char temp_index_str[2] = {0}; + +#ifdef C11_ANNEX_K + if(memcpy_s(temp_index_str, 2, (kobject_name(kobj)+4), 2) != 0) + { + return -ENOMEM; + } +#else + memcpy(temp_index_str, (kobject_name(kobj)+4), 2); +#endif + + retval = kstrtoint(temp_index_str, 10, &temp_index); + if(retval == 0) + { + SENSOR_DEBUG("temp_index:%d \n", temp_index); + } + else + { + SENSOR_DEBUG("Error:%d, temp_index:%s \n", retval, temp_index_str); + return -1; + } + + return temp_index; +} + +int get_vol_index(struct kobject *kobj) +{ + int retval=0; + unsigned int vol_index; + char vol_index_str[3] = {0}; + +#ifdef C11_ANNEX_K + if(memcpy_s(vol_index_str, 2, (kobject_name(kobj)+3), 2) != 0) + { + return -ENOMEM; + } +#else + memcpy(vol_index_str, (kobject_name(kobj)+3), 2); +#endif + retval = kstrtoint(vol_index_str, 10, &vol_index); + if(retval == 0) + { + SENSOR_DEBUG("vol_index:%d \n", vol_index); + } + else + { + SENSOR_DEBUG("Error:%d, vol_index:%s \n", retval, vol_index_str); + return -1; + } + + return vol_index; +} + +int get_curr_index(struct kobject *kobj) +{ + int retval=0; + unsigned int curr_index; + char curr_index_str[2] = {0}; + +#ifdef C11_ANNEX_K + if(memcpy_s(curr_index_str, 2, (kobject_name(kobj)+4), 2) != 0) + { + return -ENOMEM; + } +#else + memcpy(curr_index_str, (kobject_name(kobj)+4), 2); +#endif + + retval = kstrtoint(curr_index_str, 10, &curr_index); + if(retval == 0) + { + SENSOR_DEBUG("curr_index:%d \n", curr_index); + } + else + { + SENSOR_DEBUG("Error:%d, curr_index:%s \n", retval, curr_index_str); + return -1; + } + + return curr_index; +} + +static ssize_t s3ip_debug_help(struct kobject *kobj, struct sensor_attribute *attr, char *buf) +{ + int retval=0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + retval = cb_func->debug_help(buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { + return retval; + } +} + +static ssize_t s3ip_debug(struct kobject *kobj, struct sensor_attribute *attr, const char *buf, size_t count) +{ + return count; +} + +static ssize_t s3ip_get_loglevel(struct kobject *kobj, struct sensor_attribute *attr, char *buf) +{ +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", loglevel); +#else + return sprintf(buf, "%d\n", loglevel); +#endif +} + +static ssize_t s3ip_set_loglevel(struct kobject *kobj, struct sensor_attribute *attr, const char *buf, size_t count) +{ + int retval=0; + long lev; + + if(cb_func == NULL) + return -1; + + retval = kstrtol(buf, 0, &lev); + if(retval == 0) + { + SENSOR_DEBUG("lev:%ld \n", lev); + } + else + { + SENSOR_DEBUG("Error:%d, lev:%s \n", retval, buf); + return -1; + } + + loglevel = (unsigned int)lev; + + cb_func->set_loglevel(lev); + + return count; +} + +static ssize_t s3ip_get_temp_num(struct kobject *kobj, struct sensor_attribute *attr, char *buf) +{ +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", TEMP_TOTAL_NUM); +#else + return sprintf(buf, "%d\n", TEMP_TOTAL_NUM); +#endif + +} + +static ssize_t s3ip_get_vol_num(struct kobject *kobj, struct sensor_attribute *attr, char *buf) +{ +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", VOL_TOTAL_NUM); +#else + return sprintf(buf, "%d\n", VOL_TOTAL_NUM); +#endif + +} + +static ssize_t s3ip_get_curr_num(struct kobject *kobj, struct sensor_attribute *attr, char *buf) +{ +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", CURR_TOTAL_NUM); +#else + return sprintf(buf, "%d\n", CURR_TOTAL_NUM); +#endif + +} + +static ssize_t s3ip_get_temp_alias(struct kobject *kobj, struct sensor_attribute *attr, char *buf) +{ + int retval=0; + int temp_index; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + temp_index = get_temp_index(kobj); + if(temp_index < 0) + { + SENSOR_DEBUG("Get temp index failed.\n"); + return -1; + } + + retval = cb_func->get_temp_alias(temp_index-1, buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { + return retval; + } +} + +static ssize_t s3ip_get_temp_type(struct kobject *kobj, struct sensor_attribute *attr, char *buf) +{ + int retval=0; + int temp_index; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + temp_index = get_temp_index(kobj); + if(temp_index < 0) + { + SENSOR_DEBUG("Get temp index failed.\n"); + ERROR_RETURN_NA(-1); + } + + retval = cb_func->get_temp_type(temp_index-1, buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { + return retval; + } +} + +static ssize_t s3ip_get_temp_max(struct kobject *kobj, struct sensor_attribute *attr, char *buf) +{ + int retval=0; + int temp_index; + long temp_max; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + temp_index = get_temp_index(kobj); + if(temp_index < 0) + { + SENSOR_DEBUG("Get temp index failed.\n"); + ERROR_RETURN_NA(-1); + } + + retval = cb_func->get_temp_max(temp_index-1, &temp_max); + if(retval == false) + { + SENSOR_DEBUG("Get temp max failed.\n"); + ERROR_RETURN_NA(-1); + } +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%s%ld\n", ((temp_max < 0) ? "-":""), abs(temp_max)); +#else + return sprintf(buf, "%s%ld\n", ((temp_max < 0) ? "-":""), abs(temp_max)); +#endif + +} + +static ssize_t s3ip_set_temp_max(struct kobject *kobj, struct sensor_attribute *attr, const char *buf, size_t count) +{ + int retval=0; + int temp_index; + int temp_value; + + if(cb_func == NULL) + { + return -EIO; + } + + temp_index = get_temp_index(kobj); + if(temp_index < 0) + { + SENSOR_DEBUG("Get temp index failed.\n"); + return -EINVAL; + } + retval = kstrtoint(buf, 0, &temp_value); + if(retval == 0) + { + SENSOR_DEBUG("temp%d temp_max data:%d \n", temp_index, temp_value); + } + else + { + SENSOR_DEBUG("Error:%d, temp%d temp_max data:%s \n", retval, temp_index, buf); + return -EIO; + } + + retval = cb_func->set_temp_max(temp_index-1, temp_value); + if(retval < 0) + { + SENSOR_DEBUG("Set temp%d max failed.\n",temp_index); + return -EIO; + } + + return count; +} + +static ssize_t s3ip_get_temp_max_hyst(struct kobject *kobj, struct sensor_attribute *attr, char *buf) +{ + int retval=0; + int temp_index; + long temp_max_hyst; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + temp_index = get_temp_index(kobj); + if(temp_index < 0) + { + SENSOR_DEBUG("Get temp index failed.\n"); + ERROR_RETURN_NA(-1); + } + + retval = cb_func->get_temp_max_hyst(temp_index-1, &temp_max_hyst); + if(retval == false) + { + SENSOR_DEBUG("Get temp max hyst failed.\n"); + ERROR_RETURN_NA(-1); + } +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%s%ld\n", ((temp_max_hyst < 0) ? "-":""), abs(temp_max_hyst)); +#else + return sprintf(buf, "%s%ld\n", ((temp_max_hyst < 0) ? "-":""), abs(temp_max_hyst)); +#endif + +} + +static ssize_t s3ip_get_temp_min(struct kobject *kobj, struct sensor_attribute *attr, char *buf) +{ + int retval=0; + int temp_index; + long temp_min; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + temp_index = get_temp_index(kobj); + if(temp_index < 0) + { + SENSOR_DEBUG("Get temp index failed.\n"); + ERROR_RETURN_NA(-1); + } + + retval = cb_func->get_temp_min(temp_index-1, &temp_min); + if(retval == false) + { + SENSOR_DEBUG("Get temp min failed.\n"); + ERROR_RETURN_NA(-1); + } +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%s%ld\n", ((temp_min < 0) ? "-":""), abs(temp_min)); +#else + return sprintf(buf, "%s%ld\n", ((temp_min < 0) ? "-":""), abs(temp_min)); +#endif + +} + +static ssize_t s3ip_set_temp_min(struct kobject *kobj, struct sensor_attribute *attr, const char *buf, size_t count) +{ + int retval=0; + int temp_index; + int temp_value; + + if(cb_func == NULL) + { + return -EIO; + } + + temp_index = get_temp_index(kobj); + if(temp_index < 0) + { + SENSOR_DEBUG("Get temp index failed.\n"); + return -EINVAL; + } + retval = kstrtoint(buf, 0, &temp_value); + if(retval == 0) + { + SENSOR_DEBUG("temp%d temp_min data:%d \n", temp_index, temp_value); + } + else + { + SENSOR_DEBUG("Error:%d, temp%d temp_min data:%s \n", retval, temp_index, buf); + return -EIO; + } + + retval = cb_func->set_temp_min(temp_index-1, temp_value); + if(retval < 0) + { + SENSOR_DEBUG("Set temp%d min failed.\n",temp_index); + return -EIO; + } + + return count; +} + +static ssize_t s3ip_get_temp_input(struct kobject *kobj, struct sensor_attribute *attr, char *buf) +{ + int retval=0; + int temp_index; + long temp_input = 0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + temp_index = get_temp_index(kobj); + if(temp_index < 0) + { + SENSOR_DEBUG("Get temp index failed.\n"); + ERROR_RETURN_NA(-1); + } + + retval = cb_func->get_temp_input(temp_index-1, &temp_input); + if(retval == false) + { + SENSOR_DEBUG("Get temp input failed.\n"); + ERROR_RETURN_NA(-1); + } +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%s%ld\n", ((temp_input < 0) ? "-":""), abs(temp_input)); +#else + return sprintf(buf, "%s%ld\n", ((temp_input < 0) ? "-":""), abs(temp_input)); +#endif + +} + +static ssize_t s3ip_get_vol_alias(struct kobject *kobj, struct sensor_attribute *attr, char *buf) +{ + int retval=0; + int vol_index; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + vol_index = get_vol_index(kobj); + if(vol_index < 0) + { + SENSOR_DEBUG("Get vol index failed.\n"); + ERROR_RETURN_NA(-1); + } + + retval = cb_func->get_vol_alias(vol_index-1, buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { + return retval; + } +} + +static ssize_t s3ip_get_vol_type(struct kobject *kobj, struct sensor_attribute *attr, char *buf) +{ + int retval=0; + int vol_index; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + vol_index = get_vol_index(kobj); + if(vol_index < 0) + { + SENSOR_DEBUG("Get vol index failed.\n"); + ERROR_RETURN_NA(-1); + } + + retval = cb_func->get_vol_type(vol_index-1, buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { + return retval; + } +} + +static ssize_t s3ip_get_vol_max(struct kobject *kobj, struct sensor_attribute *attr, char *buf) +{ + int retval=0; + int vol_index; + long vol_max; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + vol_index = get_vol_index(kobj); + if(vol_index < 0) + { + SENSOR_DEBUG("Get vol index failed.\n"); + ERROR_RETURN_NA(-1); + } + + retval = cb_func->get_vol_max(vol_index-1, &vol_max); + if(retval == false) + { + SENSOR_DEBUG("Get vol max failed.\n"); + ERROR_RETURN_NA(-1); + } +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%s%ld\n", ((vol_max < 0) ? "-":""), abs(vol_max)); +#else + return sprintf(buf, "%s%ld\n", ((vol_max < 0) ? "-":""), abs(vol_max)); +#endif + +} + +static ssize_t s3ip_set_vol_max(struct kobject *kobj, struct sensor_attribute *attr, const char *buf, size_t count) +{ + int retval=0; + int vol_index; + int vol_value; + + if(cb_func == NULL) + { + return -EIO; + } + + vol_index = get_vol_index(kobj); + if(vol_index < 0) + { + SENSOR_DEBUG("Get vol index failed.\n"); + return -EINVAL; + } + retval = kstrtoint(buf, 0, &vol_value); + if(retval == 0) + { + SENSOR_DEBUG("vol%d vol_max data:%d \n", vol_index, vol_value); + } + else + { + SENSOR_DEBUG("Error:%d, vol%d vol_max data:%s \n", retval, vol_index, buf); + return -EIO; + } + + retval = cb_func->set_vol_max(vol_index-1, vol_value); + if(retval < 0) + { + SENSOR_DEBUG("Set vol%d max failed.\n",vol_index); + return -EIO; + } + + return count; +} + +static ssize_t s3ip_get_vol_min(struct kobject *kobj, struct sensor_attribute *attr, char *buf) +{ + int retval=0; + int vol_index; + long vol_min; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + vol_index = get_vol_index(kobj); + if(vol_index < 0) + { + SENSOR_DEBUG("Get vol index failed.\n"); + ERROR_RETURN_NA(-1); + } + + retval = cb_func->get_vol_min(vol_index-1, &vol_min); + if(retval == false) + { + SENSOR_DEBUG("Get vol min failed.\n"); + ERROR_RETURN_NA(-1); + } +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%s%ld\n", ((vol_min < 0) ? "-":""), abs(vol_min)); +#else + return sprintf(buf, "%s%ld\n", ((vol_min < 0) ? "-":""), abs(vol_min)); +#endif + +} + +static ssize_t s3ip_set_vol_min(struct kobject *kobj, struct sensor_attribute *attr, const char *buf, size_t count) +{ + int retval=0; + int vol_index; + int vol_value; + + if(cb_func == NULL) + { + return -EIO; + } + + vol_index = get_vol_index(kobj); + if(vol_index < 0) + { + SENSOR_DEBUG("Get vol index failed.\n"); + return -EINVAL; + } + retval = kstrtoint(buf, 0, &vol_value); + if(retval == 0) + { + SENSOR_DEBUG("vol%d vol_min data:%d \n", vol_index, vol_value); + } + else + { + SENSOR_DEBUG("Error:%d, vol%d vol_min data:%s \n", retval, vol_index, buf); + return -EIO; + } + + retval = cb_func->set_vol_min(vol_index-1, vol_value); + if(retval < 0) + { + SENSOR_DEBUG("Set vol%d min failed.\n",vol_index); + return -EIO; + } + + return count; +} + +static ssize_t s3ip_get_vol_nominal_value(struct kobject *kobj, struct sensor_attribute *attr, char *buf) +{ + int retval=0; + int vol_index; + long vol_nominal; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + vol_index = get_vol_index(kobj); + if(vol_index < 0) + { + SENSOR_DEBUG("Get vol index failed.\n"); + ERROR_RETURN_NA(-1); + } + + retval = cb_func->get_vol_nominal(vol_index-1, &vol_nominal); + if(retval == false) + { + SENSOR_DEBUG("Get vol nominal failed.\n"); + ERROR_RETURN_NA(-1); + } +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%ld\n", vol_nominal); +#else + return sprintf(buf, "%ld\n", vol_nominal); +#endif + +} + +static ssize_t s3ip_get_vol_input(struct kobject *kobj, struct sensor_attribute *attr, char *buf) +{ + int retval=0; + int vol_index; + long vol_input; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + vol_index = get_vol_index(kobj); + if(vol_index < 0) + { + SENSOR_DEBUG("Get vol index failed.\n"); + ERROR_RETURN_NA(-1); + } + + retval = cb_func->get_vol_input(vol_index-1, &vol_input); + if(retval == false) + { + SENSOR_DEBUG("Get vol input failed.\n"); + ERROR_RETURN_NA(-1); + } +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%s%ld\n", ((vol_input < 0) ? "-":""), abs(vol_input)); +#else + return sprintf(buf, "%s%ld\n", ((vol_input < 0) ? "-":""), abs(vol_input)); +#endif + +} + +static ssize_t s3ip_get_vol_range(struct kobject *kobj, struct sensor_attribute *attr, char *buf) +{ + int retval=0; + int vol_index; + long vol_range; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + vol_index = get_vol_index(kobj); + if(vol_index < 0) + { + SENSOR_DEBUG("Get vol index failed.\n"); + ERROR_RETURN_NA(-1); + } + + retval = cb_func->get_vol_range(vol_index-1, &vol_range); + if(retval == false) + { + SENSOR_DEBUG("Get vol range failed.\n"); + ERROR_RETURN_NA(-1); + } +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%ld\n", vol_range); +#else + return sprintf(buf, "%ld\n", vol_range); +#endif + +} + + +static ssize_t s3ip_get_curr_alias(struct kobject *kobj, struct sensor_attribute *attr, char *buf) +{ + int retval=0; + int curr_index; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + curr_index = get_curr_index(kobj); + if(curr_index < 0) + { + SENSOR_DEBUG("Get curr index failed.\n"); + return -1; + } + + retval = cb_func->get_curr_alias(curr_index-1, buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { + return retval; + } +} + +static ssize_t s3ip_get_curr_type(struct kobject *kobj, struct sensor_attribute *attr, char *buf) +{ + int retval=0; + int curr_index; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + curr_index = get_curr_index(kobj); + if(curr_index < 0) + { + SENSOR_DEBUG("Get curr index failed.\n"); + ERROR_RETURN_NA(-1); + } + + retval = cb_func->get_curr_type(curr_index-1, buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { + return retval; + } +} + +static ssize_t s3ip_get_curr_max(struct kobject *kobj, struct sensor_attribute *attr, char *buf) +{ + int retval=0; + int curr_index; + long curr_max; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + curr_index = get_curr_index(kobj); + if(curr_index < 0) + { + SENSOR_DEBUG("Get curr index failed.\n"); + ERROR_RETURN_NA(-1); + } + + retval = cb_func->get_curr_max(curr_index-1, &curr_max); + if(retval == false) + { + SENSOR_DEBUG("Get curr max failed.\n"); + ERROR_RETURN_NA(-1); + } +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%s%ld\n", ((curr_max < 0) ? "-":""), abs(curr_max)); +#else + return sprintf(buf, "%s%ld\n", ((curr_max < 0) ? "-":""), abs(curr_max)); +#endif + +} + +static ssize_t s3ip_set_curr_max(struct kobject *kobj, struct sensor_attribute *attr, const char *buf, size_t count) +{ + int retval=0; + int curr_index; + int curr_value; + + if(cb_func == NULL) + { + return -EIO; + } + + curr_index = get_curr_index(kobj); + if(curr_index < 0) + { + SENSOR_DEBUG("Get curr index failed.\n"); + return -EINVAL; + } + retval = kstrtoint(buf, 0, &curr_value); + if(retval == 0) + { + SENSOR_DEBUG("curr%d curr_max data:%d \n", curr_index, curr_value); + } + else + { + SENSOR_DEBUG("Error:%d, curr%d curr_max data:%s \n", retval, curr_index, buf); + return -EIO; + } + + retval = cb_func->set_curr_max(curr_index-1, curr_value); + if(retval < 0) + { + SENSOR_DEBUG("Set curr%d max failed.\n",curr_index); + return -EIO; + } + + return count; +} + +static ssize_t s3ip_get_curr_min(struct kobject *kobj, struct sensor_attribute *attr, char *buf) +{ + int retval=0; + int curr_index; + long curr_min; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + curr_index = get_curr_index(kobj); + if(curr_index < 0) + { + SENSOR_DEBUG("Get curr index failed.\n"); + ERROR_RETURN_NA(-1); + } + + retval = cb_func->get_curr_min(curr_index-1, &curr_min); + if(retval == false) + { + SENSOR_DEBUG("Get curr min failed.\n"); + ERROR_RETURN_NA(-1); + } +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%s%ld\n", ((curr_min < 0) ? "-":""), abs(curr_min)); +#else + return sprintf(buf, "%s%ld\n", ((curr_min < 0) ? "-":""), abs(curr_min)); +#endif + +} + +static ssize_t s3ip_set_curr_min(struct kobject *kobj, struct sensor_attribute *attr, const char *buf, size_t count) +{ + int retval=0; + int curr_index; + int curr_value; + + if(cb_func == NULL) + { + return -EIO; + } + + curr_index = get_curr_index(kobj); + if(curr_index < 0) + { + SENSOR_DEBUG("Get curr index failed.\n"); + return -EINVAL; + } + retval = kstrtoint(buf, 0, &curr_value); + if(retval == 0) + { + SENSOR_DEBUG("curr%d curr_min data:%d \n", curr_index, curr_value); + } + else + { + SENSOR_DEBUG("Error:%d, curr%d curr_min data:%s \n", retval, curr_index, buf); + return -EIO; + } + + retval = cb_func->set_curr_min(curr_index-1, curr_value); + if(retval < 0) + { + SENSOR_DEBUG("Set curr%d min failed.\n",curr_index); + return -EIO; + } + + return count; +} + +static ssize_t s3ip_get_curr_input(struct kobject *kobj, struct sensor_attribute *attr, char *buf) +{ + int retval=0; + int curr_index; + long curr_input; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + curr_index = get_curr_index(kobj); + if(curr_index < 0) + { + SENSOR_DEBUG("Get curr index failed.\n"); + ERROR_RETURN_NA(-1); + } + + retval = cb_func->get_curr_input(curr_index-1, &curr_input); + if(retval == false) + { + SENSOR_DEBUG("Get curr input failed.\n"); + ERROR_RETURN_NA(-1); + } +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%s%ld\n", ((curr_input < 0) ? "-":""), abs(curr_input)); +#else + return sprintf(buf, "%s%ld\n", ((curr_input < 0) ? "-":""), abs(curr_input)); +#endif + +} + +static struct sensor_attribute sensor_attr[NUM_SENSOR_ATTR] = { + [DEBUG] = {{.name = "debug", .mode = S_IRUGO | S_IWUSR}, s3ip_debug_help, s3ip_debug}, + [LOGLEVEL] = {{.name = "loglevel", .mode = S_IRUGO | S_IWUSR}, s3ip_get_loglevel, s3ip_set_loglevel}, + [NUM_TEMP_SENSORS] = {{.name = "num_temp_sensors", .mode = S_IRUGO}, s3ip_get_temp_num, NULL}, + [NUM_VOL_SENSORS] = {{.name = "num_vol_sensors", .mode = S_IRUGO}, s3ip_get_vol_num, NULL}, + [NUM_CURR_SENSORS] = {{.name = "num_curr_sensors", .mode = S_IRUGO}, s3ip_get_curr_num, NULL}, + [TEMP_ALIAS] = {{.name = "temp_alias", .mode = S_IRUGO}, s3ip_get_temp_alias, NULL}, + [TEMP_TYPE] = {{.name = "temp_type", .mode = S_IRUGO}, s3ip_get_temp_type, NULL}, + [TEMP_MAX] = {{.name = "temp_max", .mode = S_IRUGO | S_IWUSR}, s3ip_get_temp_max, s3ip_set_temp_max}, + [TEMP_HYST] = {{.name = "temp_hyst", .mode = S_IRUGO}, s3ip_get_temp_max_hyst, NULL}, + [TEMP_MIN] = {{.name = "temp_min", .mode = S_IRUGO | S_IWUSR}, s3ip_get_temp_min, s3ip_set_temp_min}, + [TEMP_INPUT] = {{.name = "temp_input", .mode = S_IRUGO}, s3ip_get_temp_input, NULL}, + [VOL_ALIAS] = {{.name = "vol_alias", .mode = S_IRUGO}, s3ip_get_vol_alias, NULL}, + [VOL_TYPE] = {{.name = "vol_type", .mode = S_IRUGO}, s3ip_get_vol_type, NULL}, + [VOL_MAX] = {{.name = "vol_max", .mode = S_IRUGO | S_IWUSR}, s3ip_get_vol_max, s3ip_set_vol_max}, + [VOL_MIN] = {{.name = "vol_min", .mode = S_IRUGO | S_IWUSR}, s3ip_get_vol_min, s3ip_set_vol_min}, + [VOL_INPUT] = {{.name = "vol_input", .mode = S_IRUGO}, s3ip_get_vol_input, NULL}, + [RANGE] = {{.name = "range", .mode = S_IRUGO}, s3ip_get_vol_range, NULL}, + [VOL_NOMINAL_VALUE] = {{.name = "nominal_value", .mode = S_IRUGO}, s3ip_get_vol_nominal_value, NULL}, + [CURR_ALIAS] = {{.name = "curr_alias", .mode = S_IRUGO}, s3ip_get_curr_alias, NULL}, + [CURR_TYPE] = {{.name = "curr_type", .mode = S_IRUGO}, s3ip_get_curr_type, NULL}, + [CURR_MAX] = {{.name = "curr_max", .mode = S_IRUGO | S_IWUSR}, s3ip_get_curr_max, s3ip_set_curr_max}, + [CURR_MIN] = {{.name = "curr_min", .mode = S_IRUGO | S_IWUSR}, s3ip_get_curr_min, s3ip_set_curr_min}, + [CURR_INPUT] = {{.name = "curr_input", .mode = S_IRUGO}, s3ip_get_curr_input, NULL}, +}; + +void s3ip_sensor_drivers_register(struct sensor_drivers_t *pfunc) +{ + cb_func = pfunc; + + return; +} +EXPORT_SYMBOL_GPL(s3ip_sensor_drivers_register); + +void s3ip_sensor_drivers_unregister(void) +{ + cb_func = NULL; + + return; +} +EXPORT_SYMBOL_GPL(s3ip_sensor_drivers_unregister); + +static int __init switch_sensor_init(void) +{ + int err=0; + int retval=0; + int i; + int temp_index, vol_index, curr_index; + char *temp_index_str, *vol_index_str, *curr_index_str; + + temp_index_str = (char *)kzalloc(7*sizeof(char), GFP_KERNEL); + if (!temp_index_str) + { + SENSOR_DEBUG( "Fail to alloc temp_index_str memory\n"); + return -ENOMEM; + } + + vol_index_str = (char *)kzalloc(5*sizeof(char), GFP_KERNEL); + if (!vol_index_str) + { + SENSOR_DEBUG( "Fail to alloc vol_index_str memory\n"); + err = -ENOMEM; + goto drv_allocate_vol_index_str_failed; + } + + curr_index_str = (char *)kzalloc(7*sizeof(char), GFP_KERNEL); + if (!curr_index_str) + { + SENSOR_DEBUG( "Fail to alloc curr_index_str memory\n"); + err = -ENOMEM; + goto drv_allocate_curr_index_str_failed; + } + + /* For s3ip */ + sensor_kobj = kobject_create_and_add("sensor", switch_kobj); + if(!sensor_kobj) + { + SENSOR_DEBUG( "Failed to create 'sensor'\n"); + err = -ENOMEM; + goto sysfs_create_kobject_sensor_failed; + } + + for(i=0; i<=NUM_CURR_SENSORS; i++) + { + SENSOR_DEBUG( "sysfs_create_file /sensor/%s\n", sensor_attr[i].attr.name); + retval = sysfs_create_file(sensor_kobj, &sensor_attr[i].attr); + if(retval) + { + printk(KERN_ERR "Failed to create file '%s'\n", sensor_attr[i].attr.name); + err = -retval; + goto sysfs_create_s3ip_attr_num_temp_sensors_failed; + } + } + + for(temp_index=0; temp_index /* + => module_init() + => module_exit() + => MODULE_LICENSE() + => MODULE_VERSION() + => MODULE_AUTHOR() + => struct module + */ +#include /* + => typedef int (*initcall_t)(void); + Note: the 'initcall_t' function returns 0 when succeed. + In the Linux kernel, error codes are negative numbers + belonging to the set defined in . + => typedef void (*exitcall_t)(void); + => __init + => __exit + */ +#include /* + => moduleparam() + */ +#include /* + => dev_t (u32) + */ +#include /* + => MAJOR() + => MINOR() + */ +#include /* + => void *memset() + */ +#include /* + => void kfree() + */ +#include +#include +#include +#include /* + => struct spinlock + */ +#include + +#include +#include +#include + +#define ERROR_DEBUG(...) printk(KERN_ALERT __VA_ARGS__) + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE (!FALSE) +#endif + +#define SWITCH_S3IP_SWITCH_VERSION "0.0.0.1" + +/* For s3ip */ +static struct kobject *switch_kobj; +EXPORT_SYMBOL_GPL(switch_kobj); + +static int __init switch_switch_init(void) +{ + /* For s3ip */ + switch_kobj = kobject_create_and_add("switch", NULL); + if(!switch_kobj) + { + ERROR_DEBUG( "[%s]Failed to create 'switch'\n", __func__); + return -ENOMEM; + } + + return 0; +} + +static void __exit switch_switch_exit(void) +{ + if(switch_kobj) + { + kobject_put(switch_kobj); + switch_kobj = NULL; + } + + return; +} + +MODULE_DESCRIPTION("Switch S3IP Switch Entry Driver"); +MODULE_VERSION(SWITCH_S3IP_SWITCH_VERSION); +MODULE_LICENSE("GPL"); + +module_init(switch_switch_init); +module_exit(switch_switch_exit); diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/s3ip/switch_s3ip_syseeprom.c b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/s3ip/switch_s3ip_syseeprom.c new file mode 100644 index 0000000000..4266a3a600 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/s3ip/switch_s3ip_syseeprom.c @@ -0,0 +1,378 @@ +#include /* + => module_init() + => module_exit() + => MODULE_LICENSE() + => MODULE_VERSION() + => MODULE_AUTHOR() + => struct module + */ +#include /* + => typedef int (*initcall_t)(void); + Note: the 'initcall_t' function returns 0 when succeed. + In the Linux kernel, error codes are negative numbers + belonging to the set defined in . + => typedef void (*exitcall_t)(void); + => __init + => __exit + */ +#include /* + => moduleparam() + */ +#include /* + => dev_t (u32) + */ +#include /* + => MAJOR() + => MINOR() + */ +#include /* + => void *memset() + */ +#include /* + => void kfree() + */ +#include +#include +#include +#include /* + => struct spinlock + */ +#include + +#include +#include +#include + +#include "switch_mb_driver.h" + +#define ERROR_DEBUG(...) printk(KERN_ALERT __VA_ARGS__) + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE (!FALSE) +#endif + +#define SWITCH_S3IP_SYSEEPROM_VERSION "0.0.0.1" + +unsigned int loglevel = 0; + +struct syseeprom_attribute{ + struct attribute attr; + ssize_t (*show)(struct kobject *obj, struct syseeprom_attribute *attr, char *buf); + ssize_t (*store)(struct kobject *obj, struct syseeprom_attribute *attr, const char *buf, size_t count); +}; + +struct mainboard_drivers_t *cb_func = NULL; + +/* For s3ip */ +extern struct kobject *switch_kobj; +static struct kobject *syseeprom_kobj; + +enum syseeprom_attrs { + EEPROM, + BSP_VERSION, + MGMT_MAC, + DATE, + NAME, + SN, + VENDOR, + DEBUG, + LOGLEVEL, + NUM_SYSEEPROM_ATTR, +}; + +static ssize_t s3ip_get_eeprom(struct kobject *kobj, struct syseeprom_attribute *attr, char *buf) +{ + int retval=0; + + if(cb_func == NULL) + { + return -EIO; + } + + retval = cb_func->get_syseeprom(buf); + if(retval < 0) + { + return -EIO; + } + else + { + return retval; + } +} + +static ssize_t s3ip_get_bsp_version(struct kobject *kobj, struct syseeprom_attribute *attr, char *buf) +{ + int retval=0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + retval = cb_func->get_odm_bsp_version(buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { + return retval; + } +} + +static ssize_t s3ip_get_mgmt_mac(struct kobject *kobj, struct syseeprom_attribute *attr, char *buf) +{ + int retval=0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + retval = cb_func->get_mgmt_mac(buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { + return retval; + } +} + +static ssize_t s3ip_get_date(struct kobject *kobj, struct syseeprom_attribute *attr, char *buf) +{ + int retval=0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + retval = cb_func->get_date(buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { + return retval; + } +} + +static ssize_t s3ip_get_name(struct kobject *kobj, struct syseeprom_attribute *attr, char *buf) +{ + int retval=0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + retval = cb_func->get_name(buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { + return retval; + } +} + +static ssize_t s3ip_get_sn(struct kobject *kobj, struct syseeprom_attribute *attr, char *buf) +{ + int retval=0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + retval = cb_func->get_sn(buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { + return retval; + } +} + +static ssize_t s3ip_get_switch_vendor(struct kobject *kobj, struct syseeprom_attribute *attr, char *buf) +{ + int retval=0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + retval = cb_func->get_vendor(buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { + return retval; + } +} + +static ssize_t s3ip_debug_help(struct kobject *kobj, struct syseeprom_attribute *attr, char *buf) +{ + int retval=0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + retval = cb_func->debug_help(buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { + return retval; + } +} + +static ssize_t s3ip_debug(struct kobject *kobj, struct syseeprom_attribute *attr, const char *buf, size_t count) +{ + return count; +} + +static ssize_t s3ip_get_loglevel(struct kobject *kobj, struct syseeprom_attribute *attr, char *buf) +{ +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", loglevel); +#else + return sprintf(buf, "%d\n", loglevel); +#endif +} + +static ssize_t s3ip_set_loglevel(struct kobject *kobj, struct syseeprom_attribute *attr, const char *buf, size_t count) +{ + int retval=0; + long lev; + + if(cb_func == NULL) + return -1; + + retval = kstrtol(buf, 0, &lev); + if(retval == 0) + { + MAINBOARD_DEBUG("lev:%ld \n", lev); + } + else + { + MAINBOARD_DEBUG("Error:%d, lev:%s \n", retval, buf); + return -1; + } + + loglevel = (unsigned int)lev; + + cb_func->set_loglevel(lev); + + return count; +} + +static struct syseeprom_attribute syseeprom_attr[NUM_SYSEEPROM_ATTR] = { + [EEPROM] = {{.name = "eeprom", .mode = S_IRUGO}, s3ip_get_eeprom, NULL}, + [BSP_VERSION] = {{.name = "bsp_version", .mode = S_IRUGO}, s3ip_get_bsp_version, NULL}, + [MGMT_MAC] = {{.name = "base_mac", .mode = S_IRUGO}, s3ip_get_mgmt_mac, NULL}, + [DATE] = {{.name = "date", .mode = S_IRUGO}, s3ip_get_date, NULL}, + [NAME] = {{.name = "name", .mode = S_IRUGO}, s3ip_get_name, NULL}, + [SN] = {{.name = "sn", .mode = S_IRUGO}, s3ip_get_sn, NULL}, + [VENDOR] = {{.name = "vendor", .mode = S_IRUGO}, s3ip_get_switch_vendor, NULL}, + [DEBUG] = {{.name = "debug", .mode = S_IRUGO | S_IWUSR}, s3ip_debug_help, s3ip_debug}, + [LOGLEVEL] = {{.name = "loglevel", .mode = S_IRUGO | S_IWUSR}, s3ip_get_loglevel, s3ip_set_loglevel}, +}; + + +void s3ip_mainboard_drivers_register(struct mainboard_drivers_t *pfunc) +{ + cb_func = pfunc; + + return; +} +EXPORT_SYMBOL_GPL(s3ip_mainboard_drivers_register); + +void s3ip_mainboard_drivers_unregister(void) +{ + cb_func = NULL; + + return; +} +EXPORT_SYMBOL_GPL(s3ip_mainboard_drivers_unregister); + +static int __init switch_syseeprom_init(void) +{ + int retval = 0; + int err=0; + int i; + + /* For s3ip */ + syseeprom_kobj = kobject_create_and_add("syseeprom", switch_kobj); + if(!syseeprom_kobj) + { + MAINBOARD_DEBUG( "[%s]Failed to create 'syseeprom'\n", __func__); + return -ENOMEM; + } + + for(i=0; i < NUM_SYSEEPROM_ATTR; i++) + { + MAINBOARD_DEBUG( "[%s]sysfs_create_file /syseeprom/%s\n", __func__, syseeprom_attr[i].attr.name); + retval = sysfs_create_file(syseeprom_kobj, &syseeprom_attr[i].attr); + if(retval) + { + printk(KERN_ERR "Failed to create file '%s'\n", syseeprom_attr[i].attr.name); + err = -retval; + goto sysfs_create_s3ip_syseeprom_attr_failed; + } + } + + return 0; + +sysfs_create_s3ip_syseeprom_attr_failed: + if(syseeprom_kobj) + { + kobject_put(syseeprom_kobj); + syseeprom_kobj = NULL; + } + + return err; +} + +static void __exit switch_syseeprom_exit(void) +{ + int i; + + /* For s3ip */ + for(i=0; i < NUM_SYSEEPROM_ATTR; i++) + sysfs_remove_file(syseeprom_kobj, &syseeprom_attr[i].attr); + + if(syseeprom_kobj) + { + kobject_put(syseeprom_kobj); + syseeprom_kobj = NULL; + } + + cb_func = NULL; + + return; +} + +MODULE_DESCRIPTION("Switch S3IP SYSEEPROM Driver"); +MODULE_VERSION(SWITCH_S3IP_SYSEEPROM_VERSION); +MODULE_LICENSE("GPL"); + +module_init(switch_syseeprom_init); +module_exit(switch_syseeprom_exit); diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/s3ip/switch_s3ip_sysled.c b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/s3ip/switch_s3ip_sysled.c new file mode 100644 index 0000000000..203ab32253 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/s3ip/switch_s3ip_sysled.c @@ -0,0 +1,658 @@ +#include +#include +#include + +#include "switch_led_driver.h" + +#define SWITCH_S3IP_SYSLED_VERSION "0.0.0.1" + +unsigned int loglevel = 0; + +enum led_status { + LED_ALL_OFF, + LED_GREEN_ON, + LED_YELLOW_ON, + LED_RED_ON, + LED_BLUE_ON, + LED_GREEN_FLASH, + LED_YELLOW_FLASH, + LED_RED_FLASH, + LED_BLUE_FLASH +}; + +struct led_attribute{ + struct attribute attr; + ssize_t (*show)(struct kobject *obj, struct led_attribute *attr, char *buf); + ssize_t (*store)(struct kobject *obj, struct led_attribute *attr, const char *buf, size_t count); +}; + +struct led_drivers_t *cb_func = NULL; + +/* For s3ip */ +extern struct kobject *switch_kobj; +static struct kobject *sysled_kobj; + +enum led_attrs { + DEBUG, + LOGLEVEL, + SYS_LED_STATUS, + BMC_LED_STATUS, + FAN_LED_STATUS, + PSU_LED_STATUS, + ID_LED_STATUS, + NUM_LED_ATTR, +}; + +static ssize_t s3ip_debug_help(struct kobject *kobj, struct led_attribute *attr, char *buf) +{ + int retval=0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + retval = cb_func->debug_help(buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { + return retval; + } +} + +static ssize_t s3ip_debug(struct kobject *kobj, struct led_attribute *attr, const char *buf, size_t count) +{ + return count; +} + +static ssize_t s3ip_get_loglevel(struct kobject *kobj, struct led_attribute *attr, char *buf) +{ +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", loglevel); +#else + return sprintf(buf, "%d\n", loglevel); +#endif +} + +static ssize_t s3ip_set_loglevel(struct kobject *kobj, struct led_attribute *attr, const char *buf, size_t count) +{ + int retval=0; + long lev; + + if(cb_func == NULL) + return -1; + + retval = kstrtol(buf, 0, &lev); + if(retval == 0) + { + LED_DEBUG("[%s] lev:%ld \n", __func__, lev); + } + else + { + LED_DEBUG("[%s] Error:%d, lev:%s \n", __func__, retval, buf); + return -1; + } + + loglevel = (unsigned int)lev; + + cb_func->set_loglevel(lev); + + return count; +} + +static ssize_t s3ip_get_sys_led(struct kobject *kobj, struct led_attribute *attr, char *buf) +{ + int retval=0; + unsigned char led; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-ENXIO); + } + + retval = cb_func->get_sys_led(&led); + if(retval == false) + { + LED_DEBUG("[%s] Get sys led failed.\n", __func__); + ERROR_RETURN_NA(-EPERM); + } + + switch(led) + { + case SYS_LED_OFF: + led = LED_ALL_OFF; + break; + case SYS_LED_GREEN_ON: + led = LED_GREEN_ON; + break; + case SYS_LED_YELLOW_ON: + led = LED_YELLOW_ON; + break; + case SYS_LED_RED_ON: + led = LED_RED_ON; + break; + case SYS_LED_BLUE_ON: + led = LED_BLUE_ON; + break; + case SYS_LED_GREEN_FLASH_COME_OK: + case SYS_LED_GREEN_FLASH_BMC_OK: + led = LED_GREEN_FLASH; + break; + case SYS_LED_YELLOW_FLASH_COME_OK: + case SYS_LED_YELLOW_FLASH_BMC_OK: + led = LED_YELLOW_FLASH; + break; + case SYS_LED_RED_FLASH: + led = LED_RED_FLASH; + break; + case SYS_LED_BLUE_FLASH: + led = LED_BLUE_FLASH; + break; + default: + led = -EINVAL; + ERROR_RETURN_NA(-EINVAL); + break; + } +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", led); +#else + return sprintf(buf, "%d\n", led); +#endif + +} + +ssize_t s3ip_set_sys_led(struct kobject *obj, struct led_attribute *attr, const char *buf, size_t count) +{ + int retval=0; + unsigned int led; + + if(cb_func == NULL) + return -EIO; + + retval = kstrtoint(buf, 10, &led); + if(retval == 0) + { + LED_DEBUG("[%s] led:%d \n", __func__, led); + } + else + { + LED_DEBUG("[%s] Error:%d, led:%s \n", __func__, retval, buf); + return -1; + } + + switch(led) + { + case LED_ALL_OFF: + retval = cb_func->set_sys_led(SYS_LED_OFF); + break; + case LED_GREEN_ON: + retval = cb_func->set_sys_led(SYS_LED_GREEN_ON); + break; + case LED_YELLOW_ON: + retval = cb_func->set_sys_led(SYS_LED_YELLOW_ON); + break; + case LED_RED_ON: + retval = cb_func->set_sys_led(SYS_LED_RED_ON); + break; + case LED_BLUE_ON: + retval = cb_func->set_sys_led(SYS_LED_BLUE_ON); + break; + case LED_GREEN_FLASH: + retval = cb_func->set_sys_led(SYS_LED_GREEN_FLASH_COME_OK); + break; + case LED_YELLOW_FLASH: + retval = cb_func->set_sys_led(SYS_LED_YELLOW_FLASH_COME_OK); + break; + case LED_RED_FLASH: + retval = cb_func->set_sys_led(SYS_LED_RED_FLASH); + break; + case LED_BLUE_FLASH: + retval = cb_func->set_sys_led(SYS_LED_BLUE_FLASH); + break; + default: + return -EINVAL; + break; + } + + if(retval == false) + { + LED_DEBUG("[%s] Set sys led failed.\n", __func__); + return -EPERM; + } + + return count; +} +EXPORT_SYMBOL_GPL(s3ip_set_sys_led); + +static ssize_t s3ip_get_bmc_led(struct kobject *kobj, struct led_attribute *attr, char *buf) +{ + int retval=0; + unsigned char led; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-ENXIO); + } + + retval = cb_func->get_bmc_led(&led); + if(retval == false) + { + LED_DEBUG("[%s] Get bmc led failed.\n", __func__); + ERROR_RETURN_NA(-EPERM); + } + + switch(led) + { + case BMC_LED_OFF: + led = LED_ALL_OFF; + break; + case BMC_LED_GREEN_ON: + led = LED_GREEN_ON; + break; + case BMC_LED_YELLOW_ON: + led = LED_YELLOW_ON; + break; + case BMC_LED_RED_ON: + led = LED_RED_ON; + break; + case BMC_LED_BLUE_ON: + led = LED_BLUE_ON; + break; + case BMC_LED_GREEN_FLASH: + led = LED_GREEN_FLASH; + break; + case BMC_LED_YELLOW_FLASH: + led = LED_YELLOW_FLASH; + break; + case BMC_LED_RED_FLASH: + led = LED_RED_FLASH; + break; + case BMC_LED_BLUE_FLASH: + led = LED_BLUE_FLASH; + break; + default: + led = -EINVAL; + ERROR_RETURN_NA(-EINVAL); + break; + } +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", led); +#else + return sprintf(buf, "%d\n", led); +#endif + +} + +ssize_t s3ip_set_bmc_led(struct kobject *obj, struct led_attribute *attr, const char *buf, size_t count) +{ + int retval=0; + unsigned int led; + + if(cb_func == NULL) + return -EIO; + + retval = kstrtoint(buf, 10, &led); + if(retval == 0) + { + LED_DEBUG("[%s] led:%d \n", __func__, led); + } + else + { + LED_DEBUG("[%s] Error:%d, led:%s \n", __func__, retval, buf); + return -1; + } + + switch(led) + { + case LED_ALL_OFF: + retval = cb_func->set_bmc_led(BMC_LED_OFF); + break; + case LED_GREEN_ON: + retval = cb_func->set_bmc_led(BMC_LED_GREEN_ON); + break; + case LED_YELLOW_ON: + retval = cb_func->set_bmc_led(BMC_LED_YELLOW_ON); + break; + case LED_RED_ON: + retval = cb_func->set_bmc_led(BMC_LED_RED_ON); + break; + case LED_BLUE_ON: + retval = cb_func->set_bmc_led(BMC_LED_BLUE_ON); + break; + case LED_GREEN_FLASH: + retval = cb_func->set_bmc_led(BMC_LED_GREEN_FLASH); + break; + case LED_YELLOW_FLASH: + retval = cb_func->set_bmc_led(BMC_LED_YELLOW_FLASH); + break; + case LED_RED_FLASH: + retval = cb_func->set_bmc_led(BMC_LED_RED_FLASH); + break; + case LED_BLUE_FLASH: + retval = cb_func->set_bmc_led(BMC_LED_BLUE_FLASH); + break; + default: + return -EINVAL; + break; + } + + if(retval == false) + { + LED_DEBUG("[%s] Set sys led failed.\n", __func__); + return -EPERM; + } + + return count; +} +EXPORT_SYMBOL_GPL(s3ip_set_bmc_led); +static ssize_t s3ip_get_fan_led(struct kobject *kobj, struct led_attribute *attr, char *buf) +{ + int retval=0; + unsigned char led; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-ENXIO); + } + + retval = cb_func->get_fan_led(&led); + if(retval == false) + { + LED_DEBUG("[%s] Get bmc led failed.\n", __func__); + ERROR_RETURN_NA(-EPERM); + } + + switch(led) + { + case FAN_LED_OFF: + led = LED_ALL_OFF; + break; + case FAN_LED_GREEN_ON: + led = LED_GREEN_ON; + break; + case FAN_LED_YELLOW_ON: + led = LED_YELLOW_ON; + break; + case FAN_LED_RED_ON: + led = LED_RED_ON; + break; + case FAN_LED_BLUE_ON: + led = LED_BLUE_ON; + break; + default: + led = -EINVAL; + ERROR_RETURN_NA(-EINVAL); + break; + } +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", led); +#else + return sprintf(buf, "%d\n", led); +#endif + +} + +static ssize_t s3ip_get_psu_led(struct kobject *kobj, struct led_attribute *attr, char *buf) +{ + int retval=0; + unsigned char led; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-ENXIO); + } + + retval = cb_func->get_psu_led(&led); + if(retval == false) + { + LED_DEBUG("[%s] Get bmc led failed.\n", __func__); + ERROR_RETURN_NA(-EPERM); + } + + switch(led) + { + case PSU_LED_OFF: + led = LED_ALL_OFF; + break; + case PSU_LED_GREEN_ON: + led = LED_GREEN_ON; + break; + case PSU_LED_YELLOW_ON: + led = LED_YELLOW_ON; + break; + case PSU_LED_RED_ON: + led = LED_RED_ON; + break; + case PSU_LED_BLUE_ON: + led = LED_BLUE_ON; + break; + default: + led = -EINVAL; + ERROR_RETURN_NA(-EINVAL); + break; + } +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", led); +#else + return sprintf(buf, "%d\n", led); +#endif + +} + +static ssize_t s3ip_get_id_led(struct kobject *kobj, struct led_attribute *attr, char *buf) +{ + unsigned char led; + led = -EINVAL; + ERROR_RETURN_NA(-EINVAL); +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", led); +#else + return sprintf(buf, "%d\n", led); +#endif + +} + +ssize_t s3ip_set_fan_led(struct kobject *obj, struct led_attribute *attr, const char *buf, size_t count) +{ + int retval=0; + unsigned int led; + + if(cb_func == NULL) + { + return -ENXIO; + } + + retval = kstrtoint(buf, 10, &led); + if(retval == 0) + { + LED_DEBUG("[%s] fan_led:%d \n", __func__, led); + } + else + { + LED_DEBUG("[%s] Error:%d, fan_led:%s \n", __func__, retval, buf); + return -1; + } + + switch(led) + { + case LED_ALL_OFF: + retval = cb_func->set_fan_led(FAN_LED_OFF); + break; + case LED_GREEN_ON: + retval = cb_func->set_fan_led(FAN_LED_GREEN_ON); + break; + case LED_YELLOW_ON: + retval = cb_func->set_fan_led(FAN_LED_YELLOW_ON); + break; + case LED_RED_ON: + retval = cb_func->set_fan_led(FAN_LED_RED_ON); + break; + case LED_BLUE_ON: + retval = cb_func->set_fan_led(FAN_LED_BLUE_ON); + break; + default: + return -EINVAL; + break; + } + + if(retval == false) + { + LED_DEBUG("[%s] Set fan_led failed.\n", __func__); + return -EPERM; + } + + return count; +} +EXPORT_SYMBOL_GPL(s3ip_set_fan_led); + +ssize_t s3ip_set_psu_led(struct kobject *obj, struct led_attribute *attr, const char *buf, size_t count) +{ + int retval=0; + unsigned int led; + + if(cb_func == NULL) + { + return -EIO; + } + + retval = kstrtoint(buf, 10, &led); + if(retval == 0) + { + LED_DEBUG("[%s] psu_led:%d \n", __func__, led); + } + else + { + LED_DEBUG("[%s] Error:%d, psu_led:%s \n", __func__, retval, buf); + return -1; + } + + switch(led) + { + case LED_ALL_OFF: + retval = cb_func->set_psu_led(PSU_LED_OFF); + break; + case LED_GREEN_ON: + retval = cb_func->set_psu_led(PSU_LED_GREEN_ON); + break; + case LED_YELLOW_ON: + retval = cb_func->set_psu_led(PSU_LED_YELLOW_ON); + break; + case LED_RED_ON: + retval = cb_func->set_psu_led(PSU_LED_RED_ON); + break; + case LED_BLUE_ON: + retval = cb_func->set_psu_led(PSU_LED_BLUE_ON); + break; + default: + return -EINVAL; + break; + } + + if(retval == false) + { + LED_DEBUG("[%s] Set psu_led failed.\n", __func__); + return -EPERM; + } + + return count; +} +EXPORT_SYMBOL_GPL(s3ip_set_psu_led); + +static ssize_t s3ip_set_id_led(struct kobject *obj, struct led_attribute *attr, const char *buf, size_t count) +{ + return -EPERM; +} + + +static struct led_attribute led_attr[NUM_LED_ATTR] = { + [DEBUG] = {{.name = "debug", .mode = S_IRUGO | S_IWUSR}, s3ip_debug_help, s3ip_debug}, + [LOGLEVEL] = {{.name = "loglevel", .mode = S_IRUGO | S_IWUSR}, s3ip_get_loglevel, s3ip_set_loglevel}, + [SYS_LED_STATUS] = {{.name = "sys_led_status", .mode = S_IRUGO | S_IWUSR}, s3ip_get_sys_led, s3ip_set_sys_led}, + [BMC_LED_STATUS] = {{.name = "bmc_led_status", .mode = S_IRUGO | S_IWUSR}, s3ip_get_bmc_led, s3ip_set_bmc_led}, + [FAN_LED_STATUS] = {{.name = "fan_led_status", .mode = S_IRUGO | S_IWUSR}, s3ip_get_fan_led, s3ip_set_fan_led}, + [PSU_LED_STATUS] = {{.name = "psu_led_status", .mode = S_IRUGO | S_IWUSR}, s3ip_get_psu_led, s3ip_set_psu_led}, + [ID_LED_STATUS] = {{.name = "id_led_status", .mode = S_IRUGO | S_IWUSR}, s3ip_get_id_led, s3ip_set_id_led}, +}; + +void s3ip_led_drivers_register(struct led_drivers_t *pfunc) +{ + cb_func = pfunc; + + return; +} +EXPORT_SYMBOL_GPL(s3ip_led_drivers_register); + +void s3ip_led_drivers_unregister(void) +{ + cb_func = NULL; + + return; +} +EXPORT_SYMBOL_GPL(s3ip_led_drivers_unregister); + +static int __init switch_sysled_init(void) +{ + int err=0; + int retval=0; + int i; + + /* For s3ip */ + sysled_kobj = kobject_create_and_add("sysled", switch_kobj); + if(!sysled_kobj) + { + LED_DEBUG( "[%s]Failed to create 'sysled'\n", __func__); + return -ENOMEM; + } + + for(i=0; i < NUM_LED_ATTR; i++) + { + LED_DEBUG( "[%s]sysfs_create_file /sysled/%s\n", __func__, led_attr[i].attr.name); + retval = sysfs_create_file(sysled_kobj, &led_attr[i].attr); + if(retval) + { + printk(KERN_ERR "Failed to create file '%s'\n", led_attr[i].attr.name); + err = -retval; + goto sysfs_create_s3ip_attr_failed; + } + } + + return 0; + +sysfs_create_s3ip_attr_failed: + if(sysled_kobj) + { + kobject_put(sysled_kobj); + sysled_kobj = NULL; + } + + return err; +} + +static void __exit switch_sysled_exit(void) +{ + int i; + + /* For s3ip */ + for(i=0; i < NUM_LED_ATTR; i++) + sysfs_remove_file(sysled_kobj, &led_attr[i].attr); + + if(sysled_kobj) + { + kobject_put(sysled_kobj); + sysled_kobj = NULL; + } + + cb_func = NULL; + + return; +} + +MODULE_DESCRIPTION("Switch S3IP SYSLED Driver"); +MODULE_VERSION(SWITCH_S3IP_SYSLED_VERSION); +MODULE_LICENSE("GPL"); + +module_init(switch_sysled_init); +module_exit(switch_sysled_exit); \ No newline at end of file diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/s3ip/switch_s3ip_transceiver.c b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/s3ip/switch_s3ip_transceiver.c new file mode 100644 index 0000000000..693c774b81 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/s3ip/switch_s3ip_transceiver.c @@ -0,0 +1,1233 @@ +#include +#include +#include +#include + +#include "switch_transceiver_driver.h" +#include "switch_optoe.h" + +#define SWITCH_S3IP_TRANSCEIVER_VERSION "0.0.0.1" + +unsigned int loglevel = 0; +static int multiplier = 100000000; + +struct transceiver_attribute{ + struct attribute attr; + ssize_t (*show)(struct kobject *obj, struct transceiver_attribute *attr, char *buf); + ssize_t (*store)(struct kobject *obj, struct transceiver_attribute *attr, const char *buf, size_t count); +}; + +struct transceiver_drivers_t *cb_func = NULL; + +/* For s3ip */ +extern struct kobject *switch_kobj; +static struct kobject *transceiver_kobj; +static struct kobject *transceiver_index_kobj[TRANSCEIVER_TOTAL_NUM]; + +enum transceiver_attrs { + DEBUG, + LOGLEVEL, + POWER_ON, + PRESENT, + NUM, + ETH_POWER_ON, + ETH_RESET, + ETH_LPMODE, + ETH_TEMP, + ETH_PRESENT, + ETH_INTERRUPT, + ETH_DIAGNOSTIC, + ETH_RX_LOS, + ETH_TX_LOS, + ETH_TX_DISABLE, + ETH_RX_DISABLE, + ETH_TX_CDR_LOL, + ETH_RX_CDR_LOL, + ETH_TX_FAULT, + ETH_MODULE_STATUS, + ETH_DATAPATH_STATUS, + ETH_TEMP_TYPE, + ETH_LED_STATUS, +#if 0 /* use bin_attribute instead of kobj create add */ + ETH_EEPROM, +#endif + NUM_TRANSCEIVER_ATTR, +}; + +int get_transceiver_index(struct kobject *kobj) +{ + int retval=0; + unsigned int transceiver_index; + char transceiver_index_str[5] = {0}; + +#ifdef C11_ANNEX_K + if(memcpy_s(transceiver_index_str, 5, (kobject_name(kobj)+sizeof(ETH_NAME_STRING)-1), 3) != 0) + { + return -ENOMEM; + } +#else + memcpy(transceiver_index_str, (kobject_name(kobj)+sizeof(ETH_NAME_STRING)-1), 3); +#endif + + retval = kstrtoint(transceiver_index_str, 10, &transceiver_index); + if(retval == 0) + { + TRANSCEIVER_DEBUG("[%s] transceiver_index:%d \n", __func__, transceiver_index); + } + else + { + TRANSCEIVER_DEBUG("[%s] Error:%d, transceiver_index:%s \n", __func__, retval, transceiver_index_str); + return -EINVAL; + } + + return transceiver_index; +} + +static ssize_t s3ip_debug_help(struct kobject *kobj, struct transceiver_attribute *attr, char *buf) +{ + int retval=0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + retval = cb_func->debug_help(buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { + return retval; + } +} + +static ssize_t s3ip_debug(struct kobject *kobj, struct transceiver_attribute *attr, const char *buf, size_t count) +{ + return count; +} + +static ssize_t s3ip_get_loglevel(struct kobject *kobj, struct transceiver_attribute *attr, char *buf) +{ +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", loglevel); +#else + return sprintf(buf, "%d\n", loglevel); +#endif +} + +static ssize_t s3ip_set_loglevel(struct kobject *kobj, struct transceiver_attribute *attr, const char *buf, size_t count) +{ + int retval=0; + long lev; + + if(cb_func == NULL) + return -1; + + retval = kstrtol(buf, 0, &lev); + if(retval == 0) + { + TRANSCEIVER_DEBUG("lev:%ld \n", lev); + } + else + { + TRANSCEIVER_DEBUG("Error:%d, lev:%s \n", retval, buf); + return -1; + } + + loglevel = (unsigned int)lev; + + cb_func->set_loglevel(lev); + + return count; +} + +static ssize_t s3ip_get_power_on(struct kobject *kobj, struct transceiver_attribute *attr, char *buf) +{ + unsigned int pwr_value = 0; + unsigned int transceiver_index = 0; + char rst_str[TRANSCEIVER_TOTAL_NUM + 1]; + + if(cb_func == NULL) + return -1; + + rst_str[TRANSCEIVER_TOTAL_NUM] = '\0'; + for(transceiver_index = 1; transceiver_index <= TRANSCEIVER_TOTAL_NUM; transceiver_index++) + { + pwr_value = cb_func->get_eth_power_on(transceiver_index); + rst_str[transceiver_index - 1] = pwr_value ? '1':'0'; + + } +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%s\n", rst_str); +#else + return sprintf(buf, "%s\n", rst_str); +#endif + +} + + +static ssize_t s3ip_get_present(struct kobject *kobj, struct transceiver_attribute *attr, char *buf) +{ + unsigned int present_value = 0; + unsigned int transceiver_index = 0; + char rst_str[TRANSCEIVER_TOTAL_NUM + 1]; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + rst_str[TRANSCEIVER_TOTAL_NUM] = '\0'; + for(transceiver_index = 1; transceiver_index <= TRANSCEIVER_TOTAL_NUM; transceiver_index++) + { + present_value = cb_func->get_eth_present(transceiver_index); + rst_str[transceiver_index - 1] = present_value ? '1':'0'; + } +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%s\n", rst_str); +#else + return sprintf(buf, "%s\n", rst_str); +#endif + +} + +static ssize_t s3ip_get_num(struct kobject *kobj, struct transceiver_attribute *attr, char *buf) +{ +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", TRANSCEIVER_TOTAL_NUM); +#else + return sprintf(buf, "%d\n", TRANSCEIVER_TOTAL_NUM); +#endif + +} + +static ssize_t s3ip_get_eth_power_on(struct kobject *kobj, struct transceiver_attribute *attr, char *buf) +{ + unsigned int pwr_value = 0; + int transceiver_index; + int retval=0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + transceiver_index = get_transceiver_index(kobj); + if(transceiver_index < 0) + { + TRANSCEIVER_DEBUG("[%s] Get transceiver index failed.\n", __func__); + ERROR_RETURN_NA(-EINVAL); + } + + pwr_value = cb_func->get_eth_power_on(transceiver_index); +#ifdef C11_ANNEX_K + retval = sprintf_s(buf, PAGE_SIZE, "%d\n", pwr_value); +#else + retval = sprintf(buf, "%d\n", pwr_value); +#endif + + + return retval; +} + +static ssize_t s3ip_set_eth_power_on(struct kobject *kobj, struct transceiver_attribute *attr, const char *buf, size_t count) +{ + unsigned int pwr_value = 0; + int transceiver_index = 0; + int retval=0; + + if(cb_func == NULL) + return -EIO; + + transceiver_index = get_transceiver_index(kobj); + if(transceiver_index < 0) + { + TRANSCEIVER_DEBUG("[%s] Get transceiver index failed.\n", __func__); + return -ENXIO; + } + + retval = kstrtoint(buf, 0, &pwr_value); + if(retval != 0) + { + return -EINVAL; + } + + cb_func->set_eth_power_on(transceiver_index, pwr_value); + + return count; +} + +static ssize_t s3ip_get_eth_reset(struct kobject *kobj, struct transceiver_attribute *attr, char *buf) +{ + unsigned int rst_value = 0; + int transceiver_index = 0; + int retval=0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + transceiver_index = get_transceiver_index(kobj); + if(transceiver_index < 0) + { + TRANSCEIVER_DEBUG("[%s] Get transceiver index failed.\n", __func__); + ERROR_RETURN_NA(-EINVAL); + } + + rst_value = cb_func->get_eth_reset(transceiver_index); +#ifdef C11_ANNEX_K + retval = sprintf_s(buf, PAGE_SIZE, "%d\n", rst_value); +#else + retval = sprintf(buf, "%d\n", rst_value); +#endif + + + return retval; +} + +static ssize_t s3ip_set_eth_reset(struct kobject *kobj, struct transceiver_attribute *attr, const char *buf, size_t count) +{ + unsigned int rst_value = 0; + int transceiver_index = 0; + int retval=0; + + if(cb_func == NULL) + return -EIO; + + transceiver_index = get_transceiver_index(kobj); + if(transceiver_index < 0) + { + TRANSCEIVER_DEBUG("[%s] Get transceiver index failed.\n", __func__); + return -ENXIO; + } + + retval = kstrtoint(buf, 0, &rst_value); + if(retval != 0) + { + return -EINVAL; + } + + cb_func->set_eth_reset(transceiver_index, rst_value); + + return count; +} + +static ssize_t s3ip_get_eth_lpmode(struct kobject *kobj, struct transceiver_attribute *attr, char *buf) +{ + unsigned int lpmode_value = 0; + int transceiver_index = 0; + int retval=0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + transceiver_index = get_transceiver_index(kobj); + if(transceiver_index < 0) + { + TRANSCEIVER_DEBUG("[%s] Get transceiver index failed.\n", __func__); + ERROR_RETURN_NA(-EINVAL); + } + + retval = cb_func->get_eth_lpmode(transceiver_index, &lpmode_value); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", lpmode_value); +#else + return sprintf(buf, "%d\n", lpmode_value); +#endif + +} + +static ssize_t s3ip_set_eth_lpmode(struct kobject *kobj, struct transceiver_attribute *attr, const char *buf, size_t count) +{ + unsigned int lpmode_value = 0; + int transceiver_index = 0; + int retval=0; + + if(cb_func == NULL) + return -EIO; + + transceiver_index = get_transceiver_index(kobj); + if(transceiver_index < 0) + { + TRANSCEIVER_DEBUG("[%s] Get transceiver index failed.\n", __func__); + return -ENXIO; + } + + retval = kstrtoint(buf, 0, &lpmode_value); + if(retval != 0) + { + return -EINVAL; + } + + retval = cb_func->set_eth_lpmode(transceiver_index, lpmode_value); + if(retval < 0) + { + return retval; + } + + return count; +} + +static ssize_t s3ip_get_temp(struct kobject *kobj, struct transceiver_attribute *attr, char *buf) +{ + int transceiver_index = 0; + long long temp = 0; + int retval=0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + transceiver_index = get_transceiver_index(kobj); + if(transceiver_index < 0) + { + TRANSCEIVER_DEBUG("[%s] Get transceiver index failed.\n", __func__); + ERROR_RETURN_NA(-EINVAL); + } + + retval = cb_func->get_eth_temp(transceiver_index, &temp); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%s%lld.%03lld\n", ((temp < 0) ? "-":""), abs(temp/multiplier), abs(temp%multiplier)/100000); // return value foramt: 33.123 +#else + return sprintf(buf, "%s%lld.%03lld\n", ((temp < 0) ? "-":""), abs(temp/multiplier), abs(temp%multiplier)/100000); // return value foramt: 33.123 +#endif + +} + +static ssize_t s3ip_get_eth_present(struct kobject *kobj, struct transceiver_attribute *attr, char *buf) +{ + unsigned int present_value = 0; + int transceiver_index = 0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + transceiver_index = get_transceiver_index(kobj); + if(transceiver_index < 0) + { + TRANSCEIVER_DEBUG("[%s] Get transceiver index failed.\n", __func__); + ERROR_RETURN_NA(-EINVAL); + } + + present_value = cb_func->get_eth_present(transceiver_index); +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", present_value); +#else + return sprintf(buf, "%d\n", present_value); +#endif + +} + +static ssize_t s3ip_get_eth_interrupt(struct kobject *kobj, struct transceiver_attribute *attr, char *buf) +{ + unsigned int interrupt_value = 0; + int transceiver_index = 0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + transceiver_index = get_transceiver_index(kobj); + if(transceiver_index < 0) + { + TRANSCEIVER_DEBUG("[%s] Get transceiver index failed.\n", __func__); + ERROR_RETURN_NA(-EINVAL); + } + + interrupt_value = cb_func->get_eth_interrupt(transceiver_index); +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", interrupt_value); +#else + return sprintf(buf, "%d\n", interrupt_value); +#endif + +} + +static ssize_t s3ip_get_diagnostic(struct kobject *kobj, struct transceiver_attribute *attr, char *buf) +{ + int retval=0; + int transceiver_index = 0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + transceiver_index = get_transceiver_index(kobj); + if(transceiver_index < 0) + { + TRANSCEIVER_DEBUG("[%s] Get transceiver index failed.\n", __func__); + ERROR_RETURN_NA(-EINVAL); + } + + retval = cb_func->get_eth_diagnostic(transceiver_index, buf); + + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { + return retval; + } +} + +static ssize_t s3ip_get_rx_los(struct kobject *kobj, struct transceiver_attribute *attr, char *buf) +{ + int transceiver_index = 0; + unsigned char rx_los_buf[2] = {0}; + unsigned char lane_num = 0; + unsigned char rx_los = 0; + int retval=0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + transceiver_index = get_transceiver_index(kobj); + if(transceiver_index < 0) + { + TRANSCEIVER_DEBUG("[%s] Get transceiver index failed.\n", __func__); + ERROR_RETURN_NA(-EINVAL); + } + + retval = cb_func->get_eth_rx_los(transceiver_index, rx_los_buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + + lane_num = rx_los_buf[0]; + rx_los = rx_los_buf[1]; +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "0x%02X\n", rx_los); +#else + return sprintf(buf, "0x%02X\n", rx_los); +#endif + +} + +static ssize_t s3ip_get_tx_los(struct kobject *kobj, struct transceiver_attribute *attr, char *buf) +{ + int transceiver_index = 0; + unsigned char tx_los_buf[2] = {0}; + unsigned char lane_num = 0; + unsigned char tx_los = 0; + int retval=0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + transceiver_index = get_transceiver_index(kobj); + if(transceiver_index < 0) + { + TRANSCEIVER_DEBUG("[%s] Get transceiver index failed.\n", __func__); + ERROR_RETURN_NA(-EINVAL); + } + + retval = cb_func->get_eth_tx_los(transceiver_index, tx_los_buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + + lane_num = tx_los_buf[0]; + tx_los = tx_los_buf[1]; +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "0x%02X\n", tx_los); +#else + return sprintf(buf, "0x%02X\n", tx_los); +#endif + +} + +static ssize_t s3ip_get_tx_disable(struct kobject *kobj, struct transceiver_attribute *attr, char *buf) +{ + int transceiver_index = 0; + unsigned char tx_disable_buf[2] = {0}; + unsigned char lane_num = 0; + unsigned char tx_disable = 0; + int retval=0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + transceiver_index = get_transceiver_index(kobj); + if(transceiver_index < 0) + { + TRANSCEIVER_DEBUG("[%s] Get transceiver index failed.\n", __func__); + ERROR_RETURN_NA(-EINVAL); + } + + retval = cb_func->get_eth_tx_disable(transceiver_index, tx_disable_buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + + lane_num = tx_disable_buf[0]; + tx_disable = tx_disable_buf[1]; +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "0x%02X\n", tx_disable); +#else + return sprintf(buf, "0x%02X\n", tx_disable); +#endif + +} + +static ssize_t s3ip_set_tx_disable(struct kobject *kobj, struct transceiver_attribute *attr, const char *buf, size_t count) +{ + int transceiver_index = 0; + unsigned int tx_disable_state = 0; + unsigned char tx_disable = 0; + int retval=0; + + if(cb_func == NULL) + return -EIO; + + transceiver_index = get_transceiver_index(kobj); + if(transceiver_index < 0) + { + TRANSCEIVER_DEBUG("[%s] Get transceiver index failed.\n", __func__); + return -ENXIO; + } + + retval = kstrtoint(buf, 0, &tx_disable_state); + if(retval != 0) + { + return -EINVAL; + } + + if(tx_disable_state == 0) + { + tx_disable = 0x0; + } + else + { + tx_disable = 0xFF; + } + + retval = cb_func->set_eth_tx_disable(transceiver_index, tx_disable); + if(retval < 0) + { + return retval; + } + + return count; +} + +static ssize_t s3ip_get_rx_disable(struct kobject *kobj, struct transceiver_attribute *attr, char *buf) +{ + int transceiver_index = 0; + unsigned char rx_disable_buf[2] = {0}; + unsigned char lane_num = 0; + unsigned char rx_disable = 0; + int retval=0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + transceiver_index = get_transceiver_index(kobj); + if(transceiver_index < 0) + { + TRANSCEIVER_DEBUG("[%s] Get transceiver index failed.\n", __func__); + ERROR_RETURN_NA(-EINVAL); + } + + retval = cb_func->get_eth_rx_disable(transceiver_index, rx_disable_buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + + lane_num = rx_disable_buf[0]; + rx_disable = rx_disable_buf[1]; +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "0x%02X\n", rx_disable); +#else + return sprintf(buf, "0x%02X\n", rx_disable); +#endif + +} + +static ssize_t s3ip_get_tx_cdr_lol(struct kobject *kobj, struct transceiver_attribute *attr, char *buf) +{ + int transceiver_index = 0; + unsigned char tx_cdr_lol_buf[2] = {0}; + unsigned char lane_num = 0; + unsigned char tx_cdr_lol = 0; + int retval=0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + transceiver_index = get_transceiver_index(kobj); + if(transceiver_index < 0) + { + TRANSCEIVER_DEBUG("[%s] Get transceiver index failed.\n", __func__); + ERROR_RETURN_NA(-EINVAL); + } + + retval = cb_func->get_eth_tx_cdr_lol(transceiver_index, tx_cdr_lol_buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + + lane_num = tx_cdr_lol_buf[0]; + tx_cdr_lol = tx_cdr_lol_buf[1]; +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "0x%02X\n", tx_cdr_lol); +#else + return sprintf(buf, "0x%02X\n", tx_cdr_lol); +#endif + +} + +static ssize_t s3ip_get_rx_cdr_lol(struct kobject *kobj, struct transceiver_attribute *attr, char *buf) +{ + int transceiver_index = 0; + unsigned char rx_cdr_lol_buf[2] = {0}; + unsigned char lane_num = 0; + unsigned char rx_cdr_lol = 0; + int retval=0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + transceiver_index = get_transceiver_index(kobj); + if(transceiver_index < 0) + { + TRANSCEIVER_DEBUG("[%s] Get transceiver index failed.\n", __func__); + ERROR_RETURN_NA(-EINVAL); + } + + retval = cb_func->get_eth_rx_cdr_lol(transceiver_index, rx_cdr_lol_buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + + lane_num = rx_cdr_lol_buf[0]; + rx_cdr_lol = rx_cdr_lol_buf[1]; +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "0x%02X\n", rx_cdr_lol); +#else + return sprintf(buf, "0x%02X\n", rx_cdr_lol); +#endif + +} + +static ssize_t s3ip_get_tx_fault(struct kobject *kobj, struct transceiver_attribute *attr, char *buf) +{ + int transceiver_index = 0; + unsigned char tx_fault_buf[2] = {0}; + unsigned char lane_num = 0; + unsigned char tx_fault = 0; + int retval=0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + transceiver_index = get_transceiver_index(kobj); + if(transceiver_index < 0) + { + TRANSCEIVER_DEBUG("[%s] Get transceiver index failed.\n", __func__); + ERROR_RETURN_NA(-EINVAL); + } + + retval = cb_func->get_eth_tx_fault(transceiver_index, tx_fault_buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + + lane_num = tx_fault_buf[0]; + tx_fault = tx_fault_buf[1]; +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "0x%02X\n", tx_fault); +#else + return sprintf(buf, "0x%02X\n", tx_fault); +#endif + +} + +static ssize_t s3ip_get_module_status(struct kobject *kobj, struct transceiver_attribute *attr, char *buf) +{ + int transceiver_index = 0; + unsigned char module_status = 0; + int retval=0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + transceiver_index = get_transceiver_index(kobj); + if(transceiver_index < 0) + { + TRANSCEIVER_DEBUG("[%s] Get transceiver index failed.\n", __func__); + ERROR_RETURN_NA(-EINVAL); + } + + retval = cb_func->get_eth_module_status(transceiver_index, &module_status); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "0x%X\n", module_status); +#else + return sprintf(buf, "0x%X\n", module_status); +#endif + +} + +static ssize_t s3ip_get_datapath_status(struct kobject *kobj, struct transceiver_attribute *attr, char *buf) +{ + int transceiver_index = 0; + unsigned int datapath_status = 0; + unsigned int datapath_status_buf[2] = {0}; + unsigned char lane_num = 0; + int retval=0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + transceiver_index = get_transceiver_index(kobj); + if(transceiver_index < 0) + { + TRANSCEIVER_DEBUG("[%s] Get transceiver index failed.\n", __func__); + ERROR_RETURN_NA(-EINVAL); + } + + retval = cb_func->get_eth_datapath_status(transceiver_index, datapath_status_buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + + lane_num = datapath_status_buf[0]; + datapath_status = datapath_status_buf[1]; +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "0x%08X\n", datapath_status); +#else + return sprintf(buf, "0x%08X\n", datapath_status); +#endif + +} + +static ssize_t s3ip_get_eth_eeprom(struct file *filp, struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t off, size_t count) +{ + int retval=0; + int transceiver_index = 0; + + if(cb_func == NULL) + { + return -EIO; + } + + transceiver_index = get_transceiver_index(kobj); + if(transceiver_index < 0) + { + TRANSCEIVER_DEBUG("[%s] Get transceiver index failed.\n", __func__); + return -EIO; + } + + retval = cb_func->get_eth_eeprom(transceiver_index, buf, off, count); + if(retval < 0) + { + return -EIO; + } + else + { + return retval; + } +} + +static ssize_t s3ip_set_eth_eeprom(struct file *filp, struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t off, size_t count) +{ + int transceiver_index = 0; + + if(cb_func == NULL) + return -EIO; + + transceiver_index = get_transceiver_index(kobj); + if(transceiver_index < 0) + { + TRANSCEIVER_DEBUG("[%s] Get transceiver index failed.\n", __func__); + return -ENXIO; + } + + if(count > ONE_ADDR_EEPROM_SIZE) + { + return -EINVAL; + } + + return cb_func->set_eth_eeprom(transceiver_index, buf, off, count); +} + +static char transceiver_temp_type[TRANSCEIVER_TOTAL_NUM] = {0}; + +static ssize_t s3ip_get_eth_temp_type(struct kobject *kobj, struct transceiver_attribute *attr, char *buf) +{ + int transceiver_index; + int retval=0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + transceiver_index = get_transceiver_index(kobj); + if(transceiver_index < 0) + { + TRANSCEIVER_DEBUG("[%s] Get transceiver index failed.\n", __func__); + ERROR_RETURN_NA(-EINVAL); + } + + if((transceiver_index >= 1) && (transceiver_index <= 32)) + { +#ifdef C11_ANNEX_K + retval = sprintf_s(buf, PAGE_SIZE, "%d\n",transceiver_temp_type[transceiver_index-1]); +#else + retval = sprintf(buf, "%d\n",transceiver_temp_type[transceiver_index-1]); +#endif + + }else{ + return -EINVAL; + } + + return retval; +} + +static ssize_t s3ip_set_eth_temp_type(struct kobject *kobj, struct transceiver_attribute *attr, const char *buf, size_t count) +{ + unsigned int tempTpye_value = 0; + int transceiver_index = 0; + int retval=0; + + if(cb_func == NULL) + return -EIO; + + transceiver_index = get_transceiver_index(kobj); + if(transceiver_index < 0) + { + TRANSCEIVER_DEBUG("[%s] Get transceiver index failed.\n", __func__); + return -ENXIO; + } + + retval = kstrtoint(buf, 0, &tempTpye_value); + if(retval != 0) + { + return -EINVAL; + } + if((transceiver_index >= 1) && (transceiver_index <= 32)) + { + transceiver_temp_type[transceiver_index-1] = tempTpye_value; + }else{ + return -EINVAL; + } + + return count; +} + +static ssize_t s3ip_get_eth_led_status(struct kobject *kobj, struct transceiver_attribute *attr, char *buf) +{ + unsigned int led_value = 0; + int transceiver_index = 0; + int retval=0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + transceiver_index = get_transceiver_index(kobj); + if(transceiver_index < 0) + { + TRANSCEIVER_DEBUG("[%s] Get transceiver index failed.\n", __func__); + ERROR_RETURN_NA(-EINVAL); + } + + led_value = cb_func->get_eth_led_status(transceiver_index); +#ifdef C11_ANNEX_K + retval = sprintf_s(buf, PAGE_SIZE, "0x%x\n", led_value); +#else + retval = sprintf(buf, "0x%x\n", led_value); +#endif + + + return retval; +} + + +static ssize_t s3ip_set_eth_led_status(struct kobject *kobj, struct transceiver_attribute *attr, const char *buf, size_t count) +{ + unsigned int led_value = 0; + int transceiver_index = 0; + int retval=0; + + if(cb_func == NULL) + return -EIO; + + transceiver_index = get_transceiver_index(kobj); + if(transceiver_index < 0) + { + TRANSCEIVER_DEBUG("[%s] Get transceiver index failed.\n", __func__); + return -ENXIO; + } + + retval = kstrtoint(buf, 0, &led_value); + if(retval != 0) + { + return -EINVAL; + } + + cb_func->set_eth_led_status(transceiver_index, led_value); + + return count; +} + + +static struct transceiver_attribute transceiver_attr[NUM_TRANSCEIVER_ATTR] = { + [DEBUG] = {{.name = "debug", .mode = S_IRUGO | S_IWUSR}, s3ip_debug_help, s3ip_debug}, + [LOGLEVEL] = {{.name = "loglevel", .mode = S_IRUGO | S_IWUSR}, s3ip_get_loglevel, s3ip_set_loglevel}, + [POWER_ON] = {{.name = "power_on", .mode = S_IRUGO | S_IWUSR}, s3ip_get_power_on, NULL}, + [PRESENT] = {{.name = "present", .mode = S_IRUGO}, s3ip_get_present, NULL}, + [NUM] = {{.name = "num", .mode = S_IRUGO}, s3ip_get_num, NULL}, + [ETH_POWER_ON] = {{.name = "power_on", .mode = S_IRUGO | S_IWUSR}, s3ip_get_eth_power_on, s3ip_set_eth_power_on}, + [ETH_RESET] = {{.name = "reset", .mode = S_IRUGO | S_IWUSR}, s3ip_get_eth_reset, s3ip_set_eth_reset}, + [ETH_LPMODE] = {{.name = "lpmode", .mode = S_IRUGO | S_IWUSR}, s3ip_get_eth_lpmode, s3ip_set_eth_lpmode}, + [ETH_TEMP] = {{.name = "temp_input", .mode = S_IRUGO}, s3ip_get_temp, NULL}, + [ETH_PRESENT] = {{.name = "present", .mode = S_IRUGO}, s3ip_get_eth_present, NULL}, + [ETH_INTERRUPT] = {{.name = "interrupt", .mode = S_IRUGO}, s3ip_get_eth_interrupt, NULL}, + [ETH_DIAGNOSTIC] = {{.name = "diagnostic", .mode = S_IRUGO}, s3ip_get_diagnostic, NULL}, + [ETH_RX_LOS] = {{.name = "rx_los", .mode = S_IRUGO}, s3ip_get_rx_los, NULL}, + [ETH_TX_LOS] = {{.name = "tx_los", .mode = S_IRUGO}, s3ip_get_tx_los, NULL}, + [ETH_TX_DISABLE] = {{.name = "tx_disable", .mode = S_IRUGO | S_IWUSR}, s3ip_get_tx_disable, s3ip_set_tx_disable}, + [ETH_RX_DISABLE] = {{.name = "rx_disable", .mode = S_IRUGO}, s3ip_get_rx_disable, NULL}, + [ETH_TX_CDR_LOL] = {{.name = "tx_cdr_lol", .mode = S_IRUGO}, s3ip_get_tx_cdr_lol, NULL}, + [ETH_RX_CDR_LOL] = {{.name = "rx_cdr_lol", .mode = S_IRUGO}, s3ip_get_rx_cdr_lol, NULL}, + [ETH_TX_FAULT] = {{.name = "tx_fault", .mode = S_IRUGO}, s3ip_get_tx_fault, NULL}, + [ETH_MODULE_STATUS] = {{.name = "module_status", .mode = S_IRUGO}, s3ip_get_module_status, NULL}, + [ETH_DATAPATH_STATUS] = {{.name = "datapath_status", .mode = S_IRUGO}, s3ip_get_datapath_status, NULL}, + [ETH_TEMP_TYPE] = {{.name = "temp_type", .mode = S_IRUGO | S_IWUSR}, s3ip_get_eth_temp_type, s3ip_set_eth_temp_type}, + [ETH_LED_STATUS] = {{.name = "led_status", .mode = S_IRUGO | S_IWUSR}, s3ip_get_eth_led_status, s3ip_set_eth_led_status}, +#if 0 /* use bin_attribute instead of kobj create add */ + [ETH_EEPROM] = {{.name = "eeprom", .mode = S_IRUGO | S_IWUSR}, s3ip_get_eth_eeprom, s3ip_set_eth_eeprom}, +#endif +}; + +static struct bin_attribute bin_attr; + +void s3ip_transceiver_drivers_register(struct transceiver_drivers_t *pfunc) +{ + cb_func = pfunc; + + return; +} +EXPORT_SYMBOL_GPL(s3ip_transceiver_drivers_register); + +void s3ip_transceiver_drivers_unregister(void) +{ + cb_func = NULL; + + return; +} +EXPORT_SYMBOL_GPL(s3ip_transceiver_drivers_unregister); + +static int __init switch_transceiver_init(void) +{ + int err=0; + int retval=0; + int i; + int transceiver_index; + char *transceiver_index_str; + + transceiver_index_str = (char *)kzalloc(sizeof(ETH_NAME_STRING)+3, GFP_KERNEL); + if (!transceiver_index_str) + { + TRANSCEIVER_DEBUG( "[%s] Fail to alloc transceiver_index_str memory\n", __func__); + return -ENOMEM; + } + + /* init bin attribute for the sysfs eeprom file */ + sysfs_bin_attr_init(bin_attr); + bin_attr.attr.name = "eeprom"; + bin_attr.attr.mode = (S_IRUGO | S_IWUSR); + bin_attr.read = s3ip_get_eth_eeprom; + bin_attr.write = s3ip_set_eth_eeprom; + bin_attr.size = ONE_ADDR_EEPROM_SIZE; + + /* For s3ip */ + transceiver_kobj = kobject_create_and_add(TRANSCEIVER_NAME_STRING, switch_kobj); + if(!transceiver_kobj) + { + TRANSCEIVER_DEBUG( "[%s]Failed to create 'transceiver'\n", __func__); + err = -ENOMEM; + goto sysfs_create_kobject_transceiver_failed; + } + + for(i=0; i <= NUM; i++) + { + TRANSCEIVER_DEBUG( "[%s]sysfs_create_file /transceiver/%s\n", __func__, transceiver_attr[i].attr.name); + retval = sysfs_create_file(transceiver_kobj, &transceiver_attr[i].attr); + if(retval) + { + printk(KERN_ERR "Failed to create file '%s'\n", transceiver_attr[i].attr.name); + err = -retval; + goto sysfs_create_s3ip_attr_failed; + } + } + + for(transceiver_index=0; transceiver_index /* + => module_init() + => module_exit() + => MODULE_LICENSE() + => MODULE_VERSION() + => MODULE_AUTHOR() + => struct module + */ +#include /* + => typedef int (*initcall_t)(void); + Note: the 'initcall_t' function returns 0 when succeed. + In the Linux kernel, error codes are negative numbers + belonging to the set defined in . + => typedef void (*exitcall_t)(void); + => __init + => __exit + */ +#include /* + => moduleparam() + */ +#include /* + => dev_t (u32) + */ +#include /* + => MAJOR() + => MINOR() + */ +#include /* + => void *memset() + */ +#include /* + => void kfree() + */ +#include +#include +#include +#include /* + => struct spinlock + */ +#include + +#include +#include +#include + +#include "switch_wdt_driver.h" + +#define ERROR_DEBUG(...) printk(KERN_ALERT __VA_ARGS__) + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE (!FALSE) +#endif + +#define SWITCH_S3IP_WATCHDOG_VERSION "0.0.0.1" + +unsigned int loglevel = 0; + +struct wdt_attribute{ + struct attribute attr; + ssize_t (*show)(struct kobject *obj, struct wdt_attribute *attr, char *buf); + ssize_t (*store)(struct kobject *obj, struct wdt_attribute *attr, const char *buf, size_t count); +}; + +struct wdt_drivers_t *cb_func = NULL; + +/* For s3ip */ +extern struct kobject *switch_kobj; +static struct kobject *watchdog_kobj; + +enum wdt_attrs { + DEBUG, + LOGLEVEL, + IDENTIFY, + STATE, + TIMELEFT, + TIMEOUT, + RESET, + ENABLE, + NUM_WDT_ATTR, +}; + +static ssize_t s3ip_debug_help(struct kobject *kobj, struct wdt_attribute *attr, char *buf) +{ + int retval=0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + retval = cb_func->debug_help(buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { + return retval; + } +} + +static ssize_t s3ip_debug(struct kobject *kobj, struct wdt_attribute *attr, const char *buf, size_t count) +{ + return count; +} + +static ssize_t s3ip_get_loglevel(struct kobject *kobj, struct wdt_attribute *attr, char *buf) +{ +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", loglevel); +#else + return sprintf(buf, "%d\n", loglevel); +#endif +} + +static ssize_t s3ip_set_loglevel(struct kobject *kobj, struct wdt_attribute *attr, const char *buf, size_t count) +{ + int retval=0; + long lev; + + if(cb_func == NULL) + return -1; + + retval = kstrtol(buf, 0, &lev); + if(retval == 0) + { + WDT_DEBUG("lev:%ld \n", lev); + } + else + { + WDT_DEBUG("Error:%d, lev:%s \n", retval, buf); + return -1; + } + + loglevel = (unsigned int)lev; + + cb_func->set_loglevel(lev); + + return count; +} + +static ssize_t s3ip_get_identify(struct kobject *kobj, struct wdt_attribute *attr, char *buf) +{ + int retval=0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + retval = cb_func->get_identify(buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { + return retval; + } +} + +static ssize_t s3ip_get_state(struct kobject *kobj, struct wdt_attribute *attr, char *buf) +{ + int retval=0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + retval = cb_func->get_state(buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { + return retval; + } +} + +static ssize_t s3ip_get_timeleft(struct kobject *kobj, struct wdt_attribute *attr, char *buf) +{ + int retval=0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + retval = cb_func->get_timeleft(buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { + return retval; + } +} + +static ssize_t s3ip_get_timeout(struct kobject *kobj, struct wdt_attribute *attr, char *buf) +{ + int retval=0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + retval = cb_func->get_timeout(buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { + return retval; + } +} + +static ssize_t s3ip_set_timeout(struct kobject *kobj, struct wdt_attribute *attr, const char *buf, size_t count) +{ + int retval=0; + unsigned short timeout; + + if(cb_func == NULL) + return -1; + + retval = kstrtou16(buf, 10, &timeout); + if(retval == 0) + { + WDT_DEBUG("timeout:%d \n", timeout); + } + else + { + WDT_DEBUG("Error:%d, timeout:%s \n", retval, buf); + return -1; + } + + cb_func->set_timeout(timeout); + + return count; +} + +static ssize_t s3ip_set_reset(struct kobject *kobj, struct wdt_attribute *attr, const char *buf, size_t count) +{ + int retval=0; + unsigned int reset; + + if(cb_func == NULL) + return -1; + + retval = kstrtouint(buf, 10, &reset); + if(retval == 0) + { + WDT_DEBUG("reset:%d \n", reset); + } + else + { + WDT_DEBUG("Error:%d, reset:%s \n", retval, buf); + return -1; + } + + cb_func->set_reset(reset); + + return count; +} + +static ssize_t s3ip_get_enable(struct kobject *kobj, struct wdt_attribute *attr, char *buf) +{ + int retval=0; + + if(cb_func == NULL) + { + ERROR_RETURN_NA(-1); + } + + retval = cb_func->get_enable(buf); + if(retval < 0) + { + ERROR_RETURN_NA(retval); + } + else + { + return retval; + } +} + +static ssize_t s3ip_set_enable(struct kobject *kobj, struct wdt_attribute *attr, const char *buf, size_t count) +{ + int retval=0; + unsigned int enable; + + if(cb_func == NULL) + return -1; + + retval = kstrtouint(buf, 10, &enable); + if(retval == 0) + { + WDT_DEBUG("enable:%d \n", enable); + } + else + { + WDT_DEBUG("Error:%d, enable:%s \n", retval, buf); + return -1; + } + + cb_func->set_enable(enable); + + return count; +} + +static struct wdt_attribute wdt_attr[NUM_WDT_ATTR] = { + [DEBUG] = {{.name = "debug", .mode = S_IRUGO | S_IWUSR}, s3ip_debug_help, s3ip_debug}, + [LOGLEVEL] = {{.name = "loglevel", .mode = S_IRUGO | S_IWUSR}, s3ip_get_loglevel, s3ip_set_loglevel}, + [IDENTIFY] = {{.name = "identify", .mode = S_IRUGO}, s3ip_get_identify, NULL}, + [STATE] = {{.name = "state", .mode = S_IRUGO}, s3ip_get_state, NULL}, + [TIMELEFT] = {{.name = "timeleft", .mode = S_IRUGO}, s3ip_get_timeleft, NULL}, + [TIMEOUT] = {{.name = "timeout", .mode = S_IRUGO | S_IWUSR}, s3ip_get_timeout, s3ip_set_timeout}, + [RESET] = {{.name = "reset", .mode = S_IWUSR}, NULL, s3ip_set_reset}, + [ENABLE] = {{.name = "enable", .mode = S_IRUGO | S_IWUSR}, s3ip_get_enable, s3ip_set_enable}, +}; + + +void s3ip_wdt_drivers_register(struct wdt_drivers_t *pfunc) +{ + cb_func = pfunc; + + return; +} +EXPORT_SYMBOL_GPL(s3ip_wdt_drivers_register); + +void s3ip_wdt_drivers_unregister(void) +{ + cb_func = NULL; + + return; +} +EXPORT_SYMBOL_GPL(s3ip_wdt_drivers_unregister); + +static int __init switch_wdt_init(void) +{ + int retval = 0; + int err=0; + int i; + + /* For s3ip */ + watchdog_kobj = kobject_create_and_add("watchdog", switch_kobj); + if(!watchdog_kobj) + { + WDT_DEBUG( "Failed to create 'watchdog'\n"); + return -ENOMEM; + } + + for(i=0; i < NUM_WDT_ATTR; i++) + { + WDT_DEBUG( "sysfs_create_file /watchdog/%s\n", wdt_attr[i].attr.name); + retval = sysfs_create_file(watchdog_kobj, &wdt_attr[i].attr); + if(retval) + { + printk(KERN_ERR "Failed to create file '%s'\n", wdt_attr[i].attr.name); + err = -retval; + goto sysfs_create_s3ip_watchdog_attr_failed; + } + } + + return 0; + +sysfs_create_s3ip_watchdog_attr_failed: + if(watchdog_kobj) + { + kobject_put(watchdog_kobj); + watchdog_kobj = NULL; + } + + return err; +} + +static void __exit switch_wdt_exit(void) +{ + int i; + + /* For s3ip */ + for(i=0; i < NUM_WDT_ATTR; i++) + sysfs_remove_file(watchdog_kobj, &wdt_attr[i].attr); + + if(watchdog_kobj) + { + kobject_put(watchdog_kobj); + watchdog_kobj = NULL; + } + + cb_func = NULL; + + return; +} + +MODULE_DESCRIPTION("Switch S3IP Watchdog Driver"); +MODULE_VERSION(SWITCH_S3IP_WATCHDOG_VERSION); +MODULE_LICENSE("GPL"); + +module_init(switch_wdt_init); +module_exit(switch_wdt_exit); diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch.h b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch.h new file mode 100644 index 0000000000..da26da5ec4 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch.h @@ -0,0 +1,61 @@ +#ifndef SWITCH_H +#define SWITCH_H +#ifdef C11_ANNEX_K +#include "libboundscheck/include/securec.h" +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE (!FALSE) +#endif + +enum LOG_LEVEL{ + ERR = 0x01, + WARNING = 0x02, + INFO = 0x04, + DBG = 0x08, +}; + +#define LOG_ERR(_prefix, fmt, args...) \ + do { \ + if (loglevel & ERR) \ + { \ + printk( KERN_ERR _prefix "%s "fmt, __FUNCTION__, ##args); \ + } \ + } while (0) + +#define LOG_WARNING(_prefix, fmt, args...) \ + do { \ + if (loglevel & WARNING) \ + { \ + printk( KERN_WARNING _prefix "%s "fmt, __FUNCTION__, ##args); \ + } \ + } while (0) + +#define LOG_INFO(_prefix, fmt, args...) \ + do { \ + if (loglevel & INFO) \ + { \ + printk( KERN_INFO _prefix "%s "fmt, __FUNCTION__, ##args); \ + } \ + } while (0) + +#define LOG_DBG(_prefix, fmt, args...) \ + do { \ + if (loglevel & DBG) \ + { \ + printk( KERN_DEBUG _prefix "%s "fmt, __FUNCTION__, ##args); \ + } \ + } while (0) + +// For S3IP always return success with NA for HW fail. +#ifdef C11_ANNEX_K +#define ERROR_RETURN_NA(ret) do { return sprintf_s(buf, PAGE_SIZE, "%s\n", "NA"); } while(0) +#else +#define ERROR_RETURN_NA(ret) do { return sprintf(buf, "%s\n", "NA"); } while(0) +#endif + +#endif /* SWITCH_H */ diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_avs_driver.c b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_avs_driver.c new file mode 100644 index 0000000000..99c796bb7f --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_avs_driver.c @@ -0,0 +1,706 @@ +#include +#include +#include +#include +#include +#include + +#include "pmbus.h" +#include "switch_avs_driver.h" +#include "sysfs_ipmi.h" + +#define DRVNAME "drv_avs_driver" +#define SWITCH_AVS_DRIVER_VERSION "0.0.1" + +unsigned int loglevel = 0; +static struct platform_device *drv_avs_device; + +struct mutex update_lock; + +static unsigned int avs_i2c_bus[TOTAL_FMEA_AVS_NUM] = {302, 303}; //AVS I2C bus number for TD4 platform +static unsigned short avs_i2c_addr[TOTAL_FMEA_AVS_NUM] = {0x20, 0x70}; + + +typedef struct avs_fmea_dump_reg_s { + int page; + int reg; + PMBUS_PROTOCAL_TYPE_E type; +}AVS_FMEA_DUMP_REG_T; + +static AVS_FMEA_DUMP_REG_T mp2882_fmea_dump_reg_list[] = { + /* Page 0 */ + {0, 0x20, PMBUS_PROTOCAL_TYPE_BYTE}, + {0, 0x21, PMBUS_PROTOCAL_TYPE_WORD}, + {0, 0x79, PMBUS_PROTOCAL_TYPE_WORD}, + {0, 0x7A, PMBUS_PROTOCAL_TYPE_WORD}, + {0, 0x7B, PMBUS_PROTOCAL_TYPE_WORD}, + {0, 0x7C, PMBUS_PROTOCAL_TYPE_WORD}, + {0, 0x7D, PMBUS_PROTOCAL_TYPE_WORD}, + {0, 0x7E, PMBUS_PROTOCAL_TYPE_WORD}, + {0, 0x88, PMBUS_PROTOCAL_TYPE_WORD}, + {0, 0x8B, PMBUS_PROTOCAL_TYPE_WORD}, + {0, 0x8C, PMBUS_PROTOCAL_TYPE_WORD}, + {0, 0x8D, PMBUS_PROTOCAL_TYPE_WORD}, + {0, 0x96, PMBUS_PROTOCAL_TYPE_WORD}, + {0, 0x9A, PMBUS_PROTOCAL_TYPE_BLOCK}, + {0, 0x9D, PMBUS_PROTOCAL_TYPE_BLOCK}, + {0, 0xD0, PMBUS_PROTOCAL_TYPE_WORD}, + {0, 0xD1, PMBUS_PROTOCAL_TYPE_WORD}, + {0, 0xD2, PMBUS_PROTOCAL_TYPE_WORD}, + {0, 0xD3, PMBUS_PROTOCAL_TYPE_WORD}, + {0, 0xD4, PMBUS_PROTOCAL_TYPE_WORD}, + {0, 0xD5, PMBUS_PROTOCAL_TYPE_WORD}, + {0, 0xD6, PMBUS_PROTOCAL_TYPE_WORD}, + {0, 0xD7, PMBUS_PROTOCAL_TYPE_WORD}, + {0, 0xD8, PMBUS_PROTOCAL_TYPE_WORD}, + {0, 0xD9, PMBUS_PROTOCAL_TYPE_WORD}, + {0, 0xDA, PMBUS_PROTOCAL_TYPE_WORD}, + {0, 0xDB, PMBUS_PROTOCAL_TYPE_WORD}, + {0, 0xDC, PMBUS_PROTOCAL_TYPE_WORD}, + {0, 0xDD, PMBUS_PROTOCAL_TYPE_WORD}, + {0, 0xDE, PMBUS_PROTOCAL_TYPE_WORD}, + {0, 0xDF, PMBUS_PROTOCAL_TYPE_WORD}, + /* Page 1 */ + {1, 0x20, PMBUS_PROTOCAL_TYPE_BYTE}, + {1, 0x21, PMBUS_PROTOCAL_TYPE_WORD}, + {1, 0x79, PMBUS_PROTOCAL_TYPE_WORD}, + {1, 0x7A, PMBUS_PROTOCAL_TYPE_WORD}, + {1, 0x7B, PMBUS_PROTOCAL_TYPE_WORD}, + {1, 0x7C, PMBUS_PROTOCAL_TYPE_WORD}, + {1, 0x7D, PMBUS_PROTOCAL_TYPE_WORD}, + {1, 0x7E, PMBUS_PROTOCAL_TYPE_WORD}, + {1, 0x88, PMBUS_PROTOCAL_TYPE_WORD}, + {1, 0x8B, PMBUS_PROTOCAL_TYPE_WORD}, + {1, 0x8C, PMBUS_PROTOCAL_TYPE_WORD}, + {1, 0x8D, PMBUS_PROTOCAL_TYPE_WORD}, + {1, 0x96, PMBUS_PROTOCAL_TYPE_WORD}, + {1, 0xD0, PMBUS_PROTOCAL_TYPE_WORD}, + {1, 0xD1, PMBUS_PROTOCAL_TYPE_WORD}, + {1, 0xD2, PMBUS_PROTOCAL_TYPE_WORD}, + {1, 0xD3, PMBUS_PROTOCAL_TYPE_WORD}, + {1, 0xDA, PMBUS_PROTOCAL_TYPE_WORD}, + {1, 0xDB, PMBUS_PROTOCAL_TYPE_WORD}, + {1, 0xDC, PMBUS_PROTOCAL_TYPE_WORD}, + {1, 0xDD, PMBUS_PROTOCAL_TYPE_WORD}, + {1, 0xDE, PMBUS_PROTOCAL_TYPE_WORD}, + {1, 0xDF, PMBUS_PROTOCAL_TYPE_WORD}, + {1, 0xE0, PMBUS_PROTOCAL_TYPE_WORD}, + {1, 0xE1, PMBUS_PROTOCAL_TYPE_WORD}, + {1, 0xE2, PMBUS_PROTOCAL_TYPE_WORD}, + {1, 0xE3, PMBUS_PROTOCAL_TYPE_WORD}, + {1, 0xE4, PMBUS_PROTOCAL_TYPE_WORD}, + {1, 0xE5, PMBUS_PROTOCAL_TYPE_WORD}, + {1, 0xE6, PMBUS_PROTOCAL_TYPE_WORD}, +}; + +static AVS_FMEA_DUMP_REG_T mp2975_fmea_dump_reg_list[] = { + /* Page 0 */ + {0, 0x20, PMBUS_PROTOCAL_TYPE_BYTE}, + {0, 0x21, PMBUS_PROTOCAL_TYPE_WORD}, + {0, 0x79, PMBUS_PROTOCAL_TYPE_WORD}, + {0, 0x7A, PMBUS_PROTOCAL_TYPE_BYTE}, + {0, 0x7B, PMBUS_PROTOCAL_TYPE_BYTE}, + {0, 0x7C, PMBUS_PROTOCAL_TYPE_BYTE}, + {0, 0x7D, PMBUS_PROTOCAL_TYPE_BYTE}, + {0, 0x7E, PMBUS_PROTOCAL_TYPE_BYTE}, + {0, 0x80, PMBUS_PROTOCAL_TYPE_BYTE}, + {0, 0x82, PMBUS_PROTOCAL_TYPE_BYTE}, + {0, 0x83, PMBUS_PROTOCAL_TYPE_BYTE}, + {0, 0x84, PMBUS_PROTOCAL_TYPE_BYTE}, + {0, 0x88, PMBUS_PROTOCAL_TYPE_WORD}, + {0, 0x8B, PMBUS_PROTOCAL_TYPE_WORD}, + {0, 0x8C, PMBUS_PROTOCAL_TYPE_WORD}, + {0, 0x8D, PMBUS_PROTOCAL_TYPE_WORD}, + {0, 0x90, PMBUS_PROTOCAL_TYPE_BYTE}, + {0, 0x91, PMBUS_PROTOCAL_TYPE_BYTE}, + {0, 0x96, PMBUS_PROTOCAL_TYPE_WORD}, + {0, 0x9A, PMBUS_PROTOCAL_TYPE_WORD}, + {0, 0x9D, PMBUS_PROTOCAL_TYPE_BLOCK}, + {0, 0xA4, PMBUS_PROTOCAL_TYPE_BYTE}, + {0, 0xA5, PMBUS_PROTOCAL_TYPE_BYTE}, + {0, 0xF4, PMBUS_PROTOCAL_TYPE_BYTE} +}; + +static AVS_FMEA_DUMP_REG_T ph86b03_fmea_dump_reg_list[] = { + /* Page 0 */ + {0, 0x20, PMBUS_PROTOCAL_TYPE_BYTE}, + {0, 0x21, PMBUS_PROTOCAL_TYPE_WORD}, + {0, 0x79, PMBUS_PROTOCAL_TYPE_WORD}, + {0, 0x7A, PMBUS_PROTOCAL_TYPE_BYTE}, + {0, 0x7B, PMBUS_PROTOCAL_TYPE_BYTE}, + {0, 0x7C, PMBUS_PROTOCAL_TYPE_BYTE}, + {0, 0x7D, PMBUS_PROTOCAL_TYPE_BYTE}, + {0, 0x7E, PMBUS_PROTOCAL_TYPE_BYTE}, + {0, 0x80, PMBUS_PROTOCAL_TYPE_BYTE}, + {0, 0x88, PMBUS_PROTOCAL_TYPE_WORD}, + {0, 0x8B, PMBUS_PROTOCAL_TYPE_WORD}, + {0, 0x8C, PMBUS_PROTOCAL_TYPE_WORD}, + {0, 0x8D, PMBUS_PROTOCAL_TYPE_WORD}, + {0, 0x96, PMBUS_PROTOCAL_TYPE_WORD}, + {0, 0x9A, PMBUS_PROTOCAL_TYPE_BLOCK}, + {0, 0x9D, PMBUS_PROTOCAL_TYPE_BLOCK}, + {0, 0xD7, PMBUS_PROTOCAL_TYPE_WORD}, + {0, 0xD8, PMBUS_PROTOCAL_TYPE_WORD}, + {0, 0xDE, PMBUS_PROTOCAL_TYPE_WORD}, + {0, 0xEB, PMBUS_PROTOCAL_TYPE_BLOCK}, + {0, 0xEF, PMBUS_PROTOCAL_TYPE_WORD}, + {0, 0xF4, PMBUS_PROTOCAL_TYPE_BLOCK}, + {0, 0xF9, PMBUS_PROTOCAL_TYPE_BLOCK} +}; + +void dump_fmea_reg(unsigned int index, AVS_FMEA_DUMP_REG_T *dump_reg_list, unsigned int size, char *buf) +{ + int i, block_index; + int retval=0; + int page, last_page = -1; + int reg; + PMBUS_PROTOCAL_TYPE_E type; + unsigned char tmp[64] = {0}; + unsigned char val[I2C_SMBUS_BLOCK_MAX] = {0}; + + for(i = 0; i < size; i++) + { + page = dump_reg_list[i].page; + reg = dump_reg_list[i].reg; + type = dump_reg_list[i].type; + + if(page != last_page) + { +#ifdef C11_ANNEX_K + if(sprintf_s(tmp, 64, "page%d", page) < 0) +#else + if(sprintf(tmp, "page%d", page) < 0) +#endif + { + AVS_DEBUG("avs%d sprintf failed\n", index); + } +#ifdef C11_ANNEX_K + if(strcat_s(buf, 1024, tmp) != 0) +#else + if(strcat(buf, tmp) != 0) +#endif + { + AVS_DEBUG("avs%d strcat page failed\n", index); + } + last_page = page; + } +#ifdef C11_ANNEX_K + memset_s(val, I2C_SMBUS_BLOCK_MAX, 0, sizeof(val)); +#else + memset(val, 0, sizeof(val)); +#endif + retval = pmbus_core_read_reg(avs_i2c_bus[index], avs_i2c_addr[index], page, reg, type, val); + if(retval < 0) + { + // continue to dump other registers + AVS_DEBUG("Read avs%d page %d reg 0x%x type %d failed\n", index, page, reg, type); + continue; + } +#ifdef C11_ANNEX_K + memset_s(tmp, 64, 0, sizeof(tmp)); +#else + memset(tmp, 0, sizeof(tmp)); +#endif + if(type == PMBUS_PROTOCAL_TYPE_BYTE) + { +#ifdef C11_ANNEX_K + if(sprintf_s(tmp, 64, " 0x%x: 0x%02x", reg, val[0]) < 0) +#else + if(sprintf(tmp, " 0x%x: 0x%02x", reg, val[0]) < 0) +#endif + { + AVS_DEBUG("avs%d sprintf failed\n", index); + } +#ifdef C11_ANNEX_K + if(strcat_s(buf, 1024, tmp) != 0) +#else + if(strcat(buf, tmp) != 0) +#endif + { + AVS_DEBUG("avs%d strcat_s failed\n", index); + continue; + } + } + else if(type == PMBUS_PROTOCAL_TYPE_WORD) + { +#ifdef C11_ANNEX_K + if(sprintf_s(tmp, 64, " 0x%x: 0x%02x%02x", reg, val[1], val[0]) < 0) +#else + if(sprintf(tmp, " 0x%x: 0x%02x%02x", reg, val[1], val[0]) < 0) +#endif + { + AVS_DEBUG("avs%d sprintf failed\n", index); + } +#ifdef C11_ANNEX_K + if(strcat_s(buf, 1024, tmp) != 0) +#else + if(strcat(buf, tmp) != 0) +#endif + { + AVS_DEBUG("avs%d strcat failed\n", index); + continue; + } + } + else if(type == PMBUS_PROTOCAL_TYPE_BLOCK) + { +#ifdef C11_ANNEX_K + if(sprintf_s(tmp, 64, " 0x%x: 0x", reg) < 0) +#else + if(sprintf(tmp, " 0x%x: 0x", reg) < 0) +#endif + { + AVS_DEBUG("avs%d sprintf failed\n", index); + } +#ifdef C11_ANNEX_K + if(strcat_s(buf, 1024, tmp) != 0) +#else + if(strcat(buf, tmp) != 0) +#endif + { + AVS_DEBUG("avs%d strcat failed\n", index); + continue; + } + + for(block_index = 0; block_index < retval ; block_index++) + { +#ifdef C11_ANNEX_K + memset_s(tmp, 64, 0, sizeof(tmp)); +#else + memset(tmp, 0, sizeof(tmp)); +#endif +#ifdef C11_ANNEX_K + if(sprintf_s(tmp, 64, "%02x", val[block_index]) < 0) +#else + if(sprintf(tmp, "%02x", val[block_index]) < 0) +#endif + { + AVS_DEBUG("avs%d sprintf block failed\n", index); + } +#ifdef C11_ANNEX_K + if(strcat_s(buf, 1024, tmp) != 0) +#else + if(strcat(buf, tmp) != 0) +#endif + { + AVS_DEBUG("avs%d strcat block failed\n", index); + break; + } + } + } + } + + return; +} +EXPORT_SYMBOL_GPL(dump_fmea_reg); + +ssize_t drv_fmea_get_work_status(unsigned int index, char *buf, char *plt) +{ + int page, num_page, num_page_mfr_specific = 0; + int check_status_bmp, check_status_mfr_specific_bmp = 0, check_temperature; + int mfr_id = 0; + int retval=0; + int val; + long long_value; + char tmp_buf[1024] = {0}; + AVS_FMEA_DUMP_REG_T *dump_reg_list = NULL; + unsigned int dump_size = 0; + + if(index >= TOTAL_FMEA_AVS_NUM) + { + AVS_DEBUG("Invalid avs index %d.\n", index); + return -EPERM; + } + + mutex_lock(&update_lock); + + /* Get MFR_ID (0x99) in word */ + retval = pmbus_core_read_mfr_id(avs_i2c_bus[index], avs_i2c_addr[index], &mfr_id); + if(retval < 0) + { + AVS_DEBUG("Get avs%d mfr_id failed.\n", index); + mutex_unlock(&update_lock); + return -EPERM; + } + + switch(mfr_id) + { + case PMBUS_AVS_MFR_ID_MP2882: + num_page = 2; + check_status_bmp = 0xF73D; /* Ignore bit1, bit6, bit7, bit11 for MP2882 STATUS */ + check_temperature = 112000; /* 112 celsius*/ + dump_reg_list = mp2882_fmea_dump_reg_list; + dump_size = (sizeof(mp2882_fmea_dump_reg_list)/sizeof(AVS_FMEA_DUMP_REG_T)); + break; + case PMBUS_AVS_MFR_ID_MP2975: + num_page = 1; + check_status_bmp = 0xD33D; /* Ignore bit1, bit6, bit7, bit10, bit11, bit13 for MP2975 STATUS */ + check_temperature = 112000; /* 112 celsius*/ + dump_reg_list = mp2975_fmea_dump_reg_list; + dump_size = (sizeof(mp2975_fmea_dump_reg_list)/sizeof(AVS_FMEA_DUMP_REG_T)); + break; + case PMBUS_AVS_MFR_ID_PH86B03: + num_page = 1; + num_page_mfr_specific = 1; + check_status_bmp = 0xF7BE; /* Ignore bit0, bit6, bit11 for PH86B03 STATUS */ + check_status_mfr_specific_bmp = 0xDF; /* Ignore bit5 for PH86B03 STATUS_MFR_SPECIFIC */ + check_temperature = 112000; /* 112 celsius*/ + dump_reg_list = ph86b03_fmea_dump_reg_list; + dump_size = (sizeof(ph86b03_fmea_dump_reg_list)/sizeof(AVS_FMEA_DUMP_REG_T)); + break; + default: + AVS_DEBUG("Unsupported avs%d mfr_id:0x%04X failed.\n", index, mfr_id); + mutex_unlock(&update_lock); + return -EPERM; + } + + for(page = 0; page < num_page; page++) + { + /* Check PMBUS_STATUS_WORD (0x79) */ + retval = pmbus_core_read_reg(avs_i2c_bus[index], avs_i2c_addr[index], page, PMBUS_STATUS_WORD, PMBUS_PROTOCAL_TYPE_WORD, &val); + if(retval < 0) + { + AVS_DEBUG("Read avs%d mfr_id:0x%04X page %d PMBUS_STATUS_WORD failed\n", index, mfr_id, page); + mutex_unlock(&update_lock); + return retval; + } + + if((val & check_status_bmp) != 0) + { + AVS_DEBUG("%s %d, retval&0x%04X:0x%x", __func__, __LINE__, check_status_bmp, (val & check_status_bmp)); + goto work_abnormal; + } + + /* Check PMBUS_READ_TEMPERATURE_1 (0x8D) */ + retval = pmbus_core_read_attrs_by_reg(avs_i2c_bus[index], avs_i2c_addr[index], 0, PMBUS_READ_TEMPERATURE_1, &long_value); + if(retval < 0) + { + AVS_DEBUG("Read avs%d mfr_id:0x%04X page %d PMBUS_READ_TEMPERATURE_1 failed\n", index, mfr_id, page); + mutex_unlock(&update_lock); + return retval; + } + + if((int)long_value > check_temperature) + { + AVS_DEBUG("%s %d, retval:%d > %d\n", __func__, __LINE__, (int)long_value, check_temperature); + goto work_abnormal; + } + } + + for(page = 0; page < num_page_mfr_specific; page++) + { + /* Check custom register */ + switch(mfr_id) + { + case PMBUS_AVS_MFR_ID_PH86B03: + /* Check STATUS_MFR_SPECIFIC (0x80) */ + retval = pmbus_core_read_reg(avs_i2c_bus[index], avs_i2c_addr[index], page, 0x80, PMBUS_PROTOCAL_TYPE_BYTE, &val); + if(retval < 0) + { + AVS_DEBUG("Read avs%d mfr_id:0x%04X page %d STATUS_MFR_SPECIFIC failed\n", index, mfr_id, page); + mutex_unlock(&update_lock); + return retval; + } + + if((val & check_status_mfr_specific_bmp) != 0) + { + AVS_DEBUG("%s %d, retval&0x%04X:0x%x", __func__, __LINE__, check_status_mfr_specific_bmp, (val & check_status_mfr_specific_bmp)); + goto work_abnormal; + } + break; + case PMBUS_AVS_MFR_ID_MP2882: + case PMBUS_AVS_MFR_ID_MP2975: + /* do nothing */ + break; + default: + /* do nothing */ + break; + } + } + + mutex_unlock(&update_lock); +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%s\n", "0 0x00000000"); +#else + return sprintf(buf, "%s\n", "0 0x00000000"); +#endif + +work_abnormal: + dump_fmea_reg(index, dump_reg_list, dump_size, tmp_buf); + pmbus_core_clear_fault(avs_i2c_bus[index], avs_i2c_addr[index]); + mutex_unlock(&update_lock); +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "1 %s\n", tmp_buf); +#else + return sprintf(buf, "1 %s\n", tmp_buf); +#endif + +} +EXPORT_SYMBOL_GPL(drv_fmea_get_work_status); + +ssize_t drv_fmea_get_current_status(unsigned int index, char *buf, char *plt) +{ + int retval=0; + int mfr_id = 0; + int multiplier = 1000; + int vout, iout, pout; + int iout_threshold = 0; + long val; + + if(index >= TOTAL_FMEA_AVS_NUM) + { + AVS_DEBUG("Invalid avs index %d.\n", index); + return -EPERM; + } + + mutex_lock(&update_lock); + + /* Get MFR_ID (0x99) in word */ + retval = pmbus_core_read_mfr_id(avs_i2c_bus[index], avs_i2c_addr[index], &mfr_id); + if(retval < 0) + { + AVS_DEBUG("Get avs%d mfr_id failed.\n", index); + mutex_unlock(&update_lock); + return -EPERM; + } + + switch(mfr_id) + { + case PMBUS_AVS_MFR_ID_MP2882: /* MP2882 */ + iout_threshold = 589000; /* (491A * 120%) */ + break; + case PMBUS_AVS_MFR_ID_MP2975: /* MP2975 */ + case PMBUS_AVS_MFR_ID_PH86B03: /* PH86B03 */ + iout_threshold = 63000; /* ((41W/0.77V) * 120%) */ + break; + default: + AVS_DEBUG("Unsupported avs%d mfr_id:0x%04X failed.\n", index, mfr_id); + mutex_unlock(&update_lock); + return -EPERM; + } + + retval = pmbus_core_read_attrs_by_reg(avs_i2c_bus[index], avs_i2c_addr[index], 0, PMBUS_READ_IOUT, &val); + if(retval < 0) + { + AVS_DEBUG("Read avs%d (mfr_id:0x%04X) page 0 Iout failed\n", index, mfr_id); + mutex_unlock(&update_lock); + return retval; + } + mutex_unlock(&update_lock); + + iout = (int)val; + + if(iout > iout_threshold) + { + mutex_lock(&update_lock); + retval = pmbus_core_read_attrs_by_reg(avs_i2c_bus[index], avs_i2c_addr[index], 0, PMBUS_READ_VOUT, &val); + if(retval < 0) + { + AVS_DEBUG("Read avs%d (mfr_id:0x%04X) page 0 Vout failed\n", index, mfr_id); + mutex_unlock(&update_lock); + return retval; + } + mutex_unlock(&update_lock); + + vout = (int)val; + + mutex_lock(&update_lock); + retval = pmbus_core_read_attrs_by_reg(avs_i2c_bus[index], avs_i2c_addr[index], 0, PMBUS_READ_POUT, &val); + if(retval < 0) + { + AVS_DEBUG("Read avs%d (mfr_id:0x%04X) page 0 Pout failed\n", index, mfr_id); + mutex_unlock(&update_lock); + return retval; + } + mutex_unlock(&update_lock); + + pout = (int)val; +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "1 %d.%03d(V) %s%d.%03d(A) %s%d.%03d(W)\n", (vout / multiplier), (vout % multiplier) + , ((iout < 0) ? "-":""), abs(iout / multiplier), abs(iout % multiplier) + , ((pout < 0) ? "-":""), abs(pout / (multiplier * multiplier)), ((abs(pout % (multiplier * multiplier))) / multiplier)); +#else + return sprintf(buf, "1 %d.%03d(V) %s%d.%03d(A) %s%d.%03d(W)\n", (vout / multiplier), (vout % multiplier) + , ((iout < 0) ? "-":""), abs(iout / multiplier), abs(iout % multiplier) + , ((pout < 0) ? "-":""), abs(pout / (multiplier * multiplier)), ((abs(pout % (multiplier * multiplier))) / multiplier)); +#endif + } + else +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%s\n", "0 0x00000000"); +#else + return sprintf(buf, "%s\n", "0 0x00000000"); +#endif +} +EXPORT_SYMBOL_GPL(drv_fmea_get_current_status); + +ssize_t drv_fmea_get_pmbus_status(unsigned int index, char *buf, char *plt) +{ + int retval=0; + int mfr_id = 0; + bool match = false; + + if(index >= TOTAL_FMEA_AVS_NUM) + { + AVS_DEBUG("Invalid avs index %d.\n", index); + return -EPERM; + } + + mutex_lock(&update_lock); + + /* Get MFR_ID (0x99) in word */ + retval = pmbus_core_read_mfr_id(avs_i2c_bus[index], avs_i2c_addr[index], &mfr_id); + if(retval < 0) + { + AVS_DEBUG("Get avs%d mfr_id failed.\n", index); + mutex_unlock(&update_lock); + return -EPERM; + } + + switch(mfr_id) + { + case PMBUS_AVS_MFR_ID_MP2882: /* MP2882 */ + case PMBUS_AVS_MFR_ID_MP2975: /* MP2975 */ + case PMBUS_AVS_MFR_ID_PH86B03: /* PH86B03 */ + match = true; + break; + default: + AVS_DEBUG("Unsupported avs%d mfr_id:0x%04X failed.\n", index, mfr_id); + break; + } + + mutex_unlock(&update_lock); + + if(match) +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%s\n", "0 0x00000000"); +#else + return sprintf(buf, "%s\n", "0 0x00000000"); +#endif + + else +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "1 0x%04X\n", mfr_id); +#else + return sprintf(buf, "1 0x%04X\n", mfr_id); +#endif + +} + +void drv_get_loglevel(long *lev) +{ + *lev = (long)loglevel; + + return; +} + +void drv_set_loglevel(long lev) +{ + loglevel = (unsigned int)lev; + + return; +} + +// For S3IP sonic +static struct avs_drivers_t pfunc = { + .fmea_get_work_status = drv_fmea_get_work_status, + .fmea_get_current_status = drv_fmea_get_current_status, + .fmea_get_pmbus_status = drv_fmea_get_pmbus_status, + .get_loglevel = drv_get_loglevel, + .set_loglevel = drv_set_loglevel, +}; + +static struct avs_drivers_t pfunc_bmc = { + .fmea_get_work_status = drv_fmea_get_work_status_from_bmc, + .fmea_get_current_status = drv_fmea_get_current_status_from_bmc, + .fmea_get_pmbus_status = drv_fmea_get_pmbus_status_from_bmc, + .get_loglevel = drv_get_loglevel, + .set_loglevel = drv_set_loglevel, +}; + +static int drv_avs_probe(struct platform_device *pdev) +{ + if(ipmi_bmc_is_ok()) + { + s3ip_avs_drivers_register(&pfunc_bmc); + } + else + { + s3ip_avs_drivers_register(&pfunc); + } + + return 0; +} + +static int drv_avs_remove(struct platform_device *pdev) +{ + s3ip_avs_drivers_unregister(); + + return 0; +} + +static struct platform_driver drv_avs_driver = { + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + }, + .probe = drv_avs_probe, + .remove = drv_avs_remove, +}; + +static int __init drv_avs_init(void) +{ + int err=0; + int retval=0; + + drv_avs_device = platform_device_alloc(DRVNAME, 0); + if(!drv_avs_device) + { + AVS_DEBUG("%s(#%d): platform_device_alloc fail\n", __func__, __LINE__); + return -ENOMEM; + } + + retval = platform_device_add(drv_avs_device); + if(retval) + { + AVS_DEBUG("%s(#%d): platform_device_add failed\n", __func__, __LINE__); + err = -retval; + goto dev_add_failed; + } + + retval = platform_driver_register(&drv_avs_driver); + if(retval) + { + AVS_DEBUG("%s(#%d): platform_driver_register failed(%d)\n", __func__, __LINE__, err); + err = -retval; + goto dev_reg_failed; + } + + mutex_init(&update_lock); + + return 0; + +dev_reg_failed: + platform_device_unregister(drv_avs_device); + return err; + +dev_add_failed: + platform_device_put(drv_avs_device); + return err; +} + +static void __exit drv_avs_exit(void) +{ + platform_driver_unregister(&drv_avs_driver); + platform_device_unregister(drv_avs_device); + + mutex_destroy(&update_lock); + + return; +} + +MODULE_DESCRIPTION("S3IP AVS Driver"); +MODULE_VERSION(SWITCH_AVS_DRIVER_VERSION); +MODULE_LICENSE("GPL"); + +module_init(drv_avs_init); +module_exit(drv_avs_exit); diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_avs_driver.h b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_avs_driver.h new file mode 100644 index 0000000000..70a57d3c9f --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_avs_driver.h @@ -0,0 +1,35 @@ +#ifndef SWITCH_AVS_DRIVER_H +#define SWITCH_AVS_DRIVER_H + +#include "switch.h" + +#define AVS_ERR(fmt, args...) LOG_ERR("avs: ", fmt, ##args) +#define AVS_WARNING(fmt, args...) LOG_WARNING("avs: ", fmt, ##args) +#define AVS_INFO(fmt, args...) LOG_INFO("avs: ", fmt, ##args) +#define AVS_DEBUG(fmt, args...) LOG_DBG("avs: ", fmt, ##args) + +#define CHECK_BIT(a, b) (((a) >> (b)) & 1U) + +#define TOTAL_MP2882_NUM 1 +#define TOTAL_MP2975_NUM 1 +#define TOTAL_FMEA_AVS_NUM (TOTAL_MP2882_NUM + TOTAL_MP2975_NUM) +#define AVS_INDEX_START 0 + +struct avs_drivers_t{ + ssize_t (*fmea_get_work_status) (unsigned int index, char* buf, char* plt); + ssize_t (*fmea_get_current_status) (unsigned int index, char* buf, char* plt); + ssize_t (*fmea_get_pmbus_status) (unsigned int index, char* buf, char* plt); + void (*get_loglevel) (long *lev); + void (*set_loglevel) (long lev); +}; + +ssize_t drv_fmea_get_work_status(unsigned int index, char* buf, char* plt); +ssize_t drv_fmea_get_current_status(unsigned int index, char* buf, char* plt); +ssize_t drv_fmea_get_pmbus_status(unsigned int index, char* buf, char* plt); +void drv_get_loglevel(long *lev); +void drv_set_loglevel(long lev); + +void s3ip_avs_drivers_register(struct avs_drivers_t *p_func); +void s3ip_avs_drivers_unregister(void); + +#endif /* SWITCH_AVS_DRIVER_H */ diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_bmc_driver.c b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_bmc_driver.c new file mode 100644 index 0000000000..a72a088dfc --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_bmc_driver.c @@ -0,0 +1,195 @@ +#include +#include +#include +#include +#include +#include + +#include "pmbus.h" +#include "switch_cpld_driver.h" +#include "switch_bmc_driver.h" + +#define DRVNAME "drv_bmc_driver" +#define SWITCH_BMC_DRIVER_VERSION "0.0.1" + +unsigned int loglevel = 0; +static struct platform_device *drv_bmc_device; + +struct mutex update_lock; + +bool ipmi_bmc_exist(void) +{ + unsigned char val; + switch_cpld_reg_read(0, SYSCPLD_REG_BMC_STATUS, &val); + + if(CHECK_BIT(val, 0) == 0) + { + return true; //BMC is present + } + else + { + return false; //BMC is not present + } +} +EXPORT_SYMBOL_GPL(ipmi_bmc_exist); + +bool ipmi_bmc_work(void) +{ + unsigned char val; + + /* Offset 0x29 BMC_STATUS_REGISTER Bit[2] BMC_RUN, 0:Normal; 1:Abnormal */ + switch_cpld_reg_read(0, SYSCPLD_REG_BMC_STATUS, &val); + + if(CHECK_BIT(val, 2) == 0) + { + return true; + } + else + { + return false; + } +} +EXPORT_SYMBOL_GPL(ipmi_bmc_work); + +bool ipmi_bmc_is_ok(void) +{ + if(ipmi_bmc_exist() && ipmi_bmc_work()) + { + return true; + } + else + { + return false; + } +} +EXPORT_SYMBOL_GPL(ipmi_bmc_is_ok); + + +ssize_t drv_get_status(char *buf) +{ + if(ipmi_bmc_exist()==false) + { +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "0\n"); //BMC is not present +#else + return sprintf(buf, "0\n"); //BMC is not present +#endif + } + else if(ipmi_bmc_work()==true) + { +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "1\n");//BMC is normal +#else + return sprintf(buf, "1\n");//BMC is normal +#endif + } + else +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "2\n");//BMC is abnormal +#else + return sprintf(buf, "2\n");//BMC is abnormal +#endif + return -1; +} + +void drv_get_loglevel(long *lev) +{ + *lev = (long)loglevel; + + return; +} + +void drv_set_loglevel(long lev) +{ + loglevel = (unsigned int)lev; + + return; +} + +// For s3ip +static struct bmc_drivers_t pfunc = { + .get_status = drv_get_status, + .get_loglevel = drv_get_loglevel, + .set_loglevel = drv_set_loglevel, +}; + +static int drv_bmc_probe(struct platform_device *pdev) +{ + s3ip_bmc_drivers_register(&pfunc); + + return 0; +} + +static int drv_bmc_remove(struct platform_device *pdev) +{ + s3ip_bmc_drivers_unregister(); + + return 0; +} + +static struct platform_driver drv_bmc_driver = { + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + }, + .probe = drv_bmc_probe, + .remove = drv_bmc_remove, +}; + +static int __init drv_bmc_init(void) +{ + int err=0; + int retval=0; + + drv_bmc_device = platform_device_alloc(DRVNAME, 0); + if(!drv_bmc_device) + { + BMC_DEBUG("%s(#%d): platform_device_alloc fail\n", __func__, __LINE__); + return -ENOMEM; + } + + retval = platform_device_add(drv_bmc_device); + if(retval) + { + BMC_DEBUG("%s(#%d): platform_device_add failed\n", __func__, __LINE__); + err = -retval; + goto dev_add_failed; + } + + retval = platform_driver_register(&drv_bmc_driver); + if(retval) + { + BMC_DEBUG("%s(#%d): platform_driver_register failed(%d)\n", __func__, __LINE__, err); + err = -retval; + goto dev_reg_failed; + } + + mutex_init(&update_lock); + + return 0; + +dev_reg_failed: + platform_device_unregister(drv_bmc_device); + return err; + +dev_add_failed: + platform_device_put(drv_bmc_device); + return err; +} + +static void __exit drv_bmc_exit(void) +{ + platform_driver_unregister(&drv_bmc_driver); + platform_device_unregister(drv_bmc_device); + + mutex_destroy(&update_lock); + + return; +} + +MODULE_DESCRIPTION("S3IP BMC Driver"); +MODULE_VERSION(SWITCH_BMC_DRIVER_VERSION); +MODULE_LICENSE("GPL"); + +module_init(drv_bmc_init); +module_exit(drv_bmc_exit); diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_bmc_driver.h b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_bmc_driver.h new file mode 100644 index 0000000000..5d40acaf17 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_bmc_driver.h @@ -0,0 +1,31 @@ +#ifndef SWITCH_BMC_DRIVER_H +#define SWITCH_BMC_DRIVER_H + +#include "switch.h" + +#define BMC_ERR(fmt, args...) LOG_ERR("bmc: ", fmt, ##args) +#define BMC_WARNING(fmt, args...) LOG_WARNING("bmc: ", fmt, ##args) +#define BMC_INFO(fmt, args...) LOG_INFO("bmc: ", fmt, ##args) +#define BMC_DEBUG(fmt, args...) LOG_DBG("bmc: ", fmt, ##args) + +#define CHECK_BIT(a, b) (((a) >> (b)) & 1U) + +#define SYS_CPLD_SOCKET_FULLIN_REG_OFFSET 0x10b +#define SYS_CPLD_REG_BMC_HB_STATE_OFFSET 0x1f10 +#define SYS_CPLD_REG_BMC_DIS_ADDR 0x1f35 + +struct bmc_drivers_t{ + ssize_t (*get_status) (char* buf); + void (*get_loglevel) (long *lev); + void (*set_loglevel) (long lev); + ssize_t (*get_enable) (char* buf); +}; + +ssize_t drv_get_status(char* buf); +void drv_get_loglevel(long *lev); +void drv_set_loglevel(long lev); + +void s3ip_bmc_drivers_register(struct bmc_drivers_t *p_func); +void s3ip_bmc_drivers_unregister(void); + +#endif /* SWITCH_BMC_DRIVER_H */ diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_cpld_driver.c b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_cpld_driver.c new file mode 100644 index 0000000000..9eecaccee5 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_cpld_driver.c @@ -0,0 +1,959 @@ +#include +#include +#include +#include +#include +#include +//#include +#include +#include + +#include "switch_wdt_driver.h" +#include "switch_cpld_driver.h" +#include "sysfs_ipmi.h" +#include + +//simon: try to support linux 4.x +#include + +#define DRVNAME "drv_cpld_driver" +#define SWITCH_CPLD_DRIVER_VERSION "0.0.1" +#define CPLD_DEV_NAME "cpld" +#define CPLD_CLASS_NAME "cpld_class" + +#define PREVIOUS_REBOOT_CAUSE_FILE "/host/reboot-cause/previous-reboot-cause.json" +#define IS_COLD_RESET_FILE "/tmp/isColdRst" + +unsigned int loglevel = 0; +static struct platform_device *drv_cpld_device; +bool is_wdt_trigger = false; +static struct mutex cpld_sem; + +/* function pointer for fan CPLD */ +int (*fan_cpld_reg_read_func)(u8, u8, u8*); +int (*fan_cpld_reg_write_func)(u8, u8, u8); + +static char *cpld_alias_name[CPLD_TOTAL_NUM] = { + "sys_cpld", + "fan_cpld", + "port_cpld1", + "port_cpld2" +}; + +static char *cpld_type[CPLD_TOTAL_NUM] = { + "CPLD1", + "CPLD2", + "CPLD3", + "CPLD4" +}; + +enum reboot_cause { + REBOOT_CAUSE_NON_HARDWARE, + REBOOT_CAUSE_POWER_LOSS, + REBOOT_CAUSE_THERMAL_OVERLOAD_CPU, + REBOOT_CAUSE_THERMAL_OVERLOAD_ASIC, + REBOOT_CAUSE_THERMAL_OVERLOAD_OTHER, + REBOOT_CAUSE_INSUFFICIENT_FAN_SPEED, + REBOOT_CAUSE_WATCHDOG, + REBOOT_CAUSE_HARDWARE_OTHER, + REBOOT_CAUSE_CPU_COLD_RESET, + REBOOT_CAUSE_CPU_WARM_RESET, + REBOOT_CAUSE_BIOS_RESET, + REBOOT_CAUSE_PSU_SHUTDOWN, + REBOOT_CAUSE_BMC_SHUTDOWN, + REBOOT_CAUSE_RESET_BUTTON_SHUTDOWN, + REBOOT_CAUSE_RESET_BUTTON_COLD_SHUTDOWN, + REBOOT_CAUSE_CPU_EC_NO_HB, + REBOOT_CAUSE_CPU_CPU_ERROR, + REBOOT_CAUSE_CPLD_CPU_PWR_CYCLE +}; + +void clear_cpld_reg(unsigned int cpld_index, unsigned int offset) +{ + unsigned char reg_val; + + // Write all 1 to clear register + reg_val = 0xff; + switch_cpld_reg_write(cpld_index, offset, reg_val); +} + +int read_whole_file(struct file *filp, char *content) +{ + loff_t pos = 0; + char tmp_buf[1]; + int i = 0; + + while(kernel_read(filp, tmp_buf, 1, &pos) == 1) + { + if(tmp_buf[0] != '\n') + { + content[i] = tmp_buf[0]; + i++; + } + } + + return i; +} + +bool check_if_reboot_cause_power_loss(void) +{ + unsigned char reg_val; + + switch_cpld_reg_read(0, CPLD_RST_SRC_RECORD_REG_2_OFFSET, ®_val); + if((reg_val& 0x1) == 1) + return true; + + return false; +} + +void check_if_reboot_cause_watchdog(void) +{ + if(((inb(EC_LPC_IO_BASE_ADDR + EC_WDT_CFG_REG_OFFSET) >> 7) & 0x1) == 1) + { + outb(WDT_CFG_WDT_CLEAR, (EC_LPC_IO_BASE_ADDR + EC_WDT_CFG_REG_OFFSET)); //Clear previous watchdog trigger report + is_wdt_trigger = true; + } + + return; +} + +int check_if_reboot_cause_warm_reset(void) +{ + struct file *fp = NULL; + int err = 0; + char content[1024]; + + fp = filp_open(IS_COLD_RESET_FILE, O_RDONLY, 0); + if(IS_ERR(fp)) + { + CPLD_ERR("Open %s failed\n", IS_COLD_RESET_FILE); + err = PTR_ERR(fp); + } + else + { + filp_close(fp, NULL); + return REBOOT_CAUSE_CPU_COLD_RESET; + } + + fp = filp_open(PREVIOUS_REBOOT_CAUSE_FILE, O_RDONLY, 0); + if(IS_ERR(fp)) + { + CPLD_ERR("Open %s failed\n", PREVIOUS_REBOOT_CAUSE_FILE); + + err = PTR_ERR(fp); + return -EBADF; + } + + if(!(read_whole_file(fp, content))) + { + CPLD_ERR("Read %s failed\n", PREVIOUS_REBOOT_CAUSE_FILE); + filp_close(fp, NULL); + return -EBADF; + } + filp_close(fp, NULL); + + if(strstr(content, "Unknown")) + return REBOOT_CAUSE_NON_HARDWARE; + else if(strstr(content, "warm-reboot") || strstr(content, "fast-reboot")) + return REBOOT_CAUSE_CPU_WARM_RESET; + else if(strstr(content, "reboot")) //Reboot command is modified to cold-reset via CPU 0xcf9 reg. + return REBOOT_CAUSE_CPU_COLD_RESET; + else + return -EINVAL; +} + +#if 0 +bool check_if_reboot_cause_bios(void) +{ + unsigned char val; + + val = inb(EC_LPC_IO_BASE_ADDR + EC_BIOS_SELECT_STATUS_REG_OFFSET); + if((val == 0x03) || (val == 0x30)) + return true; + + return false; +} +#endif + +int check_if_reboot_cause_cold_reset(void) +{ + unsigned char val; + + switch_cpld_reg_read(0, CPLD_REG_CPU_COOL_RST_RECORD_OFFSET, &val); + + if(((val >> 0) & 0x1) == 1) + { + return REBOOT_CAUSE_CPLD_CPU_PWR_CYCLE; + } + else if(((val >> 1) & 0x1) == 1) + { + return REBOOT_CAUSE_CPU_EC_NO_HB; + } + else if(((val >> 2) & 0x1) == 1) + { + return REBOOT_CAUSE_CPU_CPU_ERROR; + } + else if(((val >> 3) & 0x1) == 1) + { + return REBOOT_CAUSE_BMC_SHUTDOWN; + } + else + { + return -EINVAL; + } +} + +ssize_t drv_get_reboot_cause(char *buf) +{ + int ret; + + // REBOOT_CAUSE_WATCHDOG + check_if_reboot_cause_watchdog(); + if(is_wdt_trigger == true) + { +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", REBOOT_CAUSE_WATCHDOG); +#else + return sprintf(buf, "%d\n", REBOOT_CAUSE_WATCHDOG); +#endif + } + + ret = check_if_reboot_cause_cold_reset(); + if(ret == REBOOT_CAUSE_CPU_EC_NO_HB) + { +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", REBOOT_CAUSE_CPU_EC_NO_HB); +#else + return sprintf(buf, "%d\n", REBOOT_CAUSE_CPU_EC_NO_HB); +#endif + } + else if(ret == REBOOT_CAUSE_CPU_CPU_ERROR) + { +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", REBOOT_CAUSE_CPU_CPU_ERROR); +#else + return sprintf(buf, "%d\n", REBOOT_CAUSE_CPU_CPU_ERROR); +#endif + } + else if(ret == REBOOT_CAUSE_BMC_SHUTDOWN) + { +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", REBOOT_CAUSE_BMC_SHUTDOWN); +#else + return sprintf(buf, "%d\n", REBOOT_CAUSE_BMC_SHUTDOWN); +#endif + } + else if(ret == REBOOT_CAUSE_CPLD_CPU_PWR_CYCLE) + { +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", REBOOT_CAUSE_CPLD_CPU_PWR_CYCLE); +#else + return sprintf(buf, "%d\n", REBOOT_CAUSE_CPLD_CPU_PWR_CYCLE); +#endif + } + + // REBOOT_CAUSE_POWER_LOSS + if(check_if_reboot_cause_power_loss() == true) + { +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", REBOOT_CAUSE_POWER_LOSS); +#else + return sprintf(buf, "%d\n", REBOOT_CAUSE_POWER_LOSS); +#endif + } + + // REBOOT_CAUSE_CPU_WARM_RESET + ret = check_if_reboot_cause_warm_reset(); + if(ret == REBOOT_CAUSE_NON_HARDWARE) + { +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", REBOOT_CAUSE_NON_HARDWARE); +#else + return sprintf(buf, "%d\n", REBOOT_CAUSE_NON_HARDWARE); +#endif + } + else if(ret == REBOOT_CAUSE_CPU_COLD_RESET) + { +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", REBOOT_CAUSE_CPU_COLD_RESET); +#else + return sprintf(buf, "%d\n", REBOOT_CAUSE_CPU_COLD_RESET); +#endif + } + else if(ret == REBOOT_CAUSE_CPU_WARM_RESET) + { +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", REBOOT_CAUSE_CPU_WARM_RESET); +#else + return sprintf(buf, "%d\n", REBOOT_CAUSE_CPU_WARM_RESET); +#endif + } +#if 0 + // REBOOT_CAUSE_BIOS_RESET + if(check_if_reboot_cause_bios() == true) + return sprintf_s(buf, PAGE_SIZE, "%d\n", REBOOT_CAUSE_BIOS_RESET); +#endif + + // For S3IP always return success with NA for HW fail. + ERROR_RETURN_NA(-1); +} + +ssize_t drv_get_alias(unsigned int index, char *buf) +{ +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%s\n", cpld_alias_name[index-1]); +#else + return sprintf(buf, "%s\n", cpld_alias_name[index-1]); +#endif +} + +ssize_t drv_get_type(unsigned int index, char *buf) +{ +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%s\n", cpld_type[index-1]); +#else + return sprintf(buf, "%s\n", cpld_type[index-1]); +#endif +} + +ssize_t drv_get_hw_version(unsigned int index, char *buf) +{ + unsigned char maj_ver; + unsigned char min_ver; + unsigned char maj_val = 0; + unsigned char min_val = 0; + + switch_cpld_reg_read(index-1, CPLD_MAJOR_VER_OFFSET, &maj_val); + maj_ver = maj_val; + + switch_cpld_reg_read(index-1, CPLD_MINOR_VER_OFFSET, &min_val); + min_ver = min_val; +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%02x.%02x\n", maj_ver, min_ver); +#else + return sprintf(buf, "%02x.%02x\n", maj_ver, min_ver); +#endif +} + +ssize_t drv_get_board_version(unsigned int index, char *buf) +{ + unsigned char board_ver; + unsigned char board_val = 0; + + switch_cpld_reg_read(index-1, CPLD_BOARD_VER_OFFSET, &board_val); + board_ver = board_val; +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "0x%02x\n", board_ver); +#else + return sprintf(buf, "0x%02x\n", board_ver); +#endif +} + +ssize_t drv_get_reg_test(unsigned int index, char *buf) +{ + unsigned char reg_val; + switch_cpld_reg_read(index-1, PORTCPLD_REG_SCRATCH_PAD, ®_val); +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "0x%x\n", reg_val); +#else + return sprintf(buf, "0x%x\n", reg_val); +#endif +} +ssize_t drv_set_reg_test(unsigned int index, unsigned char data) +{ + int ret = 0; + ret = switch_cpld_reg_write(index-1, SYSCPLD_REG_SCRATCH_PAD, data); + + return ret; +} + +ssize_t drv_get_fmea_corrosion_status(unsigned int index, char *buf) +{ + ERROR_RETURN_NA(-1); +} + +ssize_t drv_get_fmea_voltage_status(unsigned int index, char *buf) +{ + int ret; + unsigned int status=0; + unsigned char data; + unsigned int result = 0; + + switch(index) + { + case SYS_CPLD: + ret = switch_cpld_reg_read(SYS_CPLD, SYSCPLD_REG_PG_STATUS0, &data); + if(ret < 0) + return ret; + + if((data & SYSCPLD_REG_PG_STATUS0_MASK) != 0) + { + /* PG_STATUS: 0: OK, 1: Fail */ + result = 1; + } + status |= data; + + ret = switch_cpld_reg_read(SYS_CPLD, SYSCPLD_REG_PG_STATUS1, &data); + if(ret < 0) + return ret; + + if((data & SYSCPLD_REG_PG_STATUS1_MASK) != 0) + { + /* PG_STATUS: 0: OK, 1: Fail */ + result = 1; + } + status |= (data << 8); + + ret = switch_cpld_reg_read(SYS_CPLD, SYSCPLD_REG_PG_STATUS2, &data); + if(ret < 0) + return ret; + + if((data & SYSCPLD_REG_PG_STATUS2_MASK) != 0) + { + /* PG_STATUS: 0: OK, 1: Fail */ + result = 1; + } + status |= (data << 16); + break; + case FAN_CPLD: + case PORT_CPLD1: + case PORT_CPLD2: + ERROR_RETURN_NA(-1); + break; + default: + return -EINVAL; + break; + } +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d 0x%x\n", result, status); +#else + return sprintf(buf, "%d 0x%x\n", result, status); +#endif +} + +int clock_check_fun(u8 cpld_index, u8 reg_offset,u8 mask, unsigned char *reg_value) +{ + int ret; + unsigned int result = 0; + ret = switch_cpld_reg_read(cpld_index, reg_offset, reg_value); + if(ret < 0) + return ret; + + if((*reg_value & mask) != 0) + { + /* CLK: 0: OK, 1: Fail */ + result = 1; + /*clear CLK alarm*/ + ret = switch_cpld_reg_write(cpld_index, reg_offset, 0x3); + ret = switch_cpld_reg_write(cpld_index, reg_offset, 0x0); + if(ret < 0) + return ret; + } + return result; +} + +ssize_t drv_get_fmea_clock_status(unsigned int index, char *buf) +{ + u32 status = 0; + unsigned char data; + unsigned int result = 0; + + switch(index) + { + case SYS_CPLD: + /*25M CLK*/ + result = clock_check_fun(SYS_CPLD,SYSCPLD_REG_25M_CLK,SYSCPLD_REG_25M_CLK_MASK,&data); + status |= data; + + /*100M CLK*/ + result = result | clock_check_fun(SYS_CPLD,SYSCPLD_REG_100M_CLK,SYSCPLD_REG_100M_CLK_MASK,&data); + status |= (data << 8); + + /*156M CLK*/ + result = result | clock_check_fun(SYS_CPLD,SYSCPLD_REG_156M_CLK,SYSCPLD_REG_156M_CLK_MASK,&data); + status |= (data << 16); + + break; + default: + return -EINVAL; + break; + } +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d 0x%x\n", result, status); +#else + return sprintf(buf, "%d 0x%x\n", result, status); +#endif +} + +ssize_t drv_get_fmea_battery_status(unsigned int index, char *buf) +{ + ERROR_RETURN_NA(-1); +} + +int reset_check_fun(u8 cpld_index, u8 reg_offset,u8 mask, unsigned char *reg_value) +{ + int ret; + unsigned int result = 0; + ret = switch_cpld_reg_read(cpld_index, reg_offset, reg_value); + if(ret < 0) + return ret; + + if((*reg_value & mask) == 0) + { + /* PG_STATUS: 0: OK, 1: Fail */ + result = 1; + } + return result; +} + +ssize_t drv_get_fmea_reset(unsigned int index, char *buf) +{ + unsigned int status=0; + unsigned char data; + unsigned int result = 0; + + switch(index) + { + case SYS_CPLD: + result = reset_check_fun(SYS_CPLD,SYSCPLD_REG_RESET_STATUS0,SYSCPLD_REG_RESET_STATUS0_MASK,&data); + status |= data; + + result = result | reset_check_fun(SYS_CPLD,SYSCPLD_REG_RESET_STATUS1,SYSCPLD_REG_RESET_STATUS1_MASK,&data); + status |= (data << 8); + + result = result | reset_check_fun(SYS_CPLD,SYSCPLD_REG_RESET_STATUS2,SYSCPLD_REG_RESET_STATUS2_MASK,&data); + status |= (data << 16); + + result = result | reset_check_fun(SYS_CPLD,SYSCPLD_REG_RESET_STATUS3,SYSCPLD_REG_RESET_STATUS3_MASK,&data); + status |= (data << 24); + break; + case FAN_CPLD: + ERROR_RETURN_NA(-1); + break; + case PORT_CPLD1: + case PORT_CPLD2: + result = reset_check_fun(SYS_CPLD,PORTCPLD_REG_RESET_STATUS0,PORTCPLD_REG_RESET_STATUS0_MASK,&data); + status |= data; + + result = result | reset_check_fun(SYS_CPLD,PORTCPLD_REG_RESET_STATUS1,PORTCPLD_REG_RESET_STATUS1_MASK,&data); + status |= (data << 8); + break; + default: + return -EINVAL; + break; + } +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d 0x%x\n", result, status); +#else + return sprintf(buf, "%d 0x%x\n", result, status); +#endif +} + +ssize_t drv_get_fmea_interrupt(unsigned int index, char *buf) +{ + ERROR_RETURN_NA(-1); +} + +int set_reset_fun(u8 cpld_index, u8 reg_offset,u8 reg_value) +{ + int ret; + ret = switch_cpld_reg_write(cpld_index, reg_offset, reg_value); + if(ret < 0) + return ret; + return ret; +} + +bool drv_set_fmea_reset(unsigned int index, int reset) +{ + switch(index) + { + case SYS_CPLD: + set_reset_fun(SYS_CPLD,SYSCPLD_REG_RESET_STATUS0,reset); + set_reset_fun(SYS_CPLD,SYSCPLD_REG_RESET_STATUS1,reset); + set_reset_fun(SYS_CPLD,SYSCPLD_REG_RESET_STATUS2,reset); + set_reset_fun(SYS_CPLD,SYSCPLD_REG_RESET_STATUS3,reset); + break; + case FAN_CPLD: + return -EINVAL; + case PORT_CPLD1: + case PORT_CPLD2: + set_reset_fun(SYS_CPLD,PORTCPLD_REG_RESET_STATUS0,reset); + set_reset_fun(SYS_CPLD,PORTCPLD_REG_RESET_STATUS1,reset); + break; + default: + return -EINVAL; + break; + } + return true; +} + +void drv_get_loglevel(long *lev) +{ + *lev = (long)loglevel; + + return; +} + +void drv_set_loglevel(long lev) +{ + loglevel = (unsigned int)lev; + + return; +} + +ssize_t drv_debug_help(char *buf) +{ +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, + "Use the following command to debug:\n" + "busybox devmem 8\n" + "i2cget -f -y \n" + "sysname debug cmd\n" + "reboot_cause busybox devmem 0xfc801020 8\n" + " inb 0x6308 --hex\n" + " busybox devmem 0xfc80102A 8\n" + " cat /host/reboot-cause/previous-reboot-cause.json\n" + "cpld1 board_version busybox devmem 0xfc800000 8\n" + "cpld1 hw_version busybox devmem 0xfc800001 8\n" + " busybox devmem 0xfc800002 8\n" + " i2cget -f -y 633 0x62 0x01\n" + " i2cget -f -y 633 0x62 0x02\n" + " i2cget -f -y 634 0x64 0x01\n" + " i2cget -f -y 634 0x64 0x02\n" + ); +#else + return sprintf(buf, + "Use the following command to debug:\n" + "busybox devmem 8\n" + "i2cget -f -y \n" + "sysname debug cmd\n" + "reboot_cause busybox devmem 0xfc801020 8\n" + " inb 0x6308 --hex\n" + " busybox devmem 0xfc80102A 8\n" + " cat /host/reboot-cause/previous-reboot-cause.json\n" + "cpld1 board_version busybox devmem 0xfc800000 8\n" + "cpld1 hw_version busybox devmem 0xfc800001 8\n" + " busybox devmem 0xfc800002 8\n" + " i2cget -f -y 633 0x62 0x01\n" + " i2cget -f -y 633 0x62 0x02\n" + " i2cget -f -y 634 0x64 0x01\n" + " i2cget -f -y 634 0x64 0x02\n" + ); +#endif +} + +ssize_t drv_debug(const char *buf, int count) +{ + return 0; +} + +int switch_cpld_reg_read(u8 cpld_index, u8 reg_offset, u8 *reg_value) +{ + int ret = 0; + + switch(cpld_index) + { + case SYS_CPLD: + ret = switch_system_cpld_read(reg_offset, reg_value); + break; + case FAN_CPLD: + ret = fan_cpld_reg_read_func(cpld_index, reg_offset, reg_value); + break; + case PORT_CPLD1: + case PORT_CPLD2: + ret = switch_i2c_cpld_read(cpld_index, reg_offset, reg_value); + break; + default: + ret = -1; + break; + } + + return ret; +} +EXPORT_SYMBOL_GPL(switch_cpld_reg_read); + + +int switch_cpld_reg_write(u8 cpld_index, u8 reg_offset, u8 reg_value) +{ + int ret = 0; + + switch(cpld_index) + { + case SYS_CPLD: + ret = switch_system_cpld_write(reg_offset, reg_value); + break; + case FAN_CPLD: + ret = fan_cpld_reg_write_func(cpld_index, reg_offset, reg_value); + break; + case PORT_CPLD1: + case PORT_CPLD2: + ret = switch_i2c_cpld_write(cpld_index, reg_offset, reg_value); + break; + default: + ret = -1; + break; + } + + return ret; +} +EXPORT_SYMBOL_GPL(switch_cpld_reg_write); + +// For s3ip +static struct cpld_drivers_t pfunc = { + .get_reboot_cause = drv_get_reboot_cause, + .get_alias = drv_get_alias, + .get_type = drv_get_type, + .get_hw_version = drv_get_hw_version, + .get_board_version = drv_get_board_version, + .get_fmea_selftest_status = drv_get_reg_test, + .set_reg_test = drv_set_reg_test, + .get_fmea_corrosion_status = drv_get_fmea_corrosion_status, + .get_fmea_voltage_status = drv_get_fmea_voltage_status, + .get_fmea_clock_status = drv_get_fmea_clock_status, + .get_fmea_battery_status = drv_get_fmea_battery_status, + .get_fmea_reset = drv_get_fmea_reset, + .set_fmea_reset = drv_set_fmea_reset, + .get_fmea_interrupt = drv_get_fmea_interrupt, + .get_loglevel = drv_get_loglevel, + .set_loglevel = drv_set_loglevel, + .debug_help = drv_debug_help, +}; + +#if 1 // For user space API uages +/* file_operations: ioctl */ +struct cpld_access { + /* intput output buff */ + unsigned char cs; + unsigned char offset; + unsigned char value; +}; + +#define CPLD_MAGIC 0xA7 +#define CPLD_IO_CMD 0x11 + +#define CPLD_IO_READ _IOR(CPLD_MAGIC, CPLD_IO_CMD, struct cpld_access) +#define CPLD_IO_WRITE _IOW(CPLD_MAGIC, CPLD_IO_CMD, struct cpld_access) +#endif + +long cpld_ioctl(struct file *filp, unsigned int cmd, unsigned long user_addr) +{ + struct cpld_access acc = {0}; + int retval = 0; + + if (cmd == CPLD_IO_READ) + { +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0) + if (!access_ok(VERIFY_READ,user_addr, _IOC_SIZE(cmd))) +#else + if (!access_ok(user_addr, _IOC_SIZE(cmd))) +#endif + { + return -EINVAL; + } + + retval = copy_from_user((void *) &acc, (const void *) user_addr, sizeof(struct cpld_access)); + mutex_lock(&cpld_sem); + switch_cpld_reg_read(acc.cs, acc.offset, &acc.value); + mutex_unlock(&cpld_sem); + retval = copy_to_user((void *) user_addr, (const void *) &acc, sizeof(struct cpld_access)); + return retval; + } + else if (cmd == CPLD_IO_WRITE) + { +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0) + if (!access_ok(VERIFY_WRITE,user_addr, _IOC_SIZE(cmd))) +#else + if (!access_ok(user_addr, _IOC_SIZE(cmd))) +#endif + { + return -EINVAL; + } + + retval = copy_from_user((void *) &acc, (const void *) user_addr, sizeof(struct cpld_access)); + mutex_lock(&cpld_sem); + switch_cpld_reg_write(acc.cs, acc.offset, acc.value); + mutex_unlock(&cpld_sem); + retval = copy_to_user((void *) user_addr, (const void *) &acc, sizeof(struct cpld_access)); + return(retval); + } + else + { + CPLD_ERR("[%s] Unknown command\n", __func__); + return -EINVAL; + } +} + +static const struct file_operations cpld_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = cpld_ioctl, +}; + +static int drv_cpld_probe(struct platform_device *pdev) +{ + struct cpld_drvdata *drvdata = NULL; + unsigned char bmc_exist = 0, bmc_work = 0; + int retval = 0; + + drvdata = kzalloc(sizeof(struct cpld_drvdata), GFP_KERNEL); + if (!drvdata) { + retval = -ENOMEM; + goto failed0; + } + platform_set_drvdata(pdev, (void *)drvdata); + + mutex_lock(&cpld_sem); + mutex_init(&drvdata->sem); + + /* Request the kernel for N_MINOR devices */ + retval = alloc_chrdev_region(&drvdata->devt, 0, 1, CPLD_DEV_NAME); + if (retval) + { + dev_err(&pdev->dev, "alloc_chrdev_region() failed\n"); + goto failed1; + } + + /* Initialize and create each of the device(cdev) */ + cdev_init(&drvdata->cpld_cdev, &cpld_fops); + drvdata->cpld_cdev.owner = THIS_MODULE; + retval = cdev_add(&drvdata->cpld_cdev, drvdata->devt, 1); + if (retval) + { + dev_err(&pdev->dev, "cdev_add() failed\n"); + goto failed2; + } + + /* Create a class : appears at /sys/class */ + drvdata->cpld_class = class_create(THIS_MODULE, CPLD_CLASS_NAME); + if (IS_ERR(drvdata->cpld_class)) + { + dev_err(&pdev->dev, "class_create() failed\n"); + retval = PTR_ERR(drvdata->cpld_class); + goto failed3; + } + + /* Create a device node for this device in /dev */ + drvdata->cpld_udev = device_create(drvdata->cpld_class, NULL, drvdata->devt, NULL, CPLD_DEV_NAME); + if (IS_ERR(drvdata->cpld_udev)) + { + dev_err(&pdev->dev, "device_create() failed\n"); + retval = PTR_ERR(drvdata->cpld_udev); + goto failed4; + } + + s3ip_cpld_drivers_register(&pfunc); + switch_cpld_reg_read(SYS_CPLD, SYSCPLD_REG_PRSNT_STATUS, &bmc_exist); + switch_cpld_reg_read(SYS_CPLD, SYSCPLD_REG_CORROSION_STATUS, &bmc_work); + + /* skip heartbeat condition before BMC heartbeat ready */ + if(!(bmc_exist & BMC_PRESENT_BIT_OFFSET)) + { + fan_cpld_reg_read_func = &drv_cpld_read_from_bmc; + fan_cpld_reg_write_func = &drv_cpld_write_from_bmc; + } + else + { + fan_cpld_reg_read_func = &switch_i2c_cpld_read; + fan_cpld_reg_write_func = &switch_i2c_cpld_write; + } + return 0; + + +failed4: + class_destroy(drvdata->cpld_class); + +failed3: + cdev_del(&drvdata->cpld_cdev); + +failed2: + unregister_chrdev_region(drvdata->devt, 1); + +failed1: + kfree(drvdata); + +failed0: + return retval; +} + +static int drv_cpld_remove(struct platform_device *pdev) +{ + struct cpld_drvdata *drvdata; + + s3ip_cpld_drivers_unregister(); + + drvdata = platform_get_drvdata(pdev); + if (!drvdata) + return 0; + + device_destroy(drvdata->cpld_class, drvdata->devt); + class_destroy(drvdata->cpld_class); + cdev_del(&drvdata->cpld_cdev); + unregister_chrdev_region(drvdata->devt, 1); + + platform_set_drvdata(pdev, NULL); + kfree(drvdata); + + return 0; +} + +static struct platform_driver drv_cpld_driver = { + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + }, + .probe = drv_cpld_probe, + .remove = drv_cpld_remove, +}; + +static int __init drv_cpld_init(void) +{ + int err=0; + int retval=0; + + drv_cpld_device = platform_device_alloc(DRVNAME, 0); + if(!drv_cpld_device) + { + CPLD_DEBUG("%s(#%d): platform_device_alloc fail\n", __func__, __LINE__); + return -ENOMEM; + } + + retval = platform_device_add(drv_cpld_device); + if(retval) + { + CPLD_DEBUG("%s(#%d): platform_device_add failed\n", __func__, __LINE__); + err = -retval; + goto dev_add_failed; + } + + retval = platform_driver_register(&drv_cpld_driver); + if(retval) + { + CPLD_DEBUG("%s(#%d): platform_driver_register failed(%d)\n", __func__, __LINE__, err); + err = -retval; + goto dev_reg_failed; + } + + return 0; + +dev_reg_failed: + platform_device_unregister(drv_cpld_device); + return err; + +dev_add_failed: + platform_device_put(drv_cpld_device); + return err; +} + +static void __exit drv_cpld_exit(void) +{ + platform_driver_unregister(&drv_cpld_driver); + platform_device_unregister(drv_cpld_device); + + return; +} + +MODULE_DESCRIPTION("S3IP CPLD Driver"); +MODULE_VERSION(SWITCH_CPLD_DRIVER_VERSION); +MODULE_LICENSE("GPL"); + +module_init(drv_cpld_init); +module_exit(drv_cpld_exit); diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_cpld_driver.h b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_cpld_driver.h new file mode 100644 index 0000000000..57dbefe771 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_cpld_driver.h @@ -0,0 +1,168 @@ +#ifndef SWITCH_CPLD_DRIVER_H +#define SWITCH_CPLD_DRIVER_H + +#include +#include "switch.h" + +#define CPLD_ERR(fmt, args...) LOG_ERR("cpld: ", fmt, ##args) +#define CPLD_WARNING(fmt, args...) LOG_WARNING("cpld: ", fmt, ##args) +#define CPLD_INFO(fmt, args...) LOG_INFO("cpld: ", fmt, ##args) +#define CPLD_DEBUG(fmt, args...) LOG_DBG("cpld: ", fmt, ##args) + +#define CHECK_BIT(a, b) (((a) >> (b)) & 1U) + +#define CPLD_TOTAL_NUM 4 +#define CPLD_BOARD_VER_OFFSET 0x00 +#define CPLD_MAJOR_VER_OFFSET 0x01 +#define CPLD_MINOR_VER_OFFSET 0x02 +#define CPLD_YEAR_OFFSET 0xEC +#define CPLD_MONTH_OFFSET 0xED +#define CPLD_DAY_OFFSET 0xEE +#define CPLD_HOUR_OFFSET 0xEF + +#define CPLD_TEST_REG_1_OFFSET 0x05 +#define CPLD_TEST_REG_2_OFFSET 0x07 + +#define CPLD_REG_CPU_COOL_RST_RECORD_OFFSET 0x67 +#define CPLD_RST_SRC_RECORD_REG_2_OFFSET 0x2A + +#define SYS_CPLD_BUS_NR 0 +#define PORT_CPLD1_BUS_NR 634 +#define PORT_CPLD2_BUS_NR 635 +#define FAN_CPLD_BUS_NR 143 + +#define BMC_PRESENT_BIT_OFFSET (1<<6) + +/*clock hs alarm, clock alarm*/ +#define SYSCPLD_REG_25M_CLK 0x0F +#define SYSCPLD_REG_25M_CLK_MASK 0x0C +#define SYSCPLD_REG_100M_CLK 0x14 +#define SYSCPLD_REG_100M_CLK_MASK 0x0C +#define SYSCPLD_REG_156M_CLK 0x18 +#define SYSCPLD_REG_156M_CLK_MASK 0x0C + +#define SYSCPLD_REG_SCRATCH_PAD 0x05 +/*PG status*/ +#define SYSCPLD_REG_PG_STATUS0 0x36 +#define SYSCPLD_REG_PG_STATUS0_MASK 0xFF +#define SYSCPLD_REG_PG_STATUS1 0x37 +#define SYSCPLD_REG_PG_STATUS1_MASK 0xFF +#define SYSCPLD_REG_PG_STATUS2 0x38 +#define SYSCPLD_REG_PG_STATUS2_MASK 0xFF + +/*syscpld reset status*/ +#define SYSCPLD_REG_RESET_STATUS0 0x1C +#define SYSCPLD_REG_RESET_STATUS0_MASK 0xFF +#define SYSCPLD_REG_RESET_STATUS1 0x1D +#define SYSCPLD_REG_RESET_STATUS1_MASK 0xFF +#define SYSCPLD_REG_RESET_STATUS2 0x1E +#define SYSCPLD_REG_RESET_STATUS2_MASK 0xFF +#define SYSCPLD_REG_RESET_STATUS3 0x1F +#define SYSCPLD_REG_RESET_STATUS3_MASK 0xFF + +/*portcpld reset status*/ +#define PORTCPLD_REG_RESET_STATUS0 0x38 +#define PORTCPLD_REG_RESET_STATUS0_MASK 0xFF +#define PORTCPLD_REG_RESET_STATUS1 0x39 +#define PORTCPLD_REG_RESET_STATUS1_MASK 0xFF + + +#define SYSCPLD_REG_PRSNT_STATUS 0x11 +#define SYSCPLD_REG_CORROSION_STATUS 0x12 +#define SYSCPLD_REG_BMC_HEART_MASK 0x10 +#define SYSCPLD_REG_CORROSION_MASK 0x07 +#define SYSCPLD_REG_CORROSION_IOL_0_OFFSET 0 +#define SYSCPLD_REG_CORROSION_IOL_1_OFFSET 1 +#define SYSCPLD_REG_CORROSION_IOL_2_OFFSET 2 + +#define SYSCPLD_REG_DYING_RECORD_CTL 0xD0 +#define SYSCPLD_REG_DYING_RECORD_CTL_EN (1<<0) +#define SYSCPLD_REG_DYING_RECORD_CTL_CLR (1<<1) +#define SYSCPLD_REG_DYING_RECORD_WDT_CNT 0xD1 +#define SYSCPLD_REG_DYING_RECORD_DELAY_CNT 0xD2 +#define SYSCPLD_REG_DYING_RECORD_RESET_CNT 0xD3 + +#define FANCPLD_REG_SCRATCH_PAD 0x05 + +#define PORTCPLD_REG_SCRATCH_PAD 0x05 + +/*BMC status*/ +#define SYSCPLD_REG_BMC_STATUS 0x29 + + +enum cpld_type { + sys_cpld, + fan_cpld, + port_cpld1, + port_cpld2, +}; + +enum cpld_index { + SYS_CPLD, /* SYS CPLD */ + FAN_CPLD, /* FANU CPLD */ + PORT_CPLD1, /* IOU CPLD */ + PORT_CPLD2, /* Switch CPLD1 */ + NUM_CPLD_INDEX, +}; + +struct cpld_drvdata { + struct class *cpld_class; + struct device *cpld_udev; + struct cdev cpld_cdev; + dev_t devt; + struct mutex sem; +}; + +struct cpld_drivers_t { + ssize_t (*get_reboot_cause) (char *buf); + ssize_t (*get_alias) (unsigned int index, char *buf); + ssize_t (*get_type) (unsigned int index, char *buf); + ssize_t (*get_hw_version) (unsigned int index, char *buf); + ssize_t (*get_board_version) (unsigned int index, char *buf); + ssize_t (*get_fmea_selftest_status) (unsigned int index, char *buf); + ssize_t (*set_reg_test) (unsigned int index, unsigned char data); + ssize_t (*get_fmea_corrosion_status) (unsigned int index, char *buf); + ssize_t (*get_fmea_voltage_status) (unsigned int index, char *buf); + ssize_t (*get_fmea_clock_status) (unsigned int index, char *buf); + ssize_t (*get_fmea_battery_status) (unsigned int index, char *buf); + ssize_t (*get_fmea_reset) (unsigned int index, char *buf); + bool (*set_fmea_reset) (unsigned int index, int reset); + ssize_t (*get_fmea_interrupt) (unsigned int index, char *buf); + void (*get_loglevel) (long *lev); + void (*set_loglevel) (long lev); + ssize_t (*debug_help) (char *buf); + ssize_t (*debug) (const char *buf, int count); +}; + +ssize_t drv_get_reboot_cause(char *buf); +ssize_t drv_get_alias(unsigned int index, char *buf); +ssize_t drv_get_type(unsigned int index, char *buf); +ssize_t drv_get_hw_version(unsigned int index, char *buf); +ssize_t drv_get_board_version(unsigned int index, char *buf); +ssize_t drv_get_reg_test(unsigned int index, char *buf); +ssize_t drv_get_fmea_corrosion_status(unsigned int index, char *buf); +ssize_t drv_get_fmea_voltage_status(unsigned int index, char *buf); +ssize_t drv_get_fmea_clock_status(unsigned int index, char *buf); +ssize_t drv_get_fmea_battery_status(unsigned int index, char *buf); +ssize_t drv_get_fmea_reset(unsigned int index, char *buf); +bool drv_set_fmea_reset(unsigned int index, int reset); +ssize_t drv_get_fmea_interrupt(unsigned int index, char *buf); +void drv_get_loglevel(long *lev); +void drv_set_loglevel(long lev); +ssize_t drv_debug_help(char *buf); +ssize_t drv_debug_help_hisonic(char *buf); +ssize_t drv_debug(const char *buf, int count); + +int switch_cpld_reg_read(u8 cpld_index, u8 reg_offset, u8 *reg_value); +int switch_cpld_reg_write(u8 cpld_index, u8 reg_offset, u8 reg_value); + +int switch_system_cpld_read(u8 reg, u8 *value); +int switch_system_cpld_write(u8 reg, u8 value); + +int switch_i2c_cpld_read(u8 index, u8 reg, u8 *value); +int switch_i2c_cpld_write(u8 index, u8 reg, u8 value); + +void s3ip_cpld_drivers_register(struct cpld_drivers_t *p_func); +void s3ip_cpld_drivers_unregister(void); + +#endif /* SWITCH_CPLD_DRIVER_H */ diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_fan_driver.c b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_fan_driver.c new file mode 100644 index 0000000000..7cd23d475c --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_fan_driver.c @@ -0,0 +1,769 @@ +#include +#include +#include +#include +#include +#include + +#include "switch_fan_driver.h" +#include "switch_cpld_driver.h" +#include "switch_at24.h" +#include "sysfs_ipmi.h" + +#define DRVNAME "drv_fan_driver" +#define SWITCH_FAN_DRIVER_VERSION "0.0.1" + +unsigned int loglevel = 0; +static struct platform_device *drv_fan_device; +unsigned int scan_period = 5; + +static unsigned int fan_fmea_status[MAX_FAN_NUM] = {0}; +static uint8_t fan_eeprom[MAX_FAN_NUM][FAN_EEPROM_SIZE]; + +/* Set to 1 if we've read EEPROM into memory */ +static int has_been_read[MAX_FAN_NUM] = {0}; + +struct mutex update_lock; + +const char *delim = "\r\n"; +const char *sn_str = "BarCode="; +const char *part_num_str = "Item="; +const char *model_str = "Model="; +const char *vendor_str = "VendorName="; +const char *boardtype_str = "BoardType="; +// there are two kind of fans, use the same threshold to check +/* + 29500*pwm-6400 32000*pwm+6400 +100% 23100 38400 +90% 20150 35200 +80% 17200 32000 +70% 14250 28800 +60% 11300 25600 +50% 8350 22400 +40% 5400 19200 +*/ +//static int max_speed1 = 29500; +//static int max_speed2 = 32000; + +static int fan_speed_max[MAX_FAN_NUM][MAX_MOTOR_NUM] = { + {36000, 33000}, + {36000, 33000}, + {36000, 33000}, + {36000, 33000}, + {36000, 33000}, + {36000, 33000} +}; + +static int fan_speed_min[MAX_FAN_NUM][MAX_MOTOR_NUM] = { + {5500, 5000}, + {5500, 5000}, + {5500, 5000}, + {5500, 5000}, + {5500, 5000}, + {5500, 5000} +}; + +static int fan_speed_tolerance[MAX_FAN_NUM][MAX_MOTOR_NUM] = { + {3000, 2750}, + {3000, 2750}, + {3000, 2750}, + {3000, 2750}, + {3000, 2750}, + {3000, 2750} +}; + +static int i2c_bus_fan_eeprom[MAX_FAN_NUM] = {106, 107, 108, 109, 110}; + +static unsigned int fan_pwm_reg_offset_table[MAX_FAN_NUM] = { + 0x08, //FAN_PWM_CTL1_OFFSET + 0x09, //FAN_PWM_CTL2_OFFSET + 0x0a, //FAN_PWM_CTL3_OFFSET + 0x0b, //FAN_PWM_CTL4_OFFSET + 0x0c, //FAN_PWM_CTL5_OFFSET + 0x0d, //FAN_PWM_CTL6_OFFSET +}; + +int read_fan_eeprom(unsigned int fan_index, uint8_t *eeprom) +{ + int ret; + + if(has_been_read[fan_index-1]) + { + /* read eeprom once the plug event has been detected */ + if(!drv_get_plug_his(fan_index)) + { + return 0; + } + } + + FAN_DEBUG("Read EEPROM...\n"); + + mutex_lock(&update_lock); + + /* erase the buffer before reading */ +#ifdef C11_ANNEX_K + memset_s(eeprom, FAN_EEPROM_SIZE, 0x0, FAN_EEPROM_SIZE); +#else + memset(eeprom, 0x0, FAN_EEPROM_SIZE); +#endif + ret = at24_read_fan_eeprom(i2c_bus_fan_eeprom[fan_index-1], eeprom, 0, FAN_EEPROM_SIZE); + if(ret < 0) + { + FAN_ERR("Read EEPROM failed: %d\n", ret); + mutex_unlock(&update_lock); + return -1; + } + + has_been_read[fan_index-1] = 1; + + mutex_unlock(&update_lock); + + return ret; +} + +unsigned int drv_get_number(void) +{ + return MAX_FAN_NUM; +} + +bool drv_get_fan_powergood(unsigned short *bitmap) +{ + return true; +} + +bool drv_get_plug_his(unsigned int fan_index) +{ + return false; +} + +bool drv_get_alarm(unsigned int fan_index, int *alarm) +{ + return true; +} + +ssize_t drv_get_model_name(unsigned int fan_index, char *buf) +{ + int ret; + char *token, *end; + char temp[FAN_EEPROM_SIZE] = {0}; + + ret = read_fan_eeprom(fan_index, fan_eeprom[fan_index-1]); + if(ret < 0) + { + return -1; + } +#ifdef C11_ANNEX_K + if(memcpy_s(temp, FAN_EEPROM_SIZE, fan_eeprom[fan_index-1], FAN_EEPROM_SIZE) != 0) + { + return -ENOMEM; + } +#else + memcpy(temp, fan_eeprom[fan_index-1], FAN_EEPROM_SIZE); +#endif + + end = token = temp; + while(token != NULL) + { + strsep(&end, delim); + if(strstr(token, boardtype_str)) + { + break; + } + token = end; + } + if(token) + { +#ifdef C11_ANNEX_K + return sprintf_s(buf, FAN_EEPROM_SIZE, "%s\n", token+strlen(boardtype_str)); +#else + return sprintf(buf, "%s\n", token+strlen(boardtype_str)); +#endif + } + + end = token = temp; + while(token != NULL) + { + strsep(&end, delim); + if(strstr(token, model_str)) + { + break; + } + token = end; + } + if(token) + { +#ifdef C11_ANNEX_K + return sprintf_s(buf, FAN_EEPROM_SIZE, "%s\n", token+strlen(model_str)); +#else + return sprintf(buf, "%s\n", token+strlen(model_str)); +#endif + + } +#ifdef C11_ANNEX_K + return sprintf_s(buf, FAN_EEPROM_SIZE, "NA\n"); +#else + return sprintf(buf, "NA\n"); +#endif + +} + +ssize_t drv_get_sn(unsigned int fan_index, char *buf) +{ + int ret; + char *token, *end; + char temp[FAN_EEPROM_SIZE]; + + ret = read_fan_eeprom(fan_index, fan_eeprom[fan_index-1]); + if(ret < 0) + return -1; +#ifdef C11_ANNEX_K + if(memcpy_s(temp, FAN_EEPROM_SIZE, fan_eeprom[fan_index-1], FAN_EEPROM_SIZE) != 0) + { + return -ENOMEM; + } +#else + memcpy(temp, fan_eeprom[fan_index-1], FAN_EEPROM_SIZE); +#endif + + end = token = temp; + + while(token != NULL) + { + strsep(&end, delim); + + if(strstr(token, sn_str)) + break; + + token = end; + } + + if(token) +#ifdef C11_ANNEX_K + return sprintf_s(buf, FAN_EEPROM_SIZE, "%s\n", token+strlen(sn_str)); +#else + return sprintf(buf, "%s\n", token+strlen(sn_str)); +#endif + + else +#ifdef C11_ANNEX_K + return sprintf_s(buf, FAN_EEPROM_SIZE, "NA\n"); +#else + return sprintf(buf, "NA\n"); +#endif + +} + +ssize_t drv_get_vendor(unsigned int fan_index, char *buf) +{ + int ret; + char *token, *end; + char temp[FAN_EEPROM_SIZE]; + + ret = read_fan_eeprom(fan_index, fan_eeprom[fan_index-1]); + if(ret < 0) + return -1; +#ifdef C11_ANNEX_K + if(memcpy_s(temp, FAN_EEPROM_SIZE, fan_eeprom[fan_index-1], FAN_EEPROM_SIZE) != 0) + { + return -ENOMEM; + } +#else + memcpy(temp, fan_eeprom[fan_index-1], FAN_EEPROM_SIZE); +#endif + + end = token = temp; + + while(token != NULL) + { + strsep(&end, delim); + + if(strstr(token, vendor_str)) + break; + + token = end; + } + + if(token) +#ifdef C11_ANNEX_K + return sprintf_s(buf, FAN_EEPROM_SIZE, "%s\n", token+strlen(vendor_str)); +#else + return sprintf(buf, "%s\n", token+strlen(vendor_str)); +#endif + + else +#ifdef C11_ANNEX_K + return sprintf_s(buf, FAN_EEPROM_SIZE, "NA\n"); +#else + return sprintf(buf, "NA\n"); +#endif + +} + +ssize_t drv_get_part_number(unsigned int fan_index, char *buf) +{ + int ret; + char *token, *end; + char temp[FAN_EEPROM_SIZE]; + + ret = read_fan_eeprom(fan_index, fan_eeprom[fan_index-1]); + if(ret < 0) + return -1; +#ifdef C11_ANNEX_K + if(memcpy_s(temp, FAN_EEPROM_SIZE, fan_eeprom[fan_index-1], FAN_EEPROM_SIZE) != 0) + { + return -ENOMEM; + } +#else + memcpy(temp, fan_eeprom[fan_index-1], FAN_EEPROM_SIZE); +#endif + + end = token = temp; + + while(token != NULL) + { + strsep(&end, delim); + + if(strstr(token, part_num_str)) + break; + + token = end; + } + + if(token) +#ifdef C11_ANNEX_K + return sprintf_s(buf, FAN_EEPROM_SIZE, "%s\n", token+strlen(part_num_str)); +#else + return sprintf(buf, "%s\n", token+strlen(part_num_str)); +#endif + + else +#ifdef C11_ANNEX_K + return sprintf_s(buf, FAN_EEPROM_SIZE, "NA\n"); +#else + return sprintf(buf, "NA\n"); +#endif + +} + +ssize_t drv_get_hw_version(unsigned int fan_index, char *buf) +{ +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "NA\n"); +#else + return sprintf(buf, "NA\n"); +#endif + +} + +ssize_t drv_get_speed(unsigned int fan_index, unsigned int motor_index, unsigned int *speed) +{ + int retval=0; + + retval = drv_get_speed_from_bmc(fan_index, motor_index, speed); + + if(retval < 0) + { + FAN_ERR("Get fan%d motor%d speed failed.\n", fan_index, motor_index); + return retval; + } + + return retval; +} + +ssize_t drv_get_speed_target(unsigned int fan_index, unsigned int motor_index, unsigned int *speed_target) +{ + int pwm=0; + unsigned int retval=0; + retval = drv_get_pwm_from_bmc(fan_index,&pwm); + if ((retval < 0) || (pwm < 30) || (pwm > 100)) + { + FAN_ERR("Get fan%d pwm failed.\n", fan_index); + return -1; + } + + if(motor_index == 1) + *speed_target = 264*pwm + 3570; + else if(motor_index == 2) + *speed_target = 243*pwm + 3220; + return 0; +} + +ssize_t drv_get_speed_max(unsigned int fan_index, unsigned int motor_index, unsigned int *speed) +{ + if((1 <= fan_index && fan_index <= MAX_FAN_NUM) && (1 <= motor_index && motor_index <= MAX_MOTOR_NUM)) + { + *speed = fan_speed_max[fan_index-1][motor_index-1]; + } + else + { + return -EINVAL; + } + return 0; +} + +ssize_t drv_get_speed_min(unsigned int fan_index, unsigned int motor_index, unsigned int *speed) +{ + if((1 <= fan_index && fan_index <= MAX_FAN_NUM) && (1 <= motor_index && motor_index <= MAX_MOTOR_NUM)) + { + *speed = fan_speed_min[fan_index-1][motor_index-1]; + } + else + { + return -EINVAL; + } + return 0; +} + + +ssize_t drv_get_speed_tolerance(unsigned int fan_index, unsigned int motor_index, unsigned int *speed_tolerance) +{ + if((1 <= fan_index && fan_index <= MAX_FAN_NUM) && (1 <= motor_index && motor_index <= MAX_MOTOR_NUM)) + { + *speed_tolerance = fan_speed_tolerance[fan_index-1][motor_index-1]; + } + else + { + return -EINVAL; + } + return 0; +} + + +ssize_t drv_get_pwm(unsigned int fan_index, int *pwm) +{ + int ret; + unsigned char buf; + unsigned int offset; + + offset = fan_pwm_reg_offset_table[fan_index-1]; + + ret = switch_cpld_reg_read(FAN_CPLD, offset, &buf); + if(ret < 0) + return ret; + + /* add 1 to roundup the value for pwm consistency */ + *pwm = ((buf+1) * 100) / 255; + + return 0; +} + + +ssize_t drv_set_pwm(unsigned int fan_index, int pwm) +{ + unsigned int offset; + int ret = 0; + + offset = fan_pwm_reg_offset_table[fan_index-1]; + + if((pwm>100) || (pwm<0)) + { + FAN_DEBUG("[%s] Invalid pwm value.\n", __func__); + return false; + } + + if(pwm < 30) + pwm = 30; + + ret = switch_cpld_reg_write(FAN_CPLD, offset, pwm); + + return ret; +} + +ssize_t drv_get_alarm_threshold(char *buf) +{ + return 0; +} + +ssize_t drv_get_wind(unsigned int fan_index, unsigned int *wind) +{ + *wind = FAN_DIRECT_F2B; + return 0; +} + +ssize_t drv_get_led_status(unsigned int fan_index, unsigned int *val) +{ + return 0; +} + +ssize_t drv_set_led_status(unsigned int fan_index, unsigned int led) +{ + return 0; +} + +ssize_t drv_get_scan_period(char *buf) +{ +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "present soft scan time is %d x 100ms.\n", scan_period); +#else + return sprintf(buf, "present soft scan time is %d x 100ms.\n", scan_period); +#endif + +} + +void drv_set_scan_period(unsigned int period) +{ + scan_period = period; + + return; +} + +unsigned int drv_get_status(unsigned int fan_index) +{ + unsigned int speed; + unsigned int speed_target; + unsigned int speed_tolerance=0; + unsigned int retval; + unsigned int motor_index; + bool has_failed = false; + + for(motor_index = 1; motor_index <= MAX_MOTOR_NUM; motor_index++) + { + retval = drv_get_speed(fan_index, motor_index-1, &speed); + if(retval < 0) + { + FAN_ERR("Get fan%d motor%d speed failed.\n", fan_index, motor_index); + return -1; + } + + retval = drv_get_speed_target(fan_index, motor_index, &speed_target); + if(retval < 0) + { + FAN_ERR("Get fan%d motor%d speed target failed.\n", fan_index, motor_index); + return -1; + } + + retval = drv_get_speed_tolerance(fan_index, motor_index, &speed_tolerance); + if(retval < 0) + { + FAN_DEBUG("Get fan%d motor%d speed tolerance failed.\n", fan_index, motor_index); + return -1; + } + FAN_DEBUG("Fan%d motor%d speed %d is out of valid range? (%d-%d).", fan_index, motor_index, speed, (speed_target - speed_tolerance), (speed_target + speed_tolerance)); + if((speed > (speed_target + speed_tolerance)) || (speed < (speed_target - speed_tolerance))) + { + has_failed = true; + FAN_DEBUG("Fan%d motor%d speed %d is out of valid range (%d-%d).", fan_index, motor_index, speed, (speed_target - speed_tolerance), (speed_target + speed_tolerance)); + } + } + + if(has_failed) + { + return 2; // not ok + } + else + { + return 1; // ok + } +} + +void drv_get_loglevel(long *lev) +{ + *lev = (long)loglevel; + + return; +} + +void drv_set_loglevel(long lev) +{ + loglevel = (unsigned int)lev; + + return; +} + +ssize_t drv_debug_help(char *buf) +{ +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, + "Relative registers: cs:0 reg: 0x1200\n" + "Use the following command to debug:\n" + "lpc_cpld read \n" + "lpc_cpld write \n" + "To get the fan current speed: (output value*30 = current speed)\n" + "sysname debug cmd\n" + "fan1 motor0 lpc_cpld read 0 0x1210\n" + "fan2 motor0 lpc_cpld read 0 0x1212\n" + "fan3 motor0 lpc_cpld read 0 0x1214\n" + "fan4 motor0 lpc_cpld read 0 0x1216\n" + "fan5 motor0 lpc_cpld read 0 0x1218\n" + "To get the fan current pwm: (output value*2 = current pwm)\n" + "fan1 lpc_cpld read 0 0x1208\n" + "fan2 lpc_cpld read 0 0x1209\n" + "fan3 lpc_cpld read 0 0x120a\n" + "fan4 lpc_cpld read 0 0x120b\n" + "fan5 lpc_cpld read 0 0x120c\n"); +#else + return sprintf(buf, + "Relative registers: cs:0 reg: 0x1200\n" + "Use the following command to debug:\n" + "lpc_cpld read \n" + "lpc_cpld write \n" + "To get the fan current speed: (output value*30 = current speed)\n" + "sysname debug cmd\n" + "fan1 motor0 lpc_cpld read 0 0x1210\n" + "fan2 motor0 lpc_cpld read 0 0x1212\n" + "fan3 motor0 lpc_cpld read 0 0x1214\n" + "fan4 motor0 lpc_cpld read 0 0x1216\n" + "fan5 motor0 lpc_cpld read 0 0x1218\n" + "To get the fan current pwm: (output value*2 = current pwm)\n" + "fan1 lpc_cpld read 0 0x1208\n" + "fan2 lpc_cpld read 0 0x1209\n" + "fan3 lpc_cpld read 0 0x120a\n" + "fan4 lpc_cpld read 0 0x120b\n" + "fan5 lpc_cpld read 0 0x120c\n"); +#endif + +} + +ssize_t drv_debug(const char *buf, int count) +{ + return 0; +} + +int drv_get_fmea_status(unsigned int fan_index) +{ + return fan_fmea_status[fan_index-1]; +} + +void drv_set_fmea_status(unsigned int fan_index, int status) +{ + fan_fmea_status[fan_index-1] = status; +} + +// For s3ip +static struct fan_drivers_t pfunc = { + .get_scan_period = drv_get_scan_period, + .set_scan_period = drv_set_scan_period, + .get_model_name = drv_get_model_name, + .get_sn = drv_get_sn, + .get_vendor = drv_get_vendor, + .get_part_number = drv_get_part_number, + .get_hw_version = drv_get_hw_version, + .get_speed = drv_get_speed, + .get_speed_target = drv_get_speed_target, + .get_speed_max = drv_get_speed_max, + .get_speed_min = drv_get_speed_min, + .get_speed_tolerance = drv_get_speed_tolerance, + .get_pwm = drv_get_pwm, + .set_pwm = drv_set_pwm, + .get_wind = drv_get_wind, + .get_led_status = drv_get_led_status, + .set_led_status = drv_set_led_status, + .get_status = drv_get_status, + .get_loglevel = drv_get_loglevel, + .set_loglevel = drv_set_loglevel, + .debug_help = drv_debug_help, + .get_fmea_status = drv_get_fmea_status, + .set_fmea_status = drv_set_fmea_status, +}; + +static struct fan_drivers_t pfunc_bmc = { + .get_scan_period = drv_get_scan_period, + .set_scan_period = drv_set_scan_period, + .get_model_name = drv_get_model_name_from_bmc, + .get_sn = drv_get_sn_from_bmc, + .get_vendor = drv_get_vendor_from_bmc, + .get_part_number = drv_get_part_number_from_bmc, + .get_hw_version = drv_get_hw_version_from_bmc, + .get_speed = drv_get_speed, + .get_speed_target = drv_get_speed_target, + .get_speed_max = drv_get_speed_max, + .get_speed_min = drv_get_speed_min, + .get_speed_tolerance = drv_get_speed_tolerance, + .get_pwm = drv_get_pwm_from_bmc, + .set_pwm = drv_set_pwm_from_bmc, + .get_wind = drv_get_wind, + .get_led_status = drv_get_led_status_from_bmc, + .set_led_status = drv_set_led_status_from_bmc, + .get_status = drv_get_status, + .get_loglevel = drv_get_loglevel, + .set_loglevel = drv_set_loglevel, + .debug_help = drv_debug_help, + .get_fmea_status = drv_get_fmea_status, + .set_fmea_status = drv_set_fmea_status, +}; + +static int drv_fan_probe(struct platform_device *pdev) +{ + if(ipmi_bmc_is_ok()) + { + s3ip_fan_drivers_register(&pfunc_bmc); + } + else + { + s3ip_fan_drivers_register(&pfunc); + } + + return 0; +} + +static int drv_fan_remove(struct platform_device *pdev) +{ + s3ip_fan_drivers_unregister(); + + return 0; +} + +static struct platform_driver drv_fan_driver = { + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + }, + .probe = drv_fan_probe, + .remove = drv_fan_remove, +}; + +static int __init drv_fan_init(void) +{ + int err=0; + int retval; + + drv_fan_device = platform_device_alloc(DRVNAME, 0); + if(!drv_fan_device) + { + FAN_DEBUG("%s(#%d): platform_device_alloc fail\n", __func__, __LINE__); + return -ENOMEM; + } + + retval = platform_device_add(drv_fan_device); + if(retval) + { + FAN_DEBUG("%s(#%d): platform_device_add failed\n", __func__, __LINE__); + err = -retval; + goto dev_add_failed; + } + + retval = platform_driver_register(&drv_fan_driver); + if(retval) + { + FAN_DEBUG("%s(#%d): platform_driver_register failed(%d)\n", __func__, __LINE__, err); + err = -retval; + goto dev_reg_failed; + } + + mutex_init(&update_lock); + return 0; + +dev_reg_failed: + platform_device_unregister(drv_fan_device); + return err; + +dev_add_failed: + platform_device_put(drv_fan_device); + return err; +} + +static void __exit drv_fan_exit(void) +{ + platform_driver_unregister(&drv_fan_driver); + platform_device_unregister(drv_fan_device); + + mutex_destroy(&update_lock); + return; +} + +MODULE_DESCRIPTION("S3IP Fan Driver"); +MODULE_VERSION(SWITCH_FAN_DRIVER_VERSION); +MODULE_LICENSE("GPL"); + +module_init(drv_fan_init); +module_exit(drv_fan_exit); diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_fan_driver.h b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_fan_driver.h new file mode 100644 index 0000000000..e011cc2245 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_fan_driver.h @@ -0,0 +1,101 @@ +#ifndef SWITCH_FAN_DRIVER_H +#define SWITCH_FAN_DRIVER_H + +#include "switch.h" + +#define FAN_ERR(fmt, args...) LOG_ERR("fan: ", fmt, ##args) +#define FAN_WARNING(fmt, args...) LOG_WARNING("fan: ", fmt, ##args) +#define FAN_INFO(fmt, args...) LOG_INFO("fan: ", fmt, ##args) +#define FAN_DEBUG(fmt, args...) LOG_DBG("fan: ", fmt, ##args) + +#define MAX_FAN_NUM 6 +#define MAX_MOTOR_NUM 2 +#define FAN_DIRECT_F2B 0 +#define FAN_DIRECT_B2F 1 + +#define FAN_EEPROM_SIZE 512 + +#define FAN_ONLINE_OFFSET 0x00 +#define FAN_DIRECTION_OFFSET 0x02 +#define FAN_PLUG_HIS_OFFSET 0x04 +#define FAN_PWM_CTL1_OFFSET 0x08 +#define FAN_PWM_CTL2_OFFSET 0x09 +#define FAN_PWM_CTL3_OFFSET 0x0a +#define FAN_PWM_CTL4_OFFSET 0x0b +#define FAN_PWM_CTL5_OFFSET 0x0c +#define FAN_LED_CTL1_OFFSET 0x20 +#define FAN_LED_CTL2_OFFSET 0x21 +#define FAN_POWERGOOD_OFFSET 0x24 + +#define LED_ALL_ON 0x00 +#define LED_GREEN_ON 0x0c +#define LED_GREEN_FLASH_SLOW 0x0d +#define LED_GREEN_FLASH_FAST 0x0e +#define LED_RED_ON 0x03 +#define LED_RED_FLASH_SLOW 0x07 +#define LED_RED_FLASH_FAST 0x0b +#define LED_ALL_OFF 0x0f + +struct fan_drivers_t{ + ssize_t (*get_model_name) (unsigned int index, char* buf); + ssize_t (*get_sn) (unsigned int index, char* buf); + ssize_t (*get_vendor) (unsigned int index, char* buf); + ssize_t (*get_part_number) (unsigned int index, char* buf); + ssize_t (*get_hw_version) (unsigned int index, char* buf); + unsigned int (*get_number) (void); + bool (*get_alarm) (unsigned int index, int* alarm); + ssize_t (*get_speed) (unsigned int fan_index, unsigned int motor_index, unsigned int *speed); + ssize_t (*get_speed_target) (unsigned int fan_index, unsigned int motor_index, unsigned int *speed_target); + ssize_t (*get_speed_max) (unsigned int fan_index, unsigned int motor_index, unsigned int *speed); + ssize_t (*get_speed_min) (unsigned int fan_index, unsigned int motor_index, unsigned int *speed); + ssize_t (*get_speed_tolerance) (unsigned int fan_index, unsigned int motor_index, unsigned int *speed_tolerance); + unsigned int (*get_status) (unsigned int fan_index); + ssize_t (*get_pwm) (unsigned int index, int *pwm); + ssize_t (*set_pwm) (unsigned int index, int pwm); + ssize_t (*get_wind) (unsigned int fan_index, unsigned int *wind); + ssize_t (*get_led_status) (unsigned int fan_index, unsigned int *led); + ssize_t (*set_led_status) (unsigned int fan_index, unsigned int led); + void (*get_loglevel) (long *lev); + void (*set_loglevel) (long lev); + ssize_t (*get_alarm_threshold) (char *buf); + ssize_t (*get_scan_period) (char *buf); + void (*set_scan_period) (unsigned int period); + ssize_t (*debug_help) (char *buf); + ssize_t (*debug) (const char *buf, int count); + int (*get_fmea_status) (unsigned int fan_index); + void (*set_fmea_status) (unsigned int fan_index, int status); +}; + +ssize_t drv_get_model_name(unsigned int fan_index, char *buf); +ssize_t drv_get_sn(unsigned int fan_index, char *buf); +ssize_t drv_get_vendor(unsigned int fan_index, char *buf); +ssize_t drv_get_part_number(unsigned int fan_index, char *buf); +ssize_t drv_get_hw_version(unsigned int fan_index, char *buf); +unsigned int drv_get_number(void); +bool drv_get_plug_his(unsigned int fan_index); +bool drv_get_alarm(unsigned int fan_index, int *alarm); +ssize_t drv_get_speed(unsigned int fan_index, unsigned int motor_index, unsigned int *speed); +ssize_t drv_get_speed_target(unsigned int fan_index, unsigned int motor_index, unsigned int *speed_target); +ssize_t drv_get_speed_max(unsigned int fan_index, unsigned int motor_index, unsigned int *speed); +ssize_t drv_get_speed_min(unsigned int fan_index, unsigned int motor_index, unsigned int *speed); +ssize_t drv_get_speed_tolerance(unsigned int fan_index, unsigned int motor_index, unsigned int *speed_tolerance); +unsigned int drv_get_status(unsigned int fan_index); +ssize_t drv_get_pwm(unsigned int fan_index, int *pwm); +ssize_t drv_set_pwm(unsigned int fan_index, int pwm); +ssize_t drv_get_alarm_threshold(char *buf); +ssize_t drv_get_wind(unsigned int fan_index, unsigned int *wind); +ssize_t drv_get_led_status(unsigned int fan_index, unsigned int *led); +ssize_t drv_set_led_status(unsigned int fan_index, unsigned int led); +ssize_t drv_get_scan_period(char *buf); +void drv_set_scan_period(unsigned int period); +void drv_get_loglevel(long *lev); +void drv_set_loglevel(long lev); +ssize_t drv_debug_help(char *buf); +ssize_t drv_debug(const char *buf, int count); +int drv_get_fmea_status(unsigned int fan_index); +void drv_set_fmea_status(unsigned int fan_index, int status); + +void s3ip_fan_drivers_register(struct fan_drivers_t *p_func); +void s3ip_fan_drivers_unregister(void); + +#endif /* SWITCH_FAN_DRIVER_H */ diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_fpga_driver.c b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_fpga_driver.c new file mode 100644 index 0000000000..b6e37f7864 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_fpga_driver.c @@ -0,0 +1,226 @@ +#include +#include +#include +#include +#include + +#include "switch_system_fpga.h" +#include "switch_fpga_driver.h" + +#define DRVNAME "drv_fpga_driver" +#define SWITCH_FPGA_DRIVER_VERSION "0.0.1" + +unsigned int loglevel = 0; +//module_param(loglevel,int,S_IRUGO); + +static struct platform_device *drv_fpga_device; + + +static char *fpga_alias_name[FPGA_TOTAL_NUM] = { + "fpga" +}; + +static char *fpga_type_name[FPGA_TOTAL_NUM] = { + "Xilinx" //Xilinx xc7a35t-2fgg484c +}; + +static int fpga_write(u8 device_id ,u8 reg, u32 value) +{ + return fpga_pcie_write( device_id, reg, value); +} + +static int fpga_read(u8 device_id ,u8 reg) +{ + return fpga_pcie_read( device_id, reg); +} + +ssize_t drv_get_alias(unsigned int index, char *buf) +{ +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%s\n", fpga_alias_name[index-1]); +#else + return sprintf(buf, "%s\n", fpga_alias_name[index-1]); +#endif +} + +ssize_t drv_get_type(unsigned int index, char *buf) +{ +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%s\n", fpga_type_name[index-1]); +#else + return sprintf(buf, "%s\n", fpga_type_name[index-1]); +#endif + +} + +ssize_t drv_get_hw_version(unsigned int index, char *buf) +{ + unsigned int val; + int len=0; + val = fpga_read(index-1,FPGA_VER_OFFSET); +#ifdef C11_ANNEX_K + len = sprintf_s(buf, PAGE_SIZE, "%x.%x\n", (val>>8)&0xff,val&0xff); +#else + len = sprintf(buf, "%x.%x\n", (val>>8)&0xff,val&0xff); +#endif + + + return len; +} + +ssize_t drv_get_board_version(unsigned int index, char *buf) +{ + unsigned int val; + int len=0; + val = fpga_read(index-1,FPGA_VER_OFFSET); +#ifdef C11_ANNEX_K + len = sprintf_s(buf, PAGE_SIZE, "%x\n", (val>>24)&0xff); +#else + len = sprintf(buf, "%x\n", (val>>24)&0xff); +#endif + + + return len; +} + +ssize_t drv_get_reg_test(unsigned int index, char *buf) +{ + u32 val; + val = fpga_read(index-1,FPGA_SCRATCHPAD_OFFSET); +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "0x%x\n", val); +#else + return sprintf(buf, "0x%x\n", val); +#endif + +} + +ssize_t drv_set_reg_test(unsigned int index, unsigned int data) +{ + unsigned int val; + val = fpga_write(index-1,FPGA_SCRATCHPAD_OFFSET,data); + + return val; +} + +void drv_get_loglevel(long *lev) +{ + *lev = (long)loglevel; + return; +} + +void drv_set_loglevel(long lev) +{ + loglevel = (unsigned int)lev; + return; +} + +ssize_t drv_debug_help(char *buf) +{ +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, + "Use the following command to debug:\n" + "busybox devmem 0xfbc00000 32\n"); +#else + return sprintf(buf, + "Use the following command to debug:\n" + "busybox devmem 0xfbc00000 32\n"); +#endif + +} + +ssize_t drv_debug(const char *buf, int count) +{ + return 0; +} + + +static struct fpga_drivers_t pfunc = { + .get_alias = drv_get_alias, + .get_type = drv_get_type, + .get_hw_version = drv_get_hw_version, + .get_board_version = drv_get_board_version, + .get_reg_test = drv_get_reg_test, + .set_reg_test = drv_set_reg_test, + .get_loglevel = drv_get_loglevel, + .set_loglevel = drv_set_loglevel, + .debug_help = drv_debug_help, +}; + +static int drv_fpga_probe(struct platform_device *pdev) +{ + s3ip_fpga_drivers_register(&pfunc); + + return 0; +} + +static int drv_fpga_remove(struct platform_device *pdev) +{ + s3ip_fpga_drivers_unregister(); + + return 0; +} + +static struct platform_driver drv_fpga_driver = { + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + }, + .probe = drv_fpga_probe, + .remove = drv_fpga_remove, +}; + +static int __init drv_fpga_init(void) +{ + int err=0; + int retval=0; + + drv_fpga_device = platform_device_alloc(DRVNAME, 0); + if(!drv_fpga_device) + { + FPGA_DEBUG("%s(#%d): platform_device_alloc fail\n", __func__, __LINE__); + return -ENOMEM; + } + + retval = platform_device_add(drv_fpga_device); + if(retval) + { + FPGA_DEBUG("%s(#%d): platform_device_add failed\n", __func__, __LINE__); + err = -retval; + goto dev_add_failed; + } + + retval = platform_driver_register(&drv_fpga_driver); + if(retval) + { + FPGA_DEBUG("%s(#%d): platform_driver_register failed(%d)\n", __func__, __LINE__, err); + err = -retval; + goto dev_reg_failed; + } + + return 0; + +dev_reg_failed: + platform_device_unregister(drv_fpga_device); + +dev_add_failed: + platform_device_put(drv_fpga_device); + + return err; +} + +static void __exit drv_fpga_exit(void) +{ + platform_driver_unregister(&drv_fpga_driver); + platform_device_unregister(drv_fpga_device); + platform_device_put(drv_fpga_device); + + return; +} + +MODULE_DESCRIPTION("S3IP FPGA Driver"); +MODULE_VERSION(SWITCH_FPGA_DRIVER_VERSION); +MODULE_LICENSE("GPL"); + +module_init(drv_fpga_init); +module_exit(drv_fpga_exit); diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_fpga_driver.h b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_fpga_driver.h new file mode 100644 index 0000000000..c1ebf92fb4 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_fpga_driver.h @@ -0,0 +1,43 @@ +#ifndef SWITCH_FPGA_DRIVER_H +#define SWITCH_FPGA_DRIVER_H + +#include "switch.h" + +#define FPGA_ERR(fmt, args...) LOG_ERR("fpga: ", fmt, ##args) +#define FPGA_WARNING(fmt, args...) LOG_WARNING("fpga: ", fmt, ##args) +#define FPGA_INFO(fmt, args...) LOG_INFO("fpga: ", fmt, ##args) +#define FPGA_DEBUG(fmt, args...) LOG_DBG("fpga: ", fmt, ##args) + +#define FPGA_TOTAL_NUM 1 +#define FPGA_VER_OFFSET 0x00 +#define FPGA_SCRATCHPAD_OFFSET 0x04 + +struct fpga_drivers_t{ + ssize_t (*get_alias) (unsigned int index, char *buf); + ssize_t (*get_type) (unsigned int index, char *buf); + ssize_t (*get_hw_version) (unsigned int index, char *buf); + ssize_t (*get_board_version) (unsigned int index, char *buf); + ssize_t (*get_reg_test) (unsigned int index, char *buf); + ssize_t (*set_reg_test) (unsigned int index, unsigned int data); + void (*get_loglevel) (long *lev); + void (*set_loglevel) (long lev); + ssize_t (*debug_help) (char *buf); + ssize_t (*debug) (const char *buf, int count); +}; + + +ssize_t drv_get_alias(unsigned int index, char *buf); +ssize_t drv_get_type(unsigned int index, char *buf); +ssize_t drv_get_hw_version(unsigned int index, char *buf); +ssize_t drv_get_board_version(unsigned int index, char *buf); +ssize_t drv_get_reg_test(unsigned int index, char *buf); +ssize_t drv_set_reg_test(unsigned int index, unsigned int data); +void drv_get_loglevel(long *lev); +void drv_set_loglevel(long lev); +ssize_t drv_debug_help(char *buf); +ssize_t drv_debug(const char *buf, int count); + +void s3ip_fpga_drivers_register(struct fpga_drivers_t *p_func); +void s3ip_fpga_drivers_unregister(void); + +#endif /* SWITCH_FPGA_DRIVER_H */ diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_led_driver.c b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_led_driver.c new file mode 100644 index 0000000000..511c193a59 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_led_driver.c @@ -0,0 +1,264 @@ +#include +#include +#include +#include +#include + +#include "switch_led_driver.h" +#include "switch_cpld_driver.h" + +#define DRVNAME "drv_led_driver" +#define SWITCH_LED_DRIVER_VERSION "0.0.1" + +unsigned int loglevel = 0; +static struct platform_device *drv_led_device; + +bool drv_get_location_led(unsigned int *loc) +{ + return true; +} + +bool drv_set_location_led(unsigned int loc) +{ + return true; +} + +int drv_get_port_led_num(void) +{ + return true; +} + +bool drv_get_port_led(unsigned long *bitmap) +{ + return true; +} + +bool drv_set_port_led(unsigned long *bitmap) +{ + return true; +} + +bool drv_get_sys_led(unsigned char *led) +{ + int ret = 0; + unsigned char reg_value = 0; + ret = switch_cpld_reg_read(SYS_CPLD, CPLD_SYS_LED_CTL_OFFSET, ®_value); + *led = (reg_value & 0x3F); + if(ret < 0) + return false; + return true; +} + +bool drv_set_sys_led(unsigned char led) +{ + int ret = 0; + unsigned char reg_value = 0; + reg_value = (0x40 | led); + ret = switch_cpld_reg_write(SYS_CPLD, CPLD_SYS_LED_CTL_OFFSET, reg_value); + if(ret < 0) + return false; + return true; +} + +bool drv_get_bmc_led(unsigned char *led) +{ + int ret = 0; + ret = switch_cpld_reg_read(SYS_CPLD, CPLD_BMC_LED_CTL_OFFSET, led); + if(ret < 0) + return false; + return true; +} + +bool drv_set_bmc_led(unsigned char led) +{ + int ret = 0; + ret = switch_cpld_reg_write(SYS_CPLD, CPLD_BMC_LED_CTL_OFFSET, led); + if(ret < 0) + return false; + return true; +} + +bool drv_get_fan_led(unsigned char *led) +{ + int ret = 0; + ret = switch_cpld_reg_read(SYS_CPLD, CPLD_FAN_LED_CTL_OFFSET, led); + if(ret < 0) + return false; + return true; +} + +bool drv_set_fan_led(unsigned char led) +{ + int ret = 0; + ret = switch_cpld_reg_write(SYS_CPLD, CPLD_FAN_LED_CTL_OFFSET, led); + if(ret < 0) + return false; + return true; +} + +bool drv_get_psu_led(unsigned char *led) +{ + int ret = 0; + ret = switch_cpld_reg_read(SYS_CPLD, CPLD_PSU_LED_CTL_OFFSET, led); + if(ret < 0) + return false; + return true; +} + +bool drv_set_psu_led(unsigned char led) +{ + int ret = 0; + ret = switch_cpld_reg_write(SYS_CPLD, CPLD_PSU_LED_CTL_OFFSET, led); + if(ret < 0) + return false; + return true; +} + +bool drv_get_id_led(unsigned int *led, unsigned int cs) +{ + return true; +} +bool drv_set_id_led(unsigned int led, unsigned int cs) +{ + return true; +} + +bool drv_set_hw_control(void) +{ + return true; +} + +void drv_get_loglevel(long *lev) +{ + *lev = (long)loglevel; + + return; +} + +void drv_set_loglevel(long lev) +{ + loglevel = (unsigned int)lev; + + return; +} + +ssize_t drv_debug_help(char *buf) +{ +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, + "sysname debug cmd\n" + "fan busybox devmem 0xfc800031 8\n" + "sys busybox devmem 0xfc800032 8\n" + "bmc busybox devmem 0xfc800033 8\n" + "psu busybox devmem 0xfc800034 8\n" + " \n"); +#else + return sprintf(buf, + "sysname debug cmd\n" + "fan busybox devmem 0xfc800031 8\n" + "sys busybox devmem 0xfc800032 8\n" + "bmc busybox devmem 0xfc800033 8\n" + "psu busybox devmem 0xfc800034 8\n" + " \n"); +#endif +} + +ssize_t drv_debug(const char *buf, int count) +{ + return 0; +} + +// For s3ip +static struct led_drivers_t pfunc = { + .get_sys_led = drv_get_sys_led, + .set_sys_led = drv_set_sys_led, + .get_bmc_led = drv_get_bmc_led, + .set_bmc_led = drv_set_bmc_led, + .get_fan_led = drv_get_fan_led, + .set_fan_led = drv_set_fan_led, + .get_psu_led = drv_get_psu_led, + .set_psu_led = drv_set_psu_led, + .get_id_led = drv_get_id_led, + .set_id_led = drv_set_id_led, + .set_hw_control = drv_set_hw_control, + .get_loglevel = drv_get_loglevel, + .set_loglevel = drv_set_loglevel, + .debug_help = drv_debug_help, +}; + +static int drv_led_probe(struct platform_device *pdev) +{ + s3ip_led_drivers_register(&pfunc); + + return 0; +} + +static int drv_led_remove(struct platform_device *pdev) +{ + s3ip_led_drivers_unregister(); + + return 0; +} + +static struct platform_driver drv_led_driver = { + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + }, + .probe = drv_led_probe, + .remove = drv_led_remove, +}; + +static int __init drv_led_init(void) +{ + int err=0; + int retval; + + drv_led_device = platform_device_alloc(DRVNAME, 0); + if(!drv_led_device) + { + LED_DEBUG("%s(#%d): platform_device_alloc fail\n", __func__, __LINE__); + return -ENOMEM; + } + + retval = platform_device_add(drv_led_device); + if(retval) + { + LED_DEBUG("%s(#%d): platform_device_add failed\n", __func__, __LINE__); + err = -retval; + goto dev_add_failed; + } + + retval = platform_driver_register(&drv_led_driver); + if(retval) + { + LED_DEBUG("%s(#%d): platform_driver_register failed(%d)\n", __func__, __LINE__, err); + err = -retval; + goto dev_reg_failed; + } + + return 0; + +dev_reg_failed: + platform_device_unregister(drv_led_device); + return err; + +dev_add_failed: + platform_device_put(drv_led_device); + return err; +} + +static void __exit drv_led_exit(void) +{ + platform_driver_unregister(&drv_led_driver); + platform_device_unregister(drv_led_device); + + return; +} + +MODULE_DESCRIPTION("S3IP LED Driver"); +MODULE_VERSION(SWITCH_LED_DRIVER_VERSION); +MODULE_LICENSE("GPL"); + +module_init(drv_led_init); +module_exit(drv_led_exit); \ No newline at end of file diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_led_driver.h b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_led_driver.h new file mode 100644 index 0000000000..c2dbb8a001 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_led_driver.h @@ -0,0 +1,97 @@ +#ifndef SWITCH_LED_DRIVER_H +#define SWITCH_LED_DRIVER_H + +#include "switch.h" + +#define LED_ERR(fmt, args...) LOG_ERR("sysled: ", fmt, ##args) +#define LED_WARNING(fmt, args...) LOG_WARNING("sysled: ", fmt, ##args) +#define LED_INFO(fmt, args...) LOG_INFO("sysled: ", fmt, ##args) +#define LED_DEBUG(fmt, args...) LOG_DBG("sysled: ", fmt, ##args) + +#define CPLD_FAN_LED_CTL_OFFSET 0x31 +#define CPLD_SYS_LED_CTL_OFFSET 0x32 +#define CPLD_BMC_LED_CTL_OFFSET 0x33 +#define CPLD_PSU_LED_CTL_OFFSET 0x34 + +#define SYS_LED_OFF 0x00 +#define SYS_LED_GREEN_ON 0x0C +#define SYS_LED_YELLOW_ON 0x0D +#define SYS_LED_RED_ON 0x01 +#define SYS_LED_BLUE_ON 0x10 +#define SYS_LED_GREEN_FLASH_COME_OK 0x08 +#define SYS_LED_GREEN_FLASH_BMC_OK 0x04 +#define SYS_LED_YELLOW_FLASH_COME_OK 0x0A +#define SYS_LED_YELLOW_FLASH_BMC_OK 0x06 +#define SYS_LED_RED_FLASH 0x02 +#define SYS_LED_BLUE_FLASH 0x20 + +#define BMC_LED_OFF 0x00 +#define BMC_LED_GREEN_ON 0x04 +#define BMC_LED_YELLOW_ON 0x05 +#define BMC_LED_RED_ON 0x01 +#define BMC_LED_BLUE_ON 0x10 +#define BMC_LED_GREEN_FLASH 0x08 +#define BMC_LED_YELLOW_FLASH 0x0A +#define BMC_LED_RED_FLASH 0x02 +#define BMC_LED_BLUE_FLASH 0x20 + + +#define FAN_LED_OFF 0x00 +#define FAN_LED_GREEN_ON 0x04 +#define FAN_LED_YELLOW_ON 0x05 +#define FAN_LED_RED_ON 0x01 +#define FAN_LED_BLUE_ON 0x10 + +#define PSU_LED_OFF 0x00 +#define PSU_LED_GREEN_ON 0x04 +#define PSU_LED_YELLOW_ON 0x05 +#define PSU_LED_RED_ON 0x01 +#define PSU_LED_BLUE_ON 0x10 + + +struct led_drivers_t{ + bool (*get_location_led) (unsigned char *loc); + bool (*set_location_led) (unsigned char loc); + int (*get_port_led_num) (void); + bool (*get_port_led) (unsigned long *bitmap); + bool (*set_port_led) (unsigned long *bitmap); + bool (*get_sys_led) (unsigned char *led); + bool (*set_sys_led) (unsigned char led); + bool (*get_bmc_led) (unsigned char *led); + bool (*set_bmc_led) (unsigned char led); + bool (*get_fan_led) (unsigned char *led); + bool (*set_fan_led) (unsigned char led); + bool (*get_psu_led) (unsigned char *led); + bool (*set_psu_led) (unsigned char led); + bool (*get_id_led) (unsigned int *led, unsigned int cs); + bool (*set_id_led) (unsigned int led, unsigned int cs); + bool (*set_hw_control) (void); + void (*get_loglevel) (long *lev); + void (*set_loglevel) (long lev); + ssize_t (*debug_help) (char *buf); +}; + +bool drv_get_location_led(unsigned int *loc); +bool drv_set_location_led(unsigned int loc); +int drv_get_port_led_num(void); +bool drv_get_port_led(unsigned long *bitmap); +bool drv_set_port_led(unsigned long *bitmap); +bool drv_get_sys_led(unsigned char *led); +bool drv_set_sys_led(unsigned char led); +bool drv_get_bmc_led(unsigned char *led); +bool drv_set_bmc_led(unsigned char led); +bool drv_get_fan_led(unsigned char *led); +bool drv_set_fan_led(unsigned char led); +bool drv_get_psu_led(unsigned char *led); +bool drv_set_psu_led(unsigned char led); +bool drv_get_id_led(unsigned int *led, unsigned int cs); +bool drv_set_id_led(unsigned int led, unsigned int cs); +bool drv_set_hw_control(void); +void drv_get_loglevel(long *lev); +void drv_set_loglevel(long lev); +ssize_t drv_debug_help(char *buf); + +void s3ip_led_drivers_register(struct led_drivers_t *p_func); +void s3ip_led_drivers_unregister(void); + +#endif /* SWITCH_LED_DRIVER_H */ diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_mb_driver.c b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_mb_driver.c new file mode 100644 index 0000000000..0da99cec84 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_mb_driver.c @@ -0,0 +1,724 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "switch_mb_driver.h" +#include "switch_at24.h" + +#define DRVNAME "drv_mb_driver" + +#define SWITCH_MB_DRIVER_VERSION "0.0.1" + +#define SYS_EEPROM_SIZE 256 +#define SYS_EEPROM_MAX_SIZE 4096 + +#define TLV_INFO_ID_STRING "TlvInfo" +#define TLV_INFO_ID_STRING_LENGTH 8 +#define TLV_INFO_VERSION 0x01 +#define TLV_TOTAL_LEN_MAX (SYS_EEPROM_SIZE - sizeof(tlvinfo_header_t)) +#define TLV_VALUE_MAX_LEN 255 +#define TLV_DECODE_VALUE_MAX_LEN ((5 * TLV_VALUE_MAX_LEN) + 1) + +#define ODM_BSP_VERSION "0.0.1" + +/** + * The TLV Types. + * + * Keep these in sync with tlv_code_list in cmd_sys_eeprom.c + */ +#define TLV_CODE_PRODUCT_NAME 0x21 +#define TLV_CODE_PART_NUMBER 0x22 +#define TLV_CODE_SERIAL_NUMBER 0x23 +#define TLV_CODE_MAC_BASE 0x24 +#define TLV_CODE_MANUF_DATE 0x25 +#define TLV_CODE_DEVICE_VERSION 0x26 +#define TLV_CODE_LABEL_REVISION 0x27 +#define TLV_CODE_PLATFORM_NAME 0x28 +#define TLV_CODE_ONIE_VERSION 0x29 +#define TLV_CODE_MAC_SIZE 0x2A +#define TLV_CODE_MANUF_NAME 0x2B +#define TLV_CODE_MANUF_COUNTRY 0x2C +#define TLV_CODE_VENDOR_NAME 0x2D +#define TLV_CODE_DIAG_VERSION 0x2E +#define TLV_CODE_SERVICE_TAG 0x2F +#define TLV_CODE_VENDOR_EXT 0xFD +#define TLV_CODE_CRC_32 0xFE + +unsigned int loglevel = 0; + +static struct platform_device *drv_mb_device; + +struct mutex update_lock; + +static uint8_t eeprom[SYS_EEPROM_SIZE]; + +uint32_t *global_crc32_table; + +/* Set to 1 if we've read EEPROM into memory */ +static int has_been_read = 0; +/* Set to 1 if the EEPROM contents were valid when read from hardware */ +static int hw_eeprom_valid = 1; + +/* + * Tlvinf header: Layout of the header for the TlvInfo format + * + * See the end of this file for details of this eeprom format + */ +struct tlvinfo_header_s { + char signature[TLV_INFO_ID_STRING_LENGTH]; /* 0x00 - 0x07 EEPROM Tag "TlvInfo" */ + uint8_t version; /* 0x08 Structure version */ + uint16_t totallen; /* 0x09 - 0x0A Length of all data which follows */ +} __packed; +typedef struct tlvinfo_header_s tlvinfo_header_t; + +/* + * TlvInfo TLV: Layout of a TLV field + */ +struct tlvinfo_tlv_s { + uint8_t type; + uint8_t length; + uint8_t value[0]; +} __packed; +typedef struct tlvinfo_tlv_s tlvinfo_tlv_t; + +/* + * read_sys_eeprom - read the hwinfo from TLV EEPROM + */ +int read_sys_eeprom(void *eeprom_data, loff_t offset, int len) +{ + int ret; + MAINBOARD_DEBUG("Read EEPROM...\n"); + + /* erase the buffer before reading */ +#ifdef C11_ANNEX_K + memset_s(eeprom_data, SYS_EEPROM_SIZE, 0x0, len); +#else + memset(eeprom_data, 0x0, len); +#endif + ret = at24_read_fan_eeprom(0, eeprom_data, offset, len); + if(ret < 0) + { + MAINBOARD_DEBUG("Read EEPROM failed: %d\n", ret); + return -1; + } + + return ret; +} + +uint32_t* crc32_filltable(uint32_t *crc_table, int endian) +{ + uint32_t polynomial = endian ? 0x04c11db7 : 0xedb88320; + uint32_t c; + unsigned i, j; + + if (!crc_table) + { + crc_table = kzalloc(256 * sizeof(uint32_t), GFP_KERNEL); + } + + if (!crc_table) + { + return NULL; + } + + for (i = 0; i < 256; i++) { + c = endian ? (i << 24) : i; + for (j = 8; j; j--) { + if (endian) + { + c = (c&0x80000000) ? ((c << 1) ^ polynomial) : (c << 1); + } + else + { + c = (c&1) ? ((c >> 1) ^ polynomial) : (c >> 1); + } + } + *crc_table++ = c; + } + + return crc_table - 256; +} + +uint32_t crc32_block_endian0(uint32_t val, const void *buf, unsigned len, uint32_t *crc_table) +{ + const void *end = (uint8_t*)buf + len; + + while (buf != end) { + val = crc_table[(uint8_t)val ^ *(uint8_t*)buf] ^ (val >> 8); + buf = (uint8_t*)buf + 1; + } + return val; +} + +unsigned long crc32 (unsigned long crc, const unsigned char *buf, unsigned len) +{ + if (!global_crc32_table) { + global_crc32_table = crc32_filltable(NULL, 0); + if(!global_crc32_table) + return -ENOMEM; + } + return crc32_block_endian0( crc ^ 0xffffffffL, buf, len, global_crc32_table) ^ 0xffffffffL; +} + +/* + * is_valid_tlv + * + * Perform basic sanity checks on a TLV field. The TLV is pointed to + * by the parameter provided. + * 1. The type code is not reserved (0x00 or 0xFF) + */ +static bool is_valid_tlv(tlvinfo_tlv_t *tlv) +{ + return((tlv->type != 0x00) && (tlv->type != 0xFF)); +} + +/* + * is_valid_tlvinfo_header + * + * Perform sanity checks on the first 11 bytes of the TlvInfo EEPROM + * data pointed to by the parameter: + * 1. First 8 bytes contain null-terminated ASCII string "TlvInfo" + * 2. Version byte is 1 + * 3. Total length bytes contain value which is less than or equal + * to the allowed maximum (2048-11) + * + */ +static bool is_valid_tlvinfo_header(tlvinfo_header_t *hdr) +{ + int max_size = TLV_TOTAL_LEN_MAX; + + return((strcmp(hdr->signature, TLV_INFO_ID_STRING) == 0) && + (hdr->version == TLV_INFO_VERSION) && + (be16_to_cpu(hdr->totallen) <= max_size) ); +} + +/* + * is_checksum_valid + * + * Validate the checksum in the provided TlvInfo EEPROM data. First, + * verify that the TlvInfo header is valid, then make sure the last + * TLV is a CRC-32 TLV. Then calculate the CRC over the EEPROM data + * and compare it to the value stored in the EEPROM CRC-32 TLV. + */ +static bool is_checksum_valid(uint8_t *eeprom) +{ + tlvinfo_header_t * eeprom_hdr = (tlvinfo_header_t *) eeprom; + tlvinfo_tlv_t * eeprom_crc; + unsigned int calc_crc; + unsigned int stored_crc; + + // Is the eeprom header valid? + if (!is_valid_tlvinfo_header(eeprom_hdr)) { + return(FALSE); + } + + // Is the last TLV a CRC? + eeprom_crc = (tlvinfo_tlv_t *) &eeprom[sizeof(tlvinfo_header_t) + + be16_to_cpu(eeprom_hdr->totallen) - + (sizeof(tlvinfo_tlv_t) + 4)]; + if ((eeprom_crc->type != TLV_CODE_CRC_32) || (eeprom_crc->length != 4)) { + return(FALSE); + } + + // Calculate the checksum + calc_crc = crc32(0, (void *)eeprom, sizeof(tlvinfo_header_t) + + be16_to_cpu(eeprom_hdr->totallen) - 4); + if(calc_crc < 0) + { + return FALSE; + } + stored_crc = ((eeprom_crc->value[0] << 24) | (eeprom_crc->value[1] << 16) | + (eeprom_crc->value[2] << 8) | eeprom_crc->value[3]); + return(calc_crc == stored_crc); +} + +/* + * decode_tlv_value + * + * Decode a single TLV value into a string. + + * The validity of EEPROM contents and the TLV field have been verified + * prior to calling this function. + */ +#define DECODE_NAME_MAX 20 + +int decode_tlv_value(tlvinfo_tlv_t * tlv, char* value) +{ + int i; + int ret = 0; + + switch (tlv->type) + { + case TLV_CODE_PRODUCT_NAME: + case TLV_CODE_PART_NUMBER: + case TLV_CODE_SERIAL_NUMBER: + case TLV_CODE_MANUF_DATE: + case TLV_CODE_LABEL_REVISION: + case TLV_CODE_PLATFORM_NAME: + case TLV_CODE_ONIE_VERSION: + case TLV_CODE_MANUF_NAME: + case TLV_CODE_MANUF_COUNTRY: + case TLV_CODE_VENDOR_NAME: + case TLV_CODE_DIAG_VERSION: + case TLV_CODE_SERVICE_TAG: +#ifdef C11_ANNEX_K + if(memcpy_s(value, TLV_VALUE_MAX_LEN, tlv->value, tlv->length) != 0) + { + ret = -1; + } +#else + memcpy(value, tlv->value, tlv->length); +#endif + value[tlv->length] = 0; + break; + case TLV_CODE_MAC_BASE: +#ifdef C11_ANNEX_K + ret = sprintf_s(value, TLV_VALUE_MAX_LEN, "%02X:%02X:%02X:%02X:%02X:%02X", + tlv->value[0], tlv->value[1], tlv->value[2], + tlv->value[3], tlv->value[4], tlv->value[5]); +#else + ret = sprintf(value, "%02X:%02X:%02X:%02X:%02X:%02X", + tlv->value[0], tlv->value[1], tlv->value[2], + tlv->value[3], tlv->value[4], tlv->value[5]); +#endif + break; + case TLV_CODE_DEVICE_VERSION: +#ifdef C11_ANNEX_K + ret = sprintf_s(value, TLV_VALUE_MAX_LEN, "%u", tlv->value[0]); +#else + ret = sprintf(value, "%u", tlv->value[0]); +#endif + break; + case TLV_CODE_MAC_SIZE: +#ifdef C11_ANNEX_K + ret = sprintf_s(value, TLV_VALUE_MAX_LEN, "%u", (tlv->value[0] << 8) | tlv->value[1]); +#else + ret = sprintf(value, "%u", (tlv->value[0] << 8) | tlv->value[1]); +#endif + break; + case TLV_CODE_VENDOR_EXT: + value[0] = 0; + for (i = 0; (i < (TLV_DECODE_VALUE_MAX_LEN/5)) && (i < tlv->length);i++) + { +#ifdef C11_ANNEX_K + ret = sprintf_s(value, TLV_VALUE_MAX_LEN, "%s 0x%02X", value, tlv->value[i]); +#else + ret = sprintf(value, "%s 0x%02X", value, tlv->value[i]); +#endif + } + break; + case TLV_CODE_CRC_32: +#ifdef C11_ANNEX_K + ret = sprintf_s(value, TLV_VALUE_MAX_LEN, "0x%02X%02X%02X%02X", + tlv->value[0], tlv->value[1], tlv->value[2], + tlv->value[3]); +#else + ret = sprintf(value, "0x%02X%02X%02X%02X", + tlv->value[0], tlv->value[1], tlv->value[2], + tlv->value[3]); +#endif + break; + default: + value[0] = 0; + for (i = 0; (i < (TLV_DECODE_VALUE_MAX_LEN/5)) && (i < tlv->length);i++) + { +#ifdef C11_ANNEX_K + ret = sprintf_s(value, TLV_VALUE_MAX_LEN, "%s 0x%02X", value, tlv->value[i]); +#else + ret = sprintf(value, "%s 0x%02X", value, tlv->value[i]); +#endif + } + break; + } + return ret; +} +EXPORT_SYMBOL_GPL(decode_tlv_value); + +/* + * tlvinfo_find_tlv + * + * This function finds the TLV with the supplied code in the EERPOM. + * An offset from the beginning of the EEPROM is returned in the + * eeprom_index parameter if the TLV is found. + */ +bool tlvinfo_find_tlv(uint8_t *eeprom, uint8_t tcode, + int *eeprom_index) +{ + tlvinfo_header_t * eeprom_hdr = (tlvinfo_header_t *) eeprom; + tlvinfo_tlv_t * eeprom_tlv; + int eeprom_end; + + // Search through the TLVs, looking for the first one which matches the + // supplied type code. + *eeprom_index = sizeof(tlvinfo_header_t); + eeprom_end = sizeof(tlvinfo_header_t) + be16_to_cpu(eeprom_hdr->totallen); + while (*eeprom_index < eeprom_end) + { + eeprom_tlv = (tlvinfo_tlv_t *) &eeprom[*eeprom_index]; + if (!is_valid_tlv(eeprom_tlv)) + { + return(FALSE); + } + if (eeprom_tlv->type == tcode) + { + return(TRUE); + } + *eeprom_index += sizeof(tlvinfo_tlv_t) + eeprom_tlv->length; + } + return(FALSE); +} + +/* + * tlvinfo_decode_tlv + * + * This function finds the TLV with the supplied code in the EERPOM + * and decodes the value into the buffer provided. + */ +bool tlvinfo_decode_tlv(uint8_t *eeprom, uint8_t tcode, char* value) +{ + int eeprom_index; + tlvinfo_tlv_t * eeprom_tlv; + + // Find the TLV and then decode it + if (tlvinfo_find_tlv(eeprom, tcode, &eeprom_index)) { + eeprom_tlv = (tlvinfo_tlv_t *) &eeprom[eeprom_index]; + if (decode_tlv_value(eeprom_tlv, value) < 0) + { + return FALSE; + } + return TRUE; + } + + return FALSE; +} + +/* + * update_crc + * + * This function updates the CRC-32 TLV. If there is no CRC-32 TLV, then + * one is added. This function should be called after each update to the + * EEPROM structure, to make sure the CRC is always correct. + */ +static int update_crc(uint8_t *eeprom) +{ + tlvinfo_header_t * eeprom_hdr = (tlvinfo_header_t *) eeprom; + tlvinfo_tlv_t * eeprom_crc; + int calc_crc; + int eeprom_index; + + // Discover the CRC TLV + if (!tlvinfo_find_tlv(eeprom, TLV_CODE_CRC_32, &eeprom_index)) + { + if ((be16_to_cpu(eeprom_hdr->totallen) + sizeof(tlvinfo_tlv_t) + 4) > TLV_TOTAL_LEN_MAX) + { + return -1; + } + eeprom_index = sizeof(tlvinfo_header_t) + be16_to_cpu(eeprom_hdr->totallen); + eeprom_hdr->totallen = cpu_to_be16(be16_to_cpu(eeprom_hdr->totallen) + sizeof(tlvinfo_tlv_t) + 4); + } + eeprom_crc = (tlvinfo_tlv_t *) &eeprom[eeprom_index]; + eeprom_crc->type = TLV_CODE_CRC_32; + eeprom_crc->length = 4; + + // Calculate the checksum + calc_crc = crc32(0, (void *)eeprom, sizeof(tlvinfo_header_t) + be16_to_cpu(eeprom_hdr->totallen) - 4); + if(calc_crc < 0) + { + return calc_crc; + } + + eeprom_crc->value[0] = (calc_crc >> 24) & 0xFF; + eeprom_crc->value[1] = (calc_crc >> 16) & 0xFF; + eeprom_crc->value[2] = (calc_crc >> 8) & 0xFF; + eeprom_crc->value[3] = (calc_crc >> 0) & 0xFF; + return 0; +} + +/* + * read_eeprom + * + * Read the EEPROM into memory, if it hasn't already been read. + */ +int read_eeprom(uint8_t *eeprom) +{ + int ret; + tlvinfo_header_t *eeprom_hdr = (tlvinfo_header_t *) eeprom; + tlvinfo_tlv_t *eeprom_tlv = (tlvinfo_tlv_t *)&eeprom[sizeof(tlvinfo_header_t)]; + + if (has_been_read) + { + return 0; + } +#ifdef C11_ANNEX_K + memset_s(eeprom, SYS_EEPROM_SIZE, 0xff, SYS_EEPROM_SIZE); +#else + memset(eeprom, 0xff, SYS_EEPROM_SIZE); +#endif + /* Read the header */ + ret = read_sys_eeprom((void *)eeprom_hdr, 0, sizeof(tlvinfo_header_t)); + /* If the header was successfully read, read the TLVs */ + if ((ret == 0) && is_valid_tlvinfo_header(eeprom_hdr)) + { + ret = read_sys_eeprom((void *)eeprom_tlv, sizeof(tlvinfo_header_t), be16_to_cpu(eeprom_hdr->totallen)); + } + + if ( !is_valid_tlvinfo_header(eeprom_hdr) || !is_checksum_valid(eeprom) ) + { +#ifdef C11_ANNEX_K + strcpy_s(eeprom_hdr->signature, TLV_INFO_ID_STRING_LENGTH, TLV_INFO_ID_STRING); +#else + strcpy(eeprom_hdr->signature, TLV_INFO_ID_STRING); +#endif + eeprom_hdr->version = TLV_INFO_VERSION; + eeprom_hdr->totallen = cpu_to_be16(0); + ret = update_crc(eeprom); + if(ret < 0) + { + return ret; + } + /* Note that the contents of the hardware is not valid */ + hw_eeprom_valid = 0; + } + + has_been_read = 1; + + return ret; +} + +static ssize_t mb_read(uint8_t tcode, char *buf) +{ + char tlv_value[TLV_DECODE_VALUE_MAX_LEN]; + int len = 0; + + mutex_lock(&update_lock); + + if (read_eeprom(eeprom)) + { + mutex_unlock(&update_lock); + return -1; + } + + if (!(tlvinfo_decode_tlv(eeprom, tcode, tlv_value))) + { + MAINBOARD_DEBUG("ERROR: TLV code not present in EEPROM: 0x%02x\n", tcode); + mutex_unlock(&update_lock); + return -1; + } +#ifdef C11_ANNEX_K + len = sprintf_s(buf, TLV_DECODE_VALUE_MAX_LEN, "%s\n", tlv_value); +#else + len = sprintf(buf, "%s\n", tlv_value); +#endif + + mutex_unlock(&update_lock); + + return len; +} + +ssize_t drv_get_odm_bsp_version(char *buf) +{ +#ifdef C11_ANNEX_K + return sprintf_s(buf, TLV_VALUE_MAX_LEN, "%s\n", ODM_BSP_VERSION); +#else + return sprintf(buf, "%s\n", ODM_BSP_VERSION); +#endif +} + +ssize_t drv_get_mgmt_mac(char *buf) +{ + return mb_read(TLV_CODE_MAC_BASE, buf); +} + +ssize_t drv_get_date(char *buf) +{ + return mb_read(TLV_CODE_MANUF_DATE, buf); +} + +ssize_t drv_get_hw_version(char *buf) +{ + return mb_read(TLV_CODE_DEVICE_VERSION, buf); +} + +ssize_t drv_get_name(char *buf) +{ + + return mb_read(TLV_CODE_PLATFORM_NAME, buf); +} + +ssize_t drv_get_sn(char *buf) +{ + return mb_read(TLV_CODE_SERIAL_NUMBER, buf); +} + +ssize_t drv_get_vendor(char *buf) +{ + return mb_read(TLV_CODE_VENDOR_NAME, buf); +} + + +ssize_t drv_get_chassis_sn(char *buf) +{ + return 0; +} + +ssize_t drv_get_syseeprom(char *buf) +{ + if (read_eeprom(eeprom)) + { + return -1 ; + } +#ifdef C11_ANNEX_K + if(memcpy_s(buf, SYS_EEPROM_SIZE, eeprom, SYS_EEPROM_SIZE) != 0) + { + return -EINVAL; + } +#else + memcpy(buf, eeprom, SYS_EEPROM_SIZE); +#endif + + return SYS_EEPROM_SIZE; +} + +void drv_get_loglevel(long *lev) +{ + *lev = (long)loglevel; + + return; +} + +void drv_set_loglevel(long lev) +{ + loglevel = (unsigned int)lev; + + return; +} + +ssize_t drv_debug_help(char *buf) +{ +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, + "sysname debug cmd\n" + "eeprom hexdump /sys/bus/i2c/devices/0-0057/eeprom\n" + "mgmt_mac decode-syseeprom\n" + "date decode-syseeprom\n" + "hw_version decode-syseeprom\n" + "name decode-syseeprom\n" + "sn decode-syseeprom\n" + "vendor decode-syseeprom\n"); +#else + return sprintf(buf, + "sysname debug cmd\n" + "eeprom hexdump /sys/bus/i2c/devices/0-0057/eeprom\n" + "mgmt_mac decode-syseeprom\n" + "date decode-syseeprom\n" + "hw_version decode-syseeprom\n" + "name decode-syseeprom\n" + "sn decode-syseeprom\n" + "vendor decode-syseeprom\n"); +#endif +} + +ssize_t drv_debug(const char *buf, int count) +{ + return 0; +} + +// For s3ip +static struct mainboard_drivers_t pfunc = { + .get_mgmt_mac = drv_get_mgmt_mac, + .get_date = drv_get_date, + .get_hw_version = drv_get_hw_version, + .get_name = drv_get_name, + .get_sn = drv_get_sn, + .get_vendor = drv_get_vendor, + .get_odm_bsp_version = drv_get_odm_bsp_version, + .get_syseeprom = drv_get_syseeprom, + .get_loglevel = drv_get_loglevel, + .set_loglevel = drv_set_loglevel, + .debug_help = drv_debug_help, +}; + +static int drv_mb_probe(struct platform_device *pdev) +{ + s3ip_mainboard_drivers_register(&pfunc); + + return 0; +} + +static int drv_mb_remove(struct platform_device *pdev) +{ + s3ip_mainboard_drivers_unregister(); + + return 0; +} + +static struct platform_driver drv_mb_driver = { + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + }, + .probe = drv_mb_probe, + .remove = drv_mb_remove, +}; + +static int __init drv_mb_init(void) +{ + int err=0; + int retval=0; + + drv_mb_device = platform_device_alloc(DRVNAME, 0); + if(!drv_mb_device) + { + MAINBOARD_DEBUG("%s(#%d): platform_device_alloc fail\n", __func__, __LINE__); + return -ENOMEM; + } + + retval = platform_device_add(drv_mb_device); + if(retval) + { + MAINBOARD_DEBUG("%s(#%d): platform_device_add failed\n", __func__, __LINE__); + err = -retval; + goto dev_add_failed; + } + + retval = platform_driver_register(&drv_mb_driver); + if(retval) + { + MAINBOARD_DEBUG("%s(#%d): platform_driver_register failed(%d)\n", __func__, __LINE__, err); + err = -retval; + goto dev_reg_failed; + } + + mutex_init(&update_lock); + + return 0; + +dev_reg_failed: + platform_device_unregister(drv_mb_device); + return err; + +dev_add_failed: + platform_device_put(drv_mb_device); + return err; +} + +static void __exit drv_mb_exit(void) +{ + platform_driver_unregister(&drv_mb_driver); + platform_device_unregister(drv_mb_device); + mutex_destroy(&update_lock); + + return; +} + +MODULE_DESCRIPTION("S3IP Main Board Driver"); +MODULE_VERSION(SWITCH_MB_DRIVER_VERSION); +MODULE_LICENSE("GPL"); + +module_init(drv_mb_init); +module_exit(drv_mb_exit); diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_mb_driver.h b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_mb_driver.h new file mode 100644 index 0000000000..7484873c59 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_mb_driver.h @@ -0,0 +1,52 @@ +#ifndef SWITCH_MB_DRIVER_H +#define SWITCH_MB_DRIVER_H + +#include "switch.h" + +#define MAINBOARD_ERR(fmt, args...) LOG_ERR("mainboard: ", fmt, ##args) +#define MAINBOARD_WARNING(fmt, args...) LOG_WARNING("mainboard: ", fmt, ##args) +#define MAINBOARD_INFO(fmt, args...) LOG_INFO("mainboard: ", fmt, ##args) +#define MAINBOARD_DEBUG(fmt, args...) LOG_DBG("mainboard: ", fmt, ##args) + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE (!FALSE) +#endif + +struct mainboard_drivers_t{ + ssize_t (*get_odm_bsp_version) (char* buf); + ssize_t (*get_mgmt_mac) (char* buf); + ssize_t (*get_date) (char* buf); + ssize_t (*get_hw_version) (char* buf); + ssize_t (*get_name) (char* buf); + ssize_t (*get_sn) (char* buf); + ssize_t (*get_chassis_sn) (char* buf); + ssize_t (*get_vendor) (char* buf); + ssize_t (*get_syseeprom) (char* buf); + void (*get_loglevel) (long *lev); + void (*set_loglevel) (long lev); + ssize_t (*debug_help) (char *buf); + ssize_t (*debug) (const char *buf, int count); +}; + +ssize_t drv_get_odm_bsp_version(char *buf); +ssize_t drv_get_mgmt_mac(char *buf); +ssize_t drv_get_date(char *buf); +ssize_t drv_get_hw_version(char *buf); +ssize_t drv_get_name(char *buf); +ssize_t drv_get_sn(char *buf); +ssize_t drv_get_chassis_sn(char *buf); +ssize_t drv_get_vendor(char *buf); +ssize_t drv_get_syseeprom(char *buf); +void drv_get_loglevel(long *lev); +void drv_set_loglevel(long lev); +ssize_t drv_debug_help(char *buf); +ssize_t drv_debug(const char *buf, int count); + +void s3ip_mainboard_drivers_register(struct mainboard_drivers_t *p_func); +void s3ip_mainboard_drivers_unregister(void); + +#endif /* SWITCH_MB_DRIVER_H */ diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_psu_driver.c b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_psu_driver.c new file mode 100644 index 0000000000..f0aed66e38 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_psu_driver.c @@ -0,0 +1,658 @@ +#include +#include +#include +#include +#include + +#include "switch_cpld_driver.h" +#include "switch_psu_driver.h" +#include "sysfs_ipmi.h" + +#define DRVNAME "drv_psu_driver" +#define SWITCH_PSU_DRIVER_VERSION "0.0.1" + +unsigned int loglevel = 0; +static struct platform_device *drv_psu_device; +unsigned int scan_period = 5; + +//TBD: the following information need to be confirmed with HW +static int psu_num_temp_sensors[PSU_TOTAL_NUM] = { + PSU_TOTAL_TEMP_NUM, + PSU_TOTAL_TEMP_NUM +}; + +static char *psu_temp_alias[PSU_TOTAL_NUM][PSU_TOTAL_TEMP_NUM] = { + {"PSU1_8D_TEMP1", "PSU1_8E_TEMP2"}, + {"PSU2_8D_TEMP1", "PSU2_8E_TEMP2"} +}; + +static char *psu_temp_type[PSU_TOTAL_NUM][PSU_TOTAL_TEMP_NUM] = { + {"psu1_temp1", "psu1_temp2"}, + {"psu2_temp1", "psu2_temp2"} +}; + +static int psu_temp_max[PSU_TOTAL_NUM][PSU_TOTAL_TEMP_NUM] = { + {65000, 65000}, + {65000, 65000} +}; + +static int psu_temp_max_hyst[PSU_TOTAL_NUM][PSU_TOTAL_TEMP_NUM] = { + {0, 0}, + {0, 0} +}; + +static int psu_temp_min[PSU_TOTAL_NUM][PSU_TOTAL_TEMP_NUM] = { + {-20000, -20000}, + {-20000, -20000} +}; + +static int psu_alarm_threshold_curr[PSU_TOTAL_NUM] = { + 15000, + 15000 +}; + +static int psu_alarm_threshold_vol[PSU_TOTAL_NUM] = { + 264000, + 264000 +}; +static int psu_max_output_power[PSU_TOTAL_NUM] = { + 1500000000, + 1500000000 +}; + +static unsigned int psu_present_reg_mask_table[PSU_TOTAL_NUM] = { + PSU_ONLINE_PSU1_MASK, + PSU_ONLINE_PSU2_MASK, +}; + +static unsigned int psu_pok_reg_mask_table[PSU_TOTAL_NUM] = { + PSU_STATUS_PSU1_ALARM, + PSU_STATUS_PSU2_ALARM, +}; + +static unsigned int psu_in_pok_reg_mask_table[PSU_TOTAL_NUM] = { + PSU_STATUS_PSU1_IPOK, + PSU_STATUS_PSU2_IPOK, +}; + +static unsigned int psu_out_pok_reg_mask_table[PSU_TOTAL_NUM] = { + PSU_STATUS_PSU1_OPOK, + PSU_STATUS_PSU2_OPOK, +}; + +int drv_get_mfr_info(unsigned int psu_index, u8 pmbus_command, char *buf) +{ + int retval = 0; +/* + retval = psu2312_mfr_read(psu_index, pmbus_command, buf); + if(retval < 0) + { + PSU_DEBUG("Get psu%d mfr_info failed.\n", psu_index); + return retval; + } +*/ + return retval; +} + +ssize_t drv_get_psu_temp_input(unsigned int psu_index, unsigned int psu_temp_index, long *temp_input) +{ + int retval = 0; +/* + retval = psu2312_mfr_read(psu_index, pmbus_command, buf); + if(retval < 0) + { + PSU_DEBUG("Get psu%d mfr_info failed.\n", psu_index); + return retval; + } +*/ + return retval; +} + + +ssize_t drv_get_psu_temp_input_from_bmc(unsigned int psu_index, unsigned int psu_temp_index, long *temp_input) +{ + int retval=0; + + if(psu_index == PSU1_INDEX) + { + retval = drv_get_sensor_temp_input_from_bmc(psu_temp_index + TMP75_4B_TEMP, temp_input);//PSU1_TEMP1 = 10;PSU1_TEMP2 = 11 + if(retval < 0) + { + PSU_DEBUG("Get psu%d temp%d input failed.\n", psu_index, psu_temp_index); + return retval; + } + } + + if(psu_index == PSU2_INDEX) + { + retval = drv_get_sensor_temp_input_from_bmc(psu_temp_index + TMP431_4C_TEMP2, temp_input);//PSU2_TEMP1 = 12;PSU2_TEMP2 = 13 + if(retval < 0) + { + PSU_DEBUG("Get psu%d temp%d input failed.\n", psu_index, psu_temp_index); + return retval; + } + } + return retval; +} + +ssize_t drv_get_num_temp_sensors(unsigned int psu_index, char *buf) +{ + // psu index start from 1 + if((1 <= psu_index && psu_index <= PSU_TOTAL_NUM)) + { +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", psu_num_temp_sensors[psu_index-1]); +#else + return sprintf(buf, "%d\n", psu_num_temp_sensors[psu_index-1]); +#endif + } + else + { + return -EINVAL; + } +} + +ssize_t drv_get_psu_temp_alias(unsigned int psu_index, unsigned int psu_temp_index, char *buf) +{ + // psu index start from 1, but psu temp index start from 0 + if((1 <= psu_index && psu_index <= PSU_TOTAL_NUM) && (0 <= psu_temp_index && psu_temp_index <= PSU_TOTAL_TEMP_NUM)) + { +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%s\n", psu_temp_alias[psu_index-1][psu_temp_index-1]); +#else + return sprintf(buf, "%s\n", psu_temp_alias[psu_index-1][psu_temp_index-1]); +#endif + } + else + { + return -EINVAL; + } +} + +ssize_t drv_get_psu_temp_type(unsigned int psu_index, unsigned int psu_temp_index, char *buf) +{ + // psu index start from 1, but psu temp index start from 0 + if((1 <= psu_index && psu_index <= PSU_TOTAL_NUM) && (0 <= psu_temp_index && psu_temp_index <= PSU_TOTAL_TEMP_NUM)) + { +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%s\n", psu_temp_type[psu_index-1][psu_temp_index-1]); +#else + return sprintf(buf, "%s\n", psu_temp_type[psu_index-1][psu_temp_index-1]); +#endif + } + else + { + return -EINVAL; + } +} + +ssize_t drv_get_psu_temp_max(unsigned int psu_index, unsigned int psu_temp_index, char *buf) +{ + // psu index start from 1, but psu temp index start from 0 + if((1 <= psu_index && psu_index <= PSU_TOTAL_NUM) && (0 <= psu_temp_index && psu_temp_index <= PSU_TOTAL_TEMP_NUM)) + { +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", psu_temp_max[psu_index-1][psu_temp_index-1]); +#else + return sprintf(buf, "%d\n", psu_temp_max[psu_index-1][psu_temp_index-1]); +#endif + } + else + { + return -EINVAL; + } +} + +ssize_t drv_set_psu_temp_max(unsigned int psu_index, unsigned int psu_temp_index, int val) +{ + // psu index start from 1, but psu temp index start from 0 + if((1 <= psu_index && psu_index <= PSU_TOTAL_NUM) && (0 <= psu_temp_index && psu_temp_index <= PSU_TOTAL_TEMP_NUM)) + { + psu_temp_max[psu_index-1][psu_temp_index-1] = val; + } + else + { + return -EINVAL; + } + + return 0; +} + +ssize_t drv_get_psu_temp_max_hyst(unsigned int psu_index, unsigned int psu_temp_index, char *buf) +{ + // psu index start from 1, but psu temp index start from 0 + if((1 <= psu_index && psu_index <= PSU_TOTAL_NUM) && (0 <= psu_temp_index && psu_temp_index <= PSU_TOTAL_TEMP_NUM)) + { +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", psu_temp_max_hyst[psu_index-1][psu_temp_index-1]); +#else + return sprintf(buf, "%d\n", psu_temp_max_hyst[psu_index-1][psu_temp_index-1]); +#endif + } + else + { + return -EINVAL; + } +} + +ssize_t drv_get_psu_temp_min(unsigned int psu_index, unsigned int psu_temp_index, char *buf) +{ + // psu index start from 1, but psu temp index start from 0 + if((1 <= psu_index && psu_index <= PSU_TOTAL_NUM) && (0 <= psu_temp_index && psu_temp_index <= PSU_TOTAL_TEMP_NUM)) + { +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", psu_temp_min[psu_index-1][psu_temp_index-1]); +#else + return sprintf(buf, "%d\n", psu_temp_min[psu_index-1][psu_temp_index-1]); +#endif + } + else + { + return -EINVAL; + } +} + +ssize_t drv_set_psu_temp_min(unsigned int psu_index, unsigned int psu_temp_index, int val) +{ + // psu index start from 1, but psu temp index start from 0 + if((1 <= psu_index && psu_index <= PSU_TOTAL_NUM) && (0 <= psu_temp_index && psu_temp_index <= PSU_TOTAL_TEMP_NUM)) + { + psu_temp_min[psu_index-1][psu_temp_index-1] = val; + } + else + { + return -EINVAL; + } + + return 0; +} + +ssize_t drv_get_psu_present(unsigned int psu_index, unsigned int *present) +{ + int ret; + unsigned char buf; + + if((psu_index >= 0) && (psu_index < PSU_TOTAL_NUM)) + { + ret = switch_cpld_reg_read(SYS_CPLD, PSU_ONLINE_OFFSET, &buf); + if(ret < 0) + return ret; + + /* present bit in reg: 0: not present, 1: present */ + *present = ((buf & psu_present_reg_mask_table[psu_index]) == psu_present_reg_mask_table[psu_index]) ? 0 : 1; + } + else + { + PSU_DEBUG("Invalid psu index.\n"); + return -EINVAL; + } + + return 0; +} + +ssize_t drv_get_psu_pok_alarm(unsigned int psu_index, unsigned int *alarm) +{ + int ret; + unsigned char buf; + + if((psu_index >= 0) && (psu_index < PSU_TOTAL_NUM)) + { + ret = switch_cpld_reg_read(SYS_CPLD, PSU_ONLINE_OFFSET , &buf); + if(ret < 0) + return ret; + + /* pok bit in reg: 0: ok, 1: not ok */ + *alarm = ((buf & psu_pok_reg_mask_table[psu_index]) == psu_pok_reg_mask_table[psu_index]) ? 0 : 1; + } + else + { + PSU_DEBUG("Invalid psu index.\n"); + return -EINVAL; + } + + return 0; +} + +ssize_t drv_get_psu_in_pok(unsigned int psu_index, unsigned int *status) +{ + int ret; + unsigned char buf; + + if((psu_index >= 0) && (psu_index < PSU_TOTAL_NUM)) + { + ret = switch_cpld_reg_read(SYS_CPLD, PSU_ONLINE_OFFSET, &buf); + if(ret < 0) + return ret; + + /* pok bit in reg: 0: not ok, 1: ok */ + *status = ((buf & psu_in_pok_reg_mask_table[psu_index]) == psu_in_pok_reg_mask_table[psu_index]) ? 1 : 0; + } + else + { + PSU_DEBUG("Invalid psu index.\n"); + return -EINVAL; + } + + return 0; +} + +ssize_t drv_get_psu_out_pok(unsigned int psu_index, unsigned int *status) +{ + int ret; + unsigned char buf; + + if((psu_index >= 0) && (psu_index < PSU_TOTAL_NUM)) + { + ret = switch_cpld_reg_read(SYS_CPLD, PSU_ONLINE_OFFSET, &buf); + if(ret < 0) + return ret; + + /* pok bit in reg: 0: not ok, 1: ok */ + *status = ((buf & psu_out_pok_reg_mask_table[psu_index]) == psu_out_pok_reg_mask_table[psu_index]) ? 1 : 0; + } + else + { + PSU_DEBUG("Invalid psu index.\n"); + return -EINVAL; + } + + return 0; +} + +bool drv_get_psu_alarm_threshold_curr(unsigned int psu_index, long *val) +{ + *val = psu_alarm_threshold_curr[psu_index-1]; + return true; +} + +bool drv_get_psu_alarm_threshold_vol(unsigned int psu_index, long *val) +{ + *val = psu_alarm_threshold_vol[psu_index-1]; + return true; +} + +bool drv_get_psu_max_output_power(unsigned int psu_index, long *val) +{ + *val = psu_max_output_power[psu_index-1]; + return true; +} + +void drv_get_loglevel(long *lev) +{ + *lev = (long)loglevel; + + return; +} + +void drv_set_loglevel(long lev) +{ + loglevel = (unsigned int)lev; + + return; +} + +ssize_t drv_debug_help(char *buf) +{ +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, + "For psu1, use i2cget/i2cset -f -y 110 0x59 to debug.\n" + "For psu2, use i2cget/i2cset -f -y 111 0x5a to debug.\n" + "sysname debug cmd\n" + "psu1 model_name i2cget -f -y 110 0x59 0x9a i 14\n" + "psu2 model_name i2cget -f -y 111 0x5a 0x9a i 14\n" + "psu1 serial_number i2cget -f -y 110 0x59 0x9e i 21\n" + "psu2 serial_number i2cget -f -y 111 0x5a 0x9e i 21\n" + "psu1 date i2cget -f -y 110 0x59 0x9d i 11\n" + "psu2 date i2cget -f -y 111 0x5a 0x9d i 11\n" + "psu1 vendor i2cget -f -y 110 0x59 0x99 i 7\n" + "psu2 vendor i2cget -f -y 111 0x5a 0x99 i 7\n" + "psu1 hardware_version i2cget -f -y 110 0x59 0x9b i 2\n" + "psu2 hardware_version i2cget -f -y 111 0x5a 0x9b i 2\n" + "psu1 alarm i2cget -f -y 110 0x59 0x79 w\n" + "psu2 alarm i2cget -f -y 111 0x5a 0x79 w\n" + "psu1 max_output_power i2cget -f -y 110 0x59 0x31 w\n" + "psu2 max_output_power i2cget -f -y 111 0x5a 0x31 w\n" + "psu1 in_curr i2cget -f -y 110 0x59 0x89 w\n" + "psu2 in_curr i2cget -f -y 111 0x5a 0x89 w\n" + "psu1 in_vol i2cget -f -y 110 0x59 0x88 w\n" + "psu2 in_vol i2cget -f -y 111 0x5a 0x88 w\n" + "psu1 in_power i2cget -f -y 110 0x59 0x97 w\n" + "psu2 in_power i2cget -f -y 111 0x5a 0x97 w\n" + "psu1 out_curr i2cget -f -y 110 0x59 0x8c w\n" + "psu2 out_curr i2cget -f -y 111 0x5a 0x8c w\n" + "psu1 out_vol i2cget -f -y 110 0x59 0x8b w\n" + "psu2 out_vol i2cget -f -y 111 0x5a 0x8b w\n" + "psu1 out_power i2cget -f -y 110 0x59 0x96 w\n" + "psu2 out_power i2cget -f -y 111 0x5a 0x96 w\n" + "psu1 status i2cget -f -y 110 0x59 0x7c\n" + "psu2 status i2cget -f -y 111 0x5a 0x7c\n" + "psu1 fan_speed i2cget -f -y 110 0x59 0x90 w\n" + "psu2 fan_speed i2cget -f -y 111 0x5a 0x90 w\n" + "psu1 temp1/temp_input i2cget -f -y 110 0x59 0x8d w\n" + "psu2 temp1/temp_input i2cget -f -y 111 0x5a 0x8d w\n"); +#else + return sprintf(buf, + "For psu1, use i2cget/i2cset -f -y 110 0x59 to debug.\n" + "For psu2, use i2cget/i2cset -f -y 111 0x5a to debug.\n" + "sysname debug cmd\n" + "psu1 model_name i2cget -f -y 110 0x59 0x9a i 14\n" + "psu2 model_name i2cget -f -y 111 0x5a 0x9a i 14\n" + "psu1 serial_number i2cget -f -y 110 0x59 0x9e i 21\n" + "psu2 serial_number i2cget -f -y 111 0x5a 0x9e i 21\n" + "psu1 date i2cget -f -y 110 0x59 0x9d i 11\n" + "psu2 date i2cget -f -y 111 0x5a 0x9d i 11\n" + "psu1 vendor i2cget -f -y 110 0x59 0x99 i 7\n" + "psu2 vendor i2cget -f -y 111 0x5a 0x99 i 7\n" + "psu1 hardware_version i2cget -f -y 110 0x59 0x9b i 2\n" + "psu2 hardware_version i2cget -f -y 111 0x5a 0x9b i 2\n" + "psu1 alarm i2cget -f -y 110 0x59 0x79 w\n" + "psu2 alarm i2cget -f -y 111 0x5a 0x79 w\n" + "psu1 max_output_power i2cget -f -y 110 0x59 0x31 w\n" + "psu2 max_output_power i2cget -f -y 111 0x5a 0x31 w\n" + "psu1 in_curr i2cget -f -y 110 0x59 0x89 w\n" + "psu2 in_curr i2cget -f -y 111 0x5a 0x89 w\n" + "psu1 in_vol i2cget -f -y 110 0x59 0x88 w\n" + "psu2 in_vol i2cget -f -y 111 0x5a 0x88 w\n" + "psu1 in_power i2cget -f -y 110 0x59 0x97 w\n" + "psu2 in_power i2cget -f -y 111 0x5a 0x97 w\n" + "psu1 out_curr i2cget -f -y 110 0x59 0x8c w\n" + "psu2 out_curr i2cget -f -y 111 0x5a 0x8c w\n" + "psu1 out_vol i2cget -f -y 110 0x59 0x8b w\n" + "psu2 out_vol i2cget -f -y 111 0x5a 0x8b w\n" + "psu1 out_power i2cget -f -y 110 0x59 0x96 w\n" + "psu2 out_power i2cget -f -y 111 0x5a 0x96 w\n" + "psu1 status i2cget -f -y 110 0x59 0x7c\n" + "psu2 status i2cget -f -y 111 0x5a 0x7c\n" + "psu1 fan_speed i2cget -f -y 110 0x59 0x90 w\n" + "psu2 fan_speed i2cget -f -y 111 0x5a 0x90 w\n" + "psu1 temp1/temp_input i2cget -f -y 110 0x59 0x8d w\n" + "psu2 temp1/temp_input i2cget -f -y 111 0x5a 0x8d w\n"); +#endif + +} + +ssize_t drv_debug_help_hisonic(char *buf) +{ +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, + "For psu1, use i2cget/i2cset -f -y 110 0x59 to debug.\n" + "For psu2, use i2cget/i2cset -f -y 111 0x5a to debug.\n" + "sysname debug cmd\n" + "1 status i2cget -f -y 110 0x59 0x7c\n" + "1 currents i2cget -f -y 110 0x59 0x8c w\n" + "1 power i2cget -f -y 110 0x59 0x96 w\n" + "1 voltage i2cget -f -y 110 0x59 0x8b w\n" + "2 status i2cget -f -y 111 0x5a 0x7c\n" + "2 currents i2cget -f -y 111 0x5a 0x8c w\n" + "2 power i2cget -f -y 111 0x5a 0x96 w\n" + "2 voltage i2cget -f -y 110 0x5a 0x8b w\n"); +#else + return sprintf(buf, + "For psu1, use i2cget/i2cset -f -y 110 0x59 to debug.\n" + "For psu2, use i2cget/i2cset -f -y 111 0x5a to debug.\n" + "sysname debug cmd\n" + "1 status i2cget -f -y 110 0x59 0x7c\n" + "1 currents i2cget -f -y 110 0x59 0x8c w\n" + "1 power i2cget -f -y 110 0x59 0x96 w\n" + "1 voltage i2cget -f -y 110 0x59 0x8b w\n" + "2 status i2cget -f -y 111 0x5a 0x7c\n" + "2 currents i2cget -f -y 111 0x5a 0x8c w\n" + "2 power i2cget -f -y 111 0x5a 0x96 w\n" + "2 voltage i2cget -f -y 110 0x5a 0x8b w\n"); +#endif + +} + +ssize_t drv_debug(const char *buf, int count) +{ + return 0; +} + +ssize_t drv_get_scan_period(char *buf) +{ +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "present soft scan time is %d x 100ms.\n", scan_period); +#else + return sprintf(buf, "present soft scan time is %d x 100ms.\n", scan_period); +#endif +} + +void drv_set_scan_period(unsigned int period) +{ + scan_period = period; + + return; +} + +// For s3ip +static struct psu_drivers_t pfunc = { + .get_scan_period = drv_get_scan_period, + .set_scan_period = drv_set_scan_period, + .get_mfr_info = drv_get_mfr_info, + .get_temp_input = drv_get_psu_temp_input, + .get_num_temp_sensors = drv_get_num_temp_sensors, + .get_psu_temp_alias = drv_get_psu_temp_alias, + .get_psu_temp_type = drv_get_psu_temp_type, + .get_psu_temp_max = drv_get_psu_temp_max, + .set_psu_temp_max = drv_set_psu_temp_max, + .get_psu_temp_max_hyst = drv_get_psu_temp_max_hyst, + .get_psu_temp_min = drv_get_psu_temp_min, + .set_psu_temp_min = drv_set_psu_temp_min, + .get_psu_pok_alarm = drv_get_psu_pok_alarm, + .get_psu_in_pok = drv_get_psu_in_pok, + .get_psu_out_pok = drv_get_psu_out_pok, + .get_present = drv_get_psu_present, + .get_loglevel = drv_get_loglevel, + .set_loglevel = drv_set_loglevel, + .debug_help = drv_debug_help, + .debug_help_hisonic = drv_debug_help_hisonic, +}; + +static struct psu_drivers_t pfunc_bmc= { + .get_scan_period = drv_get_scan_period, + .set_scan_period = drv_set_scan_period, + .get_mfr_info = drv_get_mfr_info_from_bmc, + .get_alarm_threshold_curr = drv_get_psu_alarm_threshold_curr, + .get_alarm_threshold_vol = drv_get_psu_alarm_threshold_vol, + .get_max_output_power = drv_get_psu_max_output_power, + .get_temp_input = drv_get_psu_temp_input_from_bmc, + .get_num_temp_sensors = drv_get_num_temp_sensors, + .get_psu_temp_alias = drv_get_psu_temp_alias, + .get_psu_temp_type = drv_get_psu_temp_type, + .get_psu_temp_max = drv_get_psu_temp_max, + .set_psu_temp_max = drv_set_psu_temp_max, + .get_psu_temp_max_hyst = drv_get_psu_temp_max_hyst, + .get_psu_temp_min = drv_get_psu_temp_min, + .set_psu_temp_min = drv_set_psu_temp_min, + .get_psu_pok_alarm = drv_get_psu_pok_alarm, + .get_psu_in_pok = drv_get_psu_in_pok, + .get_psu_out_pok = drv_get_psu_out_pok, + .get_present = drv_get_psu_present, + .get_loglevel = drv_get_loglevel, + .set_loglevel = drv_set_loglevel, + .debug_help = drv_debug_help, + .debug_help_hisonic = drv_debug_help_hisonic, +}; + +static int drv_psu_probe(struct platform_device *pdev) +{ + if(ipmi_bmc_is_ok()) + { + s3ip_psu_drivers_register(&pfunc_bmc); + } + else + { + s3ip_psu_drivers_register(&pfunc); + } + return 0; +} + +static int drv_psu_remove(struct platform_device *pdev) +{ + s3ip_psu_drivers_unregister(); + + return 0; +} + +static struct platform_driver drv_psu_driver = { + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + }, + .probe = drv_psu_probe, + .remove = drv_psu_remove, +}; + +static int __init drv_psu_init(void) +{ + int err=0; + int retval=0; + + drv_psu_device = platform_device_alloc(DRVNAME, 0); + if(!drv_psu_device) + { + PSU_ERR("(#%d): platform_device_alloc fail\n", __LINE__); + return -ENOMEM; + } + + retval = platform_device_add(drv_psu_device); + if(retval) + { + PSU_ERR("(#%d): platform_device_add failed\n", __LINE__); + err = -retval; + goto dev_add_failed; + } + + retval = platform_driver_register(&drv_psu_driver); + if(retval) + { + PSU_ERR("(#%d): platform_driver_register failed(%d)\n", __LINE__, err); + err = -retval; + goto dev_reg_failed; + } + + return 0; + +dev_reg_failed: + platform_device_unregister(drv_psu_device); + return err; + +dev_add_failed: + platform_device_put(drv_psu_device); + return err; +} + +static void __exit drv_psu_exit(void) +{ + platform_driver_unregister(&drv_psu_driver); + platform_device_unregister(drv_psu_device); + + return; +} + +MODULE_DESCRIPTION("S3IP PSU Driver"); +MODULE_VERSION(SWITCH_PSU_DRIVER_VERSION); +MODULE_LICENSE("GPL"); + +module_init(drv_psu_init); +module_exit(drv_psu_exit); diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_psu_driver.h b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_psu_driver.h new file mode 100644 index 0000000000..5fbc098bb1 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_psu_driver.h @@ -0,0 +1,116 @@ +#ifndef SWITCH_PSU_DRIVER_H +#define SWITCH_PSU_DRIVER_H + +#include "switch.h" + +#define PSU_ERR(fmt, args...) LOG_ERR("psu: ", fmt, ##args) +#define PSU_WARNING(fmt, args...) LOG_WARNING("psu: ", fmt, ##args) +#define PSU_INFO(fmt, args...) LOG_INFO("psu: ", fmt, ##args) +#define PSU_DEBUG(fmt, args...) LOG_DBG("psu: ", fmt, ##args) + +#define CHECK_BIT(a, b) (((a) >> (b)) & 1U) + +#define PSU_TOTAL_NUM 2 +#define PSU1_INDEX 1 +#define PSU2_INDEX 2 +#define PSU_FAN_DIRECT_F2B 0 +#define PSU_FAN_DIRECT_B2F 1 +#define PSU_TOTAL_TEMP_NUM 2 +#define PSU_NAME_STRING "psu" +#define TEMP_NAME_STRING "temp" +#define PSU_MAX_STR_BUFF_LENGTH 40 + +#define PSU_ONLINE_OFFSET 0x2D +#define PSU_ONLINE_PSU1_MASK 0x04 +#define PSU_ONLINE_PSU2_MASK 0x40 + +#define PSU_STATUS_OFFSET 0x0D +#define PSU_STATUS_PSU1_ALARM 0x01 +#define PSU_STATUS_PSU2_ALARM 0x10 +#define PSU_STATUS_PSU1_IPOK 0x02 +#define PSU_STATUS_PSU2_IPOK 0x20 +#define PSU_STATUS_PSU1_OPOK 0x08 +#define PSU_STATUS_PSU2_OPOK 0x80 + +// the following definition should be the same as pmbus.h +#define PMBUS_POUT_MAX 0x31 +#define PMBUS_VIN_OV_WARN_LIMIT 0x57 +#define PMBUS_IIN_OC_WARN_LIMIT 0x5D +#define PMBUS_STATUS_WORD 0x79 +#define PMBUS_STATUS_INPUT 0x7C +#define PMBUS_READ_VIN 0x88 +#define PMBUS_READ_IIN 0x89 +#define PMBUS_READ_VOUT 0x8B +#define PMBUS_READ_IOUT 0x8C +#define PMBUS_READ_TEMPERATURE_1 0x8D +#define PMBUS_READ_TEMPERATURE_2 0x8E +#define PMBUS_READ_TEMPERATURE_3 0x8F +#define PMBUS_READ_FAN_SPEED_1 0x90 +#define PMBUS_MFR_ID 0x99 +#define PMBUS_MFR_MODEL 0x9A +#define PMBUS_MFR_REVISION 0x9B +#define PMBUS_MFR_DATE 0x9D +#define PMBUS_MFR_SERIAL 0x9E +#define PMBUS_READ_POUT 0x96 +#define PMBUS_READ_PIN 0x97 +// new define +#define PMBUS_PART_NUM 0xDE + +#define PSU_ABSENT_RET 0 +#define PSU_OK_RET 1 +#define PSU_NOT_OK_RET 2 + +#define PSU_FAN_SPEED_MAX_RPM 24500 + +struct psu_drivers_t{ + int (*get_mfr_info) (unsigned int psu_index, u8 pmbus_command, char *buf); + ssize_t (*get_temp_input) (unsigned int psu_index, unsigned int psu_temp_index, long *temp_input); + bool (*get_alarm_threshold_curr) (unsigned int psu_index, long *buf); + bool (*get_alarm_threshold_vol) (unsigned int psu_index, long *buf); + bool (*get_max_output_power) (unsigned int psu_index, long *buf); + ssize_t (*get_num_temp_sensors) (unsigned int psu_index, char *buf); + ssize_t (*get_psu_temp_alias) (unsigned int psu_index, unsigned int psu_temp_index, char *buf); + ssize_t (*get_psu_temp_type) (unsigned int psu_index, unsigned int psu_temp_index, char *buf); + ssize_t (*get_psu_temp_max) (unsigned int psu_index, unsigned int psu_temp_index, char *buf); + ssize_t (*set_psu_temp_max) (unsigned int psu_index, unsigned int psu_temp_index, int val); + ssize_t (*get_psu_temp_max_hyst) (unsigned int psu_index, unsigned int psu_temp_index, char *buf); + ssize_t (*get_psu_temp_min) (unsigned int psu_index, unsigned int psu_temp_index, char *buf); + ssize_t (*set_psu_temp_min) (unsigned int psu_index, unsigned int psu_temp_index, int val); + ssize_t (*get_psu_pok_alarm) (unsigned int psu_index, unsigned int *alarm); + ssize_t (*get_psu_in_pok) (unsigned int psu_index, unsigned int *status); + ssize_t (*get_psu_out_pok) (unsigned int psu_index, unsigned int *status); + void (*get_loglevel) (long *lev); + void (*set_loglevel) (long lev); + ssize_t (*debug_help) (char *buf); + ssize_t (*debug_help_hisonic) (char *buf); + ssize_t (*debug) (const char *buf, int count); + ssize_t (*get_scan_period) (char *buf); + void (*set_scan_period) (unsigned int period); + ssize_t (*get_present) (unsigned int psu_index, unsigned int *present); +}; + +int drv_get_mfr_info(unsigned int psu_index, u8 pmbus_command, char *buf); +ssize_t drv_get_num_temp_sensors(unsigned int psu_index, char *buf); +ssize_t drv_get_psu_temp_alias(unsigned int psu_index, unsigned int psu_temp_index, char *buf); +ssize_t drv_get_psu_temp_type(unsigned int psu_index, unsigned int psu_temp_index, char *buf); +ssize_t drv_get_psu_temp_max(unsigned int psu_index, unsigned int psu_temp_index, char *buf); +ssize_t drv_set_psu_temp_max(unsigned int psu_index, unsigned int psu_temp_index, int val); +ssize_t drv_get_psu_temp_max_hyst(unsigned int psu_index, unsigned int psu_temp_index, char *buf); +ssize_t drv_get_psu_temp_min(unsigned int psu_index, unsigned int psu_temp_index, char *buf); +ssize_t drv_set_psu_temp_min(unsigned int psu_index, unsigned int psu_temp_index, int val); +ssize_t get_psu_pok_alarm(unsigned int psu_index, unsigned int *alarm); +ssize_t drv_get_psu_in_pok(unsigned int psu_index, unsigned int *status); +ssize_t drv_get_psu_out_pok(unsigned int psu_index, unsigned int *status); +void drv_get_loglevel(long *lev); +void drv_set_loglevel(long lev); +ssize_t drv_debug_help(char *buf); +ssize_t drv_debug_help_hisonic(char *buf); +ssize_t drv_debug(const char *buf, int count); +ssize_t drv_get_scan_period(char *buf); +void drv_set_scan_period(unsigned int period); +ssize_t drv_get_psu_present(unsigned int psu_index, unsigned int *present); + +void s3ip_psu_drivers_register(struct psu_drivers_t *p_func); +void s3ip_psu_drivers_unregister(void); + +#endif /* SWITCH_PSU_DRIVER_H */ diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_sensor_driver.c b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_sensor_driver.c new file mode 100644 index 0000000000..8a897ff882 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_sensor_driver.c @@ -0,0 +1,716 @@ +#include +#include +#include +#include +#include +#include + +#include "switch_sensor_driver.h" +#include "switch_coretemp.h" +#include "switch_lm75.h" +#include "pmbus.h" +#include "sysfs_ipmi.h" + +#define DRVNAME "drv_sensor_driver" +#define SWITCH_SENSOR_DRIVER_VERSION "0.0.1" + +#define ipmitool_raw_get_in_input 1 +#define ipmitool_raw_get_curr_input 2 + +unsigned int loglevel = 0; +extern unsigned int loglevel_bmc; +static struct platform_device *drv_sensor_device; + +static temp_sensor_node_t temp_sensor_nodes[TEMP_TOTAL_NUM] = { + {0, 0x00, 107000, 0, -10000, "MAC_PIPE_Temp", "LSW"}, + {0, 0x00, 99000, 0, -30000, "CPU_core0_temp", "core"}, + {0, 0x00, 99000, 0, -30000, "CPU_core1_temp", "core"}, + {0, 0x00, 99000, 0, -30000, "CPU_core2_temp", "core"}, + {0, 0x00, 99000, 0, -30000, "CPU_core3_temp", "core"}, + {0, 0x00, 99000, 0, -30000, "CPU_package_temp", "core"}, + {1, 0x48, 56000, 5000, -10000, "TMP75_48_TEMP", "tmp75_48"}, + {1, 0x49, 56000, 5000, -10000, "TMP75_49_TEMP", "tmp75_49"}, + {1, 0x4a, 80000, 5000, -10000, "TMP75_4A_TEMP", "tmp75_4a"}, + {1, 0x4b, 76000, 5000, -10000, "TMP75_4B_TEMP", "tmp75_4b"}, + {1, 0x4c, 76000, 5000, -10000, "TMP431_4C_TEMP1", "tmp431_4c1"}, + {1, 0x4c, 100000, 5000, -10000, "TMP431_4C_TEMP2", "tmp431_4c2"}, + {1, 0x4e, 73000, 5000, -10000, "TMP75_4E_TEMP", "tmp75_4e"}, + {2, 0x4d, 73000, 5000, -10000, "TMP275_4D_TEMP", "tmp275_4d"}, + {2, 0x4e, 72000, 5000, -10000, "TMP275_4E_TEMP", "tmp275_4e"}, + {5, 0x20, 150000, 0, -30000, "VCORE_MAC_TEMP", "mp2973_20_temp"}, + {5, 0x40, 150000, 0, -30000, "VDDT0V9_R_MAC_TEMP", "mp2940b_40_temp"}, + {5, 0x60, 150000, 0, -30000, "VDD1V5_1V8_MAC_TEMP", "mp2976_60_temp"}, + {5, 0x50, 150000, 0, -30000, "VCC3V3_QSFP_AB_TEMP", "mp2976_50_temp"}, + {5, 0x5f, 150000, 0, -30000, "VCC3V3_QSFP_CD_TEMP", "mp2976_5f_temp"}, +}; + +static vr_sensor_node_t vr_sensor_vol_nodes[VOL_TOTAL_NUM] = { + {UCD90320, CHANNEL_FIRST, VR_VIN, 13200, 10800, 12000, 0, "M_V12V_PSU_VOUT", "UCD90320", "in1_input"}, + {UCD90320, CHANNEL_SECORND, VR_VIN, 13200, 10800, 12000, 0, "M_V12V_COME_VOUT", "UCD90320", "in1_input"}, + {UCD90320, CHANNEL_THIRD, VR_VIN, 13200, 10800, 12000, 0, "M_V12V_MB_VOUT", "UCD90320", "in1_input"}, + {UCD90320, CHANNEL_FORTH, VR_VIN, 5500, 4500, 5000, 0, "M_V5V_SB_VOUT", "UCD90320", "in2_input"}, + {UCD90320, CHANNEL_FIFTH, VR_VIN, 3630, 2970, 3300, 0, "M_3V3SB_PSU_VOUT", "UCD90320", "in3_input"}, + {UCD90320, CHANNEL_SIXTH, VR_VIN, 3630, 2970, 3300, 0, "VOL_VCC3V3_SB_MON6", "UCD90320", "in1_input"}, + {UCD90320, CHANNEL_SEVENTH, VR_VIN, 3630, 2970, 3300, 0, "M_V3V3_SB_VOUT", "UCD90320", "in2_input"}, + {UCD90320, CHANNEL_EIGHTH, VR_VIN, 3630, 2970, 3300, 0, "M_3V3_QSFPA_VOUT", "UCD90320", "in3_input"}, + {UCD90320, CHANNEL_NINTH, VR_VIN, 3630, 2970, 3300, 0, "M_3V3_QSFPB_VOUT", "UCD90320", "in1_input"}, + {UCD90320, CHANNEL_TEHTH, VR_VIN, 3630, 2970, 3300, 0, "M_3V3_QSFPC_VOUT", "UCD90320", "in2_input"}, + {UCD90320, CHANNEL_ELEVEN, VR_VIN, 3630, 2970, 3300, 0, "M_3V3_QSFPD_VOUT", "UCD90320", "in1_input"}, + {UCD90320, CHANNEL_TWELVE, VR_VIN, 1960, 1660, 1800, 0, "M_VDDA_1V8_VOUT", "UCD90320", "in2_input"}, + {UCD90320, CHANNEL_THIRTEEN, VR_VIN, 1960, 1660, 1800, 0, "M_VDD_1V8_VOUT", "UCD90320", "in3_input"}, + {UCD90320, CHANNEL_FOURTEEN, VR_VIN, 1630, 1380, 1500, 0, "M_V1V5_VOUT", "UCD90320", "in1_input"}, + {UCD90320, CHANNEL_FIFTEEN, VR_VIN, 1630, 1380, 1500, 0, "M_VDDA_1V5_VOUT", "UCD90320", "in2_input"}, + {UCD90320, CHANNEL_SIXTEEN, VR_VIN, 1300, 1100, 1200, 0, "M_V1V2_VOUT", "UCD90320", "in3_input"}, + {UCD90320, CHANNEL_SEVENTEEN, VR_VIN, 1140, 970, 1050, 0, "M_V1V05_VOUT", "UCD90320", "in1_input"}, + {UCD90320, CHANNEL_EIGHTEEN, VR_VIN, 1140, 970, 1050, 0, "M_VDD_1V05_VOUT", "UCD90320", "in2_input"}, + {UCD90320, CHANNEL_NINETEEN, VR_VIN, 1090, 920, 1000, 0, "M_VDDA_1V0_VOUT", "UCD90320", "in1_input"}, + {UCD90320, CHANNEL_TWENTY, VR_VIN, 1090, 920, 1000, 0, "M_VDDT_1V0_VOUT", "UCD90320", "in2_input"}, + {UCD90320, CHANNEL_TWENTY_ONE, VR_VIN, 980, 830, 900, 0, "M_VDDT_0V9_VOUT", "UCD90320", "in3_input"}, + {UCD90320, CHANNEL_TWENTY_TWO, VR_VIN, 820, 690, 750, 0, "M_VDD_0V75_VOUT", "UCD90320", "in1_input"}, + {UCD90320, CHANNEL_TWENTY_THREE, VR_VIN, 950, 710, 12000, 0, "M_VDD_CORE_VOUT", "UCD90320", "in2_input"}, + {LM25066, CHANNEL_DEFAULT, VR_VIN, 13200, 10800, 12000, 0, "VCC12VIN_HOTSWAP_VIN", "LM25066", "in1_input"}, + {LM25066, CHANNEL_DEFAULT, VR_VOUT, 13200, 10800, 12000, 0, "VCC12VIN_HOTSWAP_VOUT", "LM25066", "in1_input"}, + {MP2973, CHANNEL_DEFAULT, VR_VIN, 13200, 10800, 12000, 0, "VCORE_MAC_VIN", "MP2973", "in1_input"}, + {MP2973, CHANNEL_DEFAULT, VR_VOUT, 950, 710, 0, 0, "VCORE_MAC_VOUT", "MP2973", "in2_input"}, + {MP2940, CHANNEL_DEFAULT, VR_VIN, 13200, 10800, 12000, 0, "VDDT0V9_R_MAC_VIN", "MP2940", "in3_input"}, + {MP2940, CHANNEL_DEFAULT, VR_VOUT, 885, 835, 860, 0, "VDDT0V9_R_MAC_VOUT", "MP2940", "in1_input"}, + {MP2976_U1B1, CHANNEL_DEFAULT, VR_VIN, 13200, 10800, 12000, 0, "VDD1V5_1V8_MAC_VIN", "MP2976", "in2_input"}, + {MP2976_U1B1, CHANNEL_DEFAULT, VR_VOUT, 1750, 1650, 1700, 0, "VDDA1V5_MAC_VOUT", "MP2976", "in3_input"}, + {MP2976_U1B1, CHANNEL_FIRST, VR_VOUT, 1890, 1795, 1850, 0, "VDDA1V8_MAC_VOUT", "MP2976", "in1_input"}, + {MP2976_U1J1, CHANNEL_DEFAULT, VR_VIN, 13200, 10800, 12000, 0, "VCC3V3_QSFP_AB_VIN", "MP2976", "in2_input"}, + {MP2976_U1J1, CHANNEL_DEFAULT, VR_VOUT, 3630, 2790, 3300, 0, "VCC3V3_QSFP_A_VOUT", "MP2976", "in1_input"}, + {MP2976_U1J1, CHANNEL_FIRST, VR_VOUT, 3630, 2790, 3300, 0, "VCC3V3_QSFP_B_VOUT", "MP2976", "in2_input"}, + {MP2976_U1J2, CHANNEL_DEFAULT, VR_VIN, 20700, 0, 0, 0, "VCC3V3_QSFP_CD_VIN", "MP2976", "in3_input"}, + {MP2976_U1J2, CHANNEL_DEFAULT, VR_VOUT, 37200, 0, 0, 0, "VCC3V3_QSFP_C_VOUT", "MP2976", "in1_input"}, + {MP2976_U1J2, CHANNEL_FIRST, VR_VOUT, 37200, 0, 0, 0, "VCC3V3_QSFP_D_VOUT", "MP2976", "in2_input"}, +}; + +static vr_chip_info_t vr_chips[TOTAL_VR_CHIP_NUM] = { + {UCD90320, 4, 0x11}, + {LM25066, 5, 0x41}, + {MP2973, 5, 0x20}, + {MP2940, 5, 0x40}, + {MP2976_U1B1, 5, 0x60}, + {MP2976_U1J1, 5, 0x50}, + {MP2976_U1J2, 5, 0x5f}, +}; + + +static vr_sensor_node_t vr_sensor_curr_nodes[CURR_TOTAL_NUM] = { + {LM25066, CHANNEL_DEFAULT, VR_IIN, 23400, 0, 0, 0, "VCC12VIN_HOTSWAP_IIN", "LM25066", "curr2_input"}, + {LM25066, CHANNEL_DEFAULT, VR_IOUT, 0, 0, 0, 0, "VCC12VIN_HOTSWAP_IOUT", "LM25066", "curr3_input"}, + {MP2973, CHANNEL_DEFAULT, VR_IIN, 43800, 0, 0, 0, "VCORE_MAC_IIN", "MP2973", "curr2_input"}, + {MP2973, CHANNEL_DEFAULT, VR_IOUT, 499200, 0, 0, 0, "VCORE_MAC_IOUT", "MP2973", "curr3_input"}, + {MP2940, CHANNEL_DEFAULT, VR_IIN, 78900, 0, 0, 0, "VDDT0V9_R_MAC_IOUT", "MP2940", "curr3_input"}, + {MP2976_U1B1, CHANNEL_DEFAULT, VR_IIN, 6000, 0, 0, 0, "VDD1V5_1V8_MAC_IIN", "MP2976", "curr1_input"}, + {MP2976_U1B1, CHANNEL_DEFAULT, VR_IOUT, 37200, 0, 0, 0, "VDDA1V5_MAC_IOUT", "MP2976", "curr2_label"}, + {MP2976_U1B1, CHANNEL_FIRST, VR_IOUT, 16800, 0, 0, 0, "VDDA1V8_MAC_IOUT", "MP2976", "curr3_input"}, + {MP2976_U1J1, CHANNEL_DEFAULT, VR_IIN, 20700, 0, 0, 0, "VCC3V3_QSFP_AB_IIN", "MP2976", "curr1_input"}, + {MP2976_U1J1, CHANNEL_DEFAULT, VR_IOUT, 37200, 0, 0, 0, "VCC3V3_QSFP_A_IOUT", "MP2976", "curr2_label"}, + {MP2976_U1J1, CHANNEL_FIRST, VR_IOUT, 37200, 0, 0, 0, "VCC3V3_QSFP_B_IOUT", "MP2976", "curr3_input"}, + {MP2976_U1J2, CHANNEL_DEFAULT, VR_IIN, 20700, 0, 0, 0, "VCC3V3_QSFP_CD_IIN", "MP2976", "curr1_input"}, + {MP2976_U1J2, CHANNEL_DEFAULT, VR_IOUT, 37200, 0, 0, 0, "VCC3V3_QSFP_C_IOUT", "MP2976", "curr2_label"}, + {MP2976_U1J2, CHANNEL_FIRST, VR_IOUT, 37200, 0, 0, 0, "VCC3V3_QSFP_D_IOUT", "MP2976", "curr3_input"}, +}; + +ssize_t drv_get_temp_alias(unsigned int index, char *buf) +{ +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%s\n", temp_sensor_nodes[index].alias_string); +#else + return sprintf(buf, "%s\n", temp_sensor_nodes[index].alias_string); +#endif +} + +ssize_t drv_get_temp_type(unsigned int index, char *buf) +{ +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%s\n", temp_sensor_nodes[index].type_string); +#else + return sprintf(buf, "%s\n", temp_sensor_nodes[index].type_string); +#endif +} + +bool drv_get_temp_max(unsigned int index, long *val) +{ + *val = temp_sensor_nodes[index].temp_max; + + return true; +} + +bool drv_set_temp_max(unsigned int index, long data) +{ + temp_sensor_nodes[index].temp_max = data; + + return true; +} + +bool drv_get_temp_max_hyst(unsigned int index, long *val) +{ + *val = temp_sensor_nodes[index].temp_max_hyst; + + return true; +} + +bool drv_get_temp_min(unsigned int index, long *val) +{ + *val = temp_sensor_nodes[index].temp_min; + + return true; +} + +bool drv_set_temp_min(unsigned int index, long data) +{ + temp_sensor_nodes[index].temp_min = data; + + return true; +} + +bool drv_get_temp_input_from_bmc(unsigned int index, long *temp_input) +{ + int retval=0; + int thermal_id; + + switch(index) + { + case TMP75_48_TEMP: + case TMP75_49_TEMP: + case TMP75_4A_TEMP: + case TMP75_4B_TEMP: + case TMP431_4C_TEMP1: + case TMP431_4C_TEMP2: + case TMP75_4E_TEMP: + case TMP275_4D_TEMP: + case TMP275_4E_TEMP: + thermal_id = index - X86_PACKAGE; + retval = drv_get_sensor_temp_input_from_bmc(thermal_id, temp_input); + if(retval < 0) + { + SENSOR_DEBUG("Get temp%d input failed.\n", index); + return false; + } + break; + case VCORE_MAC_TEMP: + case VDDT0V9_R_MAC_TEMP: + case VDD1V5_1V8_MAC_TEMP: + case VCC3V3_QSFP_AB_TEMP: + case VCC3V3_QSFP_CD_TEMP: + thermal_id = index - 1; + retval = drv_get_sensor_temp_input_from_bmc(thermal_id, temp_input); + if(retval < 0) + { + SENSOR_DEBUG("Get temp%d input failed.\n", index); + return false; + } + break; + case X86_CORE1: + case X86_CORE2: + case X86_CORE3: + case X86_CORE4: + case X86_PACKAGE: + retval = coretemp_get_temp_input(index, temp_input); + if(retval < 0) + { + SENSOR_DEBUG("Get temp%d input failed.\n", index); + return false; + } + break; + case LSW_CORE: + retval = 0; + break; + + default: + return false; + } + + return true; +} + +bool drv_get_temp_input(unsigned int index, long *temp_input) +{ + int retval=0; + + switch(index) + { + case TMP75_48_TEMP: + case TMP75_49_TEMP: + case TMP75_4A_TEMP: + case TMP75_4B_TEMP: + case TMP431_4C_TEMP1: + case TMP431_4C_TEMP2: + case TMP75_4E_TEMP: + case TMP275_4D_TEMP: + case TMP275_4E_TEMP: + retval = lm75_read_temp_input(temp_sensor_nodes[index].bus_num, temp_sensor_nodes[index].dev_addr, temp_input); + if(retval < 0) + { + SENSOR_DEBUG("Get temp%d input failed.\n", index); + return false; + } + break; + case X86_CORE1: + case X86_CORE2: + case X86_CORE3: + case X86_CORE4: + case X86_PACKAGE: + retval = coretemp_get_temp_input(index, temp_input); + if(retval < 0) + { + SENSOR_DEBUG("Get temp%d input failed.\n", index); + return false; + } + break; + case LSW_CORE: + retval = 0; + break; + + default: + return false; + } + + return true; +} + +ssize_t drv_get_vol_alias(unsigned int index, char *buf) +{ +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%s\n", vr_sensor_vol_nodes[index].alias_string); +#else + return sprintf(buf, "%s\n", vr_sensor_vol_nodes[index].alias_string); +#endif + +} + +ssize_t drv_get_vol_type(unsigned int index, char *buf) +{ +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%s\n", vr_sensor_vol_nodes[index].type_string); +#else + return sprintf(buf, "%s\n", vr_sensor_vol_nodes[index].type_string); +#endif +} + +bool drv_get_vol_max(unsigned int index, long *val) +{ + *val = vr_sensor_vol_nodes[index].data_max; + return true; +} + +bool drv_set_vol_max(unsigned int index, long data) +{ + vr_sensor_vol_nodes[index].data_max = data; + return true; +} + +bool drv_get_vol_min(unsigned int index, long *val) +{ + *val = vr_sensor_vol_nodes[index].data_min; + return true; +} + +bool drv_set_vol_min(unsigned int index, long data) +{ + vr_sensor_vol_nodes[index].data_min = data; + return true; +} + +bool drv_get_vol_range(unsigned int index, long *val) +{ + *val = vr_sensor_vol_nodes[index].data_range; + return true; +} + +bool drv_get_vol_nominal(unsigned int index, long *val) +{ + *val = vr_sensor_vol_nodes[index].data_nominal; + return true; +} + +bool drv_get_vol_input(unsigned int index, long *vol_input) +{ + unsigned char chip_id; + unsigned int bus_num; + unsigned short dev_addr; + int retval=0; + int i; + + chip_id = vr_sensor_vol_nodes[index].chip_id; + for(i = 0; i < TOTAL_VR_CHIP_NUM; i++) + { + if(chip_id == vr_chips[i].chip_id) + { + bus_num = vr_chips[i].bus_num; + dev_addr = vr_chips[i].dev_addr; + break; + } + } + + if(i == TOTAL_VR_CHIP_NUM) + { + SENSOR_DEBUG("Get in%d chip info failed.\n", index); + return false; + } + + retval = pmbus_core_read_attrs(bus_num, dev_addr, vr_sensor_vol_nodes[index].node_name, vol_input); + if(retval < 0) + { + SENSOR_DEBUG("Get in%d input failed.\n", index); + return false; + } + + return true; +} + +bool drv_get_vol_input_from_bmc(unsigned int index, long *vol_input) +{ + int retval=0; + + retval = drv_get_sensor_vol_input_from_bmc(vr_sensor_vol_nodes[index], vol_input); + if(retval < 0) + { + SENSOR_DEBUG("Get in%d input failed.\n", index); + return false; + } + + return true; +} + +ssize_t drv_get_curr_alias(unsigned int index, char *buf) +{ +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%s\n", vr_sensor_curr_nodes[index].alias_string); +#else + return sprintf(buf, "%s\n", vr_sensor_curr_nodes[index].alias_string); +#endif +} + +ssize_t drv_get_curr_type(unsigned int index, char *buf) +{ +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%s\n", vr_sensor_curr_nodes[index].type_string); +#else + return sprintf(buf, "%s\n", vr_sensor_curr_nodes[index].type_string); +#endif +} + +bool drv_get_curr_max(unsigned int index, long *val) +{ + *val = vr_sensor_curr_nodes[index].data_max; + return true; +} + +bool drv_set_curr_max(unsigned int index, long data) +{ + vr_sensor_curr_nodes[index].data_max = data; + return true; +} + +bool drv_get_curr_min(unsigned int index, long *val) +{ + *val = vr_sensor_curr_nodes[index].data_min; + return true; +} + +bool drv_set_curr_min(unsigned int index, long data) +{ + vr_sensor_curr_nodes[index].data_min = data; + return true; +} + +bool drv_get_curr_input(unsigned int index, long *curr_input) +{ + unsigned char chip_id; + unsigned int bus_num; + unsigned short dev_addr; + int retval=0; + int i; + + chip_id = vr_sensor_curr_nodes[index].chip_id; + for(i = 0; i < TOTAL_VR_CHIP_NUM; i++) + { + if(chip_id == vr_chips[i].chip_id) + { + bus_num = vr_chips[i].bus_num; + dev_addr = vr_chips[i].dev_addr; + break; + } + } + + if(i == TOTAL_VR_CHIP_NUM) + { + SENSOR_DEBUG("Get curr%d chip info failed.\n", index); + return false; + } + + retval = pmbus_core_read_attrs(bus_num, dev_addr, vr_sensor_curr_nodes[index].node_name, curr_input); + if(retval < 0) + { + SENSOR_DEBUG("Get curr%d input failed.\n", index); + return false; + } + + return true; +} + +bool drv_get_curr_input_from_bmc(unsigned int index, long *curr_input) +{ + int retval=0; + + retval = drv_get_sensor_curr_input_from_bmc(vr_sensor_curr_nodes[index], curr_input); + + if(retval < 0) + { + SENSOR_DEBUG("Get curr%d input failed.\n", index); + return false; + } + + return true; +} + +void drv_get_loglevel(long *lev) +{ + *lev = (long)loglevel; + + return; +} + +void drv_set_loglevel(long lev) +{ + loglevel = (unsigned int)lev; + + return; +} + +ssize_t drv_debug_help(char *buf) +{ +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, + "tmp75_48 ipmitool raw 0x36 0x10 1\n" + "tmp75_49 ipmitool raw 0x36 0x10 2\n" + "tmp75_4a ipmitool raw 0x36 0x10 3\n" + "tmp75_4b ipmitool raw 0x36 0x10 4\n" + "tmp431_4c1 ipmitool raw 0x36 0x10 5\n" + "tmp431_4c2 ipmitool raw 0x36 0x10 6\n" + "tmp75_4e ipmitool raw 0x36 0x10 7\n" + "tmp275_4d ipmitool raw 0x36 0x10 8\n" + "tmp275_4e ipmitool raw 0x36 0x10 9\n" + "lm25066_41_temp ipmitool raw 0x36 0x10 14\n" + "mp2973_20_temp ipmitool raw 0x36 0x10 15\n" + "mp2940b_40_temp ipmitool raw 0x36 0x10 16\n" + "mp2976_60_temp ipmitool raw 0x36 0x10 17\n" + "mp2976_50_temp ipmitool raw 0x36 0x10 18\n" + "mp2976_5f_temp ipmitool raw 0x36 0x10 19\n"); +#else + return sprintf(buf, + "tmp75_48 ipmitool raw 0x36 0x10 1\n" + "tmp75_49 ipmitool raw 0x36 0x10 2\n" + "tmp75_4a ipmitool raw 0x36 0x10 3\n" + "tmp75_4b ipmitool raw 0x36 0x10 4\n" + "tmp431_4c1 ipmitool raw 0x36 0x10 5\n" + "tmp431_4c2 ipmitool raw 0x36 0x10 6\n" + "tmp75_4e ipmitool raw 0x36 0x10 7\n" + "tmp275_4d ipmitool raw 0x36 0x10 8\n" + "tmp275_4e ipmitool raw 0x36 0x10 9\n" + "lm25066_41_temp ipmitool raw 0x36 0x10 14\n" + "mp2973_20_temp ipmitool raw 0x36 0x10 15\n" + "mp2940b_40_temp ipmitool raw 0x36 0x10 16\n" + "mp2976_60_temp ipmitool raw 0x36 0x10 17\n" + "mp2976_50_temp ipmitool raw 0x36 0x10 18\n" + "mp2976_5f_temp ipmitool raw 0x36 0x10 19\n"); +#endif +} + +ssize_t drv_debug_help_hisonic(char *buf) +{ +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, + "For LM75_COMe, use i2cget/i2cset -f -y 101 0x4b to debug.\n" + "For LM75_64DQ_Fanout, use i2cget/i2cset -f -y 105 0x49 to debug.\n" + "sysname debug cmd\n" + "1 temp i2cget -f -y 101 0x4b 0x0\n" + "2 temp i2cget -f -y 105 0x49 0x0\n" + "3 temp cat /sys/class/hwmon/hwmon*/name\n" + " cat /sys/class/hwmon/hwmon4/temp*_input\n" + "4 temp cat /sys/class/hwmon/hwmon*/name\n" + " cat /sys/class/hwmon/hwmon4/temp*_input\n" + "5 temp cat /sys/class/hwmon/hwmon*/name\n" + " cat /sys/class/hwmon/hwmon4/temp*_input\n" + "6 temp cat /sys/class/hwmon/hwmon*/name\n" + " cat /sys/class/hwmon/hwmon4/temp*_input\n" + "7 temp cat /sys/class/hwmon/hwmon*/name\n" + " cat /sys/class/hwmon/hwmon4/temp*_input\n" + "8 temp i2cget -f -y 105 0x49 0x0\n" + " value = 1.273*LSW_LM75+18.7\n"); +#else + return sprintf(buf, + "For LM75_COMe, use i2cget/i2cset -f -y 101 0x4b to debug.\n" + "For LM75_64DQ_Fanout, use i2cget/i2cset -f -y 105 0x49 to debug.\n" + "sysname debug cmd\n" + "1 temp i2cget -f -y 101 0x4b 0x0\n" + "2 temp i2cget -f -y 105 0x49 0x0\n" + "3 temp cat /sys/class/hwmon/hwmon*/name\n" + " cat /sys/class/hwmon/hwmon4/temp*_input\n" + "4 temp cat /sys/class/hwmon/hwmon*/name\n" + " cat /sys/class/hwmon/hwmon4/temp*_input\n" + "5 temp cat /sys/class/hwmon/hwmon*/name\n" + " cat /sys/class/hwmon/hwmon4/temp*_input\n" + "6 temp cat /sys/class/hwmon/hwmon*/name\n" + " cat /sys/class/hwmon/hwmon4/temp*_input\n" + "7 temp cat /sys/class/hwmon/hwmon*/name\n" + " cat /sys/class/hwmon/hwmon4/temp*_input\n" + "8 temp i2cget -f -y 105 0x49 0x0\n" + " value = 1.273*LSW_LM75+18.7\n"); +#endif +} + +ssize_t drv_debug(const char *buf, int count) +{ + return 0; +} + +// For s3ip +static struct sensor_drivers_t pfunc_bmc= { + .get_temp_input = drv_get_temp_input_from_bmc, + .get_temp_alias = drv_get_temp_alias, + .get_temp_type = drv_get_temp_type, + .get_temp_max = drv_get_temp_max, + .set_temp_max = drv_set_temp_max, + .get_temp_max_hyst = drv_get_temp_max_hyst, + .get_temp_min = drv_get_temp_min, + .set_temp_min = drv_set_temp_min, + .get_vol_alias = drv_get_vol_alias, + .get_vol_type = drv_get_vol_type, + .get_vol_max = drv_get_vol_max, + .set_vol_max = drv_set_vol_max, + .get_vol_min = drv_get_vol_min, + .set_vol_min = drv_set_vol_min, + .get_vol_input = drv_get_vol_input_from_bmc, + .get_vol_range = drv_get_vol_range, + .get_vol_nominal = drv_get_vol_nominal, + .get_curr_alias = drv_get_curr_alias, + .get_curr_type = drv_get_curr_type, + .get_curr_max = drv_get_curr_max, + .set_curr_max = drv_set_curr_max, + .get_curr_min = drv_get_curr_min, + .set_curr_min = drv_set_curr_min, + .get_curr_input = drv_get_curr_input_from_bmc, + .get_loglevel = drv_get_loglevel, + .set_loglevel = drv_set_loglevel, + .debug_help = drv_debug_help, + .debug_help_hisonic = drv_debug_help_hisonic, +}; + +static struct sensor_drivers_t pfunc = { + .get_temp_input = drv_get_temp_input, + .get_temp_alias = drv_get_temp_alias, + .get_temp_type = drv_get_temp_type, + .get_temp_max = drv_get_temp_max, + .set_temp_max = drv_set_temp_max, + .get_temp_max_hyst = drv_get_temp_max_hyst, + .get_temp_min = drv_get_temp_min, + .set_temp_min = drv_set_temp_min, + .get_vol_alias = drv_get_vol_alias, + .get_vol_type = drv_get_vol_type, + .get_vol_max = drv_get_vol_max, + .set_vol_max = drv_set_vol_max, + .get_vol_min = drv_get_vol_min, + .set_vol_min = drv_set_vol_min, + .get_vol_input = drv_get_vol_input, + .get_vol_range = drv_get_vol_range, + .get_vol_nominal = drv_get_vol_nominal, + .get_curr_alias = drv_get_curr_alias, + .get_curr_type = drv_get_curr_type, + .get_curr_max = drv_get_curr_max, + .set_curr_max = drv_set_curr_max, + .get_curr_min = drv_get_curr_min, + .set_curr_min = drv_set_curr_min, + .get_curr_input = drv_get_curr_input, + .get_loglevel = drv_get_loglevel, + .set_loglevel = drv_set_loglevel, + .debug_help = drv_debug_help, + .debug_help_hisonic = drv_debug_help_hisonic, +}; + +static int drv_sensor_probe(struct platform_device *pdev) +{ + if(ipmi_bmc_is_ok()) + { + s3ip_sensor_drivers_register(&pfunc_bmc); + } + else + { + s3ip_sensor_drivers_register(&pfunc); + } + + return 0; +} + +static int drv_sensor_remove(struct platform_device *pdev) +{ + s3ip_sensor_drivers_unregister(); + + return 0; +} + +static struct platform_driver drv_sensor_driver = { + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + }, + .probe = drv_sensor_probe, + .remove = drv_sensor_remove, +}; + +static int __init drv_sensor_init(void) +{ + int err=0; + int retval=0; + + drv_sensor_device = platform_device_alloc(DRVNAME, 0); + if(!drv_sensor_device) + { + SENSOR_DEBUG("%s(#%d): platform_device_alloc fail\n", __func__, __LINE__); + return -ENOMEM; + } + + retval = platform_device_add(drv_sensor_device); + if(retval) + { + SENSOR_DEBUG("%s(#%d): platform_device_add failed\n", __func__, __LINE__); + err = -retval; + goto dev_add_failed; + } + + retval = platform_driver_register(&drv_sensor_driver); + if(retval) + { + SENSOR_DEBUG("%s(#%d): platform_driver_register failed(%d)\n", __func__, __LINE__, err); + err = -retval; + goto dev_reg_failed; + } + + return 0; + +dev_reg_failed: + platform_device_unregister(drv_sensor_device); + return err; + +dev_add_failed: + platform_device_put(drv_sensor_device); + return err; +} + +static void __exit drv_sensor_exit(void) +{ + platform_driver_unregister(&drv_sensor_driver); + platform_device_unregister(drv_sensor_device); + + return; +} + +MODULE_DESCRIPTION("S3IP Sensor Driver"); +MODULE_VERSION(SWITCH_SENSOR_DRIVER_VERSION); +MODULE_LICENSE("GPL"); + +module_init(drv_sensor_init); +module_exit(drv_sensor_exit); diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_sensor_driver.h b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_sensor_driver.h new file mode 100644 index 0000000000..8eade155eb --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_sensor_driver.h @@ -0,0 +1,247 @@ +#ifndef SWITCH_SENSOR_DRIVER_H +#define SWITCH_SENSOR_DRIVER_H + +#include "switch.h" + +#define SENSOR_ERR(fmt, args...) LOG_ERR("sensor: ", fmt, ##args) +#define SENSOR_WARNING(fmt, args...) LOG_WARNING("sensor: ", fmt, ##args) +#define SENSOR_INFO(fmt, args...) LOG_INFO("sensor: ", fmt, ##args) +#define SENSOR_DEBUG(fmt, args...) LOG_DBG("sensor: ", fmt, ##args) + + +/* temperature sensor */ +struct temp_sensor_node_s { + uint bus_num; + ushort dev_addr; + long temp_max; + long temp_max_hyst; + long temp_min; + char* alias_string; + char* type_string; +}; +typedef struct temp_sensor_node_s temp_sensor_node_t; + + +enum temp_index { + LSW_CORE = 0, + X86_CORE1 = 1, + X86_CORE2 = 2, + X86_CORE3 = 3, + X86_CORE4 = 4, + X86_PACKAGE = 5, + TMP75_48_TEMP = 6, + TMP75_49_TEMP = 7, + TMP75_4A_TEMP = 8, + TMP75_4B_TEMP = 9, + TMP431_4C_TEMP1 = 10, + TMP431_4C_TEMP2 = 11, + TMP75_4E_TEMP = 12, + TMP275_4D_TEMP = 13, + TMP275_4E_TEMP = 14, + VCORE_MAC_TEMP = 15, + VDDT0V9_R_MAC_TEMP = 16, + VDD1V5_1V8_MAC_TEMP = 17, + VCC3V3_QSFP_AB_TEMP = 18, + VCC3V3_QSFP_CD_TEMP = 19, + TEMP_TOTAL_NUM = 20, +}; + +/* voltage regulator */ +struct vr_sensor_node_s { + u8 chip_id; + u8 channel; + u8 attr_type; + long data_max; + long data_min; + long data_nominal; + long data_range; + char* alias_string; + char* type_string; + char* node_name; +}; +typedef struct vr_sensor_node_s vr_sensor_node_t; + +struct vr_chip_info_s { + unsigned char chip_id; + unsigned int bus_num; + unsigned short dev_addr; +}; +typedef struct vr_chip_info_s vr_chip_info_t; + + +enum vr_chip_id { + UCD90320 = 0, + LM25066 = 1, + MP2973 = 2, + MP2940 = 3, + MP2976_U1B1 = 4, + MP2976_U1J1 = 5, + MP2976_U1J2 = 6, + TOTAL_VR_CHIP_NUM = 7, +}; + +enum vr_channel_num { + CHANNEL_DEFAULT = 0, + CHANNEL_FIRST = 1, + CHANNEL_SECORND = 2, + CHANNEL_THIRD = 3, + CHANNEL_FORTH = 4, + CHANNEL_FIFTH = 5, + CHANNEL_SIXTH = 6, + CHANNEL_SEVENTH = 7, + CHANNEL_EIGHTH = 8, + CHANNEL_NINTH = 9, + CHANNEL_TEHTH = 10, + CHANNEL_ELEVEN = 11, + CHANNEL_TWELVE = 12, + CHANNEL_THIRTEEN = 13, + CHANNEL_FOURTEEN = 14, + CHANNEL_FIFTEEN = 15, + CHANNEL_SIXTEEN = 16, + CHANNEL_SEVENTEEN = 17, + CHANNEL_EIGHTEEN = 18, + CHANNEL_NINETEEN = 19, + CHANNEL_TWENTY = 20, + CHANNEL_TWENTY_ONE = 21, + CHANNEL_TWENTY_TWO = 22, + CHANNEL_TWENTY_THREE = 23, +}; + +enum vr_attr_type { + VR_TEMP = 0, + VR_VIN = 1, + VR_VOUT = 2, + VR_IIN = 3, + VR_IOUT = 4, + VR_PIN = 5, + VR_POUT = 6, +}; + +enum vol_index { + M_V12V_PSU_VOUT = 0, + M_V12V_COME_VOUT = 1, + M_V12V_MB_VOUT = 2, + M_V5V_SB_VOUT = 3, + M_3V3SB_PSU_VOUT = 4, + M_V3V3_SB_VOUT = 5, + M_VDD3P3_VOUT = 6, + M_3V3_QSFPA_VOUT = 7, + M_3V3_QSFPB_VOUT = 8, + M_3V3_QSFPC_VOUT = 9, + M_3V3_QSFPD_VOUT = 10, + M_VDDA_1V8_VOUT = 11, + M_VDD_1V8_VOUT = 12, + M_V1V5_VOUT = 13, + M_VDDA_1V5_VOUT = 14, + M_V1V2_VOUT = 15, + M_V1V05_VOUT = 16, + M_VDD_1V05_VOUT = 17, + M_VDDA_1V0_VOUT = 18, + M_VDDT_1V0_VOUT = 19, + M_VDDT_0V9_VOUT = 20, + M_VDD_0V75_VOUT = 21, + M_VDD_CORE_VOUT = 22, + VCC12VIN_HOTSWAP_VIN = 23, + VCC12VIN_HOTSWAP_VOUT = 24, + VCORE_MAC_VIN = 25, + VCORE_MAC_VOUT = 26, + VDDT0V9_R_MAC_VIN = 27, + VDDT0V9_R_MAC_VOUT = 28, + VDD1V5_1V8_MAC_VIN = 29, + VDDA1V5_MAC_VOUT = 30, + VDDA1V8_MAC_VOUT = 31, + VCC3V3_QSFP_AB_VIN = 32, + VCC3V3_QSFP_A_VOUT1 = 33, + VCC3V3_QSFP_B_VOUT1 = 34, + VCC3V3_QSFP_CD_VIN = 35, + VCC3V3_QSFP_C_VOUT1 = 36, + VCC3V3_QSFP_D_VOUT1 = 37, + VOL_TOTAL_NUM = 38, +}; + +enum curr_index { + VCC12VIN_HOTSWAP_IIN = 0, + VCC12VIN_HOTSWAP_IOUT = 1, + VCORE_MAC_IIN = 2, + VCORE_MAC_IOUT = 3, + VDDT0V9_R_MAC_IOUT = 4, + VDD1V5_1V8_MAC_IIN = 5, + VDDA1V5_MAC_IOUT = 6, + VDDA1V8_MAC_IOUT = 7, + VCC3V3_QSFP_AB_IIN = 8, + VCC3V3_QSFP_A_IOUT = 9, + VCC3V3_QSFP_B_IOUT = 10, + VCC3V3_QSFP_CD_IIN = 11, + VCC3V3_QSFP_C_IOUT = 12, + VCC3V3_QSFP_D_IOUT = 13, + CURR_TOTAL_NUM = 14, +}; + +struct sensor_drivers_t{ + ssize_t (*get_temp_alias) (unsigned int index, char *buf); + ssize_t (*get_temp_type) (unsigned int index, char *buf); + bool (*get_temp_max) (unsigned int index, long *val); + bool (*set_temp_max) (unsigned int index, long data); + bool (*get_temp_max_hyst) (unsigned int index, long *val); + bool (*get_temp_min) (unsigned int index, long *val); + bool (*set_temp_min) (unsigned int index, long data); + bool (*get_temp_input) (unsigned int index, long *temp_input); + ssize_t (*get_vol_alias) (unsigned int index, char *buf); + ssize_t (*get_vol_type) (unsigned int index, char *buf); + bool (*get_vol_max) (unsigned int index, long *val); + bool (*set_vol_max) (unsigned int index, long data); + bool (*get_vol_min) (unsigned int index, long *val); + bool (*set_vol_min) (unsigned int index, long data); + bool (*get_vol_input) (unsigned int index, long *in_input); + bool (*get_vol_range) (unsigned int index, long *val); + bool (*get_vol_nominal) (unsigned int index, long *val); + ssize_t (*get_curr_alias) (unsigned int index, char *buf); + ssize_t (*get_curr_type) (unsigned int index, char *buf); + bool (*get_curr_max) (unsigned int index, long *val); + bool (*set_curr_max) (unsigned int index, long data); + bool (*get_curr_min) (unsigned int index, long *val); + bool (*set_curr_min) (unsigned int index, long data); + bool (*get_curr_input) (unsigned int index, long *curr_input); + void (*get_loglevel) (long *lev); + void (*set_loglevel) (long lev); + ssize_t (*debug_help) (char *buf); + ssize_t (*debug_help_hisonic) (char *buf); + ssize_t (*debug) (const char *buf, int count); +}; + +ssize_t drv_get_temp_alias(unsigned int index, char *buf); +ssize_t drv_get_temp_type(unsigned int index, char *buf); +bool drv_get_temp_max(unsigned int index, long *val); +bool drv_set_temp_max(unsigned int index, long data); +bool drv_get_temp_max_hyst(unsigned int index, long *val); +bool drv_get_temp_min(unsigned int index, long *val); +bool drv_set_temp_min(unsigned int index, long data); +bool drv_get_temp_input(unsigned int index, long *temp_input); +ssize_t drv_get_vol_alias(unsigned int index, char *buf); +ssize_t drv_get_vol_type(unsigned int index, char *buf); +bool drv_get_vol_max(unsigned int index, long *val); +bool drv_set_vol_max(unsigned int index, long data); +bool drv_get_vol_min(unsigned int index, long *val); +bool drv_set_vol_min(unsigned int index, long data); +bool drv_get_vol_input(unsigned int index, long *vol_input); +bool drv_get_vol_range(unsigned int index, long *val); +bool drv_get_vol_nominal(unsigned int index, long *val); +bool drv_get_vol_input_from_bmc(unsigned int index, long *vol_input); +ssize_t drv_get_curr_alias(unsigned int index, char *buf); +ssize_t drv_get_curr_type(unsigned int index, char *buf); +bool drv_get_curr_max(unsigned int index, long *val); +bool drv_set_curr_max(unsigned int index, long data); +bool drv_get_curr_min(unsigned int index, long *val); +bool drv_set_curr_min(unsigned int index, long data); +bool drv_get_curr_input(unsigned int index, long *curr_input); +bool drv_get_curr_input_from_bmc(unsigned int index, long *curr_input); +void drv_get_loglevel(long *lev); +void drv_set_loglevel(long lev); +ssize_t drv_debug_help(char *buf); +ssize_t drv_debug_help_hisonic(char *buf); +ssize_t drv_debug(const char *buf, int count); + +void s3ip_sensor_drivers_register(struct sensor_drivers_t *p_func); +void s3ip_sensor_drivers_unregister(void); + +#endif /* SWITCH_SENSOR_DRIVER_H */ diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_transceiver_driver.c b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_transceiver_driver.c new file mode 100644 index 0000000000..c2133b547e --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_transceiver_driver.c @@ -0,0 +1,1583 @@ +#include +#include +#include +#include +#include + +#include "switch_transceiver_driver.h" +#include "switch_optoe.h" +#include "switch_cpld_driver.h" + +#define DRVNAME "drv_transceiver_driver" +#define SWITCH_TRANSCEIVER_DRIVER_VERSION "0.0.1" + +unsigned int loglevel = 0; +static struct platform_device *drv_transceiver_device; + +struct eeprom_parse_value{ + int temperature; + int voltage; + int tx_bias[8]; + int tx_power[8]; + int rx_power[8]; +}; +int port_cpld_addr[2] = { + 0x62, + 0x64 +}; + +unsigned int drv_get_eth_value(unsigned int transceiver_index,int eth_type,unsigned int *bit_mask) +{ + unsigned int cpld_addr = 0, offset = 0, device_id = 0; + u8 value = 0; + if(transceiver_index<=16) + { + cpld_addr = port_cpld_addr[0]; + device_id = port_cpld1; + offset = eth_type+(transceiver_index-1)/8; + *bit_mask = (transceiver_index-1)%8; + } + else if(transceiver_index<=TRANSCEIVER_TOTAL_NUM) + { + cpld_addr = port_cpld_addr[1]; + device_id = port_cpld2; + offset = eth_type+(transceiver_index-17)/8; + *bit_mask = (transceiver_index-1)%8; + } + switch_i2c_cpld_read(device_id, offset, &value); + + return value; +} + +unsigned int drv_set_eth_value(unsigned int transceiver_index,int eth_type,u8 value) +{ + unsigned int cpld_addr = 0, offset = 0, bit_mask = 0, device_id = 0; + unsigned int ret = 0; + if(transceiver_index<=16) + { + cpld_addr = port_cpld_addr[0]; + device_id = port_cpld1; + offset = eth_type+(transceiver_index-1)/8; + bit_mask = (transceiver_index-1)%8; + } + else if(transceiver_index<=TRANSCEIVER_TOTAL_NUM) + { + cpld_addr = port_cpld_addr[1]; + device_id = port_cpld2; + offset = eth_type+(transceiver_index-17)/8; + bit_mask = (transceiver_index-1)%8; + } + ret = switch_i2c_cpld_write(device_id , offset, value); + + return ret; +} + +int calc_temperature(unsigned int transceiver_index, int offset) +{ + int val = 0; + unsigned char data = 0; + + optoe_bin_read_by_index(transceiver_index, &data, offset, 1); + val += (data * 100); + + optoe_bin_read_by_index(transceiver_index, &data, offset+1, 1); + val += ((data * 100)/256); + + return val; +} + +int calc_voltage(unsigned int transceiver_index, int offset) +{ + int val = 0; + unsigned char data = 0; + + optoe_bin_read_by_index(transceiver_index, &data, offset, 1); + val += (data * 256); + + optoe_bin_read_by_index(transceiver_index, &data, offset+1, 1); + val += data; + + return val; +} + +int calc_bias(unsigned int transceiver_index, int offset) +{ + int val = 0; + unsigned char data = 0; + + optoe_bin_read_by_index(transceiver_index, &data, offset, 1); + val += (data * 256); + + optoe_bin_read_by_index(transceiver_index, &data, offset+1, 1); + val += data; + + val = (val * 2); + + return val; +} + +int calc_tx_power(unsigned int transceiver_index, int offset) +{ + int val = 0; + unsigned char data = 0; + + optoe_bin_read_by_index(transceiver_index, &data, offset, 1); + val += (data * 256); + + optoe_bin_read_by_index(transceiver_index, &data, offset+1, 1); + val += data; + + return val; +} + +int calc_rx_power(unsigned int transceiver_index, int offset) +{ + int val = 0; + unsigned char data = 0; + + optoe_bin_read_by_index(transceiver_index, &data, offset, 1); + val += (data * 256); + + optoe_bin_read_by_index(transceiver_index, &data, offset+1, 1); + val += data; + + return val; +} + +/*parse power temperature voltage tx_bias*/ +static ssize_t sfp_eeprom_parse(unsigned int transceiver_index, struct eeprom_parse_value *parse_value) +{ + int i; + + parse_value->temperature = calc_temperature(transceiver_index, SFP_TEMPERATURE_OFFSET); + parse_value->voltage = calc_voltage(transceiver_index, SFP_VOLTAGE_OFFSET); + + for(i = 0;i < 1;i++){ + parse_value->tx_bias[i] = calc_bias(transceiver_index, SFP_BIAS_OFFSET + (i * 2)); + } + for(i = 0;i < 1;i++){ + parse_value->tx_power[i] = calc_tx_power(transceiver_index, SFP_TX_POWER_OFFSET + (i * 2)); + } + for(i = 0;i < 1;i++){ + parse_value->rx_power[i] = calc_rx_power(transceiver_index, SFP_RX_POWER_OFFSET + (i * 2)); + } + + return 0; +} + +static ssize_t qsfp_eeprom_parse(unsigned int transceiver_index, struct eeprom_parse_value *parse_value) +{ + int i; + + parse_value->temperature = calc_temperature(transceiver_index, QSFP_TEMPERATURE_OFFSET); + parse_value->voltage = calc_voltage(transceiver_index, QSFP_VOLTAGE_OFFSET); + + for(i = 0;i < 4;i++){ + parse_value->tx_bias[i] = calc_bias(transceiver_index, QSFP_BIAS_OFFSET + (i * 2)); + } + for(i = 0;i < 4;i++){ + parse_value->tx_power[i] = calc_tx_power(transceiver_index, QSFP_TX_POWER_OFFSET + (i * 2)); + } + for(i = 0;i < 4;i++){ + parse_value->rx_power[i] = calc_rx_power(transceiver_index, QSFP_RX_POWER_OFFSET + (i * 2)); + } + + return 0; +} + +static ssize_t qsfp_dd_eeprom_parse(unsigned int transceiver_index, struct eeprom_parse_value *parse_value) +{ + int i; + + parse_value->temperature = calc_temperature(transceiver_index, QSFP_DD_TEMPERATURE_OFFSET); + parse_value->voltage = calc_voltage(transceiver_index, QSFP_DD_VOLTAGE_OFFSET); + + for(i = 0;i < 8;i++){ + parse_value->tx_bias[i] = calc_bias(transceiver_index, QSFP_DD_BIAS_OFFSET + (i * 2)); + } + for(i = 0;i < 8;i++){ + parse_value->tx_power[i] = calc_tx_power(transceiver_index, QSFP_DD_TX_POWER_OFFSET + (i * 2)); + } + for(i = 0;i < 8;i++){ + parse_value->rx_power[i] = calc_rx_power(transceiver_index, QSFP_DD_RX_POWER_OFFSET + (i * 2)); + } + + return 0; +} +#if 0 +static ssize_t qsfp_detect_power_class(unsigned int transceiver_index, unsigned int *power_class) +{ + int ret = 0; + unsigned char data = 0; + + ret = optoe_bin_read_by_index(transceiver_index, &data, QSFP_EXT_IDENTIFIER_OFFSET, 1); + + if(ret < 0) + { + return ret; + } + + if(data & QSFP_EXT_IDENTIFIER_POWER_CLASS_8_BITMAP) + { + *power_class = 8; + } + else if((data & QSFP_EXT_IDENTIFIER_POWER_CLASS_1_4_BITMAP) != QSFP_EXT_IDENTIFIER_POWER_CLASS_1_4_BITMAP) + { + *power_class = (((data & QSFP_EXT_IDENTIFIER_POWER_CLASS_1_4_BITMAP) >> 6) + 1); + } + else + { + *power_class = ((data & QSFP_EXT_IDENTIFIER_POWER_CLASS_5_7_BITMAP) + 4); + } + + return 0; +} + +static ssize_t qsfp_enable_power_class(unsigned int transceiver_index, unsigned int power_class) +{ + int ret = 0; + unsigned char power_control = 0; + + ret = optoe_bin_read_by_index(transceiver_index, &power_control, QSFP_POWER_CONTROL_OFFSET, 1); + if(ret < 0) + return ret; + + switch(power_class) + { + case 1: + case 2: + case 3: + case 4: + power_control &= ~(QSFP_POWER_CONTROL_ENABLE_CLASS_5_7_BIT | QSFP_POWER_CONTROL_ENABLE_CLASS_8_BIT); + break; + case 5: + case 6: + case 7: + power_control |= (QSFP_POWER_CONTROL_ENABLE_CLASS_5_7_BIT); + power_control &= ~(QSFP_POWER_CONTROL_ENABLE_CLASS_8_BIT); + break; + case 8: + power_control |= (QSFP_POWER_CONTROL_ENABLE_CLASS_5_7_BIT | QSFP_POWER_CONTROL_ENABLE_CLASS_8_BIT); + break; + default: + break; + }; + + ret = optoe_bin_write_by_index(transceiver_index, &power_control, QSFP_POWER_CONTROL_OFFSET, 1); + if(ret < 0) + return ret; + + return 0; +} +#endif +unsigned int drv_get_eth_diagnostic(unsigned int transceiver_index, char *buf) +{ + int i,num; + ssize_t ret; + struct eeprom_parse_value parse_value; + unsigned char identifier = 0; + int rv = 0; + + /* Detect the transceiver type */ + rv = optoe_bin_read_by_index(transceiver_index, &identifier, TRANSCEIVER_IDENTIIFIER_OFFSET, 1); + if(rv < 0) + return rv; + + if(((identifier == 0x0) || (identifier >= 0x1f)) && (transceiver_index != 56)) + { + /* If identifier is 0, check the extended identifier */ + rv = optoe_bin_read_by_index(transceiver_index, &identifier, TRANSCEIVER_EXTENDED_IDENTIIFIER_OFFSET, 1); + if(rv < 0) + return rv; + } + + switch(identifier) + { + case 0x00: + case 0x03: /* SFP/SFP+/SFP28 */ + sfp_eeprom_parse(transceiver_index, &parse_value); + num = 1; + break; + + case 0x0d: /* QSFP+ or later with SFF8636 or SFF8436 mgmt interface */ + case 0x11: /* QSFP28 or later with SFF8636 mgmt interface */ + qsfp_eeprom_parse(transceiver_index, &parse_value); + num = 4; + break; + + case 0x18: /* QSFP-DD */ + case 0x1e: /* QSFP+ or later with CMIS */ + qsfp_dd_eeprom_parse(transceiver_index, &parse_value); + num = 8; + break; + + default: + return -EPERM; + } + + if(num){ +#ifdef C11_ANNEX_K + ret = sprintf_s(buf, 4096, "temperature :%s%3d.%-2d C \n",((parse_value.temperature < 0) ? "-":""),abs(parse_value.temperature/100),abs(parse_value.temperature%100)); +#else + ret = sprintf(buf, "temperature :%s%3d.%-2d C \n",((parse_value.temperature < 0) ? "-":""),abs(parse_value.temperature/100),abs(parse_value.temperature%100)); +#endif +#ifdef C11_ANNEX_K + //printk(KERN_DEBUG "voltage:%d",parse_value.voltage); + ret = sprintf_s(buf, 4096, "%svoltage :%3d.%-2d V \n",buf,parse_value.voltage/10000,(parse_value.voltage/10)%1000); +#else + ret = sprintf(buf, "%svoltage :%3d.%-2d V \n",buf,parse_value.voltage/10000,(parse_value.voltage/10)%1000); +#endif + for(i = 0;i < num;i++){ +#ifdef C11_ANNEX_K + ret = sprintf_s(buf, 4096, "%stxbias%d :%3d.%-2d mA \n",buf,i+1,parse_value.tx_bias[i]/1000,parse_value.tx_bias[i]%1000); +#else + ret = sprintf(buf, "%stxbias%d :%3d.%-2d mA \n",buf,i+1,parse_value.tx_bias[i]/1000,parse_value.tx_bias[i]%1000); +#endif + } + for(i = 0;i < num;i++){ +#ifdef C11_ANNEX_K + ret = sprintf_s(buf, 4096, "%stx%d_power :%3d.%-2d mW \n",buf,i+1,parse_value.tx_power[i]/10000,parse_value.tx_power[i]%10000); +#else + ret = sprintf(buf, "%stx%d_power :%3d.%-2d mW \n",buf,i+1,parse_value.tx_power[i]/10000,parse_value.tx_power[i]%10000); +#endif + } + for(i = 0;i < num;i++){ +#ifdef C11_ANNEX_K + ret = sprintf_s(buf, 4096, "%srx%d_power :%3d.%-2d mW \n",buf,i+1,parse_value.rx_power[i]/10000,parse_value.rx_power[i]%10000); +#else + ret = sprintf(buf, "%srx%d_power :%3d.%-2d mW \n",buf,i+1,parse_value.rx_power[i]/10000,parse_value.rx_power[i]%10000); +#endif + } + + return ret; + + } +#ifdef C11_ANNEX_K + ret = sprintf_s(buf, 4096, "N/A\n"); +#else + ret = sprintf(buf, "N/A\n"); +#endif + return ret; +} + +EXPORT_SYMBOL_GPL(drv_get_eth_diagnostic); + +unsigned int drv_get_eth_power_on (unsigned int transceiver_index) +{ + unsigned int power_on_value = 0,bit_mask = 0; + + power_on_value = drv_get_eth_value(transceiver_index,QSFP_POWER_ON1,&bit_mask); + if(power_on_value < 0){ + return -1; + } + power_on_value = (power_on_value >> bit_mask) & 1; + //reg present bit in reg: 1: enable, 0: disable. + //return 1: present, 0: not present + return (power_on_value == 0) ? 1 : 0; +} +unsigned int drv_set_eth_power_on(unsigned int transceiver_index, unsigned int pwr_value) +{ + int tmp_value = 0,ret = 0; + unsigned int bit_mask = 0; + + tmp_value = drv_get_eth_value(transceiver_index,QSFP_POWER_ON1,&bit_mask); + if(tmp_value < 0){ + return -1; + } + tmp_value = (tmp_value & ~(1 << bit_mask)); + /*set register*/ + tmp_value = tmp_value | (!pwr_value << bit_mask); + ret = drv_set_eth_value(transceiver_index,QSFP_POWER_ON1,tmp_value); + + return ret; + +} + +unsigned int drv_get_eth_reset(unsigned int transceiver_index) +{ + int rst_value = 0; + unsigned int bit_mask = 0; + + rst_value = drv_get_eth_value(transceiver_index,QSFP_RST1,&bit_mask); + if(rst_value < 0){ + return -1; + } + rst_value = (rst_value >> bit_mask) & 1; + /*qsfp 0 set reset*/ + + return (rst_value == 0) ? 1 : 0; +} + +unsigned int drv_set_eth_reset(unsigned int transceiver_index, unsigned int rst_value) +{ + int tmp_value = 0,ret = 0; + unsigned int bit_mask = 0; + + rst_value = rst_value == 0 ? 1 : 0; + tmp_value = drv_get_eth_value(transceiver_index,QSFP_RST1,&bit_mask); + if(tmp_value < 0){ + return -1; + } + tmp_value = (tmp_value & ~(1 << bit_mask)); + /*set register*/ + tmp_value = tmp_value | (rst_value << bit_mask); + ret = drv_set_eth_value(transceiver_index,QSFP_RST1,tmp_value); + + return ret; +} + +int drv_get_eth_lpmode(unsigned int transceiver_index, unsigned int *lpmode_value) +{ + + unsigned char identifier = 0; + unsigned char power_control = 0; + unsigned int lp_mode = 0; + unsigned int bit_mask = 0; + int tmp_value = 0; + int rv = 0; + + tmp_value = drv_get_eth_value(transceiver_index,QSFP_LPWN1,&bit_mask); + if(tmp_value < 0){ + return tmp_value; + } + tmp_value = (tmp_value >> bit_mask) & 1; + *lpmode_value = tmp_value ? 1:0; + return rv; + + /* Detect the transceiver type */ + rv = optoe_bin_read_by_index(transceiver_index, &identifier, TRANSCEIVER_IDENTIIFIER_OFFSET, 1); + if(rv < 0) + return rv; + + if((identifier == 0x0) || (identifier >= 0x1f)) + { + /* If identifier is 0, check the extended identifier */ + rv = optoe_bin_read_by_index(transceiver_index, &identifier, TRANSCEIVER_EXTENDED_IDENTIIFIER_OFFSET, 1); + if(rv < 0) + return rv; + } + + switch(identifier) + { + case 0x0d: /* QSFP+ or later with SFF8636 or SFF8436 mgmt interface */ + case 0x11: /* QSFP28 or later with SFF8636 mgmt interface */ + rv = optoe_bin_read_by_index(transceiver_index, &power_control, QSFP_POWER_CONTROL_OFFSET, 1); + if(rv < 0) + return rv; + + if(power_control & QSFP_POWER_CONTROL_POWER_OVERRIDE_BIT) + { + /* The LP mode is depending on the Power Set value when the override is true. */ + lp_mode = (power_control & QSFP_POWER_CONTROL_POWER_SET_BIT) ? 1 : 0; + } + else + { + /* The LP mode is on by default */ + lp_mode = 1; + } + break; + case 0x18: /* QSFP-DD */ + case 0x1b: /* DSFP+ or later with CMIS */ + case 0x1e: /* QSFP+ or later with CMIS */ + rv = optoe_bin_read_by_index(transceiver_index, &power_control, QSFP_DD_MODULE_GLOBAL_CONTROL_OFFSET, 1); + if(rv < 0) + return rv; + + if(power_control & QSFP_DD_MODULE_GLOBAL_CONTROL_FORCE_LOW_POWER_BIT) + { + /* Force low power is enabled */ + lp_mode = 1; + } + else + { + /* The LP mode is depending on the Low Power value when the Force Low Power is disabled. */ + lp_mode = (power_control & QSFP_DD_MODULE_GLOBAL_CONTROL_LOW_POWER_BIT) ? 1 : 0; + } + break; + default: + /* Fail to recognize the transceiver type, regard it as default low power. */ + lp_mode = 1; + break; + } + + *lpmode_value = (lp_mode ? 1 : 0); + return rv; +} +EXPORT_SYMBOL_GPL(drv_get_eth_lpmode); + +int drv_set_eth_lpmode(unsigned int transceiver_index, unsigned int lpmode_value) +{ + unsigned char identifier = 0; + unsigned char power_control = 0; + int tmp_value = 0; + int rv = 0; + unsigned int bit_mask = 0; + + + tmp_value = drv_get_eth_value(transceiver_index,QSFP_LPWN1,&bit_mask); + tmp_value = (tmp_value & ~(1 << bit_mask)); + /*set register*/ + tmp_value = tmp_value | (lpmode_value << bit_mask); + rv = drv_set_eth_value(transceiver_index,QSFP_LPWN1,tmp_value); + + return rv; + + /* Detect the transceiver type */ + rv = optoe_bin_read_by_index(transceiver_index, &identifier, TRANSCEIVER_IDENTIIFIER_OFFSET, 1); + if(rv < 0) + return rv; + + if((identifier == 0x0) || (identifier >= 0x1f)) + { + /* If identifier is 0, check the extended identifier */ + rv = optoe_bin_read_by_index(transceiver_index, &identifier, TRANSCEIVER_EXTENDED_IDENTIIFIER_OFFSET, 1); + if(rv < 0) + return rv; + } + + switch(identifier) + { + case 0x0d: /* QSFP+ or later with SFF8636 or SFF8436 mgmt interface */ + case 0x11: /* QSFP28 or later with SFF8636 mgmt interface */ + rv = optoe_bin_read_by_index(transceiver_index, &power_control, QSFP_POWER_CONTROL_OFFSET, 1); + if(rv < 0) + return rv; + + if(!(power_control & QSFP_POWER_CONTROL_POWER_OVERRIDE_BIT)) + { + /* Setup the Power Override bit */ + power_control |= QSFP_POWER_CONTROL_POWER_OVERRIDE_BIT; + } + + if(lpmode_value) + { + /* Setup the Power Set bit for the Low power mode */ + if(!(power_control & QSFP_POWER_CONTROL_POWER_SET_BIT)) + { + power_control |= QSFP_POWER_CONTROL_POWER_SET_BIT; + } + } + else + { + /* Clear the Power Set bit for the High power mode */ + if(power_control & QSFP_POWER_CONTROL_POWER_SET_BIT) + { + power_control &= ~(QSFP_POWER_CONTROL_POWER_SET_BIT); + } + } + rv = optoe_bin_write_by_index(transceiver_index, &power_control, QSFP_POWER_CONTROL_OFFSET, 1); + if(rv < 0) + return rv; + + break; + + case 0x18: /* QSFP-DD */ + case 0x1b: /* DSFP+ or later with CMIS */ + case 0x1e: /* QSFP+ or later with CMIS */ + rv = optoe_bin_read_by_index(transceiver_index, &power_control, QSFP_DD_MODULE_GLOBAL_CONTROL_OFFSET, 1); + printk("port%d value 0x%x\n",transceiver_index,power_control); + if(rv < 0) + return rv; + + if(power_control & QSFP_DD_MODULE_GLOBAL_CONTROL_FORCE_LOW_POWER_BIT) + { + /* Clear the Force Low Power bit */ + power_control &= ~QSFP_DD_MODULE_GLOBAL_CONTROL_FORCE_LOW_POWER_BIT; + } + + if(lpmode_value) + { + /* Setup the Low Power bit for the Low power mode */ + if(!(power_control & QSFP_DD_MODULE_GLOBAL_CONTROL_LOW_POWER_BIT)) + { + power_control |= QSFP_DD_MODULE_GLOBAL_CONTROL_LOW_POWER_BIT; + } + } + else + { + /* Clear the Low Power bit for the High power mode */ + if(power_control & QSFP_DD_MODULE_GLOBAL_CONTROL_LOW_POWER_BIT) + { + power_control &= ~(QSFP_DD_MODULE_GLOBAL_CONTROL_LOW_POWER_BIT); + } + } + rv = optoe_bin_write_by_index(transceiver_index, &power_control, QSFP_DD_MODULE_GLOBAL_CONTROL_OFFSET, 1); + if(rv < 0) + return rv; + + break; + + default: + break; + } + + return rv; +} +EXPORT_SYMBOL_GPL(drv_set_eth_lpmode); + +unsigned int drv_get_eth_present(unsigned int transceiver_index) +{ + unsigned int present_value = 0; + unsigned int bit_mask = 0; + present_value = drv_get_eth_value(transceiver_index,QSFP_PRESENT1,&bit_mask); + if(present_value < 0){ + return -1; + } + present_value = (present_value >> bit_mask) & 1; + //reg present bit in reg: 1: not present, 0: present. + //return 1: present, 0: not present + return (present_value == 0) ? 1 : 0; +} + +unsigned int drv_get_eth_present_history(unsigned int transceiver_index) +{ + return 0; +} + +unsigned int drv_get_eth_led_status(unsigned int transceiver_index) +{ + u8 led_value = 0; + unsigned int cpld_addr = 0, offset = 0, device_id = 0; + if(transceiver_index<=16) + { + cpld_addr = port_cpld_addr[0]; + device_id = port_cpld1; + offset = QSFP_LED+(transceiver_index-1); + } + else if(transceiver_index<=TRANSCEIVER_TOTAL_NUM) + { + cpld_addr = port_cpld_addr[1]; + device_id = port_cpld2; + offset = QSFP_LED+(transceiver_index-17); + } + switch_i2c_cpld_read(device_id, offset, &led_value); + + if(led_value < 0){ + return -1; + } + + return led_value; +} + +unsigned int drv_set_eth_led_status(unsigned int transceiver_index, unsigned int led_value) +{ + unsigned int cpld_addr = 0, offset = 0,device_id = 0; + unsigned int ret = 0; + if(transceiver_index<=16) + { + cpld_addr = port_cpld_addr[0]; + device_id = port_cpld1; + offset = QSFP_LED+(transceiver_index-1); + } + else if(transceiver_index<=TRANSCEIVER_TOTAL_NUM) + { + cpld_addr = port_cpld_addr[1]; + device_id = port_cpld2; + offset = QSFP_LED+(transceiver_index-17); + } + ret = switch_i2c_cpld_write(device_id , offset, led_value); + + return ret; +} + +unsigned int drv_get_eth_interrupt(unsigned int transceiver_index) +{ + unsigned int interrupt_value = 0; + unsigned int bit_mask = 0; + + interrupt_value = drv_get_eth_value(transceiver_index,QSFP_INTR1,&bit_mask); + if(interrupt_value < 0){ + return -1; + } + interrupt_value = (interrupt_value >> bit_mask) & 1; + //reg interrupt bit in reg: 1: block, 0: pass. + //return 1:pass , 0: block + return (interrupt_value == 0) ? 1 : 0; +} + +int drv_get_eth_eeprom(unsigned int transceiver_index, char *buf, loff_t off, size_t len) +{ + return optoe_bin_read_by_index(transceiver_index, buf, off, len); +} + +int drv_set_eth_eeprom(unsigned int transceiver_index, char *buf, loff_t off, size_t len) +{ + return optoe_bin_write_by_index(transceiver_index, buf, off, len); +} + +int drv_get_eth_temp(unsigned int transceiver_index, long long *temp) +{ + unsigned char identifier = 0; + unsigned char temp_buffer[2]; + unsigned char ddm_type = 0; + short temperature = 0; + unsigned int offset = 0; + int rv = 0; + + /* Detect the transceiver type */ + rv = optoe_bin_read_by_index(transceiver_index, &identifier, TRANSCEIVER_IDENTIIFIER_OFFSET, 1); + if(rv < 0) + return rv; + + if(((identifier == 0x0) || (identifier >= 0x1f)) && (transceiver_index != 33)) + { + /* If identifier is 0, check the extended identifier */ + rv = optoe_bin_read_by_index(transceiver_index, &identifier, TRANSCEIVER_EXTENDED_IDENTIIFIER_OFFSET, 1); + if(rv < 0) + return rv; + } + + switch(identifier) + { + case 0x00: + case 0x03: /* SFP/SFP+/SFP28 */ + offset = SFP_TEMPERATURE_OFFSET; + break; + + case 0x0d: /* QSFP+ or later with SFF8636 or SFF8436 mgmt interface */ + case 0x11: /* QSFP28 or later with SFF8636 mgmt interface */ + offset = QSFP_TEMPERATURE_OFFSET; + break; + + case 0x18: /* QSFP-DD */ + case 0x1e: /* QSFP+ or later with CMIS */ + offset = QSFP_DD_TEMPERATURE_OFFSET; + break; + + default: + return -EPERM; + } + + if((transceiver_index == 33) && (offset == SFP_TEMPERATURE_OFFSET)) + { + rv = optoe_bin_read_by_index(transceiver_index, &ddm_type, SFP_DDM_TYPE_OFFSET, 1); + if(rv < 0) + return rv; + + if(!(ddm_type & SFP_DDM_TYPE_MONITOR_IMPLEMENTED_OFFSET)) + { + /* Return -1 if the SFP does not support DDM */ + return -1; + } + } + + rv = optoe_bin_read_by_index(transceiver_index, temp_buffer, offset, 2); + temperature = (temp_buffer[1] + (temp_buffer[0] << 8)); + /* 16-bit signed twos complement value in increments of 1/256 (0.00390625) degrees Celsius */ + *temp = (((long long)temperature >> 8) * 100000000) + ((long long)(temperature & 0xFF) * 390625); + return rv; +} + +int drv_get_eth_rx_los(unsigned int transceiver_index, unsigned char *rx_los) +{ + unsigned char identifier = 0; + unsigned char buffer; + unsigned int offset = 0; + unsigned char lane_num = 0; + unsigned int mask = 0; + unsigned int bit_offset = 0; + int rv = 0; + + /* Detect the transceiver type */ + rv = optoe_bin_read_by_index(transceiver_index, &identifier, TRANSCEIVER_IDENTIIFIER_OFFSET, 1); + if(rv < 0) + return rv; + + if((identifier == 0x0) || (identifier >= 0x1f)) + { + /* If identifier is 0, check the extended identifier */ + rv = optoe_bin_read_by_index(transceiver_index, &identifier, TRANSCEIVER_EXTENDED_IDENTIIFIER_OFFSET, 1); + if(rv < 0) + return rv; + } + + switch(identifier) + { + case 0x00: + case 0x03: /* SFP/SFP+/SFP28 */ + offset = SFP_RX_LOS_OFFSET; + mask = SFP_RX_LOS_MASK; + bit_offset = SFP_RX_LOS_BIT_OFFSET; + lane_num = SFP_LANE_NUM; + break; + + case 0x0d: /* QSFP+ or later with SFF8636 or SFF8436 mgmt interface */ + case 0x11: /* QSFP28 or later with SFF8636 mgmt interface */ + offset = QSFP_RX_LOS_OFFSET; + mask = QSFP_RX_LOS_MASK; + bit_offset = QSFP_RX_LOS_BIT_OFFSET; + lane_num = QSFP_LANE_NUM; + break; + + case 0x18: /* QSFP-DD */ + case 0x1e: /* QSFP+ or later with CMIS */ + offset = QSFP_DD_RX_LOS_OFFSET; + mask = QSFP_DD_RX_LOS_MASK; + bit_offset = QSFP_DD_RX_LOS_BIT_OFFSET; + rv = optoe_bin_read_by_index(transceiver_index, &buffer, QSFP_DD_LANE_NUM_OFFSET, 1); + if(rv < 0) + return rv; + lane_num = (buffer >> QSFP_DD_LANE_NUM_MEDIALANE_BIT_OFFSET) & QSFP_DD_LANE_NUM_MEDIALANE_MASK; + break; + + default: + return -EPERM; + } + + rv = optoe_bin_read_by_index(transceiver_index, &buffer, offset, 1); + rx_los[0] = lane_num; + rx_los[1] = (buffer >> bit_offset) & mask; + + return rv; +} + +int drv_get_eth_tx_los(unsigned int transceiver_index, unsigned char *tx_los) +{ + unsigned char identifier = 0; + unsigned char buffer; + unsigned int offset = 0; + unsigned char lane_num = 0; + unsigned int mask = 0; + unsigned int bit_offset = 0; + int rv = 0; + + /* Detect the transceiver type */ + rv = optoe_bin_read_by_index(transceiver_index, &identifier, TRANSCEIVER_IDENTIIFIER_OFFSET, 1); + if(rv < 0) + return rv; + + if((identifier == 0x0) || (identifier >= 0x1f)) + { + /* If identifier is 0, check the extended identifier */ + rv = optoe_bin_read_by_index(transceiver_index, &identifier, TRANSCEIVER_EXTENDED_IDENTIIFIER_OFFSET, 1); + if(rv < 0) + return rv; + } + + switch(identifier) + { + case 0x00: + case 0x03: /* SFP/SFP+/SFP28 */ + return -1; + + case 0x0d: /* QSFP+ or later with SFF8636 or SFF8436 mgmt interface */ + case 0x11: /* QSFP28 or later with SFF8636 mgmt interface */ + offset = QSFP_TX_LOS_OFFSET; + mask = QSFP_TX_LOS_MASK; + bit_offset = QSFP_TX_LOS_BIT_OFFSET; + lane_num = QSFP_LANE_NUM; + break; + + case 0x18: /* QSFP-DD */ + case 0x1e: /* QSFP+ or later with CMIS */ + offset = QSFP_DD_TX_LOS_OFFSET; + mask = QSFP_DD_TX_LOS_MASK; + bit_offset = QSFP_DD_TX_LOS_BIT_OFFSET; + rv = optoe_bin_read_by_index(transceiver_index, &buffer, QSFP_DD_LANE_NUM_OFFSET, 1); + if(rv < 0) + return rv; + lane_num = (buffer >> QSFP_DD_LANE_NUM_MEDIALANE_BIT_OFFSET) & QSFP_DD_LANE_NUM_MEDIALANE_MASK; + break; + + default: + return -EPERM; + } + + rv = optoe_bin_read_by_index(transceiver_index, &buffer, offset, 1); + tx_los[0] = lane_num; + tx_los[1] = (buffer >> bit_offset) & mask; + + return rv; +} + +int drv_get_eth_tx_disable(unsigned int transceiver_index, unsigned char *tx_disable) +{ + unsigned char identifier = 0; + unsigned char buffer; + unsigned int offset = 0; + unsigned char lane_num = 0; + unsigned int mask = 0; + unsigned int bit_offset = 0; + int rv = 0; + + /* Detect the transceiver type */ + rv = optoe_bin_read_by_index(transceiver_index, &identifier, TRANSCEIVER_IDENTIIFIER_OFFSET, 1); + if(rv < 0) + return rv; + + if((identifier == 0x0) || (identifier >= 0x1f)) + { + /* If identifier is 0, check the extended identifier */ + rv = optoe_bin_read_by_index(transceiver_index, &identifier, TRANSCEIVER_EXTENDED_IDENTIIFIER_OFFSET, 1); + if(rv < 0) + return rv; + } + + switch(identifier) + { + case 0x00: + case 0x03: /* SFP/SFP+/SFP28 */ + offset = SFP_TX_DISABLE_OFFSET; + mask = SFP_TX_DISABLE_MASK; + bit_offset = SFP_TX_DISABLE_BIT_OFFSET; + lane_num = SFP_LANE_NUM; + break; + + case 0x0d: /* QSFP+ or later with SFF8636 or SFF8436 mgmt interface */ + case 0x11: /* QSFP28 or later with SFF8636 mgmt interface */ + offset = QSFP_TX_DISABLE_OFFSET; + mask = QSFP_TX_DISABLE_MASK; + bit_offset = QSFP_TX_DISABLE_BIT_OFFSET; + lane_num = QSFP_LANE_NUM; + break; + + case 0x18: /* QSFP-DD */ + case 0x1e: /* QSFP+ or later with CMIS */ + offset = QSFP_DD_TX_DISABLE_OFFSET; + mask = QSFP_DD_TX_DISABLE_MASK; + bit_offset = QSFP_DD_TX_DISABLE_BIT_OFFSET; + rv = optoe_bin_read_by_index(transceiver_index, &buffer, QSFP_DD_LANE_NUM_OFFSET, 1); + if(rv < 0) + return rv; + lane_num = (buffer >> QSFP_DD_LANE_NUM_MEDIALANE_BIT_OFFSET) & QSFP_DD_LANE_NUM_MEDIALANE_MASK; + break; + + default: + return -EPERM; + } + + rv = optoe_bin_read_by_index(transceiver_index, &buffer, offset, 1); + tx_disable[0] = lane_num; + tx_disable[1] = (buffer >> bit_offset) & mask; + + return rv; +} + +int drv_set_eth_tx_disable(unsigned int transceiver_index, unsigned int tx_disable) +{ + unsigned char identifier = 0; + unsigned char temp_data = 0; + unsigned char bitmap = 0; + unsigned int offset = 0; + int rv = 0; + + /* Detect the transceiver type */ + rv = optoe_bin_read_by_index(transceiver_index, &identifier, TRANSCEIVER_IDENTIIFIER_OFFSET, 1); + if(rv < 0) + return rv; + + if((identifier == 0x0) || (identifier >= 0x1f)) + { + /* If identifier is 0, check the extended identifier */ + rv = optoe_bin_read_by_index(transceiver_index, &identifier, TRANSCEIVER_EXTENDED_IDENTIIFIER_OFFSET, 1); + if(rv < 0) + return rv; + } + + switch(identifier) + { + case 0x00: + case 0x03: /* SFP/SFP+/SFP28 */ + offset = SFP_TX_DISABLE_OFFSET; + bitmap = SFP_SOFT_TX_DISABLE_BITMAP; + break; + + case 0x0d: /* QSFP+ or later with SFF8636 or SFF8436 mgmt interface */ + case 0x11: /* QSFP28 or later with SFF8636 mgmt interface */ + offset = QSFP_TX_DISABLE_OFFSET; + bitmap = QSFP_TX_DISABLE_BITMAP; + break; + + case 0x18: /* QSFP-DD */ + case 0x1e: /* QSFP+ or later with CMIS */ + offset = QSFP_DD_TX_DISABLE_OFFSET; + bitmap = QSFP_DD_TX_DISABLE_BITMAP; + break; + + default: + return -EPERM; + } + + rv = optoe_bin_read_by_index(transceiver_index, &temp_data, offset, 1); + if(rv < 0) + return rv; + + temp_data &= ~bitmap; + temp_data |= (bitmap & tx_disable); + rv = optoe_bin_write_by_index(transceiver_index, &temp_data, offset, 1); + if(rv < 0) + return rv; + + return rv; +} + +int drv_get_eth_rx_disable(unsigned int transceiver_index, unsigned char *rx_disable) +{ + unsigned char identifier = 0; + unsigned char buffer; + unsigned int offset = 0; + unsigned char lane_num = 0; + unsigned int mask = 0; + unsigned int bit_offset = 0; + int rv = 0; + + /* Detect the transceiver type */ + rv = optoe_bin_read_by_index(transceiver_index, &identifier, TRANSCEIVER_IDENTIIFIER_OFFSET, 1); + if(rv < 0) + return rv; + + if((identifier == 0x0) || (identifier >= 0x1f)) + { + /* If identifier is 0, check the extended identifier */ + rv = optoe_bin_read_by_index(transceiver_index, &identifier, TRANSCEIVER_EXTENDED_IDENTIIFIER_OFFSET, 1); + if(rv < 0) + return rv; + } + + switch(identifier) + { + case 0x00: + case 0x03: /* SFP/SFP+/SFP28 */ + return -1; + + case 0x0d: /* QSFP+ or later with SFF8636 or SFF8436 mgmt interface */ + case 0x11: /* QSFP28 or later with SFF8636 mgmt interface */ + offset = QSFP_RX_DISABLE_OFFSET; + mask = QSFP_RX_DISABLE_MASK; + bit_offset = QSFP_RX_DISABLE_BIT_OFFSET; + lane_num = QSFP_LANE_NUM; + break; + + case 0x18: /* QSFP-DD */ + case 0x1e: /* QSFP+ or later with CMIS */ + offset = QSFP_DD_RX_DISABLE_OFFSET; + mask = QSFP_DD_RX_DISABLE_MASK; + bit_offset = QSFP_DD_RX_DISABLE_BIT_OFFSET; + rv = optoe_bin_read_by_index(transceiver_index, &buffer, QSFP_DD_LANE_NUM_OFFSET, 1); + if(rv < 0) + return rv; + lane_num = (buffer >> QSFP_DD_LANE_NUM_MEDIALANE_BIT_OFFSET) & QSFP_DD_LANE_NUM_MEDIALANE_MASK; + break; + + default: + return -EPERM; + } + + rv = optoe_bin_read_by_index(transceiver_index, &buffer, offset, 1); + rx_disable[0] = lane_num; + rx_disable[1] = (buffer >> bit_offset) & mask; + + return rv; +} + +int drv_get_eth_tx_cdr_lol(unsigned int transceiver_index, unsigned char *tx_cdr_lol) +{ + unsigned char identifier = 0; + unsigned char buffer; + unsigned int offset = 0; + unsigned char lane_num = 0; + unsigned int mask = 0; + unsigned int bit_offset = 0; + int rv = 0; + + /* Detect the transceiver type */ + rv = optoe_bin_read_by_index(transceiver_index, &identifier, TRANSCEIVER_IDENTIIFIER_OFFSET, 1); + if(rv < 0) + return rv; + + if((identifier == 0x0) || (identifier >= 0x1f)) + { + /* If identifier is 0, check the extended identifier */ + rv = optoe_bin_read_by_index(transceiver_index, &identifier, TRANSCEIVER_EXTENDED_IDENTIIFIER_OFFSET, 1); + if(rv < 0) + return rv; + } + + switch(identifier) + { + case 0x00: + case 0x03: /* SFP/SFP+/SFP28 */ + offset = SFP_TX_CDR_LOL_OFFSET; + mask = SFP_TX_CDR_LOL_MASK; + bit_offset = SFP_TX_CDR_LOL_BIT_OFFSET; + lane_num = SFP_LANE_NUM; + break; + + case 0x0d: /* QSFP+ or later with SFF8636 or SFF8436 mgmt interface */ + case 0x11: /* QSFP28 or later with SFF8636 mgmt interface */ + offset = QSFP_TX_CDR_LOL_OFFSET; + mask = QSFP_TX_CDR_LOL_MASK; + bit_offset = QSFP_TX_CDR_LOL_BIT_OFFSET; + lane_num = QSFP_LANE_NUM; + break; + + case 0x18: /* QSFP-DD */ + case 0x1e: /* QSFP+ or later with CMIS */ + offset = QSFP_DD_TX_CDR_LOL_OFFSET; + mask = QSFP_DD_TX_CDR_LOL_MASK; + bit_offset = QSFP_DD_TX_CDR_LOL_BIT_OFFSET; + rv = optoe_bin_read_by_index(transceiver_index, &buffer, QSFP_DD_LANE_NUM_OFFSET, 1); + if(rv < 0) + return rv; + lane_num = (buffer >> QSFP_DD_LANE_NUM_MEDIALANE_BIT_OFFSET) & QSFP_DD_LANE_NUM_MEDIALANE_MASK; + break; + + default: + return -EPERM; + } + + rv = optoe_bin_read_by_index(transceiver_index, &buffer, offset, 1); + tx_cdr_lol[0] = lane_num; + tx_cdr_lol[1] = (buffer >> bit_offset) & mask; + + return rv; +} + +int drv_get_eth_rx_cdr_lol(unsigned int transceiver_index, unsigned char *rx_cdr_lol) +{ + unsigned char identifier = 0; + unsigned char buffer; + unsigned int offset = 0; + unsigned char lane_num = 0; + unsigned int mask = 0; + unsigned int bit_offset = 0; + int rv = 0; + + /* Detect the transceiver type */ + rv = optoe_bin_read_by_index(transceiver_index, &identifier, TRANSCEIVER_IDENTIIFIER_OFFSET, 1); + if(rv < 0) + return rv; + + if((identifier == 0x0) || (identifier >= 0x1f)) + { + /* If identifier is 0, check the extended identifier */ + rv = optoe_bin_read_by_index(transceiver_index, &identifier, TRANSCEIVER_EXTENDED_IDENTIIFIER_OFFSET, 1); + if(rv < 0) + return rv; + } + + switch(identifier) + { + case 0x00: + case 0x03: /* SFP/SFP+/SFP28 */ + offset = SFP_RX_CDR_LOL_OFFSET; + mask = SFP_RX_CDR_LOL_MASK; + bit_offset = SFP_RX_CDR_LOL_BIT_OFFSET; + lane_num = SFP_LANE_NUM; + break; + + case 0x0d: /* QSFP+ or later with SFF8636 or SFF8436 mgmt interface */ + case 0x11: /* QSFP28 or later with SFF8636 mgmt interface */ + offset = QSFP_RX_CDR_LOL_OFFSET; + mask = QSFP_RX_CDR_LOL_MASK; + bit_offset = QSFP_RX_CDR_LOL_BIT_OFFSET; + lane_num = QSFP_LANE_NUM; + break; + + case 0x18: /* QSFP-DD */ + case 0x1e: /* QSFP+ or later with CMIS */ + offset = QSFP_DD_RX_CDR_LOL_OFFSET; + mask = QSFP_DD_RX_CDR_LOL_MASK; + bit_offset = QSFP_DD_RX_CDR_LOL_BIT_OFFSET; + rv = optoe_bin_read_by_index(transceiver_index, &buffer, QSFP_DD_LANE_NUM_OFFSET, 1); + if(rv < 0) + return rv; + lane_num = (buffer >> QSFP_DD_LANE_NUM_MEDIALANE_BIT_OFFSET) & QSFP_DD_LANE_NUM_MEDIALANE_MASK; + break; + + default: + return -EPERM; + } + + rv = optoe_bin_read_by_index(transceiver_index, &buffer, offset, 1); + rx_cdr_lol[0] = lane_num; + rx_cdr_lol[1] = (buffer >> bit_offset) & mask; + + return rv; +} + +int drv_get_eth_tx_fault(unsigned int transceiver_index, unsigned char *tx_fault) +{ + unsigned char identifier = 0; + unsigned char buffer; + unsigned int offset = 0; + unsigned char lane_num = 0; + unsigned int mask = 0; + unsigned int bit_offset = 0; + int rv = 0; + + /* Detect the transceiver type */ + rv = optoe_bin_read_by_index(transceiver_index, &identifier, TRANSCEIVER_IDENTIIFIER_OFFSET, 1); + if(rv < 0) + return rv; + + if((identifier == 0x0) || (identifier >= 0x1f)) + { + /* If identifier is 0, check the extended identifier */ + rv = optoe_bin_read_by_index(transceiver_index, &identifier, TRANSCEIVER_EXTENDED_IDENTIIFIER_OFFSET, 1); + if(rv < 0) + return rv; + } + + switch(identifier) + { + case 0x00: + case 0x03: /* SFP/SFP+/SFP28 */ + offset = SFP_TX_FAULT_OFFSET; + mask = SFP_TX_FAULT_MASK; + bit_offset = SFP_TX_FAULT_BIT_OFFSET; + lane_num = SFP_LANE_NUM; + break; + + case 0x0d: /* QSFP+ or later with SFF8636 or SFF8436 mgmt interface */ + case 0x11: /* QSFP28 or later with SFF8636 mgmt interface */ + offset = QSFP_TX_FAULT_OFFSET; + mask = QSFP_TX_FAULT_MASK; + bit_offset = QSFP_TX_FAULT_BIT_OFFSET; + lane_num = QSFP_LANE_NUM; + break; + + case 0x18: /* QSFP-DD */ + case 0x1e: /* QSFP+ or later with CMIS */ + offset = QSFP_DD_TX_FAULT_OFFSET; + mask = QSFP_DD_TX_FAULT_MASK; + bit_offset = QSFP_DD_TX_FAULT_BIT_OFFSET; + rv = optoe_bin_read_by_index(transceiver_index, &buffer, QSFP_DD_LANE_NUM_OFFSET, 1); + if(rv < 0) + return rv; + lane_num = (buffer >> QSFP_DD_LANE_NUM_MEDIALANE_BIT_OFFSET) & QSFP_DD_LANE_NUM_MEDIALANE_MASK; + break; + + default: + return -EPERM; + } + + rv = optoe_bin_read_by_index(transceiver_index, &buffer, offset, 1); + tx_fault[0] = lane_num; + tx_fault[1] = (buffer >> bit_offset) & mask; + + return rv; +} + +int drv_get_eth_module_status(unsigned int transceiver_index, unsigned char *module_status) +{ + unsigned char identifier = 0; + unsigned char buffer; + unsigned int offset = 0; + int rv = 0; + + /* Detect the transceiver type */ + rv = optoe_bin_read_by_index(transceiver_index, &identifier, TRANSCEIVER_IDENTIIFIER_OFFSET, 1); + if(rv < 0) + return rv; + + if((identifier == 0x0) || (identifier >= 0x1f)) + { + /* If identifier is 0, check the extended identifier */ + rv = optoe_bin_read_by_index(transceiver_index, &identifier, TRANSCEIVER_EXTENDED_IDENTIIFIER_OFFSET, 1); + if(rv < 0) + return rv; + } + + switch(identifier) + { + case 0x00: + case 0x03: /* SFP/SFP+/SFP28 */ + /* not supported */ + return -1; + + case 0x0d: /* QSFP+ or later with SFF8636 or SFF8436 mgmt interface */ + case 0x11: /* QSFP28 or later with SFF8636 mgmt interface */ + /* not supported */ + return -1; + + case 0x18: /* QSFP-DD */ + case 0x1e: /* QSFP+ or later with CMIS */ + offset = QSFP_DD_MODULE_STATUS_OFFSET; + break; + + default: + return -EPERM; + } + + rv = optoe_bin_read_by_index(transceiver_index, &buffer, offset, 1); + *module_status = ((buffer & QSFP_DD_MODULE_STATUS_BITMAP) >> 1); + + return rv; +} + + +int drv_get_eth_bus_status(unsigned int transceiver_index, unsigned char *bus_status) +{ + unsigned char identifier = 0; + int rv = 0; + + /* Detect the transceiver type */ + rv = optoe_bin_read_by_index(transceiver_index, &identifier, TRANSCEIVER_IDENTIIFIER_OFFSET, 1); + if(rv < 0) + return rv; + + if((identifier == 0x0) || (identifier >= 0x1f)) + { + /* If identifier is 0, check the extended identifier */ + rv = optoe_bin_read_by_index(transceiver_index, &identifier, TRANSCEIVER_EXTENDED_IDENTIIFIER_OFFSET, 1); + if(rv < 0) + return rv; + } + *bus_status = identifier; + + return rv; +} + +int drv_get_eth_datapath_status(unsigned int transceiver_index, unsigned int *datapath_status) +{ + unsigned char identifier = 0; + unsigned int buffer = 0; + unsigned int offset = 0; + unsigned char lane_num = 0; + unsigned char lane_num_buf = 0; + size_t len = 0; + int rv = 0; + + /* Detect the transceiver type */ + rv = optoe_bin_read_by_index(transceiver_index, &identifier, TRANSCEIVER_IDENTIIFIER_OFFSET, 1); + if(rv < 0) + return rv; + + if((identifier == 0x0) || (identifier >= 0x1f)) + { + /* If identifier is 0, check the extended identifier */ + rv = optoe_bin_read_by_index(transceiver_index, &identifier, TRANSCEIVER_EXTENDED_IDENTIIFIER_OFFSET, 1); + if(rv < 0) + return rv; + } + + switch(identifier) + { + case 0x00: + case 0x03: /* SFP/SFP+/SFP28 */ + /* not supported */ + return -1; + + case 0x0d: /* QSFP+ or later with SFF8636 or SFF8436 mgmt interface */ + case 0x11: /* QSFP28 or later with SFF8636 mgmt interface */ + /* not supported */ + return -1; + + case 0x18: /* QSFP-DD */ + case 0x1e: /* QSFP+ or later with CMIS */ + offset = QSFP_DD_DATAPATH_STATUS_OFFSET; + len = QSFP_DD_DATAPATH_STATUS_WIDTH; + rv = optoe_bin_read_by_index(transceiver_index, &lane_num_buf, QSFP_DD_LANE_NUM_OFFSET, 1); + if(rv < 0) + return rv; + lane_num = (lane_num_buf >> QSFP_DD_LANE_NUM_MEDIALANE_BIT_OFFSET) & QSFP_DD_LANE_NUM_MEDIALANE_MASK; + break; + + default: + return -EPERM; + } + + rv = optoe_bin_read_by_index(transceiver_index, (char*)&buffer, offset, len); + datapath_status[0] = lane_num; + datapath_status[1] = buffer; + + return rv; +} + +void drv_get_loglevel(long *lev) +{ + *lev = (long)loglevel; + + return; +} + +void drv_set_loglevel(long lev) +{ + loglevel = (unsigned int)lev; + + return; +} + +ssize_t drv_debug_help(char *buf) +{ +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, + "For eeprom node, use i2cget/i2cset/i2cdump -f -y 0x50 to debug.\n" + "For other nodes, use i2cget/i2cset/i2cdump -f -y to debug.\n" + ": 601-632 for port1-port32\n" + ": address is presented as below, and each register has 8 bits to present status of 8 ports.\n" + "sysname debug cmd\n" + "port1 eeprom i2cdump -f -y 601 0x50\n" + "port2 eeprom i2cdump -f -y 602 0x50\n" + "...\n" + "port32 eeprom i2cdump -f -y 632 0x50\n" + "port1-8 present i2cget -f -y 633 0x62 0x20\n" + "port9-16 present i2cget -f -y 633 0x62 0x21\n" + "port17-24 present i2cget -f -y 634 0x64 0x20\n" + "port25-32 present i2cget -f -y 634 0x64 0x21\n" + "port1-8 reset i2cget -f -y 633 0x62 0x38\n" + "port9-16 reset i2cget -f -y 633 0x62 0x39\n" + "port17-24 reset i2cget -f -y 634 0x64 0x38\n" + "port25-32 reset i2cget -f -y 634 0x64 0x39\n" + "port1-8 lpmode i2cget -f -y 633 0x62 0x30\n" + "port9-16 lpmode i2cget -f -y 633 0x62 0x31\n" + "port17-24 lpmode i2cget -f -y 634 0x64 0x30\n" + "port25-32 lpmode i2cget -f -y 634 0x64 0x31\n" + "port1-8 power_on i2cget -f -y 633 0x62 0x40\n" + "port9-16 power_on i2cget -f -y 633 0x62 0x41\n" + "port17-24 power_on i2cget -f -y 634 0x64 0x40\n" + "port25-32 power_on i2cget -f -y 634 0x64 0x41\n" + "port1-8 interrupt i2cget -f -y 633 0x62 0x10\n" + "port9-16 interrupt i2cget -f -y 633 0x62 0x11\n" + "port17-24 interrupt i2cget -f -y 634 0x64 0x10\n" + "port25-32 interrupt i2cget -f -y 634 0x64 0x11\n"); +#else + return sprintf(buf, + "For eeprom node, use i2cget/i2cset/i2cdump -f -y 0x50 to debug.\n" + "For other nodes, use i2cget/i2cset/i2cdump -f -y to debug.\n" + ": 601-632 for port1-port32\n" + ": address is presented as below, and each register has 8 bits to present status of 8 ports.\n" + "sysname debug cmd\n" + "port1 eeprom i2cdump -f -y 601 0x50\n" + "port2 eeprom i2cdump -f -y 602 0x50\n" + "...\n" + "port32 eeprom i2cdump -f -y 632 0x50\n" + "port1-8 present i2cget -f -y 633 0x62 0x20\n" + "port9-16 present i2cget -f -y 633 0x62 0x21\n" + "port17-24 present i2cget -f -y 634 0x64 0x20\n" + "port25-32 present i2cget -f -y 634 0x64 0x21\n" + "port1-8 reset i2cget -f -y 633 0x62 0x38\n" + "port9-16 reset i2cget -f -y 633 0x62 0x39\n" + "port17-24 reset i2cget -f -y 634 0x64 0x38\n" + "port25-32 reset i2cget -f -y 634 0x64 0x39\n" + "port1-8 lpmode i2cget -f -y 633 0x62 0x30\n" + "port9-16 lpmode i2cget -f -y 633 0x62 0x31\n" + "port17-24 lpmode i2cget -f -y 634 0x64 0x30\n" + "port25-32 lpmode i2cget -f -y 634 0x64 0x31\n" + "port1-8 power_on i2cget -f -y 633 0x62 0x40\n" + "port9-16 power_on i2cget -f -y 633 0x62 0x41\n" + "port17-24 power_on i2cget -f -y 634 0x64 0x40\n" + "port25-32 power_on i2cget -f -y 634 0x64 0x41\n" + "port1-8 interrupt i2cget -f -y 633 0x62 0x10\n" + "port9-16 interrupt i2cget -f -y 633 0x62 0x11\n" + "port17-24 interrupt i2cget -f -y 634 0x64 0x10\n" + "port25-32 interrupt i2cget -f -y 634 0x64 0x11\n"); +#endif + +} + +ssize_t drv_debug(const char *buf, int count) +{ + return 0; +} + +// For s3ip +static struct transceiver_drivers_t pfunc = { + .get_eth_diagnostic = drv_get_eth_diagnostic, + .get_eth_power_on = drv_get_eth_power_on, + .set_eth_power_on = drv_set_eth_power_on, + .get_eth_reset = drv_get_eth_reset, + .set_eth_reset = drv_set_eth_reset, + .get_eth_lpmode = drv_get_eth_lpmode, + .set_eth_lpmode = drv_set_eth_lpmode, + .get_eth_present = drv_get_eth_present, + .get_eth_present_history = drv_get_eth_present_history, + .get_eth_interrupt = drv_get_eth_interrupt, + .get_eth_eeprom = drv_get_eth_eeprom, + .set_eth_eeprom = drv_set_eth_eeprom, + .get_eth_temp = drv_get_eth_temp, + .get_eth_led_status = drv_get_eth_led_status, + .set_eth_led_status = drv_set_eth_led_status, + .get_eth_tx_los = drv_get_eth_tx_los, + .get_eth_rx_los = drv_get_eth_rx_los, + .get_eth_tx_disable = drv_get_eth_tx_disable, + .set_eth_tx_disable = drv_set_eth_tx_disable, + .get_eth_rx_disable = drv_get_eth_rx_disable, + .get_eth_tx_cdr_lol = drv_get_eth_tx_cdr_lol, + .get_eth_rx_cdr_lol = drv_get_eth_rx_cdr_lol, + .get_eth_tx_fault = drv_get_eth_tx_fault, + .get_eth_module_status = drv_get_eth_module_status, + .get_eth_datapath_status = drv_get_eth_datapath_status, + .get_eth_bus_status = drv_get_eth_bus_status, + .get_loglevel = drv_get_loglevel, + .set_loglevel = drv_set_loglevel, + .debug_help = drv_debug_help, +}; + +static int drv_transceiver_probe(struct platform_device *pdev) +{ + s3ip_transceiver_drivers_register(&pfunc); + + return 0; +} + +static int drv_transceiver_remove(struct platform_device *pdev) +{ + s3ip_transceiver_drivers_unregister(); + + return 0; +} + +static struct platform_driver drv_transceiver_driver = { + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + }, + .probe = drv_transceiver_probe, + .remove = drv_transceiver_remove, +}; + +static int __init drv_transceiver_init(void) +{ + int err=0; + int retval=0; + + drv_transceiver_device = platform_device_alloc(DRVNAME, 0); + if(!drv_transceiver_device) + { + TRANSCEIVER_DEBUG("%s(#%d): platform_device_alloc fail\n", __func__, __LINE__); + return -ENOMEM; + } + + retval = platform_device_add(drv_transceiver_device); + if(retval) + { + TRANSCEIVER_DEBUG("%s(#%d): platform_device_add failed\n", __func__, __LINE__); + err = -retval; + goto dev_add_failed; + } + + retval = platform_driver_register(&drv_transceiver_driver); + if(retval) + { + TRANSCEIVER_DEBUG("%s(#%d): platform_driver_register failed(%d)\n", __func__, __LINE__, err); + err = -retval; + goto dev_reg_failed; + } + + return 0; + +dev_reg_failed: + platform_device_unregister(drv_transceiver_device); + return err; + +dev_add_failed: + platform_device_put(drv_transceiver_device); + return err; +} + +static void __exit drv_transceiver_exit(void) +{ + platform_driver_unregister(&drv_transceiver_driver); + platform_device_unregister(drv_transceiver_device); + + return; +} + +MODULE_DESCRIPTION("S3IP Transceiver Driver"); +MODULE_VERSION(SWITCH_TRANSCEIVER_DRIVER_VERSION); +MODULE_LICENSE("GPL"); + +module_init(drv_transceiver_init); +module_exit(drv_transceiver_exit); diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_transceiver_driver.h b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_transceiver_driver.h new file mode 100644 index 0000000000..25959b92e9 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_transceiver_driver.h @@ -0,0 +1,203 @@ +#ifndef SWITCH_TRANSCEIVER_DRIVER_H +#define SWITCH_TRANSCEIVER_DRIVER_H + +#include "switch.h" + +#define TRANSCEIVER_ERR(fmt, args...) LOG_ERR("transceiver: ", fmt, ##args) +#define TRANSCEIVER_WARNING(fmt, args...) LOG_WARNING("transceiver: ", fmt, ##args) +#define TRANSCEIVER_INFO(fmt, args...) LOG_INFO("transceiver: ", fmt, ##args) +#define TRANSCEIVER_DEBUG(fmt, args...) LOG_DBG("transceiver: ", fmt, ##args) + +#define TRANSCEIVER_NAME_STRING "transceiver" +#define TRANSCEIVER_TOTAL_NUM 32 +#define LSW_CPLD0_CS 1 +#define LSW_CPLD1_CS 2 + +#define QSFP_POWER_ON1 0x40 + +#define QSFP_RST1 0x38 + +#define QSFP_LED 0xA0 + +#define QSFP_LPWN1 0x30 +#define QSFP_PRESENT1 0x20 +#define QSFP_INTR1 0x10 + +#define TRANSCEIVER_IDENTIIFIER_OFFSET 0x0 +#define TRANSCEIVER_EXTENDED_IDENTIIFIER_OFFSET 0x80 + +#define SFP_DDM_TYPE_OFFSET 92 +#define SFP_DDM_TYPE_MONITOR_IMPLEMENTED_OFFSET (1<<6) +#define SFP_TEMPERATURE_OFFSET 352 +#define SFP_VOLTAGE_OFFSET 354 +#define SFP_BIAS_OFFSET 356 +#define SFP_TX_POWER_OFFSET 358 +#define SFP_RX_POWER_OFFSET 360 +#define SFP_LANE_NUM 1 +#define SFP_RX_LOS_OFFSET 366 +#define SFP_RX_LOS_BITMAP (0x02) +#define SFP_RX_LOS_MASK 0x1 +#define SFP_RX_LOS_BIT_OFFSET 1 +#define SFP_TX_DISABLE_OFFSET 366 +#define SFP_TX_DISABLE_BITMAP (0x80) +#define SFP_SOFT_TX_DISABLE_BITMAP (0x40) +#define SFP_TX_DISABLE_MASK 0x1 +#define SFP_TX_DISABLE_BIT_OFFSET 8 +#define SFP_TX_CDR_LOL_OFFSET 375 +#define SFP_TX_CDR_LOL_BITMAP (0x02) +#define SFP_TX_CDR_LOL_MASK 0x1 +#define SFP_TX_CDR_LOL_BIT_OFFSET 1 +#define SFP_RX_CDR_LOL_OFFSET 375 +#define SFP_RX_CDR_LOL_BITMAP (0x01) +#define SFP_RX_CDR_LOL_MASK 0x1 +#define SFP_RX_CDR_LOL_BIT_OFFSET 0 +#define SFP_TX_FAULT_OFFSET 366 +#define SFP_TX_FAULT_BITMAP (0x04) +#define SFP_TX_FAULT_MASK 0x1 +#define SFP_TX_FAULT_BIT_OFFSET 2 + +#define QSFP_POWER_CONTROL_OFFSET 93 +#define QSFP_POWER_CONTROL_POWER_OVERRIDE_BIT (1<<0) +#define QSFP_POWER_CONTROL_POWER_SET_BIT (1<<1) +#define QSFP_POWER_CONTROL_ENABLE_CLASS_5_7_BIT (1<<2) +#define QSFP_POWER_CONTROL_ENABLE_CLASS_8_BIT (1<<3) +#define QSFP_TEMPERATURE_OFFSET 22 +#define QSFP_VOLTAGE_OFFSET 26 +#define QSFP_BIAS_OFFSET 42 +#define QSFP_TX_POWER_OFFSET 50 +#define QSFP_RX_POWER_OFFSET 34 +#define QSFP_LANE_NUM 4 +#define QSFP_TX_LOS_OFFSET 3 +#define QSFP_TX_LOS_BITMAP (0xF0) +#define QSFP_TX_LOS_MASK 0xF +#define QSFP_TX_LOS_BIT_OFFSET 4 +#define QSFP_RX_LOS_OFFSET 3 +#define QSFP_RX_LOS_BITMAP (0x0F) +#define QSFP_RX_LOS_MASK 0xF +#define QSFP_RX_LOS_BIT_OFFSET 0 +#define QSFP_TX_DISABLE_OFFSET 86 +#define QSFP_TX_DISABLE_BITMAP (0x0F) +#define QSFP_TX_DISABLE_MASK 0xF +#define QSFP_TX_DISABLE_BIT_OFFSET 0 +#define QSFP_RX_DISABLE_OFFSET 625 +#define QSFP_RX_DISABLE_BITMAP (0xF0) +#define QSFP_RX_DISABLE_MASK 0xF +#define QSFP_RX_DISABLE_BIT_OFFSET 4 +#define QSFP_TX_CDR_LOL_OFFSET 5 +#define QSFP_TX_CDR_LOL_BITMAP (0xF0) +#define QSFP_TX_CDR_LOL_MASK 0xF +#define QSFP_TX_CDR_LOL_BIT_OFFSET 4 +#define QSFP_RX_CDR_LOL_OFFSET 5 +#define QSFP_RX_CDR_LOL_BITMAP (0x0F) +#define QSFP_RX_CDR_LOL_MASK 0xF +#define QSFP_RX_CDR_LOL_BIT_OFFSET 0 +#define QSFP_TX_FAULT_OFFSET 4 +#define QSFP_TX_FAULT_BITMAP (0x0F) +#define QSFP_TX_FAULT_MASK 0xF +#define QSFP_TX_FAULT_BIT_OFFSET 0 +#define QSFP_EXT_IDENTIFIER_OFFSET 129 +#define QSFP_EXT_IDENTIFIER_POWER_CLASS_8_BITMAP (0x20) +#define QSFP_EXT_IDENTIFIER_POWER_CLASS_1_4_BITMAP (0xC0) +#define QSFP_EXT_IDENTIFIER_POWER_CLASS_5_7_BITMAP (0x03) + +#define QSFP_DD_MODULE_GLOBAL_CONTROL_OFFSET 26 +#define QSFP_DD_MODULE_GLOBAL_CONTROL_FORCE_LOW_POWER_BIT (1<<4) +#define QSFP_DD_MODULE_GLOBAL_CONTROL_LOW_POWER_BIT (1<<6) +#define QSFP_DD_TEMPERATURE_OFFSET 14 +#define QSFP_DD_VOLTAGE_OFFSET 16 +#define QSFP_DD_BIAS_OFFSET 2346 +#define QSFP_DD_TX_POWER_OFFSET 2330 +#define QSFP_DD_RX_POWER_OFFSET 2362 +#define QSFP_DD_LANE_NUM 8 +#define QSFP_DD_LANE_NUM_OFFSET 88 +#define QSFP_DD_LANE_NUM_MEDIALANE_MASK 0xF +#define QSFP_DD_LANE_NUM_MEDIALANE_BIT_OFFSET 0 +#define QSFP_DD_TX_LOS_OFFSET 2312 +#define QSFP_DD_TX_LOS_BITMAP (0xFF) +#define QSFP_DD_TX_LOS_MASK 0xFF +#define QSFP_DD_TX_LOS_BIT_OFFSET 0 +#define QSFP_DD_RX_LOS_OFFSET 2323 +#define QSFP_DD_RX_LOS_BITMAP (0xFF) +#define QSFP_DD_RX_LOS_MASK 0xFF +#define QSFP_DD_RX_LOS_BIT_OFFSET 0 +#define QSFP_DD_TX_DISABLE_OFFSET 2178 +#define QSFP_DD_TX_DISABLE_BITMAP (0xFF) +#define QSFP_DD_TX_DISABLE_MASK 0xFF +#define QSFP_DD_TX_DISABLE_BIT_OFFSET 0 +#define QSFP_DD_RX_DISABLE_OFFSET 2186 +#define QSFP_DD_RX_DISABLE_BITMAP (0xFF) +#define QSFP_DD_RX_DISABLE_MASK 0xFF +#define QSFP_DD_RX_DISABLE_BIT_OFFSET 0 +#define QSFP_DD_TX_CDR_LOL_OFFSET 2313 +#define QSFP_DD_TX_CDR_LOL_BITMAP (0xFF) +#define QSFP_DD_TX_CDR_LOL_MASK 0xFF +#define QSFP_DD_TX_CDR_LOL_BIT_OFFSET 0 +#define QSFP_DD_RX_CDR_LOL_OFFSET 2324 +#define QSFP_DD_RX_CDR_LOL_BITMAP (0xFF) +#define QSFP_DD_RX_CDR_LOL_MASK 0xFF +#define QSFP_DD_RX_CDR_LOL_BIT_OFFSET 0 +#define QSFP_DD_TX_FAULT_OFFSET 2311 +#define QSFP_DD_TX_FAULT_BITMAP (0xFF) +#define QSFP_DD_TX_FAULT_MASK 0xFF +#define QSFP_DD_TX_FAULT_BIT_OFFSET 0 +#define QSFP_DD_MODULE_STATUS_OFFSET 3 +#define QSFP_DD_MODULE_STATUS_BITMAP (0x0E) +#define QSFP_DD_DATAPATH_STATUS_OFFSET 2304 +#define QSFP_DD_DATAPATH_STATUS_WIDTH 4 + +struct transceiver_drivers_t{ + unsigned int (*get_eth_diagnostic) (unsigned int transceiver_index, char *buf); + int (*get_eth_temp) (unsigned int transceiver_index, long long *temp); + unsigned int (*get_eth_led_status) (unsigned int transceiver_index); + unsigned int (*set_eth_led_status) (unsigned int transceiver_index, unsigned int led_value); + unsigned int (*get_eth_power_on) (unsigned int transceiver_index); + unsigned int (*set_eth_power_on) (unsigned int transceiver_index, unsigned int pwr_value); + unsigned int (*get_eth_reset) (unsigned int transceiver_index); + unsigned int (*set_eth_reset) (unsigned int transceiver_index, unsigned int rst_value); + int (*get_eth_lpmode) (unsigned int transceiver_index, unsigned int *lpmode_value); + int (*set_eth_lpmode) (unsigned int transceiver_index, unsigned int lpmode_value); + unsigned int (*get_eth_present) (unsigned int transceiver_index); + unsigned int (*get_eth_present_history) (unsigned int transceiver_index); + unsigned int (*get_eth_interrupt) (unsigned int transceiver_index); + int (*get_eth_eeprom) (unsigned int transceiver_index, char *buf, loff_t off, size_t len); + int (*set_eth_eeprom) (unsigned int transceiver_index, char *buf, loff_t off, size_t len); + int (*get_eth_tx_los) (unsigned int transceiver_index, unsigned char *tx_los); + int (*get_eth_rx_los) (unsigned int transceiver_index, unsigned char *rx_los); + int (*get_eth_tx_disable) (unsigned int transceiver_index, unsigned char *tx_disable); + int (*set_eth_tx_disable) (unsigned int transceiver_index, unsigned int tx_disable); + int (*get_eth_rx_disable) (unsigned int transceiver_index, unsigned char *rx_disable); + int (*get_eth_tx_cdr_lol) (unsigned int transceiver_index, unsigned char *tx_cdr_lol); + int (*get_eth_rx_cdr_lol) (unsigned int transceiver_index, unsigned char *rx_cdr_lol); + int (*get_eth_tx_fault) (unsigned int transceiver_index, unsigned char *tx_fault); + int (*get_eth_module_status) (unsigned int transceiver_index, unsigned char *module_status); + int (*get_eth_datapath_status) (unsigned int transceiver_index, unsigned int *datapath_status); + int (*get_eth_bus_status) (unsigned int transceiver_index, unsigned char *bus_status); + void (*get_loglevel) (long *lev); + void (*set_loglevel) (long lev); + int (*get_disable_iic_access) (char *buf, long iic_value); + int (*set_disable_iic_access) (char *buf, long iic_value); + ssize_t (*debug_help) (char *buf); + ssize_t (*debug_help_hisonic) (char *buf); + ssize_t (*debug) (const char *buf, int count); +}; + +unsigned int drv_get_eth_reset(unsigned int transceiver_index); +unsigned int drv_set_eth_reset(unsigned int transceiver_index, unsigned int rst_value); +unsigned int drv_get_eth_led_status(unsigned int transceiver_index); +unsigned int drv_set_eth_led_status(unsigned int transceiver_index, unsigned int led_value); +unsigned int drv_get_eth_present(unsigned int transceiver_index); +unsigned int drv_get_eth_present_history(unsigned int transceiver_index); +int drv_get_eth_eeprom(unsigned int transceiver_index, char *buf, loff_t off, size_t len); +int drv_set_eth_eeprom(unsigned int transceiver_index, char *buf, loff_t off, size_t len); +void drv_get_loglevel(long *lev); +void drv_set_loglevel(long lev); +int drv_get_disable_iic_access(char *buf, long iic_value); +int drv_set_disable_iic_access(char *buf, long iic_value); +ssize_t drv_debug_help(char *buf); +ssize_t drv_debug_help_hisonic(char *buf); +ssize_t drv_debug(const char *buf, int count); + +void s3ip_transceiver_drivers_register(struct transceiver_drivers_t *p_func); +void s3ip_transceiver_drivers_unregister(void); + +#endif /* SWITCH_TRANSCEIVER_DRIVER_H */ diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_wdt_driver.c b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_wdt_driver.c new file mode 100644 index 0000000000..7f53da29ce --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_wdt_driver.c @@ -0,0 +1,328 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "switch_wdt_driver.h" + +#define DRVNAME "drv_wdt_driver" + +#define SWITCH_WDT_DRIVER_VERSION "0.0.1" + +unsigned int loglevel = 0; +static struct platform_device *drv_wdt_device; + +unsigned char lpc_read_ec(unsigned short offset) +{ + return inb(EC_LPC_IO_BASE_ADDR+offset); +} + +void lpc_write_ec(unsigned int offset, unsigned char val) +{ + outb(val, EC_LPC_IO_BASE_ADDR+offset); +} + +ssize_t drv_get_identify(char *buf) +{ +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "COMe EC WDT\n"); +#else + return sprintf(buf, "COMe EC WDT\n"); +#endif +} + +ssize_t drv_get_state(char *buf) +{ + unsigned char val; + + val = lpc_read_ec(EC_WDT_CFG_REG_OFFSET); + if((val & BIT0)) +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "active\n"); +#else + return sprintf(buf, "active\n"); +#endif + + else +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "inactive\n"); +#else + return sprintf(buf, "inactive\n"); +#endif + +} + +ssize_t drv_get_timeleft(char *buf) +{ + unsigned short timeout; + + timeout = (lpc_read_ec(EC_WDT_TIMER_MSB_REG_OFFSET) << 8) | lpc_read_ec(EC_WDT_TIMER_LSB_REG_OFFSET); +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", timeout); +#else + return sprintf(buf, "%d\n", timeout); +#endif + +} + +ssize_t drv_get_timeout(char *buf) +{ + unsigned short timeout; + + timeout = (lpc_read_ec(EC_WDT_TIMER_MSB_RESET_REG_OFFSET) << 8) | lpc_read_ec(EC_WDT_TIMER_LSB_RESET_REG_OFFSET); +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", timeout); +#else + return sprintf(buf, "%d\n", timeout); +#endif + +} + +void drv_set_timeout(unsigned short timeout) +{ + lpc_write_ec(EC_WDT_TIMER_LSB_RESET_REG_OFFSET, ((timeout >> 0) & 0xff)); + lpc_write_ec(EC_WDT_TIMER_MSB_RESET_REG_OFFSET, ((timeout >> 8) & 0xff)); + + lpc_write_ec(EC_WDT_TIMER_LSB_REG_OFFSET, ((timeout >> 0) & 0xff)); + lpc_write_ec(EC_WDT_TIMER_MSB_REG_OFFSET, ((timeout >> 8) & 0xff)); + + return; +} + +void drv_set_reset(unsigned int reset) +{ + unsigned char val; + + if(reset) + { + //Config gpio52 to gpio + val = inb(GPIO_IO_BASE + GPIO_USE_SEL2_BYTE2); + val |= BIT4; + outb(val, GPIO_IO_BASE + GPIO_USE_SEL2_BYTE2); + + //Set gpio52 to output + val = inb(GPIO_IO_BASE + GP_IO_SEL2_BYTE2 ); + val &= ~BIT4; + outb(val, GPIO_IO_BASE + GP_IO_SEL2_BYTE2 ); + + //Set gpio52 to low + val = inb(GPIO_IO_BASE + GP_LVL2_BYTE2 ); + val &= ~BIT4; + outb(val, GPIO_IO_BASE + GP_LVL2_BYTE2 ); + + msleep(50); + + //Set gpio52 to high + val = inb(GPIO_IO_BASE + GP_LVL2_BYTE2 ); + val |= BIT4; + outb(val, GPIO_IO_BASE + GP_LVL2_BYTE2 ); + } + + return; +} + +ssize_t drv_get_enable(char *buf) +{ + unsigned char val; + + val = lpc_read_ec(EC_WDT_CFG_REG_OFFSET); +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", (val & WDT_CFG_WDT_ENABLE)); +#else + return sprintf(buf, "%d\n", (val & WDT_CFG_WDT_ENABLE)); +#endif + +} + +bool drv_set_enable(unsigned int enable) +{ + unsigned int ec_wdt_timeout_count = 2000; + + if(!enable) + { + lpc_write_ec(EC_WDT_CFG_REG_OFFSET, 0x00); + } + else if(enable == 1) + { + drv_set_reset(1); + lpc_write_ec(EC_WDT_CFG_REG_OFFSET, WDT_CFG_WDT_CLEAR); + + while((lpc_read_ec(EC_WDT_CFG_REG_OFFSET) & (WDT_CFG_WDT_TRIGGER_REPORT | WDT_CFG_WDT_CLEAR)) && (ec_wdt_timeout_count)) + { + ec_wdt_timeout_count--; + usleep_range(99, 101); // Delay 100 us + } + + if(!ec_wdt_timeout_count) + { + WDT_WARNING("The ec_wdt_timeout_count time-out.\n"); + return false; + } + + lpc_write_ec(EC_WDT_CFG_REG_OFFSET, (WDT_CFG_WDT_M1_DELAY_TRIGGER | WDT_CFG_WDT_M3_ENABLE | WDT_CFG_WDT_M1_ENABLE | WDT_CFG_WDT_ENABLE)); + } + else + { + WDT_WARNING("Invalid argument: %d.\n", enable); + return false; + } + + return true; +} + +void drv_get_loglevel(long *lev) +{ + *lev = (long)loglevel; + + return; +} + +void drv_set_loglevel(long lev) +{ + loglevel = (unsigned int)lev; + + return; +} + +ssize_t drv_debug_help(char *buf) +{ +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, + "COMe EC I/O base address: 0x6300\n" + "Relative registers offset:\n" + "WDT_TIMER_MSB 0x06\n" + "WDT_TIMER_LSB 0x07\n" + "WDT_CFG 0x08\n" + "WDT_TIMER_MSB_RESET 0x0c\n" + "WDT_TIMER_LSB_RESET 0x0d\n" + "\n" + "Use the following command to debug:\n" + "inb --hex\n" + "outb \n" + "sysname debug cmd\n" + "state inb 0x6308\n" + "timeout inb 0x630c\n" + " inb 0x630d\n" + "timeleft inb 0x6306\n" + " inb 0x6307\n"); +#else + return sprintf(buf, + "COMe EC I/O base address: 0x6300\n" + "Relative registers offset:\n" + "WDT_TIMER_MSB 0x06\n" + "WDT_TIMER_LSB 0x07\n" + "WDT_CFG 0x08\n" + "WDT_TIMER_MSB_RESET 0x0c\n" + "WDT_TIMER_LSB_RESET 0x0d\n" + "\n" + "Use the following command to debug:\n" + "inb --hex\n" + "outb \n" + "sysname debug cmd\n" + "state inb 0x6308\n" + "timeout inb 0x630c\n" + " inb 0x630d\n" + "timeleft inb 0x6306\n" + " inb 0x6307\n"); +#endif + +} + +ssize_t drv_debug(const char *buf, int count) +{ + return 0; +} + +static struct wdt_drivers_t pfunc = { + .get_identify = drv_get_identify, + .get_state = drv_get_state, + .get_timeleft = drv_get_timeleft, + .get_timeout = drv_get_timeout, + .set_timeout = drv_set_timeout, + .set_reset = drv_set_reset, + .get_enable = drv_get_enable, + .set_enable = drv_set_enable, + .get_loglevel = drv_get_loglevel, + .set_loglevel = drv_set_loglevel, + .debug_help = drv_debug_help, +}; + +static int drv_wdt_probe(struct platform_device *pdev) +{ + s3ip_wdt_drivers_register(&pfunc); + + return 0; +} + +static int drv_wdt_remove(struct platform_device *pdev) +{ + s3ip_wdt_drivers_unregister(); + + return 0; +} + +static struct platform_driver drv_wdt_driver = { + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + }, + .probe = drv_wdt_probe, + .remove = drv_wdt_remove, +}; + +static int __init drv_wdt_init(void) +{ + int err=0; + int retval=0; + + drv_wdt_device = platform_device_alloc(DRVNAME, 0); + if(!drv_wdt_device) + { + WDT_DEBUG("%s(#%d): platform_device_alloc fail\n", __func__, __LINE__); + return -ENOMEM; + } + + retval = platform_device_add(drv_wdt_device); + if(retval) + { + WDT_DEBUG("%s(#%d): platform_device_add failed\n", __func__, __LINE__); + err = -retval; + goto dev_add_failed; + } + + retval = platform_driver_register(&drv_wdt_driver); + if(retval) + { + WDT_DEBUG("%s(#%d): platform_driver_register failed(%d)\n", __func__, __LINE__, err); + err = -retval; + goto dev_reg_failed; + } + + return 0; + +dev_reg_failed: + platform_device_unregister(drv_wdt_device); + return err; + +dev_add_failed: + platform_device_put(drv_wdt_device); + return err; +} + +static void __exit drv_wdt_exit(void) +{ + platform_driver_unregister(&drv_wdt_driver); + platform_device_unregister(drv_wdt_device); + + return; +} + +MODULE_DESCRIPTION("S3IP Watchdog Driver"); +MODULE_VERSION(SWITCH_WDT_DRIVER_VERSION); +MODULE_LICENSE("GPL"); + +module_init(drv_wdt_init); +module_exit(drv_wdt_exit); diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_wdt_driver.h b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_wdt_driver.h new file mode 100644 index 0000000000..f19b50e671 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/switch_wdt_driver.h @@ -0,0 +1,81 @@ +#ifndef SWITCH_WDT_DRIVER_H +#define SWITCH_WDT_DRIVER_H + +#include "switch.h" + +#define WDT_ERR(fmt, args...) LOG_ERR("watchdog: ", fmt, ##args) +#define WDT_WARNING(fmt, args...) LOG_WARNING("watchdog: ", fmt, ##args) +#define WDT_INFO(fmt, args...) LOG_INFO("watchdog: ", fmt, ##args) +#define WDT_DEBUG(fmt, args...) LOG_DBG("watchdog: ", fmt, ##args) + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE (!FALSE) +#endif + +#define BIT7 (1 << 7) +#define BIT6 (1 << 6) +#define BIT5 (1 << 5) +#define BIT4 (1 << 4) +#define BIT3 (1 << 3) +#define BIT2 (1 << 2) +#define BIT1 (1 << 1) +#define BIT0 (1 << 0) + +#define WDT_CFG_WDT_TRIGGER_REPORT BIT7 +#define WDT_CFG_WDT_M1_DELAY_TRIGGER BIT6 +#define WDT_CFG_WDT_M1_MODE BIT5 +#define WDT_CFG_WDT_CLEAR BIT4 +#define WDT_CFG_WDT_M3_ENABLE BIT3 +#define WDT_CFG_WDT_M2_ENABLE BIT2 +#define WDT_CFG_WDT_M1_ENABLE BIT1 +#define WDT_CFG_WDT_ENABLE BIT0 + +#define EC_LPC_IO_BASE_ADDR 0x6300 +#define EC_WDT_TIMER_MSB_REG_OFFSET 0x06 +#define EC_WDT_TIMER_LSB_REG_OFFSET 0x07 +#define EC_WDT_CFG_REG_OFFSET 0x08 +#define EC_WDT_TIMER_MSB_RESET_REG_OFFSET 0x0c +#define EC_WDT_TIMER_LSB_RESET_REG_OFFSET 0x0d + +#define GPIO_IO_BASE 0x500 +#define GPIO_USE_SEL2_BYTE2 0x32 //gpio[48~55] +#define GP_IO_SEL2_BYTE2 0x36 //gpio[48~55] +#define GP_LVL2_BYTE2 0x3A //gpio[48~55] + + +struct wdt_drivers_t{ + ssize_t (*get_identify) (char *buf); + ssize_t (*get_state) (char *buf); + ssize_t (*get_timeleft) (char *buf); + ssize_t (*get_timeout) (char *buf); + void (*set_timeout) (unsigned short timeout); + void (*set_reset) (unsigned int reset); + ssize_t (*get_enable) (char *buf); + bool (*set_enable) (unsigned int enable); + void (*get_loglevel) (long *lev); + void (*set_loglevel) (long lev); + ssize_t (*debug_help) (char *buf); + ssize_t (*debug) (const char *buf, int count); +}; + +ssize_t drv_get_identify(char *buf); +ssize_t drv_get_state(char *buf); +ssize_t drv_get_timeleft(char *buf); +ssize_t drv_get_timeout(char *buf); +void drv_set_timeout(unsigned short timeout); +void drv_set_reset(unsigned int reset); +ssize_t drv_get_enable(char *buf); +bool drv_set_enable(unsigned int enable); +void drv_get_loglevel(long *lev); +void drv_set_loglevel(long lev); +ssize_t drv_debug_help(char *buf); +ssize_t drv_debug(const char *buf, int count); + +void s3ip_wdt_drivers_register(struct wdt_drivers_t *p_func); +void s3ip_wdt_drivers_unregister(void); + +#endif /* SWITCH_MB_DRIVER_H */ diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/Makefile b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/Makefile new file mode 100644 index 0000000000..12520821fd --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/Makefile @@ -0,0 +1,15 @@ + +obj-m += switch_system_fpga.o +obj-m += switch_intel-spi.o +obj-m += switch_optoe.o +obj-m += switch_coretemp.o +obj-m += switch_i2c_cpld.o +obj-m += switch_system_cpld.o +obj-m += switch_lm75.o +obj-m += switch_at24.o +obj-m += i2c-imc.o +obj-m += sysfs_ipmi.o +obj-m += intel-spi-platform.o +obj-m += mp2975.o +obj-m += switch_pmbus_core.o +obj-m += bel-pfe.o \ No newline at end of file diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/bel-pfe.c b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/bel-pfe.c new file mode 100644 index 0000000000..d936ed9400 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/bel-pfe.c @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Hardware monitoring driver for BEL PFE family power supplies. + * + * Copyright (c) 2019 Facebook Inc. + */ + +#include +#include +#include +#include +#include +#include + +#include "pmbus.h" + +enum chips {pfe1100, pfe1500, pfe3000}; + +/* + * Disable status check for pfe3000 devices, because some devices report + * communication error (invalid command) for VOUT_MODE command (0x20) + * although correct VOUT_MODE (0x16) is returned: it leads to incorrect + * exponent in linear mode. + */ +static struct pmbus_platform_data pfe3000_plat_data = { + .flags = PMBUS_SKIP_STATUS_CHECK, +}; + +static struct pmbus_driver_info pfe_driver_info[] = { + [pfe1100] = { + .pages = 1, + .format[PSC_VOLTAGE_IN] = linear, + .format[PSC_VOLTAGE_OUT] = linear, + .format[PSC_CURRENT_IN] = linear, + .format[PSC_CURRENT_OUT] = linear, + .format[PSC_POWER] = linear, + .format[PSC_TEMPERATURE] = linear, + .format[PSC_FAN] = linear, + + .func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | + PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_POUT | + PMBUS_HAVE_VIN | PMBUS_HAVE_IIN | + PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | + PMBUS_HAVE_STATUS_TEMP | + PMBUS_HAVE_FAN12, + }, + + [pfe1500] = { + .pages = 1, + .format[PSC_VOLTAGE_IN] = linear, + .format[PSC_VOLTAGE_OUT] = linear, + .format[PSC_CURRENT_IN] = linear, + .format[PSC_CURRENT_OUT] = linear, + .format[PSC_POWER] = linear, + .format[PSC_TEMPERATURE] = linear, + .format[PSC_FAN] = linear, + + .func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | + PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_POUT | + PMBUS_HAVE_VIN | PMBUS_HAVE_IIN | + PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | + PMBUS_HAVE_STATUS_TEMP | + PMBUS_HAVE_FAN12, + }, + + [pfe3000] = { + .pages = 7, + .format[PSC_VOLTAGE_IN] = linear, + .format[PSC_VOLTAGE_OUT] = linear, + .format[PSC_CURRENT_IN] = linear, + .format[PSC_CURRENT_OUT] = linear, + .format[PSC_POWER] = linear, + .format[PSC_TEMPERATURE] = linear, + .format[PSC_FAN] = linear, + + /* Page 0: V1. */ + .func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | + PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_POUT | PMBUS_HAVE_FAN12 | + PMBUS_HAVE_VIN | PMBUS_HAVE_IIN | + PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | + PMBUS_HAVE_TEMP3 | PMBUS_HAVE_STATUS_TEMP | + PMBUS_HAVE_VCAP, + + /* Page 1: Vsb. */ + .func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | + PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT | + PMBUS_HAVE_POUT, + + /* + * Page 2: V1 Ishare. + * Page 3: Reserved. + * Page 4: V1 Cathode. + * Page 5: Vsb Cathode. + * Page 6: V1 Sense. + */ + .func[2] = PMBUS_HAVE_VOUT, + .func[4] = PMBUS_HAVE_VOUT, + .func[5] = PMBUS_HAVE_VOUT, + .func[6] = PMBUS_HAVE_VOUT, + }, +}; + +static const struct i2c_device_id pfe_device_id[]; + +static int pfe_pmbus_probe(struct i2c_client *client) +{ + int model; + + model = (int)i2c_match_id(pfe_device_id, client)->driver_data; + + /* + * PFE3000-12-069RA devices may not stay in page 0 during device + * probe which leads to probe failure (read status word failed). + * So let's set the device to page 0 at the beginning. + */ + if (model == pfe3000) { + client->dev.platform_data = &pfe3000_plat_data; + i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0); + } + + return pmbus_do_probe(client, &pfe_driver_info[model]); +} + +static const struct i2c_device_id pfe_device_id[] = { + {"pfe1100", pfe1100}, + {"pfe1500", pfe1500}, + {"pfe3000", pfe3000}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, pfe_device_id); + +static struct i2c_driver pfe_pmbus_driver = { + .driver = { + .name = "bel-pfe", + }, + .probe_new = pfe_pmbus_probe, + .id_table = pfe_device_id, +}; + +module_i2c_driver(pfe_pmbus_driver); + +MODULE_AUTHOR("Tao Ren "); +MODULE_DESCRIPTION("PMBus driver for BEL PFE Family Power Supplies"); +MODULE_LICENSE("GPL"); diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/i2c-imc.c b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/i2c-imc.c new file mode 100644 index 0000000000..28d2fe4338 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/i2c-imc.c @@ -0,0 +1,552 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Intel Memory Controller iMC SMBus Driver to DIMMs. + * + * Copyright (c) 2013-2016 Andrew Lutomirski + * Copyright (c) 2020 Stefan Schaeckeler , Cisco Systems + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include + +//simon: try to support 4.x kernel +#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 2, 0) +#define i2c_new_client_device i2c_new_device +#endif +#ifdef C11_ANNEX_K +#include "libboundscheck/include/securec.h" +#endif + +/* iMC Main, PCI dev 0x13, fn 0, 8086.6fa8 */ +#define PCI_DEVICE_ID_INTEL_BROADWELL_IMC_TA 0x6fa8 + +/* Register offsets for channel pairs 0+1 and 2+3 */ +#define SMBSTAT(i) (0x180 + 0x10*(i)) +#define SMBCMD(i) (0x184 + 0x10*(i)) +#define SMBCNTL(i) (0x188 + 0x10*(i)) + +/* SMBSTAT fields */ +#define SMBSTAT_RDO (1U << 31) /* Read Data Valid */ +#define SMBSTAT_WOD (1U << 30) /* Write Operation Done */ +#define SMBSTAT_SBE (1U << 29) /* SMBus Error */ +#define SMBSTAT_SMB_BUSY (1U << 28) /* SMBus Busy State */ +//#define SMBSTAT_RDATA_MASK 0xffff /* Result of a read */ +#define SMBSTAT_RDATA_MASK 0xbfff /* Result of a read and delete high alarm*/ + +/* SMBCMD fields */ +#define SMBCMD_TRIGGER (1U << 31) /* CMD Trigger */ +#define SMBCMD_WORD_ACCESS (1U << 29) /* Word (vs byte) access */ +#define SMBCMD_TYPE_READ (0U << 27) /* Read */ +#define SMBCMD_TYPE_WRITE (1U << 27) /* Write */ +#define SMBCMD_SA_SHIFT 24 +#define SMBCMD_BA_SHIFT 16 + +/* SMBCNTL fields */ +#define SMBCNTL_DTI_MASK 0xf0000000 /* Slave Address low bits */ +#define SMBCNTL_DTI_SHIFT 28 /* Slave Address low bits */ +#define SMBCNTL_DIS_WRT (1U << 26) /* Disable Write */ +#define SMBCNTL_TSOD_PRES_MASK 0xff /* DIMM Present mask */ + +/* For sanity check: bits that might randomly change if we race with firmware */ +#define SMBCMD_OUR_BITS (~(u32)SMBCMD_TRIGGER) +#define SMBCNTL_OUR_BITS (SMBCNTL_DTI_MASK) + + +/* System Address Decoder, PCI dev 0xf fn 5, 8086.6ffd */ +#define PCI_DEVICE_ID_INTEL_BROADWELL_IMC_SAD 0x6ffd + +/* Register offsets */ +#define SADCNTL 0xf4 + +/* SADCNTL fields */ +#define SADCNTL_LOCAL_NODEID_MASK 0xf /* Local NodeID of socket */ + + +/* Power Control Unit, PCI dev 0x1e fn 1, 8086.6f99 */ +#define PCI_DEVICE_ID_INTEL_BROADWELL_IMC_PCU 0x6f99 + +/* Register offsets */ +#define TSODCNTL 0xe0 + +/* TSODCNTL fields */ + + +/* DIMMs hold jc42 thermal sensors starting at i2c address 0x18 */ +#define DIMM_SENSOR_DRV "jc42" +#define DIMM_SENSOR_BASE_ADR 0x18 + + +#define sanitycheck 1 + +/* Use 50/51 as the designated I2C bus. */ +#define IMC_I2C_BUS_START 50 + +struct imc_channelpair { + struct i2c_adapter adapter; + bool can_write, cltt; +}; + +struct imc_pcu { + struct pci_dev *pci_dev; + u32 tsod_polling_interval; + struct mutex mutex; /* see imc_channelpair_claim() */ +}; + +struct imc_priv { + struct pci_dev *pci_dev; + struct imc_channelpair channelpair[2]; + struct imc_pcu pcu; + bool suspended; +}; + +static int imc_channelpair_claim(struct imc_priv *priv, int i) +{ + if (priv->suspended) + return -EIO; + + /* + * i2c controllers need exclusive access to a psu register and wait + * then for 10ms before starting their transaction. + * + * Possible optimization: Once an i2c controller modified the psu + * register and waits, the other controller does not need to wait for + * the whole 10ms, but then only this other controller has to clean up + * the psu register. + */ + mutex_lock(&priv->pcu.mutex); + + if (priv->channelpair[i].cltt) { + pci_write_config_dword(priv->pcu.pci_dev, TSODCNTL, 0); + usleep_range(10000, 10500); + } + return 0; +} + +static void imc_channelpair_release(struct imc_priv *priv, int i) +{ + if (priv->channelpair[i].cltt) { + /* set tosd_control.tsod_polling_interval to previous value */ + pci_write_config_dword(priv->pcu.pci_dev, TSODCNTL, + priv->pcu.tsod_polling_interval); + } + mutex_unlock(&priv->pcu.mutex); +} + +static bool imc_wait_for_transaction(struct imc_priv *priv, int i, u32 *stat) +{ + int j; + static int busywaits = 1; + + /* + * Distribution of transaction time from 10000 collected samples: + * + * 70us: 1, 80us: 12, 90us: 34, 100us: 132, 110us: 424, 120us: 1138, + * 130us: 5224, 140us: 3035. + * + */ + usleep_range(131, 140); + + /* Don't give up, yet */ + for (j = 0; j < 20; j++) { + pci_read_config_dword(priv->pci_dev, SMBSTAT(i), stat); + if (!(*stat & SMBSTAT_SMB_BUSY)) { + if (j > busywaits) { + busywaits = j; + dev_warn(&priv->pci_dev->dev, + "Discovered surprisingly long transaction time (%d)\n", + busywaits); + } + return true; + } + udelay(9); + } + return false; +} + +/* + * The iMC supports five access types. The terminology is rather inconsistent. + * These are the types: + * + * "Write to pointer register SMBus": I2C_SMBUS_WRITE, I2C_SMBUS_BYTE + * + * Read byte/word: I2C_SMBUS_READ, I2C_SMBUS_{BYTE|WORD}_DATA + * + * Write byte/word: I2C_SMBUS_WRITE, I2C_SMBUS_{BYTE|WORD}_DATA + */ + +static u32 imc_func(struct i2c_adapter *adapter) +{ + int i; + struct imc_channelpair *cp; + struct imc_priv *priv = i2c_get_adapdata(adapter); + + i = (adapter == &priv->channelpair[0].adapter ? 0 : 1); + cp = &priv->channelpair[i]; + + if (cp->can_write) + return I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA; + else + return I2C_FUNC_SMBUS_READ_BYTE_DATA | + I2C_FUNC_SMBUS_READ_WORD_DATA; +} + +static s32 imc_smbus_xfer(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, u8 command, + int size, union i2c_smbus_data *data) +{ + int ret, i; + u32 cmd = 0, cntl, stat; +#ifdef sanitycheck + u32 final_cmd, final_cntl; +#endif + struct imc_channelpair *cp; + struct imc_priv *priv = i2c_get_adapdata(adap); + + i = (adap == &priv->channelpair[0].adapter ? 0 : 1); + cp = &priv->channelpair[i]; + + /* Encode CMD part of addresses and access size */ + cmd |= ((u32)addr & 0x7) << SMBCMD_SA_SHIFT; + cmd |= ((u32)command) << SMBCMD_BA_SHIFT; + if (size == I2C_SMBUS_WORD_DATA) + cmd |= SMBCMD_WORD_ACCESS; + + /* Encode read/write and data to write */ + if (read_write == I2C_SMBUS_READ) { + cmd |= SMBCMD_TYPE_READ; + } else { + cmd |= SMBCMD_TYPE_WRITE; + cmd |= (size == I2C_SMBUS_WORD_DATA + ? swab16(data->word) + : data->byte); + } + + ret = imc_channelpair_claim(priv, i); + if (ret) + return ret; + + pci_read_config_dword(priv->pci_dev, SMBCNTL(i), &cntl); + cntl &= ~SMBCNTL_DTI_MASK; + cntl |= ((u32)addr >> 3) << SMBCNTL_DTI_SHIFT; + pci_write_config_dword(priv->pci_dev, SMBCNTL(i), cntl); + + cmd |= SMBCMD_TRIGGER; + pci_write_config_dword(priv->pci_dev, SMBCMD(i), cmd); + + if (!imc_wait_for_transaction(priv, i, &stat)) { + dev_warn(&priv->pci_dev->dev, "smbus transaction did not complete.\n"); + ret = -ETIMEDOUT; + goto xfer_out_release; + } + +#ifdef sanitycheck /* This is a young driver. Keep the checks for now */ + pci_read_config_dword(priv->pci_dev, SMBCMD(i), &final_cmd); + pci_read_config_dword(priv->pci_dev, SMBCNTL(i), &final_cntl); + if (((cmd ^ final_cmd) & SMBCMD_OUR_BITS) || + ((cntl ^ final_cntl) & SMBCNTL_OUR_BITS)) { + dev_err(&priv->pci_dev->dev, + "Access to channel pair %d-%d raced with hardware: cmd 0x%08X->0x%08X, cntl 0x%08X->0x%08X\n", + 2*i, 2*i+1, cmd, final_cmd, cntl, final_cntl); + ret = -EIO; + goto xfer_out_release; + } +#endif + + if (stat & SMBSTAT_SBE) { + /* + * While SBE is set hardware TSOD polling is disabled. This is + * very bad as this bit is RO-V and will only be cleared after + * a further software initiated transaction finishes + * successfully. + */ + dev_err(&priv->pci_dev->dev, + "smbus error: sbe is set 0x%x\n", stat); + ret = -ENXIO; + goto xfer_out_release; + } + + if (read_write == I2C_SMBUS_READ) { + if (!(stat & SMBSTAT_RDO)) { + dev_warn(&priv->pci_dev->dev, + "Unexpected read status 0x%08X\n", stat); + ret = -EIO; + goto xfer_out_release; + } + /* + * The iMC SMBus controller thinks of SMBus words as being + * big-endian (MSB first). Linux treats them as little-endian, + * so we need to swap them. + */ + if (size == I2C_SMBUS_WORD_DATA) + data->word = swab16(stat & SMBSTAT_RDATA_MASK); + else + data->byte = stat & 0xFF; + } else { + if (!(stat & SMBSTAT_WOD)) { + dev_warn(&priv->pci_dev->dev, + "Unexpected write status 0x%08X\n", stat); + ret = -EIO; + } + } + //0x1000->1C,0x100->0.1,0x10-> -, 0x1->16 + if(command == 4) + data->word = 0xa005; //crit 90C + if(command == 3) + data->word = 0x801d; //low -40C + if(command == 2) + data->word = 0x5; //high 80C + +xfer_out_release: + imc_channelpair_release(priv, i); + + return ret; +} +EXPORT_SYMBOL_GPL(imc_smbus_xfer); + +static const struct i2c_algorithm imc_smbus_algorithm = { + .smbus_xfer = imc_smbus_xfer, + .functionality = imc_func, +}; + +static void imc_instantiate_sensors(struct i2c_adapter *adapter, u8 presence) +{ + struct i2c_board_info info = {}; +#ifdef C11_ANNEX_K + strcpy_s(info.type, I2C_NAME_SIZE, DIMM_SENSOR_DRV); +#else + strcpy(info.type, DIMM_SENSOR_DRV); +#endif + info.addr = DIMM_SENSOR_BASE_ADR; + + /* + * Presence is a bit vector. Bits from right to left map into i2c slave + * addresses starting 0x18. + */ + while (presence) { + if (presence & 0x1) + i2c_new_client_device(adapter, &info); + info.addr++; + presence >>= 1; + } +} + +static int imc_init_channelpair(struct imc_priv *priv, int i, int socket) +{ + int err=0; + u32 val; + struct imc_channelpair *cp = &priv->channelpair[i]; + + i2c_set_adapdata(&cp->adapter, priv); + cp->adapter.owner = THIS_MODULE; + cp->adapter.algo = &imc_smbus_algorithm; + cp->adapter.dev.parent = &priv->pci_dev->dev; + cp->adapter.nr = IMC_I2C_BUS_START + i; + + pci_read_config_dword(priv->pci_dev, SMBCNTL(i), &val); + cp->can_write = !(val & SMBCNTL_DIS_WRT); + + /* + * A TSOD polling interval of > 0 tells us if CLTT mode is enabled on + * some channel pair. + * + * Is there a better way to check for CLTT mode? In particular, is + * there a way to distingush the mode on a channel pair basis? + */ + cp->cltt = (priv->pcu.tsod_polling_interval > 0); +#ifdef C11_ANNEX_K + if(snprintf_s(cp->adapter.name, sizeof(cp->adapter.name), sizeof(cp->adapter.name), + "iMC socket %d for channel pair %d-%d", socket, 2*i, 2*i+1) < 0) +#else + if(snprintf(cp->adapter.name, sizeof(cp->adapter.name), + "iMC socket %d for channel pair %d-%d", socket, 2*i, 2*i+1) < 0) +#endif + { + return -ENOMEM; + } + + // Change to use 200/201 as the designated I2C bus. + //err = i2c_add_adapter(&cp->adapter); + err = i2c_add_numbered_adapter(&cp->adapter); + if (err) + return err; + + /* For reasons unknown, TSOD_PRES_MASK is only set in CLTT mode. */ + if (cp->cltt) { + dev_info(&priv->pci_dev->dev, + "CLTT is enabled on channel pair %d-%d. Thermal sensors will be automatically enabled\n", + 2*i, 2*i+1); + } else { + dev_info(&priv->pci_dev->dev, + "CLTT is disabled on channel pair %d-%d. Thermal sensors need to be manually enabled\n", + 2*i, 2*i+1); + } + + imc_instantiate_sensors(&cp->adapter, val & SMBCNTL_TSOD_PRES_MASK); + + return 0; +} + +static void imc_free_channelpair(struct imc_priv *priv, int i) +{ + struct imc_channelpair *cp = &priv->channelpair[i]; + + i2c_del_adapter(&cp->adapter); +} + +static struct pci_dev *imc_get_related_device(struct pci_bus *bus, + unsigned int devfn, u16 devid) +{ + struct pci_dev *dev = pci_get_slot(bus, devfn); + + if (!dev) + return NULL; + + if (dev->vendor != PCI_VENDOR_ID_INTEL || dev->device != devid) { + pci_dev_put(dev); + return NULL; + } + return dev; +} + +static int imc_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + int i, j, err; + struct imc_priv *priv; + struct pci_dev *sad; /* System Address Decoder */ + u32 sadcntl; + + /* Sanity check. This device is always at 0x13.0 */ + if (dev->devfn != PCI_DEVFN(0x13, 0)) + return -ENODEV; + + priv = devm_kzalloc(&dev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + priv->pci_dev = dev; + pci_set_drvdata(dev, priv); + + /* + * From sad, we learn the local node id of the socket. + * + * The socket will not change at runtime and so we throw away sad. + */ + sad = imc_get_related_device(dev->bus, PCI_DEVFN(0x0f, 5), + PCI_DEVICE_ID_INTEL_BROADWELL_IMC_SAD); + if (!sad) { + err = -ENODEV; + goto probe_out_free; + } + pci_read_config_dword(sad, SADCNTL, &sadcntl); + pci_dev_put(sad); + + /* + * From pcu, we access the CLTT polling interval. + * + * The polling interval is set by BIOS. We assume it will not change at + * runtime and cache the initial value. + */ + priv->pcu.pci_dev = imc_get_related_device(dev->bus, PCI_DEVFN(0x1e, 1), + PCI_DEVICE_ID_INTEL_BROADWELL_IMC_PCU); + if (!priv->pcu.pci_dev) { + err = -ENODEV; + goto probe_out_free; + } + pci_read_config_dword(priv->pcu.pci_dev, TSODCNTL, + &priv->pcu.tsod_polling_interval); + + mutex_init(&priv->pcu.mutex); + + for (i = 0; i < 2; i++) { + err = imc_init_channelpair(priv, i, + sadcntl & SADCNTL_LOCAL_NODEID_MASK); + if (err) + { + goto probe_out_free_channelpair; + } + } + + return 0; + +probe_out_free_channelpair: + for (j = 0; j < i; j++) + imc_free_channelpair(priv, j); + + mutex_destroy(&priv->pcu.mutex); + +probe_out_free: + kfree(priv); + return err; +} + +static void imc_remove(struct pci_dev *dev) +{ + int i; + struct imc_priv *priv = pci_get_drvdata(dev); + + for (i = 0; i < 2; i++) + imc_free_channelpair(priv, i); + + /* set tosd_control.tsod_polling_interval to initial value */ + pci_write_config_dword(priv->pcu.pci_dev, TSODCNTL, + priv->pcu.tsod_polling_interval); + + mutex_destroy(&priv->pcu.mutex); +} + +static int imc_suspend(struct pci_dev *dev, pm_message_t mesg) +{ + struct imc_priv *priv = pci_get_drvdata(dev); + + /* BIOS is in charge. We should finish any pending transaction */ + priv->suspended = true; + + return 0; +} + +static int imc_resume(struct pci_dev *dev) +{ + struct imc_priv *priv = pci_get_drvdata(dev); + + priv->suspended = false; + + return 0; +} + +static const struct pci_device_id imc_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_BROADWELL_IMC_TA) }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, imc_ids); + +static struct pci_driver imc_pci_driver = { + .name = "imc_smbus", + .id_table = imc_ids, + .probe = imc_probe, + .remove = imc_remove, + .suspend = imc_suspend, + .resume = imc_resume, +}; + +static int __init i2c_imc_init(void) +{ + return pci_register_driver(&imc_pci_driver); +} +module_init(i2c_imc_init); + +static void __exit i2c_imc_exit(void) +{ + pci_unregister_driver(&imc_pci_driver); +} +module_exit(i2c_imc_exit); + +MODULE_AUTHOR("Stefan Schaeckeler "); +MODULE_DESCRIPTION("iMC SMBus driver"); +MODULE_LICENSE("GPL v2"); diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/intel-spi-platform.c b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/intel-spi-platform.c new file mode 100644 index 0000000000..5c943df939 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/intel-spi-platform.c @@ -0,0 +1,57 @@ +/* + * Intel PCH/PCU SPI flash platform driver. + * + * Copyright (C) 2016, Intel Corporation + * Author: Mika Westerberg + * + * 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. + */ + +#include +#include +#include + +#include "intel-spi.h" + +static int intel_spi_platform_probe(struct platform_device *pdev) +{ + struct intel_spi_boardinfo *info; + struct intel_spi *ispi; + struct resource *mem; + + info = dev_get_platdata(&pdev->dev); + if (!info) + return -EINVAL; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ispi = intel_spi_probe(&pdev->dev, mem, info); + if (IS_ERR(ispi)) + return PTR_ERR(ispi); + + platform_set_drvdata(pdev, ispi); + return 0; +} + +static int intel_spi_platform_remove(struct platform_device *pdev) +{ + struct intel_spi *ispi = platform_get_drvdata(pdev); + + return intel_spi_remove(ispi); +} + +static struct platform_driver intel_spi_platform_driver = { + .probe = intel_spi_platform_probe, + .remove = intel_spi_platform_remove, + .driver = { + .name = "intel-spi", + }, +}; + +module_platform_driver(intel_spi_platform_driver); + +MODULE_DESCRIPTION("Intel PCH/PCU SPI flash platform driver"); +MODULE_AUTHOR("Mika Westerberg "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:intel-spi"); diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/intel-spi.h b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/intel-spi.h new file mode 100644 index 0000000000..5ab7dc2500 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/intel-spi.h @@ -0,0 +1,24 @@ +/* + * Intel PCH/PCU SPI flash driver. + * + * Copyright (C) 2016, Intel Corporation + * Author: Mika Westerberg + * + * 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. + */ + +#ifndef INTEL_SPI_H +#define INTEL_SPI_H + +#include + +struct intel_spi; +struct resource; + +struct intel_spi *intel_spi_probe(struct device *dev, + struct resource *mem, const struct intel_spi_boardinfo *info); +int intel_spi_remove(struct intel_spi *ispi); + +#endif /* INTEL_SPI_H */ diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/mp2975.c b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/mp2975.c new file mode 100644 index 0000000000..eeb8195a87 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/mp2975.c @@ -0,0 +1,1250 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Hardware monitoring driver for MPS Multi-phase Digital VR Controllers + * + * Copyright (c) 2020 Nvidia Technologies. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include "pmbus.h" +#ifdef C11_ANNEX_K +#include "libboundscheck/include/securec.h" +#endif +#include "switch_avs_driver.h" + +/* Vendor specific registers. */ +#define MP2975_MFR_APS_HYS_R2 0x0d +#define MP2975_MFR_SLOPE_TRIM3 0x1d +#define MP2975_MFR_VR_MULTI_CONFIG_R1 0x0d +#define MP2975_MFR_VR_MULTI_CONFIG_R2 0x1d +#define MP2975_MFR_APS_DECAY_ADV 0x56 +#define MP2975_MFR_DC_LOOP_CTRL 0x59 +#define MP2975_MFR_OCP_UCP_PHASE_SET 0x65 +#define MP2975_MFR_VR_CONFIG1 0x68 +#define MP2975_MFR_READ_CS1_2 0x82 +#define MP2975_MFR_READ_CS3_4 0x83 +#define MP2975_MFR_READ_CS5_6 0x84 +#define MP2975_MFR_READ_CS7_8 0x85 +#define MP2975_MFR_READ_CS9_10 0x86 +#define MP2975_MFR_READ_CS11_12 0x87 +#define MP2975_MFR_READ_IOUT_PK 0x90 +#define MP2975_MFR_READ_POUT_PK 0x91 +#define MP2975_MFR_READ_VREF_R1 0xa1 +#define MP2975_MFR_READ_VREF_R2 0xa3 +#define MP2975_MFR_OVP_TH_SET 0xe5 +#define MP2975_MFR_UVP_SET 0xe6 + +#define MP2975_MFR_SYS_CONFIG 0x44 + +#define MP2975_READ_CS1_R1 0xd0 +#define MP2975_READ_CS2_R1 0xd1 +#define MP2975_READ_CS3_R1 0xd2 +#define MP2975_READ_CS4_R1 0xd3 +#define MP2975_READ_CS5_R1 0xd4 +#define MP2975_READ_CS6_R1 0xd5 +#define MP2975_READ_CS7_R1 0xd6 +#define MP2975_READ_CS8_R1 0xd7 +#define MP2975_READ_CS9_R1 0xd8 +#define MP2975_READ_CS10_R1 0xd9 +#define MP2975_READ_CS11_R1 0xda +#define MP2975_READ_CS12_R1 0xdb +#define MP2975_READ_CS13_R1 0xdc +#define MP2975_READ_CS14_R1 0xdd +#define MP2975_READ_CS15_R1 0xde +#define MP2975_READ_CS16_R1 0xdf +#define MP2975_PH_REDUNDANCY_NUM1 0xe7 +#define MP2975_PH_REDUNDANCY_NUM2 0xe8 +#define MP2975_MFR_PROTEC_PH_R1 0xe9 +#define MP2975_MFR_PROTEC_PH_R2 0xea + +#define MP2975_READ_CS1_R2 0xd0 +#define MP2975_READ_CS2_R2 0xd1 +#define MP2975_READ_CS3_R2 0xd2 +#define MP2975_READ_CS4_R2 0xd3 +#define MP2975_FAULT_REPORT0 0xd9 +#define MP2975_FAULT_REPORT1 0xda +#define MP2975_FAULT_REPORT2 0xdb +#define MP2975_FAULT_REPORT3 0xdc +#define MP2975_FAULT_REPORT4 0xdd +#define MP2975_FAULT_REPORT5 0xde +#define MP2975_FAULT_REPORT6 0xdf +#define MP2975_FAULT_TIME0 0xe0 +#define MP2975_FAULT_TIME1 0xe1 +#define MP2975_FAULT_TIME2 0xe2 +#define MP2975_FAULT_IMON1_SENSE 0xe3 +#define MP2975_FAULT_IMON2_SENSE 0xe4 +#define MP2975_FAULT_VDIFF1_SENSE 0xe5 +#define MP2975_FAULT_VDIFF2_SENSE 0xe6 +#define MP2975_FAULT_VIN_SENSE 0xe7 +#define MP2975_FAULT_1st_RECORD 0xe8 +#define MP2975_FAULT_RECORD0 0xe9 +#define MP2975_FAULT_RECORD1 0xea +#define MP2975_FAULT_RECORD2 0xeb +#define MP2975_FAULT_RECORD3 0xec +#define MP2975_FAULT_RECORD4 0xed +#define MP2975_FAULT_RECORD5 0xee +#define MP2975_FAULT_RECORD6 0xef + +#define MP2975_VOUT_FORMAT BIT(15) +#define MP2975_VID_STEP_SEL_R1 BIT(4) +#define MP2975_IMVP9_EN_R1 BIT(13) +#define MP2975_VID_STEP_SEL_R2 BIT(3) +#define MP2975_IMVP9_EN_R2 BIT(12) +#define MP2975_PRT_THRES_DIV_OV_EN BIT(14) +#define MP2975_DRMOS_KCS GENMASK(13, 12) +#define MP2975_PROT_DEV_OV_OFF 10 +#define MP2975_PROT_DEV_OV_ON 5 +#define MP2975_SENSE_AMPL BIT(11) +#define MP2975_SENSE_AMPL_UNIT 1 +#define MP2975_SENSE_AMPL_HALF 2 +#define MP2975_VIN_UV_LIMIT_UNIT 8 + +#define MP2975_PSC_VOLTAGE_OUT 0x40 +#define MP2975_MAX_PHASE_RAIL1 8 +#define MP2975_MAX_PHASE_RAIL2 4 +#define MP2975_PAGE_NUM 2 + +#define MP2975_RAIL2_FUNC (PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | \ + PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT) + +struct i2c_client *mp2975_client[TOTAL_MP2975_NUM] = {NULL}; + +struct mp2975_data { + struct pmbus_driver_info info; + int vout_scale; + int vid_step[MP2975_PAGE_NUM]; + int vref[MP2975_PAGE_NUM]; + int vref_off[MP2975_PAGE_NUM]; + int vout_max[MP2975_PAGE_NUM]; + int vout_ov_fixed[MP2975_PAGE_NUM]; + int vout_format[MP2975_PAGE_NUM]; + int curr_sense_gain[MP2975_PAGE_NUM]; +}; + +#define to_mp2975_data(x) container_of(x, struct mp2975_data, info) + +static long mp2975_reg2data_direct(s64 val, struct pmbus_driver_info *info, int sensor_class) +{ + s64 b; + s32 m, R; + + m = info->m[sensor_class]; + b = info->b[sensor_class]; + R = info->R[sensor_class]; + + if (m == 0) + return 0; + + /* X = 1/m * (Y * 10^-R - b) */ + R = -R; + /* scale result to milli-units for everything but fans */ + if (!(sensor_class == PSC_FAN || sensor_class == PSC_PWM)) { + R += 3; + b *= 1000; + } + + /* scale result to micro-units for power sensors */ + if (sensor_class == PSC_POWER) { + R += 3; + b *= 1000; + } + + while (R > 0) { + val *= 10; + R--; + } + while (R < 0) { + val = div_s64(val + 5LL, 10L); /* round closest */ + R++; + } + + val = div_s64(val - b, m); + return clamp_val(val, LONG_MIN, LONG_MAX); +} + +static int mp2975_read_byte_data(struct i2c_client *client, int page, int reg) +{ + switch (reg) { + case PMBUS_VOUT_MODE: + /* + * Enforce VOUT direct format, since device allows to set the + * different formats for the different rails. Conversion from + * VID to direct provided by driver internally, in case it is + * necessary. + */ + return MP2975_PSC_VOLTAGE_OUT; + default: + return -ENODATA; + } +} + +static int +mp2975_read_word_helper(struct i2c_client *client, int page, int phase, u8 reg, + u16 mask) +{ + int ret = pmbus_read_word_data(client, page, phase, reg); + + return (ret > 0) ? ret & mask : ret; +} + +static int +mp2975_vid2direct(int vrf, int val) +{ + switch (vrf) { + case vr12: + if (val >= 0x01) + return 250 + (val - 1) * 5; + break; + case vr13: + if (val >= 0x01) + return 500 + (val - 1) * 10; + break; + case imvp9: + if (val >= 0x01) + return 200 + (val - 1) * 10; + break; + default: + return -EINVAL; + } + return 0; +} + +static int +mp2975_read_phase(struct i2c_client *client, struct mp2975_data *data, + int page, int phase, u8 reg) +{ + u16 mask; + int shift = 0, ret; + + if ((phase + 1) % MP2975_PAGE_NUM) { + mask = GENMASK(7, 0); + } else { + mask = GENMASK(15, 8); + shift = 8; + } + + ret = mp2975_read_word_helper(client, page, phase, reg, mask); + if (ret < 0) + return ret; + + ret >>= shift; + + /* + * Output value is calculated as: (READ_CSx / 80 �V 1.23) / (Kcs * Rcs) + * where: + * - Kcs is the DrMOS current sense gain of power stage, which is + * obtained from the register MP2975_MFR_VR_CONFIG1, bits 13-12 with + * the following selection of DrMOS (data->curr_sense_gain[page]): + * 00b - 5�gA/A, 01b - 8.5�gA/A, 10b - 9.7�gA/A, 11b - 10�gA/A. + * - Rcs is the internal phase current sense resistor which is constant + * value 1k?. + */ + return DIV_ROUND_CLOSEST(DIV_ROUND_CLOSEST(ret * 100 - 9840, 100) * + 100, data->curr_sense_gain[page]); +} + +static int +mp2975_read_phases(struct i2c_client *client, struct mp2975_data *data, + int page, int phase) +{ + int ret; + + if (page) { + switch (phase) { + case 0 ... 1: + ret = mp2975_read_phase(client, data, page, phase, + MP2975_MFR_READ_CS7_8); + break; + case 2 ... 3: + ret = mp2975_read_phase(client, data, page, phase, + MP2975_MFR_READ_CS9_10); + break; + case 4 ... 5: + ret = mp2975_read_phase(client, data, page, phase, + MP2975_MFR_READ_CS11_12); + break; + default: + return -ENODATA; + } + } else { + switch (phase) { + case 0 ... 1: + ret = mp2975_read_phase(client, data, page, phase, + MP2975_MFR_READ_CS1_2); + break; + case 2 ... 3: + ret = mp2975_read_phase(client, data, page, phase, + MP2975_MFR_READ_CS3_4); + break; + case 4 ... 5: + ret = mp2975_read_phase(client, data, page, phase, + MP2975_MFR_READ_CS5_6); + break; + case 6 ... 7: + ret = mp2975_read_phase(client, data, page, phase, + MP2975_MFR_READ_CS7_8); + break; + case 8 ... 9: + ret = mp2975_read_phase(client, data, page, phase, + MP2975_MFR_READ_CS9_10); + break; + case 10 ... 11: + ret = mp2975_read_phase(client, data, page, phase, + MP2975_MFR_READ_CS11_12); + break; + default: + //printk(KERN_ALERT "phase = %d\n", phase); + return -ENODATA; + } + } + return ret; +} +EXPORT_SYMBOL_GPL(mp2975_read_phases); + +int mp2975_read_word_data(struct i2c_client *client, int page, int phase, + int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct mp2975_data *data = to_mp2975_data(info); + int ret; + + switch (reg) { + case PMBUS_OT_FAULT_LIMIT: + ret = mp2975_read_word_helper(client, page, phase, reg, + GENMASK(7, 0)); + break; + case PMBUS_VIN_OV_FAULT_LIMIT: + ret = mp2975_read_word_helper(client, page, phase, reg, + GENMASK(7, 0)); + if (ret < 0) + return ret; + + ret = DIV_ROUND_CLOSEST(ret, MP2975_VIN_UV_LIMIT_UNIT); + break; + case PMBUS_VOUT_OV_FAULT_LIMIT: + /* + * Register provides two values for over-voltage protection + * threshold for fixed (ovp2) and tracking (ovp1) modes. The + * minimum of these two values is provided as over-voltage + * fault alarm. + */ + ret = mp2975_read_word_helper(client, page, phase, + MP2975_MFR_OVP_TH_SET, + GENMASK(2, 0)); + if (ret < 0) + return ret; + + ret = min_t(int, data->vout_max[page] + 50 * (ret + 1), + data->vout_ov_fixed[page]); + break; + case PMBUS_VOUT_UV_FAULT_LIMIT: + ret = mp2975_read_word_helper(client, page, phase, + MP2975_MFR_UVP_SET, + GENMASK(2, 0)); + if (ret < 0) + return ret; + + ret = DIV_ROUND_CLOSEST(data->vref[page] * 10 - 50 * + (ret + 1) * data->vout_scale, 10); + break; + case PMBUS_READ_VOUT: + ret = mp2975_read_word_helper(client, page, phase, reg, + GENMASK(11, 0)); + if (ret < 0) + return ret; + + /* + * READ_VOUT can be provided in VID or direct format. The + * format type is specified by bit 15 of the register + * MP2975_MFR_DC_LOOP_CTRL. The driver enforces VOUT direct + * format, since device allows to set the different formats for + * the different rails and also all VOUT limits registers are + * provided in a direct format. In case format is VID - convert + * to direct. + */ + if (data->vout_format[page] == vid) + ret = mp2975_vid2direct(info->vrm_version[page], ret); + break; + case PMBUS_VIRT_READ_POUT_MAX: + ret = mp2975_read_word_helper(client, page, phase, + MP2975_MFR_READ_POUT_PK, + GENMASK(12, 0)); + if (ret < 0) + return ret; + + ret = DIV_ROUND_CLOSEST(ret, 4); + break; + case PMBUS_VIRT_READ_IOUT_MAX: + ret = mp2975_read_word_helper(client, page, phase, + MP2975_MFR_READ_IOUT_PK, + GENMASK(12, 0)); + if (ret < 0) + return ret; + + ret = DIV_ROUND_CLOSEST(ret, 4); + break; + case PMBUS_READ_IOUT: + ret = mp2975_read_phases(client, data, page, phase); + if (ret < 0) + return ret; + + break; + case PMBUS_UT_WARN_LIMIT: + case PMBUS_UT_FAULT_LIMIT: + case PMBUS_VIN_UV_WARN_LIMIT: + case PMBUS_VIN_UV_FAULT_LIMIT: + case PMBUS_VOUT_UV_WARN_LIMIT: + case PMBUS_VOUT_OV_WARN_LIMIT: + case PMBUS_VIN_OV_WARN_LIMIT: + case PMBUS_IIN_OC_FAULT_LIMIT: + case PMBUS_IOUT_OC_LV_FAULT_LIMIT: + case PMBUS_IIN_OC_WARN_LIMIT: + case PMBUS_IOUT_OC_WARN_LIMIT: + case PMBUS_IOUT_OC_FAULT_LIMIT: + case PMBUS_IOUT_UC_FAULT_LIMIT: + case PMBUS_POUT_OP_FAULT_LIMIT: + case PMBUS_POUT_OP_WARN_LIMIT: + case PMBUS_PIN_OP_WARN_LIMIT: + return -ENXIO; + default: + return -ENODATA; + } + + return ret; +} +EXPORT_SYMBOL_GPL(mp2975_read_word_data); + +int mp2975_fmea_get_vout_str(unsigned int nr, int page, char *buf) +{ + struct i2c_adapter *adapter; + const struct pmbus_driver_info *info; + struct mp2975_data *data; + int ret; + int val; + int i; + + for(i=0; idev.parent); + if(adapter->nr == nr) + { + info = pmbus_get_driver_info(mp2975_client[i]); + data = to_mp2975_data(info); + + ret = mp2975_read_word_data(mp2975_client[i], page, 0xff, PMBUS_READ_VOUT); + if(ret < 0) + { + printk(KERN_ALERT "MP2975 read page %d PMBUS_READ_VOUT failed\n", page); + return ret; + } + + val = mp2975_reg2data_direct(ret, &data->info, PSC_VOLTAGE_OUT); +#ifdef C11_ANNEX_K + return sprintf_s(buf, I2C_SMBUS_BLOCK_MAX, "%d", val); +#else + return sprintf(buf, "%d", val); +#endif + } + } + } + + return -1; +} +EXPORT_SYMBOL_GPL(mp2975_fmea_get_vout_str); + +int mp2975_fmea_get_iout_str(unsigned int nr, int page, char *buf) +{ + const struct pmbus_driver_info *info; + struct mp2975_data *data; + int ret; + int val; + int i; + + for(i=0; iadapter->nr == nr) + { + info = pmbus_get_driver_info(mp2975_client[i]); + data = to_mp2975_data(info); + + ret = pmbus_read_word_data(mp2975_client[i], page, 0xff, PMBUS_READ_IOUT); + if(ret < 0) + { + printk(KERN_ALERT "MP2975 read page %d PMBUS_READ_IOUT failed\n", page); + return ret; + } + + val = mp2975_reg2data_direct(ret, &data->info, PSC_CURRENT_OUT); +#ifdef C11_ANNEX_K + return sprintf_s(buf, I2C_SMBUS_BLOCK_MAX, "%d", val); +#else + return sprintf(buf, "%d", val); +#endif + + } + } + } + + return -1; +} +EXPORT_SYMBOL_GPL(mp2975_fmea_get_iout_str); + +int mp2975_fmea_get_temp1_str(unsigned int nr, int page, char *buf) +{ + const struct pmbus_driver_info *info; + struct mp2975_data *data; + int ret; + int val; + int i; + + for(i=0; iadapter->nr == nr) + { + info = pmbus_get_driver_info(mp2975_client[i]); + data = to_mp2975_data(info); + + ret = pmbus_read_word_data(mp2975_client[i], page, 0xff, PMBUS_READ_TEMPERATURE_1); + if(ret < 0) + { + printk(KERN_ALERT "MP2975 read page %d PMBUS_READ_TEMPERATURE_1 failed\n", page); + return ret; + } + + val = mp2975_reg2data_direct(ret, &data->info, PSC_TEMPERATURE); +#ifdef C11_ANNEX_K + return sprintf_s(buf, I2C_SMBUS_BLOCK_MAX, "%d", val); +#else + return sprintf(buf, "%d", val); +#endif + + } + } + } + + return -1; +} +EXPORT_SYMBOL_GPL(mp2975_fmea_get_temp1_str); + +int mp2975_fmea_get_mfr_id(unsigned int nr, int page) +{ + int ret; + int i; + + for(i=0; iadapter->nr == nr) + { + ret = pmbus_set_page(mp2975_client[i], page, 0xff); + if(ret < 0) + { + printk(KERN_ALERT "MP2975 set page %d failed\n", page); + return ret; + } + ret = pmbus_read_word_data(mp2975_client[i], page, 0xff, PMBUS_MFR_ID); + + return ret; + } + } + } + + return -1; +} +EXPORT_SYMBOL_GPL(mp2975_fmea_get_mfr_id); + +int mp2975_fmea_read_reg_str(unsigned int nr, int page, int reg, char *buf) +{ + int ret=0; + char val[I2C_SMBUS_BLOCK_MAX] = {0}; + char tmp1[3] = {0}; + char tmp2[I2C_SMBUS_BLOCK_MAX] = {0}; + int i; + + for(i=0; iadapter->nr == nr) + { + if(page == 0) + { + switch (reg) + { + case PMBUS_MFR_DATE: + ret = pmbus_set_page(mp2975_client[i], page, 0xff); + if(ret < 0) + { + printk(KERN_ALERT "MP2975 set page %d failed\n", page); + return ret; + } + ret = i2c_smbus_read_i2c_block_data(mp2975_client[i], reg, 6+1, val); + // ASCII to Hex, ignore first byte, which is length + for(i = 1; i < ret; i++) + { +#ifdef C11_ANNEX_K + if(sprintf_s(tmp1, 3, "%02x", val[i]) < 0) +#else + if(sprintf(tmp1, "%02x", val[i]) < 0) +#endif + { + return -ENOMEM; + } +#ifdef C11_ANNEX_K + if(strcat_s(tmp2, I2C_SMBUS_BLOCK_MAX, tmp1) != 0) +#else + if(strcat(tmp2, tmp1) != 0) +#endif + { + return -ENOMEM; + } + } +#ifdef C11_ANNEX_K + return sprintf_s(buf, I2C_SMBUS_BLOCK_MAX, "0x%s", tmp2); +#else + return sprintf(buf, "0x%s", tmp2); +#endif + break; + + case PMBUS_VOUT_COMMAND: + case PMBUS_STATUS_WORD: + case PMBUS_READ_VIN: + case PMBUS_READ_VOUT: + case PMBUS_READ_IOUT: + case PMBUS_READ_TEMPERATURE_1: + case PMBUS_READ_POUT: + case PMBUS_MFR_ID: + case PMBUS_MFR_MODEL: + case MP2975_READ_CS1_R1: + case MP2975_READ_CS2_R1: + case MP2975_READ_CS3_R1: + case MP2975_READ_CS4_R1: + case MP2975_READ_CS5_R1: + case MP2975_READ_CS6_R1: + case MP2975_READ_CS7_R1: + case MP2975_READ_CS8_R1: + case MP2975_READ_CS9_R1: + case MP2975_READ_CS10_R1: + case MP2975_READ_CS11_R1: + case MP2975_READ_CS12_R1: + case MP2975_READ_CS13_R1: + case MP2975_READ_CS14_R1: + case MP2975_READ_CS15_R1: + case MP2975_READ_CS16_R1: + case MP2975_PH_REDUNDANCY_NUM1: + case MP2975_PH_REDUNDANCY_NUM2: + case MP2975_MFR_PROTEC_PH_R1: + case MP2975_MFR_PROTEC_PH_R2: + ret = pmbus_set_page(mp2975_client[i], page, 0xff); + ret = pmbus_read_word_data(mp2975_client[i], page, 0xff, reg); + if(ret < 0) + { + //printk(KERN_ALERT "MP2975 read page %d reg 0x%x failed\n", page, reg); + return ret; + } +#ifdef C11_ANNEX_K + return sprintf_s(buf, I2C_SMBUS_BLOCK_MAX, "0x%04x", ret); +#else + return sprintf(buf, "0x%04x", ret); +#endif + break; + + case PMBUS_VOUT_MODE: + case PMBUS_STATUS_VOUT: + case PMBUS_STATUS_IOUT: + case PMBUS_STATUS_INPUT: + case PMBUS_STATUS_TEMPERATURE: + case PMBUS_STATUS_CML: + case PMBUS_STATUS_MFR_SPECIFIC: + case MP2975_MFR_READ_CS1_2: + case MP2975_MFR_READ_CS3_4: + case MP2975_MFR_READ_CS5_6: + case PMBUS_READ_FAN_SPEED_1: + case PMBUS_READ_FAN_SPEED_2: + case 0xa4: + case 0xa5: + case 0xf4: + ret = pmbus_set_page(mp2975_client[i], page, 0xff); + ret = pmbus_read_byte_data(mp2975_client[i], page, reg); + if(ret < 0) + { + printk(KERN_ALERT "MP2975 read page %d reg 0x%x failed\n", page, reg); + return ret; + } +#ifdef C11_ANNEX_K + return sprintf_s(buf, I2C_SMBUS_BLOCK_MAX, "0x%02x", ret); +#else + return sprintf(buf, "0x%02x", ret); +#endif + + break; + + default: + break; + } + } + + return ret; + } + } + } + + return -1; +} +EXPORT_SYMBOL_GPL(mp2975_fmea_read_reg_str); + +int mp2975_fmea_clear_fault(unsigned int nr) +{ + int i; + + for(i=0; iadapter->nr == nr) + { + pmbus_clear_faults(mp2975_client[i]); + + return 0; + } + } + } + + return -1; +} +EXPORT_SYMBOL_GPL(mp2975_fmea_clear_fault); + +static int mp2975_identify_multiphase_rail2(struct i2c_client *client) +{ + int ret; + + /* + * Identify multiphase for rail 2 - could be from 0 to 4. + * In case phase number is zero �V only page zero is supported + */ + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 2); + if (ret < 0) + return ret; + + /* Identify multiphase for rail 2 - could be from 0 to 4. */ + ret = i2c_smbus_read_word_data(client, MP2975_MFR_VR_MULTI_CONFIG_R2); + if (ret < 0) + return ret; + + ret &= GENMASK(2, 0); + return (ret >= 4) ? 4 : ret; +} +#if 0 +static void mp2975_set_phase_rail1(struct pmbus_driver_info *info) +{ + int i; + + for (i = 0 ; i < info->phases[0]; i++) + info->pfunc[i] = PMBUS_HAVE_IOUT; +} + +static void mp2975_set_phase_rail2(struct pmbus_driver_info *info) +{ + int max_rail, i; + + /* Set phases for rail 2 from upper to lower. */ + max_rail = info->phases[1] % (MP2975_MAX_PHASE_RAIL2 - 1); + for (i = 1 ; i <= max_rail; i++) + info->pfunc[MP2975_MAX_PHASE_RAIL1 - i] = PMBUS_HAVE_IOUT; +} + +static int mp2975_set_multiphase_rail2(struct pmbus_driver_info *info) +{ + switch (info->phases[1]) { + case 1 ... 7: + mp2975_set_phase_rail2(info); + return 0; + default: + return -EINVAL; + } +} + +static int +mp2975_identify_multiphase(struct i2c_client *client, struct mp2975_data *data, + struct pmbus_driver_info *info) +{ + int ret; + + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 2); + if (ret < 0) + return ret; + + /* Identify multiphase for rail 1 - could be from 1 to 8. */ + ret = i2c_smbus_read_word_data(client, MP2975_MFR_VR_MULTI_CONFIG_R1); + if (ret > 0) + info->phases[0] = ret & GENMASK(3, 0); + else + return (ret) ? ret : -EINVAL; + + /* + * The device provides a total of 8 PWM pins, and can be configured + * to different phase count applications for rail 1 and rail 2. + * Rail 1 can be set to 8 phases, while rail 2 can only be set to 4 + * phases at most. When rail 1��s phase count is configured as 0, rail + * 1 operates with 1-phase DCM. When rail 2 phase count is configured + * as 0, rail 2 is disabled. + */ + switch (info->phases[0]) { + case 1 ... 4: + mp2975_set_phase_rail1(info); + return mp2975_set_multiphase_rail2(info); + case 5: + mp2975_set_phase_rail1(info); + switch (info->phases[1]) { + case 1 ... 3: + return mp2975_set_multiphase_rail2(info); + default: + return 0; + } + case 6: + mp2975_set_phase_rail1(info); + switch (info->phases[1]) { + case 1 ... 2: + return mp2975_set_multiphase_rail2(info); + default: + return 0; + } + case 7: + mp2975_set_phase_rail1(info); + switch (info->phases[1]) { + case 1: + return mp2975_set_multiphase_rail2(info); + default: + return 0; + } + case 8: + mp2975_set_phase_rail1(info); + return 0; + default: + return -EINVAL; + } +} +#endif +static int +mp2975_identify_vid(struct i2c_client *client, struct mp2975_data *data, + struct pmbus_driver_info *info, u32 reg, int page, + u32 imvp_bit, u32 vr_bit) +{ + int ret; + + /* Identify VID mode and step selection. */ + ret = i2c_smbus_read_word_data(client, reg); + if (ret < 0) + return ret; + + if (ret & imvp_bit) { + info->vrm_version[page] = imvp9; + data->vid_step[page] = MP2975_PROT_DEV_OV_OFF; + } else if (ret & vr_bit) { + info->vrm_version[page] = vr12; + data->vid_step[page] = MP2975_PROT_DEV_OV_ON; + } else { + info->vrm_version[page] = vr13; + data->vid_step[page] = MP2975_PROT_DEV_OV_OFF; + } + + return 0; +} + +static int +mp2975_identify_rails_vid(struct i2c_client *client, struct mp2975_data *data, + struct pmbus_driver_info *info) +{ + int ret; + + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 2); + if (ret < 0) + return ret; + + /* Identify VID mode for rail 1. */ + ret = mp2975_identify_vid(client, data, info, + MP2975_MFR_VR_MULTI_CONFIG_R1, 0, + MP2975_IMVP9_EN_R1, MP2975_VID_STEP_SEL_R1); + if (ret < 0) + return ret; + + /* Identify VID mode for rail 2, if connected. */ + if (info->pages == MP2975_PAGE_NUM) + ret = mp2975_identify_vid(client, data, info, + MP2975_MFR_VR_MULTI_CONFIG_R2, 1, + MP2975_IMVP9_EN_R2, + MP2975_VID_STEP_SEL_R2); + return ret; +} + +static int +mp2975_current_sense_gain_get(struct i2c_client *client, + struct mp2975_data *data) +{ + int i, ret; + + /* + * Obtain DrMOS current sense gain of power stage from the register + * MP2975_MFR_VR_CONFIG1, bits 13-12. The value is selected as below: + * 00b - 5�gA/A, 01b - 8.5�gA/A, 10b - 9.7�gA/A, 11b - 10�gA/A. Other + * values are invalid. + */ + for (i = 0 ; i < data->info.pages; i++) { + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, i); + if (ret < 0) + return ret; + ret = i2c_smbus_read_word_data(client, + MP2975_MFR_VR_CONFIG1); + if (ret < 0) + return ret; + + switch ((ret & MP2975_DRMOS_KCS) >> 12) { + case 0: + data->curr_sense_gain[i] = 50; + break; + case 1: + data->curr_sense_gain[i] = 85; + break; + case 2: + data->curr_sense_gain[i] = 97; + break; + default: + data->curr_sense_gain[i] = 100; + break; + } + } + + return 0; +} + +static int +mp2975_vref_get(struct i2c_client *client, struct mp2975_data *data, + struct pmbus_driver_info *info) +{ + int ret; + + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 3); + if (ret < 0) + return ret; + + /* Get voltage reference value for rail 1. */ + ret = i2c_smbus_read_word_data(client, MP2975_MFR_READ_VREF_R1); + if (ret < 0) + return ret; + + data->vref[0] = ret * data->vid_step[0]; + + /* Get voltage reference value for rail 2, if connected. */ + if (data->info.pages == MP2975_PAGE_NUM) { + ret = i2c_smbus_read_word_data(client, MP2975_MFR_READ_VREF_R2); + if (ret < 0) + return ret; + + data->vref[1] = ret * data->vid_step[1]; + } + return 0; +} + +static int +mp2975_vref_offset_get(struct i2c_client *client, struct mp2975_data *data, + int page) +{ + int ret; + + ret = i2c_smbus_read_word_data(client, MP2975_MFR_OVP_TH_SET); + if (ret < 0) + return ret; + + switch ((ret & GENMASK(5, 3)) >> 3) { + case 1: + data->vref_off[page] = 140; + break; + case 2: + data->vref_off[page] = 220; + break; + case 4: + data->vref_off[page] = 400; + break; + default: + return -EINVAL; + } + return 0; +} + +static int +mp2975_vout_max_get(struct i2c_client *client, struct mp2975_data *data, + struct pmbus_driver_info *info, int page) +{ + int ret; + + /* Get maximum reference voltage of VID-DAC in VID format. */ + ret = i2c_smbus_read_word_data(client, PMBUS_VOUT_MAX); + if (ret < 0) + return ret; + + data->vout_max[page] = mp2975_vid2direct(info->vrm_version[page], ret & + GENMASK(8, 0)); + return 0; +} + +static int +mp2975_identify_vout_format(struct i2c_client *client, + struct mp2975_data *data, int page) +{ + int ret; + + ret = i2c_smbus_read_word_data(client, MP2975_MFR_DC_LOOP_CTRL); + if (ret < 0) + return ret; + + if (ret & MP2975_VOUT_FORMAT) + data->vout_format[page] = vid; + else + data->vout_format[page] = direct; + return 0; +} + +static int +mp2975_vout_ov_scale_get(struct i2c_client *client, struct mp2975_data *data, + struct pmbus_driver_info *info) +{ + int thres_dev, sense_ampl, ret; + + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0); + if (ret < 0) + return ret; + + /* + * Get divider for over- and under-voltage protection thresholds + * configuration from the Advanced Options of Auto Phase Shedding and + * decay register. + */ + ret = i2c_smbus_read_word_data(client, MP2975_MFR_APS_DECAY_ADV); + if (ret < 0) + return ret; + thres_dev = ret & MP2975_PRT_THRES_DIV_OV_EN ? MP2975_PROT_DEV_OV_ON : + MP2975_PROT_DEV_OV_OFF; + + /* Select the gain of remote sense amplifier. */ + ret = i2c_smbus_read_word_data(client, PMBUS_VOUT_SCALE_LOOP); + if (ret < 0) + return ret; + sense_ampl = ret & MP2975_SENSE_AMPL ? MP2975_SENSE_AMPL_HALF : + MP2975_SENSE_AMPL_UNIT; + + data->vout_scale = sense_ampl * thres_dev; + + return 0; +} + +static int +mp2975_vout_per_rail_config_get(struct i2c_client *client, + struct mp2975_data *data, + struct pmbus_driver_info *info) +{ + int i, ret; + + for (i = 0; i < data->info.pages; i++) { + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, i); + if (ret < 0) + return ret; + + /* Obtain voltage reference offsets. */ + ret = mp2975_vref_offset_get(client, data, i); + if (ret < 0) + return ret; + + /* Obtain maximum voltage values. */ + ret = mp2975_vout_max_get(client, data, info, i); + if (ret < 0) + return ret; + + /* + * Get VOUT format for READ_VOUT command : VID or direct. + * Pages on same device can be configured with different + * formats. + */ + ret = mp2975_identify_vout_format(client, data, i); + if (ret < 0) + return ret; + + /* + * Set over-voltage fixed value. Thresholds are provided as + * fixed value, and tracking value. The minimum of them are + * exposed as over-voltage critical threshold. + */ + data->vout_ov_fixed[i] = data->vref[i] + + DIV_ROUND_CLOSEST(data->vref_off[i] * + data->vout_scale, + 10); + } + + return 0; +} + +static struct pmbus_driver_info mp2975_info = { + .pages = 1, + .format[PSC_VOLTAGE_IN] = linear, + .format[PSC_VOLTAGE_OUT] = direct, + .format[PSC_TEMPERATURE] = direct, + .format[PSC_CURRENT_IN] = linear, + .format[PSC_CURRENT_OUT] = direct, + .format[PSC_POWER] = direct, + .m[PSC_TEMPERATURE] = 1, + .m[PSC_VOLTAGE_OUT] = 1, + .R[PSC_VOLTAGE_OUT] = 3, + .m[PSC_CURRENT_OUT] = 1, + .m[PSC_POWER] = 1, + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | + PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | PMBUS_HAVE_POUT | + PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT, + .read_byte_data = mp2975_read_byte_data, + .read_word_data = mp2975_read_word_data, +}; + +int mp2975_probe(struct i2c_client *client) +{ + struct pmbus_driver_info *info; + struct mp2975_data *data; + int ret; + int i; + + data = devm_kzalloc(&client->dev, sizeof(struct mp2975_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; +#ifdef C11_ANNEX_K + if(memcpy_s(&data->info, sizeof(*info), &mp2975_info, sizeof(*info)) != 0) + { + return -ENOMEM; + } +#else + memcpy(&data->info, &mp2975_info, sizeof(*info)); +#endif + info = &data->info; + + /* Identify multiphase configuration for rail 2. */ + ret = mp2975_identify_multiphase_rail2(client); + if (ret < 0) + return ret; + + if (ret) + data->info.pages = MP2975_PAGE_NUM; + + if (ret) { + /* Two rails are connected. */ + data->info.pages = MP2975_PAGE_NUM; +#if 0 + data->info.phases[1] = ret; +#endif + data->info.func[1] = MP2975_RAIL2_FUNC; + } +#if 0 + /* Identify multiphase configuration. */ + ret = mp2975_identify_multiphase(client, data, info); + if (ret) + return ret; +#endif + /* Identify VID setting per rail. */ + ret = mp2975_identify_rails_vid(client, data, info); + if (ret < 0) + return ret; + + /* Obtain current sense gain of power stage. */ + ret = mp2975_current_sense_gain_get(client, data); + if (ret) + return ret; + + /* Obtain voltage reference values. */ + ret = mp2975_vref_get(client, data, info); + if (ret) + return ret; + + /* Obtain vout over-voltage scales. */ + ret = mp2975_vout_ov_scale_get(client, data, info); + if (ret < 0) + return ret; + + /* Obtain offsets, maximum and format for vout. */ + ret = mp2975_vout_per_rail_config_get(client, data, info); + if (ret) + return ret; + + for(i=0; i"); +MODULE_DESCRIPTION("PMBus driver for MPS MP2975 device"); +MODULE_LICENSE("GPL"); diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/pmbus.h b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/pmbus.h new file mode 100644 index 0000000000..5de88fb721 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/pmbus.h @@ -0,0 +1,549 @@ +/* + * pmbus.h - Common defines and structures for PMBus devices + * + * Copyright (c) 2010, 2011 Ericsson AB. + * Copyright (c) 2012 Guenter Roeck + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef PMBUS_H +#define PMBUS_H + +#include +#include + +/* + * Registers + */ +enum pmbus_regs { + PMBUS_PAGE = 0x00, + PMBUS_OPERATION = 0x01, + PMBUS_ON_OFF_CONFIG = 0x02, + PMBUS_CLEAR_FAULTS = 0x03, + PMBUS_PHASE = 0x04, + + PMBUS_WRITE_PROTECT = 0x10, + + PMBUS_CAPABILITY = 0x19, + PMBUS_QUERY = 0x1A, + + PMBUS_VOUT_MODE = 0x20, + PMBUS_VOUT_COMMAND = 0x21, + PMBUS_VOUT_TRIM = 0x22, + PMBUS_VOUT_CAL_OFFSET = 0x23, + PMBUS_VOUT_MAX = 0x24, + PMBUS_VOUT_MARGIN_HIGH = 0x25, + PMBUS_VOUT_MARGIN_LOW = 0x26, + PMBUS_VOUT_TRANSITION_RATE = 0x27, + PMBUS_VOUT_DROOP = 0x28, + PMBUS_VOUT_SCALE_LOOP = 0x29, + PMBUS_VOUT_SCALE_MONITOR = 0x2A, + + PMBUS_COEFFICIENTS = 0x30, + PMBUS_POUT_MAX = 0x31, + + PMBUS_FAN_CONFIG_12 = 0x3A, + PMBUS_FAN_COMMAND_1 = 0x3B, + PMBUS_FAN_COMMAND_2 = 0x3C, + PMBUS_FAN_CONFIG_34 = 0x3D, + PMBUS_FAN_COMMAND_3 = 0x3E, + PMBUS_FAN_COMMAND_4 = 0x3F, + + PMBUS_VOUT_OV_FAULT_LIMIT = 0x40, + PMBUS_VOUT_OV_FAULT_RESPONSE = 0x41, + PMBUS_VOUT_OV_WARN_LIMIT = 0x42, + PMBUS_VOUT_UV_WARN_LIMIT = 0x43, + PMBUS_VOUT_UV_FAULT_LIMIT = 0x44, + PMBUS_VOUT_UV_FAULT_RESPONSE = 0x45, + PMBUS_IOUT_OC_FAULT_LIMIT = 0x46, + PMBUS_IOUT_OC_FAULT_RESPONSE = 0x47, + PMBUS_IOUT_OC_LV_FAULT_LIMIT = 0x48, + PMBUS_IOUT_OC_LV_FAULT_RESPONSE = 0x49, + PMBUS_IOUT_OC_WARN_LIMIT = 0x4A, + PMBUS_IOUT_UC_FAULT_LIMIT = 0x4B, + PMBUS_IOUT_UC_FAULT_RESPONSE = 0x4C, + + PMBUS_OT_FAULT_LIMIT = 0x4F, + PMBUS_OT_FAULT_RESPONSE = 0x50, + PMBUS_OT_WARN_LIMIT = 0x51, + PMBUS_UT_WARN_LIMIT = 0x52, + PMBUS_UT_FAULT_LIMIT = 0x53, + PMBUS_UT_FAULT_RESPONSE = 0x54, + PMBUS_VIN_OV_FAULT_LIMIT = 0x55, + PMBUS_VIN_OV_FAULT_RESPONSE = 0x56, + PMBUS_VIN_OV_WARN_LIMIT = 0x57, + PMBUS_VIN_UV_WARN_LIMIT = 0x58, + PMBUS_VIN_UV_FAULT_LIMIT = 0x59, + + PMBUS_IIN_OC_FAULT_LIMIT = 0x5B, + PMBUS_IIN_OC_WARN_LIMIT = 0x5D, + + PMBUS_POUT_OP_FAULT_LIMIT = 0x68, + PMBUS_POUT_OP_WARN_LIMIT = 0x6A, + PMBUS_PIN_OP_WARN_LIMIT = 0x6B, + + PMBUS_STATUS_BYTE = 0x78, + PMBUS_STATUS_WORD = 0x79, + PMBUS_STATUS_VOUT = 0x7A, + PMBUS_STATUS_IOUT = 0x7B, + PMBUS_STATUS_INPUT = 0x7C, + PMBUS_STATUS_TEMPERATURE = 0x7D, + PMBUS_STATUS_CML = 0x7E, + PMBUS_STATUS_OTHER = 0x7F, + PMBUS_STATUS_MFR_SPECIFIC = 0x80, + PMBUS_STATUS_FAN_12 = 0x81, + PMBUS_STATUS_FAN_34 = 0x82, + + PMBUS_READ_VIN = 0x88, + PMBUS_READ_IIN = 0x89, + PMBUS_READ_VCAP = 0x8A, + PMBUS_READ_VOUT = 0x8B, + PMBUS_READ_IOUT = 0x8C, + PMBUS_READ_TEMPERATURE_1 = 0x8D, + PMBUS_READ_TEMPERATURE_2 = 0x8E, + PMBUS_READ_TEMPERATURE_3 = 0x8F, + PMBUS_READ_FAN_SPEED_1 = 0x90, + PMBUS_READ_FAN_SPEED_2 = 0x91, + PMBUS_READ_FAN_SPEED_3 = 0x92, + PMBUS_READ_FAN_SPEED_4 = 0x93, + PMBUS_READ_DUTY_CYCLE = 0x94, + PMBUS_READ_FREQUENCY = 0x95, + PMBUS_READ_POUT = 0x96, + PMBUS_READ_PIN = 0x97, + + PMBUS_REVISION = 0x98, + PMBUS_MFR_ID = 0x99, + PMBUS_MFR_MODEL = 0x9A, + PMBUS_MFR_REVISION = 0x9B, + PMBUS_MFR_LOCATION = 0x9C, + PMBUS_MFR_DATE = 0x9D, + PMBUS_MFR_SERIAL = 0x9E, + + PMBUS_MFR_VIN_MIN = 0xA0, + PMBUS_MFR_VIN_MAX = 0xA1, + PMBUS_MFR_IIN_MAX = 0xA2, + PMBUS_MFR_PIN_MAX = 0xA3, + PMBUS_MFR_VOUT_MIN = 0xA4, + PMBUS_MFR_VOUT_MAX = 0xA5, + PMBUS_MFR_IOUT_MAX = 0xA6, + PMBUS_MFR_POUT_MAX = 0xA7, + + PMBUS_IC_DEVICE_ID = 0xAD, + PMBUS_IC_DEVICE_REV = 0xAE, + + PMBUS_MFR_MAX_TEMP_1 = 0xC0, + PMBUS_MFR_MAX_TEMP_2 = 0xC1, + PMBUS_MFR_MAX_TEMP_3 = 0xC2, + +/* + * Virtual registers. + * Useful to support attributes which are not supported by standard PMBus + * registers but exist as manufacturer specific registers on individual chips. + * Must be mapped to real registers in device specific code. + * + * Semantics: + * Virtual registers are all word size. + * READ registers are read-only; writes are either ignored or return an error. + * RESET registers are read/write. Reading reset registers returns zero + * (used for detection), writing any value causes the associated history to be + * reset. + * Virtual registers have to be handled in device specific driver code. Chip + * driver code returns non-negative register values if a virtual register is + * supported, or a negative error code if not. The chip driver may return + * -ENODATA or any other error code in this case, though an error code other + * than -ENODATA is handled more efficiently and thus preferred. Either case, + * the calling PMBus core code will abort if the chip driver returns an error + * code when reading or writing virtual registers. + */ + PMBUS_VIRT_BASE = 0x100, + PMBUS_VIRT_READ_TEMP_AVG, + PMBUS_VIRT_READ_TEMP_MIN, + PMBUS_VIRT_READ_TEMP_MAX, + PMBUS_VIRT_RESET_TEMP_HISTORY, + PMBUS_VIRT_READ_VIN_AVG, + PMBUS_VIRT_READ_VIN_MIN, + PMBUS_VIRT_READ_VIN_MAX, + PMBUS_VIRT_RESET_VIN_HISTORY, + PMBUS_VIRT_READ_IIN_AVG, + PMBUS_VIRT_READ_IIN_MIN, + PMBUS_VIRT_READ_IIN_MAX, + PMBUS_VIRT_RESET_IIN_HISTORY, + PMBUS_VIRT_READ_PIN_AVG, + PMBUS_VIRT_READ_PIN_MIN, + PMBUS_VIRT_READ_PIN_MAX, + PMBUS_VIRT_RESET_PIN_HISTORY, + PMBUS_VIRT_READ_POUT_AVG, + PMBUS_VIRT_READ_POUT_MIN, + PMBUS_VIRT_READ_POUT_MAX, + PMBUS_VIRT_RESET_POUT_HISTORY, + PMBUS_VIRT_READ_VOUT_AVG, + PMBUS_VIRT_READ_VOUT_MIN, + PMBUS_VIRT_READ_VOUT_MAX, + PMBUS_VIRT_RESET_VOUT_HISTORY, + PMBUS_VIRT_READ_IOUT_AVG, + PMBUS_VIRT_READ_IOUT_MIN, + PMBUS_VIRT_READ_IOUT_MAX, + PMBUS_VIRT_RESET_IOUT_HISTORY, + PMBUS_VIRT_READ_TEMP2_AVG, + PMBUS_VIRT_READ_TEMP2_MIN, + PMBUS_VIRT_READ_TEMP2_MAX, + PMBUS_VIRT_RESET_TEMP2_HISTORY, + + PMBUS_VIRT_READ_VMON, + PMBUS_VIRT_VMON_UV_WARN_LIMIT, + PMBUS_VIRT_VMON_OV_WARN_LIMIT, + PMBUS_VIRT_VMON_UV_FAULT_LIMIT, + PMBUS_VIRT_VMON_OV_FAULT_LIMIT, + PMBUS_VIRT_STATUS_VMON, + + /* + * RPM and PWM Fan control + * + * Drivers wanting to expose PWM control must define the behaviour of + * PMBUS_VIRT_PWM_[1-4] and PMBUS_VIRT_PWM_ENABLE_[1-4] in the + * {read,write}_word_data callback. + * + * pmbus core provides a default implementation for + * PMBUS_VIRT_FAN_TARGET_[1-4]. + * + * TARGET, PWM and PWM_ENABLE members must be defined sequentially; + * pmbus core uses the difference between the provided register and + * it's _1 counterpart to calculate the FAN/PWM ID. + */ + PMBUS_VIRT_FAN_TARGET_1, + PMBUS_VIRT_FAN_TARGET_2, + PMBUS_VIRT_FAN_TARGET_3, + PMBUS_VIRT_FAN_TARGET_4, + PMBUS_VIRT_PWM_1, + PMBUS_VIRT_PWM_2, + PMBUS_VIRT_PWM_3, + PMBUS_VIRT_PWM_4, + PMBUS_VIRT_PWM_ENABLE_1, + PMBUS_VIRT_PWM_ENABLE_2, + PMBUS_VIRT_PWM_ENABLE_3, + PMBUS_VIRT_PWM_ENABLE_4, + + /* Samples for average + * + * Drivers wanting to expose functionality for changing the number of + * samples used for average values should implement support in + * {read,write}_word_data callback for either PMBUS_VIRT_SAMPLES if it + * applies to all types of measurements, or any number of specific + * PMBUS_VIRT_*_SAMPLES registers to allow for individual control. + */ + PMBUS_VIRT_SAMPLES, + PMBUS_VIRT_IN_SAMPLES, + PMBUS_VIRT_CURR_SAMPLES, + PMBUS_VIRT_POWER_SAMPLES, + PMBUS_VIRT_TEMP_SAMPLES, +}; + +/* Supported AVS MFR ID in word */ +enum pmbus_avs_mfr_id { + PMBUS_AVS_MFR_ID_MP2882 = 0x5303, + PMBUS_AVS_MFR_ID_MP2975 = 0x0025, + PMBUS_AVS_MFR_ID_PH86B03 = 0x4802, +}; + +/* + * OPERATION + */ +#define PB_OPERATION_CONTROL_ON BIT(7) + +/* + * WRITE_PROTECT + */ +#define PB_WP_ALL BIT(7) /* all but WRITE_PROTECT */ +#define PB_WP_OP BIT(6) /* all but WP, OPERATION, PAGE */ +#define PB_WP_VOUT BIT(5) /* all but WP, OPERATION, PAGE, VOUT, ON_OFF */ + +#define PB_WP_ANY (PB_WP_ALL | PB_WP_OP | PB_WP_VOUT) + +/* + * CAPABILITY + */ +#define PB_CAPABILITY_SMBALERT BIT(4) +#define PB_CAPABILITY_ERROR_CHECK BIT(7) + +/* + * VOUT_MODE + */ +#define PB_VOUT_MODE_MODE_MASK 0xe0 +#define PB_VOUT_MODE_PARAM_MASK 0x1f + +#define PB_VOUT_MODE_LINEAR 0x00 +#define PB_VOUT_MODE_VID 0x20 +#define PB_VOUT_MODE_DIRECT 0x40 + +/* + * Fan configuration + */ +#define PB_FAN_2_PULSE_MASK (BIT(0) | BIT(1)) +#define PB_FAN_2_RPM BIT(2) +#define PB_FAN_2_INSTALLED BIT(3) +#define PB_FAN_1_PULSE_MASK (BIT(4) | BIT(5)) +#define PB_FAN_1_RPM BIT(6) +#define PB_FAN_1_INSTALLED BIT(7) + +enum pmbus_fan_mode { percent = 0, rpm }; + +/* + * STATUS_BYTE, STATUS_WORD (lower) + */ +#define PB_STATUS_NONE_ABOVE BIT(0) +#define PB_STATUS_CML BIT(1) +#define PB_STATUS_TEMPERATURE BIT(2) +#define PB_STATUS_VIN_UV BIT(3) +#define PB_STATUS_IOUT_OC BIT(4) +#define PB_STATUS_VOUT_OV BIT(5) +#define PB_STATUS_OFF BIT(6) +#define PB_STATUS_BUSY BIT(7) + +/* + * STATUS_WORD (upper) + */ +#define PB_STATUS_UNKNOWN BIT(8) +#define PB_STATUS_OTHER BIT(9) +#define PB_STATUS_FANS BIT(10) +#define PB_STATUS_POWER_GOOD_N BIT(11) +#define PB_STATUS_WORD_MFR BIT(12) +#define PB_STATUS_INPUT BIT(13) +#define PB_STATUS_IOUT_POUT BIT(14) +#define PB_STATUS_VOUT BIT(15) + +/* + * STATUS_IOUT + */ +#define PB_POUT_OP_WARNING BIT(0) +#define PB_POUT_OP_FAULT BIT(1) +#define PB_POWER_LIMITING BIT(2) +#define PB_CURRENT_SHARE_FAULT BIT(3) +#define PB_IOUT_UC_FAULT BIT(4) +#define PB_IOUT_OC_WARNING BIT(5) +#define PB_IOUT_OC_LV_FAULT BIT(6) +#define PB_IOUT_OC_FAULT BIT(7) + +/* + * STATUS_VOUT, STATUS_INPUT + */ +#define PB_VOLTAGE_VIN_OFF BIT(3) +#define PB_VOLTAGE_UV_FAULT BIT(4) +#define PB_VOLTAGE_UV_WARNING BIT(5) +#define PB_VOLTAGE_OV_WARNING BIT(6) +#define PB_VOLTAGE_OV_FAULT BIT(7) + +/* + * STATUS_INPUT + */ +#define PB_PIN_OP_WARNING BIT(0) +#define PB_IIN_OC_WARNING BIT(1) +#define PB_IIN_OC_FAULT BIT(2) + +/* + * STATUS_TEMPERATURE + */ +#define PB_TEMP_UT_FAULT BIT(4) +#define PB_TEMP_UT_WARNING BIT(5) +#define PB_TEMP_OT_WARNING BIT(6) +#define PB_TEMP_OT_FAULT BIT(7) + +/* + * STATUS_FAN + */ +#define PB_FAN_AIRFLOW_WARNING BIT(0) +#define PB_FAN_AIRFLOW_FAULT BIT(1) +#define PB_FAN_FAN2_SPEED_OVERRIDE BIT(2) +#define PB_FAN_FAN1_SPEED_OVERRIDE BIT(3) +#define PB_FAN_FAN2_WARNING BIT(4) +#define PB_FAN_FAN1_WARNING BIT(5) +#define PB_FAN_FAN2_FAULT BIT(6) +#define PB_FAN_FAN1_FAULT BIT(7) + +/* + * CML_FAULT_STATUS + */ +#define PB_CML_FAULT_OTHER_MEM_LOGIC BIT(0) +#define PB_CML_FAULT_OTHER_COMM BIT(1) +#define PB_CML_FAULT_PROCESSOR BIT(3) +#define PB_CML_FAULT_MEMORY BIT(4) +#define PB_CML_FAULT_PACKET_ERROR BIT(5) +#define PB_CML_FAULT_INVALID_DATA BIT(6) +#define PB_CML_FAULT_INVALID_COMMAND BIT(7) + +enum pmbus_sensor_classes { + PSC_VOLTAGE_IN = 0, + PSC_VOLTAGE_OUT, + PSC_CURRENT_IN, + PSC_CURRENT_OUT, + PSC_POWER, + PSC_TEMPERATURE, + PSC_FAN, + PSC_PWM, + PSC_NUM_CLASSES /* Number of power sensor classes */ +}; + +#define PMBUS_PAGES 32 /* Per PMBus specification */ +#define PMBUS_PHASES 10 /* Maximum number of phases per page */ + +/* Functionality bit mask */ +#define PMBUS_HAVE_VIN BIT(0) +#define PMBUS_HAVE_VCAP BIT(1) +#define PMBUS_HAVE_VOUT BIT(2) +#define PMBUS_HAVE_IIN BIT(3) +#define PMBUS_HAVE_IOUT BIT(4) +#define PMBUS_HAVE_PIN BIT(5) +#define PMBUS_HAVE_POUT BIT(6) +#define PMBUS_HAVE_FAN12 BIT(7) +#define PMBUS_HAVE_FAN34 BIT(8) +#define PMBUS_HAVE_TEMP BIT(9) +#define PMBUS_HAVE_TEMP2 BIT(10) +#define PMBUS_HAVE_TEMP3 BIT(11) +#define PMBUS_HAVE_STATUS_VOUT BIT(12) +#define PMBUS_HAVE_STATUS_IOUT BIT(13) +#define PMBUS_HAVE_STATUS_INPUT BIT(14) +#define PMBUS_HAVE_STATUS_TEMP BIT(15) +#define PMBUS_HAVE_STATUS_FAN12 BIT(16) +#define PMBUS_HAVE_STATUS_FAN34 BIT(17) +#define PMBUS_HAVE_VMON BIT(18) +#define PMBUS_HAVE_STATUS_VMON BIT(19) +#define PMBUS_HAVE_PWM12 BIT(20) +#define PMBUS_HAVE_PWM34 BIT(21) +#define PMBUS_HAVE_SAMPLES BIT(22) + +#define PMBUS_PHASE_VIRTUAL BIT(30) /* Phases on this page are virtual */ +#define PMBUS_PAGE_VIRTUAL BIT(31) /* Page is virtual */ + +enum pmbus_data_format { linear = 0, direct, vid }; +enum vrm_version { vr11 = 0, vr12, vr13, imvp9, amd625mv }; +typedef enum pmbus_protocal_type { + PMBUS_PROTOCAL_TYPE_BYTE = 0, + PMBUS_PROTOCAL_TYPE_WORD, + PMBUS_PROTOCAL_TYPE_BLOCK +}PMBUS_PROTOCAL_TYPE_E; + +struct pmbus_driver_info { + int pages; /* Total number of pages */ + u8 phases[PMBUS_PAGES]; /* Number of phases per page */ + enum pmbus_data_format format[PSC_NUM_CLASSES]; + enum vrm_version vrm_version[PMBUS_PAGES]; /* vrm version per page */ + /* + * Support one set of coefficients for each sensor type + * Used for chips providing data in direct mode. + */ + int m[PSC_NUM_CLASSES]; /* mantissa for direct data format */ + int b[PSC_NUM_CLASSES]; /* offset */ + int R[PSC_NUM_CLASSES]; /* exponent */ + + u32 func[PMBUS_PAGES]; /* Functionality, per page */ + u32 pfunc[PMBUS_PHASES];/* Functionality, per phase */ + /* + * The following functions map manufacturing specific register values + * to PMBus standard register values. Specify only if mapping is + * necessary. + * Functions return the register value (read) or zero (write) if + * successful. A return value of -ENODATA indicates that there is no + * manufacturer specific register, but that a standard PMBus register + * may exist. Any other negative return value indicates that the + * register does not exist, and that no attempt should be made to read + * the standard register. + */ + int (*read_byte_data)(struct i2c_client *client, int page, int reg); + int (*read_word_data)(struct i2c_client *client, int page, int phase, + int reg); + int (*write_word_data)(struct i2c_client *client, int page, int reg, + u16 word); + int (*write_byte)(struct i2c_client *client, int page, u8 value); + /* + * The identify function determines supported PMBus functionality. + * This function is only necessary if a chip driver supports multiple + * chips, and the chip functionality is not pre-determined. + */ + int (*identify)(struct i2c_client *client, + struct pmbus_driver_info *info); + + /* Regulator functionality, if supported by this chip driver. */ + int num_regulators; + const struct regulator_desc *reg_desc; + + /* custom attributes */ + const struct attribute_group **groups; +}; + +/* Regulator ops */ + +extern const struct regulator_ops pmbus_regulator_ops; + +/* Macro for filling in array of struct regulator_desc */ +#define PMBUS_REGULATOR(_name, _id) \ + [_id] = { \ + .name = (_name # _id), \ + .id = (_id), \ + .of_match = of_match_ptr(_name # _id), \ + .regulators_node = of_match_ptr("regulators"), \ + .ops = &pmbus_regulator_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + } + +/* Function declarations */ + +void pmbus_clear_cache(struct i2c_client *client); +int pmbus_set_page(struct i2c_client *client, int page, int phase); +int pmbus_read_word_data(struct i2c_client *client, int page, int phase, + u8 reg); +int pmbus_write_word_data(struct i2c_client *client, int page, u8 reg, + u16 word); +int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg); +int pmbus_write_byte(struct i2c_client *client, int page, u8 value); +int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg, + u8 value); +int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg, + u8 mask, u8 value); +void pmbus_clear_faults(struct i2c_client *client); +bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg); +bool pmbus_check_word_register(struct i2c_client *client, int page, int reg); +int pmbus_do_probe(struct i2c_client *client, struct pmbus_driver_info *info); +int pmbus_do_remove(struct i2c_client *client); +const struct pmbus_driver_info *pmbus_get_driver_info(struct i2c_client + *client); +int pmbus_get_fan_rate_device(struct i2c_client *client, int page, int id, + enum pmbus_fan_mode mode); +int pmbus_get_fan_rate_cached(struct i2c_client *client, int page, int id, + enum pmbus_fan_mode mode); +int pmbus_update_fan(struct i2c_client *client, int page, int id, + u8 config, u8 mask, u16 command); +struct dentry *pmbus_get_debugfs_dir(struct i2c_client *client); + +int pmbus_core_read_reg(unsigned int adapter_nr, unsigned short i2c_addr, int page, int reg, PMBUS_PROTOCAL_TYPE_E type, void *val); +int pmbus_core_read_attrs(unsigned int adapter_nr, unsigned short i2c_addr, char *attr_name, long *val); +int pmbus_core_read_attrs_by_reg(unsigned int adapter_nr, unsigned short i2c_addr, int page, int reg, long *val); +int pmbus_core_read_mfr_id(unsigned int adapter_nr, unsigned short i2c_addr, int *val); +int pmbus_core_clear_fault(unsigned int adapter_nr, unsigned short i2c_addr); + +int mp2882_fmea_get_vout_str(int page, char *buf); +int mp2882_fmea_get_iout_str(int page, char *buf); +int mp2882_fmea_get_temp1_str(int page, char *buf); +int mp2882_fmea_get_mfr_id(int page, char *buf); +int mp2882_fmea_read_reg_str(int page, int reg, char *val); +int mp2882_fmea_clear_fault(void); +int mp2975_fmea_get_vout_str(unsigned int nr, int page, char *buf); +int mp2975_fmea_get_iout_str(unsigned int nr, int page, char *buf); +int mp2975_fmea_get_temp1_str(unsigned int nr, int page, char *buf); +int mp2975_fmea_get_mfr_id(unsigned int nr, int page); +int mp2975_fmea_read_reg_str(unsigned int nr, int page, int reg, char *val); +int mp2975_fmea_clear_fault(unsigned int nr); + +#endif /* PMBUS_H */ diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/switch_at24.c b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/switch_at24.c new file mode 100644 index 0000000000..3b4345f2ea --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/switch_at24.c @@ -0,0 +1,931 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * at24.c - handle most I2C EEPROMs + * + * Copyright (C) 2005-2007 David Brownell + * Copyright (C) 2008 Wolfram Sang, Pengutronix + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//simon: try to support 4.x kernel +#include + +#include "switch_at24.h" +#include "switch_fan_driver.h" +#include "switch_psu_driver.h" + +/* Address pointer is 16 bit. */ +#define AT24_FLAG_ADDR16 BIT(7) +/* sysfs-entry will be read-only. */ +#define AT24_FLAG_READONLY BIT(6) +/* sysfs-entry will be world-readable. */ +#define AT24_FLAG_IRUGO BIT(5) +/* Take always 8 addresses (24c00). */ +#define AT24_FLAG_TAKE8ADDR BIT(4) +/* Factory-programmed serial number. */ +#define AT24_FLAG_SERIAL BIT(3) +/* Factory-programmed mac address. */ +#define AT24_FLAG_MAC BIT(2) +/* Does not auto-rollover reads to the next slave address. */ +#define AT24_FLAG_NO_RDROL BIT(1) + +/* + * I2C EEPROMs from most vendors are inexpensive and mostly interchangeable. + * Differences between different vendor product lines (like Atmel AT24C or + * MicroChip 24LC, etc) won't much matter for typical read/write access. + * There are also I2C RAM chips, likewise interchangeable. One example + * would be the PCF8570, which acts like a 24c02 EEPROM (256 bytes). + * + * However, misconfiguration can lose data. "Set 16-bit memory address" + * to a part with 8-bit addressing will overwrite data. Writing with too + * big a page size also loses data. And it's not safe to assume that the + * conventional addresses 0x50..0x57 only hold eeproms; a PCF8563 RTC + * uses 0x51, for just one example. + * + * Accordingly, explicit board-specific configuration data should be used + * in almost all cases. (One partial exception is an SMBus used to access + * "SPD" data for DRAM sticks. Those only use 24c02 EEPROMs.) + * + * So this driver uses "new style" I2C driver binding, expecting to be + * told what devices exist. That may be in arch/X/mach-Y/board-Z.c or + * similar kernel-resident tables; or, configuration data coming from + * a bootloader. + * + * Other than binding model, current differences from "eeprom" driver are + * that this one handles write access and isn't restricted to 24c02 devices. + * It also handles larger devices (32 kbit and up) with two-byte addresses, + * which won't work on pure SMBus systems. + */ + +struct at24_client { + struct i2c_client *client; + struct regmap *regmap; +}; + +struct at24_data { + /* + * Lock protects against activities from other Linux tasks, + * but not from changes by other I2C masters. + */ + struct mutex lock; + + unsigned int write_max; + unsigned int num_addresses; + unsigned int offset_adj; + + u32 byte_len; + u16 page_size; + u8 flags; + + struct nvmem_device *nvmem; + struct regulator *vcc_reg; + void (*read_post)(unsigned int off, char *buf, size_t count); + + /* + * Some chips tie up multiple I2C addresses; dummy devices reserve + * them for us, and we'll use them with SMBus calls. + */ + struct at24_client client[]; +}; + +struct at24_data *s3ip_at24[MAX_FAN_NUM+PSU_TOTAL_NUM] = {NULL}; + +/* + * This parameter is to help this driver avoid blocking other drivers out + * of I2C for potentially troublesome amounts of time. With a 100 kHz I2C + * clock, one 256 byte read takes about 1/43 second which is excessive; + * but the 1/170 second it takes at 400 kHz may be quite reasonable; and + * at 1 MHz (Fm+) a 1/430 second delay could easily be invisible. + * + * This value is forced to be a power of two so that writes align on pages. + */ +static unsigned int at24_io_limit = 128; +module_param_named(io_limit, at24_io_limit, uint, 0); +MODULE_PARM_DESC(at24_io_limit, "Maximum bytes per I/O (default 128)"); + +/* + * Specs often allow 5 msec for a page write, sometimes 20 msec; + * it's important to recover from write timeouts. + */ +static unsigned int at24_write_timeout = 25; +module_param_named(write_timeout, at24_write_timeout, uint, 0); +MODULE_PARM_DESC(at24_write_timeout, "Time (in ms) to try writes (default 25)"); + +struct at24_chip_data { + u32 byte_len; + u8 flags; + void (*read_post)(unsigned int off, char *buf, size_t count); +}; + +#define AT24_CHIP_DATA(_name, _len, _flags) \ + static const struct at24_chip_data _name = { \ + .byte_len = _len, .flags = _flags, \ + } + +#define AT24_CHIP_DATA_CB(_name, _len, _flags, _read_post) \ + static const struct at24_chip_data _name = { \ + .byte_len = _len, .flags = _flags, \ + .read_post = _read_post, \ + } + +static void at24_read_post_vaio(unsigned int off, char *buf, size_t count) +{ + int i; + + if (capable(CAP_SYS_ADMIN)) + return; + + /* + * Hide VAIO private settings to regular users: + * - BIOS passwords: bytes 0x00 to 0x0f + * - UUID: bytes 0x10 to 0x1f + * - Serial number: 0xc0 to 0xdf + */ + for (i = 0; i < count; i++) { + if ((off + i <= 0x1f) || + (off + i >= 0xc0 && off + i <= 0xdf)) + buf[i] = 0; + } +} + +/* needs 8 addresses as A0-A2 are ignored */ +AT24_CHIP_DATA(at24_data_24c00, 128 / 8, AT24_FLAG_TAKE8ADDR); +/* old variants can't be handled with this generic entry! */ +AT24_CHIP_DATA(at24_data_24c01, 1024 / 8, 0); +AT24_CHIP_DATA(at24_data_24cs01, 16, + AT24_FLAG_SERIAL | AT24_FLAG_READONLY); +AT24_CHIP_DATA(at24_data_24c02, 2048 / 8, 0); +AT24_CHIP_DATA(at24_data_24cs02, 16, + AT24_FLAG_SERIAL | AT24_FLAG_READONLY); +AT24_CHIP_DATA(at24_data_24mac402, 48 / 8, + AT24_FLAG_MAC | AT24_FLAG_READONLY); +AT24_CHIP_DATA(at24_data_24mac602, 64 / 8, + AT24_FLAG_MAC | AT24_FLAG_READONLY); +/* spd is a 24c02 in memory DIMMs */ +AT24_CHIP_DATA(at24_data_spd, 2048 / 8, + AT24_FLAG_READONLY | AT24_FLAG_IRUGO); +/* 24c02_vaio is a 24c02 on some Sony laptops */ +AT24_CHIP_DATA_CB(at24_data_24c02_vaio, 2048 / 8, + AT24_FLAG_READONLY | AT24_FLAG_IRUGO, + at24_read_post_vaio); +AT24_CHIP_DATA(at24_data_24c04, 4096 / 8, 0); +AT24_CHIP_DATA(at24_data_24cs04, 16, + AT24_FLAG_SERIAL | AT24_FLAG_READONLY); +/* 24rf08 quirk is handled at i2c-core */ +AT24_CHIP_DATA(at24_data_24c08, 8192 / 8, 0); +AT24_CHIP_DATA(at24_data_24cs08, 16, + AT24_FLAG_SERIAL | AT24_FLAG_READONLY); +AT24_CHIP_DATA(at24_data_24c16, 16384 / 8, 0); +AT24_CHIP_DATA(at24_data_24cs16, 16, + AT24_FLAG_SERIAL | AT24_FLAG_READONLY); +AT24_CHIP_DATA(at24_data_24c32, 32768 / 8, AT24_FLAG_ADDR16); +AT24_CHIP_DATA(at24_data_24cs32, 16, + AT24_FLAG_ADDR16 | AT24_FLAG_SERIAL | AT24_FLAG_READONLY); +AT24_CHIP_DATA(at24_data_24c64, 65536 / 8, AT24_FLAG_ADDR16); +AT24_CHIP_DATA(at24_data_24cs64, 16, + AT24_FLAG_ADDR16 | AT24_FLAG_SERIAL | AT24_FLAG_READONLY); +AT24_CHIP_DATA(at24_data_24c128, 131072 / 8, AT24_FLAG_ADDR16); +AT24_CHIP_DATA(at24_data_24c256, 262144 / 8, AT24_FLAG_ADDR16); +AT24_CHIP_DATA(at24_data_24c512, 524288 / 8, AT24_FLAG_ADDR16); +AT24_CHIP_DATA(at24_data_24c1024, 1048576 / 8, AT24_FLAG_ADDR16); +AT24_CHIP_DATA(at24_data_24c2048, 2097152 / 8, AT24_FLAG_ADDR16); +/* identical to 24c08 ? */ +AT24_CHIP_DATA(at24_data_INT3499, 8192 / 8, 0); + +static const struct i2c_device_id at24_ids[] = { + { "24c00", (kernel_ulong_t)&at24_data_24c00 }, + { "24c01", (kernel_ulong_t)&at24_data_24c01 }, + { "24cs01", (kernel_ulong_t)&at24_data_24cs01 }, + { "24c02", (kernel_ulong_t)&at24_data_24c02 }, + { "24cs02", (kernel_ulong_t)&at24_data_24cs02 }, + { "24mac402", (kernel_ulong_t)&at24_data_24mac402 }, + { "24mac602", (kernel_ulong_t)&at24_data_24mac602 }, + { "spd", (kernel_ulong_t)&at24_data_spd }, + { "24c02-vaio", (kernel_ulong_t)&at24_data_24c02_vaio }, + { "24c04", (kernel_ulong_t)&at24_data_24c04 }, + { "24cs04", (kernel_ulong_t)&at24_data_24cs04 }, + { "24c08", (kernel_ulong_t)&at24_data_24c08 }, + { "24cs08", (kernel_ulong_t)&at24_data_24cs08 }, + { "24c16", (kernel_ulong_t)&at24_data_24c16 }, + { "24cs16", (kernel_ulong_t)&at24_data_24cs16 }, + { "24c32", (kernel_ulong_t)&at24_data_24c32 }, + { "24cs32", (kernel_ulong_t)&at24_data_24cs32 }, + { "24c64", (kernel_ulong_t)&at24_data_24c64 }, + { "24cs64", (kernel_ulong_t)&at24_data_24cs64 }, + { "24c128", (kernel_ulong_t)&at24_data_24c128 }, + { "24c256", (kernel_ulong_t)&at24_data_24c256 }, + { "24c512", (kernel_ulong_t)&at24_data_24c512 }, + { "24c1024", (kernel_ulong_t)&at24_data_24c1024 }, + { "24c2048", (kernel_ulong_t)&at24_data_24c2048 }, + { "at24", 0 }, + { /* END OF LIST */ } +}; +MODULE_DEVICE_TABLE(i2c, at24_ids); + +static const struct of_device_id at24_of_match[] = { + { .compatible = "atmel,24c00", .data = &at24_data_24c00 }, + { .compatible = "atmel,24c01", .data = &at24_data_24c01 }, + { .compatible = "atmel,24cs01", .data = &at24_data_24cs01 }, + { .compatible = "atmel,24c02", .data = &at24_data_24c02 }, + { .compatible = "atmel,24cs02", .data = &at24_data_24cs02 }, + { .compatible = "atmel,24mac402", .data = &at24_data_24mac402 }, + { .compatible = "atmel,24mac602", .data = &at24_data_24mac602 }, + { .compatible = "atmel,spd", .data = &at24_data_spd }, + { .compatible = "atmel,24c04", .data = &at24_data_24c04 }, + { .compatible = "atmel,24cs04", .data = &at24_data_24cs04 }, + { .compatible = "atmel,24c08", .data = &at24_data_24c08 }, + { .compatible = "atmel,24cs08", .data = &at24_data_24cs08 }, + { .compatible = "atmel,24c16", .data = &at24_data_24c16 }, + { .compatible = "atmel,24cs16", .data = &at24_data_24cs16 }, + { .compatible = "atmel,24c32", .data = &at24_data_24c32 }, + { .compatible = "atmel,24cs32", .data = &at24_data_24cs32 }, + { .compatible = "atmel,24c64", .data = &at24_data_24c64 }, + { .compatible = "atmel,24cs64", .data = &at24_data_24cs64 }, + { .compatible = "atmel,24c128", .data = &at24_data_24c128 }, + { .compatible = "atmel,24c256", .data = &at24_data_24c256 }, + { .compatible = "atmel,24c512", .data = &at24_data_24c512 }, + { .compatible = "atmel,24c1024", .data = &at24_data_24c1024 }, + { .compatible = "atmel,24c2048", .data = &at24_data_24c2048 }, + { /* END OF LIST */ }, +}; +MODULE_DEVICE_TABLE(of, at24_of_match); + +static const struct acpi_device_id __maybe_unused at24_acpi_ids[] = { + { "INT3499", (kernel_ulong_t)&at24_data_INT3499 }, + { "TPF0001", (kernel_ulong_t)&at24_data_24c1024 }, + { /* END OF LIST */ } +}; +MODULE_DEVICE_TABLE(acpi, at24_acpi_ids); + +/* + * This routine supports chips which consume multiple I2C addresses. It + * computes the addressing information to be used for a given r/w request. + * Assumes that sanity checks for offset happened at sysfs-layer. + * + * Slave address and byte offset derive from the offset. Always + * set the byte address; on a multi-master board, another master + * may have changed the chip's "current" address pointer. + */ +static struct at24_client *at24_translate_offset(struct at24_data *at24, + unsigned int *offset) +{ + unsigned int i; + + if (at24->flags & AT24_FLAG_ADDR16) { + i = *offset >> 16; + *offset &= 0xffff; + } else { + i = *offset >> 8; + *offset &= 0xff; + } + + return &at24->client[i]; +} + +static struct device *at24_base_client_dev(struct at24_data *at24) +{ + return &at24->client[0].client->dev; +} + +static size_t at24_adjust_read_count(struct at24_data *at24, + unsigned int offset, size_t count) +{ + unsigned int bits; + size_t remainder; + + /* + * In case of multi-address chips that don't rollover reads to + * the next slave address: truncate the count to the slave boundary, + * so that the read never straddles slaves. + */ + if (at24->flags & AT24_FLAG_NO_RDROL) { + bits = (at24->flags & AT24_FLAG_ADDR16) ? 16 : 8; + remainder = BIT(bits) - offset; + if (count > remainder) + count = remainder; + } + + if (count > at24_io_limit) + count = at24_io_limit; + + return count; +} + +static ssize_t at24_regmap_read(struct at24_data *at24, char *buf, + unsigned int offset, size_t count) +{ + unsigned long timeout, read_time; + struct at24_client *at24_client; + struct i2c_client *client; + struct regmap *regmap; + int ret; + + at24_client = at24_translate_offset(at24, &offset); + regmap = at24_client->regmap; + client = at24_client->client; + count = at24_adjust_read_count(at24, offset, count); + + /* adjust offset for mac and serial read ops */ + offset += at24->offset_adj; + + timeout = jiffies + msecs_to_jiffies(at24_write_timeout); + do { + /* + * The timestamp shall be taken before the actual operation + * to avoid a premature timeout in case of high CPU load. + */ + read_time = jiffies; + + ret = regmap_bulk_read(regmap, offset, buf, count); + dev_dbg(&client->dev, "read %zu@%d --> %d (%ld)\n", + count, offset, ret, jiffies); + if (!ret) + return count; + + usleep_range(1000, 1500); + } while (time_before(read_time, timeout)); + + return -ETIMEDOUT; +} + +/* + * Note that if the hardware write-protect pin is pulled high, the whole + * chip is normally write protected. But there are plenty of product + * variants here, including OTP fuses and partial chip protect. + * + * We only use page mode writes; the alternative is sloooow. These routines + * write at most one page. + */ + +static size_t at24_adjust_write_count(struct at24_data *at24, + unsigned int offset, size_t count) +{ + unsigned int next_page; + + /* write_max is at most a page */ + if (count > at24->write_max) + count = at24->write_max; + + /* Never roll over backwards, to the start of this page */ + next_page = roundup(offset + 1, at24->page_size); + if (offset + count > next_page) + count = next_page - offset; + + return count; +} + +static ssize_t at24_regmap_write(struct at24_data *at24, const char *buf, + unsigned int offset, size_t count) +{ + unsigned long timeout, write_time; + struct at24_client *at24_client; + struct i2c_client *client; + struct regmap *regmap; + int ret; + + at24_client = at24_translate_offset(at24, &offset); + regmap = at24_client->regmap; + client = at24_client->client; + count = at24_adjust_write_count(at24, offset, count); + timeout = jiffies + msecs_to_jiffies(at24_write_timeout); + + do { + /* + * The timestamp shall be taken before the actual operation + * to avoid a premature timeout in case of high CPU load. + */ + write_time = jiffies; + + ret = regmap_bulk_write(regmap, offset, buf, count); + dev_dbg(&client->dev, "write %zu@%d --> %d (%ld)\n", + count, offset, ret, jiffies); + if (!ret) + return count; + + usleep_range(1000, 1500); + } while (time_before(write_time, timeout)); + + return -ETIMEDOUT; +} + +static int at24_read(void *priv, unsigned int off, void *val, size_t count) +{ + struct at24_data *at24; + struct device *dev; + char *buf = val; + int i, ret; + + at24 = priv; + dev = at24_base_client_dev(at24); + + if (unlikely(!count)) + return count; + + if (off + count > at24->byte_len) + return -EINVAL; + + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + pm_runtime_put_noidle(dev); + return ret; + } + + /* + * Read data from chip, protecting against concurrent updates + * from this host, but not from other I2C masters. + */ + mutex_lock(&at24->lock); + + for (i = 0; count; i += ret, count -= ret) { + ret = at24_regmap_read(at24, buf + i, off + i, count); + if (ret < 0) { + mutex_unlock(&at24->lock); + pm_runtime_put(dev); + return ret; + } + } + + mutex_unlock(&at24->lock); + + pm_runtime_put(dev); + + if (unlikely(at24->read_post)) + at24->read_post(off, buf, i); + + return 0; +} + +ssize_t at24_read_fan_eeprom(unsigned int adapter_nr, uint8_t *eeprom, unsigned int offset, unsigned int count) +{ + struct at24_data *at24 = NULL; + int i; + struct at24_client *s3ip_client; + int ret; + void *temp = eeprom; + + for(i=0; iclient->adapter->nr == adapter_nr) + { + at24 = s3ip_at24[i]; + break; + } + } + } + + if(at24 != NULL) + ret = at24_read(at24, offset, temp, (size_t)count); + else + return -ENXIO; + + return ret; +} +EXPORT_SYMBOL_GPL(at24_read_fan_eeprom); + +static int at24_write(void *priv, unsigned int off, void *val, size_t count) +{ + struct at24_data *at24; + struct device *dev; + char *buf = val; + int ret; + + at24 = priv; + dev = at24_base_client_dev(at24); + + if (unlikely(!count)) + return -EINVAL; + + if (off + count > at24->byte_len) + return -EINVAL; + + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + pm_runtime_put_noidle(dev); + return ret; + } + + /* + * Write data to chip, protecting against concurrent updates + * from this host, but not from other I2C masters. + */ + mutex_lock(&at24->lock); + + while (count) { + ret = at24_regmap_write(at24, buf, off, count); + if (ret < 0) { + mutex_unlock(&at24->lock); + pm_runtime_put(dev); + return ret; + } + buf += ret; + off += ret; + count -= ret; + } + + mutex_unlock(&at24->lock); + + pm_runtime_put(dev); + + return 0; +} + +static const struct at24_chip_data *at24_get_chip_data(struct device *dev) +{ + struct device_node *of_node = dev->of_node; + const struct at24_chip_data *cdata; + const struct i2c_device_id *id; + + id = i2c_match_id(at24_ids, to_i2c_client(dev)); + + /* + * The I2C core allows OF nodes compatibles to match against the + * I2C device ID table as a fallback, so check not only if an OF + * node is present but also if it matches an OF device ID entry. + */ + if (of_node && of_match_device(at24_of_match, dev)) + cdata = of_device_get_match_data(dev); + else if (id) + cdata = (void *)id->driver_data; + else + cdata = acpi_device_get_match_data(dev); + + if (!cdata) + return ERR_PTR(-ENODEV); + + return cdata; +} + +static int at24_make_dummy_client(struct at24_data *at24, unsigned int index, + struct regmap_config *regmap_config) +{ + struct i2c_client *base_client, *dummy_client; + struct regmap *regmap; + struct device *dev; + + base_client = at24->client[0].client; + dev = &base_client->dev; +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 2, 0) + dummy_client = i2c_new_dummy(base_client->adapter, base_client->addr + index); +#else + dummy_client = devm_i2c_new_dummy_device(dev, base_client->adapter,base_client->addr + index); +#endif + + if (IS_ERR(dummy_client)) + return PTR_ERR(dummy_client); + + regmap = devm_regmap_init_i2c(dummy_client, regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + at24->client[index].client = dummy_client; + at24->client[index].regmap = regmap; + + return 0; +} + +static unsigned int at24_get_offset_adj(u8 flags, unsigned int byte_len) +{ + if (flags & AT24_FLAG_MAC) { + /* EUI-48 starts from 0x9a, EUI-64 from 0x98 */ + return 0xa0 - byte_len; + } else if (flags & AT24_FLAG_SERIAL && flags & AT24_FLAG_ADDR16) { + /* + * For 16 bit address pointers, the word address must contain + * a '10' sequence in bits 11 and 10 regardless of the + * intended position of the address pointer. + */ + return 0x0800; + } else if (flags & AT24_FLAG_SERIAL) { + /* + * Otherwise the word address must begin with a '10' sequence, + * regardless of the intended address. + */ + return 0x0080; + } else { + return 0; + } +} + +static int at24_probe(struct i2c_client *client) +{ + struct regmap_config regmap_config = { }; + struct nvmem_config nvmem_config = { }; + u32 byte_len, page_size, flags, addrw; + const struct at24_chip_data *cdata; + struct device *dev = &client->dev; + bool i2c_fn_i2c, i2c_fn_block; + unsigned int i, num_addresses; + struct at24_data *at24; + struct regmap *regmap; + bool writable; + u8 test_byte; + int err=0; + + i2c_fn_i2c = i2c_check_functionality(client->adapter, I2C_FUNC_I2C); + i2c_fn_block = i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WRITE_I2C_BLOCK); + + cdata = at24_get_chip_data(dev); + if (IS_ERR(cdata)) + return PTR_ERR(cdata); + + err = device_property_read_u32(dev, "pagesize", &page_size); + if (err) + /* + * This is slow, but we can't know all eeproms, so we better + * play safe. Specifying custom eeprom-types via device tree + * or properties is recommended anyhow. + */ + page_size = 1; + + flags = cdata->flags; + if (device_property_present(dev, "read-only")) + flags |= AT24_FLAG_READONLY; + if (device_property_present(dev, "no-read-rollover")) + flags |= AT24_FLAG_NO_RDROL; + + err = device_property_read_u32(dev, "address-width", &addrw); + if (!err) { + switch (addrw) { + case 8: + if (flags & AT24_FLAG_ADDR16) + dev_warn(dev, + "Override address width to be 8, while default is 16\n"); + flags &= ~AT24_FLAG_ADDR16; + break; + case 16: + flags |= AT24_FLAG_ADDR16; + break; + default: + dev_warn(dev, "Bad \"address-width\" property: %u\n", + addrw); + } + } + + err = device_property_read_u32(dev, "size", &byte_len); + if (err) + byte_len = cdata->byte_len; + + if (!i2c_fn_i2c && !i2c_fn_block) + page_size = 1; + + if (!page_size) { + dev_err(dev, "page_size must not be 0!\n"); + return -EINVAL; + } + + if (!is_power_of_2(page_size)) + dev_warn(dev, "page_size looks suspicious (no power of 2)!\n"); + + err = device_property_read_u32(dev, "num-addresses", &num_addresses); + if (err) { + if (flags & AT24_FLAG_TAKE8ADDR) + num_addresses = 8; + else + num_addresses = DIV_ROUND_UP(byte_len, + (flags & AT24_FLAG_ADDR16) ? 65536 : 256); + } + + if ((flags & AT24_FLAG_SERIAL) && (flags & AT24_FLAG_MAC)) { + dev_err(dev, + "invalid device data - cannot have both AT24_FLAG_SERIAL & AT24_FLAG_MAC."); + return -EINVAL; + } + + regmap_config.val_bits = 8; + regmap_config.reg_bits = (flags & AT24_FLAG_ADDR16) ? 16 : 8; + regmap_config.disable_locking = true; + + regmap = devm_regmap_init_i2c(client, ®map_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + at24 = devm_kzalloc(dev, struct_size(at24, client, num_addresses), + GFP_KERNEL); + if (!at24) + return -ENOMEM; + + mutex_init(&at24->lock); + at24->byte_len = byte_len; + at24->page_size = page_size; + at24->flags = flags; + at24->read_post = cdata->read_post; + at24->num_addresses = num_addresses; + at24->offset_adj = at24_get_offset_adj(flags, byte_len); + at24->client[0].client = client; + at24->client[0].regmap = regmap; + + at24->vcc_reg = devm_regulator_get(dev, "vcc"); + if (IS_ERR(at24->vcc_reg)) + return PTR_ERR(at24->vcc_reg); + + writable = !(flags & AT24_FLAG_READONLY); + if (writable) { + at24->write_max = min_t(unsigned int, + page_size, at24_io_limit); + if (!i2c_fn_i2c && at24->write_max > I2C_SMBUS_BLOCK_MAX) + at24->write_max = I2C_SMBUS_BLOCK_MAX; + } + + /* use dummy devices for multiple-address chips */ + for (i = 1; i < num_addresses; i++) { + err = at24_make_dummy_client(at24, i, ®map_config); + if (err) + return err; + } +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0) + nvmem_config.name = dev_name(dev); +#else +/* + * We initialize nvmem_config.id to NVMEM_DEVID_AUTO even if the + * label property is set as some platform can have multiple eeproms + * with same label and we can not register each of those with same + * label. Failing to register those eeproms trigger cascade failure + * on such platform. + */ + nvmem_config.id = NVMEM_DEVID_AUTO; + + if (device_property_present(dev, "label")) { + err = device_property_read_string(dev, "label", + &nvmem_config.name); + if (err) + return err; + } else { + nvmem_config.name = dev_name(dev); + } + + nvmem_config.type = NVMEM_TYPE_EEPROM; +#endif + + nvmem_config.dev = dev; + nvmem_config.read_only = !writable; + nvmem_config.root_only = !(flags & AT24_FLAG_IRUGO); + nvmem_config.owner = THIS_MODULE; + nvmem_config.compat = true; + nvmem_config.base_dev = dev; + nvmem_config.reg_read = at24_read; + nvmem_config.reg_write = at24_write; + nvmem_config.priv = at24; + nvmem_config.stride = 1; + nvmem_config.word_size = 1; + nvmem_config.size = byte_len; + + i2c_set_clientdata(client, at24); + + err = regulator_enable(at24->vcc_reg); + if (err) { + dev_err(dev, "Failed to enable vcc regulator\n"); + return err; + } + + /* enable runtime pm */ + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + at24->nvmem = devm_nvmem_register(dev, &nvmem_config); + if (IS_ERR(at24->nvmem)) { + pm_runtime_disable(dev); + if (!pm_runtime_status_suspended(dev)) + regulator_disable(at24->vcc_reg); + return PTR_ERR(at24->nvmem); + } + + /* + * Perform a one-byte test read to verify that the + * chip is functional. + */ + err = at24_read(at24, 0, &test_byte, 1); + if (err) { + pm_runtime_disable(dev); + if (!pm_runtime_status_suspended(dev)) + regulator_disable(at24->vcc_reg); + return -ENODEV; + } + + pm_runtime_idle(dev); + + if (writable) + dev_info(dev, "%u byte %s EEPROM, writable, %u bytes/write\n", + byte_len, client->name, at24->write_max); + else + dev_info(dev, "%u byte %s EEPROM, read-only\n", + byte_len, client->name); + + for(i=0; ilock); + return -1; + } + + break; + } + } + return 0; +} + +static int at24_remove(struct i2c_client *client) +{ + int i; + struct at24_data *at24 = i2c_get_clientdata(client); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + regulator_disable(at24->vcc_reg); + pm_runtime_set_suspended(&client->dev); + + for(i=0; ivcc_reg); +} + +static int __maybe_unused at24_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct at24_data *at24 = i2c_get_clientdata(client); + + return regulator_enable(at24->vcc_reg); +} + +static const struct dev_pm_ops at24_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(at24_suspend, at24_resume, NULL) +}; + +static struct i2c_driver at24_driver = { + .driver = { + .name = "at241", + .pm = &at24_pm_ops, + .of_match_table = at24_of_match, + .acpi_match_table = ACPI_PTR(at24_acpi_ids), + }, + .probe_new = at24_probe, + .remove = at24_remove, + .id_table = at24_ids, +}; + +static int __init at24_init(void) +{ + if (!at24_io_limit) { + pr_err("at24: at24_io_limit must not be 0!\n"); + return -EINVAL; + } + + at24_io_limit = rounddown_pow_of_two(at24_io_limit); + return i2c_add_driver(&at24_driver); +} +module_init(at24_init); + +static void __exit at24_exit(void) +{ + i2c_del_driver(&at24_driver); +} +module_exit(at24_exit); + +MODULE_DESCRIPTION("Driver for most I2C EEPROMs"); +MODULE_AUTHOR("David Brownell and Wolfram Sang"); +MODULE_LICENSE("GPL"); diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/switch_at24.h b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/switch_at24.h new file mode 100644 index 0000000000..0c0e8b34b8 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/switch_at24.h @@ -0,0 +1,6 @@ +#ifndef AT24_H +#define AT24_H + +ssize_t at24_read_fan_eeprom(unsigned int adapter_nr, uint8_t *eeprom, unsigned int offset, unsigned int count); + +#endif /* AT24_H */ \ No newline at end of file diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/switch_coretemp.c b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/switch_coretemp.c new file mode 100644 index 0000000000..ad0d015988 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/switch_coretemp.c @@ -0,0 +1,877 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * coretemp.c - Linux kernel module for hardware monitoring + * + * Copyright (C) 2007 Rudolf Marek + * + * Inspired from many hwmon drivers + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "switch_coretemp.h" + + + +#ifdef C11_ANNEX_K +#include "libboundscheck/include/securec.h" +#endif + +#define DRVNAME "switch_coretemp" + +struct device *s3ip_dev = NULL; + +/* + * force_tjmax only matters when TjMax can't be read from the CPU itself. + * When set, it replaces the driver's suboptimal heuristic. + */ +static int force_tjmax; +module_param_named(tjmax, force_tjmax, int, 0444); +MODULE_PARM_DESC(tjmax, "TjMax value in degrees Celsius"); + +#define PKG_SYSFS_ATTR_NO 1 /* Sysfs attribute for package temp */ +#define BASE_SYSFS_ATTR_NO 2 /* Sysfs Base attr no for coretemp */ +#define NUM_REAL_CORES 128 /* Number of Real cores per cpu */ +#define CORETEMP_NAME_LENGTH 19 /* String Length of attrs */ +#define MAX_CORE_ATTRS 4 /* Maximum no of basic attrs */ +#define TOTAL_ATTRS (MAX_CORE_ATTRS + 1) +#define MAX_CORE_DATA (NUM_REAL_CORES + BASE_SYSFS_ATTR_NO) + +#define TO_CORE_ID(cpu) (cpu_data(cpu).cpu_core_id) +#define TO_ATTR_NO(cpu) (TO_CORE_ID(cpu) + BASE_SYSFS_ATTR_NO) + +#ifdef CONFIG_SMP +#define for_each_sibling(i, cpu) \ + for_each_cpu(i, topology_sibling_cpumask(cpu)) +#else +#define for_each_sibling(i, cpu) for (i = 0; false; ) +#endif + +/* + * Per-Core Temperature Data + * @last_updated: The time when the current temperature value was updated + * earlier (in jiffies). + * @cpu_core_id: The CPU Core from which temperature values should be read + * This value is passed as "id" field to rdmsr/wrmsr functions. + * @status_reg: One of IA32_THERM_STATUS or IA32_PACKAGE_THERM_STATUS, + * from where the temperature values should be read. + * @attr_size: Total number of pre-core attrs displayed in the sysfs. + * @is_pkg_data: If this is 1, the temp_data holds pkgtemp data. + * Otherwise, temp_data holds coretemp data. + * @valid: If this is 1, the current temperature is valid. + */ +struct temp_data { + int temp; + int ttarget; + int tjmax; + unsigned long last_updated; + unsigned int cpu; + u32 cpu_core_id; + u32 status_reg; + int attr_size; + bool is_pkg_data; + bool valid; + struct sensor_device_attribute sd_attrs[TOTAL_ATTRS]; + char attr_name[TOTAL_ATTRS][CORETEMP_NAME_LENGTH]; + struct attribute *attrs[TOTAL_ATTRS + 1]; + struct attribute_group attr_group; + struct mutex update_lock; +}; + +/* Platform Data per Physical CPU */ +struct platform_data { + struct device *hwmon_dev; + u16 pkg_id; + struct cpumask cpumask; + struct temp_data *core_data[MAX_CORE_DATA]; + struct device_attribute name_attr; +}; + +/* Keep track of how many zone pointers we allocated in init() */ +//simon: try to support linux 4.x +#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0) +#define topology_logical_die_id topology_logical_package_id +#define max_zones topology_max_packages() +#else +static int max_zones __read_mostly; +#endif + + +/* Array of zone pointers. Serialized by cpu hotplug lock */ +static struct platform_device **zone_devices; + +static ssize_t show_label(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct platform_data *pdata = dev_get_drvdata(dev); + struct temp_data *tdata = pdata->core_data[attr->index]; + + if (tdata->is_pkg_data) +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "Package id %u\n", pdata->pkg_id); +#else + return sprintf(buf, "Package id %u\n", pdata->pkg_id); +#endif + +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "Core %u\n", tdata->cpu_core_id); +#else + return sprintf(buf, "Core %u\n", tdata->cpu_core_id); +#endif + +} + +static ssize_t show_crit_alarm(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + u32 eax, edx; + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct platform_data *pdata = dev_get_drvdata(dev); + struct temp_data *tdata = pdata->core_data[attr->index]; + + mutex_lock(&tdata->update_lock); + rdmsr_on_cpu(tdata->cpu, tdata->status_reg, &eax, &edx); + mutex_unlock(&tdata->update_lock); +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", (eax >> 5) & 1); +#else + return sprintf(buf, "%d\n", (eax >> 5) & 1); +#endif + +} + +static ssize_t show_tjmax(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct platform_data *pdata = dev_get_drvdata(dev); +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", pdata->core_data[attr->index]->tjmax); +#else + return sprintf(buf, "%d\n", pdata->core_data[attr->index]->tjmax); +#endif + +} + +static ssize_t show_ttarget(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct platform_data *pdata = dev_get_drvdata(dev); +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", pdata->core_data[attr->index]->ttarget); +#else + return sprintf(buf, "%d\n", pdata->core_data[attr->index]->ttarget); +#endif + +} + +static ssize_t show_temp(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + u32 eax, edx; + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct platform_data *pdata = dev_get_drvdata(dev); + struct temp_data *tdata = pdata->core_data[attr->index]; + + mutex_lock(&tdata->update_lock); + + /* Check whether the time interval has elapsed */ + if (!tdata->valid || time_after(jiffies, tdata->last_updated + HZ)) { + rdmsr_on_cpu(tdata->cpu, tdata->status_reg, &eax, &edx); + /* + * Ignore the valid bit. In all observed cases the register + * value is either low or zero if the valid bit is 0. + * Return it instead of reporting an error which doesn't + * really help at all. + */ + tdata->temp = tdata->tjmax - ((eax >> 16) & 0x7f) * 1000; + tdata->valid = 1; + tdata->last_updated = jiffies; + } + + mutex_unlock(&tdata->update_lock); +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d\n", tdata->temp); +#else + return sprintf(buf, "%d\n", tdata->temp); +#endif + +} + +int coretemp_get_temp_input(int index, long *temp_input) +{ + u32 eax, edx; + struct platform_data *pdata; + struct temp_data *tdata; + + if(s3ip_dev == NULL) + return -ENODEV; + + pdata = dev_get_drvdata(s3ip_dev); + tdata = pdata->core_data[index]; + + /* Check whether the time interval has elapsed */ + if (!tdata->valid || time_after(jiffies, tdata->last_updated + HZ)) { + rdmsr_on_cpu(tdata->cpu, tdata->status_reg, &eax, &edx); + /* + * Ignore the valid bit. In all observed cases the register + * value is either low or zero if the valid bit is 0. + * Return it instead of reporting an error which doesn't + * really help at all. + */ + tdata->temp = tdata->tjmax - ((eax >> 16) & 0x7f) * 1000; + tdata->valid = 1; + tdata->last_updated = jiffies; + } + + mutex_unlock(&tdata->update_lock); + + *temp_input = tdata->temp; + + return 0; +} +EXPORT_SYMBOL_GPL(coretemp_get_temp_input); + +struct tjmax_pci { + unsigned int device; + int tjmax; +}; + +static const struct tjmax_pci tjmax_pci_table[] = { + { 0x0708, 110000 }, /* CE41x0 (Sodaville ) */ + { 0x0c72, 102000 }, /* Atom S1240 (Centerton) */ + { 0x0c73, 95000 }, /* Atom S1220 (Centerton) */ + { 0x0c75, 95000 }, /* Atom S1260 (Centerton) */ +}; + +struct tjmax { + char const *id; + int tjmax; +}; + +static const struct tjmax tjmax_table[] = { + { "CPU 230", 100000 }, /* Model 0x1c, stepping 2 */ + { "CPU 330", 125000 }, /* Model 0x1c, stepping 2 */ +}; + +struct tjmax_model { + u8 model; + u8 mask; + int tjmax; +}; + +#define ANY 0xff + +static const struct tjmax_model tjmax_model_table[] = { + { 0x1c, 10, 100000 }, /* D4xx, K4xx, N4xx, D5xx, K5xx, N5xx */ + { 0x1c, ANY, 90000 }, /* Z5xx, N2xx, possibly others + * Note: Also matches 230 and 330, + * which are covered by tjmax_table + */ + { 0x26, ANY, 90000 }, /* Atom Tunnel Creek (Exx), Lincroft (Z6xx) + * Note: TjMax for E6xxT is 110C, but CPU type + * is undetectable by software + */ + { 0x27, ANY, 90000 }, /* Atom Medfield (Z2460) */ + { 0x35, ANY, 90000 }, /* Atom Clover Trail/Cloverview (Z27x0) */ + { 0x36, ANY, 100000 }, /* Atom Cedar Trail/Cedarview (N2xxx, D2xxx) + * Also matches S12x0 (stepping 9), covered by + * PCI table + */ +}; + +int adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev) +{ + /* The 100C is default for both mobile and non mobile CPUs */ + + int tjmax = 100000; + int tjmax_ee = 85000; + int usemsr_ee = 1; + int err=0; + u32 eax, edx; + int i; + u16 devfn = PCI_DEVFN(0, 0); + struct pci_dev *host_bridge = pci_get_domain_bus_and_slot(0, 0, devfn); + + /* + * Explicit tjmax table entries override heuristics. + * First try PCI host bridge IDs, followed by model ID strings + * and model/stepping information. + */ + if (host_bridge && host_bridge->vendor == PCI_VENDOR_ID_INTEL) { + for (i = 0; i < ARRAY_SIZE(tjmax_pci_table); i++) { + if (host_bridge->device == tjmax_pci_table[i].device) + return tjmax_pci_table[i].tjmax; + } + } + + for (i = 0; i < ARRAY_SIZE(tjmax_table); i++) { + if (strstr(c->x86_model_id, tjmax_table[i].id)) + return tjmax_table[i].tjmax; + } + + for (i = 0; i < ARRAY_SIZE(tjmax_model_table); i++) { + const struct tjmax_model *tm = &tjmax_model_table[i]; + if (c->x86_model == tm->model && + (tm->mask == ANY || c->x86_stepping == tm->mask)) + return tm->tjmax; + } + + /* Early chips have no MSR for TjMax */ + + if (c->x86_model == 0xf && c->x86_stepping < 4) + usemsr_ee = 0; + + if (c->x86_model > 0xe && usemsr_ee) { + u8 platform_id; + + /* + * Now we can detect the mobile CPU using Intel provided table + * http://softwarecommunity.intel.com/Wiki/Mobility/720.htm + * For Core2 cores, check MSR 0x17, bit 28 1 = Mobile CPU + */ + err = rdmsr_safe_on_cpu(id, 0x17, &eax, &edx); + if (err) { + dev_warn(dev, + "Unable to access MSR 0x17, assuming desktop" + " CPU\n"); + usemsr_ee = 0; + } else if (c->x86_model < 0x17 && !(eax & 0x10000000)) { + /* + * Trust bit 28 up to Penryn, I could not find any + * documentation on that; if you happen to know + * someone at Intel please ask + */ + usemsr_ee = 0; + } else { + /* Platform ID bits 52:50 (EDX starts at bit 32) */ + platform_id = (edx >> 18) & 0x7; + + /* + * Mobile Penryn CPU seems to be platform ID 7 or 5 + * (guesswork) + */ + if (c->x86_model == 0x17 && + (platform_id == 5 || platform_id == 7)) { + /* + * If MSR EE bit is set, set it to 90 degrees C, + * otherwise 105 degrees C + */ + tjmax_ee = 90000; + tjmax = 105000; + } + } + } + + if (usemsr_ee) { + err = rdmsr_safe_on_cpu(id, 0xee, &eax, &edx); + if (err) { + dev_warn(dev, + "Unable to access MSR 0xEE, for Tjmax, left" + " at default\n"); + } else if (eax & 0x40000000) { + tjmax = tjmax_ee; + } + } else if (tjmax == 100000) { + /* + * If we don't use msr EE it means we are desktop CPU + * (with exeception of Atom) + */ + dev_warn(dev, "Using relative temperature scale!\n"); + } + + return tjmax; +} +EXPORT_SYMBOL_GPL(adjust_tjmax); + +static bool cpu_has_tjmax(struct cpuinfo_x86 *c) +{ + u8 model = c->x86_model; + + return model > 0xe && + model != 0x1c && + model != 0x26 && + model != 0x27 && + model != 0x35 && + model != 0x36; +} + +static int get_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev) +{ + int err; + u32 eax, edx; + u32 val; + + /* + * A new feature of current Intel(R) processors, the + * IA32_TEMPERATURE_TARGET contains the TjMax value + */ + err = rdmsr_safe_on_cpu(id, MSR_IA32_TEMPERATURE_TARGET, &eax, &edx); + if (err) { + if (cpu_has_tjmax(c)) + dev_warn(dev, "Unable to read TjMax from CPU %u\n", id); + } else { + val = (eax >> 16) & 0xff; + /* + * If the TjMax is not plausible, an assumption + * will be used + */ + if (val) { + dev_dbg(dev, "TjMax is %d degrees C\n", val); + return val * 1000; + } + } + + if (force_tjmax) { + dev_notice(dev, "TjMax forced to %d degrees C by user\n", + force_tjmax); + return force_tjmax * 1000; + } + + /* + * An assumption is made for early CPUs and unreadable MSR. + * NOTE: the calculated value may not be correct. + */ + return adjust_tjmax(c, id, dev); +} + +static int create_core_attrs(struct temp_data *tdata, struct device *dev, + int attr_no) +{ + int i; + static ssize_t (*const rd_ptr[TOTAL_ATTRS]) (struct device *dev, + struct device_attribute *devattr, char *buf) = { + show_label, show_crit_alarm, show_temp, show_tjmax, + show_ttarget }; + static const char *const suffixes[TOTAL_ATTRS] = { + "label", "crit_alarm", "input", "crit", "max" + }; + + for (i = 0; i < tdata->attr_size; i++) { +#ifdef C11_ANNEX_K + if(snprintf_s(tdata->attr_name[i], CORETEMP_NAME_LENGTH, CORETEMP_NAME_LENGTH, + "temp%d_%s", attr_no, suffixes[i]) < 0) +#else + if(snprintf(tdata->attr_name[i], CORETEMP_NAME_LENGTH, + "temp%d_%s", attr_no, suffixes[i]) < 0) +#endif + { + return -ENOMEM; + } + sysfs_attr_init(&tdata->sd_attrs[i].dev_attr.attr); + tdata->sd_attrs[i].dev_attr.attr.name = tdata->attr_name[i]; + tdata->sd_attrs[i].dev_attr.attr.mode = S_IRUGO; + tdata->sd_attrs[i].dev_attr.show = rd_ptr[i]; + tdata->sd_attrs[i].index = attr_no; + tdata->attrs[i] = &tdata->sd_attrs[i].dev_attr.attr; + } + tdata->attr_group.attrs = tdata->attrs; + return sysfs_create_group(&dev->kobj, &tdata->attr_group); +} + + +static int chk_ucode_version(unsigned int cpu) +{ + struct cpuinfo_x86 *c = &cpu_data(cpu); + + /* + * Check if we have problem with errata AE18 of Core processors: + * Readings might stop update when processor visited too deep sleep, + * fixed for stepping D0 (6EC). + */ + if (c->x86_model == 0xe && c->x86_stepping < 0xc && c->microcode < 0x39) { + pr_err("Errata AE18 not fixed, update BIOS or microcode of the CPU!\n"); + return -ENODEV; + } + return 0; +} + + +static struct platform_device *coretemp_get_pdev(unsigned int cpu) +{ + int id = topology_logical_die_id(cpu); + + if (id >= 0 && id < max_zones) + return zone_devices[id]; + return NULL; +} +static struct temp_data *init_temp_data(unsigned int cpu, int pkg_flag) +{ + struct temp_data *tdata; + + tdata = kzalloc(sizeof(struct temp_data), GFP_KERNEL); + if (!tdata) + return NULL; + + tdata->status_reg = pkg_flag ? MSR_IA32_PACKAGE_THERM_STATUS : + MSR_IA32_THERM_STATUS; + tdata->is_pkg_data = pkg_flag; + tdata->cpu = cpu; + tdata->cpu_core_id = TO_CORE_ID(cpu); + tdata->attr_size = MAX_CORE_ATTRS; + mutex_init(&tdata->update_lock); + return tdata; +} + +static int create_core_data(struct platform_device *pdev, unsigned int cpu, + int pkg_flag) +{ + struct temp_data *tdata; + struct platform_data *pdata = platform_get_drvdata(pdev); + struct cpuinfo_x86 *c = &cpu_data(cpu); + u32 eax, edx; + int err=0, attr_no; + + /* + * Find attr number for sysfs: + * We map the attr number to core id of the CPU + * The attr number is always core id + 2 + * The Pkgtemp will always show up as temp1_*, if available + */ + attr_no = pkg_flag ? PKG_SYSFS_ATTR_NO : TO_ATTR_NO(cpu); + + if (attr_no > MAX_CORE_DATA - 1) + return -ERANGE; + + tdata = init_temp_data(cpu, pkg_flag); + if (!tdata) + return -ENOMEM; + + /* Test if we can access the status register */ + err = rdmsr_safe_on_cpu(cpu, tdata->status_reg, &eax, &edx); + if (err) + goto exit_free; + + /* We can access status register. Get Critical Temperature */ + tdata->tjmax = get_tjmax(c, cpu, &pdev->dev); + + /* + * Read the still undocumented bits 8:15 of IA32_TEMPERATURE_TARGET. + * The target temperature is available on older CPUs but not in this + * register. Atoms don't have the register at all. + */ + if (c->x86_model > 0xe && c->x86_model != 0x1c) { + err = rdmsr_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET, + &eax, &edx); + if (!err) { + tdata->ttarget + = tdata->tjmax - ((eax >> 8) & 0xff) * 1000; + tdata->attr_size++; + } + } + + pdata->core_data[attr_no] = tdata; + + /* Create sysfs interfaces */ + err = create_core_attrs(tdata, pdata->hwmon_dev, attr_no); + if (err) + goto exit_free; + + return 0; +exit_free: + pdata->core_data[attr_no] = NULL; + kfree(tdata); + return err; +} + +static void +coretemp_add_core(struct platform_device *pdev, unsigned int cpu, int pkg_flag) +{ + if (create_core_data(pdev, cpu, pkg_flag)) + dev_err(&pdev->dev, "Adding Core %u failed\n", cpu); +} + +static void coretemp_remove_core(struct platform_data *pdata, int indx) +{ + struct temp_data *tdata = pdata->core_data[indx]; + + /* Remove the sysfs attributes */ + sysfs_remove_group(&pdata->hwmon_dev->kobj, &tdata->attr_group); + + kfree(pdata->core_data[indx]); + pdata->core_data[indx] = NULL; +} + +static int coretemp_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct platform_data *pdata; + + /* Initialize the per-zone data structures */ + pdata = devm_kzalloc(dev, sizeof(struct platform_data), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + pdata->pkg_id = pdev->id; + platform_set_drvdata(pdev, pdata); + + pdata->hwmon_dev = devm_hwmon_device_register_with_groups(dev, DRVNAME, + pdata, NULL); + if(s3ip_dev == NULL) + { + s3ip_dev = dev; + } + + return PTR_ERR_OR_ZERO(pdata->hwmon_dev); +} + +static int coretemp_remove(struct platform_device *pdev) +{ + struct platform_data *pdata = platform_get_drvdata(pdev); + int i; + + for (i = MAX_CORE_DATA - 1; i >= 0; --i) + if (pdata->core_data[i]) + coretemp_remove_core(pdata, i); + + if(s3ip_dev != NULL) + { + s3ip_dev = NULL; + } + + return 0; +} + +static struct platform_driver coretemp_driver = { + .driver = { + .name = DRVNAME, + }, + .probe = coretemp_probe, + .remove = coretemp_remove, +}; + +static struct platform_device *coretemp_device_add(unsigned int cpu) +{ + int err, zoneid = topology_logical_die_id(cpu); + + struct platform_device *pdev; + + if (zoneid < 0) + return ERR_PTR(-ENOMEM); + + pdev = platform_device_alloc(DRVNAME, zoneid); + if (!pdev) + return ERR_PTR(-ENOMEM); + + err = platform_device_add(pdev); + if (err) { + platform_device_put(pdev); + return ERR_PTR(err); + } + + zone_devices[zoneid] = pdev; + return pdev; +} + +static int coretemp_cpu_online(unsigned int cpu) +{ + struct platform_device *pdev = coretemp_get_pdev(cpu); + struct cpuinfo_x86 *c = &cpu_data(cpu); + struct platform_data *pdata; + + /* + * Don't execute this on resume as the offline callback did + * not get executed on suspend. + */ + if (cpuhp_tasks_frozen) + return 0; + + /* + * CPUID.06H.EAX[0] indicates whether the CPU has thermal + * sensors. We check this bit only, all the early CPUs + * without thermal sensors will be filtered out. + */ + if (!cpu_has(c, X86_FEATURE_DTHERM)) + return -ENODEV; + + if (!pdev) { + /* Check the microcode version of the CPU */ + if (chk_ucode_version(cpu)) + return -EINVAL; + + /* + * Alright, we have DTS support. + * We are bringing the _first_ core in this pkg + * online. So, initialize per-pkg data structures and + * then bring this core online. + */ + pdev = coretemp_device_add(cpu); + if (IS_ERR(pdev)) + return PTR_ERR(pdev); + + /* + * Check whether pkgtemp support is available. + * If so, add interfaces for pkgtemp. + */ + if (cpu_has(c, X86_FEATURE_PTS)) + coretemp_add_core(pdev, cpu, 1); + } + + pdata = platform_get_drvdata(pdev); + /* + * Check whether a thread sibling is already online. If not add the + * interface for this CPU core. + */ + if (!cpumask_intersects(&pdata->cpumask, topology_sibling_cpumask(cpu))) + coretemp_add_core(pdev, cpu, 0); + + cpumask_set_cpu(cpu, &pdata->cpumask); + return 0; +} + +static int coretemp_cpu_offline(unsigned int cpu) +{ + struct platform_device *pdev = coretemp_get_pdev(cpu); + struct platform_data *pd; + struct temp_data *tdata; + int indx, target; + + /* + * Don't execute this on suspend as the device remove locks + * up the machine. + */ + if (cpuhp_tasks_frozen) + return 0; + + /* If the physical CPU device does not exist, just return */ + if (!pdev) + return 0; + + /* The core id is too big, just return */ + indx = TO_ATTR_NO(cpu); + if (indx > MAX_CORE_DATA - 1) + return 0; + + pd = platform_get_drvdata(pdev); + tdata = pd->core_data[indx]; + + cpumask_clear_cpu(cpu, &pd->cpumask); + + /* + * If this is the last thread sibling, remove the CPU core + * interface, If there is still a sibling online, transfer the + * target cpu of that core interface to it. + */ + target = cpumask_any_and(&pd->cpumask, topology_sibling_cpumask(cpu)); + if (target >= nr_cpu_ids) { + coretemp_remove_core(pd, indx); + } else if (tdata && tdata->cpu == cpu) { + mutex_lock(&tdata->update_lock); + tdata->cpu = target; + mutex_unlock(&tdata->update_lock); + } + + /* + * If all cores in this pkg are offline, remove the device. This + * will invoke the platform driver remove function, which cleans up + * the rest. + */ + if (cpumask_empty(&pd->cpumask)) { + zone_devices[topology_logical_die_id(cpu)] = NULL; + platform_device_unregister(pdev); + return 0; + } + + /* + * Check whether this core is the target for the package + * interface. We need to assign it to some other cpu. + */ + tdata = pd->core_data[PKG_SYSFS_ATTR_NO]; + if (tdata && tdata->cpu == cpu) { + target = cpumask_first(&pd->cpumask); + mutex_lock(&tdata->update_lock); + tdata->cpu = target; + mutex_unlock(&tdata->update_lock); + } + return 0; +} +static const struct x86_cpu_id __initconst coretemp_ids[] = { +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 7, 0) + { X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, X86_FEATURE_DTHERM }, +#else + X86_MATCH_VENDOR_FEATURE(INTEL, X86_FEATURE_DTHERM, NULL), +#endif + + + {} +}; +MODULE_DEVICE_TABLE(x86cpu, coretemp_ids); + +static enum cpuhp_state coretemp_hp_online; + +static int __init coretemp_init(void) +{ + int err=0; + + /* + * CPUID.06H.EAX[0] indicates whether the CPU has thermal + * sensors. We check this bit only, all the early CPUs + * without thermal sensors will be filtered out. + */ + if (!x86_match_cpu(coretemp_ids)) + return -ENODEV; +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 3, 0) + zone_devices = kcalloc(topology_max_packages(), sizeof(struct pkg_device *), + GFP_KERNEL); +#else + max_zones = topology_max_packages() * topology_max_die_per_package(); + zone_devices = kcalloc(max_zones, sizeof(struct platform_device *), + GFP_KERNEL); +#endif + + + if (!zone_devices) + return -ENOMEM; + + err = platform_driver_register(&coretemp_driver); + if (err) + goto outzone; + + err = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "hwmon/coretemp:online", + coretemp_cpu_online, coretemp_cpu_offline); + if (err < 0) + goto outdrv; + coretemp_hp_online = err; + return 0; + +outdrv: + platform_driver_unregister(&coretemp_driver); +outzone: + kfree(zone_devices); + return err; +} +module_init(coretemp_init) + +static void __exit coretemp_exit(void) +{ + cpuhp_remove_state(coretemp_hp_online); + platform_driver_unregister(&coretemp_driver); + kfree(zone_devices); +} +module_exit(coretemp_exit) + +MODULE_AUTHOR("Rudolf Marek "); +MODULE_DESCRIPTION("Intel Core temperature monitor"); +MODULE_LICENSE("GPL"); \ No newline at end of file diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/switch_coretemp.h b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/switch_coretemp.h new file mode 100644 index 0000000000..8b2b0995cb --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/switch_coretemp.h @@ -0,0 +1,6 @@ +#ifndef SWITCH_CORETEMP_H +#define SWITCH_CORETEMP_H + +int coretemp_get_temp_input(int index, long *temp_input); + +#endif /* SWITCH_CORETEMP_H */ \ No newline at end of file diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/switch_i2c_cpld.c b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/switch_i2c_cpld.c new file mode 100644 index 0000000000..1a2bd81c5c --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/switch_i2c_cpld.c @@ -0,0 +1,383 @@ +/* + * Copyright (C) Brandon Chuang + * + * This module supports the A15 cpld that hold the channel select + * mechanism for other i2c slave devices, such as SFP. + * This includes the: + * switch_port CPLD1/CPLD2/CPLD3/CPLD4 + * switch_fan CPLD1/CPLD2 + * + * Based on: + * pca954x.c from Kumar Gala + * Copyright (C) 2006 + * + * Based on: + * pca954x.c from Ken Harrenstien + * Copyright (C) 2004 Google, Inc. (Ken Harrenstien) + * + * Based on: + * i2c-virtual_cb.c from Brian Kuschak + * and + * pca9540.c from Jean Delvare . + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef C11_ANNEX_K +#include "libboundscheck/include/securec.h" +#endif +#include "switch_cpld_driver.h" + + +#define I2C_RW_RETRY_COUNT 10 +#define I2C_RW_RETRY_INTERVAL 60 /* ms */ + +#define BOARD_VERSION_REG 0x00 +#define MOJOR_VERSION_REG 0x01 +#define MONOR_VERSION_REG 0x02 + +#define MAX_I2C_CPLD_NAME_LEN 20 + +static LIST_HEAD(cpld_client_list); +static struct mutex list_lock; + +struct cpld_client_node { + struct i2c_client *client; + struct list_head list; +}; + + +struct switch_i2c_cpld_data { + char cpld_name[MAX_I2C_CPLD_NAME_LEN]; + u8 index; + struct mutex update_lock; + struct attribute_group attr_group; +}; + +/* sysfs attributes for hwmon */ +static int switch_i2c_cpld_read_internal(struct i2c_client *client, u8 reg); +static int switch_i2c_cpld_write_internal(struct i2c_client *client, u8 reg, u8 value); +static ssize_t i2c_cpld_show_byte(struct device *dev, struct device_attribute *devattr, char *buf); +//static ssize_t i2c_cpld_set_byte(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count); + + +static SENSOR_DEVICE_ATTR(board_ver, S_IRUGO, i2c_cpld_show_byte, NULL, BOARD_VERSION_REG); +static SENSOR_DEVICE_ATTR(major_ver, S_IRUGO, i2c_cpld_show_byte, NULL, MOJOR_VERSION_REG); +static SENSOR_DEVICE_ATTR(minor_ver, S_IRUGO, i2c_cpld_show_byte, NULL, MONOR_VERSION_REG); + + +static struct attribute *switch_i2c_cpld_attributes[] = { + &sensor_dev_attr_board_ver.dev_attr.attr, + &sensor_dev_attr_major_ver.dev_attr.attr, + &sensor_dev_attr_minor_ver.dev_attr.attr, + NULL +}; + +static const struct attribute_group switch_i2c_cpld_group = { + .attrs = switch_i2c_cpld_attributes, +}; + +static ssize_t i2c_cpld_show_byte(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct i2c_client *client = to_i2c_client(dev); + struct switch_i2c_cpld_data *data = i2c_get_clientdata(client); + unsigned char reg = attr->index; + int status = 0; + + mutex_lock(&data->update_lock); + status = switch_i2c_cpld_read_internal(client, reg); + mutex_unlock(&data->update_lock); + + if (unlikely(status < 0)) { + return status; + } + + status = status & 0xff; + return snprintf(buf, PAGE_SIZE, "0x%x\n", status); +} +#if 0 +static ssize_t i2c_cpld_set_byte(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct i2c_client *client = to_i2c_client(dev); + struct switch_i2c_cpld_data *data = i2c_get_clientdata(client); + unsigned char reg = attr->index; + unsigned char value; + int status; + + status = kstrtou8(buf, 10, &value); + if (status) { + return status; + } + + mutex_lock(&data->update_lock); + status = switch_i2c_cpld_write_internal(client, reg, value); + mutex_unlock(&data->update_lock); + if (unlikely(status < 0)) { + return status;; + } + + return count; +} +#endif + +static int switch_i2c_cpld_read_internal(struct i2c_client *client, u8 reg) +{ + int status = 0, retry = I2C_RW_RETRY_COUNT; + + while (retry) { + status = i2c_smbus_read_byte_data(client, reg); + if (unlikely(status < 0)) { + msleep(I2C_RW_RETRY_INTERVAL); + retry--; + continue; + } + + break; + } + + return status; +} + +static int switch_i2c_cpld_write_internal(struct i2c_client *client, u8 reg, u8 value) +{ + int status = 0, retry = I2C_RW_RETRY_COUNT; + + while (retry) { + status = i2c_smbus_write_byte_data(client, reg, value); + if (unlikely(status < 0)) { + msleep(I2C_RW_RETRY_INTERVAL); + retry--; + continue; + } + + break; + } + + return status; +} + +static void switch_i2c_cpld_add_client(struct i2c_client *client) +{ + struct cpld_client_node *node = kzalloc(sizeof(struct cpld_client_node), GFP_KERNEL); + + if (!node) { + dev_dbg(&client->dev, "Can't allocate cpld_client_node (0x%x)\n", client->addr); + return; + } + + node->client = client; + + mutex_lock(&list_lock); + list_add(&node->list, &cpld_client_list); + mutex_unlock(&list_lock); +} + +static void switch_i2c_cpld_remove_client(struct i2c_client *client) +{ + struct list_head *list_node = NULL; + struct cpld_client_node *cpld_node = NULL; + int found = 0; + + mutex_lock(&list_lock); + + list_for_each(list_node, &cpld_client_list) + { + cpld_node = list_entry(list_node, struct cpld_client_node, list); + + if (cpld_node->client == client) { + found = 1; + break; + } + } + + if (found) { + list_del(list_node); + kfree(cpld_node); + } + + mutex_unlock(&list_lock); +} + +/* + * I2C init/probing/exit functions + */ +static int switch_i2c_cpld_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adap = to_i2c_adapter(client->dev.parent); + struct device *dev = &client->dev; + struct switch_i2c_cpld_data *data; + int ret = -ENODEV; + + if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE)) { + dev_dbg(dev, "i2c_check_functionality failed (0x%x)\n", client->addr); + return -EIO; + } + + data = devm_kzalloc(dev, sizeof(struct switch_i2c_cpld_data), GFP_KERNEL); + if (!data) { + return -ENOMEM; + } + + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + + if (client->adapter->nr == SYS_CPLD_BUS_NR) { + strcpy(data->cpld_name, "sys_cpld"); + data->index = SYS_CPLD; + } + else if (client->adapter->nr == FAN_CPLD_BUS_NR) { + strcpy(data->cpld_name, "fan_cpld"); + data->index = FAN_CPLD; + } + else if (client->adapter->nr == PORT_CPLD1_BUS_NR) { + strcpy(data->cpld_name, "port_cpld1"); + data->index = PORT_CPLD1; + } + else if (client->adapter->nr == PORT_CPLD2_BUS_NR) { + strcpy(data->cpld_name, "port_cpld2"); + data->index = PORT_CPLD2; + } + else { + mutex_destroy(&data->update_lock); + return -EINVAL; + } + + /* Register sysfs hooks */ + data->attr_group = switch_i2c_cpld_group; + if (&data->attr_group) { + ret = sysfs_create_group(&client->dev.kobj, &data->attr_group); + if (ret) { + mutex_destroy(&data->update_lock); + return ret; + } + } + + switch_i2c_cpld_add_client(client); + return 0; +} + +static int switch_i2c_cpld_remove(struct i2c_client *client) +{ + struct switch_i2c_cpld_data *data = i2c_get_clientdata(client); + + switch_i2c_cpld_remove_client(client); + sysfs_remove_group(&client->dev.kobj, &data->attr_group); + + mutex_destroy(&data->update_lock); + + return 0; +} + +int switch_i2c_cpld_read(u8 index, u8 reg, u8 *value) +{ + struct list_head *list_node = NULL; + struct cpld_client_node *cpld_node = NULL; + struct switch_i2c_cpld_data *data = NULL; + int ret = -EPERM; + + mutex_lock(&list_lock); + list_for_each(list_node, &cpld_client_list) + { + cpld_node = list_entry(list_node, struct cpld_client_node, list); + + data = i2c_get_clientdata(cpld_node->client); + if (data->index == index) { + mutex_lock(&data->update_lock); + ret = switch_i2c_cpld_read_internal(cpld_node->client, reg); + mutex_unlock(&data->update_lock); + break; + } + } + mutex_unlock(&list_lock); + + if(ret < 0) + { + return -1; + } + *value = ret; + return 0; +} +EXPORT_SYMBOL(switch_i2c_cpld_read); + +int switch_i2c_cpld_write(u8 index, u8 reg, u8 value) +{ + struct list_head *list_node = NULL; + struct cpld_client_node *cpld_node = NULL; + struct switch_i2c_cpld_data *data = NULL; + int ret = -EIO; + + mutex_lock(&list_lock); + + list_for_each(list_node, &cpld_client_list) + { + cpld_node = list_entry(list_node, struct cpld_client_node, list); + + data = i2c_get_clientdata(cpld_node->client); + if (data->index == index) { + mutex_lock(&data->update_lock); + ret = switch_i2c_cpld_write_internal(cpld_node->client, reg, value); + mutex_unlock(&data->update_lock); + break; + } + } + + mutex_unlock(&list_lock); + + return ret; +} +EXPORT_SYMBOL(switch_i2c_cpld_write); + + +static const struct i2c_device_id switch_i2c_cpld_id[] = { + { "sys_cpld", sys_cpld }, + { "fan_cpld", fan_cpld }, + { "port_cpld1", port_cpld1 }, + { "port_cpld2", port_cpld2 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, switch_i2c_cpld_id); + +static struct i2c_driver switch_i2c_cpld_driver = { + .driver = { + .name = "switch_i2c_cpld", + .owner = THIS_MODULE, + }, + .probe = switch_i2c_cpld_probe, + .remove = switch_i2c_cpld_remove, + .id_table = switch_i2c_cpld_id, +}; + +static int __init switch_i2c_cpld_init(void) +{ + mutex_init(&list_lock); + return i2c_add_driver(&switch_i2c_cpld_driver); +} + +static void __exit switch_i2c_cpld_exit(void) +{ + i2c_del_driver(&switch_i2c_cpld_driver); + mutex_destroy(&list_lock); +} + +module_init(switch_i2c_cpld_init); +module_exit(switch_i2c_cpld_exit); + +MODULE_AUTHOR("Gil Wei"); +MODULE_DESCRIPTION("Switch I2C CPLD Driver"); +MODULE_LICENSE("GPL"); diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/switch_lm75.c b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/switch_lm75.c new file mode 100644 index 0000000000..fcb82266cd --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/switch_lm75.c @@ -0,0 +1,1028 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * lm75.c - Part of lm_sensors, Linux kernel modules for hardware + * monitoring + * Copyright (c) 1998, 1999 Frodo Looijaard + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include "lm75.h" +#include "switch_lm75.h" +#include "switch_cpld_driver.h" +//simon: try to support 4.x kernel +#include + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 2, 0) +#define HWMON_CHANNEL_INFO(stype, ...) \ + (&(struct hwmon_channel_info) { \ + .type = hwmon_##stype, \ + .config = (u32 []) { \ + __VA_ARGS__, 0 \ + } \ + }) +#endif + + +#include "switch_sensor_driver.h" +#ifdef C11_ANNEX_K +#include "libboundscheck/include/securec.h" +#endif + +struct device *s3ip_hwmon_dev[TEMP_TOTAL_NUM] = {NULL}; + +/* + * This driver handles the LM75 and compatible digital temperature sensors. + */ + +enum lm75_type { /* keep sorted in alphabetical order */ + adt75, + ds1775, + ds75, + ds7505, + g751, + lm75, + lm75a, + lm75b, + max6625, + max6626, + max31725, + mcp980x, + pct2075, + stds75, + stlm75, + tcn75, + tmp100, + tmp101, + tmp105, + tmp112, + tmp175, + tmp275, + tmp75, + tmp75b, + tmp75c, +}; + +/** + * struct lm75_params - lm75 configuration parameters. + * @set_mask: Bits to set in configuration register when configuring + * the chip. + * @clr_mask: Bits to clear in configuration register when configuring + * the chip. + * @default_resolution: Default number of bits to represent the temperature + * value. + * @resolution_limits: Limit register resolution. Optional. Should be set if + * the resolution of limit registers does not match the + * resolution of the temperature register. + * @resolutions: List of resolutions associated with sample times. + * Optional. Should be set if num_sample_times is larger + * than 1, and if the resolution changes with sample times. + * If set, number of entries must match num_sample_times. + * @default_sample_time:Sample time to be set by default. + * @num_sample_times: Number of possible sample times to be set. Optional. + * Should be set if the number of sample times is larger + * than one. + * @sample_times: All the possible sample times to be set. Mandatory if + * num_sample_times is larger than 1. If set, number of + * entries must match num_sample_times. + */ + +struct lm75_params { + u8 set_mask; + u8 clr_mask; + u8 default_resolution; + u8 resolution_limits; + const u8 *resolutions; + unsigned int default_sample_time; + u8 num_sample_times; + const unsigned int *sample_times; +}; + +/* Addresses scanned */ +static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c, + 0x4d, 0x4e, 0x4f, I2C_CLIENT_END }; + +/* The LM75 registers */ +#define LM75_REG_TEMP 0x00 +#define LM75_REG_CONF 0x01 +#define LM75_REG_HYST 0x02 +#define LM75_REG_MAX 0x03 +#define PCT2075_REG_IDLE 0x04 + +/* Each client has this additional data */ +struct lm75_data { + struct i2c_client *client; + struct regmap *regmap; + struct regulator *vs; + u8 orig_conf; + u8 current_conf; + u8 resolution; /* In bits, 9 to 16 */ + unsigned int sample_time; /* In ms */ + enum lm75_type kind; + const struct lm75_params *params; +}; + +/*-----------------------------------------------------------------------*/ + +static const u8 lm75_sample_set_masks[] = { 0 << 5, 1 << 5, 2 << 5, 3 << 5 }; + +#define LM75_SAMPLE_CLEAR_MASK (3 << 5) + +/* The structure below stores the configuration values of the supported devices. + * In case of being supported multiple configurations, the default one must + * always be the first element of the array + */ +static const struct lm75_params device_params[] = { + [adt75] = { + .clr_mask = 1 << 5, /* not one-shot mode */ + .default_resolution = 12, + .default_sample_time = MSEC_PER_SEC / 10, + }, + [ds1775] = { + .clr_mask = 3 << 5, + .set_mask = 2 << 5, /* 11-bit mode */ + .default_resolution = 11, + .default_sample_time = 500, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 125, 250, 500, 1000 }, + .resolutions = (u8 []) {9, 10, 11, 12 }, + }, + [ds75] = { + .clr_mask = 3 << 5, + .set_mask = 2 << 5, /* 11-bit mode */ + .default_resolution = 11, + .default_sample_time = 600, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 150, 300, 600, 1200 }, + .resolutions = (u8 []) {9, 10, 11, 12 }, + }, + [stds75] = { + .clr_mask = 3 << 5, + .set_mask = 2 << 5, /* 11-bit mode */ + .default_resolution = 11, + .default_sample_time = 600, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 150, 300, 600, 1200 }, + .resolutions = (u8 []) {9, 10, 11, 12 }, + }, + [stlm75] = { + .default_resolution = 9, + .default_sample_time = MSEC_PER_SEC / 6, + }, + [ds7505] = { + .set_mask = 3 << 5, /* 12-bit mode*/ + .default_resolution = 12, + .default_sample_time = 200, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 25, 50, 100, 200 }, + .resolutions = (u8 []) {9, 10, 11, 12 }, + }, + [g751] = { + .default_resolution = 9, + .default_sample_time = MSEC_PER_SEC / 10, + }, + [lm75] = { + .default_resolution = 9, + .default_sample_time = MSEC_PER_SEC / 10, + }, + [lm75a] = { + .default_resolution = 9, + .default_sample_time = MSEC_PER_SEC / 10, + }, + [lm75b] = { + .default_resolution = 11, + .default_sample_time = MSEC_PER_SEC / 10, + }, + [max6625] = { + .default_resolution = 9, + .default_sample_time = MSEC_PER_SEC / 7, + }, + [max6626] = { + .default_resolution = 12, + .default_sample_time = MSEC_PER_SEC / 7, + .resolution_limits = 9, + }, + [max31725] = { + .default_resolution = 16, + .default_sample_time = MSEC_PER_SEC / 20, + }, + [tcn75] = { + .default_resolution = 9, + .default_sample_time = MSEC_PER_SEC / 18, + }, + [pct2075] = { + .default_resolution = 11, + .default_sample_time = MSEC_PER_SEC / 10, + .num_sample_times = 31, + .sample_times = (unsigned int []){ 100, 200, 300, 400, 500, 600, + 700, 800, 900, 1000, 1100, 1200, 1300, 1400, 1500, 1600, 1700, + 1800, 1900, 2000, 2100, 2200, 2300, 2400, 2500, 2600, 2700, + 2800, 2900, 3000, 3100 }, + }, + [mcp980x] = { + .set_mask = 3 << 5, /* 12-bit mode */ + .clr_mask = 1 << 7, /* not one-shot mode */ + .default_resolution = 12, + .resolution_limits = 9, + .default_sample_time = 240, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 30, 60, 120, 240 }, + .resolutions = (u8 []) {9, 10, 11, 12 }, + }, + [tmp100] = { + .set_mask = 3 << 5, /* 12-bit mode */ + .clr_mask = 1 << 7, /* not one-shot mode */ + .default_resolution = 12, + .default_sample_time = 320, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 40, 80, 160, 320 }, + .resolutions = (u8 []) {9, 10, 11, 12 }, + }, + [tmp101] = { + .set_mask = 3 << 5, /* 12-bit mode */ + .clr_mask = 1 << 7, /* not one-shot mode */ + .default_resolution = 12, + .default_sample_time = 320, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 40, 80, 160, 320 }, + .resolutions = (u8 []) {9, 10, 11, 12 }, + }, + [tmp105] = { + .set_mask = 3 << 5, /* 12-bit mode */ + .clr_mask = 1 << 7, /* not one-shot mode*/ + .default_resolution = 12, + .default_sample_time = 220, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 28, 55, 110, 220 }, + .resolutions = (u8 []) {9, 10, 11, 12 }, + }, + [tmp112] = { + .set_mask = 3 << 5, /* 8 samples / second */ + .clr_mask = 1 << 7, /* no one-shot mode*/ + .default_resolution = 12, + .default_sample_time = 125, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 125, 250, 1000, 4000 }, + }, + [tmp175] = { + .set_mask = 3 << 5, /* 12-bit mode */ + .clr_mask = 1 << 7, /* not one-shot mode*/ + .default_resolution = 12, + .default_sample_time = 220, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 28, 55, 110, 220 }, + .resolutions = (u8 []) {9, 10, 11, 12 }, + }, + [tmp275] = { + .set_mask = 3 << 5, /* 12-bit mode */ + .clr_mask = 1 << 7, /* not one-shot mode*/ + .default_resolution = 12, + .default_sample_time = 220, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 28, 55, 110, 220 }, + .resolutions = (u8 []) {9, 10, 11, 12 }, + }, + [tmp75] = { + .set_mask = 3 << 5, /* 12-bit mode */ + .clr_mask = 1 << 7, /* not one-shot mode*/ + .default_resolution = 12, + .default_sample_time = 220, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 28, 55, 110, 220 }, + .resolutions = (u8 []) {9, 10, 11, 12 }, + }, + [tmp75b] = { /* not one-shot mode, Conversion rate 37Hz */ + .clr_mask = 1 << 7 | 3 << 5, + .default_resolution = 12, + .default_sample_time = MSEC_PER_SEC / 37, + .sample_times = (unsigned int []){ MSEC_PER_SEC / 37, + MSEC_PER_SEC / 18, + MSEC_PER_SEC / 9, MSEC_PER_SEC / 4 }, + .num_sample_times = 4, + }, + [tmp75c] = { + .clr_mask = 1 << 5, /*not one-shot mode*/ + .default_resolution = 12, + .default_sample_time = MSEC_PER_SEC / 12, + } +}; + +static inline long lm75_reg_to_mc(s16 temp, u8 resolution) +{ + return ((temp >> (16 - resolution)) * 1000) >> (resolution - 8); +} + +static int lm75_write_config(struct lm75_data *data, u8 set_mask, + u8 clr_mask) +{ + u8 value; + + clr_mask |= LM75_SHUTDOWN; + value = data->current_conf & ~clr_mask; + value |= set_mask; + + if (data->current_conf != value) { + s32 err; + + err = i2c_smbus_write_byte_data(data->client, LM75_REG_CONF, + value); + if (err) + return err; + data->current_conf = value; + } + return 0; +} + +static int lm75_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct lm75_data *data = dev_get_drvdata(dev); + unsigned int regval; + int err=0, reg; + + switch (type) { + case hwmon_chip: + switch (attr) { + case hwmon_chip_update_interval: + *val = data->sample_time; + break; + default: + return -EINVAL; + } + break; + case hwmon_temp: + switch (attr) { + case hwmon_temp_input: + reg = LM75_REG_TEMP; + break; + case hwmon_temp_max: + reg = LM75_REG_MAX; + break; + case hwmon_temp_max_hyst: + reg = LM75_REG_HYST; + break; + default: + return -EINVAL; + } + err = regmap_read(data->regmap, reg, ®val); + if (err < 0) + return err; + + *val = lm75_reg_to_mc(regval, data->resolution); + break; + default: + return -EINVAL; + } + return 0; +} + +int lm75_read_temp_max(unsigned int adapter_nr, long *val) +{ + struct lm75_data *s3ip_data; + struct i2c_client *s3ip_client; + struct i2c_adapter *s3ip_adapter; + int i; + + for(i=0; iclient; + s3ip_adapter = to_i2c_adapter(s3ip_client->dev.parent); + + if(s3ip_adapter->nr == adapter_nr) + return lm75_read(s3ip_hwmon_dev[i], hwmon_temp, hwmon_temp_max, 0, val); + } + } + + return -1; +} +EXPORT_SYMBOL_GPL(lm75_read_temp_max); + +int lm75_read_temp_max_hyst(unsigned int adapter_nr, long *val) +{ + struct lm75_data *s3ip_data; + struct i2c_client *s3ip_client; + struct i2c_adapter *s3ip_adapter; + int i; + + for(i=0; iclient; + s3ip_adapter = to_i2c_adapter(s3ip_client->dev.parent); + + if(s3ip_adapter->nr == adapter_nr) + return lm75_read(s3ip_hwmon_dev[i], hwmon_temp, hwmon_temp_max_hyst, 0, val); + } + } + + return -1; +} +EXPORT_SYMBOL_GPL(lm75_read_temp_max_hyst); + +int lm75_read_temp_input(unsigned int adapter_nr, unsigned short chip_addr, long *val) +{ + struct lm75_data *s3ip_data; + struct i2c_client *s3ip_client; + struct i2c_adapter *s3ip_adapter; + int i; + + for(i=0; iclient; + s3ip_adapter = to_i2c_adapter(s3ip_client->dev.parent); + + if((s3ip_adapter->nr == adapter_nr)&&(s3ip_client->addr == chip_addr)) + return lm75_read(s3ip_hwmon_dev[i], hwmon_temp, hwmon_temp_input, 0, val); + } + } + + return -1; +} +EXPORT_SYMBOL_GPL(lm75_read_temp_input); + +static int lm75_write_temp(struct device *dev, u32 attr, long temp) +{ + struct lm75_data *data = dev_get_drvdata(dev); + u8 resolution; + int reg; + + switch (attr) { + case hwmon_temp_max: + reg = LM75_REG_MAX; + break; + case hwmon_temp_max_hyst: + reg = LM75_REG_HYST; + break; + default: + return -EINVAL; + } + + /* + * Resolution of limit registers is assumed to be the same as the + * temperature input register resolution unless given explicitly. + */ + if (data->params->resolution_limits) + resolution = data->params->resolution_limits; + else + resolution = data->resolution; + + temp = clamp_val(temp, LM75_TEMP_MIN, LM75_TEMP_MAX); + temp = DIV_ROUND_CLOSEST(temp << (resolution - 8), + 1000) << (16 - resolution); + + return regmap_write(data->regmap, reg, (u16)temp); +} + +static int lm75_update_interval(struct device *dev, long val) +{ + struct lm75_data *data = dev_get_drvdata(dev); + unsigned int reg; + u8 index; + s32 err; + + index = find_closest(val, data->params->sample_times, + (int)data->params->num_sample_times); + + switch (data->kind) { + default: + err = lm75_write_config(data, lm75_sample_set_masks[index], + LM75_SAMPLE_CLEAR_MASK); + if (err) + return err; + + data->sample_time = data->params->sample_times[index]; + if (data->params->resolutions) + data->resolution = data->params->resolutions[index]; + break; + case tmp112: + err = regmap_read(data->regmap, LM75_REG_CONF, ®); + if (err < 0) + return err; + reg &= ~0x00c0; + reg |= (3 - index) << 6; + err = regmap_write(data->regmap, LM75_REG_CONF, reg); + if (err < 0) + return err; + data->sample_time = data->params->sample_times[index]; + break; + case pct2075: + err = i2c_smbus_write_byte_data(data->client, PCT2075_REG_IDLE, + index + 1); + if (err) + return err; + data->sample_time = data->params->sample_times[index]; + break; + } + return 0; +} + +static int lm75_write_chip(struct device *dev, u32 attr, long val) +{ + switch (attr) { + case hwmon_chip_update_interval: + return lm75_update_interval(dev, val); + default: + return -EINVAL; + } + return 0; +} + +static int lm75_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + switch (type) { + case hwmon_chip: + return lm75_write_chip(dev, attr, val); + case hwmon_temp: + return lm75_write_temp(dev, attr, val); + default: + return -EINVAL; + } + return 0; +} + +static umode_t lm75_is_visible(const void *data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + const struct lm75_data *config_data = data; + + switch (type) { + case hwmon_chip: + switch (attr) { + case hwmon_chip_update_interval: + if (config_data->params->num_sample_times > 1) + return 0644; + return 0444; + } + break; + case hwmon_temp: + switch (attr) { + case hwmon_temp_input: + return 0444; + case hwmon_temp_max: + case hwmon_temp_max_hyst: + return 0644; + } + break; + default: + break; + } + return 0; +} + +static const struct hwmon_channel_info *lm75_info[] = { + HWMON_CHANNEL_INFO(chip, + HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL), + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST), + NULL +}; + +static const struct hwmon_ops lm75_hwmon_ops = { + .is_visible = lm75_is_visible, + .read = lm75_read, + .write = lm75_write, +}; + +static const struct hwmon_chip_info lm75_chip_info = { + .ops = &lm75_hwmon_ops, + .info = lm75_info, +}; + +static bool lm75_is_writeable_reg(struct device *dev, unsigned int reg) +{ + return reg != LM75_REG_TEMP; +} + +static bool lm75_is_volatile_reg(struct device *dev, unsigned int reg) +{ + return reg == LM75_REG_TEMP || reg == LM75_REG_CONF; +} + +static const struct regmap_config lm75_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .max_register = PCT2075_REG_IDLE, + .writeable_reg = lm75_is_writeable_reg, + .volatile_reg = lm75_is_volatile_reg, + .val_format_endian = REGMAP_ENDIAN_BIG, + .cache_type = REGCACHE_RBTREE, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0) + .use_single_read = true, + .use_single_write = true, +#endif +}; + +static void lm75_disable_regulator(void *data) +{ + struct lm75_data *lm75 = data; + + regulator_disable(lm75->vs); +} + +static void lm75_remove(void *data) +{ + struct lm75_data *lm75 = data; + struct i2c_client *client = lm75->client; + i2c_smbus_write_byte_data(client, LM75_REG_CONF, lm75->orig_conf); + +} + +static const struct i2c_device_id lm75_ids[]; + +static int lm75_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct device *hwmon_dev; + struct lm75_data *data; + int status, err; + enum lm75_type kind; + + if (client->dev.of_node) + kind = (enum lm75_type)of_device_get_match_data(&client->dev); + else + kind = i2c_match_id(lm75_ids, client)->driver_data; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) + return -EIO; + + data = devm_kzalloc(dev, sizeof(struct lm75_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->client = client; + data->kind = kind; + + data->vs = devm_regulator_get(dev, "vs"); + if (IS_ERR(data->vs)) + return PTR_ERR(data->vs); + + data->regmap = devm_regmap_init_i2c(client, &lm75_regmap_config); + if (IS_ERR(data->regmap)) + return PTR_ERR(data->regmap); + + /* Set to LM75 resolution (9 bits, 1/2 degree C) and range. + * Then tweak to be more precise when appropriate. + */ + + data->params = &device_params[data->kind]; + + /* Save default sample time and resolution*/ + data->sample_time = data->params->default_sample_time; + data->resolution = data->params->default_resolution; + + /* Enable the power */ + err = regulator_enable(data->vs); + if (err) { + dev_err(dev, "failed to enable regulator: %d\n", err); + return err; + } + + err = devm_add_action_or_reset(dev, lm75_disable_regulator, data); + if (err) + return err; + + /* Cache original configuration */ + status = i2c_smbus_read_byte_data(client, LM75_REG_CONF); + if (status < 0) { + dev_dbg(dev, "Can't read config? %d\n", status); + return status; + } + data->orig_conf = status; + data->current_conf = status; + + err = lm75_write_config(data, data->params->set_mask, + data->params->clr_mask); + if (err) + return err; + + err = devm_add_action_or_reset(dev, lm75_remove, data); + if (err) + return err; + + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, + data, &lm75_chip_info, + NULL); + if (IS_ERR(hwmon_dev)) + return PTR_ERR(hwmon_dev); + dev_info(dev, "%s: sensor '%s'\n", dev_name(hwmon_dev), client->name); + + return 0; +} +EXPORT_SYMBOL_GPL(lm75_probe); + +static const struct i2c_device_id lm75_ids[] = { + { "adt75", adt75, }, + { "ds1775", ds1775, }, + { "ds75", ds75, }, + { "ds7505", ds7505, }, + { "g751", g751, }, + { "lm75", lm75, }, + { "lm75a", lm75a, }, + { "lm75b", lm75b, }, + { "max6625", max6625, }, + { "max6626", max6626, }, + { "max31725", max31725, }, + { "max31726", max31725, }, + { "mcp980x", mcp980x, }, + { "pct2075", pct2075, }, + { "stds75", stds75, }, + { "stlm75", stlm75, }, + { "tcn75", tcn75, }, + { "tmp100", tmp100, }, + { "tmp101", tmp101, }, + { "tmp105", tmp105, }, + { "tmp112", tmp112, }, + { "tmp175", tmp175, }, + { "tmp275", tmp275, }, + { "tmp75", tmp75, }, + { "tmp75b", tmp75b, }, + { "tmp75c", tmp75c, }, + { /* LIST END */ } +}; +MODULE_DEVICE_TABLE(i2c, lm75_ids); + +static const struct of_device_id __maybe_unused lm75_of_match[] = { + { + .compatible = "adi,adt75", + .data = (void *)adt75 + }, + { + .compatible = "dallas,ds1775", + .data = (void *)ds1775 + }, + { + .compatible = "dallas,ds75", + .data = (void *)ds75 + }, + { + .compatible = "dallas,ds7505", + .data = (void *)ds7505 + }, + { + .compatible = "gmt,g751", + .data = (void *)g751 + }, + { + .compatible = "national,lm75", + .data = (void *)lm75 + }, + { + .compatible = "national,lm75a", + .data = (void *)lm75a + }, + { + .compatible = "national,lm75b", + .data = (void *)lm75b + }, + { + .compatible = "maxim,max6625", + .data = (void *)max6625 + }, + { + .compatible = "maxim,max6626", + .data = (void *)max6626 + }, + { + .compatible = "maxim,max31725", + .data = (void *)max31725 + }, + { + .compatible = "maxim,max31726", + .data = (void *)max31725 + }, + { + .compatible = "maxim,mcp980x", + .data = (void *)mcp980x + }, + { + .compatible = "nxp,pct2075", + .data = (void *)pct2075 + }, + { + .compatible = "st,stds75", + .data = (void *)stds75 + }, + { + .compatible = "st,stlm75", + .data = (void *)stlm75 + }, + { + .compatible = "microchip,tcn75", + .data = (void *)tcn75 + }, + { + .compatible = "ti,tmp100", + .data = (void *)tmp100 + }, + { + .compatible = "ti,tmp101", + .data = (void *)tmp101 + }, + { + .compatible = "ti,tmp105", + .data = (void *)tmp105 + }, + { + .compatible = "ti,tmp112", + .data = (void *)tmp112 + }, + { + .compatible = "ti,tmp175", + .data = (void *)tmp175 + }, + { + .compatible = "ti,tmp275", + .data = (void *)tmp275 + }, + { + .compatible = "ti,tmp75", + .data = (void *)tmp75 + }, + { + .compatible = "ti,tmp75b", + .data = (void *)tmp75b + }, + { + .compatible = "ti,tmp75c", + .data = (void *)tmp75c + }, + { }, +}; +MODULE_DEVICE_TABLE(of, lm75_of_match); + +#define LM75A_ID 0xA1 + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int lm75_detect(struct i2c_client *new_client, + struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = new_client->adapter; + int i; + int conf, hyst, os; + bool is_lm75a = 0; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA)) + return -ENODEV; + + /* + * Now, we do the remaining detection. There is no identification- + * dedicated register so we have to rely on several tricks: + * unused bits, registers cycling over 8-address boundaries, + * addresses 0x04-0x07 returning the last read value. + * The cycling+unused addresses combination is not tested, + * since it would significantly slow the detection down and would + * hardly add any value. + * + * The National Semiconductor LM75A is different than earlier + * LM75s. It has an ID byte of 0xaX (where X is the chip + * revision, with 1 being the only revision in existence) in + * register 7, and unused registers return 0xff rather than the + * last read value. + * + * Note that this function only detects the original National + * Semiconductor LM75 and the LM75A. Clones from other vendors + * aren't detected, on purpose, because they are typically never + * found on PC hardware. They are found on embedded designs where + * they can be instantiated explicitly so detection is not needed. + * The absence of identification registers on all these clones + * would make their exhaustive detection very difficult and weak, + * and odds are that the driver would bind to unsupported devices. + */ + + /* Unused bits */ + conf = i2c_smbus_read_byte_data(new_client, 1); + if (conf & 0xe0) + return -ENODEV; + + /* First check for LM75A */ + if (i2c_smbus_read_byte_data(new_client, 7) == LM75A_ID) { + /* + * LM75A returns 0xff on unused registers so + * just to be sure we check for that too. + */ + if (i2c_smbus_read_byte_data(new_client, 4) != 0xff + || i2c_smbus_read_byte_data(new_client, 5) != 0xff + || i2c_smbus_read_byte_data(new_client, 6) != 0xff) + return -ENODEV; + is_lm75a = 1; + hyst = i2c_smbus_read_byte_data(new_client, 2); + os = i2c_smbus_read_byte_data(new_client, 3); + } else { /* Traditional style LM75 detection */ + /* Unused addresses */ + hyst = i2c_smbus_read_byte_data(new_client, 2); + if (i2c_smbus_read_byte_data(new_client, 4) != hyst + || i2c_smbus_read_byte_data(new_client, 5) != hyst + || i2c_smbus_read_byte_data(new_client, 6) != hyst + || i2c_smbus_read_byte_data(new_client, 7) != hyst) + return -ENODEV; + os = i2c_smbus_read_byte_data(new_client, 3); + if (i2c_smbus_read_byte_data(new_client, 4) != os + || i2c_smbus_read_byte_data(new_client, 5) != os + || i2c_smbus_read_byte_data(new_client, 6) != os + || i2c_smbus_read_byte_data(new_client, 7) != os) + return -ENODEV; + } + /* + * It is very unlikely that this is a LM75 if both + * hysteresis and temperature limit registers are 0. + */ + if (hyst == 0 && os == 0) + return -ENODEV; + + /* Addresses cycling */ + for (i = 8; i <= 248; i += 40) { + if (i2c_smbus_read_byte_data(new_client, i + 1) != conf + || i2c_smbus_read_byte_data(new_client, i + 2) != hyst + || i2c_smbus_read_byte_data(new_client, i + 3) != os) + return -ENODEV; + if (is_lm75a && i2c_smbus_read_byte_data(new_client, i + 7) + != LM75A_ID) + return -ENODEV; + } + + strlcpy(info->type, is_lm75a ? "lm75a" : "lm75", I2C_NAME_SIZE); + + return 0; +} +EXPORT_SYMBOL_GPL(lm75_detect); + +#ifdef CONFIG_PM +static int lm75_suspend(struct device *dev) +{ + int status; + struct i2c_client *client = to_i2c_client(dev); + + status = i2c_smbus_read_byte_data(client, LM75_REG_CONF); + if (status < 0) { + dev_dbg(&client->dev, "Can't read config? %d\n", status); + return status; + } + status = status | LM75_SHUTDOWN; + i2c_smbus_write_byte_data(client, LM75_REG_CONF, status); + return 0; +} + +static int lm75_resume(struct device *dev) +{ + int status; + struct i2c_client *client = to_i2c_client(dev); + + status = i2c_smbus_read_byte_data(client, LM75_REG_CONF); + if (status < 0) { + dev_dbg(&client->dev, "Can't read config? %d\n", status); + return status; + } + status = status & ~LM75_SHUTDOWN; + i2c_smbus_write_byte_data(client, LM75_REG_CONF, status); + return 0; +} + +static const struct dev_pm_ops lm75_dev_pm_ops = { + .suspend = lm75_suspend, + .resume = lm75_resume, +}; +#define LM75_DEV_PM_OPS (&lm75_dev_pm_ops) +#else +#define LM75_DEV_PM_OPS NULL +#endif /* CONFIG_PM */ + +static struct i2c_driver lm75_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "switch_lm75", + .of_match_table = of_match_ptr(lm75_of_match), + .pm = LM75_DEV_PM_OPS, + }, + .probe_new = lm75_probe, + .id_table = lm75_ids, + .detect = lm75_detect, + .address_list = normal_i2c, +}; + +module_i2c_driver(lm75_driver); + +MODULE_AUTHOR("Frodo Looijaard "); +MODULE_DESCRIPTION("LM75 driver"); +MODULE_LICENSE("GPL"); diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/switch_lm75.h b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/switch_lm75.h new file mode 100644 index 0000000000..8c5f16236f --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/switch_lm75.h @@ -0,0 +1,54 @@ +/* + lm75.h - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 2003 Mark M. Hoffman + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + This file contains common code for encoding/decoding LM75 type + temperature readings, which are emulated by many of the chips + we support. As the user is unlikely to load more than one driver + which contains this code, we don't worry about the wasted space. +*/ + +#include + +/* straight from the datasheet */ +#define LM75_TEMP_MIN (-55000) +#define LM75_TEMP_MAX 125000 +#define LM75_SHUTDOWN 0x01 + +/* TEMP: 0.001C/bit (-55C to +125C) + REG: (0.5C/bit, two's complement) << 7 */ +static inline u16 LM75_TEMP_TO_REG(long temp) +{ + int ntemp = clamp_val(temp, LM75_TEMP_MIN, LM75_TEMP_MAX); + ntemp += (ntemp < 0 ? -250 : 250); + return (u16)((ntemp / 500) << 7); +} + +static inline int LM75_TEMP_FROM_REG(u16 reg) +{ + /* use integer division instead of equivalent right shift to + guarantee arithmetic shift and preserve the sign */ + return ((s16)reg / 128) * 500; +} + +int lm75_read_temp_max(unsigned int adapter_nr, long *val); +int lm75_read_temp_max_hyst(unsigned int adapter_nr, long *val); +int lm75_read_temp_input(unsigned int adapter_nr, unsigned short chip_addr, long *val); + diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/switch_optoe.c b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/switch_optoe.c new file mode 100644 index 0000000000..b0b0b4c74f --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/switch_optoe.c @@ -0,0 +1,1448 @@ +/* + * optoe.c - A driver to read and write the EEPROM on optical transceivers + * (SFP, QSFP and similar I2C based devices) + * + * Copyright (C) 2014 Cumulus networks Inc. + * Copyright (C) 2017 Finisar Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Freeoftware Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +/* + * Description: + * a) Optical transceiver EEPROM read/write transactions are just like + * the at24 eeproms managed by the at24.c i2c driver + * b) The register/memory layout is up to 256 128 byte pages defined by + * a "pages valid" register and switched via a "page select" + * register as explained in below diagram. + * c) 256 bytes are mapped at a time. 'Lower page 00h' is the first 128 + * bytes of address space, and always references the same + * location, independent of the page select register. + * All mapped pages are mapped into the upper 128 bytes + * (offset 128-255) of the i2c address. + * d) Devices with one I2C address (eg QSFP) use I2C address 0x50 + * (A0h in the spec), and map all pages in the upper 128 bytes + * of that address. + * e) Devices with two I2C addresses (eg SFP) have 256 bytes of data + * at I2C address 0x50, and 256 bytes of data at I2C address + * 0x51 (A2h in the spec). Page selection and paged access + * only apply to this second I2C address (0x51). + * e) The address space is presented, by the driver, as a linear + * address space. For devices with one I2C client at address + * 0x50 (eg QSFP), offset 0-127 are in the lower + * half of address 50/A0h/client[0]. Offset 128-255 are in + * page 0, 256-383 are page 1, etc. More generally, offset + * 'n' resides in page (n/128)-1. ('page -1' is the lower + * half, offset 0-127). + * f) For devices with two I2C clients at address 0x50 and 0x51 (eg SFP), + * the address space places offset 0-127 in the lower + * half of 50/A0/client[0], offset 128-255 in the upper + * half. Offset 256-383 is in the lower half of 51/A2/client[1]. + * Offset 384-511 is in page 0, in the upper half of 51/A2/... + * Offset 512-639 is in page 1, in the upper half of 51/A2/... + * Offset 'n' is in page (n/128)-3 (for n > 383) + * + * One I2c addressed (eg QSFP) Memory Map + * + * 2-Wire Serial Address: 1010000x + * + * Lower Page 00h (128 bytes) + * ===================== + * | | + * | | + * | | + * | | + * | | + * | | + * | | + * | | + * | | + * | | + * |Page Select Byte(127)| + * ===================== + * | + * | + * | + * | + * V + * ------------------------------------------------------------ + * | | | | + * | | | | + * | | | | + * | | | | + * | | | | + * | | | | + * | | | | + * | | | | + * | | | | + * V V V V + * ------------ -------------- --------------- -------------- + * | | | | | | | | + * | Upper | | Upper | | Upper | | Upper | + * | Page 00h | | Page 01h | | Page 02h | | Page 03h | + * | | | (Optional) | | (Optional) | | (Optional | + * | | | | | | | for Cable | + * | | | | | | | Assemblies) | + * | ID | | AST | | User | | | + * | Fields | | Table | | EEPROM Data | | | + * | | | | | | | | + * | | | | | | | | + * | | | | | | | | + * ------------ -------------- --------------- -------------- + * + * The SFF 8436 (QSFP) spec only defines the 4 pages described above. + * In anticipation of future applications and devices, this driver + * supports access to the full architected range, 256 pages. + * + * The CMIS (Common Management Interface Specification) defines use of + * considerably more pages (at least to page 0xAF), which this driver + * supports. + * + * NOTE: This version of the driver ONLY SUPPORTS BANK 0 PAGES on CMIS + * devices. + * + **/ + +/* #define DEBUG 1 */ + +#undef EEPROM_CLASS +#ifdef CONFIG_EEPROM_CLASS +#define EEPROM_CLASS +#endif +#ifdef CONFIG_EEPROM_CLASS_MODULE +#define EEPROM_CLASS +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//simon: try to support linux 4.x +#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0) + #define i2c_new_dummy_device i2c_new_dummy +#endif + + +#ifdef EEPROM_CLASS +#include +#endif + +#include +#include "switch_optoe.h" +#ifdef C11_ANNEX_K +#include "libboundscheck/include/securec.h" +#endif + +/* The maximum length of a port name */ +#define MAX_PORT_NAME_LEN 20 + +struct optoe_platform_data { + u32 byte_len; /* size (sum of all addr) */ + u16 page_size; /* for writes */ + u8 flags; + void *dummy1; /* backward compatibility */ + void *dummy2; /* backward compatibility */ + +#ifdef EEPROM_CLASS + struct eeprom_platform_data *eeprom_data; +#endif + char port_name[MAX_PORT_NAME_LEN]; +}; + +/* a few constants to find our way around the EEPROM */ +#define OPTOE_PAGE_SELECT_REG 0x7F +#define ONE_ADDR_PAGEABLE_REG 0x02 +#define QSFP_NOT_PAGEABLE (1<<2) +#define CMIS_NOT_PAGEABLE (1<<7) +#define TWO_ADDR_PAGEABLE_REG 0x40 +#define TWO_ADDR_PAGEABLE (1<<4) +#define TWO_ADDR_0X51_REG 92 +#define TWO_ADDR_0X51_SUPP (1<<6) +#define OPTOE_ID_REG 0 +#define OPTOE_READ_OP 0 +#define OPTOE_WRITE_OP 1 +#define OPTOE_EOF 0 /* used for access beyond end of device */ + +struct optoe_data { + struct optoe_platform_data chip; + int use_smbus; + char port_name[MAX_PORT_NAME_LEN]; + + /* + * Lock protects against activities from other Linux tasks, + * but not from changes by other I2C masters. + */ + struct mutex lock; + struct bin_attribute bin; + struct attribute_group attr_group; + + u8 *writebuf; + unsigned int write_max; + + unsigned int num_addresses; + +#ifdef EEPROM_CLASS + struct eeprom_device *eeprom_dev; +#endif + + /* dev_class: ONE_ADDR (QSFP) or TWO_ADDR (SFP) */ + int dev_class; + + struct i2c_client *client[]; +}; + +// For S3IP SONiC optoe access +#define MAX_SWITCH_OPTOE_PORT 32 +struct optoe_data *s3ip_optoe_data[MAX_SWITCH_OPTOE_PORT]={NULL}; + +/* + * This parameter is to help this driver avoid blocking other drivers out + * of I2C for potentially troublesome amounts of time. With a 100 kHz I2C + * clock, one 256 byte read takes about 1/43 second which is excessive; + * but the 1/170 second it takes at 400 kHz may be quite reasonable; and + * at 1 MHz (Fm+) a 1/430 second delay could easily be invisible. + * + * This value is forced to be a power of two so that writes align on pages. + */ +static unsigned int io_limit = OPTOE_PAGE_SIZE; + +/* + * specs often allow 5 msec for a page write, sometimes 20 msec; + * it's important to recover from write timeouts. + */ +static unsigned int write_timeout = 25; + +/* + * flags to distinguish one-address (QSFP family) from two-address (SFP family) + * If the family is not known, figure it out when the device is accessed + */ +#define ONE_ADDR 1 +#define TWO_ADDR 2 +#define CMIS_ADDR 3 + +static const struct i2c_device_id optoe_ids[] = { + { "switch_optoe1", ONE_ADDR }, + { "switch_optoe2", TWO_ADDR }, + { "switch_optoe3", CMIS_ADDR }, + { "switch_sff8436", ONE_ADDR }, + { "switch_24c04", TWO_ADDR }, + { /* END OF LIST */ } +}; +MODULE_DEVICE_TABLE(i2c, optoe_ids); + +/*-------------------------------------------------------------------------*/ +/* + * This routine computes the addressing information to be used for + * a given r/w request. + * + * Task is to calculate the client (0 = i2c addr 50, 1 = i2c addr 51), + * the page, and the offset. + * + * Handles both single address (eg QSFP) and two address (eg SFP). + * For SFP, offset 0-255 are on client[0], >255 is on client[1] + * Offset 256-383 are on the lower half of client[1] + * Pages are accessible on the upper half of client[1]. + * Offset >383 are in 128 byte pages mapped into the upper half + * + * For QSFP, all offsets are on client[0] + * offset 0-127 are on the lower half of client[0] (no paging) + * Pages are accessible on the upper half of client[1]. + * Offset >127 are in 128 byte pages mapped into the upper half + * + * Callers must not read/write beyond the end of a client or a page + * without recomputing the client/page. Hence offset (within page) + * plus length must be less than or equal to 128. (Note that this + * routine does not have access to the length of the call, hence + * cannot do the validity check.) + * + * Offset within Lower Page 00h and Upper Page 00h are not recomputed + */ + +static uint8_t optoe_translate_offset(struct optoe_data *optoe, + loff_t *offset, struct i2c_client **client) +{ + unsigned int page = 0; + + *client = optoe->client[0]; + + /* if SFP style, offset > 255, shift to i2c addr 0x51 */ + if (optoe->dev_class == TWO_ADDR) { + if (*offset > 255) { + /* like QSFP, but shifted to client[1] */ + *client = optoe->client[1]; + *offset -= 256; + } + } + + /* + * if offset is in the range 0-128... + * page doesn't matter (using lower half), return 0. + * offset is already correct (don't add 128 to get to paged area) + */ + if (*offset < OPTOE_PAGE_SIZE) + return page; + + /* note, page will always be positive since *offset >= 128 */ + page = (*offset >> 7)-1; + /* 0x80 places the offset in the top half, offset is last 7 bits */ + *offset = OPTOE_PAGE_SIZE + (*offset & 0x7f); + + return page; /* note also returning client and offset */ +} + +static ssize_t optoe_eeprom_read(struct optoe_data *optoe, + struct i2c_client *client, + char *buf, unsigned int offset, size_t count) +{ + struct i2c_msg msg[2]; + u8 msgbuf[2]; + unsigned long timeout, read_time; + int status, i; +#ifdef C11_ANNEX_K + memset_s(msg, sizeof(msg), 0, sizeof(msg)); +#else + memset(msg, 0, sizeof(msg)); +#endif + + switch (optoe->use_smbus) { + case I2C_SMBUS_I2C_BLOCK_DATA: + /*smaller eeproms can work given some SMBus extension calls */ + if (count > I2C_SMBUS_BLOCK_MAX) + count = I2C_SMBUS_BLOCK_MAX; + break; + case I2C_SMBUS_WORD_DATA: + /* Check for odd length transaction */ + count = (count == 1) ? 1 : 2; + break; + case I2C_SMBUS_BYTE_DATA: + count = 1; + break; + default: + /* + * When we have a better choice than SMBus calls, use a + * combined I2C message. Write address; then read up to + * io_limit data bytes. msgbuf is u8 and will cast to our + * needs. + */ + i = 0; + msgbuf[i++] = offset; + + msg[0].addr = client->addr; + msg[0].buf = msgbuf; + msg[0].len = i; + + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].buf = buf; + msg[1].len = count; + } + + /* + * Reads fail if the previous write didn't complete yet. We may + * loop a few times until this one succeeds, waiting at least + * long enough for one entire page write to work. + */ + timeout = jiffies + msecs_to_jiffies(write_timeout); + do { + read_time = jiffies; + + switch (optoe->use_smbus) { + case I2C_SMBUS_I2C_BLOCK_DATA: + status = i2c_smbus_read_i2c_block_data(client, offset, + count, buf); + break; + case I2C_SMBUS_WORD_DATA: + status = i2c_smbus_read_word_data(client, offset); + if (status >= 0) { + buf[0] = status & 0xff; + if (count == 2) + buf[1] = status >> 8; + status = count; + } + break; + case I2C_SMBUS_BYTE_DATA: + status = i2c_smbus_read_byte_data(client, offset); + if (status >= 0) { + buf[0] = status; + status = count; + } + break; + default: + status = i2c_transfer(client->adapter, msg, 2); + if (status == 2) + status = count; + } + + dev_dbg(&client->dev, "eeprom read %zu@%d --> %d (%ld)\n", + count, offset, status, jiffies); + + if (status == count) /* happy path */ + return count; + + if (status == -ENXIO) /* no module present */ + return status; + + /* REVISIT: at HZ=100, this is sloooow */ + usleep_range(1000, 2000); + } while (time_before(read_time, timeout)); + + return -ETIMEDOUT; +} +EXPORT_SYMBOL_GPL(optoe_eeprom_read); + +ssize_t optoe_eeprom_write(struct optoe_data *optoe, + struct i2c_client *client, + const char *buf, + unsigned int offset, size_t count) +{ + struct i2c_msg msg; + ssize_t status; + unsigned long timeout, write_time; + unsigned int next_page_start; + int i = 0; + + /* write max is at most a page + * (In this driver, write_max is actually one byte!) + */ + if (count > optoe->write_max) + count = optoe->write_max; + + /* shorten count if necessary to avoid crossing page boundary */ + next_page_start = roundup(offset + 1, OPTOE_PAGE_SIZE); + if (offset + count > next_page_start) + count = next_page_start - offset; + + switch (optoe->use_smbus) { + case I2C_SMBUS_I2C_BLOCK_DATA: + /*smaller eeproms can work given some SMBus extension calls */ + if (count > I2C_SMBUS_BLOCK_MAX) + count = I2C_SMBUS_BLOCK_MAX; + break; + case I2C_SMBUS_WORD_DATA: + /* Check for odd length transaction */ + count = (count == 1) ? 1 : 2; + break; + case I2C_SMBUS_BYTE_DATA: + count = 1; + break; + default: + /* If we'll use I2C calls for I/O, set up the message */ + msg.addr = client->addr; + msg.flags = 0; + + /* msg.buf is u8 and casts will mask the values */ + msg.buf = optoe->writebuf; + + msg.buf[i++] = offset; +#ifdef C11_ANNEX_K + if(memcpy_s(&msg.buf[i], OPTOE_PAGE_SIZE+2, buf, count) != 0) + { + return -ENOMEM; + } +#else + memcpy(&msg.buf[i], buf, count); +#endif + + msg.len = i + count; + break; + } + + /* + * Reads fail if the previous write didn't complete yet. We may + * loop a few times until this one succeeds, waiting at least + * long enough for one entire page write to work. + */ + timeout = jiffies + msecs_to_jiffies(write_timeout); + do { + write_time = jiffies; + + switch (optoe->use_smbus) { + case I2C_SMBUS_I2C_BLOCK_DATA: + status = i2c_smbus_write_i2c_block_data(client, + offset, count, buf); + if (status == 0) + status = count; + break; + case I2C_SMBUS_WORD_DATA: + if (count == 2) { + status = i2c_smbus_write_word_data(client, + offset, (u16)((buf[0])|(buf[1] << 8))); + } else { + /* count = 1 */ + status = i2c_smbus_write_byte_data(client, + offset, buf[0]); + } + if (status == 0) + status = count; + break; + case I2C_SMBUS_BYTE_DATA: + status = i2c_smbus_write_byte_data(client, offset, + buf[0]); + if (status == 0) + status = count; + break; + default: + status = i2c_transfer(client->adapter, &msg, 1); + if (status == 1) + status = count; + break; + } + + dev_dbg(&client->dev, "eeprom write %zu@%d --> %ld (%lu)\n", + count, offset, (long int) status, jiffies); + + if (status == count) + return count; + + /* REVISIT: at HZ=100, this is sloooow */ + usleep_range(1000, 2000); + } while (time_before(write_time, timeout)); + + return -ETIMEDOUT; +} +EXPORT_SYMBOL_GPL(optoe_eeprom_write); + +ssize_t optoe_eeprom_update_client(struct optoe_data *optoe, + char *buf, loff_t off, + size_t count, int opcode) +{ + struct i2c_client *client; + ssize_t retval = 0; + uint8_t page = 0; + loff_t phy_offset = off; + int ret = 0; + ssize_t status; + + page = optoe_translate_offset(optoe, &phy_offset, &client); + dev_dbg(&client->dev, + "%s off %lld page:%d phy_offset:%lld, count:%ld, opcode:%d\n", + __func__, off, page, phy_offset, (long int) count, opcode); + if (page > 0) { + ret = optoe_eeprom_write(optoe, client, &page, + OPTOE_PAGE_SELECT_REG, 1); + if (ret < 0) { + dev_dbg(&client->dev, + "Write page register for page %d failed ret:%d!\n", + page, ret); + return ret; + } + } + + while (count) { + if (opcode == OPTOE_READ_OP) { + status = optoe_eeprom_read(optoe, client, + buf, phy_offset, count); + } else { + status = optoe_eeprom_write(optoe, client, + buf, phy_offset, count); + } + if (status <= 0) { + if (retval == 0) + retval = status; + break; + } + buf += status; + phy_offset += status; + count -= status; + retval += status; + } + + + if (page > 0) { + /* return the page register to page 0 (why?) */ + page = 0; + ret = optoe_eeprom_write(optoe, client, &page, + OPTOE_PAGE_SELECT_REG, 1); + if (ret < 0) { + dev_err(&client->dev, + "Restore page register to 0 failed:%d!\n", ret); + /* error only if nothing has been transferred */ + if (retval == 0) + retval = ret; + } + } + return retval; +} +EXPORT_SYMBOL_GPL(optoe_eeprom_update_client); + +/* + * Figure out if this access is within the range of supported pages. + * Note this is called on every access because we don't know if the + * module has been replaced since the last call. + * If/when modules support more pages, this is the routine to update + * to validate and allow access to additional pages. + * + * Returns updated len for this access: + * - entire access is legal, original len is returned. + * - access begins legal but is too long, len is truncated to fit. + * - initial offset exceeds supported pages, return OPTOE_EOF (zero) + */ +ssize_t optoe_page_legal(struct optoe_data *optoe, + loff_t off, size_t len, int eeprom_size) +{ + struct i2c_client *client = optoe->client[0]; + u8 regval; + int not_pageable; + int status; + size_t maxlen; + + if (off < 0) + return -EINVAL; + if (optoe->dev_class == TWO_ADDR) { + /* SFP case */ + /* if only using addr 0x50 (first 256 bytes) we're good */ + if ((off + len) <= TWO_ADDR_NO_0X51_SIZE) + return len; + /* if offset exceeds possible pages, we're not good */ + if (off >= TWO_ADDR_EEPROM_SIZE) + return OPTOE_EOF; + /* in between, are pages supported? */ + status = optoe_eeprom_read(optoe, client, ®val, + TWO_ADDR_PAGEABLE_REG, 1); + if (status < 0) + return status; /* error out (no module?) */ + if (regval & TWO_ADDR_PAGEABLE) { + /* Pages supported, trim len to the end of pages */ + maxlen = TWO_ADDR_EEPROM_SIZE - off; + } else { + /* pages not supported, trim len to unpaged size */ + if (off >= TWO_ADDR_EEPROM_UNPAGED_SIZE) + return OPTOE_EOF; + + /* will be accessing addr 0x51, is that supported? */ + /* byte 92, bit 6 implies DDM support, 0x51 support */ + status = optoe_eeprom_read(optoe, client, ®val, + TWO_ADDR_0X51_REG, 1); + if (status < 0) + return status; + if (regval & TWO_ADDR_0X51_SUPP) { + /* addr 0x51 is OK */ + maxlen = TWO_ADDR_EEPROM_UNPAGED_SIZE - off; + } else { + /* addr 0x51 NOT supported, trim to 256 max */ + if (off >= TWO_ADDR_NO_0X51_SIZE) + return OPTOE_EOF; + maxlen = TWO_ADDR_NO_0X51_SIZE - off; + } + } + len = (len > maxlen) ? maxlen : len; + dev_dbg(&client->dev, + "page_legal, SFP, off %lld len %ld\n", + off, (long int) len); + } else { + /* QSFP case, CMIS case */ + /* if no pages needed, we're good */ + if ((off + len) <= ONE_ADDR_EEPROM_UNPAGED_SIZE) + return len; + /* if offset exceeds possible pages, we're not good */ + if (off >= eeprom_size) + return OPTOE_EOF; + /* in between, are pages supported? */ + status = optoe_eeprom_read(optoe, client, ®val, + ONE_ADDR_PAGEABLE_REG, 1); + if (status < 0) + return status; /* error out (no module?) */ + + if (optoe->dev_class == ONE_ADDR) { + not_pageable = QSFP_NOT_PAGEABLE; + } else { + not_pageable = CMIS_NOT_PAGEABLE; + } + dev_dbg(&client->dev, + "Paging Register: 0x%x; not_pageable mask: 0x%x\n", + regval, not_pageable); + + if (regval & not_pageable) { + /* pages not supported, trim len to unpaged size */ + if (off >= ONE_ADDR_EEPROM_UNPAGED_SIZE) + return OPTOE_EOF; + maxlen = ONE_ADDR_EEPROM_UNPAGED_SIZE - off; + } else { + /* Pages supported, trim len to the end of pages */ + maxlen = eeprom_size - off; + } + len = (len > maxlen) ? maxlen : len; + dev_dbg(&client->dev, + "page_legal, QSFP, off %lld len %ld\n", + off, (long int) len); + } + return len; +} +EXPORT_SYMBOL_GPL(optoe_page_legal); + +ssize_t optoe_read_write(struct optoe_data *optoe, + char *buf, loff_t off, size_t len, int opcode, int eeprom_size) +{ + struct i2c_client *client = optoe->client[0]; + int chunk; + int status = 0; + ssize_t retval; + size_t pending_len = 0, chunk_len = 0; + loff_t chunk_offset = 0, chunk_start_offset = 0; + loff_t chunk_end_offset = 0; + + dev_dbg(&client->dev, + "%s: off %lld len:%ld, opcode:%s\n", + __func__, off, (long int) len, + (opcode == OPTOE_READ_OP) ? "r" : "w"); + if (unlikely(!len)) + return len; + + /* + * Read data from chip, protecting against concurrent updates + * from this host, but not from other I2C masters. + */ + mutex_lock(&optoe->lock); + + /* + * Confirm this access fits within the device suppored addr range + */ + status = optoe_page_legal(optoe, off, len, eeprom_size); + if ((status == OPTOE_EOF) || (status < 0)) { + mutex_unlock(&optoe->lock); + return status; + } + len = status; + + /* + * For each (128 byte) chunk involved in this request, issue a + * separate call to sff_eeprom_update_client(), to + * ensure that each access recalculates the client/page + * and writes the page register as needed. + * Note that chunk to page mapping is confusing, is different for + * QSFP and SFP, and never needs to be done. Don't try! + */ + pending_len = len; /* amount remaining to transfer */ + retval = 0; /* amount transferred */ + for (chunk = off >> 7; chunk <= (off + len - 1) >> 7; chunk++) { + + /* + * Compute the offset and number of bytes to be read/write + * + * 1. start at an offset not equal to 0 (within the chunk) + * and read/write less than the rest of the chunk + * 2. start at an offset not equal to 0 and read/write the rest + * of the chunk + * 3. start at offset 0 (within the chunk) and read/write less + * than entire chunk + * 4. start at offset 0 (within the chunk), and read/write + * the entire chunk + */ + chunk_start_offset = chunk * OPTOE_PAGE_SIZE; + chunk_end_offset = chunk_start_offset + OPTOE_PAGE_SIZE; + + if (chunk_start_offset < off) { + chunk_offset = off; + if ((off + pending_len) < chunk_end_offset) + chunk_len = pending_len; + else + chunk_len = chunk_end_offset - off; + } else { + chunk_offset = chunk_start_offset; + if (pending_len < OPTOE_PAGE_SIZE) + chunk_len = pending_len; + else + chunk_len = OPTOE_PAGE_SIZE; + } + + dev_dbg(&client->dev, + "sff_r/w: off %lld, len %ld, chunk_start_offset %lld, chunk_offset %lld, chunk_len %ld, pending_len %ld\n", + off, (long int) len, chunk_start_offset, chunk_offset, + (long int) chunk_len, (long int) pending_len); + + /* + * note: chunk_offset is from the start of the EEPROM, + * not the start of the chunk + */ + status = optoe_eeprom_update_client(optoe, buf, + chunk_offset, chunk_len, opcode); + if (status != chunk_len) { + /* This is another 'no device present' path */ + dev_dbg(&client->dev, + "o_u_c: chunk %d c_offset %lld c_len %ld failed %d!\n", + chunk, chunk_offset, (long int) chunk_len, status); + if (status > 0) + retval += status; + if (retval == 0) + retval = status; + break; + } + buf += status; + pending_len -= status; + retval += status; + } + mutex_unlock(&optoe->lock); + + return retval; +} +EXPORT_SYMBOL_GPL(optoe_read_write); + +static ssize_t optoe_bin_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct i2c_client *client = to_i2c_client(container_of(kobj, + struct device, kobj)); + int eeprom_size = ONE_ADDR_EEPROM_SIZE; + struct optoe_data *optoe = i2c_get_clientdata(client); + + return optoe_read_write(optoe, buf, off, count, OPTOE_READ_OP, eeprom_size); +} + +int optoe_bin_eeprom_size_get(unsigned int index, int *eeprom_size) +{ + unsigned char Connector = 0, transmitter = 0; + unsigned int conn_offset=0,transmitter_offset = 0,transmitter_bit = 0; + ssize_t ret = 0; + struct optoe_data *optoe = NULL; + optoe = s3ip_optoe_data[index]; + if(optoe->dev_class == ONE_ADDR){ + conn_offset = QSFP_CONNECTOR_OFF; + transmitter_offset = QSFP_TRANSMITTER_OFF; + transmitter_bit = QSFP_TRANSMITTER_BIT; + }else if (optoe->dev_class == CMIS_ADDR ){ + conn_offset = QSFP_DD_CONNECTOR_OFF; + transmitter_offset = QSFP_DD_TRANSMITTER_OFF; + transmitter_bit = QSFP_DD_TRANSMITTER_BIT; + } + ret = optoe_read_write(optoe, &Connector, conn_offset, sizeof(Connector), OPTOE_READ_OP,ONE_ADDR_EEPROM_SIZE); + if(ret != sizeof(Connector)){ + dev_dbg(&optoe->client[0]->dev, + "%s read :0x%x failed ret:%ld\n", + __func__, conn_offset, ret); + return ret; + }else if(Connector == CONNECTOR_TYPE){ + ret = optoe_read_write(optoe, &transmitter, transmitter_offset, sizeof(transmitter), OPTOE_READ_OP,ONE_ADDR_EEPROM_SIZE); + if(ret != sizeof(transmitter)){ + dev_dbg(&optoe->client[0]->dev, + "%s read :0x%x failed ret:%ld\n", + __func__, conn_offset, ret); + return ret; + }else if ((transmitter >> QSFP_TRANSMITTER_BIT & 0xf) >= TRANSMITTER_CROTICA_VALUE){ + *eeprom_size = ONE_ADDR_EEPROM_UNPAGED_SIZE; + }else{ + if (optoe->dev_class == ONE_ADDR){ + *eeprom_size = ONE_ADDR_EEPROM_PAGED_SIZE; + }else{ + *eeprom_size = CMIS_ADDR_EEPROM_SIZE; + } + } + }else{ + if (optoe->dev_class == ONE_ADDR){ + *eeprom_size = ONE_ADDR_EEPROM_PAGED_SIZE; + }else{ + *eeprom_size = CMIS_ADDR_EEPROM_SIZE; + } + } + + return ret; +} + +int optoe_bin_read_by_index(unsigned int index, char *buf, loff_t off, size_t len) +{ + int temp_optoe_index = 0; + int port_num; + int retval=0; + int eeprom_size = ONE_ADDR_EEPROM_SIZE; + for(temp_optoe_index=0; temp_optoe_index < MAX_SWITCH_OPTOE_PORT; temp_optoe_index++) + { + if(s3ip_optoe_data[temp_optoe_index] == NULL) + { + continue; + } + + retval = kstrtoint(s3ip_optoe_data[temp_optoe_index]->port_name+sizeof(ETH_NAME_STRING)-1, 10, &port_num); + if(retval != 0) + { + return -ENODEV; + } + + if(port_num == index) + { + break; + } + } + + if(temp_optoe_index == MAX_SWITCH_OPTOE_PORT) + { + return -ENODEV; + } + retval = optoe_bin_eeprom_size_get(temp_optoe_index,&eeprom_size); + if(retval < 0){ + return retval; + } + return optoe_read_write(s3ip_optoe_data[temp_optoe_index], buf, off, len, OPTOE_READ_OP, eeprom_size); +} +EXPORT_SYMBOL_GPL(optoe_bin_read_by_index); + +int optoe_bin_write_by_index(unsigned int index, char *buf, loff_t off, size_t len) +{ + int temp_optoe_index = 0; + int port_num; + int retval=0; + int eeprom_size = ONE_ADDR_EEPROM_SIZE; + for(temp_optoe_index=0; temp_optoe_index < MAX_SWITCH_OPTOE_PORT; temp_optoe_index++) + { + if(s3ip_optoe_data[temp_optoe_index] == NULL) + { + continue; + } + + retval = kstrtoint(s3ip_optoe_data[temp_optoe_index]->port_name+sizeof(ETH_NAME_STRING)-1, 10, &port_num); + if(retval != 0) + { + return -ENODEV; + } + + if(port_num == index) + { + break; + } + } + + if(temp_optoe_index == MAX_SWITCH_OPTOE_PORT) + { + return -ENODEV; + } + retval = optoe_bin_eeprom_size_get(temp_optoe_index,&eeprom_size); + if(retval < 0){ + return retval; + } + return optoe_read_write(s3ip_optoe_data[temp_optoe_index], buf, off, len, OPTOE_WRITE_OP,eeprom_size); +} +EXPORT_SYMBOL_GPL(optoe_bin_write_by_index); + +static ssize_t optoe_bin_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct i2c_client *client = to_i2c_client(container_of(kobj, + struct device, kobj)); + int eeprom_size = ONE_ADDR_EEPROM_SIZE; + struct optoe_data *optoe = i2c_get_clientdata(client); + + return optoe_read_write(optoe, buf, off, count, OPTOE_WRITE_OP,eeprom_size); +} + +static int optoe_remove(struct i2c_client *client) +{ + struct optoe_data *optoe; + int i; + int temp_optoe_index = 0; + + optoe = i2c_get_clientdata(client); + + for(temp_optoe_index=0; temp_optoe_index < MAX_SWITCH_OPTOE_PORT; temp_optoe_index++) + { + if(s3ip_optoe_data[temp_optoe_index] == NULL) + { + continue; + } + + if(s3ip_optoe_data[temp_optoe_index] == optoe) + { + s3ip_optoe_data[temp_optoe_index] = NULL; + } + } + + sysfs_remove_group(&client->dev.kobj, &optoe->attr_group); + sysfs_remove_bin_file(&client->dev.kobj, &optoe->bin); + + for (i = 1; i < optoe->num_addresses; i++) + i2c_unregister_device(optoe->client[i]); + +#ifdef EEPROM_CLASS + eeprom_device_unregister(optoe->eeprom_dev); +#endif + + mutex_destroy(&optoe->lock); + + kfree(optoe->writebuf); + kfree(optoe); + return 0; +} + +static ssize_t show_dev_class(struct device *dev, + struct device_attribute *dattr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct optoe_data *optoe = i2c_get_clientdata(client); + ssize_t count; + + mutex_lock(&optoe->lock); +#ifdef C11_ANNEX_K + count = sprintf_s(buf, 256, "%d\n", optoe->dev_class); +#else + count = sprintf(buf, "%d\n", optoe->dev_class); +#endif + mutex_unlock(&optoe->lock); + + return count; +} + +static ssize_t set_dev_class(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct optoe_data *optoe = i2c_get_clientdata(client); + int dev_class; + + /* + * dev_class is actually the number of i2c addresses used, thus + * legal values are "1" (QSFP class) and "2" (SFP class) + * And... CMIS spec is 1 i2c address, but puts the pageable + * bit in a different location, so CMIS devices are "3" + */ + + if (kstrtoint(buf, 0, &dev_class) != 0 || + dev_class < 1 || dev_class > 3) + return -EINVAL; + + mutex_lock(&optoe->lock); + if (dev_class == TWO_ADDR) { + /* SFP family */ + /* if it doesn't exist, create 0x51 i2c address */ + if (!optoe->client[1]) { + optoe->client[1] = i2c_new_dummy_device(client->adapter, 0x51); + if (!optoe->client[1]) { + dev_err(&client->dev, + "address 0x51 unavailable\n"); + mutex_unlock(&optoe->lock); + return -EADDRINUSE; + } + } + optoe->bin.size = TWO_ADDR_EEPROM_SIZE; + optoe->num_addresses = 2; + } else { + /* one-address (eg QSFP) and CMIS family */ + /* if it exists, remove 0x51 i2c address */ + if (optoe->client[1]) + i2c_unregister_device(optoe->client[1]); + optoe->bin.size = ONE_ADDR_EEPROM_SIZE; + optoe->num_addresses = 1; + } + optoe->dev_class = dev_class; + mutex_unlock(&optoe->lock); + + return count; +} + +/* + * if using the EEPROM CLASS driver, we don't report a port_name, + * the EEPROM CLASS drive handles that. Hence all this code is + * only compiled if we are NOT using the EEPROM CLASS driver. + */ +#ifndef EEPROM_CLASS + +static ssize_t show_port_name(struct device *dev, + struct device_attribute *dattr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct optoe_data *optoe = i2c_get_clientdata(client); + ssize_t count; + + mutex_lock(&optoe->lock); +#ifdef C11_ANNEX_K + count = sprintf_s(buf, MAX_PORT_NAME_LEN, "%s\n", optoe->port_name); +#else + count = sprintf(buf, "%s\n", optoe->port_name); +#endif + mutex_unlock(&optoe->lock); + + return count; +} + +static ssize_t set_port_name(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct optoe_data *optoe = i2c_get_clientdata(client); + char port_name[MAX_PORT_NAME_LEN]; + + /* no checking, this value is not used except by show_port_name */ +#ifdef C11_ANNEX_K + if (sscanf_s(buf, "%19s", port_name) < 0) +#else + if (sscanf(buf, "%19s", port_name) < 0) +#endif + { + return -EINVAL; + } + + mutex_lock(&optoe->lock); +#ifdef C11_ANNEX_K + strcpy_s(optoe->port_name, MAX_PORT_NAME_LEN, buf); +#else + strcpy(optoe->port_name, buf); +#endif + mutex_unlock(&optoe->lock); + + return count; +} + +static DEVICE_ATTR(port_name, 0644, show_port_name, set_port_name); +#endif /* if NOT defined EEPROM_CLASS, the common case */ + +static DEVICE_ATTR(dev_class, 0644, show_dev_class, set_dev_class); + +static struct attribute *optoe_attrs[] = { +#ifndef EEPROM_CLASS + &dev_attr_port_name.attr, +#endif + &dev_attr_dev_class.attr, + NULL, +}; + +static struct attribute_group optoe_attr_group = { + .attrs = optoe_attrs, +}; + +int optoe_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int err=0; + int use_smbus = 0; + struct optoe_platform_data chip; + struct optoe_data *optoe; + int num_addresses = 0; + char port_name[MAX_PORT_NAME_LEN]; + int temp_optoe_index = 0; + + if (client->addr != 0x50) { + dev_dbg(&client->dev, "probe, bad i2c addr: 0x%x\n", + client->addr); + err = -EINVAL; + goto exit; + } + + if (client->dev.platform_data) { + chip = *(struct optoe_platform_data *)client->dev.platform_data; + /* take the port name from the supplied platform data */ +#ifdef EEPROM_CLASS +#ifdef C11_ANNEX_K + if(strncpy_s(port_name, MAX_PORT_NAME_LEN, chip.eeprom_data->label, MAX_PORT_NAME_LEN) != 0) + { + return -ENOMEM; + } +#else + strncpy(port_name, chip.eeprom_data->label, MAX_PORT_NAME_LEN) +#endif + +#else +#ifdef C11_ANNEX_K + if(memcpy_s(port_name, MAX_PORT_NAME_LEN, chip.port_name, MAX_PORT_NAME_LEN) != 0) + { + err = -ENOMEM; + goto exit; + } +#else + memcpy(port_name, chip.port_name, MAX_PORT_NAME_LEN); +#endif + +#endif + dev_dbg(&client->dev, + "probe, chip provided, flags:0x%x; name: %s\n", + chip.flags, client->name); + } else { + if (!id->driver_data) { + err = -ENODEV; + goto exit; + } + dev_dbg(&client->dev, "probe, building chip\n"); +#ifdef C11_ANNEX_K + strcpy_s(port_name, MAX_PORT_NAME_LEN, "unitialized"); +#else + strcpy(port_name, "unitialized"); +#endif + chip.flags = 0; +#ifdef EEPROM_CLASS + chip.eeprom_data = NULL; +#endif + } + +#if 0 + /* Use I2C operations unless we're stuck with SMBus extensions. */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + if (i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_I2C_BLOCK)) { + use_smbus = I2C_SMBUS_I2C_BLOCK_DATA; + } else if (i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_WORD_DATA)) { + use_smbus = I2C_SMBUS_WORD_DATA; + } else if (i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_BYTE_DATA)) { + use_smbus = I2C_SMBUS_BYTE_DATA; + } else { + err = -EPFNOSUPPORT; + goto exit; + } + } +#else + /* Use the I2C_SMBUS_BYTE_DATA to avoid the incorrect data retrieving of some modules. */ + use_smbus = I2C_SMBUS_BYTE_DATA; +#endif + + + /* + * Make room for two i2c clients + */ + num_addresses = 2; + + optoe = kzalloc(sizeof(struct optoe_data) + + num_addresses * sizeof(struct i2c_client *), + GFP_KERNEL); + if (!optoe) { + err = -ENOMEM; + goto exit; + } + + mutex_init(&optoe->lock); + + /* determine whether this is a one-address or two-address module */ + if ((strcmp(client->name, "switch_optoe1") == 0) || + (strcmp(client->name, "switch_sff8436") == 0)) { + /* one-address (eg QSFP) family */ + optoe->dev_class = ONE_ADDR; + chip.byte_len = ONE_ADDR_EEPROM_SIZE; + num_addresses = 1; + } else if ((strcmp(client->name, "switch_optoe2") == 0) || + (strcmp(client->name, "switch_24c04") == 0)) { + /* SFP family */ + optoe->dev_class = TWO_ADDR; + chip.byte_len = TWO_ADDR_EEPROM_SIZE; + num_addresses = 2; + } else if (strcmp(client->name, "switch_optoe3") == 0) { + /* CMIS spec */ + optoe->dev_class = CMIS_ADDR; + chip.byte_len = ONE_ADDR_EEPROM_SIZE; + num_addresses = 1; + } else { /* those were the only choices */ + err = -EINVAL; + mutex_destroy(&optoe->lock); + goto exit; + } + + dev_dbg(&client->dev, "dev_class: %d\n", optoe->dev_class); + optoe->use_smbus = use_smbus; + optoe->chip = chip; + optoe->num_addresses = num_addresses; +#ifdef C11_ANNEX_K + if(memcpy_s(optoe->port_name, MAX_PORT_NAME_LEN, port_name, MAX_PORT_NAME_LEN) != 0) + { + err = -ENOMEM; + mutex_destroy(&optoe->lock); + goto exit; + } +#else + memcpy(optoe->port_name, port_name, MAX_PORT_NAME_LEN); +#endif + + /* + * Export the EEPROM bytes through sysfs, since that's convenient. + * By default, only root should see the data (maybe passwords etc) + */ + sysfs_bin_attr_init(&optoe->bin); + optoe->bin.attr.name = "eeprom"; + optoe->bin.attr.mode = 0444; + optoe->bin.read = optoe_bin_read; + optoe->bin.size = chip.byte_len; + + if (!use_smbus || + (i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) || + i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WRITE_WORD_DATA) || + i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) { + /* + * NOTE: AN-2079 + * Finisar recommends that the host implement 1 byte writes + * only since this module only supports 32 byte page boundaries. + * 2 byte writes are acceptable for PE and Vout changes per + * Application Note AN-2071. + */ + unsigned int write_max = 1; + + optoe->bin.write = optoe_bin_write; + optoe->bin.attr.mode |= 0200; + + if (write_max > io_limit) + write_max = io_limit; + if (use_smbus && write_max > I2C_SMBUS_BLOCK_MAX) + write_max = I2C_SMBUS_BLOCK_MAX; + optoe->write_max = write_max; + + /* buffer (data + address at the beginning) */ + optoe->writebuf = kmalloc(write_max + 2, GFP_KERNEL); + if (!optoe->writebuf) { + err = -ENOMEM; + mutex_destroy(&optoe->lock); + goto exit_kfree; + } + } else { + dev_warn(&client->dev, + "cannot write due to controller restrictions."); + } + + optoe->client[0] = client; + + /* SFF-8472 spec requires that the second I2C address be 0x51 */ + if (num_addresses == 2) { + optoe->client[1] = i2c_new_dummy_device(client->adapter, 0x51); + if (!optoe->client[1]) { + dev_err(&client->dev, "address 0x51 unavailable\n"); + err = -EADDRINUSE; + mutex_destroy(&optoe->lock); + goto err_struct; + } + } + + /* create the sysfs eeprom file */ + err = sysfs_create_bin_file(&client->dev.kobj, &optoe->bin); + if (err) { + mutex_destroy(&optoe->lock); + goto err_struct; + } + + optoe->attr_group = optoe_attr_group; + + err = sysfs_create_group(&client->dev.kobj, &optoe->attr_group); + if (err) { + dev_err(&client->dev, "failed to create sysfs attribute group.\n"); + mutex_destroy(&optoe->lock); + goto err_struct; + } + +#ifdef EEPROM_CLASS + optoe->eeprom_dev = eeprom_device_register(&client->dev, + chip.eeprom_data); + if (IS_ERR(optoe->eeprom_dev)) { + dev_err(&client->dev, "error registering eeprom device.\n"); + err = PTR_ERR(optoe->eeprom_dev); + mutex_destroy(&optoe->lock); + goto err_sysfs_cleanup; + } +#endif + + i2c_set_clientdata(client, optoe); + + dev_info(&client->dev, "%zu byte %s EEPROM, %s\n", + optoe->bin.size, client->name, + optoe->bin.write ? "read/write" : "read-only"); + +#if 0 /* Use the I2C_SMBUS_BYTE_DATA to avoid the incorrect data retrieving of some modules, not print notice messages here. */ + if (use_smbus == I2C_SMBUS_WORD_DATA || + use_smbus == I2C_SMBUS_BYTE_DATA) { + dev_notice(&client->dev, + "Falling back to %s reads, performance will suffer\n", + use_smbus == I2C_SMBUS_WORD_DATA ? "word" : "byte"); + } +#endif + + /* Give the optoe default port_name to ethxx, xx is i2c bus ID. + bus 201 -> eth1, bus 202 -> eth2 ..., etc. + KS SONiC tranciver use port name to map optoe. */ +#ifdef C11_ANNEX_K + if(sprintf_s(optoe->port_name, MAX_PORT_NAME_LEN, "%s%d", ETH_NAME_STRING, ((client->adapter->nr)%100)) < 0) +#else + if(sprintf(optoe->port_name, "%s%d", ETH_NAME_STRING, ((client->adapter->nr)%100)) < 0) +#endif + + { + dev_err(&client->dev, "switch_optoe sprintf failed\n"); + mutex_destroy(&optoe->lock); + return -ENOMEM; + } + + for(temp_optoe_index=0; temp_optoe_index < MAX_SWITCH_OPTOE_PORT; temp_optoe_index++) + { + if(s3ip_optoe_data[temp_optoe_index] != NULL) + { + continue; + } + + s3ip_optoe_data[temp_optoe_index] = optoe; + if(!s3ip_optoe_data[temp_optoe_index]) + { + dev_err(&client->dev, "s3ip_optoe_data is NULL.\n"); + mutex_destroy(&optoe->lock); + return -ENOMEM; + } + else + { + // success + break; + } + } + + return 0; + +#ifdef EEPROM_CLASS +err_sysfs_cleanup: + sysfs_remove_group(&client->dev.kobj, &optoe->attr_group); + sysfs_remove_bin_file(&client->dev.kobj, &optoe->bin); +#endif + +err_struct: + if (num_addresses == 2) { + if (optoe->client[1]) + i2c_unregister_device(optoe->client[1]); + } + + kfree(optoe->writebuf); +exit_kfree: + kfree(optoe); +exit: + dev_dbg(&client->dev, "probe error %d\n", err); + + return err; +} +EXPORT_SYMBOL_GPL(optoe_probe); + +/*-------------------------------------------------------------------------*/ + +static struct i2c_driver optoe_driver = { + .driver = { + .name = "switch_optoe", + .owner = THIS_MODULE, + }, + .probe = optoe_probe, + .remove = optoe_remove, + .id_table = optoe_ids, +}; + +static int __init optoe_init(void) +{ + + if (!io_limit) { + pr_err("optoe: io_limit must not be 0!\n"); + return -EINVAL; + } + + io_limit = rounddown_pow_of_two(io_limit); + return i2c_add_driver(&optoe_driver); +} +module_init(optoe_init); + +static void __exit optoe_exit(void) +{ + i2c_del_driver(&optoe_driver); +} +module_exit(optoe_exit); + +MODULE_DESCRIPTION("Driver for optical transceiver (SFP, QSFP, ...) EEPROMs"); +MODULE_AUTHOR("DON BOLLINGER "); +MODULE_VERSION("0.0.2"); +MODULE_LICENSE("GPL"); diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/switch_optoe.h b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/switch_optoe.h new file mode 100644 index 0000000000..11dafe3335 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/switch_optoe.h @@ -0,0 +1,40 @@ +#include + +#define ETH_NAME_STRING "eth" + +/* fundamental unit of addressing for EEPROM */ +#define OPTOE_PAGE_SIZE 128 +/* + * Single address devices (eg QSFP) have 256 pages, plus the unpaged + * low 128 bytes. If the device does not support paging, it is + * only 2 'pages' long. + */ +#define OPTOE_ARCH_PAGES 255 +#define ONE_ADDR_EEPROM_SIZE ((1 + OPTOE_ARCH_PAGES) * OPTOE_PAGE_SIZE) +#define ONE_ADDR_EEPROM_UNPAGED_SIZE (2 * OPTOE_PAGE_SIZE) +#define ONE_ADDR_EEPROM_PAGED_SIZE (6 * OPTOE_PAGE_SIZE) +#define CMIS_ADDR_EEPROM_SIZE ((2 + 0X11) * OPTOE_PAGE_SIZE) +/* + * Dual address devices (eg SFP) have 256 pages, plus the unpaged + * low 128 bytes, plus 256 bytes at 0x50. If the device does not + * support paging, it is 4 'pages' long. + */ +#define TWO_ADDR_EEPROM_SIZE ((3 + OPTOE_ARCH_PAGES) * OPTOE_PAGE_SIZE) +#define TWO_ADDR_EEPROM_UNPAGED_SIZE (4 * OPTOE_PAGE_SIZE) +#define TWO_ADDR_NO_0X51_SIZE (2 * OPTOE_PAGE_SIZE) + +#define QSFP_DD_CONNECTOR_OFF 203 +#define QSFP_CONNECTOR_OFF 130 +#define QSFP_DD_TRANSMITTER_OFF 212 +#define QSFP_TRANSMITTER_OFF 147 +#define TRANSMITTER_CROTICA_VALUE 0xa +#define QSFP_DD_TRANSMITTER_BIT 0 +#define QSFP_TRANSMITTER_BIT 4 +#define CONNECTOR_TYPE 0x23 + + +int optoe_bin_read_by_index(unsigned int index, char *buf, loff_t off, size_t len); +int optoe_bin_write_by_index(unsigned int index, char *buf, loff_t off, size_t len); +int optoe_get_disable_iic_access(char *buf, long iic_value); +int optoe_set_disable_iic_access(char *buf, long iic_value); + diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/switch_pmbus_core.c b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/switch_pmbus_core.c new file mode 100644 index 0000000000..8763514266 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/switch_pmbus_core.c @@ -0,0 +1,2960 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Hardware monitoring driver for PMBus devices + * + * Copyright (c) 2010, 2011 Ericsson AB. + * Copyright (c) 2012 Guenter Roeck + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pmbus.h" +#include "switch_sensor_driver.h" + +//simon: try to support linux 4.x +#include + +struct device *s3ip_hwmon_dev[TOTAL_VR_CHIP_NUM] = {NULL}; + +/* + * Number of additional attribute pointers to allocate + * with each call to krealloc + */ +#define PMBUS_ATTR_ALLOC_SIZE 32 +#define PMBUS_NAME_SIZE 24 + +struct pmbus_sensor { + struct pmbus_sensor *next; + char name[PMBUS_NAME_SIZE]; /* sysfs sensor name */ + struct device_attribute attribute; + u8 page; /* page number */ + u8 phase; /* phase number, 0xff for all phases */ + u16 reg; /* register */ + enum pmbus_sensor_classes class; /* sensor class */ + bool update; /* runtime sensor update needed */ + bool convert; /* Whether or not to apply linear/vid/direct */ + int data; /* Sensor data. + Negative if there was a read error */ +}; +#define to_pmbus_sensor(_attr) \ + container_of(_attr, struct pmbus_sensor, attribute) + +struct pmbus_boolean { + char name[PMBUS_NAME_SIZE]; /* sysfs boolean name */ + struct sensor_device_attribute attribute; + struct pmbus_sensor *s1; + struct pmbus_sensor *s2; +}; +#define to_pmbus_boolean(_attr) \ + container_of(_attr, struct pmbus_boolean, attribute) + +struct pmbus_label { + char name[PMBUS_NAME_SIZE]; /* sysfs label name */ + struct device_attribute attribute; + char label[PMBUS_NAME_SIZE]; /* label */ +}; +#define to_pmbus_label(_attr) \ + container_of(_attr, struct pmbus_label, attribute) + +/* Macros for converting between sensor index and register/page/status mask */ + +#define PB_STATUS_MASK 0xffff +#define PB_REG_SHIFT 16 +#define PB_REG_MASK 0x3ff +#define PB_PAGE_SHIFT 26 +#define PB_PAGE_MASK 0x3f + +#define pb_reg_to_index(page, reg, mask) (((page) << PB_PAGE_SHIFT) | \ + ((reg) << PB_REG_SHIFT) | (mask)) + +#define pb_index_to_page(index) (((index) >> PB_PAGE_SHIFT) & PB_PAGE_MASK) +#define pb_index_to_reg(index) (((index) >> PB_REG_SHIFT) & PB_REG_MASK) +#define pb_index_to_mask(index) ((index) & PB_STATUS_MASK) + +struct pmbus_data { + struct device *dev; + struct device *hwmon_dev; + + u32 flags; /* from platform data */ + + int exponent[PMBUS_PAGES]; + /* linear mode: exponent for output voltages */ + + const struct pmbus_driver_info *info; + + int max_attributes; + int num_attributes; + struct attribute_group group; + const struct attribute_group **groups; + struct dentry *debugfs; /* debugfs device directory */ + + struct pmbus_sensor *sensors; + + struct mutex update_lock; + + bool has_status_word; /* device uses STATUS_WORD register */ + int (*read_status)(struct i2c_client *client, int page); + + s16 currpage; /* current page, -1 for unknown/unset */ + s16 currphase; /* current phase, 0xff for all, -1 for unknown/unset */ +}; + +struct pmbus_debugfs_entry { + struct i2c_client *client; + u8 page; + u8 reg; +}; + +static const int pmbus_fan_rpm_mask[] = { + PB_FAN_1_RPM, + PB_FAN_2_RPM, + PB_FAN_1_RPM, + PB_FAN_2_RPM, +}; + +static const int pmbus_fan_config_registers[] = { + PMBUS_FAN_CONFIG_12, + PMBUS_FAN_CONFIG_12, + PMBUS_FAN_CONFIG_34, + PMBUS_FAN_CONFIG_34 +}; + +static const int pmbus_fan_command_registers[] = { + PMBUS_FAN_COMMAND_1, + PMBUS_FAN_COMMAND_2, + PMBUS_FAN_COMMAND_3, + PMBUS_FAN_COMMAND_4, +}; + +void pmbus_clear_cache(struct i2c_client *client) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + struct pmbus_sensor *sensor; + + for (sensor = data->sensors; sensor; sensor = sensor->next) + sensor->data = -ENODATA; +} +//EXPORT_SYMBOL_GPL(pmbus_clear_cache); + +int pmbus_set_page(struct i2c_client *client, int page, int phase) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + int rv; + + if (page < 0) + return 0; + + if (!(data->info->func[page] & PMBUS_PAGE_VIRTUAL) && + data->info->pages > 1 && page != data->currpage) { + rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page); + if (rv < 0) + return rv; + + rv = i2c_smbus_read_byte_data(client, PMBUS_PAGE); + if (rv < 0) + return rv; + + if (rv != page) + return -EIO; + } + data->currpage = page; + + if (data->info->phases[page] && data->currphase != phase && + !(data->info->func[page] & PMBUS_PHASE_VIRTUAL)) { + rv = i2c_smbus_write_byte_data(client, PMBUS_PHASE, + phase); + if (rv) + return rv; + } + data->currphase = phase; + + return 0; +} +//EXPORT_SYMBOL_GPL(pmbus_set_page); + +int pmbus_write_byte(struct i2c_client *client, int page, u8 value) +{ + int rv; + + rv = pmbus_set_page(client, page, 0xff); + if (rv < 0) + return rv; + + return i2c_smbus_write_byte(client, value); +} +//EXPORT_SYMBOL_GPL(pmbus_write_byte); + +/* + * _pmbus_write_byte() is similar to pmbus_write_byte(), but checks if + * a device specific mapping function exists and calls it if necessary. + */ +static int _pmbus_write_byte(struct i2c_client *client, int page, u8 value) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + const struct pmbus_driver_info *info = data->info; + int status; + + if (info->write_byte) { + status = info->write_byte(client, page, value); + if (status != -ENODATA) + return status; + } + return pmbus_write_byte(client, page, value); +} + +int pmbus_write_word_data(struct i2c_client *client, int page, u8 reg, + u16 word) +{ + int rv; + + rv = pmbus_set_page(client, page, 0xff); + if (rv < 0) + return rv; + + return i2c_smbus_write_word_data(client, reg, word); +} +//EXPORT_SYMBOL_GPL(pmbus_write_word_data); + + +static int pmbus_write_virt_reg(struct i2c_client *client, int page, int reg, + u16 word) +{ + int bit; + int id; + int rv; + + switch (reg) { + case PMBUS_VIRT_FAN_TARGET_1 ... PMBUS_VIRT_FAN_TARGET_4: + id = reg - PMBUS_VIRT_FAN_TARGET_1; + bit = pmbus_fan_rpm_mask[id]; + rv = pmbus_update_fan(client, page, id, bit, bit, word); + break; + default: + rv = -ENXIO; + break; + } + + return rv; +} + +/* + * _pmbus_write_word_data() is similar to pmbus_write_word_data(), but checks if + * a device specific mapping function exists and calls it if necessary. + */ +static int _pmbus_write_word_data(struct i2c_client *client, int page, int reg, + u16 word) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + const struct pmbus_driver_info *info = data->info; + int status; + + if (info->write_word_data) { + status = info->write_word_data(client, page, reg, word); + if (status != -ENODATA) + return status; + } + + if (reg >= PMBUS_VIRT_BASE) + return pmbus_write_virt_reg(client, page, reg, word); + + return pmbus_write_word_data(client, page, reg, word); +} + +int pmbus_update_fan(struct i2c_client *client, int page, int id, + u8 config, u8 mask, u16 command) +{ + int from; + int rv; + u8 to; + + from = pmbus_read_byte_data(client, page, + pmbus_fan_config_registers[id]); + if (from < 0) + return from; + + to = (from & ~mask) | (config & mask); + if (to != from) { + rv = pmbus_write_byte_data(client, page, + pmbus_fan_config_registers[id], to); + if (rv < 0) + return rv; + } + + return _pmbus_write_word_data(client, page, + pmbus_fan_command_registers[id], command); +} +//EXPORT_SYMBOL_GPL(pmbus_update_fan); + +int pmbus_read_word_data(struct i2c_client *client, int page, int phase, u8 reg) +{ + int rv; + + rv = pmbus_set_page(client, page, phase); + if (rv < 0) + return rv; + + return i2c_smbus_read_word_data(client, reg); +} +//EXPORT_SYMBOL_GPL(pmbus_read_word_data); + +static int pmbus_read_virt_reg(struct i2c_client *client, int page, int reg) +{ + int rv; + int id; + + switch (reg) { + case PMBUS_VIRT_FAN_TARGET_1 ... PMBUS_VIRT_FAN_TARGET_4: + id = reg - PMBUS_VIRT_FAN_TARGET_1; + rv = pmbus_get_fan_rate_device(client, page, id, rpm); + break; + default: + rv = -ENXIO; + break; + } + + return rv; +} + +/* + * _pmbus_read_word_data() is similar to pmbus_read_word_data(), but checks if + * a device specific mapping function exists and calls it if necessary. + */ +static int _pmbus_read_word_data(struct i2c_client *client, int page, + int phase, int reg) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + const struct pmbus_driver_info *info = data->info; + int status; + + if (info->read_word_data) { + status = info->read_word_data(client, page, phase, reg); + if (status != -ENODATA) + return status; + } + + if (reg >= PMBUS_VIRT_BASE) + return pmbus_read_virt_reg(client, page, reg); + + return pmbus_read_word_data(client, page, phase, reg); +} + +/* Same as above, but without phase parameter, for use in check functions */ +static int __pmbus_read_word_data(struct i2c_client *client, int page, int reg) +{ + return _pmbus_read_word_data(client, page, 0xff, reg); +} + +int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg) +{ + int rv; + + rv = pmbus_set_page(client, page, 0xff); + if (rv < 0) + return rv; + + return i2c_smbus_read_byte_data(client, reg); +} +//EXPORT_SYMBOL_GPL(pmbus_read_byte_data); + +int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg, u8 value) +{ + int rv; + + rv = pmbus_set_page(client, page, 0xff); + if (rv < 0) + return rv; + + return i2c_smbus_write_byte_data(client, reg, value); +} +//EXPORT_SYMBOL_GPL(pmbus_write_byte_data); + +int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg, + u8 mask, u8 value) +{ + unsigned int tmp; + int rv; + + rv = pmbus_read_byte_data(client, page, reg); + if (rv < 0) + return rv; + + tmp = (rv & ~mask) | (value & mask); + + if (tmp != rv) + rv = pmbus_write_byte_data(client, page, reg, tmp); + + return rv; +} +//EXPORT_SYMBOL_GPL(pmbus_update_byte_data); + +/* + * _pmbus_read_byte_data() is similar to pmbus_read_byte_data(), but checks if + * a device specific mapping function exists and calls it if necessary. + */ +static int _pmbus_read_byte_data(struct i2c_client *client, int page, int reg) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + const struct pmbus_driver_info *info = data->info; + int status; + + if (info->read_byte_data) { + status = info->read_byte_data(client, page, reg); + if (status != -ENODATA) + return status; + } + return pmbus_read_byte_data(client, page, reg); +} + +int pmbus_read_block_data(struct i2c_client *client, int page, int phase, u8 reg, unsigned char *data) +{ + int rv; + + rv = pmbus_set_page(client, page, phase); + if (rv < 0) + return rv; + + return i2c_smbus_read_block_data(client, reg, data); +} +EXPORT_SYMBOL_GPL(pmbus_read_block_data); + +static struct pmbus_sensor *pmbus_find_sensor(struct pmbus_data *data, int page, + int reg) +{ + struct pmbus_sensor *sensor; + + for (sensor = data->sensors; sensor; sensor = sensor->next) { + if (sensor->page == page && sensor->reg == reg) + return sensor; + } + + return ERR_PTR(-EINVAL); +} + +static int pmbus_get_fan_rate(struct i2c_client *client, int page, int id, + enum pmbus_fan_mode mode, + bool from_cache) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + bool want_rpm, have_rpm; + struct pmbus_sensor *s; + int config; + int reg; + + want_rpm = (mode == rpm); + + if (from_cache) { + reg = want_rpm ? PMBUS_VIRT_FAN_TARGET_1 : PMBUS_VIRT_PWM_1; + s = pmbus_find_sensor(data, page, reg + id); + if (IS_ERR(s)) + return PTR_ERR(s); + + return s->data; + } + + config = pmbus_read_byte_data(client, page, + pmbus_fan_config_registers[id]); + if (config < 0) + return config; + + have_rpm = !!(config & pmbus_fan_rpm_mask[id]); + if (want_rpm == have_rpm) + return pmbus_read_word_data(client, page, 0xff, + pmbus_fan_command_registers[id]); + + /* Can't sensibly map between RPM and PWM, just return zero */ + return 0; +} + +int pmbus_get_fan_rate_device(struct i2c_client *client, int page, int id, + enum pmbus_fan_mode mode) +{ + return pmbus_get_fan_rate(client, page, id, mode, false); +} +//EXPORT_SYMBOL_GPL(pmbus_get_fan_rate_device); + +int pmbus_get_fan_rate_cached(struct i2c_client *client, int page, int id, + enum pmbus_fan_mode mode) +{ + return pmbus_get_fan_rate(client, page, id, mode, true); +} +//EXPORT_SYMBOL_GPL(pmbus_get_fan_rate_cached); + +static void pmbus_clear_fault_page(struct i2c_client *client, int page) +{ + _pmbus_write_byte(client, page, PMBUS_CLEAR_FAULTS); +} + +void pmbus_clear_faults(struct i2c_client *client) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + int i; + + for (i = 0; i < data->info->pages; i++) + pmbus_clear_fault_page(client, i); +} +//EXPORT_SYMBOL_GPL(pmbus_clear_faults); + +static int pmbus_check_status_cml(struct i2c_client *client) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + int status, status2; + + status = data->read_status(client, -1); + if (status < 0 || (status & PB_STATUS_CML)) { + status2 = _pmbus_read_byte_data(client, -1, PMBUS_STATUS_CML); + if (status2 < 0 || (status2 & PB_CML_FAULT_INVALID_COMMAND)) + return -EIO; + } + return 0; +} + +static bool pmbus_check_register(struct i2c_client *client, + int (*func)(struct i2c_client *client, + int page, int reg), + int page, int reg) +{ + int rv; + struct pmbus_data *data = i2c_get_clientdata(client); + + rv = func(client, page, reg); + if (rv >= 0 && !(data->flags & PMBUS_SKIP_STATUS_CHECK)) + rv = pmbus_check_status_cml(client); + pmbus_clear_fault_page(client, -1); + return rv >= 0; +} + +static bool pmbus_check_status_register(struct i2c_client *client, int page) +{ + int status; + struct pmbus_data *data = i2c_get_clientdata(client); + + status = data->read_status(client, page); + if (status >= 0 && !(data->flags & PMBUS_SKIP_STATUS_CHECK) && + (status & PB_STATUS_CML)) { + status = _pmbus_read_byte_data(client, -1, PMBUS_STATUS_CML); + if (status < 0 || (status & PB_CML_FAULT_INVALID_COMMAND)) + status = -EIO; + } + + pmbus_clear_fault_page(client, -1); + return status >= 0; +} + +bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg) +{ + return pmbus_check_register(client, _pmbus_read_byte_data, page, reg); +} +//EXPORT_SYMBOL_GPL(pmbus_check_byte_register); + +bool pmbus_check_word_register(struct i2c_client *client, int page, int reg) +{ + return pmbus_check_register(client, __pmbus_read_word_data, page, reg); +} +//EXPORT_SYMBOL_GPL(pmbus_check_word_register); + +const struct pmbus_driver_info *pmbus_get_driver_info(struct i2c_client *client) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + + return data->info; +} +//EXPORT_SYMBOL_GPL(pmbus_get_driver_info); + +static int pmbus_get_status(struct i2c_client *client, int page, int reg) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + int status; + + switch (reg) { + case PMBUS_STATUS_WORD: + status = data->read_status(client, page); + break; + default: + status = _pmbus_read_byte_data(client, page, reg); + break; + } + if (status < 0) + pmbus_clear_faults(client); + return status; +} + +static void pmbus_update_sensor_data(struct i2c_client *client, struct pmbus_sensor *sensor) +{ + if (sensor->data < 0 || sensor->update) + sensor->data = _pmbus_read_word_data(client, sensor->page, + sensor->phase, sensor->reg); +} + +/* + * Convert linear sensor values to milli- or micro-units + * depending on sensor type. + */ +static s64 pmbus_reg2data_linear(struct pmbus_data *data, + struct pmbus_sensor *sensor) +{ + s16 exponent; + s32 mantissa; + s64 val; + + if (sensor->class == PSC_VOLTAGE_OUT) { /* LINEAR16 */ + exponent = data->exponent[sensor->page]; + mantissa = (u16) sensor->data; + } else { /* LINEAR11 */ + exponent = ((s16)sensor->data) >> 11; + mantissa = ((s16)((sensor->data & 0x7ff) << 5)) >> 5; + } + + val = mantissa; + + /* scale result to milli-units for all sensors except fans */ + if (sensor->class != PSC_FAN) + val = val * 1000LL; + + /* scale result to micro-units for power sensors */ + if (sensor->class == PSC_POWER) + val = val * 1000LL; + + if (exponent >= 0) + val <<= exponent; + else + val >>= -exponent; + + return val; +} + +/* + * Convert direct sensor values to milli- or micro-units + * depending on sensor type. + */ +static s64 pmbus_reg2data_direct(struct pmbus_data *data, + struct pmbus_sensor *sensor) +{ + s64 b, val = (s16)sensor->data; + s32 m, R; + + m = data->info->m[sensor->class]; + b = data->info->b[sensor->class]; + R = data->info->R[sensor->class]; + + if (m == 0) + return 0; + + /* X = 1/m * (Y * 10^-R - b) */ + R = -R; + /* scale result to milli-units for everything but fans */ + if (!(sensor->class == PSC_FAN || sensor->class == PSC_PWM)) { + R += 3; + b *= 1000; + } + + /* scale result to micro-units for power sensors */ + if (sensor->class == PSC_POWER) { + R += 3; + b *= 1000; + } + + while (R > 0) { + val *= 10; + R--; + } + while (R < 0) { + val = div_s64(val + 5LL, 10L); /* round closest */ + R++; + } + + val = div_s64(val - b, m); + return val; +} + +/* + * Convert VID sensor values to milli- or micro-units + * depending on sensor type. + */ +static s64 pmbus_reg2data_vid(struct pmbus_data *data, + struct pmbus_sensor *sensor) +{ + long val = sensor->data; + long rv = 0; + + switch (data->info->vrm_version[sensor->page]) { + case vr11: + if (val >= 0x02 && val <= 0xb2) + rv = DIV_ROUND_CLOSEST(160000 - (val - 2) * 625, 100); + break; + case vr12: + if (val >= 0x01) + rv = 250 + (val - 1) * 5; + break; + case vr13: + if (val >= 0x01) + rv = 500 + (val - 1) * 10; + break; + case imvp9: + if (val >= 0x01) + rv = 200 + (val - 1) * 10; + break; + case amd625mv: + if (val >= 0x0 && val <= 0xd8) + rv = DIV_ROUND_CLOSEST(155000 - val * 625, 100); + break; + } + return rv; +} + +static s64 pmbus_reg2data(struct pmbus_data *data, struct pmbus_sensor *sensor) +{ + s64 val; + + if (!sensor->convert) + return sensor->data; + + switch (data->info->format[sensor->class]) { + case direct: + val = pmbus_reg2data_direct(data, sensor); + break; + case vid: + val = pmbus_reg2data_vid(data, sensor); + break; + case linear: + default: + val = pmbus_reg2data_linear(data, sensor); + break; + } + return val; +} + +#define MAX_MANTISSA (1023 * 1000) +#define MIN_MANTISSA (511 * 1000) + +static u16 pmbus_data2reg_linear(struct pmbus_data *data, + struct pmbus_sensor *sensor, s64 val) +{ + s16 exponent = 0, mantissa; + bool negative = false; + + /* simple case */ + if (val == 0) + return 0; + + if (sensor->class == PSC_VOLTAGE_OUT) { + /* LINEAR16 does not support negative voltages */ + if (val < 0) + return 0; + + /* + * For a static exponents, we don't have a choice + * but to adjust the value to it. + */ + if (data->exponent[sensor->page] < 0) + val <<= -data->exponent[sensor->page]; + else + val >>= data->exponent[sensor->page]; + val = DIV_ROUND_CLOSEST_ULL(val, 1000); + return clamp_val(val, 0, 0xffff); + } + + if (val < 0) { + negative = true; + val = -val; + } + + /* Power is in uW. Convert to mW before converting. */ + if (sensor->class == PSC_POWER) + val = DIV_ROUND_CLOSEST_ULL(val, 1000); + + /* + * For simplicity, convert fan data to milli-units + * before calculating the exponent. + */ + if (sensor->class == PSC_FAN) + val = val * 1000LL; + + /* Reduce large mantissa until it fits into 10 bit */ + while (val >= MAX_MANTISSA && exponent < 15) { + exponent++; + val >>= 1; + } + /* Increase small mantissa to improve precision */ + while (val < MIN_MANTISSA && exponent > -15) { + exponent--; + val <<= 1; + } + + /* Convert mantissa from milli-units to units */ + mantissa = clamp_val(DIV_ROUND_CLOSEST_ULL(val, 1000), 0, 0x3ff); + + /* restore sign */ + if (negative) + mantissa = -mantissa; + + /* Convert to 5 bit exponent, 11 bit mantissa */ + return (mantissa & 0x7ff) | ((exponent << 11) & 0xf800); +} + +static u16 pmbus_data2reg_direct(struct pmbus_data *data, + struct pmbus_sensor *sensor, s64 val) +{ + s64 b; + s32 m, R; + + m = data->info->m[sensor->class]; + b = data->info->b[sensor->class]; + R = data->info->R[sensor->class]; + + /* Power is in uW. Adjust R and b. */ + if (sensor->class == PSC_POWER) { + R -= 3; + b *= 1000; + } + + /* Calculate Y = (m * X + b) * 10^R */ + if (!(sensor->class == PSC_FAN || sensor->class == PSC_PWM)) { + R -= 3; /* Adjust R and b for data in milli-units */ + b *= 1000; + } + val = val * m + b; + + while (R > 0) { + val *= 10; + R--; + } + while (R < 0) { + val = div_s64(val + 5LL, 10L); /* round closest */ + R++; + } + + return (u16)clamp_val(val, S16_MIN, S16_MAX); +} + +static u16 pmbus_data2reg_vid(struct pmbus_data *data, + struct pmbus_sensor *sensor, s64 val) +{ + val = clamp_val(val, 500, 1600); + + return 2 + DIV_ROUND_CLOSEST_ULL((1600LL - val) * 100LL, 625); +} + +static u16 pmbus_data2reg(struct pmbus_data *data, + struct pmbus_sensor *sensor, s64 val) +{ + u16 regval; + + if (!sensor->convert) + return val; + + switch (data->info->format[sensor->class]) { + case direct: + regval = pmbus_data2reg_direct(data, sensor, val); + break; + case vid: + regval = pmbus_data2reg_vid(data, sensor, val); + break; + case linear: + default: + regval = pmbus_data2reg_linear(data, sensor, val); + break; + } + return regval; +} + +/* + * Return boolean calculated from converted data. + * defines a status register index and mask. + * The mask is in the lower 8 bits, the register index is in bits 8..23. + * + * The associated pmbus_boolean structure contains optional pointers to two + * sensor attributes. If specified, those attributes are compared against each + * other to determine if a limit has been exceeded. + * + * If the sensor attribute pointers are NULL, the function returns true if + * (status[reg] & mask) is true. + * + * If sensor attribute pointers are provided, a comparison against a specified + * limit has to be performed to determine the boolean result. + * In this case, the function returns true if v1 >= v2 (where v1 and v2 are + * sensor values referenced by sensor attribute pointers s1 and s2). + * + * To determine if an object exceeds upper limits, specify = . + * To determine if an object exceeds lower limits, specify = . + * + * If a negative value is stored in any of the referenced registers, this value + * reflects an error code which will be returned. + */ +static int pmbus_get_boolean(struct i2c_client *client, struct pmbus_boolean *b, + int index) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + struct pmbus_sensor *s1 = b->s1; + struct pmbus_sensor *s2 = b->s2; + u16 mask = pb_index_to_mask(index); + u8 page = pb_index_to_page(index); + u16 reg = pb_index_to_reg(index); + int ret, status; + u16 regval; + + mutex_lock(&data->update_lock); + status = pmbus_get_status(client, page, reg); + if (status < 0) { + ret = status; + goto unlock; + } + + if (s1) + pmbus_update_sensor_data(client, s1); + if (s2) + pmbus_update_sensor_data(client, s2); + + regval = status & mask; + if (regval) { + ret = pmbus_write_byte_data(client, page, reg, regval); + if (ret) + goto unlock; + } + if (s1 && s2) { + s64 v1, v2; + + if (s1->data < 0) { + ret = s1->data; + goto unlock; + } + if (s2->data < 0) { + ret = s2->data; + goto unlock; + } + + v1 = pmbus_reg2data(data, s1); + v2 = pmbus_reg2data(data, s2); + ret = !!(regval && v1 >= v2); + } else { + ret = !!regval; + } +unlock: + mutex_unlock(&data->update_lock); + return ret; +} + +static ssize_t pmbus_show_boolean(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct pmbus_boolean *boolean = to_pmbus_boolean(attr); + struct i2c_client *client = to_i2c_client(dev->parent); + int val; + + val = pmbus_get_boolean(client, boolean, attr->index); + if (val < 0) + return val; + return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t pmbus_show_sensor(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev->parent); + struct pmbus_sensor *sensor = to_pmbus_sensor(devattr); + struct pmbus_data *data = i2c_get_clientdata(client); + ssize_t ret; + + mutex_lock(&data->update_lock); + pmbus_update_sensor_data(client, sensor); + if (sensor->data < 0) + ret = sensor->data; + else + ret = snprintf(buf, PAGE_SIZE, "%lld\n", pmbus_reg2data(data, sensor)); + mutex_unlock(&data->update_lock); + return ret; +} + +int pmbus_core_read_attrs(unsigned int adapter_nr, unsigned short i2c_addr, char *attr_name, long *val) +{ + struct pmbus_data *s3ip_data = NULL; + struct pmbus_sensor *s3ip_sensor; + struct i2c_adapter *s3ip_adapter; + struct i2c_client *s3ip_client; + int i; + + for(i=0; iparent); + s3ip_adapter = to_i2c_adapter(s3ip_client->dev.parent); + + if((s3ip_adapter->nr == adapter_nr) && (s3ip_client->addr == i2c_addr)) + { + s3ip_data = i2c_get_clientdata(s3ip_client); + break; + } + } + } + + if(!s3ip_data) + { + return -1; + } + + for (s3ip_sensor = s3ip_data ->sensors; s3ip_sensor; s3ip_sensor = s3ip_sensor->next) + { + if(!strcmp(s3ip_sensor->attribute.attr.name, attr_name)) + break; + } + + if(s3ip_sensor->data < 0) + return s3ip_sensor->data; + + *val = pmbus_reg2data(s3ip_data, s3ip_sensor); + + return 0; +} +EXPORT_SYMBOL_GPL(pmbus_core_read_attrs); + +int pmbus_core_read_attrs_by_reg(unsigned int adapter_nr, unsigned short i2c_addr, int page, int reg, long *val) +{ + struct pmbus_data *s3ip_data = NULL; + struct pmbus_sensor *s3ip_sensor; + struct i2c_adapter *s3ip_adapter; + struct i2c_client *s3ip_client; + int i; + + for(i=0; iparent); + s3ip_adapter = to_i2c_adapter(s3ip_client->dev.parent); + + if((s3ip_adapter->nr == adapter_nr) && (s3ip_client->addr == i2c_addr)) + { + s3ip_data = i2c_get_clientdata(s3ip_client); + break; + } + } + } + + if(!s3ip_data) + { + return -1; + } + + for (s3ip_sensor = s3ip_data ->sensors; s3ip_sensor; s3ip_sensor = s3ip_sensor->next) + { + if((s3ip_sensor->reg == reg) && (s3ip_sensor->page == page)) + break; + } + + if(s3ip_sensor == NULL) + { + return -1; + } + + if(s3ip_sensor->data < 0) + return s3ip_sensor->data; + + *val = pmbus_reg2data(s3ip_data, s3ip_sensor); + + return 0; +} +EXPORT_SYMBOL_GPL(pmbus_core_read_attrs_by_reg); + +int pmbus_core_read_mfr_id(unsigned int adapter_nr, unsigned short i2c_addr, int *val) +{ + struct pmbus_data *s3ip_data = NULL; + struct i2c_adapter *s3ip_adapter; + struct i2c_client *s3ip_client; + int i; + + for(i=0; iparent); + s3ip_adapter = to_i2c_adapter(s3ip_client->dev.parent); + + if((s3ip_adapter->nr == adapter_nr) && (s3ip_client->addr == i2c_addr)) + { + s3ip_data = i2c_get_clientdata(s3ip_client); + break; + } + } + } + + if(!s3ip_data) + { + return -1; + } + + *val = _pmbus_read_word_data(s3ip_client, 0, 0xff, PMBUS_MFR_ID); + + return 0; +} +EXPORT_SYMBOL_GPL(pmbus_core_read_mfr_id); + +int pmbus_core_read_reg(unsigned int adapter_nr, unsigned short i2c_addr, int page, int reg, PMBUS_PROTOCAL_TYPE_E type, void* val) +{ + struct pmbus_data *s3ip_data = NULL; + struct i2c_adapter *s3ip_adapter; + struct i2c_client *s3ip_client; + int i, ret; + + for(i=0; iparent); + s3ip_adapter = to_i2c_adapter(s3ip_client->dev.parent); + + if((s3ip_adapter->nr == adapter_nr) && (s3ip_client->addr == i2c_addr)) + { + s3ip_data = i2c_get_clientdata(s3ip_client); + break; + } + } + } + + if(!s3ip_data) + { + return -1; + } + + switch(type) + { + case PMBUS_PROTOCAL_TYPE_WORD: + *(int*)val = pmbus_read_word_data(s3ip_client, page, 0xff, reg); + break; + case PMBUS_PROTOCAL_TYPE_BYTE: + *(int*)val = pmbus_read_byte_data(s3ip_client, page, reg); + break; + case PMBUS_PROTOCAL_TYPE_BLOCK: + ret = pmbus_read_block_data(s3ip_client, page, 0xff, reg, (unsigned char*)val); + if(ret < 0) + { + return -1; + } + else + { + return ret; + } + break; + default: + return -1; + } + + return 0; +} +EXPORT_SYMBOL_GPL(pmbus_core_read_reg); + +int pmbus_core_clear_fault(unsigned int adapter_nr, unsigned short i2c_addr) +{ + struct pmbus_data *s3ip_data = NULL; + struct i2c_adapter *s3ip_adapter; + struct i2c_client *s3ip_client; + int i; + + for(i=0; iparent); + s3ip_adapter = to_i2c_adapter(s3ip_client->dev.parent); + + if((s3ip_adapter->nr == adapter_nr) && (s3ip_client->addr == i2c_addr)) + { + s3ip_data = i2c_get_clientdata(s3ip_client); + break; + } + } + } + + if(!s3ip_data) + { + return -1; + } + + pmbus_clear_faults(s3ip_client); + + return 0; +} +EXPORT_SYMBOL_GPL(pmbus_core_clear_fault); + +static ssize_t pmbus_set_sensor(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev->parent); + struct pmbus_data *data = i2c_get_clientdata(client); + struct pmbus_sensor *sensor = to_pmbus_sensor(devattr); + ssize_t rv = count; + s64 val; + int ret; + u16 regval; + + if (kstrtos64(buf, 10, &val) < 0) + return -EINVAL; + + mutex_lock(&data->update_lock); + regval = pmbus_data2reg(data, sensor, val); + ret = _pmbus_write_word_data(client, sensor->page, sensor->reg, regval); + if (ret < 0) + rv = ret; + else + sensor->data = regval; + mutex_unlock(&data->update_lock); + return rv; +} + +static ssize_t pmbus_show_label(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct pmbus_label *label = to_pmbus_label(da); + + return snprintf(buf, PAGE_SIZE, "%s\n", label->label); +} + +static int pmbus_add_attribute(struct pmbus_data *data, struct attribute *attr) +{ + if (data->num_attributes >= data->max_attributes - 1) { + int new_max_attrs = data->max_attributes + PMBUS_ATTR_ALLOC_SIZE; +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0) + void *new_attrs = krealloc(data->group.attrs, + new_max_attrs * sizeof(void *), + GFP_KERNEL); +#else + void *new_attrs = devm_krealloc(data->dev, data->group.attrs, + new_max_attrs * sizeof(void *), + GFP_KERNEL); +#endif + + if (!new_attrs) + return -ENOMEM; + data->group.attrs = new_attrs; + data->max_attributes = new_max_attrs; + } + + data->group.attrs[data->num_attributes++] = attr; + data->group.attrs[data->num_attributes] = NULL; + return 0; +} + +static void pmbus_dev_attr_init(struct device_attribute *dev_attr, + const char *name, + umode_t mode, + ssize_t (*show)(struct device *dev, + struct device_attribute *attr, + char *buf), + ssize_t (*store)(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count)) +{ + sysfs_attr_init(&dev_attr->attr); + dev_attr->attr.name = name; + dev_attr->attr.mode = mode; + dev_attr->show = show; + dev_attr->store = store; +} + +static void pmbus_attr_init(struct sensor_device_attribute *a, + const char *name, + umode_t mode, + ssize_t (*show)(struct device *dev, + struct device_attribute *attr, + char *buf), + ssize_t (*store)(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count), + int idx) +{ + pmbus_dev_attr_init(&a->dev_attr, name, mode, show, store); + a->index = idx; +} + +static int pmbus_add_boolean(struct pmbus_data *data, + const char *name, const char *type, int seq, + struct pmbus_sensor *s1, + struct pmbus_sensor *s2, + u8 page, u16 reg, u16 mask) +{ + struct pmbus_boolean *boolean; + struct sensor_device_attribute *a; + + if (WARN((s1 && !s2) || (!s1 && s2), "Bad s1/s2 parameters\n")) + return -EINVAL; + + boolean = devm_kzalloc(data->dev, sizeof(*boolean), GFP_KERNEL); + if (!boolean) + return -ENOMEM; + + a = &boolean->attribute; + + snprintf(boolean->name, sizeof(boolean->name), "%s%d_%s", + name, seq, type); + boolean->s1 = s1; + boolean->s2 = s2; + pmbus_attr_init(a, boolean->name, 0444, pmbus_show_boolean, NULL, + pb_reg_to_index(page, reg, mask)); + + return pmbus_add_attribute(data, &a->dev_attr.attr); +} + +static struct pmbus_sensor *pmbus_add_sensor(struct pmbus_data *data, + const char *name, const char *type, + int seq, int page, int phase, + int reg, + enum pmbus_sensor_classes class, + bool update, bool readonly, + bool convert) +{ + struct pmbus_sensor *sensor; + struct device_attribute *a; + + sensor = devm_kzalloc(data->dev, sizeof(*sensor), GFP_KERNEL); + if (!sensor) + return NULL; + a = &sensor->attribute; + + if (type) + snprintf(sensor->name, sizeof(sensor->name), "%s%d_%s", + name, seq, type); + else + snprintf(sensor->name, sizeof(sensor->name), "%s%d", + name, seq); +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 6, 0) + if (data->flags & PMBUS_WRITE_PROTECTED) + readonly = true; +#endif + sensor->page = page; + sensor->phase = phase; + sensor->reg = reg; + sensor->class = class; + sensor->update = update; + sensor->convert = convert; + sensor->data = -ENODATA; + pmbus_dev_attr_init(a, sensor->name, + readonly ? 0444 : 0644, + pmbus_show_sensor, pmbus_set_sensor); + + if (pmbus_add_attribute(data, &a->attr)) + return NULL; + + sensor->next = data->sensors; + data->sensors = sensor; + + return sensor; +} + +static int pmbus_add_label(struct pmbus_data *data, + const char *name, int seq, + const char *lstring, int index, int phase) +{ + struct pmbus_label *label; + struct device_attribute *a; + + label = devm_kzalloc(data->dev, sizeof(*label), GFP_KERNEL); + if (!label) + return -ENOMEM; + + a = &label->attribute; + + snprintf(label->name, sizeof(label->name), "%s%d_label", name, seq); + if (!index) { + if (phase == 0xff) + strncpy(label->label, lstring, + sizeof(label->label) - 1); + else + snprintf(label->label, sizeof(label->label), "%s.%d", + lstring, phase); + } else { + if (phase == 0xff) + snprintf(label->label, sizeof(label->label), "%s%d", + lstring, index); + else + snprintf(label->label, sizeof(label->label), "%s%d.%d", + lstring, index, phase); + } + + pmbus_dev_attr_init(a, label->name, 0444, pmbus_show_label, NULL); + return pmbus_add_attribute(data, &a->attr); +} + +/* + * Search for attributes. Allocate sensors, booleans, and labels as needed. + */ + +/* + * The pmbus_limit_attr structure describes a single limit attribute + * and its associated alarm attribute. + */ +struct pmbus_limit_attr { + u16 reg; /* Limit register */ + u16 sbit; /* Alarm attribute status bit */ + bool update; /* True if register needs updates */ + bool low; /* True if low limit; for limits with compare + functions only */ + const char *attr; /* Attribute name */ + const char *alarm; /* Alarm attribute name */ +}; + +/* + * The pmbus_sensor_attr structure describes one sensor attribute. This + * description includes a reference to the associated limit attributes. + */ +struct pmbus_sensor_attr { + u16 reg; /* sensor register */ + u16 gbit; /* generic status bit */ + u8 nlimit; /* # of limit registers */ + enum pmbus_sensor_classes class;/* sensor class */ + const char *label; /* sensor label */ + bool paged; /* true if paged sensor */ + bool update; /* true if update needed */ + bool compare; /* true if compare function needed */ + u32 func; /* sensor mask */ + u32 sfunc; /* sensor status mask */ + int sreg; /* status register */ + const struct pmbus_limit_attr *limit;/* limit registers */ +}; + +/* + * Add a set of limit attributes and, if supported, the associated + * alarm attributes. + * returns 0 if no alarm register found, 1 if an alarm register was found, + * < 0 on errors. + */ +static int pmbus_add_limit_attrs(struct i2c_client *client, + struct pmbus_data *data, + const struct pmbus_driver_info *info, + const char *name, int index, int page, + struct pmbus_sensor *base, + const struct pmbus_sensor_attr *attr) +{ + const struct pmbus_limit_attr *l = attr->limit; + int nlimit = attr->nlimit; + int have_alarm = 0; + int i, ret; + struct pmbus_sensor *curr; + + for (i = 0; i < nlimit; i++) { + if (pmbus_check_word_register(client, page, l->reg)) { + curr = pmbus_add_sensor(data, name, l->attr, index, + page, 0xff, l->reg, attr->class, + attr->update || l->update, + false, true); + if (!curr) + return -ENOMEM; + if (l->sbit && (info->func[page] & attr->sfunc)) { + ret = pmbus_add_boolean(data, name, + l->alarm, index, + attr->compare ? l->low ? curr : base + : NULL, + attr->compare ? l->low ? base : curr + : NULL, + page, attr->sreg, l->sbit); + if (ret) + return ret; + have_alarm = 1; + } + } + l++; + } + return have_alarm; +} + +static int pmbus_add_sensor_attrs_one(struct i2c_client *client, + struct pmbus_data *data, + const struct pmbus_driver_info *info, + const char *name, + int index, int page, int phase, + const struct pmbus_sensor_attr *attr, + bool paged) +{ + struct pmbus_sensor *base; + bool upper = !!(attr->gbit & 0xff00); /* need to check STATUS_WORD */ + int ret; + + if (attr->label) { + ret = pmbus_add_label(data, name, index, attr->label, + paged ? page + 1 : 0, phase); + if (ret) + return ret; + } + base = pmbus_add_sensor(data, name, "input", index, page, phase, + attr->reg, attr->class, true, true, true); + if (!base) + return -ENOMEM; + /* No limit and alarm attributes for phase specific sensors */ + if (attr->sfunc && phase == 0xff) { + ret = pmbus_add_limit_attrs(client, data, info, name, + index, page, base, attr); + if (ret < 0) + return ret; + /* + * Add generic alarm attribute only if there are no individual + * alarm attributes, if there is a global alarm bit, and if + * the generic status register (word or byte, depending on + * which global bit is set) for this page is accessible. + */ + if (!ret && attr->gbit && + (!upper || (upper && data->has_status_word)) && + pmbus_check_status_register(client, page)) { + ret = pmbus_add_boolean(data, name, "alarm", index, + NULL, NULL, + page, PMBUS_STATUS_WORD, + attr->gbit); + if (ret) + return ret; + } + } + return 0; +} + +static bool pmbus_sensor_is_paged(const struct pmbus_driver_info *info, + const struct pmbus_sensor_attr *attr) +{ + int p; + + if (attr->paged) + return true; + + /* + * Some attributes may be present on more than one page despite + * not being marked with the paged attribute. If that is the case, + * then treat the sensor as being paged and add the page suffix to the + * attribute name. + * We don't just add the paged attribute to all such attributes, in + * order to maintain the un-suffixed labels in the case where the + * attribute is only on page 0. + */ + for (p = 1; p < info->pages; p++) { + if (info->func[p] & attr->func) + return true; + } + return false; +} + +static int pmbus_add_sensor_attrs(struct i2c_client *client, + struct pmbus_data *data, + const char *name, + const struct pmbus_sensor_attr *attrs, + int nattrs) +{ + const struct pmbus_driver_info *info = data->info; + int index, i; + int ret; + + index = 1; + for (i = 0; i < nattrs; i++) { + int page, pages; + bool paged = pmbus_sensor_is_paged(info, attrs); + + pages = paged ? info->pages : 1; + for (page = 0; page < pages; page++) { + if (!(info->func[page] & attrs->func)) + continue; + ret = pmbus_add_sensor_attrs_one(client, data, info, + name, index, page, + 0xff, attrs, paged); + if (ret) + return ret; + index++; + if (info->phases[page]) { + int phase; + + for (phase = 0; phase < info->phases[page]; + phase++) { + if (!(info->pfunc[phase] & attrs->func)) + continue; + ret = pmbus_add_sensor_attrs_one(client, + data, info, name, index, page, + phase, attrs, paged); + if (ret) + return ret; + index++; + } + } + } + attrs++; + } + return 0; +} + +static const struct pmbus_limit_attr vin_limit_attrs[] = { + { + .reg = PMBUS_VIN_UV_WARN_LIMIT, + .attr = "min", + .alarm = "min_alarm", + .sbit = PB_VOLTAGE_UV_WARNING, + }, { + .reg = PMBUS_VIN_UV_FAULT_LIMIT, + .attr = "lcrit", + .alarm = "lcrit_alarm", + .sbit = PB_VOLTAGE_UV_FAULT | PB_VOLTAGE_VIN_OFF, + }, { + .reg = PMBUS_VIN_OV_WARN_LIMIT, + .attr = "max", + .alarm = "max_alarm", + .sbit = PB_VOLTAGE_OV_WARNING, + }, { + .reg = PMBUS_VIN_OV_FAULT_LIMIT, + .attr = "crit", + .alarm = "crit_alarm", + .sbit = PB_VOLTAGE_OV_FAULT, + }, { + .reg = PMBUS_VIRT_READ_VIN_AVG, + .update = true, + .attr = "average", + }, { + .reg = PMBUS_VIRT_READ_VIN_MIN, + .update = true, + .attr = "lowest", + }, { + .reg = PMBUS_VIRT_READ_VIN_MAX, + .update = true, + .attr = "highest", + }, { + .reg = PMBUS_VIRT_RESET_VIN_HISTORY, + .attr = "reset_history", + }, { + .reg = PMBUS_MFR_VIN_MIN, + .attr = "rated_min", + }, { + .reg = PMBUS_MFR_VIN_MAX, + .attr = "rated_max", + }, +}; + +static const struct pmbus_limit_attr vmon_limit_attrs[] = { + { + .reg = PMBUS_VIRT_VMON_UV_WARN_LIMIT, + .attr = "min", + .alarm = "min_alarm", + .sbit = PB_VOLTAGE_UV_WARNING, + }, { + .reg = PMBUS_VIRT_VMON_UV_FAULT_LIMIT, + .attr = "lcrit", + .alarm = "lcrit_alarm", + .sbit = PB_VOLTAGE_UV_FAULT, + }, { + .reg = PMBUS_VIRT_VMON_OV_WARN_LIMIT, + .attr = "max", + .alarm = "max_alarm", + .sbit = PB_VOLTAGE_OV_WARNING, + }, { + .reg = PMBUS_VIRT_VMON_OV_FAULT_LIMIT, + .attr = "crit", + .alarm = "crit_alarm", + .sbit = PB_VOLTAGE_OV_FAULT, + } +}; + +static const struct pmbus_limit_attr vout_limit_attrs[] = { + { + .reg = PMBUS_VOUT_UV_WARN_LIMIT, + .attr = "min", + .alarm = "min_alarm", + .sbit = PB_VOLTAGE_UV_WARNING, + }, { + .reg = PMBUS_VOUT_UV_FAULT_LIMIT, + .attr = "lcrit", + .alarm = "lcrit_alarm", + .sbit = PB_VOLTAGE_UV_FAULT, + }, { + .reg = PMBUS_VOUT_OV_WARN_LIMIT, + .attr = "max", + .alarm = "max_alarm", + .sbit = PB_VOLTAGE_OV_WARNING, + }, { + .reg = PMBUS_VOUT_OV_FAULT_LIMIT, + .attr = "crit", + .alarm = "crit_alarm", + .sbit = PB_VOLTAGE_OV_FAULT, + }, { + .reg = PMBUS_VIRT_READ_VOUT_AVG, + .update = true, + .attr = "average", + }, { + .reg = PMBUS_VIRT_READ_VOUT_MIN, + .update = true, + .attr = "lowest", + }, { + .reg = PMBUS_VIRT_READ_VOUT_MAX, + .update = true, + .attr = "highest", + }, { + .reg = PMBUS_VIRT_RESET_VOUT_HISTORY, + .attr = "reset_history", + }, { + .reg = PMBUS_MFR_VOUT_MIN, + .attr = "rated_min", + }, { + .reg = PMBUS_MFR_VOUT_MAX, + .attr = "rated_max", + }, +}; + +static const struct pmbus_sensor_attr voltage_attributes[] = { + { + .reg = PMBUS_READ_VIN, + .class = PSC_VOLTAGE_IN, + .label = "vin", + .func = PMBUS_HAVE_VIN, + .sfunc = PMBUS_HAVE_STATUS_INPUT, + .sreg = PMBUS_STATUS_INPUT, + .gbit = PB_STATUS_VIN_UV, + .limit = vin_limit_attrs, + .nlimit = ARRAY_SIZE(vin_limit_attrs), + }, { + .reg = PMBUS_VIRT_READ_VMON, + .class = PSC_VOLTAGE_IN, + .label = "vmon", + .func = PMBUS_HAVE_VMON, + .sfunc = PMBUS_HAVE_STATUS_VMON, + .sreg = PMBUS_VIRT_STATUS_VMON, + .limit = vmon_limit_attrs, + .nlimit = ARRAY_SIZE(vmon_limit_attrs), + }, { + .reg = PMBUS_READ_VCAP, + .class = PSC_VOLTAGE_IN, + .label = "vcap", + .func = PMBUS_HAVE_VCAP, + }, { + .reg = PMBUS_READ_VOUT, + .class = PSC_VOLTAGE_OUT, + .label = "vout", + .paged = true, + .func = PMBUS_HAVE_VOUT, + .sfunc = PMBUS_HAVE_STATUS_VOUT, + .sreg = PMBUS_STATUS_VOUT, + .gbit = PB_STATUS_VOUT_OV, + .limit = vout_limit_attrs, + .nlimit = ARRAY_SIZE(vout_limit_attrs), + } +}; + +/* Current attributes */ + +static const struct pmbus_limit_attr iin_limit_attrs[] = { + { + .reg = PMBUS_IIN_OC_WARN_LIMIT, + .attr = "max", + .alarm = "max_alarm", + .sbit = PB_IIN_OC_WARNING, + }, { + .reg = PMBUS_IIN_OC_FAULT_LIMIT, + .attr = "crit", + .alarm = "crit_alarm", + .sbit = PB_IIN_OC_FAULT, + }, { + .reg = PMBUS_VIRT_READ_IIN_AVG, + .update = true, + .attr = "average", + }, { + .reg = PMBUS_VIRT_READ_IIN_MIN, + .update = true, + .attr = "lowest", + }, { + .reg = PMBUS_VIRT_READ_IIN_MAX, + .update = true, + .attr = "highest", + }, { + .reg = PMBUS_VIRT_RESET_IIN_HISTORY, + .attr = "reset_history", + }, { + .reg = PMBUS_MFR_IIN_MAX, + .attr = "rated_max", + }, +}; + +static const struct pmbus_limit_attr iout_limit_attrs[] = { + { + .reg = PMBUS_IOUT_OC_WARN_LIMIT, + .attr = "max", + .alarm = "max_alarm", + .sbit = PB_IOUT_OC_WARNING, + }, { + .reg = PMBUS_IOUT_UC_FAULT_LIMIT, + .attr = "lcrit", + .alarm = "lcrit_alarm", + .sbit = PB_IOUT_UC_FAULT, + }, { + .reg = PMBUS_IOUT_OC_FAULT_LIMIT, + .attr = "crit", + .alarm = "crit_alarm", + .sbit = PB_IOUT_OC_FAULT, + }, { + .reg = PMBUS_VIRT_READ_IOUT_AVG, + .update = true, + .attr = "average", + }, { + .reg = PMBUS_VIRT_READ_IOUT_MIN, + .update = true, + .attr = "lowest", + }, { + .reg = PMBUS_VIRT_READ_IOUT_MAX, + .update = true, + .attr = "highest", + }, { + .reg = PMBUS_VIRT_RESET_IOUT_HISTORY, + .attr = "reset_history", + }, { + .reg = PMBUS_MFR_IOUT_MAX, + .attr = "rated_max", + }, +}; + +static const struct pmbus_sensor_attr current_attributes[] = { + { + .reg = PMBUS_READ_IIN, + .class = PSC_CURRENT_IN, + .label = "iin", + .func = PMBUS_HAVE_IIN, + .sfunc = PMBUS_HAVE_STATUS_INPUT, + .sreg = PMBUS_STATUS_INPUT, + .gbit = PB_STATUS_INPUT, + .limit = iin_limit_attrs, + .nlimit = ARRAY_SIZE(iin_limit_attrs), + }, { + .reg = PMBUS_READ_IOUT, + .class = PSC_CURRENT_OUT, + .label = "iout", + .paged = true, + .func = PMBUS_HAVE_IOUT, + .sfunc = PMBUS_HAVE_STATUS_IOUT, + .sreg = PMBUS_STATUS_IOUT, + .gbit = PB_STATUS_IOUT_OC, + .limit = iout_limit_attrs, + .nlimit = ARRAY_SIZE(iout_limit_attrs), + } +}; + +/* Power attributes */ + +static const struct pmbus_limit_attr pin_limit_attrs[] = { + { + .reg = PMBUS_PIN_OP_WARN_LIMIT, + .attr = "max", + .alarm = "alarm", + .sbit = PB_PIN_OP_WARNING, + }, { + .reg = PMBUS_VIRT_READ_PIN_AVG, + .update = true, + .attr = "average", + }, { + .reg = PMBUS_VIRT_READ_PIN_MIN, + .update = true, + .attr = "input_lowest", + }, { + .reg = PMBUS_VIRT_READ_PIN_MAX, + .update = true, + .attr = "input_highest", + }, { + .reg = PMBUS_VIRT_RESET_PIN_HISTORY, + .attr = "reset_history", + }, { + .reg = PMBUS_MFR_PIN_MAX, + .attr = "rated_max", + }, +}; + +static const struct pmbus_limit_attr pout_limit_attrs[] = { + { + .reg = PMBUS_POUT_MAX, + .attr = "cap", + .alarm = "cap_alarm", + .sbit = PB_POWER_LIMITING, + }, { + .reg = PMBUS_POUT_OP_WARN_LIMIT, + .attr = "max", + .alarm = "max_alarm", + .sbit = PB_POUT_OP_WARNING, + }, { + .reg = PMBUS_POUT_OP_FAULT_LIMIT, + .attr = "crit", + .alarm = "crit_alarm", + .sbit = PB_POUT_OP_FAULT, + }, { + .reg = PMBUS_VIRT_READ_POUT_AVG, + .update = true, + .attr = "average", + }, { + .reg = PMBUS_VIRT_READ_POUT_MIN, + .update = true, + .attr = "input_lowest", + }, { + .reg = PMBUS_VIRT_READ_POUT_MAX, + .update = true, + .attr = "input_highest", + }, { + .reg = PMBUS_VIRT_RESET_POUT_HISTORY, + .attr = "reset_history", + }, { + .reg = PMBUS_MFR_POUT_MAX, + .attr = "rated_max", + }, +}; + +static const struct pmbus_sensor_attr power_attributes[] = { + { + .reg = PMBUS_READ_PIN, + .class = PSC_POWER, + .label = "pin", + .func = PMBUS_HAVE_PIN, + .sfunc = PMBUS_HAVE_STATUS_INPUT, + .sreg = PMBUS_STATUS_INPUT, + .gbit = PB_STATUS_INPUT, + .limit = pin_limit_attrs, + .nlimit = ARRAY_SIZE(pin_limit_attrs), + }, { + .reg = PMBUS_READ_POUT, + .class = PSC_POWER, + .label = "pout", + .paged = true, + .func = PMBUS_HAVE_POUT, + .sfunc = PMBUS_HAVE_STATUS_IOUT, + .sreg = PMBUS_STATUS_IOUT, + .limit = pout_limit_attrs, + .nlimit = ARRAY_SIZE(pout_limit_attrs), + } +}; + +/* Temperature atributes */ + +static const struct pmbus_limit_attr temp_limit_attrs[] = { + { + .reg = PMBUS_UT_WARN_LIMIT, + .low = true, + .attr = "min", + .alarm = "min_alarm", + .sbit = PB_TEMP_UT_WARNING, + }, { + .reg = PMBUS_UT_FAULT_LIMIT, + .low = true, + .attr = "lcrit", + .alarm = "lcrit_alarm", + .sbit = PB_TEMP_UT_FAULT, + }, { + .reg = PMBUS_OT_WARN_LIMIT, + .attr = "max", + .alarm = "max_alarm", + .sbit = PB_TEMP_OT_WARNING, + }, { + .reg = PMBUS_OT_FAULT_LIMIT, + .attr = "crit", + .alarm = "crit_alarm", + .sbit = PB_TEMP_OT_FAULT, + }, { + .reg = PMBUS_VIRT_READ_TEMP_MIN, + .attr = "lowest", + }, { + .reg = PMBUS_VIRT_READ_TEMP_AVG, + .attr = "average", + }, { + .reg = PMBUS_VIRT_READ_TEMP_MAX, + .attr = "highest", + }, { + .reg = PMBUS_VIRT_RESET_TEMP_HISTORY, + .attr = "reset_history", + }, { + .reg = PMBUS_MFR_MAX_TEMP_1, + .attr = "rated_max", + }, +}; + +static const struct pmbus_limit_attr temp_limit_attrs2[] = { + { + .reg = PMBUS_UT_WARN_LIMIT, + .low = true, + .attr = "min", + .alarm = "min_alarm", + .sbit = PB_TEMP_UT_WARNING, + }, { + .reg = PMBUS_UT_FAULT_LIMIT, + .low = true, + .attr = "lcrit", + .alarm = "lcrit_alarm", + .sbit = PB_TEMP_UT_FAULT, + }, { + .reg = PMBUS_OT_WARN_LIMIT, + .attr = "max", + .alarm = "max_alarm", + .sbit = PB_TEMP_OT_WARNING, + }, { + .reg = PMBUS_OT_FAULT_LIMIT, + .attr = "crit", + .alarm = "crit_alarm", + .sbit = PB_TEMP_OT_FAULT, + }, { + .reg = PMBUS_VIRT_READ_TEMP2_MIN, + .attr = "lowest", + }, { + .reg = PMBUS_VIRT_READ_TEMP2_AVG, + .attr = "average", + }, { + .reg = PMBUS_VIRT_READ_TEMP2_MAX, + .attr = "highest", + }, { + .reg = PMBUS_VIRT_RESET_TEMP2_HISTORY, + .attr = "reset_history", + }, { + .reg = PMBUS_MFR_MAX_TEMP_2, + .attr = "rated_max", + }, +}; + +static const struct pmbus_limit_attr temp_limit_attrs3[] = { + { + .reg = PMBUS_UT_WARN_LIMIT, + .low = true, + .attr = "min", + .alarm = "min_alarm", + .sbit = PB_TEMP_UT_WARNING, + }, { + .reg = PMBUS_UT_FAULT_LIMIT, + .low = true, + .attr = "lcrit", + .alarm = "lcrit_alarm", + .sbit = PB_TEMP_UT_FAULT, + }, { + .reg = PMBUS_OT_WARN_LIMIT, + .attr = "max", + .alarm = "max_alarm", + .sbit = PB_TEMP_OT_WARNING, + }, { + .reg = PMBUS_OT_FAULT_LIMIT, + .attr = "crit", + .alarm = "crit_alarm", + .sbit = PB_TEMP_OT_FAULT, + }, { + .reg = PMBUS_MFR_MAX_TEMP_3, + .attr = "rated_max", + }, +}; + +static const struct pmbus_sensor_attr temp_attributes[] = { + { + .reg = PMBUS_READ_TEMPERATURE_1, + .class = PSC_TEMPERATURE, + .paged = true, + .update = true, + .compare = true, + .func = PMBUS_HAVE_TEMP, + .sfunc = PMBUS_HAVE_STATUS_TEMP, + .sreg = PMBUS_STATUS_TEMPERATURE, + .gbit = PB_STATUS_TEMPERATURE, + .limit = temp_limit_attrs, + .nlimit = ARRAY_SIZE(temp_limit_attrs), + }, { + .reg = PMBUS_READ_TEMPERATURE_2, + .class = PSC_TEMPERATURE, + .paged = true, + .update = true, + .compare = true, + .func = PMBUS_HAVE_TEMP2, + .sfunc = PMBUS_HAVE_STATUS_TEMP, + .sreg = PMBUS_STATUS_TEMPERATURE, + .gbit = PB_STATUS_TEMPERATURE, + .limit = temp_limit_attrs2, + .nlimit = ARRAY_SIZE(temp_limit_attrs2), + }, { + .reg = PMBUS_READ_TEMPERATURE_3, + .class = PSC_TEMPERATURE, + .paged = true, + .update = true, + .compare = true, + .func = PMBUS_HAVE_TEMP3, + .sfunc = PMBUS_HAVE_STATUS_TEMP, + .sreg = PMBUS_STATUS_TEMPERATURE, + .gbit = PB_STATUS_TEMPERATURE, + .limit = temp_limit_attrs3, + .nlimit = ARRAY_SIZE(temp_limit_attrs3), + } +}; + +static const int pmbus_fan_registers[] = { + PMBUS_READ_FAN_SPEED_1, + PMBUS_READ_FAN_SPEED_2, + PMBUS_READ_FAN_SPEED_3, + PMBUS_READ_FAN_SPEED_4 +}; + +static const int pmbus_fan_status_registers[] = { + PMBUS_STATUS_FAN_12, + PMBUS_STATUS_FAN_12, + PMBUS_STATUS_FAN_34, + PMBUS_STATUS_FAN_34 +}; + +static const u32 pmbus_fan_flags[] = { + PMBUS_HAVE_FAN12, + PMBUS_HAVE_FAN12, + PMBUS_HAVE_FAN34, + PMBUS_HAVE_FAN34 +}; + +static const u32 pmbus_fan_status_flags[] = { + PMBUS_HAVE_STATUS_FAN12, + PMBUS_HAVE_STATUS_FAN12, + PMBUS_HAVE_STATUS_FAN34, + PMBUS_HAVE_STATUS_FAN34 +}; + +/* Fans */ + +/* Precondition: FAN_CONFIG_x_y and FAN_COMMAND_x must exist for the fan ID */ +static int pmbus_add_fan_ctrl(struct i2c_client *client, + struct pmbus_data *data, int index, int page, int id, + u8 config) +{ + struct pmbus_sensor *sensor; + + sensor = pmbus_add_sensor(data, "fan", "target", index, page, + 0xff, PMBUS_VIRT_FAN_TARGET_1 + id, PSC_FAN, + false, false, true); + + if (!sensor) + return -ENOMEM; + + if (!((data->info->func[page] & PMBUS_HAVE_PWM12) || + (data->info->func[page] & PMBUS_HAVE_PWM34))) + return 0; + + sensor = pmbus_add_sensor(data, "pwm", NULL, index, page, + 0xff, PMBUS_VIRT_PWM_1 + id, PSC_PWM, + false, false, true); + + if (!sensor) + return -ENOMEM; + + sensor = pmbus_add_sensor(data, "pwm", "enable", index, page, + 0xff, PMBUS_VIRT_PWM_ENABLE_1 + id, PSC_PWM, + true, false, false); + + if (!sensor) + return -ENOMEM; + + return 0; +} + +static int pmbus_add_fan_attributes(struct i2c_client *client, + struct pmbus_data *data) +{ + const struct pmbus_driver_info *info = data->info; + int index = 1; + int page; + int ret; + + for (page = 0; page < info->pages; page++) { + int f; + + for (f = 0; f < ARRAY_SIZE(pmbus_fan_registers); f++) { + int regval; + + if (!(info->func[page] & pmbus_fan_flags[f])) + break; + + if (!pmbus_check_word_register(client, page, + pmbus_fan_registers[f])) + break; + + /* + * Skip fan if not installed. + * Each fan configuration register covers multiple fans, + * so we have to do some magic. + */ + regval = _pmbus_read_byte_data(client, page, + pmbus_fan_config_registers[f]); + if (regval < 0 || + (!(regval & (PB_FAN_1_INSTALLED >> ((f & 1) * 4))))) + continue; + + if (pmbus_add_sensor(data, "fan", "input", index, + page, 0xff, pmbus_fan_registers[f], + PSC_FAN, true, true, true) == NULL) + return -ENOMEM; + + /* Fan control */ + if (pmbus_check_word_register(client, page, + pmbus_fan_command_registers[f])) { + ret = pmbus_add_fan_ctrl(client, data, index, + page, f, regval); + if (ret < 0) + return ret; + } + + /* + * Each fan status register covers multiple fans, + * so we have to do some magic. + */ + if ((info->func[page] & pmbus_fan_status_flags[f]) && + pmbus_check_byte_register(client, + page, pmbus_fan_status_registers[f])) { + int reg; + + if (f > 1) /* fan 3, 4 */ + reg = PMBUS_STATUS_FAN_34; + else + reg = PMBUS_STATUS_FAN_12; + ret = pmbus_add_boolean(data, "fan", + "alarm", index, NULL, NULL, page, reg, + PB_FAN_FAN1_WARNING >> (f & 1)); + if (ret) + return ret; + ret = pmbus_add_boolean(data, "fan", + "fault", index, NULL, NULL, page, reg, + PB_FAN_FAN1_FAULT >> (f & 1)); + if (ret) + return ret; + } + index++; + } + } + return 0; +} + +struct pmbus_samples_attr { + int reg; + char *name; +}; + +struct pmbus_samples_reg { + int page; + struct pmbus_samples_attr *attr; + struct device_attribute dev_attr; +}; + +static struct pmbus_samples_attr pmbus_samples_registers[] = { + { + .reg = PMBUS_VIRT_SAMPLES, + .name = "samples", + }, { + .reg = PMBUS_VIRT_IN_SAMPLES, + .name = "in_samples", + }, { + .reg = PMBUS_VIRT_CURR_SAMPLES, + .name = "curr_samples", + }, { + .reg = PMBUS_VIRT_POWER_SAMPLES, + .name = "power_samples", + }, { + .reg = PMBUS_VIRT_TEMP_SAMPLES, + .name = "temp_samples", + } +}; + +#define to_samples_reg(x) container_of(x, struct pmbus_samples_reg, dev_attr) + +static ssize_t pmbus_show_samples(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int val; + struct i2c_client *client = to_i2c_client(dev->parent); + struct pmbus_samples_reg *reg = to_samples_reg(devattr); + struct pmbus_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + val = _pmbus_read_word_data(client, reg->page, 0xff, reg->attr->reg); + mutex_unlock(&data->update_lock); + if (val < 0) + return val; + + return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t pmbus_set_samples(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + int ret; + long val; + struct i2c_client *client = to_i2c_client(dev->parent); + struct pmbus_samples_reg *reg = to_samples_reg(devattr); + struct pmbus_data *data = i2c_get_clientdata(client); + + if (kstrtol(buf, 0, &val) < 0) + return -EINVAL; + + mutex_lock(&data->update_lock); + ret = _pmbus_write_word_data(client, reg->page, reg->attr->reg, val); + mutex_unlock(&data->update_lock); + + return ret ? : count; +} + +static int pmbus_add_samples_attr(struct pmbus_data *data, int page, + struct pmbus_samples_attr *attr) +{ + struct pmbus_samples_reg *reg; + + reg = devm_kzalloc(data->dev, sizeof(*reg), GFP_KERNEL); + if (!reg) + return -ENOMEM; + + reg->attr = attr; + reg->page = page; + + pmbus_dev_attr_init(®->dev_attr, attr->name, 0644, + pmbus_show_samples, pmbus_set_samples); + + return pmbus_add_attribute(data, ®->dev_attr.attr); +} + +static int pmbus_add_samples_attributes(struct i2c_client *client, + struct pmbus_data *data) +{ + const struct pmbus_driver_info *info = data->info; + int s; + + if (!(info->func[0] & PMBUS_HAVE_SAMPLES)) + return 0; + + for (s = 0; s < ARRAY_SIZE(pmbus_samples_registers); s++) { + struct pmbus_samples_attr *attr; + int ret; + + attr = &pmbus_samples_registers[s]; + if (!pmbus_check_word_register(client, 0, attr->reg)) + continue; + + ret = pmbus_add_samples_attr(data, 0, attr); + if (ret) + return ret; + } + + return 0; +} + +static int pmbus_find_attributes(struct i2c_client *client, + struct pmbus_data *data) +{ + int ret; + + /* Voltage sensors */ + ret = pmbus_add_sensor_attrs(client, data, "in", voltage_attributes, + ARRAY_SIZE(voltage_attributes)); + if (ret) + return ret; + + /* Current sensors */ + ret = pmbus_add_sensor_attrs(client, data, "curr", current_attributes, + ARRAY_SIZE(current_attributes)); + if (ret) + return ret; + + /* Power sensors */ + ret = pmbus_add_sensor_attrs(client, data, "power", power_attributes, + ARRAY_SIZE(power_attributes)); + if (ret) + return ret; + + /* Temperature sensors */ + ret = pmbus_add_sensor_attrs(client, data, "temp", temp_attributes, + ARRAY_SIZE(temp_attributes)); + if (ret) + return ret; + + /* Fans */ + ret = pmbus_add_fan_attributes(client, data); + if (ret) + return ret; + + ret = pmbus_add_samples_attributes(client, data); + return ret; +} + +/* + * Identify chip parameters. + * This function is called for all chips. + */ +static int pmbus_identify_common(struct i2c_client *client, + struct pmbus_data *data, int page) +{ + int vout_mode = -1; + + if (pmbus_check_byte_register(client, page, PMBUS_VOUT_MODE)) + vout_mode = _pmbus_read_byte_data(client, page, + PMBUS_VOUT_MODE); + if (vout_mode >= 0 && vout_mode != 0xff) { + /* + * Not all chips support the VOUT_MODE command, + * so a failure to read it is not an error. + */ + switch (vout_mode >> 5) { + case 0: /* linear mode */ + if (data->info->format[PSC_VOLTAGE_OUT] != linear) + return -ENODEV; + + data->exponent[page] = ((s8)(vout_mode << 3)) >> 3; + break; + case 1: /* VID mode */ + if (data->info->format[PSC_VOLTAGE_OUT] != vid) + return -ENODEV; + break; + case 2: /* direct mode */ + if (data->info->format[PSC_VOLTAGE_OUT] != direct) + return -ENODEV; + break; + default: + return -ENODEV; + } + } + + pmbus_clear_fault_page(client, page); + return 0; +} + +static int pmbus_read_status_byte(struct i2c_client *client, int page) +{ + return _pmbus_read_byte_data(client, page, PMBUS_STATUS_BYTE); +} + +static int pmbus_read_status_word(struct i2c_client *client, int page) +{ + return _pmbus_read_word_data(client, page, 0xff, PMBUS_STATUS_WORD); +} + +static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, + struct pmbus_driver_info *info) +{ + struct device *dev = &client->dev; + int page, ret; + + /* + * Some PMBus chips don't support PMBUS_STATUS_WORD, so try + * to use PMBUS_STATUS_BYTE instead if that is the case. + * Bail out if both registers are not supported. + */ + data->read_status = pmbus_read_status_word; + ret = i2c_smbus_read_word_data(client, PMBUS_STATUS_WORD); + if (ret < 0 || ret == 0xffff) { + data->read_status = pmbus_read_status_byte; + ret = i2c_smbus_read_byte_data(client, PMBUS_STATUS_BYTE); + if (ret < 0 || ret == 0xff) { + dev_err(dev, "PMBus status register not found\n"); + return -ENODEV; + } + } else { + data->has_status_word = true; + } + + /* Enable PEC if the controller and bus support it */ + if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_PEC)) { + ret = i2c_smbus_read_byte_data(client, PMBUS_CAPABILITY); + if (ret >= 0 && (ret & PB_CAPABILITY_ERROR_CHECK)) + client->flags |= I2C_CLIENT_PEC; + } +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 6, 0) + /* + * Check if the chip is write protected. If it is, we can not clear + * faults, and we should not try it. Also, in that case, writes into + * limit registers need to be disabled. + */ + ret = i2c_smbus_read_byte_data(client, PMBUS_WRITE_PROTECT); + if (ret > 0 && (ret & PB_WP_ANY)) + data->flags |= PMBUS_WRITE_PROTECTED | PMBUS_SKIP_STATUS_CHECK; +#endif + if (data->info->pages) + pmbus_clear_faults(client); + else + pmbus_clear_fault_page(client, -1); + + if (info->identify) { + ret = (*info->identify)(client, info); + if (ret < 0) { + dev_err(dev, "Chip identification failed\n"); + return ret; + } + } + + if (info->pages <= 0 || info->pages > PMBUS_PAGES) { + dev_err(dev, "Bad number of PMBus pages: %d\n", info->pages); + return -ENODEV; + } + + for (page = 0; page < info->pages; page++) { + ret = pmbus_identify_common(client, data, page); + if (ret < 0) { + dev_err(dev, "Failed to identify chip capabilities\n"); + return ret; + } + } + return 0; +} + +#if IS_ENABLED(CONFIG_REGULATOR) +static int pmbus_regulator_is_enabled(struct regulator_dev *rdev) +{ + struct device *dev = rdev_get_dev(rdev); + struct i2c_client *client = to_i2c_client(dev->parent); + struct pmbus_data *data = i2c_get_clientdata(client); + u8 page = rdev_get_id(rdev); + int ret; + + mutex_lock(&data->update_lock); + ret = pmbus_read_byte_data(client, page, PMBUS_OPERATION); + mutex_unlock(&data->update_lock); + + if (ret < 0) + return ret; + + return !!(ret & PB_OPERATION_CONTROL_ON); +} + +static int _pmbus_regulator_on_off(struct regulator_dev *rdev, bool enable) +{ + struct device *dev = rdev_get_dev(rdev); + struct i2c_client *client = to_i2c_client(dev->parent); + struct pmbus_data *data = i2c_get_clientdata(client); + u8 page = rdev_get_id(rdev); + int ret; + + mutex_lock(&data->update_lock); + ret = pmbus_update_byte_data(client, page, PMBUS_OPERATION, + PB_OPERATION_CONTROL_ON, + enable ? PB_OPERATION_CONTROL_ON : 0); + mutex_unlock(&data->update_lock); + + return ret; +} + +static int pmbus_regulator_enable(struct regulator_dev *rdev) +{ + return _pmbus_regulator_on_off(rdev, 1); +} + +static int pmbus_regulator_disable(struct regulator_dev *rdev) +{ + return _pmbus_regulator_on_off(rdev, 0); +} + +const struct regulator_ops pmbus_regulator_ops = { + .enable = pmbus_regulator_enable, + .disable = pmbus_regulator_disable, + .is_enabled = pmbus_regulator_is_enabled, +}; +EXPORT_SYMBOL_GPL(pmbus_regulator_ops); + +static int pmbus_regulator_register(struct pmbus_data *data) +{ + struct device *dev = data->dev; + const struct pmbus_driver_info *info = data->info; + const struct pmbus_platform_data *pdata = dev_get_platdata(dev); + struct regulator_dev *rdev; + int i; + + for (i = 0; i < info->num_regulators; i++) { + struct regulator_config config = { }; + + config.dev = dev; + config.driver_data = data; + + if (pdata && pdata->reg_init_data) + config.init_data = &pdata->reg_init_data[i]; + + rdev = devm_regulator_register(dev, &info->reg_desc[i], + &config); + if (IS_ERR(rdev)) { + dev_err(dev, "Failed to register %s regulator\n", + info->reg_desc[i].name); + return PTR_ERR(rdev); + } + } + + return 0; +} +#else +static int pmbus_regulator_register(struct pmbus_data *data) +{ + return 0; +} +#endif + +static struct dentry *pmbus_debugfs_dir; /* pmbus debugfs directory */ + +#if IS_ENABLED(CONFIG_DEBUG_FS) +static int pmbus_debugfs_get(void *data, u64 *val) +{ + int rc; + struct pmbus_debugfs_entry *entry = data; + + rc = _pmbus_read_byte_data(entry->client, entry->page, entry->reg); + if (rc < 0) + return rc; + + *val = rc; + + return 0; +} +DEFINE_DEBUGFS_ATTRIBUTE(pmbus_debugfs_ops, pmbus_debugfs_get, NULL, + "0x%02llx\n"); + +static int pmbus_debugfs_get_status(void *data, u64 *val) +{ + int rc; + struct pmbus_debugfs_entry *entry = data; + struct pmbus_data *pdata = i2c_get_clientdata(entry->client); + + rc = pdata->read_status(entry->client, entry->page); + if (rc < 0) + return rc; + + *val = rc; + + return 0; +} +DEFINE_DEBUGFS_ATTRIBUTE(pmbus_debugfs_ops_status, pmbus_debugfs_get_status, + NULL, "0x%04llx\n"); + +static int pmbus_debugfs_get_pec(void *data, u64 *val) +{ + struct i2c_client *client = data; + + *val = !!(client->flags & I2C_CLIENT_PEC); + + return 0; +} + +static int pmbus_debugfs_set_pec(void *data, u64 val) +{ + int rc; + struct i2c_client *client = data; + + if (!val) { + client->flags &= ~I2C_CLIENT_PEC; + return 0; + } + + if (val != 1) + return -EINVAL; + + rc = i2c_smbus_read_byte_data(client, PMBUS_CAPABILITY); + if (rc < 0) + return rc; + + if (!(rc & PB_CAPABILITY_ERROR_CHECK)) + return -EOPNOTSUPP; + + client->flags |= I2C_CLIENT_PEC; + + return 0; +} +DEFINE_DEBUGFS_ATTRIBUTE(pmbus_debugfs_ops_pec, pmbus_debugfs_get_pec, + pmbus_debugfs_set_pec, "%llu\n"); + +static void pmbus_remove_debugfs(void *data) +{ + struct dentry *entry = data; + + debugfs_remove_recursive(entry); +} + +static int pmbus_init_debugfs(struct i2c_client *client, + struct pmbus_data *data) +{ + int i, idx = 0; + char name[PMBUS_NAME_SIZE]; + struct pmbus_debugfs_entry *entries; + + if (!pmbus_debugfs_dir) + return -ENODEV; + + /* + * Create the debugfs directory for this device. Use the hwmon device + * name to avoid conflicts (hwmon numbers are globally unique). + */ + data->debugfs = debugfs_create_dir(dev_name(data->hwmon_dev), + pmbus_debugfs_dir); + if (IS_ERR_OR_NULL(data->debugfs)) { + data->debugfs = NULL; + return -ENODEV; + } + + /* Allocate the max possible entries we need. */ + entries = devm_kcalloc(data->dev, + data->info->pages * 10, sizeof(*entries), + GFP_KERNEL); + if (!entries) + return -ENOMEM; + + debugfs_create_file("pec", 0664, data->debugfs, client, + &pmbus_debugfs_ops_pec); + + for (i = 0; i < data->info->pages; ++i) { + /* Check accessibility of status register if it's not page 0 */ + if (!i || pmbus_check_status_register(client, i)) { + /* No need to set reg as we have special read op. */ + entries[idx].client = client; + entries[idx].page = i; + scnprintf(name, PMBUS_NAME_SIZE, "status%d", i); + debugfs_create_file(name, 0444, data->debugfs, + &entries[idx++], + &pmbus_debugfs_ops_status); + } + + if (data->info->func[i] & PMBUS_HAVE_STATUS_VOUT) { + entries[idx].client = client; + entries[idx].page = i; + entries[idx].reg = PMBUS_STATUS_VOUT; + scnprintf(name, PMBUS_NAME_SIZE, "status%d_vout", i); + debugfs_create_file(name, 0444, data->debugfs, + &entries[idx++], + &pmbus_debugfs_ops); + } + + if (data->info->func[i] & PMBUS_HAVE_STATUS_IOUT) { + entries[idx].client = client; + entries[idx].page = i; + entries[idx].reg = PMBUS_STATUS_IOUT; + scnprintf(name, PMBUS_NAME_SIZE, "status%d_iout", i); + debugfs_create_file(name, 0444, data->debugfs, + &entries[idx++], + &pmbus_debugfs_ops); + } + + if (data->info->func[i] & PMBUS_HAVE_STATUS_INPUT) { + entries[idx].client = client; + entries[idx].page = i; + entries[idx].reg = PMBUS_STATUS_INPUT; + scnprintf(name, PMBUS_NAME_SIZE, "status%d_input", i); + debugfs_create_file(name, 0444, data->debugfs, + &entries[idx++], + &pmbus_debugfs_ops); + } + + if (data->info->func[i] & PMBUS_HAVE_STATUS_TEMP) { + entries[idx].client = client; + entries[idx].page = i; + entries[idx].reg = PMBUS_STATUS_TEMPERATURE; + scnprintf(name, PMBUS_NAME_SIZE, "status%d_temp", i); + debugfs_create_file(name, 0444, data->debugfs, + &entries[idx++], + &pmbus_debugfs_ops); + } + + if (pmbus_check_byte_register(client, i, PMBUS_STATUS_CML)) { + entries[idx].client = client; + entries[idx].page = i; + entries[idx].reg = PMBUS_STATUS_CML; + scnprintf(name, PMBUS_NAME_SIZE, "status%d_cml", i); + debugfs_create_file(name, 0444, data->debugfs, + &entries[idx++], + &pmbus_debugfs_ops); + } + + if (pmbus_check_byte_register(client, i, PMBUS_STATUS_OTHER)) { + entries[idx].client = client; + entries[idx].page = i; + entries[idx].reg = PMBUS_STATUS_OTHER; + scnprintf(name, PMBUS_NAME_SIZE, "status%d_other", i); + debugfs_create_file(name, 0444, data->debugfs, + &entries[idx++], + &pmbus_debugfs_ops); + } + + if (pmbus_check_byte_register(client, i, + PMBUS_STATUS_MFR_SPECIFIC)) { + entries[idx].client = client; + entries[idx].page = i; + entries[idx].reg = PMBUS_STATUS_MFR_SPECIFIC; + scnprintf(name, PMBUS_NAME_SIZE, "status%d_mfr", i); + debugfs_create_file(name, 0444, data->debugfs, + &entries[idx++], + &pmbus_debugfs_ops); + } + + if (data->info->func[i] & PMBUS_HAVE_STATUS_FAN12) { + entries[idx].client = client; + entries[idx].page = i; + entries[idx].reg = PMBUS_STATUS_FAN_12; + scnprintf(name, PMBUS_NAME_SIZE, "status%d_fan12", i); + debugfs_create_file(name, 0444, data->debugfs, + &entries[idx++], + &pmbus_debugfs_ops); + } + + if (data->info->func[i] & PMBUS_HAVE_STATUS_FAN34) { + entries[idx].client = client; + entries[idx].page = i; + entries[idx].reg = PMBUS_STATUS_FAN_34; + scnprintf(name, PMBUS_NAME_SIZE, "status%d_fan34", i); + debugfs_create_file(name, 0444, data->debugfs, + &entries[idx++], + &pmbus_debugfs_ops); + } + } + + return devm_add_action_or_reset(data->dev, + pmbus_remove_debugfs, data->debugfs); +} +#else +static int pmbus_init_debugfs(struct i2c_client *client, + struct pmbus_data *data) +{ + return 0; +} +#endif /* IS_ENABLED(CONFIG_DEBUG_FS) */ + +int pmbus_do_probe(struct i2c_client *client, struct pmbus_driver_info *info) +{ + struct device *dev = &client->dev; + const struct pmbus_platform_data *pdata = dev_get_platdata(dev); + struct pmbus_data *data; + size_t groups_num = 0; + int ret; + int i; + + if (!info) + return -ENODEV; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE + | I2C_FUNC_SMBUS_BYTE_DATA + | I2C_FUNC_SMBUS_WORD_DATA)) + return -ENODEV; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + if (info->groups) + while (info->groups[groups_num]) + groups_num++; + + data->groups = devm_kcalloc(dev, groups_num + 2, sizeof(void *), + GFP_KERNEL); + if (!data->groups) + return -ENOMEM; + + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + data->dev = dev; + + if (pdata) + data->flags = pdata->flags; + data->info = info; + data->currpage = -1; + data->currphase = -1; + + ret = pmbus_init_common(client, data, info); + if (ret < 0) + return ret; + + ret = pmbus_find_attributes(client, data); + if (ret) + return ret; + + /* + * If there are no attributes, something is wrong. + * Bail out instead of trying to register nothing. + */ + if (!data->num_attributes) { + dev_err(dev, "No attributes found\n"); + return -ENODEV; + } + + data->groups[0] = &data->group; + memcpy(data->groups + 1, info->groups, sizeof(void *) * groups_num); + data->hwmon_dev = devm_hwmon_device_register_with_groups(dev, + client->name, data, data->groups); + if (IS_ERR(data->hwmon_dev)) { + dev_err(dev, "Failed to register hwmon device\n"); + return PTR_ERR(data->hwmon_dev); + } + + ret = pmbus_regulator_register(data); + if (ret) + return ret; + + ret = pmbus_init_debugfs(client, data); + if (ret) + dev_warn(dev, "Failed to register debugfs\n"); + + for(i=0; iname, "mp2973") || !strcmp(client->name, "mp2940b") || !strcmp(client->name, "mp2976")) + { + s3ip_hwmon_dev[i] = data->hwmon_dev; + if(!s3ip_hwmon_dev[i]) + { + mutex_destroy(&data->update_lock); + return -1; + } + } + break; + } + } + return 0; +} +//EXPORT_SYMBOL_GPL(pmbus_do_probe); + +int pmbus_do_remove(struct i2c_client *client) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct i2c_adapter *s3ip_adapter; + struct i2c_client *s3ip_client; + int i; + + debugfs_remove_recursive(data->debugfs); + + hwmon_device_unregister(data->hwmon_dev); + kfree(data->group.attrs); + + for(i=0; iparent); + s3ip_adapter = to_i2c_adapter(s3ip_client->dev.parent); + + if((s3ip_adapter->nr == adapter->nr) && (s3ip_client->addr == client->addr)) + { + s3ip_hwmon_dev[i] = NULL; + break; + } + } + } + + mutex_destroy(&data->update_lock); + + return 0; +} +//EXPORT_SYMBOL_GPL(pmbus_do_remove); + +struct dentry *pmbus_get_debugfs_dir(struct i2c_client *client) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + + return data->debugfs; +} +//EXPORT_SYMBOL_GPL(pmbus_get_debugfs_dir); + +static int __init pmbus_core_init(void) +{ + pmbus_debugfs_dir = debugfs_create_dir("pmbus", NULL); + if (IS_ERR(pmbus_debugfs_dir)) + pmbus_debugfs_dir = NULL; + + return 0; +} + +static void __exit pmbus_core_exit(void) +{ + debugfs_remove_recursive(pmbus_debugfs_dir); +} + +module_init(pmbus_core_init); +module_exit(pmbus_core_exit); + +MODULE_AUTHOR("Guenter Roeck"); +MODULE_DESCRIPTION("PMBus core driver"); +MODULE_LICENSE("GPL"); diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/switch_system_cpld.c b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/switch_system_cpld.c new file mode 100644 index 0000000000..de6413cf96 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/switch_system_cpld.c @@ -0,0 +1,1200 @@ + /* + * A hwmon driver for the SWITCH SYSTEM CPLD + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "switch_cpld_driver.h" +//simon: try to support 4.x kernel +#include + +#if 1 +#define SYS_CPLD_DEBUG(...) +#else +#define SYS_CPLD_DEBUG(...) printk(KERN_DEBUG __VA_ARGS__) +#endif + +#define LPC_DEVICE_ADDRESS1 0xFC800000 +#define LPC_IO_MEMORY_SIZE 1 << 12 + +#define BOARD_VERSION_REG 0x02 +#define BOARD_VERSION_MASK 0x10 + +#define MAJOR_VERSION_REG 0x00 +#define MAJOR_VERSION_MASK 0xFF + +#define MINOR_VERSION_REG 0x01 +#define MINOR_VERSION_MASK 0xFF + +#define I2C_MAX_CHANNEL 18 +#define SYSTEM_CPLD_SLEEP(X) usleep_range((X*8/10), X) +#define I2C_PRESCALE_HIGH_REG 0x80 // PRESCALE registers, PRERhi +#define I2C_PRESCALE_LOW_REG 0x81 // PRESCALE registers, PRERlo +#define I2C_CONTROLLER_REG 0x82 // Control Register, CTR +#define I2C_TRANSMIT_REG 0x83 // Transmit Register, TXR +#define I2C_COMMAND_REG 0x84 // Command Register, CR +#define I2C_RECEIVE_REG 0x86 // Receive Register, RXR +#define I2C_STATUS_REG 0x87 // Status Registesr, SR + + +#define PRER_HI_DEFAULT 0x00 +#define PRER_LO_DEFAULT 0x63 +#define CONTROL_DISABLE 0x00 +#define CONTROL_ENABLE 0x80 +#define COMMAND_START_WRITE 0x90 +#define COMMAND_ENABLE_WRITE 0x10 +#define COMMAND_READ 0x20 +#define COMMAND_ACK_STOP_READ 0x68 +#define COMMAND_STOP 0x40 +#define COMMAND_STOP_WRITE 0x50 +#define BUSY_BIT 0x40 +#define TIP_BIT 0x02 +#define ACK_BIT 0x80 +#define SET_TXR_WRITE(x) (x &= ~(1UL)) +#define SET_TXR_READ(x) (x |= (1UL)) + +#define MAX_BUFF_SIZE 256 + +#define DRV_NAME "switch_system_cpld" +#define SYSTEM_CPLD_SMBUS_NAME "Accton_System_CPLD_SMBus_" + +#define SYSTEM_CPLD_DRIVER_VERSION "0.0.0.1" + + +/* System CPLD SMBus control parameters */ +static unsigned int max_retry_time = 20; +static unsigned int delay_ack = 50; +static unsigned int delay_tip = 26; +static unsigned int delay_busy = 10; + +static struct platform_device *switch_system_cpld_device; + +struct switch_system_cpld_data { + struct device *dev; + void __iomem *base_addr; + + struct mutex update_lock; + struct semaphore sem[I2C_MAX_CHANNEL]; + struct i2c_adapter adapter[I2C_MAX_CHANNEL]; +}; + +#define SYSTEM_CPLD_ACCESS(cmd) \ + do { \ + mutex_lock(&cpld_data->update_lock); \ + cmd \ + mutex_unlock(&cpld_data->update_lock); \ + }while(0); + + +static const unsigned char crc8_table[256] = { + 0x00,0x07,0x0E,0x09,0x1C,0x1B,0x12,0x15,0x38,0x3F,0x36,0x31,0x24,0x23,0x2A,0x2D, + 0x70,0x77,0x7E,0x79,0x6C,0x6B,0x62,0x65,0x48,0x4F,0x46,0x41,0x54,0x53,0x5A,0x5D, + 0xE0,0xE7,0xEE,0xE9,0xFC,0xFB,0xF2,0xF5,0xD8,0xDF,0xD6,0xD1,0xC4,0xC3,0xCA,0xCD, + 0x90,0x97,0x9E,0x99,0x8C,0x8B,0x82,0x85,0xA8,0xAF,0xA6,0xA1,0xB4,0xB3,0xBA,0xBD, + 0xC7,0xC0,0xC9,0xCE,0xDB,0xDC,0xD5,0xD2,0xFF,0xF8,0xF1,0xF6,0xE3,0xE4,0xED,0xEA, + 0xB7,0xB0,0xB9,0xBE,0xAB,0xAC,0xA5,0xA2,0x8F,0x88,0x81,0x86,0x93,0x94,0x9D,0x9A, + 0x27,0x20,0x29,0x2E,0x3B,0x3C,0x35,0x32,0x1F,0x18,0x11,0x16,0x03,0x04,0x0D,0x0A, + 0x57,0x50,0x59,0x5E,0x4B,0x4C,0x45,0x42,0x6F,0x68,0x61,0x66,0x73,0x74,0x7D,0x7A, + 0x89,0x8E,0x87,0x80,0x95,0x92,0x9B,0x9C,0xB1,0xB6,0xBF,0xB8,0xAD,0xAA,0xA3,0xA4, + 0xF9,0xFE,0xF7,0xF0,0xE5,0xE2,0xEB,0xEC,0xC1,0xC6,0xCF,0xC8,0xDD,0xDA,0xD3,0xD4, + 0x69,0x6E,0x67,0x60,0x75,0x72,0x7B,0x7C,0x51,0x56,0x5F,0x58,0x4D,0x4A,0x43,0x44, + 0x19,0x1E,0x17,0x10,0x05,0x02,0x0B,0x0C,0x21,0x26,0x2F,0x28,0x3D,0x3A,0x33,0x34, + 0x4E,0x49,0x40,0x47,0x52,0x55,0x5C,0x5B,0x76,0x71,0x78,0x7F,0x6A,0x6D,0x64,0x63, + 0x3E,0x39,0x30,0x37,0x22,0x25,0x2C,0x2B,0x06,0x01,0x08,0x0F,0x1A,0x1D,0x14,0x13, + 0xAE,0xA9,0xA0,0xA7,0xB2,0xB5,0xBC,0xBB,0x96,0x91,0x98,0x9F,0x8A,0x8D,0x84,0x83, + 0xDE,0xD9,0xD0,0xD7,0xC2,0xC5,0xCC,0xCB,0xE6,0xE1,0xE8,0xEF,0xFA,0xFD,0xF4,0xF3, +}; + +static unsigned char pmbus_pec_calc(unsigned char crc, unsigned char *buffer, size_t count) +{ + unsigned char r = crc; + const unsigned char *p = (const unsigned char*)buffer; + + while (count--) + r = crc8_table[r ^ *p++]; + return r; +} +#if 0 +static ssize_t system_cpld_show_byte(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute_2 *attr2 = to_sensor_dev_attr_2(devattr); + struct switch_system_cpld_data *data = dev_get_drvdata(dev); + void __iomem *base_addr = data->base_addr; + unsigned char reg = attr2->index; + unsigned char mask = attr2->nr; + int val = 0; + + mutex_lock(&data->update_lock); + val = (int)readb(base_addr + reg); + mutex_unlock(&data->update_lock); + + if (unlikely(val < 0)) { + return val; + } + + val = val & mask; + return snprintf(buf, PAGE_SIZE, "0x%x\n", val); +} + +static ssize_t system_cpld_set_byte(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct sensor_device_attribute_2 *attr2 = to_sensor_dev_attr_2(devattr); + struct switch_system_cpld_data *data = dev_get_drvdata(dev); + void __iomem *base_addr = data->base_addr; + unsigned char reg = attr2->index; + unsigned char mask = attr2->nr; + long value; + int status; + + status = kstrtol(buf, 10, &value); + if (status) { + return status; + } + + value = value & mask; + mutex_lock(&data->update_lock); + writeb(value, base_addr + reg); + mutex_unlock(&data->update_lock); + + return count; +} +#endif +static int system_cpld_smbus_check_busy(struct switch_system_cpld_data *cpld_data, unsigned int i2c_stat_addr, unsigned int *i2c_stat) +{ + unsigned int retry_times = 0; + unsigned int transaction_delay = delay_busy; + + for(retry_times = 0; retry_times < max_retry_time; retry_times++) + { + SYSTEM_CPLD_SLEEP(transaction_delay); + SYSTEM_CPLD_ACCESS(*i2c_stat = readb(cpld_data->base_addr + i2c_stat_addr);) + SYS_CPLD_DEBUG("[check_busy] i2c_stat:%x\n", *i2c_stat); + if((*i2c_stat & BUSY_BIT) == 0) + { + return 0; + } + } + + return -1; +} + +static int system_cpld_smbus_check_tip(struct switch_system_cpld_data *cpld_data, unsigned int i2c_stat_addr, unsigned int *i2c_stat) +{ + unsigned int retry_times = 0; + unsigned int transaction_delay = delay_tip; + + for(retry_times = 0; retry_times < max_retry_time; retry_times++) + { + SYSTEM_CPLD_SLEEP(transaction_delay); + SYSTEM_CPLD_ACCESS(*i2c_stat = readb(cpld_data->base_addr + i2c_stat_addr);) + SYS_CPLD_DEBUG("[check_tip] i2c_stat:%x\n", *i2c_stat); + if((*i2c_stat & TIP_BIT) == 0) + { + return 0; + } + } + + return -1; +} + +static int system_cpld_smbus_check_ack(struct switch_system_cpld_data *cpld_data, unsigned int i2c_stat_addr, unsigned int *i2c_stat) +{ + unsigned int retry_times = 0; + unsigned int ack_transaction_delay = delay_ack; + + for(retry_times = 0; retry_times < max_retry_time; retry_times++) + { + SYS_CPLD_DEBUG("[check_ack] i2c_stat:%x\n", *i2c_stat); + if((*i2c_stat & ACK_BIT) == 0) + { + return 0; + } + SYSTEM_CPLD_SLEEP(ack_transaction_delay); + SYSTEM_CPLD_ACCESS(*i2c_stat = readb(cpld_data->base_addr + i2c_stat_addr);) + } + + if(*i2c_stat & ACK_BIT) + { + return -1; + } + + return 0; +} + +int system_cpld_smbus_read(struct switch_system_cpld_data *cpld_data, + unsigned char channel, unsigned char addr, unsigned char offset, unsigned char length, + unsigned char *data, int size) +{ + int retval = 0; + int stop_step = 0; + unsigned int i2c_txr_addr = 0, i2c_txr_data = 0; + unsigned int i2c_rxr_addr = 0, i2c_rxr_data = 0; + unsigned int i2c_cmd_addr = 0, i2c_cmd_data = 0; + unsigned int i2c_stat_addr = 0, i2c_stat_data = 0; + unsigned char byte_to_rw = 0, temp_offset = 0; + bool is_current_read = false; + bool len_change_flag = false; + + SYS_CPLD_DEBUG("[%s] READ\n", __func__); + + if(addr > 0x7F || channel >= I2C_MAX_CHANNEL) + { + return(-EINVAL); + } + + retval = down_interruptible(&(cpld_data->sem[channel])); + if(retval) + { + return(-ERESTARTSYS); + } + + i2c_txr_addr = I2C_TRANSMIT_REG + channel*0x08; + i2c_rxr_addr = I2C_RECEIVE_REG + channel*0x08; + i2c_cmd_addr = I2C_COMMAND_REG + channel*0x08; + i2c_stat_addr = I2C_STATUS_REG + channel*0x08; + + /* + 1. Set the Transmit Register TXR with a value of Slave address + Write bit. + 2. Set the Command Register CR to 8'h90 to enable the START and WRITE. This starts the transmission on the I2C bus. + 3. Check the Transfer In Progress (TIP) bit of the Status Register, SR, to make sure the command is done. + Check the Received Acknowledge (RxACK) bit of the Status Register, SR, to make sure I2C slave has ACK. + 4. Set TRX with the slave memory address, where the data is to be read from. + 5. Set CR with 8'h10 to enable a WRITE to send to the slave memory address. + 6. Check the TIP bit of SR, to make sure the command is done. + Check the Received Acknowledge (RxACK) bit of the Status Register, SR, to make sure I2C slave has ACK. + 7. Set TRX with a value of Slave address + READ bit. + 8. Set CR with the 8'h90 to enable the START (repeated START in this case) and WRITE the value in TXR to the slave device. + 9. Check the TIP bit of SR, to make sure the command is done. + Check the Received Acknowledge (RxACK) bit of the Status Register, SR, to make sure I2C slave has ACK. + 10. Set CR with 8'h20 to issue a READ command and then an ACK command. This enables the reading of data from the slave device. + 11. Check the TIP bit of SR, to make sure the command is done. + 12. Repeat steps 10 and 11 to continue to read data from the slave device. + 13. When the Master is ready to stop reading from the Slave, set CR to 8'h28. This will read the last byte of data and then issue a NACK. + Check the TIP bit of SR, to make sure the command is done. + */ + + if(size == I2C_SMBUS_BYTE) + is_current_read = true; + + if((size == I2C_SMBUS_BLOCK_DATA) ||(size == I2C_SMBUS_I2C_BLOCK_DATA)) + { + len_change_flag = true; + } + + if(is_current_read == false) + { + /* 1. Set the Transmit Register TXR with a value of Slave address + Write bit.*/ + i2c_txr_data = (addr) << 1; + SET_TXR_WRITE(i2c_txr_data); + SYSTEM_CPLD_ACCESS(writeb(i2c_txr_data, cpld_data->base_addr + i2c_txr_addr);) + SYS_CPLD_DEBUG("[1] i2c_txr_addr:%x i2c_txr_data:%x\n", i2c_txr_addr, i2c_txr_data); + + /*2. Set the Command Register CR to 8'h90 to enable the START and WRITE. This starts the transmission on the I2C bus.*/ + i2c_cmd_data = COMMAND_START_WRITE; + SYSTEM_CPLD_ACCESS(writeb(i2c_cmd_data, cpld_data->base_addr + i2c_cmd_addr);) + SYS_CPLD_DEBUG("[2] i2c_cmd_addr:%x i2c_cmd_data:%x\n", i2c_cmd_addr, i2c_cmd_data); + + /*3. Check the Transfer In Progress (TIP) bit of the Status Register, SR, to make sure the command is done.*/ + if(system_cpld_smbus_check_tip(cpld_data, i2c_stat_addr, &i2c_stat_data)) + { + stop_step = 3; + goto device_busy; + } + + /*Check the ACK bit of the Status Registesr, SR, to make sure ACK is received.*/ + if(system_cpld_smbus_check_ack(cpld_data, i2c_stat_addr, &i2c_stat_data)) + { + stop_step = 3; + goto no_ack_response; + } + + /*4. Set TRX with the slave memory address, where the data is to be read from.*/ + i2c_txr_data = offset; + SYSTEM_CPLD_ACCESS(writeb(i2c_txr_data, cpld_data->base_addr + i2c_txr_addr);) + SYS_CPLD_DEBUG("[4] i2c_txr_addr:%x i2c_txr_data:%x\n", i2c_txr_addr, i2c_txr_data); + + /*5. Set CR with 8'h10 to enable a WRITE to send to the slave memory address.*/ + i2c_cmd_data = COMMAND_ENABLE_WRITE; + SYSTEM_CPLD_ACCESS(writeb(i2c_cmd_data, cpld_data->base_addr + i2c_cmd_addr);) + SYS_CPLD_DEBUG("[5] i2c_cmd_ddr:%x i2c_cmd_data:%x\n", i2c_cmd_addr, i2c_cmd_data); + + /*6. Check the TIP bit of SR, to make sure the command is done.*/ + if(system_cpld_smbus_check_tip(cpld_data, i2c_stat_addr, &i2c_stat_data)) + { + stop_step = 6; + goto device_busy; + } + + /*Check the ACK bit of the Status Registesr, SR, to make sure ACK is received.*/ + if(system_cpld_smbus_check_ack(cpld_data, i2c_stat_addr, &i2c_stat_data)) + { + stop_step = 6; + goto no_ack_response; + } + } + + /*7. Set TRX with a value of Slave address + READ bit.*/ + i2c_txr_data = (addr) << 1; + SET_TXR_READ(i2c_txr_data); + SYSTEM_CPLD_ACCESS(writeb(i2c_txr_data, cpld_data->base_addr + i2c_txr_addr);) + SYS_CPLD_DEBUG("[7] i2c_txr_addr:%x i2c_txr_data:%x\n", i2c_txr_addr, i2c_txr_data); + + /*8. Set CR with the 8'h90 to enable the START (repeated START in this case) and WRITE the value in TXR to the slave device.*/ + i2c_cmd_data = COMMAND_START_WRITE; + SYSTEM_CPLD_ACCESS(writeb(i2c_cmd_data, cpld_data->base_addr + i2c_cmd_addr);) + SYS_CPLD_DEBUG("[8] i2c_cmd_addr:%x i2c_cmd_data:%x\n", i2c_cmd_addr, i2c_cmd_data); + + /*9. Check the TIP bit of SR, to make sure the command is done.*/ + if(system_cpld_smbus_check_tip(cpld_data, i2c_stat_addr, &i2c_stat_data)) + { + stop_step = 9; + goto device_busy; + } + + /*Check the ACK bit of the Status Registesr, SR, to make sure ACK is received.*/ + if(system_cpld_smbus_check_ack(cpld_data, i2c_stat_addr, &i2c_stat_data)) + { + stop_step = 9; + goto no_ack_response; + } + + /*12. Repeat steps 10 and 11 to continue to read data from the slave device.*/ + byte_to_rw = length; + temp_offset = 0; + while(byte_to_rw > 1) + { + /*10. Set CR with 8'h20 to issue a READ command and then an ACK command. This enables the reading of data from the slave device.*/ + i2c_cmd_data = COMMAND_READ; + SYSTEM_CPLD_ACCESS(writeb(i2c_cmd_data, cpld_data->base_addr + i2c_cmd_addr);) + SYS_CPLD_DEBUG("[10] i2c_cmd_addr:%x i2c_cmd_data:%x\n", i2c_cmd_addr, i2c_cmd_data); + + /*11. Check the TIP bit of SR, to make sure the command is done.*/ + if(system_cpld_smbus_check_tip(cpld_data, i2c_stat_addr, &i2c_stat_data)) + { + stop_step = 11; + goto device_busy; + } + + /*Check the ACK bit of the Status Registesr, SR, to make sure ACK is received.*/ + if(system_cpld_smbus_check_ack(cpld_data, i2c_stat_addr, &i2c_stat_data)) + { + stop_step = 11; + goto no_ack_response; + } + + /*Get RXR with 8-bit data from the slave device.*/ + SYSTEM_CPLD_ACCESS(i2c_rxr_data = readb(cpld_data->base_addr + i2c_rxr_addr);) + data[temp_offset] = i2c_rxr_data; + SYS_CPLD_DEBUG("[11-2] i2c_rxr_addr:%x i2c_rxr_data:%x\n", i2c_rxr_addr, i2c_rxr_data); + + if(len_change_flag == true) + { + byte_to_rw = i2c_rxr_data + 1; + len_change_flag = false; + } + + byte_to_rw--; + temp_offset++; + } + + /*13.When the Master is ready to stop reading from the Slave, set CR to 8'h28. This will read the last byte of data and then issue a NACK.*/ + i2c_cmd_data = COMMAND_ACK_STOP_READ; + SYSTEM_CPLD_ACCESS(writeb(i2c_cmd_data, cpld_data->base_addr + i2c_cmd_addr);) + SYS_CPLD_DEBUG("[13-1] i2c_cmd_addr:%x i2c_cmd_data:%x\n", i2c_cmd_addr, i2c_cmd_data); + + /*Check the BUSY bit of SR, to make sure the command is done.*/ + if(system_cpld_smbus_check_busy(cpld_data, i2c_stat_addr, &i2c_stat_data)) + { + stop_step = 13; + goto device_busy; + } + + if(byte_to_rw) + { + /*Get the last byte from RXR with 8-bit data from the slave device.*/ + SYSTEM_CPLD_ACCESS(i2c_rxr_data = readb(cpld_data->base_addr + i2c_rxr_addr);) + data[temp_offset] = i2c_rxr_data; + SYS_CPLD_DEBUG("[13-3] i2c_rxr_addr:%x i2c_rxr_data:%x\n", i2c_rxr_addr, i2c_rxr_data); + } + + up(&(cpld_data->sem[channel])); + return(0); + +device_busy: + SYS_CPLD_DEBUG("device_busy, read failed, channel:%d addr:0x%x, length:0x%x, offset:0x%x, stop_step:%d, i2c_stat:0x%x.\n", channel, addr, length, offset, stop_step, i2c_stat_data); + + /*Re-initailize BUS.*/ + /*0. Disable the core by writing 8'h00 to the Control Register, CTR.*/ + SYSTEM_CPLD_ACCESS(writeb(CONTROL_DISABLE, cpld_data->base_addr + I2C_CONTROLLER_REG + channel*0x08);) + /*1. Program the clock PRESCALE registers, PRERlo and PRERhi, with the desired value. + This value is determined by the clock frequency and the speed of the I2C bus.*/ + SYSTEM_CPLD_ACCESS(writeb(PRER_HI_DEFAULT, cpld_data->base_addr + I2C_PRESCALE_HIGH_REG + channel*0x08);) + SYSTEM_CPLD_ACCESS(writeb(PRER_LO_DEFAULT, cpld_data->base_addr + I2C_PRESCALE_LOW_REG + channel*0x08);) + + /*2. Enable the core by writing 8'h80 to the Control Register, CTR.*/ + SYSTEM_CPLD_ACCESS(writeb(CONTROL_ENABLE, cpld_data->base_addr + I2C_CONTROLLER_REG + channel*0x08);) + SYSTEM_CPLD_SLEEP(500); + + /*Set CR to 8'h40 to issue a STOP command to avoid error.*/ + i2c_cmd_data = COMMAND_STOP; + SYSTEM_CPLD_ACCESS(writeb(i2c_cmd_data, cpld_data->base_addr + i2c_cmd_addr);) + /*Check the BUSY bit of SR, to make sure STOP is sent.*/ + if(system_cpld_smbus_check_busy(cpld_data, i2c_stat_addr, &i2c_stat_data)) + { + SYS_CPLD_DEBUG("device_busy, fail to issue STOP, i2c_stat:0x%x.\n", i2c_stat_data); + } + + SYS_CPLD_DEBUG("device_busy, read failed, channel:%d addr:0x%x, length:0x%x, offset:0x%x, stop_step:%d, i2c_stat:0x%x.\n", channel, addr, length, offset, stop_step, i2c_stat_data); + up(&(cpld_data->sem[channel])); + return (-EBUSY); + +no_ack_response: + /*Set CR to 8'h40 to issue a STOP command to avoid error.*/ + i2c_cmd_data = COMMAND_STOP; + SYSTEM_CPLD_ACCESS(writeb(i2c_cmd_data, cpld_data->base_addr + i2c_cmd_addr);) + SYS_CPLD_DEBUG("no_ack_response, read failed, channel:%d addr:0x%x, length:0x%x, offset:0x%x, stop_step:%d, i2c_stat:0x%x.\n", channel, addr, length, offset, stop_step, i2c_stat_data); + + /*Check the BUSY bit of SR, to make sure STOP is sent.*/ + if(system_cpld_smbus_check_busy(cpld_data, i2c_stat_addr, &i2c_stat_data)) + { + stop_step = 14; + goto device_busy; + } + + up(&(cpld_data->sem[channel])); + return (-ETIMEDOUT); +} + +int system_cpld_smbus_write( struct switch_system_cpld_data *cpld_data, + unsigned char channel, unsigned char addr, unsigned char offset, unsigned char length, + unsigned char *data) +{ + int retval = 0; + int stop_step = 0; + unsigned int i2c_txr_addr = 0, i2c_txr_data = 0; + unsigned int i2c_cmd_addr = 0, i2c_cmd_data = 0; + unsigned int i2c_stat_addr = 0, i2c_stat_data = 0; + unsigned char byte_to_rw = 0, temp_offset = 0; + + SYS_CPLD_DEBUG("[%s] WRITE\n", __func__); + + if(addr > 0x7F || channel >= I2C_MAX_CHANNEL) + { + return(-EINVAL); + } + + retval = down_interruptible(&(cpld_data->sem[channel])); + if(retval) + { + return(-ERESTARTSYS); + } + + i2c_txr_addr = I2C_TRANSMIT_REG + channel*0x08; + i2c_cmd_addr = I2C_COMMAND_REG + channel*0x08; + i2c_stat_addr = I2C_STATUS_REG + channel*0x08; + + /* + 1. Set the Transmit Register TXR with a value of Slave address + Write bit. + 2. Set the Command Register CR to 8'h90 to enable the START and WRITE. This starts the transmission on the I2C bus. + 3. Check the Transfer In Progress (TIP) bit of the Status Register, SR, to make sure the command is done. + Check the Received Acknowledge (RxACK) bit of the Status Register, SR, to make sure I2C slave has ACK. + 4. Set TXR with a slave memory address for the data to be written to. + 5. Set CR with 8'h10 to enable a WRITE to send to the slave memory address. + 6. Check the TIP bit of SR, to make sure the command is done. + Check the Received Acknowledge (RxACK) bit of the Status Register, SR, to make sure I2C slave has ACK. + 7. Set TXR with 8-bit data for the slave device. + 8. Set CR to 8'h10 to enable a WRITE to send data. + 9. Check the TIP bit of SR, to make sure the command is done. + Check the Received Acknowledge (RxACK) bit of the Status Register, SR, to make sure I2C slave has ACK. + 10. Repeat steps 7 to 9 to continue to send data to the slave device. + 11. Set CR to 8'h40 to then issue a STOP command. + Check the TIP bit of SR, to make sure the command is done. + */ + + /* 1. Set the Transmit Register TXR with a value of Slave address + Write bit.*/ + i2c_txr_data = (addr) << 1; + SET_TXR_WRITE(i2c_txr_data); + SYSTEM_CPLD_ACCESS(writeb(i2c_txr_data, cpld_data->base_addr + i2c_txr_addr);) + SYS_CPLD_DEBUG("[1] i2c_txr_addr:%x i2c_txr_data:%x\n", i2c_txr_addr, i2c_txr_data); + + /* 2. Set the Command Register CR to 8'h90 to enable the START and WRITE. This starts the transmission on the I2C bus.*/ + i2c_cmd_data = COMMAND_START_WRITE; + SYSTEM_CPLD_ACCESS(writeb(i2c_cmd_data, cpld_data->base_addr + i2c_cmd_addr);) + SYS_CPLD_DEBUG("[2] i2c_cmd_addr:%x i2c_cmdt_data:%x\n", i2c_cmd_addr, i2c_cmd_data); + + /* 3. Check the Transfer In Progress (TIP) bit of the Status Registesr, SR, to make sure the command is done.*/ + if(system_cpld_smbus_check_tip(cpld_data, i2c_stat_addr, &i2c_stat_data)) + { + stop_step = 3; + goto device_busy; + } + + /* Check the ACK bit of the Status Registesr, SR, to make sure ACK is received.*/ + if(system_cpld_smbus_check_ack(cpld_data, i2c_stat_addr, &i2c_stat_data)) + { + stop_step = 3; + goto no_ack_response; + } + + /* 4. Set TXR with a slave memory address for the data to be written to.*/ + i2c_txr_data = offset; + SYSTEM_CPLD_ACCESS(writeb(i2c_txr_data, cpld_data->base_addr + i2c_txr_addr);) + SYS_CPLD_DEBUG("[4] i2c_txr_addr:%x i2c_txr_data:%x\n", i2c_txr_addr, i2c_txr_data); + + /* 5. Set CR with 8'h10 to enable a WRITE to send to the slave memory address.*/ + i2c_cmd_data = COMMAND_ENABLE_WRITE; + SYSTEM_CPLD_ACCESS(writeb(i2c_cmd_data, cpld_data->base_addr + i2c_cmd_addr);) + SYS_CPLD_DEBUG("[5] i2c_cmd_addr:%x i2c_cmd_data:%x\n", i2c_cmd_addr, i2c_cmd_data); + + /* 6. Check the TIP bit of SR, to make sure the command is done.*/ + if(system_cpld_smbus_check_tip(cpld_data, i2c_stat_addr, &i2c_stat_data)) + { + stop_step = 6; + goto device_busy; + } + + /* Check the ACK bit of the Status Registesr, SR, to make sure ACK is received.*/ + if(system_cpld_smbus_check_ack(cpld_data, i2c_stat_addr, &i2c_stat_data)) + { + stop_step = 6; + goto no_ack_response; + } + + /* 10. Repeat steps 7 to 9 to continue to send data to the slave device.*/ + byte_to_rw = length; + temp_offset = 0; + while(byte_to_rw > 0) + { + /* 7. Set TXR with 8-bit data for the slave device.*/ + i2c_txr_data = data[temp_offset]; + SYSTEM_CPLD_ACCESS(writeb(i2c_txr_data, cpld_data->base_addr + i2c_txr_addr);) + SYS_CPLD_DEBUG("[7] i2c_txr_addr:%x i2c_txr_data:%x\n", i2c_txr_addr, i2c_txr_data); + + /* 8. Set CR to 8'h10 to enable a WRITE to send data.*/ + i2c_cmd_data = COMMAND_ENABLE_WRITE; + SYSTEM_CPLD_ACCESS(writeb(i2c_cmd_data, cpld_data->base_addr + i2c_cmd_addr);) + SYS_CPLD_DEBUG("[8] i2c_cmd_addr:%x i2c_cmd_data:%x\n", i2c_cmd_addr, i2c_cmd_data); + + /* 9. Check the TIP bit of SR, to make sure the command is done.*/ + if(system_cpld_smbus_check_tip(cpld_data, i2c_stat_addr, &i2c_stat_data)) + { + stop_step = 9; + goto device_busy; + } + + /* Check the ACK bit of the Status Registesr, SR, to make sure ACK is received.*/ + if(system_cpld_smbus_check_ack(cpld_data, i2c_stat_addr, &i2c_stat_data)) + { + stop_step = 9; + goto no_ack_response; + } + + byte_to_rw--; + temp_offset++; + } + + /* 11. Set CR to 8'h40 to issue a STOP command.*/ + i2c_cmd_data = COMMAND_STOP; + SYSTEM_CPLD_ACCESS(writeb(i2c_cmd_data, cpld_data->base_addr + i2c_cmd_addr);) + SYS_CPLD_DEBUG("[12] i2c_cmd_addr:%x i2c_cmd_data:%x\n", i2c_cmd_addr, i2c_cmd_data); + + /* Check the BUSY bit of SR, to make sure STOP is sent.*/ + if(system_cpld_smbus_check_busy(cpld_data, i2c_stat_addr, &i2c_stat_data)) + { + stop_step = 12; + goto device_busy; + } + + up(&(cpld_data->sem[channel])); + return(0); + +device_busy: + SYS_CPLD_DEBUG("device_busy, write failed, channel:%d addr:0x%x, length:0x%x, offset:0x%x, stop_step:%d, i2c_stat:0x%x.\n", channel, addr, length, offset, stop_step, i2c_stat_data); + + /* Re-initailize BUS.*/ + /* 0. Disable the core by writing 8'h00 to the Control Register, CTR.*/ + SYSTEM_CPLD_ACCESS(writeb(CONTROL_DISABLE, cpld_data->base_addr + I2C_CONTROLLER_REG + channel*0x08);) + /* 1. Program the clock PRESCALE registers, PRERlo and PRERhi, with + the desired value. This value is determined by the clock frequency and the speed of the I2C bus.*/ + SYSTEM_CPLD_ACCESS(writeb(PRER_HI_DEFAULT, cpld_data->base_addr + I2C_PRESCALE_HIGH_REG + channel*0x08);) + SYSTEM_CPLD_ACCESS(writeb(PRER_LO_DEFAULT, cpld_data->base_addr + I2C_PRESCALE_LOW_REG + channel*0x08);) + + /* 2. Enable the core by writing 8'h80 to the Control Register, CTR.*/ + SYSTEM_CPLD_ACCESS(writeb(CONTROL_ENABLE, cpld_data->base_addr + I2C_CONTROLLER_REG + channel*0x08);) + SYSTEM_CPLD_SLEEP(500); + + /*Set CR to 8'h40 to issue a STOP command to avoid error.*/ + i2c_cmd_data = COMMAND_STOP; + SYSTEM_CPLD_ACCESS(writeb(i2c_cmd_data, cpld_data->base_addr + i2c_cmd_addr);) + /* Check the BUSY bit of SR, to make sure STOP is sent.*/ + if(system_cpld_smbus_check_busy(cpld_data, i2c_stat_addr, &i2c_stat_data)) + { + SYS_CPLD_DEBUG("device_busy, fail to issue STOP, i2c_stat:0x%x.\n", i2c_stat_data); + } + + up(&(cpld_data->sem[channel])); + return (-EBUSY); + +no_ack_response: + /* Set CR to 8'h40 to issue a STOP command to avoid error.*/ + i2c_cmd_data = COMMAND_STOP; + SYSTEM_CPLD_ACCESS(writeb(i2c_cmd_data, cpld_data->base_addr + i2c_cmd_addr);) + + SYS_CPLD_DEBUG("no_ack_response, write failed, channel:%d addr:0x%x, length:0x%x, offset:0x%x, stop_step:%d, i2c_stat:0x%x.\n", channel, addr, length, offset, stop_step, i2c_stat_data); + + /* Check the BUSY bit of SR, to make sure STOP is sent.*/ + if(system_cpld_smbus_check_busy(cpld_data, i2c_stat_addr, &i2c_stat_data)) + { + stop_step = 13; + goto device_busy; + } + + up(&(cpld_data->sem[channel])); + return (-ETIMEDOUT); +} + +/* Return negative errno on error. */ +static s32 system_cpld_smbus_access(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, u8 command, + int size, union i2c_smbus_data *data) +{ + struct switch_system_cpld_data *cpld_data; + unsigned char buff[MAX_BUFF_SIZE] = {0}; + int retval = 0; + unsigned char length = 0, channel = 0; + char cha_num_str[3] = {0}; + unsigned char addr_shift = 0; + unsigned char pec = 0; + + SYS_CPLD_DEBUG("[%s]name:%s addr:0x%x flags:0x%x read_write:0x%x command:0x%x size:0x%x\n", + __func__, adap->name, addr, flags, read_write, command, size); + + switch(size) + { + case I2C_SMBUS_BYTE: + if(I2C_SMBUS_WRITE == read_write) + { + /* It must be 0 to avoid null pointer exception in smbus_write */ + length = 0; + } + else + { + length = 1; + } + break; + case I2C_SMBUS_BYTE_DATA: + length = 1; + if(read_write == I2C_SMBUS_WRITE) + { + buff[0] = data->byte; + } + break; + case I2C_SMBUS_WORD_DATA: + length = 2; + if(read_write == I2C_SMBUS_WRITE) + { + buff[0] = data->word & 0xff; + buff[1] = data->word >> 8; + } + break; + case I2C_SMBUS_BLOCK_DATA: + length = I2C_SMBUS_BLOCK_MAX; + if(read_write == I2C_SMBUS_WRITE) + { + length = data->block[0] + 1; + memcpy(buff, data->block, length); + } + break; + case I2C_SMBUS_I2C_BLOCK_DATA: + if(data->block[0] > I2C_SMBUS_BLOCK_MAX) + { + SYS_CPLD_DEBUG("Error:Invalid block %s size %d\n", + read_write == I2C_SMBUS_READ ? "read" : "write", + data->block[0]); + return -EINVAL; + } + length = data->block[0]; + if(read_write == I2C_SMBUS_WRITE) + { + memcpy(buff, data->block + 1, data->block[0]); + } + break; + case I2C_SMBUS_QUICK: + length = 0; + break; + default: + SYS_CPLD_DEBUG("Error:Unsupported transaction %d\n", size); + return -EOPNOTSUPP; + } + + /* some device support PEC, so check PEC function */ + if((flags & I2C_CLIENT_PEC) && size != I2C_SMBUS_QUICK + && size != I2C_SMBUS_I2C_BLOCK_DATA) + { + /* we will ignore the PEC when read*/ + if(I2C_SMBUS_WRITE == read_write) + { + addr_shift = (unsigned char)(addr << 1); + pec = pmbus_pec_calc(pec, &addr_shift, 1); + pec = pmbus_pec_calc(pec, &command, 1); + pec = pmbus_pec_calc(pec, (unsigned char *)buff, length); + *((unsigned char *)buff + length) = pec; + length++; + } + } + + memcpy(cha_num_str, (adap->name+strlen(SYSTEM_CPLD_SMBUS_NAME)), 2); + retval = kstrtou8(cha_num_str, 10, &channel); + if(retval == 0) + { + SYS_CPLD_DEBUG("channel:%d \n", channel); + } + else + { + SYS_CPLD_DEBUG("Error:%d, channel:%s \n", retval, cha_num_str); + } + + cpld_data = i2c_get_adapdata(adap); + if ((cpld_data == NULL)||(length > I2C_SMBUS_BLOCK_MAX + 2)) + { + return(-EINVAL); + } + + if(I2C_SMBUS_READ == read_write) + { + retval = system_cpld_smbus_read(cpld_data, channel, addr, command, length, buff, size); + } + else if(I2C_SMBUS_WRITE == read_write) + { + retval = system_cpld_smbus_write(cpld_data, channel, addr, command, length, buff); + } + + if (read_write == I2C_SMBUS_READ) + { + switch (size) + { + case I2C_SMBUS_BYTE: + data->byte = buff[0]; + break; + case I2C_SMBUS_BYTE_DATA: + data->byte = buff[0]; + break; + case I2C_SMBUS_WORD_DATA: + data->word = buff[0] | (buff[1] << 8); + break; + case I2C_SMBUS_I2C_BLOCK_DATA: + memcpy(data->block + 1, buff, data->block[0]); + break; + case I2C_SMBUS_BLOCK_DATA: + if (buff[0] > I2C_SMBUS_BLOCK_MAX) + { + SYS_CPLD_DEBUG("Error:Invalid block size returned: %d\n", buff[0]); + return(-EPROTO); + } + memcpy(data->block, buff, buff[0] + 1); + break; + } + } + + return retval; +} + +static u32 system_cpld_smbus_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_I2C_BLOCK | + I2C_FUNC_SMBUS_PEC; +} + +static const struct i2c_algorithm smbus_algorithm = { + .smbus_xfer = system_cpld_smbus_access, + .functionality = system_cpld_smbus_func, +}; + + +#if 1 // For user space API uages +/* file_operations: ioctl */ +struct switch_cpld_access { + /* intput output buff */ + unsigned int offset; + unsigned char buf; +}; + +#define SWITCH_CPLD_MAGIC 0xA7 +#define SWITCH_CPLD_IO 0x11 + +#define SWITCH_CPLD_READ _IOR(SWITCH_CPLD_MAGIC, SWITCH_CPLD_IO, struct switch_cpld_access) +#define SWITCH_CPLD_WRITE _IOW(SWITCH_CPLD_MAGIC, SWITCH_CPLD_IO, struct switch_cpld_access) + +#endif + +struct class *switch_cpld_driver_class; +struct cdev switch_cpld_cdev; +dev_t switch_cpld_dev_num; +#define SWITCH_CPLD_DEV_NAME "switch_cpld_dev" + +struct switch_system_cpld_data *global_switch_cpld_dev = NULL; + +long switch_cpld_ioctl(struct file *filp, unsigned int cmd, unsigned long user_addr) +{ + struct switch_cpld_access acc = {0}; + struct switch_system_cpld_data *data = global_switch_cpld_dev; + int retval = 0; + void __iomem *base_addr = data->base_addr; + + if (cmd == SWITCH_CPLD_READ) + { +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0) + if (!access_ok(VERIFY_READ,user_addr, _IOC_SIZE(cmd))) +#else + if (!access_ok(user_addr, _IOC_SIZE(cmd))) +#endif + { + return(-EINVAL); + } + + retval = copy_from_user((void *) &acc, (const void *) user_addr, sizeof(struct switch_cpld_access)); + + mutex_lock(&data->update_lock); + acc.buf = readb(base_addr + acc.offset); + mutex_unlock(&data->update_lock); + + retval = copy_to_user((void *) user_addr, (const void *) &acc, sizeof(struct switch_cpld_access)); + return(retval); + } + else if(cmd == SWITCH_CPLD_WRITE) + { +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0) + if (!access_ok(VERIFY_WRITE,user_addr, _IOC_SIZE(cmd))) +#else + if (!access_ok(user_addr, _IOC_SIZE(cmd))) +#endif + { + return(-EINVAL); + } + + retval = copy_from_user((void *) &acc, (const void *) user_addr, sizeof(struct switch_cpld_access)); + + mutex_lock(&data->update_lock); + writeb(acc.buf, base_addr + acc.offset); + mutex_unlock(&data->update_lock); + + retval = copy_to_user((void *) user_addr, (const void *) &acc, sizeof(struct switch_cpld_access)); + return(retval); + } + else + { + SYS_CPLD_DEBUG( "[%s] Unknown command\n", __func__); + return(-EINVAL); + } +} +EXPORT_SYMBOL_GPL(switch_cpld_ioctl); + +static struct file_operations switch_cpld_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = switch_cpld_ioctl, +}; + + +void system_cpld_smbus_init(struct switch_system_cpld_data *cpld_data) +{ + unsigned int i2c_ctrl_addr = 0; + unsigned int i2c_ctrl_data = 0; + unsigned int i2c_pre_low_addr = 0, i2c_pre_high_addr = 0; + unsigned int i2c_pre_low_data = 0, i2c_pre_high_data = 0; + int temp_channel = 0; + + for(temp_channel = 0; temp_channel < I2C_MAX_CHANNEL; temp_channel++) + { + /* + 1. Program the clock PRESCALE registers, PRERlo and PRERhi, with the desired value. + This value is determined by the clock frequency and the speed of the I2C bus. + 2. Enable the core by writing 8'h80 to the Control Register, CTR. + */ + i2c_pre_low_addr = I2C_PRESCALE_LOW_REG + temp_channel*0x08; + i2c_pre_high_addr = I2C_PRESCALE_HIGH_REG + temp_channel*0x08; + i2c_ctrl_addr = I2C_CONTROLLER_REG + temp_channel*0x08; + + /* 1. Program the clock PRESCALE registers, PRERlo and PRERhi, + with the desired value. This value is determined by the clock frequency and the speed of the I2C bus.*/ + i2c_pre_low_data = PRER_LO_DEFAULT; + SYSTEM_CPLD_ACCESS(writeb(i2c_pre_low_data, cpld_data->base_addr + i2c_pre_low_addr);) + SYS_CPLD_DEBUG("[pre1-1] i2c_pre_low_addr:%x %x\n", i2c_pre_low_addr, i2c_pre_low_data); + + i2c_pre_high_data = PRER_HI_DEFAULT; + SYSTEM_CPLD_ACCESS(writeb(i2c_pre_high_data, cpld_data->base_addr + i2c_pre_high_addr);) + SYS_CPLD_DEBUG("[pre1-2] i2c_pre_high_addr:%x %x\n", i2c_pre_high_addr, i2c_pre_high_data); + + /* 2. Enable the core by writing 8'h80 to the Control Register, CTR.*/ + i2c_ctrl_data= CONTROL_ENABLE; + SYSTEM_CPLD_ACCESS(writeb(i2c_ctrl_data, cpld_data->base_addr + i2c_ctrl_addr);) + SYS_CPLD_DEBUG("[pre1] i2c_ctrl_addr:%x %x\n", i2c_ctrl_addr, i2c_ctrl_data); + } + + return; +} + +static int switch_system_cpld_probe(struct platform_device *pdev) +{ + struct switch_system_cpld_data *data = NULL; + int smbus_temp_num = 0; + int smbus_adapter_num = 0; + int retval = 0; + dev_t curr_dev; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + if (!request_mem_region(LPC_DEVICE_ADDRESS1, LPC_IO_MEMORY_SIZE, "cpld-lpc")) { + dev_err(&pdev->dev, "Couldn't allocate cpld-lpc memory region\n"); + return -ENODEV; + } + + if ((data->base_addr = ioremap(LPC_DEVICE_ADDRESS1, LPC_IO_MEMORY_SIZE)) == NULL) { + release_mem_region(LPC_DEVICE_ADDRESS1, LPC_IO_MEMORY_SIZE); + dev_err(&pdev->dev, "Remap cpld-lpc memory failed\n"); + return -1; + } + + dev_info(&pdev->dev, "[%s] lpc_base0: 0x%p\n", __func__, data->base_addr); + + mutex_init(&data->update_lock); + data->dev = &pdev->dev; + platform_set_drvdata(pdev, data); + + /* Request the kernel for N_MINOR devices */ + alloc_chrdev_region(&switch_cpld_dev_num, 0, 1, "switch_cpld_driver"); + + /* Create a class : appears at /sys/class */ + switch_cpld_driver_class = class_create(THIS_MODULE, "switch_cpld_driver_class"); + + /* Initialize and create each of the device(cdev) */ + { + /* Associate the cdev with a set of file_operations */ + cdev_init(&switch_cpld_cdev, &switch_cpld_fops); + + /* Build up the current device number. To be used further */ + curr_dev = MKDEV(MAJOR(switch_cpld_dev_num), MINOR(switch_cpld_dev_num)); + + /* Create a device node for this device. Look, the class is + * being used here. The same class is associated with N_MINOR + * devices. Once the function returns, device nodes will be + * created as /dev/my_dev0, /dev/my_dev1,... You can also view + * the devices under /sys/class/my_driver_class. + */ + device_create(switch_cpld_driver_class, NULL, curr_dev, NULL, SWITCH_CPLD_DEV_NAME); + + /* Now make the device live for the users to access */ + cdev_add(&switch_cpld_cdev, curr_dev, 1); + } + + for (smbus_temp_num = 0; smbus_temp_num < I2C_MAX_CHANNEL; smbus_temp_num++) + { + sema_init(&(data->sem[smbus_temp_num]), 1); + } + + for (smbus_temp_num = 0; smbus_temp_num < I2C_MAX_CHANNEL; smbus_temp_num++) + { + i2c_set_adapdata(&data->adapter[smbus_temp_num], data); + data->adapter[smbus_temp_num].owner = THIS_MODULE; + data->adapter[smbus_temp_num].class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + data->adapter[smbus_temp_num].algo = &smbus_algorithm; + data->adapter[smbus_temp_num].dev.parent = &pdev->dev; + data->adapter[smbus_temp_num].retries = 3; + + /* Default timeout in interrupt mode: 200 ms */ + data->adapter[smbus_temp_num].timeout = HZ / 5; + + snprintf(data->adapter[smbus_temp_num].name, sizeof(data->adapter[smbus_temp_num].name), + "%s%02d adapter", SYSTEM_CPLD_SMBUS_NAME, smbus_temp_num); + + data->adapter[smbus_temp_num].nr = 101 + smbus_temp_num; + retval = i2c_add_numbered_adapter(&data->adapter[smbus_temp_num]); + + if (retval) { + dev_err(&pdev->dev, "Add i2c adapter failed\n"); + goto fail; + } + } + + system_cpld_smbus_init(data); + global_switch_cpld_dev = data; + return 0; + +fail: + for (smbus_adapter_num = 0; smbus_adapter_num < smbus_temp_num; smbus_adapter_num++) + { + i2c_del_adapter(&data->adapter[smbus_adapter_num]); + } + release_mem_region(LPC_DEVICE_ADDRESS1, LPC_IO_MEMORY_SIZE); + mutex_destroy(&data->update_lock); + return retval; +} + +static int switch_system_cpld_remove(struct platform_device *pdev) +{ + struct switch_system_cpld_data *data = platform_get_drvdata(pdev); + void __iomem *base_addr = data->base_addr; + int smbus_temp_num = 0; + + if(base_addr != NULL) + iounmap(base_addr); + release_mem_region(LPC_DEVICE_ADDRESS1, LPC_IO_MEMORY_SIZE); + + data->base_addr = NULL; + mutex_destroy(&data->update_lock); + for(smbus_temp_num = 0; smbus_temp_num < I2C_MAX_CHANNEL; smbus_temp_num++) + { + i2c_del_adapter(&data->adapter[smbus_temp_num]); + } + + device_destroy(switch_cpld_driver_class, switch_cpld_dev_num); + class_destroy(switch_cpld_driver_class); + cdev_del(&switch_cpld_cdev); + unregister_chrdev_region(switch_cpld_dev_num, 1); + + return 0; +} + +int switch_system_cpld_read(u8 reg, u8 *value) +{ + struct switch_system_cpld_data *data = NULL; + void __iomem *lpc_base = NULL; + u8 val = 0; + + if(switch_system_cpld_device == NULL) + { + return -1; + } + + data = platform_get_drvdata(switch_system_cpld_device); + lpc_base = data->base_addr; + if(lpc_base == NULL) + { + return -1; + } + + mutex_lock(&data->update_lock); + val = readb(lpc_base + reg); + mutex_unlock(&data->update_lock); + SYS_CPLD_DEBUG( "[%s] base: 0x%p, reg:0x%x, val:0x%x\n", __func__, lpc_base, reg, val); + + *value = val; + return 0; +} +EXPORT_SYMBOL(switch_system_cpld_read); + +int switch_system_cpld_write(u8 reg, u8 value) +{ + struct switch_system_cpld_data *data = NULL; + void __iomem *lpc_base = NULL; + + if(switch_system_cpld_device == NULL) + { + return -1; + } + + data = platform_get_drvdata(switch_system_cpld_device); + lpc_base = data->base_addr; + if(lpc_base == NULL) + { + return -1; + } + + SYS_CPLD_DEBUG( "[%s] base: 0x%x, reg:0x%x, val:0x%x\n", __func__, lpc_base, reg, value); + mutex_lock(&data->update_lock); + writeb(value, lpc_base + reg); + mutex_unlock(&data->update_lock); + + return 0; +} +EXPORT_SYMBOL(switch_system_cpld_write); + +static struct platform_driver switch_system_cpld_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, + .probe = switch_system_cpld_probe, + .remove = switch_system_cpld_remove, +}; + +static int __init switch_system_cpld_init(void) +{ + int err=0; + + switch_system_cpld_device = platform_device_alloc(DRV_NAME, -1); + if(!switch_system_cpld_device) + { + SYS_CPLD_DEBUG("%s(#%d): platform_device_alloc fail\n", __func__, __LINE__); + return -ENOMEM; + } + + err = platform_device_add(switch_system_cpld_device); + if(err) + { + SYS_CPLD_DEBUG("%s(#%d): platform_device_add failed\n", __func__, __LINE__); + platform_device_put(switch_system_cpld_device); + return -ENODEV; + } + + err = platform_driver_register(&switch_system_cpld_driver); + if(err) + { + SYS_CPLD_DEBUG("%s(#%d): platform_driver_register failed(%d)\n", __func__, __LINE__, err); + platform_device_unregister(switch_system_cpld_device); + } + + return err; +} + +static void __exit switch_system_cpld_exit(void) +{ + platform_device_unregister(switch_system_cpld_device); + platform_driver_unregister(&switch_system_cpld_driver); +} + + +module_init(switch_system_cpld_init); +module_exit(switch_system_cpld_exit); + +MODULE_DESCRIPTION("Switch System CPLD Driver"); +MODULE_VERSION(SYSTEM_CPLD_DRIVER_VERSION); +MODULE_LICENSE("GPL"); + diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/switch_system_fpga.c b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/switch_system_fpga.c new file mode 100644 index 0000000000..9ff7ed1a26 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/switch_system_fpga.c @@ -0,0 +1,1526 @@ +#include /* + => module_init() + => module_exit() + => MODULE_LICENSE() + => MODULE_VERSION() + => MODULE_AUTHOR() + => struct module + */ +#include /* + => typedef int (*initcall_t)(void); + Note: the 'initcall_t' function returns 0 when succeed. + In the Linux kernel, error codes are negative numbers + belonging to the set defined in . + => typedef void (*exitcall_t)(void); + => __init + => __exit + */ +#include /* + => moduleparam() + */ +#include /* + => dev_t (u32) + */ +#include /* + => MAJOR() + => MINOR() + */ +#include /* + => register_chrdev_region() + => unregister_chrdev_region() + => alloc_chrdev_region() + => struct file_operations + => struct file + => struct inode + => unsigned int imajor() + => unsigned int iminor() + */ +#include /* + => struct cdev + */ +#include /* + => void *memset() + */ +#include /* + => void kfree() + */ +#include /* + => class_create() + => device_create() + */ +#include +#include +#include /* + => void cdev_init() + */ +#include /* + => access_ok() + */ +#include /* + => current + */ +#include /* + => struct semaphore + */ +#include /* + => struct wait_queue_head_t + => init_waitqueue_head() + */ +#include /* + => O_NONBLOCK + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//simon: try to support 4.x kernel +#include + +#define I2C_DEBUG(...) +/*#define I2C_DEBUG(...) printk(KERN_ALERT __VA_ARGS__)*/ +#define ERROR_DEBUG(...) printk(KERN_ALERT __VA_ARGS__) + +#ifndef ACCESS_ONCE +#define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x)) +#endif + +#define FPGA_DRIVER_VERSION "0.0.f" + +MODULE_LICENSE("GPL"); +MODULE_VERSION(FPGA_DRIVER_VERSION); +MODULE_AUTHOR("Will Chen, Junhao He"); + +#define FPGA_DEV_NAME "fpga" +#define FPGA_CLASS_NAME "fpga_class" +#define FPGA_DRIVER_NAME "fpgaio" +#define FPGA_REGION_NAME "fpga_register" +#define FPGA_SMBUS_NAME "Accton_FPGA_SMBus_" +#define BAR0 0 + +#if 1 +/*Please copy the following definition into the c files if you want to use fpga_access*/ +#define MAX_BUFF_SIZE 128 +#define MAX_EEPROM_SIZE 256 + +/* file_operations: ioctl */ +struct fpga_access { + /* input */ + unsigned char addr; /* 0x0 - 0x7F */ + unsigned char offset; + unsigned char length; + unsigned char channel; + /* intput output buff */ + unsigned char buff[MAX_BUFF_SIZE]; +}; + +#define FPGA_MAGIC 0xA7 +#define FPGA_IO_CMD 0x10 +#define FPGA_SPI_IO 0x11 +#define FPGA_LOCK_CMD 0xEE +#define FPGA_UNLOCK_CMD 0xDD + +#define FPGA_READ _IOR(FPGA_MAGIC, FPGA_IO_CMD, struct fpga_access) +#define FPGA_WRITE _IOW(FPGA_MAGIC, FPGA_IO_CMD, struct fpga_access) +#define FPGA_LOCK _IOW(FPGA_MAGIC, FPGA_LOCK_CMD, struct fpga_access) +#define FPGA_UNLOCK _IOW(FPGA_MAGIC, FPGA_UNLOCK_CMD, struct fpga_access) +#define FPGA_SPI_READ _IOR(FPGA_MAGIC, FPGA_SPI_IO, struct fpga_access) +#define FPGA_SPI_WRITE _IOW(FPGA_MAGIC, FPGA_SPI_IO, struct fpga_access) +/*Please copy the above definition into the c files if you want to use fpga_access*/ +#endif + +/* FPGA Bus control parameters */ +static unsigned int max_retry_time = 20; +static unsigned int delay_ack = 50; +static unsigned int delay_tip = 26; +static unsigned int delay_busy = 10; +static unsigned int fpga_dev_count = 1; +static unsigned int disable_iic_access = 0; + +struct spinlock fpga_pcie_lock; + +module_param(max_retry_time, uint, S_IRUGO); +MODULE_PARM_DESC(max_retry_time, "Max retry times to wait TIP/ACK (20 by default)"); +module_param(delay_ack, uint, S_IRUGO); +MODULE_PARM_DESC(delay_ack, "Delay time before the next time to check ACK bit (50 us by default)"); +module_param(delay_tip, uint, S_IRUGO); +MODULE_PARM_DESC(delay_tip, "Delay time before the next time to check TIP bit (26 us by default)"); +module_param(delay_busy, uint, S_IRUGO); +MODULE_PARM_DESC(delay_busy, "Delay time before the next time to check BUSY bit (10 us by default)"); +module_param(disable_iic_access, uint, 0644); +MODULE_PARM_DESC(disable_iic_access, "Disable i2c access when set to NOT 0."); + + +#define MAX_CHANNEL 35 +#define FPGA_SLEEP(X) usleep_range((X*8/10), X) +#define I2C_PRESCALE_LOW_OFFSET 0x0400 // PRESCALE registers, PRERlo +#define I2C_PRESCALE_HIGH_OFFSET 0x0404 // PRESCALE registers, PRERhi +#define I2C_CONTROLLER_OFFSET 0x0408 // Control Register, CTR +#define I2C_TRANSMIT_RECEIVE_OFFSET 0x040C // Transmit Register, TXR +#define I2C_COMMAND_STATUS_OFFSET 0x0410 // Command Register CR/Status Registesr, SR + +//#define PRER_LO_DEFAULT 0x007f +#define PRER_HI_DEFAULT 0x0000 +#define CONTROL_DISABLE 0x0000 +#define CONTROL_ENABLE 0x0080 +#define COMMAND_START_WRITE 0x0090 +#define COMMAND_ENABLE_WRITE 0x0010 +#define COMMAND_READ 0x0020 +#define COMMAND_ACK_STOP_READ 0x0068 +#define COMMAND_STOP 0x0040 +#define COMMAND_STOP_WRITE 0x0050 +#define BUSY_BIT 0x40 +#define TIP_BIT 0x2 +#define ACK_BIT 0x80 +#define SET_TXR_WRITE(x) (x &= ~(1UL)) +#define SET_TXR_READ(x) (x |= (1UL)) +#define MODULE_ATTR_NUM 4 +#define tBUF_TIME_NS 20000 +#define tBUF_ACTUAL_TIME_OFFSET_NS 3000 +#define tBUF_MAX_DELAY_US 20 + +static unsigned int PRER_LO_DEFAULT = 0x001f; + +module_param(PRER_LO_DEFAULT, uint, S_IRUGO); +MODULE_PARM_DESC(PRER_LO_DEFAULT, "set i2c freq(default 0x001f)"); + + +enum iic_access_type { + IIC_READ_NO_ACK = 0, + IIC_WRITE_NO_ACK = 1, + IIC_ACCESS_NO_ACK_MAX = 2, +}; + + +struct fpga_pci_device { + char *name; + dev_t fpga_dev_number; + struct class *fpga_class; + struct device *fpga_udev; + struct cdev fpga_cdev; + resource_size_t start; + resource_size_t len; + void *fpga_base; + int irq_vec_count; + unsigned int irq; + int pci_enabled; + int pci_region_requested; + int cdev_added; + struct semaphore sem[MAX_CHANNEL]; + struct i2c_adapter adapter[MAX_CHANNEL]; + unsigned long long last_stop_time[MAX_CHANNEL]; +}; + +#define FPGA_ACCESS(cmd) \ + do { \ + unsigned long flags; \ + spin_lock_irqsave(&(fpga_pcie_lock), flags); \ + cmd \ + spin_unlock_irqrestore(&(fpga_pcie_lock), flags); \ + }while(0); + +/* 10ee:7021 Maybe need modify*/ +static struct pci_device_id fpga_id_table[] = { \ + { + 0x10ee, /* Vendor ID */ + 0x7021, /* Device ID */ + PCI_ANY_ID, /* Sub-vendor ID */ + PCI_ANY_ID, /* Sub-device ID */ + 0, /* Class */ + 0, /* Class mask */ + 0, /* Driver data */ + }, + {0,}, +}; + +static unsigned long no_ack_counter[MAX_CHANNEL][IIC_ACCESS_NO_ACK_MAX] = {0}; + +static struct kobject *fpga_kobj; + +struct fpga_base_data { + struct mutex update_lock; + void *fpga_base; +}; +struct fpga_base_data fpga_data; + +int fpga_pcie_write(u8 device_id, u32 reg, u32 value) +{ + int status = 0 ; + FPGA_ACCESS(*((unsigned int *) (fpga_data.fpga_base + reg)) = value;) + I2C_DEBUG("fpga_pcie_write() 0x%x reg 0x%x\n",value,reg); + return status; +} +EXPORT_SYMBOL(fpga_pcie_write); + +int fpga_pcie_read(u8 device_id, u32 reg) +{ + int status = 0; + + FPGA_ACCESS(status = ACCESS_ONCE(*((unsigned int *)(fpga_data.fpga_base + reg)));) + I2C_DEBUG("fpga_pcie_read() 0x%x reg 0x%x\n",status,reg); + return status; +} +EXPORT_SYMBOL(fpga_pcie_read); + +static int fpga_smbus_check_busy(struct fpga_pci_device *fpga_pci_dev, unsigned int i2c_cmd_stat_addr, unsigned int *i2c_stat) +{ + unsigned int retry_times = 0; + unsigned int transaction_delay = delay_busy; + + for(retry_times = 0; retry_times < max_retry_time; retry_times++) + { + FPGA_SLEEP(transaction_delay); + FPGA_ACCESS(*i2c_stat = ACCESS_ONCE(*((unsigned int *)(fpga_pci_dev->fpga_base + i2c_cmd_stat_addr)));) + I2C_DEBUG("[check_busy] i2c_stat:%x\n", *i2c_stat); + if((*i2c_stat & BUSY_BIT) == 0) + { + return 0; + } + } + + return -1; +} + +static int fpga_smbus_check_tip(struct fpga_pci_device *fpga_pci_dev, unsigned int i2c_cmd_stat_addr, unsigned int *i2c_stat) +{ + unsigned int retry_times = 0; + unsigned int transaction_delay = delay_tip; + + for(retry_times = 0; retry_times < max_retry_time; retry_times++) + { + FPGA_SLEEP(transaction_delay); + FPGA_ACCESS(*i2c_stat = ACCESS_ONCE(*((unsigned int *)(fpga_pci_dev->fpga_base + i2c_cmd_stat_addr)));) + I2C_DEBUG("[check_tip] i2c_stat:%x\n", *i2c_stat); + if((*i2c_stat & TIP_BIT) == 0) + { + return 0; + } + } + + return -1; +} + +static int fpga_smbus_check_ack(struct fpga_pci_device *fpga_pci_dev, unsigned int i2c_cmd_stat_addr, unsigned int *i2c_stat) +{ + unsigned int retry_times = 0; + unsigned int ack_transaction_delay = delay_ack; + + for(retry_times = 0; retry_times < max_retry_time; retry_times++) + { + I2C_DEBUG("[check_ack] i2c_stat:%x\n", *i2c_stat); + if((*i2c_stat & ACK_BIT) == 0) + { + return 0; + } + FPGA_SLEEP(ack_transaction_delay); + FPGA_ACCESS(*i2c_stat = ACCESS_ONCE(*((unsigned int *)(fpga_pci_dev->fpga_base + i2c_cmd_stat_addr)));) + } + + if(*i2c_stat & ACK_BIT) + { + return -1; + } + + return 0; +} + + +static int check_disable_iic_access(void) +{ + return (disable_iic_access != 0); +} + + +int fpga_smbus_read(struct fpga_pci_device *fpga_pci_dev, + unsigned char channel, unsigned char addr, unsigned char offset, unsigned char length, + unsigned char *data) +{ + int retval = 0; + int stop_step = 0; + unsigned int i2c_txr_addr = 0, i2c_cmd_stat_addr = 0; + unsigned int i2c_txr_data = 0, i2c_cmd_stat_data = 0; + unsigned int i2c_stat = 0; + unsigned char byte_to_rw = 0, temp_offset = 0; + unsigned long long diff_time_ns = 0; + unsigned char buff[MAX_BUFF_SIZE]; + if(data == NULL) + { + return(-EINVAL); + } + + I2C_DEBUG("[%s] READ: %x \n", __func__, data); + + if (addr > 0x7F || channel >= MAX_CHANNEL) + { + return(-EINVAL); + } + + if( check_disable_iic_access() ) { + return -ETIMEDOUT; + } + + retval = down_interruptible(&(fpga_pci_dev->sem[channel])); + if (retval) + { + return(-ERESTARTSYS); + } + + i2c_txr_addr = I2C_TRANSMIT_RECEIVE_OFFSET + channel*0x20; + i2c_cmd_stat_addr = I2C_COMMAND_STATUS_OFFSET + channel*0x20; + + /* + 1. Set the Transmit Register TXR with a value of Slave address + Write bit. + 2. Set the Command Register CR to 8'h90 to enable the START and WRITE. This starts the transmission on the I2C bus. + 3. Check the Transfer In Progress (TIP) bit of the Status Register, SR, to make sure the command is done. + Check the Received Acknowledge (RxACK) bit of the Status Register, SR, to make sure I2C slave has ACK. + 4. Set TRX with the slave memory address, where the data is to be read from. + 5. Set CR with 8'h10 to enable a WRITE to send to the slave memory address. + 6. Check the TIP bit of SR, to make sure the command is done. + Check the Received Acknowledge (RxACK) bit of the Status Register, SR, to make sure I2C slave has ACK. + 7. Set TRX with a value of Slave address + READ bit. + 8. Set CR with the 8'h90 to enable the START (repeated START in this case) and WRITE the value in TXR to the slave device. + 9. Check the TIP bit of SR, to make sure the command is done. + Check the Received Acknowledge (RxACK) bit of the Status Register, SR, to make sure I2C slave has ACK. + 10. Set CR with 8'h20 to issue a READ command and then an ACK command. This enables the reading of data from the slave device. + 11. Check the TIP bit of SR, to make sure the command is done. + 12. Repeat steps 10 and 11 to continue to read data from the slave device. + 13. When the Master is ready to stop reading from the Slave, set CR to 8'h28. This will read the last byte of data and then issue a NACK. + Check the TIP bit of SR, to make sure the command is done. + */ + + /* 1. Set the Transmit Register TXR with a value of Slave address + Write bit.*/ + i2c_txr_data = (addr) << 1; + SET_TXR_WRITE(i2c_txr_data); + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + i2c_txr_addr)) = i2c_txr_data;) + I2C_DEBUG("[1] i2c_txr_addr:%x i2c_txr_data:%x\n", i2c_txr_addr, i2c_txr_data); + + diff_time_ns = (ktime_to_ns(ktime_get()) - fpga_pci_dev->last_stop_time[channel]) + tBUF_ACTUAL_TIME_OFFSET_NS; + if(diff_time_ns < tBUF_TIME_NS) + { + udelay(((tBUF_TIME_NS - diff_time_ns) / 1000) > tBUF_MAX_DELAY_US ? tBUF_MAX_DELAY_US : ((tBUF_TIME_NS - diff_time_ns) / 1000)); + } + + /*2. Set the Command Register CR to 8'h90 to enable the START and WRITE. This starts the transmission on the I2C bus.*/ + i2c_cmd_stat_data = COMMAND_START_WRITE; + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + i2c_cmd_stat_addr)) = i2c_cmd_stat_data;) + I2C_DEBUG("[2] i2c_cmd_stat_addr:%x i2c_cmd_stat_data:%x\n", i2c_cmd_stat_addr, i2c_cmd_stat_data); + + /*3. Check the Transfer In Progress (TIP) bit of the Status Register, SR, to make sure the command is done.*/ + if(fpga_smbus_check_tip(fpga_pci_dev, i2c_cmd_stat_addr, &i2c_stat)) + { + stop_step = 3; + goto device_busy; + } + + /*Check the ACK bit of the Status Registesr, SR, to make sure ACK is received.*/ + if(fpga_smbus_check_ack(fpga_pci_dev, i2c_cmd_stat_addr, &i2c_stat)) + { + stop_step = 3; + goto no_ack_response; + } + + /*4. Set TRX with the slave memory address, where the data is to be read from.*/ + i2c_txr_data = offset; + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + i2c_txr_addr)) = i2c_txr_data;) + I2C_DEBUG("[4] i2c_txr_addr:%x i2c_txr_data:%x\n", i2c_txr_addr, i2c_txr_data); + + /*5. Set CR with 8'h10 to enable a WRITE to send to the slave memory address.*/ + i2c_cmd_stat_data = COMMAND_ENABLE_WRITE; + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + i2c_cmd_stat_addr)) = i2c_cmd_stat_data;) + I2C_DEBUG("[5] i2c_cmd_stat_addr:%x i2c_cmd_stat_data:%x\n", i2c_cmd_stat_addr, i2c_cmd_stat_data); + + /*6. Check the TIP bit of SR, to make sure the command is done.*/ + if(fpga_smbus_check_tip(fpga_pci_dev, i2c_cmd_stat_addr, &i2c_stat)) + { + stop_step = 6; + goto device_busy; + } + + /*Check the ACK bit of the Status Registesr, SR, to make sure ACK is received.*/ + if(fpga_smbus_check_ack(fpga_pci_dev, i2c_cmd_stat_addr, &i2c_stat)) + { + stop_step = 6; + goto no_ack_response; + } + + /*7. Set TRX with a value of Slave address + READ bit.*/ + i2c_txr_data = (addr) << 1; + SET_TXR_READ(i2c_txr_data); + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + i2c_txr_addr)) = i2c_txr_data;) + I2C_DEBUG("[7] i2c_txr_addr:%x i2c_txr_data:%x\n", i2c_txr_addr, i2c_txr_data); + + /*8. Set CR with the 8'h90 to enable the START (repeated START in this case) and WRITE the value in TXR to the slave device.*/ + i2c_cmd_stat_data = COMMAND_START_WRITE; + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + i2c_cmd_stat_addr)) = i2c_cmd_stat_data;) + I2C_DEBUG("[8] i2c_cmd_stat_addr:%x i2c_cmd_stat_data:%x\n", i2c_cmd_stat_addr, i2c_cmd_stat_data); + + /*9. Check the TIP bit of SR, to make sure the command is done.*/ + if(fpga_smbus_check_tip(fpga_pci_dev, i2c_cmd_stat_addr, &i2c_stat)) + { + stop_step = 9; + goto device_busy; + } + + /*Check the ACK bit of the Status Registesr, SR, to make sure ACK is received.*/ + if(fpga_smbus_check_ack(fpga_pci_dev, i2c_cmd_stat_addr, &i2c_stat)) + { + stop_step = 9; + goto no_ack_response; + } + + /*12. Repeat steps 10 and 11 to continue to read data from the slave device.*/ + byte_to_rw = length; + temp_offset = 0; + while(byte_to_rw > 1) + { + /*10. Set CR with 8'h20 to issue a READ command and then an ACK command. This enables the reading of data from the slave device.*/ + i2c_cmd_stat_data = COMMAND_READ; + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + i2c_cmd_stat_addr)) = i2c_cmd_stat_data;) + I2C_DEBUG("[10] i2c_cmd_stat_addr:%x i2c_cmd_stat_data:%x\n", i2c_cmd_stat_addr, i2c_cmd_stat_data); + + /*11. Check the TIP bit of SR, to make sure the command is done.*/ + if(fpga_smbus_check_tip(fpga_pci_dev, i2c_cmd_stat_addr, &i2c_stat)) + { + stop_step = 11; + goto device_busy; + } + + /*Check the ACK bit of the Status Registesr, SR, to make sure ACK is received.*/ + if(fpga_smbus_check_ack(fpga_pci_dev, i2c_cmd_stat_addr, &i2c_stat)) + { + stop_step = 11; + goto no_ack_response; + } + + /*Get TXR with 8-bit data from the slave device.*/ + FPGA_ACCESS(i2c_txr_data = ACCESS_ONCE(*((unsigned int *)(fpga_pci_dev->fpga_base + i2c_txr_addr)));) + buff[temp_offset] = i2c_txr_data; + I2C_DEBUG("[11-2] i2c_txr_addr:%x i2c_txr_data:%x\n", i2c_txr_addr, i2c_txr_data); + + byte_to_rw--; + temp_offset++; + } + + /*13.When the Master is ready to stop reading from the Slave, set CR to 8'h28. This will read the last byte of data and then issue a NACK.*/ + i2c_cmd_stat_data = COMMAND_ACK_STOP_READ; + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + i2c_cmd_stat_addr)) = i2c_cmd_stat_data;) + I2C_DEBUG("[13-1] i2c_cmd_stat_addr:%x i2c_cmd_stat_data:%x\n", i2c_cmd_stat_addr, i2c_cmd_stat_data); + + /*Check the BUSY bit of SR, to make sure the command is done.*/ + if(fpga_smbus_check_busy(fpga_pci_dev, i2c_cmd_stat_addr, &i2c_stat)) + { + stop_step = 13; + goto device_busy; + } + fpga_pci_dev->last_stop_time[channel] = ktime_to_ns(ktime_get()); + + if(byte_to_rw) + { + /*Get the last byte from TXR with 8-bit data from the slave device.*/ + FPGA_ACCESS(i2c_txr_data = ACCESS_ONCE(*((unsigned int *)(fpga_pci_dev->fpga_base + i2c_txr_addr)));) + buff[temp_offset] = i2c_txr_data; + I2C_DEBUG("[13-3] i2c_txr_addr:%x i2c_txr_data:%x\n", i2c_txr_addr, i2c_txr_data); + } + + memcpy(data, buff, length); + + up(&(fpga_pci_dev->sem[channel])); + return(0); + +device_busy: + I2C_DEBUG("device_busy, write failed, channel:%d addr:0x%x, length:0x%x, offset:0x%x, stop_step:%d, i2c_stat:0x%x.\n", + channel, addr, length, offset, stop_step, i2c_stat); + + /*Re-initailize BUS.*/ + /*0. Disable the core by writing 8'h00 to the Control Register, CTR.*/ + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + I2C_CONTROLLER_OFFSET + channel*0x20)) = CONTROL_DISABLE;) + /*1. Program the clock PRESCALE registers, PRERlo and PRERhi, with the desired value. + This value is determined by the clock frequency and the speed of the I2C bus.*/ + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + I2C_PRESCALE_LOW_OFFSET + channel*0x20)) = PRER_LO_DEFAULT;) + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + I2C_PRESCALE_HIGH_OFFSET + channel*0x20)) = PRER_HI_DEFAULT;) + /*2. Enable the core by writing 8'h80 to the Control Register, CTR.*/ + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + I2C_CONTROLLER_OFFSET + channel*0x20)) = CONTROL_ENABLE;) + FPGA_SLEEP(500); + + /* Set CR with 8'h20 to issue a READ command. This is going to issue a 9-clock command. */ + i2c_cmd_stat_data = COMMAND_READ; + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + i2c_cmd_stat_addr)) = i2c_cmd_stat_data;) + + /* Check the TIP bit of SR, to make sure the command is done. */ + if(fpga_smbus_check_tip(fpga_pci_dev, i2c_cmd_stat_addr, &i2c_stat)) + { + I2C_DEBUG(KERN_NOTICE "device_busy read, fail to check TIP, i2c_stat:0x%x.\n", i2c_stat); + } + + /*Set CR to 8'h40 to issue a STOP command to avoid error.*/ + i2c_cmd_stat_data = COMMAND_STOP; + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + i2c_cmd_stat_addr)) = i2c_cmd_stat_data;) + /*Check the BUSY bit of SR, to make sure STOP is sent.*/ + if(fpga_smbus_check_busy(fpga_pci_dev, i2c_cmd_stat_addr, &i2c_stat)) + { + I2C_DEBUG(KERN_NOTICE "device_busy read, fail to check STOP, i2c_stat:0x%x.\n", i2c_stat); + } + + I2C_DEBUG(KERN_NOTICE "device_busy, read failed, channel:%d addr:0x%x, length:0x%x, offset:0x%x, stop_step:%d, i2c_stat:0x%x.\n", + channel, addr, length, offset, stop_step, i2c_stat); + up(&(fpga_pci_dev->sem[channel])); + return (-EBUSY); + +no_ack_response: + /* Set CR with 8'h20 to issue a READ command. This is going to issue a 9-clock command. */ + i2c_cmd_stat_data = COMMAND_READ; + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + i2c_cmd_stat_addr)) = i2c_cmd_stat_data;) + + /* Check the TIP bit of SR, to make sure the command is done. */ + if(fpga_smbus_check_tip(fpga_pci_dev, i2c_cmd_stat_addr, &i2c_stat)) + { + I2C_DEBUG(KERN_NOTICE "no_ack_response read, fail to check TIP, i2c_stat:0x%x.\n", i2c_stat); + } + + /*Set CR to 8'h40 to issue a STOP command to avoid error.*/ + i2c_cmd_stat_data = COMMAND_STOP; + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + i2c_cmd_stat_addr)) = i2c_cmd_stat_data;) + + if(no_ack_counter[channel][IIC_READ_NO_ACK] < 3) { + I2C_DEBUG(KERN_ERR "no_ack_response, read failed, channel:%d addr:0x%x, length:0x%x, offset:0x%x, stop_step:%d, i2c_stat:0x%x.\n", + channel, addr, length, offset, stop_step, i2c_stat); + } + else { + I2C_DEBUG("no_ack_response, read failed, channel:%d addr:0x%x, length:0x%x, offset:0x%x, stop_step:%d, i2c_stat:0x%x.\n", + channel, addr, length, offset, stop_step, i2c_stat); + } + no_ack_counter[channel][IIC_READ_NO_ACK] += 1; + + /*Check the BUSY bit of SR, to make sure STOP is sent.*/ + if(fpga_smbus_check_busy(fpga_pci_dev, i2c_cmd_stat_addr, &i2c_stat)) + { + stop_step = 14; + goto device_busy; + } + + up(&(fpga_pci_dev->sem[channel])); + return (-ETIMEDOUT); +} + +int fpga_smbus_write(struct fpga_pci_device *fpga_pci_dev, + unsigned char channel, unsigned char addr, unsigned char offset, unsigned char length, + unsigned char *data) +{ + int retval = 0; + int stop_step = 0; + unsigned int i2c_txr_addr = 0, i2c_cmd_stat_addr = 0; + unsigned int i2c_txr_data = 0, i2c_cmd_stat_data = 0; + unsigned int i2c_stat = 0; + unsigned char byte_to_rw = 0, temp_offset = 0; + unsigned long long diff_time_ns = 0; + unsigned char buff[MAX_BUFF_SIZE]; + if(data == NULL) + { + return(-EINVAL); + } + + I2C_DEBUG("[%s] WRITE\n", __func__); + + if (addr > 0x7F || channel >= MAX_CHANNEL) + { + return(-EINVAL); + } + + if( check_disable_iic_access() ) { + return -ETIMEDOUT; + } + + retval = down_interruptible(&(fpga_pci_dev->sem[channel])); + if (retval) + { + return(-ERESTARTSYS); + } + + i2c_txr_addr = I2C_TRANSMIT_RECEIVE_OFFSET + channel*0x20; + i2c_cmd_stat_addr = I2C_COMMAND_STATUS_OFFSET + channel*0x20; + + memcpy(buff, data, length); + + /* + 1. Set the Transmit Register TXR with a value of Slave address + Write bit. + 2. Set the Command Register CR to 8'h90 to enable the START and WRITE. This starts the transmission on the I2C bus. + 3. Check the Transfer In Progress (TIP) bit of the Status Register, SR, to make sure the command is done. + Check the Received Acknowledge (RxACK) bit of the Status Register, SR, to make sure I2C slave has ACK. + 4. Set TXR with a slave memory address for the data to be written to. + 5. Set CR with 8'h10 to enable a WRITE to send to the slave memory address. + 6. Check the TIP bit of SR, to make sure the command is done. + Check the Received Acknowledge (RxACK) bit of the Status Register, SR, to make sure I2C slave has ACK. + 7. Set TXR with 8-bit data for the slave device. + 8. Set CR to 8'h10 to enable a WRITE to send data. + 9. Check the TIP bit of SR, to make sure the command is done. + Check the Received Acknowledge (RxACK) bit of the Status Register, SR, to make sure I2C slave has ACK. + 10. Repeat steps 7 to 9 to continue to send data to the slave device. + 11. Set CR to 8'h40 to then issue a STOP command. + Check the TIP bit of SR, to make sure the command is done. + */ + + /* 1. Set the Transmit Register TXR with a value of Slave address + Write bit.*/ + i2c_txr_data = (addr) << 1; + SET_TXR_WRITE(i2c_txr_data); + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + i2c_txr_addr)) = i2c_txr_data;) + I2C_DEBUG("[1] i2c_txr_addr:%x i2c_txr_data:%x\n", i2c_txr_addr, i2c_txr_data); + + diff_time_ns = (ktime_to_ns(ktime_get()) - fpga_pci_dev->last_stop_time[channel]) + tBUF_ACTUAL_TIME_OFFSET_NS; + if(diff_time_ns < tBUF_TIME_NS) + { + udelay(((tBUF_TIME_NS - diff_time_ns) / 1000) > tBUF_MAX_DELAY_US ? tBUF_MAX_DELAY_US : ((tBUF_TIME_NS - diff_time_ns) / 1000)); + } + + /* 2. Set the Command Register CR to 8'h90 to enable the START and WRITE. This starts the transmission on the I2C bus.*/ + i2c_cmd_stat_data = COMMAND_START_WRITE; + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + i2c_cmd_stat_addr)) = i2c_cmd_stat_data;) + I2C_DEBUG("[2] i2c_cmd_stat_addr:%x i2c_cmd_stat_data:%x\n", i2c_cmd_stat_addr, i2c_cmd_stat_data); + + /* 3. Check the Transfer In Progress (TIP) bit of the Status Registesr, SR, to make sure the command is done.*/ + if(fpga_smbus_check_tip(fpga_pci_dev, i2c_cmd_stat_addr, &i2c_stat)) + { + stop_step = 3; + goto device_busy; + } + + /* Check the ACK bit of the Status Registesr, SR, to make sure ACK is received.*/ + if(fpga_smbus_check_ack(fpga_pci_dev, i2c_cmd_stat_addr, &i2c_stat)) + { + stop_step = 3; + goto no_ack_response; + } + + /* 4. Set TXR with a slave memory address for the data to be written to.*/ + i2c_txr_data = offset; + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + i2c_txr_addr)) = i2c_txr_data;) + I2C_DEBUG("[4] i2c_txr_addr:%x i2c_txr_data:%x\n", i2c_txr_addr, i2c_txr_data); + + /* 5. Set CR with 8'h10 to enable a WRITE to send to the slave memory address.*/ + i2c_cmd_stat_data = COMMAND_ENABLE_WRITE; + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + i2c_cmd_stat_addr)) = i2c_cmd_stat_data;) + I2C_DEBUG("[5] i2c_cmd_stat_addr:%x i2c_cmd_stat_data:%x\n", i2c_cmd_stat_addr, i2c_cmd_stat_data); + + /* 6. Check the TIP bit of SR, to make sure the command is done.*/ + if(fpga_smbus_check_tip(fpga_pci_dev, i2c_cmd_stat_addr, &i2c_stat)) + { + stop_step = 6; + goto device_busy; + } + + /* Check the ACK bit of the Status Registesr, SR, to make sure ACK is received.*/ + if(fpga_smbus_check_ack(fpga_pci_dev, i2c_cmd_stat_addr, &i2c_stat)) + { + stop_step = 6; + goto no_ack_response; + } + + /* 10. Repeat steps 7 to 9 to continue to send data to the slave device.*/ + byte_to_rw = length; + temp_offset = 0; + while(byte_to_rw > 0) + { + /* 7. Set TXR with 8-bit data for the slave device.*/ + i2c_txr_data = buff[temp_offset]; + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + i2c_txr_addr)) = i2c_txr_data;) + I2C_DEBUG("[7] i2c_txr_addr:%x i2c_txr_data:%x\n", i2c_txr_addr, i2c_txr_data); + + /* 8. Set CR to 8'h10 to enable a WRITE to send data.*/ + i2c_cmd_stat_data = COMMAND_ENABLE_WRITE; + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + i2c_cmd_stat_addr)) = i2c_cmd_stat_data;) + I2C_DEBUG("[8] i2c_cmd_stat_addr:%x i2c_cmd_stat_data:%x\n", i2c_cmd_stat_addr, i2c_cmd_stat_data); + + /* 9. Check the TIP bit of SR, to make sure the command is done.*/ + if(fpga_smbus_check_tip(fpga_pci_dev, i2c_cmd_stat_addr, &i2c_stat)) + { + stop_step = 9; + goto device_busy; + } + + /* Check the ACK bit of the Status Registesr, SR, to make sure ACK is received.*/ + if(fpga_smbus_check_ack(fpga_pci_dev, i2c_cmd_stat_addr, &i2c_stat)) + { + stop_step = 9; + goto no_ack_response; + } + + byte_to_rw--; + temp_offset++; + } + + /* 11. Set CR to 8'h40 to issue a STOP command.*/ + i2c_cmd_stat_data = COMMAND_STOP; + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + i2c_cmd_stat_addr)) = i2c_cmd_stat_data;) + I2C_DEBUG("[12] i2c_cmd_stat_addr:%x i2c_cmd_stat_data:%x\n", i2c_cmd_stat_addr, i2c_cmd_stat_data); + + /* Check the BUSY bit of SR, to make sure STOP is sent.*/ + if(fpga_smbus_check_busy(fpga_pci_dev, i2c_cmd_stat_addr, &i2c_stat)) + { + stop_step = 12; + goto device_busy; + } + fpga_pci_dev->last_stop_time[channel] = ktime_to_ns(ktime_get()); + + up(&(fpga_pci_dev->sem[channel])); + return(0); + +device_busy: + I2C_DEBUG("device_busy, write failed, channel:%d addr:0x%x, length:0x%x, offset:0x%x, stop_step:%d, i2c_stat:0x%x.\n", + channel, addr, length, offset, stop_step, i2c_stat); + + /* Re-initailize BUS.*/ + /* 0. Disable the core by writing 8'h00 to the Control Register, CTR.*/ + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + I2C_CONTROLLER_OFFSET + channel*0x20)) = CONTROL_DISABLE;) + /* 1. Program the clock PRESCALE registers, PRERlo and PRERhi, with + the desired value. This value is determined by the clock frequency and the speed of the I2C bus.*/ + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + I2C_PRESCALE_LOW_OFFSET + channel*0x20)) = PRER_LO_DEFAULT;) + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + I2C_PRESCALE_HIGH_OFFSET + channel*0x20)) = PRER_HI_DEFAULT;) + /* 2. Enable the core by writing 8'h80 to the Control Register, CTR.*/ + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + I2C_CONTROLLER_OFFSET + channel*0x20)) = CONTROL_ENABLE;) + FPGA_SLEEP(500); + + /* Set CR with 8'h20 to issue a READ command. This is going to issue a 9-clock command. */ + i2c_cmd_stat_data = COMMAND_READ; + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + i2c_cmd_stat_addr)) = i2c_cmd_stat_data;) + + /* Check the TIP bit of SR, to make sure the command is done. */ + if(fpga_smbus_check_tip(fpga_pci_dev, i2c_cmd_stat_addr, &i2c_stat)) + { + printk(KERN_NOTICE "device_busy write, fail to check TIP, i2c_stat:0x%x.\n", i2c_stat); + } + + /*Set CR to 8'h40 to issue a STOP command to avoid error.*/ + i2c_cmd_stat_data = COMMAND_STOP; + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + i2c_cmd_stat_addr)) = i2c_cmd_stat_data;) + /* Check the BUSY bit of SR, to make sure STOP is sent.*/ + if(fpga_smbus_check_busy(fpga_pci_dev, i2c_cmd_stat_addr, &i2c_stat)) + { + printk(KERN_NOTICE "device_busy write, fail to check BUSY, i2c_stat:0x%x.\n", i2c_stat); + } + + up(&(fpga_pci_dev->sem[channel])); + return (-EBUSY); + +no_ack_response: + /* Set CR with 8'h20 to issue a READ command. This is going to issue a 9-clock command. */ + i2c_cmd_stat_data = COMMAND_READ; + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + i2c_cmd_stat_addr)) = i2c_cmd_stat_data;) + + /* Check the TIP bit of SR, to make sure the command is done. */ + if(fpga_smbus_check_tip(fpga_pci_dev, i2c_cmd_stat_addr, &i2c_stat)) + { + printk(KERN_NOTICE "no_ack_response write, fail to check TIP, i2c_stat:0x%x.\n", i2c_stat); + } + + /* Set CR to 8'h40 to issue a STOP command to avoid error.*/ + i2c_cmd_stat_data = COMMAND_STOP; + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + i2c_cmd_stat_addr)) = i2c_cmd_stat_data;) + + if(no_ack_counter[channel][IIC_WRITE_NO_ACK] < 3) { + printk(KERN_ERR "no_ack_response, write failed, channel:%d addr:0x%x, length:0x%x, offset:0x%x, stop_step:%d, i2c_stat:0x%x.\n", + channel, addr, length, offset, stop_step, i2c_stat); + } + else { + I2C_DEBUG("no_ack_response, write failed, channel:%d addr:0x%x, length:0x%x, offset:0x%x, stop_step:%d, i2c_stat:0x%x.\n", + channel, addr, length, offset, stop_step, i2c_stat); + } + no_ack_counter[channel][IIC_WRITE_NO_ACK] += 1; + + /* Check the BUSY bit of SR, to make sure STOP is sent.*/ + if(fpga_smbus_check_busy(fpga_pci_dev, i2c_cmd_stat_addr, &i2c_stat)) + { + printk(KERN_NOTICE "no_ack_response write, fail to check BUSY, i2c_stat:0x%x.\n", i2c_stat); + stop_step = 13; + goto device_busy; + } + + up(&(fpga_pci_dev->sem[channel])); + return (-ETIMEDOUT); +} + +static u32 fpga_smbus_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK; +} + +/* Return negative errno on error. */ +static s32 fpga_smbus_access(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, u8 command, + int size, union i2c_smbus_data *data) +{ + struct fpga_pci_device *fpga_pci_dev; + int retval = 0; + unsigned char length = 0, channel = 0; + char cha_num_str[3] = {0}; + + I2C_DEBUG("[%s]name:%s addr:0x%x flags:0x%x read_write:0x%x command:0x%x size:0x%x\n", + __func__, adap->name, addr, flags, read_write, command, size); + + switch(size) + { + case I2C_SMBUS_BYTE: + length = 1; + break; + case I2C_SMBUS_BYTE_DATA: + length = 1; + break; + case I2C_SMBUS_WORD_DATA: + length = 2; + break; + case I2C_SMBUS_BLOCK_DATA: + length = data->block[0]; + break; + case I2C_SMBUS_I2C_BLOCK_DATA: + length = 32; + break; + case I2C_SMBUS_QUICK: + length = 0; + break; + default: + length = 1; + break; + } + + memcpy(cha_num_str, (adap->name+18), 2); + retval = kstrtou8(cha_num_str, 10, &channel); + if(retval == 0) + { + I2C_DEBUG("channel:%d \n", channel); + } + else + { + ERROR_DEBUG("Error:%d, channel:%s \n", retval, cha_num_str); + } + + fpga_pci_dev = i2c_get_adapdata(adap); + if(fpga_pci_dev == NULL) + { + return(-EINVAL); + } + + if(length > 32+2) + { + return(-EINVAL); + } + if(data == NULL) + { + return(-EINVAL); + } + + if(I2C_SMBUS_READ == read_write) + { + retval = fpga_smbus_read(fpga_pci_dev, channel, addr, command, length, (unsigned char *)data); + } + else if(I2C_SMBUS_WRITE == read_write) + { + if(size==I2C_SMBUS_BLOCK_DATA) + retval = fpga_smbus_write(fpga_pci_dev, channel, addr, command, length, (unsigned char *)data->block+1); + else + retval = fpga_smbus_write(fpga_pci_dev, channel, addr, command, length, (unsigned char *)data); + } + + return retval; +} + +static const struct i2c_algorithm smbus_algorithm = { + .smbus_xfer = fpga_smbus_access, + .functionality = fpga_smbus_func, +}; + +static pci_ers_result_t fpga_error_detected(struct pci_dev *pdev, pci_channel_state_t error) +{ + I2C_DEBUG( "[%s]\n", __func__); + return PCI_ERS_RESULT_CAN_RECOVER; +} + +static pci_ers_result_t fpga_mmio_enabled(struct pci_dev *pdev) +{ + I2C_DEBUG( "[%s]\n", __func__); + return PCI_ERS_RESULT_CAN_RECOVER; +} + +static pci_ers_result_t fpga_slot_reset(struct pci_dev *pdev) +{ + I2C_DEBUG( "[%s]\n", __func__); + return PCI_ERS_RESULT_CAN_RECOVER; +} + +static void fpga_resume(struct pci_dev *pdev) +{ + I2C_DEBUG( "[%s]\n", __func__); + return; +} + +static const struct pci_error_handlers fpga_error_handlers = { + .error_detected = fpga_error_detected, + .mmio_enabled = fpga_mmio_enabled, + .slot_reset = fpga_slot_reset, + .resume = fpga_resume, +}; + +static int fpga_open(struct inode *inode, struct file *filp) +{ + I2C_DEBUG( "[%s]\n", __func__); + filp->private_data = (void *) container_of(inode->i_cdev, struct fpga_pci_device, fpga_cdev); + + return(0); +} + +static int fpga_release(struct inode *inode, struct file *filp) +{ + I2C_DEBUG( "[%s]\n", __func__); + return(0); +} + +ssize_t fpga_read(struct file *filp, char __user *user_data, size_t size, loff_t *offset) +{ + struct fpga_pci_device *fpga_pci_dev = NULL; + + fpga_pci_dev = (struct fpga_pci_device *) filp->private_data; + I2C_DEBUG( "[%s] %s\n", __func__, fpga_pci_dev->name); + + return(0); +} + +ssize_t fpga_write(struct file *filp, const char __user *user_data, size_t size, loff_t *offset) +{ + struct fpga_pci_device *fpga_pci_dev = NULL; + + fpga_pci_dev = (struct fpga_pci_device *) filp->private_data; + I2C_DEBUG( "[%s] %s\n", __func__, fpga_pci_dev->name); + + return(size); +} + +long fpga_init_smbus(struct fpga_pci_device *fpga_pci_dev) +{ + unsigned int i2c_ctrl_addr = 0; + unsigned int i2c_ctrl_data = 0; + unsigned int i2c_pre_low_addr = 0, i2c_pre_high_addr = 0; + unsigned int i2c_pre_low_data = 0, i2c_pre_high_data = 0; + int temp_channel = 0; + + for(temp_channel = 0; temp_channelfpga_base + i2c_pre_low_addr)) = i2c_pre_low_data;) + I2C_DEBUG( "[pre1-1] i2c_pre_low_addr:%x %x\n", i2c_pre_low_addr, i2c_pre_low_addr); + + i2c_pre_high_data = PRER_HI_DEFAULT; + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + i2c_pre_high_addr)) = i2c_pre_high_data;) + I2C_DEBUG( "[pre1-2] i2c_pre_high_addr:%x %x\n", i2c_pre_high_addr, i2c_pre_high_addr); + + /* 2. Enable the core by writing 8'h80 to the Control Register, CTR.*/ + i2c_ctrl_data= CONTROL_ENABLE; + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + i2c_ctrl_addr)) = i2c_ctrl_data;) + I2C_DEBUG( "[pre1] i2c_ctrl_addr:%x %x\n", i2c_ctrl_addr, i2c_ctrl_addr); + } + + return 0; +} + +long fpga_mask_all_intr(struct fpga_pci_device *fpga_pci_dev) +{ + unsigned int SI5395_INT; + /* 1. Mask all I2C Module Interrupt*/ + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + 0x5c)) = 0x01FFFFF;) + + /* 2. Mask CDR Interrupt Mask*/ + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + 0x44)) = 0x00001FFF;) + + /* 3. Mask TEMP Interrupt Mask*/ + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + 0x48)) = 0x00000003;) + + /* 4. Mask QSFP Interrupt Mask*/ + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + 0x4c)) = 0x0000FFFF;) + + /* 5. Mask QDD Interrupt Mask*/ + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + 0x50)) = 0x0000000F;) + + /* 6. Mask SI5395_INT_mask Interrupt Mask*/ + FPGA_ACCESS(SI5395_INT = ACCESS_ONCE(*((unsigned int *)(fpga_pci_dev->fpga_base + 0x34)));) + SI5395_INT |= 0x00110000; + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + 0x34)) = SI5395_INT;) + + return 0; +} + +long fpga_ioctl(struct file *filp, unsigned int cmd, unsigned long user_addr) +{ + struct fpga_pci_device *fpga_pci_dev = NULL; + struct fpga_access acc = {0}; + int retval = 0; + unsigned short spi_offset = 0; + + if (cmd == FPGA_READ) + { + I2C_DEBUG( "[%s] READ\n", __func__); + fpga_pci_dev = (struct fpga_pci_device *) filp->private_data; +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0) + if (!access_ok(VERIFY_READ,user_addr, _IOC_SIZE(cmd))) +#else + if (!access_ok(user_addr, _IOC_SIZE(cmd))) +#endif + { + I2C_DEBUG( "[%s] Access NOT OK\n", __func__); + return(-EINVAL); + } + + retval = copy_from_user((void *) &acc, (const void *) user_addr, sizeof(struct fpga_access)); + if (retval) + { + return(-EFAULT); + } + + if (acc.length + acc.offset > MAX_EEPROM_SIZE) + { + return(-EINVAL); + } + + retval = fpga_smbus_read(fpga_pci_dev, acc.channel, acc.addr, acc.offset, acc.length, acc.buff); + if(retval != 0) + { + return retval; + } + + retval = copy_to_user((void *) user_addr, (const void *) &acc, sizeof(struct fpga_access)); + if (retval) + { + return(-EFAULT); + } + + return(0); + } + else if (cmd == FPGA_WRITE) + { + fpga_pci_dev = (struct fpga_pci_device *) filp->private_data; +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0) + if (!access_ok(VERIFY_WRITE,user_addr, _IOC_SIZE(cmd))) +#else + if (!access_ok(user_addr, _IOC_SIZE(cmd))) +#endif + { + I2C_DEBUG( "[%s] Access NOT OK\n", __func__); + return(-EINVAL); + } + + retval = copy_from_user((void *) &acc, (const void *) user_addr, sizeof(struct fpga_access)); + if (retval) + { + return(-EFAULT); + } + + if (acc.length + acc.offset > MAX_EEPROM_SIZE) + { + return(-EINVAL); + } + + retval = fpga_smbus_write(fpga_pci_dev, acc.channel, acc.addr, acc.offset, acc.length, acc.buff); + if(retval != 0) + { + return retval; + } + + return(0); + } + else if (cmd == FPGA_LOCK) + { + return(0); + } + else if (cmd == FPGA_UNLOCK) + { + return(0); + } + else if (cmd == FPGA_SPI_READ || cmd == FPGA_SPI_WRITE) + { + fpga_pci_dev = (struct fpga_pci_device *) filp->private_data; +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0) + if (!access_ok(VERIFY_WRITE,user_addr, _IOC_SIZE(cmd))) +#else + if (!access_ok(user_addr, _IOC_SIZE(cmd))) +#endif + { + I2C_DEBUG( "[%s] Access NOT OK\n", __func__); + return(-EINVAL); + } + + retval = copy_from_user((void *) &acc, (const void *) user_addr, sizeof(struct fpga_access)); + if (retval) + { + return(-EFAULT); + } + + if (retval) + { + return(-ERESTARTSYS); + } + spi_offset = (acc.buff[0]<<8) + acc.buff[1]; + if (cmd == FPGA_SPI_READ) + { + FPGA_ACCESS(acc.buff[2] = ACCESS_ONCE(*((unsigned char *)(fpga_pci_dev->fpga_base + spi_offset)));) + } + else if (cmd == FPGA_SPI_WRITE) + { + FPGA_ACCESS(*((unsigned char *) (fpga_pci_dev->fpga_base + spi_offset)) = acc.buff[2];) + } + retval = copy_to_user((void *) user_addr, (const void *) &acc, sizeof(struct fpga_access)); + + return(0); + } + else + { + ERROR_DEBUG( "[%s] Unknown command\n", __func__); + return(-EINVAL); + } +} + +#ifdef INTERRUPT_SUPPORT +static irqreturn_t fpga_intr_msi(int irq, void *data) +{ + static int count=0; + + disable_irq_nosync(irq); + + enable_irq(irq); + + I2C_DEBUG( "MSI irq:%d, count=%d\n", irq, count); + count++; + + return IRQ_HANDLED; +} +#endif + +static struct file_operations fpga_fops = { + .owner = THIS_MODULE, + .open = fpga_open, + .read = fpga_read, + .write = fpga_write, + .unlocked_ioctl = fpga_ioctl, + .release = fpga_release, +}; + +static int fpga_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + int retval = 0; + struct fpga_pci_device *fpga_pci_dev = NULL; + int smbus_temp_num,smbus_adapter_num = 0; + + fpga_pci_dev = (struct fpga_pci_device *) kzalloc(sizeof(struct fpga_pci_device), GFP_KERNEL); + if (!fpga_pci_dev) + { + ERROR_DEBUG( "[%s] Fail to alloc fpga_pci_dev memory\n", __func__); + retval = -ENOMEM; + goto fail_to_alloc_fpga_pci_dev; + } + + pci_set_drvdata(pdev, (void *) fpga_pci_dev); + + fpga_pci_dev->name = "Accton FPGA"; + + retval = alloc_chrdev_region(&(fpga_pci_dev->fpga_dev_number), 0, fpga_dev_count, FPGA_DEV_NAME); + if (retval) + { + ERROR_DEBUG( "[%s] Fail to alloc fpga_dev_number %d\n", __func__, fpga_pci_dev->fpga_dev_number); + goto fail_to_alloc_dev_number; + } + + fpga_pci_dev->fpga_class = class_create(THIS_MODULE, FPGA_CLASS_NAME); + if (IS_ERR(fpga_pci_dev->fpga_class)) + { + ERROR_DEBUG( "[%s] Fail to create %s [fpga_class: %p]\n", __func__, FPGA_CLASS_NAME, fpga_pci_dev->fpga_class); + retval = PTR_ERR(fpga_pci_dev->fpga_class); + goto fail_to_create_fpga_class; + } + I2C_DEBUG( "[%s] fpga_class created\n", __func__); + + fpga_pci_dev->fpga_udev = device_create(fpga_pci_dev->fpga_class, NULL, fpga_pci_dev->fpga_dev_number, + NULL, FPGA_DEV_NAME); + + if (IS_ERR(fpga_pci_dev->fpga_udev)) + { + ERROR_DEBUG( "[%s] Fail to create fpga_udev\n", __func__); + retval = PTR_ERR(fpga_pci_dev->fpga_udev); + goto fail_to_create_fpga_udev; + } + I2C_DEBUG( "[%s] fpga_udev created\n", __func__); + + retval = pci_enable_device(pdev); + if (retval) + { + ERROR_DEBUG( "[%s] Fail to enable PCIe device\n", __func__); + goto fail_to_enable_pci_device; + } + fpga_pci_dev->pci_enabled = 1; + + fpga_pci_dev->start = pci_resource_start(pdev, BAR0); + I2C_DEBUG( "[%s] Get PCI resource address 0x%llx\n", __func__, fpga_pci_dev->start); + + fpga_pci_dev->len = pci_resource_len(pdev, BAR0); + I2C_DEBUG( "[%s] Get PCI resource length %llu\n", __func__, fpga_pci_dev->len); + + retval = pci_request_region(pdev, BAR0, FPGA_REGION_NAME); + if (retval) + { + ERROR_DEBUG( "[%s] Fail to request PCI resource region\n", __func__); + goto fail_to_request_pci_region; + } + fpga_pci_dev->pci_region_requested = 1; + + fpga_pci_dev->fpga_base = pci_iomap(pdev, BAR0, fpga_pci_dev->len); + if (!fpga_pci_dev->fpga_base) { + ERROR_DEBUG( "[%s] Fail to remap PCI resource address\n", __func__); + retval = -ENODEV; + goto fail_to_remap_pci_resource; + } + else + { + I2C_DEBUG( "[%s] Remap PCI resource address 0x%p\n", __func__, fpga_pci_dev->fpga_base); + } + + /* Init these items before interrupt is enabled */ + for(smbus_temp_num = 0; smbus_temp_num < MAX_CHANNEL; smbus_temp_num++) + { + sema_init(&(fpga_pci_dev->sem[smbus_temp_num]), 1); + } + spin_lock_init(&(fpga_pcie_lock)); + + pci_set_master(pdev); + + /* Register sysfs hooks */ + fpga_data.fpga_base = fpga_pci_dev->fpga_base; + +#ifdef INTERRUPT_SUPPORT + fpga_mask_all_intr(fpga_pci_dev); + + /* Register interrupt */ + retval = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI); + if (retval > 0) + { + fpga_pci_dev->irq_vec_count = retval; + I2C_DEBUG( "[%s] PCI irq vector count %d IRQ number: %u\n", __func__, retval, pdev->irq); + } + else + { + I2C_DEBUG( "[%s] Fail to alloc PCI irq vectors\n", __func__); + goto fail_to_alloc_irq_vectors; + } + + retval = request_irq(pci_irq_vector(pdev, 0), fpga_intr_msi, 0, "FPGA MSI", (void *) fpga_pci_dev); + if (retval < 0) { + I2C_DEBUG( "[%s] Fail to request irq\n", __func__); + goto fail_to_request_irq; + } else { + I2C_DEBUG( "[%s] Request irq no. %d\n", __func__, pdev->irq); + fpga_pci_dev->irq = pdev->irq; + } +#endif + + cdev_init(&(fpga_pci_dev->fpga_cdev), &fpga_fops); + fpga_pci_dev->fpga_cdev.owner = THIS_MODULE; + + retval = cdev_add(&(fpga_pci_dev->fpga_cdev), fpga_pci_dev->fpga_dev_number, fpga_dev_count); + if (retval) + { + goto fail_to_add_fpga_cdev; + } + fpga_pci_dev->cdev_added = 1; + + for(smbus_temp_num = 0; smbus_temp_num < MAX_CHANNEL; smbus_temp_num++) + { + i2c_set_adapdata(&fpga_pci_dev->adapter[smbus_temp_num], fpga_pci_dev); + fpga_pci_dev->adapter[smbus_temp_num].owner = THIS_MODULE; + fpga_pci_dev->adapter[smbus_temp_num].class = I2C_CLASS_HWMON | I2C_CLASS_SPD;; + fpga_pci_dev->adapter[smbus_temp_num].algo = &smbus_algorithm; + fpga_pci_dev->adapter[smbus_temp_num].dev.parent = &pdev->dev; + fpga_pci_dev->adapter[smbus_temp_num].retries = 3; + + /* Default timeout in interrupt mode: 200 ms */ + fpga_pci_dev->adapter[smbus_temp_num].timeout = HZ / 5; + + snprintf(fpga_pci_dev->adapter[smbus_temp_num].name, sizeof(fpga_pci_dev->adapter[smbus_temp_num].name), + "%s%02d adapter", FPGA_SMBUS_NAME, smbus_temp_num); + + fpga_pci_dev->adapter[smbus_temp_num].nr = 601+smbus_temp_num; + retval = i2c_add_numbered_adapter(&fpga_pci_dev->adapter[smbus_temp_num]); + + if (retval) { + ERROR_DEBUG( "[%s] err: 0x%x\n", __func__, retval); + goto fail_to_add_i2c_adapter; + } + } + + fpga_init_smbus(fpga_pci_dev); + + dev_info(&(pdev->dev), "FPGA SMBus is running with delay_ack %d us, delay_tip %d us, delay_busy %d us, and max_retry_time %d, PRER_LO_DEFAULT: 0x%x \n", + delay_ack, delay_tip, delay_busy, max_retry_time, PRER_LO_DEFAULT); + + return(0); + +fail_to_add_i2c_adapter: + for(smbus_adapter_num = 0; smbus_adapter_num < smbus_temp_num; smbus_adapter_num++) + { + i2c_del_adapter(&fpga_pci_dev->adapter[smbus_adapter_num]); + } + +fail_to_add_fpga_cdev: + free_irq(pdev->irq, (void *) fpga_pci_dev); + fpga_pci_dev->cdev_added = 0; +#ifdef INTERRUPT_SUPPORT +fail_to_request_irq: + pci_free_irq_vectors(pdev); + fpga_pci_dev->irq = 0; +fail_to_alloc_irq_vectors: + iounmap(fpga_pci_dev->fpga_base); + fpga_pci_dev->irq_vec_count = 0; +#endif +fail_to_remap_pci_resource: + pci_release_region(pdev, BAR0); + fpga_pci_dev->fpga_base = NULL; +fail_to_request_pci_region: + pci_disable_device(pdev); + fpga_pci_dev->pci_region_requested = 0; +fail_to_enable_pci_device: + device_destroy(fpga_pci_dev->fpga_class, fpga_pci_dev->fpga_dev_number); + fpga_pci_dev->pci_enabled = 0; +fail_to_create_fpga_udev: + class_destroy(fpga_pci_dev->fpga_class); + fpga_pci_dev->fpga_udev = NULL; +fail_to_create_fpga_class: + unregister_chrdev_region(fpga_pci_dev->fpga_dev_number, fpga_dev_count); + fpga_pci_dev->fpga_class = NULL; +fail_to_alloc_dev_number: + fpga_pci_dev->fpga_dev_number = 0; + kfree(fpga_pci_dev); +fail_to_alloc_fpga_pci_dev: + pci_set_drvdata(pdev, (void *) NULL); + + return(retval); +} + +static void fpga_pci_remove(struct pci_dev *pdev) +{ + struct fpga_pci_device *fpga_pci_dev = NULL; + int smbus_temp_num; + + I2C_DEBUG( "[%s]\n", __func__); + + fpga_pci_dev = (struct fpga_pci_device *) pci_get_drvdata(pdev); + if (fpga_pci_dev) + { + if (fpga_pci_dev->cdev_added) + { + cdev_del(&(fpga_pci_dev->fpga_cdev)); + } + + if (fpga_pci_dev->irq > 0) + { + free_irq(pci_irq_vector(pdev, 0), (void *) fpga_pci_dev); + I2C_DEBUG( "[%s] free_irq()\n", __func__); + } + + if (fpga_pci_dev->irq_vec_count > 0) + { + pci_free_irq_vectors(pdev); + I2C_DEBUG( "[%s] pci_free_irq_vectors()\n", __func__); + } + + if (fpga_pci_dev->fpga_base != NULL) + { + iounmap(fpga_pci_dev->fpga_base); + I2C_DEBUG( "[%s] iounmap()\n", __func__); + } + + if (fpga_pci_dev->pci_region_requested) + { + pci_release_region(pdev, BAR0); + I2C_DEBUG( "[%s] pci_release_region()\n", __func__); + } + + if (fpga_pci_dev->pci_enabled) + { + pci_disable_device(pdev); + I2C_DEBUG( "[%s] pci_disable_device()\n", __func__); + } + + if (fpga_pci_dev->fpga_udev) + { + device_destroy(fpga_pci_dev->fpga_class, fpga_pci_dev->fpga_dev_number); + } + + if (fpga_pci_dev->fpga_class) + { + class_destroy(fpga_pci_dev->fpga_class); + I2C_DEBUG( "[%s] class_destroy()\n", __func__); + } + + if (fpga_pci_dev->fpga_dev_number) + { + unregister_chrdev_region(fpga_pci_dev->fpga_dev_number, fpga_dev_count); + I2C_DEBUG( "[%s] unregister_chrdev_region()\n", __func__); + } + + for(smbus_temp_num = 0; smbus_temp_num < MAX_CHANNEL; smbus_temp_num++) + { + i2c_del_adapter(&fpga_pci_dev->adapter[smbus_temp_num]); + } + + kfree(fpga_pci_dev); + I2C_DEBUG( "[%s] kfree(fpga_pci_dev)\n", __func__); + } + + return; +} + +static struct pci_driver fpga_pci_driver = { + .name = FPGA_DRIVER_NAME, + .id_table = fpga_id_table, + .probe = fpga_pci_probe, + .remove = fpga_pci_remove, + .err_handler = &fpga_error_handlers, +}; + +static int __init fpga_module_init(void) +{ + I2C_DEBUG( "[%s]\n", __func__); + mutex_init(&fpga_data.update_lock); + return pci_register_driver(&fpga_pci_driver); +} + +static void __exit fpga_module_cleanup(void) +{ + I2C_DEBUG( "[%s]\n", __func__); + if(fpga_kobj){ + kobject_put(fpga_kobj); + } + mutex_destroy(&fpga_data.update_lock); + pci_unregister_driver(&fpga_pci_driver); + return; +} + +module_init(fpga_module_init); +module_exit(fpga_module_cleanup); + +MODULE_DEVICE_TABLE(pci, fpga_id_table); + diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/switch_system_fpga.h b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/switch_system_fpga.h new file mode 100644 index 0000000000..13d7ad2454 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/switch_system_fpga.h @@ -0,0 +1,16 @@ +#ifndef FPGA_DRIVER_H +#define FPGA_DRIVER_H + +enum fpga_type { + fpga1 = 0, + fpga2, + max_type +}; + +int fpga_pcie_write(u8 device_id, u32 reg, u32 value); +int fpga_pcie_read(u8 device_id ,u32 reg); + + +#endif /* FPGA_DRIVER_H */ + + diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/sysfs_ipmi.c b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/sysfs_ipmi.c new file mode 100644 index 0000000000..6e1c3a5f75 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/sysfs_ipmi.c @@ -0,0 +1,1622 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "switch_psu_driver.h" +#include "switch_sensor_driver.h" +#include "switch_cpld_driver.h" + +/* ott_ipmi_comm.h */ +#define OTT_IPMI_XFER_DEFAULT_TIMEOUT 5 +unsigned int loglevel_bmc = 0; +EXPORT_SYMBOL_GPL(loglevel_bmc); + +#define IPMI_INTF_NUM (0) +static struct ipmi_user *g_ipmi_mh_user = NULL; +static struct semaphore g_ipmi_msg_sem; +static struct ipmi_recv_msg g_bmc_recv_msg; +typedef struct { + long timeout; /* 单位是秒,ott_ipmi_xfer对小于或等于0的值,会用OTT_IPMI_XFER_DEFAULT_TIMEOUT,所以可以初始化成0 */ + struct kernel_ipmi_msg req; + struct kernel_ipmi_msg rsp; +} ott_ipmi_xfer_info_s; + + +typedef struct { + struct wait_queue_head * wait_address; + poll_table pt; +} ott_ipmi_poll_info_s; + +typedef struct { + unsigned char ccode; /* IPMI completion code */ + unsigned int value; +} bmc_fan_speed_get_s; + +enum ipmi_mfr_info_type +{ + ACK_DATA_RESP_CHAR = 1, + ACK_DATA_RESP_INT = 2, + ACK_DATA_RESP_FLOAT = 3, + ACK_DATA_RESP_FLOAT_DIV = 4, + ACK_DATA_RESP_PRESENT = 5, + ACK_DATA_RESP_BITMAP = 6, +}; + +enum ipmi_app_power_attribute_type +{ + POWER_GOOD = 0x02, + POWER_STATUS = 0x03, + POWER_CURRENT_LIMIT = 0x04, + POWER_TEMP_LIMIT = 0x05, + POWER_INPUT_VOLTAGE = 0x0E, + POWER_OUTPUT_VOLTAGE = 0x0F, + POWER_OUTPUT_CURRENT = 0x10, + POWER_TEMPERATURE = 0x11, + POWER_FAN_SPEED = 0x12, + POWER_OUTPUT_POWER = 0x13, + POWER_INPUT_POWER = 0x14, + POWER_HARDWARE_VER = 0x21, + POWER_INPUT_CURRENT = 0x23, + POWER_TEMPERATURE_1 = 0x24, + POWER_TEMPERATURE_2 = 0x25, + POWER_TEMPERATURE_3 = 0x26, + POWER_POUT_MAX = 0x27, + +}; + +enum ipmi_app_power_alarm_type +{ + POWER_ALARM_CMD = 0x1, + POWER_ALARM_TEMPERATURE = 0x2, + POWER_ALARM_VIN_UV_FAULT = 0x3, + POWER_ALARM_IOUT_OC_FAULT = 0x4, + POWER_ALARM_VOUT_OV_FAULT = 0x5, + POWER_ALARM_VIN_OV_DETECT = 0x6, + POWER_ALARM_VIN_UNIT_OFF = 0x7, + POWER_ALARM_OTHER = 0x9, + POWER_ALARM_FAN = 0xA, + POWER_ALARM_INPUT = 0xD, + POWER_ALARM_IOUT_POUT = 0xE, + POWER_ALARM_VOUT = 0xF, +}; + +enum cpld_access_active +{ + CPLD_READ = 0x00, + CPLD_WRITE = 0x01, +}; + +enum fan_eeprom_field +{ + fan_model_name = 0x00, + fan_serial_number = 0x01, + fan_vendor = 0x02, + fan_part_number = 0x03, + fan_hardware_version = 0x04, + fan_direction = 0x05, +}; + +enum eeprom_read_type +{ + eeprom_part_number = 0x01, + eeprom_serial_number = 0x02, + eeprom_vendor = 0x03, + eeprom_model_name = 0x04, + eeprom_hardware_version = 0x05, + eeprom_date = 0x06, +}; + +enum Bits +{ + BIT0 = 0x0001, + BIT1 = 0x0002, + BIT2 = 0x0004, + BIT3 = 0x0008, + BIT4 = 0x0010, + BIT5 = 0x0020, + BIT6 = 0x0040, + BIT7 = 0x0080, + BIT8 = 0x0100, + BIT9 = 0x0200, + BIT10 = 0x0400, + BIT11 = 0x0800, + BIT12 = 0x1000, + BIT13 = 0x2000, + BIT14 = 0x4000, + BIT15 = 0x8000, +}; + +static DEFINE_MUTEX(ott_ipmi_mutex); + +#define IPMI_NETFN 0x36 +#define IPMI_GET_THERMAL_DATA 0x10 +#define IPMI_GET_FAN_SPEED 0x20 +#define IPMI_GET_FAN_PWM 0x21 +#define IPMI_SET_FAN_PWM 0x22 +#define IPMI_FAN_GET_EEPROM 0x23 +#define IPMI_FAN_GET_LED_STATUS 0x25 +#define IPMI_FAN_SET_LED_STATUS 0x26 +#define IPMI_GET_PSU_ALARM_DATA 0x18 +#define IPMI_GET_PSU_ATTRIBUTE_DATA 0x30 +#define IPMI_GET_PSU_E_LABEL_DATA 0x1C +#define IPMI_PSU_GET_EEPROM 0x31 +#define IPMI_GET_SENSOR_DATA 0x40 +#define IPMI_CPLD_REG_ACCESS 0x60 +#define IPMI_CMD_GET_FMEA_STATUS 0xA6 + +#define IPMI_PSU_PRESENT 1 +#define IPMI_PSU_ABSENT 2 + +#define FAN_MODEL_NAME_LEN 2+1 +#define FAN_SERIAL_NUM_LEN 10+1 +#define FAN_VENDOR_LEN 6+1 +#define FAN_PART_NUM_LEN 13+1 +#define FAN_HARDWARE_VER_LEN 3+1 +#define FAN_MAX_LEN 32+1 + +#define PMBUS_MFR_MODEL_LEN 21+1 +#define PMBUS_MFR_SERIAL_LEN 18+1 +#define PMBUS_MFR_DATE_LEN 10+1 +#define PMBUS_MFR_ID_LEN 3+1 +#define PMBUS_MFR_REVISION_LEN 3+1 +#define PMBUS_PART_NUM_LEN 8+1 +#define PMBUS_MFR_MAX_LEN 32+1 + +#define POWER_ALARM_CMD 1 + +#define IPMI_REQ_RETRY_TIME_MS 0 +#define IPMI_REQ_MAX_RETRIES -1 +#define IPMI_REQ_PRIORITY 1 + +volatile static unsigned char g_req_netfn = 0; +volatile static unsigned char g_req_cmd = 0; + +static void msg_handler(struct ipmi_recv_msg *msg, void *handler_data) +{ + if (((g_req_netfn + 1) != msg->msg.netfn) || (g_req_cmd != msg->msg.cmd)) + { + ipmi_free_recv_msg(msg); + return; + } + g_bmc_recv_msg = *msg; + g_bmc_recv_msg.msg.data = g_bmc_recv_msg.msg_data; + ipmi_free_recv_msg(msg); + up(&g_ipmi_msg_sem); + return; +} + +static struct ipmi_user_hndl ipmi_hndlrs = { + .ipmi_recv_hndl = msg_handler, +}; + +int ott_ipmi_xfer(ott_ipmi_xfer_info_s * para) +{ + int ret; + int rsp_len = 0; + int timeout = 0; + struct ipmi_system_interface_addr addr; + struct kernel_ipmi_msg msg = + { + .netfn= para->req.netfn, + .cmd = para->req.cmd, + .data_len = para->req.data_len, + .data = para->req.data + }; + + addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; + addr.channel = IPMI_BMC_CHANNEL; + addr.lun = 0; + mutex_lock(&ott_ipmi_mutex); + ret = ipmi_request_settime(g_ipmi_mh_user, (struct ipmi_addr *)&addr, 0, &msg, NULL, IPMI_REQ_PRIORITY, IPMI_REQ_MAX_RETRIES, IPMI_REQ_RETRY_TIME_MS); + if (ret) + { + printk("warning: send request fail, led broken\n"); + mutex_unlock(&ott_ipmi_mutex); + return ret; + } + rsp_len = para->rsp.data_len;// Set length at call ex:ipmi_info.rsp.data_len = 2; + timeout = (timeout <= 0) ? OTT_IPMI_XFER_DEFAULT_TIMEOUT : timeout; + g_req_netfn = msg.netfn; + g_req_cmd = msg.cmd; + ret = down_timeout(&g_ipmi_msg_sem, timeout*HZ);//wait for recv_msg + if(ret) + { + printk("down_interruptible error timeout ret = %d\n", ret); + mutex_unlock(&ott_ipmi_mutex); + return ret; + } + para->rsp.netfn = g_bmc_recv_msg.msg.netfn; + para->rsp.cmd = g_bmc_recv_msg.msg.cmd; + para->rsp.data_len = g_bmc_recv_msg.msg.data_len; + if (para->rsp.data_len > rsp_len ) + { + printk("%s %d,response date error,para->rsp.data_len = %d, rsp_len:%d.\n", __func__,__LINE__,para->rsp.data_len, rsp_len); + mutex_unlock(&ott_ipmi_mutex); + return -1; + } + +#ifdef C11_ANNEX_K + ret = memcpy_s(para->rsp.data, rsp_len, g_bmc_recv_msg.msg.data, para->rsp.data_len); +#else + memcpy(para->rsp.data, g_bmc_recv_msg.msg.data, para->rsp.data_len); +#endif + if(ret) + { + printk("memcpy g_bmc_recv_msg.msg.data to para->rsp.data error\n"); + printk("req:para->req.netfn = %d, para->req.cmd = %d, para->req.data_len = %d, para->req.data = %d\n", para->req.netfn, para->req.cmd, para->req.data_len, para->req.data[0]); + printk("rsp:para->rsp.netfn = %d, para->rsp.cmd = %d, para->rsp.data_len = %d, para->rsp.data = %d\n", para->rsp.netfn, para->rsp.cmd, para->rsp.data_len, para->rsp.data[0]); + mutex_unlock(&ott_ipmi_mutex); + return ret; + } + mutex_unlock(&ott_ipmi_mutex); + return ret; +} + +int drv_get_sensor_temp_input_from_bmc(unsigned int index, long *value) +{ + int ipmi_ret; + char value_get = 0; + ott_ipmi_xfer_info_s ipmi_info = {0}; + unsigned char data[1] = {1}; + unsigned char ack_data[2]; + unsigned int loglevel = loglevel_bmc; + + data[0] = (unsigned char)(index); +#ifdef C11_ANNEX_K + memset_s(ack_data, 2, 0, 2); +#else + memset(ack_data, 0, 2); +#endif + + ipmi_info.req.netfn = IPMI_NETFN; + ipmi_info.req.cmd = IPMI_GET_THERMAL_DATA; + ipmi_info.req.data = data; + ipmi_info.req.data_len = sizeof(data); + + ipmi_info.rsp.data = ack_data; + ipmi_info.rsp.data_len = 2; + ipmi_ret = ott_ipmi_xfer(&ipmi_info); + if(ipmi_ret != 0) + { + SENSOR_DEBUG("%d, get temp info fail! ipmi_ret:0x%x,index:%d.\n", + __LINE__,ipmi_ret,index); + return (unsigned int)ipmi_ret; + } + + if(ack_data[0] != 0) + { + SENSOR_DEBUG("%d, get temp info fail! ipmi_ret:0x%x,ack_data[0]:0x%x,index:%d.\n", + __LINE__,ipmi_ret,ack_data[0],index); + return -EIO; + } + + if((ipmi_info.rsp.netfn != (ipmi_info.req.netfn + 1)) || (ipmi_info.rsp.cmd != ipmi_info.req.cmd)) + { + SENSOR_DEBUG("%d, get temp info fail! ipmi_ret:0x%x,ack_data[0]:0x%x,index:%d.\n", + __LINE__,ipmi_ret,ack_data[0],index); + return -EIO; + } + value_get = (int)(ack_data[1]); + *value = value_get * 1000; + return 0; +} +EXPORT_SYMBOL_GPL(drv_get_sensor_temp_input_from_bmc); + +int drv_get_sensor_vol_input_from_bmc(vr_sensor_node_t node, long *value) +{ + int ipmi_ret; + int value_get = 0; + ott_ipmi_xfer_info_s ipmi_info = {0}; + unsigned char data[3] = {1}; + unsigned char ack_data[5]; + unsigned int loglevel = loglevel_bmc; + + data[0] = node.chip_id; + data[1] = node.channel; + data[2] = node.attr_type; +#ifdef C11_ANNEX_K + memset_s(ack_data, 5, 0, 5); +#else + memset(ack_data, 0, 5); +#endif + ipmi_info.req.netfn = IPMI_NETFN; + ipmi_info.req.cmd = IPMI_GET_SENSOR_DATA; + ipmi_info.req.data = data; + ipmi_info.req.data_len = sizeof(data); + + ipmi_info.rsp.data = ack_data; + ipmi_info.rsp.data_len = 5; + ipmi_ret = ott_ipmi_xfer(&ipmi_info); + if(ipmi_ret != 0) + { + SENSOR_DEBUG("%d, get vol info fail! ipmi_ret:0x%x,chip_id:%d,channel:%d,attr_type:%d.\n", + __LINE__,ipmi_ret,node.chip_id,node.channel,node.attr_type); + return (unsigned int)ipmi_ret; + } + + if(ack_data[0] != 0) + { + SENSOR_DEBUG("%d, get vol info fail! ipmi_ret:0x%x,ack_data[0]:0x%x,chip_id:%d,channel:%d,attr_type:%d.\n", + __LINE__,ipmi_ret,ack_data[0],node.chip_id,node.channel,node.attr_type); + return -EIO; + } + + if((ipmi_info.rsp.netfn != (ipmi_info.req.netfn + 1)) || (ipmi_info.rsp.cmd != ipmi_info.req.cmd)) + { + SENSOR_DEBUG("%d, get vol info fail! ipmi_ret:0x%x,ack_data[0]:0x%x,chip_id:%d,channel:%d,attr_type:%d.\n", + __LINE__,ipmi_ret,ack_data[0],node.chip_id,node.channel,node.attr_type); + return -EIO; + } + value_get = (((int)(ack_data[1])) << 24) + (((int)(ack_data[2])) << 16) + (((int)(ack_data[3])) << 8) + (int)(ack_data[4]); + *value = value_get; + return 0; +} +EXPORT_SYMBOL_GPL(drv_get_sensor_vol_input_from_bmc); + +int drv_get_sensor_curr_input_from_bmc(vr_sensor_node_t node, long *value) +{ + int ipmi_ret; + int value_get = 0; + ott_ipmi_xfer_info_s ipmi_info = {0}; + unsigned char data[3] = {1}; + unsigned char ack_data[5]; + unsigned int loglevel = loglevel_bmc; + + data[0] = node.chip_id; + data[1] = node.channel; + data[2] = node.attr_type; +#ifdef C11_ANNEX_K + memset_s(ack_data, 5, 0, 5); +#else + memset(ack_data, 0, 5); +#endif + ipmi_info.req.netfn = IPMI_NETFN; + ipmi_info.req.cmd = IPMI_GET_SENSOR_DATA; + ipmi_info.req.data = data; + ipmi_info.req.data_len = sizeof(data); + + ipmi_info.rsp.data = ack_data; + ipmi_info.rsp.data_len = 5; + ipmi_ret = ott_ipmi_xfer(&ipmi_info); + if(ipmi_ret != 0) + { + SENSOR_DEBUG("%d, get curr info fail! ipmi_ret:0x%x,chip_id:%d,channel:%d,attr_type:%d.\n", + __LINE__,ipmi_ret,node.chip_id,node.channel,node.attr_type); + return (unsigned int)ipmi_ret; + } + + if(ack_data[0] != 0) + { + SENSOR_DEBUG("%d, get curr info fail! ipmi_ret:0x%x,ack_data[0]:0x%x,chip_id:%d,channel:%d,attr_type:%d.\n", + __LINE__,ipmi_ret,ack_data[0],node.chip_id,node.channel,node.attr_type); + return -EIO; + } + + if((ipmi_info.rsp.netfn != (ipmi_info.req.netfn + 1)) || (ipmi_info.rsp.cmd != ipmi_info.req.cmd)) + { + SENSOR_DEBUG("%d, get temp info fail! ipmi_ret:0x%x,ack_data[0]:0x%x,chip_id:%d,channel:%d,attr_type:%d.\n", + __LINE__,ipmi_ret,ack_data[0],node.chip_id,node.channel,node.attr_type); + return -EIO; + } + value_get = (((int)(ack_data[1])) << 24) + (((int)(ack_data[2])) << 16) + (((int)(ack_data[3])) << 8) + (int)(ack_data[4]); + *value = value_get; + return 0; +} +EXPORT_SYMBOL_GPL(drv_get_sensor_curr_input_from_bmc); + +ssize_t drv_get_wind_from_bmc(unsigned int fan_index, unsigned int *wind) +{ + int ipmi_ret; + ott_ipmi_xfer_info_s ipmi_info = {0}; + unsigned char data[2]; + unsigned char ack_data[2]; + unsigned int loglevel = loglevel_bmc; + data[0]=(unsigned char)(fan_index); + data[1]= fan_direction; +#ifdef C11_ANNEX_K + memset_s(ack_data,2,0,2); +#else + memset(ack_data,0,2); +#endif + ipmi_info.req.netfn = IPMI_NETFN; + ipmi_info.req.cmd = IPMI_FAN_GET_EEPROM; + ipmi_info.req.data = data; + ipmi_info.req.data_len = sizeof(data); + + ipmi_info.rsp.data = ack_data; + ipmi_info.rsp.data_len = 2; + ipmi_ret = ott_ipmi_xfer(&ipmi_info); + + if(ipmi_ret != 0) + { + SENSOR_DEBUG("%d, get fan speed fail! ipmi_ret:0x%x,fan_index:%d.\n", __LINE__,ipmi_ret,fan_index); + return -1; + } + + if(ack_data[0] != 0) + { + SENSOR_DEBUG("%d, get fan speed fail! ipmi_ret:0x%x,ack_data[0]:0x%x,fan_index:%d.\n", __LINE__,ipmi_ret,ack_data[0],fan_index); + return -1; + } + + if((ipmi_info.rsp.netfn != (ipmi_info.req.netfn + 1)) || (ipmi_info.rsp.cmd != ipmi_info.req.cmd)) + { + SENSOR_DEBUG("%d, get fan speed fail! ipmi_ret:0x%x,ack_data[0]:0x%x,fan_index:%d.\n", __LINE__,ipmi_ret,ack_data[0],fan_index); + return -1; + } + + *wind = (unsigned int)(ack_data[1]); + if(*wind > 1) + { + return -1; + } + + return 0; +} +EXPORT_SYMBOL_GPL(drv_get_wind_from_bmc); + +ssize_t drv_get_led_status_from_bmc(unsigned int fan_index, unsigned int *led) +{ + int ipmi_ret; + ott_ipmi_xfer_info_s ipmi_info = {0}; + unsigned char data; + unsigned char ack_data[2]; + unsigned int loglevel = loglevel_bmc; + + data = (unsigned char)fan_index; +#ifdef C11_ANNEX_K + memset_s(ack_data, 2, 0, 2); +#else + memset(ack_data, 0, 2); +#endif + ipmi_info.req.netfn = IPMI_NETFN; + ipmi_info.req.cmd = IPMI_FAN_GET_LED_STATUS; + ipmi_info.req.data = &data; + ipmi_info.req.data_len = sizeof(data); + + ipmi_info.rsp.data = ack_data; + ipmi_info.rsp.data_len = sizeof(ack_data); + ipmi_ret = ott_ipmi_xfer(&ipmi_info); + + if(ipmi_ret != 0) + { + SENSOR_DEBUG("%d, get fan part_number fail! ipmi_ret:0x%x,index:%d.\n", + __LINE__,ipmi_ret,fan_index); + return (unsigned int)ipmi_ret; + } + + if(ack_data[0] != 0) + { + SENSOR_DEBUG("%d, get fan part_number fail! ipmi_ret:0x%x,ack_data[0]:0x%x,index:%d.\n", + __LINE__,ipmi_ret,ack_data[0],fan_index); + return -EIO; + } + + if((ipmi_info.rsp.netfn != (ipmi_info.req.netfn + 1)) || (ipmi_info.rsp.cmd != ipmi_info.req.cmd)) + { + SENSOR_DEBUG("%d, get fan part_number fail! ipmi_ret:0x%x,ack_data[0]:0x%x,index:%d,netfn:0x%x,cmd:0x%x.\n", + __LINE__,ipmi_ret,ack_data[0],fan_index,ipmi_info.rsp.netfn,ipmi_info.req.cmd); + return -EIO; + } + + *led = (unsigned int)(ack_data[1]); + + return 0; +} +EXPORT_SYMBOL_GPL(drv_get_led_status_from_bmc); + +ssize_t drv_set_led_status_from_bmc(unsigned int fan_id, unsigned int led) +{ + int ipmi_ret; + ott_ipmi_xfer_info_s ipmi_info = {0}; + unsigned char data[2] = {1}; + unsigned char ack_data[2]; + unsigned int loglevel = loglevel_bmc; + data[0]=(unsigned char)(fan_id); + data[1]=(unsigned char)(led); +#ifdef C11_ANNEX_K + memset_s(ack_data,2,0,2); +#else + memset(ack_data,0,2); +#endif + ipmi_info.req.netfn = IPMI_NETFN; + ipmi_info.req.cmd = IPMI_FAN_SET_LED_STATUS; + ipmi_info.req.data = data; + ipmi_info.req.data_len = sizeof(data); + + ipmi_info.rsp.data = ack_data; + ipmi_info.rsp.data_len = 2; + ipmi_ret = ott_ipmi_xfer(&ipmi_info); + + if(ipmi_ret != 0) + { + SENSOR_DEBUG("%d, set fan led fail! ipmi_ret:0x%x,fan_id:%d.\n", + __LINE__,ipmi_ret,fan_id); + return (unsigned int)ipmi_ret; + } + + if(ack_data[0] != 0) + { + SENSOR_DEBUG("%d, set fan led fail! ipmi_ret:0x%x,ack_data[0]:0x%x,fan_id:%d.\n", + __LINE__,ipmi_ret,ack_data[0],fan_id); + return -EIO; + } + + if((ipmi_info.rsp.netfn != (ipmi_info.req.netfn + 1)) || (ipmi_info.rsp.cmd != ipmi_info.req.cmd)) + { + SENSOR_DEBUG("%d, set fan led fail! ipmi_ret:0x%x,ack_data[0]:0x%x,fan_id:%d.\n", + __LINE__,ipmi_ret,ack_data[0],fan_id); + return -EIO; + } + + return 0; +} +EXPORT_SYMBOL_GPL(drv_set_led_status_from_bmc); + +ssize_t drv_get_hw_version_from_bmc(unsigned int fan_index, char *buf) +{ + int ipmi_ret; + ott_ipmi_xfer_info_s ipmi_info = {0}; + + unsigned char data[2]; + unsigned char ack_data[FAN_MAX_LEN]; + unsigned char hardware_version[FAN_HARDWARE_VER_LEN]; + unsigned int loglevel = loglevel_bmc; + + data[0] = (unsigned char)fan_index; + data[1] = fan_hardware_version; +#ifdef C11_ANNEX_K + memset_s(ack_data, FAN_MAX_LEN, 0, FAN_MAX_LEN); +#else + memset(ack_data, 0, FAN_MAX_LEN); +#endif + ipmi_info.req.netfn = IPMI_NETFN; + ipmi_info.req.cmd = IPMI_FAN_GET_EEPROM; + ipmi_info.req.data = data; + ipmi_info.req.data_len = sizeof(data); + + ipmi_info.rsp.data = ack_data; + ipmi_info.rsp.data_len = FAN_MAX_LEN; + ipmi_ret = ott_ipmi_xfer(&ipmi_info); + + + if(ipmi_ret != 0) + { + SENSOR_DEBUG("%d, get fan part_number fail! ipmi_ret:0x%x,index:%d.\n", + __LINE__,ipmi_ret,fan_index); + return (unsigned int)ipmi_ret; + } + + if(ack_data[0] != 0) + { + SENSOR_DEBUG("%d, get fan part_number fail! ipmi_ret:0x%x,ack_data[0]:0x%x,index:%d.\n", + __LINE__,ipmi_ret,ack_data[0],fan_index); + return -EIO; + } + + if((ipmi_info.rsp.netfn != (ipmi_info.req.netfn + 1)) || (ipmi_info.rsp.cmd != ipmi_info.req.cmd)) + { + SENSOR_DEBUG("%d, get fan part_number fail! ipmi_ret:0x%x,ack_data[0]:0x%x,index:%d,netfn:0x%x,cmd:0x%x.\n", + __LINE__,ipmi_ret,ack_data[0],fan_index,ipmi_info.rsp.netfn,ipmi_info.req.cmd); + return -EIO; + } +#ifdef C11_ANNEX_K + if(memcpy_s(hardware_version, FAN_HARDWARE_VER_LEN-1, (unsigned char*)&ack_data[1], FAN_HARDWARE_VER_LEN-1) != 0) + { + return -ENOMEM; + } +#else + memcpy(hardware_version, (unsigned char*)&ack_data[1], FAN_HARDWARE_VER_LEN-1); +#endif + + hardware_version[FAN_HARDWARE_VER_LEN-1]='\0'; +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%s\n", hardware_version); +#else + return sprintf(buf, "%s\n", hardware_version); +#endif +} +EXPORT_SYMBOL_GPL(drv_get_hw_version_from_bmc); + +ssize_t drv_get_model_name_from_bmc(unsigned int index, char *buf) + +{ + int ipmi_ret; + ott_ipmi_xfer_info_s ipmi_info = {0}; + unsigned char data[2]; + unsigned char ack_data[FAN_MAX_LEN]; + unsigned char model_name[FAN_MODEL_NAME_LEN]; + unsigned int loglevel = loglevel_bmc; + + data[0] = (unsigned char)index; + data[1] = fan_model_name; +#ifdef C11_ANNEX_K + memset_s(ack_data,FAN_MAX_LEN,0,FAN_MAX_LEN); +#else + memset(ack_data,0,FAN_MAX_LEN); +#endif + ipmi_info.req.netfn = IPMI_NETFN; + ipmi_info.req.cmd = IPMI_FAN_GET_EEPROM; + ipmi_info.req.data = data; + ipmi_info.req.data_len = sizeof(data); + + ipmi_info.rsp.data = ack_data; + ipmi_info.rsp.data_len = FAN_MAX_LEN; + ipmi_ret = ott_ipmi_xfer(&ipmi_info); + + if(ipmi_ret != 0) + { + SENSOR_DEBUG("%d, get fan model_name fail! ipmi_ret:0x%x,index:%d.\n",__LINE__,ipmi_ret,index); + return (unsigned int)ipmi_ret; + } + + if(ack_data[0] != 0) + { + SENSOR_DEBUG("%d, get fan model_name fail! ipmi_ret:0x%x,ack_data[0]:0x%x,index:%d.\n",__LINE__,ipmi_ret,ack_data[0],index); + return -EIO; + } + + if((ipmi_info.rsp.netfn != (ipmi_info.req.netfn + 1)) || (ipmi_info.rsp.cmd != ipmi_info.req.cmd)) + { + SENSOR_DEBUG("%d, get fan model_name fail! ipmi_ret:0x%x,ack_data[0]:0x%x,index:%d,,ipmi_info.rsp.netfn:0x%x,ipmi_info.req.cmd:0x%x.\n",__LINE__,ipmi_ret,ack_data[0],index,ipmi_info.rsp.netfn,ipmi_info.req.cmd); + return -EIO; + } +#ifdef C11_ANNEX_K + if(memcpy_s(model_name,FAN_MODEL_NAME_LEN-1,(unsigned char*)&ack_data[1],FAN_MODEL_NAME_LEN-1) != 0) + { + return -ENOMEM; + } +#else + memcpy(model_name,(unsigned char*)&ack_data[1],FAN_MODEL_NAME_LEN-1); +#endif + + model_name[FAN_MODEL_NAME_LEN-1]='\0'; +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%s\n", model_name); +#else + return sprintf(buf, "%s\n", model_name); +#endif + + +} +EXPORT_SYMBOL_GPL(drv_get_model_name_from_bmc); + +ssize_t drv_get_sn_from_bmc(unsigned int index, char *buf) +{ + int ipmi_ret; + ott_ipmi_xfer_info_s ipmi_info = {0}; + + unsigned char data[2]; + unsigned char ack_data[FAN_MAX_LEN]; + unsigned char serial_number[FAN_SERIAL_NUM_LEN]; + unsigned int loglevel = loglevel_bmc; + + data[0] = (unsigned char)index; + data[1] = fan_serial_number; +#ifdef C11_ANNEX_K + memset_s(ack_data, FAN_MAX_LEN, 0, FAN_MAX_LEN); +#else + memset(ack_data, 0, FAN_MAX_LEN); +#endif + ipmi_info.req.netfn = IPMI_NETFN; + ipmi_info.req.cmd = IPMI_FAN_GET_EEPROM; + ipmi_info.req.data = data; + ipmi_info.req.data_len = sizeof(data); + + ipmi_info.rsp.data = ack_data; + ipmi_info.rsp.data_len = FAN_MAX_LEN; + ipmi_ret = ott_ipmi_xfer(&ipmi_info); + + if(ipmi_ret != 0) + { + SENSOR_DEBUG("%d, get fan serial_number fail! ipmi_ret:0x%x,index:%d.\n",__LINE__,ipmi_ret,index); + return (unsigned int)ipmi_ret; + } + + if(ack_data[0] != 0) + { + SENSOR_DEBUG("%d, get fan serial_number fail! ipmi_ret:0x%x,ack_data[0]:0x%x,index:%d.\n",__LINE__,ipmi_ret,ack_data[0],index); + return -EIO; + } + + if((ipmi_info.rsp.netfn != (ipmi_info.req.netfn + 1)) || (ipmi_info.rsp.cmd != ipmi_info.req.cmd)) + { + SENSOR_DEBUG("%d, get fan serial_number fail! ipmi_ret:0x%x,ack_data[0]:0x%x,index:%d,,ipmi_info.rsp.netfn:0x%x,ipmi_info.req.cmd:0x%x.\n",__LINE__,ipmi_ret,ack_data[0],index,ipmi_info.rsp.netfn,ipmi_info.req.cmd); + return -EIO; + } +#ifdef C11_ANNEX_K + if(memcpy_s(serial_number, FAN_SERIAL_NUM_LEN-1, (unsigned char*)&ack_data[1], FAN_SERIAL_NUM_LEN-1) != 0) + { + return -ENOMEM; + } +#else + memcpy(serial_number, (unsigned char*)&ack_data[1], FAN_SERIAL_NUM_LEN-1); +#endif + + serial_number[FAN_SERIAL_NUM_LEN-1]='\0'; +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%s\n", serial_number); +#else + return sprintf(buf, "%s\n", serial_number); +#endif + +} +EXPORT_SYMBOL_GPL(drv_get_sn_from_bmc); + +ssize_t drv_get_vendor_from_bmc(unsigned int index, char *buf) +{ + int ipmi_ret; + ott_ipmi_xfer_info_s ipmi_info = {0}; + + unsigned char data[2]; + unsigned char ack_data[FAN_MAX_LEN]; + unsigned char vendor[FAN_VENDOR_LEN]; + unsigned int loglevel = loglevel_bmc; + + data[0] = (unsigned char)index; + data[1] = fan_vendor; +#ifdef C11_ANNEX_K + memset_s(ack_data,FAN_MAX_LEN,0,FAN_MAX_LEN); +#else + memset(ack_data,0,FAN_MAX_LEN); +#endif + ipmi_info.req.netfn = IPMI_NETFN; + ipmi_info.req.cmd = IPMI_FAN_GET_EEPROM; + ipmi_info.req.data = data; + ipmi_info.req.data_len = sizeof(data); + + ipmi_info.rsp.data = ack_data; + ipmi_info.rsp.data_len = FAN_MAX_LEN; + ipmi_ret = ott_ipmi_xfer(&ipmi_info); + + if(ipmi_ret != 0) + { + SENSOR_DEBUG("%d, get fan vendor fail! ipmi_ret:0x%x,index:%d.\n",__LINE__,ipmi_ret,index); + return (unsigned int)ipmi_ret; + } + + if(ack_data[0] != 0) + { + SENSOR_DEBUG("%d, get fan vendor fail! ipmi_ret:0x%x,ack_data[0]:0x%x,index:%d.\n",__LINE__,ipmi_ret,ack_data[0],index); + return -EIO; + } + + if((ipmi_info.rsp.netfn != (ipmi_info.req.netfn + 1)) || (ipmi_info.rsp.cmd != ipmi_info.req.cmd)) + { + SENSOR_DEBUG("%d, get fan vendor fail! ipmi_ret:0x%x,ack_data[0]:0x%x,index:%d,,ipmi_info.rsp.netfn:0x%x,ipmi_info.req.cmd:0x%x.\n",__LINE__,ipmi_ret,ack_data[0],index,ipmi_info.rsp.netfn,ipmi_info.req.cmd); + return -EIO; + } + +#ifdef C11_ANNEX_K + if(memcpy_s(vendor,FAN_VENDOR_LEN-1,(unsigned char*)&ack_data[1],FAN_VENDOR_LEN-1) != 0) + { + return -ENOMEM; + } +#else + memcpy(vendor,(unsigned char*)&ack_data[1],FAN_VENDOR_LEN-1); +#endif + + vendor[FAN_VENDOR_LEN-1]='\0'; +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%s\n", vendor); +#else + return sprintf(buf, "%s\n", vendor); +#endif + +} +EXPORT_SYMBOL_GPL(drv_get_vendor_from_bmc); + +ssize_t drv_get_part_number_from_bmc(unsigned int index, char *buf) +{ + int ipmi_ret; + ott_ipmi_xfer_info_s ipmi_info = {0}; + + unsigned char data[2]; + unsigned char ack_data[FAN_MAX_LEN]; + unsigned char part_number[FAN_PART_NUM_LEN]; + unsigned int loglevel = loglevel_bmc; + + data[0] = (unsigned char)index; + data[1] = fan_part_number; +#ifdef C11_ANNEX_K + memset_s(ack_data, FAN_MAX_LEN, 0, FAN_MAX_LEN); +#else + memset(ack_data, 0, FAN_MAX_LEN); +#endif + ipmi_info.req.netfn = IPMI_NETFN; + ipmi_info.req.cmd = IPMI_FAN_GET_EEPROM; + ipmi_info.req.data = data; + ipmi_info.req.data_len = sizeof(data); + + ipmi_info.rsp.data = ack_data; + ipmi_info.rsp.data_len = FAN_MAX_LEN; + ipmi_ret = ott_ipmi_xfer(&ipmi_info); + + + if(ipmi_ret != 0) + { + SENSOR_DEBUG("%d, get fan part_number fail! ipmi_ret:0x%x,index:%d.\n",__LINE__,ipmi_ret,index); + return (unsigned int)ipmi_ret; + } + + if(ack_data[0] != 0) + { + SENSOR_DEBUG("%d, get fan part_number fail! ipmi_ret:0x%x,ack_data[0]:0x%x,index:%d.\n",__LINE__,ipmi_ret,ack_data[0],index); + return -EIO; + } + + if((ipmi_info.rsp.netfn != (ipmi_info.req.netfn + 1)) || (ipmi_info.rsp.cmd != ipmi_info.req.cmd)) + { + SENSOR_DEBUG("%d, get fan part_number fail! ipmi_ret:0x%x,ack_data[0]:0x%x,index:%d,,ipmi_info.rsp.netfn:0x%x,ipmi_info.req.cmd:0x%x.\n",__LINE__,ipmi_ret,ack_data[0],index,ipmi_info.rsp.netfn,ipmi_info.req.cmd); + return -EIO; + } +#ifdef C11_ANNEX_K + if(memcpy_s(part_number, FAN_PART_NUM_LEN-1, (unsigned char*)&ack_data[1], FAN_PART_NUM_LEN-1) != 0) + { + return -ENOMEM; + } +#else + memcpy(part_number, (unsigned char*)&ack_data[1], FAN_PART_NUM_LEN-1); +#endif + + part_number[FAN_PART_NUM_LEN-1]='\0'; +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%s\n", part_number); +#else + return sprintf(buf, "%s\n", part_number); +#endif + +} +EXPORT_SYMBOL_GPL(drv_get_part_number_from_bmc); + +ssize_t drv_get_speed_from_bmc(unsigned int slot_id, unsigned int fan_id, unsigned int *speed) +{ + int ipmi_ret; + ott_ipmi_xfer_info_s ipmi_info = {0}; + unsigned char data[2] = {1}; + unsigned char ack_data[5]; + unsigned int loglevel = loglevel_bmc; + data[0]=(unsigned char)(slot_id); + data[1]=(unsigned char)(fan_id); +#ifdef C11_ANNEX_K + memset_s(ack_data,5,0,5); +#else + memset(ack_data,0,5); +#endif + ipmi_info.req.netfn = IPMI_NETFN; + ipmi_info.req.cmd = IPMI_GET_FAN_SPEED; + ipmi_info.req.data = data; + ipmi_info.req.data_len = sizeof(data); + + ipmi_info.rsp.data = ack_data; + ipmi_info.rsp.data_len = 5; + ipmi_ret = ott_ipmi_xfer(&ipmi_info); + + if(ipmi_ret != 0) + { + SENSOR_DEBUG("%d, get fan speed fail! ipmi_ret:0x%x,slot_id:%d,fan_id:%d.\n", + __LINE__,ipmi_ret,slot_id,fan_id); + return (unsigned int)ipmi_ret; + } + + if(ack_data[0] != 0) + { + SENSOR_DEBUG("%d, get fan speed fail! ipmi_ret:0x%x,ack_data[0]:0x%x,slot_id:%d,fan_id:%d.\n", + __LINE__,ipmi_ret,ack_data[0],slot_id,fan_id); + return -EIO; + } + + if((ipmi_info.rsp.netfn != (ipmi_info.req.netfn + 1)) || (ipmi_info.rsp.cmd != ipmi_info.req.cmd)) + { + SENSOR_DEBUG("%d, get fan speed fail! ipmi_ret:0x%x,ack_data[0]:0x%x,slot_id:%d,fan_id:%d.\n", + __LINE__,ipmi_ret,ack_data[0],slot_id,fan_id); + return -EIO; + } + *speed = (((int)(ack_data[1])) << 24) + (((int)(ack_data[2])) << 16) + (((int)(ack_data[3])) << 8) + (int)(ack_data[4]); + + return 0; +} +EXPORT_SYMBOL_GPL(drv_get_speed_from_bmc); + +ssize_t drv_get_pwm_from_bmc(unsigned int fan_id, int *pwm) +{ + int ipmi_ret; + ott_ipmi_xfer_info_s ipmi_info = {0}; + unsigned char data[1] = {1}; + unsigned char ack_data[2]; + unsigned int loglevel = loglevel_bmc; + data[0]=(unsigned char)(fan_id); +#ifdef C11_ANNEX_K + memset_s(ack_data,2,0,2); +#else + memset(ack_data,0,2); +#endif + ipmi_info.req.netfn = IPMI_NETFN; + ipmi_info.req.cmd = IPMI_GET_FAN_PWM; + ipmi_info.req.data = data; + ipmi_info.req.data_len = sizeof(data); + + ipmi_info.rsp.data = ack_data; + ipmi_info.rsp.data_len = 2; + ipmi_ret = ott_ipmi_xfer(&ipmi_info); + + if(ipmi_ret != 0) + { + SENSOR_DEBUG("%d, get fan speed fail! ipmi_ret:0x%x,fan_id:%d.\n", + __LINE__,ipmi_ret,fan_id); + return (unsigned int)ipmi_ret; + } + + if(ack_data[0] != 0) + { + SENSOR_DEBUG("%d, get fan speed fail! ipmi_ret:0x%x,ack_data[0]:0x%x,fan_id:%d.\n", + __LINE__,ipmi_ret,ack_data[0],fan_id); + return -EIO; + } + + if((ipmi_info.rsp.netfn != (ipmi_info.req.netfn + 1)) || (ipmi_info.rsp.cmd != ipmi_info.req.cmd)) + { + SENSOR_DEBUG("%d, get fan speed fail! ipmi_ret:0x%x,ack_data[0]:0x%x,fan_id:%d.\n", + __LINE__,ipmi_ret,ack_data[0],fan_id); + return -EIO; + } + *pwm = (int)(ack_data[1]); + + return 0; +} +EXPORT_SYMBOL_GPL(drv_get_pwm_from_bmc); + +ssize_t drv_set_pwm_from_bmc(unsigned int fan_id, int pwm) +{ + int ipmi_ret; + ott_ipmi_xfer_info_s ipmi_info = {0}; + unsigned char data[2] = {1}; + unsigned char ack_data[2]; + unsigned int loglevel = loglevel_bmc; + data[0]=(unsigned char)(fan_id); + data[1]=(unsigned char)(pwm); +#ifdef C11_ANNEX_K + memset_s(ack_data,2,0,2); +#else + memset(ack_data,0,2); +#endif + ipmi_info.req.netfn = IPMI_NETFN; + ipmi_info.req.cmd = IPMI_SET_FAN_PWM; + ipmi_info.req.data = data; + ipmi_info.req.data_len = sizeof(data); + + ipmi_info.rsp.data = ack_data; + ipmi_info.rsp.data_len = 2; + ipmi_ret = ott_ipmi_xfer(&ipmi_info); + + if(ipmi_ret != 0) + { + SENSOR_DEBUG("%d, get fan speed fail! ipmi_ret:0x%x,fan_id:%d.\n", + __LINE__,ipmi_ret,fan_id); + return (unsigned int)ipmi_ret; + } + + if(ack_data[0] != 0) + { + SENSOR_DEBUG("%d, get fan speed fail! ipmi_ret:0x%x,ack_data[0]:0x%x,fan_id:%d.\n", + __LINE__,ipmi_ret,ack_data[0],fan_id); + return -EIO; + } + + if((ipmi_info.rsp.netfn != (ipmi_info.req.netfn + 1)) || (ipmi_info.rsp.cmd != ipmi_info.req.cmd)) + { + SENSOR_DEBUG("%d, get fan speed fail! ipmi_ret:0x%x,ack_data[0]:0x%x,fan_id:%d.\n", + __LINE__,ipmi_ret,ack_data[0],fan_id); + return -EIO; + } + + return 0; +} +EXPORT_SYMBOL_GPL(drv_set_pwm_from_bmc); + +int drv_get_mfr_info_from_bmc(unsigned int psu_index, u8 pmbus_command, char *buf) +{ + int ipmi_ret; + int value = 0; + int psu_present = 0; + int psu_alarm = 0; + int ack_data_len = 5; + int ack_data_handle = 0; + + ott_ipmi_xfer_info_s ipmi_info = {0}; + unsigned char data[2] = {}; + unsigned char ack_data[PMBUS_MFR_MAX_LEN]; + unsigned int loglevel = loglevel_bmc; + data[0]=(unsigned char)(psu_index); + data[1]=(unsigned char)(pmbus_command); +#ifdef C11_ANNEX_K + memset_s(ack_data,PMBUS_MFR_MAX_LEN,0,PMBUS_MFR_MAX_LEN); +#else + memset(ack_data,0,PMBUS_MFR_MAX_LEN); +#endif + ipmi_info.req.netfn = IPMI_NETFN; + ipmi_info.req.data = data; + ipmi_info.req.data_len = sizeof(data); + + ipmi_info.rsp.data = ack_data; + ipmi_info.rsp.data_len = PMBUS_MFR_MAX_LEN; + + switch(pmbus_command) + { + case PMBUS_STATUS_WORD: + case PMBUS_IIN_OC_WARN_LIMIT: + case PMBUS_VIN_OV_WARN_LIMIT: + // PMBUS_IIN_OC_WARN_LIMIT and PMBUS_VIN_OV_WARN_LIMIT are hard code since PSU not support + // But we still need to check PSU status. Return NA if get failed + ack_data_handle = ACK_DATA_RESP_BITMAP; + break; + case PMBUS_STATUS_INPUT: + ack_data_handle = ACK_DATA_RESP_PRESENT; + break; + case PMBUS_POUT_MAX: + ipmi_info.req.cmd = IPMI_GET_PSU_ATTRIBUTE_DATA; + break; + case PMBUS_READ_TEMPERATURE_1: + ipmi_info.req.cmd = IPMI_GET_PSU_ATTRIBUTE_DATA; + ack_data_handle = ACK_DATA_RESP_FLOAT; + break; + case PMBUS_READ_VIN: + ipmi_info.req.cmd = IPMI_GET_PSU_ATTRIBUTE_DATA; + ack_data_handle = ACK_DATA_RESP_FLOAT; + break; + case PMBUS_READ_VOUT: + ipmi_info.req.cmd = IPMI_GET_PSU_ATTRIBUTE_DATA; + ack_data_handle = ACK_DATA_RESP_FLOAT; + break; + case PMBUS_READ_IIN: + ipmi_info.req.cmd = IPMI_GET_PSU_ATTRIBUTE_DATA; + ack_data_handle = ACK_DATA_RESP_FLOAT; + break; + case PMBUS_READ_IOUT: + ipmi_info.req.cmd = IPMI_GET_PSU_ATTRIBUTE_DATA; + ack_data_handle = ACK_DATA_RESP_FLOAT; + break; + case PMBUS_READ_PIN: + ipmi_info.req.cmd = IPMI_GET_PSU_ATTRIBUTE_DATA; + ack_data_handle = ACK_DATA_RESP_FLOAT_DIV; + break; + case PMBUS_READ_POUT: + ipmi_info.req.cmd = IPMI_GET_PSU_ATTRIBUTE_DATA; + ack_data_handle = ACK_DATA_RESP_FLOAT_DIV; + break; + case PMBUS_READ_FAN_SPEED_1: + ipmi_info.req.cmd = IPMI_GET_PSU_ATTRIBUTE_DATA; + break; + case PMBUS_MFR_SERIAL: + ack_data_len = PMBUS_MFR_SERIAL_LEN + 1; + ipmi_info.req.cmd = IPMI_PSU_GET_EEPROM; + ack_data_handle = ACK_DATA_RESP_CHAR; + break; + case PMBUS_MFR_ID: + ack_data_len = PMBUS_MFR_ID_LEN + 1; + ipmi_info.req.cmd = IPMI_PSU_GET_EEPROM; + ack_data_handle = ACK_DATA_RESP_CHAR; + break; + case PMBUS_MFR_MODEL: + ack_data_len = PMBUS_MFR_MODEL_LEN + 1; + ipmi_info.req.cmd = IPMI_PSU_GET_EEPROM; + ack_data_handle = ACK_DATA_RESP_CHAR; + break; + case PMBUS_MFR_REVISION: + ack_data_len = PMBUS_MFR_REVISION_LEN + 1; + ipmi_info.req.cmd = IPMI_PSU_GET_EEPROM; + ack_data_handle = ACK_DATA_RESP_CHAR; + break; + case PMBUS_MFR_DATE: + ack_data_len = PMBUS_MFR_DATE_LEN + 1; + ipmi_info.req.cmd = IPMI_PSU_GET_EEPROM; + ack_data_handle = ACK_DATA_RESP_CHAR; + break; + case PMBUS_PART_NUM: + ack_data_len = PMBUS_PART_NUM_LEN + 1; + ipmi_info.req.cmd = IPMI_PSU_GET_EEPROM; + ack_data_handle = ACK_DATA_RESP_CHAR; + break; + + default: +#ifdef C11_ANNEX_K + if(sprintf_s(buf, PAGE_SIZE, "%x", pmbus_command) < 0) +#else + if(sprintf(buf, "%x", pmbus_command) < 0) +#endif + { + return -ENOMEM; + } + return 0; + break; + } + + ipmi_ret = ott_ipmi_xfer(&ipmi_info); + + + if(ipmi_ret != 0) + { + SENSOR_DEBUG("%d, get fan info fail! ipmi_ret:0x%x,psu_index:%d,pmbus_command:%x.\n", + __LINE__,ipmi_ret,psu_index,pmbus_command); + return (unsigned int)ipmi_ret; + } + + if(ack_data[0] != 0) + { + SENSOR_DEBUG("%d, get fan info fail! ipmi_ret:0x%x,ack_data[0]:0x%x,psu_index:%d,pmbus_command:%x.\n", + __LINE__,ipmi_ret,ack_data[0],psu_index,pmbus_command); + return -EIO; + } + + if((ipmi_info.rsp.netfn != (ipmi_info.req.netfn + 1)) || (ipmi_info.rsp.cmd != ipmi_info.req.cmd)) + { + SENSOR_DEBUG("%d, get fan info fail! ipmi_ret:0x%x,ack_data[0]:0x%x,psu_index:%d,pmbus_command:%x.\n", + __LINE__,ipmi_ret,ack_data[0],psu_index,pmbus_command); + return -EIO; + } + + if(ack_data_handle == ACK_DATA_RESP_CHAR) + { +#ifdef C11_ANNEX_K + if(memcpy_s(buf,PAGE_SIZE,ack_data+1,ack_data_len) != 0) + { + return -ENOMEM; + } +#else + memcpy(buf,ack_data+1,ack_data_len); +#endif + return 0; + } + else if(ack_data_handle == ACK_DATA_RESP_INT) + { + char value[ack_data_len]; + int i = 0; + for(i = 1 ; i < ack_data_len ; i++) + value[i]= (int)(ack_data[i]); +#ifdef C11_ANNEX_K + if(memcpy_s(buf,PAGE_SIZE,&value,sizeof(value)) != 0) + { + return -ENOMEM; + } +#else + memcpy(buf,&value,sizeof(value)); +#endif + return 0; + } + value = (((int)(ack_data[1])) << 24) + (((int)(ack_data[2])) << 16) + (((int)(ack_data[3])) << 8) + (int)(ack_data[4]); + if(ack_data_handle == ACK_DATA_RESP_FLOAT) + { +#ifdef C11_ANNEX_K + if(sprintf_s(buf, PAGE_SIZE, "%d", value) < 0) +#else + if(sprintf(buf, "%d", value) < 0) +#endif + { + return -ENOMEM; + } + return 0; + } + else if(ack_data_handle == ACK_DATA_RESP_FLOAT_DIV) + { + value = value; +#ifdef C11_ANNEX_K + if(sprintf_s(buf, PAGE_SIZE, "%d", value) < 0) +#else + if(sprintf(buf, "%d", value) < 0) +#endif + { + return -ENOMEM; + } + return 0; + } + else if(ack_data_handle == ACK_DATA_RESP_PRESENT) + { + if(value) + { + psu_present = IPMI_PSU_ABSENT; + } + else + { + psu_present = IPMI_PSU_PRESENT; + } +#ifdef C11_ANNEX_K + if(sprintf_s(buf, PAGE_SIZE, "%d", psu_present) < 0) +#else + if(sprintf(buf, "%d", psu_present) < 0) +#endif + { + return -ENOMEM; + } + return 0; + } + else if(ack_data_handle == ACK_DATA_RESP_BITMAP) + { + psu_alarm = value & 0x1fff; +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%d", psu_alarm); +#else + return sprintf(buf, "%d", psu_alarm); +#endif + } +#ifdef C11_ANNEX_K + if(sprintf_s(buf, PAGE_SIZE, "%d", value) < 0) +#else + if(sprintf(buf, "%d", value) < 0) +#endif + { + return -ENOMEM; + } + + return 0; +} +EXPORT_SYMBOL_GPL(drv_get_mfr_info_from_bmc); + +ssize_t drv_fmea_get_work_status_from_bmc(unsigned int index, char *buf, char *plt) +{ + int ipmi_ret; + ott_ipmi_xfer_info_s ipmi_info = {0}; + unsigned char data[] = {(unsigned char)index, 1}; + unsigned char ack_data[5]; + unsigned char work_status[5]; + unsigned int loglevel = loglevel_bmc; +#ifdef C11_ANNEX_K + memset_s(ack_data,5,0,5); +#else + memset(ack_data,0,5); +#endif + ipmi_info.req.netfn = IPMI_NETFN; + ipmi_info.req.cmd = IPMI_CMD_GET_FMEA_STATUS; + ipmi_info.req.data = data; + ipmi_info.req.data_len = sizeof(data); + + ipmi_info.rsp.data = ack_data; + ipmi_info.rsp.data_len = 5; + ipmi_ret = ott_ipmi_xfer(&ipmi_info); + + if(ipmi_ret != 0) + { + SENSOR_DEBUG("%s:%d fail! ipmi_ret:0x%x,index:%d.\n",__func__,__LINE__,ipmi_ret,index); + return (unsigned int)ipmi_ret; + } + + if(ack_data[0] != 0) + { + SENSOR_DEBUG("%s:%d fail! ipmi_ret:0x%x,ack_data[0]:0x%x,index:%d.\n",__func__,__LINE__,ipmi_ret,ack_data[0],index); + return -EIO; + } + + if((ipmi_info.rsp.netfn != (ipmi_info.req.netfn + 1)) || (ipmi_info.rsp.cmd != ipmi_info.req.cmd)) + { + SENSOR_DEBUG("%s:%d fail! ipmi_ret:0x%x,ack_data[0]:0x%x,index:%d,,ipmi_info.rsp.netfn:0x%x,ipmi_info.req.cmd:0x%x.\n",__func__,__LINE__,ipmi_ret,ack_data[0],index,ipmi_info.rsp.netfn,ipmi_info.req.cmd); + return -EIO; + } +#ifdef C11_ANNEX_K + if(memcpy_s(work_status,4,(unsigned char*)&ack_data[1],4) != 0) + { + return -ENOMEM; + } +#else + memcpy(work_status,(unsigned char*)&ack_data[1],4); +#endif + + work_status[4]='\0'; +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%s\n", work_status); +#else + return sprintf(buf, "%s\n", work_status); +#endif + +} +EXPORT_SYMBOL_GPL(drv_fmea_get_work_status_from_bmc); + +ssize_t drv_fmea_get_current_status_from_bmc(unsigned int index, char *buf, char *plt) +{ + int ipmi_ret; + ott_ipmi_xfer_info_s ipmi_info = {0}; + unsigned char data[] = {(unsigned char)index, 1}; + unsigned char ack_data[5]; + unsigned char current_status[5]; + unsigned int loglevel = loglevel_bmc; +#ifdef C11_ANNEX_K + memset_s(ack_data,5,0,5); +#else + memset(ack_data,0,5); +#endif + ipmi_info.req.netfn = IPMI_NETFN; + ipmi_info.req.cmd = IPMI_CMD_GET_FMEA_STATUS; + ipmi_info.req.data = data; + ipmi_info.req.data_len = sizeof(data); + + ipmi_info.rsp.data = ack_data; + ipmi_info.rsp.data_len = 5; + ipmi_ret = ott_ipmi_xfer(&ipmi_info); + + if(ipmi_ret != 0) + { + SENSOR_DEBUG("%s:%d fail! ipmi_ret:0x%x,index:%d.\n",__func__,__LINE__,ipmi_ret,index); + return (unsigned int)ipmi_ret; + } + + if(ack_data[0] != 0) + { + SENSOR_DEBUG("%s:%d fail! ipmi_ret:0x%x,ack_data[0]:0x%x,index:%d.\n",__func__,__LINE__,ipmi_ret,ack_data[0],index); + return -EIO; + } + + if((ipmi_info.rsp.netfn != (ipmi_info.req.netfn + 1)) || (ipmi_info.rsp.cmd != ipmi_info.req.cmd)) + { + SENSOR_DEBUG("%s:%d fail! ipmi_ret:0x%x,ack_data[0]:0x%x,index:%d,,ipmi_info.rsp.netfn:0x%x,ipmi_info.req.cmd:0x%x.\n",__func__,__LINE__,ipmi_ret,ack_data[0],index,ipmi_info.rsp.netfn,ipmi_info.req.cmd); + return -EIO; + } +#ifdef C11_ANNEX_K + if(memcpy_s(current_status,4,(unsigned char*)&ack_data[1],4) != 0) + { + return -ENOMEM; + } +#else + memcpy(current_status,(unsigned char*)&ack_data[1],4); +#endif + + current_status[4]='\0'; +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%s\n", current_status); +#else + return sprintf(buf, "%s\n", current_status); +#endif + +} +EXPORT_SYMBOL_GPL(drv_fmea_get_current_status_from_bmc); + +ssize_t drv_fmea_get_pmbus_status_from_bmc(unsigned int index, char *buf, char *plt) +{ + int ipmi_ret; + ott_ipmi_xfer_info_s ipmi_info = {0}; + unsigned char data[] = {(unsigned char)index, 1}; + unsigned char ack_data[5]; + unsigned char pmbus_status[5]; + unsigned int loglevel = loglevel_bmc; +#ifdef C11_ANNEX_K + memset_s(ack_data,5,0,5); +#else + memset(ack_data,0,5); +#endif + ipmi_info.req.netfn = IPMI_NETFN; + ipmi_info.req.cmd = IPMI_CMD_GET_FMEA_STATUS; + ipmi_info.req.data = data; + ipmi_info.req.data_len = sizeof(data); + + ipmi_info.rsp.data = ack_data; + ipmi_info.rsp.data_len = 5; + ipmi_ret = ott_ipmi_xfer(&ipmi_info); + + if(ipmi_ret != 0) + { + SENSOR_DEBUG("%s:%d fail! ipmi_ret:0x%x,index:%d.\n",__func__,__LINE__,ipmi_ret,index); + return (unsigned int)ipmi_ret; + } + + if(ack_data[0] != 0) + { + SENSOR_DEBUG("%s:%d fail! ipmi_ret:0x%x,ack_data[0]:0x%x,index:%d.\n",__func__,__LINE__,ipmi_ret,ack_data[0],index); + return -EIO; + } + + if((ipmi_info.rsp.netfn != (ipmi_info.req.netfn + 1)) || (ipmi_info.rsp.cmd != ipmi_info.req.cmd)) + { + SENSOR_DEBUG("%s:%d fail! ipmi_ret:0x%x,ack_data[0]:0x%x,index:%d,,ipmi_info.rsp.netfn:0x%x,ipmi_info.req.cmd:0x%x.\n",__func__,__LINE__,ipmi_ret,ack_data[0],index,ipmi_info.rsp.netfn,ipmi_info.req.cmd); + return -EIO; + } +#ifdef C11_ANNEX_K + if(memcpy_s(pmbus_status,4,(unsigned char*)&ack_data[1],4) != 0) + { + return -ENOMEM; + } +#else + memcpy(pmbus_status,(unsigned char*)&ack_data[1],4); +#endif + + pmbus_status[4]='\0'; +#ifdef C11_ANNEX_K + return sprintf_s(buf, PAGE_SIZE, "%s\n", pmbus_status); +#else + return sprintf(buf, "%s\n", pmbus_status); +#endif + +} +EXPORT_SYMBOL_GPL(drv_fmea_get_pmbus_status_from_bmc); + +ssize_t drv_fmea_get_mfr_id_from_bmc(unsigned int index, int *value) +{ + int ipmi_ret; + ott_ipmi_xfer_info_s ipmi_info = {0}; + unsigned char ack_data[3]; + unsigned char data[] = {(unsigned char)index, 4}; + unsigned int loglevel = loglevel_bmc; +#ifdef C11_ANNEX_K + memset_s(ack_data,3,0,3); +#else + memset(ack_data,0,3); +#endif + ipmi_info.req.netfn = IPMI_NETFN; + ipmi_info.req.cmd = IPMI_CMD_GET_FMEA_STATUS; + ipmi_info.req.data = data; + ipmi_info.req.data_len = sizeof(data); + + ipmi_info.rsp.data = ack_data; + ipmi_info.rsp.data_len = 3; + ipmi_ret = ott_ipmi_xfer(&ipmi_info); + + if(ipmi_ret != 0) + { + SENSOR_DEBUG("Fail! ipmi_ret:0x%x,index:%d.\n",ipmi_ret,index); + return (unsigned int)ipmi_ret; + } + + if(ack_data[0] != 0) + { + SENSOR_DEBUG("Fail! ipmi_ret:0x%x,ack_data[0]:0x%x,index:%d.\n",ipmi_ret,ack_data[0],index); + return -EIO; + } + + if((ipmi_info.rsp.netfn != (ipmi_info.req.netfn + 1)) || (ipmi_info.rsp.cmd != ipmi_info.req.cmd)) + { + SENSOR_DEBUG("Fail! ipmi_ret:0x%x,ack_data[0]:0x%x,index:%d,,ipmi_info.rsp.netfn:0x%x,ipmi_info.req.cmd:0x%x.\n",ipmi_ret,ack_data[0],index,ipmi_info.rsp.netfn,ipmi_info.req.cmd); + return -EIO; + } +#ifdef C11_ANNEX_K + if(memcpy_s((char *)value,2,(unsigned char*)&ack_data[1],2) != 0) + { + return -ENOMEM; + } +#else + memcpy((char *)value,(unsigned char*)&ack_data[1],2); +#endif + + + return 0; +} +EXPORT_SYMBOL_GPL(drv_fmea_get_mfr_id_from_bmc); + +int drv_cpld_write_from_bmc(unsigned char index, unsigned char reg, unsigned char value) +{ + int ipmi_ret; + ott_ipmi_xfer_info_s ipmi_info = {0}; + unsigned char data[4]; + unsigned char ack_data[2]; + unsigned int loglevel = loglevel_bmc; + + if(index != FAN_CPLD) + { + return -1; + } + + data[0] = index; + data[1] = CPLD_WRITE; + data[2] = reg; + data[3] = value; +#ifdef C11_ANNEX_K + memset_s(ack_data, 2, 0, 2); +#else + memset(ack_data, 0, 2); +#endif + ipmi_info.req.netfn = IPMI_NETFN; + ipmi_info.req.cmd = IPMI_CPLD_REG_ACCESS; + ipmi_info.req.data = data; + ipmi_info.req.data_len = sizeof(data); + + ipmi_info.rsp.data = ack_data; + ipmi_info.rsp.data_len = 2; + ipmi_ret = ott_ipmi_xfer(&ipmi_info); + + if(ipmi_ret != 0) + { + SENSOR_DEBUG("%d, write cpld fail! ipmi_ret:0x%x,cpld_id:%d.\n", + __LINE__,ipmi_ret,index); + return (unsigned int)ipmi_ret; + } + + if(ack_data[0] != 0) + { + SENSOR_DEBUG("%d, write cpld fail! ipmi_ret:0x%x,ack_data[0]:0x%x,cpld_id:%d.\n", + __LINE__,ipmi_ret,ack_data[0],index); + return -EIO; + } + + if((ipmi_info.rsp.netfn != (ipmi_info.req.netfn + 1)) || (ipmi_info.rsp.cmd != ipmi_info.req.cmd)) + { + SENSOR_DEBUG("%d, write cpld fail! ipmi_ret:0x%x,ack_data[0]:0x%x,cpld_id:%d.\n", + __LINE__,ipmi_ret,ack_data[0],index); + return -EIO; + } + + return 0; +} +EXPORT_SYMBOL_GPL(drv_cpld_write_from_bmc); + +int drv_cpld_read_from_bmc(unsigned char index, unsigned char reg, unsigned char *value) +{ + int ipmi_ret; + ott_ipmi_xfer_info_s ipmi_info = {0}; + unsigned char data[4]; + unsigned char ack_data[2]; + unsigned int loglevel = loglevel_bmc; + + if(index != FAN_CPLD) + { + return -1; + } + + data[0] = index; + data[1] = CPLD_READ; + data[2] = reg; + data[3] = 0xff; +#ifdef C11_ANNEX_K + memset_s(ack_data, 2, 0, 2); +#else + memset(ack_data, 0, 2); +#endif + ipmi_info.req.netfn = IPMI_NETFN; + ipmi_info.req.cmd = IPMI_CPLD_REG_ACCESS; + ipmi_info.req.data = data; + ipmi_info.req.data_len = sizeof(data); + + ipmi_info.rsp.data = ack_data; + ipmi_info.rsp.data_len = 2; + ipmi_ret = ott_ipmi_xfer(&ipmi_info); + + if(ipmi_ret != 0) + { + SENSOR_DEBUG("%d, read cpld fail! ipmi_ret:0x%x,cpld_id:%d.\n", + __LINE__,ipmi_ret,index); + return (unsigned int)ipmi_ret; + } + + if(ack_data[0] != 0) + { + SENSOR_DEBUG("%d, read cpld fail! ipmi_ret:0x%x,ack_data[0]:0x%x,cpld_id:%d.\n", + __LINE__,ipmi_ret,ack_data[0],index); + return -EIO; + } + + if((ipmi_info.rsp.netfn != (ipmi_info.req.netfn + 1)) || (ipmi_info.rsp.cmd != ipmi_info.req.cmd)) + { + SENSOR_DEBUG("%d, read cpld fail! ipmi_ret:0x%x,ack_data[0]:0x%x,cpld_id:%d.\n", + __LINE__,ipmi_ret,ack_data[0],index); + return -EIO; + } + + *value = ack_data[1]; + + return 0; +} +EXPORT_SYMBOL_GPL(drv_cpld_read_from_bmc); + +static int __init sys_impi_init(void) +{ + int ret = 0; + sema_init(&g_ipmi_msg_sem, 0); + ret = ipmi_create_user(IPMI_INTF_NUM, &ipmi_hndlrs, NULL, &g_ipmi_mh_user); + if(ret) + { + printk("error:ipmi_create_user failed!!!\n"); + } + + return 0; +} +static void __exit sys_impi_exit(void) +{ + if(g_ipmi_mh_user != NULL) + ipmi_destroy_user(g_ipmi_mh_user); + return; +} + +MODULE_AUTHOR("Jian Wang "); +MODULE_DESCRIPTION("sys ipmi"); +MODULE_VERSION("0.0.0.1"); +MODULE_LICENSE("GPL"); +module_init(sys_impi_init); +module_exit(sys_impi_exit); diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/sysfs_ipmi.h b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/sysfs_ipmi.h new file mode 100644 index 0000000000..368e64a310 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/modules/builds/src/third_party/sysfs_ipmi.h @@ -0,0 +1,30 @@ +#ifndef SYSFS_IPMI_H +#define SYSFS_IPMI_H + +#include "switch_sensor_driver.h" + +//unsigned int bmc_get_temp(unsigned int index, long *temp_input); +int drv_get_sensor_temp_input_from_bmc(unsigned int index, long *value); +int drv_get_sensor_vol_input_from_bmc(vr_sensor_node_t node, long *value); +int drv_get_sensor_curr_input_from_bmc(vr_sensor_node_t node, long *value); +ssize_t drv_get_wind_from_bmc(unsigned int fan_index, unsigned int *wind); +ssize_t drv_get_hw_version_from_bmc(unsigned int fan_index, char *buf); +ssize_t drv_get_model_name_from_bmc(unsigned int fan_index, char *buf); +ssize_t drv_get_sn_from_bmc(unsigned int fan_index, char *buf); +ssize_t drv_get_vendor_from_bmc(unsigned int fan_index, char *buf); +ssize_t drv_get_part_number_from_bmc(unsigned int fan_index, char *buf); +ssize_t drv_get_speed_from_bmc(unsigned int slot_id, unsigned int fan_id, unsigned int *speed); +ssize_t drv_get_pwm_from_bmc(unsigned int fan_id, int *pwm); +ssize_t drv_set_pwm_from_bmc(unsigned int fan_id, int pwm); +ssize_t drv_get_led_status_from_bmc(unsigned int fan_index, unsigned int *led); +ssize_t drv_set_led_status_from_bmc(unsigned int fan_index, unsigned int led); +int drv_get_mfr_info_from_bmc(unsigned int psu_index, u8 pmbus_command, char *buf); +bool ipmi_bmc_is_ok(void); +ssize_t drv_fmea_get_work_status_from_bmc(unsigned int index, char *buf, char *plt); +ssize_t drv_fmea_get_current_status_from_bmc(unsigned int index, char *buf, char *plt); +ssize_t drv_fmea_get_pmbus_status_from_bmc(unsigned int index, char *buf, char *plt); +ssize_t drv_fmea_get_mfr_id_from_bmc(unsigned int index, int *value); +int drv_cpld_write_from_bmc(unsigned char index, unsigned char reg, unsigned char value); +int drv_cpld_read_from_bmc(unsigned char index, unsigned char reg, unsigned char *value); + +#endif /*SYSFS_IPMI_H*/ diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/onlp/Makefile b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/Makefile new file mode 100644 index 0000000000..502e772a7b --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/Makefile @@ -0,0 +1 @@ +include $(ONL)/make/pkg.mk diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/onlp/PKG.yml b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/PKG.yml new file mode 100644 index 0000000000..fb21f507df --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/PKG.yml @@ -0,0 +1 @@ +!include $ONL_TEMPLATES/onlp-platform-revision.yml PLATFORM=x86-64-accton-dcg8510-32d ARCH=amd64 TOOLCHAIN=x86_64-linux-gnu REVISION=r0 diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/Makefile b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/Makefile new file mode 100644 index 0000000000..e7437cb23a --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/Makefile @@ -0,0 +1,2 @@ +FILTER=src +include $(ONL)/make/subdirs.mk diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/lib/Makefile b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/lib/Makefile new file mode 100644 index 0000000000..6adaffcd06 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/lib/Makefile @@ -0,0 +1,2 @@ +PLATFORM := x86-64-accton-dcg8510-32d +include $(ONL)/packages/base/any/onlp/builds/platform/libonlp-platform.mk diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/onlpdump/Makefile b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/onlpdump/Makefile new file mode 100644 index 0000000000..9a4bf483d3 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/onlpdump/Makefile @@ -0,0 +1,2 @@ +PLATFORM := x86-64-accton-dcg8510-32d +include $(ONL)/packages/base/any/onlp/builds/platform/onlps.mk diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/.module b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/.module new file mode 100644 index 0000000000..5579ca826f --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/.module @@ -0,0 +1 @@ +name: x86_64_accton_dcg8510_32d diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/Makefile b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/Makefile new file mode 100644 index 0000000000..4bf79eed33 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/Makefile @@ -0,0 +1,9 @@ +############################################################################### +# +# +# +############################################################################### +include $(ONL)/make/config.mk +MODULE := x86_64_accton_dcg8510_32d +AUTOMODULE := x86_64_accton_dcg8510_32d +include $(BUILDER)/definemodule.mk diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/README b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/README new file mode 100644 index 0000000000..de342344fa --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/README @@ -0,0 +1,5 @@ +############################################################################### +# +# x86_64_accton_dcg8510_32d README +# +############################################################################### diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/auto/make.mk b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/auto/make.mk new file mode 100644 index 0000000000..dbcf9ce18d --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/auto/make.mk @@ -0,0 +1,8 @@ +############################################################################### +# +# x86_64_accton_dcg8510_32d Autogeneration +# +############################################################################### +x86_64_accton_dcg8510_32d_AUTO_DEFS := module/auto/x86_64_accton_dcg8510_32d.yml +x86_64_accton_dcg8510_32d_AUTO_DIRS := module/inc/x86_64_accton_dcg8510_32d module/src +include $(BUILDER)/auto.mk diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/auto/x86_64_accton_dcg8510_32d.yml b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/auto/x86_64_accton_dcg8510_32d.yml new file mode 100644 index 0000000000..9119fb04e6 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/auto/x86_64_accton_dcg8510_32d.yml @@ -0,0 +1,50 @@ +############################################################################### +# +# X86_64_ACCTON_DCG8510_32D Autogeneration Definitions. +# +############################################################################### + +cdefs: &cdefs +- X86_64_ACCTON_DCG8510_32D_CONFIG_INCLUDE_LOGGING: + doc: "Include or exclude logging." + default: 1 +- X86_64_ACCTON_DCG8510_32D_CONFIG_LOG_OPTIONS_DEFAULT: + doc: "Default enabled log options." + default: AIM_LOG_OPTIONS_DEFAULT +- X86_64_ACCTON_DCG8510_32D_CONFIG_LOG_BITS_DEFAULT: + doc: "Default enabled log bits." + default: AIM_LOG_BITS_DEFAULT +- X86_64_ACCTON_DCG8510_32D_CONFIG_LOG_CUSTOM_BITS_DEFAULT: + doc: "Default enabled custom log bits." + default: 0 +- X86_64_ACCTON_DCG8510_32D_CONFIG_PORTING_STDLIB: + doc: "Default all porting macros to use the C standard libraries." + default: 1 +- X86_64_ACCTON_DCG8510_32D_CONFIG_PORTING_INCLUDE_STDLIB_HEADERS: + doc: "Include standard library headers for stdlib porting macros." + default: X86_64_ACCTON_DCG8510_32D_CONFIG_PORTING_STDLIB +- X86_64_ACCTON_DCG8510_32D_CONFIG_INCLUDE_UCLI: + doc: "Include generic uCli support." + default: 0 +- X86_64_ACCTON_DCG8510_32D_CONFIG_INCLUDE_DEFAULT_FAN_DIRECTION: + doc: "Assume chassis fan direction is the same as the PSU fan direction." + default: 0 + + +definitions: + cdefs: + X86_64_ACCTON_DCG8510_32D_CONFIG_HEADER: + defs: *cdefs + basename: x86_64_accton_dcg8510_32d_config + + portingmacro: + X86_64_ACCTON_DCG8510_32D: + macros: + - malloc + - free + - memset + - memcpy + + - vsnprintf + - snprintf + - strlen diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/inc/x86_64_accton_dcg8510_32d/x86_64_accton_dcg8510_32d.x b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/inc/x86_64_accton_dcg8510_32d/x86_64_accton_dcg8510_32d.x new file mode 100644 index 0000000000..eb1ef33b44 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/inc/x86_64_accton_dcg8510_32d/x86_64_accton_dcg8510_32d.x @@ -0,0 +1,12 @@ +/**************************************************************************//** + * + * + * + *****************************************************************************/ +#include + +/* <--auto.start.xmacro(ALL).define> */ +/* */ + +/* <--auto.start.xenum(ALL).define> */ +/* */ diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/inc/x86_64_accton_dcg8510_32d/x86_64_accton_dcg8510_32d_config.h b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/inc/x86_64_accton_dcg8510_32d/x86_64_accton_dcg8510_32d_config.h new file mode 100644 index 0000000000..3834859a8b --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/inc/x86_64_accton_dcg8510_32d/x86_64_accton_dcg8510_32d_config.h @@ -0,0 +1,137 @@ +/**************************************************************************//** + * + * @file + * @brief x86_64_accton_dcg8510_32d Configuration Header + * + * @addtogroup x86_64_accton_dcg8510_32d-config + * @{ + * + *****************************************************************************/ +#ifndef __X86_64_ACCTON_DCG8510_32D_CONFIG_H__ +#define __X86_64_ACCTON_DCG8510_32D_CONFIG_H__ + +#ifdef GLOBAL_INCLUDE_CUSTOM_CONFIG +#include +#endif +#ifdef X86_64_ACCTON_DCG8510_32D_INCLUDE_CUSTOM_CONFIG +#include +#endif + +/* */ +#include +/** + * X86_64_ACCTON_DCG8510_32D_CONFIG_INCLUDE_LOGGING + * + * Include or exclude logging. */ + + +#ifndef X86_64_ACCTON_DCG8510_32D_CONFIG_INCLUDE_LOGGING +#define X86_64_ACCTON_DCG8510_32D_CONFIG_INCLUDE_LOGGING 1 +#endif + +/** + * X86_64_ACCTON_DCG8510_32D_CONFIG_LOG_OPTIONS_DEFAULT + * + * Default enabled log options. */ + + +#ifndef X86_64_ACCTON_DCG8510_32D_CONFIG_LOG_OPTIONS_DEFAULT +#define X86_64_ACCTON_DCG8510_32D_CONFIG_LOG_OPTIONS_DEFAULT AIM_LOG_OPTIONS_DEFAULT +#endif + +/** + * X86_64_ACCTON_DCG8510_32D_CONFIG_LOG_BITS_DEFAULT + * + * Default enabled log bits. */ + + +#ifndef X86_64_ACCTON_DCG8510_32D_CONFIG_LOG_BITS_DEFAULT +#define X86_64_ACCTON_DCG8510_32D_CONFIG_LOG_BITS_DEFAULT AIM_LOG_BITS_DEFAULT +#endif + +/** + * X86_64_ACCTON_DCG8510_32D_CONFIG_LOG_CUSTOM_BITS_DEFAULT + * + * Default enabled custom log bits. */ + + +#ifndef X86_64_ACCTON_DCG8510_32D_CONFIG_LOG_CUSTOM_BITS_DEFAULT +#define X86_64_ACCTON_DCG8510_32D_CONFIG_LOG_CUSTOM_BITS_DEFAULT 0 +#endif + +/** + * X86_64_ACCTON_DCG8510_32D_CONFIG_PORTING_STDLIB + * + * Default all porting macros to use the C standard libraries. */ + + +#ifndef X86_64_ACCTON_DCG8510_32D_CONFIG_PORTING_STDLIB +#define X86_64_ACCTON_DCG8510_32D_CONFIG_PORTING_STDLIB 1 +#endif + +/** + * X86_64_ACCTON_DCG8510_32D_CONFIG_PORTING_INCLUDE_STDLIB_HEADERS + * + * Include standard library headers for stdlib porting macros. */ + + +#ifndef X86_64_ACCTON_DCG8510_32D_CONFIG_PORTING_INCLUDE_STDLIB_HEADERS +#define X86_64_ACCTON_DCG8510_32D_CONFIG_PORTING_INCLUDE_STDLIB_HEADERS X86_64_ACCTON_DCG8510_32D_CONFIG_PORTING_STDLIB +#endif + +/** + * X86_64_ACCTON_DCG8510_32D_CONFIG_INCLUDE_UCLI + * + * Include generic uCli support. */ + + +#ifndef X86_64_ACCTON_DCG8510_32D_CONFIG_INCLUDE_UCLI +#define X86_64_ACCTON_DCG8510_32D_CONFIG_INCLUDE_UCLI 0 +#endif + +/** + * X86_64_ACCTON_DCG8510_32D_CONFIG_INCLUDE_DEFAULT_FAN_DIRECTION + * + * Assume chassis fan direction is the same as the PSU fan direction. */ + + +#ifndef X86_64_ACCTON_DCG8510_32D_CONFIG_INCLUDE_DEFAULT_FAN_DIRECTION +#define X86_64_ACCTON_DCG8510_32D_CONFIG_INCLUDE_DEFAULT_FAN_DIRECTION 0 +#endif + + + +/** + * All compile time options can be queried or displayed + */ + +/** Configuration settings structure. */ +typedef struct x86_64_accton_dcg8510_32d_config_settings_s { + /** name */ + const char* name; + /** value */ + const char* value; +} x86_64_accton_dcg8510_32d_config_settings_t; + +/** Configuration settings table. */ +/** x86_64_accton_dcg8510_32d_config_settings table. */ +extern x86_64_accton_dcg8510_32d_config_settings_t x86_64_accton_dcg8510_32d_config_settings[]; + +/** + * @brief Lookup a configuration setting. + * @param setting The name of the configuration option to lookup. + */ +const char* x86_64_accton_dcg8510_32d_config_lookup(const char* setting); + +/** + * @brief Show the compile-time configuration. + * @param pvs The output stream. + */ +int x86_64_accton_dcg8510_32d_config_show(struct aim_pvs_s* pvs); + +/* */ + +#include "x86_64_accton_dcg8510_32d_porting.h" + +#endif /* __X86_64_ACCTON_DCG8510_32D_CONFIG_H__ */ +/* @} */ diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/inc/x86_64_accton_dcg8510_32d/x86_64_accton_dcg8510_32d_dox.h b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/inc/x86_64_accton_dcg8510_32d/x86_64_accton_dcg8510_32d_dox.h new file mode 100644 index 0000000000..7a599b615b --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/inc/x86_64_accton_dcg8510_32d/x86_64_accton_dcg8510_32d_dox.h @@ -0,0 +1,26 @@ +/**************************************************************************//** + * + * x86_64_accton_dcg8510_32d Doxygen Header + * + *****************************************************************************/ +#ifndef __X86_64_ACCTON_DCG8510_32D_DOX_H__ +#define __X86_64_ACCTON_DCG8510_32D_DOX_H__ + +/** + * @defgroup x86_64_accton_dcg8510_32d x86_64_accton_dcg8510_32d - x86_64_accton_dcg8510_32d Description + * + +The documentation overview for this module should go here. + + * + * @{ + * + * @defgroup x86_64_accton_dcg8510_32d-x86_64_accton_dcg8510_32d Public Interface + * @defgroup x86_64_accton_dcg8510_32d-config Compile Time Configuration + * @defgroup x86_64_accton_dcg8510_32d-porting Porting Macros + * + * @} + * + */ + +#endif /* __X86_64_ACCTON_DCG8510_32D_DOX_H__ */ diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/inc/x86_64_accton_dcg8510_32d/x86_64_accton_dcg8510_32d_porting.h b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/inc/x86_64_accton_dcg8510_32d/x86_64_accton_dcg8510_32d_porting.h new file mode 100644 index 0000000000..00f3c8c723 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/inc/x86_64_accton_dcg8510_32d/x86_64_accton_dcg8510_32d_porting.h @@ -0,0 +1,96 @@ +/**************************************************************************//** + * + * @file + * @brief x86_64_accton_dcg8510_32d Porting Macros. + * + * @addtogroup x86_64_accton_dcg8510_32d-porting + * @{ + * + *****************************************************************************/ +#ifndef __X86_64_ACCTON_DCG8510_32D_PORTING_H__ +#define __X86_64_ACCTON_DCG8510_32D_PORTING_H__ + +/* */ +#if X86_64_ACCTON_DCG8510_32D_CONFIG_PORTING_INCLUDE_STDLIB_HEADERS == 1 +#include +#include +#include +#include +#include +#endif + +#ifndef X86_64_ACCTON_DCG8510_32D_MALLOC + #if defined(GLOBAL_MALLOC) + #define X86_64_ACCTON_DCG8510_32D_MALLOC GLOBAL_MALLOC + #elif X86_64_ACCTON_DCG8510_32D_CONFIG_PORTING_STDLIB == 1 + #define X86_64_ACCTON_DCG8510_32D_MALLOC malloc + #else + #error The macro X86_64_ACCTON_DCG8510_32D_MALLOC is required but cannot be defined. + #endif +#endif + +#ifndef X86_64_ACCTON_DCG8510_32D_FREE + #if defined(GLOBAL_FREE) + #define X86_64_ACCTON_DCG8510_32D_FREE GLOBAL_FREE + #elif X86_64_ACCTON_DCG8510_32D_CONFIG_PORTING_STDLIB == 1 + #define X86_64_ACCTON_DCG8510_32D_FREE free + #else + #error The macro X86_64_ACCTON_DCG8510_32D_FREE is required but cannot be defined. + #endif +#endif + +#ifndef X86_64_ACCTON_DCG8510_32D_MEMSET + #if defined(GLOBAL_MEMSET) + #define X86_64_ACCTON_DCG8510_32D_MEMSET GLOBAL_MEMSET + #elif X86_64_ACCTON_DCG8510_32D_CONFIG_PORTING_STDLIB == 1 + #define X86_64_ACCTON_DCG8510_32D_MEMSET memset + #else + #error The macro X86_64_ACCTON_DCG8510_32D_MEMSET is required but cannot be defined. + #endif +#endif + +#ifndef X86_64_ACCTON_DCG8510_32D_MEMCPY + #if defined(GLOBAL_MEMCPY) + #define X86_64_ACCTON_DCG8510_32D_MEMCPY GLOBAL_MEMCPY + #elif X86_64_ACCTON_DCG8510_32D_CONFIG_PORTING_STDLIB == 1 + #define X86_64_ACCTON_DCG8510_32D_MEMCPY memcpy + #else + #error The macro X86_64_ACCTON_DCG8510_32D_MEMCPY is required but cannot be defined. + #endif +#endif + +#ifndef X86_64_ACCTON_DCG8510_32D_VSNPRINTF + #if defined(GLOBAL_VSNPRINTF) + #define X86_64_ACCTON_DCG8510_32D_VSNPRINTF GLOBAL_VSNPRINTF + #elif X86_64_ACCTON_DCG8510_32D_CONFIG_PORTING_STDLIB == 1 + #define X86_64_ACCTON_DCG8510_32D_VSNPRINTF vsnprintf + #else + #error The macro X86_64_ACCTON_DCG8510_32D_VSNPRINTF is required but cannot be defined. + #endif +#endif + +#ifndef X86_64_ACCTON_DCG8510_32D_SNPRINTF + #if defined(GLOBAL_SNPRINTF) + #define X86_64_ACCTON_DCG8510_32D_SNPRINTF GLOBAL_SNPRINTF + #elif X86_64_ACCTON_DCG8510_32D_CONFIG_PORTING_STDLIB == 1 + #define X86_64_ACCTON_DCG8510_32D_SNPRINTF snprintf + #else + #error The macro X86_64_ACCTON_DCG8510_32D_SNPRINTF is required but cannot be defined. + #endif +#endif + +#ifndef X86_64_ACCTON_DCG8510_32D_STRLEN + #if defined(GLOBAL_STRLEN) + #define X86_64_ACCTON_DCG8510_32D_STRLEN GLOBAL_STRLEN + #elif X86_64_ACCTON_DCG8510_32D_CONFIG_PORTING_STDLIB == 1 + #define X86_64_ACCTON_DCG8510_32D_STRLEN strlen + #else + #error The macro X86_64_ACCTON_DCG8510_32D_STRLEN is required but cannot be defined. + #endif +#endif + +/* */ + + +#endif /* __X86_64_ACCTON_DCG8510_32D_PORTING_H__ */ +/* @} */ diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/make.mk b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/make.mk new file mode 100644 index 0000000000..f9c70d1c5b --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/make.mk @@ -0,0 +1,9 @@ +############################################################################### +# +# +# +############################################################################### +THIS_DIR := $(dir $(lastword $(MAKEFILE_LIST))) +x86_64_accton_dcg8510_32d_INCLUDES := -I $(THIS_DIR)inc +x86_64_accton_dcg8510_32d_INTERNAL_INCLUDES := -I $(THIS_DIR)src +x86_64_accton_dcg8510_32d_DEPENDMODULE_ENTRIES := init:x86_64_accton_dcg8510_32d ucli:x86_64_accton_dcg8510_32d diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/Makefile b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/Makefile new file mode 100644 index 0000000000..7ea27f8f90 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/Makefile @@ -0,0 +1,8 @@ +############################################################################### +# +# Local source generation targets. +# +############################################################################### + +ucli: + @../../../../tools/uclihandlers.py x86_64_accton_dcg8510_32d_ucli.c diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/fani.c b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/fani.c new file mode 100644 index 0000000000..3311a0fa75 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/fani.c @@ -0,0 +1,366 @@ +/************************************************************ + * + * + * Copyright 2014 Big Switch Networks, Inc. + * Copyright 2014 Accton Technology Corporation. + * + * Licensed under the Eclipse Public License, Version 1.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.eclipse.org/legal/epl-v10.html + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + * + * + ************************************************************ + * + * Fan Platform Implementation Defaults. + * + ***********************************************************/ +#include +#include + +#include "platform_lib.h" + +#define VALIDATE(_id) \ + do { \ + if(!ONLP_OID_IS_FAN(_id)) { \ + return ONLP_STATUS_E_INVALID; \ + } \ + } while(0) + + +#define FAN_BOARD_PATH "/sys/switch/fan/" +#define PSU_PATH "/sys/switch/psu/" + +#define FAN_NAME(fan_id) "Chassis Fan-" #fan_id +#define CHASSIS_FAN_ID(fid,mid) FAN_##fid##_MOTOR_##mid##_ON_FAN_BOARD +#define PSU_FAN_ID(psuid) FAN_ON_PSU##psuid +#define CHASSIS_FAN_INFO(_id,_desc) \ + { \ + { ONLP_FAN_ID_CREATE(_id), _desc, 0 },\ + 0x0,\ + ONLP_FAN_CAPS_SET_PERCENTAGE | ONLP_FAN_CAPS_GET_RPM | ONLP_FAN_CAPS_GET_PERCENTAGE,\ + 0,\ + 0,\ + ONLP_FAN_MODE_INVALID,\ + } +#define PSU_FAN_INFO(_id,_desc) \ + { \ + { ONLP_FAN_ID_CREATE(_id), _desc, 0 },\ + 0x0,\ + ONLP_FAN_CAPS_SET_PERCENTAGE | ONLP_FAN_CAPS_GET_RPM | ONLP_FAN_CAPS_GET_PERCENTAGE,\ + 0,\ + 0,\ + ONLP_FAN_MODE_INVALID,\ + } + +/* Static fan information */ +onlp_fan_info_t finfo[] = { + { }, /* Not used */ + CHASSIS_FAN_INFO(1,"Chassis Fan-1 Motor-1"), + CHASSIS_FAN_INFO(2,"Chassis Fan-2 Motor-1"), + CHASSIS_FAN_INFO(3,"Chassis Fan-3 Motor-1"), + CHASSIS_FAN_INFO(4,"Chassis Fan-4 Motor-1"), + CHASSIS_FAN_INFO(5,"Chassis Fan-5 Motor-1"), + CHASSIS_FAN_INFO(6,"Chassis Fan-6 Motor-1"), + CHASSIS_FAN_INFO(7,"Chassis Fan-1 Motor-2"), + CHASSIS_FAN_INFO(8,"Chassis Fan-2 Motor-2"), + CHASSIS_FAN_INFO(9,"Chassis Fan-3 Motor-2"), + CHASSIS_FAN_INFO(10,"Chassis Fan-4 Motor-2"), + CHASSIS_FAN_INFO(11,"Chassis Fan-5 Motor-2"), + CHASSIS_FAN_INFO(12,"Chassis Fan-6 Motor-2"), + PSU_FAN_INFO(FAN_ON_PSU1,"PSU 1 Fan"), + PSU_FAN_INFO(FAN_ON_PSU2,"PSU 2 Fan"), +}; + + +typedef struct fan_syspath_s { + char * status; + char * speed; + char * percentage; + char * direction; + char * model_name; + char * serial_number; +} fan_syspath_t; + +fan_syspath_t syspath[]={ + { }, /* Not used */ + { /*FAN_1_MOTOR_1_ON_FAN_BOARD*/ \ + FAN_BOARD_PATH"fan1/status",\ + FAN_BOARD_PATH"fan1/motor1/speed",\ + FAN_BOARD_PATH"fan1/motor1/ratio",\ + FAN_BOARD_PATH"fan1/motor1/direction",\ + FAN_BOARD_PATH"fan1/model_name" ,\ + FAN_BOARD_PATH"fan1/serial_number",\ + }, + { /*FAN_2_MOTOR_1_ON_FAN_BOARD*/ \ + FAN_BOARD_PATH"fan2/status",\ + FAN_BOARD_PATH"fan2/motor1/speed",\ + FAN_BOARD_PATH"fan2/motor1/ratio",\ + FAN_BOARD_PATH"fan2/motor1/direction",\ + FAN_BOARD_PATH"fan2/model_name" ,\ + FAN_BOARD_PATH"fan2/serial_number",\ + }, + { /*FAN_3_MOTOR_1_ON_FAN_BOARD*/ \ + FAN_BOARD_PATH"fan3/status",\ + FAN_BOARD_PATH"fan3/motor1/speed",\ + FAN_BOARD_PATH"fan3/motor1/ratio",\ + FAN_BOARD_PATH"fan3/motor1/direction",\ + FAN_BOARD_PATH"fan3/model_name" ,\ + FAN_BOARD_PATH"fan3/serial_number",\ + }, + { /*FAN_4_MOTOR_1_ON_FAN_BOARD*/ \ + FAN_BOARD_PATH"fan4/status",\ + FAN_BOARD_PATH"fan4/motor1/speed",\ + FAN_BOARD_PATH"fan4/motor1/ratio",\ + FAN_BOARD_PATH"fan4/motor1/direction",\ + FAN_BOARD_PATH"fan4/model_name" ,\ + FAN_BOARD_PATH"fan4/serial_number",\ + }, + { /*FAN_5_MOTOR_1_ON_FAN_BOARD*/ \ + FAN_BOARD_PATH"fan5/status",\ + FAN_BOARD_PATH"fan5/motor1/speed",\ + FAN_BOARD_PATH"fan5/motor1/ratio",\ + FAN_BOARD_PATH"fan5/motor1/direction",\ + FAN_BOARD_PATH"fan5/model_name" ,\ + FAN_BOARD_PATH"fan5/serial_number",\ + }, + { /*FAN_6_MOTOR_1_ON_FAN_BOARD*/ \ + FAN_BOARD_PATH"fan6/status",\ + FAN_BOARD_PATH"fan6/motor1/speed",\ + FAN_BOARD_PATH"fan6/motor1/ratio",\ + FAN_BOARD_PATH"fan6/motor1/direction",\ + FAN_BOARD_PATH"fan6/model_name" ,\ + FAN_BOARD_PATH"fan6/serial_number",\ + }, + { /*FAN_1_MOTOR_2_ON_FAN_BOARD*/ \ + FAN_BOARD_PATH"fan1/status",\ + FAN_BOARD_PATH"fan1/motor2/speed",\ + FAN_BOARD_PATH"fan1/motor2/ratio",\ + FAN_BOARD_PATH"fan1/motor2/direction",\ + FAN_BOARD_PATH"fan1/model_name" ,\ + FAN_BOARD_PATH"fan1/serial_number",\ + }, + { /*FAN_2_MOTOR_2_ON_FAN_BOARD*/ \ + FAN_BOARD_PATH"fan2/status",\ + FAN_BOARD_PATH"fan2/motor2/speed",\ + FAN_BOARD_PATH"fan2/motor2/ratio",\ + FAN_BOARD_PATH"fan2/motor2/direction",\ + FAN_BOARD_PATH"fan2/model_name" ,\ + FAN_BOARD_PATH"fan2/serial_number",\ + }, + { /*FAN_3_MOTOR_2_ON_FAN_BOARD*/ \ + FAN_BOARD_PATH"fan3/status",\ + FAN_BOARD_PATH"fan3/motor2/speed",\ + FAN_BOARD_PATH"fan3/motor2/ratio",\ + FAN_BOARD_PATH"fan3/motor2/direction",\ + FAN_BOARD_PATH"fan3/model_name" ,\ + FAN_BOARD_PATH"fan3/serial_number",\ + }, + { /*FAN_4_MOTOR_2_ON_FAN_BOARD*/ \ + FAN_BOARD_PATH"fan4/status",\ + FAN_BOARD_PATH"fan4/motor2/speed",\ + FAN_BOARD_PATH"fan4/motor2/ratio",\ + FAN_BOARD_PATH"fan4/motor2/direction",\ + FAN_BOARD_PATH"fan4/model_name" ,\ + FAN_BOARD_PATH"fan4/serial_number",\ + }, + { /*FAN_5_MOTOR_2_ON_FAN_BOARD*/ \ + FAN_BOARD_PATH"fan5/status",\ + FAN_BOARD_PATH"fan5/motor2/speed",\ + FAN_BOARD_PATH"fan5/motor2/ratio",\ + FAN_BOARD_PATH"fan5/motor2/direction",\ + FAN_BOARD_PATH"fan5/model_name" ,\ + FAN_BOARD_PATH"fan5/serial_number",\ + }, + { /*FAN_6_MOTOR_2_ON_FAN_BOARD*/ \ + FAN_BOARD_PATH"fan6/status",\ + FAN_BOARD_PATH"fan6/motor2/speed",\ + FAN_BOARD_PATH"fan6/motor2/ratio",\ + FAN_BOARD_PATH"fan6/motor2/direction",\ + FAN_BOARD_PATH"fan6/model_name" ,\ + FAN_BOARD_PATH"fan6/serial_number",\ + }, + { /*FAN_ON_PSU1*/ \ + PSU_PATH"psu1/present",\ + PSU_PATH"psu1/fan_speed",\ + PSU_PATH"psu1/fan_ratio",\ + 0,\ + 0,\ + 0,\ + }, + { /*FAN_ON_PSU2*/ \ + PSU_PATH"psu2/present",\ + PSU_PATH"psu2/fan_speed",\ + PSU_PATH"psu2/fan_ratio",\ + 0,\ + 0,\ + 0,\ + }, +}; + + +/* + * This function will be called prior to all of onlp_fani_* functions. + */ +int +onlp_fani_init(void) +{ + //get speed_max + return ONLP_STATUS_OK; +} + + +int +onlp_fani_info_get(onlp_oid_t id, onlp_fan_info_t* info) +{ + int value = 0, fid,len; + VALIDATE(id); + + fid = ONLP_OID_ID_GET(id); + *info = finfo[fid]; + + /* get fan present status + */ + // /sys/switch/fan/fan1/status :1(good) + if(syspath[fid].status){ + if (0 > onlp_file_read_int(&value, syspath[fid].status )) + return ONLP_STATUS_E_INTERNAL; + if (value != 1) { + return ONLP_STATUS_OK; /* fan is not present */ + } + info->status |= ONLP_FAN_STATUS_PRESENT; + if(fid <= FAN_6_MOTOR_2_ON_FAN_BOARD){ + if (value==2) //2(present but fail) + info->status |= ONLP_FAN_STATUS_FAILED; + } + } + + + /* get front fan rpm + */ + if(syspath[fid].speed){ + if (0 > onlp_file_read_int(&value, syspath[fid].speed )) + return ONLP_STATUS_E_INTERNAL; + info->rpm = value; + } + + /* set fan fail based on rpm + */ + if(fid>FAN_6_MOTOR_2_ON_FAN_BOARD){ + if (!info->rpm) { + info->status |= ONLP_FAN_STATUS_FAILED; + return ONLP_STATUS_OK; + } + } + + /* get speed percentage + */ + if(syspath[fid].percentage){ + if (0 > onlp_file_read_int(&value, syspath[fid].percentage )) + return ONLP_STATUS_E_INTERNAL; + info->percentage = value; + } + + /* get fan direction + */ + value=0; + if(syspath[fid].direction){ + if (0 > onlp_file_read_int(&value, syspath[fid].direction )) + return ONLP_STATUS_E_INTERNAL; + } + if(value == 0) + info->status |= ONLP_FAN_STATUS_F2B; + else if (value == 1) + info->status |= ONLP_FAN_STATUS_B2F; + + /* Get model name */ + if(syspath[fid].model_name){ + if (0 > onlp_file_read((uint8_t*)info->model,sizeof(info->model),&len, syspath[fid].model_name )) + return ONLP_STATUS_E_INTERNAL; + } + + /* Get serial*/ + if(syspath[fid].serial_number){ + if (0 > onlp_file_read((uint8_t*)info->serial,sizeof(info->model),&len, syspath[fid].serial_number )) + return ONLP_STATUS_E_INTERNAL; + } + return ONLP_STATUS_OK; +} + +/* + * This function sets the speed of the given fan in RPM. + * + * This function will only be called if the fan supprots the RPM_SET + * capability. + * + * It is optional if you have no fans at all with this feature. + */ +int +onlp_fani_rpm_set(onlp_oid_t id, int rpm) +{ + return ONLP_STATUS_E_UNSUPPORTED; +} + +/* + * This function sets the fan speed of the given OID as a percentage. + * + * This will only be called if the OID has the PERCENTAGE_SET + * capability. + * + * It is optional if you have no fans at all with this feature. + */ +int +onlp_fani_percentage_set(onlp_oid_t id, int p) +{ + int fid; + fid = ONLP_OID_ID_GET(id); + if(syspath[fid].percentage){ + if ( 0 > onlp_file_write_int(p, syspath[fid].percentage)) + return ONLP_STATUS_E_INTERNAL; + } + return ONLP_STATUS_OK; +} + +/* + * This function sets the fan speed of the given OID as per + * the predefined ONLP fan speed modes: off, slow, normal, fast, max. + * + * Interpretation of these modes is up to the platform. + * + */ +int +onlp_fani_mode_set(onlp_oid_t id, onlp_fan_mode_t mode) +{ + return ONLP_STATUS_E_UNSUPPORTED; +} + +/* + * This function sets the fan direction of the given OID. + * + * This function is only relevant if the fan OID supports both direction + * capabilities. + * + * This function is optional unless the functionality is available. + */ +int +onlp_fani_dir_set(onlp_oid_t id, onlp_fan_dir_t dir) +{ + return ONLP_STATUS_E_UNSUPPORTED; +} + +/* + * Generic fan ioctl. Optional. + */ +int +onlp_fani_ioctl(onlp_oid_t id, va_list vargs) +{ + return ONLP_STATUS_E_UNSUPPORTED; +} diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/ledi.c b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/ledi.c new file mode 100644 index 0000000000..66c1bf535d --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/ledi.c @@ -0,0 +1,238 @@ +/************************************************************ + * + * + * Copyright 2014 Big Switch Networks, Inc. + * Copyright 2013 Accton Technology Corporation. + * + * Licensed under the Eclipse Public License, Version 1.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.eclipse.org/legal/epl-v10.html + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + * + * + ************************************************************ + * + * + * + ***********************************************************/ +#include + +#include +#include +#include + +#include "platform_lib.h" + +#define VALIDATE(_id) \ + do \ + { \ + if(!ONLP_OID_IS_LED(_id)) \ + { \ + return ONLP_STATUS_E_INVALID; \ + } \ + } while(0) + +/* LED related data */ +enum onlp_led_id +{ + LED_RESERVED = 0, + LED_SYS, + LED_PSU, + LED_FAN, + LED_BMC +}; +#define LED_PATH "/sys/switch/sysled/" +typedef struct led_syspath_s { + enum onlp_led_id id; + char* path; +} led_syspath_t; + +typedef struct led_address_s { + enum onlp_led_id id; + uint8_t bus; + uint8_t devaddr; + uint8_t offset; +} led_address_t; + +typedef struct led_mode_info_s { + onlp_led_mode_t mode; + uint8_t regval; +} led_mode_info_t; + +static led_syspath_t led_syspath[] = +{ + { }, /* Not used */ + {LED_SYS, LED_PATH "sys_led_status"}, + {LED_PSU, LED_PATH "psu_led_status"}, + {LED_FAN, LED_PATH "fan_led_status"}, + {LED_BMC, LED_PATH "bmc_led_status"}, +}; +static led_mode_info_t led_mode_info[] = +{ + {ONLP_LED_MODE_OFF , 0}, + {ONLP_LED_MODE_GREEN , 1}, + {ONLP_LED_MODE_YELLOW , 2}, + {ONLP_LED_MODE_RED , 3}, + {ONLP_LED_MODE_BLUE , 4}, + {ONLP_LED_MODE_GREEN_BLINKING , 5}, + {ONLP_LED_MODE_YELLOW_BLINKING , 6}, + {ONLP_LED_MODE_RED_BLINKING , 7}, + {ONLP_LED_MODE_BLUE_BLINKING , 8}, +}; + +/* + * Get the information for the given LED OID. + */ +static onlp_led_info_t linfo[] = +{ + { }, /* Not used */ + { + { ONLP_LED_ID_CREATE(LED_SYS), "Chassis LED 1 (SYS)", 0 }, + ONLP_LED_STATUS_PRESENT, + ONLP_LED_CAPS_AUTO + }, + { + { ONLP_LED_ID_CREATE(LED_PSU), "Chassis LED 2 (PSU)", 0 }, + ONLP_LED_STATUS_PRESENT, + ONLP_LED_CAPS_AUTO + }, + { + { ONLP_LED_ID_CREATE(LED_FAN), "Chassis LED 3 (FAN)", 0 }, + ONLP_LED_STATUS_PRESENT, + ONLP_LED_CAPS_AUTO + }, + { + { ONLP_LED_ID_CREATE(LED_BMC), "Chassis LED 4 (BMC)", 0 }, + ONLP_LED_STATUS_PRESENT, + ONLP_LED_CAPS_AUTO + }, +}; + + +/* + * This function will be called prior to any other onlp_ledi_* functions. + */ +int +onlp_ledi_init(void) +{ + return ONLP_STATUS_OK; +} + +static int +reg_value_to_onlp_led_mode(enum onlp_led_id id, int value) +{ + int i; + + for (i = 0; i < AIM_ARRAYSIZE(led_mode_info); i++) + { + if (value != led_mode_info[i].regval) + { + continue; + } + + return led_mode_info[i].mode; + } + + return ONLP_LED_MODE_AUTO; +} + +static int +onlp_led_mode_to_reg_value(enum onlp_led_id id, onlp_led_mode_t onlp_led_mode) +{ + int i; + + for (i = 0; i < AIM_ARRAYSIZE(led_mode_info); i++) + { + if (onlp_led_mode != led_mode_info[i].mode) + { + continue; + } + + return led_mode_info[i].regval; + } + + return 0; +} + +int +onlp_ledi_info_get(onlp_oid_t id, onlp_led_info_t* info) +{ + int lid, value; + + VALIDATE(id); + lid = ONLP_OID_ID_GET(id); + + /* Set the onlp_oid_hdr_t and capabilities */ + *info = linfo[ONLP_OID_ID_GET(id)]; + if (0 > onlp_file_read_int(&value, led_syspath[lid].path )) + return ONLP_STATUS_E_INTERNAL; + + info->mode = reg_value_to_onlp_led_mode(lid, value); + info->mode = ONLP_LED_MODE_AUTO; + /* Set the on/off status */ + if (info->mode != ONLP_LED_MODE_OFF) + { + info->status |= ONLP_LED_STATUS_ON; + } + + return ONLP_STATUS_OK; +} + +/* + * Turn an LED on or off. + * + * This function will only be called if the LED OID supports the ONOFF + * capability. + * + * What 'on' means in terms of colors or modes for multimode LEDs is + * up to the platform to decide. This is intended as baseline toggle mechanism. + */ +int +onlp_ledi_set(onlp_oid_t id, int on_or_off) +{ + VALIDATE(id); + + if (!on_or_off) + { + return onlp_ledi_mode_set(id, ONLP_LED_MODE_OFF); + } + + return ONLP_STATUS_E_UNSUPPORTED; +} + +/* + * This function puts the LED into the given mode. It is a more functional + * interface for multimode LEDs. + * + * Only modes reported in the LED's capabilities will be attempted. + */ +int +onlp_ledi_mode_set(onlp_oid_t id, onlp_led_mode_t mode) +{ + int value,lid; + VALIDATE(id); + lid = ONLP_OID_ID_GET(id); + + value = onlp_led_mode_to_reg_value(lid, mode); + if (0 > onlp_file_write_int(value, led_syspath[lid].path )) + return ONLP_STATUS_E_INTERNAL; + + return ONLP_STATUS_OK; +} + +/* + * Generic LED ioctl interface. + */ +int +onlp_ledi_ioctl(onlp_oid_t id, va_list vargs) +{ + return ONLP_STATUS_E_UNSUPPORTED; +} diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/make.mk b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/make.mk new file mode 100644 index 0000000000..fe9cd6307b --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/make.mk @@ -0,0 +1,8 @@ +############################################################################### +# +# +# +############################################################################### +LIBRARY := x86_64_accton_dcg8510_32d +$(LIBRARY)_SUBDIR := $(dir $(lastword $(MAKEFILE_LIST))) +include $(BUILDER)/lib.mk diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/platform_lib.c b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/platform_lib.c new file mode 100644 index 0000000000..c66c41cb62 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/platform_lib.c @@ -0,0 +1,150 @@ +/************************************************************ + * + * + * Copyright 2017 Accton Technology Corporation. + * + * Licensed under the Eclipse Public License, Version 1.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.eclipse.org/legal/epl-v10.html + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + * + * + ************************************************************ + * + * + * + ***********************************************************/ +#include +#include +#include +#include +#include +#include "platform_lib.h" + +int +onlp_file_read_hex(int* value, const char* fmt, ...) +{ + int rv; + va_list vargs; + va_start(vargs, fmt); + uint8_t data[16]={0}; + int len; + rv = onlp_file_vread(data, 16, &len, fmt, vargs); + va_end(vargs); + *value = strtol((const char * )data, NULL, 0);// Base 0 automatically detects hex + return rv; +} + +int +onlp_file_vwrite_hex(int value, const char* fmt, va_list vargs) +{ + int rv; + char* s = aim_fstrdup("0x%x", value); + rv = onlp_file_vwrite_str(s, fmt, vargs); + aim_free(s); + return rv; +} + + +int +onlp_file_write_hex(int value, const char* fmt, ...) +{ + int rv; + va_list vargs; + va_start(vargs, fmt); + rv = onlp_file_vwrite_hex(value, fmt, vargs); + va_end(vargs); + return rv; +} + +int +onlp_file_readb(int* value,int offset, const char* fmt, ...) +{ + int fd; + va_list vargs; + uint8_t data; + va_start(vargs, fmt); + fd = onlp_file_vopen(O_RDONLY, 0, fmt, vargs); + va_end(vargs); + if (fd<0) + return ONLP_STATUS_E_INTERNAL; + lseek(fd, offset, SEEK_SET); + if(read(fd, &data, 1) <=0){ + close(fd); + return ONLP_STATUS_E_INTERNAL; + } + close(fd); + *value=data; + return ONLP_STATUS_OK; +} + +int +onlp_file_readw(int* value,int offset, const char* fmt, ...) +{ + int fd; + va_list vargs; + uint8_t data[2]; + va_start(vargs, fmt); + fd = onlp_file_vopen(O_RDONLY, 0, fmt, vargs); + va_end(vargs); + if (fd<0) + return ONLP_STATUS_E_INTERNAL; + lseek(fd, offset, SEEK_SET); + if(read(fd, &data, 2) <=0){ + close(fd); + return ONLP_STATUS_E_INTERNAL; + } + close(fd); + *value=data[0]<<8|data[1]; + return ONLP_STATUS_OK; +} + +int +onlp_file_writeb(int value,int offset, const char* fmt, ...) +{ + int fd; + va_list vargs; + uint8_t data=value; + va_start(vargs, fmt); + fd = onlp_file_vopen(O_WRONLY, 0, fmt, vargs); + va_end(vargs); + if (fd<0) + return ONLP_STATUS_E_INTERNAL; + lseek(fd, offset, SEEK_SET); + if(write(fd, &data, 1) <=0){ + close(fd); + return ONLP_STATUS_E_INTERNAL; + } + close(fd); + return ONLP_STATUS_OK; +} + +int +onlp_file_writew(int value,int offset, const char* fmt, ...) +{ + int fd; + va_list vargs; + uint8_t data[2]; + data[1]=value&0xff; + data[0]=(value >> 8 )&0xff; + va_start(vargs, fmt); + fd = onlp_file_vopen(O_WRONLY, 0, fmt, vargs); + va_end(vargs); + if (fd<0) + return ONLP_STATUS_E_INTERNAL; + lseek(fd, offset, SEEK_SET); + if(write(fd, &data, 2) <=0){ + close(fd); + return ONLP_STATUS_E_INTERNAL; + } + close(fd); + return ONLP_STATUS_OK; +} \ No newline at end of file diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/platform_lib.h b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/platform_lib.h new file mode 100644 index 0000000000..423e43d324 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/platform_lib.h @@ -0,0 +1,100 @@ +/************************************************************ + * + * + * Copyright 2014 Big Switch Networks, Inc. + * Copyright 2014 Accton Technology Corporation. + * + * Licensed under the Eclipse Public License, Version 1.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.eclipse.org/legal/epl-v10.html + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + * + * + ************************************************************ + * + * + * + ***********************************************************/ +#ifndef __PLATFORM_LIB_H__ +#define __PLATFORM_LIB_H__ + +#include +#include "x86_64_accton_dcg8510_32d_log.h" + + +#define DEBUG_MODE 0 + +#if (DEBUG_MODE == 1) + #define DEBUG_PRINT(fmt, args...) \ + printf("%s:%s[%d]: " fmt "\r\n", __FILE__, __FUNCTION__, __LINE__, ##args) +#else + #define DEBUG_PRINT(fmt, args...) +#endif + +#define CHASSIS_FAN_COUNT 12 +#define CHASSIS_THERMAL_COUNT 19 +#define CHASSIS_LED_COUNT 4 +#define CHASSIS_PSU_COUNT 2 + +enum onlp_thermal_id +{ + THERMAL_RESERVED = 0, + THERMAL_MAC_PIPE=0, + THERMAL_CPU_CORE_0=1, + THERMAL_CPU_CORE_1, + THERMAL_CPU_CORE_2, + THERMAL_CPU_CORE_3, + THERMAL_CPU_PACKAGE, + THERMAL_TMP75_48, + THERMAL_TMP75_49, + THERMAL_TMP75_4A, + THERMAL_TMP75_4B, + THERMAL_1_TMP431_4C, + THERMAL_2_TMP431_4C, + THERMAL_TMP75_4E, + THERMAL_TMP75_4D, + THERMAL_TMP275_4E, + THERMAL_VCORE_MAC, + THERMAL_VDDT0V9_R_MAC, + THERMAL_VDD1V5_1V8_MAC, + THERMAL_VCC3V3_QSFP_AB, + THERMAL_VCC3V3_QSFP_CD, + THERMAL_PSU1_TEMP_1, + THERMAL_PSU1_TEMP_2, + THERMAL_PSU2_TEMP_1, + THERMAL_PSU2_TEMP_2, +}; + +enum onlp_fan_id { + FAN_1_MOTOR_1_ON_FAN_BOARD = 1, + FAN_2_MOTOR_1_ON_FAN_BOARD, + FAN_3_MOTOR_1_ON_FAN_BOARD, + FAN_4_MOTOR_1_ON_FAN_BOARD, + FAN_5_MOTOR_1_ON_FAN_BOARD, + FAN_6_MOTOR_1_ON_FAN_BOARD, + FAN_1_MOTOR_2_ON_FAN_BOARD, + FAN_2_MOTOR_2_ON_FAN_BOARD, + FAN_3_MOTOR_2_ON_FAN_BOARD, + FAN_4_MOTOR_2_ON_FAN_BOARD, + FAN_5_MOTOR_2_ON_FAN_BOARD, + FAN_6_MOTOR_2_ON_FAN_BOARD, + FAN_ON_PSU1, + FAN_ON_PSU2, +}; + + +int onlp_file_read_hex(int* value, const char* fmt, ...); +int onlp_file_write_hex(int value, const char* fmt, ...); +int onlp_file_readb(int* value,int offset, const char* fmt, ...); +int onlp_file_readw(int* value,int offset, const char* fmt, ...); +int onlp_file_writeb(int value,int offset, const char* fmt, ...); +int onlp_file_writew(int value,int offset, const char* fmt, ...); +#endif /* __PLATFORM_LIB_H__ */ diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/psui.c b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/psui.c new file mode 100644 index 0000000000..82e485ea42 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/psui.c @@ -0,0 +1,202 @@ +/************************************************************ + * + * + * Copyright 2014 Big Switch Networks, Inc. + * Copyright 2014 Accton Technology Corporation. + * + * Licensed under the Eclipse Public License, Version 1.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.eclipse.org/legal/epl-v10.html + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + * + * + ************************************************************ + * + * + * + ***********************************************************/ +#include +#include +#include +#include +#include "platform_lib.h" + +#define VALIDATE(_id) \ + do { \ + if(!ONLP_OID_IS_PSU(_id)) { \ + return ONLP_STATUS_E_INVALID; \ + } \ + } while(0) + +#define PSU1_ID 1 +#define PSU2_ID 2 + + +#define PSU_PATH "/sys/switch/psu/" + + + + +/* + * Get all information about the given PSU oid. + */ +static onlp_psu_info_t pinfo[] = +{ + { }, /* Not used */ + { + { ONLP_PSU_ID_CREATE(PSU1_ID), "PSU-1", 0 , + { + ONLP_THERMAL_ID_CREATE(THERMAL_PSU1_TEMP_1), + ONLP_THERMAL_ID_CREATE(THERMAL_PSU1_TEMP_2), + ONLP_FAN_ID_CREATE(FAN_ON_PSU1), + } + }, + }, + { + { ONLP_PSU_ID_CREATE(PSU2_ID), "PSU-2", 0 , + { + ONLP_THERMAL_ID_CREATE(THERMAL_PSU2_TEMP_1), + ONLP_THERMAL_ID_CREATE(THERMAL_PSU2_TEMP_2), + ONLP_FAN_ID_CREATE(FAN_ON_PSU2), + } + }, + } +}; + +int +onlp_psui_init(void) +{ + return ONLP_STATUS_OK; +} + + +int +onlp_psui_info_get(onlp_oid_t id, onlp_psu_info_t* info) +{ + int pid, value; + char path[80] = {0}; + int len; + VALIDATE(id); + pid = ONLP_OID_ID_GET(id); + + *info = pinfo[pid]; /* Set the onlp_oid_hdr_t */ + + /* Get the present status */ + // /sys/switch/psu/psu[n]/present + sprintf(path, "%spsu%d/present", PSU_PATH, pid); + if (0 > onlp_file_read_int(&value, path )) + return ONLP_STATUS_E_INTERNAL; + + if (value!=1) { + info->status &= ~ONLP_PSU_STATUS_PRESENT; + return ONLP_STATUS_OK; + } + info->status |= ONLP_PSU_STATUS_PRESENT; + info->caps = ONLP_PSU_CAPS_AC; + + /* Get model name */ + // /sys_switch/psu/psu[n]/model_name + sprintf(path, "%spsu%d/model_name", PSU_PATH, pid); + if (0 > onlp_file_read((uint8_t*)info->model,sizeof(info->model),&len, path )) + return ONLP_STATUS_E_INTERNAL; + /* Get serial number */ + // /sys_switch/psu/psu[n]/serial_number + sprintf(path, "%spsu%d/serial_number", PSU_PATH, pid); + if (0 > onlp_file_read((uint8_t*)info->serial,sizeof(info->serial),&len, path )) + return ONLP_STATUS_E_INTERNAL; + + /* Read vin */ + // /sys_switch/psu/psu[n]/in_vol + sprintf(path, "%spsu%d/in_vol", PSU_PATH, pid); + if (0 > onlp_file_read_int(&value, path )) + return ONLP_STATUS_E_INTERNAL; + if (value > 0) { + info->mvin = value; + info->caps |= ONLP_PSU_CAPS_VIN; + }else{ + info->status |=ONLP_PSU_STATUS_UNPLUGGED; + return ONLP_STATUS_OK; + } + + /* Get power good status */ + // /sys_switch/psu/psu[n]/out_status + sprintf(path, "%spsu%d/out_status", PSU_PATH, pid); + if (0 > onlp_file_read_int(&value, path )) + return ONLP_STATUS_E_INTERNAL; + + if (value!=1) { + info->status |= ONLP_PSU_STATUS_FAILED; + return ONLP_STATUS_OK; + } + + /* Get input output power status */ + + + + /* Read iin */ + // /sys_switch/psu/psu[n]/in_curr + sprintf(path, "%spsu%d/in_curr", PSU_PATH, pid); + if (0 > onlp_file_read_int(&value, path )) + return ONLP_STATUS_E_INTERNAL; + if (value >= 0) { + info->miin = value; + info->caps |= ONLP_PSU_CAPS_IIN; + } + + /* Get pin */ + // /sys_switch/psu/psu[n]/in_power + sprintf(path, "%spsu%d/in_power", PSU_PATH, pid); + if (0 > onlp_file_read_int(&value, path )) + return ONLP_STATUS_E_INTERNAL; + if (value >= 0) { + info->mpin = value; + info->caps |= ONLP_PSU_CAPS_PIN; + } + + /* Read iout */ + // /sys_switch/psu/psu[n]/out_curr + sprintf(path, "%spsu%d/out_curr", PSU_PATH, pid); + if (0 > onlp_file_read_int(&value, path )) + return ONLP_STATUS_E_INTERNAL; + if (value >= 0) { + info->miout = value; + info->caps |= ONLP_PSU_CAPS_IOUT; + } + + /* Read pout */ + // /sys_switch/psu/psu[n]/out_power + sprintf(path, "%spsu%d/out_power", PSU_PATH, pid); + if (0 > onlp_file_read_int(&value, path )) + return ONLP_STATUS_E_INTERNAL; + if (value >= 0) { + info->mpout = value; + info->caps |= ONLP_PSU_CAPS_POUT; + } + + /* Get vout */ + // /sys_switch/psu/psu[n]/out_vol + sprintf(path, "%spsu%d/out_vol", PSU_PATH, pid); + if (0 > onlp_file_read_int(&value, path )) + return ONLP_STATUS_E_INTERNAL; + if (value >= 0) { + info->mvout = value; + info->caps |= ONLP_PSU_CAPS_VOUT; + } + + + return ONLP_STATUS_OK; +} + +int +onlp_psui_ioctl(onlp_oid_t pid, va_list vargs) +{ + return ONLP_STATUS_E_UNSUPPORTED; +} diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/sfpi.c b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/sfpi.c new file mode 100644 index 0000000000..fd9fea8e80 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/sfpi.c @@ -0,0 +1,303 @@ +/************************************************************ + * + * + * Copyright 2014 Big Switch Networks, Inc. + * Copyright 2016 Accton Technology Corporation. + * + * Licensed under the Eclipse Public License, Version 1.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.eclipse.org/legal/epl-v10.html + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + * + * + ************************************************************ + * + * + * + ***********************************************************/ +#include +#include +#include +#include +#include +#include "platform_lib.h" +#include "x86_64_accton_dcg8510_32d_log.h" + +#define NUM_OF_QSFP_PORT 32 + +/* Bit value */ +#define BIT0 0x0001 +#define BIT1 0x0002 +#define BIT2 0x0004 +#define BIT3 0x0008 +#define BIT4 0x0010 +#define BIT5 0x0020 +#define BIT6 0x0040 +#define BIT7 0x0080 +#define BIT8 0x0100 +#define BIT9 0x0200 +#define BIT10 0x0400 +#define BIT11 0x0800 +#define BIT12 0x1000 +#define BIT13 0x2000 +#define BIT14 0x4000 +#define BIT15 0x8000 + +#define SFP_PATH "/sys/switch/transceiver/" +static int port_id_to_eth_id(int port) +{ + return port+1; +} + +typedef struct port_info_s +{ + int port; + uint8_t address; + uint8_t chan; + uint16_t bit; +} port_info_t; + +typedef struct bitmap_info_s +{ + uint16_t bit; + int port; +} bitmap_info_t; + + +uint64_t g_present_port_val; +/************************************************************ + * + * SFPI Entry Points + * + ***********************************************************/ +int +onlp_sfpi_init(void) +{ + /* Called at initialization time */ + return ONLP_STATUS_OK; +} + +int +onlp_sfpi_bitmap_get(onlp_sfp_bitmap_t* bmap) +{ + int p; + AIM_BITMAP_CLR_ALL(bmap); + + for(p = 0; p < NUM_OF_QSFP_PORT; p++) + { + AIM_BITMAP_SET(bmap, p); + } + + return ONLP_STATUS_OK; +} + +int +onlp_sfpi_is_present(int port) +{ + /* + * Return 1 if present. + * Return 0 if not present. + * Return < 0 if error. + */ + + char path[128] = {0}; + int value = 0; + ///sys_switch/transceiver/eth[n]/present + sprintf(path, "%seth%d/present", SFP_PATH, port_id_to_eth_id(port)); + if (0 > onlp_file_read_int(&value, path )) + return ONLP_STATUS_E_INTERNAL; + return value; +} + +int +onlp_sfpi_rx_los_bitmap_get(onlp_sfp_bitmap_t* dst) +{ + return ONLP_STATUS_E_UNSUPPORTED; +} + +int +onlp_sfpi_eeprom_read(int port, uint8_t data[256]) +{ + int size; + char path[128] = {0}; + if(port <0 || port >= NUM_OF_QSFP_PORT) + return ONLP_STATUS_E_INTERNAL; + + /* get data of eeprom */ + //sys_switch/transceiver/eth[n]/eeprom + sprintf(path, "%seth%d/eeprom", SFP_PATH, port_id_to_eth_id(port)); + + if(onlp_file_read(data, 256, &size, path) == ONLP_STATUS_OK) { + if(size == 256) { + return ONLP_STATUS_OK; + } + } + return ONLP_STATUS_E_INTERNAL; + +} + + + + +int +onlp_sfpi_dev_readb(int port, uint8_t devaddr, uint8_t addr) +{ + int value; + onlp_file_readb(&value,addr, "%seth%d/eeprom", SFP_PATH, port_id_to_eth_id(port)); + return value; +} + +int +onlp_sfpi_dev_writeb(int port, uint8_t devaddr, uint8_t addr, uint8_t value) +{ + return onlp_file_writeb(value,addr, "%seth%d/eeprom", SFP_PATH, port_id_to_eth_id(port)); +} + +int +onlp_sfpi_dev_readw(int port, uint8_t devaddr, uint8_t addr) +{ + int value; + onlp_file_readw(&value,addr, "%seth%d/eeprom", SFP_PATH, port_id_to_eth_id(port)); + return value; +} + +int +onlp_sfpi_dev_writew(int port, uint8_t devaddr, uint8_t addr, uint16_t value) +{ + return onlp_file_writew(value,addr, "%seth%d/eeprom", SFP_PATH, port_id_to_eth_id(port)); +} + +int +onlp_sfpi_control_set(int port, onlp_sfp_control_t control, int value) +{ + int rv; + char path[128] = {0}; + rv = ONLP_STATUS_OK; + + switch(control) + { + case ONLP_SFP_CONTROL_RESET: + { + sprintf(path, "%seth%d/reset", SFP_PATH, port_id_to_eth_id(port)); + if (0 > onlp_file_write_int(value, path)) + rv= ONLP_STATUS_E_INTERNAL; + break; + } + case ONLP_SFP_CONTROL_LP_MODE: + { + sprintf(path, "%seth%d/lpmode", SFP_PATH, port_id_to_eth_id(port)); + if (0 > onlp_file_write_int(value, path)) + rv= ONLP_STATUS_E_INTERNAL; + break; + } + case ONLP_SFP_CONTROL_TX_DISABLE: + { + sprintf(path, "%seth%d/tx_disable", SFP_PATH, port_id_to_eth_id(port)); + if (0 > onlp_file_write_hex(value, path)) + rv= ONLP_STATUS_E_INTERNAL; + break; + } + default: + rv = ONLP_STATUS_E_UNSUPPORTED; + break; + } + + return rv; +} + + +int +onlp_sfpi_control_get(int port, onlp_sfp_control_t control, int* value) +{ + int rv=ONLP_STATUS_E_UNSUPPORTED; + char path[128] = {0}; + switch(control) + { + case ONLP_SFP_CONTROL_TX_DISABLE: + { + rv=ONLP_STATUS_OK; + //sys_switch/transceiver/eth[n]/tx_disable + sprintf(path, "%seth%d/tx_disable", SFP_PATH, port_id_to_eth_id(port)); + if (0 > onlp_file_read_hex(value, path )) + rv= ONLP_STATUS_E_INTERNAL; + break; + } + /* + case ONLP_SFP_CONTROL_TX_FAULT: + { + rv=ONLP_STATUS_OK; + //sys_switch/transceiver/eth[n]/tx_fault + sprintf(path, "%seth%d/tx_fault", SFP_PATH, port_id_to_eth_id(port)); + if (0 > onlp_file_read_hex(value, path )) + rv= ONLP_STATUS_E_INTERNAL; + break; + } + */ + /* + case ONLP_SFP_CONTROL_RX_LOS: + { + rv=ONLP_STATUS_OK; + //sys_switch/transceiver/eth[n]/rx_los + sprintf(path, "%seth%d/rx_los", SFP_PATH, port_id_to_eth_id(port)); + if (0 > onlp_file_read_hex(value, path )) + rv= ONLP_STATUS_E_INTERNAL; + break; + } + */ + case ONLP_SFP_CONTROL_RESET: + { + rv=ONLP_STATUS_OK; + sprintf(path, "%seth%d/reset", SFP_PATH, port_id_to_eth_id(port)); + if (0 > onlp_file_read_int(value, path )) + rv= ONLP_STATUS_E_INTERNAL; + break; + } + case ONLP_SFP_CONTROL_LP_MODE: + { + rv=ONLP_STATUS_OK; + // /sys_switch/transceiver/eth[n]/lpmode + sprintf(path, "%seth%d/lpmode", SFP_PATH, port_id_to_eth_id(port)); + if (0 > onlp_file_read_int(value, path )) + rv= ONLP_STATUS_E_INTERNAL; + break; + } + default: + rv = ONLP_STATUS_E_UNSUPPORTED; + break; + } + + return rv; +} + +int +onlp_sfpi_control_supported(int port, onlp_sfp_control_t control, int* rv) +{ + //@param rv [out] Receives 1 if supported, 0 if not supported. + switch(control) + { + case ONLP_SFP_CONTROL_RESET: + case ONLP_SFP_CONTROL_LP_MODE: + case ONLP_SFP_CONTROL_TX_DISABLE: + *rv=1; + break; + default: + *rv = 0; + break; + } + return ONLP_STATUS_OK; +} + + +int +onlp_sfpi_denit(void) +{ + return ONLP_STATUS_OK; +} diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/sysi.c b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/sysi.c new file mode 100644 index 0000000000..74ad8765a7 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/sysi.c @@ -0,0 +1,183 @@ +/************************************************************ + * + * + * Copyright 2014 Big Switch Networks, Inc. + * Copyright 2014 Accton Technology Corporation. + * + * Licensed under the Eclipse Public License, Version 1.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.eclipse.org/legal/epl-v10.html + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + * + * + ************************************************************ + * + * + * + ***********************************************************/ +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "platform_lib.h" +#include "x86_64_accton_dcg8510_32d_int.h" +#include "x86_64_accton_dcg8510_32d_log.h" + +//#define IDPROM_PATH "/sys/bus/i2c/devices/i2c-0/0-0057/eeprom" +#define IDPROM_PATH "/sys/switch/syseeprom/eeprom" +#define SYSTEM_CPLD_REV_PATH "/sys/switch/cpld/cpld1/firmware_version" +#define FAN_CPLD_REV_PATH "/sys/switch/cpld/cpld2/firmware_version" +#define PORT_CPLD1_REV_PATH "/sys/switch/cpld/cpld3/firmware_version" +#define PORT_CPLD2_REV_PATH "/sys/switch/cpld/cpld4/firmware_version" + +typedef struct cpld_version +{ + char *attr_name; + char version[16]; + char *description; +} cpld_version_t; + +const char* +onlp_sysi_platform_get(void) +{ +/* if (fpga_pltfm_init(0) != 0) + { + return NULL; + } */ + + return "x86-64-accton-dcg8510-32d-r0"; +} +int +onlp_sysi_onie_data_get(uint8_t** data, int* size) +{ + uint8_t* rdata = aim_zmalloc(256); + if(onlp_file_read(rdata, 256, size, IDPROM_PATH) == ONLP_STATUS_OK) { + if(*size == 256) { + *data = rdata; + return ONLP_STATUS_OK; + } + } + + aim_free(rdata); + *size = 0; + return ONLP_STATUS_E_INTERNAL; +} + + +int +onlp_sysi_oids_get(onlp_oid_t* table, int max) +{ + int i; + onlp_oid_t* e = table; + memset(table, 0, max*sizeof(onlp_oid_t)); + + /* 20 Thermal sensors on the chassis */ + for (i = 1; i <= CHASSIS_THERMAL_COUNT; i++) + { + *e++ = ONLP_THERMAL_ID_CREATE(i); + } + + /* 4 LEDs on the chassis */ + for (i = 1; i <= CHASSIS_LED_COUNT; i++) + { + *e++ = ONLP_LED_ID_CREATE(i); + } + + /* 2 PSUs on the chassis */ + for (i = 1; i <= CHASSIS_PSU_COUNT; i++) + { + *e++ = ONLP_PSU_ID_CREATE(i); + } + + /* 6 Fans on the chassis */ + for (i = 1; i <= CHASSIS_FAN_COUNT; i++) + { + *e++ = ONLP_FAN_ID_CREATE(i); + } + + //bmc_tty_init(); + + return 0; +} + +int +onlp_sysi_platform_info_get(onlp_platform_info_t* pi) +{ + int i; + char path[64] = {0}; + cpld_version_t cplds[] = { { "sys_cpld_ver", "", "SYSTEM CPLD"}, + { "fan_cpld_ver", "", "FAN CPLD"}, + { "port_cpld1_ver", "", "PORT CPLD1"}, + { "port_cpld2_ver", "", "PORT CPLD2"}, + }; + uint8_t version_strings[16]={0}; + int readi; + /* Read CPLD version */ + for (i = 0; i < AIM_ARRAYSIZE(cplds); i++) + { + if(strcmp("sys_cpld_ver", cplds[i].attr_name) ==0) + { + strcpy(path, SYSTEM_CPLD_REV_PATH); + } + + if(strcmp("fan_cpld_ver", cplds[i].attr_name) ==0) + { + strcpy(path, FAN_CPLD_REV_PATH); + } + + if(strcmp("port_cpld1_ver", cplds[i].attr_name) ==0) + { + strcpy(path, PORT_CPLD1_REV_PATH); + } + + if(strcmp("port_cpld2_ver", cplds[i].attr_name) ==0) + { + strcpy(path, PORT_CPLD2_REV_PATH); + } + + if(0 > onlp_file_read(version_strings, 16, &readi, path)) + return ONLP_STATUS_E_INTERNAL; + + + strcpy(cplds[i].version, (char*)version_strings); + } + + pi->cpld_versions = aim_fstrdup("%s:%s, %s:%s,%s:%s", + cplds[0].description, cplds[0].version, + cplds[1].description, cplds[1].version, + cplds[2].description, cplds[2].version, + cplds[3].description, cplds[3].version); + return ONLP_STATUS_OK; +} + +void +onlp_sysi_platform_info_free(onlp_platform_info_t* pi) +{ + aim_free(pi->cpld_versions); +} + +int +onlp_sysi_platform_manage_fans(void) +{ + return ONLP_STATUS_E_UNSUPPORTED; +} + +int +onlp_sysi_platform_manage_leds(void) +{ + return ONLP_STATUS_E_UNSUPPORTED; +} diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/thermali.c b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/thermali.c new file mode 100644 index 0000000000..c3553e2f9e --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/thermali.c @@ -0,0 +1,202 @@ +/************************************************************ + * + * + * Copyright 2014 Big Switch Networks, Inc. + * Copyright 2014 Accton Technology Corporation. + * + * Licensed under the Eclipse Public License, Version 1.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.eclipse.org/legal/epl-v10.html + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + * + * + ************************************************************ + * + * Thermal Sensor Platform Implementation. + * + ***********************************************************/ +#include +#include +#include "platform_lib.h" + +#define VALIDATE(_id) \ + do { \ + if(!ONLP_OID_IS_THERMAL(_id)) { \ + return ONLP_STATUS_E_INVALID; \ + } \ + } while(0) + +#define THERMAL_PATH_FORMAT "/sys/switch/sensor/temp%d/temp_input" +#define THERMAL_PSU_FORMAT "/sys/switch/psu/psu%d/temp%d/temp_input" + + +/* Static values */ +static onlp_thermal_info_t linfo[] = +{ + { }, /* Not used */ + /* + { { ONLP_THERMAL_ID_CREATE(THERMAL_MAC_PIPE), "MAC_PIPE", 0}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS + }, + */ + { { ONLP_THERMAL_ID_CREATE(THERMAL_CPU_CORE_0), "CPU_CORE_0", 0}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_CPU_CORE_1), "CPU_CORE_1", 0}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_CPU_CORE_2), "CPU_CORE_2", 0}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_CPU_CORE_3), "CPU_CORE_3", 0}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_CPU_PACKAGE), "CPU_PACKAGE", 0}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_TMP75_48), "TMP75_48", 0}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_TMP75_49), "TMP75_49", 0}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_TMP75_4A), "TMP75_4A", 0}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_TMP75_4B), "TMP75_4B", 0}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_1_TMP431_4C), "TMP431_4C_1", 0}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_2_TMP431_4C), "TMP431_4C_2", 0}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_TMP75_4E), "TMP75_4E", 0}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_TMP75_4D), "TMP75_4D", 0}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_TMP275_4E), "TMP275_4E", 0}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_VCORE_MAC), "VCORE_MAC", 0}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_VDDT0V9_R_MAC), "VDDT0V9_R_MAC", 0}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_VDD1V5_1V8_MAC), "VDD1V5_1V8_MAC", 0}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_VCC3V3_QSFP_AB), "VCC3V3_QSFP_AB", 0}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_VCC3V3_QSFP_CD), "VCC3V3_QSFP_CD", 0}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_PSU1_TEMP_1), "PSU1_TEMP_1", 0}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_PSU1_TEMP_2), "PSU1_TEMP_2", 0}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_PSU2_TEMP_1), "PSU2_TEMP_1", 0}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_PSU2_TEMP_2), "PSU2_TEMP_2", 0}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS + }, +}; + +/* + * This will be called to intiialize the thermali subsystem. + */ +int +onlp_thermali_init(void) +{ + return ONLP_STATUS_OK; +} + +/* + * Retrieve the information structure for the given thermal OID. + * + * If the OID is invalid, return ONLP_E_STATUS_INVALID. + * If an unexpected error occurs, return ONLP_E_STATUS_INTERNAL. + * Otherwise, return ONLP_STATUS_OK with the OID's information. + * + * Note -- it is expected that you fill out the information + * structure even if the sensor described by the OID is not present. + */ +int +onlp_thermali_info_get(onlp_oid_t id, onlp_thermal_info_t* info) +{ + int tid; + char path[128] = {0}; + VALIDATE(id); + + tid = ONLP_OID_ID_GET(id); + /* Set the onlp_oid_hdr_t and capabilities */ + *info = linfo[tid]; + if(tid onlp_file_read_int(&info->mcelsius, path )) + return ONLP_STATUS_E_INTERNAL; + } + else{ + switch(tid){ + case THERMAL_PSU1_TEMP_1: + sprintf(path, THERMAL_PSU_FORMAT, 1,1); + break; + case THERMAL_PSU1_TEMP_2: + sprintf(path, THERMAL_PSU_FORMAT, 1,2); + break; + case THERMAL_PSU2_TEMP_1: + sprintf(path, THERMAL_PSU_FORMAT, 2,1); + break; + case THERMAL_PSU2_TEMP_2: + sprintf(path, THERMAL_PSU_FORMAT, 2,2); + break; + default: + return ONLP_STATUS_E_INTERNAL; + } + if (0 > onlp_file_read_int(&info->mcelsius, path )) + return ONLP_STATUS_E_INTERNAL; + + } + + return ONLP_STATUS_OK; +} diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/x86_64_accton_dcg8510_32d_config.c b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/x86_64_accton_dcg8510_32d_config.c new file mode 100644 index 0000000000..058d49ff09 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/x86_64_accton_dcg8510_32d_config.c @@ -0,0 +1,80 @@ +/**************************************************************************//** + * + * + * + *****************************************************************************/ +#include + +/* */ +#define __x86_64_accton_dcg8510_32d_config_STRINGIFY_NAME(_x) #_x +#define __x86_64_accton_dcg8510_32d_config_STRINGIFY_VALUE(_x) __x86_64_accton_dcg8510_32d_config_STRINGIFY_NAME(_x) +x86_64_accton_dcg8510_32d_config_settings_t x86_64_accton_dcg8510_32d_config_settings[] = +{ +#ifdef X86_64_ACCTON_DCG8510_32D_CONFIG_INCLUDE_LOGGING + { __x86_64_accton_dcg8510_32d_config_STRINGIFY_NAME(X86_64_ACCTON_DCG8510_32D_CONFIG_INCLUDE_LOGGING), __x86_64_accton_dcg8510_32d_config_STRINGIFY_VALUE(X86_64_ACCTON_DCG8510_32D_CONFIG_INCLUDE_LOGGING) }, +#else +{ X86_64_ACCTON_DCG8510_32D_CONFIG_INCLUDE_LOGGING(__x86_64_accton_dcg8510_32d_config_STRINGIFY_NAME), "__undefined__" }, +#endif +#ifdef X86_64_ACCTON_DCG8510_32D_CONFIG_LOG_OPTIONS_DEFAULT + { __x86_64_accton_dcg8510_32d_config_STRINGIFY_NAME(X86_64_ACCTON_DCG8510_32D_CONFIG_LOG_OPTIONS_DEFAULT), __x86_64_accton_dcg8510_32d_config_STRINGIFY_VALUE(X86_64_ACCTON_DCG8510_32D_CONFIG_LOG_OPTIONS_DEFAULT) }, +#else +{ X86_64_ACCTON_DCG8510_32D_CONFIG_LOG_OPTIONS_DEFAULT(__x86_64_accton_dcg8510_32d_config_STRINGIFY_NAME), "__undefined__" }, +#endif +#ifdef X86_64_ACCTON_DCG8510_32D_CONFIG_LOG_BITS_DEFAULT + { __x86_64_accton_dcg8510_32d_config_STRINGIFY_NAME(X86_64_ACCTON_DCG8510_32D_CONFIG_LOG_BITS_DEFAULT), __x86_64_accton_dcg8510_32d_config_STRINGIFY_VALUE(X86_64_ACCTON_DCG8510_32D_CONFIG_LOG_BITS_DEFAULT) }, +#else +{ X86_64_ACCTON_DCG8510_32D_CONFIG_LOG_BITS_DEFAULT(__x86_64_accton_dcg8510_32d_config_STRINGIFY_NAME), "__undefined__" }, +#endif +#ifdef X86_64_ACCTON_DCG8510_32D_CONFIG_LOG_CUSTOM_BITS_DEFAULT + { __x86_64_accton_dcg8510_32d_config_STRINGIFY_NAME(X86_64_ACCTON_DCG8510_32D_CONFIG_LOG_CUSTOM_BITS_DEFAULT), __x86_64_accton_dcg8510_32d_config_STRINGIFY_VALUE(X86_64_ACCTON_DCG8510_32D_CONFIG_LOG_CUSTOM_BITS_DEFAULT) }, +#else +{ X86_64_ACCTON_DCG8510_32D_CONFIG_LOG_CUSTOM_BITS_DEFAULT(__x86_64_accton_dcg8510_32d_config_STRINGIFY_NAME), "__undefined__" }, +#endif +#ifdef X86_64_ACCTON_DCG8510_32D_CONFIG_PORTING_STDLIB + { __x86_64_accton_dcg8510_32d_config_STRINGIFY_NAME(X86_64_ACCTON_DCG8510_32D_CONFIG_PORTING_STDLIB), __x86_64_accton_dcg8510_32d_config_STRINGIFY_VALUE(X86_64_ACCTON_DCG8510_32D_CONFIG_PORTING_STDLIB) }, +#else +{ X86_64_ACCTON_DCG8510_32D_CONFIG_PORTING_STDLIB(__x86_64_accton_dcg8510_32d_config_STRINGIFY_NAME), "__undefined__" }, +#endif +#ifdef X86_64_ACCTON_DCG8510_32D_CONFIG_PORTING_INCLUDE_STDLIB_HEADERS + { __x86_64_accton_dcg8510_32d_config_STRINGIFY_NAME(X86_64_ACCTON_DCG8510_32D_CONFIG_PORTING_INCLUDE_STDLIB_HEADERS), __x86_64_accton_dcg8510_32d_config_STRINGIFY_VALUE(X86_64_ACCTON_DCG8510_32D_CONFIG_PORTING_INCLUDE_STDLIB_HEADERS) }, +#else +{ X86_64_ACCTON_DCG8510_32D_CONFIG_PORTING_INCLUDE_STDLIB_HEADERS(__x86_64_accton_dcg8510_32d_config_STRINGIFY_NAME), "__undefined__" }, +#endif +#ifdef X86_64_ACCTON_DCG8510_32D_CONFIG_INCLUDE_UCLI + { __x86_64_accton_dcg8510_32d_config_STRINGIFY_NAME(X86_64_ACCTON_DCG8510_32D_CONFIG_INCLUDE_UCLI), __x86_64_accton_dcg8510_32d_config_STRINGIFY_VALUE(X86_64_ACCTON_DCG8510_32D_CONFIG_INCLUDE_UCLI) }, +#else +{ X86_64_ACCTON_DCG8510_32D_CONFIG_INCLUDE_UCLI(__x86_64_accton_dcg8510_32d_config_STRINGIFY_NAME), "__undefined__" }, +#endif +#ifdef X86_64_ACCTON_DCG8510_32D_CONFIG_INCLUDE_DEFAULT_FAN_DIRECTION + { __x86_64_accton_dcg8510_32d_config_STRINGIFY_NAME(X86_64_ACCTON_DCG8510_32D_CONFIG_INCLUDE_DEFAULT_FAN_DIRECTION), __x86_64_accton_dcg8510_32d_config_STRINGIFY_VALUE(X86_64_ACCTON_DCG8510_32D_CONFIG_INCLUDE_DEFAULT_FAN_DIRECTION) }, +#else +{ X86_64_ACCTON_DCG8510_32D_CONFIG_INCLUDE_DEFAULT_FAN_DIRECTION(__x86_64_accton_dcg8510_32d_config_STRINGIFY_NAME), "__undefined__" }, +#endif + { NULL, NULL } +}; +#undef __x86_64_accton_dcg8510_32d_config_STRINGIFY_VALUE +#undef __x86_64_accton_dcg8510_32d_config_STRINGIFY_NAME + +const char* +x86_64_accton_dcg8510_32d_config_lookup(const char* setting) +{ + int i; + for(i = 0; x86_64_accton_dcg8510_32d_config_settings[i].name; i++) { + if(!strcmp(x86_64_accton_dcg8510_32d_config_settings[i].name, setting)) { + return x86_64_accton_dcg8510_32d_config_settings[i].value; + } + } + return NULL; +} + +int +x86_64_accton_dcg8510_32d_config_show(struct aim_pvs_s* pvs) +{ + int i; + for(i = 0; x86_64_accton_dcg8510_32d_config_settings[i].name; i++) { + aim_printf(pvs, "%s = %s\n", x86_64_accton_dcg8510_32d_config_settings[i].name, x86_64_accton_dcg8510_32d_config_settings[i].value); + } + return i; +} + +/* */ diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/x86_64_accton_dcg8510_32d_enums.c b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/x86_64_accton_dcg8510_32d_enums.c new file mode 100644 index 0000000000..b2d8f6637c --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/x86_64_accton_dcg8510_32d_enums.c @@ -0,0 +1,9 @@ +/**************************************************************************//** + * + * + * + *****************************************************************************/ +#include + +/* <--auto.start.enum(ALL).source> */ +/* */ diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/x86_64_accton_dcg8510_32d_int.h b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/x86_64_accton_dcg8510_32d_int.h new file mode 100644 index 0000000000..7807d104ce --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/x86_64_accton_dcg8510_32d_int.h @@ -0,0 +1,12 @@ +/**************************************************************************//** + * + * x86_64_accton_dcg8510_32d Internal Header + * + *****************************************************************************/ +#ifndef __x86_64_accton_dcg8510_32d_INT_H__ +#define __x86_64_accton_dcg8510_32d_INT_H__ + +#include + + +#endif /* __x86_64_accton_dcg8510_32d_INT_H__ */ diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/x86_64_accton_dcg8510_32d_log.c b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/x86_64_accton_dcg8510_32d_log.c new file mode 100644 index 0000000000..c18ec40c7b --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/x86_64_accton_dcg8510_32d_log.c @@ -0,0 +1,17 @@ +/**************************************************************************//** + * + * + * + *****************************************************************************/ +#include + +#include "x86_64_accton_dcg8510_32d_log.h" +/* + * x86_64_accton_dcg8510_32d log struct. + */ +AIM_LOG_STRUCT_DEFINE( + X86_64_ACCTON_DCG8510_32D_CONFIG_LOG_OPTIONS_DEFAULT, + X86_64_ACCTON_DCG8510_32D_CONFIG_LOG_BITS_DEFAULT, + NULL, /* Custom log map */ + X86_64_ACCTON_DCG8510_32D_CONFIG_LOG_CUSTOM_BITS_DEFAULT + ); diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/x86_64_accton_dcg8510_32d_log.h b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/x86_64_accton_dcg8510_32d_log.h new file mode 100644 index 0000000000..e66486c85b --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/x86_64_accton_dcg8510_32d_log.h @@ -0,0 +1,12 @@ +/**************************************************************************//** + * + * + * + *****************************************************************************/ +#ifndef __x86_64_accton_dcg8510_32d_LOG_H__ +#define __x86_64_accton_dcg8510_32d_LOG_H__ + +#define AIM_LOG_MODULE_NAME x86_64_accton_dcg8510_32d +#include + +#endif /* __x86_64_accton_dcg8510_32d_LOG_H__ */ diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/x86_64_accton_dcg8510_32d_module.c b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/x86_64_accton_dcg8510_32d_module.c new file mode 100644 index 0000000000..5247070acd --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/x86_64_accton_dcg8510_32d_module.c @@ -0,0 +1,24 @@ +/**************************************************************************//** + * + * + * + *****************************************************************************/ +#include + +#include "x86_64_accton_dcg8510_32d_log.h" + +static int +datatypes_init__(void) +{ +#define x86_64_accton_dcg8510_32d_ENUMERATION_ENTRY(_enum_name, _desc) AIM_DATATYPE_MAP_REGISTER(_enum_name, _enum_name##_map, _desc, AIM_LOG_INTERNAL); +#include + return 0; +} + +void __x86_64_accton_dcg8510_32d_module_init__(void) +{ + AIM_LOG_STRUCT_REGISTER(); + datatypes_init__(); +} + +int __onlp_platform_version__ = 1; diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/x86_64_accton_dcg8510_32d_ucli.c b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/x86_64_accton_dcg8510_32d_ucli.c new file mode 100644 index 0000000000..f5e629323e --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/onlp/builds/x86_64_accton_dcg8510_32d/module/src/x86_64_accton_dcg8510_32d_ucli.c @@ -0,0 +1,49 @@ +/**************************************************************************//** + * + * + * + *****************************************************************************/ +#include + +#if x86_64_accton_dcg8510_32d_CONFIG_INCLUDE_UCLI == 1 + +#include +#include +#include + +static ucli_status_t +x86_64_accton_dcg8510_32d_ucli_ucli__config__(ucli_context_t* uc) +{ + UCLI_HANDLER_MACRO_MODULE_CONFIG(x86_64_accton_dcg8510_32d) +} + +/* */ +/* */ + +static ucli_module_t +x86_64_accton_dcg8510_32d_ucli_module__ = + { + "x86_64_accton_dcg8510_32d_ucli", + NULL, + x86_64_accton_dcg8510_32d_ucli_ucli_handlers__, + NULL, + NULL, + }; + +ucli_node_t* +x86_64_accton_dcg8510_32d_ucli_node_create(void) +{ + ucli_node_t* n; + ucli_module_init(&x86_64_accton_dcg8510_32d_ucli_module__); + n = ucli_node_create("x86_64_accton_dcg8510_32d", NULL, &x86_64_accton_dcg8510_32d_ucli_module__); + ucli_node_subnode_add(n, ucli_module_log_node_create("x86_64_accton_dcg8510_32d")); + return n; +} + +#else +void* +x86_64_accton_dcg8510_32d_ucli_node_create(void) +{ + return NULL; +} +#endif diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/platform-config/Makefile b/packages/platforms/accton/x86-64/dcg8510_32d/platform-config/Makefile new file mode 100644 index 0000000000..502e772a7b --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/platform-config/Makefile @@ -0,0 +1 @@ +include $(ONL)/make/pkg.mk diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/platform-config/r0/Makefile b/packages/platforms/accton/x86-64/dcg8510_32d/platform-config/r0/Makefile new file mode 100644 index 0000000000..dc1e7b86f0 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/platform-config/r0/Makefile @@ -0,0 +1 @@ +include $(ONL)/make/pkg.mk diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/platform-config/r0/PKG.yml b/packages/platforms/accton/x86-64/dcg8510_32d/platform-config/r0/PKG.yml new file mode 100644 index 0000000000..c0b71a1907 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/platform-config/r0/PKG.yml @@ -0,0 +1 @@ +!include $ONL_TEMPLATES/platform-config-platform.yml ARCH=amd64 VENDOR=accton BASENAME=x86-64-accton-dcg8510-32d REVISION=r0 diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/platform-config/r0/src/lib/x86-64-accton-dcg8510-32d-r0.yml b/packages/platforms/accton/x86-64/dcg8510_32d/platform-config/r0/src/lib/x86-64-accton-dcg8510-32d-r0.yml new file mode 100644 index 0000000000..adf598443d --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/platform-config/r0/src/lib/x86-64-accton-dcg8510-32d-r0.yml @@ -0,0 +1,34 @@ +--- + +###################################################################### +# +# platform-config for DCG8510-32D +# +###################################################################### +x86-64-accton-dcg8510-32d-r0: + + grub: + + serial: >- + --unit=0 + --speed=115200 + --word=8 + --parity=0 + --stop=1 + + kernel: + <<: *kernel-4-19 + + args: >- + nopat + console=ttyS0,115200n8 + rd_NO_MD + rd_NO_LUKS + intel_iommu=off + noapic + + ##network + ## interfaces: + ## ma1: + ## name: ~ + ## syspath: pci0000:00/0000:00:14.0 diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/platform-config/r0/src/python/x86_64_accton_dcg8510_32d_r0/__init__.py b/packages/platforms/accton/x86-64/dcg8510_32d/platform-config/r0/src/python/x86_64_accton_dcg8510_32d_r0/__init__.py new file mode 100644 index 0000000000..159d53d086 --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/platform-config/r0/src/python/x86_64_accton_dcg8510_32d_r0/__init__.py @@ -0,0 +1,95 @@ +from onl.platform.base import * +from onl.platform.accton import * + +class OnlPlatform_x86_64_accton_dcg8510_32d_r0(OnlPlatformAccton, + OnlPlatformPortConfig_32x400): + MODEL="dcg-8510-32d" + PLATFORM="x86-64-accton-dcg8510-32d-r0" + SYS_OBJECT_ID=".8510.32.1" + + def baseconfig(self): + # rootfs overlay + stamp_dirname = os.path.dirname(__file__) + + #rootfs overlay + if not os.path.exists(stamp_dirname + "/rootfs_overlay.stamp"): + os.system("cp -r " + stamp_dirname + "/rootfs_overlay/* /") + os.system("sync /") + os.system("touch " + stamp_dirname + "/rootfs_overlay.stamp") + + + self.insmod('s3ip/switch_s3ip_switch') + self.insmod('s3ip/switch_s3ip_bmc') + self.insmod('s3ip/switch_s3ip_cpld') + self.insmod('s3ip/switch_s3ip_fan') + self.insmod('s3ip/switch_s3ip_fpga') + self.insmod('s3ip/switch_s3ip_psu') + self.insmod('s3ip/switch_s3ip_sensor') + self.insmod('s3ip/switch_s3ip_syseeprom') + self.insmod('s3ip/switch_s3ip_sysled') + self.insmod('s3ip/switch_s3ip_transceiver') + self.insmod('s3ip/switch_s3ip_watchdog') + self.insmod('third_party/switch_system_fpga') + self.insmod('third_party/switch_optoe') + self.insmod('third_party/switch_at24') + self.insmod('third_party/switch_pmbus_core') + self.insmod('third_party/switch_lm75') + self.insmod('third_party/sysfs_ipmi') + self.insmod('third_party/switch_i2c_cpld') + self.insmod('third_party/switch_system_cpld') + self.insmod('switch_mb_driver') + self.insmod('switch_cpld_driver') + self.insmod('switch_fpga_driver') + self.insmod('switch_bmc_driver') + self.insmod('switch_transceiver_driver') + self.insmod('switch_led_driver') + self.insmod('switch_wdt_driver') + self.insmod('switch_fan_driver') + self.insmod('switch_psu_driver') + self.insmod('third_party/switch_coretemp') + self.insmod('switch_sensor_driver') + MODULE_FOLDER="/lib/modules/4.19.81-OpenNetworkLinux/onl/accton/x86-64-accton-dcg8510-32d" + + self.new_i2c_devices([ + ('24c64', 0x57, 0), + ('port_cpld1', 0x62, 634), + ('port_cpld2', 0x64, 635), + ('switch_optoe3', 0x50, 601), + ('switch_optoe3', 0x50, 602), + ('switch_optoe3', 0x50, 603), + ('switch_optoe3', 0x50, 604), + ('switch_optoe3', 0x50, 605), + ('switch_optoe3', 0x50, 606), + ('switch_optoe3', 0x50, 607), + ('switch_optoe3', 0x50, 608), + ('switch_optoe3', 0x50, 609), + ('switch_optoe3', 0x50, 610), + ('switch_optoe3', 0x50, 611), + ('switch_optoe3', 0x50, 612), + ('switch_optoe3', 0x50, 613), + ('switch_optoe3', 0x50, 614), + ('switch_optoe3', 0x50, 615), + ('switch_optoe3', 0x50, 616), + ('switch_optoe3', 0x50, 617), + ('switch_optoe3', 0x50, 618), + ('switch_optoe3', 0x50, 619), + ('switch_optoe3', 0x50, 620), + ('switch_optoe3', 0x50, 621), + ('switch_optoe3', 0x50, 622), + ('switch_optoe3', 0x50, 623), + ('switch_optoe3', 0x50, 624), + ('switch_optoe3', 0x50, 625), + ('switch_optoe3', 0x50, 626), + ('switch_optoe3', 0x50, 627), + ('switch_optoe3', 0x50, 628), + ('switch_optoe3', 0x50, 629), + ('switch_optoe3', 0x50, 630), + ('switch_optoe3', 0x50, 631), + ('switch_optoe3', 0x50, 632), + ]) + commands = [ + "ifconfig usb0 up", + ] + #for cmd in commands: + # process = subprocess.call(cmd, shell=True) + return True diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/platform-config/r0/src/python/x86_64_accton_dcg8510_32d_r0/rootfs_overlay/usr/local/bin/s3ip_temp_monitor.py b/packages/platforms/accton/x86-64/dcg8510_32d/platform-config/r0/src/python/x86_64_accton_dcg8510_32d_r0/rootfs_overlay/usr/local/bin/s3ip_temp_monitor.py new file mode 100644 index 0000000000..e3cbd95e3e --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/platform-config/r0/src/python/x86_64_accton_dcg8510_32d_r0/rootfs_overlay/usr/local/bin/s3ip_temp_monitor.py @@ -0,0 +1,283 @@ +#!/usr/bin/env python + +try: + import getopt + import sys + import syslog + import subprocess + import logging + import logging.config + import logging.handlers + import time # this is only being used as part of the example + #from sonic_platform.bfn_extensions.platform_sensors import * + +except ImportError as e: + raise ImportError('%s - required module not found' % str(e)) + +RET_TRUE = 0 +RET_FALSE = -1 + +FUNCTION_NAME = 'temp-to-bmc' +PRODUCT_NAME = 'dcg8510_32d' + +TEST_MODE = False +DEBUG_MODE = False +SHOW_MODE = False + + +class commands: + @staticmethod + def getstatusoutput(cmd): + try: + data = subprocess.check_output(cmd, shell=True, universal_newlines=True, stderr=subprocess.STDOUT) + status = 0 + except subprocess.CalledProcessError as ex: + data = ex.output + status = ex.returncode + if data[-1:] == '\n': + data = data[:-1] + return status, data + + +############################################################################### +# LOG. + +# priorities (these are ordered) + +LOG_EMERG = 0 # system is unusable +LOG_ALERT = 1 # action must be taken immediately +LOG_CRIT = 2 # critical conditions +LOG_ERR = 3 # error conditions +LOG_WARNING = 4 # warning conditions +LOG_NOTICE = 5 # normal but significant condition +LOG_INFO = 6 # informational +LOG_DEBUG = 7 # debug-level messages + +priority_name = { + LOG_CRIT : "critical", + LOG_DEBUG : "debug", + LOG_WARNING : "warning", + LOG_INFO : "info", +} + +def SYS_LOG(level, msg): + if TEST_MODE: + time_str = time.strftime("%Y-%m-%dT%H:%M:%S", time.localtime()) + print('%s %s: %-12s %s' %(time_str, FUNCTION_NAME, priority_name[level].upper(), msg)) + else: + syslog.syslog(level, msg) + +def DBG_LOG(msg): + if DEBUG_MODE: + level = syslog.LOG_DEBUG + x = PRODUCT_NAME + ' ' + priority_name[level].upper() + ' : ' + msg + SYS_LOG(level, x) + +def SYS_LOG_INFO(msg): + level = syslog.LOG_INFO + x = PRODUCT_NAME + ' ' + priority_name[level].upper() + ' : ' + msg + SYS_LOG(level, x) + +def SYS_LOG_WARN(msg): + level = syslog.LOG_WARNING + x = PRODUCT_NAME + ' ' + priority_name[level].upper() + ' : ' + msg + SYS_LOG(level, x) + +def SYS_LOG_CRITICAL(msg): + level = syslog.LOG_CRIT + x = PRODUCT_NAME + ' ' + priority_name[level].upper() + ' : ' + msg + SYS_LOG(level, x) + +# Deafults +VERSION = '1.0' +FUNCTION_NAME = '/usr/local/bin/dcg8510_32d_temp_to_bmc' +DEBUG = False + +TEMP_TIME = 10 + +def my_log(txt): + if DEBUG == True: + print("[ACCTON DBG]: "+txt) + return + +def log_os_system(cmd): + logging.info('Run :'+cmd) + status = 1 + output = "" + status, output = commands.getstatusoutput(cmd) + if status: + logging.info('Failed :'+cmd) + return status, output + +############################################################################################## +def get_sfp_temperature(): + max_result = 0; + + for port in range(32): + + present_cmd = 'cat /sys/switch/transceiver/eth%d/present' % (port + 1) + ret, present_output = commands.getstatusoutput(present_cmd) + if(RET_TRUE != ret): + DBG_LOG('fail to get SFP(%d) present.' %(port)) + continue + + if (0 == int(present_output)): + continue + + tmp_int = 0; + tmp_cmd = 'cat /sys/switch/transceiver/eth%d/temp_input' % (port + 1) + ret, tmp_output = commands.getstatusoutput(tmp_cmd) + if(RET_TRUE != ret): + DBG_LOG('fail to get SFP(%d) temperature.' %(port + 1)) + continue + + tmp_int = float(tmp_output) + if(tmp_int > max_result): + max_result = tmp_int + + return max_result + +def get_tf2_pipe_temperature(): + max_result = 0.0 + + try: + #max_result = platform_tf2_max_pipe_temp_get() + max_result = 0 + except Exception as e: + repr(e) + return 0 + + if(max_result < 0): + tmp_val = abs(int(max_result-0.5)) + result = ((~tmp_val)&0xff)+1 + else: + result = int(max_result+0.5) + + return result + +def get_x86_core_temperature(): + max_result = 0; + + for x86_core in range(2,6): + tmp_int = 0; + tmp_cmd = 'cat /sys/switch/sensor/temp%d/temp_input' % (x86_core) + ret, tmp_output = commands.getstatusoutput(tmp_cmd) + if(RET_TRUE != ret): + DBG_LOG('fail to get x86_core(%d) temperature.' %x86_core) + continue + + tmp_int = float(tmp_output) + if(tmp_int > max_result): + max_result = tmp_int + + return (max_result/1000) + +def send_sfp_temp_to_bmc(temp): + cmd = 'ipmitool raw 0x36 0x16 %s' % (hex(int(temp))) + + ret, output = commands.getstatusoutput(cmd) + #SYS_LOG_INFO('%s, ret:%d, "%s"' %(cmd, ret, output)) + if(RET_TRUE != ret): + DBG_LOG('fail to send SFP temperature to BMC.') + return RET_FALSE + + return RET_TRUE + +def send_tf2_pipe_temp_to_bmc(temp): + cmd = 'ipmitool raw 0x36 0x17 %s' % (hex(int(temp))) + + ret, output = commands.getstatusoutput(cmd) + #SYS_LOG_INFO('%s, ret:%d, "%s"' %(cmd, ret, output)) + if(RET_TRUE != ret): + DBG_LOG('fail to send tfe pipe temperature to BMC.') + return RET_FALSE + + return RET_TRUE + +def send_x86_core_temp_to_bmc(temp): + cmd = 'ipmitool raw 0x36 0xA3 %s' % (hex(int(temp))) + + ret, output = commands.getstatusoutput(cmd) + #SYS_LOG_INFO('%s, ret:%d, "%s"' %(cmd, ret, output)) + if(RET_TRUE != ret): + DBG_LOG('fail to send x86 core temperature to BMC.') + return RET_FALSE + + return RET_TRUE + +class device_monitor(object): + + def __init__(self, log_file, log_level): + """Needs a logger and a logger level.""" + # set up logging to file + logging.basicConfig( + filename=log_file, + filemode='w', + level=log_level, + format= '[%(asctime)s] {%(pathname)s:%(lineno)d} %(levelname)s - %(message)s', + datefmt='%H:%M:%S' + ) + + # set up logging to console + if log_level == logging.DEBUG: + console = logging.StreamHandler() + console.setLevel(log_level) + formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s') + console.setFormatter(formatter) + logging.getLogger('').addHandler(console) + + sys_handler = handler = logging.handlers.SysLogHandler(address = '/dev/log') + sys_handler.setLevel(logging.WARNING) + logging.getLogger('').addHandler(sys_handler) + + #logging.debug('SET. logfile:%s / loglevel:%d', log_file, log_level) + + def send_temp_to_bmc_progress(self): + #sfp + sfp_tmp = get_sfp_temperature() + send_sfp_temp_to_bmc(round(sfp_tmp)) + + time.sleep(TEMP_TIME) + + #tf2 pipe + tf2_pipe_tmp = get_tf2_pipe_temperature() + send_tf2_pipe_temp_to_bmc(round(tf2_pipe_tmp)) + + time.sleep(TEMP_TIME) + + #x86 core temperature + x86_core_tmp = get_x86_core_temperature() + #send_x86_core_temp_to_bmc(round(x86_core_tmp)) + + + time.sleep(TEMP_TIME) + return True + +def main(argv): + log_file = '%s.log' % FUNCTION_NAME + log_level = logging.INFO + + if len(sys.argv) != 1: + try: + opts, args = getopt.getopt(argv,'hdlr',['lfile=']) + except getopt.GetoptError: + print('A:Usage: %s [-d] [-l ]' % sys.argv[0]) + return 0 + + for opt, arg in opts: + if opt == '-h': + print('B:Usage: %s [-d] [-l ]' % sys.argv[0]) + return 0 + elif opt in ('-d', '--debug'): + log_level = logging.DEBUG + elif opt in ('-l', '--lfile'): + log_file = arg + + monitor = device_monitor(log_file, log_level) + + while True: + #1 heartbeat progress + monitor.send_temp_to_bmc_progress() + +if __name__ == "__main__": + main(sys.argv) diff --git a/packages/platforms/accton/x86-64/dcg8510_32d/s3ip_temp_monitor.py b/packages/platforms/accton/x86-64/dcg8510_32d/s3ip_temp_monitor.py new file mode 100644 index 0000000000..c2eda65f8d --- /dev/null +++ b/packages/platforms/accton/x86-64/dcg8510_32d/s3ip_temp_monitor.py @@ -0,0 +1,284 @@ +#!/usr/bin/env python + +try: + import getopt + import sys + import syslog + import subprocess + import logging + import logging.config + import logging.handlers + import time # this is only being used as part of the example + #from sonic_platform.bfn_extensions.platform_sensors import * + +except ImportError as e: + raise ImportError('%s - required module not found' % str(e)) + +RET_TRUE = 0 +RET_FALSE = -1 + +FUNCTION_NAME = 'temp-to-bmc' +PRODUCT_NAME = 'dcg8510_32d' + +TEST_MODE = False +DEBUG_MODE = False +SHOW_MODE = False + + +class commands: + @staticmethod + def getstatusoutput(cmd): + try: + data = subprocess.check_output(cmd, shell=True, universal_newlines=True, stderr=subprocess.STDOUT) + status = 0 + except subprocess.CalledProcessError as ex: + data = ex.output + status = ex.returncode + if data[-1:] == '\n': + data = data[:-1] + return status, data + + +############################################################################### +# LOG. + +# priorities (these are ordered) + +LOG_EMERG = 0 # system is unusable +LOG_ALERT = 1 # action must be taken immediately +LOG_CRIT = 2 # critical conditions +LOG_ERR = 3 # error conditions +LOG_WARNING = 4 # warning conditions +LOG_NOTICE = 5 # normal but significant condition +LOG_INFO = 6 # informational +LOG_DEBUG = 7 # debug-level messages + +priority_name = { + LOG_CRIT : "critical", + LOG_DEBUG : "debug", + LOG_WARNING : "warning", + LOG_INFO : "info", +} + +def SYS_LOG(level, msg): + if TEST_MODE: + time_str = time.strftime("%Y-%m-%dT%H:%M:%S", time.localtime()) + print('%s %s: %-12s %s' %(time_str, FUNCTION_NAME, priority_name[level].upper(), msg)) + else: + syslog.syslog(level, msg) + +def DBG_LOG(msg): + if DEBUG_MODE: + level = syslog.LOG_DEBUG + x = PRODUCT_NAME + ' ' + priority_name[level].upper() + ' : ' + msg + SYS_LOG(level, x) + +def SYS_LOG_INFO(msg): + level = syslog.LOG_INFO + x = PRODUCT_NAME + ' ' + priority_name[level].upper() + ' : ' + msg + SYS_LOG(level, x) + +def SYS_LOG_WARN(msg): + level = syslog.LOG_WARNING + x = PRODUCT_NAME + ' ' + priority_name[level].upper() + ' : ' + msg + SYS_LOG(level, x) + +def SYS_LOG_CRITICAL(msg): + level = syslog.LOG_CRIT + x = PRODUCT_NAME + ' ' + priority_name[level].upper() + ' : ' + msg + SYS_LOG(level, x) + +# Deafults +VERSION = '1.0' +FUNCTION_NAME = '/usr/local/bin/dcg8510_32d_temp_to_bmc' +DEBUG = False + +TEMP_TIME = 10 +#simon: for debug +TEMP_TIME = 1 + +def my_log(txt): + if DEBUG == True: + print("[ACCTON DBG]: "+txt) + return + +def log_os_system(cmd): + logging.info('Run :'+cmd) + status = 1 + output = "" + status, output = commands.getstatusoutput(cmd) + if status: + logging.info('Failed :'+cmd) + return status, output + +############################################################################################## +def get_sfp_temperature(): + max_result = 0; + + for port in range(32): + + present_cmd = 'cat /sys/switch/transceiver/eth%d/present' % (port + 1) + ret, present_output = commands.getstatusoutput(present_cmd) + if(RET_TRUE != ret): + DBG_LOG('fail to get SFP(%d) present.' %(port)) + continue + + if (0 == int(present_output)): + continue + + tmp_int = 0; + tmp_cmd = 'cat /sys/switch/transceiver/eth%d/temp_input' % (port + 1) + ret, tmp_output = commands.getstatusoutput(tmp_cmd) + if(RET_TRUE != ret): + DBG_LOG('fail to get SFP(%d) temperature.' %(port + 1)) + continue + + tmp_int = float(tmp_output) + if(tmp_int > max_result): + max_result = tmp_int + + return max_result + +def get_tf2_pipe_temperature(): + max_result = 0.0 + + try: + #max_result = platform_tf2_max_pipe_temp_get() + max_result = 0 + except Exception as e: + repr(e) + return 0 + + if(max_result < 0): + tmp_val = abs(int(max_result-0.5)) + result = ((~tmp_val)&0xff)+1 + else: + result = int(max_result+0.5) + + return result + +def get_x86_core_temperature(): + max_result = 0; + + for x86_core in range(2,6): + tmp_int = 0; + tmp_cmd = 'cat /sys/switch/sensor/temp%d/temp_input' % (x86_core + 1) + ret, tmp_output = commands.getstatusoutput(tmp_cmd) + if(RET_TRUE != ret): + DBG_LOG('fail to get x86_core(%d) temperature.' %x86_core) + continue + + tmp_int = float(tmp_output) + if(tmp_int > max_result): + max_result = tmp_int + + return max_result + +def send_sfp_temp_to_bmc(temp): + cmd = 'ipmitool raw 0x36 0x16 %d' % (temp) + + ret, output = commands.getstatusoutput(cmd) + #SYS_LOG_INFO('%s, ret:%d, "%s"' %(cmd, ret, output)) + if(RET_TRUE != ret): + DBG_LOG('fail to send SFP temperature to BMC.') + return RET_FALSE + + return RET_TRUE + +def send_tf2_pipe_temp_to_bmc(temp): + cmd = 'ipmitool raw 0x36 0x17 %d' % (temp) + + ret, output = commands.getstatusoutput(cmd) + #SYS_LOG_INFO('%s, ret:%d, "%s"' %(cmd, ret, output)) + if(RET_TRUE != ret): + DBG_LOG('fail to send tfe pipe temperature to BMC.') + return RET_FALSE + + return RET_TRUE + +def send_x86_core_temp_to_bmc(temp): + cmd = 'ipmitool raw 0x36 0xA3 %d' % (temp) + + ret, output = commands.getstatusoutput(cmd) + #SYS_LOG_INFO('%s, ret:%d, "%s"' %(cmd, ret, output)) + if(RET_TRUE != ret): + DBG_LOG('fail to send x86 core temperature to BMC.') + return RET_FALSE + + return RET_TRUE + +class device_monitor(object): + + def __init__(self, log_file, log_level): + """Needs a logger and a logger level.""" + # set up logging to file + logging.basicConfig( + filename=log_file, + filemode='w', + level=log_level, + format= '[%(asctime)s] {%(pathname)s:%(lineno)d} %(levelname)s - %(message)s', + datefmt='%H:%M:%S' + ) + + # set up logging to console + if log_level == logging.DEBUG: + console = logging.StreamHandler() + console.setLevel(log_level) + formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s') + console.setFormatter(formatter) + logging.getLogger('').addHandler(console) + + sys_handler = handler = logging.handlers.SysLogHandler(address = '/dev/log') + sys_handler.setLevel(logging.WARNING) + logging.getLogger('').addHandler(sys_handler) + + #logging.debug('SET. logfile:%s / loglevel:%d', log_file, log_level) + + def send_temp_to_bmc_progress(self): + #sfp + sfp_tmp = get_sfp_temperature() + send_sfp_temp_to_bmc(sfp_tmp) + + time.sleep(TEMP_TIME) + + #tf2 pipe + tf2_pipe_tmp = get_tf2_pipe_temperature() + send_tf2_pipe_temp_to_bmc(tf2_pipe_tmp) + + time.sleep(TEMP_TIME) + + #x86 core temperature + x86_core_tmp = get_x86_core_temperature() + send_x86_core_temp_to_bmc(x86_core_tmp) + + time.sleep(TEMP_TIME) + return True + +def main(argv): + log_file = '%s.log' % FUNCTION_NAME + log_level = logging.INFO + + if len(sys.argv) != 1: + try: + opts, args = getopt.getopt(argv,'hdlr',['lfile=']) + except getopt.GetoptError: + print('A:Usage: %s [-d] [-l ]' % sys.argv[0]) + return 0 + + for opt, arg in opts: + if opt == '-h': + print('B:Usage: %s [-d] [-l ]' % sys.argv[0]) + return 0 + elif opt in ('-d', '--debug'): + log_level = logging.DEBUG + elif opt in ('-l', '--lfile'): + log_file = arg + + monitor = device_monitor(log_file, log_level) + + while True: + #1 heartbeat progress + monitor.send_temp_to_bmc_progress() + +if __name__ == "__main__": + main(sys.argv)