Unverified Commit 67ebf712 authored by Mark Brown's avatar Mark Brown
Browse files

Adjust all AMD audio drivers to use AMD_NODE

Merge series from Mario Limonciello <superm1@kernel.org>:

The various AMD audio drivers have self contained implementations
for SMN router communication that require hardcoding the bridge ID.

These implementations also don't prevent race conditions with other
drivers performing SMN communication.

A new centralized driver AMD_NODE is introduced and all drivers in
the kernel should use this instead. Adjust all AMD audio drivers to
use it.
parents 5d9fca12 a261d77f
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -142,7 +142,7 @@ static __always_inline int syscall_32_enter(struct pt_regs *regs)
#ifdef CONFIG_IA32_EMULATION
bool __ia32_enabled __ro_after_init = !IS_ENABLED(CONFIG_IA32_EMULATION_DEFAULT_DISABLED);

static int ia32_emulation_override_cmdline(char *arg)
static int __init ia32_emulation_override_cmdline(char *arg)
{
	return kstrtobool(arg, &__ia32_enabled);
}
+0 −1
Original line number Diff line number Diff line
@@ -27,7 +27,6 @@ struct amd_l3_cache {
};

struct amd_northbridge {
	struct pci_dev *root;
	struct pci_dev *misc;
	struct pci_dev *link;
	struct amd_l3_cache l3_cache;
+24 −0
Original line number Diff line number Diff line
@@ -30,7 +30,31 @@ static inline u16 amd_num_nodes(void)
	return topology_amd_nodes_per_pkg() * topology_max_packages();
}

#ifdef CONFIG_AMD_NODE
int __must_check amd_smn_read(u16 node, u32 address, u32 *value);
int __must_check amd_smn_write(u16 node, u32 address, u32 value);

/* Should only be used by the HSMP driver. */
int __must_check amd_smn_hsmp_rdwr(u16 node, u32 address, u32 *value, bool write);
#else
static inline int __must_check amd_smn_read(u16 node, u32 address, u32 *value) { return -ENODEV; }
static inline int __must_check amd_smn_write(u16 node, u32 address, u32 value) { return -ENODEV; }

static inline int __must_check amd_smn_hsmp_rdwr(u16 node, u32 address, u32 *value, bool write)
{
	return -ENODEV;
}
#endif /* CONFIG_AMD_NODE */

/* helper for use with read_poll_timeout */
static inline int smn_read_register(u32 reg)
{
	int data, rc;

	rc = amd_smn_read(0, reg, &data);
	if (rc)
		return rc;

	return data;
}
#endif /*_ASM_X86_AMD_NODE_H_*/
+0 −1
Original line number Diff line number Diff line
@@ -73,7 +73,6 @@ static int amd_cache_northbridges(void)
	amd_northbridges.nb = nb;

	for (i = 0; i < amd_northbridges.num; i++) {
		node_to_amd_nb(i)->root = amd_node_get_root(i);
		node_to_amd_nb(i)->misc = amd_node_get_func(i, 3);

		/*
+149 −0
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@
 * Author: Yazen Ghannam <Yazen.Ghannam@amd.com>
 */

#include <linux/debugfs.h>
#include <asm/amd_node.h>

/*
@@ -93,10 +94,14 @@ static struct pci_dev **amd_roots;

/* Protect the PCI config register pairs used for SMN. */
static DEFINE_MUTEX(smn_mutex);
static bool smn_exclusive;

#define SMN_INDEX_OFFSET	0x60
#define SMN_DATA_OFFSET		0x64

#define HSMP_INDEX_OFFSET	0xc4
#define HSMP_DATA_OFFSET	0xc8

/*
 * SMN accesses may fail in ways that are difficult to detect here in the called
 * functions amd_smn_read() and amd_smn_write(). Therefore, callers must do
@@ -146,6 +151,9 @@ static int __amd_smn_rw(u8 i_off, u8 d_off, u16 node, u32 address, u32 *value, b
	if (!root)
		return err;

	if (!smn_exclusive)
		return err;

	guard(mutex)(&smn_mutex);

	err = pci_write_config_dword(root, i_off, address);
@@ -179,6 +187,93 @@ int __must_check amd_smn_write(u16 node, u32 address, u32 value)
}
EXPORT_SYMBOL_GPL(amd_smn_write);

int __must_check amd_smn_hsmp_rdwr(u16 node, u32 address, u32 *value, bool write)
{
	return __amd_smn_rw(HSMP_INDEX_OFFSET, HSMP_DATA_OFFSET, node, address, value, write);
}
EXPORT_SYMBOL_GPL(amd_smn_hsmp_rdwr);

static struct dentry *debugfs_dir;
static u16 debug_node;
static u32 debug_address;

static ssize_t smn_node_write(struct file *file, const char __user *userbuf,
			      size_t count, loff_t *ppos)
{
	u16 node;
	int ret;

	ret = kstrtou16_from_user(userbuf, count, 0, &node);
	if (ret)
		return ret;

	if (node >= amd_num_nodes())
		return -ENODEV;

	debug_node = node;
	return count;
}

static int smn_node_show(struct seq_file *m, void *v)
{
	seq_printf(m, "0x%08x\n", debug_node);
	return 0;
}

static ssize_t smn_address_write(struct file *file, const char __user *userbuf,
				 size_t count, loff_t *ppos)
{
	int ret;

	ret = kstrtouint_from_user(userbuf, count, 0, &debug_address);
	if (ret)
		return ret;

	return count;
}

static int smn_address_show(struct seq_file *m, void *v)
{
	seq_printf(m, "0x%08x\n", debug_address);
	return 0;
}

static int smn_value_show(struct seq_file *m, void *v)
{
	u32 val;
	int ret;

	ret = amd_smn_read(debug_node, debug_address, &val);
	if (ret)
		return ret;

	seq_printf(m, "0x%08x\n", val);
	return 0;
}

static ssize_t smn_value_write(struct file *file, const char __user *userbuf,
			       size_t count, loff_t *ppos)
{
	u32 val;
	int ret;

	ret = kstrtouint_from_user(userbuf, count, 0, &val);
	if (ret)
		return ret;

	add_taint(TAINT_CPU_OUT_OF_SPEC, LOCKDEP_STILL_OK);

	ret = amd_smn_write(debug_node, debug_address, val);
	if (ret)
		return ret;

	return count;
}

DEFINE_SHOW_STORE_ATTRIBUTE(smn_node);
DEFINE_SHOW_STORE_ATTRIBUTE(smn_address);
DEFINE_SHOW_STORE_ATTRIBUTE(smn_value);

static int amd_cache_roots(void)
{
	u16 node, num_nodes = amd_num_nodes();
@@ -193,6 +288,48 @@ static int amd_cache_roots(void)
	return 0;
}

static int reserve_root_config_spaces(void)
{
	struct pci_dev *root = NULL;
	struct pci_bus *bus = NULL;

	while ((bus = pci_find_next_bus(bus))) {
		/* Root device is Device 0 Function 0 on each Primary Bus. */
		root = pci_get_slot(bus, 0);
		if (!root)
			continue;

		if (root->vendor != PCI_VENDOR_ID_AMD &&
		    root->vendor != PCI_VENDOR_ID_HYGON)
			continue;

		pci_dbg(root, "Reserving PCI config space\n");

		/*
		 * There are a few SMN index/data pairs and other registers
		 * that shouldn't be accessed by user space.
		 * So reserve the entire PCI config space for simplicity rather
		 * than covering specific registers piecemeal.
		 */
		if (!pci_request_config_region_exclusive(root, 0, PCI_CFG_SPACE_SIZE, NULL)) {
			pci_err(root, "Failed to reserve config space\n");
			return -EEXIST;
		}
	}

	smn_exclusive = true;
	return 0;
}

static bool enable_dfs;

static int __init amd_smn_enable_dfs(char *str)
{
	enable_dfs = true;
	return 1;
}
__setup("amd_smn_debugfs_enable", amd_smn_enable_dfs);

static int __init amd_smn_init(void)
{
	int err;
@@ -209,6 +346,18 @@ static int __init amd_smn_init(void)
	if (err)
		return err;

	err = reserve_root_config_spaces();
	if (err)
		return err;

	if (enable_dfs) {
		debugfs_dir = debugfs_create_dir("amd_smn", arch_debugfs_dir);

		debugfs_create_file("node",	0600, debugfs_dir, NULL, &smn_node_fops);
		debugfs_create_file("address",	0600, debugfs_dir, NULL, &smn_address_fops);
		debugfs_create_file("value",	0600, debugfs_dir, NULL, &smn_value_fops);
	}

	return 0;
}

Loading