Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/acpi/proc.c
15109 views
1
#include <linux/proc_fs.h>
2
#include <linux/seq_file.h>
3
#include <linux/suspend.h>
4
#include <linux/bcd.h>
5
#include <asm/uaccess.h>
6
7
#include <acpi/acpi_bus.h>
8
#include <acpi/acpi_drivers.h>
9
10
#ifdef CONFIG_X86
11
#include <linux/mc146818rtc.h>
12
#endif
13
14
#include "sleep.h"
15
16
#define _COMPONENT ACPI_SYSTEM_COMPONENT
17
18
/*
19
* this file provides support for:
20
* /proc/acpi/alarm
21
* /proc/acpi/wakeup
22
*/
23
24
ACPI_MODULE_NAME("sleep")
25
26
#if defined(CONFIG_RTC_DRV_CMOS) || defined(CONFIG_RTC_DRV_CMOS_MODULE) || !defined(CONFIG_X86)
27
/* use /sys/class/rtc/rtcX/wakealarm instead; it's not ACPI-specific */
28
#else
29
#define HAVE_ACPI_LEGACY_ALARM
30
#endif
31
32
#ifdef HAVE_ACPI_LEGACY_ALARM
33
34
static u32 cmos_bcd_read(int offset, int rtc_control);
35
36
static int acpi_system_alarm_seq_show(struct seq_file *seq, void *offset)
37
{
38
u32 sec, min, hr;
39
u32 day, mo, yr, cent = 0;
40
u32 today = 0;
41
unsigned char rtc_control = 0;
42
unsigned long flags;
43
44
spin_lock_irqsave(&rtc_lock, flags);
45
46
rtc_control = CMOS_READ(RTC_CONTROL);
47
sec = cmos_bcd_read(RTC_SECONDS_ALARM, rtc_control);
48
min = cmos_bcd_read(RTC_MINUTES_ALARM, rtc_control);
49
hr = cmos_bcd_read(RTC_HOURS_ALARM, rtc_control);
50
51
/* If we ever get an FACP with proper values... */
52
if (acpi_gbl_FADT.day_alarm) {
53
/* ACPI spec: only low 6 its should be cared */
54
day = CMOS_READ(acpi_gbl_FADT.day_alarm) & 0x3F;
55
if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
56
day = bcd2bin(day);
57
} else
58
day = cmos_bcd_read(RTC_DAY_OF_MONTH, rtc_control);
59
if (acpi_gbl_FADT.month_alarm)
60
mo = cmos_bcd_read(acpi_gbl_FADT.month_alarm, rtc_control);
61
else {
62
mo = cmos_bcd_read(RTC_MONTH, rtc_control);
63
today = cmos_bcd_read(RTC_DAY_OF_MONTH, rtc_control);
64
}
65
if (acpi_gbl_FADT.century)
66
cent = cmos_bcd_read(acpi_gbl_FADT.century, rtc_control);
67
68
yr = cmos_bcd_read(RTC_YEAR, rtc_control);
69
70
spin_unlock_irqrestore(&rtc_lock, flags);
71
72
/* we're trusting the FADT (see above) */
73
if (!acpi_gbl_FADT.century)
74
/* If we're not trusting the FADT, we should at least make it
75
* right for _this_ century... ehm, what is _this_ century?
76
*
77
* TBD:
78
* ASAP: find piece of code in the kernel, e.g. star tracker driver,
79
* which we can trust to determine the century correctly. Atom
80
* watch driver would be nice, too...
81
*
82
* if that has not happened, change for first release in 2050:
83
* if (yr<50)
84
* yr += 2100;
85
* else
86
* yr += 2000; // current line of code
87
*
88
* if that has not happened either, please do on 2099/12/31:23:59:59
89
* s/2000/2100
90
*
91
*/
92
yr += 2000;
93
else
94
yr += cent * 100;
95
96
/*
97
* Show correct dates for alarms up to a month into the future.
98
* This solves issues for nearly all situations with the common
99
* 30-day alarm clocks in PC hardware.
100
*/
101
if (day < today) {
102
if (mo < 12) {
103
mo += 1;
104
} else {
105
mo = 1;
106
yr += 1;
107
}
108
}
109
110
seq_printf(seq, "%4.4u-", yr);
111
(mo > 12) ? seq_puts(seq, "**-") : seq_printf(seq, "%2.2u-", mo);
112
(day > 31) ? seq_puts(seq, "** ") : seq_printf(seq, "%2.2u ", day);
113
(hr > 23) ? seq_puts(seq, "**:") : seq_printf(seq, "%2.2u:", hr);
114
(min > 59) ? seq_puts(seq, "**:") : seq_printf(seq, "%2.2u:", min);
115
(sec > 59) ? seq_puts(seq, "**\n") : seq_printf(seq, "%2.2u\n", sec);
116
117
return 0;
118
}
119
120
static int acpi_system_alarm_open_fs(struct inode *inode, struct file *file)
121
{
122
return single_open(file, acpi_system_alarm_seq_show, PDE(inode)->data);
123
}
124
125
static int get_date_field(char **p, u32 * value)
126
{
127
char *next = NULL;
128
char *string_end = NULL;
129
int result = -EINVAL;
130
131
/*
132
* Try to find delimeter, only to insert null. The end of the
133
* string won't have one, but is still valid.
134
*/
135
if (*p == NULL)
136
return result;
137
138
next = strpbrk(*p, "- :");
139
if (next)
140
*next++ = '\0';
141
142
*value = simple_strtoul(*p, &string_end, 10);
143
144
/* Signal success if we got a good digit */
145
if (string_end != *p)
146
result = 0;
147
148
if (next)
149
*p = next;
150
else
151
*p = NULL;
152
153
return result;
154
}
155
156
/* Read a possibly BCD register, always return binary */
157
static u32 cmos_bcd_read(int offset, int rtc_control)
158
{
159
u32 val = CMOS_READ(offset);
160
if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
161
val = bcd2bin(val);
162
return val;
163
}
164
165
/* Write binary value into possibly BCD register */
166
static void cmos_bcd_write(u32 val, int offset, int rtc_control)
167
{
168
if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
169
val = bin2bcd(val);
170
CMOS_WRITE(val, offset);
171
}
172
173
static ssize_t
174
acpi_system_write_alarm(struct file *file,
175
const char __user * buffer, size_t count, loff_t * ppos)
176
{
177
int result = 0;
178
char alarm_string[30] = { '\0' };
179
char *p = alarm_string;
180
u32 sec, min, hr, day, mo, yr;
181
int adjust = 0;
182
unsigned char rtc_control = 0;
183
184
if (count > sizeof(alarm_string) - 1)
185
return -EINVAL;
186
187
if (copy_from_user(alarm_string, buffer, count))
188
return -EFAULT;
189
190
alarm_string[count] = '\0';
191
192
/* check for time adjustment */
193
if (alarm_string[0] == '+') {
194
p++;
195
adjust = 1;
196
}
197
198
if ((result = get_date_field(&p, &yr)))
199
goto end;
200
if ((result = get_date_field(&p, &mo)))
201
goto end;
202
if ((result = get_date_field(&p, &day)))
203
goto end;
204
if ((result = get_date_field(&p, &hr)))
205
goto end;
206
if ((result = get_date_field(&p, &min)))
207
goto end;
208
if ((result = get_date_field(&p, &sec)))
209
goto end;
210
211
spin_lock_irq(&rtc_lock);
212
213
rtc_control = CMOS_READ(RTC_CONTROL);
214
215
if (adjust) {
216
yr += cmos_bcd_read(RTC_YEAR, rtc_control);
217
mo += cmos_bcd_read(RTC_MONTH, rtc_control);
218
day += cmos_bcd_read(RTC_DAY_OF_MONTH, rtc_control);
219
hr += cmos_bcd_read(RTC_HOURS, rtc_control);
220
min += cmos_bcd_read(RTC_MINUTES, rtc_control);
221
sec += cmos_bcd_read(RTC_SECONDS, rtc_control);
222
}
223
224
spin_unlock_irq(&rtc_lock);
225
226
if (sec > 59) {
227
min += sec/60;
228
sec = sec%60;
229
}
230
if (min > 59) {
231
hr += min/60;
232
min = min%60;
233
}
234
if (hr > 23) {
235
day += hr/24;
236
hr = hr%24;
237
}
238
if (day > 31) {
239
mo += day/32;
240
day = day%32;
241
}
242
if (mo > 12) {
243
yr += mo/13;
244
mo = mo%13;
245
}
246
247
spin_lock_irq(&rtc_lock);
248
/*
249
* Disable alarm interrupt before setting alarm timer or else
250
* when ACPI_EVENT_RTC is enabled, a spurious ACPI interrupt occurs
251
*/
252
rtc_control &= ~RTC_AIE;
253
CMOS_WRITE(rtc_control, RTC_CONTROL);
254
CMOS_READ(RTC_INTR_FLAGS);
255
256
/* write the fields the rtc knows about */
257
cmos_bcd_write(hr, RTC_HOURS_ALARM, rtc_control);
258
cmos_bcd_write(min, RTC_MINUTES_ALARM, rtc_control);
259
cmos_bcd_write(sec, RTC_SECONDS_ALARM, rtc_control);
260
261
/*
262
* If the system supports an enhanced alarm it will have non-zero
263
* offsets into the CMOS RAM here -- which for some reason are pointing
264
* to the RTC area of memory.
265
*/
266
if (acpi_gbl_FADT.day_alarm)
267
cmos_bcd_write(day, acpi_gbl_FADT.day_alarm, rtc_control);
268
if (acpi_gbl_FADT.month_alarm)
269
cmos_bcd_write(mo, acpi_gbl_FADT.month_alarm, rtc_control);
270
if (acpi_gbl_FADT.century) {
271
if (adjust)
272
yr += cmos_bcd_read(acpi_gbl_FADT.century, rtc_control) * 100;
273
cmos_bcd_write(yr / 100, acpi_gbl_FADT.century, rtc_control);
274
}
275
/* enable the rtc alarm interrupt */
276
rtc_control |= RTC_AIE;
277
CMOS_WRITE(rtc_control, RTC_CONTROL);
278
CMOS_READ(RTC_INTR_FLAGS);
279
280
spin_unlock_irq(&rtc_lock);
281
282
acpi_clear_event(ACPI_EVENT_RTC);
283
acpi_enable_event(ACPI_EVENT_RTC, 0);
284
285
*ppos += count;
286
287
result = 0;
288
end:
289
return result ? result : count;
290
}
291
#endif /* HAVE_ACPI_LEGACY_ALARM */
292
293
static int
294
acpi_system_wakeup_device_seq_show(struct seq_file *seq, void *offset)
295
{
296
struct list_head *node, *next;
297
298
seq_printf(seq, "Device\tS-state\t Status Sysfs node\n");
299
300
mutex_lock(&acpi_device_lock);
301
list_for_each_safe(node, next, &acpi_wakeup_device_list) {
302
struct acpi_device *dev =
303
container_of(node, struct acpi_device, wakeup_list);
304
struct device *ldev;
305
306
if (!dev->wakeup.flags.valid)
307
continue;
308
309
ldev = acpi_get_physical_device(dev->handle);
310
seq_printf(seq, "%s\t S%d\t%c%-8s ",
311
dev->pnp.bus_id,
312
(u32) dev->wakeup.sleep_state,
313
dev->wakeup.flags.run_wake ? '*' : ' ',
314
(device_may_wakeup(&dev->dev)
315
|| (ldev && device_may_wakeup(ldev))) ?
316
"enabled" : "disabled");
317
if (ldev)
318
seq_printf(seq, "%s:%s",
319
ldev->bus ? ldev->bus->name : "no-bus",
320
dev_name(ldev));
321
seq_printf(seq, "\n");
322
put_device(ldev);
323
324
}
325
mutex_unlock(&acpi_device_lock);
326
return 0;
327
}
328
329
static void physical_device_enable_wakeup(struct acpi_device *adev)
330
{
331
struct device *dev = acpi_get_physical_device(adev->handle);
332
333
if (dev && device_can_wakeup(dev)) {
334
bool enable = !device_may_wakeup(dev);
335
device_set_wakeup_enable(dev, enable);
336
}
337
}
338
339
static ssize_t
340
acpi_system_write_wakeup_device(struct file *file,
341
const char __user * buffer,
342
size_t count, loff_t * ppos)
343
{
344
struct list_head *node, *next;
345
char strbuf[5];
346
char str[5] = "";
347
unsigned int len = count;
348
349
if (len > 4)
350
len = 4;
351
if (len < 0)
352
return -EFAULT;
353
354
if (copy_from_user(strbuf, buffer, len))
355
return -EFAULT;
356
strbuf[len] = '\0';
357
sscanf(strbuf, "%s", str);
358
359
mutex_lock(&acpi_device_lock);
360
list_for_each_safe(node, next, &acpi_wakeup_device_list) {
361
struct acpi_device *dev =
362
container_of(node, struct acpi_device, wakeup_list);
363
if (!dev->wakeup.flags.valid)
364
continue;
365
366
if (!strncmp(dev->pnp.bus_id, str, 4)) {
367
if (device_can_wakeup(&dev->dev)) {
368
bool enable = !device_may_wakeup(&dev->dev);
369
device_set_wakeup_enable(&dev->dev, enable);
370
} else {
371
physical_device_enable_wakeup(dev);
372
}
373
break;
374
}
375
}
376
mutex_unlock(&acpi_device_lock);
377
return count;
378
}
379
380
static int
381
acpi_system_wakeup_device_open_fs(struct inode *inode, struct file *file)
382
{
383
return single_open(file, acpi_system_wakeup_device_seq_show,
384
PDE(inode)->data);
385
}
386
387
static const struct file_operations acpi_system_wakeup_device_fops = {
388
.owner = THIS_MODULE,
389
.open = acpi_system_wakeup_device_open_fs,
390
.read = seq_read,
391
.write = acpi_system_write_wakeup_device,
392
.llseek = seq_lseek,
393
.release = single_release,
394
};
395
396
#ifdef HAVE_ACPI_LEGACY_ALARM
397
static const struct file_operations acpi_system_alarm_fops = {
398
.owner = THIS_MODULE,
399
.open = acpi_system_alarm_open_fs,
400
.read = seq_read,
401
.write = acpi_system_write_alarm,
402
.llseek = seq_lseek,
403
.release = single_release,
404
};
405
406
static u32 rtc_handler(void *context)
407
{
408
acpi_clear_event(ACPI_EVENT_RTC);
409
acpi_disable_event(ACPI_EVENT_RTC, 0);
410
411
return ACPI_INTERRUPT_HANDLED;
412
}
413
#endif /* HAVE_ACPI_LEGACY_ALARM */
414
415
int __init acpi_sleep_proc_init(void)
416
{
417
#ifdef HAVE_ACPI_LEGACY_ALARM
418
/* 'alarm' [R/W] */
419
proc_create("alarm", S_IFREG | S_IRUGO | S_IWUSR,
420
acpi_root_dir, &acpi_system_alarm_fops);
421
422
acpi_install_fixed_event_handler(ACPI_EVENT_RTC, rtc_handler, NULL);
423
/*
424
* Disable the RTC event after installing RTC handler.
425
* Only when RTC alarm is set will it be enabled.
426
*/
427
acpi_clear_event(ACPI_EVENT_RTC);
428
acpi_disable_event(ACPI_EVENT_RTC, 0);
429
#endif /* HAVE_ACPI_LEGACY_ALARM */
430
431
/* 'wakeup device' [R/W] */
432
proc_create("wakeup", S_IFREG | S_IRUGO | S_IWUSR,
433
acpi_root_dir, &acpi_system_wakeup_device_fops);
434
435
return 0;
436
}
437
438