diff options
author | Vincent Donnefort <vdonnefort@gmail.com> | 2014-08-08 17:20:18 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-08-08 18:57:20 -0400 |
commit | ede3e9d47cca565d96613bea9213eccf91c7a2a7 (patch) | |
tree | da81c72d3728bcd672085204db44ed49b046af48 /drivers/rtc | |
parent | 2784366c6797e6a3078abca2f303ebf7a36290be (diff) |
drivers/rtc/rtc-pcf8563.c: add alarm support
This patch adds alarm support for the NXP PCF8563 chip.
Signed-off-by: Vincent Donnefort <vdonnefort@gmail.com>
Cc: Simon Guinot <simon.guinot@sequanux.org>
Cc: Jason Cooper <jason@lakedaemon.net>
Cc: Andrew Lunn <andrew@lunn.ch>
Cc: Alessandro Zummo <a.zummo@towertech.it>
Cc: Dan Carpenter <dan.carpenter@oracle.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-pcf8563.c | 157 |
1 files changed, 153 insertions, 4 deletions
diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c index 7cbbf50cd895..5a197d9dc7e7 100644 --- a/drivers/rtc/rtc-pcf8563.c +++ b/drivers/rtc/rtc-pcf8563.c | |||
@@ -26,6 +26,8 @@ | |||
26 | 26 | ||
27 | #define PCF8563_REG_ST1 0x00 /* status */ | 27 | #define PCF8563_REG_ST1 0x00 /* status */ |
28 | #define PCF8563_REG_ST2 0x01 | 28 | #define PCF8563_REG_ST2 0x01 |
29 | #define PCF8563_BIT_AIE (1 << 1) | ||
30 | #define PCF8563_BIT_AF (1 << 3) | ||
29 | 31 | ||
30 | #define PCF8563_REG_SC 0x02 /* datetime */ | 32 | #define PCF8563_REG_SC 0x02 /* datetime */ |
31 | #define PCF8563_REG_MN 0x03 | 33 | #define PCF8563_REG_MN 0x03 |
@@ -36,9 +38,6 @@ | |||
36 | #define PCF8563_REG_YR 0x08 | 38 | #define PCF8563_REG_YR 0x08 |
37 | 39 | ||
38 | #define PCF8563_REG_AMN 0x09 /* alarm */ | 40 | #define PCF8563_REG_AMN 0x09 /* alarm */ |
39 | #define PCF8563_REG_AHR 0x0A | ||
40 | #define PCF8563_REG_ADM 0x0B | ||
41 | #define PCF8563_REG_ADW 0x0C | ||
42 | 41 | ||
43 | #define PCF8563_REG_CLKO 0x0D /* clock out */ | 42 | #define PCF8563_REG_CLKO 0x0D /* clock out */ |
44 | #define PCF8563_REG_TMRC 0x0E /* timer control */ | 43 | #define PCF8563_REG_TMRC 0x0E /* timer control */ |
@@ -67,6 +66,8 @@ struct pcf8563 { | |||
67 | */ | 66 | */ |
68 | int c_polarity; /* 0: MO_C=1 means 19xx, otherwise MO_C=1 means 20xx */ | 67 | int c_polarity; /* 0: MO_C=1 means 19xx, otherwise MO_C=1 means 20xx */ |
69 | int voltage_low; /* incicates if a low_voltage was detected */ | 68 | int voltage_low; /* incicates if a low_voltage was detected */ |
69 | |||
70 | struct i2c_client *client; | ||
70 | }; | 71 | }; |
71 | 72 | ||
72 | static int pcf8563_read_block_data(struct i2c_client *client, unsigned char reg, | 73 | static int pcf8563_read_block_data(struct i2c_client *client, unsigned char reg, |
@@ -115,6 +116,69 @@ static int pcf8563_write_block_data(struct i2c_client *client, | |||
115 | return 0; | 116 | return 0; |
116 | } | 117 | } |
117 | 118 | ||
119 | static int pcf8563_set_alarm_mode(struct i2c_client *client, bool on) | ||
120 | { | ||
121 | unsigned char buf[2]; | ||
122 | int err; | ||
123 | |||
124 | err = pcf8563_read_block_data(client, PCF8563_REG_ST2, 1, buf + 1); | ||
125 | if (err < 0) | ||
126 | return err; | ||
127 | |||
128 | if (on) | ||
129 | buf[1] |= PCF8563_BIT_AIE; | ||
130 | else | ||
131 | buf[1] &= ~PCF8563_BIT_AIE; | ||
132 | |||
133 | buf[1] &= ~PCF8563_BIT_AF; | ||
134 | buf[0] = PCF8563_REG_ST2; | ||
135 | |||
136 | err = pcf8563_write_block_data(client, PCF8563_REG_ST2, 1, buf + 1); | ||
137 | if (err < 0) { | ||
138 | dev_err(&client->dev, "%s: write error\n", __func__); | ||
139 | return -EIO; | ||
140 | } | ||
141 | |||
142 | return 0; | ||
143 | } | ||
144 | |||
145 | static int pcf8563_get_alarm_mode(struct i2c_client *client, unsigned char *en, | ||
146 | unsigned char *pen) | ||
147 | { | ||
148 | unsigned char buf; | ||
149 | int err; | ||
150 | |||
151 | err = pcf8563_read_block_data(client, PCF8563_REG_ST2, 1, &buf); | ||
152 | if (err) | ||
153 | return err; | ||
154 | |||
155 | if (en) | ||
156 | *en = !!(buf & PCF8563_BIT_AIE); | ||
157 | if (pen) | ||
158 | *pen = !!(buf & PCF8563_BIT_AF); | ||
159 | |||
160 | return 0; | ||
161 | } | ||
162 | |||
163 | static irqreturn_t pcf8563_irq(int irq, void *dev_id) | ||
164 | { | ||
165 | struct pcf8563 *pcf8563 = i2c_get_clientdata(dev_id); | ||
166 | int err; | ||
167 | char pending; | ||
168 | |||
169 | err = pcf8563_get_alarm_mode(pcf8563->client, NULL, &pending); | ||
170 | if (err < 0) | ||
171 | return err; | ||
172 | |||
173 | if (pending) { | ||
174 | rtc_update_irq(pcf8563->rtc, 1, RTC_IRQF | RTC_AF); | ||
175 | pcf8563_set_alarm_mode(pcf8563->client, 1); | ||
176 | return IRQ_HANDLED; | ||
177 | } | ||
178 | |||
179 | return IRQ_NONE; | ||
180 | } | ||
181 | |||
118 | /* | 182 | /* |
119 | * In the routines that deal directly with the pcf8563 hardware, we use | 183 | * In the routines that deal directly with the pcf8563 hardware, we use |
120 | * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch. | 184 | * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch. |
@@ -257,16 +321,83 @@ static int pcf8563_rtc_set_time(struct device *dev, struct rtc_time *tm) | |||
257 | return pcf8563_set_datetime(to_i2c_client(dev), tm); | 321 | return pcf8563_set_datetime(to_i2c_client(dev), tm); |
258 | } | 322 | } |
259 | 323 | ||
324 | static int pcf8563_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *tm) | ||
325 | { | ||
326 | struct i2c_client *client = to_i2c_client(dev); | ||
327 | unsigned char buf[4]; | ||
328 | int err; | ||
329 | |||
330 | err = pcf8563_read_block_data(client, PCF8563_REG_AMN, 4, buf); | ||
331 | if (err) | ||
332 | return err; | ||
333 | |||
334 | dev_dbg(&client->dev, | ||
335 | "%s: raw data is min=%02x, hr=%02x, mday=%02x, wday=%02x\n", | ||
336 | __func__, buf[0], buf[1], buf[2], buf[3]); | ||
337 | |||
338 | tm->time.tm_min = bcd2bin(buf[0] & 0x7F); | ||
339 | tm->time.tm_hour = bcd2bin(buf[1] & 0x7F); | ||
340 | tm->time.tm_mday = bcd2bin(buf[2] & 0x1F); | ||
341 | tm->time.tm_wday = bcd2bin(buf[3] & 0x7); | ||
342 | tm->time.tm_mon = -1; | ||
343 | tm->time.tm_year = -1; | ||
344 | tm->time.tm_yday = -1; | ||
345 | tm->time.tm_isdst = -1; | ||
346 | |||
347 | err = pcf8563_get_alarm_mode(client, &tm->enabled, &tm->pending); | ||
348 | if (err < 0) | ||
349 | return err; | ||
350 | |||
351 | dev_dbg(&client->dev, "%s: tm is mins=%d, hours=%d, mday=%d, wday=%d," | ||
352 | " enabled=%d, pending=%d\n", __func__, tm->time.tm_min, | ||
353 | tm->time.tm_hour, tm->time.tm_mday, tm->time.tm_wday, | ||
354 | tm->enabled, tm->pending); | ||
355 | |||
356 | return 0; | ||
357 | } | ||
358 | |||
359 | static int pcf8563_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *tm) | ||
360 | { | ||
361 | struct i2c_client *client = to_i2c_client(dev); | ||
362 | unsigned char buf[4]; | ||
363 | int err; | ||
364 | |||
365 | dev_dbg(dev, "%s, min=%d hour=%d wday=%d mday=%d " | ||
366 | "enabled=%d pending=%d\n", __func__, | ||
367 | tm->time.tm_min, tm->time.tm_hour, tm->time.tm_wday, | ||
368 | tm->time.tm_mday, tm->enabled, tm->pending); | ||
369 | |||
370 | buf[0] = bin2bcd(tm->time.tm_min); | ||
371 | buf[1] = bin2bcd(tm->time.tm_hour); | ||
372 | buf[2] = bin2bcd(tm->time.tm_mday); | ||
373 | buf[3] = tm->time.tm_wday & 0x07; | ||
374 | |||
375 | err = pcf8563_write_block_data(client, PCF8563_REG_AMN, 4, buf); | ||
376 | if (err) | ||
377 | return err; | ||
378 | |||
379 | return pcf8563_set_alarm_mode(client, 1); | ||
380 | } | ||
381 | |||
382 | static int pcf8563_irq_enable(struct device *dev, unsigned int enabled) | ||
383 | { | ||
384 | return pcf8563_set_alarm_mode(to_i2c_client(dev), !!enabled); | ||
385 | } | ||
386 | |||
260 | static const struct rtc_class_ops pcf8563_rtc_ops = { | 387 | static const struct rtc_class_ops pcf8563_rtc_ops = { |
261 | .ioctl = pcf8563_rtc_ioctl, | 388 | .ioctl = pcf8563_rtc_ioctl, |
262 | .read_time = pcf8563_rtc_read_time, | 389 | .read_time = pcf8563_rtc_read_time, |
263 | .set_time = pcf8563_rtc_set_time, | 390 | .set_time = pcf8563_rtc_set_time, |
391 | .read_alarm = pcf8563_rtc_read_alarm, | ||
392 | .set_alarm = pcf8563_rtc_set_alarm, | ||
393 | .alarm_irq_enable = pcf8563_irq_enable, | ||
264 | }; | 394 | }; |
265 | 395 | ||
266 | static int pcf8563_probe(struct i2c_client *client, | 396 | static int pcf8563_probe(struct i2c_client *client, |
267 | const struct i2c_device_id *id) | 397 | const struct i2c_device_id *id) |
268 | { | 398 | { |
269 | struct pcf8563 *pcf8563; | 399 | struct pcf8563 *pcf8563; |
400 | int err; | ||
270 | 401 | ||
271 | dev_dbg(&client->dev, "%s\n", __func__); | 402 | dev_dbg(&client->dev, "%s\n", __func__); |
272 | 403 | ||
@@ -281,12 +412,30 @@ static int pcf8563_probe(struct i2c_client *client, | |||
281 | dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n"); | 412 | dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n"); |
282 | 413 | ||
283 | i2c_set_clientdata(client, pcf8563); | 414 | i2c_set_clientdata(client, pcf8563); |
415 | pcf8563->client = client; | ||
416 | device_set_wakeup_capable(&client->dev, 1); | ||
284 | 417 | ||
285 | pcf8563->rtc = devm_rtc_device_register(&client->dev, | 418 | pcf8563->rtc = devm_rtc_device_register(&client->dev, |
286 | pcf8563_driver.driver.name, | 419 | pcf8563_driver.driver.name, |
287 | &pcf8563_rtc_ops, THIS_MODULE); | 420 | &pcf8563_rtc_ops, THIS_MODULE); |
288 | 421 | ||
289 | return PTR_ERR_OR_ZERO(pcf8563->rtc); | 422 | if (IS_ERR(pcf8563->rtc)) |
423 | return PTR_ERR(pcf8563->rtc); | ||
424 | |||
425 | if (client->irq > 0) { | ||
426 | err = devm_request_threaded_irq(&client->dev, client->irq, | ||
427 | NULL, pcf8563_irq, | ||
428 | IRQF_SHARED|IRQF_ONESHOT|IRQF_TRIGGER_FALLING, | ||
429 | pcf8563->rtc->name, client); | ||
430 | if (err) { | ||
431 | dev_err(&client->dev, "unable to request IRQ %d\n", | ||
432 | client->irq); | ||
433 | return err; | ||
434 | } | ||
435 | |||
436 | } | ||
437 | |||
438 | return 0; | ||
290 | } | 439 | } |
291 | 440 | ||
292 | static const struct i2c_device_id pcf8563_id[] = { | 441 | static const struct i2c_device_id pcf8563_id[] = { |