1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * AMD Address Translation Library
5 * access.c : DF Indirect Access functions
7 * Copyright (c) 2023, Advanced Micro Devices, Inc.
10 * Author: Yazen Ghannam <Yazen.Ghannam@amd.com>
15 /* Protect the PCI config register pairs used for DF indirect access. */
16 static DEFINE_MUTEX(df_indirect_mutex
);
19 * Data Fabric Indirect Access uses FICAA/FICAD.
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.
25 * Fabric Indirect Configuration Access Data (FICAD): there are FICAD
26 * low and high registers but so far only the low register is needed.
28 * Use Instance Id 0xFF to indicate a broadcast read.
30 #define DF_BROADCAST 0xFF
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)
37 #define DF_FICAA_REG_NUM_LEGACY GENMASK(10, 2)
39 static u16
get_accessible_node(u16 node
)
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.
47 if (df_cfg
.flags
.heterogeneous
) {
48 /* Only Node 0 is accessible on DF3.5 systems. */
49 if (df_cfg
.rev
== DF3p5
)
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.
58 if (df_cfg
.rev
== DF4p5
)
59 node
>>= df_cfg
.socket_id_shift
- df_cfg
.node_id_shift
;
65 static int __df_indirect_read(u16 node
, u8 func
, u16 reg
, u8 instance_id
, u32
*lo
)
67 u32 ficaa_addr
= 0x8C, ficad_addr
= 0xB8;
72 node
= get_accessible_node(node
);
73 if (node
>= amd_nb_num()) {
74 pr_debug("Node %u is out of bounds\n", node
);
78 F4
= node_to_amd_nb(node
)->link
;
80 pr_debug("DF function 4 not found\n");
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
);
91 * The two least-significant bits are masked when inputing the
92 * register offset to FICAA.
96 if (df_cfg
.flags
.legacy_ficaa
) {
100 ficaa
|= FIELD_PREP(DF_FICAA_REG_NUM_LEGACY
, reg
);
102 ficaa
|= FIELD_PREP(DF_FICAA_REG_NUM
, reg
);
105 ficaa
|= FIELD_PREP(DF_FICAA_FUNC_NUM
, func
);
107 mutex_lock(&df_indirect_mutex
);
109 err
= pci_write_config_dword(F4
, ficaa_addr
, ficaa
);
111 pr_warn("Error writing DF Indirect FICAA, FICAA=0x%x\n", ficaa
);
115 err
= pci_read_config_dword(F4
, ficad_addr
, lo
);
117 pr_warn("Error reading DF Indirect FICAD LO, FICAA=0x%x.\n", ficaa
);
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
);
123 mutex_unlock(&df_indirect_mutex
);
129 int df_indirect_read_instance(u16 node
, u8 func
, u16 reg
, u8 instance_id
, u32
*lo
)
131 return __df_indirect_read(node
, func
, reg
, instance_id
, lo
);
134 int df_indirect_read_broadcast(u16 node
, u8 func
, u16 reg
, u32
*lo
)
136 return __df_indirect_read(node
, func
, reg
, DF_BROADCAST
, lo
);