diff options
author | Adam Ward <adam.ward.opensource@diasemi.com> | 2015-04-16 15:45:26 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-04-17 09:04:00 -0400 |
commit | 03cc1625e1f2e9fc6910af4174c942d56292f556 (patch) | |
tree | cab9d331c2d2d16be6e360bf70d2a5255f51adf5 /drivers/rtc | |
parent | 52ef84d566dc145b4d469f06667c5fa3118fdd44 (diff) |
drivers/rtc/rtc-da9052.c: add extra reads with timeouts to avoid returning partially updated values
The RTC is in a different clock domain so a quick read after write
can retrieve a mangled value of the old/new values
Signed-off-by: Adam Ward <adam.ward.opensource@diasemi.com>
Tested-by: Adam Ward <adam.ward.opensource@diasemi.com>
Cc: Alessandro Zummo <a.zummo@towertech.it>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/rtc')
-rw-r--r-- | drivers/rtc/rtc-da9052.c | 87 |
1 files changed, 66 insertions, 21 deletions
diff --git a/drivers/rtc/rtc-da9052.c b/drivers/rtc/rtc-da9052.c index 613c43b7e9ae..ead02fad2de5 100644 --- a/drivers/rtc/rtc-da9052.c +++ b/drivers/rtc/rtc-da9052.c | |||
@@ -16,6 +16,7 @@ | |||
16 | #include <linux/platform_device.h> | 16 | #include <linux/platform_device.h> |
17 | #include <linux/rtc.h> | 17 | #include <linux/rtc.h> |
18 | #include <linux/err.h> | 18 | #include <linux/err.h> |
19 | #include <linux/delay.h> | ||
19 | 20 | ||
20 | #include <linux/mfd/da9052/da9052.h> | 21 | #include <linux/mfd/da9052/da9052.h> |
21 | #include <linux/mfd/da9052/reg.h> | 22 | #include <linux/mfd/da9052/reg.h> |
@@ -23,6 +24,8 @@ | |||
23 | #define rtc_err(rtc, fmt, ...) \ | 24 | #define rtc_err(rtc, fmt, ...) \ |
24 | dev_err(rtc->da9052->dev, "%s: " fmt, __func__, ##__VA_ARGS__) | 25 | dev_err(rtc->da9052->dev, "%s: " fmt, __func__, ##__VA_ARGS__) |
25 | 26 | ||
27 | #define DA9052_GET_TIME_RETRIES 5 | ||
28 | |||
26 | struct da9052_rtc { | 29 | struct da9052_rtc { |
27 | struct rtc_device *rtc; | 30 | struct rtc_device *rtc; |
28 | struct da9052 *da9052; | 31 | struct da9052 *da9052; |
@@ -58,22 +61,43 @@ static irqreturn_t da9052_rtc_irq(int irq, void *data) | |||
58 | static int da9052_read_alarm(struct da9052_rtc *rtc, struct rtc_time *rtc_tm) | 61 | static int da9052_read_alarm(struct da9052_rtc *rtc, struct rtc_time *rtc_tm) |
59 | { | 62 | { |
60 | int ret; | 63 | int ret; |
61 | uint8_t v[5]; | 64 | uint8_t v[2][5]; |
65 | int idx = 1; | ||
66 | int timeout = DA9052_GET_TIME_RETRIES; | ||
62 | 67 | ||
63 | ret = da9052_group_read(rtc->da9052, DA9052_ALARM_MI_REG, 5, v); | 68 | ret = da9052_group_read(rtc->da9052, DA9052_ALARM_MI_REG, 5, &v[0][0]); |
64 | if (ret != 0) { | 69 | if (ret) { |
65 | rtc_err(rtc, "Failed to group read ALM: %d\n", ret); | 70 | rtc_err(rtc, "Failed to group read ALM: %d\n", ret); |
66 | return ret; | 71 | return ret; |
67 | } | 72 | } |
68 | 73 | ||
69 | rtc_tm->tm_year = (v[4] & DA9052_RTC_YEAR) + 100; | 74 | do { |
70 | rtc_tm->tm_mon = (v[3] & DA9052_RTC_MONTH) - 1; | 75 | ret = da9052_group_read(rtc->da9052, |
71 | rtc_tm->tm_mday = v[2] & DA9052_RTC_DAY; | 76 | DA9052_ALARM_MI_REG, 5, &v[idx][0]); |
72 | rtc_tm->tm_hour = v[1] & DA9052_RTC_HOUR; | 77 | if (ret) { |
73 | rtc_tm->tm_min = v[0] & DA9052_RTC_MIN; | 78 | rtc_err(rtc, "Failed to group read ALM: %d\n", ret); |
79 | return ret; | ||
80 | } | ||
74 | 81 | ||
75 | ret = rtc_valid_tm(rtc_tm); | 82 | if (memcmp(&v[0][0], &v[1][0], 5) == 0) { |
76 | return ret; | 83 | rtc_tm->tm_year = (v[0][4] & DA9052_RTC_YEAR) + 100; |
84 | rtc_tm->tm_mon = (v[0][3] & DA9052_RTC_MONTH) - 1; | ||
85 | rtc_tm->tm_mday = v[0][2] & DA9052_RTC_DAY; | ||
86 | rtc_tm->tm_hour = v[0][1] & DA9052_RTC_HOUR; | ||
87 | rtc_tm->tm_min = v[0][0] & DA9052_RTC_MIN; | ||
88 | |||
89 | ret = rtc_valid_tm(rtc_tm); | ||
90 | return ret; | ||
91 | } | ||
92 | |||
93 | idx = (1-idx); | ||
94 | msleep(20); | ||
95 | |||
96 | } while (timeout--); | ||
97 | |||
98 | rtc_err(rtc, "Timed out reading alarm time\n"); | ||
99 | |||
100 | return -EIO; | ||
77 | } | 101 | } |
78 | 102 | ||
79 | static int da9052_set_alarm(struct da9052_rtc *rtc, struct rtc_time *rtc_tm) | 103 | static int da9052_set_alarm(struct da9052_rtc *rtc, struct rtc_time *rtc_tm) |
@@ -135,24 +159,45 @@ static int da9052_rtc_get_alarm_status(struct da9052_rtc *rtc) | |||
135 | static int da9052_rtc_read_time(struct device *dev, struct rtc_time *rtc_tm) | 159 | static int da9052_rtc_read_time(struct device *dev, struct rtc_time *rtc_tm) |
136 | { | 160 | { |
137 | struct da9052_rtc *rtc = dev_get_drvdata(dev); | 161 | struct da9052_rtc *rtc = dev_get_drvdata(dev); |
138 | uint8_t v[6]; | ||
139 | int ret; | 162 | int ret; |
163 | uint8_t v[2][6]; | ||
164 | int idx = 1; | ||
165 | int timeout = DA9052_GET_TIME_RETRIES; | ||
140 | 166 | ||
141 | ret = da9052_group_read(rtc->da9052, DA9052_COUNT_S_REG, 6, v); | 167 | ret = da9052_group_read(rtc->da9052, DA9052_COUNT_S_REG, 6, &v[0][0]); |
142 | if (ret < 0) { | 168 | if (ret) { |
143 | rtc_err(rtc, "Failed to read RTC time : %d\n", ret); | 169 | rtc_err(rtc, "Failed to read RTC time : %d\n", ret); |
144 | return ret; | 170 | return ret; |
145 | } | 171 | } |
146 | 172 | ||
147 | rtc_tm->tm_year = (v[5] & DA9052_RTC_YEAR) + 100; | 173 | do { |
148 | rtc_tm->tm_mon = (v[4] & DA9052_RTC_MONTH) - 1; | 174 | ret = da9052_group_read(rtc->da9052, |
149 | rtc_tm->tm_mday = v[3] & DA9052_RTC_DAY; | 175 | DA9052_COUNT_S_REG, 6, &v[idx][0]); |
150 | rtc_tm->tm_hour = v[2] & DA9052_RTC_HOUR; | 176 | if (ret) { |
151 | rtc_tm->tm_min = v[1] & DA9052_RTC_MIN; | 177 | rtc_err(rtc, "Failed to read RTC time : %d\n", ret); |
152 | rtc_tm->tm_sec = v[0] & DA9052_RTC_SEC; | 178 | return ret; |
179 | } | ||
153 | 180 | ||
154 | ret = rtc_valid_tm(rtc_tm); | 181 | if (memcmp(&v[0][0], &v[1][0], 6) == 0) { |
155 | return ret; | 182 | rtc_tm->tm_year = (v[0][5] & DA9052_RTC_YEAR) + 100; |
183 | rtc_tm->tm_mon = (v[0][4] & DA9052_RTC_MONTH) - 1; | ||
184 | rtc_tm->tm_mday = v[0][3] & DA9052_RTC_DAY; | ||
185 | rtc_tm->tm_hour = v[0][2] & DA9052_RTC_HOUR; | ||
186 | rtc_tm->tm_min = v[0][1] & DA9052_RTC_MIN; | ||
187 | rtc_tm->tm_sec = v[0][0] & DA9052_RTC_SEC; | ||
188 | |||
189 | ret = rtc_valid_tm(rtc_tm); | ||
190 | return ret; | ||
191 | } | ||
192 | |||
193 | idx = (1-idx); | ||
194 | msleep(20); | ||
195 | |||
196 | } while (timeout--); | ||
197 | |||
198 | rtc_err(rtc, "Timed out reading time\n"); | ||
199 | |||
200 | return -EIO; | ||
156 | } | 201 | } |
157 | 202 | ||
158 | static int da9052_rtc_set_time(struct device *dev, struct rtc_time *tm) | 203 | static int da9052_rtc_set_time(struct device *dev, struct rtc_time *tm) |