]> Git Repo - J-linux.git/blob - drivers/ras/amd/atl/access.c
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.git] / drivers / ras / amd / atl / access.c
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 <[email protected]>
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 }
This page took 0.034623 seconds and 4 git commands to generate.