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