diff options
author | Raghavendra Ganiga <ravi23ganiga@gmail.com> | 2014-06-06 17:35:59 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-06-06 19:08:08 -0400 |
commit | 1d6316f56d04a10f39621088f3b672a3c5528b80 (patch) | |
tree | 8f47af0ca2014982dd349161381484ac41e702b4 /drivers/rtc | |
parent | 7abe3f5404fae84cecd851e140a2218ba9d24e4f (diff) |
drivers/rtc: add support for maxim dallas rtc ds1343 and ds1344
Signed-off-by: Raghavendra Chandra Ganiga <ravi23ganiga@gmail.com>
Cc: Alessandro Zummo <a.zummo@towertech.it>
Cc: Jingoo Han <jg1.han@samsung.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/Kconfig | 10 | ||||
-rw-r--r-- | drivers/rtc/Makefile | 1 | ||||
-rw-r--r-- | drivers/rtc/rtc-ds1343.c | 679 |
3 files changed, 690 insertions, 0 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index f4e113874e7a..a2f5b31f40d3 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig | |||
@@ -573,6 +573,16 @@ config RTC_DRV_DS1305 | |||
573 | This driver can also be built as a module. If so, the module | 573 | This driver can also be built as a module. If so, the module |
574 | will be called rtc-ds1305. | 574 | will be called rtc-ds1305. |
575 | 575 | ||
576 | config RTC_DRV_DS1343 | ||
577 | tristate "Dallas/Maxim DS1343/DS1344" | ||
578 | help | ||
579 | If you say yes here you get support for the | ||
580 | Dallas/Maxim DS1343 and DS1344 real time clock chips. | ||
581 | Support for trickle charger, alarm is provided. | ||
582 | |||
583 | This driver can also be built as a module. If so, the module | ||
584 | will be called rtc-ds1343. | ||
585 | |||
576 | config RTC_DRV_DS1347 | 586 | config RTC_DRV_DS1347 |
577 | tristate "Dallas/Maxim DS1347" | 587 | tristate "Dallas/Maxim DS1347" |
578 | help | 588 | help |
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 4545b4a88f30..660ab296b956 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile | |||
@@ -40,6 +40,7 @@ obj-$(CONFIG_RTC_DRV_DS1286) += rtc-ds1286.o | |||
40 | obj-$(CONFIG_RTC_DRV_DS1302) += rtc-ds1302.o | 40 | obj-$(CONFIG_RTC_DRV_DS1302) += rtc-ds1302.o |
41 | obj-$(CONFIG_RTC_DRV_DS1305) += rtc-ds1305.o | 41 | obj-$(CONFIG_RTC_DRV_DS1305) += rtc-ds1305.o |
42 | obj-$(CONFIG_RTC_DRV_DS1307) += rtc-ds1307.o | 42 | obj-$(CONFIG_RTC_DRV_DS1307) += rtc-ds1307.o |
43 | obj-$(CONFIG_RTC_DRV_DS1343) += rtc-ds1343.o | ||
43 | obj-$(CONFIG_RTC_DRV_DS1347) += rtc-ds1347.o | 44 | obj-$(CONFIG_RTC_DRV_DS1347) += rtc-ds1347.o |
44 | obj-$(CONFIG_RTC_DRV_DS1374) += rtc-ds1374.o | 45 | obj-$(CONFIG_RTC_DRV_DS1374) += rtc-ds1374.o |
45 | obj-$(CONFIG_RTC_DRV_DS1390) += rtc-ds1390.o | 46 | obj-$(CONFIG_RTC_DRV_DS1390) += rtc-ds1390.o |
diff --git a/drivers/rtc/rtc-ds1343.c b/drivers/rtc/rtc-ds1343.c new file mode 100644 index 000000000000..8ccc75021175 --- /dev/null +++ b/drivers/rtc/rtc-ds1343.c | |||
@@ -0,0 +1,679 @@ | |||
1 | /* rtc-ds1343.c | ||
2 | * | ||
3 | * Driver for Dallas Semiconductor DS1343 Low Current, SPI Compatible | ||
4 | * Real Time Clock | ||
5 | * | ||
6 | * Author : Raghavendra Chandra Ganiga <ravi23ganiga@gmail.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | * | ||
12 | */ | ||
13 | |||
14 | #include <linux/init.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/interrupt.h> | ||
17 | #include <linux/device.h> | ||
18 | #include <linux/spi/spi.h> | ||
19 | #include <linux/regmap.h> | ||
20 | #include <linux/rtc.h> | ||
21 | #include <linux/bcd.h> | ||
22 | #include <linux/pm.h> | ||
23 | #include <linux/slab.h> | ||
24 | |||
25 | #define DS1343_DRV_VERSION "01.00" | ||
26 | #define DALLAS_MAXIM_DS1343 0 | ||
27 | #define DALLAS_MAXIM_DS1344 1 | ||
28 | |||
29 | /* RTC DS1343 Registers */ | ||
30 | #define DS1343_SECONDS_REG 0x00 | ||
31 | #define DS1343_MINUTES_REG 0x01 | ||
32 | #define DS1343_HOURS_REG 0x02 | ||
33 | #define DS1343_DAY_REG 0x03 | ||
34 | #define DS1343_DATE_REG 0x04 | ||
35 | #define DS1343_MONTH_REG 0x05 | ||
36 | #define DS1343_YEAR_REG 0x06 | ||
37 | #define DS1343_ALM0_SEC_REG 0x07 | ||
38 | #define DS1343_ALM0_MIN_REG 0x08 | ||
39 | #define DS1343_ALM0_HOUR_REG 0x09 | ||
40 | #define DS1343_ALM0_DAY_REG 0x0A | ||
41 | #define DS1343_ALM1_SEC_REG 0x0B | ||
42 | #define DS1343_ALM1_MIN_REG 0x0C | ||
43 | #define DS1343_ALM1_HOUR_REG 0x0D | ||
44 | #define DS1343_ALM1_DAY_REG 0x0E | ||
45 | #define DS1343_CONTROL_REG 0x0F | ||
46 | #define DS1343_STATUS_REG 0x10 | ||
47 | #define DS1343_TRICKLE_REG 0x11 | ||
48 | |||
49 | /* DS1343 Control Registers bits */ | ||
50 | #define DS1343_EOSC 0x80 | ||
51 | #define DS1343_DOSF 0x20 | ||
52 | #define DS1343_EGFIL 0x10 | ||
53 | #define DS1343_SQW 0x08 | ||
54 | #define DS1343_INTCN 0x04 | ||
55 | #define DS1343_A1IE 0x02 | ||
56 | #define DS1343_A0IE 0x01 | ||
57 | |||
58 | /* DS1343 Status Registers bits */ | ||
59 | #define DS1343_OSF 0x80 | ||
60 | #define DS1343_IRQF1 0x02 | ||
61 | #define DS1343_IRQF0 0x01 | ||
62 | |||
63 | /* DS1343 Trickle Charger Registers bits */ | ||
64 | #define DS1343_TRICKLE_MAGIC 0xa0 | ||
65 | #define DS1343_TRICKLE_DS1 0x08 | ||
66 | #define DS1343_TRICKLE_1K 0x01 | ||
67 | #define DS1343_TRICKLE_2K 0x02 | ||
68 | #define DS1343_TRICKLE_4K 0x03 | ||
69 | |||
70 | static const struct spi_device_id ds1343_id[] = { | ||
71 | { "ds1343", DALLAS_MAXIM_DS1343 }, | ||
72 | { "ds1344", DALLAS_MAXIM_DS1344 }, | ||
73 | { } | ||
74 | }; | ||
75 | MODULE_DEVICE_TABLE(spi, ds1343_id); | ||
76 | |||
77 | struct ds1343_priv { | ||
78 | struct spi_device *spi; | ||
79 | struct rtc_device *rtc; | ||
80 | struct regmap *map; | ||
81 | struct mutex mutex; | ||
82 | unsigned int irqen; | ||
83 | int alarm_sec; | ||
84 | int alarm_min; | ||
85 | int alarm_hour; | ||
86 | int alarm_mday; | ||
87 | }; | ||
88 | |||
89 | static int ds1343_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) | ||
90 | { | ||
91 | switch (cmd) { | ||
92 | #ifdef RTC_SET_CHARGE | ||
93 | case RTC_SET_CHARGE: | ||
94 | { | ||
95 | int val; | ||
96 | |||
97 | if (copy_from_user(&val, (int __user *)arg, sizeof(int))) | ||
98 | return -EFAULT; | ||
99 | |||
100 | return regmap_write(priv->map, DS1343_TRICKLE_REG, val); | ||
101 | } | ||
102 | break; | ||
103 | #endif | ||
104 | } | ||
105 | |||
106 | return -ENOIOCTLCMD; | ||
107 | } | ||
108 | |||
109 | static ssize_t ds1343_show_glitchfilter(struct device *dev, | ||
110 | struct device_attribute *attr, char *buf) | ||
111 | { | ||
112 | struct ds1343_priv *priv = dev_get_drvdata(dev); | ||
113 | int glitch_filt_status, data; | ||
114 | |||
115 | regmap_read(priv->map, DS1343_CONTROL_REG, &data); | ||
116 | |||
117 | glitch_filt_status = !!(data & DS1343_EGFIL); | ||
118 | |||
119 | if (glitch_filt_status) | ||
120 | return sprintf(buf, "enabled\n"); | ||
121 | else | ||
122 | return sprintf(buf, "disabled\n"); | ||
123 | } | ||
124 | |||
125 | static ssize_t ds1343_store_glitchfilter(struct device *dev, | ||
126 | struct device_attribute *attr, | ||
127 | const char *buf, size_t count) | ||
128 | { | ||
129 | struct ds1343_priv *priv = dev_get_drvdata(dev); | ||
130 | int data; | ||
131 | |||
132 | regmap_read(priv->map, DS1343_CONTROL_REG, &data); | ||
133 | |||
134 | if (strncmp(buf, "enabled", 7) == 0) | ||
135 | data |= DS1343_EGFIL; | ||
136 | |||
137 | else if (strncmp(buf, "disabled", 8) == 0) | ||
138 | data &= ~(DS1343_EGFIL); | ||
139 | |||
140 | else | ||
141 | return -EINVAL; | ||
142 | |||
143 | regmap_write(priv->map, DS1343_CONTROL_REG, data); | ||
144 | |||
145 | return count; | ||
146 | } | ||
147 | |||
148 | static DEVICE_ATTR(glitch_filter, S_IRUGO | S_IWUSR, ds1343_show_glitchfilter, | ||
149 | ds1343_store_glitchfilter); | ||
150 | |||
151 | static ssize_t ds1343_show_alarmstatus(struct device *dev, | ||
152 | struct device_attribute *attr, char *buf) | ||
153 | { | ||
154 | struct ds1343_priv *priv = dev_get_drvdata(dev); | ||
155 | int alarmstatus, data; | ||
156 | |||
157 | regmap_read(priv->map, DS1343_CONTROL_REG, &data); | ||
158 | |||
159 | alarmstatus = !!(data & DS1343_A0IE); | ||
160 | |||
161 | if (alarmstatus) | ||
162 | return sprintf(buf, "enabled\n"); | ||
163 | else | ||
164 | return sprintf(buf, "disabled\n"); | ||
165 | } | ||
166 | |||
167 | static DEVICE_ATTR(alarm_status, S_IRUGO, ds1343_show_alarmstatus, NULL); | ||
168 | |||
169 | static ssize_t ds1343_show_alarmmode(struct device *dev, | ||
170 | struct device_attribute *attr, char *buf) | ||
171 | { | ||
172 | struct ds1343_priv *priv = dev_get_drvdata(dev); | ||
173 | int alarm_mode, data; | ||
174 | char *alarm_str; | ||
175 | |||
176 | regmap_read(priv->map, DS1343_ALM0_SEC_REG, &data); | ||
177 | alarm_mode = (data & 0x80) >> 4; | ||
178 | |||
179 | regmap_read(priv->map, DS1343_ALM0_MIN_REG, &data); | ||
180 | alarm_mode |= (data & 0x80) >> 5; | ||
181 | |||
182 | regmap_read(priv->map, DS1343_ALM0_HOUR_REG, &data); | ||
183 | alarm_mode |= (data & 0x80) >> 6; | ||
184 | |||
185 | regmap_read(priv->map, DS1343_ALM0_DAY_REG, &data); | ||
186 | alarm_mode |= (data & 0x80) >> 7; | ||
187 | |||
188 | switch (alarm_mode) { | ||
189 | case 15: | ||
190 | alarm_str = "each second"; | ||
191 | break; | ||
192 | |||
193 | case 7: | ||
194 | alarm_str = "seconds match"; | ||
195 | break; | ||
196 | |||
197 | case 3: | ||
198 | alarm_str = "minutes and seconds match"; | ||
199 | break; | ||
200 | |||
201 | case 1: | ||
202 | alarm_str = "hours, minutes and seconds match"; | ||
203 | break; | ||
204 | |||
205 | case 0: | ||
206 | alarm_str = "day, hours, minutes and seconds match"; | ||
207 | break; | ||
208 | |||
209 | default: | ||
210 | alarm_str = "invalid"; | ||
211 | break; | ||
212 | } | ||
213 | |||
214 | return sprintf(buf, "%s\n", alarm_str); | ||
215 | } | ||
216 | |||
217 | static DEVICE_ATTR(alarm_mode, S_IRUGO, ds1343_show_alarmmode, NULL); | ||
218 | |||
219 | static ssize_t ds1343_show_tricklecharger(struct device *dev, | ||
220 | struct device_attribute *attr, char *buf) | ||
221 | { | ||
222 | struct ds1343_priv *priv = dev_get_drvdata(dev); | ||
223 | int data; | ||
224 | char *diodes = "disabled", *resistors = " "; | ||
225 | |||
226 | regmap_read(priv->map, DS1343_TRICKLE_REG, &data); | ||
227 | |||
228 | if ((data & 0xf0) == DS1343_TRICKLE_MAGIC) { | ||
229 | switch (data & 0x0c) { | ||
230 | case DS1343_TRICKLE_DS1: | ||
231 | diodes = "one diode,"; | ||
232 | break; | ||
233 | |||
234 | default: | ||
235 | diodes = "no diode,"; | ||
236 | break; | ||
237 | } | ||
238 | |||
239 | switch (data & 0x03) { | ||
240 | case DS1343_TRICKLE_1K: | ||
241 | resistors = "1k Ohm"; | ||
242 | break; | ||
243 | |||
244 | case DS1343_TRICKLE_2K: | ||
245 | resistors = "2k Ohm"; | ||
246 | break; | ||
247 | |||
248 | case DS1343_TRICKLE_4K: | ||
249 | resistors = "4k Ohm"; | ||
250 | break; | ||
251 | |||
252 | default: | ||
253 | diodes = "disabled"; | ||
254 | break; | ||
255 | } | ||
256 | } | ||
257 | |||
258 | return sprintf(buf, "%s %s\n", diodes, resistors); | ||
259 | } | ||
260 | |||
261 | static DEVICE_ATTR(trickle_charger, S_IRUGO, ds1343_show_tricklecharger, NULL); | ||
262 | |||
263 | static int ds1343_sysfs_register(struct device *dev) | ||
264 | { | ||
265 | int err; | ||
266 | |||
267 | err = device_create_file(dev, &dev_attr_glitch_filter); | ||
268 | if (err) | ||
269 | return err; | ||
270 | |||
271 | err = device_create_file(dev, &dev_attr_alarm_status); | ||
272 | if (err) | ||
273 | goto error1; | ||
274 | |||
275 | err = device_create_file(dev, &dev_attr_alarm_mode); | ||
276 | if (err) | ||
277 | goto error2; | ||
278 | |||
279 | err = device_create_file(dev, &dev_attr_trickle_charger); | ||
280 | if (!err) | ||
281 | return err; | ||
282 | |||
283 | device_remove_file(dev, &dev_attr_alarm_mode); | ||
284 | |||
285 | error2: | ||
286 | device_remove_file(dev, &dev_attr_alarm_status); | ||
287 | |||
288 | error1: | ||
289 | device_remove_file(dev, &dev_attr_glitch_filter); | ||
290 | |||
291 | return err; | ||
292 | } | ||
293 | |||
294 | static void ds1343_sysfs_unregister(struct device *dev) | ||
295 | { | ||
296 | device_remove_file(dev, &dev_attr_glitch_filter); | ||
297 | device_remove_file(dev, &dev_attr_alarm_status); | ||
298 | device_remove_file(dev, &dev_attr_alarm_mode); | ||
299 | device_remove_file(dev, &dev_attr_trickle_charger); | ||
300 | } | ||
301 | |||
302 | static int ds1343_read_time(struct device *dev, struct rtc_time *dt) | ||
303 | { | ||
304 | struct ds1343_priv *priv = dev_get_drvdata(dev); | ||
305 | unsigned char buf[7]; | ||
306 | int res; | ||
307 | |||
308 | res = regmap_bulk_read(priv->map, DS1343_SECONDS_REG, buf, 7); | ||
309 | if (res) | ||
310 | return res; | ||
311 | |||
312 | dt->tm_sec = bcd2bin(buf[0]); | ||
313 | dt->tm_min = bcd2bin(buf[1]); | ||
314 | dt->tm_hour = bcd2bin(buf[2] & 0x3F); | ||
315 | dt->tm_wday = bcd2bin(buf[3]) - 1; | ||
316 | dt->tm_mday = bcd2bin(buf[4]); | ||
317 | dt->tm_mon = bcd2bin(buf[5] & 0x1F) - 1; | ||
318 | dt->tm_year = bcd2bin(buf[6]) + 100; /* year offset from 1900 */ | ||
319 | |||
320 | return rtc_valid_tm(dt); | ||
321 | } | ||
322 | |||
323 | static int ds1343_set_time(struct device *dev, struct rtc_time *dt) | ||
324 | { | ||
325 | struct ds1343_priv *priv = dev_get_drvdata(dev); | ||
326 | int res; | ||
327 | |||
328 | res = regmap_write(priv->map, DS1343_SECONDS_REG, | ||
329 | bin2bcd(dt->tm_sec)); | ||
330 | if (res) | ||
331 | return res; | ||
332 | |||
333 | res = regmap_write(priv->map, DS1343_MINUTES_REG, | ||
334 | bin2bcd(dt->tm_min)); | ||
335 | if (res) | ||
336 | return res; | ||
337 | |||
338 | res = regmap_write(priv->map, DS1343_HOURS_REG, | ||
339 | bin2bcd(dt->tm_hour) & 0x3F); | ||
340 | if (res) | ||
341 | return res; | ||
342 | |||
343 | res = regmap_write(priv->map, DS1343_DAY_REG, | ||
344 | bin2bcd(dt->tm_wday + 1)); | ||
345 | if (res) | ||
346 | return res; | ||
347 | |||
348 | res = regmap_write(priv->map, DS1343_DATE_REG, | ||
349 | bin2bcd(dt->tm_mday)); | ||
350 | if (res) | ||
351 | return res; | ||
352 | |||
353 | res = regmap_write(priv->map, DS1343_MONTH_REG, | ||
354 | bin2bcd(dt->tm_mon + 1)); | ||
355 | if (res) | ||
356 | return res; | ||
357 | |||
358 | dt->tm_year %= 100; | ||
359 | |||
360 | res = regmap_write(priv->map, DS1343_YEAR_REG, | ||
361 | bin2bcd(dt->tm_year)); | ||
362 | if (res) | ||
363 | return res; | ||
364 | |||
365 | return 0; | ||
366 | } | ||
367 | |||
368 | static int ds1343_update_alarm(struct device *dev) | ||
369 | { | ||
370 | struct ds1343_priv *priv = dev_get_drvdata(dev); | ||
371 | unsigned int control, stat; | ||
372 | unsigned char buf[4]; | ||
373 | int res = 0; | ||
374 | |||
375 | res = regmap_read(priv->map, DS1343_CONTROL_REG, &control); | ||
376 | if (res) | ||
377 | return res; | ||
378 | |||
379 | res = regmap_read(priv->map, DS1343_STATUS_REG, &stat); | ||
380 | if (res) | ||
381 | return res; | ||
382 | |||
383 | control &= ~(DS1343_A0IE); | ||
384 | stat &= ~(DS1343_IRQF0); | ||
385 | |||
386 | res = regmap_write(priv->map, DS1343_CONTROL_REG, control); | ||
387 | if (res) | ||
388 | return res; | ||
389 | |||
390 | res = regmap_write(priv->map, DS1343_STATUS_REG, stat); | ||
391 | if (res) | ||
392 | return res; | ||
393 | |||
394 | buf[0] = priv->alarm_sec < 0 || (priv->irqen & RTC_UF) ? | ||
395 | 0x80 : bin2bcd(priv->alarm_sec) & 0x7F; | ||
396 | buf[1] = priv->alarm_min < 0 || (priv->irqen & RTC_UF) ? | ||
397 | 0x80 : bin2bcd(priv->alarm_min) & 0x7F; | ||
398 | buf[2] = priv->alarm_hour < 0 || (priv->irqen & RTC_UF) ? | ||
399 | 0x80 : bin2bcd(priv->alarm_hour) & 0x3F; | ||
400 | buf[3] = priv->alarm_mday < 0 || (priv->irqen & RTC_UF) ? | ||
401 | 0x80 : bin2bcd(priv->alarm_mday) & 0x7F; | ||
402 | |||
403 | res = regmap_bulk_write(priv->map, DS1343_ALM0_SEC_REG, buf, 4); | ||
404 | if (res) | ||
405 | return res; | ||
406 | |||
407 | if (priv->irqen) { | ||
408 | control |= DS1343_A0IE; | ||
409 | res = regmap_write(priv->map, DS1343_CONTROL_REG, control); | ||
410 | } | ||
411 | |||
412 | return res; | ||
413 | } | ||
414 | |||
415 | static int ds1343_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) | ||
416 | { | ||
417 | struct ds1343_priv *priv = dev_get_drvdata(dev); | ||
418 | struct spi_device *spi = priv->spi; | ||
419 | int res = 0; | ||
420 | unsigned int stat; | ||
421 | |||
422 | if (spi->irq <= 0) | ||
423 | return -EINVAL; | ||
424 | |||
425 | mutex_lock(&priv->mutex); | ||
426 | |||
427 | res = regmap_read(priv->map, DS1343_STATUS_REG, &stat); | ||
428 | if (res) | ||
429 | goto out; | ||
430 | |||
431 | alarm->enabled = !!(priv->irqen & RTC_AF); | ||
432 | alarm->pending = !!(stat & DS1343_IRQF0); | ||
433 | |||
434 | alarm->time.tm_sec = priv->alarm_sec < 0 ? 0 : priv->alarm_sec; | ||
435 | alarm->time.tm_min = priv->alarm_min < 0 ? 0 : priv->alarm_min; | ||
436 | alarm->time.tm_hour = priv->alarm_hour < 0 ? 0 : priv->alarm_hour; | ||
437 | alarm->time.tm_mday = priv->alarm_mday < 0 ? 0 : priv->alarm_mday; | ||
438 | |||
439 | alarm->time.tm_mon = -1; | ||
440 | alarm->time.tm_year = -1; | ||
441 | alarm->time.tm_wday = -1; | ||
442 | alarm->time.tm_yday = -1; | ||
443 | alarm->time.tm_isdst = -1; | ||
444 | |||
445 | out: | ||
446 | mutex_unlock(&priv->mutex); | ||
447 | return res; | ||
448 | } | ||
449 | |||
450 | static int ds1343_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) | ||
451 | { | ||
452 | struct ds1343_priv *priv = dev_get_drvdata(dev); | ||
453 | struct spi_device *spi = priv->spi; | ||
454 | int res = 0; | ||
455 | |||
456 | if (spi->irq <= 0) | ||
457 | return -EINVAL; | ||
458 | |||
459 | mutex_lock(&priv->mutex); | ||
460 | |||
461 | priv->alarm_sec = alarm->time.tm_sec; | ||
462 | priv->alarm_min = alarm->time.tm_min; | ||
463 | priv->alarm_hour = alarm->time.tm_hour; | ||
464 | priv->alarm_mday = alarm->time.tm_mday; | ||
465 | |||
466 | if (alarm->enabled) | ||
467 | priv->irqen |= RTC_AF; | ||
468 | |||
469 | res = ds1343_update_alarm(dev); | ||
470 | |||
471 | mutex_unlock(&priv->mutex); | ||
472 | |||
473 | return res; | ||
474 | } | ||
475 | |||
476 | static int ds1343_alarm_irq_enable(struct device *dev, unsigned int enabled) | ||
477 | { | ||
478 | struct ds1343_priv *priv = dev_get_drvdata(dev); | ||
479 | struct spi_device *spi = priv->spi; | ||
480 | int res = 0; | ||
481 | |||
482 | if (spi->irq <= 0) | ||
483 | return -EINVAL; | ||
484 | |||
485 | mutex_lock(&priv->mutex); | ||
486 | |||
487 | if (enabled) | ||
488 | priv->irqen |= RTC_AF; | ||
489 | else | ||
490 | priv->irqen &= ~RTC_AF; | ||
491 | |||
492 | res = ds1343_update_alarm(dev); | ||
493 | |||
494 | mutex_unlock(&priv->mutex); | ||
495 | |||
496 | return res; | ||
497 | } | ||
498 | |||
499 | static irqreturn_t ds1343_thread(int irq, void *dev_id) | ||
500 | { | ||
501 | struct ds1343_priv *priv = dev_id; | ||
502 | unsigned int stat, control; | ||
503 | int res = 0; | ||
504 | |||
505 | mutex_lock(&priv->mutex); | ||
506 | |||
507 | res = regmap_read(priv->map, DS1343_STATUS_REG, &stat); | ||
508 | if (res) | ||
509 | goto out; | ||
510 | |||
511 | if (stat & DS1343_IRQF0) { | ||
512 | stat &= ~DS1343_IRQF0; | ||
513 | regmap_write(priv->map, DS1343_STATUS_REG, stat); | ||
514 | |||
515 | res = regmap_read(priv->map, DS1343_CONTROL_REG, &control); | ||
516 | if (res) | ||
517 | goto out; | ||
518 | |||
519 | control &= ~DS1343_A0IE; | ||
520 | regmap_write(priv->map, DS1343_CONTROL_REG, control); | ||
521 | |||
522 | rtc_update_irq(priv->rtc, 1, RTC_AF | RTC_IRQF); | ||
523 | } | ||
524 | |||
525 | out: | ||
526 | mutex_unlock(&priv->mutex); | ||
527 | return IRQ_HANDLED; | ||
528 | } | ||
529 | |||
530 | static const struct rtc_class_ops ds1343_rtc_ops = { | ||
531 | .ioctl = ds1343_ioctl, | ||
532 | .read_time = ds1343_read_time, | ||
533 | .set_time = ds1343_set_time, | ||
534 | .read_alarm = ds1343_read_alarm, | ||
535 | .set_alarm = ds1343_set_alarm, | ||
536 | .alarm_irq_enable = ds1343_alarm_irq_enable, | ||
537 | }; | ||
538 | |||
539 | static int ds1343_probe(struct spi_device *spi) | ||
540 | { | ||
541 | struct ds1343_priv *priv; | ||
542 | struct regmap_config config; | ||
543 | unsigned int data; | ||
544 | int res; | ||
545 | |||
546 | memset(&config, 0, sizeof(config)); | ||
547 | config.reg_bits = 8; | ||
548 | config.val_bits = 8; | ||
549 | config.write_flag_mask = 0x80; | ||
550 | |||
551 | priv = devm_kzalloc(&spi->dev, sizeof(struct ds1343_priv), GFP_KERNEL); | ||
552 | if (!priv) | ||
553 | return -ENOMEM; | ||
554 | |||
555 | priv->spi = spi; | ||
556 | mutex_init(&priv->mutex); | ||
557 | |||
558 | /* RTC DS1347 works in spi mode 3 and | ||
559 | * its chip select is active high | ||
560 | */ | ||
561 | spi->mode = SPI_MODE_3 | SPI_CS_HIGH; | ||
562 | spi->bits_per_word = 8; | ||
563 | res = spi_setup(spi); | ||
564 | if (res) | ||
565 | return res; | ||
566 | |||
567 | spi_set_drvdata(spi, priv); | ||
568 | |||
569 | priv->map = devm_regmap_init_spi(spi, &config); | ||
570 | |||
571 | if (IS_ERR(priv->map)) { | ||
572 | dev_err(&spi->dev, "spi regmap init failed for rtc ds1343\n"); | ||
573 | return PTR_ERR(priv->map); | ||
574 | } | ||
575 | |||
576 | res = regmap_read(priv->map, DS1343_SECONDS_REG, &data); | ||
577 | if (res) | ||
578 | return res; | ||
579 | |||
580 | regmap_read(priv->map, DS1343_CONTROL_REG, &data); | ||
581 | data |= DS1343_INTCN; | ||
582 | data &= ~(DS1343_EOSC | DS1343_A1IE | DS1343_A0IE); | ||
583 | regmap_write(priv->map, DS1343_CONTROL_REG, data); | ||
584 | |||
585 | regmap_read(priv->map, DS1343_STATUS_REG, &data); | ||
586 | data &= ~(DS1343_OSF | DS1343_IRQF1 | DS1343_IRQF0); | ||
587 | regmap_write(priv->map, DS1343_STATUS_REG, data); | ||
588 | |||
589 | priv->rtc = devm_rtc_device_register(&spi->dev, "ds1343", | ||
590 | &ds1343_rtc_ops, THIS_MODULE); | ||
591 | if (IS_ERR(priv->rtc)) { | ||
592 | dev_err(&spi->dev, "unable to register rtc ds1343\n"); | ||
593 | return PTR_ERR(priv->rtc); | ||
594 | } | ||
595 | |||
596 | if (spi->irq >= 0) { | ||
597 | res = devm_request_threaded_irq(&spi->dev, spi->irq, NULL, | ||
598 | ds1343_thread, | ||
599 | IRQF_NO_SUSPEND | IRQF_ONESHOT, | ||
600 | "ds1343", priv); | ||
601 | if (res) { | ||
602 | dev_err(&spi->dev, | ||
603 | "unable to request irq for rtc ds1343\n"); | ||
604 | return res; | ||
605 | } | ||
606 | |||
607 | device_set_wakeup_capable(&spi->dev, 1); | ||
608 | } | ||
609 | |||
610 | res = ds1343_sysfs_register(&spi->dev); | ||
611 | if (res) | ||
612 | dev_err(&spi->dev, | ||
613 | "unable to create sysfs entries for rtc ds1343\n"); | ||
614 | |||
615 | return 0; | ||
616 | } | ||
617 | |||
618 | static int ds1343_remove(struct spi_device *spi) | ||
619 | { | ||
620 | struct ds1343_priv *priv = spi_get_drvdata(spi); | ||
621 | |||
622 | if (spi->irq) { | ||
623 | mutex_lock(&priv->mutex); | ||
624 | priv->irqen &= ~RTC_AF; | ||
625 | mutex_unlock(&priv->mutex); | ||
626 | |||
627 | devm_free_irq(&spi->dev, spi->irq, priv); | ||
628 | } | ||
629 | |||
630 | spi_set_drvdata(spi, NULL); | ||
631 | |||
632 | ds1343_sysfs_unregister(&spi->dev); | ||
633 | |||
634 | return 0; | ||
635 | } | ||
636 | |||
637 | #ifdef CONFIG_PM_SLEEP | ||
638 | |||
639 | static int ds1343_suspend(struct device *dev) | ||
640 | { | ||
641 | struct spi_device *spi = to_spi_device(dev); | ||
642 | |||
643 | if (spi->irq >= 0 && device_may_wakeup(dev)) | ||
644 | enable_irq_wake(spi->irq); | ||
645 | |||
646 | return 0; | ||
647 | } | ||
648 | |||
649 | static int ds1343_resume(struct device *dev) | ||
650 | { | ||
651 | struct spi_device *spi = to_spi_device(dev); | ||
652 | |||
653 | if (spi->irq >= 0 && device_may_wakeup(dev)) | ||
654 | disable_irq_wake(spi->irq); | ||
655 | |||
656 | return 0; | ||
657 | } | ||
658 | |||
659 | #endif | ||
660 | |||
661 | static SIMPLE_DEV_PM_OPS(ds1343_pm, ds1343_suspend, ds1343_resume); | ||
662 | |||
663 | static struct spi_driver ds1343_driver = { | ||
664 | .driver = { | ||
665 | .name = "ds1343", | ||
666 | .owner = THIS_MODULE, | ||
667 | .pm = &ds1343_pm, | ||
668 | }, | ||
669 | .probe = ds1343_probe, | ||
670 | .remove = ds1343_remove, | ||
671 | .id_table = ds1343_id, | ||
672 | }; | ||
673 | |||
674 | module_spi_driver(ds1343_driver); | ||
675 | |||
676 | MODULE_DESCRIPTION("DS1343 RTC SPI Driver"); | ||
677 | MODULE_AUTHOR("Raghavendra Chandra Ganiga <ravi23ganiga@gmail.com>"); | ||
678 | MODULE_LICENSE("GPL v2"); | ||
679 | MODULE_VERSION(DS1343_DRV_VERSION); | ||