From: David E. Box Date: Tue, 27 Jul 2021 16:49:28 +0000 (-0700) Subject: platform/x86/intel: Move Intel PMT drivers to new subfolder X-Git-Url: http://git.maquefel.me/?a=commitdiff_plain;h=e184b1e589a7fbb80bfdd0364c11422999a17a26;p=linux.git platform/x86/intel: Move Intel PMT drivers to new subfolder Move all Intel Platform Monitoring Technology drivers to drivers/platform/x86/intel/pmt. Signed-off-by: David E. Box Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20210727164928.3171521-1-david.e.box@linux.intel.com Signed-off-by: Hans de Goede --- diff --git a/MAINTAINERS b/MAINTAINERS index a61f4f3b78a91..14dd0045bc78e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9494,7 +9494,7 @@ INTEL PMT DRIVER M: "David E. Box" S: Maintained F: drivers/mfd/intel_pmt.c -F: drivers/platform/x86/intel_pmt_* +F: drivers/platform/x86/intel/pmt/ INTEL PRO/WIRELESS 2100, 2200BG, 2915ABG NETWORK CONNECTION SUPPORT M: Stanislav Yakovlev diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index d12db6c316ea2..6ad35158ae4ef 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -1208,42 +1208,6 @@ config INTEL_PMC_CORE - Low Power Mode registers (Tigerlake and beyond) - PMC quirks as needed to enable SLPS0/S0ix -config INTEL_PMT_CLASS - tristate - help - The Intel Platform Monitoring Technology (PMT) class driver provides - the basic sysfs interface and file hierarchy used by PMT devices. - - For more information, see: - - - To compile this driver as a module, choose M here: the module - will be called intel_pmt_class. - -config INTEL_PMT_TELEMETRY - tristate "Intel Platform Monitoring Technology (PMT) Telemetry driver" - depends on MFD_INTEL_PMT - select INTEL_PMT_CLASS - help - The Intel Platform Monitory Technology (PMT) Telemetry driver provides - access to hardware telemetry metrics on devices that support the - feature. - - To compile this driver as a module, choose M here: the module - will be called intel_pmt_telemetry. - -config INTEL_PMT_CRASHLOG - tristate "Intel Platform Monitoring Technology (PMT) Crashlog driver" - depends on MFD_INTEL_PMT - select INTEL_PMT_CLASS - help - The Intel Platform Monitoring Technology (PMT) crashlog driver provides - access to hardware crashlog capabilities on devices that support the - feature. - - To compile this driver as a module, choose M here: the module - will be called intel_pmt_crashlog. - config INTEL_PUNIT_IPC tristate "Intel P-Unit IPC Driver" help diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 7ee369aab10d0..5edfdc2ea7f29 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -129,9 +129,6 @@ obj-$(CONFIG_INTEL_BXTWC_PMIC_TMU) += intel_bxtwc_tmu.o obj-$(CONFIG_INTEL_CHTDC_TI_PWRBTN) += intel_chtdc_ti_pwrbtn.o obj-$(CONFIG_INTEL_MRFLD_PWRBTN) += intel_mrfld_pwrbtn.o obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core.o intel_pmc_core_pltdrv.o -obj-$(CONFIG_INTEL_PMT_CLASS) += intel_pmt_class.o -obj-$(CONFIG_INTEL_PMT_TELEMETRY) += intel_pmt_telemetry.o -obj-$(CONFIG_INTEL_PMT_CRASHLOG) += intel_pmt_crashlog.o obj-$(CONFIG_INTEL_PUNIT_IPC) += intel_punit_ipc.o obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o obj-$(CONFIG_INTEL_SCU_PCI) += intel_scu_pcidrv.o diff --git a/drivers/platform/x86/intel/Kconfig b/drivers/platform/x86/intel/Kconfig index f2eef337eb985..6eec084d9bf96 100644 --- a/drivers/platform/x86/intel/Kconfig +++ b/drivers/platform/x86/intel/Kconfig @@ -18,5 +18,6 @@ if X86_PLATFORM_DRIVERS_INTEL source "drivers/platform/x86/intel/int33fe/Kconfig" source "drivers/platform/x86/intel/int3472/Kconfig" +source "drivers/platform/x86/intel/pmt/Kconfig" endif # X86_PLATFORM_DRIVERS_INTEL diff --git a/drivers/platform/x86/intel/Makefile b/drivers/platform/x86/intel/Makefile index 0653055942d5b..ca0ec2c85b052 100644 --- a/drivers/platform/x86/intel/Makefile +++ b/drivers/platform/x86/intel/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_INTEL_CHT_INT33FE) += int33fe/ obj-$(CONFIG_INTEL_SKL_INT3472) += int3472/ +obj-$(CONFIG_INTEL_PMT_CLASS) += pmt/ diff --git a/drivers/platform/x86/intel/pmt/Kconfig b/drivers/platform/x86/intel/pmt/Kconfig new file mode 100644 index 0000000000000..d630f883a7173 --- /dev/null +++ b/drivers/platform/x86/intel/pmt/Kconfig @@ -0,0 +1,40 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Intel Platform Monitoring Technology drivers +# + +config INTEL_PMT_CLASS + tristate + help + The Intel Platform Monitoring Technology (PMT) class driver provides + the basic sysfs interface and file hierarchy used by PMT devices. + + For more information, see: + + + To compile this driver as a module, choose M here: the module + will be called intel_pmt_class. + +config INTEL_PMT_TELEMETRY + tristate "Intel Platform Monitoring Technology (PMT) Telemetry driver" + depends on MFD_INTEL_PMT + select INTEL_PMT_CLASS + help + The Intel Platform Monitory Technology (PMT) Telemetry driver provides + access to hardware telemetry metrics on devices that support the + feature. + + To compile this driver as a module, choose M here: the module + will be called intel_pmt_telemetry. + +config INTEL_PMT_CRASHLOG + tristate "Intel Platform Monitoring Technology (PMT) Crashlog driver" + depends on MFD_INTEL_PMT + select INTEL_PMT_CLASS + help + The Intel Platform Monitoring Technology (PMT) crashlog driver provides + access to hardware crashlog capabilities on devices that support the + feature. + + To compile this driver as a module, choose M here: the module + will be called intel_pmt_crashlog. diff --git a/drivers/platform/x86/intel/pmt/Makefile b/drivers/platform/x86/intel/pmt/Makefile new file mode 100644 index 0000000000000..019103ee65221 --- /dev/null +++ b/drivers/platform/x86/intel/pmt/Makefile @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for linux/drivers/platform/x86/intel/pmt +# Intel Platform Monitoring Technology Drivers +# + +pmt_class-objs += class.o +obj-$(CONFIG_INTEL_PMT_CLASS) += pmt_class.o +pmt_telemetry-objs += telemetry.o +obj-$(CONFIG_INTEL_PMT_TELEMETRY) += pmt_telemetry.o +pmt_crashlog-objs += crashlog.o +obj-$(CONFIG_INTEL_PMT_CRASHLOG) += pmt_crashlog.o diff --git a/drivers/platform/x86/intel/pmt/class.c b/drivers/platform/x86/intel/pmt/class.c new file mode 100644 index 0000000000000..659b1073033c2 --- /dev/null +++ b/drivers/platform/x86/intel/pmt/class.c @@ -0,0 +1,344 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intel Platform Monitory Technology Telemetry driver + * + * Copyright (c) 2020, Intel Corporation. + * All Rights Reserved. + * + * Author: "Alexander Duyck" + */ + +#include +#include +#include +#include + +#include "class.h" + +#define PMT_XA_START 0 +#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); + + return !!pci_match_id(pmt_telem_early_client_pci_ids, parent); +} +EXPORT_SYMBOL_GPL(intel_pmt_is_early_client_hw); + +/* + * sysfs + */ +static ssize_t +intel_pmt_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *buf, loff_t off, + size_t count) +{ + struct intel_pmt_entry *entry = container_of(attr, + struct intel_pmt_entry, + pmt_bin_attr); + + if (off < 0) + return -EINVAL; + + if (off >= entry->size) + return 0; + + if (count > entry->size - off) + count = entry->size - off; + + memcpy_fromio(buf, entry->base + off, count); + + return count; +} + +static int +intel_pmt_mmap(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, struct vm_area_struct *vma) +{ + struct intel_pmt_entry *entry = container_of(attr, + struct intel_pmt_entry, + pmt_bin_attr); + unsigned long vsize = vma->vm_end - vma->vm_start; + struct device *dev = kobj_to_dev(kobj); + unsigned long phys = entry->base_addr; + unsigned long pfn = PFN_DOWN(phys); + unsigned long psize; + + if (vma->vm_flags & (VM_WRITE | VM_MAYWRITE)) + return -EROFS; + + psize = (PFN_UP(entry->base_addr + entry->size) - pfn) * PAGE_SIZE; + if (vsize > psize) { + dev_err(dev, "Requested mmap size is too large\n"); + return -EINVAL; + } + + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + if (io_remap_pfn_range(vma, vma->vm_start, pfn, + vsize, vma->vm_page_prot)) + return -EAGAIN; + + return 0; +} + +static ssize_t +guid_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct intel_pmt_entry *entry = dev_get_drvdata(dev); + + return sprintf(buf, "0x%x\n", entry->guid); +} +static DEVICE_ATTR_RO(guid); + +static ssize_t size_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct intel_pmt_entry *entry = dev_get_drvdata(dev); + + return sprintf(buf, "%zu\n", entry->size); +} +static DEVICE_ATTR_RO(size); + +static ssize_t +offset_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct intel_pmt_entry *entry = dev_get_drvdata(dev); + + return sprintf(buf, "%lu\n", offset_in_page(entry->base_addr)); +} +static DEVICE_ATTR_RO(offset); + +static struct attribute *intel_pmt_attrs[] = { + &dev_attr_guid.attr, + &dev_attr_size.attr, + &dev_attr_offset.attr, + NULL +}; +ATTRIBUTE_GROUPS(intel_pmt); + +static struct class intel_pmt_class = { + .name = "intel_pmt", + .owner = THIS_MODULE, + .dev_groups = intel_pmt_groups, +}; + +static int intel_pmt_populate_entry(struct intel_pmt_entry *entry, + struct intel_pmt_header *header, + struct device *dev, + struct resource *disc_res) +{ + struct pci_dev *pci_dev = to_pci_dev(dev->parent); + u8 bir; + + /* + * The base offset should always be 8 byte aligned. + * + * For non-local access types the lower 3 bits of base offset + * contains the index of the base address register where the + * telemetry can be found. + */ + bir = GET_BIR(header->base_offset); + + /* Local access and BARID only for now */ + switch (header->access_type) { + case ACCESS_LOCAL: + if (bir) { + dev_err(dev, + "Unsupported BAR index %d for access type %d\n", + bir, header->access_type); + return -EINVAL; + } + /* + * For access_type LOCAL, the base address is as follows: + * base address = end of discovery region + base offset + */ + entry->base_addr = disc_res->end + 1 + header->base_offset; + + /* + * Some hardware use a different calculation for the base address + * when access_type == ACCESS_LOCAL. On the these systems + * ACCCESS_LOCAL refers to an address in the same BAR as the + * header but at a fixed offset. But as the header address was + * supplied to the driver, we don't know which BAR it was in. + * So search for the bar whose range includes the header address. + */ + if (intel_pmt_is_early_client_hw(dev)) { + int i; + + entry->base_addr = 0; + for (i = 0; i < 6; i++) + if (disc_res->start >= pci_resource_start(pci_dev, i) && + (disc_res->start <= pci_resource_end(pci_dev, i))) { + entry->base_addr = pci_resource_start(pci_dev, i) + + header->base_offset; + break; + } + if (!entry->base_addr) + return -EINVAL; + } + + break; + case ACCESS_BARID: + /* + * If another BAR was specified then the base offset + * represents the offset within that BAR. SO retrieve the + * address from the parent PCI device and add offset. + */ + entry->base_addr = pci_resource_start(pci_dev, bir) + + GET_ADDRESS(header->base_offset); + break; + default: + dev_err(dev, "Unsupported access type %d\n", + header->access_type); + return -EINVAL; + } + + entry->guid = header->guid; + entry->size = header->size; + + return 0; +} + +static int intel_pmt_dev_register(struct intel_pmt_entry *entry, + struct intel_pmt_namespace *ns, + struct device *parent) +{ + struct resource res = {0}; + struct device *dev; + int ret; + + ret = xa_alloc(ns->xa, &entry->devid, entry, PMT_XA_LIMIT, GFP_KERNEL); + if (ret) + return ret; + + dev = device_create(&intel_pmt_class, parent, MKDEV(0, 0), entry, + "%s%d", ns->name, entry->devid); + + if (IS_ERR(dev)) { + dev_err(parent, "Could not create %s%d device node\n", + ns->name, entry->devid); + ret = PTR_ERR(dev); + goto fail_dev_create; + } + + entry->kobj = &dev->kobj; + + if (ns->attr_grp) { + ret = sysfs_create_group(entry->kobj, ns->attr_grp); + if (ret) + goto fail_sysfs; + } + + /* if size is 0 assume no data buffer, so no file needed */ + if (!entry->size) + return 0; + + res.start = entry->base_addr; + res.end = res.start + entry->size - 1; + res.flags = IORESOURCE_MEM; + + entry->base = devm_ioremap_resource(dev, &res); + if (IS_ERR(entry->base)) { + ret = PTR_ERR(entry->base); + goto fail_ioremap; + } + + sysfs_bin_attr_init(&entry->pmt_bin_attr); + entry->pmt_bin_attr.attr.name = ns->name; + entry->pmt_bin_attr.attr.mode = 0440; + entry->pmt_bin_attr.mmap = intel_pmt_mmap; + entry->pmt_bin_attr.read = intel_pmt_read; + entry->pmt_bin_attr.size = entry->size; + + ret = sysfs_create_bin_file(&dev->kobj, &entry->pmt_bin_attr); + if (!ret) + return 0; + +fail_ioremap: + if (ns->attr_grp) + sysfs_remove_group(entry->kobj, ns->attr_grp); +fail_sysfs: + device_unregister(dev); +fail_dev_create: + xa_erase(ns->xa, entry->devid); + + return ret; +} + +int intel_pmt_dev_create(struct intel_pmt_entry *entry, + struct intel_pmt_namespace *ns, + struct platform_device *pdev, int idx) +{ + struct intel_pmt_header header; + struct resource *disc_res; + int ret = -ENODEV; + + disc_res = platform_get_resource(pdev, IORESOURCE_MEM, idx); + if (!disc_res) + return ret; + + entry->disc_table = devm_platform_ioremap_resource(pdev, idx); + if (IS_ERR(entry->disc_table)) + return PTR_ERR(entry->disc_table); + + ret = ns->pmt_header_decode(entry, &header, &pdev->dev); + if (ret) + return ret; + + ret = intel_pmt_populate_entry(entry, &header, &pdev->dev, disc_res); + if (ret) + return ret; + + return intel_pmt_dev_register(entry, ns, &pdev->dev); + +} +EXPORT_SYMBOL_GPL(intel_pmt_dev_create); + +void intel_pmt_dev_destroy(struct intel_pmt_entry *entry, + struct intel_pmt_namespace *ns) +{ + struct device *dev = kobj_to_dev(entry->kobj); + + if (entry->size) + sysfs_remove_bin_file(entry->kobj, &entry->pmt_bin_attr); + + if (ns->attr_grp) + sysfs_remove_group(entry->kobj, ns->attr_grp); + + device_unregister(dev); + xa_erase(ns->xa, entry->devid); +} +EXPORT_SYMBOL_GPL(intel_pmt_dev_destroy); + +static int __init pmt_class_init(void) +{ + return class_register(&intel_pmt_class); +} + +static void __exit pmt_class_exit(void) +{ + class_unregister(&intel_pmt_class); +} + +module_init(pmt_class_init); +module_exit(pmt_class_exit); + +MODULE_AUTHOR("Alexander Duyck "); +MODULE_DESCRIPTION("Intel PMT Class driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/platform/x86/intel/pmt/class.h b/drivers/platform/x86/intel/pmt/class.h new file mode 100644 index 0000000000000..1337019c2873e --- /dev/null +++ b/drivers/platform/x86/intel/pmt/class.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _INTEL_PMT_CLASS_H +#define _INTEL_PMT_CLASS_H + +#include +#include +#include +#include +#include +#include + +/* PMT access types */ +#define ACCESS_BARID 2 +#define ACCESS_LOCAL 3 + +/* PMT discovery base address/offset register layout */ +#define GET_BIR(v) ((v) & GENMASK(2, 0)) +#define GET_ADDRESS(v) ((v) & GENMASK(31, 3)) + +struct intel_pmt_entry { + struct bin_attribute pmt_bin_attr; + struct kobject *kobj; + void __iomem *disc_table; + void __iomem *base; + unsigned long base_addr; + size_t size; + u32 guid; + int devid; +}; + +struct intel_pmt_header { + u32 base_offset; + u32 size; + u32 guid; + u8 access_type; +}; + +struct intel_pmt_namespace { + const char *name; + struct xarray *xa; + const struct attribute_group *attr_grp; + int (*pmt_header_decode)(struct intel_pmt_entry *entry, + struct intel_pmt_header *header, + struct device *dev); +}; + +bool intel_pmt_is_early_client_hw(struct device *dev); +int intel_pmt_dev_create(struct intel_pmt_entry *entry, + struct intel_pmt_namespace *ns, + struct platform_device *pdev, int idx); +void intel_pmt_dev_destroy(struct intel_pmt_entry *entry, + struct intel_pmt_namespace *ns); +#endif diff --git a/drivers/platform/x86/intel/pmt/crashlog.c b/drivers/platform/x86/intel/pmt/crashlog.c new file mode 100644 index 0000000000000..1c1021f04d3ca --- /dev/null +++ b/drivers/platform/x86/intel/pmt/crashlog.c @@ -0,0 +1,327 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intel Platform Monitoring Technology Crashlog driver + * + * Copyright (c) 2020, Intel Corporation. + * All Rights Reserved. + * + * Author: "Alexander Duyck" + */ + +#include +#include +#include +#include +#include +#include + +#include "class.h" + +#define DRV_NAME "pmt_crashlog" + +/* Crashlog discovery header types */ +#define CRASH_TYPE_OOBMSM 1 + +/* Control Flags */ +#define CRASHLOG_FLAG_DISABLE BIT(28) + +/* + * Bits 29 and 30 control the state of bit 31. + * + * Bit 29 will clear bit 31, if set, allowing a new crashlog to be captured. + * Bit 30 will immediately trigger a crashlog to be generated, setting bit 31. + * Bit 31 is the read-only status with a 1 indicating log is complete. + */ +#define CRASHLOG_FLAG_TRIGGER_CLEAR BIT(29) +#define CRASHLOG_FLAG_TRIGGER_EXECUTE BIT(30) +#define CRASHLOG_FLAG_TRIGGER_COMPLETE BIT(31) +#define CRASHLOG_FLAG_TRIGGER_MASK GENMASK(31, 28) + +/* Crashlog Discovery Header */ +#define CONTROL_OFFSET 0x0 +#define GUID_OFFSET 0x4 +#define BASE_OFFSET 0x8 +#define SIZE_OFFSET 0xC +#define GET_ACCESS(v) ((v) & GENMASK(3, 0)) +#define GET_TYPE(v) (((v) & GENMASK(7, 4)) >> 4) +#define GET_VERSION(v) (((v) & GENMASK(19, 16)) >> 16) +/* size is in bytes */ +#define GET_SIZE(v) ((v) * sizeof(u32)) + +struct crashlog_entry { + /* entry must be first member of struct */ + struct intel_pmt_entry entry; + struct mutex control_mutex; +}; + +struct pmt_crashlog_priv { + int num_entries; + struct crashlog_entry entry[]; +}; + +/* + * I/O + */ +static bool pmt_crashlog_complete(struct intel_pmt_entry *entry) +{ + u32 control = readl(entry->disc_table + CONTROL_OFFSET); + + /* return current value of the crashlog complete flag */ + return !!(control & CRASHLOG_FLAG_TRIGGER_COMPLETE); +} + +static bool pmt_crashlog_disabled(struct intel_pmt_entry *entry) +{ + u32 control = readl(entry->disc_table + CONTROL_OFFSET); + + /* return current value of the crashlog disabled flag */ + return !!(control & CRASHLOG_FLAG_DISABLE); +} + +static bool pmt_crashlog_supported(struct intel_pmt_entry *entry) +{ + u32 discovery_header = readl(entry->disc_table + CONTROL_OFFSET); + u32 crash_type, version; + + crash_type = GET_TYPE(discovery_header); + version = GET_VERSION(discovery_header); + + /* + * Currently we only recognize OOBMSM version 0 devices. + * We can ignore all other crashlog devices in the system. + */ + return crash_type == CRASH_TYPE_OOBMSM && version == 0; +} + +static void pmt_crashlog_set_disable(struct intel_pmt_entry *entry, + bool disable) +{ + u32 control = readl(entry->disc_table + CONTROL_OFFSET); + + /* clear trigger bits so we are only modifying disable flag */ + control &= ~CRASHLOG_FLAG_TRIGGER_MASK; + + if (disable) + control |= CRASHLOG_FLAG_DISABLE; + else + control &= ~CRASHLOG_FLAG_DISABLE; + + writel(control, entry->disc_table + CONTROL_OFFSET); +} + +static void pmt_crashlog_set_clear(struct intel_pmt_entry *entry) +{ + u32 control = readl(entry->disc_table + CONTROL_OFFSET); + + control &= ~CRASHLOG_FLAG_TRIGGER_MASK; + control |= CRASHLOG_FLAG_TRIGGER_CLEAR; + + writel(control, entry->disc_table + CONTROL_OFFSET); +} + +static void pmt_crashlog_set_execute(struct intel_pmt_entry *entry) +{ + u32 control = readl(entry->disc_table + CONTROL_OFFSET); + + control &= ~CRASHLOG_FLAG_TRIGGER_MASK; + control |= CRASHLOG_FLAG_TRIGGER_EXECUTE; + + writel(control, entry->disc_table + CONTROL_OFFSET); +} + +/* + * sysfs + */ +static ssize_t +enable_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct intel_pmt_entry *entry = dev_get_drvdata(dev); + int enabled = !pmt_crashlog_disabled(entry); + + return sprintf(buf, "%d\n", enabled); +} + +static ssize_t +enable_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct crashlog_entry *entry; + bool enabled; + int result; + + entry = dev_get_drvdata(dev); + + result = kstrtobool(buf, &enabled); + if (result) + return result; + + mutex_lock(&entry->control_mutex); + pmt_crashlog_set_disable(&entry->entry, !enabled); + mutex_unlock(&entry->control_mutex); + + return count; +} +static DEVICE_ATTR_RW(enable); + +static ssize_t +trigger_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct intel_pmt_entry *entry; + int trigger; + + entry = dev_get_drvdata(dev); + trigger = pmt_crashlog_complete(entry); + + return sprintf(buf, "%d\n", trigger); +} + +static ssize_t +trigger_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct crashlog_entry *entry; + bool trigger; + int result; + + entry = dev_get_drvdata(dev); + + result = kstrtobool(buf, &trigger); + if (result) + return result; + + mutex_lock(&entry->control_mutex); + + if (!trigger) { + pmt_crashlog_set_clear(&entry->entry); + } else if (pmt_crashlog_complete(&entry->entry)) { + /* we cannot trigger a new crash if one is still pending */ + result = -EEXIST; + goto err; + } else if (pmt_crashlog_disabled(&entry->entry)) { + /* if device is currently disabled, return busy */ + result = -EBUSY; + goto err; + } else { + pmt_crashlog_set_execute(&entry->entry); + } + + result = count; +err: + mutex_unlock(&entry->control_mutex); + return result; +} +static DEVICE_ATTR_RW(trigger); + +static struct attribute *pmt_crashlog_attrs[] = { + &dev_attr_enable.attr, + &dev_attr_trigger.attr, + NULL +}; + +static const struct attribute_group pmt_crashlog_group = { + .attrs = pmt_crashlog_attrs, +}; + +static int pmt_crashlog_header_decode(struct intel_pmt_entry *entry, + struct intel_pmt_header *header, + struct device *dev) +{ + void __iomem *disc_table = entry->disc_table; + struct crashlog_entry *crashlog; + + if (!pmt_crashlog_supported(entry)) + return 1; + + /* initialize control mutex */ + crashlog = container_of(entry, struct crashlog_entry, entry); + mutex_init(&crashlog->control_mutex); + + header->access_type = GET_ACCESS(readl(disc_table)); + header->guid = readl(disc_table + GUID_OFFSET); + header->base_offset = readl(disc_table + BASE_OFFSET); + + /* Size is measured in DWORDS, but accessor returns bytes */ + header->size = GET_SIZE(readl(disc_table + SIZE_OFFSET)); + + return 0; +} + +static DEFINE_XARRAY_ALLOC(crashlog_array); +static struct intel_pmt_namespace pmt_crashlog_ns = { + .name = "crashlog", + .xa = &crashlog_array, + .attr_grp = &pmt_crashlog_group, + .pmt_header_decode = pmt_crashlog_header_decode, +}; + +/* + * initialization + */ +static int pmt_crashlog_remove(struct platform_device *pdev) +{ + struct pmt_crashlog_priv *priv = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < priv->num_entries; i++) + intel_pmt_dev_destroy(&priv->entry[i].entry, &pmt_crashlog_ns); + + return 0; +} + +static int pmt_crashlog_probe(struct platform_device *pdev) +{ + struct pmt_crashlog_priv *priv; + size_t size; + int i, ret; + + size = struct_size(priv, entry, pdev->num_resources); + priv = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); + if (!priv) + return -ENOMEM; + + platform_set_drvdata(pdev, priv); + + for (i = 0; i < pdev->num_resources; i++) { + struct intel_pmt_entry *entry = &priv->entry[i].entry; + + ret = intel_pmt_dev_create(entry, &pmt_crashlog_ns, pdev, i); + if (ret < 0) + goto abort_probe; + if (ret) + continue; + + priv->num_entries++; + } + + return 0; +abort_probe: + pmt_crashlog_remove(pdev); + return ret; +} + +static struct platform_driver pmt_crashlog_driver = { + .driver = { + .name = DRV_NAME, + }, + .remove = pmt_crashlog_remove, + .probe = pmt_crashlog_probe, +}; + +static int __init pmt_crashlog_init(void) +{ + return platform_driver_register(&pmt_crashlog_driver); +} + +static void __exit pmt_crashlog_exit(void) +{ + platform_driver_unregister(&pmt_crashlog_driver); + xa_destroy(&crashlog_array); +} + +module_init(pmt_crashlog_init); +module_exit(pmt_crashlog_exit); + +MODULE_AUTHOR("Alexander Duyck "); +MODULE_DESCRIPTION("Intel PMT Crashlog driver"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/platform/x86/intel/pmt/telemetry.c b/drivers/platform/x86/intel/pmt/telemetry.c new file mode 100644 index 0000000000000..a58843360fbf6 --- /dev/null +++ b/drivers/platform/x86/intel/pmt/telemetry.c @@ -0,0 +1,140 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intel Platform Monitory Technology Telemetry driver + * + * Copyright (c) 2020, Intel Corporation. + * All Rights Reserved. + * + * Author: "David E. Box" + */ + +#include +#include +#include +#include +#include +#include + +#include "class.h" + +#define TELEM_DEV_NAME "pmt_telemetry" + +#define TELEM_SIZE_OFFSET 0x0 +#define TELEM_GUID_OFFSET 0x4 +#define TELEM_BASE_OFFSET 0x8 +#define TELEM_ACCESS(v) ((v) & GENMASK(3, 0)) +/* 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 + +struct pmt_telem_priv { + int num_entries; + struct intel_pmt_entry entry[]; +}; + +static bool pmt_telem_region_overlaps(struct intel_pmt_entry *entry, + struct device *dev) +{ + u32 guid = readl(entry->disc_table + TELEM_GUID_OFFSET); + + if (guid != TELEM_CLIENT_FIXED_BLOCK_GUID) + return false; + + return intel_pmt_is_early_client_hw(dev); +} + +static int pmt_telem_header_decode(struct intel_pmt_entry *entry, + struct intel_pmt_header *header, + struct device *dev) +{ + void __iomem *disc_table = entry->disc_table; + + if (pmt_telem_region_overlaps(entry, dev)) + return 1; + + header->access_type = TELEM_ACCESS(readl(disc_table)); + header->guid = readl(disc_table + TELEM_GUID_OFFSET); + header->base_offset = readl(disc_table + TELEM_BASE_OFFSET); + + /* Size is measured in DWORDS, but accessor returns bytes */ + header->size = TELEM_SIZE(readl(disc_table)); + + return 0; +} + +static DEFINE_XARRAY_ALLOC(telem_array); +static struct intel_pmt_namespace pmt_telem_ns = { + .name = "telem", + .xa = &telem_array, + .pmt_header_decode = pmt_telem_header_decode, +}; + +static int pmt_telem_remove(struct platform_device *pdev) +{ + struct pmt_telem_priv *priv = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < priv->num_entries; i++) + intel_pmt_dev_destroy(&priv->entry[i], &pmt_telem_ns); + + return 0; +} + +static int pmt_telem_probe(struct platform_device *pdev) +{ + struct pmt_telem_priv *priv; + size_t size; + int i, ret; + + size = struct_size(priv, entry, pdev->num_resources); + priv = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); + if (!priv) + return -ENOMEM; + + platform_set_drvdata(pdev, priv); + + for (i = 0; i < pdev->num_resources; i++) { + struct intel_pmt_entry *entry = &priv->entry[i]; + + ret = intel_pmt_dev_create(entry, &pmt_telem_ns, pdev, i); + if (ret < 0) + goto abort_probe; + if (ret) + continue; + + priv->num_entries++; + } + + return 0; +abort_probe: + pmt_telem_remove(pdev); + return ret; +} + +static struct platform_driver pmt_telem_driver = { + .driver = { + .name = TELEM_DEV_NAME, + }, + .remove = pmt_telem_remove, + .probe = pmt_telem_probe, +}; + +static int __init pmt_telem_init(void) +{ + return platform_driver_register(&pmt_telem_driver); +} +module_init(pmt_telem_init); + +static void __exit pmt_telem_exit(void) +{ + platform_driver_unregister(&pmt_telem_driver); + xa_destroy(&telem_array); +} +module_exit(pmt_telem_exit); + +MODULE_AUTHOR("David E. Box "); +MODULE_DESCRIPTION("Intel PMT Telemetry driver"); +MODULE_ALIAS("platform:" TELEM_DEV_NAME); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/platform/x86/intel_pmt_class.c b/drivers/platform/x86/intel_pmt_class.c deleted file mode 100644 index c86ff15b1ed52..0000000000000 --- a/drivers/platform/x86/intel_pmt_class.c +++ /dev/null @@ -1,344 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Intel Platform Monitory Technology Telemetry driver - * - * Copyright (c) 2020, Intel Corporation. - * All Rights Reserved. - * - * Author: "Alexander Duyck" - */ - -#include -#include -#include -#include - -#include "intel_pmt_class.h" - -#define PMT_XA_START 0 -#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); - - return !!pci_match_id(pmt_telem_early_client_pci_ids, parent); -} -EXPORT_SYMBOL_GPL(intel_pmt_is_early_client_hw); - -/* - * sysfs - */ -static ssize_t -intel_pmt_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, char *buf, loff_t off, - size_t count) -{ - struct intel_pmt_entry *entry = container_of(attr, - struct intel_pmt_entry, - pmt_bin_attr); - - if (off < 0) - return -EINVAL; - - if (off >= entry->size) - return 0; - - if (count > entry->size - off) - count = entry->size - off; - - memcpy_fromio(buf, entry->base + off, count); - - return count; -} - -static int -intel_pmt_mmap(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, struct vm_area_struct *vma) -{ - struct intel_pmt_entry *entry = container_of(attr, - struct intel_pmt_entry, - pmt_bin_attr); - unsigned long vsize = vma->vm_end - vma->vm_start; - struct device *dev = kobj_to_dev(kobj); - unsigned long phys = entry->base_addr; - unsigned long pfn = PFN_DOWN(phys); - unsigned long psize; - - if (vma->vm_flags & (VM_WRITE | VM_MAYWRITE)) - return -EROFS; - - psize = (PFN_UP(entry->base_addr + entry->size) - pfn) * PAGE_SIZE; - if (vsize > psize) { - dev_err(dev, "Requested mmap size is too large\n"); - return -EINVAL; - } - - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - if (io_remap_pfn_range(vma, vma->vm_start, pfn, - vsize, vma->vm_page_prot)) - return -EAGAIN; - - return 0; -} - -static ssize_t -guid_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct intel_pmt_entry *entry = dev_get_drvdata(dev); - - return sprintf(buf, "0x%x\n", entry->guid); -} -static DEVICE_ATTR_RO(guid); - -static ssize_t size_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct intel_pmt_entry *entry = dev_get_drvdata(dev); - - return sprintf(buf, "%zu\n", entry->size); -} -static DEVICE_ATTR_RO(size); - -static ssize_t -offset_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct intel_pmt_entry *entry = dev_get_drvdata(dev); - - return sprintf(buf, "%lu\n", offset_in_page(entry->base_addr)); -} -static DEVICE_ATTR_RO(offset); - -static struct attribute *intel_pmt_attrs[] = { - &dev_attr_guid.attr, - &dev_attr_size.attr, - &dev_attr_offset.attr, - NULL -}; -ATTRIBUTE_GROUPS(intel_pmt); - -static struct class intel_pmt_class = { - .name = "intel_pmt", - .owner = THIS_MODULE, - .dev_groups = intel_pmt_groups, -}; - -static int intel_pmt_populate_entry(struct intel_pmt_entry *entry, - struct intel_pmt_header *header, - struct device *dev, - struct resource *disc_res) -{ - struct pci_dev *pci_dev = to_pci_dev(dev->parent); - u8 bir; - - /* - * The base offset should always be 8 byte aligned. - * - * For non-local access types the lower 3 bits of base offset - * contains the index of the base address register where the - * telemetry can be found. - */ - bir = GET_BIR(header->base_offset); - - /* Local access and BARID only for now */ - switch (header->access_type) { - case ACCESS_LOCAL: - if (bir) { - dev_err(dev, - "Unsupported BAR index %d for access type %d\n", - bir, header->access_type); - return -EINVAL; - } - /* - * For access_type LOCAL, the base address is as follows: - * base address = end of discovery region + base offset - */ - entry->base_addr = disc_res->end + 1 + header->base_offset; - - /* - * Some hardware use a different calculation for the base address - * when access_type == ACCESS_LOCAL. On the these systems - * ACCCESS_LOCAL refers to an address in the same BAR as the - * header but at a fixed offset. But as the header address was - * supplied to the driver, we don't know which BAR it was in. - * So search for the bar whose range includes the header address. - */ - if (intel_pmt_is_early_client_hw(dev)) { - int i; - - entry->base_addr = 0; - for (i = 0; i < 6; i++) - if (disc_res->start >= pci_resource_start(pci_dev, i) && - (disc_res->start <= pci_resource_end(pci_dev, i))) { - entry->base_addr = pci_resource_start(pci_dev, i) + - header->base_offset; - break; - } - if (!entry->base_addr) - return -EINVAL; - } - - break; - case ACCESS_BARID: - /* - * If another BAR was specified then the base offset - * represents the offset within that BAR. SO retrieve the - * address from the parent PCI device and add offset. - */ - entry->base_addr = pci_resource_start(pci_dev, bir) + - GET_ADDRESS(header->base_offset); - break; - default: - dev_err(dev, "Unsupported access type %d\n", - header->access_type); - return -EINVAL; - } - - entry->guid = header->guid; - entry->size = header->size; - - return 0; -} - -static int intel_pmt_dev_register(struct intel_pmt_entry *entry, - struct intel_pmt_namespace *ns, - struct device *parent) -{ - struct resource res = {0}; - struct device *dev; - int ret; - - ret = xa_alloc(ns->xa, &entry->devid, entry, PMT_XA_LIMIT, GFP_KERNEL); - if (ret) - return ret; - - dev = device_create(&intel_pmt_class, parent, MKDEV(0, 0), entry, - "%s%d", ns->name, entry->devid); - - if (IS_ERR(dev)) { - dev_err(parent, "Could not create %s%d device node\n", - ns->name, entry->devid); - ret = PTR_ERR(dev); - goto fail_dev_create; - } - - entry->kobj = &dev->kobj; - - if (ns->attr_grp) { - ret = sysfs_create_group(entry->kobj, ns->attr_grp); - if (ret) - goto fail_sysfs; - } - - /* if size is 0 assume no data buffer, so no file needed */ - if (!entry->size) - return 0; - - res.start = entry->base_addr; - res.end = res.start + entry->size - 1; - res.flags = IORESOURCE_MEM; - - entry->base = devm_ioremap_resource(dev, &res); - if (IS_ERR(entry->base)) { - ret = PTR_ERR(entry->base); - goto fail_ioremap; - } - - sysfs_bin_attr_init(&entry->pmt_bin_attr); - entry->pmt_bin_attr.attr.name = ns->name; - entry->pmt_bin_attr.attr.mode = 0440; - entry->pmt_bin_attr.mmap = intel_pmt_mmap; - entry->pmt_bin_attr.read = intel_pmt_read; - entry->pmt_bin_attr.size = entry->size; - - ret = sysfs_create_bin_file(&dev->kobj, &entry->pmt_bin_attr); - if (!ret) - return 0; - -fail_ioremap: - if (ns->attr_grp) - sysfs_remove_group(entry->kobj, ns->attr_grp); -fail_sysfs: - device_unregister(dev); -fail_dev_create: - xa_erase(ns->xa, entry->devid); - - return ret; -} - -int intel_pmt_dev_create(struct intel_pmt_entry *entry, - struct intel_pmt_namespace *ns, - struct platform_device *pdev, int idx) -{ - struct intel_pmt_header header; - struct resource *disc_res; - int ret = -ENODEV; - - disc_res = platform_get_resource(pdev, IORESOURCE_MEM, idx); - if (!disc_res) - return ret; - - entry->disc_table = devm_platform_ioremap_resource(pdev, idx); - if (IS_ERR(entry->disc_table)) - return PTR_ERR(entry->disc_table); - - ret = ns->pmt_header_decode(entry, &header, &pdev->dev); - if (ret) - return ret; - - ret = intel_pmt_populate_entry(entry, &header, &pdev->dev, disc_res); - if (ret) - return ret; - - return intel_pmt_dev_register(entry, ns, &pdev->dev); - -} -EXPORT_SYMBOL_GPL(intel_pmt_dev_create); - -void intel_pmt_dev_destroy(struct intel_pmt_entry *entry, - struct intel_pmt_namespace *ns) -{ - struct device *dev = kobj_to_dev(entry->kobj); - - if (entry->size) - sysfs_remove_bin_file(entry->kobj, &entry->pmt_bin_attr); - - if (ns->attr_grp) - sysfs_remove_group(entry->kobj, ns->attr_grp); - - device_unregister(dev); - xa_erase(ns->xa, entry->devid); -} -EXPORT_SYMBOL_GPL(intel_pmt_dev_destroy); - -static int __init pmt_class_init(void) -{ - return class_register(&intel_pmt_class); -} - -static void __exit pmt_class_exit(void) -{ - class_unregister(&intel_pmt_class); -} - -module_init(pmt_class_init); -module_exit(pmt_class_exit); - -MODULE_AUTHOR("Alexander Duyck "); -MODULE_DESCRIPTION("Intel PMT Class driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/platform/x86/intel_pmt_class.h b/drivers/platform/x86/intel_pmt_class.h deleted file mode 100644 index 1337019c2873e..0000000000000 --- a/drivers/platform/x86/intel_pmt_class.h +++ /dev/null @@ -1,53 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _INTEL_PMT_CLASS_H -#define _INTEL_PMT_CLASS_H - -#include -#include -#include -#include -#include -#include - -/* PMT access types */ -#define ACCESS_BARID 2 -#define ACCESS_LOCAL 3 - -/* PMT discovery base address/offset register layout */ -#define GET_BIR(v) ((v) & GENMASK(2, 0)) -#define GET_ADDRESS(v) ((v) & GENMASK(31, 3)) - -struct intel_pmt_entry { - struct bin_attribute pmt_bin_attr; - struct kobject *kobj; - void __iomem *disc_table; - void __iomem *base; - unsigned long base_addr; - size_t size; - u32 guid; - int devid; -}; - -struct intel_pmt_header { - u32 base_offset; - u32 size; - u32 guid; - u8 access_type; -}; - -struct intel_pmt_namespace { - const char *name; - struct xarray *xa; - const struct attribute_group *attr_grp; - int (*pmt_header_decode)(struct intel_pmt_entry *entry, - struct intel_pmt_header *header, - struct device *dev); -}; - -bool intel_pmt_is_early_client_hw(struct device *dev); -int intel_pmt_dev_create(struct intel_pmt_entry *entry, - struct intel_pmt_namespace *ns, - struct platform_device *pdev, int idx); -void intel_pmt_dev_destroy(struct intel_pmt_entry *entry, - struct intel_pmt_namespace *ns); -#endif diff --git a/drivers/platform/x86/intel_pmt_crashlog.c b/drivers/platform/x86/intel_pmt_crashlog.c deleted file mode 100644 index 56963ceb6345f..0000000000000 --- a/drivers/platform/x86/intel_pmt_crashlog.c +++ /dev/null @@ -1,327 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Intel Platform Monitoring Technology Crashlog driver - * - * Copyright (c) 2020, Intel Corporation. - * All Rights Reserved. - * - * Author: "Alexander Duyck" - */ - -#include -#include -#include -#include -#include -#include - -#include "intel_pmt_class.h" - -#define DRV_NAME "pmt_crashlog" - -/* Crashlog discovery header types */ -#define CRASH_TYPE_OOBMSM 1 - -/* Control Flags */ -#define CRASHLOG_FLAG_DISABLE BIT(28) - -/* - * Bits 29 and 30 control the state of bit 31. - * - * Bit 29 will clear bit 31, if set, allowing a new crashlog to be captured. - * Bit 30 will immediately trigger a crashlog to be generated, setting bit 31. - * Bit 31 is the read-only status with a 1 indicating log is complete. - */ -#define CRASHLOG_FLAG_TRIGGER_CLEAR BIT(29) -#define CRASHLOG_FLAG_TRIGGER_EXECUTE BIT(30) -#define CRASHLOG_FLAG_TRIGGER_COMPLETE BIT(31) -#define CRASHLOG_FLAG_TRIGGER_MASK GENMASK(31, 28) - -/* Crashlog Discovery Header */ -#define CONTROL_OFFSET 0x0 -#define GUID_OFFSET 0x4 -#define BASE_OFFSET 0x8 -#define SIZE_OFFSET 0xC -#define GET_ACCESS(v) ((v) & GENMASK(3, 0)) -#define GET_TYPE(v) (((v) & GENMASK(7, 4)) >> 4) -#define GET_VERSION(v) (((v) & GENMASK(19, 16)) >> 16) -/* size is in bytes */ -#define GET_SIZE(v) ((v) * sizeof(u32)) - -struct crashlog_entry { - /* entry must be first member of struct */ - struct intel_pmt_entry entry; - struct mutex control_mutex; -}; - -struct pmt_crashlog_priv { - int num_entries; - struct crashlog_entry entry[]; -}; - -/* - * I/O - */ -static bool pmt_crashlog_complete(struct intel_pmt_entry *entry) -{ - u32 control = readl(entry->disc_table + CONTROL_OFFSET); - - /* return current value of the crashlog complete flag */ - return !!(control & CRASHLOG_FLAG_TRIGGER_COMPLETE); -} - -static bool pmt_crashlog_disabled(struct intel_pmt_entry *entry) -{ - u32 control = readl(entry->disc_table + CONTROL_OFFSET); - - /* return current value of the crashlog disabled flag */ - return !!(control & CRASHLOG_FLAG_DISABLE); -} - -static bool pmt_crashlog_supported(struct intel_pmt_entry *entry) -{ - u32 discovery_header = readl(entry->disc_table + CONTROL_OFFSET); - u32 crash_type, version; - - crash_type = GET_TYPE(discovery_header); - version = GET_VERSION(discovery_header); - - /* - * Currently we only recognize OOBMSM version 0 devices. - * We can ignore all other crashlog devices in the system. - */ - return crash_type == CRASH_TYPE_OOBMSM && version == 0; -} - -static void pmt_crashlog_set_disable(struct intel_pmt_entry *entry, - bool disable) -{ - u32 control = readl(entry->disc_table + CONTROL_OFFSET); - - /* clear trigger bits so we are only modifying disable flag */ - control &= ~CRASHLOG_FLAG_TRIGGER_MASK; - - if (disable) - control |= CRASHLOG_FLAG_DISABLE; - else - control &= ~CRASHLOG_FLAG_DISABLE; - - writel(control, entry->disc_table + CONTROL_OFFSET); -} - -static void pmt_crashlog_set_clear(struct intel_pmt_entry *entry) -{ - u32 control = readl(entry->disc_table + CONTROL_OFFSET); - - control &= ~CRASHLOG_FLAG_TRIGGER_MASK; - control |= CRASHLOG_FLAG_TRIGGER_CLEAR; - - writel(control, entry->disc_table + CONTROL_OFFSET); -} - -static void pmt_crashlog_set_execute(struct intel_pmt_entry *entry) -{ - u32 control = readl(entry->disc_table + CONTROL_OFFSET); - - control &= ~CRASHLOG_FLAG_TRIGGER_MASK; - control |= CRASHLOG_FLAG_TRIGGER_EXECUTE; - - writel(control, entry->disc_table + CONTROL_OFFSET); -} - -/* - * sysfs - */ -static ssize_t -enable_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct intel_pmt_entry *entry = dev_get_drvdata(dev); - int enabled = !pmt_crashlog_disabled(entry); - - return sprintf(buf, "%d\n", enabled); -} - -static ssize_t -enable_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct crashlog_entry *entry; - bool enabled; - int result; - - entry = dev_get_drvdata(dev); - - result = kstrtobool(buf, &enabled); - if (result) - return result; - - mutex_lock(&entry->control_mutex); - pmt_crashlog_set_disable(&entry->entry, !enabled); - mutex_unlock(&entry->control_mutex); - - return count; -} -static DEVICE_ATTR_RW(enable); - -static ssize_t -trigger_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct intel_pmt_entry *entry; - int trigger; - - entry = dev_get_drvdata(dev); - trigger = pmt_crashlog_complete(entry); - - return sprintf(buf, "%d\n", trigger); -} - -static ssize_t -trigger_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct crashlog_entry *entry; - bool trigger; - int result; - - entry = dev_get_drvdata(dev); - - result = kstrtobool(buf, &trigger); - if (result) - return result; - - mutex_lock(&entry->control_mutex); - - if (!trigger) { - pmt_crashlog_set_clear(&entry->entry); - } else if (pmt_crashlog_complete(&entry->entry)) { - /* we cannot trigger a new crash if one is still pending */ - result = -EEXIST; - goto err; - } else if (pmt_crashlog_disabled(&entry->entry)) { - /* if device is currently disabled, return busy */ - result = -EBUSY; - goto err; - } else { - pmt_crashlog_set_execute(&entry->entry); - } - - result = count; -err: - mutex_unlock(&entry->control_mutex); - return result; -} -static DEVICE_ATTR_RW(trigger); - -static struct attribute *pmt_crashlog_attrs[] = { - &dev_attr_enable.attr, - &dev_attr_trigger.attr, - NULL -}; - -static const struct attribute_group pmt_crashlog_group = { - .attrs = pmt_crashlog_attrs, -}; - -static int pmt_crashlog_header_decode(struct intel_pmt_entry *entry, - struct intel_pmt_header *header, - struct device *dev) -{ - void __iomem *disc_table = entry->disc_table; - struct crashlog_entry *crashlog; - - if (!pmt_crashlog_supported(entry)) - return 1; - - /* initialize control mutex */ - crashlog = container_of(entry, struct crashlog_entry, entry); - mutex_init(&crashlog->control_mutex); - - header->access_type = GET_ACCESS(readl(disc_table)); - header->guid = readl(disc_table + GUID_OFFSET); - header->base_offset = readl(disc_table + BASE_OFFSET); - - /* Size is measured in DWORDS, but accessor returns bytes */ - header->size = GET_SIZE(readl(disc_table + SIZE_OFFSET)); - - return 0; -} - -static DEFINE_XARRAY_ALLOC(crashlog_array); -static struct intel_pmt_namespace pmt_crashlog_ns = { - .name = "crashlog", - .xa = &crashlog_array, - .attr_grp = &pmt_crashlog_group, - .pmt_header_decode = pmt_crashlog_header_decode, -}; - -/* - * initialization - */ -static int pmt_crashlog_remove(struct platform_device *pdev) -{ - struct pmt_crashlog_priv *priv = platform_get_drvdata(pdev); - int i; - - for (i = 0; i < priv->num_entries; i++) - intel_pmt_dev_destroy(&priv->entry[i].entry, &pmt_crashlog_ns); - - return 0; -} - -static int pmt_crashlog_probe(struct platform_device *pdev) -{ - struct pmt_crashlog_priv *priv; - size_t size; - int i, ret; - - size = struct_size(priv, entry, pdev->num_resources); - priv = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); - if (!priv) - return -ENOMEM; - - platform_set_drvdata(pdev, priv); - - for (i = 0; i < pdev->num_resources; i++) { - struct intel_pmt_entry *entry = &priv->entry[i].entry; - - ret = intel_pmt_dev_create(entry, &pmt_crashlog_ns, pdev, i); - if (ret < 0) - goto abort_probe; - if (ret) - continue; - - priv->num_entries++; - } - - return 0; -abort_probe: - pmt_crashlog_remove(pdev); - return ret; -} - -static struct platform_driver pmt_crashlog_driver = { - .driver = { - .name = DRV_NAME, - }, - .remove = pmt_crashlog_remove, - .probe = pmt_crashlog_probe, -}; - -static int __init pmt_crashlog_init(void) -{ - return platform_driver_register(&pmt_crashlog_driver); -} - -static void __exit pmt_crashlog_exit(void) -{ - platform_driver_unregister(&pmt_crashlog_driver); - xa_destroy(&crashlog_array); -} - -module_init(pmt_crashlog_init); -module_exit(pmt_crashlog_exit); - -MODULE_AUTHOR("Alexander Duyck "); -MODULE_DESCRIPTION("Intel PMT Crashlog driver"); -MODULE_ALIAS("platform:" DRV_NAME); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/platform/x86/intel_pmt_telemetry.c b/drivers/platform/x86/intel_pmt_telemetry.c deleted file mode 100644 index 9b95ef0504576..0000000000000 --- a/drivers/platform/x86/intel_pmt_telemetry.c +++ /dev/null @@ -1,140 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Intel Platform Monitory Technology Telemetry driver - * - * Copyright (c) 2020, Intel Corporation. - * All Rights Reserved. - * - * Author: "David E. Box" - */ - -#include -#include -#include -#include -#include -#include - -#include "intel_pmt_class.h" - -#define TELEM_DEV_NAME "pmt_telemetry" - -#define TELEM_SIZE_OFFSET 0x0 -#define TELEM_GUID_OFFSET 0x4 -#define TELEM_BASE_OFFSET 0x8 -#define TELEM_ACCESS(v) ((v) & GENMASK(3, 0)) -/* 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 - -struct pmt_telem_priv { - int num_entries; - struct intel_pmt_entry entry[]; -}; - -static bool pmt_telem_region_overlaps(struct intel_pmt_entry *entry, - struct device *dev) -{ - u32 guid = readl(entry->disc_table + TELEM_GUID_OFFSET); - - if (guid != TELEM_CLIENT_FIXED_BLOCK_GUID) - return false; - - return intel_pmt_is_early_client_hw(dev); -} - -static int pmt_telem_header_decode(struct intel_pmt_entry *entry, - struct intel_pmt_header *header, - struct device *dev) -{ - void __iomem *disc_table = entry->disc_table; - - if (pmt_telem_region_overlaps(entry, dev)) - return 1; - - header->access_type = TELEM_ACCESS(readl(disc_table)); - header->guid = readl(disc_table + TELEM_GUID_OFFSET); - header->base_offset = readl(disc_table + TELEM_BASE_OFFSET); - - /* Size is measured in DWORDS, but accessor returns bytes */ - header->size = TELEM_SIZE(readl(disc_table)); - - return 0; -} - -static DEFINE_XARRAY_ALLOC(telem_array); -static struct intel_pmt_namespace pmt_telem_ns = { - .name = "telem", - .xa = &telem_array, - .pmt_header_decode = pmt_telem_header_decode, -}; - -static int pmt_telem_remove(struct platform_device *pdev) -{ - struct pmt_telem_priv *priv = platform_get_drvdata(pdev); - int i; - - for (i = 0; i < priv->num_entries; i++) - intel_pmt_dev_destroy(&priv->entry[i], &pmt_telem_ns); - - return 0; -} - -static int pmt_telem_probe(struct platform_device *pdev) -{ - struct pmt_telem_priv *priv; - size_t size; - int i, ret; - - size = struct_size(priv, entry, pdev->num_resources); - priv = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); - if (!priv) - return -ENOMEM; - - platform_set_drvdata(pdev, priv); - - for (i = 0; i < pdev->num_resources; i++) { - struct intel_pmt_entry *entry = &priv->entry[i]; - - ret = intel_pmt_dev_create(entry, &pmt_telem_ns, pdev, i); - if (ret < 0) - goto abort_probe; - if (ret) - continue; - - priv->num_entries++; - } - - return 0; -abort_probe: - pmt_telem_remove(pdev); - return ret; -} - -static struct platform_driver pmt_telem_driver = { - .driver = { - .name = TELEM_DEV_NAME, - }, - .remove = pmt_telem_remove, - .probe = pmt_telem_probe, -}; - -static int __init pmt_telem_init(void) -{ - return platform_driver_register(&pmt_telem_driver); -} -module_init(pmt_telem_init); - -static void __exit pmt_telem_exit(void) -{ - platform_driver_unregister(&pmt_telem_driver); - xa_destroy(&telem_array); -} -module_exit(pmt_telem_exit); - -MODULE_AUTHOR("David E. Box "); -MODULE_DESCRIPTION("Intel PMT Telemetry driver"); -MODULE_ALIAS("platform:" TELEM_DEV_NAME); -MODULE_LICENSE("GPL v2");