summaryrefslogtreecommitdiff
path: root/drivers/ras/amd/atl/access.c
blob: f6dd87bb2c35a20f8bf0e3441f9f5450e209a403 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * AMD Address Translation Library
 *
 * access.c : DF Indirect Access functions
 *
 * Copyright (c) 2023, Advanced Micro Devices, Inc.
 * All Rights Reserved.
 *
 * Author: Yazen Ghannam <Yazen.Ghannam@amd.com>
 */

#include "internal.h"

/* Protect the PCI config register pairs used for DF indirect access. */
static DEFINE_MUTEX(df_indirect_mutex);

/*
 * Data Fabric Indirect Access uses FICAA/FICAD.
 *
 * Fabric Indirect Configuration Access Address (FICAA): constructed based
 * on the device's Instance Id and the PCI function and register offset of
 * the desired register.
 *
 * Fabric Indirect Configuration Access Data (FICAD): there are FICAD
 * low and high registers but so far only the low register is needed.
 *
 * Use Instance Id 0xFF to indicate a broadcast read.
 */
#define DF_BROADCAST		0xFF

#define DF_FICAA_INST_EN	BIT(0)
#define DF_FICAA_REG_NUM	GENMASK(10, 1)
#define DF_FICAA_FUNC_NUM	GENMASK(13, 11)
#define DF_FICAA_INST_ID	GENMASK(23, 16)

#define DF_FICAA_REG_NUM_LEGACY	GENMASK(10, 2)

static int __df_indirect_read(u16 node, u8 func, u16 reg, u8 instance_id, u32 *lo)
{
	u32 ficaa_addr = 0x8C, ficad_addr = 0xB8;
	struct pci_dev *F4;
	int err = -ENODEV;
	u32 ficaa = 0;

	if (node >= amd_nb_num())
		goto out;

	F4 = node_to_amd_nb(node)->link;
	if (!F4)
		goto out;

	/* Enable instance-specific access. */
	if (instance_id != DF_BROADCAST) {
		ficaa |= FIELD_PREP(DF_FICAA_INST_EN, 1);
		ficaa |= FIELD_PREP(DF_FICAA_INST_ID, instance_id);
	}

	/*
	 * The two least-significant bits are masked when inputing the
	 * register offset to FICAA.
	 */
	reg >>= 2;

	if (df_cfg.flags.legacy_ficaa) {
		ficaa_addr = 0x5C;
		ficad_addr = 0x98;

		ficaa |= FIELD_PREP(DF_FICAA_REG_NUM_LEGACY, reg);
	} else {
		ficaa |= FIELD_PREP(DF_FICAA_REG_NUM, reg);
	}

	ficaa |= FIELD_PREP(DF_FICAA_FUNC_NUM, func);

	mutex_lock(&df_indirect_mutex);

	err = pci_write_config_dword(F4, ficaa_addr, ficaa);
	if (err) {
		pr_warn("Error writing DF Indirect FICAA, FICAA=0x%x\n", ficaa);
		goto out_unlock;
	}

	err = pci_read_config_dword(F4, ficad_addr, lo);
	if (err)
		pr_warn("Error reading DF Indirect FICAD LO, FICAA=0x%x.\n", ficaa);

	pr_debug("node=%u inst=0x%x func=0x%x reg=0x%x val=0x%x",
		 node, instance_id, func, reg << 2, *lo);

out_unlock:
	mutex_unlock(&df_indirect_mutex);

out:
	return err;
}

int df_indirect_read_instance(u16 node, u8 func, u16 reg, u8 instance_id, u32 *lo)
{
	return __df_indirect_read(node, func, reg, instance_id, lo);
}

int df_indirect_read_broadcast(u16 node, u8 func, u16 reg, u32 *lo)
{
	return __df_indirect_read(node, func, reg, DF_BROADCAST, lo);
}