aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoshua Clayton <stillcompiling@gmail.com>2016-02-05 15:41:13 -0500
committerAlexandre Belloni <alexandre.belloni@free-electrons.com>2016-03-14 12:08:16 -0400
commitbae2f647025549fd2a24f02e9587481a50c0066b (patch)
tree5e614db01c4a0a8eb9e892f22facd52893ff9e29
parent5495a4159f7413f0367e8c9727ba9facd40ade7f (diff)
rtc: pcf2123: implement read_offset and set_offset
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> Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
-rw-r--r--drivers/rtc/rtc-pcf2123.c57
1 files changed, 57 insertions, 0 deletions
diff --git a/drivers/rtc/rtc-pcf2123.c b/drivers/rtc/rtc-pcf2123.c
index d9a44ad1239b..da27738b1242 100644
--- a/drivers/rtc/rtc-pcf2123.c
+++ b/drivers/rtc/rtc-pcf2123.c
@@ -100,6 +100,7 @@
100/* PCF2123_REG_OFFSET BITS */ 100/* PCF2123_REG_OFFSET BITS */
101#define OFFSET_SIGN_BIT BIT(6) /* 2's complement sign bit */ 101#define OFFSET_SIGN_BIT BIT(6) /* 2's complement sign bit */
102#define OFFSET_COARSE BIT(7) /* Coarse mode offset */ 102#define OFFSET_COARSE BIT(7) /* Coarse mode offset */
103#define OFFSET_STEP (2170) /* Offset step in parts per billion */
103 104
104/* READ/WRITE ADDRESS BITS */ 105/* READ/WRITE ADDRESS BITS */
105#define PCF2123_WRITE BIT(4) 106#define PCF2123_WRITE BIT(4)
@@ -206,6 +207,59 @@ static ssize_t pcf2123_store(struct device *dev, struct device_attribute *attr,
206 return count; 207 return count;
207} 208}
208 209
210static int pcf2123_read_offset(struct device *dev, long *offset)
211{
212 int ret;
213 s8 reg;
214
215 ret = pcf2123_read(dev, PCF2123_REG_OFFSET, &reg, 1);
216 if (ret < 0)
217 return ret;
218
219 if (reg & OFFSET_COARSE)
220 reg <<= 1; /* multiply by 2 and sign extend */
221 else
222 reg |= (reg & OFFSET_SIGN_BIT) << 1; /* sign extend only */
223
224 *offset = ((long)reg) * OFFSET_STEP;
225
226 return 0;
227}
228
229/*
230 * The offset register is a 7 bit signed value with a coarse bit in bit 7.
231 * The main difference between the two is normal offset adjusts the first
232 * second of n minutes every other hour, with 61, 62 and 63 being shoved
233 * into the 60th minute.
234 * The coarse adjustment does the same, but every hour.
235 * the two overlap, with every even normal offset value corresponding
236 * to a coarse offset. Based on this algorithm, it seems that despite the
237 * name, coarse offset is a better fit for overlapping values.
238 */
239static int pcf2123_set_offset(struct device *dev, long offset)
240{
241 s8 reg;
242
243 if (offset > OFFSET_STEP * 127)
244 reg = 127;
245 else if (offset < OFFSET_STEP * -128)
246 reg = -128;
247 else
248 reg = (s8)((offset + (OFFSET_STEP >> 1)) / OFFSET_STEP);
249
250 /* choose fine offset only for odd values in the normal range */
251 if (reg & 1 && reg <= 63 && reg >= -64) {
252 /* Normal offset. Clear the coarse bit */
253 reg &= ~OFFSET_COARSE;
254 } else {
255 /* Coarse offset. Divide by 2 and set the coarse bit */
256 reg >>= 1;
257 reg |= OFFSET_COARSE;
258 }
259
260 return pcf2123_write_reg(dev, PCF2123_REG_OFFSET, reg);
261}
262
209static int pcf2123_rtc_read_time(struct device *dev, struct rtc_time *tm) 263static int pcf2123_rtc_read_time(struct device *dev, struct rtc_time *tm)
210{ 264{
211 u8 rxbuf[7]; 265 u8 rxbuf[7];
@@ -314,6 +368,9 @@ static int pcf2123_reset(struct device *dev)
314static const struct rtc_class_ops pcf2123_rtc_ops = { 368static const struct rtc_class_ops pcf2123_rtc_ops = {
315 .read_time = pcf2123_rtc_read_time, 369 .read_time = pcf2123_rtc_read_time,
316 .set_time = pcf2123_rtc_set_time, 370 .set_time = pcf2123_rtc_set_time,
371 .read_offset = pcf2123_read_offset,
372 .set_offset = pcf2123_set_offset,
373
317}; 374};
318 375
319static int pcf2123_probe(struct spi_device *spi) 376static int pcf2123_probe(struct spi_device *spi)