Commit 79230ff6 authored by Giulio Benetti's avatar Giulio Benetti Committed by Alexandre Belloni
Browse files

rtc: ds1307: add offset sysfs for mt41txx chips.



m41txx chips can hold a calibration value to get correct clock bias.

Add offset handling (ranging between -63ppm and 126ppm) via sysfs.

Signed-off-by: default avatarGiulio Benetti <giulio.benetti@micronovasrl.com>
Signed-off-by: default avatarAlexandre Belloni <alexandre.belloni@bootlin.com>
parent 1b4c794f
Loading
Loading
Loading
Loading
+77 −0
Original line number Diff line number Diff line
@@ -114,6 +114,20 @@ enum ds_type {
#	define RX8025_BIT_VDET		0x40
#	define RX8025_BIT_XST		0x20

#define M41TXX_REG_CONTROL	0x07
#	define M41TXX_BIT_OUT		BIT(7)
#	define M41TXX_BIT_FT		BIT(6)
#	define M41TXX_BIT_CALIB_SIGN	BIT(5)
#	define M41TXX_M_CALIBRATION	GENMASK(4, 0)

/* negative offset step is -2.034ppm */
#define M41TXX_NEG_OFFSET_STEP_PPB	2034
/* positive offset step is +4.068ppm */
#define M41TXX_POS_OFFSET_STEP_PPB	4068
/* Min and max values supported with 'offset' interface by M41TXX */
#define M41TXX_MIN_OFFSET	((-31) * M41TXX_NEG_OFFSET_STEP_PPB)
#define M41TXX_MAX_OFFSET	((31) * M41TXX_POS_OFFSET_STEP_PPB)

struct ds1307 {
	enum ds_type		type;
	unsigned long		flags;
@@ -146,6 +160,9 @@ struct chip_desc {

static int ds1307_get_time(struct device *dev, struct rtc_time *t);
static int ds1307_set_time(struct device *dev, struct rtc_time *t);
static int ds1337_read_alarm(struct device *dev, struct rtc_wkalrm *t);
static int ds1337_set_alarm(struct device *dev, struct rtc_wkalrm *t);
static int ds1307_alarm_irq_enable(struct device *dev, unsigned int enabled);
static u8 do_trickle_setup_ds1339(struct ds1307 *, u32 ohms, bool diode);
static irqreturn_t rx8130_irq(int irq, void *dev_id);
static int rx8130_read_alarm(struct device *dev, struct rtc_wkalrm *t);
@@ -155,6 +172,8 @@ static irqreturn_t mcp794xx_irq(int irq, void *dev_id);
static int mcp794xx_read_alarm(struct device *dev, struct rtc_wkalrm *t);
static int mcp794xx_set_alarm(struct device *dev, struct rtc_wkalrm *t);
static int mcp794xx_alarm_irq_enable(struct device *dev, unsigned int enabled);
static int m41txx_rtc_read_offset(struct device *dev, long *offset);
static int m41txx_rtc_set_offset(struct device *dev, long offset);

static const struct rtc_class_ops rx8130_rtc_ops = {
	.read_time      = ds1307_get_time,
@@ -172,6 +191,16 @@ static const struct rtc_class_ops mcp794xx_rtc_ops = {
	.alarm_irq_enable = mcp794xx_alarm_irq_enable,
};

static const struct rtc_class_ops m41txx_rtc_ops = {
	.read_time      = ds1307_get_time,
	.set_time       = ds1307_set_time,
	.read_alarm	= ds1337_read_alarm,
	.set_alarm	= ds1337_set_alarm,
	.alarm_irq_enable = ds1307_alarm_irq_enable,
	.read_offset	= m41txx_rtc_read_offset,
	.set_offset	= m41txx_rtc_set_offset,
};

static const struct chip_desc chips[last_ds_type] = {
	[ds_1307] = {
		.nvram_offset	= 8,
@@ -228,10 +257,17 @@ static const struct chip_desc chips[last_ds_type] = {
		.irq_handler = rx8130_irq,
		.rtc_ops = &rx8130_rtc_ops,
	},
	[m41t0] = {
		.rtc_ops	= &m41txx_rtc_ops,
	},
	[m41t00] = {
		.rtc_ops	= &m41txx_rtc_ops,
	},
	[m41t11] = {
		/* this is battery backed SRAM */
		.nvram_offset	= 8,
		.nvram_size	= 56,
		.rtc_ops	= &m41txx_rtc_ops,
	},
	[mcp794xx] = {
		.alarm		= 1,
@@ -973,6 +1009,47 @@ static int mcp794xx_alarm_irq_enable(struct device *dev, unsigned int enabled)
				  enabled ? MCP794XX_BIT_ALM0_EN : 0);
}

static int m41txx_rtc_read_offset(struct device *dev, long *offset)
{
	struct ds1307 *ds1307 = dev_get_drvdata(dev);
	unsigned int ctrl_reg;
	u8 val;

	regmap_read(ds1307->regmap, M41TXX_REG_CONTROL, &ctrl_reg);

	val = ctrl_reg & M41TXX_M_CALIBRATION;

	/* check if positive */
	if (ctrl_reg & M41TXX_BIT_CALIB_SIGN)
		*offset = (val * M41TXX_POS_OFFSET_STEP_PPB);
	else
		*offset = -(val * M41TXX_NEG_OFFSET_STEP_PPB);

	return 0;
}

static int m41txx_rtc_set_offset(struct device *dev, long offset)
{
	struct ds1307 *ds1307 = dev_get_drvdata(dev);
	unsigned int ctrl_reg;

	if ((offset < M41TXX_MIN_OFFSET) || (offset > M41TXX_MAX_OFFSET))
		return -ERANGE;

	if (offset >= 0) {
		ctrl_reg = DIV_ROUND_CLOSEST(offset,
					     M41TXX_POS_OFFSET_STEP_PPB);
		ctrl_reg |= M41TXX_BIT_CALIB_SIGN;
	} else {
		ctrl_reg = DIV_ROUND_CLOSEST(abs(offset),
					     M41TXX_NEG_OFFSET_STEP_PPB);
	}

	return regmap_update_bits(ds1307->regmap, M41TXX_REG_CONTROL,
				  M41TXX_M_CALIBRATION | M41TXX_BIT_CALIB_SIGN,
				  ctrl_reg);
}

/*----------------------------------------------------------------------*/

static int ds1307_nvram_read(void *priv, unsigned int offset, void *val,