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/kernel.h>
12 #include <linux/module.h>
13 #include <linux/pci.h>
14 #include <linux/slab.h>
15 #include <linux/uaccess.h>
16 #include <linux/overflow.h>
18 #include "intel_pmt_class.h"
20 #define DRV_NAME "pmt_crashlog"
22 /* Crashlog discovery header types */
23 #define CRASH_TYPE_OOBMSM 1
26 #define CRASHLOG_FLAG_DISABLE BIT(27)
29 * Bits 28 and 29 control the state of bit 31.
31 * Bit 28 will clear bit 31, if set, allowing a new crashlog to be captured.
32 * Bit 29 will immediately trigger a crashlog to be generated, setting bit 31.
33 * Bit 30 is read-only and reserved as 0.
34 * Bit 31 is the read-only status with a 1 indicating log is complete.
36 #define CRASHLOG_FLAG_TRIGGER_CLEAR BIT(28)
37 #define CRASHLOG_FLAG_TRIGGER_EXECUTE BIT(29)
38 #define CRASHLOG_FLAG_TRIGGER_COMPLETE BIT(31)
39 #define CRASHLOG_FLAG_TRIGGER_MASK GENMASK(31, 28)
41 /* Crashlog Discovery Header */
42 #define CONTROL_OFFSET 0x0
43 #define GUID_OFFSET 0x4
44 #define BASE_OFFSET 0x8
45 #define SIZE_OFFSET 0xC
46 #define GET_ACCESS(v) ((v) & GENMASK(3, 0))
47 #define GET_TYPE(v) (((v) & GENMASK(7, 4)) >> 4)
48 #define GET_VERSION(v) (((v) & GENMASK(19, 16)) >> 16)
49 /* size is in bytes */
50 #define GET_SIZE(v) ((v) * sizeof(u32))
52 struct crashlog_entry
{
53 /* entry must be first member of struct */
54 struct intel_pmt_entry entry
;
55 struct mutex control_mutex
;
58 struct pmt_crashlog_priv
{
60 struct crashlog_entry entry
[];
66 static bool pmt_crashlog_complete(struct intel_pmt_entry
*entry
)
68 u32 control
= readl(entry
->disc_table
+ CONTROL_OFFSET
);
70 /* return current value of the crashlog complete flag */
71 return !!(control
& CRASHLOG_FLAG_TRIGGER_COMPLETE
);
74 static bool pmt_crashlog_disabled(struct intel_pmt_entry
*entry
)
76 u32 control
= readl(entry
->disc_table
+ CONTROL_OFFSET
);
78 /* return current value of the crashlog disabled flag */
79 return !!(control
& CRASHLOG_FLAG_DISABLE
);
82 static bool pmt_crashlog_supported(struct intel_pmt_entry
*entry
)
84 u32 discovery_header
= readl(entry
->disc_table
+ CONTROL_OFFSET
);
85 u32 crash_type
, version
;
87 crash_type
= GET_TYPE(discovery_header
);
88 version
= GET_VERSION(discovery_header
);
91 * Currently we only recognize OOBMSM version 0 devices.
92 * We can ignore all other crashlog devices in the system.
94 return crash_type
== CRASH_TYPE_OOBMSM
&& version
== 0;
97 static void pmt_crashlog_set_disable(struct intel_pmt_entry
*entry
,
100 u32 control
= readl(entry
->disc_table
+ CONTROL_OFFSET
);
102 /* clear trigger bits so we are only modifying disable flag */
103 control
&= ~CRASHLOG_FLAG_TRIGGER_MASK
;
106 control
|= CRASHLOG_FLAG_DISABLE
;
108 control
&= ~CRASHLOG_FLAG_DISABLE
;
110 writel(control
, entry
->disc_table
+ CONTROL_OFFSET
);
113 static void pmt_crashlog_set_clear(struct intel_pmt_entry
*entry
)
115 u32 control
= readl(entry
->disc_table
+ CONTROL_OFFSET
);
117 control
&= ~CRASHLOG_FLAG_TRIGGER_MASK
;
118 control
|= CRASHLOG_FLAG_TRIGGER_CLEAR
;
120 writel(control
, entry
->disc_table
+ CONTROL_OFFSET
);
123 static void pmt_crashlog_set_execute(struct intel_pmt_entry
*entry
)
125 u32 control
= readl(entry
->disc_table
+ CONTROL_OFFSET
);
127 control
&= ~CRASHLOG_FLAG_TRIGGER_MASK
;
128 control
|= CRASHLOG_FLAG_TRIGGER_EXECUTE
;
130 writel(control
, entry
->disc_table
+ CONTROL_OFFSET
);
137 enable_show(struct device
*dev
, struct device_attribute
*attr
, char *buf
)
139 struct intel_pmt_entry
*entry
= dev_get_drvdata(dev
);
140 int enabled
= !pmt_crashlog_disabled(entry
);
142 return sprintf(buf
, "%d\n", enabled
);
146 enable_store(struct device
*dev
, struct device_attribute
*attr
,
147 const char *buf
, size_t count
)
149 struct crashlog_entry
*entry
;
153 entry
= dev_get_drvdata(dev
);
155 result
= kstrtobool(buf
, &enabled
);
159 mutex_lock(&entry
->control_mutex
);
160 pmt_crashlog_set_disable(&entry
->entry
, !enabled
);
161 mutex_unlock(&entry
->control_mutex
);
165 static DEVICE_ATTR_RW(enable
);
168 trigger_show(struct device
*dev
, struct device_attribute
*attr
, char *buf
)
170 struct intel_pmt_entry
*entry
;
173 entry
= dev_get_drvdata(dev
);
174 trigger
= pmt_crashlog_complete(entry
);
176 return sprintf(buf
, "%d\n", trigger
);
180 trigger_store(struct device
*dev
, struct device_attribute
*attr
,
181 const char *buf
, size_t count
)
183 struct crashlog_entry
*entry
;
187 entry
= dev_get_drvdata(dev
);
189 result
= kstrtobool(buf
, &trigger
);
193 mutex_lock(&entry
->control_mutex
);
196 pmt_crashlog_set_clear(&entry
->entry
);
197 } else if (pmt_crashlog_complete(&entry
->entry
)) {
198 /* we cannot trigger a new crash if one is still pending */
201 } else if (pmt_crashlog_disabled(&entry
->entry
)) {
202 /* if device is currently disabled, return busy */
206 pmt_crashlog_set_execute(&entry
->entry
);
211 mutex_unlock(&entry
->control_mutex
);
214 static DEVICE_ATTR_RW(trigger
);
216 static struct attribute
*pmt_crashlog_attrs
[] = {
217 &dev_attr_enable
.attr
,
218 &dev_attr_trigger
.attr
,
222 static struct attribute_group pmt_crashlog_group
= {
223 .attrs
= pmt_crashlog_attrs
,
226 static int pmt_crashlog_header_decode(struct intel_pmt_entry
*entry
,
227 struct intel_pmt_header
*header
,
230 void __iomem
*disc_table
= entry
->disc_table
;
231 struct crashlog_entry
*crashlog
;
233 if (!pmt_crashlog_supported(entry
))
236 /* initialize control mutex */
237 crashlog
= container_of(entry
, struct crashlog_entry
, entry
);
238 mutex_init(&crashlog
->control_mutex
);
240 header
->access_type
= GET_ACCESS(readl(disc_table
));
241 header
->guid
= readl(disc_table
+ GUID_OFFSET
);
242 header
->base_offset
= readl(disc_table
+ BASE_OFFSET
);
244 /* Size is measured in DWORDS, but accessor returns bytes */
245 header
->size
= GET_SIZE(readl(disc_table
+ SIZE_OFFSET
));
250 static DEFINE_XARRAY_ALLOC(crashlog_array
);
251 static struct intel_pmt_namespace pmt_crashlog_ns
= {
253 .xa
= &crashlog_array
,
254 .attr_grp
= &pmt_crashlog_group
,
255 .pmt_header_decode
= pmt_crashlog_header_decode
,
261 static int pmt_crashlog_remove(struct platform_device
*pdev
)
263 struct pmt_crashlog_priv
*priv
= platform_get_drvdata(pdev
);
266 for (i
= 0; i
< priv
->num_entries
; i
++)
267 intel_pmt_dev_destroy(&priv
->entry
[i
].entry
, &pmt_crashlog_ns
);
272 static int pmt_crashlog_probe(struct platform_device
*pdev
)
274 struct pmt_crashlog_priv
*priv
;
278 size
= struct_size(priv
, entry
, pdev
->num_resources
);
279 priv
= devm_kzalloc(&pdev
->dev
, size
, GFP_KERNEL
);
283 platform_set_drvdata(pdev
, priv
);
285 for (i
= 0; i
< pdev
->num_resources
; i
++) {
286 struct intel_pmt_entry
*entry
= &priv
->entry
[i
].entry
;
288 ret
= intel_pmt_dev_create(entry
, &pmt_crashlog_ns
, pdev
, i
);
299 pmt_crashlog_remove(pdev
);
303 static struct platform_driver pmt_crashlog_driver
= {
307 .remove
= pmt_crashlog_remove
,
308 .probe
= pmt_crashlog_probe
,
311 static int __init
pmt_crashlog_init(void)
313 return platform_driver_register(&pmt_crashlog_driver
);
316 static void __exit
pmt_crashlog_exit(void)
318 platform_driver_unregister(&pmt_crashlog_driver
);
319 xa_destroy(&crashlog_array
);
322 module_init(pmt_crashlog_init
);
323 module_exit(pmt_crashlog_exit
);
325 MODULE_AUTHOR("Alexander Duyck <alexander.h.duyck@linux.intel.com>");
326 MODULE_DESCRIPTION("Intel PMT Crashlog driver");
327 MODULE_ALIAS("platform:" DRV_NAME
);
328 MODULE_LICENSE("GPL v2");