[2/2] rtc: pcf2127: add support for pcf2127 tamper functionality

Message ID 20170120123644.118612-2-sean.nyekjaer@prevas.dk
State Not Applicable
Headers show

Commit Message

Sean Nyekjaer Jan. 20, 2017, 12:36 p.m.
Signed-off-by: Sean Nyekjaer <sean.nyekjaer@prevas.dk>
---
 drivers/rtc/Kconfig       |   7 +++
 drivers/rtc/rtc-pcf2127.c | 128 +++++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 129 insertions(+), 6 deletions(-)

Patch

diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index c8985be81d83..e8d7944a4018 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -806,6 +806,13 @@  config RTC_DRV_PCF2127_WDT
 	  If you say Y here you will get support for the
 	  watchdog timer in the NXP PCF2127 chip real-time clock chips.
 
+config RTC_DRV_PCF2127_TAMPER
+	bool "NXP PCF2127 tamper"
+	depends on RTC_DRV_PCF2127
+	help
+	  If you say Y here you will get support for the
+	  tamper device in the NXP PCF2127 chip real-time clock chips.
+
 config RTC_DRV_RV3029C2
 	tristate "Micro Crystal RV3029/3049"
 	depends on RTC_I2C_AND_SPI
diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
index 31627c59c44d..5f96cff9ebd5 100644
--- a/drivers/rtc/rtc-pcf2127.c
+++ b/drivers/rtc/rtc-pcf2127.c
@@ -30,7 +30,10 @@ 
 #endif
 
 #define PCF2127_REG_CTRL1       (0x00)  /* Control Register 1 */
+#define PCF2127_TSF1		BIT(4)
 #define PCF2127_REG_CTRL2       (0x01)  /* Control Register 2 */
+#define PCF2127_TSIE		BIT(2)
+#define PCF2127_TSF2		BIT(5)
 
 #define PCF2127_REG_CTRL3       (0x02)  /* Control Register 3 */
 #define PCF2127_REG_CTRL3_BLF		BIT(2)
@@ -42,6 +45,17 @@ 
 #define PCF2127_REG_WDG_T_TF_1HZ	BIT(1)	/* Timer clock source */
 #define PCF2127_REG_WDG_TIMVAL		(0x11)
 
+#define PCF2127_REG_TIMSTP_CTL		(0x12)
+#define PCF2127_TSM			BIT(7)
+#define PCF2127_TSOFF			BIT(6)
+
+#define PCF2127_REG_TIMSTP_SC		(0x01)
+#define PCF2127_REG_TIMSTP_MN		(0x02)
+#define PCF2127_REG_TIMSTP_HR		(0x03)
+#define PCF2127_REG_TIMSTP_DM		(0x04)
+#define PCF2127_REG_TIMSTP_MO		(0x05)
+#define PCF2127_REG_TIMSTP_YR		(0x06)
+
 #define PCF2127_REG_SC          (0x03)  /* datetime */
 #define PCF2127_REG_MN          (0x04)
 #define PCF2127_REG_HR          (0x05)
@@ -55,10 +69,84 @@ 
 struct pcf2127 {
 	struct rtc_device *rtc;
 	struct regmap *regmap;
+	struct rtc_time tamper_event;
+	bool tamper_enabled;
+	int irq;
 };
 
-#ifdef CONFIG_RTC_DRV_PCF2127_WDT
+#if defined(CONFIG_RTC_DRV_PCF2127_TAMPER) || defined(CONFIG_RTC_DRV_PCF2127_WDT)
 static struct pcf2127 *save_pcf2127;
+#endif
+
+#ifdef CONFIG_RTC_DRV_PCF2127_TAMPER
+static int pcf2127_tamper_event_print(struct device *dev)
+{
+	int err;
+	unsigned char buf[7];
+	struct rtc_time *tm;
+
+	err = regmap_bulk_read(save_pcf2127->regmap, PCF2127_REG_TIMSTP_CTL,
+			       buf, sizeof(buf));
+	if (err)
+		return err;
+
+	tm = &save_pcf2127->tamper_event;
+
+	tm->tm_sec = bcd2bin(buf[PCF2127_REG_TIMSTP_SC] & 0x7F);
+	tm->tm_min = bcd2bin(buf[PCF2127_REG_TIMSTP_MN] & 0x7F);
+	tm->tm_hour = bcd2bin(buf[PCF2127_REG_TIMSTP_HR] & 0x3F);
+	tm->tm_mday = bcd2bin(buf[PCF2127_REG_TIMSTP_DM] & 0x3F);
+	tm->tm_mon = bcd2bin(buf[PCF2127_REG_TIMSTP_MO] & 0x1F) - 1;
+	tm->tm_year = bcd2bin(buf[PCF2127_REG_TIMSTP_YR]);
+
+	dev_emerg(dev, "%s: Tamper detected at secs=%d, mins=%d, hours=%d, "
+		  "mday=%d, mon=%d, year=%d\n",
+		  __func__,
+		  tm->tm_sec, tm->tm_min, tm->tm_hour,
+		  tm->tm_mday, tm->tm_mon, tm->tm_year);
+
+	return 0;
+}
+
+static int pcf2127_tamper_enable(void)
+{
+	int err;
+
+	/* Enable interrupt on tamper event */
+	err = regmap_write_bits(save_pcf2127->regmap, PCF2127_REG_CTRL2,
+				PCF2127_TSIE, PCF2127_TSIE);
+	if (err)
+		return err;
+
+	/* Enable timestamp function */
+	err = regmap_write_bits(save_pcf2127->regmap, PCF2127_REG_TIMSTP_CTL,
+				PCF2127_TSM | PCF2127_TSOFF, PCF2127_TSM);
+	if (err)
+		return err;
+
+	save_pcf2127->tamper_enabled = true;
+
+	return 0;
+}
+
+static irqreturn_t pcf2127_tamper_event_irq(int irq, void *data)
+{
+	struct device *dev = data;
+	struct pcf2127 *pcf2127 = dev_get_drvdata(dev);
+
+	pcf2127_tamper_event_print(dev);
+
+	/* Clear interrupt pin */
+	regmap_write_bits(pcf2127->regmap, PCF2127_REG_CTRL1,
+			PCF2127_TSF1, 0);
+	regmap_write_bits(pcf2127->regmap, PCF2127_REG_CTRL2,
+			PCF2127_TSF2, 0);
+
+	return IRQ_HANDLED;
+}
+#endif
+
+#ifdef CONFIG_RTC_DRV_PCF2127_WDT
 
 /* default 32 sec timeout */
 #define WD_TIMO 32
@@ -219,6 +307,11 @@  static int pcf2127_rtc_read_time(struct device *dev, struct rtc_time *tm)
 			 "oscillator stop detected, date/time is not reliable\n");
 		return -EINVAL;
 	}
+#ifdef CONFIG_RTC_DRV_PCF2127_TAMPER
+	if (buf[PCF2127_REG_CTRL1] & PCF2127_TSF1) {
+		pcf2127_tamper_event_print(dev);
+	}
+#endif
 
 	dev_dbg(dev,
 		"%s: raw data is cr1=%02x, cr2=%02x, cr3=%02x, "
@@ -319,9 +412,9 @@  static const struct rtc_class_ops pcf2127_rtc_ops = {
 };
 
 static int pcf2127_probe(struct device *dev, struct regmap *regmap,
-			const char *name)
+			 const char *name, int irq)
 {
-#ifdef CONFIG_RTC_DRV_PCF2127_WDT
+#if defined(CONFIG_RTC_DRV_PCF2127_TAMPER) || defined(CONFIG_RTC_DRV_PCF2127_WDT)
 	int ret;
 #endif
 	struct pcf2127 *pcf2127;
@@ -332,7 +425,7 @@  static int pcf2127_probe(struct device *dev, struct regmap *regmap,
 	if (!pcf2127)
 		return -ENOMEM;
 
-#ifdef CONFIG_RTC_DRV_PCF2127_WDT
+#if defined(CONFIG_RTC_DRV_PCF2127_TAMPER) || defined(CONFIG_RTC_DRV_PCF2127_WDT)
 	save_pcf2127 = pcf2127;
 #endif
 
@@ -358,6 +451,28 @@  static int pcf2127_probe(struct device *dev, struct regmap *regmap,
 	pcf2127_wdt_settimeout(32);
 #endif
 
+#ifdef CONFIG_RTC_DRV_PCF2127_TAMPER
+	pcf2127->tamper_enabled = false;
+
+	if (!of_device_is_compatible(dev->of_node, "nxp,pcf2127"))
+		return 0;
+
+	if (of_property_read_bool(dev->of_node, "tamper")) {
+		pcf2127_tamper_enable();
+
+		ret = devm_request_threaded_irq(dev, irq, NULL,
+						pcf2127_tamper_event_irq,
+						IRQF_ONESHOT | IRQF_TRIGGER_FALLING, dev_name(dev),
+						dev);
+		if (ret < 0) {
+			dev_err(dev, "IRQ is not free.\n");
+			return ret;
+		}
+
+		pcf2127->irq = irq;
+	}
+#endif
+
 	return 0;
 }
 
@@ -466,7 +581,7 @@  static int pcf2127_i2c_probe(struct i2c_client *client,
 	}
 
 	return pcf2127_probe(&client->dev, regmap,
-				pcf2127_i2c_driver.driver.name);
+				pcf2127_i2c_driver.driver.name, client->irq);
 }
 
 static const struct i2c_device_id pcf2127_i2c_id[] = {
@@ -529,7 +644,8 @@  static int pcf2127_spi_probe(struct spi_device *spi)
 		return PTR_ERR(regmap);
 	}
 
-	return pcf2127_probe(&spi->dev, regmap, pcf2127_spi_driver.driver.name);
+	return pcf2127_probe(&spi->dev, regmap, pcf2127_spi_driver.driver.name,
+			     spi->irq);
 }
 
 static const struct spi_device_id pcf2127_spi_id[] = {