Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 09832dc0 authored by Oliver.Rohe@wago.com's avatar Oliver.Rohe@wago.com Committed by Alexandre Belloni
Browse files

rtc: rs5c372: Fix reading from rtc when the oscillator got interrupted.



When the oscillator of the rtc gets interrupted,
e.g. due to an empty battery, reading from the rtc will now return an error
and the oscillator bit will be cleared, once the rtc is successfully reset.

Signed-off-by: default avatarOliver Rohe <oliver.rohe@wago.com>
Signed-off-by: default avatarAlexandre Belloni <alexandre.belloni@bootlin.com>
parent 7dd63ec0
Loading
Loading
Loading
Loading
+47 −7
Original line number Original line Diff line number Diff line
@@ -52,8 +52,10 @@
#	define RS5C_CTRL1_CT4		(4 << 0)	/* 1 Hz level irq */
#	define RS5C_CTRL1_CT4		(4 << 0)	/* 1 Hz level irq */
#define RS5C_REG_CTRL2		15
#define RS5C_REG_CTRL2		15
#	define RS5C372_CTRL2_24		(1 << 5)
#	define RS5C372_CTRL2_24		(1 << 5)
#	define R2x2x_CTRL2_XSTP		(1 << 5)	/* only if R2x2x */
#	define RS5C_CTRL2_XSTP		(1 << 4)	/* only if !R2x2x */
#	define RS5C_CTRL2_XSTP		(1 << 4)	/* only if !R2x2x */
#	define R2x2x_CTRL2_VDET		(1 << 6)	/* only if  R2x2x */
#	define R2x2x_CTRL2_XSTP		(1 << 5)	/* only if  R2x2x */
#	define R2x2x_CTRL2_PON		(1 << 4)	/* only if  R2x2x */
#	define RS5C_CTRL2_CTFG		(1 << 2)
#	define RS5C_CTRL2_CTFG		(1 << 2)
#	define RS5C_CTRL2_AAFG		(1 << 1)	/* or WAFG */
#	define RS5C_CTRL2_AAFG		(1 << 1)	/* or WAFG */
#	define RS5C_CTRL2_BAFG		(1 << 0)	/* or DAFG */
#	define RS5C_CTRL2_BAFG		(1 << 0)	/* or DAFG */
@@ -212,10 +214,27 @@ static int rs5c372_rtc_read_time(struct device *dev, struct rtc_time *tm)
	struct i2c_client *client = to_i2c_client(dev);
	struct i2c_client *client = to_i2c_client(dev);
	struct rs5c372	*rs5c = i2c_get_clientdata(client);
	struct rs5c372	*rs5c = i2c_get_clientdata(client);
	int		status = rs5c_get_regs(rs5c);
	int		status = rs5c_get_regs(rs5c);
	unsigned char ctrl2 = rs5c->regs[RS5C_REG_CTRL2];


	if (status < 0)
	if (status < 0)
		return status;
		return status;


	switch (rs5c->type) {
	case rtc_r2025sd:
	case rtc_r2221tl:
		if ((rs5c->type == rtc_r2025sd && !(ctrl2 & R2x2x_CTRL2_XSTP)) ||
		    (rs5c->type == rtc_r2221tl &&  (ctrl2 & R2x2x_CTRL2_XSTP))) {
			dev_warn(&client->dev, "rtc oscillator interruption detected. Please reset the rtc clock.\n");
			return -EINVAL;
		}
		break;
	default:
		if (ctrl2 & RS5C_CTRL2_XSTP) {
			dev_warn(&client->dev, "rtc oscillator interruption detected. Please reset the rtc clock.\n");
			return -EINVAL;
		}
	}

	tm->tm_sec = bcd2bin(rs5c->regs[RS5C372_REG_SECS] & 0x7f);
	tm->tm_sec = bcd2bin(rs5c->regs[RS5C372_REG_SECS] & 0x7f);
	tm->tm_min = bcd2bin(rs5c->regs[RS5C372_REG_MINS] & 0x7f);
	tm->tm_min = bcd2bin(rs5c->regs[RS5C372_REG_MINS] & 0x7f);
	tm->tm_hour = rs5c_reg2hr(rs5c, rs5c->regs[RS5C372_REG_HOURS]);
	tm->tm_hour = rs5c_reg2hr(rs5c, rs5c->regs[RS5C372_REG_HOURS]);
@@ -243,6 +262,7 @@ static int rs5c372_rtc_set_time(struct device *dev, struct rtc_time *tm)
	struct i2c_client *client = to_i2c_client(dev);
	struct i2c_client *client = to_i2c_client(dev);
	struct rs5c372	*rs5c = i2c_get_clientdata(client);
	struct rs5c372	*rs5c = i2c_get_clientdata(client);
	unsigned char	buf[7];
	unsigned char	buf[7];
	unsigned char	ctrl2;
	int		addr;
	int		addr;


	dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d "
	dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d "
@@ -261,7 +281,32 @@ static int rs5c372_rtc_set_time(struct device *dev, struct rtc_time *tm)
	buf[6] = bin2bcd(tm->tm_year - 100);
	buf[6] = bin2bcd(tm->tm_year - 100);


	if (i2c_smbus_write_i2c_block_data(client, addr, sizeof(buf), buf) < 0) {
	if (i2c_smbus_write_i2c_block_data(client, addr, sizeof(buf), buf) < 0) {
		dev_err(&client->dev, "%s: write error\n", __func__);
		dev_dbg(&client->dev, "%s: write error in line %i\n",
			__func__, __LINE__);
		return -EIO;
	}

	addr = RS5C_ADDR(RS5C_REG_CTRL2);
	ctrl2 = i2c_smbus_read_byte_data(client, addr);

	/* clear rtc warning bits */
	switch (rs5c->type) {
	case rtc_r2025sd:
	case rtc_r2221tl:
		ctrl2 &= ~(R2x2x_CTRL2_VDET | R2x2x_CTRL2_PON);
		if (rs5c->type == rtc_r2025sd)
			ctrl2 |= R2x2x_CTRL2_XSTP;
		else
			ctrl2 &= ~R2x2x_CTRL2_XSTP;
		break;
	default:
		ctrl2 &= ~RS5C_CTRL2_XSTP;
		break;
	}

	if (i2c_smbus_write_byte_data(client, addr, ctrl2) < 0) {
		dev_dbg(&client->dev, "%s: write error in line %i\n",
			__func__, __LINE__);
		return -EIO;
		return -EIO;
	}
	}


@@ -523,23 +568,18 @@ static int rs5c_oscillator_setup(struct rs5c372 *rs5c372)
	buf[0] = rs5c372->regs[RS5C_REG_CTRL1];
	buf[0] = rs5c372->regs[RS5C_REG_CTRL1];
	buf[1] = rs5c372->regs[RS5C_REG_CTRL2];
	buf[1] = rs5c372->regs[RS5C_REG_CTRL2];


	/* handle xstp bit */
	switch (rs5c372->type) {
	switch (rs5c372->type) {
	case rtc_r2025sd:
	case rtc_r2025sd:
		if (buf[1] & R2x2x_CTRL2_XSTP)
		if (buf[1] & R2x2x_CTRL2_XSTP)
			return ret;
			return ret;
		rs5c372->regs[RS5C_REG_CTRL2] |= R2x2x_CTRL2_XSTP;
		break;
		break;
	case rtc_r2221tl:
	case rtc_r2221tl:
		if (!(buf[1] & R2x2x_CTRL2_XSTP))
		if (!(buf[1] & R2x2x_CTRL2_XSTP))
			return ret;
			return ret;
		rs5c372->regs[RS5C_REG_CTRL2] &= ~R2x2x_CTRL2_XSTP;
		break;
		break;

	default:
	default:
		if (!(buf[1] & RS5C_CTRL2_XSTP))
		if (!(buf[1] & RS5C_CTRL2_XSTP))
			return ret;
			return ret;
		rs5c372->regs[RS5C_REG_CTRL2] &= ~RS5C_CTRL2_XSTP;
		break;
		break;
	}
	}