aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesper Nilsson <jesper.nilsson@axis.com>2008-01-24 08:24:09 -0500
committerJesper Nilsson <jesper.nilsson@axis.com>2008-02-08 05:06:34 -0500
commit7edf744053873e390d7d05ab0136c5162cf89c27 (patch)
tree3c6da49d72f829b3a1c782fb2acd9e274b0704b3
parentd8ac17a0eeab6580cced355de85ac90227096bb9 (diff)
CRIS v32: Update driver for RTC chip pcf8563.
- Moved all calls to register_chrdev to a function called by module_init. - Added mutex locking. - Added better error handling at start up. - Added BIN_TO_BCD of the month value before it is saved to the RTC. - Corrected the month value returned by pcf8563_readreg. - Cache the voltage low value at driver init so the battery status information does not get 'accidentally' cleared when setting the RTC time. - Removed obsolete CONFIG_ETRAX_RTC_READONLY - Voltage low ioctl():s RTC_VLOW_RD -> RTC_VL_READ, RTC_VLOW_SET -> RTC_VL_CLR
-rw-r--r--arch/cris/arch-v32/drivers/pcf8563.c296
1 files changed, 163 insertions, 133 deletions
diff --git a/arch/cris/arch-v32/drivers/pcf8563.c b/arch/cris/arch-v32/drivers/pcf8563.c
index 6dbd700d3d66..53db3870ba04 100644
--- a/arch/cris/arch-v32/drivers/pcf8563.c
+++ b/arch/cris/arch-v32/drivers/pcf8563.c
@@ -10,7 +10,7 @@
10 * 400 kbits/s. The built-in word address register is incremented 10 * 400 kbits/s. The built-in word address register is incremented
11 * automatically after each written or read byte. 11 * automatically after each written or read byte.
12 * 12 *
13 * Copyright (c) 2002-2003, Axis Communications AB 13 * Copyright (c) 2002-2007, Axis Communications AB
14 * All rights reserved. 14 * All rights reserved.
15 * 15 *
16 * Author: Tobias Anderberg <tobiasa@axis.com>. 16 * Author: Tobias Anderberg <tobiasa@axis.com>.
@@ -26,6 +26,7 @@
26#include <linux/ioctl.h> 26#include <linux/ioctl.h>
27#include <linux/delay.h> 27#include <linux/delay.h>
28#include <linux/bcd.h> 28#include <linux/bcd.h>
29#include <linux/mutex.h>
29 30
30#include <asm/uaccess.h> 31#include <asm/uaccess.h>
31#include <asm/system.h> 32#include <asm/system.h>
@@ -37,24 +38,27 @@
37#define PCF8563_MAJOR 121 /* Local major number. */ 38#define PCF8563_MAJOR 121 /* Local major number. */
38#define DEVICE_NAME "rtc" /* Name which is registered in /proc/devices. */ 39#define DEVICE_NAME "rtc" /* Name which is registered in /proc/devices. */
39#define PCF8563_NAME "PCF8563" 40#define PCF8563_NAME "PCF8563"
40#define DRIVER_VERSION "$Revision: 1.1 $" 41#define DRIVER_VERSION "$Revision: 1.17 $"
41 42
42/* Two simple wrapper macros, saves a few keystrokes. */ 43/* Two simple wrapper macros, saves a few keystrokes. */
43#define rtc_read(x) i2c_readreg(RTC_I2C_READ, x) 44#define rtc_read(x) i2c_readreg(RTC_I2C_READ, x)
44#define rtc_write(x,y) i2c_writereg(RTC_I2C_WRITE, x, y) 45#define rtc_write(x,y) i2c_writereg(RTC_I2C_WRITE, x, y)
45 46
47static DEFINE_MUTEX(rtc_lock); /* Protect state etc */
48
46static const unsigned char days_in_month[] = 49static const unsigned char days_in_month[] =
47 { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; 50 { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
48 51
49int pcf8563_ioctl(struct inode *, struct file *, unsigned int, unsigned long); 52int pcf8563_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
50int pcf8563_open(struct inode *, struct file *); 53
51int pcf8563_release(struct inode *, struct file *); 54/* Cache VL bit value read at driver init since writing the RTC_SECOND
55 * register clears the VL status.
56 */
57static int voltage_low;
52 58
53static const struct file_operations pcf8563_fops = { 59static const struct file_operations pcf8563_fops = {
54 .owner = THIS_MODULE, 60 .owner = THIS_MODULE,
55 .ioctl = pcf8563_ioctl, 61 .ioctl = pcf8563_ioctl
56 .open = pcf8563_open,
57 .release = pcf8563_release,
58}; 62};
59 63
60unsigned char 64unsigned char
@@ -62,7 +66,7 @@ pcf8563_readreg(int reg)
62{ 66{
63 unsigned char res = rtc_read(reg); 67 unsigned char res = rtc_read(reg);
64 68
65 /* The PCF8563 does not return 0 for unimplemented bits */ 69 /* The PCF8563 does not return 0 for unimplemented bits. */
66 switch (reg) { 70 switch (reg) {
67 case RTC_SECONDS: 71 case RTC_SECONDS:
68 case RTC_MINUTES: 72 case RTC_MINUTES:
@@ -95,11 +99,6 @@ pcf8563_readreg(int reg)
95void 99void
96pcf8563_writereg(int reg, unsigned char val) 100pcf8563_writereg(int reg, unsigned char val)
97{ 101{
98#ifdef CONFIG_ETRAX_RTC_READONLY
99 if (reg == RTC_CONTROL1 || (reg >= RTC_SECONDS && reg <= RTC_YEAR))
100 return;
101#endif
102
103 rtc_write(reg, val); 102 rtc_write(reg, val);
104} 103}
105 104
@@ -114,11 +113,13 @@ get_rtc_time(struct rtc_time *tm)
114 tm->tm_mon = rtc_read(RTC_MONTH); 113 tm->tm_mon = rtc_read(RTC_MONTH);
115 tm->tm_year = rtc_read(RTC_YEAR); 114 tm->tm_year = rtc_read(RTC_YEAR);
116 115
117 if (tm->tm_sec & 0x80) 116 if (tm->tm_sec & 0x80) {
118 printk(KERN_WARNING "%s: RTC Voltage Low - reliable date/time " 117 printk(KERN_ERR "%s: RTC Voltage Low - reliable date/time "
119 "information is no longer guaranteed!\n", PCF8563_NAME); 118 "information is no longer guaranteed!\n", PCF8563_NAME);
119 }
120 120
121 tm->tm_year = BCD_TO_BIN(tm->tm_year) + ((tm->tm_mon & 0x80) ? 100 : 0); 121 tm->tm_year = BCD_TO_BIN(tm->tm_year) +
122 ((tm->tm_mon & 0x80) ? 100 : 0);
122 tm->tm_sec &= 0x7F; 123 tm->tm_sec &= 0x7F;
123 tm->tm_min &= 0x7F; 124 tm->tm_min &= 0x7F;
124 tm->tm_hour &= 0x3F; 125 tm->tm_hour &= 0x3F;
@@ -137,8 +138,19 @@ get_rtc_time(struct rtc_time *tm)
137int __init 138int __init
138pcf8563_init(void) 139pcf8563_init(void)
139{ 140{
141 static int res;
142 static int first = 1;
143
144 if (!first)
145 return res;
146 first = 0;
147
140 /* Initiate the i2c protocol. */ 148 /* Initiate the i2c protocol. */
141 i2c_init(); 149 res = i2c_init();
150 if (res < 0) {
151 printk(KERN_CRIT "pcf8563_init: Failed to init i2c.\n");
152 return res;
153 }
142 154
143 /* 155 /*
144 * First of all we need to reset the chip. This is done by 156 * First of all we need to reset the chip. This is done by
@@ -170,24 +182,20 @@ pcf8563_init(void)
170 if (rtc_write(RTC_WEEKDAY_ALARM, 0x80) < 0) 182 if (rtc_write(RTC_WEEKDAY_ALARM, 0x80) < 0)
171 goto err; 183 goto err;
172 184
173 if (register_chrdev(PCF8563_MAJOR, DEVICE_NAME, &pcf8563_fops) < 0) { 185 /* Check for low voltage, and warn about it. */
174 printk(KERN_INFO "%s: Unable to get major number %d for RTC device.\n", 186 if (rtc_read(RTC_SECONDS) & 0x80) {
175 PCF8563_NAME, PCF8563_MAJOR); 187 voltage_low = 1;
176 return -1; 188 printk(KERN_WARNING "%s: RTC Voltage Low - reliable "
189 "date/time information is no longer guaranteed!\n",
190 PCF8563_NAME);
177 } 191 }
178 192
179 printk(KERN_INFO "%s Real-Time Clock Driver, %s\n", PCF8563_NAME, DRIVER_VERSION); 193 return res;
180
181 /* Check for low voltage, and warn about it.. */
182 if (rtc_read(RTC_SECONDS) & 0x80)
183 printk(KERN_WARNING "%s: RTC Voltage Low - reliable date/time "
184 "information is no longer guaranteed!\n", PCF8563_NAME);
185
186 return 0;
187 194
188err: 195err:
189 printk(KERN_INFO "%s: Error initializing chip.\n", PCF8563_NAME); 196 printk(KERN_INFO "%s: Error initializing chip.\n", PCF8563_NAME);
190 return -1; 197 res = -1;
198 return res;
191} 199}
192 200
193void __exit 201void __exit
@@ -200,8 +208,8 @@ pcf8563_exit(void)
200 * ioctl calls for this driver. Why return -ENOTTY upon error? Because 208 * ioctl calls for this driver. Why return -ENOTTY upon error? Because
201 * POSIX says so! 209 * POSIX says so!
202 */ 210 */
203int 211int pcf8563_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
204pcf8563_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) 212 unsigned long arg)
205{ 213{
206 /* Some sanity checks. */ 214 /* Some sanity checks. */
207 if (_IOC_TYPE(cmd) != RTC_MAGIC) 215 if (_IOC_TYPE(cmd) != RTC_MAGIC)
@@ -211,125 +219,147 @@ pcf8563_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned
211 return -ENOTTY; 219 return -ENOTTY;
212 220
213 switch (cmd) { 221 switch (cmd) {
214 case RTC_RD_TIME: 222 case RTC_RD_TIME:
215 { 223 {
216 struct rtc_time tm; 224 struct rtc_time tm;
217 225
218 memset(&tm, 0, sizeof (struct rtc_time)); 226 mutex_lock(&rtc_lock);
219 get_rtc_time(&tm); 227 memset(&tm, 0, sizeof tm);
220 228 get_rtc_time(&tm);
221 if (copy_to_user((struct rtc_time *) arg, &tm, sizeof tm)) { 229
222 return -EFAULT; 230 if (copy_to_user((struct rtc_time *) arg, &tm,
223 } 231 sizeof tm)) {
224 232 spin_unlock(&rtc_lock);
225 return 0; 233 return -EFAULT;
226 } 234 }
227 235
228 case RTC_SET_TIME: 236 mutex_unlock(&rtc_lock);
229 { 237
230#ifdef CONFIG_ETRAX_RTC_READONLY 238 return 0;
239 }
240 case RTC_SET_TIME:
241 {
242 int leap;
243 int year;
244 int century;
245 struct rtc_time tm;
246
247 memset(&tm, 0, sizeof tm);
248 if (!capable(CAP_SYS_TIME))
231 return -EPERM; 249 return -EPERM;
232#else
233 int leap;
234 int year;
235 int century;
236 struct rtc_time tm;
237
238 if (!capable(CAP_SYS_TIME))
239 return -EPERM;
240
241 if (copy_from_user(&tm, (struct rtc_time *) arg, sizeof tm))
242 return -EFAULT;
243
244 /* Convert from struct tm to struct rtc_time. */
245 tm.tm_year += 1900;
246 tm.tm_mon += 1;
247
248 /*
249 * Check if tm.tm_year is a leap year. A year is a leap
250 * year if it is divisible by 4 but not 100, except
251 * that years divisible by 400 _are_ leap years.
252 */
253 year = tm.tm_year;
254 leap = (tm.tm_mon == 2) && ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0);
255
256 /* Perform some sanity checks. */
257 if ((tm.tm_year < 1970) ||
258 (tm.tm_mon > 12) ||
259 (tm.tm_mday == 0) ||
260 (tm.tm_mday > days_in_month[tm.tm_mon] + leap) ||
261 (tm.tm_wday >= 7) ||
262 (tm.tm_hour >= 24) ||
263 (tm.tm_min >= 60) ||
264 (tm.tm_sec >= 60))
265 return -EINVAL;
266
267 century = (tm.tm_year >= 2000) ? 0x80 : 0;
268 tm.tm_year = tm.tm_year % 100;
269
270 BIN_TO_BCD(tm.tm_year);
271 BIN_TO_BCD(tm.tm_mday);
272 BIN_TO_BCD(tm.tm_hour);
273 BIN_TO_BCD(tm.tm_min);
274 BIN_TO_BCD(tm.tm_sec);
275 tm.tm_mon |= century;
276
277 rtc_write(RTC_YEAR, tm.tm_year);
278 rtc_write(RTC_MONTH, tm.tm_mon);
279 rtc_write(RTC_WEEKDAY, tm.tm_wday); /* Not coded in BCD. */
280 rtc_write(RTC_DAY_OF_MONTH, tm.tm_mday);
281 rtc_write(RTC_HOURS, tm.tm_hour);
282 rtc_write(RTC_MINUTES, tm.tm_min);
283 rtc_write(RTC_SECONDS, tm.tm_sec);
284
285 return 0;
286#endif /* !CONFIG_ETRAX_RTC_READONLY */
287 }
288 250
289 case RTC_VLOW_RD: 251 if (copy_from_user(&tm, (struct rtc_time *) arg,
290 { 252 sizeof tm))
291 int vl_bit = 0; 253 return -EFAULT;
254
255 /* Convert from struct tm to struct rtc_time. */
256 tm.tm_year += 1900;
257 tm.tm_mon += 1;
258
259 /*
260 * Check if tm.tm_year is a leap year. A year is a leap
261 * year if it is divisible by 4 but not 100, except
262 * that years divisible by 400 _are_ leap years.
263 */
264 year = tm.tm_year;
265 leap = (tm.tm_mon == 2) &&
266 ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0);
267
268 /* Perform some sanity checks. */
269 if ((tm.tm_year < 1970) ||
270 (tm.tm_mon > 12) ||
271 (tm.tm_mday == 0) ||
272 (tm.tm_mday > days_in_month[tm.tm_mon] + leap) ||
273 (tm.tm_wday >= 7) ||
274 (tm.tm_hour >= 24) ||
275 (tm.tm_min >= 60) ||
276 (tm.tm_sec >= 60))
277 return -EINVAL;
278
279 century = (tm.tm_year >= 2000) ? 0x80 : 0;
280 tm.tm_year = tm.tm_year % 100;
281
282 BIN_TO_BCD(tm.tm_year);
283 BIN_TO_BCD(tm.tm_mon);
284 BIN_TO_BCD(tm.tm_mday);
285 BIN_TO_BCD(tm.tm_hour);
286 BIN_TO_BCD(tm.tm_min);
287 BIN_TO_BCD(tm.tm_sec);
288 tm.tm_mon |= century;
289
290 mutex_lock(&rtc_lock);
291
292 rtc_write(RTC_YEAR, tm.tm_year);
293 rtc_write(RTC_MONTH, tm.tm_mon);
294 rtc_write(RTC_WEEKDAY, tm.tm_wday); /* Not coded in BCD. */
295 rtc_write(RTC_DAY_OF_MONTH, tm.tm_mday);
296 rtc_write(RTC_HOURS, tm.tm_hour);
297 rtc_write(RTC_MINUTES, tm.tm_min);
298 rtc_write(RTC_SECONDS, tm.tm_sec);
299
300 mutex_unlock(&rtc_lock);
301
302 return 0;
303 }
304 case RTC_VL_READ:
305 if (voltage_low)
306 printk(KERN_ERR "%s: RTC Voltage Low - "
307 "reliable date/time information is no "
308 "longer guaranteed!\n", PCF8563_NAME);
292 309
293 if (rtc_read(RTC_SECONDS) & 0x80) { 310 if (copy_to_user((int *) arg, &voltage_low, sizeof(int)))
294 vl_bit = 1; 311 return -EFAULT;
295 printk(KERN_WARNING "%s: RTC Voltage Low - reliable " 312 return 0;
296 "date/time information is no longer guaranteed!\n",
297 PCF8563_NAME);
298 }
299 if (copy_to_user((int *) arg, &vl_bit, sizeof(int)))
300 return -EFAULT;
301 313
302 return 0; 314 case RTC_VL_CLR:
303 } 315 {
316 /* Clear the VL bit in the seconds register in case
317 * the time has not been set already (which would
318 * have cleared it). This does not really matter
319 * because of the cached voltage_low value but do it
320 * anyway for consistency. */
304 321
305 case RTC_VLOW_SET: 322 int ret = rtc_read(RTC_SECONDS);
306 {
307 /* Clear the VL bit in the seconds register */
308 int ret = rtc_read(RTC_SECONDS);
309 323
310 rtc_write(RTC_SECONDS, (ret & 0x7F)); 324 rtc_write(RTC_SECONDS, (ret & 0x7F));
311 325
312 return 0; 326 /* Clear the cached value. */
313 } 327 voltage_low = 0;
314 328
315 default: 329 return 0;
316 return -ENOTTY; 330 }
331 default:
332 return -ENOTTY;
317 } 333 }
318 334
319 return 0; 335 return 0;
320} 336}
321 337
322int 338static int __init pcf8563_register(void)
323pcf8563_open(struct inode *inode, struct file *filp)
324{ 339{
325 return 0; 340 if (pcf8563_init() < 0) {
326} 341 printk(KERN_INFO "%s: Unable to initialize Real-Time Clock "
342 "Driver, %s\n", PCF8563_NAME, DRIVER_VERSION);
343 return -1;
344 }
345
346 if (register_chrdev(PCF8563_MAJOR, DEVICE_NAME, &pcf8563_fops) < 0) {
347 printk(KERN_INFO "%s: Unable to get major numer %d for RTC "
348 "device.\n", PCF8563_NAME, PCF8563_MAJOR);
349 return -1;
350 }
351
352 printk(KERN_INFO "%s Real-Time Clock Driver, %s\n", PCF8563_NAME,
353 DRIVER_VERSION);
354
355 /* Check for low voltage, and warn about it. */
356 if (voltage_low) {
357 printk(KERN_WARNING "%s: RTC Voltage Low - reliable date/time "
358 "information is no longer guaranteed!\n", PCF8563_NAME);
359 }
327 360
328int
329pcf8563_release(struct inode *inode, struct file *filp)
330{
331 return 0; 361 return 0;
332} 362}
333 363
334module_init(pcf8563_init); 364module_init(pcf8563_register);
335module_exit(pcf8563_exit); 365module_exit(pcf8563_exit);