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:
parent
88dbf4a46d
commit
2336e8989a
|
@ -84,6 +84,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;
|
||||
|
@ -121,6 +125,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
|
||||
|
||||
|
||||
|
|
|
@ -27,10 +27,14 @@
|
|||
#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_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. */
|
||||
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) },
|
||||
|
@ -231,6 +235,192 @@ out:
|
|||
}
|
||||
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)
|
||||
{
|
||||
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;
|
||||
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;
|
||||
|
|
Loading…
Reference in New Issue