mirror of
https://github.com/torvalds/linux.git
synced 2024-12-11 13:41:55 +00:00
eb55307e67
- Limit the hardcoded topology quirk for Hygon CPUs to those which have a model ID less than 4. The newer models have the topology CPUID leaf 0xB correctly implemented and are not affected. - Make SMT control more robust against enumeration failures SMT control was added to allow controlling SMT at boottime or runtime. The primary purpose was to provide a simple mechanism to disable SMT in the light of speculation attack vectors. It turned out that the code is sensible to enumeration failures and worked only by chance for XEN/PV. XEN/PV has no real APIC enumeration which means the primary thread mask is not set up correctly. By chance a XEN/PV boot ends up with smp_num_siblings == 2, which makes the hotplug control stay at its default value "enabled". So the mask is never evaluated. The ongoing rework of the topology evaluation caused XEN/PV to end up with smp_num_siblings == 1, which sets the SMT control to "not supported" and the empty primary thread mask causes the hotplug core to deny the bringup of the APS. Make the decision logic more robust and take 'not supported' and 'not implemented' into account for the decision whether a CPU should be booted or not. - Fake primary thread mask for XEN/PV Pretend that all XEN/PV vCPUs are primary threads, which makes the usage of the primary thread mask valid on XEN/PV. That is consistent with because all of the topology information on XEN/PV is fake or even non-existent. - Encapsulate topology information in cpuinfo_x86 Move the randomly scattered topology data into a separate data structure for readability and as a preparatory step for the topology evaluation overhaul. - Consolidate APIC ID data type to u32 It's fixed width hardware data and not randomly u16, int, unsigned long or whatever developers decided to use. - Cure the abuse of cpuinfo for persisting logical IDs. Per CPU cpuinfo is used to persist the logical package and die IDs. That's really not the right place simply because cpuinfo is subject to be reinitialized when a CPU goes through an offline/online cycle. Use separate per CPU data for the persisting to enable the further topology management rework. It will be removed once the new topology management is in place. - Provide a debug interface for inspecting topology information Useful in general and extremly helpful for validating the topology management rework in terms of correctness or "bug" compatibility. -----BEGIN PGP SIGNATURE----- iQJHBAABCgAxFiEEQp8+kY+LLUocC4bMphj1TA10mKEFAmU+yX0THHRnbHhAbGlu dXRyb25peC5kZQAKCRCmGPVMDXSYoROUD/4vlvKEcpm9rbI5DzLcaq4DFHKbyEZF cQtzuOSM/9vTc9DHnuoNNLl9TWSYxiVYnejf3E21evfsqspYlzbTH8bId9XBCUid 6B68AJW842M2erNuwj0b0HwF1z++zpDmBDyhGOty/KQhoM8pYOHMvntAmbzJbuso Dgx6BLVFcboTy6RwlfRa0EE8f9W5V+JbmG/VBDpdyCInal7VrudoVFZmWQnPIft7 zwOJpAoehkp8OKq7geKDf79yWxu9a1sNPd62HtaVEvfHwehHqE6OaMLss1us+0vT SJ/D6gmRQBOwcXaZL0wL1dG7Km9Et4AisOvzhXGvTa5b2D5oljVoqJ7V7FTf5g3u y3aqWbeUJzERUbeJt1HoGVAKyA4GtZOvg+TNIysf6F1Z4khl9alfa9jiqjj4g1au zgItq/ZMBEBmJ7X4FxQUEUVBG2CDsEidyNBDRcimWQUDfBakV/iCs0suD8uu8ZOD K5jMx8Hi2+xFx7r1YqsfsyMBYOf/zUZw65RbNe+kI992JbJ9nhcODbnbo5MlAsyv vcqlK5FwXgZ4YAC8dZHU/tyTiqAW7oaOSkqKwTP5gcyNEqsjQHV//q6v+uqtjfYn 1C4oUsRHT2vJiV9ktNJTA4GQHIYF4geGgpG8Ih2SjXsSzdGtUd3DtX1iq0YiLEOk eHhYsnniqsYB5g== =xrz8 -----END PGP SIGNATURE----- Merge tag 'x86-core-2023-10-29-v2' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip Pull x86 core updates from Thomas Gleixner: - Limit the hardcoded topology quirk for Hygon CPUs to those which have a model ID less than 4. The newer models have the topology CPUID leaf 0xB correctly implemented and are not affected. - Make SMT control more robust against enumeration failures SMT control was added to allow controlling SMT at boottime or runtime. The primary purpose was to provide a simple mechanism to disable SMT in the light of speculation attack vectors. It turned out that the code is sensible to enumeration failures and worked only by chance for XEN/PV. XEN/PV has no real APIC enumeration which means the primary thread mask is not set up correctly. By chance a XEN/PV boot ends up with smp_num_siblings == 2, which makes the hotplug control stay at its default value "enabled". So the mask is never evaluated. The ongoing rework of the topology evaluation caused XEN/PV to end up with smp_num_siblings == 1, which sets the SMT control to "not supported" and the empty primary thread mask causes the hotplug core to deny the bringup of the APS. Make the decision logic more robust and take 'not supported' and 'not implemented' into account for the decision whether a CPU should be booted or not. - Fake primary thread mask for XEN/PV Pretend that all XEN/PV vCPUs are primary threads, which makes the usage of the primary thread mask valid on XEN/PV. That is consistent with because all of the topology information on XEN/PV is fake or even non-existent. - Encapsulate topology information in cpuinfo_x86 Move the randomly scattered topology data into a separate data structure for readability and as a preparatory step for the topology evaluation overhaul. - Consolidate APIC ID data type to u32 It's fixed width hardware data and not randomly u16, int, unsigned long or whatever developers decided to use. - Cure the abuse of cpuinfo for persisting logical IDs. Per CPU cpuinfo is used to persist the logical package and die IDs. That's really not the right place simply because cpuinfo is subject to be reinitialized when a CPU goes through an offline/online cycle. Use separate per CPU data for the persisting to enable the further topology management rework. It will be removed once the new topology management is in place. - Provide a debug interface for inspecting topology information Useful in general and extremly helpful for validating the topology management rework in terms of correctness or "bug" compatibility. * tag 'x86-core-2023-10-29-v2' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (23 commits) x86/apic, x86/hyperv: Use u32 in hv_snp_boot_ap() too x86/cpu: Provide debug interface x86/cpu/topology: Cure the abuse of cpuinfo for persisting logical ids x86/apic: Use u32 for wakeup_secondary_cpu[_64]() x86/apic: Use u32 for [gs]et_apic_id() x86/apic: Use u32 for phys_pkg_id() x86/apic: Use u32 for cpu_present_to_apicid() x86/apic: Use u32 for check_apicid_used() x86/apic: Use u32 for APIC IDs in global data x86/apic: Use BAD_APICID consistently x86/cpu: Move cpu_l[l2]c_id into topology info x86/cpu: Move logical package and die IDs into topology info x86/cpu: Remove pointless evaluation of x86_coreid_bits x86/cpu: Move cu_id into topology info x86/cpu: Move cpu_core_id into topology info hwmon: (fam15h_power) Use topology_core_id() scsi: lpfc: Use topology_core_id() x86/cpu: Move cpu_die_id into topology info x86/cpu: Move phys_proc_id into topology info x86/cpu: Encapsulate topology information in cpuinfo_x86 ...
544 lines
16 KiB
C
544 lines
16 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Shared support code for AMD K8 northbridges and derivatives.
|
|
* Copyright 2006 Andi Kleen, SUSE Labs.
|
|
*/
|
|
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
|
|
#include <linux/types.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/init.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/export.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/pci_ids.h>
|
|
#include <asm/amd_nb.h>
|
|
|
|
#define PCI_DEVICE_ID_AMD_17H_ROOT 0x1450
|
|
#define PCI_DEVICE_ID_AMD_17H_M10H_ROOT 0x15d0
|
|
#define PCI_DEVICE_ID_AMD_17H_M30H_ROOT 0x1480
|
|
#define PCI_DEVICE_ID_AMD_17H_M60H_ROOT 0x1630
|
|
#define PCI_DEVICE_ID_AMD_17H_MA0H_ROOT 0x14b5
|
|
#define PCI_DEVICE_ID_AMD_19H_M10H_ROOT 0x14a4
|
|
#define PCI_DEVICE_ID_AMD_19H_M40H_ROOT 0x14b5
|
|
#define PCI_DEVICE_ID_AMD_19H_M60H_ROOT 0x14d8
|
|
#define PCI_DEVICE_ID_AMD_19H_M70H_ROOT 0x14e8
|
|
#define PCI_DEVICE_ID_AMD_1AH_M00H_ROOT 0x153a
|
|
#define PCI_DEVICE_ID_AMD_1AH_M20H_ROOT 0x1507
|
|
#define PCI_DEVICE_ID_AMD_MI200_ROOT 0x14bb
|
|
#define PCI_DEVICE_ID_AMD_MI300_ROOT 0x14f8
|
|
|
|
#define PCI_DEVICE_ID_AMD_17H_DF_F4 0x1464
|
|
#define PCI_DEVICE_ID_AMD_17H_M10H_DF_F4 0x15ec
|
|
#define PCI_DEVICE_ID_AMD_17H_M30H_DF_F4 0x1494
|
|
#define PCI_DEVICE_ID_AMD_17H_M60H_DF_F4 0x144c
|
|
#define PCI_DEVICE_ID_AMD_17H_M70H_DF_F4 0x1444
|
|
#define PCI_DEVICE_ID_AMD_17H_MA0H_DF_F4 0x1728
|
|
#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_M40H_DF_F4 0x167d
|
|
#define PCI_DEVICE_ID_AMD_19H_M50H_DF_F4 0x166e
|
|
#define PCI_DEVICE_ID_AMD_19H_M60H_DF_F4 0x14e4
|
|
#define PCI_DEVICE_ID_AMD_19H_M70H_DF_F4 0x14f4
|
|
#define PCI_DEVICE_ID_AMD_19H_M78H_DF_F4 0x12fc
|
|
#define PCI_DEVICE_ID_AMD_1AH_M00H_DF_F4 0x12c4
|
|
#define PCI_DEVICE_ID_AMD_MI200_DF_F4 0x14d4
|
|
#define PCI_DEVICE_ID_AMD_MI300_DF_F4 0x152c
|
|
|
|
/* Protect the PCI config register pairs used for SMN. */
|
|
static DEFINE_MUTEX(smn_mutex);
|
|
|
|
static u32 *flush_words;
|
|
|
|
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_M10H_ROOT) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_M30H_ROOT) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_M60H_ROOT) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_MA0H_ROOT) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_19H_M10H_ROOT) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_19H_M40H_ROOT) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_19H_M60H_ROOT) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_19H_M70H_ROOT) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_1AH_M00H_ROOT) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_1AH_M20H_ROOT) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_MI200_ROOT) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_MI300_ROOT) },
|
|
{}
|
|
};
|
|
|
|
#define PCI_DEVICE_ID_AMD_CNB17H_F4 0x1704
|
|
|
|
static const struct pci_device_id amd_nb_misc_ids[] = {
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_K8_NB_MISC) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_10H_NB_MISC) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_NB_F3) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_M10H_F3) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_M30H_NB_F3) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_M60H_NB_F3) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_16H_NB_F3) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_16H_M30H_NB_F3) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_DF_F3) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_M10H_DF_F3) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_M30H_DF_F3) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_M60H_DF_F3) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_MA0H_DF_F3) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CNB17H_F3) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_M70H_DF_F3) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_19H_DF_F3) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_19H_M10H_DF_F3) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_19H_M40H_DF_F3) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_19H_M50H_DF_F3) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_19H_M60H_DF_F3) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_19H_M70H_DF_F3) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_19H_M78H_DF_F3) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_1AH_M00H_DF_F3) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_1AH_M20H_DF_F3) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_MI200_DF_F3) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_MI300_DF_F3) },
|
|
{}
|
|
};
|
|
|
|
static const struct pci_device_id amd_nb_link_ids[] = {
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_NB_F4) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_M30H_NB_F4) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_M60H_NB_F4) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_16H_NB_F4) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_16H_M30H_NB_F4) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_DF_F4) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_M10H_DF_F4) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_M30H_DF_F4) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_M60H_DF_F4) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_M70H_DF_F4) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_MA0H_DF_F4) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_19H_DF_F4) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_19H_M10H_DF_F4) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_19H_M40H_DF_F4) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_19H_M50H_DF_F4) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_19H_M60H_DF_F4) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_19H_M70H_DF_F4) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_19H_M78H_DF_F4) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CNB17H_F4) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_1AH_M00H_DF_F4) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_MI200_DF_F4) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_MI300_DF_F4) },
|
|
{}
|
|
};
|
|
|
|
static const struct pci_device_id hygon_root_ids[] = {
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_HYGON, PCI_DEVICE_ID_AMD_17H_ROOT) },
|
|
{}
|
|
};
|
|
|
|
static const struct pci_device_id hygon_nb_misc_ids[] = {
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_HYGON, PCI_DEVICE_ID_AMD_17H_DF_F3) },
|
|
{}
|
|
};
|
|
|
|
static const struct pci_device_id hygon_nb_link_ids[] = {
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_HYGON, PCI_DEVICE_ID_AMD_17H_DF_F4) },
|
|
{}
|
|
};
|
|
|
|
const struct amd_nb_bus_dev_range amd_nb_bus_dev_ranges[] __initconst = {
|
|
{ 0x00, 0x18, 0x20 },
|
|
{ 0xff, 0x00, 0x20 },
|
|
{ 0xfe, 0x00, 0x20 },
|
|
{ }
|
|
};
|
|
|
|
static struct amd_northbridge_info amd_northbridges;
|
|
|
|
u16 amd_nb_num(void)
|
|
{
|
|
return amd_northbridges.num;
|
|
}
|
|
EXPORT_SYMBOL_GPL(amd_nb_num);
|
|
|
|
bool amd_nb_has_feature(unsigned int feature)
|
|
{
|
|
return ((amd_northbridges.flags & feature) == feature);
|
|
}
|
|
EXPORT_SYMBOL_GPL(amd_nb_has_feature);
|
|
|
|
struct amd_northbridge *node_to_amd_nb(int node)
|
|
{
|
|
return (node < amd_northbridges.num) ? &amd_northbridges.nb[node] : NULL;
|
|
}
|
|
EXPORT_SYMBOL_GPL(node_to_amd_nb);
|
|
|
|
static struct pci_dev *next_northbridge(struct pci_dev *dev,
|
|
const struct pci_device_id *ids)
|
|
{
|
|
do {
|
|
dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev);
|
|
if (!dev)
|
|
break;
|
|
} while (!pci_match_id(ids, dev));
|
|
return dev;
|
|
}
|
|
|
|
static int __amd_smn_rw(u16 node, u32 address, u32 *value, bool write)
|
|
{
|
|
struct pci_dev *root;
|
|
int err = -ENODEV;
|
|
|
|
if (node >= amd_northbridges.num)
|
|
goto out;
|
|
|
|
root = node_to_amd_nb(node)->root;
|
|
if (!root)
|
|
goto out;
|
|
|
|
mutex_lock(&smn_mutex);
|
|
|
|
err = pci_write_config_dword(root, 0x60, address);
|
|
if (err) {
|
|
pr_warn("Error programming SMN address 0x%x.\n", address);
|
|
goto out_unlock;
|
|
}
|
|
|
|
err = (write ? pci_write_config_dword(root, 0x64, *value)
|
|
: pci_read_config_dword(root, 0x64, value));
|
|
if (err)
|
|
pr_warn("Error %s SMN address 0x%x.\n",
|
|
(write ? "writing to" : "reading from"), address);
|
|
|
|
out_unlock:
|
|
mutex_unlock(&smn_mutex);
|
|
|
|
out:
|
|
return err;
|
|
}
|
|
|
|
int amd_smn_read(u16 node, u32 address, u32 *value)
|
|
{
|
|
return __amd_smn_rw(node, address, value, false);
|
|
}
|
|
EXPORT_SYMBOL_GPL(amd_smn_read);
|
|
|
|
int amd_smn_write(u16 node, u32 address, u32 value)
|
|
{
|
|
return __amd_smn_rw(node, address, &value, true);
|
|
}
|
|
EXPORT_SYMBOL_GPL(amd_smn_write);
|
|
|
|
|
|
static int amd_cache_northbridges(void)
|
|
{
|
|
const struct pci_device_id *misc_ids = amd_nb_misc_ids;
|
|
const struct pci_device_id *link_ids = amd_nb_link_ids;
|
|
const struct pci_device_id *root_ids = amd_root_ids;
|
|
struct pci_dev *root, *misc, *link;
|
|
struct amd_northbridge *nb;
|
|
u16 roots_per_misc = 0;
|
|
u16 misc_count = 0;
|
|
u16 root_count = 0;
|
|
u16 i, j;
|
|
|
|
if (amd_northbridges.num)
|
|
return 0;
|
|
|
|
if (boot_cpu_data.x86_vendor == X86_VENDOR_HYGON) {
|
|
root_ids = hygon_root_ids;
|
|
misc_ids = hygon_nb_misc_ids;
|
|
link_ids = hygon_nb_link_ids;
|
|
}
|
|
|
|
misc = NULL;
|
|
while ((misc = next_northbridge(misc, misc_ids)))
|
|
misc_count++;
|
|
|
|
if (!misc_count)
|
|
return -ENODEV;
|
|
|
|
root = NULL;
|
|
while ((root = next_northbridge(root, root_ids)))
|
|
root_count++;
|
|
|
|
if (root_count) {
|
|
roots_per_misc = root_count / misc_count;
|
|
|
|
/*
|
|
* There should be _exactly_ N roots for each DF/SMN
|
|
* interface.
|
|
*/
|
|
if (!roots_per_misc || (root_count % roots_per_misc)) {
|
|
pr_info("Unsupported AMD DF/PCI configuration found\n");
|
|
return -ENODEV;
|
|
}
|
|
}
|
|
|
|
nb = kcalloc(misc_count, sizeof(struct amd_northbridge), GFP_KERNEL);
|
|
if (!nb)
|
|
return -ENOMEM;
|
|
|
|
amd_northbridges.nb = nb;
|
|
amd_northbridges.num = misc_count;
|
|
|
|
link = misc = root = NULL;
|
|
for (i = 0; i < amd_northbridges.num; i++) {
|
|
node_to_amd_nb(i)->root = root =
|
|
next_northbridge(root, root_ids);
|
|
node_to_amd_nb(i)->misc = misc =
|
|
next_northbridge(misc, misc_ids);
|
|
node_to_amd_nb(i)->link = link =
|
|
next_northbridge(link, link_ids);
|
|
|
|
/*
|
|
* If there are more PCI root devices than data fabric/
|
|
* system management network interfaces, then the (N)
|
|
* PCI roots per DF/SMN interface are functionally the
|
|
* same (for DF/SMN access) and N-1 are redundant. N-1
|
|
* PCI roots should be skipped per DF/SMN interface so
|
|
* the following DF/SMN interfaces get mapped to
|
|
* correct PCI roots.
|
|
*/
|
|
for (j = 1; j < roots_per_misc; j++)
|
|
root = next_northbridge(root, root_ids);
|
|
}
|
|
|
|
if (amd_gart_present())
|
|
amd_northbridges.flags |= AMD_NB_GART;
|
|
|
|
/*
|
|
* Check for L3 cache presence.
|
|
*/
|
|
if (!cpuid_edx(0x80000006))
|
|
return 0;
|
|
|
|
/*
|
|
* Some CPU families support L3 Cache Index Disable. There are some
|
|
* limitations because of E382 and E388 on family 0x10.
|
|
*/
|
|
if (boot_cpu_data.x86 == 0x10 &&
|
|
boot_cpu_data.x86_model >= 0x8 &&
|
|
(boot_cpu_data.x86_model > 0x9 ||
|
|
boot_cpu_data.x86_stepping >= 0x1))
|
|
amd_northbridges.flags |= AMD_NB_L3_INDEX_DISABLE;
|
|
|
|
if (boot_cpu_data.x86 == 0x15)
|
|
amd_northbridges.flags |= AMD_NB_L3_INDEX_DISABLE;
|
|
|
|
/* L3 cache partitioning is supported on family 0x15 */
|
|
if (boot_cpu_data.x86 == 0x15)
|
|
amd_northbridges.flags |= AMD_NB_L3_PARTITIONING;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Ignores subdevice/subvendor but as far as I can figure out
|
|
* they're useless anyways
|
|
*/
|
|
bool __init early_is_amd_nb(u32 device)
|
|
{
|
|
const struct pci_device_id *misc_ids = amd_nb_misc_ids;
|
|
const struct pci_device_id *id;
|
|
u32 vendor = device & 0xffff;
|
|
|
|
if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD &&
|
|
boot_cpu_data.x86_vendor != X86_VENDOR_HYGON)
|
|
return false;
|
|
|
|
if (boot_cpu_data.x86_vendor == X86_VENDOR_HYGON)
|
|
misc_ids = hygon_nb_misc_ids;
|
|
|
|
device >>= 16;
|
|
for (id = misc_ids; id->vendor; id++)
|
|
if (vendor == id->vendor && device == id->device)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
struct resource *amd_get_mmconfig_range(struct resource *res)
|
|
{
|
|
u32 address;
|
|
u64 base, msr;
|
|
unsigned int segn_busn_bits;
|
|
|
|
if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD &&
|
|
boot_cpu_data.x86_vendor != X86_VENDOR_HYGON)
|
|
return NULL;
|
|
|
|
/* assume all cpus from fam10h have mmconfig */
|
|
if (boot_cpu_data.x86 < 0x10)
|
|
return NULL;
|
|
|
|
address = MSR_FAM10H_MMIO_CONF_BASE;
|
|
rdmsrl(address, msr);
|
|
|
|
/* mmconfig is not enabled */
|
|
if (!(msr & FAM10H_MMIO_CONF_ENABLE))
|
|
return NULL;
|
|
|
|
base = msr & (FAM10H_MMIO_CONF_BASE_MASK<<FAM10H_MMIO_CONF_BASE_SHIFT);
|
|
|
|
segn_busn_bits = (msr >> FAM10H_MMIO_CONF_BUSRANGE_SHIFT) &
|
|
FAM10H_MMIO_CONF_BUSRANGE_MASK;
|
|
|
|
res->flags = IORESOURCE_MEM;
|
|
res->start = base;
|
|
res->end = base + (1ULL<<(segn_busn_bits + 20)) - 1;
|
|
return res;
|
|
}
|
|
|
|
int amd_get_subcaches(int cpu)
|
|
{
|
|
struct pci_dev *link = node_to_amd_nb(topology_die_id(cpu))->link;
|
|
unsigned int mask;
|
|
|
|
if (!amd_nb_has_feature(AMD_NB_L3_PARTITIONING))
|
|
return 0;
|
|
|
|
pci_read_config_dword(link, 0x1d4, &mask);
|
|
|
|
return (mask >> (4 * cpu_data(cpu).topo.core_id)) & 0xf;
|
|
}
|
|
|
|
int amd_set_subcaches(int cpu, unsigned long mask)
|
|
{
|
|
static unsigned int reset, ban;
|
|
struct amd_northbridge *nb = node_to_amd_nb(topology_die_id(cpu));
|
|
unsigned int reg;
|
|
int cuid;
|
|
|
|
if (!amd_nb_has_feature(AMD_NB_L3_PARTITIONING) || mask > 0xf)
|
|
return -EINVAL;
|
|
|
|
/* if necessary, collect reset state of L3 partitioning and BAN mode */
|
|
if (reset == 0) {
|
|
pci_read_config_dword(nb->link, 0x1d4, &reset);
|
|
pci_read_config_dword(nb->misc, 0x1b8, &ban);
|
|
ban &= 0x180000;
|
|
}
|
|
|
|
/* deactivate BAN mode if any subcaches are to be disabled */
|
|
if (mask != 0xf) {
|
|
pci_read_config_dword(nb->misc, 0x1b8, ®);
|
|
pci_write_config_dword(nb->misc, 0x1b8, reg & ~0x180000);
|
|
}
|
|
|
|
cuid = cpu_data(cpu).topo.core_id;
|
|
mask <<= 4 * cuid;
|
|
mask |= (0xf ^ (1 << cuid)) << 26;
|
|
|
|
pci_write_config_dword(nb->link, 0x1d4, mask);
|
|
|
|
/* reset BAN mode if L3 partitioning returned to reset state */
|
|
pci_read_config_dword(nb->link, 0x1d4, ®);
|
|
if (reg == reset) {
|
|
pci_read_config_dword(nb->misc, 0x1b8, ®);
|
|
reg &= ~0x180000;
|
|
pci_write_config_dword(nb->misc, 0x1b8, reg | ban);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void amd_cache_gart(void)
|
|
{
|
|
u16 i;
|
|
|
|
if (!amd_nb_has_feature(AMD_NB_GART))
|
|
return;
|
|
|
|
flush_words = kmalloc_array(amd_northbridges.num, sizeof(u32), GFP_KERNEL);
|
|
if (!flush_words) {
|
|
amd_northbridges.flags &= ~AMD_NB_GART;
|
|
pr_notice("Cannot initialize GART flush words, GART support disabled\n");
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i != amd_northbridges.num; i++)
|
|
pci_read_config_dword(node_to_amd_nb(i)->misc, 0x9c, &flush_words[i]);
|
|
}
|
|
|
|
void amd_flush_garts(void)
|
|
{
|
|
int flushed, i;
|
|
unsigned long flags;
|
|
static DEFINE_SPINLOCK(gart_lock);
|
|
|
|
if (!amd_nb_has_feature(AMD_NB_GART))
|
|
return;
|
|
|
|
/*
|
|
* Avoid races between AGP and IOMMU. In theory it's not needed
|
|
* but I'm not sure if the hardware won't lose flush requests
|
|
* when another is pending. This whole thing is so expensive anyways
|
|
* that it doesn't matter to serialize more. -AK
|
|
*/
|
|
spin_lock_irqsave(&gart_lock, flags);
|
|
flushed = 0;
|
|
for (i = 0; i < amd_northbridges.num; i++) {
|
|
pci_write_config_dword(node_to_amd_nb(i)->misc, 0x9c,
|
|
flush_words[i] | 1);
|
|
flushed++;
|
|
}
|
|
for (i = 0; i < amd_northbridges.num; i++) {
|
|
u32 w;
|
|
/* Make sure the hardware actually executed the flush*/
|
|
for (;;) {
|
|
pci_read_config_dword(node_to_amd_nb(i)->misc,
|
|
0x9c, &w);
|
|
if (!(w & 1))
|
|
break;
|
|
cpu_relax();
|
|
}
|
|
}
|
|
spin_unlock_irqrestore(&gart_lock, flags);
|
|
if (!flushed)
|
|
pr_notice("nothing to flush?\n");
|
|
}
|
|
EXPORT_SYMBOL_GPL(amd_flush_garts);
|
|
|
|
static void __fix_erratum_688(void *info)
|
|
{
|
|
#define MSR_AMD64_IC_CFG 0xC0011021
|
|
|
|
msr_set_bit(MSR_AMD64_IC_CFG, 3);
|
|
msr_set_bit(MSR_AMD64_IC_CFG, 14);
|
|
}
|
|
|
|
/* Apply erratum 688 fix so machines without a BIOS fix work. */
|
|
static __init void fix_erratum_688(void)
|
|
{
|
|
struct pci_dev *F4;
|
|
u32 val;
|
|
|
|
if (boot_cpu_data.x86 != 0x14)
|
|
return;
|
|
|
|
if (!amd_northbridges.num)
|
|
return;
|
|
|
|
F4 = node_to_amd_nb(0)->link;
|
|
if (!F4)
|
|
return;
|
|
|
|
if (pci_read_config_dword(F4, 0x164, &val))
|
|
return;
|
|
|
|
if (val & BIT(2))
|
|
return;
|
|
|
|
on_each_cpu(__fix_erratum_688, NULL, 0);
|
|
|
|
pr_info("x86/cpu/AMD: CPU erratum 688 worked around\n");
|
|
}
|
|
|
|
static __init int init_amd_nbs(void)
|
|
{
|
|
amd_cache_northbridges();
|
|
amd_cache_gart();
|
|
|
|
fix_erratum_688();
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* This has to go after the PCI subsystem */
|
|
fs_initcall(init_amd_nbs);
|