aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Grzeschik <m.grzeschik@pengutronix.de>2018-07-24 07:31:21 -0400
committerAlexandre Belloni <alexandre.belloni@bootlin.com>2018-08-14 17:00:58 -0400
commitdd35bdb0768f1d03b043c4ba704fe9760eaa5891 (patch)
tree36fadcea99e2d4a4605806e48c32c6b6ee2052ad
parenta0a1a1ba303261d25814d11a05008d2931a69a8b (diff)
rtc: isl1208: add support for isl1219 with tamper detection
We add support for the ISL1219 chip that got an integrated tamper detection function. This patch implements the feature by adding an additional timestamp0 file to sysfs device path. This file contains seconds since epoch, if an event occurred, or is empty, if none occurred. Signed-off-by: Michael Grzeschik <m.grzeschik@pengutronix.de> Signed-off-by: Denis Osterland <Denis.Osterland@diehl.com> Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
-rw-r--r--drivers/rtc/rtc-isl1208.c131
1 files changed, 124 insertions, 7 deletions
diff --git a/drivers/rtc/rtc-isl1208.c b/drivers/rtc/rtc-isl1208.c
index 1a2c38cc0178..d426eac965e9 100644
--- a/drivers/rtc/rtc-isl1208.c
+++ b/drivers/rtc/rtc-isl1208.c
@@ -14,6 +14,7 @@
14#include <linux/i2c.h> 14#include <linux/i2c.h>
15#include <linux/bcd.h> 15#include <linux/bcd.h>
16#include <linux/rtc.h> 16#include <linux/rtc.h>
17#include "rtc-core.h"
17 18
18/* Register map */ 19/* Register map */
19/* rtc section */ 20/* rtc section */
@@ -33,13 +34,15 @@
33#define ISL1208_REG_SR_ARST (1<<7) /* auto reset */ 34#define ISL1208_REG_SR_ARST (1<<7) /* auto reset */
34#define ISL1208_REG_SR_XTOSCB (1<<6) /* crystal oscillator */ 35#define ISL1208_REG_SR_XTOSCB (1<<6) /* crystal oscillator */
35#define ISL1208_REG_SR_WRTC (1<<4) /* write rtc */ 36#define ISL1208_REG_SR_WRTC (1<<4) /* write rtc */
37#define ISL1208_REG_SR_EVT (1<<3) /* event */
36#define ISL1208_REG_SR_ALM (1<<2) /* alarm */ 38#define ISL1208_REG_SR_ALM (1<<2) /* alarm */
37#define ISL1208_REG_SR_BAT (1<<1) /* battery */ 39#define ISL1208_REG_SR_BAT (1<<1) /* battery */
38#define ISL1208_REG_SR_RTCF (1<<0) /* rtc fail */ 40#define ISL1208_REG_SR_RTCF (1<<0) /* rtc fail */
39#define ISL1208_REG_INT 0x08 41#define ISL1208_REG_INT 0x08
40#define ISL1208_REG_INT_ALME (1<<6) /* alarm enable */ 42#define ISL1208_REG_INT_ALME (1<<6) /* alarm enable */
41#define ISL1208_REG_INT_IM (1<<7) /* interrupt/alarm mode */ 43#define ISL1208_REG_INT_IM (1<<7) /* interrupt/alarm mode */
42#define ISL1208_REG_09 0x09 /* reserved */ 44#define ISL1219_REG_EV 0x09
45#define ISL1219_REG_EV_EVEN (1<<4) /* event detection enable */
43#define ISL1208_REG_ATR 0x0a 46#define ISL1208_REG_ATR 0x0a
44#define ISL1208_REG_DTR 0x0b 47#define ISL1208_REG_DTR 0x0b
45 48
@@ -57,8 +60,24 @@
57#define ISL1208_REG_USR2 0x13 60#define ISL1208_REG_USR2 0x13
58#define ISL1208_USR_SECTION_LEN 2 61#define ISL1208_USR_SECTION_LEN 2
59 62
63/* event section */
64#define ISL1219_REG_SCT 0x14
65#define ISL1219_REG_MNT 0x15
66#define ISL1219_REG_HRT 0x16
67#define ISL1219_REG_DTT 0x17
68#define ISL1219_REG_MOT 0x18
69#define ISL1219_REG_YRT 0x19
70#define ISL1219_EVT_SECTION_LEN 6
71
60static struct i2c_driver isl1208_driver; 72static struct i2c_driver isl1208_driver;
61 73
74/* ISL1208 various variants */
75enum {
76 TYPE_ISL1208 = 0,
77 TYPE_ISL1218,
78 TYPE_ISL1219,
79};
80
62/* block read */ 81/* block read */
63static int 82static int
64isl1208_i2c_read_regs(struct i2c_client *client, u8 reg, u8 buf[], 83isl1208_i2c_read_regs(struct i2c_client *client, u8 reg, u8 buf[],
@@ -80,8 +99,8 @@ isl1208_i2c_read_regs(struct i2c_client *client, u8 reg, u8 buf[],
80 }; 99 };
81 int ret; 100 int ret;
82 101
83 BUG_ON(reg > ISL1208_REG_USR2); 102 WARN_ON(reg > ISL1219_REG_YRT);
84 BUG_ON(reg + len > ISL1208_REG_USR2 + 1); 103 WARN_ON(reg + len > ISL1219_REG_YRT + 1);
85 104
86 ret = i2c_transfer(client->adapter, msgs, 2); 105 ret = i2c_transfer(client->adapter, msgs, 2);
87 if (ret > 0) 106 if (ret > 0)
@@ -104,8 +123,8 @@ isl1208_i2c_set_regs(struct i2c_client *client, u8 reg, u8 const buf[],
104 }; 123 };
105 int ret; 124 int ret;
106 125
107 BUG_ON(reg > ISL1208_REG_USR2); 126 WARN_ON(reg > ISL1219_REG_YRT);
108 BUG_ON(reg + len > ISL1208_REG_USR2 + 1); 127 WARN_ON(reg + len > ISL1219_REG_YRT + 1);
109 128
110 i2c_buf[0] = reg; 129 i2c_buf[0] = reg;
111 memcpy(&i2c_buf[1], &buf[0], len); 130 memcpy(&i2c_buf[1], &buf[0], len);
@@ -493,6 +512,73 @@ isl1208_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
493 return isl1208_i2c_set_alarm(to_i2c_client(dev), alarm); 512 return isl1208_i2c_set_alarm(to_i2c_client(dev), alarm);
494} 513}
495 514
515static ssize_t timestamp0_store(struct device *dev,
516 struct device_attribute *attr,
517 const char *buf, size_t count)
518{
519 struct i2c_client *client = dev_get_drvdata(dev);
520 int sr;
521
522 sr = isl1208_i2c_get_sr(client);
523 if (sr < 0) {
524 dev_err(dev, "%s: reading SR failed\n", __func__);
525 return sr;
526 }
527
528 sr &= ~ISL1208_REG_SR_EVT;
529
530 sr = i2c_smbus_write_byte_data(client, ISL1208_REG_SR, sr);
531 if (sr < 0)
532 dev_err(dev, "%s: writing SR failed\n",
533 __func__);
534
535 return count;
536};
537
538static ssize_t timestamp0_show(struct device *dev,
539 struct device_attribute *attr, char *buf)
540{
541 struct i2c_client *client = dev_get_drvdata(dev);
542 u8 regs[ISL1219_EVT_SECTION_LEN] = { 0, };
543 struct rtc_time tm;
544 int sr;
545
546 sr = isl1208_i2c_get_sr(client);
547 if (sr < 0) {
548 dev_err(dev, "%s: reading SR failed\n", __func__);
549 return sr;
550 }
551
552 if (!(sr & ISL1208_REG_SR_EVT))
553 return 0;
554
555 sr = isl1208_i2c_read_regs(client, ISL1219_REG_SCT, regs,
556 ISL1219_EVT_SECTION_LEN);
557 if (sr < 0) {
558 dev_err(dev, "%s: reading event section failed\n",
559 __func__);
560 return 0;
561 }
562
563 /* MSB of each alarm register is an enable bit */
564 tm.tm_sec = bcd2bin(regs[ISL1219_REG_SCT - ISL1219_REG_SCT] & 0x7f);
565 tm.tm_min = bcd2bin(regs[ISL1219_REG_MNT - ISL1219_REG_SCT] & 0x7f);
566 tm.tm_hour = bcd2bin(regs[ISL1219_REG_HRT - ISL1219_REG_SCT] & 0x3f);
567 tm.tm_mday = bcd2bin(regs[ISL1219_REG_DTT - ISL1219_REG_SCT] & 0x3f);
568 tm.tm_mon =
569 bcd2bin(regs[ISL1219_REG_MOT - ISL1219_REG_SCT] & 0x1f) - 1;
570 tm.tm_year = bcd2bin(regs[ISL1219_REG_YRT - ISL1219_REG_SCT]) + 100;
571
572 sr = rtc_valid_tm(&tm);
573 if (sr)
574 return sr;
575
576 return sprintf(buf, "%llu\n",
577 (unsigned long long)rtc_tm_to_time64(&tm));
578};
579
580static DEVICE_ATTR_RW(timestamp0);
581
496static irqreturn_t 582static irqreturn_t
497isl1208_rtc_interrupt(int irq, void *data) 583isl1208_rtc_interrupt(int irq, void *data)
498{ 584{
@@ -538,6 +624,13 @@ isl1208_rtc_interrupt(int irq, void *data)
538 return err; 624 return err;
539 } 625 }
540 626
627 if (sr & ISL1208_REG_SR_EVT) {
628 sysfs_notify(&rtc->dev.kobj, NULL,
629 dev_attr_timestamp0.attr.name);
630 dev_warn(&client->dev, "event detected");
631 handled = 1;
632 }
633
541 return handled ? IRQ_HANDLED : IRQ_NONE; 634 return handled ? IRQ_HANDLED : IRQ_NONE;
542} 635}
543 636
@@ -623,6 +716,15 @@ static const struct attribute_group isl1208_rtc_sysfs_files = {
623 .attrs = isl1208_rtc_attrs, 716 .attrs = isl1208_rtc_attrs,
624}; 717};
625 718
719static struct attribute *isl1219_rtc_attrs[] = {
720 &dev_attr_timestamp0.attr,
721 NULL
722};
723
724static const struct attribute_group isl1219_rtc_sysfs_files = {
725 .attrs = isl1219_rtc_attrs,
726};
727
626static int 728static int
627isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id) 729isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id)
628{ 730{
@@ -642,6 +744,7 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id)
642 rtc->ops = &isl1208_rtc_ops; 744 rtc->ops = &isl1208_rtc_ops;
643 745
644 i2c_set_clientdata(client, rtc); 746 i2c_set_clientdata(client, rtc);
747 dev_set_drvdata(&rtc->dev, client);
645 748
646 rc = isl1208_i2c_get_sr(client); 749 rc = isl1208_i2c_get_sr(client);
647 if (rc < 0) { 750 if (rc < 0) {
@@ -653,6 +756,18 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id)
653 dev_warn(&client->dev, "rtc power failure detected, " 756 dev_warn(&client->dev, "rtc power failure detected, "
654 "please set clock.\n"); 757 "please set clock.\n");
655 758
759 if (id->driver_data == TYPE_ISL1219) {
760 rc = i2c_smbus_write_byte_data(client, ISL1219_REG_EV,
761 ISL1219_REG_EV_EVEN);
762 if (rc < 0) {
763 dev_err(&client->dev, "could not enable tamper detection\n");
764 return rc;
765 }
766 rc = rtc_add_group(rtc, &isl1219_rtc_sysfs_files);
767 if (rc)
768 return rc;
769 }
770
656 rc = sysfs_create_group(&client->dev.kobj, &isl1208_rtc_sysfs_files); 771 rc = sysfs_create_group(&client->dev.kobj, &isl1208_rtc_sysfs_files);
657 if (rc) 772 if (rc)
658 return rc; 773 return rc;
@@ -686,8 +801,9 @@ isl1208_remove(struct i2c_client *client)
686} 801}
687 802
688static const struct i2c_device_id isl1208_id[] = { 803static const struct i2c_device_id isl1208_id[] = {
689 { "isl1208", 0 }, 804 { "isl1208", TYPE_ISL1208 },
690 { "isl1218", 0 }, 805 { "isl1218", TYPE_ISL1218 },
806 { "isl1219", TYPE_ISL1219 },
691 { } 807 { }
692}; 808};
693MODULE_DEVICE_TABLE(i2c, isl1208_id); 809MODULE_DEVICE_TABLE(i2c, isl1208_id);
@@ -695,6 +811,7 @@ MODULE_DEVICE_TABLE(i2c, isl1208_id);
695static const struct of_device_id isl1208_of_match[] = { 811static const struct of_device_id isl1208_of_match[] = {
696 { .compatible = "isil,isl1208" }, 812 { .compatible = "isil,isl1208" },
697 { .compatible = "isil,isl1218" }, 813 { .compatible = "isil,isl1218" },
814 { .compatible = "isil,isl1219" },
698 { } 815 { }
699}; 816};
700MODULE_DEVICE_TABLE(of, isl1208_of_match); 817MODULE_DEVICE_TABLE(of, isl1208_of_match);