aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorRyan Mallon <ryan@bluewatersys.com>2011-03-22 19:34:53 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2011-03-22 20:44:16 -0400
commitcf044f0ed526752b8c2aaae748220759608b3fc8 (patch)
treebafd4c7abaed6fa03c5314d59f6de79a639a5008 /drivers
parentbc96ba7414ca4456661d0873856e0f363d32ba67 (diff)
drivers/rtc/rtc-isl1208.c: add alarm support
Add alarm/wakeup support to rtc isl1208 driver Signed-off-by: Ryan Mallon <ryan@bluewatersys.com> Cc: Alessandro Zummo <a.zummo@towertech.it> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/rtc/rtc-isl1208.c176
1 files changed, 171 insertions, 5 deletions
diff --git a/drivers/rtc/rtc-isl1208.c b/drivers/rtc/rtc-isl1208.c
index 468200c38ecb..da8beb8cae51 100644
--- a/drivers/rtc/rtc-isl1208.c
+++ b/drivers/rtc/rtc-isl1208.c
@@ -39,6 +39,8 @@
39#define ISL1208_REG_SR_BAT (1<<1) /* battery */ 39#define ISL1208_REG_SR_BAT (1<<1) /* battery */
40#define ISL1208_REG_SR_RTCF (1<<0) /* rtc fail */ 40#define ISL1208_REG_SR_RTCF (1<<0) /* rtc fail */
41#define ISL1208_REG_INT 0x08 41#define ISL1208_REG_INT 0x08
42#define ISL1208_REG_INT_ALME (1<<6) /* alarm enable */
43#define ISL1208_REG_INT_IM (1<<7) /* interrupt/alarm mode */
42#define ISL1208_REG_09 0x09 /* reserved */ 44#define ISL1208_REG_09 0x09 /* reserved */
43#define ISL1208_REG_ATR 0x0a 45#define ISL1208_REG_ATR 0x0a
44#define ISL1208_REG_DTR 0x0b 46#define ISL1208_REG_DTR 0x0b
@@ -202,6 +204,30 @@ isl1208_i2c_set_usr(struct i2c_client *client, u16 usr)
202} 204}
203 205
204static int 206static int
207isl1208_rtc_toggle_alarm(struct i2c_client *client, int enable)
208{
209 int icr = i2c_smbus_read_byte_data(client, ISL1208_REG_INT);
210
211 if (icr < 0) {
212 dev_err(&client->dev, "%s: reading INT failed\n", __func__);
213 return icr;
214 }
215
216 if (enable)
217 icr |= ISL1208_REG_INT_ALME | ISL1208_REG_INT_IM;
218 else
219 icr &= ~(ISL1208_REG_INT_ALME | ISL1208_REG_INT_IM);
220
221 icr = i2c_smbus_write_byte_data(client, ISL1208_REG_INT, icr);
222 if (icr < 0) {
223 dev_err(&client->dev, "%s: writing INT failed\n", __func__);
224 return icr;
225 }
226
227 return 0;
228}
229
230static int
205isl1208_rtc_proc(struct device *dev, struct seq_file *seq) 231isl1208_rtc_proc(struct device *dev, struct seq_file *seq)
206{ 232{
207 struct i2c_client *const client = to_i2c_client(dev); 233 struct i2c_client *const client = to_i2c_client(dev);
@@ -288,9 +314,8 @@ isl1208_i2c_read_alarm(struct i2c_client *client, struct rtc_wkalrm *alarm)
288{ 314{
289 struct rtc_time *const tm = &alarm->time; 315 struct rtc_time *const tm = &alarm->time;
290 u8 regs[ISL1208_ALARM_SECTION_LEN] = { 0, }; 316 u8 regs[ISL1208_ALARM_SECTION_LEN] = { 0, };
291 int sr; 317 int icr, yr, sr = isl1208_i2c_get_sr(client);
292 318
293 sr = isl1208_i2c_get_sr(client);
294 if (sr < 0) { 319 if (sr < 0) {
295 dev_err(&client->dev, "%s: reading SR failed\n", __func__); 320 dev_err(&client->dev, "%s: reading SR failed\n", __func__);
296 return sr; 321 return sr;
@@ -313,6 +338,73 @@ isl1208_i2c_read_alarm(struct i2c_client *client, struct rtc_wkalrm *alarm)
313 bcd2bin(regs[ISL1208_REG_MOA - ISL1208_REG_SCA] & 0x1f) - 1; 338 bcd2bin(regs[ISL1208_REG_MOA - ISL1208_REG_SCA] & 0x1f) - 1;
314 tm->tm_wday = bcd2bin(regs[ISL1208_REG_DWA - ISL1208_REG_SCA] & 0x03); 339 tm->tm_wday = bcd2bin(regs[ISL1208_REG_DWA - ISL1208_REG_SCA] & 0x03);
315 340
341 /* The alarm doesn't store the year so get it from the rtc section */
342 yr = i2c_smbus_read_byte_data(client, ISL1208_REG_YR);
343 if (yr < 0) {
344 dev_err(&client->dev, "%s: reading RTC YR failed\n", __func__);
345 return yr;
346 }
347 tm->tm_year = bcd2bin(yr) + 100;
348
349 icr = i2c_smbus_read_byte_data(client, ISL1208_REG_INT);
350 if (icr < 0) {
351 dev_err(&client->dev, "%s: reading INT failed\n", __func__);
352 return icr;
353 }
354 alarm->enabled = !!(icr & ISL1208_REG_INT_ALME);
355
356 return 0;
357}
358
359static int
360isl1208_i2c_set_alarm(struct i2c_client *client, struct rtc_wkalrm *alarm)
361{
362 struct rtc_time *alarm_tm = &alarm->time;
363 u8 regs[ISL1208_ALARM_SECTION_LEN] = { 0, };
364 const int offs = ISL1208_REG_SCA;
365 unsigned long rtc_secs, alarm_secs;
366 struct rtc_time rtc_tm;
367 int err, enable;
368
369 err = isl1208_i2c_read_time(client, &rtc_tm);
370 if (err)
371 return err;
372 err = rtc_tm_to_time(&rtc_tm, &rtc_secs);
373 if (err)
374 return err;
375 err = rtc_tm_to_time(alarm_tm, &alarm_secs);
376 if (err)
377 return err;
378
379 /* If the alarm time is before the current time disable the alarm */
380 if (!alarm->enabled || alarm_secs <= rtc_secs)
381 enable = 0x00;
382 else
383 enable = 0x80;
384
385 /* Program the alarm and enable it for each setting */
386 regs[ISL1208_REG_SCA - offs] = bin2bcd(alarm_tm->tm_sec) | enable;
387 regs[ISL1208_REG_MNA - offs] = bin2bcd(alarm_tm->tm_min) | enable;
388 regs[ISL1208_REG_HRA - offs] = bin2bcd(alarm_tm->tm_hour) |
389 ISL1208_REG_HR_MIL | enable;
390
391 regs[ISL1208_REG_DTA - offs] = bin2bcd(alarm_tm->tm_mday) | enable;
392 regs[ISL1208_REG_MOA - offs] = bin2bcd(alarm_tm->tm_mon + 1) | enable;
393 regs[ISL1208_REG_DWA - offs] = bin2bcd(alarm_tm->tm_wday & 7) | enable;
394
395 /* write ALARM registers */
396 err = isl1208_i2c_set_regs(client, offs, regs,
397 ISL1208_ALARM_SECTION_LEN);
398 if (err < 0) {
399 dev_err(&client->dev, "%s: writing ALARM section failed\n",
400 __func__);
401 return err;
402 }
403
404 err = isl1208_rtc_toggle_alarm(client, enable);
405 if (err)
406 return err;
407
316 return 0; 408 return 0;
317} 409}
318 410
@@ -391,12 +483,63 @@ isl1208_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
391 return isl1208_i2c_read_alarm(to_i2c_client(dev), alarm); 483 return isl1208_i2c_read_alarm(to_i2c_client(dev), alarm);
392} 484}
393 485
486static int
487isl1208_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
488{
489 return isl1208_i2c_set_alarm(to_i2c_client(dev), alarm);
490}
491
492static irqreturn_t
493isl1208_rtc_interrupt(int irq, void *data)
494{
495 unsigned long timeout = jiffies + msecs_to_jiffies(1000);
496 struct i2c_client *client = data;
497 int handled = 0, sr, err;
498
499 /*
500 * I2C reads get NAK'ed if we read straight away after an interrupt?
501 * Using a mdelay/msleep didn't seem to help either, so we work around
502 * this by continually trying to read the register for a short time.
503 */
504 while (1) {
505 sr = isl1208_i2c_get_sr(client);
506 if (sr >= 0)
507 break;
508
509 if (time_after(jiffies, timeout)) {
510 dev_err(&client->dev, "%s: reading SR failed\n",
511 __func__);
512 return sr;
513 }
514 }
515
516 if (sr & ISL1208_REG_SR_ALM) {
517 dev_dbg(&client->dev, "alarm!\n");
518
519 /* Clear the alarm */
520 sr &= ~ISL1208_REG_SR_ALM;
521 sr = i2c_smbus_write_byte_data(client, ISL1208_REG_SR, sr);
522 if (sr < 0)
523 dev_err(&client->dev, "%s: writing SR failed\n",
524 __func__);
525 else
526 handled = 1;
527
528 /* Disable the alarm */
529 err = isl1208_rtc_toggle_alarm(client, 0);
530 if (err)
531 return err;
532 }
533
534 return handled ? IRQ_HANDLED : IRQ_NONE;
535}
536
394static const struct rtc_class_ops isl1208_rtc_ops = { 537static const struct rtc_class_ops isl1208_rtc_ops = {
395 .proc = isl1208_rtc_proc, 538 .proc = isl1208_rtc_proc,
396 .read_time = isl1208_rtc_read_time, 539 .read_time = isl1208_rtc_read_time,
397 .set_time = isl1208_rtc_set_time, 540 .set_time = isl1208_rtc_set_time,
398 .read_alarm = isl1208_rtc_read_alarm, 541 .read_alarm = isl1208_rtc_read_alarm,
399 /*.set_alarm = isl1208_rtc_set_alarm, */ 542 .set_alarm = isl1208_rtc_set_alarm,
400}; 543};
401 544
402/* sysfs interface */ 545/* sysfs interface */
@@ -488,11 +631,29 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id)
488 dev_info(&client->dev, 631 dev_info(&client->dev,
489 "chip found, driver version " DRV_VERSION "\n"); 632 "chip found, driver version " DRV_VERSION "\n");
490 633
634 if (client->irq > 0) {
635 rc = request_threaded_irq(client->irq, NULL,
636 isl1208_rtc_interrupt,
637 IRQF_SHARED,
638 isl1208_driver.driver.name, client);
639 if (!rc) {
640 device_init_wakeup(&client->dev, 1);
641 enable_irq_wake(client->irq);
642 } else {
643 dev_err(&client->dev,
644 "Unable to request irq %d, no alarm support\n",
645 client->irq);
646 client->irq = 0;
647 }
648 }
649
491 rtc = rtc_device_register(isl1208_driver.driver.name, 650 rtc = rtc_device_register(isl1208_driver.driver.name,
492 &client->dev, &isl1208_rtc_ops, 651 &client->dev, &isl1208_rtc_ops,
493 THIS_MODULE); 652 THIS_MODULE);
494 if (IS_ERR(rtc)) 653 if (IS_ERR(rtc)) {
495 return PTR_ERR(rtc); 654 rc = PTR_ERR(rtc);
655 goto exit_free_irq;
656 }
496 657
497 i2c_set_clientdata(client, rtc); 658 i2c_set_clientdata(client, rtc);
498 659
@@ -514,6 +675,9 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id)
514 675
515exit_unregister: 676exit_unregister:
516 rtc_device_unregister(rtc); 677 rtc_device_unregister(rtc);
678exit_free_irq:
679 if (client->irq)
680 free_irq(client->irq, client);
517 681
518 return rc; 682 return rc;
519} 683}
@@ -525,6 +689,8 @@ isl1208_remove(struct i2c_client *client)
525 689
526 sysfs_remove_group(&client->dev.kobj, &isl1208_rtc_sysfs_files); 690 sysfs_remove_group(&client->dev.kobj, &isl1208_rtc_sysfs_files);
527 rtc_device_unregister(rtc); 691 rtc_device_unregister(rtc);
692 if (client->irq)
693 free_irq(client->irq, client);
528 694
529 return 0; 695 return 0;
530} 696}