1 // SPDX-License-Identifier: GPL-2.0
3 * Intel Platform Monitoring Technology Crashlog driver
5 * Copyright (c) 2020, Intel Corporation.
8 * Author: "Alexander Duyck" <alexander.h.duyck@linux.intel.com>
11 #include <linux/auxiliary_bus.h>
12 #include <linux/intel_vsec.h>
13 #include <linux/kernel.h>
14 #include <linux/module.h>
15 #include <linux/pci.h>
16 #include <linux/slab.h>
17 #include <linux/uaccess.h>
18 #include <linux/overflow.h>
22 /* Crashlog discovery header types */
23 #define CRASH_TYPE_OOBMSM 1
26 #define CRASHLOG_FLAG_DISABLE BIT(28)
29 * Bits 29 and 30 control the state of bit 31.
31 * Bit 29 will clear bit 31, if set, allowing a new crashlog to be captured.
32 * Bit 30 will immediately trigger a crashlog to be generated, setting bit 31.
33 * Bit 31 is the read-only status with a 1 indicating log is complete.
35 #define CRASHLOG_FLAG_TRIGGER_CLEAR BIT(29)
36 #define CRASHLOG_FLAG_TRIGGER_EXECUTE BIT(30)
37 #define CRASHLOG_FLAG_TRIGGER_COMPLETE BIT(31)
38 #define CRASHLOG_FLAG_TRIGGER_MASK GENMASK(31, 28)
40 /* Crashlog Discovery Header */
41 #define CONTROL_OFFSET 0x0
42 #define GUID_OFFSET 0x4
43 #define BASE_OFFSET 0x8
44 #define SIZE_OFFSET 0xC
45 #define GET_ACCESS(v) ((v) & GENMASK(3, 0))
46 #define GET_TYPE(v) (((v) & GENMASK(7, 4)) >> 4)
47 #define GET_VERSION(v) (((v) & GENMASK(19, 16)) >> 16)
48 /* size is in bytes */
49 #define GET_SIZE(v) ((v) * sizeof(u32))
51 struct crashlog_entry
{
52 /* entry must be first member of struct */
53 struct intel_pmt_entry entry
;
54 struct mutex control_mutex
;
57 struct pmt_crashlog_priv
{
59 struct crashlog_entry entry
[];
65 static bool pmt_crashlog_complete(struct intel_pmt_entry
*entry
)
67 u32 control
= readl(entry
->disc_table
+ CONTROL_OFFSET
);
69 /* return current value of the crashlog complete flag */
70 return !!(control
& CRASHLOG_FLAG_TRIGGER_COMPLETE
);
73 static bool pmt_crashlog_disabled(struct intel_pmt_entry
*entry
)
75 u32 control
= readl(entry
->disc_table
+ CONTROL_OFFSET
);
77 /* return current value of the crashlog disabled flag */
78 return !!(control
& CRASHLOG_FLAG_DISABLE
);
81 static bool pmt_crashlog_supported(struct intel_pmt_entry
*entry
)
83 u32 discovery_header
= readl(entry
->disc_table
+ CONTROL_OFFSET
);
84 u32 crash_type
, version
;
86 crash_type
= GET_TYPE(discovery_header
);
87 version
= GET_VERSION(discovery_header
);
90 * Currently we only recognize OOBMSM version 0 devices.
91 * We can ignore all other crashlog devices in the system.
93 return crash_type
== CRASH_TYPE_OOBMSM
&& version
== 0;
96 static void pmt_crashlog_set_disable(struct intel_pmt_entry
*entry
,
99 u32 control
= readl(entry
->disc_table
+ CONTROL_OFFSET
);
101 /* clear trigger bits so we are only modifying disable flag */
102 control
&= ~CRASHLOG_FLAG_TRIGGER_MASK
;
105 control
|= CRASHLOG_FLAG_DISABLE
;
107 control
&= ~CRASHLOG_FLAG_DISABLE
;
109 writel(control
, entry
->disc_table
+ CONTROL_OFFSET
);
112 static void pmt_crashlog_set_clear(struct intel_pmt_entry
*entry
)
114 u32 control
= readl(entry
->disc_table
+ CONTROL_OFFSET
);
116 control
&= ~CRASHLOG_FLAG_TRIGGER_MASK
;
117 control
|= CRASHLOG_FLAG_TRIGGER_CLEAR
;
119 writel(control
, entry
->disc_table
+ CONTROL_OFFSET
);
122 static void pmt_crashlog_set_execute(struct intel_pmt_entry
*entry
)
124 u32 control
= readl(entry
->disc_table
+ CONTROL_OFFSET
);
126 control
&= ~CRASHLOG_FLAG_TRIGGER_MASK
;
127 control
|= CRASHLOG_FLAG_TRIGGER_EXECUTE
;
129 writel(control
, entry
->disc_table
+ CONTROL_OFFSET
);
136 enable_show(struct device
*dev
, struct device_attribute
*attr
, char *buf
)
138 struct intel_pmt_entry
*entry
= dev_get_drvdata(dev
);
139 int enabled
= !pmt_crashlog_disabled(entry
);
141 return sprintf(buf
, "%d\n", enabled
);
145 enable_store(struct device
*dev
, struct device_attribute
*attr
,
146 const char *buf
, size_t count
)
148 struct crashlog_entry
*entry
;
152 entry
= dev_get_drvdata(dev
);
154 result
= kstrtobool(buf
, &enabled
);
158 mutex_lock(&entry
->control_mutex
);
159 pmt_crashlog_set_disable(&entry
->entry
, !enabled
);
160 mutex_unlock(&entry
->control_mutex
);
164 static DEVICE_ATTR_RW(enable
);
167 trigger_show(struct device
*dev
, struct device_attribute
*attr
, char *buf
)
169 struct intel_pmt_entry
*entry
;
172 entry
= dev_get_drvdata(dev
);
173 trigger
= pmt_crashlog_complete(entry
);
175 return sprintf(buf
, "%d\n", trigger
);
179 trigger_store(struct device
*dev
, struct device_attribute
*attr
,
180 const char *buf
, size_t count
)
182 struct crashlog_entry
*entry
;
186 entry
= dev_get_drvdata(dev
);
188 result
= kstrtobool(buf
, &trigger
);
192 mutex_lock(&entry
->control_mutex
);
195 pmt_crashlog_set_clear(&entry
->entry
);
196 } else if (pmt_crashlog_complete(&entry
->entry
)) {
197 /* we cannot trigger a new crash if one is still pending */
200 } else if (pmt_crashlog_disabled(&entry
->entry
)) {
201 /* if device is currently disabled, return busy */
205 pmt_crashlog_set_execute(&entry
->entry
);
210 mutex_unlock(&entry
->control_mutex
);
213 static DEVICE_ATTR_RW(trigger
);
215 static struct attribute
*pmt_crashlog_attrs
[] = {
216 &dev_attr_enable
.attr
,
217 &dev_attr_trigger
.attr
,
221 static const struct attribute_group pmt_crashlog_group
= {
222 .attrs
= pmt_crashlog_attrs
,
225 static int pmt_crashlog_header_decode(struct intel_pmt_entry
*entry
,
228 void __iomem
*disc_table
= entry
->disc_table
;
229 struct intel_pmt_header
*header
= &entry
->header
;
230 struct crashlog_entry
*crashlog
;
232 if (!pmt_crashlog_supported(entry
))
235 /* initialize control mutex */
236 crashlog
= container_of(entry
, struct crashlog_entry
, entry
);
237 mutex_init(&crashlog
->control_mutex
);
239 header
->access_type
= GET_ACCESS(readl(disc_table
));
240 header
->guid
= readl(disc_table
+ GUID_OFFSET
);
241 header
->base_offset
= readl(disc_table
+ BASE_OFFSET
);
243 /* Size is measured in DWORDS, but accessor returns bytes */
244 header
->size
= GET_SIZE(readl(disc_table
+ SIZE_OFFSET
));
249 static DEFINE_XARRAY_ALLOC(crashlog_array
);
250 static struct intel_pmt_namespace pmt_crashlog_ns
= {
252 .xa
= &crashlog_array
,
253 .attr_grp
= &pmt_crashlog_group
,
254 .pmt_header_decode
= pmt_crashlog_header_decode
,
260 static void pmt_crashlog_remove(struct auxiliary_device
*auxdev
)
262 struct pmt_crashlog_priv
*priv
= auxiliary_get_drvdata(auxdev
);
265 for (i
= 0; i
< priv
->num_entries
; i
++)
266 intel_pmt_dev_destroy(&priv
->entry
[i
].entry
, &pmt_crashlog_ns
);
269 static int pmt_crashlog_probe(struct auxiliary_device
*auxdev
,
270 const struct auxiliary_device_id
*id
)
272 struct intel_vsec_device
*intel_vsec_dev
= auxdev_to_ivdev(auxdev
);
273 struct pmt_crashlog_priv
*priv
;
277 size
= struct_size(priv
, entry
, intel_vsec_dev
->num_resources
);
278 priv
= devm_kzalloc(&auxdev
->dev
, size
, GFP_KERNEL
);
282 auxiliary_set_drvdata(auxdev
, priv
);
284 for (i
= 0; i
< intel_vsec_dev
->num_resources
; i
++) {
285 struct intel_pmt_entry
*entry
= &priv
->entry
[priv
->num_entries
].entry
;
287 ret
= intel_pmt_dev_create(entry
, &pmt_crashlog_ns
, intel_vsec_dev
, i
);
298 pmt_crashlog_remove(auxdev
);
302 static const struct auxiliary_device_id pmt_crashlog_id_table
[] = {
303 { .name
= "intel_vsec.crashlog" },
306 MODULE_DEVICE_TABLE(auxiliary
, pmt_crashlog_id_table
);
308 static struct auxiliary_driver pmt_crashlog_aux_driver
= {
309 .id_table
= pmt_crashlog_id_table
,
310 .remove
= pmt_crashlog_remove
,
311 .probe
= pmt_crashlog_probe
,
314 static int __init
pmt_crashlog_init(void)
316 return auxiliary_driver_register(&pmt_crashlog_aux_driver
);
319 static void __exit
pmt_crashlog_exit(void)
321 auxiliary_driver_unregister(&pmt_crashlog_aux_driver
);
322 xa_destroy(&crashlog_array
);
325 module_init(pmt_crashlog_init
);
326 module_exit(pmt_crashlog_exit
);
328 MODULE_AUTHOR("Alexander Duyck <alexander.h.duyck@linux.intel.com>");
329 MODULE_DESCRIPTION("Intel PMT Crashlog driver");
330 MODULE_LICENSE("GPL v2");
331 MODULE_IMPORT_NS("INTEL_PMT");