Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.8.
  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
 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
 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
 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
122out_unlock:
123	mutex_unlock(&df_indirect_mutex);
124
125out:
126	return err;
127}
128
129int 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
134int 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}