]>
Commit | Line | Data |
---|---|---|
95c513ec RW |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * ACPI Time and Alarm (TAD) Device Driver | |
4 | * | |
5 | * Copyright (C) 2018 Intel Corporation | |
6 | * Author: Rafael J. Wysocki <[email protected]> | |
7 | * | |
8 | * This driver is based on Section 9.18 of the ACPI 6.2 specification revision. | |
9 | * | |
10 | * It only supports the system wakeup capabilities of the TAD. | |
11 | * | |
12 | * Provided are sysfs attributes, available under the TAD platform device, | |
13 | * allowing user space to manage the AC and DC wakeup timers of the TAD: | |
14 | * set and read their values, set and check their expire timer wake policies, | |
15 | * check and clear their status and check the capabilities of the TAD reported | |
16 | * by AML. The DC timer attributes are only present if the TAD supports a | |
17 | * separate DC alarm timer. | |
18 | * | |
19 | * The wakeup events handling and power management of the TAD is expected to | |
20 | * be taken care of by the ACPI PM domain attached to its platform device. | |
21 | */ | |
22 | ||
23 | #include <linux/acpi.h> | |
24 | #include <linux/kernel.h> | |
25 | #include <linux/module.h> | |
26 | #include <linux/platform_device.h> | |
27 | #include <linux/pm_runtime.h> | |
28 | #include <linux/suspend.h> | |
29 | ||
30 | MODULE_LICENSE("GPL v2"); | |
31 | MODULE_AUTHOR("Rafael J. Wysocki"); | |
32 | ||
33 | /* ACPI TAD capability flags (ACPI 6.2, Section 9.18.2) */ | |
34 | #define ACPI_TAD_AC_WAKE BIT(0) | |
35 | #define ACPI_TAD_DC_WAKE BIT(1) | |
36 | #define ACPI_TAD_RT BIT(2) | |
37 | #define ACPI_TAD_RT_IN_MS BIT(3) | |
38 | #define ACPI_TAD_S4_S5__GWS BIT(4) | |
39 | #define ACPI_TAD_AC_S4_WAKE BIT(5) | |
40 | #define ACPI_TAD_AC_S5_WAKE BIT(6) | |
41 | #define ACPI_TAD_DC_S4_WAKE BIT(7) | |
42 | #define ACPI_TAD_DC_S5_WAKE BIT(8) | |
43 | ||
44 | /* ACPI TAD alarm timer selection */ | |
45 | #define ACPI_TAD_AC_TIMER (u32)0 | |
46 | #define ACPI_TAD_DC_TIMER (u32)1 | |
47 | ||
48 | /* Special value for disabled timer or expired timer wake policy. */ | |
49 | #define ACPI_TAD_WAKE_DISABLED (~(u32)0) | |
50 | ||
51 | struct acpi_tad_driver_data { | |
52 | u32 capabilities; | |
53 | }; | |
54 | ||
3230b2b3 RW |
55 | struct acpi_tad_rt { |
56 | u16 year; /* 1900 - 9999 */ | |
57 | u8 month; /* 1 - 12 */ | |
58 | u8 day; /* 1 - 31 */ | |
59 | u8 hour; /* 0 - 23 */ | |
60 | u8 minute; /* 0 - 59 */ | |
61 | u8 second; /* 0 - 59 */ | |
62 | u8 valid; /* 0 (failed) or 1 (success) for reads, 0 for writes */ | |
63 | u16 msec; /* 1 - 1000 */ | |
64 | s16 tz; /* -1440 to 1440 or 2047 (unspecified) */ | |
65 | u8 daylight; | |
66 | u8 padding[3]; /* must be 0 */ | |
67 | } __packed; | |
68 | ||
69 | static int acpi_tad_set_real_time(struct device *dev, struct acpi_tad_rt *rt) | |
70 | { | |
71 | acpi_handle handle = ACPI_HANDLE(dev); | |
72 | union acpi_object args[] = { | |
73 | { .type = ACPI_TYPE_BUFFER, }, | |
74 | }; | |
75 | struct acpi_object_list arg_list = { | |
76 | .pointer = args, | |
77 | .count = ARRAY_SIZE(args), | |
78 | }; | |
79 | unsigned long long retval; | |
80 | acpi_status status; | |
81 | ||
82 | if (rt->year < 1900 || rt->year > 9999 || | |
83 | rt->month < 1 || rt->month > 12 || | |
84 | rt->hour > 23 || rt->minute > 59 || rt->second > 59 || | |
85 | rt->tz < -1440 || (rt->tz > 1440 && rt->tz != 2047) || | |
86 | rt->daylight > 3) | |
87 | return -ERANGE; | |
88 | ||
89 | args[0].buffer.pointer = (u8 *)rt; | |
90 | args[0].buffer.length = sizeof(*rt); | |
91 | ||
92 | pm_runtime_get_sync(dev); | |
93 | ||
94 | status = acpi_evaluate_integer(handle, "_SRT", &arg_list, &retval); | |
95 | ||
96 | pm_runtime_put_sync(dev); | |
97 | ||
98 | if (ACPI_FAILURE(status) || retval) | |
99 | return -EIO; | |
100 | ||
101 | return 0; | |
102 | } | |
103 | ||
104 | static int acpi_tad_get_real_time(struct device *dev, struct acpi_tad_rt *rt) | |
105 | { | |
106 | acpi_handle handle = ACPI_HANDLE(dev); | |
107 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER }; | |
108 | union acpi_object *out_obj; | |
109 | struct acpi_tad_rt *data; | |
110 | acpi_status status; | |
111 | int ret = -EIO; | |
112 | ||
113 | pm_runtime_get_sync(dev); | |
114 | ||
115 | status = acpi_evaluate_object(handle, "_GRT", NULL, &output); | |
116 | ||
117 | pm_runtime_put_sync(dev); | |
118 | ||
119 | if (ACPI_FAILURE(status)) | |
120 | goto out_free; | |
121 | ||
122 | out_obj = output.pointer; | |
123 | if (out_obj->type != ACPI_TYPE_BUFFER) | |
124 | goto out_free; | |
125 | ||
126 | if (out_obj->buffer.length != sizeof(*rt)) | |
127 | goto out_free; | |
128 | ||
129 | data = (struct acpi_tad_rt *)(out_obj->buffer.pointer); | |
130 | if (!data->valid) | |
131 | goto out_free; | |
132 | ||
133 | memcpy(rt, data, sizeof(*rt)); | |
134 | ret = 0; | |
135 | ||
136 | out_free: | |
137 | ACPI_FREE(output.pointer); | |
138 | return ret; | |
139 | } | |
140 | ||
141 | static char *acpi_tad_rt_next_field(char *s, int *val) | |
142 | { | |
143 | char *p; | |
144 | ||
145 | p = strchr(s, ':'); | |
146 | if (!p) | |
147 | return NULL; | |
148 | ||
149 | *p = '\0'; | |
150 | if (kstrtoint(s, 10, val)) | |
151 | return NULL; | |
152 | ||
153 | return p + 1; | |
154 | } | |
155 | ||
156 | static ssize_t time_store(struct device *dev, struct device_attribute *attr, | |
157 | const char *buf, size_t count) | |
158 | { | |
159 | struct acpi_tad_rt rt; | |
160 | char *str, *s; | |
161 | int val, ret = -ENODATA; | |
162 | ||
163 | str = kmemdup_nul(buf, count, GFP_KERNEL); | |
164 | if (!str) | |
165 | return -ENOMEM; | |
166 | ||
167 | s = acpi_tad_rt_next_field(str, &val); | |
168 | if (!s) | |
169 | goto out_free; | |
170 | ||
171 | rt.year = val; | |
172 | ||
173 | s = acpi_tad_rt_next_field(s, &val); | |
174 | if (!s) | |
175 | goto out_free; | |
176 | ||
177 | rt.month = val; | |
178 | ||
179 | s = acpi_tad_rt_next_field(s, &val); | |
180 | if (!s) | |
181 | goto out_free; | |
182 | ||
183 | rt.day = val; | |
184 | ||
185 | s = acpi_tad_rt_next_field(s, &val); | |
186 | if (!s) | |
187 | goto out_free; | |
188 | ||
189 | rt.hour = val; | |
190 | ||
191 | s = acpi_tad_rt_next_field(s, &val); | |
192 | if (!s) | |
193 | goto out_free; | |
194 | ||
195 | rt.minute = val; | |
196 | ||
197 | s = acpi_tad_rt_next_field(s, &val); | |
198 | if (!s) | |
199 | goto out_free; | |
200 | ||
201 | rt.second = val; | |
202 | ||
203 | s = acpi_tad_rt_next_field(s, &val); | |
204 | if (!s) | |
205 | goto out_free; | |
206 | ||
207 | rt.tz = val; | |
208 | ||
209 | if (kstrtoint(s, 10, &val)) | |
210 | goto out_free; | |
211 | ||
212 | rt.daylight = val; | |
213 | ||
214 | rt.valid = 0; | |
215 | rt.msec = 0; | |
216 | memset(rt.padding, 0, 3); | |
217 | ||
218 | ret = acpi_tad_set_real_time(dev, &rt); | |
219 | ||
220 | out_free: | |
221 | kfree(str); | |
222 | return ret ? ret : count; | |
223 | } | |
224 | ||
225 | static ssize_t time_show(struct device *dev, struct device_attribute *attr, | |
226 | char *buf) | |
227 | { | |
228 | struct acpi_tad_rt rt; | |
229 | int ret; | |
230 | ||
231 | ret = acpi_tad_get_real_time(dev, &rt); | |
232 | if (ret) | |
233 | return ret; | |
234 | ||
235 | return sprintf(buf, "%u:%u:%u:%u:%u:%u:%d:%u\n", | |
236 | rt.year, rt.month, rt.day, rt.hour, rt.minute, rt.second, | |
237 | rt.tz, rt.daylight); | |
238 | } | |
239 | ||
240 | static DEVICE_ATTR(time, S_IRUSR | S_IWUSR, time_show, time_store); | |
241 | ||
242 | static struct attribute *acpi_tad_time_attrs[] = { | |
243 | &dev_attr_time.attr, | |
244 | NULL, | |
245 | }; | |
246 | static const struct attribute_group acpi_tad_time_attr_group = { | |
247 | .attrs = acpi_tad_time_attrs, | |
248 | }; | |
249 | ||
95c513ec RW |
250 | static int acpi_tad_wake_set(struct device *dev, char *method, u32 timer_id, |
251 | u32 value) | |
252 | { | |
253 | acpi_handle handle = ACPI_HANDLE(dev); | |
254 | union acpi_object args[] = { | |
255 | { .type = ACPI_TYPE_INTEGER, }, | |
256 | { .type = ACPI_TYPE_INTEGER, }, | |
257 | }; | |
258 | struct acpi_object_list arg_list = { | |
259 | .pointer = args, | |
260 | .count = ARRAY_SIZE(args), | |
261 | }; | |
262 | unsigned long long retval; | |
263 | acpi_status status; | |
264 | ||
265 | args[0].integer.value = timer_id; | |
266 | args[1].integer.value = value; | |
267 | ||
268 | pm_runtime_get_sync(dev); | |
269 | ||
270 | status = acpi_evaluate_integer(handle, method, &arg_list, &retval); | |
271 | ||
272 | pm_runtime_put_sync(dev); | |
273 | ||
274 | if (ACPI_FAILURE(status) || retval) | |
275 | return -EIO; | |
276 | ||
277 | return 0; | |
278 | } | |
279 | ||
280 | static int acpi_tad_wake_write(struct device *dev, const char *buf, char *method, | |
281 | u32 timer_id, const char *specval) | |
282 | { | |
283 | u32 value; | |
284 | ||
285 | if (sysfs_streq(buf, specval)) { | |
286 | value = ACPI_TAD_WAKE_DISABLED; | |
287 | } else { | |
288 | int ret = kstrtou32(buf, 0, &value); | |
289 | ||
290 | if (ret) | |
291 | return ret; | |
292 | ||
293 | if (value == ACPI_TAD_WAKE_DISABLED) | |
294 | return -EINVAL; | |
295 | } | |
296 | ||
297 | return acpi_tad_wake_set(dev, method, timer_id, value); | |
298 | } | |
299 | ||
300 | static ssize_t acpi_tad_wake_read(struct device *dev, char *buf, char *method, | |
301 | u32 timer_id, const char *specval) | |
302 | { | |
303 | acpi_handle handle = ACPI_HANDLE(dev); | |
304 | union acpi_object args[] = { | |
305 | { .type = ACPI_TYPE_INTEGER, }, | |
306 | }; | |
307 | struct acpi_object_list arg_list = { | |
308 | .pointer = args, | |
309 | .count = ARRAY_SIZE(args), | |
310 | }; | |
311 | unsigned long long retval; | |
312 | acpi_status status; | |
313 | ||
314 | args[0].integer.value = timer_id; | |
315 | ||
316 | pm_runtime_get_sync(dev); | |
317 | ||
318 | status = acpi_evaluate_integer(handle, method, &arg_list, &retval); | |
319 | ||
320 | pm_runtime_put_sync(dev); | |
321 | ||
322 | if (ACPI_FAILURE(status)) | |
323 | return -EIO; | |
324 | ||
325 | if ((u32)retval == ACPI_TAD_WAKE_DISABLED) | |
326 | return sprintf(buf, "%s\n", specval); | |
327 | ||
328 | return sprintf(buf, "%u\n", (u32)retval); | |
329 | } | |
330 | ||
331 | static const char *alarm_specval = "disabled"; | |
332 | ||
333 | static int acpi_tad_alarm_write(struct device *dev, const char *buf, | |
334 | u32 timer_id) | |
335 | { | |
336 | return acpi_tad_wake_write(dev, buf, "_STV", timer_id, alarm_specval); | |
337 | } | |
338 | ||
339 | static ssize_t acpi_tad_alarm_read(struct device *dev, char *buf, u32 timer_id) | |
340 | { | |
341 | return acpi_tad_wake_read(dev, buf, "_TIV", timer_id, alarm_specval); | |
342 | } | |
343 | ||
344 | static const char *policy_specval = "never"; | |
345 | ||
346 | static int acpi_tad_policy_write(struct device *dev, const char *buf, | |
347 | u32 timer_id) | |
348 | { | |
349 | return acpi_tad_wake_write(dev, buf, "_STP", timer_id, policy_specval); | |
350 | } | |
351 | ||
352 | static ssize_t acpi_tad_policy_read(struct device *dev, char *buf, u32 timer_id) | |
353 | { | |
354 | return acpi_tad_wake_read(dev, buf, "_TIP", timer_id, policy_specval); | |
355 | } | |
356 | ||
357 | static int acpi_tad_clear_status(struct device *dev, u32 timer_id) | |
358 | { | |
359 | acpi_handle handle = ACPI_HANDLE(dev); | |
360 | union acpi_object args[] = { | |
361 | { .type = ACPI_TYPE_INTEGER, }, | |
362 | }; | |
363 | struct acpi_object_list arg_list = { | |
364 | .pointer = args, | |
365 | .count = ARRAY_SIZE(args), | |
366 | }; | |
367 | unsigned long long retval; | |
368 | acpi_status status; | |
369 | ||
370 | args[0].integer.value = timer_id; | |
371 | ||
372 | pm_runtime_get_sync(dev); | |
373 | ||
374 | status = acpi_evaluate_integer(handle, "_CWS", &arg_list, &retval); | |
375 | ||
376 | pm_runtime_put_sync(dev); | |
377 | ||
378 | if (ACPI_FAILURE(status) || retval) | |
379 | return -EIO; | |
380 | ||
381 | return 0; | |
382 | } | |
383 | ||
384 | static int acpi_tad_status_write(struct device *dev, const char *buf, u32 timer_id) | |
385 | { | |
386 | int ret, value; | |
387 | ||
388 | ret = kstrtoint(buf, 0, &value); | |
389 | if (ret) | |
390 | return ret; | |
391 | ||
392 | if (value) | |
393 | return -EINVAL; | |
394 | ||
395 | return acpi_tad_clear_status(dev, timer_id); | |
396 | } | |
397 | ||
398 | static ssize_t acpi_tad_status_read(struct device *dev, char *buf, u32 timer_id) | |
399 | { | |
400 | acpi_handle handle = ACPI_HANDLE(dev); | |
401 | union acpi_object args[] = { | |
402 | { .type = ACPI_TYPE_INTEGER, }, | |
403 | }; | |
404 | struct acpi_object_list arg_list = { | |
405 | .pointer = args, | |
406 | .count = ARRAY_SIZE(args), | |
407 | }; | |
408 | unsigned long long retval; | |
409 | acpi_status status; | |
410 | ||
411 | args[0].integer.value = timer_id; | |
412 | ||
413 | pm_runtime_get_sync(dev); | |
414 | ||
415 | status = acpi_evaluate_integer(handle, "_GWS", &arg_list, &retval); | |
416 | ||
417 | pm_runtime_put_sync(dev); | |
418 | ||
419 | if (ACPI_FAILURE(status)) | |
420 | return -EIO; | |
421 | ||
422 | return sprintf(buf, "0x%02X\n", (u32)retval); | |
423 | } | |
424 | ||
425 | static ssize_t caps_show(struct device *dev, struct device_attribute *attr, | |
426 | char *buf) | |
427 | { | |
428 | struct acpi_tad_driver_data *dd = dev_get_drvdata(dev); | |
429 | ||
430 | return sprintf(buf, "0x%02X\n", dd->capabilities); | |
431 | } | |
432 | ||
433 | static DEVICE_ATTR_RO(caps); | |
434 | ||
435 | static ssize_t ac_alarm_store(struct device *dev, struct device_attribute *attr, | |
436 | const char *buf, size_t count) | |
437 | { | |
438 | int ret = acpi_tad_alarm_write(dev, buf, ACPI_TAD_AC_TIMER); | |
439 | ||
440 | return ret ? ret : count; | |
441 | } | |
442 | ||
443 | static ssize_t ac_alarm_show(struct device *dev, struct device_attribute *attr, | |
444 | char *buf) | |
445 | { | |
446 | return acpi_tad_alarm_read(dev, buf, ACPI_TAD_AC_TIMER); | |
447 | } | |
448 | ||
449 | static DEVICE_ATTR(ac_alarm, S_IRUSR | S_IWUSR, ac_alarm_show, ac_alarm_store); | |
450 | ||
451 | static ssize_t ac_policy_store(struct device *dev, struct device_attribute *attr, | |
452 | const char *buf, size_t count) | |
453 | { | |
454 | int ret = acpi_tad_policy_write(dev, buf, ACPI_TAD_AC_TIMER); | |
455 | ||
456 | return ret ? ret : count; | |
457 | } | |
458 | ||
459 | static ssize_t ac_policy_show(struct device *dev, struct device_attribute *attr, | |
460 | char *buf) | |
461 | { | |
462 | return acpi_tad_policy_read(dev, buf, ACPI_TAD_AC_TIMER); | |
463 | } | |
464 | ||
465 | static DEVICE_ATTR(ac_policy, S_IRUSR | S_IWUSR, ac_policy_show, ac_policy_store); | |
466 | ||
467 | static ssize_t ac_status_store(struct device *dev, struct device_attribute *attr, | |
468 | const char *buf, size_t count) | |
469 | { | |
470 | int ret = acpi_tad_status_write(dev, buf, ACPI_TAD_AC_TIMER); | |
471 | ||
472 | return ret ? ret : count; | |
473 | } | |
474 | ||
475 | static ssize_t ac_status_show(struct device *dev, struct device_attribute *attr, | |
476 | char *buf) | |
477 | { | |
478 | return acpi_tad_status_read(dev, buf, ACPI_TAD_AC_TIMER); | |
479 | } | |
480 | ||
481 | static DEVICE_ATTR(ac_status, S_IRUSR | S_IWUSR, ac_status_show, ac_status_store); | |
482 | ||
483 | static struct attribute *acpi_tad_attrs[] = { | |
484 | &dev_attr_caps.attr, | |
485 | &dev_attr_ac_alarm.attr, | |
486 | &dev_attr_ac_policy.attr, | |
487 | &dev_attr_ac_status.attr, | |
488 | NULL, | |
489 | }; | |
490 | static const struct attribute_group acpi_tad_attr_group = { | |
491 | .attrs = acpi_tad_attrs, | |
492 | }; | |
493 | ||
494 | static ssize_t dc_alarm_store(struct device *dev, struct device_attribute *attr, | |
495 | const char *buf, size_t count) | |
496 | { | |
497 | int ret = acpi_tad_alarm_write(dev, buf, ACPI_TAD_DC_TIMER); | |
498 | ||
499 | return ret ? ret : count; | |
500 | } | |
501 | ||
502 | static ssize_t dc_alarm_show(struct device *dev, struct device_attribute *attr, | |
503 | char *buf) | |
504 | { | |
505 | return acpi_tad_alarm_read(dev, buf, ACPI_TAD_DC_TIMER); | |
506 | } | |
507 | ||
508 | static DEVICE_ATTR(dc_alarm, S_IRUSR | S_IWUSR, dc_alarm_show, dc_alarm_store); | |
509 | ||
510 | static ssize_t dc_policy_store(struct device *dev, struct device_attribute *attr, | |
511 | const char *buf, size_t count) | |
512 | { | |
513 | int ret = acpi_tad_policy_write(dev, buf, ACPI_TAD_DC_TIMER); | |
514 | ||
515 | return ret ? ret : count; | |
516 | } | |
517 | ||
518 | static ssize_t dc_policy_show(struct device *dev, struct device_attribute *attr, | |
519 | char *buf) | |
520 | { | |
521 | return acpi_tad_policy_read(dev, buf, ACPI_TAD_DC_TIMER); | |
522 | } | |
523 | ||
524 | static DEVICE_ATTR(dc_policy, S_IRUSR | S_IWUSR, dc_policy_show, dc_policy_store); | |
525 | ||
526 | static ssize_t dc_status_store(struct device *dev, struct device_attribute *attr, | |
527 | const char *buf, size_t count) | |
528 | { | |
529 | int ret = acpi_tad_status_write(dev, buf, ACPI_TAD_DC_TIMER); | |
530 | ||
531 | return ret ? ret : count; | |
532 | } | |
533 | ||
534 | static ssize_t dc_status_show(struct device *dev, struct device_attribute *attr, | |
535 | char *buf) | |
536 | { | |
537 | return acpi_tad_status_read(dev, buf, ACPI_TAD_DC_TIMER); | |
538 | } | |
539 | ||
540 | static DEVICE_ATTR(dc_status, S_IRUSR | S_IWUSR, dc_status_show, dc_status_store); | |
541 | ||
542 | static struct attribute *acpi_tad_dc_attrs[] = { | |
543 | &dev_attr_dc_alarm.attr, | |
544 | &dev_attr_dc_policy.attr, | |
545 | &dev_attr_dc_status.attr, | |
546 | NULL, | |
547 | }; | |
548 | static const struct attribute_group acpi_tad_dc_attr_group = { | |
549 | .attrs = acpi_tad_dc_attrs, | |
550 | }; | |
551 | ||
552 | static int acpi_tad_disable_timer(struct device *dev, u32 timer_id) | |
553 | { | |
554 | return acpi_tad_wake_set(dev, "_STV", timer_id, ACPI_TAD_WAKE_DISABLED); | |
555 | } | |
556 | ||
557 | static int acpi_tad_remove(struct platform_device *pdev) | |
558 | { | |
559 | struct device *dev = &pdev->dev; | |
560 | struct acpi_tad_driver_data *dd = dev_get_drvdata(dev); | |
561 | ||
562 | device_init_wakeup(dev, false); | |
563 | ||
564 | pm_runtime_get_sync(dev); | |
565 | ||
566 | if (dd->capabilities & ACPI_TAD_DC_WAKE) | |
567 | sysfs_remove_group(&dev->kobj, &acpi_tad_dc_attr_group); | |
568 | ||
569 | sysfs_remove_group(&dev->kobj, &acpi_tad_attr_group); | |
570 | ||
571 | acpi_tad_disable_timer(dev, ACPI_TAD_AC_TIMER); | |
572 | acpi_tad_clear_status(dev, ACPI_TAD_AC_TIMER); | |
573 | if (dd->capabilities & ACPI_TAD_DC_WAKE) { | |
574 | acpi_tad_disable_timer(dev, ACPI_TAD_DC_TIMER); | |
575 | acpi_tad_clear_status(dev, ACPI_TAD_DC_TIMER); | |
576 | } | |
577 | ||
578 | pm_runtime_put_sync(dev); | |
579 | pm_runtime_disable(dev); | |
580 | return 0; | |
581 | } | |
582 | ||
583 | static int acpi_tad_probe(struct platform_device *pdev) | |
584 | { | |
585 | struct device *dev = &pdev->dev; | |
586 | acpi_handle handle = ACPI_HANDLE(dev); | |
587 | struct acpi_tad_driver_data *dd; | |
588 | acpi_status status; | |
589 | unsigned long long caps; | |
590 | int ret; | |
591 | ||
592 | /* | |
593 | * Initialization failure messages are mostly about firmware issues, so | |
594 | * print them at the "info" level. | |
595 | */ | |
596 | status = acpi_evaluate_integer(handle, "_GCP", NULL, &caps); | |
597 | if (ACPI_FAILURE(status)) { | |
598 | dev_info(dev, "Unable to get capabilities\n"); | |
599 | return -ENODEV; | |
600 | } | |
601 | ||
602 | if (!(caps & ACPI_TAD_AC_WAKE)) { | |
603 | dev_info(dev, "Unsupported capabilities\n"); | |
604 | return -ENODEV; | |
605 | } | |
606 | ||
607 | if (!acpi_has_method(handle, "_PRW")) { | |
608 | dev_info(dev, "Missing _PRW\n"); | |
609 | return -ENODEV; | |
610 | } | |
611 | ||
612 | dd = devm_kzalloc(dev, sizeof(*dd), GFP_KERNEL); | |
613 | if (!dd) | |
614 | return -ENOMEM; | |
615 | ||
616 | dd->capabilities = caps; | |
617 | dev_set_drvdata(dev, dd); | |
618 | ||
619 | /* | |
620 | * Assume that the ACPI PM domain has been attached to the device and | |
621 | * simply enable system wakeup and runtime PM and put the device into | |
622 | * runtime suspend. Everything else should be taken care of by the ACPI | |
623 | * PM domain callbacks. | |
624 | */ | |
625 | device_init_wakeup(dev, true); | |
626 | dev_pm_set_driver_flags(dev, DPM_FLAG_SMART_SUSPEND | | |
627 | DPM_FLAG_LEAVE_SUSPENDED); | |
628 | /* | |
629 | * The platform bus type layer tells the ACPI PM domain powers up the | |
630 | * device, so set the runtime PM status of it to "active". | |
631 | */ | |
632 | pm_runtime_set_active(dev); | |
633 | pm_runtime_enable(dev); | |
634 | pm_runtime_suspend(dev); | |
635 | ||
636 | ret = sysfs_create_group(&dev->kobj, &acpi_tad_attr_group); | |
637 | if (ret) | |
638 | goto fail; | |
639 | ||
640 | if (caps & ACPI_TAD_DC_WAKE) { | |
641 | ret = sysfs_create_group(&dev->kobj, &acpi_tad_dc_attr_group); | |
642 | if (ret) | |
643 | goto fail; | |
644 | } | |
645 | ||
3230b2b3 RW |
646 | if (caps & ACPI_TAD_RT) { |
647 | ret = sysfs_create_group(&dev->kobj, &acpi_tad_time_attr_group); | |
648 | if (ret) | |
649 | goto fail; | |
650 | } | |
651 | ||
95c513ec RW |
652 | return 0; |
653 | ||
654 | fail: | |
655 | acpi_tad_remove(pdev); | |
656 | return ret; | |
657 | } | |
658 | ||
659 | static const struct acpi_device_id acpi_tad_ids[] = { | |
660 | {"ACPI000E", 0}, | |
661 | {} | |
662 | }; | |
663 | ||
664 | static struct platform_driver acpi_tad_driver = { | |
665 | .driver = { | |
666 | .name = "acpi-tad", | |
667 | .acpi_match_table = acpi_tad_ids, | |
668 | }, | |
669 | .probe = acpi_tad_probe, | |
670 | .remove = acpi_tad_remove, | |
671 | }; | |
672 | MODULE_DEVICE_TABLE(acpi, acpi_tad_ids); | |
673 | ||
674 | module_platform_driver(acpi_tad_driver); |