All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2] RTC/PCF85063: fix reading/setting time/date
@ 2016-02-09 10:57 ` Juergen Borleis
  0 siblings, 0 replies; 10+ messages in thread
From: Juergen Borleis @ 2016-02-09 10:57 UTC (permalink / raw)
  To: rtc-linux; +Cc: Alessandro Zummo, Alexandre Belloni, linux-kernel, kernel

The PCF85063 RTC needs special treatment while setting or reading the
time/date:

 - when reading the 7 time/date registers they are blocked from updating by
   the 'one second' pulse internally. So reading all time/date registers
   should happen in one turn to ensure reading is an atomic operation

 - to let setting the time/date be an atomic operation as well, the clock
   dividers must be kept in reset state to avoid a 'one second' pulse during
   writing the 7 time/date registers

Changes from v1:

 - using high level I2C access functions instead of the low level i2c_transfer()
   to simplify the code

Comments are welcome
Juergen

Please keep me on CC since I'm not subscribed to the list.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [rtc-linux] [PATCH v2] RTC/PCF85063: fix reading/setting time/date
@ 2016-02-09 10:57 ` Juergen Borleis
  0 siblings, 0 replies; 10+ messages in thread
From: Juergen Borleis @ 2016-02-09 10:57 UTC (permalink / raw)
  To: rtc-linux; +Cc: Alessandro Zummo, Alexandre Belloni, linux-kernel, kernel

The PCF85063 RTC needs special treatment while setting or reading the
time/date:

 - when reading the 7 time/date registers they are blocked from updating by
   the 'one second' pulse internally. So reading all time/date registers
   should happen in one turn to ensure reading is an atomic operation

 - to let setting the time/date be an atomic operation as well, the clock
   dividers must be kept in reset state to avoid a 'one second' pulse during
   writing the 7 time/date registers

Changes from v1:

 - using high level I2C access functions instead of the low level i2c_transfer()
   to simplify the code

Comments are welcome
Juergen

Please keep me on CC since I'm not subscribed to the list.

-- 
-- 
You received this message because you are subscribed to "rtc-linux".
Membership options at http://groups.google.com/group/rtc-linux .
Please read http://groups.google.com/group/rtc-linux/web/checklist
before submitting a driver.
--- 
You received this message because you are subscribed to the Google Groups "rtc-linux" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rtc-linux+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH v2 1/3] RTC/PCF85063: simplify code to read the current time
  2016-02-09 10:57 ` [rtc-linux] " Juergen Borleis
@ 2016-02-09 10:57   ` Juergen Borleis
  -1 siblings, 0 replies; 10+ messages in thread
From: Juergen Borleis @ 2016-02-09 10:57 UTC (permalink / raw)
  To: rtc-linux; +Cc: Alessandro Zummo, Alexandre Belloni, linux-kernel, kernel

By using i2c_smbus_read_i2c_block_data() the code is now much simpler.

While at it: when reading the RTC's seconds register, all time/date registers
are frozen until the RTC's year register is read. So it is important to read
all time/date registers in one turn to not lose a second event. Make it more
clear why the read must happen in this way.

Signed-off-by: Juergen Borleis <jbe@pengutronix.de>
---
 drivers/rtc/rtc-pcf85063.c | 47 +++++++++++++++++++++-------------------------
 1 file changed, 21 insertions(+), 26 deletions(-)

diff --git a/drivers/rtc/rtc-pcf85063.c b/drivers/rtc/rtc-pcf85063.c
index 63334cb..7f9caee 100644
--- a/drivers/rtc/rtc-pcf85063.c
+++ b/drivers/rtc/rtc-pcf85063.c
@@ -45,39 +45,34 @@ struct pcf85063 {
  */
 static int pcf85063_get_datetime(struct i2c_client *client, struct rtc_time *tm)
 {
+	int rc;
 	struct pcf85063 *pcf85063 = i2c_get_clientdata(client);
-	unsigned char buf[13] = { PCF85063_REG_CTRL1 };
-	struct i2c_msg msgs[] = {
-		{/* setup read ptr */
-			.addr = client->addr,
-			.len = 1,
-			.buf = buf
-		},
-		{/* read status + date */
-			.addr = client->addr,
-			.flags = I2C_M_RD,
-			.len = 13,
-			.buf = buf
-		},
-	};
-
-	/* read registers */
-	if ((i2c_transfer(client->adapter, msgs, 2)) != 2) {
-		dev_err(&client->dev, "%s: read error\n", __func__);
+	u8 regs[7];
+
+	/*
+	 * while reading, the time/date registers are blocked and not updated
+	 * anymore until the access is finished. To not lose a second
+	 * event, the access must be finished within one second. So, read all
+	 * time/date registers in one turn.
+	 */
+	rc = i2c_smbus_read_i2c_block_data(client, PCF85063_REG_SC,
+					   sizeof(regs), regs);
+	if (rc != sizeof(regs)) {
+		dev_err(&client->dev, "date/time register read error\n");
 		return -EIO;
 	}
 
-	tm->tm_sec = bcd2bin(buf[PCF85063_REG_SC] & 0x7F);
-	tm->tm_min = bcd2bin(buf[PCF85063_REG_MN] & 0x7F);
-	tm->tm_hour = bcd2bin(buf[PCF85063_REG_HR] & 0x3F); /* rtc hr 0-23 */
-	tm->tm_mday = bcd2bin(buf[PCF85063_REG_DM] & 0x3F);
-	tm->tm_wday = buf[PCF85063_REG_DW] & 0x07;
-	tm->tm_mon = bcd2bin(buf[PCF85063_REG_MO] & 0x1F) - 1; /* rtc mn 1-12 */
-	tm->tm_year = bcd2bin(buf[PCF85063_REG_YR]);
+	tm->tm_sec = bcd2bin(regs[0] & 0x7F);
+	tm->tm_min = bcd2bin(regs[1] & 0x7F);
+	tm->tm_hour = bcd2bin(regs[2] & 0x3F); /* rtc hr 0-23 */
+	tm->tm_mday = bcd2bin(regs[3] & 0x3F);
+	tm->tm_wday = regs[4] & 0x07;
+	tm->tm_mon = bcd2bin(regs[5] & 0x1F) - 1; /* rtc mn 1-12 */
+	tm->tm_year = bcd2bin(regs[6]);
 	if (tm->tm_year < 70)
 		tm->tm_year += 100;	/* assume we are in 1970...2069 */
 	/* detect the polarity heuristically. see note above. */
-	pcf85063->c_polarity = (buf[PCF85063_REG_MO] & PCF85063_MO_C) ?
+	pcf85063->c_polarity = (regs[5] & PCF85063_MO_C) ?
 		(tm->tm_year >= 100) : (tm->tm_year < 100);
 
 	return rtc_valid_tm(tm);
-- 
2.7.0.rc3

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [rtc-linux] [PATCH v2 1/3] RTC/PCF85063: simplify code to read the current time
@ 2016-02-09 10:57   ` Juergen Borleis
  0 siblings, 0 replies; 10+ messages in thread
From: Juergen Borleis @ 2016-02-09 10:57 UTC (permalink / raw)
  To: rtc-linux; +Cc: Alessandro Zummo, Alexandre Belloni, linux-kernel, kernel

By using i2c_smbus_read_i2c_block_data() the code is now much simpler.

While at it: when reading the RTC's seconds register, all time/date registers
are frozen until the RTC's year register is read. So it is important to read
all time/date registers in one turn to not lose a second event. Make it more
clear why the read must happen in this way.

Signed-off-by: Juergen Borleis <jbe@pengutronix.de>
---
 drivers/rtc/rtc-pcf85063.c | 47 +++++++++++++++++++++-------------------------
 1 file changed, 21 insertions(+), 26 deletions(-)

diff --git a/drivers/rtc/rtc-pcf85063.c b/drivers/rtc/rtc-pcf85063.c
index 63334cb..7f9caee 100644
--- a/drivers/rtc/rtc-pcf85063.c
+++ b/drivers/rtc/rtc-pcf85063.c
@@ -45,39 +45,34 @@ struct pcf85063 {
  */
 static int pcf85063_get_datetime(struct i2c_client *client, struct rtc_time *tm)
 {
+	int rc;
 	struct pcf85063 *pcf85063 = i2c_get_clientdata(client);
-	unsigned char buf[13] = { PCF85063_REG_CTRL1 };
-	struct i2c_msg msgs[] = {
-		{/* setup read ptr */
-			.addr = client->addr,
-			.len = 1,
-			.buf = buf
-		},
-		{/* read status + date */
-			.addr = client->addr,
-			.flags = I2C_M_RD,
-			.len = 13,
-			.buf = buf
-		},
-	};
-
-	/* read registers */
-	if ((i2c_transfer(client->adapter, msgs, 2)) != 2) {
-		dev_err(&client->dev, "%s: read error\n", __func__);
+	u8 regs[7];
+
+	/*
+	 * while reading, the time/date registers are blocked and not updated
+	 * anymore until the access is finished. To not lose a second
+	 * event, the access must be finished within one second. So, read all
+	 * time/date registers in one turn.
+	 */
+	rc = i2c_smbus_read_i2c_block_data(client, PCF85063_REG_SC,
+					   sizeof(regs), regs);
+	if (rc != sizeof(regs)) {
+		dev_err(&client->dev, "date/time register read error\n");
 		return -EIO;
 	}
 
-	tm->tm_sec = bcd2bin(buf[PCF85063_REG_SC] & 0x7F);
-	tm->tm_min = bcd2bin(buf[PCF85063_REG_MN] & 0x7F);
-	tm->tm_hour = bcd2bin(buf[PCF85063_REG_HR] & 0x3F); /* rtc hr 0-23 */
-	tm->tm_mday = bcd2bin(buf[PCF85063_REG_DM] & 0x3F);
-	tm->tm_wday = buf[PCF85063_REG_DW] & 0x07;
-	tm->tm_mon = bcd2bin(buf[PCF85063_REG_MO] & 0x1F) - 1; /* rtc mn 1-12 */
-	tm->tm_year = bcd2bin(buf[PCF85063_REG_YR]);
+	tm->tm_sec = bcd2bin(regs[0] & 0x7F);
+	tm->tm_min = bcd2bin(regs[1] & 0x7F);
+	tm->tm_hour = bcd2bin(regs[2] & 0x3F); /* rtc hr 0-23 */
+	tm->tm_mday = bcd2bin(regs[3] & 0x3F);
+	tm->tm_wday = regs[4] & 0x07;
+	tm->tm_mon = bcd2bin(regs[5] & 0x1F) - 1; /* rtc mn 1-12 */
+	tm->tm_year = bcd2bin(regs[6]);
 	if (tm->tm_year < 70)
 		tm->tm_year += 100;	/* assume we are in 1970...2069 */
 	/* detect the polarity heuristically. see note above. */
-	pcf85063->c_polarity = (buf[PCF85063_REG_MO] & PCF85063_MO_C) ?
+	pcf85063->c_polarity = (regs[5] & PCF85063_MO_C) ?
 		(tm->tm_year >= 100) : (tm->tm_year < 100);
 
 	return rtc_valid_tm(tm);
-- 
2.7.0.rc3

-- 
-- 
You received this message because you are subscribed to "rtc-linux".
Membership options at http://groups.google.com/group/rtc-linux .
Please read http://groups.google.com/group/rtc-linux/web/checklist
before submitting a driver.
--- 
You received this message because you are subscribed to the Google Groups "rtc-linux" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rtc-linux+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v2 2/3] RTC/PCF85063: fix time/date reading
  2016-02-09 10:57 ` [rtc-linux] " Juergen Borleis
@ 2016-02-09 10:57   ` Juergen Borleis
  -1 siblings, 0 replies; 10+ messages in thread
From: Juergen Borleis @ 2016-02-09 10:57 UTC (permalink / raw)
  To: rtc-linux; +Cc: Alessandro Zummo, Alexandre Belloni, linux-kernel, kernel

Check if the RTC signals an invalid time/date (due to a battery power loss
for example). In this case ignore the time/date until it is really set again.

Signed-off-by: Juergen Borleis <jbe@pengutronix.de>
---
 drivers/rtc/rtc-pcf85063.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/drivers/rtc/rtc-pcf85063.c b/drivers/rtc/rtc-pcf85063.c
index 7f9caee..e0343e6 100644
--- a/drivers/rtc/rtc-pcf85063.c
+++ b/drivers/rtc/rtc-pcf85063.c
@@ -22,6 +22,7 @@
 #define PCF85063_REG_CTRL2		0x01
 
 #define PCF85063_REG_SC			0x04 /* datetime */
+#define PCF85063_REG_SC_OS		0x80
 #define PCF85063_REG_MN			0x05
 #define PCF85063_REG_HR			0x06
 #define PCF85063_REG_DM			0x07
@@ -62,6 +63,12 @@ static int pcf85063_get_datetime(struct i2c_client *client, struct rtc_time *tm)
 		return -EIO;
 	}
 
+	/* if the clock has lost its power it makes no sense to use its time */
+	if (regs[0] & PCF85063_REG_SC_OS) {
+		dev_warn(&client->dev, "Power loss detected, invalid time\n");
+		return -EINVAL;
+	}
+
 	tm->tm_sec = bcd2bin(regs[0] & 0x7F);
 	tm->tm_min = bcd2bin(regs[1] & 0x7F);
 	tm->tm_hour = bcd2bin(regs[2] & 0x3F); /* rtc hr 0-23 */
-- 
2.7.0.rc3

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [rtc-linux] [PATCH v2 2/3] RTC/PCF85063: fix time/date reading
@ 2016-02-09 10:57   ` Juergen Borleis
  0 siblings, 0 replies; 10+ messages in thread
From: Juergen Borleis @ 2016-02-09 10:57 UTC (permalink / raw)
  To: rtc-linux; +Cc: Alessandro Zummo, Alexandre Belloni, linux-kernel, kernel

Check if the RTC signals an invalid time/date (due to a battery power loss
for example). In this case ignore the time/date until it is really set again.

Signed-off-by: Juergen Borleis <jbe@pengutronix.de>
---
 drivers/rtc/rtc-pcf85063.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/drivers/rtc/rtc-pcf85063.c b/drivers/rtc/rtc-pcf85063.c
index 7f9caee..e0343e6 100644
--- a/drivers/rtc/rtc-pcf85063.c
+++ b/drivers/rtc/rtc-pcf85063.c
@@ -22,6 +22,7 @@
 #define PCF85063_REG_CTRL2		0x01
 
 #define PCF85063_REG_SC			0x04 /* datetime */
+#define PCF85063_REG_SC_OS		0x80
 #define PCF85063_REG_MN			0x05
 #define PCF85063_REG_HR			0x06
 #define PCF85063_REG_DM			0x07
@@ -62,6 +63,12 @@ static int pcf85063_get_datetime(struct i2c_client *client, struct rtc_time *tm)
 		return -EIO;
 	}
 
+	/* if the clock has lost its power it makes no sense to use its time */
+	if (regs[0] & PCF85063_REG_SC_OS) {
+		dev_warn(&client->dev, "Power loss detected, invalid time\n");
+		return -EINVAL;
+	}
+
 	tm->tm_sec = bcd2bin(regs[0] & 0x7F);
 	tm->tm_min = bcd2bin(regs[1] & 0x7F);
 	tm->tm_hour = bcd2bin(regs[2] & 0x3F); /* rtc hr 0-23 */
-- 
2.7.0.rc3

-- 
-- 
You received this message because you are subscribed to "rtc-linux".
Membership options at http://groups.google.com/group/rtc-linux .
Please read http://groups.google.com/group/rtc-linux/web/checklist
before submitting a driver.
--- 
You received this message because you are subscribed to the Google Groups "rtc-linux" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rtc-linux+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v2 3/3] RTC/PCF85063: fix time/date setting
  2016-02-09 10:57 ` [rtc-linux] " Juergen Borleis
@ 2016-02-09 10:57   ` Juergen Borleis
  -1 siblings, 0 replies; 10+ messages in thread
From: Juergen Borleis @ 2016-02-09 10:57 UTC (permalink / raw)
  To: rtc-linux; +Cc: Alessandro Zummo, Alexandre Belloni, linux-kernel, kernel

When setting a new time/date the RTC's clock must be stopped first, in
order to write the time/date registers in an atomic manner.
So, this change stops the clock first and then writes the time/date
registers and the clock control register (to re-enable the clock) in one
turn.

Signed-off-by: Juergen Borleis <jbe@pengutronix.de>
---
 drivers/rtc/rtc-pcf85063.c | 78 ++++++++++++++++++++++++++++++++--------------
 1 file changed, 55 insertions(+), 23 deletions(-)

diff --git a/drivers/rtc/rtc-pcf85063.c b/drivers/rtc/rtc-pcf85063.c
index e0343e6..c5db231 100644
--- a/drivers/rtc/rtc-pcf85063.c
+++ b/drivers/rtc/rtc-pcf85063.c
@@ -19,6 +19,7 @@
 #define DRV_VERSION "0.0.1"
 
 #define PCF85063_REG_CTRL1		0x00 /* status */
+#define PCF85063_REG_CTRL1_STOP		BIT(5)
 #define PCF85063_REG_CTRL2		0x01
 
 #define PCF85063_REG_SC			0x04 /* datetime */
@@ -40,6 +41,30 @@ struct pcf85063 {
 	int voltage_low; /* indicates if a low_voltage was detected */
 };
 
+static int pcf85063_stop_clock(struct i2c_client *client, u8 *ctrl1)
+{
+	s32 ret;
+
+	ret = i2c_smbus_read_byte_data(client, PCF85063_REG_CTRL1);
+	if (ret < 0) {
+		dev_err(&client->dev, "Failing to stop the clock\n");
+		return -EIO;
+	}
+
+	/* stop the clock */
+	ret |= PCF85063_REG_CTRL1_STOP;
+
+	ret = i2c_smbus_write_byte_data(client, PCF85063_REG_CTRL1, ret);
+	if (ret < 0) {
+		dev_err(&client->dev, "Failing to stop the clock\n");
+		return -EIO;
+	}
+
+	*ctrl1 = ret;
+
+	return 0;
+}
+
 /*
  * In the routines that deal directly with the pcf85063 hardware, we use
  * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch.
@@ -87,41 +112,48 @@ static int pcf85063_get_datetime(struct i2c_client *client, struct rtc_time *tm)
 
 static int pcf85063_set_datetime(struct i2c_client *client, struct rtc_time *tm)
 {
-	int i = 0, err = 0;
-	unsigned char buf[11];
+	int rc;
+	u8 regs[8];
 
-	/* Control & status */
-	buf[PCF85063_REG_CTRL1] = 0;
-	buf[PCF85063_REG_CTRL2] = 5;
+	/*
+	 * to accurately set the time, reset the divider chain and keep it in
+	 * reset state until all time/date registers are written
+	 */
+	rc = pcf85063_stop_clock(client, &regs[7]);
+	if (rc != 0)
+		return rc;
 
 	/* hours, minutes and seconds */
-	buf[PCF85063_REG_SC] = bin2bcd(tm->tm_sec) & 0x7F;
+	regs[0] = bin2bcd(tm->tm_sec) & 0x7F; /* clear OS flag */
 
-	buf[PCF85063_REG_MN] = bin2bcd(tm->tm_min);
-	buf[PCF85063_REG_HR] = bin2bcd(tm->tm_hour);
+	regs[1] = bin2bcd(tm->tm_min);
+	regs[2] = bin2bcd(tm->tm_hour);
 
 	/* Day of month, 1 - 31 */
-	buf[PCF85063_REG_DM] = bin2bcd(tm->tm_mday);
+	regs[3] = bin2bcd(tm->tm_mday);
 
 	/* Day, 0 - 6 */
-	buf[PCF85063_REG_DW] = tm->tm_wday & 0x07;
+	regs[4] = tm->tm_wday & 0x07;
 
 	/* month, 1 - 12 */
-	buf[PCF85063_REG_MO] = bin2bcd(tm->tm_mon + 1);
+	regs[5] = bin2bcd(tm->tm_mon + 1);
 
 	/* year and century */
-	buf[PCF85063_REG_YR] = bin2bcd(tm->tm_year % 100);
-
-	/* write register's data */
-	for (i = 0; i < sizeof(buf); i++) {
-		unsigned char data[2] = { i, buf[i] };
-
-		err = i2c_master_send(client, data, sizeof(data));
-		if (err != sizeof(data)) {
-			dev_err(&client->dev, "%s: err=%d addr=%02x, data=%02x\n",
-					__func__, err, data[0], data[1]);
-			return -EIO;
-		}
+	regs[6] = bin2bcd(tm->tm_year % 100);
+
+	/*
+	 * after all time/date registers are written, let the 'address auto
+	 * increment' feature wrap around and write register CTRL1 to re-enable
+	 * the clock divider chain again
+	 */
+	regs[7] &= ~PCF85063_REG_CTRL1_STOP;
+
+	/* write all registers at once */
+	rc = i2c_smbus_write_i2c_block_data(client, PCF85063_REG_SC,
+					    sizeof(regs), regs);
+	if (rc < 0) {
+		dev_err(&client->dev, "date/time register write error\n");
+		return rc;
 	}
 
 	return 0;
-- 
2.7.0.rc3

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [rtc-linux] [PATCH v2 3/3] RTC/PCF85063: fix time/date setting
@ 2016-02-09 10:57   ` Juergen Borleis
  0 siblings, 0 replies; 10+ messages in thread
From: Juergen Borleis @ 2016-02-09 10:57 UTC (permalink / raw)
  To: rtc-linux; +Cc: Alessandro Zummo, Alexandre Belloni, linux-kernel, kernel

When setting a new time/date the RTC's clock must be stopped first, in
order to write the time/date registers in an atomic manner.
So, this change stops the clock first and then writes the time/date
registers and the clock control register (to re-enable the clock) in one
turn.

Signed-off-by: Juergen Borleis <jbe@pengutronix.de>
---
 drivers/rtc/rtc-pcf85063.c | 78 ++++++++++++++++++++++++++++++++--------------
 1 file changed, 55 insertions(+), 23 deletions(-)

diff --git a/drivers/rtc/rtc-pcf85063.c b/drivers/rtc/rtc-pcf85063.c
index e0343e6..c5db231 100644
--- a/drivers/rtc/rtc-pcf85063.c
+++ b/drivers/rtc/rtc-pcf85063.c
@@ -19,6 +19,7 @@
 #define DRV_VERSION "0.0.1"
 
 #define PCF85063_REG_CTRL1		0x00 /* status */
+#define PCF85063_REG_CTRL1_STOP		BIT(5)
 #define PCF85063_REG_CTRL2		0x01
 
 #define PCF85063_REG_SC			0x04 /* datetime */
@@ -40,6 +41,30 @@ struct pcf85063 {
 	int voltage_low; /* indicates if a low_voltage was detected */
 };
 
+static int pcf85063_stop_clock(struct i2c_client *client, u8 *ctrl1)
+{
+	s32 ret;
+
+	ret = i2c_smbus_read_byte_data(client, PCF85063_REG_CTRL1);
+	if (ret < 0) {
+		dev_err(&client->dev, "Failing to stop the clock\n");
+		return -EIO;
+	}
+
+	/* stop the clock */
+	ret |= PCF85063_REG_CTRL1_STOP;
+
+	ret = i2c_smbus_write_byte_data(client, PCF85063_REG_CTRL1, ret);
+	if (ret < 0) {
+		dev_err(&client->dev, "Failing to stop the clock\n");
+		return -EIO;
+	}
+
+	*ctrl1 = ret;
+
+	return 0;
+}
+
 /*
  * In the routines that deal directly with the pcf85063 hardware, we use
  * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch.
@@ -87,41 +112,48 @@ static int pcf85063_get_datetime(struct i2c_client *client, struct rtc_time *tm)
 
 static int pcf85063_set_datetime(struct i2c_client *client, struct rtc_time *tm)
 {
-	int i = 0, err = 0;
-	unsigned char buf[11];
+	int rc;
+	u8 regs[8];
 
-	/* Control & status */
-	buf[PCF85063_REG_CTRL1] = 0;
-	buf[PCF85063_REG_CTRL2] = 5;
+	/*
+	 * to accurately set the time, reset the divider chain and keep it in
+	 * reset state until all time/date registers are written
+	 */
+	rc = pcf85063_stop_clock(client, &regs[7]);
+	if (rc != 0)
+		return rc;
 
 	/* hours, minutes and seconds */
-	buf[PCF85063_REG_SC] = bin2bcd(tm->tm_sec) & 0x7F;
+	regs[0] = bin2bcd(tm->tm_sec) & 0x7F; /* clear OS flag */
 
-	buf[PCF85063_REG_MN] = bin2bcd(tm->tm_min);
-	buf[PCF85063_REG_HR] = bin2bcd(tm->tm_hour);
+	regs[1] = bin2bcd(tm->tm_min);
+	regs[2] = bin2bcd(tm->tm_hour);
 
 	/* Day of month, 1 - 31 */
-	buf[PCF85063_REG_DM] = bin2bcd(tm->tm_mday);
+	regs[3] = bin2bcd(tm->tm_mday);
 
 	/* Day, 0 - 6 */
-	buf[PCF85063_REG_DW] = tm->tm_wday & 0x07;
+	regs[4] = tm->tm_wday & 0x07;
 
 	/* month, 1 - 12 */
-	buf[PCF85063_REG_MO] = bin2bcd(tm->tm_mon + 1);
+	regs[5] = bin2bcd(tm->tm_mon + 1);
 
 	/* year and century */
-	buf[PCF85063_REG_YR] = bin2bcd(tm->tm_year % 100);
-
-	/* write register's data */
-	for (i = 0; i < sizeof(buf); i++) {
-		unsigned char data[2] = { i, buf[i] };
-
-		err = i2c_master_send(client, data, sizeof(data));
-		if (err != sizeof(data)) {
-			dev_err(&client->dev, "%s: err=%d addr=%02x, data=%02x\n",
-					__func__, err, data[0], data[1]);
-			return -EIO;
-		}
+	regs[6] = bin2bcd(tm->tm_year % 100);
+
+	/*
+	 * after all time/date registers are written, let the 'address auto
+	 * increment' feature wrap around and write register CTRL1 to re-enable
+	 * the clock divider chain again
+	 */
+	regs[7] &= ~PCF85063_REG_CTRL1_STOP;
+
+	/* write all registers at once */
+	rc = i2c_smbus_write_i2c_block_data(client, PCF85063_REG_SC,
+					    sizeof(regs), regs);
+	if (rc < 0) {
+		dev_err(&client->dev, "date/time register write error\n");
+		return rc;
 	}
 
 	return 0;
-- 
2.7.0.rc3

-- 
-- 
You received this message because you are subscribed to "rtc-linux".
Membership options at http://groups.google.com/group/rtc-linux .
Please read http://groups.google.com/group/rtc-linux/web/checklist
before submitting a driver.
--- 
You received this message because you are subscribed to the Google Groups "rtc-linux" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rtc-linux+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH v2] RTC/PCF85063: fix reading/setting time/date
  2016-02-09 10:57 ` [rtc-linux] " Juergen Borleis
@ 2016-02-23 23:04   ` Alexandre Belloni
  -1 siblings, 0 replies; 10+ messages in thread
From: Alexandre Belloni @ 2016-02-23 23:04 UTC (permalink / raw)
  To: Juergen Borleis; +Cc: rtc-linux, Alessandro Zummo, linux-kernel, kernel, jbe

On 09/02/2016 at 11:57:24 +0100, Juergen Borleis wrote :
> The PCF85063 RTC needs special treatment while setting or reading the
> time/date:
> 
>  - when reading the 7 time/date registers they are blocked from updating by
>    the 'one second' pulse internally. So reading all time/date registers
>    should happen in one turn to ensure reading is an atomic operation
> 
>  - to let setting the time/date be an atomic operation as well, the clock
>    dividers must be kept in reset state to avoid a 'one second' pulse during
>    writing the 7 time/date registers
> 
> Changes from v1:
> 
>  - using high level I2C access functions instead of the low level i2c_transfer()
>    to simplify the code
> 

All applied, thanks!

However, I found two interesting things in that driver. I'll send a
patch shortly, removing the useless century handling.
Also, I'm quite unsure about the year range handling. I have a patch but
I'm not sure about the RTC behaviour when the year goes from 99 to 00.
Can you check that works properly?

-- 
Alexandre Belloni, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [rtc-linux] Re: [PATCH v2] RTC/PCF85063: fix reading/setting time/date
@ 2016-02-23 23:04   ` Alexandre Belloni
  0 siblings, 0 replies; 10+ messages in thread
From: Alexandre Belloni @ 2016-02-23 23:04 UTC (permalink / raw)
  To: Juergen Borleis; +Cc: rtc-linux, Alessandro Zummo, linux-kernel, kernel, jbe

On 09/02/2016 at 11:57:24 +0100, Juergen Borleis wrote :
> The PCF85063 RTC needs special treatment while setting or reading the
> time/date:
> 
>  - when reading the 7 time/date registers they are blocked from updating by
>    the 'one second' pulse internally. So reading all time/date registers
>    should happen in one turn to ensure reading is an atomic operation
> 
>  - to let setting the time/date be an atomic operation as well, the clock
>    dividers must be kept in reset state to avoid a 'one second' pulse during
>    writing the 7 time/date registers
> 
> Changes from v1:
> 
>  - using high level I2C access functions instead of the low level i2c_transfer()
>    to simplify the code
> 

All applied, thanks!

However, I found two interesting things in that driver. I'll send a
patch shortly, removing the useless century handling.
Also, I'm quite unsure about the year range handling. I have a patch but
I'm not sure about the RTC behaviour when the year goes from 99 to 00.
Can you check that works properly?

-- 
Alexandre Belloni, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com

-- 
-- 
You received this message because you are subscribed to "rtc-linux".
Membership options at http://groups.google.com/group/rtc-linux .
Please read http://groups.google.com/group/rtc-linux/web/checklist
before submitting a driver.
--- 
You received this message because you are subscribed to the Google Groups "rtc-linux" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rtc-linux+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2016-02-23 23:04 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-02-09 10:57 [PATCH v2] RTC/PCF85063: fix reading/setting time/date Juergen Borleis
2016-02-09 10:57 ` [rtc-linux] " Juergen Borleis
2016-02-09 10:57 ` [PATCH v2 1/3] RTC/PCF85063: simplify code to read the current time Juergen Borleis
2016-02-09 10:57   ` [rtc-linux] " Juergen Borleis
2016-02-09 10:57 ` [PATCH v2 2/3] RTC/PCF85063: fix time/date reading Juergen Borleis
2016-02-09 10:57   ` [rtc-linux] " Juergen Borleis
2016-02-09 10:57 ` [PATCH v2 3/3] RTC/PCF85063: fix time/date setting Juergen Borleis
2016-02-09 10:57   ` [rtc-linux] " Juergen Borleis
2016-02-23 23:04 ` [PATCH v2] RTC/PCF85063: fix reading/setting time/date Alexandre Belloni
2016-02-23 23:04   ` [rtc-linux] " Alexandre Belloni

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.