Re: [PATCH 4/4] rtc: isl1208: add support for isl1219 with hwmon for tamper detection

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




On Tue, Jan 23, 2018 at 10:22:54AM -0800, Guenter Roeck wrote:
> On Tue, Jan 23, 2018 at 01:18:01PM +0100, Michael Grzeschik wrote:
> > We add support for the ISL1219 chip that got an integrated tamper
> > detection function. This patch implements the feature by using an hwmon
> > interface.
> > 
> > The ISL1219 can also describe the timestamp of the intrusion
> > event. For this we add the documentation of the new interface
> > intrusion[0-*]_timestamp.
> > 
> > The devicetree documentation for the ISL1219 device tree
> > binding is added with an short example.
> > 
> > Signed-off-by: Michael Grzeschik <m.grzeschik@xxxxxxxxxxxxxx>
> > Signed-off-by: Denis Osterland <Denis.Osterland@xxxxxxxxx>
> > ---
> >  .../rtc/{intersil,isl1208.txt => isil,isl1208.txt} |  18 +-
> >  Documentation/hwmon/sysfs-interface                |   7 +
> >  drivers/rtc/rtc-isl1208.c                          | 190 +++++++++++++++++++--
> >  3 files changed, 201 insertions(+), 14 deletions(-)
> >  rename Documentation/devicetree/bindings/rtc/{intersil,isl1208.txt => isil,isl1208.txt} (57%)
> > 
> > diff --git a/Documentation/devicetree/bindings/rtc/intersil,isl1208.txt b/Documentation/devicetree/bindings/rtc/isil,isl1208.txt
> > similarity index 57%
> > rename from Documentation/devicetree/bindings/rtc/intersil,isl1208.txt
> > rename to Documentation/devicetree/bindings/rtc/isil,isl1208.txt
> > index a54e99feae1ca..d549699e1cfc4 100644
> > --- a/Documentation/devicetree/bindings/rtc/intersil,isl1208.txt
> > +++ b/Documentation/devicetree/bindings/rtc/isil,isl1208.txt
> > @@ -1,14 +1,21 @@
> > -Intersil ISL1208, ISL1218 I2C RTC/Alarm chip
> > +Intersil ISL1208, ISL1218, ISL1219 I2C RTC/Alarm chip
> >  
> >  ISL1208 is a trivial I2C device (it has simple device tree bindings,
> >  consisting of a compatible field, an address and possibly an interrupt
> >  line).
> >  
> > +ISL1219 supports tamper detection user space representation through
> > +case intrusion hwmon sensor.
> > +ISL1219 has additional pins EVIN and #EVDET for tamper detection.
> > +I2C devices support only one irq. #IRQ and #EVDET are open-drain active low,
> > +so it is possible layout them to one SoC pin with pull-up.
> > +
> >  Required properties supported by the device:
> >  
> >   - "compatible": must be one of
> >  	"isil,isl1208"
> >  	"isil,isl1218"
> > +	"isil,isl1219"
> >   - "reg": I2C bus address of the device
> >  
> >  Optional properties:
> > @@ -33,3 +40,12 @@ Example isl1208 node with #IRQ pin connected to SoC gpio1 pin 12:
> >  		interrupt-parent = <&gpio1>;
> >  		interrupts = <12 IRQ_TYPE_EDGE_FALLING>;
> >  	};
> > +
> > +Example isl1219 node with #IRQ pin and #EVDET pin connected to SoC gpio1 pin 12:
> > +
> > +	isl1219: isl1219@68 {
> > +		compatible = "intersil,isl1219";
> > +		reg = <0x68>;
> > +		interrupts-extended = <&gpio1 12 IRQ_TYPE_EDGE_FALLING>;
> > +	};
> > +
> > diff --git a/Documentation/hwmon/sysfs-interface b/Documentation/hwmon/sysfs-interface
> > index fc337c317c673..a12b3c2b2a18c 100644
> > --- a/Documentation/hwmon/sysfs-interface
> > +++ b/Documentation/hwmon/sysfs-interface
> > @@ -702,6 +702,13 @@ intrusion[0-*]_alarm
> >  		the user. This is done by writing 0 to the file. Writing
> >  		other values is unsupported.
> >  
> > +intrusion[0-*]_timestamp
> > +		Chassis intrusion detection
> > +		YYYY-MM-DD HH:MM:SS UTC (ts.sec): intrusion detected
> > +		RO
> > +		The corresponding timestamp on which the intrustion
> > +		was detected.
> > +
> 
> Sneaky. Nack. You don't just add attributes to the ABI because you want it,
> without serious discussion, and much less so hidden in an RTC driver
> (and even less as unparseable attribute).

Right; but it was not meant to be sneaky. I should have stick to my first
thought and label this patch RFC. Sorry for that.

> In addition to that, I consider the attribute unnecessary. The intrusion
> already generates an event which should be sufficient for all practical
> purposes.

Would it make sense in between the other sysfs attributes of this driver?

Thanks,
Michael

> >  intrusion[0-*]_beep
> >  		Chassis intrusion beep
> >  		0: disable
> > diff --git a/drivers/rtc/rtc-isl1208.c b/drivers/rtc/rtc-isl1208.c
> > index a13a4ba79004d..6e4d24614d98b 100644
> > --- a/drivers/rtc/rtc-isl1208.c
> > +++ b/drivers/rtc/rtc-isl1208.c
> > @@ -14,6 +14,8 @@
> >  #include <linux/i2c.h>
> >  #include <linux/bcd.h>
> >  #include <linux/rtc.h>
> > +#include <linux/hwmon.h>
> > +#include <linux/hwmon-sysfs.h>
> >  
> >  /* Register map */
> >  /* rtc section */
> > @@ -33,6 +35,7 @@
> >  #define ISL1208_REG_SR_ARST    (1<<7)	/* auto reset */
> >  #define ISL1208_REG_SR_XTOSCB  (1<<6)	/* crystal oscillator */
> >  #define ISL1208_REG_SR_WRTC    (1<<4)	/* write rtc */
> > +#define ISL1208_REG_SR_EVT     (1<<3)	/* event */
> >  #define ISL1208_REG_SR_ALM     (1<<2)	/* alarm */
> >  #define ISL1208_REG_SR_BAT     (1<<1)	/* battery */
> >  #define ISL1208_REG_SR_RTCF    (1<<0)	/* rtc fail */
> > @@ -57,8 +60,29 @@
> >  #define ISL1208_REG_USR2 0x13
> >  #define ISL1208_USR_SECTION_LEN 2
> >  
> > +/* event section */
> > +#define ISL1208_REG_SCT 0x14
> > +#define ISL1208_REG_MNT 0x15
> > +#define ISL1208_REG_HRT 0x16
> > +#define ISL1208_REG_DTT 0x17
> > +#define ISL1208_REG_MOT 0x18
> > +#define ISL1208_REG_YRT 0x19
> > +#define ISL1208_EVT_SECTION_LEN 6
> > +
> >  static struct i2c_driver isl1208_driver;
> >  
> > +/* ISL1208 various variants */
> > +enum {
> > +	TYPE_ISL1208 = 0,
> > +	TYPE_ISL1218,
> > +	TYPE_ISL1219,
> > +};
> > +
> > +struct isl1208 {
> > +	struct rtc_device *rtc;
> > +	struct device *hwmon;
> > +};
> > +
> >  /* block read */
> >  static int
> >  isl1208_i2c_read_regs(struct i2c_client *client, u8 reg, u8 buf[],
> > @@ -80,8 +104,8 @@ isl1208_i2c_read_regs(struct i2c_client *client, u8 reg, u8 buf[],
> >  	};
> >  	int ret;
> >  
> > -	BUG_ON(reg > ISL1208_REG_USR2);
> > -	BUG_ON(reg + len > ISL1208_REG_USR2 + 1);
> > +	WARN_ON(reg > ISL1208_REG_YRT);
> > +	WARN_ON(reg + len > ISL1208_REG_YRT + 1);
> >  
> >  	ret = i2c_transfer(client->adapter, msgs, 2);
> >  	if (ret > 0)
> > @@ -104,8 +128,8 @@ isl1208_i2c_set_regs(struct i2c_client *client, u8 reg, u8 const buf[],
> >  	};
> >  	int ret;
> >  
> > -	BUG_ON(reg > ISL1208_REG_USR2);
> > -	BUG_ON(reg + len > ISL1208_REG_USR2 + 1);
> > +	WARN_ON(reg > ISL1208_REG_YRT);
> > +	WARN_ON(reg + len > ISL1208_REG_YRT + 1);
> >  
> >  	i2c_buf[0] = reg;
> >  	memcpy(&i2c_buf[1], &buf[0], len);
> > @@ -493,12 +517,128 @@ isl1208_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
> >  	return isl1208_i2c_set_alarm(to_i2c_client(dev), alarm);
> >  }
> >  
> > +static ssize_t isl1208_hwmon_show_tamper(struct device *dev,
> > +				struct device_attribute *attr, char *buf)
> > +{
> > +	struct i2c_client *client = dev_get_drvdata(dev);
> > +	int sr;
> > +
> > +	sr = isl1208_i2c_get_sr(client);
> > +	if (sr < 0) {
> > +		dev_err(dev, "%s: reading SR failed\n", __func__);
> > +		return sr;
> > +	}
> > +
> > +	if (sr & ISL1208_REG_SR_EVT)
> > +		return sprintf(buf, "1\n");
> > +
> > +	return sprintf(buf, "0\n");
> > +};
> > +
> > +static ssize_t isl1208_hwmon_clear_caseopen(struct device *dev,
> > +		struct device_attribute *attr,
> > +		const char *buf, size_t count)
> > +{
> > +	struct i2c_client *client = dev_get_drvdata(dev);
> > +	unsigned long val;
> > +	int sr;
> > +
> > +	if (kstrtoul(buf, 10, &val) || val != 0)
> > +		return -EINVAL;
> > +
> > +	sr = isl1208_i2c_get_sr(client);
> > +	if (sr < 0) {
> > +		dev_err(dev, "%s: reading SR failed\n", __func__);
> > +		return sr;
> > +	}
> > +
> > +	sr &= ~ISL1208_REG_SR_EVT;
> > +
> > +	sr = i2c_smbus_write_byte_data(client, ISL1208_REG_SR, sr);
> > +	if (sr < 0)
> > +		dev_err(dev, "%s: writing SR failed\n",
> > +			__func__);
> > +
> > +	return count;
> > +};
> > +
> > +static ssize_t isl1208_hwmon_show_tamper_timestamp(struct device *dev,
> > +				struct device_attribute *attr, char *buf)
> > +{
> > +	struct i2c_client *client = dev_get_drvdata(dev);
> > +	u8 regs[ISL1208_EVT_SECTION_LEN] = { 0, };
> > +	struct timespec64 tv64;
> > +	struct rtc_time tm;
> > +	int sr;
> > +
> > +	sr = isl1208_i2c_get_sr(client);
> > +	if (sr < 0) {
> > +		dev_err(dev, "%s: reading SR failed\n", __func__);
> > +		return sr;
> > +	}
> > +
> > +	if (!(sr & ISL1208_REG_SR_EVT))
> > +		return 0;
> > +
> > +	sr = isl1208_i2c_read_regs(client, ISL1208_REG_SCT, regs,
> > +				   ISL1208_EVT_SECTION_LEN);
> > +	if (sr < 0) {
> > +		dev_err(dev, "%s: reading event section failed\n",
> > +			__func__);
> > +		return 0;
> > +	}
> > +
> > +	/* MSB of each alarm register is an enable bit */
> > +	tm.tm_sec = bcd2bin(regs[ISL1208_REG_SCT - ISL1208_REG_SCT] & 0x7f);
> > +	tm.tm_min = bcd2bin(regs[ISL1208_REG_MNT - ISL1208_REG_SCT] & 0x7f);
> > +	tm.tm_hour = bcd2bin(regs[ISL1208_REG_HRT - ISL1208_REG_SCT] & 0x3f);
> > +	tm.tm_mday = bcd2bin(regs[ISL1208_REG_DTT - ISL1208_REG_SCT] & 0x3f);
> > +	tm.tm_mon =
> > +		bcd2bin(regs[ISL1208_REG_MOT - ISL1208_REG_SCT] & 0x1f) - 1;
> > +	tm.tm_year = bcd2bin(regs[ISL1208_REG_YRT - ISL1208_REG_SCT]) + 100;
> > +
> > +	tv64.tv_sec = rtc_tm_to_time64(&tm);
> > +
> > +	return sprintf(buf,
> > +		"%d-%02d-%02d %02d:%02d:%02d UTC (%lld)\n",
> > +		tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
> > +		tm.tm_hour, tm.tm_min, tm.tm_sec,
> > +		(long long) tv64.tv_sec);
> > +};
> > +
> > +static SENSOR_DEVICE_ATTR(intrusion0_alarm, 0644, isl1208_hwmon_show_tamper,
> > +			isl1208_hwmon_clear_caseopen, 0);
> > +
> > +static SENSOR_DEVICE_ATTR(intrusion0_timestamp, 0444,
> > +		isl1208_hwmon_show_tamper_timestamp, NULL, 0);
> > +
> > +static struct attribute *isl1208_hwmon_attrs[] = {
> > +	&sensor_dev_attr_intrusion0_alarm.dev_attr.attr,
> > +	&sensor_dev_attr_intrusion0_timestamp.dev_attr.attr,
> > +	NULL,
> > +};
> > +ATTRIBUTE_GROUPS(isl1208_hwmon);
> > +
> > +static void isl1208_hwmon_register(struct i2c_client *client)
> > +{
> > +	struct isl1208 *isl1208 = i2c_get_clientdata(client);
> > +
> > +	isl1208->hwmon = devm_hwmon_device_register_with_groups(&client->dev,
> > +						client->name,
> > +						client, isl1208_hwmon_groups);
> > +	if (IS_ERR(isl1208->hwmon)) {
> > +		dev_warn(&client->dev,
> > +			"unable to register hwmon device %ld\n",
> > +			PTR_ERR(isl1208->hwmon));
> > +	}
> > +}
> > +
> >  static irqreturn_t
> >  isl1208_rtc_interrupt(int irq, void *data)
> >  {
> >  	unsigned long timeout = jiffies + msecs_to_jiffies(1000);
> >  	struct i2c_client *client = data;
> > -	struct rtc_device *rtc = i2c_get_clientdata(client);
> > +	struct isl1208 *isl1208 = i2c_get_clientdata(client);
> >  	int handled = 0, sr, err;
> >  
> >  	/*
> > @@ -521,7 +661,7 @@ isl1208_rtc_interrupt(int irq, void *data)
> >  	if (sr & ISL1208_REG_SR_ALM) {
> >  		dev_dbg(&client->dev, "alarm!\n");
> >  
> > -		rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF);
> > +		rtc_update_irq(isl1208->rtc, 1, RTC_IRQF | RTC_AF);
> >  
> >  		/* Clear the alarm */
> >  		sr &= ~ISL1208_REG_SR_ALM;
> > @@ -538,6 +678,13 @@ isl1208_rtc_interrupt(int irq, void *data)
> >  			return err;
> >  	}
> >  
> > +	if (isl1208->hwmon && (sr & ISL1208_REG_SR_EVT)) {
> > +		sysfs_notify(&isl1208->hwmon->kobj, NULL,
> > +			sensor_dev_attr_intrusion0_alarm.dev_attr.attr.name);
> > +		dev_warn(&client->dev, "event detected");
> > +		handled = 1;
> > +	}
> > +
> >  	return handled ? IRQ_HANDLED : IRQ_NONE;
> >  }
> >  
> > @@ -627,7 +774,7 @@ static int
> >  isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id)
> >  {
> >  	int rc = 0;
> > -	struct rtc_device *rtc;
> > +	struct isl1208 *isl1208;
> >  
> >  	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
> >  		return -ENODEV;
> > @@ -635,13 +782,19 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id)
> >  	if (isl1208_i2c_validate_client(client) < 0)
> >  		return -ENODEV;
> >  
> > -	rtc = devm_rtc_device_register(&client->dev, isl1208_driver.driver.name,
> > +	isl1208 = devm_kzalloc(&client->dev, sizeof(struct isl1208),
> > +				GFP_KERNEL);
> > +	if (!isl1208)
> > +		return -ENOMEM;
> > +
> > +	isl1208->rtc = devm_rtc_device_register(&client->dev,
> > +				  isl1208_driver.driver.name,
> >  				  &isl1208_rtc_ops,
> >  				  THIS_MODULE);
> > -	if (IS_ERR(rtc))
> > -		return PTR_ERR(rtc);
> > +	if (IS_ERR(isl1208->rtc))
> > +		return PTR_ERR(isl1208->rtc);
> >  
> > -	i2c_set_clientdata(client, rtc);
> > +	i2c_set_clientdata(client, isl1208);
> >  
> >  	rc = isl1208_i2c_get_sr(client);
> >  	if (rc < 0) {
> > @@ -657,6 +810,15 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id)
> >  	if (rc)
> >  		return rc;
> >  
> > +	if (id->driver_data == TYPE_ISL1219) {
> > +		rc = i2c_smbus_write_byte_data(client, ISL1208_REG_09, 0x10);
> > +		if (rc < 0) {
> > +			dev_err(&client->dev, "could not enable tamper detection\n");
> > +			return rc;
> > +		}
> > +		isl1208_hwmon_register(client);
> > +	}
> > +
> >  	if (client->irq > 0) {
> >  		rc = devm_request_threaded_irq(&client->dev, client->irq, NULL,
> >  					       isl1208_rtc_interrupt,
> > @@ -686,8 +848,9 @@ isl1208_remove(struct i2c_client *client)
> >  }
> >  
> >  static const struct i2c_device_id isl1208_id[] = {
> > -	{ "isl1208", 0 },
> > -	{ "isl1218", 0 },
> > +	{ "isl1208", TYPE_ISL1208 },
> > +	{ "isl1218", TYPE_ISL1218 },
> > +	{ "isl1219", TYPE_ISL1219 },
> >  	{ }
> >  };
> >  MODULE_DEVICE_TABLE(i2c, isl1208_id);
> > @@ -695,6 +858,7 @@ MODULE_DEVICE_TABLE(i2c, isl1208_id);
> >  static const struct of_device_id isl1208_of_match[] = {
> >  	{ .compatible = "isil,isl1208" },
> >  	{ .compatible = "isil,isl1218" },
> > +	{ .compatible = "isil,isl1219" },
> >  	{ }
> >  };
> >  MODULE_DEVICE_TABLE(of, isl1208_of_match);
> > -- 
> > 2.11.0
> > 
> 

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

Attachment: signature.asc
Description: PGP signature


[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]


  Powered by Linux