162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/* Copyright(c) 2022 Intel Corporation. */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/cpu.h>
562306a36Sopenharmony_ci#include <linux/delay.h>
662306a36Sopenharmony_ci#include <linux/fs.h>
762306a36Sopenharmony_ci#include <linux/semaphore.h>
862306a36Sopenharmony_ci#include <linux/slab.h>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include "ifs.h"
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci/*
1362306a36Sopenharmony_ci * Protects against simultaneous tests on multiple cores, or
1462306a36Sopenharmony_ci * reloading can file while a test is in progress
1562306a36Sopenharmony_ci */
1662306a36Sopenharmony_cistatic DEFINE_SEMAPHORE(ifs_sem, 1);
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci/*
1962306a36Sopenharmony_ci * The sysfs interface to check additional details of last test
2062306a36Sopenharmony_ci * cat /sys/devices/system/platform/ifs/details
2162306a36Sopenharmony_ci */
2262306a36Sopenharmony_cistatic ssize_t details_show(struct device *dev,
2362306a36Sopenharmony_ci			    struct device_attribute *attr,
2462306a36Sopenharmony_ci			    char *buf)
2562306a36Sopenharmony_ci{
2662306a36Sopenharmony_ci	struct ifs_data *ifsd = ifs_get_data(dev);
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	return sysfs_emit(buf, "%#llx\n", ifsd->scan_details);
2962306a36Sopenharmony_ci}
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic DEVICE_ATTR_RO(details);
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic const char * const status_msg[] = {
3462306a36Sopenharmony_ci	[SCAN_NOT_TESTED] = "untested",
3562306a36Sopenharmony_ci	[SCAN_TEST_PASS] = "pass",
3662306a36Sopenharmony_ci	[SCAN_TEST_FAIL] = "fail"
3762306a36Sopenharmony_ci};
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci/*
4062306a36Sopenharmony_ci * The sysfs interface to check the test status:
4162306a36Sopenharmony_ci * To check the status of last test
4262306a36Sopenharmony_ci * cat /sys/devices/platform/ifs/status
4362306a36Sopenharmony_ci */
4462306a36Sopenharmony_cistatic ssize_t status_show(struct device *dev,
4562306a36Sopenharmony_ci			   struct device_attribute *attr,
4662306a36Sopenharmony_ci			   char *buf)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	struct ifs_data *ifsd = ifs_get_data(dev);
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	return sysfs_emit(buf, "%s\n", status_msg[ifsd->status]);
5162306a36Sopenharmony_ci}
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistatic DEVICE_ATTR_RO(status);
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci/*
5662306a36Sopenharmony_ci * The sysfs interface for single core testing
5762306a36Sopenharmony_ci * To start test, for example, cpu5
5862306a36Sopenharmony_ci * echo 5 > /sys/devices/platform/ifs/run_test
5962306a36Sopenharmony_ci * To check the result:
6062306a36Sopenharmony_ci * cat /sys/devices/platform/ifs/result
6162306a36Sopenharmony_ci * The sibling core gets tested at the same time.
6262306a36Sopenharmony_ci */
6362306a36Sopenharmony_cistatic ssize_t run_test_store(struct device *dev,
6462306a36Sopenharmony_ci			      struct device_attribute *attr,
6562306a36Sopenharmony_ci			      const char *buf, size_t count)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	unsigned int cpu;
6862306a36Sopenharmony_ci	int rc;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	rc = kstrtouint(buf, 0, &cpu);
7162306a36Sopenharmony_ci	if (rc < 0 || cpu >= nr_cpu_ids)
7262306a36Sopenharmony_ci		return -EINVAL;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	if (down_interruptible(&ifs_sem))
7562306a36Sopenharmony_ci		return -EINTR;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	rc = do_core_test(cpu, dev);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	up(&ifs_sem);
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	return rc ? rc : count;
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic DEVICE_ATTR_WO(run_test);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic ssize_t current_batch_store(struct device *dev,
8762306a36Sopenharmony_ci				   struct device_attribute *attr,
8862306a36Sopenharmony_ci				   const char *buf, size_t count)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	struct ifs_data *ifsd = ifs_get_data(dev);
9162306a36Sopenharmony_ci	unsigned int cur_batch;
9262306a36Sopenharmony_ci	int rc;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	rc = kstrtouint(buf, 0, &cur_batch);
9562306a36Sopenharmony_ci	if (rc < 0 || cur_batch > 0xff)
9662306a36Sopenharmony_ci		return -EINVAL;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	if (down_interruptible(&ifs_sem))
9962306a36Sopenharmony_ci		return -EINTR;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	ifsd->cur_batch = cur_batch;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	rc = ifs_load_firmware(dev);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	up(&ifs_sem);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	return (rc == 0) ? count : rc;
10862306a36Sopenharmony_ci}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cistatic ssize_t current_batch_show(struct device *dev,
11162306a36Sopenharmony_ci				  struct device_attribute *attr, char *buf)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	struct ifs_data *ifsd = ifs_get_data(dev);
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	if (!ifsd->loaded)
11662306a36Sopenharmony_ci		return sysfs_emit(buf, "none\n");
11762306a36Sopenharmony_ci	else
11862306a36Sopenharmony_ci		return sysfs_emit(buf, "0x%02x\n", ifsd->cur_batch);
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic DEVICE_ATTR_RW(current_batch);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci/*
12462306a36Sopenharmony_ci * Display currently loaded IFS image version.
12562306a36Sopenharmony_ci */
12662306a36Sopenharmony_cistatic ssize_t image_version_show(struct device *dev,
12762306a36Sopenharmony_ci				  struct device_attribute *attr, char *buf)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	struct ifs_data *ifsd = ifs_get_data(dev);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	if (!ifsd->loaded)
13262306a36Sopenharmony_ci		return sysfs_emit(buf, "%s\n", "none");
13362306a36Sopenharmony_ci	else
13462306a36Sopenharmony_ci		return sysfs_emit(buf, "%#x\n", ifsd->loaded_version);
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic DEVICE_ATTR_RO(image_version);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci/* global scan sysfs attributes */
14062306a36Sopenharmony_cistruct attribute *plat_ifs_attrs[] = {
14162306a36Sopenharmony_ci	&dev_attr_details.attr,
14262306a36Sopenharmony_ci	&dev_attr_status.attr,
14362306a36Sopenharmony_ci	&dev_attr_run_test.attr,
14462306a36Sopenharmony_ci	&dev_attr_current_batch.attr,
14562306a36Sopenharmony_ci	&dev_attr_image_version.attr,
14662306a36Sopenharmony_ci	NULL
14762306a36Sopenharmony_ci};
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci/* global array sysfs attributes */
15062306a36Sopenharmony_cistruct attribute *plat_ifs_array_attrs[] = {
15162306a36Sopenharmony_ci	&dev_attr_details.attr,
15262306a36Sopenharmony_ci	&dev_attr_status.attr,
15362306a36Sopenharmony_ci	&dev_attr_run_test.attr,
15462306a36Sopenharmony_ci	NULL
15562306a36Sopenharmony_ci};
156