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);
|
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
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue