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

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

Signed-off-by: Pu Wen <puwen@hygon.cn>
Signed-off-by: Jinliang Zheng <alexjlzheng@tencent.com>
Reviewed-by: Bin Lai <robinlai@tencent.com>
Signed-off-by: Jinliang Zheng <alexjlzheng@tencent.com>
Reviewed-by: caelli <caelli@tencent.com>
Signed-off-by: Jianping Liu <frankjpliu@tencent.com>
This commit is contained in:
Pu Wen 2023-06-08 12:36:27 +08:00 committed by Jianping Liu
parent 88dbf4a46d
commit 2336e8989a
2 changed files with 203 additions and 0 deletions

View File

@ -84,6 +84,10 @@ u16 amd_nb_num(void);
bool amd_nb_has_feature(unsigned int feature); bool amd_nb_has_feature(unsigned int feature);
struct amd_northbridge *node_to_amd_nb(int node); 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) static inline u16 amd_pci_dev_to_node_id(struct pci_dev *pdev)
{ {
struct pci_dev *misc; struct pci_dev *misc;
@ -121,6 +125,10 @@ static inline bool amd_gart_present(void)
#define node_to_amd_nb(x) NULL #define node_to_amd_nb(x) NULL
#define amd_gart_present(x) false #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 #endif

View File

@ -27,10 +27,14 @@
#define PCI_DEVICE_ID_AMD_17H_M70H_DF_F4 0x1444 #define PCI_DEVICE_ID_AMD_17H_M70H_DF_F4 0x1444
#define PCI_DEVICE_ID_AMD_19H_DF_F4 0x1654 #define PCI_DEVICE_ID_AMD_19H_DF_F4 0x1654
#define PCI_DEVICE_ID_AMD_19H_M10H_DF_F4 0x14b1 #define PCI_DEVICE_ID_AMD_19H_M10H_DF_F4 0x14b1
#define PCI_DEVICE_ID_HYGON_18H_M04H_DF_F1 0x1491
/* Protect the PCI config register pairs used for SMN and DF indirect access. */ /* Protect the PCI config register pairs used for SMN and DF indirect access. */
static DEFINE_MUTEX(smn_mutex); static DEFINE_MUTEX(smn_mutex);
static u32 *flush_words; static u32 *flush_words;
static u16 nb_num;
static const struct pci_device_id amd_root_ids[] = { static const struct pci_device_id amd_root_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_ROOT) }, { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_ROOT) },
@ -231,6 +235,192 @@ out:
} }
EXPORT_SYMBOL_GPL(amd_df_indirect_read); EXPORT_SYMBOL_GPL(amd_df_indirect_read);
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;
}
int amd_cache_northbridges(void) int amd_cache_northbridges(void)
{ {
const struct pci_device_id *misc_ids = amd_nb_misc_ids; const struct pci_device_id *misc_ids = amd_nb_misc_ids;
@ -250,6 +440,11 @@ int amd_cache_northbridges(void)
root_ids = hygon_root_ids; root_ids = hygon_root_ids;
misc_ids = hygon_nb_misc_ids; misc_ids = hygon_nb_misc_ids;
link_ids = hygon_nb_link_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; misc = NULL;