Commit fc02cdbf authored by Pu Wen's avatar Pu Wen
Browse files

x86/amd_nb: Add northbridge support for Hygon family 18h model 4h

hygon inclusion
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/I8U3JU


CVE: NA

---------------------------

Add dedicated functions to initialize the northbridge for Hygon family
18h model 4h processors.

Signed-off-by: default avatarPu Wen <puwen@hygon.cn>
parent ae71cb88
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -82,6 +82,10 @@ u16 amd_nb_num(void);
bool amd_nb_has_feature(unsigned int feature);
struct amd_northbridge *node_to_amd_nb(int node);

bool hygon_f18h_m4h(void);
u16 hygon_nb_num(void);
int get_df_id(struct pci_dev *misc, u8 *id);

static inline u16 amd_pci_dev_to_node_id(struct pci_dev *pdev)
{
	struct pci_dev *misc;
@@ -119,6 +123,10 @@ static inline bool amd_gart_present(void)
#define node_to_amd_nb(x)	NULL
#define amd_gart_present(x)	false

#define hygon_f18h_m4h		false
#define hygon_nb_num(x)	0
#define get_df_id(x, y)	NULL

#endif


+193 −0
Original line number Diff line number Diff line
@@ -44,10 +44,13 @@
#define PCI_DEVICE_ID_AMD_1AH_M00H_DF_F4	0x12c4
#define PCI_DEVICE_ID_AMD_MI200_DF_F4		0x14d4

#define PCI_DEVICE_ID_HYGON_18H_M04H_DF_F1	0x1491

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

static u32 *flush_words;
static u16 nb_num;

static const struct pci_device_id amd_root_ids[] = {
	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_ROOT) },
@@ -222,6 +225,191 @@ int amd_smn_write(u16 node, u32 address, u32 value)
}
EXPORT_SYMBOL_GPL(amd_smn_write);

bool hygon_f18h_m4h(void)
{
	if (boot_cpu_data.x86_vendor != X86_VENDOR_HYGON)
		return false;

	if (boot_cpu_data.x86 == 0x18 &&
	    boot_cpu_data.x86_model >= 0x4 &&
	    boot_cpu_data.x86_model <= 0xf)
		return true;

	return false;
}
EXPORT_SYMBOL_GPL(hygon_f18h_m4h);

u16 hygon_nb_num(void)
{
	return nb_num;
}
EXPORT_SYMBOL_GPL(hygon_nb_num);

static int get_df1_register(struct pci_dev *misc, int offset, u32 *value)
{
	struct pci_dev *df_f1 = NULL;
	int err;

	while ((df_f1 = pci_get_device(misc->vendor,
			PCI_DEVICE_ID_HYGON_18H_M04H_DF_F1, df_f1)))
		if (pci_domain_nr(df_f1->bus) == pci_domain_nr(misc->bus) &&
		    df_f1->bus->number == misc->bus->number &&
		    PCI_SLOT(df_f1->devfn) == PCI_SLOT(misc->devfn))
			break;

	if (!df_f1) {
		pr_warn("Error getting DF F1 device.\n");
		return -ENODEV;
	}

	err = pci_read_config_dword(df_f1, offset, value);
	if (err)
		pr_warn("Error reading DF F1 register.\n");

	return err;
}

int get_df_id(struct pci_dev *misc, u8 *id)
{
	u32 value;
	int ret;

	/* F1x200[23:20]: DF ID */
	ret = get_df1_register(misc, 0x200, &value);
	*id = (value >> 20) & 0xf;

	return ret;
}
EXPORT_SYMBOL_GPL(get_df_id);

static u8 get_socket_num(struct pci_dev *misc)
{
	u32 value;
	int ret;

	/* F1x200[7:0]: Which socket is present. */
	ret = get_df1_register(misc, 0x200, &value);

	return ret ? 0 : hweight8(value & 0xff);
}

static int northbridge_init_f18h_m4h(const struct pci_device_id *root_ids,
					const struct pci_device_id *misc_ids,
					const struct pci_device_id *link_ids)
{
	struct pci_dev *root, *misc, *link;
	struct pci_dev *root_first = NULL;
	struct amd_northbridge *nb;
	u16 roots_per_socket = 0;
	u16 miscs_per_socket = 0;
	u16 socket_num = 0;
	u16 root_count = 0;
	u16 misc_count = 0;
	int err = -ENODEV;
	u8 i, j, m, n;
	u8 id;

	pr_info("Hygon Fam%xh Model%xh NB driver.\n",
		boot_cpu_data.x86, boot_cpu_data.x86_model);

	misc = next_northbridge(NULL, misc_ids);
	if (misc != NULL) {
		socket_num = get_socket_num(misc);
		pr_info("Socket number: %d\n", socket_num);
		if (!socket_num) {
			err = -ENODEV;
			goto ret;
		}
	} else {
		err = -ENODEV;
		goto ret;
	}

	misc = NULL;
	while ((misc = next_northbridge(misc, misc_ids)) != NULL)
		misc_count++;

	root = NULL;
	while ((root = next_northbridge(root, root_ids)) != NULL)
		root_count++;

	if (!root_count || !misc_count) {
		err = -ENODEV;
		goto ret;
	}

	/*
	 * There should be _exactly_ N roots for each DF/SMN
	 * interface, and M DF/SMN interfaces in one socket.
	 */
	roots_per_socket = root_count / socket_num;
	miscs_per_socket = misc_count / socket_num;

	if (!roots_per_socket || !miscs_per_socket) {
		err = -ENODEV;
		goto ret;
	}

	nb = kcalloc(misc_count, sizeof(struct amd_northbridge), GFP_KERNEL);
	if (!nb) {
		err = -ENOMEM;
		goto ret;
	}

	amd_northbridges.nb = nb;
	amd_northbridges.num = misc_count;

	link = misc = root = NULL;
	j = m = n = 0;
	for (i = 0; i < amd_northbridges.num; i++) {
		misc = next_northbridge(misc, misc_ids);
		link = next_northbridge(link, link_ids);

		/* Only save the first PCI root device for each socket. */
		if (!(i % miscs_per_socket)) {
			root_first = next_northbridge(root, root_ids);
			root = root_first;
			j = 1;
		}

		if (get_df_id(misc, &id)) {
			err = -ENODEV;
			goto err;
		}
		pr_info("DF ID: %d\n", id);

		if (id < 4) {
			/* Add the devices with id<4 from the tail. */
			node_to_amd_nb(misc_count - m - 1)->misc = misc;
			node_to_amd_nb(misc_count - m - 1)->link = link;
			node_to_amd_nb(misc_count - m - 1)->root = root_first;
			m++;
		} else {
			node_to_amd_nb(n)->misc = misc;
			node_to_amd_nb(n)->link = link;
			node_to_amd_nb(n)->root = root_first;
			n++;
		}

		/* Skip the redundant PCI root devices per socket. */
		while (j < roots_per_socket) {
			root = next_northbridge(root, root_ids);
			j++;
		}
	}
	nb_num = n;

	return 0;

err:
	kfree(nb);
	amd_northbridges.nb = NULL;

ret:
	pr_err("Hygon Fam%xh Model%xh northbridge init failed(%d)!\n",
		boot_cpu_data.x86, boot_cpu_data.x86_model, err);
	return err;
}

static int amd_cache_northbridges(void)
{
@@ -242,6 +430,11 @@ static int amd_cache_northbridges(void)
		root_ids = hygon_root_ids;
		misc_ids = hygon_nb_misc_ids;
		link_ids = hygon_nb_link_ids;

		if (boot_cpu_data.x86_model >= 0x4 &&
		    boot_cpu_data.x86_model <= 0xf)
			return northbridge_init_f18h_m4h(root_ids,
					misc_ids, link_ids);
	}

	misc = NULL;