Merge branch 'platform-drivers-x86-intel-pmt' into for-next

This commit is contained in:
Ilpo Järvinen 2026-10-13 11:46:29 +03:00
commit abecb32b8c
No known key found for this signature in database
GPG Key ID: 59AC4F6153E5CE31
6 changed files with 204 additions and 90 deletions

View File

@ -12,6 +12,7 @@
#include <linux/log2.h>
#include <linux/intel_vsec.h>
#include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/ioport.h>
#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/mm.h>
@ -100,7 +101,7 @@ intel_pmt_read(struct file *filp, struct kobject *kobj,
if (count > entry->size - off)
count = entry->size - off;
count = pmt_telem_read_mmio(entry->ep->dev, entry->cb, entry->header.guid, buf,
count = pmt_telem_read_mmio(entry->dev, entry->cb, entry->header.guid, buf,
entry->base, off, count);
return count;
@ -286,8 +287,6 @@ static int pmt_resolve_access_pci(struct intel_pmt_entry *entry,
return -EINVAL;
}
entry->pcidev = pci_dev;
return 0;
}
@ -365,6 +364,7 @@ static int intel_pmt_populate_entry(struct intel_pmt_entry *entry,
entry->guid = header->guid;
entry->size = header->size;
entry->cb = ivdev->priv_data;
entry->dev = ivdev->dev;
return 0;
}
@ -374,7 +374,6 @@ static int intel_pmt_dev_register(struct intel_pmt_entry *entry,
struct device *parent)
{
struct intel_vsec_device *ivdev = dev_to_ivdev(parent);
struct resource res = {0};
struct device *dev;
int ret;
@ -404,20 +403,32 @@ static int intel_pmt_dev_register(struct intel_pmt_entry *entry,
if (!entry->size)
return 0;
res.start = entry->base_addr;
res.end = res.start + entry->size - 1;
res.flags = IORESOURCE_MEM;
/*
* The read_telem callback is responsible for this mapping, and may have
* different requirements for use. If the callback is present do not
* create the map.
*/
if (!(entry->cb && entry->cb->read_telem)) {
struct resource res = DEFINE_RES_MEM(entry->base_addr, entry->size);
entry->base = devm_ioremap_resource(dev, &res);
if (IS_ERR(entry->base)) {
ret = PTR_ERR(entry->base);
goto fail_ioremap;
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;
/*
* The read_telem callback controls access to the memory area. Block
* usage if the read_telem callback is present.
*/
if (!(entry->cb && entry->cb->read_telem))
entry->pmt_bin_attr.mmap = intel_pmt_mmap;
entry->pmt_bin_attr.read = intel_pmt_read;
entry->pmt_bin_attr.size = entry->size;

View File

@ -20,7 +20,6 @@
#define GET_ADDRESS(v) ((v) & GENMASK(31, 3))
struct device;
struct pci_dev;
extern struct class intel_pmt_class;
struct telem_endpoint {
@ -42,14 +41,14 @@ struct intel_pmt_header {
struct intel_pmt_entry {
struct telem_endpoint *ep;
struct pci_dev *pcidev;
struct device *dev;
struct intel_pmt_header header;
u32 disc_header[PMT_DISC_DWORDS];
struct bin_attribute pmt_bin_attr;
const struct attribute_group *attr_grp;
struct kobject *kobj;
void __iomem *disc_table;
void __iomem *base;
void __iomem *base; /* unused if cb->read_telem is in use */
struct pmt_callbacks *cb;
unsigned long base_addr;
size_t size;

View File

@ -11,10 +11,12 @@
#include <linux/auxiliary_bus.h>
#include <linux/cleanup.h>
#include <linux/intel_vsec.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/pci.h>
#include <linux/printk.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/overflow.h>
@ -124,12 +126,36 @@ struct pmt_crashlog_priv {
* I/O
*/
static int pmt_crashlog_read_reg(struct intel_pmt_entry *entry, u32 *reg, u32 offset)
{
int ret;
*reg = 0;
if (entry->cb && entry->cb->read_reg) {
ret = entry->cb->read_reg(entry->dev, entry->header.guid, reg, offset);
if (ret) {
dev_err_ratelimited(entry->dev, "failed to read reg: %d\n", ret);
return ret;
}
} else {
*reg = readl(entry->disc_table + offset);
}
return 0;
}
/* Read, modify, write the control register, setting or clearing @bit based on @set */
static void pmt_crashlog_rmw(struct crashlog_entry *crashlog, u32 bit, bool set)
static int pmt_crashlog_rmw(struct crashlog_entry *crashlog, u32 bit, bool set)
{
const struct crashlog_control *control = &crashlog->info->control;
struct intel_pmt_entry *entry = &crashlog->entry;
u32 reg = readl(entry->disc_table + control->offset);
u32 reg;
int ret;
ret = pmt_crashlog_read_reg(entry, &reg, control->offset);
if (ret)
return ret;
reg &= ~control->trigger_mask;
@ -138,28 +164,46 @@ static void pmt_crashlog_rmw(struct crashlog_entry *crashlog, u32 bit, bool set)
else
reg &= ~bit;
writel(reg, entry->disc_table + control->offset);
if (entry->cb && entry->cb->write_reg) {
ret = entry->cb->write_reg(entry->dev, entry->header.guid, reg, control->offset);
if (ret) {
dev_err_ratelimited(entry->dev, "failed to write reg: %d\n", ret);
return ret;
}
} else {
writel(reg, entry->disc_table + control->offset);
}
return 0;
}
/* Read the status register and see if the specified @bit is set */
static bool pmt_crashlog_rc(struct crashlog_entry *crashlog, u32 bit)
static int pmt_crashlog_rc(struct crashlog_entry *crashlog, u32 bit, bool *state)
{
const struct crashlog_status *status = &crashlog->info->status;
u32 reg = readl(crashlog->entry.disc_table + status->offset);
struct intel_pmt_entry *entry = &crashlog->entry;
u32 reg;
int ret;
return !!(reg & bit);
ret = pmt_crashlog_read_reg(entry, &reg, status->offset);
if (ret)
return ret;
*state = !!(reg & bit);
return 0;
}
static bool pmt_crashlog_complete(struct crashlog_entry *crashlog)
static int pmt_crashlog_complete(struct crashlog_entry *crashlog, bool *state)
{
/* return current value of the crashlog complete flag */
return pmt_crashlog_rc(crashlog, crashlog->info->status.complete);
return pmt_crashlog_rc(crashlog, crashlog->info->status.complete, state);
}
static bool pmt_crashlog_disabled(struct crashlog_entry *crashlog)
static int pmt_crashlog_disabled(struct crashlog_entry *crashlog, bool *state)
{
/* return current value of the crashlog disabled flag */
return pmt_crashlog_rc(crashlog, crashlog->info->status.disabled);
return pmt_crashlog_rc(crashlog, crashlog->info->status.disabled, state);
}
static bool pmt_crashlog_supported(struct intel_pmt_entry *entry, u32 *crash_type, u32 *version)
@ -181,50 +225,50 @@ static bool pmt_crashlog_supported(struct intel_pmt_entry *entry, u32 *crash_typ
return false;
}
static void pmt_crashlog_set_disable(struct crashlog_entry *crashlog,
bool disable)
static int pmt_crashlog_set_disable(struct crashlog_entry *crashlog,
bool disable)
{
pmt_crashlog_rmw(crashlog, crashlog->info->control.disable, disable);
return pmt_crashlog_rmw(crashlog, crashlog->info->control.disable, disable);
}
static void pmt_crashlog_set_clear(struct crashlog_entry *crashlog)
static int pmt_crashlog_set_clear(struct crashlog_entry *crashlog)
{
pmt_crashlog_rmw(crashlog, crashlog->info->control.clear, true);
return pmt_crashlog_rmw(crashlog, crashlog->info->control.clear, true);
}
static void pmt_crashlog_set_execute(struct crashlog_entry *crashlog)
static int pmt_crashlog_set_execute(struct crashlog_entry *crashlog)
{
pmt_crashlog_rmw(crashlog, crashlog->info->control.manual, true);
return pmt_crashlog_rmw(crashlog, crashlog->info->control.manual, true);
}
static bool pmt_crashlog_cleared(struct crashlog_entry *crashlog)
static int pmt_crashlog_cleared(struct crashlog_entry *crashlog, bool *state)
{
return pmt_crashlog_rc(crashlog, crashlog->info->status.cleared);
return pmt_crashlog_rc(crashlog, crashlog->info->status.cleared, state);
}
static bool pmt_crashlog_consumed(struct crashlog_entry *crashlog)
static int pmt_crashlog_consumed(struct crashlog_entry *crashlog, bool *state)
{
return pmt_crashlog_rc(crashlog, crashlog->info->status.consumed);
return pmt_crashlog_rc(crashlog, crashlog->info->status.consumed, state);
}
static void pmt_crashlog_set_consumed(struct crashlog_entry *crashlog)
static int pmt_crashlog_set_consumed(struct crashlog_entry *crashlog)
{
pmt_crashlog_rmw(crashlog, crashlog->info->control.consume, true);
return pmt_crashlog_rmw(crashlog, crashlog->info->control.consume, true);
}
static bool pmt_crashlog_error(struct crashlog_entry *crashlog)
static int pmt_crashlog_error(struct crashlog_entry *crashlog, bool *state)
{
return pmt_crashlog_rc(crashlog, crashlog->info->status.error);
return pmt_crashlog_rc(crashlog, crashlog->info->status.error, state);
}
static bool pmt_crashlog_rearm(struct crashlog_entry *crashlog)
static int pmt_crashlog_rearm(struct crashlog_entry *crashlog, bool *state)
{
return pmt_crashlog_rc(crashlog, crashlog->info->status.rearmed);
return pmt_crashlog_rc(crashlog, crashlog->info->status.rearmed, state);
}
static void pmt_crashlog_set_rearm(struct crashlog_entry *crashlog)
static int pmt_crashlog_set_rearm(struct crashlog_entry *crashlog)
{
pmt_crashlog_rmw(crashlog, crashlog->info->control.rearm, true);
return pmt_crashlog_rmw(crashlog, crashlog->info->control.rearm, true);
}
/*
@ -234,7 +278,12 @@ static ssize_t
clear_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct crashlog_entry *crashlog = dev_get_drvdata(dev);
bool cleared = pmt_crashlog_cleared(crashlog);
bool cleared;
int ret;
ret = pmt_crashlog_cleared(crashlog, &cleared);
if (ret)
return ret;
return sysfs_emit(buf, "%d\n", cleared);
}
@ -245,13 +294,13 @@ clear_store(struct device *dev, struct device_attribute *attr,
{
struct crashlog_entry *crashlog;
bool clear;
int result;
int ret;
crashlog = dev_get_drvdata(dev);
result = kstrtobool(buf, &clear);
if (result)
return result;
ret = kstrtobool(buf, &clear);
if (ret)
return ret;
/* set bit only */
if (!clear)
@ -259,9 +308,9 @@ clear_store(struct device *dev, struct device_attribute *attr,
guard(mutex)(&crashlog->control_mutex);
pmt_crashlog_set_clear(crashlog);
ret = pmt_crashlog_set_clear(crashlog);
return count;
return ret ?: count;
}
static DEVICE_ATTR_RW(clear);
@ -269,7 +318,12 @@ static ssize_t
consumed_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct crashlog_entry *crashlog = dev_get_drvdata(dev);
bool consumed = pmt_crashlog_consumed(crashlog);
bool consumed;
int ret;
ret = pmt_crashlog_consumed(crashlog, &consumed);
if (ret)
return ret;
return sysfs_emit(buf, "%d\n", consumed);
}
@ -279,14 +333,16 @@ consumed_store(struct device *dev, struct device_attribute *attr, const char *bu
size_t count)
{
struct crashlog_entry *crashlog;
bool complete;
bool consumed;
int result;
bool disabled;
int ret;
crashlog = dev_get_drvdata(dev);
result = kstrtobool(buf, &consumed);
if (result)
return result;
ret = kstrtobool(buf, &consumed);
if (ret)
return ret;
/* set bit only */
if (!consumed)
@ -294,15 +350,21 @@ consumed_store(struct device *dev, struct device_attribute *attr, const char *bu
guard(mutex)(&crashlog->control_mutex);
if (pmt_crashlog_disabled(crashlog))
ret = pmt_crashlog_disabled(crashlog, &disabled);
if (ret)
return ret;
if (disabled)
return -EBUSY;
if (!pmt_crashlog_complete(crashlog))
ret = pmt_crashlog_complete(crashlog, &complete);
if (ret)
return ret;
if (!complete)
return -EEXIST;
pmt_crashlog_set_consumed(crashlog);
ret = pmt_crashlog_set_consumed(crashlog);
return count;
return ret ?: count;
}
static DEVICE_ATTR_RW(consumed);
@ -310,9 +372,14 @@ static ssize_t
enable_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct crashlog_entry *crashlog = dev_get_drvdata(dev);
bool enabled = !pmt_crashlog_disabled(crashlog);
bool disabled;
int ret;
return sprintf(buf, "%d\n", enabled);
ret = pmt_crashlog_disabled(crashlog, &disabled);
if (ret)
return ret;
return sysfs_emit(buf, "%d\n", !disabled);
}
static ssize_t
@ -321,19 +388,19 @@ enable_store(struct device *dev, struct device_attribute *attr,
{
struct crashlog_entry *crashlog;
bool enabled;
int result;
int ret;
crashlog = dev_get_drvdata(dev);
result = kstrtobool(buf, &enabled);
if (result)
return result;
ret = kstrtobool(buf, &enabled);
if (ret)
return ret;
guard(mutex)(&crashlog->control_mutex);
pmt_crashlog_set_disable(crashlog, !enabled);
ret = pmt_crashlog_set_disable(crashlog, !enabled);
return count;
return ret ?: count;
}
static DEVICE_ATTR_RW(enable);
@ -341,7 +408,12 @@ static ssize_t
error_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct crashlog_entry *crashlog = dev_get_drvdata(dev);
bool error = pmt_crashlog_error(crashlog);
bool error;
int ret;
ret = pmt_crashlog_error(crashlog, &error);
if (ret)
return ret;
return sysfs_emit(buf, "%d\n", error);
}
@ -351,7 +423,12 @@ static ssize_t
rearm_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct crashlog_entry *crashlog = dev_get_drvdata(dev);
int rearmed = pmt_crashlog_rearm(crashlog);
bool rearmed;
int ret;
ret = pmt_crashlog_rearm(crashlog, &rearmed);
if (ret)
return ret;
return sysfs_emit(buf, "%d\n", rearmed);
}
@ -362,13 +439,13 @@ rearm_store(struct device *dev, struct device_attribute *attr, const char *buf,
{
struct crashlog_entry *crashlog;
bool rearm;
int result;
int ret;
crashlog = dev_get_drvdata(dev);
result = kstrtobool(buf, &rearm);
if (result)
return result;
ret = kstrtobool(buf, &rearm);
if (ret)
return ret;
/* set only */
if (!rearm)
@ -376,9 +453,9 @@ rearm_store(struct device *dev, struct device_attribute *attr, const char *buf,
guard(mutex)(&crashlog->control_mutex);
pmt_crashlog_set_rearm(crashlog);
ret = pmt_crashlog_set_rearm(crashlog);
return count;
return ret ?: count;
}
static DEVICE_ATTR_RW(rearm);
@ -387,11 +464,15 @@ trigger_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct crashlog_entry *crashlog;
bool trigger;
int ret;
crashlog = dev_get_drvdata(dev);
trigger = pmt_crashlog_complete(crashlog);
return sprintf(buf, "%d\n", trigger);
ret = pmt_crashlog_complete(crashlog, &trigger);
if (ret)
return ret;
return sysfs_emit(buf, "%d\n", trigger);
}
static ssize_t
@ -399,33 +480,41 @@ trigger_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct crashlog_entry *crashlog;
bool complete;
bool disabled;
bool trigger;
int result;
int ret;
crashlog = dev_get_drvdata(dev);
result = kstrtobool(buf, &trigger);
if (result)
return result;
ret = kstrtobool(buf, &trigger);
if (ret)
return ret;
guard(mutex)(&crashlog->control_mutex);
/* if device is currently disabled, return busy */
if (pmt_crashlog_disabled(crashlog))
ret = pmt_crashlog_disabled(crashlog, &disabled);
if (ret)
return ret;
if (disabled)
return -EBUSY;
if (!trigger) {
pmt_crashlog_set_clear(crashlog);
return count;
ret = pmt_crashlog_set_clear(crashlog);
return ret ?: count;
}
/* we cannot trigger a new crash if one is still pending */
if (pmt_crashlog_complete(crashlog))
ret = pmt_crashlog_complete(crashlog, &complete);
if (ret)
return ret;
if (complete)
return -EEXIST;
pmt_crashlog_set_execute(crashlog);
ret = pmt_crashlog_set_execute(crashlog);
return count;
return ret ?: count;
}
static DEVICE_ATTR_RW(trigger);

View File

@ -609,7 +609,7 @@ void intel_pmt_get_features(struct intel_pmt_entry *entry)
mutex_lock(&feature_list_lock);
list_for_each_entry(feature, &pmt_feature_list, list) {
if (feature->priv->parent != entry->ep->dev)
if (feature->priv->parent != entry->dev)
continue;
pmt_get_features(entry, feature);

View File

@ -329,6 +329,9 @@ int pmt_telem_read32(struct telem_endpoint *ep, u32 id, u32 *data, u32 count)
if (!ep->present)
return -ENODEV;
if (!ep->base)
return -EIO;
offset = SAMPLE_ID_OFFSET32(id);
size = ep->header.size;

View File

@ -90,13 +90,25 @@ enum intel_vsec_quirks {
* @read_telem: when specified, called by client driver to access PMT
* data (instead of direct copy).
* * dev: device reference for the callback's use
* * guid: ID of data to acccss
* * guid: ID of data to access
* * data: buffer for the data to be copied
* * off: offset into the requested buffer
* * count: size of buffer
* @read_reg: when specified called by client driver to read PMT state
* * dev: device reference for the callback's use
* * guid: ID of data to access
* * reg_data: register data
* * offset: offset of register to read
* @write_reg: when specified called by client driver to write PMT state
* * dev: device reference for the callback's use
* * guid: ID of data to access
* * reg_data: register data
* * offset: offset of register to write
*/
struct pmt_callbacks {
int (*read_telem)(struct device *dev, u32 guid, u64 *data, loff_t off, u32 count);
int (*read_reg)(struct device *dev, u32 guid, u32 *reg_data, u32 offset);
int (*write_reg)(struct device *dev, u32 guid, u32 reg_data, u32 offset);
};
struct vsec_feature_dependency {