summaryrefslogtreecommitdiff
path: root/drivers/platform/x86/intel/ifs/sysfs.c
blob: 01b7502f46b09555c41b6e4cfbf26baceedb280f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright(c) 2022 Intel Corporation. */

#include <linux/cpu.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/semaphore.h>
#include <linux/slab.h>

#include "ifs.h"

/*
 * Protects against simultaneous tests on multiple cores, or
 * reloading can file while a test is in progress
 */
static DEFINE_SEMAPHORE(ifs_sem, 1);

/*
 * The sysfs interface to check additional details of last test
 * cat /sys/devices/system/platform/ifs/details
 */
static ssize_t details_show(struct device *dev,
			    struct device_attribute *attr,
			    char *buf)
{
	struct ifs_data *ifsd = ifs_get_data(dev);

	return sysfs_emit(buf, "%#llx\n", ifsd->scan_details);
}

static DEVICE_ATTR_RO(details);

static const char * const status_msg[] = {
	[SCAN_NOT_TESTED] = "untested",
	[SCAN_TEST_PASS] = "pass",
	[SCAN_TEST_FAIL] = "fail"
};

/*
 * The sysfs interface to check the test status:
 * To check the status of last test
 * cat /sys/devices/platform/ifs/status
 */
static ssize_t status_show(struct device *dev,
			   struct device_attribute *attr,
			   char *buf)
{
	struct ifs_data *ifsd = ifs_get_data(dev);

	return sysfs_emit(buf, "%s\n", status_msg[ifsd->status]);
}

static DEVICE_ATTR_RO(status);

/*
 * The sysfs interface for single core testing
 * To start test, for example, cpu5
 * echo 5 > /sys/devices/platform/ifs/run_test
 * To check the result:
 * cat /sys/devices/platform/ifs/result
 * The sibling core gets tested at the same time.
 */
static ssize_t run_test_store(struct device *dev,
			      struct device_attribute *attr,
			      const char *buf, size_t count)
{
	unsigned int cpu;
	int rc;

	rc = kstrtouint(buf, 0, &cpu);
	if (rc < 0 || cpu >= nr_cpu_ids)
		return -EINVAL;

	if (down_interruptible(&ifs_sem))
		return -EINTR;

	rc = do_core_test(cpu, dev);

	up(&ifs_sem);

	return rc ? rc : count;
}

static DEVICE_ATTR_WO(run_test);

static ssize_t current_batch_store(struct device *dev,
				   struct device_attribute *attr,
				   const char *buf, size_t count)
{
	struct ifs_data *ifsd = ifs_get_data(dev);
	unsigned int cur_batch;
	int rc;

	rc = kstrtouint(buf, 0, &cur_batch);
	if (rc < 0 || cur_batch > 0xff)
		return -EINVAL;

	if (down_interruptible(&ifs_sem))
		return -EINTR;

	ifsd->cur_batch = cur_batch;

	rc = ifs_load_firmware(dev);

	up(&ifs_sem);

	return (rc == 0) ? count : rc;
}

static ssize_t current_batch_show(struct device *dev,
				  struct device_attribute *attr, char *buf)
{
	struct ifs_data *ifsd = ifs_get_data(dev);

	if (!ifsd->loaded)
		return sysfs_emit(buf, "none\n");
	else
		return sysfs_emit(buf, "0x%02x\n", ifsd->cur_batch);
}

static DEVICE_ATTR_RW(current_batch);

/*
 * Display currently loaded IFS image version.
 */
static ssize_t image_version_show(struct device *dev,
				  struct device_attribute *attr, char *buf)
{
	struct ifs_data *ifsd = ifs_get_data(dev);

	if (!ifsd->loaded)
		return sysfs_emit(buf, "%s\n", "none");
	else
		return sysfs_emit(buf, "%#x\n", ifsd->loaded_version);
}

static DEVICE_ATTR_RO(image_version);

/* global scan sysfs attributes */
struct attribute *plat_ifs_attrs[] = {
	&dev_attr_details.attr,
	&dev_attr_status.attr,
	&dev_attr_run_test.attr,
	&dev_attr_current_batch.attr,
	&dev_attr_image_version.attr,
	NULL
};

/* global array sysfs attributes */
struct attribute *plat_ifs_array_attrs[] = {
	&dev_attr_details.attr,
	&dev_attr_status.attr,
	&dev_attr_run_test.attr,
	NULL
};