Commit cf044f0ed526752b8c2aaae748220759608b3fc8

Authored by Ryan Mallon
Committed by Linus Torvalds
1 parent bc96ba7414

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>

Showing 1 changed file with 171 additions and 5 deletions Side-by-side Diff

drivers/rtc/rtc-isl1208.c
... ... @@ -39,6 +39,8 @@
39 39 #define ISL1208_REG_SR_BAT (1<<1) /* battery */
40 40 #define ISL1208_REG_SR_RTCF (1<<0) /* rtc fail */
41 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 44 #define ISL1208_REG_09 0x09 /* reserved */
43 45 #define ISL1208_REG_ATR 0x0a
44 46 #define ISL1208_REG_DTR 0x0b
... ... @@ -202,6 +204,30 @@
202 204 }
203 205  
204 206 static int
  207 +isl1208_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 +
  230 +static int
205 231 isl1208_rtc_proc(struct device *dev, struct seq_file *seq)
206 232 {
207 233 struct i2c_client *const client = to_i2c_client(dev);
208 234  
... ... @@ -288,9 +314,8 @@
288 314 {
289 315 struct rtc_time *const tm = &alarm->time;
290 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 319 if (sr < 0) {
295 320 dev_err(&client->dev, "%s: reading SR failed\n", __func__);
296 321 return sr;
297 322  
... ... @@ -313,10 +338,77 @@
313 338 bcd2bin(regs[ISL1208_REG_MOA - ISL1208_REG_SCA] & 0x1f) - 1;
314 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 +
316 356 return 0;
317 357 }
318 358  
319 359 static int
  360 +isl1208_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 +
  408 + return 0;
  409 +}
  410 +
  411 +static int
320 412 isl1208_rtc_read_time(struct device *dev, struct rtc_time *tm)
321 413 {
322 414 return isl1208_i2c_read_time(to_i2c_client(dev), tm);
323 415  
... ... @@ -391,12 +483,63 @@
391 483 return isl1208_i2c_read_alarm(to_i2c_client(dev), alarm);
392 484 }
393 485  
  486 +static int
  487 +isl1208_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
  488 +{
  489 + return isl1208_i2c_set_alarm(to_i2c_client(dev), alarm);
  490 +}
  491 +
  492 +static irqreturn_t
  493 +isl1208_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 +
394 537 static const struct rtc_class_ops isl1208_rtc_ops = {
395 538 .proc = isl1208_rtc_proc,
396 539 .read_time = isl1208_rtc_read_time,
397 540 .set_time = isl1208_rtc_set_time,
398 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 545 /* sysfs interface */
403 546  
... ... @@ -488,11 +631,29 @@
488 631 dev_info(&client->dev,
489 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 650 rtc = rtc_device_register(isl1208_driver.driver.name,
492 651 &client->dev, &isl1208_rtc_ops,
493 652 THIS_MODULE);
494   - if (IS_ERR(rtc))
495   - return PTR_ERR(rtc);
  653 + if (IS_ERR(rtc)) {
  654 + rc = PTR_ERR(rtc);
  655 + goto exit_free_irq;
  656 + }
496 657  
497 658 i2c_set_clientdata(client, rtc);
498 659  
... ... @@ -514,6 +675,9 @@
514 675  
515 676 exit_unregister:
516 677 rtc_device_unregister(rtc);
  678 +exit_free_irq:
  679 + if (client->irq)
  680 + free_irq(client->irq, client);
517 681  
518 682 return rc;
519 683 }
... ... @@ -525,6 +689,8 @@
525 689  
526 690 sysfs_remove_group(&client->dev.kobj, &isl1208_rtc_sysfs_files);
527 691 rtc_device_unregister(rtc);
  692 + if (client->irq)
  693 + free_irq(client->irq, client);
528 694  
529 695 return 0;
530 696 }