Merge tag 'edac_updates_for_v6.9' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-block.git] / drivers / ras / amd / atl / access.c
CommitLineData
3f317499
YG
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. */
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 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
453f0ae7
M
39static 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
3f317499
YG
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
453f0ae7 72 node = get_accessible_node(node);
3f317499
YG
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 the
88 * 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}