// SPDX-License-Identifier: GPL-2.0-or-later
/*
* AMD Address Translation Library
*
* access . c : DF Indirect Access functions
*
* Copyright ( c ) 2023 , Advanced Micro Devices , Inc .
* All Rights Reserved .
*
* Author : Yazen Ghannam < Yazen . Ghannam @ amd . com >
*/
#include "internal.h"
/* Protect the PCI config register pairs used for DF indirect access. */
static DEFINE_MUTEX(df_indirect_mutex);
/*
* Data Fabric Indirect Access uses FICAA / FICAD .
*
* Fabric Indirect Configuration Access Address ( FICAA ) : constructed based
* on the device ' s Instance Id and the PCI function and register offset of
* the desired register .
*
* Fabric Indirect Configuration Access Data ( FICAD ) : there are FICAD
* low and high registers but so far only the low register is needed .
*
* Use Instance Id 0 xFF to indicate a broadcast read .
*/
#define DF_BROADCAST 0 xFF
#define DF_FICAA_INST_EN BIT(0 )
#define DF_FICAA_REG_NUM GENMASK(10 , 1 )
#define DF_FICAA_FUNC_NUM GENMASK(13 , 11 )
#define DF_FICAA_INST_ID GENMASK(23 , 16 )
#define DF_FICAA_REG_NUM_LEGACY GENMASK(10 , 2 )
static u16 get_accessible_node(u16 node)
{
/*
* On heterogeneous systems , not all AMD Nodes are accessible
* through software - visible registers . The Node ID needs to be
* adjusted for register accesses . But its value should not be
* changed for the translation methods .
*/
if (df_cfg.flags.heterogeneous) {
/* Only Node 0 is accessible on DF3.5 systems. */
if (df_cfg.rev == DF3p5)
node = 0 ;
/*
* Only the first Node in each Socket is accessible on
* DF4 . 5 systems , and this is visible to software as one
* Fabric per Socket . The Socket ID can be derived from
* the Node ID and global shift values .
*/
if (df_cfg.rev == DF4p5)
node >>= df_cfg.socket_id_shift - df_cfg.node_id_shift;
}
return node;
}
static int __df_indirect_read(u16 node, u8 func, u16 reg, u8 instance_id, u32 *lo)
{
u32 ficaa_addr = 0 x8C, ficad_addr = 0 xB8;
struct pci_dev *F4;
int err = -ENODEV;
u32 ficaa = 0 ;
node = get_accessible_node(node);
if (node >= amd_nb_num()) {
pr_debug("Node %u is out of bounds\n" , node);
goto out;
}
F4 = node_to_amd_nb(node)->link;
if (!F4) {
pr_debug("DF function 4 not found\n" );
goto out;
}
/* Enable instance-specific access. */
if (instance_id != DF_BROADCAST) {
ficaa |= FIELD_PREP(DF_FICAA_INST_EN, 1 );
ficaa |= FIELD_PREP(DF_FICAA_INST_ID, instance_id);
}
/*
* The two least - significant bits are masked when inputing the
* register offset to FICAA .
*/
reg >>= 2 ;
if (df_cfg.flags.legacy_ficaa) {
ficaa_addr = 0 x5C;
ficad_addr = 0 x98;
ficaa |= FIELD_PREP(DF_FICAA_REG_NUM_LEGACY, reg);
} else {
ficaa |= FIELD_PREP(DF_FICAA_REG_NUM, reg);
}
ficaa |= FIELD_PREP(DF_FICAA_FUNC_NUM, func);
mutex_lock(&df_indirect_mutex);
err = pci_write_config_dword(F4, ficaa_addr, ficaa);
if (err) {
pr_warn("Error writing DF Indirect FICAA, FICAA=0x%x\n" , ficaa);
goto out_unlock;
}
err = pci_read_config_dword(F4, ficad_addr, lo);
if (err)
pr_warn("Error reading DF Indirect FICAD LO, FICAA=0x%x.\n" , ficaa);
pr_debug("node=%u inst=0x%x func=0x%x reg=0x%x val=0x%x" ,
node, instance_id, func, reg << 2 , *lo);
out_unlock:
mutex_unlock(&df_indirect_mutex);
out:
return err;
}
int df_indirect_read_instance(u16 node, u8 func, u16 reg, u8 instance_id, u32 *lo)
{
return __df_indirect_read(node, func, reg, instance_id, lo);
}
int df_indirect_read_broadcast(u16 node, u8 func, u16 reg, u32 *lo)
{
return __df_indirect_read(node, func, reg, DF_BROADCAST, lo);
}
Messung V0.5 in Prozent C=95 H=96 G=95
¤ Dauer der Verarbeitung: 0.4 Sekunden
¤
*© Formatika GbR, Deutschland