138 lines
3.5 KiB
C
138 lines
3.5 KiB
C
|
// 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 0xFF to indicate a broadcast read.
|
||
|
*/
|
||
|
#define DF_BROADCAST 0xFF
|
||
|
|
||
|
#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 = 0x8C, ficad_addr = 0xB8;
|
||
|
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 = 0x5C;
|
||
|
ficad_addr = 0x98;
|
||
|
|
||
|
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);
|
||
|
}
|