18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ACPI Time and Alarm (TAD) Device Driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2018 Intel Corporation 68c2ecf20Sopenharmony_ci * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * This driver is based on Section 9.18 of the ACPI 6.2 specification revision. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * It only supports the system wakeup capabilities of the TAD. 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * Provided are sysfs attributes, available under the TAD platform device, 138c2ecf20Sopenharmony_ci * allowing user space to manage the AC and DC wakeup timers of the TAD: 148c2ecf20Sopenharmony_ci * set and read their values, set and check their expire timer wake policies, 158c2ecf20Sopenharmony_ci * check and clear their status and check the capabilities of the TAD reported 168c2ecf20Sopenharmony_ci * by AML. The DC timer attributes are only present if the TAD supports a 178c2ecf20Sopenharmony_ci * separate DC alarm timer. 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * The wakeup events handling and power management of the TAD is expected to 208c2ecf20Sopenharmony_ci * be taken care of by the ACPI PM domain attached to its platform device. 218c2ecf20Sopenharmony_ci */ 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include <linux/acpi.h> 248c2ecf20Sopenharmony_ci#include <linux/kernel.h> 258c2ecf20Sopenharmony_ci#include <linux/module.h> 268c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 278c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 288c2ecf20Sopenharmony_ci#include <linux/suspend.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 318c2ecf20Sopenharmony_ciMODULE_AUTHOR("Rafael J. Wysocki"); 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci/* ACPI TAD capability flags (ACPI 6.2, Section 9.18.2) */ 348c2ecf20Sopenharmony_ci#define ACPI_TAD_AC_WAKE BIT(0) 358c2ecf20Sopenharmony_ci#define ACPI_TAD_DC_WAKE BIT(1) 368c2ecf20Sopenharmony_ci#define ACPI_TAD_RT BIT(2) 378c2ecf20Sopenharmony_ci#define ACPI_TAD_RT_IN_MS BIT(3) 388c2ecf20Sopenharmony_ci#define ACPI_TAD_S4_S5__GWS BIT(4) 398c2ecf20Sopenharmony_ci#define ACPI_TAD_AC_S4_WAKE BIT(5) 408c2ecf20Sopenharmony_ci#define ACPI_TAD_AC_S5_WAKE BIT(6) 418c2ecf20Sopenharmony_ci#define ACPI_TAD_DC_S4_WAKE BIT(7) 428c2ecf20Sopenharmony_ci#define ACPI_TAD_DC_S5_WAKE BIT(8) 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/* ACPI TAD alarm timer selection */ 458c2ecf20Sopenharmony_ci#define ACPI_TAD_AC_TIMER (u32)0 468c2ecf20Sopenharmony_ci#define ACPI_TAD_DC_TIMER (u32)1 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/* Special value for disabled timer or expired timer wake policy. */ 498c2ecf20Sopenharmony_ci#define ACPI_TAD_WAKE_DISABLED (~(u32)0) 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistruct acpi_tad_driver_data { 528c2ecf20Sopenharmony_ci u32 capabilities; 538c2ecf20Sopenharmony_ci}; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistruct acpi_tad_rt { 568c2ecf20Sopenharmony_ci u16 year; /* 1900 - 9999 */ 578c2ecf20Sopenharmony_ci u8 month; /* 1 - 12 */ 588c2ecf20Sopenharmony_ci u8 day; /* 1 - 31 */ 598c2ecf20Sopenharmony_ci u8 hour; /* 0 - 23 */ 608c2ecf20Sopenharmony_ci u8 minute; /* 0 - 59 */ 618c2ecf20Sopenharmony_ci u8 second; /* 0 - 59 */ 628c2ecf20Sopenharmony_ci u8 valid; /* 0 (failed) or 1 (success) for reads, 0 for writes */ 638c2ecf20Sopenharmony_ci u16 msec; /* 1 - 1000 */ 648c2ecf20Sopenharmony_ci s16 tz; /* -1440 to 1440 or 2047 (unspecified) */ 658c2ecf20Sopenharmony_ci u8 daylight; 668c2ecf20Sopenharmony_ci u8 padding[3]; /* must be 0 */ 678c2ecf20Sopenharmony_ci} __packed; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic int acpi_tad_set_real_time(struct device *dev, struct acpi_tad_rt *rt) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci acpi_handle handle = ACPI_HANDLE(dev); 728c2ecf20Sopenharmony_ci union acpi_object args[] = { 738c2ecf20Sopenharmony_ci { .type = ACPI_TYPE_BUFFER, }, 748c2ecf20Sopenharmony_ci }; 758c2ecf20Sopenharmony_ci struct acpi_object_list arg_list = { 768c2ecf20Sopenharmony_ci .pointer = args, 778c2ecf20Sopenharmony_ci .count = ARRAY_SIZE(args), 788c2ecf20Sopenharmony_ci }; 798c2ecf20Sopenharmony_ci unsigned long long retval; 808c2ecf20Sopenharmony_ci acpi_status status; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci if (rt->year < 1900 || rt->year > 9999 || 838c2ecf20Sopenharmony_ci rt->month < 1 || rt->month > 12 || 848c2ecf20Sopenharmony_ci rt->hour > 23 || rt->minute > 59 || rt->second > 59 || 858c2ecf20Sopenharmony_ci rt->tz < -1440 || (rt->tz > 1440 && rt->tz != 2047) || 868c2ecf20Sopenharmony_ci rt->daylight > 3) 878c2ecf20Sopenharmony_ci return -ERANGE; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci args[0].buffer.pointer = (u8 *)rt; 908c2ecf20Sopenharmony_ci args[0].buffer.length = sizeof(*rt); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci pm_runtime_get_sync(dev); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci status = acpi_evaluate_integer(handle, "_SRT", &arg_list, &retval); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci pm_runtime_put_sync(dev); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status) || retval) 998c2ecf20Sopenharmony_ci return -EIO; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci return 0; 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic int acpi_tad_get_real_time(struct device *dev, struct acpi_tad_rt *rt) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci acpi_handle handle = ACPI_HANDLE(dev); 1078c2ecf20Sopenharmony_ci struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER }; 1088c2ecf20Sopenharmony_ci union acpi_object *out_obj; 1098c2ecf20Sopenharmony_ci struct acpi_tad_rt *data; 1108c2ecf20Sopenharmony_ci acpi_status status; 1118c2ecf20Sopenharmony_ci int ret = -EIO; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci pm_runtime_get_sync(dev); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci status = acpi_evaluate_object(handle, "_GRT", NULL, &output); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci pm_runtime_put_sync(dev); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) 1208c2ecf20Sopenharmony_ci goto out_free; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci out_obj = output.pointer; 1238c2ecf20Sopenharmony_ci if (out_obj->type != ACPI_TYPE_BUFFER) 1248c2ecf20Sopenharmony_ci goto out_free; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci if (out_obj->buffer.length != sizeof(*rt)) 1278c2ecf20Sopenharmony_ci goto out_free; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci data = (struct acpi_tad_rt *)(out_obj->buffer.pointer); 1308c2ecf20Sopenharmony_ci if (!data->valid) 1318c2ecf20Sopenharmony_ci goto out_free; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci memcpy(rt, data, sizeof(*rt)); 1348c2ecf20Sopenharmony_ci ret = 0; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ciout_free: 1378c2ecf20Sopenharmony_ci ACPI_FREE(output.pointer); 1388c2ecf20Sopenharmony_ci return ret; 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic char *acpi_tad_rt_next_field(char *s, int *val) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci char *p; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci p = strchr(s, ':'); 1468c2ecf20Sopenharmony_ci if (!p) 1478c2ecf20Sopenharmony_ci return NULL; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci *p = '\0'; 1508c2ecf20Sopenharmony_ci if (kstrtoint(s, 10, val)) 1518c2ecf20Sopenharmony_ci return NULL; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci return p + 1; 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic ssize_t time_store(struct device *dev, struct device_attribute *attr, 1578c2ecf20Sopenharmony_ci const char *buf, size_t count) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci struct acpi_tad_rt rt; 1608c2ecf20Sopenharmony_ci char *str, *s; 1618c2ecf20Sopenharmony_ci int val, ret = -ENODATA; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci str = kmemdup_nul(buf, count, GFP_KERNEL); 1648c2ecf20Sopenharmony_ci if (!str) 1658c2ecf20Sopenharmony_ci return -ENOMEM; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci s = acpi_tad_rt_next_field(str, &val); 1688c2ecf20Sopenharmony_ci if (!s) 1698c2ecf20Sopenharmony_ci goto out_free; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci rt.year = val; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci s = acpi_tad_rt_next_field(s, &val); 1748c2ecf20Sopenharmony_ci if (!s) 1758c2ecf20Sopenharmony_ci goto out_free; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci rt.month = val; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci s = acpi_tad_rt_next_field(s, &val); 1808c2ecf20Sopenharmony_ci if (!s) 1818c2ecf20Sopenharmony_ci goto out_free; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci rt.day = val; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci s = acpi_tad_rt_next_field(s, &val); 1868c2ecf20Sopenharmony_ci if (!s) 1878c2ecf20Sopenharmony_ci goto out_free; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci rt.hour = val; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci s = acpi_tad_rt_next_field(s, &val); 1928c2ecf20Sopenharmony_ci if (!s) 1938c2ecf20Sopenharmony_ci goto out_free; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci rt.minute = val; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci s = acpi_tad_rt_next_field(s, &val); 1988c2ecf20Sopenharmony_ci if (!s) 1998c2ecf20Sopenharmony_ci goto out_free; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci rt.second = val; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci s = acpi_tad_rt_next_field(s, &val); 2048c2ecf20Sopenharmony_ci if (!s) 2058c2ecf20Sopenharmony_ci goto out_free; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci rt.tz = val; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (kstrtoint(s, 10, &val)) 2108c2ecf20Sopenharmony_ci goto out_free; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci rt.daylight = val; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci rt.valid = 0; 2158c2ecf20Sopenharmony_ci rt.msec = 0; 2168c2ecf20Sopenharmony_ci memset(rt.padding, 0, 3); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci ret = acpi_tad_set_real_time(dev, &rt); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ciout_free: 2218c2ecf20Sopenharmony_ci kfree(str); 2228c2ecf20Sopenharmony_ci return ret ? ret : count; 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic ssize_t time_show(struct device *dev, struct device_attribute *attr, 2268c2ecf20Sopenharmony_ci char *buf) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci struct acpi_tad_rt rt; 2298c2ecf20Sopenharmony_ci int ret; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci ret = acpi_tad_get_real_time(dev, &rt); 2328c2ecf20Sopenharmony_ci if (ret) 2338c2ecf20Sopenharmony_ci return ret; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci return sprintf(buf, "%u:%u:%u:%u:%u:%u:%d:%u\n", 2368c2ecf20Sopenharmony_ci rt.year, rt.month, rt.day, rt.hour, rt.minute, rt.second, 2378c2ecf20Sopenharmony_ci rt.tz, rt.daylight); 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(time); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic struct attribute *acpi_tad_time_attrs[] = { 2438c2ecf20Sopenharmony_ci &dev_attr_time.attr, 2448c2ecf20Sopenharmony_ci NULL, 2458c2ecf20Sopenharmony_ci}; 2468c2ecf20Sopenharmony_cistatic const struct attribute_group acpi_tad_time_attr_group = { 2478c2ecf20Sopenharmony_ci .attrs = acpi_tad_time_attrs, 2488c2ecf20Sopenharmony_ci}; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic int acpi_tad_wake_set(struct device *dev, char *method, u32 timer_id, 2518c2ecf20Sopenharmony_ci u32 value) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci acpi_handle handle = ACPI_HANDLE(dev); 2548c2ecf20Sopenharmony_ci union acpi_object args[] = { 2558c2ecf20Sopenharmony_ci { .type = ACPI_TYPE_INTEGER, }, 2568c2ecf20Sopenharmony_ci { .type = ACPI_TYPE_INTEGER, }, 2578c2ecf20Sopenharmony_ci }; 2588c2ecf20Sopenharmony_ci struct acpi_object_list arg_list = { 2598c2ecf20Sopenharmony_ci .pointer = args, 2608c2ecf20Sopenharmony_ci .count = ARRAY_SIZE(args), 2618c2ecf20Sopenharmony_ci }; 2628c2ecf20Sopenharmony_ci unsigned long long retval; 2638c2ecf20Sopenharmony_ci acpi_status status; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci args[0].integer.value = timer_id; 2668c2ecf20Sopenharmony_ci args[1].integer.value = value; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci pm_runtime_get_sync(dev); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci status = acpi_evaluate_integer(handle, method, &arg_list, &retval); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci pm_runtime_put_sync(dev); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status) || retval) 2758c2ecf20Sopenharmony_ci return -EIO; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci return 0; 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cistatic int acpi_tad_wake_write(struct device *dev, const char *buf, char *method, 2818c2ecf20Sopenharmony_ci u32 timer_id, const char *specval) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci u32 value; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci if (sysfs_streq(buf, specval)) { 2868c2ecf20Sopenharmony_ci value = ACPI_TAD_WAKE_DISABLED; 2878c2ecf20Sopenharmony_ci } else { 2888c2ecf20Sopenharmony_ci int ret = kstrtou32(buf, 0, &value); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci if (ret) 2918c2ecf20Sopenharmony_ci return ret; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci if (value == ACPI_TAD_WAKE_DISABLED) 2948c2ecf20Sopenharmony_ci return -EINVAL; 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci return acpi_tad_wake_set(dev, method, timer_id, value); 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_cistatic ssize_t acpi_tad_wake_read(struct device *dev, char *buf, char *method, 3018c2ecf20Sopenharmony_ci u32 timer_id, const char *specval) 3028c2ecf20Sopenharmony_ci{ 3038c2ecf20Sopenharmony_ci acpi_handle handle = ACPI_HANDLE(dev); 3048c2ecf20Sopenharmony_ci union acpi_object args[] = { 3058c2ecf20Sopenharmony_ci { .type = ACPI_TYPE_INTEGER, }, 3068c2ecf20Sopenharmony_ci }; 3078c2ecf20Sopenharmony_ci struct acpi_object_list arg_list = { 3088c2ecf20Sopenharmony_ci .pointer = args, 3098c2ecf20Sopenharmony_ci .count = ARRAY_SIZE(args), 3108c2ecf20Sopenharmony_ci }; 3118c2ecf20Sopenharmony_ci unsigned long long retval; 3128c2ecf20Sopenharmony_ci acpi_status status; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci args[0].integer.value = timer_id; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci pm_runtime_get_sync(dev); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci status = acpi_evaluate_integer(handle, method, &arg_list, &retval); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci pm_runtime_put_sync(dev); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) 3238c2ecf20Sopenharmony_ci return -EIO; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci if ((u32)retval == ACPI_TAD_WAKE_DISABLED) 3268c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", specval); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", (u32)retval); 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic const char *alarm_specval = "disabled"; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_cistatic int acpi_tad_alarm_write(struct device *dev, const char *buf, 3348c2ecf20Sopenharmony_ci u32 timer_id) 3358c2ecf20Sopenharmony_ci{ 3368c2ecf20Sopenharmony_ci return acpi_tad_wake_write(dev, buf, "_STV", timer_id, alarm_specval); 3378c2ecf20Sopenharmony_ci} 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_cistatic ssize_t acpi_tad_alarm_read(struct device *dev, char *buf, u32 timer_id) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci return acpi_tad_wake_read(dev, buf, "_TIV", timer_id, alarm_specval); 3428c2ecf20Sopenharmony_ci} 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_cistatic const char *policy_specval = "never"; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_cistatic int acpi_tad_policy_write(struct device *dev, const char *buf, 3478c2ecf20Sopenharmony_ci u32 timer_id) 3488c2ecf20Sopenharmony_ci{ 3498c2ecf20Sopenharmony_ci return acpi_tad_wake_write(dev, buf, "_STP", timer_id, policy_specval); 3508c2ecf20Sopenharmony_ci} 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_cistatic ssize_t acpi_tad_policy_read(struct device *dev, char *buf, u32 timer_id) 3538c2ecf20Sopenharmony_ci{ 3548c2ecf20Sopenharmony_ci return acpi_tad_wake_read(dev, buf, "_TIP", timer_id, policy_specval); 3558c2ecf20Sopenharmony_ci} 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_cistatic int acpi_tad_clear_status(struct device *dev, u32 timer_id) 3588c2ecf20Sopenharmony_ci{ 3598c2ecf20Sopenharmony_ci acpi_handle handle = ACPI_HANDLE(dev); 3608c2ecf20Sopenharmony_ci union acpi_object args[] = { 3618c2ecf20Sopenharmony_ci { .type = ACPI_TYPE_INTEGER, }, 3628c2ecf20Sopenharmony_ci }; 3638c2ecf20Sopenharmony_ci struct acpi_object_list arg_list = { 3648c2ecf20Sopenharmony_ci .pointer = args, 3658c2ecf20Sopenharmony_ci .count = ARRAY_SIZE(args), 3668c2ecf20Sopenharmony_ci }; 3678c2ecf20Sopenharmony_ci unsigned long long retval; 3688c2ecf20Sopenharmony_ci acpi_status status; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci args[0].integer.value = timer_id; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci pm_runtime_get_sync(dev); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci status = acpi_evaluate_integer(handle, "_CWS", &arg_list, &retval); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci pm_runtime_put_sync(dev); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status) || retval) 3798c2ecf20Sopenharmony_ci return -EIO; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci return 0; 3828c2ecf20Sopenharmony_ci} 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_cistatic int acpi_tad_status_write(struct device *dev, const char *buf, u32 timer_id) 3858c2ecf20Sopenharmony_ci{ 3868c2ecf20Sopenharmony_ci int ret, value; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci ret = kstrtoint(buf, 0, &value); 3898c2ecf20Sopenharmony_ci if (ret) 3908c2ecf20Sopenharmony_ci return ret; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci if (value) 3938c2ecf20Sopenharmony_ci return -EINVAL; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci return acpi_tad_clear_status(dev, timer_id); 3968c2ecf20Sopenharmony_ci} 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_cistatic ssize_t acpi_tad_status_read(struct device *dev, char *buf, u32 timer_id) 3998c2ecf20Sopenharmony_ci{ 4008c2ecf20Sopenharmony_ci acpi_handle handle = ACPI_HANDLE(dev); 4018c2ecf20Sopenharmony_ci union acpi_object args[] = { 4028c2ecf20Sopenharmony_ci { .type = ACPI_TYPE_INTEGER, }, 4038c2ecf20Sopenharmony_ci }; 4048c2ecf20Sopenharmony_ci struct acpi_object_list arg_list = { 4058c2ecf20Sopenharmony_ci .pointer = args, 4068c2ecf20Sopenharmony_ci .count = ARRAY_SIZE(args), 4078c2ecf20Sopenharmony_ci }; 4088c2ecf20Sopenharmony_ci unsigned long long retval; 4098c2ecf20Sopenharmony_ci acpi_status status; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci args[0].integer.value = timer_id; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci pm_runtime_get_sync(dev); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci status = acpi_evaluate_integer(handle, "_GWS", &arg_list, &retval); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci pm_runtime_put_sync(dev); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) 4208c2ecf20Sopenharmony_ci return -EIO; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci return sprintf(buf, "0x%02X\n", (u32)retval); 4238c2ecf20Sopenharmony_ci} 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_cistatic ssize_t caps_show(struct device *dev, struct device_attribute *attr, 4268c2ecf20Sopenharmony_ci char *buf) 4278c2ecf20Sopenharmony_ci{ 4288c2ecf20Sopenharmony_ci struct acpi_tad_driver_data *dd = dev_get_drvdata(dev); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci return sprintf(buf, "0x%02X\n", dd->capabilities); 4318c2ecf20Sopenharmony_ci} 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(caps); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_cistatic ssize_t ac_alarm_store(struct device *dev, struct device_attribute *attr, 4368c2ecf20Sopenharmony_ci const char *buf, size_t count) 4378c2ecf20Sopenharmony_ci{ 4388c2ecf20Sopenharmony_ci int ret = acpi_tad_alarm_write(dev, buf, ACPI_TAD_AC_TIMER); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci return ret ? ret : count; 4418c2ecf20Sopenharmony_ci} 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_cistatic ssize_t ac_alarm_show(struct device *dev, struct device_attribute *attr, 4448c2ecf20Sopenharmony_ci char *buf) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci return acpi_tad_alarm_read(dev, buf, ACPI_TAD_AC_TIMER); 4478c2ecf20Sopenharmony_ci} 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(ac_alarm); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_cistatic ssize_t ac_policy_store(struct device *dev, struct device_attribute *attr, 4528c2ecf20Sopenharmony_ci const char *buf, size_t count) 4538c2ecf20Sopenharmony_ci{ 4548c2ecf20Sopenharmony_ci int ret = acpi_tad_policy_write(dev, buf, ACPI_TAD_AC_TIMER); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci return ret ? ret : count; 4578c2ecf20Sopenharmony_ci} 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_cistatic ssize_t ac_policy_show(struct device *dev, struct device_attribute *attr, 4608c2ecf20Sopenharmony_ci char *buf) 4618c2ecf20Sopenharmony_ci{ 4628c2ecf20Sopenharmony_ci return acpi_tad_policy_read(dev, buf, ACPI_TAD_AC_TIMER); 4638c2ecf20Sopenharmony_ci} 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(ac_policy); 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_cistatic ssize_t ac_status_store(struct device *dev, struct device_attribute *attr, 4688c2ecf20Sopenharmony_ci const char *buf, size_t count) 4698c2ecf20Sopenharmony_ci{ 4708c2ecf20Sopenharmony_ci int ret = acpi_tad_status_write(dev, buf, ACPI_TAD_AC_TIMER); 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci return ret ? ret : count; 4738c2ecf20Sopenharmony_ci} 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_cistatic ssize_t ac_status_show(struct device *dev, struct device_attribute *attr, 4768c2ecf20Sopenharmony_ci char *buf) 4778c2ecf20Sopenharmony_ci{ 4788c2ecf20Sopenharmony_ci return acpi_tad_status_read(dev, buf, ACPI_TAD_AC_TIMER); 4798c2ecf20Sopenharmony_ci} 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(ac_status); 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_cistatic struct attribute *acpi_tad_attrs[] = { 4848c2ecf20Sopenharmony_ci &dev_attr_caps.attr, 4858c2ecf20Sopenharmony_ci &dev_attr_ac_alarm.attr, 4868c2ecf20Sopenharmony_ci &dev_attr_ac_policy.attr, 4878c2ecf20Sopenharmony_ci &dev_attr_ac_status.attr, 4888c2ecf20Sopenharmony_ci NULL, 4898c2ecf20Sopenharmony_ci}; 4908c2ecf20Sopenharmony_cistatic const struct attribute_group acpi_tad_attr_group = { 4918c2ecf20Sopenharmony_ci .attrs = acpi_tad_attrs, 4928c2ecf20Sopenharmony_ci}; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_cistatic ssize_t dc_alarm_store(struct device *dev, struct device_attribute *attr, 4958c2ecf20Sopenharmony_ci const char *buf, size_t count) 4968c2ecf20Sopenharmony_ci{ 4978c2ecf20Sopenharmony_ci int ret = acpi_tad_alarm_write(dev, buf, ACPI_TAD_DC_TIMER); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci return ret ? ret : count; 5008c2ecf20Sopenharmony_ci} 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_cistatic ssize_t dc_alarm_show(struct device *dev, struct device_attribute *attr, 5038c2ecf20Sopenharmony_ci char *buf) 5048c2ecf20Sopenharmony_ci{ 5058c2ecf20Sopenharmony_ci return acpi_tad_alarm_read(dev, buf, ACPI_TAD_DC_TIMER); 5068c2ecf20Sopenharmony_ci} 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(dc_alarm); 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_cistatic ssize_t dc_policy_store(struct device *dev, struct device_attribute *attr, 5118c2ecf20Sopenharmony_ci const char *buf, size_t count) 5128c2ecf20Sopenharmony_ci{ 5138c2ecf20Sopenharmony_ci int ret = acpi_tad_policy_write(dev, buf, ACPI_TAD_DC_TIMER); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci return ret ? ret : count; 5168c2ecf20Sopenharmony_ci} 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_cistatic ssize_t dc_policy_show(struct device *dev, struct device_attribute *attr, 5198c2ecf20Sopenharmony_ci char *buf) 5208c2ecf20Sopenharmony_ci{ 5218c2ecf20Sopenharmony_ci return acpi_tad_policy_read(dev, buf, ACPI_TAD_DC_TIMER); 5228c2ecf20Sopenharmony_ci} 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(dc_policy); 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_cistatic ssize_t dc_status_store(struct device *dev, struct device_attribute *attr, 5278c2ecf20Sopenharmony_ci const char *buf, size_t count) 5288c2ecf20Sopenharmony_ci{ 5298c2ecf20Sopenharmony_ci int ret = acpi_tad_status_write(dev, buf, ACPI_TAD_DC_TIMER); 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci return ret ? ret : count; 5328c2ecf20Sopenharmony_ci} 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_cistatic ssize_t dc_status_show(struct device *dev, struct device_attribute *attr, 5358c2ecf20Sopenharmony_ci char *buf) 5368c2ecf20Sopenharmony_ci{ 5378c2ecf20Sopenharmony_ci return acpi_tad_status_read(dev, buf, ACPI_TAD_DC_TIMER); 5388c2ecf20Sopenharmony_ci} 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(dc_status); 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_cistatic struct attribute *acpi_tad_dc_attrs[] = { 5438c2ecf20Sopenharmony_ci &dev_attr_dc_alarm.attr, 5448c2ecf20Sopenharmony_ci &dev_attr_dc_policy.attr, 5458c2ecf20Sopenharmony_ci &dev_attr_dc_status.attr, 5468c2ecf20Sopenharmony_ci NULL, 5478c2ecf20Sopenharmony_ci}; 5488c2ecf20Sopenharmony_cistatic const struct attribute_group acpi_tad_dc_attr_group = { 5498c2ecf20Sopenharmony_ci .attrs = acpi_tad_dc_attrs, 5508c2ecf20Sopenharmony_ci}; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_cistatic int acpi_tad_disable_timer(struct device *dev, u32 timer_id) 5538c2ecf20Sopenharmony_ci{ 5548c2ecf20Sopenharmony_ci return acpi_tad_wake_set(dev, "_STV", timer_id, ACPI_TAD_WAKE_DISABLED); 5558c2ecf20Sopenharmony_ci} 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_cistatic int acpi_tad_remove(struct platform_device *pdev) 5588c2ecf20Sopenharmony_ci{ 5598c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 5608c2ecf20Sopenharmony_ci struct acpi_tad_driver_data *dd = dev_get_drvdata(dev); 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci device_init_wakeup(dev, false); 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci pm_runtime_get_sync(dev); 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci if (dd->capabilities & ACPI_TAD_DC_WAKE) 5678c2ecf20Sopenharmony_ci sysfs_remove_group(&dev->kobj, &acpi_tad_dc_attr_group); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci sysfs_remove_group(&dev->kobj, &acpi_tad_attr_group); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci acpi_tad_disable_timer(dev, ACPI_TAD_AC_TIMER); 5728c2ecf20Sopenharmony_ci acpi_tad_clear_status(dev, ACPI_TAD_AC_TIMER); 5738c2ecf20Sopenharmony_ci if (dd->capabilities & ACPI_TAD_DC_WAKE) { 5748c2ecf20Sopenharmony_ci acpi_tad_disable_timer(dev, ACPI_TAD_DC_TIMER); 5758c2ecf20Sopenharmony_ci acpi_tad_clear_status(dev, ACPI_TAD_DC_TIMER); 5768c2ecf20Sopenharmony_ci } 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci pm_runtime_put_sync(dev); 5798c2ecf20Sopenharmony_ci pm_runtime_disable(dev); 5808c2ecf20Sopenharmony_ci return 0; 5818c2ecf20Sopenharmony_ci} 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_cistatic int acpi_tad_probe(struct platform_device *pdev) 5848c2ecf20Sopenharmony_ci{ 5858c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 5868c2ecf20Sopenharmony_ci acpi_handle handle = ACPI_HANDLE(dev); 5878c2ecf20Sopenharmony_ci struct acpi_tad_driver_data *dd; 5888c2ecf20Sopenharmony_ci acpi_status status; 5898c2ecf20Sopenharmony_ci unsigned long long caps; 5908c2ecf20Sopenharmony_ci int ret; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci /* 5938c2ecf20Sopenharmony_ci * Initialization failure messages are mostly about firmware issues, so 5948c2ecf20Sopenharmony_ci * print them at the "info" level. 5958c2ecf20Sopenharmony_ci */ 5968c2ecf20Sopenharmony_ci status = acpi_evaluate_integer(handle, "_GCP", NULL, &caps); 5978c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) { 5988c2ecf20Sopenharmony_ci dev_info(dev, "Unable to get capabilities\n"); 5998c2ecf20Sopenharmony_ci return -ENODEV; 6008c2ecf20Sopenharmony_ci } 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci if (!(caps & ACPI_TAD_AC_WAKE)) { 6038c2ecf20Sopenharmony_ci dev_info(dev, "Unsupported capabilities\n"); 6048c2ecf20Sopenharmony_ci return -ENODEV; 6058c2ecf20Sopenharmony_ci } 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci if (!acpi_has_method(handle, "_PRW")) { 6088c2ecf20Sopenharmony_ci dev_info(dev, "Missing _PRW\n"); 6098c2ecf20Sopenharmony_ci return -ENODEV; 6108c2ecf20Sopenharmony_ci } 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci dd = devm_kzalloc(dev, sizeof(*dd), GFP_KERNEL); 6138c2ecf20Sopenharmony_ci if (!dd) 6148c2ecf20Sopenharmony_ci return -ENOMEM; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci dd->capabilities = caps; 6178c2ecf20Sopenharmony_ci dev_set_drvdata(dev, dd); 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci /* 6208c2ecf20Sopenharmony_ci * Assume that the ACPI PM domain has been attached to the device and 6218c2ecf20Sopenharmony_ci * simply enable system wakeup and runtime PM and put the device into 6228c2ecf20Sopenharmony_ci * runtime suspend. Everything else should be taken care of by the ACPI 6238c2ecf20Sopenharmony_ci * PM domain callbacks. 6248c2ecf20Sopenharmony_ci */ 6258c2ecf20Sopenharmony_ci device_init_wakeup(dev, true); 6268c2ecf20Sopenharmony_ci dev_pm_set_driver_flags(dev, DPM_FLAG_SMART_SUSPEND | 6278c2ecf20Sopenharmony_ci DPM_FLAG_MAY_SKIP_RESUME); 6288c2ecf20Sopenharmony_ci /* 6298c2ecf20Sopenharmony_ci * The platform bus type layer tells the ACPI PM domain powers up the 6308c2ecf20Sopenharmony_ci * device, so set the runtime PM status of it to "active". 6318c2ecf20Sopenharmony_ci */ 6328c2ecf20Sopenharmony_ci pm_runtime_set_active(dev); 6338c2ecf20Sopenharmony_ci pm_runtime_enable(dev); 6348c2ecf20Sopenharmony_ci pm_runtime_suspend(dev); 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci ret = sysfs_create_group(&dev->kobj, &acpi_tad_attr_group); 6378c2ecf20Sopenharmony_ci if (ret) 6388c2ecf20Sopenharmony_ci goto fail; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci if (caps & ACPI_TAD_DC_WAKE) { 6418c2ecf20Sopenharmony_ci ret = sysfs_create_group(&dev->kobj, &acpi_tad_dc_attr_group); 6428c2ecf20Sopenharmony_ci if (ret) 6438c2ecf20Sopenharmony_ci goto fail; 6448c2ecf20Sopenharmony_ci } 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci if (caps & ACPI_TAD_RT) { 6478c2ecf20Sopenharmony_ci ret = sysfs_create_group(&dev->kobj, &acpi_tad_time_attr_group); 6488c2ecf20Sopenharmony_ci if (ret) 6498c2ecf20Sopenharmony_ci goto fail; 6508c2ecf20Sopenharmony_ci } 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci return 0; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_cifail: 6558c2ecf20Sopenharmony_ci acpi_tad_remove(pdev); 6568c2ecf20Sopenharmony_ci return ret; 6578c2ecf20Sopenharmony_ci} 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_cistatic const struct acpi_device_id acpi_tad_ids[] = { 6608c2ecf20Sopenharmony_ci {"ACPI000E", 0}, 6618c2ecf20Sopenharmony_ci {} 6628c2ecf20Sopenharmony_ci}; 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_cistatic struct platform_driver acpi_tad_driver = { 6658c2ecf20Sopenharmony_ci .driver = { 6668c2ecf20Sopenharmony_ci .name = "acpi-tad", 6678c2ecf20Sopenharmony_ci .acpi_match_table = acpi_tad_ids, 6688c2ecf20Sopenharmony_ci }, 6698c2ecf20Sopenharmony_ci .probe = acpi_tad_probe, 6708c2ecf20Sopenharmony_ci .remove = acpi_tad_remove, 6718c2ecf20Sopenharmony_ci}; 6728c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, acpi_tad_ids); 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_cimodule_platform_driver(acpi_tad_driver); 675