134 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * AMD Address Translation Library4 *5 * access.c : DF Indirect Access functions6 *7 * Copyright (c) 2023, Advanced Micro Devices, Inc.8 * All Rights Reserved.9 *10 * Author: Yazen Ghannam <Yazen.Ghannam@amd.com>11 */12 13#include "internal.h"14 15/* Protect the PCI config register pairs used for DF indirect access. */16static DEFINE_MUTEX(df_indirect_mutex);17 18/*19 * Data Fabric Indirect Access uses FICAA/FICAD.20 *21 * Fabric Indirect Configuration Access Address (FICAA): constructed based22 * on the device's Instance Id and the PCI function and register offset of23 * the desired register.24 *25 * Fabric Indirect Configuration Access Data (FICAD): there are FICAD26 * low and high registers but so far only the low register is needed.27 *28 * Use Instance Id 0xFF to indicate a broadcast read.29 */30#define DF_BROADCAST 0xFF31 32#define DF_FICAA_INST_EN BIT(0)33#define DF_FICAA_REG_NUM GENMASK(10, 1)34#define DF_FICAA_FUNC_NUM GENMASK(13, 11)35#define DF_FICAA_INST_ID GENMASK(23, 16)36 37#define DF_FICAA_REG_NUM_LEGACY GENMASK(10, 2)38 39static u16 get_accessible_node(u16 node)40{41 /*42 * On heterogeneous systems, not all AMD Nodes are accessible43 * through software-visible registers. The Node ID needs to be44 * adjusted for register accesses. But its value should not be45 * changed for the translation methods.46 */47 if (df_cfg.flags.heterogeneous) {48 /* Only Node 0 is accessible on DF3.5 systems. */49 if (df_cfg.rev == DF3p5)50 node = 0;51 52 /*53 * Only the first Node in each Socket is accessible on54 * DF4.5 systems, and this is visible to software as one55 * Fabric per Socket. The Socket ID can be derived from56 * the Node ID and global shift values.57 */58 if (df_cfg.rev == DF4p5)59 node >>= df_cfg.socket_id_shift - df_cfg.node_id_shift;60 }61 62 return node;63}64 65static int __df_indirect_read(u16 node, u8 func, u16 reg, u8 instance_id, u32 *lo)66{67 u32 ficaa_addr = 0x8C, ficad_addr = 0xB8;68 struct pci_dev *F4;69 int err = -ENODEV;70 u32 ficaa = 0;71 72 node = get_accessible_node(node);73 if (node >= amd_nb_num())74 goto out;75 76 F4 = node_to_amd_nb(node)->link;77 if (!F4)78 goto out;79 80 /* Enable instance-specific access. */81 if (instance_id != DF_BROADCAST) {82 ficaa |= FIELD_PREP(DF_FICAA_INST_EN, 1);83 ficaa |= FIELD_PREP(DF_FICAA_INST_ID, instance_id);84 }85 86 /*87 * The two least-significant bits are masked when inputing the88 * register offset to FICAA.89 */90 reg >>= 2;91 92 if (df_cfg.flags.legacy_ficaa) {93 ficaa_addr = 0x5C;94 ficad_addr = 0x98;95 96 ficaa |= FIELD_PREP(DF_FICAA_REG_NUM_LEGACY, reg);97 } else {98 ficaa |= FIELD_PREP(DF_FICAA_REG_NUM, reg);99 }100 101 ficaa |= FIELD_PREP(DF_FICAA_FUNC_NUM, func);102 103 mutex_lock(&df_indirect_mutex);104 105 err = pci_write_config_dword(F4, ficaa_addr, ficaa);106 if (err) {107 pr_warn("Error writing DF Indirect FICAA, FICAA=0x%x\n", ficaa);108 goto out_unlock;109 }110 111 err = pci_read_config_dword(F4, ficad_addr, lo);112 if (err)113 pr_warn("Error reading DF Indirect FICAD LO, FICAA=0x%x.\n", ficaa);114 115 pr_debug("node=%u inst=0x%x func=0x%x reg=0x%x val=0x%x",116 node, instance_id, func, reg << 2, *lo);117 118out_unlock:119 mutex_unlock(&df_indirect_mutex);120 121out:122 return err;123}124 125int df_indirect_read_instance(u16 node, u8 func, u16 reg, u8 instance_id, u32 *lo)126{127 return __df_indirect_read(node, func, reg, instance_id, lo);128}129 130int df_indirect_read_broadcast(u16 node, u8 func, u16 reg, u32 *lo)131{132 return __df_indirect_read(node, func, reg, DF_BROADCAST, lo);133}134