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