]>
Commit | Line | Data |
---|---|---|
32a4a4eb MV |
1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | // | |
3 | // Copyright (C) 2018 ROHM Semiconductors | |
4 | // | |
5 | // RTC driver for ROHM BD70528 PMIC | |
6 | ||
7 | #include <linux/bcd.h> | |
8 | #include <linux/mfd/rohm-bd70528.h> | |
fe5a591b | 9 | #include <linux/mfd/rohm-bd71828.h> |
32a4a4eb MV |
10 | #include <linux/module.h> |
11 | #include <linux/of.h> | |
12 | #include <linux/platform_device.h> | |
13 | #include <linux/regmap.h> | |
14 | #include <linux/rtc.h> | |
15 | ||
16 | /* | |
17 | * We read regs RTC_SEC => RTC_YEAR | |
18 | * this struct is ordered according to chip registers. | |
fe5a591b | 19 | * Keep it u8 only (or packed) to avoid padding issues. |
32a4a4eb MV |
20 | */ |
21 | struct bd70528_rtc_day { | |
22 | u8 sec; | |
23 | u8 min; | |
24 | u8 hour; | |
25 | } __packed; | |
26 | ||
27 | struct bd70528_rtc_data { | |
28 | struct bd70528_rtc_day time; | |
29 | u8 week; | |
30 | u8 day; | |
31 | u8 month; | |
32 | u8 year; | |
33 | } __packed; | |
34 | ||
35 | struct bd70528_rtc_wake { | |
36 | struct bd70528_rtc_day time; | |
37 | u8 ctrl; | |
38 | } __packed; | |
39 | ||
fe5a591b MV |
40 | struct bd71828_rtc_alm { |
41 | struct bd70528_rtc_data alm0; | |
42 | struct bd70528_rtc_data alm1; | |
43 | u8 alm_mask; | |
44 | u8 alm1_mask; | |
45 | } __packed; | |
46 | ||
32a4a4eb MV |
47 | struct bd70528_rtc_alm { |
48 | struct bd70528_rtc_data data; | |
49 | u8 alm_mask; | |
50 | u8 alm_repeat; | |
51 | } __packed; | |
52 | ||
53 | struct bd70528_rtc { | |
fe5a591b | 54 | struct rohm_regmap_dev *parent; |
32a4a4eb | 55 | struct device *dev; |
fe5a591b MV |
56 | u8 reg_time_start; |
57 | bool has_rtc_timers; | |
32a4a4eb MV |
58 | }; |
59 | ||
60 | static int bd70528_set_wake(struct rohm_regmap_dev *bd70528, | |
61 | int enable, int *old_state) | |
62 | { | |
63 | int ret; | |
64 | unsigned int ctrl_reg; | |
65 | ||
66 | ret = regmap_read(bd70528->regmap, BD70528_REG_WAKE_EN, &ctrl_reg); | |
67 | if (ret) | |
68 | return ret; | |
69 | ||
70 | if (old_state) { | |
71 | if (ctrl_reg & BD70528_MASK_WAKE_EN) | |
72 | *old_state |= BD70528_WAKE_STATE_BIT; | |
73 | else | |
74 | *old_state &= ~BD70528_WAKE_STATE_BIT; | |
75 | ||
76 | if (!enable == !(*old_state & BD70528_WAKE_STATE_BIT)) | |
77 | return 0; | |
78 | } | |
79 | ||
80 | if (enable) | |
81 | ctrl_reg |= BD70528_MASK_WAKE_EN; | |
82 | else | |
83 | ctrl_reg &= ~BD70528_MASK_WAKE_EN; | |
84 | ||
85 | return regmap_write(bd70528->regmap, BD70528_REG_WAKE_EN, | |
86 | ctrl_reg); | |
87 | } | |
88 | ||
89 | static int bd70528_set_elapsed_tmr(struct rohm_regmap_dev *bd70528, | |
90 | int enable, int *old_state) | |
91 | { | |
92 | int ret; | |
93 | unsigned int ctrl_reg; | |
94 | ||
95 | /* | |
96 | * TBD | |
97 | * What is the purpose of elapsed timer ? | |
98 | * Is the timeout registers counting down, or is the disable - re-enable | |
99 | * going to restart the elapsed-time counting? If counting is restarted | |
100 | * the timeout should be decreased by the amount of time that has | |
101 | * elapsed since starting the timer. Maybe we should store the monotonic | |
102 | * clock value when timer is started so that if RTC is set while timer | |
103 | * is armed we could do the compensation. This is a hack if RTC/system | |
104 | * clk are drifting. OTOH, RTC controlled via I2C is in any case | |
105 | * inaccurate... | |
106 | */ | |
107 | ret = regmap_read(bd70528->regmap, BD70528_REG_ELAPSED_TIMER_EN, | |
108 | &ctrl_reg); | |
109 | if (ret) | |
110 | return ret; | |
111 | ||
112 | if (old_state) { | |
113 | if (ctrl_reg & BD70528_MASK_ELAPSED_TIMER_EN) | |
114 | *old_state |= BD70528_ELAPSED_STATE_BIT; | |
115 | else | |
116 | *old_state &= ~BD70528_ELAPSED_STATE_BIT; | |
117 | ||
118 | if ((!enable) == (!(*old_state & BD70528_ELAPSED_STATE_BIT))) | |
119 | return 0; | |
120 | } | |
121 | ||
122 | if (enable) | |
123 | ctrl_reg |= BD70528_MASK_ELAPSED_TIMER_EN; | |
124 | else | |
125 | ctrl_reg &= ~BD70528_MASK_ELAPSED_TIMER_EN; | |
126 | ||
127 | return regmap_write(bd70528->regmap, BD70528_REG_ELAPSED_TIMER_EN, | |
128 | ctrl_reg); | |
129 | } | |
130 | ||
131 | static int bd70528_set_rtc_based_timers(struct bd70528_rtc *r, int new_state, | |
132 | int *old_state) | |
133 | { | |
134 | int ret; | |
135 | ||
fe5a591b | 136 | ret = bd70528_wdt_set(r->parent, new_state & BD70528_WDT_STATE_BIT, |
32a4a4eb MV |
137 | old_state); |
138 | if (ret) { | |
139 | dev_err(r->dev, | |
140 | "Failed to disable WDG for RTC setting (%d)\n", ret); | |
141 | return ret; | |
142 | } | |
fe5a591b | 143 | ret = bd70528_set_elapsed_tmr(r->parent, |
32a4a4eb MV |
144 | new_state & BD70528_ELAPSED_STATE_BIT, |
145 | old_state); | |
146 | if (ret) { | |
147 | dev_err(r->dev, | |
148 | "Failed to disable 'elapsed timer' for RTC setting\n"); | |
149 | return ret; | |
150 | } | |
fe5a591b | 151 | ret = bd70528_set_wake(r->parent, new_state & BD70528_WAKE_STATE_BIT, |
32a4a4eb MV |
152 | old_state); |
153 | if (ret) { | |
154 | dev_err(r->dev, | |
155 | "Failed to disable 'wake timer' for RTC setting\n"); | |
156 | return ret; | |
157 | } | |
158 | ||
159 | return ret; | |
160 | } | |
161 | ||
162 | static int bd70528_re_enable_rtc_based_timers(struct bd70528_rtc *r, | |
163 | int old_state) | |
164 | { | |
fe5a591b MV |
165 | if (!r->has_rtc_timers) |
166 | return 0; | |
167 | ||
32a4a4eb MV |
168 | return bd70528_set_rtc_based_timers(r, old_state, NULL); |
169 | } | |
170 | ||
171 | static int bd70528_disable_rtc_based_timers(struct bd70528_rtc *r, | |
172 | int *old_state) | |
173 | { | |
fe5a591b MV |
174 | if (!r->has_rtc_timers) |
175 | return 0; | |
176 | ||
32a4a4eb MV |
177 | return bd70528_set_rtc_based_timers(r, 0, old_state); |
178 | } | |
179 | ||
180 | static inline void tmday2rtc(struct rtc_time *t, struct bd70528_rtc_day *d) | |
181 | { | |
182 | d->sec &= ~BD70528_MASK_RTC_SEC; | |
183 | d->min &= ~BD70528_MASK_RTC_MINUTE; | |
184 | d->hour &= ~BD70528_MASK_RTC_HOUR; | |
185 | d->sec |= bin2bcd(t->tm_sec); | |
186 | d->min |= bin2bcd(t->tm_min); | |
187 | d->hour |= bin2bcd(t->tm_hour); | |
188 | } | |
189 | ||
190 | static inline void tm2rtc(struct rtc_time *t, struct bd70528_rtc_data *r) | |
191 | { | |
192 | r->day &= ~BD70528_MASK_RTC_DAY; | |
193 | r->week &= ~BD70528_MASK_RTC_WEEK; | |
194 | r->month &= ~BD70528_MASK_RTC_MONTH; | |
195 | /* | |
196 | * PM and 24H bits are not used by Wake - thus we clear them | |
197 | * here and not in tmday2rtc() which is also used by wake. | |
198 | */ | |
199 | r->time.hour &= ~(BD70528_MASK_RTC_HOUR_PM | BD70528_MASK_RTC_HOUR_24H); | |
200 | ||
201 | tmday2rtc(t, &r->time); | |
202 | /* | |
203 | * We do always set time in 24H mode. | |
204 | */ | |
205 | r->time.hour |= BD70528_MASK_RTC_HOUR_24H; | |
206 | r->day |= bin2bcd(t->tm_mday); | |
207 | r->week |= bin2bcd(t->tm_wday); | |
208 | r->month |= bin2bcd(t->tm_mon + 1); | |
209 | r->year = bin2bcd(t->tm_year - 100); | |
210 | } | |
211 | ||
212 | static inline void rtc2tm(struct bd70528_rtc_data *r, struct rtc_time *t) | |
213 | { | |
214 | t->tm_sec = bcd2bin(r->time.sec & BD70528_MASK_RTC_SEC); | |
215 | t->tm_min = bcd2bin(r->time.min & BD70528_MASK_RTC_MINUTE); | |
216 | t->tm_hour = bcd2bin(r->time.hour & BD70528_MASK_RTC_HOUR); | |
217 | /* | |
218 | * If RTC is in 12H mode, then bit BD70528_MASK_RTC_HOUR_PM | |
219 | * is not BCD value but tells whether it is AM or PM | |
220 | */ | |
221 | if (!(r->time.hour & BD70528_MASK_RTC_HOUR_24H)) { | |
222 | t->tm_hour %= 12; | |
223 | if (r->time.hour & BD70528_MASK_RTC_HOUR_PM) | |
224 | t->tm_hour += 12; | |
225 | } | |
226 | t->tm_mday = bcd2bin(r->day & BD70528_MASK_RTC_DAY); | |
227 | t->tm_mon = bcd2bin(r->month & BD70528_MASK_RTC_MONTH) - 1; | |
228 | t->tm_year = 100 + bcd2bin(r->year & BD70528_MASK_RTC_YEAR); | |
229 | t->tm_wday = bcd2bin(r->week & BD70528_MASK_RTC_WEEK); | |
230 | } | |
231 | ||
fe5a591b MV |
232 | static int bd71828_set_alarm(struct device *dev, struct rtc_wkalrm *a) |
233 | { | |
234 | int ret; | |
235 | struct bd71828_rtc_alm alm; | |
236 | struct bd70528_rtc *r = dev_get_drvdata(dev); | |
237 | struct rohm_regmap_dev *parent = r->parent; | |
238 | ||
239 | ret = regmap_bulk_read(parent->regmap, BD71828_REG_RTC_ALM_START, | |
240 | &alm, sizeof(alm)); | |
241 | if (ret) { | |
242 | dev_err(dev, "Failed to read alarm regs\n"); | |
243 | return ret; | |
244 | } | |
245 | ||
246 | tm2rtc(&a->time, &alm.alm0); | |
247 | ||
248 | if (!a->enabled) | |
249 | alm.alm_mask &= ~BD70528_MASK_ALM_EN; | |
250 | else | |
251 | alm.alm_mask |= BD70528_MASK_ALM_EN; | |
252 | ||
253 | ret = regmap_bulk_write(parent->regmap, BD71828_REG_RTC_ALM_START, | |
254 | &alm, sizeof(alm)); | |
255 | if (ret) | |
256 | dev_err(dev, "Failed to set alarm time\n"); | |
257 | ||
258 | return ret; | |
259 | ||
260 | } | |
261 | ||
32a4a4eb MV |
262 | static int bd70528_set_alarm(struct device *dev, struct rtc_wkalrm *a) |
263 | { | |
264 | struct bd70528_rtc_wake wake; | |
265 | struct bd70528_rtc_alm alm; | |
266 | int ret; | |
267 | struct bd70528_rtc *r = dev_get_drvdata(dev); | |
fe5a591b | 268 | struct rohm_regmap_dev *parent = r->parent; |
32a4a4eb | 269 | |
fe5a591b | 270 | ret = regmap_bulk_read(parent->regmap, BD70528_REG_RTC_WAKE_START, |
32a4a4eb MV |
271 | &wake, sizeof(wake)); |
272 | if (ret) { | |
273 | dev_err(dev, "Failed to read wake regs\n"); | |
274 | return ret; | |
275 | } | |
276 | ||
fe5a591b | 277 | ret = regmap_bulk_read(parent->regmap, BD70528_REG_RTC_ALM_START, |
32a4a4eb MV |
278 | &alm, sizeof(alm)); |
279 | if (ret) { | |
280 | dev_err(dev, "Failed to read alarm regs\n"); | |
281 | return ret; | |
282 | } | |
283 | ||
284 | tm2rtc(&a->time, &alm.data); | |
285 | tmday2rtc(&a->time, &wake.time); | |
286 | ||
287 | if (a->enabled) { | |
288 | alm.alm_mask &= ~BD70528_MASK_ALM_EN; | |
289 | wake.ctrl |= BD70528_MASK_WAKE_EN; | |
290 | } else { | |
291 | alm.alm_mask |= BD70528_MASK_ALM_EN; | |
292 | wake.ctrl &= ~BD70528_MASK_WAKE_EN; | |
293 | } | |
294 | ||
fe5a591b | 295 | ret = regmap_bulk_write(parent->regmap, |
32a4a4eb MV |
296 | BD70528_REG_RTC_WAKE_START, &wake, |
297 | sizeof(wake)); | |
298 | if (ret) { | |
299 | dev_err(dev, "Failed to set wake time\n"); | |
300 | return ret; | |
301 | } | |
fe5a591b | 302 | ret = regmap_bulk_write(parent->regmap, BD70528_REG_RTC_ALM_START, |
32a4a4eb MV |
303 | &alm, sizeof(alm)); |
304 | if (ret) | |
305 | dev_err(dev, "Failed to set alarm time\n"); | |
306 | ||
307 | return ret; | |
308 | } | |
309 | ||
fe5a591b MV |
310 | static int bd71828_read_alarm(struct device *dev, struct rtc_wkalrm *a) |
311 | { | |
312 | int ret; | |
313 | struct bd71828_rtc_alm alm; | |
314 | struct bd70528_rtc *r = dev_get_drvdata(dev); | |
315 | struct rohm_regmap_dev *parent = r->parent; | |
316 | ||
317 | ret = regmap_bulk_read(parent->regmap, BD71828_REG_RTC_ALM_START, | |
318 | &alm, sizeof(alm)); | |
319 | if (ret) { | |
320 | dev_err(dev, "Failed to read alarm regs\n"); | |
321 | return ret; | |
322 | } | |
323 | ||
324 | rtc2tm(&alm.alm0, &a->time); | |
325 | a->time.tm_mday = -1; | |
326 | a->time.tm_mon = -1; | |
327 | a->time.tm_year = -1; | |
328 | a->enabled = !!(alm.alm_mask & BD70528_MASK_ALM_EN); | |
329 | a->pending = 0; | |
330 | ||
331 | return 0; | |
332 | } | |
333 | ||
32a4a4eb MV |
334 | static int bd70528_read_alarm(struct device *dev, struct rtc_wkalrm *a) |
335 | { | |
336 | struct bd70528_rtc_alm alm; | |
337 | int ret; | |
338 | struct bd70528_rtc *r = dev_get_drvdata(dev); | |
fe5a591b | 339 | struct rohm_regmap_dev *parent = r->parent; |
32a4a4eb | 340 | |
fe5a591b | 341 | ret = regmap_bulk_read(parent->regmap, BD70528_REG_RTC_ALM_START, |
32a4a4eb MV |
342 | &alm, sizeof(alm)); |
343 | if (ret) { | |
344 | dev_err(dev, "Failed to read alarm regs\n"); | |
345 | return ret; | |
346 | } | |
347 | ||
348 | rtc2tm(&alm.data, &a->time); | |
349 | a->time.tm_mday = -1; | |
350 | a->time.tm_mon = -1; | |
351 | a->time.tm_year = -1; | |
352 | a->enabled = !(alm.alm_mask & BD70528_MASK_ALM_EN); | |
353 | a->pending = 0; | |
354 | ||
355 | return 0; | |
356 | } | |
357 | ||
358 | static int bd70528_set_time_locked(struct device *dev, struct rtc_time *t) | |
359 | { | |
360 | int ret, tmpret, old_states; | |
361 | struct bd70528_rtc_data rtc_data; | |
362 | struct bd70528_rtc *r = dev_get_drvdata(dev); | |
fe5a591b | 363 | struct rohm_regmap_dev *parent = r->parent; |
32a4a4eb MV |
364 | |
365 | ret = bd70528_disable_rtc_based_timers(r, &old_states); | |
366 | if (ret) | |
367 | return ret; | |
368 | ||
fe5a591b MV |
369 | tmpret = regmap_bulk_read(parent->regmap, |
370 | r->reg_time_start, &rtc_data, | |
32a4a4eb MV |
371 | sizeof(rtc_data)); |
372 | if (tmpret) { | |
373 | dev_err(dev, "Failed to read RTC time registers\n"); | |
374 | goto renable_out; | |
375 | } | |
376 | tm2rtc(t, &rtc_data); | |
377 | ||
fe5a591b MV |
378 | tmpret = regmap_bulk_write(parent->regmap, |
379 | r->reg_time_start, &rtc_data, | |
32a4a4eb MV |
380 | sizeof(rtc_data)); |
381 | if (tmpret) { | |
382 | dev_err(dev, "Failed to set RTC time\n"); | |
383 | goto renable_out; | |
384 | } | |
385 | ||
386 | renable_out: | |
387 | ret = bd70528_re_enable_rtc_based_timers(r, old_states); | |
388 | if (tmpret) | |
389 | ret = tmpret; | |
390 | ||
391 | return ret; | |
392 | } | |
393 | ||
fe5a591b MV |
394 | static int bd71828_set_time(struct device *dev, struct rtc_time *t) |
395 | { | |
396 | return bd70528_set_time_locked(dev, t); | |
397 | } | |
398 | ||
32a4a4eb MV |
399 | static int bd70528_set_time(struct device *dev, struct rtc_time *t) |
400 | { | |
401 | int ret; | |
402 | struct bd70528_rtc *r = dev_get_drvdata(dev); | |
403 | ||
fe5a591b | 404 | bd70528_wdt_lock(r->parent); |
32a4a4eb | 405 | ret = bd70528_set_time_locked(dev, t); |
fe5a591b | 406 | bd70528_wdt_unlock(r->parent); |
32a4a4eb MV |
407 | return ret; |
408 | } | |
409 | ||
410 | static int bd70528_get_time(struct device *dev, struct rtc_time *t) | |
411 | { | |
412 | struct bd70528_rtc *r = dev_get_drvdata(dev); | |
fe5a591b | 413 | struct rohm_regmap_dev *parent = r->parent; |
32a4a4eb MV |
414 | struct bd70528_rtc_data rtc_data; |
415 | int ret; | |
416 | ||
417 | /* read the RTC date and time registers all at once */ | |
fe5a591b MV |
418 | ret = regmap_bulk_read(parent->regmap, |
419 | r->reg_time_start, &rtc_data, | |
32a4a4eb MV |
420 | sizeof(rtc_data)); |
421 | if (ret) { | |
422 | dev_err(dev, "Failed to read RTC time (err %d)\n", ret); | |
423 | return ret; | |
424 | } | |
425 | ||
426 | rtc2tm(&rtc_data, t); | |
427 | ||
428 | return 0; | |
429 | } | |
430 | ||
431 | static int bd70528_alm_enable(struct device *dev, unsigned int enabled) | |
432 | { | |
433 | int ret; | |
434 | unsigned int enableval = BD70528_MASK_ALM_EN; | |
435 | struct bd70528_rtc *r = dev_get_drvdata(dev); | |
436 | ||
437 | if (enabled) | |
438 | enableval = 0; | |
439 | ||
fe5a591b MV |
440 | bd70528_wdt_lock(r->parent); |
441 | ret = bd70528_set_wake(r->parent, enabled, NULL); | |
32a4a4eb MV |
442 | if (ret) { |
443 | dev_err(dev, "Failed to change wake state\n"); | |
444 | goto out_unlock; | |
445 | } | |
fe5a591b | 446 | ret = regmap_update_bits(r->parent->regmap, BD70528_REG_RTC_ALM_MASK, |
32a4a4eb MV |
447 | BD70528_MASK_ALM_EN, enableval); |
448 | if (ret) | |
449 | dev_err(dev, "Failed to change alarm state\n"); | |
450 | ||
451 | out_unlock: | |
fe5a591b MV |
452 | bd70528_wdt_unlock(r->parent); |
453 | return ret; | |
454 | } | |
455 | ||
456 | static int bd71828_alm_enable(struct device *dev, unsigned int enabled) | |
457 | { | |
458 | int ret; | |
459 | struct bd70528_rtc *r = dev_get_drvdata(dev); | |
460 | unsigned int enableval = BD70528_MASK_ALM_EN; | |
461 | ||
462 | if (!enabled) | |
463 | enableval = 0; | |
464 | ||
465 | ret = regmap_update_bits(r->parent->regmap, BD71828_REG_RTC_ALM0_MASK, | |
466 | BD70528_MASK_ALM_EN, enableval); | |
467 | if (ret) | |
468 | dev_err(dev, "Failed to change alarm state\n"); | |
469 | ||
32a4a4eb MV |
470 | return ret; |
471 | } | |
472 | ||
473 | static const struct rtc_class_ops bd70528_rtc_ops = { | |
474 | .read_time = bd70528_get_time, | |
475 | .set_time = bd70528_set_time, | |
476 | .read_alarm = bd70528_read_alarm, | |
477 | .set_alarm = bd70528_set_alarm, | |
478 | .alarm_irq_enable = bd70528_alm_enable, | |
479 | }; | |
480 | ||
fe5a591b MV |
481 | static const struct rtc_class_ops bd71828_rtc_ops = { |
482 | .read_time = bd70528_get_time, | |
483 | .set_time = bd71828_set_time, | |
484 | .read_alarm = bd71828_read_alarm, | |
485 | .set_alarm = bd71828_set_alarm, | |
486 | .alarm_irq_enable = bd71828_alm_enable, | |
487 | }; | |
488 | ||
32a4a4eb MV |
489 | static irqreturn_t alm_hndlr(int irq, void *data) |
490 | { | |
491 | struct rtc_device *rtc = data; | |
492 | ||
493 | rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF | RTC_PF); | |
494 | return IRQ_HANDLED; | |
495 | } | |
496 | ||
497 | static int bd70528_probe(struct platform_device *pdev) | |
498 | { | |
499 | struct bd70528_rtc *bd_rtc; | |
fe5a591b MV |
500 | const struct rtc_class_ops *rtc_ops; |
501 | struct rohm_regmap_dev *parent; | |
502 | const char *irq_name; | |
32a4a4eb MV |
503 | int ret; |
504 | struct rtc_device *rtc; | |
505 | int irq; | |
506 | unsigned int hr; | |
fe5a591b MV |
507 | bool enable_main_irq = false; |
508 | u8 hour_reg; | |
509 | enum rohm_chip_type chip = platform_get_device_id(pdev)->driver_data; | |
32a4a4eb | 510 | |
fe5a591b MV |
511 | parent = dev_get_drvdata(pdev->dev.parent); |
512 | if (!parent) { | |
32a4a4eb MV |
513 | dev_err(&pdev->dev, "No MFD driver data\n"); |
514 | return -EINVAL; | |
515 | } | |
516 | bd_rtc = devm_kzalloc(&pdev->dev, sizeof(*bd_rtc), GFP_KERNEL); | |
517 | if (!bd_rtc) | |
518 | return -ENOMEM; | |
519 | ||
fe5a591b | 520 | bd_rtc->parent = parent; |
32a4a4eb MV |
521 | bd_rtc->dev = &pdev->dev; |
522 | ||
fe5a591b MV |
523 | switch (chip) { |
524 | case ROHM_CHIP_TYPE_BD70528: | |
525 | irq_name = "bd70528-rtc-alm"; | |
526 | bd_rtc->has_rtc_timers = true; | |
527 | bd_rtc->reg_time_start = BD70528_REG_RTC_START; | |
528 | hour_reg = BD70528_REG_RTC_HOUR; | |
529 | enable_main_irq = true; | |
530 | rtc_ops = &bd70528_rtc_ops; | |
531 | break; | |
532 | case ROHM_CHIP_TYPE_BD71828: | |
533 | irq_name = "bd71828-rtc-alm-0"; | |
534 | bd_rtc->reg_time_start = BD71828_REG_RTC_START; | |
535 | hour_reg = BD71828_REG_RTC_HOUR; | |
536 | rtc_ops = &bd71828_rtc_ops; | |
537 | break; | |
538 | default: | |
539 | dev_err(&pdev->dev, "Unknown chip\n"); | |
540 | return -ENOENT; | |
541 | } | |
542 | ||
543 | irq = platform_get_irq_byname(pdev, irq_name); | |
544 | ||
545 | if (irq < 0) { | |
546 | dev_err(&pdev->dev, "Failed to get irq\n"); | |
32a4a4eb | 547 | return irq; |
fe5a591b | 548 | } |
32a4a4eb MV |
549 | |
550 | platform_set_drvdata(pdev, bd_rtc); | |
551 | ||
fe5a591b | 552 | ret = regmap_read(parent->regmap, hour_reg, &hr); |
32a4a4eb MV |
553 | |
554 | if (ret) { | |
555 | dev_err(&pdev->dev, "Failed to reag RTC clock\n"); | |
556 | return ret; | |
557 | } | |
558 | ||
559 | if (!(hr & BD70528_MASK_RTC_HOUR_24H)) { | |
560 | struct rtc_time t; | |
561 | ||
fe5a591b | 562 | ret = rtc_ops->read_time(&pdev->dev, &t); |
32a4a4eb MV |
563 | |
564 | if (!ret) | |
fe5a591b | 565 | ret = rtc_ops->set_time(&pdev->dev, &t); |
32a4a4eb MV |
566 | |
567 | if (ret) { | |
568 | dev_err(&pdev->dev, | |
569 | "Setting 24H clock for RTC failed\n"); | |
570 | return ret; | |
571 | } | |
572 | } | |
573 | ||
574 | device_set_wakeup_capable(&pdev->dev, true); | |
575 | device_wakeup_enable(&pdev->dev); | |
576 | ||
577 | rtc = devm_rtc_allocate_device(&pdev->dev); | |
578 | if (IS_ERR(rtc)) { | |
579 | dev_err(&pdev->dev, "RTC device creation failed\n"); | |
580 | return PTR_ERR(rtc); | |
581 | } | |
582 | ||
583 | rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; | |
584 | rtc->range_max = RTC_TIMESTAMP_END_2099; | |
fe5a591b | 585 | rtc->ops = rtc_ops; |
32a4a4eb MV |
586 | |
587 | /* Request alarm IRQ prior to registerig the RTC */ | |
588 | ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, &alm_hndlr, | |
589 | IRQF_ONESHOT, "bd70528-rtc", rtc); | |
590 | if (ret) | |
591 | return ret; | |
592 | ||
593 | /* | |
594 | * BD70528 irq controller is not touching the main mask register. | |
595 | * So enable the RTC block interrupts at main level. We can just | |
596 | * leave them enabled as irq-controller should disable irqs | |
597 | * from sub-registers when IRQ is disabled or freed. | |
598 | */ | |
fe5a591b MV |
599 | if (enable_main_irq) { |
600 | ret = regmap_update_bits(parent->regmap, | |
32a4a4eb MV |
601 | BD70528_REG_INT_MAIN_MASK, |
602 | BD70528_INT_RTC_MASK, 0); | |
fe5a591b MV |
603 | if (ret) { |
604 | dev_err(&pdev->dev, "Failed to enable RTC interrupts\n"); | |
605 | return ret; | |
606 | } | |
32a4a4eb MV |
607 | } |
608 | ||
44c638ce | 609 | return rtc_register_device(rtc); |
32a4a4eb MV |
610 | } |
611 | ||
fe5a591b MV |
612 | static const struct platform_device_id bd718x7_rtc_id[] = { |
613 | { "bd70528-rtc", ROHM_CHIP_TYPE_BD70528 }, | |
614 | { "bd71828-rtc", ROHM_CHIP_TYPE_BD71828 }, | |
615 | { }, | |
616 | }; | |
617 | MODULE_DEVICE_TABLE(platform, bd718x7_rtc_id); | |
618 | ||
32a4a4eb MV |
619 | static struct platform_driver bd70528_rtc = { |
620 | .driver = { | |
621 | .name = "bd70528-rtc" | |
622 | }, | |
623 | .probe = bd70528_probe, | |
fe5a591b | 624 | .id_table = bd718x7_rtc_id, |
32a4a4eb MV |
625 | }; |
626 | ||
627 | module_platform_driver(bd70528_rtc); | |
628 | ||
629 | MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>"); | |
fe5a591b | 630 | MODULE_DESCRIPTION("ROHM BD70528 and BD71828 PMIC RTC driver"); |
32a4a4eb | 631 | MODULE_LICENSE("GPL"); |
afe19a7a | 632 | MODULE_ALIAS("platform:bd70528-rtc"); |