diff options
author | Michael Langer <michael.brainbug.langer@googlemail.com> | 2012-10-04 20:14:37 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-10-05 14:05:08 -0400 |
commit | 542dd33a4925757c93b2c811b19434822a6c1a73 (patch) | |
tree | 6a98832296a192e5c505eb58104a40d052118ebf /drivers/rtc | |
parent | 48e9766726ebb8f5d98823fe6b32dff570bc04d8 (diff) |
drivers/rtc/rtc-s35390a.c: add wakealarm support for rtc-s35390A rtc chip
Add basic get/set alarm support for the Seiko Instruments S-35390A. The
chip is used on the QNAP TS-219P+ NAS device.
Signed-off-by: Michael Langer <michael.brainbug.langer@googlemail.com>
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-s35390a.c | 116 |
1 files changed, 116 insertions, 0 deletions
diff --git a/drivers/rtc/rtc-s35390a.c b/drivers/rtc/rtc-s35390a.c index cc5c5166b018..8a092325188d 100644 --- a/drivers/rtc/rtc-s35390a.c +++ b/drivers/rtc/rtc-s35390a.c | |||
@@ -19,6 +19,8 @@ | |||
19 | #define S35390A_CMD_STATUS1 0 | 19 | #define S35390A_CMD_STATUS1 0 |
20 | #define S35390A_CMD_STATUS2 1 | 20 | #define S35390A_CMD_STATUS2 1 |
21 | #define S35390A_CMD_TIME1 2 | 21 | #define S35390A_CMD_TIME1 2 |
22 | #define S35390A_CMD_TIME2 3 | ||
23 | #define S35390A_CMD_INT2_REG1 5 | ||
22 | 24 | ||
23 | #define S35390A_BYTE_YEAR 0 | 25 | #define S35390A_BYTE_YEAR 0 |
24 | #define S35390A_BYTE_MONTH 1 | 26 | #define S35390A_BYTE_MONTH 1 |
@@ -28,12 +30,23 @@ | |||
28 | #define S35390A_BYTE_MINS 5 | 30 | #define S35390A_BYTE_MINS 5 |
29 | #define S35390A_BYTE_SECS 6 | 31 | #define S35390A_BYTE_SECS 6 |
30 | 32 | ||
33 | #define S35390A_ALRM_BYTE_WDAY 0 | ||
34 | #define S35390A_ALRM_BYTE_HOURS 1 | ||
35 | #define S35390A_ALRM_BYTE_MINS 2 | ||
36 | |||
31 | #define S35390A_FLAG_POC 0x01 | 37 | #define S35390A_FLAG_POC 0x01 |
32 | #define S35390A_FLAG_BLD 0x02 | 38 | #define S35390A_FLAG_BLD 0x02 |
33 | #define S35390A_FLAG_24H 0x40 | 39 | #define S35390A_FLAG_24H 0x40 |
34 | #define S35390A_FLAG_RESET 0x80 | 40 | #define S35390A_FLAG_RESET 0x80 |
35 | #define S35390A_FLAG_TEST 0x01 | 41 | #define S35390A_FLAG_TEST 0x01 |
36 | 42 | ||
43 | #define S35390A_INT2_MODE_MASK 0xF0 | ||
44 | |||
45 | #define S35390A_INT2_MODE_NOINTR 0x00 | ||
46 | #define S35390A_INT2_MODE_FREQ 0x10 | ||
47 | #define S35390A_INT2_MODE_ALARM 0x40 | ||
48 | #define S35390A_INT2_MODE_PMIN_EDG 0x20 | ||
49 | |||
37 | static const struct i2c_device_id s35390a_id[] = { | 50 | static const struct i2c_device_id s35390a_id[] = { |
38 | { "s35390a", 0 }, | 51 | { "s35390a", 0 }, |
39 | { } | 52 | { } |
@@ -193,6 +206,104 @@ static int s35390a_get_datetime(struct i2c_client *client, struct rtc_time *tm) | |||
193 | return rtc_valid_tm(tm); | 206 | return rtc_valid_tm(tm); |
194 | } | 207 | } |
195 | 208 | ||
209 | static int s35390a_set_alarm(struct i2c_client *client, struct rtc_wkalrm *alm) | ||
210 | { | ||
211 | struct s35390a *s35390a = i2c_get_clientdata(client); | ||
212 | char buf[3], sts = 0; | ||
213 | int err, i; | ||
214 | |||
215 | dev_dbg(&client->dev, "%s: alm is secs=%d, mins=%d, hours=%d mday=%d, "\ | ||
216 | "mon=%d, year=%d, wday=%d\n", __func__, alm->time.tm_sec, | ||
217 | alm->time.tm_min, alm->time.tm_hour, alm->time.tm_mday, | ||
218 | alm->time.tm_mon, alm->time.tm_year, alm->time.tm_wday); | ||
219 | |||
220 | /* disable interrupt */ | ||
221 | err = s35390a_set_reg(s35390a, S35390A_CMD_STATUS2, &sts, sizeof(sts)); | ||
222 | if (err < 0) | ||
223 | return err; | ||
224 | |||
225 | /* clear pending interrupt, if any */ | ||
226 | err = s35390a_get_reg(s35390a, S35390A_CMD_STATUS1, &sts, sizeof(sts)); | ||
227 | if (err < 0) | ||
228 | return err; | ||
229 | |||
230 | if (alm->enabled) | ||
231 | sts = S35390A_INT2_MODE_ALARM; | ||
232 | else | ||
233 | sts = S35390A_INT2_MODE_NOINTR; | ||
234 | |||
235 | /* This chip expects the bits of each byte to be in reverse order */ | ||
236 | sts = bitrev8(sts); | ||
237 | |||
238 | /* set interupt mode*/ | ||
239 | err = s35390a_set_reg(s35390a, S35390A_CMD_STATUS2, &sts, sizeof(sts)); | ||
240 | if (err < 0) | ||
241 | return err; | ||
242 | |||
243 | if (alm->time.tm_wday != -1) | ||
244 | buf[S35390A_ALRM_BYTE_WDAY] = bin2bcd(alm->time.tm_wday) | 0x80; | ||
245 | |||
246 | buf[S35390A_ALRM_BYTE_HOURS] = s35390a_hr2reg(s35390a, | ||
247 | alm->time.tm_hour) | 0x80; | ||
248 | buf[S35390A_ALRM_BYTE_MINS] = bin2bcd(alm->time.tm_min) | 0x80; | ||
249 | |||
250 | if (alm->time.tm_hour >= 12) | ||
251 | buf[S35390A_ALRM_BYTE_HOURS] |= 0x40; | ||
252 | |||
253 | for (i = 0; i < 3; ++i) | ||
254 | buf[i] = bitrev8(buf[i]); | ||
255 | |||
256 | err = s35390a_set_reg(s35390a, S35390A_CMD_INT2_REG1, buf, | ||
257 | sizeof(buf)); | ||
258 | |||
259 | return err; | ||
260 | } | ||
261 | |||
262 | static int s35390a_read_alarm(struct i2c_client *client, struct rtc_wkalrm *alm) | ||
263 | { | ||
264 | struct s35390a *s35390a = i2c_get_clientdata(client); | ||
265 | char buf[3], sts; | ||
266 | int i, err; | ||
267 | |||
268 | err = s35390a_get_reg(s35390a, S35390A_CMD_STATUS2, &sts, sizeof(sts)); | ||
269 | if (err < 0) | ||
270 | return err; | ||
271 | |||
272 | if (bitrev8(sts) != S35390A_INT2_MODE_ALARM) | ||
273 | return -EINVAL; | ||
274 | |||
275 | err = s35390a_get_reg(s35390a, S35390A_CMD_INT2_REG1, buf, sizeof(buf)); | ||
276 | if (err < 0) | ||
277 | return err; | ||
278 | |||
279 | /* This chip returns the bits of each byte in reverse order */ | ||
280 | for (i = 0; i < 3; ++i) { | ||
281 | buf[i] = bitrev8(buf[i]); | ||
282 | buf[i] &= ~0x80; | ||
283 | } | ||
284 | |||
285 | alm->time.tm_wday = bcd2bin(buf[S35390A_ALRM_BYTE_WDAY]); | ||
286 | alm->time.tm_hour = s35390a_reg2hr(s35390a, | ||
287 | buf[S35390A_ALRM_BYTE_HOURS]); | ||
288 | alm->time.tm_min = bcd2bin(buf[S35390A_ALRM_BYTE_MINS]); | ||
289 | |||
290 | dev_dbg(&client->dev, "%s: alm is mins=%d, hours=%d, wday=%d\n", | ||
291 | __func__, alm->time.tm_min, alm->time.tm_hour, | ||
292 | alm->time.tm_wday); | ||
293 | |||
294 | return 0; | ||
295 | } | ||
296 | |||
297 | static int s35390a_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) | ||
298 | { | ||
299 | return s35390a_read_alarm(to_i2c_client(dev), alm); | ||
300 | } | ||
301 | |||
302 | static int s35390a_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) | ||
303 | { | ||
304 | return s35390a_set_alarm(to_i2c_client(dev), alm); | ||
305 | } | ||
306 | |||
196 | static int s35390a_rtc_read_time(struct device *dev, struct rtc_time *tm) | 307 | static int s35390a_rtc_read_time(struct device *dev, struct rtc_time *tm) |
197 | { | 308 | { |
198 | return s35390a_get_datetime(to_i2c_client(dev), tm); | 309 | return s35390a_get_datetime(to_i2c_client(dev), tm); |
@@ -206,6 +317,9 @@ static int s35390a_rtc_set_time(struct device *dev, struct rtc_time *tm) | |||
206 | static const struct rtc_class_ops s35390a_rtc_ops = { | 317 | static const struct rtc_class_ops s35390a_rtc_ops = { |
207 | .read_time = s35390a_rtc_read_time, | 318 | .read_time = s35390a_rtc_read_time, |
208 | .set_time = s35390a_rtc_set_time, | 319 | .set_time = s35390a_rtc_set_time, |
320 | .set_alarm = s35390a_rtc_set_alarm, | ||
321 | .read_alarm = s35390a_rtc_read_alarm, | ||
322 | |||
209 | }; | 323 | }; |
210 | 324 | ||
211 | static struct i2c_driver s35390a_driver; | 325 | static struct i2c_driver s35390a_driver; |
@@ -270,6 +384,8 @@ static int s35390a_probe(struct i2c_client *client, | |||
270 | if (s35390a_get_datetime(client, &tm) < 0) | 384 | if (s35390a_get_datetime(client, &tm) < 0) |
271 | dev_warn(&client->dev, "clock needs to be set\n"); | 385 | dev_warn(&client->dev, "clock needs to be set\n"); |
272 | 386 | ||
387 | device_set_wakeup_capable(&client->dev, 1); | ||
388 | |||
273 | s35390a->rtc = rtc_device_register(s35390a_driver.driver.name, | 389 | s35390a->rtc = rtc_device_register(s35390a_driver.driver.name, |
274 | &client->dev, &s35390a_rtc_ops, THIS_MODULE); | 390 | &client->dev, &s35390a_rtc_ops, THIS_MODULE); |
275 | 391 | ||