diff options
Diffstat (limited to 'drivers/platform/x86/intel')
-rw-r--r-- | drivers/platform/x86/intel/pmt/class.c | 23 | ||||
-rw-r--r-- | drivers/platform/x86/intel/pmt/telemetry.c | 18 | ||||
-rw-r--r-- | drivers/platform/x86/intel/speed_select_if/isst_if_common.c | 39 | ||||
-rw-r--r-- | drivers/platform/x86/intel/vsec.c | 130 | ||||
-rw-r--r-- | drivers/platform/x86/intel/vsec.h | 11 |
5 files changed, 168 insertions, 53 deletions
diff --git a/drivers/platform/x86/intel/pmt/class.c b/drivers/platform/x86/intel/pmt/class.c index 1c9e3f3ea41c..53d7fd2943b4 100644 --- a/drivers/platform/x86/intel/pmt/class.c +++ b/drivers/platform/x86/intel/pmt/class.c @@ -20,25 +20,16 @@ #define PMT_XA_MAX INT_MAX #define PMT_XA_LIMIT XA_LIMIT(PMT_XA_START, PMT_XA_MAX) -/* - * Early implementations of PMT on client platforms have some - * differences from the server platforms (which use the Out Of Band - * Management Services Module OOBMSM). This list tracks those - * platforms as needed to handle those differences. Newer client - * platforms are expected to be fully compatible with server. - */ -static const struct pci_device_id pmt_telem_early_client_pci_ids[] = { - { PCI_VDEVICE(INTEL, 0x467d) }, /* ADL */ - { PCI_VDEVICE(INTEL, 0x490e) }, /* DG1 */ - { PCI_VDEVICE(INTEL, 0x9a0d) }, /* TGL */ - { } -}; - bool intel_pmt_is_early_client_hw(struct device *dev) { - struct pci_dev *parent = to_pci_dev(dev->parent); + struct intel_vsec_device *ivdev = dev_to_ivdev(dev); - return !!pci_match_id(pmt_telem_early_client_pci_ids, parent); + /* + * Early implementations of PMT on client platforms have some + * differences from the server platforms (which use the Out Of Band + * Management Services Module OOBMSM). + */ + return !!(ivdev->info->quirks & VSEC_QUIRK_EARLY_HW); } EXPORT_SYMBOL_GPL(intel_pmt_is_early_client_hw); diff --git a/drivers/platform/x86/intel/pmt/telemetry.c b/drivers/platform/x86/intel/pmt/telemetry.c index f73ecfd4a309..5e4009c05ecf 100644 --- a/drivers/platform/x86/intel/pmt/telemetry.c +++ b/drivers/platform/x86/intel/pmt/telemetry.c @@ -23,12 +23,19 @@ #define TELEM_GUID_OFFSET 0x4 #define TELEM_BASE_OFFSET 0x8 #define TELEM_ACCESS(v) ((v) & GENMASK(3, 0)) +#define TELEM_TYPE(v) (((v) & GENMASK(7, 4)) >> 4) /* size is in bytes */ #define TELEM_SIZE(v) (((v) & GENMASK(27, 12)) >> 10) /* Used by client hardware to identify a fixed telemetry entry*/ #define TELEM_CLIENT_FIXED_BLOCK_GUID 0x10000000 +enum telem_type { + TELEM_TYPE_PUNIT = 0, + TELEM_TYPE_CRASHLOG, + TELEM_TYPE_PUNIT_FIXED, +}; + struct pmt_telem_priv { int num_entries; struct intel_pmt_entry entry[]; @@ -39,10 +46,15 @@ static bool pmt_telem_region_overlaps(struct intel_pmt_entry *entry, { u32 guid = readl(entry->disc_table + TELEM_GUID_OFFSET); - if (guid != TELEM_CLIENT_FIXED_BLOCK_GUID) - return false; + if (intel_pmt_is_early_client_hw(dev)) { + u32 type = TELEM_TYPE(readl(entry->disc_table)); + + if ((type == TELEM_TYPE_PUNIT_FIXED) || + (guid == TELEM_CLIENT_FIXED_BLOCK_GUID)) + return true; + } - return intel_pmt_is_early_client_hw(dev); + return false; } static int pmt_telem_header_decode(struct intel_pmt_entry *entry, diff --git a/drivers/platform/x86/intel/speed_select_if/isst_if_common.c b/drivers/platform/x86/intel/speed_select_if/isst_if_common.c index e8424e70d81d..fd102678c75f 100644 --- a/drivers/platform/x86/intel/speed_select_if/isst_if_common.c +++ b/drivers/platform/x86/intel/speed_select_if/isst_if_common.c @@ -277,29 +277,38 @@ static int isst_if_get_platform_info(void __user *argp) return 0; } +#define ISST_MAX_BUS_NUMBER 2 struct isst_if_cpu_info { /* For BUS 0 and BUS 1 only, which we need for PUNIT interface */ - int bus_info[2]; - struct pci_dev *pci_dev[2]; + int bus_info[ISST_MAX_BUS_NUMBER]; + struct pci_dev *pci_dev[ISST_MAX_BUS_NUMBER]; int punit_cpu_id; int numa_node; }; +struct isst_if_pkg_info { + struct pci_dev *pci_dev[ISST_MAX_BUS_NUMBER]; +}; + static struct isst_if_cpu_info *isst_cpu_info; +static struct isst_if_pkg_info *isst_pkg_info; + #define ISST_MAX_PCI_DOMAINS 8 static struct pci_dev *_isst_if_get_pci_dev(int cpu, int bus_no, int dev, int fn) { struct pci_dev *matched_pci_dev = NULL; struct pci_dev *pci_dev = NULL; - int no_matches = 0; + int no_matches = 0, pkg_id; int i, bus_number; - if (bus_no < 0 || bus_no > 1 || cpu < 0 || cpu >= nr_cpu_ids || - cpu >= num_possible_cpus()) + if (bus_no < 0 || bus_no >= ISST_MAX_BUS_NUMBER || cpu < 0 || + cpu >= nr_cpu_ids || cpu >= num_possible_cpus()) return NULL; + pkg_id = topology_physical_package_id(cpu); + bus_number = isst_cpu_info[cpu].bus_info[bus_no]; if (bus_number < 0) return NULL; @@ -324,6 +333,8 @@ static struct pci_dev *_isst_if_get_pci_dev(int cpu, int bus_no, int dev, int fn } if (node == isst_cpu_info[cpu].numa_node) { + isst_pkg_info[pkg_id].pci_dev[bus_no] = _pci_dev; + pci_dev = _pci_dev; break; } @@ -342,6 +353,10 @@ static struct pci_dev *_isst_if_get_pci_dev(int cpu, int bus_no, int dev, int fn if (!pci_dev && no_matches == 1) pci_dev = matched_pci_dev; + /* Return pci_dev pointer for any matched CPU in the package */ + if (!pci_dev) + pci_dev = isst_pkg_info[pkg_id].pci_dev[bus_no]; + return pci_dev; } @@ -361,8 +376,8 @@ struct pci_dev *isst_if_get_pci_dev(int cpu, int bus_no, int dev, int fn) { struct pci_dev *pci_dev; - if (bus_no < 0 || bus_no > 1 || cpu < 0 || cpu >= nr_cpu_ids || - cpu >= num_possible_cpus()) + if (bus_no < 0 || bus_no >= ISST_MAX_BUS_NUMBER || cpu < 0 || + cpu >= nr_cpu_ids || cpu >= num_possible_cpus()) return NULL; pci_dev = isst_cpu_info[cpu].pci_dev[bus_no]; @@ -417,10 +432,19 @@ static int isst_if_cpu_info_init(void) if (!isst_cpu_info) return -ENOMEM; + isst_pkg_info = kcalloc(topology_max_packages(), + sizeof(*isst_pkg_info), + GFP_KERNEL); + if (!isst_pkg_info) { + kfree(isst_cpu_info); + return -ENOMEM; + } + ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "platform/x86/isst-if:online", isst_if_cpu_online, NULL); if (ret < 0) { + kfree(isst_pkg_info); kfree(isst_cpu_info); return ret; } @@ -433,6 +457,7 @@ static int isst_if_cpu_info_init(void) static void isst_if_cpu_info_exit(void) { cpuhp_remove_state(isst_if_online_id); + kfree(isst_pkg_info); kfree(isst_cpu_info); }; diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c index bed436bf181f..bb81b8b1f7e9 100644 --- a/drivers/platform/x86/intel/vsec.c +++ b/drivers/platform/x86/intel/vsec.c @@ -15,6 +15,7 @@ #include <linux/auxiliary_bus.h> #include <linux/bits.h> +#include <linux/delay.h> #include <linux/kernel.h> #include <linux/idr.h> #include <linux/module.h> @@ -30,9 +31,13 @@ #define INTEL_DVSEC_TABLE_BAR(x) ((x) & GENMASK(2, 0)) #define INTEL_DVSEC_TABLE_OFFSET(x) ((x) & GENMASK(31, 3)) #define TABLE_OFFSET_SHIFT 3 +#define PMT_XA_START 0 +#define PMT_XA_MAX INT_MAX +#define PMT_XA_LIMIT XA_LIMIT(PMT_XA_START, PMT_XA_MAX) static DEFINE_IDA(intel_vsec_ida); static DEFINE_IDA(intel_vsec_sdsi_ida); +static DEFINE_XARRAY_ALLOC(auxdev_array); /** * struct intel_vsec_header - Common fields of Intel VSEC and DVSEC registers. @@ -54,12 +59,6 @@ struct intel_vsec_header { u32 offset; }; -/* Platform specific data */ -struct intel_vsec_platform_info { - struct intel_vsec_header **capabilities; - unsigned long quirks; -}; - enum intel_vsec_id { VSEC_ID_TELEMETRY = 2, VSEC_ID_WATCHER = 3, @@ -138,7 +137,7 @@ static int intel_vsec_add_aux(struct pci_dev *pdev, struct intel_vsec_device *in const char *name) { struct auxiliary_device *auxdev = &intel_vsec_dev->auxdev; - int ret; + int ret, id; ret = ida_alloc(intel_vsec_dev->ida, GFP_KERNEL); if (ret < 0) { @@ -165,14 +164,26 @@ static int intel_vsec_add_aux(struct pci_dev *pdev, struct intel_vsec_device *in return ret; } - return devm_add_action_or_reset(&pdev->dev, intel_vsec_remove_aux, auxdev); + ret = devm_add_action_or_reset(&pdev->dev, intel_vsec_remove_aux, + auxdev); + if (ret < 0) + return ret; + + /* Add auxdev to list */ + ret = xa_alloc(&auxdev_array, &id, intel_vsec_dev, PMT_XA_LIMIT, + GFP_KERNEL); + if (ret) + return ret; + + return 0; } static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *header, - unsigned long quirks) + struct intel_vsec_platform_info *info) { struct intel_vsec_device *intel_vsec_dev; struct resource *res, *tmp; + unsigned long quirks = info->quirks; int i; if (!intel_vsec_allowed(header->id) || intel_vsec_disabled(header->id, quirks)) @@ -216,7 +227,7 @@ static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *he intel_vsec_dev->pcidev = pdev; intel_vsec_dev->resource = res; intel_vsec_dev->num_resources = header->num_entries; - intel_vsec_dev->quirks = quirks; + intel_vsec_dev->info = info; if (header->id == VSEC_ID_SDSI) intel_vsec_dev->ida = &intel_vsec_sdsi_ida; @@ -226,14 +237,15 @@ static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *he return intel_vsec_add_aux(pdev, intel_vsec_dev, intel_vsec_name(header->id)); } -static bool intel_vsec_walk_header(struct pci_dev *pdev, unsigned long quirks, - struct intel_vsec_header **header) +static bool intel_vsec_walk_header(struct pci_dev *pdev, + struct intel_vsec_platform_info *info) { + struct intel_vsec_header **header = info->capabilities; bool have_devices = false; int ret; for ( ; *header; header++) { - ret = intel_vsec_add_dev(pdev, *header, quirks); + ret = intel_vsec_add_dev(pdev, *header, info); if (ret) dev_info(&pdev->dev, "Could not add device for DVSEC id %d\n", (*header)->id); @@ -244,7 +256,8 @@ static bool intel_vsec_walk_header(struct pci_dev *pdev, unsigned long quirks, return have_devices; } -static bool intel_vsec_walk_dvsec(struct pci_dev *pdev, unsigned long quirks) +static bool intel_vsec_walk_dvsec(struct pci_dev *pdev, + struct intel_vsec_platform_info *info) { bool have_devices = false; int pos = 0; @@ -283,7 +296,7 @@ static bool intel_vsec_walk_dvsec(struct pci_dev *pdev, unsigned long quirks) pci_read_config_dword(pdev, pos + PCI_DVSEC_HEADER2, &hdr); header.id = PCI_DVSEC_HEADER2_ID(hdr); - ret = intel_vsec_add_dev(pdev, &header, quirks); + ret = intel_vsec_add_dev(pdev, &header, info); if (ret) continue; @@ -293,7 +306,8 @@ static bool intel_vsec_walk_dvsec(struct pci_dev *pdev, unsigned long quirks) return have_devices; } -static bool intel_vsec_walk_vsec(struct pci_dev *pdev, unsigned long quirks) +static bool intel_vsec_walk_vsec(struct pci_dev *pdev, + struct intel_vsec_platform_info *info) { bool have_devices = false; int pos = 0; @@ -327,7 +341,7 @@ static bool intel_vsec_walk_vsec(struct pci_dev *pdev, unsigned long quirks) header.tbir = INTEL_DVSEC_TABLE_BAR(table); header.offset = INTEL_DVSEC_TABLE_OFFSET(table); - ret = intel_vsec_add_dev(pdev, &header, quirks); + ret = intel_vsec_add_dev(pdev, &header, info); if (ret) continue; @@ -341,25 +355,25 @@ static int intel_vsec_pci_probe(struct pci_dev *pdev, const struct pci_device_id { struct intel_vsec_platform_info *info; bool have_devices = false; - unsigned long quirks = 0; int ret; ret = pcim_enable_device(pdev); if (ret) return ret; + pci_save_state(pdev); info = (struct intel_vsec_platform_info *)id->driver_data; - if (info) - quirks = info->quirks; + if (!info) + return -EINVAL; - if (intel_vsec_walk_dvsec(pdev, quirks)) + if (intel_vsec_walk_dvsec(pdev, info)) have_devices = true; - if (intel_vsec_walk_vsec(pdev, quirks)) + if (intel_vsec_walk_vsec(pdev, info)) have_devices = true; if (info && (info->quirks & VSEC_QUIRK_NO_DVSEC) && - intel_vsec_walk_header(pdev, quirks, info->capabilities)) + intel_vsec_walk_header(pdev, info)) have_devices = true; if (!have_devices) @@ -370,7 +384,8 @@ static int intel_vsec_pci_probe(struct pci_dev *pdev, const struct pci_device_id /* TGL info */ static const struct intel_vsec_platform_info tgl_info = { - .quirks = VSEC_QUIRK_NO_WATCHER | VSEC_QUIRK_NO_CRASHLOG | VSEC_QUIRK_TABLE_SHIFT, + .quirks = VSEC_QUIRK_NO_WATCHER | VSEC_QUIRK_NO_CRASHLOG | + VSEC_QUIRK_TABLE_SHIFT | VSEC_QUIRK_EARLY_HW, }; /* DG1 info */ @@ -390,26 +405,89 @@ static struct intel_vsec_header *dg1_capabilities[] = { static const struct intel_vsec_platform_info dg1_info = { .capabilities = dg1_capabilities, - .quirks = VSEC_QUIRK_NO_DVSEC, + .quirks = VSEC_QUIRK_NO_DVSEC | VSEC_QUIRK_EARLY_HW, }; #define PCI_DEVICE_ID_INTEL_VSEC_ADL 0x467d #define PCI_DEVICE_ID_INTEL_VSEC_DG1 0x490e #define PCI_DEVICE_ID_INTEL_VSEC_OOBMSM 0x09a7 +#define PCI_DEVICE_ID_INTEL_VSEC_RPL 0xa77d #define PCI_DEVICE_ID_INTEL_VSEC_TGL 0x9a0d static const struct pci_device_id intel_vsec_pci_ids[] = { { PCI_DEVICE_DATA(INTEL, VSEC_ADL, &tgl_info) }, { PCI_DEVICE_DATA(INTEL, VSEC_DG1, &dg1_info) }, - { PCI_DEVICE_DATA(INTEL, VSEC_OOBMSM, NULL) }, + { PCI_DEVICE_DATA(INTEL, VSEC_OOBMSM, &(struct intel_vsec_platform_info) {}) }, + { PCI_DEVICE_DATA(INTEL, VSEC_RPL, &tgl_info) }, { PCI_DEVICE_DATA(INTEL, VSEC_TGL, &tgl_info) }, { } }; MODULE_DEVICE_TABLE(pci, intel_vsec_pci_ids); +static pci_ers_result_t intel_vsec_pci_error_detected(struct pci_dev *pdev, + pci_channel_state_t state) +{ + pci_ers_result_t status = PCI_ERS_RESULT_NEED_RESET; + + dev_info(&pdev->dev, "PCI error detected, state %d", state); + + if (state == pci_channel_io_perm_failure) + status = PCI_ERS_RESULT_DISCONNECT; + else + pci_disable_device(pdev); + + return status; +} + +static pci_ers_result_t intel_vsec_pci_slot_reset(struct pci_dev *pdev) +{ + struct intel_vsec_device *intel_vsec_dev; + pci_ers_result_t status = PCI_ERS_RESULT_DISCONNECT; + const struct pci_device_id *pci_dev_id; + unsigned long index; + + dev_info(&pdev->dev, "Resetting PCI slot\n"); + + msleep(2000); + if (pci_enable_device(pdev)) { + dev_info(&pdev->dev, + "Failed to re-enable PCI device after reset.\n"); + goto out; + } + + status = PCI_ERS_RESULT_RECOVERED; + + xa_for_each(&auxdev_array, index, intel_vsec_dev) { + /* check if pdev doesn't match */ + if (pdev != intel_vsec_dev->pcidev) + continue; + devm_release_action(&pdev->dev, intel_vsec_remove_aux, + &intel_vsec_dev->auxdev); + } + pci_disable_device(pdev); + pci_restore_state(pdev); + pci_dev_id = pci_match_id(intel_vsec_pci_ids, pdev); + intel_vsec_pci_probe(pdev, pci_dev_id); + +out: + return status; +} + +static void intel_vsec_pci_resume(struct pci_dev *pdev) +{ + dev_info(&pdev->dev, "Done resuming PCI device\n"); +} + +static const struct pci_error_handlers intel_vsec_pci_err_handlers = { + .error_detected = intel_vsec_pci_error_detected, + .slot_reset = intel_vsec_pci_slot_reset, + .resume = intel_vsec_pci_resume, +}; + static struct pci_driver intel_vsec_pci_driver = { .name = "intel_vsec", .id_table = intel_vsec_pci_ids, .probe = intel_vsec_pci_probe, + .err_handler = &intel_vsec_pci_err_handlers, }; module_pci_driver(intel_vsec_pci_driver); diff --git a/drivers/platform/x86/intel/vsec.h b/drivers/platform/x86/intel/vsec.h index 4cc36678e8c5..3deeb05cf394 100644 --- a/drivers/platform/x86/intel/vsec.h +++ b/drivers/platform/x86/intel/vsec.h @@ -20,6 +20,15 @@ enum intel_vsec_quirks { /* DVSEC not present (provided in driver data) */ VSEC_QUIRK_NO_DVSEC = BIT(3), + + /* Platforms requiring quirk in the auxiliary driver */ + VSEC_QUIRK_EARLY_HW = BIT(4), +}; + +/* Platform specific data */ +struct intel_vsec_platform_info { + struct intel_vsec_header **capabilities; + unsigned long quirks; }; struct intel_vsec_device { @@ -27,7 +36,7 @@ struct intel_vsec_device { struct pci_dev *pcidev; struct resource *resource; struct ida *ida; - unsigned long quirks; + struct intel_vsec_platform_info *info; int num_resources; }; |