On Tue, Mar 07, 2017 at 05:02:38AM +0300, Serge Semin 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 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)
> 
> Signed-off-by: Serge Semin <fancer.lan...@gmail.com>
> Acked-by: Allen Hubbe <allen.hu...@dell.com>
> 
> ---
> 
> 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
> 
> Changelog v5:
> - Fix minor documentation issues
> - Replace writel/readl with iowrite32/ioread32 methods
> - Discard dev_*() wrappers with origins
> - Use pci_alloc_irq_vectors() for IRQ number and ISR initialization
> - Use Mananged Device Resource as much as possible:
> devm_request_threaded_irq(), pcim_iomap_regions_request_all()
> 
>  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 | 2600 
> +++++++++++++++++++++++++++++++++++++++
>  drivers/ntb/hw/idt/ntb_hw_idt.h | 1149 +++++++++++++++++
>  6 files changed, 3783 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..c5d53ee
> --- /dev/null
> +++ b/drivers/ntb/hw/idt/ntb_hw_idt.c
> @@ -0,0 +1,2600 @@
> +/*
> + *   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.lan...@gmail.com>, <sergey.se...@t-platforms.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
> + *
> + * IDT PCIe-switch registers are all Little endian.
> + */
> +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 */
> +     iowrite32(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
> + *
> + * IDT PCIe-switch Global configuration registers are all Little endian.
> + *
> + * 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 ioread32(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
> + *
> + * IDT PCIe-switch Global configuration registers are all Little endian.
> + */
> +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 */
> +     iowrite32((u32)reg, ndev->cfgspc + (ptrdiff_t)IDT_NT_GASAADDR);
> +     /* Put the new value of the register */
> +     iowrite32(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
> + *
> + * IDT PCIe-switch Global configuration registers are all Little endian.
> + *
> + * 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 */
> +     iowrite32((u32)reg, ndev->cfgspc + (ptrdiff_t)IDT_NT_GASAADDR);
> +     /* Get the data of the register */
> +     data = ioread32(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(&ndev->ntb.pdev->dev, "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(&ndev->ntb.pdev->dev, "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(&ndev->ntb.pdev->dev, "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(&ndev->ntb.pdev->dev, "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(&ndev->ntb.pdev->dev, "SE 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(&ndev->ntb.pdev->dev, "Local NTB link 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(&ndev->ntb.pdev->dev, "Local NTB link 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);

drivers/ntb/hw/idt/ntb_hw_idt.c: In function ‘idt_scan_mws’:
drivers/ntb/hw/idt/ntb_hw_idt.c:1032:42: warning: ‘skip_bar’ may be used 
uninitialized in this function [-Wmaybe-uninitialized]
  for (bidx = 0; bidx < IDT_BAR_CNT; bidx += 1 + skip_bar) {

Also, the name sort of sucks.  Perhaps, "64bit_bar" would make it more
obvious what you are doing here.


> +
> +             /* 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(&ndev->ntb.pdev->dev,
> +                     "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(&ndev->ntb.pdev->dev,
> +                             "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(&ndev->ntb.pdev->dev, "Outbound and inbound MWs initialized");
> +
> +     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(&ndev->ntb.pdev->dev, "DB 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(&ndev->ntb.pdev->dev, "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(&ndev->ntb.pdev->dev, "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(&ndev->ntb.pdev->dev,
> +             "Temp sensor IRQ detected %#08x", ntint_sts);
> +
> +     /* Print temperature value to log */
> +     dev_warn(&ndev->ntb.pdev->dev, "Temperature %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
> + *
> + * 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;
> +
> +     /* Allocate just one interrupt vector for the ISR */
> +     ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI | PCI_IRQ_LEGACY);
> +     if (ret != 1) {
> +             dev_err(&pdev->dev, "Failed to allocate IRQ vector");
> +             return ret;
> +     }
> +
> +     /* Retrieve the IRQ vector */
> +     ret = pci_irq_vector(pdev, 0);
> +     if (ret < 0) {
> +             dev_err(&pdev->dev, "Failed to get IRQ vector");
> +             goto err_free_vectors;
> +     }
> +
> +     /* Set the IRQ handler */
> +     ret = devm_request_threaded_irq(&pdev->dev, ret, NULL, idt_thread_isr,
> +                                     IRQF_ONESHOT, NTB_IRQNAME, ndev);
> +     if (ret != 0) {
> +             dev_err(&pdev->dev, "Failed to set MSI IRQ handler, %d", ret);
> +             goto err_free_vectors;
> +     }
> +
> +     /* 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);
> +
> +     /* From now on the interrupts are enabled */
> +     dev_dbg(&pdev->dev, "NTB interrupts initialized");
> +
> +     return 0;
> +
> +err_free_vectors:
> +     pci_free_irq_vectors(pdev);
> +
> +     return ret;
> +}
> +
> +
> +/*
> + * idt_deinit_ist() - deinitialize PCIe interrupt handler
> + * @ndev:    IDT NTB hardware driver descriptor
> + *
> + * Disable corresponding interrupts and free allocated IRQ vectors.
> + */
> +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);
> +
> +     /* Free allocated IRQ vectors */
> +     pci_free_irq_vectors(pdev);
> +
> +     dev_dbg(&pdev->dev, "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(&ndev->ntb.pdev->dev, "IDT IRQs 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(&ndev->ntb.pdev->dev, "Failed to register NTB device");
> +             return ret;
> +     }
> +
> +     dev_dbg(&ndev->ntb.pdev->dev, "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(&ndev->ntb.pdev->dev, "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(&ndev->ntb.pdev->dev, "Top DebugFS directory 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(&ndev->ntb.pdev->dev, "Failed to create DebugFS node");
> +             return PTR_ERR(ndev->dbgfs_info);
> +     }
> +
> +     dev_dbg(&ndev->ntb.pdev->dev, "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(&ndev->ntb.pdev->dev, "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, "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(&pdev->dev, "IDT %s discovered", ndev->swcfg->name);
> +
> +     dev_dbg(&pdev->dev, "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;
> +
> +     /* 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(&pdev->dev, "Failed to set DMA bit mask\n");
> +                     return ret;
> +             }
> +             dev_warn(&pdev->dev, "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(&pdev->dev,
> +                             "Failed to set consistent DMA bit mask\n");
> +                     return ret;
> +             }
> +             dev_warn(&pdev->dev,
> +                     "Cannot set consistent DMA highmem bit mask\n");
> +     }
> +
> +     /*
> +      * 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(&pdev->dev, "PCIe AER capability 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(&pdev->dev, "Failed to enable 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 and map BAR0 only */
> +     ret = pcim_iomap_regions_request_all(pdev, 1, NTB_NAME);
> +     if (ret != 0) {
> +             dev_err(&pdev->dev, "Failed to request resources\n");
> +             goto err_clear_master;
> +     }
> +
> +     /* Retrieve virtual address of BAR0 - PCI configuration space */
> +     ndev->cfgspc = pcim_iomap_table(pdev)[0];
> +
> +     /* Put the IDT driver data pointer to the PCI-device private pointer */
> +     pci_set_drvdata(pdev, ndev);
> +
> +     dev_dbg(&pdev->dev, "NT-function PCIe interface initialized");
> +
> +     return 0;
> +
> +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);
> +
> +     /* Clear the bus master disabling the Request TLPs translation */
> +     pci_clear_master(pdev);
> +
> +     /* Disable the AER capability */
> +     (void)pci_disable_pcie_error_reporting(pdev);
> +
> +     dev_dbg(&pdev->dev, "NT-function PCIe interface cleared");
> +}
> +
> +/*===========================================================================
> + *                       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(&pdev->dev, "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..856fd18
> --- /dev/null
> +++ b/drivers/ntb/hw/idt/ntb_hw_idt.h
> @@ -0,0 +1,1149 @@
> +/*
> + *   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.lan...@gmail.com>, <sergey.se...@t-platforms.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
> +
> +/*
> + * 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
> 

Reply via email to