diff --git a/drivers/net/ethernet/mft/COPYING b/drivers/net/ethernet/mft/COPYING new file mode 100644 index 00000000..21ab96ed --- /dev/null +++ b/drivers/net/ethernet/mft/COPYING @@ -0,0 +1,378 @@ +This software is available to you under a choice of one of two licenses. +You may chose to be licensed under the terms of the the OpenIB.org BSD +license or the GNU General Public License (GPL) Version 2, both included +below. + +Copyright (c) 2002-2011 Mellanox Technologies LTD. All rights reserved. + +================================================================== + + OpenIB.org BSD license + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +================================================================== + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Library General +Public License instead of this License. diff --git a/drivers/net/ethernet/mft/Makefile b/drivers/net/ethernet/mft/Makefile new file mode 100644 index 00000000..d7e04809 --- /dev/null +++ b/drivers/net/ethernet/mft/Makefile @@ -0,0 +1,25 @@ +CPU_ARCH ?= $(shell uname -m) + +all: + cd mst_backward_compatibility/mst_pci && $(MAKE) + cd mst_backward_compatibility/mst_pciconf && $(MAKE) + +ifneq ($(findstring ppc64, $(CPU_ARCH)),) + cd mst_backward_compatibility/mst_ppc && $(MAKE) +endif + +ifneq ($(findstring aarch64, $(CPU_ARCH)),) + cd misc_drivers/bf3_livefish && $(MAKE) +endif + +clean: + $(MAKE) clean -C mst_backward_compatibility/mst_pci + $(MAKE) clean -C mst_backward_compatibility/mst_pciconf + +ifneq ($(findstring ppc64, $(CPU_ARCH)),) + $(MAKE) clean -C mst_backward_compatibility/mst_ppc +endif + +ifneq ($(findstring aarch64, $(CPU_ARCH)),) + $(MAKE) clean -C misc_drivers/bf3_livefish +endif diff --git a/drivers/net/ethernet/mft/common.postinst b/drivers/net/ethernet/mft/common.postinst new file mode 100755 index 00000000..47cd4bbd --- /dev/null +++ b/drivers/net/ethernet/mft/common.postinst @@ -0,0 +1,296 @@ +#!/bin/sh + +set -e + +uname_s=$(uname -s) + +_get_kernel_dir() { + KVER=$1 + case ${uname_s} in + Linux) DIR="/lib/modules/$KVER/build" ;; + GNU/kFreeBSD) DIR="/usr/src/kfreebsd-headers-$KVER/sys" ;; + esac + echo $DIR +} + +_check_kernel_dir() { + DIR=$(_get_kernel_dir $1) + case ${uname_s} in + Linux) test -e $DIR/include ;; + GNU/kFreeBSD) test -e $DIR/kern && test -e $DIR/conf/kmod.mk ;; + *) return 1 ;; + esac + return $? +} + +# Check the existence of a kernel named as $1 +_is_kernel_name_correct() { + CORRECT="no" + KERNEL_NAME=$1 + + for kernel in /boot/config-*; do + KERNEL=${kernel#*-} + if [ "${KERNEL}" = "${KERNEL_NAME}" ]; then + CORRECT="yes" + break + fi + done + + echo $CORRECT +} + + +# Get the most recent kernel on Debian based systems. This keeps +# into account both the version and the ABI. If the current kernel +# is the most recent kernel then the function will print a null string. +_get_newest_kernel_debian() { + NEWEST_KERNEL= + NEWEST_VERSION= + NEWEST_ABI= + + for kernel in /boot/config-*; do + KERNEL=${kernel#*-} + KERNEL_VERSION=${KERNEL%%-*} + ABI=${KERNEL#*-} + ABI=${ABI%%-*} + + if [ -z "$NEWEST_KERNEL" ]; then + # The 1st time get a version which is bigger than $1 + COMPARE_TO=$1 + else + # Get the biggest version + COMPARE_TO="$NEWEST_VERSION-$NEWEST_ABI" + fi + + # Make sure that kernel exists before comparing it + if [ ! -e /lib/modules/$KERNEL ]; then + continue + fi + + # if $kernel is greater than $COMPARE_TO + if [ `dpkg --compare-versions "$KERNEL_VERSION-$ABI" gt "$COMPARE_TO" && echo "yes" || \ + echo "no"` = "yes" ]; then + NEWEST_KERNEL=$KERNEL + NEWEST_VERSION=$KERNEL_VERSION + NEWEST_ABI=$ABI + fi + done + + echo "$NEWEST_KERNEL" +} + +# Get the most recent kernel in Rhel based systems. If the current kernel +# is the most recent kernel then the function will print a null string. +_get_newest_kernel_rhel() { + NEWEST_KERNEL= + + LAST_INSTALLED_KERNEL=$(rpm -q --whatprovides kernel --last | grep kernel -m1 | cut -f1 -d' ') + + LIK_FORMATTED_NAME=$(rpm -q $LAST_INSTALLED_KERNEL --queryformat="%{VERSION}-%{RELEASE}.%{ARCH}\n") + + if [ `echo $LIK_FORMATTED_NAME | grep 2.6 >/dev/null` ]; then + # Fedora and Suse + NEWEST_KERNEL=$LIK_FORMATTED_NAME + else + # Hack for Mandriva where $LIK_FORMATTED_NAME is broken + LIK_NAME=$(rpm -q $LAST_INSTALLED_KERNEL --queryformat="%{NAME}\n") + LIK_TYPE=${LIK_NAME#kernel-} + LIK_TYPE=${LIK_TYPE%%-*} + LIK_STRIPPED=${LIK_NAME#kernel-} + LIK_STRIPPED=${LIK_STRIPPED#$LIK_TYPE-} + LIK_STRIPPED_BASE=${LIK_STRIPPED%%-*} + LIK_STRIPPED_END=${LIK_STRIPPED#$LIK_STRIPPED_BASE-} + LIK_FINAL=$LIK_STRIPPED_BASE-$LIK_TYPE-$LIK_STRIPPED_END + + NEWEST_KERNEL=$LIK_FINAL + fi + + echo $NEWEST_KERNEL +} + +# Get the newest kernel on Debian and Rhel based systems. +get_newest_kernel() { + NEWEST_KERNEL= + # Try Debian first as rpm can be installed in Debian based distros + if [ -e /usr/bin/dpkg ]; then + # If DEB based + CURRENT_KERNEL=$1 + CURRENT_VERSION=${CURRENT_KERNEL%%-*} + CURRENT_ABI=${CURRENT_KERNEL#*-} + CURRENT_FLAVOUR=${CURRENT_ABI#*-} + CURRENT_ABI=${CURRENT_ABI%%-*} + NEWEST_KERNEL=$(_get_newest_kernel_debian "$CURRENT_VERSION-$CURRENT_ABI") + + elif [ `which rpm >/dev/null` ]; then + # If RPM based + NEWEST_KERNEL=$(_get_newest_kernel_rhel) + fi + + # Make sure that kernel name that we extracted corresponds to an installed + # kernel + if [ -n "$NEWEST_KERNEL" ] && [ `_is_kernel_name_correct $NEWEST_KERNEL` = "no" ]; then + NEWEST_KERNEL= + fi + + echo $NEWEST_KERNEL +} + +NAME=$1 +VERSION=$2 +TARBALL_ROOT=$3 +ARCH=$4 +UPGRADE=$5 + +if [ -z "$NAME" ] || [ -z "$VERSION" ]; then + echo "Need NAME, and VERSION defined" + echo "ARCH is optional" + exit 1 +fi + +KERNELS=$(ls /lib/modules/ 2>/dev/null || true) +CURRENT_KERNEL=$(uname -r) + +#We never want to keep an older version side by side to prevent conflicts +if [ -e "/var/lib/dkms/$NAME/$VERSION" ]; then + echo "Removing old $NAME-$VERSION DKMS files..." + dkms remove -m $NAME -v $VERSION --all +fi + +#Load new files, by source package and by tarball +if [ -f "$TARBALL_ROOT/$NAME-$VERSION.dkms.tar.gz" ]; then + if ! dkms ldtarball --archive "$TARBALL_ROOT/$NAME-$VERSION.dkms.tar.gz"; then + echo "" + echo "" + echo "Unable to load DKMS tarball $TARBALL_ROOT/$NAME-$VERSION.dkms.tar.gz." + echo "Common causes include: " + echo " - You must be using DKMS 2.1.0.0 or later to support binaries only" + echo " distribution specific archives." + echo " - Corrupt distribution specific archive" + echo "" + echo "" + exit 2 + fi +elif [ -d "/usr/src/$NAME-$VERSION" ]; then + echo "Loading new $NAME-$VERSION DKMS files..." + dkms add -m $NAME -v $VERSION > /dev/null +fi + +# On 1st installation, let us look for a directory +# in /lib/modules which matches `uname -r`. If none +# is found it is possible that buildd is being used +# and that uname -r is giving us the name of the +# kernel used by the buildd machine. +# +# If this is the case we try to build the kernel +# module for each kernel which has a directory in +# /lib/modules. Furthermore we will have to tell +# DKMS which architecture it should build the module +# for (e.g. if the buildd machine is using a +# 2.6.24-23-xen 64bit kernel). +# +# NOTE: if the headers are not installed then the +# module won't be built, as usual +if [ -z "$UPGRADE" ]; then + echo "First Installation: checking all kernels..." + for KERNEL in $KERNELS; do + if [ ${KERNEL} = ${CURRENT_KERNEL} ]; then + # Kernel found + KERNELS=$CURRENT_KERNEL + break + fi + done +else + KERNELS=$CURRENT_KERNEL +fi + +# Here we look for the most recent kernel so that we can +# build the module for it (in addition to doing it for the +# current kernel. +NEWEST_KERNEL=$(get_newest_kernel "$KERNELS") + +# If the current kernel doesn't come from the host of a chroot +if [ `_is_kernel_name_correct $CURRENT_KERNEL` = "yes" ]; then + # See if it's worth building the module for both the newest kernel + # and for the current kernel + if [ -n "$NEWEST_KERNEL" ] && [ ${CURRENT_KERNEL} != ${NEWEST_KERNEL} ]; then + echo "Building for $CURRENT_KERNEL and $NEWEST_KERNEL" + KERNELS="$CURRENT_KERNEL $NEWEST_KERNEL" + else + echo "Building only for $CURRENT_KERNEL" + fi +# The current kernel is not useful as it's the host's +else + echo "It is likely that $CURRENT_KERNEL belongs to a chroot's host" + + # Let's use only the newest kernel + if [ -n "$NEWEST_KERNEL" ]; then + KERNELS="$NEWEST_KERNEL" + echo "Building only for $NEWEST_KERNEL" + fi +fi + +if [ -n "$ARCH" ]; then + if which lsb_release >/dev/null && [ $(lsb_release -s -i) = "Ubuntu" ]; then + case $ARCH in + amd64) + ARCH="x86_64" + ;; + lpia|i?86) + ARCH="i686" + ;; + esac + fi + echo "Building for architecture $ARCH" + ARCH="-a $ARCH" +fi + +for KERNEL in $KERNELS; do + dkms_status=`dkms status -m $NAME -v $VERSION -k $KERNEL $ARCH` + if [ `echo $KERNEL | grep -c "BOOT"` -gt 0 ]; then + echo "" + echo "Module build and install for $KERNEL was skipped as " + echo "it is a BOOT variant" + continue + fi + + + #if the module isn't yet built, try to build it + if [ `echo $dkms_status | grep -c ": built"` -eq 0 ]; then + if [ ! -L /var/lib/dkms/$NAME/$VERSION/source ]; then + echo "This package appears to be a binaries-only package" + echo " you will not be able to build against kernel $KERNEL" + echo " since the package source was not provided" + continue + fi + if _check_kernel_dir $KERNEL; then + echo "Building initial module for $KERNEL" + set +e + dkms build -m $NAME -v $VERSION -k $KERNEL $ARCH > /dev/null + case $? in + 9) + set -e + echo "Skipped." + continue + ;; + 0) + set -e + echo "Done." + ;; + *) + exit $? + ;; + esac + dkms_status=`dkms status -m $NAME -v $VERSION -k $KERNEL $ARCH` + else + echo "Module build for the currently running kernel was skipped since the" + echo "kernel source for this kernel does not seem to be installed." + fi + fi + + #if the module is built (either pre-built or just now), install it + if [ `echo $dkms_status | grep -c ": built"` -eq 1 ] && + [ `echo $dkms_status | grep -c ": installed"` -eq 0 ]; then + dkms install -m $NAME -v $VERSION -k $KERNEL $ARCH --force + fi +done + diff --git a/drivers/net/ethernet/mft/dkms.conf b/drivers/net/ethernet/mft/dkms.conf new file mode 100644 index 00000000..70362fbc --- /dev/null +++ b/drivers/net/ethernet/mft/dkms.conf @@ -0,0 +1,21 @@ +PACKAGE_NAME=kernel-mft-dkms +PACKAGE_VERSION=4.26.1 +MAKE[0]="make all KPVER=$kernelver" +CLEAN="make -C src/ clean" +BUILT_MODULE_NAME[0]=mst_pci +BUILT_MODULE_NAME[1]=mst_pciconf +BUILT_MODULE_LOCATION[0]=./mst_backward_compatibility/mst_pci +BUILT_MODULE_LOCATION[1]=./mst_backward_compatibility/mst_pciconf +DEST_MODULE_LOCATION[0]="/updates" +DEST_MODULE_LOCATION[1]="/updates" + +if [ $(arch) = aarch64 ]; then + BUILT_MODULE_NAME[2]=bf3_livefish + BUILT_MODULE_LOCATION[2]=./misc_drivers/bf3_livefish/ + DEST_MODULE_LOCATION[2]="/updates" +fi + +#REMAKE_INITRD=yes + +# Rebuild and autoinstall automatically when dkms_autoinstaller runs for a new kernel +AUTOINSTALL="yes" diff --git a/drivers/net/ethernet/mft/misc_drivers/bf3_livefish/Makefile b/drivers/net/ethernet/mft/misc_drivers/bf3_livefish/Makefile new file mode 100644 index 00000000..9bdac08b --- /dev/null +++ b/drivers/net/ethernet/mft/misc_drivers/bf3_livefish/Makefile @@ -0,0 +1,11 @@ +KPVER ?= $(shell uname -r) +KSRC ?= /lib/modules/$(KPVER)/build + +obj-m += bf3_livefish.o + +all: + make -C $(KSRC) M=$(PWD) CONFIG_CTF= CONFIG_CC_STACKPROTECTOR_STRONG= $(WITH_MAKE_PARAMS) modules + +clean: + make -C $(KSRC) M=$(PWD) clean + diff --git a/drivers/net/ethernet/mft/misc_drivers/bf3_livefish/bf3_livefish.c b/drivers/net/ethernet/mft/misc_drivers/bf3_livefish/bf3_livefish.c new file mode 100644 index 00000000..9f6cfd5e --- /dev/null +++ b/drivers/net/ethernet/mft/misc_drivers/bf3_livefish/bf3_livefish.c @@ -0,0 +1,357 @@ +/* + * Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. + * + * This software product is a proprietary product of Nvidia Corporation and its affiliates + * (the "Company") and all right, title, and interest in and to the software + * product, including all associated intellectual property rights, are and + * shall remain exclusively with the Company. + * + * This software product is governed by the End User License Agreement + * provided with the software product. + */ + + + +#include "bf3_livefish.h" +#include +#include +#include +#include +#include +#include +#include +#include + +static __iomem void *hca_va; + + +/* + * A valid I/O must be entirely within CR space and not extend into + * any unmapped areas of CR space. We don't truncate I/O that extends + * past the end of the CR space region (unlike the behavior of, for + * example, simple_read_from_buffer) but instead just call the whole + * I/O invalid. We also enforce 4-byte alignment for all I/O. + */ +static bool valid_range(loff_t offset, size_t len) +{ + if ((offset % 4 != 0) || (len % 4 != 0)) { + // Unaligned, + return false; + } + + if ((offset >= 0) && (offset + len <= CRSPACE_SIZE)) { + // Inside the HCA space. + return true; + } + + return false; +} + + + +static bool set_lock(void) +{ + int retries; + + for (retries = 0; retries < 100; retries++) { + // Reading the lock value from g_gw_arm_nonsecure.lock. + u32 lock_dword = readl_relaxed(hca_va); + if (!(lock_dword & 0x80000000)) { + return true; + } + } + + return false; +} + + + +static void release_lock(void) +{ + // Reading the lock value from g_gw_arm_nonsecure.lock. + u32 lock_dword = readl_relaxed(hca_va); + lock_dword &= 0x7fffffff; + writel_relaxed(lock_dword, hca_va); +} + + + +static bool set_busy(void) +{ + int retries; + + // Set busy bit. + u32 busy_dword = readl_relaxed(hca_va); + busy_dword |= 1UL << 30; + writel_relaxed(busy_dword, hca_va); + + for (retries = 0; retries < 1000; retries++) { + // Reading the bust value from g_gw_arm_nonsecure.busy. + u32 busy = readl_relaxed(hca_va); + if (!(busy & 0x40000000)) { + return true; + } + } + + return false; +} + + + + +static u32 crspace_read(int offset) +{ + u32 data, new_offset; + + if (!set_lock()) { + return -EINVAL; + } + + // Write the address to the GA: g_gw_arm_nonsecure.desc0.addr. + new_offset = offset >> 2; // HW expects addr[25:2] in that register. + writel_relaxed(new_offset, hca_va + 0x18); + + // Set read operation. + writel_relaxed(1, hca_va + 0x0c); + + if (!set_busy()) { + release_lock(); + return -EINVAL; + } + + // Reading the value of the desired address from + // gw_cr_64b.g_gw_arm_nonsecure.desc0.data_31_0 plus offset 0x14. + data = readl_relaxed(hca_va + 0x14); + + release_lock(); + + return data; +} + + + + +static void crspace_write(u32 data, int offset) +{ + u32 new_offset; + + if (!set_lock()) { + return; + } + + // Write the address to the GA: g_gw_arm_nonsecure.desc0.addr. + new_offset = offset >> 2; // HW expects addr[25:2] in that register. + writel_relaxed(new_offset, hca_va + 0x18); + + // Writing the value of the desired address from + // gw_cr_64b.g_gw_arm_nonsecure.desc0.data_31_0 plus offset 0x14. + writel_relaxed(data, hca_va + 0x14); + + // Set write operation. + writel_relaxed(0, hca_va + 0x0c); + + if (!set_busy()) { + release_lock(); + return; + } + + release_lock(); +} + + + + +/* + * Note that you can seek to illegal areas within the livefish device, + * but you won't be able to read or write there. + */ +static loff_t livefish_llseek(struct file *filp, loff_t offset, int whence) +{ + if (offset % 4 != 0) { + return -EINVAL; + } + + return fixed_size_llseek(filp, offset, whence, CRSPACE_SIZE); +} + + + + +static ssize_t livefish_read(struct file *filp, char __user *to, + size_t len, loff_t *ppos) +{ + loff_t pos = *ppos; + size_t counter; + int word; + + if (!valid_range(pos, len)) { + return -EINVAL; + } + + if (len == 0) { + return 0; + } + + for (counter = 0; counter < len; counter += 4, pos += 4) { + word = crspace_read(pos); + + if (put_user(word, (int __user *)(to + counter)) != 0) { + break; + } + } + + *ppos = pos; + + return counter ?: -EFAULT; +} + + + + +static ssize_t livefish_write(struct file *filp, const char __user *from, + size_t len, loff_t *ppos) +{ + loff_t pos = *ppos; + size_t counter; + int word; + + if (!valid_range(pos, len)) { + return -EINVAL; + } + + if (len == 0) { + return 0; + } + + for (counter = 0; counter < len; counter += 4, pos += 4) { + if (get_user(word, (int __user *)(from + counter)) != 0) { + break; + } + + crspace_write(word, pos); + } + + *ppos = pos; + + return counter ?: -EFAULT; +} + + +static const struct file_operations livefish_fops = { + .owner = THIS_MODULE, + .llseek = livefish_llseek, + .read = livefish_read, + .write = livefish_write, +}; + +/* This name causes the correct semantics for the Mellanox MST tools. */ +static struct miscdevice livefish_dev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "bf3-livefish", + .mode = 0600, + .fops = &livefish_fops +}; + + +/* Release any VA or PA mappings that have been set up. */ +static void livefish_cleanup_mappings(void) +{ + if (hca_va) { + iounmap(hca_va); + } +} + + + + +static int livefish_probe(struct platform_device *pdev) +{ + struct acpi_device *acpi_dev = ACPI_COMPANION(&pdev->dev); + const char *hid = acpi_device_hid(acpi_dev); + struct resource *res; + int error = 0; + + // Device ID validation. + if (strcmp(hid, "MLNXBF45") != 0) { + dev_err(&pdev->dev, "Invalid device ID %s\n", hid); + error = -ENODEV; + goto ReturnOnError; + } + + // Find and map the HCA region. + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + error = -ENODEV; + goto ReturnOnError; + } + + if (request_mem_region(res->start, resource_size(res), + "LiveFish (HCA)") == NULL) { + error = -ENODEV; + goto ReturnOnError; + } + + hca_va = ioremap(res->start, resource_size(res)); + + if (!hca_va) { + error = -EINVAL; + goto ReturnOnError; + } + + dev_info(&pdev->dev, "HCA Region PA: 0x%llx Size: 0x%llx\n", + res->start, resource_size(res)); + + error = misc_register(&livefish_dev); + if (error) { + goto ReturnOnError; + } + + dev_info(&pdev->dev, "probed\n"); + + return error; + +ReturnOnError: + livefish_cleanup_mappings(); + return error; +} + + + + +static int livefish_remove(struct platform_device *pdev) +{ + misc_deregister(&livefish_dev); + livefish_cleanup_mappings(); + return 0; +} + +static const struct of_device_id livefish_of_match[] = { + { .compatible = "mellanox,mlxbf-livefish" }, + {}, +}; + +MODULE_DEVICE_TABLE(of, livefish_of_match); + +static const struct acpi_device_id livefish_acpi_match[] = { + { "MLNXBF45", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, livefish_acpi_match); + +static struct platform_driver livefish_driver = { + .driver = { + .name = "mlxbf-livefish", + .of_match_table = livefish_of_match, + .acpi_match_table = ACPI_PTR(livefish_acpi_match), + }, + .probe = livefish_probe, + .remove = livefish_remove, +}; + + +module_platform_driver(livefish_driver); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("BlueField 3 LiveFish driver"); +MODULE_AUTHOR("Itay Avraham "); +MODULE_VERSION(STRINGIFY(DRIVER_VERSION)); diff --git a/drivers/net/ethernet/mft/misc_drivers/bf3_livefish/bf3_livefish.h b/drivers/net/ethernet/mft/misc_drivers/bf3_livefish/bf3_livefish.h new file mode 100644 index 00000000..53025f81 --- /dev/null +++ b/drivers/net/ethernet/mft/misc_drivers/bf3_livefish/bf3_livefish.h @@ -0,0 +1,9 @@ +#ifndef BF3_LIVEFISH_H +#define BF3_LIVEFISH_H + + +#define DRIVER_VERSION 1.0 +#define STRINGIFY(s) #s +#define CRSPACE_SIZE (2 * 1024 * 1024) + +#endif // BF3_LIVEFISH_H diff --git a/drivers/net/ethernet/mft/mst_backward_compatibility/mst_pci/Makefile b/drivers/net/ethernet/mft/mst_backward_compatibility/mst_pci/Makefile new file mode 100644 index 00000000..07213432 --- /dev/null +++ b/drivers/net/ethernet/mft/mst_backward_compatibility/mst_pci/Makefile @@ -0,0 +1,32 @@ +KPVER ?= $(shell uname -r) +KSRC ?= /lib/modules/$(KPVER)/build + +# Oracle Linux OS. +ifneq ($(shell if (echo $(KPVER) | grep -qE 'uek'); then \ + echo "YES"; else echo ""; fi),) +override WITH_MAKE_PARAMS += ctf-dir=$(CWD)/.ctf +endif + +NNT_DRIVER_LOCATION = ../../nnt_driver +PACKAGE_NAME = nnt-driver +PACKAGE_VERSION = 1.0.0 +PACKAGE_RC = 1 + +%: %.in + sed \ + -e 's/@PACKAGE_NAME@/$(PACKAGE_NAME)/g' \ + -e 's/@PACKAGE_VERSION@/$(PACKAGE_VERSION)/g' \ + -e 's/@PACKAGE_RC@/$(PACKAGE_RC)/g' \ + <$< >$@ + +obj-m += mst_pci.o +EXTRA_CFLAGS= -I$(PWD)/$(NNT_DRIVER_LOCATION) +mst_pci-objs += $(NNT_DRIVER_LOCATION)/nnt_device.o $(NNT_DRIVER_LOCATION)/nnt_dma.o $(NNT_DRIVER_LOCATION)/nnt_pci_conf_access.o \ + $(NNT_DRIVER_LOCATION)/nnt_pci_conf_access_no_vsec.o $(NNT_DRIVER_LOCATION)/nnt_memory_access.o \ + $(NNT_DRIVER_LOCATION)/nnt_ioctl.o mst_pci_bc.o + +all: + make -C $(KSRC) M=$(PWD) CONFIG_CTF= CONFIG_CC_STACKPROTECTOR_STRONG= $(WITH_MAKE_PARAMS) modules + +clean: + make -C $(KSRC) M=$(PWD) clean diff --git a/drivers/net/ethernet/mft/mst_backward_compatibility/mst_pci/Module.supported b/drivers/net/ethernet/mft/mst_backward_compatibility/mst_pci/Module.supported new file mode 100644 index 00000000..eac57483 --- /dev/null +++ b/drivers/net/ethernet/mft/mst_backward_compatibility/mst_pci/Module.supported @@ -0,0 +1,3 @@ +mst_pciconf.ko external +mst_pci.ko external +mst_ppc_pci_reset.ko external diff --git a/drivers/net/ethernet/mft/mst_backward_compatibility/mst_pci/mst_pci_bc.c b/drivers/net/ethernet/mft/mst_backward_compatibility/mst_pci/mst_pci_bc.c new file mode 100644 index 00000000..701503bf --- /dev/null +++ b/drivers/net/ethernet/mft/mst_backward_compatibility/mst_pci/mst_pci_bc.c @@ -0,0 +1,436 @@ +#include +#include +#include +#include +#include +#include "nnt_ioctl.h" +#include "nnt_defs.h" +#include "nnt_device.h" +#include "nnt_ioctl_defs.h" +#include "nnt_pci_conf_access.h" +#include "mst_pci_bc.h" + +MODULE_AUTHOR("Itay Avraham "); +MODULE_DESCRIPTION("NNT Linux driver (NVIDIA® networking tools driver), this is the backward compatibility driver"); +MODULE_LICENSE("Dual BSD/GPL"); + +struct driver_info nnt_driver_info; +static int major_number = -1; +static char* name = "mst_pci"; + +#define INIT PCI_INIT +#define STOP PCI_STOP +#define PCI_PARAMS_ PCI_PARAMS +#define CONNECTX_WA PCI_CONNECTX_WA + +struct mst_device_data +{ + char buffer[MST_BC_BUFFER_SIZE]; + int buffer_used; +}; + +static struct mst_device_data mst_devices[MST_BC_MAX_MINOR]; + +static int mst_pci_bc_open(struct inode* inode, struct file* file) +{ + if (file->private_data) + { + return 0; + } + + set_private_data_open(file); + + return 0; +} + +static ssize_t mst_pci_bc_read(struct file* file, char* buf, size_t count, loff_t* f_pos) +{ + struct mst_device_data* mst_device = NULL; + struct nnt_device* nnt_device = NULL; + int* buffer_used = NULL; + char* buffer = NULL; + int minor = 0; + int error = 0; + + /* Get the nnt device structure */ + error = get_nnt_device(file, &nnt_device); + if (error) + { + minor = iminor(file_inode(file)); + mst_device = &mst_devices[minor]; + buffer = mst_device->buffer; + buffer_used = &mst_device->buffer_used; + } + else + { + buffer = nnt_device->buffer_bc; + buffer_used = &nnt_device->buffer_used_bc; + error = mutex_lock_nnt(file); + CHECK_ERROR(error); + } + + if (*f_pos >= *buffer_used) + { + count = 0; + goto MutexUnlock; + } + + if (*f_pos + count > *buffer_used) + { + count = *buffer_used - *f_pos; + } + + if (copy_to_user(buf, buffer + *f_pos, count)) + { + count = -EFAULT; + goto MutexUnlock; + } + + *f_pos += count; + +MutexUnlock: + if (nnt_device) + { + mutex_unlock_nnt(file); + } + +ReturnOnFinished: + return count; +} + +static ssize_t mst_pci_bc_write(struct file* file, const char* buf, size_t count, loff_t* f_pos) +{ + struct mst_device_data* mst_device = NULL; + struct nnt_device* nnt_device = NULL; + int* buffer_used = NULL; + char* buffer = NULL; + int minor = 0; + int error = 0; + + /* Get the nnt device structure */ + error = get_nnt_device(file, &nnt_device); + if (error) + { + minor = iminor(file_inode(file)); + mst_device = &mst_devices[minor]; + buffer = mst_device->buffer; + buffer_used = &mst_device->buffer_used; + } + else + { + buffer = nnt_device->buffer_bc; + buffer_used = &nnt_device->buffer_used_bc; + error = mutex_lock_nnt(file); + CHECK_ERROR(error); + } + + if (*f_pos >= MST_BC_BUFFER_SIZE) + { + count = 0; + goto MutexUnlock; + } + + if (*f_pos + count > MST_BC_BUFFER_SIZE) + { + count = MST_BC_BUFFER_SIZE - *f_pos; + } + + if (copy_from_user(buffer + *f_pos, buf, count)) + { + count = -EFAULT; + goto MutexUnlock; + } + + *f_pos += count; + + if (*buffer_used < *f_pos) + { + *buffer_used = *f_pos; + } + +MutexUnlock: + if (nnt_device) + { + mutex_unlock_nnt(file); + } + +ReturnOnFinished: + return count; +} + +static inline int noncached_address(unsigned long addr) +{ + return addr >= __pa(high_memory); +} + +static int mst_pci_mmap(struct file* file, struct vm_area_struct* vma) +{ + struct nnt_device* nnt_device = NULL; + unsigned long long offset = 0; + unsigned long vsize = 0; + unsigned long off = 0; + int error = 0; + + /* Get the nnt device structure */ + error = get_nnt_device(file, &nnt_device); + if (error) + { + goto ReturnOnFinished; + } + + off = vma->vm_pgoff << PAGE_SHIFT; + vsize = vma->vm_end - vma->vm_start; + + if ((nnt_device->device_pci.bar_size <= off) || (nnt_device->device_pci.bar_size < off + vsize)) + { + error = -EINVAL; + goto ReturnOnFinished; + } + + offset = nnt_device->device_pci.bar_address + off; + + /* Accessing memory above the top the kernel knows about or through + a file pointer that was marked O_SYNC will be done non-cached. */ + if (noncached_address(offset) || (file->f_flags & O_SYNC)) + { + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + } + + error = io_remap_pfn_range(vma, vma->vm_start, offset >> PAGE_SHIFT, vsize, vma->vm_page_prot); + +ReturnOnFinished: + return error; +} + +static long ioctl(struct file* file, unsigned int command, unsigned long argument) +{ + void* user_buffer = (void*)argument; + struct nnt_device* nnt_device = NULL; + int error = 0; + + /* By convention, any user gets read access + * and is allowed to use the device. + * Commands with no direction are administration + * commands, and you need write permission + * for this */ + + if (_IOC_DIR(command) == _IOC_NONE) + { + if (!(file->f_mode & FMODE_WRITE)) + { + return -EPERM; + } + } + else + { + if (!(file->f_mode & FMODE_READ)) + { + return -EPERM; + } + } + + if (command != INIT) + { + error = mutex_lock_nnt(file); + if (error) + { + return 0; + } + CHECK_ERROR(error); + + /* Get the nnt device structure */ + error = get_nnt_device(file, &nnt_device); + if (error) + { + error = 0; + goto ReturnOnFinished; + } + } + + switch (command) + { + case INIT: + { + struct mst_pci_init_st mst_init; + struct pci_bus* bus = NULL; + + /* Copy the request from user space. */ + if (copy_from_user(&mst_init, user_buffer, sizeof(struct mst_pci_init_st))) + { + return -EFAULT; + } + + error = set_private_data_bc(file, mst_init.bus, mst_init.devfn, mst_init.domain); + if (error) + { + return 0; + } + + error = mutex_lock_nnt(file); + CHECK_ERROR(error); + + /* Get the nnt device structure */ + error = get_nnt_device(file, &nnt_device); + if (error) + { + goto ReturnOnFinished; + } + + bus = pci_find_bus(mst_init.domain, mst_init.bus); + + if (!bus) + { + printk(KERN_ERR "unable to find pci bus for domain: %x and bus: %x\n", mst_init.domain, mst_init.bus); + error = -ENXIO; + goto ReturnOnFinished; + } + + nnt_device->pci_device = NULL; + nnt_device->pci_device = pci_get_slot(bus, mst_init.devfn); + + if (!nnt_device->pci_device) + { + printk(KERN_ERR "missing pci device"); + error = -ENXIO; + goto ReturnOnFinished; + } + + if (mst_init.bar >= DEVICE_COUNT_RESOURCE) + { + printk(KERN_ERR "bar offset is too large"); + error = -ENXIO; + goto ReturnOnFinished; + } + + nnt_device->device_pci.bar_address = nnt_device->pci_device->resource[mst_init.bar].start; + nnt_device->device_pci.bar_size = nnt_device->pci_device->resource[mst_init.bar].end + 1 - + nnt_device->pci_device->resource[mst_init.bar].start; + + if (nnt_device->device_pci.bar_size == 1) + { + nnt_device->device_pci.bar_size = 0; + } + + nnt_device->buffer_used_bc = 0; + break; + } + + case PCI_PARAMS_: + { + struct mst_pci_params_st params; + params.bar = nnt_device->device_pci.bar_address; + params.size = nnt_device->device_pci.bar_size; + + if (copy_to_user(user_buffer, ¶ms, sizeof(struct mst_pci_params_st))) + { + error = -EFAULT; + goto ReturnOnFinished; + } + + break; + } + case CONNECTX_WA: + { + struct nnt_connectx_wa connectx_wa; + error = pci_connectx_wa(&connectx_wa, nnt_device); + + /* Copy the data to the user space. */ + if (copy_to_user(user_buffer, &connectx_wa, sizeof(struct nnt_connectx_wa)) != 0) + { + error = -EFAULT; + goto ReturnOnFinished; + } + + break; + } + case STOP: + error = destroy_nnt_device_bc(nnt_device); + break; + default: + error = -EINVAL; + break; + } + +ReturnOnFinished: + mutex_unlock_nnt(file); + + return error; +} + +static int mst_release(struct inode* inode, struct file* file) +{ + struct nnt_device* nnt_device = NULL; + int error = 0; + + /* Get the nnt device structure */ + error = get_nnt_device(file, &nnt_device); + if (error) + { + error = 0; + goto ReturnOnFinished; + } + + if (nnt_device->memory_device.connectx_wa_slot_p1) + { + unsigned int mask = 0; + + error = mutex_lock_nnt(file); + CHECK_ERROR(error); + + mask = ~(1 << (nnt_device->memory_device.connectx_wa_slot_p1 - 1)); + + nnt_device->memory_device.connectx_wa_slot_p1 &= mask; // Fix me + + nnt_device->memory_device.connectx_wa_slot_p1 = 0; + mutex_unlock_nnt(file); + } + +ReturnOnFinished: + return 0; +} + +struct file_operations fop = {.unlocked_ioctl = ioctl, + .open = mst_pci_bc_open, + .write = mst_pci_bc_write, + .read = mst_pci_bc_read, + .mmap = mst_pci_mmap, + .release = mst_release, + .owner = THIS_MODULE}; + +int with_unknown = 0; + +module_param(with_unknown, int, S_IRUSR | S_IWUSR); + +static int __init mst_pci_init_module(void) +{ + dev_t device_number = -1; + int is_alloc_chrdev_region = 0; + int error = 0; + + /* Allocate char driver region and assign major number */ + major_number = register_chrdev(0, name, &fop); + if (major_number <= 0) + { + printk(KERN_ERR "Unable to register character mst pci driver.\n"); + error = -EINVAL; + } + + /* Create device files for MFT. */ + error = create_nnt_devices(device_number, is_alloc_chrdev_region, &fop, NNT_PCI_DEVICES, NNT_MELLANOX_PCI_VENDOR, + with_unknown) || + create_nnt_devices(device_number, is_alloc_chrdev_region, &fop, NNT_PCI_DEVICES, NNT_NVIDIA_PCI_VENDOR, + with_unknown); + + return error; +} + +static void __exit mst_pci_cleanup_module(void) +{ + int is_alloc_chrdev_region = 0; + + destroy_nnt_devices(is_alloc_chrdev_region); + unregister_chrdev(major_number, name); +} + +module_init(mst_pci_init_module); +module_exit(mst_pci_cleanup_module); diff --git a/drivers/net/ethernet/mft/mst_backward_compatibility/mst_pci/mst_pci_bc.h b/drivers/net/ethernet/mft/mst_backward_compatibility/mst_pci/mst_pci_bc.h new file mode 100644 index 00000000..deb109a0 --- /dev/null +++ b/drivers/net/ethernet/mft/mst_backward_compatibility/mst_pci/mst_pci_bc.h @@ -0,0 +1,31 @@ +#ifndef MST_PCI_H +#define MST_PCI_H + +/* These will be specific for PCI */ +#define PCI_MAGIC 0xD1 + +#define PCI_INIT _IOC(_IOC_NONE,PCI_MAGIC,0,sizeof(struct mst_pci_init_st)) +struct mst_pci_init_st { + unsigned int domain; + unsigned int bus; + unsigned int devfn; + int bar; +}; + +#define PCI_STOP _IOC(_IOC_NONE,PCI_MAGIC,1,0) + +#define PCI_PARAMS _IOR(PCI_MAGIC,2, struct mst_pci_params_st) + +struct mst_pci_params_st { + unsigned long long __attribute__((packed)) bar; + unsigned long long __attribute__((packed)) size; +}; + + +#define CONNECTX_WA_BASE 0xf0384 // SEM BASE ADDR. SEM 0xf0380 is reserved for external tools usage. +#define CONNECTX_WA_SIZE 3 // Size in entries + +#define PCI_CONNECTX_WA _IOR(PCI_MAGIC,3, u_int32_t) + + +#endif diff --git a/drivers/net/ethernet/mft/mst_backward_compatibility/mst_pciconf/Makefile b/drivers/net/ethernet/mft/mst_backward_compatibility/mst_pciconf/Makefile new file mode 100644 index 00000000..646a8693 --- /dev/null +++ b/drivers/net/ethernet/mft/mst_backward_compatibility/mst_pciconf/Makefile @@ -0,0 +1,32 @@ +KPVER ?= $(shell uname -r) +KSRC ?= /lib/modules/$(KPVER)/build + +# Oracle Linux OS. +ifneq ($(shell if (echo $(KPVER) | grep -qE 'uek'); then \ + echo "YES"; else echo ""; fi),) +override WITH_MAKE_PARAMS += ctf-dir=$(CWD)/.ctf +endif + +NNT_DRIVER_LOCATION = ../../nnt_driver +PACKAGE_NAME = nnt-driver +PACKAGE_VERSION = 1.0.0 +PACKAGE_RC = 1 + +%: %.in + sed \ + -e 's/@PACKAGE_NAME@/$(PACKAGE_NAME)/g' \ + -e 's/@PACKAGE_VERSION@/$(PACKAGE_VERSION)/g' \ + -e 's/@PACKAGE_RC@/$(PACKAGE_RC)/g' \ + <$< >$@ + +obj-m += mst_pciconf.o +EXTRA_CFLAGS= -I$(PWD)/$(NNT_DRIVER_LOCATION) +mst_pciconf-objs += $(NNT_DRIVER_LOCATION)/nnt_device.o $(NNT_DRIVER_LOCATION)/nnt_dma.o $(NNT_DRIVER_LOCATION)/nnt_pci_conf_access.o \ + $(NNT_DRIVER_LOCATION)/nnt_pci_conf_access_no_vsec.o $(NNT_DRIVER_LOCATION)/nnt_memory_access.o \ + $(NNT_DRIVER_LOCATION)/nnt_ioctl.o mst_pciconf_bc.o + +all: + make -C $(KSRC) M=$(PWD) CONFIG_CTF= CONFIG_CC_STACKPROTECTOR_STRONG= $(WITH_MAKE_PARAMS) modules + +clean: + make -C $(KSRC) M=$(PWD) clean diff --git a/drivers/net/ethernet/mft/mst_backward_compatibility/mst_pciconf/Module.supported b/drivers/net/ethernet/mft/mst_backward_compatibility/mst_pciconf/Module.supported new file mode 100644 index 00000000..eac57483 --- /dev/null +++ b/drivers/net/ethernet/mft/mst_backward_compatibility/mst_pciconf/Module.supported @@ -0,0 +1,3 @@ +mst_pciconf.ko external +mst_pci.ko external +mst_ppc_pci_reset.ko external diff --git a/drivers/net/ethernet/mft/mst_backward_compatibility/mst_pciconf/mst_pciconf_bc.c b/drivers/net/ethernet/mft/mst_backward_compatibility/mst_pciconf/mst_pciconf_bc.c new file mode 100644 index 00000000..981d4ca9 --- /dev/null +++ b/drivers/net/ethernet/mft/mst_backward_compatibility/mst_pciconf/mst_pciconf_bc.c @@ -0,0 +1,635 @@ +#include +#include +#include +#include +#include +#include "nnt_ioctl.h" +#include "nnt_defs.h" +#include "nnt_device.h" +#include "nnt_ioctl_defs.h" +#include "nnt_pci_conf_access.h" +#include "mst_pciconf_bc.h" + +MODULE_AUTHOR("Itay Avraham "); +MODULE_DESCRIPTION("NNT Linux driver (NVIDIA® networking tools driver), this is the backward compatibility driver"); +MODULE_LICENSE("Dual BSD/GPL"); + +struct driver_info nnt_driver_info; +static int major_number = -1; +static char* name = "mst_pciconf"; + +#define INIT PCICONF_INIT +#define STOP PCICONF_STOP +#define READ4 PCICONF_READ4 +#define READ4_NEW PCICONF_READ4_NEW +#define WRITE4 PCICONF_WRITE4 +#define WRITE4_NEW PCICONF_WRITE4_NEW +#define MODIFY PCICONF_MODIFY +#define READ4_BUFFER PCICONF_READ4_BUFFER +#define READ4_BUFFER_EX PCICONF_READ4_BUFFER_EX +#define WRITE4_BUFFER PCICONF_WRITE4_BUFFER +#define MST_PARAMS PCICONF_MST_PARAMS +#define MST_META_DATA PCICONF_MST_META_DATA +#define GET_DMA_PAGES PCICONF_GET_DMA_PAGES +#define RELEASE_DMA_PAGES PCICONF_RELEASE_DMA_PAGES +#define READ_DWORD_FROM_CONFIG_SPACE PCICONF_READ_DWORD_FROM_CONFIG_SPACE + +static int mst_pciconf_bc_open(struct inode* inode, struct file* file) +{ + if (file->private_data) + { + return 0; + } + + set_private_data_open(file); + + return 0; +} + +static ssize_t mst_pciconf_bc_read(struct file* file, char* buf, size_t count, loff_t* f_pos) +{ + struct nnt_device* nnt_device = NULL; + int error = 0; + + /* Get the nnt device structure */ + error = get_nnt_device(file, &nnt_device); + if (error) + { + count = -EFAULT; + goto ReturnOnFinished; + } + + error = mutex_lock_nnt(file); + + if (*f_pos >= nnt_device->buffer_used_bc) + { + count = 0; + goto MutexUnlock; + } + + if (*f_pos + count > nnt_device->buffer_used_bc) + { + count = nnt_device->buffer_used_bc - *f_pos; + } + + if (copy_to_user(buf, nnt_device->buffer_bc + *f_pos, count)) + { + count = -EFAULT; + goto MutexUnlock; + } + + *f_pos += count; + +MutexUnlock: + mutex_unlock_nnt(file); +ReturnOnFinished: + return count; +} + +static ssize_t mst_pciconf_bc_write(struct file* file, const char* buf, size_t count, loff_t* f_pos) +{ + struct nnt_device* nnt_device = NULL; + int error = 0; + + /* Get the nnt device structure */ + error = get_nnt_device(file, &nnt_device); + if (error) + { + count = -EFAULT; + goto ReturnOnFinished; + } + + error = mutex_lock_nnt(file); + + if (*f_pos >= MST_BC_BUFFER_SIZE) + { + count = 0; + goto MutexUnlock; + } + + if (*f_pos + count > MST_BC_BUFFER_SIZE) + { + count = MST_BC_BUFFER_SIZE - *f_pos; + } + + if (copy_from_user(nnt_device->buffer_bc + *f_pos, buf, count)) + { + count = -EFAULT; + goto MutexUnlock; + } + + *f_pos += count; + + if (nnt_device->buffer_used_bc < *f_pos) + { + nnt_device->buffer_used_bc = *f_pos; + } + +MutexUnlock: + mutex_unlock_nnt(file); +ReturnOnFinished: + return count; +} + +static long ioctl(struct file* file, unsigned int command, unsigned long argument) +{ + void* user_buffer = (void*)argument; + struct nnt_device* nnt_device = NULL; + int error = 0; + + /* By convention, any user gets read access + * and is allowed to use the device. + * Commands with no direction are administration + * commands, and you need write permission + * for this */ + + if (_IOC_DIR(command) == _IOC_NONE) + { + if (!(file->f_mode & FMODE_WRITE)) + { + return -EPERM; + } + } + else + { + if (!(file->f_mode & FMODE_READ)) + { + return -EPERM; + } + } + + if (command != INIT) + { + error = mutex_lock_nnt(file); + CHECK_ERROR(error); + + /* Get the nnt device structure */ + error = get_nnt_device(file, &nnt_device); + if (error) + { + goto ReturnOnFinished; + } + } + + switch (command) + { + case INIT: + { + struct nnt_pciconf_init nnt_init; + struct mst_pciconf_init_st mst_init; + struct nnt_device* nnt_device = NULL; + + if (copy_from_user(&mst_init, user_buffer, sizeof(struct mst_pciconf_init_st))) + { + error = -EFAULT; + goto ReturnOnFinished; + } + + error = set_private_data_bc(file, mst_init.bus, mst_init.devfn, mst_init.domain); + if (error) + { + goto ReturnOnFinished; + } + + error = mutex_lock_nnt(file); + CHECK_ERROR(error); + + /* Get the nnt device structure */ + error = get_nnt_device(file, &nnt_device); + if (error) + { + goto ReturnOnFinished; + } + + nnt_init.address_register = mst_init.addr_reg; + nnt_init.address_data_register = mst_init.data_reg; + + /* Truncate to 0 length on open for writing. */ + if (file->f_flags & O_APPEND) + { + file->f_pos = nnt_device->buffer_used_bc; + } + else if ((file->f_flags & O_TRUNC) || (file->f_flags & O_WRONLY)) + { + nnt_device->buffer_used_bc = 0; + } + + error = nnt_device->access.init(nnt_device); + break; + } + case WRITE4: + { + struct nnt_rw_operation rw_operation; + struct mst_write4_st mst_write; + + /* Copy the request from user space. */ + if (copy_from_user(&mst_write, user_buffer, sizeof(struct mst_write4_st))) + { + error = -EFAULT; + goto ReturnOnFinished; + } + rw_operation.data[0] = mst_write.data; + rw_operation.offset = mst_write.offset; + rw_operation.size = 4; + + error = nnt_device->access.write(nnt_device, &rw_operation); + + break; + } + case WRITE4_NEW: + { + struct nnt_rw_operation rw_operation; + struct mst_write4_new_st mst_write; + + /* Copy the request from user space. */ + if (copy_from_user(&mst_write, user_buffer, sizeof(struct mst_write4_new_st))) + { + error = -EFAULT; + goto ReturnOnFinished; + } + + rw_operation.data[0] = mst_write.data; + rw_operation.offset = mst_write.offset; + rw_operation.address_space = mst_write.address_space; + rw_operation.size = 4; + + error = nnt_device->access.write(nnt_device, &rw_operation); + + break; + } + case WRITE4_BUFFER: + { + struct mst_write4_buffer_st mst_write; + + /* Copy the request from user space. */ + if (copy_from_user(&mst_write, user_buffer, sizeof(struct mst_write4_buffer_st))) + { + error = -EFAULT; + goto ReturnOnFinished; + } + + error = nnt_device->access.write(nnt_device, (struct nnt_rw_operation*)&mst_write); + if (error) + { + goto ReturnOnFinished; + } + + /* No error, return the requested data length. */ + error = mst_write.size; + + break; + } + case READ4: + { + struct nnt_rw_operation rw_operation; + struct mst_read4_st mst_read; + + /* Copy the request from user space. */ + if (copy_from_user(&mst_read, user_buffer, sizeof(struct mst_read4_st))) + { + error = -EFAULT; + goto ReturnOnFinished; + } + + rw_operation.offset = mst_read.offset; + rw_operation.size = 4; + + error = nnt_device->access.read(nnt_device, &rw_operation); + if (error) + { + goto ReturnOnFinished; + } + + mst_read.data = rw_operation.data[0]; + + /* Copy the data to the user space. */ + if (copy_to_user(user_buffer, &mst_read, sizeof(struct mst_read4_st)) != 0) + { + error = -EFAULT; + goto ReturnOnFinished; + } + break; + } + case READ4_NEW: + { + struct nnt_rw_operation rw_operation; + struct mst_read4_new_st mst_read; + + /* Copy the request from user space. */ + if (copy_from_user(&mst_read, user_buffer, sizeof(struct mst_read4_new_st))) + { + error = -EFAULT; + goto ReturnOnFinished; + } + + rw_operation.offset = mst_read.offset; + rw_operation.address_space = mst_read.address_space; + rw_operation.size = 4; + error = nnt_device->access.read(nnt_device, &rw_operation); + if (error) + { + goto ReturnOnFinished; + } + + mst_read.data = rw_operation.data[0]; + + /* Copy the data to the user space. */ + if (copy_to_user(user_buffer, &mst_read, sizeof(struct mst_read4_new_st)) != 0) + { + error = -EFAULT; + goto ReturnOnFinished; + } + + break; + } + case READ4_BUFFER_EX: + case READ4_BUFFER: + { + struct mst_read4_buffer_st mst_read; + + /* Copy the request from user space. */ + if (copy_from_user(&mst_read, user_buffer, sizeof(struct mst_read4_buffer_st))) + { + error = -EFAULT; + goto ReturnOnFinished; + } + + error = nnt_device->access.read(nnt_device, (struct nnt_rw_operation*)&mst_read); + if (error) + { + goto ReturnOnFinished; + } + + /* Copy the data to the user space. */ + if (copy_to_user(user_buffer, &mst_read, sizeof(struct mst_read4_buffer_st)) != 0) + { + error = -EFAULT; + goto ReturnOnFinished; + } + + /* No error, return the requested data length. */ + error = mst_read.size; + + break; + } + case PCICONF_VPD_READ4: + { + int vpd_default_timeout = 2000; + struct mst_vpd_read4_st mst_vpd_read; + struct nnt_vpd nnt_vpd; + + if (!nnt_device->vpd_capability_address) + { + printk(KERN_ERR "Device %s not support Vital Product Data\n", nnt_device->device_name); + error = -ENODEV; + goto ReturnOnFinished; + } + + /* Copy the request from user space. */ + if (copy_from_user(&mst_vpd_read, user_buffer, sizeof(struct mst_vpd_read4_st)) != 0) + { + error = -EFAULT; + goto ReturnOnFinished; + } + + nnt_vpd.offset = mst_vpd_read.offset; + nnt_vpd.data = mst_vpd_read.data; + + if (!nnt_vpd.timeout) + { + nnt_vpd.timeout = vpd_default_timeout; + } + + error = vpd_read(&nnt_vpd, nnt_device); + if (error) + { + goto ReturnOnFinished; + } + + mst_vpd_read.offset = nnt_vpd.offset; + mst_vpd_read.data = nnt_vpd.data; + + /* Copy the data to the user space. */ + if (copy_to_user(user_buffer, &mst_vpd_read, sizeof(struct mst_vpd_read4_st)) != 0) + { + error = -EFAULT; + goto ReturnOnFinished; + } + + break; + } + case PCICONF_VPD_WRITE4: + { + int vpd_default_timeout = 2000; + struct mst_vpd_write4_st mst_vpd_write; + struct nnt_vpd nnt_vpd; + + if (!nnt_device->vpd_capability_address) + { + printk(KERN_ERR "Device %s not support Vital Product Data\n", nnt_device->device_name); + error = -ENODEV; + goto ReturnOnFinished; + } + + /* Copy the request from user space. */ + if (copy_from_user(&mst_vpd_write, user_buffer, sizeof(struct mst_vpd_write4_st)) != 0) + { + error = -EFAULT; + goto ReturnOnFinished; + } + + nnt_vpd.offset = mst_vpd_write.offset; + nnt_vpd.data = mst_vpd_write.data; + + if (!nnt_vpd.timeout) + { + nnt_vpd.timeout = vpd_default_timeout; + } + + error = vpd_write(&nnt_vpd, nnt_device); + if (error) + { + goto ReturnOnFinished; + } + + mst_vpd_write.offset = nnt_vpd.offset; + mst_vpd_write.data = nnt_vpd.data; + + /* Copy the data to the user space. */ + if (copy_to_user(user_buffer, &mst_vpd_write, sizeof(struct mst_vpd_write4_st)) != 0) + { + error = -EFAULT; + goto ReturnOnFinished; + } + + break; + } + case GET_DMA_PAGES: + { + error = dma_pages_ioctl(NNT_GET_DMA_PAGES, user_buffer, nnt_device); + break; + } + case RELEASE_DMA_PAGES: + { + error = dma_pages_ioctl(NNT_RELEASE_DMA_PAGES, user_buffer, nnt_device); + break; + } + case READ_DWORD_FROM_CONFIG_SPACE: + { + struct nnt_read_dword_from_config_space nnt_read_from_cspace = {0}; + + /* Copy the request from user space. */ + if (copy_from_user(&nnt_read_from_cspace, user_buffer, sizeof(struct nnt_read_dword_from_config_space))) + { + error = -EFAULT; + goto ReturnOnFinished; + } + + /* Read the dword. */ + if (read_dword(&nnt_read_from_cspace, nnt_device)) + { + goto ReturnOnFinished; + } + + /* Copy the data to the user space. */ + if (copy_to_user(user_buffer, &nnt_read_from_cspace, sizeof(struct nnt_read_dword_from_config_space)) != 0) + { + error = -EFAULT; + goto ReturnOnFinished; + } + + break; + } + case MST_META_DATA: + { + struct mst_meta_data meta_data; + struct mst_hdr hdr; + memset(&meta_data, 0, sizeof(meta_data)); + + /* Copy the request from user space. */ + if (copy_from_user(&hdr, user_buffer, sizeof(struct mst_hdr))) + { + error = -EFAULT; + goto ReturnOnFinished; + } + + if (hdr.payload_version_major != MST_META_DATA_VERSION_MAJOR || hdr.payload_len < sizeof(meta_data.data)) + { + error = -EINVAL; + goto ReturnOnFinished; + } + // fill meta_data hdr + meta_data.hdr.hdr_version = MST_HDR_VERSION; + meta_data.hdr.hdr_len = sizeof(meta_data.hdr); + meta_data.hdr.payload_len = sizeof(meta_data.data); + meta_data.hdr.payload_version_major = MST_META_DATA_VERSION_MAJOR; + meta_data.hdr.payload_version_minor = MST_META_DATA_VERSION_MINOR; + // fill payload + meta_data.data.api_version_major = MST_API_VERSION_MAJOR; + meta_data.data.api_version_minor = MST_API_VERSION_MINOR; + + /* Copy the data to the user space. */ + if (copy_to_user(user_buffer, &meta_data, sizeof(meta_data)) != 0) + { + error = -EFAULT; + goto ReturnOnFinished; + } + + break; + } + case MST_PARAMS: + { + struct nnt_device_parameters nnt_parameters; + struct mst_params_st mst_params; + + error = get_nnt_device_parameters(&nnt_parameters, nnt_device); + if (error) + { + goto ReturnOnFinished; + } + + mst_params.bus = nnt_parameters.bus; + mst_params.bar = 0; + mst_params.domain = nnt_parameters.domain; + mst_params.func = nnt_parameters.function; + mst_params.slot = nnt_parameters.slot; + mst_params.device = nnt_parameters.device; + mst_params.vendor = nnt_parameters.vendor; + mst_params.subsystem_device = nnt_parameters.subsystem_device; + mst_params.subsystem_vendor = nnt_parameters.subsystem_vendor; + mst_params.vendor_specific_cap = nnt_parameters.vendor_specific_capability; + mst_params.multifunction = nnt_parameters.multifunction; + mst_params.vsec_cap_mask = nnt_parameters.vsec_capability_mask; + + /* Copy the data to the user space. */ + if (copy_to_user(user_buffer, &mst_params, sizeof(struct mst_params_st)) != 0) + { + error = -EFAULT; + goto ReturnOnFinished; + } + + break; + } + case STOP: + { + error = destroy_nnt_device_bc(nnt_device); + break; + } + case PCICONF_DMA_PROPS: + case PCICONF_MEM_ACCESS: + case MODIFY: + break; + default: + error = -EINVAL; + break; + } + +ReturnOnFinished: + mutex_unlock_nnt(file); + + return error; +} + +struct file_operations fop = {.unlocked_ioctl = ioctl, + .open = mst_pciconf_bc_open, + .write = mst_pciconf_bc_write, + .read = mst_pciconf_bc_read, + .owner = THIS_MODULE}; + +int with_unknown = 0; + +module_param(with_unknown, int, S_IRUSR | S_IWUSR); + +static int __init mst_pciconf_init_module(void) +{ + dev_t device_number = -1; + int is_alloc_chrdev_region = 0; + int error = 0; + + /* Allocate char driver region and assign major number */ + major_number = register_chrdev(0, name, &fop); + if (major_number <= 0) + { + printk(KERN_ERR "Unable to register character mst pciconf driver.\n"); + error = -EINVAL; + } + + /* Create device files for MFT. */ + error = create_nnt_devices(device_number, is_alloc_chrdev_region, &fop, NNT_PCICONF_DEVICES, + NNT_MELLANOX_PCI_VENDOR, with_unknown) || + create_nnt_devices(device_number, is_alloc_chrdev_region, &fop, NNT_PCI_DEVICES, NNT_NVIDIA_PCI_VENDOR, + with_unknown); + + return error; +} + +static void __exit mst_pciconf_cleanup_module(void) +{ + int is_alloc_chrdev_region = 0; + + destroy_nnt_devices(is_alloc_chrdev_region); + unregister_chrdev(major_number, name); +} + +module_init(mst_pciconf_init_module); +module_exit(mst_pciconf_cleanup_module); diff --git a/drivers/net/ethernet/mft/mst_backward_compatibility/mst_pciconf/mst_pciconf_bc.h b/drivers/net/ethernet/mft/mst_backward_compatibility/mst_pciconf/mst_pciconf_bc.h new file mode 100644 index 00000000..a5266d70 --- /dev/null +++ b/drivers/net/ethernet/mft/mst_backward_compatibility/mst_pciconf/mst_pciconf_bc.h @@ -0,0 +1,193 @@ +#ifndef MST_PCICONF_H +#define MST_PCICONF_H + + +/* These will be specific for PCI CONF*/ +#define PCICONF_MAGIC 0xD2 +#define PCICONF_MAX_BUFFER_SIZE 256 +#define PCICONF_MAX_MEMACCESS_SIZE 1024 +#define PCICONF_MAX_PAGES_SIZE 8 +#define PCICONF_CAP_VEC_LEN 16 + +/* Versions */ +#define MST_HDR_VERSION 1 +#define MST_META_DATA_VERSION_MAJOR 1 +#define MST_META_DATA_VERSION_MINOR 0 +#define MST_API_VERSION_MAJOR 1 +#define MST_API_VERSION_MINOR 0 + +/* Common Structs*/ +struct mst_hdr { + unsigned short hdr_version; + unsigned short hdr_len; + unsigned short payload_version_major; + unsigned int payload_version_minor; + unsigned int payload_len; +}; + + +#define PCICONF_INIT _IOC(_IOC_NONE,PCICONF_MAGIC,0,sizeof(struct mst_pciconf_init_st)) +struct mst_pciconf_init_st { + unsigned int domain; + unsigned int bus; + unsigned int devfn; + /* Byte offsets in configuration space */ + unsigned int addr_reg; + unsigned int data_reg; +}; + +#define PCICONF_STOP _IOC (_IOC_NONE,PCICONF_MAGIC,1,0) + +#define PCICONF_READ4 _IOR (PCICONF_MAGIC,1,struct mst_read4_st) +struct mst_read4_st { + unsigned int offset; + unsigned int data; /*OUT*/ +}; + +#define PCICONF_WRITE4 _IOW (PCICONF_MAGIC,2,struct mst_write4_st) +struct mst_write4_st { + unsigned int offset; + unsigned int data; +}; + + +#define PCICONF_MODIFY _IOWR(PCICONF_MAGIC,3,struct mst_modify_st) +struct mst_modify_st { + unsigned int address_space; + unsigned int offset; + unsigned int data; + unsigned int mask; + unsigned int old_data; /*OUT*/ +}; + +#define PCICONF_READ4_BUFFER _IOR (PCICONF_MAGIC,4,struct mst_read4_st) +#define PCICONF_READ4_BUFFER_EX _IOR (PCICONF_MAGIC,4,struct mst_read4_buffer_st) +struct mst_read4_buffer_st { + unsigned int address_space; + unsigned int offset; + int size; + unsigned int data[PCICONF_MAX_BUFFER_SIZE/4]; /*OUT*/ +}; + +#define PCICONF_WRITE4_BUFFER _IOW (PCICONF_MAGIC,5,struct mst_write4_buffer_st) +struct mst_write4_buffer_st { + unsigned int address_space; + unsigned int offset; + int size; + unsigned int data[PCICONF_MAX_BUFFER_SIZE/4]; /*IN*/ +}; + +#define PCICONF_MST_PARAMS _IOR (PCICONF_MAGIC,6,struct mst_params_st) +struct mst_params_st { + unsigned int domain; + unsigned int bus; + unsigned int slot; + unsigned int func; + unsigned int bar; + unsigned int device; + unsigned int vendor; + unsigned int subsystem_device; + unsigned int subsystem_vendor; + unsigned int vendor_specific_cap; + u_int32_t vsec_cap_mask; + unsigned int multifunction; +}; + +#define PCICONF_READ4_NEW _IOR (PCICONF_MAGIC,7,struct mst_read4_new_st) +struct mst_read4_new_st { + unsigned int address_space; + unsigned int offset; + unsigned int data; /*OUT*/ +}; + +/****************************************************/ +/* VPD ACCESS */ +#define PCICONF_VPD_READ4 _IOR(PCICONF_MAGIC, 7, struct mst_vpd_read4_st) +struct mst_vpd_read4_st { + unsigned int offset; /* IN - must be aligned to DWORD */ + unsigned int data; /* OUT */ + +}; + +#define PCICONF_WRITE4_NEW _IOW (PCICONF_MAGIC,8,struct mst_write4_new_st) +struct mst_write4_new_st { + unsigned int address_space; + unsigned int offset; + unsigned int data; +}; + + +#define PCICONF_VPD_WRITE4 _IOW(PCICONF_MAGIC, 8, struct mst_vpd_write4_st) +struct mst_vpd_write4_st { + unsigned int offset; /* IN - must be aligned to DWORD */ + unsigned int data; /* IN */ +}; + +/* + * MEM_ACCESS + */ + + +typedef enum { + MST_DMA_ICMD, + MST_DMA_END=32 +} mst_dma_type_t; + + +#define PCICONF_MEM_ACCESS _IOWR(PCICONF_MAGIC, 10, struct mst_mem_access_st) +struct mst_mem_access_st { + mst_dma_type_t mem_type; + unsigned int _rw; /* READ: 0, WRITE: 1 */ + unsigned int offset; + unsigned int size; + unsigned char data[PCICONF_MAX_MEMACCESS_SIZE]; +}; + +#define PCICONF_DMA_PROPS _IOR (PCICONF_MAGIC, 11, struct mst_dma_props_st) + +struct dma_prop { + unsigned long long int dma_pa; + unsigned int mem_size; +}; +struct mst_dma_props_st { + struct dma_prop dma_props[MST_DMA_END]; +}; + + +#define PCICONF_MST_META_DATA _IOR (PCICONF_MAGIC, 12, struct mst_meta_data) + +struct mst_meta_data_payload { + unsigned short api_version_major; + unsigned int api_version_minor; + unsigned int cap_vector[PCICONF_CAP_VEC_LEN]; +}; + +struct mst_meta_data { + struct mst_hdr hdr; + struct mst_meta_data_payload data; +}; + + +#define PCICONF_GET_DMA_PAGES _IOR (PCICONF_MAGIC, 13, struct page_info_st) +#define PCICONF_RELEASE_DMA_PAGES _IOR (PCICONF_MAGIC, 14, struct page_info_st) + +struct page_address_st { + u_int64_t dma_address; + u_int64_t virtual_address; +}; + + +struct page_info_st { + unsigned int page_amount; + unsigned long page_pointer_start; + struct page_address_st page_address_array[PCICONF_MAX_PAGES_SIZE]; +}; + + +#define PCICONF_READ_DWORD_FROM_CONFIG_SPACE _IOR (PCICONF_MAGIC, 15, struct read_dword_from_config_space) +struct read_dword_from_config_space { + unsigned int offset; + unsigned int data; +}; + +#endif diff --git a/drivers/net/ethernet/mft/mst_backward_compatibility/mst_ppc/Makefile b/drivers/net/ethernet/mft/mst_backward_compatibility/mst_ppc/Makefile new file mode 100644 index 00000000..cffe811d --- /dev/null +++ b/drivers/net/ethernet/mft/mst_backward_compatibility/mst_ppc/Makefile @@ -0,0 +1,32 @@ +KPVER ?= $(shell uname -r) +KSRC ?= /lib/modules/$(KPVER)/build +CPU_ARCH ?= $(shell uname -m) + +# Oracle Linux OS. +ifneq ($(shell if (echo $(KPVER) | grep -qE 'uek'); then \ + echo "YES"; else echo ""; fi),) +override WITH_MAKE_PARAMS += ctf-dir=$(CWD)/.ctf +endif + +NNT_DRIVER_LOCATION = ../../nnt_driver +PACKAGE_NAME = nnt-driver +PACKAGE_VERSION = 1.0.0 +PACKAGE_RC = 1 + +%: %.in + sed \ + -e 's/@PACKAGE_NAME@/$(PACKAGE_NAME)/g' \ + -e 's/@PACKAGE_VERSION@/$(PACKAGE_VERSION)/g' \ + -e 's/@PACKAGE_RC@/$(PACKAGE_RC)/g' \ + <$< >$@ + +ifneq ($(findstring ppc64, $(CPU_ARCH)),) +obj-m += mst_ppc_pci_reset.o +EXTRA_CFLAGS= -I$(PWD)/$(NNT_DRIVER_LOCATION) +endif + +all: + make -C $(KSRC) M=$(PWD) CONFIG_CTF= CONFIG_CC_STACKPROTECTOR_STRONG= $(WITH_MAKE_PARAMS) modules + +clean: + make -C $(KSRC) M=$(PWD) clean diff --git a/drivers/net/ethernet/mft/mst_backward_compatibility/mst_ppc/mst_ppc_pci_reset.c b/drivers/net/ethernet/mft/mst_backward_compatibility/mst_ppc/mst_ppc_pci_reset.c new file mode 100755 index 00000000..2dc0bfc6 --- /dev/null +++ b/drivers/net/ethernet/mft/mst_backward_compatibility/mst_ppc/mst_ppc_pci_reset.c @@ -0,0 +1,306 @@ +#include +#include +#include +#include +#include +#include "nnt_ppc_device_list.h" +#include "nnt_ppc_driver_defs.h" +#include "nnt_defs.h" + +MODULE_AUTHOR("Itay Avraham "); +MODULE_DESCRIPTION("NNT PPC driver (NVIDIA® networking tools driver)"); +MODULE_LICENSE("Dual BSD/GPL"); + + +/* Passing PCI devices (DBDF addresses), separated by comma, for example: + * 0000:00:08.0,0000:00:08.1 */ +char pci_device_list[NNT_DEVICE_LIST_SIZE]; + +/* Create the file in sysfs. */ +module_param_string(pci_dev, pci_device_list, sizeof(pci_device_list), 0444); + + +struct nnt_ppc_reset_info nnt_ppc_reset; + + +void restore_pci_configuration_space(void) +{ + struct nnt_ppc_device* nnt_pci_device; + + list_for_each_entry(nnt_pci_device, &nnt_device_list, + entry) { + /* Restore the saved state of a PCI device. */ + pci_restore_state(nnt_pci_device->pci_device); + } +} + + +int wait_for_response(void) +{ + unsigned short device_id = NNT_UNKNOWN_DEVICE_ID; + struct nnt_ppc_device* nnt_pci_device; + int polling_counter = 0; + int error = 0; + + list_for_each_entry(nnt_pci_device, &nnt_device_list, + entry) { + struct pci_dev* pci_device = nnt_pci_device->pci_device; + + /* Device id still unknown ? */ + while(device_id != pci_device->device) { + /* 100ms is the minimum time that prevents error logs on + dmesg (device is not ready for PCI configuration cycles). */ + msleep(NNT_MINIMUM_WAITING_TIME); + + /* Read the device id. + Access can fail (if device is not ready) and + as a result we might get errors in dmesg. */ + pci_read_config_word(pci_device, PCI_DEVICE_ID, + &device_id); + + /* Polling counter violation. */ + if (polling_counter > NNT_MAXIMUM_POLLING_NUMBER) { + printk(KERN_ERR "%s Polling on device id failed: reached max value of polling failures for device: %s\n", + dev_driver_string(&pci_device->dev), dev_name(&pci_device->dev)); + error = -EINVAL; + goto ReturnOnFinished; + } + + polling_counter++; + } + } + +ReturnOnFinished: + return error; +} + + +int set_reset_state(enum pcie_reset_state state) +{ + struct nnt_ppc_device* nnt_pci_device; + int error = 0; + + list_for_each_entry(nnt_pci_device, &nnt_device_list, + entry) { + struct pci_dev* pci_device = nnt_pci_device->pci_device; + + if (PCI_FUNC(pci_device->devfn) == 0) { + /* Set reset state for device devce. */ + printk(KERN_DEBUG "%s Send hot reset to the device: %s\n",dev_driver_string(&pci_device->dev), dev_name(&pci_device->dev)); + error = pci_set_pcie_reset_state(pci_device, state); + if (error) { + printk(KERN_ERR "%s Set reset state for device failed for device: %s - error: %d\n", + dev_driver_string(&pci_device->dev), dev_name(&pci_device->dev), error); + goto ReturnOnFinished; + } + } + } + +ReturnOnFinished: + return error; +} + + +int save_pci_configucation_space(void) +{ + struct nnt_ppc_device* nnt_pci_device = NULL; + int error = 0; + + list_for_each_entry(nnt_pci_device, &nnt_device_list, + entry) { + struct pci_dev* pci_device = nnt_pci_device->pci_device; + + /* Initialize device before it's used by a driver. */ + error = pci_enable_device(pci_device); + if (error) { + printk(KERN_ERR "%s Reset failed for device: %s - error: %d\n", + dev_driver_string(&pci_device->dev), dev_name(&pci_device->dev), error); + goto ReturnOnFinished; + } + + /* Enables bus-mastering for device device. */ + pci_set_master(pci_device); + + /* Save the PCI configuration space of a device before sending hot reset. */ + error = pci_save_state(pci_device); + if (error) { + printk(KERN_ERR "%s Reset failed for device: %s - error: %d\n", + dev_driver_string(&pci_device->dev), dev_name(&pci_device->dev), error); + goto ReturnOnFinished; + } + } + +ReturnOnFinished: + return error; +} + + +int pci_devices_reset(void) +{ + int error = 0; + + if (nnt_ppc_reset.reset_was_done) { + goto ReturnOnFinished; + } + + /* Save configuration space for all devices. */ + error = save_pci_configucation_space(); + CHECK_ERROR(error); + + /* Disable the link by sending the hot reset. */ + error = set_reset_state(pcie_hot_reset); + CHECK_ERROR(error); + + msleep(jiffies_to_msecs(HZ)); + + /* Enable the link by sending the hot reset. */ + error = set_reset_state(pcie_deassert_reset); + CHECK_ERROR(error); + + /* Wait for the device to response to PCI configuration cycles. */ + error = wait_for_response(); + CHECK_ERROR(error); + + /* Restore PCI configuration space for all PCI devices. */ + restore_pci_configuration_space(); + + nnt_ppc_reset.reset_was_done = 1; + +ReturnOnFinished: + return error; +} + + +static int init_pci_device(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct nnt_ppc_device* nnt_pci_device; + + list_for_each_entry(nnt_pci_device, &nnt_device_list, + entry) { + if (!strcmp(nnt_pci_device->pci_device_dbdf_name, dev_name(&pdev->dev))) { + nnt_pci_device->pci_device = pdev; + nnt_ppc_reset.number_of_found_pci_device++; + } + } + + if (nnt_ppc_reset.number_of_requested_pci_device == nnt_ppc_reset.number_of_found_pci_device) { + return pci_devices_reset(); + } + + return 0; +} + + +static void remove_pci_device(struct pci_dev *pdev) +{ + struct nnt_ppc_device* nnt_pci_device; + + list_for_each_entry(nnt_pci_device, &nnt_device_list, + entry) { + if (!strcmp(nnt_pci_device->pci_device_dbdf_name, dev_name(&pdev->dev))) { + pci_clear_master(pdev); + pci_disable_device(pdev); + return; + } + } +} + + +int ppc_device_structure_init(struct nnt_ppc_device** nnt_pci_device, unsigned int pci_device_name_length) +{ + /* Allocate nnt device structure. */ + *nnt_pci_device= + kzalloc(sizeof(struct nnt_ppc_device),GFP_KERNEL); + + if (!(*nnt_pci_device)) { + return -ENOMEM; + } + + /* initialize nnt structure. */ + memset(*nnt_pci_device, 0, sizeof(struct nnt_ppc_device)); + + (*nnt_pci_device)->pci_device_dbdf_name = + kzalloc(pci_device_name_length,GFP_KERNEL); + + if (!(*nnt_pci_device)->pci_device_dbdf_name) { + return -ENOMEM; + } + + return 0; +} + + +int parse_pci_devices_string(void) +{ + struct nnt_ppc_device* nnt_pci_device; + char buffer[NNT_DEVICE_LIST_SIZE]; + char* pci_device_dbdf_name = NULL; + char* dbdf_list = NULL; + int error; + + strncpy(buffer, pci_device_list, NNT_DEVICE_LIST_SIZE); + dbdf_list = buffer; + + /* Add the pci device name (DBDF) to the list. */ + while ((pci_device_dbdf_name = strsep(&dbdf_list, ",")) != NULL) { + /* Allocate ppc device info structure. */ + unsigned int pci_device_name_length = strlen(pci_device_dbdf_name); + nnt_pci_device = NULL; + + error = ppc_device_structure_init(&nnt_pci_device, pci_device_name_length); + CHECK_ERROR(error); + + /* Copy the device name string. */ + strncpy(nnt_pci_device->pci_device_dbdf_name, pci_device_dbdf_name, + pci_device_name_length); + + /* Create a device entry in the list. */ + list_add_tail(&nnt_pci_device->entry, &nnt_device_list); + nnt_ppc_reset.number_of_requested_pci_device++; + } + +ReturnOnFinished: + return error; +} + + +void init_members(void) +{ + memset(&nnt_ppc_reset, 0, sizeof(struct nnt_ppc_reset_info)); +} + + +static struct pci_driver nnt_ppc_driver = { + .name = "nnt_ppc_driver", + .id_table = pciconf_devices, + .probe = init_pci_device, + .remove = remove_pci_device, +}; + +static int __init init(void) +{ + int error; + + init_members(); + + /* Parse the parameters from the user space. */ + error = parse_pci_devices_string(); + CHECK_ERROR(error); + + /* Register the NNT PPC driver. */ + return pci_register_driver(&nnt_ppc_driver); + +ReturnOnFinished: + return error; +} + + +static void __exit cleanup(void) +{ + /* Unregister the NNT PPC driver. */ + pci_unregister_driver(&nnt_ppc_driver); +} + + +module_init(init); +module_exit(cleanup); diff --git a/drivers/net/ethernet/mft/nnt_driver/Makefile b/drivers/net/ethernet/mft/nnt_driver/Makefile new file mode 100644 index 00000000..de51a092 --- /dev/null +++ b/drivers/net/ethernet/mft/nnt_driver/Makefile @@ -0,0 +1,34 @@ +KVERSION ?= $(shell uname -r) +CPU_ARCH ?= $(shell uname -m) + +# Oracle Linux OS. +ifneq ($(shell if (echo $(KVERSION) | grep -qE 'uek'); then \ + echo "YES"; else echo ""; fi),) +override WITH_MAKE_PARAMS += ctf-dir=$(CWD)/.ctf +endif + +PACKAGE_NAME = nnt-driver +PACKAGE_VERSION = 1.0.0 +PACKAGE_RC = 1 + +%: %.in + sed \ + -e 's/@PACKAGE_NAME@/$(PACKAGE_NAME)/g' \ + -e 's/@PACKAGE_VERSION@/$(PACKAGE_VERSION)/g' \ + -e 's/@PACKAGE_RC@/$(PACKAGE_RC)/g' \ + <$< >$@ + +ifneq ($(findstring ppc64, $(CPU_ARCH)),) +obj-m += mst_ppc_pci_reset.o +endif + +obj-m += nnt_driver.o +nnt_driver-objs += nnt_device.o nnt_dma.o nnt_pci_conf_access.o \ + nnt_pci_conf_access_no_vsec.o nnt_memory_access.o \ + nnt_ioctl.o + +all: + make -C /lib/modules/$(KVERSION)/build M=$(PWD) CONFIG_CTF= CONFIG_CC_STACKPROTECTOR_STRONG= $(WITH_MAKE_PARAMS) modules + +clean: + make -C /lib/modules/$(KVERSION)/build M=$(PWD) clean diff --git a/drivers/net/ethernet/mft/nnt_driver/nnt_defs.h b/drivers/net/ethernet/mft/nnt_driver/nnt_defs.h new file mode 100644 index 00000000..05a70fbb --- /dev/null +++ b/drivers/net/ethernet/mft/nnt_driver/nnt_defs.h @@ -0,0 +1,42 @@ +#ifndef NNT_DEFS_H +#define NNT_DEFS_H + + +#include +#include + + +/* Passing MFT flag argument */ +extern int is_mft_package; +extern struct driver_info nnt_driver_info; + +#define NNT_DRIVER_NAME "nnt_driver" +#define NNT_CLASS_NAME "nnt_class" +#define NNT_DEVICE_PREFIX "mt" +#define NNT_DRIVER "NNT Driver::" + +#define CHECK_PCI_READ_ERROR(error, address) \ + if (error) { \ + printk(KERN_ERR "Failed to read from address: %x\n", address); \ + goto ReturnOnFinished; \ + } + +#define CHECK_PCI_WRITE_ERROR(error, address, data) \ + if (error) { \ + printk(KERN_ERR "Failed to write to address: %x, data: %x\n", address, data); \ + goto ReturnOnFinished; \ + } + +#define CHECK_ERROR(error) \ + if (error) { \ + goto ReturnOnFinished; \ + } + +struct driver_info { + dev_t device_number; + int contiguous_device_numbers; + struct class* class_driver; +}; + + +#endif diff --git a/drivers/net/ethernet/mft/nnt_driver/nnt_device.c b/drivers/net/ethernet/mft/nnt_driver/nnt_device.c new file mode 100644 index 00000000..553dc2ab --- /dev/null +++ b/drivers/net/ethernet/mft/nnt_driver/nnt_device.c @@ -0,0 +1,626 @@ +#include "nnt_device.h" +#include "nnt_device_defs.h" +#include "nnt_defs.h" +#include "nnt_pci_conf_access.h" +#include "nnt_pci_conf_access_no_vsec.h" +#include "nnt_memory_access.h" +#include + +MODULE_LICENSE("GPL"); + +/* Device list to check if device is available + since it could be removed by hotplug event. */ +LIST_HEAD(nnt_device_list); + +int get_nnt_device(struct file* file, struct nnt_device** nnt_device) +{ + int error_code = 0; + + if (!file->private_data) + { + error_code = -EINVAL; + } + else + { + *nnt_device = file->private_data; + } + + return error_code; +} + +void set_private_data_open(struct file* file) +{ + struct nnt_device* current_nnt_device = NULL; + struct nnt_device* temp_nnt_device = NULL; + int minor = iminor(file_inode(file)); + + /* Set private data to nnt structure. */ + list_for_each_entry_safe(current_nnt_device, temp_nnt_device, &nnt_device_list, entry) + { + if ((minor == current_nnt_device->device_minor_number) && current_nnt_device->device_enabled) + { + file->private_data = current_nnt_device; + return; + } + } +} + +int set_private_data_bc(struct file* file, unsigned int bus, unsigned int devfn, unsigned int domain) +{ + struct nnt_device* current_nnt_device = NULL; + struct nnt_device* temp_nnt_device = NULL; + int minor = iminor(file_inode(file)); + unsigned int current_function; + unsigned int current_device; + + /* Set private data to nnt structure. */ + list_for_each_entry_safe(current_nnt_device, temp_nnt_device, &nnt_device_list, entry) + { + struct pci_bus* pci_bus = pci_find_bus(current_nnt_device->dbdf.domain, current_nnt_device->dbdf.bus); + if (!pci_bus) + { + return -ENXIO; + } + + current_nnt_device->pci_device = pci_get_slot(pci_bus, current_nnt_device->dbdf.devfn); + if (!current_nnt_device->pci_device) + { + return -ENXIO; + } + + current_function = PCI_FUNC(current_nnt_device->dbdf.devfn); + current_device = PCI_SLOT(current_nnt_device->dbdf.devfn); + + if ((current_nnt_device->dbdf.bus == bus) && (current_device == PCI_SLOT(devfn)) && + (current_function == PCI_FUNC(devfn)) && (current_nnt_device->dbdf.domain == domain)) + { + current_nnt_device->device_minor_number = minor; + current_nnt_device->device_enabled = true; + file->private_data = current_nnt_device; + return 0; + } + } + + return -EINVAL; +} + +int set_private_data(struct file* file) +{ + struct nnt_device* current_nnt_device = NULL; + struct nnt_device* temp_nnt_device = NULL; + int minor = iminor(file_inode(file)); + + /* Set private data to nnt structure. */ + list_for_each_entry_safe(current_nnt_device, temp_nnt_device, &nnt_device_list, entry) + { + if (current_nnt_device->device_minor_number == minor) + { + file->private_data = current_nnt_device; + return 0; + } + } + + printk(KERN_ERR "failed to find device with minor=%d\n", minor); + + return -EINVAL; +} + +int create_file_name_mstflint(struct pci_dev* pci_device, struct nnt_device* nnt_dev, enum nnt_device_type device_type) +{ + sprintf(nnt_dev->device_name, "%4.4x:%2.2x:%2.2x.%1.1x_%s", pci_domain_nr(pci_device->bus), pci_device->bus->number, + PCI_SLOT(pci_device->devfn), PCI_FUNC(pci_device->devfn), + (device_type == NNT_PCICONF) ? MSTFLINT_PCICONF_DEVICE_NAME : MSTFLINT_MEMORY_DEVICE_NAME); + + printk(KERN_DEBUG + "MSTFlint device name created: id: %d, slot id: %d, device name: /dev/%s domain: 0x%x bus: 0x%x\n", + pci_device->device, PCI_FUNC(pci_device->devfn), nnt_dev->device_name, pci_domain_nr(pci_device->bus), + pci_device->bus->number); + + return 0; +} + +int create_file_name_mft(struct pci_dev* pci_device, struct nnt_device* nnt_dev, enum nnt_device_type device_type) +{ + sprintf(nnt_dev->device_name, "mst/mt%d_%s0.%x", pci_device->device, + (device_type == NNT_PCICONF) ? MFT_PCICONF_DEVICE_NAME : MFT_MEMORY_DEVICE_NAME, + PCI_FUNC(pci_device->devfn)); + + printk(KERN_DEBUG "MFT device name created: id: %d, slot id: %d, device name: /dev/%s domain: 0x%x bus: 0x%x\n", + pci_device->device, PCI_FUNC(pci_device->devfn), nnt_dev->device_name, pci_domain_nr(pci_device->bus), + pci_device->bus->number); + + return 0; +} + +int nnt_device_structure_init(struct nnt_device** nnt_device) +{ + /* Allocate nnt device structure. */ + *nnt_device = kzalloc(sizeof(struct nnt_device), GFP_KERNEL); + + if (!(*nnt_device)) + { + return -ENOMEM; + } + + /* initialize nnt structure. */ + memset(*nnt_device, 0, sizeof(struct nnt_device)); + + return 0; +} + +int create_nnt_device(struct pci_dev* pci_device, enum nnt_device_type device_type, int is_alloc_chrdev_region) +{ + struct nnt_device* nnt_device = NULL; + int error_code = 0; + + /* Allocate nnt device info structure. */ + if ((error_code = nnt_device_structure_init(&nnt_device)) != 0) + goto ReturnOnError; + + if (is_alloc_chrdev_region) + { + /* Build the device file name of MSTFlint. */ + if ((error_code = create_file_name_mstflint(pci_device, nnt_device, device_type)) != 0) + goto ReturnOnError; + } + else + { + /* Build the device file name of MFT. */ + if ((error_code = create_file_name_mft(pci_device, nnt_device, device_type)) != 0) + goto ReturnOnError; + } + + nnt_device->dbdf.bus = pci_device->bus->number; + nnt_device->dbdf.devfn = pci_device->devfn; + nnt_device->dbdf.domain = pci_domain_nr(pci_device->bus); + nnt_device->pci_device = pci_device; + nnt_device->device_type = device_type; + + /* Add the nnt device structure to the list. */ + list_add_tail(&nnt_device->entry, &nnt_device_list); + + return error_code; + +ReturnOnError: + if (nnt_device) + { + kfree(nnt_device); + } + + return error_code; +} + +int check_pci_id_range(unsigned short pci_device_id, unsigned short id_range_start) +{ + return (pci_device_id >= id_range_start) && (pci_device_id <= (id_range_start + 100)); +} + +int is_connectx(unsigned short pci_device_id) +{ + return check_pci_id_range(pci_device_id, CONNECTX3_PCI_ID); +} + +int is_connectx3(unsigned short pci_device_id) +{ + return pci_device_id == CONNECTX3_PCI_ID || pci_device_id == CONNECTX3PRO_PCI_ID; +} + +int is_bluefield(unsigned short pci_device_id) +{ + return check_pci_id_range(pci_device_id, BLUEFIELD_PCI_ID) || + check_pci_id_range(pci_device_id, BLUEFIELD_DPU_AUX_PCI_ID); +} + +int is_pcie_switch(unsigned short pci_device_id) +{ + return check_pci_id_range(pci_device_id, SCHRODINGER_PCI_ID); +} + +int is_quantum(unsigned short pci_device_id) +{ + return check_pci_id_range(pci_device_id, QUANTUM_PCI_ID); +} + +int is_spectrum(unsigned short pci_device_id) +{ + return (pci_device_id == SPECTRUM_PCI_ID) || (check_pci_id_range(pci_device_id, SPECTRUM2_PCI_ID)); +} + +int is_switch_ib(unsigned short pci_device_id) +{ + return pci_device_id == SWITCHIB_PCI_ID || pci_device_id == SWITCHIB2_PCI_ID; +} + +int is_bw00(unsigned short pci_device_id) +{ + return check_pci_id_range(pci_device_id, BW00_PCI_ID); +} + +int is_bw02(unsigned short pci_device_id) +{ + return check_pci_id_range(pci_device_id, BW02_PCI_ID); +} + +int is_livefish_device(unsigned short pci_device_id) +{ + return pci_device_id >= CONNECTX3_LIVEFISH_ID && pci_device_id < CONNECTX3_PCI_ID; +} + +int is_nic(unsigned short pci_device_id) +{ + return is_connectx(pci_device_id) || is_bluefield(pci_device_id); +} + +int is_switch(unsigned short pci_device_id) +{ + return is_pcie_switch(pci_device_id) || is_quantum(pci_device_id) || is_spectrum(pci_device_id) || + is_switch_ib(pci_device_id) || is_bw00(pci_device_id) || is_bw02(pci_device_id); +} + +int is_toolspf(unsigned short pci_device_id) +{ + return is_nic(pci_device_id - 4000) || is_switch(pci_device_id - 4000); +} + +int is_pciconf_device(unsigned short pci_device_id) +{ + return is_nic(pci_device_id) || is_toolspf(pci_device_id) || is_livefish_device(pci_device_id) || + is_switch(pci_device_id); +} + +int is_pcicr_device(unsigned short pci_device_id) +{ + return (is_switch(pci_device_id) || is_toolspf(pci_device_id) || is_connectx3(pci_device_id)) && + (!is_livefish_device(pci_device_id)); +} + +int create_device_file(struct nnt_device* current_nnt_device, + dev_t device_number, + int minor, + struct file_operations* fop, + int is_alloc_chrdev_region) +{ + struct device* device = NULL; + int error = 0; + int count = 1; + + /* NNT driver will create the device file + once we stop support backward compatibility. */ + current_nnt_device->device_minor_number = -1; + current_nnt_device->device_number = device_number; + current_nnt_device->mcdev.owner = THIS_MODULE; + + mutex_init(¤t_nnt_device->lock); + + if (!is_alloc_chrdev_region) + { + goto ReturnOnFinished; + } + + // Create device with a new minor number. + current_nnt_device->device_minor_number = minor; + current_nnt_device->device_number = MKDEV(MAJOR(device_number), minor); + + current_nnt_device->device_enabled = true; + current_nnt_device->connectx_wa_slot_p1 = 0; + + /* Create device node. */ + device = device_create(nnt_driver_info.class_driver, NULL, current_nnt_device->device_number, NULL, + current_nnt_device->device_name); + if (!device) + { + printk(KERN_ERR "Device creation failed\n"); + error = -EINVAL; + goto ReturnOnFinished; + } + + /* Init new device. */ + cdev_init(¤t_nnt_device->mcdev, fop); + + /* Add device to the system. */ + error = cdev_add(¤t_nnt_device->mcdev, current_nnt_device->device_number, count); + if (error) + { + goto ReturnOnFinished; + } + +ReturnOnFinished: + return error; +} + +int check_if_vsec_supported(struct nnt_device* nnt_device) +{ + int error = 0; + + error = nnt_device->access.init(nnt_device); + CHECK_ERROR(error); + + if (!nnt_device->pciconf_device.vsec_fully_supported) + { + nnt_device->device_type = NNT_PCICONF_NO_VSEC; + nnt_device->access.read = read_pciconf_no_vsec; + nnt_device->access.write = write_pciconf_no_vsec; + nnt_device->access.init = init_pciconf_no_vsec; + } + +ReturnOnFinished: + return error; +} + +int is_mellanox_vendor_type(struct nnt_device* current_nnt_device, unsigned int vsec_address) +{ + u_int8_t vendor_type = 0; + int error = 0; + /* Read the capability type field */ + if ((error = pci_read_config_byte(current_nnt_device->pci_device, (vsec_address + PCI_TYPE_OFFSET), &vendor_type))) + { + printk(KERN_ERR "Reading VSEC type failed with error %d\n", error); + return 0; + } + + if (vendor_type == MELLANOX_VSEC_TYPE) + { + return 1; + } + return 0; +} + +unsigned int get_mellanox_vsec_address(struct nnt_device* current_nnt_device) +{ + unsigned int vsec_address = 0; + + /* Look for the Mellanox VSEC address. if Mellanox VSEC isn't supported the address should be 0 */ + vsec_address = pci_find_capability(current_nnt_device->pci_device, VSEC_CAPABILITY_ADDRESS); + if (!vsec_address || is_mellanox_vendor_type(current_nnt_device, vsec_address)) + { + return vsec_address; + } + + /* if found a non-Mellanox type VSEC, iterate of the next available VSECs*/ + while ( + (vsec_address = pci_find_next_capability(current_nnt_device->pci_device, vsec_address, VSEC_CAPABILITY_ADDRESS))) + { + if (is_mellanox_vendor_type(current_nnt_device, vsec_address)) + { + return vsec_address; + } + } + /* if Mellanox VSEC address was not found, return 0 */ + return 0; +} + +int create_devices(dev_t device_number, struct file_operations* fop, int is_alloc_chrdev_region) +{ + struct nnt_device* current_nnt_device = NULL; + struct nnt_device* temp_nnt_device = NULL; + int minor = 0; + int error = 0; + + /* Create necessary number of the devices. */ + list_for_each_entry_safe(current_nnt_device, temp_nnt_device, &nnt_device_list, entry) + { + /* Create the device file. */ + if ((error = create_device_file(current_nnt_device, device_number, minor, fop, is_alloc_chrdev_region)) != 0) + goto ReturnOnFinished; + + /* Members initialization. */ + current_nnt_device->pciconf_device.vendor_specific_capability = get_mellanox_vsec_address(current_nnt_device); + current_nnt_device->vpd_capability_address = + pci_find_capability(current_nnt_device->pci_device, PCI_CAP_ID_VPD); + + if (!current_nnt_device->pciconf_device.vendor_specific_capability) + { + current_nnt_device->device_type = NNT_PCICONF_NO_VSEC; + } + + switch (current_nnt_device->device_type) + { + case NNT_PCICONF: + current_nnt_device->access.read = read_pciconf; + current_nnt_device->access.write = write_pciconf; + current_nnt_device->access.init = init_pciconf; + + error = check_if_vsec_supported(current_nnt_device); + CHECK_ERROR(error); + break; + + case NNT_PCICONF_NO_VSEC: + current_nnt_device->access.read = read_pciconf_no_vsec; + current_nnt_device->access.write = write_pciconf_no_vsec; + current_nnt_device->access.init = init_pciconf_no_vsec; + break; + + case NNT_PCI_MEMORY: + current_nnt_device->access.read = read_memory; + current_nnt_device->access.write = write_memory; + current_nnt_device->access.init = init_memory; + break; + } + + if (is_alloc_chrdev_region) + { + error = current_nnt_device->access.init(current_nnt_device); + } + + minor++; + } + +ReturnOnFinished: + return error; +} + +int create_nnt_devices(dev_t device_number, + int is_alloc_chrdev_region, + struct file_operations* fop, + enum nnt_device_type_flag nnt_device_flag, + unsigned int vendor_id, + int with_unknown) +{ + struct pci_dev* pci_device = NULL; + int error_code = 0; + + /* Find all Nvidia PCI devices. */ + while ((pci_device = pci_get_device(vendor_id, PCI_ANY_ID, pci_device)) != NULL) + { + if ((nnt_device_flag == NNT_PCICONF_DEVICES) || (nnt_device_flag == NNT_ALL_DEVICES)) + { + /* Create pciconf device. */ + if (with_unknown || is_pciconf_device(pci_device->device)) + { + if ((error_code = create_nnt_device(pci_device, NNT_PCICONF, is_alloc_chrdev_region)) != 0) + { + printk(KERN_ERR "Failed to create pci conf device\n"); + goto ReturnOnFinished; + } + } + } + + if ((nnt_device_flag == NNT_PCI_DEVICES) || (nnt_device_flag == NNT_ALL_DEVICES)) + { + /* Create pci memory device. */ + if (with_unknown || is_pcicr_device(pci_device->device)) + { + if ((error_code = create_nnt_device(pci_device, NNT_PCI_MEMORY, is_alloc_chrdev_region)) != 0) + { + printk(KERN_ERR "Failed to create pci memory device\n"); + goto ReturnOnFinished; + } + } + } + } + + /* Create the devices. */ + if ((error_code = create_devices(device_number, fop, is_alloc_chrdev_region)) != 0) + { + return error_code; + } + +ReturnOnFinished: + return error_code; +} + +int find_all_vendor_devices(unsigned int vendor_id) +{ + struct pci_dev* pci_device = NULL; + int contiguous_device_numbers = 0; + while ((pci_device = pci_get_device(vendor_id, PCI_ANY_ID, pci_device)) != NULL) + { + contiguous_device_numbers++; + } + return contiguous_device_numbers; +} + +int get_amount_of_nvidia_devices(void) +{ + int contiguous_device_numbers = 0; + /* Find all Mellanox & Nvidia PCI devices. */ + contiguous_device_numbers += + find_all_vendor_devices(NNT_MELLANOX_PCI_VENDOR) + find_all_vendor_devices(NNT_NVIDIA_PCI_VENDOR); + return contiguous_device_numbers; +} + +int mutex_lock_nnt(struct file* file) +{ + struct nnt_device* nnt_device; + + if (!file) + { + return 1; + } + + nnt_device = file->private_data; + + if (!nnt_device) + { + return -EINVAL; + } + + mutex_lock(&nnt_device->lock); + + return 0; +} + +void mutex_unlock_nnt(struct file* file) +{ + struct nnt_device* nnt_device = file->private_data; + + if (nnt_device) + { + mutex_unlock(&nnt_device->lock); + } +} + +void destroy_nnt_devices(int is_alloc_chrdev_region) +{ + struct nnt_device* current_nnt_device; + struct nnt_device* temp_nnt_device; + + /* free all nnt_devices */ + list_for_each_entry_safe(current_nnt_device, temp_nnt_device, &nnt_device_list, entry) + { + /* Character device is no longer, it must be properly destroyed. */ + if (is_alloc_chrdev_region) + { + cdev_del(¤t_nnt_device->mcdev); + device_destroy(nnt_driver_info.class_driver, current_nnt_device->device_number); + } + + list_del(¤t_nnt_device->entry); + kfree(current_nnt_device); + } +} + +void destroy_nnt_devices_bc(void) +{ + struct nnt_device* current_nnt_device; + struct nnt_device* temp_nnt_device; + + /* free all nnt_devices */ + list_for_each_entry_safe(current_nnt_device, temp_nnt_device, &nnt_device_list, entry) + { + /* Character device is no longer, it must be properly destroyed. */ + list_del(¤t_nnt_device->entry); + kfree(current_nnt_device); + } +} + +int destroy_nnt_device_bc(struct nnt_device* nnt_device) +{ + struct nnt_device* current_nnt_device; + struct nnt_device* temp_nnt_device; + unsigned int current_function; + unsigned int current_device; + + /* Set private data to nnt structure. */ + list_for_each_entry_safe(current_nnt_device, temp_nnt_device, &nnt_device_list, entry) + { + struct pci_bus* pci_bus = pci_find_bus(current_nnt_device->dbdf.domain, current_nnt_device->dbdf.bus); + if (!pci_bus) + { + return -ENXIO; + } + + current_nnt_device->pci_device = pci_get_slot(pci_bus, current_nnt_device->dbdf.devfn); + if (!current_nnt_device->pci_device) + { + return -ENXIO; + } + + current_function = PCI_FUNC(current_nnt_device->dbdf.devfn); + current_device = PCI_SLOT(current_nnt_device->dbdf.devfn); + + if ((current_nnt_device->dbdf.bus == nnt_device->dbdf.bus) && + (current_device == PCI_SLOT(nnt_device->dbdf.devfn)) && + (current_function == PCI_FUNC(nnt_device->dbdf.devfn)) && + (current_nnt_device->dbdf.domain == nnt_device->dbdf.domain)) + { + /* Character device is no longer, it must be properly disabled. */ + current_nnt_device->device_enabled = false; + printk(KERN_DEBUG "Device removed: domain: %d, bus: %d, device:%d, function:%d \n", + current_nnt_device->dbdf.domain, current_nnt_device->dbdf.bus, current_device, current_function); + return 0; + } + } + + return 0; +} diff --git a/drivers/net/ethernet/mft/nnt_driver/nnt_device.h b/drivers/net/ethernet/mft/nnt_driver/nnt_device.h new file mode 100644 index 00000000..8049ba2a --- /dev/null +++ b/drivers/net/ethernet/mft/nnt_driver/nnt_device.h @@ -0,0 +1,26 @@ +#ifndef NNT_DEVICE_H +#define NNT_DEVICE_H + +#include +#include "nnt_device_defs.h" + +int create_nnt_devices(dev_t device_number, + int is_alloc_chrdev_region, + struct file_operations* fop, + enum nnt_device_type_flag nnt_device_flag, + unsigned int vendor_id, + int with_unknown); +void destroy_nnt_devices(int is_alloc_chrdev_region); +void destroy_nnt_devices_bc(void); +int destroy_nnt_device_bc(struct nnt_device* nnt_device); +int is_pciconf_device(unsigned short pci_device_id); +int is_pcicr_device(unsigned short pci_device_id); +int get_amount_of_nvidia_devices(void); +int set_private_data(struct file* file); +void set_private_data_open(struct file* file); +int set_private_data_bc(struct file* file, unsigned int bus, unsigned int devfn, unsigned int domain); +int get_nnt_device(struct file* file, struct nnt_device** nnt_device); +int mutex_lock_nnt(struct file* file); +void mutex_unlock_nnt(struct file* file); + +#endif diff --git a/drivers/net/ethernet/mft/nnt_driver/nnt_device_defs.h b/drivers/net/ethernet/mft/nnt_driver/nnt_device_defs.h new file mode 100644 index 00000000..39e8a5c7 --- /dev/null +++ b/drivers/net/ethernet/mft/nnt_driver/nnt_device_defs.h @@ -0,0 +1,169 @@ +#ifndef NNT_DEVICE_DEFS_H +#define NNT_DEVICE_DEFS_H + +#include +#include +#include +#include "nnt_ioctl_defs.h" + +#define NNT_DEVICE_ID_OFFSET 0xf0014 +#define NNT_WO_REG_ADDR_DATA 0xbadacce5 +#define NNT_NAME_SIZE 75 +#define NNT_CONF_ADDRES_REGISETER 88 +#define NNT_CONF_DATA_REGISTER 92 +#define MELLANOX_VSEC_TYPE 0 +#define PCI_TYPE_OFFSET 0x03 +#define PCI_SEMAPHORE_OFFSET 0x0c +#define PCI_ADDRESS_OFFSET 0x10 +#define PCI_DATA_OFFSET 0x14 +#define NNT_MEMORY_SIZE 1024 * 1024 +#define VSEC_CAPABILITY_ADDRESS 0x9 + +#define MSTFLINT_PCICONF_DEVICE_NAME "mstconf" +#define MSTFLINT_MEMORY_DEVICE_NAME "mstcr" +#define MFT_PCICONF_DEVICE_NAME "pciconf" +#define MFT_MEMORY_DEVICE_NAME "pci_cr" + +#define MST_BC_BUFFER_SIZE 256 +#define MST_BC_MAX_MINOR 256 + +// Mellanox Vendor ID. +#define NNT_MELLANOX_PCI_VENDOR 0x15b3 + +// NVIDIA Vendor ID. +#define NNT_NVIDIA_PCI_VENDOR 0x10de + +// Livefish Device ID range. +#define CONNECTX3_LIVEFISH_ID 502 + +// PCI Device IDs. +#define CONNECTX3_PCI_ID 4099 +#define CONNECTX3PRO_PCI_ID 4103 +#define CONNECTIB_PCI_ID 4113 +#define CONNECTX4_PCI_ID 4115 +#define CONNECTX4LX_PCI_ID 4117 +#define CONNECTX5_PCI_ID 4119 +#define CONNECTX5EX_PCI_ID 4121 +#define CONNECTX6_PCI_ID 4123 +#define CONNECTX6DX_PCI_ID 4125 +#define CONNECTX6LX_PCI_ID 4127 +#define CONNECTX7_PCI_ID 4129 +#define CONNECTX8_PCI_ID 4131 +#define SCHRODINGER_PCI_ID 6518 +#define FREYSA_PCI_ID 6521 +#define BLUEFIELD_PCI_ID 41682 +#define BLUEFIELD2_PCI_ID 41686 +#define BLUEFIELD_DPU_AUX_PCI_ID 49873 +#define BLUEFIELD3_PCI_ID 41692 +#define BLUEFIELD4_PCI_ID 41694 +#define SWITCHIB_PCI_ID 52000 +#define SWITCHIB2_PCI_ID 53000 +#define QUANTUM_PCI_ID 54000 +#define QUANTUM2_PCI_ID 54002 +#define QUANTUM3_PCI_ID 54004 +#define SPECTRUM_PCI_ID 52100 +#define SPECTRUM2_PCI_ID 53100 +#define SPECTRUM3_PCI_ID 53104 +#define SPECTRUM4_PCI_ID 53120 +#define BW00_PCI_ID 10496 +#define BW02_PCI_ID 10624 + +enum nnt_device_type_flag +{ + NNT_PCICONF_DEVICES = 0x01, + NNT_PCI_DEVICES, + NNT_ALL_DEVICES +}; + +struct nnt_dma_page +{ + struct page** page_list; + dma_addr_t* dma_address_list; +}; + +enum nnt_device_type +{ + NNT_PCICONF, + NNT_PCICONF_NO_VSEC, + NNT_PCI_MEMORY +}; + +struct supported_address_space +{ + int cr_space; + int icmd; + int semaphore; +}; + +/* Forward declaration */ +struct nnt_device; +typedef int (*callback_read)(struct nnt_device* nnt_device, struct nnt_rw_operation* read_operation); +typedef int (*callback_write)(struct nnt_device* nnt_device, struct nnt_rw_operation* write_operation); +typedef int (*callback_init)(struct nnt_device* nnt_device); + +struct nnt_access_callback +{ + callback_read read; + callback_write write; + callback_init init; +}; + +struct nnt_device_pciconf +{ + struct supported_address_space address_space; + unsigned int address_register; + unsigned int data_register; + unsigned int semaphore_offset; + unsigned int data_offset; + unsigned int address_offset; + unsigned int vendor_specific_capability; + unsigned int vsec_capability_mask; + unsigned int vsec_fully_supported; +}; + +struct nnt_device_pci +{ + unsigned long long bar_address; + unsigned int bar_size; +}; + +struct nnt_device_memory +{ + unsigned int pci_memory_bar_address; + unsigned int connectx_wa_slot_p1; + void* hardware_memory_address; +}; + +struct nnt_device_dbdf +{ + unsigned int domain; + unsigned int bus; + unsigned int devfn; +}; + +struct nnt_device +{ + struct nnt_device_pciconf pciconf_device; + struct nnt_device_memory memory_device; + struct nnt_access_callback access; + struct nnt_device_pci device_pci; + struct pci_dev* pci_device; + struct nnt_dma_page dma_page; + struct list_head entry; + struct cdev mcdev; + struct mutex lock; + struct nnt_device_dbdf dbdf; + enum nnt_device_type device_type; + int vpd_capability_address; + int device_minor_number; + int wo_address; + int buffer_used_bc; + int device_enabled; + char device_name[NNT_NAME_SIZE]; + char buffer_bc[MST_BC_BUFFER_SIZE]; + unsigned int connectx_wa_slot_p1; + unsigned char connectx_wa_slots; + dev_t device_number; +}; + +#endif diff --git a/drivers/net/ethernet/mft/nnt_driver/nnt_dma.c b/drivers/net/ethernet/mft/nnt_driver/nnt_dma.c new file mode 100644 index 00000000..6947cd22 --- /dev/null +++ b/drivers/net/ethernet/mft/nnt_driver/nnt_dma.c @@ -0,0 +1,196 @@ +#include +#include +#include "nnt_device_defs.h" +#include "nnt_pci_conf_access_defs.h" +#include "nnt_pci_conf_access.h" +#include "nnt_defs.h" +#include "nnt_ioctl_defs.h" + +int dma_mapping_page(unsigned int total_pinned, unsigned int page_mapped_counter, + struct nnt_device* nnt_device, struct page** current_pages, + struct nnt_page_info* page_info) +{ + int current_page = total_pinned + page_mapped_counter; + int error_code = 0; + + /* Get the dma address. */ + nnt_device->dma_page.dma_address_list[current_page] = + dma_map_page(&nnt_device->pci_device->dev, current_pages[current_page], + 0, PAGE_SIZE, + DMA_BIDIRECTIONAL); + /* Do we get a valid dma address ? */ + if (dma_mapping_error(&nnt_device->pci_device->dev, nnt_device->dma_page.dma_address_list[current_page])) { + printk(KERN_ERR "Failed to get DMA addresses\n"); + error_code = -EINVAL; + goto ReturnOnFinsihed; + } + + page_info->page_address_array[current_page].dma_address = + nnt_device->dma_page.dma_address_list[current_page]; + +ReturnOnFinsihed: + return error_code; +} + + + + +int pin_user_pages_in_kernel_space(unsigned int total_pages, unsigned int total_pinned, + int* pinned_pages, struct nnt_device* nnt_device, + struct nnt_page_info* page_info, struct page*** current_pages) +{ + unsigned long page_pointer_start = page_info->page_pointer_start; + int error_code = 0; + + + /* Remaining pages to pin. */ + int remaining_pages = total_pages - total_pinned; + + /* Point to the current offset. */ + uint64_t current_page_pointer = page_pointer_start + (total_pinned * PAGE_SIZE); + + /* Save the current page. */ + *current_pages = nnt_device->dma_page.page_list + total_pinned; + + /* Returns number of pages pinned - this may be fewer than the number requested + or -errno in case of error. */ + *pinned_pages = get_user_pages_fast(current_page_pointer, remaining_pages, + FOLL_WRITE, *current_pages); + + if (*pinned_pages < 1) { + kfree(nnt_device->dma_page.page_list); + error_code = -EFAULT; + } + + /* Allocate dma addresses structure. */ + if ((nnt_device->dma_page.dma_address_list = + kcalloc(total_pages, sizeof(dma_addr_t), GFP_KERNEL)) == NULL) { + error_code = -ENOMEM; + } + + return error_code; +} + + + + +int pin_user_memory_in_kernel_space(unsigned int total_pages, struct nnt_device* nnt_device, + struct nnt_page_info* page_info) +{ + unsigned int page_mapped_counter= 0; + unsigned int total_pinned = 0; + unsigned int pinned_pages; + struct page** current_pages = NULL; + int error_code = 0; + + while (total_pinned < total_pages) { + /* Pinning user pages in kernel space. */ + if((error_code = + pin_user_pages_in_kernel_space(total_pages, total_pinned, + &pinned_pages, nnt_device, + page_info, ¤t_pages)) != 0) + goto ReturnOnFinished; + + /* When the parameter 'inter_iommu' is on, we need to set up + a mapping on a pages in order to access the physical address. */ + while(page_mapped_counter < pinned_pages) + { + if((error_code = + dma_mapping_page(total_pinned, page_mapped_counter, + nnt_device, current_pages, + page_info)) != 0) + goto ReturnOnFinished; + + page_mapped_counter++; + } + + /* Advance the memory that already pinned. */ + total_pinned += pinned_pages; + } + + /* There is a page not pinned in the kernel space ? */ + if (total_pinned != total_pages) { + return -EFAULT; + } + +ReturnOnFinished: + return error_code; +} + + + + +int map_dma_pages(struct nnt_page_info* page_info, struct nnt_device* nnt_device) +{ + unsigned int total_pages = page_info->total_pages; + int page_counter = 0; + int error_code = 0; + + + /* Check if we allow locking memory. */ + if (!can_do_mlock()) { + error_code = -EPERM; + goto ReturnOnFinished; + } + + /* Allocate the page list. */ + if ((nnt_device->dma_page.page_list = + kcalloc(total_pages, sizeof(struct page *), GFP_KERNEL)) == NULL) { + error_code = -ENOMEM; + goto ReturnOnFinished; + } + + /* Go over the user memory buffer and pin user pages in kernel space */ + if((error_code = + pin_user_memory_in_kernel_space(total_pages, nnt_device, + page_info)) != 0) + goto ReturnOnFinished; + + for (page_counter = 0; + page_counter < total_pages; + page_counter++) { + printk(KERN_INFO "Page address structure number: %d, device: %04x:%02x:%02x.%0x\n", + page_counter, pci_domain_nr(nnt_device->pci_device->bus), + nnt_device->pci_device->bus->number, PCI_SLOT(nnt_device->pci_device->devfn), + PCI_FUNC(nnt_device->pci_device->devfn)); + } + +ReturnOnFinished: + return error_code; +} + + + +int release_dma_pages(struct nnt_page_info* page_info, struct nnt_device* nnt_device) +{ + int page_counter; + + if (nnt_device->dma_page.page_list == NULL) { + return 0; + } + + /* Deallocate the pages. */ + for (page_counter = 0; + page_counter < page_info->total_pages; + page_counter++) { + /* DMA activity is finished. */ + dma_unmap_page(&nnt_device->pci_device->dev, nnt_device->dma_page.dma_address_list[page_counter], + PAGE_SIZE, DMA_BIDIRECTIONAL); + + /* Release the page list. */ + set_page_dirty(nnt_device->dma_page.page_list[page_counter]); + put_page(nnt_device->dma_page.page_list[page_counter]); + nnt_device->dma_page.page_list[page_counter] = NULL; + nnt_device->dma_page.dma_address_list[page_counter] = 0; + + printk(KERN_INFO "Page structure number: %d was released. device:%04x:%02x:%02x.%0x\n", + page_counter, pci_domain_nr(nnt_device->pci_device->bus), + nnt_device->pci_device->bus->number, PCI_SLOT(nnt_device->pci_device->devfn), + PCI_FUNC(nnt_device->pci_device->devfn)); + } + + // All the pages are clean. + nnt_device->dma_page.page_list = NULL; + + return 0; +} diff --git a/drivers/net/ethernet/mft/nnt_driver/nnt_dma.h b/drivers/net/ethernet/mft/nnt_driver/nnt_dma.h new file mode 100644 index 00000000..bb36e268 --- /dev/null +++ b/drivers/net/ethernet/mft/nnt_driver/nnt_dma.h @@ -0,0 +1,10 @@ +#ifndef NNT_DMA_H +#define NNT_DMA_H + +#include "nnt_ioctl_defs.h" +#include "nnt_device_defs.h" + +int map_dma_pages(struct nnt_page_info* page_info, struct nnt_device* nnt_device); +int release_dma_pages(struct nnt_page_info* page_info, struct nnt_device* nnt_device); + +#endif diff --git a/drivers/net/ethernet/mft/nnt_driver/nnt_ioctl.c b/drivers/net/ethernet/mft/nnt_driver/nnt_ioctl.c new file mode 100644 index 00000000..7acb2a3e --- /dev/null +++ b/drivers/net/ethernet/mft/nnt_driver/nnt_ioctl.c @@ -0,0 +1,230 @@ +#include +#include +#include +#include +#include +#include "nnt_dma.h" +#include "nnt_defs.h" +#include "nnt_pci_conf_access.h" + + +unsigned int mask = 0x7fff; +unsigned int msb_mask = 0x8000; + +int dma_pages_ioctl(unsigned int command, void* user_buffer, + struct nnt_device* nnt_device) +{ + struct nnt_page_info page_info; + int error_code = 0; + + + /* Copy the page info structure from user space. */ + if (copy_from_user(&page_info, user_buffer, + sizeof(struct nnt_page_info))) { + error_code = -EFAULT; + goto ReturnOnFinished; + } + + if (command == NNT_GET_DMA_PAGES) { + if (map_dma_pages(&page_info, nnt_device)) { + goto ReturnOnFinished; + } + + /* Return the physical address to the user */ + if (copy_to_user(user_buffer, &page_info, + sizeof(struct nnt_page_info)) != 0) { + error_code = -EFAULT; + goto ReturnOnFinished; + } + } else { + error_code = + release_dma_pages(&page_info, nnt_device); + } + +ReturnOnFinished: + return error_code; +} + + + +int read_dword_ioctl(void* user_buffer, struct nnt_device* nnt_device) +{ + struct nnt_read_dword_from_config_space read_from_cspace; + int error_code = 0; + + + /* Copy the request from user space. */ + if (copy_from_user(&read_from_cspace, user_buffer, + sizeof(struct nnt_read_dword_from_config_space)) != 0) { + error_code = -EFAULT; + goto ReturnOnFinished; + } + + /* Read the dword. */ + if (read_dword(&read_from_cspace, nnt_device)) { + goto ReturnOnFinished; + } + + /* Copy the data to the user space. */ + if (copy_to_user(user_buffer, &read_from_cspace, + sizeof(struct nnt_read_dword_from_config_space)) != 0) { + error_code = -EFAULT; + goto ReturnOnFinished; + } + +ReturnOnFinished: + return error_code; +} + + +int get_nnt_device_parameters(struct nnt_device_parameters* nnt_parameters, struct nnt_device* nnt_device) +{ + int error = 0; + + nnt_parameters->domain = pci_domain_nr(nnt_device->pci_device->bus); + nnt_parameters->bus = nnt_device->pci_device->bus->number; + nnt_parameters->slot = PCI_SLOT(nnt_device->pci_device->devfn); + nnt_parameters->function = PCI_FUNC(nnt_device->pci_device->devfn); + nnt_parameters->pci_memory_bar_address= nnt_device->memory_device.pci_memory_bar_address; + nnt_parameters->device = nnt_device->pci_device->device; + nnt_parameters->vendor = nnt_device->pci_device->vendor; + nnt_parameters->subsystem_device = nnt_device->pci_device->subsystem_device; + nnt_parameters->multifunction = nnt_device->pci_device->multifunction; + nnt_parameters->subsystem_vendor = nnt_device->pci_device->subsystem_vendor; + + check_address_space_support(nnt_device); + if (nnt_device->pciconf_device.vendor_specific_capability && + (nnt_device->pciconf_device.address_space.icmd || nnt_device->pciconf_device.address_space.cr_space || + nnt_device->pciconf_device.address_space.semaphore)) { + nnt_parameters->vendor_specific_capability = nnt_device->pciconf_device.vendor_specific_capability; + nnt_parameters->vsec_capability_mask = nnt_device->pciconf_device.vsec_capability_mask; + } else { + nnt_parameters->vendor_specific_capability = 0; + } + + return error; +} + + +int pci_connectx_wa(struct nnt_connectx_wa* connectx_wa, struct nnt_device* nnt_device) +{ + unsigned int slot_mask; + int error = 0; + + /* Is this slot exists ? */ + if (nnt_device->memory_device.connectx_wa_slot_p1) { + printk(KERN_DEBUG "Slot exits for file %s, slot:0x%x\n", + nnt_device->device_name, nnt_device->memory_device.connectx_wa_slot_p1); + error = 0; + goto ReturnOnFinished; + } + + /* Find first un(set) bit. and remember the slot */ + nnt_device->memory_device.connectx_wa_slot_p1= ffs(~nnt_device->memory_device.connectx_wa_slot_p1); + if (nnt_device->memory_device.connectx_wa_slot_p1 == 0 || + nnt_device->memory_device.connectx_wa_slot_p1 > NNT_CONNECTX_WA_SIZE) { + error = -ENOLCK; + goto ReturnOnFinished; + } + + slot_mask = 1 << (nnt_device->memory_device.connectx_wa_slot_p1 - 1); + + /* set the slot as taken */ + nnt_device->memory_device.connectx_wa_slot_p1 |= slot_mask; + + connectx_wa->connectx_wa_slot_p1 = nnt_device->memory_device.connectx_wa_slot_p1; + +ReturnOnFinished: + return error; +} + + +int vpd_read(struct nnt_vpd* vpd, struct nnt_device* nnt_device) +{ + unsigned long jiffies_time; + unsigned int address; + unsigned short data; + int is_bit_set = 0; + int error; + + /* Sets F bit to zero and write VPD address. */ + address = mask & vpd->offset; + error = pci_write_config_word(nnt_device->pci_device, nnt_device->vpd_capability_address + PCI_VPD_ADDR, + address); + CHECK_PCI_WRITE_ERROR(error, nnt_device->vpd_capability_address + PCI_VPD_ADDR, + address); + + /* Wait for data until F bit is set with one */ + jiffies_time = msecs_to_jiffies(vpd->timeout) + jiffies; + while (time_before(jiffies, jiffies_time)) { + error = pci_read_config_word(nnt_device->pci_device, nnt_device->vpd_capability_address + PCI_VPD_ADDR, + &data); + CHECK_PCI_READ_ERROR(error, nnt_device->vpd_capability_address + PCI_VPD_ADDR); + + if (data & msb_mask) { + is_bit_set = 1; + break; + } + + cond_resched(); + } + + if (!is_bit_set) { + printk(KERN_ERR "Failed to retrieve valid data\n"); + return -ETIMEDOUT; + } + + /* read data */ + error = pci_read_config_dword(nnt_device->pci_device, nnt_device->vpd_capability_address + PCI_VPD_DATA, + &vpd->data); + CHECK_PCI_READ_ERROR(error, nnt_device->vpd_capability_address + PCI_VPD_DATA); + +ReturnOnFinished: + return error; +} + + +int vpd_write(struct nnt_vpd* vpd, struct nnt_device* nnt_device) +{ + unsigned long jiffies_time; + unsigned int address; + unsigned short data; + int is_bit_set = 0; + int error; + + /* Write the user data */ + error = pci_write_config_dword(nnt_device->pci_device, nnt_device->vpd_capability_address + PCI_VPD_DATA, + vpd->data); + CHECK_PCI_WRITE_ERROR(error, nnt_device->vpd_capability_address + PCI_VPD_DATA, + vpd->data); + + /* sets F bit to one and write VPD addr */ + address = msb_mask | (mask & vpd->offset); + error = pci_write_config_word(nnt_device->pci_device, nnt_device->vpd_capability_address + PCI_VPD_ADDR, + address); + CHECK_PCI_WRITE_ERROR(error, nnt_device->vpd_capability_address + PCI_VPD_ADDR, + address); + + /* wait for data until F bit is set with zero */ + jiffies_time = msecs_to_jiffies(vpd->timeout) + jiffies; + while (time_before(jiffies, jiffies_time)) { + error = pci_read_config_word(nnt_device->pci_device, nnt_device->vpd_capability_address + PCI_VPD_ADDR, + &data); + CHECK_PCI_READ_ERROR(error, nnt_device->vpd_capability_address + PCI_VPD_ADDR); + + if (!(data & msb_mask)) { + is_bit_set = 1; + break; + } + + cond_resched(); + } + + if (!is_bit_set) { + printk(KERN_ERR "Failed to retrieve valid data\n"); + return -ETIMEDOUT; + } + +ReturnOnFinished: + return error; +} diff --git a/drivers/net/ethernet/mft/nnt_driver/nnt_ioctl.h b/drivers/net/ethernet/mft/nnt_driver/nnt_ioctl.h new file mode 100644 index 00000000..44227f93 --- /dev/null +++ b/drivers/net/ethernet/mft/nnt_driver/nnt_ioctl.h @@ -0,0 +1,14 @@ +#ifndef NNT_IOCTL_H +#define NNT_IOCTL_H + +#include "nnt_device_defs.h" + +int dma_pages_ioctl(unsigned int command, void* user_buffer, + struct nnt_device* nnt_device); +int read_dword_ioctl(unsigned int command, void* user_buffer, + struct nnt_device* nnt_device); +int get_nnt_device_parameters(struct nnt_device_parameters* nnt_parameters, struct nnt_device* nnt_device); +int pci_connectx_wa(struct nnt_connectx_wa* connectx_wa, struct nnt_device* nnt_device); +int vpd_read(struct nnt_vpd* vpd, struct nnt_device* nnt_device); +int vpd_write(struct nnt_vpd* vpd, struct nnt_device* nnt_device); +#endif diff --git a/drivers/net/ethernet/mft/nnt_driver/nnt_ioctl_defs.h b/drivers/net/ethernet/mft/nnt_driver/nnt_ioctl_defs.h new file mode 100644 index 00000000..020a79b8 --- /dev/null +++ b/drivers/net/ethernet/mft/nnt_driver/nnt_ioctl_defs.h @@ -0,0 +1,83 @@ +#ifndef NNT_IOCTL_DEFS_H +#define NNT_IOCTL_DEFS_H + +#define NNT_MAGIC_NUMBER 0xD3 +#define MAX_BUFFER_BLOCK_SIZE 256 +#define NNT_MAX_PAGES_SIZE 8 +#define NNT_CONNECTX_WA_SIZE 3 + +#define NNT_WRITE _IOW (NNT_MAGIC_NUMBER, 1, struct nnt_rw_operation) +#define NNT_READ _IOW (NNT_MAGIC_NUMBER, 2, struct nnt_rw_operation) +#define NNT_GET_DMA_PAGES _IOR (NNT_MAGIC_NUMBER, 3, struct nnt_page_info) +#define NNT_RELEASE_DMA_PAGES _IOR (NNT_MAGIC_NUMBER, 4, struct nnt_page_info) +#define NNT_READ_DWORD_FROM_CONFIG_SPACE _IOR (NNT_MAGIC_NUMBER, 5, struct nnt_read_dword_from_config_space) +#define NNT_GET_DEVICE_PARAMETERS _IOR (NNT_MAGIC_NUMBER, 6, struct nnt_device_parameters) +#define NNT_INIT _IOR (NNT_MAGIC_NUMBER, 7, struct nnt_pciconf_init) +#define NNT_PCI_CONNECTX_WA _IOR (NNT_MAGIC_NUMBER, 8, u_int32_t) +#define NNT_VPD_READ _IOR (NNT_MAGIC_NUMBER, 9, struct nnt_vpd) +#define NNT_VPD_WRITE _IOW (NNT_MAGIC_NUMBER, 10, struct nnt_vpd) + + + +struct nnt_vpd { + unsigned int offset; + unsigned int timeout; + unsigned int data; +}; + + +struct nnt_pciconf_init { + unsigned int address_register; + unsigned int address_data_register; +}; + + +struct nnt_device_parameters { + unsigned int domain; + unsigned int bus; + unsigned int slot; + unsigned int function; + unsigned int pci_memory_bar_address; + unsigned int device; + unsigned int vendor; + unsigned int subsystem_device; + unsigned int subsystem_vendor; + unsigned int multifunction; + unsigned int vendor_specific_capability; + unsigned int vsec_capability_mask; +}; + + +struct nnt_page_address { + u_int64_t dma_address; + u_int64_t virtual_address; +}; + + +struct nnt_page_info { + unsigned int total_pages; + unsigned long page_pointer_start; + struct nnt_page_address page_address_array[NNT_MAX_PAGES_SIZE]; +}; + + +struct nnt_read_dword_from_config_space { + unsigned int offset; + unsigned int data; +}; + + +struct nnt_rw_operation { + unsigned int address_space; + unsigned int offset; + int size; + unsigned int data[MAX_BUFFER_BLOCK_SIZE / 4]; +}; + + +struct nnt_connectx_wa { + unsigned int connectx_wa_slot_p1; +}; + + +#endif diff --git a/drivers/net/ethernet/mft/nnt_driver/nnt_linux_driver_main.c b/drivers/net/ethernet/mft/nnt_driver/nnt_linux_driver_main.c new file mode 100644 index 00000000..8db9e541 --- /dev/null +++ b/drivers/net/ethernet/mft/nnt_driver/nnt_linux_driver_main.c @@ -0,0 +1,295 @@ +/* + * Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * BSD-3-Clause + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include "nnt_defs.h" +#include "nnt_device.h" +#include "nnt_ioctl.h" +#include "nnt_ioctl_defs.h" + +MODULE_AUTHOR("Itay Avraham "); +MODULE_DESCRIPTION("NNT Linux driver (NVIDIA® networking tools driver)"); + +/* Passing MFT flag argument */ +int mft_package = 0; + +/* Create the file in sysfs. */ +module_param(mft_package, int, S_IRUSR); + +struct driver_info nnt_driver_info; + +static long nnt_ioctl(struct file* file, unsigned int command, unsigned long argument) +{ + void* user_buffer = (void*)argument; + struct nnt_device* nnt_device = NULL; + int error; + + /* By convention, any user gets read access + * and is allowed to use the device. + * Commands with no direction are administration + * commands, and you need write permission for this */ + + if (_IOC_DIR(command) == _IOC_NONE) + { + if (!(file->f_mode & FMODE_WRITE)) + { + return -EPERM; + } + } + else + { + if (!(file->f_mode & FMODE_READ)) + { + return -EPERM; + } + } + + error = mutex_lock_nnt(file); + CHECK_ERROR(error); + + /* Get the nnt device structure */ + error = get_nnt_device(file, &nnt_device); + if (error) + { + goto ReturnOnFinished; + } + + switch (command) + { + case NNT_GET_DMA_PAGES: + case NNT_RELEASE_DMA_PAGES: + error = dma_pages_ioctl(command, user_buffer, nnt_device); + break; + + case NNT_READ_DWORD_FROM_CONFIG_SPACE: + error = read_dword_ioctl(command, user_buffer, nnt_device); + break; + + case NNT_WRITE: + case NNT_READ: + { + struct nnt_rw_operation rw_operation; + + /* Copy the request from user space. */ + if (copy_from_user(&rw_operation, user_buffer, sizeof(struct nnt_rw_operation)) != 0) + { + error = -EFAULT; + goto ReturnOnFinished; + } + + switch (command) + { + case NNT_WRITE: + nnt_device->access.write(nnt_device, &rw_operation); + break; + case NNT_READ: + nnt_device->access.read(nnt_device, &rw_operation); + break; + } + + /* Copy the data to the user space. */ + if (copy_to_user(user_buffer, &rw_operation, sizeof(struct nnt_rw_operation)) != 0) + { + error = -EFAULT; + goto ReturnOnFinished; + } + break; + } + case NNT_GET_DEVICE_PARAMETERS: + { + struct nnt_device_parameters nnt_parameters; + + error = get_nnt_device_parameters(&nnt_parameters, nnt_device); + + /* Copy the data to the user space. */ + if (copy_to_user(user_buffer, &nn_parameters, sizeof(struct device_parameters)) != 0) + { + error = -EFAULT; + goto ReturnOnFinished; + } + + break; + } + case NNT_INIT: + { + struct nnt_pciconf_init init; + + /* Copy the request from user space. */ + if (copy_from_user(&init, user_buffer, sizeof(struct nnt_pciconf_init)) != 0) + { + error = -EFAULT; + goto ReturnOnFinished; + } + error = nnt_device->access.init(&init, nnt_device); + break; + } + case NNT_PCI_CONNECTX_WA: + { + struct nnt_connectx_wa connectx_wa; + error = pci_connectx_wa(&connectx_wa, nnt_device); + + /* Copy the data to the user space. */ + if (copy_to_user(user_buffer, &connectx_wa, sizeof(struct nnt_connectx_wa)) != 0) + { + error = -EFAULT; + goto ReturnOnFinished; + } + + break; + } + case NNT_VPD_READ: + case NNT_VPD_WRITE: + { + int vpd_default_timeout = 2000; + struct nnt_vpd vpd; + + if (!nnt_device->vpd_capability_address) + { + printk(KERN_ERR "Device %s not support Vital Product Data\n", nnt_device->device_name); + return -ENODEV; + } + + /* Copy the request from user space. */ + if (copy_from_user(&vpd, user_buffer, sizeof(struct nnt_vpd)) != 0) + { + error = -EFAULT; + goto ReturnOnFinished; + } + + if (!vpd.timeout) + { + vpd.timeout = vpd_default_timeout; + } + + switch (command) + { + case NNT_VPD_READ: + error = vpd_read(command, &vpd, nnt_device); + break; + case NNT_VPD_WRITE: + error = vpd_write(command, &vpd, nnt_device); + break; + } + + break; + } + default: + printk(KERN_ERR "ioctl not implemented, command id: %x\n", command); + error = -EINVAL; + break; + } + +ReturnOnFinished: + mutex_unlock_nnt(file); + + return error; +} + +static int nnt_open(struct inode* inode, struct file* file) +{ + if (file->private_data) + { + return 0; + } + + return set_private_data(inode, file); +} + +struct file_operations fop = {.unlocked_ioctl = ioctl, .open = nnt_open, .owner = THIS_MODULE}; + +int with_unknown = 0; + +module_param(with_unknown, int, S_IRUSR | S_IWUSR); + +static int __init nnt_init_module(void) +{ + int first_minor_number = 0; + int error = 0; + dev_t device_numbers; + + /* Get the amount of the Nvidia devices. */ + if ((nnt_driver_info.contiguous_device_numbers = get_amount_of_nvidia_devices()) == 0) + { + printk(KERN_ERR "No devices found\n"); + goto ReturnOnFinished; + } + + /* Allocate char driver region and assign major number */ + if ((error = alloc_chrdev_region(&device_numbers, first_minor_number, nnt_driver_info.contiguous_device_numbers, + NNT_DRIVER_NAME)) != 0) + { + printk(KERN_ERR "failed to allocate chrdev_region\n"); + goto CharDeviceAllocated; + } + + nnt_driver_info.driver_major_number = MAJOR(device_numbers); + + /* create sysfs class. */ + if ((nnt_driver_info.class_driver = class_create(THIS_MODULE, NNT_CLASS_NAME)) == NULL) + { + printk(KERN_ERR "Class creation failed\n"); + error = -EFAULT; + goto DriverClassAllocated; + } + + /* Create device files for MSTflint and MFT */ + error = create_nnt_devices(nnt_driver_info.contiguous_device_numbers, device_numbers, mft_package, + NNT_MELLANOX_PCI_VENDOR, &fop) || + create_nnt_devices(nnt_driver_info.contiguous_device_numbers, device_numbers, mft_package, + NNT_NVIDIA_PCI_VENDOR, &fop); + if ((error) == 0) + { + goto ReturnOnFinished; + } + +DriverClassAllocated: + destroy_nnt_devices(); + class_destroy(nnt_driver_info.class_driver); + +CharDeviceAllocated: + unregister_chrdev_region(nnt_driver_info.driver_major_number, nnt_driver_info.contiguous_device_numbers); + +ReturnOnFinished: + return error; +} + +static void __exit nnt_cleanup_module(void) +{ + destroy_nnt_devices(); + class_destroy(nnt_driver_info.class_driver); + unregister_chrdev_region(nnt_driver_info.driver_major_number, nnt_driver_info.contiguous_device_numbers); +} + +module_init(nnt_init_module); +module_exit(nnt_cleanup_module); diff --git a/drivers/net/ethernet/mft/nnt_driver/nnt_memory_access.c b/drivers/net/ethernet/mft/nnt_driver/nnt_memory_access.c new file mode 100644 index 00000000..b21f6a79 --- /dev/null +++ b/drivers/net/ethernet/mft/nnt_driver/nnt_memory_access.c @@ -0,0 +1,46 @@ +#include "nnt_device_defs.h" +#include "nnt_pci_conf_access_defs.h" +#include "nnt_pci_conf_access.h" +#include "nnt_defs.h" + + +int write_memory(struct nnt_device* nnt_device, struct nnt_rw_operation* write_operation) +{ + /* Endianness conversion. */ + cpu_to_be32s(write_operation->data); + write_operation->data[0] = cpu_to_le32(write_operation->data[0]); + + /* Write to the hardware memory address. */ + iowrite32(write_operation->data[0], nnt_device->memory_device.hardware_memory_address + write_operation->offset); + + return 0; +} + + + +int read_memory(struct nnt_device* nnt_device, struct nnt_rw_operation* read_operation) +{ + /* Read from the hardware memory address. */ + read_operation->data[0] = ioread32(nnt_device->memory_device.hardware_memory_address + read_operation->offset); + + /* Endianness conversion */ + be32_to_cpus(read_operation->data); + read_operation->data[0] = cpu_to_le32(read_operation->data[0]); + + return 0; +} + + +int init_memory(struct nnt_device* nnt_device) +{ + nnt_device->memory_device.connectx_wa_slot_p1 = 0; + nnt_device->memory_device.hardware_memory_address = + ioremap(pci_resource_start(nnt_device->pci_device, nnt_device->memory_device.pci_memory_bar_address), + NNT_MEMORY_SIZE); + + if (nnt_device->memory_device.hardware_memory_address <= 0) { + printk(KERN_ERR "could not map device memory\n"); + } + + return 0; +} diff --git a/drivers/net/ethernet/mft/nnt_driver/nnt_memory_access.h b/drivers/net/ethernet/mft/nnt_driver/nnt_memory_access.h new file mode 100644 index 00000000..6f1d5481 --- /dev/null +++ b/drivers/net/ethernet/mft/nnt_driver/nnt_memory_access.h @@ -0,0 +1,7 @@ +#ifndef NNT_MEMORY_ACCESS_H +#define NNT_MEMORY_ACCESS_H + +int read_memory(struct nnt_device* nnt_device, struct nnt_rw_operation* read_operation); +int write_memory(struct nnt_device* nnt_device, struct nnt_rw_operation* write_operation); +int init_memory(struct nnt_device* nnt_device); +#endif diff --git a/drivers/net/ethernet/mft/nnt_driver/nnt_pci_conf_access.c b/drivers/net/ethernet/mft/nnt_driver/nnt_pci_conf_access.c new file mode 100644 index 00000000..f72c742a --- /dev/null +++ b/drivers/net/ethernet/mft/nnt_driver/nnt_pci_conf_access.c @@ -0,0 +1,430 @@ +#include "nnt_device_defs.h" +#include "nnt_pci_conf_access_defs.h" +#include "nnt_pci_conf_access.h" +#include "nnt_defs.h" +#include +#include + + +int clear_vsec_semaphore(struct nnt_device* nnt_device) +{ + /* Clear the semaphore. */ + int error = pci_write_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.semaphore_offset, + 0); + CHECK_PCI_WRITE_ERROR(error, nnt_device->pciconf_device.semaphore_offset, + 0) + +ReturnOnFinished: + return error; +} + + +int get_semaphore_ticket(struct nnt_device* nnt_device, unsigned int* lock_value, + unsigned int* counter) +{ + unsigned int counter_offset = nnt_device->pciconf_device.vendor_specific_capability + PCI_COUNTER_OFFSET; + int error = 0; + + /* Read ticket. */ + error = pci_read_config_dword(nnt_device->pci_device, counter_offset, + counter); + CHECK_PCI_READ_ERROR(error, counter_offset); + + /* Write to semaphore ticket. */ + error = pci_write_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.semaphore_offset, + *counter); + CHECK_PCI_WRITE_ERROR(error, nnt_device->pciconf_device.semaphore_offset, + *counter); + + /* Read back semaphore to make sure the + * ticket is equal to semphore */ + error = pci_read_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.semaphore_offset, + lock_value); + CHECK_PCI_READ_ERROR(error, nnt_device->pciconf_device.semaphore_offset); + +ReturnOnFinished: + return error; +} + + + + +int lock_vsec_semaphore(struct nnt_device* nnt_device) +{ + unsigned int lock_value = -1; + unsigned int counter = 0; + unsigned int retries = 0; + int error = 0; + + do { + if (retries > SEMAPHORE_MAX_RETRIES) { + return -EINVAL; + } + + /* Read the semaphore, until we will get 0. */ + error = pci_read_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.semaphore_offset, + &lock_value); + + CHECK_PCI_READ_ERROR(error, nnt_device->pciconf_device.semaphore_offset); + + /* Is semaphore taken ? */ + if (lock_value) { + retries++; + udelay(1000); + continue; + } + + /* Get semaphore ticket */ + error = get_semaphore_ticket(nnt_device, &lock_value, + &counter); + CHECK_ERROR(error); + } while (counter != lock_value); + +ReturnOnFinished: + return error; +} + + + + +int read_dword(struct nnt_read_dword_from_config_space* read_from_cspace, struct nnt_device* nnt_device) +{ + int error = 0; + + /* Take semaphore. */ + error = lock_vsec_semaphore(nnt_device); + CHECK_ERROR(error); + + /* Read dword from config space. */ + error = pci_read_config_dword(nnt_device->pci_device, read_from_cspace->offset, + &read_from_cspace->data); + CHECK_PCI_READ_ERROR(error, read_from_cspace->offset); + +ReturnOnFinished: + /* Clear semaphore. */ + clear_vsec_semaphore(nnt_device); + return error; +} + + +int wait_on_flag(struct nnt_device* nnt_device, u8 expected_val) +{ + unsigned int flag = 0; + int retries = 0; + int error = -1; + + for (retries = 0; retries < IFC_MAX_RETRIES; retries++) { + /* Read the flag. */ + error = pci_read_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.address_offset, + &flag); + CHECK_PCI_READ_ERROR(error, nnt_device->pciconf_device.address_offset); + + flag = EXTRACT(flag, PCI_FLAG_BIT_OFFSET, + 1); + if (flag == expected_val) { + return 0; + } + } + +ReturnOnFinished: + return error; +} + + +int set_address_space(struct nnt_device* nnt_device, unsigned int address_space) +{ + unsigned int control_offset = nnt_device->pciconf_device.vendor_specific_capability + PCI_CONTROL_OFFSET; + unsigned int value = 0; + int error = 0; + + /* Read value from control offset. */ + error = pci_read_config_dword(nnt_device->pci_device, control_offset, + &value); + CHECK_PCI_READ_ERROR(error, control_offset); + + /* Set the bit address_space indication and write it back. */ + value = MERGE(value, address_space, + PCI_SPACE_BIT_OFFSET, PCI_SPACE_BIT_LENGTH); + error = pci_write_config_dword(nnt_device->pci_device, control_offset, + value); + CHECK_PCI_WRITE_ERROR(error, control_offset, + value); + + /* Read status and make sure address_space is supported. */ + error = pci_read_config_dword(nnt_device->pci_device, control_offset, + &value); + CHECK_PCI_READ_ERROR(error, control_offset); + + if (EXTRACT(value, PCI_STATUS_BIT_OFFSET, + PCI_STATUS_BIT_LEN) == 0) { + error = -EINVAL; + } + +ReturnOnFinished: + return error; +} + + +int check_address_space_support(struct nnt_device* nnt_device) +{ + int error = 0; + + if ((!nnt_device->pciconf_device.vendor_specific_capability) || (!nnt_device->pci_device)) { + return 0; + } + + /* Get semaphore ticket */ + error = lock_vsec_semaphore(nnt_device); + CHECK_ERROR(error); + + /* Is ICMD address space supported ?*/ + if(set_address_space(nnt_device, ADDRESS_SPACE_ICMD) == 0) { + nnt_device->pciconf_device.address_space.icmd = 1; + } + + /* Is CR Space address space supported ?*/ + if(set_address_space(nnt_device, ADDRESS_SPACE_CR_SPACE) == 0) { + nnt_device->pciconf_device.address_space.cr_space = 1; + } + + /* Is semaphore address space supported ?*/ + if(set_address_space(nnt_device, ADDRESS_SPACE_SEMAPHORE) == 0) { + nnt_device->pciconf_device.address_space.semaphore = 1; + } + +ReturnOnFinished: + /* Clear semaphore. */ + clear_vsec_semaphore(nnt_device); + + return 0; +} + + + +int set_rw_address(unsigned int* offset, unsigned int rw) +{ + u32 address = *offset; + + /* Last 2 bits must be zero as we only allow 30 bits addresses. */ + if (EXTRACT(address, 30, + 2)) { + return -1; + } + + address = MERGE(address, rw, + PCI_FLAG_BIT_OFFSET, 1); + *offset = address; + + return 0; +} + + + + +int read(struct nnt_device* nnt_device, unsigned int offset, + unsigned int* data) +{ + int error = set_rw_address(&offset, READ_OPERATION); + CHECK_ERROR(error); + + /* Write address. */ + error = pci_write_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.address_offset, + offset); + CHECK_PCI_WRITE_ERROR(error, nnt_device->pciconf_device.address_offset, + offset); + + error = wait_on_flag(nnt_device, 1); + CHECK_ERROR(error); + + /* Read data. */ + error = pci_read_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.data_offset, + data); + CHECK_PCI_READ_ERROR(error, nnt_device->pciconf_device.data_offset); + +ReturnOnFinished: + return error; +} + + + +int read_pciconf(struct nnt_device* nnt_device, struct nnt_rw_operation* read_operation) +{ + int counter = 0; + int error = 0; + + /* Lock semaphore. */ + error = lock_vsec_semaphore(nnt_device); + CHECK_ERROR(error); + + /* Is CR Space address space supported ?*/ + error = set_address_space(nnt_device, read_operation->address_space); + CHECK_ERROR(error); + + for (counter = 0; counter < read_operation->size; counter += 4) { + if (read(nnt_device, read_operation->offset + counter, + &read_operation->data[counter >> 2])) { + error = counter; + goto ReturnOnFinished; + } + } + +ReturnOnFinished: + /* Clear semaphore. */ + clear_vsec_semaphore(nnt_device); + + return error; +} + + + +int write(struct nnt_device* nnt_device, unsigned int offset, + unsigned int data) +{ + int error = set_rw_address(&offset, WRITE_OPERATION); + CHECK_ERROR(error); + + /* Write data. */ + error = pci_write_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.data_offset, + data); + CHECK_PCI_WRITE_ERROR(error,nnt_device->pciconf_device.data_offset, + data); + + /* Write address. */ + error = pci_write_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.address_offset, + offset); + CHECK_PCI_WRITE_ERROR(error, nnt_device->pciconf_device.address_offset, + offset); + + error = wait_on_flag(nnt_device, 0); + +ReturnOnFinished: + return error; +} + + +int write_pciconf(struct nnt_device* nnt_device, struct nnt_rw_operation* write_operation) +{ + int counter = 0; + int error = 0; + + /* Lock semaphore. */ + error = lock_vsec_semaphore(nnt_device); + CHECK_ERROR(error); + + /* Is CR Space address space supported ?*/ + error = set_address_space(nnt_device, write_operation->address_space); + CHECK_ERROR(error); + + for (counter = 0; counter < write_operation->size; counter += 4) { + if (write(nnt_device, write_operation->offset + counter, + write_operation->data[counter >> 2])) { + error = counter; + goto ReturnOnFinished; + } + } + +ReturnOnFinished: + /* Clear semaphore. */ + clear_vsec_semaphore(nnt_device); + + return error; +} + + +int address_space_to_capability(u_int16_t address_space) +{ + switch (address_space) { + case NNT_SPACE_ICMD: + return NNT_VSEC_ICMD_SPACE_SUPPORTED; + case NNT_SPACE_CR_SPACE: + return NNT_VSEC_CRSPACE_SPACE_SUPPORTED; + case NNT_SPACE_ALL_ICMD: + return NNT_VSEC_ALL_ICMD_SPACE_SUPPORTED; + case NNT_SPACE_NODNIC_INIT_SEG: + return NNT_VSEC_NODNIC_INIT_SEG_SPACE_SUPPORTED; + case NNT_SPACE_EXPANSION_ROM: + return NNT_VSEC_EXPANSION_ROM_SPACE_SUPPORTED; + case NNT_SPACE_ND_CR_SPACE: + return NNT_VSEC_ND_CRSPACE_SPACE_SUPPORTED; + case NNT_SPACE_SCAN_CR_SPACE: + return NNT_VSEC_SCAN_CRSPACE_SPACE_SUPPORTED; + case NNT_SPACE_GLOBAL_SEMAPHORE: + return NNT_VSEC_GLOBAL_SEMAPHORE_SPACE_SUPPORTED; + case NNT_SPACE_MAC: + return NNT_VSEC_MAC_SPACE_SUPPORTED; + default: + return 0; + } +} + + +int get_space_support_status(struct nnt_device* nnt_device, u_int16_t address_space) +{ + int status = 0; + + if(set_address_space(nnt_device, address_space) == 0) { + status = 1; + } + + nnt_device->pciconf_device.vsec_capability_mask |= + (status << address_space_to_capability(address_space)); + + return status; +} + + +int init_vsec_capability_mask(struct nnt_device* nnt_device) +{ + int error = 0; + + /* Lock semaphore. */ + error = lock_vsec_semaphore(nnt_device); + CHECK_ERROR(error); + + get_space_support_status(nnt_device, NNT_SPACE_ICMD); + get_space_support_status(nnt_device, NNT_SPACE_CR_SPACE); + get_space_support_status(nnt_device, NNT_SPACE_ALL_ICMD); + get_space_support_status(nnt_device, NNT_SPACE_NODNIC_INIT_SEG); + get_space_support_status(nnt_device, NNT_SPACE_EXPANSION_ROM); + get_space_support_status(nnt_device, NNT_SPACE_ND_CR_SPACE); + get_space_support_status(nnt_device, NNT_SPACE_SCAN_CR_SPACE); + get_space_support_status(nnt_device, NNT_SPACE_GLOBAL_SEMAPHORE); + get_space_support_status(nnt_device, NNT_SPACE_MAC); + nnt_device->pciconf_device.vsec_capability_mask |= (1 << NNT_VSEC_INITIALIZED); + +ReturnOnFinished: + /* Clear semaphore. */ + clear_vsec_semaphore(nnt_device); + + return 0; +} + + +void check_vsec_minimum_support(struct nnt_device* nnt_device) +{ + if ((nnt_device->pciconf_device.vsec_capability_mask & (1 << NNT_VSEC_INITIALIZED)) && + (nnt_device->pciconf_device.vsec_capability_mask & (1 << NNT_VSEC_ICMD_SPACE_SUPPORTED)) && + (nnt_device->pciconf_device.vsec_capability_mask & (1 << NNT_VSEC_CRSPACE_SPACE_SUPPORTED)) && + (nnt_device->pciconf_device.vsec_capability_mask & (1 << NNT_VSEC_GLOBAL_SEMAPHORE_SPACE_SUPPORTED))) { + + nnt_device->pciconf_device.vsec_fully_supported = 1; + } +} + + +int init_pciconf(struct nnt_device* nnt_device) +{ + int error = 0; + + nnt_device->pciconf_device.semaphore_offset = + nnt_device->pciconf_device.vendor_specific_capability + PCI_SEMAPHORE_OFFSET; + nnt_device->pciconf_device.data_offset = + nnt_device->pciconf_device.vendor_specific_capability + PCI_DATA_OFFSET; + nnt_device->pciconf_device.address_offset = + nnt_device->pciconf_device.vendor_specific_capability + PCI_ADDRESS_OFFSET; + + error = init_vsec_capability_mask(nnt_device); + check_vsec_minimum_support(nnt_device); + + return error; +} diff --git a/drivers/net/ethernet/mft/nnt_driver/nnt_pci_conf_access.h b/drivers/net/ethernet/mft/nnt_driver/nnt_pci_conf_access.h new file mode 100644 index 00000000..d890fe74 --- /dev/null +++ b/drivers/net/ethernet/mft/nnt_driver/nnt_pci_conf_access.h @@ -0,0 +1,14 @@ +#ifndef NNT_PCICONF_H +#define NNT_PCICONF_H + +#include "nnt_device_defs.h" +#include "nnt_ioctl_defs.h" + + +int read_pciconf(struct nnt_device* nnt_device, struct nnt_rw_operation* read_operation); +int write_pciconf(struct nnt_device* nnt_device, struct nnt_rw_operation* write_operation); +int init_pciconf(struct nnt_device* nnt_device); +int read_dword(struct nnt_read_dword_from_config_space* read_from_cspace, struct nnt_device* nnt_device); +int check_address_space_support(struct nnt_device* nnt_device); + +#endif diff --git a/drivers/net/ethernet/mft/nnt_driver/nnt_pci_conf_access_defs.h b/drivers/net/ethernet/mft/nnt_driver/nnt_pci_conf_access_defs.h new file mode 100644 index 00000000..56bf69d9 --- /dev/null +++ b/drivers/net/ethernet/mft/nnt_driver/nnt_pci_conf_access_defs.h @@ -0,0 +1,64 @@ +#ifndef NNT_PCICONF_DEFS_H +#define NNT_PCICONF_DEFS_H + +#define ADDRESS_SPACE_CR_SPACE 0x2 +#define ADDRESS_SPACE_ICMD 0x3 +#define ADDRESS_SPACE_SEMAPHORE 0xa + +#define PCI_CONTROL_OFFSET 0x04 +#define PCI_COUNTER_OFFSET 0x08 + +#define SEMAPHORE_MAX_RETRIES 0x1000 + +#define PCI_SPACE_BIT_OFFSET 0 +#define PCI_SPACE_BIT_LENGTH 16 +#define PCI_STATUS_BIT_OFFSET 29 +#define PCI_STATUS_BIT_LEN 3 +#define PCI_FLAG_BIT_OFFSET 31 + +#define READ_OPERATION 0 +#define WRITE_OPERATION 1 + +#define IFC_MAX_RETRIES 0x10000 +#define SEMAPHORE_MAX_RETRIES 0x1000 + + +typedef enum nnt_space_address { + NNT_SPACE_ICMD = 0x1, + NNT_SPACE_CR_SPACE = 0x2, + NNT_SPACE_ALL_ICMD = 0x3, + NNT_SPACE_NODNIC_INIT_SEG = 0x4, + NNT_SPACE_EXPANSION_ROM = 0x5, + NNT_SPACE_ND_CR_SPACE = 0x6, + NNT_SPACE_SCAN_CR_SPACE = 0x7, + NNT_SPACE_GLOBAL_SEMAPHORE = 0xa, + NNT_SPACE_MAC = 0xf +} NNT_SPACE_ADDRESS; + + +typedef enum nnt_vsec_capability { + NNT_VSEC_INITIALIZED = 0, + NNT_VSEC_ICMD_SPACE_SUPPORTED, + NNT_VSEC_CRSPACE_SPACE_SUPPORTED, + NNT_VSEC_ALL_ICMD_SPACE_SUPPORTED, + NNT_VSEC_NODNIC_INIT_SEG_SPACE_SUPPORTED, + NNT_VSEC_EXPANSION_ROM_SPACE_SUPPORTED, + NNT_VSEC_ND_CRSPACE_SPACE_SUPPORTED, + NNT_VSEC_SCAN_CRSPACE_SPACE_SUPPORTED, + NNT_VSEC_GLOBAL_SEMAPHORE_SPACE_SUPPORTED, + NNT_VSEC_MAC_SPACE_SUPPORTED, +} NNT_VSEC_CAPABILITY; + + +// BIT Slicing macros +#define ONES32(size) ((size)?(0xffffffff>>(32-(size))):0) +#define MASK32(offset, size) ( ONES32(size)<<(offset)) + +#define EXTRACT_C(source, offset, size) ((((unsigned int)(source))>>(offset)) & ONES32(size)) +#define EXTRACT(src, start, len) (((len) == 32)?(src):EXTRACT_C(src, start, len)) + +#define MERGE_C(rsrc1, rsrc2, start, len) ((((rsrc2)<<(start)) & (MASK32((start), (len)))) | ((rsrc1) & (~MASK32((start), (len))))) +#define MERGE(rsrc1, rsrc2, start, len) (((len) == 32)?(rsrc2):MERGE_C(rsrc1, rsrc2, start, len)) + + +#endif diff --git a/drivers/net/ethernet/mft/nnt_driver/nnt_pci_conf_access_no_vsec.c b/drivers/net/ethernet/mft/nnt_driver/nnt_pci_conf_access_no_vsec.c new file mode 100644 index 00000000..22ed60fc --- /dev/null +++ b/drivers/net/ethernet/mft/nnt_driver/nnt_pci_conf_access_no_vsec.c @@ -0,0 +1,132 @@ +#include "nnt_device_defs.h" +#include "nnt_pci_conf_access_no_vsec.h" +#include "nnt_defs.h" + + +int read_no_vsec(struct nnt_device* nnt_device, unsigned int offset, + unsigned int* data) +{ + int error = 0; + + if (nnt_device->wo_address) { + offset |= 0x1; + } + + /* Write the wanted address to address register. */ + error = pci_write_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.address_register, + offset); + CHECK_PCI_WRITE_ERROR(error, nnt_device->pciconf_device.address_register, + offset); + + /* Read the result from data register */ + error = pci_read_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.data_register, + data); + CHECK_PCI_READ_ERROR(error, nnt_device->pciconf_device.data_register); + +ReturnOnFinished: + return error; +} + + +int read_pciconf_no_vsec(struct nnt_device* nnt_device, struct nnt_rw_operation* read_operation) +{ + int counter = 0; + int error = 0; + + for (counter = 0; counter < read_operation->size; counter += 4) { + if (read_no_vsec(nnt_device, read_operation->offset + counter, + &read_operation->data[counter >> 2])) { + error = counter; + goto ReturnOnFinished; + } + } + +ReturnOnFinished: + return error; +} + + +int write_no_vsec(struct nnt_device* nnt_device, unsigned int offset, + unsigned int data) +{ + int error = 0; + + if (nnt_device->wo_address) { + /* write the data to the data register. */ + error = pci_write_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.data_register, + data); + CHECK_PCI_WRITE_ERROR(error, nnt_device->pciconf_device.data_register, + data); + /* Write the destination address to address register. */ + error = pci_write_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.address_register, + offset); + CHECK_PCI_WRITE_ERROR(error, nnt_device->pciconf_device.address_register, + offset); + } else { + /* Write the destination address to address register. */ + error = pci_write_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.address_register, + offset); + CHECK_PCI_WRITE_ERROR(error, nnt_device->pciconf_device.address_register, + offset); + + /* write the data to the data register. */ + error = pci_write_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.data_register, + data); + CHECK_PCI_WRITE_ERROR(error, nnt_device->pciconf_device.data_register, + data); + } + +ReturnOnFinished: + return error; +} + + +int write_pciconf_no_vsec(struct nnt_device* nnt_device, struct nnt_rw_operation* write_operation) +{ + int counter = 0; + int error = 0; + + for (counter = 0; counter < write_operation->size; counter += 4) { + if (write_no_vsec(nnt_device, write_operation->offset + counter, + write_operation->data[counter >> 2])) { + error = counter; + goto ReturnOnFinished; + } + } + +ReturnOnFinished: + return error; +} + + +int is_wo_gw(struct nnt_device* nnt_device) +{ + unsigned int data = 0; + int error = 0; + + error = pci_write_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.address_register, + NNT_DEVICE_ID_OFFSET); + CHECK_PCI_WRITE_ERROR(error, nnt_device->pciconf_device.address_register, + NNT_DEVICE_ID_OFFSET); + + /* Read the result from data register */ + error = pci_read_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.address_register, + &data); + CHECK_PCI_READ_ERROR(error, nnt_device->pciconf_device.address_register); + + if (data == NNT_WO_REG_ADDR_DATA) { + error = 1; + } + +ReturnOnFinished: + return error; +} + + +int init_pciconf_no_vsec(struct nnt_device* nnt_device) +{ + nnt_device->pciconf_device.address_register = NNT_CONF_ADDRES_REGISETER; + nnt_device->pciconf_device.data_register = NNT_CONF_DATA_REGISTER; + nnt_device->wo_address = is_wo_gw(nnt_device); + return 0; +} diff --git a/drivers/net/ethernet/mft/nnt_driver/nnt_pci_conf_access_no_vsec.h b/drivers/net/ethernet/mft/nnt_driver/nnt_pci_conf_access_no_vsec.h new file mode 100644 index 00000000..6601679a --- /dev/null +++ b/drivers/net/ethernet/mft/nnt_driver/nnt_pci_conf_access_no_vsec.h @@ -0,0 +1,12 @@ +#ifndef NNT_PCICONF_NO_VSEC_H +#define NNT_PCICONF_NO_VSEC_H + +#include "nnt_device_defs.h" +#include "nnt_ioctl_defs.h" + + +int read_pciconf_no_vsec(struct nnt_device* nnt_device, struct nnt_rw_operation* read_operation); +int write_pciconf_no_vsec(struct nnt_device* nnt_device, struct nnt_rw_operation* write_operation); +int init_pciconf_no_vsec(struct nnt_device* nnt_device); + +#endif diff --git a/drivers/net/ethernet/mft/nnt_driver/nnt_ppc_device_list.h b/drivers/net/ethernet/mft/nnt_driver/nnt_ppc_device_list.h new file mode 100644 index 00000000..5dd81c74 --- /dev/null +++ b/drivers/net/ethernet/mft/nnt_driver/nnt_ppc_device_list.h @@ -0,0 +1,38 @@ +#ifndef NNT_DEVICE_LIST_H +#define NNT_DEVICE_LIST_H + +#include "nnt_device_defs.h" + +static struct pci_device_id pciconf_devices[] = {{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, CONNECTX3_PCI_ID)}, + {PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, CONNECTX3PRO_PCI_ID)}, + {PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, CONNECTIB_PCI_ID)}, + {PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, CONNECTX4_PCI_ID)}, + {PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, CONNECTX4LX_PCI_ID)}, + {PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, CONNECTX5_PCI_ID)}, + {PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, CONNECTX5EX_PCI_ID)}, + {PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, CONNECTX6_PCI_ID)}, + {PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, CONNECTX6DX_PCI_ID)}, + {PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, CONNECTX6LX_PCI_ID)}, + {PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, CONNECTX7_PCI_ID)}, + {PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, CONNECTX8_PCI_ID)}, + {PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, SCHRODINGER_PCI_ID)}, + {PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, FREYSA_PCI_ID)}, + {PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, BLUEFIELD_PCI_ID)}, + {PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, BLUEFIELD2_PCI_ID)}, + {PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, BLUEFIELD3_PCI_ID)}, + {PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, BLUEFIELD4_PCI_ID)}, + {PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, SWITCHIB_PCI_ID)}, + {PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, SWITCHIB2_PCI_ID)}, + {PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, QUANTUM_PCI_ID)}, + {PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, QUANTUM2_PCI_ID)}, + {PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, QUANTUM3_PCI_ID)}, + {PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, SPECTRUM_PCI_ID)}, + {PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, SPECTRUM2_PCI_ID)}, + {PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, SPECTRUM3_PCI_ID)}, + {PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, SPECTRUM4_PCI_ID)}, + {PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, BW00_PCI_ID)}, + { + 0, + }}; + +#endif // NNT_DEVICE_LIST_H \ No newline at end of file diff --git a/drivers/net/ethernet/mft/nnt_driver/nnt_ppc_driver_defs.h b/drivers/net/ethernet/mft/nnt_driver/nnt_ppc_driver_defs.h new file mode 100644 index 00000000..6323fe64 --- /dev/null +++ b/drivers/net/ethernet/mft/nnt_driver/nnt_ppc_driver_defs.h @@ -0,0 +1,26 @@ +#ifndef NNT_DRIVER_PPC_H +#define NNT_DRIVER_PPC_H + +LIST_HEAD(nnt_device_list); + +#define NNT_MAXIMUM_DEVICE_NAME_LENGTH 128 +#define NNT_MAXIMUM_NUMBER_OF_DEVICES 8 +#define NNT_MAXIMUM_POLLING_NUMBER 100 +#define NNT_UNKNOWN_DEVICE_ID 0xffff +#define NNT_MINIMUM_WAITING_TIME 100 +#define NNT_DEVICE_LIST_SIZE NNT_MAXIMUM_DEVICE_NAME_LENGTH * NNT_MAXIMUM_NUMBER_OF_DEVICES + +struct nnt_ppc_device { + struct list_head entry; + struct pci_dev* pci_device; + char* pci_device_dbdf_name; +}; + + +struct nnt_ppc_reset_info { + unsigned int number_of_found_pci_device; + unsigned int number_of_requested_pci_device; + int reset_was_done; +}; + +#endif diff --git a/drivers/net/ethernet/mft/scripts/install_mst_ppc_pci_reset.sh b/drivers/net/ethernet/mft/scripts/install_mst_ppc_pci_reset.sh new file mode 100755 index 00000000..522b9663 --- /dev/null +++ b/drivers/net/ethernet/mft/scripts/install_mst_ppc_pci_reset.sh @@ -0,0 +1,13 @@ +#!/bin/bash +set -e + +kernel_version="`modinfo -Fvermagic ./mst_backward_compatibility/mst_ppc/mst_ppc_pci_reset.ko | awk '{ print $1 }'`" +path_to_build="`pwd`" +path_to_build="$path_to_build/../build" +cd $path_to_build + +mkdir -p /etc/mft/mlxfwreset/$kernel_version +/bin/cp -f ./mst_backward_compatibility/mst_ppc/mst_ppc_pci_reset.ko /etc/mft/mlxfwreset/$kernel_version/ + +cd - +