1// SPDX-License-Identifier: GPL-2.0+
2/*
3 *  Copyright 2013 Matthew Garrett <mjg59@srcf.ucam.org>
4 */
5
6#include <linux/acpi.h>
7#include <linux/module.h>
8#include <linux/slab.h>
9
10MODULE_LICENSE("GPL");
11
12static ssize_t irst_show_wakeup_events(struct device *dev,
13				       struct device_attribute *attr,
14				       char *buf)
15{
16	struct acpi_device *acpi;
17	unsigned long long value;
18	acpi_status status;
19
20	acpi = to_acpi_device(dev);
21
22	status = acpi_evaluate_integer(acpi->handle, "GFFS", NULL, &value);
23	if (ACPI_FAILURE(status))
24		return -EINVAL;
25
26	return sprintf(buf, "%lld\n", value);
27}
28
29static ssize_t irst_store_wakeup_events(struct device *dev,
30					struct device_attribute *attr,
31					const char *buf, size_t count)
32{
33	struct acpi_device *acpi;
34	acpi_status status;
35	unsigned long value;
36	int error;
37
38	acpi = to_acpi_device(dev);
39
40	error = kstrtoul(buf, 0, &value);
41	if (error)
42		return error;
43
44	status = acpi_execute_simple_method(acpi->handle, "SFFS", value);
45	if (ACPI_FAILURE(status))
46		return -EINVAL;
47
48	return count;
49}
50
51static struct device_attribute irst_wakeup_attr = {
52	.attr = { .name = "wakeup_events", .mode = 0600 },
53	.show = irst_show_wakeup_events,
54	.store = irst_store_wakeup_events
55};
56
57static ssize_t irst_show_wakeup_time(struct device *dev,
58				     struct device_attribute *attr, char *buf)
59{
60	struct acpi_device *acpi;
61	unsigned long long value;
62	acpi_status status;
63
64	acpi = to_acpi_device(dev);
65
66	status = acpi_evaluate_integer(acpi->handle, "GFTV", NULL, &value);
67	if (ACPI_FAILURE(status))
68		return -EINVAL;
69
70	return sprintf(buf, "%lld\n", value);
71}
72
73static ssize_t irst_store_wakeup_time(struct device *dev,
74				      struct device_attribute *attr,
75				      const char *buf, size_t count)
76{
77	struct acpi_device *acpi;
78	acpi_status status;
79	unsigned long value;
80	int error;
81
82	acpi = to_acpi_device(dev);
83
84	error = kstrtoul(buf, 0, &value);
85	if (error)
86		return error;
87
88	status = acpi_execute_simple_method(acpi->handle, "SFTV", value);
89	if (ACPI_FAILURE(status))
90		return -EINVAL;
91
92	return count;
93}
94
95static struct device_attribute irst_timeout_attr = {
96	.attr = { .name = "wakeup_time", .mode = 0600 },
97	.show = irst_show_wakeup_time,
98	.store = irst_store_wakeup_time
99};
100
101static int irst_add(struct acpi_device *acpi)
102{
103	int error;
104
105	error = device_create_file(&acpi->dev, &irst_timeout_attr);
106	if (unlikely(error))
107		return error;
108
109	error = device_create_file(&acpi->dev, &irst_wakeup_attr);
110	if (unlikely(error))
111		device_remove_file(&acpi->dev, &irst_timeout_attr);
112
113	return error;
114}
115
116static void irst_remove(struct acpi_device *acpi)
117{
118	device_remove_file(&acpi->dev, &irst_wakeup_attr);
119	device_remove_file(&acpi->dev, &irst_timeout_attr);
120}
121
122static const struct acpi_device_id irst_ids[] = {
123	{"INT3392", 0},
124	{"", 0}
125};
126
127static struct acpi_driver irst_driver = {
128	.name = "intel_rapid_start",
129	.class = "intel_rapid_start",
130	.ids = irst_ids,
131	.ops = {
132		.add = irst_add,
133		.remove = irst_remove,
134	},
135};
136
137module_acpi_driver(irst_driver);
138
139MODULE_DEVICE_TABLE(acpi, irst_ids);
140