195c513ecSRafael J. Wysocki // SPDX-License-Identifier: GPL-2.0 295c513ecSRafael J. Wysocki /* 395c513ecSRafael J. Wysocki * ACPI Time and Alarm (TAD) Device Driver 495c513ecSRafael J. Wysocki * 595c513ecSRafael J. Wysocki * Copyright (C) 2018 Intel Corporation 695c513ecSRafael J. Wysocki * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com> 795c513ecSRafael J. Wysocki * 895c513ecSRafael J. Wysocki * This driver is based on Section 9.18 of the ACPI 6.2 specification revision. 995c513ecSRafael J. Wysocki * 1095c513ecSRafael J. Wysocki * It only supports the system wakeup capabilities of the TAD. 1195c513ecSRafael J. Wysocki * 1295c513ecSRafael J. Wysocki * Provided are sysfs attributes, available under the TAD platform device, 1395c513ecSRafael J. Wysocki * allowing user space to manage the AC and DC wakeup timers of the TAD: 1495c513ecSRafael J. Wysocki * set and read their values, set and check their expire timer wake policies, 1595c513ecSRafael J. Wysocki * check and clear their status and check the capabilities of the TAD reported 1695c513ecSRafael J. Wysocki * by AML. The DC timer attributes are only present if the TAD supports a 1795c513ecSRafael J. Wysocki * separate DC alarm timer. 1895c513ecSRafael J. Wysocki * 1995c513ecSRafael J. Wysocki * The wakeup events handling and power management of the TAD is expected to 2095c513ecSRafael J. Wysocki * be taken care of by the ACPI PM domain attached to its platform device. 2195c513ecSRafael J. Wysocki */ 2295c513ecSRafael J. Wysocki 2395c513ecSRafael J. Wysocki #include <linux/acpi.h> 2495c513ecSRafael J. Wysocki #include <linux/kernel.h> 2595c513ecSRafael J. Wysocki #include <linux/module.h> 2695c513ecSRafael J. Wysocki #include <linux/platform_device.h> 2795c513ecSRafael J. Wysocki #include <linux/pm_runtime.h> 2895c513ecSRafael J. Wysocki #include <linux/suspend.h> 2995c513ecSRafael J. Wysocki 3095c513ecSRafael J. Wysocki MODULE_LICENSE("GPL v2"); 3195c513ecSRafael J. Wysocki MODULE_AUTHOR("Rafael J. Wysocki"); 3295c513ecSRafael J. Wysocki 3395c513ecSRafael J. Wysocki /* ACPI TAD capability flags (ACPI 6.2, Section 9.18.2) */ 3495c513ecSRafael J. Wysocki #define ACPI_TAD_AC_WAKE BIT(0) 3595c513ecSRafael J. Wysocki #define ACPI_TAD_DC_WAKE BIT(1) 3695c513ecSRafael J. Wysocki #define ACPI_TAD_RT BIT(2) 3795c513ecSRafael J. Wysocki #define ACPI_TAD_RT_IN_MS BIT(3) 3895c513ecSRafael J. Wysocki #define ACPI_TAD_S4_S5__GWS BIT(4) 3995c513ecSRafael J. Wysocki #define ACPI_TAD_AC_S4_WAKE BIT(5) 4095c513ecSRafael J. Wysocki #define ACPI_TAD_AC_S5_WAKE BIT(6) 4195c513ecSRafael J. Wysocki #define ACPI_TAD_DC_S4_WAKE BIT(7) 4295c513ecSRafael J. Wysocki #define ACPI_TAD_DC_S5_WAKE BIT(8) 4395c513ecSRafael J. Wysocki 4495c513ecSRafael J. Wysocki /* ACPI TAD alarm timer selection */ 4595c513ecSRafael J. Wysocki #define ACPI_TAD_AC_TIMER (u32)0 4695c513ecSRafael J. Wysocki #define ACPI_TAD_DC_TIMER (u32)1 4795c513ecSRafael J. Wysocki 4895c513ecSRafael J. Wysocki /* Special value for disabled timer or expired timer wake policy. */ 4995c513ecSRafael J. Wysocki #define ACPI_TAD_WAKE_DISABLED (~(u32)0) 5095c513ecSRafael J. Wysocki 5195c513ecSRafael J. Wysocki struct acpi_tad_driver_data { 5295c513ecSRafael J. Wysocki u32 capabilities; 5395c513ecSRafael J. Wysocki }; 5495c513ecSRafael J. Wysocki 553230b2b3SRafael J. Wysocki struct acpi_tad_rt { 563230b2b3SRafael J. Wysocki u16 year; /* 1900 - 9999 */ 573230b2b3SRafael J. Wysocki u8 month; /* 1 - 12 */ 583230b2b3SRafael J. Wysocki u8 day; /* 1 - 31 */ 593230b2b3SRafael J. Wysocki u8 hour; /* 0 - 23 */ 603230b2b3SRafael J. Wysocki u8 minute; /* 0 - 59 */ 613230b2b3SRafael J. Wysocki u8 second; /* 0 - 59 */ 623230b2b3SRafael J. Wysocki u8 valid; /* 0 (failed) or 1 (success) for reads, 0 for writes */ 633230b2b3SRafael J. Wysocki u16 msec; /* 1 - 1000 */ 643230b2b3SRafael J. Wysocki s16 tz; /* -1440 to 1440 or 2047 (unspecified) */ 653230b2b3SRafael J. Wysocki u8 daylight; 663230b2b3SRafael J. Wysocki u8 padding[3]; /* must be 0 */ 673230b2b3SRafael J. Wysocki } __packed; 683230b2b3SRafael J. Wysocki 693230b2b3SRafael J. Wysocki static int acpi_tad_set_real_time(struct device *dev, struct acpi_tad_rt *rt) 703230b2b3SRafael J. Wysocki { 713230b2b3SRafael J. Wysocki acpi_handle handle = ACPI_HANDLE(dev); 723230b2b3SRafael J. Wysocki union acpi_object args[] = { 733230b2b3SRafael J. Wysocki { .type = ACPI_TYPE_BUFFER, }, 743230b2b3SRafael J. Wysocki }; 753230b2b3SRafael J. Wysocki struct acpi_object_list arg_list = { 763230b2b3SRafael J. Wysocki .pointer = args, 773230b2b3SRafael J. Wysocki .count = ARRAY_SIZE(args), 783230b2b3SRafael J. Wysocki }; 793230b2b3SRafael J. Wysocki unsigned long long retval; 803230b2b3SRafael J. Wysocki acpi_status status; 813230b2b3SRafael J. Wysocki 823230b2b3SRafael J. Wysocki if (rt->year < 1900 || rt->year > 9999 || 833230b2b3SRafael J. Wysocki rt->month < 1 || rt->month > 12 || 843230b2b3SRafael J. Wysocki rt->hour > 23 || rt->minute > 59 || rt->second > 59 || 853230b2b3SRafael J. Wysocki rt->tz < -1440 || (rt->tz > 1440 && rt->tz != 2047) || 863230b2b3SRafael J. Wysocki rt->daylight > 3) 873230b2b3SRafael J. Wysocki return -ERANGE; 883230b2b3SRafael J. Wysocki 893230b2b3SRafael J. Wysocki args[0].buffer.pointer = (u8 *)rt; 903230b2b3SRafael J. Wysocki args[0].buffer.length = sizeof(*rt); 913230b2b3SRafael J. Wysocki 923230b2b3SRafael J. Wysocki pm_runtime_get_sync(dev); 933230b2b3SRafael J. Wysocki 943230b2b3SRafael J. Wysocki status = acpi_evaluate_integer(handle, "_SRT", &arg_list, &retval); 953230b2b3SRafael J. Wysocki 963230b2b3SRafael J. Wysocki pm_runtime_put_sync(dev); 973230b2b3SRafael J. Wysocki 983230b2b3SRafael J. Wysocki if (ACPI_FAILURE(status) || retval) 993230b2b3SRafael J. Wysocki return -EIO; 1003230b2b3SRafael J. Wysocki 1013230b2b3SRafael J. Wysocki return 0; 1023230b2b3SRafael J. Wysocki } 1033230b2b3SRafael J. Wysocki 1043230b2b3SRafael J. Wysocki static int acpi_tad_get_real_time(struct device *dev, struct acpi_tad_rt *rt) 1053230b2b3SRafael J. Wysocki { 1063230b2b3SRafael J. Wysocki acpi_handle handle = ACPI_HANDLE(dev); 1073230b2b3SRafael J. Wysocki struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER }; 1083230b2b3SRafael J. Wysocki union acpi_object *out_obj; 1093230b2b3SRafael J. Wysocki struct acpi_tad_rt *data; 1103230b2b3SRafael J. Wysocki acpi_status status; 1113230b2b3SRafael J. Wysocki int ret = -EIO; 1123230b2b3SRafael J. Wysocki 1133230b2b3SRafael J. Wysocki pm_runtime_get_sync(dev); 1143230b2b3SRafael J. Wysocki 1153230b2b3SRafael J. Wysocki status = acpi_evaluate_object(handle, "_GRT", NULL, &output); 1163230b2b3SRafael J. Wysocki 1173230b2b3SRafael J. Wysocki pm_runtime_put_sync(dev); 1183230b2b3SRafael J. Wysocki 1193230b2b3SRafael J. Wysocki if (ACPI_FAILURE(status)) 1203230b2b3SRafael J. Wysocki goto out_free; 1213230b2b3SRafael J. Wysocki 1223230b2b3SRafael J. Wysocki out_obj = output.pointer; 1233230b2b3SRafael J. Wysocki if (out_obj->type != ACPI_TYPE_BUFFER) 1243230b2b3SRafael J. Wysocki goto out_free; 1253230b2b3SRafael J. Wysocki 1263230b2b3SRafael J. Wysocki if (out_obj->buffer.length != sizeof(*rt)) 1273230b2b3SRafael J. Wysocki goto out_free; 1283230b2b3SRafael J. Wysocki 1293230b2b3SRafael J. Wysocki data = (struct acpi_tad_rt *)(out_obj->buffer.pointer); 1303230b2b3SRafael J. Wysocki if (!data->valid) 1313230b2b3SRafael J. Wysocki goto out_free; 1323230b2b3SRafael J. Wysocki 1333230b2b3SRafael J. Wysocki memcpy(rt, data, sizeof(*rt)); 1343230b2b3SRafael J. Wysocki ret = 0; 1353230b2b3SRafael J. Wysocki 1363230b2b3SRafael J. Wysocki out_free: 1373230b2b3SRafael J. Wysocki ACPI_FREE(output.pointer); 1383230b2b3SRafael J. Wysocki return ret; 1393230b2b3SRafael J. Wysocki } 1403230b2b3SRafael J. Wysocki 1413230b2b3SRafael J. Wysocki static char *acpi_tad_rt_next_field(char *s, int *val) 1423230b2b3SRafael J. Wysocki { 1433230b2b3SRafael J. Wysocki char *p; 1443230b2b3SRafael J. Wysocki 1453230b2b3SRafael J. Wysocki p = strchr(s, ':'); 1463230b2b3SRafael J. Wysocki if (!p) 1473230b2b3SRafael J. Wysocki return NULL; 1483230b2b3SRafael J. Wysocki 1493230b2b3SRafael J. Wysocki *p = '\0'; 1503230b2b3SRafael J. Wysocki if (kstrtoint(s, 10, val)) 1513230b2b3SRafael J. Wysocki return NULL; 1523230b2b3SRafael J. Wysocki 1533230b2b3SRafael J. Wysocki return p + 1; 1543230b2b3SRafael J. Wysocki } 1553230b2b3SRafael J. Wysocki 1563230b2b3SRafael J. Wysocki static ssize_t time_store(struct device *dev, struct device_attribute *attr, 1573230b2b3SRafael J. Wysocki const char *buf, size_t count) 1583230b2b3SRafael J. Wysocki { 1593230b2b3SRafael J. Wysocki struct acpi_tad_rt rt; 1603230b2b3SRafael J. Wysocki char *str, *s; 1613230b2b3SRafael J. Wysocki int val, ret = -ENODATA; 1623230b2b3SRafael J. Wysocki 1633230b2b3SRafael J. Wysocki str = kmemdup_nul(buf, count, GFP_KERNEL); 1643230b2b3SRafael J. Wysocki if (!str) 1653230b2b3SRafael J. Wysocki return -ENOMEM; 1663230b2b3SRafael J. Wysocki 1673230b2b3SRafael J. Wysocki s = acpi_tad_rt_next_field(str, &val); 1683230b2b3SRafael J. Wysocki if (!s) 1693230b2b3SRafael J. Wysocki goto out_free; 1703230b2b3SRafael J. Wysocki 1713230b2b3SRafael J. Wysocki rt.year = val; 1723230b2b3SRafael J. Wysocki 1733230b2b3SRafael J. Wysocki s = acpi_tad_rt_next_field(s, &val); 1743230b2b3SRafael J. Wysocki if (!s) 1753230b2b3SRafael J. Wysocki goto out_free; 1763230b2b3SRafael J. Wysocki 1773230b2b3SRafael J. Wysocki rt.month = val; 1783230b2b3SRafael J. Wysocki 1793230b2b3SRafael J. Wysocki s = acpi_tad_rt_next_field(s, &val); 1803230b2b3SRafael J. Wysocki if (!s) 1813230b2b3SRafael J. Wysocki goto out_free; 1823230b2b3SRafael J. Wysocki 1833230b2b3SRafael J. Wysocki rt.day = val; 1843230b2b3SRafael J. Wysocki 1853230b2b3SRafael J. Wysocki s = acpi_tad_rt_next_field(s, &val); 1863230b2b3SRafael J. Wysocki if (!s) 1873230b2b3SRafael J. Wysocki goto out_free; 1883230b2b3SRafael J. Wysocki 1893230b2b3SRafael J. Wysocki rt.hour = val; 1903230b2b3SRafael J. Wysocki 1913230b2b3SRafael J. Wysocki s = acpi_tad_rt_next_field(s, &val); 1923230b2b3SRafael J. Wysocki if (!s) 1933230b2b3SRafael J. Wysocki goto out_free; 1943230b2b3SRafael J. Wysocki 1953230b2b3SRafael J. Wysocki rt.minute = val; 1963230b2b3SRafael J. Wysocki 1973230b2b3SRafael J. Wysocki s = acpi_tad_rt_next_field(s, &val); 1983230b2b3SRafael J. Wysocki if (!s) 1993230b2b3SRafael J. Wysocki goto out_free; 2003230b2b3SRafael J. Wysocki 2013230b2b3SRafael J. Wysocki rt.second = val; 2023230b2b3SRafael J. Wysocki 2033230b2b3SRafael J. Wysocki s = acpi_tad_rt_next_field(s, &val); 2043230b2b3SRafael J. Wysocki if (!s) 2053230b2b3SRafael J. Wysocki goto out_free; 2063230b2b3SRafael J. Wysocki 2073230b2b3SRafael J. Wysocki rt.tz = val; 2083230b2b3SRafael J. Wysocki 2093230b2b3SRafael J. Wysocki if (kstrtoint(s, 10, &val)) 2103230b2b3SRafael J. Wysocki goto out_free; 2113230b2b3SRafael J. Wysocki 2123230b2b3SRafael J. Wysocki rt.daylight = val; 2133230b2b3SRafael J. Wysocki 2143230b2b3SRafael J. Wysocki rt.valid = 0; 2153230b2b3SRafael J. Wysocki rt.msec = 0; 2163230b2b3SRafael J. Wysocki memset(rt.padding, 0, 3); 2173230b2b3SRafael J. Wysocki 2183230b2b3SRafael J. Wysocki ret = acpi_tad_set_real_time(dev, &rt); 2193230b2b3SRafael J. Wysocki 2203230b2b3SRafael J. Wysocki out_free: 2213230b2b3SRafael J. Wysocki kfree(str); 2223230b2b3SRafael J. Wysocki return ret ? ret : count; 2233230b2b3SRafael J. Wysocki } 2243230b2b3SRafael J. Wysocki 2253230b2b3SRafael J. Wysocki static ssize_t time_show(struct device *dev, struct device_attribute *attr, 2263230b2b3SRafael J. Wysocki char *buf) 2273230b2b3SRafael J. Wysocki { 2283230b2b3SRafael J. Wysocki struct acpi_tad_rt rt; 2293230b2b3SRafael J. Wysocki int ret; 2303230b2b3SRafael J. Wysocki 2313230b2b3SRafael J. Wysocki ret = acpi_tad_get_real_time(dev, &rt); 2323230b2b3SRafael J. Wysocki if (ret) 2333230b2b3SRafael J. Wysocki return ret; 2343230b2b3SRafael J. Wysocki 2353230b2b3SRafael J. Wysocki return sprintf(buf, "%u:%u:%u:%u:%u:%u:%d:%u\n", 2363230b2b3SRafael J. Wysocki rt.year, rt.month, rt.day, rt.hour, rt.minute, rt.second, 2373230b2b3SRafael J. Wysocki rt.tz, rt.daylight); 2383230b2b3SRafael J. Wysocki } 2393230b2b3SRafael J. Wysocki 2400f39ee83SDwaipayan Ray static DEVICE_ATTR_RW(time); 2413230b2b3SRafael J. Wysocki 2423230b2b3SRafael J. Wysocki static struct attribute *acpi_tad_time_attrs[] = { 2433230b2b3SRafael J. Wysocki &dev_attr_time.attr, 2443230b2b3SRafael J. Wysocki NULL, 2453230b2b3SRafael J. Wysocki }; 2463230b2b3SRafael J. Wysocki static const struct attribute_group acpi_tad_time_attr_group = { 2473230b2b3SRafael J. Wysocki .attrs = acpi_tad_time_attrs, 2483230b2b3SRafael J. Wysocki }; 2493230b2b3SRafael J. Wysocki 25095c513ecSRafael J. Wysocki static int acpi_tad_wake_set(struct device *dev, char *method, u32 timer_id, 25195c513ecSRafael J. Wysocki u32 value) 25295c513ecSRafael J. Wysocki { 25395c513ecSRafael J. Wysocki acpi_handle handle = ACPI_HANDLE(dev); 25495c513ecSRafael J. Wysocki union acpi_object args[] = { 25595c513ecSRafael J. Wysocki { .type = ACPI_TYPE_INTEGER, }, 25695c513ecSRafael J. Wysocki { .type = ACPI_TYPE_INTEGER, }, 25795c513ecSRafael J. Wysocki }; 25895c513ecSRafael J. Wysocki struct acpi_object_list arg_list = { 25995c513ecSRafael J. Wysocki .pointer = args, 26095c513ecSRafael J. Wysocki .count = ARRAY_SIZE(args), 26195c513ecSRafael J. Wysocki }; 26295c513ecSRafael J. Wysocki unsigned long long retval; 26395c513ecSRafael J. Wysocki acpi_status status; 26495c513ecSRafael J. Wysocki 26595c513ecSRafael J. Wysocki args[0].integer.value = timer_id; 26695c513ecSRafael J. Wysocki args[1].integer.value = value; 26795c513ecSRafael J. Wysocki 26895c513ecSRafael J. Wysocki pm_runtime_get_sync(dev); 26995c513ecSRafael J. Wysocki 27095c513ecSRafael J. Wysocki status = acpi_evaluate_integer(handle, method, &arg_list, &retval); 27195c513ecSRafael J. Wysocki 27295c513ecSRafael J. Wysocki pm_runtime_put_sync(dev); 27395c513ecSRafael J. Wysocki 27495c513ecSRafael J. Wysocki if (ACPI_FAILURE(status) || retval) 27595c513ecSRafael J. Wysocki return -EIO; 27695c513ecSRafael J. Wysocki 27795c513ecSRafael J. Wysocki return 0; 27895c513ecSRafael J. Wysocki } 27995c513ecSRafael J. Wysocki 28095c513ecSRafael J. Wysocki static int acpi_tad_wake_write(struct device *dev, const char *buf, char *method, 28195c513ecSRafael J. Wysocki u32 timer_id, const char *specval) 28295c513ecSRafael J. Wysocki { 28395c513ecSRafael J. Wysocki u32 value; 28495c513ecSRafael J. Wysocki 28595c513ecSRafael J. Wysocki if (sysfs_streq(buf, specval)) { 28695c513ecSRafael J. Wysocki value = ACPI_TAD_WAKE_DISABLED; 28795c513ecSRafael J. Wysocki } else { 28895c513ecSRafael J. Wysocki int ret = kstrtou32(buf, 0, &value); 28995c513ecSRafael J. Wysocki 29095c513ecSRafael J. Wysocki if (ret) 29195c513ecSRafael J. Wysocki return ret; 29295c513ecSRafael J. Wysocki 29395c513ecSRafael J. Wysocki if (value == ACPI_TAD_WAKE_DISABLED) 29495c513ecSRafael J. Wysocki return -EINVAL; 29595c513ecSRafael J. Wysocki } 29695c513ecSRafael J. Wysocki 29795c513ecSRafael J. Wysocki return acpi_tad_wake_set(dev, method, timer_id, value); 29895c513ecSRafael J. Wysocki } 29995c513ecSRafael J. Wysocki 30095c513ecSRafael J. Wysocki static ssize_t acpi_tad_wake_read(struct device *dev, char *buf, char *method, 30195c513ecSRafael J. Wysocki u32 timer_id, const char *specval) 30295c513ecSRafael J. Wysocki { 30395c513ecSRafael J. Wysocki acpi_handle handle = ACPI_HANDLE(dev); 30495c513ecSRafael J. Wysocki union acpi_object args[] = { 30595c513ecSRafael J. Wysocki { .type = ACPI_TYPE_INTEGER, }, 30695c513ecSRafael J. Wysocki }; 30795c513ecSRafael J. Wysocki struct acpi_object_list arg_list = { 30895c513ecSRafael J. Wysocki .pointer = args, 30995c513ecSRafael J. Wysocki .count = ARRAY_SIZE(args), 31095c513ecSRafael J. Wysocki }; 31195c513ecSRafael J. Wysocki unsigned long long retval; 31295c513ecSRafael J. Wysocki acpi_status status; 31395c513ecSRafael J. Wysocki 31495c513ecSRafael J. Wysocki args[0].integer.value = timer_id; 31595c513ecSRafael J. Wysocki 31695c513ecSRafael J. Wysocki pm_runtime_get_sync(dev); 31795c513ecSRafael J. Wysocki 31895c513ecSRafael J. Wysocki status = acpi_evaluate_integer(handle, method, &arg_list, &retval); 31995c513ecSRafael J. Wysocki 32095c513ecSRafael J. Wysocki pm_runtime_put_sync(dev); 32195c513ecSRafael J. Wysocki 32295c513ecSRafael J. Wysocki if (ACPI_FAILURE(status)) 32395c513ecSRafael J. Wysocki return -EIO; 32495c513ecSRafael J. Wysocki 32595c513ecSRafael J. Wysocki if ((u32)retval == ACPI_TAD_WAKE_DISABLED) 32695c513ecSRafael J. Wysocki return sprintf(buf, "%s\n", specval); 32795c513ecSRafael J. Wysocki 32895c513ecSRafael J. Wysocki return sprintf(buf, "%u\n", (u32)retval); 32995c513ecSRafael J. Wysocki } 33095c513ecSRafael J. Wysocki 33195c513ecSRafael J. Wysocki static const char *alarm_specval = "disabled"; 33295c513ecSRafael J. Wysocki 33395c513ecSRafael J. Wysocki static int acpi_tad_alarm_write(struct device *dev, const char *buf, 33495c513ecSRafael J. Wysocki u32 timer_id) 33595c513ecSRafael J. Wysocki { 33695c513ecSRafael J. Wysocki return acpi_tad_wake_write(dev, buf, "_STV", timer_id, alarm_specval); 33795c513ecSRafael J. Wysocki } 33895c513ecSRafael J. Wysocki 33995c513ecSRafael J. Wysocki static ssize_t acpi_tad_alarm_read(struct device *dev, char *buf, u32 timer_id) 34095c513ecSRafael J. Wysocki { 34195c513ecSRafael J. Wysocki return acpi_tad_wake_read(dev, buf, "_TIV", timer_id, alarm_specval); 34295c513ecSRafael J. Wysocki } 34395c513ecSRafael J. Wysocki 34495c513ecSRafael J. Wysocki static const char *policy_specval = "never"; 34595c513ecSRafael J. Wysocki 34695c513ecSRafael J. Wysocki static int acpi_tad_policy_write(struct device *dev, const char *buf, 34795c513ecSRafael J. Wysocki u32 timer_id) 34895c513ecSRafael J. Wysocki { 34995c513ecSRafael J. Wysocki return acpi_tad_wake_write(dev, buf, "_STP", timer_id, policy_specval); 35095c513ecSRafael J. Wysocki } 35195c513ecSRafael J. Wysocki 35295c513ecSRafael J. Wysocki static ssize_t acpi_tad_policy_read(struct device *dev, char *buf, u32 timer_id) 35395c513ecSRafael J. Wysocki { 35495c513ecSRafael J. Wysocki return acpi_tad_wake_read(dev, buf, "_TIP", timer_id, policy_specval); 35595c513ecSRafael J. Wysocki } 35695c513ecSRafael J. Wysocki 35795c513ecSRafael J. Wysocki static int acpi_tad_clear_status(struct device *dev, u32 timer_id) 35895c513ecSRafael J. Wysocki { 35995c513ecSRafael J. Wysocki acpi_handle handle = ACPI_HANDLE(dev); 36095c513ecSRafael J. Wysocki union acpi_object args[] = { 36195c513ecSRafael J. Wysocki { .type = ACPI_TYPE_INTEGER, }, 36295c513ecSRafael J. Wysocki }; 36395c513ecSRafael J. Wysocki struct acpi_object_list arg_list = { 36495c513ecSRafael J. Wysocki .pointer = args, 36595c513ecSRafael J. Wysocki .count = ARRAY_SIZE(args), 36695c513ecSRafael J. Wysocki }; 36795c513ecSRafael J. Wysocki unsigned long long retval; 36895c513ecSRafael J. Wysocki acpi_status status; 36995c513ecSRafael J. Wysocki 37095c513ecSRafael J. Wysocki args[0].integer.value = timer_id; 37195c513ecSRafael J. Wysocki 37295c513ecSRafael J. Wysocki pm_runtime_get_sync(dev); 37395c513ecSRafael J. Wysocki 37495c513ecSRafael J. Wysocki status = acpi_evaluate_integer(handle, "_CWS", &arg_list, &retval); 37595c513ecSRafael J. Wysocki 37695c513ecSRafael J. Wysocki pm_runtime_put_sync(dev); 37795c513ecSRafael J. Wysocki 37895c513ecSRafael J. Wysocki if (ACPI_FAILURE(status) || retval) 37995c513ecSRafael J. Wysocki return -EIO; 38095c513ecSRafael J. Wysocki 38195c513ecSRafael J. Wysocki return 0; 38295c513ecSRafael J. Wysocki } 38395c513ecSRafael J. Wysocki 38495c513ecSRafael J. Wysocki static int acpi_tad_status_write(struct device *dev, const char *buf, u32 timer_id) 38595c513ecSRafael J. Wysocki { 38695c513ecSRafael J. Wysocki int ret, value; 38795c513ecSRafael J. Wysocki 38895c513ecSRafael J. Wysocki ret = kstrtoint(buf, 0, &value); 38995c513ecSRafael J. Wysocki if (ret) 39095c513ecSRafael J. Wysocki return ret; 39195c513ecSRafael J. Wysocki 39295c513ecSRafael J. Wysocki if (value) 39395c513ecSRafael J. Wysocki return -EINVAL; 39495c513ecSRafael J. Wysocki 39595c513ecSRafael J. Wysocki return acpi_tad_clear_status(dev, timer_id); 39695c513ecSRafael J. Wysocki } 39795c513ecSRafael J. Wysocki 39895c513ecSRafael J. Wysocki static ssize_t acpi_tad_status_read(struct device *dev, char *buf, u32 timer_id) 39995c513ecSRafael J. Wysocki { 40095c513ecSRafael J. Wysocki acpi_handle handle = ACPI_HANDLE(dev); 40195c513ecSRafael J. Wysocki union acpi_object args[] = { 40295c513ecSRafael J. Wysocki { .type = ACPI_TYPE_INTEGER, }, 40395c513ecSRafael J. Wysocki }; 40495c513ecSRafael J. Wysocki struct acpi_object_list arg_list = { 40595c513ecSRafael J. Wysocki .pointer = args, 40695c513ecSRafael J. Wysocki .count = ARRAY_SIZE(args), 40795c513ecSRafael J. Wysocki }; 40895c513ecSRafael J. Wysocki unsigned long long retval; 40995c513ecSRafael J. Wysocki acpi_status status; 41095c513ecSRafael J. Wysocki 41195c513ecSRafael J. Wysocki args[0].integer.value = timer_id; 41295c513ecSRafael J. Wysocki 41395c513ecSRafael J. Wysocki pm_runtime_get_sync(dev); 41495c513ecSRafael J. Wysocki 41595c513ecSRafael J. Wysocki status = acpi_evaluate_integer(handle, "_GWS", &arg_list, &retval); 41695c513ecSRafael J. Wysocki 41795c513ecSRafael J. Wysocki pm_runtime_put_sync(dev); 41895c513ecSRafael J. Wysocki 41995c513ecSRafael J. Wysocki if (ACPI_FAILURE(status)) 42095c513ecSRafael J. Wysocki return -EIO; 42195c513ecSRafael J. Wysocki 42295c513ecSRafael J. Wysocki return sprintf(buf, "0x%02X\n", (u32)retval); 42395c513ecSRafael J. Wysocki } 42495c513ecSRafael J. Wysocki 42595c513ecSRafael J. Wysocki static ssize_t caps_show(struct device *dev, struct device_attribute *attr, 42695c513ecSRafael J. Wysocki char *buf) 42795c513ecSRafael J. Wysocki { 42895c513ecSRafael J. Wysocki struct acpi_tad_driver_data *dd = dev_get_drvdata(dev); 42995c513ecSRafael J. Wysocki 43095c513ecSRafael J. Wysocki return sprintf(buf, "0x%02X\n", dd->capabilities); 43195c513ecSRafael J. Wysocki } 43295c513ecSRafael J. Wysocki 43395c513ecSRafael J. Wysocki static DEVICE_ATTR_RO(caps); 43495c513ecSRafael J. Wysocki 43595c513ecSRafael J. Wysocki static ssize_t ac_alarm_store(struct device *dev, struct device_attribute *attr, 43695c513ecSRafael J. Wysocki const char *buf, size_t count) 43795c513ecSRafael J. Wysocki { 43895c513ecSRafael J. Wysocki int ret = acpi_tad_alarm_write(dev, buf, ACPI_TAD_AC_TIMER); 43995c513ecSRafael J. Wysocki 44095c513ecSRafael J. Wysocki return ret ? ret : count; 44195c513ecSRafael J. Wysocki } 44295c513ecSRafael J. Wysocki 44395c513ecSRafael J. Wysocki static ssize_t ac_alarm_show(struct device *dev, struct device_attribute *attr, 44495c513ecSRafael J. Wysocki char *buf) 44595c513ecSRafael J. Wysocki { 44695c513ecSRafael J. Wysocki return acpi_tad_alarm_read(dev, buf, ACPI_TAD_AC_TIMER); 44795c513ecSRafael J. Wysocki } 44895c513ecSRafael J. Wysocki 4490f39ee83SDwaipayan Ray static DEVICE_ATTR_RW(ac_alarm); 45095c513ecSRafael J. Wysocki 45195c513ecSRafael J. Wysocki static ssize_t ac_policy_store(struct device *dev, struct device_attribute *attr, 45295c513ecSRafael J. Wysocki const char *buf, size_t count) 45395c513ecSRafael J. Wysocki { 45495c513ecSRafael J. Wysocki int ret = acpi_tad_policy_write(dev, buf, ACPI_TAD_AC_TIMER); 45595c513ecSRafael J. Wysocki 45695c513ecSRafael J. Wysocki return ret ? ret : count; 45795c513ecSRafael J. Wysocki } 45895c513ecSRafael J. Wysocki 45995c513ecSRafael J. Wysocki static ssize_t ac_policy_show(struct device *dev, struct device_attribute *attr, 46095c513ecSRafael J. Wysocki char *buf) 46195c513ecSRafael J. Wysocki { 46295c513ecSRafael J. Wysocki return acpi_tad_policy_read(dev, buf, ACPI_TAD_AC_TIMER); 46395c513ecSRafael J. Wysocki } 46495c513ecSRafael J. Wysocki 4650f39ee83SDwaipayan Ray static DEVICE_ATTR_RW(ac_policy); 46695c513ecSRafael J. Wysocki 46795c513ecSRafael J. Wysocki static ssize_t ac_status_store(struct device *dev, struct device_attribute *attr, 46895c513ecSRafael J. Wysocki const char *buf, size_t count) 46995c513ecSRafael J. Wysocki { 47095c513ecSRafael J. Wysocki int ret = acpi_tad_status_write(dev, buf, ACPI_TAD_AC_TIMER); 47195c513ecSRafael J. Wysocki 47295c513ecSRafael J. Wysocki return ret ? ret : count; 47395c513ecSRafael J. Wysocki } 47495c513ecSRafael J. Wysocki 47595c513ecSRafael J. Wysocki static ssize_t ac_status_show(struct device *dev, struct device_attribute *attr, 47695c513ecSRafael J. Wysocki char *buf) 47795c513ecSRafael J. Wysocki { 47895c513ecSRafael J. Wysocki return acpi_tad_status_read(dev, buf, ACPI_TAD_AC_TIMER); 47995c513ecSRafael J. Wysocki } 48095c513ecSRafael J. Wysocki 4810f39ee83SDwaipayan Ray static DEVICE_ATTR_RW(ac_status); 48295c513ecSRafael J. Wysocki 48395c513ecSRafael J. Wysocki static struct attribute *acpi_tad_attrs[] = { 48495c513ecSRafael J. Wysocki &dev_attr_caps.attr, 48595c513ecSRafael J. Wysocki &dev_attr_ac_alarm.attr, 48695c513ecSRafael J. Wysocki &dev_attr_ac_policy.attr, 48795c513ecSRafael J. Wysocki &dev_attr_ac_status.attr, 48895c513ecSRafael J. Wysocki NULL, 48995c513ecSRafael J. Wysocki }; 49095c513ecSRafael J. Wysocki static const struct attribute_group acpi_tad_attr_group = { 49195c513ecSRafael J. Wysocki .attrs = acpi_tad_attrs, 49295c513ecSRafael J. Wysocki }; 49395c513ecSRafael J. Wysocki 49495c513ecSRafael J. Wysocki static ssize_t dc_alarm_store(struct device *dev, struct device_attribute *attr, 49595c513ecSRafael J. Wysocki const char *buf, size_t count) 49695c513ecSRafael J. Wysocki { 49795c513ecSRafael J. Wysocki int ret = acpi_tad_alarm_write(dev, buf, ACPI_TAD_DC_TIMER); 49895c513ecSRafael J. Wysocki 49995c513ecSRafael J. Wysocki return ret ? ret : count; 50095c513ecSRafael J. Wysocki } 50195c513ecSRafael J. Wysocki 50295c513ecSRafael J. Wysocki static ssize_t dc_alarm_show(struct device *dev, struct device_attribute *attr, 50395c513ecSRafael J. Wysocki char *buf) 50495c513ecSRafael J. Wysocki { 50595c513ecSRafael J. Wysocki return acpi_tad_alarm_read(dev, buf, ACPI_TAD_DC_TIMER); 50695c513ecSRafael J. Wysocki } 50795c513ecSRafael J. Wysocki 5080f39ee83SDwaipayan Ray static DEVICE_ATTR_RW(dc_alarm); 50995c513ecSRafael J. Wysocki 51095c513ecSRafael J. Wysocki static ssize_t dc_policy_store(struct device *dev, struct device_attribute *attr, 51195c513ecSRafael J. Wysocki const char *buf, size_t count) 51295c513ecSRafael J. Wysocki { 51395c513ecSRafael J. Wysocki int ret = acpi_tad_policy_write(dev, buf, ACPI_TAD_DC_TIMER); 51495c513ecSRafael J. Wysocki 51595c513ecSRafael J. Wysocki return ret ? ret : count; 51695c513ecSRafael J. Wysocki } 51795c513ecSRafael J. Wysocki 51895c513ecSRafael J. Wysocki static ssize_t dc_policy_show(struct device *dev, struct device_attribute *attr, 51995c513ecSRafael J. Wysocki char *buf) 52095c513ecSRafael J. Wysocki { 52195c513ecSRafael J. Wysocki return acpi_tad_policy_read(dev, buf, ACPI_TAD_DC_TIMER); 52295c513ecSRafael J. Wysocki } 52395c513ecSRafael J. Wysocki 5240f39ee83SDwaipayan Ray static DEVICE_ATTR_RW(dc_policy); 52595c513ecSRafael J. Wysocki 52695c513ecSRafael J. Wysocki static ssize_t dc_status_store(struct device *dev, struct device_attribute *attr, 52795c513ecSRafael J. Wysocki const char *buf, size_t count) 52895c513ecSRafael J. Wysocki { 52995c513ecSRafael J. Wysocki int ret = acpi_tad_status_write(dev, buf, ACPI_TAD_DC_TIMER); 53095c513ecSRafael J. Wysocki 53195c513ecSRafael J. Wysocki return ret ? ret : count; 53295c513ecSRafael J. Wysocki } 53395c513ecSRafael J. Wysocki 53495c513ecSRafael J. Wysocki static ssize_t dc_status_show(struct device *dev, struct device_attribute *attr, 53595c513ecSRafael J. Wysocki char *buf) 53695c513ecSRafael J. Wysocki { 53795c513ecSRafael J. Wysocki return acpi_tad_status_read(dev, buf, ACPI_TAD_DC_TIMER); 53895c513ecSRafael J. Wysocki } 53995c513ecSRafael J. Wysocki 5400f39ee83SDwaipayan Ray static DEVICE_ATTR_RW(dc_status); 54195c513ecSRafael J. Wysocki 54295c513ecSRafael J. Wysocki static struct attribute *acpi_tad_dc_attrs[] = { 54395c513ecSRafael J. Wysocki &dev_attr_dc_alarm.attr, 54495c513ecSRafael J. Wysocki &dev_attr_dc_policy.attr, 54595c513ecSRafael J. Wysocki &dev_attr_dc_status.attr, 54695c513ecSRafael J. Wysocki NULL, 54795c513ecSRafael J. Wysocki }; 54895c513ecSRafael J. Wysocki static const struct attribute_group acpi_tad_dc_attr_group = { 54995c513ecSRafael J. Wysocki .attrs = acpi_tad_dc_attrs, 55095c513ecSRafael J. Wysocki }; 55195c513ecSRafael J. Wysocki 55295c513ecSRafael J. Wysocki static int acpi_tad_disable_timer(struct device *dev, u32 timer_id) 55395c513ecSRafael J. Wysocki { 55495c513ecSRafael J. Wysocki return acpi_tad_wake_set(dev, "_STV", timer_id, ACPI_TAD_WAKE_DISABLED); 55595c513ecSRafael J. Wysocki } 55695c513ecSRafael J. Wysocki 557*9a7897a2SUwe Kleine-König static void acpi_tad_remove(struct platform_device *pdev) 55895c513ecSRafael J. Wysocki { 55995c513ecSRafael J. Wysocki struct device *dev = &pdev->dev; 560596ca52aSZhang Rui acpi_handle handle = ACPI_HANDLE(dev); 56195c513ecSRafael J. Wysocki struct acpi_tad_driver_data *dd = dev_get_drvdata(dev); 56295c513ecSRafael J. Wysocki 56395c513ecSRafael J. Wysocki device_init_wakeup(dev, false); 56495c513ecSRafael J. Wysocki 56595c513ecSRafael J. Wysocki pm_runtime_get_sync(dev); 56695c513ecSRafael J. Wysocki 56795c513ecSRafael J. Wysocki if (dd->capabilities & ACPI_TAD_DC_WAKE) 56895c513ecSRafael J. Wysocki sysfs_remove_group(&dev->kobj, &acpi_tad_dc_attr_group); 56995c513ecSRafael J. Wysocki 57095c513ecSRafael J. Wysocki sysfs_remove_group(&dev->kobj, &acpi_tad_attr_group); 57195c513ecSRafael J. Wysocki 57295c513ecSRafael J. Wysocki acpi_tad_disable_timer(dev, ACPI_TAD_AC_TIMER); 57395c513ecSRafael J. Wysocki acpi_tad_clear_status(dev, ACPI_TAD_AC_TIMER); 57495c513ecSRafael J. Wysocki if (dd->capabilities & ACPI_TAD_DC_WAKE) { 57595c513ecSRafael J. Wysocki acpi_tad_disable_timer(dev, ACPI_TAD_DC_TIMER); 57695c513ecSRafael J. Wysocki acpi_tad_clear_status(dev, ACPI_TAD_DC_TIMER); 57795c513ecSRafael J. Wysocki } 57895c513ecSRafael J. Wysocki 57995c513ecSRafael J. Wysocki pm_runtime_put_sync(dev); 58095c513ecSRafael J. Wysocki pm_runtime_disable(dev); 581596ca52aSZhang Rui acpi_remove_cmos_rtc_space_handler(handle); 58295c513ecSRafael J. Wysocki } 58395c513ecSRafael J. Wysocki 58495c513ecSRafael J. Wysocki static int acpi_tad_probe(struct platform_device *pdev) 58595c513ecSRafael J. Wysocki { 58695c513ecSRafael J. Wysocki struct device *dev = &pdev->dev; 58795c513ecSRafael J. Wysocki acpi_handle handle = ACPI_HANDLE(dev); 58895c513ecSRafael J. Wysocki struct acpi_tad_driver_data *dd; 58995c513ecSRafael J. Wysocki acpi_status status; 59095c513ecSRafael J. Wysocki unsigned long long caps; 59195c513ecSRafael J. Wysocki int ret; 59295c513ecSRafael J. Wysocki 593596ca52aSZhang Rui ret = acpi_install_cmos_rtc_space_handler(handle); 594596ca52aSZhang Rui if (ret < 0) { 595596ca52aSZhang Rui dev_info(dev, "Unable to install space handler\n"); 596596ca52aSZhang Rui return -ENODEV; 597596ca52aSZhang Rui } 59895c513ecSRafael J. Wysocki /* 59995c513ecSRafael J. Wysocki * Initialization failure messages are mostly about firmware issues, so 60095c513ecSRafael J. Wysocki * print them at the "info" level. 60195c513ecSRafael J. Wysocki */ 60295c513ecSRafael J. Wysocki status = acpi_evaluate_integer(handle, "_GCP", NULL, &caps); 60395c513ecSRafael J. Wysocki if (ACPI_FAILURE(status)) { 60495c513ecSRafael J. Wysocki dev_info(dev, "Unable to get capabilities\n"); 605596ca52aSZhang Rui ret = -ENODEV; 606596ca52aSZhang Rui goto remove_handler; 60795c513ecSRafael J. Wysocki } 60895c513ecSRafael J. Wysocki 60995c513ecSRafael J. Wysocki if (!(caps & ACPI_TAD_AC_WAKE)) { 61095c513ecSRafael J. Wysocki dev_info(dev, "Unsupported capabilities\n"); 611596ca52aSZhang Rui ret = -ENODEV; 612596ca52aSZhang Rui goto remove_handler; 61395c513ecSRafael J. Wysocki } 61495c513ecSRafael J. Wysocki 61595c513ecSRafael J. Wysocki if (!acpi_has_method(handle, "_PRW")) { 61695c513ecSRafael J. Wysocki dev_info(dev, "Missing _PRW\n"); 617596ca52aSZhang Rui ret = -ENODEV; 618596ca52aSZhang Rui goto remove_handler; 61995c513ecSRafael J. Wysocki } 62095c513ecSRafael J. Wysocki 62195c513ecSRafael J. Wysocki dd = devm_kzalloc(dev, sizeof(*dd), GFP_KERNEL); 622596ca52aSZhang Rui if (!dd) { 623596ca52aSZhang Rui ret = -ENOMEM; 624596ca52aSZhang Rui goto remove_handler; 625596ca52aSZhang Rui } 62695c513ecSRafael J. Wysocki 62795c513ecSRafael J. Wysocki dd->capabilities = caps; 62895c513ecSRafael J. Wysocki dev_set_drvdata(dev, dd); 62995c513ecSRafael J. Wysocki 63095c513ecSRafael J. Wysocki /* 63195c513ecSRafael J. Wysocki * Assume that the ACPI PM domain has been attached to the device and 63295c513ecSRafael J. Wysocki * simply enable system wakeup and runtime PM and put the device into 63395c513ecSRafael J. Wysocki * runtime suspend. Everything else should be taken care of by the ACPI 63495c513ecSRafael J. Wysocki * PM domain callbacks. 63595c513ecSRafael J. Wysocki */ 63695c513ecSRafael J. Wysocki device_init_wakeup(dev, true); 63795c513ecSRafael J. Wysocki dev_pm_set_driver_flags(dev, DPM_FLAG_SMART_SUSPEND | 6382a3f3475SRafael J. Wysocki DPM_FLAG_MAY_SKIP_RESUME); 63995c513ecSRafael J. Wysocki /* 64095c513ecSRafael J. Wysocki * The platform bus type layer tells the ACPI PM domain powers up the 64195c513ecSRafael J. Wysocki * device, so set the runtime PM status of it to "active". 64295c513ecSRafael J. Wysocki */ 64395c513ecSRafael J. Wysocki pm_runtime_set_active(dev); 64495c513ecSRafael J. Wysocki pm_runtime_enable(dev); 64595c513ecSRafael J. Wysocki pm_runtime_suspend(dev); 64695c513ecSRafael J. Wysocki 64795c513ecSRafael J. Wysocki ret = sysfs_create_group(&dev->kobj, &acpi_tad_attr_group); 64895c513ecSRafael J. Wysocki if (ret) 64995c513ecSRafael J. Wysocki goto fail; 65095c513ecSRafael J. Wysocki 65195c513ecSRafael J. Wysocki if (caps & ACPI_TAD_DC_WAKE) { 65295c513ecSRafael J. Wysocki ret = sysfs_create_group(&dev->kobj, &acpi_tad_dc_attr_group); 65395c513ecSRafael J. Wysocki if (ret) 65495c513ecSRafael J. Wysocki goto fail; 65595c513ecSRafael J. Wysocki } 65695c513ecSRafael J. Wysocki 6573230b2b3SRafael J. Wysocki if (caps & ACPI_TAD_RT) { 6583230b2b3SRafael J. Wysocki ret = sysfs_create_group(&dev->kobj, &acpi_tad_time_attr_group); 6593230b2b3SRafael J. Wysocki if (ret) 6603230b2b3SRafael J. Wysocki goto fail; 6613230b2b3SRafael J. Wysocki } 6623230b2b3SRafael J. Wysocki 66395c513ecSRafael J. Wysocki return 0; 66495c513ecSRafael J. Wysocki 66595c513ecSRafael J. Wysocki fail: 66695c513ecSRafael J. Wysocki acpi_tad_remove(pdev); 667596ca52aSZhang Rui /* Don't fallthrough because cmos rtc space handler is removed in acpi_tad_remove() */ 668596ca52aSZhang Rui return ret; 669596ca52aSZhang Rui 670596ca52aSZhang Rui remove_handler: 671596ca52aSZhang Rui acpi_remove_cmos_rtc_space_handler(handle); 67295c513ecSRafael J. Wysocki return ret; 67395c513ecSRafael J. Wysocki } 67495c513ecSRafael J. Wysocki 67595c513ecSRafael J. Wysocki static const struct acpi_device_id acpi_tad_ids[] = { 67695c513ecSRafael J. Wysocki {"ACPI000E", 0}, 67795c513ecSRafael J. Wysocki {} 67895c513ecSRafael J. Wysocki }; 67995c513ecSRafael J. Wysocki 68095c513ecSRafael J. Wysocki static struct platform_driver acpi_tad_driver = { 68195c513ecSRafael J. Wysocki .driver = { 68295c513ecSRafael J. Wysocki .name = "acpi-tad", 68395c513ecSRafael J. Wysocki .acpi_match_table = acpi_tad_ids, 68495c513ecSRafael J. Wysocki }, 68595c513ecSRafael J. Wysocki .probe = acpi_tad_probe, 686*9a7897a2SUwe Kleine-König .remove_new = acpi_tad_remove, 68795c513ecSRafael J. Wysocki }; 68895c513ecSRafael J. Wysocki MODULE_DEVICE_TABLE(acpi, acpi_tad_ids); 68995c513ecSRafael J. Wysocki 69095c513ecSRafael J. Wysocki module_platform_driver(acpi_tad_driver); 691