|
| 1 | +From 16292687fd316543bba3d0d0179c2439a85acb6a Mon Sep 17 00:00:00 2001 |
| 2 | +From: Max Marrone < [email protected]> |
| 3 | +Date: Mon, 8 Aug 2022 15:34:12 -0400 |
| 4 | +Subject: [PATCH] Work around RV-3028 problems via artificial delay |
| 5 | + |
| 6 | +This is an Opentrons workaround for what we suspect is an undocumented |
| 7 | +silicon erratum in the RV-3028 RTC. It adds an artificial sleep whenever |
| 8 | +something reads the current time from the RTC's registers. |
| 9 | + |
| 10 | +Whenever there is an open I2C transaction, the RV-3028 will hold its |
| 11 | +time registers steady, in order to avoid a torn read/write hazard. |
| 12 | +If the time registers would have ticked to a new value during that |
| 13 | +transaction, the RV-3028 is supposed to postpone that update and |
| 14 | +apply it as soon as the transaction ends. |
| 15 | + |
| 16 | +But on some boards, we're seeing the time registers never update at |
| 17 | +all whenever they're under rapid polling. `hwclock` does rapid |
| 18 | +polling under normal usage, so this causes it to error with |
| 19 | +"Timed out waiting for time change". |
| 20 | + |
| 21 | +We theorize that problematic RV-3028 chips need a big block of idle |
| 22 | +bus time in order to apply postponed time updates. |
| 23 | + |
| 24 | +See Opentrons Jira RSS-9 for investigation details. |
| 25 | + |
| 26 | +We sleep for 2-4 milliseconds. Shorter sleeps might also work; |
| 27 | +we just haven't tested with them. |
| 28 | +--- |
| 29 | + drivers/rtc/rtc-rv3028.c | 6 ++++++ |
| 30 | + 1 file changed, 6 insertions(+) |
| 31 | + |
| 32 | +diff --git a/drivers/rtc/rtc-rv3028.c b/drivers/rtc/rtc-rv3028.c |
| 33 | +index a714e5aeefb8..d499a2e5d956 100644 |
| 34 | +--- a/drivers/rtc/rtc-rv3028.c |
| 35 | ++++ b/drivers/rtc/rtc-rv3028.c |
| 36 | +@@ -10,6 +10,7 @@ |
| 37 | + |
| 38 | + #include <linux/bcd.h> |
| 39 | + #include <linux/bitops.h> |
| 40 | ++#include <linux/delay.h> |
| 41 | + #include <linux/i2c.h> |
| 42 | + #include <linux/interrupt.h> |
| 43 | + #include <linux/kernel.h> |
| 44 | +@@ -79,6 +80,9 @@ |
| 45 | + |
| 46 | + #define OFFSET_STEP_PPT 953674 |
| 47 | + |
| 48 | ++#define WORKAROUND_SLEEP_MIN_US 2000 |
| 49 | ++#define WORKAROUND_SLEEP_MAX_US 4000 |
| 50 | ++ |
| 51 | + enum rv3028_type { |
| 52 | + rv_3028, |
| 53 | + }; |
| 54 | +@@ -227,6 +231,8 @@ static int rv3028_get_time(struct device *dev, struct rtc_time *tm) |
| 55 | + return -EINVAL; |
| 56 | + } |
| 57 | + |
| 58 | ++ usleep_range(WORKAROUND_SLEEP_MIN_US, WORKAROUND_SLEEP_MAX_US); |
| 59 | ++ |
| 60 | + ret = regmap_bulk_read(rv3028->regmap, RV3028_SEC, date, sizeof(date)); |
| 61 | + if (ret) |
| 62 | + return ret; |
| 63 | +-- |
| 64 | +2.37.1 |
| 65 | + |
0 commit comments