lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite for Android: free password hash cracker in your pocket
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:   Wed, 1 Mar 2017 11:30:56 -0500
From:   Jon Mason <jdmason@...zu.us>
To:     Serge Semin <fancer.lancer@...il.com>
Cc:     Dave Jiang <dave.jiang@...el.com>,
        "Hubbe, Allen" <Allen.Hubbe@....com>,
        "Yu, Xiangliang" <Xiangliang.Yu@....com>,
        Sergey.Semin@...latforms.ru, linux-ntb@...glegroups.com,
        linux-kernel <linux-kernel@...r.kernel.org>
Subject: Re: [PATCH v4] NTB: Add IDT 89HPESxNTx PCIe-switches support

Please run checkpatch
# git show | ./scripts/checkpatch.pl

On Mon, Feb 27, 2017 at 4:22 AM, Serge Semin <fancer.lancer@...il.com> wrote:
> IDT 89HPESxNTx device series is PCIe-switches, which support
> Non-Transparent bridging between domains connected to the device ports.
> Since new NTB API exposes multi-port interface and messaging API, the IDT

WARNING: Possible unwrapped commit description (prefer a maximum 75
chars per line)
#9:
    Since new NTB API exposes multi-port interface and messaging API, the IDT

> NT-functions can be now supported in the kernel. This driver adds the
> following functionality:
> 1) Multi-port NTB API to have information of possible NT-functions
> activated in compliance with available device ports.
> 2) Memory windows of direct and look up table based address translation
> with all possible combinations of BARs setup.
> 3) Traditional doorbell NTB API.
> 4) One-on-one messaging NTB API.
>
> There are some IDT PCIe-switch setups, which must be done before any of
> the NTB peers started. It can be performed either by system BIOS via IDT
> SMBus-slave interface or by pre-initialized IDT PCIe-switch EEPROM:
> 1) NT-functions of corresponding ports must be activated using SWPARTxCTL
> and SWPORTxCTL registers.
> 2) BAR0 must be configured to expose NT-function configuration registers
> map.
> 3) The rest of the BARs must have at least one memory window configured,
> otherwise the driver will just return an error.
> Temperature sensor of IDT PCIe-switches can be also optionally activated
> by BIOS or EEPROM.
> (See IDT documentations for details of how the pre-initialization can be
> done)

I think you want to add '---' here, to make it so the following lines
are not included in the patch when I apply it.


> Changelog v2:
> - Fix minor checkpatch.pl issues
> - Get rid of obfuscating macros
>
> Changelog v3:
> - No write to registers if address is either out of bound or unaligned
> - Fix idt_reg_set_bits()/idt_reg_clear_bits() methods race condition
> - Fix invalid argument of write method called from
> idt_reg_set_bits()/idt_reg_clear_bits() functions
> - Add appropriate naming of function idt_get_mw_size()
> - Fix some documentation notes
> - Replace symbolic permission S_IRUSR with octal 0400
>
> Changelog v4:
> - Return ~0 on read from registers with invalid address
> - Don't check bits validity on registers bits clearing
> - Keep up driver loading (just print a warning) if there is no any peer
> NTBs found
> - Fix unnecessary branching logic
> - Fix some documentation notes
>
> Signed-off-by: Serge Semin <fancer.lancer@...il.com>
> Acked-by: Allen Hubbe <Allen.Hubbe@...l.com>

Move the S-O-Bs above the '---'

>
> ---
>  drivers/ntb/hw/Kconfig          |    1 +
>  drivers/ntb/hw/Makefile         |    1 +
>  drivers/ntb/hw/idt/Kconfig      |   31 +
>  drivers/ntb/hw/idt/Makefile     |    1 +
>  drivers/ntb/hw/idt/ntb_hw_idt.c | 2647 +++++++++++++++++++++++++++++++++++++++
>  drivers/ntb/hw/idt/ntb_hw_idt.h | 1162 +++++++++++++++++
>  6 files changed, 3843 insertions(+)
>  create mode 100644 drivers/ntb/hw/idt/Kconfig
>  create mode 100644 drivers/ntb/hw/idt/Makefile
>  create mode 100644 drivers/ntb/hw/idt/ntb_hw_idt.c
>  create mode 100644 drivers/ntb/hw/idt/ntb_hw_idt.h
>
> diff --git a/drivers/ntb/hw/Kconfig b/drivers/ntb/hw/Kconfig
> index 7116472..a89243c 100644
> --- a/drivers/ntb/hw/Kconfig
> +++ b/drivers/ntb/hw/Kconfig
> @@ -1,2 +1,3 @@
>  source "drivers/ntb/hw/amd/Kconfig"
> +source "drivers/ntb/hw/idt/Kconfig"
>  source "drivers/ntb/hw/intel/Kconfig"
> diff --git a/drivers/ntb/hw/Makefile b/drivers/ntb/hw/Makefile
> index 532e085..87332c3 100644
> --- a/drivers/ntb/hw/Makefile
> +++ b/drivers/ntb/hw/Makefile
> @@ -1,2 +1,3 @@
>  obj-$(CONFIG_NTB_AMD)  += amd/
> +obj-$(CONFIG_NTB_IDT)  += idt/
>  obj-$(CONFIG_NTB_INTEL)        += intel/
> diff --git a/drivers/ntb/hw/idt/Kconfig b/drivers/ntb/hw/idt/Kconfig
> new file mode 100644
> index 0000000..b360e56
> --- /dev/null
> +++ b/drivers/ntb/hw/idt/Kconfig
> @@ -0,0 +1,31 @@
> +config NTB_IDT
> +       tristate "IDT PCIe-switch Non-Transparent Bridge support"
> +       depends on PCI
> +       help
> +        This driver supports NTB of cappable IDT PCIe-switches.
> +
> +        Some of the pre-initializations must be made before IDT PCIe-switch
> +        exposes it NT-functions correctly. It should be done by either proper
> +        initialisation of EEPROM connected to master smbus of the switch or
> +        by BIOS using slave-SMBus interface changing corresponding registers
> +        value. Evidently it must be done before PCI bus enumeration is
> +        finished in Linux kernel.
> +
> +        First of all partitions must be activated and properly assigned to all
> +        the ports with NT-functions intended to be activated (see SWPARTxCTL
> +        and SWPORTxCTL registers). Then all NT-function BARs must be enabled
> +        with chosen valid aperture. For memory windows related BARs the
> +        aperture settings shall determine the maximum size of memory windows
> +        accepted by a BAR. Note that BAR0 must map PCI configuration space
> +        registers.
> +
> +        It's worth to note, that since a part of this driver relies on the
> +        BAR settings of peer NT-functions, the BAR setups can't be done over
> +        kernel PCI fixups. That's why the alternative pre-initialization
> +        techniques like BIOS using SMBus interface or EEPROM should be
> +        utilized. Additionally if one needs to have temperature sensor
> +        information printed to system log, the corresponding registers must
> +        be initialized within BIOS/EEPROM as well.
> +
> +        If unsure, say N.
> +
> diff --git a/drivers/ntb/hw/idt/Makefile b/drivers/ntb/hw/idt/Makefile
> new file mode 100644
> index 0000000..a102cf1
> --- /dev/null
> +++ b/drivers/ntb/hw/idt/Makefile
> @@ -0,0 +1 @@
> +obj-$(CONFIG_NTB_IDT) += ntb_hw_idt.o
> diff --git a/drivers/ntb/hw/idt/ntb_hw_idt.c b/drivers/ntb/hw/idt/ntb_hw_idt.c
> new file mode 100644
> index 0000000..f7b6076
> --- /dev/null
> +++ b/drivers/ntb/hw/idt/ntb_hw_idt.c
> @@ -0,0 +1,2647 @@
> +/*
> + *   This file is provided under a GPLv2 license.  When using or
> + *   redistributing this file, you may do so under that license.
> + *
> + *   GPL LICENSE SUMMARY
> + *
> + *   Copyright (C) 2016 T-Platforms All Rights Reserved.
> + *
> + *   This program is free software; you can redistribute it and/or modify it
> + *   under the terms and conditions of the GNU General Public License,
> + *   version 2, as published by the Free Software Foundation.
> + *
> + *   This program is distributed in the hope that it will be useful, but WITHOUT
> + *   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + *   FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
> + *   more details.
> + *
> + *   You should have received a copy of the GNU General Public License along with

80 chars

Also, you should use the newer way of doing this to avoid all of this.  Checkout
http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/drivers/usb/core/buffer.c

Also, there is a similar issue with itd.h.  So, the suggestion above
would address that as well.


> + *   this program; if not, one can be found <http://www.gnu.org/licenses/>.
> + *
> + *   The full GNU General Public License is included in this distribution in
> + *   the file called "COPYING".
> + *
> + *   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.
> + *
> + * IDT PCIe-switch NTB Linux driver
> + *
> + * Contact Information:
> + * Serge Semin <fancer.lancer@...il.com>, <Sergey.Semin@...latforms.ru>
> + */
> +
> +#include <linux/stddef.h>
> +#include <linux/types.h>
> +#include <linux/kernel.h>
> +#include <linux/bitops.h>
> +#include <linux/sizes.h>
> +#include <linux/module.h>
> +#include <linux/moduleparam.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/spinlock.h>
> +#include <linux/pci.h>
> +#include <linux/aer.h>
> +#include <linux/slab.h>
> +#include <linux/list.h>
> +#include <linux/debugfs.h>
> +#include <linux/ntb.h>
> +
> +#include "ntb_hw_idt.h"
> +
> +#define NTB_NAME       "ntb_hw_idt"
> +#define NTB_DESC       "IDT PCI-E Non-Transparent Bridge Driver"
> +#define NTB_VER                "2.0"
> +#define NTB_IRQNAME    "ntb_irq_idt"
> +
> +MODULE_DESCRIPTION(NTB_DESC);
> +MODULE_VERSION(NTB_VER);
> +MODULE_LICENSE("GPL v2");
> +MODULE_AUTHOR("T-platforms");
> +
> +/*
> + * NT Endpoint registers table simplifying a loop access to the functionally
> + * related registers
> + */
> +static const struct idt_ntb_regs ntdata_tbl = {
> +       { {IDT_NT_BARSETUP0,    IDT_NT_BARLIMIT0,
> +          IDT_NT_BARLTBASE0,   IDT_NT_BARUTBASE0},
> +         {IDT_NT_BARSETUP1,    IDT_NT_BARLIMIT1,
> +          IDT_NT_BARLTBASE1,   IDT_NT_BARUTBASE1},
> +         {IDT_NT_BARSETUP2,    IDT_NT_BARLIMIT2,
> +          IDT_NT_BARLTBASE2,   IDT_NT_BARUTBASE2},
> +         {IDT_NT_BARSETUP3,    IDT_NT_BARLIMIT3,
> +          IDT_NT_BARLTBASE3,   IDT_NT_BARUTBASE3},
> +         {IDT_NT_BARSETUP4,    IDT_NT_BARLIMIT4,
> +          IDT_NT_BARLTBASE4,   IDT_NT_BARUTBASE4},
> +         {IDT_NT_BARSETUP5,    IDT_NT_BARLIMIT5,
> +          IDT_NT_BARLTBASE5,   IDT_NT_BARUTBASE5} },
> +       { {IDT_NT_INMSG0,       IDT_NT_OUTMSG0, IDT_NT_INMSGSRC0},
> +         {IDT_NT_INMSG1,       IDT_NT_OUTMSG1, IDT_NT_INMSGSRC1},
> +         {IDT_NT_INMSG2,       IDT_NT_OUTMSG2, IDT_NT_INMSGSRC2},
> +         {IDT_NT_INMSG3,       IDT_NT_OUTMSG3, IDT_NT_INMSGSRC3} }
> +};
> +
> +/*
> + * NT Endpoint ports data table with the corresponding pcie command, link
> + * status, control and BAR-related registers
> + */
> +static const struct idt_ntb_port portdata_tbl[IDT_MAX_NR_PORTS] = {
> +/*0*/  { IDT_SW_NTP0_PCIECMDSTS,       IDT_SW_NTP0_PCIELCTLSTS,
> +         IDT_SW_NTP0_NTCTL,
> +         IDT_SW_SWPORT0CTL,            IDT_SW_SWPORT0STS,
> +         { {IDT_SW_NTP0_BARSETUP0,     IDT_SW_NTP0_BARLIMIT0,
> +            IDT_SW_NTP0_BARLTBASE0,    IDT_SW_NTP0_BARUTBASE0},
> +           {IDT_SW_NTP0_BARSETUP1,     IDT_SW_NTP0_BARLIMIT1,
> +            IDT_SW_NTP0_BARLTBASE1,    IDT_SW_NTP0_BARUTBASE1},
> +           {IDT_SW_NTP0_BARSETUP2,     IDT_SW_NTP0_BARLIMIT2,
> +            IDT_SW_NTP0_BARLTBASE2,    IDT_SW_NTP0_BARUTBASE2},
> +           {IDT_SW_NTP0_BARSETUP3,     IDT_SW_NTP0_BARLIMIT3,
> +            IDT_SW_NTP0_BARLTBASE3,    IDT_SW_NTP0_BARUTBASE3},
> +           {IDT_SW_NTP0_BARSETUP4,     IDT_SW_NTP0_BARLIMIT4,
> +            IDT_SW_NTP0_BARLTBASE4,    IDT_SW_NTP0_BARUTBASE4},
> +           {IDT_SW_NTP0_BARSETUP5,     IDT_SW_NTP0_BARLIMIT5,
> +            IDT_SW_NTP0_BARLTBASE5,    IDT_SW_NTP0_BARUTBASE5} } },
> +/*1*/  {0},
> +/*2*/  { IDT_SW_NTP2_PCIECMDSTS,       IDT_SW_NTP2_PCIELCTLSTS,
> +         IDT_SW_NTP2_NTCTL,
> +         IDT_SW_SWPORT2CTL,            IDT_SW_SWPORT2STS,
> +         { {IDT_SW_NTP2_BARSETUP0,     IDT_SW_NTP2_BARLIMIT0,
> +            IDT_SW_NTP2_BARLTBASE0,    IDT_SW_NTP2_BARUTBASE0},
> +           {IDT_SW_NTP2_BARSETUP1,     IDT_SW_NTP2_BARLIMIT1,
> +            IDT_SW_NTP2_BARLTBASE1,    IDT_SW_NTP2_BARUTBASE1},
> +           {IDT_SW_NTP2_BARSETUP2,     IDT_SW_NTP2_BARLIMIT2,
> +            IDT_SW_NTP2_BARLTBASE2,    IDT_SW_NTP2_BARUTBASE2},
> +           {IDT_SW_NTP2_BARSETUP3,     IDT_SW_NTP2_BARLIMIT3,
> +            IDT_SW_NTP2_BARLTBASE3,    IDT_SW_NTP2_BARUTBASE3},
> +           {IDT_SW_NTP2_BARSETUP4,     IDT_SW_NTP2_BARLIMIT4,
> +            IDT_SW_NTP2_BARLTBASE4,    IDT_SW_NTP2_BARUTBASE4},
> +           {IDT_SW_NTP2_BARSETUP5,     IDT_SW_NTP2_BARLIMIT5,
> +            IDT_SW_NTP2_BARLTBASE5,    IDT_SW_NTP2_BARUTBASE5} } },
> +/*3*/  {0},
> +/*4*/  { IDT_SW_NTP4_PCIECMDSTS,       IDT_SW_NTP4_PCIELCTLSTS,
> +         IDT_SW_NTP4_NTCTL,
> +         IDT_SW_SWPORT4CTL,            IDT_SW_SWPORT4STS,
> +         { {IDT_SW_NTP4_BARSETUP0,     IDT_SW_NTP4_BARLIMIT0,
> +            IDT_SW_NTP4_BARLTBASE0,    IDT_SW_NTP4_BARUTBASE0},
> +           {IDT_SW_NTP4_BARSETUP1,     IDT_SW_NTP4_BARLIMIT1,
> +            IDT_SW_NTP4_BARLTBASE1,    IDT_SW_NTP4_BARUTBASE1},
> +           {IDT_SW_NTP4_BARSETUP2,     IDT_SW_NTP4_BARLIMIT2,
> +            IDT_SW_NTP4_BARLTBASE2,    IDT_SW_NTP4_BARUTBASE2},
> +           {IDT_SW_NTP4_BARSETUP3,     IDT_SW_NTP4_BARLIMIT3,
> +            IDT_SW_NTP4_BARLTBASE3,    IDT_SW_NTP4_BARUTBASE3},
> +           {IDT_SW_NTP4_BARSETUP4,     IDT_SW_NTP4_BARLIMIT4,
> +            IDT_SW_NTP4_BARLTBASE4,    IDT_SW_NTP4_BARUTBASE4},
> +           {IDT_SW_NTP4_BARSETUP5,     IDT_SW_NTP4_BARLIMIT5,
> +            IDT_SW_NTP4_BARLTBASE5,    IDT_SW_NTP4_BARUTBASE5} } },
> +/*5*/  {0},
> +/*6*/  { IDT_SW_NTP6_PCIECMDSTS,       IDT_SW_NTP6_PCIELCTLSTS,
> +         IDT_SW_NTP6_NTCTL,
> +         IDT_SW_SWPORT6CTL,            IDT_SW_SWPORT6STS,
> +         { {IDT_SW_NTP6_BARSETUP0,     IDT_SW_NTP6_BARLIMIT0,
> +            IDT_SW_NTP6_BARLTBASE0,    IDT_SW_NTP6_BARUTBASE0},
> +           {IDT_SW_NTP6_BARSETUP1,     IDT_SW_NTP6_BARLIMIT1,
> +            IDT_SW_NTP6_BARLTBASE1,    IDT_SW_NTP6_BARUTBASE1},
> +           {IDT_SW_NTP6_BARSETUP2,     IDT_SW_NTP6_BARLIMIT2,
> +            IDT_SW_NTP6_BARLTBASE2,    IDT_SW_NTP6_BARUTBASE2},
> +           {IDT_SW_NTP6_BARSETUP3,     IDT_SW_NTP6_BARLIMIT3,
> +            IDT_SW_NTP6_BARLTBASE3,    IDT_SW_NTP6_BARUTBASE3},
> +           {IDT_SW_NTP6_BARSETUP4,     IDT_SW_NTP6_BARLIMIT4,
> +            IDT_SW_NTP6_BARLTBASE4,    IDT_SW_NTP6_BARUTBASE4},
> +           {IDT_SW_NTP6_BARSETUP5,     IDT_SW_NTP6_BARLIMIT5,
> +            IDT_SW_NTP6_BARLTBASE5,    IDT_SW_NTP6_BARUTBASE5} } },
> +/*7*/  {0},
> +/*8*/  { IDT_SW_NTP8_PCIECMDSTS,       IDT_SW_NTP8_PCIELCTLSTS,
> +         IDT_SW_NTP8_NTCTL,
> +         IDT_SW_SWPORT8CTL,            IDT_SW_SWPORT8STS,
> +         { {IDT_SW_NTP8_BARSETUP0,     IDT_SW_NTP8_BARLIMIT0,
> +            IDT_SW_NTP8_BARLTBASE0,    IDT_SW_NTP8_BARUTBASE0},
> +           {IDT_SW_NTP8_BARSETUP1,     IDT_SW_NTP8_BARLIMIT1,
> +            IDT_SW_NTP8_BARLTBASE1,    IDT_SW_NTP8_BARUTBASE1},
> +           {IDT_SW_NTP8_BARSETUP2,     IDT_SW_NTP8_BARLIMIT2,
> +            IDT_SW_NTP8_BARLTBASE2,    IDT_SW_NTP8_BARUTBASE2},
> +           {IDT_SW_NTP8_BARSETUP3,     IDT_SW_NTP8_BARLIMIT3,
> +            IDT_SW_NTP8_BARLTBASE3,    IDT_SW_NTP8_BARUTBASE3},
> +           {IDT_SW_NTP8_BARSETUP4,     IDT_SW_NTP8_BARLIMIT4,
> +            IDT_SW_NTP8_BARLTBASE4,    IDT_SW_NTP8_BARUTBASE4},
> +           {IDT_SW_NTP8_BARSETUP5,     IDT_SW_NTP8_BARLIMIT5,
> +            IDT_SW_NTP8_BARLTBASE5,    IDT_SW_NTP8_BARUTBASE5} } },
> +/*9*/  {0},
> +/*10*/ {0},
> +/*11*/ {0},
> +/*12*/ { IDT_SW_NTP12_PCIECMDSTS,      IDT_SW_NTP12_PCIELCTLSTS,
> +         IDT_SW_NTP12_NTCTL,
> +         IDT_SW_SWPORT12CTL,           IDT_SW_SWPORT12STS,
> +         { {IDT_SW_NTP12_BARSETUP0,    IDT_SW_NTP12_BARLIMIT0,
> +            IDT_SW_NTP12_BARLTBASE0,   IDT_SW_NTP12_BARUTBASE0},
> +           {IDT_SW_NTP12_BARSETUP1,    IDT_SW_NTP12_BARLIMIT1,
> +            IDT_SW_NTP12_BARLTBASE1,   IDT_SW_NTP12_BARUTBASE1},
> +           {IDT_SW_NTP12_BARSETUP2,    IDT_SW_NTP12_BARLIMIT2,
> +            IDT_SW_NTP12_BARLTBASE2,   IDT_SW_NTP12_BARUTBASE2},
> +           {IDT_SW_NTP12_BARSETUP3,    IDT_SW_NTP12_BARLIMIT3,
> +            IDT_SW_NTP12_BARLTBASE3,   IDT_SW_NTP12_BARUTBASE3},
> +           {IDT_SW_NTP12_BARSETUP4,    IDT_SW_NTP12_BARLIMIT4,
> +            IDT_SW_NTP12_BARLTBASE4,   IDT_SW_NTP12_BARUTBASE4},
> +           {IDT_SW_NTP12_BARSETUP5,    IDT_SW_NTP12_BARLIMIT5,
> +            IDT_SW_NTP12_BARLTBASE5,   IDT_SW_NTP12_BARUTBASE5} } },
> +/*13*/ {0},
> +/*14*/ {0},
> +/*15*/ {0},
> +/*16*/ { IDT_SW_NTP16_PCIECMDSTS,      IDT_SW_NTP16_PCIELCTLSTS,
> +         IDT_SW_NTP16_NTCTL,
> +         IDT_SW_SWPORT16CTL,           IDT_SW_SWPORT16STS,
> +         { {IDT_SW_NTP16_BARSETUP0,    IDT_SW_NTP16_BARLIMIT0,
> +            IDT_SW_NTP16_BARLTBASE0,   IDT_SW_NTP16_BARUTBASE0},
> +           {IDT_SW_NTP16_BARSETUP1,    IDT_SW_NTP16_BARLIMIT1,
> +            IDT_SW_NTP16_BARLTBASE1,   IDT_SW_NTP16_BARUTBASE1},
> +           {IDT_SW_NTP16_BARSETUP2,    IDT_SW_NTP16_BARLIMIT2,
> +            IDT_SW_NTP16_BARLTBASE2,   IDT_SW_NTP16_BARUTBASE2},
> +           {IDT_SW_NTP16_BARSETUP3,    IDT_SW_NTP16_BARLIMIT3,
> +            IDT_SW_NTP16_BARLTBASE3,   IDT_SW_NTP16_BARUTBASE3},
> +           {IDT_SW_NTP16_BARSETUP4,    IDT_SW_NTP16_BARLIMIT4,
> +            IDT_SW_NTP16_BARLTBASE4,   IDT_SW_NTP16_BARUTBASE4},
> +           {IDT_SW_NTP16_BARSETUP5,    IDT_SW_NTP16_BARLIMIT5,
> +            IDT_SW_NTP16_BARLTBASE5,   IDT_SW_NTP16_BARUTBASE5} } },
> +/*17*/ {0},
> +/*18*/ {0},
> +/*19*/ {0},
> +/*20*/ { IDT_SW_NTP20_PCIECMDSTS,      IDT_SW_NTP20_PCIELCTLSTS,
> +         IDT_SW_NTP20_NTCTL,
> +         IDT_SW_SWPORT20CTL,           IDT_SW_SWPORT20STS,
> +         { {IDT_SW_NTP20_BARSETUP0,    IDT_SW_NTP20_BARLIMIT0,
> +            IDT_SW_NTP20_BARLTBASE0,   IDT_SW_NTP20_BARUTBASE0},
> +           {IDT_SW_NTP20_BARSETUP1,    IDT_SW_NTP20_BARLIMIT1,
> +            IDT_SW_NTP20_BARLTBASE1,   IDT_SW_NTP20_BARUTBASE1},
> +           {IDT_SW_NTP20_BARSETUP2,    IDT_SW_NTP20_BARLIMIT2,
> +            IDT_SW_NTP20_BARLTBASE2,   IDT_SW_NTP20_BARUTBASE2},
> +           {IDT_SW_NTP20_BARSETUP3,    IDT_SW_NTP20_BARLIMIT3,
> +            IDT_SW_NTP20_BARLTBASE3,   IDT_SW_NTP20_BARUTBASE3},
> +           {IDT_SW_NTP20_BARSETUP4,    IDT_SW_NTP20_BARLIMIT4,
> +            IDT_SW_NTP20_BARLTBASE4,   IDT_SW_NTP20_BARUTBASE4},
> +           {IDT_SW_NTP20_BARSETUP5,    IDT_SW_NTP20_BARLIMIT5,
> +            IDT_SW_NTP20_BARLTBASE5,   IDT_SW_NTP20_BARUTBASE5} } },
> +/*21*/ {0},
> +/*22*/ {0},
> +/*23*/ {0}
> +};
> +
> +/*
> + * IDT PCIe-switch partitions table with the corresponding control, status
> + * and messages control registers
> + */
> +static const struct idt_ntb_part partdata_tbl[IDT_MAX_NR_PARTS] = {
> +/*0*/  { IDT_SW_SWPART0CTL,    IDT_SW_SWPART0STS,
> +         {IDT_SW_SWP0MSGCTL0,  IDT_SW_SWP0MSGCTL1,
> +          IDT_SW_SWP0MSGCTL2,  IDT_SW_SWP0MSGCTL3} },
> +/*1*/  { IDT_SW_SWPART1CTL,    IDT_SW_SWPART1STS,
> +         {IDT_SW_SWP1MSGCTL0,  IDT_SW_SWP1MSGCTL1,
> +          IDT_SW_SWP1MSGCTL2,  IDT_SW_SWP1MSGCTL3} },
> +/*2*/  { IDT_SW_SWPART2CTL,    IDT_SW_SWPART2STS,
> +         {IDT_SW_SWP2MSGCTL0,  IDT_SW_SWP2MSGCTL1,
> +          IDT_SW_SWP2MSGCTL2,  IDT_SW_SWP2MSGCTL3} },
> +/*3*/  { IDT_SW_SWPART3CTL,    IDT_SW_SWPART3STS,
> +         {IDT_SW_SWP3MSGCTL0,  IDT_SW_SWP3MSGCTL1,
> +          IDT_SW_SWP3MSGCTL2,  IDT_SW_SWP3MSGCTL3} },
> +/*4*/  { IDT_SW_SWPART4CTL,    IDT_SW_SWPART4STS,
> +         {IDT_SW_SWP4MSGCTL0,  IDT_SW_SWP4MSGCTL1,
> +          IDT_SW_SWP4MSGCTL2,  IDT_SW_SWP4MSGCTL3} },
> +/*5*/  { IDT_SW_SWPART5CTL,    IDT_SW_SWPART5STS,
> +         {IDT_SW_SWP5MSGCTL0,  IDT_SW_SWP5MSGCTL1,
> +          IDT_SW_SWP5MSGCTL2,  IDT_SW_SWP5MSGCTL3} },
> +/*6*/  { IDT_SW_SWPART6CTL,    IDT_SW_SWPART6STS,
> +         {IDT_SW_SWP6MSGCTL0,  IDT_SW_SWP6MSGCTL1,
> +          IDT_SW_SWP6MSGCTL2,  IDT_SW_SWP6MSGCTL3} },
> +/*7*/  { IDT_SW_SWPART7CTL,    IDT_SW_SWPART7STS,
> +         {IDT_SW_SWP7MSGCTL0,  IDT_SW_SWP7MSGCTL1,
> +          IDT_SW_SWP7MSGCTL2,  IDT_SW_SWP7MSGCTL3} }
> +};
> +
> +/*
> + * DebugFS directory to place the driver debug file
> + */
> +static struct dentry *dbgfs_topdir;
> +
> +/*=============================================================================
> + *                1. IDT PCIe-switch registers IO-functions
> + *
> + *    Beside ordinary configuration space registers IDT PCIe-switch expose
> + * global configuration registers, which are used to determine state of other
> + * device ports as well as being notified of some switch-related events.
> + * Additionally all the configuration space registers of all the IDT
> + * PCIe-switch functions are mapped to the Global Address space, so each
> + * function can determine a configuration of any other PCI-function.
> + *    Functions declared in this chapter are created to encapsulate access
> + * to configuration and global registers, so the driver code just need to
> + * provide IDT NTB hardware descriptor and a register address.
> + *=============================================================================
> + */
> +
> +/*
> + * idt_nt_write() - PCI configuration space registers write method
> + * @ndev:      IDT NTB hardware driver descriptor
> + * @reg:       Register to write data to
> + * @data:      Value to write to the register
> + *
> + * WARNING! IDT PCIe-switch registers are all Little endian. So corresponding
> + *         writel operations must have embedded endiannes conversion. If local
> + *         platform doesn't have it, the driver won't properly work.
> + */
> +static void idt_nt_write(struct idt_ntb_dev *ndev,
> +                        const unsigned int reg, const u32 data)
> +{
> +       /*
> +        * It's obvious bug to request a register exceeding the maximum possible
> +        * value as well as to have it unaligned.
> +        */
> +       if (WARN_ON(reg > IDT_REG_PCI_MAX || !IS_ALIGNED(reg, IDT_REG_ALIGN)))
> +               return;
> +
> +       /* Just write the value to the specified register */
> +       writel(data, ndev->cfgspc + (ptrdiff_t)reg);
> +}
> +
> +/*
> + * idt_nt_read() - PCI configuration space registers read method
> + * @ndev:      IDT NTB hardware driver descriptor
> + * @reg:       Register to write data to
> + *
> + * WARNING! IDT PCIe-switch registers are all Little endian. So corresponding
> + *         readl operations must have embedded endiannes conversion. If local
> + *         platform doesn't have it, the driver won't properly work.
> + *
> + * Return: register value
> + */
> +static u32 idt_nt_read(struct idt_ntb_dev *ndev, const unsigned int reg)
> +{
> +       /*
> +        * It's obvious bug to request a register exceeding the maximum possible
> +        * value as well as to have it unaligned.
> +        */
> +       if (WARN_ON(reg > IDT_REG_PCI_MAX || !IS_ALIGNED(reg, IDT_REG_ALIGN)))
> +               return ~0;
> +
> +       /* Just read the value from the specified register */
> +       return readl(ndev->cfgspc + (ptrdiff_t)reg);
> +}
> +
> +/*
> + * idt_sw_write() - Global registers write method
> + * @ndev:      IDT NTB hardware driver descriptor
> + * @reg:       Register to write data to
> + * @data:      Value to write to the register
> + *
> + * WARNING! IDT PCIe-switch registers are all Little endian. So corresponding
> + *         writel operations must have embedded endiannes conversion. If local
> + *         platform doesn't have it, the driver won't properly work.
> + */
> +static void idt_sw_write(struct idt_ntb_dev *ndev,
> +                        const unsigned int reg, const u32 data)
> +{
> +       unsigned long irqflags;
> +
> +       /*
> +        * It's obvious bug to request a register exceeding the maximum possible
> +        * value as well as to have it unaligned.
> +        */
> +       if (WARN_ON(reg > IDT_REG_SW_MAX || !IS_ALIGNED(reg, IDT_REG_ALIGN)))
> +               return;
> +
> +       /* Lock GASA registers operations */
> +       spin_lock_irqsave(&ndev->gasa_lock, irqflags);
> +       /* Set the global register address */
> +       writel((u32)reg, ndev->cfgspc + (ptrdiff_t)IDT_NT_GASAADDR);
> +       /* Put the new value of the register */
> +       writel(data, ndev->cfgspc + (ptrdiff_t)IDT_NT_GASADATA);
> +       /* Unlock GASA registers operations */
> +       spin_unlock_irqrestore(&ndev->gasa_lock, irqflags);
> +}
> +
> +/*
> + * idt_sw_read() - Global registers read method
> + * @ndev:      IDT NTB hardware driver descriptor
> + * @reg:       Register to write data to
> + *
> + * WARNING! IDT PCIe-switch registers are all Little endian. So corresponding
> + *         readl operations must have embedded endiannes conversion. If local
> + *         platform doesn't have it, the driver won't properly work.
> + *
> + * Return: register value
> + */
> +static u32 idt_sw_read(struct idt_ntb_dev *ndev, const unsigned int reg)
> +{
> +       unsigned long irqflags;
> +       u32 data;
> +
> +       /*
> +        * It's obvious bug to request a register exceeding the maximum possible
> +        * value as well as to have it unaligned.
> +        */
> +       if (WARN_ON(reg > IDT_REG_SW_MAX || !IS_ALIGNED(reg, IDT_REG_ALIGN)))
> +               return ~0;
> +
> +       /* Lock GASA registers operations */
> +       spin_lock_irqsave(&ndev->gasa_lock, irqflags);
> +       /* Set the global register address */
> +       writel((u32)reg, ndev->cfgspc + (ptrdiff_t)IDT_NT_GASAADDR);
> +       /* Get the data of the register */
> +       data = readl(ndev->cfgspc + (ptrdiff_t)IDT_NT_GASADATA);
> +       /* Unlock GASA registers operations */
> +       spin_unlock_irqrestore(&ndev->gasa_lock, irqflags);
> +
> +       return data;
> +}
> +
> +/*
> + * idt_reg_set_bits() - set bits of a passed register
> + * @ndev:      IDT NTB hardware driver descriptor
> + * @reg:       Register to change bits of
> + * @reg_lock:  Register access spin lock
> + * @valid_mask:        Mask of valid bits
> + * @set_bits:  Bitmask to set
> + *
> + * Helper method to check whether a passed bitfield is valid and set
> + * corresponding bits of a register.
> + *
> + * WARNING! Make sure the passed register isn't accessed over plane
> + * idt_nt_write() method (read method is ok to be used concurrently).
> + *
> + * Return: zero on success, negative error on invalid bitmask.
> + */
> +static inline int idt_reg_set_bits(struct idt_ntb_dev *ndev, unsigned int reg,
> +                                  spinlock_t *reg_lock,
> +                                  u64 valid_mask, u64 set_bits)
> +{
> +       unsigned long irqflags;
> +       u32 data;
> +
> +       if (set_bits & ~(u64)valid_mask)
> +               return -EINVAL;
> +
> +       /* Lock access to the register unless the change is written back */
> +       spin_lock_irqsave(reg_lock, irqflags);
> +       data = idt_nt_read(ndev, reg) | (u32)set_bits;
> +       idt_nt_write(ndev, reg, data);
> +       /* Unlock the register */
> +       spin_unlock_irqrestore(reg_lock, irqflags);
> +
> +       return 0;
> +}
> +
> +/*
> + * idt_reg_clear_bits() - clear bits of a passed register
> + * @ndev:      IDT NTB hardware driver descriptor
> + * @reg:       Register to change bits of
> + * @reg_lock:  Register access spin lock
> + * @set_bits:  Bitmask to clear
> + *
> + * Helper method to check whether a passed bitfield is valid and clear
> + * corresponding bits of a register.
> + *
> + * NOTE! Invalid bits are always considered cleared so it's not an error
> + * to clear them over.
> + *
> + * WARNING! Make sure the passed register isn't accessed over plane
> + * idt_nt_write() method (read method is ok to use concurrently).
> + */
> +static inline void idt_reg_clear_bits(struct idt_ntb_dev *ndev,
> +                                    unsigned int reg, spinlock_t *reg_lock,
> +                                    u64 clear_bits)
> +{
> +       unsigned long irqflags;
> +       u32 data;
> +
> +       /* Lock access to the register unless the change is written back */
> +       spin_lock_irqsave(reg_lock, irqflags);
> +       data = idt_nt_read(ndev, reg) & ~(u32)clear_bits;
> +       idt_nt_write(ndev, reg, data);
> +       /* Unlock the register */
> +       spin_unlock_irqrestore(reg_lock, irqflags);
> +}
> +
> +/*===========================================================================
> + *                           2. Ports operations
> + *
> + *    IDT PCIe-switches can have from 3 up to 8 ports with possible
> + * NT-functions enabled. So all the possible ports need to be scanned looking
> + * for NTB activated. NTB API will have enumerated only the ports with NTB.
> + *===========================================================================
> + */
> +
> +/*
> + * idt_scan_ports() - scan IDT PCIe-switch ports collecting info in the tables
> + * @ndev:      Pointer to the PCI device descriptor
> + *
> + * Return: zero on success, otherwise a negative error number.
> + */
> +static int idt_scan_ports(struct idt_ntb_dev *ndev)
> +{
> +       unsigned char pidx, port, part;
> +       u32 data, portsts, partsts;
> +
> +       /* Retrieve the local port number */
> +       data = idt_nt_read(ndev, IDT_NT_PCIELCAP);
> +       ndev->port = GET_FIELD(PCIELCAP_PORTNUM, data);
> +
> +       /* Retrieve the local partition number */
> +       portsts = idt_sw_read(ndev, portdata_tbl[ndev->port].sts);
> +       ndev->part = GET_FIELD(SWPORTxSTS_SWPART, portsts);
> +
> +       /* Initialize port/partition -> index tables with invalid values */
> +       memset(ndev->port_idx_map, -EINVAL, sizeof(ndev->port_idx_map));
> +       memset(ndev->part_idx_map, -EINVAL, sizeof(ndev->part_idx_map));
> +
> +       /*
> +        * Walk over all the possible ports checking whether any of them has
> +        * NT-function activated
> +        */
> +       ndev->peer_cnt = 0;
> +       for (pidx = 0; pidx < ndev->swcfg->port_cnt; pidx++) {
> +               port = ndev->swcfg->ports[pidx];
> +               /* Skip local port */
> +               if (port == ndev->port)
> +                       continue;
> +
> +               /* Read the port status register to get it partition */
> +               portsts = idt_sw_read(ndev, portdata_tbl[port].sts);
> +               part = GET_FIELD(SWPORTxSTS_SWPART, portsts);
> +
> +               /* Retrieve the partition status */
> +               partsts = idt_sw_read(ndev, partdata_tbl[part].sts);
> +               /* Check if partition state is active and port has NTB */
> +               if (IS_FLD_SET(SWPARTxSTS_STATE, partsts, ACT) &&
> +                   (IS_FLD_SET(SWPORTxSTS_MODE, portsts, NT) ||
> +                    IS_FLD_SET(SWPORTxSTS_MODE, portsts, USNT) ||
> +                    IS_FLD_SET(SWPORTxSTS_MODE, portsts, USNTDMA) ||
> +                    IS_FLD_SET(SWPORTxSTS_MODE, portsts, NTDMA))) {
> +                       /* Save the port and partition numbers */
> +                       ndev->peers[ndev->peer_cnt].port = port;
> +                       ndev->peers[ndev->peer_cnt].part = part;
> +                       /* Fill in the port/partition -> index tables */
> +                       ndev->port_idx_map[port] = ndev->peer_cnt;
> +                       ndev->part_idx_map[part] = ndev->peer_cnt;
> +                       ndev->peer_cnt++;
> +               }
> +       }
> +
> +       dev_dbg_pci(ndev, "IDT NT local port: %hhu, num of peers: %hhu\n",
> +               ndev->port, ndev->peer_cnt);
> +
> +       /* It's useless to have this driver loaded if there is no any peer */
> +       if (ndev->peer_cnt == 0) {
> +               dev_warn_pci(ndev, "No active peer found\n");
> +               return -ENODEV;
> +       }
> +
> +       return 0;
> +}
> +
> +/*
> + * idt_ntb_port_number() - get the local port number
> + * @ntb:       NTB device context.
> + *
> + * Return: the local port number
> + */
> +static int idt_ntb_port_number(struct ntb_dev *ntb)
> +{
> +       struct idt_ntb_dev *ndev = to_ndev_ntb(ntb);
> +
> +       return ndev->port;
> +}
> +
> +/*
> + * idt_ntb_peer_port_count() - get the number of peer ports
> + * @ntb:       NTB device context.
> + *
> + * Return the count of detected peer NT-functions.
> + *
> + * Return: number of peer ports
> + */
> +static int idt_ntb_peer_port_count(struct ntb_dev *ntb)
> +{
> +       struct idt_ntb_dev *ndev = to_ndev_ntb(ntb);
> +
> +       return ndev->peer_cnt;
> +}
> +
> +/*
> + * idt_ntb_peer_port_number() - get peer port by given index
> + * @ntb:       NTB device context.
> + * @pidx:      Peer port index.
> + *
> + * Return: peer port or negative error
> + */
> +static int idt_ntb_peer_port_number(struct ntb_dev *ntb, int pidx)
> +{
> +       struct idt_ntb_dev *ndev = to_ndev_ntb(ntb);
> +
> +       if (pidx < 0 || ndev->peer_cnt <= pidx)
> +               return -EINVAL;
> +
> +       /* Return the detected NT-function port number */
> +       return ndev->peers[pidx].port;
> +}
> +
> +/*
> + * idt_ntb_peer_port_idx() - get peer port index by given port number
> + * @ntb:       NTB device context.
> + * @port:      Peer port number.
> + *
> + * Internal port -> index table is pre-initialized with -EINVAL values,
> + * so we just need to return it value
> + *
> + * Return: peer NT-function port index or negative error
> + */
> +static int idt_ntb_peer_port_idx(struct ntb_dev *ntb, int port)
> +{
> +       struct idt_ntb_dev *ndev = to_ndev_ntb(ntb);
> +
> +       if (port < 0 || IDT_MAX_NR_PORTS <= port)
> +               return -EINVAL;
> +
> +       return ndev->port_idx_map[port];
> +}
> +
> +/*===========================================================================
> + *                         3. Link status operations
> + *    There is no any ready-to-use method to have peer ports notified if NTB
> + * link is set up or got down. Instead global signal can be used instead.
> + * In case if any one of ports changes local NTB link state, it sends
> + * global signal and clears corresponding global state bit. Then all the ports
> + * receive a notification of that, so to make client driver being aware of
> + * possible NTB link change.
> + *    Additionally each of active NT-functions is subscribed to PCIe-link
> + * state changes of peer ports.
> + *===========================================================================
> + */
> +
> +static void idt_ntb_local_link_disable(struct idt_ntb_dev *ndev);
> +
> +/*
> + * idt_init_link() - Initialize NTB link state notification subsystem
> + * @ndev:      IDT NTB hardware driver descriptor
> + *
> + * Function performs the basic initialization of some global registers
> + * needed to enable IRQ-based notifications of PCIe Link Up/Down and
> + * Global Signal events.
> + * NOTE Since it's not possible to determine when all the NTB peer drivers are
> + * unloaded as well as have those registers accessed concurrently, we must
> + * preinitialize them with the same value and leave it uncleared on local
> + * driver unload.
> + */
> +static void idt_init_link(struct idt_ntb_dev *ndev)
> +{
> +       u32 part_mask, port_mask, se_mask;
> +       unsigned char pidx;
> +
> +       /* Initialize spin locker of Mapping Table access registers */
> +       spin_lock_init(&ndev->mtbl_lock);
> +
> +       /* Walk over all detected peers collecting port and partition masks */
> +       port_mask = ~BIT(ndev->port);
> +       part_mask = ~BIT(ndev->part);
> +       for (pidx = 0; pidx < ndev->peer_cnt; pidx++) {
> +               port_mask &= ~BIT(ndev->peers[pidx].port);
> +               part_mask &= ~BIT(ndev->peers[pidx].part);
> +       }
> +
> +       /* Clean the Link Up/Down and GLobal Signal status registers */
> +       idt_sw_write(ndev, IDT_SW_SELINKUPSTS, (u32)-1);
> +       idt_sw_write(ndev, IDT_SW_SELINKDNSTS, (u32)-1);
> +       idt_sw_write(ndev, IDT_SW_SEGSIGSTS, (u32)-1);
> +
> +       /* Unmask NT-activated partitions to receive Global Switch events */
> +       idt_sw_write(ndev, IDT_SW_SEPMSK, part_mask);
> +
> +       /* Enable PCIe Link Up events of NT-activated ports */
> +       idt_sw_write(ndev, IDT_SW_SELINKUPMSK, port_mask);
> +
> +       /* Enable PCIe Link Down events of NT-activated ports */
> +       idt_sw_write(ndev, IDT_SW_SELINKDNMSK, port_mask);
> +
> +       /* Unmask NT-activated partitions to receive Global Signal events */
> +       idt_sw_write(ndev, IDT_SW_SEGSIGMSK, part_mask);
> +
> +       /* Unmask Link Up/Down and Global Switch Events */
> +       se_mask = ~(IDT_SEMSK_LINKUP | IDT_SEMSK_LINKDN | IDT_SEMSK_GSIGNAL);
> +       idt_sw_write(ndev, IDT_SW_SEMSK, se_mask);
> +
> +       dev_dbg_pci(ndev, "IDT NTB link status events initialized");
> +}
> +
> +/*
> + * idt_deinit_link() - deinitialize link subsystem
> + * @ndev:      IDT NTB hardware driver descriptor
> + *
> + * Just disable the link back.
> + */
> +static void idt_deinit_link(struct idt_ntb_dev *ndev)
> +{
> +       /* Disable the link */
> +       idt_ntb_local_link_disable(ndev);
> +
> +       dev_dbg_pci(ndev, "IDT NTB link status events deinitialized");
> +}
> +
> +/*
> + * idt_se_isr() - switch events ISR
> + * @ndev:      IDT NTB hardware driver descriptor
> + * @ntint_sts: NT-function interrupt status
> + *
> + * This driver doesn't support IDT PCIe-switch dynamic reconfigurations,
> + * Failover capability, etc, so switch events are utilized to notify of
> + * PCIe and NTB link events.
> + * The method is called from PCIe ISR bottom-half routine.
> + */
> +static void idt_se_isr(struct idt_ntb_dev *ndev, u32 ntint_sts)
> +{
> +       u32 sests;
> +
> +       /* Read Switch Events status */
> +       sests = idt_sw_read(ndev, IDT_SW_SESTS);
> +
> +       /* Clean the Link Up/Down and Global Signal status registers */
> +       idt_sw_write(ndev, IDT_SW_SELINKUPSTS, (u32)-1);
> +       idt_sw_write(ndev, IDT_SW_SELINKDNSTS, (u32)-1);
> +       idt_sw_write(ndev, IDT_SW_SEGSIGSTS, (u32)-1);
> +
> +       /* Clean the corresponding interrupt bit */
> +       idt_nt_write(ndev, IDT_NT_NTINTSTS, IDT_NTINTSTS_SEVENT);
> +
> +       dev_dbg_pci(ndev, "Switch Event IRQ detected %#08x (SESTS %#08x)",
> +                         ntint_sts, sests);
> +
> +       /* Notify the client driver of possible link state change */
> +       ntb_link_event(&ndev->ntb);
> +}
> +
> +/*
> + * idt_ntb_local_link_enable() - enable the local NTB link.
> + * @ndev:      IDT NTB hardware driver descriptor
> + *
> + * In order to enable the NTB link we need:
> + * - enable Completion TLPs translation
> + * - initialize mapping table to enable the Request ID translation
> + * - notify peers of NTB link state change
> + */
> +static void idt_ntb_local_link_enable(struct idt_ntb_dev *ndev)
> +{
> +       u32 reqid, mtbldata = 0;
> +       unsigned long irqflags;
> +
> +       /* Enable the ID protection and Completion TLPs translation */
> +       idt_nt_write(ndev, IDT_NT_NTCTL, IDT_NTCTL_CPEN);
> +
> +       /* Retrieve the current Requester ID (Bus:Device:Function) */
> +       reqid = idt_nt_read(ndev, IDT_NT_REQIDCAP);
> +
> +       /*
> +        * Set the corresponding NT Mapping table entry of port partition index
> +        * with the data to perform the Request ID translation
> +        */
> +       mtbldata = SET_FIELD(NTMTBLDATA_REQID, 0, reqid) |
> +                  SET_FIELD(NTMTBLDATA_PART, 0, ndev->part) |
> +                  IDT_NTMTBLDATA_VALID;
> +       spin_lock_irqsave(&ndev->mtbl_lock, irqflags);
> +       idt_nt_write(ndev, IDT_NT_NTMTBLADDR, ndev->part);
> +       idt_nt_write(ndev, IDT_NT_NTMTBLDATA, mtbldata);
> +       spin_unlock_irqrestore(&ndev->mtbl_lock, irqflags);
> +
> +       /* Notify the peers by setting and clearing the global signal bit */
> +       idt_nt_write(ndev, IDT_NT_NTGSIGNAL, IDT_NTGSIGNAL_SET);
> +       idt_sw_write(ndev, IDT_SW_SEGSIGSTS, (u32)1 << ndev->part);
> +}
> +
> +/*
> + * idt_ntb_local_link_disable() - disable the local NTB link.
> + * @ndev:      IDT NTB hardware driver descriptor
> + *
> + * In order to enable the NTB link we need:
> + * - disable Completion TLPs translation
> + * - clear corresponding mapping table entry
> + * - notify peers of NTB link state change
> + */
> +static void idt_ntb_local_link_disable(struct idt_ntb_dev *ndev)
> +{
> +       unsigned long irqflags;
> +
> +       /* Disable Completion TLPs translation */
> +       idt_nt_write(ndev, IDT_NT_NTCTL, 0);
> +
> +       /* Clear the corresponding NT Mapping table entry */
> +       spin_lock_irqsave(&ndev->mtbl_lock, irqflags);
> +       idt_nt_write(ndev, IDT_NT_NTMTBLADDR, ndev->part);
> +       idt_nt_write(ndev, IDT_NT_NTMTBLDATA, 0);
> +       spin_unlock_irqrestore(&ndev->mtbl_lock, irqflags);
> +
> +       /* Notify the peers by setting and clearing the global signal bit */
> +       idt_nt_write(ndev, IDT_NT_NTGSIGNAL, IDT_NTGSIGNAL_SET);
> +       idt_sw_write(ndev, IDT_SW_SEGSIGSTS, (u32)1 << ndev->part);
> +}
> +
> +/*
> + * idt_ntb_local_link_is_up() - test wethter local NTB link is up
> + * @ndev:      IDT NTB hardware driver descriptor
> + *
> + * Local link is up under the following conditions:
> + * - Bus mastering is enabled
> + * - NTCTL has Completion TLPs translation enabled
> + * - Mapping table permits Request TLPs translation
> + * NOTE: We don't need to check PCIe link state since it's obviously
> + * up while we are able to communicate with IDT PCIe-switch
> + *
> + * Return: true if link is up, otherwise false
> + */
> +static bool idt_ntb_local_link_is_up(struct idt_ntb_dev *ndev)
> +{
> +       unsigned long irqflags;
> +       u32 data;
> +
> +       /* Read the local Bus Master Enable status */
> +       data = idt_nt_read(ndev, IDT_NT_PCICMDSTS);
> +       if (!(data & IDT_PCICMDSTS_BME))
> +               return false;
> +
> +       /* Read the local Completion TLPs translation enable status */
> +       data = idt_nt_read(ndev, IDT_NT_NTCTL);
> +       if (!(data & IDT_NTCTL_CPEN))
> +               return false;
> +
> +       /* Read Mapping table entry corresponding to the local partition */
> +       spin_lock_irqsave(&ndev->mtbl_lock, irqflags);
> +       idt_nt_write(ndev, IDT_NT_NTMTBLADDR, ndev->part);
> +       data = idt_nt_read(ndev, IDT_NT_NTMTBLDATA);
> +       spin_unlock_irqrestore(&ndev->mtbl_lock, irqflags);
> +
> +       return !!(data & IDT_NTMTBLDATA_VALID);
> +}
> +
> +/*
> + * idt_ntb_peer_link_is_up() - test whether peer NTB link is up
> + * @ndev:      IDT NTB hardware driver descriptor
> + * @pidx:      Peer port index
> + *
> + * Peer link is up under the following conditions:
> + * - PCIe link is up
> + * - Bus mastering is enabled
> + * - NTCTL has Completion TLPs translation enabled
> + * - Mapping table permits Request TLPs translation
> + *
> + * Return: true if link is up, otherwise false
> + */
> +static bool idt_ntb_peer_link_is_up(struct idt_ntb_dev *ndev, int pidx)
> +{
> +       unsigned long irqflags;
> +       unsigned char port;
> +       u32 data;
> +
> +       /* Retrieve the device port number */
> +       port = ndev->peers[pidx].port;
> +
> +       /* Check whether PCIe link is up */
> +       data = idt_sw_read(ndev, portdata_tbl[port].sts);
> +       if (!(data & IDT_SWPORTxSTS_LINKUP))
> +               return false;
> +
> +       /* Check whether bus mastering is enabled on the peer port */
> +       data = idt_sw_read(ndev, portdata_tbl[port].pcicmdsts);
> +       if (!(data & IDT_PCICMDSTS_BME))
> +               return false;
> +
> +       /* Check if Completion TLPs translation is enabled on the peer port */
> +       data = idt_sw_read(ndev, portdata_tbl[port].ntctl);
> +       if (!(data & IDT_NTCTL_CPEN))
> +               return false;
> +
> +       /* Read Mapping table entry corresponding to the peer partition */
> +       spin_lock_irqsave(&ndev->mtbl_lock, irqflags);
> +       idt_nt_write(ndev, IDT_NT_NTMTBLADDR, ndev->peers[pidx].part);
> +       data = idt_nt_read(ndev, IDT_NT_NTMTBLDATA);
> +       spin_unlock_irqrestore(&ndev->mtbl_lock, irqflags);
> +
> +       return !!(data & IDT_NTMTBLDATA_VALID);
> +}
> +
> +/*
> + * idt_ntb_link_is_up() - get the current ntb link state (NTB API callback)
> + * @ntb:       NTB device context.
> + * @speed:     OUT - The link speed expressed as PCIe generation number.
> + * @width:     OUT - The link width expressed as the number of PCIe lanes.
> + *
> + * Get the bitfield of NTB link states for all peer ports
> + *
> + * Return: bitfield of indexed ports link state: bit is set/cleared if the
> + *         link is up/down respectively.
> + */
> +static u64 idt_ntb_link_is_up(struct ntb_dev *ntb,
> +                             enum ntb_speed *speed, enum ntb_width *width)
> +{
> +       struct idt_ntb_dev *ndev = to_ndev_ntb(ntb);
> +       unsigned char pidx;
> +       u64 status;
> +       u32 data;
> +
> +       /* Retrieve the local link speed and width */
> +       if (speed != NULL || width != NULL) {
> +               data = idt_nt_read(ndev, IDT_NT_PCIELCTLSTS);
> +               if (speed != NULL)
> +                       *speed = GET_FIELD(PCIELCTLSTS_CLS, data);
> +               if (width != NULL)
> +                       *width = GET_FIELD(PCIELCTLSTS_NLW, data);
> +       }
> +
> +       /* If local NTB link isn't up then all the links are considered down */
> +       if (!idt_ntb_local_link_is_up(ndev))
> +               return 0;
> +
> +       /* Collect all the peer ports link states into the bitfield */
> +       status = 0;
> +       for (pidx = 0; pidx < ndev->peer_cnt; pidx++) {
> +               if (idt_ntb_peer_link_is_up(ndev, pidx))
> +                       status |= ((u64)1 << pidx);
> +       }
> +
> +       return status;
> +}
> +
> +/*
> + * idt_ntb_link_enable() - enable local port ntb link (NTB API callback)
> + * @ntb:       NTB device context.
> + * @max_speed: The maximum link speed expressed as PCIe generation number.
> + * @max_width: The maximum link width expressed as the number of PCIe lanes.
> + *
> + * Enable just local NTB link. PCIe link parameters are ignored.
> + *
> + * Return: always zero.
> + */
> +static int idt_ntb_link_enable(struct ntb_dev *ntb, enum ntb_speed speed,
> +                              enum ntb_width width)
> +{
> +       struct idt_ntb_dev *ndev = to_ndev_ntb(ntb);
> +
> +       /* Just enable the local NTB link */
> +       idt_ntb_local_link_enable(ndev);
> +
> +       dev_dbg_pci(ndev, "IDT local NTB link is enabled");
> +
> +       return 0;
> +}
> +
> +/*
> + * idt_ntb_link_disable() - disable local port ntb link (NTB API callback)
> + * @ntb:       NTB device context.
> + *
> + * Disable just local NTB link.
> + *
> + * Return: always zero.
> + */
> +static int idt_ntb_link_disable(struct ntb_dev *ntb)
> +{
> +       struct idt_ntb_dev *ndev = to_ndev_ntb(ntb);
> +
> +       /* Just disable the local NTB link */
> +       idt_ntb_local_link_disable(ndev);
> +
> +       dev_dbg_pci(ndev, "IDT local NTB link is disabled");
> +
> +       return 0;
> +}
> +
> +/*=============================================================================
> + *                         4. Memory Window operations
> + *
> + *    IDT PCIe-switches have two types of memory windows: MWs with direct
> + * address translation and MWs with LUT based translation. The first type of
> + * MWs is simple map of corresponding BAR address space to a memory space
> + * of specified target port. So it implemets just ont-to-one mapping. Lookup
> + * table in its turn can map one BAR address space to up to 24 different
> + * memory spaces of different ports.
> + *    NT-functions BARs can be turned on to implement either direct or lookup
> + * table based address translations, so:
> + * BAR0 - NT configuration registers space/direct address translation
> + * BAR1 - direct address translation/upper address of BAR0x64
> + * BAR2 - direct address translation/Lookup table with either 12 or 24 entries
> + * BAR3 - direct address translation/upper address of BAR2x64
> + * BAR4 - direct address translation/Lookup table with either 12 or 24 entries
> + * BAR5 - direct address translation/upper address of BAR4x64
> + *    Additionally BAR2 and BAR4 can't have 24-entries LUT enabled at the same
> + * time. Since the BARs setup can be rather complicated this driver implements
> + * a scanning algorithm to have all the possible memory windows configuration
> + * covered.
> + *
> + * NOTE 1 BAR setup must be done before Linux kernel enumerated NT-function
> + * of any port, so this driver would have memory windows configurations fixed.
> + * In this way all initializations must be performed either by platform BIOS
> + * or using EEPROM connected to IDT PCIe-switch master SMBus.
> + *
> + * NOTE 2 This driver expects BAR0 mapping NT-function configuration space.
> + * Easy calculation can give us an upper boundary of 29 possible memory windows
> + * per each NT-function if all the BARs are of 32bit type.
> + *=============================================================================
> + */
> +
> +/*
> + * idt_get_mw_count() - get memory window count
> + * @mw_type:   Memory window type
> + *
> + * Return: number of memory windows with respect to the BAR type
> + */
> +static inline unsigned char idt_get_mw_count(enum idt_mw_type mw_type)
> +{
> +       switch (mw_type) {
> +       case IDT_MW_DIR:
> +               return 1;
> +       case IDT_MW_LUT12:
> +               return 12;
> +       case IDT_MW_LUT24:
> +               return 24;
> +       default:
> +               break;
> +       }
> +
> +       return 0;
> +}
> +
> +/*
> + * idt_scan_mws() - scan memory windows of the port
> + * @ndev:      IDT NTB hardware driver descriptor
> + * @port:      Port to get number of memory windows for
> + * @mw_cnt:    Out - number of memory windows
> + *
> + * It walks over BAR setup registers of the specified port and determines
> + * the memory windows parameters if any activated.
> + *
> + * Return: array of memory windows
> + */
> +static struct idt_mw_cfg *idt_scan_mws(struct idt_ntb_dev *ndev, int port,
> +                                      unsigned char *mw_cnt)
> +{
> +       struct idt_mw_cfg mws[IDT_MAX_NR_MWS], *ret_mws;
> +       const struct idt_ntb_bar *bars;
> +       enum idt_mw_type mw_type;
> +       unsigned char widx, bidx, en_cnt;
> +       bool skip_bar;
> +       int aprt_size;
> +       u32 data;
> +
> +       /* Retrieve the array of the BARs registers */
> +       bars = portdata_tbl[port].bars;
> +
> +       /* Scan all the BARs belonging to the port */
> +       *mw_cnt = 0;
> +       for (bidx = 0; bidx < IDT_BAR_CNT; bidx += 1 + skip_bar) {
> +               /* Read BARSETUP register value */
> +               data = idt_sw_read(ndev, bars[bidx].setup);
> +
> +               /* Skip disabled BARs */
> +               if (!(data & IDT_BARSETUP_EN))
> +                       continue;
> +
> +               /* Skip next BARSETUP if current one has 64bit addressing */
> +               skip_bar = IS_FLD_SET(BARSETUP_TYPE, data, 64);
> +
> +               /* Skip configuration space mapping BARs */
> +               if (data & IDT_BARSETUP_MODE_CFG)
> +                       continue;
> +
> +               /* Retrieve MW type/entries count and aperture size */
> +               mw_type = GET_FIELD(BARSETUP_ATRAN, data);
> +               en_cnt = idt_get_mw_count(mw_type);
> +               aprt_size = (u64)1 << GET_FIELD(BARSETUP_SIZE, data);
> +
> +               /* Save configurations of all available memory windows */
> +               for (widx = 0; widx < en_cnt; widx++, (*mw_cnt)++) {
> +                       /*
> +                        * IDT can expose a limited number of MWs, so it's bug
> +                        * to have more than the driver expects
> +                        */
> +                       if (*mw_cnt >= IDT_MAX_NR_MWS)
> +                               return ERR_PTR(-EINVAL);
> +
> +                       /* Save basic MW info */
> +                       mws[*mw_cnt].type = mw_type;
> +                       mws[*mw_cnt].bar = bidx;
> +                       mws[*mw_cnt].idx = widx;
> +                       /* It's always DWORD aligned */
> +                       mws[*mw_cnt].addr_align = IDT_TRANS_ALIGN;
> +                       /* DIR and LUT approachs differently configure MWs */
> +                       if (mw_type == IDT_MW_DIR)
> +                               mws[*mw_cnt].size_max = aprt_size;
> +                       else if (mw_type == IDT_MW_LUT12)
> +                               mws[*mw_cnt].size_max = aprt_size / 16;
> +                       else
> +                               mws[*mw_cnt].size_max = aprt_size / 32;
> +                       mws[*mw_cnt].size_align = (mw_type == IDT_MW_DIR) ?
> +                               IDT_DIR_SIZE_ALIGN : mws[*mw_cnt].size_max;
> +               }
> +       }
> +
> +       /* Allocate memory for memory window descriptors */
> +       ret_mws = devm_kcalloc(&ndev->ntb.pdev->dev, *mw_cnt,
> +                               sizeof(*ret_mws), GFP_KERNEL);
> +       if (IS_ERR_OR_NULL(ret_mws))
> +               return ERR_PTR(-ENOMEM);
> +
> +       /* Copy the info of detected memory windows */
> +       memcpy(ret_mws, mws, (*mw_cnt)*sizeof(*ret_mws));
> +
> +       return ret_mws;
> +}
> +
> +/*
> + * idt_init_mws() - initialize memory windows subsystem
> + * @ndev:      IDT NTB hardware driver descriptor
> + *
> + * Scan BAR setup registers of local and peer ports to determine the
> + * outbound and inbound memory windows parameters
> + *
> + * Return: zero on success, otherwise a negative error number
> + */
> +static int idt_init_mws(struct idt_ntb_dev *ndev)
> +{
> +       struct idt_ntb_peer *peer;
> +       unsigned char pidx;
> +
> +       /* Scan memory windows of the local port */
> +       ndev->mws = idt_scan_mws(ndev, ndev->port, &ndev->mw_cnt);
> +       if (IS_ERR(ndev->mws)) {
> +               dev_err_pci(ndev, "Failed to scan mws of local port %hhu",
> +                       ndev->port);
> +               return PTR_ERR(ndev->mws);
> +       }
> +
> +       /* Scan memory windows of the peer ports */
> +       for (pidx = 0; pidx < ndev->peer_cnt; pidx++) {
> +               peer = &ndev->peers[pidx];
> +               peer->mws = idt_scan_mws(ndev, peer->port, &peer->mw_cnt);
> +               if (IS_ERR(peer->mws)) {
> +                       dev_err_pci(ndev, "Failed to scan mws of port %hhu",
> +                               peer->port);
> +                       return PTR_ERR(peer->mws);
> +               }
> +       }
> +
> +       /* Initialize spin locker of the LUT registers */
> +       spin_lock_init(&ndev->lut_lock);
> +
> +       dev_dbg_pci(ndev, "IDT %hhu outbound and all inbound MWs initialized",
> +               ndev->mw_cnt);
> +
> +       return 0;
> +}
> +
> +/*
> + * idt_ntb_mw_count() - number of inbound memory windows (NTB API callback)
> + * @ntb:       NTB device context.
> + * @pidx:      Port index of peer device.
> + *
> + * The value is returned for the specified peer, so generally speaking it can
> + * be different for different port depending on the IDT PCIe-switch
> + * initialization.
> + *
> + * Return: the number of memory windows.
> + */
> +static int idt_ntb_mw_count(struct ntb_dev *ntb, int pidx)
> +{
> +       struct idt_ntb_dev *ndev = to_ndev_ntb(ntb);
> +
> +       if (pidx < 0 || ndev->peer_cnt <= pidx)
> +               return -EINVAL;
> +
> +       return ndev->peers[pidx].mw_cnt;
> +}
> +
> +/*
> + * idt_ntb_mw_get_align() - inbound memory window parameters (NTB API callback)
> + * @ntb:       NTB device context.
> + * @pidx:      Port index of peer device.
> + * @widx:      Memory window index.
> + * @addr_align:        OUT - the base alignment for translating the memory window
> + * @size_align:        OUT - the size alignment for translating the memory window
> + * @size_max:  OUT - the maximum size of the memory window
> + *
> + * The peer memory window parameters have already been determined, so just
> + * return the corresponding values, which mustn't change within session.
> + *
> + * Return: Zero on success, otherwise a negative error number.
> + */
> +static int idt_ntb_mw_get_align(struct ntb_dev *ntb, int pidx, int widx,
> +                               resource_size_t *addr_align,
> +                               resource_size_t *size_align,
> +                               resource_size_t *size_max)
> +{
> +       struct idt_ntb_dev *ndev = to_ndev_ntb(ntb);
> +       struct idt_ntb_peer *peer;
> +
> +       if (pidx < 0 || ndev->peer_cnt <= pidx)
> +               return -EINVAL;
> +
> +       peer = &ndev->peers[pidx];
> +
> +       if (widx < 0 || peer->mw_cnt <= widx)
> +               return -EINVAL;
> +
> +       if (addr_align != NULL)
> +               *addr_align = peer->mws[widx].addr_align;
> +
> +       if (size_align != NULL)
> +               *size_align = peer->mws[widx].size_align;
> +
> +       if (size_max != NULL)
> +               *size_max = peer->mws[widx].size_max;
> +
> +       return 0;
> +}
> +
> +/*
> + * idt_ntb_peer_mw_count() - number of outbound memory windows
> + *                          (NTB API callback)
> + * @ntb:       NTB device context.
> + *
> + * Outbound memory windows parameters have been determined based on the
> + * BAR setup registers value, which are mostly constants within one session.
> + *
> + * Return: the number of memory windows.
> + */
> +static int idt_ntb_peer_mw_count(struct ntb_dev *ntb)
> +{
> +       struct idt_ntb_dev *ndev = to_ndev_ntb(ntb);
> +
> +       return ndev->mw_cnt;
> +}
> +
> +/*
> + * idt_ntb_peer_mw_get_addr() - get map address of an outbound memory window
> + *                             (NTB API callback)
> + * @ntb:       NTB device context.
> + * @widx:      Memory window index (within ntb_peer_mw_count() return value).
> + * @base:      OUT - the base address of mapping region.
> + * @size:      OUT - the size of mapping region.
> + *
> + * Return just parameters of BAR resources mapping. Size reflects just the size
> + * of the resource
> + *
> + * Return: Zero on success, otherwise a negative error number.
> + */
> +static int idt_ntb_peer_mw_get_addr(struct ntb_dev *ntb, int widx,
> +                                   phys_addr_t *base, resource_size_t *size)
> +{
> +       struct idt_ntb_dev *ndev = to_ndev_ntb(ntb);
> +
> +       if (widx < 0 || ndev->mw_cnt <= widx)
> +               return -EINVAL;
> +
> +       /* Mapping address is just properly shifted BAR resource start */
> +       if (base != NULL)
> +               *base = pci_resource_start(ntb->pdev, ndev->mws[widx].bar) +
> +                       ndev->mws[widx].idx * ndev->mws[widx].size_max;
> +
> +       /* Mapping size has already been calculated at MWs scanning */
> +       if (size != NULL)
> +               *size = ndev->mws[widx].size_max;
> +
> +       return 0;
> +}
> +
> +/*
> + * idt_ntb_peer_mw_set_trans() - set a translation address of a memory window
> + *                              (NTB API callback)
> + * @ntb:       NTB device context.
> + * @pidx:      Port index of peer device the translation address received from.
> + * @widx:      Memory window index.
> + * @addr:      The dma address of the shared memory to access.
> + * @size:      The size of the shared memory to access.
> + *
> + * The Direct address translation and LUT base translation is initialized a
> + * bit differenet. Although the parameters restriction are now determined by
> + * the same code.
> + *
> + * Return: Zero on success, otherwise an error number.
> + */
> +static int idt_ntb_peer_mw_set_trans(struct ntb_dev *ntb, int pidx, int widx,
> +                                    u64 addr, resource_size_t size)
> +{
> +       struct idt_ntb_dev *ndev = to_ndev_ntb(ntb);
> +       struct idt_mw_cfg *mw_cfg;
> +       u32 data = 0, lutoff = 0;
> +
> +       if (pidx < 0 || ndev->peer_cnt <= pidx)
> +               return -EINVAL;
> +
> +       if (widx < 0 || ndev->mw_cnt <= widx)
> +               return -EINVAL;
> +
> +       /*
> +        * Retrieve the memory window config to make sure the passed arguments
> +        * fit it restrictions
> +        */
> +       mw_cfg = &ndev->mws[widx];
> +       if (!IS_ALIGNED(addr, mw_cfg->addr_align))
> +               return -EINVAL;
> +       if (!IS_ALIGNED(size, mw_cfg->size_align) || size > mw_cfg->size_max)
> +               return -EINVAL;
> +
> +       /* DIR and LUT based translations are initialized differently */
> +       if (mw_cfg->type == IDT_MW_DIR) {
> +               const struct idt_ntb_bar *bar = &ntdata_tbl.bars[mw_cfg->bar];
> +               u64 limit;
> +               /* Set destination partition of translation */
> +               data = idt_nt_read(ndev, bar->setup);
> +               data = SET_FIELD(BARSETUP_TPART, data, ndev->peers[pidx].part);
> +               idt_nt_write(ndev, bar->setup, data);
> +               /* Set translation base address */
> +               idt_nt_write(ndev, bar->ltbase, (u32)addr);
> +               idt_nt_write(ndev, bar->utbase, (u32)(addr >> 32));
> +               /* Set the custom BAR aperture limit */
> +               limit = pci_resource_start(ntb->pdev, mw_cfg->bar) + size;
> +               idt_nt_write(ndev, bar->limit, (u32)limit);
> +               if (IS_FLD_SET(BARSETUP_TYPE, data, 64))
> +                       idt_nt_write(ndev, (bar + 1)->limit, (limit >> 32));
> +       } else {
> +               unsigned long irqflags;
> +               /* Initialize corresponding LUT entry */
> +               lutoff = SET_FIELD(LUTOFFSET_INDEX, 0, mw_cfg->idx) |
> +                        SET_FIELD(LUTOFFSET_BAR, 0, mw_cfg->bar);
> +               data = SET_FIELD(LUTUDATA_PART, 0, ndev->peers[pidx].part) |
> +                       IDT_LUTUDATA_VALID;
> +               spin_lock_irqsave(&ndev->lut_lock, irqflags);
> +               idt_nt_write(ndev, IDT_NT_LUTOFFSET, lutoff);
> +               idt_nt_write(ndev, IDT_NT_LUTLDATA, (u32)addr);
> +               idt_nt_write(ndev, IDT_NT_LUTMDATA, (u32)(addr >> 32));
> +               idt_nt_write(ndev, IDT_NT_LUTUDATA, data);
> +               spin_unlock_irqrestore(&ndev->lut_lock, irqflags);
> +               /* Limit address isn't specified since size is fixed for LUT */
> +       }
> +
> +       return 0;
> +}
> +
> +/*
> + * idt_ntb_peer_mw_clear_trans() - clear the outbound MW translation address
> + *                                (NTB API callback)
> + * @ntb:       NTB device context.
> + * @pidx:      Port index of peer device.
> + * @widx:      Memory window index.
> + *
> + * It effectively disables the translation over the specified outbound MW.
> + *
> + * Return: Zero on success, otherwise an error number.
> + */
> +static int idt_ntb_peer_mw_clear_trans(struct ntb_dev *ntb, int pidx,
> +                                       int widx)
> +{
> +       struct idt_ntb_dev *ndev = to_ndev_ntb(ntb);
> +       struct idt_mw_cfg *mw_cfg;
> +
> +       if (pidx < 0 || ndev->peer_cnt <= pidx)
> +               return -EINVAL;
> +
> +       if (widx < 0 || ndev->mw_cnt <= widx)
> +               return -EINVAL;
> +
> +       mw_cfg = &ndev->mws[widx];
> +
> +       /* DIR and LUT based translations are initialized differently */
> +       if (mw_cfg->type == IDT_MW_DIR) {
> +               const struct idt_ntb_bar *bar = &ntdata_tbl.bars[mw_cfg->bar];
> +               u32 data;
> +               /* Read BARSETUP to check BAR type */
> +               data = idt_nt_read(ndev, bar->setup);
> +               /* Disable translation by specifying zero BAR limit */
> +               idt_nt_write(ndev, bar->limit, 0);
> +               if (IS_FLD_SET(BARSETUP_TYPE, data, 64))
> +                       idt_nt_write(ndev, (bar + 1)->limit, 0);
> +       } else {
> +               unsigned long irqflags;
> +               u32 lutoff;
> +               /* Clear the corresponding LUT entry up */
> +               lutoff = SET_FIELD(LUTOFFSET_INDEX, 0, mw_cfg->idx) |
> +                        SET_FIELD(LUTOFFSET_BAR, 0, mw_cfg->bar);
> +               spin_lock_irqsave(&ndev->lut_lock, irqflags);
> +               idt_nt_write(ndev, IDT_NT_LUTOFFSET, lutoff);
> +               idt_nt_write(ndev, IDT_NT_LUTLDATA, 0);
> +               idt_nt_write(ndev, IDT_NT_LUTMDATA, 0);
> +               idt_nt_write(ndev, IDT_NT_LUTUDATA, 0);
> +               spin_unlock_irqrestore(&ndev->lut_lock, irqflags);
> +       }
> +
> +       return 0;
> +}
> +
> +/*=============================================================================
> + *                          5. Doorbell operations
> + *
> + *    Doorbell functionality of IDT PCIe-switches is pretty unusual. First of
> + * all there is global doorbell register which state can by changed by any
> + * NT-function of the IDT device in accordance with global permissions. These
> + * permissions configs are not supported by NTB API, so it must be done by
> + * either BIOS or EEPROM settings. In the same way the state of the global
> + * doorbell is reflected to the NT-functions local inbound doorbell registers.
> + * It can lead to situations when client driver sets some peer doorbell bits
> + * and get them bounced back to local inbound doorbell if permissions are
> + * granted.
> + *    Secondly there is just one IRQ vector for Doorbell, Message, Temperature
> + * and Switch events, so if client driver left any of Doorbell bits set and
> + * some other event occurred, the driver will be notified of Doorbell event
> + * again.
> + *=============================================================================
> + */
> +
> +/*
> + * idt_db_isr() - doorbell event ISR
> + * @ndev:      IDT NTB hardware driver descriptor
> + * @ntint_sts: NT-function interrupt status
> + *
> + * Doorbell event happans when DBELL bit of NTINTSTS switches from 0 to 1.
> + * It happens only when unmasked doorbell bits are set to ones on completely
> + * zeroed doorbell register.
> + * The method is called from PCIe ISR bottom-half routine.
> + */
> +static void idt_db_isr(struct idt_ntb_dev *ndev, u32 ntint_sts)
> +{
> +       /*
> +        * Doorbell IRQ status will be cleaned only when client
> +        * driver unsets all the doorbell bits.
> +        */
> +       dev_dbg_pci(ndev, "Doorbell IRQ detected %#08x", ntint_sts);
> +
> +       /* Notify the client driver of possible doorbell state change */
> +       ntb_db_event(&ndev->ntb, 0);
> +}
> +
> +/*
> + * idt_ntb_db_valid_mask() - get a mask of doorbell bits supported by the ntb
> + *                          (NTB API callback)
> + * @ntb:       NTB device context.
> + *
> + * IDT PCIe-switches expose just one Doorbell register of DWORD size.
> + *
> + * Return: A mask of doorbell bits supported by the ntb.
> + */
> +static u64 idt_ntb_db_valid_mask(struct ntb_dev *ntb)
> +{
> +       return IDT_DBELL_MASK;
> +}
> +
> +/*
> + * idt_ntb_db_read() - read the local doorbell register (NTB API callback)
> + * @ntb:       NTB device context.
> + *
> + * There is just on inbound doorbell register of each NT-function, so
> + * this method return it value.
> + *
> + * Return: The bits currently set in the local doorbell register.
> + */
> +static u64 idt_ntb_db_read(struct ntb_dev *ntb)
> +{
> +       struct idt_ntb_dev *ndev = to_ndev_ntb(ntb);
> +
> +       return idt_nt_read(ndev, IDT_NT_INDBELLSTS);
> +}
> +
> +/*
> + * idt_ntb_db_clear() - clear bits in the local doorbell register
> + *                     (NTB API callback)
> + * @ntb:       NTB device context.
> + * @db_bits:   Doorbell bits to clear.
> + *
> + * Clear bits of inbound doorbell register by writing ones to it.
> + *
> + * NOTE! Invalid bits are always considered cleared so it's not an error
> + * to clear them over.
> + *
> + * Return: always zero as success.
> + */
> +static int idt_ntb_db_clear(struct ntb_dev *ntb, u64 db_bits)
> +{
> +       struct idt_ntb_dev *ndev = to_ndev_ntb(ntb);
> +
> +       idt_nt_write(ndev, IDT_NT_INDBELLSTS, (u32)db_bits);
> +
> +       return 0;
> +}
> +
> +/*
> + * idt_ntb_db_read_mask() - read the local doorbell mask (NTB API callback)
> + * @ntb:       NTB device context.
> + *
> + * Each inbound doorbell bit can be masked from generating IRQ by setting
> + * the corresponding bit in inbound doorbell mask. So this method returns
> + * the value of the register.
> + *
> + * Return: The bits currently set in the local doorbell mask register.
> + */
> +static u64 idt_ntb_db_read_mask(struct ntb_dev *ntb)
> +{
> +       struct idt_ntb_dev *ndev = to_ndev_ntb(ntb);
> +
> +       return idt_nt_read(ndev, IDT_NT_INDBELLMSK);
> +}
> +
> +/*
> + * idt_ntb_db_set_mask() - set bits in the local doorbell mask
> + *                        (NTB API callback)
> + * @ntb:       NTB device context.
> + * @db_bits:   Doorbell mask bits to set.
> + *
> + * The inbound doorbell register mask value must be read, then OR'ed with
> + * passed field and only then set back.
> + *
> + * Return: zero on success, negative error if invalid argument passed.
> + */
> +static int idt_ntb_db_set_mask(struct ntb_dev *ntb, u64 db_bits)
> +{
> +       struct idt_ntb_dev *ndev = to_ndev_ntb(ntb);
> +
> +       return idt_reg_set_bits(ndev, IDT_NT_INDBELLMSK, &ndev->db_mask_lock,
> +                               IDT_DBELL_MASK, db_bits);
> +}
> +
> +/*
> + * idt_ntb_db_clear_mask() - clear bits in the local doorbell mask
> + *                          (NTB API callback)
> + * @ntb:       NTB device context.
> + * @db_bits:   Doorbell bits to clear.
> + *
> + * The method just clears the set bits up in accordance with the passed
> + * bitfield. IDT PCIe-switch shall generate an interrupt if there hasn't
> + * been any unmasked bit set before current unmasking. Otherwise IRQ won't
> + * be generated since there is only one IRQ vector for all doorbells.
> + *
> + * Return: always zero as success
> + */
> +static int idt_ntb_db_clear_mask(struct ntb_dev *ntb, u64 db_bits)
> +{
> +       struct idt_ntb_dev *ndev = to_ndev_ntb(ntb);
> +
> +       idt_reg_clear_bits(ndev, IDT_NT_INDBELLMSK, &ndev->db_mask_lock,
> +                          db_bits);
> +
> +       return 0;
> +}
> +
> +/*
> + * idt_ntb_peer_db_set() - set bits in the peer doorbell register
> + *                        (NTB API callback)
> + * @ntb:       NTB device context.
> + * @db_bits:   Doorbell bits to set.
> + *
> + * IDT PCIe-switches exposes local outbound doorbell register to change peer
> + * inbound doorbell register state.
> + *
> + * Return: zero on success, negative error if invalid argument passed.
> + */
> +static int idt_ntb_peer_db_set(struct ntb_dev *ntb, u64 db_bits)
> +{
> +       struct idt_ntb_dev *ndev = to_ndev_ntb(ntb);
> +
> +       if (db_bits & ~(u64)IDT_DBELL_MASK)
> +               return -EINVAL;
> +
> +       idt_nt_write(ndev, IDT_NT_OUTDBELLSET, (u32)db_bits);
> +       return 0;
> +}
> +
> +/*=============================================================================
> + *                          6. Messaging operations
> + *
> + *    Each NT-function of IDT PCIe-switch has four inbound and four outbound
> + * message registers. Each outbound message register can be connected to one or
> + * even more than one peer inbound message registers by setting global
> + * configurations. Since NTB API permits one-on-one message registers mapping
> + * only, the driver acts in according with that restriction.
> + *=============================================================================
> + */
> +
> +/*
> + * idt_init_msg() - initialize messaging interface
> + * @ndev:      IDT NTB hardware driver descriptor
> + *
> + * Just initialize the message registers routing tables locker.
> + */
> +static void idt_init_msg(struct idt_ntb_dev *ndev)
> +{
> +       unsigned char midx;
> +
> +       /* Init the messages routing table lockers */
> +       for (midx = 0; midx < IDT_MSG_CNT; midx++)
> +               spin_lock_init(&ndev->msg_locks[midx]);
> +
> +       dev_dbg_pci(ndev, "IDT NTB messaging initialized");
> +}
> +
> +/*
> + * idt_msg_isr() - message event ISR
> + * @ndev:      IDT NTB hardware driver descriptor
> + * @ntint_sts: NT-function interrupt status
> + *
> + * Message event happens when MSG bit of NTINTSTS switches from 0 to 1.
> + * It happens only when unmasked message status bits are set to ones on
> + * completely zeroed message status register.
> + * The method is called from PCIe ISR bottom-half routine.
> + */
> +static void idt_msg_isr(struct idt_ntb_dev *ndev, u32 ntint_sts)
> +{
> +       /*
> +        * Message IRQ status will be cleaned only when client
> +        * driver unsets all the message status bits.
> +        */
> +       dev_dbg_pci(ndev, "Message IRQ detected %#08x", ntint_sts);
> +
> +       /* Notify the client driver of possible message status change */
> +       ntb_msg_event(&ndev->ntb);
> +}
> +
> +/*
> + * idt_ntb_msg_count() - get the number of message registers (NTB API callback)
> + * @ntb:       NTB device context.
> + *
> + * IDT PCIe-switches support four message registers.
> + *
> + * Return: the number of message registers.
> + */
> +static int idt_ntb_msg_count(struct ntb_dev *ntb)
> +{
> +       return IDT_MSG_CNT;
> +}
> +
> +/*
> + * idt_ntb_msg_inbits() - get a bitfield of inbound message registers status
> + *                       (NTB API callback)
> + * @ntb:       NTB device context.
> + *
> + * NT message status register is shared between inbound and outbound message
> + * registers status
> + *
> + * Return: bitfield of inbound message registers.
> + */
> +static u64 idt_ntb_msg_inbits(struct ntb_dev *ntb)
> +{
> +       return (u64)IDT_INMSG_MASK;
> +}
> +
> +/*
> + * idt_ntb_msg_outbits() - get a bitfield of outbound message registers status
> + *                       (NTB API callback)
> + * @ntb:       NTB device context.
> + *
> + * NT message status register is shared between inbound and outbound message
> + * registers status
> + *
> + * Return: bitfield of outbound message registers.
> + */
> +static u64 idt_ntb_msg_outbits(struct ntb_dev *ntb)
> +{
> +       return (u64)IDT_OUTMSG_MASK;
> +}
> +
> +/*
> + * idt_ntb_msg_read_sts() - read the message registers status (NTB API callback)
> + * @ntb:       NTB device context.
> + *
> + * IDT PCIe-switches expose message status registers to notify drivers of
> + * incoming data and failures in case if peer message register isn't freed.
> + *
> + * Return: status bits of message registers
> + */
> +static u64 idt_ntb_msg_read_sts(struct ntb_dev *ntb)
> +{
> +       struct idt_ntb_dev *ndev = to_ndev_ntb(ntb);
> +
> +       return idt_nt_read(ndev, IDT_NT_MSGSTS);
> +}
> +
> +/*
> + * idt_ntb_msg_clear_sts() - clear status bits of message registers
> + *                          (NTB API callback)
> + * @ntb:       NTB device context.
> + * @sts_bits:  Status bits to clear.
> + *
> + * Clear bits in the status register by writing ones.
> + *
> + * NOTE! Invalid bits are always considered cleared so it's not an error
> + * to clear them over.
> + *
> + * Return: always zero as success.
> + */
> +static int idt_ntb_msg_clear_sts(struct ntb_dev *ntb, u64 sts_bits)
> +{
> +       struct idt_ntb_dev *ndev = to_ndev_ntb(ntb);
> +
> +       idt_nt_write(ndev, IDT_NT_MSGSTS, sts_bits);
> +
> +       return 0;
> +}
> +
> +/*
> + * idt_ntb_msg_set_mask() - set mask of message register status bits
> + *                         (NTB API callback)
> + * @ntb:       NTB device context.
> + * @mask_bits: Mask bits.
> + *
> + * Mask the message status bits from raising an IRQ.
> + *
> + * Return: zero on success, negative error if invalid argument passed.
> + */
> +static int idt_ntb_msg_set_mask(struct ntb_dev *ntb, u64 mask_bits)
> +{
> +       struct idt_ntb_dev *ndev = to_ndev_ntb(ntb);
> +
> +       return idt_reg_set_bits(ndev, IDT_NT_MSGSTSMSK, &ndev->msg_mask_lock,
> +                               IDT_MSG_MASK, mask_bits);
> +}
> +
> +/*
> + * idt_ntb_msg_clear_mask() - clear message registers mask
> + *                           (NTB API callback)
> + * @ntb:       NTB device context.
> + * @mask_bits: Mask bits.
> + *
> + * Clear mask of message status bits IRQs.
> + *
> + * Return: always zero as success.
> + */
> +static int idt_ntb_msg_clear_mask(struct ntb_dev *ntb, u64 mask_bits)
> +{
> +       struct idt_ntb_dev *ndev = to_ndev_ntb(ntb);
> +
> +       idt_reg_clear_bits(ndev, IDT_NT_MSGSTSMSK, &ndev->msg_mask_lock,
> +                          mask_bits);
> +
> +       return 0;
> +}
> +
> +/*
> + * idt_ntb_msg_read() - read message register with specified index
> + *                     (NTB API callback)
> + * @ntb:       NTB device context.
> + * @midx:      Message register index
> + * @pidx:      OUT - Port index of peer device a message retrieved from
> + * @msg:       OUT - Data
> + *
> + * Read data from the specified message register and source register.
> + *
> + * Return: zero on success, negative error if invalid argument passed.
> + */
> +static int idt_ntb_msg_read(struct ntb_dev *ntb, int midx, int *pidx, u32 *msg)
> +{
> +       struct idt_ntb_dev *ndev = to_ndev_ntb(ntb);
> +
> +       if (midx < 0 || IDT_MSG_CNT <= midx)
> +               return -EINVAL;
> +
> +       /* Retrieve source port index of the message */
> +       if (pidx != NULL) {
> +               u32 srcpart;
> +
> +               srcpart = idt_nt_read(ndev, ntdata_tbl.msgs[midx].src);
> +               *pidx = ndev->part_idx_map[srcpart];
> +       }
> +
> +       /* Retrieve data of the corresponding message register */
> +       if (msg != NULL)
> +               *msg = idt_nt_read(ndev, ntdata_tbl.msgs[midx].in);
> +
> +       return 0;
> +}
> +
> +/*
> + * idt_ntb_msg_write() - write data to the specified message register
> + *                      (NTB API callback)
> + * @ntb:       NTB device context.
> + * @midx:      Message register index
> + * @pidx:      Port index of peer device a message being sent to
> + * @msg:       Data to send
> + *
> + * Just try to send data to a peer. Message status register should be
> + * checked by client driver.
> + *
> + * Return: zero on success, negative error if invalid argument passed.
> + */
> +static int idt_ntb_msg_write(struct ntb_dev *ntb, int midx, int pidx, u32 msg)
> +{
> +       struct idt_ntb_dev *ndev = to_ndev_ntb(ntb);
> +       unsigned long irqflags;
> +       u32 swpmsgctl = 0;
> +
> +       if (midx < 0 || IDT_MSG_CNT <= midx)
> +               return -EINVAL;
> +
> +       if (pidx < 0 || ndev->peer_cnt <= pidx)
> +               return -EINVAL;
> +
> +       /* Collect the routing information */
> +       swpmsgctl = SET_FIELD(SWPxMSGCTL_REG, 0, midx) |
> +                   SET_FIELD(SWPxMSGCTL_PART, 0, ndev->peers[pidx].part);
> +
> +       /* Lock the messages routing table of the specified register */
> +       spin_lock_irqsave(&ndev->msg_locks[midx], irqflags);
> +       /* Set the route and send the data */
> +       idt_sw_write(ndev, partdata_tbl[ndev->part].msgctl[midx], swpmsgctl);
> +       idt_nt_write(ndev, ntdata_tbl.msgs[midx].out, msg);
> +       /* Unlock the messages routing table */
> +       spin_unlock_irqrestore(&ndev->msg_locks[midx], irqflags);
> +
> +       /* Client driver shall check the status register */
> +       return 0;
> +}
> +
> +/*=============================================================================
> + *                      7. Temperature sensor operations
> + *
> + *    IDT PCIe-switch has an embedded temperature sensor, which can be used to
> + * warn a user-space of possible chip overheating. Since workload temperature
> + * can be different on different platforms, temperature thresholds as well as
> + * general sensor settings must be setup in the framework of BIOS/EEPROM
> + * initializations. It includes the actual sensor enabling as well.
> + *=============================================================================
> + */
> +
> +/*
> + * idt_read_temp() - read temperature from chip sensor
> + * @ntb:       NTB device context.
> + * @val:       OUT - integer value of temperature
> + * @frac:      OUT - fraction
> + */
> +static void idt_read_temp(struct idt_ntb_dev *ndev, unsigned char *val,
> +                         unsigned char *frac)
> +{
> +       u32 data;
> +
> +       /* Read the data from TEMP field of the TMPSTS register */
> +       data = idt_sw_read(ndev, IDT_SW_TMPSTS);
> +       data = GET_FIELD(TMPSTS_TEMP, data);
> +       /* TEMP field has one fractional bit and seven integer bits */
> +       *val = data >> 1;
> +       *frac = ((data & 0x1) ? 5 : 0);
> +}
> +
> +/*
> + * idt_temp_isr() - temperature sensor alarm events ISR
> + * @ndev:      IDT NTB hardware driver descriptor
> + * @ntint_sts: NT-function interrupt status
> + *
> + * It handles events of temperature crossing alarm thresholds. Since reading
> + * of TMPALARM register clears it up, the function doesn't analyze the
> + * read value, instead the current temperature value just warningly printed to
> + * log.
> + * The method is called from PCIe ISR bottom-half routine.
> + */
> +static void idt_temp_isr(struct idt_ntb_dev *ndev, u32 ntint_sts)
> +{
> +       unsigned char val, frac;
> +
> +       /* Read the current temperature value */
> +       idt_read_temp(ndev, &val, &frac);
> +
> +       /* Read the temperature alarm to clean the alarm status out */
> +       /*(void)idt_sw_read(ndev, IDT_SW_TMPALARM);*/
> +
> +       /* Clean the corresponding interrupt bit */
> +       idt_nt_write(ndev, IDT_NT_NTINTSTS, IDT_NTINTSTS_TMPSENSOR);
> +
> +       dev_dbg_pci(ndev, "Temp sensor IRQ detected %#08x", ntint_sts);
> +
> +       /* Print temperature value to log */
> +       dev_warn_pci(ndev, "IDT temperature is %hhu.%hhu", val, frac);
> +}
> +
> +/*=============================================================================
> + *                           8. ISRs related operations
> + *
> + *    IDT PCIe-switch has strangely developed IRQ system. There is just one
> + * interrupt vector for doorbell and message registers. So the hardware driver
> + * can't determine actual source of IRQ if, for example, message event happened
> + * while any of unmasked doorbell is still set. The similar situation may be if
> + * switch or temperature sensor events pop up. The difference is that SEVENT
> + * and TMPSENSOR bits of NT interrupt status register can be cleaned by
> + * IRQ handler so a next interrupt request won't have false handling of
> + * corresponding events.
> + *    The hardware driver has only bottom-half handler of the IRQ, since if any
> + * of events happened the device won't raise it again before the last one is
> + * handled by clearing of corresponding NTINTSTS bit.
> + *=============================================================================
> + */
> +
> +static irqreturn_t idt_thread_isr(int irq, void *devid);
> +
> +/*
> + * idt_init_isr() - initialize PCIe interrupt handler
> + * @ndev:      IDT NTB hardware driver descriptor
> + *
> + * The code is gotoed a bit, but still it's pretty obvious. First it tries
> + * to enable MSI interrupt. If it fails we initiate the INTx interrupt.
> + * The IRQ line will be freed on the driver unload.
> + *
> + * Return: zero on success, otherwise a negative error number.
> + */
> +static int idt_init_isr(struct idt_ntb_dev *ndev)
> +{
> +       struct pci_dev *pdev = ndev->ntb.pdev;
> +       u32 ntint_mask;
> +       int ret;
> +
> +       /* Enable MSI interrupts */
> +       ret = pci_enable_msi(pdev);
> +       if (ret != 0) {
> +               dev_err_pci(ndev, "IDT failed to enable MSI interrupt");
> +               goto err_try_intx;
> +       }
> +
> +       /* Request corresponding IRQ number */
> +       ret = request_threaded_irq(pdev->irq, NULL, idt_thread_isr,
> +                                  IRQF_ONESHOT, NTB_IRQNAME, ndev);
> +       if (ret != 0) {
> +               dev_err_pci(ndev, "IDT failed to set MSI IRQ handler, %d", ret);
> +               goto err_disable_msi;
> +       }
> +
> +       /* From now on the MSI interrupt is used */
> +       dev_dbg_pci(ndev, "IDT NTB MSI interrupts initialized");
> +
> +       /* Unmask Message/Doorbell/SE/Temperature interrupts */
> +       ntint_mask = idt_nt_read(ndev, IDT_NT_NTINTMSK) & ~IDT_NTINTMSK_ALL;
> +       idt_nt_write(ndev, IDT_NT_NTINTMSK, ntint_mask);
> +
> +       /* Just return from the method. IRQs are enabled from init methods */
> +       return 0;
> +
> +err_disable_msi:
> +       pci_disable_msi(pdev);
> +
> +err_try_intx:
> +       /* Enable INTx interrupts since MSI can't be used */
> +       pci_intx(pdev, 1);
> +
> +       /* Request corresponding IRQ number, which may be shared */
> +       ret = request_threaded_irq(pdev->irq, NULL, idt_thread_isr,
> +                                  IRQF_SHARED | IRQF_ONESHOT,
> +                                  NTB_IRQNAME, ndev);
> +       if (ret != 0) {
> +               dev_err_pci(ndev, "IDT failed to set INTx IRQ handler");
> +               goto err_pci_indx;
> +       }
> +
> +       /* From now on the INTx interrupt is used */
> +       dev_dbg_pci(ndev, "IDT NTB INTx interrupts initialized");
> +
> +       /* Unmask Message/Doorbell/SE/Temperature interrupts */
> +       ntint_mask = idt_nt_read(ndev, IDT_NT_NTINTMSK) & ~IDT_NTINTMSK_ALL;
> +       idt_nt_write(ndev, IDT_NT_NTINTMSK, ntint_mask);
> +
> +       return 0;
> +
> +err_pci_indx:
> +       pci_intx(pdev, 0);
> +
> +       return ret;
> +}
> +
> +
> +/*
> + * idt_deinit_ist() - deinitialize PCIe interrupt handler
> + * @ndev:      IDT NTB hardware driver descriptor
> + *
> + * Just disable corresponding interrupts. IRQ number will be freed
> + * on the driver unload.
> + */
> +static void idt_deinit_isr(struct idt_ntb_dev *ndev)
> +{
> +       struct pci_dev *pdev = ndev->ntb.pdev;
> +       u32 ntint_mask;
> +
> +       /* Mask interrupts back */
> +       ntint_mask = idt_nt_read(ndev, IDT_NT_NTINTMSK) | IDT_NTINTMSK_ALL;
> +       idt_nt_write(ndev, IDT_NT_NTINTMSK, ntint_mask);
> +
> +       /* Disable PCIe interrupts */
> +       free_irq(pdev->irq, ndev);
> +       if (pci_dev_msi_enabled(pdev))
> +               pci_disable_msi(pdev);
> +       else
> +               pci_intx(pdev, 0);
> +
> +       dev_dbg_pci(ndev, "IDT NTB interrupts deinitialized");
> +}
> +
> +/*
> + * idt_thread_isr() - NT function interrupts handler
> + * @irq:       IRQ number
> + * @devid:     Custom buffer
> + *
> + * It reads current NT interrupts state register and handles all the event
> + * it declares.
> + * The method is bottom-half routine of actual default PCIe IRQ handler.
> + */
> +static irqreturn_t idt_thread_isr(int irq, void *devid)
> +{
> +       struct idt_ntb_dev *ndev = devid;
> +       bool handled = false;
> +       u32 ntint_sts;
> +
> +       /* Read the NT interrupts status register */
> +       ntint_sts = idt_nt_read(ndev, IDT_NT_NTINTSTS);
> +
> +       /* Handle messaging interrupts */
> +       if (ntint_sts & IDT_NTINTSTS_MSG) {
> +               idt_msg_isr(ndev, ntint_sts);
> +               handled = true;
> +       }
> +
> +       /* Handle doorbell interrupts */
> +       if (ntint_sts & IDT_NTINTSTS_DBELL) {
> +               idt_db_isr(ndev, ntint_sts);
> +               handled = true;
> +       }
> +
> +       /* Handle switch event interrupts */
> +       if (ntint_sts & IDT_NTINTSTS_SEVENT) {
> +               idt_se_isr(ndev, ntint_sts);
> +               handled = true;
> +       }
> +
> +       /* Handle temperature sensor interrupt */
> +       if (ntint_sts & IDT_NTINTSTS_TMPSENSOR) {
> +               idt_temp_isr(ndev, ntint_sts);
> +               handled = true;
> +       }
> +
> +       dev_dbg_pci(ndev, "IDT interrupts 0x%08x handled", ntint_sts);
> +
> +       return handled ? IRQ_HANDLED : IRQ_NONE;
> +}
> +
> +/*===========================================================================
> + *                     9. NTB hardware driver initialization
> + *===========================================================================
> + */
> +
> +/*
> + * NTB API operations
> + */
> +static const struct ntb_dev_ops idt_ntb_ops = {
> +       .port_number            = idt_ntb_port_number,
> +       .peer_port_count        = idt_ntb_peer_port_count,
> +       .peer_port_number       = idt_ntb_peer_port_number,
> +       .peer_port_idx          = idt_ntb_peer_port_idx,
> +       .link_is_up             = idt_ntb_link_is_up,
> +       .link_enable            = idt_ntb_link_enable,
> +       .link_disable           = idt_ntb_link_disable,
> +       .mw_count               = idt_ntb_mw_count,
> +       .mw_get_align           = idt_ntb_mw_get_align,
> +       .peer_mw_count          = idt_ntb_peer_mw_count,
> +       .peer_mw_get_addr       = idt_ntb_peer_mw_get_addr,
> +       .peer_mw_set_trans      = idt_ntb_peer_mw_set_trans,
> +       .peer_mw_clear_trans    = idt_ntb_peer_mw_clear_trans,
> +       .db_valid_mask          = idt_ntb_db_valid_mask,
> +       .db_read                = idt_ntb_db_read,
> +       .db_clear               = idt_ntb_db_clear,
> +       .db_read_mask           = idt_ntb_db_read_mask,
> +       .db_set_mask            = idt_ntb_db_set_mask,
> +       .db_clear_mask          = idt_ntb_db_clear_mask,
> +       .peer_db_set            = idt_ntb_peer_db_set,
> +       .msg_count              = idt_ntb_msg_count,
> +       .msg_inbits             = idt_ntb_msg_inbits,
> +       .msg_outbits            = idt_ntb_msg_outbits,
> +       .msg_read_sts           = idt_ntb_msg_read_sts,
> +       .msg_clear_sts          = idt_ntb_msg_clear_sts,
> +       .msg_set_mask           = idt_ntb_msg_set_mask,
> +       .msg_clear_mask         = idt_ntb_msg_clear_mask,
> +       .msg_read               = idt_ntb_msg_read,
> +       .msg_write              = idt_ntb_msg_write
> +};
> +
> +/*
> + * idt_register_device() - register IDT NTB device
> + * @ndev:      IDT NTB hardware driver descriptor
> + *
> + * Return: zero on success, otherwise a negative error number.
> + */
> +static int idt_register_device(struct idt_ntb_dev *ndev)
> +{
> +       int ret;
> +
> +       /* Initialize the rest of NTB device structure and register it */
> +       ndev->ntb.ops = &idt_ntb_ops;
> +       ndev->ntb.topo = NTB_TOPO_PRI;
> +
> +       ret = ntb_register_device(&ndev->ntb);
> +       if (ret != 0) {
> +               dev_err_pci(ndev, "Failed to register NTB device");
> +               return ret;
> +       }
> +
> +       dev_dbg_pci(ndev, "IDT NTB device successfully registered");
> +
> +       return 0;
> +}
> +
> +/*
> + * idt_unregister_device() - unregister IDT NTB device
> + * @ndev:      IDT NTB hardware driver descriptor
> + */
> +static void idt_unregister_device(struct idt_ntb_dev *ndev)
> +{
> +       /* Just unregister the NTB device */
> +       ntb_unregister_device(&ndev->ntb);
> +
> +       dev_dbg_pci(ndev, "IDT NTB device unregistered");
> +}
> +
> +/*=============================================================================
> + *                        10. DebugFS node initialization
> + *=============================================================================
> + */
> +
> +static ssize_t idt_dbgfs_info_read(struct file *filp, char __user *ubuf,
> +                                  size_t count, loff_t *offp);
> +
> +/*
> + * Driver DebugFS info file operations
> + */
> +static const struct file_operations idt_dbgfs_info_ops = {
> +       .owner = THIS_MODULE,
> +       .open = simple_open,
> +       .read = idt_dbgfs_info_read
> +};
> +
> +/*
> + * idt_dbgfs_info_read() - DebugFS read info node callback
> + * @file:      File node descriptor.
> + * @ubuf:      User-space buffer to put data to
> + * @count:     Size of the buffer
> + * @offp:      Offset within the buffer
> + */
> +static ssize_t idt_dbgfs_info_read(struct file *filp, char __user *ubuf,
> +                                  size_t count, loff_t *offp)
> +{
> +       struct idt_ntb_dev *ndev = filp->private_data;
> +       enum ntb_speed speed;
> +       enum ntb_width width;
> +       char *strbuf;
> +       size_t size;
> +       ssize_t ret = 0, off = 0;
> +       unsigned char temp, frac, idx;
> +       u32 data;
> +
> +       /* Lets limit the buffer size the way the Intel/AMD drivers do */
> +       size = min_t(size_t, count, 0x500U);
> +
> +       /* Allocate the memory for the buffer */
> +       strbuf = kmalloc(size, GFP_KERNEL);
> +       if (strbuf == NULL)
> +               return -ENOMEM;
> +
> +       /* Put the data into the string buffer */
> +       off += scnprintf(strbuf + off, size - off,
> +               "\n\t\tIDT NTB device Information:\n\n");
> +
> +       /* General device configurations */
> +       off += scnprintf(strbuf + off, size - off,
> +               "Switch port\t\t\t- %hhu\n", ndev->port);
> +       off += scnprintf(strbuf + off, size - off,
> +               "Port partition\t\t\t- %hhu\n", ndev->part);
> +       off += scnprintf(strbuf + off, size - off,
> +               "Peers Port:Partition\t\t- ");
> +       for (idx = 0; idx < ndev->peer_cnt; idx++) {
> +               off += scnprintf(strbuf + off, size - off, "p%hhu:%hhu ",
> +                       ndev->peers[idx].port, ndev->peers[idx].part);
> +       }
> +       off += scnprintf(strbuf + off, size - off, "\n");
> +
> +       /* Links status */
> +       data = idt_ntb_link_is_up(&ndev->ntb, &speed, &width);
> +       off += scnprintf(strbuf + off, size - off,
> +               "NTB link status\t\t\t- 0x%08x, ", data);
> +       off += scnprintf(strbuf + off, size - off, "PCIe Gen %d ",
> +               speed);
> +       off += scnprintf(strbuf + off, size - off, "x%d lanes\n",
> +               width);
> +
> +       /* Memory windows information */
> +       off += scnprintf(strbuf + off, size - off,
> +                "Outbound MWs count\t\t- %u\n", ndev->mw_cnt);
> +       off += scnprintf(strbuf + off, size - off,
> +                "Inbound MWs count\t\t- ");
> +       for (idx = 0; idx < ndev->peer_cnt; idx++) {
> +               off += scnprintf(strbuf + off, size - off, "p%hhu:%hhu ",
> +                       ndev->peers[idx].port, ndev->peers[idx].mw_cnt);
> +       }
> +       off += scnprintf(strbuf + off, size - off, "\n");
> +
> +       /* Doorbell information */
> +       data = idt_ntb_db_read(&ndev->ntb);
> +       off += scnprintf(strbuf + off, size - off,
> +                "Doorbell register state\t\t- 0x%08x\n", data);
> +       data = idt_nt_read(ndev, IDT_NT_INDBELLMSK);
> +       off += scnprintf(strbuf + off, size - off,
> +                "Doorbell mask state\t\t- 0x%08x\n", data);
> +
> +       /* Messaging information */
> +       data = idt_ntb_msg_read_sts(&ndev->ntb);
> +       off += scnprintf(strbuf + off, size - off,
> +                "Message registers status\t- 0x%08x\n", data);
> +       off += scnprintf(strbuf + off, size - off,
> +                "Message data\t\t\t- ");
> +       for (idx = 0; idx < IDT_MSG_CNT; idx++) {
> +               int src;
> +               (void)idt_ntb_msg_read(&ndev->ntb, idx, &src, &data);
> +               off += scnprintf(strbuf + off, size - off,
> +                       "m%hhu:0x%08x<-%hhu ",
> +                       idx, data, ndev->peers[src].port);
> +       }
> +       off += scnprintf(strbuf + off, size - off, "\n");
> +
> +       /* Current temperature */
> +       idt_read_temp(ndev, &temp, &frac);
> +       off += scnprintf(strbuf + off, size - off,
> +               "Switch temperature\t\t- %hhu.%hhuC\n", temp, frac);
> +
> +       /* Copy the buffer to the User Space */
> +       ret = simple_read_from_buffer(ubuf, count, offp, strbuf, off);
> +       kfree(strbuf);
> +
> +       return ret;
> +}
> +
> +/*
> + * idt_init_dbgfs() - initialize DebugFS node
> + * @ndev:      IDT NTB hardware driver descriptor
> + *
> + * Return: zero on success, otherwise a negative error number.
> + */
> +static int idt_init_dbgfs(struct idt_ntb_dev *ndev)
> +{
> +       char devname[64];
> +
> +       /* If the top directory is not created then do nothing */
> +       if (IS_ERR_OR_NULL(dbgfs_topdir)) {
> +               dev_info_pci(ndev, "Top DebugFS directory is absent");
> +               return PTR_ERR(dbgfs_topdir);
> +       }
> +
> +       /* Create the info file node */
> +       snprintf(devname, 64, "info:%s", pci_name(ndev->ntb.pdev));
> +       ndev->dbgfs_info = debugfs_create_file(devname, 0400, dbgfs_topdir,
> +               ndev, &idt_dbgfs_info_ops);
> +       if (IS_ERR(ndev->dbgfs_info)) {
> +               dev_dbg_pci(ndev, "Could not create DebugFS info node");
> +               return PTR_ERR(ndev->dbgfs_info);
> +       }
> +
> +       dev_dbg_pci(ndev, "IDT NTB device DebugFS node created");
> +
> +       return 0;
> +}
> +
> +/*
> + * idt_deinit_dbgfs() - deinitialize DebugFS node
> + * @ndev:      IDT NTB hardware driver descriptor
> + *
> + * Just discard the info node from DebugFS
> + */
> +static void idt_deinit_dbgfs(struct idt_ntb_dev *ndev)
> +{
> +       debugfs_remove(ndev->dbgfs_info);
> +
> +       dev_dbg_pci(ndev, "IDT NTB device DebugFS node discarded");
> +}
> +
> +/*=============================================================================
> + *                     11. Basic PCIe device initialization
> + *=============================================================================
> + */
> +
> +/*
> + * idt_check_setup() - Check whether the IDT PCIe-swtich is properly
> + *                    pre-initialized
> + * @pdev:      Pointer to the PCI device descriptor
> + *
> + * Return: zero on success, otherwise a negative error number.
> + */
> +static int idt_check_setup(struct pci_dev *pdev)
> +{
> +       u32 data;
> +       int ret;
> +
> +       /* Read the BARSETUP0 */
> +       ret = pci_read_config_dword(pdev, IDT_NT_BARSETUP0, &data);
> +       if (ret != 0) {
> +               dev_err(&pdev->dev,
> +                       "Failed to read BARSETUP0 config register");
> +               return ret;
> +       }
> +
> +       /* Check whether the BAR0 register is enabled to be of config space */
> +       if (!(data & IDT_BARSETUP_EN) || !(data & IDT_BARSETUP_MODE_CFG)) {
> +               dev_err(&pdev->dev, "BAR0 doesn't map config space");
> +               return -EINVAL;
> +       }
> +
> +       /* Configuration space BAR0 must have certain size */
> +       if ((data & IDT_BARSETUP_SIZE_MASK) != IDT_BARSETUP_SIZE_CFG) {
> +               dev_err(&pdev->dev, "Invalid size of config space");
> +               return -EINVAL;
> +       }
> +
> +       dev_dbg(&pdev->dev, "IDT NTB device pre-initialized correctly");
> +
> +       return 0;
> +}
> +
> +/*
> + * Create the IDT PCIe-switch driver descriptor
> + * @pdev:      Pointer to the PCI device descriptor
> + * @id:                IDT PCIe-device configuration
> + *
> + * It just allocates a memory for IDT PCIe-switch device structure and
> + * initializes some commonly used fields.
> + *
> + * No need of release method, since managed device resource is used for
> + * memory allocation.
> + *
> + * Return: pointer to the descriptor, otherwise a negative error number.
> + */
> +static struct idt_ntb_dev *idt_create_dev(struct pci_dev *pdev,
> +                                         const struct pci_device_id *id)
> +{
> +       struct idt_ntb_dev *ndev;
> +
> +       /* Allocate memory for the IDT PCIe-device descriptor */
> +       ndev = devm_kzalloc(&pdev->dev, sizeof(*ndev), GFP_KERNEL);
> +       if (IS_ERR_OR_NULL(ndev)) {
> +               dev_err(&pdev->dev, "Memory allocation failed for descriptor");
> +               return ERR_PTR(-ENOMEM);
> +       }
> +
> +       /* Save the IDT PCIe-switch ports configuration */
> +       ndev->swcfg = (struct idt_89hpes_cfg *)id->driver_data;
> +       /* Save the PCI-device pointer inside the NTB device structure */
> +       ndev->ntb.pdev = pdev;
> +
> +       /* Initialize spin locker of Doorbell, Message and GASA registers */
> +       spin_lock_init(&ndev->db_mask_lock);
> +       spin_lock_init(&ndev->msg_mask_lock);
> +       spin_lock_init(&ndev->gasa_lock);
> +
> +       dev_info_pci(ndev, "IDT %s was discovered", ndev->swcfg->name);
> +
> +       dev_dbg_pci(ndev, "IDT NTB device descriptor created");
> +
> +       return ndev;
> +}
> +
> +/*
> + * idt_init_pci() - initialize the basic PCI-related subsystem
> + * @ndev:      Pointer to the IDT PCIe-switch driver descriptor
> + *
> + * Managed device resources will be freed automatically in case of failure or
> + * driver detachment.
> + *
> + * Return: zero on success, otherwise negative error number.
> + */
> +static int idt_init_pci(struct idt_ntb_dev *ndev)
> +{
> +       struct pci_dev *pdev = ndev->ntb.pdev;
> +       int ret;
> +
> +       /*
> +        * Enable the device advanced error reporting. It's not critical to
> +        * have AER disabled in the kernel.
> +        */
> +       ret = pci_enable_pcie_error_reporting(pdev);
> +       if (ret != 0)
> +               dev_warn_pci(ndev, "PCIe AER capability is disabled\n");
> +       else /* Cleanup uncorrectable error status before getting to init */
> +               pci_cleanup_aer_uncorrect_error_status(pdev);
> +
> +       /* First enable the PCI device */
> +       ret = pcim_enable_device(pdev);
> +       if (ret != 0) {
> +               dev_err_pci(ndev, "Failed to enable IDT PCIe device\n");
> +               goto err_disable_aer;
> +       }
> +
> +       /*
> +        * Enable the bus mastering, which effectively enables MSI IRQs and
> +        * Request TLPs translation
> +        */
> +       pci_set_master(pdev);
> +
> +       /* Request all BARs resources */
> +       ret = pci_request_regions(pdev, NTB_NAME);
> +       if (ret != 0) {
> +               dev_err_pci(ndev, "Failed to request IDT resources\n");
> +               goto err_clear_master;
> +       }
> +
> +       /* Initialize the bit mask of DMA */
> +       ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(64));
> +       if (ret != 0) {
> +               ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
> +               if (ret != 0) {
> +                       dev_err_pci(ndev, "Failed to set any DMA bit mask\n");
> +                       goto err_release_regions;
> +               }
> +               dev_warn_pci(ndev, "Cannot set DMA highmem bit mask\n");
> +       }
> +       ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));
> +       if (ret != 0) {
> +               ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
> +               if (ret != 0) {
> +                       dev_err_pci(ndev,
> +                               "Failed to set any consistent DMA bit mask\n");
> +                       goto err_release_regions;
> +               }
> +               dev_warn_pci(ndev,
> +                       "Cannot set consistent DMA highmem bit mask\n");
> +       }
> +
> +       /* Retrieve virtual address of BAR0 - PCI configuration space */
> +       ndev->cfgspc = pcim_iomap(pdev, 0, 0);
> +       if (IS_ERR_OR_NULL(ndev->cfgspc)) {
> +               dev_err_pci(ndev,
> +                       "Failed to map configuration space\n");
> +               ret = -EIO;
> +               goto err_release_regions;
> +       }
> +
> +       /* Put the IDT driver data pointer to the PCI-device private pointer */
> +       pci_set_drvdata(pdev, ndev);
> +
> +       dev_dbg_pci(ndev, "IDT NT-function PCIe interface initialized");
> +
> +       return 0;
> +
> +err_release_regions:
> +       pci_release_regions(pdev);
> +err_clear_master:
> +       pci_clear_master(pdev);
> +err_disable_aer:
> +       (void)pci_disable_pcie_error_reporting(pdev);
> +
> +       return ret;
> +}
> +
> +/*
> + * idt_deinit_pci() - deinitialize the basic PCI-related subsystem
> + * @ndev:      Pointer to the IDT PCIe-switch driver descriptor
> + *
> + * Managed resources will be freed on the driver detachment
> + */
> +static void idt_deinit_pci(struct idt_ntb_dev *ndev)
> +{
> +       struct pci_dev *pdev = ndev->ntb.pdev;
> +
> +       /* Clean up the PCI-device private data pointer */
> +       pci_set_drvdata(pdev, NULL);
> +
> +       /* Disable the AER capability */
> +       (void)pci_disable_pcie_error_reporting(pdev);
> +
> +       /* Clear the bus master disabling the Request TLPs translation */
> +       pci_clear_master(pdev);
> +
> +       /* Release the PCI-device BAR0 resources */
> +       pci_release_regions(pdev);
> +
> +       dev_dbg_pci(ndev, "IDT NTB function PCI interface was cleaned");
> +}
> +
> +/*===========================================================================
> + *                       12. PCI bus callback functions
> + *===========================================================================
> + */
> +
> +/*
> + * idt_pci_probe() - PCI device probe callback
> + * @pdev:      Pointer to PCI device structure
> + * @id:                PCIe device custom descriptor
> + *
> + * Return: zero on success, otherwise negative error number
> + */
> +static int idt_pci_probe(struct pci_dev *pdev,
> +                        const struct pci_device_id *id)
> +{
> +       struct idt_ntb_dev *ndev;
> +       int ret;
> +
> +       /* Check whether IDT PCIe-switch is properly pre-initialized */
> +       ret = idt_check_setup(pdev);
> +       if (ret != 0)
> +               return ret;
> +
> +       /* Allocate the memory for IDT NTB device data */
> +       ndev = idt_create_dev(pdev, id);
> +       if (IS_ERR_OR_NULL(ndev))
> +               return PTR_ERR(ndev);
> +
> +       /* Initialize the basic PCI subsystem of the device */
> +       ret = idt_init_pci(ndev);
> +       if (ret != 0)
> +               return ret;
> +
> +       /* Scan ports of the IDT PCIe-switch */
> +       (void)idt_scan_ports(ndev);
> +
> +       /* Initialize NTB link events subsystem */
> +       idt_init_link(ndev);
> +
> +       /* Initialize MWs subsystem */
> +       ret = idt_init_mws(ndev);
> +       if (ret != 0)
> +               goto err_deinit_link;
> +
> +       /* Initialize Messaging subsystem */
> +       idt_init_msg(ndev);
> +
> +       /* Initialize IDT interrupts handler */
> +       ret = idt_init_isr(ndev);
> +       if (ret != 0)
> +               goto err_deinit_link;
> +
> +       /* Register IDT NTB devices on the NTB bus */
> +       ret = idt_register_device(ndev);
> +       if (ret != 0)
> +               goto err_deinit_isr;
> +
> +       /* Initialize DebugFS info node */
> +       (void)idt_init_dbgfs(ndev);
> +
> +       /* IDT PCIe-switch NTB driver is finally initialized */
> +       dev_info_pci(ndev, "IDT NTB device is ready");
> +
> +       /* May the force be with us... */
> +       return 0;
> +
> +err_deinit_isr:
> +       idt_deinit_isr(ndev);
> +err_deinit_link:
> +       idt_deinit_link(ndev);
> +       idt_deinit_pci(ndev);
> +
> +       return ret;
> +}
> +
> +/*
> + * idt_pci_probe() - PCI device remove callback
> + * @pdev:      Pointer to PCI device structure
> + */
> +static void idt_pci_remove(struct pci_dev *pdev)
> +{
> +       struct idt_ntb_dev *ndev = pci_get_drvdata(pdev);
> +
> +       /* Deinit the DebugFS node */
> +       idt_deinit_dbgfs(ndev);
> +
> +       /* Unregister NTB device */
> +       idt_unregister_device(ndev);
> +
> +       /* Stop the interrupts handling */
> +       idt_deinit_isr(ndev);
> +
> +       /* Deinitialize link event subsystem */
> +       idt_deinit_link(ndev);
> +
> +       /* Deinit basic PCI subsystem */
> +       idt_deinit_pci(ndev);
> +
> +       /* IDT PCIe-switch NTB driver is finally initialized */
> +       dev_info(&pdev->dev, "IDT NTB device is removed");
> +
> +       /* Sayonara... */
> +}
> +
> +/*
> + * IDT PCIe-switch models ports configuration structures
> + */
> +static struct idt_89hpes_cfg idt_89hpes24nt6ag2_config = {
> +       .name = "89HPES24NT6AG2",
> +       .port_cnt = 6, .ports = {0, 2, 4, 6, 8, 12}
> +};
> +static struct idt_89hpes_cfg idt_89hpes32nt8ag2_config = {
> +       .name = "89HPES32NT8AG2",
> +       .port_cnt = 8, .ports = {0, 2, 4, 6, 8, 12, 16, 20}
> +};
> +static struct idt_89hpes_cfg idt_89hpes32nt8bg2_config = {
> +       .name = "89HPES32NT8BG2",
> +       .port_cnt = 8, .ports = {0, 2, 4, 6, 8, 12, 16, 20}
> +};
> +static struct idt_89hpes_cfg idt_89hpes12nt12g2_config = {
> +       .name = "89HPES12NT12G2",
> +       .port_cnt = 3, .ports = {0, 8, 16}
> +};
> +static struct idt_89hpes_cfg idt_89hpes16nt16g2_config = {
> +       .name = "89HPES16NT16G2",
> +       .port_cnt = 4, .ports = {0, 8, 12, 16}
> +};
> +static struct idt_89hpes_cfg idt_89hpes24nt24g2_config = {
> +       .name = "89HPES24NT24G2",
> +       .port_cnt = 8, .ports = {0, 2, 4, 6, 8, 12, 16, 20}
> +};
> +static struct idt_89hpes_cfg idt_89hpes32nt24ag2_config = {
> +       .name = "89HPES32NT24AG2",
> +       .port_cnt = 8, .ports = {0, 2, 4, 6, 8, 12, 16, 20}
> +};
> +static struct idt_89hpes_cfg idt_89hpes32nt24bg2_config = {
> +       .name = "89HPES32NT24BG2",
> +       .port_cnt = 8, .ports = {0, 2, 4, 6, 8, 12, 16, 20}
> +};
> +
> +/*
> + * PCI-ids table of the supported IDT PCIe-switch devices
> + */
> +static const struct pci_device_id idt_pci_tbl[] = {
> +       {IDT_PCI_DEVICE_IDS(89HPES24NT6AG2,  idt_89hpes24nt6ag2_config)},
> +       {IDT_PCI_DEVICE_IDS(89HPES32NT8AG2,  idt_89hpes32nt8ag2_config)},
> +       {IDT_PCI_DEVICE_IDS(89HPES32NT8BG2,  idt_89hpes32nt8bg2_config)},
> +       {IDT_PCI_DEVICE_IDS(89HPES12NT12G2,  idt_89hpes12nt12g2_config)},
> +       {IDT_PCI_DEVICE_IDS(89HPES16NT16G2,  idt_89hpes16nt16g2_config)},
> +       {IDT_PCI_DEVICE_IDS(89HPES24NT24G2,  idt_89hpes24nt24g2_config)},
> +       {IDT_PCI_DEVICE_IDS(89HPES32NT24AG2, idt_89hpes32nt24ag2_config)},
> +       {IDT_PCI_DEVICE_IDS(89HPES32NT24BG2, idt_89hpes32nt24bg2_config)},
> +       {0}
> +};
> +MODULE_DEVICE_TABLE(pci, idt_pci_tbl);
> +
> +/*
> + * IDT PCIe-switch NT-function device driver structure definition
> + */
> +static struct pci_driver idt_pci_driver = {
> +       .name           = KBUILD_MODNAME,
> +       .probe          = idt_pci_probe,
> +       .remove         = idt_pci_remove,
> +       .id_table       = idt_pci_tbl,
> +};
> +
> +static int __init idt_pci_driver_init(void)
> +{
> +       pr_info("%s %s\n", NTB_DESC, NTB_VER);
> +
> +       /* Create the top DebugFS directory if the FS is initialized */
> +       if (debugfs_initialized())
> +               dbgfs_topdir = debugfs_create_dir(KBUILD_MODNAME, NULL);
> +
> +       /* Register the NTB hardware driver to handle the PCI device */
> +       return pci_register_driver(&idt_pci_driver);
> +}
> +module_init(idt_pci_driver_init);
> +
> +static void __exit idt_pci_driver_exit(void)
> +{
> +       /* Unregister the NTB hardware driver */
> +       pci_unregister_driver(&idt_pci_driver);
> +
> +       /* Discard the top DebugFS directory */
> +       debugfs_remove_recursive(dbgfs_topdir);
> +}
> +module_exit(idt_pci_driver_exit);
> +
> diff --git a/drivers/ntb/hw/idt/ntb_hw_idt.h b/drivers/ntb/hw/idt/ntb_hw_idt.h
> new file mode 100644
> index 0000000..2add71d
> --- /dev/null
> +++ b/drivers/ntb/hw/idt/ntb_hw_idt.h
> @@ -0,0 +1,1162 @@
> +/*
> + *   This file is provided under a GPLv2 license.  When using or
> + *   redistributing this file, you may do so under that license.
> + *
> + *   GPL LICENSE SUMMARY
> + *
> + *   Copyright (C) 2016 T-Platforms All Rights Reserved.
> + *
> + *   This program is free software; you can redistribute it and/or modify it
> + *   under the terms and conditions of the GNU General Public License,
> + *   version 2, as published by the Free Software Foundation.
> + *
> + *   This program is distributed in the hope that it will be useful, but WITHOUT
> + *   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + *   FITNESS FOR A PARTICULAR PURPOSE. See 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, one can be found <http://www.gnu.org/licenses/>.
> + *
> + *   The full GNU General Public License is included in this distribution in
> + *   the file called "COPYING".
> + *
> + *   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.
> + *
> + * IDT PCIe-switch NTB Linux driver
> + *
> + * Contact Information:
> + * Serge Semin <fancer.lancer@...il.com>, <Sergey.Semin@...latforms.ru>
> + */
> +
> +#ifndef NTB_HW_IDT_H
> +#define NTB_HW_IDT_H
> +
> +#include <linux/types.h>
> +#include <linux/pci.h>
> +#include <linux/pci_ids.h>
> +#include <linux/interrupt.h>
> +#include <linux/spinlock.h>
> +#include <linux/ntb.h>
> +
> +
> +/*
> + * Macro is used to create the struct pci_device_id that matches
> + * the supported IDT PCIe-switches
> + * @devname: Capitalized name of the particular device
> + * @data: Variable passed to the driver of the particular device
> + */
> +#define IDT_PCI_DEVICE_IDS(devname, data) \
> +       .vendor = PCI_VENDOR_ID_IDT, .device = PCI_DEVICE_ID_IDT_##devname, \
> +       .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, \
> +       .class = (PCI_CLASS_BRIDGE_OTHER << 8), .class_mask = (0xFFFF00), \
> +       .driver_data = (kernel_ulong_t)&data
> +
> +/*
> + * IDT PCIe-switches device IDs
> + */
> +#define PCI_DEVICE_ID_IDT_89HPES24NT6AG2  0x8091
> +#define PCI_DEVICE_ID_IDT_89HPES32NT8AG2  0x808F
> +#define PCI_DEVICE_ID_IDT_89HPES32NT8BG2  0x8088
> +#define PCI_DEVICE_ID_IDT_89HPES12NT12G2  0x8092
> +#define PCI_DEVICE_ID_IDT_89HPES16NT16G2  0x8090
> +#define PCI_DEVICE_ID_IDT_89HPES24NT24G2  0x808E
> +#define PCI_DEVICE_ID_IDT_89HPES32NT24AG2 0x808C
> +#define PCI_DEVICE_ID_IDT_89HPES32NT24BG2 0x808A
> +
> +/*
> + * Wrapper dev_err/dev_warn/dev_info/dev_dbg macros so
> + * shorten the PCI-device related logger print calls
> + */
> +#define dev_err_pci(ndev, args...) \
> +       dev_err(&(ndev)->ntb.pdev->dev, ## args)
> +#define dev_warn_pci(ndev, args...) \
> +       dev_warn(&(ndev)->ntb.pdev->dev, ## args)
> +#define dev_info_pci(ndev, args...) \
> +       dev_info(&(ndev)->ntb.pdev->dev, ## args)
> +#define dev_dbg_pci(ndev, args...) \
> +       dev_dbg(&(ndev)->ntb.pdev->dev, ## args)
> +
> +/*
> + * NT-function Configuration Space registers
> + * NOTE 1) The IDT PCIe-switch internal data is little-endian
> + *      so it must be taken into account in the driver
> + *      internals.
> + *      2) Additionally the registers should be accessed either
> + *      with byte-enables corresponding to their native size or
> + *      the size of one DWORD
> + *
> + * So to simplify the driver code, there is only DWORD-sized read/write
> + * operations utilized.
> + */
> +/* PCI Express Configuration Space */
> +/* PCI Express command/status register (DWORD) */
> +#define IDT_NT_PCICMDSTS               0x00004U
> +/* PCI Express Device Capabilities     (DWORD) */
> +#define IDT_NT_PCIEDCAP                        0x00044U
> +/* PCI Express Device Control/Status   (WORD+WORD) */
> +#define IDT_NT_PCIEDCTLSTS             0x00048U
> +/* PCI Express Link Capabilities       (DWORD) */
> +#define IDT_NT_PCIELCAP                        0x0004CU
> +/* PCI Express Link Control/Status     (WORD+WORD) */
> +#define IDT_NT_PCIELCTLSTS             0x00050U
> +/* PCI Express Device Capabilities 2   (DWORD) */
> +#define IDT_NT_PCIEDCAP2               0x00064U
> +/* PCI Express Device Control 2                (WORD+WORD) */
> +#define IDT_NT_PCIEDCTL2               0x00068U
> +/* PCI Power Management Control and Status (DWORD) */
> +#define IDT_NT_PMCSR                   0x000C4U
> +/*==========================================*/
> +/* IDT Proprietary NT-port-specific registers */
> +/* NT-function main control registers */
> +/* NT Endpoint Control                 (DWORD) */
> +#define IDT_NT_NTCTL                   0x00400U
> +/* NT Endpoint Interrupt Status/Mask   (DWORD) */
> +#define IDT_NT_NTINTSTS                        0x00404U
> +#define IDT_NT_NTINTMSK                        0x00408U
> +/* NT Endpoint Signal Data             (DWORD) */
> +#define IDT_NT_NTSDATA                 0x0040CU
> +/* NT Endpoint Global Signal           (DWORD) */
> +#define IDT_NT_NTGSIGNAL               0x00410U
> +/* Internal Error Reporting Mask 0/1   (DWORD) */
> +#define IDT_NT_NTIERRORMSK0            0x00414U
> +#define IDT_NT_NTIERRORMSK1            0x00418U
> +/* Doorbel registers */
> +/* NT Outbound Doorbell Set            (DWORD) */
> +#define IDT_NT_OUTDBELLSET             0x00420U
> +/* NT Inbound Doorbell Status/Mask     (DWORD) */
> +#define IDT_NT_INDBELLSTS              0x00428U
> +#define IDT_NT_INDBELLMSK              0x0042CU
> +/* Message registers */
> +/* Outbound Message N                  (DWORD) */
> +#define IDT_NT_OUTMSG0                 0x00430U
> +#define IDT_NT_OUTMSG1                 0x00434U
> +#define IDT_NT_OUTMSG2                 0x00438U
> +#define IDT_NT_OUTMSG3                 0x0043CU
> +/* Inbound Message N                   (DWORD) */
> +#define IDT_NT_INMSG0                  0x00440U
> +#define IDT_NT_INMSG1                  0x00444U
> +#define IDT_NT_INMSG2                  0x00448U
> +#define IDT_NT_INMSG3                  0x0044CU
> +/* Inbound Message Source N            (DWORD) */
> +#define IDT_NT_INMSGSRC0               0x00450U
> +#define IDT_NT_INMSGSRC1               0x00454U
> +#define IDT_NT_INMSGSRC2               0x00458U
> +#define IDT_NT_INMSGSRC3               0x0045CU
> +/* Message Status                      (DWORD) */
> +#define IDT_NT_MSGSTS                  0x00460U
> +/* Message Status Mask                 (DWORD) */
> +#define IDT_NT_MSGSTSMSK               0x00464U
> +/* BAR-setup registers */
> +/* BAR N Setup/Limit Address/Lower and Upper Translated Base Address (DWORD) */
> +#define IDT_NT_BARSETUP0               0x00470U
> +#define IDT_NT_BARLIMIT0               0x00474U
> +#define IDT_NT_BARLTBASE0              0x00478U
> +#define IDT_NT_BARUTBASE0              0x0047CU
> +#define IDT_NT_BARSETUP1               0x00480U
> +#define IDT_NT_BARLIMIT1               0x00484U
> +#define IDT_NT_BARLTBASE1              0x00488U
> +#define IDT_NT_BARUTBASE1              0x0048CU
> +#define IDT_NT_BARSETUP2               0x00490U
> +#define IDT_NT_BARLIMIT2               0x00494U
> +#define IDT_NT_BARLTBASE2              0x00498U
> +#define IDT_NT_BARUTBASE2              0x0049CU
> +#define IDT_NT_BARSETUP3               0x004A0U
> +#define IDT_NT_BARLIMIT3               0x004A4U
> +#define IDT_NT_BARLTBASE3              0x004A8U
> +#define IDT_NT_BARUTBASE3              0x004ACU
> +#define IDT_NT_BARSETUP4               0x004B0U
> +#define IDT_NT_BARLIMIT4               0x004B4U
> +#define IDT_NT_BARLTBASE4              0x004B8U
> +#define IDT_NT_BARUTBASE4              0x004BCU
> +#define IDT_NT_BARSETUP5               0x004C0U
> +#define IDT_NT_BARLIMIT5               0x004C4U
> +#define IDT_NT_BARLTBASE5              0x004C8U
> +#define IDT_NT_BARUTBASE5              0x004CCU
> +/* NT mapping table registers */
> +/* NT Mapping Table Address/Status/Data        (DWORD) */
> +#define IDT_NT_NTMTBLADDR              0x004D0U
> +#define IDT_NT_NTMTBLSTS               0x004D4U
> +#define IDT_NT_NTMTBLDATA              0x004D8U
> +/* Requester ID (Bus:Device:Function) Capture  (DWORD) */
> +#define IDT_NT_REQIDCAP                        0x004DCU
> +/* Memory Windows Lookup table registers */
> +/* Lookup Table Offset/Lower, Middle and Upper data    (DWORD) */
> +#define IDT_NT_LUTOFFSET               0x004E0U
> +#define IDT_NT_LUTLDATA                        0x004E4U
> +#define IDT_NT_LUTMDATA                        0x004E8U
> +#define IDT_NT_LUTUDATA                        0x004ECU
> +/* NT Endpoint Uncorrectable/Correctable Errors Emulation registers (DWORD) */
> +#define IDT_NT_NTUEEM                  0x004F0U
> +#define IDT_NT_NTCEEM                  0x004F4U
> +/* Global Address Space Access/Data registers  (DWARD) */
> +#define IDT_NT_GASAADDR                        0x00FF8U
> +#define IDT_NT_GASADATA                        0x00FFCU
> +
> +/*
> + * IDT PCIe-switch Global Configuration and Status registers
> + */
> +/* Port N Configuration register in global space */
> +/* PCI Express command/status and link control/status registers (WORD+WORD) */
> +#define IDT_SW_NTP0_PCIECMDSTS         0x01004U
> +#define IDT_SW_NTP0_PCIELCTLSTS                0x01050U
> +/* NT-function control register                (DWORD) */
> +#define IDT_SW_NTP0_NTCTL              0x01400U
> +/* BAR setup/limit/base address registers (DWORD) */
> +#define IDT_SW_NTP0_BARSETUP0          0x01470U
> +#define IDT_SW_NTP0_BARLIMIT0          0x01474U
> +#define IDT_SW_NTP0_BARLTBASE0         0x01478U
> +#define IDT_SW_NTP0_BARUTBASE0         0x0147CU
> +#define IDT_SW_NTP0_BARSETUP1          0x01480U
> +#define IDT_SW_NTP0_BARLIMIT1          0x01484U
> +#define IDT_SW_NTP0_BARLTBASE1         0x01488U
> +#define IDT_SW_NTP0_BARUTBASE1         0x0148CU
> +#define IDT_SW_NTP0_BARSETUP2          0x01490U
> +#define IDT_SW_NTP0_BARLIMIT2          0x01494U
> +#define IDT_SW_NTP0_BARLTBASE2         0x01498U
> +#define IDT_SW_NTP0_BARUTBASE2         0x0149CU
> +#define IDT_SW_NTP0_BARSETUP3          0x014A0U
> +#define IDT_SW_NTP0_BARLIMIT3          0x014A4U
> +#define IDT_SW_NTP0_BARLTBASE3         0x014A8U
> +#define IDT_SW_NTP0_BARUTBASE3         0x014ACU
> +#define IDT_SW_NTP0_BARSETUP4          0x014B0U
> +#define IDT_SW_NTP0_BARLIMIT4          0x014B4U
> +#define IDT_SW_NTP0_BARLTBASE4         0x014B8U
> +#define IDT_SW_NTP0_BARUTBASE4         0x014BCU
> +#define IDT_SW_NTP0_BARSETUP5          0x014C0U
> +#define IDT_SW_NTP0_BARLIMIT5          0x014C4U
> +#define IDT_SW_NTP0_BARLTBASE5         0x014C8U
> +#define IDT_SW_NTP0_BARUTBASE5         0x014CCU
> +/* PCI Express command/status and link control/status registers (WORD+WORD) */
> +#define IDT_SW_NTP2_PCIECMDSTS         0x05004U
> +#define IDT_SW_NTP2_PCIELCTLSTS                0x05050U
> +/* NT-function control register                (DWORD) */
> +#define IDT_SW_NTP2_NTCTL              0x05400U
> +/* BAR setup/limit/base address registers (DWORD) */
> +#define IDT_SW_NTP2_BARSETUP0          0x05470U
> +#define IDT_SW_NTP2_BARLIMIT0          0x05474U
> +#define IDT_SW_NTP2_BARLTBASE0         0x05478U
> +#define IDT_SW_NTP2_BARUTBASE0         0x0547CU
> +#define IDT_SW_NTP2_BARSETUP1          0x05480U
> +#define IDT_SW_NTP2_BARLIMIT1          0x05484U
> +#define IDT_SW_NTP2_BARLTBASE1         0x05488U
> +#define IDT_SW_NTP2_BARUTBASE1         0x0548CU
> +#define IDT_SW_NTP2_BARSETUP2          0x05490U
> +#define IDT_SW_NTP2_BARLIMIT2          0x05494U
> +#define IDT_SW_NTP2_BARLTBASE2         0x05498U
> +#define IDT_SW_NTP2_BARUTBASE2         0x0549CU
> +#define IDT_SW_NTP2_BARSETUP3          0x054A0U
> +#define IDT_SW_NTP2_BARLIMIT3          0x054A4U
> +#define IDT_SW_NTP2_BARLTBASE3         0x054A8U
> +#define IDT_SW_NTP2_BARUTBASE3         0x054ACU
> +#define IDT_SW_NTP2_BARSETUP4          0x054B0U
> +#define IDT_SW_NTP2_BARLIMIT4          0x054B4U
> +#define IDT_SW_NTP2_BARLTBASE4         0x054B8U
> +#define IDT_SW_NTP2_BARUTBASE4         0x054BCU
> +#define IDT_SW_NTP2_BARSETUP5          0x054C0U
> +#define IDT_SW_NTP2_BARLIMIT5          0x054C4U
> +#define IDT_SW_NTP2_BARLTBASE5         0x054C8U
> +#define IDT_SW_NTP2_BARUTBASE5         0x054CCU
> +/* PCI Express command/status and link control/status registers (WORD+WORD) */
> +#define IDT_SW_NTP4_PCIECMDSTS         0x09004U
> +#define IDT_SW_NTP4_PCIELCTLSTS                0x09050U
> +/* NT-function control register                (DWORD) */
> +#define IDT_SW_NTP4_NTCTL              0x09400U
> +/* BAR setup/limit/base address registers (DWORD) */
> +#define IDT_SW_NTP4_BARSETUP0          0x09470U
> +#define IDT_SW_NTP4_BARLIMIT0          0x09474U
> +#define IDT_SW_NTP4_BARLTBASE0         0x09478U
> +#define IDT_SW_NTP4_BARUTBASE0         0x0947CU
> +#define IDT_SW_NTP4_BARSETUP1          0x09480U
> +#define IDT_SW_NTP4_BARLIMIT1          0x09484U
> +#define IDT_SW_NTP4_BARLTBASE1         0x09488U
> +#define IDT_SW_NTP4_BARUTBASE1         0x0948CU
> +#define IDT_SW_NTP4_BARSETUP2          0x09490U
> +#define IDT_SW_NTP4_BARLIMIT2          0x09494U
> +#define IDT_SW_NTP4_BARLTBASE2         0x09498U
> +#define IDT_SW_NTP4_BARUTBASE2         0x0949CU
> +#define IDT_SW_NTP4_BARSETUP3          0x094A0U
> +#define IDT_SW_NTP4_BARLIMIT3          0x094A4U
> +#define IDT_SW_NTP4_BARLTBASE3         0x094A8U
> +#define IDT_SW_NTP4_BARUTBASE3         0x094ACU
> +#define IDT_SW_NTP4_BARSETUP4          0x094B0U
> +#define IDT_SW_NTP4_BARLIMIT4          0x094B4U
> +#define IDT_SW_NTP4_BARLTBASE4         0x094B8U
> +#define IDT_SW_NTP4_BARUTBASE4         0x094BCU
> +#define IDT_SW_NTP4_BARSETUP5          0x094C0U
> +#define IDT_SW_NTP4_BARLIMIT5          0x094C4U
> +#define IDT_SW_NTP4_BARLTBASE5         0x094C8U
> +#define IDT_SW_NTP4_BARUTBASE5         0x094CCU
> +/* PCI Express command/status and link control/status registers (WORD+WORD) */
> +#define IDT_SW_NTP6_PCIECMDSTS         0x0D004U
> +#define IDT_SW_NTP6_PCIELCTLSTS                0x0D050U
> +/* NT-function control register                (DWORD) */
> +#define IDT_SW_NTP6_NTCTL              0x0D400U
> +/* BAR setup/limit/base address registers (DWORD) */
> +#define IDT_SW_NTP6_BARSETUP0          0x0D470U
> +#define IDT_SW_NTP6_BARLIMIT0          0x0D474U
> +#define IDT_SW_NTP6_BARLTBASE0         0x0D478U
> +#define IDT_SW_NTP6_BARUTBASE0         0x0D47CU
> +#define IDT_SW_NTP6_BARSETUP1          0x0D480U
> +#define IDT_SW_NTP6_BARLIMIT1          0x0D484U
> +#define IDT_SW_NTP6_BARLTBASE1         0x0D488U
> +#define IDT_SW_NTP6_BARUTBASE1         0x0D48CU
> +#define IDT_SW_NTP6_BARSETUP2          0x0D490U
> +#define IDT_SW_NTP6_BARLIMIT2          0x0D494U
> +#define IDT_SW_NTP6_BARLTBASE2         0x0D498U
> +#define IDT_SW_NTP6_BARUTBASE2         0x0D49CU
> +#define IDT_SW_NTP6_BARSETUP3          0x0D4A0U
> +#define IDT_SW_NTP6_BARLIMIT3          0x0D4A4U
> +#define IDT_SW_NTP6_BARLTBASE3         0x0D4A8U
> +#define IDT_SW_NTP6_BARUTBASE3         0x0D4ACU
> +#define IDT_SW_NTP6_BARSETUP4          0x0D4B0U
> +#define IDT_SW_NTP6_BARLIMIT4          0x0D4B4U
> +#define IDT_SW_NTP6_BARLTBASE4         0x0D4B8U
> +#define IDT_SW_NTP6_BARUTBASE4         0x0D4BCU
> +#define IDT_SW_NTP6_BARSETUP5          0x0D4C0U
> +#define IDT_SW_NTP6_BARLIMIT5          0x0D4C4U
> +#define IDT_SW_NTP6_BARLTBASE5         0x0D4C8U
> +#define IDT_SW_NTP6_BARUTBASE5         0x0D4CCU
> +/* PCI Express command/status and link control/status registers (WORD+WORD) */
> +#define IDT_SW_NTP8_PCIECMDSTS         0x11004U
> +#define IDT_SW_NTP8_PCIELCTLSTS                0x11050U
> +/* NT-function control register                (DWORD) */
> +#define IDT_SW_NTP8_NTCTL              0x11400U
> +/* BAR setup/limit/base address registers (DWORD) */
> +#define IDT_SW_NTP8_BARSETUP0          0x11470U
> +#define IDT_SW_NTP8_BARLIMIT0          0x11474U
> +#define IDT_SW_NTP8_BARLTBASE0         0x11478U
> +#define IDT_SW_NTP8_BARUTBASE0         0x1147CU
> +#define IDT_SW_NTP8_BARSETUP1          0x11480U
> +#define IDT_SW_NTP8_BARLIMIT1          0x11484U
> +#define IDT_SW_NTP8_BARLTBASE1         0x11488U
> +#define IDT_SW_NTP8_BARUTBASE1         0x1148CU
> +#define IDT_SW_NTP8_BARSETUP2          0x11490U
> +#define IDT_SW_NTP8_BARLIMIT2          0x11494U
> +#define IDT_SW_NTP8_BARLTBASE2         0x11498U
> +#define IDT_SW_NTP8_BARUTBASE2         0x1149CU
> +#define IDT_SW_NTP8_BARSETUP3          0x114A0U
> +#define IDT_SW_NTP8_BARLIMIT3          0x114A4U
> +#define IDT_SW_NTP8_BARLTBASE3         0x114A8U
> +#define IDT_SW_NTP8_BARUTBASE3         0x114ACU
> +#define IDT_SW_NTP8_BARSETUP4          0x114B0U
> +#define IDT_SW_NTP8_BARLIMIT4          0x114B4U
> +#define IDT_SW_NTP8_BARLTBASE4         0x114B8U
> +#define IDT_SW_NTP8_BARUTBASE4         0x114BCU
> +#define IDT_SW_NTP8_BARSETUP5          0x114C0U
> +#define IDT_SW_NTP8_BARLIMIT5          0x114C4U
> +#define IDT_SW_NTP8_BARLTBASE5         0x114C8U
> +#define IDT_SW_NTP8_BARUTBASE5         0x114CCU
> +/* PCI Express command/status and link control/status registers (WORD+WORD) */
> +#define IDT_SW_NTP12_PCIECMDSTS                0x19004U
> +#define IDT_SW_NTP12_PCIELCTLSTS       0x19050U
> +/* NT-function control register                (DWORD) */
> +#define IDT_SW_NTP12_NTCTL             0x19400U
> +/* BAR setup/limit/base address registers (DWORD) */
> +#define IDT_SW_NTP12_BARSETUP0         0x19470U
> +#define IDT_SW_NTP12_BARLIMIT0         0x19474U
> +#define IDT_SW_NTP12_BARLTBASE0                0x19478U
> +#define IDT_SW_NTP12_BARUTBASE0                0x1947CU
> +#define IDT_SW_NTP12_BARSETUP1         0x19480U
> +#define IDT_SW_NTP12_BARLIMIT1         0x19484U
> +#define IDT_SW_NTP12_BARLTBASE1                0x19488U
> +#define IDT_SW_NTP12_BARUTBASE1                0x1948CU
> +#define IDT_SW_NTP12_BARSETUP2         0x19490U
> +#define IDT_SW_NTP12_BARLIMIT2         0x19494U
> +#define IDT_SW_NTP12_BARLTBASE2                0x19498U
> +#define IDT_SW_NTP12_BARUTBASE2                0x1949CU
> +#define IDT_SW_NTP12_BARSETUP3         0x194A0U
> +#define IDT_SW_NTP12_BARLIMIT3         0x194A4U
> +#define IDT_SW_NTP12_BARLTBASE3                0x194A8U
> +#define IDT_SW_NTP12_BARUTBASE3                0x194ACU
> +#define IDT_SW_NTP12_BARSETUP4         0x194B0U
> +#define IDT_SW_NTP12_BARLIMIT4         0x194B4U
> +#define IDT_SW_NTP12_BARLTBASE4                0x194B8U
> +#define IDT_SW_NTP12_BARUTBASE4                0x194BCU
> +#define IDT_SW_NTP12_BARSETUP5         0x194C0U
> +#define IDT_SW_NTP12_BARLIMIT5         0x194C4U
> +#define IDT_SW_NTP12_BARLTBASE5                0x194C8U
> +#define IDT_SW_NTP12_BARUTBASE5                0x194CCU
> +/* PCI Express command/status and link control/status registers (WORD+WORD) */
> +#define IDT_SW_NTP16_PCIECMDSTS                0x21004U
> +#define IDT_SW_NTP16_PCIELCTLSTS       0x21050U
> +/* NT-function control register                (DWORD) */
> +#define IDT_SW_NTP16_NTCTL             0x21400U
> +/* BAR setup/limit/base address registers (DWORD) */
> +#define IDT_SW_NTP16_BARSETUP0         0x21470U
> +#define IDT_SW_NTP16_BARLIMIT0         0x21474U
> +#define IDT_SW_NTP16_BARLTBASE0                0x21478U
> +#define IDT_SW_NTP16_BARUTBASE0                0x2147CU
> +#define IDT_SW_NTP16_BARSETUP1         0x21480U
> +#define IDT_SW_NTP16_BARLIMIT1         0x21484U
> +#define IDT_SW_NTP16_BARLTBASE1                0x21488U
> +#define IDT_SW_NTP16_BARUTBASE1                0x2148CU
> +#define IDT_SW_NTP16_BARSETUP2         0x21490U
> +#define IDT_SW_NTP16_BARLIMIT2         0x21494U
> +#define IDT_SW_NTP16_BARLTBASE2                0x21498U
> +#define IDT_SW_NTP16_BARUTBASE2                0x2149CU
> +#define IDT_SW_NTP16_BARSETUP3         0x214A0U
> +#define IDT_SW_NTP16_BARLIMIT3         0x214A4U
> +#define IDT_SW_NTP16_BARLTBASE3                0x214A8U
> +#define IDT_SW_NTP16_BARUTBASE3                0x214ACU
> +#define IDT_SW_NTP16_BARSETUP4         0x214B0U
> +#define IDT_SW_NTP16_BARLIMIT4         0x214B4U
> +#define IDT_SW_NTP16_BARLTBASE4                0x214B8U
> +#define IDT_SW_NTP16_BARUTBASE4                0x214BCU
> +#define IDT_SW_NTP16_BARSETUP5         0x214C0U
> +#define IDT_SW_NTP16_BARLIMIT5         0x214C4U
> +#define IDT_SW_NTP16_BARLTBASE5                0x214C8U
> +#define IDT_SW_NTP16_BARUTBASE5                0x214CCU
> +/* PCI Express command/status and link control/status registers (WORD+WORD) */
> +#define IDT_SW_NTP20_PCIECMDSTS                0x29004U
> +#define IDT_SW_NTP20_PCIELCTLSTS       0x29050U
> +/* NT-function control register                (DWORD) */
> +#define IDT_SW_NTP20_NTCTL             0x29400U
> +/* BAR setup/limit/base address registers (DWORD) */
> +#define IDT_SW_NTP20_BARSETUP0         0x29470U
> +#define IDT_SW_NTP20_BARLIMIT0         0x29474U
> +#define IDT_SW_NTP20_BARLTBASE0                0x29478U
> +#define IDT_SW_NTP20_BARUTBASE0                0x2947CU
> +#define IDT_SW_NTP20_BARSETUP1         0x29480U
> +#define IDT_SW_NTP20_BARLIMIT1         0x29484U
> +#define IDT_SW_NTP20_BARLTBASE1                0x29488U
> +#define IDT_SW_NTP20_BARUTBASE1                0x2948CU
> +#define IDT_SW_NTP20_BARSETUP2         0x29490U
> +#define IDT_SW_NTP20_BARLIMIT2         0x29494U
> +#define IDT_SW_NTP20_BARLTBASE2                0x29498U
> +#define IDT_SW_NTP20_BARUTBASE2                0x2949CU
> +#define IDT_SW_NTP20_BARSETUP3         0x294A0U
> +#define IDT_SW_NTP20_BARLIMIT3         0x294A4U
> +#define IDT_SW_NTP20_BARLTBASE3                0x294A8U
> +#define IDT_SW_NTP20_BARUTBASE3                0x294ACU
> +#define IDT_SW_NTP20_BARSETUP4         0x294B0U
> +#define IDT_SW_NTP20_BARLIMIT4         0x294B4U
> +#define IDT_SW_NTP20_BARLTBASE4                0x294B8U
> +#define IDT_SW_NTP20_BARUTBASE4                0x294BCU
> +#define IDT_SW_NTP20_BARSETUP5         0x294C0U
> +#define IDT_SW_NTP20_BARLIMIT5         0x294C4U
> +#define IDT_SW_NTP20_BARLTBASE5                0x294C8U
> +#define IDT_SW_NTP20_BARUTBASE5                0x294CCU
> +/* IDT PCIe-switch control register    (DWORD) */
> +#define IDT_SW_CTL                     0x3E000U
> +/* Boot Configuration Vector Status    (DWORD) */
> +#define IDT_SW_BCVSTS                  0x3E004U
> +/* Port Clocking Mode                  (DWORD) */
> +#define IDT_SW_PCLKMODE                        0x3E008U
> +/* Reset Drain Delay                   (DWORD) */
> +#define IDT_SW_RDRAINDELAY             0x3E080U
> +/* Port Operating Mode Change Drain Delay (DWORD) */
> +#define IDT_SW_POMCDELAY               0x3E084U
> +/* Side Effect Delay                   (DWORD) */
> +#define IDT_SW_SEDELAY                 0x3E088U
> +/* Upstream Secondary Bus Reset Delay  (DWORD) */
> +#define IDT_SW_SSBRDELAY               0x3E08CU
> +/* Switch partition N Control/Status/Failover registers */
> +#define IDT_SW_SWPART0CTL              0x3E100U
> +#define IDT_SW_SWPART0STS              0x3E104U
> +#define IDT_SW_SWPART0FCTL             0x3E108U
> +#define IDT_SW_SWPART1CTL              0x3E120U
> +#define IDT_SW_SWPART1STS              0x3E124U
> +#define IDT_SW_SWPART1FCTL             0x3E128U
> +#define IDT_SW_SWPART2CTL              0x3E140U
> +#define IDT_SW_SWPART2STS              0x3E144U
> +#define IDT_SW_SWPART2FCTL             0x3E148U
> +#define IDT_SW_SWPART3CTL              0x3E160U
> +#define IDT_SW_SWPART3STS              0x3E164U
> +#define IDT_SW_SWPART3FCTL             0x3E168U
> +#define IDT_SW_SWPART4CTL              0x3E180U
> +#define IDT_SW_SWPART4STS              0x3E184U
> +#define IDT_SW_SWPART4FCTL             0x3E188U
> +#define IDT_SW_SWPART5CTL              0x3E1A0U
> +#define IDT_SW_SWPART5STS              0x3E1A4U
> +#define IDT_SW_SWPART5FCTL             0x3E1A8U
> +#define IDT_SW_SWPART6CTL              0x3E1C0U
> +#define IDT_SW_SWPART6STS              0x3E1C4U
> +#define IDT_SW_SWPART6FCTL             0x3E1C8U
> +#define IDT_SW_SWPART7CTL              0x3E1E0U
> +#define IDT_SW_SWPART7STS              0x3E1E4U
> +#define IDT_SW_SWPART7FCTL             0x3E1E8U
> +/* Switch port N control and status registers */
> +#define IDT_SW_SWPORT0CTL              0x3E200U
> +#define IDT_SW_SWPORT0STS              0x3E204U
> +#define IDT_SW_SWPORT0FCTL             0x3E208U
> +#define IDT_SW_SWPORT2CTL              0x3E240U
> +#define IDT_SW_SWPORT2STS              0x3E244U
> +#define IDT_SW_SWPORT2FCTL             0x3E248U
> +#define IDT_SW_SWPORT4CTL              0x3E280U
> +#define IDT_SW_SWPORT4STS              0x3E284U
> +#define IDT_SW_SWPORT4FCTL             0x3E288U
> +#define IDT_SW_SWPORT6CTL              0x3E2C0U
> +#define IDT_SW_SWPORT6STS              0x3E2C4U
> +#define IDT_SW_SWPORT6FCTL             0x3E2C8U
> +#define IDT_SW_SWPORT8CTL              0x3E300U
> +#define IDT_SW_SWPORT8STS              0x3E304U
> +#define IDT_SW_SWPORT8FCTL             0x3E308U
> +#define IDT_SW_SWPORT12CTL             0x3E380U
> +#define IDT_SW_SWPORT12STS             0x3E384U
> +#define IDT_SW_SWPORT12FCTL            0x3E388U
> +#define IDT_SW_SWPORT16CTL             0x3E400U
> +#define IDT_SW_SWPORT16STS             0x3E404U
> +#define IDT_SW_SWPORT16FCTL            0x3E408U
> +#define IDT_SW_SWPORT20CTL             0x3E480U
> +#define IDT_SW_SWPORT20STS             0x3E484U
> +#define IDT_SW_SWPORT20FCTL            0x3E488U
> +/* Switch Event registers */
> +/* Switch Event Status/Mask/Partition mask (DWORD) */
> +#define IDT_SW_SESTS                   0x3EC00U
> +#define IDT_SW_SEMSK                   0x3EC04U
> +#define IDT_SW_SEPMSK                  0x3EC08U
> +/* Switch Event Link Up/Down Status/Mask (DWORD) */
> +#define IDT_SW_SELINKUPSTS             0x3EC0CU
> +#define IDT_SW_SELINKUPMSK             0x3EC10U
> +#define IDT_SW_SELINKDNSTS             0x3EC14U
> +#define IDT_SW_SELINKDNMSK             0x3EC18U
> +/* Switch Event Fundamental Reset Status/Mask (DWORD) */
> +#define IDT_SW_SEFRSTSTS               0x3EC1CU
> +#define IDT_SW_SEFRSTMSK               0x3EC20U
> +/* Switch Event Hot Reset Status/Mask  (DWORD) */
> +#define IDT_SW_SEHRSTSTS               0x3EC24U
> +#define IDT_SW_SEHRSTMSK               0x3EC28U
> +/* Switch Event Failover Mask          (DWORD) */
> +#define IDT_SW_SEFOVRMSK               0x3EC2CU
> +/* Switch Event Global Signal Status/Mask (DWORD) */
> +#define IDT_SW_SEGSIGSTS               0x3EC30U
> +#define IDT_SW_SEGSIGMSK               0x3EC34U
> +/* NT Global Doorbell Status           (DWORD) */
> +#define IDT_SW_GDBELLSTS               0x3EC3CU
> +/* Switch partition N message M control (msgs routing table) (DWORD) */
> +#define IDT_SW_SWP0MSGCTL0             0x3EE00U
> +#define IDT_SW_SWP1MSGCTL0             0x3EE04U
> +#define IDT_SW_SWP2MSGCTL0             0x3EE08U
> +#define IDT_SW_SWP3MSGCTL0             0x3EE0CU
> +#define IDT_SW_SWP4MSGCTL0             0x3EE10U
> +#define IDT_SW_SWP5MSGCTL0             0x3EE14U
> +#define IDT_SW_SWP6MSGCTL0             0x3EE18U
> +#define IDT_SW_SWP7MSGCTL0             0x3EE1CU
> +#define IDT_SW_SWP0MSGCTL1             0x3EE20U
> +#define IDT_SW_SWP1MSGCTL1             0x3EE24U
> +#define IDT_SW_SWP2MSGCTL1             0x3EE28U
> +#define IDT_SW_SWP3MSGCTL1             0x3EE2CU
> +#define IDT_SW_SWP4MSGCTL1             0x3EE30U
> +#define IDT_SW_SWP5MSGCTL1             0x3EE34U
> +#define IDT_SW_SWP6MSGCTL1             0x3EE38U
> +#define IDT_SW_SWP7MSGCTL1             0x3EE3CU
> +#define IDT_SW_SWP0MSGCTL2             0x3EE40U
> +#define IDT_SW_SWP1MSGCTL2             0x3EE44U
> +#define IDT_SW_SWP2MSGCTL2             0x3EE48U
> +#define IDT_SW_SWP3MSGCTL2             0x3EE4CU
> +#define IDT_SW_SWP4MSGCTL2             0x3EE50U
> +#define IDT_SW_SWP5MSGCTL2             0x3EE54U
> +#define IDT_SW_SWP6MSGCTL2             0x3EE58U
> +#define IDT_SW_SWP7MSGCTL2             0x3EE5CU
> +#define IDT_SW_SWP0MSGCTL3             0x3EE60U
> +#define IDT_SW_SWP1MSGCTL3             0x3EE64U
> +#define IDT_SW_SWP2MSGCTL3             0x3EE68U
> +#define IDT_SW_SWP3MSGCTL3             0x3EE6CU
> +#define IDT_SW_SWP4MSGCTL3             0x3EE70U
> +#define IDT_SW_SWP5MSGCTL3             0x3EE74U
> +#define IDT_SW_SWP6MSGCTL3             0x3EE78U
> +#define IDT_SW_SWP7MSGCTL3             0x3EE7CU
> +/* SMBus Status and Control registers  (DWORD) */
> +#define IDT_SW_SMBUSSTS                        0x3F188U
> +#define IDT_SW_SMBUSCTL                        0x3F18CU
> +/* Serial EEPROM Interface             (DWORD) */
> +#define IDT_SW_EEPROMINTF              0x3F190U
> +/* MBus I/O Expander Address N         (DWORD) */
> +#define IDT_SW_IOEXPADDR0              0x3F198U
> +#define IDT_SW_IOEXPADDR1              0x3F19CU
> +#define IDT_SW_IOEXPADDR2              0x3F1A0U
> +#define IDT_SW_IOEXPADDR3              0x3F1A4U
> +#define IDT_SW_IOEXPADDR4              0x3F1A8U
> +#define IDT_SW_IOEXPADDR5              0x3F1ACU
> +/* General Purpose Events Control and Status registers (DWORD) */
> +#define IDT_SW_GPECTL                  0x3F1B0U
> +#define IDT_SW_GPESTS                  0x3F1B4U
> +/* Temperature sensor Control/Status/Alarm/Adjustment/Slope registers */
> +#define IDT_SW_TMPCTL                  0x3F1D4U
> +#define IDT_SW_TMPSTS                  0x3F1D8U
> +#define IDT_SW_TMPALARM                        0x3F1DCU
> +#define IDT_SW_TMPADJ                  0x3F1E0U
> +#define IDT_SW_TSSLOPE                 0x3F1E4U
> +/* SMBus Configuration Block header log        (DWORD) */
> +#define IDT_SW_SMBUSCBHL               0x3F1E8U
> +
> +/*
> + * Common registers related constants
> + * @IDT_REG_ALIGN:     Registers alignment used in the driver
> + * @IDT_REG_PCI_MAX:   Maximum PCI configuration space register value
> + * @IDT_REG_SW_MAX:    Maximum global register value
> + */
> +#define IDT_REG_ALIGN                  4
> +#define IDT_REG_PCI_MAX                        0x00FFFU
> +#define IDT_REG_SW_MAX                 0x3FFFFU
> +
> +/*
> + * PCICMDSTS register fields related constants
> + * @IDT_PCICMDSTS_IOAE:        I/O access enable
> + * @IDT_PCICMDSTS_MAE: Memory access enable
> + * @IDT_PCICMDSTS_BME: Bus master enable
> + */
> +#define IDT_PCICMDSTS_IOAE             0x00000001U
> +#define IDT_PCICMDSTS_MAE              0x00000002U
> +#define IDT_PCICMDSTS_BME              0x00000004U
> +
> +/*
> + * PCIEDCAP register fields related constants
> + * @IDT_PCIEDCAP_MPAYLOAD_MASK:         Maximum payload size mask
> + * @IDT_PCIEDCAP_MPAYLOAD_FLD:  Maximum payload size field offset
> + * @IDT_PCIEDCAP_MPAYLOAD_S128:         Max supported payload size of 128 bytes
> + * @IDT_PCIEDCAP_MPAYLOAD_S256:         Max supported payload size of 256 bytes
> + * @IDT_PCIEDCAP_MPAYLOAD_S512:         Max supported payload size of 512 bytes
> + * @IDT_PCIEDCAP_MPAYLOAD_S1024: Max supported payload size of 1024 bytes
> + * @IDT_PCIEDCAP_MPAYLOAD_S2048: Max supported payload size of 2048 bytes
> + */
> +#define IDT_PCIEDCAP_MPAYLOAD_MASK     0x00000007U
> +#define IDT_PCIEDCAP_MPAYLOAD_FLD      0
> +#define IDT_PCIEDCAP_MPAYLOAD_S128     0x00000000U
> +#define IDT_PCIEDCAP_MPAYLOAD_S256     0x00000001U
> +#define IDT_PCIEDCAP_MPAYLOAD_S512     0x00000002U
> +#define IDT_PCIEDCAP_MPAYLOAD_S1024    0x00000003U
> +#define IDT_PCIEDCAP_MPAYLOAD_S2048    0x00000004U
> +
> +/*
> + * PCIEDCTLSTS registers fields related constants
> + * @IDT_PCIEDCTL_MPS_MASK:     Maximum payload size mask
> + * @IDT_PCIEDCTL_MPS_FLD:      MPS field offset
> + * @IDT_PCIEDCTL_MPS_S128:     Max payload size of 128 bytes
> + * @IDT_PCIEDCTL_MPS_S256:     Max payload size of 256 bytes
> + * @IDT_PCIEDCTL_MPS_S512:     Max payload size of 512 bytes
> + * @IDT_PCIEDCTL_MPS_S1024:    Max payload size of 1024 bytes
> + * @IDT_PCIEDCTL_MPS_S2048:    Max payload size of 2048 bytes
> + * @IDT_PCIEDCTL_MPS_S4096:    Max payload size of 4096 bytes
> + */
> +#define IDT_PCIEDCTLSTS_MPS_MASK       0x000000E0U
> +#define IDT_PCIEDCTLSTS_MPS_FLD                5
> +#define IDT_PCIEDCTLSTS_MPS_S128       0x00000000U
> +#define IDT_PCIEDCTLSTS_MPS_S256       0x00000020U
> +#define IDT_PCIEDCTLSTS_MPS_S512       0x00000040U
> +#define IDT_PCIEDCTLSTS_MPS_S1024      0x00000060U
> +#define IDT_PCIEDCTLSTS_MPS_S2048      0x00000080U
> +#define IDT_PCIEDCTLSTS_MPS_S4096      0x000000A0U
> +
> +/*
> + * PCIELCAP register fields related constants
> + * @IDT_PCIELCAP_PORTNUM_MASK: Port number field mask
> + * @IDT_PCIELCAP_PORTNUM_FLD:  Port number field offset
> + */
> +#define IDT_PCIELCAP_PORTNUM_MASK      0xFF000000U
> +#define IDT_PCIELCAP_PORTNUM_FLD       24
> +
> +/*
> + * PCIELCTLSTS registers fields related constants
> + * @IDT_PCIELSTS_CLS_MASK:     Current link speed mask
> + * @IDT_PCIELSTS_CLS_FLD:      Current link speed field offset
> + * @IDT_PCIELSTS_NLW_MASK:     Negotiated link width mask
> + * @IDT_PCIELSTS_NLW_FLD:      Negotiated link width field offset
> + * @IDT_PCIELSTS_SCLK_COM:     Common slot clock configuration
> + */
> +#define IDT_PCIELCTLSTS_CLS_MASK       0x000F0000U
> +#define IDT_PCIELCTLSTS_CLS_FLD                16
> +#define IDT_PCIELCTLSTS_NLW_MASK       0x03F00000U
> +#define IDT_PCIELCTLSTS_NLW_FLD                20
> +#define IDT_PCIELCTLSTS_SCLK_COM       0x10000000U
> +
> +/*
> + * NTCTL register fields related constants
> + * @IDT_NTCTL_IDPROTDIS:       ID Protection check disable (disable MTBL)
> + * @IDT_NTCTL_CPEN:            Completion enable
> + * @IDT_NTCTL_RNS:             Request no snoop processing (if MTBL disabled)
> + * @IDT_NTCTL_ATP:             Address type processing (if MTBL disabled)
> + */
> +#define IDT_NTCTL_IDPROTDIS            0x00000001U
> +#define IDT_NTCTL_CPEN                 0x00000002U
> +#define IDT_NTCTL_RNS                  0x00000004U
> +#define IDT_NTCTL_ATP                  0x00000008U
> +
> +/*
> + * NTINTSTS register fields related constants
> + * @IDT_NTINTSTS_MSG:          Message interrupt bit
> + * @IDT_NTINTSTS_DBELL:                Doorbell interrupt bit
> + * @IDT_NTINTSTS_SEVENT:       Switch Event interrupt bit
> + * @IDT_NTINTSTS_TMPSENSOR:    Temperature sensor interrupt bit
> + */
> +#define IDT_NTINTSTS_MSG               0x00000001U
> +#define IDT_NTINTSTS_DBELL             0x00000002U
> +#define IDT_NTINTSTS_SEVENT            0x00000008U
> +#define IDT_NTINTSTS_TMPSENSOR         0x00000080U
> +
> +/*
> + * NTINTMSK register fields related constants
> + * @IDT_NTINTMSK_MSG:          Message interrupt mask bit
> + * @IDT_NTINTMSK_DBELL:                Doorbell interrupt mask bit
> + * @IDT_NTINTMSK_SEVENT:       Switch Event interrupt mask bit
> + * @IDT_NTINTMSK_TMPSENSOR:    Temperature sensor interrupt mask bit
> + * @IDT_NTINTMSK_ALL:          All the useful interrupts mask
> + */
> +#define IDT_NTINTMSK_MSG               0x00000001U
> +#define IDT_NTINTMSK_DBELL             0x00000002U
> +#define IDT_NTINTMSK_SEVENT            0x00000008U
> +#define IDT_NTINTMSK_TMPSENSOR         0x00000080U
> +#define IDT_NTINTMSK_ALL \
> +       (IDT_NTINTMSK_MSG | IDT_NTINTMSK_DBELL | \
> +        IDT_NTINTMSK_SEVENT | IDT_NTINTMSK_TMPSENSOR)
> +
> +/*
> + * NTGSIGNAL register fields related constants
> + * @IDT_NTGSIGNAL_SET: Set global signal of the local partition
> + */
> +#define IDT_NTGSIGNAL_SET              0x00000001U
> +
> +/*
> + * BARSETUP register fields related constants
> + * @IDT_BARSETUP_TYPE_MASK:    Mask of the TYPE field
> + * @IDT_BARSETUP_TYPE_32:      32-bit addressing BAR
> + * @IDT_BARSETUP_TYPE_64:      64-bit addressing BAR
> + * @IDT_BARSETUP_PREF:         Value of the BAR prefetchable field
> + * @IDT_BARSETUP_SIZE_MASK:    Mask of the SIZE field
> + * @IDT_BARSETUP_SIZE_FLD:     SIZE field offset
> + * @IDT_BARSETUP_SIZE_CFG:     SIZE field value in case of config space MODE
> + * @IDT_BARSETUP_MODE_CFG:     Configuration space BAR mode
> + * @IDT_BARSETUP_ATRAN_MASK:   ATRAN field mask
> + * @IDT_BARSETUP_ATRAN_FLD:    ATRAN field offset
> + * @IDT_BARSETUP_ATRAN_DIR:    Direct address translation memory window
> + * @IDT_BARSETUP_ATRAN_LUT12:  12-entry lookup table
> + * @IDT_BARSETUP_ATRAN_LUT24:  24-entry lookup table
> + * @IDT_BARSETUP_TPART_MASK:   TPART field mask
> + * @IDT_BARSETUP_TPART_FLD:    TPART field offset
> + * @IDT_BARSETUP_EN:           BAR enable bit
> + */
> +#define IDT_BARSETUP_TYPE_MASK         0x00000006U
> +#define IDT_BARSETUP_TYPE_FLD          0
> +#define IDT_BARSETUP_TYPE_32           0x00000000U
> +#define IDT_BARSETUP_TYPE_64           0x00000004U
> +#define IDT_BARSETUP_PREF              0x00000008U
> +#define IDT_BARSETUP_SIZE_MASK         0x000003F0U
> +#define IDT_BARSETUP_SIZE_FLD          4
> +#define IDT_BARSETUP_SIZE_CFG          0x000000C0U
> +#define IDT_BARSETUP_MODE_CFG          0x00000400U
> +#define IDT_BARSETUP_ATRAN_MASK                0x00001800U
> +#define IDT_BARSETUP_ATRAN_FLD         11
> +#define IDT_BARSETUP_ATRAN_DIR         0x00000000U
> +#define IDT_BARSETUP_ATRAN_LUT12       0x00000800U
> +#define IDT_BARSETUP_ATRAN_LUT24       0x00001000U
> +#define IDT_BARSETUP_TPART_MASK                0x0000E000U
> +#define IDT_BARSETUP_TPART_FLD         13
> +#define IDT_BARSETUP_EN                        0x80000000U
> +
> +/*
> + * NTMTBLDATA register fields related constants
> + * @IDT_NTMTBLDATA_VALID:      Set the MTBL entry being valid
> + * @IDT_NTMTBLDATA_REQID_MASK: Bus:Device:Function field mask
> + * @IDT_NTMTBLDATA_REQID_FLD:  Bus:Device:Function field offset
> + * @IDT_NTMTBLDATA_PART_MASK:  Partition field mask
> + * @IDT_NTMTBLDATA_PART_FLD:   Partition field offset
> + * @IDT_NTMTBLDATA_ATP_TRANS:  Enable AT field translation on request TLPs
> + * @IDT_NTMTBLDATA_CNS_INV:    Enable No Snoop attribute inversion of
> + *                             Completion TLPs
> + * @IDT_NTMTBLDATA_RNS_INV:    Enable No Snoop attribute inversion of
> + *                             Request TLPs
> + */
> +#define IDT_NTMTBLDATA_VALID           0x00000001U
> +#define IDT_NTMTBLDATA_REQID_MASK      0x0001FFFEU
> +#define IDT_NTMTBLDATA_REQID_FLD       1
> +#define IDT_NTMTBLDATA_PART_MASK       0x000E0000U
> +#define IDT_NTMTBLDATA_PART_FLD                17
> +#define IDT_NTMTBLDATA_ATP_TRANS       0x20000000U
> +#define IDT_NTMTBLDATA_CNS_INV         0x40000000U
> +#define IDT_NTMTBLDATA_RNS_INV         0x80000000U
> +
> +/*
> + * REQIDCAP register fields related constants
> + * @IDT_REQIDCAP_REQID_MASK:   Request ID field mask
> + * @IDT_REQIDCAP_REQID_FLD:    Request ID field offset
> + */
> +#define IDT_REQIDCAP_REQID_MASK                0x0000FFFFU
> +#define IDT_REQIDCAP_REQID_FLD         0
> +
> +/*
> + * LUTOFFSET register fields related constants
> + * @IDT_LUTOFFSET_INDEX_MASK:  Lookup table index field mask
> + * @IDT_LUTOFFSET_INDEX_FLD:   Lookup table index field offset
> + * @IDT_LUTOFFSET_BAR_MASK:    Lookup table BAR select field mask
> + * @IDT_LUTOFFSET_BAR_FLD:     Lookup table BAR select field offset
> + */
> +#define IDT_LUTOFFSET_INDEX_MASK       0x0000001FU
> +#define IDT_LUTOFFSET_INDEX_FLD                0
> +#define IDT_LUTOFFSET_BAR_MASK         0x00000700U
> +#define IDT_LUTOFFSET_BAR_FLD          8
> +
> +/*
> + * LUTUDATA register fields related constants
> + * @IDT_LUTUDATA_PART_MASK:    Partition field mask
> + * @IDT_LUTUDATA_PART_FLD:     Partition field offset
> + * @IDT_LUTUDATA_VALID:                Lookup table entry valid bit
> + */
> +#define IDT_LUTUDATA_PART_MASK         0x0000000FU
> +#define IDT_LUTUDATA_PART_FLD          0
> +#define IDT_LUTUDATA_VALID             0x80000000U
> +
> +/*
> + * SWPARTxSTS register fields related constants
> + * @IDT_SWPARTxSTS_SCI:                Switch partition state change initiated
> + * @IDT_SWPARTxSTS_SCC:                Switch partition state change completed
> + * @IDT_SWPARTxSTS_STATE_MASK: Switch partition state mask
> + * @IDT_SWPARTxSTS_STATE_FLD:  Switch partition state field offset
> + * @IDT_SWPARTxSTS_STATE_DIS:  Switch partition disabled
> + * @IDT_SWPARTxSTS_STATE_ACT:  Switch partition enabled
> + * @IDT_SWPARTxSTS_STATE_RES:  Switch partition in reset
> + * @IDT_SWPARTxSTS_US:         Switch partition has upstream port
> + * @IDT_SWPARTxSTS_USID_MASK:  Switch partition upstream port ID mask
> + * @IDT_SWPARTxSTS_USID_FLD:   Switch partition upstream port ID field offset
> + * @IDT_SWPARTxSTS_NT:         Upstream port has NT function
> + * @IDT_SWPARTxSTS_DMA:                Upstream port has DMA function
> + */
> +#define IDT_SWPARTxSTS_SCI             0x00000001U
> +#define IDT_SWPARTxSTS_SCC             0x00000002U
> +#define IDT_SWPARTxSTS_STATE_MASK      0x00000060U
> +#define IDT_SWPARTxSTS_STATE_FLD       5
> +#define IDT_SWPARTxSTS_STATE_DIS       0x00000000U
> +#define IDT_SWPARTxSTS_STATE_ACT       0x00000020U
> +#define IDT_SWPARTxSTS_STATE_RES       0x00000060U
> +#define IDT_SWPARTxSTS_US              0x00000100U
> +#define IDT_SWPARTxSTS_USID_MASK       0x00003E00U
> +#define IDT_SWPARTxSTS_USID_FLD                9
> +#define IDT_SWPARTxSTS_NT              0x00004000U
> +#define IDT_SWPARTxSTS_DMA             0x00008000U
> +
> +/*
> + * SWPORTxSTS register fields related constants
> + * @IDT_SWPORTxSTS_OMCI:       Operation mode change initiated
> + * @IDT_SWPORTxSTS_OMCC:       Operation mode change completed
> + * @IDT_SWPORTxSTS_LINKUP:     Link up status
> + * @IDT_SWPORTxSTS_DS:         Port lanes behave as downstream lanes
> + * @IDT_SWPORTxSTS_MODE_MASK:  Port mode field mask
> + * @IDT_SWPORTxSTS_MODE_FLD:   Port mode field offset
> + * @IDT_SWPORTxSTS_MODE_DIS:   Port mode - disabled
> + * @IDT_SWPORTxSTS_MODE_DS:    Port mode - downstream switch port
> + * @IDT_SWPORTxSTS_MODE_US:    Port mode - upstream switch port
> + * @IDT_SWPORTxSTS_MODE_NT:    Port mode - NT function
> + * @IDT_SWPORTxSTS_MODE_USNT:  Port mode - upstream switch port with NTB
> + * @IDT_SWPORTxSTS_MODE_UNAT:  Port mode - unattached
> + * @IDT_SWPORTxSTS_MODE_USDMA: Port mode - upstream switch port with DMA
> + * @IDT_SWPORTxSTS_MODE_USNTDMA:Port mode - upstream port with NTB and DMA
> + * @IDT_SWPORTxSTS_MODE_NTDMA: Port mode - NT function with DMA
> + * @IDT_SWPORTxSTS_SWPART_MASK:        Port partition field mask
> + * @IDT_SWPORTxSTS_SWPART_FLD: Port partition field offset
> + * @IDT_SWPORTxSTS_DEVNUM_MASK:        Port device number field mask
> + * @IDT_SWPORTxSTS_DEVNUM_FLD: Port device number field offset
> + */
> +#define IDT_SWPORTxSTS_OMCI            0x00000001U
> +#define IDT_SWPORTxSTS_OMCC            0x00000002U
> +#define IDT_SWPORTxSTS_LINKUP          0x00000010U
> +#define IDT_SWPORTxSTS_DS              0x00000020U
> +#define IDT_SWPORTxSTS_MODE_MASK       0x000003C0U
> +#define IDT_SWPORTxSTS_MODE_FLD                6
> +#define IDT_SWPORTxSTS_MODE_DIS                0x00000000U
> +#define IDT_SWPORTxSTS_MODE_DS         0x00000040U
> +#define IDT_SWPORTxSTS_MODE_US         0x00000080U
> +#define IDT_SWPORTxSTS_MODE_NT         0x000000C0U
> +#define IDT_SWPORTxSTS_MODE_USNT       0x00000100U
> +#define IDT_SWPORTxSTS_MODE_UNAT       0x00000140U
> +#define IDT_SWPORTxSTS_MODE_USDMA      0x00000180U
> +#define IDT_SWPORTxSTS_MODE_USNTDMA    0x000001C0U
> +#define IDT_SWPORTxSTS_MODE_NTDMA      0x00000200U
> +#define IDT_SWPORTxSTS_SWPART_MASK     0x00001C00U
> +#define IDT_SWPORTxSTS_SWPART_FLD      10
> +#define IDT_SWPORTxSTS_DEVNUM_MASK     0x001F0000U
> +#define IDT_SWPORTxSTS_DEVNUM_FLD      16
> +
> +/*
> + * SEMSK register fields related constants
> + * @IDT_SEMSK_LINKUP:  Link Up event mask bit
> + * @IDT_SEMSK_LINKDN:  Link Down event mask bit
> + * @IDT_SEMSK_GSIGNAL: Global Signal event mask bit
> + */
> +#define IDT_SEMSK_LINKUP               0x00000001U
> +#define IDT_SEMSK_LINKDN               0x00000002U
> +#define IDT_SEMSK_GSIGNAL              0x00000020U
> +
> +/*
> + * SWPxMSGCTL register fields related constants
> + * @IDT_SWPxMSGCTL_REG_MASK:   Register select field mask
> + * @IDT_SWPxMSGCTL_REG_FLD:    Register select field offset
> + * @IDT_SWPxMSGCTL_PART_MASK:  Partition select field mask
> + * @IDT_SWPxMSGCTL_PART_FLD:   Partition select field offset
> + */
> +#define IDT_SWPxMSGCTL_REG_MASK                0x00000003U
> +#define IDT_SWPxMSGCTL_REG_FLD         0
> +#define IDT_SWPxMSGCTL_PART_MASK       0x00000070U
> +#define IDT_SWPxMSGCTL_PART_FLD                4
> +
> +/*
> + * TMPSTS register fields related constants
> + * @IDT_TMPSTS_TEMP_MASK:      Current temperature field mask
> + * @IDT_TMPSTS_TEMP_FLD:       Current temperature field offset
> + */
> +#define IDT_TMPSTS_TEMP_MASK           0x000000FFU
> +#define IDT_TMPSTS_TEMP_FLD            0
> +
> +/*
> + * Helper macro to get/set the corresponding field value
> + * @GET_FIELD:         Retrieve the value of the corresponding field
> + * @SET_FIELD:         Set the specified field up
> + * @IS_FLD_SET:                Check whether a field is set with value
> + */
> +#define GET_FIELD(field, data) \
> +       (((u32)(data) & IDT_ ##field## _MASK) >> IDT_ ##field## _FLD)
> +#define SET_FIELD(field, data, value) \
> +       (((u32)(data) & ~IDT_ ##field## _MASK) | \
> +        ((u32)(value) << IDT_ ##field## _FLD))
> +#define IS_FLD_SET(field, data, value) \
> +       (((u32)(data) & IDT_ ##field## _MASK) == IDT_ ##field## _ ##value)
> +
> +/*
> + * Useful registers masks:
> + * @IDT_DBELL_MASK:    Doorbell bits mask
> + * @IDT_OUTMSG_MASK:   Out messages status bits mask
> + * @IDT_INMSG_MASK:    In messages status bits mask
> + * @IDT_MSG_MASK:      Any message status bits mask
> + */
> +#define IDT_DBELL_MASK         ((u32)0xFFFFFFFFU)
> +#define IDT_OUTMSG_MASK                ((u32)0x0000000FU)
> +#define IDT_INMSG_MASK         ((u32)0x000F0000U)
> +#define IDT_MSG_MASK           (IDT_INMSG_MASK | IDT_OUTMSG_MASK)
> +
> +/*
> + * Number of IDT NTB resources:
> + * @IDT_MSG_CNT:       Number of Message registers
> + * @IDT_BAR_CNT:       Number of BARs of each port
> + * @IDT_MTBL_ENTRY_CNT:        Number mapping table entries
> + */
> +#define IDT_MSG_CNT            4
> +#define IDT_BAR_CNT            6
> +#define IDT_MTBL_ENTRY_CNT     64
> +
> +/*
> + * General IDT PCIe-switch constant
> + * @IDT_MAX_NR_PORTS:  Maximum number of ports per IDT PCIe-switch
> + * @IDT_MAX_NR_PARTS:  Maximum number of partitions per IDT PCIe-switch
> + * @IDT_MAX_NR_PEERS:  Maximum number of NT-peers per IDT PCIe-switch
> + * @IDT_MAX_NR_MWS:    Maximum number of Memory Widows
> + * @IDT_PCIE_REGSIZE:  Size of the registers in bytes
> + * @IDT_TRANS_ALIGN:   Alignment of translated base address
> + * @IDT_DIR_SIZE_ALIGN:        Alignment of size setting for direct translated MWs.
> + *                     Even though the lower 10 bits are reserved, they are
> + *                     treated by IDT as one's so basically there is no any
> + *                     alignment of size limit for DIR address translation.
> + */
> +#define IDT_MAX_NR_PORTS       24
> +#define IDT_MAX_NR_PARTS       8
> +#define IDT_MAX_NR_PEERS       8
> +#define IDT_MAX_NR_MWS         29
> +#define IDT_PCIE_REGSIZE       4
> +#define IDT_TRANS_ALIGN                4
> +#define IDT_DIR_SIZE_ALIGN     1
> +
> +/*
> + * IDT Memory Windows type. Depending on the device settings, IDT supports
> + * Direct Address Translation MW registers and Lookup Table registers
> + * @IDT_MW_DIR:                Direct address translation
> + * @IDT_MW_LUT12:      12-entry lookup table entry
> + * @IDT_MW_LUT24:      24-entry lookup table entry
> + *
> + * NOTE These values are exactly the same as one of the BARSETUP ATRAN field
> + */
> +enum idt_mw_type {
> +       IDT_MW_DIR = 0x0,
> +       IDT_MW_LUT12 = 0x1,
> +       IDT_MW_LUT24 = 0x2
> +};
> +
> +/*
> + * IDT PCIe-switch model private data
> + * @name:      Device name
> + * @port_cnt:  Total number of NT endpoint ports
> + * @ports:     Port ids
> + */
> +struct idt_89hpes_cfg {
> +       char *name;
> +       unsigned char port_cnt;
> +       unsigned char ports[];
> +};
> +
> +/*
> + * Memory window configuration structure
> + * @type:      Type of the memory window (direct address translation or lookup
> + *             table)
> + *
> + * @bar:       PCIe BAR the memory window referenced to
> + * @idx:       Index of the memory window within the BAR
> + *
> + * @addr_align:        Alignment of translated address
> + * @size_align:        Alignment of memory window size
> + * @size_max:  Maximum size of memory window
> + */
> +struct idt_mw_cfg {
> +       enum idt_mw_type type;
> +
> +       unsigned char bar;
> +       unsigned char idx;
> +
> +       u64 addr_align;
> +       u64 size_align;
> +       u64 size_max;
> +};
> +
> +/*
> + * Description structure of peer IDT NT-functions:
> + * @port:              NT-function port
> + * @part:              NT-function partition
> + *
> + * @mw_cnt:            Number of memory windows supported by NT-function
> + * @mws:               Array of memory windows descriptors
> + */
> +struct idt_ntb_peer {
> +       unsigned char port;
> +       unsigned char part;
> +
> +       unsigned char mw_cnt;
> +       struct idt_mw_cfg *mws;
> +};
> +
> +/*
> + * Description structure of local IDT NT-function:
> + * @ntb:               Linux NTB-device description structure
> + * @swcfg:             Pointer to the structure of local IDT PCIe-switch
> + *                     specific cofnfigurations
> + *
> + * @port:              Local NT-function port
> + * @part:              Local NT-function partition
> + *
> + * @peer_cnt:          Number of peers with activated NTB-function
> + * @peers:             Array of peers descripting structures
> + * @port_idx_map:      Map of port number -> peer index
> + * @part_idx_map:      Map of partition number -> peer index
> + *
> + * @mtbl_lock:         Mapping table access lock
> + *
> + * @mw_cnt:            Number of memory windows supported by NT-function
> + * @mws:               Array of memory windows descriptors
> + * @lut_lock:          Lookup table access lock
> + *
> + * @msg_locks:         Message registers mapping table lockers
> + *
> + * @cfgspc:            Virtual address of the memory mapped configuration
> + *                     space of the NT-function
> + * @db_mask_lock:      Doorbell mask register lock
> + * @msg_mask_lock:     Message mask register lock
> + * @gasa_lock:         GASA registers access lock
> + *
> + * @dbgfs_info:                DebugFS info node
> + */
> +struct idt_ntb_dev {
> +       struct ntb_dev ntb;
> +       struct idt_89hpes_cfg *swcfg;
> +
> +       unsigned char port;
> +       unsigned char part;
> +
> +       unsigned char peer_cnt;
> +       struct idt_ntb_peer peers[IDT_MAX_NR_PEERS];
> +       char port_idx_map[IDT_MAX_NR_PORTS];
> +       char part_idx_map[IDT_MAX_NR_PARTS];
> +
> +       spinlock_t mtbl_lock;
> +
> +       unsigned char mw_cnt;
> +       struct idt_mw_cfg *mws;
> +       spinlock_t lut_lock;
> +
> +       spinlock_t msg_locks[IDT_MSG_CNT];
> +
> +       void __iomem *cfgspc;
> +       spinlock_t db_mask_lock;
> +       spinlock_t msg_mask_lock;
> +       spinlock_t gasa_lock;
> +
> +       struct dentry *dbgfs_info;
> +};
> +#define to_ndev_ntb(__ntb) container_of(__ntb, struct idt_ntb_dev, ntb)
> +
> +/*
> + * Descriptor of the IDT PCIe-switch BAR resources
> + * @setup:     BAR setup register
> + * @limit:     BAR limit register
> + * @ltbase:    Lower translated base address
> + * @utbase:    Upper translated base address
> + */
> +struct idt_ntb_bar {
> +       unsigned int setup;
> +       unsigned int limit;
> +       unsigned int ltbase;
> +       unsigned int utbase;
> +};
> +
> +/*
> + * Descriptor of the IDT PCIe-switch message resources
> + * @in:                Inbound message register
> + * @out:       Outbound message register
> + * @src:       Source of inbound message register
> + */
> +struct idt_ntb_msg {
> +       unsigned int in;
> +       unsigned int out;
> +       unsigned int src;
> +};
> +
> +/*
> + * Descriptor of the IDT PCIe-switch NT-function specific parameters in the
> + * PCI Configuration Space
> + * @bars:      BARs related registers
> + * @msgs:      Messaging related registers
> + */
> +struct idt_ntb_regs {
> +       struct idt_ntb_bar bars[IDT_BAR_CNT];
> +       struct idt_ntb_msg msgs[IDT_MSG_CNT];
> +};
> +
> +/*
> + * Descriptor of the IDT PCIe-switch port specific parameters in the
> + * Global Configuration Space
> + * @pcicmdsts:  PCI command/status register
> + * @pcielctlsts: PCIe link control/status
> + *
> + * @ctl:       Port control register
> + * @sts:       Port status register
> + *
> + * @bars:      BARs related registers
> + */
> +struct idt_ntb_port {
> +       unsigned int pcicmdsts;
> +       unsigned int pcielctlsts;
> +       unsigned int ntctl;
> +
> +       unsigned int ctl;
> +       unsigned int sts;
> +
> +       struct idt_ntb_bar bars[IDT_BAR_CNT];
> +};
> +
> +/*
> + * Descriptor of the IDT PCIe-switch partition specific parameters.
> + * @ctl:       Partition control register in the Global Address Space
> + * @sts:       Partition status register in the Global Address Space
> + * @msgctl:    Messages control registers
> + */
> +struct idt_ntb_part {
> +       unsigned int ctl;
> +       unsigned int sts;
> +       unsigned int msgctl[IDT_MSG_CNT];
> +};
> +
> +#endif /* NTB_HW_IDT_H */
> --
> 2.6.6
>

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ