Message ID | 1322667741-10771-1-git-send-email-linus.walleij@stericsson.com |
---|---|
State | Superseded |
Headers | show |
Acked-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> Best Regards, J. On 16:42 Wed 30 Nov , Linus Walleij wrote: > From: Mark Godfrey <mark.godfrey@stericsson.com> > > The rtc_calibration attribute allows user-space to get and set > the AB8500's RtcCalibration register. The AB8500 will then use the > value in this register to compensate for RTC drift every 60 seconds. > > ChangeLog v1->v2: allow -128 (0x80) as minimum calibration. > > Signed-off-by: Mark Godfrey <mark.godfrey@stericsson.com> > Signed-off-by: Linus Walleij <linus.walleij@stericsson.com> > --- > .../sysfs-class-rtc-rtc0-device-rtc_calibration | 12 ++ > drivers/rtc/rtc-ab8500.c | 109 ++++++++++++++++++++ > 2 files changed, 121 insertions(+), 0 deletions(-) > create mode 100644 Documentation/ABI/testing/sysfs-class-rtc-rtc0-device-rtc_calibration > > diff --git a/Documentation/ABI/testing/sysfs-class-rtc-rtc0-device-rtc_calibration b/Documentation/ABI/testing/sysfs-class-rtc-rtc0-device-rtc_calibration > new file mode 100644 > index 0000000..4cf1e72 > --- /dev/null > +++ b/Documentation/ABI/testing/sysfs-class-rtc-rtc0-device-rtc_calibration > @@ -0,0 +1,12 @@ > +What: Attribute for calibrating ST-Ericsson AB8500 Real Time Clock > +Date: Oct 2011 > +KernelVersion: 3.0 > +Contact: Mark Godfrey <mark.godfrey@stericsson.com> > +Description: The rtc_calibration attribute allows the userspace to > + calibrate the AB8500.s 32KHz Real Time Clock. > + Every 60 seconds the AB8500 will correct the RTC's value > + by adding to it the value of this attribute. > + The range of the attribute is -127 to +127 in units of > + 30.5 micro-seconds (half-parts-per-million of the 32KHz clock) > +Users: The /vendor/st-ericsson/base_utilities/core/rtc_calibration > + daemon uses this interface. > diff --git a/drivers/rtc/rtc-ab8500.c b/drivers/rtc/rtc-ab8500.c > index 326a1a5..85c14c1 100644 > --- a/drivers/rtc/rtc-ab8500.c > +++ b/drivers/rtc/rtc-ab8500.c > @@ -258,6 +258,106 @@ static int ab8500_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) > return ab8500_rtc_irq_enable(dev, alarm->enabled); > } > > + > +static int ab8500_rtc_set_calibration(struct device *dev, int calibration) > +{ > + int retval; > + u8 rtccal = 0; > + > + /* > + * Check that the calibration value (which is in units of 0.5 parts-per-million) > + * is in the AB8500's range for RtcCalibration register. > + */ > + if ((calibration < -128) || (calibration > 127)) { > + dev_err(dev, "RtcCalibration value outside permitted range\n"); > + return -EINVAL; > + } > + > + /* > + * The AB8500 uses sign (in bit7) and magnitude (in bits0-7) > + * so need to convert to this sort of representation before writing > + * into RtcCalibration register... > + */ > + if (calibration >= 0) > + rtccal = 0x7F & calibration; > + else > + rtccal = ~(calibration -1) | 0x80; > + > + retval = abx500_set_register_interruptible(dev, AB8500_RTC, > + AB8500_RTC_CALIB_REG, rtccal); > + > + return retval; > +} > + > +static int ab8500_rtc_get_calibration(struct device *dev, int *calibration) > +{ > + int retval; > + u8 rtccal = 0; > + > + retval = abx500_get_register_interruptible(dev, AB8500_RTC, > + AB8500_RTC_CALIB_REG, &rtccal); > + if (retval >= 0) { > + /* > + * The AB8500 uses sign (in bit7) and magnitude (in bits0-7) > + * so need to convert value from RtcCalibration register into > + * a two's complement signed value... > + */ > + if (rtccal & 0x80) > + *calibration = 0 - (rtccal & 0x7F); > + else > + *calibration = 0x7F & rtccal; > + } > + > + return retval; > +} > + > +static ssize_t ab8500_sysfs_store_rtc_calibration(struct device *dev, > + struct device_attribute *attr, > + const char *buf, size_t count) > +{ > + int retval; > + int calibration = 0; > + > + if (sscanf(buf, " %i ", &calibration) != 1) { > + dev_err(dev, "Failed to store RTC calibration attribute\n"); > + return -EINVAL; > + } > + > + retval = ab8500_rtc_set_calibration(dev, calibration); > + > + return retval ? retval : count; > +} > + > +static ssize_t ab8500_sysfs_show_rtc_calibration(struct device *dev, > + struct device_attribute *attr, char *buf) > +{ > + int retval = 0; > + int calibration = 0; > + > + retval = ab8500_rtc_get_calibration(dev, &calibration); > + if (retval < 0) { > + dev_err(dev, "Failed to read RTC calibration attribute\n"); > + sprintf(buf, "0\n"); > + return retval; > + } > + > + return sprintf(buf, "%d\n", calibration); > +} > + > +static DEVICE_ATTR(rtc_calibration, S_IRUGO | S_IWUSR, > + ab8500_sysfs_show_rtc_calibration, > + ab8500_sysfs_store_rtc_calibration); > + > +static int ab8500_sysfs_rtc_register(struct device *dev) > +{ > + return device_create_file(dev, &dev_attr_rtc_calibration); > +} > + > +static void ab8500_sysfs_rtc_unregister(struct device *dev) > +{ > + device_remove_file(dev, &dev_attr_rtc_calibration); > +} > + > static irqreturn_t rtc_alarm_handler(int irq, void *data) > { > struct rtc_device *rtc = data; > @@ -327,6 +427,13 @@ static int __devinit ab8500_rtc_probe(struct platform_device *pdev) > > platform_set_drvdata(pdev, rtc); > > + > + err = ab8500_sysfs_rtc_register(&pdev->dev); > + if (err) { > + dev_err(&pdev->dev, "sysfs RTC failed to register\n"); > + return err; > + } > + > return 0; > } > > @@ -335,6 +442,8 @@ static int __devexit ab8500_rtc_remove(struct platform_device *pdev) > struct rtc_device *rtc = platform_get_drvdata(pdev); > int irq = platform_get_irq_byname(pdev, "ALARM"); > > + ab8500_sysfs_rtc_unregister(&pdev->dev); > + > free_irq(irq, rtc); > rtc_device_unregister(rtc); > platform_set_drvdata(pdev, NULL); > -- > 1.7.3.2 > > -- > 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.
diff --git a/Documentation/ABI/testing/sysfs-class-rtc-rtc0-device-rtc_calibration b/Documentation/ABI/testing/sysfs-class-rtc-rtc0-device-rtc_calibration new file mode 100644 index 0000000..4cf1e72 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-rtc-rtc0-device-rtc_calibration @@ -0,0 +1,12 @@ +What: Attribute for calibrating ST-Ericsson AB8500 Real Time Clock +Date: Oct 2011 +KernelVersion: 3.0 +Contact: Mark Godfrey <mark.godfrey@stericsson.com> +Description: The rtc_calibration attribute allows the userspace to + calibrate the AB8500.s 32KHz Real Time Clock. + Every 60 seconds the AB8500 will correct the RTC's value + by adding to it the value of this attribute. + The range of the attribute is -127 to +127 in units of + 30.5 micro-seconds (half-parts-per-million of the 32KHz clock) +Users: The /vendor/st-ericsson/base_utilities/core/rtc_calibration + daemon uses this interface. diff --git a/drivers/rtc/rtc-ab8500.c b/drivers/rtc/rtc-ab8500.c index 326a1a5..85c14c1 100644 --- a/drivers/rtc/rtc-ab8500.c +++ b/drivers/rtc/rtc-ab8500.c @@ -258,6 +258,106 @@ static int ab8500_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) return ab8500_rtc_irq_enable(dev, alarm->enabled); } + +static int ab8500_rtc_set_calibration(struct device *dev, int calibration) +{ + int retval; + u8 rtccal = 0; + + /* + * Check that the calibration value (which is in units of 0.5 parts-per-million) + * is in the AB8500's range for RtcCalibration register. + */ + if ((calibration < -128) || (calibration > 127)) { + dev_err(dev, "RtcCalibration value outside permitted range\n"); + return -EINVAL; + } + + /* + * The AB8500 uses sign (in bit7) and magnitude (in bits0-7) + * so need to convert to this sort of representation before writing + * into RtcCalibration register... + */ + if (calibration >= 0) + rtccal = 0x7F & calibration; + else + rtccal = ~(calibration -1) | 0x80; + + retval = abx500_set_register_interruptible(dev, AB8500_RTC, + AB8500_RTC_CALIB_REG, rtccal); + + return retval; +} + +static int ab8500_rtc_get_calibration(struct device *dev, int *calibration) +{ + int retval; + u8 rtccal = 0; + + retval = abx500_get_register_interruptible(dev, AB8500_RTC, + AB8500_RTC_CALIB_REG, &rtccal); + if (retval >= 0) { + /* + * The AB8500 uses sign (in bit7) and magnitude (in bits0-7) + * so need to convert value from RtcCalibration register into + * a two's complement signed value... + */ + if (rtccal & 0x80) + *calibration = 0 - (rtccal & 0x7F); + else + *calibration = 0x7F & rtccal; + } + + return retval; +} + +static ssize_t ab8500_sysfs_store_rtc_calibration(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int retval; + int calibration = 0; + + if (sscanf(buf, " %i ", &calibration) != 1) { + dev_err(dev, "Failed to store RTC calibration attribute\n"); + return -EINVAL; + } + + retval = ab8500_rtc_set_calibration(dev, calibration); + + return retval ? retval : count; +} + +static ssize_t ab8500_sysfs_show_rtc_calibration(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval = 0; + int calibration = 0; + + retval = ab8500_rtc_get_calibration(dev, &calibration); + if (retval < 0) { + dev_err(dev, "Failed to read RTC calibration attribute\n"); + sprintf(buf, "0\n"); + return retval; + } + + return sprintf(buf, "%d\n", calibration); +} + +static DEVICE_ATTR(rtc_calibration, S_IRUGO | S_IWUSR, + ab8500_sysfs_show_rtc_calibration, + ab8500_sysfs_store_rtc_calibration); + +static int ab8500_sysfs_rtc_register(struct device *dev) +{ + return device_create_file(dev, &dev_attr_rtc_calibration); +} + +static void ab8500_sysfs_rtc_unregister(struct device *dev) +{ + device_remove_file(dev, &dev_attr_rtc_calibration); +} + static irqreturn_t rtc_alarm_handler(int irq, void *data) { struct rtc_device *rtc = data; @@ -327,6 +427,13 @@ static int __devinit ab8500_rtc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, rtc); + + err = ab8500_sysfs_rtc_register(&pdev->dev); + if (err) { + dev_err(&pdev->dev, "sysfs RTC failed to register\n"); + return err; + } + return 0; } @@ -335,6 +442,8 @@ static int __devexit ab8500_rtc_remove(struct platform_device *pdev) struct rtc_device *rtc = platform_get_drvdata(pdev); int irq = platform_get_irq_byname(pdev, "ALARM"); + ab8500_sysfs_rtc_unregister(&pdev->dev); + free_irq(irq, rtc); rtc_device_unregister(rtc); platform_set_drvdata(pdev, NULL);