blob: 5e924f3cde9034f6d43af80f05c80b617efe4a00 [file] [log] [blame]
Jonghwa Leefca1dd02013-02-21 16:44:26 -08001/*
Javier Martinez Canillasf9031292016-01-27 00:36:42 -03002 * RTC driver for Maxim MAX77686 and MAX77802
Jonghwa Leefca1dd02013-02-21 16:44:26 -08003 *
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
Laxman Dewanganf3937542016-02-09 22:56:34 +053015#include <linux/i2c.h>
Jonghwa Leefca1dd02013-02-21 16:44:26 -080016#include <linux/slab.h>
17#include <linux/rtc.h>
18#include <linux/delay.h>
19#include <linux/mutex.h>
20#include <linux/module.h>
21#include <linux/platform_device.h>
22#include <linux/mfd/max77686-private.h>
23#include <linux/irqdomain.h>
24#include <linux/regmap.h>
25
Laxman Dewanganf3937542016-02-09 22:56:34 +053026#define MAX77686_I2C_ADDR_RTC (0x0C >> 1)
27#define MAX77686_INVALID_I2C_ADDR (-1)
28
Jonghwa Leefca1dd02013-02-21 16:44:26 -080029/* RTC Control Register */
30#define BCD_EN_SHIFT 0
Laxman Dewangan0b4f8b02016-02-09 22:56:30 +053031#define BCD_EN_MASK BIT(BCD_EN_SHIFT)
Jonghwa Leefca1dd02013-02-21 16:44:26 -080032#define MODEL24_SHIFT 1
Laxman Dewangan0b4f8b02016-02-09 22:56:30 +053033#define MODEL24_MASK BIT(MODEL24_SHIFT)
Jonghwa Leefca1dd02013-02-21 16:44:26 -080034/* RTC Update Register1 */
35#define RTC_UDR_SHIFT 0
Laxman Dewangan0b4f8b02016-02-09 22:56:30 +053036#define RTC_UDR_MASK BIT(RTC_UDR_SHIFT)
Jonghwa Leefca1dd02013-02-21 16:44:26 -080037#define RTC_RBUDR_SHIFT 4
Laxman Dewangan0b4f8b02016-02-09 22:56:30 +053038#define RTC_RBUDR_MASK BIT(RTC_RBUDR_SHIFT)
Jonghwa Leefca1dd02013-02-21 16:44:26 -080039/* RTC Hour register */
40#define HOUR_PM_SHIFT 6
Laxman Dewangan0b4f8b02016-02-09 22:56:30 +053041#define HOUR_PM_MASK BIT(HOUR_PM_SHIFT)
Jonghwa Leefca1dd02013-02-21 16:44:26 -080042/* RTC Alarm Enable */
43#define ALARM_ENABLE_SHIFT 7
Laxman Dewangan0b4f8b02016-02-09 22:56:30 +053044#define ALARM_ENABLE_MASK BIT(ALARM_ENABLE_SHIFT)
Jonghwa Leefca1dd02013-02-21 16:44:26 -080045
Javier Martinez Canillasf9031292016-01-27 00:36:42 -030046#define REG_RTC_NONE 0xdeadbeef
47
48/*
49 * MAX77802 has separate register (RTCAE1) for alarm enable instead
50 * using 1 bit from registers RTC{SEC,MIN,HOUR,DAY,MONTH,YEAR,DATE}
51 * as in done in MAX77686.
52 */
53#define MAX77802_ALARM_ENABLE_VALUE 0x77
54
Jonghwa Leefca1dd02013-02-21 16:44:26 -080055enum {
56 RTC_SEC = 0,
57 RTC_MIN,
58 RTC_HOUR,
59 RTC_WEEKDAY,
60 RTC_MONTH,
61 RTC_YEAR,
62 RTC_DATE,
63 RTC_NR_TIME
64};
65
Javier Martinez Canillas01ea01b2016-01-27 00:36:40 -030066struct max77686_rtc_driver_data {
67 /* Minimum usecs needed for a RTC update */
68 unsigned long delay;
69 /* Mask used to read RTC registers value */
70 u8 mask;
Javier Martinez Canillas90a56982016-01-27 00:36:41 -030071 /* Registers offset to I2C addresses map */
72 const unsigned int *map;
Javier Martinez Canillasf9031292016-01-27 00:36:42 -030073 /* Has a separate alarm enable register? */
74 bool alarm_enable_reg;
Laxman Dewanganf3937542016-02-09 22:56:34 +053075 /* I2C address for RTC block */
76 int rtc_i2c_addr;
77 /* RTC IRQ CHIP for regmap */
78 const struct regmap_irq_chip *rtc_irq_chip;
Javier Martinez Canillas01ea01b2016-01-27 00:36:40 -030079};
80
Jonghwa Leefca1dd02013-02-21 16:44:26 -080081struct max77686_rtc_info {
82 struct device *dev;
Jonghwa Leefca1dd02013-02-21 16:44:26 -080083 struct i2c_client *rtc;
84 struct rtc_device *rtc_dev;
85 struct mutex lock;
86
87 struct regmap *regmap;
Laxman Dewanganf604c482016-02-09 22:56:32 +053088 struct regmap *rtc_regmap;
Jonghwa Leefca1dd02013-02-21 16:44:26 -080089
Javier Martinez Canillas01ea01b2016-01-27 00:36:40 -030090 const struct max77686_rtc_driver_data *drv_data;
Laxman Dewanganf3937542016-02-09 22:56:34 +053091 struct regmap_irq_chip_data *rtc_irq_data;
Javier Martinez Canillas01ea01b2016-01-27 00:36:40 -030092
Laxman Dewanganf3937542016-02-09 22:56:34 +053093 int rtc_irq;
Jonghwa Leefca1dd02013-02-21 16:44:26 -080094 int virq;
95 int rtc_24hr_mode;
96};
97
98enum MAX77686_RTC_OP {
99 MAX77686_RTC_WRITE,
100 MAX77686_RTC_READ,
101};
102
Javier Martinez Canillas90a56982016-01-27 00:36:41 -0300103/* These are not registers but just offsets that are mapped to addresses */
104enum max77686_rtc_reg_offset {
105 REG_RTC_CONTROLM = 0,
106 REG_RTC_CONTROL,
107 REG_RTC_UPDATE0,
108 REG_WTSR_SMPL_CNTL,
109 REG_RTC_SEC,
110 REG_RTC_MIN,
111 REG_RTC_HOUR,
112 REG_RTC_WEEKDAY,
113 REG_RTC_MONTH,
114 REG_RTC_YEAR,
115 REG_RTC_DATE,
116 REG_ALARM1_SEC,
117 REG_ALARM1_MIN,
118 REG_ALARM1_HOUR,
119 REG_ALARM1_WEEKDAY,
120 REG_ALARM1_MONTH,
121 REG_ALARM1_YEAR,
122 REG_ALARM1_DATE,
123 REG_ALARM2_SEC,
124 REG_ALARM2_MIN,
125 REG_ALARM2_HOUR,
126 REG_ALARM2_WEEKDAY,
127 REG_ALARM2_MONTH,
128 REG_ALARM2_YEAR,
129 REG_ALARM2_DATE,
Javier Martinez Canillasf9031292016-01-27 00:36:42 -0300130 REG_RTC_AE1,
Javier Martinez Canillas90a56982016-01-27 00:36:41 -0300131 REG_RTC_END,
132};
133
134/* Maps RTC registers offset to the MAX77686 register addresses */
135static const unsigned int max77686_map[REG_RTC_END] = {
136 [REG_RTC_CONTROLM] = MAX77686_RTC_CONTROLM,
137 [REG_RTC_CONTROL] = MAX77686_RTC_CONTROL,
138 [REG_RTC_UPDATE0] = MAX77686_RTC_UPDATE0,
139 [REG_WTSR_SMPL_CNTL] = MAX77686_WTSR_SMPL_CNTL,
140 [REG_RTC_SEC] = MAX77686_RTC_SEC,
141 [REG_RTC_MIN] = MAX77686_RTC_MIN,
142 [REG_RTC_HOUR] = MAX77686_RTC_HOUR,
143 [REG_RTC_WEEKDAY] = MAX77686_RTC_WEEKDAY,
144 [REG_RTC_MONTH] = MAX77686_RTC_MONTH,
145 [REG_RTC_YEAR] = MAX77686_RTC_YEAR,
146 [REG_RTC_DATE] = MAX77686_RTC_DATE,
147 [REG_ALARM1_SEC] = MAX77686_ALARM1_SEC,
148 [REG_ALARM1_MIN] = MAX77686_ALARM1_MIN,
149 [REG_ALARM1_HOUR] = MAX77686_ALARM1_HOUR,
150 [REG_ALARM1_WEEKDAY] = MAX77686_ALARM1_WEEKDAY,
151 [REG_ALARM1_MONTH] = MAX77686_ALARM1_MONTH,
152 [REG_ALARM1_YEAR] = MAX77686_ALARM1_YEAR,
153 [REG_ALARM1_DATE] = MAX77686_ALARM1_DATE,
154 [REG_ALARM2_SEC] = MAX77686_ALARM2_SEC,
155 [REG_ALARM2_MIN] = MAX77686_ALARM2_MIN,
156 [REG_ALARM2_HOUR] = MAX77686_ALARM2_HOUR,
157 [REG_ALARM2_WEEKDAY] = MAX77686_ALARM2_WEEKDAY,
158 [REG_ALARM2_MONTH] = MAX77686_ALARM2_MONTH,
159 [REG_ALARM2_YEAR] = MAX77686_ALARM2_YEAR,
160 [REG_ALARM2_DATE] = MAX77686_ALARM2_DATE,
Javier Martinez Canillasf9031292016-01-27 00:36:42 -0300161 [REG_RTC_AE1] = REG_RTC_NONE,
Javier Martinez Canillas90a56982016-01-27 00:36:41 -0300162};
163
Laxman Dewanganf3937542016-02-09 22:56:34 +0530164static const struct regmap_irq max77686_rtc_irqs[] = {
165 /* RTC interrupts */
166 { .reg_offset = 0, .mask = MAX77686_RTCINT_RTC60S_MSK, },
167 { .reg_offset = 0, .mask = MAX77686_RTCINT_RTCA1_MSK, },
168 { .reg_offset = 0, .mask = MAX77686_RTCINT_RTCA2_MSK, },
169 { .reg_offset = 0, .mask = MAX77686_RTCINT_SMPL_MSK, },
170 { .reg_offset = 0, .mask = MAX77686_RTCINT_RTC1S_MSK, },
171 { .reg_offset = 0, .mask = MAX77686_RTCINT_WTSR_MSK, },
172};
173
174static const struct regmap_irq_chip max77686_rtc_irq_chip = {
175 .name = "max77686-rtc",
176 .status_base = MAX77686_RTC_INT,
177 .mask_base = MAX77686_RTC_INTM,
178 .num_regs = 1,
179 .irqs = max77686_rtc_irqs,
180 .num_irqs = ARRAY_SIZE(max77686_rtc_irqs),
181};
182
Javier Martinez Canillas01ea01b2016-01-27 00:36:40 -0300183static const struct max77686_rtc_driver_data max77686_drv_data = {
184 .delay = 16000,
185 .mask = 0x7f,
Javier Martinez Canillas90a56982016-01-27 00:36:41 -0300186 .map = max77686_map,
Javier Martinez Canillasf9031292016-01-27 00:36:42 -0300187 .alarm_enable_reg = false,
Laxman Dewanganf3937542016-02-09 22:56:34 +0530188 .rtc_i2c_addr = MAX77686_I2C_ADDR_RTC,
189 .rtc_irq_chip = &max77686_rtc_irq_chip,
Javier Martinez Canillasf9031292016-01-27 00:36:42 -0300190};
191
192static const unsigned int max77802_map[REG_RTC_END] = {
193 [REG_RTC_CONTROLM] = MAX77802_RTC_CONTROLM,
194 [REG_RTC_CONTROL] = MAX77802_RTC_CONTROL,
195 [REG_RTC_UPDATE0] = MAX77802_RTC_UPDATE0,
196 [REG_WTSR_SMPL_CNTL] = MAX77802_WTSR_SMPL_CNTL,
197 [REG_RTC_SEC] = MAX77802_RTC_SEC,
198 [REG_RTC_MIN] = MAX77802_RTC_MIN,
199 [REG_RTC_HOUR] = MAX77802_RTC_HOUR,
200 [REG_RTC_WEEKDAY] = MAX77802_RTC_WEEKDAY,
201 [REG_RTC_MONTH] = MAX77802_RTC_MONTH,
202 [REG_RTC_YEAR] = MAX77802_RTC_YEAR,
203 [REG_RTC_DATE] = MAX77802_RTC_DATE,
204 [REG_ALARM1_SEC] = MAX77802_ALARM1_SEC,
205 [REG_ALARM1_MIN] = MAX77802_ALARM1_MIN,
206 [REG_ALARM1_HOUR] = MAX77802_ALARM1_HOUR,
207 [REG_ALARM1_WEEKDAY] = MAX77802_ALARM1_WEEKDAY,
208 [REG_ALARM1_MONTH] = MAX77802_ALARM1_MONTH,
209 [REG_ALARM1_YEAR] = MAX77802_ALARM1_YEAR,
210 [REG_ALARM1_DATE] = MAX77802_ALARM1_DATE,
211 [REG_ALARM2_SEC] = MAX77802_ALARM2_SEC,
212 [REG_ALARM2_MIN] = MAX77802_ALARM2_MIN,
213 [REG_ALARM2_HOUR] = MAX77802_ALARM2_HOUR,
214 [REG_ALARM2_WEEKDAY] = MAX77802_ALARM2_WEEKDAY,
215 [REG_ALARM2_MONTH] = MAX77802_ALARM2_MONTH,
216 [REG_ALARM2_YEAR] = MAX77802_ALARM2_YEAR,
217 [REG_ALARM2_DATE] = MAX77802_ALARM2_DATE,
218 [REG_RTC_AE1] = MAX77802_RTC_AE1,
219};
220
Laxman Dewanganf3937542016-02-09 22:56:34 +0530221static const struct regmap_irq_chip max77802_rtc_irq_chip = {
222 .name = "max77802-rtc",
223 .status_base = MAX77802_RTC_INT,
224 .mask_base = MAX77802_RTC_INTM,
225 .num_regs = 1,
226 .irqs = max77686_rtc_irqs, /* same masks as 77686 */
227 .num_irqs = ARRAY_SIZE(max77686_rtc_irqs),
228};
229
Javier Martinez Canillasf9031292016-01-27 00:36:42 -0300230static const struct max77686_rtc_driver_data max77802_drv_data = {
231 .delay = 200,
232 .mask = 0xff,
233 .map = max77802_map,
234 .alarm_enable_reg = true,
Laxman Dewanganf3937542016-02-09 22:56:34 +0530235 .rtc_i2c_addr = MAX77686_INVALID_I2C_ADDR,
236 .rtc_irq_chip = &max77802_rtc_irq_chip,
Javier Martinez Canillas01ea01b2016-01-27 00:36:40 -0300237};
238
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800239static void max77686_rtc_data_to_tm(u8 *data, struct rtc_time *tm,
Javier Martinez Canillas01ea01b2016-01-27 00:36:40 -0300240 struct max77686_rtc_info *info)
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800241{
Javier Martinez Canillas01ea01b2016-01-27 00:36:40 -0300242 u8 mask = info->drv_data->mask;
243
244 tm->tm_sec = data[RTC_SEC] & mask;
245 tm->tm_min = data[RTC_MIN] & mask;
Laxman Dewangan0b4f8b02016-02-09 22:56:30 +0530246 if (info->rtc_24hr_mode) {
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800247 tm->tm_hour = data[RTC_HOUR] & 0x1f;
Laxman Dewangan0b4f8b02016-02-09 22:56:30 +0530248 } else {
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800249 tm->tm_hour = data[RTC_HOUR] & 0x0f;
250 if (data[RTC_HOUR] & HOUR_PM_MASK)
251 tm->tm_hour += 12;
252 }
253
Javier Martinez Canillasa20cd882014-10-13 15:53:03 -0700254 /* Only a single bit is set in data[], so fls() would be equivalent */
Javier Martinez Canillas01ea01b2016-01-27 00:36:40 -0300255 tm->tm_wday = ffs(data[RTC_WEEKDAY] & mask) - 1;
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800256 tm->tm_mday = data[RTC_DATE] & 0x1f;
257 tm->tm_mon = (data[RTC_MONTH] & 0x0f) - 1;
Javier Martinez Canillasf9031292016-01-27 00:36:42 -0300258 tm->tm_year = data[RTC_YEAR] & mask;
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800259 tm->tm_yday = 0;
260 tm->tm_isdst = 0;
Javier Martinez Canillasf9031292016-01-27 00:36:42 -0300261
262 /*
263 * MAX77686 uses 1 bit from sec/min/hour/etc RTC registers and the
264 * year values are just 0..99 so add 100 to support up to 2099.
265 */
266 if (!info->drv_data->alarm_enable_reg)
267 tm->tm_year += 100;
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800268}
269
Javier Martinez Canillasf9031292016-01-27 00:36:42 -0300270static int max77686_rtc_tm_to_data(struct rtc_time *tm, u8 *data,
271 struct max77686_rtc_info *info)
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800272{
273 data[RTC_SEC] = tm->tm_sec;
274 data[RTC_MIN] = tm->tm_min;
275 data[RTC_HOUR] = tm->tm_hour;
276 data[RTC_WEEKDAY] = 1 << tm->tm_wday;
277 data[RTC_DATE] = tm->tm_mday;
278 data[RTC_MONTH] = tm->tm_mon + 1;
Javier Martinez Canillasf9031292016-01-27 00:36:42 -0300279
280 if (info->drv_data->alarm_enable_reg) {
281 data[RTC_YEAR] = tm->tm_year;
282 return 0;
283 }
284
Sachin Kamatcdf5f4a2013-07-03 15:05:55 -0700285 data[RTC_YEAR] = tm->tm_year > 100 ? (tm->tm_year - 100) : 0;
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800286
287 if (tm->tm_year < 100) {
Javier Martinez Canillas1e5813b2016-01-27 00:36:43 -0300288 dev_err(info->dev, "RTC cannot handle the year %d.\n",
Joe Perchesa737e8352015-04-16 12:46:14 -0700289 1900 + tm->tm_year);
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800290 return -EINVAL;
291 }
Javier Martinez Canillasf9031292016-01-27 00:36:42 -0300292
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800293 return 0;
294}
295
296static int max77686_rtc_update(struct max77686_rtc_info *info,
Laxman Dewangan0b4f8b02016-02-09 22:56:30 +0530297 enum MAX77686_RTC_OP op)
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800298{
299 int ret;
300 unsigned int data;
Javier Martinez Canillas01ea01b2016-01-27 00:36:40 -0300301 unsigned long delay = info->drv_data->delay;
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800302
303 if (op == MAX77686_RTC_WRITE)
304 data = 1 << RTC_UDR_SHIFT;
305 else
306 data = 1 << RTC_RBUDR_SHIFT;
307
Laxman Dewanganf604c482016-02-09 22:56:32 +0530308 ret = regmap_update_bits(info->rtc_regmap,
Javier Martinez Canillas90a56982016-01-27 00:36:41 -0300309 info->drv_data->map[REG_RTC_UPDATE0],
310 data, data);
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800311 if (ret < 0)
Krzysztof Kozlowskibf035f42016-01-27 15:46:11 +0900312 dev_err(info->dev, "Fail to write update reg(ret=%d, data=0x%x)\n",
313 ret, data);
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800314 else {
Javier Martinez Canillas01ea01b2016-01-27 00:36:40 -0300315 /* Minimum delay required before RTC update. */
316 usleep_range(delay, delay * 2);
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800317 }
318
319 return ret;
320}
321
322static int max77686_rtc_read_time(struct device *dev, struct rtc_time *tm)
323{
324 struct max77686_rtc_info *info = dev_get_drvdata(dev);
325 u8 data[RTC_NR_TIME];
326 int ret;
327
328 mutex_lock(&info->lock);
329
330 ret = max77686_rtc_update(info, MAX77686_RTC_READ);
331 if (ret < 0)
332 goto out;
333
Laxman Dewanganf604c482016-02-09 22:56:32 +0530334 ret = regmap_bulk_read(info->rtc_regmap,
Javier Martinez Canillas90a56982016-01-27 00:36:41 -0300335 info->drv_data->map[REG_RTC_SEC],
336 data, ARRAY_SIZE(data));
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800337 if (ret < 0) {
Krzysztof Kozlowskibf035f42016-01-27 15:46:11 +0900338 dev_err(info->dev, "Fail to read time reg(%d)\n", ret);
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800339 goto out;
340 }
341
Javier Martinez Canillas01ea01b2016-01-27 00:36:40 -0300342 max77686_rtc_data_to_tm(data, tm, info);
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800343
344 ret = rtc_valid_tm(tm);
345
346out:
347 mutex_unlock(&info->lock);
348 return ret;
349}
350
351static int max77686_rtc_set_time(struct device *dev, struct rtc_time *tm)
352{
353 struct max77686_rtc_info *info = dev_get_drvdata(dev);
354 u8 data[RTC_NR_TIME];
355 int ret;
356
Javier Martinez Canillasf9031292016-01-27 00:36:42 -0300357 ret = max77686_rtc_tm_to_data(tm, data, info);
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800358 if (ret < 0)
359 return ret;
360
361 mutex_lock(&info->lock);
362
Laxman Dewanganf604c482016-02-09 22:56:32 +0530363 ret = regmap_bulk_write(info->rtc_regmap,
Javier Martinez Canillas90a56982016-01-27 00:36:41 -0300364 info->drv_data->map[REG_RTC_SEC],
365 data, ARRAY_SIZE(data));
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800366 if (ret < 0) {
Krzysztof Kozlowskibf035f42016-01-27 15:46:11 +0900367 dev_err(info->dev, "Fail to write time reg(%d)\n", ret);
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800368 goto out;
369 }
370
371 ret = max77686_rtc_update(info, MAX77686_RTC_WRITE);
372
373out:
374 mutex_unlock(&info->lock);
375 return ret;
376}
377
378static int max77686_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
379{
380 struct max77686_rtc_info *info = dev_get_drvdata(dev);
381 u8 data[RTC_NR_TIME];
382 unsigned int val;
Javier Martinez Canillas90a56982016-01-27 00:36:41 -0300383 const unsigned int *map = info->drv_data->map;
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800384 int i, ret;
385
386 mutex_lock(&info->lock);
387
388 ret = max77686_rtc_update(info, MAX77686_RTC_READ);
389 if (ret < 0)
390 goto out;
391
Laxman Dewanganf604c482016-02-09 22:56:32 +0530392 ret = regmap_bulk_read(info->rtc_regmap, map[REG_ALARM1_SEC],
393 data, ARRAY_SIZE(data));
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800394 if (ret < 0) {
Krzysztof Kozlowskibf035f42016-01-27 15:46:11 +0900395 dev_err(info->dev, "Fail to read alarm reg(%d)\n", ret);
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800396 goto out;
397 }
398
Javier Martinez Canillas01ea01b2016-01-27 00:36:40 -0300399 max77686_rtc_data_to_tm(data, &alrm->time, info);
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800400
401 alrm->enabled = 0;
Javier Martinez Canillasf9031292016-01-27 00:36:42 -0300402
403 if (info->drv_data->alarm_enable_reg) {
404 if (map[REG_RTC_AE1] == REG_RTC_NONE) {
405 ret = -EINVAL;
406 dev_err(info->dev,
407 "alarm enable register not set(%d)\n", ret);
408 goto out;
409 }
410
Laxman Dewanganf604c482016-02-09 22:56:32 +0530411 ret = regmap_read(info->rtc_regmap, map[REG_RTC_AE1], &val);
Javier Martinez Canillasf9031292016-01-27 00:36:42 -0300412 if (ret < 0) {
413 dev_err(info->dev,
414 "fail to read alarm enable(%d)\n", ret);
415 goto out;
416 }
417
418 if (val)
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800419 alrm->enabled = 1;
Javier Martinez Canillasf9031292016-01-27 00:36:42 -0300420 } else {
421 for (i = 0; i < ARRAY_SIZE(data); i++) {
422 if (data[i] & ALARM_ENABLE_MASK) {
423 alrm->enabled = 1;
424 break;
425 }
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800426 }
427 }
428
429 alrm->pending = 0;
Laxman Dewanganf604c482016-02-09 22:56:32 +0530430 ret = regmap_read(info->regmap, MAX77686_REG_STATUS2, &val);
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800431 if (ret < 0) {
Krzysztof Kozlowskibf035f42016-01-27 15:46:11 +0900432 dev_err(info->dev, "Fail to read status2 reg(%d)\n", ret);
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800433 goto out;
434 }
435
436 if (val & (1 << 4)) /* RTCA1 */
437 alrm->pending = 1;
438
439out:
440 mutex_unlock(&info->lock);
Javier Martinez Canillas7cdffeb2016-01-27 00:36:37 -0300441 return ret;
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800442}
443
444static int max77686_rtc_stop_alarm(struct max77686_rtc_info *info)
445{
446 u8 data[RTC_NR_TIME];
447 int ret, i;
448 struct rtc_time tm;
Javier Martinez Canillas90a56982016-01-27 00:36:41 -0300449 const unsigned int *map = info->drv_data->map;
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800450
451 if (!mutex_is_locked(&info->lock))
452 dev_warn(info->dev, "%s: should have mutex locked\n", __func__);
453
454 ret = max77686_rtc_update(info, MAX77686_RTC_READ);
455 if (ret < 0)
456 goto out;
457
Javier Martinez Canillasf9031292016-01-27 00:36:42 -0300458 if (info->drv_data->alarm_enable_reg) {
459 if (map[REG_RTC_AE1] == REG_RTC_NONE) {
460 ret = -EINVAL;
461 dev_err(info->dev,
462 "alarm enable register not set(%d)\n", ret);
463 goto out;
464 }
465
Laxman Dewanganf604c482016-02-09 22:56:32 +0530466 ret = regmap_write(info->rtc_regmap, map[REG_RTC_AE1], 0);
Javier Martinez Canillasf9031292016-01-27 00:36:42 -0300467 } else {
Laxman Dewanganf604c482016-02-09 22:56:32 +0530468 ret = regmap_bulk_read(info->rtc_regmap, map[REG_ALARM1_SEC],
469 data, ARRAY_SIZE(data));
Javier Martinez Canillasf9031292016-01-27 00:36:42 -0300470 if (ret < 0) {
Krzysztof Kozlowskibf035f42016-01-27 15:46:11 +0900471 dev_err(info->dev, "Fail to read alarm reg(%d)\n", ret);
Javier Martinez Canillasf9031292016-01-27 00:36:42 -0300472 goto out;
473 }
474
475 max77686_rtc_data_to_tm(data, &tm, info);
476
477 for (i = 0; i < ARRAY_SIZE(data); i++)
478 data[i] &= ~ALARM_ENABLE_MASK;
479
Laxman Dewanganf604c482016-02-09 22:56:32 +0530480 ret = regmap_bulk_write(info->rtc_regmap, map[REG_ALARM1_SEC],
481 data, ARRAY_SIZE(data));
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800482 }
483
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800484 if (ret < 0) {
Krzysztof Kozlowskibf035f42016-01-27 15:46:11 +0900485 dev_err(info->dev, "Fail to write alarm reg(%d)\n", ret);
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800486 goto out;
487 }
488
489 ret = max77686_rtc_update(info, MAX77686_RTC_WRITE);
490out:
491 return ret;
492}
493
494static int max77686_rtc_start_alarm(struct max77686_rtc_info *info)
495{
496 u8 data[RTC_NR_TIME];
497 int ret;
498 struct rtc_time tm;
Javier Martinez Canillas90a56982016-01-27 00:36:41 -0300499 const unsigned int *map = info->drv_data->map;
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800500
501 if (!mutex_is_locked(&info->lock))
502 dev_warn(info->dev, "%s: should have mutex locked\n", __func__);
503
504 ret = max77686_rtc_update(info, MAX77686_RTC_READ);
505 if (ret < 0)
506 goto out;
507
Javier Martinez Canillasf9031292016-01-27 00:36:42 -0300508 if (info->drv_data->alarm_enable_reg) {
Laxman Dewanganf604c482016-02-09 22:56:32 +0530509 ret = regmap_write(info->rtc_regmap, map[REG_RTC_AE1],
Javier Martinez Canillasf9031292016-01-27 00:36:42 -0300510 MAX77802_ALARM_ENABLE_VALUE);
511 } else {
Laxman Dewanganf604c482016-02-09 22:56:32 +0530512 ret = regmap_bulk_read(info->rtc_regmap, map[REG_ALARM1_SEC],
513 data, ARRAY_SIZE(data));
Javier Martinez Canillasf9031292016-01-27 00:36:42 -0300514 if (ret < 0) {
Krzysztof Kozlowskibf035f42016-01-27 15:46:11 +0900515 dev_err(info->dev, "Fail to read alarm reg(%d)\n", ret);
Javier Martinez Canillasf9031292016-01-27 00:36:42 -0300516 goto out;
517 }
518
519 max77686_rtc_data_to_tm(data, &tm, info);
520
521 data[RTC_SEC] |= (1 << ALARM_ENABLE_SHIFT);
522 data[RTC_MIN] |= (1 << ALARM_ENABLE_SHIFT);
523 data[RTC_HOUR] |= (1 << ALARM_ENABLE_SHIFT);
524 data[RTC_WEEKDAY] &= ~ALARM_ENABLE_MASK;
525 if (data[RTC_MONTH] & 0xf)
526 data[RTC_MONTH] |= (1 << ALARM_ENABLE_SHIFT);
527 if (data[RTC_YEAR] & info->drv_data->mask)
528 data[RTC_YEAR] |= (1 << ALARM_ENABLE_SHIFT);
529 if (data[RTC_DATE] & 0x1f)
530 data[RTC_DATE] |= (1 << ALARM_ENABLE_SHIFT);
531
Laxman Dewanganf604c482016-02-09 22:56:32 +0530532 ret = regmap_bulk_write(info->rtc_regmap, map[REG_ALARM1_SEC],
533 data, ARRAY_SIZE(data));
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800534 }
535
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800536 if (ret < 0) {
Krzysztof Kozlowskibf035f42016-01-27 15:46:11 +0900537 dev_err(info->dev, "Fail to write alarm reg(%d)\n", ret);
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800538 goto out;
539 }
540
541 ret = max77686_rtc_update(info, MAX77686_RTC_WRITE);
542out:
543 return ret;
544}
545
546static int max77686_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
547{
548 struct max77686_rtc_info *info = dev_get_drvdata(dev);
549 u8 data[RTC_NR_TIME];
550 int ret;
551
Javier Martinez Canillasf9031292016-01-27 00:36:42 -0300552 ret = max77686_rtc_tm_to_data(&alrm->time, data, info);
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800553 if (ret < 0)
554 return ret;
555
556 mutex_lock(&info->lock);
557
558 ret = max77686_rtc_stop_alarm(info);
559 if (ret < 0)
560 goto out;
561
Laxman Dewanganf604c482016-02-09 22:56:32 +0530562 ret = regmap_bulk_write(info->rtc_regmap,
Javier Martinez Canillas90a56982016-01-27 00:36:41 -0300563 info->drv_data->map[REG_ALARM1_SEC],
564 data, ARRAY_SIZE(data));
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800565
566 if (ret < 0) {
Krzysztof Kozlowskibf035f42016-01-27 15:46:11 +0900567 dev_err(info->dev, "Fail to write alarm reg(%d)\n", ret);
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800568 goto out;
569 }
570
571 ret = max77686_rtc_update(info, MAX77686_RTC_WRITE);
572 if (ret < 0)
573 goto out;
574
575 if (alrm->enabled)
576 ret = max77686_rtc_start_alarm(info);
577out:
578 mutex_unlock(&info->lock);
579 return ret;
580}
581
582static int max77686_rtc_alarm_irq_enable(struct device *dev,
Laxman Dewangan0b4f8b02016-02-09 22:56:30 +0530583 unsigned int enabled)
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800584{
585 struct max77686_rtc_info *info = dev_get_drvdata(dev);
586 int ret;
587
588 mutex_lock(&info->lock);
589 if (enabled)
590 ret = max77686_rtc_start_alarm(info);
591 else
592 ret = max77686_rtc_stop_alarm(info);
593 mutex_unlock(&info->lock);
594
595 return ret;
596}
597
598static irqreturn_t max77686_rtc_alarm_irq(int irq, void *data)
599{
600 struct max77686_rtc_info *info = data;
601
Krzysztof Kozlowskibf035f42016-01-27 15:46:11 +0900602 dev_dbg(info->dev, "RTC alarm IRQ: %d\n", irq);
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800603
604 rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF);
605
606 return IRQ_HANDLED;
607}
608
609static const struct rtc_class_ops max77686_rtc_ops = {
610 .read_time = max77686_rtc_read_time,
611 .set_time = max77686_rtc_set_time,
612 .read_alarm = max77686_rtc_read_alarm,
613 .set_alarm = max77686_rtc_set_alarm,
614 .alarm_irq_enable = max77686_rtc_alarm_irq_enable,
615};
616
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800617static int max77686_rtc_init_reg(struct max77686_rtc_info *info)
618{
619 u8 data[2];
620 int ret;
621
622 /* Set RTC control register : Binary mode, 24hour mdoe */
623 data[0] = (1 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT);
624 data[1] = (0 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT);
625
626 info->rtc_24hr_mode = 1;
627
Laxman Dewanganf604c482016-02-09 22:56:32 +0530628 ret = regmap_bulk_write(info->rtc_regmap,
Javier Martinez Canillas90a56982016-01-27 00:36:41 -0300629 info->drv_data->map[REG_RTC_CONTROLM],
630 data, ARRAY_SIZE(data));
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800631 if (ret < 0) {
Krzysztof Kozlowskibf035f42016-01-27 15:46:11 +0900632 dev_err(info->dev, "Fail to write controlm reg(%d)\n", ret);
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800633 return ret;
634 }
635
636 ret = max77686_rtc_update(info, MAX77686_RTC_WRITE);
637 return ret;
638}
639
Laxman Dewanganf3937542016-02-09 22:56:34 +0530640static const struct regmap_config max77686_rtc_regmap_config = {
641 .reg_bits = 8,
642 .val_bits = 8,
643};
644
645static int max77686_init_rtc_regmap(struct max77686_rtc_info *info)
646{
647 struct device *parent = info->dev->parent;
648 struct i2c_client *parent_i2c = to_i2c_client(parent);
649 int ret;
650
651 info->rtc_irq = parent_i2c->irq;
652
653 info->regmap = dev_get_regmap(parent, NULL);
654 if (!info->regmap) {
655 dev_err(info->dev, "Failed to get rtc regmap\n");
656 return -ENODEV;
657 }
658
659 if (info->drv_data->rtc_i2c_addr == MAX77686_INVALID_I2C_ADDR) {
660 info->rtc_regmap = info->regmap;
661 goto add_rtc_irq;
662 }
663
664 info->rtc = i2c_new_dummy(parent_i2c->adapter,
665 info->drv_data->rtc_i2c_addr);
666 if (!info->rtc) {
667 dev_err(info->dev, "Failed to allocate I2C device for RTC\n");
668 return -ENODEV;
669 }
670
671 info->rtc_regmap = devm_regmap_init_i2c(info->rtc,
672 &max77686_rtc_regmap_config);
673 if (IS_ERR(info->rtc_regmap)) {
674 ret = PTR_ERR(info->rtc_regmap);
675 dev_err(info->dev, "Failed to allocate RTC regmap: %d\n", ret);
676 goto err_unregister_i2c;
677 }
678
679add_rtc_irq:
680 ret = regmap_add_irq_chip(info->rtc_regmap, info->rtc_irq,
681 IRQF_TRIGGER_FALLING | IRQF_ONESHOT |
682 IRQF_SHARED, 0, info->drv_data->rtc_irq_chip,
683 &info->rtc_irq_data);
684 if (ret < 0) {
685 dev_err(info->dev, "Failed to add RTC irq chip: %d\n", ret);
686 goto err_unregister_i2c;
687 }
688
689 return 0;
690
691err_unregister_i2c:
692 if (info->rtc)
693 i2c_unregister_device(info->rtc);
694 return ret;
695}
696
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800697static int max77686_rtc_probe(struct platform_device *pdev)
698{
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800699 struct max77686_rtc_info *info;
Javier Martinez Canillas01ea01b2016-01-27 00:36:40 -0300700 const struct platform_device_id *id = platform_get_device_id(pdev);
Javier Martinez Canillas6f1c1e72014-07-04 22:24:04 +0200701 int ret;
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800702
Jingoo Han0f64f852013-04-29 16:18:30 -0700703 info = devm_kzalloc(&pdev->dev, sizeof(struct max77686_rtc_info),
Laxman Dewangan0b4f8b02016-02-09 22:56:30 +0530704 GFP_KERNEL);
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800705 if (!info)
706 return -ENOMEM;
707
708 mutex_init(&info->lock);
709 info->dev = &pdev->dev;
Javier Martinez Canillas01ea01b2016-01-27 00:36:40 -0300710 info->drv_data = (const struct max77686_rtc_driver_data *)
711 id->driver_data;
Javier Martinez Canillas6f1c1e72014-07-04 22:24:04 +0200712
Laxman Dewanganf3937542016-02-09 22:56:34 +0530713 ret = max77686_init_rtc_regmap(info);
714 if (ret < 0)
715 return ret;
Javier Martinez Canillasf9031292016-01-27 00:36:42 -0300716
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800717 platform_set_drvdata(pdev, info);
718
719 ret = max77686_rtc_init_reg(info);
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800720 if (ret < 0) {
721 dev_err(&pdev->dev, "Failed to initialize RTC reg:%d\n", ret);
722 goto err_rtc;
723 }
724
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800725 device_init_wakeup(&pdev->dev, 1);
726
Javier Martinez Canillasf9031292016-01-27 00:36:42 -0300727 info->rtc_dev = devm_rtc_device_register(&pdev->dev, id->name,
Jingoo Hanf56950e2013-04-29 16:19:05 -0700728 &max77686_rtc_ops, THIS_MODULE);
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800729
730 if (IS_ERR(info->rtc_dev)) {
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800731 ret = PTR_ERR(info->rtc_dev);
732 dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret);
733 if (ret == 0)
734 ret = -EINVAL;
735 goto err_rtc;
736 }
Javier Martinez Canillas6f1c1e72014-07-04 22:24:04 +0200737
Laxman Dewanganf3937542016-02-09 22:56:34 +0530738 info->virq = regmap_irq_get_virq(info->rtc_irq_data,
Javier Martinez Canillas6f1c1e72014-07-04 22:24:04 +0200739 MAX77686_RTCIRQ_RTCA1);
Krzysztof Kozlowskifb166ba2016-02-04 09:26:35 +0900740 if (info->virq <= 0) {
Sachin Kamatad819032013-04-29 16:20:12 -0700741 ret = -ENXIO;
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800742 goto err_rtc;
Sachin Kamatad819032013-04-29 16:20:12 -0700743 }
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800744
Laxman Dewanganf3937542016-02-09 22:56:34 +0530745 ret = request_threaded_irq(info->virq, NULL, max77686_rtc_alarm_irq, 0,
746 "rtc-alarm1", info);
747 if (ret < 0) {
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800748 dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n",
749 info->virq, ret);
Laxman Dewanganf3937542016-02-09 22:56:34 +0530750 goto err_rtc;
751 }
752
753 return 0;
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800754
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800755err_rtc:
Laxman Dewanganf3937542016-02-09 22:56:34 +0530756 regmap_del_irq_chip(info->rtc_irq, info->rtc_irq_data);
757 if (info->rtc)
758 i2c_unregister_device(info->rtc);
759
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800760 return ret;
761}
762
Laxman Dewanganf3937542016-02-09 22:56:34 +0530763static int max77686_rtc_remove(struct platform_device *pdev)
764{
765 struct max77686_rtc_info *info = platform_get_drvdata(pdev);
766
767 free_irq(info->virq, info);
768 regmap_del_irq_chip(info->rtc_irq, info->rtc_irq_data);
769 if (info->rtc)
770 i2c_unregister_device(info->rtc);
771
772 return 0;
773}
774
Doug Andersone7f7fc72014-10-13 15:52:55 -0700775#ifdef CONFIG_PM_SLEEP
776static int max77686_rtc_suspend(struct device *dev)
777{
778 if (device_may_wakeup(dev)) {
779 struct max77686_rtc_info *info = dev_get_drvdata(dev);
780
781 return enable_irq_wake(info->virq);
782 }
783
784 return 0;
785}
786
787static int max77686_rtc_resume(struct device *dev)
788{
789 if (device_may_wakeup(dev)) {
790 struct max77686_rtc_info *info = dev_get_drvdata(dev);
791
792 return disable_irq_wake(info->virq);
793 }
794
795 return 0;
796}
797#endif
798
799static SIMPLE_DEV_PM_OPS(max77686_rtc_pm_ops,
800 max77686_rtc_suspend, max77686_rtc_resume);
801
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800802static const struct platform_device_id rtc_id[] = {
Javier Martinez Canillas01ea01b2016-01-27 00:36:40 -0300803 { "max77686-rtc", .driver_data = (kernel_ulong_t)&max77686_drv_data, },
Javier Martinez Canillasf9031292016-01-27 00:36:42 -0300804 { "max77802-rtc", .driver_data = (kernel_ulong_t)&max77802_drv_data, },
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800805 {},
806};
Javier Martinez Canillas2d0cca02015-05-13 17:18:01 +0200807MODULE_DEVICE_TABLE(platform, rtc_id);
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800808
809static struct platform_driver max77686_rtc_driver = {
810 .driver = {
811 .name = "max77686-rtc",
Doug Andersone7f7fc72014-10-13 15:52:55 -0700812 .pm = &max77686_rtc_pm_ops,
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800813 },
814 .probe = max77686_rtc_probe,
Laxman Dewanganf3937542016-02-09 22:56:34 +0530815 .remove = max77686_rtc_remove,
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800816 .id_table = rtc_id,
817};
818
Jingoo Han0c58ff52013-04-29 16:18:28 -0700819module_platform_driver(max77686_rtc_driver);
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800820
821MODULE_DESCRIPTION("Maxim MAX77686 RTC driver");
Jingoo Hanf5b1d3c2013-04-29 16:18:29 -0700822MODULE_AUTHOR("Chiwoong Byun <woong.byun@samsung.com>");
Jonghwa Leefca1dd02013-02-21 16:44:26 -0800823MODULE_LICENSE("GPL");