Merge tag 'drm-msm-fixes-2023-03-09' of https://gitlab.freedesktop.org/drm/msm into...
[linux-block.git] / drivers / rtc / rtc-pm8xxx.c
CommitLineData
97fb5e8d 1// SPDX-License-Identifier: GPL-2.0-only
3ca04951
JH
2/*
3 * pm8xxx RTC driver
4 *
5 * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
6 * Copyright (c) 2023, Linaro Limited
9a9a54ad 7 */
5a418558 8#include <linux/of.h>
9a9a54ad 9#include <linux/module.h>
3ca04951 10#include <linux/nvmem-consumer.h>
9a9a54ad
AG
11#include <linux/init.h>
12#include <linux/rtc.h>
5d7dc4cf 13#include <linux/platform_device.h>
9a9a54ad 14#include <linux/pm.h>
b5bf5b28 15#include <linux/pm_wakeirq.h>
5d7dc4cf 16#include <linux/regmap.h>
9a9a54ad
AG
17#include <linux/slab.h>
18#include <linux/spinlock.h>
19
79dd7566
JH
20#include <asm/unaligned.h>
21
9a9a54ad
AG
22/* RTC_CTRL register bit fields */
23#define PM8xxx_RTC_ENABLE BIT(7)
9a9a54ad 24#define PM8xxx_RTC_ALARM_CLEAR BIT(0)
121f54ef 25#define PM8xxx_RTC_ALARM_ENABLE BIT(7)
9a9a54ad
AG
26
27#define NUM_8_BIT_RTC_REGS 0x4
28
c8d523a4
SV
29/**
30 * struct pm8xxx_rtc_regs - describe RTC registers per PMIC versions
3c332639
JH
31 * @ctrl: address of control register
32 * @write: base address of write registers
33 * @read: base address of read registers
34 * @alarm_ctrl: address of alarm control register
35 * @alarm_ctrl2: address of alarm control2 register
36 * @alarm_rw: base address of alarm read-write registers
37 * @alarm_en: alarm enable mask
c8d523a4
SV
38 */
39struct pm8xxx_rtc_regs {
40 unsigned int ctrl;
41 unsigned int write;
42 unsigned int read;
43 unsigned int alarm_ctrl;
44 unsigned int alarm_ctrl2;
45 unsigned int alarm_rw;
46 unsigned int alarm_en;
47};
48
9a9a54ad 49/**
3c332639
JH
50 * struct pm8xxx_rtc - RTC driver internal structure
51 * @rtc: RTC device
52 * @regmap: regmap used to access registers
53 * @allow_set_time: whether the time can be set
4727b58f 54 * @alarm_irq: alarm irq number
3c332639 55 * @regs: register description
a375510e 56 * @dev: device structure
3ca04951
JH
57 * @nvmem_cell: nvmem cell for offset
58 * @offset: offset from epoch in seconds
9a9a54ad
AG
59 */
60struct pm8xxx_rtc {
61 struct rtc_device *rtc;
5d7dc4cf 62 struct regmap *regmap;
5a418558 63 bool allow_set_time;
4727b58f 64 int alarm_irq;
c8d523a4 65 const struct pm8xxx_rtc_regs *regs;
a375510e 66 struct device *dev;
3ca04951
JH
67 struct nvmem_cell *nvmem_cell;
68 u32 offset;
9a9a54ad
AG
69};
70
3ca04951
JH
71static int pm8xxx_rtc_read_nvmem_offset(struct pm8xxx_rtc *rtc_dd)
72{
73 size_t len;
74 void *buf;
75 int rc;
76
77 buf = nvmem_cell_read(rtc_dd->nvmem_cell, &len);
78 if (IS_ERR(buf)) {
79 rc = PTR_ERR(buf);
80 dev_dbg(rtc_dd->dev, "failed to read nvmem offset: %d\n", rc);
81 return rc;
82 }
83
84 if (len != sizeof(u32)) {
85 dev_dbg(rtc_dd->dev, "unexpected nvmem cell size %zu\n", len);
86 kfree(buf);
87 return -EINVAL;
88 }
89
90 rtc_dd->offset = get_unaligned_le32(buf);
91
92 kfree(buf);
93
94 return 0;
95}
96
97static int pm8xxx_rtc_write_nvmem_offset(struct pm8xxx_rtc *rtc_dd, u32 offset)
98{
99 u8 buf[sizeof(u32)];
100 int rc;
101
102 put_unaligned_le32(offset, buf);
103
104 rc = nvmem_cell_write(rtc_dd->nvmem_cell, buf, sizeof(buf));
105 if (rc < 0) {
106 dev_dbg(rtc_dd->dev, "failed to write nvmem offset: %d\n", rc);
107 return rc;
108 }
109
110 return 0;
111}
112
113static int pm8xxx_rtc_read_offset(struct pm8xxx_rtc *rtc_dd)
114{
115 if (!rtc_dd->nvmem_cell)
116 return 0;
117
118 return pm8xxx_rtc_read_nvmem_offset(rtc_dd);
119}
120
da862c3d
JH
121static int pm8xxx_rtc_read_raw(struct pm8xxx_rtc *rtc_dd, u32 *secs)
122{
123 const struct pm8xxx_rtc_regs *regs = rtc_dd->regs;
124 u8 value[NUM_8_BIT_RTC_REGS];
125 unsigned int reg;
126 int rc;
127
128 rc = regmap_bulk_read(rtc_dd->regmap, regs->read, value, sizeof(value));
129 if (rc)
130 return rc;
131
132 /*
133 * Read the LSB again and check if there has been a carry over.
134 * If there has, redo the read operation.
135 */
136 rc = regmap_read(rtc_dd->regmap, regs->read, &reg);
137 if (rc < 0)
138 return rc;
139
140 if (reg < value[0]) {
141 rc = regmap_bulk_read(rtc_dd->regmap, regs->read, value,
142 sizeof(value));
143 if (rc)
144 return rc;
145 }
146
147 *secs = get_unaligned_le32(value);
148
149 return 0;
150}
151
3ca04951
JH
152static int pm8xxx_rtc_update_offset(struct pm8xxx_rtc *rtc_dd, u32 secs)
153{
154 u32 raw_secs;
155 u32 offset;
156 int rc;
157
158 if (!rtc_dd->nvmem_cell)
159 return -ENODEV;
160
161 rc = pm8xxx_rtc_read_raw(rtc_dd, &raw_secs);
162 if (rc)
163 return rc;
164
165 offset = secs - raw_secs;
166
167 if (offset == rtc_dd->offset)
168 return 0;
169
170 rc = pm8xxx_rtc_write_nvmem_offset(rtc_dd, offset);
171 if (rc)
172 return rc;
173
174 rtc_dd->offset = offset;
175
176 return 0;
177}
178
9a9a54ad
AG
179/*
180 * Steps to write the RTC registers.
181 * 1. Disable alarm if enabled.
83220bf3
MA
182 * 2. Disable rtc if enabled.
183 * 3. Write 0x00 to LSB.
184 * 4. Write Byte[1], Byte[2], Byte[3] then Byte[0].
185 * 5. Enable rtc if disabled in step 2.
186 * 6. Enable alarm if disabled in step 1.
9a9a54ad 187 */
3ca04951 188static int __pm8xxx_rtc_set_time(struct pm8xxx_rtc *rtc_dd, u32 secs)
9a9a54ad 189{
c8d523a4 190 const struct pm8xxx_rtc_regs *regs = rtc_dd->regs;
182c23bb
JH
191 u8 value[NUM_8_BIT_RTC_REGS];
192 bool alarm_enabled;
79dd7566 193 int rc;
9a9a54ad 194
79dd7566 195 put_unaligned_le32(secs, value);
9a9a54ad 196
182c23bb
JH
197 rc = regmap_update_bits_check(rtc_dd->regmap, regs->alarm_ctrl,
198 regs->alarm_en, 0, &alarm_enabled);
c8d523a4 199 if (rc)
8d273f33 200 return rc;
c8d523a4 201
3c332639 202 /* Disable RTC */
182c23bb 203 rc = regmap_update_bits(rtc_dd->regmap, regs->ctrl, PM8xxx_RTC_ENABLE, 0);
83220bf3 204 if (rc)
8d273f33 205 return rc;
83220bf3 206
9a9a54ad 207 /* Write 0 to Byte[0] */
c8d523a4 208 rc = regmap_write(rtc_dd->regmap, regs->write, 0);
eb245631 209 if (rc)
8d273f33 210 return rc;
9a9a54ad
AG
211
212 /* Write Byte[1], Byte[2], Byte[3] */
c8d523a4 213 rc = regmap_bulk_write(rtc_dd->regmap, regs->write + 1,
5d7dc4cf 214 &value[1], sizeof(value) - 1);
eb245631 215 if (rc)
8d273f33 216 return rc;
9a9a54ad
AG
217
218 /* Write Byte[0] */
c8d523a4 219 rc = regmap_write(rtc_dd->regmap, regs->write, value[0]);
eb245631 220 if (rc)
8d273f33 221 return rc;
9a9a54ad 222
3c332639 223 /* Enable RTC */
182c23bb
JH
224 rc = regmap_update_bits(rtc_dd->regmap, regs->ctrl, PM8xxx_RTC_ENABLE,
225 PM8xxx_RTC_ENABLE);
226 if (rc)
8d273f33 227 return rc;
83220bf3 228
9a9a54ad 229 if (alarm_enabled) {
182c23bb
JH
230 rc = regmap_update_bits(rtc_dd->regmap, regs->alarm_ctrl,
231 regs->alarm_en, regs->alarm_en);
eb245631 232 if (rc)
8d273f33 233 return rc;
9a9a54ad
AG
234 }
235
8d273f33 236 return 0;
9a9a54ad
AG
237}
238
3ca04951
JH
239static int pm8xxx_rtc_set_time(struct device *dev, struct rtc_time *tm)
240{
241 struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
242 u32 secs;
243 int rc;
244
245 secs = rtc_tm_to_time64(tm);
246
247 if (rtc_dd->allow_set_time)
248 rc = __pm8xxx_rtc_set_time(rtc_dd, secs);
249 else
250 rc = pm8xxx_rtc_update_offset(rtc_dd, secs);
251
252 if (rc)
253 return rc;
254
255 dev_dbg(dev, "set time: %ptRd %ptRt (%u + %u)\n", tm, tm,
256 secs - rtc_dd->offset, rtc_dd->offset);
257 return 0;
258}
259
9a9a54ad
AG
260static int pm8xxx_rtc_read_time(struct device *dev, struct rtc_time *tm)
261{
9a9a54ad 262 struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
35d9c472 263 u32 secs;
da862c3d 264 int rc;
9a9a54ad 265
da862c3d 266 rc = pm8xxx_rtc_read_raw(rtc_dd, &secs);
eb245631 267 if (rc)
9a9a54ad 268 return rc;
9a9a54ad 269
3ca04951 270 secs += rtc_dd->offset;
4c470b2f 271 rtc_time64_to_tm(secs, tm);
9a9a54ad 272
3ca04951
JH
273 dev_dbg(dev, "read time: %ptRd %ptRt (%u + %u)\n", tm, tm,
274 secs - rtc_dd->offset, rtc_dd->offset);
9a9a54ad
AG
275 return 0;
276}
277
278static int pm8xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
279{
9a9a54ad 280 struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
c8d523a4 281 const struct pm8xxx_rtc_regs *regs = rtc_dd->regs;
9e5a7991 282 u8 value[NUM_8_BIT_RTC_REGS];
35d9c472 283 u32 secs;
79dd7566 284 int rc;
9a9a54ad 285
4c470b2f 286 secs = rtc_tm_to_time64(&alarm->time);
3ca04951 287 secs -= rtc_dd->offset;
79dd7566 288 put_unaligned_le32(secs, value);
9a9a54ad 289
c88db0ef
JH
290 rc = regmap_update_bits(rtc_dd->regmap, regs->alarm_ctrl,
291 regs->alarm_en, 0);
292 if (rc)
293 return rc;
294
c8d523a4 295 rc = regmap_bulk_write(rtc_dd->regmap, regs->alarm_rw, value,
5d7dc4cf 296 sizeof(value));
eb245631 297 if (rc)
8d273f33 298 return rc;
9a9a54ad 299
c88db0ef
JH
300 if (alarm->enabled) {
301 rc = regmap_update_bits(rtc_dd->regmap, regs->alarm_ctrl,
302 regs->alarm_en, regs->alarm_en);
303 if (rc)
8d273f33 304 return rc;
9a9a54ad
AG
305 }
306
c996956f 307 dev_dbg(dev, "set alarm: %ptRd %ptRt\n", &alarm->time, &alarm->time);
8d273f33
JH
308
309 return 0;
9a9a54ad
AG
310}
311
312static int pm8xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
313{
9a9a54ad 314 struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
c8d523a4 315 const struct pm8xxx_rtc_regs *regs = rtc_dd->regs;
9e5a7991
JH
316 u8 value[NUM_8_BIT_RTC_REGS];
317 unsigned int ctrl_reg;
35d9c472 318 u32 secs;
9e5a7991 319 int rc;
9a9a54ad 320
c8d523a4 321 rc = regmap_bulk_read(rtc_dd->regmap, regs->alarm_rw, value,
5d7dc4cf 322 sizeof(value));
eb245631 323 if (rc)
9a9a54ad 324 return rc;
9a9a54ad 325
79dd7566 326 secs = get_unaligned_le32(value);
3ca04951 327 secs += rtc_dd->offset;
4c470b2f 328 rtc_time64_to_tm(secs, &alarm->time);
9a9a54ad 329
121f54ef 330 rc = regmap_read(rtc_dd->regmap, regs->alarm_ctrl, &ctrl_reg);
eb245631 331 if (rc)
121f54ef 332 return rc;
eb245631 333
121f54ef
GW
334 alarm->enabled = !!(ctrl_reg & PM8xxx_RTC_ALARM_ENABLE);
335
c996956f 336 dev_dbg(dev, "read alarm: %ptRd %ptRt\n", &alarm->time, &alarm->time);
9a9a54ad
AG
337
338 return 0;
339}
340
341static int pm8xxx_rtc_alarm_irq_enable(struct device *dev, unsigned int enable)
342{
9a9a54ad 343 struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
c8d523a4 344 const struct pm8xxx_rtc_regs *regs = rtc_dd->regs;
34ce2977 345 u8 value[NUM_8_BIT_RTC_REGS] = {0};
182c23bb 346 unsigned int val;
9e5a7991 347 int rc;
9a9a54ad 348
5bed811d 349 if (enable)
182c23bb 350 val = regs->alarm_en;
5bed811d 351 else
182c23bb 352 val = 0;
9a9a54ad 353
182c23bb
JH
354 rc = regmap_update_bits(rtc_dd->regmap, regs->alarm_ctrl,
355 regs->alarm_en, val);
eb245631 356 if (rc)
8d273f33 357 return rc;
9a9a54ad 358
3c332639 359 /* Clear alarm register */
34ce2977 360 if (!enable) {
361 rc = regmap_bulk_write(rtc_dd->regmap, regs->alarm_rw, value,
362 sizeof(value));
eb245631 363 if (rc)
8d273f33 364 return rc;
34ce2977 365 }
366
8d273f33 367 return 0;
9a9a54ad
AG
368}
369
5a418558 370static const struct rtc_class_ops pm8xxx_rtc_ops = {
9a9a54ad 371 .read_time = pm8xxx_rtc_read_time,
5a418558 372 .set_time = pm8xxx_rtc_set_time,
9a9a54ad
AG
373 .set_alarm = pm8xxx_rtc_set_alarm,
374 .read_alarm = pm8xxx_rtc_read_alarm,
375 .alarm_irq_enable = pm8xxx_rtc_alarm_irq_enable,
376};
377
378static irqreturn_t pm8xxx_alarm_trigger(int irq, void *dev_id)
379{
380 struct pm8xxx_rtc *rtc_dd = dev_id;
c8d523a4 381 const struct pm8xxx_rtc_regs *regs = rtc_dd->regs;
9a9a54ad 382 int rc;
9a9a54ad
AG
383
384 rtc_update_irq(rtc_dd->rtc, 1, RTC_IRQF | RTC_AF);
385
3c332639 386 /* Disable alarm */
182c23bb
JH
387 rc = regmap_update_bits(rtc_dd->regmap, regs->alarm_ctrl,
388 regs->alarm_en, 0);
8d273f33 389 if (rc)
cb9bb7b2 390 return IRQ_NONE;
9a9a54ad 391
3c332639 392 /* Clear alarm status */
182c23bb
JH
393 rc = regmap_update_bits(rtc_dd->regmap, regs->alarm_ctrl2,
394 PM8xxx_RTC_ALARM_CLEAR, 0);
eb245631 395 if (rc)
cb9bb7b2
JH
396 return IRQ_NONE;
397
9a9a54ad
AG
398 return IRQ_HANDLED;
399}
400
c8d523a4
SV
401static int pm8xxx_rtc_enable(struct pm8xxx_rtc *rtc_dd)
402{
403 const struct pm8xxx_rtc_regs *regs = rtc_dd->regs;
c8d523a4 404
182c23bb
JH
405 return regmap_update_bits(rtc_dd->regmap, regs->ctrl, PM8xxx_RTC_ENABLE,
406 PM8xxx_RTC_ENABLE);
c8d523a4
SV
407}
408
409static const struct pm8xxx_rtc_regs pm8921_regs = {
410 .ctrl = 0x11d,
411 .write = 0x11f,
412 .read = 0x123,
413 .alarm_rw = 0x127,
414 .alarm_ctrl = 0x11d,
415 .alarm_ctrl2 = 0x11e,
416 .alarm_en = BIT(1),
417};
418
419static const struct pm8xxx_rtc_regs pm8058_regs = {
420 .ctrl = 0x1e8,
421 .write = 0x1ea,
422 .read = 0x1ee,
423 .alarm_rw = 0x1f2,
424 .alarm_ctrl = 0x1e8,
425 .alarm_ctrl2 = 0x1e9,
426 .alarm_en = BIT(1),
427};
428
429static const struct pm8xxx_rtc_regs pm8941_regs = {
430 .ctrl = 0x6046,
431 .write = 0x6040,
432 .read = 0x6048,
433 .alarm_rw = 0x6140,
434 .alarm_ctrl = 0x6146,
435 .alarm_ctrl2 = 0x6148,
436 .alarm_en = BIT(7),
437};
438
c8f0ca8b 439static const struct pm8xxx_rtc_regs pmk8350_regs = {
440 .ctrl = 0x6146,
441 .write = 0x6140,
442 .read = 0x6148,
443 .alarm_rw = 0x6240,
444 .alarm_ctrl = 0x6246,
445 .alarm_ctrl2 = 0x6248,
446 .alarm_en = BIT(7),
447};
448
5a418558 449static const struct of_device_id pm8xxx_id_table[] = {
c8d523a4
SV
450 { .compatible = "qcom,pm8921-rtc", .data = &pm8921_regs },
451 { .compatible = "qcom,pm8058-rtc", .data = &pm8058_regs },
452 { .compatible = "qcom,pm8941-rtc", .data = &pm8941_regs },
c8f0ca8b 453 { .compatible = "qcom,pmk8350-rtc", .data = &pmk8350_regs },
5a418558
JC
454 { },
455};
456MODULE_DEVICE_TABLE(of, pm8xxx_id_table);
457
5a167f45 458static int pm8xxx_rtc_probe(struct platform_device *pdev)
9a9a54ad 459{
5a418558 460 const struct of_device_id *match;
9e5a7991
JH
461 struct pm8xxx_rtc *rtc_dd;
462 int rc;
9a9a54ad 463
5a418558
JC
464 match = of_match_node(pm8xxx_id_table, pdev->dev.of_node);
465 if (!match)
466 return -ENXIO;
9a9a54ad 467
c417299c 468 rtc_dd = devm_kzalloc(&pdev->dev, sizeof(*rtc_dd), GFP_KERNEL);
49ae425b 469 if (rtc_dd == NULL)
9a9a54ad 470 return -ENOMEM;
9a9a54ad 471
5d7dc4cf 472 rtc_dd->regmap = dev_get_regmap(pdev->dev.parent, NULL);
c94fb939 473 if (!rtc_dd->regmap)
5d7dc4cf 474 return -ENXIO;
5d7dc4cf 475
4727b58f
JH
476 rtc_dd->alarm_irq = platform_get_irq(pdev, 0);
477 if (rtc_dd->alarm_irq < 0)
c417299c 478 return -ENXIO;
9a9a54ad 479
5a418558
JC
480 rtc_dd->allow_set_time = of_property_read_bool(pdev->dev.of_node,
481 "allow-set-time");
9a9a54ad 482
3ca04951
JH
483 rtc_dd->nvmem_cell = devm_nvmem_cell_get(&pdev->dev, "offset");
484 if (IS_ERR(rtc_dd->nvmem_cell)) {
485 rc = PTR_ERR(rtc_dd->nvmem_cell);
486 if (rc != -ENOENT)
487 return rc;
488 rtc_dd->nvmem_cell = NULL;
489 }
490
c8d523a4 491 rtc_dd->regs = match->data;
a375510e 492 rtc_dd->dev = &pdev->dev;
9a9a54ad 493
3ca04951
JH
494 if (!rtc_dd->allow_set_time) {
495 rc = pm8xxx_rtc_read_offset(rtc_dd);
496 if (rc)
497 return rc;
498 }
499
c8d523a4
SV
500 rc = pm8xxx_rtc_enable(rtc_dd);
501 if (rc)
c417299c 502 return rc;
9a9a54ad
AG
503
504 platform_set_drvdata(pdev, rtc_dd);
505
fda9909d
JC
506 device_init_wakeup(&pdev->dev, 1);
507
d5d55b70
AB
508 rtc_dd->rtc = devm_rtc_allocate_device(&pdev->dev);
509 if (IS_ERR(rtc_dd->rtc))
c417299c 510 return PTR_ERR(rtc_dd->rtc);
d5d55b70
AB
511
512 rtc_dd->rtc->ops = &pm8xxx_rtc_ops;
3cfe5260 513 rtc_dd->rtc->range_max = U32_MAX;
9a9a54ad 514
4727b58f 515 rc = devm_request_any_context_irq(&pdev->dev, rtc_dd->alarm_irq,
bffcbc08
JC
516 pm8xxx_alarm_trigger,
517 IRQF_TRIGGER_RISING,
518 "pm8xxx_rtc_alarm", rtc_dd);
c94fb939 519 if (rc < 0)
c417299c 520 return rc;
9a9a54ad 521
b5bf5b28
LP
522 rc = devm_rtc_register_device(rtc_dd->rtc);
523 if (rc)
524 return rc;
9a9a54ad 525
4727b58f 526 rc = dev_pm_set_wake_irq(&pdev->dev, rtc_dd->alarm_irq);
b5bf5b28
LP
527 if (rc)
528 return rc;
9a9a54ad
AG
529
530 return 0;
531}
532
b5bf5b28 533static int pm8xxx_remove(struct platform_device *pdev)
9a9a54ad 534{
b5bf5b28 535 dev_pm_clear_wake_irq(&pdev->dev);
9a9a54ad
AG
536 return 0;
537}
9a9a54ad
AG
538
539static struct platform_driver pm8xxx_rtc_driver = {
540 .probe = pm8xxx_rtc_probe,
b5bf5b28 541 .remove = pm8xxx_remove,
9a9a54ad 542 .driver = {
5a418558 543 .name = "rtc-pm8xxx",
5a418558 544 .of_match_table = pm8xxx_id_table,
9a9a54ad
AG
545 },
546};
547
0c4eae66 548module_platform_driver(pm8xxx_rtc_driver);
9a9a54ad
AG
549
550MODULE_ALIAS("platform:rtc-pm8xxx");
551MODULE_DESCRIPTION("PMIC8xxx RTC driver");
552MODULE_LICENSE("GPL v2");
553MODULE_AUTHOR("Anirudh Ghayal <aghayal@codeaurora.org>");
3ca04951 554MODULE_AUTHOR("Johan Hovold <johan@kernel.org>");