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 55*3230b2b3SRafael J. Wysocki struct acpi_tad_rt { 56*3230b2b3SRafael J. Wysocki u16 year; /* 1900 - 9999 */ 57*3230b2b3SRafael J. Wysocki u8 month; /* 1 - 12 */ 58*3230b2b3SRafael J. Wysocki u8 day; /* 1 - 31 */ 59*3230b2b3SRafael J. Wysocki u8 hour; /* 0 - 23 */ 60*3230b2b3SRafael J. Wysocki u8 minute; /* 0 - 59 */ 61*3230b2b3SRafael J. Wysocki u8 second; /* 0 - 59 */ 62*3230b2b3SRafael J. Wysocki u8 valid; /* 0 (failed) or 1 (success) for reads, 0 for writes */ 63*3230b2b3SRafael J. Wysocki u16 msec; /* 1 - 1000 */ 64*3230b2b3SRafael J. Wysocki s16 tz; /* -1440 to 1440 or 2047 (unspecified) */ 65*3230b2b3SRafael J. Wysocki u8 daylight; 66*3230b2b3SRafael J. Wysocki u8 padding[3]; /* must be 0 */ 67*3230b2b3SRafael J. Wysocki } __packed; 68*3230b2b3SRafael J. Wysocki 69*3230b2b3SRafael J. Wysocki static int acpi_tad_set_real_time(struct device *dev, struct acpi_tad_rt *rt) 70*3230b2b3SRafael J. Wysocki { 71*3230b2b3SRafael J. Wysocki acpi_handle handle = ACPI_HANDLE(dev); 72*3230b2b3SRafael J. Wysocki union acpi_object args[] = { 73*3230b2b3SRafael J. Wysocki { .type = ACPI_TYPE_BUFFER, }, 74*3230b2b3SRafael J. Wysocki }; 75*3230b2b3SRafael J. Wysocki struct acpi_object_list arg_list = { 76*3230b2b3SRafael J. Wysocki .pointer = args, 77*3230b2b3SRafael J. Wysocki .count = ARRAY_SIZE(args), 78*3230b2b3SRafael J. Wysocki }; 79*3230b2b3SRafael J. Wysocki unsigned long long retval; 80*3230b2b3SRafael J. Wysocki acpi_status status; 81*3230b2b3SRafael J. Wysocki 82*3230b2b3SRafael J. Wysocki if (rt->year < 1900 || rt->year > 9999 || 83*3230b2b3SRafael J. Wysocki rt->month < 1 || rt->month > 12 || 84*3230b2b3SRafael J. Wysocki rt->hour > 23 || rt->minute > 59 || rt->second > 59 || 85*3230b2b3SRafael J. Wysocki rt->tz < -1440 || (rt->tz > 1440 && rt->tz != 2047) || 86*3230b2b3SRafael J. Wysocki rt->daylight > 3) 87*3230b2b3SRafael J. Wysocki return -ERANGE; 88*3230b2b3SRafael J. Wysocki 89*3230b2b3SRafael J. Wysocki args[0].buffer.pointer = (u8 *)rt; 90*3230b2b3SRafael J. Wysocki args[0].buffer.length = sizeof(*rt); 91*3230b2b3SRafael J. Wysocki 92*3230b2b3SRafael J. Wysocki pm_runtime_get_sync(dev); 93*3230b2b3SRafael J. Wysocki 94*3230b2b3SRafael J. Wysocki status = acpi_evaluate_integer(handle, "_SRT", &arg_list, &retval); 95*3230b2b3SRafael J. Wysocki 96*3230b2b3SRafael J. Wysocki pm_runtime_put_sync(dev); 97*3230b2b3SRafael J. Wysocki 98*3230b2b3SRafael J. Wysocki if (ACPI_FAILURE(status) || retval) 99*3230b2b3SRafael J. Wysocki return -EIO; 100*3230b2b3SRafael J. Wysocki 101*3230b2b3SRafael J. Wysocki return 0; 102*3230b2b3SRafael J. Wysocki } 103*3230b2b3SRafael J. Wysocki 104*3230b2b3SRafael J. Wysocki static int acpi_tad_get_real_time(struct device *dev, struct acpi_tad_rt *rt) 105*3230b2b3SRafael J. Wysocki { 106*3230b2b3SRafael J. Wysocki acpi_handle handle = ACPI_HANDLE(dev); 107*3230b2b3SRafael J. Wysocki struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER }; 108*3230b2b3SRafael J. Wysocki union acpi_object *out_obj; 109*3230b2b3SRafael J. Wysocki struct acpi_tad_rt *data; 110*3230b2b3SRafael J. Wysocki acpi_status status; 111*3230b2b3SRafael J. Wysocki int ret = -EIO; 112*3230b2b3SRafael J. Wysocki 113*3230b2b3SRafael J. Wysocki pm_runtime_get_sync(dev); 114*3230b2b3SRafael J. Wysocki 115*3230b2b3SRafael J. Wysocki status = acpi_evaluate_object(handle, "_GRT", NULL, &output); 116*3230b2b3SRafael J. Wysocki 117*3230b2b3SRafael J. Wysocki pm_runtime_put_sync(dev); 118*3230b2b3SRafael J. Wysocki 119*3230b2b3SRafael J. Wysocki if (ACPI_FAILURE(status)) 120*3230b2b3SRafael J. Wysocki goto out_free; 121*3230b2b3SRafael J. Wysocki 122*3230b2b3SRafael J. Wysocki out_obj = output.pointer; 123*3230b2b3SRafael J. Wysocki if (out_obj->type != ACPI_TYPE_BUFFER) 124*3230b2b3SRafael J. Wysocki goto out_free; 125*3230b2b3SRafael J. Wysocki 126*3230b2b3SRafael J. Wysocki if (out_obj->buffer.length != sizeof(*rt)) 127*3230b2b3SRafael J. Wysocki goto out_free; 128*3230b2b3SRafael J. Wysocki 129*3230b2b3SRafael J. Wysocki data = (struct acpi_tad_rt *)(out_obj->buffer.pointer); 130*3230b2b3SRafael J. Wysocki if (!data->valid) 131*3230b2b3SRafael J. Wysocki goto out_free; 132*3230b2b3SRafael J. Wysocki 133*3230b2b3SRafael J. Wysocki memcpy(rt, data, sizeof(*rt)); 134*3230b2b3SRafael J. Wysocki ret = 0; 135*3230b2b3SRafael J. Wysocki 136*3230b2b3SRafael J. Wysocki out_free: 137*3230b2b3SRafael J. Wysocki ACPI_FREE(output.pointer); 138*3230b2b3SRafael J. Wysocki return ret; 139*3230b2b3SRafael J. Wysocki } 140*3230b2b3SRafael J. Wysocki 141*3230b2b3SRafael J. Wysocki static char *acpi_tad_rt_next_field(char *s, int *val) 142*3230b2b3SRafael J. Wysocki { 143*3230b2b3SRafael J. Wysocki char *p; 144*3230b2b3SRafael J. Wysocki 145*3230b2b3SRafael J. Wysocki p = strchr(s, ':'); 146*3230b2b3SRafael J. Wysocki if (!p) 147*3230b2b3SRafael J. Wysocki return NULL; 148*3230b2b3SRafael J. Wysocki 149*3230b2b3SRafael J. Wysocki *p = '\0'; 150*3230b2b3SRafael J. Wysocki if (kstrtoint(s, 10, val)) 151*3230b2b3SRafael J. Wysocki return NULL; 152*3230b2b3SRafael J. Wysocki 153*3230b2b3SRafael J. Wysocki return p + 1; 154*3230b2b3SRafael J. Wysocki } 155*3230b2b3SRafael J. Wysocki 156*3230b2b3SRafael J. Wysocki static ssize_t time_store(struct device *dev, struct device_attribute *attr, 157*3230b2b3SRafael J. Wysocki const char *buf, size_t count) 158*3230b2b3SRafael J. Wysocki { 159*3230b2b3SRafael J. Wysocki struct acpi_tad_rt rt; 160*3230b2b3SRafael J. Wysocki char *str, *s; 161*3230b2b3SRafael J. Wysocki int val, ret = -ENODATA; 162*3230b2b3SRafael J. Wysocki 163*3230b2b3SRafael J. Wysocki str = kmemdup_nul(buf, count, GFP_KERNEL); 164*3230b2b3SRafael J. Wysocki if (!str) 165*3230b2b3SRafael J. Wysocki return -ENOMEM; 166*3230b2b3SRafael J. Wysocki 167*3230b2b3SRafael J. Wysocki s = acpi_tad_rt_next_field(str, &val); 168*3230b2b3SRafael J. Wysocki if (!s) 169*3230b2b3SRafael J. Wysocki goto out_free; 170*3230b2b3SRafael J. Wysocki 171*3230b2b3SRafael J. Wysocki rt.year = val; 172*3230b2b3SRafael J. Wysocki 173*3230b2b3SRafael J. Wysocki s = acpi_tad_rt_next_field(s, &val); 174*3230b2b3SRafael J. Wysocki if (!s) 175*3230b2b3SRafael J. Wysocki goto out_free; 176*3230b2b3SRafael J. Wysocki 177*3230b2b3SRafael J. Wysocki rt.month = val; 178*3230b2b3SRafael J. Wysocki 179*3230b2b3SRafael J. Wysocki s = acpi_tad_rt_next_field(s, &val); 180*3230b2b3SRafael J. Wysocki if (!s) 181*3230b2b3SRafael J. Wysocki goto out_free; 182*3230b2b3SRafael J. Wysocki 183*3230b2b3SRafael J. Wysocki rt.day = val; 184*3230b2b3SRafael J. Wysocki 185*3230b2b3SRafael J. Wysocki s = acpi_tad_rt_next_field(s, &val); 186*3230b2b3SRafael J. Wysocki if (!s) 187*3230b2b3SRafael J. Wysocki goto out_free; 188*3230b2b3SRafael J. Wysocki 189*3230b2b3SRafael J. Wysocki rt.hour = val; 190*3230b2b3SRafael J. Wysocki 191*3230b2b3SRafael J. Wysocki s = acpi_tad_rt_next_field(s, &val); 192*3230b2b3SRafael J. Wysocki if (!s) 193*3230b2b3SRafael J. Wysocki goto out_free; 194*3230b2b3SRafael J. Wysocki 195*3230b2b3SRafael J. Wysocki rt.minute = val; 196*3230b2b3SRafael J. Wysocki 197*3230b2b3SRafael J. Wysocki s = acpi_tad_rt_next_field(s, &val); 198*3230b2b3SRafael J. Wysocki if (!s) 199*3230b2b3SRafael J. Wysocki goto out_free; 200*3230b2b3SRafael J. Wysocki 201*3230b2b3SRafael J. Wysocki rt.second = val; 202*3230b2b3SRafael J. Wysocki 203*3230b2b3SRafael J. Wysocki s = acpi_tad_rt_next_field(s, &val); 204*3230b2b3SRafael J. Wysocki if (!s) 205*3230b2b3SRafael J. Wysocki goto out_free; 206*3230b2b3SRafael J. Wysocki 207*3230b2b3SRafael J. Wysocki rt.tz = val; 208*3230b2b3SRafael J. Wysocki 209*3230b2b3SRafael J. Wysocki if (kstrtoint(s, 10, &val)) 210*3230b2b3SRafael J. Wysocki goto out_free; 211*3230b2b3SRafael J. Wysocki 212*3230b2b3SRafael J. Wysocki rt.daylight = val; 213*3230b2b3SRafael J. Wysocki 214*3230b2b3SRafael J. Wysocki rt.valid = 0; 215*3230b2b3SRafael J. Wysocki rt.msec = 0; 216*3230b2b3SRafael J. Wysocki memset(rt.padding, 0, 3); 217*3230b2b3SRafael J. Wysocki 218*3230b2b3SRafael J. Wysocki ret = acpi_tad_set_real_time(dev, &rt); 219*3230b2b3SRafael J. Wysocki 220*3230b2b3SRafael J. Wysocki out_free: 221*3230b2b3SRafael J. Wysocki kfree(str); 222*3230b2b3SRafael J. Wysocki return ret ? ret : count; 223*3230b2b3SRafael J. Wysocki } 224*3230b2b3SRafael J. Wysocki 225*3230b2b3SRafael J. Wysocki static ssize_t time_show(struct device *dev, struct device_attribute *attr, 226*3230b2b3SRafael J. Wysocki char *buf) 227*3230b2b3SRafael J. Wysocki { 228*3230b2b3SRafael J. Wysocki struct acpi_tad_rt rt; 229*3230b2b3SRafael J. Wysocki int ret; 230*3230b2b3SRafael J. Wysocki 231*3230b2b3SRafael J. Wysocki ret = acpi_tad_get_real_time(dev, &rt); 232*3230b2b3SRafael J. Wysocki if (ret) 233*3230b2b3SRafael J. Wysocki return ret; 234*3230b2b3SRafael J. Wysocki 235*3230b2b3SRafael J. Wysocki return sprintf(buf, "%u:%u:%u:%u:%u:%u:%d:%u\n", 236*3230b2b3SRafael J. Wysocki rt.year, rt.month, rt.day, rt.hour, rt.minute, rt.second, 237*3230b2b3SRafael J. Wysocki rt.tz, rt.daylight); 238*3230b2b3SRafael J. Wysocki } 239*3230b2b3SRafael J. Wysocki 240*3230b2b3SRafael J. Wysocki static DEVICE_ATTR(time, S_IRUSR | S_IWUSR, time_show, time_store); 241*3230b2b3SRafael J. Wysocki 242*3230b2b3SRafael J. Wysocki static struct attribute *acpi_tad_time_attrs[] = { 243*3230b2b3SRafael J. Wysocki &dev_attr_time.attr, 244*3230b2b3SRafael J. Wysocki NULL, 245*3230b2b3SRafael J. Wysocki }; 246*3230b2b3SRafael J. Wysocki static const struct attribute_group acpi_tad_time_attr_group = { 247*3230b2b3SRafael J. Wysocki .attrs = acpi_tad_time_attrs, 248*3230b2b3SRafael J. Wysocki }; 249*3230b2b3SRafael 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 44995c513ecSRafael J. Wysocki static DEVICE_ATTR(ac_alarm, S_IRUSR | S_IWUSR, ac_alarm_show, ac_alarm_store); 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 46595c513ecSRafael J. Wysocki static DEVICE_ATTR(ac_policy, S_IRUSR | S_IWUSR, ac_policy_show, ac_policy_store); 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 48195c513ecSRafael J. Wysocki static DEVICE_ATTR(ac_status, S_IRUSR | S_IWUSR, ac_status_show, ac_status_store); 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 50895c513ecSRafael J. Wysocki static DEVICE_ATTR(dc_alarm, S_IRUSR | S_IWUSR, dc_alarm_show, dc_alarm_store); 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 52495c513ecSRafael J. Wysocki static DEVICE_ATTR(dc_policy, S_IRUSR | S_IWUSR, dc_policy_show, dc_policy_store); 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 54095c513ecSRafael J. Wysocki static DEVICE_ATTR(dc_status, S_IRUSR | S_IWUSR, dc_status_show, dc_status_store); 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 55795c513ecSRafael J. Wysocki static int acpi_tad_remove(struct platform_device *pdev) 55895c513ecSRafael J. Wysocki { 55995c513ecSRafael J. Wysocki struct device *dev = &pdev->dev; 56095c513ecSRafael J. Wysocki struct acpi_tad_driver_data *dd = dev_get_drvdata(dev); 56195c513ecSRafael J. Wysocki 56295c513ecSRafael J. Wysocki device_init_wakeup(dev, false); 56395c513ecSRafael J. Wysocki 56495c513ecSRafael J. Wysocki pm_runtime_get_sync(dev); 56595c513ecSRafael J. Wysocki 56695c513ecSRafael J. Wysocki if (dd->capabilities & ACPI_TAD_DC_WAKE) 56795c513ecSRafael J. Wysocki sysfs_remove_group(&dev->kobj, &acpi_tad_dc_attr_group); 56895c513ecSRafael J. Wysocki 56995c513ecSRafael J. Wysocki sysfs_remove_group(&dev->kobj, &acpi_tad_attr_group); 57095c513ecSRafael J. Wysocki 57195c513ecSRafael J. Wysocki acpi_tad_disable_timer(dev, ACPI_TAD_AC_TIMER); 57295c513ecSRafael J. Wysocki acpi_tad_clear_status(dev, ACPI_TAD_AC_TIMER); 57395c513ecSRafael J. Wysocki if (dd->capabilities & ACPI_TAD_DC_WAKE) { 57495c513ecSRafael J. Wysocki acpi_tad_disable_timer(dev, ACPI_TAD_DC_TIMER); 57595c513ecSRafael J. Wysocki acpi_tad_clear_status(dev, ACPI_TAD_DC_TIMER); 57695c513ecSRafael J. Wysocki } 57795c513ecSRafael J. Wysocki 57895c513ecSRafael J. Wysocki pm_runtime_put_sync(dev); 57995c513ecSRafael J. Wysocki pm_runtime_disable(dev); 58095c513ecSRafael J. Wysocki return 0; 58195c513ecSRafael J. Wysocki } 58295c513ecSRafael J. Wysocki 58395c513ecSRafael J. Wysocki static int acpi_tad_probe(struct platform_device *pdev) 58495c513ecSRafael J. Wysocki { 58595c513ecSRafael J. Wysocki struct device *dev = &pdev->dev; 58695c513ecSRafael J. Wysocki acpi_handle handle = ACPI_HANDLE(dev); 58795c513ecSRafael J. Wysocki struct acpi_tad_driver_data *dd; 58895c513ecSRafael J. Wysocki acpi_status status; 58995c513ecSRafael J. Wysocki unsigned long long caps; 59095c513ecSRafael J. Wysocki int ret; 59195c513ecSRafael J. Wysocki 59295c513ecSRafael J. Wysocki /* 59395c513ecSRafael J. Wysocki * Initialization failure messages are mostly about firmware issues, so 59495c513ecSRafael J. Wysocki * print them at the "info" level. 59595c513ecSRafael J. Wysocki */ 59695c513ecSRafael J. Wysocki status = acpi_evaluate_integer(handle, "_GCP", NULL, &caps); 59795c513ecSRafael J. Wysocki if (ACPI_FAILURE(status)) { 59895c513ecSRafael J. Wysocki dev_info(dev, "Unable to get capabilities\n"); 59995c513ecSRafael J. Wysocki return -ENODEV; 60095c513ecSRafael J. Wysocki } 60195c513ecSRafael J. Wysocki 60295c513ecSRafael J. Wysocki if (!(caps & ACPI_TAD_AC_WAKE)) { 60395c513ecSRafael J. Wysocki dev_info(dev, "Unsupported capabilities\n"); 60495c513ecSRafael J. Wysocki return -ENODEV; 60595c513ecSRafael J. Wysocki } 60695c513ecSRafael J. Wysocki 60795c513ecSRafael J. Wysocki if (!acpi_has_method(handle, "_PRW")) { 60895c513ecSRafael J. Wysocki dev_info(dev, "Missing _PRW\n"); 60995c513ecSRafael J. Wysocki return -ENODEV; 61095c513ecSRafael J. Wysocki } 61195c513ecSRafael J. Wysocki 61295c513ecSRafael J. Wysocki dd = devm_kzalloc(dev, sizeof(*dd), GFP_KERNEL); 61395c513ecSRafael J. Wysocki if (!dd) 61495c513ecSRafael J. Wysocki return -ENOMEM; 61595c513ecSRafael J. Wysocki 61695c513ecSRafael J. Wysocki dd->capabilities = caps; 61795c513ecSRafael J. Wysocki dev_set_drvdata(dev, dd); 61895c513ecSRafael J. Wysocki 61995c513ecSRafael J. Wysocki /* 62095c513ecSRafael J. Wysocki * Assume that the ACPI PM domain has been attached to the device and 62195c513ecSRafael J. Wysocki * simply enable system wakeup and runtime PM and put the device into 62295c513ecSRafael J. Wysocki * runtime suspend. Everything else should be taken care of by the ACPI 62395c513ecSRafael J. Wysocki * PM domain callbacks. 62495c513ecSRafael J. Wysocki */ 62595c513ecSRafael J. Wysocki device_init_wakeup(dev, true); 62695c513ecSRafael J. Wysocki dev_pm_set_driver_flags(dev, DPM_FLAG_SMART_SUSPEND | 62795c513ecSRafael J. Wysocki DPM_FLAG_LEAVE_SUSPENDED); 62895c513ecSRafael J. Wysocki /* 62995c513ecSRafael J. Wysocki * The platform bus type layer tells the ACPI PM domain powers up the 63095c513ecSRafael J. Wysocki * device, so set the runtime PM status of it to "active". 63195c513ecSRafael J. Wysocki */ 63295c513ecSRafael J. Wysocki pm_runtime_set_active(dev); 63395c513ecSRafael J. Wysocki pm_runtime_enable(dev); 63495c513ecSRafael J. Wysocki pm_runtime_suspend(dev); 63595c513ecSRafael J. Wysocki 63695c513ecSRafael J. Wysocki ret = sysfs_create_group(&dev->kobj, &acpi_tad_attr_group); 63795c513ecSRafael J. Wysocki if (ret) 63895c513ecSRafael J. Wysocki goto fail; 63995c513ecSRafael J. Wysocki 64095c513ecSRafael J. Wysocki if (caps & ACPI_TAD_DC_WAKE) { 64195c513ecSRafael J. Wysocki ret = sysfs_create_group(&dev->kobj, &acpi_tad_dc_attr_group); 64295c513ecSRafael J. Wysocki if (ret) 64395c513ecSRafael J. Wysocki goto fail; 64495c513ecSRafael J. Wysocki } 64595c513ecSRafael J. Wysocki 646*3230b2b3SRafael J. Wysocki if (caps & ACPI_TAD_RT) { 647*3230b2b3SRafael J. Wysocki ret = sysfs_create_group(&dev->kobj, &acpi_tad_time_attr_group); 648*3230b2b3SRafael J. Wysocki if (ret) 649*3230b2b3SRafael J. Wysocki goto fail; 650*3230b2b3SRafael J. Wysocki } 651*3230b2b3SRafael J. Wysocki 65295c513ecSRafael J. Wysocki return 0; 65395c513ecSRafael J. Wysocki 65495c513ecSRafael J. Wysocki fail: 65595c513ecSRafael J. Wysocki acpi_tad_remove(pdev); 65695c513ecSRafael J. Wysocki return ret; 65795c513ecSRafael J. Wysocki } 65895c513ecSRafael J. Wysocki 65995c513ecSRafael J. Wysocki static const struct acpi_device_id acpi_tad_ids[] = { 66095c513ecSRafael J. Wysocki {"ACPI000E", 0}, 66195c513ecSRafael J. Wysocki {} 66295c513ecSRafael J. Wysocki }; 66395c513ecSRafael J. Wysocki 66495c513ecSRafael J. Wysocki static struct platform_driver acpi_tad_driver = { 66595c513ecSRafael J. Wysocki .driver = { 66695c513ecSRafael J. Wysocki .name = "acpi-tad", 66795c513ecSRafael J. Wysocki .acpi_match_table = acpi_tad_ids, 66895c513ecSRafael J. Wysocki }, 66995c513ecSRafael J. Wysocki .probe = acpi_tad_probe, 67095c513ecSRafael J. Wysocki .remove = acpi_tad_remove, 67195c513ecSRafael J. Wysocki }; 67295c513ecSRafael J. Wysocki MODULE_DEVICE_TABLE(acpi, acpi_tad_ids); 67395c513ecSRafael J. Wysocki 67495c513ecSRafael J. Wysocki module_platform_driver(acpi_tad_driver); 675