1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * AMD Address Translation Library 4 * 5 * access.c : DF Indirect Access functions 6 * 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. */ 16 static DEFINE_MUTEX(df_indirect_mutex); 17 18 /* 19 * Data Fabric Indirect Access uses FICAA/FICAD. 20 * 21 * Fabric Indirect Configuration Access Address (FICAA): constructed based 22 * on the device's Instance Id and the PCI function and register offset of 23 * the desired register. 24 * 25 * Fabric Indirect Configuration Access Data (FICAD): there are FICAD 26 * 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 0xFF 31 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 39 static u16 get_accessible_node(u16 node) 40 { 41 /* 42 * On heterogeneous systems, not all AMD Nodes are accessible 43 * through software-visible registers. The Node ID needs to be 44 * adjusted for register accesses. But its value should not be 45 * 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 on 54 * DF4.5 systems, and this is visible to software as one 55 * Fabric per Socket. The Socket ID can be derived from 56 * 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 65 static 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 pr_debug("Node %u is out of bounds\n", node); 75 goto out; 76 } 77 78 F4 = node_to_amd_nb(node)->link; 79 if (!F4) { 80 pr_debug("DF function 4 not found\n"); 81 goto out; 82 } 83 84 /* Enable instance-specific access. */ 85 if (instance_id != DF_BROADCAST) { 86 ficaa |= FIELD_PREP(DF_FICAA_INST_EN, 1); 87 ficaa |= FIELD_PREP(DF_FICAA_INST_ID, instance_id); 88 } 89 90 /* 91 * The two least-significant bits are masked when inputing the 92 * register offset to FICAA. 93 */ 94 reg >>= 2; 95 96 if (df_cfg.flags.legacy_ficaa) { 97 ficaa_addr = 0x5C; 98 ficad_addr = 0x98; 99 100 ficaa |= FIELD_PREP(DF_FICAA_REG_NUM_LEGACY, reg); 101 } else { 102 ficaa |= FIELD_PREP(DF_FICAA_REG_NUM, reg); 103 } 104 105 ficaa |= FIELD_PREP(DF_FICAA_FUNC_NUM, func); 106 107 mutex_lock(&df_indirect_mutex); 108 109 err = pci_write_config_dword(F4, ficaa_addr, ficaa); 110 if (err) { 111 pr_warn("Error writing DF Indirect FICAA, FICAA=0x%x\n", ficaa); 112 goto out_unlock; 113 } 114 115 err = pci_read_config_dword(F4, ficad_addr, lo); 116 if (err) 117 pr_warn("Error reading DF Indirect FICAD LO, FICAA=0x%x.\n", ficaa); 118 119 pr_debug("node=%u inst=0x%x func=0x%x reg=0x%x val=0x%x", 120 node, instance_id, func, reg << 2, *lo); 121 122 out_unlock: 123 mutex_unlock(&df_indirect_mutex); 124 125 out: 126 return err; 127 } 128 129 int df_indirect_read_instance(u16 node, u8 func, u16 reg, u8 instance_id, u32 *lo) 130 { 131 return __df_indirect_read(node, func, reg, instance_id, lo); 132 } 133 134 int df_indirect_read_broadcast(u16 node, u8 func, u16 reg, u32 *lo) 135 { 136 return __df_indirect_read(node, func, reg, DF_BROADCAST, lo); 137 } 138