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