diff --git a/os/LICENSES.md b/os/LICENSES.md index 19959432..eab89162 100644 --- a/os/LICENSES.md +++ b/os/LICENSES.md @@ -86,6 +86,17 @@ says `Ship: yes`. | Lemonade (lemond, the lemonade CLI, the web app) | third_party/lemonade pin | Apache-2.0, Advanced Micro Devices, Inc.; web app bundle licenses in `renderer.bundle.js.LICENSE.txt` | https://github.com/1bit-MONSTER/lemonade | | ROCr HSA runtime (TheRock, for HRX) | the build's ONEBIT_HRX_LIBHSA | University of Illinois/NCSA | https://github.com/ROCm/ROCR-Runtime, https://github.com/ROCm/TheRock | +## The board driver and the SMU tool + +The appliance controls its fans and its power profile on the boards it knows: an out-of-tree driver for +the board's embedded controller, and (when the build machine has it) the tool that sets the SMU power +limits. Both are shipped with their licences, and the driver also with its source. + +| Component | Release | License | Source | +|---|---|---|---| +| `ec_su_axb35` (fan curve, EC temperature, EC power mode on the AXB35-02 board) | commit `f62c2c228959a08683273a26ef3afd8991e69f6d`, built for the image kernel by `os/mini/mkimage.sh` | GPL-2.0-only | `os/licenses/ec_su_axb35/` in this repository; the same source is installed in the image at `/usr/share/licenses/1bit-os/ec_su_axb35/src`; upstream https://github.com/cmetz/ec-su_axb35-linux | +| `ryzenadj` (sets the SMU power limits; `/opt/1bit/ryzenadj` in the image) | **pinned v0.19.0**, commit `ea71739b4a3e1a0a624dfd1c9c268a31cb2a4182`, built by `os/mini/mkimage.sh` from the source in `os/licenses/ryzenadj` | LGPL-3.0, Copyright Jiaxun Yang and the RyzenAdj contributors | `os/licenses/ryzenadj/` in this repository (LICENSE, SOURCE, and the tag's source verbatim); the same source is installed in the image at `/usr/share/licenses/1bit-os/ryzenadj/src`; upstream https://github.com/FlyGoat/RyzenAdj | + ## System software from Ubuntu mkimage.sh copies these from the build machine (Ubuntu 26.04), unmodified, and records the exact @@ -123,4 +134,6 @@ source available; the MPL requires the same for its covered files. For each: | eSpeak NG in kokoro's `koko`, and Kokoros | https://crates.io/api/v1/crates/espeak-rs-sys/0.1.9/download (eSpeak NG as built into koko); https://github.com/lemonade-sdk/Kokoros/tree/b17 | | TheRock sysdeps in llamacpp-hrx (elfutils, libnl, numactl, libmnl) | the upstream tarballs TheRock builds, https://github.com/ROCm/TheRock/tree/main/third-party/sysdeps/linux (elfutils 0.195, libnl 3.12.0, numactl 2.0.19, libmnl 1.0.5 at the time of this review) | | libquadmath, libgfortran in moonshine | GCC, https://gcc.gnu.org | +| `ec_su_axb35` (the board driver in the image) | the source as shipped in `os/licenses/ec_su_axb35/src`, upstream https://github.com/cmetz/ec-su_axb35-linux at commit `f62c2c228959a08683273a26ef3afd8991e69f6d` | +| `ryzenadj` | the source as shipped in `os/licenses/ryzenadj/src`, upstream https://github.com/FlyGoat/RyzenAdj at tag v0.19.0 (commit `ea71739b4a3e1a0a624dfd1c9c268a31cb2a4182`), built by `os/mini/mkimage.sh` | | certifi, tqdm in moonshine (MPL-2.0) | the covered files are on the image as shipped (Python source and cacert.pem); https://pypi.org/project/certifi/, https://pypi.org/project/tqdm/ | diff --git a/os/licenses/ec_su_axb35/LICENSE b/os/licenses/ec_su_axb35/LICENSE new file mode 100644 index 00000000..d159169d --- /dev/null +++ b/os/licenses/ec_su_axb35/LICENSE @@ -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/os/licenses/ec_su_axb35/SOURCE b/os/licenses/ec_su_axb35/SOURCE new file mode 100644 index 00000000..e8686074 --- /dev/null +++ b/os/licenses/ec_su_axb35/SOURCE @@ -0,0 +1,11 @@ +Board driver: ec_su_axb35 (Sixunited AXB35-02 embedded controller: fans, EC temperature, EC power mode) +Ship: yes +Project: ec-su_axb35-linux, https://github.com/cmetz/ec-su_axb35-linux +Release: commit f62c2c228959a08683273a26ef3afd8991e69f6d ("Reset all fans to AUTO mode before unloading module (#31)") +License: GPL-2.0 (LICENSE, shipped here); the module source in src/ is that commit, verbatim, and it is +also installed into the image under /usr/share/licenses/1bit-os/ec_su_axb35/src, so the binary in the +image always travels with the source it was built from. +Build: out of tree against the image kernel, by os/mini/mkimage.sh: + make -C KERNEL_BUILD=/lib/modules//build modules +Why it ships: the kernel has no driver for this EC, so without it the appliance has no fan control and no +EC power mode at all - the fans would sit on the firmware curve. diff --git a/os/licenses/ec_su_axb35/src/.clang-format b/os/licenses/ec_su_axb35/src/.clang-format new file mode 100644 index 00000000..01572da8 --- /dev/null +++ b/os/licenses/ec_su_axb35/src/.clang-format @@ -0,0 +1,119 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# clang-format configuration file. Intended for clang-format >= 11. +# +# For more information, see: +# +# Documentation/dev-tools/clang-format.rst +# https://clang.llvm.org/docs/ClangFormat.html +# https://clang.llvm.org/docs/ClangFormatStyleOptions.html +# +--- +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: true +AlignConsecutiveDeclarations: true +AlignEscapedNewlines: Left +AlignOperands: true +AlignTrailingComments: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: false +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Custom +BreakBeforeInheritanceComma: false +BreakBeforeTernaryOperators: false +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeComma +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: false +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: false +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: false +ForEachMacros: +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '.*' + Priority: 1 +IncludeIsMainRegex: '(Test)?$' +IndentCaseLabels: false +IndentGotoLabels: false +IndentPPDirectives: None +IndentWidth: 4 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 4 +ObjCSpaceAfterProperty: true +ObjCSpaceBeforeProtocolList: true + +# Taken from git's rules +PenaltyBreakAssignment: 10 +PenaltyBreakBeforeFirstCallParameter: 30 +PenaltyBreakComment: 10 +PenaltyBreakFirstLessLess: 0 +PenaltyBreakString: 10 +PenaltyExcessCharacter: 100 +PenaltyReturnTypeOnItsOwnLine: 60 + +PointerAlignment: Right +ReflowComments: true +SortIncludes: true +SortUsingDeclarations: false +SpaceAfterCStyleCast: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatementsExceptForEachMacros +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: false +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp03 +TabWidth: 4 +UseTab: Never +... diff --git a/os/licenses/ec_su_axb35/src/.clangd b/os/licenses/ec_su_axb35/src/.clangd new file mode 100644 index 00000000..99df3955 --- /dev/null +++ b/os/licenses/ec_su_axb35/src/.clangd @@ -0,0 +1,3 @@ +CompileFlags: + Add: -Wno-unknown-warning-option + Remove: [-m*, -f*] \ No newline at end of file diff --git a/os/licenses/ec_su_axb35/src/.gitignore b/os/licenses/ec_su_axb35/src/.gitignore new file mode 100644 index 00000000..c69f359f --- /dev/null +++ b/os/licenses/ec_su_axb35/src/.gitignore @@ -0,0 +1,56 @@ +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf +.direnv +.cache +compile_commands.json +.envrc diff --git a/os/licenses/ec_su_axb35/src/Kbuild b/os/licenses/ec_su_axb35/src/Kbuild new file mode 100644 index 00000000..83b275aa --- /dev/null +++ b/os/licenses/ec_su_axb35/src/Kbuild @@ -0,0 +1,2 @@ +obj-m += ec_su_axb35.o +ec_su_axb35-y := src/ec_su_axb35.o diff --git a/os/licenses/ec_su_axb35/src/LICENSE b/os/licenses/ec_su_axb35/src/LICENSE new file mode 100644 index 00000000..d159169d --- /dev/null +++ b/os/licenses/ec_su_axb35/src/LICENSE @@ -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/os/licenses/ec_su_axb35/src/Makefile b/os/licenses/ec_su_axb35/src/Makefile new file mode 100644 index 00000000..da7e717c --- /dev/null +++ b/os/licenses/ec_su_axb35/src/Makefile @@ -0,0 +1,45 @@ +# Makefile for ec_su_axb35 + +KERNEL_BUILD ?= /lib/modules/$(shell uname -r)/build +MODULE_NAME := ec_su_axb35.ko +MODULE_INSTALLED_PATH := $(shell modinfo -n $(basename $(MODULE_NAME)) 2>/dev/null) +PWD := $(CURDIR) + +.PHONY: default +default: modules + +.PHONY: modules +modules: + $(MAKE) -C $(KERNEL_BUILD) M=$(PWD) modules + +.PHONY: modules_install +modules_install: modules + $(MAKE) -C $(KERNEL_BUILD) M=$(PWD) modules_install + +.PHONY: clean +clean: + $(MAKE) -C $(KERNEL_BUILD) M=$(PWD) clean + rm -rf $(BUILD_DIR) + +.PHONY: install +install: modules_install + depmod + @echo "Installing su_axb35_monitor script to /usr/local/bin/" + install -m 0755 scripts/su_axb35_monitor /usr/local/bin/ + +.PHONY: uninstall +uninstall: + @if [ -n "$(MODULE_INSTALLED_PATH)" ]; then \ + echo "Removing $(MODULE_INSTALLED_PATH)"; \ + rm -v "$(MODULE_INSTALLED_PATH)"; \ + depmod; \ + else \ + echo "Module not found."; \ + fi + @if [ -f "/usr/local/bin/su_axb35_monitor" ]; then \ + echo "Removing /usr/local/bin/su_axb35_monitor"; \ + rm -v "/usr/local/bin/su_axb35_monitor"; \ + else \ + echo "Monitor script not found."; \ + fi + diff --git a/os/licenses/ec_su_axb35/src/README.md b/os/licenses/ec_su_axb35/src/README.md new file mode 100644 index 00000000..e2d8589a --- /dev/null +++ b/os/licenses/ec_su_axb35/src/README.md @@ -0,0 +1,51 @@ +# ec-su_axb35-linux +Linux driver for the embedded controller on the Sixunited AXB35-02 board. + +Vendors using that board: + - GMKtec EVO-X2 + - Bosgame M5 + - FEVM FA-EX9 + - Peladn YO1 + - NIMO AI MiniPC + - Corsair AI Workstation 300 + +An update to date list can be found [here](https://strixhalo-homelab.d7.wtf/Hardware/Boards/Sixunited-AXB35) + +For more details, please have a look at the desevens wiki: https://strixhalo-homelab.d7.wtf/Guides/Power-Mode-and-Fan-Control + +# Build instructions +``` +$ make +$ sudo make install +$ sudo insmod ec_su_axb35 +``` + +# Devices +``` +# Fan devices +/sys/class/ec_su_axb35/fan1/ - CPU fan 1 +/sys/class/ec_su_axb35/fan2/ - CPU fan 2 +/sys/class/ec_su_axb35/fan3/ - System fan +/sys/class/ec_su_axb35/fanX/rpm (RO) - current speed in rpm +/sys/class/ec_su_axb35/fanX/mode (RW) - [auto, fixed, curve] +/sys/class/ec_su_axb35/fanX/level (RW) - [0-5] (0=0%, 1=20%, ..., 5=100%) +/sys/class/ec_su_axb35/fanX/rampup_curve (RW) - 5 values (°C thresholds for level 1-5) +/sys/class/ec_su_axb35/fanX/rampdown_curve (RW) - 5 values (°C thresholds for level 1-5) + +# Temperature device +/sys/class/ec_su_axb35/temp1/ - CPU temperature in °C +/sys/class/ec_su_axb35/temp1/temp (RO) - current +/sys/class/ec_su_axb35/temp1/min (RO) - min temp measured since dirver load +/sys/class/ec_su_axb35/temp1/max (RO) - amx temp measured since driver load + +# APU device +/sys/class/ec_su_axb35/apu/power_mode (RW) - [quiet, balanced, performance] +``` + +# Python GUI app (needs root to write to /sys/class/ec_su_axb35/*) +to test: +python ./ec-su_axb35-linux-gui.py + +to install: +sudo install -m 755 ec-su_axb35-linux-gui.py /usr/local/bin/ec-su_axb35-linux-gui +cp ec-fan-control.desktop ~/.local/share/applications/ diff --git a/os/licenses/ec_su_axb35/src/contrib/netdata/README.md b/os/licenses/ec_su_axb35/src/contrib/netdata/README.md new file mode 100644 index 00000000..2b2e48c0 --- /dev/null +++ b/os/licenses/ec_su_axb35/src/contrib/netdata/README.md @@ -0,0 +1,7 @@ +# Netdata Plugin +This plugin adds all key metrics from the module to your dashboard. + +### Installation & Usage +1. Copy `axb35.chart.sh` to `/usr/libexec/netdata/charts.d`. +2. Restart your Netdata service (`systemctl restart netdata`). +3. New section called `axb35` should appear on your dashboard. \ No newline at end of file diff --git a/os/licenses/ec_su_axb35/src/contrib/netdata/axb35.chart.sh b/os/licenses/ec_su_axb35/src/contrib/netdata/axb35.chart.sh new file mode 100755 index 00000000..132e8e8d --- /dev/null +++ b/os/licenses/ec_su_axb35/src/contrib/netdata/axb35.chart.sh @@ -0,0 +1,111 @@ +# params +axb35_update_every=1 +axb35_priority=1 +axb35_path=/sys/class/ec_su_axb35 +declare -a axb35_vars + +# main metrics function +axb35_get() { + if [ ! -d $axb35_path ]; then + return 1 + fi + + # read all values from the module + mapfile -t axb35_vars < <( + cat \ + $axb35_path/fan{1..3}/rpm \ + $axb35_path/fan{1..3}/mode \ + $axb35_path/fan{1..3}/level \ + $axb35_path/temp1/{temp,min,max} \ + $axb35_path/apu/power_mode + ) + + # convert fan modes to numeric values + for i in {3..5}; do + case "${axb35_vars[i]}" in + "fixed") axb35_vars[i]=3 ;; + "curve") axb35_vars[i]=2 ;; + "auto") + axb35_vars[i]=1 + axb35_vars[(($i+3))]=6 + ;; + *) axb35_vars[i]=0 ;; # unknown mode + esac + done + + # convert power mode to numeric value + case "${axb35_vars[12]}" in + "performance") axb35_vars[12]=3 ;; + "balanced") axb35_vars[12]=2 ;; + "quiet") axb35_vars[12]=1 ;; + *) axb35_vars[12]=0 ;; # unknown mode + esac + + return 0 +} + +# called once to find out if this chart should be enabled or not +axb35_check() { + [ ! -d $axb35_path ] && error "can't find AXB35 class in $axb35_path" && return 1 + return 0 +} + +# called once to create the charts +axb35_create() { + cat << EOF +CHART axb35.cputemp 'axb351' "CPU Temp" "Degrees" "CPU Temperature" '' line $((axb35_priority)) $axb35_update_every '' '' 'axb35' +DIMENSION cputemp '' absolute 1 1 +DIMENSION cputempmin '' absolute 1 1 +DIMENSION cputempmax '' absolute 1 1 +CHART axb35.fanrpm 'axb352' "Fan RPMs" "rpm" "Fan RPMs" '' stacked $((axb35_priority+1)) $axb35_update_every '' '' 'axb35' +DIMENSION fan1rpm '' absolute 1 1 +DIMENSION fan2rpm '' absolute 1 1 +DIMENSION fan3rpm '' absolute 1 1 +CHART axb35.fanmode 'axb353' "Fan Modes (1 = Auto, 2 = Curve, 3 = Manual)" "Mode" "Fan Modes" '' line $((axb35_priority+2)) $axb35_update_every '' '' 'axb35' +DIMENSION fan1mode '' absolute 1 1 +DIMENSION fan2mode '' absolute 1 1 +DIMENSION fan3mode '' absolute 1 1 +CHART axb35.fanlevel 'axb354' "Fan Power Levels (6 = Auto)" "Level" "Fan Power Levels" '' line $((axb35_priority+3)) $axb35_update_every '' '' 'axb35' +DIMENSION fan1level '' absolute 1 1 +DIMENSION fan2level '' absolute 1 1 +DIMENSION fan3level '' absolute 1 1 +CHART axb35.powermode 'axb355' "Power Mode (1 = Quiet, 2 = Balanced, 3 = Performance)" "Mode" "Power Modes" '' line $((axb35_priority+4)) $axb35_update_every '' '' 'axb35' +DIMENSION powermode '' absolute 1 1 +EOF + + return 0 +} + +# called continuously to collect the values +axb35_update() { + axb35_get || return 1 + + cat << VALUESEOF +BEGIN axb35.fanrpm $1 +SET fan1rpm = ${axb35_vars[0]} +SET fan2rpm = ${axb35_vars[1]} +SET fan3rpm = ${axb35_vars[2]} +END +BEGIN axb35.fanmode $1 +SET fan1mode = ${axb35_vars[3]} +SET fan2mode = ${axb35_vars[4]} +SET fan3mode = ${axb35_vars[5]} +END +BEGIN axb35.fanlevel $1 +SET fan1level = ${axb35_vars[6]} +SET fan2level = ${axb35_vars[7]} +SET fan3level = ${axb35_vars[8]} +END +BEGIN axb35.cputemp $1 +SET cputemp = ${axb35_vars[9]} +SET cputempmin = ${axb35_vars[10]} +SET cputempmax = ${axb35_vars[11]} +END +BEGIN axb35.powermode $1 +SET powermode = ${axb35_vars[12]} +END + +VALUESEOF + + return 0 +} diff --git a/os/licenses/ec_su_axb35/src/contrib/ubuntu/01_setup_dkms_mok.sh b/os/licenses/ec_su_axb35/src/contrib/ubuntu/01_setup_dkms_mok.sh new file mode 100755 index 00000000..3ca82873 --- /dev/null +++ b/os/licenses/ec_su_axb35/src/contrib/ubuntu/01_setup_dkms_mok.sh @@ -0,0 +1,72 @@ +#!/usr/bin/env bash +# 01_setup_dkms_mok.sh +# Sets up DKMS and enrolls a MOK key for secure boot module signing. +# Run this first, then reboot and complete MOK enrollment before running script 2. +# Tested under Ubuntu 26.04 + +set -euo pipefail + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +info() { echo -e "${GREEN}[INFO]${NC} $*"; } +warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } +error() { echo -e "${RED}[ERROR]${NC} $*"; exit 1; } + +MOK_KEY="/var/lib/shim-signed/mok/MOK.priv" +MOK_CERT="/var/lib/shim-signed/mok/MOK.der" + +[[ $EUID -ne 0 ]] && error "Run as root (sudo $0)" + +# Check secure boot state +if ! mokutil --sb-state 2>/dev/null | grep -q "SecureBoot enabled"; then + warn "Secure boot does not appear to be enabled." + read -rp "Continue anyway? [y/N] " confirm + [[ "$confirm" =~ ^[Yy]$ ]] || exit 0 +fi + +# Check if MOK already exists +if [[ -f "$MOK_KEY" && -f "$MOK_CERT" ]]; then + info "MOK key pair already exists at ${MOK_KEY} and ${MOK_CERT}" + info "Skipping key generation." +else + info "Installing dkms and openssl..." + apt-get install -y dkms openssl + + info "Generating DKMS MOK key pair..." + dkms generate_mok + info "MOK key pair created at ${MOK_KEY} and ${MOK_CERT}" +fi + +# Enroll the key +info "Enrolling MOK public key..." +echo "" +warn "You will be prompted to set a password for MOK enrollment." +warn "Password MUST be 5 characters or fewer (UEFI keyboard limitation)." +warn "You will need this password at the next boot screen." +echo "" + +mokutil --import "$MOK_CERT" + +info "MOK enrollment request submitted." +echo "" +echo -e "${YELLOW}========================================${NC}" +echo -e "${YELLOW} REBOOT REQUIRED${NC}" +echo -e "${YELLOW}========================================${NC}" +echo "" +echo "At the blue MOK Manager screen:" +echo " 1. Press any key" +echo " 2. Select 'Enroll MOK'" +echo " 3. Select 'Continue'" +echo " 4. Select 'Yes'" +echo " 5. Enter the password you just set" +echo " 6. Select 'Reboot'" +echo "" +echo "After reboot, run: sudo bash contrib/ubuntu/02_install_ec_axb35_dkms.sh" +echo "" +read -rp "Reboot now? [y/N] " doreboot +if [[ "$doreboot" =~ ^[Yy]$ ]]; then + systemctl reboot +fi diff --git a/os/licenses/ec_su_axb35/src/contrib/ubuntu/02_install_ec_axb35_dkms.sh b/os/licenses/ec_su_axb35/src/contrib/ubuntu/02_install_ec_axb35_dkms.sh new file mode 100755 index 00000000..0c5d0975 --- /dev/null +++ b/os/licenses/ec_su_axb35/src/contrib/ubuntu/02_install_ec_axb35_dkms.sh @@ -0,0 +1,120 @@ +#!/usr/bin/env bash +# 02_install_ec_axb35_dkms.sh +# Installs the ec_su_axb35 kernel module via DKMS with MOK signing. +# Must be run from the contrib/ubuntu/ directory inside the cloned ec-su_axb35-linux repo. +# Requires 01_setup_dkms_mok.sh to have been run and MOK enrolled at boot. +# Tested under Ubuntu 26.04 + +set -euo pipefail + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +info() { echo -e "${GREEN}[INFO]${NC} $*"; } +warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } +error() { echo -e "${RED}[ERROR]${NC} $*"; exit 1; } + +MOK_KEY="/var/lib/shim-signed/mok/MOK.priv" +MOK_CERT="/var/lib/shim-signed/mok/MOK.der" + +[[ $EUID -ne 0 ]] && error "Run as root (sudo $0)" + +# Resolve repo root from script location +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && cd .. && pwd)" + +# Sanity check we're in the right repo +[[ -f "${REPO_ROOT}/Makefile" && -f "${REPO_ROOT}/Kbuild" ]] \ + || error "Could not find Makefile/Kbuild at ${REPO_ROOT}. Is this script in the repo's scripts/ dir?" + +MODULE_NAME="ec_su_axb35" +MODULE_VERSION="1.0" +DKMS_SRC="/usr/src/${MODULE_NAME}-${MODULE_VERSION}" + +# Verify MOK keys exist +[[ -f "$MOK_KEY" && -f "$MOK_CERT" ]] \ + || error "MOK keys not found at ${MOK_KEY} / ${MOK_CERT}. Run 01_setup_dkms_mok.sh first." + +# Verify MOK is enrolled +if mokutil --sb-state 2>/dev/null | grep -q "SecureBoot enabled"; then + mokutil --list-enrolled 2>/dev/null | grep "Secure Boot Module Signature key" + if !mokutil --test-key "$MOK_CERT" 2> /dev/null | grep -q "is already enrolled"; then + warn "MOK key doesn't appear to be enrolled yet." + warn "Did you complete the MOK enrollment at the boot screen?" + read -rp "Continue anyway? [y/N] " confirm + [[ "$confirm" =~ ^[Yy]$ ]] || exit 0 + else + info "MOK key confirmed enrolled." + fi +fi + +# Install build deps +info "Installing build dependencies..." +apt-get install -y dkms build-essential "linux-headers-$(uname -r)" + +# Remove existing DKMS entry + source if present +if dkms status "${MODULE_NAME}" 2>/dev/null | grep -q "${MODULE_NAME}"; then + warn "Removing existing DKMS entry for ${MODULE_NAME}..." + dkms remove "${MODULE_NAME}/${MODULE_VERSION}" --all 2>/dev/null || true +fi +[[ -d "$DKMS_SRC" ]] && rm -rf "$DKMS_SRC" + +# Copy repo root into DKMS source tree +info "Copying repo source to ${DKMS_SRC}..." +cp -r "${REPO_ROOT}" "${DKMS_SRC}" + +# Write dkms.conf into the DKMS source tree +info "Writing dkms.conf..." +cat > "${DKMS_SRC}/dkms.conf" << EOF +PACKAGE_NAME="${MODULE_NAME}" +PACKAGE_VERSION="${MODULE_VERSION}" +BUILT_MODULE_NAME[0]="${MODULE_NAME}" +DEST_MODULE_LOCATION[0]="/updates" +AUTOINSTALL="yes" +POST_BUILD="sign-file sha256 ${MOK_KEY} ${MOK_CERT} \${dkms_tree}/\${PACKAGE_NAME}/\${PACKAGE_VERSION}/\${kernelver}/\${arch}/module/\${BUILT_MODULE_NAME[0]}.ko" +EOF + +# DKMS add / build / install +info "Adding module to DKMS..." +dkms add "${MODULE_NAME}/${MODULE_VERSION}" + +info "Building module..." +dkms build "${MODULE_NAME}/${MODULE_VERSION}" + +info "Installing module..." +dkms install "${MODULE_NAME}/${MODULE_VERSION}" + +info "DKMS status:" +dkms status "${MODULE_NAME}" + +# Load module +info "Loading module..." +modprobe "${MODULE_NAME}" + +if dmesg | grep -q "Sixunited AXB35-02 EC driver loaded"; then + info "Module loaded successfully." +else + warn "Module loaded but expected dmesg message not found. Check: dmesg | grep -i axb35" +fi + +# Auto-load on boot +if grep -q "^${MODULE_NAME}$" /etc/modules 2>/dev/null; then + info "${MODULE_NAME} already in /etc/modules, skipping." +else + info "Adding ${MODULE_NAME} to /etc/modules..." + echo "${MODULE_NAME}" >> /etc/modules +fi + +echo "" +echo -e "${GREEN}========================================${NC}" +echo -e "${GREEN} Done!${NC}" +echo -e "${GREEN}========================================${NC}" +echo "" +echo "Read power mode: cat /sys/class/ec_su_axb35/apu/power_mode" +echo "Set power mode: echo performance > /sys/class/ec_su_axb35/apu/power_mode" +echo " echo balanced > /sys/class/ec_su_axb35/apu/power_mode" +echo " echo quiet > /sys/class/ec_su_axb35/apu/power_mode" +echo "Live monitor: su_axb35_monitor" +echo "" diff --git a/os/licenses/ec_su_axb35/src/hwmon/Makefile b/os/licenses/ec_su_axb35/src/hwmon/Makefile new file mode 100644 index 00000000..ad5fa4e8 --- /dev/null +++ b/os/licenses/ec_su_axb35/src/hwmon/Makefile @@ -0,0 +1,10 @@ +obj-m += su_axb35_hwmon.o + +KDIR := /lib/modules/$(shell uname -r)/build +PWD := $(shell pwd) + +all: + $(MAKE) -C $(KDIR) M=$(PWD) modules + +clean: + $(MAKE) -C $(KDIR) M=$(PWD) clean diff --git a/os/licenses/ec_su_axb35/src/hwmon/README.md b/os/licenses/ec_su_axb35/src/hwmon/README.md new file mode 100644 index 00000000..73834291 --- /dev/null +++ b/os/licenses/ec_su_axb35/src/hwmon/README.md @@ -0,0 +1,11 @@ +# ec-su_axb35-hwmon +Linux model ko file to show fan and temp in sensors + +Vendors using that board: + - GMKTec EVO-X2 + +# Build instructions +``` +$ make +$ sudo insmod ec-su_axb35-hwmon +$ sensors diff --git a/os/licenses/ec_su_axb35/src/hwmon/ec-su_axb35-hwmon.c b/os/licenses/ec_su_axb35/src/hwmon/ec-su_axb35-hwmon.c new file mode 100644 index 00000000..a62cd5cc --- /dev/null +++ b/os/licenses/ec_su_axb35/src/hwmon/ec-su_axb35-hwmon.c @@ -0,0 +1,200 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +static int read_fan_rpm_from_sysfs(int fan_num) +{ + char path[128]; + char buffer[32]; + struct file *filp; + loff_t pos = 0; + ssize_t bytes_read; + int rpm = 0; + + snprintf(path, sizeof(path), "/sys/class/ec_su_axb35/fan%d/rpm", fan_num + 1); + + filp = filp_open(path, O_RDONLY, 0); + if (IS_ERR(filp)) + return PTR_ERR(filp); + + bytes_read = kernel_read(filp, buffer, sizeof(buffer) - 1, &pos); + if (bytes_read > 0) { + buffer[bytes_read] = '\0'; + kstrtoint(buffer, 10, &rpm); + } + + filp_close(filp, NULL); + return rpm; +} + +static int read_temperature_from_sysfs(void) +{ + char path[] = "/sys/class/ec_su_axb35/temp1/temp"; + char buffer[32]; + struct file *filp; + loff_t pos = 0; + ssize_t bytes_read; + int temp = 0; + + filp = filp_open(path, O_RDONLY, 0); + if (IS_ERR(filp)) + return PTR_ERR(filp); + + bytes_read = kernel_read(filp, buffer, sizeof(buffer) - 1, &pos); + if (bytes_read > 0) { + buffer[bytes_read] = '\0'; + kstrtoint(buffer, 10, &temp); + } + + filp_close(filp, NULL); + return temp; +} + +static umode_t suaxb35_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr, int channel) +{ + switch (type) { + case hwmon_temp: + if (attr == hwmon_temp_input) + return 0444; + break; + case hwmon_fan: + if (attr == hwmon_fan_input) + return 0444; + break; + default: + break; + } + return 0; +} + +static int suaxb35_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long *val) +{ + switch (type) { + case hwmon_temp: + if (attr == hwmon_temp_input) { + *val = read_temperature_from_sysfs() * 1000; // Convert to millidegrees + return 0; + } + break; + case hwmon_fan: + if (attr == hwmon_fan_input && channel < 3) { + *val = read_fan_rpm_from_sysfs(channel); + return 0; + } + break; + default: + break; + } + return -EOPNOTSUPP; +} + +static const struct hwmon_ops suaxb35_hwmon_ops = { + .is_visible = suaxb35_is_visible, + .read = suaxb35_read, +}; + +static const u32 temp_config[] = { + HWMON_T_INPUT, + 0 +}; + +static const u32 fan_config[] = { + HWMON_F_INPUT, + HWMON_F_INPUT, + HWMON_F_INPUT, + 0 +}; + +static const struct hwmon_channel_info temp_info = { + .type = hwmon_temp, + .config = temp_config, +}; + +static const struct hwmon_channel_info fan_info = { + .type = hwmon_fan, + .config = fan_config, +}; + +static const struct hwmon_channel_info *suaxb35_info[] = { + &temp_info, + &fan_info, + NULL +}; + +static const struct hwmon_chip_info suaxb35_chip_info = { + .ops = &suaxb35_hwmon_ops, + .info = suaxb35_info, +}; + +static struct class *hwmon_class; +static struct device *parent_device; +static struct device *hwmon_dev; + +static int __init su_axb35_hwmon_init(void) +{ + int ret; + + // Check if source files exist + struct file *test_file = filp_open("/sys/class/ec_su_axb35/fan1/rpm", O_RDONLY, 0); + if (IS_ERR(test_file)) { + pr_err("ec_su_axb35 sysfs interface not found\n"); + return PTR_ERR(test_file); + } + filp_close(test_file, NULL); + + hwmon_class = class_create("su_axb35_hwmon"); + if (IS_ERR(hwmon_class)) { + ret = PTR_ERR(hwmon_class); + goto err; + } + + parent_device = device_create(hwmon_class, NULL, MKDEV(0, 0), NULL, "su_axb35_device"); + if (IS_ERR(parent_device)) { + ret = PTR_ERR(parent_device); + goto err_device; + } + + hwmon_dev = hwmon_device_register_with_info(parent_device, "su_axb35", + NULL, &suaxb35_chip_info, NULL); + if (IS_ERR(hwmon_dev)) { + ret = PTR_ERR(hwmon_dev); + pr_err("Failed to register hwmon device: %d\n", ret); + goto err_hwmon; + } + + pr_info("su_axb35 hwmon driver loaded - reading from existing ec_su_axb35 interface\n"); + return 0; + +err_hwmon: + device_destroy(hwmon_class, MKDEV(0, 0)); +err_device: + class_destroy(hwmon_class); +err: + return ret; +} + +static void __exit su_axb35_hwmon_exit(void) +{ + if (!IS_ERR_OR_NULL(hwmon_dev)) { + hwmon_device_unregister(hwmon_dev); + } + if (!IS_ERR_OR_NULL(parent_device)) { + device_destroy(hwmon_class, MKDEV(0, 0)); + } + if (!IS_ERR_OR_NULL(hwmon_class)) { + class_destroy(hwmon_class); + } + pr_info("su_axb35 hwmon driver unloaded\n"); +} + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("mike"); +MODULE_DESCRIPTION("SU-AXB35 Hardware Monitoring Driver - Bridge to existing interface"); + +module_init(su_axb35_hwmon_init); +module_exit(su_axb35_hwmon_exit); diff --git a/os/licenses/ec_su_axb35/src/python-gui/ec-fan-control.desktop b/os/licenses/ec_su_axb35/src/python-gui/ec-fan-control.desktop new file mode 100644 index 00000000..aa06794d --- /dev/null +++ b/os/licenses/ec_su_axb35/src/python-gui/ec-fan-control.desktop @@ -0,0 +1,8 @@ +[Desktop Entry] +Type=Application +Name=EC Fan Control +Comment=Fan and APU control for ec_su_axb35 +Exec=/usr/local/bin/ec-su_axb35-linux-gui +Icon=fan +Terminal=false +Categories=System;Settings; \ No newline at end of file diff --git a/os/licenses/ec_su_axb35/src/python-gui/ec-su_axb35-linux-gui.py b/os/licenses/ec_su_axb35/src/python-gui/ec-su_axb35-linux-gui.py new file mode 100644 index 00000000..a620b647 --- /dev/null +++ b/os/licenses/ec_su_axb35/src/python-gui/ec-su_axb35-linux-gui.py @@ -0,0 +1,669 @@ +#!/usr/bin/env python3 +""" +Fan Control GUI for ec_su_axb35 driver +Requires appropriate permissions to read/write /sys/class/ec_su_axb35/ +""" +import os +import sys +import subprocess + +def ensure_root(): + if os.geteuid() == 0: + return # already root + + # Prevent infinite relaunch loop + if os.environ.get("EC_FAN_CONTROL_ROOT") == "1": + sys.exit("Failed to gain root privileges") + + env = os.environ.copy() + env["EC_FAN_CONTROL_ROOT"] = "1" + + cmd = [ + "pkexec", + "env", + f"DISPLAY={env.get('DISPLAY', '')}", + f"XAUTHORITY={env.get('XAUTHORITY', '')}", + sys.executable, + os.path.abspath(__file__) + ] + + subprocess.run(cmd, env=env) + sys.exit(0) + +ensure_root() + +import argparse +import json +import tkinter as tk +from tkinter import ttk, messagebox, filedialog +import threading +import queue +import os + +CONFIG_PATH = "/etc/ec-fan-control.json" +BASE_PATH = "/sys/class/ec_su_axb35" + +class FanControlGUI: + def __init__(self, root): + self.root = root + self.root.title("Fan Control - ec_su_axb35") + self.root.geometry("900x800") + + self.config_path = CONFIG_PATH + self.base_path = BASE_PATH + self.update_interval = 1.0 + self.running = True + self.stop_event = threading.Event() + self.ui_queue = queue.Queue() + self.ui_after_id = None + self.mode_check_timer = None + self.fan_mode_check_timers = {} + self.curve_write_delay = 0.4 # seconds + # (fan, curve) -> generation counter + self.curve_write_gen = {} + # (fan, curve) -> threading.Timer + self.curve_write_timers = {} + + # Create GUI + self.create_widgets() + + # Only the main thread may interact with Tk. Worker threads put UI + # callbacks on this queue, which is drained from the Tk event loop. + self.ui_after_id = self.root.after(50, self.process_ui_queue) + + # Start monitoring thread + self.monitor_thread = threading.Thread(target=self.monitor_loop, daemon=True) + self.monitor_thread.start() + + # Initial mode read + self.read_all_modes() + + def queue_ui(self, callback): + """Queue a Tk callback for execution by the main thread.""" + if self.running: + self.ui_queue.put(callback) + + def process_ui_queue(self): + """Execute callbacks queued by worker threads.""" + if not self.running: + return + + while True: + try: + callback = self.ui_queue.get_nowait() + except queue.Empty: + break + callback() + + self.ui_after_id = self.root.after(50, self.process_ui_queue) + + def read_sysfs(self, path): + """Read value from sysfs file""" + try: + with open(path, 'r') as f: + return f.read().strip() + except Exception as e: + print(f"Error reading {path}: {e}") + return None + + def write_sysfs(self, path, value): + """Write value to sysfs file""" + try: + with open(path, 'w') as f: + f.write(str(value)) + return True + except Exception as e: + self.queue_ui(lambda error=e: messagebox.showerror( + "Error", f"Failed to write to {path}: {error}" + )) + return False + + def create_widgets(self): + # Top frame - Temperature and Fan RPMs + top_frame = ttk.LabelFrame(self.root, text="System Status", padding=10) + top_frame.pack(fill=tk.X, padx=10, pady=5) + + # Temperature + ttk.Label(top_frame, text="CPU Temperature:").grid(row=0, column=0, sticky=tk.W, padx=5) + self.temp_label = ttk.Label(top_frame, text="--°C", font=('Arial', 12, 'bold')) + self.temp_label.grid(row=0, column=1, sticky=tk.W, padx=5) + + # Fan RPMs + ttk.Label(top_frame, text="Fan 1 RPM:").grid(row=0, column=2, sticky=tk.W, padx=5) + self.fan1_rpm_label = ttk.Label(top_frame, text="----", font=('Arial', 12, 'bold')) + self.fan1_rpm_label.grid(row=0, column=3, sticky=tk.W, padx=5) + + ttk.Label(top_frame, text="Fan 2 RPM:").grid(row=1, column=0, sticky=tk.W, padx=5) + self.fan2_rpm_label = ttk.Label(top_frame, text="----", font=('Arial', 12, 'bold')) + self.fan2_rpm_label.grid(row=1, column=1, sticky=tk.W, padx=5) + + ttk.Label(top_frame, text="Fan 3 RPM:").grid(row=1, column=2, sticky=tk.W, padx=5) + self.fan3_rpm_label = ttk.Label(top_frame, text="----", font=('Arial', 12, 'bold')) + self.fan3_rpm_label.grid(row=1, column=3, sticky=tk.W, padx=5) + + # Update interval selector + ttk.Label(top_frame, text="Update Interval:").grid(row=0, column=4, sticky=tk.W, padx=5) + self.interval_var = tk.StringVar(value="1") + interval_combo = ttk.Combobox(top_frame, textvariable=self.interval_var, + values=["0.5", "1", "2", "5"], width=5, state='readonly') + interval_combo.grid(row=0, column=5, sticky=tk.W, padx=5) + interval_combo.bind('<>', self.on_interval_change) + ttk.Label(top_frame, text="sec").grid(row=0, column=6, sticky=tk.W) + + # APU Power Mode frame + apu_frame = ttk.LabelFrame(self.root, text="APU Power Mode", padding=10) + apu_frame.pack(fill=tk.X, padx=10, pady=5) + + ttk.Label(apu_frame, text="Power Mode:").grid(row=0, column=0, sticky=tk.W, padx=5) + self.apu_mode_var = tk.StringVar() + self.apu_mode_combo = ttk.Combobox(apu_frame, textvariable=self.apu_mode_var, + values=["quiet", "balanced", "performance"], + width=15, state='readonly') + self.apu_mode_combo.grid(row=0, column=1, sticky=tk.W, padx=5) + self.apu_mode_combo.bind('<>', self.on_apu_mode_change) + + # Fan control blocks + fans_frame = ttk.Frame(self.root) + fans_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=5) + + self.fan_controls = {} + for i, fan_num in enumerate([1, 2, 3], 1): + fan_name = ["CPU Fan 1", "CPU Fan 2", "System Fan"][i-1] + self.create_fan_control(fans_frame, fan_num, fan_name, i-1) + + # Config control block + config_frame = ttk.Frame(self.root) + config_frame.pack(fill=tk.X, padx=10, pady=5) + + ttk.Button( + config_frame, + text="Save Config", + command=self.save_config + ).pack(side=tk.LEFT, padx=5) + + ttk.Button( + config_frame, + text="Load Config", + command=self.load_config + ).pack(side=tk.LEFT, padx=5) + + def create_fan_control(self, parent, fan_num, fan_name, column): + """Create control block for a single fan""" + frame = ttk.LabelFrame(parent, text=fan_name, padding=10) + frame.grid(row=0, column=column, sticky=tk.NSEW, padx=5) + parent.columnconfigure(column, weight=1) + parent.rowconfigure(0, weight=1) + + # RPM display + ttk.Label(frame, text="Current RPM:").pack() + rpm_label = ttk.Label(frame, text="----", font=('Arial', 14, 'bold')) + rpm_label.pack() + + # Mode selector + ttk.Label(frame, text="Fan Mode:", font=('Arial', 10, 'bold')).pack(pady=(10, 0)) + mode_var = tk.StringVar() + mode_combo = ttk.Combobox(frame, textvariable=mode_var, + values=["auto", "fixed", "curve"], + width=12, state='readonly') + mode_combo.pack(pady=5) + mode_combo.bind('<>', + lambda e, fn=fan_num: self.on_fan_mode_change(fn)) + + # Level selector (for fixed mode) + level_frame = ttk.Frame(frame) + ttk.Label(level_frame, text="Level:").pack(side=tk.LEFT) + level_var = tk.StringVar() + level_combo = ttk.Combobox(level_frame, textvariable=level_var, + values=["1", "2", "3", "4", "5"], + width=5, state='readonly') + level_combo.pack(side=tk.LEFT, padx=5) + level_combo.bind('<>', + lambda e, fn=fan_num: self.on_level_change(fn)) + + # Curve controls + curve_frame = ttk.LabelFrame(frame, text="Fan Curves", padding=5) + + # Ramp up curve + rampup_frame = ttk.Frame(curve_frame) + rampup_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5) + ttk.Label(rampup_frame, text="Ramp Up (°C)").pack() + + rampup_sliders = [] + rampup_labels = [] + slider_frame = ttk.Frame(rampup_frame) + slider_frame.pack(fill=tk.BOTH, expand=True) + + for i in range(5): + col_frame = ttk.Frame(slider_frame) + col_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=2) + + value_label = ttk.Label(col_frame, text="--", font=('Arial', 9)) + value_label.pack() + + slider = tk.Scale(col_frame, from_=100, to=30, orient=tk.VERTICAL, + length=150, showvalue=0, + command=lambda v, fn=fan_num, idx=i, curve='rampup': + self.on_curve_change(fn, curve, idx, v)) + slider.pack() + + label = ttk.Label(col_frame, text=f"L{i+1}") + label.pack() + + rampup_sliders.append(slider) + rampup_labels.append(value_label) + + # Ramp down curve + rampdown_frame = ttk.Frame(curve_frame) + rampdown_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5) + ttk.Label(rampdown_frame, text="Ramp Down (°C)").pack() + + rampdown_sliders = [] + rampdown_labels = [] + slider_frame = ttk.Frame(rampdown_frame) + slider_frame.pack(fill=tk.BOTH, expand=True) + + for i in range(5): + col_frame = ttk.Frame(slider_frame) + col_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=2) + + value_label = ttk.Label(col_frame, text="--", font=('Arial', 9)) + value_label.pack() + + slider = tk.Scale(col_frame, from_=100, to=30, orient=tk.VERTICAL, + length=150, showvalue=0, + command=lambda v, fn=fan_num, idx=i, curve='rampdown': + self.on_curve_change(fn, curve, idx, v)) + slider.pack() + + label = ttk.Label(col_frame, text=f"L{i+1}") + label.pack() + + rampdown_sliders.append(slider) + rampdown_labels.append(value_label) + + # Store references + self.fan_controls[fan_num] = { + 'rpm_label': rpm_label, + 'mode_var': mode_var, + 'mode_combo': mode_combo, + 'level_var': level_var, + 'level_combo': level_combo, + 'level_frame': level_frame, + 'curve_frame': curve_frame, + 'rampup_sliders': rampup_sliders, + 'rampup_labels': rampup_labels, + 'rampup_values': [None] * 5, + 'rampdown_sliders': rampdown_sliders, + 'rampdown_labels': rampdown_labels, + 'rampdown_values': [None] * 5, + } + _ = self.read_curve(fan_num, "rampup") + _ = self.read_curve(fan_num, "rampdown") + + # Initially hide level and curve controls + level_frame.pack_forget() + curve_frame.pack_forget() + + def on_interval_change(self, event): + """Handle update interval change""" + self.update_interval = float(self.interval_var.get()) + + def on_apu_mode_change(self, event): + """Handle APU power mode change""" + mode = self.apu_mode_var.get() + path = f"{self.base_path}/apu/power_mode" + if self.write_sysfs(path, mode): + # Schedule mode verification after 10 seconds + if self.mode_check_timer: + self.mode_check_timer.cancel() + self.mode_check_timer = threading.Timer(10.0, self.read_apu_mode) + self.mode_check_timer.daemon = True + self.mode_check_timer.start() + + def on_fan_mode_change(self, fan_num): + """Handle fan mode change""" + mode = self.fan_controls[fan_num]['mode_var'].get() + path = f"{self.base_path}/fan{fan_num}/mode" + + if self.write_sysfs(path, mode): + # Update UI based on mode + self.update_fan_mode_ui(fan_num, mode) + + # If switching to curve mode, read current curve values + if mode == "curve": + self.read_fan_curves(fan_num) + + # Schedule mode verification after 10 seconds + old_timer = self.fan_mode_check_timers.get(fan_num) + if old_timer: + old_timer.cancel() + timer = threading.Timer(10.0, self.read_fan_mode, args=(fan_num,)) + timer.daemon = True + self.fan_mode_check_timers[fan_num] = timer + timer.start() + + def update_fan_mode_ui(self, fan_num, mode): + """Show/hide controls based on fan mode""" + controls = self.fan_controls[fan_num] + + # Hide everything first + controls['level_frame'].pack_forget() + controls['curve_frame'].pack_forget() + + # Show relevant controls based on mode + if mode == "fixed": + controls['level_frame'].pack(pady=5) + elif mode == "curve": + controls['curve_frame'].pack(fill=tk.BOTH, expand=True, pady=5) + + def on_level_change(self, fan_num): + """Handle fan level change""" + level = self.fan_controls[fan_num]['level_var'].get() + path = f"{self.base_path}/fan{fan_num}/level" + self.write_sysfs(path, level) + + def schedule_curve_write(self, fan_num, curve_type, values): + key = (fan_num, curve_type) + + # Increment generation + gen = self.curve_write_gen.get(key, 0) + 1 + self.curve_write_gen[key] = gen + + # Cancel old timer if it exists + old_timer = self.curve_write_timers.get(key) + if old_timer: + old_timer.cancel() + + def do_write(expected_gen): + # Discard if superseded + if self.curve_write_gen.get(key) != expected_gen: + return + + curve_str = ",".join(map(str, values)) + path = f"{self.base_path}/fan{fan_num}/{curve_type}_curve" + + self.write_sysfs(path, curve_str) + + timer = threading.Timer( + self.curve_write_delay, + lambda: do_write(gen) + ) + timer.daemon = True + + self.curve_write_timers[key] = timer + timer.start() + + def on_curve_change(self, fan_num, curve_type, index, value): + """Handle curve slider change with cascading constraints""" + controls = self.fan_controls[fan_num] + sliders = controls[f'{curve_type}_sliders'] + labels = controls[f'{curve_type}_labels'] + values = controls[f'{curve_type}_values'] + other_curve = 'rampdown' if curve_type == 'rampup' else 'rampup' + other_sliders = controls[f'{other_curve}_sliders'] + other_labels = controls[f'{other_curve}_labels'] + other_values = controls[f'{other_curve}_values'] + new_value = int(float(value)) + other_modified = False + + # Apply cascading constraints + # If we're moving a slider, ensure all subsequent sliders are >= this value + if new_value != values[index]: + values[index] = new_value + + # Enforce minimum constraint: each level must be >= previous level + for i in range(index + 1, 5): + if values[i] < values[index]: + values[i] = values[index] + # Temporarily block the callback to avoid recursion + sliders[i].config(command='') + sliders[i].set(values[i]) + sliders[i].config(command=lambda v, fn=fan_num, idx=i, curve=curve_type: + self.on_curve_change(fn, curve, idx, v)) + + # Also check backwards: if previous levels are > current, adjust them + for i in range(index - 1, -1, -1): + if values[i] > values[index]: + values[i] = values[index] + # Temporarily block the callback to avoid recursion + sliders[i].config(command='') + sliders[i].set(values[i]) + sliders[i].config(command=lambda v, fn=fan_num, idx=i, curve=curve_type: + self.on_curve_change(fn, curve, idx, v)) + + # Enforce rampup/rampdown constraint: rampdown <= rampup for each level + if curve_type == 'rampup': + for i in range(5): + if other_values[i] > values[i]: + other_values[i] = values[i] + other_sliders[i].config(command='') + other_sliders[i].set(other_values[i]) + other_sliders[i].config(command=lambda v, fn=fan_num, idx=i, curve=other_curve: + self.on_curve_change(fn, curve, idx, v)) + other_modified = True + else: # curve_type == 'rampdown' + for i in range(5): + if other_values[i] < values[i]: + other_values[i] = values[i] + other_sliders[i].config(command='') + other_sliders[i].set(other_values[i]) + other_sliders[i].config(command=lambda v, fn=fan_num, idx=i, curve=other_curve: + self.on_curve_change(fn, curve, idx, v)) + other_modified = True + + # If other was modified, enforce cascading constraint on it + if other_modified: + for i in range(4): + if other_values[i+1] < other_values[i]: + other_values[i+1] = other_values[i] + other_sliders[i+1].config(command='') + other_sliders[i+1].set(other_values[i+1]) + other_sliders[i+1].config(command=lambda v, fn=fan_num, idx=i+1, curve=other_curve: + self.on_curve_change(fn, curve, idx, v)) + + # Update all labels + for i, val in enumerate(values): + labels[i].config(text=f"{val}°C") + for i, val in enumerate(other_values): + other_labels[i].config(text=f"{val}°C") + + # Schedule Write all values to sysfs + self.schedule_curve_write(fan_num, curve_type, values) + controls[f'{curve_type}_values'] = values + + # Also write the other curve if we modified it + if other_modified: + self.schedule_curve_write(fan_num, other_curve, other_values) + controls[f'{other_curve}_values'] = other_values + + def read_curve(self, fan_num, curve_type): + """Read a fan curve (rampup_curve or rampdown_curve)""" + path = f"{self.base_path}/fan{fan_num}/{curve_type}_curve" + data = self.read_sysfs(path) + if data: + try: + values = [int(x) for x in data.replace(',', ' ').split()] + self.fan_controls[fan_num][f'{curve_type}_values'] = values + return values + except: + return None + return None + + def read_fan_curves(self, fan_num): + """Read and update curve sliders for a fan""" + rampup = self.read_curve(fan_num, "rampup") + rampdown = self.read_curve(fan_num, "rampdown") + + controls = self.fan_controls[fan_num] + + if rampup and len(rampup) == 5: + for i, value in enumerate(rampup): + controls['rampup_sliders'][i].set(value) + controls['rampup_labels'][i].config(text=f"{value}°C") + + if rampdown and len(rampdown) == 5: + for i, value in enumerate(rampdown): + controls['rampdown_sliders'][i].set(value) + controls['rampdown_labels'][i].config(text=f"{value}°C") + + def read_fan_mode(self, fan_num): + """Read current fan mode""" + path = f"{self.base_path}/fan{fan_num}/mode" + mode = self.read_sysfs(path) + if mode: + # Extract current mode from [auto, fixed, curve] format + if '[' in mode: + for m in ['auto', 'fixed', 'curve']: + if f'[{m}]' in mode: + mode = m + break + self.queue_ui(lambda m=mode: self.fan_controls[fan_num]['mode_var'].set(m)) + self.queue_ui(lambda m=mode: self.update_fan_mode_ui(fan_num, m)) + # If mode is curve, read the current curve values + if mode == 'curve': + self.queue_ui(lambda: self.read_fan_curves(fan_num)) + + def read_all_modes(self): + """Read all fan modes and APU mode""" + for fan_num in [1, 2, 3]: + self.read_fan_mode(fan_num) + self.read_apu_mode() + + def read_apu_mode(self): + """Read current APU power mode""" + path = f"{self.base_path}/apu/power_mode" + mode = self.read_sysfs(path) + if mode: + # Extract current mode from [quiet, balanced, performance] format + if '[' in mode: + for m in ['quiet', 'balanced', 'performance']: + if f'[{m}]' in mode: + mode = m + break + self.queue_ui(lambda m=mode: self.apu_mode_var.set(m)) + + def monitor_loop(self): + """Background thread to monitor temperature and RPM""" + while self.running: + try: + # Read temperature + temp_path = f"{self.base_path}/temp1/temp" + temp = self.read_sysfs(temp_path) + if temp: + self.queue_ui(lambda t=temp: self.temp_label.config(text=f"{t}°C")) + + # Read fan RPMs + for fan_num in [1, 2, 3]: + rpm_path = f"{self.base_path}/fan{fan_num}/rpm" + rpm = self.read_sysfs(rpm_path) + if rpm: + if fan_num == 1: + self.queue_ui(lambda r=rpm: self.fan1_rpm_label.config(text=r)) + elif fan_num == 2: + self.queue_ui(lambda r=rpm: self.fan2_rpm_label.config(text=r)) + elif fan_num == 3: + self.queue_ui(lambda r=rpm: self.fan3_rpm_label.config(text=r)) + + # Update fan control block RPM + self.queue_ui(lambda fn=fan_num, r=rpm: + self.fan_controls[fn]['rpm_label'].config(text=r)) + + except Exception as e: + print(f"Monitor error: {e}") + + self.stop_event.wait(self.update_interval) + + def save_config(self): + data = { + "apu_mode": self.apu_mode_var.get(), + "fans": {} + } + + for fan_num, c in self.fan_controls.items(): + data["fans"][str(fan_num)] = { + "mode": c["mode_var"].get(), + "level": c["level_var"].get(), + "rampup_curve": c["rampup_values"][:], + "rampdown_curve": c["rampdown_values"][:], + } + + try: + with open(self.config_path, "w") as f: + json.dump(data, f, indent=2) + messagebox.showinfo("Saved", "Configuration saved successfully.") + except Exception as e: + messagebox.showerror("Error", f"Failed to save config:\n{e}") + + def load_config(self): + try: + with open(self.config_path) as f: + data = json.load(f) + except Exception as e: + messagebox.showerror("Error", f"Failed to load config:\n{e}") + return + + # APU mode + apu = data.get("apu_mode") + if apu: + self.apu_mode_var.set(apu) + self.write_sysfs(f"{self.base_path}/apu/power_mode", apu) + + for fan_num_str, cfg in data.get("fans", {}).items(): + fan_num = int(fan_num_str) + controls = self.fan_controls.get(fan_num) + if not controls: + continue + + mode = cfg.get("mode") + if mode: + controls["mode_var"].set(mode) + self.write_sysfs(f"{self.base_path}/fan{fan_num}/mode", mode) + self.update_fan_mode_ui(fan_num, mode) + + level = cfg.get("level") + if level and mode == "fixed": + controls["level_var"].set(level) + self.write_sysfs(f"{self.base_path}/fan{fan_num}/level", level) + + # Curves + if mode == "curve": + ru = cfg.get("rampup_curve") + rd = cfg.get("rampdown_curve") + + if ru and len(ru) == 5: + controls["rampup_values"][:] = ru + for i, s in enumerate(controls["rampup_sliders"]): + s.set(ru[i]) + self.schedule_curve_write(fan_num, "rampup", ru) + + if rd and len(rd) == 5: + controls["rampdown_values"][:] = rd + for i, s in enumerate(controls["rampdown_sliders"]): + s.set(rd[i]) + self.schedule_curve_write(fan_num, "rampdown", rd) + messagebox.showinfo("Loaded", "Configuration loaded successfully.") + + def on_closing(self): + """Handle window close""" + self.running = False + self.stop_event.set() + if self.mode_check_timer: + self.mode_check_timer.cancel() + for timer in self.fan_mode_check_timers.values(): + timer.cancel() + for timer in self.curve_write_timers.values(): + timer.cancel() + if self.ui_after_id is not None: + self.root.after_cancel(self.ui_after_id) + self.ui_after_id = None + self.monitor_thread.join(timeout=1.0) + self.root.destroy() + +def main(): + root = tk.Tk() + app = FanControlGUI(root) + root.protocol("WM_DELETE_WINDOW", app.on_closing) + root.mainloop() + +if __name__ == "__main__": + main() diff --git a/os/licenses/ec_su_axb35/src/scripts/info.sh b/os/licenses/ec_su_axb35/src/scripts/info.sh new file mode 100755 index 00000000..b7857d00 --- /dev/null +++ b/os/licenses/ec_su_axb35/src/scripts/info.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + + +printf "+--------------------------------------------------------------+\n" +# APU Power Mode +apu_mode=$(cat /sys/class/ec_su_axb35/apu/power_mode) +# Temperature +temp=$(cat /sys/class/ec_su_axb35/temp1/temp) +printf "| %-26s | %-31s |\n" "CPU-Temp: ${temp} C" "Power mode: $apu_mode" + + +# Fan Table +printf "+-----+-------+-------+------+----------------+----------------+\n" +printf "| %-3s | %-5s | %-5s | %-4s | %-14s | %-14s |\n" "FAN" "MODE" "LEVEL" "RPM" "RAMPUP" "RAMPDOWN" +for i in 1 2 3; do + mode=$(cat /sys/class/ec_su_axb35/fan${i}/mode) + level=$(cat /sys/class/ec_su_axb35/fan${i}/level) + rpm=$(cat /sys/class/ec_su_axb35/fan${i}/rpm) + rampup=$(cat /sys/class/ec_su_axb35/fan${i}/rampup_curve) + rampdown=$(cat /sys/class/ec_su_axb35/fan${i}/rampdown_curve) + printf "| %-3d | %-5s | %-5d | %4d | %14s | %14s |\n" $i "$mode" "$level" "$rpm" "$rampup" "$rampdown" + +done +printf "+-----+-------+-------+------+----------------+----------------+\n" diff --git a/os/licenses/ec_su_axb35/src/scripts/su_axb35_monitor b/os/licenses/ec_su_axb35/src/scripts/su_axb35_monitor new file mode 100755 index 00000000..13ee0217 --- /dev/null +++ b/os/licenses/ec_su_axb35/src/scripts/su_axb35_monitor @@ -0,0 +1,571 @@ +#!/usr/bin/env bash + +# mandatory bash version check +if [[ "${BASH_VERSION%%.*}" -lt 4 ]]; then + echo "Error: This script requires Bash version 4 or higher" + echo "Current version: $BASH_VERSION" + exit 1 +fi + +axb35_name=ec_su_axb35 +axb35_path=/sys/class/$axb35_name +axb35_delay=1 +raw_mode=0 +dev_mode=0 +declare -a axb35_vars + +# usage info +usage() { + cat << EOF +Usage: $(basename "$0") [OPTIONS] + +OPTIONS: + -i SECONDS Update interval (0.3-10 seconds, default: 1) + -j Output current values in JSON and exit + -r Display raw values without additional processing + -h, --help Display this usage information + + +EOF + exit 0 +} + +# colors and formatting +RESET="\033[0m" +BOLD="\033[1m" +GREEN="\033[32m" +YELLOW="\033[33m" +BLUE="\033[34m" +CYAN="\033[36m" +RED="\033[31m" +MAGENTA="\033[35m" + +# magic numbers to navigate the array +fan1rpm=0 fan2rpm=1 fan3rpm=2 +fan1mode=3 fan2mode=4 fan3mode=5 +fan1level=6 fan2level=7 fan3level=8 +fan1ru=9 fan2ru=10 fan3ru=11 +fan1rd=12 fan2rd=13 fan3rd=14 +temp=15 tempmin=16 tempmax=17 +powermode=18 + +# function to get all needed values from the module +get_current_state() { + mapfile -t axb35_vars < <( + cat \ + $axb35_path/fan{1..3}/rpm \ + $axb35_path/fan{1..3}/mode \ + $axb35_path/fan{1..3}/level \ + $axb35_path/fan{1..3}/rampup_curve \ + $axb35_path/fan{1..3}/rampdown_curve \ + $axb35_path/temp1/{temp,min,max} \ + $axb35_path/apu/power_mode + ) +} + +# function to process received values +process_current_state() { + local modes=($fan1mode $fan2mode $fan3mode) + local rus=($fan1ru $fan2ru $fan3ru) + local rds=($fan1rd $fan2rd $fan3rd) + local levels=($fan1level $fan2level $fan3level) + + # for fans in auto or fixed mode, ramp-up and ramp-down curves are not applicable + # for fans in auto mode, fan level is not applicable + for i in {0..2}; do + if [[ "${axb35_vars[${modes[$i]}]}" == "auto" || "${axb35_vars[${modes[$i]}]}" == "fixed" ]]; then + axb35_vars[${rus[$i]}]='' + axb35_vars[${rds[$i]}]='' + fi + [[ "${axb35_vars[${modes[$i]}]}" == "auto" ]] && axb35_vars[${levels[$i]}]='' + done + + # check if any conditional field changed to/from empty compared to previous state + needs_redraw=0 + local conditional_indices=($fan1level $fan1ru $fan1rd $fan2level $fan2ru $fan2rd $fan3level $fan3ru $fan3rd) + for idx in "${conditional_indices[@]}"; do + local cur_empty=0 prev_empty=0 + [[ -z "${axb35_vars[$idx]}" ]] && cur_empty=1 + [[ -z "${axb35_vars_old[$idx]}" ]] && prev_empty=1 + if [[ $cur_empty -ne $prev_empty ]]; then + needs_redraw=1 + break + fi + done + axb35_vars_old=("${axb35_vars[@]}") +} + +# check if module is loaded and path exists (skipped in dev mode) +check_module() { + if ! lsmod | grep -q "$axb35_name" || [ ! -d "$axb35_path" ]; then + echo "Error: Module '$axb35_name' is not loaded or not accessible" + exit 2 + fi +} + +# dev mode state generator - cycles through various states to exercise the UI +_dev_tick=0 +get_dev_state() { + local tick=$(( _dev_tick % 40 )) + (( _dev_tick++ )) + + # cycle through phases: + # 0-9: all fans auto, low temp, quiet power + # 10-19: fan1 fixed, fan2 curve, fan3 auto, mid temp, balanced power + # 20-29: all fans curve, high temp, performance power + # 30-39: fan1 curve, fan2 auto, fan3 fixed, cooling down, balanced power + + local f1rpm f2rpm f3rpm + local f1mode f2mode f3mode + local f1level f2level f3level + local f1ru f2ru f3ru + local f1rd f2rd f3rd + local cur_temp pwr_mode + + if (( tick < 10 )); then + # phase 0: all fans auto, cool, quiet + f1rpm=$(( 800 + tick * 20 )) + f2rpm=$(( 750 + tick * 15 )) + f3rpm=$(( 600 + tick * 10 )) + f1mode=auto; f2mode=auto; f3mode=auto + f1level=''; f2level=''; f3level='' + f1ru=''; f2ru=''; f3ru='' + f1rd=''; f2rd=''; f3rd='' + cur_temp=$(( 38 + tick )) + pwr_mode=quiet + elif (( tick < 20 )); then + # phase 1: fan1 fixed, fan2 curve, fan3 auto, warming, balanced + local t=$(( tick - 10 )) + f1rpm=$(( 1800 + t * 80 )) + f2rpm=$(( 1200 + t * 60 )) + f3rpm=$(( 900 + t * 40 )) + f1mode=fixed; f2mode=curve; f3mode=auto + f1level=$(( 1 + t / 3 )); f2level=''; f3level='' + f1ru=''; f2ru="60,70,83,95,97"; f3ru='' + f1rd=''; f2rd="40,50,80,94,96"; f3rd='' + cur_temp=$(( 50 + t * 2 )) + pwr_mode=balanced + elif (( tick < 30 )); then + # phase 2: all fans curve, hot, performance + local t=$(( tick - 20 )) + f1rpm=$(( 3000 + t * 100 )) + f2rpm=$(( 2800 + t * 90 )) + f3rpm=$(( 2200 + t * 70 )) + f1mode=curve; f2mode=curve; f3mode=curve + f1level=''; f2level=''; f3level='' + f1ru="60,70,83,95,97"; f2ru="60,70,83,95,97"; f3ru="20,60,83,95,97" + f1rd="40,50,80,94,96"; f2rd="40,50,80,94,96"; f3rd="0,50,80,94,96" + cur_temp=$(( 72 + t )) + pwr_mode=performance + else + # phase 3: fan1 curve, fan2 auto, fan3 fixed, cooling, balanced + local t=$(( tick - 30 )) + f1rpm=$(( 3900 - t * 120 )) + f2rpm=$(( 3600 - t * 100 )) + f3rpm=$(( 2900 - t * 80 )) + f1mode=curve; f2mode=auto; f3mode=fixed + f1level=''; f2level=''; f3level=$(( 5 - t / 2 )) + f1ru="60,70,83,95,97"; f2ru=''; f3ru='' + f1rd="40,50,80,94,96"; f2rd=''; f3rd='' + cur_temp=$(( 80 - t * 2 )) + pwr_mode=balanced + fi + + axb35_vars=( + "$f1rpm" "$f2rpm" "$f3rpm" + "$f1mode" "$f2mode" "$f3mode" + "$f1level" "$f2level" "$f3level" + "$f1ru" "$f2ru" "$f3ru" + "$f1rd" "$f2rd" "$f3rd" + "$cur_temp" "35" "82" + "$pwr_mode" + ) +} + +# command line arguments processing +while getopts ":i:jrdh-:" opt; do + case $opt in + i) + if [[ $(echo "$OPTARG > 0.2 && $OPTARG <= 10" | bc) -eq 1 ]]; then + axb35_delay=$OPTARG + else + echo "Error: Interval must be between 0.3 and 10 seconds" + exit 1 + fi + ;; + j) + get_current_state + cat << EOF +{ + "fan1": { + "rpm": ${axb35_vars[$fan1rpm]}, + "mode": "${axb35_vars[$fan1mode]}", + "level": ${axb35_vars[$fan1level]}, + "rampup_curve": "${axb35_vars[$fan1ru]}", + "rampdown_curve": "${axb35_vars[$fan1rd]}" + }, + "fan2": { + "rpm": ${axb35_vars[$fan2rpm]}, + "mode": "${axb35_vars[$fan2mode]}", + "level": ${axb35_vars[$fan2level]}, + "rampup_curve": "${axb35_vars[$fan2ru]}", + "rampdown_curve": "${axb35_vars[$fan2rd]}" + }, + "fan3": { + "rpm": ${axb35_vars[$fan3rpm]}, + "mode": "${axb35_vars[$fan3mode]}", + "level": ${axb35_vars[$fan3level]}, + "rampup_curve": "${axb35_vars[$fan3ru]}", + "rampdown_curve": "${axb35_vars[$fan3rd]}" + }, + "temperature": { + "current": ${axb35_vars[$temp]}, + "min": ${axb35_vars[$tempmin]}, + "max": ${axb35_vars[$tempmax]} + }, + "power_mode": "${axb35_vars[$powermode]}" +} +EOF + exit 0 + ;; + r) + raw_mode=1 + ;; + d) + dev_mode=1 + ;; + h) + usage + ;; + -) + case "${OPTARG}" in + help) + usage + ;; + *) + echo "Invalid option: --${OPTARG}" >&2 + usage + ;; + esac + ;; + \?) + echo "Invalid option: -$OPTARG" >&2 + usage + ;; + :) + echo "Option -$OPTARG requires an argument." >&2 + usage + ;; + esac +done + +# module check runs after argument parsing so -d can suppress it +[[ $dev_mode -eq 0 ]] && check_module + +# function to format the RPM value with color +format_rpm() { + local rpm=$1 + if (( rpm < 1200 )); then + echo -e "${GREEN}${rpm}${RESET} RPM" + elif (( rpm < 3000 )); then + echo -e "${YELLOW}${rpm}${RESET} RPM" + else + echo -e "${RED}${rpm}${RESET} RPM" + fi +} + +# function to format the temperature with color +format_temp() { + local temp=$1 + if (( temp < 50 )); then + echo -e "${GREEN}${temp}${RESET} °C" + elif (( temp < 70 )); then + echo -e "${YELLOW}${temp}${RESET} °C" + else + echo -e "${RED}${temp}${RESET} °C" + fi +} + +# function to format the mode with color +format_mode() { + local mode=$1 + case "$mode" in + "auto") echo -e "[${GREEN}${mode}${RESET}]" ;; + "fixed") echo -e "[${YELLOW}${mode}${RESET}]" ;; + "curve") echo -e "[${CYAN}${mode}${RESET}]" ;; + *) echo -e "[${mode}]" ;; + esac +} + +# function to format the power mode with color +format_power_mode() { + local mode=$1 + case "$mode" in + "quiet") echo -e "${GREEN}${mode}${RESET}" ;; + "balanced") echo -e "${YELLOW}${mode}${RESET}" ;; + "performance") echo -e "${RED}${mode}${RESET}" ;; + *) echo -e "${mode}" ;; + esac +} + +# function to draw the interface +draw_interface() { + local value_offset=20 + local term_rows=$(tput lines) + local term_cols=$(tput cols) + + tput cup 0 0 + tput ed + + # terminal dimensions and update interval in top right corner + local interval_ms=$( printf "%.0f" $(echo "${axb35_delay} * 1000" | bc -l) ) + local dim_text="[i:${interval_ms}ms | t:${term_cols}x${term_rows}]" + local dim_pos=$((term_cols - ${#dim_text})) + tput cup 0 $dim_pos + echo -n -e "${CYAN}${dim_text}${RESET}" + + # title + tput cup 0 0 + echo -e "${BOLD}${MAGENTA}=== AXB35 System Monitor ===${RESET}" + echo "" + + # calculate layout based on terminal size + # each block is 6 rows high (1 title + 4 data + 1 empty) + # each column is 40 chars wide (static) + local block_height=6 + local column_width=40 + local max_columns=$((term_cols / column_width)) + [[ $max_columns -lt 1 ]] && max_columns=1 + + local usable_rows=$((term_rows - 4)) + local blocks_per_column=$((usable_rows / block_height)) + [[ $blocks_per_column -lt 1 ]] && blocks_per_column=1 + + # store the positions of each value for later updates + declare -gA positions + + # Block 1: APU Fan 1 + local col=0 + local row=2 + + if [[ $((row + block_height)) -gt $((usable_rows + 2)) ]]; then + col=$((col + 1)) + row=2 + fi + + if [[ $col -lt $max_columns ]]; then + local col_offset=$((col * column_width)) + tput cup $row $col_offset + echo -e "${BOLD}APU Fan 1${RESET}" + positions[fan1mode_pos]="$row $((col_offset + value_offset))" + + row=$((row + 1)) + tput cup $row $col_offset + echo -n " Speed: " + positions[fan1rpm_pos]="$row $((col_offset + value_offset))" + + row=$((row + 1)) + tput cup $row $col_offset + [[ -n "${axb35_vars[$fan1level]}" ]] && echo -n " Level: " + positions[fan1level_pos]="$row $((col_offset + value_offset))" + + row=$((row + 1)) + tput cup $row $col_offset + [[ -n "${axb35_vars[$fan1ru]}" ]] && echo -n " Ramp Up Curve: " + positions[fan1ru_pos]="$row $((col_offset + value_offset))" + + row=$((row + 1)) + tput cup $row $col_offset + [[ -n "${axb35_vars[$fan1rd]}" ]] && echo -n " Ramp Down Curve: " + positions[fan1rd_pos]="$row $((col_offset + value_offset))" + + row=$((row + 2)) # skip one line + fi + + # Block 2: APU Fan 2 + if [[ $((row + block_height)) -gt $((usable_rows + 2)) ]]; then + col=$((col + 1)) + row=2 + fi + + if [[ $col -lt $max_columns ]]; then + local col_offset=$((col * column_width)) + tput cup $row $col_offset + echo -e "${BOLD}APU Fan 2${RESET}" + positions[fan2mode_pos]="$row $((col_offset + value_offset))" + + row=$((row + 1)) + tput cup $row $col_offset + echo -n " Speed: " + positions[fan2rpm_pos]="$row $((col_offset + value_offset))" + + row=$((row + 1)) + tput cup $row $col_offset + [[ -n "${axb35_vars[$fan2level]}" ]] && echo -n " Level: " + positions[fan2level_pos]="$row $((col_offset + value_offset))" + + row=$((row + 1)) + tput cup $row $col_offset + [[ -n "${axb35_vars[$fan2ru]}" ]] && echo -n " Ramp Up Curve: " + positions[fan2ru_pos]="$row $((col_offset + value_offset))" + + row=$((row + 1)) + tput cup $row $col_offset + [[ -n "${axb35_vars[$fan2rd]}" ]] && echo -n " Ramp Down Curve: " + positions[fan2rd_pos]="$row $((col_offset + value_offset))" + + row=$((row + 2)) # skip one line + fi + + # Block 3: System Fan + if [[ $((row + block_height)) -gt $((usable_rows + 2)) ]]; then + col=$((col + 1)) + row=2 + fi + + if [[ $col -lt $max_columns ]]; then + local col_offset=$((col * column_width)) + tput cup $row $col_offset + echo -e "${BOLD}System Fan${RESET}" + positions[fan3mode_pos]="$row $((col_offset + value_offset))" + + row=$((row + 1)) + tput cup $row $col_offset + echo -n " Speed: " + positions[fan3rpm_pos]="$row $((col_offset + value_offset))" + + row=$((row + 1)) + tput cup $row $col_offset + [[ -n "${axb35_vars[$fan3level]}" ]] && echo -n " Level: " + positions[fan3level_pos]="$row $((col_offset + value_offset))" + + row=$((row + 1)) + tput cup $row $col_offset + [[ -n "${axb35_vars[$fan3ru]}" ]] && echo -n " Ramp Up Curve: " + positions[fan3ru_pos]="$row $((col_offset + value_offset))" + + row=$((row + 1)) + tput cup $row $col_offset + [[ -n "${axb35_vars[$fan3rd]}" ]] && echo -n " Ramp Down Curve: " + positions[fan3rd_pos]="$row $((col_offset + value_offset))" + + row=$((row + 2)) # skip one line + fi + + # Block 4: Temperature & Power + if [[ $((row + block_height)) -gt $((usable_rows + 2)) ]]; then + col=$((col + 1)) + row=2 + fi + + if [[ $col -lt $max_columns ]]; then + local col_offset=$((col * column_width)) + tput cup $row $col_offset + echo -e "${BOLD}Temperature & Power${RESET}" + + row=$((row + 1)) + tput cup $row $col_offset + echo -n " Current: " + positions[temp_pos]="$row $((col_offset + value_offset))" + + row=$((row + 1)) + tput cup $row $col_offset + echo -n " Min: " + positions[tempmin_pos]="$row $((col_offset + value_offset))" + + row=$((row + 1)) + tput cup $row $col_offset + echo -n " Max: " + positions[tempmax_pos]="$row $((col_offset + value_offset))" + + row=$((row + 1)) + tput cup $row $col_offset + echo -n " Power Mode: " + positions[powermode_pos]="$row $((col_offset + value_offset))" + + row=$((row + 2)) # skip one line + fi + + # prompt + local prompt_row=$((term_rows - 2)) + tput cup $prompt_row 0 + echo -e "${BOLD}${BLUE}Press Ctrl+C to exit${RESET}" +} + +# function to draw the values in their positions +draw_values() { + for pos in "${!positions[@]}"; do + read -r row col <<< "${positions[$pos]}" + tput cup "$row" "$col" + + local value_str='' + + # extract the variable name without _pos suffix + var_name=${pos%_pos} + + # special formatting for different types of values + case "$var_name" in + fan1rpm|fan2rpm|fan3rpm) + value_str=$(format_rpm "${axb35_vars[${!var_name}]}") + ;; + temp|tempmin|tempmax) + value_str=$(format_temp "${axb35_vars[${!var_name}]}") + ;; + fan1mode|fan2mode|fan3mode) + value_str=$(format_mode "${axb35_vars[${!var_name}]}") + ;; + powermode) + value_str=$(format_power_mode "${axb35_vars[${!var_name}]}") + ;; + *) + value_str="${axb35_vars[${!var_name}]}" + ;; + esac + + # calculate required padding + local clean_str=${value_str//$'\e'[\[0-9;]*m/} + local visible_length=${#clean_str} + local padding=$((15 - visible_length)) + printf "%s%${padding}s" "$value_str" "" + done +} + +# function to handle terminal resize +handle_resize() { + draw_interface + draw_values +} + +# function to handle cleanup on exit +cleanup() { + tput cnorm + tput sgr0 + clear + echo "Monitor terminated." + exit 0 +} + +# set up signal handlers +trap cleanup SIGINT SIGTERM +trap handle_resize SIGWINCH + +# hide cursor +tput civis + +# previous state array for change detection in process_current_state +declare -a axb35_vars_old + +# main loop +handle_resize +while true; do + if [[ $dev_mode -eq 1 ]]; then + get_dev_state + else + get_current_state + fi + [[ $raw_mode -eq 0 ]] && process_current_state + [[ $needs_redraw -eq 1 ]] && draw_interface + + draw_values + sleep $axb35_delay +done diff --git a/os/licenses/ec_su_axb35/src/scripts/test_fan_mode_fixed.sh b/os/licenses/ec_su_axb35/src/scripts/test_fan_mode_fixed.sh new file mode 100755 index 00000000..2519c534 --- /dev/null +++ b/os/licenses/ec_su_axb35/src/scripts/test_fan_mode_fixed.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash + +class_path=/sys/class/ec_su_axb35 +declare -a fan1_rpms fan2_rpms fan3_rpms +declare -a original_modes original_levels + +echo "Saving original fan settings..." +for i in {1..3}; do + original_modes[$i]=$(cat $class_path/fan$i/mode) + original_levels[$i]=$(cat $class_path/fan$i/level) + echo "Fan $i: mode=${original_modes[$i]}, level=${original_levels[$i]}" +done +echo "------------------------" + +echo "Setting fans to fixed mode..." +echo fixed | tee $class_path/fan{1..3}/mode > /dev/null + +# Test each fan rpm level from 0 to 5 +for level in {0..5}; do + echo "Setting fans to level $level..." + echo "$level" | tee $class_path/fan{1..3}/level > /dev/null + + echo "Waiting 5 seconds for fans to adjust..." + sleep 5 + + fan1_rpms[$level]=$(cat $class_path/fan1/rpm) + fan2_rpms[$level]=$(cat $class_path/fan2/rpm) + fan3_rpms[$level]=$(cat $class_path/fan3/rpm) + + echo "Fan 1 rpm: ${fan1_rpms[$level]}" + echo "Fan 2 rpm: ${fan2_rpms[$level]}" + echo "Fan 3 rpm: ${fan3_rpms[$level]}" + echo "------------------------" +done + +echo "Restoring original fan settings..." +for i in {1..3}; do + echo "${original_modes[$i]}" > $class_path/fan$i/mode + echo "${original_levels[$i]}" > $class_path/fan$i/level + echo "Fan $i restored to: mode=${original_modes[$i]}, level=${original_levels[$i]}" +done + +# Generate report +echo " +=========================================== +| Level | Fan 1 | Fan 2 | Fan 3 | +===========================================" + +for level in {0..5}; do + printf "| %d | %4s | %4s | %4s |\n" $level "${fan1_rpms[$level]}" "${fan2_rpms[$level]}" "${fan3_rpms[$level]}" + echo "-------------------------------------------" +done + +echo "Fan rpm test completed." diff --git a/os/licenses/ec_su_axb35/src/src/ec_su_axb35.c b/os/licenses/ec_su_axb35/src/src/ec_su_axb35.c new file mode 100644 index 00000000..bb4c7858 --- /dev/null +++ b/os/licenses/ec_su_axb35/src/src/ec_su_axb35.c @@ -0,0 +1,660 @@ +// ec_su_axb35.c + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern int ec_read(u8 addr, u8 *val); +extern int ec_write(u8 addr, u8 val); + +enum fan_mode { AUTO, FIXED, CURVE }; + +struct ec_fan { + const char *name; + u8 speed_reg_high; + u8 speed_reg_low; + u8 mode_reg; + u8 rampup_curve[6]; + u8 rampdown_curve[6]; + enum fan_mode mode; + struct device *dev; +}; + +struct ec_temp { + const char *name; + u8 reg; + u8 temp_min; + u8 temp_max; + struct device *dev; +}; + +struct ec_apu { + const char *name; + u8 power_mode_reg; + struct device *dev; +}; + +static struct class *ec_class; + +static struct ec_fan ec_fans[] = { + { .name = "fan1", + .speed_reg_high = 0x35, + .speed_reg_low = 0x36, + .mode_reg = 0x21, + .rampup_curve = { 0, 60, 70, 83, 95, 97 }, + .rampdown_curve = { 0, 40, 50, 80, 94, 96 } }, + { .name = "fan2", + .speed_reg_high = 0x37, + .speed_reg_low = 0x38, + .mode_reg = 0x23, + .rampup_curve = { 0, 60, 70, 83, 95, 97 }, + .rampdown_curve = { 0, 40, 50, 80, 94, 96 } }, + { .name = "fan3", + .speed_reg_high = 0x28, + .speed_reg_low = 0x29, + .mode_reg = 0x25, + .rampup_curve = { 0, 20, 60, 83, 95, 97 }, + .rampdown_curve = { 0, 0, 50, 80, 94, 96 } }, +}; + +static struct ec_temp ec_temp = { + .name = "temp1", + .reg = 0x70, +}; + +static struct ec_apu ec_apu = { + .name = "apu", + .power_mode_reg = 0x31, +}; + +static ssize_t fan_rpm_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct ec_fan *fan = dev_get_drvdata(dev); + u8 hi; + // TODO: handle error + ec_read(fan->speed_reg_high, &hi); + u8 lo; + // TODO: handle error + ec_read(fan->speed_reg_low, &lo); + u16 rpm = (hi << 8) | lo; + // wired fan3 behavior, displaying 8000 before turning to 0 + if (strcmp(fan->name, "fan3") == 0 && rpm == 8000) + rpm = 0; + return sprintf(buf, "%u\n", rpm); +} + +static struct device_attribute dev_attr_fan_rpm = + __ATTR(rpm, 0444, fan_rpm_show, NULL); + +static void update_fan_mode(struct ec_fan *fan) +{ + u8 val; + // TODO: handle error + ec_read(fan->mode_reg, &val); + + switch (val) { + case 0x10: + case 0x20: + case 0x30: + fan->mode = AUTO; + break; + case 0x11: + case 0x21: + case 0x31: + // curve and fixed use the value in the EC register + // so fixed is only allowed if it was already know as + // FIXED to the driver + if (fan->mode != FIXED) { + fan->mode = CURVE; + } else { + fan->mode = FIXED; + } + break; + } +} + +static ssize_t fan_mode_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct ec_fan *fan = dev_get_drvdata(dev); + + update_fan_mode(fan); + + const char *mode = "unknown"; + switch (fan->mode) { + case AUTO: + mode = "auto"; + break; + case FIXED: + mode = "fixed"; + break; + case CURVE: + mode = "curve"; + break; + } + + return sprintf(buf, "%s\n", mode); +} + +static u8 read_fan_level(struct ec_fan *fan) +{ + u8 val; + // TODO: handle error + ec_read(fan->mode_reg + 1, &val); + + switch (val & 0xF) { + case 0x2: // 20% + return 1; + case 0x3: // 40% + return 2; + case 0x4: // 60% + return 3; + case 0x5: // 80% + return 4; + case 0x6: // 100% + return 5; + case 0x7: + default: // off + return 0; + } +} + +static ssize_t fan_level_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct ec_fan *fan = dev_get_drvdata(dev); + return sprintf(buf, "%u\n", read_fan_level(fan)); +} + +static void write_fan_level(struct ec_fan *fan, u8 level) +{ + u8 val; + + switch (fan->mode_reg) { + case 0x21: + val = 0x10; + break; + case 0x23: + val = 0x20; + break; + case 0x25: + val = 0x30; + break; + } + + switch (level) { + case 0: + val += 0x7; + break; + case 1: + val += 0x2; + break; + case 2: + val += 0x3; + break; + case 3: + val += 0x4; + break; + case 4: + val += 0x5; + break; + case 5: + default: + val += 0x6; + } + + // TODO: handle error + ec_write(fan->mode_reg + 1, val); +} + +static ssize_t fan_level_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct ec_fan *fan = dev_get_drvdata(dev); + u8 val; + + if (kstrtou8(buf, 10, &val)) + return -EINVAL; + + write_fan_level(fan, val); + + return count; +} + +static struct device_attribute dev_attr_fan_level = + __ATTR(level, 0644, fan_level_show, fan_level_store); + +static ssize_t fan_mode_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ec_fan *fan = dev_get_drvdata(dev); + u8 val; + + if (sysfs_streq(buf, "auto")) { + fan->mode = AUTO; + } else if (sysfs_streq(buf, "fixed")) { + fan->mode = FIXED; + } else if (sysfs_streq(buf, "curve")) { + fan->mode = CURVE; + } else { + return -EINVAL; + } + + switch (fan->mode) { + case AUTO: + fan->mode = AUTO; + switch (fan->mode_reg) { + case 0x21: + val = 0x10; + break; + case 0x23: + val = 0x20; + break; + case 0x25: + val = 0x30; + break; + default: + return -EINVAL; + } + break; + case FIXED: + case CURVE: + switch (fan->mode_reg) { + case 0x21: + val = 0x11; + break; + case 0x23: + val = 0x21; + break; + case 0x25: + val = 0x31; + break; + default: + return -EINVAL; + } + break; + } + + // TODO: handle error + ec_write(fan->mode_reg, val); + + // When switching to CURVE mode, set initial fan level based on current temperature + // to prevent RPM burst from inappropriate starting level + if (fan->mode == CURVE) { + u8 temp; + // TODO: handle error + if (ec_read(ec_temp.reg, &temp) == 0) { + u8 initial_level = 0; + int i; + + // Find the appropriate level based on current temperature + // Use rampup curve for initial positioning to be more responsive + for (i = 5; i > 0; i--) { + if (temp >= fan->rampup_curve[i]) { + initial_level = i; + break; + } + } + + write_fan_level(fan, initial_level); + } + } + + return count; +} + +static struct device_attribute dev_attr_fan_mode = + __ATTR(mode, 0644, fan_mode_show, fan_mode_store); + +static ssize_t fan_curve_show(u8 *curve, char *buf) +{ + int i; + char *p = buf; + + for (i = 1; i < 6; i++) { + p += sprintf(p, "%d", curve[i]); + if (i < 5) { + *p++ = ','; + } + } + *p++ = '\n'; + *p = '\0'; + + return p - buf; +} + +static ssize_t fan_curve_store(u8 *curve, const char *buf, size_t count) +{ + char *str = kstrndup(buf, count, GFP_KERNEL); + char *token; + int values[5]; + int i = 0; + int ret = 0; + + if (!str) + return -ENOMEM; + + token = strsep(&str, ","); + while (token && i < 5) { + if (kstrtoint(token, 10, &values[i]) < 0) { + ret = -EINVAL; + break; + } + if (values[i] < 0 || values[i] > 100) { + ret = -EINVAL; + break; + } + i++; + token = strsep(&str, ","); + } + + if (i != 5) + ret = -EINVAL; + + if (ret == 0) { + for (i = 0; i < 5; i++) { + curve[i + 1] = values[i]; + } + } + + kfree(str); + return ret ? ret : count; +} + +static ssize_t fan_rampup_curve_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ec_fan *fan = dev_get_drvdata(dev); + return fan_curve_show(fan->rampup_curve, buf); +} + +static ssize_t fan_rampup_curve_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ec_fan *fan = dev_get_drvdata(dev); + return fan_curve_store(fan->rampup_curve, buf, count); +} + +static struct device_attribute dev_attr_fan_rampup_curve = + __ATTR(rampup_curve, 0644, fan_rampup_curve_show, fan_rampup_curve_store); + +static ssize_t fan_rampdown_curve_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ec_fan *fan = dev_get_drvdata(dev); + return fan_curve_show(fan->rampdown_curve, buf); +} + +static ssize_t fan_rampdown_curve_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ec_fan *fan = dev_get_drvdata(dev); + return fan_curve_store(fan->rampdown_curve, buf, count); +} + +static struct device_attribute dev_attr_fan_rampdown_curve = __ATTR( + rampdown_curve, 0644, fan_rampdown_curve_show, fan_rampdown_curve_store); + +static ssize_t temp_current_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ec_temp *temp = dev_get_drvdata(dev); + u8 val; + // TODO: handle error + ec_read(temp->reg, &val); + return sprintf(buf, "%u\n", val); +} + +static struct device_attribute dev_attr_temp_cur = + __ATTR(temp, 0444, temp_current_show, NULL); + +static ssize_t temp_min_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct ec_temp *temp = dev_get_drvdata(dev); + return sprintf(buf, "%u\n", temp->temp_min); +} + +static struct device_attribute dev_attr_temp_min = + __ATTR(min, 0444, temp_min_show, NULL); + +static ssize_t temp_max_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct ec_temp *temp = dev_get_drvdata(dev); + return sprintf(buf, "%u\n", temp->temp_max); +} + +static struct device_attribute dev_attr_temp_max = + __ATTR(max, 0444, temp_max_show, NULL); + +static ssize_t apu_power_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ec_apu *apu = dev_get_drvdata(dev); + u8 val; + // TODO: handle error + ec_read(apu->power_mode_reg, &val); + + const char *mode = "unknown"; + switch (val) { + case 0x00: + mode = "balanced"; + break; + case 0x01: + mode = "performance"; + break; + case 0x02: + mode = "quiet"; + break; + default: + return -EINVAL; + } + + return sprintf(buf, "%s\n", mode); +} + +static ssize_t apu_power_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ec_apu *apu = dev_get_drvdata(dev); + u8 val; + if (sysfs_streq(buf, "balanced")) { + val = 0x00; + } else if (sysfs_streq(buf, "performance")) { + val = 0x01; + } else if (sysfs_streq(buf, "quiet")) { + val = 0x02; + } else { + return -EINVAL; + } + + ec_write(apu->power_mode_reg, val); + return count; +} + +static struct device_attribute dev_attr_apu_power_mode = + __ATTR(power_mode, 0644, apu_power_mode_show, apu_power_mode_store); + +static struct delayed_work ec_update_work; + +static void ec_update_worker(struct work_struct *work) +{ + u8 temp; + // TODO: handle error + ec_read(ec_temp.reg, &temp); + int i; + + // Update min/max + if (ec_temp.temp_min == 0 || temp < ec_temp.temp_min) + ec_temp.temp_min = temp; + + if (temp > ec_temp.temp_max) + ec_temp.temp_max = temp; + + // update fan level if curve mode is active + for (i = 0; i < ARRAY_SIZE(ec_fans); i++) { + struct ec_fan *fan = &ec_fans[i]; + + if (fan->mode == CURVE) { + u8 level = read_fan_level(fan); + if (level < 5 && temp >= fan->rampup_curve[level + 1]) { + write_fan_level(fan, level + 1); + } else if (level > 0 && temp <= fan->rampdown_curve[level]) + write_fan_level(fan, level - 1); + } + } + + // Requeue the work + schedule_delayed_work(&ec_update_work, + msecs_to_jiffies(1000)); // every 1 sec +} + +static dev_t ec_su_axb35_dev; +static struct class *ec_class; + +static int __init ec_su_axb35_init(void) +{ + int i; + int ret; + + ret = alloc_chrdev_region(&ec_su_axb35_dev, 0, ARRAY_SIZE(ec_fans) + 2, + "ec_su_axb35"); + if (ret < 0) { + pr_err("ec_su_axb35: Failed to allocation major number\n"); + return ret; + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0) + ec_class = class_create("ec_su_axb35"); +#else + ec_class = class_create(THIS_MODULE, "ec_su_axb35"); +#endif + + if (IS_ERR(ec_class)) { + unregister_chrdev_region(ec_su_axb35_dev, ARRAY_SIZE(ec_fans) + 2); + return PTR_ERR(ec_class); + } + + for (i = 0; i < ARRAY_SIZE(ec_fans); i++) { + struct ec_fan *fan = &ec_fans[i]; + + fan->dev = device_create( + ec_class, NULL, MKDEV(MAJOR(ec_su_axb35_dev), i), fan, fan->name); + if (IS_ERR(fan->dev)) + continue; + + dev_set_drvdata(fan->dev, fan); + device_create_file(fan->dev, &dev_attr_fan_rpm); + device_create_file(fan->dev, &dev_attr_fan_mode); + device_create_file(fan->dev, &dev_attr_fan_level); + device_create_file(fan->dev, &dev_attr_fan_rampup_curve); + device_create_file(fan->dev, &dev_attr_fan_rampdown_curve); + update_fan_mode(fan); + } + + ec_temp.dev = device_create( + ec_class, NULL, MKDEV(MAJOR(ec_su_axb35_dev), ARRAY_SIZE(ec_fans)), + &ec_temp, ec_temp.name); + if (!IS_ERR(ec_temp.dev)) { + dev_set_drvdata(ec_temp.dev, &ec_temp); + device_create_file(ec_temp.dev, &dev_attr_temp_cur); + device_create_file(ec_temp.dev, &dev_attr_temp_min); + device_create_file(ec_temp.dev, &dev_attr_temp_max); + } + + ec_apu.dev = device_create( + ec_class, NULL, MKDEV(MAJOR(ec_su_axb35_dev), ARRAY_SIZE(ec_fans) + 1), + &ec_apu, ec_apu.name); + if (!IS_ERR(ec_apu.dev)) { + dev_set_drvdata(ec_apu.dev, &ec_apu); + device_create_file(ec_apu.dev, &dev_attr_apu_power_mode); + } + + INIT_DELAYED_WORK(&ec_update_work, ec_update_worker); + schedule_delayed_work(&ec_update_work, msecs_to_jiffies(1000)); + + pr_info("ec_su_axb35: Sixunited AXB35-02 EC driver loaded\n"); + return 0; +} + +static void __exit ec_su_axb35_exit(void) +{ + int i; + + /* Reset all fans to AUTO mode before unloading */ + for (i = 0; i < ARRAY_SIZE(ec_fans); i++) { + struct ec_fan *fan = &ec_fans[i]; + u8 val; + + fan->mode = AUTO; + + switch (fan->mode_reg) { + case 0x21: // Fan 1 + val = 0x10; + break; + case 0x23: // Fan 2 + val = 0x20; + break; + case 0x25: // Fan 3 + val = 0x30; + break; + default: + continue; + } + ec_write(fan->mode_reg, val); + pr_info("ec_su_axb35: %s reset to AUTO mode\n", fan->name); + } + + for (i = 0; i < ARRAY_SIZE(ec_fans); i++) { + if (!IS_ERR(ec_fans[i].dev)) { + device_remove_file(ec_fans[i].dev, &dev_attr_fan_rpm); + device_remove_file(ec_fans[i].dev, &dev_attr_fan_mode); + device_remove_file(ec_fans[i].dev, &dev_attr_fan_level); + device_remove_file(ec_fans[i].dev, &dev_attr_fan_rampup_curve); + device_remove_file(ec_fans[i].dev, &dev_attr_fan_rampdown_curve); + device_destroy(ec_class, MKDEV(MAJOR(ec_su_axb35_dev), i)); + } + } + + if (!IS_ERR(ec_temp.dev)) { + device_remove_file(ec_temp.dev, &dev_attr_temp_cur); + device_remove_file(ec_temp.dev, &dev_attr_temp_min); + device_remove_file(ec_temp.dev, &dev_attr_temp_max); + device_destroy(ec_class, + MKDEV(MAJOR(ec_su_axb35_dev), ARRAY_SIZE(ec_fans))); + } + + if (!IS_ERR(ec_apu.dev)) { + device_remove_file(ec_apu.dev, &dev_attr_apu_power_mode); + device_destroy(ec_class, + MKDEV(MAJOR(ec_su_axb35_dev), ARRAY_SIZE(ec_fans) + 1)); + } + + cancel_delayed_work_sync(&ec_update_work); + + class_destroy(ec_class); + unregister_chrdev_region(ec_su_axb35_dev, ARRAY_SIZE(ec_fans) + 2); + pr_info("ec_su_axb35: Module unloaded\n"); +} + +module_init(ec_su_axb35_init); +module_exit(ec_su_axb35_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("loom@mopper.de"); +MODULE_DESCRIPTION("Sixunited AXB35-02 Embedded Controller driver"); diff --git a/os/licenses/ryzenadj/LICENSE b/os/licenses/ryzenadj/LICENSE new file mode 100644 index 00000000..0a041280 --- /dev/null +++ b/os/licenses/ryzenadj/LICENSE @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser 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 +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/os/licenses/ryzenadj/SOURCE b/os/licenses/ryzenadj/SOURCE new file mode 100644 index 00000000..f617296b --- /dev/null +++ b/os/licenses/ryzenadj/SOURCE @@ -0,0 +1,12 @@ +SMU power-limit tool: ryzenadj (sets STAPM / slow / fast PPT and the die temperature limit) +Ship: yes +Project: RyzenAdj, https://github.com/FlyGoat/RyzenAdj +Release: v0.19.0, commit ea71739b4a3e1a0a624dfd1c9c268a31cb2a4182 (2026-05-13, "Bump version to 0.19.0") +License: LGPL-3.0 (LICENSE, shipped here); the source in src/ is that tag, verbatim, and it is installed +into the image under /usr/share/licenses/1bit-os/ryzenadj/src. +Build: from the source in this directory, by os/mini/mkimage.sh (deterministic - the image never depends on + what the build machine happens to have): + cmake -B build -DCMAKE_BUILD_TYPE=Release && cmake --build build -j4 + needs cmake, gcc, pkg-config and libpci-dev on the build machine; links libpci, libudev, libz, libc. +Why it ships: the EC power mode alone gives the firmware's own profile; the 90 W sustained cap this image +ships needs the SMU limits written with this tool (through /dev/mem; see os/LICENSES.md and TOSFANS-STATE). diff --git a/os/licenses/ryzenadj/src/.github/workflows/build.yml b/os/licenses/ryzenadj/src/.github/workflows/build.yml new file mode 100644 index 00000000..494fb0e2 --- /dev/null +++ b/os/licenses/ryzenadj/src/.github/workflows/build.yml @@ -0,0 +1,96 @@ +name: Build + +on: + push: + pull_request: + +jobs: + Windows: + runs-on: windows-latest + + steps: + # Checks-out + - uses: actions/checkout@v4 + + - uses: ilammy/msvc-dev-cmd@v1 + + #don't use run-cmake for windows because only one build should add warnings to pull request + - name: Build Debug + run: | + mkdir debug + cd debug + cmake -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Debug .. + nmake + + - name: Upload ryzenadj debug + uses: actions/upload-artifact@v4 + with: + name: ryzenadj-win64-debug + path: | + debug/ryzenadj.exe + debug/libryzenadj.dll + +# - name: Test Scripts +# shell: pwsh +# run: | +# cd release +# $expectedOutput = "Not AMD processor, must be kidding" +# $cmdDemoOutput = '' | cmd.exe /c .\demo.bat +# if ($cmdDemoOutput -notcontains $expectedOutput){ +# Write-Error "$cmdDemoOutput" +# exit 1 +# } +# $psServiceOutput = Powershell .\readjustService.ps1 -noGUI +# if ($psServiceOutput -ne $expectedOutput){ +# Write-Error "$psServiceOutput" +# exit 1 +# } +# $pythonOutput = python pmtable-example.py +# if ($pythonOutput -notcontains $expectedOutput){ +# Write-Error "$pythonOutput" +# exit 1 +# } +# $pythonOutput = python readjust.py +# if ($pythonOutput -notcontains $expectedOutput){ +# Write-Error "$pythonOutput" +# exit 1 +# } +# exit 0 + + Linux: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Install Dependencies + run: | + sudo apt-get update + sudo apt-get install libpci-dev + + - name: run-cmake #with support for inline error reporting + uses: lukka/run-cmake@v3.3 + + - name: Test make like readme + run: | + mkdir build && cd build + cmake -DCMAKE_BUILD_TYPE=Release .. + make + +# - name: Test Scripts +# shell: bash +# run: | +# cd examples +# export LD_LIBRARY_PATH="${{ github.workspace }}/build/" +# expectedOutput="Not AMD processor, must be kidding" +# pythonOutput=$(python pmtable-example.py 2>&1) || true +# if [[ ! "$pythonOutput" =~ "$expectedOutput" ]]; then +# echo "pmtable-example.py output: $pythonOutput" +# exit 1 +# fi +# pythonOutput=$(python readjust.py 2>&1) || true +# if [[ ! "$pythonOutput" =~ "$expectedOutput" ]]; then +# echo "readjust.py output: $pythonOutput" +# exit 1 +# fi +# exit 0 diff --git a/os/licenses/ryzenadj/src/.github/workflows/release.yml b/os/licenses/ryzenadj/src/.github/workflows/release.yml new file mode 100644 index 00000000..ed0e8a3c --- /dev/null +++ b/os/licenses/ryzenadj/src/.github/workflows/release.yml @@ -0,0 +1,191 @@ +name: Release + +on: + push: + workflow_dispatch: + release: + types: [created] + +permissions: + contents: write + +env: + RELEASE_TAG: ${{ github.event.release.tag_name || github.ref_name }} + +jobs: + manylinux_2_28: + name: manylinux_2_28 + runs-on: ubuntu-latest + container: quay.io/pypa/manylinux_2_28_x86_64 + + steps: + - name: Install build dependencies + run: | + yum install -y epel-release + yum install -y cmake3 git make pciutils-devel pkgconfig tar gzip + + - uses: actions/checkout@v4 + + - name: Build RyzenAdj + run: | + cmake3 -S . -B build \ + -DCMAKE_BUILD_TYPE=Release + cmake3 --build build --parallel + ldd build/ryzenadj + if ! ldd build/ryzenadj | grep -q libpci; then + echo "ryzenadj must link against shared libpci in the manylinux_2_28 package" + exit 1 + fi + + - name: Package + run: | + package="ryzenadj-manylinux_2_28-x86_64" + mkdir -p "dist/${package}" + cp build/ryzenadj "dist/${package}/" + cp build/libryzenadj.so "dist/${package}/" + cp lib/ryzenadj.h "dist/${package}/" + cp LICENSE README.md "dist/${package}/" + cp -r examples "dist/${package}/" + tar -czf "dist/${package}.tar.gz" -C dist "${package}" + + - uses: actions/upload-artifact@v4 + with: + name: release-manylinux_2_28 + path: dist/*.tar.gz + + musl-static: + name: musl-static + runs-on: ubuntu-latest + container: alpine:3.20 + + steps: + - name: Install build dependencies + run: | + apk add --no-cache build-base cmake curl file git linux-headers pkgconf tar + + - uses: actions/checkout@v4 + + - name: Build static libpci + env: + PCIUTILS_VERSION: 3.13.0 + run: | + curl -fsSLO "https://mj.ucw.cz/download/linux/pci/pciutils-${PCIUTILS_VERSION}.tar.gz" + tar -xf "pciutils-${PCIUTILS_VERSION}.tar.gz" + make -C "pciutils-${PCIUTILS_VERSION}" -j"$(nproc)" \ + PREFIX=/opt/pciutils-static \ + SHARED=no \ + ZLIB=no \ + DNS=no \ + HWDB=no \ + PCI_HAVE_PM_LINUX_SYSFS=1 \ + PCI_HAVE_PM_LINUX_PROC=1 \ + LIBKMOD=no + make -C "pciutils-${PCIUTILS_VERSION}" install-lib \ + PREFIX=/opt/pciutils-static \ + SHARED=no \ + ZLIB=no \ + DNS=no \ + HWDB=no \ + PCI_HAVE_PM_LINUX_SYSFS=1 \ + PCI_HAVE_PM_LINUX_PROC=1 \ + LIBKMOD=no + + - name: Build RyzenAdj + env: + PKG_CONFIG_PATH: /opt/pciutils-static/lib/pkgconfig + run: | + cmake -S . -B build-musl \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_SHARED_LIBS=OFF \ + -DPREFER_STATIC_LINKING=ON + cmake --build build-musl --parallel + + file build-musl/ryzenadj + if ! file build-musl/ryzenadj | grep -q "statically linked"; then + echo "ryzenadj must be a static executable in the musl-static package" + exit 1 + fi + + - name: Package + run: | + package="ryzenadj-linux-musl-static-x86_64" + mkdir -p "dist-musl/${package}" + cp build-musl/ryzenadj "dist-musl/${package}/" + cp build-musl/libryzenadj.a "dist-musl/${package}/" + cp lib/ryzenadj.h "dist-musl/${package}/" + cp LICENSE README.md "dist-musl/${package}/" + cp -r examples "dist-musl/${package}/" + tar -czf "dist-musl/${package}.tar.gz" -C dist-musl "${package}" + + - uses: actions/upload-artifact@v4 + with: + name: release-musl-static + path: dist-musl/*.tar.gz + + windows: + name: windows + runs-on: windows-latest + + steps: + - uses: actions/checkout@v4 + + - uses: ilammy/msvc-dev-cmd@v1 + + - name: Build RyzenAdj + run: | + mkdir build + cd build + cmake -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release .. + nmake + + - name: Prepare release folder + run: | + mkdir release + copy build/ryzenadj.exe release/ + copy build/libryzenadj.dll release/ + copy win32/* release/ + copy examples/* release/ + + - name: Package + shell: pwsh + run: | + New-Item -ItemType Directory -Force -Path dist + Remove-Item "release/*.lib", "release/*.exp" -ErrorAction SilentlyContinue + Compress-Archive -Path release/* -DestinationPath "dist/ryzenadj-win64.zip" + + New-Item -ItemType Directory -Force -Path lib-release + Copy-Item build/libryzenadj.dll, build/libryzenadj.lib, lib/ryzenadj.h -Destination lib-release + Copy-Item win32/inpoutx64.dll, win32/WinRing0x64.dll, win32/WinRing0x64.sys -Destination lib-release + Compress-Archive -Path lib-release/* -DestinationPath "dist/libryzenadj-win64.zip" + + - uses: actions/upload-artifact@v4 + with: + name: ryzenadj-win64 + path: dist/*.zip + + publish: + name: publish + runs-on: ubuntu-latest + needs: + - manylinux_2_28 + - musl-static + - windows + + steps: + - uses: actions/download-artifact@v4 + with: + path: artifacts + merge-multiple: true + + - name: Upload release build run artifact + uses: actions/upload-artifact@v4 + with: + name: release-builds + path: artifacts/* + + - name: Upload release assets + if: github.event_name == 'release' + env: + GH_TOKEN: ${{ github.token }} + run: | + gh release upload "${RELEASE_TAG}" artifacts/* --clobber diff --git a/os/licenses/ryzenadj/src/.gitignore b/os/licenses/ryzenadj/src/.gitignore new file mode 100644 index 00000000..edbd6728 --- /dev/null +++ b/os/licenses/ryzenadj/src/.gitignore @@ -0,0 +1,350 @@ +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +build/ + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ +# ASP.NET Core default setup: bower directory is configured as wwwroot/lib/ and bower restore is true +**/wwwroot/lib/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- Backup*.rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# End of https://www.gitignore.io/api/visualstudio diff --git a/os/licenses/ryzenadj/src/CMakeLists.txt b/os/licenses/ryzenadj/src/CMakeLists.txt new file mode 100644 index 00000000..06d9d9af --- /dev/null +++ b/os/licenses/ryzenadj/src/CMakeLists.txt @@ -0,0 +1,79 @@ +#cmake version +CMAKE_MINIMUM_REQUIRED(VERSION 3.10) + +#define project name +PROJECT(ryzenadj) + +include(GNUInstallDirs) + +set(CMAKE_C_VISIBILITY_PRESET hidden) + +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_VISIBILITY_PRESET hidden) + +option(BUILD_SHARED_LIBS "Build using shared libraries" ON) +option(PREFER_STATIC_LINKING "Prefer static linking for the ryzenadj executable" OFF) + +message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") + +#Enable LTO +include(CheckIPOSupported) +check_ipo_supported(RESULT supported OUTPUT error) +if( supported ) + message(STATUS "IPO / LTO enabled") + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) +endif() + +AUX_SOURCE_DIRECTORY(./ SRC_DIR) + +if(WIN32) +set(OS_SOURCE lib/win32/osdep_win32.cpp) +set(OS_LINK_LIBRARY WinRing0x64) +set(OS_LINK_DIR ./win32) +LINK_DIRECTORIES(${OS_LINK_DIR}) +elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") +set(OS_SOURCE lib/linux/osdep_linux.c lib/linux/osdep_linux_mem.c lib/linux/osdep_linux_smu_kernel_module.c) + +find_package(PkgConfig REQUIRED) +pkg_check_modules(LIBPCI REQUIRED libpci) + +add_library(ryzenadj_libpci INTERFACE) +add_library(RyzenAdj::Libpci ALIAS ryzenadj_libpci) +target_include_directories(ryzenadj_libpci INTERFACE ${LIBPCI_INCLUDE_DIRS}) +target_compile_options(ryzenadj_libpci INTERFACE ${LIBPCI_CFLAGS_OTHER}) +target_link_libraries(ryzenadj_libpci INTERFACE ${LIBPCI_LDFLAGS}) + +add_library(ryzenadj_libpci_static INTERFACE) +add_library(RyzenAdj::LibpciStatic ALIAS ryzenadj_libpci_static) +target_include_directories(ryzenadj_libpci_static INTERFACE ${LIBPCI_STATIC_INCLUDE_DIRS}) +target_compile_options(ryzenadj_libpci_static INTERFACE ${LIBPCI_STATIC_CFLAGS_OTHER}) +target_link_libraries(ryzenadj_libpci_static INTERFACE ${LIBPCI_STATIC_LDFLAGS}) + +set(OS_LINK_LIBRARY RyzenAdj::Libpci) +set(OS_STATIC_LINK_LIBRARY RyzenAdj::LibpciStatic) +else() +message(FATAL_ERROR "Unsupported operating system: ${CMAKE_SYSTEM_NAME}") +endif() + +set(COMMON_SOURCES lib/nb_smu_ops.c lib/api.c lib/cpuid.c) +add_definitions(-D_LIBRYZENADJ_INTERNAL) + +ADD_EXECUTABLE(${PROJECT_NAME} ${OS_SOURCE} ${COMMON_SOURCES} argparse.c main.c) +if(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND (PREFER_STATIC_LINKING OR NOT BUILD_SHARED_LIBS)) + if(PREFER_STATIC_LINKING) + set_property(TARGET ${PROJECT_NAME} APPEND_STRING PROPERTY LINK_FLAGS " -static") + endif() + target_link_libraries(${PROJECT_NAME} ${OS_STATIC_LINK_LIBRARY}) +else() + target_link_libraries(${PROJECT_NAME} ${OS_LINK_LIBRARY}) +endif() +#SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES LINKER_LANGUAGE C) +ADD_LIBRARY (libryzenadj ${OS_SOURCE} ${COMMON_SOURCES}) +set_target_properties(libryzenadj PROPERTIES PREFIX "") +if(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND NOT BUILD_SHARED_LIBS) + target_link_libraries(libryzenadj ${OS_STATIC_LINK_LIBRARY}) +else() + target_link_libraries(libryzenadj ${OS_LINK_LIBRARY}) +endif() +#SET_TARGET_PROPERTIES(libryzenadj PROPERTIES LINKER_LANGUAGE C) +install(TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/os/licenses/ryzenadj/src/LICENSE b/os/licenses/ryzenadj/src/LICENSE new file mode 100644 index 00000000..0a041280 --- /dev/null +++ b/os/licenses/ryzenadj/src/LICENSE @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser 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 +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/os/licenses/ryzenadj/src/README.md b/os/licenses/ryzenadj/src/README.md new file mode 100644 index 00000000..f64a3ba0 --- /dev/null +++ b/os/licenses/ryzenadj/src/README.md @@ -0,0 +1,210 @@ +# RyzenAdj +Adjust power management settings for Ryzen Mobile Processors. + +[![Build Status](https://travis-ci.org/FlyGoat/RyzenAdj.svg?branch=master)](https://travis-ci.org/FlyGoat/RyzenAdj) + +Based on: [FlyGoat/ryzen_nb_smu](https://github.com/flygoat/ryzen_nb_smu) + +RyzenAdjUI_WPF by "JustSkill" is no longer maintained, for GUI please see [Universal x86 Tuning Utility](https://github.com/JamesCJ60/Universal-x86-Tuning-Utility) or [ryzen-controller-team/ryzen-controller](https://gitlab.com/ryzen-controller-team/ryzen-controller/). + +## Usage +The command line interface is identical on both Windows and Unix-Like OS. + +You should run it with Administrator on Windows or root on Linux. + +You can write a shell script or bat to do it automatically. + +``` +$./ryzenadj -h +Usage: ryzenadj [options] + + Ryzen Power Management adjust tool. + + -h, --help show this help message and exit + +Options + -i, --info Show information and most important power metrics after adjustment + --dump-table Show whole power metric table before and after adjustment + +Settings + -a, --stapm-limit= Sustained Power Limit - STAPM LIMIT (mW) + -b, --fast-limit= Actual Power Limit - PPT LIMIT FAST (mW) + -c, --slow-limit= Average Power Limit - PPT LIMIT SLOW (mW) + -d, --slow-time= Slow PPT Constant Time (s) + -e, --stapm-time= STAPM constant time (s) + -f, --tctl-temp= Tctl Temperature Limit (degree C) + -g, --vrm-current= VRM Current Limit - TDC LIMIT VDD (mA) + -j, --vrmsoc-current= VRM SoC Current Limit - TDC LIMIT SoC (mA) + -k, --vrmmax-current= VRM Maximum Current Limit - EDC LIMIT VDD (mA) + -l, --vrmsocmax-current= VRM SoC Maximum Current Limit - EDC LIMIT SoC (mA) + -m, --psi0-current= PSI0 VDD Current Limit (mA) + -n, --psi0soc-current= PSI0 SoC Current Limit (mA) + -o, --max-socclk-frequency= Maximum SoC Clock Frequency (MHz) + -p, --min-socclk-frequency= Minimum SoC Clock Frequency (MHz) + -q, --max-fclk-frequency= Maximum Transmission (CPU-GPU) Frequency (MHz) + -r, --min-fclk-frequency= Minimum Transmission (CPU-GPU) Frequency (MHz) + -s, --max-vcn= Maximum Video Core Next (VCE - Video Coding Engine) (MHz) + -t, --min-vcn= Minimum Video Core Next (VCE - Video Coding Engine) (MHz) + -u, --max-lclk= Maximum Data Launch Clock (MHz) + -v, --min-lclk= Minimum Data Launch Clock (MHz) + -w, --max-gfxclk= Maximum GFX Clock (MHz) + -x, --min-gfxclk= Minimum GFX Clock (MHz) + -y, --prochot-deassertion-ramp= Ramp Time After Prochot is Deasserted: limit power based on value, higher values does apply tighter limits after prochot is over + --apu-skin-temp= APU Skin Temperature Limit - STT LIMIT APU (degree C) + --dgpu-skin-temp= dGPU Skin Temperature Limit - STT LIMIT dGPU (degree C) + --apu-slow-limit= APU PPT Slow Power limit for A+A dGPU platform - PPT LIMIT APU (mW) + --skin-temp-limit= Skin Temperature Power Limit (mW) + --power-saving Hidden options to improve power efficiency (is set when AC unplugged): behavior depends on CPU generation, Device and Manufacture + --max-performance Hidden options to improve performance (is set when AC plugged in): behavior depends on CPU generation, Device and Manufacture +``` + +### Demo +If I'm going to set all the Power Limit to 45W, and Tctl to 90 °C, +then the command line should be: + + ./ryzenadj --stapm-limit=45000 --fast-limit=45000 --slow-limit=45000 --tctl-temp=90 + +### Documentation +- [Supported Models](https://github.com/FlyGoat/RyzenAdj/wiki/Supported-Models) +- [Renoir Tuning Guide](https://github.com/FlyGoat/RyzenAdj/wiki/Renoir-Tuning-Guide) +- [Options](https://github.com/FlyGoat/RyzenAdj/wiki/Options) +- [FAQ](https://github.com/FlyGoat/RyzenAdj/wiki/FAQ) + +## Installation + +You don't need to install RyzenAdj because it does not need configuration, everything is set via arguments +However, some settings could get overwritten by power management features of your device, and you need to regularly set your values again. + +We did provide some examples for automation. And these require configuration during installation. + +### Linux Installation + +Because it is very easy to build the latest version of RyzenAdj on Linux, we don't provide precompiled packages for distributions. +Just follow the build instructions below and you are ready to use it. + +### Windows Installation + +Before you start installing anything, it is highly recommended getting familiar with RyzenAdj to find out what can be done on your device. +Use the CLI `ryzenadj.exe` to test the support of your device and to benchmark the effects of each setting. +If your values don't stay persistent you may want to consider installing our example script for automation. + +1. Prepare your favorite RyzenAdj arguments +1. Copy the content of your RyzenAdj folder to the final destination +1. Put your configuration into `readjustService.ps1` and test it as administrator until everything works as expected +1. Install `readjustService.ps1` as Task for Windows Task Scheduler by running `installServiceTask.bat` + +Deinstallation of the Task can be done via `uninstallServiceTask.bat` + +Over Windows Task Scheduler you can check if it is running. It is called `RyzenAdj` below `AMD` folder. +Or just run + + SCHTASKS /query /TN "AMD\RyzenAdj" + +## Build + +### Build Requirements + +Building this tool requires C & C++ compilers as well as **cmake**. + +### Linux + +RyzenAdj needs elevated access to the NB config space. This can be achieved by using either one of +these two methods: + +* Using libpci and exposing `/dev/mem` +* Using the ryzen\_smu kernel module + +RyzenAdj will try ryzen\_smu first, and then fallback to /dev/mem, if no compatible smu driver is found. +The minimum supported version of ryzen_smu is 0.1.7 +If no backend is available, RyzenAdj will fail initialization. + +_**Please note that `/dev/mem` access may be restricted, for security reasons, in your kernel config**_ + +Please make sure that you have libpci dependency before compiling. + +On Debian-based distros this is covered by installing **pcilib-dev** package: + + sudo apt install build-essential cmake libpci-dev + +On Fedora: + + sudo dnf install cmake gcc-c++ pciutils-devel + +On Arch: + + sudo pacman -S base-devel pciutils cmake + + +On OpenSUSE Tumbleweed: + + sudo zypper in cmake gcc14-c++ pciutils-devel + +You may need to add the `iomem=relaxed` param to your kernel params on Tumbleweed, or [you may run into errors at runtime](https://github.com/FlyGoat/RyzenAdj/issues/241). + +If your Distribution is not supported, try finding the packages or use [Distrobox](https://github.com/89luca89/distrobox) or [Toolbox](https://docs.fedoraproject.org/en-US/fedora-silverblue/toolbox/) instead. + +The simplest way to build it: + + git clone https://github.com/FlyGoat/RyzenAdj.git + cd RyzenAdj + rm -r win32 + mkdir build && cd build + cmake -DCMAKE_BUILD_TYPE=Release .. + make + if [ -d ~/.local/bin ]; then ln -s $(readlink -f ryzenadj) ~/.local/bin/ryzenadj && echo "symlinked to ~/.local/bin/ryzenadj"; fi + if [ -d ~/.bin ]; then ln -s $(readlink -f ryzenadj) ~/.bin/ryzenadj && echo "symlinked to ~/.bin/ryzenadj"; fi + +#### Ryzen\_smu + +To let RyzenAdj use ryzen\_smu module, you have to install it first, it is not part of the linux kernel. + +On Fedora: + +```sh +sudo dnf install cmake gcc gcc-c++ dkms openssl +``` + +Clone and install ryzen\_smu: + +```sh +git clone https://github.com/amkillam/ryzen_smu # Active fork of the original module +(cd ryzen_smu/ && sudo make dkms-install) +``` + +If you are using secure boot, you have to enroll the UEFI keys which dkms has generated on its first +run. These have to be added to your machines UEFI key database. This can be done with following +command, which will ask you to set a password. This password is only needed _one single time_ later +in the MOK manager. + +```sh +sudo mokutil --import /var/lib/dkms/mok.pub +``` + +Restart your system. This will boot into the MOK manager. Choose `Enroll MOK`, enter your password +and then reboot. +[Here](https://github.com/dell/dkms/blob/f7f526c145ecc01fb4ac4eab3009b1879b14ced4/README.md#secure-boot) +are some screenshots describing the process. + +The module is now loaded and visible via dmesg. It will show a message about the kernel being +tainted, but this just means it loaded a (potentially proprietary) binary blob. + +Build and install RyzenAdj: + +```sh +git clone https://github.com/FlyGoat/RyzenAdj +cd RyzenAdj +cmake -B build -DCMAKE_BUILD_TYPE=Release +make -C build -j"$(nproc)" +sudo cp -v build/ryzenadj /usr/local/bin/ +``` + +### Windows + +It can be built by Visual Studio + MSVC automatically, or Clang + Nmake in command line. +However, as for now, MingW-gcc can't be used to compile for some reason. + +Required dll is included in ./win32 of source tree. Please put the dll +library and sys driver in the same folder with ryzenadj.exe. + +We don't recommend you to build by yourself on Windows since the environment configuration +is very complicated. If you would like to use ryzenadj functions in your program, see libryzenadj. diff --git a/os/licenses/ryzenadj/src/argparse.c b/os/licenses/ryzenadj/src/argparse.c new file mode 100644 index 00000000..ee39f939 --- /dev/null +++ b/os/licenses/ryzenadj/src/argparse.c @@ -0,0 +1,435 @@ +/** + * Copyright (C) 2012-2015 Yecheng Fu + * All rights reserved. + * + * Use of this source code is governed by a MIT-style license that can be found + * in the LICENSE file. + */ +#include +#include +#include +#include +#include +#include "argparse.h" + +#define OPT_UNSET 1 +#define OPT_LONG (1 << 1) + +static const char * +prefix_skip(const char *str, const char *prefix) +{ + size_t len = strlen(prefix); + return strncmp(str, prefix, len) ? NULL : str + len; +} + +static int +prefix_cmp(const char *str, const char *prefix) +{ + for (;; str++, prefix++) + if (!*prefix) { + return 0; + } else if (*str != *prefix) { + return (unsigned char)*prefix - (unsigned char)*str; + } +} + +static void +argparse_error(struct argparse *self, const struct argparse_option *opt, + const char *reason, int flags) +{ + (void)self; + if (flags & OPT_LONG) { + fprintf(stderr, "error: option `--%s` %s\n", opt->long_name, reason); + } else { + fprintf(stderr, "error: option `-%c` %s\n", opt->short_name, reason); + } + exit(1); +} + +static int +argparse_getvalue(struct argparse *self, const struct argparse_option *opt, + int flags) +{ + const char *s = NULL; + char buf[256]; + buf[0] = 0; + char *pbuf = buf; + + if (!opt->value) + goto skipped; + switch (opt->type) { + case ARGPARSE_OPT_BOOLEAN: + if (flags & OPT_UNSET) { + *(int *)opt->value = *(int *)opt->value - 1; + } else { + *(int *)opt->value = *(int *)opt->value + 1; + } + if (*(int *)opt->value < 0) { + *(int *)opt->value = 0; + } + break; + case ARGPARSE_OPT_BIT: + if (flags & OPT_UNSET) { + *(int *)opt->value &= ~opt->data; + } else { + *(int *)opt->value |= opt->data; + } + break; + case ARGPARSE_OPT_STRING: + if (self->optvalue) { + *(const char **)opt->value = self->optvalue; + self->optvalue = NULL; + } else if (self->argc > 1) { + self->argc--; + *(const char **)opt->value = *++self->argv; + } else { + argparse_error(self, opt, "requires a value", flags); + } + break; + case ARGPARSE_OPT_INTEGER: + errno = 0; + if (self->optvalue) { + *(int *)opt->value = strtol(self->optvalue, (char **)&s, 0); + self->optvalue = NULL; + } else if (self->argc > 1) { + self->argc--; + *(int *)opt->value = strtol(*++self->argv, (char **)&s, 0); + } else { + argparse_error(self, opt, "requires a value", flags); + } + if (errno){ + #ifdef _WIN32 + strerror_s(buf, sizeof(buf), errno); + #else + strerror_r(errno, buf, sizeof(buf)); + #endif + argparse_error(self, opt, pbuf, flags); + } + if (s[0] != '\0') + argparse_error(self, opt, "expects an integer value", flags); + break; + case ARGPARSE_OPT_U32: + errno = 0; + if (self->optvalue) { + *(uint32_t *)opt->value = strtoul(self->optvalue, (char **)&s, 0); + self->optvalue = NULL; + } else if (self->argc > 1) { + self->argc--; + *(uint32_t *)opt->value = strtoul(*++self->argv, (char **)&s, 0); + } else { + argparse_error(self, opt, "requires a value", flags); + } + if (errno){ + #ifdef _WIN32 + strerror_s(buf, sizeof(buf), errno); + #else + strerror_r(errno, buf, sizeof(buf)); + #endif + argparse_error(self, opt, pbuf, flags); + } + if (s[0] != '\0') + argparse_error(self, opt, "expects an unsigned 32-bit integer value", flags); + break; + case ARGPARSE_OPT_FLOAT: + errno = 0; + if (self->optvalue) { + *(float *)opt->value = strtof(self->optvalue, (char **)&s); + self->optvalue = NULL; + } else if (self->argc > 1) { + self->argc--; + *(float *)opt->value = strtof(*++self->argv, (char **)&s); + } else { + argparse_error(self, opt, "requires a value", flags); + } + if (errno){ + #ifdef _WIN32 + strerror_s(buf, sizeof(buf), errno); + #else + strerror_r(errno, buf, sizeof(buf)); + #endif + argparse_error(self, opt, pbuf, flags); + } + if (s[0] != '\0') + argparse_error(self, opt, "expects a numerical value", flags); + break; + default: + assert(0); + } + +skipped: + if (opt->callback) { + return opt->callback(self, opt); + } + + return 0; +} + +static void +argparse_options_check(const struct argparse_option *options) +{ + for (; options->type != ARGPARSE_OPT_END; options++) { + switch (options->type) { + case ARGPARSE_OPT_END: + case ARGPARSE_OPT_BOOLEAN: + case ARGPARSE_OPT_BIT: + case ARGPARSE_OPT_INTEGER: + case ARGPARSE_OPT_U32: + case ARGPARSE_OPT_FLOAT: + case ARGPARSE_OPT_STRING: + case ARGPARSE_OPT_GROUP: + continue; + default: + fprintf(stderr, "wrong option type: %d", options->type); + break; + } + } +} + +static int +argparse_short_opt(struct argparse *self, const struct argparse_option *options) +{ + for (; options->type != ARGPARSE_OPT_END; options++) { + if (options->short_name == *self->optvalue) { + self->optvalue = self->optvalue[1] ? self->optvalue + 1 : NULL; + return argparse_getvalue(self, options, 0); + } + } + return -2; +} + +static int +argparse_long_opt(struct argparse *self, const struct argparse_option *options) +{ + for (; options->type != ARGPARSE_OPT_END; options++) { + const char *rest; + int opt_flags = 0; + if (!options->long_name) + continue; + + rest = prefix_skip(self->argv[0] + 2, options->long_name); + if (!rest) { + // negation disabled? + if (options->flags & OPT_NONEG) { + continue; + } + // only OPT_BOOLEAN/OPT_BIT supports negation + if (options->type != ARGPARSE_OPT_BOOLEAN && options->type != + ARGPARSE_OPT_BIT) { + continue; + } + + if (prefix_cmp(self->argv[0] + 2, "no-")) { + continue; + } + rest = prefix_skip(self->argv[0] + 2 + 3, options->long_name); + if (!rest) + continue; + opt_flags |= OPT_UNSET; + } + if (*rest) { + if (*rest != '=') + continue; + self->optvalue = rest + 1; + } + return argparse_getvalue(self, options, opt_flags | OPT_LONG); + } + return -2; +} + +int +argparse_init(struct argparse *self, struct argparse_option *options, + const char *const *usages, int flags) +{ + memset(self, 0, sizeof(*self)); + self->options = options; + self->usages = usages; + self->flags = flags; + self->description = NULL; + self->epilog = NULL; + return 0; +} + +void +argparse_describe(struct argparse *self, const char *description, + const char *epilog) +{ + self->description = description; + self->epilog = epilog; +} + +int +argparse_parse(struct argparse *self, int argc, const char **argv) +{ + self->argc = argc - 1; + self->argv = argv + 1; + self->out = argv; + + argparse_options_check(self->options); + if(!self->argc) { + argparse_usage(self); + exit(1); + } + + for (; self->argc; self->argc--, self->argv++) { + const char *arg = self->argv[0]; + if (arg[0] != '-' || !arg[1]) { + if (self->flags & ARGPARSE_STOP_AT_NON_OPTION) { + goto end; + } + if (self->flags & ARGPARSE_NON_OPTION_IS_INVALID) { + goto unknown; + } + // if it's not option or is a single char '-', copy verbatim + self->out[self->cpidx++] = self->argv[0]; + continue; + } + // short option + if (arg[1] != '-') { + self->optvalue = arg + 1; + switch (argparse_short_opt(self, self->options)) { + case -1: + break; + case -2: + goto unknown; + } + while (self->optvalue) { + switch (argparse_short_opt(self, self->options)) { + case -1: + break; + case -2: + goto unknown; + } + } + continue; + } + // if '--' presents + if (!arg[2]) { + self->argc--; + self->argv++; + break; + } + // long option + switch (argparse_long_opt(self, self->options)) { + case -1: + break; + case -2: + goto unknown; + } + continue; + +unknown: + fprintf(stderr, "error: unknown option `%s`\n", self->argv[0]); + argparse_usage(self); + exit(1); + } + +end: + memmove(self->out + self->cpidx, self->argv, + self->argc * sizeof(*self->out)); + self->out[self->cpidx + self->argc] = NULL; + + return self->cpidx + self->argc; +} + +void +argparse_usage(struct argparse *self) +{ + if (self->usages) { + fprintf(stdout, "Usage: %s\n", *self->usages++); + while (*self->usages && **self->usages) + fprintf(stdout, " or: %s\n", *self->usages++); + } else { + fprintf(stdout, "Usage:\n"); + } + + // print description + if (self->description) + fprintf(stdout, "%s\n", self->description); + + fputc('\n', stdout); + + const struct argparse_option *options; + + // figure out best width + size_t usage_opts_width = 0; + size_t len; + options = self->options; + for (; options->type != ARGPARSE_OPT_END; options++) { + len = 0; + if ((options)->short_name) { + len += 2; + } + if ((options)->short_name && (options)->long_name) { + len += 2; // separator ", " + } + if ((options)->long_name) { + len += strlen((options)->long_name) + 2; + } + if (options->type == ARGPARSE_OPT_INTEGER) { + len += strlen("="); + } + if (options->type == ARGPARSE_OPT_U32) { + len += strlen("="); + } + if (options->type == ARGPARSE_OPT_FLOAT) { + len += strlen("="); + } else if (options->type == ARGPARSE_OPT_STRING) { + len += strlen("="); + } + len = (len + 3) - ((len + 3) & 3); + if (usage_opts_width < len) { + usage_opts_width = len; + } + } + usage_opts_width += 4; // 4 spaces prefix + + options = self->options; + for (; options->type != ARGPARSE_OPT_END; options++) { + size_t pos = 0; + size_t pad = 0; + if (options->type == ARGPARSE_OPT_GROUP) { + fputc('\n', stdout); + fprintf(stdout, "%s", options->help); + fputc('\n', stdout); + continue; + } + pos = fprintf(stdout, " "); + if (options->short_name) { + pos += fprintf(stdout, "-%c", options->short_name); + } + if (options->long_name && options->short_name) { + pos += fprintf(stdout, ", "); + } + if (options->long_name) { + pos += fprintf(stdout, "--%s", options->long_name); + } + if (options->type == ARGPARSE_OPT_INTEGER) { + pos += fprintf(stdout, "="); + } else if (options->type == ARGPARSE_OPT_U32) { + pos += fprintf(stdout, "="); + } else if (options->type == ARGPARSE_OPT_FLOAT) { + pos += fprintf(stdout, "="); + } else if (options->type == ARGPARSE_OPT_STRING) { + pos += fprintf(stdout, "="); + } + if (pos <= usage_opts_width) { + pad = usage_opts_width - pos; + } else { + fputc('\n', stdout); + pad = usage_opts_width; + } + fprintf(stdout, "%*s%s\n", (int)pad + 2, "", options->help); + } + + // print epilog + if (self->epilog) + fprintf(stdout, "%s\n", self->epilog); +} + +int +argparse_help_cb(struct argparse *self, const struct argparse_option *option) +{ + (void)option; + argparse_usage(self); + exit(0); +} diff --git a/os/licenses/ryzenadj/src/argparse.h b/os/licenses/ryzenadj/src/argparse.h new file mode 100644 index 00000000..c516be66 --- /dev/null +++ b/os/licenses/ryzenadj/src/argparse.h @@ -0,0 +1,133 @@ +/** + * Copyright (C) 2012-2015 Yecheng Fu + * All rights reserved. + * + * Use of this source code is governed by a MIT-style license that can be found + * in the LICENSE file. + */ +#ifndef ARGPARSE_H +#define ARGPARSE_H + +/* For c++ compatibility */ +#ifdef __cplusplus +extern "C" { +#endif + +#include + +struct argparse; +struct argparse_option; + +typedef int argparse_callback (struct argparse *self, + const struct argparse_option *option); + +enum argparse_flag { + ARGPARSE_STOP_AT_NON_OPTION = 1, + ARGPARSE_NON_OPTION_IS_INVALID = 2, +}; + +enum argparse_option_type { + /* special */ + ARGPARSE_OPT_END, + ARGPARSE_OPT_GROUP, + /* options with no arguments */ + ARGPARSE_OPT_BOOLEAN, + ARGPARSE_OPT_BIT, + /* options with arguments (optional or required) */ + ARGPARSE_OPT_INTEGER, + ARGPARSE_OPT_U32, + ARGPARSE_OPT_FLOAT, + ARGPARSE_OPT_STRING, +}; + +enum argparse_option_flags { + OPT_NONEG = 1, /* disable negation */ +}; + +/** + * argparse option + * + * `type`: + * holds the type of the option, you must have an ARGPARSE_OPT_END last in your + * array. + * + * `short_name`: + * the character to use as a short option name, '\0' if none. + * + * `long_name`: + * the long option name, without the leading dash, NULL if none. + * + * `value`: + * stores pointer to the value to be filled. + * + * `help`: + * the short help message associated to what the option does. + * Must never be NULL (except for ARGPARSE_OPT_END). + * + * `callback`: + * function is called when corresponding argument is parsed. + * + * `data`: + * associated data. Callbacks can use it like they want. + * + * `flags`: + * option flags. + */ +struct argparse_option { + enum argparse_option_type type; + const char short_name; + const char *long_name; + void *value; + const char *help; + argparse_callback *callback; + intptr_t data; + int flags; +}; + +/** + * argpparse + */ +struct argparse { + // user supplied + const struct argparse_option *options; + const char *const *usages; + int flags; + const char *description; // a description after usage + const char *epilog; // a description at the end + // internal context + int argc; + const char **argv; + const char **out; + int cpidx; + const char *optvalue; // current option value +}; + +// built-in callbacks +int argparse_help_cb(struct argparse *self, + const struct argparse_option *option); + +// built-in option macros +#define OPT_END() { ARGPARSE_OPT_END, 0, NULL, NULL, 0, NULL, 0, 0 } +#define OPT_BOOLEAN(...) { ARGPARSE_OPT_BOOLEAN, __VA_ARGS__ } +#define OPT_BIT(...) { ARGPARSE_OPT_BIT, __VA_ARGS__ } +#define OPT_INTEGER(...) { ARGPARSE_OPT_INTEGER, __VA_ARGS__ } +#define OPT_U32(...) { ARGPARSE_OPT_U32, __VA_ARGS__ } +#define OPT_FLOAT(...) { ARGPARSE_OPT_FLOAT, __VA_ARGS__ } +#define OPT_STRING(...) { ARGPARSE_OPT_STRING, __VA_ARGS__ } +#define OPT_GROUP(h) { ARGPARSE_OPT_GROUP, 0, NULL, NULL, h, NULL, 0, 0 } +#define OPT_HELP() OPT_BOOLEAN('h', "help", NULL, \ + "show this help message and exit", \ + argparse_help_cb, 0, OPT_NONEG) + +int argparse_init(struct argparse *self, struct argparse_option *options, + const char *const *usages, int flags); +void argparse_describe(struct argparse *self, const char *description, + const char *epilog); +int argparse_parse(struct argparse *self, int argc, const char **argv); +void argparse_usage(struct argparse *self); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/os/licenses/ryzenadj/src/examples/pmtable-example.py b/os/licenses/ryzenadj/src/examples/pmtable-example.py new file mode 100644 index 00000000..c2ab4106 --- /dev/null +++ b/os/licenses/ryzenadj/src/examples/pmtable-example.py @@ -0,0 +1,59 @@ +import os, sys, time +from ctypes import * +from shutil import copyfile + +lib_path = os.path.dirname(os.path.abspath(__file__)) +os.chdir(lib_path) + +if sys.platform == 'win32' or sys.platform == 'cygwin': + try: + os.add_dll_directory(lib_path) + except AttributeError: + pass #not needed for old python version + + winring0_driver_file_path = os.path.join(os.path.dirname(os.path.abspath(sys.executable)), 'WinRing0x64.sys') + if not os.path.isfile(winring0_driver_file_path): + copyfile(os.path.join(lib_path, 'WinRing0x64.sys'), winring0_driver_file_path) + + lib = cdll.LoadLibrary('libryzenadj') +else: + lib = cdll.LoadLibrary('libryzenadj.so') + +# define ctype mappings for types which can not be mapped automatically +lib.init_ryzenadj.restype = c_void_p +lib.get_table_ver.argtypes = [c_void_p] +lib.get_table_size.argtypes = [c_void_p] +lib.get_table_values.restype = POINTER(c_float) +lib.get_table_values.argtypes = [c_void_p] +lib.refresh_table.argtypes = [c_void_p] + +ry = lib.init_ryzenadj() + +if not ry: + sys.exit("RyzenAdj could not get initialized") + +print("pmtable version: {:x}".format(lib.get_table_ver(ry))) + +input("Press any key to show all pmtable values...") + +pmtable_size = lib.get_table_size(ry) // 4 +pmtable = lib.get_table_values(ry) + +while True: + lib.refresh_table(ry) + columns, lines = os.get_terminal_size() + table_columns = columns // 16 # 16 chars per table entry + os.system('cls' if sys.platform == 'win32' else 'clear') + table_rows = 0 + for index in (range(pmtable_size)): + sys.stdout.write("{:3d}:{:8.2f}\t".format(index, pmtable[index])) + if index % table_columns == table_columns - 1: + sys.stdout.write('\n') + table_rows += 1 + if table_rows >= lines - 1: + sys.stdout.write('{:d} More entries ...'.format(pmtable_size - 1 - index)) + break + + if index % table_columns != table_columns - 1: sys.stdout.write('\n') + sys.stdout.flush() + time.sleep(1) diff --git a/os/licenses/ryzenadj/src/examples/readjust.py b/os/licenses/ryzenadj/src/examples/readjust.py new file mode 100644 index 00000000..74afb15a --- /dev/null +++ b/os/licenses/ryzenadj/src/examples/readjust.py @@ -0,0 +1,70 @@ +import os, sys, time +from ctypes import * +from shutil import copyfile + +lib_path = os.path.dirname(os.path.abspath(__file__)) +os.chdir(lib_path) + +if sys.platform == 'win32' or sys.platform == 'cygwin': + try: + os.add_dll_directory(lib_path) + except AttributeError: + pass #not needed for old python version + + winring0_driver_file_path = os.path.join(os.path.dirname(os.path.abspath(sys.executable)), 'WinRing0x64.sys') + if not os.path.isfile(winring0_driver_file_path): + copyfile(os.path.join(lib_path, 'WinRing0x64.sys'), winring0_driver_file_path) + + lib = cdll.LoadLibrary('libryzenadj') +else: + lib = cdll.LoadLibrary('libryzenadj.so') + +# define ctype mappings for types which can not be mapped automatically +lib.init_ryzenadj.restype = c_void_p +lib.refresh_table.argtypes = [c_void_p] +lib.get_fast_limit.restype = c_float +lib.get_fast_limit.argtypes = [c_void_p] + +ry = lib.init_ryzenadj() + +if not ry: + sys.exit("RyzenAdj could not get initialized") + +error_messages = { + -1: "{:s} is not supported on this family\n", + -3: "{:s} is not supported on this SMU\n", + -4: "{:s} is rejected by SMU\n" +} + +def adjust(field, value): + function_name = "set_" + field + adjust_func = lib.__getattr__(function_name) + adjust_func.argtypes = [c_void_p, c_ulong] + res = adjust_func(ry, value) + if res: + error = error_messages.get(res, "{:s} did fail with {:d}\n") + sys.stderr.write(error.format(function_name, res)); + +def enable(field): + function_name = "set_" + field + adjust_func = lib.__getattr__(function_name) + adjust_func.argtypes = [c_void_p] + res = adjust_func(ry) + if res: + error = error_messages.get(res, "{:s} did fail with {:d}\n") + sys.stderr.write(error.format(function_name, res)); + +print("Monitor if fast limit is not 35W") +while True: + lib.refresh_table(ry) + limit = round(lib.get_fast_limit(ry)) + if limit != 35: + print("reapply limits, because old limit was {:d}".format(limit)) + adjust("fast_limit", 35000) + adjust("slow_limit", 22000) + adjust("slow_time", 30) + adjust("tctl_temp", 97) + adjust("apu_skin_temp_limit", 50) + adjust("vrmmax_current", 100000) + enable("max_performance") + time.sleep(3) diff --git a/os/licenses/ryzenadj/src/lib/api.c b/os/licenses/ryzenadj/src/lib/api.c new file mode 100644 index 00000000..f4007edd --- /dev/null +++ b/os/licenses/ryzenadj/src/lib/api.c @@ -0,0 +1,2825 @@ +// SPDX-License-Identifier: LGPL +/* Copyright (C) 2018-2019 Jiaxun Yang */ +/* RyzenAdj API */ +#include +#include + +#include "ryzenadj.h" +#include "math.h" + +#ifndef _WIN32 +#include +#define Sleep(x) usleep((x)*1000) +#endif + + +EXP ryzen_access CALL init_ryzenadj() { + const enum ryzen_family family = cpuid_get_family(); + ryzen_access ry; + + if (family == FAM_UNKNOWN) + return NULL; + + ry = (ryzen_access)malloc(sizeof(*ry)); + + if (!ry){ + printf("Out of memory\n"); + return NULL; + } + + memset(ry, 0, sizeof(*ry)); + + ry->family = family; + //init version and power metric table only on demand to avoid unnecessary SMU writes + ry->bios_if_ver = 0; + ry->table_values = NULL; + + ry->os_access = init_os_access_obj(); + if(!ry->os_access){ + printf("Unable to get os_access Obj, check permission\n"); + return NULL; + } + + ry->mp1_smu = get_smu(ry->os_access, TYPE_MP1); + if(!ry->mp1_smu){ + printf("Unable to get MP1 SMU Obj\n"); + goto err_exit; + } + + ry->psmu = get_smu(ry->os_access, TYPE_PSMU); + if(!ry->psmu){ + printf("Unable to get RSMU Obj\n"); + goto err_exit; + } + + return ry; + +err_exit: + cleanup_ryzenadj(ry); + return NULL; +} + +EXP void CALL cleanup_ryzenadj(ryzen_access ry) { + if (ry == NULL) + return; + + free(ry->mp1_smu); + free(ry->psmu); + free_os_access_obj(ry->os_access); + free(ry->table_values); + free(ry); +} + +EXP enum ryzen_family get_cpu_family(ryzen_access ry) +{ + return ry->family; +} + +EXP int get_bios_if_ver(ryzen_access ry) +{ + if(ry->bios_if_ver) + return ry->bios_if_ver; + + smu_service_args_t args = {0, 0, 0, 0, 0, 0}; + smu_service_req(ry->mp1_smu, 0x3, &args); + ry->bios_if_ver = args.arg0; + return ry->bios_if_ver; +} + +#define _return_translated_smu_error(SMU_RESP) \ +do { \ + if (SMU_RESP == REP_MSG_UnknownCmd) { \ + printf("%s is unsupported\n", __func__); \ + return ADJ_ERR_SMU_UNSUPPORTED; \ + } else if (SMU_RESP == REP_MSG_CmdRejectedPrereq){ \ + printf("%s was rejected\n", __func__); \ + return ADJ_ERR_SMU_REJECTED; \ + } else if (SMU_RESP == REP_MSG_CmdRejectedBusy) { \ + printf("%s was rejected - busy\n", __func__); \ + return ADJ_ERR_SMU_REJECTED; \ + } else if (SMU_RESP == REP_MSG_Failed) { \ + printf("%s failed\n", __func__); \ + return ADJ_ERR_SMU_REJECTED; \ + } else { \ + printf("%s failed with unknown response %x\n", __func__, SMU_RESP); \ + return ADJ_ERR_SMU_REJECTED; \ + } \ +} while (0); + +static int request_table_ver_and_size(ryzen_access ry) { + unsigned int get_table_ver_msg; + int resp; + + switch (ry->family) { + case FAM_RAVEN: + case FAM_PICASSO: + case FAM_DALI: + get_table_ver_msg = 0xC; + break; + case FAM_RENOIR: + case FAM_LUCIENNE: + case FAM_CEZANNE: + case FAM_REMBRANDT: + case FAM_PHOENIX: + case FAM_HAWKPOINT: + case FAM_KRACKANPOINT: + case FAM_STRIXPOINT: + case FAM_STRIXHALO: + get_table_ver_msg = 0x6; + break; + default: + printf("request_table_ver_and_size is not supported on this family\n"); + return ADJ_ERR_FAM_UNSUPPORTED; + } + + smu_service_args_t args = {0, 0, 0, 0, 0, 0}; + resp = smu_service_req(ry->psmu, get_table_ver_msg, &args); + ry->table_ver = args.arg0; + + switch (ry->table_ver) { + case 0x1E0001: ry->table_size = 0x568; break; + case 0x1E0002: ry->table_size = 0x580; break; + case 0x1E0003: ry->table_size = 0x578; break; + case 0x1E0004: + case 0x1E0005: + case 0x1E000A: + case 0x1E0101: ry->table_size = 0x608; break; + case 0x370000: ry->table_size = 0x794; break; + case 0x370001: ry->table_size = 0x884; break; + case 0x370002: ry->table_size = 0x88C; break; + case 0x370003: + case 0x370004: ry->table_size = 0x8AC; break; + case 0x370005: ry->table_size = 0x8C8; break; + case 0x3F0000: ry->table_size = 0x7AC; break; + case 0x400001: ry->table_size = 0x910; break; + case 0x400002: ry->table_size = 0x928; break; + case 0x400003: ry->table_size = 0x94C; break; + case 0x400004: + case 0x400005: ry->table_size = 0x944; break; + case 0x450004: ry->table_size = 0xAA4; break; + case 0x450005: ry->table_size = 0xAB0; break; + case 0x4C0003: ry->table_size = 0xB18; break; + case 0x4C0004: ry->table_size = 0xB1C; break; + case 0x4C0005: ry->table_size = 0xAF8; break; + case 0x4C0006: ry->table_size = 0xAFC; break; + case 0x4C0008: ry->table_size = 0xAF0; break; + case 0x4C0007: + case 0x4C0009: ry->table_size = 0xB00; break; + case 0x5D0008: + case 0x5D0009: + case 0x5D000B: ry->table_size = 0xD54; break; + case 0x64020c: ry->table_size = 0xE50; break; + + // use a larger size then the largest known table to be able to test real table size of unknown tables + default: ry->table_size = 0x1000; break; + } + + if (resp != REP_MSG_OK) { + _return_translated_smu_error(resp); + } + if(!ry->table_ver){ + printf("request_table_ver_and_size did not return anything\n"); + return ADJ_ERR_SMU_UNSUPPORTED; + } + return 0; +} + +static int request_table_addr(ryzen_access ry) +{ + unsigned int get_table_addr_msg; + int resp; + smu_service_args_t args = {0, 0, 0, 0, 0, 0}; + switch (ry->family) + { + case FAM_RAVEN: + case FAM_PICASSO: + case FAM_DALI: + args.arg0 = 3; + get_table_addr_msg = 0xB; + break; + case FAM_RENOIR: + case FAM_LUCIENNE: + case FAM_CEZANNE: + case FAM_REMBRANDT: + case FAM_PHOENIX: + case FAM_HAWKPOINT: + case FAM_KRACKANPOINT: + case FAM_STRIXPOINT: + case FAM_STRIXHALO: + get_table_addr_msg = 0x66; + break; + default: + printf("request_table_addr is not supported on this family\n"); + return ADJ_ERR_FAM_UNSUPPORTED; + } + + resp = smu_service_req(ry->psmu, get_table_addr_msg, &args); + + switch (ry->family) + { + case FAM_REMBRANDT: + case FAM_PHOENIX: + case FAM_HAWKPOINT: + case FAM_KRACKANPOINT: + case FAM_STRIXPOINT: + case FAM_STRIXHALO: + ry->table_addr = (uint64_t) args.arg1 << 32 | args.arg0; + break; + default: + ry->table_addr = args.arg0; + } + + if(resp != REP_MSG_OK){ + _return_translated_smu_error(resp); + } + if(!ry->table_addr){ + printf("request_table_addr did not return anything\n"); + return ADJ_ERR_SMU_UNSUPPORTED; + } + return 0; +} + +static int request_transfer_table(ryzen_access ry) +{ + int resp; + unsigned int transfer_table_msg; + smu_service_args_t args = {0, 0, 0, 0, 0, 0}; + switch (ry->family) + { + case FAM_RAVEN: + case FAM_PICASSO: + case FAM_DALI: + args.arg0 = 3; + transfer_table_msg = 0x3D; + break; + case FAM_RENOIR: + case FAM_LUCIENNE: + case FAM_CEZANNE: + case FAM_REMBRANDT: + case FAM_PHOENIX: + case FAM_HAWKPOINT: + case FAM_KRACKANPOINT: + case FAM_STRIXPOINT: + case FAM_STRIXHALO: + transfer_table_msg = 0x65; + break; + default: + printf("request_transfer_table is not supported on this family\n"); + return ADJ_ERR_FAM_UNSUPPORTED; + } + + resp = smu_service_req(ry->psmu, transfer_table_msg, &args); + if (resp == REP_MSG_CmdRejectedPrereq) { + //2nd try is needed for 2 usecase: if SMU got interrupted or first call after boot on Zen2 + //we need to wait because if we don't wait 2nd call will fail, too: similar to Raven and Picasso issue but with real reject instead of 0 data response + //but because we don't have to check any physical memory values, don't waste CPU cycles and use sleep instead + Sleep(10); + resp = smu_service_req(ry->psmu, transfer_table_msg, &args); + if(resp == REP_MSG_CmdRejectedPrereq){ + printf("request_transfer_table was rejected twice\n"); + Sleep(100); + resp = smu_service_req(ry->psmu, transfer_table_msg, &args); + } + } + if(resp != REP_MSG_OK){ + _return_translated_smu_error(resp); + } + return 0; +} + +EXP int CALL init_table(ryzen_access ry) +{ + DBG("init_table\n"); + int errorcode = 0; + + errorcode = request_table_ver_and_size(ry); + if(errorcode){ + return errorcode; + } + + errorcode = request_table_addr(ry); + if(errorcode){ + return errorcode; + } + + //init memory object because it is prerequiremt to woring with physical memory address + if (init_mem_obj(ry->os_access, ry->table_addr) < 0) { + printf("Unable to get memory access\n"); + return ADJ_ERR_MEMORY_ACCESS; + } + + //when using the ryzen_smu kernel module, its reported PM table size is authoritative + //because it reads the real value from hardware; prefer it over the SMU-derived size, + //especially for unknown table versions that fall back to the 0x1000 sentinel +#ifndef _WIN32 + if (is_using_smu_driver() && + ry->os_access->access.kmod.pm_table_size != ry->table_size) { + DBG("PM table size: using kmod value (%zu) over SMU-derived value (%zu)\n", + ry->os_access->access.kmod.pm_table_size, ry->table_size); + ry->table_size = ry->os_access->access.kmod.pm_table_size; + } +#endif + + //hold copy of table value in memory for our single value getters + ry->table_values = calloc(ry->table_size / 4, 4); + + errorcode = refresh_table(ry); + if(errorcode) + { + return errorcode; + } + + if(!ry->table_values[0]){ + //Raven and Picasso don't get table refresh on the very first transfer call after boot, but respond with OK + //if we detact 0 data, do an initial 2nd call after a small delay + //transfer, transfer, wait, wait longer; don't work + //transfer, wait, wait longer; don't work + //transfer, wait, transfer; does work + DBG("empty table detected, try again\n"); + Sleep(10); + return refresh_table(ry); + } + + return 0; +} + +#define _lazy_init_table(RETURN_VAR) \ +do { \ + if(!ry->table_values) { \ + DBG("warning: %s was called before init_table\n", __func__); \ + errorcode = init_table(ry); \ + if(errorcode) return RETURN_VAR; \ + } \ +} while (0); + +EXP uint32_t CALL get_table_ver(ryzen_access ry) +{ + int errorcode; + _lazy_init_table(0); + + return ry->table_ver; +} + +EXP size_t CALL get_table_size(ryzen_access ry) +{ + int errorcode; + _lazy_init_table(0); + + return ry->table_size; +} + +EXP float* CALL get_table_values(ryzen_access ry) +{ + return ry->table_values; +} + +EXP int CALL refresh_table(ryzen_access ry) +{ + int errorcode = 0; + _lazy_init_table(errorcode); + + //only execute request table if we don't use SMU driver + if(!is_using_smu_driver()){ + //if other tools call tables transfer, we may already find new data inside the memory and can avoid calling transfer table twice + //avoiding transfer table twice is important because SMU tend to reject transfer table calls if you repeat them too fast + //transfer table rejection happens even if we did correctly wait for response register change + //if multiple tools retry transfer table in a loop, both will get rejections, avoid this issue by checking if we need to transfer table + //refresh table if this is the first call (table is empty) or if the first 6 table values in memory doesn't have new values (compare result = 0) + if(ry->table_values[0] == 0 || compare_pm_table(ry->table_values, 6 * 4) == 0){ + errorcode = request_transfer_table(ry); + } + } + + if(errorcode){ + return errorcode; + } + + if(copy_pm_table(ry->os_access, ry->table_values, ry->table_size)){ + printf("refresh_table failed\n"); + return ADJ_ERR_MEMORY_ACCESS; + } + + return 0; +} + +#define _do_adjust(OPT) \ +do { \ + smu_service_args_t args = {0, 0, 0, 0, 0, 0}; \ + int resp; \ + args.arg0 = value; \ + resp = smu_service_req(ry->mp1_smu, OPT, &args); \ + if (resp == REP_MSG_OK) { \ + err = 0; \ + } else if (resp == REP_MSG_UnknownCmd) { \ + err = ADJ_ERR_SMU_UNSUPPORTED; \ + } else { \ + err = ADJ_ERR_SMU_REJECTED; \ + } \ +} while (0); + + +#define _do_adjust_psmu(OPT) \ +do { \ + smu_service_args_t args = {0, 0, 0, 0, 0, 0}; \ + int resp; \ + args.arg0 = value; \ + resp = smu_service_req(ry->psmu, OPT, &args); \ + if (resp == REP_MSG_OK) { \ + err = 0; \ + } else if (resp == REP_MSG_UnknownCmd) { \ + err = ADJ_ERR_SMU_UNSUPPORTED; \ + } else { \ + err = ADJ_ERR_SMU_REJECTED; \ + } \ +} while (0); + +#define _read_float_value(OFFSET) \ +do { \ + if(!ry->table_values) \ + return NAN; \ + return ry->table_values[(OFFSET) / 4]; \ +} while (0); + + +EXP int CALL set_stapm_limit(ryzen_access ry, uint32_t value){ + int err = ADJ_ERR_FAM_UNSUPPORTED; + + /* \_SB.ALIB (0x0c, [size, 0x05, val]) */ + + switch (ry->family) + { + case FAM_RAVEN: + case FAM_PICASSO: + case FAM_DALI: + _do_adjust(0x1a); + break; + case FAM_RENOIR: + case FAM_LUCIENNE: + case FAM_CEZANNE: + case FAM_VANGOGH: + case FAM_REMBRANDT: + case FAM_MENDOCINO: + case FAM_PHOENIX: + case FAM_HAWKPOINT: + case FAM_KRACKANPOINT: + case FAM_STRIXPOINT: + case FAM_STRIXHALO: + _do_adjust(0x14); + if (err) { + printf("%s: Retry with PSMU\n", __func__); + _do_adjust_psmu(0x31); + } + break; + case FAM_DRAGONRANGE: + case FAM_FIRERANGE: + _do_adjust(0x4f); + break; + default: + break; + } + return err; +} + +EXP int CALL set_fast_limit(ryzen_access ry, uint32_t value){ + int err = ADJ_ERR_FAM_UNSUPPORTED; + + /* \_SB.ALIB (0x0c, [size, 0x06, val]) */ + switch (ry->family) + { + case FAM_RAVEN: + case FAM_PICASSO: + case FAM_DALI: + _do_adjust(0x1b); + break; + case FAM_RENOIR: + case FAM_LUCIENNE: + case FAM_CEZANNE: + case FAM_VANGOGH: + case FAM_REMBRANDT: + case FAM_MENDOCINO: + case FAM_PHOENIX: + case FAM_HAWKPOINT: + case FAM_KRACKANPOINT: + case FAM_STRIXPOINT: + case FAM_STRIXHALO: + _do_adjust(0x15); + break; + case FAM_DRAGONRANGE: + case FAM_FIRERANGE: + _do_adjust(0x3e); + break; + default: + break; + } + return err; +} + +EXP int CALL set_slow_limit(ryzen_access ry, uint32_t value){ + int err = ADJ_ERR_FAM_UNSUPPORTED; + + /* \_SB.ALIB (0x0c, [size, 0x07, val]) */ + + switch (ry->family) + { + case FAM_RAVEN: + case FAM_PICASSO: + case FAM_DALI: + _do_adjust(0x1c); + break; + case FAM_RENOIR: + case FAM_LUCIENNE: + case FAM_CEZANNE: + case FAM_VANGOGH: + case FAM_REMBRANDT: + case FAM_MENDOCINO: + case FAM_PHOENIX: + case FAM_HAWKPOINT: + case FAM_KRACKANPOINT: + case FAM_STRIXPOINT: + case FAM_STRIXHALO: + _do_adjust(0x16); + break; + case FAM_DRAGONRANGE: + case FAM_FIRERANGE: + _do_adjust(0x5f); + break; + default: + break; + } + return err; +} + +EXP int CALL set_slow_time(ryzen_access ry, uint32_t value){ + int err = ADJ_ERR_FAM_UNSUPPORTED; + + /* \_SB.ALIB (0x0c, [size, 0x08, val]) */ + + switch (ry->family) + { + case FAM_RAVEN: + case FAM_PICASSO: + case FAM_DALI: + _do_adjust(0x1d); + break; + case FAM_RENOIR: + case FAM_LUCIENNE: + case FAM_CEZANNE: + case FAM_VANGOGH: + case FAM_REMBRANDT: + case FAM_MENDOCINO: + case FAM_PHOENIX: + case FAM_HAWKPOINT: + case FAM_KRACKANPOINT: + case FAM_STRIXPOINT: + case FAM_STRIXHALO: + _do_adjust(0x17); + break; + case FAM_DRAGONRANGE: + case FAM_FIRERANGE: + _do_adjust(0x60); + break; + default: + break; + } + return err; +} + +EXP int CALL set_stapm_time(ryzen_access ry, uint32_t value){ + int err = ADJ_ERR_FAM_UNSUPPORTED; + + /* \_SB.ALIB (0x0c, [size, 0x01, val]) */ + + switch (ry->family) + { + case FAM_RAVEN: + case FAM_PICASSO: + case FAM_DALI: + _do_adjust(0x1e); + break; + case FAM_RENOIR: + case FAM_LUCIENNE: + case FAM_CEZANNE: + case FAM_VANGOGH: + case FAM_REMBRANDT: + case FAM_MENDOCINO: + case FAM_PHOENIX: + case FAM_HAWKPOINT: + case FAM_KRACKANPOINT: + case FAM_STRIXPOINT: + case FAM_STRIXHALO: + _do_adjust(0x18); + case FAM_DRAGONRANGE: + case FAM_FIRERANGE: + _do_adjust(0x4e); + default: + break; + } + return err; +} + +EXP int CALL set_tctl_temp(ryzen_access ry, uint32_t value){ + int err = ADJ_ERR_FAM_UNSUPPORTED; + + /* \_SB.ALIB (0x0c, [size, 0x03, val]) */ + + switch (ry->family) + { + case FAM_RAVEN: + case FAM_PICASSO: + case FAM_DALI: + _do_adjust(0x1f); + break; + case FAM_RENOIR: + case FAM_LUCIENNE: + case FAM_CEZANNE: + case FAM_VANGOGH: + case FAM_REMBRANDT: + case FAM_MENDOCINO: + case FAM_PHOENIX: + case FAM_HAWKPOINT: + case FAM_KRACKANPOINT: + case FAM_STRIXPOINT: + case FAM_STRIXHALO: + _do_adjust(0x19); + break; + case FAM_DRAGONRANGE: + case FAM_FIRERANGE: + _do_adjust(0x3f); + break; + default: + break; + } + return err; +} + +EXP int CALL set_vrm_current(ryzen_access ry, uint32_t value){ + int err = ADJ_ERR_FAM_UNSUPPORTED; + + /* \_SB.ALIB (0x0c, [size, 0x0b, val]) */ + + switch (ry->family) + { + case FAM_RAVEN: + case FAM_PICASSO: + case FAM_DALI: + _do_adjust(0x20); + break; + case FAM_RENOIR: + case FAM_LUCIENNE: + case FAM_CEZANNE: + case FAM_VANGOGH: + case FAM_REMBRANDT: + case FAM_MENDOCINO: + case FAM_PHOENIX: + case FAM_HAWKPOINT: + case FAM_KRACKANPOINT: + case FAM_STRIXPOINT: + case FAM_STRIXHALO: + _do_adjust(0x1a); + break; + case FAM_DRAGONRANGE: + case FAM_FIRERANGE: + _do_adjust(0x3c); + break; + default: + break; + } + return err; +} + +EXP int CALL set_vrmsoc_current(ryzen_access ry, uint32_t value){ + int err = ADJ_ERR_FAM_UNSUPPORTED; + + /* \_SB.ALIB (0x0c, [size, 0x0e, val]) */ + + switch (ry->family) + { + case FAM_RAVEN: + case FAM_PICASSO: + case FAM_DALI: + _do_adjust(0x21); + break; + case FAM_RENOIR: + case FAM_LUCIENNE: + case FAM_CEZANNE: + case FAM_VANGOGH: + case FAM_REMBRANDT: + case FAM_MENDOCINO: + case FAM_PHOENIX: + case FAM_HAWKPOINT: + case FAM_KRACKANPOINT: + case FAM_STRIXPOINT: + case FAM_STRIXHALO: + _do_adjust(0x1b); + default: + break; + } + return err; +} + +EXP int CALL set_vrmgfx_current(ryzen_access ry, uint32_t value){ + int err = ADJ_ERR_FAM_UNSUPPORTED; + + switch (ry->family) + { + case FAM_VANGOGH: + _do_adjust(0x1c); + default: + break; + } + return err; +} + +EXP int CALL set_vrmcvip_current(ryzen_access ry, uint32_t value){ + int err = ADJ_ERR_FAM_UNSUPPORTED; + + switch (ry->family) + { + case FAM_VANGOGH: + _do_adjust(0x1d); + default: + break; + } + return err; +} + +EXP int CALL set_vrmmax_current(ryzen_access ry, uint32_t value){ + int err = ADJ_ERR_FAM_UNSUPPORTED; + + /* \_SB.ALIB (0x0c, [size, 0x0c, val]) */ + + switch (ry->family) + { + case FAM_RAVEN: + case FAM_PICASSO: + case FAM_DALI: + _do_adjust(0x22); + break; + case FAM_RENOIR: + case FAM_LUCIENNE: + case FAM_CEZANNE: + case FAM_REMBRANDT: + case FAM_MENDOCINO: + case FAM_PHOENIX: + case FAM_HAWKPOINT: + case FAM_KRACKANPOINT: + case FAM_STRIXPOINT: + case FAM_STRIXHALO: + _do_adjust(0x1c); + break; + case FAM_VANGOGH: + _do_adjust(0x1e); + default: + break; + } + return err; +} + +EXP int CALL set_vrmgfxmax_current(ryzen_access ry, uint32_t value){ + int err = ADJ_ERR_FAM_UNSUPPORTED; + + switch (ry->family) + { + case FAM_VANGOGH: + _do_adjust(0x1f); + default: + break; + } + return err; +} + +EXP int CALL set_vrmsocmax_current(ryzen_access ry, uint32_t value){ + int err = ADJ_ERR_FAM_UNSUPPORTED; + + /* \_SB.ALIB (0x0c, [size, 0x11, val]) */ + + switch (ry->family) + { + case FAM_RAVEN: + case FAM_PICASSO: + case FAM_DALI: + _do_adjust(0x23); + break; + case FAM_RENOIR: + case FAM_LUCIENNE: + case FAM_CEZANNE: + case FAM_REMBRANDT: + case FAM_MENDOCINO: + case FAM_PHOENIX: + case FAM_HAWKPOINT: + case FAM_KRACKANPOINT: + case FAM_STRIXPOINT: + case FAM_STRIXHALO: + _do_adjust(0x1d); + default: + break; + } + return err; +} + +EXP int CALL set_psi0_current(ryzen_access ry, uint32_t value){ + int err = ADJ_ERR_FAM_UNSUPPORTED; + + switch (ry->family) + { + case FAM_RAVEN: + case FAM_PICASSO: + case FAM_DALI: + _do_adjust(0x24); + break; + case FAM_RENOIR: + case FAM_LUCIENNE: + case FAM_CEZANNE: + _do_adjust(0x1e); + default: + break; + } + return err; +} + +EXP int CALL set_psi3cpu_current(ryzen_access ry, uint32_t value){ + int err = ADJ_ERR_FAM_UNSUPPORTED; + + switch (ry->family) + { + case FAM_VANGOGH: + _do_adjust(0x20); + default: + break; + } + return err; +} + +EXP int CALL set_psi0soc_current(ryzen_access ry, uint32_t value){ + int err = ADJ_ERR_FAM_UNSUPPORTED; + + switch (ry->family) + { + case FAM_RAVEN: + case FAM_PICASSO: + case FAM_DALI: + _do_adjust(0x25); + break; + case FAM_RENOIR: + case FAM_LUCIENNE: + case FAM_CEZANNE: + _do_adjust(0x1f); + default: + break; + } + return err; +} + +EXP int CALL set_psi3gfx_current(ryzen_access ry, uint32_t value){ + int err = ADJ_ERR_FAM_UNSUPPORTED; + + switch (ry->family) + { + case FAM_VANGOGH: + _do_adjust(0x21); + default: + break; + } + return err; +} + +EXP int CALL set_max_gfxclk_freq(ryzen_access ry, uint32_t value) { + int err = ADJ_ERR_FAM_UNSUPPORTED; + + switch (ry->family) + { + case FAM_RAVEN: + case FAM_PICASSO: + case FAM_DALI: + case FAM_LUCIENNE: + _do_adjust(0x46); + break; + default: + break; + } + return err; +} + +EXP int CALL set_min_gfxclk_freq(ryzen_access ry, uint32_t value) { + int err = ADJ_ERR_FAM_UNSUPPORTED; + + switch (ry->family) + { + case FAM_RAVEN: + case FAM_PICASSO: + case FAM_DALI: + case FAM_LUCIENNE: + _do_adjust(0x47); + break; + default: + break; + } + return err; +} + +EXP int CALL set_max_socclk_freq(ryzen_access ry, uint32_t value){ + int err = ADJ_ERR_FAM_UNSUPPORTED; + + switch (ry->family) + { + case FAM_RAVEN: + case FAM_PICASSO: + case FAM_DALI: + _do_adjust(0x48); + break; + default: + break; + } + return err; +} + +EXP int CALL set_min_socclk_freq(ryzen_access ry, uint32_t value){ + int err = ADJ_ERR_FAM_UNSUPPORTED; + + switch (ry->family) + { + case FAM_RAVEN: + case FAM_PICASSO: + case FAM_DALI: + _do_adjust(0x49); + break; + default: + break; + } + return err; +} + +EXP int CALL set_max_fclk_freq(ryzen_access ry, uint32_t value){ + int err = ADJ_ERR_FAM_UNSUPPORTED; + + switch (ry->family) + { + case FAM_RAVEN: + case FAM_PICASSO: + case FAM_DALI: + _do_adjust(0x4A); + break; + default: + break; + } + return err; +} + +EXP int CALL set_min_fclk_freq(ryzen_access ry, uint32_t value){ + int err = ADJ_ERR_FAM_UNSUPPORTED; + + switch (ry->family) + { + case FAM_RAVEN: + case FAM_PICASSO: + case FAM_DALI: + _do_adjust(0x4B); + break; + default: + break; + } + return err; +} + +EXP int CALL set_max_vcn(ryzen_access ry, uint32_t value){ + int err = ADJ_ERR_FAM_UNSUPPORTED; + + switch (ry->family) + { + case FAM_RAVEN: + case FAM_PICASSO: + case FAM_DALI: + _do_adjust(0x4C); + break; + default: + break; + } + return err; +} + +EXP int CALL set_min_vcn(ryzen_access ry, uint32_t value){ + int err = ADJ_ERR_FAM_UNSUPPORTED; + + switch (ry->family) + { + case FAM_RAVEN: + case FAM_PICASSO: + case FAM_DALI: + _do_adjust(0x4D); + break; + default: + break; + } + return err; +} + +EXP int CALL set_max_lclk(ryzen_access ry, uint32_t value){ + int err = ADJ_ERR_FAM_UNSUPPORTED; + + switch (ry->family) + { + case FAM_RAVEN: + case FAM_PICASSO: + case FAM_DALI: + _do_adjust(0x4E); + break; + default: + break; + } + return err; +} + +EXP int CALL set_min_lclk(ryzen_access ry, uint32_t value){ + int err = ADJ_ERR_FAM_UNSUPPORTED; + + switch (ry->family) + { + case FAM_RAVEN: + case FAM_PICASSO: + case FAM_DALI: + _do_adjust(0x4F); + break; + default: + break; + } + return err; +} + +EXP int CALL set_prochot_deassertion_ramp(ryzen_access ry, uint32_t value) { + int err = ADJ_ERR_FAM_UNSUPPORTED; + + /* \_SB.ALIB (0x0c, [size, 0x09, val]) */ + + switch (ry->family) + { + case FAM_RAVEN: + case FAM_PICASSO: + case FAM_DALI: + _do_adjust(0x26); + break; + case FAM_RENOIR: + case FAM_LUCIENNE: + case FAM_CEZANNE: + _do_adjust(0x20); + break; + case FAM_VANGOGH: + _do_adjust(0x22); + break; + case FAM_REMBRANDT: + case FAM_MENDOCINO: + case FAM_PHOENIX: + case FAM_HAWKPOINT: + case FAM_KRACKANPOINT: + case FAM_STRIXPOINT: + case FAM_STRIXHALO: + _do_adjust(0x1f); + default: + break; + } + return err; +} + +EXP int CALL set_apu_skin_temp_limit(ryzen_access ry, uint32_t value) { + int err = ADJ_ERR_FAM_UNSUPPORTED; + + /* \_SB.ALIB (0x0c, [size, 0x22, val]) */ + + value *= 256; + switch (ry->family) + { + case FAM_RENOIR: + case FAM_LUCIENNE: + case FAM_CEZANNE: + _do_adjust(0x38); + break; + case FAM_VANGOGH: + case FAM_REMBRANDT: + case FAM_MENDOCINO: + case FAM_PHOENIX: + case FAM_HAWKPOINT: + _do_adjust(0x33); + break; + case FAM_STRIXHALO: + // not implemented on StrixHalo, seems to be controlled only via tctl-temp + default: + break; + } + return err; +} + +EXP int CALL set_dgpu_skin_temp_limit(ryzen_access ry, uint32_t value) { + int err = ADJ_ERR_FAM_UNSUPPORTED; + + /* \_SB.ALIB (0x0c, [size, 0x23, val]) */ + + value *= 256; + switch (ry->family) + { + case FAM_RENOIR: + case FAM_LUCIENNE: + case FAM_CEZANNE: + _do_adjust(0x39); + break; + case FAM_VANGOGH: + case FAM_REMBRANDT: + case FAM_MENDOCINO: + case FAM_PHOENIX: + case FAM_HAWKPOINT: + case FAM_STRIXPOINT: + _do_adjust(0x34); + break; + case FAM_STRIXHALO: + // not implemented on StrixHalo, seems to be controlled only via tctl-temp + default: + break; + } + return err; +} + +EXP int CALL set_apu_slow_limit(ryzen_access ry, uint32_t value) { + int err = ADJ_ERR_FAM_UNSUPPORTED; + + /* \_SB.ALIB (0x0c, [size, 0x13, val]) */ + + switch (ry->family) + { + case FAM_RENOIR: + case FAM_LUCIENNE: + case FAM_CEZANNE: + _do_adjust(0x21); + break; + case FAM_REMBRANDT: + case FAM_PHOENIX: + case FAM_HAWKPOINT: + case FAM_KRACKANPOINT: + case FAM_STRIXPOINT: + case FAM_STRIXHALO: + _do_adjust(0x23); + break; + default: + break; + } + return err; +} + +EXP int CALL set_skin_temp_power_limit(ryzen_access ry, uint32_t value) { + int err = ADJ_ERR_FAM_UNSUPPORTED; + + /* \_SB.ALIB (0x0c, [size, 0x2e, val]) */ + + switch (ry->family) + { + case FAM_RENOIR: + case FAM_LUCIENNE: + case FAM_CEZANNE: + _do_adjust(0x53); + break; + case FAM_VANGOGH: + case FAM_REMBRANDT: + case FAM_MENDOCINO: + case FAM_PHOENIX: + case FAM_HAWKPOINT: + case FAM_KRACKANPOINT: + case FAM_STRIXPOINT: + case FAM_STRIXHALO: + _do_adjust(0x4a); + break; + default: + break; + } + return err; +} + +EXP int CALL set_gfx_clk(ryzen_access ry, uint32_t value) { + int err = ADJ_ERR_FAM_UNSUPPORTED; + + switch (ry->family) + { + case FAM_RENOIR: + case FAM_LUCIENNE: + case FAM_CEZANNE: + case FAM_VANGOGH: + case FAM_REMBRANDT: + case FAM_MENDOCINO: + case FAM_PHOENIX: + case FAM_HAWKPOINT: + case FAM_KRACKANPOINT: /* Added to debug on KRK, STX, & STXH */ + case FAM_STRIXPOINT: + case FAM_STRIXHALO: + _do_adjust_psmu(0x89); + break; + default: + break; + } + return err; +} + +EXP int CALL set_power_saving(ryzen_access ry) { + int err = ADJ_ERR_FAM_UNSUPPORTED; + uint32_t value = 0; + + /* \_SB.ALIB (0x01, [size, 0x1]) */ + + switch (ry->family) + { + case FAM_RAVEN: + case FAM_PICASSO: + case FAM_DALI: + _do_adjust(0x19); + break; + case FAM_RENOIR: + case FAM_LUCIENNE: + case FAM_CEZANNE: + case FAM_VANGOGH: + case FAM_REMBRANDT: + case FAM_MENDOCINO: + case FAM_PHOENIX: + case FAM_HAWKPOINT: + case FAM_KRACKANPOINT: + case FAM_STRIXPOINT: + case FAM_STRIXHALO: + _do_adjust(0x12); + break; + default: + break; + } + return err; +} + +EXP int CALL set_max_performance(ryzen_access ry) { + int err = ADJ_ERR_FAM_UNSUPPORTED; + uint32_t value = 0; + + /* \_SB.ALIB (0x01, [size, 0x0]) */ + + switch (ry->family) + { + case FAM_RAVEN: + case FAM_PICASSO: + case FAM_DALI: + _do_adjust(0x18); + break; + case FAM_RENOIR: + case FAM_LUCIENNE: + case FAM_CEZANNE: + case FAM_VANGOGH: + case FAM_REMBRANDT: + case FAM_MENDOCINO: + case FAM_PHOENIX: + case FAM_HAWKPOINT: + case FAM_KRACKANPOINT: + case FAM_STRIXPOINT: + case FAM_STRIXHALO: + _do_adjust(0x11); + break; + default: + break; + } + return err; +} + +EXP int CALL set_oc_clk(ryzen_access ry, uint32_t value) { + int err = ADJ_ERR_FAM_UNSUPPORTED; + + switch (ry->family) + { + case FAM_LUCIENNE: + case FAM_RENOIR: + case FAM_CEZANNE: + case FAM_REMBRANDT: + _do_adjust(0x31); + if (err) { + printf("%s: Retry with PSMU\n", __func__); + _do_adjust_psmu(0x19); + } + break; + default: + break; + } + return err; +} + +EXP int CALL set_per_core_oc_clk(ryzen_access ry, uint32_t value) { + int err = ADJ_ERR_FAM_UNSUPPORTED; + + switch (ry->family) + { + case FAM_LUCIENNE: + case FAM_RENOIR: + case FAM_CEZANNE: + case FAM_REMBRANDT: + _do_adjust(0x32); + if (err) { + printf("%s: Retry with PSMU\n", __func__); + _do_adjust_psmu(0x1a); + } + break; + default: + break; + } + return err; +} + +EXP int CALL set_oc_volt(ryzen_access ry, uint32_t value) { + int err = ADJ_ERR_FAM_UNSUPPORTED; + + switch (ry->family) + { + case FAM_LUCIENNE: + case FAM_RENOIR: + case FAM_CEZANNE: + _do_adjust(0x33); + if (err) { + printf("%s: Retry with PSMU\n", __func__); + _do_adjust_psmu(0x1b); + } + break; + default: + break; + } + return err; +} + +EXP int CALL set_disable_oc(ryzen_access ry) { + int err = ADJ_ERR_FAM_UNSUPPORTED; + uint32_t value = 0x0; + + switch (ry->family) + { + case FAM_LUCIENNE: + case FAM_RENOIR: + case FAM_CEZANNE: + _do_adjust(0x30); + if (err) { + printf("%s: Retry with PSMU\n", __func__); + _do_adjust_psmu(0x1d); + } + break; + case FAM_REMBRANDT: + _do_adjust_psmu(0x18); + break; + default: + break; + } + return err; +} + +EXP int CALL set_enable_oc(ryzen_access ry) { + int err = ADJ_ERR_FAM_UNSUPPORTED; + uint32_t value = 0x0; + + switch (ry->family) + { + case FAM_LUCIENNE: + case FAM_RENOIR: + case FAM_CEZANNE: + _do_adjust(0x2F); + break; + case FAM_REMBRANDT: + _do_adjust_psmu(0x17); + break; + default: + break; + } + return err; +} + +EXP int CALL set_coall(ryzen_access ry, uint32_t value) { + int err = ADJ_ERR_FAM_UNSUPPORTED; + + switch (ry->family) + { + case FAM_CEZANNE: + case FAM_RENOIR: + case FAM_LUCIENNE: + _do_adjust(0x55); + break; + case FAM_REMBRANDT: + case FAM_VANGOGH: + case FAM_PHOENIX: + case FAM_HAWKPOINT: + case FAM_KRACKANPOINT: + case FAM_STRIXPOINT: + case FAM_STRIXHALO: + _do_adjust(0x4C); + break; + case FAM_DRAGONRANGE: + case FAM_FIRERANGE: + _do_adjust_psmu(0x7); + break; + default: + break; + } + return err; +} + +EXP int CALL set_coper(ryzen_access ry, uint32_t value) { + int err = ADJ_ERR_FAM_UNSUPPORTED; + + switch (ry->family) + { + case FAM_CEZANNE: + case FAM_RENOIR: + case FAM_LUCIENNE: + _do_adjust(0x54); + break; + case FAM_REMBRANDT: + case FAM_PHOENIX: + case FAM_VANGOGH: + case FAM_HAWKPOINT: + case FAM_KRACKANPOINT: + case FAM_STRIXPOINT: + case FAM_STRIXHALO: + _do_adjust(0x4b); + break; + case FAM_DRAGONRANGE: + case FAM_FIRERANGE: + _do_adjust_psmu(0x6); + break; + default: + break; + } + return err; +} + +EXP int CALL set_cogfx(ryzen_access ry, uint32_t value) { + int err = ADJ_ERR_FAM_UNSUPPORTED; + + switch (ry->family) + { + case FAM_CEZANNE: + case FAM_RENOIR: + case FAM_LUCIENNE: + _do_adjust(0x64); + break; + case FAM_REMBRANDT: + case FAM_PHOENIX: + case FAM_HAWKPOINT: + case FAM_VANGOGH: + _do_adjust_psmu(0xB7); + break; + case FAM_STRIXHALO: + // 0xB7 is rejected on this architecture + default: + break; + } + return err; +} + +//PM Table section, offset of first lines are stable across multiple PM Table versions +EXP float CALL get_stapm_limit(ryzen_access ry){_read_float_value(0x0);} +EXP float CALL get_stapm_value(ryzen_access ry){_read_float_value(0x4);} +EXP float CALL get_fast_limit(ryzen_access ry){_read_float_value(0x8);} +EXP float CALL get_fast_value(ryzen_access ry){_read_float_value(0xC);} +EXP float CALL get_slow_limit(ryzen_access ry){_read_float_value(0x10);} +EXP float CALL get_slow_value(ryzen_access ry){_read_float_value(0x14);} + +//custom section, offsets are depending on table version +EXP float CALL get_apu_slow_limit(ryzen_access ry) { + switch (ry->table_ver) + { + case 0x00370000: + case 0x00370001: + case 0x00370002: + case 0x00370003: + case 0x00370004: + case 0x00370005: + case 0x003F0000: + case 0x00400001: + case 0x00400002: + case 0x00400003: + case 0x00400004: + case 0x00400005: + case 0x00450004: + case 0x00450005: + case 0x004C0006: + case 0x004C0007: + case 0x004C0008: + case 0x004C0009: + case 0x005D0008: // Strix Point - looks correct from dumping table, defaults to 45W + case 0x005D0009: + case 0x005D000B: + case 0x0064020c: // StrixHalo - looks correct from dumping table, defaults to 70W + case 0x00650005: // Krackan Point + _read_float_value(0x18); + default: + break; + } + return NAN; +} +EXP float CALL get_apu_slow_value(ryzen_access ry) { + switch (ry->table_ver) + { + case 0x00370000: + case 0x00370001: + case 0x00370002: + case 0x00370003: + case 0x00370004: + case 0x00370005: + case 0x003F0000: + case 0x00400001: + case 0x00400002: + case 0x00400003: + case 0x00400004: + case 0x00400005: + case 0x00450004: + case 0x00450005: + case 0x004C0006: + case 0x004C0009: + case 0x005D0008: // Strix Point - untested, always 0? + case 0x005D0009: + case 0x005D000B: + case 0x0064020c: // StrixHalo - untested! + case 0x00650005: // Krackan Point + _read_float_value(0x1C); + default: + break; + } + return NAN; +} +EXP float CALL get_vrm_current(ryzen_access ry) { + switch (ry->table_ver) + { + case 0x001E0001: + case 0x001E0002: + case 0x001E0003: + case 0x001E0004: + case 0x001E0005: + case 0x001E000A: + case 0x001E0101: + _read_float_value(0x18); + case 0x00370000: + case 0x00370001: + case 0x00370002: + case 0x00370003: + case 0x00370004: + case 0x00370005: + case 0x00400001: + case 0x00400002: + case 0x00400003: + case 0x00400004: + case 0x00400005: + case 0x00450004: + case 0x00450005: + case 0x004C0006: + case 0x004C0007: + case 0x004C0008: + case 0x004C0009: + _read_float_value(0x20); + case 0x005D0008: // Strix Point - tested, defaults to 70, max 70 + case 0x005D0009: + case 0x005D000B: + case 0x00650005: // Krackan Point + _read_float_value(0x30); + default: + break; + } + return NAN; +} +EXP float CALL get_vrm_current_value(ryzen_access ry) { + switch (ry->table_ver) + { + case 0x001E0001: + case 0x001E0002: + case 0x001E0003: + case 0x001E0004: + case 0x001E0005: + case 0x001E000A: + case 0x001E0101: + _read_float_value(0x1C); + case 0x00370000: + case 0x00370001: + case 0x00370002: + case 0x00370003: + case 0x00370004: + case 0x00370005: + case 0x00400001: + case 0x00400002: + case 0x00400003: + case 0x00400004: + case 0x00400005: + case 0x00450004: + case 0x00450005: + case 0x004C0006: + case 0x004C0007: + case 0x004C0008: + case 0x004C0009: + _read_float_value(0x24); + case 0x005D0008: // Strix Point - looks correct from dumping table + case 0x005D0009: + case 0x005D000B: + case 0x00650005: // Krackan Point + _read_float_value(0x34); + default: + break; + } + return NAN; +} +EXP float CALL get_vrmsoc_current(ryzen_access ry) { + switch (ry->table_ver) + { + case 0x001E0001: + case 0x001E0002: + case 0x001E0003: + case 0x001E0004: + case 0x001E0005: + case 0x001E000A: + case 0x001E0101: + _read_float_value(0x20); + case 0x00370000: + case 0x00370001: + case 0x00370002: + case 0x00370003: + case 0x00370004: + case 0x00370005: + case 0x00400001: + case 0x00400002: + case 0x00400003: + case 0x00400004: + case 0x00400005: + case 0x00450004: + case 0x00450005: + case 0x004C0006: + case 0x004C0007: + case 0x004C0008: + case 0x004C0009: + _read_float_value(0x28); + case 0x005D0008: // Strix Point - tested, defaults to 30, max 30 + case 0x005D0009: + case 0x005D000B: + case 0x00650005: // Krackan Point + _read_float_value(0x38); + default: + break; + } + return NAN; +} +EXP float CALL get_vrmsoc_current_value(ryzen_access ry) { + switch (ry->table_ver) + { + case 0x001E0001: + case 0x001E0002: + case 0x001E0003: + case 0x001E0004: + case 0x001E0005: + case 0x001E000A: + case 0x001E0101: + _read_float_value(0x24); + case 0x00370000: + case 0x00370001: + case 0x00370002: + case 0x00370003: + case 0x00370004: + case 0x00370005: + case 0x00400001: + case 0x00400002: + case 0x00400003: + case 0x00400004: + case 0x00400005: + case 0x00450004: + case 0x00450005: + case 0x004C0006: + case 0x004C0007: + case 0x004C0008: + case 0x004C0009: + _read_float_value(0x2C); + case 0x005D0008: // Strix Point - looks correct from dumping table + case 0x005D0009: + case 0x005D000B: + case 0x00650005: // Krackan Point + _read_float_value(0x3C); + default: + break; + } + return NAN; +} +EXP float CALL get_vrmmax_current(ryzen_access ry) { + switch (ry->table_ver) + { + case 0x001E0001: + case 0x001E0002: + case 0x001E0003: + case 0x001E0004: + case 0x001E0005: + case 0x001E000A: + case 0x001E0101: + _read_float_value(0x28); + case 0x00370000: + case 0x00370001: + case 0x00370002: + case 0x00370003: + case 0x00370004: + case 0x00370005: + case 0x00400001: + case 0x00400002: + case 0x00400003: + case 0x00400004: + case 0x00400005: + case 0x00450004: + case 0x00450005: + case 0x004C0006: + case 0x004C0007: + case 0x004C0008: + case 0x004C0009: + // Strix Point - this is vrm_current; 0x1C-0x2C are all 0 + // Same for Krackan Point + _read_float_value(0x30); + case 0x005D0008: + case 0x005D0009: + case 0x005D000B: + _read_float_value(0x30); + default: + break; + } + return NAN; +} +EXP float CALL get_vrmmax_current_value(ryzen_access ry) { + switch (ry->table_ver) + { + case 0x001E0001: + case 0x001E0002: + case 0x001E0003: + case 0x001E0004: + case 0x001E0005: + case 0x001E000A: + case 0x001E0101: + _read_float_value(0x2C); + case 0x00370000: + case 0x00370001: + case 0x00370002: + case 0x00370003: + case 0x00370004: + case 0x00370005: + case 0x00400001: + case 0x00400002: + case 0x00400003: + case 0x00400004: + case 0x00400005: + case 0x00450004: + case 0x00450005: + case 0x004C0006: + case 0x004C0007: + case 0x004C0008: + case 0x004C0009: + _read_float_value(0x34); + case 0x005D0008: + case 0x005D0009: + case 0x005D000B: + _read_float_value(0x34); + default: + break; + } + return NAN; +} +EXP float CALL get_vrmsocmax_current(ryzen_access ry) { + switch (ry->table_ver) + { + case 0x001E0001: + case 0x001E0002: + case 0x001E0003: + case 0x001E0004: + case 0x001E0005: + case 0x001E000A: + case 0x001E0101: + _read_float_value(0x34); + case 0x00370000: + case 0x00370001: + case 0x00370002: + case 0x00370003: + case 0x00370004: + case 0x00370005: + case 0x00400001: + case 0x00400002: + case 0x00400003: + case 0x00400004: + case 0x00400005: + case 0x00450004: + case 0x00450005: + case 0x004C0006: + case 0x004C0007: + case 0x004C0008: + case 0x004C0009: + // Strix Point - this is vrmsoc_current; 0x1C-0x2C are all 0 + // Same for Krackan Point + _read_float_value(0x38); + case 0x005D0008: + case 0x005D0009: + case 0x005D000B: + _read_float_value(0x38); + default: + break; + } + return NAN; +} +EXP float CALL get_vrmsocmax_current_value(ryzen_access ry) { + switch (ry->table_ver) + { + case 0x001E0001: + case 0x001E0002: + case 0x001E0003: + case 0x001E0004: + case 0x001E0005: + case 0x001E000A: + case 0x001E0101: + _read_float_value(0x38); + case 0x00370000: + case 0x00370001: + case 0x00370002: + case 0x00370003: + case 0x00370004: + case 0x00370005: + case 0x00400001: + case 0x00400002: + case 0x00400003: + case 0x00400004: + case 0x00400005: + case 0x00450004: + case 0x00450005: + case 0x004C0006: + case 0x004C0007: + case 0x004C0008: + case 0x004C0009: + _read_float_value(0x3C); + case 0x005D0008: + case 0x005D0009: + case 0x005D000B: + _read_float_value(0x3C); + default: + break; + } + return NAN; +} +EXP float CALL get_tctl_temp(ryzen_access ry) { + switch (ry->table_ver) + { + case 0x001E0001: + case 0x001E0002: + case 0x001E0003: + case 0x001E0004: + case 0x001E0005: + case 0x001E000A: + case 0x001E0101: + case 0x0064020c: // StrixHalo tested + _read_float_value(0x58); //use core1 because core0 is not reported on dual core cpus + case 0x00370000: + case 0x00370001: + case 0x00370002: + case 0x00370003: + case 0x00370004: + case 0x00370005: + case 0x003F0000: + case 0x00400001: + case 0x00400002: + case 0x00400003: + case 0x00400004: + case 0x00400005: + case 0x00450004: + case 0x00450005: + case 0x004C0006: + case 0x004C0007: + case 0x004C0008: + case 0x004C0009: + case 0x005D0008: // Strix Point - untested, defaults to 100 + case 0x005D0009: + case 0x005D000B: + case 0x00650005: // Krackan Point + _read_float_value(0x40); + default: + break; + } + return NAN; +} +EXP float CALL get_tctl_temp_value(ryzen_access ry) { + switch (ry->table_ver) + { + case 0x001E0001: + case 0x001E0002: + case 0x001E0003: + case 0x001E0004: + case 0x001E0005: + case 0x001E000A: + case 0x001E0101: + case 0x0064020c: // StrixHalo tested + _read_float_value(0x5C); //use core1 because core0 is not reported on dual core cpus + case 0x00370000: + case 0x00370001: + case 0x00370002: + case 0x00370003: + case 0x00370004: + case 0x00370005: + case 0x003F0000: + case 0x00400001: + case 0x00400002: + case 0x00400003: + case 0x00400004: + case 0x00400005: + case 0x00450004: + case 0x00450005: + case 0x004C0006: + case 0x004C0007: + case 0x004C0008: + case 0x004C0009: + /* + * Strix Point - tested + * 0x44: Zen5 clst, taskset + stress-ng + * 0x4C: Zen5c clst, ditto + * 0x54: gfx, llama-bench/FurMark, gpu_metrics_v3_0 + * 0x5C: soc, all of above + memtester + * Corresponding limits default to 100 (untested) + */ + case 0x005D0008: + case 0x005D0009: + case 0x005D000B: + case 0x00650005: // Krackan Point + _read_float_value(0x44); + default: + break; + } + return NAN; +} +EXP float CALL get_apu_skin_temp_limit(ryzen_access ry) { + switch (ry->table_ver) + { + case 0x00370000: + case 0x00370001: + case 0x00370002: + case 0x00370003: + case 0x00370004: + case 0x00370005: + case 0x003F0000: + case 0x00400001: + case 0x00400002: + case 0x00400003: + case 0x00400004: + case 0x00400005: + case 0x00450004: + case 0x00450005: + case 0x004C0006: + case 0x004C0007: + case 0x004C0008: + case 0x004C0009: + case 0x005D0008: // Strix Point - untested + case 0x005D0009: + case 0x005D000B: + case 0x0064020c: // StrixHalo tested + _read_float_value(0x58); + break; + default: + break; + } + return NAN; +} +EXP float CALL get_apu_skin_temp_value(ryzen_access ry) { + switch (ry->table_ver) + { + case 0x00370000: + case 0x00370001: + case 0x00370002: + case 0x00370003: + case 0x00370004: + case 0x00370005: + case 0x003F0000: + case 0x00400001: + case 0x00400002: + case 0x00400003: + case 0x00400004: + case 0x00400005: + case 0x00450004: + case 0x00450005: + case 0x004C0006: + case 0x004C0007: + case 0x004C0008: + case 0x004C0009: + case 0x005D0008: // Strix Point - this is gpu_metrics_v3_0.temperature_soc, !=gpu_metrics_v3_0.temperature_skin + case 0x005D0009: + case 0x005D000B: + case 0x0064020c: // StrixHalo tested + _read_float_value(0x5C); + default: + break; + } + return NAN; +} +EXP float CALL get_dgpu_skin_temp_limit(ryzen_access ry) { + switch (ry->table_ver) + { + case 0x00370000: + case 0x00370001: + case 0x00370002: + case 0x00370003: + case 0x00370004: + case 0x00370005: + case 0x00400001: + case 0x00400002: + case 0x00400003: + case 0x00400004: + case 0x00400005: + case 0x00450004: + case 0x00450005: + case 0x004C0006: + case 0x004C0007: + case 0x004C0008: + case 0x004C0009: + case 0x0064020c: // StrixHalo tested + _read_float_value(0x60); + case 0x005D0008: // Strix Point - tested + case 0x005D0009: + case 0x005D000B: + _read_float_value(0x68); + default: + break; + } + return NAN; +} +EXP float CALL get_dgpu_skin_temp_value(ryzen_access ry) { + switch (ry->table_ver) + { + case 0x00370000: + case 0x00370001: + case 0x00370002: + case 0x00370003: + case 0x00370004: + case 0x00370005: + case 0x00400001: + case 0x00400002: + case 0x00400003: + case 0x00400004: + case 0x00400005: + case 0x00450004: + case 0x00450005: + case 0x004C0006: + case 0x004C0007: + case 0x004C0008: + case 0x004C0009: + case 0x0064020c: + _read_float_value(0x64); + case 0x005D0008: // Strix Point - calculated from corresponding limit + 0x4, 0 on my device due to no dGPU + case 0x005D0009: + case 0x005D000B: + _read_float_value(0x6C); + default: + break; + } + return NAN; +} + +EXP float CALL get_psi0_current(ryzen_access ry) { + switch (ry->table_ver) + { + case 0x001E0001: + case 0x001E0002: + case 0x001E0003: + case 0x001E0004: + case 0x001E0005: + case 0x001E000A: + case 0x001E0101: + _read_float_value(0x40); + case 0x00370000: + case 0x00370001: + case 0x00370002: + case 0x00370003: + case 0x00370004: + case 0x00370005: + case 0x00400001: + case 0x00400002: + case 0x00400003: + case 0x00400004: + case 0x00400005: + case 0x004C0006: + case 0x004C0007: + case 0x004C0008: + case 0x004C0009: + _read_float_value(0x78); + default: + break; + } + return NAN; +} + +EXP float CALL get_psi0soc_current(ryzen_access ry) { + switch (ry->table_ver) + { + case 0x001E0001: + case 0x001E0002: + case 0x001E0003: + case 0x001E0004: + case 0x001E0005: + case 0x001E000A: + case 0x001E0101: + _read_float_value(0x48); + case 0x00370000: + case 0x00370001: + case 0x00370002: + case 0x00370003: + case 0x00370004: + case 0x00370005: + case 0x00400001: + case 0x00400002: + case 0x00400003: + case 0x00400004: + case 0x00400005: + case 0x004C0006: + case 0x004C0007: + case 0x004C0008: + case 0x004C0009: + _read_float_value(0x80); + default: + break; + } + return NAN; +} + +EXP float CALL get_cclk_setpoint(ryzen_access ry) { + switch (ry->table_ver) + { + case 0x001E0001: + case 0x001E0002: + case 0x001E0003: + case 0x001E0004: + case 0x001E0005: + case 0x001E000A: + case 0x001E0101: + _read_float_value(0x98); //use core1 because core0 is not reported on dual core cpus; + case 0x00370000: + case 0x00370001: + case 0x00370002: + case 0x00370003: + case 0x00370004: + case 0x00370005: + _read_float_value(0xFC); + case 0x00400001: + case 0x00400002: + case 0x00400003: + case 0x00400004: + case 0x00400005: + _read_float_value(0x100); + case 0x005D0008: // Strix Point - first entry of 12-core boost target array, tested + case 0x005D0009: + case 0x005D000B: + _read_float_value(0xD0); + default: + break; + } + return NAN; +} + +EXP float CALL get_cclk_busy_value(ryzen_access ry) { + switch (ry->table_ver) + { + case 0x001E0001: + case 0x001E0002: + case 0x001E0003: + case 0x001E0004: + case 0x001E0005: + case 0x001E000A: + case 0x001E0101: + _read_float_value(0x9C); //use core1 because core0 is not reported on dual core cpus + case 0x00370000: + case 0x00370001: + case 0x00370002: + case 0x00370003: + case 0x00370004: + case 0x00370005: + _read_float_value(0x100); + case 0x00400001: + case 0x00400002: + case 0x00400003: + case 0x00400004: + case 0x00400005: + _read_float_value(0x104); + case 0x005D0008: // Strix Point - aggregate busy clock, tested + case 0x005D0009: + case 0x005D000B: + _read_float_value(0xCC); + default: + break; + } + return NAN; +} + +EXP float CALL get_stapm_time(ryzen_access ry) +{ + switch (ry->table_ver) + { + case 0x001E0002: + _read_float_value(0x564); + case 0x001E0003: + _read_float_value(0x55C); + case 0x001E0004: + case 0x001E0005: + case 0x001E000A: + case 0x001E0101: + _read_float_value(0x5E0); + case 0x00370000: + _read_float_value(0x768); + case 0x00370001: + _read_float_value(0x858); + case 0x00370002: + _read_float_value(0x860); + case 0x00370003: + case 0x00370004: + _read_float_value(0x880); + case 0x00370005: + _read_float_value(0x89C); + case 0x00400001: + _read_float_value(0x8E4); + case 0x00400002: + _read_float_value(0x8FC); + case 0x00400003: + _read_float_value(0x920); + case 0x00400004: + case 0x00400005: + case 0x004C0006: + case 0x004C0007: + case 0x004C0008: + case 0x004C0009: + _read_float_value(0x918); + case 0x005D0008: // Strix Point - calculated from slow time (0x9C0 - 0x4), always 1? + case 0x005D0009: + case 0x005D000B: + _read_float_value(0x9BC); + case 0x00650005: // Krackan Point, might be incorrect + _read_float_value(0x90C); + default: + break; + } + return NAN; +} + +EXP float CALL get_slow_time(ryzen_access ry) { + switch (ry->table_ver) + { + case 0x001E0002: + _read_float_value(0x568); + case 0x001E0003: + _read_float_value(0x560); + case 0x001E0004: + case 0x001E0005: + case 0x001E000A: + case 0x001E0101: + _read_float_value(0x5E4); + case 0x00370000: + _read_float_value(0x76C); + case 0x00370001: + _read_float_value(0x85C); + case 0x00370002: + _read_float_value(0x864); + case 0x00370003: + case 0x00370004: + _read_float_value(0x884); + case 0x00370005: + _read_float_value(0x8A0); + case 0x00400001: + _read_float_value(0x8E8); + case 0x00400002: + _read_float_value(0x900); + case 0x00400003: + _read_float_value(0x924); + case 0x00400004: + case 0x00400005: + case 0x004C0006: + case 0x004C0007: + case 0x004C0008: + case 0x004C0009: + _read_float_value(0x91C); + case 0x005D0008: // Strix Point - tested, defaults to 5 (low-power/balanced) or 15 (performance), max 30 + case 0x005D0009: + case 0x005D000B: + _read_float_value(0x9C0); + case 0x00650005: // Krackan Point, might be incorrect + _read_float_value(0x910); + default: + break; + } + return NAN; +} + +EXP float CALL get_core_power(ryzen_access ry, uint32_t core) { + if (core > 15) + return NAN; + + uint32_t baseOffset; + // kevin 0x104 might be power, 16 entries + switch (ry->table_ver) { + case 0x00370000: + case 0x00370001: + case 0x00370002: + case 0x00370003: + case 0x00370004: + baseOffset = 0x300; + break; + case 0x00370005: + baseOffset = 0x31C; + break; + case 0x003F0000: { // Van Gogh + if (core >= 4) + return NAN; + + baseOffset = 0x238; + } + break; + case 0x00400001: + baseOffset = 0x304; + break; + case 0x00400004: + case 0x00400005: + baseOffset = 0x320; + break; + case 0x005D0008: // Strix Point - manufacturer-disabled cores are 0W (12 cores in total) + case 0x005D0009: + case 0x005D000B: + baseOffset = 0x9D8; + break; + case 0x0064020c: // Strix Halo + baseOffset = 0xB90; + break; + default: + return NAN; + } + + _read_float_value(baseOffset + (core * 4)); +} + +EXP float CALL get_core_volt(ryzen_access ry, uint32_t core) { + if (core > 15) + return NAN; + + uint32_t baseOffset; + // kevinh 0x1cc - 17 entries? + switch (ry->table_ver) { + case 0x00370000: + case 0x00370001: + case 0x00370002: + case 0x00370003: + case 0x00370004: + baseOffset = 0x320; + break; + case 0x00370005: + baseOffset = 0x33C; + break; + case 0x003F0000: { // Van Gogh + if (core >= 4) + return NAN; + + baseOffset = 0x248; + } + break; + case 0x00400004: + case 0x00400005: + baseOffset = 0x340; + break; + case 0x005D0008: // Strix Point - manufacturer-disabled cores are 0V + case 0x005D0009: + case 0x005D000B: + baseOffset = 0xA08; + break; + case 0x0064020c: // Strix Halo + baseOffset = 0xBD0; + break; + default: + return NAN; + } + + _read_float_value(baseOffset + (core * 4)); +} + +EXP float CALL get_core_temp(ryzen_access ry, uint32_t core) { + if (core > 15) + return NAN; + + uint32_t baseOffset; + + switch (ry->table_ver) { + case 0x00370000: + case 0x00370001: + case 0x00370002: + case 0x00370003: + case 0x00370004: + baseOffset = 0x340; + break; + case 0x00370005: + baseOffset = 0x35C; + break; + case 0x003F0000: { // Van Gogh + if (core >= 4) + return NAN; + + baseOffset = 0x258; + } + break; + case 0x00400004: + case 0x00400005: + baseOffset = 0x360; + break; + case 0x005D0008: // Strix Point - manufacturer-disabled cores also have temp collected + case 0x005D0009: + case 0x005D000B: + baseOffset = 0xA38; + break; + case 0x0064020c: // Strix Halo + baseOffset = 0xC10; + break; + default: + return NAN; + } + + _read_float_value(baseOffset + (core * 4)); +} + +EXP float CALL get_core_clk(ryzen_access ry, uint32_t core) { + if (core > 15) + return NAN; + + uint32_t baseOffset; + + switch (ry->table_ver) { + case 0x00370000: + case 0x00370001: + case 0x00370002: + case 0x00370003: + case 0x00370004: + baseOffset = 0x3A0; + break; + case 0x00370005: + baseOffset = 0x3BC; + break; + case 0x003F0000: { // Van Gogh + if (core >= 4) + return NAN; + + baseOffset = 0x288; + } + break; + case 0x00400004: + case 0x00400005: + baseOffset = 0x3c0; + break; + case 0x005D0008: // Strix Point - manufacturer-disabled cores are 0GHz + case 0x005D0009: + case 0x005D000B: + baseOffset = 0xA68; + break; + case 0x0064020c: + baseOffset = 0xc50; + break; + default: + return NAN; + } + + _read_float_value(baseOffset + (core * 4)); +} + +EXP float CALL get_l3_clk(ryzen_access ry) { + switch (ry->table_ver) + { + case 0x00370000: + case 0x00370001: + case 0x00370002: + case 0x00370003: + case 0x00370004: + _read_float_value(0x568); + case 0x00370005: + _read_float_value(0x584); + case 0x003F0000: //Van Gogh + _read_float_value(0x35C); //860 + case 0x00400004: + case 0x00400005: + _read_float_value(0x614); //1556 + default: + break; + } + return NAN; +} + +EXP float CALL get_l3_logic(ryzen_access ry) { + switch (ry->table_ver) + { + case 0x00370000: + case 0x00370001: + case 0x00370002: + case 0x00370003: + case 0x00370004: + _read_float_value(0x540); + case 0x00370005: + _read_float_value(0x55C); + case 0x003F0000: //Van Gogh + _read_float_value(0x348); //840 + case 0x00400004: + case 0x00400005: + _read_float_value(0x600); //1536 + default: + break; + } + return NAN; +} + +EXP float CALL get_l3_vddm(ryzen_access ry) { + switch (ry->table_ver) + { + case 0x00370000: + case 0x00370001: + case 0x00370002: + case 0x00370003: + case 0x00370004: + _read_float_value(0x548); + case 0x00370005: + _read_float_value(0x564); + case 0x003F0000: //Van Gogh + _read_float_value(0x34C); //844 + case 0x00400004: + case 0x00400005: + _read_float_value(0x604); //1540 + default: + break; + } + return NAN; +} + +EXP float CALL get_l3_temp(ryzen_access ry) { + switch (ry->table_ver) + { + case 0x00370000: + case 0x00370001: + case 0x00370002: + case 0x00370003: + case 0x00370004: + _read_float_value(0x550); + case 0x00370005: + _read_float_value(0x56C); + case 0x003F0000: //Van Gogh + _read_float_value(0x350); //848 + case 0x00400004: + case 0x00400005: + _read_float_value(0x608); //1544 + default: + break; + } + return NAN; +} + +EXP float CALL get_gfx_clk(ryzen_access ry) { + switch (ry->table_ver) + { + case 0x00370000: + case 0x00370001: + case 0x00370002: + case 0x00370003: + case 0x00370004: + _read_float_value(0x5B4); + case 0x00370005: + _read_float_value(0x5D0); + case 0x00400001: + _read_float_value(0x60C); //1548 + case 0x00400002: + _read_float_value(0x624); //1572 + case 0x00400003: + _read_float_value(0x644); //1604 + case 0x00400004: + case 0x00400005: + _read_float_value(0x648); //1608 + case 0x003F0000: //Van Gogh + _read_float_value(0x388); //904 + /* + * Strix Point - tested, RAPL + gpu_metrics_v3_0 + llama-bench/FurMark/memtester + switching platform_profile + * 0x4B4: gfx power, as FurMark consumes more than llama-bench when pkg power is constant + * memtester won't increase this, so this is not uncore power + * 0x4B8: volt, 4BC: temp, 4C{0,4}: gfx-related clk (MHz), 4C8: unknown clk + */ + case 0x005D0008: + case 0x005D0009: + case 0x005D000B: + _read_float_value(0x4C0); // 4C0 and 4C4 are always close to each other, but 4C0 seems more correct + case 0x0064020c: // Strix Halo + _read_float_value(0x558); + default: + break; + } + return NAN; +} + +EXP float CALL get_gfx_volt(ryzen_access ry) { + switch (ry->table_ver) + { + case 0x00370000: + case 0x00370001: + case 0x00370002: + case 0x00370003: + case 0x00370004: + _read_float_value(0x5A8); + case 0x00370005: + _read_float_value(0x5C4); + case 0x00400001: + _read_float_value(0x600); //1536 + case 0x00400002: + _read_float_value(0x618); //1560 + case 0x00400003: + _read_float_value(0x638); //1592 + case 0x00400004: + case 0x00400005: + _read_float_value(0x63C); //1596 + case 0x003F0000: //Van Gogh + _read_float_value(0x37C); //896 + case 0x005D0008: // Strix Point + case 0x005D0009: + case 0x005D000B: + _read_float_value(0x4B8); + case 0x0064020c: // Strix Halo + _read_float_value(0x54C); + default: + break; + } + return NAN; +} + +EXP float CALL get_gfx_temp(ryzen_access ry) { + switch (ry->table_ver) + { + case 0x00370000: + case 0x00370001: + case 0x00370002: + case 0x00370003: + case 0x00370004: + _read_float_value(0x5AC); + case 0x00370005: + _read_float_value(0x5C8); + case 0x00400001: + _read_float_value(0x604); //1540 + case 0x00400002: + _read_float_value(0x61C); //1564 + case 0x00400003: + _read_float_value(0x63C); //1596 + case 0x00400004: + case 0x00400005: + _read_float_value(0x640); //1600 + case 0x003F0000: //Van Gogh + _read_float_value(0x380); //896 + case 0x005D0008: // Strix Point + case 0x005D0009: + case 0x005D000B: + _read_float_value(0x4C8); + case 0x0064020c: // Strix Halo + _read_float_value(0x550); + default: + break; + } + return NAN; +} + +EXP float CALL get_fclk(ryzen_access ry) { + switch (ry->table_ver) + { + case 0x00370000: + case 0x00370001: + case 0x00370002: + case 0x00370003: + case 0x00370004: + _read_float_value(0x5CC); + case 0x00370005: + _read_float_value(0x5E8); + case 0x003F0000: //Van Gogh + _read_float_value(0x3C5); //956 + case 0x00400004: + case 0x00400005: + _read_float_value(0x664); //1636 + /* + * Strix Point - tested (AI 365, LPDDR5-7500), switching platform_profile + * 0x4E0-0x548 seems to be target clocks (avg between two reads) + * 0x550-0x5B4 seems to be corresponding sampled clocks (avg between two reads) + * 0x550-0x568 maybe 0 if platform_profile is low-power + * 0x4E0/54C: fabric_clk = gpu_metrics_v3_0.average_fclk_frequency + * 0x4E4/550: controller_clk? = DRM_IOCTL_AMDGPU_INFO GFX_MCLK/gpu_metrics_v3_0.average_uclk_frequency + * 0x4E8/554: phy_clk? = controller_clk * 2 + * 0x4EC/558: transfer rate (MT/s) = phy_clk * 2 (low-power) or 4 (balanced/performance) + * 0x4F0/55C: = gpu_metrics_v3_0.average_vclk_frequency + * 0x4F8/564: = gpu_metrics_v3_0.average_socclk_frequency + * 0x50C/578: = gpu_metrics_v3_0.average_mpipu_frequency + * 0x510/57C: = gpu_metrics_v3_0.average_ipuclk_frequency + * Possible 0x4E0 values: + * 400, 1050, 1200, 1400, 1600, 1960 (declared at 0x240-0x254) + * 400, 1050, 1200, 1400, 1500, 1600, 1800, 1960 (amdgpu sysfs pp_dpm_fclk) + * Possible 0x4E4 values: + * 400, 800, 937.5, 937.5, 937.5, 937.5 (declared at 0x258-0x26C) + * 400, 800, 937.5 (amdgpu sysfs pp_dpm_mclk) + * Possible 0x4E8 values: + * 800, 1600, 1875, 1875, 1875, 1875 (declared at 0x270-0x284) + * Possible 0x4EC values: + * 1600, 6400, 7500, 7500, 7500, 7500 (declared at 0x288-0x29C) + * Possible 0x4F0 values: + * 800, 800, 1605, 1766, 1962, 2208, 2523, 2944 (amdgpu sysfs pp_dpm_vclk) + * Possible 0x4F8 values: + * 600, 736, 883, 981, 1104, 1261, 1261, 1472 (amdgpu sysfs pp_dpm_socclk) + * See also: https://semiengineering.com/advantages-of-lpddr5-a-new-clocking-scheme/ + */ + case 0x005D0008: + case 0x005D0009: + case 0x005D000B: + _read_float_value(0x4E0); + default: + break; + } + return NAN; +} + +EXP float CALL get_mem_clk(ryzen_access ry) { + switch (ry->table_ver) + { + case 0x00370000: + case 0x00370001: + case 0x00370002: + case 0x00370003: + case 0x00370004: + _read_float_value(0x5D4); + case 0x00370005: + _read_float_value(0x5F0); + case 0x003F0000: //Van Gogh + _read_float_value(0x3C4); //964 + case 0x00400004: + case 0x00400005: + _read_float_value(0x66c); //1644 + case 0x005D0008: // Strix Point - see above + case 0x005D0009: + case 0x005D000B: + _read_float_value(0x4EC); // FIXME: which one? + default: + break; + } + return NAN; +} + +EXP float CALL get_soc_volt(ryzen_access ry) { + switch (ry->table_ver) + { + case 0x00370000: + case 0x00370001: + case 0x00370002: + case 0x00370003: + case 0x00370004: + _read_float_value(0x198); + case 0x00370005: + _read_float_value(0x198); + case 0x003F0000: //Van Gogh + _read_float_value(0x1A0); //416 + case 0x00400004: + case 0x00400005: + _read_float_value(0x19c); //412 + + default: + break; + } + return NAN; +} + +EXP float CALL get_soc_power(ryzen_access ry) { + switch (ry->table_ver) + { + case 0x00370000: + case 0x00370001: + case 0x00370002: + case 0x00370003: + case 0x00370004: + _read_float_value(0x1A0); + case 0x00370005: + _read_float_value(0x1A0); + case 0x003F0000: //Van Gogh + _read_float_value(0x1A8); //424 + case 0x00400004: + case 0x00400005: + _read_float_value(0x1a4); //420 + default: + break; + } + return NAN; +} + +EXP float CALL get_socket_power(ryzen_access ry) { + switch (ry->table_ver) + { + case 0x00370000: + case 0x00370001: + case 0x00370002: + case 0x00370003: + case 0x00370004: + case 0x00370005: + _read_float_value(0x98); + case 0x00400001: + case 0x00400002: + case 0x00400003: + case 0x00400004: + case 0x00400005: + _read_float_value(0x98); //152 + case 0x003F0000: //Van Gogh + _read_float_value(0xA8); //168 + /* + * Strix Point - tested, RAPL + memtester/llama-bench + switching platform_profile + * 0xA4: = RAPL pkg power - gfx power, should this be SoC power??? + * 0xD0: = RAPL pkg power + */ + case 0x005D0008: + case 0x005D0009: + case 0x005D000B: + _read_float_value(0xD0); + default: + break; + } + return NAN; +} diff --git a/os/licenses/ryzenadj/src/lib/cpuid.c b/os/licenses/ryzenadj/src/lib/cpuid.c new file mode 100644 index 00000000..505fc9ef --- /dev/null +++ b/os/licenses/ryzenadj/src/lib/cpuid.c @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: LGPL +/* Copyright (C) 2020 Jiaxun Yang */ +/* RyzenAdj CPU ID stuff */ + +#include "ryzenadj.h" + +#include + +#if defined(__GNUC__) +#include +#elif defined (_WIN32) +#include +#endif + +#define CPUID_VENDOR_AMD "AuthenticAMD" + +static void getcpuid(unsigned int CPUInfo[4], unsigned int InfoType) +{ +#if defined(__GNUC__) + __cpuid(InfoType, CPUInfo[0],CPUInfo[1],CPUInfo[2],CPUInfo[3]); +#elif defined(_WIN32) + __cpuid((int*)(void*)CPUInfo, (int)InfoType); +#endif +} + +static enum ryzen_family cpuid_family = WAIT_FOR_LOAD; + +static enum ryzen_family cpuid_load_family() +{ + uint32_t regs[4]; + int family, model; + char vendor[4 * 4]; + + getcpuid(regs, 0); + + /* Hack Alert! Put into str buffer */ + *(uint32_t *) &vendor[0] = regs[1]; + *(uint32_t *) &vendor[4] = regs[3]; + *(uint32_t *) &vendor[8] = regs[2]; + + if (strncmp(vendor, CPUID_VENDOR_AMD, strlen(CPUID_VENDOR_AMD))) { + printf("Not AMD processor, must be kidding\n"); + return FAM_UNKNOWN; + } + + getcpuid(regs, 1); + + family = ((regs[0] >> 8) & 0xf) + ((regs[0] >> 20) & 0xff); + model = ((regs[0] >> 4) & 0xf) | ((regs[0] >> 12) & 0xf0); + + switch (family) { + case 0x17: /* Zen, Zen+, Zen2 */ + switch (model) { + case 17: + return FAM_RAVEN; + case 24: + return FAM_PICASSO; + case 32: + return FAM_DALI; + case 96: + return FAM_RENOIR; + case 104: + return FAM_LUCIENNE; + case 144: + case 145: + return FAM_VANGOGH; + case 160: + return FAM_MENDOCINO; + default: + printf("Fam%xh: unsupported model %d\n", family, model); + break; + }; + break; + + case 0x19: /* Zen3, Zen4 */ + switch (model) { + case 80: + return FAM_CEZANNE; + case 64: + case 68: + return FAM_REMBRANDT; + case 97: + return FAM_DRAGONRANGE; + case 116: + case 120: + return FAM_PHOENIX; + case 117: + return FAM_HAWKPOINT; + default: + printf("Fam%xh: unsupported model %d\n", family, model); + break; + }; + break; + case 0x1A: /* Zen5, Zen6 */ + switch (model) { + case 32: + case 36: + return FAM_STRIXPOINT; + case 68: + return FAM_FIRERANGE; + case 96: + return FAM_KRACKANPOINT; + case 112: + return FAM_STRIXHALO; + default: + printf("Fam%xh: unsupported model %d\n", family, model); + break; + } + + default: + printf("Unsupported family: %xh\n", family); + break; + } + + printf("Only Ryzen Mobile Series are supported\n"); + return FAM_UNKNOWN; +} + +/* + * The function cpuid_load_family() processes a lot of + * information. This information will be used several + * times by acquiring the same data. Avoid this wasted + * computation by entering cpuid_get_family(). This function + * guarantees the load is done only 1 time. + */ +enum ryzen_family cpuid_get_family() { + if (cpuid_family == WAIT_FOR_LOAD) + cpuid_family = cpuid_load_family(); + + return cpuid_family; +} diff --git a/os/licenses/ryzenadj/src/lib/linux/osdep_linux.c b/os/licenses/ryzenadj/src/lib/linux/osdep_linux.c new file mode 100644 index 00000000..ee1d1b61 --- /dev/null +++ b/os/licenses/ryzenadj/src/lib/linux/osdep_linux.c @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: LGPL +/* Copyright (C) 2018-2019 Jiaxun Yang */ +#include + +#include "osdep_linux_mem.h" +#include "osdep_linux_smu_kernel_module.h" + +bool is_smu = false; + +static bool is_ryzen_smu_driver_compatible() { + FILE *drv_ver = fopen("/sys/kernel/ryzen_smu_drv/drv_version", "r"); + int major, minor, patch, ret; + + if (drv_ver == NULL) { + DBG("failed to open drv_version"); + return false; + } + + ret = fscanf(drv_ver, "%d.%d.%d", &major, &minor, &patch); + if (ret == EOF || ret < 3) { + DBG("failed to parse ryzen_smu version string\n"); + fclose(drv_ver); + return false; + } + + if (major != 0 || minor != 1 || patch < 7) { + fclose(drv_ver); + return false; + } + + fclose(drv_ver); + return true; +} + +os_access_obj_t *init_os_access_obj() { + struct stat stats; + + if (lstat("/sys/kernel/ryzen_smu_drv", &stats) == 0 && is_ryzen_smu_driver_compatible()) { + fprintf(stderr, "detected compatible ryzen_smu kernel module\n"); + is_smu = true; + return init_os_access_obj_kmod(); + } + + fprintf(stderr, "no compatible ryzen_smu kernel module found, fallback to /dev/mem\n"); + return init_os_access_obj_mem(); +} + +int init_mem_obj(os_access_obj_t *os_access, const uintptr_t physAddr) { + if (is_smu) + return init_mem_obj_kmod(os_access, physAddr); + + return init_mem_obj_mem(os_access, physAddr); +} + +void free_os_access_obj(os_access_obj_t *obj) { + if (is_smu) + free_os_access_obj_kmod(obj); + else + free_os_access_obj_mem(obj); + + is_smu = false; +} + +uint32_t smn_reg_read(const os_access_obj_t *obj, const uint32_t addr) { + if (is_smu) + return smn_reg_read_kmod(obj, addr); + + return smn_reg_read_mem(obj, addr); +} + +void smn_reg_write(const os_access_obj_t *obj, const uint32_t addr, const uint32_t data) { + if (is_smu) + smn_reg_write_kmod(obj, addr, data); + else + smn_reg_write_mem(obj, addr, data); +} + +int copy_pm_table(const os_access_obj_t *obj, void *buffer, const size_t size) { + if (is_smu) + return copy_pm_table_kmod(obj, buffer, size); + + return copy_pm_table_mem(obj, buffer, size); +} + +int compare_pm_table(const void *buffer, const size_t size) { + if (is_smu) + return compare_pm_table_kmod(buffer, size); + + return compare_pm_table_mem(buffer, size); +} + +bool is_using_smu_driver() { + return is_smu; +} diff --git a/os/licenses/ryzenadj/src/lib/linux/osdep_linux_mem.c b/os/licenses/ryzenadj/src/lib/linux/osdep_linux_mem.c new file mode 100644 index 00000000..5bf2db18 --- /dev/null +++ b/os/licenses/ryzenadj/src/lib/linux/osdep_linux_mem.c @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: LGPL +/* Copyright (C) 2018-2019 Jiaxun Yang */ +/* Access PCI Config Space - libpci */ +#include +#include +#include +#include +#include +#include + +#include "osdep_linux_mem.h" + +void *phy_map = MAP_FAILED; + +os_access_obj_t *init_os_access_obj_mem() { + os_access_obj_t *obj = malloc(sizeof(os_access_obj_t)); + + if (obj == NULL) + return NULL; + + memset(obj, 0, sizeof(os_access_obj_t)); + + obj->access.mem.pci_acc = pci_alloc(); + if (!obj->access.mem.pci_acc) { + fprintf(stderr, "pci_alloc failed\n"); + goto err_exit; + } + + pci_init(obj->access.mem.pci_acc); + + obj->access.mem.pci_dev = pci_get_dev(obj->access.mem.pci_acc, 0, 0, 0, 0); + if (!obj->access.mem.pci_dev) { + fprintf(stderr, "Unable to get pci device\n"); + pci_cleanup(obj->access.mem.pci_acc); + goto err_exit; + } + + pci_fill_info(obj->access.mem.pci_dev, PCI_FILL_IDENT | PCI_FILL_BASES | PCI_FILL_CLASS); + return obj; + +err_exit: + free(obj); + return NULL; +} + +int init_mem_obj_mem([[maybe_unused]] os_access_obj_t *os_access, const uintptr_t physAddr) { + const int dev_mem_fd = open("/dev/mem", O_RDONLY); + + // It is too complicated to check PAT, CONFIG_NONPROMISC_DEVMEM, CONFIG_STRICT_DEVMEM or other dependencies, just try to open /dev/mem + if (dev_mem_fd > 0) { + phy_map = mmap(NULL, 0x1000, PROT_READ, MAP_SHARED, dev_mem_fd, (long)physAddr); + close(dev_mem_fd); + } + + return phy_map == MAP_FAILED ? -1 : 0; +} + +void free_os_access_obj_mem(os_access_obj_t *obj) { + if (obj == NULL) + return; + + if (obj->access.mem.pci_dev) + pci_free_dev(obj->access.mem.pci_dev); + + if (obj->access.mem.pci_acc) + pci_cleanup(obj->access.mem.pci_acc); + + if (phy_map != MAP_FAILED) { + munmap(phy_map, 0x1000); + phy_map = MAP_FAILED; + } + + free(obj); +} + +uint32_t smn_reg_read_mem(const os_access_obj_t *obj, const uint32_t addr) { + pci_write_long(obj->access.mem.pci_dev, NB_PCI_REG_ADDR_ADDR, addr & (~0x3)); + return pci_read_long(obj->access.mem.pci_dev, NB_PCI_REG_DATA_ADDR); +} + +void smn_reg_write_mem(const os_access_obj_t *obj, const uint32_t addr, const uint32_t data) { + pci_write_long(obj->access.mem.pci_dev, NB_PCI_REG_ADDR_ADDR, addr); + pci_write_long(obj->access.mem.pci_dev, NB_PCI_REG_DATA_ADDR, data); +} + +int copy_pm_table_mem([[maybe_unused]] const os_access_obj_t *obj, void *buffer, const size_t size) { + if (phy_map != MAP_FAILED) { + memcpy(buffer, phy_map, size); + return 0; + } + + DBG("failed to get pm_table from /dev/mem\n"); + return -1; +} + +int compare_pm_table_mem(const void *buffer, const size_t size) { + return memcmp(buffer, phy_map, size); +} diff --git a/os/licenses/ryzenadj/src/lib/linux/osdep_linux_mem.h b/os/licenses/ryzenadj/src/lib/linux/osdep_linux_mem.h new file mode 100644 index 00000000..baba82b3 --- /dev/null +++ b/os/licenses/ryzenadj/src/lib/linux/osdep_linux_mem.h @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: LGPL +/* Copyright (C) 2018-2019 Jiaxun Yang */ +/* Access PCI Config Space - libpci */ +#pragma once + +#include "../nb_smu_ops.h" + +os_access_obj_t *init_os_access_obj_mem(); +int init_mem_obj_mem(os_access_obj_t *os_access, uintptr_t physAddr); +int copy_pm_table_mem(const os_access_obj_t *obj, void *buffer, size_t size); +int compare_pm_table_mem(const void *buffer, size_t size); +void free_os_access_obj_mem(os_access_obj_t *obj); + +uint32_t smn_reg_read_mem(const os_access_obj_t *obj, uint32_t addr); +void smn_reg_write_mem(const os_access_obj_t *obj, uint32_t addr, uint32_t data); diff --git a/os/licenses/ryzenadj/src/lib/linux/osdep_linux_smu_kernel_module.c b/os/licenses/ryzenadj/src/lib/linux/osdep_linux_smu_kernel_module.c new file mode 100644 index 00000000..41ef6209 --- /dev/null +++ b/os/licenses/ryzenadj/src/lib/linux/osdep_linux_smu_kernel_module.c @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: LGPL +/* Backend which uses the sysfs interface provided by the ryzen_smu kernel module. */ +#include +#include +#include +#include +#include + +#include "osdep_linux_smu_kernel_module.h" + +static uint32_t get_pm_table_size() { + const int fd = open("/sys/kernel/ryzen_smu_drv/pm_table_size", O_RDONLY); + uint32_t table_sz = 0; + + if (fd == -1) + return -1; + + if (read(fd, &table_sz, sizeof(table_sz)) == -1) { + DBG("failed to retrieve PM table size: %s\n", strerror(errno)); + close(fd); + return -1; + } + + close(fd); + return table_sz; +} + +os_access_obj_t *init_os_access_obj_kmod() { + os_access_obj_t *obj = malloc(sizeof(os_access_obj_t)); + + if (obj == NULL) + return NULL; + + memset(obj, 0, sizeof(os_access_obj_t)); + + obj->access.kmod.pm_table_size = get_pm_table_size(); + if (obj->access.kmod.pm_table_size == -1) + goto err_exit; + + obj->access.kmod.smn_fd = open("/sys/kernel/ryzen_smu_drv/smn", O_RDWR); + if (obj->access.kmod.smn_fd == -1) { + DBG("failed to open smn fd: %s\n", strerror(errno)); + goto err_exit; + } + + obj->access.kmod.pm_table_fd = open("/sys/kernel/ryzen_smu_drv/pm_table", O_RDONLY); + if (obj->access.kmod.pm_table_fd == -1) { + DBG("failed to open pm_table fd: %s\n", strerror(errno)); + close(obj->access.kmod.smn_fd); + goto err_exit; + } + + return obj; + +err_exit: + free(obj); + return NULL; +} + +int init_mem_obj_kmod([[maybe_unused]] os_access_obj_t *os_access, [[maybe_unused]] const uintptr_t physAddr) { + return 0; +} + +void free_os_access_obj_kmod(os_access_obj_t *obj) { + close(obj->access.kmod.smn_fd); + close(obj->access.kmod.pm_table_fd); + free(obj); +} + +uint32_t smn_reg_read_kmod(const os_access_obj_t *obj, const uint32_t addr) { + uint32_t result = 0; + + lseek(obj->access.kmod.smn_fd, 0, SEEK_SET); + + if (write(obj->access.kmod.smn_fd, &addr, sizeof(addr)) == -1) { + DBG("%s: write error: %s\n", __func__, strerror(errno)); + return 0; + } + + lseek(obj->access.kmod.smn_fd, 0, SEEK_SET); + + if (read(obj->access.kmod.smn_fd, &result, sizeof(result)) == -1) { + DBG("%s: read error: %s\n", __func__, strerror(errno)); + return 0; + } + + return result; +} + +void smn_reg_write_kmod(const os_access_obj_t *obj, const uint32_t addr, const uint32_t data) { + const uint32_t write_buffer[2] = { addr, data }; + + lseek(obj->access.kmod.smn_fd, 0, SEEK_SET); + + if (write(obj->access.kmod.smn_fd, &write_buffer, sizeof(write_buffer)) == -1) + DBG("%s: error: %s\n", __func__, strerror(errno)); +} + +int copy_pm_table_kmod(const os_access_obj_t *obj, void *buffer, const size_t size) { + if (obj->access.kmod.pm_table_size < size) { + DBG("PM table size too small: ryzenadj (%zd) | ryzen_smu (%zd)\n", size, obj->access.kmod.pm_table_size); + return -1; + } + + if (obj->access.kmod.pm_table_size != size) { + DBG("PM table size mismatch (reading prefix): ryzenadj (%zd) | ryzen_smu (%zd)\n", size, obj->access.kmod.pm_table_size); + } + + lseek(obj->access.kmod.pm_table_fd, 0, SEEK_SET); + + if (read(obj->access.kmod.pm_table_fd, buffer, size) == -1) { + DBG("%s: error: %s\n", __func__, strerror(errno)); + return -1; + } + + return 0; +} + +int compare_pm_table_kmod([[maybe_unused]] const void *buffer, [[maybe_unused]] size_t size) { + DBG("internal error: compare_pm_table() should never be called if ryzen_smu is loaded\n"); + return -1; +} diff --git a/os/licenses/ryzenadj/src/lib/linux/osdep_linux_smu_kernel_module.h b/os/licenses/ryzenadj/src/lib/linux/osdep_linux_smu_kernel_module.h new file mode 100644 index 00000000..ef32d08a --- /dev/null +++ b/os/licenses/ryzenadj/src/lib/linux/osdep_linux_smu_kernel_module.h @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: LGPL +#pragma once + +#include "../nb_smu_ops.h" + +os_access_obj_t *init_os_access_obj_kmod(); +int init_mem_obj_kmod(os_access_obj_t *os_access, uintptr_t physAddr); +int copy_pm_table_kmod(const os_access_obj_t *obj, void *buffer, size_t size); +int compare_pm_table_kmod(const void *buffer, size_t size); +void free_os_access_obj_kmod(os_access_obj_t *obj); + +uint32_t smn_reg_read_kmod(const os_access_obj_t *obj, uint32_t addr); +void smn_reg_write_kmod(const os_access_obj_t *obj, uint32_t addr, uint32_t data); diff --git a/os/licenses/ryzenadj/src/lib/nb_smu_ops.c b/os/licenses/ryzenadj/src/lib/nb_smu_ops.c new file mode 100644 index 00000000..ff7c8f9f --- /dev/null +++ b/os/licenses/ryzenadj/src/lib/nb_smu_ops.c @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: LGPL +/* Copyright (C) 2018-2019 Jiaxun Yang */ +/* Ryzen NB SMU Service Request Operations */ +#include + +#include "ryzenadj.h" + +#define MP1_C2PMSG_MESSAGE_ADDR_1 0x3B10528 +#define MP1_C2PMSG_RESPONSE_ADDR_1 0x3B10564 +#define MP1_C2PMSG_ARG_BASE_1 0x3B10998 + +/* For Vangogh and Rembrandt */ +#define MP1_C2PMSG_MESSAGE_ADDR_2 0x3B10528 +#define MP1_C2PMSG_RESPONSE_ADDR_2 0x3B10578 +#define MP1_C2PMSG_ARG_BASE_2 0x3B10998 + +/* For Strix Point */ +#define MP1_C2PMSG_MESSAGE_ADDR_3 0x3b10928 +#define MP1_C2PMSG_RESPONSE_ADDR_3 0x3b10978 +#define MP1_C2PMSG_ARG_BASE_3 0x3b10998 + +#define PSMU_C2PMSG_MESSAGE_ADDR_1 0x3B10a20 +#define PSMU_C2PMSG_RESPONSE_ADDR_1 0x3B10a80 +#define PSMU_C2PMSG_ARG_BASE_1 0x3B10a88 + +/* For Dragon and Fire Range */ +#define MP1_C2PMSG_MESSAGE_ADDR_4 0x3B10530 +#define MP1_C2PMSG_RESPONSE_ADDR_4 0x3B1057C +#define MP1_C2PMSG_ARG_BASE_4 0x3B109C4 + +#define PSMU_C2PMSG_MESSAGE_ADDR_2 0x03B10524 +#define PSMU_C2PMSG_RESPONSE_ADDR_2 0x03B10570 +#define PSMU_C2PMSG_ARG_BASE_2 0x03B10A40 + +/* +* All the SMU have the same TestMessage as for now +* Correct me if they don't +*/ +#define SMU_TEST_MSG 0x1 + +static uint32_t c2pmsg_argX_addr(const uint32_t base, const uint32_t offt) { + return base + 4 * offt; +} + +uint32_t smu_service_req(smu_t smu, const uint32_t id, smu_service_args_t *args) { + uint32_t response = 0x0; + DBG("SMU_SERVICE REQ_ID:0x%x\n", id); + DBG("SMU_SERVICE REQ: arg0: 0x%x, arg1:0x%x, arg2:0x%x, arg3:0x%x, arg4: 0x%x, arg5: 0x%x\n", \ + args->arg0, args->arg1, args->arg2, args->arg3, args->arg4, args->arg5); + + /* Clear the response */ + smn_reg_write(smu->os_access, smu->rep, 0x0); + /* Pass arguments */ + smn_reg_write(smu->os_access, c2pmsg_argX_addr(smu->arg_base, 0), args->arg0); + smn_reg_write(smu->os_access, c2pmsg_argX_addr(smu->arg_base, 1), args->arg1); + smn_reg_write(smu->os_access, c2pmsg_argX_addr(smu->arg_base, 2), args->arg2); + smn_reg_write(smu->os_access, c2pmsg_argX_addr(smu->arg_base, 3), args->arg3); + smn_reg_write(smu->os_access, c2pmsg_argX_addr(smu->arg_base, 4), args->arg4); + smn_reg_write(smu->os_access, c2pmsg_argX_addr(smu->arg_base, 5), args->arg5); + /* Send message ID */ + smn_reg_write(smu->os_access, smu->msg, id); + /* Wait until response changed */ + while(response == 0x0) { + response = smn_reg_read(smu->os_access, smu->rep); + } + /* Read back arguments */ + args->arg0 = smn_reg_read(smu->os_access, c2pmsg_argX_addr(smu->arg_base, 0)); + args->arg1 = smn_reg_read(smu->os_access, c2pmsg_argX_addr(smu->arg_base, 1)); + args->arg2 = smn_reg_read(smu->os_access, c2pmsg_argX_addr(smu->arg_base, 2)); + args->arg3 = smn_reg_read(smu->os_access, c2pmsg_argX_addr(smu->arg_base, 3)); + args->arg4 = smn_reg_read(smu->os_access, c2pmsg_argX_addr(smu->arg_base, 4)); + args->arg5 = smn_reg_read(smu->os_access, c2pmsg_argX_addr(smu->arg_base, 5)); + + DBG("SMU_SERVICE REP: REP: 0x%x, arg0: 0x%x, arg1:0x%x, arg2:0x%x, arg3:0x%x, arg4: 0x%x, arg5: 0x%x\n", \ + response, args->arg0, args->arg1, args->arg2, args->arg3, args->arg4, args->arg5); + + return response; +} + +static int smu_service_test(smu_t smu) +{ + uint32_t response = 0x0; + + /* Clear the response */ + smn_reg_write(smu->os_access, smu->rep, 0x0); + /* Test message with unique argument */ + smn_reg_write(smu->os_access, smu->arg_base, 0x47); + if(smn_reg_read(smu->os_access, smu->arg_base) != 0x47){ + printf("PCI Bus is not writeable, check secure boot\n"); + return 0; + } + + /* Send message ID */ + smn_reg_write(smu->os_access, smu->msg, SMU_TEST_MSG); + /* Wait until response changed */ + while(response == 0x0) { + response = smn_reg_read(smu->os_access, smu->rep); + } + + return response == REP_MSG_OK; +} + +smu_t get_smu(os_access_obj_t *obj, const int smu_type) { + smu_t smu = malloc(sizeof(*smu)); + + if (smu == NULL) + return NULL; + + smu->os_access = obj; + + /* Fill SMU information */ + switch(smu_type){ + case TYPE_MP1: + switch (cpuid_get_family()) { + case FAM_REMBRANDT: + case FAM_VANGOGH: + case FAM_MENDOCINO: + case FAM_PHOENIX: + case FAM_HAWKPOINT: + smu->msg = MP1_C2PMSG_MESSAGE_ADDR_2; + smu->rep = MP1_C2PMSG_RESPONSE_ADDR_2; + smu->arg_base = MP1_C2PMSG_ARG_BASE_2; + break; + case FAM_KRACKANPOINT: + case FAM_STRIXPOINT: + case FAM_STRIXHALO: + smu->msg = MP1_C2PMSG_MESSAGE_ADDR_3; + smu->rep = MP1_C2PMSG_RESPONSE_ADDR_3; + smu->arg_base = MP1_C2PMSG_ARG_BASE_3; + break; + case FAM_DRAGONRANGE: + case FAM_FIRERANGE: + smu->msg = MP1_C2PMSG_MESSAGE_ADDR_4; + smu->rep = MP1_C2PMSG_RESPONSE_ADDR_4; + smu->arg_base = MP1_C2PMSG_ARG_BASE_4; + break; + default: + smu->msg = MP1_C2PMSG_MESSAGE_ADDR_1; + smu->rep = MP1_C2PMSG_RESPONSE_ADDR_1; + smu->arg_base = MP1_C2PMSG_ARG_BASE_1; + break; + } + break; + case TYPE_PSMU: + switch (cpuid_get_family()) { + case FAM_DRAGONRANGE: + case FAM_FIRERANGE: + smu->msg = PSMU_C2PMSG_MESSAGE_ADDR_2; + smu->rep = PSMU_C2PMSG_RESPONSE_ADDR_2; + smu->arg_base = PSMU_C2PMSG_ARG_BASE_2; + break; + default: + smu->msg = PSMU_C2PMSG_MESSAGE_ADDR_1; + smu->rep = PSMU_C2PMSG_RESPONSE_ADDR_1; + smu->arg_base = PSMU_C2PMSG_ARG_BASE_1; + break; + } + break; + default: + DBG("Failed to get SMU, unknown SMU_TYPE: %i\n", smu_type); + goto err; + break; + } + + if(smu_service_test(smu)){ + return smu; + } else { + DBG("Failed to get SMU, SMU_TYPE: %i\n", smu_type); + goto err; + } +err: + free(smu); + return NULL; +} diff --git a/os/licenses/ryzenadj/src/lib/nb_smu_ops.h b/os/licenses/ryzenadj/src/lib/nb_smu_ops.h new file mode 100644 index 00000000..dfb2d48d --- /dev/null +++ b/os/licenses/ryzenadj/src/lib/nb_smu_ops.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: LGPL */ +/* Copyright (C) 2018-2019 Jiaxun Yang */ +/* Ryzen NB SMU Service Request Operations */ + +#pragma once + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#endif +#include +#include +#include + +#ifdef NDEBUG +#define DBG(...) +#else +#define DBG(...) fprintf(stderr, __VA_ARGS__) +#endif + +enum SMU_TYPE{ + TYPE_MP1, + TYPE_PSMU, + TYPE_COUNT, +}; + +#define NB_PCI_REG_ADDR_ADDR 0xB8 +#define NB_PCI_REG_DATA_ADDR 0xBC + +#define REP_MSG_OK 0x1 +#define REP_MSG_Failed 0xFF +#define REP_MSG_UnknownCmd 0xFE +#define REP_MSG_CmdRejectedPrereq 0xFD +#define REP_MSG_CmdRejectedBusy 0xFC + +typedef struct _smu_service_args_t { + uint32_t arg0; + uint32_t arg1; + uint32_t arg2; + uint32_t arg3; + uint32_t arg4; + uint32_t arg5; +} smu_service_args_t; + +typedef struct { +#ifdef _WIN32 + uint32_t pci_address; + HINSTANCE inpoutDll; +#else + union { + struct { + struct pci_access *pci_acc; + struct pci_dev *pci_dev; + } mem; + struct { + int smn_fd; + int pm_table_fd; + size_t pm_table_size; + } kmod; + } access; +#endif +} os_access_obj_t; + +typedef struct _smu_t { + os_access_obj_t *os_access; + uint32_t msg; + uint32_t rep; + uint32_t arg_base; +} *smu_t; + +os_access_obj_t *init_os_access_obj(); +int init_mem_obj(os_access_obj_t *os_access, uintptr_t physAddr); +int copy_pm_table(const os_access_obj_t *obj, void *buffer, size_t size); +int compare_pm_table(const void *buffer, size_t size); +void free_os_access_obj(os_access_obj_t *obj); + +uint32_t smn_reg_read(const os_access_obj_t *obj, uint32_t addr); +void smn_reg_write(const os_access_obj_t *obj, uint32_t addr, uint32_t data); +bool is_using_smu_driver(); + +smu_t get_smu(os_access_obj_t *obj, int smu_type); +uint32_t smu_service_req(smu_t smu, uint32_t id, smu_service_args_t *args); diff --git a/os/licenses/ryzenadj/src/lib/ryzenadj.h b/os/licenses/ryzenadj/src/lib/ryzenadj.h new file mode 100644 index 00000000..56be8939 --- /dev/null +++ b/os/licenses/ryzenadj/src/lib/ryzenadj.h @@ -0,0 +1,180 @@ +/* SPDX-License-Identifier: LGPL */ +/* Copyright (C) 2019 Jiaxun Yang */ +/* RyzenAdj API */ + +#ifndef RYZENADJ_H +#define RYZENADJ_H +#ifdef __cplusplus +extern "C" { +#endif + +#define RYZENADJ_REVISION_VER 0 +#define RYZENADJ_MAJOR_VER 19 +#define RYZENADJ_MINIOR_VER 0 + +enum ryzen_family { + WAIT_FOR_LOAD = -2, + FAM_UNKNOWN = -1, + FAM_RAVEN = 0, + FAM_PICASSO, + FAM_RENOIR, + FAM_CEZANNE, + FAM_DALI, + FAM_LUCIENNE, + FAM_VANGOGH, + FAM_REMBRANDT, + FAM_MENDOCINO, + FAM_PHOENIX, + FAM_HAWKPOINT, + FAM_DRAGONRANGE, + FAM_KRACKANPOINT, + FAM_STRIXPOINT, + FAM_STRIXHALO, + FAM_FIRERANGE, + FAM_END +}; + +#ifdef _LIBRYZENADJ_INTERNAL +#include "ryzenadj_priv.h" + +#ifdef _WIN32 +#define EXP __declspec(dllexport) +#define CALL __stdcall +#else +#define EXP __attribute__((visibility("default"))) +#define CALL +#endif + +#else + +#ifdef _WIN32 +#define EXP __declspec(dllimport) +#define CALL __stdcall +#else +#define EXP +#define CALL +#endif +struct _ryzen_access; + +#endif + +#define ADJ_ERR_FAM_UNSUPPORTED -1 +#define ADJ_ERR_SMU_TIMEOUT -2 +#define ADJ_ERR_SMU_UNSUPPORTED -3 +#define ADJ_ERR_SMU_REJECTED -4 +#define ADJ_ERR_MEMORY_ACCESS -5 + +typedef struct _ryzen_access *ryzen_access; + +EXP ryzen_access CALL init_ryzenadj(); + +EXP void CALL cleanup_ryzenadj(ryzen_access ry); + +EXP enum ryzen_family get_cpu_family(ryzen_access ry); +EXP int get_bios_if_ver(ryzen_access ry); + +EXP int CALL init_table(ryzen_access ry); +EXP uint32_t CALL get_table_ver(ryzen_access ry); +EXP size_t CALL get_table_size(ryzen_access ry); +EXP float* CALL get_table_values(ryzen_access ry); +EXP int CALL refresh_table(ryzen_access ry); + +EXP int CALL set_stapm_limit(ryzen_access, uint32_t value); +EXP int CALL set_fast_limit(ryzen_access, uint32_t value); +EXP int CALL set_slow_limit(ryzen_access, uint32_t value); +EXP int CALL set_slow_time(ryzen_access, uint32_t value); +EXP int CALL set_stapm_time(ryzen_access, uint32_t value); +EXP int CALL set_tctl_temp(ryzen_access, uint32_t value); +EXP int CALL set_vrm_current(ryzen_access, uint32_t value); +EXP int CALL set_vrmsoc_current(ryzen_access, uint32_t value); +EXP int CALL set_vrmgfx_current(ryzen_access, uint32_t value); +EXP int CALL set_vrmcvip_current(ryzen_access, uint32_t value); +EXP int CALL set_vrmmax_current(ryzen_access, uint32_t value); +EXP int CALL set_vrmgfxmax_current(ryzen_access, uint32_t value); +EXP int CALL set_vrmsocmax_current(ryzen_access, uint32_t value); +EXP int CALL set_psi0_current(ryzen_access, uint32_t value); +EXP int CALL set_psi3cpu_current(ryzen_access, uint32_t value); +EXP int CALL set_psi0soc_current(ryzen_access, uint32_t value); +EXP int CALL set_psi3gfx_current(ryzen_access, uint32_t value); +EXP int CALL set_max_gfxclk_freq(ryzen_access, uint32_t value); +EXP int CALL set_min_gfxclk_freq(ryzen_access, uint32_t value); +EXP int CALL set_max_socclk_freq(ryzen_access, uint32_t value); +EXP int CALL set_min_socclk_freq(ryzen_access, uint32_t value); +EXP int CALL set_max_fclk_freq(ryzen_access, uint32_t value); +EXP int CALL set_min_fclk_freq(ryzen_access, uint32_t value); +EXP int CALL set_max_vcn(ryzen_access, uint32_t value); +EXP int CALL set_min_vcn(ryzen_access, uint32_t value); +EXP int CALL set_max_lclk(ryzen_access, uint32_t value); +EXP int CALL set_min_lclk(ryzen_access, uint32_t value); +EXP int CALL set_prochot_deassertion_ramp(ryzen_access ry, uint32_t value); +EXP int CALL set_apu_skin_temp_limit(ryzen_access, uint32_t value); +EXP int CALL set_dgpu_skin_temp_limit(ryzen_access, uint32_t value); +EXP int CALL set_apu_slow_limit(ryzen_access, uint32_t value); +EXP int CALL set_skin_temp_power_limit(ryzen_access ry, uint32_t value); +EXP int CALL set_gfx_clk(ryzen_access ry, uint32_t value); +EXP int CALL set_oc_clk(ryzen_access ry, uint32_t value); +EXP int CALL set_per_core_oc_clk(ryzen_access ry, uint32_t value); +EXP int CALL set_oc_volt(ryzen_access ry, uint32_t value); +EXP int CALL set_disable_oc(ryzen_access ry); +EXP int CALL set_enable_oc(ryzen_access ry); +EXP int CALL set_power_saving(ryzen_access ry); +EXP int CALL set_max_performance(ryzen_access ry); +EXP int CALL set_coall(ryzen_access ry, uint32_t value); +EXP int CALL set_coper(ryzen_access ry, uint32_t value); +EXP int CALL set_cogfx(ryzen_access ry, uint32_t value); + +EXP float CALL get_stapm_limit(ryzen_access ry); +EXP float CALL get_stapm_value(ryzen_access ry); +EXP float CALL get_fast_limit(ryzen_access ry); +EXP float CALL get_fast_value(ryzen_access ry); +EXP float CALL get_slow_limit(ryzen_access ry); +EXP float CALL get_slow_value(ryzen_access ry); +EXP float CALL get_apu_slow_limit(ryzen_access ry); +EXP float CALL get_apu_slow_value(ryzen_access ry); +EXP float CALL get_vrm_current(ryzen_access ry); +EXP float CALL get_vrm_current_value(ryzen_access ry); +EXP float CALL get_vrmsoc_current(ryzen_access ry); +EXP float CALL get_vrmsoc_current_value(ryzen_access ry); +EXP float CALL get_vrmmax_current(ryzen_access ry); +EXP float CALL get_vrmmax_current_value(ryzen_access ry); +EXP float CALL get_vrmsocmax_current(ryzen_access ry); +EXP float CALL get_vrmsocmax_current_value(ryzen_access ry); +EXP float CALL get_tctl_temp(ryzen_access ry); +EXP float CALL get_tctl_temp_value(ryzen_access ry); +EXP float CALL get_apu_skin_temp_limit(ryzen_access ry); +EXP float CALL get_apu_skin_temp_value(ryzen_access ry); +EXP float CALL get_dgpu_skin_temp_limit(ryzen_access ry); +EXP float CALL get_dgpu_skin_temp_value(ryzen_access ry); +EXP float CALL get_psi0_current(ryzen_access ry); +EXP float CALL get_psi0soc_current(ryzen_access ry); +EXP float CALL get_stapm_time(ryzen_access ry); +EXP float CALL get_slow_time(ryzen_access ry); +EXP float CALL get_cclk_setpoint(ryzen_access ry); +EXP float CALL get_cclk_busy_value(ryzen_access ry); + +EXP float CALL get_core_clk(ryzen_access ry, uint32_t value); +EXP float CALL get_core_volt(ryzen_access ry, uint32_t value); +EXP float CALL get_core_power(ryzen_access ry, uint32_t value); +EXP float CALL get_core_temp(ryzen_access ry, uint32_t value); + +EXP float CALL get_l3_clk(ryzen_access ry); +EXP float CALL get_l3_logic(ryzen_access ry); +EXP float CALL get_l3_vddm(ryzen_access ry); +EXP float CALL get_l3_temp(ryzen_access ry); + +EXP float CALL get_gfx_clk(ryzen_access ry); +EXP float CALL get_gfx_temp(ryzen_access ry); +EXP float CALL get_gfx_volt(ryzen_access ry); + +EXP float CALL get_mem_clk(ryzen_access ry); +EXP float CALL get_fclk(ryzen_access ry); + +EXP float CALL get_soc_power(ryzen_access ry); +EXP float CALL get_soc_volt(ryzen_access ry); + +EXP float CALL get_socket_power(ryzen_access ry); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/os/licenses/ryzenadj/src/lib/ryzenadj_priv.h b/os/licenses/ryzenadj/src/lib/ryzenadj_priv.h new file mode 100644 index 00000000..3e74de6b --- /dev/null +++ b/os/licenses/ryzenadj/src/lib/ryzenadj_priv.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: LGPL */ +/* Copyright (C) 2020 Jiaxun Yang */ +/* RyzenAdj Private stuff */ +/* Do not include this file! */ + +#ifndef RYZENADJ_PRIV_H +#define RYZENADJ_PRIV_H + +#include + +#include "nb_smu_ops.h" + +struct _ryzen_access { + os_access_obj_t *os_access; + smu_t mp1_smu; + smu_t psmu; + enum ryzen_family family; + int bios_if_ver; + uintptr_t table_addr; + uint32_t table_ver; + size_t table_size; + float *table_values; +}; + +enum ryzen_family cpuid_get_family(); + +#endif diff --git a/os/licenses/ryzenadj/src/lib/win32/OlsApi.h b/os/licenses/ryzenadj/src/lib/win32/OlsApi.h new file mode 100644 index 00000000..9f11e6aa --- /dev/null +++ b/os/licenses/ryzenadj/src/lib/win32/OlsApi.h @@ -0,0 +1,580 @@ +//----------------------------------------------------------------------------- +// Author : hiyohiyo +// Mail : hiyohiyo@crystalmark.info +// Web : http://openlibsys.org/ +// License : The modified BSD license +// +// Copyright 2007-2009 OpenLibSys.org. All rights reserved. +//----------------------------------------------------------------------------- +// for WinRing0 1.3.x + +#pragma once + +/****************************************************************************** +** +** DLL Information +** +******************************************************************************/ + +//----------------------------------------------------------------------------- +// GetDllStatus +//----------------------------------------------------------------------------- +DWORD // DLL Status, defined OLS_DLL_**** +WINAPI GetDllStatus(); + +//----------------------------------------------------------------------------- +// GetDllVersion +//----------------------------------------------------------------------------- +DWORD // DLL Version, defined OLS_VERSION +WINAPI GetDllVersion( + PBYTE major, // major version + PBYTE minor, // minor version + PBYTE revision, // revision + PBYTE release // release/build +); + +//----------------------------------------------------------------------------- +// GetDriverVersion +//----------------------------------------------------------------------------- +DWORD // Device Driver Version, defined OLS_DRIVER_VERSION +WINAPI GetDriverVersion( + PBYTE major, // major version + PBYTE minor, // minor version + PBYTE revision, // revision + PBYTE release // release/build +); + +//----------------------------------------------------------------------------- +// GetDriverType +//----------------------------------------------------------------------------- +DWORD // Device Driver Type, defined OLS_DRIVER_TYPE_**** +WINAPI GetDriverType(); + +//----------------------------------------------------------------------------- +// InitializeOls +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI InitializeOls(); + +//----------------------------------------------------------------------------- +// DeinitializeOls +//----------------------------------------------------------------------------- +VOID WINAPI DeinitializeOls(); + +/****************************************************************************** +** +** CPU +** +******************************************************************************/ + +//----------------------------------------------------------------------------- +// IsCpuid +//----------------------------------------------------------------------------- +BOOL // TRUE: support CPUID instruction, FALSE: not support CPUID instruction +WINAPI IsCpuid(); + +//----------------------------------------------------------------------------- +// IsMsr +//----------------------------------------------------------------------------- +BOOL // TRUE: support MSR(Model-Specific Register), FALSE: not support MSR +WINAPI IsMsr(); + +//----------------------------------------------------------------------------- +// IsTsc +//----------------------------------------------------------------------------- +BOOL // TRUE: support TSC(Time Stamp Counter), FALSE: not support TSC +WINAPI IsTsc(); + +//----------------------------------------------------------------------------- +// Rdmsr +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI Rdmsr( + DWORD index, // MSR index + PDWORD eax, // bit 0-31 + PDWORD edx // bit 32-63 +); + +//----------------------------------------------------------------------------- +// RdmsrTx +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI RdmsrTx( + DWORD index, // MSR index + PDWORD eax, // bit 0-31 + PDWORD edx, // bit 32-63 + DWORD_PTR threadAffinityMask +); + +//----------------------------------------------------------------------------- +// RdmsrPx +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI RdmsrPx( + DWORD index, // MSR index + PDWORD eax, // bit 0-31 + PDWORD edx, // bit 32-63 + DWORD_PTR processAffinityMask +); + +//----------------------------------------------------------------------------- +// Wrmsr +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI Wrmsr( + DWORD index, // MSR index + DWORD eax, // bit 0-31 + DWORD edx // bit 32-63 +); + +//----------------------------------------------------------------------------- +// WrmsrTx +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI WrmsrTx( + DWORD index, // MSR index + DWORD eax, // bit 0-31 + DWORD edx, // bit 32-63 + DWORD_PTR threadAffinityMask +); + +//----------------------------------------------------------------------------- +// WrmsrPx +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI WrmsrPx( + DWORD index, // MSR index + DWORD eax, // bit 0-31 + DWORD edx, // bit 32-63 + DWORD_PTR processAffinityMask +); + +//----------------------------------------------------------------------------- +// Rdpmc +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI Rdpmc( + DWORD index, // PMC index + PDWORD eax, // bit 0-31 + PDWORD edx // bit 32-63 +); + +//----------------------------------------------------------------------------- +// RdmsrTx +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI RdpmcTx( + DWORD index, // PMC index + PDWORD eax, // bit 0-31 + PDWORD edx, // bit 32-63 + DWORD_PTR threadAffinityMask +); + +//----------------------------------------------------------------------------- +// RdmsrPx +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI RdpmcPx( + DWORD index, // PMC index + PDWORD eax, // bit 0-31 + PDWORD edx, // bit 32-63 + DWORD_PTR processAffinityMask +); + +//----------------------------------------------------------------------------- +// Cpuid +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI Cpuid( + DWORD index, // CPUID index + PDWORD eax, + PDWORD ebx, + PDWORD ecx, + PDWORD edx +); + +//----------------------------------------------------------------------------- +// CpuidTx +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI CpuidTx( + DWORD index, // CPUID index + PDWORD eax, + PDWORD ebx, + PDWORD ecx, + PDWORD edx, + DWORD_PTR threadAffinityMask +); + +//----------------------------------------------------------------------------- +// CpuidPx +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI CpuidPx( + DWORD index, // CPUID index + PDWORD eax, + PDWORD ebx, + PDWORD ecx, + PDWORD edx, + DWORD_PTR processAffinityMask +); + +//----------------------------------------------------------------------------- +// Rdtsc +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI Rdtsc( + PDWORD eax, // bit 0-31 + PDWORD edx // bit 32-63 +); + +//----------------------------------------------------------------------------- +// RdmsrTx +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI RdtscTx( + PDWORD eax, // bit 0-31 + PDWORD edx, // bit 32-63 + DWORD_PTR threadAffinityMask +); + +//----------------------------------------------------------------------------- +// RdmsrPx +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI RdtscPx( + PDWORD eax, // bit 0-31 + PDWORD edx, // bit 32-63 + DWORD_PTR processAffinityMask +); + +//----------------------------------------------------------------------------- +// Hlt +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI Hlt(); + +//----------------------------------------------------------------------------- +// HltTx +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI HltTx( + DWORD_PTR threadAffinityMask +); + +//----------------------------------------------------------------------------- +// HltPx +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI HltTx( + DWORD_PTR processAffinityMask +); + +/****************************************************************************** +** +** I/O +** +******************************************************************************/ + +//----------------------------------------------------------------------------- +// ReadIoPortByte +//----------------------------------------------------------------------------- +BYTE // Read Value +WINAPI ReadIoPortByte( + WORD port // I/O port address +); + +//----------------------------------------------------------------------------- +// ReadIoPortWord +//----------------------------------------------------------------------------- +WORD // Read Value +WINAPI ReadIoPortWord( + WORD port // I/O port address +); + +//----------------------------------------------------------------------------- +// ReadIoPortDword +//----------------------------------------------------------------------------- +DWORD // Read Value +WINAPI ReadIoPortDword( + WORD port // I/O port address +); + +//----------------------------------------------------------------------------- +// ReadIoPortByteEx +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI ReadIoPortByteEx( + WORD port, // I/O port address + PBYTE value // Read Value +); +//----------------------------------------------------------------------------- +// ReadIoPortWordEx +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI ReadIoPortWordEx( + WORD port, // I/O port address + PWORD value // Read Value +); +//----------------------------------------------------------------------------- +// ReadIoPortDwordEx +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI ReadIoPortDwordEx( + WORD port, // I/O port address + PDWORD value // Read Value +); + +//----------------------------------------------------------------------------- +// WriteIoPortByte +//----------------------------------------------------------------------------- +VOID +WINAPI WriteIoPortByte( + WORD port, // I/O port address + BYTE value // Write Value +); + +//----------------------------------------------------------------------------- +// WriteIoPortDword +//----------------------------------------------------------------------------- +VOID +WINAPI WriteIoPortDword( + WORD port, // I/O port address + DWORD value // Write Value +); + + +//----------------------------------------------------------------------------- +// WriteIoPortWord +//----------------------------------------------------------------------------- +VOID +WINAPI WriteIoPortWord( + WORD port, // I/O port address + WORD value // Write Value +); + +//----------------------------------------------------------------------------- +// WriteIoPortByteEx +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI WriteIoPortByteEx( + WORD port, // I/O port address + BYTE value // Write Value +); + +//----------------------------------------------------------------------------- +// WriteIoPortWordEx +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI WriteIoPortWordEx( + WORD port, // I/O port address + WORD value // Write Value +); + + +//----------------------------------------------------------------------------- +// WriteIoPortDwordEx +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI WriteIoPortDwordEx( + WORD port, // I/O port address + DWORD value // Write Value +); + +/****************************************************************************** +** +** PCI +** +******************************************************************************/ +// pciAddress +// 0- 2: Function Number +// 3- 7: Device Number +// 8-15: PCI Bus Number +// 16-31: Reserved +// 0xFFFFFFFF : Error + +//----------------------------------------------------------------------------- +// SetPciMaxBusNo +//----------------------------------------------------------------------------- +VOID +WINAPI SetPciMaxBusIndex( + BYTE max // Max PCI Bus to Scan +); + +//----------------------------------------------------------------------------- +// ReadPciConfigByte +//----------------------------------------------------------------------------- +BYTE // Read Value +WINAPI ReadPciConfigByte( + DWORD pciAddress, // PCI Device Address + BYTE regAddress // Configuration Address 0-255 +); + +//----------------------------------------------------------------------------- +// ReadPciConfigWord +//----------------------------------------------------------------------------- +WORD // Read Value +WINAPI ReadPciConfigWord( + DWORD pciAddress, // PCI Device Address + BYTE regAddress // Configuration Address 0-255 +); + +//----------------------------------------------------------------------------- +// ReadPciConfigDword +//----------------------------------------------------------------------------- +DWORD // Read Value +WINAPI ReadPciConfigDword( + DWORD pciAddress, // PCI Device Address + BYTE regAddress // Configuration Address 0-255 +); + +//----------------------------------------------------------------------------- +// ReadPciConfigByteEx +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI ReadPciConfigByteEx( + DWORD pciAddress, // PCI Device Address + DWORD regAddress, // Configuration Address 0-whatever + PBYTE value // Read Value +); + +//----------------------------------------------------------------------------- +// ReadPciConfigWordEx +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI ReadPciConfigWordEx( + DWORD pciAddress, // PCI Device Address + DWORD regAddress, // Configuration Address 0-whatever + PWORD value // Read Value +); + +//----------------------------------------------------------------------------- +// ReadPciConfigDwordEx +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI ReadPciConfigDwordEx( + DWORD pciAddress, // PCI Device Address + DWORD regAddress, // Configuration Address 0-whatever + PDWORD value // Read Value +); + +//----------------------------------------------------------------------------- +// WritePciConfigByte +//----------------------------------------------------------------------------- +VOID +WINAPI WritePciConfigByte( + DWORD pciAddress, // PCI Device Address + BYTE regAddress, // Configuration Address 0-255 + BYTE value // Write Value +); + +//----------------------------------------------------------------------------- +// WritePciConfigWord +//----------------------------------------------------------------------------- +VOID +WINAPI WritePciConfigWord( + DWORD pciAddress, // PCI Device Address + BYTE regAddress, // Configuration Address 0-255 + WORD value // Write Value +); + +//----------------------------------------------------------------------------- +// WritePciConfigDword +//----------------------------------------------------------------------------- +VOID +WINAPI WritePciConfigDword( + DWORD pciAddress, // PCI Device Address + BYTE regAddress, // Configuration Address 0-255 + DWORD value // Write Value +); + +//----------------------------------------------------------------------------- +// WritePciConfigByteEx +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI WritePciConfigByteEx( + DWORD pciAddress, // PCI Device Address + DWORD regAddress, // Configuration Address 0-whatever + BYTE value // Write Value +); + +//----------------------------------------------------------------------------- +// WritePciConfigWordEx +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI WritePciConfigWordEx( + DWORD pciAddress, // PCI Device Address + DWORD regAddress, // Configuration Address 0-whatever + WORD value // Write Value +); + +//----------------------------------------------------------------------------- +// WritePciConfigDwordEx +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI WritePciConfigDwordEx( + DWORD pciAddress, // PCI Device Address + DWORD regAddress, // Configuration Address 0-whatever + DWORD value // Write Value +); + +//----------------------------------------------------------------------------- +// FindPciDeviceById +//----------------------------------------------------------------------------- +DWORD // pciAddress, 0xFFFFFFFF: failure +WINAPI FindPciDeviceById( + WORD vendorId, // Vendor ID + WORD deviceId, // Device ID + BYTE index // Index +); + +//----------------------------------------------------------------------------- +// FindPciDeviceByClass +//----------------------------------------------------------------------------- +DWORD // pciAddress, 0xFFFFFFFF: failure +WINAPI FindPciDeviceByClass( + BYTE baseClass, // Base Class + BYTE subClass, // Sub Class + BYTE programIf, // Program Interface + BYTE index // Index +); + +/****************************************************************************** +** +** Memory (Special API) +** +******************************************************************************/ + +#ifdef _PHYSICAL_MEMORY_SUPPORT +//----------------------------------------------------------------------------- +// ReadDmiMemory +//----------------------------------------------------------------------------- +DWORD // Read size(byte), 0: failure +WINAPI ReadDmiMemory( + PBYTE buffer, // Buffer + DWORD count, // Count + DWORD unitSize // Unit Size (BYTE, WORD, DWORD) +); + +//----------------------------------------------------------------------------- +// ReadPhysicalMemory +//----------------------------------------------------------------------------- +DWORD // Read size(byte), 0: failure +WINAPI ReadPhysicalMemory( + DWORD_PTR address, // Physical Memory Address + PBYTE buffer, // Buffer + DWORD count, // Count + DWORD unitSize // Unit Size (BYTE, WORD, DWORD) +); + +//----------------------------------------------------------------------------- +// WritePhysicalMemory +//----------------------------------------------------------------------------- +DWORD // Write size(byte), 0: failure +WINAPI WritePhysicalMemory( + DWORD_PTR address, // Physical Memory Address + PBYTE buffer, // Buffer + DWORD count, // Count + DWORD unitSize // Unit Size (BYTE, WORD, DWORD) +); +#endif \ No newline at end of file diff --git a/os/licenses/ryzenadj/src/lib/win32/OlsDef.h b/os/licenses/ryzenadj/src/lib/win32/OlsDef.h new file mode 100644 index 00000000..f6f57028 --- /dev/null +++ b/os/licenses/ryzenadj/src/lib/win32/OlsDef.h @@ -0,0 +1,63 @@ +//----------------------------------------------------------------------------- +// Author : hiyohiyo +// Mail : hiyohiyo@crystalmark.info +// Web : http://openlibsys.org/ +// License : The modified BSD license +// +// Copyright 2007 OpenLibSys.org. All rights reserved. +//----------------------------------------------------------------------------- + +#pragma once + +//----------------------------------------------------------------------------- +// +// DLL Status Code +// +//----------------------------------------------------------------------------- + +#define OLS_DLL_NO_ERROR 0 +#define OLS_DLL_UNSUPPORTED_PLATFORM 1 +#define OLS_DLL_DRIVER_NOT_LOADED 2 +#define OLS_DLL_DRIVER_NOT_FOUND 3 +#define OLS_DLL_DRIVER_UNLOADED 4 +#define OLS_DLL_DRIVER_NOT_LOADED_ON_NETWORK 5 +#define OLS_DLL_UNKNOWN_ERROR 9 + +//----------------------------------------------------------------------------- +// +// Driver Type +// +//----------------------------------------------------------------------------- + +#define OLS_DRIVER_TYPE_UNKNOWN 0 +#define OLS_DRIVER_TYPE_WIN_9X 1 +#define OLS_DRIVER_TYPE_WIN_NT 2 +#define OLS_DRIVER_TYPE_WIN_NT4 3 // Obsolete +#define OLS_DRIVER_TYPE_WIN_NT_X64 4 +#define OLS_DRIVER_TYPE_WIN_NT_IA64 5 // Reserved + +//----------------------------------------------------------------------------- +// +// PCI Error Code +// +//----------------------------------------------------------------------------- + +#define OLS_ERROR_PCI_BUS_NOT_EXIST (0xE0000001L) +#define OLS_ERROR_PCI_NO_DEVICE (0xE0000002L) +#define OLS_ERROR_PCI_WRITE_CONFIG (0xE0000003L) +#define OLS_ERROR_PCI_READ_CONFIG (0xE0000004L) + +//----------------------------------------------------------------------------- +// +// Support Macros +// +//----------------------------------------------------------------------------- + +// Bus Number, Device Number and Function Number to PCI Device Address +#define PciBusDevFunc(Bus, Dev, Func) ((Bus&0xFF)<<8) | ((Dev&0x1F)<<3) | (Func&7) +// PCI Device Address to Bus Number +#define PciGetBus(address) ((address>>8) & 0xFF) +// PCI Device Address to Device Number +#define PciGetDev(address) ((address>>3) & 0x1F) +// PCI Device Address to Function Number +#define PciGetFunc(address) (address&7) diff --git a/os/licenses/ryzenadj/src/lib/win32/osdep_win32.cpp b/os/licenses/ryzenadj/src/lib/win32/osdep_win32.cpp new file mode 100644 index 00000000..ae6317db --- /dev/null +++ b/os/licenses/ryzenadj/src/lib/win32/osdep_win32.cpp @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: LGPL +/* Copyright (C) 2018-2019 Jiaxun Yang */ +/* Access PCI Config Space - winring0 */ +extern "C" { +#include "../nb_smu_ops.h" +} +#include + +#include "OlsApi.h" +#include "OlsDef.h" + +typedef BOOL (__stdcall *lpIsInpOutDriverOpen)(void); +typedef PBYTE (__stdcall *lpMapPhysToLin)(uintptr_t pbPhysAddr, size_t dwPhysSize, HANDLE *pPhysicalMemoryHandle); +typedef BOOL (__stdcall *lpUnmapPhysicalMemory)(HANDLE PhysicalMemoryHandle, uintptr_t pbLinAddr); +typedef BOOL (__stdcall *lpGetPhysLong)(uintptr_t pbPhysAddr, uint32_t *pdwPhysVal); +lpIsInpOutDriverOpen gfpIsInpOutDriverOpen; +lpGetPhysLong gfpGetPhysLong; +lpMapPhysToLin gfpMapPhysToLin; +lpUnmapPhysicalMemory gfpUnmapPhysicalMemory; +uint32_t *pdwLinAddr; +HANDLE physicalMemoryHandle; + +#ifdef __cplusplus +extern "C" { +#endif + +os_access_obj_t *init_os_access_obj() { + InitializeOls(); + + const DWORD dllStatus = GetDllStatus(); + + switch (dllStatus) { + case OLS_DLL_NO_ERROR: { + os_access_obj_t *obj = static_cast(std::malloc(sizeof(os_access_obj_t))); + + if (obj == NULL) + return NULL; + + memset(obj, 0, sizeof(os_access_obj_t)); + return obj; + } + case OLS_DLL_UNSUPPORTED_PLATFORM: + DBG("WinRing0 Err: Unsupported Platform\n"); + break; + case OLS_DLL_DRIVER_NOT_LOADED: + DBG("WinRing0 Err: Driver not loaded\n"); + break; + case OLS_DLL_DRIVER_NOT_FOUND: + DBG("WinRing0 Err: Driver not found\n"); + break; + case OLS_DLL_DRIVER_UNLOADED: + DBG("WinRing0 Err: Driver unloaded\n"); + break; + case OLS_DLL_DRIVER_NOT_LOADED_ON_NETWORK: + DBG("WinRing0 Err: Driver not loaded on network\n"); + break; + case OLS_DLL_UNKNOWN_ERROR: + DBG("WinRing0 Err: unknown error\n"); + break; + default: + DBG("WinRing0 Err: unknown status code %lu\n", dllStatus); + break; + } + + return NULL; +} + +void free_os_access_obj(os_access_obj_t *obj) { + if (obj == NULL) + return; + + DeinitializeOls(); + gfpUnmapPhysicalMemory(physicalMemoryHandle, *pdwLinAddr); + FreeLibrary(obj->inpoutDll); + free(obj); +} + +uint32_t smn_reg_read(const os_access_obj_t *obj, uint32_t addr) { + WritePciConfigDword(obj->pci_address, NB_PCI_REG_ADDR_ADDR, addr & (~0x3)); + return ReadPciConfigDword(obj->pci_address, NB_PCI_REG_DATA_ADDR); +} + +void smn_reg_write(const os_access_obj_t *obj, uint32_t addr, uint32_t data) { + WritePciConfigDword(obj->pci_address, NB_PCI_REG_ADDR_ADDR, addr); + WritePciConfigDword(obj->pci_address, NB_PCI_REG_DATA_ADDR, data); +} + +int init_mem_obj(os_access_obj_t *os_access, uintptr_t physAddr) { + HINSTANCE hInpOutDll = LoadLibrary ("inpoutx64.DLL"); + + if (hInpOutDll == NULL) { + DBG("Could not load inpoutx64.DLL\n"); + return -1; + } + + gfpIsInpOutDriverOpen = (lpIsInpOutDriverOpen)GetProcAddress(hInpOutDll, "IsInpOutDriverOpen"); + gfpGetPhysLong = (lpGetPhysLong)GetProcAddress(hInpOutDll, "GetPhysLong"); + gfpMapPhysToLin = (lpMapPhysToLin)GetProcAddress(hInpOutDll, "MapPhysToLin"); + gfpUnmapPhysicalMemory = (lpUnmapPhysicalMemory)GetProcAddress(hInpOutDll, "UnmapPhysicalMemory"); + + if (!gfpIsInpOutDriverOpen()) { + DBG("Could not open inpoutx64 driver\n"); + return -1; + } + + pdwLinAddr = (uint32_t*)gfpMapPhysToLin(physAddr, 0x1000, &physicalMemoryHandle); + if (pdwLinAddr == NULL) { + DBG("failed to map memory\n"); + return -1; + } + + os_access->inpoutDll = hInpOutDll; + + return 0; +} + +int copy_pm_table(const os_access_obj_t *obj, void *buffer, const size_t size) { + memcpy(buffer, pdwLinAddr, size); + return 0; +} + +int compare_pm_table(const void *buffer, const size_t size) { + return memcmp(buffer, pdwLinAddr, size); +} + +bool is_using_smu_driver() { + return false; +} + +#ifdef __cplusplus +} +#endif diff --git a/os/licenses/ryzenadj/src/main.c b/os/licenses/ryzenadj/src/main.c new file mode 100644 index 00000000..fed00193 --- /dev/null +++ b/os/licenses/ryzenadj/src/main.c @@ -0,0 +1,346 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2018-2019 Jiaxun Yang */ +/* Ryzen NB SMU Service Request Tool */ + +#include +#include + +#include "lib/ryzenadj.h" +#include "argparse.h" + +#define STRINGIFY2(X) #X +#define STRINGIFY(X) STRINGIFY2(X) + +#define _do_adjust(ARG) \ +do { \ + /* ignore max unsigned integer values */ \ + if (ARG != -1) { \ + int adjerr = set_##ARG(ry, ARG); \ + if (!adjerr){ \ + any_adjust_applied = 1; \ + printf("Successfully set " STRINGIFY(ARG) " to %u\n", ARG); \ + } else if (adjerr == ADJ_ERR_FAM_UNSUPPORTED) { \ + printf("set_" STRINGIFY(ARG) " is not supported on this family\n"); \ + err = -1; \ + } else if (adjerr == ADJ_ERR_SMU_UNSUPPORTED) { \ + printf("set_" STRINGIFY(ARG) " is not supported on this SMU\n"); \ + err = -1; \ + } else if (adjerr == ADJ_ERR_SMU_REJECTED) { \ + printf("set_" STRINGIFY(ARG) " is rejected by SMU\n"); \ + err = -1; \ + } else { \ + printf("Failed to set" STRINGIFY(ARG) " \n"); \ + err = -1; \ + } \ + } \ +} while(0); + +#define _do_enable(ARG) \ +do { \ + if (ARG) { \ + int adjerr = set_##ARG(ry); \ + if (!adjerr){ \ + any_adjust_applied = 1; \ + printf("Successfully enable " STRINGIFY(ARG) "\n"); \ + } else if (adjerr == ADJ_ERR_FAM_UNSUPPORTED) { \ + printf("set_" STRINGIFY(ARG) " is not supported on this family\n"); \ + err = -1; \ + } else if (adjerr == ADJ_ERR_SMU_UNSUPPORTED) { \ + printf("set_" STRINGIFY(ARG) " is not supported on this SMU\n"); \ + err = -1; \ + } else if (adjerr == ADJ_ERR_SMU_REJECTED) { \ + printf("set_" STRINGIFY(ARG) " is rejected by SMU\n"); \ + err = -1; \ + } else { \ + printf("Failed to set" STRINGIFY(ARG) " \n"); \ + err = -1; \ + } \ + } \ +} while(0); + +static const char *const usage[] = { + "ryzenadj [options]", + NULL, +}; + +static const char *family_name(enum ryzen_family fam) +{ + switch (fam) { + case FAM_RAVEN: return "Raven"; + case FAM_PICASSO: return "Picasso"; + case FAM_RENOIR: return "Renoir"; + case FAM_CEZANNE: return "Cezanne"; + case FAM_DALI: return "Dali"; + case FAM_LUCIENNE: return "Lucienne"; + case FAM_VANGOGH: return "Vangogh"; + case FAM_REMBRANDT: return "Rembrandt"; + case FAM_PHOENIX: return "Phoenix Point"; + case FAM_HAWKPOINT: return "Hawk Point"; + case FAM_DRAGONRANGE: return "Dragon Range"; + case FAM_KRACKANPOINT: return "Krackan Point"; + case FAM_STRIXPOINT: return "Strix Point"; + case FAM_STRIXHALO: return "Strix Halo"; + case FAM_FIRERANGE: return "Fire Range"; + default: break; + } + + return "Unknown"; +} + +static void show_info_header(ryzen_access ry) +{ + printf("CPU Family: %s\n", family_name(get_cpu_family(ry))); + printf("SMU BIOS Interface Version: %d\n", get_bios_if_ver(ry)); + printf("Version: v" STRINGIFY(RYZENADJ_REVISION_VER) "." STRINGIFY(RYZENADJ_MAJOR_VER) "." STRINGIFY(RYZENADJ_MINIOR_VER) " \n"); +} + +static void show_info_table(ryzen_access ry) +{ + printf("PM Table Version: %x\n", get_table_ver(ry)); + + //get refresh table after adjust + int errorcode = refresh_table(ry); + if(errorcode){ + printf("Unable to refresh power metric table: %d\n", errorcode); + return; + } + + //print table in github markdown + printf("| Name | Value | Parameter |\n"); + printf("|---------------------|-----------|--------------------|\n"); + char tableFormat[] = "| %-19s | %9.3lf | %-18s |\n"; + printf(tableFormat, "STAPM LIMIT", get_stapm_limit(ry), "stapm-limit"); + printf(tableFormat, "STAPM VALUE", get_stapm_value(ry), ""); + printf(tableFormat, "PPT LIMIT FAST", get_fast_limit(ry), "fast-limit"); + printf(tableFormat, "PPT VALUE FAST", get_fast_value(ry), ""); + printf(tableFormat, "PPT LIMIT SLOW", get_slow_limit(ry), "slow-limit"); + printf(tableFormat, "PPT VALUE SLOW", get_slow_value(ry), ""); + printf(tableFormat, "StapmTimeConst", get_stapm_time(ry), "stapm-time"); + printf(tableFormat, "SlowPPTTimeConst", get_slow_time(ry), "slow-time"); + printf(tableFormat, "PPT LIMIT APU", get_apu_slow_limit(ry), "apu-slow-limit"); + printf(tableFormat, "PPT VALUE APU", get_apu_slow_value(ry), ""); + printf(tableFormat, "TDC LIMIT VDD", get_vrm_current(ry), "vrm-current"); + printf(tableFormat, "TDC VALUE VDD", get_vrm_current_value(ry), ""); + printf(tableFormat, "TDC LIMIT SOC", get_vrmsoc_current(ry), "vrmsoc-current"); + printf(tableFormat, "TDC VALUE SOC", get_vrmsoc_current_value(ry), ""); + printf(tableFormat, "EDC LIMIT VDD", get_vrmmax_current(ry), "vrmmax-current"); + printf(tableFormat, "EDC VALUE VDD", get_vrmmax_current_value(ry), ""); + printf(tableFormat, "EDC LIMIT SOC", get_vrmsocmax_current(ry), "vrmsocmax-current"); + printf(tableFormat, "EDC VALUE SOC", get_vrmsocmax_current_value(ry), ""); + printf(tableFormat, "THM LIMIT CORE", get_tctl_temp(ry), "tctl-temp"); + printf(tableFormat, "THM VALUE CORE", get_tctl_temp_value(ry), ""); + printf(tableFormat, "STT LIMIT APU", get_apu_skin_temp_limit(ry), "apu-skin-temp"); + printf(tableFormat, "STT VALUE APU", get_apu_skin_temp_value(ry), ""); + printf(tableFormat, "STT LIMIT dGPU", get_dgpu_skin_temp_limit(ry), "dgpu-skin-temp"); + printf(tableFormat, "STT VALUE dGPU", get_dgpu_skin_temp_value(ry), ""); + printf(tableFormat, "CCLK Boost SETPOINT", get_cclk_setpoint(ry), "power-saving /"); + printf(tableFormat, "CCLK BUSY VALUE", get_cclk_busy_value(ry), "max-performance"); +} + +static void show_table_dump(ryzen_access ry, int any_adjust_applied) +{ + size_t index, table_size; + uint32_t *table_data_copy; + float *current_table_values, *old_table_values; + + printf("PM Table Dump of Version: %x\n", get_table_ver(ry)); + table_size = get_table_size(ry); + + current_table_values = get_table_values(ry); + table_data_copy = malloc(table_size); + memcpy(table_data_copy, current_table_values, table_size); + + if(any_adjust_applied) + { + //copy old values before refresh + old_table_values = malloc(table_size); + memcpy(old_table_values, table_data_copy, table_size); + + int errorcode = refresh_table(ry); + if(errorcode){ + printf("Unable to refresh power metric table: %d\n", errorcode); + } + + //print table in github markdown + printf("| Offset | Data | Value | After Adjust |\n"); + printf("|--------|------------|-----------|--------------|\n"); + char tableFormat[] = "| 0x%04X | 0x%08X | %9.3lf | %12.3lf |\n"; + for(index = 0; index < table_size / 4; index++) + { + printf(tableFormat, index * 4, table_data_copy[index], old_table_values[index], current_table_values[index]); + } + + free(old_table_values); + } + else + { + //print table in github markdown + printf("| Offset | Data | Value |\n"); + printf("|--------|------------|-----------|\n"); + char tableFormat[] = "| 0x%04X | 0x%08X | %9.3lf |\n"; + for(index = 0; index < table_size / 4; index++) + { + printf(tableFormat, index * 4, table_data_copy[index], current_table_values[index]); + } + } + + free(table_data_copy); + //don't free current_table_values because this would deinitialize our table +} + + +int main(int argc, const char **argv) +{ + ryzen_access ry; + int err = 0; + + int info = 0, dump_table = 0, any_adjust_applied = 0; + int power_saving = 0, max_performance = 0, enable_oc = 0x0, disable_oc = 0x0; + //init unsigned types with max value because we treat max value as unset + uint32_t stapm_limit = -1, fast_limit = -1, slow_limit = -1, slow_time = -1, stapm_time = -1, tctl_temp = -1; + uint32_t vrm_current = -1, vrmsoc_current = -1, vrmmax_current = -1, vrmsocmax_current = -1, psi0_current = -1, psi0soc_current = -1; + uint32_t vrmgfx_current = -1, vrmcvip_current = -1, vrmgfxmax_current = -1, psi3cpu_current = -1, psi3gfx_current = -1; + uint32_t max_socclk_freq = -1, min_socclk_freq = -1, max_fclk_freq = -1, min_fclk_freq = -1, max_vcn = -1, min_vcn = -1, max_lclk = -1, min_lclk = -1; + uint32_t max_gfxclk_freq = -1, min_gfxclk_freq = -1, prochot_deassertion_ramp = -1, apu_skin_temp_limit = -1, dgpu_skin_temp_limit = -1, apu_slow_limit = -1; + uint32_t skin_temp_power_limit = -1; + uint32_t gfx_clk = -1, oc_clk = -1, oc_volt = -1, coall = -1, coper = -1, cogfx = -1; + + //create structure for parsing + struct argparse_option options[] = { + OPT_HELP(), + OPT_GROUP("Options"), + OPT_BOOLEAN('i', "info", &info, "Show information and most important power metrics after adjustment"), + OPT_BOOLEAN('\0', "dump-table", &dump_table, "Show whole power metric table before and after adjustment"), + OPT_GROUP("Settings"), + OPT_U32('a', "stapm-limit", &stapm_limit, "Sustained Power Limit - STAPM LIMIT (mW)"), + OPT_U32('b', "fast-limit", &fast_limit, "Actual Power Limit - PPT LIMIT FAST (mW)"), + OPT_U32('c', "slow-limit", &slow_limit, "Average Power Limit - PPT LIMIT SLOW (mW)"), + OPT_U32('d', "slow-time", &slow_time, "Slow PPT Constant Time (s)"), + OPT_U32('e', "stapm-time", &stapm_time, "STAPM constant time (s)"), + OPT_U32('f', "tctl-temp", &tctl_temp, "Tctl Temperature Limit (degree C)"), + OPT_U32('g', "vrm-current", &vrm_current, "VRM Current Limit - TDC LIMIT VDD (mA)"), + OPT_U32('j', "vrmsoc-current", &vrmsoc_current, "VRM SoC Current Limit - TDC LIMIT SoC (mA)"), + OPT_U32('\0', "vrmgfx-current", &vrmgfx_current, "VRM GFX Current Limit - TDC LIMIT GFX (mA)"), + OPT_U32('\0', "vrmcvip-current", &vrmcvip_current, "VRM CVIP Current Limit - TDC LIMIT CVIP (mA)"), + OPT_U32('k', "vrmmax-current", &vrmmax_current, "VRM Maximum Current Limit - EDC LIMIT VDD (mA)"), + OPT_U32('l', "vrmsocmax-current", &vrmsocmax_current, "VRM SoC Maximum Current Limit - EDC LIMIT SoC (mA)"), + OPT_U32('\0', "vrmgfxmax_current", &vrmgfxmax_current, "VRM GFX Maximum Current Limit - EDC LIMIT GFX (mA)"), + OPT_U32('m', "psi0-current", &psi0_current, "PSI0 VDD Current Limit (mA)"), + OPT_U32('\0', "psi3cpu_current", &psi3cpu_current, "PSI3 CPU Current Limit (mA)"), + OPT_U32('n', "psi0soc-current", &psi0soc_current, "PSI0 SoC Current Limit (mA)"), + OPT_U32('\0', "psi3gfx_current", &psi3gfx_current, "PSI3 GFX Current Limit (mA)"), + OPT_U32('o', "max-socclk-frequency", &max_socclk_freq, "Maximum SoC Clock Frequency (MHz)"), + OPT_U32('p', "min-socclk-frequency", &min_socclk_freq, "Minimum SoC Clock Frequency (MHz)"), + OPT_U32('q', "max-fclk-frequency", &max_fclk_freq, "Maximum Transmission (CPU-GPU) Frequency (MHz)"), + OPT_U32('r', "min-fclk-frequency", &min_fclk_freq, "Minimum Transmission (CPU-GPU) Frequency (MHz)"), + OPT_U32('s', "max-vcn", &max_vcn, "Maximum Video Core Next (VCE - Video Coding Engine) (MHz)"), + OPT_U32('t', "min-vcn", &min_vcn, "Minimum Video Core Next (VCE - Video Coding Engine) (MHz)"), + OPT_U32('u', "max-lclk", &max_lclk, "Maximum Data Launch Clock (MHz)"), + OPT_U32('v', "min-lclk", &min_lclk, "Minimum Data Launch Clock (MHz)"), + OPT_U32('w', "max-gfxclk", &max_gfxclk_freq, "Maximum GFX Clock (MHz)"), + OPT_U32('x', "min-gfxclk", &min_gfxclk_freq, "Minimum GFX Clock (MHz)"), + OPT_U32('y', "prochot-deassertion-ramp", &prochot_deassertion_ramp, "Ramp Time After Prochot is Deasserted: limit power based on value, higher values does apply tighter limits after prochot is over"), + OPT_U32('\0', "apu-skin-temp", &apu_skin_temp_limit, "APU Skin Temperature Limit - STT LIMIT APU (degree C)"), + OPT_U32('\0', "dgpu-skin-temp", &dgpu_skin_temp_limit, "dGPU Skin Temperature Limit - STT LIMIT dGPU (degree C)"), + OPT_U32('\0', "apu-slow-limit", &apu_slow_limit, "APU PPT Slow Power limit for A+A dGPU platform - PPT LIMIT APU (mW)"), + OPT_U32('\0', "skin-temp-limit", &skin_temp_power_limit, "Skin Temperature Power Limit (mW)"), + OPT_U32('\0', "gfx-clk", &gfx_clk, "Forced Clock Speed MHz (Renoir Only)"), + OPT_U32('\0', "oc-clk", &oc_clk, "Forced Core Clock Speed MHz (Renoir and up Only)"), + OPT_U32('\0', "oc-volt", &oc_volt, "Forced Core VID: Must follow this calculation (1.55 - [VID you want to set e.g. 1.25 for 1.25v]) / 0.00625 (Renoir and up Only)"), + OPT_BOOLEAN('\0', "enable-oc", &enable_oc, "Enable OC (Renoir and up Only)"), + OPT_BOOLEAN('\0', "disable-oc", &disable_oc, "Disable OC (Renoir and up Only)"), + OPT_U32('\0', "set-coall", &coall, "All core Curve Optimiser"), + OPT_U32('\0', "set-coper", &coper, "Per core Curve Optimiser"), + OPT_U32('\0', "set-cogfx", &cogfx, "iGPU Curve Optimiser"), + OPT_BOOLEAN('\0', "power-saving", &power_saving, "Hidden options to improve power efficiency (is set when AC unplugged): behavior depends on CPU generation, Device and Manufacture"), + OPT_BOOLEAN('\0', "max-performance", &max_performance, "Hidden options to improve performance (is set when AC plugged in): behavior depends on CPU generation, Device and Manufacture"), + OPT_GROUP("P-State Functions"), + OPT_END(), + }; + + + struct argparse argparse; + argparse_init(&argparse, options, usage, ARGPARSE_NON_OPTION_IS_INVALID); + argparse_describe(&argparse, "\n Ryzen Power Management adjust tool.", "\nWARNING: Use at your own risk!\nBy Jiaxun Yang , Under LGPL.\nVersion: v" STRINGIFY(RYZENADJ_REVISION_VER) "." STRINGIFY(RYZENADJ_MAJOR_VER) "." STRINGIFY(RYZENADJ_MINIOR_VER)); + argc = argparse_parse(&argparse, argc, argv); + + + //init RyzenAdj and validate that it was able to + ry = init_ryzenadj(); + if(!ry){ + printf("Unable to init ryzenadj\n"); + return -1; + } + + //shows info header before init_table + if (info) { + show_info_header(ry); + } + + if (info || dump_table) { + //init before adjustment to get the default values + err = init_table(ry); + if (err) { + printf("Unable to init power metric table: %d, this does not affect adjustments because it is only needed for monitoring.\n", err); + } + } + + //adjust all the arguments sent to RyzenAdj.exe + _do_adjust(stapm_limit); + _do_adjust(fast_limit); + _do_adjust(slow_limit); + _do_adjust(slow_time); + _do_adjust(stapm_time); + _do_adjust(tctl_temp); + _do_adjust(vrm_current); + _do_adjust(vrmsoc_current); + _do_adjust(vrmgfx_current); + _do_adjust(vrmcvip_current); + _do_adjust(vrmmax_current); + _do_adjust(vrmsocmax_current); + _do_adjust(vrmgfxmax_current); + _do_adjust(psi0_current); + _do_adjust(psi3cpu_current); + _do_adjust(psi0soc_current); + _do_adjust(psi3gfx_current); + _do_adjust(max_socclk_freq); + _do_adjust(min_socclk_freq); + _do_adjust(max_fclk_freq); + _do_adjust(min_fclk_freq); + _do_adjust(max_vcn); + _do_adjust(min_vcn); + _do_adjust(max_lclk); + _do_adjust(min_lclk); + _do_adjust(max_gfxclk_freq); + _do_adjust(min_gfxclk_freq); + _do_adjust(prochot_deassertion_ramp); + _do_adjust(apu_skin_temp_limit); + _do_adjust(dgpu_skin_temp_limit); + _do_adjust(apu_slow_limit); + _do_adjust(skin_temp_power_limit); + _do_adjust(gfx_clk); + _do_adjust(oc_clk); + _do_adjust(oc_volt); + _do_enable(power_saving); + _do_enable(max_performance); + _do_enable(enable_oc) + _do_enable(disable_oc); + _do_adjust(coall); + _do_adjust(coper); + _do_adjust(cogfx); + + if (!err) { + //call show table dump before anybody did call table refresh, because we want to copy the old values first + if (dump_table) { + show_table_dump(ry, any_adjust_applied); + } + //show power table after apply settings + if (info) { + show_info_table(ry); + } + } + + cleanup_ryzenadj(ry); + + return err; +} diff --git a/os/licenses/ryzenadj/src/win32/RyzenAdjServiceTask.xml.template b/os/licenses/ryzenadj/src/win32/RyzenAdjServiceTask.xml.template new file mode 100644 index 00000000..240a74f2 --- /dev/null +++ b/os/licenses/ryzenadj/src/win32/RyzenAdjServiceTask.xml.template @@ -0,0 +1,43 @@ + + + + + true + + + + + InteractiveToken + HighestAvailable + + + + IgnoreNew + false + false + true + false + false + + false + false + + true + true + false + false + false + PT0S + 7 + + PT60M + 10 + + + + + powershell + -WindowStyle hidden -ExecutionPolicy Bypass -file "###SCRIPTPATH###" + + + diff --git a/os/licenses/ryzenadj/src/win32/WinRing0x64.dll b/os/licenses/ryzenadj/src/win32/WinRing0x64.dll new file mode 100644 index 00000000..4a48c7a1 Binary files /dev/null and b/os/licenses/ryzenadj/src/win32/WinRing0x64.dll differ diff --git a/os/licenses/ryzenadj/src/win32/WinRing0x64.exp b/os/licenses/ryzenadj/src/win32/WinRing0x64.exp new file mode 100644 index 00000000..0aa76f87 Binary files /dev/null and b/os/licenses/ryzenadj/src/win32/WinRing0x64.exp differ diff --git a/os/licenses/ryzenadj/src/win32/WinRing0x64.lib b/os/licenses/ryzenadj/src/win32/WinRing0x64.lib new file mode 100644 index 00000000..0f169771 Binary files /dev/null and b/os/licenses/ryzenadj/src/win32/WinRing0x64.lib differ diff --git a/os/licenses/ryzenadj/src/win32/WinRing0x64.sys b/os/licenses/ryzenadj/src/win32/WinRing0x64.sys new file mode 100644 index 00000000..197c255a Binary files /dev/null and b/os/licenses/ryzenadj/src/win32/WinRing0x64.sys differ diff --git a/os/licenses/ryzenadj/src/win32/demo.bat b/os/licenses/ryzenadj/src/win32/demo.bat new file mode 100644 index 00000000..fc41aa04 --- /dev/null +++ b/os/licenses/ryzenadj/src/win32/demo.bat @@ -0,0 +1,2 @@ +%~dp0\ryzenadj.exe --stapm-limit=40000 --fast-limit=45000 --slow-limit=45000 --tctl-temp=90 +pause diff --git a/os/licenses/ryzenadj/src/win32/inpoutx64.dll b/os/licenses/ryzenadj/src/win32/inpoutx64.dll new file mode 100644 index 00000000..82c343fe Binary files /dev/null and b/os/licenses/ryzenadj/src/win32/inpoutx64.dll differ diff --git a/os/licenses/ryzenadj/src/win32/inpoutx64.lib b/os/licenses/ryzenadj/src/win32/inpoutx64.lib new file mode 100644 index 00000000..b3d0dbb7 Binary files /dev/null and b/os/licenses/ryzenadj/src/win32/inpoutx64.lib differ diff --git a/os/licenses/ryzenadj/src/win32/installServiceTask.bat b/os/licenses/ryzenadj/src/win32/installServiceTask.bat new file mode 100644 index 00000000..65569f38 --- /dev/null +++ b/os/licenses/ryzenadj/src/win32/installServiceTask.bat @@ -0,0 +1,39 @@ +@echo off +NET FILE 1>NUL 2>NUL +if %errorlevel% NEQ 0 ( + echo Installation need be run as Administrator to install a Task + pause + exit /B 0 +) + +cd /D "%~dp0" +choice /C YN /M "Do you want to install Service based on directory %~dp0? It can not be changed after installation." +if %ERRORLEVEL% NEQ 1 exit /B 1 + +for %%f in (RyzenAdjServiceTask.xml.template readjustService.ps1 libryzenadj.dll WinRing0x64.dll WinRing0x64.sys inpoutx64.dll) do ( + if not exist %%f echo %%f is missing && goto failed +) + +echo Please configure RyzenAdjService by adding your preferred values in the top section of the powershell script. +timeout /t 2 > NUL +notepad "%~dp0\readjustService.ps1" + +powershell -Command "(gc '%~dp0RyzenAdjServiceTask.xml.template') -replace '###SCRIPTPATH###', '%~dp0readjustService.ps1' | Out-File -encoding ASCII '%~dp0RyzenAdjServiceTask.xml'" + +SCHTASKS /Create /TN "AMD\RyzenAdj" /XML "%~dp0RyzenAdjServiceTask.xml" /F || goto failed + +SCHTASKS /run /TN "AMD\RyzenAdj" || goto failed + +timeout /t 2 > NUL + +SCHTASKS /query /TN "AMD\RyzenAdj" || goto failed + +echo. +echo Installation successful +pause +exit /B 0 + +:FAILED +echo Installation failed +pause +exit /B 1 diff --git a/os/licenses/ryzenadj/src/win32/readjustService.ps1 b/os/licenses/ryzenadj/src/win32/readjustService.ps1 new file mode 100644 index 00000000..b4d44a42 --- /dev/null +++ b/os/licenses/ryzenadj/src/win32/readjustService.ps1 @@ -0,0 +1,464 @@ +<# +.SYNOPSIS + Automates ryzenAdj calls based on custom conditions +.DESCRIPTION + This script is designed to provide maximum flexibility to the user. For that reason it does not use parameters. + Instead of parameters, you need to populate the functions in the configuration section with your custom adjustments and additional custom code. +.NOTES + SPDX-License-Identifier: LGPL + Falco Schaffrath +#> + +Param([Parameter(Mandatory=$false)][switch]$noGUI) +$Error.Clear() +################################################################################ +#### Configuration Start +################################################################################ +# WARNING: Use at your own risk! + +$pathToRyzenAdjDlls = Split-Path -Parent $PSCommandPath #script path is DLL path, needs to be absolute path if you define something else + +$showErrorPopupsDuringInit = $true +# debug mode prints adjust success messages too instead of errorss only +$debugMode = $false +# if monitorField is set, this script does only adjust values if something did revert your monitored value. Clear monitorField String to disable monitoring +# This needs to be an value which actually gets overwritten by your device firmware/software if no changes get detected, your settings will not reapplied +$monitorField = "fast_limit" +# Does reapply adjustments if power slider did change position, check $Script:acSlider or $Script:dcSlider to apply slider specific values +$monitorPowerSlider = $true +# Does reapply adjustments if AC line status did change when AC power cable is plugged or unplugged +$monitorACLineStatus = $true +# HWiNFO needs to be restartet after this script did run the first time with this option +$updateHWINFOSensors = $false +# some Zen3 devices have a locked STAPM limit, this workaround resets the stapm timer to have unlimited stapm. Use max stapm_limit and stapm_time (usually 500) to trigger as less resets as possible +$resetSTAPMUsage = $false + +function doAdjust_ACmode { + $Script:repeatWaitTimeSeconds = 1 #only use values below 5s if you are using $monitorField + adjust "fast_limit" 46000 + adjust "slow_limit" 25000 + #adjust "slow_time" 30 + #adjust "tctl_temp" 93 + #adjust "apu_skin_temp_limit" 50 + #adjust "vrmmax_current" 100000 + #replace any_other_field with additional adjustments. Name is equal to RyzenAdj options but it uses _ instead of - + #adjust "any_other_field" 1234 + + #custom code, for example set fan control back to auto + #values (WriteRegister: 47, FanSpeedResetValue:128) extracted from similar devices at https://github.com/hirschmann/nbfc/blob/master/Configs/ + #Start-Process -NoNewWindow -Wait -filePath "C:\Program Files (x86)\NoteBook FanControl\ec-probe.exe" -ArgumentList("write", "47", "128") + + if($Script:acSlider -eq $Script:betterBattery){ + #put adjustments for energie saving slider position here: + enable "power_saving" #add 10s boost delay for usage on cable to reduce idle power consumption + } +} + +function doAdjust_BatteryMode { + $Script:repeatWaitTimeSeconds = 10 #do less reapplies and less HWiNFO updates to save power + adjust "fast_limit" 26000 + adjust "slow_limit" 10000 + #adjust "any_other_field" 1234 + + if($Script:dcSlider -eq $Script:betterBattery){ + #put adjustments for energie saving slider position here: for example disable fan to save power + #Start-Process -NoNewWindow -Wait -filePath "C:\Program Files (x86)\NoteBook FanControl\ec-probe.exe" -ArgumentList("write", "47", "0") + } + + if($Script:dcSlider -eq $Script:bestPerformance){ + #put adjustments for highest performance slider position here: + enable "max_performance" #removes 10s boost delay on battery + doAdjust_ACmode #set limits from cable mode on battery + } +} +################################################################################ +#### Configuration End +################################################################################ + +$env:PATH += ";$pathToRyzenAdjDlls" +$NL = $([System.Environment]::NewLine); + +if($noGUI){ $showErrorPopupsDuringInit = $false } + +$apiHeader = @' +[DllImport("libryzenadj.dll")] public static extern IntPtr init_ryzenadj(); +[DllImport("libryzenadj.dll")] public static extern int set_stapm_limit(IntPtr ry, [In]uint value); +[DllImport("libryzenadj.dll")] public static extern int set_fast_limit(IntPtr ry, [In]uint value); +[DllImport("libryzenadj.dll")] public static extern int set_slow_limit(IntPtr ry, [In]uint value); +[DllImport("libryzenadj.dll")] public static extern int set_slow_time(IntPtr ry, [In]uint value); +[DllImport("libryzenadj.dll")] public static extern int set_stapm_time(IntPtr ry, [In]uint value); +[DllImport("libryzenadj.dll")] public static extern int set_tctl_temp(IntPtr ry, [In]uint value); +[DllImport("libryzenadj.dll")] public static extern int set_vrm_current(IntPtr ry, [In]uint value); +[DllImport("libryzenadj.dll")] public static extern int set_vrmsoc_current(IntPtr ry, [In]uint value); +[DllImport("libryzenadj.dll")] public static extern int set_vrmmax_current(IntPtr ry, [In]uint value); +[DllImport("libryzenadj.dll")] public static extern int set_vrmsocmax_current(IntPtr ry, [In]uint value); +[DllImport("libryzenadj.dll")] public static extern int set_psi0_current(IntPtr ry, [In]uint value); +[DllImport("libryzenadj.dll")] public static extern int set_psi0soc_current(IntPtr ry, [In]uint value); +[DllImport("libryzenadj.dll")] public static extern int set_max_gfxclk_freq(IntPtr ry, [In]uint value); +[DllImport("libryzenadj.dll")] public static extern int set_min_gfxclk_freq(IntPtr ry, [In]uint value); +[DllImport("libryzenadj.dll")] public static extern int set_max_socclk_freq(IntPtr ry, [In]uint value); +[DllImport("libryzenadj.dll")] public static extern int set_min_socclk_freq(IntPtr ry, [In]uint value); +[DllImport("libryzenadj.dll")] public static extern int set_max_fclk_freq(IntPtr ry, [In]uint value); +[DllImport("libryzenadj.dll")] public static extern int set_min_fclk_freq(IntPtr ry, [In]uint value); +[DllImport("libryzenadj.dll")] public static extern int set_max_vcn(IntPtr ry, [In]uint value); +[DllImport("libryzenadj.dll")] public static extern int set_min_vcn(IntPtr ry, [In]uint value); +[DllImport("libryzenadj.dll")] public static extern int set_max_lclk(IntPtr ry, [In]uint value); +[DllImport("libryzenadj.dll")] public static extern int set_min_lclk(IntPtr ry, [In]uint value); +[DllImport("libryzenadj.dll")] public static extern int set_prochot_deassertion_ramp(IntPtr ry, [In]uint value); +[DllImport("libryzenadj.dll")] public static extern int set_apu_skin_temp_limit(IntPtr ry, [In]uint value); +[DllImport("libryzenadj.dll")] public static extern int set_dgpu_skin_temp_limit(IntPtr ry, [In]uint value); +[DllImport("libryzenadj.dll")] public static extern int set_apu_slow_limit(IntPtr ry, [In]uint value); +[DllImport("libryzenadj.dll")] public static extern int set_power_saving(IntPtr ry); +[DllImport("libryzenadj.dll")] public static extern int set_max_performance(IntPtr ry); + +[DllImport("libryzenadj.dll")] public static extern int refresh_table(IntPtr ry); +[DllImport("libryzenadj.dll")] public static extern IntPtr get_table_values(IntPtr ry); +[DllImport("libryzenadj.dll")] public static extern float get_stapm_limit(IntPtr ry); +[DllImport("libryzenadj.dll")] public static extern float get_stapm_value(IntPtr ry); +[DllImport("libryzenadj.dll")] public static extern float get_stapm_time(IntPtr ry); +[DllImport("libryzenadj.dll")] public static extern float get_fast_limit(IntPtr ry); +[DllImport("libryzenadj.dll")] public static extern float get_fast_value(IntPtr ry); +[DllImport("libryzenadj.dll")] public static extern float get_slow_limit(IntPtr ry); +[DllImport("libryzenadj.dll")] public static extern float get_slow_value(IntPtr ry); +[DllImport("libryzenadj.dll")] public static extern float get_apu_slow_limit(IntPtr ry); +[DllImport("libryzenadj.dll")] public static extern float get_apu_slow_value(IntPtr ry); +[DllImport("libryzenadj.dll")] public static extern float get_vrm_current(IntPtr ry); +[DllImport("libryzenadj.dll")] public static extern float get_vrm_current_value(IntPtr ry); +[DllImport("libryzenadj.dll")] public static extern float get_vrmsoc_current(IntPtr ry); +[DllImport("libryzenadj.dll")] public static extern float get_vrmsoc_current_value(IntPtr ry); +[DllImport("libryzenadj.dll")] public static extern float get_vrmmax_current(IntPtr ry); +[DllImport("libryzenadj.dll")] public static extern float get_vrmmax_current_value(IntPtr ry); +[DllImport("libryzenadj.dll")] public static extern float get_vrmsocmax_current(IntPtr ry); +[DllImport("libryzenadj.dll")] public static extern float get_vrmsocmax_current_value(IntPtr ry); +[DllImport("libryzenadj.dll")] public static extern float get_tctl_temp(IntPtr ry); +[DllImport("libryzenadj.dll")] public static extern float get_tctl_temp_value(IntPtr ry); +[DllImport("libryzenadj.dll")] public static extern float get_apu_skin_temp_limit(IntPtr ry); +[DllImport("libryzenadj.dll")] public static extern float get_apu_skin_temp_value(IntPtr ry); +[DllImport("libryzenadj.dll")] public static extern float get_dgpu_skin_temp_limit(IntPtr ry); +[DllImport("libryzenadj.dll")] public static extern float get_dgpu_skin_temp_value(IntPtr ry); + +[DllImport("kernel32.dll")] public static extern uint GetModuleFileName(IntPtr hModule, [Out]StringBuilder lpFilename, [In]int nSize); +[DllImport("kernel32.dll")] public static extern Boolean GetSystemPowerStatus(out SystemPowerStatus sps); +public struct SystemPowerStatus { + public Byte ACLineStatus; + public Byte BatteryFlag; + public Byte BatteryLifePercent; + public Byte Reserved1; + public Int32 BatteryLifeTime; + public Int32 BatteryFullLifeTime; +} + +public static String getExpectedWinRing0DriverFilepath(){ + StringBuilder fileName = new StringBuilder(255); + GetModuleFileName(IntPtr.Zero, fileName, fileName.Capacity); + return Path.GetDirectoryName(fileName.ToString()) + "\\WinRing0x64.sys"; +} + +public static String getDllImportErrors(){ + try { + Marshal.PrelinkAll(typeof(adj)); + } catch (Exception e) { + return e.Message; + } + return ""; +} +'@ + +if(-not ([System.Management.Automation.PSTypeName]'ryzen.adj').Type){ + Add-Type -MemberDefinition $apiHeader -Namespace 'ryzen' -Name 'adj' -UsingNamespace ('System.Text', 'System.IO') +} + +Add-Type -AssemblyName System.Windows.Forms +function showErrorMsg ([String] $msg){ + if($showErrorPopupsDuringInit){ + [void][System.Windows.Forms.MessageBox]::Show($msg, $PSCommandPath, + [System.Windows.Forms.MessageBoxButtons]::OK, + [System.Windows.Forms.MessageBoxIcon]::Error) + } +} + +$dllImportErrors = [ryzen.adj]::getDllImportErrors(); +if($dllImportErrors -or $Error){ + Write-Error $dllImportErrors + showErrorMsg "Problem with using libryzenadj.dll$NL$NL$($Error -join $NL)" + exit 1 +} + +$winring0DriverFilepath = [ryzen.adj]::getExpectedWinRing0DriverFilepath() +if(!(Test-Path $winring0DriverFilepath)) { Copy-Item -Path $pathToRyzenAdjDlls\WinRing0x64.sys -Destination $winring0DriverFilepath } + +$ry = [ryzen.adj]::init_ryzenadj() +if($ry -eq 0){ + $msg = "RyzenAdj could not get initialized.$($NL)Reason can be found inside Powershell$($NL)" + if($psISE) { $msg += "It is not possible to see the error reason inside ISE, you need to test it in PowerShell Console" } + showErrorMsg "$msg$NL$NL$($Error -join $NL)" + exit 1 +} + +function adjust ([String] $fieldName, [uInt32] $value) { + if($fieldName -eq $Script:monitorField) { + # Adjust target value to the unit of measurement of return value of RyzenAdj + $multiplier = if($value -gt 2000) { 0.001 } else { 1 } + $newTargetValue = [math]::round($value * $multiplier, 3, 0) + if($Script:monitorFieldAdjTarget -ne $newTargetValue){ + $Script:monitorFieldAdjTarget = $newTargetValue + Write-Host "set new monitoring target $fieldName to $newTargetValue" + } + } + $res = Invoke-Expression "[ryzen.adj]::set_$fieldName($ry, $value)" + switch ($res) { + 0 { + if($debugMode) { Write-Host "set $fieldName to $value" } + return + } + -1 { Write-Error "set_$fieldName is not supported on this family"} + -3 { Write-Error "set_$fieldName is not supported on this SMU"} + -4 { Write-Error "set_$fieldName is rejected by SMU"} + default { Write-Error "set_$fieldName did fail with $res"} + } +} + +function enable ([String] $fieldName) { + $res = Invoke-Expression "[ryzen.adj]::set_$fieldName($ry)" + switch ($res) { + 0 { + if($debugMode) { Write-Host "enable $fieldName"} + return + } + -1 { Write-Error "set_$fieldName is not supported on this family"} + -3 { Write-Error "set_$fieldName is not supported on this SMU"} + -4 { Write-Error "set_$fieldName is rejected by SMU"} + default { Write-Error "set_$fieldName did fail with $res"} + } +} + +function testMonitorField { + if($monitorField -and $Script:monitorFieldAdjTarget -eq 0){ + Write-Error ("You forgot to set $monitorField in your profile.$NL$NL" + + "If you ignore it, the script will apply values unnecessary often.$NL") + } +} + +function updateMonitorFieldAdjResult { + if($monitorField){ + [void][ryzen.adj]::refresh_table($ry) + $Script:monitorFieldAdjResult = [math]::round((getMonitorValue), 3, 0) + if($Script:monitorFieldAdjTarget -ne $Script:monitorFieldAdjResult){ + Write-Host ("Warning - $monitorField adjust result $Script:monitorFieldAdjResult does not match target value $Script:monitorFieldAdjTarget. Value $Script:monitorFieldAdjResult will be used for monitoring") + } + } +} + +function testConfiguration { + Write-Host "Test Adjustments" + if($Script:systemPowerStatus.ACLineStatus){ + doAdjust_BatteryMode + testMonitorField + $Script:monitorFieldAdjTarget = 0 + doAdjust_ACmode + } else { + doAdjust_ACmode + testMonitorField + $Script:monitorFieldAdjTarget = 0 + doAdjust_BatteryMode + } + testMonitorField + updateMonitorFieldAdjResult + + if($resetSTAPMUsage -and [ryzen.adj]::get_stapm_time($ry) -eq 1) { + Write-Error "resetSTAPMUsage function does only work on devices with active STAPM control and don't do anything on devices with enabled STT control." + } + + if($Error -and $showErrorPopupsDuringInit){ + $answer = [System.Windows.Forms.MessageBox]::Show("Your Adjustment configuration did not work.$NL$NL$($Error -join $NL)", $PSCommandPath, + [System.Windows.Forms.MessageBoxButtons]::AbortRetryIgnore, + [System.Windows.Forms.MessageBoxIcon]::Warning) + $Error.Clear() + if($answer -eq "Abort"){ exit 1 } + if($answer -eq "Retry"){ testConfiguration } + } +} + +function getMonitorValue { + if($monitorField){ + return Invoke-Expression "[ryzen.adj]::get_$monitorField($ry)" + } + return 0 +} + +function createOrDeleteHWINFOSensors { + if($updateHWINFOSensors){ + New-Item -Path HKCU:\Software\HWiNFO64\Sensors\Custom -Name RyzenAdj -Force > $null + 'Key,Name,Value + Power0,STAPM Limit + Power1,STAPM + Power2,PPT FAST Limit + Power3,PPT FAST + Power4,PPT SLOW Limit + Power5,PPT SLOW + Power6,APU SLOW Limit + Power7,APU SLOW + Current0,TDC VRM Limit + Current1,TDC VRM + Current2,TDC VRM SoC Limit + Current3,TDC VRM SoC + Current4,EDC VRM Max Limit + Current5,EDC VRM Max + Current6,EDC VRM SoC Max Limit + Current7,EDC VRM SoC Max + Temp0,TCTL Temp Limit + Temp1,TCTL Temp + Temp2,SST APU Skin Temp Limit + Temp3,SST APU Skin Temp + Temp4,SST dGPU Skin Temp Limit + Temp5,SST dGPU Skin Temp + Usage0,STAPM Limit Usage, ("STAPM" / "STAPM Limit") * 100 + Usage1,PPT FAST Limit Usage, ("PPT FAST" / "PPT FAST Limit") * 100 + Usage2,PPT SLOW Limit Usage, ("PPT SLOW" / "PPT SLOW Limit") * 100 + Usage3,APU SLOW Limit Usage, ("APU SLOW" / "APU SLOW Limit") * 100 + Usage4,TDC VRM Limit Usage, ("TDC VRM" / "TDC VRM Limit") * 100 + Usage5,TDC VRM SoC Limit Usage, ("TDC VRM SoC" / "TDC VRM SoC Limit") * 100 + Usage6,EDC VRM Max Limit Usage, ("EDC VRM Max" / "EDC VRM Max Limit") * 100 + Usage7,EDC VRM SoC Max Limit Usage, ("EDC VRM SoC Max" / "EDC VRM SoC Max Limit") * 100 + Usage8,TCTL Temp Limit Usage, ("TCTL Temp" / "TCTL Temp Limit") * 100 + Usage9,SST APU Skin Temp Limit Usage, ("SST APU Skin Temp" / "SST APU Skin Temp Limit") * 100 + Usage10,SST dGPU Skin Temp Limit Usage, ("SST dGPU Skin Temp" / "SST dGPU Skin Temp Limit") * 100' | ConvertFrom-Csv -outvariable hwinfo_keys > $null + $hwinfo_keys | ForEach-Object {New-item -Path HKCU:\Software\HWiNFO64\Sensors\Custom\RyzenAdj -Name $_.Key -Force} > $null + $hwinfo_keys | ForEach-Object {[Microsoft.Win32.Registry]::SetValue("HKEY_CURRENT_USER\Software\HWiNFO64\Sensors\Custom\RyzenAdj\" + $_.Key,"Name",$_.Name)} > $null + $hwinfo_keys | Where Value -ne $null | ForEach-Object {[Microsoft.Win32.Registry]::SetValue("HKEY_CURRENT_USER\Software\HWiNFO64\Sensors\Custom\RyzenAdj\" + $_.Key,"Value",$_.Value)} > $null + } else { + Remove-Item HKCU:\Software\HWiNFO64\Sensors\Custom\RyzenAdj -Recurse -ErrorAction:Ignore + } +} + +function setHWINFOValue ([String] $name, [float] $value) { + if(![float]::IsNaN($value)){ [Microsoft.Win32.Registry]::SetValue("HKEY_CURRENT_USER\Software\HWiNFO64\Sensors\Custom\RyzenAdj\" + $name,"Value",[String]$value) } +} + +function updateHWINFOSensors { + setHWINFOValue Power0 ([ryzen.adj]::get_stapm_limit($ry)) + setHWINFOValue Power1 ([ryzen.adj]::get_stapm_value($ry)) + setHWINFOValue Power2 ([ryzen.adj]::get_fast_limit($ry)) + setHWINFOValue Power3 ([ryzen.adj]::get_fast_value($ry)) + setHWINFOValue Power4 ([ryzen.adj]::get_slow_limit($ry)) + setHWINFOValue Power5 ([ryzen.adj]::get_slow_value($ry)) + setHWINFOValue Power6 ([ryzen.adj]::get_apu_slow_limit($ry)) + setHWINFOValue Power7 ([ryzen.adj]::get_apu_slow_value($ry)) + setHWINFOValue Current0 ([ryzen.adj]::get_vrm_current($ry)) + setHWINFOValue Current1 ([ryzen.adj]::get_vrm_current_value($ry)) + setHWINFOValue Current2 ([ryzen.adj]::get_vrmsoc_current($ry)) + setHWINFOValue Current3 ([ryzen.adj]::get_vrmsoc_current_value($ry)) + setHWINFOValue Current4 ([ryzen.adj]::get_vrmmax_current($ry)) + setHWINFOValue Current5 ([ryzen.adj]::get_vrmmax_current_value($ry)) + setHWINFOValue Current6 ([ryzen.adj]::get_vrmsocmax_current($ry)) + setHWINFOValue Current7 ([ryzen.adj]::get_vrmsocmax_current_value($ry)) + setHWINFOValue Temp0 ([ryzen.adj]::get_tctl_temp($ry)) + setHWINFOValue Temp1 ([ryzen.adj]::get_tctl_temp_value($ry)) + setHWINFOValue Temp2 ([ryzen.adj]::get_apu_skin_temp_limit($ry)) + setHWINFOValue Temp3 ([ryzen.adj]::get_apu_skin_temp_value($ry)) + setHWINFOValue Temp4 ([ryzen.adj]::get_dgpu_skin_temp_limit($ry)) + setHWINFOValue Temp5 ([ryzen.adj]::get_dgpu_skin_temp_value($ry)) + #setHWINFOValue Usage11 $pmTable[546] +} + +function resetSTAPMIfNeeded { + $stapm_limit = [ryzen.adj]::get_stapm_limit($ry) + $stapm_value = [ryzen.adj]::get_stapm_value($ry) + $stapm_hysteresis = 1 #Throttling starts around ~0.9W before limit + + if ($stapm_value -gt ($stapm_limit - $stapm_hysteresis)) { + $stapm_time = [ryzen.adj]::get_stapm_time($ry) + $reduced_stapm_limit = ($stapm_limit - 5) #reduce stapm by 5W + Write-Host "[STAPM_RESET] stapm_value ($stapm_value) nearing stapm_limit ($stapm_limit), resetting..." + [void][ryzen.adj]::set_stapm_limit($ry, ($reduced_stapm_limit) * 1000) + [void][ryzen.adj]::set_stapm_time($ry, 0) + [Threading.Thread]::Sleep(10) #10ms is usually enough time + [void][ryzen.adj]::set_stapm_time($ry, $stapm_time) + [void][ryzen.adj]::set_stapm_limit($ry, $stapm_limit * 1250) # add 25% STAPM limit in case we are at battery saving mode where applied values get reduced by 10% or 20% + } +} + +if(-not $Script:repeatWaitTimeSeconds) { $Script:repeatWaitTimeSeconds = 5 } +$Script:monitorFieldAdjResult = 0; #adjust result will be used for monitoring because SMU may only set 90% and 80% of your value +$Script:monitorFieldAdjTarget = 0; +$powerkey = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey("SYSTEM\ControlSet001\Control\Power\User\PowerSchemes\") +$Script:betterBattery = "961cc777-2547-4f9d-8174-7d86181b8a7a" +$Script:betterPerformance = "00000000-0000-0000-0000-000000000000" +$Script:bestPerformance = "ded574b5-45a0-4f42-8737-46345c09c238" +$Script:acSlider = $powerkey.GetValue("ActiveOverlayACPowerScheme") +$Script:dcSlider = $powerkey.GetValue("ActiveOverlayDCPowerScheme") + +$systemPowerStatus = New-Object ryzen.adj+SystemPowerStatus +[void][ryzen.adj]::GetSystemPowerStatus([ref]$systemPowerStatus) +$Script:acLineStatus = $systemPowerStatus.ACLineStatus + +testConfiguration + +<# Example how to get 560 lines of ptable +$pmTable = [float[]]::new(560) +$tablePtr = [ryzen.adj]::get_table_values($ry); +[System.Runtime.InteropServices.Marshal]::Copy($tablePtr, $pmTable, 0, 560); +#> +createOrDeleteHWINFOSensors + +$mtxtArray = @() +if($monitorField){$mtxtArray += "$monitorField changes"} +if($monitorPowerSlider){$mtxtArray += "PowerSlider changes"} +if($monitorACLineStatus){$mtxtArray += "ACLineStatus changes"} +if($mtxtArray.Length){ + $processType = "Monitor " + ($mtxtArray -join " and ") +} else { + $processType = "Apply Settings" +} +Write-Host "$processType every $Script:repeatWaitTimeSeconds seconds..." +while($true) { + $doAdjust = !$monitorField -and !$monitorPowerSlider -and !$monitorACLineStatus + if($monitorField -or $updateHWINFOSensors -or $resetSTAPMUsage) { + [void][ryzen.adj]::refresh_table($ry) + #[System.Runtime.InteropServices.Marshal]::Copy($tablePtr, $pmTable, 0, 560); + } + + if($updateHWINFOSensors){ + updateHWINFOSensors + } + + if($monitorPowerSlider -and ($Script:acSlider -ne $powerkey.GetValue("ActiveOverlayACPowerScheme") -or $Script:dcSlider -ne $powerkey.GetValue("ActiveOverlayDCPowerScheme"))){ + Write-Host "Power Slider changed" + $Script:acSlider = $powerkey.GetValue("ActiveOverlayACPowerScheme") + $Script:dcSlider = $powerkey.GetValue("ActiveOverlayDCPowerScheme") + $doAdjust = $true + } + if($monitorField){ + $monitorValue = getMonitorValue + if($Script:monitorFieldAdjResult -ne [math]::round($monitorValue, 3, 0)){ + Write-Host "$monitorField value unexpectedly changed from $Script:monitorFieldAdjResult to $monitorValue" + $doAdjust = $true + } + } + if($monitorACLineStatus){ + [void][ryzen.adj]::GetSystemPowerStatus([ref]$systemPowerStatus) + if($Script:acLineStatus -ne $systemPowerStatus.ACLineStatus){ + Write-Host "AC Line Status changed" + $Script:acLineStatus = $systemPowerStatus.ACLineStatus + $doAdjust = $true + } + } + + if($resetSTAPMUsage){ + resetSTAPMIfNeeded + } + + if($doAdjust){ + [void][ryzen.adj]::GetSystemPowerStatus([ref]$systemPowerStatus) + $oldWait = $Script:repeatWaitTimeSeconds + if($systemPowerStatus.ACLineStatus){ + doAdjust_ACmode + } else { + doAdjust_BatteryMode + } + updateMonitorFieldAdjResult + if($oldWait -ne $Script:repeatWaitTimeSeconds ) { Write-Host "$processType every $Script:repeatWaitTimeSeconds seconds..." } + } + + sleep $Script:repeatWaitTimeSeconds +} diff --git a/os/licenses/ryzenadj/src/win32/uninstallServiceTask.bat b/os/licenses/ryzenadj/src/win32/uninstallServiceTask.bat new file mode 100644 index 00000000..83045b65 --- /dev/null +++ b/os/licenses/ryzenadj/src/win32/uninstallServiceTask.bat @@ -0,0 +1,23 @@ +@echo off +NET FILE 1>NUL 2>NUL +if %errorlevel% NEQ 0 ( + echo Deinstallation need to be run as Administrator to delete your Scheduled Task + pause + exit /B 0 +) + +reg query HKCU\Software\HWiNFO64\Sensors\Custom\RyzenAdj 2>NUL +if %errorlevel% EQU 0 reg delete HKCU\Software\HWiNFO64\Sensors\Custom\RyzenAdj + +SCHTASKS /query /TN "AMD\RyzenAdj" 2>NUL + +if %errorlevel% NEQ 0 ( + echo RyzenAdj Service Task is not installed + pause + exit /B 0 +) + +SCHTASKS /delete /TN "AMD\RyzenAdj" 2>NUL + +pause +exit /B 0 diff --git a/os/mini/board.sh b/os/mini/board.sh new file mode 100755 index 00000000..01a01cdf --- /dev/null +++ b/os/mini/board.sh @@ -0,0 +1,246 @@ +#!/bin/sh +# Copyright 2026 bong-water-water-bong +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 +# +# 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. +# +# 1bit-board: fans and power profile on the boards this image knows. +# +# The firmware's own fan curve is conservative, so on a known board this sets the EC's fan curve plus a +# predictive boost (level 4 on load, back to the curve when quiet) and applies the power profile. All of it +# goes through the EC driver (ec_su_axb35), never through debug registers. On a board it does not know it +# writes nothing at all and says so: the firmware curve stays. +# +# Usage (root): 1bit-board known | curve | boost | status | power | power-status +# known exit 0 when this board's EC is one we drive (DMI match), 1 otherwise +# curve write the EC fan curve (mode=curve + ramp up/down) to fan1/fan2 +# boost the 0.5 s predictive-boost loop (init starts it in the background) +# status one line: fans: controlled (fan1 N rpm, fan2 N rpm) / not controlled (reason) +# power apply the power profile (EC power mode, SMU limits if the tool is present, clock pins) +# power-status one line: power: , / not applied (reason) +set -u + +EC=${EC_DIR:-/sys/class/ec_su_axb35} +POWER_CONF=${POWER_CONF:-/etc/1bit/power} +UP=${FAN_UP:-45,54,62,70,76} # level 1..5 engage at these EC temperatures (C); level 5 from 82 C +DOWN=${FAN_DOWN:-41,50,58,66,72} # release points: 4 C hysteresis, so the curve does not hunt +FANS=${FAN_NAMES:-"fan1 fan2"} # fan3 (system fan) is left alone: it reads 0 rpm on this board +BOOST_LEVEL=${BOOST_LEVEL:-4} # 80 %: level 5 is never the boost level +BOOST_HOT5_C=${BOOST_HOT5_C:-82} # above this Tctl the boost uses level 5 (100 %) and stays there to 76 C +BOOST_W=${BOOST_W:-35} # package watts that count as load +BOOST_BUSY=${BOOST_BUSY:-40} # GPU busy % that counts as load +BOOST_HOT_C=${BOOST_HOT_C:-70} # Tctl that keeps the boost on regardless of power +BOOST_SLOPE=${BOOST_SLOPE:-3} # Tctl rise per poll that counts as load +BOOST_HOLD=${BOOST_HOLD:-20} # quiet seconds before releasing to the curve +POWER_MODE=${POWER_MODE:-performance} # EC power mode: quiet | balanced | performance +STAPM_W=${STAPM_W:-90} # 90 W sustained, the profile the development box runs +SLOW_W=${SLOW_W:-90} +FAST_W=${FAST_W:-105} +TCTL_C=${TCTL_C:-95} +FCLK=${FCLK:-7} # amdgpu pp_dpm_fclk index, "auto" to leave it alone +SOCCLK=${SOCCLK:-6} # amdgpu pp_dpm_socclk index +SKIP_POWER=${SKIP_POWER:-0} +FALLBACK_MODE=${FALLBACK_MODE:-balanced} # EC mode when the SMU limits cannot be written +RYZENADJ=${RYZENADJ:-} + +say() { printf '%s\n' "$*"; } +die() { printf '1bit-board: %s\n' "$*" >&2; exit 1; } +# BusyBox sleep may not do fractions; a one-second poll still beats the firmware curve. +nap() { sleep 0.5 2>/dev/null || sleep 1; } +hwmon() { for h in /sys/class/hwmon/*; do [ "$(cat "$h/name" 2>/dev/null)" = "$1" ] && echo "$h"; done; } + +dmi() { cat "/sys/class/dmi/id/$1" 2>/dev/null | tr '[:upper:]' '[:lower:]'; } +known_board() { + # Sixunited AXB35-02, sold as BOSGAME M5 "BeyondMax Series": the one board whose EC we drive today. + case "$(dmi product_name) $(dmi board_name)" in + *beyondmax* | *axb35*) return 0 ;; + *) return 1 ;; + esac +} + +ec_ready() { # the driver loaded and the interface we need is there + [ -d "$EC" ] && [ -r "$EC/temp1/temp" ] || return 1 + for f in $FANS; do [ -w "$EC/$f/mode" ] || return 1; done + return 0 +} + +curve() { + ec_ready || die "no EC driver at $EC (nothing written)" + known_board || die "not a known board (nothing written)" + for f in $FANS; do + printf '%s\n' "$UP" > "$EC/$f/rampup_curve" || die "cannot write $EC/$f/rampup_curve" + printf '%s\n' "$DOWN" > "$EC/$f/rampdown_curve" || die "cannot write $EC/$f/rampdown_curve" + printf '%s\n' curve > "$EC/$f/mode" || die "cannot write $EC/$f/mode" + done + say "fans: curve $UP / $DOWN on $FANS (the EC applies it on its own sensor)" +} + +set_fans() { # set_fans fixed|curve [level] + for f in $FANS; do + printf '%s\n' "$1" > "$EC/$f/mode" 2>/dev/null + [ "${2:-}" ] && printf '%s\n' "$2" > "$EC/$f/level" 2>/dev/null + done +} + +boost() { + ec_ready || die "no EC driver at $EC (boost not started)" + known_board || die "not a known board (boost not started)" + K=$(hwmon k10temp); G=$(hwmon amdgpu); B=$(ls /sys/class/drm/card*/device/gpu_busy_percent 2>/dev/null | head -n 1) + [ -n "$K" ] && [ -n "$G" ] || die "no k10temp/amdgpu hwmon (boost not started)" + set_fans curve + on=0; quiet=$(date +%s); last=$(( $(cat "$K/temp1_input" 2>/dev/null || echo 0) / 1000 )); lvl=$BOOST_LEVEL + trap 'set_fans curve; exit 0' TERM INT + say "fans: boost loop up (level $BOOST_LEVEL on load, $BOOST_HOLD s hold, ${BOOST_HOT5_C} C -> level 5)" + while :; do + tc=$(( $(cat "$K/temp1_input" 2>/dev/null || echo 0) / 1000 )) + w=$(( $(cat "$G/power1_input" 2>/dev/null || echo 0) / 1000000 )) + busy=$(cat "$B" 2>/dev/null || echo 0) + now=$(date +%s); load=0 + if [ "$w" -ge "$BOOST_W" ] || [ "$busy" -ge "$BOOST_BUSY" ] || [ "$tc" -ge "$BOOST_HOT_C" ] \ + || [ $(( tc - last )) -ge "$BOOST_SLOPE" ]; then load=1; fi + last=$tc + want=$BOOST_LEVEL + if [ "$tc" -ge "$BOOST_HOT5_C" ]; then want=5 + elif [ "$lvl" = 5 ] && [ "$tc" -gt $(( BOOST_HOT5_C - 6 )) ]; then want=5; fi + if [ "$load" = 1 ]; then + quiet=$now + if [ "$on" = 0 ]; then lvl=$want; set_fans fixed "$lvl"; on=1 + say "fans: boost ${w}W busy=${busy}% Tctl=${tc}C level $lvl" + elif [ "$want" != "$lvl" ]; then lvl=$want; set_fans fixed "$lvl"; say "fans: level $lvl at Tctl=${tc}C" + fi + # something else (the curve, a manual test) may have moved the mode: re-assert it + [ "$(cat "$EC/fan1/mode" 2>/dev/null)" = fixed ] && \ + [ "$(cat "$EC/fan1/level" 2>/dev/null)" = "$lvl" ] || set_fans fixed "$lvl" + elif [ "$on" = 1 ] && [ $(( now - quiet )) -ge "$BOOST_HOLD" ]; then + set_fans curve; on=0; say "fans: back to the curve (${w}W Tctl=${tc}C)" + fi + nap + done +} + +status() { # one line, for the banner, the health output and the release check + if ! known_board; then + say "fans: not controlled (this board is not one we know; the firmware curve stays)" + return 0 + fi + if ! ec_ready; then + say "fans: not controlled (no EC driver: the firmware curve stays)" + return 1 + fi + line="" + for f in $FANS; do + rpm=$(cat "$EC/$f/rpm" 2>/dev/null || echo 0) + line="$line $f $rpm rpm" + done + # informational: at low temperatures the EC legitimately leaves the fans stopped (level 0), so a + # 0 rpm reading here is not a fault. The release check forces level 4 and then expects rpm > 0. + say "fans: controlled ($(printf '%s' "$line" | sed 's/^ //'))" + return 0 +} + +ryzenadj_bin() { + [ -n "$RYZENADJ" ] && { [ -x "$RYZENADJ" ] && echo "$RYZENADJ"; return 0; } + for p in /opt/1bit/ryzenadj /usr/local/bin/ryzenadj /usr/bin/ryzenadj; do + [ -x "$p" ] && { echo "$p"; return 0; } + done + command -v ryzenadj 2>/dev/null && return 0 + return 1 +} + +power_conf() { # read KEY=value overrides from $POWER_CONF without executing it + [ -r "$POWER_CONF" ] || return 0 + while IFS= read -r line; do + case "$line" in ''|'#'*) continue ;; esac + key=${line%%=*}; val=${line#*=} + key=$(printf '%s' "$key" | tr -d ' \t') + val=$(printf '%s' "$val" | sed 's/[[:space:]]*#.*$//; s/^[[:space:]]*//; s/[[:space:]]*$//') + case "$key" in + POWER_MODE|STAPM_W|SLOW_W|FAST_W|TCTL_C|FCLK|SOCCLK|SKIP_POWER|RYZENADJ|FAN_UP|FAN_DOWN) + eval "CONF_$key=\$val" ;; + esac + done < "$POWER_CONF" + for k in POWER_MODE STAPM_W SLOW_W FAST_W TCTL_C FCLK SOCCLK SKIP_POWER RYZENADJ FALLBACK_MODE; do + eval "v=\${CONF_$k:-}"; [ -n "$v" ] && eval "$k=\$v" + done + v=${CONF_FAN_HOT5_C:-}; [ -n "$v" ] && BOOST_HOT5_C=$v + return 0 +} + +power() { + power_conf + [ "$SKIP_POWER" = 1 ] && { say "power: not applied (SKIP_POWER=1 in $POWER_CONF)"; return 0; } + notes="" + if [ -w "$EC/apu/power_mode" ]; then + # the EC power mode resets the SMU limits, so it goes first - same order as the development box + printf '%s\n' "$POWER_MODE" > "$EC/apu/power_mode" 2>/dev/null && notes="$notes mode=$POWER_MODE" \ + || notes="$notes mode=write-failed" + sleep 2 + else + notes="$notes mode=no-ec" + fi + cap_ok=0 + if [ -n "$RYZENADJ" ] && [ ! -x "$RYZENADJ" ]; then + notes="$notes cap=failed(RYZENADJ=$RYZENADJ is not executable)" + elif RJ=$(ryzenadj_bin); then + if "$RJ" --stapm-limit=$(( STAPM_W * 1000 )) --slow-limit=$(( SLOW_W * 1000 )) \ + --fast-limit=$(( FAST_W * 1000 )) --tctl-temp="$TCTL_C" >/dev/null 2>&1; then + notes="$notes cap=${STAPM_W}/${SLOW_W}/${FAST_W}W tctl=$TCTL_C"; cap_ok=1 + else + notes="$notes cap=failed($RJ)" + fi + else + notes="$notes cap=not-applied(no ryzenadj)" + fi + if [ "$cap_ok" = 0 ]; then + # Secure Boot lockdown, no /dev/mem access or no tool: the SMU limits cannot be written, so fall back + # to the EC's own balanced profile (85 W sustained, 120 W burst). Never leave the board in performance + # mode (its ~120 W profile) with no cap at all. + if printf '%s\n' "$FALLBACK_MODE" > "$EC/apu/power_mode" 2>/dev/null; then + notes="$notes fallback=$FALLBACK_MODE (the EC profile alone is in force)" + else + notes="$notes fallback=unavailable" + fi + fi + G=$(ls -d /sys/class/drm/card*/device 2>/dev/null | head -n 1) + if [ -n "$G" ] && [ -w "$G/power_dpm_force_performance_level" ]; then + if [ "$FCLK" = auto ]; then + printf 'auto\n' > "$G/power_dpm_force_performance_level" 2>/dev/null && notes="$notes clocks=auto" + else + printf 'manual\n' > "$G/power_dpm_force_performance_level" 2>/dev/null + printf '%s\n' "$FCLK" > "$G/pp_dpm_fclk" 2>/dev/null + printf '%s\n' "$SOCCLK" > "$G/pp_dpm_socclk" 2>/dev/null + notes="$notes fclk=$FCLK socclk=$SOCCLK" + fi + fi + say "power:$notes" +} + +power_status() { + mode=$(cat "$EC/apu/power_mode" 2>/dev/null || echo none) + if RJ=$(ryzenadj_bin) && info=$("$RJ" --info 2>/dev/null); then + lim=$(printf '%s\n' "$info" | sed -n 's/^STAPM LIMIT *| *\([0-9.]*\)\..*/\1/p' | head -n 1) + say "power: $mode mode, SMU cap ${lim:-?} W sustained" + else + say "power: $mode mode, EC profile only (no SMU cap: ryzenadj absent or /dev/mem not writable)" + fi +} + +case "${1:-}" in + known) known_board && exit 0 || exit 1 ;; + curve) curve ;; + boost) boost ;; + status) status ;; + power) power ;; + power-status) power_status ;; + *) say "usage: 1bit-board known|curve|boost|status|power|power-status" >&2; exit 2 ;; +esac diff --git a/os/mini/init.sh b/os/mini/init.sh index 2e8e06fc..257909b9 100644 --- a/os/mini/init.sh +++ b/os/mini/init.sh @@ -199,6 +199,17 @@ else say "[gpu] hwmon: $(for h in /sys/class/hwmon/hwmon*; do cat $h/name 2>/dev/null; done | tr '\n' ' ')" fi + +# fans and power: the EC driver exists only for the boards we know (os/mini/board.sh). On any other +# board nothing is written at all and the firmware's own fan curve stays. +if [ -x /usr/sbin/1bit-board ] && /usr/sbin/1bit-board known 2>/dev/null; then + modprobe -q ec_su_axb35 + say "[fans] curve: $(/usr/sbin/1bit-board curve 2>&1 | head -n 1)" + say "[power] $(/usr/sbin/1bit-board power 2>&1 | head -n 1)" + setsid /usr/sbin/1bit-board boost > /tmp/fans.log 2>&1 & +else + say "[fans] not controlled: this board is not one we know, the firmware curve stays" +fi # HRX: TheRock's HSA runtime (1bit serve also finds it at its compiled-in path), and the Loom # kernel cache on the stick, so kernels compiled on the first boot are reused after a reboot [ -s /etc/1bit/libhsa ] && export IREE_HAL_AMDGPU_LIBHSA_PATH="$(cat /etc/1bit/libhsa)" @@ -295,6 +306,16 @@ else touch /etc/1bit/live 2>/dev/null fi echo "GPU: $(ls /dev/dri/renderD* /dev/kfd 2>/dev/null | tr '\n' ' ') NPU: $(ls /dev/accel/accel* 2>/dev/null | tr '\n' ' ')" + +# health: fans, power profile and the two temperatures this board reports +if [ -x /usr/sbin/1bit-board ]; then + say "[fans] $(/usr/sbin/1bit-board status 2>/dev/null)" + say "[power] $(/usr/sbin/1bit-board power-status 2>/dev/null)" +fi +TJ=$(for h in /sys/class/hwmon/hwmon*; do [ "$(cat "$h/name" 2>/dev/null)" = k10temp ] && echo "$h"; done | head -n 1) +if [ -n "$TJ" ]; then + say "temps: Tctl $(( $(cat "$TJ/temp1_input" 2>/dev/null || echo 0) / 1000 ))C EC $(cat /sys/class/ec_su_axb35/temp1/temp 2>/dev/null || echo n/a)C" +fi echo "IP: $(ip -4 -o addr show scope global | awk '{print $2, $4}' | tr '\n' ' ')" echo; echo "Lemonade's web app and API: http://$lhost:$lport (from another machine: ssh -L $lport:localhost:$lport root@)" echo "Prove the engine answers from this boot: 1bit-selftest (console or SSH; logs to /data/selftest.log)" @@ -327,6 +348,12 @@ if grep -q "1bit.check" /proc/cmdline; then if find / -xdev \( -name 'libxrt*' -o -name 'libopenvino*' -o -name 'libonnxruntime*' \) 2>/dev/null | grep -q .; then echo "CHECK runtime deps: FAILED (an external accelerator runtime is in the image)"; ok=0 else echo "CHECK runtime deps: self-contained"; fi + if [ -x /usr/sbin/1bit-board ] && /usr/sbin/1bit-board known 2>/dev/null; then + if /usr/sbin/1bit-board status > /dev/null 2>&1; then echo "CHECK fans: controlled" + else echo "CHECK fans: FAILED"; ok=0; fi + else + echo "CHECK fans: not controlled (no EC driver on this board)" + fi [ -e /lib/firmware/amdnpu/17f0_11 ] && echo "CHECK NPU firmware: present" || { echo "CHECK NPU firmware: FAILED"; ok=0; } dropbear -V 2>&1 | grep -q Dropbear && echo "CHECK dropbear: runs" || { echo "CHECK dropbear: FAILED"; ok=0; } echo "CHECK modules: $(find /lib/modules -name '*.ko*' | wc -l) kernel modules, amdgpu $(modinfo -F version amdgpu >/dev/null 2>&1 && echo found || echo MISSING)" diff --git a/os/mini/mkimage.sh b/os/mini/mkimage.sh index d0f82299..902d98fc 100755 --- a/os/mini/mkimage.sh +++ b/os/mini/mkimage.sh @@ -123,6 +123,73 @@ for m in $mods; do modprobe -S "$kver" --show-depends "$m" 2>/dev/null | awk '$1 == "insmod" {print $2}' done | sort -u | while read -r ko; do mkdir -p "$root$(dirname "$ko")"; cp "$ko" "$root$ko"; done cp /lib/modules/"$kver"/modules.{order,builtin,builtin.modinfo} "$root/lib/modules/$kver/" 2>/dev/null || true + +echo "== board driver (ec_su_axb35, GPL-2.0, source shipped in os/licenses/ec_su_axb35)" +# The fans and the EC power mode are driven by the board's embedded controller and the kernel has no +# driver for it, so it is built here, out of tree, against the image kernel - from the source this +# repository ships next to it (GPL-2.0: the binary in the image always travels with its source). +lic=$(cd "$here/.." && pwd)/licenses # self-contained: $licenses is defined further down +ecsrc=$lic/ec_su_axb35/src +if [ "${EC_MODULE:-build}" = skip ]; then + echo " skipped (EC_MODULE=skip): this image will leave the fans on the firmware curve" +elif [ ! -d "$ecsrc" ]; then + echo " os/licenses/ec_su_axb35/src is missing: the fans would stay on the firmware curve"; exit 1 +else + mkdir -p "$out/logs" + ecbuild=$(mktemp -d) + cp -a "$ecsrc"/. "$ecbuild"/ + # the image kernel is clang-built (CONFIG_CC_IS_CLANG): an out-of-tree module has to use the same + # toolchain, or the compiler rejects the kernel's own flags (e.g. -mretpoline-external-thunk) + kmake="" + if grep -q "^CONFIG_CC_IS_CLANG=y" /boot/config-"$kver" 2>/dev/null; then + # LLVM=1 wants the whole LLVM toolchain (ld.lld, llvm-ar, llvm-nm, ...), so the matching + # /usr/lib/llvm-/bin goes on PATH - a bare `clang` is not enough + kv=$(sed -n 's/^CONFIG_CLANG_VERSION=\([0-9]*\)/\1/p' /boot/config-"$kver" | head -n 1) + major=$(( ${kv:-99} / 10000 )) + for d in "/usr/lib/llvm-$major/bin" "$HOME/.cache/xdna-km/llvm$major/bin" /usr/lib/llvm-18/bin; do + [ -x "$d/ld.lld" ] && [ -x "$d/clang" ] && { PATH="$d:$PATH"; export PATH; kmake="LLVM=1"; break; } + done + [ -n "$kmake" ] || echo " no LLVM toolchain (ld.lld) on this builder: the kernel is clang-built, the module will not build" + fi + if make -s -C "$ecbuild" KERNEL_BUILD=/lib/modules/"$kver"/build $kmake modules > "$out/logs/ec_su_axb35-build.log" 2>&1; then + mkdir -p "$root/lib/modules/$kver/updates" + # Secure Boot: an out-of-tree module has to be signed by a key the kernel trusts, i.e. a MOK that is + # enrolled. It must be the MOK - the "Secure Boot Kernel" cert carries no module-signing OID, so it + # can sign an EFI binary but not a module (verified on the development box: amdxdna.ko and our own + # ec_su_axb35.ko are both signed by "strixhalo Secure Boot MOK"). + # note: MODULE_SIGN_KEY/MODULE_SIGN_CERT sign the module, and they are not MOK_KEY/MOK_CERT, which sign + # the EFI image further down: a module needs the MOK with the module-signing OID, the image needs the + # kernel-signing key. Both certificates are enrolled on the development box. + kdir=/lib/modules/"$kver"/build + modkey=${MODULE_SIGN_KEY:-/var/lib/shim-signed/mok/MOK.priv} + modcrt=${MODULE_SIGN_CERT:-/var/lib/shim-signed/mok/MOK.der} + # sign the scratch copy, never the file inside the image: sign-file needs the root-only MOK key, so it + # may run through sudo, and a root-written file inside the image tree breaks the later cpio/chmod + ko=$ecbuild/ec_su_axb35.ko; kosign=$ecbuild/ec_su_axb35.signed.ko + signed=0 + if [ -x "$kdir/scripts/sign-file" ] && [ -r "$modcrt" ]; then + if [ -r "$modkey" ]; then + "$kdir/scripts/sign-file" sha256 "$modkey" "$modcrt" "$ko" "$kosign" && signed=1 + elif sudo -n true 2> /dev/null; then + sudo -n "$kdir/scripts/sign-file" sha256 "$modkey" "$modcrt" "$ko" "$kosign" && signed=1 + fi + fi + [ "$signed" = 1 ] && cp "$kosign" "$root/lib/modules/$kver/updates/ec_su_axb35.ko" \ + || cp "$ko" "$root/lib/modules/$kver/updates/ec_su_axb35.ko" + chmod 0644 "$root/lib/modules/$kver/updates/ec_su_axb35.ko" + echo " ec_su_axb35.ko for $kver: $(stat -c %s "$root/lib/modules/$kver/updates/ec_su_axb35.ko") bytes" + if [ "$signed" = 1 ]; then + echo " signed with $(basename "$modcrt") (the module-signing MOK)" + else + echo " WARNING: ec_su_axb35.ko left UNSIGNED (need $modkey, $modcrt and $kdir/scripts/sign-file)" + echo " it loads where module signature checking is off, which is the case for our kernel:" + echo " CONFIG_LOCK_DOWN_IN_EFI_SECURE_BOOT is not set, so lockdown stays 'none' even with SB on" + fi + else + echo " build FAILED (see $out/logs/ec_su_axb35-build.log); set EC_MODULE=skip to ship without it"; exit 1 + fi + rm -rf "$ecbuild" +fi # The NPU needs the PRIVATE XDNA driver build, not the stock module in /lib/modules: with the stock one # the engine's NPU route resets the machine. Same vermagic, so it is a straight drop-in. xdna=${AMDXDNA_KO:-$HOME/wt/perfopt-7.3-obj/drivers/accel/amdxdna/amdxdna.ko} @@ -259,6 +326,31 @@ cp /usr/sbin/mke2fs "$root/bin/mke2fs"; chmod +x "$root/bin/mke2fs" copy_libs /usr/sbin/mke2fs cp /etc/mke2fs.conf "$root/etc/mke2fs.conf" cp "$here/release-check.sh" "$root/bin/1bit-release-check"; chmod +x "$root/bin/1bit-release-check" +install -m 0755 "$here/board.sh" "$root/usr/sbin/1bit-board" +mkdir -p "$root/etc/1bit" +install -m 0644 "$here/power.conf.example" "$root/etc/1bit/power" +# The SMU limits (the 90 W sustained cap) are set through ryzenadj, which is LGPL-3.0: ship the tool and +# its licence. A builder without it still works - the EC power mode alone applies and the image says so. +echo "== SMU tool (ryzenadj v0.19.0, LGPL-3.0, source pinned in os/licenses/ryzenadj, userspace build)" +# Deterministic: built from the source this repository ships, never from whatever the build machine has. +rjsrc=$lic/ryzenadj/src +if [ ! -d "$rjsrc" ]; then echo " os/licenses/ryzenadj/src is missing: the SMU cap could not be applied"; exit 1; fi +mkdir -p "$out/logs" +rjbuild=$(mktemp -d) +if cp -a "$rjsrc"/. "$rjbuild"/ && ( cd "$rjbuild" \ + && cmake -B build -DCMAKE_BUILD_TYPE=Release . > "$out/logs/ryzenadj-build.log" 2>&1 \ + && cmake --build build -j4 >> "$out/logs/ryzenadj-build.log" 2>&1 ); then + mkdir -p "$root/opt/1bit" + cp "$rjbuild/build/ryzenadj" "$root/opt/1bit/ryzenadj"; chmod 0755 "$root/opt/1bit/ryzenadj" + for lib in $(ldd "$root/opt/1bit/ryzenadj" 2>/dev/null | awk '/=> \//{print $3}'); do + case "$lib" in */libc.so*|*/libm.so*|*/ld-linux*) continue ;; esac + if [ ! -e "$root$lib" ]; then mkdir -p "$root$(dirname "$lib")"; cp -L "$lib" "$root$lib"; fi + done + echo " /opt/1bit/ryzenadj $(stat -c %s "$root/opt/1bit/ryzenadj") bytes ($(ldd "$root/opt/1bit/ryzenadj" 2>/dev/null | wc -l) libs linked)" +else + echo " build FAILED (see $out/logs/ryzenadj-build.log); needs cmake, a C compiler and libpci-dev"; exit 1 +fi +rm -rf "$rjbuild" cp "$here/udhcpc.sh" "$root/etc/udhcpc.script"; chmod +x "$root/etc/udhcpc.script" echo "1bit-os" > "$root/etc/hostname" printf 'root:x:0:0:root:/root:/bin/sh\n' > "$root/etc/passwd" @@ -274,6 +366,12 @@ cp "$here/../LICENSES.md" "$src/LICENSE" "$src/NOTICE" "$src/LICENSE-BINARY" "$s cp "$src/third_party/lemonade/LICENSE" "$doc/lemonade-LICENSE" || { echo "no third_party/lemonade/LICENSE (git submodule update --init third_party/lemonade)"; exit 1; } cp "$licenses/llamacpp-hrx/ROCR-Runtime-LICENSE" "$doc/ROCR-Runtime-LICENSE" # TheRock's libhsa for HRX +# the board driver is GPL-2.0 and the SMU tool LGPL-3.0: source and licence travel in the image with them +mkdir -p "$doc/ec_su_axb35" "$doc/ryzenadj" +cp -a "$lic/ec_su_axb35/LICENSE" "$lic/ec_su_axb35/SOURCE" "$doc/ec_su_axb35/" 2>/dev/null || true +if [ -d "$lic/ec_su_axb35/src" ]; then cp -a "$lic/ec_su_axb35/src" "$doc/ec_su_axb35/src"; fi +cp -a "$lic/ryzenadj/LICENSE" "$lic/ryzenadj/SOURCE" "$doc/ryzenadj/" 2>/dev/null || true +if [ -d "$lic/ryzenadj/src" ]; then cp -a "$lic/ryzenadj/src" "$doc/ryzenadj/src"; fi cp -rL /usr/share/common-licenses "$root/usr/share/" # the Ubuntu package behind each file in the image, found at its path or its merged-/usr alias (cd "$root" && find . -type f | cut -c2-) > "$out/image-files.txt" diff --git a/os/mini/power.conf.example b/os/mini/power.conf.example new file mode 100644 index 00000000..1469219f --- /dev/null +++ b/os/mini/power.conf.example @@ -0,0 +1,44 @@ +# Copyright 2026 bong-water-water-bong +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 +# +# 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. +# +# /etc/1bit/power — power profile and fan overrides for 1bit OS. +# +# The defaults below are the values in force with this file untouched: they are the profile the 1bit +# development box runs on the same board (performance mode, 90 W sustained), so a shipped appliance and the +# numbers on the leaderboard describe the same machine. Change a line to override it; the file is read as +# KEY=value and nothing in it is executed. +# +# Order at boot: the EC power mode first (a mode write resets the SMU limits), then the SMU limits, then the +# fabric/SoC clock pins. If the SMU limits cannot be written (no tool, or /dev/mem refused - see the Secure +# Boot note in TOSFANS-STATE), the EC falls back to FALLBACK_MODE and the boot banner says which cap is in force. +# +#POWER_MODE=performance # quiet | balanced | performance (the EC's own power mode) +#STAPM_W=90 # sustained package power limit, watts +#SLOW_W=90 # slow (average) limit, watts +#FAST_W=105 # fast (burst) limit, watts +#TCTL_C=95 # die temperature limit, degrees C +#FALLBACK_MODE=balanced # EC mode used when the SMU limits cannot be written (85 W sustained, 120 W burst) +#FCLK=7 # amdgpu pp_dpm_fclk index, or auto to let the driver choose +#SOCCLK=6 # amdgpu pp_dpm_socclk index +#RYZENADJ=/opt/1bit/ryzenadj # the SMU tool; empty = search /opt/1bit, /usr/local/bin, /usr/bin +#SKIP_POWER=1 # do not touch power at all (the firmware profile stays) +# +# Fans: the EC applies this curve on its own sensor (levels 1..5 = 20/40/60/80/100 %). +# The boost raises the CPU fans to level 4 on load and back to the curve after 20 s quiet. +#FAN_UP=45,54,62,70,76 +#FAN_DOWN=41,50,58,66,72 +#FAN_HOT5_C=82 # Tctl that sends the boost to level 5 (100 %) and holds it down to 76 C. + # FIRST THING TO REVISIT on a hotter unit: this trip, and the curve points above, + # were tuned on the development box's board with a phase-change pad. diff --git a/os/mini/release-check.sh b/os/mini/release-check.sh index 6cd68ef6..bd70fe17 100755 --- a/os/mini/release-check.sh +++ b/os/mini/release-check.sh @@ -90,6 +90,34 @@ else skip npu "no NPU in this VM: /dev/accel and /lib/firmware/amdnpu only appear on the machine" fi +# ── fans (EC fan curve; only a board we know has one we drive) ────────────────────────────────── +if [ ! -x /usr/sbin/1bit-board ]; then + skip fans "no /usr/sbin/1bit-board in this image: the fans stay on the firmware curve" +elif ! /usr/sbin/1bit-board known; then + skip fans "this board has no EC we drive: the firmware fan curve stays" +elif [ ! -d /sys/class/ec_su_axb35 ]; then + skip fans "ec_su_axb35 did not load: the firmware fan curve stays" +else + # level 4 (the boost level) for 5 s, then every CPU fan must report rpm > 0. A fan that reads 0 rpm + # at 80 % is exactly the fault this row exists for - it is how the system fan failed on 2026-10-04. + for f in fan1 fan2; do + printf 'fixed\n' > "/sys/class/ec_su_axb35/$f/mode" 2>/dev/null + printf '4\n' > "/sys/class/ec_su_axb35/$f/level" 2>/dev/null + done + sleep 5 + RPMS=; BAD=0 + for f in fan1 fan2; do + r=$(cat "/sys/class/ec_su_axb35/$f/rpm" 2>/dev/null || echo 0) + RPMS="$RPMS$f $r rpm, " + [ "$r" -gt 0 ] 2>/dev/null || BAD=1 + done + for f in fan1 fan2; do printf 'curve\n' > "/sys/class/ec_su_axb35/$f/mode" 2>/dev/null; done + RPMS=$(printf '%s' "$RPMS" | sed 's/, $//') + if [ "$BAD" = 0 ]; then pass fans "controlled ($RPMS at level 4)" + else fail fans "the driver loaded but a fan reads 0 rpm at level 4 ($RPMS): the curve and the boost cannot cool this board" + fi +fi + # ── Lemonade, with and without the key ────────────────────────────────────────────────────────── if [ -z "$KEY" ]; then fail lemonade "no LEMONADE_API_KEY in /data/lemonade/lemond.conf: a network-facing server without a key" diff --git a/tools/copyright.py b/tools/copyright.py index 5d24c56a..63646e0e 100644 --- a/tools/copyright.py +++ b/tools/copyright.py @@ -32,7 +32,7 @@ LINE_STYLES = [ (("*.cpp", "*.h", "*.inc", "*.hpp", "*.c", "*.cc", "*.rs", "*.hip"), "//"), (("*.py", "*.sh", "*.yml", "*.yaml", "*.cff", "*.cmake", "*.cmake.in", "CMakeLists.txt", ".gitmodules", - ".gitignore", ".gitattributes", ".clang-format", "*.toml", "*.config", "*.fragment", + ".gitignore", ".gitattributes", ".clang-format", "*.toml", "*.config", "*.fragment", "*.example", "*.service", "*.timer", "*.conf", "lemonade-hrx", "Dockerfile", "*.Dockerfile"), "#"), ] BLOCK_STYLES = [(("*.md", "*.html", "*.svg"), ("")), (("*.css", "*.js"), ("/*", "*/"))]