class.c 6.8 KB
Newer Older
A
Alessandro Zummo 已提交
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>
18
#include <linux/slab.h>
19
#include <linux/workqueue.h>
A
Alessandro Zummo 已提交
20

21 22 23
#include "rtc-core.h"


A
Alessandro Zummo 已提交
24 25 26 27
static DEFINE_IDR(rtc_idr);
static DEFINE_MUTEX(idr_lock);
struct class *rtc_class;

28
static void rtc_device_release(struct device *dev)
A
Alessandro Zummo 已提交
29
{
30
	struct rtc_device *rtc = to_rtc_device(dev);
A
Alessandro Zummo 已提交
31 32 33 34 35 36
	mutex_lock(&idr_lock);
	idr_remove(&rtc_idr, rtc->id);
	mutex_unlock(&idr_lock);
	kfree(rtc);
}

37 38 39 40 41 42 43
#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().
 */

44 45
static struct timespec old_rtc, old_system, old_delta;

46 47 48 49 50

static int rtc_suspend(struct device *dev, pm_message_t mesg)
{
	struct rtc_device	*rtc = to_rtc_device(dev);
	struct rtc_time		tm;
51
	struct timespec		delta, delta_delta;
52
	if (strcmp(dev_name(&rtc->dev), CONFIG_RTC_HCTOSYS_DEVICE) != 0)
53 54
		return 0;

55
	/* snapshot the current RTC and system time at suspend*/
56
	rtc_read_time(rtc, &tm);
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
	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);
	if (abs(delta_delta.tv_sec)  >= 2) {
		/*
		 * 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);
	}
79 80 81 82 83 84 85 86

	return 0;
}

static int rtc_resume(struct device *dev)
{
	struct rtc_device	*rtc = to_rtc_device(dev);
	struct rtc_time		tm;
87 88
	struct timespec		new_system, new_rtc;
	struct timespec		sleep_time;
89

90
	if (strcmp(dev_name(&rtc->dev), CONFIG_RTC_HCTOSYS_DEVICE) != 0)
91 92
		return 0;

93 94
	/* snapshot the current rtc and system time at resume */
	getnstimeofday(&new_system);
95 96
	rtc_read_time(rtc, &tm);
	if (rtc_valid_tm(&tm) != 0) {
97
		pr_debug("%s:  bogus resume time\n", dev_name(&rtc->dev));
98 99
		return 0;
	}
100 101 102 103 104
	rtc_tm_to_time(&tm, &new_rtc.tv_sec);
	new_rtc.tv_nsec = 0;

	if (new_rtc.tv_sec <= old_rtc.tv_sec) {
		if (new_rtc.tv_sec < old_rtc.tv_sec)
105
			pr_debug("%s:  time travel!\n", dev_name(&rtc->dev));
106 107 108
		return 0;
	}

109 110 111 112 113 114 115 116 117 118 119 120
	/* 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));
121

122
	timekeeping_inject_sleeptime(&sleep_time);
123 124 125 126 127 128 129 130 131
	return 0;
}

#else
#define rtc_suspend	NULL
#define rtc_resume	NULL
#endif


A
Alessandro Zummo 已提交
132 133 134 135 136 137 138 139 140 141
/**
 * 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,
142
					const struct rtc_class_ops *ops,
A
Alessandro Zummo 已提交
143 144 145
					struct module *owner)
{
	struct rtc_device *rtc;
146
	struct rtc_wkalrm alrm;
A
Alessandro Zummo 已提交
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
	int id, err;

	if (idr_pre_get(&rtc_idr, GFP_KERNEL) == 0) {
		err = -ENOMEM;
		goto exit;
	}


	mutex_lock(&idr_lock);
	err = idr_get_new(&rtc_idr, NULL, &id);
	mutex_unlock(&idr_lock);

	if (err < 0)
		goto exit;

	id = id & MAX_ID_MASK;

	rtc = kzalloc(sizeof(struct rtc_device), GFP_KERNEL);
	if (rtc == NULL) {
		err = -ENOMEM;
		goto exit_idr;
	}

	rtc->id = id;
	rtc->ops = ops;
	rtc->owner = owner;
173
	rtc->irq_freq = 1;
174
	rtc->max_user_freq = 64;
175 176 177
	rtc->dev.parent = dev;
	rtc->dev.class = rtc_class;
	rtc->dev.release = rtc_device_release;
A
Alessandro Zummo 已提交
178 179 180 181

	mutex_init(&rtc->ops_lock);
	spin_lock_init(&rtc->irq_lock);
	spin_lock_init(&rtc->irq_task_lock);
A
Alessandro Zummo 已提交
182
	init_waitqueue_head(&rtc->irq_queue);
A
Alessandro Zummo 已提交
183

184 185
	/* Init timerqueue */
	timerqueue_init_head(&rtc->timerqueue);
T
Thomas Gleixner 已提交
186
	INIT_WORK(&rtc->irqwork, rtc_timer_do_work);
187
	/* Init aie timer */
T
Thomas Gleixner 已提交
188
	rtc_timer_init(&rtc->aie_timer, rtc_aie_update_irq, (void *)rtc);
189
	/* Init uie timer */
T
Thomas Gleixner 已提交
190
	rtc_timer_init(&rtc->uie_rtctimer, rtc_uie_update_irq, (void *)rtc);
191 192 193 194 195
	/* 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;

196 197 198 199
	/* 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))
200
		rtc_initialize_alarm(rtc, &alrm);
201

A
Alessandro Zummo 已提交
202
	strlcpy(rtc->name, name, RTC_DEVICE_NAME_SIZE);
203
	dev_set_name(&rtc->dev, "rtc%d", id);
A
Alessandro Zummo 已提交
204

205 206
	rtc_dev_prepare(rtc);

207
	err = device_register(&rtc->dev);
208 209
	if (err) {
		put_device(&rtc->dev);
A
Alessandro Zummo 已提交
210
		goto exit_kfree;
211
	}
A
Alessandro Zummo 已提交
212

213
	rtc_dev_add_device(rtc);
214
	rtc_sysfs_add_device(rtc);
215
	rtc_proc_add_device(rtc);
216

A
Alessandro Zummo 已提交
217
	dev_info(dev, "rtc core: registered %s as %s\n",
218
			rtc->name, dev_name(&rtc->dev));
A
Alessandro Zummo 已提交
219 220 221 222 223 224 225

	return rtc;

exit_kfree:
	kfree(rtc);

exit_idr:
S
Sonny Rao 已提交
226
	mutex_lock(&idr_lock);
A
Alessandro Zummo 已提交
227
	idr_remove(&rtc_idr, id);
S
Sonny Rao 已提交
228
	mutex_unlock(&idr_lock);
A
Alessandro Zummo 已提交
229 230

exit:
231 232
	dev_err(dev, "rtc core: unable to register %s, err = %d\n",
			name, err);
A
Alessandro Zummo 已提交
233 234 235 236 237 238 239 240 241 242 243 244
	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)
{
245
	if (get_device(&rtc->dev) != NULL) {
D
David Brownell 已提交
246 247 248 249
		mutex_lock(&rtc->ops_lock);
		/* remove innards of this RTC, then disable it, before
		 * letting any rtc_class_open() users access it again
		 */
250
		rtc_sysfs_del_device(rtc);
251
		rtc_dev_del_device(rtc);
252
		rtc_proc_del_device(rtc);
253
		device_unregister(&rtc->dev);
D
David Brownell 已提交
254 255
		rtc->ops = NULL;
		mutex_unlock(&rtc->ops_lock);
256
		put_device(&rtc->dev);
D
David Brownell 已提交
257
	}
A
Alessandro Zummo 已提交
258 259 260 261 262 263 264 265 266 267
}
EXPORT_SYMBOL_GPL(rtc_device_unregister);

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\n", __FILE__);
		return PTR_ERR(rtc_class);
	}
268 269
	rtc_class->suspend = rtc_suspend;
	rtc_class->resume = rtc_resume;
270
	rtc_dev_init();
271
	rtc_sysfs_init(rtc_class);
A
Alessandro Zummo 已提交
272 273 274 275 276
	return 0;
}

static void __exit rtc_exit(void)
{
277
	rtc_dev_exit();
A
Alessandro Zummo 已提交
278
	class_destroy(rtc_class);
A
Aaro Koskinen 已提交
279
	idr_destroy(&rtc_idr);
A
Alessandro Zummo 已提交
280 281
}

282
subsys_initcall(rtc_init);
A
Alessandro Zummo 已提交
283 284
module_exit(rtc_exit);

285
MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
A
Alessandro Zummo 已提交
286 287
MODULE_DESCRIPTION("RTC class support");
MODULE_LICENSE("GPL");