diff options
Diffstat (limited to 'drivers/rtc/rtc-rs5c372.c')
-rw-r--r-- | drivers/rtc/rtc-rs5c372.c | 209 |
1 files changed, 131 insertions, 78 deletions
diff --git a/drivers/rtc/rtc-rs5c372.c b/drivers/rtc/rtc-rs5c372.c index 56caf6b2c3e5..c390e3355595 100644 --- a/drivers/rtc/rtc-rs5c372.c +++ b/drivers/rtc/rtc-rs5c372.c | |||
@@ -3,6 +3,7 @@ | |||
3 | * | 3 | * |
4 | * Copyright (C) 2005 Pavel Mironchik <pmironchik@optifacio.net> | 4 | * Copyright (C) 2005 Pavel Mironchik <pmironchik@optifacio.net> |
5 | * Copyright (C) 2006 Tower Technologies | 5 | * Copyright (C) 2006 Tower Technologies |
6 | * Copyright (C) 2008 Paul Mundt | ||
6 | * | 7 | * |
7 | * This program is free software; you can redistribute it and/or modify | 8 | * This program is free software; you can redistribute it and/or modify |
8 | * it under the terms of the GNU General Public License version 2 as | 9 | * it under the terms of the GNU General Public License version 2 as |
@@ -13,7 +14,7 @@ | |||
13 | #include <linux/rtc.h> | 14 | #include <linux/rtc.h> |
14 | #include <linux/bcd.h> | 15 | #include <linux/bcd.h> |
15 | 16 | ||
16 | #define DRV_VERSION "0.5" | 17 | #define DRV_VERSION "0.6" |
17 | 18 | ||
18 | 19 | ||
19 | /* | 20 | /* |
@@ -89,6 +90,7 @@ struct rs5c372 { | |||
89 | enum rtc_type type; | 90 | enum rtc_type type; |
90 | unsigned time24:1; | 91 | unsigned time24:1; |
91 | unsigned has_irq:1; | 92 | unsigned has_irq:1; |
93 | unsigned smbus:1; | ||
92 | char buf[17]; | 94 | char buf[17]; |
93 | char *regs; | 95 | char *regs; |
94 | }; | 96 | }; |
@@ -106,10 +108,25 @@ static int rs5c_get_regs(struct rs5c372 *rs5c) | |||
106 | * | 108 | * |
107 | * The first method doesn't work with the iop3xx adapter driver, on at | 109 | * The first method doesn't work with the iop3xx adapter driver, on at |
108 | * least 80219 chips; this works around that bug. | 110 | * least 80219 chips; this works around that bug. |
111 | * | ||
112 | * The third method on the other hand doesn't work for the SMBus-only | ||
113 | * configurations, so we use the the first method there, stripping off | ||
114 | * the extra register in the process. | ||
109 | */ | 115 | */ |
110 | if ((i2c_transfer(client->adapter, msgs, 1)) != 1) { | 116 | if (rs5c->smbus) { |
111 | dev_warn(&client->dev, "can't read registers\n"); | 117 | int addr = RS5C_ADDR(RS5C372_REG_SECS); |
112 | return -EIO; | 118 | int size = sizeof(rs5c->buf) - 1; |
119 | |||
120 | if (i2c_smbus_read_i2c_block_data(client, addr, size, | ||
121 | rs5c->buf + 1) != size) { | ||
122 | dev_warn(&client->dev, "can't read registers\n"); | ||
123 | return -EIO; | ||
124 | } | ||
125 | } else { | ||
126 | if ((i2c_transfer(client->adapter, msgs, 1)) != 1) { | ||
127 | dev_warn(&client->dev, "can't read registers\n"); | ||
128 | return -EIO; | ||
129 | } | ||
113 | } | 130 | } |
114 | 131 | ||
115 | dev_dbg(&client->dev, | 132 | dev_dbg(&client->dev, |
@@ -187,6 +204,7 @@ static int rs5c372_set_datetime(struct i2c_client *client, struct rtc_time *tm) | |||
187 | { | 204 | { |
188 | struct rs5c372 *rs5c = i2c_get_clientdata(client); | 205 | struct rs5c372 *rs5c = i2c_get_clientdata(client); |
189 | unsigned char buf[8]; | 206 | unsigned char buf[8]; |
207 | int addr; | ||
190 | 208 | ||
191 | dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d " | 209 | dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d " |
192 | "mday=%d, mon=%d, year=%d, wday=%d\n", | 210 | "mday=%d, mon=%d, year=%d, wday=%d\n", |
@@ -194,16 +212,16 @@ static int rs5c372_set_datetime(struct i2c_client *client, struct rtc_time *tm) | |||
194 | tm->tm_sec, tm->tm_min, tm->tm_hour, | 212 | tm->tm_sec, tm->tm_min, tm->tm_hour, |
195 | tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); | 213 | tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); |
196 | 214 | ||
197 | buf[0] = RS5C_ADDR(RS5C372_REG_SECS); | 215 | addr = RS5C_ADDR(RS5C372_REG_SECS); |
198 | buf[1] = BIN2BCD(tm->tm_sec); | 216 | buf[0] = BIN2BCD(tm->tm_sec); |
199 | buf[2] = BIN2BCD(tm->tm_min); | 217 | buf[1] = BIN2BCD(tm->tm_min); |
200 | buf[3] = rs5c_hr2reg(rs5c, tm->tm_hour); | 218 | buf[2] = rs5c_hr2reg(rs5c, tm->tm_hour); |
201 | buf[4] = BIN2BCD(tm->tm_wday); | 219 | buf[3] = BIN2BCD(tm->tm_wday); |
202 | buf[5] = BIN2BCD(tm->tm_mday); | 220 | buf[4] = BIN2BCD(tm->tm_mday); |
203 | buf[6] = BIN2BCD(tm->tm_mon + 1); | 221 | buf[5] = BIN2BCD(tm->tm_mon + 1); |
204 | buf[7] = BIN2BCD(tm->tm_year - 100); | 222 | buf[6] = BIN2BCD(tm->tm_year - 100); |
205 | 223 | ||
206 | if ((i2c_master_send(client, buf, 8)) != 8) { | 224 | if (i2c_smbus_write_i2c_block_data(client, addr, sizeof(buf), buf) < 0) { |
207 | dev_err(&client->dev, "%s: write error\n", __func__); | 225 | dev_err(&client->dev, "%s: write error\n", __func__); |
208 | return -EIO; | 226 | return -EIO; |
209 | } | 227 | } |
@@ -266,16 +284,16 @@ rs5c_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) | |||
266 | { | 284 | { |
267 | struct i2c_client *client = to_i2c_client(dev); | 285 | struct i2c_client *client = to_i2c_client(dev); |
268 | struct rs5c372 *rs5c = i2c_get_clientdata(client); | 286 | struct rs5c372 *rs5c = i2c_get_clientdata(client); |
269 | unsigned char buf[2]; | 287 | unsigned char buf; |
270 | int status; | 288 | int status, addr; |
271 | 289 | ||
272 | buf[1] = rs5c->regs[RS5C_REG_CTRL1]; | 290 | buf = rs5c->regs[RS5C_REG_CTRL1]; |
273 | switch (cmd) { | 291 | switch (cmd) { |
274 | case RTC_UIE_OFF: | 292 | case RTC_UIE_OFF: |
275 | case RTC_UIE_ON: | 293 | case RTC_UIE_ON: |
276 | /* some 327a modes use a different IRQ pin for 1Hz irqs */ | 294 | /* some 327a modes use a different IRQ pin for 1Hz irqs */ |
277 | if (rs5c->type == rtc_rs5c372a | 295 | if (rs5c->type == rtc_rs5c372a |
278 | && (buf[1] & RS5C372A_CTRL1_SL1)) | 296 | && (buf & RS5C372A_CTRL1_SL1)) |
279 | return -ENOIOCTLCMD; | 297 | return -ENOIOCTLCMD; |
280 | case RTC_AIE_OFF: | 298 | case RTC_AIE_OFF: |
281 | case RTC_AIE_ON: | 299 | case RTC_AIE_ON: |
@@ -293,28 +311,30 @@ rs5c_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) | |||
293 | if (status < 0) | 311 | if (status < 0) |
294 | return status; | 312 | return status; |
295 | 313 | ||
296 | buf[0] = RS5C_ADDR(RS5C_REG_CTRL1); | 314 | addr = RS5C_ADDR(RS5C_REG_CTRL1); |
297 | switch (cmd) { | 315 | switch (cmd) { |
298 | case RTC_AIE_OFF: /* alarm off */ | 316 | case RTC_AIE_OFF: /* alarm off */ |
299 | buf[1] &= ~RS5C_CTRL1_AALE; | 317 | buf &= ~RS5C_CTRL1_AALE; |
300 | break; | 318 | break; |
301 | case RTC_AIE_ON: /* alarm on */ | 319 | case RTC_AIE_ON: /* alarm on */ |
302 | buf[1] |= RS5C_CTRL1_AALE; | 320 | buf |= RS5C_CTRL1_AALE; |
303 | break; | 321 | break; |
304 | case RTC_UIE_OFF: /* update off */ | 322 | case RTC_UIE_OFF: /* update off */ |
305 | buf[1] &= ~RS5C_CTRL1_CT_MASK; | 323 | buf &= ~RS5C_CTRL1_CT_MASK; |
306 | break; | 324 | break; |
307 | case RTC_UIE_ON: /* update on */ | 325 | case RTC_UIE_ON: /* update on */ |
308 | buf[1] &= ~RS5C_CTRL1_CT_MASK; | 326 | buf &= ~RS5C_CTRL1_CT_MASK; |
309 | buf[1] |= RS5C_CTRL1_CT4; | 327 | buf |= RS5C_CTRL1_CT4; |
310 | break; | 328 | break; |
311 | } | 329 | } |
312 | if ((i2c_master_send(client, buf, 2)) != 2) { | 330 | |
331 | if (i2c_smbus_write_byte_data(client, addr, buf) < 0) { | ||
313 | printk(KERN_WARNING "%s: can't update alarm\n", | 332 | printk(KERN_WARNING "%s: can't update alarm\n", |
314 | rs5c->rtc->name); | 333 | rs5c->rtc->name); |
315 | status = -EIO; | 334 | status = -EIO; |
316 | } else | 335 | } else |
317 | rs5c->regs[RS5C_REG_CTRL1] = buf[1]; | 336 | rs5c->regs[RS5C_REG_CTRL1] = buf; |
337 | |||
318 | return status; | 338 | return status; |
319 | } | 339 | } |
320 | 340 | ||
@@ -364,8 +384,8 @@ static int rs5c_set_alarm(struct device *dev, struct rtc_wkalrm *t) | |||
364 | { | 384 | { |
365 | struct i2c_client *client = to_i2c_client(dev); | 385 | struct i2c_client *client = to_i2c_client(dev); |
366 | struct rs5c372 *rs5c = i2c_get_clientdata(client); | 386 | struct rs5c372 *rs5c = i2c_get_clientdata(client); |
367 | int status; | 387 | int status, addr, i; |
368 | unsigned char buf[4]; | 388 | unsigned char buf[3]; |
369 | 389 | ||
370 | /* only handle up to 24 hours in the future, like RTC_ALM_SET */ | 390 | /* only handle up to 24 hours in the future, like RTC_ALM_SET */ |
371 | if (t->time.tm_mday != -1 | 391 | if (t->time.tm_mday != -1 |
@@ -380,33 +400,36 @@ static int rs5c_set_alarm(struct device *dev, struct rtc_wkalrm *t) | |||
380 | if (status < 0) | 400 | if (status < 0) |
381 | return status; | 401 | return status; |
382 | if (rs5c->regs[RS5C_REG_CTRL1] & RS5C_CTRL1_AALE) { | 402 | if (rs5c->regs[RS5C_REG_CTRL1] & RS5C_CTRL1_AALE) { |
383 | buf[0] = RS5C_ADDR(RS5C_REG_CTRL1); | 403 | addr = RS5C_ADDR(RS5C_REG_CTRL1); |
384 | buf[1] = rs5c->regs[RS5C_REG_CTRL1] & ~RS5C_CTRL1_AALE; | 404 | buf[0] = rs5c->regs[RS5C_REG_CTRL1] & ~RS5C_CTRL1_AALE; |
385 | if (i2c_master_send(client, buf, 2) != 2) { | 405 | if (i2c_smbus_write_byte_data(client, addr, buf[0]) < 0) { |
386 | pr_debug("%s: can't disable alarm\n", rs5c->rtc->name); | 406 | pr_debug("%s: can't disable alarm\n", rs5c->rtc->name); |
387 | return -EIO; | 407 | return -EIO; |
388 | } | 408 | } |
389 | rs5c->regs[RS5C_REG_CTRL1] = buf[1]; | 409 | rs5c->regs[RS5C_REG_CTRL1] = buf[0]; |
390 | } | 410 | } |
391 | 411 | ||
392 | /* set alarm */ | 412 | /* set alarm */ |
393 | buf[0] = RS5C_ADDR(RS5C_REG_ALARM_A_MIN); | 413 | buf[0] = BIN2BCD(t->time.tm_min); |
394 | buf[1] = BIN2BCD(t->time.tm_min); | 414 | buf[1] = rs5c_hr2reg(rs5c, t->time.tm_hour); |
395 | buf[2] = rs5c_hr2reg(rs5c, t->time.tm_hour); | 415 | buf[2] = 0x7f; /* any/all days */ |
396 | buf[3] = 0x7f; /* any/all days */ | 416 | |
397 | if ((i2c_master_send(client, buf, 4)) != 4) { | 417 | for (i = 0; i < sizeof(buf); i++) { |
398 | pr_debug("%s: can't set alarm time\n", rs5c->rtc->name); | 418 | addr = RS5C_ADDR(RS5C_REG_ALARM_A_MIN + i); |
399 | return -EIO; | 419 | if (i2c_smbus_write_byte_data(client, addr, buf[i]) < 0) { |
420 | pr_debug("%s: can't set alarm time\n", rs5c->rtc->name); | ||
421 | return -EIO; | ||
422 | } | ||
400 | } | 423 | } |
401 | 424 | ||
402 | /* ... and maybe enable its irq */ | 425 | /* ... and maybe enable its irq */ |
403 | if (t->enabled) { | 426 | if (t->enabled) { |
404 | buf[0] = RS5C_ADDR(RS5C_REG_CTRL1); | 427 | addr = RS5C_ADDR(RS5C_REG_CTRL1); |
405 | buf[1] = rs5c->regs[RS5C_REG_CTRL1] | RS5C_CTRL1_AALE; | 428 | buf[0] = rs5c->regs[RS5C_REG_CTRL1] | RS5C_CTRL1_AALE; |
406 | if ((i2c_master_send(client, buf, 2)) != 2) | 429 | if (i2c_smbus_write_byte_data(client, addr, buf[0]) < 0) |
407 | printk(KERN_WARNING "%s: can't enable alarm\n", | 430 | printk(KERN_WARNING "%s: can't enable alarm\n", |
408 | rs5c->rtc->name); | 431 | rs5c->rtc->name); |
409 | rs5c->regs[RS5C_REG_CTRL1] = buf[1]; | 432 | rs5c->regs[RS5C_REG_CTRL1] = buf[0]; |
410 | } | 433 | } |
411 | 434 | ||
412 | return 0; | 435 | return 0; |
@@ -503,18 +526,74 @@ static void rs5c_sysfs_unregister(struct device *dev) | |||
503 | 526 | ||
504 | static struct i2c_driver rs5c372_driver; | 527 | static struct i2c_driver rs5c372_driver; |
505 | 528 | ||
529 | static int rs5c_oscillator_setup(struct rs5c372 *rs5c372) | ||
530 | { | ||
531 | unsigned char buf[2]; | ||
532 | int addr, i, ret = 0; | ||
533 | |||
534 | if (!(rs5c372->regs[RS5C_REG_CTRL2] & RS5C_CTRL2_XSTP)) | ||
535 | return ret; | ||
536 | rs5c372->regs[RS5C_REG_CTRL2] &= ~RS5C_CTRL2_XSTP; | ||
537 | |||
538 | addr = RS5C_ADDR(RS5C_REG_CTRL1); | ||
539 | buf[0] = rs5c372->regs[RS5C_REG_CTRL1]; | ||
540 | buf[1] = rs5c372->regs[RS5C_REG_CTRL2]; | ||
541 | |||
542 | /* use 24hr mode */ | ||
543 | switch (rs5c372->type) { | ||
544 | case rtc_rs5c372a: | ||
545 | case rtc_rs5c372b: | ||
546 | buf[1] |= RS5C372_CTRL2_24; | ||
547 | rs5c372->time24 = 1; | ||
548 | break; | ||
549 | case rtc_rv5c386: | ||
550 | case rtc_rv5c387a: | ||
551 | buf[0] |= RV5C387_CTRL1_24; | ||
552 | rs5c372->time24 = 1; | ||
553 | break; | ||
554 | default: | ||
555 | /* impossible */ | ||
556 | break; | ||
557 | } | ||
558 | |||
559 | for (i = 0; i < sizeof(buf); i++) { | ||
560 | addr = RS5C_ADDR(RS5C_REG_CTRL1 + i); | ||
561 | ret = i2c_smbus_write_byte_data(rs5c372->client, addr, buf[i]); | ||
562 | if (unlikely(ret < 0)) | ||
563 | return ret; | ||
564 | } | ||
565 | |||
566 | rs5c372->regs[RS5C_REG_CTRL1] = buf[0]; | ||
567 | rs5c372->regs[RS5C_REG_CTRL2] = buf[1]; | ||
568 | |||
569 | return 0; | ||
570 | } | ||
571 | |||
506 | static int rs5c372_probe(struct i2c_client *client, | 572 | static int rs5c372_probe(struct i2c_client *client, |
507 | const struct i2c_device_id *id) | 573 | const struct i2c_device_id *id) |
508 | { | 574 | { |
509 | int err = 0; | 575 | int err = 0; |
576 | int smbus_mode = 0; | ||
510 | struct rs5c372 *rs5c372; | 577 | struct rs5c372 *rs5c372; |
511 | struct rtc_time tm; | 578 | struct rtc_time tm; |
512 | 579 | ||
513 | dev_dbg(&client->dev, "%s\n", __func__); | 580 | dev_dbg(&client->dev, "%s\n", __func__); |
514 | 581 | ||
515 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | 582 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C | |
516 | err = -ENODEV; | 583 | I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_I2C_BLOCK)) { |
517 | goto exit; | 584 | /* |
585 | * If we don't have any master mode adapter, try breaking | ||
586 | * it down in to the barest of capabilities. | ||
587 | */ | ||
588 | if (i2c_check_functionality(client->adapter, | ||
589 | I2C_FUNC_SMBUS_BYTE_DATA | | ||
590 | I2C_FUNC_SMBUS_I2C_BLOCK)) | ||
591 | smbus_mode = 1; | ||
592 | else { | ||
593 | /* Still no good, give up */ | ||
594 | err = -ENODEV; | ||
595 | goto exit; | ||
596 | } | ||
518 | } | 597 | } |
519 | 598 | ||
520 | if (!(rs5c372 = kzalloc(sizeof(struct rs5c372), GFP_KERNEL))) { | 599 | if (!(rs5c372 = kzalloc(sizeof(struct rs5c372), GFP_KERNEL))) { |
@@ -528,6 +607,7 @@ static int rs5c372_probe(struct i2c_client *client, | |||
528 | 607 | ||
529 | /* we read registers 0x0f then 0x00-0x0f; skip the first one */ | 608 | /* we read registers 0x0f then 0x00-0x0f; skip the first one */ |
530 | rs5c372->regs = &rs5c372->buf[1]; | 609 | rs5c372->regs = &rs5c372->buf[1]; |
610 | rs5c372->smbus = smbus_mode; | ||
531 | 611 | ||
532 | err = rs5c_get_regs(rs5c372); | 612 | err = rs5c_get_regs(rs5c372); |
533 | if (err < 0) | 613 | if (err < 0) |
@@ -559,38 +639,10 @@ static int rs5c372_probe(struct i2c_client *client, | |||
559 | /* if the oscillator lost power and no other software (like | 639 | /* if the oscillator lost power and no other software (like |
560 | * the bootloader) set it up, do it here. | 640 | * the bootloader) set it up, do it here. |
561 | */ | 641 | */ |
562 | if (rs5c372->regs[RS5C_REG_CTRL2] & RS5C_CTRL2_XSTP) { | 642 | err = rs5c_oscillator_setup(rs5c372); |
563 | unsigned char buf[3]; | 643 | if (unlikely(err < 0)) { |
564 | 644 | dev_err(&client->dev, "setup error\n"); | |
565 | rs5c372->regs[RS5C_REG_CTRL2] &= ~RS5C_CTRL2_XSTP; | 645 | goto exit_kfree; |
566 | |||
567 | buf[0] = RS5C_ADDR(RS5C_REG_CTRL1); | ||
568 | buf[1] = rs5c372->regs[RS5C_REG_CTRL1]; | ||
569 | buf[2] = rs5c372->regs[RS5C_REG_CTRL2]; | ||
570 | |||
571 | /* use 24hr mode */ | ||
572 | switch (rs5c372->type) { | ||
573 | case rtc_rs5c372a: | ||
574 | case rtc_rs5c372b: | ||
575 | buf[2] |= RS5C372_CTRL2_24; | ||
576 | rs5c372->time24 = 1; | ||
577 | break; | ||
578 | case rtc_rv5c386: | ||
579 | case rtc_rv5c387a: | ||
580 | buf[1] |= RV5C387_CTRL1_24; | ||
581 | rs5c372->time24 = 1; | ||
582 | break; | ||
583 | default: | ||
584 | /* impossible */ | ||
585 | break; | ||
586 | } | ||
587 | |||
588 | if ((i2c_master_send(client, buf, 3)) != 3) { | ||
589 | dev_err(&client->dev, "setup error\n"); | ||
590 | goto exit_kfree; | ||
591 | } | ||
592 | rs5c372->regs[RS5C_REG_CTRL1] = buf[1]; | ||
593 | rs5c372->regs[RS5C_REG_CTRL2] = buf[2]; | ||
594 | } | 646 | } |
595 | 647 | ||
596 | if (rs5c372_get_datetime(client, &tm) < 0) | 648 | if (rs5c372_get_datetime(client, &tm) < 0) |
@@ -667,7 +719,8 @@ module_exit(rs5c372_exit); | |||
667 | 719 | ||
668 | MODULE_AUTHOR( | 720 | MODULE_AUTHOR( |
669 | "Pavel Mironchik <pmironchik@optifacio.net>, " | 721 | "Pavel Mironchik <pmironchik@optifacio.net>, " |
670 | "Alessandro Zummo <a.zummo@towertech.it>"); | 722 | "Alessandro Zummo <a.zummo@towertech.it>, " |
723 | "Paul Mundt <lethal@linux-sh.org>"); | ||
671 | MODULE_DESCRIPTION("Ricoh RS5C372 RTC driver"); | 724 | MODULE_DESCRIPTION("Ricoh RS5C372 RTC driver"); |
672 | MODULE_LICENSE("GPL"); | 725 | MODULE_LICENSE("GPL"); |
673 | MODULE_VERSION(DRV_VERSION); | 726 | MODULE_VERSION(DRV_VERSION); |