class.c 5.7 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 44
#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().
 */

static time_t		oldtime;
45
static struct timespec	oldts;
46 47 48 49 50 51

static int rtc_suspend(struct device *dev, pm_message_t mesg)
{
	struct rtc_device	*rtc = to_rtc_device(dev);
	struct rtc_time		tm;

52
	if (strcmp(dev_name(&rtc->dev), CONFIG_RTC_HCTOSYS_DEVICE) != 0)
53 54 55
		return 0;

	rtc_read_time(rtc, &tm);
56
	ktime_get_ts(&oldts);
57 58 59 60 61 62 63 64 65 66 67
	rtc_tm_to_time(&tm, &oldtime);

	return 0;
}

static int rtc_resume(struct device *dev)
{
	struct rtc_device	*rtc = to_rtc_device(dev);
	struct rtc_time		tm;
	time_t			newtime;
	struct timespec		time;
68
	struct timespec		newts;
69

70
	if (strcmp(dev_name(&rtc->dev), CONFIG_RTC_HCTOSYS_DEVICE) != 0)
71 72
		return 0;

73
	ktime_get_ts(&newts);
74 75
	rtc_read_time(rtc, &tm);
	if (rtc_valid_tm(&tm) != 0) {
76
		pr_debug("%s:  bogus resume time\n", dev_name(&rtc->dev));
77 78 79 80 81
		return 0;
	}
	rtc_tm_to_time(&tm, &newtime);
	if (newtime <= oldtime) {
		if (newtime < oldtime)
82
			pr_debug("%s:  time travel!\n", dev_name(&rtc->dev));
83 84
		return 0;
	}
85 86
	/* calculate the RTC time delta */
	set_normalized_timespec(&time, newtime - oldtime, 0);
87

88 89
	/* subtract kernel time between rtc_suspend to rtc_resume */
	time = timespec_sub(time, timespec_sub(newts, oldts));
90

91
	timekeeping_inject_sleeptime(&time);
92 93 94 95 96 97 98 99 100
	return 0;
}

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


A
Alessandro Zummo 已提交
101 102 103 104 105 106 107 108 109 110
/**
 * 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,
111
					const struct rtc_class_ops *ops,
A
Alessandro Zummo 已提交
112 113 114
					struct module *owner)
{
	struct rtc_device *rtc;
115
	struct rtc_wkalrm alrm;
A
Alessandro Zummo 已提交
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
	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;
142
	rtc->irq_freq = 1;
143
	rtc->max_user_freq = 64;
144 145 146
	rtc->dev.parent = dev;
	rtc->dev.class = rtc_class;
	rtc->dev.release = rtc_device_release;
A
Alessandro Zummo 已提交
147 148 149 150

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

153 154
	/* Init timerqueue */
	timerqueue_init_head(&rtc->timerqueue);
T
Thomas Gleixner 已提交
155
	INIT_WORK(&rtc->irqwork, rtc_timer_do_work);
156
	/* Init aie timer */
T
Thomas Gleixner 已提交
157
	rtc_timer_init(&rtc->aie_timer, rtc_aie_update_irq, (void *)rtc);
158
	/* Init uie timer */
T
Thomas Gleixner 已提交
159
	rtc_timer_init(&rtc->uie_rtctimer, rtc_uie_update_irq, (void *)rtc);
160 161 162 163 164
	/* 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;

165 166 167 168
	/* 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))
169
		rtc_initialize_alarm(rtc, &alrm);
170

A
Alessandro Zummo 已提交
171
	strlcpy(rtc->name, name, RTC_DEVICE_NAME_SIZE);
172
	dev_set_name(&rtc->dev, "rtc%d", id);
A
Alessandro Zummo 已提交
173

174 175
	rtc_dev_prepare(rtc);

176
	err = device_register(&rtc->dev);
177 178
	if (err) {
		put_device(&rtc->dev);
A
Alessandro Zummo 已提交
179
		goto exit_kfree;
180
	}
A
Alessandro Zummo 已提交
181

182
	rtc_dev_add_device(rtc);
183
	rtc_sysfs_add_device(rtc);
184
	rtc_proc_add_device(rtc);
185

A
Alessandro Zummo 已提交
186
	dev_info(dev, "rtc core: registered %s as %s\n",
187
			rtc->name, dev_name(&rtc->dev));
A
Alessandro Zummo 已提交
188 189 190 191 192 193 194

	return rtc;

exit_kfree:
	kfree(rtc);

exit_idr:
S
Sonny Rao 已提交
195
	mutex_lock(&idr_lock);
A
Alessandro Zummo 已提交
196
	idr_remove(&rtc_idr, id);
S
Sonny Rao 已提交
197
	mutex_unlock(&idr_lock);
A
Alessandro Zummo 已提交
198 199

exit:
200 201
	dev_err(dev, "rtc core: unable to register %s, err = %d\n",
			name, err);
A
Alessandro Zummo 已提交
202 203 204 205 206 207 208 209 210 211 212 213
	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)
{
214
	if (get_device(&rtc->dev) != NULL) {
D
David Brownell 已提交
215 216 217 218
		mutex_lock(&rtc->ops_lock);
		/* remove innards of this RTC, then disable it, before
		 * letting any rtc_class_open() users access it again
		 */
219
		rtc_sysfs_del_device(rtc);
220
		rtc_dev_del_device(rtc);
221
		rtc_proc_del_device(rtc);
222
		device_unregister(&rtc->dev);
D
David Brownell 已提交
223 224
		rtc->ops = NULL;
		mutex_unlock(&rtc->ops_lock);
225
		put_device(&rtc->dev);
D
David Brownell 已提交
226
	}
A
Alessandro Zummo 已提交
227 228 229 230 231 232 233 234 235 236
}
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);
	}
237 238
	rtc_class->suspend = rtc_suspend;
	rtc_class->resume = rtc_resume;
239
	rtc_dev_init();
240
	rtc_sysfs_init(rtc_class);
A
Alessandro Zummo 已提交
241 242 243 244 245
	return 0;
}

static void __exit rtc_exit(void)
{
246
	rtc_dev_exit();
A
Alessandro Zummo 已提交
247
	class_destroy(rtc_class);
A
Aaro Koskinen 已提交
248
	idr_destroy(&rtc_idr);
A
Alessandro Zummo 已提交
249 250
}

251
subsys_initcall(rtc_init);
A
Alessandro Zummo 已提交
252 253
module_exit(rtc_exit);

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