Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
1d6316f5 RG |
2 | /* rtc-ds1343.c |
3 | * | |
4 | * Driver for Dallas Semiconductor DS1343 Low Current, SPI Compatible | |
5 | * Real Time Clock | |
6 | * | |
7 | * Author : Raghavendra Chandra Ganiga <ravi23ganiga@gmail.com> | |
571eb883 | 8 | * Ankur Srivastava <sankurece@gmail.com> : DS1343 Nvram Support |
1d6316f5 RG |
9 | */ |
10 | ||
11 | #include <linux/init.h> | |
12 | #include <linux/module.h> | |
13 | #include <linux/interrupt.h> | |
14 | #include <linux/device.h> | |
15 | #include <linux/spi/spi.h> | |
16 | #include <linux/regmap.h> | |
17 | #include <linux/rtc.h> | |
18 | #include <linux/bcd.h> | |
19 | #include <linux/pm.h> | |
caff0cc4 | 20 | #include <linux/pm_wakeirq.h> |
1d6316f5 RG |
21 | #include <linux/slab.h> |
22 | ||
1d6316f5 RG |
23 | #define DALLAS_MAXIM_DS1343 0 |
24 | #define DALLAS_MAXIM_DS1344 1 | |
25 | ||
26 | /* RTC DS1343 Registers */ | |
27 | #define DS1343_SECONDS_REG 0x00 | |
28 | #define DS1343_MINUTES_REG 0x01 | |
29 | #define DS1343_HOURS_REG 0x02 | |
30 | #define DS1343_DAY_REG 0x03 | |
31 | #define DS1343_DATE_REG 0x04 | |
32 | #define DS1343_MONTH_REG 0x05 | |
33 | #define DS1343_YEAR_REG 0x06 | |
34 | #define DS1343_ALM0_SEC_REG 0x07 | |
35 | #define DS1343_ALM0_MIN_REG 0x08 | |
36 | #define DS1343_ALM0_HOUR_REG 0x09 | |
37 | #define DS1343_ALM0_DAY_REG 0x0A | |
38 | #define DS1343_ALM1_SEC_REG 0x0B | |
39 | #define DS1343_ALM1_MIN_REG 0x0C | |
40 | #define DS1343_ALM1_HOUR_REG 0x0D | |
41 | #define DS1343_ALM1_DAY_REG 0x0E | |
42 | #define DS1343_CONTROL_REG 0x0F | |
43 | #define DS1343_STATUS_REG 0x10 | |
44 | #define DS1343_TRICKLE_REG 0x11 | |
571eb883 RG |
45 | #define DS1343_NVRAM 0x20 |
46 | ||
47 | #define DS1343_NVRAM_LEN 96 | |
1d6316f5 RG |
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 { | |
1d6316f5 RG |
78 | struct rtc_device *rtc; |
79 | struct regmap *map; | |
10b06b87 | 80 | int irq; |
1d6316f5 RG |
81 | }; |
82 | ||
1d6316f5 RG |
83 | static ssize_t ds1343_show_glitchfilter(struct device *dev, |
84 | struct device_attribute *attr, char *buf) | |
85 | { | |
580daaf4 | 86 | struct ds1343_priv *priv = dev_get_drvdata(dev->parent); |
1d6316f5 | 87 | int glitch_filt_status, data; |
ce0fd9db | 88 | int res; |
1d6316f5 | 89 | |
ce0fd9db AB |
90 | res = regmap_read(priv->map, DS1343_CONTROL_REG, &data); |
91 | if (res) | |
92 | return res; | |
1d6316f5 RG |
93 | |
94 | glitch_filt_status = !!(data & DS1343_EGFIL); | |
95 | ||
96 | if (glitch_filt_status) | |
97 | return sprintf(buf, "enabled\n"); | |
98 | else | |
99 | return sprintf(buf, "disabled\n"); | |
100 | } | |
101 | ||
102 | static ssize_t ds1343_store_glitchfilter(struct device *dev, | |
103 | struct device_attribute *attr, | |
104 | const char *buf, size_t count) | |
105 | { | |
580daaf4 | 106 | struct ds1343_priv *priv = dev_get_drvdata(dev->parent); |
ac08888b AB |
107 | int data = 0; |
108 | int res; | |
1d6316f5 RG |
109 | |
110 | if (strncmp(buf, "enabled", 7) == 0) | |
ac08888b AB |
111 | data = DS1343_EGFIL; |
112 | else if (strncmp(buf, "disabled", 8)) | |
1d6316f5 RG |
113 | return -EINVAL; |
114 | ||
ac08888b AB |
115 | res = regmap_update_bits(priv->map, DS1343_CONTROL_REG, |
116 | DS1343_EGFIL, data); | |
117 | if (res) | |
118 | return res; | |
1d6316f5 RG |
119 | |
120 | return count; | |
121 | } | |
122 | ||
123 | static DEVICE_ATTR(glitch_filter, S_IRUGO | S_IWUSR, ds1343_show_glitchfilter, | |
124 | ds1343_store_glitchfilter); | |
125 | ||
d7501f70 AB |
126 | static int ds1343_nvram_write(void *priv, unsigned int off, void *val, |
127 | size_t bytes) | |
571eb883 | 128 | { |
d7501f70 | 129 | struct ds1343_priv *ds1343 = priv; |
571eb883 | 130 | |
d7501f70 | 131 | return regmap_bulk_write(ds1343->map, DS1343_NVRAM + off, val, bytes); |
571eb883 RG |
132 | } |
133 | ||
d7501f70 AB |
134 | static int ds1343_nvram_read(void *priv, unsigned int off, void *val, |
135 | size_t bytes) | |
571eb883 | 136 | { |
d7501f70 | 137 | struct ds1343_priv *ds1343 = priv; |
571eb883 | 138 | |
d7501f70 | 139 | return regmap_bulk_read(ds1343->map, DS1343_NVRAM + off, val, bytes); |
571eb883 RG |
140 | } |
141 | ||
1d6316f5 RG |
142 | static ssize_t ds1343_show_tricklecharger(struct device *dev, |
143 | struct device_attribute *attr, char *buf) | |
144 | { | |
580daaf4 | 145 | struct ds1343_priv *priv = dev_get_drvdata(dev->parent); |
ce0fd9db | 146 | int res, data; |
1d6316f5 RG |
147 | char *diodes = "disabled", *resistors = " "; |
148 | ||
ce0fd9db AB |
149 | res = regmap_read(priv->map, DS1343_TRICKLE_REG, &data); |
150 | if (res) | |
151 | return res; | |
1d6316f5 RG |
152 | |
153 | if ((data & 0xf0) == DS1343_TRICKLE_MAGIC) { | |
154 | switch (data & 0x0c) { | |
155 | case DS1343_TRICKLE_DS1: | |
156 | diodes = "one diode,"; | |
157 | break; | |
158 | ||
159 | default: | |
160 | diodes = "no diode,"; | |
161 | break; | |
162 | } | |
163 | ||
164 | switch (data & 0x03) { | |
165 | case DS1343_TRICKLE_1K: | |
166 | resistors = "1k Ohm"; | |
167 | break; | |
168 | ||
169 | case DS1343_TRICKLE_2K: | |
170 | resistors = "2k Ohm"; | |
171 | break; | |
172 | ||
173 | case DS1343_TRICKLE_4K: | |
174 | resistors = "4k Ohm"; | |
175 | break; | |
176 | ||
177 | default: | |
178 | diodes = "disabled"; | |
179 | break; | |
180 | } | |
181 | } | |
182 | ||
183 | return sprintf(buf, "%s %s\n", diodes, resistors); | |
184 | } | |
185 | ||
186 | static DEVICE_ATTR(trickle_charger, S_IRUGO, ds1343_show_tricklecharger, NULL); | |
187 | ||
580daaf4 AB |
188 | static struct attribute *ds1343_attrs[] = { |
189 | &dev_attr_glitch_filter.attr, | |
190 | &dev_attr_trickle_charger.attr, | |
191 | NULL | |
192 | }; | |
1d6316f5 | 193 | |
580daaf4 AB |
194 | static const struct attribute_group ds1343_attr_group = { |
195 | .attrs = ds1343_attrs, | |
196 | }; | |
1d6316f5 RG |
197 | |
198 | static int ds1343_read_time(struct device *dev, struct rtc_time *dt) | |
199 | { | |
200 | struct ds1343_priv *priv = dev_get_drvdata(dev); | |
201 | unsigned char buf[7]; | |
202 | int res; | |
203 | ||
204 | res = regmap_bulk_read(priv->map, DS1343_SECONDS_REG, buf, 7); | |
205 | if (res) | |
206 | return res; | |
207 | ||
208 | dt->tm_sec = bcd2bin(buf[0]); | |
209 | dt->tm_min = bcd2bin(buf[1]); | |
210 | dt->tm_hour = bcd2bin(buf[2] & 0x3F); | |
211 | dt->tm_wday = bcd2bin(buf[3]) - 1; | |
212 | dt->tm_mday = bcd2bin(buf[4]); | |
213 | dt->tm_mon = bcd2bin(buf[5] & 0x1F) - 1; | |
214 | dt->tm_year = bcd2bin(buf[6]) + 100; /* year offset from 1900 */ | |
215 | ||
22652ba7 | 216 | return 0; |
1d6316f5 RG |
217 | } |
218 | ||
219 | static int ds1343_set_time(struct device *dev, struct rtc_time *dt) | |
220 | { | |
221 | struct ds1343_priv *priv = dev_get_drvdata(dev); | |
f308b682 AB |
222 | u8 buf[7]; |
223 | ||
224 | buf[0] = bin2bcd(dt->tm_sec); | |
225 | buf[1] = bin2bcd(dt->tm_min); | |
226 | buf[2] = bin2bcd(dt->tm_hour) & 0x3F; | |
227 | buf[3] = bin2bcd(dt->tm_wday + 1); | |
228 | buf[4] = bin2bcd(dt->tm_mday); | |
229 | buf[5] = bin2bcd(dt->tm_mon + 1); | |
230 | buf[6] = bin2bcd(dt->tm_year - 100); | |
231 | ||
232 | return regmap_bulk_write(priv->map, DS1343_SECONDS_REG, | |
233 | buf, sizeof(buf)); | |
1d6316f5 RG |
234 | } |
235 | ||
0680a6cd | 236 | static int ds1343_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) |
1d6316f5 RG |
237 | { |
238 | struct ds1343_priv *priv = dev_get_drvdata(dev); | |
1d6316f5 | 239 | unsigned char buf[4]; |
0680a6cd AB |
240 | unsigned int val; |
241 | int res; | |
1d6316f5 | 242 | |
0680a6cd AB |
243 | if (priv->irq <= 0) |
244 | return -EINVAL; | |
1d6316f5 | 245 | |
0680a6cd | 246 | res = regmap_read(priv->map, DS1343_STATUS_REG, &val); |
1d6316f5 RG |
247 | if (res) |
248 | return res; | |
249 | ||
0680a6cd | 250 | alarm->pending = !!(val & DS1343_IRQF0); |
1d6316f5 | 251 | |
0680a6cd | 252 | res = regmap_read(priv->map, DS1343_CONTROL_REG, &val); |
1d6316f5 RG |
253 | if (res) |
254 | return res; | |
0680a6cd | 255 | alarm->enabled = !!(val & DS1343_A0IE); |
1d6316f5 | 256 | |
0680a6cd | 257 | res = regmap_bulk_read(priv->map, DS1343_ALM0_SEC_REG, buf, 4); |
1d6316f5 RG |
258 | if (res) |
259 | return res; | |
260 | ||
0680a6cd AB |
261 | alarm->time.tm_sec = bcd2bin(buf[0]) & 0x7f; |
262 | alarm->time.tm_min = bcd2bin(buf[1]) & 0x7f; | |
263 | alarm->time.tm_hour = bcd2bin(buf[2]) & 0x3f; | |
264 | alarm->time.tm_mday = bcd2bin(buf[3]) & 0x3f; | |
1d6316f5 | 265 | |
0680a6cd | 266 | return 0; |
1d6316f5 RG |
267 | } |
268 | ||
0680a6cd | 269 | static int ds1343_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) |
1d6316f5 RG |
270 | { |
271 | struct ds1343_priv *priv = dev_get_drvdata(dev); | |
0680a6cd AB |
272 | unsigned char buf[4]; |
273 | int res = 0; | |
1d6316f5 | 274 | |
10b06b87 | 275 | if (priv->irq <= 0) |
1d6316f5 RG |
276 | return -EINVAL; |
277 | ||
0680a6cd | 278 | res = regmap_update_bits(priv->map, DS1343_CONTROL_REG, DS1343_A0IE, 0); |
1d6316f5 | 279 | if (res) |
a9864290 | 280 | return res; |
1d6316f5 | 281 | |
0680a6cd AB |
282 | buf[0] = bin2bcd(alarm->time.tm_sec); |
283 | buf[1] = bin2bcd(alarm->time.tm_min); | |
284 | buf[2] = bin2bcd(alarm->time.tm_hour); | |
285 | buf[3] = bin2bcd(alarm->time.tm_mday); | |
1d6316f5 | 286 | |
0680a6cd AB |
287 | res = regmap_bulk_write(priv->map, DS1343_ALM0_SEC_REG, buf, 4); |
288 | if (res) | |
289 | return res; | |
1d6316f5 RG |
290 | |
291 | if (alarm->enabled) | |
0680a6cd AB |
292 | res = regmap_update_bits(priv->map, DS1343_CONTROL_REG, |
293 | DS1343_A0IE, DS1343_A0IE); | |
1d6316f5 | 294 | |
0680a6cd | 295 | return res; |
1d6316f5 RG |
296 | } |
297 | ||
298 | static int ds1343_alarm_irq_enable(struct device *dev, unsigned int enabled) | |
299 | { | |
300 | struct ds1343_priv *priv = dev_get_drvdata(dev); | |
1d6316f5 | 301 | |
10b06b87 | 302 | if (priv->irq <= 0) |
1d6316f5 RG |
303 | return -EINVAL; |
304 | ||
0680a6cd AB |
305 | return regmap_update_bits(priv->map, DS1343_CONTROL_REG, |
306 | DS1343_A0IE, enabled ? DS1343_A0IE : 0); | |
1d6316f5 RG |
307 | } |
308 | ||
309 | static irqreturn_t ds1343_thread(int irq, void *dev_id) | |
310 | { | |
311 | struct ds1343_priv *priv = dev_id; | |
0680a6cd | 312 | unsigned int stat; |
1d6316f5 RG |
313 | int res = 0; |
314 | ||
a9864290 | 315 | rtc_lock(priv->rtc); |
1d6316f5 RG |
316 | |
317 | res = regmap_read(priv->map, DS1343_STATUS_REG, &stat); | |
318 | if (res) | |
319 | goto out; | |
320 | ||
321 | if (stat & DS1343_IRQF0) { | |
322 | stat &= ~DS1343_IRQF0; | |
323 | regmap_write(priv->map, DS1343_STATUS_REG, stat); | |
324 | ||
1d6316f5 | 325 | rtc_update_irq(priv->rtc, 1, RTC_AF | RTC_IRQF); |
0680a6cd AB |
326 | |
327 | regmap_update_bits(priv->map, DS1343_CONTROL_REG, | |
328 | DS1343_A0IE, 0); | |
1d6316f5 RG |
329 | } |
330 | ||
331 | out: | |
a9864290 | 332 | rtc_unlock(priv->rtc); |
1d6316f5 RG |
333 | return IRQ_HANDLED; |
334 | } | |
335 | ||
336 | static const struct rtc_class_ops ds1343_rtc_ops = { | |
1d6316f5 RG |
337 | .read_time = ds1343_read_time, |
338 | .set_time = ds1343_set_time, | |
339 | .read_alarm = ds1343_read_alarm, | |
340 | .set_alarm = ds1343_set_alarm, | |
341 | .alarm_irq_enable = ds1343_alarm_irq_enable, | |
342 | }; | |
343 | ||
344 | static int ds1343_probe(struct spi_device *spi) | |
345 | { | |
346 | struct ds1343_priv *priv; | |
b5086150 AB |
347 | struct regmap_config config = { .reg_bits = 8, .val_bits = 8, |
348 | .write_flag_mask = 0x80, }; | |
1d6316f5 RG |
349 | unsigned int data; |
350 | int res; | |
d7501f70 AB |
351 | struct nvmem_config nvmem_cfg = { |
352 | .name = "ds1343-", | |
353 | .word_size = 1, | |
354 | .stride = 1, | |
355 | .size = DS1343_NVRAM_LEN, | |
356 | .reg_read = ds1343_nvram_read, | |
357 | .reg_write = ds1343_nvram_write, | |
358 | }; | |
1d6316f5 | 359 | |
1d6316f5 RG |
360 | priv = devm_kzalloc(&spi->dev, sizeof(struct ds1343_priv), GFP_KERNEL); |
361 | if (!priv) | |
362 | return -ENOMEM; | |
363 | ||
1d6316f5 | 364 | /* RTC DS1347 works in spi mode 3 and |
3b52093d LW |
365 | * its chip select is active high. Active high should be defined as |
366 | * "inverse polarity" as GPIO-based chip selects can be logically | |
367 | * active high but inverted by the GPIO library. | |
1d6316f5 | 368 | */ |
3b52093d LW |
369 | spi->mode |= SPI_MODE_3; |
370 | spi->mode ^= SPI_CS_HIGH; | |
1d6316f5 RG |
371 | spi->bits_per_word = 8; |
372 | res = spi_setup(spi); | |
373 | if (res) | |
374 | return res; | |
375 | ||
376 | spi_set_drvdata(spi, priv); | |
377 | ||
378 | priv->map = devm_regmap_init_spi(spi, &config); | |
379 | ||
380 | if (IS_ERR(priv->map)) { | |
381 | dev_err(&spi->dev, "spi regmap init failed for rtc ds1343\n"); | |
382 | return PTR_ERR(priv->map); | |
383 | } | |
384 | ||
385 | res = regmap_read(priv->map, DS1343_SECONDS_REG, &data); | |
386 | if (res) | |
387 | return res; | |
388 | ||
389 | regmap_read(priv->map, DS1343_CONTROL_REG, &data); | |
390 | data |= DS1343_INTCN; | |
391 | data &= ~(DS1343_EOSC | DS1343_A1IE | DS1343_A0IE); | |
392 | regmap_write(priv->map, DS1343_CONTROL_REG, data); | |
393 | ||
394 | regmap_read(priv->map, DS1343_STATUS_REG, &data); | |
395 | data &= ~(DS1343_OSF | DS1343_IRQF1 | DS1343_IRQF0); | |
396 | regmap_write(priv->map, DS1343_STATUS_REG, data); | |
397 | ||
1536f6dc AB |
398 | priv->rtc = devm_rtc_allocate_device(&spi->dev); |
399 | if (IS_ERR(priv->rtc)) | |
1d6316f5 | 400 | return PTR_ERR(priv->rtc); |
1536f6dc AB |
401 | |
402 | priv->rtc->ops = &ds1343_rtc_ops; | |
21783322 AB |
403 | priv->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; |
404 | priv->rtc->range_max = RTC_TIMESTAMP_END_2099; | |
1536f6dc | 405 | |
580daaf4 AB |
406 | res = rtc_add_group(priv->rtc, &ds1343_attr_group); |
407 | if (res) | |
408 | dev_err(&spi->dev, | |
409 | "unable to create sysfs entries for rtc ds1343\n"); | |
410 | ||
fdcfd854 | 411 | res = devm_rtc_register_device(priv->rtc); |
1536f6dc AB |
412 | if (res) |
413 | return res; | |
1d6316f5 | 414 | |
d7501f70 | 415 | nvmem_cfg.priv = priv; |
3a905c2d | 416 | devm_rtc_nvmem_register(priv->rtc, &nvmem_cfg); |
d7501f70 | 417 | |
10b06b87 RG |
418 | priv->irq = spi->irq; |
419 | ||
420 | if (priv->irq >= 0) { | |
1d6316f5 | 421 | res = devm_request_threaded_irq(&spi->dev, spi->irq, NULL, |
caff0cc4 | 422 | ds1343_thread, IRQF_ONESHOT, |
1d6316f5 RG |
423 | "ds1343", priv); |
424 | if (res) { | |
10b06b87 | 425 | priv->irq = -1; |
1d6316f5 RG |
426 | dev_err(&spi->dev, |
427 | "unable to request irq for rtc ds1343\n"); | |
10b06b87 | 428 | } else { |
caff0cc4 SH |
429 | device_init_wakeup(&spi->dev, true); |
430 | dev_pm_set_wake_irq(&spi->dev, spi->irq); | |
1d6316f5 | 431 | } |
1d6316f5 RG |
432 | } |
433 | ||
1d6316f5 RG |
434 | return 0; |
435 | } | |
436 | ||
a0386bba | 437 | static void ds1343_remove(struct spi_device *spi) |
1d6316f5 | 438 | { |
05df5572 | 439 | dev_pm_clear_wake_irq(&spi->dev); |
1d6316f5 RG |
440 | } |
441 | ||
442 | #ifdef CONFIG_PM_SLEEP | |
443 | ||
444 | static int ds1343_suspend(struct device *dev) | |
445 | { | |
446 | struct spi_device *spi = to_spi_device(dev); | |
447 | ||
448 | if (spi->irq >= 0 && device_may_wakeup(dev)) | |
449 | enable_irq_wake(spi->irq); | |
450 | ||
451 | return 0; | |
452 | } | |
453 | ||
454 | static int ds1343_resume(struct device *dev) | |
455 | { | |
456 | struct spi_device *spi = to_spi_device(dev); | |
457 | ||
458 | if (spi->irq >= 0 && device_may_wakeup(dev)) | |
459 | disable_irq_wake(spi->irq); | |
460 | ||
461 | return 0; | |
462 | } | |
463 | ||
464 | #endif | |
465 | ||
466 | static SIMPLE_DEV_PM_OPS(ds1343_pm, ds1343_suspend, ds1343_resume); | |
467 | ||
468 | static struct spi_driver ds1343_driver = { | |
469 | .driver = { | |
470 | .name = "ds1343", | |
1d6316f5 RG |
471 | .pm = &ds1343_pm, |
472 | }, | |
473 | .probe = ds1343_probe, | |
474 | .remove = ds1343_remove, | |
475 | .id_table = ds1343_id, | |
476 | }; | |
477 | ||
478 | module_spi_driver(ds1343_driver); | |
479 | ||
480 | MODULE_DESCRIPTION("DS1343 RTC SPI Driver"); | |
571eb883 RG |
481 | MODULE_AUTHOR("Raghavendra Chandra Ganiga <ravi23ganiga@gmail.com>," |
482 | "Ankur Srivastava <sankurece@gmail.com>"); | |
1d6316f5 | 483 | MODULE_LICENSE("GPL v2"); |