Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * PCF8563 RTC | |
3 | * | |
4 | * From Phillips' datasheet: | |
5 | * | |
6 | * The PCF8563 is a CMOS real-time clock/calendar optimized for low power | |
3a4fa0a2 | 7 | * consumption. A programmable clock output, interrupt output and voltage |
1da177e4 LT |
8 | * low detector are also provided. All address and data are transferred |
9 | * serially via two-line bidirectional I2C-bus. Maximum bus speed is | |
10 | * 400 kbits/s. The built-in word address register is incremented | |
34a8e501 | 11 | * automatically after each written or read byte. |
1da177e4 | 12 | * |
34a8e501 | 13 | * Copyright (c) 2002-2007, Axis Communications AB |
1da177e4 LT |
14 | * All rights reserved. |
15 | * | |
16 | * Author: Tobias Anderberg <tobiasa@axis.com>. | |
17 | * | |
1da177e4 LT |
18 | */ |
19 | ||
1da177e4 LT |
20 | #include <linux/module.h> |
21 | #include <linux/kernel.h> | |
22 | #include <linux/types.h> | |
23 | #include <linux/sched.h> | |
24 | #include <linux/init.h> | |
25 | #include <linux/fs.h> | |
26 | #include <linux/ioctl.h> | |
27 | #include <linux/delay.h> | |
28 | #include <linux/bcd.h> | |
34a8e501 | 29 | #include <linux/mutex.h> |
1da177e4 LT |
30 | |
31 | #include <asm/uaccess.h> | |
1da177e4 | 32 | #include <asm/io.h> |
1da177e4 | 33 | #include <asm/rtc.h> |
34a8e501 | 34 | |
1da177e4 LT |
35 | #include "i2c.h" |
36 | ||
34a8e501 JN |
37 | #define PCF8563_MAJOR 121 /* Local major number. */ |
38 | #define DEVICE_NAME "rtc" /* Name which is registered in /proc/devices. */ | |
1da177e4 | 39 | #define PCF8563_NAME "PCF8563" |
34a8e501 | 40 | #define DRIVER_VERSION "$Revision: 1.24 $" |
1da177e4 LT |
41 | |
42 | /* I2C bus slave registers. */ | |
43 | #define RTC_I2C_READ 0xa3 | |
44 | #define RTC_I2C_WRITE 0xa2 | |
45 | ||
46 | /* Two simple wrapper macros, saves a few keystrokes. */ | |
47 | #define rtc_read(x) i2c_readreg(RTC_I2C_READ, x) | |
48 | #define rtc_write(x,y) i2c_writereg(RTC_I2C_WRITE, x, y) | |
7e920426 | 49 | |
0890b588 | 50 | static DEFINE_MUTEX(pcf8563_mutex); |
34a8e501 JN |
51 | static DEFINE_MUTEX(rtc_lock); /* Protect state etc */ |
52 | ||
1da177e4 LT |
53 | static const unsigned char days_in_month[] = |
54 | { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; | |
55 | ||
f35d7764 | 56 | static long pcf8563_unlocked_ioctl(struct file *, unsigned int, unsigned long); |
1da177e4 | 57 | |
34a8e501 JN |
58 | /* Cache VL bit value read at driver init since writing the RTC_SECOND |
59 | * register clears the VL status. | |
60 | */ | |
61 | static int voltage_low; | |
62 | ||
5dfe4c96 | 63 | static const struct file_operations pcf8563_fops = { |
1da177e4 | 64 | .owner = THIS_MODULE, |
f35d7764 | 65 | .unlocked_ioctl = pcf8563_unlocked_ioctl, |
6038f373 | 66 | .llseek = noop_llseek, |
1da177e4 LT |
67 | }; |
68 | ||
69 | unsigned char | |
34a8e501 | 70 | pcf8563_readreg(int reg) |
1da177e4 | 71 | { |
34a8e501 JN |
72 | unsigned char res = rtc_read(reg); |
73 | ||
74 | /* The PCF8563 does not return 0 for unimplemented bits. */ | |
75 | switch (reg) { | |
76 | case RTC_SECONDS: | |
77 | case RTC_MINUTES: | |
78 | res &= 0x7F; | |
79 | break; | |
80 | case RTC_HOURS: | |
81 | case RTC_DAY_OF_MONTH: | |
82 | res &= 0x3F; | |
83 | break; | |
84 | case RTC_WEEKDAY: | |
85 | res &= 0x07; | |
86 | break; | |
87 | case RTC_MONTH: | |
88 | res &= 0x1F; | |
89 | break; | |
90 | case RTC_CONTROL1: | |
91 | res &= 0xA8; | |
92 | break; | |
93 | case RTC_CONTROL2: | |
94 | res &= 0x1F; | |
95 | break; | |
96 | case RTC_CLOCKOUT_FREQ: | |
97 | case RTC_TIMER_CONTROL: | |
98 | res &= 0x83; | |
99 | break; | |
1da177e4 LT |
100 | } |
101 | return res; | |
102 | } | |
103 | ||
104 | void | |
34a8e501 | 105 | pcf8563_writereg(int reg, unsigned char val) |
1da177e4 | 106 | { |
1da177e4 LT |
107 | rtc_write(reg, val); |
108 | } | |
109 | ||
110 | void | |
111 | get_rtc_time(struct rtc_time *tm) | |
112 | { | |
34a8e501 JN |
113 | tm->tm_sec = rtc_read(RTC_SECONDS); |
114 | tm->tm_min = rtc_read(RTC_MINUTES); | |
1da177e4 LT |
115 | tm->tm_hour = rtc_read(RTC_HOURS); |
116 | tm->tm_mday = rtc_read(RTC_DAY_OF_MONTH); | |
34a8e501 JN |
117 | tm->tm_wday = rtc_read(RTC_WEEKDAY); |
118 | tm->tm_mon = rtc_read(RTC_MONTH); | |
1da177e4 LT |
119 | tm->tm_year = rtc_read(RTC_YEAR); |
120 | ||
34a8e501 JN |
121 | if (tm->tm_sec & 0x80) { |
122 | printk(KERN_ERR "%s: RTC Voltage Low - reliable date/time " | |
123 | "information is no longer guaranteed!\n", PCF8563_NAME); | |
124 | } | |
1da177e4 | 125 | |
4110a0d6 | 126 | tm->tm_year = bcd2bin(tm->tm_year) + |
34a8e501 JN |
127 | ((tm->tm_mon & 0x80) ? 100 : 0); |
128 | tm->tm_sec &= 0x7F; | |
129 | tm->tm_min &= 0x7F; | |
130 | tm->tm_hour &= 0x3F; | |
131 | tm->tm_mday &= 0x3F; | |
132 | tm->tm_wday &= 0x07; /* Not coded in BCD. */ | |
133 | tm->tm_mon &= 0x1F; | |
1da177e4 | 134 | |
4110a0d6 AB |
135 | tm->tm_sec = bcd2bin(tm->tm_sec); |
136 | tm->tm_min = bcd2bin(tm->tm_min); | |
137 | tm->tm_hour = bcd2bin(tm->tm_hour); | |
138 | tm->tm_mday = bcd2bin(tm->tm_mday); | |
139 | tm->tm_mon = bcd2bin(tm->tm_mon); | |
1da177e4 LT |
140 | tm->tm_mon--; /* Month is 1..12 in RTC but 0..11 in linux */ |
141 | } | |
142 | ||
143 | int __init | |
144 | pcf8563_init(void) | |
145 | { | |
34a8e501 JN |
146 | static int res; |
147 | static int first = 1; | |
148 | ||
149 | if (!first) | |
150 | return res; | |
151 | first = 0; | |
152 | ||
153 | /* Initiate the i2c protocol. */ | |
154 | res = i2c_init(); | |
155 | if (res < 0) { | |
156 | printk(KERN_CRIT "pcf8563_init: Failed to init i2c.\n"); | |
157 | return res; | |
7e920426 | 158 | } |
1da177e4 LT |
159 | |
160 | /* | |
161 | * First of all we need to reset the chip. This is done by | |
34a8e501 JN |
162 | * clearing control1, control2 and clk freq and resetting |
163 | * all alarms. | |
1da177e4 LT |
164 | */ |
165 | if (rtc_write(RTC_CONTROL1, 0x00) < 0) | |
166 | goto err; | |
167 | ||
168 | if (rtc_write(RTC_CONTROL2, 0x00) < 0) | |
169 | goto err; | |
170 | ||
171 | if (rtc_write(RTC_CLOCKOUT_FREQ, 0x00) < 0) | |
172 | goto err; | |
173 | ||
34a8e501 | 174 | if (rtc_write(RTC_TIMER_CONTROL, 0x03) < 0) |
1da177e4 | 175 | goto err; |
34a8e501 | 176 | |
1da177e4 | 177 | /* Reset the alarms. */ |
34a8e501 | 178 | if (rtc_write(RTC_MINUTE_ALARM, 0x80) < 0) |
1da177e4 | 179 | goto err; |
34a8e501 JN |
180 | |
181 | if (rtc_write(RTC_HOUR_ALARM, 0x80) < 0) | |
1da177e4 | 182 | goto err; |
34a8e501 JN |
183 | |
184 | if (rtc_write(RTC_DAY_ALARM, 0x80) < 0) | |
1da177e4 | 185 | goto err; |
34a8e501 JN |
186 | |
187 | if (rtc_write(RTC_WEEKDAY_ALARM, 0x80) < 0) | |
1da177e4 | 188 | goto err; |
34a8e501 JN |
189 | |
190 | /* Check for low voltage, and warn about it. */ | |
191 | if (rtc_read(RTC_SECONDS) & 0x80) { | |
192 | voltage_low = 1; | |
193 | printk(KERN_WARNING "%s: RTC Voltage Low - reliable " | |
194 | "date/time information is no longer guaranteed!\n", | |
195 | PCF8563_NAME); | |
196 | } | |
197 | ||
198 | return res; | |
1da177e4 LT |
199 | |
200 | err: | |
201 | printk(KERN_INFO "%s: Error initializing chip.\n", PCF8563_NAME); | |
34a8e501 JN |
202 | res = -1; |
203 | return res; | |
1da177e4 LT |
204 | } |
205 | ||
206 | void __exit | |
207 | pcf8563_exit(void) | |
208 | { | |
68fc4fab | 209 | unregister_chrdev(PCF8563_MAJOR, DEVICE_NAME); |
1da177e4 LT |
210 | } |
211 | ||
212 | /* | |
213 | * ioctl calls for this driver. Why return -ENOTTY upon error? Because | |
214 | * POSIX says so! | |
215 | */ | |
f35d7764 | 216 | static int pcf8563_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) |
1da177e4 LT |
217 | { |
218 | /* Some sanity checks. */ | |
219 | if (_IOC_TYPE(cmd) != RTC_MAGIC) | |
220 | return -ENOTTY; | |
221 | ||
222 | if (_IOC_NR(cmd) > RTC_MAX_IOCTL) | |
223 | return -ENOTTY; | |
224 | ||
225 | switch (cmd) { | |
34a8e501 JN |
226 | case RTC_RD_TIME: |
227 | { | |
228 | struct rtc_time tm; | |
1da177e4 | 229 | |
34a8e501 JN |
230 | mutex_lock(&rtc_lock); |
231 | memset(&tm, 0, sizeof tm); | |
232 | get_rtc_time(&tm); | |
1da177e4 | 233 | |
34a8e501 JN |
234 | if (copy_to_user((struct rtc_time *) arg, &tm, |
235 | sizeof tm)) { | |
9be48a94 | 236 | mutex_unlock(&rtc_lock); |
34a8e501 JN |
237 | return -EFAULT; |
238 | } | |
239 | ||
240 | mutex_unlock(&rtc_lock); | |
1da177e4 | 241 | |
34a8e501 JN |
242 | return 0; |
243 | } | |
244 | case RTC_SET_TIME: | |
245 | { | |
246 | int leap; | |
247 | int year; | |
248 | int century; | |
249 | struct rtc_time tm; | |
250 | ||
251 | memset(&tm, 0, sizeof tm); | |
252 | if (!capable(CAP_SYS_TIME)) | |
253 | return -EPERM; | |
254 | ||
255 | if (copy_from_user(&tm, (struct rtc_time *) arg, sizeof tm)) | |
256 | return -EFAULT; | |
257 | ||
258 | /* Convert from struct tm to struct rtc_time. */ | |
259 | tm.tm_year += 1900; | |
260 | tm.tm_mon += 1; | |
261 | ||
262 | /* | |
263 | * Check if tm.tm_year is a leap year. A year is a leap | |
264 | * year if it is divisible by 4 but not 100, except | |
265 | * that years divisible by 400 _are_ leap years. | |
266 | */ | |
267 | year = tm.tm_year; | |
268 | leap = (tm.tm_mon == 2) && | |
269 | ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0); | |
270 | ||
271 | /* Perform some sanity checks. */ | |
272 | if ((tm.tm_year < 1970) || | |
273 | (tm.tm_mon > 12) || | |
274 | (tm.tm_mday == 0) || | |
275 | (tm.tm_mday > days_in_month[tm.tm_mon] + leap) || | |
276 | (tm.tm_wday >= 7) || | |
277 | (tm.tm_hour >= 24) || | |
278 | (tm.tm_min >= 60) || | |
279 | (tm.tm_sec >= 60)) | |
280 | return -EINVAL; | |
281 | ||
282 | century = (tm.tm_year >= 2000) ? 0x80 : 0; | |
283 | tm.tm_year = tm.tm_year % 100; | |
284 | ||
4110a0d6 AB |
285 | tm.tm_year = bin2bcd(tm.tm_year); |
286 | tm.tm_mon = bin2bcd(tm.tm_mon); | |
287 | tm.tm_mday = bin2bcd(tm.tm_mday); | |
288 | tm.tm_hour = bin2bcd(tm.tm_hour); | |
289 | tm.tm_min = bin2bcd(tm.tm_min); | |
290 | tm.tm_sec = bin2bcd(tm.tm_sec); | |
34a8e501 JN |
291 | tm.tm_mon |= century; |
292 | ||
293 | mutex_lock(&rtc_lock); | |
294 | ||
295 | rtc_write(RTC_YEAR, tm.tm_year); | |
296 | rtc_write(RTC_MONTH, tm.tm_mon); | |
297 | rtc_write(RTC_WEEKDAY, tm.tm_wday); /* Not coded in BCD. */ | |
298 | rtc_write(RTC_DAY_OF_MONTH, tm.tm_mday); | |
299 | rtc_write(RTC_HOURS, tm.tm_hour); | |
300 | rtc_write(RTC_MINUTES, tm.tm_min); | |
301 | rtc_write(RTC_SECONDS, tm.tm_sec); | |
302 | ||
303 | mutex_unlock(&rtc_lock); | |
304 | ||
305 | return 0; | |
306 | } | |
307 | case RTC_VL_READ: | |
308 | if (voltage_low) { | |
309 | printk(KERN_ERR "%s: RTC Voltage Low - " | |
310 | "reliable date/time information is no " | |
311 | "longer guaranteed!\n", PCF8563_NAME); | |
1da177e4 LT |
312 | } |
313 | ||
34a8e501 JN |
314 | if (copy_to_user((int *) arg, &voltage_low, sizeof(int))) |
315 | return -EFAULT; | |
316 | return 0; | |
317 | ||
318 | case RTC_VL_CLR: | |
319 | { | |
320 | /* Clear the VL bit in the seconds register in case | |
321 | * the time has not been set already (which would | |
322 | * have cleared it). This does not really matter | |
323 | * because of the cached voltage_low value but do it | |
324 | * anyway for consistency. */ | |
325 | ||
326 | int ret = rtc_read(RTC_SECONDS); | |
327 | ||
328 | rtc_write(RTC_SECONDS, (ret & 0x7F)); | |
329 | ||
330 | /* Clear the cached value. */ | |
331 | voltage_low = 0; | |
332 | ||
333 | return 0; | |
334 | } | |
335 | default: | |
336 | return -ENOTTY; | |
1da177e4 LT |
337 | } |
338 | ||
339 | return 0; | |
340 | } | |
341 | ||
f35d7764 AB |
342 | static long pcf8563_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) |
343 | { | |
344 | int ret; | |
345 | ||
0890b588 | 346 | mutex_lock(&pcf8563_mutex); |
33874cb8 | 347 | ret = pcf8563_ioctl(filp, cmd, arg); |
0890b588 | 348 | mutex_unlock(&pcf8563_mutex); |
f35d7764 AB |
349 | |
350 | return ret; | |
351 | } | |
352 | ||
34a8e501 | 353 | static int __init pcf8563_register(void) |
1da177e4 | 354 | { |
34a8e501 JN |
355 | if (pcf8563_init() < 0) { |
356 | printk(KERN_INFO "%s: Unable to initialize Real-Time Clock " | |
357 | "Driver, %s\n", PCF8563_NAME, DRIVER_VERSION); | |
358 | return -1; | |
359 | } | |
360 | ||
1da177e4 | 361 | if (register_chrdev(PCF8563_MAJOR, DEVICE_NAME, &pcf8563_fops) < 0) { |
e34f80cd | 362 | printk(KERN_INFO "%s: Unable to get major number %d for RTC device.\n", |
1da177e4 LT |
363 | PCF8563_NAME, PCF8563_MAJOR); |
364 | return -1; | |
365 | } | |
366 | ||
34a8e501 JN |
367 | printk(KERN_INFO "%s Real-Time Clock Driver, %s\n", PCF8563_NAME, |
368 | DRIVER_VERSION); | |
369 | ||
370 | /* Check for low voltage, and warn about it. */ | |
371 | if (voltage_low) { | |
372 | printk(KERN_WARNING "%s: RTC Voltage Low - reliable date/time " | |
373 | "information is no longer guaranteed!\n", PCF8563_NAME); | |
374 | } | |
375 | ||
376 | return 0; | |
1da177e4 LT |
377 | } |
378 | ||
379 | module_init(pcf8563_register); | |
380 | module_exit(pcf8563_exit); |