xref: /linux/drivers/acpi/acpi_tad.c (revision 95c513ec84f7ff35cd9d8c7ce4a99f770bd82c3b)
1*95c513ecSRafael J. Wysocki // SPDX-License-Identifier: GPL-2.0
2*95c513ecSRafael J. Wysocki /*
3*95c513ecSRafael J. Wysocki  * ACPI Time and Alarm (TAD) Device Driver
4*95c513ecSRafael J. Wysocki  *
5*95c513ecSRafael J. Wysocki  * Copyright (C) 2018 Intel Corporation
6*95c513ecSRafael J. Wysocki  * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
7*95c513ecSRafael J. Wysocki  *
8*95c513ecSRafael J. Wysocki  * This driver is based on Section 9.18 of the ACPI 6.2 specification revision.
9*95c513ecSRafael J. Wysocki  *
10*95c513ecSRafael J. Wysocki  * It only supports the system wakeup capabilities of the TAD.
11*95c513ecSRafael J. Wysocki  *
12*95c513ecSRafael J. Wysocki  * Provided are sysfs attributes, available under the TAD platform device,
13*95c513ecSRafael J. Wysocki  * allowing user space to manage the AC and DC wakeup timers of the TAD:
14*95c513ecSRafael J. Wysocki  * set and read their values, set and check their expire timer wake policies,
15*95c513ecSRafael J. Wysocki  * check and clear their status and check the capabilities of the TAD reported
16*95c513ecSRafael J. Wysocki  * by AML.  The DC timer attributes are only present if the TAD supports a
17*95c513ecSRafael J. Wysocki  * separate DC alarm timer.
18*95c513ecSRafael J. Wysocki  *
19*95c513ecSRafael J. Wysocki  * The wakeup events handling and power management of the TAD is expected to
20*95c513ecSRafael J. Wysocki  * be taken care of by the ACPI PM domain attached to its platform device.
21*95c513ecSRafael J. Wysocki  */
22*95c513ecSRafael J. Wysocki 
23*95c513ecSRafael J. Wysocki #include <linux/acpi.h>
24*95c513ecSRafael J. Wysocki #include <linux/kernel.h>
25*95c513ecSRafael J. Wysocki #include <linux/module.h>
26*95c513ecSRafael J. Wysocki #include <linux/platform_device.h>
27*95c513ecSRafael J. Wysocki #include <linux/pm_runtime.h>
28*95c513ecSRafael J. Wysocki #include <linux/suspend.h>
29*95c513ecSRafael J. Wysocki 
30*95c513ecSRafael J. Wysocki MODULE_LICENSE("GPL v2");
31*95c513ecSRafael J. Wysocki MODULE_AUTHOR("Rafael J. Wysocki");
32*95c513ecSRafael J. Wysocki 
33*95c513ecSRafael J. Wysocki /* ACPI TAD capability flags (ACPI 6.2, Section 9.18.2) */
34*95c513ecSRafael J. Wysocki #define ACPI_TAD_AC_WAKE	BIT(0)
35*95c513ecSRafael J. Wysocki #define ACPI_TAD_DC_WAKE	BIT(1)
36*95c513ecSRafael J. Wysocki #define ACPI_TAD_RT		BIT(2)
37*95c513ecSRafael J. Wysocki #define ACPI_TAD_RT_IN_MS	BIT(3)
38*95c513ecSRafael J. Wysocki #define ACPI_TAD_S4_S5__GWS	BIT(4)
39*95c513ecSRafael J. Wysocki #define ACPI_TAD_AC_S4_WAKE	BIT(5)
40*95c513ecSRafael J. Wysocki #define ACPI_TAD_AC_S5_WAKE	BIT(6)
41*95c513ecSRafael J. Wysocki #define ACPI_TAD_DC_S4_WAKE	BIT(7)
42*95c513ecSRafael J. Wysocki #define ACPI_TAD_DC_S5_WAKE	BIT(8)
43*95c513ecSRafael J. Wysocki 
44*95c513ecSRafael J. Wysocki /* ACPI TAD alarm timer selection */
45*95c513ecSRafael J. Wysocki #define ACPI_TAD_AC_TIMER	(u32)0
46*95c513ecSRafael J. Wysocki #define ACPI_TAD_DC_TIMER	(u32)1
47*95c513ecSRafael J. Wysocki 
48*95c513ecSRafael J. Wysocki /* Special value for disabled timer or expired timer wake policy. */
49*95c513ecSRafael J. Wysocki #define ACPI_TAD_WAKE_DISABLED	(~(u32)0)
50*95c513ecSRafael J. Wysocki 
51*95c513ecSRafael J. Wysocki struct acpi_tad_driver_data {
52*95c513ecSRafael J. Wysocki 	u32 capabilities;
53*95c513ecSRafael J. Wysocki };
54*95c513ecSRafael J. Wysocki 
55*95c513ecSRafael J. Wysocki static int acpi_tad_wake_set(struct device *dev, char *method, u32 timer_id,
56*95c513ecSRafael J. Wysocki 			     u32 value)
57*95c513ecSRafael J. Wysocki {
58*95c513ecSRafael J. Wysocki 	acpi_handle handle = ACPI_HANDLE(dev);
59*95c513ecSRafael J. Wysocki 	union acpi_object args[] = {
60*95c513ecSRafael J. Wysocki 		{ .type = ACPI_TYPE_INTEGER, },
61*95c513ecSRafael J. Wysocki 		{ .type = ACPI_TYPE_INTEGER, },
62*95c513ecSRafael J. Wysocki 	};
63*95c513ecSRafael J. Wysocki 	struct acpi_object_list arg_list = {
64*95c513ecSRafael J. Wysocki 		.pointer = args,
65*95c513ecSRafael J. Wysocki 		.count = ARRAY_SIZE(args),
66*95c513ecSRafael J. Wysocki 	};
67*95c513ecSRafael J. Wysocki 	unsigned long long retval;
68*95c513ecSRafael J. Wysocki 	acpi_status status;
69*95c513ecSRafael J. Wysocki 
70*95c513ecSRafael J. Wysocki 	args[0].integer.value = timer_id;
71*95c513ecSRafael J. Wysocki 	args[1].integer.value = value;
72*95c513ecSRafael J. Wysocki 
73*95c513ecSRafael J. Wysocki 	pm_runtime_get_sync(dev);
74*95c513ecSRafael J. Wysocki 
75*95c513ecSRafael J. Wysocki 	status = acpi_evaluate_integer(handle, method, &arg_list, &retval);
76*95c513ecSRafael J. Wysocki 
77*95c513ecSRafael J. Wysocki 	pm_runtime_put_sync(dev);
78*95c513ecSRafael J. Wysocki 
79*95c513ecSRafael J. Wysocki 	if (ACPI_FAILURE(status) || retval)
80*95c513ecSRafael J. Wysocki 		return -EIO;
81*95c513ecSRafael J. Wysocki 
82*95c513ecSRafael J. Wysocki 	return 0;
83*95c513ecSRafael J. Wysocki }
84*95c513ecSRafael J. Wysocki 
85*95c513ecSRafael J. Wysocki static int acpi_tad_wake_write(struct device *dev, const char *buf, char *method,
86*95c513ecSRafael J. Wysocki 			       u32 timer_id, const char *specval)
87*95c513ecSRafael J. Wysocki {
88*95c513ecSRafael J. Wysocki 	u32 value;
89*95c513ecSRafael J. Wysocki 
90*95c513ecSRafael J. Wysocki 	if (sysfs_streq(buf, specval)) {
91*95c513ecSRafael J. Wysocki 		value = ACPI_TAD_WAKE_DISABLED;
92*95c513ecSRafael J. Wysocki 	} else {
93*95c513ecSRafael J. Wysocki 		int ret = kstrtou32(buf, 0, &value);
94*95c513ecSRafael J. Wysocki 
95*95c513ecSRafael J. Wysocki 		if (ret)
96*95c513ecSRafael J. Wysocki 			return ret;
97*95c513ecSRafael J. Wysocki 
98*95c513ecSRafael J. Wysocki 		if (value == ACPI_TAD_WAKE_DISABLED)
99*95c513ecSRafael J. Wysocki 			return -EINVAL;
100*95c513ecSRafael J. Wysocki 	}
101*95c513ecSRafael J. Wysocki 
102*95c513ecSRafael J. Wysocki 	return acpi_tad_wake_set(dev, method, timer_id, value);
103*95c513ecSRafael J. Wysocki }
104*95c513ecSRafael J. Wysocki 
105*95c513ecSRafael J. Wysocki static ssize_t acpi_tad_wake_read(struct device *dev, char *buf, char *method,
106*95c513ecSRafael J. Wysocki 				  u32 timer_id, const char *specval)
107*95c513ecSRafael J. Wysocki {
108*95c513ecSRafael J. Wysocki 	acpi_handle handle = ACPI_HANDLE(dev);
109*95c513ecSRafael J. Wysocki 	union acpi_object args[] = {
110*95c513ecSRafael J. Wysocki 		{ .type = ACPI_TYPE_INTEGER, },
111*95c513ecSRafael J. Wysocki 	};
112*95c513ecSRafael J. Wysocki 	struct acpi_object_list arg_list = {
113*95c513ecSRafael J. Wysocki 		.pointer = args,
114*95c513ecSRafael J. Wysocki 		.count = ARRAY_SIZE(args),
115*95c513ecSRafael J. Wysocki 	};
116*95c513ecSRafael J. Wysocki 	unsigned long long retval;
117*95c513ecSRafael J. Wysocki 	acpi_status status;
118*95c513ecSRafael J. Wysocki 
119*95c513ecSRafael J. Wysocki 	args[0].integer.value = timer_id;
120*95c513ecSRafael J. Wysocki 
121*95c513ecSRafael J. Wysocki 	pm_runtime_get_sync(dev);
122*95c513ecSRafael J. Wysocki 
123*95c513ecSRafael J. Wysocki 	status = acpi_evaluate_integer(handle, method, &arg_list, &retval);
124*95c513ecSRafael J. Wysocki 
125*95c513ecSRafael J. Wysocki 	pm_runtime_put_sync(dev);
126*95c513ecSRafael J. Wysocki 
127*95c513ecSRafael J. Wysocki 	if (ACPI_FAILURE(status))
128*95c513ecSRafael J. Wysocki 		return -EIO;
129*95c513ecSRafael J. Wysocki 
130*95c513ecSRafael J. Wysocki 	if ((u32)retval == ACPI_TAD_WAKE_DISABLED)
131*95c513ecSRafael J. Wysocki 		return sprintf(buf, "%s\n", specval);
132*95c513ecSRafael J. Wysocki 
133*95c513ecSRafael J. Wysocki 	return sprintf(buf, "%u\n", (u32)retval);
134*95c513ecSRafael J. Wysocki }
135*95c513ecSRafael J. Wysocki 
136*95c513ecSRafael J. Wysocki static const char *alarm_specval = "disabled";
137*95c513ecSRafael J. Wysocki 
138*95c513ecSRafael J. Wysocki static int acpi_tad_alarm_write(struct device *dev, const char *buf,
139*95c513ecSRafael J. Wysocki 				u32 timer_id)
140*95c513ecSRafael J. Wysocki {
141*95c513ecSRafael J. Wysocki 	return acpi_tad_wake_write(dev, buf, "_STV", timer_id, alarm_specval);
142*95c513ecSRafael J. Wysocki }
143*95c513ecSRafael J. Wysocki 
144*95c513ecSRafael J. Wysocki static ssize_t acpi_tad_alarm_read(struct device *dev, char *buf, u32 timer_id)
145*95c513ecSRafael J. Wysocki {
146*95c513ecSRafael J. Wysocki 	return acpi_tad_wake_read(dev, buf, "_TIV", timer_id, alarm_specval);
147*95c513ecSRafael J. Wysocki }
148*95c513ecSRafael J. Wysocki 
149*95c513ecSRafael J. Wysocki static const char *policy_specval = "never";
150*95c513ecSRafael J. Wysocki 
151*95c513ecSRafael J. Wysocki static int acpi_tad_policy_write(struct device *dev, const char *buf,
152*95c513ecSRafael J. Wysocki 				 u32 timer_id)
153*95c513ecSRafael J. Wysocki {
154*95c513ecSRafael J. Wysocki 	return acpi_tad_wake_write(dev, buf, "_STP", timer_id, policy_specval);
155*95c513ecSRafael J. Wysocki }
156*95c513ecSRafael J. Wysocki 
157*95c513ecSRafael J. Wysocki static ssize_t acpi_tad_policy_read(struct device *dev, char *buf, u32 timer_id)
158*95c513ecSRafael J. Wysocki {
159*95c513ecSRafael J. Wysocki 	return acpi_tad_wake_read(dev, buf, "_TIP", timer_id, policy_specval);
160*95c513ecSRafael J. Wysocki }
161*95c513ecSRafael J. Wysocki 
162*95c513ecSRafael J. Wysocki static int acpi_tad_clear_status(struct device *dev, u32 timer_id)
163*95c513ecSRafael J. Wysocki {
164*95c513ecSRafael J. Wysocki 	acpi_handle handle = ACPI_HANDLE(dev);
165*95c513ecSRafael J. Wysocki 	union acpi_object args[] = {
166*95c513ecSRafael J. Wysocki 		{ .type = ACPI_TYPE_INTEGER, },
167*95c513ecSRafael J. Wysocki 	};
168*95c513ecSRafael J. Wysocki 	struct acpi_object_list arg_list = {
169*95c513ecSRafael J. Wysocki 		.pointer = args,
170*95c513ecSRafael J. Wysocki 		.count = ARRAY_SIZE(args),
171*95c513ecSRafael J. Wysocki 	};
172*95c513ecSRafael J. Wysocki 	unsigned long long retval;
173*95c513ecSRafael J. Wysocki 	acpi_status status;
174*95c513ecSRafael J. Wysocki 
175*95c513ecSRafael J. Wysocki 	args[0].integer.value = timer_id;
176*95c513ecSRafael J. Wysocki 
177*95c513ecSRafael J. Wysocki 	pm_runtime_get_sync(dev);
178*95c513ecSRafael J. Wysocki 
179*95c513ecSRafael J. Wysocki 	status = acpi_evaluate_integer(handle, "_CWS", &arg_list, &retval);
180*95c513ecSRafael J. Wysocki 
181*95c513ecSRafael J. Wysocki 	pm_runtime_put_sync(dev);
182*95c513ecSRafael J. Wysocki 
183*95c513ecSRafael J. Wysocki 	if (ACPI_FAILURE(status) || retval)
184*95c513ecSRafael J. Wysocki 		return -EIO;
185*95c513ecSRafael J. Wysocki 
186*95c513ecSRafael J. Wysocki 	return 0;
187*95c513ecSRafael J. Wysocki }
188*95c513ecSRafael J. Wysocki 
189*95c513ecSRafael J. Wysocki static int acpi_tad_status_write(struct device *dev, const char *buf, u32 timer_id)
190*95c513ecSRafael J. Wysocki {
191*95c513ecSRafael J. Wysocki 	int ret, value;
192*95c513ecSRafael J. Wysocki 
193*95c513ecSRafael J. Wysocki 	ret = kstrtoint(buf, 0, &value);
194*95c513ecSRafael J. Wysocki 	if (ret)
195*95c513ecSRafael J. Wysocki 		return ret;
196*95c513ecSRafael J. Wysocki 
197*95c513ecSRafael J. Wysocki 	if (value)
198*95c513ecSRafael J. Wysocki 		return -EINVAL;
199*95c513ecSRafael J. Wysocki 
200*95c513ecSRafael J. Wysocki 	return acpi_tad_clear_status(dev, timer_id);
201*95c513ecSRafael J. Wysocki }
202*95c513ecSRafael J. Wysocki 
203*95c513ecSRafael J. Wysocki static ssize_t acpi_tad_status_read(struct device *dev, char *buf, u32 timer_id)
204*95c513ecSRafael J. Wysocki {
205*95c513ecSRafael J. Wysocki 	acpi_handle handle = ACPI_HANDLE(dev);
206*95c513ecSRafael J. Wysocki 	union acpi_object args[] = {
207*95c513ecSRafael J. Wysocki 		{ .type = ACPI_TYPE_INTEGER, },
208*95c513ecSRafael J. Wysocki 	};
209*95c513ecSRafael J. Wysocki 	struct acpi_object_list arg_list = {
210*95c513ecSRafael J. Wysocki 		.pointer = args,
211*95c513ecSRafael J. Wysocki 		.count = ARRAY_SIZE(args),
212*95c513ecSRafael J. Wysocki 	};
213*95c513ecSRafael J. Wysocki 	unsigned long long retval;
214*95c513ecSRafael J. Wysocki 	acpi_status status;
215*95c513ecSRafael J. Wysocki 
216*95c513ecSRafael J. Wysocki 	args[0].integer.value = timer_id;
217*95c513ecSRafael J. Wysocki 
218*95c513ecSRafael J. Wysocki 	pm_runtime_get_sync(dev);
219*95c513ecSRafael J. Wysocki 
220*95c513ecSRafael J. Wysocki 	status = acpi_evaluate_integer(handle, "_GWS", &arg_list, &retval);
221*95c513ecSRafael J. Wysocki 
222*95c513ecSRafael J. Wysocki 	pm_runtime_put_sync(dev);
223*95c513ecSRafael J. Wysocki 
224*95c513ecSRafael J. Wysocki 	if (ACPI_FAILURE(status))
225*95c513ecSRafael J. Wysocki 		return -EIO;
226*95c513ecSRafael J. Wysocki 
227*95c513ecSRafael J. Wysocki 	return sprintf(buf, "0x%02X\n", (u32)retval);
228*95c513ecSRafael J. Wysocki }
229*95c513ecSRafael J. Wysocki 
230*95c513ecSRafael J. Wysocki static ssize_t caps_show(struct device *dev, struct device_attribute *attr,
231*95c513ecSRafael J. Wysocki 			 char *buf)
232*95c513ecSRafael J. Wysocki {
233*95c513ecSRafael J. Wysocki 	struct acpi_tad_driver_data *dd = dev_get_drvdata(dev);
234*95c513ecSRafael J. Wysocki 
235*95c513ecSRafael J. Wysocki 	return sprintf(buf, "0x%02X\n", dd->capabilities);
236*95c513ecSRafael J. Wysocki }
237*95c513ecSRafael J. Wysocki 
238*95c513ecSRafael J. Wysocki static DEVICE_ATTR_RO(caps);
239*95c513ecSRafael J. Wysocki 
240*95c513ecSRafael J. Wysocki static ssize_t ac_alarm_store(struct device *dev, struct device_attribute *attr,
241*95c513ecSRafael J. Wysocki 			      const char *buf, size_t count)
242*95c513ecSRafael J. Wysocki {
243*95c513ecSRafael J. Wysocki 	int ret = acpi_tad_alarm_write(dev, buf, ACPI_TAD_AC_TIMER);
244*95c513ecSRafael J. Wysocki 
245*95c513ecSRafael J. Wysocki 	return ret ? ret : count;
246*95c513ecSRafael J. Wysocki }
247*95c513ecSRafael J. Wysocki 
248*95c513ecSRafael J. Wysocki static ssize_t ac_alarm_show(struct device *dev, struct device_attribute *attr,
249*95c513ecSRafael J. Wysocki 			     char *buf)
250*95c513ecSRafael J. Wysocki {
251*95c513ecSRafael J. Wysocki 	return acpi_tad_alarm_read(dev, buf, ACPI_TAD_AC_TIMER);
252*95c513ecSRafael J. Wysocki }
253*95c513ecSRafael J. Wysocki 
254*95c513ecSRafael J. Wysocki static DEVICE_ATTR(ac_alarm, S_IRUSR | S_IWUSR, ac_alarm_show, ac_alarm_store);
255*95c513ecSRafael J. Wysocki 
256*95c513ecSRafael J. Wysocki static ssize_t ac_policy_store(struct device *dev, struct device_attribute *attr,
257*95c513ecSRafael J. Wysocki 			       const char *buf, size_t count)
258*95c513ecSRafael J. Wysocki {
259*95c513ecSRafael J. Wysocki 	int ret = acpi_tad_policy_write(dev, buf, ACPI_TAD_AC_TIMER);
260*95c513ecSRafael J. Wysocki 
261*95c513ecSRafael J. Wysocki 	return ret ? ret : count;
262*95c513ecSRafael J. Wysocki }
263*95c513ecSRafael J. Wysocki 
264*95c513ecSRafael J. Wysocki static ssize_t ac_policy_show(struct device *dev, struct device_attribute *attr,
265*95c513ecSRafael J. Wysocki 			      char *buf)
266*95c513ecSRafael J. Wysocki {
267*95c513ecSRafael J. Wysocki 	return acpi_tad_policy_read(dev, buf, ACPI_TAD_AC_TIMER);
268*95c513ecSRafael J. Wysocki }
269*95c513ecSRafael J. Wysocki 
270*95c513ecSRafael J. Wysocki static DEVICE_ATTR(ac_policy, S_IRUSR | S_IWUSR, ac_policy_show, ac_policy_store);
271*95c513ecSRafael J. Wysocki 
272*95c513ecSRafael J. Wysocki static ssize_t ac_status_store(struct device *dev, struct device_attribute *attr,
273*95c513ecSRafael J. Wysocki 			       const char *buf, size_t count)
274*95c513ecSRafael J. Wysocki {
275*95c513ecSRafael J. Wysocki 	int ret = acpi_tad_status_write(dev, buf, ACPI_TAD_AC_TIMER);
276*95c513ecSRafael J. Wysocki 
277*95c513ecSRafael J. Wysocki 	return ret ? ret : count;
278*95c513ecSRafael J. Wysocki }
279*95c513ecSRafael J. Wysocki 
280*95c513ecSRafael J. Wysocki static ssize_t ac_status_show(struct device *dev, struct device_attribute *attr,
281*95c513ecSRafael J. Wysocki 			      char *buf)
282*95c513ecSRafael J. Wysocki {
283*95c513ecSRafael J. Wysocki 	return acpi_tad_status_read(dev, buf, ACPI_TAD_AC_TIMER);
284*95c513ecSRafael J. Wysocki }
285*95c513ecSRafael J. Wysocki 
286*95c513ecSRafael J. Wysocki static DEVICE_ATTR(ac_status, S_IRUSR | S_IWUSR, ac_status_show, ac_status_store);
287*95c513ecSRafael J. Wysocki 
288*95c513ecSRafael J. Wysocki static struct attribute *acpi_tad_attrs[] = {
289*95c513ecSRafael J. Wysocki 	&dev_attr_caps.attr,
290*95c513ecSRafael J. Wysocki 	&dev_attr_ac_alarm.attr,
291*95c513ecSRafael J. Wysocki 	&dev_attr_ac_policy.attr,
292*95c513ecSRafael J. Wysocki 	&dev_attr_ac_status.attr,
293*95c513ecSRafael J. Wysocki 	NULL,
294*95c513ecSRafael J. Wysocki };
295*95c513ecSRafael J. Wysocki static const struct attribute_group acpi_tad_attr_group = {
296*95c513ecSRafael J. Wysocki 	.attrs	= acpi_tad_attrs,
297*95c513ecSRafael J. Wysocki };
298*95c513ecSRafael J. Wysocki 
299*95c513ecSRafael J. Wysocki static ssize_t dc_alarm_store(struct device *dev, struct device_attribute *attr,
300*95c513ecSRafael J. Wysocki 			      const char *buf, size_t count)
301*95c513ecSRafael J. Wysocki {
302*95c513ecSRafael J. Wysocki 	int ret = acpi_tad_alarm_write(dev, buf, ACPI_TAD_DC_TIMER);
303*95c513ecSRafael J. Wysocki 
304*95c513ecSRafael J. Wysocki 	return ret ? ret : count;
305*95c513ecSRafael J. Wysocki }
306*95c513ecSRafael J. Wysocki 
307*95c513ecSRafael J. Wysocki static ssize_t dc_alarm_show(struct device *dev, struct device_attribute *attr,
308*95c513ecSRafael J. Wysocki 			     char *buf)
309*95c513ecSRafael J. Wysocki {
310*95c513ecSRafael J. Wysocki 	return acpi_tad_alarm_read(dev, buf, ACPI_TAD_DC_TIMER);
311*95c513ecSRafael J. Wysocki }
312*95c513ecSRafael J. Wysocki 
313*95c513ecSRafael J. Wysocki static DEVICE_ATTR(dc_alarm, S_IRUSR | S_IWUSR, dc_alarm_show, dc_alarm_store);
314*95c513ecSRafael J. Wysocki 
315*95c513ecSRafael J. Wysocki static ssize_t dc_policy_store(struct device *dev, struct device_attribute *attr,
316*95c513ecSRafael J. Wysocki 			       const char *buf, size_t count)
317*95c513ecSRafael J. Wysocki {
318*95c513ecSRafael J. Wysocki 	int ret = acpi_tad_policy_write(dev, buf, ACPI_TAD_DC_TIMER);
319*95c513ecSRafael J. Wysocki 
320*95c513ecSRafael J. Wysocki 	return ret ? ret : count;
321*95c513ecSRafael J. Wysocki }
322*95c513ecSRafael J. Wysocki 
323*95c513ecSRafael J. Wysocki static ssize_t dc_policy_show(struct device *dev, struct device_attribute *attr,
324*95c513ecSRafael J. Wysocki 			      char *buf)
325*95c513ecSRafael J. Wysocki {
326*95c513ecSRafael J. Wysocki 	return acpi_tad_policy_read(dev, buf, ACPI_TAD_DC_TIMER);
327*95c513ecSRafael J. Wysocki }
328*95c513ecSRafael J. Wysocki 
329*95c513ecSRafael J. Wysocki static DEVICE_ATTR(dc_policy, S_IRUSR | S_IWUSR, dc_policy_show, dc_policy_store);
330*95c513ecSRafael J. Wysocki 
331*95c513ecSRafael J. Wysocki static ssize_t dc_status_store(struct device *dev, struct device_attribute *attr,
332*95c513ecSRafael J. Wysocki 			       const char *buf, size_t count)
333*95c513ecSRafael J. Wysocki {
334*95c513ecSRafael J. Wysocki 	int ret = acpi_tad_status_write(dev, buf, ACPI_TAD_DC_TIMER);
335*95c513ecSRafael J. Wysocki 
336*95c513ecSRafael J. Wysocki 	return ret ? ret : count;
337*95c513ecSRafael J. Wysocki }
338*95c513ecSRafael J. Wysocki 
339*95c513ecSRafael J. Wysocki static ssize_t dc_status_show(struct device *dev, struct device_attribute *attr,
340*95c513ecSRafael J. Wysocki 			      char *buf)
341*95c513ecSRafael J. Wysocki {
342*95c513ecSRafael J. Wysocki 	return acpi_tad_status_read(dev, buf, ACPI_TAD_DC_TIMER);
343*95c513ecSRafael J. Wysocki }
344*95c513ecSRafael J. Wysocki 
345*95c513ecSRafael J. Wysocki static DEVICE_ATTR(dc_status, S_IRUSR | S_IWUSR, dc_status_show, dc_status_store);
346*95c513ecSRafael J. Wysocki 
347*95c513ecSRafael J. Wysocki static struct attribute *acpi_tad_dc_attrs[] = {
348*95c513ecSRafael J. Wysocki 	&dev_attr_dc_alarm.attr,
349*95c513ecSRafael J. Wysocki 	&dev_attr_dc_policy.attr,
350*95c513ecSRafael J. Wysocki 	&dev_attr_dc_status.attr,
351*95c513ecSRafael J. Wysocki 	NULL,
352*95c513ecSRafael J. Wysocki };
353*95c513ecSRafael J. Wysocki static const struct attribute_group acpi_tad_dc_attr_group = {
354*95c513ecSRafael J. Wysocki 	.attrs	= acpi_tad_dc_attrs,
355*95c513ecSRafael J. Wysocki };
356*95c513ecSRafael J. Wysocki 
357*95c513ecSRafael J. Wysocki static int acpi_tad_disable_timer(struct device *dev, u32 timer_id)
358*95c513ecSRafael J. Wysocki {
359*95c513ecSRafael J. Wysocki 	return acpi_tad_wake_set(dev, "_STV", timer_id, ACPI_TAD_WAKE_DISABLED);
360*95c513ecSRafael J. Wysocki }
361*95c513ecSRafael J. Wysocki 
362*95c513ecSRafael J. Wysocki static int acpi_tad_remove(struct platform_device *pdev)
363*95c513ecSRafael J. Wysocki {
364*95c513ecSRafael J. Wysocki 	struct device *dev = &pdev->dev;
365*95c513ecSRafael J. Wysocki 	struct acpi_tad_driver_data *dd = dev_get_drvdata(dev);
366*95c513ecSRafael J. Wysocki 
367*95c513ecSRafael J. Wysocki 	device_init_wakeup(dev, false);
368*95c513ecSRafael J. Wysocki 
369*95c513ecSRafael J. Wysocki 	pm_runtime_get_sync(dev);
370*95c513ecSRafael J. Wysocki 
371*95c513ecSRafael J. Wysocki 	if (dd->capabilities & ACPI_TAD_DC_WAKE)
372*95c513ecSRafael J. Wysocki 		sysfs_remove_group(&dev->kobj, &acpi_tad_dc_attr_group);
373*95c513ecSRafael J. Wysocki 
374*95c513ecSRafael J. Wysocki 	sysfs_remove_group(&dev->kobj, &acpi_tad_attr_group);
375*95c513ecSRafael J. Wysocki 
376*95c513ecSRafael J. Wysocki 	acpi_tad_disable_timer(dev, ACPI_TAD_AC_TIMER);
377*95c513ecSRafael J. Wysocki 	acpi_tad_clear_status(dev, ACPI_TAD_AC_TIMER);
378*95c513ecSRafael J. Wysocki 	if (dd->capabilities & ACPI_TAD_DC_WAKE) {
379*95c513ecSRafael J. Wysocki 		acpi_tad_disable_timer(dev, ACPI_TAD_DC_TIMER);
380*95c513ecSRafael J. Wysocki 		acpi_tad_clear_status(dev, ACPI_TAD_DC_TIMER);
381*95c513ecSRafael J. Wysocki 	}
382*95c513ecSRafael J. Wysocki 
383*95c513ecSRafael J. Wysocki 	pm_runtime_put_sync(dev);
384*95c513ecSRafael J. Wysocki 	pm_runtime_disable(dev);
385*95c513ecSRafael J. Wysocki 	return 0;
386*95c513ecSRafael J. Wysocki }
387*95c513ecSRafael J. Wysocki 
388*95c513ecSRafael J. Wysocki static int acpi_tad_probe(struct platform_device *pdev)
389*95c513ecSRafael J. Wysocki {
390*95c513ecSRafael J. Wysocki 	struct device *dev = &pdev->dev;
391*95c513ecSRafael J. Wysocki 	acpi_handle handle = ACPI_HANDLE(dev);
392*95c513ecSRafael J. Wysocki 	struct acpi_tad_driver_data *dd;
393*95c513ecSRafael J. Wysocki 	acpi_status status;
394*95c513ecSRafael J. Wysocki 	unsigned long long caps;
395*95c513ecSRafael J. Wysocki 	int ret;
396*95c513ecSRafael J. Wysocki 
397*95c513ecSRafael J. Wysocki 	/*
398*95c513ecSRafael J. Wysocki 	 * Initialization failure messages are mostly about firmware issues, so
399*95c513ecSRafael J. Wysocki 	 * print them at the "info" level.
400*95c513ecSRafael J. Wysocki 	 */
401*95c513ecSRafael J. Wysocki 	status = acpi_evaluate_integer(handle, "_GCP", NULL, &caps);
402*95c513ecSRafael J. Wysocki 	if (ACPI_FAILURE(status)) {
403*95c513ecSRafael J. Wysocki 		dev_info(dev, "Unable to get capabilities\n");
404*95c513ecSRafael J. Wysocki 		return -ENODEV;
405*95c513ecSRafael J. Wysocki 	}
406*95c513ecSRafael J. Wysocki 
407*95c513ecSRafael J. Wysocki 	if (!(caps & ACPI_TAD_AC_WAKE)) {
408*95c513ecSRafael J. Wysocki 		dev_info(dev, "Unsupported capabilities\n");
409*95c513ecSRafael J. Wysocki 		return -ENODEV;
410*95c513ecSRafael J. Wysocki 	}
411*95c513ecSRafael J. Wysocki 
412*95c513ecSRafael J. Wysocki 	if (!acpi_has_method(handle, "_PRW")) {
413*95c513ecSRafael J. Wysocki 		dev_info(dev, "Missing _PRW\n");
414*95c513ecSRafael J. Wysocki 		return -ENODEV;
415*95c513ecSRafael J. Wysocki 	}
416*95c513ecSRafael J. Wysocki 
417*95c513ecSRafael J. Wysocki 	dd = devm_kzalloc(dev, sizeof(*dd), GFP_KERNEL);
418*95c513ecSRafael J. Wysocki 	if (!dd)
419*95c513ecSRafael J. Wysocki 		return -ENOMEM;
420*95c513ecSRafael J. Wysocki 
421*95c513ecSRafael J. Wysocki 	dd->capabilities = caps;
422*95c513ecSRafael J. Wysocki 	dev_set_drvdata(dev, dd);
423*95c513ecSRafael J. Wysocki 
424*95c513ecSRafael J. Wysocki 	/*
425*95c513ecSRafael J. Wysocki 	 * Assume that the ACPI PM domain has been attached to the device and
426*95c513ecSRafael J. Wysocki 	 * simply enable system wakeup and runtime PM and put the device into
427*95c513ecSRafael J. Wysocki 	 * runtime suspend.  Everything else should be taken care of by the ACPI
428*95c513ecSRafael J. Wysocki 	 * PM domain callbacks.
429*95c513ecSRafael J. Wysocki 	 */
430*95c513ecSRafael J. Wysocki 	device_init_wakeup(dev, true);
431*95c513ecSRafael J. Wysocki 	dev_pm_set_driver_flags(dev, DPM_FLAG_SMART_SUSPEND |
432*95c513ecSRafael J. Wysocki 				     DPM_FLAG_LEAVE_SUSPENDED);
433*95c513ecSRafael J. Wysocki 	/*
434*95c513ecSRafael J. Wysocki 	 * The platform bus type layer tells the ACPI PM domain powers up the
435*95c513ecSRafael J. Wysocki 	 * device, so set the runtime PM status of it to "active".
436*95c513ecSRafael J. Wysocki 	 */
437*95c513ecSRafael J. Wysocki 	pm_runtime_set_active(dev);
438*95c513ecSRafael J. Wysocki 	pm_runtime_enable(dev);
439*95c513ecSRafael J. Wysocki 	pm_runtime_suspend(dev);
440*95c513ecSRafael J. Wysocki 
441*95c513ecSRafael J. Wysocki 	ret = sysfs_create_group(&dev->kobj, &acpi_tad_attr_group);
442*95c513ecSRafael J. Wysocki 	if (ret)
443*95c513ecSRafael J. Wysocki 		goto fail;
444*95c513ecSRafael J. Wysocki 
445*95c513ecSRafael J. Wysocki 	if (caps & ACPI_TAD_DC_WAKE) {
446*95c513ecSRafael J. Wysocki 		ret = sysfs_create_group(&dev->kobj, &acpi_tad_dc_attr_group);
447*95c513ecSRafael J. Wysocki 		if (ret)
448*95c513ecSRafael J. Wysocki 			goto fail;
449*95c513ecSRafael J. Wysocki 	}
450*95c513ecSRafael J. Wysocki 
451*95c513ecSRafael J. Wysocki 	return 0;
452*95c513ecSRafael J. Wysocki 
453*95c513ecSRafael J. Wysocki fail:
454*95c513ecSRafael J. Wysocki 	acpi_tad_remove(pdev);
455*95c513ecSRafael J. Wysocki 	return ret;
456*95c513ecSRafael J. Wysocki }
457*95c513ecSRafael J. Wysocki 
458*95c513ecSRafael J. Wysocki static const struct acpi_device_id acpi_tad_ids[] = {
459*95c513ecSRafael J. Wysocki 	{"ACPI000E", 0},
460*95c513ecSRafael J. Wysocki 	{}
461*95c513ecSRafael J. Wysocki };
462*95c513ecSRafael J. Wysocki 
463*95c513ecSRafael J. Wysocki static struct platform_driver acpi_tad_driver = {
464*95c513ecSRafael J. Wysocki 	.driver = {
465*95c513ecSRafael J. Wysocki 		.name = "acpi-tad",
466*95c513ecSRafael J. Wysocki 		.acpi_match_table = acpi_tad_ids,
467*95c513ecSRafael J. Wysocki 	},
468*95c513ecSRafael J. Wysocki 	.probe = acpi_tad_probe,
469*95c513ecSRafael J. Wysocki 	.remove = acpi_tad_remove,
470*95c513ecSRafael J. Wysocki };
471*95c513ecSRafael J. Wysocki MODULE_DEVICE_TABLE(acpi, acpi_tad_ids);
472*95c513ecSRafael J. Wysocki 
473*95c513ecSRafael J. Wysocki module_platform_driver(acpi_tad_driver);
474