diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-06-06 19:35:10 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-06-06 19:35:10 -0400 |
commit | 57d326169e878a1a37b2bccd1cf81f6809ee67b9 (patch) | |
tree | 86ed74ae4dc2beaebce1c67b8459f1873b777d3a /drivers/rtc/rtc-ds1343.c | |
parent | 7b215de3d0abbc4f6daf2efd19e8809af0564490 (diff) | |
parent | 0244756edc4b98c129e92c7061d9f383708cf786 (diff) |
Merge branch 'akpm' (patches from Andrew Morton) into next
Merge more updates from Andrew Morton:
- Most of the rest of MM.
This includes "mark remap_file_pages syscall as deprecated" but the
actual "replace remap_file_pages syscall with emulation" is held
back. I guess we'll need to work out when to pull the trigger on
that one.
- various minor cleanups to obscure filesystems
- the drivers/rtc queue
- hfsplus updates
- ufs, hpfs, fatfs, affs, reiserfs
- Documentation/
- signals
- procfs
- cpu hotplug
- lib/idr.c
- rapidio
- sysctl
- ipc updates
* emailed patches from Andrew Morton <akpm@linux-foundation.org>: (171 commits)
ufs: sb mutex merge + mutex_destroy
powerpc: update comments for generic idle conversion
cris: update comments for generic idle conversion
idle: remove cpu_idle() forward declarations
nbd: zero from and len fields in NBD_CMD_DISCONNECT.
mm: convert some level-less printks to pr_*
MAINTAINERS: adi-buildroot-devel is moderated
MAINTAINERS: add linux-api for review of API/ABI changes
mm/kmemleak-test.c: use pr_fmt for logging
fs/dlm/debug_fs.c: replace seq_printf by seq_puts
fs/dlm/lockspace.c: convert simple_str to kstr
fs/dlm/config.c: convert simple_str to kstr
mm: mark remap_file_pages() syscall as deprecated
mm: memcontrol: remove unnecessary memcg argument from soft limit functions
mm: memcontrol: clean up memcg zoneinfo lookup
mm/memblock.c: call kmemleak directly from memblock_(alloc|free)
mm/mempool.c: update the kmemleak stack trace for mempool allocations
lib/radix-tree.c: update the kmemleak stack trace for radix tree allocations
mm: introduce kmemleak_update_trace()
mm/kmemleak.c: use %u to print ->checksum
...
Diffstat (limited to 'drivers/rtc/rtc-ds1343.c')
-rw-r--r-- | drivers/rtc/rtc-ds1343.c | 689 |
1 files changed, 689 insertions, 0 deletions
diff --git a/drivers/rtc/rtc-ds1343.c b/drivers/rtc/rtc-ds1343.c new file mode 100644 index 000000000000..c3719189dd96 --- /dev/null +++ b/drivers/rtc/rtc-ds1343.c | |||
@@ -0,0 +1,689 @@ | |||
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 irq; | ||
84 | int alarm_sec; | ||
85 | int alarm_min; | ||
86 | int alarm_hour; | ||
87 | int alarm_mday; | ||
88 | }; | ||
89 | |||
90 | static int ds1343_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) | ||
91 | { | ||
92 | switch (cmd) { | ||
93 | #ifdef RTC_SET_CHARGE | ||
94 | case RTC_SET_CHARGE: | ||
95 | { | ||
96 | int val; | ||
97 | |||
98 | if (copy_from_user(&val, (int __user *)arg, sizeof(int))) | ||
99 | return -EFAULT; | ||
100 | |||
101 | return regmap_write(priv->map, DS1343_TRICKLE_REG, val); | ||
102 | } | ||
103 | break; | ||
104 | #endif | ||
105 | } | ||
106 | |||
107 | return -ENOIOCTLCMD; | ||
108 | } | ||
109 | |||
110 | static ssize_t ds1343_show_glitchfilter(struct device *dev, | ||
111 | struct device_attribute *attr, char *buf) | ||
112 | { | ||
113 | struct ds1343_priv *priv = dev_get_drvdata(dev); | ||
114 | int glitch_filt_status, data; | ||
115 | |||
116 | regmap_read(priv->map, DS1343_CONTROL_REG, &data); | ||
117 | |||
118 | glitch_filt_status = !!(data & DS1343_EGFIL); | ||
119 | |||
120 | if (glitch_filt_status) | ||
121 | return sprintf(buf, "enabled\n"); | ||
122 | else | ||
123 | return sprintf(buf, "disabled\n"); | ||
124 | } | ||
125 | |||
126 | static ssize_t ds1343_store_glitchfilter(struct device *dev, | ||
127 | struct device_attribute *attr, | ||
128 | const char *buf, size_t count) | ||
129 | { | ||
130 | struct ds1343_priv *priv = dev_get_drvdata(dev); | ||
131 | int data; | ||
132 | |||
133 | regmap_read(priv->map, DS1343_CONTROL_REG, &data); | ||
134 | |||
135 | if (strncmp(buf, "enabled", 7) == 0) | ||
136 | data |= DS1343_EGFIL; | ||
137 | |||
138 | else if (strncmp(buf, "disabled", 8) == 0) | ||
139 | data &= ~(DS1343_EGFIL); | ||
140 | |||
141 | else | ||
142 | return -EINVAL; | ||
143 | |||
144 | regmap_write(priv->map, DS1343_CONTROL_REG, data); | ||
145 | |||
146 | return count; | ||
147 | } | ||
148 | |||
149 | static DEVICE_ATTR(glitch_filter, S_IRUGO | S_IWUSR, ds1343_show_glitchfilter, | ||
150 | ds1343_store_glitchfilter); | ||
151 | |||
152 | static ssize_t ds1343_show_alarmstatus(struct device *dev, | ||
153 | struct device_attribute *attr, char *buf) | ||
154 | { | ||
155 | struct ds1343_priv *priv = dev_get_drvdata(dev); | ||
156 | int alarmstatus, data; | ||
157 | |||
158 | regmap_read(priv->map, DS1343_CONTROL_REG, &data); | ||
159 | |||
160 | alarmstatus = !!(data & DS1343_A0IE); | ||
161 | |||
162 | if (alarmstatus) | ||
163 | return sprintf(buf, "enabled\n"); | ||
164 | else | ||
165 | return sprintf(buf, "disabled\n"); | ||
166 | } | ||
167 | |||
168 | static DEVICE_ATTR(alarm_status, S_IRUGO, ds1343_show_alarmstatus, NULL); | ||
169 | |||
170 | static ssize_t ds1343_show_alarmmode(struct device *dev, | ||
171 | struct device_attribute *attr, char *buf) | ||
172 | { | ||
173 | struct ds1343_priv *priv = dev_get_drvdata(dev); | ||
174 | int alarm_mode, data; | ||
175 | char *alarm_str; | ||
176 | |||
177 | regmap_read(priv->map, DS1343_ALM0_SEC_REG, &data); | ||
178 | alarm_mode = (data & 0x80) >> 4; | ||
179 | |||
180 | regmap_read(priv->map, DS1343_ALM0_MIN_REG, &data); | ||
181 | alarm_mode |= (data & 0x80) >> 5; | ||
182 | |||
183 | regmap_read(priv->map, DS1343_ALM0_HOUR_REG, &data); | ||
184 | alarm_mode |= (data & 0x80) >> 6; | ||
185 | |||
186 | regmap_read(priv->map, DS1343_ALM0_DAY_REG, &data); | ||
187 | alarm_mode |= (data & 0x80) >> 7; | ||
188 | |||
189 | switch (alarm_mode) { | ||
190 | case 15: | ||
191 | alarm_str = "each second"; | ||
192 | break; | ||
193 | |||
194 | case 7: | ||
195 | alarm_str = "seconds match"; | ||
196 | break; | ||
197 | |||
198 | case 3: | ||
199 | alarm_str = "minutes and seconds match"; | ||
200 | break; | ||
201 | |||
202 | case 1: | ||
203 | alarm_str = "hours, minutes and seconds match"; | ||
204 | break; | ||
205 | |||
206 | case 0: | ||
207 | alarm_str = "day, hours, minutes and seconds match"; | ||
208 | break; | ||
209 | |||
210 | default: | ||
211 | alarm_str = "invalid"; | ||
212 | break; | ||
213 | } | ||
214 | |||
215 | return sprintf(buf, "%s\n", alarm_str); | ||
216 | } | ||
217 | |||
218 | static DEVICE_ATTR(alarm_mode, S_IRUGO, ds1343_show_alarmmode, NULL); | ||
219 | |||
220 | static ssize_t ds1343_show_tricklecharger(struct device *dev, | ||
221 | struct device_attribute *attr, char *buf) | ||
222 | { | ||
223 | struct ds1343_priv *priv = dev_get_drvdata(dev); | ||
224 | int data; | ||
225 | char *diodes = "disabled", *resistors = " "; | ||
226 | |||
227 | regmap_read(priv->map, DS1343_TRICKLE_REG, &data); | ||
228 | |||
229 | if ((data & 0xf0) == DS1343_TRICKLE_MAGIC) { | ||
230 | switch (data & 0x0c) { | ||
231 | case DS1343_TRICKLE_DS1: | ||
232 | diodes = "one diode,"; | ||
233 | break; | ||
234 | |||
235 | default: | ||
236 | diodes = "no diode,"; | ||
237 | break; | ||
238 | } | ||
239 | |||
240 | switch (data & 0x03) { | ||
241 | case DS1343_TRICKLE_1K: | ||
242 | resistors = "1k Ohm"; | ||
243 | break; | ||
244 | |||
245 | case DS1343_TRICKLE_2K: | ||
246 | resistors = "2k Ohm"; | ||
247 | break; | ||
248 | |||
249 | case DS1343_TRICKLE_4K: | ||
250 | resistors = "4k Ohm"; | ||
251 | break; | ||
252 | |||
253 | default: | ||
254 | diodes = "disabled"; | ||
255 | break; | ||
256 | } | ||
257 | } | ||
258 | |||
259 | return sprintf(buf, "%s %s\n", diodes, resistors); | ||
260 | } | ||
261 | |||
262 | static DEVICE_ATTR(trickle_charger, S_IRUGO, ds1343_show_tricklecharger, NULL); | ||
263 | |||
264 | static int ds1343_sysfs_register(struct device *dev) | ||
265 | { | ||
266 | struct ds1343_priv *priv = dev_get_drvdata(dev); | ||
267 | int err; | ||
268 | |||
269 | err = device_create_file(dev, &dev_attr_glitch_filter); | ||
270 | if (err) | ||
271 | return err; | ||
272 | |||
273 | err = device_create_file(dev, &dev_attr_trickle_charger); | ||
274 | if (err) | ||
275 | goto error1; | ||
276 | |||
277 | if (priv->irq <= 0) | ||
278 | return err; | ||
279 | |||
280 | err = device_create_file(dev, &dev_attr_alarm_mode); | ||
281 | if (err) | ||
282 | goto error2; | ||
283 | |||
284 | err = device_create_file(dev, &dev_attr_alarm_status); | ||
285 | if (!err) | ||
286 | return err; | ||
287 | |||
288 | device_remove_file(dev, &dev_attr_alarm_mode); | ||
289 | |||
290 | error2: | ||
291 | device_remove_file(dev, &dev_attr_trickle_charger); | ||
292 | |||
293 | error1: | ||
294 | device_remove_file(dev, &dev_attr_glitch_filter); | ||
295 | |||
296 | return err; | ||
297 | } | ||
298 | |||
299 | static void ds1343_sysfs_unregister(struct device *dev) | ||
300 | { | ||
301 | struct ds1343_priv *priv = dev_get_drvdata(dev); | ||
302 | |||
303 | device_remove_file(dev, &dev_attr_glitch_filter); | ||
304 | device_remove_file(dev, &dev_attr_trickle_charger); | ||
305 | |||
306 | if (priv->irq <= 0) | ||
307 | return; | ||
308 | |||
309 | device_remove_file(dev, &dev_attr_alarm_status); | ||
310 | device_remove_file(dev, &dev_attr_alarm_mode); | ||
311 | } | ||
312 | |||
313 | static int ds1343_read_time(struct device *dev, struct rtc_time *dt) | ||
314 | { | ||
315 | struct ds1343_priv *priv = dev_get_drvdata(dev); | ||
316 | unsigned char buf[7]; | ||
317 | int res; | ||
318 | |||
319 | res = regmap_bulk_read(priv->map, DS1343_SECONDS_REG, buf, 7); | ||
320 | if (res) | ||
321 | return res; | ||
322 | |||
323 | dt->tm_sec = bcd2bin(buf[0]); | ||
324 | dt->tm_min = bcd2bin(buf[1]); | ||
325 | dt->tm_hour = bcd2bin(buf[2] & 0x3F); | ||
326 | dt->tm_wday = bcd2bin(buf[3]) - 1; | ||
327 | dt->tm_mday = bcd2bin(buf[4]); | ||
328 | dt->tm_mon = bcd2bin(buf[5] & 0x1F) - 1; | ||
329 | dt->tm_year = bcd2bin(buf[6]) + 100; /* year offset from 1900 */ | ||
330 | |||
331 | return rtc_valid_tm(dt); | ||
332 | } | ||
333 | |||
334 | static int ds1343_set_time(struct device *dev, struct rtc_time *dt) | ||
335 | { | ||
336 | struct ds1343_priv *priv = dev_get_drvdata(dev); | ||
337 | int res; | ||
338 | |||
339 | res = regmap_write(priv->map, DS1343_SECONDS_REG, | ||
340 | bin2bcd(dt->tm_sec)); | ||
341 | if (res) | ||
342 | return res; | ||
343 | |||
344 | res = regmap_write(priv->map, DS1343_MINUTES_REG, | ||
345 | bin2bcd(dt->tm_min)); | ||
346 | if (res) | ||
347 | return res; | ||
348 | |||
349 | res = regmap_write(priv->map, DS1343_HOURS_REG, | ||
350 | bin2bcd(dt->tm_hour) & 0x3F); | ||
351 | if (res) | ||
352 | return res; | ||
353 | |||
354 | res = regmap_write(priv->map, DS1343_DAY_REG, | ||
355 | bin2bcd(dt->tm_wday + 1)); | ||
356 | if (res) | ||
357 | return res; | ||
358 | |||
359 | res = regmap_write(priv->map, DS1343_DATE_REG, | ||
360 | bin2bcd(dt->tm_mday)); | ||
361 | if (res) | ||
362 | return res; | ||
363 | |||
364 | res = regmap_write(priv->map, DS1343_MONTH_REG, | ||
365 | bin2bcd(dt->tm_mon + 1)); | ||
366 | if (res) | ||
367 | return res; | ||
368 | |||
369 | dt->tm_year %= 100; | ||
370 | |||
371 | res = regmap_write(priv->map, DS1343_YEAR_REG, | ||
372 | bin2bcd(dt->tm_year)); | ||
373 | if (res) | ||
374 | return res; | ||
375 | |||
376 | return 0; | ||
377 | } | ||
378 | |||
379 | static int ds1343_update_alarm(struct device *dev) | ||
380 | { | ||
381 | struct ds1343_priv *priv = dev_get_drvdata(dev); | ||
382 | unsigned int control, stat; | ||
383 | unsigned char buf[4]; | ||
384 | int res = 0; | ||
385 | |||
386 | res = regmap_read(priv->map, DS1343_CONTROL_REG, &control); | ||
387 | if (res) | ||
388 | return res; | ||
389 | |||
390 | res = regmap_read(priv->map, DS1343_STATUS_REG, &stat); | ||
391 | if (res) | ||
392 | return res; | ||
393 | |||
394 | control &= ~(DS1343_A0IE); | ||
395 | stat &= ~(DS1343_IRQF0); | ||
396 | |||
397 | res = regmap_write(priv->map, DS1343_CONTROL_REG, control); | ||
398 | if (res) | ||
399 | return res; | ||
400 | |||
401 | res = regmap_write(priv->map, DS1343_STATUS_REG, stat); | ||
402 | if (res) | ||
403 | return res; | ||
404 | |||
405 | buf[0] = priv->alarm_sec < 0 || (priv->irqen & RTC_UF) ? | ||
406 | 0x80 : bin2bcd(priv->alarm_sec) & 0x7F; | ||
407 | buf[1] = priv->alarm_min < 0 || (priv->irqen & RTC_UF) ? | ||
408 | 0x80 : bin2bcd(priv->alarm_min) & 0x7F; | ||
409 | buf[2] = priv->alarm_hour < 0 || (priv->irqen & RTC_UF) ? | ||
410 | 0x80 : bin2bcd(priv->alarm_hour) & 0x3F; | ||
411 | buf[3] = priv->alarm_mday < 0 || (priv->irqen & RTC_UF) ? | ||
412 | 0x80 : bin2bcd(priv->alarm_mday) & 0x7F; | ||
413 | |||
414 | res = regmap_bulk_write(priv->map, DS1343_ALM0_SEC_REG, buf, 4); | ||
415 | if (res) | ||
416 | return res; | ||
417 | |||
418 | if (priv->irqen) { | ||
419 | control |= DS1343_A0IE; | ||
420 | res = regmap_write(priv->map, DS1343_CONTROL_REG, control); | ||
421 | } | ||
422 | |||
423 | return res; | ||
424 | } | ||
425 | |||
426 | static int ds1343_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) | ||
427 | { | ||
428 | struct ds1343_priv *priv = dev_get_drvdata(dev); | ||
429 | int res = 0; | ||
430 | unsigned int stat; | ||
431 | |||
432 | if (priv->irq <= 0) | ||
433 | return -EINVAL; | ||
434 | |||
435 | mutex_lock(&priv->mutex); | ||
436 | |||
437 | res = regmap_read(priv->map, DS1343_STATUS_REG, &stat); | ||
438 | if (res) | ||
439 | goto out; | ||
440 | |||
441 | alarm->enabled = !!(priv->irqen & RTC_AF); | ||
442 | alarm->pending = !!(stat & DS1343_IRQF0); | ||
443 | |||
444 | alarm->time.tm_sec = priv->alarm_sec < 0 ? 0 : priv->alarm_sec; | ||
445 | alarm->time.tm_min = priv->alarm_min < 0 ? 0 : priv->alarm_min; | ||
446 | alarm->time.tm_hour = priv->alarm_hour < 0 ? 0 : priv->alarm_hour; | ||
447 | alarm->time.tm_mday = priv->alarm_mday < 0 ? 0 : priv->alarm_mday; | ||
448 | |||
449 | alarm->time.tm_mon = -1; | ||
450 | alarm->time.tm_year = -1; | ||
451 | alarm->time.tm_wday = -1; | ||
452 | alarm->time.tm_yday = -1; | ||
453 | alarm->time.tm_isdst = -1; | ||
454 | |||
455 | out: | ||
456 | mutex_unlock(&priv->mutex); | ||
457 | return res; | ||
458 | } | ||
459 | |||
460 | static int ds1343_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) | ||
461 | { | ||
462 | struct ds1343_priv *priv = dev_get_drvdata(dev); | ||
463 | int res = 0; | ||
464 | |||
465 | if (priv->irq <= 0) | ||
466 | return -EINVAL; | ||
467 | |||
468 | mutex_lock(&priv->mutex); | ||
469 | |||
470 | priv->alarm_sec = alarm->time.tm_sec; | ||
471 | priv->alarm_min = alarm->time.tm_min; | ||
472 | priv->alarm_hour = alarm->time.tm_hour; | ||
473 | priv->alarm_mday = alarm->time.tm_mday; | ||
474 | |||
475 | if (alarm->enabled) | ||
476 | priv->irqen |= RTC_AF; | ||
477 | |||
478 | res = ds1343_update_alarm(dev); | ||
479 | |||
480 | mutex_unlock(&priv->mutex); | ||
481 | |||
482 | return res; | ||
483 | } | ||
484 | |||
485 | static int ds1343_alarm_irq_enable(struct device *dev, unsigned int enabled) | ||
486 | { | ||
487 | struct ds1343_priv *priv = dev_get_drvdata(dev); | ||
488 | int res = 0; | ||
489 | |||
490 | if (priv->irq <= 0) | ||
491 | return -EINVAL; | ||
492 | |||
493 | mutex_lock(&priv->mutex); | ||
494 | |||
495 | if (enabled) | ||
496 | priv->irqen |= RTC_AF; | ||
497 | else | ||
498 | priv->irqen &= ~RTC_AF; | ||
499 | |||
500 | res = ds1343_update_alarm(dev); | ||
501 | |||
502 | mutex_unlock(&priv->mutex); | ||
503 | |||
504 | return res; | ||
505 | } | ||
506 | |||
507 | static irqreturn_t ds1343_thread(int irq, void *dev_id) | ||
508 | { | ||
509 | struct ds1343_priv *priv = dev_id; | ||
510 | unsigned int stat, control; | ||
511 | int res = 0; | ||
512 | |||
513 | mutex_lock(&priv->mutex); | ||
514 | |||
515 | res = regmap_read(priv->map, DS1343_STATUS_REG, &stat); | ||
516 | if (res) | ||
517 | goto out; | ||
518 | |||
519 | if (stat & DS1343_IRQF0) { | ||
520 | stat &= ~DS1343_IRQF0; | ||
521 | regmap_write(priv->map, DS1343_STATUS_REG, stat); | ||
522 | |||
523 | res = regmap_read(priv->map, DS1343_CONTROL_REG, &control); | ||
524 | if (res) | ||
525 | goto out; | ||
526 | |||
527 | control &= ~DS1343_A0IE; | ||
528 | regmap_write(priv->map, DS1343_CONTROL_REG, control); | ||
529 | |||
530 | rtc_update_irq(priv->rtc, 1, RTC_AF | RTC_IRQF); | ||
531 | } | ||
532 | |||
533 | out: | ||
534 | mutex_unlock(&priv->mutex); | ||
535 | return IRQ_HANDLED; | ||
536 | } | ||
537 | |||
538 | static const struct rtc_class_ops ds1343_rtc_ops = { | ||
539 | .ioctl = ds1343_ioctl, | ||
540 | .read_time = ds1343_read_time, | ||
541 | .set_time = ds1343_set_time, | ||
542 | .read_alarm = ds1343_read_alarm, | ||
543 | .set_alarm = ds1343_set_alarm, | ||
544 | .alarm_irq_enable = ds1343_alarm_irq_enable, | ||
545 | }; | ||
546 | |||
547 | static int ds1343_probe(struct spi_device *spi) | ||
548 | { | ||
549 | struct ds1343_priv *priv; | ||
550 | struct regmap_config config; | ||
551 | unsigned int data; | ||
552 | int res; | ||
553 | |||
554 | memset(&config, 0, sizeof(config)); | ||
555 | config.reg_bits = 8; | ||
556 | config.val_bits = 8; | ||
557 | config.write_flag_mask = 0x80; | ||
558 | |||
559 | priv = devm_kzalloc(&spi->dev, sizeof(struct ds1343_priv), GFP_KERNEL); | ||
560 | if (!priv) | ||
561 | return -ENOMEM; | ||
562 | |||
563 | priv->spi = spi; | ||
564 | mutex_init(&priv->mutex); | ||
565 | |||
566 | /* RTC DS1347 works in spi mode 3 and | ||
567 | * its chip select is active high | ||
568 | */ | ||
569 | spi->mode = SPI_MODE_3 | SPI_CS_HIGH; | ||
570 | spi->bits_per_word = 8; | ||
571 | res = spi_setup(spi); | ||
572 | if (res) | ||
573 | return res; | ||
574 | |||
575 | spi_set_drvdata(spi, priv); | ||
576 | |||
577 | priv->map = devm_regmap_init_spi(spi, &config); | ||
578 | |||
579 | if (IS_ERR(priv->map)) { | ||
580 | dev_err(&spi->dev, "spi regmap init failed for rtc ds1343\n"); | ||
581 | return PTR_ERR(priv->map); | ||
582 | } | ||
583 | |||
584 | res = regmap_read(priv->map, DS1343_SECONDS_REG, &data); | ||
585 | if (res) | ||
586 | return res; | ||
587 | |||
588 | regmap_read(priv->map, DS1343_CONTROL_REG, &data); | ||
589 | data |= DS1343_INTCN; | ||
590 | data &= ~(DS1343_EOSC | DS1343_A1IE | DS1343_A0IE); | ||
591 | regmap_write(priv->map, DS1343_CONTROL_REG, data); | ||
592 | |||
593 | regmap_read(priv->map, DS1343_STATUS_REG, &data); | ||
594 | data &= ~(DS1343_OSF | DS1343_IRQF1 | DS1343_IRQF0); | ||
595 | regmap_write(priv->map, DS1343_STATUS_REG, data); | ||
596 | |||
597 | priv->rtc = devm_rtc_device_register(&spi->dev, "ds1343", | ||
598 | &ds1343_rtc_ops, THIS_MODULE); | ||
599 | if (IS_ERR(priv->rtc)) { | ||
600 | dev_err(&spi->dev, "unable to register rtc ds1343\n"); | ||
601 | return PTR_ERR(priv->rtc); | ||
602 | } | ||
603 | |||
604 | priv->irq = spi->irq; | ||
605 | |||
606 | if (priv->irq >= 0) { | ||
607 | res = devm_request_threaded_irq(&spi->dev, spi->irq, NULL, | ||
608 | ds1343_thread, | ||
609 | IRQF_NO_SUSPEND | IRQF_ONESHOT, | ||
610 | "ds1343", priv); | ||
611 | if (res) { | ||
612 | priv->irq = -1; | ||
613 | dev_err(&spi->dev, | ||
614 | "unable to request irq for rtc ds1343\n"); | ||
615 | } else { | ||
616 | device_set_wakeup_capable(&spi->dev, 1); | ||
617 | } | ||
618 | } | ||
619 | |||
620 | res = ds1343_sysfs_register(&spi->dev); | ||
621 | if (res) | ||
622 | dev_err(&spi->dev, | ||
623 | "unable to create sysfs entries for rtc ds1343\n"); | ||
624 | |||
625 | return 0; | ||
626 | } | ||
627 | |||
628 | static int ds1343_remove(struct spi_device *spi) | ||
629 | { | ||
630 | struct ds1343_priv *priv = spi_get_drvdata(spi); | ||
631 | |||
632 | if (spi->irq) { | ||
633 | mutex_lock(&priv->mutex); | ||
634 | priv->irqen &= ~RTC_AF; | ||
635 | mutex_unlock(&priv->mutex); | ||
636 | |||
637 | devm_free_irq(&spi->dev, spi->irq, priv); | ||
638 | } | ||
639 | |||
640 | spi_set_drvdata(spi, NULL); | ||
641 | |||
642 | ds1343_sysfs_unregister(&spi->dev); | ||
643 | |||
644 | return 0; | ||
645 | } | ||
646 | |||
647 | #ifdef CONFIG_PM_SLEEP | ||
648 | |||
649 | static int ds1343_suspend(struct device *dev) | ||
650 | { | ||
651 | struct spi_device *spi = to_spi_device(dev); | ||
652 | |||
653 | if (spi->irq >= 0 && device_may_wakeup(dev)) | ||
654 | enable_irq_wake(spi->irq); | ||
655 | |||
656 | return 0; | ||
657 | } | ||
658 | |||
659 | static int ds1343_resume(struct device *dev) | ||
660 | { | ||
661 | struct spi_device *spi = to_spi_device(dev); | ||
662 | |||
663 | if (spi->irq >= 0 && device_may_wakeup(dev)) | ||
664 | disable_irq_wake(spi->irq); | ||
665 | |||
666 | return 0; | ||
667 | } | ||
668 | |||
669 | #endif | ||
670 | |||
671 | static SIMPLE_DEV_PM_OPS(ds1343_pm, ds1343_suspend, ds1343_resume); | ||
672 | |||
673 | static struct spi_driver ds1343_driver = { | ||
674 | .driver = { | ||
675 | .name = "ds1343", | ||
676 | .owner = THIS_MODULE, | ||
677 | .pm = &ds1343_pm, | ||
678 | }, | ||
679 | .probe = ds1343_probe, | ||
680 | .remove = ds1343_remove, | ||
681 | .id_table = ds1343_id, | ||
682 | }; | ||
683 | |||
684 | module_spi_driver(ds1343_driver); | ||
685 | |||
686 | MODULE_DESCRIPTION("DS1343 RTC SPI Driver"); | ||
687 | MODULE_AUTHOR("Raghavendra Chandra Ganiga <ravi23ganiga@gmail.com>"); | ||
688 | MODULE_LICENSE("GPL v2"); | ||
689 | MODULE_VERSION(DS1343_DRV_VERSION); | ||