rtc/ab8500: add calibration attribute to AB8500 RTC
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. [akpm@linux-foundation.org: coding-style fixes] Signed-off-by: Mark Godfrey <mark.godfrey@stericsson.com> Signed-off-by: Linus Walleij <linus.walleij@stericsson.com> Acked-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> Cc: Alessandro Zummo <a.zummo@towertech.it> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
parent
012e52e15e
commit
dda367ac06
|
@ -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.
|
|
@ -258,6 +258,109 @@ 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. -128 (0x80) is not permitted because the AB8500 uses
|
||||
* a sign-bit rather than two's complement, so 0x80 is just another
|
||||
* representation of zero.
|
||||
*/
|
||||
if ((calibration < -127) || (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 +430,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 +445,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);
|
||||
|
|
Loading…
Reference in New Issue