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