1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2012 Avionic Design GmbH |
4 | */ |
5 | |
6 | #include <linux/bcd.h> |
7 | #include <linux/bitfield.h> |
8 | #include <linux/i2c.h> |
9 | #include <linux/module.h> |
10 | #include <linux/regmap.h> |
11 | #include <linux/rtc.h> |
12 | #include <linux/of.h> |
13 | #include <linux/pm_wakeirq.h> |
14 | |
15 | #define PCF8523_REG_CONTROL1 0x00 |
16 | #define PCF8523_CONTROL1_CAP_SEL BIT(7) |
17 | #define PCF8523_CONTROL1_STOP BIT(5) |
18 | #define PCF8523_CONTROL1_AIE BIT(1) |
19 | |
20 | #define PCF8523_REG_CONTROL2 0x01 |
21 | #define PCF8523_CONTROL2_AF BIT(3) |
22 | |
23 | #define PCF8523_REG_CONTROL3 0x02 |
24 | #define PCF8523_CONTROL3_PM GENMASK(7, 5) |
25 | #define PCF8523_PM_STANDBY 0x7 |
26 | #define PCF8523_CONTROL3_BLF BIT(2) /* battery low bit, read-only */ |
27 | #define PCF8523_CONTROL3_BSF BIT(3) |
28 | |
29 | #define PCF8523_REG_SECONDS 0x03 |
30 | #define PCF8523_SECONDS_OS BIT(7) |
31 | |
32 | #define PCF8523_REG_MINUTES 0x04 |
33 | #define PCF8523_REG_HOURS 0x05 |
34 | #define PCF8523_REG_DAYS 0x06 |
35 | #define PCF8523_REG_WEEKDAYS 0x07 |
36 | #define PCF8523_REG_MONTHS 0x08 |
37 | #define PCF8523_REG_YEARS 0x09 |
38 | |
39 | #define PCF8523_REG_MINUTE_ALARM 0x0a |
40 | #define PCF8523_REG_HOUR_ALARM 0x0b |
41 | #define PCF8523_REG_DAY_ALARM 0x0c |
42 | #define PCF8523_REG_WEEKDAY_ALARM 0x0d |
43 | #define ALARM_DIS BIT(7) |
44 | |
45 | #define PCF8523_REG_OFFSET 0x0e |
46 | #define PCF8523_OFFSET_MODE BIT(7) |
47 | |
48 | #define PCF8523_TMR_CLKOUT_CTRL 0x0f |
49 | |
50 | struct pcf8523 { |
51 | struct rtc_device *rtc; |
52 | struct regmap *regmap; |
53 | }; |
54 | |
55 | static int pcf8523_load_capacitance(struct pcf8523 *pcf8523, struct device_node *node) |
56 | { |
57 | u32 load, value = 0; |
58 | |
59 | load = 12500; |
60 | of_property_read_u32(np: node, propname: "quartz-load-femtofarads" , out_value: &load); |
61 | |
62 | switch (load) { |
63 | default: |
64 | dev_warn(&pcf8523->rtc->dev, "Unknown quartz-load-femtofarads value: %d. Assuming 12500" , |
65 | load); |
66 | fallthrough; |
67 | case 12500: |
68 | value = PCF8523_CONTROL1_CAP_SEL; |
69 | break; |
70 | case 7000: |
71 | break; |
72 | } |
73 | |
74 | return regmap_update_bits(map: pcf8523->regmap, PCF8523_REG_CONTROL1, |
75 | PCF8523_CONTROL1_CAP_SEL, val: value); |
76 | } |
77 | |
78 | static irqreturn_t pcf8523_irq(int irq, void *dev_id) |
79 | { |
80 | struct pcf8523 *pcf8523 = dev_id; |
81 | u32 value; |
82 | int err; |
83 | |
84 | err = regmap_read(map: pcf8523->regmap, PCF8523_REG_CONTROL2, val: &value); |
85 | if (err < 0) |
86 | return IRQ_HANDLED; |
87 | |
88 | if (value & PCF8523_CONTROL2_AF) { |
89 | value &= ~PCF8523_CONTROL2_AF; |
90 | regmap_write(map: pcf8523->regmap, PCF8523_REG_CONTROL2, val: value); |
91 | rtc_update_irq(rtc: pcf8523->rtc, num: 1, RTC_IRQF | RTC_AF); |
92 | |
93 | return IRQ_HANDLED; |
94 | } |
95 | |
96 | return IRQ_NONE; |
97 | } |
98 | |
99 | static int pcf8523_rtc_read_time(struct device *dev, struct rtc_time *tm) |
100 | { |
101 | struct pcf8523 *pcf8523 = dev_get_drvdata(dev); |
102 | u8 regs[10]; |
103 | int err; |
104 | |
105 | err = regmap_bulk_read(map: pcf8523->regmap, PCF8523_REG_CONTROL1, val: regs, |
106 | val_count: sizeof(regs)); |
107 | if (err < 0) |
108 | return err; |
109 | |
110 | if ((regs[0] & PCF8523_CONTROL1_STOP) || (regs[3] & PCF8523_SECONDS_OS)) |
111 | return -EINVAL; |
112 | |
113 | tm->tm_sec = bcd2bin(regs[3] & 0x7f); |
114 | tm->tm_min = bcd2bin(regs[4] & 0x7f); |
115 | tm->tm_hour = bcd2bin(regs[5] & 0x3f); |
116 | tm->tm_mday = bcd2bin(regs[6] & 0x3f); |
117 | tm->tm_wday = regs[7] & 0x7; |
118 | tm->tm_mon = bcd2bin(regs[8] & 0x1f) - 1; |
119 | tm->tm_year = bcd2bin(regs[9]) + 100; |
120 | |
121 | return 0; |
122 | } |
123 | |
124 | static int pcf8523_rtc_set_time(struct device *dev, struct rtc_time *tm) |
125 | { |
126 | struct pcf8523 *pcf8523 = dev_get_drvdata(dev); |
127 | u8 regs[7]; |
128 | int err; |
129 | |
130 | err = regmap_update_bits(map: pcf8523->regmap, PCF8523_REG_CONTROL1, |
131 | PCF8523_CONTROL1_STOP, PCF8523_CONTROL1_STOP); |
132 | if (err < 0) |
133 | return err; |
134 | |
135 | /* This will purposely overwrite PCF8523_SECONDS_OS */ |
136 | regs[0] = bin2bcd(tm->tm_sec); |
137 | regs[1] = bin2bcd(tm->tm_min); |
138 | regs[2] = bin2bcd(tm->tm_hour); |
139 | regs[3] = bin2bcd(tm->tm_mday); |
140 | regs[4] = tm->tm_wday; |
141 | regs[5] = bin2bcd(tm->tm_mon + 1); |
142 | regs[6] = bin2bcd(tm->tm_year - 100); |
143 | |
144 | err = regmap_bulk_write(map: pcf8523->regmap, PCF8523_REG_SECONDS, val: regs, |
145 | val_count: sizeof(regs)); |
146 | if (err < 0) { |
147 | /* |
148 | * If the time cannot be set, restart the RTC anyway. Note |
149 | * that errors are ignored if the RTC cannot be started so |
150 | * that we have a chance to propagate the original error. |
151 | */ |
152 | regmap_update_bits(map: pcf8523->regmap, PCF8523_REG_CONTROL1, |
153 | PCF8523_CONTROL1_STOP, val: 0); |
154 | return err; |
155 | } |
156 | |
157 | return regmap_update_bits(map: pcf8523->regmap, PCF8523_REG_CONTROL1, |
158 | PCF8523_CONTROL1_STOP, val: 0); |
159 | } |
160 | |
161 | static int pcf8523_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *tm) |
162 | { |
163 | struct pcf8523 *pcf8523 = dev_get_drvdata(dev); |
164 | u8 regs[4]; |
165 | u32 value; |
166 | int err; |
167 | |
168 | err = regmap_bulk_read(map: pcf8523->regmap, PCF8523_REG_MINUTE_ALARM, val: regs, |
169 | val_count: sizeof(regs)); |
170 | if (err < 0) |
171 | return err; |
172 | |
173 | tm->time.tm_sec = 0; |
174 | tm->time.tm_min = bcd2bin(regs[0] & 0x7F); |
175 | tm->time.tm_hour = bcd2bin(regs[1] & 0x3F); |
176 | tm->time.tm_mday = bcd2bin(regs[2] & 0x3F); |
177 | tm->time.tm_wday = bcd2bin(regs[3] & 0x7); |
178 | |
179 | err = regmap_read(map: pcf8523->regmap, PCF8523_REG_CONTROL1, val: &value); |
180 | if (err < 0) |
181 | return err; |
182 | tm->enabled = !!(value & PCF8523_CONTROL1_AIE); |
183 | |
184 | err = regmap_read(map: pcf8523->regmap, PCF8523_REG_CONTROL2, val: &value); |
185 | if (err < 0) |
186 | return err; |
187 | tm->pending = !!(value & PCF8523_CONTROL2_AF); |
188 | |
189 | return 0; |
190 | } |
191 | |
192 | static int pcf8523_irq_enable(struct device *dev, unsigned int enabled) |
193 | { |
194 | struct pcf8523 *pcf8523 = dev_get_drvdata(dev); |
195 | |
196 | return regmap_update_bits(map: pcf8523->regmap, PCF8523_REG_CONTROL1, |
197 | PCF8523_CONTROL1_AIE, val: enabled ? |
198 | PCF8523_CONTROL1_AIE : 0); |
199 | } |
200 | |
201 | static int pcf8523_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *tm) |
202 | { |
203 | struct pcf8523 *pcf8523 = dev_get_drvdata(dev); |
204 | u8 regs[5]; |
205 | int err; |
206 | |
207 | err = pcf8523_irq_enable(dev, enabled: 0); |
208 | if (err) |
209 | return err; |
210 | |
211 | err = regmap_write(map: pcf8523->regmap, PCF8523_REG_CONTROL2, val: 0); |
212 | if (err < 0) |
213 | return err; |
214 | |
215 | regs[0] = bin2bcd(tm->time.tm_min); |
216 | regs[1] = bin2bcd(tm->time.tm_hour); |
217 | regs[2] = bin2bcd(tm->time.tm_mday); |
218 | regs[3] = ALARM_DIS; |
219 | |
220 | err = regmap_bulk_write(map: pcf8523->regmap, PCF8523_REG_MINUTE_ALARM, val: regs, |
221 | val_count: sizeof(regs)); |
222 | if (err < 0) |
223 | return err; |
224 | |
225 | if (tm->enabled) |
226 | return pcf8523_irq_enable(dev, enabled: tm->enabled); |
227 | |
228 | return 0; |
229 | } |
230 | |
231 | static int pcf8523_param_get(struct device *dev, struct rtc_param *param) |
232 | { |
233 | struct pcf8523 *pcf8523 = dev_get_drvdata(dev); |
234 | int ret; |
235 | u32 value; |
236 | |
237 | switch (param->param) { |
238 | case RTC_PARAM_BACKUP_SWITCH_MODE: |
239 | ret = regmap_read(map: pcf8523->regmap, PCF8523_REG_CONTROL3, val: &value); |
240 | if (ret < 0) |
241 | return ret; |
242 | |
243 | value = FIELD_GET(PCF8523_CONTROL3_PM, value); |
244 | |
245 | switch (value) { |
246 | case 0x0: |
247 | case 0x4: |
248 | param->uvalue = RTC_BSM_LEVEL; |
249 | break; |
250 | case 0x1: |
251 | case 0x5: |
252 | param->uvalue = RTC_BSM_DIRECT; |
253 | break; |
254 | case PCF8523_PM_STANDBY: |
255 | param->uvalue = RTC_BSM_STANDBY; |
256 | break; |
257 | default: |
258 | param->uvalue = RTC_BSM_DISABLED; |
259 | } |
260 | |
261 | break; |
262 | |
263 | default: |
264 | return -EINVAL; |
265 | } |
266 | |
267 | return 0; |
268 | } |
269 | |
270 | static int pcf8523_param_set(struct device *dev, struct rtc_param *param) |
271 | { |
272 | struct pcf8523 *pcf8523 = dev_get_drvdata(dev); |
273 | u8 mode; |
274 | |
275 | switch (param->param) { |
276 | case RTC_PARAM_BACKUP_SWITCH_MODE: |
277 | switch (param->uvalue) { |
278 | case RTC_BSM_DISABLED: |
279 | mode = 0x2; |
280 | break; |
281 | case RTC_BSM_DIRECT: |
282 | mode = 0x1; |
283 | break; |
284 | case RTC_BSM_LEVEL: |
285 | mode = 0x0; |
286 | break; |
287 | case RTC_BSM_STANDBY: |
288 | mode = PCF8523_PM_STANDBY; |
289 | break; |
290 | default: |
291 | return -EINVAL; |
292 | } |
293 | |
294 | return regmap_update_bits(map: pcf8523->regmap, PCF8523_REG_CONTROL3, |
295 | PCF8523_CONTROL3_PM, |
296 | FIELD_PREP(PCF8523_CONTROL3_PM, mode)); |
297 | |
298 | break; |
299 | |
300 | default: |
301 | return -EINVAL; |
302 | } |
303 | |
304 | return 0; |
305 | } |
306 | |
307 | static int pcf8523_rtc_ioctl(struct device *dev, unsigned int cmd, |
308 | unsigned long arg) |
309 | { |
310 | struct pcf8523 *pcf8523 = dev_get_drvdata(dev); |
311 | unsigned int flags = 0; |
312 | u32 value; |
313 | int ret; |
314 | |
315 | switch (cmd) { |
316 | case RTC_VL_READ: |
317 | ret = regmap_read(map: pcf8523->regmap, PCF8523_REG_CONTROL3, val: &value); |
318 | if (ret < 0) |
319 | return ret; |
320 | |
321 | if (value & PCF8523_CONTROL3_BLF) |
322 | flags |= RTC_VL_BACKUP_LOW; |
323 | |
324 | ret = regmap_read(map: pcf8523->regmap, PCF8523_REG_SECONDS, val: &value); |
325 | if (ret < 0) |
326 | return ret; |
327 | |
328 | if (value & PCF8523_SECONDS_OS) |
329 | flags |= RTC_VL_DATA_INVALID; |
330 | |
331 | return put_user(flags, (unsigned int __user *)arg); |
332 | |
333 | default: |
334 | return -ENOIOCTLCMD; |
335 | } |
336 | } |
337 | |
338 | static int pcf8523_rtc_read_offset(struct device *dev, long *offset) |
339 | { |
340 | struct pcf8523 *pcf8523 = dev_get_drvdata(dev); |
341 | int err; |
342 | u32 value; |
343 | s8 val; |
344 | |
345 | err = regmap_read(map: pcf8523->regmap, PCF8523_REG_OFFSET, val: &value); |
346 | if (err < 0) |
347 | return err; |
348 | |
349 | /* sign extend the 7-bit offset value */ |
350 | val = value << 1; |
351 | *offset = (value & PCF8523_OFFSET_MODE ? 4069 : 4340) * (val >> 1); |
352 | |
353 | return 0; |
354 | } |
355 | |
356 | static int pcf8523_rtc_set_offset(struct device *dev, long offset) |
357 | { |
358 | struct pcf8523 *pcf8523 = dev_get_drvdata(dev); |
359 | long reg_m0, reg_m1; |
360 | u32 value; |
361 | |
362 | reg_m0 = clamp(DIV_ROUND_CLOSEST(offset, 4340), -64L, 63L); |
363 | reg_m1 = clamp(DIV_ROUND_CLOSEST(offset, 4069), -64L, 63L); |
364 | |
365 | if (abs(reg_m0 * 4340 - offset) < abs(reg_m1 * 4069 - offset)) |
366 | value = reg_m0 & 0x7f; |
367 | else |
368 | value = (reg_m1 & 0x7f) | PCF8523_OFFSET_MODE; |
369 | |
370 | return regmap_write(map: pcf8523->regmap, PCF8523_REG_OFFSET, val: value); |
371 | } |
372 | |
373 | static const struct rtc_class_ops pcf8523_rtc_ops = { |
374 | .read_time = pcf8523_rtc_read_time, |
375 | .set_time = pcf8523_rtc_set_time, |
376 | .read_alarm = pcf8523_rtc_read_alarm, |
377 | .set_alarm = pcf8523_rtc_set_alarm, |
378 | .alarm_irq_enable = pcf8523_irq_enable, |
379 | .ioctl = pcf8523_rtc_ioctl, |
380 | .read_offset = pcf8523_rtc_read_offset, |
381 | .set_offset = pcf8523_rtc_set_offset, |
382 | .param_get = pcf8523_param_get, |
383 | .param_set = pcf8523_param_set, |
384 | }; |
385 | |
386 | static const struct regmap_config regmap_config = { |
387 | .reg_bits = 8, |
388 | .val_bits = 8, |
389 | .max_register = 0x13, |
390 | }; |
391 | |
392 | static int pcf8523_probe(struct i2c_client *client) |
393 | { |
394 | struct pcf8523 *pcf8523; |
395 | struct rtc_device *rtc; |
396 | bool wakeup_source = false; |
397 | u32 value; |
398 | int err; |
399 | |
400 | if (!i2c_check_functionality(adap: client->adapter, I2C_FUNC_I2C)) |
401 | return -ENODEV; |
402 | |
403 | pcf8523 = devm_kzalloc(dev: &client->dev, size: sizeof(struct pcf8523), GFP_KERNEL); |
404 | if (!pcf8523) |
405 | return -ENOMEM; |
406 | |
407 | pcf8523->regmap = devm_regmap_init_i2c(client, ®map_config); |
408 | if (IS_ERR(ptr: pcf8523->regmap)) |
409 | return PTR_ERR(ptr: pcf8523->regmap); |
410 | |
411 | i2c_set_clientdata(client, data: pcf8523); |
412 | |
413 | rtc = devm_rtc_allocate_device(dev: &client->dev); |
414 | if (IS_ERR(ptr: rtc)) |
415 | return PTR_ERR(ptr: rtc); |
416 | pcf8523->rtc = rtc; |
417 | |
418 | err = pcf8523_load_capacitance(pcf8523, node: client->dev.of_node); |
419 | if (err < 0) |
420 | dev_warn(&client->dev, "failed to set xtal load capacitance: %d" , |
421 | err); |
422 | |
423 | err = regmap_read(map: pcf8523->regmap, PCF8523_REG_SECONDS, val: &value); |
424 | if (err < 0) |
425 | return err; |
426 | |
427 | if (value & PCF8523_SECONDS_OS) { |
428 | err = regmap_read(map: pcf8523->regmap, PCF8523_REG_CONTROL3, val: &value); |
429 | if (err < 0) |
430 | return err; |
431 | |
432 | if (FIELD_GET(PCF8523_CONTROL3_PM, value) == PCF8523_PM_STANDBY) { |
433 | err = regmap_write(map: pcf8523->regmap, PCF8523_REG_CONTROL3, |
434 | val: value & ~PCF8523_CONTROL3_PM); |
435 | if (err < 0) |
436 | return err; |
437 | } |
438 | } |
439 | |
440 | rtc->ops = &pcf8523_rtc_ops; |
441 | rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; |
442 | rtc->range_max = RTC_TIMESTAMP_END_2099; |
443 | set_bit(RTC_FEATURE_ALARM_RES_MINUTE, addr: rtc->features); |
444 | clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, addr: rtc->features); |
445 | |
446 | if (client->irq > 0) { |
447 | unsigned long irqflags = IRQF_TRIGGER_LOW; |
448 | |
449 | if (dev_fwnode(&client->dev)) |
450 | irqflags = 0; |
451 | |
452 | err = regmap_write(map: pcf8523->regmap, PCF8523_TMR_CLKOUT_CTRL, val: 0x38); |
453 | if (err < 0) |
454 | return err; |
455 | |
456 | err = devm_request_threaded_irq(dev: &client->dev, irq: client->irq, |
457 | NULL, thread_fn: pcf8523_irq, |
458 | IRQF_SHARED | IRQF_ONESHOT | irqflags, |
459 | devname: dev_name(dev: &rtc->dev), dev_id: pcf8523); |
460 | if (err) |
461 | return err; |
462 | |
463 | dev_pm_set_wake_irq(dev: &client->dev, irq: client->irq); |
464 | } |
465 | |
466 | wakeup_source = of_property_read_bool(np: client->dev.of_node, propname: "wakeup-source" ); |
467 | if (client->irq > 0 || wakeup_source) |
468 | device_init_wakeup(dev: &client->dev, enable: true); |
469 | |
470 | return devm_rtc_register_device(rtc); |
471 | } |
472 | |
473 | static const struct i2c_device_id pcf8523_id[] = { |
474 | { "pcf8523" , 0 }, |
475 | { } |
476 | }; |
477 | MODULE_DEVICE_TABLE(i2c, pcf8523_id); |
478 | |
479 | static const struct of_device_id pcf8523_of_match[] = { |
480 | { .compatible = "nxp,pcf8523" }, |
481 | { .compatible = "microcrystal,rv8523" }, |
482 | { } |
483 | }; |
484 | MODULE_DEVICE_TABLE(of, pcf8523_of_match); |
485 | |
486 | static struct i2c_driver pcf8523_driver = { |
487 | .driver = { |
488 | .name = "rtc-pcf8523" , |
489 | .of_match_table = pcf8523_of_match, |
490 | }, |
491 | .probe = pcf8523_probe, |
492 | .id_table = pcf8523_id, |
493 | }; |
494 | module_i2c_driver(pcf8523_driver); |
495 | |
496 | MODULE_AUTHOR("Thierry Reding <thierry.reding@avionic-design.de>" ); |
497 | MODULE_DESCRIPTION("NXP PCF8523 RTC driver" ); |
498 | MODULE_LICENSE("GPL v2" ); |
499 | |