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 | |
11 | * automatically after each written or read bute. | |
12 | * | |
13 | * Copyright (c) 2002, Axis Communications AB | |
14 | * All rights reserved. | |
15 | * | |
16 | * Author: Tobias Anderberg <tobiasa@axis.com>. | |
17 | * | |
7e920426 | 18 | * $Id: pcf8563.c,v 1.11 2005/03/07 13:13:07 starvik Exp $ |
1da177e4 LT |
19 | */ |
20 | ||
1da177e4 LT |
21 | #include <linux/module.h> |
22 | #include <linux/kernel.h> | |
23 | #include <linux/types.h> | |
24 | #include <linux/sched.h> | |
25 | #include <linux/init.h> | |
26 | #include <linux/fs.h> | |
27 | #include <linux/ioctl.h> | |
28 | #include <linux/delay.h> | |
29 | #include <linux/bcd.h> | |
a9415644 | 30 | #include <linux/capability.h> |
1da177e4 LT |
31 | |
32 | #include <asm/uaccess.h> | |
33 | #include <asm/system.h> | |
34 | #include <asm/io.h> | |
35 | #include <asm/arch/svinto.h> | |
36 | #include <asm/rtc.h> | |
37 | #include "i2c.h" | |
38 | ||
39 | #define PCF8563_MAJOR 121 /* Local major number. */ | |
40 | #define DEVICE_NAME "rtc" /* Name which is registered in /proc/devices. */ | |
41 | #define PCF8563_NAME "PCF8563" | |
7e920426 | 42 | #define DRIVER_VERSION "$Revision: 1.11 $" |
1da177e4 LT |
43 | |
44 | /* I2C bus slave registers. */ | |
45 | #define RTC_I2C_READ 0xa3 | |
46 | #define RTC_I2C_WRITE 0xa2 | |
47 | ||
48 | /* Two simple wrapper macros, saves a few keystrokes. */ | |
49 | #define rtc_read(x) i2c_readreg(RTC_I2C_READ, x) | |
50 | #define rtc_write(x,y) i2c_writereg(RTC_I2C_WRITE, x, y) | |
7e920426 MS |
51 | |
52 | static DEFINE_SPINLOCK(rtc_lock); /* Protect state etc */ | |
1da177e4 LT |
53 | |
54 | static const unsigned char days_in_month[] = | |
55 | { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; | |
56 | ||
57 | int pcf8563_ioctl(struct inode *, struct file *, unsigned int, unsigned long); | |
58 | ||
5dfe4c96 | 59 | static const struct file_operations pcf8563_fops = { |
1da177e4 LT |
60 | .owner = THIS_MODULE, |
61 | .ioctl = pcf8563_ioctl, | |
62 | }; | |
63 | ||
64 | unsigned char | |
65 | pcf8563_readreg(int reg) | |
66 | { | |
67 | unsigned char res = i2c_readreg(RTC_I2C_READ, reg); | |
68 | ||
69 | /* The PCF8563 does not return 0 for unimplemented bits */ | |
70 | switch(reg) | |
71 | { | |
72 | case RTC_SECONDS: | |
73 | case RTC_MINUTES: | |
74 | res &= 0x7f; | |
75 | break; | |
76 | case RTC_HOURS: | |
77 | case RTC_DAY_OF_MONTH: | |
78 | res &= 0x3f; | |
79 | break; | |
80 | case RTC_MONTH: | |
81 | res = (res & 0x1f) - 1; /* PCF8563 returns month in range 1-12 */ | |
82 | break; | |
83 | } | |
84 | return res; | |
85 | } | |
86 | ||
87 | void | |
88 | pcf8563_writereg(int reg, unsigned char val) | |
89 | { | |
90 | #ifdef CONFIG_ETRAX_RTC_READONLY | |
91 | if (reg == RTC_CONTROL1 || (reg >= RTC_SECONDS && reg <= RTC_YEAR)) | |
92 | return; | |
93 | #endif | |
94 | ||
95 | rtc_write(reg, val); | |
96 | } | |
97 | ||
98 | void | |
99 | get_rtc_time(struct rtc_time *tm) | |
100 | { | |
101 | tm->tm_sec = rtc_read(RTC_SECONDS); | |
102 | tm->tm_min = rtc_read(RTC_MINUTES); | |
103 | tm->tm_hour = rtc_read(RTC_HOURS); | |
104 | tm->tm_mday = rtc_read(RTC_DAY_OF_MONTH); | |
105 | tm->tm_mon = rtc_read(RTC_MONTH); | |
106 | tm->tm_year = rtc_read(RTC_YEAR); | |
107 | ||
108 | if (tm->tm_sec & 0x80) | |
109 | printk(KERN_WARNING "%s: RTC Low Voltage - date/time is not reliable!\n", PCF8563_NAME); | |
110 | ||
111 | tm->tm_year = BCD_TO_BIN(tm->tm_year) + ((tm->tm_mon & 0x80) ? 100 : 0); | |
112 | tm->tm_sec &= 0x7f; | |
113 | tm->tm_min &= 0x7f; | |
114 | tm->tm_hour &= 0x3f; | |
115 | tm->tm_mday &= 0x3f; | |
116 | tm->tm_mon &= 0x1f; | |
117 | ||
118 | BCD_TO_BIN(tm->tm_sec); | |
119 | BCD_TO_BIN(tm->tm_min); | |
120 | BCD_TO_BIN(tm->tm_hour); | |
121 | BCD_TO_BIN(tm->tm_mday); | |
122 | BCD_TO_BIN(tm->tm_mon); | |
123 | tm->tm_mon--; /* Month is 1..12 in RTC but 0..11 in linux */ | |
124 | } | |
125 | ||
126 | int __init | |
127 | pcf8563_init(void) | |
128 | { | |
7e920426 | 129 | int ret; |
1da177e4 | 130 | |
7e920426 MS |
131 | if ((ret = i2c_init())) { |
132 | printk(KERN_CRIT "pcf8563_init: failed to init i2c\n"); | |
133 | return ret; | |
134 | } | |
1da177e4 LT |
135 | |
136 | /* | |
137 | * First of all we need to reset the chip. This is done by | |
138 | * clearing control1, control2 and clk freq, clear the | |
139 | * Voltage Low bit, and resetting all alarms. | |
140 | */ | |
141 | if (rtc_write(RTC_CONTROL1, 0x00) < 0) | |
142 | goto err; | |
143 | ||
144 | if (rtc_write(RTC_CONTROL2, 0x00) < 0) | |
145 | goto err; | |
146 | ||
147 | if (rtc_write(RTC_CLOCKOUT_FREQ, 0x00) < 0) | |
148 | goto err; | |
149 | ||
150 | /* Clear the VL bit in the seconds register. */ | |
151 | ret = rtc_read(RTC_SECONDS); | |
152 | ||
153 | if (rtc_write(RTC_SECONDS, (ret & 0x7f)) < 0) | |
154 | goto err; | |
155 | ||
156 | /* Reset the alarms. */ | |
157 | if (rtc_write(RTC_MINUTE_ALARM, 0x00) < 0) | |
158 | goto err; | |
159 | ||
160 | if (rtc_write(RTC_HOUR_ALARM, 0x00) < 0) | |
161 | goto err; | |
162 | ||
163 | if (rtc_write(RTC_DAY_ALARM, 0x00) < 0) | |
164 | goto err; | |
165 | ||
166 | if (rtc_write(RTC_WEEKDAY_ALARM, 0x00) < 0) | |
167 | goto err; | |
168 | ||
169 | /* Check for low voltage, and warn about it.. */ | |
170 | if (rtc_read(RTC_SECONDS) & 0x80) | |
171 | printk(KERN_WARNING "%s: RTC Low Voltage - date/time is not reliable!\n", PCF8563_NAME); | |
172 | ||
173 | return 0; | |
174 | ||
175 | err: | |
176 | printk(KERN_INFO "%s: Error initializing chip.\n", PCF8563_NAME); | |
177 | return -1; | |
178 | } | |
179 | ||
180 | void __exit | |
181 | pcf8563_exit(void) | |
182 | { | |
68fc4fab | 183 | unregister_chrdev(PCF8563_MAJOR, DEVICE_NAME); |
1da177e4 LT |
184 | } |
185 | ||
186 | /* | |
187 | * ioctl calls for this driver. Why return -ENOTTY upon error? Because | |
188 | * POSIX says so! | |
189 | */ | |
190 | int | |
191 | pcf8563_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) | |
192 | { | |
193 | /* Some sanity checks. */ | |
194 | if (_IOC_TYPE(cmd) != RTC_MAGIC) | |
195 | return -ENOTTY; | |
196 | ||
197 | if (_IOC_NR(cmd) > RTC_MAX_IOCTL) | |
198 | return -ENOTTY; | |
199 | ||
200 | switch (cmd) { | |
201 | case RTC_RD_TIME: | |
202 | { | |
203 | struct rtc_time tm; | |
204 | ||
7e920426 | 205 | spin_lock(&rtc_lock); |
1da177e4 LT |
206 | get_rtc_time(&tm); |
207 | ||
208 | if (copy_to_user((struct rtc_time *) arg, &tm, sizeof(struct rtc_time))) { | |
7e920426 | 209 | spin_unlock(&rtc_lock); |
1da177e4 LT |
210 | return -EFAULT; |
211 | } | |
212 | ||
7e920426 | 213 | spin_unlock(&rtc_lock); |
1da177e4 LT |
214 | return 0; |
215 | } | |
216 | break; | |
217 | case RTC_SET_TIME: | |
218 | { | |
219 | #ifdef CONFIG_ETRAX_RTC_READONLY | |
220 | return -EPERM; | |
221 | #else | |
222 | int leap; | |
223 | int century; | |
224 | struct rtc_time tm; | |
225 | ||
226 | memset(&tm, 0, sizeof (struct rtc_time)); | |
227 | if (!capable(CAP_SYS_TIME)) | |
228 | return -EPERM; | |
229 | ||
230 | if (copy_from_user(&tm, (struct rtc_time *) arg, sizeof(struct rtc_time))) | |
231 | return -EFAULT; | |
232 | ||
233 | /* Convert from struct tm to struct rtc_time. */ | |
234 | tm.tm_year += 1900; | |
235 | tm.tm_mon += 1; | |
236 | ||
237 | leap = ((tm.tm_mon == 2) && ((tm.tm_year % 4) == 0)) ? 1 : 0; | |
238 | ||
239 | /* Perform some sanity checks. */ | |
240 | if ((tm.tm_year < 1970) || | |
241 | (tm.tm_mon > 12) || | |
242 | (tm.tm_mday == 0) || | |
243 | (tm.tm_mday > days_in_month[tm.tm_mon] + leap) || | |
244 | (tm.tm_hour >= 24) || | |
245 | (tm.tm_min >= 60) || | |
246 | (tm.tm_sec >= 60)) | |
247 | return -EINVAL; | |
248 | ||
249 | century = (tm.tm_year >= 2000) ? 0x80 : 0; | |
250 | tm.tm_year = tm.tm_year % 100; | |
251 | ||
252 | BIN_TO_BCD(tm.tm_year); | |
253 | BIN_TO_BCD(tm.tm_mday); | |
254 | BIN_TO_BCD(tm.tm_hour); | |
255 | BIN_TO_BCD(tm.tm_min); | |
256 | BIN_TO_BCD(tm.tm_sec); | |
257 | tm.tm_mon |= century; | |
7e920426 MS |
258 | |
259 | spin_lock(&rtc_lock); | |
1da177e4 LT |
260 | |
261 | rtc_write(RTC_YEAR, tm.tm_year); | |
262 | rtc_write(RTC_MONTH, tm.tm_mon); | |
263 | rtc_write(RTC_DAY_OF_MONTH, tm.tm_mday); | |
264 | rtc_write(RTC_HOURS, tm.tm_hour); | |
265 | rtc_write(RTC_MINUTES, tm.tm_min); | |
266 | rtc_write(RTC_SECONDS, tm.tm_sec); | |
267 | ||
7e920426 MS |
268 | spin_unlock(&rtc_lock); |
269 | ||
1da177e4 LT |
270 | return 0; |
271 | #endif /* !CONFIG_ETRAX_RTC_READONLY */ | |
272 | } | |
273 | ||
274 | case RTC_VLOW_RD: | |
275 | { | |
276 | int vl_bit = 0; | |
277 | ||
278 | if (rtc_read(RTC_SECONDS) & 0x80) { | |
279 | vl_bit = 1; | |
280 | printk(KERN_WARNING "%s: RTC Voltage Low - reliable " | |
281 | "date/time information is no longer guaranteed!\n", | |
282 | PCF8563_NAME); | |
283 | } | |
284 | if (copy_to_user((int *) arg, &vl_bit, sizeof(int))) | |
285 | return -EFAULT; | |
286 | ||
287 | return 0; | |
288 | } | |
289 | ||
290 | case RTC_VLOW_SET: | |
291 | { | |
292 | /* Clear the VL bit in the seconds register */ | |
293 | int ret = rtc_read(RTC_SECONDS); | |
294 | ||
295 | rtc_write(RTC_SECONDS, (ret & 0x7F)); | |
296 | ||
297 | return 0; | |
298 | } | |
299 | ||
300 | default: | |
301 | return -ENOTTY; | |
302 | } | |
303 | ||
304 | return 0; | |
305 | } | |
306 | ||
307 | static int __init | |
308 | pcf8563_register(void) | |
309 | { | |
310 | pcf8563_init(); | |
311 | if (register_chrdev(PCF8563_MAJOR, DEVICE_NAME, &pcf8563_fops) < 0) { | |
e34f80cd | 312 | printk(KERN_INFO "%s: Unable to get major number %d for RTC device.\n", |
1da177e4 LT |
313 | PCF8563_NAME, PCF8563_MAJOR); |
314 | return -1; | |
315 | } | |
316 | ||
317 | printk(KERN_INFO "%s Real-Time Clock Driver, %s\n", PCF8563_NAME, DRIVER_VERSION); | |
318 | return 0; | |
319 | } | |
320 | ||
321 | module_init(pcf8563_register); | |
322 | module_exit(pcf8563_exit); |