.. | .. |
---|
52 | 52 | u32 capabilities; |
---|
53 | 53 | }; |
---|
54 | 54 | |
---|
| 55 | +struct acpi_tad_rt { |
---|
| 56 | + u16 year; /* 1900 - 9999 */ |
---|
| 57 | + u8 month; /* 1 - 12 */ |
---|
| 58 | + u8 day; /* 1 - 31 */ |
---|
| 59 | + u8 hour; /* 0 - 23 */ |
---|
| 60 | + u8 minute; /* 0 - 59 */ |
---|
| 61 | + u8 second; /* 0 - 59 */ |
---|
| 62 | + u8 valid; /* 0 (failed) or 1 (success) for reads, 0 for writes */ |
---|
| 63 | + u16 msec; /* 1 - 1000 */ |
---|
| 64 | + s16 tz; /* -1440 to 1440 or 2047 (unspecified) */ |
---|
| 65 | + u8 daylight; |
---|
| 66 | + u8 padding[3]; /* must be 0 */ |
---|
| 67 | +} __packed; |
---|
| 68 | + |
---|
| 69 | +static int acpi_tad_set_real_time(struct device *dev, struct acpi_tad_rt *rt) |
---|
| 70 | +{ |
---|
| 71 | + acpi_handle handle = ACPI_HANDLE(dev); |
---|
| 72 | + union acpi_object args[] = { |
---|
| 73 | + { .type = ACPI_TYPE_BUFFER, }, |
---|
| 74 | + }; |
---|
| 75 | + struct acpi_object_list arg_list = { |
---|
| 76 | + .pointer = args, |
---|
| 77 | + .count = ARRAY_SIZE(args), |
---|
| 78 | + }; |
---|
| 79 | + unsigned long long retval; |
---|
| 80 | + acpi_status status; |
---|
| 81 | + |
---|
| 82 | + if (rt->year < 1900 || rt->year > 9999 || |
---|
| 83 | + rt->month < 1 || rt->month > 12 || |
---|
| 84 | + rt->hour > 23 || rt->minute > 59 || rt->second > 59 || |
---|
| 85 | + rt->tz < -1440 || (rt->tz > 1440 && rt->tz != 2047) || |
---|
| 86 | + rt->daylight > 3) |
---|
| 87 | + return -ERANGE; |
---|
| 88 | + |
---|
| 89 | + args[0].buffer.pointer = (u8 *)rt; |
---|
| 90 | + args[0].buffer.length = sizeof(*rt); |
---|
| 91 | + |
---|
| 92 | + pm_runtime_get_sync(dev); |
---|
| 93 | + |
---|
| 94 | + status = acpi_evaluate_integer(handle, "_SRT", &arg_list, &retval); |
---|
| 95 | + |
---|
| 96 | + pm_runtime_put_sync(dev); |
---|
| 97 | + |
---|
| 98 | + if (ACPI_FAILURE(status) || retval) |
---|
| 99 | + return -EIO; |
---|
| 100 | + |
---|
| 101 | + return 0; |
---|
| 102 | +} |
---|
| 103 | + |
---|
| 104 | +static int acpi_tad_get_real_time(struct device *dev, struct acpi_tad_rt *rt) |
---|
| 105 | +{ |
---|
| 106 | + acpi_handle handle = ACPI_HANDLE(dev); |
---|
| 107 | + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER }; |
---|
| 108 | + union acpi_object *out_obj; |
---|
| 109 | + struct acpi_tad_rt *data; |
---|
| 110 | + acpi_status status; |
---|
| 111 | + int ret = -EIO; |
---|
| 112 | + |
---|
| 113 | + pm_runtime_get_sync(dev); |
---|
| 114 | + |
---|
| 115 | + status = acpi_evaluate_object(handle, "_GRT", NULL, &output); |
---|
| 116 | + |
---|
| 117 | + pm_runtime_put_sync(dev); |
---|
| 118 | + |
---|
| 119 | + if (ACPI_FAILURE(status)) |
---|
| 120 | + goto out_free; |
---|
| 121 | + |
---|
| 122 | + out_obj = output.pointer; |
---|
| 123 | + if (out_obj->type != ACPI_TYPE_BUFFER) |
---|
| 124 | + goto out_free; |
---|
| 125 | + |
---|
| 126 | + if (out_obj->buffer.length != sizeof(*rt)) |
---|
| 127 | + goto out_free; |
---|
| 128 | + |
---|
| 129 | + data = (struct acpi_tad_rt *)(out_obj->buffer.pointer); |
---|
| 130 | + if (!data->valid) |
---|
| 131 | + goto out_free; |
---|
| 132 | + |
---|
| 133 | + memcpy(rt, data, sizeof(*rt)); |
---|
| 134 | + ret = 0; |
---|
| 135 | + |
---|
| 136 | +out_free: |
---|
| 137 | + ACPI_FREE(output.pointer); |
---|
| 138 | + return ret; |
---|
| 139 | +} |
---|
| 140 | + |
---|
| 141 | +static char *acpi_tad_rt_next_field(char *s, int *val) |
---|
| 142 | +{ |
---|
| 143 | + char *p; |
---|
| 144 | + |
---|
| 145 | + p = strchr(s, ':'); |
---|
| 146 | + if (!p) |
---|
| 147 | + return NULL; |
---|
| 148 | + |
---|
| 149 | + *p = '\0'; |
---|
| 150 | + if (kstrtoint(s, 10, val)) |
---|
| 151 | + return NULL; |
---|
| 152 | + |
---|
| 153 | + return p + 1; |
---|
| 154 | +} |
---|
| 155 | + |
---|
| 156 | +static ssize_t time_store(struct device *dev, struct device_attribute *attr, |
---|
| 157 | + const char *buf, size_t count) |
---|
| 158 | +{ |
---|
| 159 | + struct acpi_tad_rt rt; |
---|
| 160 | + char *str, *s; |
---|
| 161 | + int val, ret = -ENODATA; |
---|
| 162 | + |
---|
| 163 | + str = kmemdup_nul(buf, count, GFP_KERNEL); |
---|
| 164 | + if (!str) |
---|
| 165 | + return -ENOMEM; |
---|
| 166 | + |
---|
| 167 | + s = acpi_tad_rt_next_field(str, &val); |
---|
| 168 | + if (!s) |
---|
| 169 | + goto out_free; |
---|
| 170 | + |
---|
| 171 | + rt.year = val; |
---|
| 172 | + |
---|
| 173 | + s = acpi_tad_rt_next_field(s, &val); |
---|
| 174 | + if (!s) |
---|
| 175 | + goto out_free; |
---|
| 176 | + |
---|
| 177 | + rt.month = val; |
---|
| 178 | + |
---|
| 179 | + s = acpi_tad_rt_next_field(s, &val); |
---|
| 180 | + if (!s) |
---|
| 181 | + goto out_free; |
---|
| 182 | + |
---|
| 183 | + rt.day = val; |
---|
| 184 | + |
---|
| 185 | + s = acpi_tad_rt_next_field(s, &val); |
---|
| 186 | + if (!s) |
---|
| 187 | + goto out_free; |
---|
| 188 | + |
---|
| 189 | + rt.hour = val; |
---|
| 190 | + |
---|
| 191 | + s = acpi_tad_rt_next_field(s, &val); |
---|
| 192 | + if (!s) |
---|
| 193 | + goto out_free; |
---|
| 194 | + |
---|
| 195 | + rt.minute = val; |
---|
| 196 | + |
---|
| 197 | + s = acpi_tad_rt_next_field(s, &val); |
---|
| 198 | + if (!s) |
---|
| 199 | + goto out_free; |
---|
| 200 | + |
---|
| 201 | + rt.second = val; |
---|
| 202 | + |
---|
| 203 | + s = acpi_tad_rt_next_field(s, &val); |
---|
| 204 | + if (!s) |
---|
| 205 | + goto out_free; |
---|
| 206 | + |
---|
| 207 | + rt.tz = val; |
---|
| 208 | + |
---|
| 209 | + if (kstrtoint(s, 10, &val)) |
---|
| 210 | + goto out_free; |
---|
| 211 | + |
---|
| 212 | + rt.daylight = val; |
---|
| 213 | + |
---|
| 214 | + rt.valid = 0; |
---|
| 215 | + rt.msec = 0; |
---|
| 216 | + memset(rt.padding, 0, 3); |
---|
| 217 | + |
---|
| 218 | + ret = acpi_tad_set_real_time(dev, &rt); |
---|
| 219 | + |
---|
| 220 | +out_free: |
---|
| 221 | + kfree(str); |
---|
| 222 | + return ret ? ret : count; |
---|
| 223 | +} |
---|
| 224 | + |
---|
| 225 | +static ssize_t time_show(struct device *dev, struct device_attribute *attr, |
---|
| 226 | + char *buf) |
---|
| 227 | +{ |
---|
| 228 | + struct acpi_tad_rt rt; |
---|
| 229 | + int ret; |
---|
| 230 | + |
---|
| 231 | + ret = acpi_tad_get_real_time(dev, &rt); |
---|
| 232 | + if (ret) |
---|
| 233 | + return ret; |
---|
| 234 | + |
---|
| 235 | + return sprintf(buf, "%u:%u:%u:%u:%u:%u:%d:%u\n", |
---|
| 236 | + rt.year, rt.month, rt.day, rt.hour, rt.minute, rt.second, |
---|
| 237 | + rt.tz, rt.daylight); |
---|
| 238 | +} |
---|
| 239 | + |
---|
| 240 | +static DEVICE_ATTR_RW(time); |
---|
| 241 | + |
---|
| 242 | +static struct attribute *acpi_tad_time_attrs[] = { |
---|
| 243 | + &dev_attr_time.attr, |
---|
| 244 | + NULL, |
---|
| 245 | +}; |
---|
| 246 | +static const struct attribute_group acpi_tad_time_attr_group = { |
---|
| 247 | + .attrs = acpi_tad_time_attrs, |
---|
| 248 | +}; |
---|
| 249 | + |
---|
55 | 250 | static int acpi_tad_wake_set(struct device *dev, char *method, u32 timer_id, |
---|
56 | 251 | u32 value) |
---|
57 | 252 | { |
---|
.. | .. |
---|
251 | 446 | return acpi_tad_alarm_read(dev, buf, ACPI_TAD_AC_TIMER); |
---|
252 | 447 | } |
---|
253 | 448 | |
---|
254 | | -static DEVICE_ATTR(ac_alarm, S_IRUSR | S_IWUSR, ac_alarm_show, ac_alarm_store); |
---|
| 449 | +static DEVICE_ATTR_RW(ac_alarm); |
---|
255 | 450 | |
---|
256 | 451 | static ssize_t ac_policy_store(struct device *dev, struct device_attribute *attr, |
---|
257 | 452 | const char *buf, size_t count) |
---|
.. | .. |
---|
267 | 462 | return acpi_tad_policy_read(dev, buf, ACPI_TAD_AC_TIMER); |
---|
268 | 463 | } |
---|
269 | 464 | |
---|
270 | | -static DEVICE_ATTR(ac_policy, S_IRUSR | S_IWUSR, ac_policy_show, ac_policy_store); |
---|
| 465 | +static DEVICE_ATTR_RW(ac_policy); |
---|
271 | 466 | |
---|
272 | 467 | static ssize_t ac_status_store(struct device *dev, struct device_attribute *attr, |
---|
273 | 468 | const char *buf, size_t count) |
---|
.. | .. |
---|
283 | 478 | return acpi_tad_status_read(dev, buf, ACPI_TAD_AC_TIMER); |
---|
284 | 479 | } |
---|
285 | 480 | |
---|
286 | | -static DEVICE_ATTR(ac_status, S_IRUSR | S_IWUSR, ac_status_show, ac_status_store); |
---|
| 481 | +static DEVICE_ATTR_RW(ac_status); |
---|
287 | 482 | |
---|
288 | 483 | static struct attribute *acpi_tad_attrs[] = { |
---|
289 | 484 | &dev_attr_caps.attr, |
---|
.. | .. |
---|
310 | 505 | return acpi_tad_alarm_read(dev, buf, ACPI_TAD_DC_TIMER); |
---|
311 | 506 | } |
---|
312 | 507 | |
---|
313 | | -static DEVICE_ATTR(dc_alarm, S_IRUSR | S_IWUSR, dc_alarm_show, dc_alarm_store); |
---|
| 508 | +static DEVICE_ATTR_RW(dc_alarm); |
---|
314 | 509 | |
---|
315 | 510 | static ssize_t dc_policy_store(struct device *dev, struct device_attribute *attr, |
---|
316 | 511 | const char *buf, size_t count) |
---|
.. | .. |
---|
326 | 521 | return acpi_tad_policy_read(dev, buf, ACPI_TAD_DC_TIMER); |
---|
327 | 522 | } |
---|
328 | 523 | |
---|
329 | | -static DEVICE_ATTR(dc_policy, S_IRUSR | S_IWUSR, dc_policy_show, dc_policy_store); |
---|
| 524 | +static DEVICE_ATTR_RW(dc_policy); |
---|
330 | 525 | |
---|
331 | 526 | static ssize_t dc_status_store(struct device *dev, struct device_attribute *attr, |
---|
332 | 527 | const char *buf, size_t count) |
---|
.. | .. |
---|
342 | 537 | return acpi_tad_status_read(dev, buf, ACPI_TAD_DC_TIMER); |
---|
343 | 538 | } |
---|
344 | 539 | |
---|
345 | | -static DEVICE_ATTR(dc_status, S_IRUSR | S_IWUSR, dc_status_show, dc_status_store); |
---|
| 540 | +static DEVICE_ATTR_RW(dc_status); |
---|
346 | 541 | |
---|
347 | 542 | static struct attribute *acpi_tad_dc_attrs[] = { |
---|
348 | 543 | &dev_attr_dc_alarm.attr, |
---|
.. | .. |
---|
429 | 624 | */ |
---|
430 | 625 | device_init_wakeup(dev, true); |
---|
431 | 626 | dev_pm_set_driver_flags(dev, DPM_FLAG_SMART_SUSPEND | |
---|
432 | | - DPM_FLAG_LEAVE_SUSPENDED); |
---|
| 627 | + DPM_FLAG_MAY_SKIP_RESUME); |
---|
433 | 628 | /* |
---|
434 | 629 | * The platform bus type layer tells the ACPI PM domain powers up the |
---|
435 | 630 | * device, so set the runtime PM status of it to "active". |
---|
.. | .. |
---|
448 | 643 | goto fail; |
---|
449 | 644 | } |
---|
450 | 645 | |
---|
| 646 | + if (caps & ACPI_TAD_RT) { |
---|
| 647 | + ret = sysfs_create_group(&dev->kobj, &acpi_tad_time_attr_group); |
---|
| 648 | + if (ret) |
---|
| 649 | + goto fail; |
---|
| 650 | + } |
---|
| 651 | + |
---|
451 | 652 | return 0; |
---|
452 | 653 | |
---|
453 | 654 | fail: |
---|