Blame view

drivers/rtc/class.c 6.58 KB
0c86edc0d   Alessandro Zummo   [PATCH] RTC subsy...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  /*
   * RTC subsystem, base class
   *
   * Copyright (C) 2005 Tower Technologies
   * Author: Alessandro Zummo <a.zummo@towertech.it>
   *
   * class skeleton from drivers/hwmon/hwmon.c
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License version 2 as
   * published by the Free Software Foundation.
  */
  
  #include <linux/module.h>
  #include <linux/rtc.h>
  #include <linux/kdev_t.h>
  #include <linux/idr.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
18
  #include <linux/slab.h>
6610e0893   John Stultz   RTC: Rework RTC c...
19
  #include <linux/workqueue.h>
0c86edc0d   Alessandro Zummo   [PATCH] RTC subsy...
20

5726fb201   David Brownell   rtc: remove /sys/...
21
  #include "rtc-core.h"
6d03d06db   Jonathan Cameron   drivers/rtc/class...
22
  static DEFINE_IDA(rtc_ida);
0c86edc0d   Alessandro Zummo   [PATCH] RTC subsy...
23
  struct class *rtc_class;
cd9662094   David Brownell   rtc: remove rest ...
24
  static void rtc_device_release(struct device *dev)
0c86edc0d   Alessandro Zummo   [PATCH] RTC subsy...
25
  {
cd9662094   David Brownell   rtc: remove rest ...
26
  	struct rtc_device *rtc = to_rtc_device(dev);
6d03d06db   Jonathan Cameron   drivers/rtc/class...
27
  	ida_simple_remove(&rtc_ida, rtc->id);
0c86edc0d   Alessandro Zummo   [PATCH] RTC subsy...
28
29
  	kfree(rtc);
  }
7ca1d488f   David Brownell   rtc: suspend()/re...
30
31
32
33
34
35
  #if defined(CONFIG_PM) && defined(CONFIG_RTC_HCTOSYS_DEVICE)
  
  /*
   * On suspend(), measure the delta between one RTC and the
   * system's wall clock; restore it on resume().
   */
3dcad5ff0   John Stultz   rtc: Avoid accumu...
36
  static struct timespec old_rtc, old_system, old_delta;
7ca1d488f   David Brownell   rtc: suspend()/re...
37
38
39
40
41
  
  static int rtc_suspend(struct device *dev, pm_message_t mesg)
  {
  	struct rtc_device	*rtc = to_rtc_device(dev);
  	struct rtc_time		tm;
3dcad5ff0   John Stultz   rtc: Avoid accumu...
42
  	struct timespec		delta, delta_delta;
d4afc76c0   Kay Sievers   rtc: struct devic...
43
  	if (strcmp(dev_name(&rtc->dev), CONFIG_RTC_HCTOSYS_DEVICE) != 0)
7ca1d488f   David Brownell   rtc: suspend()/re...
44
  		return 0;
3dcad5ff0   John Stultz   rtc: Avoid accumu...
45
  	/* snapshot the current RTC and system time at suspend*/
7ca1d488f   David Brownell   rtc: suspend()/re...
46
  	rtc_read_time(rtc, &tm);
3dcad5ff0   John Stultz   rtc: Avoid accumu...
47
48
49
50
51
52
53
54
55
56
57
58
  	getnstimeofday(&old_system);
  	rtc_tm_to_time(&tm, &old_rtc.tv_sec);
  
  
  	/*
  	 * To avoid drift caused by repeated suspend/resumes,
  	 * which each can add ~1 second drift error,
  	 * try to compensate so the difference in system time
  	 * and rtc time stays close to constant.
  	 */
  	delta = timespec_sub(old_system, old_rtc);
  	delta_delta = timespec_sub(delta, old_delta);
6a8943d9e   Arve Hjønnevåg   rtc: Fix some bug...
59
  	if (delta_delta.tv_sec < -2 || delta_delta.tv_sec >= 2) {
3dcad5ff0   John Stultz   rtc: Avoid accumu...
60
61
62
63
64
65
66
67
68
  		/*
  		 * if delta_delta is too large, assume time correction
  		 * has occured and set old_delta to the current delta.
  		 */
  		old_delta = delta;
  	} else {
  		/* Otherwise try to adjust old_system to compensate */
  		old_system = timespec_sub(old_system, delta_delta);
  	}
7ca1d488f   David Brownell   rtc: suspend()/re...
69

7ca1d488f   David Brownell   rtc: suspend()/re...
70
71
72
73
74
75
76
  	return 0;
  }
  
  static int rtc_resume(struct device *dev)
  {
  	struct rtc_device	*rtc = to_rtc_device(dev);
  	struct rtc_time		tm;
3dcad5ff0   John Stultz   rtc: Avoid accumu...
77
78
  	struct timespec		new_system, new_rtc;
  	struct timespec		sleep_time;
7ca1d488f   David Brownell   rtc: suspend()/re...
79

d4afc76c0   Kay Sievers   rtc: struct devic...
80
  	if (strcmp(dev_name(&rtc->dev), CONFIG_RTC_HCTOSYS_DEVICE) != 0)
7ca1d488f   David Brownell   rtc: suspend()/re...
81
  		return 0;
3dcad5ff0   John Stultz   rtc: Avoid accumu...
82
83
  	/* snapshot the current rtc and system time at resume */
  	getnstimeofday(&new_system);
7ca1d488f   David Brownell   rtc: suspend()/re...
84
85
  	rtc_read_time(rtc, &tm);
  	if (rtc_valid_tm(&tm) != 0) {
d4afc76c0   Kay Sievers   rtc: struct devic...
86
87
  		pr_debug("%s:  bogus resume time
  ", dev_name(&rtc->dev));
7ca1d488f   David Brownell   rtc: suspend()/re...
88
89
  		return 0;
  	}
3dcad5ff0   John Stultz   rtc: Avoid accumu...
90
91
  	rtc_tm_to_time(&tm, &new_rtc.tv_sec);
  	new_rtc.tv_nsec = 0;
6a8943d9e   Arve Hjønnevåg   rtc: Fix some bug...
92
93
94
  	if (new_rtc.tv_sec < old_rtc.tv_sec) {
  		pr_debug("%s:  time travel!
  ", dev_name(&rtc->dev));
7ca1d488f   David Brownell   rtc: suspend()/re...
95
96
  		return 0;
  	}
3dcad5ff0   John Stultz   rtc: Avoid accumu...
97
98
99
100
101
102
103
104
105
106
107
108
  	/* calculate the RTC time delta (sleep time)*/
  	sleep_time = timespec_sub(new_rtc, old_rtc);
  
  	/*
  	 * Since these RTC suspend/resume handlers are not called
  	 * at the very end of suspend or the start of resume,
  	 * some run-time may pass on either sides of the sleep time
  	 * so subtract kernel run-time between rtc_suspend to rtc_resume
  	 * to keep things accurate.
  	 */
  	sleep_time = timespec_sub(sleep_time,
  			timespec_sub(new_system, old_system));
7ca1d488f   David Brownell   rtc: suspend()/re...
109

6a8943d9e   Arve Hjønnevåg   rtc: Fix some bug...
110
111
  	if (sleep_time.tv_sec >= 0)
  		timekeeping_inject_sleeptime(&sleep_time);
7ca1d488f   David Brownell   rtc: suspend()/re...
112
113
114
115
116
117
118
  	return 0;
  }
  
  #else
  #define rtc_suspend	NULL
  #define rtc_resume	NULL
  #endif
0c86edc0d   Alessandro Zummo   [PATCH] RTC subsy...
119
120
121
122
123
124
125
126
127
128
  /**
   * rtc_device_register - register w/ RTC class
   * @dev: the device to register
   *
   * rtc_device_unregister() must be called when the class device is no
   * longer needed.
   *
   * Returns the pointer to the new struct class device.
   */
  struct rtc_device *rtc_device_register(const char *name, struct device *dev,
ff8371ac9   David Brownell   [PATCH] constify ...
129
  					const struct rtc_class_ops *ops,
0c86edc0d   Alessandro Zummo   [PATCH] RTC subsy...
130
131
132
  					struct module *owner)
  {
  	struct rtc_device *rtc;
f44f7f96a   John Stultz   RTC: Initialize k...
133
  	struct rtc_wkalrm alrm;
0c86edc0d   Alessandro Zummo   [PATCH] RTC subsy...
134
  	int id, err;
6d03d06db   Jonathan Cameron   drivers/rtc/class...
135
136
137
  	id = ida_simple_get(&rtc_ida, 0, 0, GFP_KERNEL);
  	if (id < 0) {
  		err = id;
0c86edc0d   Alessandro Zummo   [PATCH] RTC subsy...
138
139
  		goto exit;
  	}
0c86edc0d   Alessandro Zummo   [PATCH] RTC subsy...
140
141
142
  	rtc = kzalloc(sizeof(struct rtc_device), GFP_KERNEL);
  	if (rtc == NULL) {
  		err = -ENOMEM;
6d03d06db   Jonathan Cameron   drivers/rtc/class...
143
  		goto exit_ida;
0c86edc0d   Alessandro Zummo   [PATCH] RTC subsy...
144
145
146
147
148
  	}
  
  	rtc->id = id;
  	rtc->ops = ops;
  	rtc->owner = owner;
83a06bf50   Marcelo Roberto Jimenez   RTC: Prevents a d...
149
  	rtc->irq_freq = 1;
110d693d5   Alessandro Zummo   [PATCH] rtc subsy...
150
  	rtc->max_user_freq = 64;
cd9662094   David Brownell   rtc: remove rest ...
151
152
153
  	rtc->dev.parent = dev;
  	rtc->dev.class = rtc_class;
  	rtc->dev.release = rtc_device_release;
0c86edc0d   Alessandro Zummo   [PATCH] RTC subsy...
154
155
156
157
  
  	mutex_init(&rtc->ops_lock);
  	spin_lock_init(&rtc->irq_lock);
  	spin_lock_init(&rtc->irq_task_lock);
d691eb901   Alessandro Zummo   RTC: periodic irq...
158
  	init_waitqueue_head(&rtc->irq_queue);
0c86edc0d   Alessandro Zummo   [PATCH] RTC subsy...
159

6610e0893   John Stultz   RTC: Rework RTC c...
160
161
  	/* Init timerqueue */
  	timerqueue_init_head(&rtc->timerqueue);
96c8f06a0   Thomas Gleixner   rtc: Namespace fixup
162
  	INIT_WORK(&rtc->irqwork, rtc_timer_do_work);
6610e0893   John Stultz   RTC: Rework RTC c...
163
  	/* Init aie timer */
96c8f06a0   Thomas Gleixner   rtc: Namespace fixup
164
  	rtc_timer_init(&rtc->aie_timer, rtc_aie_update_irq, (void *)rtc);
6610e0893   John Stultz   RTC: Rework RTC c...
165
  	/* Init uie timer */
96c8f06a0   Thomas Gleixner   rtc: Namespace fixup
166
  	rtc_timer_init(&rtc->uie_rtctimer, rtc_uie_update_irq, (void *)rtc);
6610e0893   John Stultz   RTC: Rework RTC c...
167
168
169
170
  	/* Init pie timer */
  	hrtimer_init(&rtc->pie_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
  	rtc->pie_timer.function = rtc_pie_update_irq;
  	rtc->pie_enabled = 0;
f44f7f96a   John Stultz   RTC: Initialize k...
171
172
173
174
  	/* Check to see if there is an ALARM already set in hw */
  	err = __rtc_read_alarm(rtc, &alrm);
  
  	if (!err && !rtc_valid_tm(&alrm.time))
f6d5b3312   John Stultz   RTC: Fix early ir...
175
  		rtc_initialize_alarm(rtc, &alrm);
f44f7f96a   John Stultz   RTC: Initialize k...
176

0c86edc0d   Alessandro Zummo   [PATCH] RTC subsy...
177
  	strlcpy(rtc->name, name, RTC_DEVICE_NAME_SIZE);
d4afc76c0   Kay Sievers   rtc: struct devic...
178
  	dev_set_name(&rtc->dev, "rtc%d", id);
0c86edc0d   Alessandro Zummo   [PATCH] RTC subsy...
179

cb3a58d2a   David Brownell   rtc: update to cl...
180
  	rtc_dev_prepare(rtc);
cd9662094   David Brownell   rtc: remove rest ...
181
  	err = device_register(&rtc->dev);
59cca865f   Vasiliy Kulikov   drivers/rtc/class...
182
183
  	if (err) {
  		put_device(&rtc->dev);
0c86edc0d   Alessandro Zummo   [PATCH] RTC subsy...
184
  		goto exit_kfree;
59cca865f   Vasiliy Kulikov   drivers/rtc/class...
185
  	}
0c86edc0d   Alessandro Zummo   [PATCH] RTC subsy...
186

5726fb201   David Brownell   rtc: remove /sys/...
187
  	rtc_dev_add_device(rtc);
446ecbd92   David Brownell   rtc: simplified r...
188
  	rtc_sysfs_add_device(rtc);
7d9f99ecc   David Brownell   rtc: simplified /...
189
  	rtc_proc_add_device(rtc);
5726fb201   David Brownell   rtc: remove /sys/...
190

0c86edc0d   Alessandro Zummo   [PATCH] RTC subsy...
191
192
  	dev_info(dev, "rtc core: registered %s as %s
  ",
d4afc76c0   Kay Sievers   rtc: struct devic...
193
  			rtc->name, dev_name(&rtc->dev));
0c86edc0d   Alessandro Zummo   [PATCH] RTC subsy...
194
195
196
197
198
  
  	return rtc;
  
  exit_kfree:
  	kfree(rtc);
6d03d06db   Jonathan Cameron   drivers/rtc/class...
199
200
  exit_ida:
  	ida_simple_remove(&rtc_ida, id);
0c86edc0d   Alessandro Zummo   [PATCH] RTC subsy...
201
202
  
  exit:
d1d65b771   Alessandro Zummo   [PATCH] RTC subsy...
203
204
205
  	dev_err(dev, "rtc core: unable to register %s, err = %d
  ",
  			name, err);
0c86edc0d   Alessandro Zummo   [PATCH] RTC subsy...
206
207
208
209
210
211
212
213
214
215
216
217
  	return ERR_PTR(err);
  }
  EXPORT_SYMBOL_GPL(rtc_device_register);
  
  
  /**
   * rtc_device_unregister - removes the previously registered RTC class device
   *
   * @rtc: the RTC class device to destroy
   */
  void rtc_device_unregister(struct rtc_device *rtc)
  {
cd9662094   David Brownell   rtc: remove rest ...
218
  	if (get_device(&rtc->dev) != NULL) {
e109ebd1e   David Brownell   [PATCH] rtc_cmos ...
219
220
221
222
  		mutex_lock(&rtc->ops_lock);
  		/* remove innards of this RTC, then disable it, before
  		 * letting any rtc_class_open() users access it again
  		 */
446ecbd92   David Brownell   rtc: simplified r...
223
  		rtc_sysfs_del_device(rtc);
5726fb201   David Brownell   rtc: remove /sys/...
224
  		rtc_dev_del_device(rtc);
7d9f99ecc   David Brownell   rtc: simplified /...
225
  		rtc_proc_del_device(rtc);
cd9662094   David Brownell   rtc: remove rest ...
226
  		device_unregister(&rtc->dev);
e109ebd1e   David Brownell   [PATCH] rtc_cmos ...
227
228
  		rtc->ops = NULL;
  		mutex_unlock(&rtc->ops_lock);
cd9662094   David Brownell   rtc: remove rest ...
229
  		put_device(&rtc->dev);
e109ebd1e   David Brownell   [PATCH] rtc_cmos ...
230
  	}
0c86edc0d   Alessandro Zummo   [PATCH] RTC subsy...
231
232
  }
  EXPORT_SYMBOL_GPL(rtc_device_unregister);
0c86edc0d   Alessandro Zummo   [PATCH] RTC subsy...
233
234
235
236
237
238
239
240
  static int __init rtc_init(void)
  {
  	rtc_class = class_create(THIS_MODULE, "rtc");
  	if (IS_ERR(rtc_class)) {
  		printk(KERN_ERR "%s: couldn't create class
  ", __FILE__);
  		return PTR_ERR(rtc_class);
  	}
7ca1d488f   David Brownell   rtc: suspend()/re...
241
242
  	rtc_class->suspend = rtc_suspend;
  	rtc_class->resume = rtc_resume;
5726fb201   David Brownell   rtc: remove /sys/...
243
  	rtc_dev_init();
446ecbd92   David Brownell   rtc: simplified r...
244
  	rtc_sysfs_init(rtc_class);
0c86edc0d   Alessandro Zummo   [PATCH] RTC subsy...
245
246
247
248
249
  	return 0;
  }
  
  static void __exit rtc_exit(void)
  {
5726fb201   David Brownell   rtc: remove /sys/...
250
  	rtc_dev_exit();
0c86edc0d   Alessandro Zummo   [PATCH] RTC subsy...
251
  	class_destroy(rtc_class);
6d03d06db   Jonathan Cameron   drivers/rtc/class...
252
  	ida_destroy(&rtc_ida);
0c86edc0d   Alessandro Zummo   [PATCH] RTC subsy...
253
  }
818a8674b   David Brownell   [PATCH] RTC class...
254
  subsys_initcall(rtc_init);
0c86edc0d   Alessandro Zummo   [PATCH] RTC subsy...
255
  module_exit(rtc_exit);
818a8674b   David Brownell   [PATCH] RTC class...
256
  MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
0c86edc0d   Alessandro Zummo   [PATCH] RTC subsy...
257
258
  MODULE_DESCRIPTION("RTC class support");
  MODULE_LICENSE("GPL");