Commit | Line | Data |
---|---|---|
fca1dd03 | 1 | /* |
f903129b | 2 | * RTC driver for Maxim MAX77686 and MAX77802 |
fca1dd03 JL |
3 | * |
4 | * Copyright (C) 2012 Samsung Electronics Co.Ltd | |
5 | * | |
6 | * based on rtc-max8997.c | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify it | |
9 | * under the terms of the GNU General Public License as published by the | |
10 | * Free Software Foundation; either version 2 of the License, or (at your | |
11 | * option) any later version. | |
12 | * | |
13 | */ | |
14 | ||
15 | #include <linux/slab.h> | |
16 | #include <linux/rtc.h> | |
17 | #include <linux/delay.h> | |
18 | #include <linux/mutex.h> | |
19 | #include <linux/module.h> | |
20 | #include <linux/platform_device.h> | |
21 | #include <linux/mfd/max77686-private.h> | |
22 | #include <linux/irqdomain.h> | |
23 | #include <linux/regmap.h> | |
24 | ||
25 | /* RTC Control Register */ | |
26 | #define BCD_EN_SHIFT 0 | |
0b4f8b08 | 27 | #define BCD_EN_MASK BIT(BCD_EN_SHIFT) |
fca1dd03 | 28 | #define MODEL24_SHIFT 1 |
0b4f8b08 | 29 | #define MODEL24_MASK BIT(MODEL24_SHIFT) |
fca1dd03 JL |
30 | /* RTC Update Register1 */ |
31 | #define RTC_UDR_SHIFT 0 | |
0b4f8b08 | 32 | #define RTC_UDR_MASK BIT(RTC_UDR_SHIFT) |
fca1dd03 | 33 | #define RTC_RBUDR_SHIFT 4 |
0b4f8b08 | 34 | #define RTC_RBUDR_MASK BIT(RTC_RBUDR_SHIFT) |
fca1dd03 JL |
35 | /* RTC Hour register */ |
36 | #define HOUR_PM_SHIFT 6 | |
0b4f8b08 | 37 | #define HOUR_PM_MASK BIT(HOUR_PM_SHIFT) |
fca1dd03 JL |
38 | /* RTC Alarm Enable */ |
39 | #define ALARM_ENABLE_SHIFT 7 | |
0b4f8b08 | 40 | #define ALARM_ENABLE_MASK BIT(ALARM_ENABLE_SHIFT) |
fca1dd03 | 41 | |
f903129b JMC |
42 | #define REG_RTC_NONE 0xdeadbeef |
43 | ||
44 | /* | |
45 | * MAX77802 has separate register (RTCAE1) for alarm enable instead | |
46 | * using 1 bit from registers RTC{SEC,MIN,HOUR,DAY,MONTH,YEAR,DATE} | |
47 | * as in done in MAX77686. | |
48 | */ | |
49 | #define MAX77802_ALARM_ENABLE_VALUE 0x77 | |
50 | ||
fca1dd03 JL |
51 | enum { |
52 | RTC_SEC = 0, | |
53 | RTC_MIN, | |
54 | RTC_HOUR, | |
55 | RTC_WEEKDAY, | |
56 | RTC_MONTH, | |
57 | RTC_YEAR, | |
58 | RTC_DATE, | |
59 | RTC_NR_TIME | |
60 | }; | |
61 | ||
01ea01b3 JMC |
62 | struct max77686_rtc_driver_data { |
63 | /* Minimum usecs needed for a RTC update */ | |
64 | unsigned long delay; | |
65 | /* Mask used to read RTC registers value */ | |
66 | u8 mask; | |
90a5698a JMC |
67 | /* Registers offset to I2C addresses map */ |
68 | const unsigned int *map; | |
f903129b JMC |
69 | /* Has a separate alarm enable register? */ |
70 | bool alarm_enable_reg; | |
71 | /* Has a separate I2C regmap for the RTC? */ | |
72 | bool separate_i2c_addr; | |
01ea01b3 JMC |
73 | }; |
74 | ||
fca1dd03 JL |
75 | struct max77686_rtc_info { |
76 | struct device *dev; | |
fca1dd03 JL |
77 | struct i2c_client *rtc; |
78 | struct rtc_device *rtc_dev; | |
79 | struct mutex lock; | |
80 | ||
81 | struct regmap *regmap; | |
f604c488 | 82 | struct regmap *rtc_regmap; |
fca1dd03 | 83 | |
01ea01b3 JMC |
84 | const struct max77686_rtc_driver_data *drv_data; |
85 | ||
fca1dd03 JL |
86 | int virq; |
87 | int rtc_24hr_mode; | |
88 | }; | |
89 | ||
90 | enum MAX77686_RTC_OP { | |
91 | MAX77686_RTC_WRITE, | |
92 | MAX77686_RTC_READ, | |
93 | }; | |
94 | ||
90a5698a JMC |
95 | /* These are not registers but just offsets that are mapped to addresses */ |
96 | enum max77686_rtc_reg_offset { | |
97 | REG_RTC_CONTROLM = 0, | |
98 | REG_RTC_CONTROL, | |
99 | REG_RTC_UPDATE0, | |
100 | REG_WTSR_SMPL_CNTL, | |
101 | REG_RTC_SEC, | |
102 | REG_RTC_MIN, | |
103 | REG_RTC_HOUR, | |
104 | REG_RTC_WEEKDAY, | |
105 | REG_RTC_MONTH, | |
106 | REG_RTC_YEAR, | |
107 | REG_RTC_DATE, | |
108 | REG_ALARM1_SEC, | |
109 | REG_ALARM1_MIN, | |
110 | REG_ALARM1_HOUR, | |
111 | REG_ALARM1_WEEKDAY, | |
112 | REG_ALARM1_MONTH, | |
113 | REG_ALARM1_YEAR, | |
114 | REG_ALARM1_DATE, | |
115 | REG_ALARM2_SEC, | |
116 | REG_ALARM2_MIN, | |
117 | REG_ALARM2_HOUR, | |
118 | REG_ALARM2_WEEKDAY, | |
119 | REG_ALARM2_MONTH, | |
120 | REG_ALARM2_YEAR, | |
121 | REG_ALARM2_DATE, | |
f903129b | 122 | REG_RTC_AE1, |
90a5698a JMC |
123 | REG_RTC_END, |
124 | }; | |
125 | ||
126 | /* Maps RTC registers offset to the MAX77686 register addresses */ | |
127 | static const unsigned int max77686_map[REG_RTC_END] = { | |
128 | [REG_RTC_CONTROLM] = MAX77686_RTC_CONTROLM, | |
129 | [REG_RTC_CONTROL] = MAX77686_RTC_CONTROL, | |
130 | [REG_RTC_UPDATE0] = MAX77686_RTC_UPDATE0, | |
131 | [REG_WTSR_SMPL_CNTL] = MAX77686_WTSR_SMPL_CNTL, | |
132 | [REG_RTC_SEC] = MAX77686_RTC_SEC, | |
133 | [REG_RTC_MIN] = MAX77686_RTC_MIN, | |
134 | [REG_RTC_HOUR] = MAX77686_RTC_HOUR, | |
135 | [REG_RTC_WEEKDAY] = MAX77686_RTC_WEEKDAY, | |
136 | [REG_RTC_MONTH] = MAX77686_RTC_MONTH, | |
137 | [REG_RTC_YEAR] = MAX77686_RTC_YEAR, | |
138 | [REG_RTC_DATE] = MAX77686_RTC_DATE, | |
139 | [REG_ALARM1_SEC] = MAX77686_ALARM1_SEC, | |
140 | [REG_ALARM1_MIN] = MAX77686_ALARM1_MIN, | |
141 | [REG_ALARM1_HOUR] = MAX77686_ALARM1_HOUR, | |
142 | [REG_ALARM1_WEEKDAY] = MAX77686_ALARM1_WEEKDAY, | |
143 | [REG_ALARM1_MONTH] = MAX77686_ALARM1_MONTH, | |
144 | [REG_ALARM1_YEAR] = MAX77686_ALARM1_YEAR, | |
145 | [REG_ALARM1_DATE] = MAX77686_ALARM1_DATE, | |
146 | [REG_ALARM2_SEC] = MAX77686_ALARM2_SEC, | |
147 | [REG_ALARM2_MIN] = MAX77686_ALARM2_MIN, | |
148 | [REG_ALARM2_HOUR] = MAX77686_ALARM2_HOUR, | |
149 | [REG_ALARM2_WEEKDAY] = MAX77686_ALARM2_WEEKDAY, | |
150 | [REG_ALARM2_MONTH] = MAX77686_ALARM2_MONTH, | |
151 | [REG_ALARM2_YEAR] = MAX77686_ALARM2_YEAR, | |
152 | [REG_ALARM2_DATE] = MAX77686_ALARM2_DATE, | |
f903129b | 153 | [REG_RTC_AE1] = REG_RTC_NONE, |
90a5698a JMC |
154 | }; |
155 | ||
01ea01b3 JMC |
156 | static const struct max77686_rtc_driver_data max77686_drv_data = { |
157 | .delay = 16000, | |
158 | .mask = 0x7f, | |
90a5698a | 159 | .map = max77686_map, |
f903129b JMC |
160 | .alarm_enable_reg = false, |
161 | .separate_i2c_addr = true, | |
162 | }; | |
163 | ||
164 | static const unsigned int max77802_map[REG_RTC_END] = { | |
165 | [REG_RTC_CONTROLM] = MAX77802_RTC_CONTROLM, | |
166 | [REG_RTC_CONTROL] = MAX77802_RTC_CONTROL, | |
167 | [REG_RTC_UPDATE0] = MAX77802_RTC_UPDATE0, | |
168 | [REG_WTSR_SMPL_CNTL] = MAX77802_WTSR_SMPL_CNTL, | |
169 | [REG_RTC_SEC] = MAX77802_RTC_SEC, | |
170 | [REG_RTC_MIN] = MAX77802_RTC_MIN, | |
171 | [REG_RTC_HOUR] = MAX77802_RTC_HOUR, | |
172 | [REG_RTC_WEEKDAY] = MAX77802_RTC_WEEKDAY, | |
173 | [REG_RTC_MONTH] = MAX77802_RTC_MONTH, | |
174 | [REG_RTC_YEAR] = MAX77802_RTC_YEAR, | |
175 | [REG_RTC_DATE] = MAX77802_RTC_DATE, | |
176 | [REG_ALARM1_SEC] = MAX77802_ALARM1_SEC, | |
177 | [REG_ALARM1_MIN] = MAX77802_ALARM1_MIN, | |
178 | [REG_ALARM1_HOUR] = MAX77802_ALARM1_HOUR, | |
179 | [REG_ALARM1_WEEKDAY] = MAX77802_ALARM1_WEEKDAY, | |
180 | [REG_ALARM1_MONTH] = MAX77802_ALARM1_MONTH, | |
181 | [REG_ALARM1_YEAR] = MAX77802_ALARM1_YEAR, | |
182 | [REG_ALARM1_DATE] = MAX77802_ALARM1_DATE, | |
183 | [REG_ALARM2_SEC] = MAX77802_ALARM2_SEC, | |
184 | [REG_ALARM2_MIN] = MAX77802_ALARM2_MIN, | |
185 | [REG_ALARM2_HOUR] = MAX77802_ALARM2_HOUR, | |
186 | [REG_ALARM2_WEEKDAY] = MAX77802_ALARM2_WEEKDAY, | |
187 | [REG_ALARM2_MONTH] = MAX77802_ALARM2_MONTH, | |
188 | [REG_ALARM2_YEAR] = MAX77802_ALARM2_YEAR, | |
189 | [REG_ALARM2_DATE] = MAX77802_ALARM2_DATE, | |
190 | [REG_RTC_AE1] = MAX77802_RTC_AE1, | |
191 | }; | |
192 | ||
193 | static const struct max77686_rtc_driver_data max77802_drv_data = { | |
194 | .delay = 200, | |
195 | .mask = 0xff, | |
196 | .map = max77802_map, | |
197 | .alarm_enable_reg = true, | |
198 | .separate_i2c_addr = false, | |
01ea01b3 JMC |
199 | }; |
200 | ||
fca1dd03 | 201 | static void max77686_rtc_data_to_tm(u8 *data, struct rtc_time *tm, |
01ea01b3 | 202 | struct max77686_rtc_info *info) |
fca1dd03 | 203 | { |
01ea01b3 JMC |
204 | u8 mask = info->drv_data->mask; |
205 | ||
206 | tm->tm_sec = data[RTC_SEC] & mask; | |
207 | tm->tm_min = data[RTC_MIN] & mask; | |
0b4f8b08 | 208 | if (info->rtc_24hr_mode) { |
fca1dd03 | 209 | tm->tm_hour = data[RTC_HOUR] & 0x1f; |
0b4f8b08 | 210 | } else { |
fca1dd03 JL |
211 | tm->tm_hour = data[RTC_HOUR] & 0x0f; |
212 | if (data[RTC_HOUR] & HOUR_PM_MASK) | |
213 | tm->tm_hour += 12; | |
214 | } | |
215 | ||
a20cd88e | 216 | /* Only a single bit is set in data[], so fls() would be equivalent */ |
01ea01b3 | 217 | tm->tm_wday = ffs(data[RTC_WEEKDAY] & mask) - 1; |
fca1dd03 JL |
218 | tm->tm_mday = data[RTC_DATE] & 0x1f; |
219 | tm->tm_mon = (data[RTC_MONTH] & 0x0f) - 1; | |
f903129b | 220 | tm->tm_year = data[RTC_YEAR] & mask; |
fca1dd03 JL |
221 | tm->tm_yday = 0; |
222 | tm->tm_isdst = 0; | |
f903129b JMC |
223 | |
224 | /* | |
225 | * MAX77686 uses 1 bit from sec/min/hour/etc RTC registers and the | |
226 | * year values are just 0..99 so add 100 to support up to 2099. | |
227 | */ | |
228 | if (!info->drv_data->alarm_enable_reg) | |
229 | tm->tm_year += 100; | |
fca1dd03 JL |
230 | } |
231 | ||
f903129b JMC |
232 | static int max77686_rtc_tm_to_data(struct rtc_time *tm, u8 *data, |
233 | struct max77686_rtc_info *info) | |
fca1dd03 JL |
234 | { |
235 | data[RTC_SEC] = tm->tm_sec; | |
236 | data[RTC_MIN] = tm->tm_min; | |
237 | data[RTC_HOUR] = tm->tm_hour; | |
238 | data[RTC_WEEKDAY] = 1 << tm->tm_wday; | |
239 | data[RTC_DATE] = tm->tm_mday; | |
240 | data[RTC_MONTH] = tm->tm_mon + 1; | |
f903129b JMC |
241 | |
242 | if (info->drv_data->alarm_enable_reg) { | |
243 | data[RTC_YEAR] = tm->tm_year; | |
244 | return 0; | |
245 | } | |
246 | ||
cdf5f4ac | 247 | data[RTC_YEAR] = tm->tm_year > 100 ? (tm->tm_year - 100) : 0; |
fca1dd03 JL |
248 | |
249 | if (tm->tm_year < 100) { | |
1e5813bd | 250 | dev_err(info->dev, "RTC cannot handle the year %d.\n", |
a737e835 | 251 | 1900 + tm->tm_year); |
fca1dd03 JL |
252 | return -EINVAL; |
253 | } | |
f903129b | 254 | |
fca1dd03 JL |
255 | return 0; |
256 | } | |
257 | ||
258 | static int max77686_rtc_update(struct max77686_rtc_info *info, | |
0b4f8b08 | 259 | enum MAX77686_RTC_OP op) |
fca1dd03 JL |
260 | { |
261 | int ret; | |
262 | unsigned int data; | |
01ea01b3 | 263 | unsigned long delay = info->drv_data->delay; |
fca1dd03 JL |
264 | |
265 | if (op == MAX77686_RTC_WRITE) | |
266 | data = 1 << RTC_UDR_SHIFT; | |
267 | else | |
268 | data = 1 << RTC_RBUDR_SHIFT; | |
269 | ||
f604c488 | 270 | ret = regmap_update_bits(info->rtc_regmap, |
90a5698a JMC |
271 | info->drv_data->map[REG_RTC_UPDATE0], |
272 | data, data); | |
fca1dd03 | 273 | if (ret < 0) |
bf035f42 KK |
274 | dev_err(info->dev, "Fail to write update reg(ret=%d, data=0x%x)\n", |
275 | ret, data); | |
fca1dd03 | 276 | else { |
01ea01b3 JMC |
277 | /* Minimum delay required before RTC update. */ |
278 | usleep_range(delay, delay * 2); | |
fca1dd03 JL |
279 | } |
280 | ||
281 | return ret; | |
282 | } | |
283 | ||
284 | static int max77686_rtc_read_time(struct device *dev, struct rtc_time *tm) | |
285 | { | |
286 | struct max77686_rtc_info *info = dev_get_drvdata(dev); | |
287 | u8 data[RTC_NR_TIME]; | |
288 | int ret; | |
289 | ||
290 | mutex_lock(&info->lock); | |
291 | ||
292 | ret = max77686_rtc_update(info, MAX77686_RTC_READ); | |
293 | if (ret < 0) | |
294 | goto out; | |
295 | ||
f604c488 | 296 | ret = regmap_bulk_read(info->rtc_regmap, |
90a5698a JMC |
297 | info->drv_data->map[REG_RTC_SEC], |
298 | data, ARRAY_SIZE(data)); | |
fca1dd03 | 299 | if (ret < 0) { |
bf035f42 | 300 | dev_err(info->dev, "Fail to read time reg(%d)\n", ret); |
fca1dd03 JL |
301 | goto out; |
302 | } | |
303 | ||
01ea01b3 | 304 | max77686_rtc_data_to_tm(data, tm, info); |
fca1dd03 JL |
305 | |
306 | ret = rtc_valid_tm(tm); | |
307 | ||
308 | out: | |
309 | mutex_unlock(&info->lock); | |
310 | return ret; | |
311 | } | |
312 | ||
313 | static int max77686_rtc_set_time(struct device *dev, struct rtc_time *tm) | |
314 | { | |
315 | struct max77686_rtc_info *info = dev_get_drvdata(dev); | |
316 | u8 data[RTC_NR_TIME]; | |
317 | int ret; | |
318 | ||
f903129b | 319 | ret = max77686_rtc_tm_to_data(tm, data, info); |
fca1dd03 JL |
320 | if (ret < 0) |
321 | return ret; | |
322 | ||
323 | mutex_lock(&info->lock); | |
324 | ||
f604c488 | 325 | ret = regmap_bulk_write(info->rtc_regmap, |
90a5698a JMC |
326 | info->drv_data->map[REG_RTC_SEC], |
327 | data, ARRAY_SIZE(data)); | |
fca1dd03 | 328 | if (ret < 0) { |
bf035f42 | 329 | dev_err(info->dev, "Fail to write time reg(%d)\n", ret); |
fca1dd03 JL |
330 | goto out; |
331 | } | |
332 | ||
333 | ret = max77686_rtc_update(info, MAX77686_RTC_WRITE); | |
334 | ||
335 | out: | |
336 | mutex_unlock(&info->lock); | |
337 | return ret; | |
338 | } | |
339 | ||
340 | static int max77686_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) | |
341 | { | |
342 | struct max77686_rtc_info *info = dev_get_drvdata(dev); | |
343 | u8 data[RTC_NR_TIME]; | |
344 | unsigned int val; | |
90a5698a | 345 | const unsigned int *map = info->drv_data->map; |
fca1dd03 JL |
346 | int i, ret; |
347 | ||
348 | mutex_lock(&info->lock); | |
349 | ||
350 | ret = max77686_rtc_update(info, MAX77686_RTC_READ); | |
351 | if (ret < 0) | |
352 | goto out; | |
353 | ||
f604c488 LD |
354 | ret = regmap_bulk_read(info->rtc_regmap, map[REG_ALARM1_SEC], |
355 | data, ARRAY_SIZE(data)); | |
fca1dd03 | 356 | if (ret < 0) { |
bf035f42 | 357 | dev_err(info->dev, "Fail to read alarm reg(%d)\n", ret); |
fca1dd03 JL |
358 | goto out; |
359 | } | |
360 | ||
01ea01b3 | 361 | max77686_rtc_data_to_tm(data, &alrm->time, info); |
fca1dd03 JL |
362 | |
363 | alrm->enabled = 0; | |
f903129b JMC |
364 | |
365 | if (info->drv_data->alarm_enable_reg) { | |
366 | if (map[REG_RTC_AE1] == REG_RTC_NONE) { | |
367 | ret = -EINVAL; | |
368 | dev_err(info->dev, | |
369 | "alarm enable register not set(%d)\n", ret); | |
370 | goto out; | |
371 | } | |
372 | ||
f604c488 | 373 | ret = regmap_read(info->rtc_regmap, map[REG_RTC_AE1], &val); |
f903129b JMC |
374 | if (ret < 0) { |
375 | dev_err(info->dev, | |
376 | "fail to read alarm enable(%d)\n", ret); | |
377 | goto out; | |
378 | } | |
379 | ||
380 | if (val) | |
fca1dd03 | 381 | alrm->enabled = 1; |
f903129b JMC |
382 | } else { |
383 | for (i = 0; i < ARRAY_SIZE(data); i++) { | |
384 | if (data[i] & ALARM_ENABLE_MASK) { | |
385 | alrm->enabled = 1; | |
386 | break; | |
387 | } | |
fca1dd03 JL |
388 | } |
389 | } | |
390 | ||
391 | alrm->pending = 0; | |
f604c488 | 392 | ret = regmap_read(info->regmap, MAX77686_REG_STATUS2, &val); |
fca1dd03 | 393 | if (ret < 0) { |
bf035f42 | 394 | dev_err(info->dev, "Fail to read status2 reg(%d)\n", ret); |
fca1dd03 JL |
395 | goto out; |
396 | } | |
397 | ||
398 | if (val & (1 << 4)) /* RTCA1 */ | |
399 | alrm->pending = 1; | |
400 | ||
401 | out: | |
402 | mutex_unlock(&info->lock); | |
7cdffeb5 | 403 | return ret; |
fca1dd03 JL |
404 | } |
405 | ||
406 | static int max77686_rtc_stop_alarm(struct max77686_rtc_info *info) | |
407 | { | |
408 | u8 data[RTC_NR_TIME]; | |
409 | int ret, i; | |
410 | struct rtc_time tm; | |
90a5698a | 411 | const unsigned int *map = info->drv_data->map; |
fca1dd03 JL |
412 | |
413 | if (!mutex_is_locked(&info->lock)) | |
414 | dev_warn(info->dev, "%s: should have mutex locked\n", __func__); | |
415 | ||
416 | ret = max77686_rtc_update(info, MAX77686_RTC_READ); | |
417 | if (ret < 0) | |
418 | goto out; | |
419 | ||
f903129b JMC |
420 | if (info->drv_data->alarm_enable_reg) { |
421 | if (map[REG_RTC_AE1] == REG_RTC_NONE) { | |
422 | ret = -EINVAL; | |
423 | dev_err(info->dev, | |
424 | "alarm enable register not set(%d)\n", ret); | |
425 | goto out; | |
426 | } | |
427 | ||
f604c488 | 428 | ret = regmap_write(info->rtc_regmap, map[REG_RTC_AE1], 0); |
f903129b | 429 | } else { |
f604c488 LD |
430 | ret = regmap_bulk_read(info->rtc_regmap, map[REG_ALARM1_SEC], |
431 | data, ARRAY_SIZE(data)); | |
f903129b | 432 | if (ret < 0) { |
bf035f42 | 433 | dev_err(info->dev, "Fail to read alarm reg(%d)\n", ret); |
f903129b JMC |
434 | goto out; |
435 | } | |
fca1dd03 | 436 | |
f903129b | 437 | max77686_rtc_data_to_tm(data, &tm, info); |
fca1dd03 | 438 | |
f903129b JMC |
439 | for (i = 0; i < ARRAY_SIZE(data); i++) |
440 | data[i] &= ~ALARM_ENABLE_MASK; | |
441 | ||
f604c488 LD |
442 | ret = regmap_bulk_write(info->rtc_regmap, map[REG_ALARM1_SEC], |
443 | data, ARRAY_SIZE(data)); | |
f903129b | 444 | } |
fca1dd03 | 445 | |
fca1dd03 | 446 | if (ret < 0) { |
bf035f42 | 447 | dev_err(info->dev, "Fail to write alarm reg(%d)\n", ret); |
fca1dd03 JL |
448 | goto out; |
449 | } | |
450 | ||
451 | ret = max77686_rtc_update(info, MAX77686_RTC_WRITE); | |
452 | out: | |
453 | return ret; | |
454 | } | |
455 | ||
456 | static int max77686_rtc_start_alarm(struct max77686_rtc_info *info) | |
457 | { | |
458 | u8 data[RTC_NR_TIME]; | |
459 | int ret; | |
460 | struct rtc_time tm; | |
90a5698a | 461 | const unsigned int *map = info->drv_data->map; |
fca1dd03 JL |
462 | |
463 | if (!mutex_is_locked(&info->lock)) | |
464 | dev_warn(info->dev, "%s: should have mutex locked\n", __func__); | |
465 | ||
466 | ret = max77686_rtc_update(info, MAX77686_RTC_READ); | |
467 | if (ret < 0) | |
468 | goto out; | |
469 | ||
f903129b | 470 | if (info->drv_data->alarm_enable_reg) { |
f604c488 | 471 | ret = regmap_write(info->rtc_regmap, map[REG_RTC_AE1], |
f903129b JMC |
472 | MAX77802_ALARM_ENABLE_VALUE); |
473 | } else { | |
f604c488 LD |
474 | ret = regmap_bulk_read(info->rtc_regmap, map[REG_ALARM1_SEC], |
475 | data, ARRAY_SIZE(data)); | |
f903129b | 476 | if (ret < 0) { |
bf035f42 | 477 | dev_err(info->dev, "Fail to read alarm reg(%d)\n", ret); |
f903129b JMC |
478 | goto out; |
479 | } | |
fca1dd03 | 480 | |
f903129b JMC |
481 | max77686_rtc_data_to_tm(data, &tm, info); |
482 | ||
483 | data[RTC_SEC] |= (1 << ALARM_ENABLE_SHIFT); | |
484 | data[RTC_MIN] |= (1 << ALARM_ENABLE_SHIFT); | |
485 | data[RTC_HOUR] |= (1 << ALARM_ENABLE_SHIFT); | |
486 | data[RTC_WEEKDAY] &= ~ALARM_ENABLE_MASK; | |
487 | if (data[RTC_MONTH] & 0xf) | |
488 | data[RTC_MONTH] |= (1 << ALARM_ENABLE_SHIFT); | |
489 | if (data[RTC_YEAR] & info->drv_data->mask) | |
490 | data[RTC_YEAR] |= (1 << ALARM_ENABLE_SHIFT); | |
491 | if (data[RTC_DATE] & 0x1f) | |
492 | data[RTC_DATE] |= (1 << ALARM_ENABLE_SHIFT); | |
493 | ||
f604c488 LD |
494 | ret = regmap_bulk_write(info->rtc_regmap, map[REG_ALARM1_SEC], |
495 | data, ARRAY_SIZE(data)); | |
f903129b | 496 | } |
fca1dd03 | 497 | |
fca1dd03 | 498 | if (ret < 0) { |
bf035f42 | 499 | dev_err(info->dev, "Fail to write alarm reg(%d)\n", ret); |
fca1dd03 JL |
500 | goto out; |
501 | } | |
502 | ||
503 | ret = max77686_rtc_update(info, MAX77686_RTC_WRITE); | |
504 | out: | |
505 | return ret; | |
506 | } | |
507 | ||
508 | static int max77686_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) | |
509 | { | |
510 | struct max77686_rtc_info *info = dev_get_drvdata(dev); | |
511 | u8 data[RTC_NR_TIME]; | |
512 | int ret; | |
513 | ||
f903129b | 514 | ret = max77686_rtc_tm_to_data(&alrm->time, data, info); |
fca1dd03 JL |
515 | if (ret < 0) |
516 | return ret; | |
517 | ||
518 | mutex_lock(&info->lock); | |
519 | ||
520 | ret = max77686_rtc_stop_alarm(info); | |
521 | if (ret < 0) | |
522 | goto out; | |
523 | ||
f604c488 | 524 | ret = regmap_bulk_write(info->rtc_regmap, |
90a5698a JMC |
525 | info->drv_data->map[REG_ALARM1_SEC], |
526 | data, ARRAY_SIZE(data)); | |
fca1dd03 JL |
527 | |
528 | if (ret < 0) { | |
bf035f42 | 529 | dev_err(info->dev, "Fail to write alarm reg(%d)\n", ret); |
fca1dd03 JL |
530 | goto out; |
531 | } | |
532 | ||
533 | ret = max77686_rtc_update(info, MAX77686_RTC_WRITE); | |
534 | if (ret < 0) | |
535 | goto out; | |
536 | ||
537 | if (alrm->enabled) | |
538 | ret = max77686_rtc_start_alarm(info); | |
539 | out: | |
540 | mutex_unlock(&info->lock); | |
541 | return ret; | |
542 | } | |
543 | ||
544 | static int max77686_rtc_alarm_irq_enable(struct device *dev, | |
0b4f8b08 | 545 | unsigned int enabled) |
fca1dd03 JL |
546 | { |
547 | struct max77686_rtc_info *info = dev_get_drvdata(dev); | |
548 | int ret; | |
549 | ||
550 | mutex_lock(&info->lock); | |
551 | if (enabled) | |
552 | ret = max77686_rtc_start_alarm(info); | |
553 | else | |
554 | ret = max77686_rtc_stop_alarm(info); | |
555 | mutex_unlock(&info->lock); | |
556 | ||
557 | return ret; | |
558 | } | |
559 | ||
560 | static irqreturn_t max77686_rtc_alarm_irq(int irq, void *data) | |
561 | { | |
562 | struct max77686_rtc_info *info = data; | |
563 | ||
bf035f42 | 564 | dev_dbg(info->dev, "RTC alarm IRQ: %d\n", irq); |
fca1dd03 JL |
565 | |
566 | rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF); | |
567 | ||
568 | return IRQ_HANDLED; | |
569 | } | |
570 | ||
571 | static const struct rtc_class_ops max77686_rtc_ops = { | |
572 | .read_time = max77686_rtc_read_time, | |
573 | .set_time = max77686_rtc_set_time, | |
574 | .read_alarm = max77686_rtc_read_alarm, | |
575 | .set_alarm = max77686_rtc_set_alarm, | |
576 | .alarm_irq_enable = max77686_rtc_alarm_irq_enable, | |
577 | }; | |
578 | ||
fca1dd03 JL |
579 | static int max77686_rtc_init_reg(struct max77686_rtc_info *info) |
580 | { | |
581 | u8 data[2]; | |
582 | int ret; | |
583 | ||
584 | /* Set RTC control register : Binary mode, 24hour mdoe */ | |
585 | data[0] = (1 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT); | |
586 | data[1] = (0 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT); | |
587 | ||
588 | info->rtc_24hr_mode = 1; | |
589 | ||
f604c488 | 590 | ret = regmap_bulk_write(info->rtc_regmap, |
90a5698a JMC |
591 | info->drv_data->map[REG_RTC_CONTROLM], |
592 | data, ARRAY_SIZE(data)); | |
fca1dd03 | 593 | if (ret < 0) { |
bf035f42 | 594 | dev_err(info->dev, "Fail to write controlm reg(%d)\n", ret); |
fca1dd03 JL |
595 | return ret; |
596 | } | |
597 | ||
598 | ret = max77686_rtc_update(info, MAX77686_RTC_WRITE); | |
599 | return ret; | |
600 | } | |
601 | ||
fca1dd03 JL |
602 | static int max77686_rtc_probe(struct platform_device *pdev) |
603 | { | |
604 | struct max77686_dev *max77686 = dev_get_drvdata(pdev->dev.parent); | |
605 | struct max77686_rtc_info *info; | |
01ea01b3 | 606 | const struct platform_device_id *id = platform_get_device_id(pdev); |
6f1c1e71 | 607 | int ret; |
fca1dd03 | 608 | |
0f64f853 | 609 | info = devm_kzalloc(&pdev->dev, sizeof(struct max77686_rtc_info), |
0b4f8b08 | 610 | GFP_KERNEL); |
fca1dd03 JL |
611 | if (!info) |
612 | return -ENOMEM; | |
613 | ||
614 | mutex_init(&info->lock); | |
615 | info->dev = &pdev->dev; | |
fca1dd03 | 616 | info->rtc = max77686->rtc; |
01ea01b3 JMC |
617 | info->drv_data = (const struct max77686_rtc_driver_data *) |
618 | id->driver_data; | |
6f1c1e71 | 619 | |
f604c488 LD |
620 | info->regmap = max77686->regmap; |
621 | info->rtc_regmap = (info->drv_data->separate_i2c_addr) ? | |
622 | max77686->rtc_regmap : info->regmap; | |
f903129b | 623 | |
fca1dd03 JL |
624 | platform_set_drvdata(pdev, info); |
625 | ||
626 | ret = max77686_rtc_init_reg(info); | |
627 | ||
628 | if (ret < 0) { | |
629 | dev_err(&pdev->dev, "Failed to initialize RTC reg:%d\n", ret); | |
630 | goto err_rtc; | |
631 | } | |
632 | ||
fca1dd03 JL |
633 | device_init_wakeup(&pdev->dev, 1); |
634 | ||
f903129b | 635 | info->rtc_dev = devm_rtc_device_register(&pdev->dev, id->name, |
f56950ec | 636 | &max77686_rtc_ops, THIS_MODULE); |
fca1dd03 JL |
637 | |
638 | if (IS_ERR(info->rtc_dev)) { | |
fca1dd03 JL |
639 | ret = PTR_ERR(info->rtc_dev); |
640 | dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret); | |
641 | if (ret == 0) | |
642 | ret = -EINVAL; | |
643 | goto err_rtc; | |
644 | } | |
6f1c1e71 | 645 | |
1745d6d3 JMC |
646 | if (!max77686->rtc_irq_data) { |
647 | ret = -EINVAL; | |
bf035f42 | 648 | dev_err(&pdev->dev, "No RTC regmap IRQ chip\n"); |
1745d6d3 JMC |
649 | goto err_rtc; |
650 | } | |
651 | ||
6f1c1e71 JMC |
652 | info->virq = regmap_irq_get_virq(max77686->rtc_irq_data, |
653 | MAX77686_RTCIRQ_RTCA1); | |
fb166ba1 | 654 | if (info->virq <= 0) { |
ad819039 | 655 | ret = -ENXIO; |
fca1dd03 | 656 | goto err_rtc; |
ad819039 | 657 | } |
fca1dd03 | 658 | |
6f1c1e71 | 659 | ret = devm_request_threaded_irq(&pdev->dev, info->virq, NULL, |
0b4f8b08 LD |
660 | max77686_rtc_alarm_irq, 0, |
661 | "rtc-alarm1", info); | |
ad819039 | 662 | if (ret < 0) |
fca1dd03 JL |
663 | dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n", |
664 | info->virq, ret); | |
fca1dd03 | 665 | |
fca1dd03 | 666 | err_rtc: |
fca1dd03 JL |
667 | return ret; |
668 | } | |
669 | ||
e7f7fc73 DA |
670 | #ifdef CONFIG_PM_SLEEP |
671 | static int max77686_rtc_suspend(struct device *dev) | |
672 | { | |
673 | if (device_may_wakeup(dev)) { | |
674 | struct max77686_rtc_info *info = dev_get_drvdata(dev); | |
675 | ||
676 | return enable_irq_wake(info->virq); | |
677 | } | |
678 | ||
679 | return 0; | |
680 | } | |
681 | ||
682 | static int max77686_rtc_resume(struct device *dev) | |
683 | { | |
684 | if (device_may_wakeup(dev)) { | |
685 | struct max77686_rtc_info *info = dev_get_drvdata(dev); | |
686 | ||
687 | return disable_irq_wake(info->virq); | |
688 | } | |
689 | ||
690 | return 0; | |
691 | } | |
692 | #endif | |
693 | ||
694 | static SIMPLE_DEV_PM_OPS(max77686_rtc_pm_ops, | |
695 | max77686_rtc_suspend, max77686_rtc_resume); | |
696 | ||
fca1dd03 | 697 | static const struct platform_device_id rtc_id[] = { |
01ea01b3 | 698 | { "max77686-rtc", .driver_data = (kernel_ulong_t)&max77686_drv_data, }, |
f903129b | 699 | { "max77802-rtc", .driver_data = (kernel_ulong_t)&max77802_drv_data, }, |
fca1dd03 JL |
700 | {}, |
701 | }; | |
2d0cca0d | 702 | MODULE_DEVICE_TABLE(platform, rtc_id); |
fca1dd03 JL |
703 | |
704 | static struct platform_driver max77686_rtc_driver = { | |
705 | .driver = { | |
706 | .name = "max77686-rtc", | |
e7f7fc73 | 707 | .pm = &max77686_rtc_pm_ops, |
fca1dd03 JL |
708 | }, |
709 | .probe = max77686_rtc_probe, | |
fca1dd03 JL |
710 | .id_table = rtc_id, |
711 | }; | |
712 | ||
0c58ff58 | 713 | module_platform_driver(max77686_rtc_driver); |
fca1dd03 JL |
714 | |
715 | MODULE_DESCRIPTION("Maxim MAX77686 RTC driver"); | |
f5b1d3c5 | 716 | MODULE_AUTHOR("Chiwoong Byun <woong.byun@samsung.com>"); |
fca1dd03 | 717 | MODULE_LICENSE("GPL"); |