Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > linux.kernel > #1322612 > unrolled thread

Re: [PATCH v2 7/8] rtc: implement a sysfs interface for clock offset

Started byAlexandre Belloni <alexandre.belloni@free-electrons.com>
First post2016-01-31 12:50 +0100
Last post2016-02-05 21:50 +0100
Articles 13 — 2 participants

Back to article view | Back to linux.kernel

This discussion starts older than the indexed window; earlier articles aren't shown. The article labeled Started by below is the oldest one visible, not the original post.


Contents

  Re: [PATCH v2 7/8] rtc: implement a sysfs interface for clock offset Alexandre Belloni <alexandre.belloni@free-electrons.com> - 2016-01-31 12:50 +0100
    Re: [PATCH v2 7/8] rtc: implement a sysfs interface for clock  offset Joshua Clayton <stillcompiling@gmail.com> - 2016-02-01 22:00 +0100
      Re: [PATCH v2 7/8] rtc: implement a sysfs interface for clock offset Alexandre Belloni <alexandre.belloni@free-electrons.com> - 2016-02-02 11:50 +0100
        [PATCH v3 1/3] rtc: Add functions to set and read rtc offset Joshua Clayton <stillcompiling@gmail.com> - 2016-02-03 18:20 +0100
          [PATCH v3 3/3] rtc-pcf2123: implement read_offset and set_offset Joshua Clayton <stillcompiling@gmail.com> - 2016-02-03 18:20 +0100
          [PATCH v3 2/3] rtc: implement a sysfs interface for clock offset Joshua Clayton <stillcompiling@gmail.com> - 2016-02-03 18:20 +0100
            Re: [PATCH v3 2/3] rtc: implement a sysfs interface for clock offset Alexandre Belloni <alexandre.belloni@free-electrons.com> - 2016-02-04 23:20 +0100
          Re: [PATCH v3 1/3] rtc: Add functions to set and read rtc offset Alexandre Belloni <alexandre.belloni@free-electrons.com> - 2016-02-04 23:10 +0100
            Re: [PATCH v3 1/3] rtc: Add functions to set and read rtc offset Joshua Clayton <stillcompiling@gmail.com> - 2016-02-05 00:40 +0100
              Re: [PATCH v3 1/3] rtc: Add functions to set and read rtc offset Alexandre Belloni <alexandre.belloni@free-electrons.com> - 2016-02-05 15:40 +0100
                [PATCH v4 1/3] rtc: Add functions to set and read rtc offset Joshua Clayton <stillcompiling@gmail.com> - 2016-02-05 21:50 +0100
                  [PATCH v4 2/3] rtc: implement a sysfs interface for clock offset Joshua Clayton <stillcompiling@gmail.com> - 2016-02-05 21:50 +0100
                  [PATCH v4 3/3] rtc-pcf2123: implement read_offset and set_offset Joshua Clayton <stillcompiling@gmail.com> - 2016-02-05 21:50 +0100

#1322612 — Re: [PATCH v2 7/8] rtc: implement a sysfs interface for clock offset

FromAlexandre Belloni <alexandre.belloni@free-electrons.com>
Date2016-01-31 12:50 +0100
SubjectRe: [PATCH v2 7/8] rtc: implement a sysfs interface for clock offset
Message-ID<qWYFY-2GE-11@gated-at.bofh.it>
Hi,

On 04/01/2016 at 10:31:25 -0800, Joshua Clayton wrote :
> The file is called "offset", and may be set and read in decimal.
> For rtcs that do not have read_offset or set_offset implemented,
> read always returns zero and write will return -EINVAL.
> 
Can you expand rtc_attr_is_visible() instead of having the offset file
always present?

Also, you need to expand Documentation/rtc.txt, section SYSFS INTERFACE.

I'm fine with having parts per billion as the unit and I hope we won't
ever need anything more precise :)

> Signed-off-by: Joshua Clayton <stillcompiling@gmail.com>
> ---
>  drivers/rtc/interface.c |  2 +-
>  drivers/rtc/rtc-sysfs.c | 29 +++++++++++++++++++++++++++++
>  2 files changed, 30 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c
> index 57cd928..c064eb9 100644
> --- a/drivers/rtc/interface.c
> +++ b/drivers/rtc/interface.c
> @@ -959,7 +959,7 @@ int rtc_read_offset(struct rtc_device *rtc, long *offset)
>  		return -ENODEV;
>  
>  	if (!rtc->ops->set_offset) {
> -		offset = 0;
> +		*offset = 0;
>  		return 0;
>  	}

>  
> diff --git a/drivers/rtc/rtc-sysfs.c b/drivers/rtc/rtc-sysfs.c
> index 7273855..622a0dc 100644
> --- a/drivers/rtc/rtc-sysfs.c
> +++ b/drivers/rtc/rtc-sysfs.c
> @@ -211,6 +211,34 @@ wakealarm_store(struct device *dev, struct device_attribute *attr,
>  }
>  static DEVICE_ATTR_RW(wakealarm);
>  
> +static ssize_t
> +offset_show(struct device *dev, struct device_attribute *attr, char *buf)
> +{
> +	ssize_t retval;
> +	long offset;
> +
> +	retval = rtc_read_offset(to_rtc_device(dev), &offset);
> +	if (retval == 0)
> +		retval = sprintf(buf, "%ld\n", offset);
> +
> +	return retval;
> +}
> +
> +static ssize_t
> +offset_store(struct device *dev, struct device_attribute *attr,
> +		const char *buf, size_t n)
> +{
> +	ssize_t retval;
> +	long offset;
> +
> +	retval = kstrtol(buf, 10, &offset);
> +	if (retval == 0)
> +		retval = rtc_set_offset(to_rtc_device(dev), offset);
> +
> +	return (retval < 0) ? retval : n;
> +}
> +static DEVICE_ATTR_RW(offset);
> +
>  static struct attribute *rtc_attrs[] = {
>  	&dev_attr_name.attr,
>  	&dev_attr_date.attr,
> @@ -219,6 +247,7 @@ static struct attribute *rtc_attrs[] = {
>  	&dev_attr_max_user_freq.attr,
>  	&dev_attr_hctosys.attr,
>  	&dev_attr_wakealarm.attr,
> +	&dev_attr_offset.attr,
>  	NULL,
>  };
>  

-- 
Alexandre Belloni, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com

[toc] | [next] | [standalone]


#1323500 — Re: [PATCH v2 7/8] rtc: implement a sysfs interface for clock offset

FromJoshua Clayton <stillcompiling@gmail.com>
Date2016-02-01 22:00 +0100
SubjectRe: [PATCH v2 7/8] rtc: implement a sysfs interface for clock offset
Message-ID<qXtJL-8pU-3@gated-at.bofh.it>
In reply to#1322612
On Sun, 31 Jan 2016 12:41:15 +0100
Alexandre Belloni <alexandre.belloni@free-electrons.com> wrote:

> Hi,
> 
> On 04/01/2016 at 10:31:25 -0800, Joshua Clayton wrote :
> > The file is called "offset", and may be set and read in decimal.
> > For rtcs that do not have read_offset or set_offset implemented,
> > read always returns zero and write will return -EINVAL.
> > 
> Can you expand rtc_attr_is_visible() instead of having the offset file
> always present?

Yes, Absolutely.
I wanted to do something like this, but didn't know is_visible
existed... and no wonder. Its in attribute_group

> 
> Also, you need to expand Documentation/rtc.txt, section SYSFS
> INTERFACE.
> 
OK. Will do.

> I'm fine with having parts per billion as the unit and I hope we won't
> ever need anything more precise :)
> 
It is orders of magnitude more than needed at present
...and no will ever need more than 640k of memory... right?

[toc] | [prev] | [next] | [standalone]


#1323962

FromAlexandre Belloni <alexandre.belloni@free-electrons.com>
Date2016-02-02 11:50 +0100
Message-ID<qXGGZ-1oC-3@gated-at.bofh.it>
In reply to#1323500
On 01/02/2016 at 12:56:48 -0800, Joshua Clayton wrote :
> > I'm fine with having parts per billion as the unit and I hope we won't
> > ever need anything more precise :)
> > 
> It is orders of magnitude more than needed at present

Yeah, until we get an RTC with an offset precision that has 4 decimals in ppm.

> ...and no will ever need more than 640k of memory... right?

Right :)

-- 
Alexandre Belloni, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com

[toc] | [prev] | [next] | [standalone]


#1325705 — [PATCH v3 1/3] rtc: Add functions to set and read rtc offset

FromJoshua Clayton <stillcompiling@gmail.com>
Date2016-02-03 18:20 +0100
Subject[PATCH v3 1/3] rtc: Add functions to set and read rtc offset
Message-ID<qY9fY-4Sx-9@gated-at.bofh.it>
In reply to#1323962
A number of rtc devices, such as the NXP pcf2123 include a facility
to adjust the clock in order to compensate for temperature or a
crystal, capacitor, etc, that results in the rtc clock not running
at exactly 32.768 kHz.

Data sheets I have seen refer to this as a clock offset, and measure it
in parts per million, however they often reference ppm to 2 digits of
precision, which makes integer ppm less than ideal.

We use parts per billion, which more than covers the precision needed
and works nicely within 32 bits

Signed-off-by: Joshua Clayton <stillcompiling@gmail.com>
---
Changes since v2:
- patches 1 - 5 from v2 were merged, so 6, 7 , and 8 become 1, 2, 3
- Added documentation of the sysfs interface in Documenbtation/rtc.txt
- Added code so that the offset attrbute does not appear on rtcs that
  do not support it.

 drivers/rtc/interface.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/rtc.h     |  4 ++++
 2 files changed, 61 insertions(+)

diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c
index 5836751..c064eb9 100644
--- a/drivers/rtc/interface.c
+++ b/drivers/rtc/interface.c
@@ -939,4 +939,61 @@ void rtc_timer_cancel(struct rtc_device *rtc, struct rtc_timer *timer)
 	mutex_unlock(&rtc->ops_lock);
 }
 
+/**
+ * rtc_read_offset - Read the amount of rtc offset in parts per billion
+ * @ rtc: rtc device to be used
+ * @ offset: the offset in parts per billion
+ *
+ * see below for details.
+ *
+ * Kernel interface to read rtc clock offset
+ * Returns 0 on success, or a negative number on error.
+ * If the rtc offset is not setable (or not implemented), return 0 and put
+ * 0 in the offset value;
+ */
+int rtc_read_offset(struct rtc_device *rtc, long *offset)
+{
+	int ret = 0;
+
+	if (!rtc->ops)
+		return -ENODEV;
+
+	if (!rtc->ops->set_offset) {
+		*offset = 0;
+		return 0;
+	}
+
+	mutex_lock(&rtc->ops_lock);
+	ret = rtc->ops->read_offset(rtc->dev.parent, offset);
+	mutex_unlock(&rtc->ops_lock);
+	return ret;
+}
 
+/**
+ * rtc_set_offset - Adjusts the duration of the average second
+ * @ rtc: rtc device to be used
+ * @ offset: the offset in parts per billion
+ *
+ * Some rtc's allow an adjustment to the average duration of a second
+ * to compensate for differences in the actual clock rate due to temperature,
+ * the crystal, capacitor, etc.
+ *
+ * Kernel interface to adjust an rtc clock offset.
+ * Return 0 on success, or a negative number on error.
+ * If the rtc offset is not setable (or not implemented), return -EINVAL
+ */
+int rtc_set_offset(struct rtc_device *rtc, long offset)
+{
+	int ret = 0;
+
+	if (!rtc->ops)
+		return -ENODEV;
+
+	if (!rtc->ops->set_offset)
+		return -EINVAL;
+
+	mutex_lock(&rtc->ops_lock);
+	ret = rtc->ops->set_offset(rtc->dev.parent, offset);
+	mutex_unlock(&rtc->ops_lock);
+	return ret;
+}
diff --git a/include/linux/rtc.h b/include/linux/rtc.h
index 3359f04..b693ada 100644
--- a/include/linux/rtc.h
+++ b/include/linux/rtc.h
@@ -89,6 +89,8 @@ struct rtc_class_ops {
 	int (*set_mmss)(struct device *, unsigned long secs);
 	int (*read_callback)(struct device *, int data);
 	int (*alarm_irq_enable)(struct device *, unsigned int enabled);
+	int (*read_offset)(struct device *, long *offset);
+	int (*set_offset)(struct device *, long offset);
 };
 
 #define RTC_DEVICE_NAME_SIZE 20
@@ -208,6 +210,8 @@ void rtc_timer_init(struct rtc_timer *timer, void (*f)(void *p), void *data);
 int rtc_timer_start(struct rtc_device *rtc, struct rtc_timer *timer,
 		    ktime_t expires, ktime_t period);
 void rtc_timer_cancel(struct rtc_device *rtc, struct rtc_timer *timer);
+int rtc_read_offset(struct rtc_device *rtc, long *offset);
+int rtc_set_offset(struct rtc_device *rtc, long offset);
 void rtc_timer_do_work(struct work_struct *work);
 
 static inline bool is_leap_year(unsigned int year)
-- 
2.5.0

[toc] | [prev] | [next] | [standalone]


#1325707 — [PATCH v3 3/3] rtc-pcf2123: implement read_offset and set_offset

FromJoshua Clayton <stillcompiling@gmail.com>
Date2016-02-03 18:20 +0100
Subject[PATCH v3 3/3] rtc-pcf2123: implement read_offset and set_offset
Message-ID<qY9fY-4Sx-13@gated-at.bofh.it>
In reply to#1325705
pcf2123 has an offset register, which can be used to make minor
adjustments to the clock rate to compensate for temperature or
a crystal that is not exactly right.

Signed-off-by: Joshua Clayton <stillcompiling@gmail.com>
---
 drivers/rtc/rtc-pcf2123.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 57 insertions(+)

diff --git a/drivers/rtc/rtc-pcf2123.c b/drivers/rtc/rtc-pcf2123.c
index d9a44ad..da27738 100644
--- a/drivers/rtc/rtc-pcf2123.c
+++ b/drivers/rtc/rtc-pcf2123.c
@@ -100,6 +100,7 @@
 /* PCF2123_REG_OFFSET BITS */
 #define OFFSET_SIGN_BIT		BIT(6)	/* 2's complement sign bit */
 #define OFFSET_COARSE		BIT(7)	/* Coarse mode offset */
+#define OFFSET_STEP		(2170)	/* Offset step in parts per billion */
 
 /* READ/WRITE ADDRESS BITS */
 #define PCF2123_WRITE		BIT(4)
@@ -206,6 +207,59 @@ static ssize_t pcf2123_store(struct device *dev, struct device_attribute *attr,
 	return count;
 }
 
+static int pcf2123_read_offset(struct device *dev, long *offset)
+{
+	int ret;
+	s8 reg;
+
+	ret = pcf2123_read(dev, PCF2123_REG_OFFSET, &reg, 1);
+	if (ret < 0)
+		return ret;
+
+	if (reg & OFFSET_COARSE)
+		reg <<= 1; /* multiply by 2 and sign extend */
+	else
+		reg |= (reg & OFFSET_SIGN_BIT) << 1; /* sign extend only */
+
+	*offset = ((long)reg) * OFFSET_STEP;
+
+	return 0;
+}
+
+/*
+ * The offset register is a 7 bit signed value with a coarse bit in bit 7.
+ * The main difference between the two is normal offset adjusts the first
+ * second of n minutes every other hour, with 61, 62 and 63 being shoved
+ * into the 60th minute.
+ * The coarse adjustment does the same, but every hour.
+ * the two overlap, with every even normal offset value corresponding
+ * to a coarse offset. Based on this algorithm, it seems that despite the
+ * name, coarse offset is a better fit for overlapping values.
+ */
+static int pcf2123_set_offset(struct device *dev, long offset)
+{
+	s8 reg;
+
+	if (offset > OFFSET_STEP * 127)
+		reg = 127;
+	else if (offset < OFFSET_STEP * -128)
+		reg = -128;
+	else
+		reg = (s8)((offset + (OFFSET_STEP >> 1)) / OFFSET_STEP);
+
+	/* choose fine offset only for odd values in the normal range */
+	if (reg & 1 && reg <= 63 && reg >= -64) {
+		/* Normal offset. Clear the coarse bit */
+		reg &= ~OFFSET_COARSE;
+	} else {
+		/* Coarse offset. Divide by 2 and set the coarse bit */
+		reg >>= 1;
+		reg |= OFFSET_COARSE;
+	}
+
+	return pcf2123_write_reg(dev, PCF2123_REG_OFFSET, reg);
+}
+
 static int pcf2123_rtc_read_time(struct device *dev, struct rtc_time *tm)
 {
 	u8 rxbuf[7];
@@ -314,6 +368,9 @@ static int pcf2123_reset(struct device *dev)
 static const struct rtc_class_ops pcf2123_rtc_ops = {
 	.read_time	= pcf2123_rtc_read_time,
 	.set_time	= pcf2123_rtc_set_time,
+	.read_offset	= pcf2123_read_offset,
+	.set_offset	= pcf2123_set_offset,
+
 };
 
 static int pcf2123_probe(struct spi_device *spi)
-- 
2.5.0

[toc] | [prev] | [next] | [standalone]


#1325714 — [PATCH v3 2/3] rtc: implement a sysfs interface for clock offset

FromJoshua Clayton <stillcompiling@gmail.com>
Date2016-02-03 18:20 +0100
Subject[PATCH v3 2/3] rtc: implement a sysfs interface for clock offset
Message-ID<qY9fZ-4Sx-29@gated-at.bofh.it>
In reply to#1325705
clock offset may be set and read in decimal parts per billion
attribute is /sys/class/rtc/rtcN/offset
The attribute is only visible for rtcs that have set_offset implemented.

Signed-off-by: Joshua Clayton <stillcompiling@gmail.com>
---
 Documentation/rtc.txt   |  6 ++++++
 drivers/rtc/rtc-sysfs.c | 35 ++++++++++++++++++++++++++++++++++-
 2 files changed, 40 insertions(+), 1 deletion(-)

diff --git a/Documentation/rtc.txt b/Documentation/rtc.txt
index 8446f1e..ddc3660 100644
--- a/Documentation/rtc.txt
+++ b/Documentation/rtc.txt
@@ -157,6 +157,12 @@ wakealarm:	 The time at which the clock will generate a system wakeup
 		 the epoch by default, or if there's a leading +, seconds in the
 		 future, or if there is a leading +=, seconds ahead of the current
 		 alarm.
+offset:		 The amount which the rtc clock has been adjusted in firmware.
+		 Visible only if the driver supports clock offset adjustment.
+		 The unit is parts per billion, i.e. The number of clock ticks
+		 which are added to or removed from the rtc's base clock per
+		 billion ticks. A positive value makes a day pass more slowly,
+		 longer, and a negative value makes a day pass more quickly.
 
 IOCTL INTERFACE
 ---------------
diff --git a/drivers/rtc/rtc-sysfs.c b/drivers/rtc/rtc-sysfs.c
index 463e286..f8d7f8d 100644
--- a/drivers/rtc/rtc-sysfs.c
+++ b/drivers/rtc/rtc-sysfs.c
@@ -218,6 +218,34 @@ wakealarm_store(struct device *dev, struct device_attribute *attr,
 }
 static DEVICE_ATTR_RW(wakealarm);
 
+static ssize_t
+offset_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	ssize_t retval;
+	long offset;
+
+	retval = rtc_read_offset(to_rtc_device(dev), &offset);
+	if (retval == 0)
+		retval = sprintf(buf, "%ld\n", offset);
+
+	return retval;
+}
+
+static ssize_t
+offset_store(struct device *dev, struct device_attribute *attr,
+		const char *buf, size_t n)
+{
+	ssize_t retval;
+	long offset;
+
+	retval = kstrtol(buf, 10, &offset);
+	if (retval == 0)
+		retval = rtc_set_offset(to_rtc_device(dev), offset);
+
+	return (retval < 0) ? retval : n;
+}
+static DEVICE_ATTR_RW(offset);
+
 static struct attribute *rtc_attrs[] = {
 	&dev_attr_name.attr,
 	&dev_attr_date.attr,
@@ -226,6 +254,7 @@ static struct attribute *rtc_attrs[] = {
 	&dev_attr_max_user_freq.attr,
 	&dev_attr_hctosys.attr,
 	&dev_attr_wakealarm.attr,
+	&dev_attr_offset.attr,
 	NULL,
 };
 
@@ -249,9 +278,13 @@ static umode_t rtc_attr_is_visible(struct kobject *kobj,
 	struct rtc_device *rtc = to_rtc_device(dev);
 	umode_t mode = attr->mode;
 
-	if (attr == &dev_attr_wakealarm.attr)
+	if (attr == &dev_attr_wakealarm.attr) {
 		if (!rtc_does_wakealarm(rtc))
 			mode = 0;
+	} else if (attr == &dev_attr_offset.attr) {
+		if (!rtc->ops->set_offset)
+			mode = 0;
+	}
 
 	return mode;
 }
-- 
2.5.0

[toc] | [prev] | [next] | [standalone]


#1327231 — Re: [PATCH v3 2/3] rtc: implement a sysfs interface for clock offset

FromAlexandre Belloni <alexandre.belloni@free-electrons.com>
Date2016-02-04 23:20 +0100
SubjectRe: [PATCH v3 2/3] rtc: implement a sysfs interface for clock offset
Message-ID<qYApP-887-5@gated-at.bofh.it>
In reply to#1325714
A really small nitpick below

On 03/02/2016 at 09:16:43 -0800, Joshua Clayton wrote :
> clock offset may be set and read in decimal parts per billion
> attribute is /sys/class/rtc/rtcN/offset
> The attribute is only visible for rtcs that have set_offset implemented.
> 
> Signed-off-by: Joshua Clayton <stillcompiling@gmail.com>
> ---
>  Documentation/rtc.txt   |  6 ++++++
>  drivers/rtc/rtc-sysfs.c | 35 ++++++++++++++++++++++++++++++++++-
>  2 files changed, 40 insertions(+), 1 deletion(-)
> 
> diff --git a/Documentation/rtc.txt b/Documentation/rtc.txt
> index 8446f1e..ddc3660 100644
> --- a/Documentation/rtc.txt
> +++ b/Documentation/rtc.txt
> @@ -157,6 +157,12 @@ wakealarm:	 The time at which the clock will generate a system wakeup
>  		 the epoch by default, or if there's a leading +, seconds in the
>  		 future, or if there is a leading +=, seconds ahead of the current
>  		 alarm.
> +offset:		 The amount which the rtc clock has been adjusted in firmware.
> +		 Visible only if the driver supports clock offset adjustment.
> +		 The unit is parts per billion, i.e. The number of clock ticks
> +		 which are added to or removed from the rtc's base clock per
> +		 billion ticks. A positive value makes a day pass more slowly,
> +		 longer, and a negative value makes a day pass more quickly.
>  
>  IOCTL INTERFACE
>  ---------------
> diff --git a/drivers/rtc/rtc-sysfs.c b/drivers/rtc/rtc-sysfs.c
> index 463e286..f8d7f8d 100644
> --- a/drivers/rtc/rtc-sysfs.c
> +++ b/drivers/rtc/rtc-sysfs.c
> @@ -218,6 +218,34 @@ wakealarm_store(struct device *dev, struct device_attribute *attr,
>  }
>  static DEVICE_ATTR_RW(wakealarm);
>  
> +static ssize_t
> +offset_show(struct device *dev, struct device_attribute *attr, char *buf)
> +{
> +	ssize_t retval;
> +	long offset;
> +
> +	retval = rtc_read_offset(to_rtc_device(dev), &offset);
> +	if (retval == 0)
> +		retval = sprintf(buf, "%ld\n", offset);
> +
> +	return retval;
> +}
> +
> +static ssize_t
> +offset_store(struct device *dev, struct device_attribute *attr,
> +		const char *buf, size_t n)

This is not properly aligned.

> +{
> +	ssize_t retval;
> +	long offset;
> +
> +	retval = kstrtol(buf, 10, &offset);
> +	if (retval == 0)
> +		retval = rtc_set_offset(to_rtc_device(dev), offset);
> +
> +	return (retval < 0) ? retval : n;
> +}
> +static DEVICE_ATTR_RW(offset);
> +
>  static struct attribute *rtc_attrs[] = {
>  	&dev_attr_name.attr,
>  	&dev_attr_date.attr,
> @@ -226,6 +254,7 @@ static struct attribute *rtc_attrs[] = {
>  	&dev_attr_max_user_freq.attr,
>  	&dev_attr_hctosys.attr,
>  	&dev_attr_wakealarm.attr,
> +	&dev_attr_offset.attr,
>  	NULL,
>  };
>  
> @@ -249,9 +278,13 @@ static umode_t rtc_attr_is_visible(struct kobject *kobj,
>  	struct rtc_device *rtc = to_rtc_device(dev);
>  	umode_t mode = attr->mode;
>  
> -	if (attr == &dev_attr_wakealarm.attr)
> +	if (attr == &dev_attr_wakealarm.attr) {
>  		if (!rtc_does_wakealarm(rtc))
>  			mode = 0;
> +	} else if (attr == &dev_attr_offset.attr) {
> +		if (!rtc->ops->set_offset)
> +			mode = 0;
> +	}
>  
>  	return mode;
>  }
> -- 
> 2.5.0
> 

-- 
Alexandre Belloni, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com

[toc] | [prev] | [next] | [standalone]


#1327221 — Re: [PATCH v3 1/3] rtc: Add functions to set and read rtc offset

FromAlexandre Belloni <alexandre.belloni@free-electrons.com>
Date2016-02-04 23:10 +0100
SubjectRe: [PATCH v3 1/3] rtc: Add functions to set and read rtc offset
Message-ID<qYAga-84a-13@gated-at.bofh.it>
In reply to#1325705
Hi,

On 03/02/2016 at 09:16:42 -0800, Joshua Clayton wrote :
> diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c
> index 5836751..c064eb9 100644
> --- a/drivers/rtc/interface.c
> +++ b/drivers/rtc/interface.c
> @@ -939,4 +939,61 @@ void rtc_timer_cancel(struct rtc_device *rtc, struct rtc_timer *timer)
>  	mutex_unlock(&rtc->ops_lock);
>  }
>  
> +/**
> + * rtc_read_offset - Read the amount of rtc offset in parts per billion
> + * @ rtc: rtc device to be used
> + * @ offset: the offset in parts per billion
> + *
> + * see below for details.
> + *
> + * Kernel interface to read rtc clock offset
> + * Returns 0 on success, or a negative number on error.
> + * If the rtc offset is not setable (or not implemented), return 0 and put
> + * 0 in the offset value;
> + */
> +int rtc_read_offset(struct rtc_device *rtc, long *offset)
> +{
> +	int ret = 0;
> +
> +	if (!rtc->ops)
> +		return -ENODEV;
> +
> +	if (!rtc->ops->set_offset) {
> +		*offset = 0;
> +		return 0;
> +	}
> +

I should have been clearer but this is not necessary anymore since the
sysfs interface will not always be present but you
should probably test rtc->ops->read_offset instead.

> +	mutex_lock(&rtc->ops_lock);
> +	ret = rtc->ops->read_offset(rtc->dev.parent, offset);
> +	mutex_unlock(&rtc->ops_lock);
> +	return ret;
> +}
>  
> +/**
> + * rtc_set_offset - Adjusts the duration of the average second
> + * @ rtc: rtc device to be used
> + * @ offset: the offset in parts per billion
> + *
> + * Some rtc's allow an adjustment to the average duration of a second
> + * to compensate for differences in the actual clock rate due to temperature,
> + * the crystal, capacitor, etc.
> + *
> + * Kernel interface to adjust an rtc clock offset.
> + * Return 0 on success, or a negative number on error.
> + * If the rtc offset is not setable (or not implemented), return -EINVAL
> + */
> +int rtc_set_offset(struct rtc_device *rtc, long offset)
> +{
> +	int ret = 0;
> +
> +	if (!rtc->ops)
> +		return -ENODEV;
> +
> +	if (!rtc->ops->set_offset)
> +		return -EINVAL;
> +
> +	mutex_lock(&rtc->ops_lock);
> +	ret = rtc->ops->set_offset(rtc->dev.parent, offset);
> +	mutex_unlock(&rtc->ops_lock);
> +	return ret;
> +}
> diff --git a/include/linux/rtc.h b/include/linux/rtc.h
> index 3359f04..b693ada 100644
> --- a/include/linux/rtc.h
> +++ b/include/linux/rtc.h
> @@ -89,6 +89,8 @@ struct rtc_class_ops {
>  	int (*set_mmss)(struct device *, unsigned long secs);
>  	int (*read_callback)(struct device *, int data);
>  	int (*alarm_irq_enable)(struct device *, unsigned int enabled);
> +	int (*read_offset)(struct device *, long *offset);
> +	int (*set_offset)(struct device *, long offset);
>  };
>  
>  #define RTC_DEVICE_NAME_SIZE 20
> @@ -208,6 +210,8 @@ void rtc_timer_init(struct rtc_timer *timer, void (*f)(void *p), void *data);
>  int rtc_timer_start(struct rtc_device *rtc, struct rtc_timer *timer,
>  		    ktime_t expires, ktime_t period);
>  void rtc_timer_cancel(struct rtc_device *rtc, struct rtc_timer *timer);
> +int rtc_read_offset(struct rtc_device *rtc, long *offset);
> +int rtc_set_offset(struct rtc_device *rtc, long offset);
>  void rtc_timer_do_work(struct work_struct *work);
>  
>  static inline bool is_leap_year(unsigned int year)
> -- 
> 2.5.0
> 

-- 
Alexandre Belloni, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com

[toc] | [prev] | [next] | [standalone]


#1327349 — Re: [PATCH v3 1/3] rtc: Add functions to set and read rtc offset

FromJoshua Clayton <stillcompiling@gmail.com>
Date2016-02-05 00:40 +0100
SubjectRe: [PATCH v3 1/3] rtc: Add functions to set and read rtc offset
Message-ID<qYBFg-u5-9@gated-at.bofh.it>
In reply to#1327221
On Thu, 4 Feb 2016 23:07:54 +0100
Alexandre Belloni <alexandre.belloni@free-electrons.com> wrote:

> Hi,
> 
> On 03/02/2016 at 09:16:42 -0800, Joshua Clayton wrote :
> > diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c
> > index 5836751..c064eb9 100644
> > --- a/drivers/rtc/interface.c
> > +++ b/drivers/rtc/interface.c
> > @@ -939,4 +939,61 @@ void rtc_timer_cancel(struct rtc_device *rtc,
> > struct rtc_timer *timer) mutex_unlock(&rtc->ops_lock);
> >  }
> >  
> > +/**
> > + * rtc_read_offset - Read the amount of rtc offset in parts per
> > billion
> > + * @ rtc: rtc device to be used
> > + * @ offset: the offset in parts per billion
> > + *
> > + * see below for details.
> > + *
> > + * Kernel interface to read rtc clock offset
> > + * Returns 0 on success, or a negative number on error.
> > + * If the rtc offset is not setable (or not implemented), return 0
> > and put
> > + * 0 in the offset value;
> > + */
> > +int rtc_read_offset(struct rtc_device *rtc, long *offset)
> > +{
> > +	int ret = 0;
> > +
> > +	if (!rtc->ops)
> > +		return -ENODEV;
> > +
> > +	if (!rtc->ops->set_offset) {
> > +		*offset = 0;
> > +		return 0;
> > +	}
> > +
> 
> I should have been clearer but this is not necessary anymore since the
> sysfs interface will not always be present but you
> should probably test rtc->ops->read_offset instead.
>
I left it because the kernel API is still there even if the sysfs
file is not...
but yeah, you are right. I'll fix the check, and the formatting
in the other patch.

> > +	mutex_lock(&rtc->ops_lock);
> > +	ret = rtc->ops->read_offset(rtc->dev.parent, offset);
> > +	mutex_unlock(&rtc->ops_lock);
> > +	return ret;
> > +}
> >  
> > +/**
> > + * rtc_set_offset - Adjusts the duration of the average second
> > + * @ rtc: rtc device to be used
> > + * @ offset: the offset in parts per billion
> > + *
> > + * Some rtc's allow an adjustment to the average duration of a
> > second
> > + * to compensate for differences in the actual clock rate due to
> > temperature,
> > + * the crystal, capacitor, etc.
> > + *
> > + * Kernel interface to adjust an rtc clock offset.
> > + * Return 0 on success, or a negative number on error.
> > + * If the rtc offset is not setable (or not implemented), return
> > -EINVAL
> > + */
> > +int rtc_set_offset(struct rtc_device *rtc, long offset)
> > +{
> > +	int ret = 0;
> > +
> > +	if (!rtc->ops)
> > +		return -ENODEV;
> > +
> > +	if (!rtc->ops->set_offset)
> > +		return -EINVAL;
> > +
> > +	mutex_lock(&rtc->ops_lock);
> > +	ret = rtc->ops->set_offset(rtc->dev.parent, offset);
> > +	mutex_unlock(&rtc->ops_lock);
> > +	return ret;
> > +}
> > diff --git a/include/linux/rtc.h b/include/linux/rtc.h
> > index 3359f04..b693ada 100644
> > --- a/include/linux/rtc.h
> > +++ b/include/linux/rtc.h
> > @@ -89,6 +89,8 @@ struct rtc_class_ops {
> >  	int (*set_mmss)(struct device *, unsigned long secs);
> >  	int (*read_callback)(struct device *, int data);
> >  	int (*alarm_irq_enable)(struct device *, unsigned int
> > enabled);
> > +	int (*read_offset)(struct device *, long *offset);
> > +	int (*set_offset)(struct device *, long offset);
> >  };
> >  
> >  #define RTC_DEVICE_NAME_SIZE 20
> > @@ -208,6 +210,8 @@ void rtc_timer_init(struct rtc_timer *timer,
> > void (*f)(void *p), void *data); int rtc_timer_start(struct
> > rtc_device *rtc, struct rtc_timer *timer, ktime_t expires, ktime_t
> > period); void rtc_timer_cancel(struct rtc_device *rtc, struct
> > rtc_timer *timer); +int rtc_read_offset(struct rtc_device *rtc,
> > long *offset); +int rtc_set_offset(struct rtc_device *rtc, long
> > offset); void rtc_timer_do_work(struct work_struct *work);
> >  
> >  static inline bool is_leap_year(unsigned int year)
> > -- 
> > 2.5.0
> > 
> 

[toc] | [prev] | [next] | [standalone]


#1327805 — Re: [PATCH v3 1/3] rtc: Add functions to set and read rtc offset

FromAlexandre Belloni <alexandre.belloni@free-electrons.com>
Date2016-02-05 15:40 +0100
SubjectRe: [PATCH v3 1/3] rtc: Add functions to set and read rtc offset
Message-ID<qYPIf-1t6-27@gated-at.bofh.it>
In reply to#1327349
On 04/02/2016 at 15:32:42 -0800, Joshua Clayton wrote :
> > > +int rtc_read_offset(struct rtc_device *rtc, long *offset)
> > > +{
> > > +	int ret = 0;
> > > +
> > > +	if (!rtc->ops)
> > > +		return -ENODEV;
> > > +
> > > +	if (!rtc->ops->set_offset) {
> > > +		*offset = 0;
> > > +		return 0;
> > > +	}
> > > +
> > 
> > I should have been clearer but this is not necessary anymore since the
> > sysfs interface will not always be present but you
> > should probably test rtc->ops->read_offset instead.
> >
> I left it because the kernel API is still there even if the sysfs
> file is not...
> but yeah, you are right. I'll fix the check, and the formatting
> in the other patch.
> 

Yeah but from inside the kernel, I feel that it is more useful to know
that the value doesn't exist instead of having 0.


-- 
Alexandre Belloni, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com

[toc] | [prev] | [next] | [standalone]


#1328062 — [PATCH v4 1/3] rtc: Add functions to set and read rtc offset

FromJoshua Clayton <stillcompiling@gmail.com>
Date2016-02-05 21:50 +0100
Subject[PATCH v4 1/3] rtc: Add functions to set and read rtc offset
Message-ID<qYVui-5aD-17@gated-at.bofh.it>
In reply to#1327805
A number of rtc devices, such as the NXP pcf2123 include a facility
to adjust the clock in order to compensate for temperature or a
crystal, capacitor, etc, that results in the rtc clock not running
at exactly 32.768 kHz.

Data sheets I have seen refer to this as a clock offset, and measure it
in parts per million, however they often reference ppm to 2 digits of
precision, which makes integer ppm less than ideal.

We use parts per billion, which more than covers the precision needed
and works nicely within 32 bits

Signed-off-by: Joshua Clayton <stillcompiling@gmail.com>
---
 drivers/rtc/interface.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/rtc.h     |  4 ++++
 2 files changed, 58 insertions(+)

diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c
index 5836751..9ef5f6f 100644
--- a/drivers/rtc/interface.c
+++ b/drivers/rtc/interface.c
@@ -939,4 +939,58 @@ void rtc_timer_cancel(struct rtc_device *rtc, struct rtc_timer *timer)
 	mutex_unlock(&rtc->ops_lock);
 }
 
+/**
+ * rtc_read_offset - Read the amount of rtc offset in parts per billion
+ * @ rtc: rtc device to be used
+ * @ offset: the offset in parts per billion
+ *
+ * see below for details.
+ *
+ * Kernel interface to read rtc clock offset
+ * Returns 0 on success, or a negative number on error.
+ * If read_offset() is not implemented for the rtc, return -EINVAL
+ */
+int rtc_read_offset(struct rtc_device *rtc, long *offset)
+{
+	int ret;
+
+	if (!rtc->ops)
+		return -ENODEV;
+
+	if (!rtc->ops->read_offset)
+		return -EINVAL;
+
+	mutex_lock(&rtc->ops_lock);
+	ret = rtc->ops->read_offset(rtc->dev.parent, offset);
+	mutex_unlock(&rtc->ops_lock);
+	return ret;
+}
 
+/**
+ * rtc_set_offset - Adjusts the duration of the average second
+ * @ rtc: rtc device to be used
+ * @ offset: the offset in parts per billion
+ *
+ * Some rtc's allow an adjustment to the average duration of a second
+ * to compensate for differences in the actual clock rate due to temperature,
+ * the crystal, capacitor, etc.
+ *
+ * Kernel interface to adjust an rtc clock offset.
+ * Return 0 on success, or a negative number on error.
+ * If the rtc offset is not setable (or not implemented), return -EINVAL
+ */
+int rtc_set_offset(struct rtc_device *rtc, long offset)
+{
+	int ret;
+
+	if (!rtc->ops)
+		return -ENODEV;
+
+	if (!rtc->ops->set_offset)
+		return -EINVAL;
+
+	mutex_lock(&rtc->ops_lock);
+	ret = rtc->ops->set_offset(rtc->dev.parent, offset);
+	mutex_unlock(&rtc->ops_lock);
+	return ret;
+}
diff --git a/include/linux/rtc.h b/include/linux/rtc.h
index 3359f04..b693ada 100644
--- a/include/linux/rtc.h
+++ b/include/linux/rtc.h
@@ -89,6 +89,8 @@ struct rtc_class_ops {
 	int (*set_mmss)(struct device *, unsigned long secs);
 	int (*read_callback)(struct device *, int data);
 	int (*alarm_irq_enable)(struct device *, unsigned int enabled);
+	int (*read_offset)(struct device *, long *offset);
+	int (*set_offset)(struct device *, long offset);
 };
 
 #define RTC_DEVICE_NAME_SIZE 20
@@ -208,6 +210,8 @@ void rtc_timer_init(struct rtc_timer *timer, void (*f)(void *p), void *data);
 int rtc_timer_start(struct rtc_device *rtc, struct rtc_timer *timer,
 		    ktime_t expires, ktime_t period);
 void rtc_timer_cancel(struct rtc_device *rtc, struct rtc_timer *timer);
+int rtc_read_offset(struct rtc_device *rtc, long *offset);
+int rtc_set_offset(struct rtc_device *rtc, long offset);
 void rtc_timer_do_work(struct work_struct *work);
 
 static inline bool is_leap_year(unsigned int year)
-- 
2.5.0

[toc] | [prev] | [next] | [standalone]


#1328064 — [PATCH v4 2/3] rtc: implement a sysfs interface for clock offset

FromJoshua Clayton <stillcompiling@gmail.com>
Date2016-02-05 21:50 +0100
Subject[PATCH v4 2/3] rtc: implement a sysfs interface for clock offset
Message-ID<qYVuj-5aD-19@gated-at.bofh.it>
In reply to#1328062
clock offset may be set and read in decimal parts per billion
attribute is /sys/class/rtc/rtcN/offset
The attribute is only visible for rtcs that have set_offset implemented.

Signed-off-by: Joshua Clayton <stillcompiling@gmail.com>
---
 Documentation/rtc.txt   |  6 ++++++
 drivers/rtc/rtc-sysfs.c | 35 ++++++++++++++++++++++++++++++++++-
 2 files changed, 40 insertions(+), 1 deletion(-)

diff --git a/Documentation/rtc.txt b/Documentation/rtc.txt
index 8446f1e..ddc3660 100644
--- a/Documentation/rtc.txt
+++ b/Documentation/rtc.txt
@@ -157,6 +157,12 @@ wakealarm:	 The time at which the clock will generate a system wakeup
 		 the epoch by default, or if there's a leading +, seconds in the
 		 future, or if there is a leading +=, seconds ahead of the current
 		 alarm.
+offset:		 The amount which the rtc clock has been adjusted in firmware.
+		 Visible only if the driver supports clock offset adjustment.
+		 The unit is parts per billion, i.e. The number of clock ticks
+		 which are added to or removed from the rtc's base clock per
+		 billion ticks. A positive value makes a day pass more slowly,
+		 longer, and a negative value makes a day pass more quickly.
 
 IOCTL INTERFACE
 ---------------
diff --git a/drivers/rtc/rtc-sysfs.c b/drivers/rtc/rtc-sysfs.c
index 463e286..63b9fb1 100644
--- a/drivers/rtc/rtc-sysfs.c
+++ b/drivers/rtc/rtc-sysfs.c
@@ -218,6 +218,34 @@ wakealarm_store(struct device *dev, struct device_attribute *attr,
 }
 static DEVICE_ATTR_RW(wakealarm);
 
+static ssize_t
+offset_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	ssize_t retval;
+	long offset;
+
+	retval = rtc_read_offset(to_rtc_device(dev), &offset);
+	if (retval == 0)
+		retval = sprintf(buf, "%ld\n", offset);
+
+	return retval;
+}
+
+static ssize_t
+offset_store(struct device *dev, struct device_attribute *attr,
+	     const char *buf, size_t n)
+{
+	ssize_t retval;
+	long offset;
+
+	retval = kstrtol(buf, 10, &offset);
+	if (retval == 0)
+		retval = rtc_set_offset(to_rtc_device(dev), offset);
+
+	return (retval < 0) ? retval : n;
+}
+static DEVICE_ATTR_RW(offset);
+
 static struct attribute *rtc_attrs[] = {
 	&dev_attr_name.attr,
 	&dev_attr_date.attr,
@@ -226,6 +254,7 @@ static struct attribute *rtc_attrs[] = {
 	&dev_attr_max_user_freq.attr,
 	&dev_attr_hctosys.attr,
 	&dev_attr_wakealarm.attr,
+	&dev_attr_offset.attr,
 	NULL,
 };
 
@@ -249,9 +278,13 @@ static umode_t rtc_attr_is_visible(struct kobject *kobj,
 	struct rtc_device *rtc = to_rtc_device(dev);
 	umode_t mode = attr->mode;
 
-	if (attr == &dev_attr_wakealarm.attr)
+	if (attr == &dev_attr_wakealarm.attr) {
 		if (!rtc_does_wakealarm(rtc))
 			mode = 0;
+	} else if (attr == &dev_attr_offset.attr) {
+		if (!rtc->ops->set_offset)
+			mode = 0;
+	}
 
 	return mode;
 }
-- 
2.5.0

[toc] | [prev] | [next] | [standalone]


#1328065 — [PATCH v4 3/3] rtc-pcf2123: implement read_offset and set_offset

FromJoshua Clayton <stillcompiling@gmail.com>
Date2016-02-05 21:50 +0100
Subject[PATCH v4 3/3] rtc-pcf2123: implement read_offset and set_offset
Message-ID<qYVuj-5aD-21@gated-at.bofh.it>
In reply to#1328062
pcf2123 has an offset register, which can be used to make minor
adjustments to the clock rate to compensate for temperature or
a crystal that is not exactly right.

Signed-off-by: Joshua Clayton <stillcompiling@gmail.com>
---
 drivers/rtc/rtc-pcf2123.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 57 insertions(+)

diff --git a/drivers/rtc/rtc-pcf2123.c b/drivers/rtc/rtc-pcf2123.c
index d9a44ad..da27738 100644
--- a/drivers/rtc/rtc-pcf2123.c
+++ b/drivers/rtc/rtc-pcf2123.c
@@ -100,6 +100,7 @@
 /* PCF2123_REG_OFFSET BITS */
 #define OFFSET_SIGN_BIT		BIT(6)	/* 2's complement sign bit */
 #define OFFSET_COARSE		BIT(7)	/* Coarse mode offset */
+#define OFFSET_STEP		(2170)	/* Offset step in parts per billion */
 
 /* READ/WRITE ADDRESS BITS */
 #define PCF2123_WRITE		BIT(4)
@@ -206,6 +207,59 @@ static ssize_t pcf2123_store(struct device *dev, struct device_attribute *attr,
 	return count;
 }
 
+static int pcf2123_read_offset(struct device *dev, long *offset)
+{
+	int ret;
+	s8 reg;
+
+	ret = pcf2123_read(dev, PCF2123_REG_OFFSET, &reg, 1);
+	if (ret < 0)
+		return ret;
+
+	if (reg & OFFSET_COARSE)
+		reg <<= 1; /* multiply by 2 and sign extend */
+	else
+		reg |= (reg & OFFSET_SIGN_BIT) << 1; /* sign extend only */
+
+	*offset = ((long)reg) * OFFSET_STEP;
+
+	return 0;
+}
+
+/*
+ * The offset register is a 7 bit signed value with a coarse bit in bit 7.
+ * The main difference between the two is normal offset adjusts the first
+ * second of n minutes every other hour, with 61, 62 and 63 being shoved
+ * into the 60th minute.
+ * The coarse adjustment does the same, but every hour.
+ * the two overlap, with every even normal offset value corresponding
+ * to a coarse offset. Based on this algorithm, it seems that despite the
+ * name, coarse offset is a better fit for overlapping values.
+ */
+static int pcf2123_set_offset(struct device *dev, long offset)
+{
+	s8 reg;
+
+	if (offset > OFFSET_STEP * 127)
+		reg = 127;
+	else if (offset < OFFSET_STEP * -128)
+		reg = -128;
+	else
+		reg = (s8)((offset + (OFFSET_STEP >> 1)) / OFFSET_STEP);
+
+	/* choose fine offset only for odd values in the normal range */
+	if (reg & 1 && reg <= 63 && reg >= -64) {
+		/* Normal offset. Clear the coarse bit */
+		reg &= ~OFFSET_COARSE;
+	} else {
+		/* Coarse offset. Divide by 2 and set the coarse bit */
+		reg >>= 1;
+		reg |= OFFSET_COARSE;
+	}
+
+	return pcf2123_write_reg(dev, PCF2123_REG_OFFSET, reg);
+}
+
 static int pcf2123_rtc_read_time(struct device *dev, struct rtc_time *tm)
 {
 	u8 rxbuf[7];
@@ -314,6 +368,9 @@ static int pcf2123_reset(struct device *dev)
 static const struct rtc_class_ops pcf2123_rtc_ops = {
 	.read_time	= pcf2123_rtc_read_time,
 	.set_time	= pcf2123_rtc_set_time,
+	.read_offset	= pcf2123_read_offset,
+	.set_offset	= pcf2123_set_offset,
+
 };
 
 static int pcf2123_probe(struct spi_device *spi)
-- 
2.5.0

[toc] | [prev] | [standalone]


Back to top | Article view | linux.kernel


csiph-web