blob: 0ba56b7cb3823d61b88ae442f73841bd6b014d05 [file] [log] [blame]
Sangbeom Kim5bccae62013-11-12 15:11:04 -08001/*
2 * Copyright (c) 2013 Samsung Electronics Co., Ltd
3 * http://www.samsung.com
4 *
5 * Copyright (C) 2013 Google, Inc
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 */
17
18#include <linux/module.h>
19#include <linux/i2c.h>
20#include <linux/slab.h>
21#include <linux/bcd.h>
22#include <linux/bitops.h>
23#include <linux/regmap.h>
24#include <linux/rtc.h>
25#include <linux/delay.h>
26#include <linux/platform_device.h>
27#include <linux/mfd/samsung/core.h>
28#include <linux/mfd/samsung/irq.h>
29#include <linux/mfd/samsung/rtc.h>
30
Krzysztof Kozlowskid73238d2013-12-12 17:12:28 -080031/*
32 * Maximum number of retries for checking changes in UDR field
33 * of SEC_RTC_UDR_CON register (to limit possible endless loop).
34 *
35 * After writing to RTC registers (setting time or alarm) read the UDR field
36 * in SEC_RTC_UDR_CON register. UDR is auto-cleared when data have
37 * been transferred.
38 */
39#define UDR_READ_RETRY_CNT 5
40
Sangbeom Kim5bccae62013-11-12 15:11:04 -080041struct s5m_rtc_info {
42 struct device *dev;
43 struct sec_pmic_dev *s5m87xx;
Geert Uytterhoeven5ccb7d72013-12-12 17:12:25 -080044 struct regmap *regmap;
Sangbeom Kim5bccae62013-11-12 15:11:04 -080045 struct rtc_device *rtc_dev;
46 int irq;
47 int device_type;
48 int rtc_24hr_mode;
49 bool wtsr_smpl;
50};
51
52static void s5m8767_data_to_tm(u8 *data, struct rtc_time *tm,
53 int rtc_24hr_mode)
54{
55 tm->tm_sec = data[RTC_SEC] & 0x7f;
56 tm->tm_min = data[RTC_MIN] & 0x7f;
57 if (rtc_24hr_mode) {
58 tm->tm_hour = data[RTC_HOUR] & 0x1f;
59 } else {
60 tm->tm_hour = data[RTC_HOUR] & 0x0f;
61 if (data[RTC_HOUR] & HOUR_PM_MASK)
62 tm->tm_hour += 12;
63 }
64
65 tm->tm_wday = ffs(data[RTC_WEEKDAY] & 0x7f);
66 tm->tm_mday = data[RTC_DATE] & 0x1f;
67 tm->tm_mon = (data[RTC_MONTH] & 0x0f) - 1;
68 tm->tm_year = (data[RTC_YEAR1] & 0x7f) + 100;
69 tm->tm_yday = 0;
70 tm->tm_isdst = 0;
71}
72
73static int s5m8767_tm_to_data(struct rtc_time *tm, u8 *data)
74{
75 data[RTC_SEC] = tm->tm_sec;
76 data[RTC_MIN] = tm->tm_min;
77
78 if (tm->tm_hour >= 12)
79 data[RTC_HOUR] = tm->tm_hour | HOUR_PM_MASK;
80 else
81 data[RTC_HOUR] = tm->tm_hour & ~HOUR_PM_MASK;
82
83 data[RTC_WEEKDAY] = 1 << tm->tm_wday;
84 data[RTC_DATE] = tm->tm_mday;
85 data[RTC_MONTH] = tm->tm_mon + 1;
86 data[RTC_YEAR1] = tm->tm_year > 100 ? (tm->tm_year - 100) : 0;
87
88 if (tm->tm_year < 100) {
89 pr_err("s5m8767 RTC cannot handle the year %d.\n",
90 1900 + tm->tm_year);
91 return -EINVAL;
92 } else {
93 return 0;
94 }
95}
96
Krzysztof Kozlowskid73238d2013-12-12 17:12:28 -080097/*
98 * Read RTC_UDR_CON register and wait till UDR field is cleared.
99 * This indicates that time/alarm update ended.
100 */
101static inline int s5m8767_wait_for_udr_update(struct s5m_rtc_info *info)
102{
103 int ret, retry = UDR_READ_RETRY_CNT;
104 unsigned int data;
105
106 do {
107 ret = regmap_read(info->regmap, SEC_RTC_UDR_CON, &data);
108 } while (--retry && (data & RTC_UDR_MASK) && !ret);
109
110 if (!retry)
111 dev_err(info->dev, "waiting for UDR update, reached max number of retries\n");
112
113 return ret;
114}
115
Sangbeom Kim5bccae62013-11-12 15:11:04 -0800116static inline int s5m8767_rtc_set_time_reg(struct s5m_rtc_info *info)
117{
118 int ret;
119 unsigned int data;
120
Geert Uytterhoeven5ccb7d72013-12-12 17:12:25 -0800121 ret = regmap_read(info->regmap, SEC_RTC_UDR_CON, &data);
Sangbeom Kim5bccae62013-11-12 15:11:04 -0800122 if (ret < 0) {
123 dev_err(info->dev, "failed to read update reg(%d)\n", ret);
124 return ret;
125 }
126
127 data |= RTC_TIME_EN_MASK;
128 data |= RTC_UDR_MASK;
129
Geert Uytterhoeven5ccb7d72013-12-12 17:12:25 -0800130 ret = regmap_write(info->regmap, SEC_RTC_UDR_CON, data);
Sangbeom Kim5bccae62013-11-12 15:11:04 -0800131 if (ret < 0) {
132 dev_err(info->dev, "failed to write update reg(%d)\n", ret);
133 return ret;
134 }
135
Krzysztof Kozlowskid73238d2013-12-12 17:12:28 -0800136 ret = s5m8767_wait_for_udr_update(info);
Sangbeom Kim5bccae62013-11-12 15:11:04 -0800137
138 return ret;
139}
140
141static inline int s5m8767_rtc_set_alarm_reg(struct s5m_rtc_info *info)
142{
143 int ret;
144 unsigned int data;
145
Geert Uytterhoeven5ccb7d72013-12-12 17:12:25 -0800146 ret = regmap_read(info->regmap, SEC_RTC_UDR_CON, &data);
Sangbeom Kim5bccae62013-11-12 15:11:04 -0800147 if (ret < 0) {
148 dev_err(info->dev, "%s: fail to read update reg(%d)\n",
149 __func__, ret);
150 return ret;
151 }
152
153 data &= ~RTC_TIME_EN_MASK;
154 data |= RTC_UDR_MASK;
155
Geert Uytterhoeven5ccb7d72013-12-12 17:12:25 -0800156 ret = regmap_write(info->regmap, SEC_RTC_UDR_CON, data);
Sangbeom Kim5bccae62013-11-12 15:11:04 -0800157 if (ret < 0) {
158 dev_err(info->dev, "%s: fail to write update reg(%d)\n",
159 __func__, ret);
160 return ret;
161 }
162
Krzysztof Kozlowskid73238d2013-12-12 17:12:28 -0800163 ret = s5m8767_wait_for_udr_update(info);
Sangbeom Kim5bccae62013-11-12 15:11:04 -0800164
165 return ret;
166}
167
168static void s5m8763_data_to_tm(u8 *data, struct rtc_time *tm)
169{
170 tm->tm_sec = bcd2bin(data[RTC_SEC]);
171 tm->tm_min = bcd2bin(data[RTC_MIN]);
172
173 if (data[RTC_HOUR] & HOUR_12) {
174 tm->tm_hour = bcd2bin(data[RTC_HOUR] & 0x1f);
175 if (data[RTC_HOUR] & HOUR_PM)
176 tm->tm_hour += 12;
177 } else {
178 tm->tm_hour = bcd2bin(data[RTC_HOUR] & 0x3f);
179 }
180
181 tm->tm_wday = data[RTC_WEEKDAY] & 0x07;
182 tm->tm_mday = bcd2bin(data[RTC_DATE]);
183 tm->tm_mon = bcd2bin(data[RTC_MONTH]);
184 tm->tm_year = bcd2bin(data[RTC_YEAR1]) + bcd2bin(data[RTC_YEAR2]) * 100;
185 tm->tm_year -= 1900;
186}
187
188static void s5m8763_tm_to_data(struct rtc_time *tm, u8 *data)
189{
190 data[RTC_SEC] = bin2bcd(tm->tm_sec);
191 data[RTC_MIN] = bin2bcd(tm->tm_min);
192 data[RTC_HOUR] = bin2bcd(tm->tm_hour);
193 data[RTC_WEEKDAY] = tm->tm_wday;
194 data[RTC_DATE] = bin2bcd(tm->tm_mday);
195 data[RTC_MONTH] = bin2bcd(tm->tm_mon);
196 data[RTC_YEAR1] = bin2bcd(tm->tm_year % 100);
197 data[RTC_YEAR2] = bin2bcd((tm->tm_year + 1900) / 100);
198}
199
200static int s5m_rtc_read_time(struct device *dev, struct rtc_time *tm)
201{
202 struct s5m_rtc_info *info = dev_get_drvdata(dev);
203 u8 data[8];
204 int ret;
205
Geert Uytterhoeven5ccb7d72013-12-12 17:12:25 -0800206 ret = regmap_bulk_read(info->regmap, SEC_RTC_SEC, data, 8);
Sangbeom Kim5bccae62013-11-12 15:11:04 -0800207 if (ret < 0)
208 return ret;
209
210 switch (info->device_type) {
211 case S5M8763X:
212 s5m8763_data_to_tm(data, tm);
213 break;
214
215 case S5M8767X:
216 s5m8767_data_to_tm(data, tm, info->rtc_24hr_mode);
217 break;
218
219 default:
220 return -EINVAL;
221 }
222
223 dev_dbg(dev, "%s: %d/%d/%d %d:%d:%d(%d)\n", __func__,
224 1900 + tm->tm_year, 1 + tm->tm_mon, tm->tm_mday,
225 tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_wday);
226
227 return rtc_valid_tm(tm);
228}
229
230static int s5m_rtc_set_time(struct device *dev, struct rtc_time *tm)
231{
232 struct s5m_rtc_info *info = dev_get_drvdata(dev);
233 u8 data[8];
234 int ret = 0;
235
236 switch (info->device_type) {
237 case S5M8763X:
238 s5m8763_tm_to_data(tm, data);
239 break;
240 case S5M8767X:
241 ret = s5m8767_tm_to_data(tm, data);
242 break;
243 default:
244 return -EINVAL;
245 }
246
247 if (ret < 0)
248 return ret;
249
250 dev_dbg(dev, "%s: %d/%d/%d %d:%d:%d(%d)\n", __func__,
251 1900 + tm->tm_year, 1 + tm->tm_mon, tm->tm_mday,
252 tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_wday);
253
Geert Uytterhoeven5ccb7d72013-12-12 17:12:25 -0800254 ret = regmap_raw_write(info->regmap, SEC_RTC_SEC, data, 8);
Sangbeom Kim5bccae62013-11-12 15:11:04 -0800255 if (ret < 0)
256 return ret;
257
258 ret = s5m8767_rtc_set_time_reg(info);
259
260 return ret;
261}
262
263static int s5m_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
264{
265 struct s5m_rtc_info *info = dev_get_drvdata(dev);
266 u8 data[8];
267 unsigned int val;
268 int ret, i;
269
Geert Uytterhoeven5ccb7d72013-12-12 17:12:25 -0800270 ret = regmap_bulk_read(info->regmap, SEC_ALARM0_SEC, data, 8);
Sangbeom Kim5bccae62013-11-12 15:11:04 -0800271 if (ret < 0)
272 return ret;
273
274 switch (info->device_type) {
275 case S5M8763X:
276 s5m8763_data_to_tm(data, &alrm->time);
Geert Uytterhoeven5ccb7d72013-12-12 17:12:25 -0800277 ret = regmap_read(info->regmap, SEC_ALARM0_CONF, &val);
Sangbeom Kim5bccae62013-11-12 15:11:04 -0800278 if (ret < 0)
279 return ret;
280
281 alrm->enabled = !!val;
282
Geert Uytterhoeven5ccb7d72013-12-12 17:12:25 -0800283 ret = regmap_read(info->regmap, SEC_RTC_STATUS, &val);
Sangbeom Kim5bccae62013-11-12 15:11:04 -0800284 if (ret < 0)
285 return ret;
286
287 break;
288
289 case S5M8767X:
290 s5m8767_data_to_tm(data, &alrm->time, info->rtc_24hr_mode);
291 dev_dbg(dev, "%s: %d/%d/%d %d:%d:%d(%d)\n", __func__,
292 1900 + alrm->time.tm_year, 1 + alrm->time.tm_mon,
293 alrm->time.tm_mday, alrm->time.tm_hour,
294 alrm->time.tm_min, alrm->time.tm_sec,
295 alrm->time.tm_wday);
296
297 alrm->enabled = 0;
298 for (i = 0; i < 7; i++) {
299 if (data[i] & ALARM_ENABLE_MASK) {
300 alrm->enabled = 1;
301 break;
302 }
303 }
304
305 alrm->pending = 0;
Geert Uytterhoeven5ccb7d72013-12-12 17:12:25 -0800306 ret = regmap_read(info->regmap, SEC_RTC_STATUS, &val);
Sangbeom Kim5bccae62013-11-12 15:11:04 -0800307 if (ret < 0)
308 return ret;
309 break;
310
311 default:
312 return -EINVAL;
313 }
314
315 if (val & ALARM0_STATUS)
316 alrm->pending = 1;
317 else
318 alrm->pending = 0;
319
320 return 0;
321}
322
323static int s5m_rtc_stop_alarm(struct s5m_rtc_info *info)
324{
325 u8 data[8];
326 int ret, i;
327 struct rtc_time tm;
328
Geert Uytterhoeven5ccb7d72013-12-12 17:12:25 -0800329 ret = regmap_bulk_read(info->regmap, SEC_ALARM0_SEC, data, 8);
Sangbeom Kim5bccae62013-11-12 15:11:04 -0800330 if (ret < 0)
331 return ret;
332
333 s5m8767_data_to_tm(data, &tm, info->rtc_24hr_mode);
334 dev_dbg(info->dev, "%s: %d/%d/%d %d:%d:%d(%d)\n", __func__,
335 1900 + tm.tm_year, 1 + tm.tm_mon, tm.tm_mday,
336 tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_wday);
337
338 switch (info->device_type) {
339 case S5M8763X:
Geert Uytterhoeven5ccb7d72013-12-12 17:12:25 -0800340 ret = regmap_write(info->regmap, SEC_ALARM0_CONF, 0);
Sangbeom Kim5bccae62013-11-12 15:11:04 -0800341 break;
342
343 case S5M8767X:
344 for (i = 0; i < 7; i++)
345 data[i] &= ~ALARM_ENABLE_MASK;
346
Geert Uytterhoeven5ccb7d72013-12-12 17:12:25 -0800347 ret = regmap_raw_write(info->regmap, SEC_ALARM0_SEC, data, 8);
Sangbeom Kim5bccae62013-11-12 15:11:04 -0800348 if (ret < 0)
349 return ret;
350
351 ret = s5m8767_rtc_set_alarm_reg(info);
352
353 break;
354
355 default:
356 return -EINVAL;
357 }
358
359 return ret;
360}
361
362static int s5m_rtc_start_alarm(struct s5m_rtc_info *info)
363{
364 int ret;
365 u8 data[8];
366 u8 alarm0_conf;
367 struct rtc_time tm;
368
Geert Uytterhoeven5ccb7d72013-12-12 17:12:25 -0800369 ret = regmap_bulk_read(info->regmap, SEC_ALARM0_SEC, data, 8);
Sangbeom Kim5bccae62013-11-12 15:11:04 -0800370 if (ret < 0)
371 return ret;
372
373 s5m8767_data_to_tm(data, &tm, info->rtc_24hr_mode);
374 dev_dbg(info->dev, "%s: %d/%d/%d %d:%d:%d(%d)\n", __func__,
375 1900 + tm.tm_year, 1 + tm.tm_mon, tm.tm_mday,
376 tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_wday);
377
378 switch (info->device_type) {
379 case S5M8763X:
380 alarm0_conf = 0x77;
Geert Uytterhoeven5ccb7d72013-12-12 17:12:25 -0800381 ret = regmap_write(info->regmap, SEC_ALARM0_CONF, alarm0_conf);
Sangbeom Kim5bccae62013-11-12 15:11:04 -0800382 break;
383
384 case S5M8767X:
385 data[RTC_SEC] |= ALARM_ENABLE_MASK;
386 data[RTC_MIN] |= ALARM_ENABLE_MASK;
387 data[RTC_HOUR] |= ALARM_ENABLE_MASK;
388 data[RTC_WEEKDAY] &= ~ALARM_ENABLE_MASK;
389 if (data[RTC_DATE] & 0x1f)
390 data[RTC_DATE] |= ALARM_ENABLE_MASK;
391 if (data[RTC_MONTH] & 0xf)
392 data[RTC_MONTH] |= ALARM_ENABLE_MASK;
393 if (data[RTC_YEAR1] & 0x7f)
394 data[RTC_YEAR1] |= ALARM_ENABLE_MASK;
395
Geert Uytterhoeven5ccb7d72013-12-12 17:12:25 -0800396 ret = regmap_raw_write(info->regmap, SEC_ALARM0_SEC, data, 8);
Sangbeom Kim5bccae62013-11-12 15:11:04 -0800397 if (ret < 0)
398 return ret;
399 ret = s5m8767_rtc_set_alarm_reg(info);
400
401 break;
402
403 default:
404 return -EINVAL;
405 }
406
407 return ret;
408}
409
410static int s5m_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
411{
412 struct s5m_rtc_info *info = dev_get_drvdata(dev);
413 u8 data[8];
414 int ret;
415
416 switch (info->device_type) {
417 case S5M8763X:
418 s5m8763_tm_to_data(&alrm->time, data);
419 break;
420
421 case S5M8767X:
422 s5m8767_tm_to_data(&alrm->time, data);
423 break;
424
425 default:
426 return -EINVAL;
427 }
428
429 dev_dbg(dev, "%s: %d/%d/%d %d:%d:%d(%d)\n", __func__,
430 1900 + alrm->time.tm_year, 1 + alrm->time.tm_mon,
431 alrm->time.tm_mday, alrm->time.tm_hour, alrm->time.tm_min,
432 alrm->time.tm_sec, alrm->time.tm_wday);
433
434 ret = s5m_rtc_stop_alarm(info);
435 if (ret < 0)
436 return ret;
437
Geert Uytterhoeven5ccb7d72013-12-12 17:12:25 -0800438 ret = regmap_raw_write(info->regmap, SEC_ALARM0_SEC, data, 8);
Sangbeom Kim5bccae62013-11-12 15:11:04 -0800439 if (ret < 0)
440 return ret;
441
442 ret = s5m8767_rtc_set_alarm_reg(info);
443 if (ret < 0)
444 return ret;
445
446 if (alrm->enabled)
447 ret = s5m_rtc_start_alarm(info);
448
449 return ret;
450}
451
452static int s5m_rtc_alarm_irq_enable(struct device *dev,
453 unsigned int enabled)
454{
455 struct s5m_rtc_info *info = dev_get_drvdata(dev);
456
457 if (enabled)
458 return s5m_rtc_start_alarm(info);
459 else
460 return s5m_rtc_stop_alarm(info);
461}
462
463static irqreturn_t s5m_rtc_alarm_irq(int irq, void *data)
464{
465 struct s5m_rtc_info *info = data;
466
467 rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF);
468
469 return IRQ_HANDLED;
470}
471
472static const struct rtc_class_ops s5m_rtc_ops = {
473 .read_time = s5m_rtc_read_time,
474 .set_time = s5m_rtc_set_time,
475 .read_alarm = s5m_rtc_read_alarm,
476 .set_alarm = s5m_rtc_set_alarm,
477 .alarm_irq_enable = s5m_rtc_alarm_irq_enable,
478};
479
480static void s5m_rtc_enable_wtsr(struct s5m_rtc_info *info, bool enable)
481{
482 int ret;
Geert Uytterhoeven5ccb7d72013-12-12 17:12:25 -0800483 ret = regmap_update_bits(info->regmap, SEC_WTSR_SMPL_CNTL,
Sangbeom Kim5bccae62013-11-12 15:11:04 -0800484 WTSR_ENABLE_MASK,
485 enable ? WTSR_ENABLE_MASK : 0);
486 if (ret < 0)
487 dev_err(info->dev, "%s: fail to update WTSR reg(%d)\n",
488 __func__, ret);
489}
490
491static void s5m_rtc_enable_smpl(struct s5m_rtc_info *info, bool enable)
492{
493 int ret;
Geert Uytterhoeven5ccb7d72013-12-12 17:12:25 -0800494 ret = regmap_update_bits(info->regmap, SEC_WTSR_SMPL_CNTL,
Sangbeom Kim5bccae62013-11-12 15:11:04 -0800495 SMPL_ENABLE_MASK,
496 enable ? SMPL_ENABLE_MASK : 0);
497 if (ret < 0)
498 dev_err(info->dev, "%s: fail to update SMPL reg(%d)\n",
499 __func__, ret);
500}
501
502static int s5m8767_rtc_init_reg(struct s5m_rtc_info *info)
503{
504 u8 data[2];
505 unsigned int tp_read;
506 int ret;
507 struct rtc_time tm;
508
Geert Uytterhoeven5ccb7d72013-12-12 17:12:25 -0800509 ret = regmap_read(info->regmap, SEC_RTC_UDR_CON, &tp_read);
Sangbeom Kim5bccae62013-11-12 15:11:04 -0800510 if (ret < 0) {
511 dev_err(info->dev, "%s: fail to read control reg(%d)\n",
512 __func__, ret);
513 return ret;
514 }
515
516 /* Set RTC control register : Binary mode, 24hour mode */
517 data[0] = (1 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT);
518 data[1] = (0 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT);
519
520 info->rtc_24hr_mode = 1;
Geert Uytterhoeven5ccb7d72013-12-12 17:12:25 -0800521 ret = regmap_raw_write(info->regmap, SEC_ALARM0_CONF, data, 2);
Sangbeom Kim5bccae62013-11-12 15:11:04 -0800522 if (ret < 0) {
523 dev_err(info->dev, "%s: fail to write controlm reg(%d)\n",
524 __func__, ret);
525 return ret;
526 }
527
528 /* In first boot time, Set rtc time to 1/1/2012 00:00:00(SUN) */
529 if ((tp_read & RTC_TCON_MASK) == 0) {
530 dev_dbg(info->dev, "rtc init\n");
531 tm.tm_sec = 0;
532 tm.tm_min = 0;
533 tm.tm_hour = 0;
534 tm.tm_wday = 0;
535 tm.tm_mday = 1;
536 tm.tm_mon = 0;
537 tm.tm_year = 112;
538 tm.tm_yday = 0;
539 tm.tm_isdst = 0;
540 ret = s5m_rtc_set_time(info->dev, &tm);
541 }
542
Geert Uytterhoeven5ccb7d72013-12-12 17:12:25 -0800543 ret = regmap_update_bits(info->regmap, SEC_RTC_UDR_CON,
Sangbeom Kim5bccae62013-11-12 15:11:04 -0800544 RTC_TCON_MASK, tp_read | RTC_TCON_MASK);
545 if (ret < 0)
546 dev_err(info->dev, "%s: fail to update TCON reg(%d)\n",
547 __func__, ret);
548
549 return ret;
550}
551
552static int s5m_rtc_probe(struct platform_device *pdev)
553{
554 struct sec_pmic_dev *s5m87xx = dev_get_drvdata(pdev->dev.parent);
555 struct sec_platform_data *pdata = s5m87xx->pdata;
556 struct s5m_rtc_info *info;
557 int ret;
558
559 if (!pdata) {
560 dev_err(pdev->dev.parent, "Platform data not supplied\n");
561 return -ENODEV;
562 }
563
564 info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
565 if (!info)
566 return -ENOMEM;
567
568 info->dev = &pdev->dev;
569 info->s5m87xx = s5m87xx;
Geert Uytterhoeven5ccb7d72013-12-12 17:12:25 -0800570 info->regmap = s5m87xx->regmap;
Sangbeom Kim5bccae62013-11-12 15:11:04 -0800571 info->device_type = s5m87xx->device_type;
572 info->wtsr_smpl = s5m87xx->wtsr_smpl;
573
574 switch (pdata->device_type) {
575 case S5M8763X:
Krzysztof Kozlowski7b003be2013-12-12 17:12:26 -0800576 info->irq = regmap_irq_get_virq(s5m87xx->irq_data,
577 S5M8763_IRQ_ALARM0);
Sangbeom Kim5bccae62013-11-12 15:11:04 -0800578 break;
579
580 case S5M8767X:
Krzysztof Kozlowski7b003be2013-12-12 17:12:26 -0800581 info->irq = regmap_irq_get_virq(s5m87xx->irq_data,
582 S5M8767_IRQ_RTCA1);
Sangbeom Kim5bccae62013-11-12 15:11:04 -0800583 break;
584
585 default:
586 ret = -EINVAL;
587 dev_err(&pdev->dev, "Unsupported device type: %d\n", ret);
588 return ret;
589 }
590
591 platform_set_drvdata(pdev, info);
592
593 ret = s5m8767_rtc_init_reg(info);
594
595 if (info->wtsr_smpl) {
596 s5m_rtc_enable_wtsr(info, true);
597 s5m_rtc_enable_smpl(info, true);
598 }
599
600 device_init_wakeup(&pdev->dev, 1);
601
602 info->rtc_dev = devm_rtc_device_register(&pdev->dev, "s5m-rtc",
603 &s5m_rtc_ops, THIS_MODULE);
604
605 if (IS_ERR(info->rtc_dev))
606 return PTR_ERR(info->rtc_dev);
607
608 ret = devm_request_threaded_irq(&pdev->dev, info->irq, NULL,
609 s5m_rtc_alarm_irq, 0, "rtc-alarm0",
610 info);
611 if (ret < 0)
612 dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n",
613 info->irq, ret);
614
615 return ret;
616}
617
618static void s5m_rtc_shutdown(struct platform_device *pdev)
619{
620 struct s5m_rtc_info *info = platform_get_drvdata(pdev);
621 int i;
622 unsigned int val = 0;
623 if (info->wtsr_smpl) {
624 for (i = 0; i < 3; i++) {
625 s5m_rtc_enable_wtsr(info, false);
Geert Uytterhoeven5ccb7d72013-12-12 17:12:25 -0800626 regmap_read(info->regmap, SEC_WTSR_SMPL_CNTL, &val);
Sangbeom Kim5bccae62013-11-12 15:11:04 -0800627 pr_debug("%s: WTSR_SMPL reg(0x%02x)\n", __func__, val);
628 if (val & WTSR_ENABLE_MASK)
629 pr_emerg("%s: fail to disable WTSR\n",
630 __func__);
631 else {
632 pr_info("%s: success to disable WTSR\n",
633 __func__);
634 break;
635 }
636 }
637 }
638 /* Disable SMPL when power off */
639 s5m_rtc_enable_smpl(info, false);
640}
641
Krzysztof Kozlowski222ead72013-12-12 17:12:30 -0800642static int s5m_rtc_resume(struct device *dev)
643{
644 struct s5m_rtc_info *info = dev_get_drvdata(dev);
645 int ret = 0;
646
647 if (device_may_wakeup(dev))
648 ret = disable_irq_wake(info->irq);
649
650 return ret;
651}
652
653static int s5m_rtc_suspend(struct device *dev)
654{
655 struct s5m_rtc_info *info = dev_get_drvdata(dev);
656 int ret = 0;
657
658 if (device_may_wakeup(dev))
659 ret = enable_irq_wake(info->irq);
660
661 return ret;
662}
663
664static SIMPLE_DEV_PM_OPS(s5m_rtc_pm_ops, s5m_rtc_suspend, s5m_rtc_resume);
665
Sangbeom Kim5bccae62013-11-12 15:11:04 -0800666static const struct platform_device_id s5m_rtc_id[] = {
667 { "s5m-rtc", 0 },
668};
669
670static struct platform_driver s5m_rtc_driver = {
671 .driver = {
672 .name = "s5m-rtc",
673 .owner = THIS_MODULE,
Krzysztof Kozlowski222ead72013-12-12 17:12:30 -0800674 .pm = &s5m_rtc_pm_ops,
Sangbeom Kim5bccae62013-11-12 15:11:04 -0800675 },
676 .probe = s5m_rtc_probe,
677 .shutdown = s5m_rtc_shutdown,
678 .id_table = s5m_rtc_id,
679};
680
681module_platform_driver(s5m_rtc_driver);
682
683/* Module information */
684MODULE_AUTHOR("Sangbeom Kim <sbkim73@samsung.com>");
685MODULE_DESCRIPTION("Samsung S5M RTC driver");
686MODULE_LICENSE("GPL");
687MODULE_ALIAS("platform:s5m-rtc");