On Sat, 10 Dec 2016, Vesa Jääskeläinen wrote:

> Texas Instrument's TPS65910 has support for compensating RTC crystal
> inaccuracies. When enabled every hour RTC counter value will be compensated
> with two's complement value.
> 
> Signed-off-by: Vesa Jääskeläinen <vesa.jaaskelai...@vaisala.com>
> ---
>  drivers/rtc/rtc-tps65910.c   | 132 
> +++++++++++++++++++++++++++++++++++++++++++
>  include/linux/mfd/tps65910.h |   1 +

For the MFD change:

Acked-by: Lee Jones <lee.jo...@linaro.org>

>  2 files changed, 133 insertions(+)
> 
> diff --git a/drivers/rtc/rtc-tps65910.c b/drivers/rtc/rtc-tps65910.c
> index 5a3d53c..aafa716 100644
> --- a/drivers/rtc/rtc-tps65910.c
> +++ b/drivers/rtc/rtc-tps65910.c
> @@ -21,6 +21,7 @@
>  #include <linux/types.h>
>  #include <linux/rtc.h>
>  #include <linux/bcd.h>
> +#include <linux/math64.h>
>  #include <linux/platform_device.h>
>  #include <linux/interrupt.h>
>  #include <linux/mfd/tps65910.h>
> @@ -33,6 +34,19 @@ struct tps65910_rtc {
>  /* Total number of RTC registers needed to set time*/
>  #define NUM_TIME_REGS        (TPS65910_YEARS - TPS65910_SECONDS + 1)
>  
> +/* Total number of RTC registers needed to set compensation registers */
> +#define NUM_COMP_REGS        (TPS65910_RTC_COMP_MSB - TPS65910_RTC_COMP_LSB 
> + 1)
> +
> +/* Min and max values supported with 'offset' interface (swapped sign) */
> +#define MIN_OFFSET   (-277761)
> +#define MAX_OFFSET   (277778)
> +
> +/* Number of ticks per hour */
> +#define TICKS_PER_HOUR       (32768 * 3600)
> +
> +/* Multiplier for ppb conversions */
> +#define PPB_MULT     (1000000000LL)
> +
>  static int tps65910_rtc_alarm_irq_enable(struct device *dev, unsigned 
> enabled)
>  {
>       struct tps65910 *tps = dev_get_drvdata(dev->parent);
> @@ -187,6 +201,122 @@ static int tps65910_rtc_set_alarm(struct device *dev, 
> struct rtc_wkalrm *alm)
>       return ret;
>  }
>  
> +static int tps65910_rtc_set_calibration(struct device *dev, int calibration)
> +{
> +     unsigned char comp_data[NUM_COMP_REGS];
> +     struct tps65910 *tps = dev_get_drvdata(dev->parent);
> +     s16 value;
> +     int ret;
> +
> +     /*
> +      * TPS65910 uses two's complement 16 bit value for compensation for RTC
> +      * crystal inaccuracies. One time every hour when seconds counter
> +      * increments from 0 to 1 compensation value will be added to internal
> +      * RTC counter value.
> +      *
> +      * Compensation value 0x7FFF is prohibited value.
> +      *
> +      * Valid range for compensation value: [-32768 .. 32766]
> +      */
> +     if ((calibration < -32768) || (calibration > 32766)) {
> +             dev_err(dev, "RTC calibration value out of range: %d\n",
> +                     calibration);
> +             return -EINVAL;
> +     }
> +
> +     value = (s16)calibration;
> +
> +     comp_data[0] = (u16)value & 0xFF;
> +     comp_data[1] = ((u16)value >> 8) & 0xFF;
> +
> +     /* Update all the compensation registers in one shot */
> +     ret = regmap_bulk_write(tps->regmap, TPS65910_RTC_COMP_LSB,
> +             comp_data, NUM_COMP_REGS);
> +     if (ret < 0) {
> +             dev_err(dev, "rtc_set_calibration error: %d\n", ret);
> +             return ret;
> +     }
> +
> +     /* Enable automatic compensation */
> +     ret = regmap_update_bits(tps->regmap, TPS65910_RTC_CTRL,
> +             TPS65910_RTC_CTRL_AUTO_COMP, TPS65910_RTC_CTRL_AUTO_COMP);
> +     if (ret < 0)
> +             dev_err(dev, "auto_comp enable failed with error: %d\n", ret);
> +
> +     return ret;
> +}
> +
> +static int tps65910_rtc_get_calibration(struct device *dev, int *calibration)
> +{
> +     unsigned char comp_data[NUM_COMP_REGS];
> +     struct tps65910 *tps = dev_get_drvdata(dev->parent);
> +     u16 value;
> +     int ret;
> +
> +     ret = regmap_bulk_read(tps->regmap, TPS65910_RTC_COMP_LSB, comp_data,
> +             NUM_COMP_REGS);
> +     if (ret < 0) {
> +             dev_err(dev, "rtc_get_calibration error: %d\n", ret);
> +             return ret;
> +     }
> +
> +     value = (u16)comp_data[0] | ((u16)comp_data[1] << 8);
> +
> +     *calibration = (s16)value;
> +
> +     return 0;
> +}
> +
> +static int tps65910_read_offset(struct device *dev, long *offset)
> +{
> +     int calibration;
> +     s64 tmp;
> +     int ret;
> +
> +     ret = tps65910_rtc_get_calibration(dev, &calibration);
> +     if (ret < 0)
> +             return ret;
> +
> +     /* Convert from RTC calibration register format to ppb format */
> +     tmp = calibration * (s64)PPB_MULT;
> +     if (tmp < 0)
> +             tmp -= TICKS_PER_HOUR / 2LL;
> +     else
> +             tmp += TICKS_PER_HOUR / 2LL;
> +     tmp = div_s64(tmp, TICKS_PER_HOUR);
> +
> +     /* Offset value operates in negative way, so swap sign */
> +     *offset = (long)-tmp;
> +
> +     return 0;
> +}
> +
> +static int tps65910_set_offset(struct device *dev, long offset)
> +{
> +     int calibration;
> +     s64 tmp;
> +     int ret;
> +
> +     /* Make sure offset value is within supported range */
> +     if (offset < MIN_OFFSET || offset > MAX_OFFSET)
> +             return -ERANGE;
> +
> +     /* Convert from ppb format to RTC calibration register format */
> +     tmp = offset * (s64)TICKS_PER_HOUR;
> +     if (tmp < 0)
> +             tmp -= PPB_MULT / 2LL;
> +     else
> +             tmp += PPB_MULT / 2LL;
> +     tmp = div_s64(tmp, PPB_MULT);
> +
> +     /* Offset value operates in negative way, so swap sign */
> +     calibration = (int)-tmp;
> +
> +     ret = tps65910_rtc_set_calibration(dev, calibration);
> +
> +     return ret;
> +}
> +
>  static irqreturn_t tps65910_rtc_interrupt(int irq, void *rtc)
>  {
>       struct device *dev = rtc;
> @@ -219,6 +349,8 @@ static const struct rtc_class_ops tps65910_rtc_ops = {
>       .read_alarm     = tps65910_rtc_read_alarm,
>       .set_alarm      = tps65910_rtc_set_alarm,
>       .alarm_irq_enable = tps65910_rtc_alarm_irq_enable,
> +     .read_offset    = tps65910_read_offset,
> +     .set_offset     = tps65910_set_offset,
>  };
>  
>  static int tps65910_rtc_probe(struct platform_device *pdev)
> diff --git a/include/linux/mfd/tps65910.h b/include/linux/mfd/tps65910.h
> index 6483a6f..ffb21e7 100644
> --- a/include/linux/mfd/tps65910.h
> +++ b/include/linux/mfd/tps65910.h
> @@ -134,6 +134,7 @@
>  
>  /* RTC_CTRL_REG bitfields */
>  #define TPS65910_RTC_CTRL_STOP_RTC                   0x01 /*0=stop, 1=run */
> +#define TPS65910_RTC_CTRL_AUTO_COMP                  0x04
>  #define TPS65910_RTC_CTRL_GET_TIME                   0x40
>  
>  /* RTC_STATUS_REG bitfields */

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

-- 
You received this message because you are subscribed to "rtc-linux".
Membership options at http://groups.google.com/group/rtc-linux .
Please read http://groups.google.com/group/rtc-linux/web/checklist
before submitting a driver.
--- 
You received this message because you are subscribed to the Google Groups 
"rtc-linux" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to rtc-linux+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to