rtc.c 6.15 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
/*
2
 * RTC related functions
Linus Torvalds's avatar
Linus Torvalds committed
3
 */
Thomas Gleixner's avatar
Thomas Gleixner committed
4
#include <linux/acpi.h>
5
#include <linux/bcd.h>
Linus Torvalds's avatar
Linus Torvalds committed
6
#include <linux/mc146818rtc.h>
7
8
#include <linux/platform_device.h>
#include <linux/pnp.h>
Linus Torvalds's avatar
Linus Torvalds committed
9

10
#include <asm/time.h>
11
#include <asm/vsyscall.h>
Linus Torvalds's avatar
Linus Torvalds committed
12

Thomas Gleixner's avatar
Thomas Gleixner committed
13
14
15
16
17
18
19
20
21
22
#ifdef CONFIG_X86_32
/*
 * This is a special lock that is owned by the CPU and holds the index
 * register we are working with.  It is required for NMI access to the
 * CMOS/RTC registers.  See include/asm-i386/mc146818rtc.h for details.
 */
volatile unsigned long cmos_lock = 0;
EXPORT_SYMBOL(cmos_lock);
#endif

23
24
25
/* For two digit years assume time is always after that */
#define CMOS_YEARS_OFFS 2000

Thomas Gleixner's avatar
Thomas Gleixner committed
26
27
28
DEFINE_SPINLOCK(rtc_lock);
EXPORT_SYMBOL(rtc_lock);

Linus Torvalds's avatar
Linus Torvalds committed
29
30
31
32
33
34
35
36
37
38
/*
 * In order to set the CMOS clock precisely, set_rtc_mmss has to be
 * called 500 ms after the second nowtime has started, because when
 * nowtime is written into the registers of the CMOS clock, it will
 * jump to the next second precisely 500 ms later. Check the Motorola
 * MC146818A or Dallas DS12887 data sheet for details.
 *
 * BUG: This routine does not handle hour overflow properly; it just
 *      sets the minutes. Usually you'll only notice that after reboot!
 */
39
int mach_set_rtc_mmss(unsigned long nowtime)
Linus Torvalds's avatar
Linus Torvalds committed
40
41
42
43
44
{
	int retval = 0;
	int real_seconds, real_minutes, cmos_minutes;
	unsigned char save_control, save_freq_select;

Thomas Gleixner's avatar
Thomas Gleixner committed
45
46
	 /* tell the clock it's being set */
	save_control = CMOS_READ(RTC_CONTROL);
Linus Torvalds's avatar
Linus Torvalds committed
47
48
	CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL);

Thomas Gleixner's avatar
Thomas Gleixner committed
49
50
	/* stop and reset prescaler */
	save_freq_select = CMOS_READ(RTC_FREQ_SELECT);
Linus Torvalds's avatar
Linus Torvalds committed
51
52
53
54
55
56
57
58
59
60
61
62
63
64
	CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT);

	cmos_minutes = CMOS_READ(RTC_MINUTES);
	if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
		BCD_TO_BIN(cmos_minutes);

	/*
	 * since we're only adjusting minutes and seconds,
	 * don't interfere with hour overflow. This avoids
	 * messing with unknown time zones but requires your
	 * RTC not to be off by more than 15 minutes
	 */
	real_seconds = nowtime % 60;
	real_minutes = nowtime / 60;
Thomas Gleixner's avatar
Thomas Gleixner committed
65
	/* correct for half hour time zone */
Linus Torvalds's avatar
Linus Torvalds committed
66
	if (((abs(real_minutes - cmos_minutes) + 15)/30) & 1)
Thomas Gleixner's avatar
Thomas Gleixner committed
67
		real_minutes += 30;
Linus Torvalds's avatar
Linus Torvalds committed
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
	real_minutes %= 60;

	if (abs(real_minutes - cmos_minutes) < 30) {
		if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
			BIN_TO_BCD(real_seconds);
			BIN_TO_BCD(real_minutes);
		}
		CMOS_WRITE(real_seconds,RTC_SECONDS);
		CMOS_WRITE(real_minutes,RTC_MINUTES);
	} else {
		printk(KERN_WARNING
		       "set_rtc_mmss: can't update from %d to %d\n",
		       cmos_minutes, real_minutes);
		retval = -1;
	}

	/* The following flags have to be released exactly in this order,
	 * otherwise the DS12887 (popular MC146818A clone with integrated
	 * battery and quartz) will not reset the oscillator and will not
	 * update precisely 500 ms later. You won't find this mentioned in
	 * the Dallas Semiconductor data sheets, but who believes data
	 * sheets anyway ...                           -- Markus Kuhn
	 */
	CMOS_WRITE(save_control, RTC_CONTROL);
	CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT);

	return retval;
}

97
unsigned long mach_get_cmos_time(void)
Linus Torvalds's avatar
Linus Torvalds committed
98
{
99
	unsigned int status, year, mon, day, hour, min, sec, century = 0;
Thomas Gleixner's avatar
Thomas Gleixner committed
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116

	/*
	 * If UIP is clear, then we have >= 244 microseconds before
	 * RTC registers will be updated.  Spec sheet says that this
	 * is the reliable way to read RTC - registers. If UIP is set
	 * then the register access might be invalid.
	 */
	while ((CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP))
		cpu_relax();

	sec = CMOS_READ(RTC_SECONDS);
	min = CMOS_READ(RTC_MINUTES);
	hour = CMOS_READ(RTC_HOURS);
	day = CMOS_READ(RTC_DAY_OF_MONTH);
	mon = CMOS_READ(RTC_MONTH);
	year = CMOS_READ(RTC_YEAR);

117
#ifdef CONFIG_ACPI
Thomas Gleixner's avatar
Thomas Gleixner committed
118
119
120
121
122
	if (acpi_gbl_FADT.header.revision >= FADT2_REVISION_ID &&
	    acpi_gbl_FADT.century)
		century = CMOS_READ(acpi_gbl_FADT.century);
#endif

123
	status = CMOS_READ(RTC_CONTROL);
124
	WARN_ON_ONCE(RTC_ALWAYS_BCD && (status & RTC_DM_BINARY));
125
126

	if (RTC_ALWAYS_BCD || !(status & RTC_DM_BINARY)) {
127
128
129
130
131
132
133
134
		BCD_TO_BIN(sec);
		BCD_TO_BIN(min);
		BCD_TO_BIN(hour);
		BCD_TO_BIN(day);
		BCD_TO_BIN(mon);
		BCD_TO_BIN(year);
	}

Thomas Gleixner's avatar
Thomas Gleixner committed
135
136
137
138
	if (century) {
		BCD_TO_BIN(century);
		year += century * 100;
		printk(KERN_INFO "Extended CMOS year: %d\n", century * 100);
139
	} else
Thomas Gleixner's avatar
Thomas Gleixner committed
140
		year += CMOS_YEARS_OFFS;
Linus Torvalds's avatar
Linus Torvalds committed
141
142
143
144

	return mktime(year, mon, day, hour, min, sec);
}

145
146
147
148
149
150
/* Routines for accessing the CMOS RAM/RTC. */
unsigned char rtc_cmos_read(unsigned char addr)
{
	unsigned char val;

	lock_cmos_prefix(addr);
151
152
	outb(addr, RTC_PORT(0));
	val = inb(RTC_PORT(1));
153
154
155
156
157
158
159
160
	lock_cmos_suffix(addr);
	return val;
}
EXPORT_SYMBOL(rtc_cmos_read);

void rtc_cmos_write(unsigned char val, unsigned char addr)
{
	lock_cmos_prefix(addr);
161
162
	outb(addr, RTC_PORT(0));
	outb(val, RTC_PORT(1));
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
	lock_cmos_suffix(addr);
}
EXPORT_SYMBOL(rtc_cmos_write);

static int set_rtc_mmss(unsigned long nowtime)
{
	int retval;
	unsigned long flags;

	spin_lock_irqsave(&rtc_lock, flags);
	retval = set_wallclock(nowtime);
	spin_unlock_irqrestore(&rtc_lock, flags);

	return retval;
}

/* not static: needed by APM */
unsigned long read_persistent_clock(void)
{
Thomas Gleixner's avatar
Thomas Gleixner committed
182
	unsigned long retval, flags;
183
184
185
186
187
188
189
190
191
192
193
194

	spin_lock_irqsave(&rtc_lock, flags);
	retval = get_wallclock();
	spin_unlock_irqrestore(&rtc_lock, flags);

	return retval;
}

int update_persistent_clock(struct timespec now)
{
	return set_rtc_mmss(now.tv_sec);
}
195

Ingo Molnar's avatar
Ingo Molnar committed
196
unsigned long long native_read_tsc(void)
197
{
Ingo Molnar's avatar
Ingo Molnar committed
198
	return __native_read_tsc();
199
}
Ingo Molnar's avatar
Ingo Molnar committed
200
201
EXPORT_SYMBOL(native_read_tsc);

202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225

static struct resource rtc_resources[] = {
	[0] = {
		.start	= RTC_PORT(0),
		.end	= RTC_PORT(1),
		.flags	= IORESOURCE_IO,
	},
	[1] = {
		.start	= RTC_IRQ,
		.end	= RTC_IRQ,
		.flags	= IORESOURCE_IRQ,
	}
};

static struct platform_device rtc_device = {
	.name		= "rtc_cmos",
	.id		= -1,
	.resource	= rtc_resources,
	.num_resources	= ARRAY_SIZE(rtc_resources),
};

static __init int add_rtc_cmos(void)
{
#ifdef CONFIG_PNP
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
	static const char *ids[] __initconst =
	    { "PNP0b00", "PNP0b01", "PNP0b02", };
	struct pnp_dev *dev;
	struct pnp_id *id;
	int i;

	pnp_for_each_dev(dev) {
		for (id = dev->id; id; id = id->next) {
			for (i = 0; i < ARRAY_SIZE(ids); i++) {
				if (compare_pnp_id(id, ids[i]) != 0)
					return 0;
			}
		}
	}
#endif

242
	platform_device_register(&rtc_device);
243
244
	dev_info(&rtc_device.dev,
		 "registered platform RTC device (no PNP device found)\n");
245
246
247
	return 0;
}
device_initcall(add_rtc_cmos);