18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci *   This file is provided under a GPLv2 license.  When using or
38c2ecf20Sopenharmony_ci *   redistributing this file, you may do so under that license.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *   GPL LICENSE SUMMARY
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *   Copyright (C) 2016-2018 T-Platforms JSC All Rights Reserved.
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci *   This program is free software; you can redistribute it and/or modify it
108c2ecf20Sopenharmony_ci *   under the terms and conditions of the GNU General Public License,
118c2ecf20Sopenharmony_ci *   version 2, as published by the Free Software Foundation.
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci *   This program is distributed in the hope that it will be useful, but
148c2ecf20Sopenharmony_ci *   WITHOUT ANY WARRANTY; without even the implied warranty of
158c2ecf20Sopenharmony_ci *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
168c2ecf20Sopenharmony_ci *   Public License for more details.
178c2ecf20Sopenharmony_ci *
188c2ecf20Sopenharmony_ci *   You should have received a copy of the GNU General Public License along
198c2ecf20Sopenharmony_ci *   with this program; if not, one can be found http://www.gnu.org/licenses/.
208c2ecf20Sopenharmony_ci *
218c2ecf20Sopenharmony_ci *   The full GNU General Public License is included in this distribution in
228c2ecf20Sopenharmony_ci *   the file called "COPYING".
238c2ecf20Sopenharmony_ci *
248c2ecf20Sopenharmony_ci *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
258c2ecf20Sopenharmony_ci *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
268c2ecf20Sopenharmony_ci *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
278c2ecf20Sopenharmony_ci *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
288c2ecf20Sopenharmony_ci *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
298c2ecf20Sopenharmony_ci *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
308c2ecf20Sopenharmony_ci *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
318c2ecf20Sopenharmony_ci *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
328c2ecf20Sopenharmony_ci *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
338c2ecf20Sopenharmony_ci *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
348c2ecf20Sopenharmony_ci *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
358c2ecf20Sopenharmony_ci *
368c2ecf20Sopenharmony_ci * IDT PCIe-switch NTB Linux driver
378c2ecf20Sopenharmony_ci *
388c2ecf20Sopenharmony_ci * Contact Information:
398c2ecf20Sopenharmony_ci * Serge Semin <fancer.lancer@gmail.com>, <Sergey.Semin@t-platforms.ru>
408c2ecf20Sopenharmony_ci */
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci#include <linux/stddef.h>
438c2ecf20Sopenharmony_ci#include <linux/types.h>
448c2ecf20Sopenharmony_ci#include <linux/kernel.h>
458c2ecf20Sopenharmony_ci#include <linux/bitops.h>
468c2ecf20Sopenharmony_ci#include <linux/sizes.h>
478c2ecf20Sopenharmony_ci#include <linux/module.h>
488c2ecf20Sopenharmony_ci#include <linux/moduleparam.h>
498c2ecf20Sopenharmony_ci#include <linux/init.h>
508c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
518c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
528c2ecf20Sopenharmony_ci#include <linux/mutex.h>
538c2ecf20Sopenharmony_ci#include <linux/pci.h>
548c2ecf20Sopenharmony_ci#include <linux/aer.h>
558c2ecf20Sopenharmony_ci#include <linux/slab.h>
568c2ecf20Sopenharmony_ci#include <linux/list.h>
578c2ecf20Sopenharmony_ci#include <linux/debugfs.h>
588c2ecf20Sopenharmony_ci#include <linux/hwmon.h>
598c2ecf20Sopenharmony_ci#include <linux/hwmon-sysfs.h>
608c2ecf20Sopenharmony_ci#include <linux/ntb.h>
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci#include "ntb_hw_idt.h"
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci#define NTB_NAME	"ntb_hw_idt"
658c2ecf20Sopenharmony_ci#define NTB_DESC	"IDT PCI-E Non-Transparent Bridge Driver"
668c2ecf20Sopenharmony_ci#define NTB_VER		"2.0"
678c2ecf20Sopenharmony_ci#define NTB_IRQNAME	"ntb_irq_idt"
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(NTB_DESC);
708c2ecf20Sopenharmony_ciMODULE_VERSION(NTB_VER);
718c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
728c2ecf20Sopenharmony_ciMODULE_AUTHOR("T-platforms");
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci/*
758c2ecf20Sopenharmony_ci * NT Endpoint registers table simplifying a loop access to the functionally
768c2ecf20Sopenharmony_ci * related registers
778c2ecf20Sopenharmony_ci */
788c2ecf20Sopenharmony_cistatic const struct idt_ntb_regs ntdata_tbl = {
798c2ecf20Sopenharmony_ci	{ {IDT_NT_BARSETUP0,	IDT_NT_BARLIMIT0,
808c2ecf20Sopenharmony_ci	   IDT_NT_BARLTBASE0,	IDT_NT_BARUTBASE0},
818c2ecf20Sopenharmony_ci	  {IDT_NT_BARSETUP1,	IDT_NT_BARLIMIT1,
828c2ecf20Sopenharmony_ci	   IDT_NT_BARLTBASE1,	IDT_NT_BARUTBASE1},
838c2ecf20Sopenharmony_ci	  {IDT_NT_BARSETUP2,	IDT_NT_BARLIMIT2,
848c2ecf20Sopenharmony_ci	   IDT_NT_BARLTBASE2,	IDT_NT_BARUTBASE2},
858c2ecf20Sopenharmony_ci	  {IDT_NT_BARSETUP3,	IDT_NT_BARLIMIT3,
868c2ecf20Sopenharmony_ci	   IDT_NT_BARLTBASE3,	IDT_NT_BARUTBASE3},
878c2ecf20Sopenharmony_ci	  {IDT_NT_BARSETUP4,	IDT_NT_BARLIMIT4,
888c2ecf20Sopenharmony_ci	   IDT_NT_BARLTBASE4,	IDT_NT_BARUTBASE4},
898c2ecf20Sopenharmony_ci	  {IDT_NT_BARSETUP5,	IDT_NT_BARLIMIT5,
908c2ecf20Sopenharmony_ci	   IDT_NT_BARLTBASE5,	IDT_NT_BARUTBASE5} },
918c2ecf20Sopenharmony_ci	{ {IDT_NT_INMSG0,	IDT_NT_OUTMSG0,	IDT_NT_INMSGSRC0},
928c2ecf20Sopenharmony_ci	  {IDT_NT_INMSG1,	IDT_NT_OUTMSG1,	IDT_NT_INMSGSRC1},
938c2ecf20Sopenharmony_ci	  {IDT_NT_INMSG2,	IDT_NT_OUTMSG2,	IDT_NT_INMSGSRC2},
948c2ecf20Sopenharmony_ci	  {IDT_NT_INMSG3,	IDT_NT_OUTMSG3,	IDT_NT_INMSGSRC3} }
958c2ecf20Sopenharmony_ci};
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci/*
988c2ecf20Sopenharmony_ci * NT Endpoint ports data table with the corresponding pcie command, link
998c2ecf20Sopenharmony_ci * status, control and BAR-related registers
1008c2ecf20Sopenharmony_ci */
1018c2ecf20Sopenharmony_cistatic const struct idt_ntb_port portdata_tbl[IDT_MAX_NR_PORTS] = {
1028c2ecf20Sopenharmony_ci/*0*/	{ IDT_SW_NTP0_PCIECMDSTS,	IDT_SW_NTP0_PCIELCTLSTS,
1038c2ecf20Sopenharmony_ci	  IDT_SW_NTP0_NTCTL,
1048c2ecf20Sopenharmony_ci	  IDT_SW_SWPORT0CTL,		IDT_SW_SWPORT0STS,
1058c2ecf20Sopenharmony_ci	  { {IDT_SW_NTP0_BARSETUP0,	IDT_SW_NTP0_BARLIMIT0,
1068c2ecf20Sopenharmony_ci	     IDT_SW_NTP0_BARLTBASE0,	IDT_SW_NTP0_BARUTBASE0},
1078c2ecf20Sopenharmony_ci	    {IDT_SW_NTP0_BARSETUP1,	IDT_SW_NTP0_BARLIMIT1,
1088c2ecf20Sopenharmony_ci	     IDT_SW_NTP0_BARLTBASE1,	IDT_SW_NTP0_BARUTBASE1},
1098c2ecf20Sopenharmony_ci	    {IDT_SW_NTP0_BARSETUP2,	IDT_SW_NTP0_BARLIMIT2,
1108c2ecf20Sopenharmony_ci	     IDT_SW_NTP0_BARLTBASE2,	IDT_SW_NTP0_BARUTBASE2},
1118c2ecf20Sopenharmony_ci	    {IDT_SW_NTP0_BARSETUP3,	IDT_SW_NTP0_BARLIMIT3,
1128c2ecf20Sopenharmony_ci	     IDT_SW_NTP0_BARLTBASE3,	IDT_SW_NTP0_BARUTBASE3},
1138c2ecf20Sopenharmony_ci	    {IDT_SW_NTP0_BARSETUP4,	IDT_SW_NTP0_BARLIMIT4,
1148c2ecf20Sopenharmony_ci	     IDT_SW_NTP0_BARLTBASE4,	IDT_SW_NTP0_BARUTBASE4},
1158c2ecf20Sopenharmony_ci	    {IDT_SW_NTP0_BARSETUP5,	IDT_SW_NTP0_BARLIMIT5,
1168c2ecf20Sopenharmony_ci	     IDT_SW_NTP0_BARLTBASE5,	IDT_SW_NTP0_BARUTBASE5} } },
1178c2ecf20Sopenharmony_ci/*1*/	{0},
1188c2ecf20Sopenharmony_ci/*2*/	{ IDT_SW_NTP2_PCIECMDSTS,	IDT_SW_NTP2_PCIELCTLSTS,
1198c2ecf20Sopenharmony_ci	  IDT_SW_NTP2_NTCTL,
1208c2ecf20Sopenharmony_ci	  IDT_SW_SWPORT2CTL,		IDT_SW_SWPORT2STS,
1218c2ecf20Sopenharmony_ci	  { {IDT_SW_NTP2_BARSETUP0,	IDT_SW_NTP2_BARLIMIT0,
1228c2ecf20Sopenharmony_ci	     IDT_SW_NTP2_BARLTBASE0,	IDT_SW_NTP2_BARUTBASE0},
1238c2ecf20Sopenharmony_ci	    {IDT_SW_NTP2_BARSETUP1,	IDT_SW_NTP2_BARLIMIT1,
1248c2ecf20Sopenharmony_ci	     IDT_SW_NTP2_BARLTBASE1,	IDT_SW_NTP2_BARUTBASE1},
1258c2ecf20Sopenharmony_ci	    {IDT_SW_NTP2_BARSETUP2,	IDT_SW_NTP2_BARLIMIT2,
1268c2ecf20Sopenharmony_ci	     IDT_SW_NTP2_BARLTBASE2,	IDT_SW_NTP2_BARUTBASE2},
1278c2ecf20Sopenharmony_ci	    {IDT_SW_NTP2_BARSETUP3,	IDT_SW_NTP2_BARLIMIT3,
1288c2ecf20Sopenharmony_ci	     IDT_SW_NTP2_BARLTBASE3,	IDT_SW_NTP2_BARUTBASE3},
1298c2ecf20Sopenharmony_ci	    {IDT_SW_NTP2_BARSETUP4,	IDT_SW_NTP2_BARLIMIT4,
1308c2ecf20Sopenharmony_ci	     IDT_SW_NTP2_BARLTBASE4,	IDT_SW_NTP2_BARUTBASE4},
1318c2ecf20Sopenharmony_ci	    {IDT_SW_NTP2_BARSETUP5,	IDT_SW_NTP2_BARLIMIT5,
1328c2ecf20Sopenharmony_ci	     IDT_SW_NTP2_BARLTBASE5,	IDT_SW_NTP2_BARUTBASE5} } },
1338c2ecf20Sopenharmony_ci/*3*/	{0},
1348c2ecf20Sopenharmony_ci/*4*/	{ IDT_SW_NTP4_PCIECMDSTS,	IDT_SW_NTP4_PCIELCTLSTS,
1358c2ecf20Sopenharmony_ci	  IDT_SW_NTP4_NTCTL,
1368c2ecf20Sopenharmony_ci	  IDT_SW_SWPORT4CTL,		IDT_SW_SWPORT4STS,
1378c2ecf20Sopenharmony_ci	  { {IDT_SW_NTP4_BARSETUP0,	IDT_SW_NTP4_BARLIMIT0,
1388c2ecf20Sopenharmony_ci	     IDT_SW_NTP4_BARLTBASE0,	IDT_SW_NTP4_BARUTBASE0},
1398c2ecf20Sopenharmony_ci	    {IDT_SW_NTP4_BARSETUP1,	IDT_SW_NTP4_BARLIMIT1,
1408c2ecf20Sopenharmony_ci	     IDT_SW_NTP4_BARLTBASE1,	IDT_SW_NTP4_BARUTBASE1},
1418c2ecf20Sopenharmony_ci	    {IDT_SW_NTP4_BARSETUP2,	IDT_SW_NTP4_BARLIMIT2,
1428c2ecf20Sopenharmony_ci	     IDT_SW_NTP4_BARLTBASE2,	IDT_SW_NTP4_BARUTBASE2},
1438c2ecf20Sopenharmony_ci	    {IDT_SW_NTP4_BARSETUP3,	IDT_SW_NTP4_BARLIMIT3,
1448c2ecf20Sopenharmony_ci	     IDT_SW_NTP4_BARLTBASE3,	IDT_SW_NTP4_BARUTBASE3},
1458c2ecf20Sopenharmony_ci	    {IDT_SW_NTP4_BARSETUP4,	IDT_SW_NTP4_BARLIMIT4,
1468c2ecf20Sopenharmony_ci	     IDT_SW_NTP4_BARLTBASE4,	IDT_SW_NTP4_BARUTBASE4},
1478c2ecf20Sopenharmony_ci	    {IDT_SW_NTP4_BARSETUP5,	IDT_SW_NTP4_BARLIMIT5,
1488c2ecf20Sopenharmony_ci	     IDT_SW_NTP4_BARLTBASE5,	IDT_SW_NTP4_BARUTBASE5} } },
1498c2ecf20Sopenharmony_ci/*5*/	{0},
1508c2ecf20Sopenharmony_ci/*6*/	{ IDT_SW_NTP6_PCIECMDSTS,	IDT_SW_NTP6_PCIELCTLSTS,
1518c2ecf20Sopenharmony_ci	  IDT_SW_NTP6_NTCTL,
1528c2ecf20Sopenharmony_ci	  IDT_SW_SWPORT6CTL,		IDT_SW_SWPORT6STS,
1538c2ecf20Sopenharmony_ci	  { {IDT_SW_NTP6_BARSETUP0,	IDT_SW_NTP6_BARLIMIT0,
1548c2ecf20Sopenharmony_ci	     IDT_SW_NTP6_BARLTBASE0,	IDT_SW_NTP6_BARUTBASE0},
1558c2ecf20Sopenharmony_ci	    {IDT_SW_NTP6_BARSETUP1,	IDT_SW_NTP6_BARLIMIT1,
1568c2ecf20Sopenharmony_ci	     IDT_SW_NTP6_BARLTBASE1,	IDT_SW_NTP6_BARUTBASE1},
1578c2ecf20Sopenharmony_ci	    {IDT_SW_NTP6_BARSETUP2,	IDT_SW_NTP6_BARLIMIT2,
1588c2ecf20Sopenharmony_ci	     IDT_SW_NTP6_BARLTBASE2,	IDT_SW_NTP6_BARUTBASE2},
1598c2ecf20Sopenharmony_ci	    {IDT_SW_NTP6_BARSETUP3,	IDT_SW_NTP6_BARLIMIT3,
1608c2ecf20Sopenharmony_ci	     IDT_SW_NTP6_BARLTBASE3,	IDT_SW_NTP6_BARUTBASE3},
1618c2ecf20Sopenharmony_ci	    {IDT_SW_NTP6_BARSETUP4,	IDT_SW_NTP6_BARLIMIT4,
1628c2ecf20Sopenharmony_ci	     IDT_SW_NTP6_BARLTBASE4,	IDT_SW_NTP6_BARUTBASE4},
1638c2ecf20Sopenharmony_ci	    {IDT_SW_NTP6_BARSETUP5,	IDT_SW_NTP6_BARLIMIT5,
1648c2ecf20Sopenharmony_ci	     IDT_SW_NTP6_BARLTBASE5,	IDT_SW_NTP6_BARUTBASE5} } },
1658c2ecf20Sopenharmony_ci/*7*/	{0},
1668c2ecf20Sopenharmony_ci/*8*/	{ IDT_SW_NTP8_PCIECMDSTS,	IDT_SW_NTP8_PCIELCTLSTS,
1678c2ecf20Sopenharmony_ci	  IDT_SW_NTP8_NTCTL,
1688c2ecf20Sopenharmony_ci	  IDT_SW_SWPORT8CTL,		IDT_SW_SWPORT8STS,
1698c2ecf20Sopenharmony_ci	  { {IDT_SW_NTP8_BARSETUP0,	IDT_SW_NTP8_BARLIMIT0,
1708c2ecf20Sopenharmony_ci	     IDT_SW_NTP8_BARLTBASE0,	IDT_SW_NTP8_BARUTBASE0},
1718c2ecf20Sopenharmony_ci	    {IDT_SW_NTP8_BARSETUP1,	IDT_SW_NTP8_BARLIMIT1,
1728c2ecf20Sopenharmony_ci	     IDT_SW_NTP8_BARLTBASE1,	IDT_SW_NTP8_BARUTBASE1},
1738c2ecf20Sopenharmony_ci	    {IDT_SW_NTP8_BARSETUP2,	IDT_SW_NTP8_BARLIMIT2,
1748c2ecf20Sopenharmony_ci	     IDT_SW_NTP8_BARLTBASE2,	IDT_SW_NTP8_BARUTBASE2},
1758c2ecf20Sopenharmony_ci	    {IDT_SW_NTP8_BARSETUP3,	IDT_SW_NTP8_BARLIMIT3,
1768c2ecf20Sopenharmony_ci	     IDT_SW_NTP8_BARLTBASE3,	IDT_SW_NTP8_BARUTBASE3},
1778c2ecf20Sopenharmony_ci	    {IDT_SW_NTP8_BARSETUP4,	IDT_SW_NTP8_BARLIMIT4,
1788c2ecf20Sopenharmony_ci	     IDT_SW_NTP8_BARLTBASE4,	IDT_SW_NTP8_BARUTBASE4},
1798c2ecf20Sopenharmony_ci	    {IDT_SW_NTP8_BARSETUP5,	IDT_SW_NTP8_BARLIMIT5,
1808c2ecf20Sopenharmony_ci	     IDT_SW_NTP8_BARLTBASE5,	IDT_SW_NTP8_BARUTBASE5} } },
1818c2ecf20Sopenharmony_ci/*9*/	{0},
1828c2ecf20Sopenharmony_ci/*10*/	{0},
1838c2ecf20Sopenharmony_ci/*11*/	{0},
1848c2ecf20Sopenharmony_ci/*12*/	{ IDT_SW_NTP12_PCIECMDSTS,	IDT_SW_NTP12_PCIELCTLSTS,
1858c2ecf20Sopenharmony_ci	  IDT_SW_NTP12_NTCTL,
1868c2ecf20Sopenharmony_ci	  IDT_SW_SWPORT12CTL,		IDT_SW_SWPORT12STS,
1878c2ecf20Sopenharmony_ci	  { {IDT_SW_NTP12_BARSETUP0,	IDT_SW_NTP12_BARLIMIT0,
1888c2ecf20Sopenharmony_ci	     IDT_SW_NTP12_BARLTBASE0,	IDT_SW_NTP12_BARUTBASE0},
1898c2ecf20Sopenharmony_ci	    {IDT_SW_NTP12_BARSETUP1,	IDT_SW_NTP12_BARLIMIT1,
1908c2ecf20Sopenharmony_ci	     IDT_SW_NTP12_BARLTBASE1,	IDT_SW_NTP12_BARUTBASE1},
1918c2ecf20Sopenharmony_ci	    {IDT_SW_NTP12_BARSETUP2,	IDT_SW_NTP12_BARLIMIT2,
1928c2ecf20Sopenharmony_ci	     IDT_SW_NTP12_BARLTBASE2,	IDT_SW_NTP12_BARUTBASE2},
1938c2ecf20Sopenharmony_ci	    {IDT_SW_NTP12_BARSETUP3,	IDT_SW_NTP12_BARLIMIT3,
1948c2ecf20Sopenharmony_ci	     IDT_SW_NTP12_BARLTBASE3,	IDT_SW_NTP12_BARUTBASE3},
1958c2ecf20Sopenharmony_ci	    {IDT_SW_NTP12_BARSETUP4,	IDT_SW_NTP12_BARLIMIT4,
1968c2ecf20Sopenharmony_ci	     IDT_SW_NTP12_BARLTBASE4,	IDT_SW_NTP12_BARUTBASE4},
1978c2ecf20Sopenharmony_ci	    {IDT_SW_NTP12_BARSETUP5,	IDT_SW_NTP12_BARLIMIT5,
1988c2ecf20Sopenharmony_ci	     IDT_SW_NTP12_BARLTBASE5,	IDT_SW_NTP12_BARUTBASE5} } },
1998c2ecf20Sopenharmony_ci/*13*/	{0},
2008c2ecf20Sopenharmony_ci/*14*/	{0},
2018c2ecf20Sopenharmony_ci/*15*/	{0},
2028c2ecf20Sopenharmony_ci/*16*/	{ IDT_SW_NTP16_PCIECMDSTS,	IDT_SW_NTP16_PCIELCTLSTS,
2038c2ecf20Sopenharmony_ci	  IDT_SW_NTP16_NTCTL,
2048c2ecf20Sopenharmony_ci	  IDT_SW_SWPORT16CTL,		IDT_SW_SWPORT16STS,
2058c2ecf20Sopenharmony_ci	  { {IDT_SW_NTP16_BARSETUP0,	IDT_SW_NTP16_BARLIMIT0,
2068c2ecf20Sopenharmony_ci	     IDT_SW_NTP16_BARLTBASE0,	IDT_SW_NTP16_BARUTBASE0},
2078c2ecf20Sopenharmony_ci	    {IDT_SW_NTP16_BARSETUP1,	IDT_SW_NTP16_BARLIMIT1,
2088c2ecf20Sopenharmony_ci	     IDT_SW_NTP16_BARLTBASE1,	IDT_SW_NTP16_BARUTBASE1},
2098c2ecf20Sopenharmony_ci	    {IDT_SW_NTP16_BARSETUP2,	IDT_SW_NTP16_BARLIMIT2,
2108c2ecf20Sopenharmony_ci	     IDT_SW_NTP16_BARLTBASE2,	IDT_SW_NTP16_BARUTBASE2},
2118c2ecf20Sopenharmony_ci	    {IDT_SW_NTP16_BARSETUP3,	IDT_SW_NTP16_BARLIMIT3,
2128c2ecf20Sopenharmony_ci	     IDT_SW_NTP16_BARLTBASE3,	IDT_SW_NTP16_BARUTBASE3},
2138c2ecf20Sopenharmony_ci	    {IDT_SW_NTP16_BARSETUP4,	IDT_SW_NTP16_BARLIMIT4,
2148c2ecf20Sopenharmony_ci	     IDT_SW_NTP16_BARLTBASE4,	IDT_SW_NTP16_BARUTBASE4},
2158c2ecf20Sopenharmony_ci	    {IDT_SW_NTP16_BARSETUP5,	IDT_SW_NTP16_BARLIMIT5,
2168c2ecf20Sopenharmony_ci	     IDT_SW_NTP16_BARLTBASE5,	IDT_SW_NTP16_BARUTBASE5} } },
2178c2ecf20Sopenharmony_ci/*17*/	{0},
2188c2ecf20Sopenharmony_ci/*18*/	{0},
2198c2ecf20Sopenharmony_ci/*19*/	{0},
2208c2ecf20Sopenharmony_ci/*20*/	{ IDT_SW_NTP20_PCIECMDSTS,	IDT_SW_NTP20_PCIELCTLSTS,
2218c2ecf20Sopenharmony_ci	  IDT_SW_NTP20_NTCTL,
2228c2ecf20Sopenharmony_ci	  IDT_SW_SWPORT20CTL,		IDT_SW_SWPORT20STS,
2238c2ecf20Sopenharmony_ci	  { {IDT_SW_NTP20_BARSETUP0,	IDT_SW_NTP20_BARLIMIT0,
2248c2ecf20Sopenharmony_ci	     IDT_SW_NTP20_BARLTBASE0,	IDT_SW_NTP20_BARUTBASE0},
2258c2ecf20Sopenharmony_ci	    {IDT_SW_NTP20_BARSETUP1,	IDT_SW_NTP20_BARLIMIT1,
2268c2ecf20Sopenharmony_ci	     IDT_SW_NTP20_BARLTBASE1,	IDT_SW_NTP20_BARUTBASE1},
2278c2ecf20Sopenharmony_ci	    {IDT_SW_NTP20_BARSETUP2,	IDT_SW_NTP20_BARLIMIT2,
2288c2ecf20Sopenharmony_ci	     IDT_SW_NTP20_BARLTBASE2,	IDT_SW_NTP20_BARUTBASE2},
2298c2ecf20Sopenharmony_ci	    {IDT_SW_NTP20_BARSETUP3,	IDT_SW_NTP20_BARLIMIT3,
2308c2ecf20Sopenharmony_ci	     IDT_SW_NTP20_BARLTBASE3,	IDT_SW_NTP20_BARUTBASE3},
2318c2ecf20Sopenharmony_ci	    {IDT_SW_NTP20_BARSETUP4,	IDT_SW_NTP20_BARLIMIT4,
2328c2ecf20Sopenharmony_ci	     IDT_SW_NTP20_BARLTBASE4,	IDT_SW_NTP20_BARUTBASE4},
2338c2ecf20Sopenharmony_ci	    {IDT_SW_NTP20_BARSETUP5,	IDT_SW_NTP20_BARLIMIT5,
2348c2ecf20Sopenharmony_ci	     IDT_SW_NTP20_BARLTBASE5,	IDT_SW_NTP20_BARUTBASE5} } },
2358c2ecf20Sopenharmony_ci/*21*/	{0},
2368c2ecf20Sopenharmony_ci/*22*/	{0},
2378c2ecf20Sopenharmony_ci/*23*/	{0}
2388c2ecf20Sopenharmony_ci};
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci/*
2418c2ecf20Sopenharmony_ci * IDT PCIe-switch partitions table with the corresponding control, status
2428c2ecf20Sopenharmony_ci * and messages control registers
2438c2ecf20Sopenharmony_ci */
2448c2ecf20Sopenharmony_cistatic const struct idt_ntb_part partdata_tbl[IDT_MAX_NR_PARTS] = {
2458c2ecf20Sopenharmony_ci/*0*/	{ IDT_SW_SWPART0CTL,	IDT_SW_SWPART0STS,
2468c2ecf20Sopenharmony_ci	  {IDT_SW_SWP0MSGCTL0,	IDT_SW_SWP0MSGCTL1,
2478c2ecf20Sopenharmony_ci	   IDT_SW_SWP0MSGCTL2,	IDT_SW_SWP0MSGCTL3} },
2488c2ecf20Sopenharmony_ci/*1*/	{ IDT_SW_SWPART1CTL,	IDT_SW_SWPART1STS,
2498c2ecf20Sopenharmony_ci	  {IDT_SW_SWP1MSGCTL0,	IDT_SW_SWP1MSGCTL1,
2508c2ecf20Sopenharmony_ci	   IDT_SW_SWP1MSGCTL2,	IDT_SW_SWP1MSGCTL3} },
2518c2ecf20Sopenharmony_ci/*2*/	{ IDT_SW_SWPART2CTL,	IDT_SW_SWPART2STS,
2528c2ecf20Sopenharmony_ci	  {IDT_SW_SWP2MSGCTL0,	IDT_SW_SWP2MSGCTL1,
2538c2ecf20Sopenharmony_ci	   IDT_SW_SWP2MSGCTL2,	IDT_SW_SWP2MSGCTL3} },
2548c2ecf20Sopenharmony_ci/*3*/	{ IDT_SW_SWPART3CTL,	IDT_SW_SWPART3STS,
2558c2ecf20Sopenharmony_ci	  {IDT_SW_SWP3MSGCTL0,	IDT_SW_SWP3MSGCTL1,
2568c2ecf20Sopenharmony_ci	   IDT_SW_SWP3MSGCTL2,	IDT_SW_SWP3MSGCTL3} },
2578c2ecf20Sopenharmony_ci/*4*/	{ IDT_SW_SWPART4CTL,	IDT_SW_SWPART4STS,
2588c2ecf20Sopenharmony_ci	  {IDT_SW_SWP4MSGCTL0,	IDT_SW_SWP4MSGCTL1,
2598c2ecf20Sopenharmony_ci	   IDT_SW_SWP4MSGCTL2,	IDT_SW_SWP4MSGCTL3} },
2608c2ecf20Sopenharmony_ci/*5*/	{ IDT_SW_SWPART5CTL,	IDT_SW_SWPART5STS,
2618c2ecf20Sopenharmony_ci	  {IDT_SW_SWP5MSGCTL0,	IDT_SW_SWP5MSGCTL1,
2628c2ecf20Sopenharmony_ci	   IDT_SW_SWP5MSGCTL2,	IDT_SW_SWP5MSGCTL3} },
2638c2ecf20Sopenharmony_ci/*6*/	{ IDT_SW_SWPART6CTL,	IDT_SW_SWPART6STS,
2648c2ecf20Sopenharmony_ci	  {IDT_SW_SWP6MSGCTL0,	IDT_SW_SWP6MSGCTL1,
2658c2ecf20Sopenharmony_ci	   IDT_SW_SWP6MSGCTL2,	IDT_SW_SWP6MSGCTL3} },
2668c2ecf20Sopenharmony_ci/*7*/	{ IDT_SW_SWPART7CTL,	IDT_SW_SWPART7STS,
2678c2ecf20Sopenharmony_ci	  {IDT_SW_SWP7MSGCTL0,	IDT_SW_SWP7MSGCTL1,
2688c2ecf20Sopenharmony_ci	   IDT_SW_SWP7MSGCTL2,	IDT_SW_SWP7MSGCTL3} }
2698c2ecf20Sopenharmony_ci};
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci/*
2728c2ecf20Sopenharmony_ci * DebugFS directory to place the driver debug file
2738c2ecf20Sopenharmony_ci */
2748c2ecf20Sopenharmony_cistatic struct dentry *dbgfs_topdir;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci/*=============================================================================
2778c2ecf20Sopenharmony_ci *                1. IDT PCIe-switch registers IO-functions
2788c2ecf20Sopenharmony_ci *
2798c2ecf20Sopenharmony_ci *    Beside ordinary configuration space registers IDT PCIe-switch expose
2808c2ecf20Sopenharmony_ci * global configuration registers, which are used to determine state of other
2818c2ecf20Sopenharmony_ci * device ports as well as being notified of some switch-related events.
2828c2ecf20Sopenharmony_ci * Additionally all the configuration space registers of all the IDT
2838c2ecf20Sopenharmony_ci * PCIe-switch functions are mapped to the Global Address space, so each
2848c2ecf20Sopenharmony_ci * function can determine a configuration of any other PCI-function.
2858c2ecf20Sopenharmony_ci *    Functions declared in this chapter are created to encapsulate access
2868c2ecf20Sopenharmony_ci * to configuration and global registers, so the driver code just need to
2878c2ecf20Sopenharmony_ci * provide IDT NTB hardware descriptor and a register address.
2888c2ecf20Sopenharmony_ci *=============================================================================
2898c2ecf20Sopenharmony_ci */
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci/*
2928c2ecf20Sopenharmony_ci * idt_nt_write() - PCI configuration space registers write method
2938c2ecf20Sopenharmony_ci * @ndev:	IDT NTB hardware driver descriptor
2948c2ecf20Sopenharmony_ci * @reg:	Register to write data to
2958c2ecf20Sopenharmony_ci * @data:	Value to write to the register
2968c2ecf20Sopenharmony_ci *
2978c2ecf20Sopenharmony_ci * IDT PCIe-switch registers are all Little endian.
2988c2ecf20Sopenharmony_ci */
2998c2ecf20Sopenharmony_cistatic void idt_nt_write(struct idt_ntb_dev *ndev,
3008c2ecf20Sopenharmony_ci			 const unsigned int reg, const u32 data)
3018c2ecf20Sopenharmony_ci{
3028c2ecf20Sopenharmony_ci	/*
3038c2ecf20Sopenharmony_ci	 * It's obvious bug to request a register exceeding the maximum possible
3048c2ecf20Sopenharmony_ci	 * value as well as to have it unaligned.
3058c2ecf20Sopenharmony_ci	 */
3068c2ecf20Sopenharmony_ci	if (WARN_ON(reg > IDT_REG_PCI_MAX || !IS_ALIGNED(reg, IDT_REG_ALIGN)))
3078c2ecf20Sopenharmony_ci		return;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	/* Just write the value to the specified register */
3108c2ecf20Sopenharmony_ci	iowrite32(data, ndev->cfgspc + (ptrdiff_t)reg);
3118c2ecf20Sopenharmony_ci}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci/*
3148c2ecf20Sopenharmony_ci * idt_nt_read() - PCI configuration space registers read method
3158c2ecf20Sopenharmony_ci * @ndev:	IDT NTB hardware driver descriptor
3168c2ecf20Sopenharmony_ci * @reg:	Register to write data to
3178c2ecf20Sopenharmony_ci *
3188c2ecf20Sopenharmony_ci * IDT PCIe-switch Global configuration registers are all Little endian.
3198c2ecf20Sopenharmony_ci *
3208c2ecf20Sopenharmony_ci * Return: register value
3218c2ecf20Sopenharmony_ci */
3228c2ecf20Sopenharmony_cistatic u32 idt_nt_read(struct idt_ntb_dev *ndev, const unsigned int reg)
3238c2ecf20Sopenharmony_ci{
3248c2ecf20Sopenharmony_ci	/*
3258c2ecf20Sopenharmony_ci	 * It's obvious bug to request a register exceeding the maximum possible
3268c2ecf20Sopenharmony_ci	 * value as well as to have it unaligned.
3278c2ecf20Sopenharmony_ci	 */
3288c2ecf20Sopenharmony_ci	if (WARN_ON(reg > IDT_REG_PCI_MAX || !IS_ALIGNED(reg, IDT_REG_ALIGN)))
3298c2ecf20Sopenharmony_ci		return ~0;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	/* Just read the value from the specified register */
3328c2ecf20Sopenharmony_ci	return ioread32(ndev->cfgspc + (ptrdiff_t)reg);
3338c2ecf20Sopenharmony_ci}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci/*
3368c2ecf20Sopenharmony_ci * idt_sw_write() - Global registers write method
3378c2ecf20Sopenharmony_ci * @ndev:	IDT NTB hardware driver descriptor
3388c2ecf20Sopenharmony_ci * @reg:	Register to write data to
3398c2ecf20Sopenharmony_ci * @data:	Value to write to the register
3408c2ecf20Sopenharmony_ci *
3418c2ecf20Sopenharmony_ci * IDT PCIe-switch Global configuration registers are all Little endian.
3428c2ecf20Sopenharmony_ci */
3438c2ecf20Sopenharmony_cistatic void idt_sw_write(struct idt_ntb_dev *ndev,
3448c2ecf20Sopenharmony_ci			 const unsigned int reg, const u32 data)
3458c2ecf20Sopenharmony_ci{
3468c2ecf20Sopenharmony_ci	unsigned long irqflags;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	/*
3498c2ecf20Sopenharmony_ci	 * It's obvious bug to request a register exceeding the maximum possible
3508c2ecf20Sopenharmony_ci	 * value as well as to have it unaligned.
3518c2ecf20Sopenharmony_ci	 */
3528c2ecf20Sopenharmony_ci	if (WARN_ON(reg > IDT_REG_SW_MAX || !IS_ALIGNED(reg, IDT_REG_ALIGN)))
3538c2ecf20Sopenharmony_ci		return;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	/* Lock GASA registers operations */
3568c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ndev->gasa_lock, irqflags);
3578c2ecf20Sopenharmony_ci	/* Set the global register address */
3588c2ecf20Sopenharmony_ci	iowrite32((u32)reg, ndev->cfgspc + (ptrdiff_t)IDT_NT_GASAADDR);
3598c2ecf20Sopenharmony_ci	/* Put the new value of the register */
3608c2ecf20Sopenharmony_ci	iowrite32(data, ndev->cfgspc + (ptrdiff_t)IDT_NT_GASADATA);
3618c2ecf20Sopenharmony_ci	/* Unlock GASA registers operations */
3628c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ndev->gasa_lock, irqflags);
3638c2ecf20Sopenharmony_ci}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci/*
3668c2ecf20Sopenharmony_ci * idt_sw_read() - Global registers read method
3678c2ecf20Sopenharmony_ci * @ndev:	IDT NTB hardware driver descriptor
3688c2ecf20Sopenharmony_ci * @reg:	Register to write data to
3698c2ecf20Sopenharmony_ci *
3708c2ecf20Sopenharmony_ci * IDT PCIe-switch Global configuration registers are all Little endian.
3718c2ecf20Sopenharmony_ci *
3728c2ecf20Sopenharmony_ci * Return: register value
3738c2ecf20Sopenharmony_ci */
3748c2ecf20Sopenharmony_cistatic u32 idt_sw_read(struct idt_ntb_dev *ndev, const unsigned int reg)
3758c2ecf20Sopenharmony_ci{
3768c2ecf20Sopenharmony_ci	unsigned long irqflags;
3778c2ecf20Sopenharmony_ci	u32 data;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	/*
3808c2ecf20Sopenharmony_ci	 * It's obvious bug to request a register exceeding the maximum possible
3818c2ecf20Sopenharmony_ci	 * value as well as to have it unaligned.
3828c2ecf20Sopenharmony_ci	 */
3838c2ecf20Sopenharmony_ci	if (WARN_ON(reg > IDT_REG_SW_MAX || !IS_ALIGNED(reg, IDT_REG_ALIGN)))
3848c2ecf20Sopenharmony_ci		return ~0;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	/* Lock GASA registers operations */
3878c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ndev->gasa_lock, irqflags);
3888c2ecf20Sopenharmony_ci	/* Set the global register address */
3898c2ecf20Sopenharmony_ci	iowrite32((u32)reg, ndev->cfgspc + (ptrdiff_t)IDT_NT_GASAADDR);
3908c2ecf20Sopenharmony_ci	/* Get the data of the register (read ops acts as MMIO barrier) */
3918c2ecf20Sopenharmony_ci	data = ioread32(ndev->cfgspc + (ptrdiff_t)IDT_NT_GASADATA);
3928c2ecf20Sopenharmony_ci	/* Unlock GASA registers operations */
3938c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ndev->gasa_lock, irqflags);
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	return data;
3968c2ecf20Sopenharmony_ci}
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci/*
3998c2ecf20Sopenharmony_ci * idt_reg_set_bits() - set bits of a passed register
4008c2ecf20Sopenharmony_ci * @ndev:	IDT NTB hardware driver descriptor
4018c2ecf20Sopenharmony_ci * @reg:	Register to change bits of
4028c2ecf20Sopenharmony_ci * @reg_lock:	Register access spin lock
4038c2ecf20Sopenharmony_ci * @valid_mask:	Mask of valid bits
4048c2ecf20Sopenharmony_ci * @set_bits:	Bitmask to set
4058c2ecf20Sopenharmony_ci *
4068c2ecf20Sopenharmony_ci * Helper method to check whether a passed bitfield is valid and set
4078c2ecf20Sopenharmony_ci * corresponding bits of a register.
4088c2ecf20Sopenharmony_ci *
4098c2ecf20Sopenharmony_ci * WARNING! Make sure the passed register isn't accessed over plane
4108c2ecf20Sopenharmony_ci * idt_nt_write() method (read method is ok to be used concurrently).
4118c2ecf20Sopenharmony_ci *
4128c2ecf20Sopenharmony_ci * Return: zero on success, negative error on invalid bitmask.
4138c2ecf20Sopenharmony_ci */
4148c2ecf20Sopenharmony_cistatic inline int idt_reg_set_bits(struct idt_ntb_dev *ndev, unsigned int reg,
4158c2ecf20Sopenharmony_ci				   spinlock_t *reg_lock,
4168c2ecf20Sopenharmony_ci				   u64 valid_mask, u64 set_bits)
4178c2ecf20Sopenharmony_ci{
4188c2ecf20Sopenharmony_ci	unsigned long irqflags;
4198c2ecf20Sopenharmony_ci	u32 data;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	if (set_bits & ~(u64)valid_mask)
4228c2ecf20Sopenharmony_ci		return -EINVAL;
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	/* Lock access to the register unless the change is written back */
4258c2ecf20Sopenharmony_ci	spin_lock_irqsave(reg_lock, irqflags);
4268c2ecf20Sopenharmony_ci	data = idt_nt_read(ndev, reg) | (u32)set_bits;
4278c2ecf20Sopenharmony_ci	idt_nt_write(ndev, reg, data);
4288c2ecf20Sopenharmony_ci	/* Unlock the register */
4298c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(reg_lock, irqflags);
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	return 0;
4328c2ecf20Sopenharmony_ci}
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci/*
4358c2ecf20Sopenharmony_ci * idt_reg_clear_bits() - clear bits of a passed register
4368c2ecf20Sopenharmony_ci * @ndev:	IDT NTB hardware driver descriptor
4378c2ecf20Sopenharmony_ci * @reg:	Register to change bits of
4388c2ecf20Sopenharmony_ci * @reg_lock:	Register access spin lock
4398c2ecf20Sopenharmony_ci * @set_bits:	Bitmask to clear
4408c2ecf20Sopenharmony_ci *
4418c2ecf20Sopenharmony_ci * Helper method to check whether a passed bitfield is valid and clear
4428c2ecf20Sopenharmony_ci * corresponding bits of a register.
4438c2ecf20Sopenharmony_ci *
4448c2ecf20Sopenharmony_ci * NOTE! Invalid bits are always considered cleared so it's not an error
4458c2ecf20Sopenharmony_ci * to clear them over.
4468c2ecf20Sopenharmony_ci *
4478c2ecf20Sopenharmony_ci * WARNING! Make sure the passed register isn't accessed over plane
4488c2ecf20Sopenharmony_ci * idt_nt_write() method (read method is ok to use concurrently).
4498c2ecf20Sopenharmony_ci */
4508c2ecf20Sopenharmony_cistatic inline void idt_reg_clear_bits(struct idt_ntb_dev *ndev,
4518c2ecf20Sopenharmony_ci				     unsigned int reg, spinlock_t *reg_lock,
4528c2ecf20Sopenharmony_ci				     u64 clear_bits)
4538c2ecf20Sopenharmony_ci{
4548c2ecf20Sopenharmony_ci	unsigned long irqflags;
4558c2ecf20Sopenharmony_ci	u32 data;
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	/* Lock access to the register unless the change is written back */
4588c2ecf20Sopenharmony_ci	spin_lock_irqsave(reg_lock, irqflags);
4598c2ecf20Sopenharmony_ci	data = idt_nt_read(ndev, reg) & ~(u32)clear_bits;
4608c2ecf20Sopenharmony_ci	idt_nt_write(ndev, reg, data);
4618c2ecf20Sopenharmony_ci	/* Unlock the register */
4628c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(reg_lock, irqflags);
4638c2ecf20Sopenharmony_ci}
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci/*===========================================================================
4668c2ecf20Sopenharmony_ci *                           2. Ports operations
4678c2ecf20Sopenharmony_ci *
4688c2ecf20Sopenharmony_ci *    IDT PCIe-switches can have from 3 up to 8 ports with possible
4698c2ecf20Sopenharmony_ci * NT-functions enabled. So all the possible ports need to be scanned looking
4708c2ecf20Sopenharmony_ci * for NTB activated. NTB API will have enumerated only the ports with NTB.
4718c2ecf20Sopenharmony_ci *===========================================================================
4728c2ecf20Sopenharmony_ci */
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci/*
4758c2ecf20Sopenharmony_ci * idt_scan_ports() - scan IDT PCIe-switch ports collecting info in the tables
4768c2ecf20Sopenharmony_ci * @ndev:	Pointer to the PCI device descriptor
4778c2ecf20Sopenharmony_ci *
4788c2ecf20Sopenharmony_ci * Return: zero on success, otherwise a negative error number.
4798c2ecf20Sopenharmony_ci */
4808c2ecf20Sopenharmony_cistatic int idt_scan_ports(struct idt_ntb_dev *ndev)
4818c2ecf20Sopenharmony_ci{
4828c2ecf20Sopenharmony_ci	unsigned char pidx, port, part;
4838c2ecf20Sopenharmony_ci	u32 data, portsts, partsts;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	/* Retrieve the local port number */
4868c2ecf20Sopenharmony_ci	data = idt_nt_read(ndev, IDT_NT_PCIELCAP);
4878c2ecf20Sopenharmony_ci	ndev->port = GET_FIELD(PCIELCAP_PORTNUM, data);
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	/* Retrieve the local partition number */
4908c2ecf20Sopenharmony_ci	portsts = idt_sw_read(ndev, portdata_tbl[ndev->port].sts);
4918c2ecf20Sopenharmony_ci	ndev->part = GET_FIELD(SWPORTxSTS_SWPART, portsts);
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	/* Initialize port/partition -> index tables with invalid values */
4948c2ecf20Sopenharmony_ci	memset(ndev->port_idx_map, -EINVAL, sizeof(ndev->port_idx_map));
4958c2ecf20Sopenharmony_ci	memset(ndev->part_idx_map, -EINVAL, sizeof(ndev->part_idx_map));
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	/*
4988c2ecf20Sopenharmony_ci	 * Walk over all the possible ports checking whether any of them has
4998c2ecf20Sopenharmony_ci	 * NT-function activated
5008c2ecf20Sopenharmony_ci	 */
5018c2ecf20Sopenharmony_ci	ndev->peer_cnt = 0;
5028c2ecf20Sopenharmony_ci	for (pidx = 0; pidx < ndev->swcfg->port_cnt; pidx++) {
5038c2ecf20Sopenharmony_ci		port = ndev->swcfg->ports[pidx];
5048c2ecf20Sopenharmony_ci		/* Skip local port */
5058c2ecf20Sopenharmony_ci		if (port == ndev->port)
5068c2ecf20Sopenharmony_ci			continue;
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci		/* Read the port status register to get it partition */
5098c2ecf20Sopenharmony_ci		portsts = idt_sw_read(ndev, portdata_tbl[port].sts);
5108c2ecf20Sopenharmony_ci		part = GET_FIELD(SWPORTxSTS_SWPART, portsts);
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci		/* Retrieve the partition status */
5138c2ecf20Sopenharmony_ci		partsts = idt_sw_read(ndev, partdata_tbl[part].sts);
5148c2ecf20Sopenharmony_ci		/* Check if partition state is active and port has NTB */
5158c2ecf20Sopenharmony_ci		if (IS_FLD_SET(SWPARTxSTS_STATE, partsts, ACT) &&
5168c2ecf20Sopenharmony_ci		    (IS_FLD_SET(SWPORTxSTS_MODE, portsts, NT) ||
5178c2ecf20Sopenharmony_ci		     IS_FLD_SET(SWPORTxSTS_MODE, portsts, USNT) ||
5188c2ecf20Sopenharmony_ci		     IS_FLD_SET(SWPORTxSTS_MODE, portsts, USNTDMA) ||
5198c2ecf20Sopenharmony_ci		     IS_FLD_SET(SWPORTxSTS_MODE, portsts, NTDMA))) {
5208c2ecf20Sopenharmony_ci			/* Save the port and partition numbers */
5218c2ecf20Sopenharmony_ci			ndev->peers[ndev->peer_cnt].port = port;
5228c2ecf20Sopenharmony_ci			ndev->peers[ndev->peer_cnt].part = part;
5238c2ecf20Sopenharmony_ci			/* Fill in the port/partition -> index tables */
5248c2ecf20Sopenharmony_ci			ndev->port_idx_map[port] = ndev->peer_cnt;
5258c2ecf20Sopenharmony_ci			ndev->part_idx_map[part] = ndev->peer_cnt;
5268c2ecf20Sopenharmony_ci			ndev->peer_cnt++;
5278c2ecf20Sopenharmony_ci		}
5288c2ecf20Sopenharmony_ci	}
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	dev_dbg(&ndev->ntb.pdev->dev, "Local port: %hhu, num of peers: %hhu\n",
5318c2ecf20Sopenharmony_ci		ndev->port, ndev->peer_cnt);
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	/* It's useless to have this driver loaded if there is no any peer */
5348c2ecf20Sopenharmony_ci	if (ndev->peer_cnt == 0) {
5358c2ecf20Sopenharmony_ci		dev_warn(&ndev->ntb.pdev->dev, "No active peer found\n");
5368c2ecf20Sopenharmony_ci		return -ENODEV;
5378c2ecf20Sopenharmony_ci	}
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	return 0;
5408c2ecf20Sopenharmony_ci}
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci/*
5438c2ecf20Sopenharmony_ci * idt_ntb_port_number() - get the local port number
5448c2ecf20Sopenharmony_ci * @ntb:	NTB device context.
5458c2ecf20Sopenharmony_ci *
5468c2ecf20Sopenharmony_ci * Return: the local port number
5478c2ecf20Sopenharmony_ci */
5488c2ecf20Sopenharmony_cistatic int idt_ntb_port_number(struct ntb_dev *ntb)
5498c2ecf20Sopenharmony_ci{
5508c2ecf20Sopenharmony_ci	struct idt_ntb_dev *ndev = to_ndev_ntb(ntb);
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	return ndev->port;
5538c2ecf20Sopenharmony_ci}
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci/*
5568c2ecf20Sopenharmony_ci * idt_ntb_peer_port_count() - get the number of peer ports
5578c2ecf20Sopenharmony_ci * @ntb:	NTB device context.
5588c2ecf20Sopenharmony_ci *
5598c2ecf20Sopenharmony_ci * Return the count of detected peer NT-functions.
5608c2ecf20Sopenharmony_ci *
5618c2ecf20Sopenharmony_ci * Return: number of peer ports
5628c2ecf20Sopenharmony_ci */
5638c2ecf20Sopenharmony_cistatic int idt_ntb_peer_port_count(struct ntb_dev *ntb)
5648c2ecf20Sopenharmony_ci{
5658c2ecf20Sopenharmony_ci	struct idt_ntb_dev *ndev = to_ndev_ntb(ntb);
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	return ndev->peer_cnt;
5688c2ecf20Sopenharmony_ci}
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci/*
5718c2ecf20Sopenharmony_ci * idt_ntb_peer_port_number() - get peer port by given index
5728c2ecf20Sopenharmony_ci * @ntb:	NTB device context.
5738c2ecf20Sopenharmony_ci * @pidx:	Peer port index.
5748c2ecf20Sopenharmony_ci *
5758c2ecf20Sopenharmony_ci * Return: peer port or negative error
5768c2ecf20Sopenharmony_ci */
5778c2ecf20Sopenharmony_cistatic int idt_ntb_peer_port_number(struct ntb_dev *ntb, int pidx)
5788c2ecf20Sopenharmony_ci{
5798c2ecf20Sopenharmony_ci	struct idt_ntb_dev *ndev = to_ndev_ntb(ntb);
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	if (pidx < 0 || ndev->peer_cnt <= pidx)
5828c2ecf20Sopenharmony_ci		return -EINVAL;
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	/* Return the detected NT-function port number */
5858c2ecf20Sopenharmony_ci	return ndev->peers[pidx].port;
5868c2ecf20Sopenharmony_ci}
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci/*
5898c2ecf20Sopenharmony_ci * idt_ntb_peer_port_idx() - get peer port index by given port number
5908c2ecf20Sopenharmony_ci * @ntb:	NTB device context.
5918c2ecf20Sopenharmony_ci * @port:	Peer port number.
5928c2ecf20Sopenharmony_ci *
5938c2ecf20Sopenharmony_ci * Internal port -> index table is pre-initialized with -EINVAL values,
5948c2ecf20Sopenharmony_ci * so we just need to return it value
5958c2ecf20Sopenharmony_ci *
5968c2ecf20Sopenharmony_ci * Return: peer NT-function port index or negative error
5978c2ecf20Sopenharmony_ci */
5988c2ecf20Sopenharmony_cistatic int idt_ntb_peer_port_idx(struct ntb_dev *ntb, int port)
5998c2ecf20Sopenharmony_ci{
6008c2ecf20Sopenharmony_ci	struct idt_ntb_dev *ndev = to_ndev_ntb(ntb);
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	if (port < 0 || IDT_MAX_NR_PORTS <= port)
6038c2ecf20Sopenharmony_ci		return -EINVAL;
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	return ndev->port_idx_map[port];
6068c2ecf20Sopenharmony_ci}
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci/*===========================================================================
6098c2ecf20Sopenharmony_ci *                         3. Link status operations
6108c2ecf20Sopenharmony_ci *    There is no any ready-to-use method to have peer ports notified if NTB
6118c2ecf20Sopenharmony_ci * link is set up or got down. Instead global signal can be used instead.
6128c2ecf20Sopenharmony_ci * In case if any one of ports changes local NTB link state, it sends
6138c2ecf20Sopenharmony_ci * global signal and clears corresponding global state bit. Then all the ports
6148c2ecf20Sopenharmony_ci * receive a notification of that, so to make client driver being aware of
6158c2ecf20Sopenharmony_ci * possible NTB link change.
6168c2ecf20Sopenharmony_ci *    Additionally each of active NT-functions is subscribed to PCIe-link
6178c2ecf20Sopenharmony_ci * state changes of peer ports.
6188c2ecf20Sopenharmony_ci *===========================================================================
6198c2ecf20Sopenharmony_ci */
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_cistatic void idt_ntb_local_link_disable(struct idt_ntb_dev *ndev);
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci/*
6248c2ecf20Sopenharmony_ci * idt_init_link() - Initialize NTB link state notification subsystem
6258c2ecf20Sopenharmony_ci * @ndev:	IDT NTB hardware driver descriptor
6268c2ecf20Sopenharmony_ci *
6278c2ecf20Sopenharmony_ci * Function performs the basic initialization of some global registers
6288c2ecf20Sopenharmony_ci * needed to enable IRQ-based notifications of PCIe Link Up/Down and
6298c2ecf20Sopenharmony_ci * Global Signal events.
6308c2ecf20Sopenharmony_ci * NOTE Since it's not possible to determine when all the NTB peer drivers are
6318c2ecf20Sopenharmony_ci * unloaded as well as have those registers accessed concurrently, we must
6328c2ecf20Sopenharmony_ci * preinitialize them with the same value and leave it uncleared on local
6338c2ecf20Sopenharmony_ci * driver unload.
6348c2ecf20Sopenharmony_ci */
6358c2ecf20Sopenharmony_cistatic void idt_init_link(struct idt_ntb_dev *ndev)
6368c2ecf20Sopenharmony_ci{
6378c2ecf20Sopenharmony_ci	u32 part_mask, port_mask, se_mask;
6388c2ecf20Sopenharmony_ci	unsigned char pidx;
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	/* Initialize spin locker of Mapping Table access registers */
6418c2ecf20Sopenharmony_ci	spin_lock_init(&ndev->mtbl_lock);
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	/* Walk over all detected peers collecting port and partition masks */
6448c2ecf20Sopenharmony_ci	port_mask = ~BIT(ndev->port);
6458c2ecf20Sopenharmony_ci	part_mask = ~BIT(ndev->part);
6468c2ecf20Sopenharmony_ci	for (pidx = 0; pidx < ndev->peer_cnt; pidx++) {
6478c2ecf20Sopenharmony_ci		port_mask &= ~BIT(ndev->peers[pidx].port);
6488c2ecf20Sopenharmony_ci		part_mask &= ~BIT(ndev->peers[pidx].part);
6498c2ecf20Sopenharmony_ci	}
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	/* Clean the Link Up/Down and GLobal Signal status registers */
6528c2ecf20Sopenharmony_ci	idt_sw_write(ndev, IDT_SW_SELINKUPSTS, (u32)-1);
6538c2ecf20Sopenharmony_ci	idt_sw_write(ndev, IDT_SW_SELINKDNSTS, (u32)-1);
6548c2ecf20Sopenharmony_ci	idt_sw_write(ndev, IDT_SW_SEGSIGSTS, (u32)-1);
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	/* Unmask NT-activated partitions to receive Global Switch events */
6578c2ecf20Sopenharmony_ci	idt_sw_write(ndev, IDT_SW_SEPMSK, part_mask);
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci	/* Enable PCIe Link Up events of NT-activated ports */
6608c2ecf20Sopenharmony_ci	idt_sw_write(ndev, IDT_SW_SELINKUPMSK, port_mask);
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	/* Enable PCIe Link Down events of NT-activated ports */
6638c2ecf20Sopenharmony_ci	idt_sw_write(ndev, IDT_SW_SELINKDNMSK, port_mask);
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	/* Unmask NT-activated partitions to receive Global Signal events */
6668c2ecf20Sopenharmony_ci	idt_sw_write(ndev, IDT_SW_SEGSIGMSK, part_mask);
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	/* Unmask Link Up/Down and Global Switch Events */
6698c2ecf20Sopenharmony_ci	se_mask = ~(IDT_SEMSK_LINKUP | IDT_SEMSK_LINKDN | IDT_SEMSK_GSIGNAL);
6708c2ecf20Sopenharmony_ci	idt_sw_write(ndev, IDT_SW_SEMSK, se_mask);
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	dev_dbg(&ndev->ntb.pdev->dev, "NTB link status events initialized");
6738c2ecf20Sopenharmony_ci}
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci/*
6768c2ecf20Sopenharmony_ci * idt_deinit_link() - deinitialize link subsystem
6778c2ecf20Sopenharmony_ci * @ndev:	IDT NTB hardware driver descriptor
6788c2ecf20Sopenharmony_ci *
6798c2ecf20Sopenharmony_ci * Just disable the link back.
6808c2ecf20Sopenharmony_ci */
6818c2ecf20Sopenharmony_cistatic void idt_deinit_link(struct idt_ntb_dev *ndev)
6828c2ecf20Sopenharmony_ci{
6838c2ecf20Sopenharmony_ci	/* Disable the link */
6848c2ecf20Sopenharmony_ci	idt_ntb_local_link_disable(ndev);
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	dev_dbg(&ndev->ntb.pdev->dev, "NTB link status events deinitialized");
6878c2ecf20Sopenharmony_ci}
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci/*
6908c2ecf20Sopenharmony_ci * idt_se_isr() - switch events ISR
6918c2ecf20Sopenharmony_ci * @ndev:	IDT NTB hardware driver descriptor
6928c2ecf20Sopenharmony_ci * @ntint_sts:	NT-function interrupt status
6938c2ecf20Sopenharmony_ci *
6948c2ecf20Sopenharmony_ci * This driver doesn't support IDT PCIe-switch dynamic reconfigurations,
6958c2ecf20Sopenharmony_ci * Failover capability, etc, so switch events are utilized to notify of
6968c2ecf20Sopenharmony_ci * PCIe and NTB link events.
6978c2ecf20Sopenharmony_ci * The method is called from PCIe ISR bottom-half routine.
6988c2ecf20Sopenharmony_ci */
6998c2ecf20Sopenharmony_cistatic void idt_se_isr(struct idt_ntb_dev *ndev, u32 ntint_sts)
7008c2ecf20Sopenharmony_ci{
7018c2ecf20Sopenharmony_ci	u32 sests;
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci	/* Read Switch Events status */
7048c2ecf20Sopenharmony_ci	sests = idt_sw_read(ndev, IDT_SW_SESTS);
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci	/* Clean the Link Up/Down and Global Signal status registers */
7078c2ecf20Sopenharmony_ci	idt_sw_write(ndev, IDT_SW_SELINKUPSTS, (u32)-1);
7088c2ecf20Sopenharmony_ci	idt_sw_write(ndev, IDT_SW_SELINKDNSTS, (u32)-1);
7098c2ecf20Sopenharmony_ci	idt_sw_write(ndev, IDT_SW_SEGSIGSTS, (u32)-1);
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	/* Clean the corresponding interrupt bit */
7128c2ecf20Sopenharmony_ci	idt_nt_write(ndev, IDT_NT_NTINTSTS, IDT_NTINTSTS_SEVENT);
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci	dev_dbg(&ndev->ntb.pdev->dev, "SE IRQ detected %#08x (SESTS %#08x)",
7158c2ecf20Sopenharmony_ci			  ntint_sts, sests);
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	/* Notify the client driver of possible link state change */
7188c2ecf20Sopenharmony_ci	ntb_link_event(&ndev->ntb);
7198c2ecf20Sopenharmony_ci}
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci/*
7228c2ecf20Sopenharmony_ci * idt_ntb_local_link_enable() - enable the local NTB link.
7238c2ecf20Sopenharmony_ci * @ndev:	IDT NTB hardware driver descriptor
7248c2ecf20Sopenharmony_ci *
7258c2ecf20Sopenharmony_ci * In order to enable the NTB link we need:
7268c2ecf20Sopenharmony_ci * - enable Completion TLPs translation
7278c2ecf20Sopenharmony_ci * - initialize mapping table to enable the Request ID translation
7288c2ecf20Sopenharmony_ci * - notify peers of NTB link state change
7298c2ecf20Sopenharmony_ci */
7308c2ecf20Sopenharmony_cistatic void idt_ntb_local_link_enable(struct idt_ntb_dev *ndev)
7318c2ecf20Sopenharmony_ci{
7328c2ecf20Sopenharmony_ci	u32 reqid, mtbldata = 0;
7338c2ecf20Sopenharmony_ci	unsigned long irqflags;
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci	/* Enable the ID protection and Completion TLPs translation */
7368c2ecf20Sopenharmony_ci	idt_nt_write(ndev, IDT_NT_NTCTL, IDT_NTCTL_CPEN);
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci	/* Retrieve the current Requester ID (Bus:Device:Function) */
7398c2ecf20Sopenharmony_ci	reqid = idt_nt_read(ndev, IDT_NT_REQIDCAP);
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	/*
7428c2ecf20Sopenharmony_ci	 * Set the corresponding NT Mapping table entry of port partition index
7438c2ecf20Sopenharmony_ci	 * with the data to perform the Request ID translation
7448c2ecf20Sopenharmony_ci	 */
7458c2ecf20Sopenharmony_ci	mtbldata = SET_FIELD(NTMTBLDATA_REQID, 0, reqid) |
7468c2ecf20Sopenharmony_ci		   SET_FIELD(NTMTBLDATA_PART, 0, ndev->part) |
7478c2ecf20Sopenharmony_ci		   IDT_NTMTBLDATA_VALID;
7488c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ndev->mtbl_lock, irqflags);
7498c2ecf20Sopenharmony_ci	idt_nt_write(ndev, IDT_NT_NTMTBLADDR, ndev->part);
7508c2ecf20Sopenharmony_ci	idt_nt_write(ndev, IDT_NT_NTMTBLDATA, mtbldata);
7518c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ndev->mtbl_lock, irqflags);
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci	/* Notify the peers by setting and clearing the global signal bit */
7548c2ecf20Sopenharmony_ci	idt_nt_write(ndev, IDT_NT_NTGSIGNAL, IDT_NTGSIGNAL_SET);
7558c2ecf20Sopenharmony_ci	idt_sw_write(ndev, IDT_SW_SEGSIGSTS, (u32)1 << ndev->part);
7568c2ecf20Sopenharmony_ci}
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci/*
7598c2ecf20Sopenharmony_ci * idt_ntb_local_link_disable() - disable the local NTB link.
7608c2ecf20Sopenharmony_ci * @ndev:	IDT NTB hardware driver descriptor
7618c2ecf20Sopenharmony_ci *
7628c2ecf20Sopenharmony_ci * In order to enable the NTB link we need:
7638c2ecf20Sopenharmony_ci * - disable Completion TLPs translation
7648c2ecf20Sopenharmony_ci * - clear corresponding mapping table entry
7658c2ecf20Sopenharmony_ci * - notify peers of NTB link state change
7668c2ecf20Sopenharmony_ci */
7678c2ecf20Sopenharmony_cistatic void idt_ntb_local_link_disable(struct idt_ntb_dev *ndev)
7688c2ecf20Sopenharmony_ci{
7698c2ecf20Sopenharmony_ci	unsigned long irqflags;
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci	/* Disable Completion TLPs translation */
7728c2ecf20Sopenharmony_ci	idt_nt_write(ndev, IDT_NT_NTCTL, 0);
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci	/* Clear the corresponding NT Mapping table entry */
7758c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ndev->mtbl_lock, irqflags);
7768c2ecf20Sopenharmony_ci	idt_nt_write(ndev, IDT_NT_NTMTBLADDR, ndev->part);
7778c2ecf20Sopenharmony_ci	idt_nt_write(ndev, IDT_NT_NTMTBLDATA, 0);
7788c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ndev->mtbl_lock, irqflags);
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci	/* Notify the peers by setting and clearing the global signal bit */
7818c2ecf20Sopenharmony_ci	idt_nt_write(ndev, IDT_NT_NTGSIGNAL, IDT_NTGSIGNAL_SET);
7828c2ecf20Sopenharmony_ci	idt_sw_write(ndev, IDT_SW_SEGSIGSTS, (u32)1 << ndev->part);
7838c2ecf20Sopenharmony_ci}
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci/*
7868c2ecf20Sopenharmony_ci * idt_ntb_local_link_is_up() - test wethter local NTB link is up
7878c2ecf20Sopenharmony_ci * @ndev:	IDT NTB hardware driver descriptor
7888c2ecf20Sopenharmony_ci *
7898c2ecf20Sopenharmony_ci * Local link is up under the following conditions:
7908c2ecf20Sopenharmony_ci * - Bus mastering is enabled
7918c2ecf20Sopenharmony_ci * - NTCTL has Completion TLPs translation enabled
7928c2ecf20Sopenharmony_ci * - Mapping table permits Request TLPs translation
7938c2ecf20Sopenharmony_ci * NOTE: We don't need to check PCIe link state since it's obviously
7948c2ecf20Sopenharmony_ci * up while we are able to communicate with IDT PCIe-switch
7958c2ecf20Sopenharmony_ci *
7968c2ecf20Sopenharmony_ci * Return: true if link is up, otherwise false
7978c2ecf20Sopenharmony_ci */
7988c2ecf20Sopenharmony_cistatic bool idt_ntb_local_link_is_up(struct idt_ntb_dev *ndev)
7998c2ecf20Sopenharmony_ci{
8008c2ecf20Sopenharmony_ci	unsigned long irqflags;
8018c2ecf20Sopenharmony_ci	u32 data;
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	/* Read the local Bus Master Enable status */
8048c2ecf20Sopenharmony_ci	data = idt_nt_read(ndev, IDT_NT_PCICMDSTS);
8058c2ecf20Sopenharmony_ci	if (!(data & IDT_PCICMDSTS_BME))
8068c2ecf20Sopenharmony_ci		return false;
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci	/* Read the local Completion TLPs translation enable status */
8098c2ecf20Sopenharmony_ci	data = idt_nt_read(ndev, IDT_NT_NTCTL);
8108c2ecf20Sopenharmony_ci	if (!(data & IDT_NTCTL_CPEN))
8118c2ecf20Sopenharmony_ci		return false;
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci	/* Read Mapping table entry corresponding to the local partition */
8148c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ndev->mtbl_lock, irqflags);
8158c2ecf20Sopenharmony_ci	idt_nt_write(ndev, IDT_NT_NTMTBLADDR, ndev->part);
8168c2ecf20Sopenharmony_ci	data = idt_nt_read(ndev, IDT_NT_NTMTBLDATA);
8178c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ndev->mtbl_lock, irqflags);
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci	return !!(data & IDT_NTMTBLDATA_VALID);
8208c2ecf20Sopenharmony_ci}
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci/*
8238c2ecf20Sopenharmony_ci * idt_ntb_peer_link_is_up() - test whether peer NTB link is up
8248c2ecf20Sopenharmony_ci * @ndev:	IDT NTB hardware driver descriptor
8258c2ecf20Sopenharmony_ci * @pidx:	Peer port index
8268c2ecf20Sopenharmony_ci *
8278c2ecf20Sopenharmony_ci * Peer link is up under the following conditions:
8288c2ecf20Sopenharmony_ci * - PCIe link is up
8298c2ecf20Sopenharmony_ci * - Bus mastering is enabled
8308c2ecf20Sopenharmony_ci * - NTCTL has Completion TLPs translation enabled
8318c2ecf20Sopenharmony_ci * - Mapping table permits Request TLPs translation
8328c2ecf20Sopenharmony_ci *
8338c2ecf20Sopenharmony_ci * Return: true if link is up, otherwise false
8348c2ecf20Sopenharmony_ci */
8358c2ecf20Sopenharmony_cistatic bool idt_ntb_peer_link_is_up(struct idt_ntb_dev *ndev, int pidx)
8368c2ecf20Sopenharmony_ci{
8378c2ecf20Sopenharmony_ci	unsigned long irqflags;
8388c2ecf20Sopenharmony_ci	unsigned char port;
8398c2ecf20Sopenharmony_ci	u32 data;
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci	/* Retrieve the device port number */
8428c2ecf20Sopenharmony_ci	port = ndev->peers[pidx].port;
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci	/* Check whether PCIe link is up */
8458c2ecf20Sopenharmony_ci	data = idt_sw_read(ndev, portdata_tbl[port].sts);
8468c2ecf20Sopenharmony_ci	if (!(data & IDT_SWPORTxSTS_LINKUP))
8478c2ecf20Sopenharmony_ci		return false;
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci	/* Check whether bus mastering is enabled on the peer port */
8508c2ecf20Sopenharmony_ci	data = idt_sw_read(ndev, portdata_tbl[port].pcicmdsts);
8518c2ecf20Sopenharmony_ci	if (!(data & IDT_PCICMDSTS_BME))
8528c2ecf20Sopenharmony_ci		return false;
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci	/* Check if Completion TLPs translation is enabled on the peer port */
8558c2ecf20Sopenharmony_ci	data = idt_sw_read(ndev, portdata_tbl[port].ntctl);
8568c2ecf20Sopenharmony_ci	if (!(data & IDT_NTCTL_CPEN))
8578c2ecf20Sopenharmony_ci		return false;
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci	/* Read Mapping table entry corresponding to the peer partition */
8608c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ndev->mtbl_lock, irqflags);
8618c2ecf20Sopenharmony_ci	idt_nt_write(ndev, IDT_NT_NTMTBLADDR, ndev->peers[pidx].part);
8628c2ecf20Sopenharmony_ci	data = idt_nt_read(ndev, IDT_NT_NTMTBLDATA);
8638c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ndev->mtbl_lock, irqflags);
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci	return !!(data & IDT_NTMTBLDATA_VALID);
8668c2ecf20Sopenharmony_ci}
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci/*
8698c2ecf20Sopenharmony_ci * idt_ntb_link_is_up() - get the current ntb link state (NTB API callback)
8708c2ecf20Sopenharmony_ci * @ntb:	NTB device context.
8718c2ecf20Sopenharmony_ci * @speed:	OUT - The link speed expressed as PCIe generation number.
8728c2ecf20Sopenharmony_ci * @width:	OUT - The link width expressed as the number of PCIe lanes.
8738c2ecf20Sopenharmony_ci *
8748c2ecf20Sopenharmony_ci * Get the bitfield of NTB link states for all peer ports
8758c2ecf20Sopenharmony_ci *
8768c2ecf20Sopenharmony_ci * Return: bitfield of indexed ports link state: bit is set/cleared if the
8778c2ecf20Sopenharmony_ci *         link is up/down respectively.
8788c2ecf20Sopenharmony_ci */
8798c2ecf20Sopenharmony_cistatic u64 idt_ntb_link_is_up(struct ntb_dev *ntb,
8808c2ecf20Sopenharmony_ci			      enum ntb_speed *speed, enum ntb_width *width)
8818c2ecf20Sopenharmony_ci{
8828c2ecf20Sopenharmony_ci	struct idt_ntb_dev *ndev = to_ndev_ntb(ntb);
8838c2ecf20Sopenharmony_ci	unsigned char pidx;
8848c2ecf20Sopenharmony_ci	u64 status;
8858c2ecf20Sopenharmony_ci	u32 data;
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci	/* Retrieve the local link speed and width */
8888c2ecf20Sopenharmony_ci	if (speed != NULL || width != NULL) {
8898c2ecf20Sopenharmony_ci		data = idt_nt_read(ndev, IDT_NT_PCIELCTLSTS);
8908c2ecf20Sopenharmony_ci		if (speed != NULL)
8918c2ecf20Sopenharmony_ci			*speed = GET_FIELD(PCIELCTLSTS_CLS, data);
8928c2ecf20Sopenharmony_ci		if (width != NULL)
8938c2ecf20Sopenharmony_ci			*width = GET_FIELD(PCIELCTLSTS_NLW, data);
8948c2ecf20Sopenharmony_ci	}
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci	/* If local NTB link isn't up then all the links are considered down */
8978c2ecf20Sopenharmony_ci	if (!idt_ntb_local_link_is_up(ndev))
8988c2ecf20Sopenharmony_ci		return 0;
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_ci	/* Collect all the peer ports link states into the bitfield */
9018c2ecf20Sopenharmony_ci	status = 0;
9028c2ecf20Sopenharmony_ci	for (pidx = 0; pidx < ndev->peer_cnt; pidx++) {
9038c2ecf20Sopenharmony_ci		if (idt_ntb_peer_link_is_up(ndev, pidx))
9048c2ecf20Sopenharmony_ci			status |= ((u64)1 << pidx);
9058c2ecf20Sopenharmony_ci	}
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_ci	return status;
9088c2ecf20Sopenharmony_ci}
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_ci/*
9118c2ecf20Sopenharmony_ci * idt_ntb_link_enable() - enable local port ntb link (NTB API callback)
9128c2ecf20Sopenharmony_ci * @ntb:	NTB device context.
9138c2ecf20Sopenharmony_ci * @max_speed:	The maximum link speed expressed as PCIe generation number.
9148c2ecf20Sopenharmony_ci * @max_width:	The maximum link width expressed as the number of PCIe lanes.
9158c2ecf20Sopenharmony_ci *
9168c2ecf20Sopenharmony_ci * Enable just local NTB link. PCIe link parameters are ignored.
9178c2ecf20Sopenharmony_ci *
9188c2ecf20Sopenharmony_ci * Return: always zero.
9198c2ecf20Sopenharmony_ci */
9208c2ecf20Sopenharmony_cistatic int idt_ntb_link_enable(struct ntb_dev *ntb, enum ntb_speed speed,
9218c2ecf20Sopenharmony_ci			       enum ntb_width width)
9228c2ecf20Sopenharmony_ci{
9238c2ecf20Sopenharmony_ci	struct idt_ntb_dev *ndev = to_ndev_ntb(ntb);
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci	/* Just enable the local NTB link */
9268c2ecf20Sopenharmony_ci	idt_ntb_local_link_enable(ndev);
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_ci	dev_dbg(&ndev->ntb.pdev->dev, "Local NTB link enabled");
9298c2ecf20Sopenharmony_ci
9308c2ecf20Sopenharmony_ci	return 0;
9318c2ecf20Sopenharmony_ci}
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci/*
9348c2ecf20Sopenharmony_ci * idt_ntb_link_disable() - disable local port ntb link (NTB API callback)
9358c2ecf20Sopenharmony_ci * @ntb:	NTB device context.
9368c2ecf20Sopenharmony_ci *
9378c2ecf20Sopenharmony_ci * Disable just local NTB link.
9388c2ecf20Sopenharmony_ci *
9398c2ecf20Sopenharmony_ci * Return: always zero.
9408c2ecf20Sopenharmony_ci */
9418c2ecf20Sopenharmony_cistatic int idt_ntb_link_disable(struct ntb_dev *ntb)
9428c2ecf20Sopenharmony_ci{
9438c2ecf20Sopenharmony_ci	struct idt_ntb_dev *ndev = to_ndev_ntb(ntb);
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_ci	/* Just disable the local NTB link */
9468c2ecf20Sopenharmony_ci	idt_ntb_local_link_disable(ndev);
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_ci	dev_dbg(&ndev->ntb.pdev->dev, "Local NTB link disabled");
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ci	return 0;
9518c2ecf20Sopenharmony_ci}
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_ci/*=============================================================================
9548c2ecf20Sopenharmony_ci *                         4. Memory Window operations
9558c2ecf20Sopenharmony_ci *
9568c2ecf20Sopenharmony_ci *    IDT PCIe-switches have two types of memory windows: MWs with direct
9578c2ecf20Sopenharmony_ci * address translation and MWs with LUT based translation. The first type of
9588c2ecf20Sopenharmony_ci * MWs is simple map of corresponding BAR address space to a memory space
9598c2ecf20Sopenharmony_ci * of specified target port. So it implemets just ont-to-one mapping. Lookup
9608c2ecf20Sopenharmony_ci * table in its turn can map one BAR address space to up to 24 different
9618c2ecf20Sopenharmony_ci * memory spaces of different ports.
9628c2ecf20Sopenharmony_ci *    NT-functions BARs can be turned on to implement either direct or lookup
9638c2ecf20Sopenharmony_ci * table based address translations, so:
9648c2ecf20Sopenharmony_ci * BAR0 - NT configuration registers space/direct address translation
9658c2ecf20Sopenharmony_ci * BAR1 - direct address translation/upper address of BAR0x64
9668c2ecf20Sopenharmony_ci * BAR2 - direct address translation/Lookup table with either 12 or 24 entries
9678c2ecf20Sopenharmony_ci * BAR3 - direct address translation/upper address of BAR2x64
9688c2ecf20Sopenharmony_ci * BAR4 - direct address translation/Lookup table with either 12 or 24 entries
9698c2ecf20Sopenharmony_ci * BAR5 - direct address translation/upper address of BAR4x64
9708c2ecf20Sopenharmony_ci *    Additionally BAR2 and BAR4 can't have 24-entries LUT enabled at the same
9718c2ecf20Sopenharmony_ci * time. Since the BARs setup can be rather complicated this driver implements
9728c2ecf20Sopenharmony_ci * a scanning algorithm to have all the possible memory windows configuration
9738c2ecf20Sopenharmony_ci * covered.
9748c2ecf20Sopenharmony_ci *
9758c2ecf20Sopenharmony_ci * NOTE 1 BAR setup must be done before Linux kernel enumerated NT-function
9768c2ecf20Sopenharmony_ci * of any port, so this driver would have memory windows configurations fixed.
9778c2ecf20Sopenharmony_ci * In this way all initializations must be performed either by platform BIOS
9788c2ecf20Sopenharmony_ci * or using EEPROM connected to IDT PCIe-switch master SMBus.
9798c2ecf20Sopenharmony_ci *
9808c2ecf20Sopenharmony_ci * NOTE 2 This driver expects BAR0 mapping NT-function configuration space.
9818c2ecf20Sopenharmony_ci * Easy calculation can give us an upper boundary of 29 possible memory windows
9828c2ecf20Sopenharmony_ci * per each NT-function if all the BARs are of 32bit type.
9838c2ecf20Sopenharmony_ci *=============================================================================
9848c2ecf20Sopenharmony_ci */
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci/*
9878c2ecf20Sopenharmony_ci * idt_get_mw_count() - get memory window count
9888c2ecf20Sopenharmony_ci * @mw_type:	Memory window type
9898c2ecf20Sopenharmony_ci *
9908c2ecf20Sopenharmony_ci * Return: number of memory windows with respect to the BAR type
9918c2ecf20Sopenharmony_ci */
9928c2ecf20Sopenharmony_cistatic inline unsigned char idt_get_mw_count(enum idt_mw_type mw_type)
9938c2ecf20Sopenharmony_ci{
9948c2ecf20Sopenharmony_ci	switch (mw_type) {
9958c2ecf20Sopenharmony_ci	case IDT_MW_DIR:
9968c2ecf20Sopenharmony_ci		return 1;
9978c2ecf20Sopenharmony_ci	case IDT_MW_LUT12:
9988c2ecf20Sopenharmony_ci		return 12;
9998c2ecf20Sopenharmony_ci	case IDT_MW_LUT24:
10008c2ecf20Sopenharmony_ci		return 24;
10018c2ecf20Sopenharmony_ci	default:
10028c2ecf20Sopenharmony_ci		break;
10038c2ecf20Sopenharmony_ci	}
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_ci	return 0;
10068c2ecf20Sopenharmony_ci}
10078c2ecf20Sopenharmony_ci
10088c2ecf20Sopenharmony_ci/*
10098c2ecf20Sopenharmony_ci * idt_get_mw_name() - get memory window name
10108c2ecf20Sopenharmony_ci * @mw_type:	Memory window type
10118c2ecf20Sopenharmony_ci *
10128c2ecf20Sopenharmony_ci * Return: pointer to a string with name
10138c2ecf20Sopenharmony_ci */
10148c2ecf20Sopenharmony_cistatic inline char *idt_get_mw_name(enum idt_mw_type mw_type)
10158c2ecf20Sopenharmony_ci{
10168c2ecf20Sopenharmony_ci	switch (mw_type) {
10178c2ecf20Sopenharmony_ci	case IDT_MW_DIR:
10188c2ecf20Sopenharmony_ci		return "DIR  ";
10198c2ecf20Sopenharmony_ci	case IDT_MW_LUT12:
10208c2ecf20Sopenharmony_ci		return "LUT12";
10218c2ecf20Sopenharmony_ci	case IDT_MW_LUT24:
10228c2ecf20Sopenharmony_ci		return "LUT24";
10238c2ecf20Sopenharmony_ci	default:
10248c2ecf20Sopenharmony_ci		break;
10258c2ecf20Sopenharmony_ci	}
10268c2ecf20Sopenharmony_ci
10278c2ecf20Sopenharmony_ci	return "unknown";
10288c2ecf20Sopenharmony_ci}
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci/*
10318c2ecf20Sopenharmony_ci * idt_scan_mws() - scan memory windows of the port
10328c2ecf20Sopenharmony_ci * @ndev:	IDT NTB hardware driver descriptor
10338c2ecf20Sopenharmony_ci * @port:	Port to get number of memory windows for
10348c2ecf20Sopenharmony_ci * @mw_cnt:	Out - number of memory windows
10358c2ecf20Sopenharmony_ci *
10368c2ecf20Sopenharmony_ci * It walks over BAR setup registers of the specified port and determines
10378c2ecf20Sopenharmony_ci * the memory windows parameters if any activated.
10388c2ecf20Sopenharmony_ci *
10398c2ecf20Sopenharmony_ci * Return: array of memory windows
10408c2ecf20Sopenharmony_ci */
10418c2ecf20Sopenharmony_cistatic struct idt_mw_cfg *idt_scan_mws(struct idt_ntb_dev *ndev, int port,
10428c2ecf20Sopenharmony_ci				       unsigned char *mw_cnt)
10438c2ecf20Sopenharmony_ci{
10448c2ecf20Sopenharmony_ci	struct idt_mw_cfg mws[IDT_MAX_NR_MWS], *ret_mws;
10458c2ecf20Sopenharmony_ci	const struct idt_ntb_bar *bars;
10468c2ecf20Sopenharmony_ci	enum idt_mw_type mw_type;
10478c2ecf20Sopenharmony_ci	unsigned char widx, bidx, en_cnt;
10488c2ecf20Sopenharmony_ci	bool bar_64bit = false;
10498c2ecf20Sopenharmony_ci	int aprt_size;
10508c2ecf20Sopenharmony_ci	u32 data;
10518c2ecf20Sopenharmony_ci
10528c2ecf20Sopenharmony_ci	/* Retrieve the array of the BARs registers */
10538c2ecf20Sopenharmony_ci	bars = portdata_tbl[port].bars;
10548c2ecf20Sopenharmony_ci
10558c2ecf20Sopenharmony_ci	/* Scan all the BARs belonging to the port */
10568c2ecf20Sopenharmony_ci	*mw_cnt = 0;
10578c2ecf20Sopenharmony_ci	for (bidx = 0; bidx < IDT_BAR_CNT; bidx += 1 + bar_64bit) {
10588c2ecf20Sopenharmony_ci		/* Read BARSETUP register value */
10598c2ecf20Sopenharmony_ci		data = idt_sw_read(ndev, bars[bidx].setup);
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_ci		/* Skip disabled BARs */
10628c2ecf20Sopenharmony_ci		if (!(data & IDT_BARSETUP_EN)) {
10638c2ecf20Sopenharmony_ci			bar_64bit = false;
10648c2ecf20Sopenharmony_ci			continue;
10658c2ecf20Sopenharmony_ci		}
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_ci		/* Skip next BARSETUP if current one has 64bit addressing */
10688c2ecf20Sopenharmony_ci		bar_64bit = IS_FLD_SET(BARSETUP_TYPE, data, 64);
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_ci		/* Skip configuration space mapping BARs */
10718c2ecf20Sopenharmony_ci		if (data & IDT_BARSETUP_MODE_CFG)
10728c2ecf20Sopenharmony_ci			continue;
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_ci		/* Retrieve MW type/entries count and aperture size */
10758c2ecf20Sopenharmony_ci		mw_type = GET_FIELD(BARSETUP_ATRAN, data);
10768c2ecf20Sopenharmony_ci		en_cnt = idt_get_mw_count(mw_type);
10778c2ecf20Sopenharmony_ci		aprt_size = (u64)1 << GET_FIELD(BARSETUP_SIZE, data);
10788c2ecf20Sopenharmony_ci
10798c2ecf20Sopenharmony_ci		/* Save configurations of all available memory windows */
10808c2ecf20Sopenharmony_ci		for (widx = 0; widx < en_cnt; widx++, (*mw_cnt)++) {
10818c2ecf20Sopenharmony_ci			/*
10828c2ecf20Sopenharmony_ci			 * IDT can expose a limited number of MWs, so it's bug
10838c2ecf20Sopenharmony_ci			 * to have more than the driver expects
10848c2ecf20Sopenharmony_ci			 */
10858c2ecf20Sopenharmony_ci			if (*mw_cnt >= IDT_MAX_NR_MWS)
10868c2ecf20Sopenharmony_ci				return ERR_PTR(-EINVAL);
10878c2ecf20Sopenharmony_ci
10888c2ecf20Sopenharmony_ci			/* Save basic MW info */
10898c2ecf20Sopenharmony_ci			mws[*mw_cnt].type = mw_type;
10908c2ecf20Sopenharmony_ci			mws[*mw_cnt].bar = bidx;
10918c2ecf20Sopenharmony_ci			mws[*mw_cnt].idx = widx;
10928c2ecf20Sopenharmony_ci			/* It's always DWORD aligned */
10938c2ecf20Sopenharmony_ci			mws[*mw_cnt].addr_align = IDT_TRANS_ALIGN;
10948c2ecf20Sopenharmony_ci			/* DIR and LUT approachs differently configure MWs */
10958c2ecf20Sopenharmony_ci			if (mw_type == IDT_MW_DIR)
10968c2ecf20Sopenharmony_ci				mws[*mw_cnt].size_max = aprt_size;
10978c2ecf20Sopenharmony_ci			else if (mw_type == IDT_MW_LUT12)
10988c2ecf20Sopenharmony_ci				mws[*mw_cnt].size_max = aprt_size / 16;
10998c2ecf20Sopenharmony_ci			else
11008c2ecf20Sopenharmony_ci				mws[*mw_cnt].size_max = aprt_size / 32;
11018c2ecf20Sopenharmony_ci			mws[*mw_cnt].size_align = (mw_type == IDT_MW_DIR) ?
11028c2ecf20Sopenharmony_ci				IDT_DIR_SIZE_ALIGN : mws[*mw_cnt].size_max;
11038c2ecf20Sopenharmony_ci		}
11048c2ecf20Sopenharmony_ci	}
11058c2ecf20Sopenharmony_ci
11068c2ecf20Sopenharmony_ci	/* Allocate memory for memory window descriptors */
11078c2ecf20Sopenharmony_ci	ret_mws = devm_kcalloc(&ndev->ntb.pdev->dev, *mw_cnt, sizeof(*ret_mws),
11088c2ecf20Sopenharmony_ci			       GFP_KERNEL);
11098c2ecf20Sopenharmony_ci	if (!ret_mws)
11108c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
11118c2ecf20Sopenharmony_ci
11128c2ecf20Sopenharmony_ci	/* Copy the info of detected memory windows */
11138c2ecf20Sopenharmony_ci	memcpy(ret_mws, mws, (*mw_cnt)*sizeof(*ret_mws));
11148c2ecf20Sopenharmony_ci
11158c2ecf20Sopenharmony_ci	return ret_mws;
11168c2ecf20Sopenharmony_ci}
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_ci/*
11198c2ecf20Sopenharmony_ci * idt_init_mws() - initialize memory windows subsystem
11208c2ecf20Sopenharmony_ci * @ndev:	IDT NTB hardware driver descriptor
11218c2ecf20Sopenharmony_ci *
11228c2ecf20Sopenharmony_ci * Scan BAR setup registers of local and peer ports to determine the
11238c2ecf20Sopenharmony_ci * outbound and inbound memory windows parameters
11248c2ecf20Sopenharmony_ci *
11258c2ecf20Sopenharmony_ci * Return: zero on success, otherwise a negative error number
11268c2ecf20Sopenharmony_ci */
11278c2ecf20Sopenharmony_cistatic int idt_init_mws(struct idt_ntb_dev *ndev)
11288c2ecf20Sopenharmony_ci{
11298c2ecf20Sopenharmony_ci	struct idt_ntb_peer *peer;
11308c2ecf20Sopenharmony_ci	unsigned char pidx;
11318c2ecf20Sopenharmony_ci
11328c2ecf20Sopenharmony_ci	/* Scan memory windows of the local port */
11338c2ecf20Sopenharmony_ci	ndev->mws = idt_scan_mws(ndev, ndev->port, &ndev->mw_cnt);
11348c2ecf20Sopenharmony_ci	if (IS_ERR(ndev->mws)) {
11358c2ecf20Sopenharmony_ci		dev_err(&ndev->ntb.pdev->dev,
11368c2ecf20Sopenharmony_ci			"Failed to scan mws of local port %hhu", ndev->port);
11378c2ecf20Sopenharmony_ci		return PTR_ERR(ndev->mws);
11388c2ecf20Sopenharmony_ci	}
11398c2ecf20Sopenharmony_ci
11408c2ecf20Sopenharmony_ci	/* Scan memory windows of the peer ports */
11418c2ecf20Sopenharmony_ci	for (pidx = 0; pidx < ndev->peer_cnt; pidx++) {
11428c2ecf20Sopenharmony_ci		peer = &ndev->peers[pidx];
11438c2ecf20Sopenharmony_ci		peer->mws = idt_scan_mws(ndev, peer->port, &peer->mw_cnt);
11448c2ecf20Sopenharmony_ci		if (IS_ERR(peer->mws)) {
11458c2ecf20Sopenharmony_ci			dev_err(&ndev->ntb.pdev->dev,
11468c2ecf20Sopenharmony_ci				"Failed to scan mws of port %hhu", peer->port);
11478c2ecf20Sopenharmony_ci			return PTR_ERR(peer->mws);
11488c2ecf20Sopenharmony_ci		}
11498c2ecf20Sopenharmony_ci	}
11508c2ecf20Sopenharmony_ci
11518c2ecf20Sopenharmony_ci	/* Initialize spin locker of the LUT registers */
11528c2ecf20Sopenharmony_ci	spin_lock_init(&ndev->lut_lock);
11538c2ecf20Sopenharmony_ci
11548c2ecf20Sopenharmony_ci	dev_dbg(&ndev->ntb.pdev->dev, "Outbound and inbound MWs initialized");
11558c2ecf20Sopenharmony_ci
11568c2ecf20Sopenharmony_ci	return 0;
11578c2ecf20Sopenharmony_ci}
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_ci/*
11608c2ecf20Sopenharmony_ci * idt_ntb_mw_count() - number of inbound memory windows (NTB API callback)
11618c2ecf20Sopenharmony_ci * @ntb:	NTB device context.
11628c2ecf20Sopenharmony_ci * @pidx:	Port index of peer device.
11638c2ecf20Sopenharmony_ci *
11648c2ecf20Sopenharmony_ci * The value is returned for the specified peer, so generally speaking it can
11658c2ecf20Sopenharmony_ci * be different for different port depending on the IDT PCIe-switch
11668c2ecf20Sopenharmony_ci * initialization.
11678c2ecf20Sopenharmony_ci *
11688c2ecf20Sopenharmony_ci * Return: the number of memory windows.
11698c2ecf20Sopenharmony_ci */
11708c2ecf20Sopenharmony_cistatic int idt_ntb_mw_count(struct ntb_dev *ntb, int pidx)
11718c2ecf20Sopenharmony_ci{
11728c2ecf20Sopenharmony_ci	struct idt_ntb_dev *ndev = to_ndev_ntb(ntb);
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_ci	if (pidx < 0 || ndev->peer_cnt <= pidx)
11758c2ecf20Sopenharmony_ci		return -EINVAL;
11768c2ecf20Sopenharmony_ci
11778c2ecf20Sopenharmony_ci	return ndev->peers[pidx].mw_cnt;
11788c2ecf20Sopenharmony_ci}
11798c2ecf20Sopenharmony_ci
11808c2ecf20Sopenharmony_ci/*
11818c2ecf20Sopenharmony_ci * idt_ntb_mw_get_align() - inbound memory window parameters (NTB API callback)
11828c2ecf20Sopenharmony_ci * @ntb:	NTB device context.
11838c2ecf20Sopenharmony_ci * @pidx:	Port index of peer device.
11848c2ecf20Sopenharmony_ci * @widx:	Memory window index.
11858c2ecf20Sopenharmony_ci * @addr_align:	OUT - the base alignment for translating the memory window
11868c2ecf20Sopenharmony_ci * @size_align:	OUT - the size alignment for translating the memory window
11878c2ecf20Sopenharmony_ci * @size_max:	OUT - the maximum size of the memory window
11888c2ecf20Sopenharmony_ci *
11898c2ecf20Sopenharmony_ci * The peer memory window parameters have already been determined, so just
11908c2ecf20Sopenharmony_ci * return the corresponding values, which mustn't change within session.
11918c2ecf20Sopenharmony_ci *
11928c2ecf20Sopenharmony_ci * Return: Zero on success, otherwise a negative error number.
11938c2ecf20Sopenharmony_ci */
11948c2ecf20Sopenharmony_cistatic int idt_ntb_mw_get_align(struct ntb_dev *ntb, int pidx, int widx,
11958c2ecf20Sopenharmony_ci				resource_size_t *addr_align,
11968c2ecf20Sopenharmony_ci				resource_size_t *size_align,
11978c2ecf20Sopenharmony_ci				resource_size_t *size_max)
11988c2ecf20Sopenharmony_ci{
11998c2ecf20Sopenharmony_ci	struct idt_ntb_dev *ndev = to_ndev_ntb(ntb);
12008c2ecf20Sopenharmony_ci	struct idt_ntb_peer *peer;
12018c2ecf20Sopenharmony_ci
12028c2ecf20Sopenharmony_ci	if (pidx < 0 || ndev->peer_cnt <= pidx)
12038c2ecf20Sopenharmony_ci		return -EINVAL;
12048c2ecf20Sopenharmony_ci
12058c2ecf20Sopenharmony_ci	peer = &ndev->peers[pidx];
12068c2ecf20Sopenharmony_ci
12078c2ecf20Sopenharmony_ci	if (widx < 0 || peer->mw_cnt <= widx)
12088c2ecf20Sopenharmony_ci		return -EINVAL;
12098c2ecf20Sopenharmony_ci
12108c2ecf20Sopenharmony_ci	if (addr_align != NULL)
12118c2ecf20Sopenharmony_ci		*addr_align = peer->mws[widx].addr_align;
12128c2ecf20Sopenharmony_ci
12138c2ecf20Sopenharmony_ci	if (size_align != NULL)
12148c2ecf20Sopenharmony_ci		*size_align = peer->mws[widx].size_align;
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_ci	if (size_max != NULL)
12178c2ecf20Sopenharmony_ci		*size_max = peer->mws[widx].size_max;
12188c2ecf20Sopenharmony_ci
12198c2ecf20Sopenharmony_ci	return 0;
12208c2ecf20Sopenharmony_ci}
12218c2ecf20Sopenharmony_ci
12228c2ecf20Sopenharmony_ci/*
12238c2ecf20Sopenharmony_ci * idt_ntb_peer_mw_count() - number of outbound memory windows
12248c2ecf20Sopenharmony_ci *			     (NTB API callback)
12258c2ecf20Sopenharmony_ci * @ntb:	NTB device context.
12268c2ecf20Sopenharmony_ci *
12278c2ecf20Sopenharmony_ci * Outbound memory windows parameters have been determined based on the
12288c2ecf20Sopenharmony_ci * BAR setup registers value, which are mostly constants within one session.
12298c2ecf20Sopenharmony_ci *
12308c2ecf20Sopenharmony_ci * Return: the number of memory windows.
12318c2ecf20Sopenharmony_ci */
12328c2ecf20Sopenharmony_cistatic int idt_ntb_peer_mw_count(struct ntb_dev *ntb)
12338c2ecf20Sopenharmony_ci{
12348c2ecf20Sopenharmony_ci	struct idt_ntb_dev *ndev = to_ndev_ntb(ntb);
12358c2ecf20Sopenharmony_ci
12368c2ecf20Sopenharmony_ci	return ndev->mw_cnt;
12378c2ecf20Sopenharmony_ci}
12388c2ecf20Sopenharmony_ci
12398c2ecf20Sopenharmony_ci/*
12408c2ecf20Sopenharmony_ci * idt_ntb_peer_mw_get_addr() - get map address of an outbound memory window
12418c2ecf20Sopenharmony_ci *				(NTB API callback)
12428c2ecf20Sopenharmony_ci * @ntb:	NTB device context.
12438c2ecf20Sopenharmony_ci * @widx:	Memory window index (within ntb_peer_mw_count() return value).
12448c2ecf20Sopenharmony_ci * @base:	OUT - the base address of mapping region.
12458c2ecf20Sopenharmony_ci * @size:	OUT - the size of mapping region.
12468c2ecf20Sopenharmony_ci *
12478c2ecf20Sopenharmony_ci * Return just parameters of BAR resources mapping. Size reflects just the size
12488c2ecf20Sopenharmony_ci * of the resource
12498c2ecf20Sopenharmony_ci *
12508c2ecf20Sopenharmony_ci * Return: Zero on success, otherwise a negative error number.
12518c2ecf20Sopenharmony_ci */
12528c2ecf20Sopenharmony_cistatic int idt_ntb_peer_mw_get_addr(struct ntb_dev *ntb, int widx,
12538c2ecf20Sopenharmony_ci				    phys_addr_t *base, resource_size_t *size)
12548c2ecf20Sopenharmony_ci{
12558c2ecf20Sopenharmony_ci	struct idt_ntb_dev *ndev = to_ndev_ntb(ntb);
12568c2ecf20Sopenharmony_ci
12578c2ecf20Sopenharmony_ci	if (widx < 0 || ndev->mw_cnt <= widx)
12588c2ecf20Sopenharmony_ci		return -EINVAL;
12598c2ecf20Sopenharmony_ci
12608c2ecf20Sopenharmony_ci	/* Mapping address is just properly shifted BAR resource start */
12618c2ecf20Sopenharmony_ci	if (base != NULL)
12628c2ecf20Sopenharmony_ci		*base = pci_resource_start(ntb->pdev, ndev->mws[widx].bar) +
12638c2ecf20Sopenharmony_ci			ndev->mws[widx].idx * ndev->mws[widx].size_max;
12648c2ecf20Sopenharmony_ci
12658c2ecf20Sopenharmony_ci	/* Mapping size has already been calculated at MWs scanning */
12668c2ecf20Sopenharmony_ci	if (size != NULL)
12678c2ecf20Sopenharmony_ci		*size = ndev->mws[widx].size_max;
12688c2ecf20Sopenharmony_ci
12698c2ecf20Sopenharmony_ci	return 0;
12708c2ecf20Sopenharmony_ci}
12718c2ecf20Sopenharmony_ci
12728c2ecf20Sopenharmony_ci/*
12738c2ecf20Sopenharmony_ci * idt_ntb_peer_mw_set_trans() - set a translation address of a memory window
12748c2ecf20Sopenharmony_ci *				 (NTB API callback)
12758c2ecf20Sopenharmony_ci * @ntb:	NTB device context.
12768c2ecf20Sopenharmony_ci * @pidx:	Port index of peer device the translation address received from.
12778c2ecf20Sopenharmony_ci * @widx:	Memory window index.
12788c2ecf20Sopenharmony_ci * @addr:	The dma address of the shared memory to access.
12798c2ecf20Sopenharmony_ci * @size:	The size of the shared memory to access.
12808c2ecf20Sopenharmony_ci *
12818c2ecf20Sopenharmony_ci * The Direct address translation and LUT base translation is initialized a
12828c2ecf20Sopenharmony_ci * bit differenet. Although the parameters restriction are now determined by
12838c2ecf20Sopenharmony_ci * the same code.
12848c2ecf20Sopenharmony_ci *
12858c2ecf20Sopenharmony_ci * Return: Zero on success, otherwise an error number.
12868c2ecf20Sopenharmony_ci */
12878c2ecf20Sopenharmony_cistatic int idt_ntb_peer_mw_set_trans(struct ntb_dev *ntb, int pidx, int widx,
12888c2ecf20Sopenharmony_ci				     u64 addr, resource_size_t size)
12898c2ecf20Sopenharmony_ci{
12908c2ecf20Sopenharmony_ci	struct idt_ntb_dev *ndev = to_ndev_ntb(ntb);
12918c2ecf20Sopenharmony_ci	struct idt_mw_cfg *mw_cfg;
12928c2ecf20Sopenharmony_ci	u32 data = 0, lutoff = 0;
12938c2ecf20Sopenharmony_ci
12948c2ecf20Sopenharmony_ci	if (pidx < 0 || ndev->peer_cnt <= pidx)
12958c2ecf20Sopenharmony_ci		return -EINVAL;
12968c2ecf20Sopenharmony_ci
12978c2ecf20Sopenharmony_ci	if (widx < 0 || ndev->mw_cnt <= widx)
12988c2ecf20Sopenharmony_ci		return -EINVAL;
12998c2ecf20Sopenharmony_ci
13008c2ecf20Sopenharmony_ci	/*
13018c2ecf20Sopenharmony_ci	 * Retrieve the memory window config to make sure the passed arguments
13028c2ecf20Sopenharmony_ci	 * fit it restrictions
13038c2ecf20Sopenharmony_ci	 */
13048c2ecf20Sopenharmony_ci	mw_cfg = &ndev->mws[widx];
13058c2ecf20Sopenharmony_ci	if (!IS_ALIGNED(addr, mw_cfg->addr_align))
13068c2ecf20Sopenharmony_ci		return -EINVAL;
13078c2ecf20Sopenharmony_ci	if (!IS_ALIGNED(size, mw_cfg->size_align) || size > mw_cfg->size_max)
13088c2ecf20Sopenharmony_ci		return -EINVAL;
13098c2ecf20Sopenharmony_ci
13108c2ecf20Sopenharmony_ci	/* DIR and LUT based translations are initialized differently */
13118c2ecf20Sopenharmony_ci	if (mw_cfg->type == IDT_MW_DIR) {
13128c2ecf20Sopenharmony_ci		const struct idt_ntb_bar *bar = &ntdata_tbl.bars[mw_cfg->bar];
13138c2ecf20Sopenharmony_ci		u64 limit;
13148c2ecf20Sopenharmony_ci		/* Set destination partition of translation */
13158c2ecf20Sopenharmony_ci		data = idt_nt_read(ndev, bar->setup);
13168c2ecf20Sopenharmony_ci		data = SET_FIELD(BARSETUP_TPART, data, ndev->peers[pidx].part);
13178c2ecf20Sopenharmony_ci		idt_nt_write(ndev, bar->setup, data);
13188c2ecf20Sopenharmony_ci		/* Set translation base address */
13198c2ecf20Sopenharmony_ci		idt_nt_write(ndev, bar->ltbase, (u32)addr);
13208c2ecf20Sopenharmony_ci		idt_nt_write(ndev, bar->utbase, (u32)(addr >> 32));
13218c2ecf20Sopenharmony_ci		/* Set the custom BAR aperture limit */
13228c2ecf20Sopenharmony_ci		limit = pci_bus_address(ntb->pdev, mw_cfg->bar) + size;
13238c2ecf20Sopenharmony_ci		idt_nt_write(ndev, bar->limit, (u32)limit);
13248c2ecf20Sopenharmony_ci		if (IS_FLD_SET(BARSETUP_TYPE, data, 64))
13258c2ecf20Sopenharmony_ci			idt_nt_write(ndev, (bar + 1)->limit, (limit >> 32));
13268c2ecf20Sopenharmony_ci	} else {
13278c2ecf20Sopenharmony_ci		unsigned long irqflags;
13288c2ecf20Sopenharmony_ci		/* Initialize corresponding LUT entry */
13298c2ecf20Sopenharmony_ci		lutoff = SET_FIELD(LUTOFFSET_INDEX, 0, mw_cfg->idx) |
13308c2ecf20Sopenharmony_ci			 SET_FIELD(LUTOFFSET_BAR, 0, mw_cfg->bar);
13318c2ecf20Sopenharmony_ci		data = SET_FIELD(LUTUDATA_PART, 0, ndev->peers[pidx].part) |
13328c2ecf20Sopenharmony_ci			IDT_LUTUDATA_VALID;
13338c2ecf20Sopenharmony_ci		spin_lock_irqsave(&ndev->lut_lock, irqflags);
13348c2ecf20Sopenharmony_ci		idt_nt_write(ndev, IDT_NT_LUTOFFSET, lutoff);
13358c2ecf20Sopenharmony_ci		idt_nt_write(ndev, IDT_NT_LUTLDATA, (u32)addr);
13368c2ecf20Sopenharmony_ci		idt_nt_write(ndev, IDT_NT_LUTMDATA, (u32)(addr >> 32));
13378c2ecf20Sopenharmony_ci		idt_nt_write(ndev, IDT_NT_LUTUDATA, data);
13388c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ndev->lut_lock, irqflags);
13398c2ecf20Sopenharmony_ci		/* Limit address isn't specified since size is fixed for LUT */
13408c2ecf20Sopenharmony_ci	}
13418c2ecf20Sopenharmony_ci
13428c2ecf20Sopenharmony_ci	return 0;
13438c2ecf20Sopenharmony_ci}
13448c2ecf20Sopenharmony_ci
13458c2ecf20Sopenharmony_ci/*
13468c2ecf20Sopenharmony_ci * idt_ntb_peer_mw_clear_trans() - clear the outbound MW translation address
13478c2ecf20Sopenharmony_ci *				   (NTB API callback)
13488c2ecf20Sopenharmony_ci * @ntb:	NTB device context.
13498c2ecf20Sopenharmony_ci * @pidx:	Port index of peer device.
13508c2ecf20Sopenharmony_ci * @widx:	Memory window index.
13518c2ecf20Sopenharmony_ci *
13528c2ecf20Sopenharmony_ci * It effectively disables the translation over the specified outbound MW.
13538c2ecf20Sopenharmony_ci *
13548c2ecf20Sopenharmony_ci * Return: Zero on success, otherwise an error number.
13558c2ecf20Sopenharmony_ci */
13568c2ecf20Sopenharmony_cistatic int idt_ntb_peer_mw_clear_trans(struct ntb_dev *ntb, int pidx,
13578c2ecf20Sopenharmony_ci					int widx)
13588c2ecf20Sopenharmony_ci{
13598c2ecf20Sopenharmony_ci	struct idt_ntb_dev *ndev = to_ndev_ntb(ntb);
13608c2ecf20Sopenharmony_ci	struct idt_mw_cfg *mw_cfg;
13618c2ecf20Sopenharmony_ci
13628c2ecf20Sopenharmony_ci	if (pidx < 0 || ndev->peer_cnt <= pidx)
13638c2ecf20Sopenharmony_ci		return -EINVAL;
13648c2ecf20Sopenharmony_ci
13658c2ecf20Sopenharmony_ci	if (widx < 0 || ndev->mw_cnt <= widx)
13668c2ecf20Sopenharmony_ci		return -EINVAL;
13678c2ecf20Sopenharmony_ci
13688c2ecf20Sopenharmony_ci	mw_cfg = &ndev->mws[widx];
13698c2ecf20Sopenharmony_ci
13708c2ecf20Sopenharmony_ci	/* DIR and LUT based translations are initialized differently */
13718c2ecf20Sopenharmony_ci	if (mw_cfg->type == IDT_MW_DIR) {
13728c2ecf20Sopenharmony_ci		const struct idt_ntb_bar *bar = &ntdata_tbl.bars[mw_cfg->bar];
13738c2ecf20Sopenharmony_ci		u32 data;
13748c2ecf20Sopenharmony_ci		/* Read BARSETUP to check BAR type */
13758c2ecf20Sopenharmony_ci		data = idt_nt_read(ndev, bar->setup);
13768c2ecf20Sopenharmony_ci		/* Disable translation by specifying zero BAR limit */
13778c2ecf20Sopenharmony_ci		idt_nt_write(ndev, bar->limit, 0);
13788c2ecf20Sopenharmony_ci		if (IS_FLD_SET(BARSETUP_TYPE, data, 64))
13798c2ecf20Sopenharmony_ci			idt_nt_write(ndev, (bar + 1)->limit, 0);
13808c2ecf20Sopenharmony_ci	} else {
13818c2ecf20Sopenharmony_ci		unsigned long irqflags;
13828c2ecf20Sopenharmony_ci		u32 lutoff;
13838c2ecf20Sopenharmony_ci		/* Clear the corresponding LUT entry up */
13848c2ecf20Sopenharmony_ci		lutoff = SET_FIELD(LUTOFFSET_INDEX, 0, mw_cfg->idx) |
13858c2ecf20Sopenharmony_ci			 SET_FIELD(LUTOFFSET_BAR, 0, mw_cfg->bar);
13868c2ecf20Sopenharmony_ci		spin_lock_irqsave(&ndev->lut_lock, irqflags);
13878c2ecf20Sopenharmony_ci		idt_nt_write(ndev, IDT_NT_LUTOFFSET, lutoff);
13888c2ecf20Sopenharmony_ci		idt_nt_write(ndev, IDT_NT_LUTLDATA, 0);
13898c2ecf20Sopenharmony_ci		idt_nt_write(ndev, IDT_NT_LUTMDATA, 0);
13908c2ecf20Sopenharmony_ci		idt_nt_write(ndev, IDT_NT_LUTUDATA, 0);
13918c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ndev->lut_lock, irqflags);
13928c2ecf20Sopenharmony_ci	}
13938c2ecf20Sopenharmony_ci
13948c2ecf20Sopenharmony_ci	return 0;
13958c2ecf20Sopenharmony_ci}
13968c2ecf20Sopenharmony_ci
13978c2ecf20Sopenharmony_ci/*=============================================================================
13988c2ecf20Sopenharmony_ci *                          5. Doorbell operations
13998c2ecf20Sopenharmony_ci *
14008c2ecf20Sopenharmony_ci *    Doorbell functionality of IDT PCIe-switches is pretty unusual. First of
14018c2ecf20Sopenharmony_ci * all there is global doorbell register which state can be changed by any
14028c2ecf20Sopenharmony_ci * NT-function of the IDT device in accordance with global permissions. These
14038c2ecf20Sopenharmony_ci * permissions configs are not supported by NTB API, so it must be done by
14048c2ecf20Sopenharmony_ci * either BIOS or EEPROM settings. In the same way the state of the global
14058c2ecf20Sopenharmony_ci * doorbell is reflected to the NT-functions local inbound doorbell registers.
14068c2ecf20Sopenharmony_ci * It can lead to situations when client driver sets some peer doorbell bits
14078c2ecf20Sopenharmony_ci * and get them bounced back to local inbound doorbell if permissions are
14088c2ecf20Sopenharmony_ci * granted.
14098c2ecf20Sopenharmony_ci *    Secondly there is just one IRQ vector for Doorbell, Message, Temperature
14108c2ecf20Sopenharmony_ci * and Switch events, so if client driver left any of Doorbell bits set and
14118c2ecf20Sopenharmony_ci * some other event occurred, the driver will be notified of Doorbell event
14128c2ecf20Sopenharmony_ci * again.
14138c2ecf20Sopenharmony_ci *=============================================================================
14148c2ecf20Sopenharmony_ci */
14158c2ecf20Sopenharmony_ci
14168c2ecf20Sopenharmony_ci/*
14178c2ecf20Sopenharmony_ci * idt_db_isr() - doorbell event ISR
14188c2ecf20Sopenharmony_ci * @ndev:	IDT NTB hardware driver descriptor
14198c2ecf20Sopenharmony_ci * @ntint_sts:	NT-function interrupt status
14208c2ecf20Sopenharmony_ci *
14218c2ecf20Sopenharmony_ci * Doorbell event happans when DBELL bit of NTINTSTS switches from 0 to 1.
14228c2ecf20Sopenharmony_ci * It happens only when unmasked doorbell bits are set to ones on completely
14238c2ecf20Sopenharmony_ci * zeroed doorbell register.
14248c2ecf20Sopenharmony_ci * The method is called from PCIe ISR bottom-half routine.
14258c2ecf20Sopenharmony_ci */
14268c2ecf20Sopenharmony_cistatic void idt_db_isr(struct idt_ntb_dev *ndev, u32 ntint_sts)
14278c2ecf20Sopenharmony_ci{
14288c2ecf20Sopenharmony_ci	/*
14298c2ecf20Sopenharmony_ci	 * Doorbell IRQ status will be cleaned only when client
14308c2ecf20Sopenharmony_ci	 * driver unsets all the doorbell bits.
14318c2ecf20Sopenharmony_ci	 */
14328c2ecf20Sopenharmony_ci	dev_dbg(&ndev->ntb.pdev->dev, "DB IRQ detected %#08x", ntint_sts);
14338c2ecf20Sopenharmony_ci
14348c2ecf20Sopenharmony_ci	/* Notify the client driver of possible doorbell state change */
14358c2ecf20Sopenharmony_ci	ntb_db_event(&ndev->ntb, 0);
14368c2ecf20Sopenharmony_ci}
14378c2ecf20Sopenharmony_ci
14388c2ecf20Sopenharmony_ci/*
14398c2ecf20Sopenharmony_ci * idt_ntb_db_valid_mask() - get a mask of doorbell bits supported by the ntb
14408c2ecf20Sopenharmony_ci *			     (NTB API callback)
14418c2ecf20Sopenharmony_ci * @ntb:	NTB device context.
14428c2ecf20Sopenharmony_ci *
14438c2ecf20Sopenharmony_ci * IDT PCIe-switches expose just one Doorbell register of DWORD size.
14448c2ecf20Sopenharmony_ci *
14458c2ecf20Sopenharmony_ci * Return: A mask of doorbell bits supported by the ntb.
14468c2ecf20Sopenharmony_ci */
14478c2ecf20Sopenharmony_cistatic u64 idt_ntb_db_valid_mask(struct ntb_dev *ntb)
14488c2ecf20Sopenharmony_ci{
14498c2ecf20Sopenharmony_ci	return IDT_DBELL_MASK;
14508c2ecf20Sopenharmony_ci}
14518c2ecf20Sopenharmony_ci
14528c2ecf20Sopenharmony_ci/*
14538c2ecf20Sopenharmony_ci * idt_ntb_db_read() - read the local doorbell register (NTB API callback)
14548c2ecf20Sopenharmony_ci * @ntb:	NTB device context.
14558c2ecf20Sopenharmony_ci *
14568c2ecf20Sopenharmony_ci * There is just on inbound doorbell register of each NT-function, so
14578c2ecf20Sopenharmony_ci * this method return it value.
14588c2ecf20Sopenharmony_ci *
14598c2ecf20Sopenharmony_ci * Return: The bits currently set in the local doorbell register.
14608c2ecf20Sopenharmony_ci */
14618c2ecf20Sopenharmony_cistatic u64 idt_ntb_db_read(struct ntb_dev *ntb)
14628c2ecf20Sopenharmony_ci{
14638c2ecf20Sopenharmony_ci	struct idt_ntb_dev *ndev = to_ndev_ntb(ntb);
14648c2ecf20Sopenharmony_ci
14658c2ecf20Sopenharmony_ci	return idt_nt_read(ndev, IDT_NT_INDBELLSTS);
14668c2ecf20Sopenharmony_ci}
14678c2ecf20Sopenharmony_ci
14688c2ecf20Sopenharmony_ci/*
14698c2ecf20Sopenharmony_ci * idt_ntb_db_clear() - clear bits in the local doorbell register
14708c2ecf20Sopenharmony_ci *			(NTB API callback)
14718c2ecf20Sopenharmony_ci * @ntb:	NTB device context.
14728c2ecf20Sopenharmony_ci * @db_bits:	Doorbell bits to clear.
14738c2ecf20Sopenharmony_ci *
14748c2ecf20Sopenharmony_ci * Clear bits of inbound doorbell register by writing ones to it.
14758c2ecf20Sopenharmony_ci *
14768c2ecf20Sopenharmony_ci * NOTE! Invalid bits are always considered cleared so it's not an error
14778c2ecf20Sopenharmony_ci * to clear them over.
14788c2ecf20Sopenharmony_ci *
14798c2ecf20Sopenharmony_ci * Return: always zero as success.
14808c2ecf20Sopenharmony_ci */
14818c2ecf20Sopenharmony_cistatic int idt_ntb_db_clear(struct ntb_dev *ntb, u64 db_bits)
14828c2ecf20Sopenharmony_ci{
14838c2ecf20Sopenharmony_ci	struct idt_ntb_dev *ndev = to_ndev_ntb(ntb);
14848c2ecf20Sopenharmony_ci
14858c2ecf20Sopenharmony_ci	idt_nt_write(ndev, IDT_NT_INDBELLSTS, (u32)db_bits);
14868c2ecf20Sopenharmony_ci
14878c2ecf20Sopenharmony_ci	return 0;
14888c2ecf20Sopenharmony_ci}
14898c2ecf20Sopenharmony_ci
14908c2ecf20Sopenharmony_ci/*
14918c2ecf20Sopenharmony_ci * idt_ntb_db_read_mask() - read the local doorbell mask (NTB API callback)
14928c2ecf20Sopenharmony_ci * @ntb:	NTB device context.
14938c2ecf20Sopenharmony_ci *
14948c2ecf20Sopenharmony_ci * Each inbound doorbell bit can be masked from generating IRQ by setting
14958c2ecf20Sopenharmony_ci * the corresponding bit in inbound doorbell mask. So this method returns
14968c2ecf20Sopenharmony_ci * the value of the register.
14978c2ecf20Sopenharmony_ci *
14988c2ecf20Sopenharmony_ci * Return: The bits currently set in the local doorbell mask register.
14998c2ecf20Sopenharmony_ci */
15008c2ecf20Sopenharmony_cistatic u64 idt_ntb_db_read_mask(struct ntb_dev *ntb)
15018c2ecf20Sopenharmony_ci{
15028c2ecf20Sopenharmony_ci	struct idt_ntb_dev *ndev = to_ndev_ntb(ntb);
15038c2ecf20Sopenharmony_ci
15048c2ecf20Sopenharmony_ci	return idt_nt_read(ndev, IDT_NT_INDBELLMSK);
15058c2ecf20Sopenharmony_ci}
15068c2ecf20Sopenharmony_ci
15078c2ecf20Sopenharmony_ci/*
15088c2ecf20Sopenharmony_ci * idt_ntb_db_set_mask() - set bits in the local doorbell mask
15098c2ecf20Sopenharmony_ci *			   (NTB API callback)
15108c2ecf20Sopenharmony_ci * @ntb:	NTB device context.
15118c2ecf20Sopenharmony_ci * @db_bits:	Doorbell mask bits to set.
15128c2ecf20Sopenharmony_ci *
15138c2ecf20Sopenharmony_ci * The inbound doorbell register mask value must be read, then OR'ed with
15148c2ecf20Sopenharmony_ci * passed field and only then set back.
15158c2ecf20Sopenharmony_ci *
15168c2ecf20Sopenharmony_ci * Return: zero on success, negative error if invalid argument passed.
15178c2ecf20Sopenharmony_ci */
15188c2ecf20Sopenharmony_cistatic int idt_ntb_db_set_mask(struct ntb_dev *ntb, u64 db_bits)
15198c2ecf20Sopenharmony_ci{
15208c2ecf20Sopenharmony_ci	struct idt_ntb_dev *ndev = to_ndev_ntb(ntb);
15218c2ecf20Sopenharmony_ci
15228c2ecf20Sopenharmony_ci	return idt_reg_set_bits(ndev, IDT_NT_INDBELLMSK, &ndev->db_mask_lock,
15238c2ecf20Sopenharmony_ci				IDT_DBELL_MASK, db_bits);
15248c2ecf20Sopenharmony_ci}
15258c2ecf20Sopenharmony_ci
15268c2ecf20Sopenharmony_ci/*
15278c2ecf20Sopenharmony_ci * idt_ntb_db_clear_mask() - clear bits in the local doorbell mask
15288c2ecf20Sopenharmony_ci *			     (NTB API callback)
15298c2ecf20Sopenharmony_ci * @ntb:	NTB device context.
15308c2ecf20Sopenharmony_ci * @db_bits:	Doorbell bits to clear.
15318c2ecf20Sopenharmony_ci *
15328c2ecf20Sopenharmony_ci * The method just clears the set bits up in accordance with the passed
15338c2ecf20Sopenharmony_ci * bitfield. IDT PCIe-switch shall generate an interrupt if there hasn't
15348c2ecf20Sopenharmony_ci * been any unmasked bit set before current unmasking. Otherwise IRQ won't
15358c2ecf20Sopenharmony_ci * be generated since there is only one IRQ vector for all doorbells.
15368c2ecf20Sopenharmony_ci *
15378c2ecf20Sopenharmony_ci * Return: always zero as success
15388c2ecf20Sopenharmony_ci */
15398c2ecf20Sopenharmony_cistatic int idt_ntb_db_clear_mask(struct ntb_dev *ntb, u64 db_bits)
15408c2ecf20Sopenharmony_ci{
15418c2ecf20Sopenharmony_ci	struct idt_ntb_dev *ndev = to_ndev_ntb(ntb);
15428c2ecf20Sopenharmony_ci
15438c2ecf20Sopenharmony_ci	idt_reg_clear_bits(ndev, IDT_NT_INDBELLMSK, &ndev->db_mask_lock,
15448c2ecf20Sopenharmony_ci			   db_bits);
15458c2ecf20Sopenharmony_ci
15468c2ecf20Sopenharmony_ci	return 0;
15478c2ecf20Sopenharmony_ci}
15488c2ecf20Sopenharmony_ci
15498c2ecf20Sopenharmony_ci/*
15508c2ecf20Sopenharmony_ci * idt_ntb_peer_db_set() - set bits in the peer doorbell register
15518c2ecf20Sopenharmony_ci *			   (NTB API callback)
15528c2ecf20Sopenharmony_ci * @ntb:	NTB device context.
15538c2ecf20Sopenharmony_ci * @db_bits:	Doorbell bits to set.
15548c2ecf20Sopenharmony_ci *
15558c2ecf20Sopenharmony_ci * IDT PCIe-switches exposes local outbound doorbell register to change peer
15568c2ecf20Sopenharmony_ci * inbound doorbell register state.
15578c2ecf20Sopenharmony_ci *
15588c2ecf20Sopenharmony_ci * Return: zero on success, negative error if invalid argument passed.
15598c2ecf20Sopenharmony_ci */
15608c2ecf20Sopenharmony_cistatic int idt_ntb_peer_db_set(struct ntb_dev *ntb, u64 db_bits)
15618c2ecf20Sopenharmony_ci{
15628c2ecf20Sopenharmony_ci	struct idt_ntb_dev *ndev = to_ndev_ntb(ntb);
15638c2ecf20Sopenharmony_ci
15648c2ecf20Sopenharmony_ci	if (db_bits & ~(u64)IDT_DBELL_MASK)
15658c2ecf20Sopenharmony_ci		return -EINVAL;
15668c2ecf20Sopenharmony_ci
15678c2ecf20Sopenharmony_ci	idt_nt_write(ndev, IDT_NT_OUTDBELLSET, (u32)db_bits);
15688c2ecf20Sopenharmony_ci	return 0;
15698c2ecf20Sopenharmony_ci}
15708c2ecf20Sopenharmony_ci
15718c2ecf20Sopenharmony_ci/*=============================================================================
15728c2ecf20Sopenharmony_ci *                          6. Messaging operations
15738c2ecf20Sopenharmony_ci *
15748c2ecf20Sopenharmony_ci *    Each NT-function of IDT PCIe-switch has four inbound and four outbound
15758c2ecf20Sopenharmony_ci * message registers. Each outbound message register can be connected to one or
15768c2ecf20Sopenharmony_ci * even more than one peer inbound message registers by setting global
15778c2ecf20Sopenharmony_ci * configurations. Since NTB API permits one-on-one message registers mapping
15788c2ecf20Sopenharmony_ci * only, the driver acts in according with that restriction.
15798c2ecf20Sopenharmony_ci *=============================================================================
15808c2ecf20Sopenharmony_ci */
15818c2ecf20Sopenharmony_ci
15828c2ecf20Sopenharmony_ci/*
15838c2ecf20Sopenharmony_ci * idt_init_msg() - initialize messaging interface
15848c2ecf20Sopenharmony_ci * @ndev:	IDT NTB hardware driver descriptor
15858c2ecf20Sopenharmony_ci *
15868c2ecf20Sopenharmony_ci * Just initialize the message registers routing tables locker.
15878c2ecf20Sopenharmony_ci */
15888c2ecf20Sopenharmony_cistatic void idt_init_msg(struct idt_ntb_dev *ndev)
15898c2ecf20Sopenharmony_ci{
15908c2ecf20Sopenharmony_ci	unsigned char midx;
15918c2ecf20Sopenharmony_ci
15928c2ecf20Sopenharmony_ci	/* Init the messages routing table lockers */
15938c2ecf20Sopenharmony_ci	for (midx = 0; midx < IDT_MSG_CNT; midx++)
15948c2ecf20Sopenharmony_ci		spin_lock_init(&ndev->msg_locks[midx]);
15958c2ecf20Sopenharmony_ci
15968c2ecf20Sopenharmony_ci	dev_dbg(&ndev->ntb.pdev->dev, "NTB Messaging initialized");
15978c2ecf20Sopenharmony_ci}
15988c2ecf20Sopenharmony_ci
15998c2ecf20Sopenharmony_ci/*
16008c2ecf20Sopenharmony_ci * idt_msg_isr() - message event ISR
16018c2ecf20Sopenharmony_ci * @ndev:	IDT NTB hardware driver descriptor
16028c2ecf20Sopenharmony_ci * @ntint_sts:	NT-function interrupt status
16038c2ecf20Sopenharmony_ci *
16048c2ecf20Sopenharmony_ci * Message event happens when MSG bit of NTINTSTS switches from 0 to 1.
16058c2ecf20Sopenharmony_ci * It happens only when unmasked message status bits are set to ones on
16068c2ecf20Sopenharmony_ci * completely zeroed message status register.
16078c2ecf20Sopenharmony_ci * The method is called from PCIe ISR bottom-half routine.
16088c2ecf20Sopenharmony_ci */
16098c2ecf20Sopenharmony_cistatic void idt_msg_isr(struct idt_ntb_dev *ndev, u32 ntint_sts)
16108c2ecf20Sopenharmony_ci{
16118c2ecf20Sopenharmony_ci	/*
16128c2ecf20Sopenharmony_ci	 * Message IRQ status will be cleaned only when client
16138c2ecf20Sopenharmony_ci	 * driver unsets all the message status bits.
16148c2ecf20Sopenharmony_ci	 */
16158c2ecf20Sopenharmony_ci	dev_dbg(&ndev->ntb.pdev->dev, "Message IRQ detected %#08x", ntint_sts);
16168c2ecf20Sopenharmony_ci
16178c2ecf20Sopenharmony_ci	/* Notify the client driver of possible message status change */
16188c2ecf20Sopenharmony_ci	ntb_msg_event(&ndev->ntb);
16198c2ecf20Sopenharmony_ci}
16208c2ecf20Sopenharmony_ci
16218c2ecf20Sopenharmony_ci/*
16228c2ecf20Sopenharmony_ci * idt_ntb_msg_count() - get the number of message registers (NTB API callback)
16238c2ecf20Sopenharmony_ci * @ntb:	NTB device context.
16248c2ecf20Sopenharmony_ci *
16258c2ecf20Sopenharmony_ci * IDT PCIe-switches support four message registers.
16268c2ecf20Sopenharmony_ci *
16278c2ecf20Sopenharmony_ci * Return: the number of message registers.
16288c2ecf20Sopenharmony_ci */
16298c2ecf20Sopenharmony_cistatic int idt_ntb_msg_count(struct ntb_dev *ntb)
16308c2ecf20Sopenharmony_ci{
16318c2ecf20Sopenharmony_ci	return IDT_MSG_CNT;
16328c2ecf20Sopenharmony_ci}
16338c2ecf20Sopenharmony_ci
16348c2ecf20Sopenharmony_ci/*
16358c2ecf20Sopenharmony_ci * idt_ntb_msg_inbits() - get a bitfield of inbound message registers status
16368c2ecf20Sopenharmony_ci *			  (NTB API callback)
16378c2ecf20Sopenharmony_ci * @ntb:	NTB device context.
16388c2ecf20Sopenharmony_ci *
16398c2ecf20Sopenharmony_ci * NT message status register is shared between inbound and outbound message
16408c2ecf20Sopenharmony_ci * registers status
16418c2ecf20Sopenharmony_ci *
16428c2ecf20Sopenharmony_ci * Return: bitfield of inbound message registers.
16438c2ecf20Sopenharmony_ci */
16448c2ecf20Sopenharmony_cistatic u64 idt_ntb_msg_inbits(struct ntb_dev *ntb)
16458c2ecf20Sopenharmony_ci{
16468c2ecf20Sopenharmony_ci	return (u64)IDT_INMSG_MASK;
16478c2ecf20Sopenharmony_ci}
16488c2ecf20Sopenharmony_ci
16498c2ecf20Sopenharmony_ci/*
16508c2ecf20Sopenharmony_ci * idt_ntb_msg_outbits() - get a bitfield of outbound message registers status
16518c2ecf20Sopenharmony_ci *			  (NTB API callback)
16528c2ecf20Sopenharmony_ci * @ntb:	NTB device context.
16538c2ecf20Sopenharmony_ci *
16548c2ecf20Sopenharmony_ci * NT message status register is shared between inbound and outbound message
16558c2ecf20Sopenharmony_ci * registers status
16568c2ecf20Sopenharmony_ci *
16578c2ecf20Sopenharmony_ci * Return: bitfield of outbound message registers.
16588c2ecf20Sopenharmony_ci */
16598c2ecf20Sopenharmony_cistatic u64 idt_ntb_msg_outbits(struct ntb_dev *ntb)
16608c2ecf20Sopenharmony_ci{
16618c2ecf20Sopenharmony_ci	return (u64)IDT_OUTMSG_MASK;
16628c2ecf20Sopenharmony_ci}
16638c2ecf20Sopenharmony_ci
16648c2ecf20Sopenharmony_ci/*
16658c2ecf20Sopenharmony_ci * idt_ntb_msg_read_sts() - read the message registers status (NTB API callback)
16668c2ecf20Sopenharmony_ci * @ntb:	NTB device context.
16678c2ecf20Sopenharmony_ci *
16688c2ecf20Sopenharmony_ci * IDT PCIe-switches expose message status registers to notify drivers of
16698c2ecf20Sopenharmony_ci * incoming data and failures in case if peer message register isn't freed.
16708c2ecf20Sopenharmony_ci *
16718c2ecf20Sopenharmony_ci * Return: status bits of message registers
16728c2ecf20Sopenharmony_ci */
16738c2ecf20Sopenharmony_cistatic u64 idt_ntb_msg_read_sts(struct ntb_dev *ntb)
16748c2ecf20Sopenharmony_ci{
16758c2ecf20Sopenharmony_ci	struct idt_ntb_dev *ndev = to_ndev_ntb(ntb);
16768c2ecf20Sopenharmony_ci
16778c2ecf20Sopenharmony_ci	return idt_nt_read(ndev, IDT_NT_MSGSTS);
16788c2ecf20Sopenharmony_ci}
16798c2ecf20Sopenharmony_ci
16808c2ecf20Sopenharmony_ci/*
16818c2ecf20Sopenharmony_ci * idt_ntb_msg_clear_sts() - clear status bits of message registers
16828c2ecf20Sopenharmony_ci *			     (NTB API callback)
16838c2ecf20Sopenharmony_ci * @ntb:	NTB device context.
16848c2ecf20Sopenharmony_ci * @sts_bits:	Status bits to clear.
16858c2ecf20Sopenharmony_ci *
16868c2ecf20Sopenharmony_ci * Clear bits in the status register by writing ones.
16878c2ecf20Sopenharmony_ci *
16888c2ecf20Sopenharmony_ci * NOTE! Invalid bits are always considered cleared so it's not an error
16898c2ecf20Sopenharmony_ci * to clear them over.
16908c2ecf20Sopenharmony_ci *
16918c2ecf20Sopenharmony_ci * Return: always zero as success.
16928c2ecf20Sopenharmony_ci */
16938c2ecf20Sopenharmony_cistatic int idt_ntb_msg_clear_sts(struct ntb_dev *ntb, u64 sts_bits)
16948c2ecf20Sopenharmony_ci{
16958c2ecf20Sopenharmony_ci	struct idt_ntb_dev *ndev = to_ndev_ntb(ntb);
16968c2ecf20Sopenharmony_ci
16978c2ecf20Sopenharmony_ci	idt_nt_write(ndev, IDT_NT_MSGSTS, sts_bits);
16988c2ecf20Sopenharmony_ci
16998c2ecf20Sopenharmony_ci	return 0;
17008c2ecf20Sopenharmony_ci}
17018c2ecf20Sopenharmony_ci
17028c2ecf20Sopenharmony_ci/*
17038c2ecf20Sopenharmony_ci * idt_ntb_msg_set_mask() - set mask of message register status bits
17048c2ecf20Sopenharmony_ci *			    (NTB API callback)
17058c2ecf20Sopenharmony_ci * @ntb:	NTB device context.
17068c2ecf20Sopenharmony_ci * @mask_bits:	Mask bits.
17078c2ecf20Sopenharmony_ci *
17088c2ecf20Sopenharmony_ci * Mask the message status bits from raising an IRQ.
17098c2ecf20Sopenharmony_ci *
17108c2ecf20Sopenharmony_ci * Return: zero on success, negative error if invalid argument passed.
17118c2ecf20Sopenharmony_ci */
17128c2ecf20Sopenharmony_cistatic int idt_ntb_msg_set_mask(struct ntb_dev *ntb, u64 mask_bits)
17138c2ecf20Sopenharmony_ci{
17148c2ecf20Sopenharmony_ci	struct idt_ntb_dev *ndev = to_ndev_ntb(ntb);
17158c2ecf20Sopenharmony_ci
17168c2ecf20Sopenharmony_ci	return idt_reg_set_bits(ndev, IDT_NT_MSGSTSMSK, &ndev->msg_mask_lock,
17178c2ecf20Sopenharmony_ci				IDT_MSG_MASK, mask_bits);
17188c2ecf20Sopenharmony_ci}
17198c2ecf20Sopenharmony_ci
17208c2ecf20Sopenharmony_ci/*
17218c2ecf20Sopenharmony_ci * idt_ntb_msg_clear_mask() - clear message registers mask
17228c2ecf20Sopenharmony_ci *			      (NTB API callback)
17238c2ecf20Sopenharmony_ci * @ntb:	NTB device context.
17248c2ecf20Sopenharmony_ci * @mask_bits:	Mask bits.
17258c2ecf20Sopenharmony_ci *
17268c2ecf20Sopenharmony_ci * Clear mask of message status bits IRQs.
17278c2ecf20Sopenharmony_ci *
17288c2ecf20Sopenharmony_ci * Return: always zero as success.
17298c2ecf20Sopenharmony_ci */
17308c2ecf20Sopenharmony_cistatic int idt_ntb_msg_clear_mask(struct ntb_dev *ntb, u64 mask_bits)
17318c2ecf20Sopenharmony_ci{
17328c2ecf20Sopenharmony_ci	struct idt_ntb_dev *ndev = to_ndev_ntb(ntb);
17338c2ecf20Sopenharmony_ci
17348c2ecf20Sopenharmony_ci	idt_reg_clear_bits(ndev, IDT_NT_MSGSTSMSK, &ndev->msg_mask_lock,
17358c2ecf20Sopenharmony_ci			   mask_bits);
17368c2ecf20Sopenharmony_ci
17378c2ecf20Sopenharmony_ci	return 0;
17388c2ecf20Sopenharmony_ci}
17398c2ecf20Sopenharmony_ci
17408c2ecf20Sopenharmony_ci/*
17418c2ecf20Sopenharmony_ci * idt_ntb_msg_read() - read message register with specified index
17428c2ecf20Sopenharmony_ci *			(NTB API callback)
17438c2ecf20Sopenharmony_ci * @ntb:	NTB device context.
17448c2ecf20Sopenharmony_ci * @pidx:	OUT - Port index of peer device a message retrieved from
17458c2ecf20Sopenharmony_ci * @midx:	Message register index
17468c2ecf20Sopenharmony_ci *
17478c2ecf20Sopenharmony_ci * Read data from the specified message register and source register.
17488c2ecf20Sopenharmony_ci *
17498c2ecf20Sopenharmony_ci * Return: inbound message register value.
17508c2ecf20Sopenharmony_ci */
17518c2ecf20Sopenharmony_cistatic u32 idt_ntb_msg_read(struct ntb_dev *ntb, int *pidx, int midx)
17528c2ecf20Sopenharmony_ci{
17538c2ecf20Sopenharmony_ci	struct idt_ntb_dev *ndev = to_ndev_ntb(ntb);
17548c2ecf20Sopenharmony_ci
17558c2ecf20Sopenharmony_ci	if (midx < 0 || IDT_MSG_CNT <= midx)
17568c2ecf20Sopenharmony_ci		return ~(u32)0;
17578c2ecf20Sopenharmony_ci
17588c2ecf20Sopenharmony_ci	/* Retrieve source port index of the message */
17598c2ecf20Sopenharmony_ci	if (pidx != NULL) {
17608c2ecf20Sopenharmony_ci		u32 srcpart;
17618c2ecf20Sopenharmony_ci
17628c2ecf20Sopenharmony_ci		srcpart = idt_nt_read(ndev, ntdata_tbl.msgs[midx].src);
17638c2ecf20Sopenharmony_ci		*pidx = ndev->part_idx_map[srcpart];
17648c2ecf20Sopenharmony_ci
17658c2ecf20Sopenharmony_ci		/* Sanity check partition index (for initial case) */
17668c2ecf20Sopenharmony_ci		if (*pidx == -EINVAL)
17678c2ecf20Sopenharmony_ci			*pidx = 0;
17688c2ecf20Sopenharmony_ci	}
17698c2ecf20Sopenharmony_ci
17708c2ecf20Sopenharmony_ci	/* Retrieve data of the corresponding message register */
17718c2ecf20Sopenharmony_ci	return idt_nt_read(ndev, ntdata_tbl.msgs[midx].in);
17728c2ecf20Sopenharmony_ci}
17738c2ecf20Sopenharmony_ci
17748c2ecf20Sopenharmony_ci/*
17758c2ecf20Sopenharmony_ci * idt_ntb_peer_msg_write() - write data to the specified message register
17768c2ecf20Sopenharmony_ci *			      (NTB API callback)
17778c2ecf20Sopenharmony_ci * @ntb:	NTB device context.
17788c2ecf20Sopenharmony_ci * @pidx:	Port index of peer device a message being sent to
17798c2ecf20Sopenharmony_ci * @midx:	Message register index
17808c2ecf20Sopenharmony_ci * @msg:	Data to send
17818c2ecf20Sopenharmony_ci *
17828c2ecf20Sopenharmony_ci * Just try to send data to a peer. Message status register should be
17838c2ecf20Sopenharmony_ci * checked by client driver.
17848c2ecf20Sopenharmony_ci *
17858c2ecf20Sopenharmony_ci * Return: zero on success, negative error if invalid argument passed.
17868c2ecf20Sopenharmony_ci */
17878c2ecf20Sopenharmony_cistatic int idt_ntb_peer_msg_write(struct ntb_dev *ntb, int pidx, int midx,
17888c2ecf20Sopenharmony_ci				  u32 msg)
17898c2ecf20Sopenharmony_ci{
17908c2ecf20Sopenharmony_ci	struct idt_ntb_dev *ndev = to_ndev_ntb(ntb);
17918c2ecf20Sopenharmony_ci	unsigned long irqflags;
17928c2ecf20Sopenharmony_ci	u32 swpmsgctl = 0;
17938c2ecf20Sopenharmony_ci
17948c2ecf20Sopenharmony_ci	if (midx < 0 || IDT_MSG_CNT <= midx)
17958c2ecf20Sopenharmony_ci		return -EINVAL;
17968c2ecf20Sopenharmony_ci
17978c2ecf20Sopenharmony_ci	if (pidx < 0 || ndev->peer_cnt <= pidx)
17988c2ecf20Sopenharmony_ci		return -EINVAL;
17998c2ecf20Sopenharmony_ci
18008c2ecf20Sopenharmony_ci	/* Collect the routing information */
18018c2ecf20Sopenharmony_ci	swpmsgctl = SET_FIELD(SWPxMSGCTL_REG, 0, midx) |
18028c2ecf20Sopenharmony_ci		    SET_FIELD(SWPxMSGCTL_PART, 0, ndev->peers[pidx].part);
18038c2ecf20Sopenharmony_ci
18048c2ecf20Sopenharmony_ci	/* Lock the messages routing table of the specified register */
18058c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ndev->msg_locks[midx], irqflags);
18068c2ecf20Sopenharmony_ci	/* Set the route and send the data */
18078c2ecf20Sopenharmony_ci	idt_sw_write(ndev, partdata_tbl[ndev->part].msgctl[midx], swpmsgctl);
18088c2ecf20Sopenharmony_ci	idt_nt_write(ndev, ntdata_tbl.msgs[midx].out, msg);
18098c2ecf20Sopenharmony_ci	/* Unlock the messages routing table */
18108c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ndev->msg_locks[midx], irqflags);
18118c2ecf20Sopenharmony_ci
18128c2ecf20Sopenharmony_ci	/* Client driver shall check the status register */
18138c2ecf20Sopenharmony_ci	return 0;
18148c2ecf20Sopenharmony_ci}
18158c2ecf20Sopenharmony_ci
18168c2ecf20Sopenharmony_ci/*=============================================================================
18178c2ecf20Sopenharmony_ci *                      7. Temperature sensor operations
18188c2ecf20Sopenharmony_ci *
18198c2ecf20Sopenharmony_ci *    IDT PCIe-switch has an embedded temperature sensor, which can be used to
18208c2ecf20Sopenharmony_ci * check current chip core temperature. Since a workload environment can be
18218c2ecf20Sopenharmony_ci * different on different platforms, an offset and ADC/filter settings can be
18228c2ecf20Sopenharmony_ci * specified. Although the offset configuration is only exposed to the sysfs
18238c2ecf20Sopenharmony_ci * hwmon interface at the moment. The rest of the settings can be adjusted
18248c2ecf20Sopenharmony_ci * for instance by the BIOS/EEPROM firmware.
18258c2ecf20Sopenharmony_ci *=============================================================================
18268c2ecf20Sopenharmony_ci */
18278c2ecf20Sopenharmony_ci
18288c2ecf20Sopenharmony_ci/*
18298c2ecf20Sopenharmony_ci * idt_get_deg() - convert millidegree Celsius value to just degree
18308c2ecf20Sopenharmony_ci * @mdegC:	IN - millidegree Celsius value
18318c2ecf20Sopenharmony_ci *
18328c2ecf20Sopenharmony_ci * Return: Degree corresponding to the passed millidegree value
18338c2ecf20Sopenharmony_ci */
18348c2ecf20Sopenharmony_cistatic inline s8 idt_get_deg(long mdegC)
18358c2ecf20Sopenharmony_ci{
18368c2ecf20Sopenharmony_ci	return mdegC / 1000;
18378c2ecf20Sopenharmony_ci}
18388c2ecf20Sopenharmony_ci
18398c2ecf20Sopenharmony_ci/*
18408c2ecf20Sopenharmony_ci * idt_get_frac() - retrieve 0/0.5 fraction of the millidegree Celsius value
18418c2ecf20Sopenharmony_ci * @mdegC:	IN - millidegree Celsius value
18428c2ecf20Sopenharmony_ci *
18438c2ecf20Sopenharmony_ci * Return: 0/0.5 degree fraction of the passed millidegree value
18448c2ecf20Sopenharmony_ci */
18458c2ecf20Sopenharmony_cistatic inline u8 idt_get_deg_frac(long mdegC)
18468c2ecf20Sopenharmony_ci{
18478c2ecf20Sopenharmony_ci	return (mdegC % 1000) >= 500 ? 5 : 0;
18488c2ecf20Sopenharmony_ci}
18498c2ecf20Sopenharmony_ci
18508c2ecf20Sopenharmony_ci/*
18518c2ecf20Sopenharmony_ci * idt_get_temp_fmt() - convert millidegree Celsius value to 0:7:1 format
18528c2ecf20Sopenharmony_ci * @mdegC:	IN - millidegree Celsius value
18538c2ecf20Sopenharmony_ci *
18548c2ecf20Sopenharmony_ci * Return: 0:7:1 format acceptable by the IDT temperature sensor
18558c2ecf20Sopenharmony_ci */
18568c2ecf20Sopenharmony_cistatic inline u8 idt_temp_get_fmt(long mdegC)
18578c2ecf20Sopenharmony_ci{
18588c2ecf20Sopenharmony_ci	return (idt_get_deg(mdegC) << 1) | (idt_get_deg_frac(mdegC) ? 1 : 0);
18598c2ecf20Sopenharmony_ci}
18608c2ecf20Sopenharmony_ci
18618c2ecf20Sopenharmony_ci/*
18628c2ecf20Sopenharmony_ci * idt_get_temp_sval() - convert temp sample to signed millidegree Celsius
18638c2ecf20Sopenharmony_ci * @data:	IN - shifted to LSB 8-bits temperature sample
18648c2ecf20Sopenharmony_ci *
18658c2ecf20Sopenharmony_ci * Return: signed millidegree Celsius
18668c2ecf20Sopenharmony_ci */
18678c2ecf20Sopenharmony_cistatic inline long idt_get_temp_sval(u32 data)
18688c2ecf20Sopenharmony_ci{
18698c2ecf20Sopenharmony_ci	return ((s8)data / 2) * 1000 + (data & 0x1 ? 500 : 0);
18708c2ecf20Sopenharmony_ci}
18718c2ecf20Sopenharmony_ci
18728c2ecf20Sopenharmony_ci/*
18738c2ecf20Sopenharmony_ci * idt_get_temp_sval() - convert temp sample to unsigned millidegree Celsius
18748c2ecf20Sopenharmony_ci * @data:	IN - shifted to LSB 8-bits temperature sample
18758c2ecf20Sopenharmony_ci *
18768c2ecf20Sopenharmony_ci * Return: unsigned millidegree Celsius
18778c2ecf20Sopenharmony_ci */
18788c2ecf20Sopenharmony_cistatic inline long idt_get_temp_uval(u32 data)
18798c2ecf20Sopenharmony_ci{
18808c2ecf20Sopenharmony_ci	return (data / 2) * 1000 + (data & 0x1 ? 500 : 0);
18818c2ecf20Sopenharmony_ci}
18828c2ecf20Sopenharmony_ci
18838c2ecf20Sopenharmony_ci/*
18848c2ecf20Sopenharmony_ci * idt_read_temp() - read temperature from chip sensor
18858c2ecf20Sopenharmony_ci * @ntb:	NTB device context.
18868c2ecf20Sopenharmony_ci * @type:	IN - type of the temperature value to read
18878c2ecf20Sopenharmony_ci * @val:	OUT - integer value of temperature in millidegree Celsius
18888c2ecf20Sopenharmony_ci */
18898c2ecf20Sopenharmony_cistatic void idt_read_temp(struct idt_ntb_dev *ndev,
18908c2ecf20Sopenharmony_ci			  const enum idt_temp_val type, long *val)
18918c2ecf20Sopenharmony_ci{
18928c2ecf20Sopenharmony_ci	u32 data;
18938c2ecf20Sopenharmony_ci
18948c2ecf20Sopenharmony_ci	/* Alter the temperature field in accordance with the passed type */
18958c2ecf20Sopenharmony_ci	switch (type) {
18968c2ecf20Sopenharmony_ci	case IDT_TEMP_CUR:
18978c2ecf20Sopenharmony_ci		data = GET_FIELD(TMPSTS_TEMP,
18988c2ecf20Sopenharmony_ci				 idt_sw_read(ndev, IDT_SW_TMPSTS));
18998c2ecf20Sopenharmony_ci		break;
19008c2ecf20Sopenharmony_ci	case IDT_TEMP_LOW:
19018c2ecf20Sopenharmony_ci		data = GET_FIELD(TMPSTS_LTEMP,
19028c2ecf20Sopenharmony_ci				 idt_sw_read(ndev, IDT_SW_TMPSTS));
19038c2ecf20Sopenharmony_ci		break;
19048c2ecf20Sopenharmony_ci	case IDT_TEMP_HIGH:
19058c2ecf20Sopenharmony_ci		data = GET_FIELD(TMPSTS_HTEMP,
19068c2ecf20Sopenharmony_ci				 idt_sw_read(ndev, IDT_SW_TMPSTS));
19078c2ecf20Sopenharmony_ci		break;
19088c2ecf20Sopenharmony_ci	case IDT_TEMP_OFFSET:
19098c2ecf20Sopenharmony_ci		/* This is the only field with signed 0:7:1 format */
19108c2ecf20Sopenharmony_ci		data = GET_FIELD(TMPADJ_OFFSET,
19118c2ecf20Sopenharmony_ci				 idt_sw_read(ndev, IDT_SW_TMPADJ));
19128c2ecf20Sopenharmony_ci		*val = idt_get_temp_sval(data);
19138c2ecf20Sopenharmony_ci		return;
19148c2ecf20Sopenharmony_ci	default:
19158c2ecf20Sopenharmony_ci		data = GET_FIELD(TMPSTS_TEMP,
19168c2ecf20Sopenharmony_ci				 idt_sw_read(ndev, IDT_SW_TMPSTS));
19178c2ecf20Sopenharmony_ci		break;
19188c2ecf20Sopenharmony_ci	}
19198c2ecf20Sopenharmony_ci
19208c2ecf20Sopenharmony_ci	/* The rest of the fields accept unsigned 0:7:1 format */
19218c2ecf20Sopenharmony_ci	*val = idt_get_temp_uval(data);
19228c2ecf20Sopenharmony_ci}
19238c2ecf20Sopenharmony_ci
19248c2ecf20Sopenharmony_ci/*
19258c2ecf20Sopenharmony_ci * idt_write_temp() - write temperature to the chip sensor register
19268c2ecf20Sopenharmony_ci * @ntb:	NTB device context.
19278c2ecf20Sopenharmony_ci * @type:	IN - type of the temperature value to change
19288c2ecf20Sopenharmony_ci * @val:	IN - integer value of temperature in millidegree Celsius
19298c2ecf20Sopenharmony_ci */
19308c2ecf20Sopenharmony_cistatic void idt_write_temp(struct idt_ntb_dev *ndev,
19318c2ecf20Sopenharmony_ci			   const enum idt_temp_val type, const long val)
19328c2ecf20Sopenharmony_ci{
19338c2ecf20Sopenharmony_ci	unsigned int reg;
19348c2ecf20Sopenharmony_ci	u32 data;
19358c2ecf20Sopenharmony_ci	u8 fmt;
19368c2ecf20Sopenharmony_ci
19378c2ecf20Sopenharmony_ci	/* Retrieve the properly formatted temperature value */
19388c2ecf20Sopenharmony_ci	fmt = idt_temp_get_fmt(val);
19398c2ecf20Sopenharmony_ci
19408c2ecf20Sopenharmony_ci	mutex_lock(&ndev->hwmon_mtx);
19418c2ecf20Sopenharmony_ci	switch (type) {
19428c2ecf20Sopenharmony_ci	case IDT_TEMP_LOW:
19438c2ecf20Sopenharmony_ci		reg = IDT_SW_TMPALARM;
19448c2ecf20Sopenharmony_ci		data = SET_FIELD(TMPALARM_LTEMP, idt_sw_read(ndev, reg), fmt) &
19458c2ecf20Sopenharmony_ci			~IDT_TMPALARM_IRQ_MASK;
19468c2ecf20Sopenharmony_ci		break;
19478c2ecf20Sopenharmony_ci	case IDT_TEMP_HIGH:
19488c2ecf20Sopenharmony_ci		reg = IDT_SW_TMPALARM;
19498c2ecf20Sopenharmony_ci		data = SET_FIELD(TMPALARM_HTEMP, idt_sw_read(ndev, reg), fmt) &
19508c2ecf20Sopenharmony_ci			~IDT_TMPALARM_IRQ_MASK;
19518c2ecf20Sopenharmony_ci		break;
19528c2ecf20Sopenharmony_ci	case IDT_TEMP_OFFSET:
19538c2ecf20Sopenharmony_ci		reg = IDT_SW_TMPADJ;
19548c2ecf20Sopenharmony_ci		data = SET_FIELD(TMPADJ_OFFSET, idt_sw_read(ndev, reg), fmt);
19558c2ecf20Sopenharmony_ci		break;
19568c2ecf20Sopenharmony_ci	default:
19578c2ecf20Sopenharmony_ci		goto inval_spin_unlock;
19588c2ecf20Sopenharmony_ci	}
19598c2ecf20Sopenharmony_ci
19608c2ecf20Sopenharmony_ci	idt_sw_write(ndev, reg, data);
19618c2ecf20Sopenharmony_ci
19628c2ecf20Sopenharmony_ciinval_spin_unlock:
19638c2ecf20Sopenharmony_ci	mutex_unlock(&ndev->hwmon_mtx);
19648c2ecf20Sopenharmony_ci}
19658c2ecf20Sopenharmony_ci
19668c2ecf20Sopenharmony_ci/*
19678c2ecf20Sopenharmony_ci * idt_sysfs_show_temp() - printout corresponding temperature value
19688c2ecf20Sopenharmony_ci * @dev:	Pointer to the NTB device structure
19698c2ecf20Sopenharmony_ci * @da:		Sensor device attribute structure
19708c2ecf20Sopenharmony_ci * @buf:	Buffer to print temperature out
19718c2ecf20Sopenharmony_ci *
19728c2ecf20Sopenharmony_ci * Return: Number of written symbols or negative error
19738c2ecf20Sopenharmony_ci */
19748c2ecf20Sopenharmony_cistatic ssize_t idt_sysfs_show_temp(struct device *dev,
19758c2ecf20Sopenharmony_ci				   struct device_attribute *da, char *buf)
19768c2ecf20Sopenharmony_ci{
19778c2ecf20Sopenharmony_ci	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
19788c2ecf20Sopenharmony_ci	struct idt_ntb_dev *ndev = dev_get_drvdata(dev);
19798c2ecf20Sopenharmony_ci	enum idt_temp_val type = attr->index;
19808c2ecf20Sopenharmony_ci	long mdeg;
19818c2ecf20Sopenharmony_ci
19828c2ecf20Sopenharmony_ci	idt_read_temp(ndev, type, &mdeg);
19838c2ecf20Sopenharmony_ci	return sprintf(buf, "%ld\n", mdeg);
19848c2ecf20Sopenharmony_ci}
19858c2ecf20Sopenharmony_ci
19868c2ecf20Sopenharmony_ci/*
19878c2ecf20Sopenharmony_ci * idt_sysfs_set_temp() - set corresponding temperature value
19888c2ecf20Sopenharmony_ci * @dev:	Pointer to the NTB device structure
19898c2ecf20Sopenharmony_ci * @da:		Sensor device attribute structure
19908c2ecf20Sopenharmony_ci * @buf:	Buffer to print temperature out
19918c2ecf20Sopenharmony_ci * @count:	Size of the passed buffer
19928c2ecf20Sopenharmony_ci *
19938c2ecf20Sopenharmony_ci * Return: Number of written symbols or negative error
19948c2ecf20Sopenharmony_ci */
19958c2ecf20Sopenharmony_cistatic ssize_t idt_sysfs_set_temp(struct device *dev,
19968c2ecf20Sopenharmony_ci				  struct device_attribute *da, const char *buf,
19978c2ecf20Sopenharmony_ci				  size_t count)
19988c2ecf20Sopenharmony_ci{
19998c2ecf20Sopenharmony_ci	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
20008c2ecf20Sopenharmony_ci	struct idt_ntb_dev *ndev = dev_get_drvdata(dev);
20018c2ecf20Sopenharmony_ci	enum idt_temp_val type = attr->index;
20028c2ecf20Sopenharmony_ci	long mdeg;
20038c2ecf20Sopenharmony_ci	int ret;
20048c2ecf20Sopenharmony_ci
20058c2ecf20Sopenharmony_ci	ret = kstrtol(buf, 10, &mdeg);
20068c2ecf20Sopenharmony_ci	if (ret)
20078c2ecf20Sopenharmony_ci		return ret;
20088c2ecf20Sopenharmony_ci
20098c2ecf20Sopenharmony_ci	/* Clamp the passed value in accordance with the type */
20108c2ecf20Sopenharmony_ci	if (type == IDT_TEMP_OFFSET)
20118c2ecf20Sopenharmony_ci		mdeg = clamp_val(mdeg, IDT_TEMP_MIN_OFFSET,
20128c2ecf20Sopenharmony_ci				 IDT_TEMP_MAX_OFFSET);
20138c2ecf20Sopenharmony_ci	else
20148c2ecf20Sopenharmony_ci		mdeg = clamp_val(mdeg, IDT_TEMP_MIN_MDEG, IDT_TEMP_MAX_MDEG);
20158c2ecf20Sopenharmony_ci
20168c2ecf20Sopenharmony_ci	idt_write_temp(ndev, type, mdeg);
20178c2ecf20Sopenharmony_ci
20188c2ecf20Sopenharmony_ci	return count;
20198c2ecf20Sopenharmony_ci}
20208c2ecf20Sopenharmony_ci
20218c2ecf20Sopenharmony_ci/*
20228c2ecf20Sopenharmony_ci * idt_sysfs_reset_hist() - reset temperature history
20238c2ecf20Sopenharmony_ci * @dev:	Pointer to the NTB device structure
20248c2ecf20Sopenharmony_ci * @da:		Sensor device attribute structure
20258c2ecf20Sopenharmony_ci * @buf:	Buffer to print temperature out
20268c2ecf20Sopenharmony_ci * @count:	Size of the passed buffer
20278c2ecf20Sopenharmony_ci *
20288c2ecf20Sopenharmony_ci * Return: Number of written symbols or negative error
20298c2ecf20Sopenharmony_ci */
20308c2ecf20Sopenharmony_cistatic ssize_t idt_sysfs_reset_hist(struct device *dev,
20318c2ecf20Sopenharmony_ci				    struct device_attribute *da,
20328c2ecf20Sopenharmony_ci				    const char *buf, size_t count)
20338c2ecf20Sopenharmony_ci{
20348c2ecf20Sopenharmony_ci	struct idt_ntb_dev *ndev = dev_get_drvdata(dev);
20358c2ecf20Sopenharmony_ci
20368c2ecf20Sopenharmony_ci	/* Just set the maximal value to the lowest temperature field and
20378c2ecf20Sopenharmony_ci	 * minimal value to the highest temperature field
20388c2ecf20Sopenharmony_ci	 */
20398c2ecf20Sopenharmony_ci	idt_write_temp(ndev, IDT_TEMP_LOW, IDT_TEMP_MAX_MDEG);
20408c2ecf20Sopenharmony_ci	idt_write_temp(ndev, IDT_TEMP_HIGH, IDT_TEMP_MIN_MDEG);
20418c2ecf20Sopenharmony_ci
20428c2ecf20Sopenharmony_ci	return count;
20438c2ecf20Sopenharmony_ci}
20448c2ecf20Sopenharmony_ci
20458c2ecf20Sopenharmony_ci/*
20468c2ecf20Sopenharmony_ci * Hwmon IDT sysfs attributes
20478c2ecf20Sopenharmony_ci */
20488c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(temp1_input, 0444, idt_sysfs_show_temp, NULL,
20498c2ecf20Sopenharmony_ci			  IDT_TEMP_CUR);
20508c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(temp1_lowest, 0444, idt_sysfs_show_temp, NULL,
20518c2ecf20Sopenharmony_ci			  IDT_TEMP_LOW);
20528c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(temp1_highest, 0444, idt_sysfs_show_temp, NULL,
20538c2ecf20Sopenharmony_ci			  IDT_TEMP_HIGH);
20548c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(temp1_offset, 0644, idt_sysfs_show_temp,
20558c2ecf20Sopenharmony_ci			  idt_sysfs_set_temp, IDT_TEMP_OFFSET);
20568c2ecf20Sopenharmony_cistatic DEVICE_ATTR(temp1_reset_history, 0200, NULL, idt_sysfs_reset_hist);
20578c2ecf20Sopenharmony_ci
20588c2ecf20Sopenharmony_ci/*
20598c2ecf20Sopenharmony_ci * Hwmon IDT sysfs attributes group
20608c2ecf20Sopenharmony_ci */
20618c2ecf20Sopenharmony_cistatic struct attribute *idt_temp_attrs[] = {
20628c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp1_input.dev_attr.attr,
20638c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp1_lowest.dev_attr.attr,
20648c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp1_highest.dev_attr.attr,
20658c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp1_offset.dev_attr.attr,
20668c2ecf20Sopenharmony_ci	&dev_attr_temp1_reset_history.attr,
20678c2ecf20Sopenharmony_ci	NULL
20688c2ecf20Sopenharmony_ci};
20698c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(idt_temp);
20708c2ecf20Sopenharmony_ci
20718c2ecf20Sopenharmony_ci/*
20728c2ecf20Sopenharmony_ci * idt_init_temp() - initialize temperature sensor interface
20738c2ecf20Sopenharmony_ci * @ndev:	IDT NTB hardware driver descriptor
20748c2ecf20Sopenharmony_ci *
20758c2ecf20Sopenharmony_ci * Simple sensor initializarion method is responsible for device switching
20768c2ecf20Sopenharmony_ci * on and resource management based hwmon interface registration. Note, that
20778c2ecf20Sopenharmony_ci * since the device is shared we won't disable it on remove, but leave it
20788c2ecf20Sopenharmony_ci * working until the system is powered off.
20798c2ecf20Sopenharmony_ci */
20808c2ecf20Sopenharmony_cistatic void idt_init_temp(struct idt_ntb_dev *ndev)
20818c2ecf20Sopenharmony_ci{
20828c2ecf20Sopenharmony_ci	struct device *hwmon;
20838c2ecf20Sopenharmony_ci
20848c2ecf20Sopenharmony_ci	/* Enable sensor if it hasn't been already */
20858c2ecf20Sopenharmony_ci	idt_sw_write(ndev, IDT_SW_TMPCTL, 0x0);
20868c2ecf20Sopenharmony_ci
20878c2ecf20Sopenharmony_ci	/* Initialize hwmon interface fields */
20888c2ecf20Sopenharmony_ci	mutex_init(&ndev->hwmon_mtx);
20898c2ecf20Sopenharmony_ci
20908c2ecf20Sopenharmony_ci	hwmon = devm_hwmon_device_register_with_groups(&ndev->ntb.pdev->dev,
20918c2ecf20Sopenharmony_ci		ndev->swcfg->name, ndev, idt_temp_groups);
20928c2ecf20Sopenharmony_ci	if (IS_ERR(hwmon)) {
20938c2ecf20Sopenharmony_ci		dev_err(&ndev->ntb.pdev->dev, "Couldn't create hwmon device");
20948c2ecf20Sopenharmony_ci		return;
20958c2ecf20Sopenharmony_ci	}
20968c2ecf20Sopenharmony_ci
20978c2ecf20Sopenharmony_ci	dev_dbg(&ndev->ntb.pdev->dev, "Temperature HWmon interface registered");
20988c2ecf20Sopenharmony_ci}
20998c2ecf20Sopenharmony_ci
21008c2ecf20Sopenharmony_ci/*=============================================================================
21018c2ecf20Sopenharmony_ci *                           8. ISRs related operations
21028c2ecf20Sopenharmony_ci *
21038c2ecf20Sopenharmony_ci *    IDT PCIe-switch has strangely developed IRQ system. There is just one
21048c2ecf20Sopenharmony_ci * interrupt vector for doorbell and message registers. So the hardware driver
21058c2ecf20Sopenharmony_ci * can't determine actual source of IRQ if, for example, message event happened
21068c2ecf20Sopenharmony_ci * while any of unmasked doorbell is still set. The similar situation may be if
21078c2ecf20Sopenharmony_ci * switch or temperature sensor events pop up. The difference is that SEVENT
21088c2ecf20Sopenharmony_ci * and TMPSENSOR bits of NT interrupt status register can be cleaned by
21098c2ecf20Sopenharmony_ci * IRQ handler so a next interrupt request won't have false handling of
21108c2ecf20Sopenharmony_ci * corresponding events.
21118c2ecf20Sopenharmony_ci *    The hardware driver has only bottom-half handler of the IRQ, since if any
21128c2ecf20Sopenharmony_ci * of events happened the device won't raise it again before the last one is
21138c2ecf20Sopenharmony_ci * handled by clearing of corresponding NTINTSTS bit.
21148c2ecf20Sopenharmony_ci *=============================================================================
21158c2ecf20Sopenharmony_ci */
21168c2ecf20Sopenharmony_ci
21178c2ecf20Sopenharmony_cistatic irqreturn_t idt_thread_isr(int irq, void *devid);
21188c2ecf20Sopenharmony_ci
21198c2ecf20Sopenharmony_ci/*
21208c2ecf20Sopenharmony_ci * idt_init_isr() - initialize PCIe interrupt handler
21218c2ecf20Sopenharmony_ci * @ndev:	IDT NTB hardware driver descriptor
21228c2ecf20Sopenharmony_ci *
21238c2ecf20Sopenharmony_ci * Return: zero on success, otherwise a negative error number.
21248c2ecf20Sopenharmony_ci */
21258c2ecf20Sopenharmony_cistatic int idt_init_isr(struct idt_ntb_dev *ndev)
21268c2ecf20Sopenharmony_ci{
21278c2ecf20Sopenharmony_ci	struct pci_dev *pdev = ndev->ntb.pdev;
21288c2ecf20Sopenharmony_ci	u32 ntint_mask;
21298c2ecf20Sopenharmony_ci	int ret;
21308c2ecf20Sopenharmony_ci
21318c2ecf20Sopenharmony_ci	/* Allocate just one interrupt vector for the ISR */
21328c2ecf20Sopenharmony_ci	ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI | PCI_IRQ_LEGACY);
21338c2ecf20Sopenharmony_ci	if (ret != 1) {
21348c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Failed to allocate IRQ vector");
21358c2ecf20Sopenharmony_ci		return ret;
21368c2ecf20Sopenharmony_ci	}
21378c2ecf20Sopenharmony_ci
21388c2ecf20Sopenharmony_ci	/* Retrieve the IRQ vector */
21398c2ecf20Sopenharmony_ci	ret = pci_irq_vector(pdev, 0);
21408c2ecf20Sopenharmony_ci	if (ret < 0) {
21418c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Failed to get IRQ vector");
21428c2ecf20Sopenharmony_ci		goto err_free_vectors;
21438c2ecf20Sopenharmony_ci	}
21448c2ecf20Sopenharmony_ci
21458c2ecf20Sopenharmony_ci	/* Set the IRQ handler */
21468c2ecf20Sopenharmony_ci	ret = devm_request_threaded_irq(&pdev->dev, ret, NULL, idt_thread_isr,
21478c2ecf20Sopenharmony_ci					IRQF_ONESHOT, NTB_IRQNAME, ndev);
21488c2ecf20Sopenharmony_ci	if (ret != 0) {
21498c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Failed to set MSI IRQ handler, %d", ret);
21508c2ecf20Sopenharmony_ci		goto err_free_vectors;
21518c2ecf20Sopenharmony_ci	}
21528c2ecf20Sopenharmony_ci
21538c2ecf20Sopenharmony_ci	/* Unmask Message/Doorbell/SE interrupts */
21548c2ecf20Sopenharmony_ci	ntint_mask = idt_nt_read(ndev, IDT_NT_NTINTMSK) & ~IDT_NTINTMSK_ALL;
21558c2ecf20Sopenharmony_ci	idt_nt_write(ndev, IDT_NT_NTINTMSK, ntint_mask);
21568c2ecf20Sopenharmony_ci
21578c2ecf20Sopenharmony_ci	/* From now on the interrupts are enabled */
21588c2ecf20Sopenharmony_ci	dev_dbg(&pdev->dev, "NTB interrupts initialized");
21598c2ecf20Sopenharmony_ci
21608c2ecf20Sopenharmony_ci	return 0;
21618c2ecf20Sopenharmony_ci
21628c2ecf20Sopenharmony_cierr_free_vectors:
21638c2ecf20Sopenharmony_ci	pci_free_irq_vectors(pdev);
21648c2ecf20Sopenharmony_ci
21658c2ecf20Sopenharmony_ci	return ret;
21668c2ecf20Sopenharmony_ci}
21678c2ecf20Sopenharmony_ci
21688c2ecf20Sopenharmony_ci/*
21698c2ecf20Sopenharmony_ci * idt_deinit_ist() - deinitialize PCIe interrupt handler
21708c2ecf20Sopenharmony_ci * @ndev:	IDT NTB hardware driver descriptor
21718c2ecf20Sopenharmony_ci *
21728c2ecf20Sopenharmony_ci * Disable corresponding interrupts and free allocated IRQ vectors.
21738c2ecf20Sopenharmony_ci */
21748c2ecf20Sopenharmony_cistatic void idt_deinit_isr(struct idt_ntb_dev *ndev)
21758c2ecf20Sopenharmony_ci{
21768c2ecf20Sopenharmony_ci	struct pci_dev *pdev = ndev->ntb.pdev;
21778c2ecf20Sopenharmony_ci	u32 ntint_mask;
21788c2ecf20Sopenharmony_ci
21798c2ecf20Sopenharmony_ci	/* Mask interrupts back */
21808c2ecf20Sopenharmony_ci	ntint_mask = idt_nt_read(ndev, IDT_NT_NTINTMSK) | IDT_NTINTMSK_ALL;
21818c2ecf20Sopenharmony_ci	idt_nt_write(ndev, IDT_NT_NTINTMSK, ntint_mask);
21828c2ecf20Sopenharmony_ci
21838c2ecf20Sopenharmony_ci	/* Manually free IRQ otherwise PCI free irq vectors will fail */
21848c2ecf20Sopenharmony_ci	devm_free_irq(&pdev->dev, pci_irq_vector(pdev, 0), ndev);
21858c2ecf20Sopenharmony_ci
21868c2ecf20Sopenharmony_ci	/* Free allocated IRQ vectors */
21878c2ecf20Sopenharmony_ci	pci_free_irq_vectors(pdev);
21888c2ecf20Sopenharmony_ci
21898c2ecf20Sopenharmony_ci	dev_dbg(&pdev->dev, "NTB interrupts deinitialized");
21908c2ecf20Sopenharmony_ci}
21918c2ecf20Sopenharmony_ci
21928c2ecf20Sopenharmony_ci/*
21938c2ecf20Sopenharmony_ci * idt_thread_isr() - NT function interrupts handler
21948c2ecf20Sopenharmony_ci * @irq:	IRQ number
21958c2ecf20Sopenharmony_ci * @devid:	Custom buffer
21968c2ecf20Sopenharmony_ci *
21978c2ecf20Sopenharmony_ci * It reads current NT interrupts state register and handles all the event
21988c2ecf20Sopenharmony_ci * it declares.
21998c2ecf20Sopenharmony_ci * The method is bottom-half routine of actual default PCIe IRQ handler.
22008c2ecf20Sopenharmony_ci */
22018c2ecf20Sopenharmony_cistatic irqreturn_t idt_thread_isr(int irq, void *devid)
22028c2ecf20Sopenharmony_ci{
22038c2ecf20Sopenharmony_ci	struct idt_ntb_dev *ndev = devid;
22048c2ecf20Sopenharmony_ci	bool handled = false;
22058c2ecf20Sopenharmony_ci	u32 ntint_sts;
22068c2ecf20Sopenharmony_ci
22078c2ecf20Sopenharmony_ci	/* Read the NT interrupts status register */
22088c2ecf20Sopenharmony_ci	ntint_sts = idt_nt_read(ndev, IDT_NT_NTINTSTS);
22098c2ecf20Sopenharmony_ci
22108c2ecf20Sopenharmony_ci	/* Handle messaging interrupts */
22118c2ecf20Sopenharmony_ci	if (ntint_sts & IDT_NTINTSTS_MSG) {
22128c2ecf20Sopenharmony_ci		idt_msg_isr(ndev, ntint_sts);
22138c2ecf20Sopenharmony_ci		handled = true;
22148c2ecf20Sopenharmony_ci	}
22158c2ecf20Sopenharmony_ci
22168c2ecf20Sopenharmony_ci	/* Handle doorbell interrupts */
22178c2ecf20Sopenharmony_ci	if (ntint_sts & IDT_NTINTSTS_DBELL) {
22188c2ecf20Sopenharmony_ci		idt_db_isr(ndev, ntint_sts);
22198c2ecf20Sopenharmony_ci		handled = true;
22208c2ecf20Sopenharmony_ci	}
22218c2ecf20Sopenharmony_ci
22228c2ecf20Sopenharmony_ci	/* Handle switch event interrupts */
22238c2ecf20Sopenharmony_ci	if (ntint_sts & IDT_NTINTSTS_SEVENT) {
22248c2ecf20Sopenharmony_ci		idt_se_isr(ndev, ntint_sts);
22258c2ecf20Sopenharmony_ci		handled = true;
22268c2ecf20Sopenharmony_ci	}
22278c2ecf20Sopenharmony_ci
22288c2ecf20Sopenharmony_ci	dev_dbg(&ndev->ntb.pdev->dev, "IDT IRQs 0x%08x handled", ntint_sts);
22298c2ecf20Sopenharmony_ci
22308c2ecf20Sopenharmony_ci	return handled ? IRQ_HANDLED : IRQ_NONE;
22318c2ecf20Sopenharmony_ci}
22328c2ecf20Sopenharmony_ci
22338c2ecf20Sopenharmony_ci/*===========================================================================
22348c2ecf20Sopenharmony_ci *                     9. NTB hardware driver initialization
22358c2ecf20Sopenharmony_ci *===========================================================================
22368c2ecf20Sopenharmony_ci */
22378c2ecf20Sopenharmony_ci
22388c2ecf20Sopenharmony_ci/*
22398c2ecf20Sopenharmony_ci * NTB API operations
22408c2ecf20Sopenharmony_ci */
22418c2ecf20Sopenharmony_cistatic const struct ntb_dev_ops idt_ntb_ops = {
22428c2ecf20Sopenharmony_ci	.port_number		= idt_ntb_port_number,
22438c2ecf20Sopenharmony_ci	.peer_port_count	= idt_ntb_peer_port_count,
22448c2ecf20Sopenharmony_ci	.peer_port_number	= idt_ntb_peer_port_number,
22458c2ecf20Sopenharmony_ci	.peer_port_idx		= idt_ntb_peer_port_idx,
22468c2ecf20Sopenharmony_ci	.link_is_up		= idt_ntb_link_is_up,
22478c2ecf20Sopenharmony_ci	.link_enable		= idt_ntb_link_enable,
22488c2ecf20Sopenharmony_ci	.link_disable		= idt_ntb_link_disable,
22498c2ecf20Sopenharmony_ci	.mw_count		= idt_ntb_mw_count,
22508c2ecf20Sopenharmony_ci	.mw_get_align		= idt_ntb_mw_get_align,
22518c2ecf20Sopenharmony_ci	.peer_mw_count		= idt_ntb_peer_mw_count,
22528c2ecf20Sopenharmony_ci	.peer_mw_get_addr	= idt_ntb_peer_mw_get_addr,
22538c2ecf20Sopenharmony_ci	.peer_mw_set_trans	= idt_ntb_peer_mw_set_trans,
22548c2ecf20Sopenharmony_ci	.peer_mw_clear_trans	= idt_ntb_peer_mw_clear_trans,
22558c2ecf20Sopenharmony_ci	.db_valid_mask		= idt_ntb_db_valid_mask,
22568c2ecf20Sopenharmony_ci	.db_read		= idt_ntb_db_read,
22578c2ecf20Sopenharmony_ci	.db_clear		= idt_ntb_db_clear,
22588c2ecf20Sopenharmony_ci	.db_read_mask		= idt_ntb_db_read_mask,
22598c2ecf20Sopenharmony_ci	.db_set_mask		= idt_ntb_db_set_mask,
22608c2ecf20Sopenharmony_ci	.db_clear_mask		= idt_ntb_db_clear_mask,
22618c2ecf20Sopenharmony_ci	.peer_db_set		= idt_ntb_peer_db_set,
22628c2ecf20Sopenharmony_ci	.msg_count		= idt_ntb_msg_count,
22638c2ecf20Sopenharmony_ci	.msg_inbits		= idt_ntb_msg_inbits,
22648c2ecf20Sopenharmony_ci	.msg_outbits		= idt_ntb_msg_outbits,
22658c2ecf20Sopenharmony_ci	.msg_read_sts		= idt_ntb_msg_read_sts,
22668c2ecf20Sopenharmony_ci	.msg_clear_sts		= idt_ntb_msg_clear_sts,
22678c2ecf20Sopenharmony_ci	.msg_set_mask		= idt_ntb_msg_set_mask,
22688c2ecf20Sopenharmony_ci	.msg_clear_mask		= idt_ntb_msg_clear_mask,
22698c2ecf20Sopenharmony_ci	.msg_read		= idt_ntb_msg_read,
22708c2ecf20Sopenharmony_ci	.peer_msg_write		= idt_ntb_peer_msg_write
22718c2ecf20Sopenharmony_ci};
22728c2ecf20Sopenharmony_ci
22738c2ecf20Sopenharmony_ci/*
22748c2ecf20Sopenharmony_ci * idt_register_device() - register IDT NTB device
22758c2ecf20Sopenharmony_ci * @ndev:	IDT NTB hardware driver descriptor
22768c2ecf20Sopenharmony_ci *
22778c2ecf20Sopenharmony_ci * Return: zero on success, otherwise a negative error number.
22788c2ecf20Sopenharmony_ci */
22798c2ecf20Sopenharmony_cistatic int idt_register_device(struct idt_ntb_dev *ndev)
22808c2ecf20Sopenharmony_ci{
22818c2ecf20Sopenharmony_ci	int ret;
22828c2ecf20Sopenharmony_ci
22838c2ecf20Sopenharmony_ci	/* Initialize the rest of NTB device structure and register it */
22848c2ecf20Sopenharmony_ci	ndev->ntb.ops = &idt_ntb_ops;
22858c2ecf20Sopenharmony_ci	ndev->ntb.topo = NTB_TOPO_SWITCH;
22868c2ecf20Sopenharmony_ci
22878c2ecf20Sopenharmony_ci	ret = ntb_register_device(&ndev->ntb);
22888c2ecf20Sopenharmony_ci	if (ret != 0) {
22898c2ecf20Sopenharmony_ci		dev_err(&ndev->ntb.pdev->dev, "Failed to register NTB device");
22908c2ecf20Sopenharmony_ci		return ret;
22918c2ecf20Sopenharmony_ci	}
22928c2ecf20Sopenharmony_ci
22938c2ecf20Sopenharmony_ci	dev_dbg(&ndev->ntb.pdev->dev, "NTB device successfully registered");
22948c2ecf20Sopenharmony_ci
22958c2ecf20Sopenharmony_ci	return 0;
22968c2ecf20Sopenharmony_ci}
22978c2ecf20Sopenharmony_ci
22988c2ecf20Sopenharmony_ci/*
22998c2ecf20Sopenharmony_ci * idt_unregister_device() - unregister IDT NTB device
23008c2ecf20Sopenharmony_ci * @ndev:	IDT NTB hardware driver descriptor
23018c2ecf20Sopenharmony_ci */
23028c2ecf20Sopenharmony_cistatic void idt_unregister_device(struct idt_ntb_dev *ndev)
23038c2ecf20Sopenharmony_ci{
23048c2ecf20Sopenharmony_ci	/* Just unregister the NTB device */
23058c2ecf20Sopenharmony_ci	ntb_unregister_device(&ndev->ntb);
23068c2ecf20Sopenharmony_ci
23078c2ecf20Sopenharmony_ci	dev_dbg(&ndev->ntb.pdev->dev, "NTB device unregistered");
23088c2ecf20Sopenharmony_ci}
23098c2ecf20Sopenharmony_ci
23108c2ecf20Sopenharmony_ci/*=============================================================================
23118c2ecf20Sopenharmony_ci *                        10. DebugFS node initialization
23128c2ecf20Sopenharmony_ci *=============================================================================
23138c2ecf20Sopenharmony_ci */
23148c2ecf20Sopenharmony_ci
23158c2ecf20Sopenharmony_cistatic ssize_t idt_dbgfs_info_read(struct file *filp, char __user *ubuf,
23168c2ecf20Sopenharmony_ci				   size_t count, loff_t *offp);
23178c2ecf20Sopenharmony_ci
23188c2ecf20Sopenharmony_ci/*
23198c2ecf20Sopenharmony_ci * Driver DebugFS info file operations
23208c2ecf20Sopenharmony_ci */
23218c2ecf20Sopenharmony_cistatic const struct file_operations idt_dbgfs_info_ops = {
23228c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
23238c2ecf20Sopenharmony_ci	.open = simple_open,
23248c2ecf20Sopenharmony_ci	.read = idt_dbgfs_info_read
23258c2ecf20Sopenharmony_ci};
23268c2ecf20Sopenharmony_ci
23278c2ecf20Sopenharmony_ci/*
23288c2ecf20Sopenharmony_ci * idt_dbgfs_info_read() - DebugFS read info node callback
23298c2ecf20Sopenharmony_ci * @file:	File node descriptor.
23308c2ecf20Sopenharmony_ci * @ubuf:	User-space buffer to put data to
23318c2ecf20Sopenharmony_ci * @count:	Size of the buffer
23328c2ecf20Sopenharmony_ci * @offp:	Offset within the buffer
23338c2ecf20Sopenharmony_ci */
23348c2ecf20Sopenharmony_cistatic ssize_t idt_dbgfs_info_read(struct file *filp, char __user *ubuf,
23358c2ecf20Sopenharmony_ci				   size_t count, loff_t *offp)
23368c2ecf20Sopenharmony_ci{
23378c2ecf20Sopenharmony_ci	struct idt_ntb_dev *ndev = filp->private_data;
23388c2ecf20Sopenharmony_ci	unsigned char idx, pidx, cnt;
23398c2ecf20Sopenharmony_ci	unsigned long irqflags, mdeg;
23408c2ecf20Sopenharmony_ci	ssize_t ret = 0, off = 0;
23418c2ecf20Sopenharmony_ci	enum ntb_speed speed;
23428c2ecf20Sopenharmony_ci	enum ntb_width width;
23438c2ecf20Sopenharmony_ci	char *strbuf;
23448c2ecf20Sopenharmony_ci	size_t size;
23458c2ecf20Sopenharmony_ci	u32 data;
23468c2ecf20Sopenharmony_ci
23478c2ecf20Sopenharmony_ci	/* Lets limit the buffer size the way the Intel/AMD drivers do */
23488c2ecf20Sopenharmony_ci	size = min_t(size_t, count, 0x1000U);
23498c2ecf20Sopenharmony_ci
23508c2ecf20Sopenharmony_ci	/* Allocate the memory for the buffer */
23518c2ecf20Sopenharmony_ci	strbuf = kmalloc(size, GFP_KERNEL);
23528c2ecf20Sopenharmony_ci	if (strbuf == NULL)
23538c2ecf20Sopenharmony_ci		return -ENOMEM;
23548c2ecf20Sopenharmony_ci
23558c2ecf20Sopenharmony_ci	/* Put the data into the string buffer */
23568c2ecf20Sopenharmony_ci	off += scnprintf(strbuf + off, size - off,
23578c2ecf20Sopenharmony_ci		"\n\t\tIDT NTB device Information:\n\n");
23588c2ecf20Sopenharmony_ci
23598c2ecf20Sopenharmony_ci	/* General local device configurations */
23608c2ecf20Sopenharmony_ci	off += scnprintf(strbuf + off, size - off,
23618c2ecf20Sopenharmony_ci		"Local Port %hhu, Partition %hhu\n", ndev->port, ndev->part);
23628c2ecf20Sopenharmony_ci
23638c2ecf20Sopenharmony_ci	/* Peer ports information */
23648c2ecf20Sopenharmony_ci	off += scnprintf(strbuf + off, size - off, "Peers:\n");
23658c2ecf20Sopenharmony_ci	for (idx = 0; idx < ndev->peer_cnt; idx++) {
23668c2ecf20Sopenharmony_ci		off += scnprintf(strbuf + off, size - off,
23678c2ecf20Sopenharmony_ci			"\t%hhu. Port %hhu, Partition %hhu\n",
23688c2ecf20Sopenharmony_ci			idx, ndev->peers[idx].port, ndev->peers[idx].part);
23698c2ecf20Sopenharmony_ci	}
23708c2ecf20Sopenharmony_ci
23718c2ecf20Sopenharmony_ci	/* Links status */
23728c2ecf20Sopenharmony_ci	data = idt_ntb_link_is_up(&ndev->ntb, &speed, &width);
23738c2ecf20Sopenharmony_ci	off += scnprintf(strbuf + off, size - off,
23748c2ecf20Sopenharmony_ci		"NTB link status\t- 0x%08x, ", data);
23758c2ecf20Sopenharmony_ci	off += scnprintf(strbuf + off, size - off, "PCIe Gen %d x%d lanes\n",
23768c2ecf20Sopenharmony_ci		speed, width);
23778c2ecf20Sopenharmony_ci
23788c2ecf20Sopenharmony_ci	/* Mapping table entries */
23798c2ecf20Sopenharmony_ci	off += scnprintf(strbuf + off, size - off, "NTB Mapping Table:\n");
23808c2ecf20Sopenharmony_ci	for (idx = 0; idx < IDT_MTBL_ENTRY_CNT; idx++) {
23818c2ecf20Sopenharmony_ci		spin_lock_irqsave(&ndev->mtbl_lock, irqflags);
23828c2ecf20Sopenharmony_ci		idt_nt_write(ndev, IDT_NT_NTMTBLADDR, idx);
23838c2ecf20Sopenharmony_ci		data = idt_nt_read(ndev, IDT_NT_NTMTBLDATA);
23848c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ndev->mtbl_lock, irqflags);
23858c2ecf20Sopenharmony_ci
23868c2ecf20Sopenharmony_ci		/* Print valid entries only */
23878c2ecf20Sopenharmony_ci		if (data & IDT_NTMTBLDATA_VALID) {
23888c2ecf20Sopenharmony_ci			off += scnprintf(strbuf + off, size - off,
23898c2ecf20Sopenharmony_ci				"\t%hhu. Partition %d, Requester ID 0x%04x\n",
23908c2ecf20Sopenharmony_ci				idx, GET_FIELD(NTMTBLDATA_PART, data),
23918c2ecf20Sopenharmony_ci				GET_FIELD(NTMTBLDATA_REQID, data));
23928c2ecf20Sopenharmony_ci		}
23938c2ecf20Sopenharmony_ci	}
23948c2ecf20Sopenharmony_ci	off += scnprintf(strbuf + off, size - off, "\n");
23958c2ecf20Sopenharmony_ci
23968c2ecf20Sopenharmony_ci	/* Outbound memory windows information */
23978c2ecf20Sopenharmony_ci	off += scnprintf(strbuf + off, size - off,
23988c2ecf20Sopenharmony_ci		"Outbound Memory Windows:\n");
23998c2ecf20Sopenharmony_ci	for (idx = 0; idx < ndev->mw_cnt; idx += cnt) {
24008c2ecf20Sopenharmony_ci		data = ndev->mws[idx].type;
24018c2ecf20Sopenharmony_ci		cnt = idt_get_mw_count(data);
24028c2ecf20Sopenharmony_ci
24038c2ecf20Sopenharmony_ci		/* Print Memory Window information */
24048c2ecf20Sopenharmony_ci		if (data == IDT_MW_DIR)
24058c2ecf20Sopenharmony_ci			off += scnprintf(strbuf + off, size - off,
24068c2ecf20Sopenharmony_ci				"\t%hhu.\t", idx);
24078c2ecf20Sopenharmony_ci		else
24088c2ecf20Sopenharmony_ci			off += scnprintf(strbuf + off, size - off,
24098c2ecf20Sopenharmony_ci				"\t%hhu-%hhu.\t", idx, idx + cnt - 1);
24108c2ecf20Sopenharmony_ci
24118c2ecf20Sopenharmony_ci		off += scnprintf(strbuf + off, size - off, "%s BAR%hhu, ",
24128c2ecf20Sopenharmony_ci			idt_get_mw_name(data), ndev->mws[idx].bar);
24138c2ecf20Sopenharmony_ci
24148c2ecf20Sopenharmony_ci		off += scnprintf(strbuf + off, size - off,
24158c2ecf20Sopenharmony_ci			"Address align 0x%08llx, ", ndev->mws[idx].addr_align);
24168c2ecf20Sopenharmony_ci
24178c2ecf20Sopenharmony_ci		off += scnprintf(strbuf + off, size - off,
24188c2ecf20Sopenharmony_ci			"Size align 0x%08llx, Size max %llu\n",
24198c2ecf20Sopenharmony_ci			ndev->mws[idx].size_align, ndev->mws[idx].size_max);
24208c2ecf20Sopenharmony_ci	}
24218c2ecf20Sopenharmony_ci
24228c2ecf20Sopenharmony_ci	/* Inbound memory windows information */
24238c2ecf20Sopenharmony_ci	for (pidx = 0; pidx < ndev->peer_cnt; pidx++) {
24248c2ecf20Sopenharmony_ci		off += scnprintf(strbuf + off, size - off,
24258c2ecf20Sopenharmony_ci			"Inbound Memory Windows for peer %hhu (Port %hhu):\n",
24268c2ecf20Sopenharmony_ci			pidx, ndev->peers[pidx].port);
24278c2ecf20Sopenharmony_ci
24288c2ecf20Sopenharmony_ci		/* Print Memory Windows information */
24298c2ecf20Sopenharmony_ci		for (idx = 0; idx < ndev->peers[pidx].mw_cnt; idx += cnt) {
24308c2ecf20Sopenharmony_ci			data = ndev->peers[pidx].mws[idx].type;
24318c2ecf20Sopenharmony_ci			cnt = idt_get_mw_count(data);
24328c2ecf20Sopenharmony_ci
24338c2ecf20Sopenharmony_ci			if (data == IDT_MW_DIR)
24348c2ecf20Sopenharmony_ci				off += scnprintf(strbuf + off, size - off,
24358c2ecf20Sopenharmony_ci					"\t%hhu.\t", idx);
24368c2ecf20Sopenharmony_ci			else
24378c2ecf20Sopenharmony_ci				off += scnprintf(strbuf + off, size - off,
24388c2ecf20Sopenharmony_ci					"\t%hhu-%hhu.\t", idx, idx + cnt - 1);
24398c2ecf20Sopenharmony_ci
24408c2ecf20Sopenharmony_ci			off += scnprintf(strbuf + off, size - off,
24418c2ecf20Sopenharmony_ci				"%s BAR%hhu, ", idt_get_mw_name(data),
24428c2ecf20Sopenharmony_ci				ndev->peers[pidx].mws[idx].bar);
24438c2ecf20Sopenharmony_ci
24448c2ecf20Sopenharmony_ci			off += scnprintf(strbuf + off, size - off,
24458c2ecf20Sopenharmony_ci				"Address align 0x%08llx, ",
24468c2ecf20Sopenharmony_ci				ndev->peers[pidx].mws[idx].addr_align);
24478c2ecf20Sopenharmony_ci
24488c2ecf20Sopenharmony_ci			off += scnprintf(strbuf + off, size - off,
24498c2ecf20Sopenharmony_ci				"Size align 0x%08llx, Size max %llu\n",
24508c2ecf20Sopenharmony_ci				ndev->peers[pidx].mws[idx].size_align,
24518c2ecf20Sopenharmony_ci				ndev->peers[pidx].mws[idx].size_max);
24528c2ecf20Sopenharmony_ci		}
24538c2ecf20Sopenharmony_ci	}
24548c2ecf20Sopenharmony_ci	off += scnprintf(strbuf + off, size - off, "\n");
24558c2ecf20Sopenharmony_ci
24568c2ecf20Sopenharmony_ci	/* Doorbell information */
24578c2ecf20Sopenharmony_ci	data = idt_sw_read(ndev, IDT_SW_GDBELLSTS);
24588c2ecf20Sopenharmony_ci	off += scnprintf(strbuf + off, size - off,
24598c2ecf20Sopenharmony_ci		 "Global Doorbell state\t- 0x%08x\n", data);
24608c2ecf20Sopenharmony_ci	data = idt_ntb_db_read(&ndev->ntb);
24618c2ecf20Sopenharmony_ci	off += scnprintf(strbuf + off, size - off,
24628c2ecf20Sopenharmony_ci		 "Local  Doorbell state\t- 0x%08x\n", data);
24638c2ecf20Sopenharmony_ci	data = idt_nt_read(ndev, IDT_NT_INDBELLMSK);
24648c2ecf20Sopenharmony_ci	off += scnprintf(strbuf + off, size - off,
24658c2ecf20Sopenharmony_ci		 "Local  Doorbell mask\t- 0x%08x\n", data);
24668c2ecf20Sopenharmony_ci	off += scnprintf(strbuf + off, size - off, "\n");
24678c2ecf20Sopenharmony_ci
24688c2ecf20Sopenharmony_ci	/* Messaging information */
24698c2ecf20Sopenharmony_ci	off += scnprintf(strbuf + off, size - off,
24708c2ecf20Sopenharmony_ci		 "Message event valid\t- 0x%08x\n", IDT_MSG_MASK);
24718c2ecf20Sopenharmony_ci	data = idt_ntb_msg_read_sts(&ndev->ntb);
24728c2ecf20Sopenharmony_ci	off += scnprintf(strbuf + off, size - off,
24738c2ecf20Sopenharmony_ci		 "Message event status\t- 0x%08x\n", data);
24748c2ecf20Sopenharmony_ci	data = idt_nt_read(ndev, IDT_NT_MSGSTSMSK);
24758c2ecf20Sopenharmony_ci	off += scnprintf(strbuf + off, size - off,
24768c2ecf20Sopenharmony_ci		 "Message event mask\t- 0x%08x\n", data);
24778c2ecf20Sopenharmony_ci	off += scnprintf(strbuf + off, size - off,
24788c2ecf20Sopenharmony_ci		 "Message data:\n");
24798c2ecf20Sopenharmony_ci	for (idx = 0; idx < IDT_MSG_CNT; idx++) {
24808c2ecf20Sopenharmony_ci		int src;
24818c2ecf20Sopenharmony_ci		data = idt_ntb_msg_read(&ndev->ntb, &src, idx);
24828c2ecf20Sopenharmony_ci		off += scnprintf(strbuf + off, size - off,
24838c2ecf20Sopenharmony_ci			"\t%hhu. 0x%08x from peer %hhu (Port %hhu)\n",
24848c2ecf20Sopenharmony_ci			idx, data, src, ndev->peers[src].port);
24858c2ecf20Sopenharmony_ci	}
24868c2ecf20Sopenharmony_ci	off += scnprintf(strbuf + off, size - off, "\n");
24878c2ecf20Sopenharmony_ci
24888c2ecf20Sopenharmony_ci	/* Current temperature */
24898c2ecf20Sopenharmony_ci	idt_read_temp(ndev, IDT_TEMP_CUR, &mdeg);
24908c2ecf20Sopenharmony_ci	off += scnprintf(strbuf + off, size - off,
24918c2ecf20Sopenharmony_ci		"Switch temperature\t\t- %hhd.%hhuC\n",
24928c2ecf20Sopenharmony_ci		idt_get_deg(mdeg), idt_get_deg_frac(mdeg));
24938c2ecf20Sopenharmony_ci
24948c2ecf20Sopenharmony_ci	/* Copy the buffer to the User Space */
24958c2ecf20Sopenharmony_ci	ret = simple_read_from_buffer(ubuf, count, offp, strbuf, off);
24968c2ecf20Sopenharmony_ci	kfree(strbuf);
24978c2ecf20Sopenharmony_ci
24988c2ecf20Sopenharmony_ci	return ret;
24998c2ecf20Sopenharmony_ci}
25008c2ecf20Sopenharmony_ci
25018c2ecf20Sopenharmony_ci/*
25028c2ecf20Sopenharmony_ci * idt_init_dbgfs() - initialize DebugFS node
25038c2ecf20Sopenharmony_ci * @ndev:	IDT NTB hardware driver descriptor
25048c2ecf20Sopenharmony_ci *
25058c2ecf20Sopenharmony_ci * Return: zero on success, otherwise a negative error number.
25068c2ecf20Sopenharmony_ci */
25078c2ecf20Sopenharmony_cistatic int idt_init_dbgfs(struct idt_ntb_dev *ndev)
25088c2ecf20Sopenharmony_ci{
25098c2ecf20Sopenharmony_ci	char devname[64];
25108c2ecf20Sopenharmony_ci
25118c2ecf20Sopenharmony_ci	/* If the top directory is not created then do nothing */
25128c2ecf20Sopenharmony_ci	if (IS_ERR_OR_NULL(dbgfs_topdir)) {
25138c2ecf20Sopenharmony_ci		dev_info(&ndev->ntb.pdev->dev, "Top DebugFS directory absent");
25148c2ecf20Sopenharmony_ci		return PTR_ERR(dbgfs_topdir);
25158c2ecf20Sopenharmony_ci	}
25168c2ecf20Sopenharmony_ci
25178c2ecf20Sopenharmony_ci	/* Create the info file node */
25188c2ecf20Sopenharmony_ci	snprintf(devname, 64, "info:%s", pci_name(ndev->ntb.pdev));
25198c2ecf20Sopenharmony_ci	ndev->dbgfs_info = debugfs_create_file(devname, 0400, dbgfs_topdir,
25208c2ecf20Sopenharmony_ci		ndev, &idt_dbgfs_info_ops);
25218c2ecf20Sopenharmony_ci	if (IS_ERR(ndev->dbgfs_info)) {
25228c2ecf20Sopenharmony_ci		dev_dbg(&ndev->ntb.pdev->dev, "Failed to create DebugFS node");
25238c2ecf20Sopenharmony_ci		return PTR_ERR(ndev->dbgfs_info);
25248c2ecf20Sopenharmony_ci	}
25258c2ecf20Sopenharmony_ci
25268c2ecf20Sopenharmony_ci	dev_dbg(&ndev->ntb.pdev->dev, "NTB device DebugFS node created");
25278c2ecf20Sopenharmony_ci
25288c2ecf20Sopenharmony_ci	return 0;
25298c2ecf20Sopenharmony_ci}
25308c2ecf20Sopenharmony_ci
25318c2ecf20Sopenharmony_ci/*
25328c2ecf20Sopenharmony_ci * idt_deinit_dbgfs() - deinitialize DebugFS node
25338c2ecf20Sopenharmony_ci * @ndev:	IDT NTB hardware driver descriptor
25348c2ecf20Sopenharmony_ci *
25358c2ecf20Sopenharmony_ci * Just discard the info node from DebugFS
25368c2ecf20Sopenharmony_ci */
25378c2ecf20Sopenharmony_cistatic void idt_deinit_dbgfs(struct idt_ntb_dev *ndev)
25388c2ecf20Sopenharmony_ci{
25398c2ecf20Sopenharmony_ci	debugfs_remove(ndev->dbgfs_info);
25408c2ecf20Sopenharmony_ci
25418c2ecf20Sopenharmony_ci	dev_dbg(&ndev->ntb.pdev->dev, "NTB device DebugFS node discarded");
25428c2ecf20Sopenharmony_ci}
25438c2ecf20Sopenharmony_ci
25448c2ecf20Sopenharmony_ci/*=============================================================================
25458c2ecf20Sopenharmony_ci *                     11. Basic PCIe device initialization
25468c2ecf20Sopenharmony_ci *=============================================================================
25478c2ecf20Sopenharmony_ci */
25488c2ecf20Sopenharmony_ci
25498c2ecf20Sopenharmony_ci/*
25508c2ecf20Sopenharmony_ci * idt_check_setup() - Check whether the IDT PCIe-swtich is properly
25518c2ecf20Sopenharmony_ci *		       pre-initialized
25528c2ecf20Sopenharmony_ci * @pdev:	Pointer to the PCI device descriptor
25538c2ecf20Sopenharmony_ci *
25548c2ecf20Sopenharmony_ci * Return: zero on success, otherwise a negative error number.
25558c2ecf20Sopenharmony_ci */
25568c2ecf20Sopenharmony_cistatic int idt_check_setup(struct pci_dev *pdev)
25578c2ecf20Sopenharmony_ci{
25588c2ecf20Sopenharmony_ci	u32 data;
25598c2ecf20Sopenharmony_ci	int ret;
25608c2ecf20Sopenharmony_ci
25618c2ecf20Sopenharmony_ci	/* Read the BARSETUP0 */
25628c2ecf20Sopenharmony_ci	ret = pci_read_config_dword(pdev, IDT_NT_BARSETUP0, &data);
25638c2ecf20Sopenharmony_ci	if (ret != 0) {
25648c2ecf20Sopenharmony_ci		dev_err(&pdev->dev,
25658c2ecf20Sopenharmony_ci			"Failed to read BARSETUP0 config register");
25668c2ecf20Sopenharmony_ci		return ret;
25678c2ecf20Sopenharmony_ci	}
25688c2ecf20Sopenharmony_ci
25698c2ecf20Sopenharmony_ci	/* Check whether the BAR0 register is enabled to be of config space */
25708c2ecf20Sopenharmony_ci	if (!(data & IDT_BARSETUP_EN) || !(data & IDT_BARSETUP_MODE_CFG)) {
25718c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "BAR0 doesn't map config space");
25728c2ecf20Sopenharmony_ci		return -EINVAL;
25738c2ecf20Sopenharmony_ci	}
25748c2ecf20Sopenharmony_ci
25758c2ecf20Sopenharmony_ci	/* Configuration space BAR0 must have certain size */
25768c2ecf20Sopenharmony_ci	if ((data & IDT_BARSETUP_SIZE_MASK) != IDT_BARSETUP_SIZE_CFG) {
25778c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Invalid size of config space");
25788c2ecf20Sopenharmony_ci		return -EINVAL;
25798c2ecf20Sopenharmony_ci	}
25808c2ecf20Sopenharmony_ci
25818c2ecf20Sopenharmony_ci	dev_dbg(&pdev->dev, "NTB device pre-initialized correctly");
25828c2ecf20Sopenharmony_ci
25838c2ecf20Sopenharmony_ci	return 0;
25848c2ecf20Sopenharmony_ci}
25858c2ecf20Sopenharmony_ci
25868c2ecf20Sopenharmony_ci/*
25878c2ecf20Sopenharmony_ci * Create the IDT PCIe-switch driver descriptor
25888c2ecf20Sopenharmony_ci * @pdev:	Pointer to the PCI device descriptor
25898c2ecf20Sopenharmony_ci * @id:		IDT PCIe-device configuration
25908c2ecf20Sopenharmony_ci *
25918c2ecf20Sopenharmony_ci * It just allocates a memory for IDT PCIe-switch device structure and
25928c2ecf20Sopenharmony_ci * initializes some commonly used fields.
25938c2ecf20Sopenharmony_ci *
25948c2ecf20Sopenharmony_ci * No need of release method, since managed device resource is used for
25958c2ecf20Sopenharmony_ci * memory allocation.
25968c2ecf20Sopenharmony_ci *
25978c2ecf20Sopenharmony_ci * Return: pointer to the descriptor, otherwise a negative error number.
25988c2ecf20Sopenharmony_ci */
25998c2ecf20Sopenharmony_cistatic struct idt_ntb_dev *idt_create_dev(struct pci_dev *pdev,
26008c2ecf20Sopenharmony_ci					  const struct pci_device_id *id)
26018c2ecf20Sopenharmony_ci{
26028c2ecf20Sopenharmony_ci	struct idt_ntb_dev *ndev;
26038c2ecf20Sopenharmony_ci
26048c2ecf20Sopenharmony_ci	/* Allocate memory for the IDT PCIe-device descriptor */
26058c2ecf20Sopenharmony_ci	ndev = devm_kzalloc(&pdev->dev, sizeof(*ndev), GFP_KERNEL);
26068c2ecf20Sopenharmony_ci	if (!ndev) {
26078c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Memory allocation failed for descriptor");
26088c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
26098c2ecf20Sopenharmony_ci	}
26108c2ecf20Sopenharmony_ci
26118c2ecf20Sopenharmony_ci	/* Save the IDT PCIe-switch ports configuration */
26128c2ecf20Sopenharmony_ci	ndev->swcfg = (struct idt_89hpes_cfg *)id->driver_data;
26138c2ecf20Sopenharmony_ci	/* Save the PCI-device pointer inside the NTB device structure */
26148c2ecf20Sopenharmony_ci	ndev->ntb.pdev = pdev;
26158c2ecf20Sopenharmony_ci
26168c2ecf20Sopenharmony_ci	/* Initialize spin locker of Doorbell, Message and GASA registers */
26178c2ecf20Sopenharmony_ci	spin_lock_init(&ndev->db_mask_lock);
26188c2ecf20Sopenharmony_ci	spin_lock_init(&ndev->msg_mask_lock);
26198c2ecf20Sopenharmony_ci	spin_lock_init(&ndev->gasa_lock);
26208c2ecf20Sopenharmony_ci
26218c2ecf20Sopenharmony_ci	dev_info(&pdev->dev, "IDT %s discovered", ndev->swcfg->name);
26228c2ecf20Sopenharmony_ci
26238c2ecf20Sopenharmony_ci	dev_dbg(&pdev->dev, "NTB device descriptor created");
26248c2ecf20Sopenharmony_ci
26258c2ecf20Sopenharmony_ci	return ndev;
26268c2ecf20Sopenharmony_ci}
26278c2ecf20Sopenharmony_ci
26288c2ecf20Sopenharmony_ci/*
26298c2ecf20Sopenharmony_ci * idt_init_pci() - initialize the basic PCI-related subsystem
26308c2ecf20Sopenharmony_ci * @ndev:	Pointer to the IDT PCIe-switch driver descriptor
26318c2ecf20Sopenharmony_ci *
26328c2ecf20Sopenharmony_ci * Managed device resources will be freed automatically in case of failure or
26338c2ecf20Sopenharmony_ci * driver detachment.
26348c2ecf20Sopenharmony_ci *
26358c2ecf20Sopenharmony_ci * Return: zero on success, otherwise negative error number.
26368c2ecf20Sopenharmony_ci */
26378c2ecf20Sopenharmony_cistatic int idt_init_pci(struct idt_ntb_dev *ndev)
26388c2ecf20Sopenharmony_ci{
26398c2ecf20Sopenharmony_ci	struct pci_dev *pdev = ndev->ntb.pdev;
26408c2ecf20Sopenharmony_ci	int ret;
26418c2ecf20Sopenharmony_ci
26428c2ecf20Sopenharmony_ci	/* Initialize the bit mask of PCI/NTB DMA */
26438c2ecf20Sopenharmony_ci	ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(64));
26448c2ecf20Sopenharmony_ci	if (ret != 0) {
26458c2ecf20Sopenharmony_ci		ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
26468c2ecf20Sopenharmony_ci		if (ret != 0) {
26478c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "Failed to set DMA bit mask\n");
26488c2ecf20Sopenharmony_ci			return ret;
26498c2ecf20Sopenharmony_ci		}
26508c2ecf20Sopenharmony_ci		dev_warn(&pdev->dev, "Cannot set DMA highmem bit mask\n");
26518c2ecf20Sopenharmony_ci	}
26528c2ecf20Sopenharmony_ci	ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));
26538c2ecf20Sopenharmony_ci	if (ret != 0) {
26548c2ecf20Sopenharmony_ci		ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
26558c2ecf20Sopenharmony_ci		if (ret != 0) {
26568c2ecf20Sopenharmony_ci			dev_err(&pdev->dev,
26578c2ecf20Sopenharmony_ci				"Failed to set consistent DMA bit mask\n");
26588c2ecf20Sopenharmony_ci			return ret;
26598c2ecf20Sopenharmony_ci		}
26608c2ecf20Sopenharmony_ci		dev_warn(&pdev->dev,
26618c2ecf20Sopenharmony_ci			"Cannot set consistent DMA highmem bit mask\n");
26628c2ecf20Sopenharmony_ci	}
26638c2ecf20Sopenharmony_ci
26648c2ecf20Sopenharmony_ci	/*
26658c2ecf20Sopenharmony_ci	 * Enable the device advanced error reporting. It's not critical to
26668c2ecf20Sopenharmony_ci	 * have AER disabled in the kernel.
26678c2ecf20Sopenharmony_ci	 */
26688c2ecf20Sopenharmony_ci	ret = pci_enable_pcie_error_reporting(pdev);
26698c2ecf20Sopenharmony_ci	if (ret != 0)
26708c2ecf20Sopenharmony_ci		dev_warn(&pdev->dev, "PCIe AER capability disabled\n");
26718c2ecf20Sopenharmony_ci	else /* Cleanup nonfatal error status before getting to init */
26728c2ecf20Sopenharmony_ci		pci_aer_clear_nonfatal_status(pdev);
26738c2ecf20Sopenharmony_ci
26748c2ecf20Sopenharmony_ci	/* First enable the PCI device */
26758c2ecf20Sopenharmony_ci	ret = pcim_enable_device(pdev);
26768c2ecf20Sopenharmony_ci	if (ret != 0) {
26778c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Failed to enable PCIe device\n");
26788c2ecf20Sopenharmony_ci		goto err_disable_aer;
26798c2ecf20Sopenharmony_ci	}
26808c2ecf20Sopenharmony_ci
26818c2ecf20Sopenharmony_ci	/*
26828c2ecf20Sopenharmony_ci	 * Enable the bus mastering, which effectively enables MSI IRQs and
26838c2ecf20Sopenharmony_ci	 * Request TLPs translation
26848c2ecf20Sopenharmony_ci	 */
26858c2ecf20Sopenharmony_ci	pci_set_master(pdev);
26868c2ecf20Sopenharmony_ci
26878c2ecf20Sopenharmony_ci	/* Request all BARs resources and map BAR0 only */
26888c2ecf20Sopenharmony_ci	ret = pcim_iomap_regions_request_all(pdev, 1, NTB_NAME);
26898c2ecf20Sopenharmony_ci	if (ret != 0) {
26908c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Failed to request resources\n");
26918c2ecf20Sopenharmony_ci		goto err_clear_master;
26928c2ecf20Sopenharmony_ci	}
26938c2ecf20Sopenharmony_ci
26948c2ecf20Sopenharmony_ci	/* Retrieve virtual address of BAR0 - PCI configuration space */
26958c2ecf20Sopenharmony_ci	ndev->cfgspc = pcim_iomap_table(pdev)[0];
26968c2ecf20Sopenharmony_ci
26978c2ecf20Sopenharmony_ci	/* Put the IDT driver data pointer to the PCI-device private pointer */
26988c2ecf20Sopenharmony_ci	pci_set_drvdata(pdev, ndev);
26998c2ecf20Sopenharmony_ci
27008c2ecf20Sopenharmony_ci	dev_dbg(&pdev->dev, "NT-function PCIe interface initialized");
27018c2ecf20Sopenharmony_ci
27028c2ecf20Sopenharmony_ci	return 0;
27038c2ecf20Sopenharmony_ci
27048c2ecf20Sopenharmony_cierr_clear_master:
27058c2ecf20Sopenharmony_ci	pci_clear_master(pdev);
27068c2ecf20Sopenharmony_cierr_disable_aer:
27078c2ecf20Sopenharmony_ci	(void)pci_disable_pcie_error_reporting(pdev);
27088c2ecf20Sopenharmony_ci
27098c2ecf20Sopenharmony_ci	return ret;
27108c2ecf20Sopenharmony_ci}
27118c2ecf20Sopenharmony_ci
27128c2ecf20Sopenharmony_ci/*
27138c2ecf20Sopenharmony_ci * idt_deinit_pci() - deinitialize the basic PCI-related subsystem
27148c2ecf20Sopenharmony_ci * @ndev:	Pointer to the IDT PCIe-switch driver descriptor
27158c2ecf20Sopenharmony_ci *
27168c2ecf20Sopenharmony_ci * Managed resources will be freed on the driver detachment
27178c2ecf20Sopenharmony_ci */
27188c2ecf20Sopenharmony_cistatic void idt_deinit_pci(struct idt_ntb_dev *ndev)
27198c2ecf20Sopenharmony_ci{
27208c2ecf20Sopenharmony_ci	struct pci_dev *pdev = ndev->ntb.pdev;
27218c2ecf20Sopenharmony_ci
27228c2ecf20Sopenharmony_ci	/* Clean up the PCI-device private data pointer */
27238c2ecf20Sopenharmony_ci	pci_set_drvdata(pdev, NULL);
27248c2ecf20Sopenharmony_ci
27258c2ecf20Sopenharmony_ci	/* Clear the bus master disabling the Request TLPs translation */
27268c2ecf20Sopenharmony_ci	pci_clear_master(pdev);
27278c2ecf20Sopenharmony_ci
27288c2ecf20Sopenharmony_ci	/* Disable the AER capability */
27298c2ecf20Sopenharmony_ci	(void)pci_disable_pcie_error_reporting(pdev);
27308c2ecf20Sopenharmony_ci
27318c2ecf20Sopenharmony_ci	dev_dbg(&pdev->dev, "NT-function PCIe interface cleared");
27328c2ecf20Sopenharmony_ci}
27338c2ecf20Sopenharmony_ci
27348c2ecf20Sopenharmony_ci/*===========================================================================
27358c2ecf20Sopenharmony_ci *                       12. PCI bus callback functions
27368c2ecf20Sopenharmony_ci *===========================================================================
27378c2ecf20Sopenharmony_ci */
27388c2ecf20Sopenharmony_ci
27398c2ecf20Sopenharmony_ci/*
27408c2ecf20Sopenharmony_ci * idt_pci_probe() - PCI device probe callback
27418c2ecf20Sopenharmony_ci * @pdev:	Pointer to PCI device structure
27428c2ecf20Sopenharmony_ci * @id:		PCIe device custom descriptor
27438c2ecf20Sopenharmony_ci *
27448c2ecf20Sopenharmony_ci * Return: zero on success, otherwise negative error number
27458c2ecf20Sopenharmony_ci */
27468c2ecf20Sopenharmony_cistatic int idt_pci_probe(struct pci_dev *pdev,
27478c2ecf20Sopenharmony_ci			 const struct pci_device_id *id)
27488c2ecf20Sopenharmony_ci{
27498c2ecf20Sopenharmony_ci	struct idt_ntb_dev *ndev;
27508c2ecf20Sopenharmony_ci	int ret;
27518c2ecf20Sopenharmony_ci
27528c2ecf20Sopenharmony_ci	/* Check whether IDT PCIe-switch is properly pre-initialized */
27538c2ecf20Sopenharmony_ci	ret = idt_check_setup(pdev);
27548c2ecf20Sopenharmony_ci	if (ret != 0)
27558c2ecf20Sopenharmony_ci		return ret;
27568c2ecf20Sopenharmony_ci
27578c2ecf20Sopenharmony_ci	/* Allocate the memory for IDT NTB device data */
27588c2ecf20Sopenharmony_ci	ndev = idt_create_dev(pdev, id);
27598c2ecf20Sopenharmony_ci	if (IS_ERR_OR_NULL(ndev))
27608c2ecf20Sopenharmony_ci		return PTR_ERR(ndev);
27618c2ecf20Sopenharmony_ci
27628c2ecf20Sopenharmony_ci	/* Initialize the basic PCI subsystem of the device */
27638c2ecf20Sopenharmony_ci	ret = idt_init_pci(ndev);
27648c2ecf20Sopenharmony_ci	if (ret != 0)
27658c2ecf20Sopenharmony_ci		return ret;
27668c2ecf20Sopenharmony_ci
27678c2ecf20Sopenharmony_ci	/* Scan ports of the IDT PCIe-switch */
27688c2ecf20Sopenharmony_ci	(void)idt_scan_ports(ndev);
27698c2ecf20Sopenharmony_ci
27708c2ecf20Sopenharmony_ci	/* Initialize NTB link events subsystem */
27718c2ecf20Sopenharmony_ci	idt_init_link(ndev);
27728c2ecf20Sopenharmony_ci
27738c2ecf20Sopenharmony_ci	/* Initialize MWs subsystem */
27748c2ecf20Sopenharmony_ci	ret = idt_init_mws(ndev);
27758c2ecf20Sopenharmony_ci	if (ret != 0)
27768c2ecf20Sopenharmony_ci		goto err_deinit_link;
27778c2ecf20Sopenharmony_ci
27788c2ecf20Sopenharmony_ci	/* Initialize Messaging subsystem */
27798c2ecf20Sopenharmony_ci	idt_init_msg(ndev);
27808c2ecf20Sopenharmony_ci
27818c2ecf20Sopenharmony_ci	/* Initialize hwmon interface */
27828c2ecf20Sopenharmony_ci	idt_init_temp(ndev);
27838c2ecf20Sopenharmony_ci
27848c2ecf20Sopenharmony_ci	/* Initialize IDT interrupts handler */
27858c2ecf20Sopenharmony_ci	ret = idt_init_isr(ndev);
27868c2ecf20Sopenharmony_ci	if (ret != 0)
27878c2ecf20Sopenharmony_ci		goto err_deinit_link;
27888c2ecf20Sopenharmony_ci
27898c2ecf20Sopenharmony_ci	/* Register IDT NTB devices on the NTB bus */
27908c2ecf20Sopenharmony_ci	ret = idt_register_device(ndev);
27918c2ecf20Sopenharmony_ci	if (ret != 0)
27928c2ecf20Sopenharmony_ci		goto err_deinit_isr;
27938c2ecf20Sopenharmony_ci
27948c2ecf20Sopenharmony_ci	/* Initialize DebugFS info node */
27958c2ecf20Sopenharmony_ci	(void)idt_init_dbgfs(ndev);
27968c2ecf20Sopenharmony_ci
27978c2ecf20Sopenharmony_ci	/* IDT PCIe-switch NTB driver is finally initialized */
27988c2ecf20Sopenharmony_ci	dev_info(&pdev->dev, "IDT NTB device is ready");
27998c2ecf20Sopenharmony_ci
28008c2ecf20Sopenharmony_ci	/* May the force be with us... */
28018c2ecf20Sopenharmony_ci	return 0;
28028c2ecf20Sopenharmony_ci
28038c2ecf20Sopenharmony_cierr_deinit_isr:
28048c2ecf20Sopenharmony_ci	idt_deinit_isr(ndev);
28058c2ecf20Sopenharmony_cierr_deinit_link:
28068c2ecf20Sopenharmony_ci	idt_deinit_link(ndev);
28078c2ecf20Sopenharmony_ci	idt_deinit_pci(ndev);
28088c2ecf20Sopenharmony_ci
28098c2ecf20Sopenharmony_ci	return ret;
28108c2ecf20Sopenharmony_ci}
28118c2ecf20Sopenharmony_ci
28128c2ecf20Sopenharmony_ci/*
28138c2ecf20Sopenharmony_ci * idt_pci_probe() - PCI device remove callback
28148c2ecf20Sopenharmony_ci * @pdev:	Pointer to PCI device structure
28158c2ecf20Sopenharmony_ci */
28168c2ecf20Sopenharmony_cistatic void idt_pci_remove(struct pci_dev *pdev)
28178c2ecf20Sopenharmony_ci{
28188c2ecf20Sopenharmony_ci	struct idt_ntb_dev *ndev = pci_get_drvdata(pdev);
28198c2ecf20Sopenharmony_ci
28208c2ecf20Sopenharmony_ci	/* Deinit the DebugFS node */
28218c2ecf20Sopenharmony_ci	idt_deinit_dbgfs(ndev);
28228c2ecf20Sopenharmony_ci
28238c2ecf20Sopenharmony_ci	/* Unregister NTB device */
28248c2ecf20Sopenharmony_ci	idt_unregister_device(ndev);
28258c2ecf20Sopenharmony_ci
28268c2ecf20Sopenharmony_ci	/* Stop the interrupts handling */
28278c2ecf20Sopenharmony_ci	idt_deinit_isr(ndev);
28288c2ecf20Sopenharmony_ci
28298c2ecf20Sopenharmony_ci	/* Deinitialize link event subsystem */
28308c2ecf20Sopenharmony_ci	idt_deinit_link(ndev);
28318c2ecf20Sopenharmony_ci
28328c2ecf20Sopenharmony_ci	/* Deinit basic PCI subsystem */
28338c2ecf20Sopenharmony_ci	idt_deinit_pci(ndev);
28348c2ecf20Sopenharmony_ci
28358c2ecf20Sopenharmony_ci	/* IDT PCIe-switch NTB driver is finally initialized */
28368c2ecf20Sopenharmony_ci	dev_info(&pdev->dev, "IDT NTB device is removed");
28378c2ecf20Sopenharmony_ci
28388c2ecf20Sopenharmony_ci	/* Sayonara... */
28398c2ecf20Sopenharmony_ci}
28408c2ecf20Sopenharmony_ci
28418c2ecf20Sopenharmony_ci/*
28428c2ecf20Sopenharmony_ci * IDT PCIe-switch models ports configuration structures
28438c2ecf20Sopenharmony_ci */
28448c2ecf20Sopenharmony_cistatic const struct idt_89hpes_cfg idt_89hpes24nt6ag2_config = {
28458c2ecf20Sopenharmony_ci	.name = "89HPES24NT6AG2",
28468c2ecf20Sopenharmony_ci	.port_cnt = 6, .ports = {0, 2, 4, 6, 8, 12}
28478c2ecf20Sopenharmony_ci};
28488c2ecf20Sopenharmony_cistatic const struct idt_89hpes_cfg idt_89hpes32nt8ag2_config = {
28498c2ecf20Sopenharmony_ci	.name = "89HPES32NT8AG2",
28508c2ecf20Sopenharmony_ci	.port_cnt = 8, .ports = {0, 2, 4, 6, 8, 12, 16, 20}
28518c2ecf20Sopenharmony_ci};
28528c2ecf20Sopenharmony_cistatic const struct idt_89hpes_cfg idt_89hpes32nt8bg2_config = {
28538c2ecf20Sopenharmony_ci	.name = "89HPES32NT8BG2",
28548c2ecf20Sopenharmony_ci	.port_cnt = 8, .ports = {0, 2, 4, 6, 8, 12, 16, 20}
28558c2ecf20Sopenharmony_ci};
28568c2ecf20Sopenharmony_cistatic const struct idt_89hpes_cfg idt_89hpes12nt12g2_config = {
28578c2ecf20Sopenharmony_ci	.name = "89HPES12NT12G2",
28588c2ecf20Sopenharmony_ci	.port_cnt = 3, .ports = {0, 8, 16}
28598c2ecf20Sopenharmony_ci};
28608c2ecf20Sopenharmony_cistatic const struct idt_89hpes_cfg idt_89hpes16nt16g2_config = {
28618c2ecf20Sopenharmony_ci	.name = "89HPES16NT16G2",
28628c2ecf20Sopenharmony_ci	.port_cnt = 4, .ports = {0, 8, 12, 16}
28638c2ecf20Sopenharmony_ci};
28648c2ecf20Sopenharmony_cistatic const struct idt_89hpes_cfg idt_89hpes24nt24g2_config = {
28658c2ecf20Sopenharmony_ci	.name = "89HPES24NT24G2",
28668c2ecf20Sopenharmony_ci	.port_cnt = 8, .ports = {0, 2, 4, 6, 8, 12, 16, 20}
28678c2ecf20Sopenharmony_ci};
28688c2ecf20Sopenharmony_cistatic const struct idt_89hpes_cfg idt_89hpes32nt24ag2_config = {
28698c2ecf20Sopenharmony_ci	.name = "89HPES32NT24AG2",
28708c2ecf20Sopenharmony_ci	.port_cnt = 8, .ports = {0, 2, 4, 6, 8, 12, 16, 20}
28718c2ecf20Sopenharmony_ci};
28728c2ecf20Sopenharmony_cistatic const struct idt_89hpes_cfg idt_89hpes32nt24bg2_config = {
28738c2ecf20Sopenharmony_ci	.name = "89HPES32NT24BG2",
28748c2ecf20Sopenharmony_ci	.port_cnt = 8, .ports = {0, 2, 4, 6, 8, 12, 16, 20}
28758c2ecf20Sopenharmony_ci};
28768c2ecf20Sopenharmony_ci
28778c2ecf20Sopenharmony_ci/*
28788c2ecf20Sopenharmony_ci * PCI-ids table of the supported IDT PCIe-switch devices
28798c2ecf20Sopenharmony_ci */
28808c2ecf20Sopenharmony_cistatic const struct pci_device_id idt_pci_tbl[] = {
28818c2ecf20Sopenharmony_ci	{IDT_PCI_DEVICE_IDS(89HPES24NT6AG2,  idt_89hpes24nt6ag2_config)},
28828c2ecf20Sopenharmony_ci	{IDT_PCI_DEVICE_IDS(89HPES32NT8AG2,  idt_89hpes32nt8ag2_config)},
28838c2ecf20Sopenharmony_ci	{IDT_PCI_DEVICE_IDS(89HPES32NT8BG2,  idt_89hpes32nt8bg2_config)},
28848c2ecf20Sopenharmony_ci	{IDT_PCI_DEVICE_IDS(89HPES12NT12G2,  idt_89hpes12nt12g2_config)},
28858c2ecf20Sopenharmony_ci	{IDT_PCI_DEVICE_IDS(89HPES16NT16G2,  idt_89hpes16nt16g2_config)},
28868c2ecf20Sopenharmony_ci	{IDT_PCI_DEVICE_IDS(89HPES24NT24G2,  idt_89hpes24nt24g2_config)},
28878c2ecf20Sopenharmony_ci	{IDT_PCI_DEVICE_IDS(89HPES32NT24AG2, idt_89hpes32nt24ag2_config)},
28888c2ecf20Sopenharmony_ci	{IDT_PCI_DEVICE_IDS(89HPES32NT24BG2, idt_89hpes32nt24bg2_config)},
28898c2ecf20Sopenharmony_ci	{0}
28908c2ecf20Sopenharmony_ci};
28918c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, idt_pci_tbl);
28928c2ecf20Sopenharmony_ci
28938c2ecf20Sopenharmony_ci/*
28948c2ecf20Sopenharmony_ci * IDT PCIe-switch NT-function device driver structure definition
28958c2ecf20Sopenharmony_ci */
28968c2ecf20Sopenharmony_cistatic struct pci_driver idt_pci_driver = {
28978c2ecf20Sopenharmony_ci	.name		= KBUILD_MODNAME,
28988c2ecf20Sopenharmony_ci	.probe		= idt_pci_probe,
28998c2ecf20Sopenharmony_ci	.remove		= idt_pci_remove,
29008c2ecf20Sopenharmony_ci	.id_table	= idt_pci_tbl,
29018c2ecf20Sopenharmony_ci};
29028c2ecf20Sopenharmony_ci
29038c2ecf20Sopenharmony_cistatic int __init idt_pci_driver_init(void)
29048c2ecf20Sopenharmony_ci{
29058c2ecf20Sopenharmony_ci	int ret;
29068c2ecf20Sopenharmony_ci	pr_info("%s %s\n", NTB_DESC, NTB_VER);
29078c2ecf20Sopenharmony_ci
29088c2ecf20Sopenharmony_ci	/* Create the top DebugFS directory if the FS is initialized */
29098c2ecf20Sopenharmony_ci	if (debugfs_initialized())
29108c2ecf20Sopenharmony_ci		dbgfs_topdir = debugfs_create_dir(KBUILD_MODNAME, NULL);
29118c2ecf20Sopenharmony_ci
29128c2ecf20Sopenharmony_ci	/* Register the NTB hardware driver to handle the PCI device */
29138c2ecf20Sopenharmony_ci	ret = pci_register_driver(&idt_pci_driver);
29148c2ecf20Sopenharmony_ci	if (ret)
29158c2ecf20Sopenharmony_ci		debugfs_remove_recursive(dbgfs_topdir);
29168c2ecf20Sopenharmony_ci
29178c2ecf20Sopenharmony_ci	return ret;
29188c2ecf20Sopenharmony_ci}
29198c2ecf20Sopenharmony_cimodule_init(idt_pci_driver_init);
29208c2ecf20Sopenharmony_ci
29218c2ecf20Sopenharmony_cistatic void __exit idt_pci_driver_exit(void)
29228c2ecf20Sopenharmony_ci{
29238c2ecf20Sopenharmony_ci	/* Unregister the NTB hardware driver */
29248c2ecf20Sopenharmony_ci	pci_unregister_driver(&idt_pci_driver);
29258c2ecf20Sopenharmony_ci
29268c2ecf20Sopenharmony_ci	/* Discard the top DebugFS directory */
29278c2ecf20Sopenharmony_ci	debugfs_remove_recursive(dbgfs_topdir);
29288c2ecf20Sopenharmony_ci}
29298c2ecf20Sopenharmony_cimodule_exit(idt_pci_driver_exit);
29308c2ecf20Sopenharmony_ci
2931