162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * This file is provided under a GPLv2 license. When using or 362306a36Sopenharmony_ci * redistributing this file, you may do so under that license. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * GPL LICENSE SUMMARY 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (C) 2016-2018 T-Platforms JSC All Rights Reserved. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or modify it 1062306a36Sopenharmony_ci * under the terms and conditions of the GNU General Public License, 1162306a36Sopenharmony_ci * version 2, as published by the Free Software Foundation. 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * This program is distributed in the hope that it will be useful, but 1462306a36Sopenharmony_ci * WITHOUT ANY WARRANTY; without even the implied warranty of 1562306a36Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 1662306a36Sopenharmony_ci * Public License for more details. 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * You should have received a copy of the GNU General Public License along 1962306a36Sopenharmony_ci * with this program; if not, one can be found http://www.gnu.org/licenses/. 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * The full GNU General Public License is included in this distribution in 2262306a36Sopenharmony_ci * the file called "COPYING". 2362306a36Sopenharmony_ci * 2462306a36Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 2562306a36Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 2662306a36Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 2762306a36Sopenharmony_ci * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 2862306a36Sopenharmony_ci * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 2962306a36Sopenharmony_ci * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 3062306a36Sopenharmony_ci * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 3162306a36Sopenharmony_ci * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 3262306a36Sopenharmony_ci * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3362306a36Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 3462306a36Sopenharmony_ci * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3562306a36Sopenharmony_ci * 3662306a36Sopenharmony_ci * IDT PCIe-switch NTB Linux driver 3762306a36Sopenharmony_ci * 3862306a36Sopenharmony_ci * Contact Information: 3962306a36Sopenharmony_ci * Serge Semin <fancer.lancer@gmail.com>, <Sergey.Semin@t-platforms.ru> 4062306a36Sopenharmony_ci */ 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#include <linux/stddef.h> 4362306a36Sopenharmony_ci#include <linux/types.h> 4462306a36Sopenharmony_ci#include <linux/kernel.h> 4562306a36Sopenharmony_ci#include <linux/bitops.h> 4662306a36Sopenharmony_ci#include <linux/sizes.h> 4762306a36Sopenharmony_ci#include <linux/module.h> 4862306a36Sopenharmony_ci#include <linux/moduleparam.h> 4962306a36Sopenharmony_ci#include <linux/init.h> 5062306a36Sopenharmony_ci#include <linux/interrupt.h> 5162306a36Sopenharmony_ci#include <linux/spinlock.h> 5262306a36Sopenharmony_ci#include <linux/mutex.h> 5362306a36Sopenharmony_ci#include <linux/pci.h> 5462306a36Sopenharmony_ci#include <linux/aer.h> 5562306a36Sopenharmony_ci#include <linux/slab.h> 5662306a36Sopenharmony_ci#include <linux/list.h> 5762306a36Sopenharmony_ci#include <linux/debugfs.h> 5862306a36Sopenharmony_ci#include <linux/hwmon.h> 5962306a36Sopenharmony_ci#include <linux/hwmon-sysfs.h> 6062306a36Sopenharmony_ci#include <linux/ntb.h> 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci#include "ntb_hw_idt.h" 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci#define NTB_NAME "ntb_hw_idt" 6562306a36Sopenharmony_ci#define NTB_DESC "IDT PCI-E Non-Transparent Bridge Driver" 6662306a36Sopenharmony_ci#define NTB_VER "2.0" 6762306a36Sopenharmony_ci#define NTB_IRQNAME "ntb_irq_idt" 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ciMODULE_DESCRIPTION(NTB_DESC); 7062306a36Sopenharmony_ciMODULE_VERSION(NTB_VER); 7162306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 7262306a36Sopenharmony_ciMODULE_AUTHOR("T-platforms"); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci/* 7562306a36Sopenharmony_ci * NT Endpoint registers table simplifying a loop access to the functionally 7662306a36Sopenharmony_ci * related registers 7762306a36Sopenharmony_ci */ 7862306a36Sopenharmony_cistatic const struct idt_ntb_regs ntdata_tbl = { 7962306a36Sopenharmony_ci { {IDT_NT_BARSETUP0, IDT_NT_BARLIMIT0, 8062306a36Sopenharmony_ci IDT_NT_BARLTBASE0, IDT_NT_BARUTBASE0}, 8162306a36Sopenharmony_ci {IDT_NT_BARSETUP1, IDT_NT_BARLIMIT1, 8262306a36Sopenharmony_ci IDT_NT_BARLTBASE1, IDT_NT_BARUTBASE1}, 8362306a36Sopenharmony_ci {IDT_NT_BARSETUP2, IDT_NT_BARLIMIT2, 8462306a36Sopenharmony_ci IDT_NT_BARLTBASE2, IDT_NT_BARUTBASE2}, 8562306a36Sopenharmony_ci {IDT_NT_BARSETUP3, IDT_NT_BARLIMIT3, 8662306a36Sopenharmony_ci IDT_NT_BARLTBASE3, IDT_NT_BARUTBASE3}, 8762306a36Sopenharmony_ci {IDT_NT_BARSETUP4, IDT_NT_BARLIMIT4, 8862306a36Sopenharmony_ci IDT_NT_BARLTBASE4, IDT_NT_BARUTBASE4}, 8962306a36Sopenharmony_ci {IDT_NT_BARSETUP5, IDT_NT_BARLIMIT5, 9062306a36Sopenharmony_ci IDT_NT_BARLTBASE5, IDT_NT_BARUTBASE5} }, 9162306a36Sopenharmony_ci { {IDT_NT_INMSG0, IDT_NT_OUTMSG0, IDT_NT_INMSGSRC0}, 9262306a36Sopenharmony_ci {IDT_NT_INMSG1, IDT_NT_OUTMSG1, IDT_NT_INMSGSRC1}, 9362306a36Sopenharmony_ci {IDT_NT_INMSG2, IDT_NT_OUTMSG2, IDT_NT_INMSGSRC2}, 9462306a36Sopenharmony_ci {IDT_NT_INMSG3, IDT_NT_OUTMSG3, IDT_NT_INMSGSRC3} } 9562306a36Sopenharmony_ci}; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci/* 9862306a36Sopenharmony_ci * NT Endpoint ports data table with the corresponding pcie command, link 9962306a36Sopenharmony_ci * status, control and BAR-related registers 10062306a36Sopenharmony_ci */ 10162306a36Sopenharmony_cistatic const struct idt_ntb_port portdata_tbl[IDT_MAX_NR_PORTS] = { 10262306a36Sopenharmony_ci/*0*/ { IDT_SW_NTP0_PCIECMDSTS, IDT_SW_NTP0_PCIELCTLSTS, 10362306a36Sopenharmony_ci IDT_SW_NTP0_NTCTL, 10462306a36Sopenharmony_ci IDT_SW_SWPORT0CTL, IDT_SW_SWPORT0STS, 10562306a36Sopenharmony_ci { {IDT_SW_NTP0_BARSETUP0, IDT_SW_NTP0_BARLIMIT0, 10662306a36Sopenharmony_ci IDT_SW_NTP0_BARLTBASE0, IDT_SW_NTP0_BARUTBASE0}, 10762306a36Sopenharmony_ci {IDT_SW_NTP0_BARSETUP1, IDT_SW_NTP0_BARLIMIT1, 10862306a36Sopenharmony_ci IDT_SW_NTP0_BARLTBASE1, IDT_SW_NTP0_BARUTBASE1}, 10962306a36Sopenharmony_ci {IDT_SW_NTP0_BARSETUP2, IDT_SW_NTP0_BARLIMIT2, 11062306a36Sopenharmony_ci IDT_SW_NTP0_BARLTBASE2, IDT_SW_NTP0_BARUTBASE2}, 11162306a36Sopenharmony_ci {IDT_SW_NTP0_BARSETUP3, IDT_SW_NTP0_BARLIMIT3, 11262306a36Sopenharmony_ci IDT_SW_NTP0_BARLTBASE3, IDT_SW_NTP0_BARUTBASE3}, 11362306a36Sopenharmony_ci {IDT_SW_NTP0_BARSETUP4, IDT_SW_NTP0_BARLIMIT4, 11462306a36Sopenharmony_ci IDT_SW_NTP0_BARLTBASE4, IDT_SW_NTP0_BARUTBASE4}, 11562306a36Sopenharmony_ci {IDT_SW_NTP0_BARSETUP5, IDT_SW_NTP0_BARLIMIT5, 11662306a36Sopenharmony_ci IDT_SW_NTP0_BARLTBASE5, IDT_SW_NTP0_BARUTBASE5} } }, 11762306a36Sopenharmony_ci/*1*/ {0}, 11862306a36Sopenharmony_ci/*2*/ { IDT_SW_NTP2_PCIECMDSTS, IDT_SW_NTP2_PCIELCTLSTS, 11962306a36Sopenharmony_ci IDT_SW_NTP2_NTCTL, 12062306a36Sopenharmony_ci IDT_SW_SWPORT2CTL, IDT_SW_SWPORT2STS, 12162306a36Sopenharmony_ci { {IDT_SW_NTP2_BARSETUP0, IDT_SW_NTP2_BARLIMIT0, 12262306a36Sopenharmony_ci IDT_SW_NTP2_BARLTBASE0, IDT_SW_NTP2_BARUTBASE0}, 12362306a36Sopenharmony_ci {IDT_SW_NTP2_BARSETUP1, IDT_SW_NTP2_BARLIMIT1, 12462306a36Sopenharmony_ci IDT_SW_NTP2_BARLTBASE1, IDT_SW_NTP2_BARUTBASE1}, 12562306a36Sopenharmony_ci {IDT_SW_NTP2_BARSETUP2, IDT_SW_NTP2_BARLIMIT2, 12662306a36Sopenharmony_ci IDT_SW_NTP2_BARLTBASE2, IDT_SW_NTP2_BARUTBASE2}, 12762306a36Sopenharmony_ci {IDT_SW_NTP2_BARSETUP3, IDT_SW_NTP2_BARLIMIT3, 12862306a36Sopenharmony_ci IDT_SW_NTP2_BARLTBASE3, IDT_SW_NTP2_BARUTBASE3}, 12962306a36Sopenharmony_ci {IDT_SW_NTP2_BARSETUP4, IDT_SW_NTP2_BARLIMIT4, 13062306a36Sopenharmony_ci IDT_SW_NTP2_BARLTBASE4, IDT_SW_NTP2_BARUTBASE4}, 13162306a36Sopenharmony_ci {IDT_SW_NTP2_BARSETUP5, IDT_SW_NTP2_BARLIMIT5, 13262306a36Sopenharmony_ci IDT_SW_NTP2_BARLTBASE5, IDT_SW_NTP2_BARUTBASE5} } }, 13362306a36Sopenharmony_ci/*3*/ {0}, 13462306a36Sopenharmony_ci/*4*/ { IDT_SW_NTP4_PCIECMDSTS, IDT_SW_NTP4_PCIELCTLSTS, 13562306a36Sopenharmony_ci IDT_SW_NTP4_NTCTL, 13662306a36Sopenharmony_ci IDT_SW_SWPORT4CTL, IDT_SW_SWPORT4STS, 13762306a36Sopenharmony_ci { {IDT_SW_NTP4_BARSETUP0, IDT_SW_NTP4_BARLIMIT0, 13862306a36Sopenharmony_ci IDT_SW_NTP4_BARLTBASE0, IDT_SW_NTP4_BARUTBASE0}, 13962306a36Sopenharmony_ci {IDT_SW_NTP4_BARSETUP1, IDT_SW_NTP4_BARLIMIT1, 14062306a36Sopenharmony_ci IDT_SW_NTP4_BARLTBASE1, IDT_SW_NTP4_BARUTBASE1}, 14162306a36Sopenharmony_ci {IDT_SW_NTP4_BARSETUP2, IDT_SW_NTP4_BARLIMIT2, 14262306a36Sopenharmony_ci IDT_SW_NTP4_BARLTBASE2, IDT_SW_NTP4_BARUTBASE2}, 14362306a36Sopenharmony_ci {IDT_SW_NTP4_BARSETUP3, IDT_SW_NTP4_BARLIMIT3, 14462306a36Sopenharmony_ci IDT_SW_NTP4_BARLTBASE3, IDT_SW_NTP4_BARUTBASE3}, 14562306a36Sopenharmony_ci {IDT_SW_NTP4_BARSETUP4, IDT_SW_NTP4_BARLIMIT4, 14662306a36Sopenharmony_ci IDT_SW_NTP4_BARLTBASE4, IDT_SW_NTP4_BARUTBASE4}, 14762306a36Sopenharmony_ci {IDT_SW_NTP4_BARSETUP5, IDT_SW_NTP4_BARLIMIT5, 14862306a36Sopenharmony_ci IDT_SW_NTP4_BARLTBASE5, IDT_SW_NTP4_BARUTBASE5} } }, 14962306a36Sopenharmony_ci/*5*/ {0}, 15062306a36Sopenharmony_ci/*6*/ { IDT_SW_NTP6_PCIECMDSTS, IDT_SW_NTP6_PCIELCTLSTS, 15162306a36Sopenharmony_ci IDT_SW_NTP6_NTCTL, 15262306a36Sopenharmony_ci IDT_SW_SWPORT6CTL, IDT_SW_SWPORT6STS, 15362306a36Sopenharmony_ci { {IDT_SW_NTP6_BARSETUP0, IDT_SW_NTP6_BARLIMIT0, 15462306a36Sopenharmony_ci IDT_SW_NTP6_BARLTBASE0, IDT_SW_NTP6_BARUTBASE0}, 15562306a36Sopenharmony_ci {IDT_SW_NTP6_BARSETUP1, IDT_SW_NTP6_BARLIMIT1, 15662306a36Sopenharmony_ci IDT_SW_NTP6_BARLTBASE1, IDT_SW_NTP6_BARUTBASE1}, 15762306a36Sopenharmony_ci {IDT_SW_NTP6_BARSETUP2, IDT_SW_NTP6_BARLIMIT2, 15862306a36Sopenharmony_ci IDT_SW_NTP6_BARLTBASE2, IDT_SW_NTP6_BARUTBASE2}, 15962306a36Sopenharmony_ci {IDT_SW_NTP6_BARSETUP3, IDT_SW_NTP6_BARLIMIT3, 16062306a36Sopenharmony_ci IDT_SW_NTP6_BARLTBASE3, IDT_SW_NTP6_BARUTBASE3}, 16162306a36Sopenharmony_ci {IDT_SW_NTP6_BARSETUP4, IDT_SW_NTP6_BARLIMIT4, 16262306a36Sopenharmony_ci IDT_SW_NTP6_BARLTBASE4, IDT_SW_NTP6_BARUTBASE4}, 16362306a36Sopenharmony_ci {IDT_SW_NTP6_BARSETUP5, IDT_SW_NTP6_BARLIMIT5, 16462306a36Sopenharmony_ci IDT_SW_NTP6_BARLTBASE5, IDT_SW_NTP6_BARUTBASE5} } }, 16562306a36Sopenharmony_ci/*7*/ {0}, 16662306a36Sopenharmony_ci/*8*/ { IDT_SW_NTP8_PCIECMDSTS, IDT_SW_NTP8_PCIELCTLSTS, 16762306a36Sopenharmony_ci IDT_SW_NTP8_NTCTL, 16862306a36Sopenharmony_ci IDT_SW_SWPORT8CTL, IDT_SW_SWPORT8STS, 16962306a36Sopenharmony_ci { {IDT_SW_NTP8_BARSETUP0, IDT_SW_NTP8_BARLIMIT0, 17062306a36Sopenharmony_ci IDT_SW_NTP8_BARLTBASE0, IDT_SW_NTP8_BARUTBASE0}, 17162306a36Sopenharmony_ci {IDT_SW_NTP8_BARSETUP1, IDT_SW_NTP8_BARLIMIT1, 17262306a36Sopenharmony_ci IDT_SW_NTP8_BARLTBASE1, IDT_SW_NTP8_BARUTBASE1}, 17362306a36Sopenharmony_ci {IDT_SW_NTP8_BARSETUP2, IDT_SW_NTP8_BARLIMIT2, 17462306a36Sopenharmony_ci IDT_SW_NTP8_BARLTBASE2, IDT_SW_NTP8_BARUTBASE2}, 17562306a36Sopenharmony_ci {IDT_SW_NTP8_BARSETUP3, IDT_SW_NTP8_BARLIMIT3, 17662306a36Sopenharmony_ci IDT_SW_NTP8_BARLTBASE3, IDT_SW_NTP8_BARUTBASE3}, 17762306a36Sopenharmony_ci {IDT_SW_NTP8_BARSETUP4, IDT_SW_NTP8_BARLIMIT4, 17862306a36Sopenharmony_ci IDT_SW_NTP8_BARLTBASE4, IDT_SW_NTP8_BARUTBASE4}, 17962306a36Sopenharmony_ci {IDT_SW_NTP8_BARSETUP5, IDT_SW_NTP8_BARLIMIT5, 18062306a36Sopenharmony_ci IDT_SW_NTP8_BARLTBASE5, IDT_SW_NTP8_BARUTBASE5} } }, 18162306a36Sopenharmony_ci/*9*/ {0}, 18262306a36Sopenharmony_ci/*10*/ {0}, 18362306a36Sopenharmony_ci/*11*/ {0}, 18462306a36Sopenharmony_ci/*12*/ { IDT_SW_NTP12_PCIECMDSTS, IDT_SW_NTP12_PCIELCTLSTS, 18562306a36Sopenharmony_ci IDT_SW_NTP12_NTCTL, 18662306a36Sopenharmony_ci IDT_SW_SWPORT12CTL, IDT_SW_SWPORT12STS, 18762306a36Sopenharmony_ci { {IDT_SW_NTP12_BARSETUP0, IDT_SW_NTP12_BARLIMIT0, 18862306a36Sopenharmony_ci IDT_SW_NTP12_BARLTBASE0, IDT_SW_NTP12_BARUTBASE0}, 18962306a36Sopenharmony_ci {IDT_SW_NTP12_BARSETUP1, IDT_SW_NTP12_BARLIMIT1, 19062306a36Sopenharmony_ci IDT_SW_NTP12_BARLTBASE1, IDT_SW_NTP12_BARUTBASE1}, 19162306a36Sopenharmony_ci {IDT_SW_NTP12_BARSETUP2, IDT_SW_NTP12_BARLIMIT2, 19262306a36Sopenharmony_ci IDT_SW_NTP12_BARLTBASE2, IDT_SW_NTP12_BARUTBASE2}, 19362306a36Sopenharmony_ci {IDT_SW_NTP12_BARSETUP3, IDT_SW_NTP12_BARLIMIT3, 19462306a36Sopenharmony_ci IDT_SW_NTP12_BARLTBASE3, IDT_SW_NTP12_BARUTBASE3}, 19562306a36Sopenharmony_ci {IDT_SW_NTP12_BARSETUP4, IDT_SW_NTP12_BARLIMIT4, 19662306a36Sopenharmony_ci IDT_SW_NTP12_BARLTBASE4, IDT_SW_NTP12_BARUTBASE4}, 19762306a36Sopenharmony_ci {IDT_SW_NTP12_BARSETUP5, IDT_SW_NTP12_BARLIMIT5, 19862306a36Sopenharmony_ci IDT_SW_NTP12_BARLTBASE5, IDT_SW_NTP12_BARUTBASE5} } }, 19962306a36Sopenharmony_ci/*13*/ {0}, 20062306a36Sopenharmony_ci/*14*/ {0}, 20162306a36Sopenharmony_ci/*15*/ {0}, 20262306a36Sopenharmony_ci/*16*/ { IDT_SW_NTP16_PCIECMDSTS, IDT_SW_NTP16_PCIELCTLSTS, 20362306a36Sopenharmony_ci IDT_SW_NTP16_NTCTL, 20462306a36Sopenharmony_ci IDT_SW_SWPORT16CTL, IDT_SW_SWPORT16STS, 20562306a36Sopenharmony_ci { {IDT_SW_NTP16_BARSETUP0, IDT_SW_NTP16_BARLIMIT0, 20662306a36Sopenharmony_ci IDT_SW_NTP16_BARLTBASE0, IDT_SW_NTP16_BARUTBASE0}, 20762306a36Sopenharmony_ci {IDT_SW_NTP16_BARSETUP1, IDT_SW_NTP16_BARLIMIT1, 20862306a36Sopenharmony_ci IDT_SW_NTP16_BARLTBASE1, IDT_SW_NTP16_BARUTBASE1}, 20962306a36Sopenharmony_ci {IDT_SW_NTP16_BARSETUP2, IDT_SW_NTP16_BARLIMIT2, 21062306a36Sopenharmony_ci IDT_SW_NTP16_BARLTBASE2, IDT_SW_NTP16_BARUTBASE2}, 21162306a36Sopenharmony_ci {IDT_SW_NTP16_BARSETUP3, IDT_SW_NTP16_BARLIMIT3, 21262306a36Sopenharmony_ci IDT_SW_NTP16_BARLTBASE3, IDT_SW_NTP16_BARUTBASE3}, 21362306a36Sopenharmony_ci {IDT_SW_NTP16_BARSETUP4, IDT_SW_NTP16_BARLIMIT4, 21462306a36Sopenharmony_ci IDT_SW_NTP16_BARLTBASE4, IDT_SW_NTP16_BARUTBASE4}, 21562306a36Sopenharmony_ci {IDT_SW_NTP16_BARSETUP5, IDT_SW_NTP16_BARLIMIT5, 21662306a36Sopenharmony_ci IDT_SW_NTP16_BARLTBASE5, IDT_SW_NTP16_BARUTBASE5} } }, 21762306a36Sopenharmony_ci/*17*/ {0}, 21862306a36Sopenharmony_ci/*18*/ {0}, 21962306a36Sopenharmony_ci/*19*/ {0}, 22062306a36Sopenharmony_ci/*20*/ { IDT_SW_NTP20_PCIECMDSTS, IDT_SW_NTP20_PCIELCTLSTS, 22162306a36Sopenharmony_ci IDT_SW_NTP20_NTCTL, 22262306a36Sopenharmony_ci IDT_SW_SWPORT20CTL, IDT_SW_SWPORT20STS, 22362306a36Sopenharmony_ci { {IDT_SW_NTP20_BARSETUP0, IDT_SW_NTP20_BARLIMIT0, 22462306a36Sopenharmony_ci IDT_SW_NTP20_BARLTBASE0, IDT_SW_NTP20_BARUTBASE0}, 22562306a36Sopenharmony_ci {IDT_SW_NTP20_BARSETUP1, IDT_SW_NTP20_BARLIMIT1, 22662306a36Sopenharmony_ci IDT_SW_NTP20_BARLTBASE1, IDT_SW_NTP20_BARUTBASE1}, 22762306a36Sopenharmony_ci {IDT_SW_NTP20_BARSETUP2, IDT_SW_NTP20_BARLIMIT2, 22862306a36Sopenharmony_ci IDT_SW_NTP20_BARLTBASE2, IDT_SW_NTP20_BARUTBASE2}, 22962306a36Sopenharmony_ci {IDT_SW_NTP20_BARSETUP3, IDT_SW_NTP20_BARLIMIT3, 23062306a36Sopenharmony_ci IDT_SW_NTP20_BARLTBASE3, IDT_SW_NTP20_BARUTBASE3}, 23162306a36Sopenharmony_ci {IDT_SW_NTP20_BARSETUP4, IDT_SW_NTP20_BARLIMIT4, 23262306a36Sopenharmony_ci IDT_SW_NTP20_BARLTBASE4, IDT_SW_NTP20_BARUTBASE4}, 23362306a36Sopenharmony_ci {IDT_SW_NTP20_BARSETUP5, IDT_SW_NTP20_BARLIMIT5, 23462306a36Sopenharmony_ci IDT_SW_NTP20_BARLTBASE5, IDT_SW_NTP20_BARUTBASE5} } }, 23562306a36Sopenharmony_ci/*21*/ {0}, 23662306a36Sopenharmony_ci/*22*/ {0}, 23762306a36Sopenharmony_ci/*23*/ {0} 23862306a36Sopenharmony_ci}; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci/* 24162306a36Sopenharmony_ci * IDT PCIe-switch partitions table with the corresponding control, status 24262306a36Sopenharmony_ci * and messages control registers 24362306a36Sopenharmony_ci */ 24462306a36Sopenharmony_cistatic const struct idt_ntb_part partdata_tbl[IDT_MAX_NR_PARTS] = { 24562306a36Sopenharmony_ci/*0*/ { IDT_SW_SWPART0CTL, IDT_SW_SWPART0STS, 24662306a36Sopenharmony_ci {IDT_SW_SWP0MSGCTL0, IDT_SW_SWP0MSGCTL1, 24762306a36Sopenharmony_ci IDT_SW_SWP0MSGCTL2, IDT_SW_SWP0MSGCTL3} }, 24862306a36Sopenharmony_ci/*1*/ { IDT_SW_SWPART1CTL, IDT_SW_SWPART1STS, 24962306a36Sopenharmony_ci {IDT_SW_SWP1MSGCTL0, IDT_SW_SWP1MSGCTL1, 25062306a36Sopenharmony_ci IDT_SW_SWP1MSGCTL2, IDT_SW_SWP1MSGCTL3} }, 25162306a36Sopenharmony_ci/*2*/ { IDT_SW_SWPART2CTL, IDT_SW_SWPART2STS, 25262306a36Sopenharmony_ci {IDT_SW_SWP2MSGCTL0, IDT_SW_SWP2MSGCTL1, 25362306a36Sopenharmony_ci IDT_SW_SWP2MSGCTL2, IDT_SW_SWP2MSGCTL3} }, 25462306a36Sopenharmony_ci/*3*/ { IDT_SW_SWPART3CTL, IDT_SW_SWPART3STS, 25562306a36Sopenharmony_ci {IDT_SW_SWP3MSGCTL0, IDT_SW_SWP3MSGCTL1, 25662306a36Sopenharmony_ci IDT_SW_SWP3MSGCTL2, IDT_SW_SWP3MSGCTL3} }, 25762306a36Sopenharmony_ci/*4*/ { IDT_SW_SWPART4CTL, IDT_SW_SWPART4STS, 25862306a36Sopenharmony_ci {IDT_SW_SWP4MSGCTL0, IDT_SW_SWP4MSGCTL1, 25962306a36Sopenharmony_ci IDT_SW_SWP4MSGCTL2, IDT_SW_SWP4MSGCTL3} }, 26062306a36Sopenharmony_ci/*5*/ { IDT_SW_SWPART5CTL, IDT_SW_SWPART5STS, 26162306a36Sopenharmony_ci {IDT_SW_SWP5MSGCTL0, IDT_SW_SWP5MSGCTL1, 26262306a36Sopenharmony_ci IDT_SW_SWP5MSGCTL2, IDT_SW_SWP5MSGCTL3} }, 26362306a36Sopenharmony_ci/*6*/ { IDT_SW_SWPART6CTL, IDT_SW_SWPART6STS, 26462306a36Sopenharmony_ci {IDT_SW_SWP6MSGCTL0, IDT_SW_SWP6MSGCTL1, 26562306a36Sopenharmony_ci IDT_SW_SWP6MSGCTL2, IDT_SW_SWP6MSGCTL3} }, 26662306a36Sopenharmony_ci/*7*/ { IDT_SW_SWPART7CTL, IDT_SW_SWPART7STS, 26762306a36Sopenharmony_ci {IDT_SW_SWP7MSGCTL0, IDT_SW_SWP7MSGCTL1, 26862306a36Sopenharmony_ci IDT_SW_SWP7MSGCTL2, IDT_SW_SWP7MSGCTL3} } 26962306a36Sopenharmony_ci}; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci/* 27262306a36Sopenharmony_ci * DebugFS directory to place the driver debug file 27362306a36Sopenharmony_ci */ 27462306a36Sopenharmony_cistatic struct dentry *dbgfs_topdir; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci/*============================================================================= 27762306a36Sopenharmony_ci * 1. IDT PCIe-switch registers IO-functions 27862306a36Sopenharmony_ci * 27962306a36Sopenharmony_ci * Beside ordinary configuration space registers IDT PCIe-switch expose 28062306a36Sopenharmony_ci * global configuration registers, which are used to determine state of other 28162306a36Sopenharmony_ci * device ports as well as being notified of some switch-related events. 28262306a36Sopenharmony_ci * Additionally all the configuration space registers of all the IDT 28362306a36Sopenharmony_ci * PCIe-switch functions are mapped to the Global Address space, so each 28462306a36Sopenharmony_ci * function can determine a configuration of any other PCI-function. 28562306a36Sopenharmony_ci * Functions declared in this chapter are created to encapsulate access 28662306a36Sopenharmony_ci * to configuration and global registers, so the driver code just need to 28762306a36Sopenharmony_ci * provide IDT NTB hardware descriptor and a register address. 28862306a36Sopenharmony_ci *============================================================================= 28962306a36Sopenharmony_ci */ 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci/* 29262306a36Sopenharmony_ci * idt_nt_write() - PCI configuration space registers write method 29362306a36Sopenharmony_ci * @ndev: IDT NTB hardware driver descriptor 29462306a36Sopenharmony_ci * @reg: Register to write data to 29562306a36Sopenharmony_ci * @data: Value to write to the register 29662306a36Sopenharmony_ci * 29762306a36Sopenharmony_ci * IDT PCIe-switch registers are all Little endian. 29862306a36Sopenharmony_ci */ 29962306a36Sopenharmony_cistatic void idt_nt_write(struct idt_ntb_dev *ndev, 30062306a36Sopenharmony_ci const unsigned int reg, const u32 data) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci /* 30362306a36Sopenharmony_ci * It's obvious bug to request a register exceeding the maximum possible 30462306a36Sopenharmony_ci * value as well as to have it unaligned. 30562306a36Sopenharmony_ci */ 30662306a36Sopenharmony_ci if (WARN_ON(reg > IDT_REG_PCI_MAX || !IS_ALIGNED(reg, IDT_REG_ALIGN))) 30762306a36Sopenharmony_ci return; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci /* Just write the value to the specified register */ 31062306a36Sopenharmony_ci iowrite32(data, ndev->cfgspc + (ptrdiff_t)reg); 31162306a36Sopenharmony_ci} 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci/* 31462306a36Sopenharmony_ci * idt_nt_read() - PCI configuration space registers read method 31562306a36Sopenharmony_ci * @ndev: IDT NTB hardware driver descriptor 31662306a36Sopenharmony_ci * @reg: Register to write data to 31762306a36Sopenharmony_ci * 31862306a36Sopenharmony_ci * IDT PCIe-switch Global configuration registers are all Little endian. 31962306a36Sopenharmony_ci * 32062306a36Sopenharmony_ci * Return: register value 32162306a36Sopenharmony_ci */ 32262306a36Sopenharmony_cistatic u32 idt_nt_read(struct idt_ntb_dev *ndev, const unsigned int reg) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci /* 32562306a36Sopenharmony_ci * It's obvious bug to request a register exceeding the maximum possible 32662306a36Sopenharmony_ci * value as well as to have it unaligned. 32762306a36Sopenharmony_ci */ 32862306a36Sopenharmony_ci if (WARN_ON(reg > IDT_REG_PCI_MAX || !IS_ALIGNED(reg, IDT_REG_ALIGN))) 32962306a36Sopenharmony_ci return ~0; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci /* Just read the value from the specified register */ 33262306a36Sopenharmony_ci return ioread32(ndev->cfgspc + (ptrdiff_t)reg); 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci/* 33662306a36Sopenharmony_ci * idt_sw_write() - Global registers write method 33762306a36Sopenharmony_ci * @ndev: IDT NTB hardware driver descriptor 33862306a36Sopenharmony_ci * @reg: Register to write data to 33962306a36Sopenharmony_ci * @data: Value to write to the register 34062306a36Sopenharmony_ci * 34162306a36Sopenharmony_ci * IDT PCIe-switch Global configuration registers are all Little endian. 34262306a36Sopenharmony_ci */ 34362306a36Sopenharmony_cistatic void idt_sw_write(struct idt_ntb_dev *ndev, 34462306a36Sopenharmony_ci const unsigned int reg, const u32 data) 34562306a36Sopenharmony_ci{ 34662306a36Sopenharmony_ci unsigned long irqflags; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci /* 34962306a36Sopenharmony_ci * It's obvious bug to request a register exceeding the maximum possible 35062306a36Sopenharmony_ci * value as well as to have it unaligned. 35162306a36Sopenharmony_ci */ 35262306a36Sopenharmony_ci if (WARN_ON(reg > IDT_REG_SW_MAX || !IS_ALIGNED(reg, IDT_REG_ALIGN))) 35362306a36Sopenharmony_ci return; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci /* Lock GASA registers operations */ 35662306a36Sopenharmony_ci spin_lock_irqsave(&ndev->gasa_lock, irqflags); 35762306a36Sopenharmony_ci /* Set the global register address */ 35862306a36Sopenharmony_ci iowrite32((u32)reg, ndev->cfgspc + (ptrdiff_t)IDT_NT_GASAADDR); 35962306a36Sopenharmony_ci /* Put the new value of the register */ 36062306a36Sopenharmony_ci iowrite32(data, ndev->cfgspc + (ptrdiff_t)IDT_NT_GASADATA); 36162306a36Sopenharmony_ci /* Unlock GASA registers operations */ 36262306a36Sopenharmony_ci spin_unlock_irqrestore(&ndev->gasa_lock, irqflags); 36362306a36Sopenharmony_ci} 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci/* 36662306a36Sopenharmony_ci * idt_sw_read() - Global registers read method 36762306a36Sopenharmony_ci * @ndev: IDT NTB hardware driver descriptor 36862306a36Sopenharmony_ci * @reg: Register to write data to 36962306a36Sopenharmony_ci * 37062306a36Sopenharmony_ci * IDT PCIe-switch Global configuration registers are all Little endian. 37162306a36Sopenharmony_ci * 37262306a36Sopenharmony_ci * Return: register value 37362306a36Sopenharmony_ci */ 37462306a36Sopenharmony_cistatic u32 idt_sw_read(struct idt_ntb_dev *ndev, const unsigned int reg) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci unsigned long irqflags; 37762306a36Sopenharmony_ci u32 data; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci /* 38062306a36Sopenharmony_ci * It's obvious bug to request a register exceeding the maximum possible 38162306a36Sopenharmony_ci * value as well as to have it unaligned. 38262306a36Sopenharmony_ci */ 38362306a36Sopenharmony_ci if (WARN_ON(reg > IDT_REG_SW_MAX || !IS_ALIGNED(reg, IDT_REG_ALIGN))) 38462306a36Sopenharmony_ci return ~0; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci /* Lock GASA registers operations */ 38762306a36Sopenharmony_ci spin_lock_irqsave(&ndev->gasa_lock, irqflags); 38862306a36Sopenharmony_ci /* Set the global register address */ 38962306a36Sopenharmony_ci iowrite32((u32)reg, ndev->cfgspc + (ptrdiff_t)IDT_NT_GASAADDR); 39062306a36Sopenharmony_ci /* Get the data of the register (read ops acts as MMIO barrier) */ 39162306a36Sopenharmony_ci data = ioread32(ndev->cfgspc + (ptrdiff_t)IDT_NT_GASADATA); 39262306a36Sopenharmony_ci /* Unlock GASA registers operations */ 39362306a36Sopenharmony_ci spin_unlock_irqrestore(&ndev->gasa_lock, irqflags); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci return data; 39662306a36Sopenharmony_ci} 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci/* 39962306a36Sopenharmony_ci * idt_reg_set_bits() - set bits of a passed register 40062306a36Sopenharmony_ci * @ndev: IDT NTB hardware driver descriptor 40162306a36Sopenharmony_ci * @reg: Register to change bits of 40262306a36Sopenharmony_ci * @reg_lock: Register access spin lock 40362306a36Sopenharmony_ci * @valid_mask: Mask of valid bits 40462306a36Sopenharmony_ci * @set_bits: Bitmask to set 40562306a36Sopenharmony_ci * 40662306a36Sopenharmony_ci * Helper method to check whether a passed bitfield is valid and set 40762306a36Sopenharmony_ci * corresponding bits of a register. 40862306a36Sopenharmony_ci * 40962306a36Sopenharmony_ci * WARNING! Make sure the passed register isn't accessed over plane 41062306a36Sopenharmony_ci * idt_nt_write() method (read method is ok to be used concurrently). 41162306a36Sopenharmony_ci * 41262306a36Sopenharmony_ci * Return: zero on success, negative error on invalid bitmask. 41362306a36Sopenharmony_ci */ 41462306a36Sopenharmony_cistatic inline int idt_reg_set_bits(struct idt_ntb_dev *ndev, unsigned int reg, 41562306a36Sopenharmony_ci spinlock_t *reg_lock, 41662306a36Sopenharmony_ci u64 valid_mask, u64 set_bits) 41762306a36Sopenharmony_ci{ 41862306a36Sopenharmony_ci unsigned long irqflags; 41962306a36Sopenharmony_ci u32 data; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci if (set_bits & ~(u64)valid_mask) 42262306a36Sopenharmony_ci return -EINVAL; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci /* Lock access to the register unless the change is written back */ 42562306a36Sopenharmony_ci spin_lock_irqsave(reg_lock, irqflags); 42662306a36Sopenharmony_ci data = idt_nt_read(ndev, reg) | (u32)set_bits; 42762306a36Sopenharmony_ci idt_nt_write(ndev, reg, data); 42862306a36Sopenharmony_ci /* Unlock the register */ 42962306a36Sopenharmony_ci spin_unlock_irqrestore(reg_lock, irqflags); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci return 0; 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci/* 43562306a36Sopenharmony_ci * idt_reg_clear_bits() - clear bits of a passed register 43662306a36Sopenharmony_ci * @ndev: IDT NTB hardware driver descriptor 43762306a36Sopenharmony_ci * @reg: Register to change bits of 43862306a36Sopenharmony_ci * @reg_lock: Register access spin lock 43962306a36Sopenharmony_ci * @set_bits: Bitmask to clear 44062306a36Sopenharmony_ci * 44162306a36Sopenharmony_ci * Helper method to check whether a passed bitfield is valid and clear 44262306a36Sopenharmony_ci * corresponding bits of a register. 44362306a36Sopenharmony_ci * 44462306a36Sopenharmony_ci * NOTE! Invalid bits are always considered cleared so it's not an error 44562306a36Sopenharmony_ci * to clear them over. 44662306a36Sopenharmony_ci * 44762306a36Sopenharmony_ci * WARNING! Make sure the passed register isn't accessed over plane 44862306a36Sopenharmony_ci * idt_nt_write() method (read method is ok to use concurrently). 44962306a36Sopenharmony_ci */ 45062306a36Sopenharmony_cistatic inline void idt_reg_clear_bits(struct idt_ntb_dev *ndev, 45162306a36Sopenharmony_ci unsigned int reg, spinlock_t *reg_lock, 45262306a36Sopenharmony_ci u64 clear_bits) 45362306a36Sopenharmony_ci{ 45462306a36Sopenharmony_ci unsigned long irqflags; 45562306a36Sopenharmony_ci u32 data; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci /* Lock access to the register unless the change is written back */ 45862306a36Sopenharmony_ci spin_lock_irqsave(reg_lock, irqflags); 45962306a36Sopenharmony_ci data = idt_nt_read(ndev, reg) & ~(u32)clear_bits; 46062306a36Sopenharmony_ci idt_nt_write(ndev, reg, data); 46162306a36Sopenharmony_ci /* Unlock the register */ 46262306a36Sopenharmony_ci spin_unlock_irqrestore(reg_lock, irqflags); 46362306a36Sopenharmony_ci} 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci/*=========================================================================== 46662306a36Sopenharmony_ci * 2. Ports operations 46762306a36Sopenharmony_ci * 46862306a36Sopenharmony_ci * IDT PCIe-switches can have from 3 up to 8 ports with possible 46962306a36Sopenharmony_ci * NT-functions enabled. So all the possible ports need to be scanned looking 47062306a36Sopenharmony_ci * for NTB activated. NTB API will have enumerated only the ports with NTB. 47162306a36Sopenharmony_ci *=========================================================================== 47262306a36Sopenharmony_ci */ 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci/* 47562306a36Sopenharmony_ci * idt_scan_ports() - scan IDT PCIe-switch ports collecting info in the tables 47662306a36Sopenharmony_ci * @ndev: Pointer to the PCI device descriptor 47762306a36Sopenharmony_ci * 47862306a36Sopenharmony_ci * Return: zero on success, otherwise a negative error number. 47962306a36Sopenharmony_ci */ 48062306a36Sopenharmony_cistatic int idt_scan_ports(struct idt_ntb_dev *ndev) 48162306a36Sopenharmony_ci{ 48262306a36Sopenharmony_ci unsigned char pidx, port, part; 48362306a36Sopenharmony_ci u32 data, portsts, partsts; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci /* Retrieve the local port number */ 48662306a36Sopenharmony_ci data = idt_nt_read(ndev, IDT_NT_PCIELCAP); 48762306a36Sopenharmony_ci ndev->port = GET_FIELD(PCIELCAP_PORTNUM, data); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci /* Retrieve the local partition number */ 49062306a36Sopenharmony_ci portsts = idt_sw_read(ndev, portdata_tbl[ndev->port].sts); 49162306a36Sopenharmony_ci ndev->part = GET_FIELD(SWPORTxSTS_SWPART, portsts); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci /* Initialize port/partition -> index tables with invalid values */ 49462306a36Sopenharmony_ci memset(ndev->port_idx_map, -EINVAL, sizeof(ndev->port_idx_map)); 49562306a36Sopenharmony_ci memset(ndev->part_idx_map, -EINVAL, sizeof(ndev->part_idx_map)); 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci /* 49862306a36Sopenharmony_ci * Walk over all the possible ports checking whether any of them has 49962306a36Sopenharmony_ci * NT-function activated 50062306a36Sopenharmony_ci */ 50162306a36Sopenharmony_ci ndev->peer_cnt = 0; 50262306a36Sopenharmony_ci for (pidx = 0; pidx < ndev->swcfg->port_cnt; pidx++) { 50362306a36Sopenharmony_ci port = ndev->swcfg->ports[pidx]; 50462306a36Sopenharmony_ci /* Skip local port */ 50562306a36Sopenharmony_ci if (port == ndev->port) 50662306a36Sopenharmony_ci continue; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci /* Read the port status register to get it partition */ 50962306a36Sopenharmony_ci portsts = idt_sw_read(ndev, portdata_tbl[port].sts); 51062306a36Sopenharmony_ci part = GET_FIELD(SWPORTxSTS_SWPART, portsts); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci /* Retrieve the partition status */ 51362306a36Sopenharmony_ci partsts = idt_sw_read(ndev, partdata_tbl[part].sts); 51462306a36Sopenharmony_ci /* Check if partition state is active and port has NTB */ 51562306a36Sopenharmony_ci if (IS_FLD_SET(SWPARTxSTS_STATE, partsts, ACT) && 51662306a36Sopenharmony_ci (IS_FLD_SET(SWPORTxSTS_MODE, portsts, NT) || 51762306a36Sopenharmony_ci IS_FLD_SET(SWPORTxSTS_MODE, portsts, USNT) || 51862306a36Sopenharmony_ci IS_FLD_SET(SWPORTxSTS_MODE, portsts, USNTDMA) || 51962306a36Sopenharmony_ci IS_FLD_SET(SWPORTxSTS_MODE, portsts, NTDMA))) { 52062306a36Sopenharmony_ci /* Save the port and partition numbers */ 52162306a36Sopenharmony_ci ndev->peers[ndev->peer_cnt].port = port; 52262306a36Sopenharmony_ci ndev->peers[ndev->peer_cnt].part = part; 52362306a36Sopenharmony_ci /* Fill in the port/partition -> index tables */ 52462306a36Sopenharmony_ci ndev->port_idx_map[port] = ndev->peer_cnt; 52562306a36Sopenharmony_ci ndev->part_idx_map[part] = ndev->peer_cnt; 52662306a36Sopenharmony_ci ndev->peer_cnt++; 52762306a36Sopenharmony_ci } 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci dev_dbg(&ndev->ntb.pdev->dev, "Local port: %hhu, num of peers: %hhu\n", 53162306a36Sopenharmony_ci ndev->port, ndev->peer_cnt); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci /* It's useless to have this driver loaded if there is no any peer */ 53462306a36Sopenharmony_ci if (ndev->peer_cnt == 0) { 53562306a36Sopenharmony_ci dev_warn(&ndev->ntb.pdev->dev, "No active peer found\n"); 53662306a36Sopenharmony_ci return -ENODEV; 53762306a36Sopenharmony_ci } 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci return 0; 54062306a36Sopenharmony_ci} 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci/* 54362306a36Sopenharmony_ci * idt_ntb_port_number() - get the local port number 54462306a36Sopenharmony_ci * @ntb: NTB device context. 54562306a36Sopenharmony_ci * 54662306a36Sopenharmony_ci * Return: the local port number 54762306a36Sopenharmony_ci */ 54862306a36Sopenharmony_cistatic int idt_ntb_port_number(struct ntb_dev *ntb) 54962306a36Sopenharmony_ci{ 55062306a36Sopenharmony_ci struct idt_ntb_dev *ndev = to_ndev_ntb(ntb); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci return ndev->port; 55362306a36Sopenharmony_ci} 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci/* 55662306a36Sopenharmony_ci * idt_ntb_peer_port_count() - get the number of peer ports 55762306a36Sopenharmony_ci * @ntb: NTB device context. 55862306a36Sopenharmony_ci * 55962306a36Sopenharmony_ci * Return the count of detected peer NT-functions. 56062306a36Sopenharmony_ci * 56162306a36Sopenharmony_ci * Return: number of peer ports 56262306a36Sopenharmony_ci */ 56362306a36Sopenharmony_cistatic int idt_ntb_peer_port_count(struct ntb_dev *ntb) 56462306a36Sopenharmony_ci{ 56562306a36Sopenharmony_ci struct idt_ntb_dev *ndev = to_ndev_ntb(ntb); 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci return ndev->peer_cnt; 56862306a36Sopenharmony_ci} 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci/* 57162306a36Sopenharmony_ci * idt_ntb_peer_port_number() - get peer port by given index 57262306a36Sopenharmony_ci * @ntb: NTB device context. 57362306a36Sopenharmony_ci * @pidx: Peer port index. 57462306a36Sopenharmony_ci * 57562306a36Sopenharmony_ci * Return: peer port or negative error 57662306a36Sopenharmony_ci */ 57762306a36Sopenharmony_cistatic int idt_ntb_peer_port_number(struct ntb_dev *ntb, int pidx) 57862306a36Sopenharmony_ci{ 57962306a36Sopenharmony_ci struct idt_ntb_dev *ndev = to_ndev_ntb(ntb); 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci if (pidx < 0 || ndev->peer_cnt <= pidx) 58262306a36Sopenharmony_ci return -EINVAL; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci /* Return the detected NT-function port number */ 58562306a36Sopenharmony_ci return ndev->peers[pidx].port; 58662306a36Sopenharmony_ci} 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci/* 58962306a36Sopenharmony_ci * idt_ntb_peer_port_idx() - get peer port index by given port number 59062306a36Sopenharmony_ci * @ntb: NTB device context. 59162306a36Sopenharmony_ci * @port: Peer port number. 59262306a36Sopenharmony_ci * 59362306a36Sopenharmony_ci * Internal port -> index table is pre-initialized with -EINVAL values, 59462306a36Sopenharmony_ci * so we just need to return it value 59562306a36Sopenharmony_ci * 59662306a36Sopenharmony_ci * Return: peer NT-function port index or negative error 59762306a36Sopenharmony_ci */ 59862306a36Sopenharmony_cistatic int idt_ntb_peer_port_idx(struct ntb_dev *ntb, int port) 59962306a36Sopenharmony_ci{ 60062306a36Sopenharmony_ci struct idt_ntb_dev *ndev = to_ndev_ntb(ntb); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci if (port < 0 || IDT_MAX_NR_PORTS <= port) 60362306a36Sopenharmony_ci return -EINVAL; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci return ndev->port_idx_map[port]; 60662306a36Sopenharmony_ci} 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci/*=========================================================================== 60962306a36Sopenharmony_ci * 3. Link status operations 61062306a36Sopenharmony_ci * There is no any ready-to-use method to have peer ports notified if NTB 61162306a36Sopenharmony_ci * link is set up or got down. Instead global signal can be used instead. 61262306a36Sopenharmony_ci * In case if any one of ports changes local NTB link state, it sends 61362306a36Sopenharmony_ci * global signal and clears corresponding global state bit. Then all the ports 61462306a36Sopenharmony_ci * receive a notification of that, so to make client driver being aware of 61562306a36Sopenharmony_ci * possible NTB link change. 61662306a36Sopenharmony_ci * Additionally each of active NT-functions is subscribed to PCIe-link 61762306a36Sopenharmony_ci * state changes of peer ports. 61862306a36Sopenharmony_ci *=========================================================================== 61962306a36Sopenharmony_ci */ 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_cistatic void idt_ntb_local_link_disable(struct idt_ntb_dev *ndev); 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci/* 62462306a36Sopenharmony_ci * idt_init_link() - Initialize NTB link state notification subsystem 62562306a36Sopenharmony_ci * @ndev: IDT NTB hardware driver descriptor 62662306a36Sopenharmony_ci * 62762306a36Sopenharmony_ci * Function performs the basic initialization of some global registers 62862306a36Sopenharmony_ci * needed to enable IRQ-based notifications of PCIe Link Up/Down and 62962306a36Sopenharmony_ci * Global Signal events. 63062306a36Sopenharmony_ci * NOTE Since it's not possible to determine when all the NTB peer drivers are 63162306a36Sopenharmony_ci * unloaded as well as have those registers accessed concurrently, we must 63262306a36Sopenharmony_ci * preinitialize them with the same value and leave it uncleared on local 63362306a36Sopenharmony_ci * driver unload. 63462306a36Sopenharmony_ci */ 63562306a36Sopenharmony_cistatic void idt_init_link(struct idt_ntb_dev *ndev) 63662306a36Sopenharmony_ci{ 63762306a36Sopenharmony_ci u32 part_mask, port_mask, se_mask; 63862306a36Sopenharmony_ci unsigned char pidx; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci /* Initialize spin locker of Mapping Table access registers */ 64162306a36Sopenharmony_ci spin_lock_init(&ndev->mtbl_lock); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci /* Walk over all detected peers collecting port and partition masks */ 64462306a36Sopenharmony_ci port_mask = ~BIT(ndev->port); 64562306a36Sopenharmony_ci part_mask = ~BIT(ndev->part); 64662306a36Sopenharmony_ci for (pidx = 0; pidx < ndev->peer_cnt; pidx++) { 64762306a36Sopenharmony_ci port_mask &= ~BIT(ndev->peers[pidx].port); 64862306a36Sopenharmony_ci part_mask &= ~BIT(ndev->peers[pidx].part); 64962306a36Sopenharmony_ci } 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci /* Clean the Link Up/Down and GLobal Signal status registers */ 65262306a36Sopenharmony_ci idt_sw_write(ndev, IDT_SW_SELINKUPSTS, (u32)-1); 65362306a36Sopenharmony_ci idt_sw_write(ndev, IDT_SW_SELINKDNSTS, (u32)-1); 65462306a36Sopenharmony_ci idt_sw_write(ndev, IDT_SW_SEGSIGSTS, (u32)-1); 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci /* Unmask NT-activated partitions to receive Global Switch events */ 65762306a36Sopenharmony_ci idt_sw_write(ndev, IDT_SW_SEPMSK, part_mask); 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci /* Enable PCIe Link Up events of NT-activated ports */ 66062306a36Sopenharmony_ci idt_sw_write(ndev, IDT_SW_SELINKUPMSK, port_mask); 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci /* Enable PCIe Link Down events of NT-activated ports */ 66362306a36Sopenharmony_ci idt_sw_write(ndev, IDT_SW_SELINKDNMSK, port_mask); 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci /* Unmask NT-activated partitions to receive Global Signal events */ 66662306a36Sopenharmony_ci idt_sw_write(ndev, IDT_SW_SEGSIGMSK, part_mask); 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci /* Unmask Link Up/Down and Global Switch Events */ 66962306a36Sopenharmony_ci se_mask = ~(IDT_SEMSK_LINKUP | IDT_SEMSK_LINKDN | IDT_SEMSK_GSIGNAL); 67062306a36Sopenharmony_ci idt_sw_write(ndev, IDT_SW_SEMSK, se_mask); 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci dev_dbg(&ndev->ntb.pdev->dev, "NTB link status events initialized"); 67362306a36Sopenharmony_ci} 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci/* 67662306a36Sopenharmony_ci * idt_deinit_link() - deinitialize link subsystem 67762306a36Sopenharmony_ci * @ndev: IDT NTB hardware driver descriptor 67862306a36Sopenharmony_ci * 67962306a36Sopenharmony_ci * Just disable the link back. 68062306a36Sopenharmony_ci */ 68162306a36Sopenharmony_cistatic void idt_deinit_link(struct idt_ntb_dev *ndev) 68262306a36Sopenharmony_ci{ 68362306a36Sopenharmony_ci /* Disable the link */ 68462306a36Sopenharmony_ci idt_ntb_local_link_disable(ndev); 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci dev_dbg(&ndev->ntb.pdev->dev, "NTB link status events deinitialized"); 68762306a36Sopenharmony_ci} 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci/* 69062306a36Sopenharmony_ci * idt_se_isr() - switch events ISR 69162306a36Sopenharmony_ci * @ndev: IDT NTB hardware driver descriptor 69262306a36Sopenharmony_ci * @ntint_sts: NT-function interrupt status 69362306a36Sopenharmony_ci * 69462306a36Sopenharmony_ci * This driver doesn't support IDT PCIe-switch dynamic reconfigurations, 69562306a36Sopenharmony_ci * Failover capability, etc, so switch events are utilized to notify of 69662306a36Sopenharmony_ci * PCIe and NTB link events. 69762306a36Sopenharmony_ci * The method is called from PCIe ISR bottom-half routine. 69862306a36Sopenharmony_ci */ 69962306a36Sopenharmony_cistatic void idt_se_isr(struct idt_ntb_dev *ndev, u32 ntint_sts) 70062306a36Sopenharmony_ci{ 70162306a36Sopenharmony_ci u32 sests; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci /* Read Switch Events status */ 70462306a36Sopenharmony_ci sests = idt_sw_read(ndev, IDT_SW_SESTS); 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci /* Clean the Link Up/Down and Global Signal status registers */ 70762306a36Sopenharmony_ci idt_sw_write(ndev, IDT_SW_SELINKUPSTS, (u32)-1); 70862306a36Sopenharmony_ci idt_sw_write(ndev, IDT_SW_SELINKDNSTS, (u32)-1); 70962306a36Sopenharmony_ci idt_sw_write(ndev, IDT_SW_SEGSIGSTS, (u32)-1); 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci /* Clean the corresponding interrupt bit */ 71262306a36Sopenharmony_ci idt_nt_write(ndev, IDT_NT_NTINTSTS, IDT_NTINTSTS_SEVENT); 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci dev_dbg(&ndev->ntb.pdev->dev, "SE IRQ detected %#08x (SESTS %#08x)", 71562306a36Sopenharmony_ci ntint_sts, sests); 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci /* Notify the client driver of possible link state change */ 71862306a36Sopenharmony_ci ntb_link_event(&ndev->ntb); 71962306a36Sopenharmony_ci} 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci/* 72262306a36Sopenharmony_ci * idt_ntb_local_link_enable() - enable the local NTB link. 72362306a36Sopenharmony_ci * @ndev: IDT NTB hardware driver descriptor 72462306a36Sopenharmony_ci * 72562306a36Sopenharmony_ci * In order to enable the NTB link we need: 72662306a36Sopenharmony_ci * - enable Completion TLPs translation 72762306a36Sopenharmony_ci * - initialize mapping table to enable the Request ID translation 72862306a36Sopenharmony_ci * - notify peers of NTB link state change 72962306a36Sopenharmony_ci */ 73062306a36Sopenharmony_cistatic void idt_ntb_local_link_enable(struct idt_ntb_dev *ndev) 73162306a36Sopenharmony_ci{ 73262306a36Sopenharmony_ci u32 reqid, mtbldata = 0; 73362306a36Sopenharmony_ci unsigned long irqflags; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci /* Enable the ID protection and Completion TLPs translation */ 73662306a36Sopenharmony_ci idt_nt_write(ndev, IDT_NT_NTCTL, IDT_NTCTL_CPEN); 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci /* Retrieve the current Requester ID (Bus:Device:Function) */ 73962306a36Sopenharmony_ci reqid = idt_nt_read(ndev, IDT_NT_REQIDCAP); 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci /* 74262306a36Sopenharmony_ci * Set the corresponding NT Mapping table entry of port partition index 74362306a36Sopenharmony_ci * with the data to perform the Request ID translation 74462306a36Sopenharmony_ci */ 74562306a36Sopenharmony_ci mtbldata = SET_FIELD(NTMTBLDATA_REQID, 0, reqid) | 74662306a36Sopenharmony_ci SET_FIELD(NTMTBLDATA_PART, 0, ndev->part) | 74762306a36Sopenharmony_ci IDT_NTMTBLDATA_VALID; 74862306a36Sopenharmony_ci spin_lock_irqsave(&ndev->mtbl_lock, irqflags); 74962306a36Sopenharmony_ci idt_nt_write(ndev, IDT_NT_NTMTBLADDR, ndev->part); 75062306a36Sopenharmony_ci idt_nt_write(ndev, IDT_NT_NTMTBLDATA, mtbldata); 75162306a36Sopenharmony_ci spin_unlock_irqrestore(&ndev->mtbl_lock, irqflags); 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci /* Notify the peers by setting and clearing the global signal bit */ 75462306a36Sopenharmony_ci idt_nt_write(ndev, IDT_NT_NTGSIGNAL, IDT_NTGSIGNAL_SET); 75562306a36Sopenharmony_ci idt_sw_write(ndev, IDT_SW_SEGSIGSTS, (u32)1 << ndev->part); 75662306a36Sopenharmony_ci} 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci/* 75962306a36Sopenharmony_ci * idt_ntb_local_link_disable() - disable the local NTB link. 76062306a36Sopenharmony_ci * @ndev: IDT NTB hardware driver descriptor 76162306a36Sopenharmony_ci * 76262306a36Sopenharmony_ci * In order to enable the NTB link we need: 76362306a36Sopenharmony_ci * - disable Completion TLPs translation 76462306a36Sopenharmony_ci * - clear corresponding mapping table entry 76562306a36Sopenharmony_ci * - notify peers of NTB link state change 76662306a36Sopenharmony_ci */ 76762306a36Sopenharmony_cistatic void idt_ntb_local_link_disable(struct idt_ntb_dev *ndev) 76862306a36Sopenharmony_ci{ 76962306a36Sopenharmony_ci unsigned long irqflags; 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci /* Disable Completion TLPs translation */ 77262306a36Sopenharmony_ci idt_nt_write(ndev, IDT_NT_NTCTL, 0); 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci /* Clear the corresponding NT Mapping table entry */ 77562306a36Sopenharmony_ci spin_lock_irqsave(&ndev->mtbl_lock, irqflags); 77662306a36Sopenharmony_ci idt_nt_write(ndev, IDT_NT_NTMTBLADDR, ndev->part); 77762306a36Sopenharmony_ci idt_nt_write(ndev, IDT_NT_NTMTBLDATA, 0); 77862306a36Sopenharmony_ci spin_unlock_irqrestore(&ndev->mtbl_lock, irqflags); 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci /* Notify the peers by setting and clearing the global signal bit */ 78162306a36Sopenharmony_ci idt_nt_write(ndev, IDT_NT_NTGSIGNAL, IDT_NTGSIGNAL_SET); 78262306a36Sopenharmony_ci idt_sw_write(ndev, IDT_SW_SEGSIGSTS, (u32)1 << ndev->part); 78362306a36Sopenharmony_ci} 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci/* 78662306a36Sopenharmony_ci * idt_ntb_local_link_is_up() - test wethter local NTB link is up 78762306a36Sopenharmony_ci * @ndev: IDT NTB hardware driver descriptor 78862306a36Sopenharmony_ci * 78962306a36Sopenharmony_ci * Local link is up under the following conditions: 79062306a36Sopenharmony_ci * - Bus mastering is enabled 79162306a36Sopenharmony_ci * - NTCTL has Completion TLPs translation enabled 79262306a36Sopenharmony_ci * - Mapping table permits Request TLPs translation 79362306a36Sopenharmony_ci * NOTE: We don't need to check PCIe link state since it's obviously 79462306a36Sopenharmony_ci * up while we are able to communicate with IDT PCIe-switch 79562306a36Sopenharmony_ci * 79662306a36Sopenharmony_ci * Return: true if link is up, otherwise false 79762306a36Sopenharmony_ci */ 79862306a36Sopenharmony_cistatic bool idt_ntb_local_link_is_up(struct idt_ntb_dev *ndev) 79962306a36Sopenharmony_ci{ 80062306a36Sopenharmony_ci unsigned long irqflags; 80162306a36Sopenharmony_ci u32 data; 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci /* Read the local Bus Master Enable status */ 80462306a36Sopenharmony_ci data = idt_nt_read(ndev, IDT_NT_PCICMDSTS); 80562306a36Sopenharmony_ci if (!(data & IDT_PCICMDSTS_BME)) 80662306a36Sopenharmony_ci return false; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci /* Read the local Completion TLPs translation enable status */ 80962306a36Sopenharmony_ci data = idt_nt_read(ndev, IDT_NT_NTCTL); 81062306a36Sopenharmony_ci if (!(data & IDT_NTCTL_CPEN)) 81162306a36Sopenharmony_ci return false; 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci /* Read Mapping table entry corresponding to the local partition */ 81462306a36Sopenharmony_ci spin_lock_irqsave(&ndev->mtbl_lock, irqflags); 81562306a36Sopenharmony_ci idt_nt_write(ndev, IDT_NT_NTMTBLADDR, ndev->part); 81662306a36Sopenharmony_ci data = idt_nt_read(ndev, IDT_NT_NTMTBLDATA); 81762306a36Sopenharmony_ci spin_unlock_irqrestore(&ndev->mtbl_lock, irqflags); 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci return !!(data & IDT_NTMTBLDATA_VALID); 82062306a36Sopenharmony_ci} 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci/* 82362306a36Sopenharmony_ci * idt_ntb_peer_link_is_up() - test whether peer NTB link is up 82462306a36Sopenharmony_ci * @ndev: IDT NTB hardware driver descriptor 82562306a36Sopenharmony_ci * @pidx: Peer port index 82662306a36Sopenharmony_ci * 82762306a36Sopenharmony_ci * Peer link is up under the following conditions: 82862306a36Sopenharmony_ci * - PCIe link is up 82962306a36Sopenharmony_ci * - Bus mastering is enabled 83062306a36Sopenharmony_ci * - NTCTL has Completion TLPs translation enabled 83162306a36Sopenharmony_ci * - Mapping table permits Request TLPs translation 83262306a36Sopenharmony_ci * 83362306a36Sopenharmony_ci * Return: true if link is up, otherwise false 83462306a36Sopenharmony_ci */ 83562306a36Sopenharmony_cistatic bool idt_ntb_peer_link_is_up(struct idt_ntb_dev *ndev, int pidx) 83662306a36Sopenharmony_ci{ 83762306a36Sopenharmony_ci unsigned long irqflags; 83862306a36Sopenharmony_ci unsigned char port; 83962306a36Sopenharmony_ci u32 data; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci /* Retrieve the device port number */ 84262306a36Sopenharmony_ci port = ndev->peers[pidx].port; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci /* Check whether PCIe link is up */ 84562306a36Sopenharmony_ci data = idt_sw_read(ndev, portdata_tbl[port].sts); 84662306a36Sopenharmony_ci if (!(data & IDT_SWPORTxSTS_LINKUP)) 84762306a36Sopenharmony_ci return false; 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci /* Check whether bus mastering is enabled on the peer port */ 85062306a36Sopenharmony_ci data = idt_sw_read(ndev, portdata_tbl[port].pcicmdsts); 85162306a36Sopenharmony_ci if (!(data & IDT_PCICMDSTS_BME)) 85262306a36Sopenharmony_ci return false; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci /* Check if Completion TLPs translation is enabled on the peer port */ 85562306a36Sopenharmony_ci data = idt_sw_read(ndev, portdata_tbl[port].ntctl); 85662306a36Sopenharmony_ci if (!(data & IDT_NTCTL_CPEN)) 85762306a36Sopenharmony_ci return false; 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci /* Read Mapping table entry corresponding to the peer partition */ 86062306a36Sopenharmony_ci spin_lock_irqsave(&ndev->mtbl_lock, irqflags); 86162306a36Sopenharmony_ci idt_nt_write(ndev, IDT_NT_NTMTBLADDR, ndev->peers[pidx].part); 86262306a36Sopenharmony_ci data = idt_nt_read(ndev, IDT_NT_NTMTBLDATA); 86362306a36Sopenharmony_ci spin_unlock_irqrestore(&ndev->mtbl_lock, irqflags); 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci return !!(data & IDT_NTMTBLDATA_VALID); 86662306a36Sopenharmony_ci} 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci/* 86962306a36Sopenharmony_ci * idt_ntb_link_is_up() - get the current ntb link state (NTB API callback) 87062306a36Sopenharmony_ci * @ntb: NTB device context. 87162306a36Sopenharmony_ci * @speed: OUT - The link speed expressed as PCIe generation number. 87262306a36Sopenharmony_ci * @width: OUT - The link width expressed as the number of PCIe lanes. 87362306a36Sopenharmony_ci * 87462306a36Sopenharmony_ci * Get the bitfield of NTB link states for all peer ports 87562306a36Sopenharmony_ci * 87662306a36Sopenharmony_ci * Return: bitfield of indexed ports link state: bit is set/cleared if the 87762306a36Sopenharmony_ci * link is up/down respectively. 87862306a36Sopenharmony_ci */ 87962306a36Sopenharmony_cistatic u64 idt_ntb_link_is_up(struct ntb_dev *ntb, 88062306a36Sopenharmony_ci enum ntb_speed *speed, enum ntb_width *width) 88162306a36Sopenharmony_ci{ 88262306a36Sopenharmony_ci struct idt_ntb_dev *ndev = to_ndev_ntb(ntb); 88362306a36Sopenharmony_ci unsigned char pidx; 88462306a36Sopenharmony_ci u64 status; 88562306a36Sopenharmony_ci u32 data; 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci /* Retrieve the local link speed and width */ 88862306a36Sopenharmony_ci if (speed != NULL || width != NULL) { 88962306a36Sopenharmony_ci data = idt_nt_read(ndev, IDT_NT_PCIELCTLSTS); 89062306a36Sopenharmony_ci if (speed != NULL) 89162306a36Sopenharmony_ci *speed = GET_FIELD(PCIELCTLSTS_CLS, data); 89262306a36Sopenharmony_ci if (width != NULL) 89362306a36Sopenharmony_ci *width = GET_FIELD(PCIELCTLSTS_NLW, data); 89462306a36Sopenharmony_ci } 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci /* If local NTB link isn't up then all the links are considered down */ 89762306a36Sopenharmony_ci if (!idt_ntb_local_link_is_up(ndev)) 89862306a36Sopenharmony_ci return 0; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci /* Collect all the peer ports link states into the bitfield */ 90162306a36Sopenharmony_ci status = 0; 90262306a36Sopenharmony_ci for (pidx = 0; pidx < ndev->peer_cnt; pidx++) { 90362306a36Sopenharmony_ci if (idt_ntb_peer_link_is_up(ndev, pidx)) 90462306a36Sopenharmony_ci status |= ((u64)1 << pidx); 90562306a36Sopenharmony_ci } 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci return status; 90862306a36Sopenharmony_ci} 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci/* 91162306a36Sopenharmony_ci * idt_ntb_link_enable() - enable local port ntb link (NTB API callback) 91262306a36Sopenharmony_ci * @ntb: NTB device context. 91362306a36Sopenharmony_ci * @max_speed: The maximum link speed expressed as PCIe generation number. 91462306a36Sopenharmony_ci * @max_width: The maximum link width expressed as the number of PCIe lanes. 91562306a36Sopenharmony_ci * 91662306a36Sopenharmony_ci * Enable just local NTB link. PCIe link parameters are ignored. 91762306a36Sopenharmony_ci * 91862306a36Sopenharmony_ci * Return: always zero. 91962306a36Sopenharmony_ci */ 92062306a36Sopenharmony_cistatic int idt_ntb_link_enable(struct ntb_dev *ntb, enum ntb_speed speed, 92162306a36Sopenharmony_ci enum ntb_width width) 92262306a36Sopenharmony_ci{ 92362306a36Sopenharmony_ci struct idt_ntb_dev *ndev = to_ndev_ntb(ntb); 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci /* Just enable the local NTB link */ 92662306a36Sopenharmony_ci idt_ntb_local_link_enable(ndev); 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci dev_dbg(&ndev->ntb.pdev->dev, "Local NTB link enabled"); 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci return 0; 93162306a36Sopenharmony_ci} 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci/* 93462306a36Sopenharmony_ci * idt_ntb_link_disable() - disable local port ntb link (NTB API callback) 93562306a36Sopenharmony_ci * @ntb: NTB device context. 93662306a36Sopenharmony_ci * 93762306a36Sopenharmony_ci * Disable just local NTB link. 93862306a36Sopenharmony_ci * 93962306a36Sopenharmony_ci * Return: always zero. 94062306a36Sopenharmony_ci */ 94162306a36Sopenharmony_cistatic int idt_ntb_link_disable(struct ntb_dev *ntb) 94262306a36Sopenharmony_ci{ 94362306a36Sopenharmony_ci struct idt_ntb_dev *ndev = to_ndev_ntb(ntb); 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci /* Just disable the local NTB link */ 94662306a36Sopenharmony_ci idt_ntb_local_link_disable(ndev); 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci dev_dbg(&ndev->ntb.pdev->dev, "Local NTB link disabled"); 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci return 0; 95162306a36Sopenharmony_ci} 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci/*============================================================================= 95462306a36Sopenharmony_ci * 4. Memory Window operations 95562306a36Sopenharmony_ci * 95662306a36Sopenharmony_ci * IDT PCIe-switches have two types of memory windows: MWs with direct 95762306a36Sopenharmony_ci * address translation and MWs with LUT based translation. The first type of 95862306a36Sopenharmony_ci * MWs is simple map of corresponding BAR address space to a memory space 95962306a36Sopenharmony_ci * of specified target port. So it implemets just ont-to-one mapping. Lookup 96062306a36Sopenharmony_ci * table in its turn can map one BAR address space to up to 24 different 96162306a36Sopenharmony_ci * memory spaces of different ports. 96262306a36Sopenharmony_ci * NT-functions BARs can be turned on to implement either direct or lookup 96362306a36Sopenharmony_ci * table based address translations, so: 96462306a36Sopenharmony_ci * BAR0 - NT configuration registers space/direct address translation 96562306a36Sopenharmony_ci * BAR1 - direct address translation/upper address of BAR0x64 96662306a36Sopenharmony_ci * BAR2 - direct address translation/Lookup table with either 12 or 24 entries 96762306a36Sopenharmony_ci * BAR3 - direct address translation/upper address of BAR2x64 96862306a36Sopenharmony_ci * BAR4 - direct address translation/Lookup table with either 12 or 24 entries 96962306a36Sopenharmony_ci * BAR5 - direct address translation/upper address of BAR4x64 97062306a36Sopenharmony_ci * Additionally BAR2 and BAR4 can't have 24-entries LUT enabled at the same 97162306a36Sopenharmony_ci * time. Since the BARs setup can be rather complicated this driver implements 97262306a36Sopenharmony_ci * a scanning algorithm to have all the possible memory windows configuration 97362306a36Sopenharmony_ci * covered. 97462306a36Sopenharmony_ci * 97562306a36Sopenharmony_ci * NOTE 1 BAR setup must be done before Linux kernel enumerated NT-function 97662306a36Sopenharmony_ci * of any port, so this driver would have memory windows configurations fixed. 97762306a36Sopenharmony_ci * In this way all initializations must be performed either by platform BIOS 97862306a36Sopenharmony_ci * or using EEPROM connected to IDT PCIe-switch master SMBus. 97962306a36Sopenharmony_ci * 98062306a36Sopenharmony_ci * NOTE 2 This driver expects BAR0 mapping NT-function configuration space. 98162306a36Sopenharmony_ci * Easy calculation can give us an upper boundary of 29 possible memory windows 98262306a36Sopenharmony_ci * per each NT-function if all the BARs are of 32bit type. 98362306a36Sopenharmony_ci *============================================================================= 98462306a36Sopenharmony_ci */ 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci/* 98762306a36Sopenharmony_ci * idt_get_mw_count() - get memory window count 98862306a36Sopenharmony_ci * @mw_type: Memory window type 98962306a36Sopenharmony_ci * 99062306a36Sopenharmony_ci * Return: number of memory windows with respect to the BAR type 99162306a36Sopenharmony_ci */ 99262306a36Sopenharmony_cistatic inline unsigned char idt_get_mw_count(enum idt_mw_type mw_type) 99362306a36Sopenharmony_ci{ 99462306a36Sopenharmony_ci switch (mw_type) { 99562306a36Sopenharmony_ci case IDT_MW_DIR: 99662306a36Sopenharmony_ci return 1; 99762306a36Sopenharmony_ci case IDT_MW_LUT12: 99862306a36Sopenharmony_ci return 12; 99962306a36Sopenharmony_ci case IDT_MW_LUT24: 100062306a36Sopenharmony_ci return 24; 100162306a36Sopenharmony_ci default: 100262306a36Sopenharmony_ci break; 100362306a36Sopenharmony_ci } 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci return 0; 100662306a36Sopenharmony_ci} 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci/* 100962306a36Sopenharmony_ci * idt_get_mw_name() - get memory window name 101062306a36Sopenharmony_ci * @mw_type: Memory window type 101162306a36Sopenharmony_ci * 101262306a36Sopenharmony_ci * Return: pointer to a string with name 101362306a36Sopenharmony_ci */ 101462306a36Sopenharmony_cistatic inline char *idt_get_mw_name(enum idt_mw_type mw_type) 101562306a36Sopenharmony_ci{ 101662306a36Sopenharmony_ci switch (mw_type) { 101762306a36Sopenharmony_ci case IDT_MW_DIR: 101862306a36Sopenharmony_ci return "DIR "; 101962306a36Sopenharmony_ci case IDT_MW_LUT12: 102062306a36Sopenharmony_ci return "LUT12"; 102162306a36Sopenharmony_ci case IDT_MW_LUT24: 102262306a36Sopenharmony_ci return "LUT24"; 102362306a36Sopenharmony_ci default: 102462306a36Sopenharmony_ci break; 102562306a36Sopenharmony_ci } 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci return "unknown"; 102862306a36Sopenharmony_ci} 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci/* 103162306a36Sopenharmony_ci * idt_scan_mws() - scan memory windows of the port 103262306a36Sopenharmony_ci * @ndev: IDT NTB hardware driver descriptor 103362306a36Sopenharmony_ci * @port: Port to get number of memory windows for 103462306a36Sopenharmony_ci * @mw_cnt: Out - number of memory windows 103562306a36Sopenharmony_ci * 103662306a36Sopenharmony_ci * It walks over BAR setup registers of the specified port and determines 103762306a36Sopenharmony_ci * the memory windows parameters if any activated. 103862306a36Sopenharmony_ci * 103962306a36Sopenharmony_ci * Return: array of memory windows 104062306a36Sopenharmony_ci */ 104162306a36Sopenharmony_cistatic struct idt_mw_cfg *idt_scan_mws(struct idt_ntb_dev *ndev, int port, 104262306a36Sopenharmony_ci unsigned char *mw_cnt) 104362306a36Sopenharmony_ci{ 104462306a36Sopenharmony_ci struct idt_mw_cfg mws[IDT_MAX_NR_MWS], *ret_mws; 104562306a36Sopenharmony_ci const struct idt_ntb_bar *bars; 104662306a36Sopenharmony_ci enum idt_mw_type mw_type; 104762306a36Sopenharmony_ci unsigned char widx, bidx, en_cnt; 104862306a36Sopenharmony_ci bool bar_64bit = false; 104962306a36Sopenharmony_ci int aprt_size; 105062306a36Sopenharmony_ci u32 data; 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci /* Retrieve the array of the BARs registers */ 105362306a36Sopenharmony_ci bars = portdata_tbl[port].bars; 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci /* Scan all the BARs belonging to the port */ 105662306a36Sopenharmony_ci *mw_cnt = 0; 105762306a36Sopenharmony_ci for (bidx = 0; bidx < IDT_BAR_CNT; bidx += 1 + bar_64bit) { 105862306a36Sopenharmony_ci /* Read BARSETUP register value */ 105962306a36Sopenharmony_ci data = idt_sw_read(ndev, bars[bidx].setup); 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci /* Skip disabled BARs */ 106262306a36Sopenharmony_ci if (!(data & IDT_BARSETUP_EN)) { 106362306a36Sopenharmony_ci bar_64bit = false; 106462306a36Sopenharmony_ci continue; 106562306a36Sopenharmony_ci } 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci /* Skip next BARSETUP if current one has 64bit addressing */ 106862306a36Sopenharmony_ci bar_64bit = IS_FLD_SET(BARSETUP_TYPE, data, 64); 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci /* Skip configuration space mapping BARs */ 107162306a36Sopenharmony_ci if (data & IDT_BARSETUP_MODE_CFG) 107262306a36Sopenharmony_ci continue; 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci /* Retrieve MW type/entries count and aperture size */ 107562306a36Sopenharmony_ci mw_type = GET_FIELD(BARSETUP_ATRAN, data); 107662306a36Sopenharmony_ci en_cnt = idt_get_mw_count(mw_type); 107762306a36Sopenharmony_ci aprt_size = (u64)1 << GET_FIELD(BARSETUP_SIZE, data); 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci /* Save configurations of all available memory windows */ 108062306a36Sopenharmony_ci for (widx = 0; widx < en_cnt; widx++, (*mw_cnt)++) { 108162306a36Sopenharmony_ci /* 108262306a36Sopenharmony_ci * IDT can expose a limited number of MWs, so it's bug 108362306a36Sopenharmony_ci * to have more than the driver expects 108462306a36Sopenharmony_ci */ 108562306a36Sopenharmony_ci if (*mw_cnt >= IDT_MAX_NR_MWS) 108662306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci /* Save basic MW info */ 108962306a36Sopenharmony_ci mws[*mw_cnt].type = mw_type; 109062306a36Sopenharmony_ci mws[*mw_cnt].bar = bidx; 109162306a36Sopenharmony_ci mws[*mw_cnt].idx = widx; 109262306a36Sopenharmony_ci /* It's always DWORD aligned */ 109362306a36Sopenharmony_ci mws[*mw_cnt].addr_align = IDT_TRANS_ALIGN; 109462306a36Sopenharmony_ci /* DIR and LUT approachs differently configure MWs */ 109562306a36Sopenharmony_ci if (mw_type == IDT_MW_DIR) 109662306a36Sopenharmony_ci mws[*mw_cnt].size_max = aprt_size; 109762306a36Sopenharmony_ci else if (mw_type == IDT_MW_LUT12) 109862306a36Sopenharmony_ci mws[*mw_cnt].size_max = aprt_size / 16; 109962306a36Sopenharmony_ci else 110062306a36Sopenharmony_ci mws[*mw_cnt].size_max = aprt_size / 32; 110162306a36Sopenharmony_ci mws[*mw_cnt].size_align = (mw_type == IDT_MW_DIR) ? 110262306a36Sopenharmony_ci IDT_DIR_SIZE_ALIGN : mws[*mw_cnt].size_max; 110362306a36Sopenharmony_ci } 110462306a36Sopenharmony_ci } 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci /* Allocate memory for memory window descriptors */ 110762306a36Sopenharmony_ci ret_mws = devm_kcalloc(&ndev->ntb.pdev->dev, *mw_cnt, sizeof(*ret_mws), 110862306a36Sopenharmony_ci GFP_KERNEL); 110962306a36Sopenharmony_ci if (!ret_mws) 111062306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci /* Copy the info of detected memory windows */ 111362306a36Sopenharmony_ci memcpy(ret_mws, mws, (*mw_cnt)*sizeof(*ret_mws)); 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci return ret_mws; 111662306a36Sopenharmony_ci} 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci/* 111962306a36Sopenharmony_ci * idt_init_mws() - initialize memory windows subsystem 112062306a36Sopenharmony_ci * @ndev: IDT NTB hardware driver descriptor 112162306a36Sopenharmony_ci * 112262306a36Sopenharmony_ci * Scan BAR setup registers of local and peer ports to determine the 112362306a36Sopenharmony_ci * outbound and inbound memory windows parameters 112462306a36Sopenharmony_ci * 112562306a36Sopenharmony_ci * Return: zero on success, otherwise a negative error number 112662306a36Sopenharmony_ci */ 112762306a36Sopenharmony_cistatic int idt_init_mws(struct idt_ntb_dev *ndev) 112862306a36Sopenharmony_ci{ 112962306a36Sopenharmony_ci struct idt_ntb_peer *peer; 113062306a36Sopenharmony_ci unsigned char pidx; 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci /* Scan memory windows of the local port */ 113362306a36Sopenharmony_ci ndev->mws = idt_scan_mws(ndev, ndev->port, &ndev->mw_cnt); 113462306a36Sopenharmony_ci if (IS_ERR(ndev->mws)) { 113562306a36Sopenharmony_ci dev_err(&ndev->ntb.pdev->dev, 113662306a36Sopenharmony_ci "Failed to scan mws of local port %hhu", ndev->port); 113762306a36Sopenharmony_ci return PTR_ERR(ndev->mws); 113862306a36Sopenharmony_ci } 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci /* Scan memory windows of the peer ports */ 114162306a36Sopenharmony_ci for (pidx = 0; pidx < ndev->peer_cnt; pidx++) { 114262306a36Sopenharmony_ci peer = &ndev->peers[pidx]; 114362306a36Sopenharmony_ci peer->mws = idt_scan_mws(ndev, peer->port, &peer->mw_cnt); 114462306a36Sopenharmony_ci if (IS_ERR(peer->mws)) { 114562306a36Sopenharmony_ci dev_err(&ndev->ntb.pdev->dev, 114662306a36Sopenharmony_ci "Failed to scan mws of port %hhu", peer->port); 114762306a36Sopenharmony_ci return PTR_ERR(peer->mws); 114862306a36Sopenharmony_ci } 114962306a36Sopenharmony_ci } 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci /* Initialize spin locker of the LUT registers */ 115262306a36Sopenharmony_ci spin_lock_init(&ndev->lut_lock); 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci dev_dbg(&ndev->ntb.pdev->dev, "Outbound and inbound MWs initialized"); 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci return 0; 115762306a36Sopenharmony_ci} 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci/* 116062306a36Sopenharmony_ci * idt_ntb_mw_count() - number of inbound memory windows (NTB API callback) 116162306a36Sopenharmony_ci * @ntb: NTB device context. 116262306a36Sopenharmony_ci * @pidx: Port index of peer device. 116362306a36Sopenharmony_ci * 116462306a36Sopenharmony_ci * The value is returned for the specified peer, so generally speaking it can 116562306a36Sopenharmony_ci * be different for different port depending on the IDT PCIe-switch 116662306a36Sopenharmony_ci * initialization. 116762306a36Sopenharmony_ci * 116862306a36Sopenharmony_ci * Return: the number of memory windows. 116962306a36Sopenharmony_ci */ 117062306a36Sopenharmony_cistatic int idt_ntb_mw_count(struct ntb_dev *ntb, int pidx) 117162306a36Sopenharmony_ci{ 117262306a36Sopenharmony_ci struct idt_ntb_dev *ndev = to_ndev_ntb(ntb); 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci if (pidx < 0 || ndev->peer_cnt <= pidx) 117562306a36Sopenharmony_ci return -EINVAL; 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci return ndev->peers[pidx].mw_cnt; 117862306a36Sopenharmony_ci} 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci/* 118162306a36Sopenharmony_ci * idt_ntb_mw_get_align() - inbound memory window parameters (NTB API callback) 118262306a36Sopenharmony_ci * @ntb: NTB device context. 118362306a36Sopenharmony_ci * @pidx: Port index of peer device. 118462306a36Sopenharmony_ci * @widx: Memory window index. 118562306a36Sopenharmony_ci * @addr_align: OUT - the base alignment for translating the memory window 118662306a36Sopenharmony_ci * @size_align: OUT - the size alignment for translating the memory window 118762306a36Sopenharmony_ci * @size_max: OUT - the maximum size of the memory window 118862306a36Sopenharmony_ci * 118962306a36Sopenharmony_ci * The peer memory window parameters have already been determined, so just 119062306a36Sopenharmony_ci * return the corresponding values, which mustn't change within session. 119162306a36Sopenharmony_ci * 119262306a36Sopenharmony_ci * Return: Zero on success, otherwise a negative error number. 119362306a36Sopenharmony_ci */ 119462306a36Sopenharmony_cistatic int idt_ntb_mw_get_align(struct ntb_dev *ntb, int pidx, int widx, 119562306a36Sopenharmony_ci resource_size_t *addr_align, 119662306a36Sopenharmony_ci resource_size_t *size_align, 119762306a36Sopenharmony_ci resource_size_t *size_max) 119862306a36Sopenharmony_ci{ 119962306a36Sopenharmony_ci struct idt_ntb_dev *ndev = to_ndev_ntb(ntb); 120062306a36Sopenharmony_ci struct idt_ntb_peer *peer; 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ci if (pidx < 0 || ndev->peer_cnt <= pidx) 120362306a36Sopenharmony_ci return -EINVAL; 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci peer = &ndev->peers[pidx]; 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci if (widx < 0 || peer->mw_cnt <= widx) 120862306a36Sopenharmony_ci return -EINVAL; 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci if (addr_align != NULL) 121162306a36Sopenharmony_ci *addr_align = peer->mws[widx].addr_align; 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci if (size_align != NULL) 121462306a36Sopenharmony_ci *size_align = peer->mws[widx].size_align; 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci if (size_max != NULL) 121762306a36Sopenharmony_ci *size_max = peer->mws[widx].size_max; 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci return 0; 122062306a36Sopenharmony_ci} 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci/* 122362306a36Sopenharmony_ci * idt_ntb_peer_mw_count() - number of outbound memory windows 122462306a36Sopenharmony_ci * (NTB API callback) 122562306a36Sopenharmony_ci * @ntb: NTB device context. 122662306a36Sopenharmony_ci * 122762306a36Sopenharmony_ci * Outbound memory windows parameters have been determined based on the 122862306a36Sopenharmony_ci * BAR setup registers value, which are mostly constants within one session. 122962306a36Sopenharmony_ci * 123062306a36Sopenharmony_ci * Return: the number of memory windows. 123162306a36Sopenharmony_ci */ 123262306a36Sopenharmony_cistatic int idt_ntb_peer_mw_count(struct ntb_dev *ntb) 123362306a36Sopenharmony_ci{ 123462306a36Sopenharmony_ci struct idt_ntb_dev *ndev = to_ndev_ntb(ntb); 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci return ndev->mw_cnt; 123762306a36Sopenharmony_ci} 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci/* 124062306a36Sopenharmony_ci * idt_ntb_peer_mw_get_addr() - get map address of an outbound memory window 124162306a36Sopenharmony_ci * (NTB API callback) 124262306a36Sopenharmony_ci * @ntb: NTB device context. 124362306a36Sopenharmony_ci * @widx: Memory window index (within ntb_peer_mw_count() return value). 124462306a36Sopenharmony_ci * @base: OUT - the base address of mapping region. 124562306a36Sopenharmony_ci * @size: OUT - the size of mapping region. 124662306a36Sopenharmony_ci * 124762306a36Sopenharmony_ci * Return just parameters of BAR resources mapping. Size reflects just the size 124862306a36Sopenharmony_ci * of the resource 124962306a36Sopenharmony_ci * 125062306a36Sopenharmony_ci * Return: Zero on success, otherwise a negative error number. 125162306a36Sopenharmony_ci */ 125262306a36Sopenharmony_cistatic int idt_ntb_peer_mw_get_addr(struct ntb_dev *ntb, int widx, 125362306a36Sopenharmony_ci phys_addr_t *base, resource_size_t *size) 125462306a36Sopenharmony_ci{ 125562306a36Sopenharmony_ci struct idt_ntb_dev *ndev = to_ndev_ntb(ntb); 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci if (widx < 0 || ndev->mw_cnt <= widx) 125862306a36Sopenharmony_ci return -EINVAL; 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_ci /* Mapping address is just properly shifted BAR resource start */ 126162306a36Sopenharmony_ci if (base != NULL) 126262306a36Sopenharmony_ci *base = pci_resource_start(ntb->pdev, ndev->mws[widx].bar) + 126362306a36Sopenharmony_ci ndev->mws[widx].idx * ndev->mws[widx].size_max; 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_ci /* Mapping size has already been calculated at MWs scanning */ 126662306a36Sopenharmony_ci if (size != NULL) 126762306a36Sopenharmony_ci *size = ndev->mws[widx].size_max; 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci return 0; 127062306a36Sopenharmony_ci} 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci/* 127362306a36Sopenharmony_ci * idt_ntb_peer_mw_set_trans() - set a translation address of a memory window 127462306a36Sopenharmony_ci * (NTB API callback) 127562306a36Sopenharmony_ci * @ntb: NTB device context. 127662306a36Sopenharmony_ci * @pidx: Port index of peer device the translation address received from. 127762306a36Sopenharmony_ci * @widx: Memory window index. 127862306a36Sopenharmony_ci * @addr: The dma address of the shared memory to access. 127962306a36Sopenharmony_ci * @size: The size of the shared memory to access. 128062306a36Sopenharmony_ci * 128162306a36Sopenharmony_ci * The Direct address translation and LUT base translation is initialized a 128262306a36Sopenharmony_ci * bit differenet. Although the parameters restriction are now determined by 128362306a36Sopenharmony_ci * the same code. 128462306a36Sopenharmony_ci * 128562306a36Sopenharmony_ci * Return: Zero on success, otherwise an error number. 128662306a36Sopenharmony_ci */ 128762306a36Sopenharmony_cistatic int idt_ntb_peer_mw_set_trans(struct ntb_dev *ntb, int pidx, int widx, 128862306a36Sopenharmony_ci u64 addr, resource_size_t size) 128962306a36Sopenharmony_ci{ 129062306a36Sopenharmony_ci struct idt_ntb_dev *ndev = to_ndev_ntb(ntb); 129162306a36Sopenharmony_ci struct idt_mw_cfg *mw_cfg; 129262306a36Sopenharmony_ci u32 data = 0, lutoff = 0; 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci if (pidx < 0 || ndev->peer_cnt <= pidx) 129562306a36Sopenharmony_ci return -EINVAL; 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_ci if (widx < 0 || ndev->mw_cnt <= widx) 129862306a36Sopenharmony_ci return -EINVAL; 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci /* 130162306a36Sopenharmony_ci * Retrieve the memory window config to make sure the passed arguments 130262306a36Sopenharmony_ci * fit it restrictions 130362306a36Sopenharmony_ci */ 130462306a36Sopenharmony_ci mw_cfg = &ndev->mws[widx]; 130562306a36Sopenharmony_ci if (!IS_ALIGNED(addr, mw_cfg->addr_align)) 130662306a36Sopenharmony_ci return -EINVAL; 130762306a36Sopenharmony_ci if (!IS_ALIGNED(size, mw_cfg->size_align) || size > mw_cfg->size_max) 130862306a36Sopenharmony_ci return -EINVAL; 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_ci /* DIR and LUT based translations are initialized differently */ 131162306a36Sopenharmony_ci if (mw_cfg->type == IDT_MW_DIR) { 131262306a36Sopenharmony_ci const struct idt_ntb_bar *bar = &ntdata_tbl.bars[mw_cfg->bar]; 131362306a36Sopenharmony_ci u64 limit; 131462306a36Sopenharmony_ci /* Set destination partition of translation */ 131562306a36Sopenharmony_ci data = idt_nt_read(ndev, bar->setup); 131662306a36Sopenharmony_ci data = SET_FIELD(BARSETUP_TPART, data, ndev->peers[pidx].part); 131762306a36Sopenharmony_ci idt_nt_write(ndev, bar->setup, data); 131862306a36Sopenharmony_ci /* Set translation base address */ 131962306a36Sopenharmony_ci idt_nt_write(ndev, bar->ltbase, (u32)addr); 132062306a36Sopenharmony_ci idt_nt_write(ndev, bar->utbase, (u32)(addr >> 32)); 132162306a36Sopenharmony_ci /* Set the custom BAR aperture limit */ 132262306a36Sopenharmony_ci limit = pci_bus_address(ntb->pdev, mw_cfg->bar) + size; 132362306a36Sopenharmony_ci idt_nt_write(ndev, bar->limit, (u32)limit); 132462306a36Sopenharmony_ci if (IS_FLD_SET(BARSETUP_TYPE, data, 64)) 132562306a36Sopenharmony_ci idt_nt_write(ndev, (bar + 1)->limit, (limit >> 32)); 132662306a36Sopenharmony_ci } else { 132762306a36Sopenharmony_ci unsigned long irqflags; 132862306a36Sopenharmony_ci /* Initialize corresponding LUT entry */ 132962306a36Sopenharmony_ci lutoff = SET_FIELD(LUTOFFSET_INDEX, 0, mw_cfg->idx) | 133062306a36Sopenharmony_ci SET_FIELD(LUTOFFSET_BAR, 0, mw_cfg->bar); 133162306a36Sopenharmony_ci data = SET_FIELD(LUTUDATA_PART, 0, ndev->peers[pidx].part) | 133262306a36Sopenharmony_ci IDT_LUTUDATA_VALID; 133362306a36Sopenharmony_ci spin_lock_irqsave(&ndev->lut_lock, irqflags); 133462306a36Sopenharmony_ci idt_nt_write(ndev, IDT_NT_LUTOFFSET, lutoff); 133562306a36Sopenharmony_ci idt_nt_write(ndev, IDT_NT_LUTLDATA, (u32)addr); 133662306a36Sopenharmony_ci idt_nt_write(ndev, IDT_NT_LUTMDATA, (u32)(addr >> 32)); 133762306a36Sopenharmony_ci idt_nt_write(ndev, IDT_NT_LUTUDATA, data); 133862306a36Sopenharmony_ci spin_unlock_irqrestore(&ndev->lut_lock, irqflags); 133962306a36Sopenharmony_ci /* Limit address isn't specified since size is fixed for LUT */ 134062306a36Sopenharmony_ci } 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci return 0; 134362306a36Sopenharmony_ci} 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_ci/* 134662306a36Sopenharmony_ci * idt_ntb_peer_mw_clear_trans() - clear the outbound MW translation address 134762306a36Sopenharmony_ci * (NTB API callback) 134862306a36Sopenharmony_ci * @ntb: NTB device context. 134962306a36Sopenharmony_ci * @pidx: Port index of peer device. 135062306a36Sopenharmony_ci * @widx: Memory window index. 135162306a36Sopenharmony_ci * 135262306a36Sopenharmony_ci * It effectively disables the translation over the specified outbound MW. 135362306a36Sopenharmony_ci * 135462306a36Sopenharmony_ci * Return: Zero on success, otherwise an error number. 135562306a36Sopenharmony_ci */ 135662306a36Sopenharmony_cistatic int idt_ntb_peer_mw_clear_trans(struct ntb_dev *ntb, int pidx, 135762306a36Sopenharmony_ci int widx) 135862306a36Sopenharmony_ci{ 135962306a36Sopenharmony_ci struct idt_ntb_dev *ndev = to_ndev_ntb(ntb); 136062306a36Sopenharmony_ci struct idt_mw_cfg *mw_cfg; 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_ci if (pidx < 0 || ndev->peer_cnt <= pidx) 136362306a36Sopenharmony_ci return -EINVAL; 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_ci if (widx < 0 || ndev->mw_cnt <= widx) 136662306a36Sopenharmony_ci return -EINVAL; 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci mw_cfg = &ndev->mws[widx]; 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_ci /* DIR and LUT based translations are initialized differently */ 137162306a36Sopenharmony_ci if (mw_cfg->type == IDT_MW_DIR) { 137262306a36Sopenharmony_ci const struct idt_ntb_bar *bar = &ntdata_tbl.bars[mw_cfg->bar]; 137362306a36Sopenharmony_ci u32 data; 137462306a36Sopenharmony_ci /* Read BARSETUP to check BAR type */ 137562306a36Sopenharmony_ci data = idt_nt_read(ndev, bar->setup); 137662306a36Sopenharmony_ci /* Disable translation by specifying zero BAR limit */ 137762306a36Sopenharmony_ci idt_nt_write(ndev, bar->limit, 0); 137862306a36Sopenharmony_ci if (IS_FLD_SET(BARSETUP_TYPE, data, 64)) 137962306a36Sopenharmony_ci idt_nt_write(ndev, (bar + 1)->limit, 0); 138062306a36Sopenharmony_ci } else { 138162306a36Sopenharmony_ci unsigned long irqflags; 138262306a36Sopenharmony_ci u32 lutoff; 138362306a36Sopenharmony_ci /* Clear the corresponding LUT entry up */ 138462306a36Sopenharmony_ci lutoff = SET_FIELD(LUTOFFSET_INDEX, 0, mw_cfg->idx) | 138562306a36Sopenharmony_ci SET_FIELD(LUTOFFSET_BAR, 0, mw_cfg->bar); 138662306a36Sopenharmony_ci spin_lock_irqsave(&ndev->lut_lock, irqflags); 138762306a36Sopenharmony_ci idt_nt_write(ndev, IDT_NT_LUTOFFSET, lutoff); 138862306a36Sopenharmony_ci idt_nt_write(ndev, IDT_NT_LUTLDATA, 0); 138962306a36Sopenharmony_ci idt_nt_write(ndev, IDT_NT_LUTMDATA, 0); 139062306a36Sopenharmony_ci idt_nt_write(ndev, IDT_NT_LUTUDATA, 0); 139162306a36Sopenharmony_ci spin_unlock_irqrestore(&ndev->lut_lock, irqflags); 139262306a36Sopenharmony_ci } 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_ci return 0; 139562306a36Sopenharmony_ci} 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_ci/*============================================================================= 139862306a36Sopenharmony_ci * 5. Doorbell operations 139962306a36Sopenharmony_ci * 140062306a36Sopenharmony_ci * Doorbell functionality of IDT PCIe-switches is pretty unusual. First of 140162306a36Sopenharmony_ci * all there is global doorbell register which state can be changed by any 140262306a36Sopenharmony_ci * NT-function of the IDT device in accordance with global permissions. These 140362306a36Sopenharmony_ci * permissions configs are not supported by NTB API, so it must be done by 140462306a36Sopenharmony_ci * either BIOS or EEPROM settings. In the same way the state of the global 140562306a36Sopenharmony_ci * doorbell is reflected to the NT-functions local inbound doorbell registers. 140662306a36Sopenharmony_ci * It can lead to situations when client driver sets some peer doorbell bits 140762306a36Sopenharmony_ci * and get them bounced back to local inbound doorbell if permissions are 140862306a36Sopenharmony_ci * granted. 140962306a36Sopenharmony_ci * Secondly there is just one IRQ vector for Doorbell, Message, Temperature 141062306a36Sopenharmony_ci * and Switch events, so if client driver left any of Doorbell bits set and 141162306a36Sopenharmony_ci * some other event occurred, the driver will be notified of Doorbell event 141262306a36Sopenharmony_ci * again. 141362306a36Sopenharmony_ci *============================================================================= 141462306a36Sopenharmony_ci */ 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_ci/* 141762306a36Sopenharmony_ci * idt_db_isr() - doorbell event ISR 141862306a36Sopenharmony_ci * @ndev: IDT NTB hardware driver descriptor 141962306a36Sopenharmony_ci * @ntint_sts: NT-function interrupt status 142062306a36Sopenharmony_ci * 142162306a36Sopenharmony_ci * Doorbell event happans when DBELL bit of NTINTSTS switches from 0 to 1. 142262306a36Sopenharmony_ci * It happens only when unmasked doorbell bits are set to ones on completely 142362306a36Sopenharmony_ci * zeroed doorbell register. 142462306a36Sopenharmony_ci * The method is called from PCIe ISR bottom-half routine. 142562306a36Sopenharmony_ci */ 142662306a36Sopenharmony_cistatic void idt_db_isr(struct idt_ntb_dev *ndev, u32 ntint_sts) 142762306a36Sopenharmony_ci{ 142862306a36Sopenharmony_ci /* 142962306a36Sopenharmony_ci * Doorbell IRQ status will be cleaned only when client 143062306a36Sopenharmony_ci * driver unsets all the doorbell bits. 143162306a36Sopenharmony_ci */ 143262306a36Sopenharmony_ci dev_dbg(&ndev->ntb.pdev->dev, "DB IRQ detected %#08x", ntint_sts); 143362306a36Sopenharmony_ci 143462306a36Sopenharmony_ci /* Notify the client driver of possible doorbell state change */ 143562306a36Sopenharmony_ci ntb_db_event(&ndev->ntb, 0); 143662306a36Sopenharmony_ci} 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_ci/* 143962306a36Sopenharmony_ci * idt_ntb_db_valid_mask() - get a mask of doorbell bits supported by the ntb 144062306a36Sopenharmony_ci * (NTB API callback) 144162306a36Sopenharmony_ci * @ntb: NTB device context. 144262306a36Sopenharmony_ci * 144362306a36Sopenharmony_ci * IDT PCIe-switches expose just one Doorbell register of DWORD size. 144462306a36Sopenharmony_ci * 144562306a36Sopenharmony_ci * Return: A mask of doorbell bits supported by the ntb. 144662306a36Sopenharmony_ci */ 144762306a36Sopenharmony_cistatic u64 idt_ntb_db_valid_mask(struct ntb_dev *ntb) 144862306a36Sopenharmony_ci{ 144962306a36Sopenharmony_ci return IDT_DBELL_MASK; 145062306a36Sopenharmony_ci} 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_ci/* 145362306a36Sopenharmony_ci * idt_ntb_db_read() - read the local doorbell register (NTB API callback) 145462306a36Sopenharmony_ci * @ntb: NTB device context. 145562306a36Sopenharmony_ci * 145662306a36Sopenharmony_ci * There is just on inbound doorbell register of each NT-function, so 145762306a36Sopenharmony_ci * this method return it value. 145862306a36Sopenharmony_ci * 145962306a36Sopenharmony_ci * Return: The bits currently set in the local doorbell register. 146062306a36Sopenharmony_ci */ 146162306a36Sopenharmony_cistatic u64 idt_ntb_db_read(struct ntb_dev *ntb) 146262306a36Sopenharmony_ci{ 146362306a36Sopenharmony_ci struct idt_ntb_dev *ndev = to_ndev_ntb(ntb); 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_ci return idt_nt_read(ndev, IDT_NT_INDBELLSTS); 146662306a36Sopenharmony_ci} 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_ci/* 146962306a36Sopenharmony_ci * idt_ntb_db_clear() - clear bits in the local doorbell register 147062306a36Sopenharmony_ci * (NTB API callback) 147162306a36Sopenharmony_ci * @ntb: NTB device context. 147262306a36Sopenharmony_ci * @db_bits: Doorbell bits to clear. 147362306a36Sopenharmony_ci * 147462306a36Sopenharmony_ci * Clear bits of inbound doorbell register by writing ones to it. 147562306a36Sopenharmony_ci * 147662306a36Sopenharmony_ci * NOTE! Invalid bits are always considered cleared so it's not an error 147762306a36Sopenharmony_ci * to clear them over. 147862306a36Sopenharmony_ci * 147962306a36Sopenharmony_ci * Return: always zero as success. 148062306a36Sopenharmony_ci */ 148162306a36Sopenharmony_cistatic int idt_ntb_db_clear(struct ntb_dev *ntb, u64 db_bits) 148262306a36Sopenharmony_ci{ 148362306a36Sopenharmony_ci struct idt_ntb_dev *ndev = to_ndev_ntb(ntb); 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_ci idt_nt_write(ndev, IDT_NT_INDBELLSTS, (u32)db_bits); 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ci return 0; 148862306a36Sopenharmony_ci} 148962306a36Sopenharmony_ci 149062306a36Sopenharmony_ci/* 149162306a36Sopenharmony_ci * idt_ntb_db_read_mask() - read the local doorbell mask (NTB API callback) 149262306a36Sopenharmony_ci * @ntb: NTB device context. 149362306a36Sopenharmony_ci * 149462306a36Sopenharmony_ci * Each inbound doorbell bit can be masked from generating IRQ by setting 149562306a36Sopenharmony_ci * the corresponding bit in inbound doorbell mask. So this method returns 149662306a36Sopenharmony_ci * the value of the register. 149762306a36Sopenharmony_ci * 149862306a36Sopenharmony_ci * Return: The bits currently set in the local doorbell mask register. 149962306a36Sopenharmony_ci */ 150062306a36Sopenharmony_cistatic u64 idt_ntb_db_read_mask(struct ntb_dev *ntb) 150162306a36Sopenharmony_ci{ 150262306a36Sopenharmony_ci struct idt_ntb_dev *ndev = to_ndev_ntb(ntb); 150362306a36Sopenharmony_ci 150462306a36Sopenharmony_ci return idt_nt_read(ndev, IDT_NT_INDBELLMSK); 150562306a36Sopenharmony_ci} 150662306a36Sopenharmony_ci 150762306a36Sopenharmony_ci/* 150862306a36Sopenharmony_ci * idt_ntb_db_set_mask() - set bits in the local doorbell mask 150962306a36Sopenharmony_ci * (NTB API callback) 151062306a36Sopenharmony_ci * @ntb: NTB device context. 151162306a36Sopenharmony_ci * @db_bits: Doorbell mask bits to set. 151262306a36Sopenharmony_ci * 151362306a36Sopenharmony_ci * The inbound doorbell register mask value must be read, then OR'ed with 151462306a36Sopenharmony_ci * passed field and only then set back. 151562306a36Sopenharmony_ci * 151662306a36Sopenharmony_ci * Return: zero on success, negative error if invalid argument passed. 151762306a36Sopenharmony_ci */ 151862306a36Sopenharmony_cistatic int idt_ntb_db_set_mask(struct ntb_dev *ntb, u64 db_bits) 151962306a36Sopenharmony_ci{ 152062306a36Sopenharmony_ci struct idt_ntb_dev *ndev = to_ndev_ntb(ntb); 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_ci return idt_reg_set_bits(ndev, IDT_NT_INDBELLMSK, &ndev->db_mask_lock, 152362306a36Sopenharmony_ci IDT_DBELL_MASK, db_bits); 152462306a36Sopenharmony_ci} 152562306a36Sopenharmony_ci 152662306a36Sopenharmony_ci/* 152762306a36Sopenharmony_ci * idt_ntb_db_clear_mask() - clear bits in the local doorbell mask 152862306a36Sopenharmony_ci * (NTB API callback) 152962306a36Sopenharmony_ci * @ntb: NTB device context. 153062306a36Sopenharmony_ci * @db_bits: Doorbell bits to clear. 153162306a36Sopenharmony_ci * 153262306a36Sopenharmony_ci * The method just clears the set bits up in accordance with the passed 153362306a36Sopenharmony_ci * bitfield. IDT PCIe-switch shall generate an interrupt if there hasn't 153462306a36Sopenharmony_ci * been any unmasked bit set before current unmasking. Otherwise IRQ won't 153562306a36Sopenharmony_ci * be generated since there is only one IRQ vector for all doorbells. 153662306a36Sopenharmony_ci * 153762306a36Sopenharmony_ci * Return: always zero as success 153862306a36Sopenharmony_ci */ 153962306a36Sopenharmony_cistatic int idt_ntb_db_clear_mask(struct ntb_dev *ntb, u64 db_bits) 154062306a36Sopenharmony_ci{ 154162306a36Sopenharmony_ci struct idt_ntb_dev *ndev = to_ndev_ntb(ntb); 154262306a36Sopenharmony_ci 154362306a36Sopenharmony_ci idt_reg_clear_bits(ndev, IDT_NT_INDBELLMSK, &ndev->db_mask_lock, 154462306a36Sopenharmony_ci db_bits); 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_ci return 0; 154762306a36Sopenharmony_ci} 154862306a36Sopenharmony_ci 154962306a36Sopenharmony_ci/* 155062306a36Sopenharmony_ci * idt_ntb_peer_db_set() - set bits in the peer doorbell register 155162306a36Sopenharmony_ci * (NTB API callback) 155262306a36Sopenharmony_ci * @ntb: NTB device context. 155362306a36Sopenharmony_ci * @db_bits: Doorbell bits to set. 155462306a36Sopenharmony_ci * 155562306a36Sopenharmony_ci * IDT PCIe-switches exposes local outbound doorbell register to change peer 155662306a36Sopenharmony_ci * inbound doorbell register state. 155762306a36Sopenharmony_ci * 155862306a36Sopenharmony_ci * Return: zero on success, negative error if invalid argument passed. 155962306a36Sopenharmony_ci */ 156062306a36Sopenharmony_cistatic int idt_ntb_peer_db_set(struct ntb_dev *ntb, u64 db_bits) 156162306a36Sopenharmony_ci{ 156262306a36Sopenharmony_ci struct idt_ntb_dev *ndev = to_ndev_ntb(ntb); 156362306a36Sopenharmony_ci 156462306a36Sopenharmony_ci if (db_bits & ~(u64)IDT_DBELL_MASK) 156562306a36Sopenharmony_ci return -EINVAL; 156662306a36Sopenharmony_ci 156762306a36Sopenharmony_ci idt_nt_write(ndev, IDT_NT_OUTDBELLSET, (u32)db_bits); 156862306a36Sopenharmony_ci return 0; 156962306a36Sopenharmony_ci} 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci/*============================================================================= 157262306a36Sopenharmony_ci * 6. Messaging operations 157362306a36Sopenharmony_ci * 157462306a36Sopenharmony_ci * Each NT-function of IDT PCIe-switch has four inbound and four outbound 157562306a36Sopenharmony_ci * message registers. Each outbound message register can be connected to one or 157662306a36Sopenharmony_ci * even more than one peer inbound message registers by setting global 157762306a36Sopenharmony_ci * configurations. Since NTB API permits one-on-one message registers mapping 157862306a36Sopenharmony_ci * only, the driver acts in according with that restriction. 157962306a36Sopenharmony_ci *============================================================================= 158062306a36Sopenharmony_ci */ 158162306a36Sopenharmony_ci 158262306a36Sopenharmony_ci/* 158362306a36Sopenharmony_ci * idt_init_msg() - initialize messaging interface 158462306a36Sopenharmony_ci * @ndev: IDT NTB hardware driver descriptor 158562306a36Sopenharmony_ci * 158662306a36Sopenharmony_ci * Just initialize the message registers routing tables locker. 158762306a36Sopenharmony_ci */ 158862306a36Sopenharmony_cistatic void idt_init_msg(struct idt_ntb_dev *ndev) 158962306a36Sopenharmony_ci{ 159062306a36Sopenharmony_ci unsigned char midx; 159162306a36Sopenharmony_ci 159262306a36Sopenharmony_ci /* Init the messages routing table lockers */ 159362306a36Sopenharmony_ci for (midx = 0; midx < IDT_MSG_CNT; midx++) 159462306a36Sopenharmony_ci spin_lock_init(&ndev->msg_locks[midx]); 159562306a36Sopenharmony_ci 159662306a36Sopenharmony_ci dev_dbg(&ndev->ntb.pdev->dev, "NTB Messaging initialized"); 159762306a36Sopenharmony_ci} 159862306a36Sopenharmony_ci 159962306a36Sopenharmony_ci/* 160062306a36Sopenharmony_ci * idt_msg_isr() - message event ISR 160162306a36Sopenharmony_ci * @ndev: IDT NTB hardware driver descriptor 160262306a36Sopenharmony_ci * @ntint_sts: NT-function interrupt status 160362306a36Sopenharmony_ci * 160462306a36Sopenharmony_ci * Message event happens when MSG bit of NTINTSTS switches from 0 to 1. 160562306a36Sopenharmony_ci * It happens only when unmasked message status bits are set to ones on 160662306a36Sopenharmony_ci * completely zeroed message status register. 160762306a36Sopenharmony_ci * The method is called from PCIe ISR bottom-half routine. 160862306a36Sopenharmony_ci */ 160962306a36Sopenharmony_cistatic void idt_msg_isr(struct idt_ntb_dev *ndev, u32 ntint_sts) 161062306a36Sopenharmony_ci{ 161162306a36Sopenharmony_ci /* 161262306a36Sopenharmony_ci * Message IRQ status will be cleaned only when client 161362306a36Sopenharmony_ci * driver unsets all the message status bits. 161462306a36Sopenharmony_ci */ 161562306a36Sopenharmony_ci dev_dbg(&ndev->ntb.pdev->dev, "Message IRQ detected %#08x", ntint_sts); 161662306a36Sopenharmony_ci 161762306a36Sopenharmony_ci /* Notify the client driver of possible message status change */ 161862306a36Sopenharmony_ci ntb_msg_event(&ndev->ntb); 161962306a36Sopenharmony_ci} 162062306a36Sopenharmony_ci 162162306a36Sopenharmony_ci/* 162262306a36Sopenharmony_ci * idt_ntb_msg_count() - get the number of message registers (NTB API callback) 162362306a36Sopenharmony_ci * @ntb: NTB device context. 162462306a36Sopenharmony_ci * 162562306a36Sopenharmony_ci * IDT PCIe-switches support four message registers. 162662306a36Sopenharmony_ci * 162762306a36Sopenharmony_ci * Return: the number of message registers. 162862306a36Sopenharmony_ci */ 162962306a36Sopenharmony_cistatic int idt_ntb_msg_count(struct ntb_dev *ntb) 163062306a36Sopenharmony_ci{ 163162306a36Sopenharmony_ci return IDT_MSG_CNT; 163262306a36Sopenharmony_ci} 163362306a36Sopenharmony_ci 163462306a36Sopenharmony_ci/* 163562306a36Sopenharmony_ci * idt_ntb_msg_inbits() - get a bitfield of inbound message registers status 163662306a36Sopenharmony_ci * (NTB API callback) 163762306a36Sopenharmony_ci * @ntb: NTB device context. 163862306a36Sopenharmony_ci * 163962306a36Sopenharmony_ci * NT message status register is shared between inbound and outbound message 164062306a36Sopenharmony_ci * registers status 164162306a36Sopenharmony_ci * 164262306a36Sopenharmony_ci * Return: bitfield of inbound message registers. 164362306a36Sopenharmony_ci */ 164462306a36Sopenharmony_cistatic u64 idt_ntb_msg_inbits(struct ntb_dev *ntb) 164562306a36Sopenharmony_ci{ 164662306a36Sopenharmony_ci return (u64)IDT_INMSG_MASK; 164762306a36Sopenharmony_ci} 164862306a36Sopenharmony_ci 164962306a36Sopenharmony_ci/* 165062306a36Sopenharmony_ci * idt_ntb_msg_outbits() - get a bitfield of outbound message registers status 165162306a36Sopenharmony_ci * (NTB API callback) 165262306a36Sopenharmony_ci * @ntb: NTB device context. 165362306a36Sopenharmony_ci * 165462306a36Sopenharmony_ci * NT message status register is shared between inbound and outbound message 165562306a36Sopenharmony_ci * registers status 165662306a36Sopenharmony_ci * 165762306a36Sopenharmony_ci * Return: bitfield of outbound message registers. 165862306a36Sopenharmony_ci */ 165962306a36Sopenharmony_cistatic u64 idt_ntb_msg_outbits(struct ntb_dev *ntb) 166062306a36Sopenharmony_ci{ 166162306a36Sopenharmony_ci return (u64)IDT_OUTMSG_MASK; 166262306a36Sopenharmony_ci} 166362306a36Sopenharmony_ci 166462306a36Sopenharmony_ci/* 166562306a36Sopenharmony_ci * idt_ntb_msg_read_sts() - read the message registers status (NTB API callback) 166662306a36Sopenharmony_ci * @ntb: NTB device context. 166762306a36Sopenharmony_ci * 166862306a36Sopenharmony_ci * IDT PCIe-switches expose message status registers to notify drivers of 166962306a36Sopenharmony_ci * incoming data and failures in case if peer message register isn't freed. 167062306a36Sopenharmony_ci * 167162306a36Sopenharmony_ci * Return: status bits of message registers 167262306a36Sopenharmony_ci */ 167362306a36Sopenharmony_cistatic u64 idt_ntb_msg_read_sts(struct ntb_dev *ntb) 167462306a36Sopenharmony_ci{ 167562306a36Sopenharmony_ci struct idt_ntb_dev *ndev = to_ndev_ntb(ntb); 167662306a36Sopenharmony_ci 167762306a36Sopenharmony_ci return idt_nt_read(ndev, IDT_NT_MSGSTS); 167862306a36Sopenharmony_ci} 167962306a36Sopenharmony_ci 168062306a36Sopenharmony_ci/* 168162306a36Sopenharmony_ci * idt_ntb_msg_clear_sts() - clear status bits of message registers 168262306a36Sopenharmony_ci * (NTB API callback) 168362306a36Sopenharmony_ci * @ntb: NTB device context. 168462306a36Sopenharmony_ci * @sts_bits: Status bits to clear. 168562306a36Sopenharmony_ci * 168662306a36Sopenharmony_ci * Clear bits in the status register by writing ones. 168762306a36Sopenharmony_ci * 168862306a36Sopenharmony_ci * NOTE! Invalid bits are always considered cleared so it's not an error 168962306a36Sopenharmony_ci * to clear them over. 169062306a36Sopenharmony_ci * 169162306a36Sopenharmony_ci * Return: always zero as success. 169262306a36Sopenharmony_ci */ 169362306a36Sopenharmony_cistatic int idt_ntb_msg_clear_sts(struct ntb_dev *ntb, u64 sts_bits) 169462306a36Sopenharmony_ci{ 169562306a36Sopenharmony_ci struct idt_ntb_dev *ndev = to_ndev_ntb(ntb); 169662306a36Sopenharmony_ci 169762306a36Sopenharmony_ci idt_nt_write(ndev, IDT_NT_MSGSTS, sts_bits); 169862306a36Sopenharmony_ci 169962306a36Sopenharmony_ci return 0; 170062306a36Sopenharmony_ci} 170162306a36Sopenharmony_ci 170262306a36Sopenharmony_ci/* 170362306a36Sopenharmony_ci * idt_ntb_msg_set_mask() - set mask of message register status bits 170462306a36Sopenharmony_ci * (NTB API callback) 170562306a36Sopenharmony_ci * @ntb: NTB device context. 170662306a36Sopenharmony_ci * @mask_bits: Mask bits. 170762306a36Sopenharmony_ci * 170862306a36Sopenharmony_ci * Mask the message status bits from raising an IRQ. 170962306a36Sopenharmony_ci * 171062306a36Sopenharmony_ci * Return: zero on success, negative error if invalid argument passed. 171162306a36Sopenharmony_ci */ 171262306a36Sopenharmony_cistatic int idt_ntb_msg_set_mask(struct ntb_dev *ntb, u64 mask_bits) 171362306a36Sopenharmony_ci{ 171462306a36Sopenharmony_ci struct idt_ntb_dev *ndev = to_ndev_ntb(ntb); 171562306a36Sopenharmony_ci 171662306a36Sopenharmony_ci return idt_reg_set_bits(ndev, IDT_NT_MSGSTSMSK, &ndev->msg_mask_lock, 171762306a36Sopenharmony_ci IDT_MSG_MASK, mask_bits); 171862306a36Sopenharmony_ci} 171962306a36Sopenharmony_ci 172062306a36Sopenharmony_ci/* 172162306a36Sopenharmony_ci * idt_ntb_msg_clear_mask() - clear message registers mask 172262306a36Sopenharmony_ci * (NTB API callback) 172362306a36Sopenharmony_ci * @ntb: NTB device context. 172462306a36Sopenharmony_ci * @mask_bits: Mask bits. 172562306a36Sopenharmony_ci * 172662306a36Sopenharmony_ci * Clear mask of message status bits IRQs. 172762306a36Sopenharmony_ci * 172862306a36Sopenharmony_ci * Return: always zero as success. 172962306a36Sopenharmony_ci */ 173062306a36Sopenharmony_cistatic int idt_ntb_msg_clear_mask(struct ntb_dev *ntb, u64 mask_bits) 173162306a36Sopenharmony_ci{ 173262306a36Sopenharmony_ci struct idt_ntb_dev *ndev = to_ndev_ntb(ntb); 173362306a36Sopenharmony_ci 173462306a36Sopenharmony_ci idt_reg_clear_bits(ndev, IDT_NT_MSGSTSMSK, &ndev->msg_mask_lock, 173562306a36Sopenharmony_ci mask_bits); 173662306a36Sopenharmony_ci 173762306a36Sopenharmony_ci return 0; 173862306a36Sopenharmony_ci} 173962306a36Sopenharmony_ci 174062306a36Sopenharmony_ci/* 174162306a36Sopenharmony_ci * idt_ntb_msg_read() - read message register with specified index 174262306a36Sopenharmony_ci * (NTB API callback) 174362306a36Sopenharmony_ci * @ntb: NTB device context. 174462306a36Sopenharmony_ci * @pidx: OUT - Port index of peer device a message retrieved from 174562306a36Sopenharmony_ci * @midx: Message register index 174662306a36Sopenharmony_ci * 174762306a36Sopenharmony_ci * Read data from the specified message register and source register. 174862306a36Sopenharmony_ci * 174962306a36Sopenharmony_ci * Return: inbound message register value. 175062306a36Sopenharmony_ci */ 175162306a36Sopenharmony_cistatic u32 idt_ntb_msg_read(struct ntb_dev *ntb, int *pidx, int midx) 175262306a36Sopenharmony_ci{ 175362306a36Sopenharmony_ci struct idt_ntb_dev *ndev = to_ndev_ntb(ntb); 175462306a36Sopenharmony_ci 175562306a36Sopenharmony_ci if (midx < 0 || IDT_MSG_CNT <= midx) 175662306a36Sopenharmony_ci return ~(u32)0; 175762306a36Sopenharmony_ci 175862306a36Sopenharmony_ci /* Retrieve source port index of the message */ 175962306a36Sopenharmony_ci if (pidx != NULL) { 176062306a36Sopenharmony_ci u32 srcpart; 176162306a36Sopenharmony_ci 176262306a36Sopenharmony_ci srcpart = idt_nt_read(ndev, ntdata_tbl.msgs[midx].src); 176362306a36Sopenharmony_ci *pidx = ndev->part_idx_map[srcpart]; 176462306a36Sopenharmony_ci 176562306a36Sopenharmony_ci /* Sanity check partition index (for initial case) */ 176662306a36Sopenharmony_ci if (*pidx == -EINVAL) 176762306a36Sopenharmony_ci *pidx = 0; 176862306a36Sopenharmony_ci } 176962306a36Sopenharmony_ci 177062306a36Sopenharmony_ci /* Retrieve data of the corresponding message register */ 177162306a36Sopenharmony_ci return idt_nt_read(ndev, ntdata_tbl.msgs[midx].in); 177262306a36Sopenharmony_ci} 177362306a36Sopenharmony_ci 177462306a36Sopenharmony_ci/* 177562306a36Sopenharmony_ci * idt_ntb_peer_msg_write() - write data to the specified message register 177662306a36Sopenharmony_ci * (NTB API callback) 177762306a36Sopenharmony_ci * @ntb: NTB device context. 177862306a36Sopenharmony_ci * @pidx: Port index of peer device a message being sent to 177962306a36Sopenharmony_ci * @midx: Message register index 178062306a36Sopenharmony_ci * @msg: Data to send 178162306a36Sopenharmony_ci * 178262306a36Sopenharmony_ci * Just try to send data to a peer. Message status register should be 178362306a36Sopenharmony_ci * checked by client driver. 178462306a36Sopenharmony_ci * 178562306a36Sopenharmony_ci * Return: zero on success, negative error if invalid argument passed. 178662306a36Sopenharmony_ci */ 178762306a36Sopenharmony_cistatic int idt_ntb_peer_msg_write(struct ntb_dev *ntb, int pidx, int midx, 178862306a36Sopenharmony_ci u32 msg) 178962306a36Sopenharmony_ci{ 179062306a36Sopenharmony_ci struct idt_ntb_dev *ndev = to_ndev_ntb(ntb); 179162306a36Sopenharmony_ci unsigned long irqflags; 179262306a36Sopenharmony_ci u32 swpmsgctl = 0; 179362306a36Sopenharmony_ci 179462306a36Sopenharmony_ci if (midx < 0 || IDT_MSG_CNT <= midx) 179562306a36Sopenharmony_ci return -EINVAL; 179662306a36Sopenharmony_ci 179762306a36Sopenharmony_ci if (pidx < 0 || ndev->peer_cnt <= pidx) 179862306a36Sopenharmony_ci return -EINVAL; 179962306a36Sopenharmony_ci 180062306a36Sopenharmony_ci /* Collect the routing information */ 180162306a36Sopenharmony_ci swpmsgctl = SET_FIELD(SWPxMSGCTL_REG, 0, midx) | 180262306a36Sopenharmony_ci SET_FIELD(SWPxMSGCTL_PART, 0, ndev->peers[pidx].part); 180362306a36Sopenharmony_ci 180462306a36Sopenharmony_ci /* Lock the messages routing table of the specified register */ 180562306a36Sopenharmony_ci spin_lock_irqsave(&ndev->msg_locks[midx], irqflags); 180662306a36Sopenharmony_ci /* Set the route and send the data */ 180762306a36Sopenharmony_ci idt_sw_write(ndev, partdata_tbl[ndev->part].msgctl[midx], swpmsgctl); 180862306a36Sopenharmony_ci idt_nt_write(ndev, ntdata_tbl.msgs[midx].out, msg); 180962306a36Sopenharmony_ci /* Unlock the messages routing table */ 181062306a36Sopenharmony_ci spin_unlock_irqrestore(&ndev->msg_locks[midx], irqflags); 181162306a36Sopenharmony_ci 181262306a36Sopenharmony_ci /* Client driver shall check the status register */ 181362306a36Sopenharmony_ci return 0; 181462306a36Sopenharmony_ci} 181562306a36Sopenharmony_ci 181662306a36Sopenharmony_ci/*============================================================================= 181762306a36Sopenharmony_ci * 7. Temperature sensor operations 181862306a36Sopenharmony_ci * 181962306a36Sopenharmony_ci * IDT PCIe-switch has an embedded temperature sensor, which can be used to 182062306a36Sopenharmony_ci * check current chip core temperature. Since a workload environment can be 182162306a36Sopenharmony_ci * different on different platforms, an offset and ADC/filter settings can be 182262306a36Sopenharmony_ci * specified. Although the offset configuration is only exposed to the sysfs 182362306a36Sopenharmony_ci * hwmon interface at the moment. The rest of the settings can be adjusted 182462306a36Sopenharmony_ci * for instance by the BIOS/EEPROM firmware. 182562306a36Sopenharmony_ci *============================================================================= 182662306a36Sopenharmony_ci */ 182762306a36Sopenharmony_ci 182862306a36Sopenharmony_ci/* 182962306a36Sopenharmony_ci * idt_get_deg() - convert millidegree Celsius value to just degree 183062306a36Sopenharmony_ci * @mdegC: IN - millidegree Celsius value 183162306a36Sopenharmony_ci * 183262306a36Sopenharmony_ci * Return: Degree corresponding to the passed millidegree value 183362306a36Sopenharmony_ci */ 183462306a36Sopenharmony_cistatic inline s8 idt_get_deg(long mdegC) 183562306a36Sopenharmony_ci{ 183662306a36Sopenharmony_ci return mdegC / 1000; 183762306a36Sopenharmony_ci} 183862306a36Sopenharmony_ci 183962306a36Sopenharmony_ci/* 184062306a36Sopenharmony_ci * idt_get_frac() - retrieve 0/0.5 fraction of the millidegree Celsius value 184162306a36Sopenharmony_ci * @mdegC: IN - millidegree Celsius value 184262306a36Sopenharmony_ci * 184362306a36Sopenharmony_ci * Return: 0/0.5 degree fraction of the passed millidegree value 184462306a36Sopenharmony_ci */ 184562306a36Sopenharmony_cistatic inline u8 idt_get_deg_frac(long mdegC) 184662306a36Sopenharmony_ci{ 184762306a36Sopenharmony_ci return (mdegC % 1000) >= 500 ? 5 : 0; 184862306a36Sopenharmony_ci} 184962306a36Sopenharmony_ci 185062306a36Sopenharmony_ci/* 185162306a36Sopenharmony_ci * idt_get_temp_fmt() - convert millidegree Celsius value to 0:7:1 format 185262306a36Sopenharmony_ci * @mdegC: IN - millidegree Celsius value 185362306a36Sopenharmony_ci * 185462306a36Sopenharmony_ci * Return: 0:7:1 format acceptable by the IDT temperature sensor 185562306a36Sopenharmony_ci */ 185662306a36Sopenharmony_cistatic inline u8 idt_temp_get_fmt(long mdegC) 185762306a36Sopenharmony_ci{ 185862306a36Sopenharmony_ci return (idt_get_deg(mdegC) << 1) | (idt_get_deg_frac(mdegC) ? 1 : 0); 185962306a36Sopenharmony_ci} 186062306a36Sopenharmony_ci 186162306a36Sopenharmony_ci/* 186262306a36Sopenharmony_ci * idt_get_temp_sval() - convert temp sample to signed millidegree Celsius 186362306a36Sopenharmony_ci * @data: IN - shifted to LSB 8-bits temperature sample 186462306a36Sopenharmony_ci * 186562306a36Sopenharmony_ci * Return: signed millidegree Celsius 186662306a36Sopenharmony_ci */ 186762306a36Sopenharmony_cistatic inline long idt_get_temp_sval(u32 data) 186862306a36Sopenharmony_ci{ 186962306a36Sopenharmony_ci return ((s8)data / 2) * 1000 + (data & 0x1 ? 500 : 0); 187062306a36Sopenharmony_ci} 187162306a36Sopenharmony_ci 187262306a36Sopenharmony_ci/* 187362306a36Sopenharmony_ci * idt_get_temp_sval() - convert temp sample to unsigned millidegree Celsius 187462306a36Sopenharmony_ci * @data: IN - shifted to LSB 8-bits temperature sample 187562306a36Sopenharmony_ci * 187662306a36Sopenharmony_ci * Return: unsigned millidegree Celsius 187762306a36Sopenharmony_ci */ 187862306a36Sopenharmony_cistatic inline long idt_get_temp_uval(u32 data) 187962306a36Sopenharmony_ci{ 188062306a36Sopenharmony_ci return (data / 2) * 1000 + (data & 0x1 ? 500 : 0); 188162306a36Sopenharmony_ci} 188262306a36Sopenharmony_ci 188362306a36Sopenharmony_ci/* 188462306a36Sopenharmony_ci * idt_read_temp() - read temperature from chip sensor 188562306a36Sopenharmony_ci * @ntb: NTB device context. 188662306a36Sopenharmony_ci * @type: IN - type of the temperature value to read 188762306a36Sopenharmony_ci * @val: OUT - integer value of temperature in millidegree Celsius 188862306a36Sopenharmony_ci */ 188962306a36Sopenharmony_cistatic void idt_read_temp(struct idt_ntb_dev *ndev, 189062306a36Sopenharmony_ci const enum idt_temp_val type, long *val) 189162306a36Sopenharmony_ci{ 189262306a36Sopenharmony_ci u32 data; 189362306a36Sopenharmony_ci 189462306a36Sopenharmony_ci /* Alter the temperature field in accordance with the passed type */ 189562306a36Sopenharmony_ci switch (type) { 189662306a36Sopenharmony_ci case IDT_TEMP_CUR: 189762306a36Sopenharmony_ci data = GET_FIELD(TMPSTS_TEMP, 189862306a36Sopenharmony_ci idt_sw_read(ndev, IDT_SW_TMPSTS)); 189962306a36Sopenharmony_ci break; 190062306a36Sopenharmony_ci case IDT_TEMP_LOW: 190162306a36Sopenharmony_ci data = GET_FIELD(TMPSTS_LTEMP, 190262306a36Sopenharmony_ci idt_sw_read(ndev, IDT_SW_TMPSTS)); 190362306a36Sopenharmony_ci break; 190462306a36Sopenharmony_ci case IDT_TEMP_HIGH: 190562306a36Sopenharmony_ci data = GET_FIELD(TMPSTS_HTEMP, 190662306a36Sopenharmony_ci idt_sw_read(ndev, IDT_SW_TMPSTS)); 190762306a36Sopenharmony_ci break; 190862306a36Sopenharmony_ci case IDT_TEMP_OFFSET: 190962306a36Sopenharmony_ci /* This is the only field with signed 0:7:1 format */ 191062306a36Sopenharmony_ci data = GET_FIELD(TMPADJ_OFFSET, 191162306a36Sopenharmony_ci idt_sw_read(ndev, IDT_SW_TMPADJ)); 191262306a36Sopenharmony_ci *val = idt_get_temp_sval(data); 191362306a36Sopenharmony_ci return; 191462306a36Sopenharmony_ci default: 191562306a36Sopenharmony_ci data = GET_FIELD(TMPSTS_TEMP, 191662306a36Sopenharmony_ci idt_sw_read(ndev, IDT_SW_TMPSTS)); 191762306a36Sopenharmony_ci break; 191862306a36Sopenharmony_ci } 191962306a36Sopenharmony_ci 192062306a36Sopenharmony_ci /* The rest of the fields accept unsigned 0:7:1 format */ 192162306a36Sopenharmony_ci *val = idt_get_temp_uval(data); 192262306a36Sopenharmony_ci} 192362306a36Sopenharmony_ci 192462306a36Sopenharmony_ci/* 192562306a36Sopenharmony_ci * idt_write_temp() - write temperature to the chip sensor register 192662306a36Sopenharmony_ci * @ntb: NTB device context. 192762306a36Sopenharmony_ci * @type: IN - type of the temperature value to change 192862306a36Sopenharmony_ci * @val: IN - integer value of temperature in millidegree Celsius 192962306a36Sopenharmony_ci */ 193062306a36Sopenharmony_cistatic void idt_write_temp(struct idt_ntb_dev *ndev, 193162306a36Sopenharmony_ci const enum idt_temp_val type, const long val) 193262306a36Sopenharmony_ci{ 193362306a36Sopenharmony_ci unsigned int reg; 193462306a36Sopenharmony_ci u32 data; 193562306a36Sopenharmony_ci u8 fmt; 193662306a36Sopenharmony_ci 193762306a36Sopenharmony_ci /* Retrieve the properly formatted temperature value */ 193862306a36Sopenharmony_ci fmt = idt_temp_get_fmt(val); 193962306a36Sopenharmony_ci 194062306a36Sopenharmony_ci mutex_lock(&ndev->hwmon_mtx); 194162306a36Sopenharmony_ci switch (type) { 194262306a36Sopenharmony_ci case IDT_TEMP_LOW: 194362306a36Sopenharmony_ci reg = IDT_SW_TMPALARM; 194462306a36Sopenharmony_ci data = SET_FIELD(TMPALARM_LTEMP, idt_sw_read(ndev, reg), fmt) & 194562306a36Sopenharmony_ci ~IDT_TMPALARM_IRQ_MASK; 194662306a36Sopenharmony_ci break; 194762306a36Sopenharmony_ci case IDT_TEMP_HIGH: 194862306a36Sopenharmony_ci reg = IDT_SW_TMPALARM; 194962306a36Sopenharmony_ci data = SET_FIELD(TMPALARM_HTEMP, idt_sw_read(ndev, reg), fmt) & 195062306a36Sopenharmony_ci ~IDT_TMPALARM_IRQ_MASK; 195162306a36Sopenharmony_ci break; 195262306a36Sopenharmony_ci case IDT_TEMP_OFFSET: 195362306a36Sopenharmony_ci reg = IDT_SW_TMPADJ; 195462306a36Sopenharmony_ci data = SET_FIELD(TMPADJ_OFFSET, idt_sw_read(ndev, reg), fmt); 195562306a36Sopenharmony_ci break; 195662306a36Sopenharmony_ci default: 195762306a36Sopenharmony_ci goto inval_spin_unlock; 195862306a36Sopenharmony_ci } 195962306a36Sopenharmony_ci 196062306a36Sopenharmony_ci idt_sw_write(ndev, reg, data); 196162306a36Sopenharmony_ci 196262306a36Sopenharmony_ciinval_spin_unlock: 196362306a36Sopenharmony_ci mutex_unlock(&ndev->hwmon_mtx); 196462306a36Sopenharmony_ci} 196562306a36Sopenharmony_ci 196662306a36Sopenharmony_ci/* 196762306a36Sopenharmony_ci * idt_sysfs_show_temp() - printout corresponding temperature value 196862306a36Sopenharmony_ci * @dev: Pointer to the NTB device structure 196962306a36Sopenharmony_ci * @da: Sensor device attribute structure 197062306a36Sopenharmony_ci * @buf: Buffer to print temperature out 197162306a36Sopenharmony_ci * 197262306a36Sopenharmony_ci * Return: Number of written symbols or negative error 197362306a36Sopenharmony_ci */ 197462306a36Sopenharmony_cistatic ssize_t idt_sysfs_show_temp(struct device *dev, 197562306a36Sopenharmony_ci struct device_attribute *da, char *buf) 197662306a36Sopenharmony_ci{ 197762306a36Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(da); 197862306a36Sopenharmony_ci struct idt_ntb_dev *ndev = dev_get_drvdata(dev); 197962306a36Sopenharmony_ci enum idt_temp_val type = attr->index; 198062306a36Sopenharmony_ci long mdeg; 198162306a36Sopenharmony_ci 198262306a36Sopenharmony_ci idt_read_temp(ndev, type, &mdeg); 198362306a36Sopenharmony_ci return sprintf(buf, "%ld\n", mdeg); 198462306a36Sopenharmony_ci} 198562306a36Sopenharmony_ci 198662306a36Sopenharmony_ci/* 198762306a36Sopenharmony_ci * idt_sysfs_set_temp() - set corresponding temperature value 198862306a36Sopenharmony_ci * @dev: Pointer to the NTB device structure 198962306a36Sopenharmony_ci * @da: Sensor device attribute structure 199062306a36Sopenharmony_ci * @buf: Buffer to print temperature out 199162306a36Sopenharmony_ci * @count: Size of the passed buffer 199262306a36Sopenharmony_ci * 199362306a36Sopenharmony_ci * Return: Number of written symbols or negative error 199462306a36Sopenharmony_ci */ 199562306a36Sopenharmony_cistatic ssize_t idt_sysfs_set_temp(struct device *dev, 199662306a36Sopenharmony_ci struct device_attribute *da, const char *buf, 199762306a36Sopenharmony_ci size_t count) 199862306a36Sopenharmony_ci{ 199962306a36Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(da); 200062306a36Sopenharmony_ci struct idt_ntb_dev *ndev = dev_get_drvdata(dev); 200162306a36Sopenharmony_ci enum idt_temp_val type = attr->index; 200262306a36Sopenharmony_ci long mdeg; 200362306a36Sopenharmony_ci int ret; 200462306a36Sopenharmony_ci 200562306a36Sopenharmony_ci ret = kstrtol(buf, 10, &mdeg); 200662306a36Sopenharmony_ci if (ret) 200762306a36Sopenharmony_ci return ret; 200862306a36Sopenharmony_ci 200962306a36Sopenharmony_ci /* Clamp the passed value in accordance with the type */ 201062306a36Sopenharmony_ci if (type == IDT_TEMP_OFFSET) 201162306a36Sopenharmony_ci mdeg = clamp_val(mdeg, IDT_TEMP_MIN_OFFSET, 201262306a36Sopenharmony_ci IDT_TEMP_MAX_OFFSET); 201362306a36Sopenharmony_ci else 201462306a36Sopenharmony_ci mdeg = clamp_val(mdeg, IDT_TEMP_MIN_MDEG, IDT_TEMP_MAX_MDEG); 201562306a36Sopenharmony_ci 201662306a36Sopenharmony_ci idt_write_temp(ndev, type, mdeg); 201762306a36Sopenharmony_ci 201862306a36Sopenharmony_ci return count; 201962306a36Sopenharmony_ci} 202062306a36Sopenharmony_ci 202162306a36Sopenharmony_ci/* 202262306a36Sopenharmony_ci * idt_sysfs_reset_hist() - reset temperature history 202362306a36Sopenharmony_ci * @dev: Pointer to the NTB device structure 202462306a36Sopenharmony_ci * @da: Sensor device attribute structure 202562306a36Sopenharmony_ci * @buf: Buffer to print temperature out 202662306a36Sopenharmony_ci * @count: Size of the passed buffer 202762306a36Sopenharmony_ci * 202862306a36Sopenharmony_ci * Return: Number of written symbols or negative error 202962306a36Sopenharmony_ci */ 203062306a36Sopenharmony_cistatic ssize_t idt_sysfs_reset_hist(struct device *dev, 203162306a36Sopenharmony_ci struct device_attribute *da, 203262306a36Sopenharmony_ci const char *buf, size_t count) 203362306a36Sopenharmony_ci{ 203462306a36Sopenharmony_ci struct idt_ntb_dev *ndev = dev_get_drvdata(dev); 203562306a36Sopenharmony_ci 203662306a36Sopenharmony_ci /* Just set the maximal value to the lowest temperature field and 203762306a36Sopenharmony_ci * minimal value to the highest temperature field 203862306a36Sopenharmony_ci */ 203962306a36Sopenharmony_ci idt_write_temp(ndev, IDT_TEMP_LOW, IDT_TEMP_MAX_MDEG); 204062306a36Sopenharmony_ci idt_write_temp(ndev, IDT_TEMP_HIGH, IDT_TEMP_MIN_MDEG); 204162306a36Sopenharmony_ci 204262306a36Sopenharmony_ci return count; 204362306a36Sopenharmony_ci} 204462306a36Sopenharmony_ci 204562306a36Sopenharmony_ci/* 204662306a36Sopenharmony_ci * Hwmon IDT sysfs attributes 204762306a36Sopenharmony_ci */ 204862306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR(temp1_input, 0444, idt_sysfs_show_temp, NULL, 204962306a36Sopenharmony_ci IDT_TEMP_CUR); 205062306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR(temp1_lowest, 0444, idt_sysfs_show_temp, NULL, 205162306a36Sopenharmony_ci IDT_TEMP_LOW); 205262306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR(temp1_highest, 0444, idt_sysfs_show_temp, NULL, 205362306a36Sopenharmony_ci IDT_TEMP_HIGH); 205462306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR(temp1_offset, 0644, idt_sysfs_show_temp, 205562306a36Sopenharmony_ci idt_sysfs_set_temp, IDT_TEMP_OFFSET); 205662306a36Sopenharmony_cistatic DEVICE_ATTR(temp1_reset_history, 0200, NULL, idt_sysfs_reset_hist); 205762306a36Sopenharmony_ci 205862306a36Sopenharmony_ci/* 205962306a36Sopenharmony_ci * Hwmon IDT sysfs attributes group 206062306a36Sopenharmony_ci */ 206162306a36Sopenharmony_cistatic struct attribute *idt_temp_attrs[] = { 206262306a36Sopenharmony_ci &sensor_dev_attr_temp1_input.dev_attr.attr, 206362306a36Sopenharmony_ci &sensor_dev_attr_temp1_lowest.dev_attr.attr, 206462306a36Sopenharmony_ci &sensor_dev_attr_temp1_highest.dev_attr.attr, 206562306a36Sopenharmony_ci &sensor_dev_attr_temp1_offset.dev_attr.attr, 206662306a36Sopenharmony_ci &dev_attr_temp1_reset_history.attr, 206762306a36Sopenharmony_ci NULL 206862306a36Sopenharmony_ci}; 206962306a36Sopenharmony_ciATTRIBUTE_GROUPS(idt_temp); 207062306a36Sopenharmony_ci 207162306a36Sopenharmony_ci/* 207262306a36Sopenharmony_ci * idt_init_temp() - initialize temperature sensor interface 207362306a36Sopenharmony_ci * @ndev: IDT NTB hardware driver descriptor 207462306a36Sopenharmony_ci * 207562306a36Sopenharmony_ci * Simple sensor initializarion method is responsible for device switching 207662306a36Sopenharmony_ci * on and resource management based hwmon interface registration. Note, that 207762306a36Sopenharmony_ci * since the device is shared we won't disable it on remove, but leave it 207862306a36Sopenharmony_ci * working until the system is powered off. 207962306a36Sopenharmony_ci */ 208062306a36Sopenharmony_cistatic void idt_init_temp(struct idt_ntb_dev *ndev) 208162306a36Sopenharmony_ci{ 208262306a36Sopenharmony_ci struct device *hwmon; 208362306a36Sopenharmony_ci 208462306a36Sopenharmony_ci /* Enable sensor if it hasn't been already */ 208562306a36Sopenharmony_ci idt_sw_write(ndev, IDT_SW_TMPCTL, 0x0); 208662306a36Sopenharmony_ci 208762306a36Sopenharmony_ci /* Initialize hwmon interface fields */ 208862306a36Sopenharmony_ci mutex_init(&ndev->hwmon_mtx); 208962306a36Sopenharmony_ci 209062306a36Sopenharmony_ci hwmon = devm_hwmon_device_register_with_groups(&ndev->ntb.pdev->dev, 209162306a36Sopenharmony_ci ndev->swcfg->name, ndev, idt_temp_groups); 209262306a36Sopenharmony_ci if (IS_ERR(hwmon)) { 209362306a36Sopenharmony_ci dev_err(&ndev->ntb.pdev->dev, "Couldn't create hwmon device"); 209462306a36Sopenharmony_ci return; 209562306a36Sopenharmony_ci } 209662306a36Sopenharmony_ci 209762306a36Sopenharmony_ci dev_dbg(&ndev->ntb.pdev->dev, "Temperature HWmon interface registered"); 209862306a36Sopenharmony_ci} 209962306a36Sopenharmony_ci 210062306a36Sopenharmony_ci/*============================================================================= 210162306a36Sopenharmony_ci * 8. ISRs related operations 210262306a36Sopenharmony_ci * 210362306a36Sopenharmony_ci * IDT PCIe-switch has strangely developed IRQ system. There is just one 210462306a36Sopenharmony_ci * interrupt vector for doorbell and message registers. So the hardware driver 210562306a36Sopenharmony_ci * can't determine actual source of IRQ if, for example, message event happened 210662306a36Sopenharmony_ci * while any of unmasked doorbell is still set. The similar situation may be if 210762306a36Sopenharmony_ci * switch or temperature sensor events pop up. The difference is that SEVENT 210862306a36Sopenharmony_ci * and TMPSENSOR bits of NT interrupt status register can be cleaned by 210962306a36Sopenharmony_ci * IRQ handler so a next interrupt request won't have false handling of 211062306a36Sopenharmony_ci * corresponding events. 211162306a36Sopenharmony_ci * The hardware driver has only bottom-half handler of the IRQ, since if any 211262306a36Sopenharmony_ci * of events happened the device won't raise it again before the last one is 211362306a36Sopenharmony_ci * handled by clearing of corresponding NTINTSTS bit. 211462306a36Sopenharmony_ci *============================================================================= 211562306a36Sopenharmony_ci */ 211662306a36Sopenharmony_ci 211762306a36Sopenharmony_cistatic irqreturn_t idt_thread_isr(int irq, void *devid); 211862306a36Sopenharmony_ci 211962306a36Sopenharmony_ci/* 212062306a36Sopenharmony_ci * idt_init_isr() - initialize PCIe interrupt handler 212162306a36Sopenharmony_ci * @ndev: IDT NTB hardware driver descriptor 212262306a36Sopenharmony_ci * 212362306a36Sopenharmony_ci * Return: zero on success, otherwise a negative error number. 212462306a36Sopenharmony_ci */ 212562306a36Sopenharmony_cistatic int idt_init_isr(struct idt_ntb_dev *ndev) 212662306a36Sopenharmony_ci{ 212762306a36Sopenharmony_ci struct pci_dev *pdev = ndev->ntb.pdev; 212862306a36Sopenharmony_ci u32 ntint_mask; 212962306a36Sopenharmony_ci int ret; 213062306a36Sopenharmony_ci 213162306a36Sopenharmony_ci /* Allocate just one interrupt vector for the ISR */ 213262306a36Sopenharmony_ci ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI | PCI_IRQ_LEGACY); 213362306a36Sopenharmony_ci if (ret != 1) { 213462306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to allocate IRQ vector"); 213562306a36Sopenharmony_ci return ret; 213662306a36Sopenharmony_ci } 213762306a36Sopenharmony_ci 213862306a36Sopenharmony_ci /* Retrieve the IRQ vector */ 213962306a36Sopenharmony_ci ret = pci_irq_vector(pdev, 0); 214062306a36Sopenharmony_ci if (ret < 0) { 214162306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to get IRQ vector"); 214262306a36Sopenharmony_ci goto err_free_vectors; 214362306a36Sopenharmony_ci } 214462306a36Sopenharmony_ci 214562306a36Sopenharmony_ci /* Set the IRQ handler */ 214662306a36Sopenharmony_ci ret = devm_request_threaded_irq(&pdev->dev, ret, NULL, idt_thread_isr, 214762306a36Sopenharmony_ci IRQF_ONESHOT, NTB_IRQNAME, ndev); 214862306a36Sopenharmony_ci if (ret != 0) { 214962306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to set MSI IRQ handler, %d", ret); 215062306a36Sopenharmony_ci goto err_free_vectors; 215162306a36Sopenharmony_ci } 215262306a36Sopenharmony_ci 215362306a36Sopenharmony_ci /* Unmask Message/Doorbell/SE interrupts */ 215462306a36Sopenharmony_ci ntint_mask = idt_nt_read(ndev, IDT_NT_NTINTMSK) & ~IDT_NTINTMSK_ALL; 215562306a36Sopenharmony_ci idt_nt_write(ndev, IDT_NT_NTINTMSK, ntint_mask); 215662306a36Sopenharmony_ci 215762306a36Sopenharmony_ci /* From now on the interrupts are enabled */ 215862306a36Sopenharmony_ci dev_dbg(&pdev->dev, "NTB interrupts initialized"); 215962306a36Sopenharmony_ci 216062306a36Sopenharmony_ci return 0; 216162306a36Sopenharmony_ci 216262306a36Sopenharmony_cierr_free_vectors: 216362306a36Sopenharmony_ci pci_free_irq_vectors(pdev); 216462306a36Sopenharmony_ci 216562306a36Sopenharmony_ci return ret; 216662306a36Sopenharmony_ci} 216762306a36Sopenharmony_ci 216862306a36Sopenharmony_ci/* 216962306a36Sopenharmony_ci * idt_deinit_ist() - deinitialize PCIe interrupt handler 217062306a36Sopenharmony_ci * @ndev: IDT NTB hardware driver descriptor 217162306a36Sopenharmony_ci * 217262306a36Sopenharmony_ci * Disable corresponding interrupts and free allocated IRQ vectors. 217362306a36Sopenharmony_ci */ 217462306a36Sopenharmony_cistatic void idt_deinit_isr(struct idt_ntb_dev *ndev) 217562306a36Sopenharmony_ci{ 217662306a36Sopenharmony_ci struct pci_dev *pdev = ndev->ntb.pdev; 217762306a36Sopenharmony_ci u32 ntint_mask; 217862306a36Sopenharmony_ci 217962306a36Sopenharmony_ci /* Mask interrupts back */ 218062306a36Sopenharmony_ci ntint_mask = idt_nt_read(ndev, IDT_NT_NTINTMSK) | IDT_NTINTMSK_ALL; 218162306a36Sopenharmony_ci idt_nt_write(ndev, IDT_NT_NTINTMSK, ntint_mask); 218262306a36Sopenharmony_ci 218362306a36Sopenharmony_ci /* Manually free IRQ otherwise PCI free irq vectors will fail */ 218462306a36Sopenharmony_ci devm_free_irq(&pdev->dev, pci_irq_vector(pdev, 0), ndev); 218562306a36Sopenharmony_ci 218662306a36Sopenharmony_ci /* Free allocated IRQ vectors */ 218762306a36Sopenharmony_ci pci_free_irq_vectors(pdev); 218862306a36Sopenharmony_ci 218962306a36Sopenharmony_ci dev_dbg(&pdev->dev, "NTB interrupts deinitialized"); 219062306a36Sopenharmony_ci} 219162306a36Sopenharmony_ci 219262306a36Sopenharmony_ci/* 219362306a36Sopenharmony_ci * idt_thread_isr() - NT function interrupts handler 219462306a36Sopenharmony_ci * @irq: IRQ number 219562306a36Sopenharmony_ci * @devid: Custom buffer 219662306a36Sopenharmony_ci * 219762306a36Sopenharmony_ci * It reads current NT interrupts state register and handles all the event 219862306a36Sopenharmony_ci * it declares. 219962306a36Sopenharmony_ci * The method is bottom-half routine of actual default PCIe IRQ handler. 220062306a36Sopenharmony_ci */ 220162306a36Sopenharmony_cistatic irqreturn_t idt_thread_isr(int irq, void *devid) 220262306a36Sopenharmony_ci{ 220362306a36Sopenharmony_ci struct idt_ntb_dev *ndev = devid; 220462306a36Sopenharmony_ci bool handled = false; 220562306a36Sopenharmony_ci u32 ntint_sts; 220662306a36Sopenharmony_ci 220762306a36Sopenharmony_ci /* Read the NT interrupts status register */ 220862306a36Sopenharmony_ci ntint_sts = idt_nt_read(ndev, IDT_NT_NTINTSTS); 220962306a36Sopenharmony_ci 221062306a36Sopenharmony_ci /* Handle messaging interrupts */ 221162306a36Sopenharmony_ci if (ntint_sts & IDT_NTINTSTS_MSG) { 221262306a36Sopenharmony_ci idt_msg_isr(ndev, ntint_sts); 221362306a36Sopenharmony_ci handled = true; 221462306a36Sopenharmony_ci } 221562306a36Sopenharmony_ci 221662306a36Sopenharmony_ci /* Handle doorbell interrupts */ 221762306a36Sopenharmony_ci if (ntint_sts & IDT_NTINTSTS_DBELL) { 221862306a36Sopenharmony_ci idt_db_isr(ndev, ntint_sts); 221962306a36Sopenharmony_ci handled = true; 222062306a36Sopenharmony_ci } 222162306a36Sopenharmony_ci 222262306a36Sopenharmony_ci /* Handle switch event interrupts */ 222362306a36Sopenharmony_ci if (ntint_sts & IDT_NTINTSTS_SEVENT) { 222462306a36Sopenharmony_ci idt_se_isr(ndev, ntint_sts); 222562306a36Sopenharmony_ci handled = true; 222662306a36Sopenharmony_ci } 222762306a36Sopenharmony_ci 222862306a36Sopenharmony_ci dev_dbg(&ndev->ntb.pdev->dev, "IDT IRQs 0x%08x handled", ntint_sts); 222962306a36Sopenharmony_ci 223062306a36Sopenharmony_ci return handled ? IRQ_HANDLED : IRQ_NONE; 223162306a36Sopenharmony_ci} 223262306a36Sopenharmony_ci 223362306a36Sopenharmony_ci/*=========================================================================== 223462306a36Sopenharmony_ci * 9. NTB hardware driver initialization 223562306a36Sopenharmony_ci *=========================================================================== 223662306a36Sopenharmony_ci */ 223762306a36Sopenharmony_ci 223862306a36Sopenharmony_ci/* 223962306a36Sopenharmony_ci * NTB API operations 224062306a36Sopenharmony_ci */ 224162306a36Sopenharmony_cistatic const struct ntb_dev_ops idt_ntb_ops = { 224262306a36Sopenharmony_ci .port_number = idt_ntb_port_number, 224362306a36Sopenharmony_ci .peer_port_count = idt_ntb_peer_port_count, 224462306a36Sopenharmony_ci .peer_port_number = idt_ntb_peer_port_number, 224562306a36Sopenharmony_ci .peer_port_idx = idt_ntb_peer_port_idx, 224662306a36Sopenharmony_ci .link_is_up = idt_ntb_link_is_up, 224762306a36Sopenharmony_ci .link_enable = idt_ntb_link_enable, 224862306a36Sopenharmony_ci .link_disable = idt_ntb_link_disable, 224962306a36Sopenharmony_ci .mw_count = idt_ntb_mw_count, 225062306a36Sopenharmony_ci .mw_get_align = idt_ntb_mw_get_align, 225162306a36Sopenharmony_ci .peer_mw_count = idt_ntb_peer_mw_count, 225262306a36Sopenharmony_ci .peer_mw_get_addr = idt_ntb_peer_mw_get_addr, 225362306a36Sopenharmony_ci .peer_mw_set_trans = idt_ntb_peer_mw_set_trans, 225462306a36Sopenharmony_ci .peer_mw_clear_trans = idt_ntb_peer_mw_clear_trans, 225562306a36Sopenharmony_ci .db_valid_mask = idt_ntb_db_valid_mask, 225662306a36Sopenharmony_ci .db_read = idt_ntb_db_read, 225762306a36Sopenharmony_ci .db_clear = idt_ntb_db_clear, 225862306a36Sopenharmony_ci .db_read_mask = idt_ntb_db_read_mask, 225962306a36Sopenharmony_ci .db_set_mask = idt_ntb_db_set_mask, 226062306a36Sopenharmony_ci .db_clear_mask = idt_ntb_db_clear_mask, 226162306a36Sopenharmony_ci .peer_db_set = idt_ntb_peer_db_set, 226262306a36Sopenharmony_ci .msg_count = idt_ntb_msg_count, 226362306a36Sopenharmony_ci .msg_inbits = idt_ntb_msg_inbits, 226462306a36Sopenharmony_ci .msg_outbits = idt_ntb_msg_outbits, 226562306a36Sopenharmony_ci .msg_read_sts = idt_ntb_msg_read_sts, 226662306a36Sopenharmony_ci .msg_clear_sts = idt_ntb_msg_clear_sts, 226762306a36Sopenharmony_ci .msg_set_mask = idt_ntb_msg_set_mask, 226862306a36Sopenharmony_ci .msg_clear_mask = idt_ntb_msg_clear_mask, 226962306a36Sopenharmony_ci .msg_read = idt_ntb_msg_read, 227062306a36Sopenharmony_ci .peer_msg_write = idt_ntb_peer_msg_write 227162306a36Sopenharmony_ci}; 227262306a36Sopenharmony_ci 227362306a36Sopenharmony_ci/* 227462306a36Sopenharmony_ci * idt_register_device() - register IDT NTB device 227562306a36Sopenharmony_ci * @ndev: IDT NTB hardware driver descriptor 227662306a36Sopenharmony_ci * 227762306a36Sopenharmony_ci * Return: zero on success, otherwise a negative error number. 227862306a36Sopenharmony_ci */ 227962306a36Sopenharmony_cistatic int idt_register_device(struct idt_ntb_dev *ndev) 228062306a36Sopenharmony_ci{ 228162306a36Sopenharmony_ci int ret; 228262306a36Sopenharmony_ci 228362306a36Sopenharmony_ci /* Initialize the rest of NTB device structure and register it */ 228462306a36Sopenharmony_ci ndev->ntb.ops = &idt_ntb_ops; 228562306a36Sopenharmony_ci ndev->ntb.topo = NTB_TOPO_SWITCH; 228662306a36Sopenharmony_ci 228762306a36Sopenharmony_ci ret = ntb_register_device(&ndev->ntb); 228862306a36Sopenharmony_ci if (ret != 0) { 228962306a36Sopenharmony_ci dev_err(&ndev->ntb.pdev->dev, "Failed to register NTB device"); 229062306a36Sopenharmony_ci return ret; 229162306a36Sopenharmony_ci } 229262306a36Sopenharmony_ci 229362306a36Sopenharmony_ci dev_dbg(&ndev->ntb.pdev->dev, "NTB device successfully registered"); 229462306a36Sopenharmony_ci 229562306a36Sopenharmony_ci return 0; 229662306a36Sopenharmony_ci} 229762306a36Sopenharmony_ci 229862306a36Sopenharmony_ci/* 229962306a36Sopenharmony_ci * idt_unregister_device() - unregister IDT NTB device 230062306a36Sopenharmony_ci * @ndev: IDT NTB hardware driver descriptor 230162306a36Sopenharmony_ci */ 230262306a36Sopenharmony_cistatic void idt_unregister_device(struct idt_ntb_dev *ndev) 230362306a36Sopenharmony_ci{ 230462306a36Sopenharmony_ci /* Just unregister the NTB device */ 230562306a36Sopenharmony_ci ntb_unregister_device(&ndev->ntb); 230662306a36Sopenharmony_ci 230762306a36Sopenharmony_ci dev_dbg(&ndev->ntb.pdev->dev, "NTB device unregistered"); 230862306a36Sopenharmony_ci} 230962306a36Sopenharmony_ci 231062306a36Sopenharmony_ci/*============================================================================= 231162306a36Sopenharmony_ci * 10. DebugFS node initialization 231262306a36Sopenharmony_ci *============================================================================= 231362306a36Sopenharmony_ci */ 231462306a36Sopenharmony_ci 231562306a36Sopenharmony_cistatic ssize_t idt_dbgfs_info_read(struct file *filp, char __user *ubuf, 231662306a36Sopenharmony_ci size_t count, loff_t *offp); 231762306a36Sopenharmony_ci 231862306a36Sopenharmony_ci/* 231962306a36Sopenharmony_ci * Driver DebugFS info file operations 232062306a36Sopenharmony_ci */ 232162306a36Sopenharmony_cistatic const struct file_operations idt_dbgfs_info_ops = { 232262306a36Sopenharmony_ci .owner = THIS_MODULE, 232362306a36Sopenharmony_ci .open = simple_open, 232462306a36Sopenharmony_ci .read = idt_dbgfs_info_read 232562306a36Sopenharmony_ci}; 232662306a36Sopenharmony_ci 232762306a36Sopenharmony_ci/* 232862306a36Sopenharmony_ci * idt_dbgfs_info_read() - DebugFS read info node callback 232962306a36Sopenharmony_ci * @file: File node descriptor. 233062306a36Sopenharmony_ci * @ubuf: User-space buffer to put data to 233162306a36Sopenharmony_ci * @count: Size of the buffer 233262306a36Sopenharmony_ci * @offp: Offset within the buffer 233362306a36Sopenharmony_ci */ 233462306a36Sopenharmony_cistatic ssize_t idt_dbgfs_info_read(struct file *filp, char __user *ubuf, 233562306a36Sopenharmony_ci size_t count, loff_t *offp) 233662306a36Sopenharmony_ci{ 233762306a36Sopenharmony_ci struct idt_ntb_dev *ndev = filp->private_data; 233862306a36Sopenharmony_ci unsigned char idx, pidx, cnt; 233962306a36Sopenharmony_ci unsigned long irqflags, mdeg; 234062306a36Sopenharmony_ci ssize_t ret = 0, off = 0; 234162306a36Sopenharmony_ci enum ntb_speed speed; 234262306a36Sopenharmony_ci enum ntb_width width; 234362306a36Sopenharmony_ci char *strbuf; 234462306a36Sopenharmony_ci size_t size; 234562306a36Sopenharmony_ci u32 data; 234662306a36Sopenharmony_ci 234762306a36Sopenharmony_ci /* Lets limit the buffer size the way the Intel/AMD drivers do */ 234862306a36Sopenharmony_ci size = min_t(size_t, count, 0x1000U); 234962306a36Sopenharmony_ci 235062306a36Sopenharmony_ci /* Allocate the memory for the buffer */ 235162306a36Sopenharmony_ci strbuf = kmalloc(size, GFP_KERNEL); 235262306a36Sopenharmony_ci if (strbuf == NULL) 235362306a36Sopenharmony_ci return -ENOMEM; 235462306a36Sopenharmony_ci 235562306a36Sopenharmony_ci /* Put the data into the string buffer */ 235662306a36Sopenharmony_ci off += scnprintf(strbuf + off, size - off, 235762306a36Sopenharmony_ci "\n\t\tIDT NTB device Information:\n\n"); 235862306a36Sopenharmony_ci 235962306a36Sopenharmony_ci /* General local device configurations */ 236062306a36Sopenharmony_ci off += scnprintf(strbuf + off, size - off, 236162306a36Sopenharmony_ci "Local Port %hhu, Partition %hhu\n", ndev->port, ndev->part); 236262306a36Sopenharmony_ci 236362306a36Sopenharmony_ci /* Peer ports information */ 236462306a36Sopenharmony_ci off += scnprintf(strbuf + off, size - off, "Peers:\n"); 236562306a36Sopenharmony_ci for (idx = 0; idx < ndev->peer_cnt; idx++) { 236662306a36Sopenharmony_ci off += scnprintf(strbuf + off, size - off, 236762306a36Sopenharmony_ci "\t%hhu. Port %hhu, Partition %hhu\n", 236862306a36Sopenharmony_ci idx, ndev->peers[idx].port, ndev->peers[idx].part); 236962306a36Sopenharmony_ci } 237062306a36Sopenharmony_ci 237162306a36Sopenharmony_ci /* Links status */ 237262306a36Sopenharmony_ci data = idt_ntb_link_is_up(&ndev->ntb, &speed, &width); 237362306a36Sopenharmony_ci off += scnprintf(strbuf + off, size - off, 237462306a36Sopenharmony_ci "NTB link status\t- 0x%08x, ", data); 237562306a36Sopenharmony_ci off += scnprintf(strbuf + off, size - off, "PCIe Gen %d x%d lanes\n", 237662306a36Sopenharmony_ci speed, width); 237762306a36Sopenharmony_ci 237862306a36Sopenharmony_ci /* Mapping table entries */ 237962306a36Sopenharmony_ci off += scnprintf(strbuf + off, size - off, "NTB Mapping Table:\n"); 238062306a36Sopenharmony_ci for (idx = 0; idx < IDT_MTBL_ENTRY_CNT; idx++) { 238162306a36Sopenharmony_ci spin_lock_irqsave(&ndev->mtbl_lock, irqflags); 238262306a36Sopenharmony_ci idt_nt_write(ndev, IDT_NT_NTMTBLADDR, idx); 238362306a36Sopenharmony_ci data = idt_nt_read(ndev, IDT_NT_NTMTBLDATA); 238462306a36Sopenharmony_ci spin_unlock_irqrestore(&ndev->mtbl_lock, irqflags); 238562306a36Sopenharmony_ci 238662306a36Sopenharmony_ci /* Print valid entries only */ 238762306a36Sopenharmony_ci if (data & IDT_NTMTBLDATA_VALID) { 238862306a36Sopenharmony_ci off += scnprintf(strbuf + off, size - off, 238962306a36Sopenharmony_ci "\t%hhu. Partition %d, Requester ID 0x%04x\n", 239062306a36Sopenharmony_ci idx, GET_FIELD(NTMTBLDATA_PART, data), 239162306a36Sopenharmony_ci GET_FIELD(NTMTBLDATA_REQID, data)); 239262306a36Sopenharmony_ci } 239362306a36Sopenharmony_ci } 239462306a36Sopenharmony_ci off += scnprintf(strbuf + off, size - off, "\n"); 239562306a36Sopenharmony_ci 239662306a36Sopenharmony_ci /* Outbound memory windows information */ 239762306a36Sopenharmony_ci off += scnprintf(strbuf + off, size - off, 239862306a36Sopenharmony_ci "Outbound Memory Windows:\n"); 239962306a36Sopenharmony_ci for (idx = 0; idx < ndev->mw_cnt; idx += cnt) { 240062306a36Sopenharmony_ci data = ndev->mws[idx].type; 240162306a36Sopenharmony_ci cnt = idt_get_mw_count(data); 240262306a36Sopenharmony_ci 240362306a36Sopenharmony_ci /* Print Memory Window information */ 240462306a36Sopenharmony_ci if (data == IDT_MW_DIR) 240562306a36Sopenharmony_ci off += scnprintf(strbuf + off, size - off, 240662306a36Sopenharmony_ci "\t%hhu.\t", idx); 240762306a36Sopenharmony_ci else 240862306a36Sopenharmony_ci off += scnprintf(strbuf + off, size - off, 240962306a36Sopenharmony_ci "\t%hhu-%d.\t", idx, idx + cnt - 1); 241062306a36Sopenharmony_ci 241162306a36Sopenharmony_ci off += scnprintf(strbuf + off, size - off, "%s BAR%hhu, ", 241262306a36Sopenharmony_ci idt_get_mw_name(data), ndev->mws[idx].bar); 241362306a36Sopenharmony_ci 241462306a36Sopenharmony_ci off += scnprintf(strbuf + off, size - off, 241562306a36Sopenharmony_ci "Address align 0x%08llx, ", ndev->mws[idx].addr_align); 241662306a36Sopenharmony_ci 241762306a36Sopenharmony_ci off += scnprintf(strbuf + off, size - off, 241862306a36Sopenharmony_ci "Size align 0x%08llx, Size max %llu\n", 241962306a36Sopenharmony_ci ndev->mws[idx].size_align, ndev->mws[idx].size_max); 242062306a36Sopenharmony_ci } 242162306a36Sopenharmony_ci 242262306a36Sopenharmony_ci /* Inbound memory windows information */ 242362306a36Sopenharmony_ci for (pidx = 0; pidx < ndev->peer_cnt; pidx++) { 242462306a36Sopenharmony_ci off += scnprintf(strbuf + off, size - off, 242562306a36Sopenharmony_ci "Inbound Memory Windows for peer %hhu (Port %hhu):\n", 242662306a36Sopenharmony_ci pidx, ndev->peers[pidx].port); 242762306a36Sopenharmony_ci 242862306a36Sopenharmony_ci /* Print Memory Windows information */ 242962306a36Sopenharmony_ci for (idx = 0; idx < ndev->peers[pidx].mw_cnt; idx += cnt) { 243062306a36Sopenharmony_ci data = ndev->peers[pidx].mws[idx].type; 243162306a36Sopenharmony_ci cnt = idt_get_mw_count(data); 243262306a36Sopenharmony_ci 243362306a36Sopenharmony_ci if (data == IDT_MW_DIR) 243462306a36Sopenharmony_ci off += scnprintf(strbuf + off, size - off, 243562306a36Sopenharmony_ci "\t%hhu.\t", idx); 243662306a36Sopenharmony_ci else 243762306a36Sopenharmony_ci off += scnprintf(strbuf + off, size - off, 243862306a36Sopenharmony_ci "\t%hhu-%d.\t", idx, idx + cnt - 1); 243962306a36Sopenharmony_ci 244062306a36Sopenharmony_ci off += scnprintf(strbuf + off, size - off, 244162306a36Sopenharmony_ci "%s BAR%hhu, ", idt_get_mw_name(data), 244262306a36Sopenharmony_ci ndev->peers[pidx].mws[idx].bar); 244362306a36Sopenharmony_ci 244462306a36Sopenharmony_ci off += scnprintf(strbuf + off, size - off, 244562306a36Sopenharmony_ci "Address align 0x%08llx, ", 244662306a36Sopenharmony_ci ndev->peers[pidx].mws[idx].addr_align); 244762306a36Sopenharmony_ci 244862306a36Sopenharmony_ci off += scnprintf(strbuf + off, size - off, 244962306a36Sopenharmony_ci "Size align 0x%08llx, Size max %llu\n", 245062306a36Sopenharmony_ci ndev->peers[pidx].mws[idx].size_align, 245162306a36Sopenharmony_ci ndev->peers[pidx].mws[idx].size_max); 245262306a36Sopenharmony_ci } 245362306a36Sopenharmony_ci } 245462306a36Sopenharmony_ci off += scnprintf(strbuf + off, size - off, "\n"); 245562306a36Sopenharmony_ci 245662306a36Sopenharmony_ci /* Doorbell information */ 245762306a36Sopenharmony_ci data = idt_sw_read(ndev, IDT_SW_GDBELLSTS); 245862306a36Sopenharmony_ci off += scnprintf(strbuf + off, size - off, 245962306a36Sopenharmony_ci "Global Doorbell state\t- 0x%08x\n", data); 246062306a36Sopenharmony_ci data = idt_ntb_db_read(&ndev->ntb); 246162306a36Sopenharmony_ci off += scnprintf(strbuf + off, size - off, 246262306a36Sopenharmony_ci "Local Doorbell state\t- 0x%08x\n", data); 246362306a36Sopenharmony_ci data = idt_nt_read(ndev, IDT_NT_INDBELLMSK); 246462306a36Sopenharmony_ci off += scnprintf(strbuf + off, size - off, 246562306a36Sopenharmony_ci "Local Doorbell mask\t- 0x%08x\n", data); 246662306a36Sopenharmony_ci off += scnprintf(strbuf + off, size - off, "\n"); 246762306a36Sopenharmony_ci 246862306a36Sopenharmony_ci /* Messaging information */ 246962306a36Sopenharmony_ci off += scnprintf(strbuf + off, size - off, 247062306a36Sopenharmony_ci "Message event valid\t- 0x%08x\n", IDT_MSG_MASK); 247162306a36Sopenharmony_ci data = idt_ntb_msg_read_sts(&ndev->ntb); 247262306a36Sopenharmony_ci off += scnprintf(strbuf + off, size - off, 247362306a36Sopenharmony_ci "Message event status\t- 0x%08x\n", data); 247462306a36Sopenharmony_ci data = idt_nt_read(ndev, IDT_NT_MSGSTSMSK); 247562306a36Sopenharmony_ci off += scnprintf(strbuf + off, size - off, 247662306a36Sopenharmony_ci "Message event mask\t- 0x%08x\n", data); 247762306a36Sopenharmony_ci off += scnprintf(strbuf + off, size - off, 247862306a36Sopenharmony_ci "Message data:\n"); 247962306a36Sopenharmony_ci for (idx = 0; idx < IDT_MSG_CNT; idx++) { 248062306a36Sopenharmony_ci int src; 248162306a36Sopenharmony_ci data = idt_ntb_msg_read(&ndev->ntb, &src, idx); 248262306a36Sopenharmony_ci off += scnprintf(strbuf + off, size - off, 248362306a36Sopenharmony_ci "\t%hhu. 0x%08x from peer %d (Port %hhu)\n", 248462306a36Sopenharmony_ci idx, data, src, ndev->peers[src].port); 248562306a36Sopenharmony_ci } 248662306a36Sopenharmony_ci off += scnprintf(strbuf + off, size - off, "\n"); 248762306a36Sopenharmony_ci 248862306a36Sopenharmony_ci /* Current temperature */ 248962306a36Sopenharmony_ci idt_read_temp(ndev, IDT_TEMP_CUR, &mdeg); 249062306a36Sopenharmony_ci off += scnprintf(strbuf + off, size - off, 249162306a36Sopenharmony_ci "Switch temperature\t\t- %hhd.%hhuC\n", 249262306a36Sopenharmony_ci idt_get_deg(mdeg), idt_get_deg_frac(mdeg)); 249362306a36Sopenharmony_ci 249462306a36Sopenharmony_ci /* Copy the buffer to the User Space */ 249562306a36Sopenharmony_ci ret = simple_read_from_buffer(ubuf, count, offp, strbuf, off); 249662306a36Sopenharmony_ci kfree(strbuf); 249762306a36Sopenharmony_ci 249862306a36Sopenharmony_ci return ret; 249962306a36Sopenharmony_ci} 250062306a36Sopenharmony_ci 250162306a36Sopenharmony_ci/* 250262306a36Sopenharmony_ci * idt_init_dbgfs() - initialize DebugFS node 250362306a36Sopenharmony_ci * @ndev: IDT NTB hardware driver descriptor 250462306a36Sopenharmony_ci * 250562306a36Sopenharmony_ci * Return: zero on success, otherwise a negative error number. 250662306a36Sopenharmony_ci */ 250762306a36Sopenharmony_cistatic int idt_init_dbgfs(struct idt_ntb_dev *ndev) 250862306a36Sopenharmony_ci{ 250962306a36Sopenharmony_ci char devname[64]; 251062306a36Sopenharmony_ci 251162306a36Sopenharmony_ci /* If the top directory is not created then do nothing */ 251262306a36Sopenharmony_ci if (IS_ERR_OR_NULL(dbgfs_topdir)) { 251362306a36Sopenharmony_ci dev_info(&ndev->ntb.pdev->dev, "Top DebugFS directory absent"); 251462306a36Sopenharmony_ci return PTR_ERR_OR_ZERO(dbgfs_topdir); 251562306a36Sopenharmony_ci } 251662306a36Sopenharmony_ci 251762306a36Sopenharmony_ci /* Create the info file node */ 251862306a36Sopenharmony_ci snprintf(devname, 64, "info:%s", pci_name(ndev->ntb.pdev)); 251962306a36Sopenharmony_ci ndev->dbgfs_info = debugfs_create_file(devname, 0400, dbgfs_topdir, 252062306a36Sopenharmony_ci ndev, &idt_dbgfs_info_ops); 252162306a36Sopenharmony_ci if (IS_ERR(ndev->dbgfs_info)) { 252262306a36Sopenharmony_ci dev_dbg(&ndev->ntb.pdev->dev, "Failed to create DebugFS node"); 252362306a36Sopenharmony_ci return PTR_ERR(ndev->dbgfs_info); 252462306a36Sopenharmony_ci } 252562306a36Sopenharmony_ci 252662306a36Sopenharmony_ci dev_dbg(&ndev->ntb.pdev->dev, "NTB device DebugFS node created"); 252762306a36Sopenharmony_ci 252862306a36Sopenharmony_ci return 0; 252962306a36Sopenharmony_ci} 253062306a36Sopenharmony_ci 253162306a36Sopenharmony_ci/* 253262306a36Sopenharmony_ci * idt_deinit_dbgfs() - deinitialize DebugFS node 253362306a36Sopenharmony_ci * @ndev: IDT NTB hardware driver descriptor 253462306a36Sopenharmony_ci * 253562306a36Sopenharmony_ci * Just discard the info node from DebugFS 253662306a36Sopenharmony_ci */ 253762306a36Sopenharmony_cistatic void idt_deinit_dbgfs(struct idt_ntb_dev *ndev) 253862306a36Sopenharmony_ci{ 253962306a36Sopenharmony_ci debugfs_remove(ndev->dbgfs_info); 254062306a36Sopenharmony_ci 254162306a36Sopenharmony_ci dev_dbg(&ndev->ntb.pdev->dev, "NTB device DebugFS node discarded"); 254262306a36Sopenharmony_ci} 254362306a36Sopenharmony_ci 254462306a36Sopenharmony_ci/*============================================================================= 254562306a36Sopenharmony_ci * 11. Basic PCIe device initialization 254662306a36Sopenharmony_ci *============================================================================= 254762306a36Sopenharmony_ci */ 254862306a36Sopenharmony_ci 254962306a36Sopenharmony_ci/* 255062306a36Sopenharmony_ci * idt_check_setup() - Check whether the IDT PCIe-swtich is properly 255162306a36Sopenharmony_ci * pre-initialized 255262306a36Sopenharmony_ci * @pdev: Pointer to the PCI device descriptor 255362306a36Sopenharmony_ci * 255462306a36Sopenharmony_ci * Return: zero on success, otherwise a negative error number. 255562306a36Sopenharmony_ci */ 255662306a36Sopenharmony_cistatic int idt_check_setup(struct pci_dev *pdev) 255762306a36Sopenharmony_ci{ 255862306a36Sopenharmony_ci u32 data; 255962306a36Sopenharmony_ci int ret; 256062306a36Sopenharmony_ci 256162306a36Sopenharmony_ci /* Read the BARSETUP0 */ 256262306a36Sopenharmony_ci ret = pci_read_config_dword(pdev, IDT_NT_BARSETUP0, &data); 256362306a36Sopenharmony_ci if (ret != 0) { 256462306a36Sopenharmony_ci dev_err(&pdev->dev, 256562306a36Sopenharmony_ci "Failed to read BARSETUP0 config register"); 256662306a36Sopenharmony_ci return ret; 256762306a36Sopenharmony_ci } 256862306a36Sopenharmony_ci 256962306a36Sopenharmony_ci /* Check whether the BAR0 register is enabled to be of config space */ 257062306a36Sopenharmony_ci if (!(data & IDT_BARSETUP_EN) || !(data & IDT_BARSETUP_MODE_CFG)) { 257162306a36Sopenharmony_ci dev_err(&pdev->dev, "BAR0 doesn't map config space"); 257262306a36Sopenharmony_ci return -EINVAL; 257362306a36Sopenharmony_ci } 257462306a36Sopenharmony_ci 257562306a36Sopenharmony_ci /* Configuration space BAR0 must have certain size */ 257662306a36Sopenharmony_ci if ((data & IDT_BARSETUP_SIZE_MASK) != IDT_BARSETUP_SIZE_CFG) { 257762306a36Sopenharmony_ci dev_err(&pdev->dev, "Invalid size of config space"); 257862306a36Sopenharmony_ci return -EINVAL; 257962306a36Sopenharmony_ci } 258062306a36Sopenharmony_ci 258162306a36Sopenharmony_ci dev_dbg(&pdev->dev, "NTB device pre-initialized correctly"); 258262306a36Sopenharmony_ci 258362306a36Sopenharmony_ci return 0; 258462306a36Sopenharmony_ci} 258562306a36Sopenharmony_ci 258662306a36Sopenharmony_ci/* 258762306a36Sopenharmony_ci * Create the IDT PCIe-switch driver descriptor 258862306a36Sopenharmony_ci * @pdev: Pointer to the PCI device descriptor 258962306a36Sopenharmony_ci * @id: IDT PCIe-device configuration 259062306a36Sopenharmony_ci * 259162306a36Sopenharmony_ci * It just allocates a memory for IDT PCIe-switch device structure and 259262306a36Sopenharmony_ci * initializes some commonly used fields. 259362306a36Sopenharmony_ci * 259462306a36Sopenharmony_ci * No need of release method, since managed device resource is used for 259562306a36Sopenharmony_ci * memory allocation. 259662306a36Sopenharmony_ci * 259762306a36Sopenharmony_ci * Return: pointer to the descriptor, otherwise a negative error number. 259862306a36Sopenharmony_ci */ 259962306a36Sopenharmony_cistatic struct idt_ntb_dev *idt_create_dev(struct pci_dev *pdev, 260062306a36Sopenharmony_ci const struct pci_device_id *id) 260162306a36Sopenharmony_ci{ 260262306a36Sopenharmony_ci struct idt_ntb_dev *ndev; 260362306a36Sopenharmony_ci 260462306a36Sopenharmony_ci /* Allocate memory for the IDT PCIe-device descriptor */ 260562306a36Sopenharmony_ci ndev = devm_kzalloc(&pdev->dev, sizeof(*ndev), GFP_KERNEL); 260662306a36Sopenharmony_ci if (!ndev) { 260762306a36Sopenharmony_ci dev_err(&pdev->dev, "Memory allocation failed for descriptor"); 260862306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 260962306a36Sopenharmony_ci } 261062306a36Sopenharmony_ci 261162306a36Sopenharmony_ci /* Save the IDT PCIe-switch ports configuration */ 261262306a36Sopenharmony_ci ndev->swcfg = (struct idt_89hpes_cfg *)id->driver_data; 261362306a36Sopenharmony_ci /* Save the PCI-device pointer inside the NTB device structure */ 261462306a36Sopenharmony_ci ndev->ntb.pdev = pdev; 261562306a36Sopenharmony_ci 261662306a36Sopenharmony_ci /* Initialize spin locker of Doorbell, Message and GASA registers */ 261762306a36Sopenharmony_ci spin_lock_init(&ndev->db_mask_lock); 261862306a36Sopenharmony_ci spin_lock_init(&ndev->msg_mask_lock); 261962306a36Sopenharmony_ci spin_lock_init(&ndev->gasa_lock); 262062306a36Sopenharmony_ci 262162306a36Sopenharmony_ci dev_info(&pdev->dev, "IDT %s discovered", ndev->swcfg->name); 262262306a36Sopenharmony_ci 262362306a36Sopenharmony_ci dev_dbg(&pdev->dev, "NTB device descriptor created"); 262462306a36Sopenharmony_ci 262562306a36Sopenharmony_ci return ndev; 262662306a36Sopenharmony_ci} 262762306a36Sopenharmony_ci 262862306a36Sopenharmony_ci/* 262962306a36Sopenharmony_ci * idt_init_pci() - initialize the basic PCI-related subsystem 263062306a36Sopenharmony_ci * @ndev: Pointer to the IDT PCIe-switch driver descriptor 263162306a36Sopenharmony_ci * 263262306a36Sopenharmony_ci * Managed device resources will be freed automatically in case of failure or 263362306a36Sopenharmony_ci * driver detachment. 263462306a36Sopenharmony_ci * 263562306a36Sopenharmony_ci * Return: zero on success, otherwise negative error number. 263662306a36Sopenharmony_ci */ 263762306a36Sopenharmony_cistatic int idt_init_pci(struct idt_ntb_dev *ndev) 263862306a36Sopenharmony_ci{ 263962306a36Sopenharmony_ci struct pci_dev *pdev = ndev->ntb.pdev; 264062306a36Sopenharmony_ci int ret; 264162306a36Sopenharmony_ci 264262306a36Sopenharmony_ci /* Initialize the bit mask of PCI/NTB DMA */ 264362306a36Sopenharmony_ci ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); 264462306a36Sopenharmony_ci if (ret != 0) { 264562306a36Sopenharmony_ci ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); 264662306a36Sopenharmony_ci if (ret != 0) { 264762306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to set DMA bit mask\n"); 264862306a36Sopenharmony_ci return ret; 264962306a36Sopenharmony_ci } 265062306a36Sopenharmony_ci dev_warn(&pdev->dev, "Cannot set DMA highmem bit mask\n"); 265162306a36Sopenharmony_ci } 265262306a36Sopenharmony_ci 265362306a36Sopenharmony_ci /* 265462306a36Sopenharmony_ci * The PCI core enables device error reporting. It's not critical to 265562306a36Sopenharmony_ci * have AER disabled in the kernel. 265662306a36Sopenharmony_ci * 265762306a36Sopenharmony_ci * Cleanup nonfatal error status before getting to init. 265862306a36Sopenharmony_ci */ 265962306a36Sopenharmony_ci pci_aer_clear_nonfatal_status(pdev); 266062306a36Sopenharmony_ci 266162306a36Sopenharmony_ci /* First enable the PCI device */ 266262306a36Sopenharmony_ci ret = pcim_enable_device(pdev); 266362306a36Sopenharmony_ci if (ret != 0) { 266462306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to enable PCIe device\n"); 266562306a36Sopenharmony_ci return ret; 266662306a36Sopenharmony_ci } 266762306a36Sopenharmony_ci 266862306a36Sopenharmony_ci /* 266962306a36Sopenharmony_ci * Enable the bus mastering, which effectively enables MSI IRQs and 267062306a36Sopenharmony_ci * Request TLPs translation 267162306a36Sopenharmony_ci */ 267262306a36Sopenharmony_ci pci_set_master(pdev); 267362306a36Sopenharmony_ci 267462306a36Sopenharmony_ci /* Request all BARs resources and map BAR0 only */ 267562306a36Sopenharmony_ci ret = pcim_iomap_regions_request_all(pdev, 1, NTB_NAME); 267662306a36Sopenharmony_ci if (ret != 0) { 267762306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to request resources\n"); 267862306a36Sopenharmony_ci goto err_clear_master; 267962306a36Sopenharmony_ci } 268062306a36Sopenharmony_ci 268162306a36Sopenharmony_ci /* Retrieve virtual address of BAR0 - PCI configuration space */ 268262306a36Sopenharmony_ci ndev->cfgspc = pcim_iomap_table(pdev)[0]; 268362306a36Sopenharmony_ci 268462306a36Sopenharmony_ci /* Put the IDT driver data pointer to the PCI-device private pointer */ 268562306a36Sopenharmony_ci pci_set_drvdata(pdev, ndev); 268662306a36Sopenharmony_ci 268762306a36Sopenharmony_ci dev_dbg(&pdev->dev, "NT-function PCIe interface initialized"); 268862306a36Sopenharmony_ci 268962306a36Sopenharmony_ci return 0; 269062306a36Sopenharmony_ci 269162306a36Sopenharmony_cierr_clear_master: 269262306a36Sopenharmony_ci pci_clear_master(pdev); 269362306a36Sopenharmony_ci 269462306a36Sopenharmony_ci return ret; 269562306a36Sopenharmony_ci} 269662306a36Sopenharmony_ci 269762306a36Sopenharmony_ci/* 269862306a36Sopenharmony_ci * idt_deinit_pci() - deinitialize the basic PCI-related subsystem 269962306a36Sopenharmony_ci * @ndev: Pointer to the IDT PCIe-switch driver descriptor 270062306a36Sopenharmony_ci * 270162306a36Sopenharmony_ci * Managed resources will be freed on the driver detachment 270262306a36Sopenharmony_ci */ 270362306a36Sopenharmony_cistatic void idt_deinit_pci(struct idt_ntb_dev *ndev) 270462306a36Sopenharmony_ci{ 270562306a36Sopenharmony_ci struct pci_dev *pdev = ndev->ntb.pdev; 270662306a36Sopenharmony_ci 270762306a36Sopenharmony_ci /* Clean up the PCI-device private data pointer */ 270862306a36Sopenharmony_ci pci_set_drvdata(pdev, NULL); 270962306a36Sopenharmony_ci 271062306a36Sopenharmony_ci /* Clear the bus master disabling the Request TLPs translation */ 271162306a36Sopenharmony_ci pci_clear_master(pdev); 271262306a36Sopenharmony_ci 271362306a36Sopenharmony_ci dev_dbg(&pdev->dev, "NT-function PCIe interface cleared"); 271462306a36Sopenharmony_ci} 271562306a36Sopenharmony_ci 271662306a36Sopenharmony_ci/*=========================================================================== 271762306a36Sopenharmony_ci * 12. PCI bus callback functions 271862306a36Sopenharmony_ci *=========================================================================== 271962306a36Sopenharmony_ci */ 272062306a36Sopenharmony_ci 272162306a36Sopenharmony_ci/* 272262306a36Sopenharmony_ci * idt_pci_probe() - PCI device probe callback 272362306a36Sopenharmony_ci * @pdev: Pointer to PCI device structure 272462306a36Sopenharmony_ci * @id: PCIe device custom descriptor 272562306a36Sopenharmony_ci * 272662306a36Sopenharmony_ci * Return: zero on success, otherwise negative error number 272762306a36Sopenharmony_ci */ 272862306a36Sopenharmony_cistatic int idt_pci_probe(struct pci_dev *pdev, 272962306a36Sopenharmony_ci const struct pci_device_id *id) 273062306a36Sopenharmony_ci{ 273162306a36Sopenharmony_ci struct idt_ntb_dev *ndev; 273262306a36Sopenharmony_ci int ret; 273362306a36Sopenharmony_ci 273462306a36Sopenharmony_ci /* Check whether IDT PCIe-switch is properly pre-initialized */ 273562306a36Sopenharmony_ci ret = idt_check_setup(pdev); 273662306a36Sopenharmony_ci if (ret != 0) 273762306a36Sopenharmony_ci return ret; 273862306a36Sopenharmony_ci 273962306a36Sopenharmony_ci /* Allocate the memory for IDT NTB device data */ 274062306a36Sopenharmony_ci ndev = idt_create_dev(pdev, id); 274162306a36Sopenharmony_ci if (IS_ERR(ndev)) 274262306a36Sopenharmony_ci return PTR_ERR(ndev); 274362306a36Sopenharmony_ci 274462306a36Sopenharmony_ci /* Initialize the basic PCI subsystem of the device */ 274562306a36Sopenharmony_ci ret = idt_init_pci(ndev); 274662306a36Sopenharmony_ci if (ret != 0) 274762306a36Sopenharmony_ci return ret; 274862306a36Sopenharmony_ci 274962306a36Sopenharmony_ci /* Scan ports of the IDT PCIe-switch */ 275062306a36Sopenharmony_ci (void)idt_scan_ports(ndev); 275162306a36Sopenharmony_ci 275262306a36Sopenharmony_ci /* Initialize NTB link events subsystem */ 275362306a36Sopenharmony_ci idt_init_link(ndev); 275462306a36Sopenharmony_ci 275562306a36Sopenharmony_ci /* Initialize MWs subsystem */ 275662306a36Sopenharmony_ci ret = idt_init_mws(ndev); 275762306a36Sopenharmony_ci if (ret != 0) 275862306a36Sopenharmony_ci goto err_deinit_link; 275962306a36Sopenharmony_ci 276062306a36Sopenharmony_ci /* Initialize Messaging subsystem */ 276162306a36Sopenharmony_ci idt_init_msg(ndev); 276262306a36Sopenharmony_ci 276362306a36Sopenharmony_ci /* Initialize hwmon interface */ 276462306a36Sopenharmony_ci idt_init_temp(ndev); 276562306a36Sopenharmony_ci 276662306a36Sopenharmony_ci /* Initialize IDT interrupts handler */ 276762306a36Sopenharmony_ci ret = idt_init_isr(ndev); 276862306a36Sopenharmony_ci if (ret != 0) 276962306a36Sopenharmony_ci goto err_deinit_link; 277062306a36Sopenharmony_ci 277162306a36Sopenharmony_ci /* Register IDT NTB devices on the NTB bus */ 277262306a36Sopenharmony_ci ret = idt_register_device(ndev); 277362306a36Sopenharmony_ci if (ret != 0) 277462306a36Sopenharmony_ci goto err_deinit_isr; 277562306a36Sopenharmony_ci 277662306a36Sopenharmony_ci /* Initialize DebugFS info node */ 277762306a36Sopenharmony_ci (void)idt_init_dbgfs(ndev); 277862306a36Sopenharmony_ci 277962306a36Sopenharmony_ci /* IDT PCIe-switch NTB driver is finally initialized */ 278062306a36Sopenharmony_ci dev_info(&pdev->dev, "IDT NTB device is ready"); 278162306a36Sopenharmony_ci 278262306a36Sopenharmony_ci /* May the force be with us... */ 278362306a36Sopenharmony_ci return 0; 278462306a36Sopenharmony_ci 278562306a36Sopenharmony_cierr_deinit_isr: 278662306a36Sopenharmony_ci idt_deinit_isr(ndev); 278762306a36Sopenharmony_cierr_deinit_link: 278862306a36Sopenharmony_ci idt_deinit_link(ndev); 278962306a36Sopenharmony_ci idt_deinit_pci(ndev); 279062306a36Sopenharmony_ci 279162306a36Sopenharmony_ci return ret; 279262306a36Sopenharmony_ci} 279362306a36Sopenharmony_ci 279462306a36Sopenharmony_ci/* 279562306a36Sopenharmony_ci * idt_pci_probe() - PCI device remove callback 279662306a36Sopenharmony_ci * @pdev: Pointer to PCI device structure 279762306a36Sopenharmony_ci */ 279862306a36Sopenharmony_cistatic void idt_pci_remove(struct pci_dev *pdev) 279962306a36Sopenharmony_ci{ 280062306a36Sopenharmony_ci struct idt_ntb_dev *ndev = pci_get_drvdata(pdev); 280162306a36Sopenharmony_ci 280262306a36Sopenharmony_ci /* Deinit the DebugFS node */ 280362306a36Sopenharmony_ci idt_deinit_dbgfs(ndev); 280462306a36Sopenharmony_ci 280562306a36Sopenharmony_ci /* Unregister NTB device */ 280662306a36Sopenharmony_ci idt_unregister_device(ndev); 280762306a36Sopenharmony_ci 280862306a36Sopenharmony_ci /* Stop the interrupts handling */ 280962306a36Sopenharmony_ci idt_deinit_isr(ndev); 281062306a36Sopenharmony_ci 281162306a36Sopenharmony_ci /* Deinitialize link event subsystem */ 281262306a36Sopenharmony_ci idt_deinit_link(ndev); 281362306a36Sopenharmony_ci 281462306a36Sopenharmony_ci /* Deinit basic PCI subsystem */ 281562306a36Sopenharmony_ci idt_deinit_pci(ndev); 281662306a36Sopenharmony_ci 281762306a36Sopenharmony_ci /* IDT PCIe-switch NTB driver is finally initialized */ 281862306a36Sopenharmony_ci dev_info(&pdev->dev, "IDT NTB device is removed"); 281962306a36Sopenharmony_ci 282062306a36Sopenharmony_ci /* Sayonara... */ 282162306a36Sopenharmony_ci} 282262306a36Sopenharmony_ci 282362306a36Sopenharmony_ci/* 282462306a36Sopenharmony_ci * IDT PCIe-switch models ports configuration structures 282562306a36Sopenharmony_ci */ 282662306a36Sopenharmony_cistatic const struct idt_89hpes_cfg idt_89hpes24nt6ag2_config = { 282762306a36Sopenharmony_ci .name = "89HPES24NT6AG2", 282862306a36Sopenharmony_ci .port_cnt = 6, .ports = {0, 2, 4, 6, 8, 12} 282962306a36Sopenharmony_ci}; 283062306a36Sopenharmony_cistatic const struct idt_89hpes_cfg idt_89hpes32nt8ag2_config = { 283162306a36Sopenharmony_ci .name = "89HPES32NT8AG2", 283262306a36Sopenharmony_ci .port_cnt = 8, .ports = {0, 2, 4, 6, 8, 12, 16, 20} 283362306a36Sopenharmony_ci}; 283462306a36Sopenharmony_cistatic const struct idt_89hpes_cfg idt_89hpes32nt8bg2_config = { 283562306a36Sopenharmony_ci .name = "89HPES32NT8BG2", 283662306a36Sopenharmony_ci .port_cnt = 8, .ports = {0, 2, 4, 6, 8, 12, 16, 20} 283762306a36Sopenharmony_ci}; 283862306a36Sopenharmony_cistatic const struct idt_89hpes_cfg idt_89hpes12nt12g2_config = { 283962306a36Sopenharmony_ci .name = "89HPES12NT12G2", 284062306a36Sopenharmony_ci .port_cnt = 3, .ports = {0, 8, 16} 284162306a36Sopenharmony_ci}; 284262306a36Sopenharmony_cistatic const struct idt_89hpes_cfg idt_89hpes16nt16g2_config = { 284362306a36Sopenharmony_ci .name = "89HPES16NT16G2", 284462306a36Sopenharmony_ci .port_cnt = 4, .ports = {0, 8, 12, 16} 284562306a36Sopenharmony_ci}; 284662306a36Sopenharmony_cistatic const struct idt_89hpes_cfg idt_89hpes24nt24g2_config = { 284762306a36Sopenharmony_ci .name = "89HPES24NT24G2", 284862306a36Sopenharmony_ci .port_cnt = 8, .ports = {0, 2, 4, 6, 8, 12, 16, 20} 284962306a36Sopenharmony_ci}; 285062306a36Sopenharmony_cistatic const struct idt_89hpes_cfg idt_89hpes32nt24ag2_config = { 285162306a36Sopenharmony_ci .name = "89HPES32NT24AG2", 285262306a36Sopenharmony_ci .port_cnt = 8, .ports = {0, 2, 4, 6, 8, 12, 16, 20} 285362306a36Sopenharmony_ci}; 285462306a36Sopenharmony_cistatic const struct idt_89hpes_cfg idt_89hpes32nt24bg2_config = { 285562306a36Sopenharmony_ci .name = "89HPES32NT24BG2", 285662306a36Sopenharmony_ci .port_cnt = 8, .ports = {0, 2, 4, 6, 8, 12, 16, 20} 285762306a36Sopenharmony_ci}; 285862306a36Sopenharmony_ci 285962306a36Sopenharmony_ci/* 286062306a36Sopenharmony_ci * PCI-ids table of the supported IDT PCIe-switch devices 286162306a36Sopenharmony_ci */ 286262306a36Sopenharmony_cistatic const struct pci_device_id idt_pci_tbl[] = { 286362306a36Sopenharmony_ci {IDT_PCI_DEVICE_IDS(89HPES24NT6AG2, idt_89hpes24nt6ag2_config)}, 286462306a36Sopenharmony_ci {IDT_PCI_DEVICE_IDS(89HPES32NT8AG2, idt_89hpes32nt8ag2_config)}, 286562306a36Sopenharmony_ci {IDT_PCI_DEVICE_IDS(89HPES32NT8BG2, idt_89hpes32nt8bg2_config)}, 286662306a36Sopenharmony_ci {IDT_PCI_DEVICE_IDS(89HPES12NT12G2, idt_89hpes12nt12g2_config)}, 286762306a36Sopenharmony_ci {IDT_PCI_DEVICE_IDS(89HPES16NT16G2, idt_89hpes16nt16g2_config)}, 286862306a36Sopenharmony_ci {IDT_PCI_DEVICE_IDS(89HPES24NT24G2, idt_89hpes24nt24g2_config)}, 286962306a36Sopenharmony_ci {IDT_PCI_DEVICE_IDS(89HPES32NT24AG2, idt_89hpes32nt24ag2_config)}, 287062306a36Sopenharmony_ci {IDT_PCI_DEVICE_IDS(89HPES32NT24BG2, idt_89hpes32nt24bg2_config)}, 287162306a36Sopenharmony_ci {0} 287262306a36Sopenharmony_ci}; 287362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, idt_pci_tbl); 287462306a36Sopenharmony_ci 287562306a36Sopenharmony_ci/* 287662306a36Sopenharmony_ci * IDT PCIe-switch NT-function device driver structure definition 287762306a36Sopenharmony_ci */ 287862306a36Sopenharmony_cistatic struct pci_driver idt_pci_driver = { 287962306a36Sopenharmony_ci .name = KBUILD_MODNAME, 288062306a36Sopenharmony_ci .probe = idt_pci_probe, 288162306a36Sopenharmony_ci .remove = idt_pci_remove, 288262306a36Sopenharmony_ci .id_table = idt_pci_tbl, 288362306a36Sopenharmony_ci}; 288462306a36Sopenharmony_ci 288562306a36Sopenharmony_cistatic int __init idt_pci_driver_init(void) 288662306a36Sopenharmony_ci{ 288762306a36Sopenharmony_ci int ret; 288862306a36Sopenharmony_ci pr_info("%s %s\n", NTB_DESC, NTB_VER); 288962306a36Sopenharmony_ci 289062306a36Sopenharmony_ci /* Create the top DebugFS directory if the FS is initialized */ 289162306a36Sopenharmony_ci if (debugfs_initialized()) 289262306a36Sopenharmony_ci dbgfs_topdir = debugfs_create_dir(KBUILD_MODNAME, NULL); 289362306a36Sopenharmony_ci 289462306a36Sopenharmony_ci /* Register the NTB hardware driver to handle the PCI device */ 289562306a36Sopenharmony_ci ret = pci_register_driver(&idt_pci_driver); 289662306a36Sopenharmony_ci if (ret) 289762306a36Sopenharmony_ci debugfs_remove_recursive(dbgfs_topdir); 289862306a36Sopenharmony_ci 289962306a36Sopenharmony_ci return ret; 290062306a36Sopenharmony_ci} 290162306a36Sopenharmony_cimodule_init(idt_pci_driver_init); 290262306a36Sopenharmony_ci 290362306a36Sopenharmony_cistatic void __exit idt_pci_driver_exit(void) 290462306a36Sopenharmony_ci{ 290562306a36Sopenharmony_ci /* Unregister the NTB hardware driver */ 290662306a36Sopenharmony_ci pci_unregister_driver(&idt_pci_driver); 290762306a36Sopenharmony_ci 290862306a36Sopenharmony_ci /* Discard the top DebugFS directory */ 290962306a36Sopenharmony_ci debugfs_remove_recursive(dbgfs_topdir); 291062306a36Sopenharmony_ci} 291162306a36Sopenharmony_cimodule_exit(idt_pci_driver_exit); 291262306a36Sopenharmony_ci 2913