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