Commit bae2f647 authored by Joshua Clayton's avatar Joshua Clayton Committed by Alexandre Belloni
Browse files

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: default avatarJoshua Clayton <stillcompiling@gmail.com>
Signed-off-by: default avatarAlexandre Belloni <alexandre.belloni@free-electrons.com>
parent 5495a415
Loading
Loading
Loading
Loading
+57 −0
Original line number Diff line number Diff line
@@ -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)