Skip to content

Commit

Permalink
rtc: s35390a: handle invalid RTC time
Browse files Browse the repository at this point in the history
If RTC time have been altered by low voltage, we notify users
that RTC time is invalid by returning -EINVAL.
The RTC time needs to be set correctly to clear the invalid flag.

Signed-off-by: Fabien Lahoudere <fabien.lahoudere@collabora.co.uk>
Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
  • Loading branch information
Fabien Lahoudere authored and Alexandre Belloni committed Jul 30, 2017
1 parent 5771a8c commit 16486d0
Showing 1 changed file with 42 additions and 30 deletions.
72 changes: 42 additions & 30 deletions drivers/rtc/rtc-s35390a.c
Original file line number Diff line number Diff line change
Expand Up @@ -106,33 +106,12 @@ static int s35390a_get_reg(struct s35390a *s35390a, int reg, char *buf, int len)
return 0;
}

/*
* Returns <0 on error, 0 if rtc is setup fine and 1 if the chip was reset.
* To keep the information if an irq is pending, pass the value read from
* STATUS1 to the caller.
*/
static int s35390a_reset(struct s35390a *s35390a, char *status1)
static int s35390a_init(struct s35390a *s35390a)
{
char buf;
int ret;
unsigned initcount = 0;

ret = s35390a_get_reg(s35390a, S35390A_CMD_STATUS1, status1, 1);
if (ret < 0)
return ret;

if (*status1 & S35390A_FLAG_POC)
/*
* Do not communicate for 0.5 seconds since the power-on
* detection circuit is in operation.
*/
msleep(500);
else if (!(*status1 & S35390A_FLAG_BLD))
/*
* If both POC and BLD are unset everything is fine.
*/
return 0;

/*
* At least one of POC and BLD are set, so reinitialise chip. Keeping
* this information in the hardware to know later that the time isn't
Expand All @@ -142,7 +121,6 @@ static int s35390a_reset(struct s35390a *s35390a, char *status1)
* The 24H bit is kept over reset, so set it already here.
*/
initialize:
*status1 = S35390A_FLAG_24H;
buf = S35390A_FLAG_RESET | S35390A_FLAG_24H;
ret = s35390a_set_reg(s35390a, S35390A_CMD_STATUS1, &buf, 1);

Expand All @@ -165,6 +143,34 @@ static int s35390a_reset(struct s35390a *s35390a, char *status1)
return 1;
}

/*
* Returns <0 on error, 0 if rtc is setup fine and 1 if the chip was reset.
* To keep the information if an irq is pending, pass the value read from
* STATUS1 to the caller.
*/
static int s35390a_read_status(struct s35390a *s35390a, char *status1)
{
int ret;

ret = s35390a_get_reg(s35390a, S35390A_CMD_STATUS1, status1, 1);
if (ret < 0)
return ret;

if (*status1 & S35390A_FLAG_POC) {
/*
* Do not communicate for 0.5 seconds since the power-on
* detection circuit is in operation.
*/
msleep(500);
return 1;
} else if (*status1 & S35390A_FLAG_BLD)
return 1;
/*
* If both POC and BLD are unset everything is fine.
*/
return 0;
}

static int s35390a_disable_test_mode(struct s35390a *s35390a)
{
char buf[1];
Expand Down Expand Up @@ -208,13 +214,16 @@ static int s35390a_set_datetime(struct i2c_client *client, struct rtc_time *tm)
{
struct s35390a *s35390a = i2c_get_clientdata(client);
int i, err;
char buf[7];
char buf[7], status;

dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d mday=%d, "
"mon=%d, year=%d, wday=%d\n", __func__, tm->tm_sec,
tm->tm_min, tm->tm_hour, tm->tm_mday, tm->tm_mon, tm->tm_year,
tm->tm_wday);

if (s35390a_read_status(s35390a, &status) == 1)
s35390a_init(s35390a);

buf[S35390A_BYTE_YEAR] = bin2bcd(tm->tm_year - 100);
buf[S35390A_BYTE_MONTH] = bin2bcd(tm->tm_mon + 1);
buf[S35390A_BYTE_DAY] = bin2bcd(tm->tm_mday);
Expand All @@ -235,9 +244,12 @@ static int s35390a_set_datetime(struct i2c_client *client, struct rtc_time *tm)
static int s35390a_get_datetime(struct i2c_client *client, struct rtc_time *tm)
{
struct s35390a *s35390a = i2c_get_clientdata(client);
char buf[7];
char buf[7], status;
int i, err;

if (s35390a_read_status(s35390a, &status) == 1)
return -EINVAL;

err = s35390a_get_reg(s35390a, S35390A_CMD_TIME1, buf, sizeof(buf));
if (err < 0)
return err;
Expand Down Expand Up @@ -405,7 +417,7 @@ static struct i2c_driver s35390a_driver;
static int s35390a_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int err, err_reset;
int err, err_read;
unsigned int i;
struct s35390a *s35390a;
struct rtc_time tm;
Expand Down Expand Up @@ -438,9 +450,9 @@ static int s35390a_probe(struct i2c_client *client,
}
}

err_reset = s35390a_reset(s35390a, &status1);
if (err_reset < 0) {
err = err_reset;
err_read = s35390a_read_status(s35390a, &status1);
if (err_read < 0) {
err = err_read;
dev_err(&client->dev, "error resetting chip\n");
goto exit_dummy;
}
Expand All @@ -466,7 +478,7 @@ static int s35390a_probe(struct i2c_client *client,
}
}

if (err_reset > 0 || s35390a_get_datetime(client, &tm) < 0)
if (err_read > 0 || s35390a_get_datetime(client, &tm) < 0)
dev_warn(&client->dev, "clock needs to be set\n");

device_set_wakeup_capable(&client->dev, 1);
Expand Down

0 comments on commit 16486d0

Please sign in to comment.