1734c5cd5SKrzysztof Kozlowski // SPDX-License-Identifier: GPL-2.0+
2734c5cd5SKrzysztof Kozlowski //
3734c5cd5SKrzysztof Kozlowski // RTC driver for Maxim MAX77686 and MAX77802
4734c5cd5SKrzysztof Kozlowski //
5734c5cd5SKrzysztof Kozlowski // Copyright (C) 2012 Samsung Electronics Co.Ltd
6734c5cd5SKrzysztof Kozlowski //
7734c5cd5SKrzysztof Kozlowski // based on rtc-max8997.c
8fca1dd03SJonghwa Lee
9f3937549SLaxman Dewangan #include <linux/i2c.h>
10fca1dd03SJonghwa Lee #include <linux/slab.h>
11fca1dd03SJonghwa Lee #include <linux/rtc.h>
12fca1dd03SJonghwa Lee #include <linux/delay.h>
13fca1dd03SJonghwa Lee #include <linux/mutex.h>
14fca1dd03SJonghwa Lee #include <linux/module.h>
15fca1dd03SJonghwa Lee #include <linux/platform_device.h>
16fca1dd03SJonghwa Lee #include <linux/mfd/max77686-private.h>
17fca1dd03SJonghwa Lee #include <linux/irqdomain.h>
18fca1dd03SJonghwa Lee #include <linux/regmap.h>
19fca1dd03SJonghwa Lee
20f3937549SLaxman Dewangan #define MAX77686_I2C_ADDR_RTC (0x0C >> 1)
21726fe738SLaxman Dewangan #define MAX77620_I2C_ADDR_RTC 0x68
22c58e4963SLuca Ceresoli #define MAX77714_I2C_ADDR_RTC 0x48
23f3937549SLaxman Dewangan #define MAX77686_INVALID_I2C_ADDR (-1)
24f3937549SLaxman Dewangan
25726fe738SLaxman Dewangan /* Define non existing register */
26726fe738SLaxman Dewangan #define MAX77686_INVALID_REG (-1)
27726fe738SLaxman Dewangan
28fca1dd03SJonghwa Lee /* RTC Control Register */
29fca1dd03SJonghwa Lee #define BCD_EN_SHIFT 0
300b4f8b08SLaxman Dewangan #define BCD_EN_MASK BIT(BCD_EN_SHIFT)
31fca1dd03SJonghwa Lee #define MODEL24_SHIFT 1
320b4f8b08SLaxman Dewangan #define MODEL24_MASK BIT(MODEL24_SHIFT)
33fca1dd03SJonghwa Lee /* RTC Update Register1 */
34fca1dd03SJonghwa Lee #define RTC_UDR_SHIFT 0
350b4f8b08SLaxman Dewangan #define RTC_UDR_MASK BIT(RTC_UDR_SHIFT)
36fca1dd03SJonghwa Lee #define RTC_RBUDR_SHIFT 4
370b4f8b08SLaxman Dewangan #define RTC_RBUDR_MASK BIT(RTC_RBUDR_SHIFT)
38fca1dd03SJonghwa Lee /* RTC Alarm Enable */
39fca1dd03SJonghwa Lee #define ALARM_ENABLE_SHIFT 7
400b4f8b08SLaxman Dewangan #define ALARM_ENABLE_MASK BIT(ALARM_ENABLE_SHIFT)
41fca1dd03SJonghwa Lee
42f903129bSJavier Martinez Canillas #define REG_RTC_NONE 0xdeadbeef
43f903129bSJavier Martinez Canillas
44f903129bSJavier Martinez Canillas /*
45f903129bSJavier Martinez Canillas * MAX77802 has separate register (RTCAE1) for alarm enable instead
46f903129bSJavier Martinez Canillas * using 1 bit from registers RTC{SEC,MIN,HOUR,DAY,MONTH,YEAR,DATE}
47f903129bSJavier Martinez Canillas * as in done in MAX77686.
48f903129bSJavier Martinez Canillas */
49f903129bSJavier Martinez Canillas #define MAX77802_ALARM_ENABLE_VALUE 0x77
50f903129bSJavier Martinez Canillas
51fca1dd03SJonghwa Lee enum {
52fca1dd03SJonghwa Lee RTC_SEC = 0,
53fca1dd03SJonghwa Lee RTC_MIN,
54fca1dd03SJonghwa Lee RTC_HOUR,
55fca1dd03SJonghwa Lee RTC_WEEKDAY,
56fca1dd03SJonghwa Lee RTC_MONTH,
57fca1dd03SJonghwa Lee RTC_YEAR,
58a26d8463SLuca Ceresoli RTC_MONTHDAY,
59fca1dd03SJonghwa Lee RTC_NR_TIME
60fca1dd03SJonghwa Lee };
61fca1dd03SJonghwa Lee
6272c356c2SLuca Ceresoli /**
6372c356c2SLuca Ceresoli * struct max77686_rtc_driver_data - model-specific configuration
6472c356c2SLuca Ceresoli * @delay: Minimum usecs needed for a RTC update
6572c356c2SLuca Ceresoli * @mask: Mask used to read RTC registers value
6672c356c2SLuca Ceresoli * @map: Registers offset to I2C addresses map
6772c356c2SLuca Ceresoli * @alarm_enable_reg: Has a separate alarm enable register?
6872c356c2SLuca Ceresoli * @rtc_i2c_addr: I2C address for RTC block
6972c356c2SLuca Ceresoli * @rtc_irq_from_platform: RTC interrupt via platform resource
7072c356c2SLuca Ceresoli * @alarm_pending_status_reg: Pending alarm status register
7172c356c2SLuca Ceresoli * @rtc_irq_chip: RTC IRQ CHIP for regmap
7272c356c2SLuca Ceresoli * @regmap_config: regmap configuration for the chip
7372c356c2SLuca Ceresoli */
7401ea01b3SJavier Martinez Canillas struct max77686_rtc_driver_data {
7501ea01b3SJavier Martinez Canillas unsigned long delay;
7601ea01b3SJavier Martinez Canillas u8 mask;
7790a5698aSJavier Martinez Canillas const unsigned int *map;
78f903129bSJavier Martinez Canillas bool alarm_enable_reg;
79f3937549SLaxman Dewangan int rtc_i2c_addr;
80726fe738SLaxman Dewangan bool rtc_irq_from_platform;
81726fe738SLaxman Dewangan int alarm_pending_status_reg;
82f3937549SLaxman Dewangan const struct regmap_irq_chip *rtc_irq_chip;
8363a52f63SThierry Reding const struct regmap_config *regmap_config;
8401ea01b3SJavier Martinez Canillas };
8501ea01b3SJavier Martinez Canillas
86fca1dd03SJonghwa Lee struct max77686_rtc_info {
87fca1dd03SJonghwa Lee struct device *dev;
88fca1dd03SJonghwa Lee struct i2c_client *rtc;
89fca1dd03SJonghwa Lee struct rtc_device *rtc_dev;
90fca1dd03SJonghwa Lee struct mutex lock;
91fca1dd03SJonghwa Lee
92fca1dd03SJonghwa Lee struct regmap *regmap;
93f604c488SLaxman Dewangan struct regmap *rtc_regmap;
94fca1dd03SJonghwa Lee
9501ea01b3SJavier Martinez Canillas const struct max77686_rtc_driver_data *drv_data;
96f3937549SLaxman Dewangan struct regmap_irq_chip_data *rtc_irq_data;
9701ea01b3SJavier Martinez Canillas
98f3937549SLaxman Dewangan int rtc_irq;
99fca1dd03SJonghwa Lee int virq;
100fca1dd03SJonghwa Lee };
101fca1dd03SJonghwa Lee
102fca1dd03SJonghwa Lee enum MAX77686_RTC_OP {
103fca1dd03SJonghwa Lee MAX77686_RTC_WRITE,
104fca1dd03SJonghwa Lee MAX77686_RTC_READ,
105fca1dd03SJonghwa Lee };
106fca1dd03SJonghwa Lee
10790a5698aSJavier Martinez Canillas /* These are not registers but just offsets that are mapped to addresses */
10890a5698aSJavier Martinez Canillas enum max77686_rtc_reg_offset {
10990a5698aSJavier Martinez Canillas REG_RTC_CONTROLM = 0,
11090a5698aSJavier Martinez Canillas REG_RTC_CONTROL,
11190a5698aSJavier Martinez Canillas REG_RTC_UPDATE0,
11290a5698aSJavier Martinez Canillas REG_WTSR_SMPL_CNTL,
11390a5698aSJavier Martinez Canillas REG_RTC_SEC,
11490a5698aSJavier Martinez Canillas REG_RTC_MIN,
11590a5698aSJavier Martinez Canillas REG_RTC_HOUR,
11690a5698aSJavier Martinez Canillas REG_RTC_WEEKDAY,
11790a5698aSJavier Martinez Canillas REG_RTC_MONTH,
11890a5698aSJavier Martinez Canillas REG_RTC_YEAR,
119a26d8463SLuca Ceresoli REG_RTC_MONTHDAY,
12090a5698aSJavier Martinez Canillas REG_ALARM1_SEC,
12190a5698aSJavier Martinez Canillas REG_ALARM1_MIN,
12290a5698aSJavier Martinez Canillas REG_ALARM1_HOUR,
12390a5698aSJavier Martinez Canillas REG_ALARM1_WEEKDAY,
12490a5698aSJavier Martinez Canillas REG_ALARM1_MONTH,
12590a5698aSJavier Martinez Canillas REG_ALARM1_YEAR,
12690a5698aSJavier Martinez Canillas REG_ALARM1_DATE,
12790a5698aSJavier Martinez Canillas REG_ALARM2_SEC,
12890a5698aSJavier Martinez Canillas REG_ALARM2_MIN,
12990a5698aSJavier Martinez Canillas REG_ALARM2_HOUR,
13090a5698aSJavier Martinez Canillas REG_ALARM2_WEEKDAY,
13190a5698aSJavier Martinez Canillas REG_ALARM2_MONTH,
13290a5698aSJavier Martinez Canillas REG_ALARM2_YEAR,
13390a5698aSJavier Martinez Canillas REG_ALARM2_DATE,
134f903129bSJavier Martinez Canillas REG_RTC_AE1,
13590a5698aSJavier Martinez Canillas REG_RTC_END,
13690a5698aSJavier Martinez Canillas };
13790a5698aSJavier Martinez Canillas
13890a5698aSJavier Martinez Canillas /* Maps RTC registers offset to the MAX77686 register addresses */
13990a5698aSJavier Martinez Canillas static const unsigned int max77686_map[REG_RTC_END] = {
14090a5698aSJavier Martinez Canillas [REG_RTC_CONTROLM] = MAX77686_RTC_CONTROLM,
14190a5698aSJavier Martinez Canillas [REG_RTC_CONTROL] = MAX77686_RTC_CONTROL,
14290a5698aSJavier Martinez Canillas [REG_RTC_UPDATE0] = MAX77686_RTC_UPDATE0,
14390a5698aSJavier Martinez Canillas [REG_WTSR_SMPL_CNTL] = MAX77686_WTSR_SMPL_CNTL,
14490a5698aSJavier Martinez Canillas [REG_RTC_SEC] = MAX77686_RTC_SEC,
14590a5698aSJavier Martinez Canillas [REG_RTC_MIN] = MAX77686_RTC_MIN,
14690a5698aSJavier Martinez Canillas [REG_RTC_HOUR] = MAX77686_RTC_HOUR,
14790a5698aSJavier Martinez Canillas [REG_RTC_WEEKDAY] = MAX77686_RTC_WEEKDAY,
14890a5698aSJavier Martinez Canillas [REG_RTC_MONTH] = MAX77686_RTC_MONTH,
14990a5698aSJavier Martinez Canillas [REG_RTC_YEAR] = MAX77686_RTC_YEAR,
150a26d8463SLuca Ceresoli [REG_RTC_MONTHDAY] = MAX77686_RTC_MONTHDAY,
15190a5698aSJavier Martinez Canillas [REG_ALARM1_SEC] = MAX77686_ALARM1_SEC,
15290a5698aSJavier Martinez Canillas [REG_ALARM1_MIN] = MAX77686_ALARM1_MIN,
15390a5698aSJavier Martinez Canillas [REG_ALARM1_HOUR] = MAX77686_ALARM1_HOUR,
15490a5698aSJavier Martinez Canillas [REG_ALARM1_WEEKDAY] = MAX77686_ALARM1_WEEKDAY,
15590a5698aSJavier Martinez Canillas [REG_ALARM1_MONTH] = MAX77686_ALARM1_MONTH,
15690a5698aSJavier Martinez Canillas [REG_ALARM1_YEAR] = MAX77686_ALARM1_YEAR,
15790a5698aSJavier Martinez Canillas [REG_ALARM1_DATE] = MAX77686_ALARM1_DATE,
15890a5698aSJavier Martinez Canillas [REG_ALARM2_SEC] = MAX77686_ALARM2_SEC,
15990a5698aSJavier Martinez Canillas [REG_ALARM2_MIN] = MAX77686_ALARM2_MIN,
16090a5698aSJavier Martinez Canillas [REG_ALARM2_HOUR] = MAX77686_ALARM2_HOUR,
16190a5698aSJavier Martinez Canillas [REG_ALARM2_WEEKDAY] = MAX77686_ALARM2_WEEKDAY,
16290a5698aSJavier Martinez Canillas [REG_ALARM2_MONTH] = MAX77686_ALARM2_MONTH,
16390a5698aSJavier Martinez Canillas [REG_ALARM2_YEAR] = MAX77686_ALARM2_YEAR,
16490a5698aSJavier Martinez Canillas [REG_ALARM2_DATE] = MAX77686_ALARM2_DATE,
165f903129bSJavier Martinez Canillas [REG_RTC_AE1] = REG_RTC_NONE,
16690a5698aSJavier Martinez Canillas };
16790a5698aSJavier Martinez Canillas
168f3937549SLaxman Dewangan static const struct regmap_irq max77686_rtc_irqs[] = {
169f3937549SLaxman Dewangan /* RTC interrupts */
1707e84536cSLaxman Dewangan REGMAP_IRQ_REG(0, 0, MAX77686_RTCINT_RTC60S_MSK),
1717e84536cSLaxman Dewangan REGMAP_IRQ_REG(1, 0, MAX77686_RTCINT_RTCA1_MSK),
1727e84536cSLaxman Dewangan REGMAP_IRQ_REG(2, 0, MAX77686_RTCINT_RTCA2_MSK),
1737e84536cSLaxman Dewangan REGMAP_IRQ_REG(3, 0, MAX77686_RTCINT_SMPL_MSK),
1747e84536cSLaxman Dewangan REGMAP_IRQ_REG(4, 0, MAX77686_RTCINT_RTC1S_MSK),
1757e84536cSLaxman Dewangan REGMAP_IRQ_REG(5, 0, MAX77686_RTCINT_WTSR_MSK),
176f3937549SLaxman Dewangan };
177f3937549SLaxman Dewangan
178f3937549SLaxman Dewangan static const struct regmap_irq_chip max77686_rtc_irq_chip = {
179f3937549SLaxman Dewangan .name = "max77686-rtc",
180f3937549SLaxman Dewangan .status_base = MAX77686_RTC_INT,
181f3937549SLaxman Dewangan .mask_base = MAX77686_RTC_INTM,
182f3937549SLaxman Dewangan .num_regs = 1,
183f3937549SLaxman Dewangan .irqs = max77686_rtc_irqs,
184f3937549SLaxman Dewangan .num_irqs = ARRAY_SIZE(max77686_rtc_irqs),
185f3937549SLaxman Dewangan };
186f3937549SLaxman Dewangan
18763a52f63SThierry Reding static const struct regmap_config max77686_rtc_regmap_config = {
18863a52f63SThierry Reding .reg_bits = 8,
18963a52f63SThierry Reding .val_bits = 8,
19063a52f63SThierry Reding };
19163a52f63SThierry Reding
19201ea01b3SJavier Martinez Canillas static const struct max77686_rtc_driver_data max77686_drv_data = {
19301ea01b3SJavier Martinez Canillas .delay = 16000,
19401ea01b3SJavier Martinez Canillas .mask = 0x7f,
19590a5698aSJavier Martinez Canillas .map = max77686_map,
196f903129bSJavier Martinez Canillas .alarm_enable_reg = false,
197726fe738SLaxman Dewangan .rtc_irq_from_platform = false,
198726fe738SLaxman Dewangan .alarm_pending_status_reg = MAX77686_REG_STATUS2,
199f3937549SLaxman Dewangan .rtc_i2c_addr = MAX77686_I2C_ADDR_RTC,
200f3937549SLaxman Dewangan .rtc_irq_chip = &max77686_rtc_irq_chip,
20163a52f63SThierry Reding .regmap_config = &max77686_rtc_regmap_config,
20263a52f63SThierry Reding };
20363a52f63SThierry Reding
204c58e4963SLuca Ceresoli static const struct regmap_irq_chip max77714_rtc_irq_chip = {
205c58e4963SLuca Ceresoli .name = "max77714-rtc",
206c58e4963SLuca Ceresoli .status_base = MAX77686_RTC_INT,
207c58e4963SLuca Ceresoli .mask_base = MAX77686_RTC_INTM,
208c58e4963SLuca Ceresoli .num_regs = 1,
209c58e4963SLuca Ceresoli .irqs = max77686_rtc_irqs,
210c58e4963SLuca Ceresoli .num_irqs = ARRAY_SIZE(max77686_rtc_irqs) - 1, /* no WTSR on 77714 */
211c58e4963SLuca Ceresoli };
212c58e4963SLuca Ceresoli
213c58e4963SLuca Ceresoli static const struct max77686_rtc_driver_data max77714_drv_data = {
214c58e4963SLuca Ceresoli .delay = 16000,
215c58e4963SLuca Ceresoli .mask = 0x7f,
216c58e4963SLuca Ceresoli .map = max77686_map,
217c58e4963SLuca Ceresoli .alarm_enable_reg = false,
218c58e4963SLuca Ceresoli .rtc_irq_from_platform = false,
219c58e4963SLuca Ceresoli /* On MAX77714 RTCA1 is BIT 1 of RTCINT (0x00). Not supported by this driver. */
220c58e4963SLuca Ceresoli .alarm_pending_status_reg = MAX77686_INVALID_REG,
221c58e4963SLuca Ceresoli .rtc_i2c_addr = MAX77714_I2C_ADDR_RTC,
222c58e4963SLuca Ceresoli .rtc_irq_chip = &max77714_rtc_irq_chip,
223c58e4963SLuca Ceresoli .regmap_config = &max77686_rtc_regmap_config,
224c58e4963SLuca Ceresoli };
225c58e4963SLuca Ceresoli
22663a52f63SThierry Reding static const struct regmap_config max77620_rtc_regmap_config = {
22763a52f63SThierry Reding .reg_bits = 8,
22863a52f63SThierry Reding .val_bits = 8,
22963a52f63SThierry Reding .use_single_write = true,
230f903129bSJavier Martinez Canillas };
231f903129bSJavier Martinez Canillas
232726fe738SLaxman Dewangan static const struct max77686_rtc_driver_data max77620_drv_data = {
233726fe738SLaxman Dewangan .delay = 16000,
234726fe738SLaxman Dewangan .mask = 0x7f,
235726fe738SLaxman Dewangan .map = max77686_map,
236726fe738SLaxman Dewangan .alarm_enable_reg = false,
237726fe738SLaxman Dewangan .rtc_irq_from_platform = true,
238726fe738SLaxman Dewangan .alarm_pending_status_reg = MAX77686_INVALID_REG,
239726fe738SLaxman Dewangan .rtc_i2c_addr = MAX77620_I2C_ADDR_RTC,
240726fe738SLaxman Dewangan .rtc_irq_chip = &max77686_rtc_irq_chip,
24163a52f63SThierry Reding .regmap_config = &max77620_rtc_regmap_config,
242726fe738SLaxman Dewangan };
243726fe738SLaxman Dewangan
244f903129bSJavier Martinez Canillas static const unsigned int max77802_map[REG_RTC_END] = {
245f903129bSJavier Martinez Canillas [REG_RTC_CONTROLM] = MAX77802_RTC_CONTROLM,
246f903129bSJavier Martinez Canillas [REG_RTC_CONTROL] = MAX77802_RTC_CONTROL,
247f903129bSJavier Martinez Canillas [REG_RTC_UPDATE0] = MAX77802_RTC_UPDATE0,
248f903129bSJavier Martinez Canillas [REG_WTSR_SMPL_CNTL] = MAX77802_WTSR_SMPL_CNTL,
249f903129bSJavier Martinez Canillas [REG_RTC_SEC] = MAX77802_RTC_SEC,
250f903129bSJavier Martinez Canillas [REG_RTC_MIN] = MAX77802_RTC_MIN,
251f903129bSJavier Martinez Canillas [REG_RTC_HOUR] = MAX77802_RTC_HOUR,
252f903129bSJavier Martinez Canillas [REG_RTC_WEEKDAY] = MAX77802_RTC_WEEKDAY,
253f903129bSJavier Martinez Canillas [REG_RTC_MONTH] = MAX77802_RTC_MONTH,
254f903129bSJavier Martinez Canillas [REG_RTC_YEAR] = MAX77802_RTC_YEAR,
255a26d8463SLuca Ceresoli [REG_RTC_MONTHDAY] = MAX77802_RTC_MONTHDAY,
256f903129bSJavier Martinez Canillas [REG_ALARM1_SEC] = MAX77802_ALARM1_SEC,
257f903129bSJavier Martinez Canillas [REG_ALARM1_MIN] = MAX77802_ALARM1_MIN,
258f903129bSJavier Martinez Canillas [REG_ALARM1_HOUR] = MAX77802_ALARM1_HOUR,
259f903129bSJavier Martinez Canillas [REG_ALARM1_WEEKDAY] = MAX77802_ALARM1_WEEKDAY,
260f903129bSJavier Martinez Canillas [REG_ALARM1_MONTH] = MAX77802_ALARM1_MONTH,
261f903129bSJavier Martinez Canillas [REG_ALARM1_YEAR] = MAX77802_ALARM1_YEAR,
262f903129bSJavier Martinez Canillas [REG_ALARM1_DATE] = MAX77802_ALARM1_DATE,
263f903129bSJavier Martinez Canillas [REG_ALARM2_SEC] = MAX77802_ALARM2_SEC,
264f903129bSJavier Martinez Canillas [REG_ALARM2_MIN] = MAX77802_ALARM2_MIN,
265f903129bSJavier Martinez Canillas [REG_ALARM2_HOUR] = MAX77802_ALARM2_HOUR,
266f903129bSJavier Martinez Canillas [REG_ALARM2_WEEKDAY] = MAX77802_ALARM2_WEEKDAY,
267f903129bSJavier Martinez Canillas [REG_ALARM2_MONTH] = MAX77802_ALARM2_MONTH,
268f903129bSJavier Martinez Canillas [REG_ALARM2_YEAR] = MAX77802_ALARM2_YEAR,
269f903129bSJavier Martinez Canillas [REG_ALARM2_DATE] = MAX77802_ALARM2_DATE,
270f903129bSJavier Martinez Canillas [REG_RTC_AE1] = MAX77802_RTC_AE1,
271f903129bSJavier Martinez Canillas };
272f903129bSJavier Martinez Canillas
273f3937549SLaxman Dewangan static const struct regmap_irq_chip max77802_rtc_irq_chip = {
274f3937549SLaxman Dewangan .name = "max77802-rtc",
275f3937549SLaxman Dewangan .status_base = MAX77802_RTC_INT,
276f3937549SLaxman Dewangan .mask_base = MAX77802_RTC_INTM,
277f3937549SLaxman Dewangan .num_regs = 1,
278f3937549SLaxman Dewangan .irqs = max77686_rtc_irqs, /* same masks as 77686 */
279f3937549SLaxman Dewangan .num_irqs = ARRAY_SIZE(max77686_rtc_irqs),
280f3937549SLaxman Dewangan };
281f3937549SLaxman Dewangan
282f903129bSJavier Martinez Canillas static const struct max77686_rtc_driver_data max77802_drv_data = {
283f903129bSJavier Martinez Canillas .delay = 200,
284f903129bSJavier Martinez Canillas .mask = 0xff,
285f903129bSJavier Martinez Canillas .map = max77802_map,
286f903129bSJavier Martinez Canillas .alarm_enable_reg = true,
287726fe738SLaxman Dewangan .rtc_irq_from_platform = false,
288726fe738SLaxman Dewangan .alarm_pending_status_reg = MAX77686_REG_STATUS2,
289f3937549SLaxman Dewangan .rtc_i2c_addr = MAX77686_INVALID_I2C_ADDR,
290f3937549SLaxman Dewangan .rtc_irq_chip = &max77802_rtc_irq_chip,
29101ea01b3SJavier Martinez Canillas };
29201ea01b3SJavier Martinez Canillas
max77686_rtc_data_to_tm(u8 * data,struct rtc_time * tm,struct max77686_rtc_info * info)293fca1dd03SJonghwa Lee static void max77686_rtc_data_to_tm(u8 *data, struct rtc_time *tm,
29401ea01b3SJavier Martinez Canillas struct max77686_rtc_info *info)
295fca1dd03SJonghwa Lee {
29601ea01b3SJavier Martinez Canillas u8 mask = info->drv_data->mask;
29701ea01b3SJavier Martinez Canillas
29801ea01b3SJavier Martinez Canillas tm->tm_sec = data[RTC_SEC] & mask;
29901ea01b3SJavier Martinez Canillas tm->tm_min = data[RTC_MIN] & mask;
300fca1dd03SJonghwa Lee tm->tm_hour = data[RTC_HOUR] & 0x1f;
301fca1dd03SJonghwa Lee
302a20cd88eSJavier Martinez Canillas /* Only a single bit is set in data[], so fls() would be equivalent */
30301ea01b3SJavier Martinez Canillas tm->tm_wday = ffs(data[RTC_WEEKDAY] & mask) - 1;
304a26d8463SLuca Ceresoli tm->tm_mday = data[RTC_MONTHDAY] & 0x1f;
305fca1dd03SJonghwa Lee tm->tm_mon = (data[RTC_MONTH] & 0x0f) - 1;
306f903129bSJavier Martinez Canillas tm->tm_year = data[RTC_YEAR] & mask;
307fca1dd03SJonghwa Lee tm->tm_yday = 0;
308fca1dd03SJonghwa Lee tm->tm_isdst = 0;
309f903129bSJavier Martinez Canillas
310f903129bSJavier Martinez Canillas /*
311f903129bSJavier Martinez Canillas * MAX77686 uses 1 bit from sec/min/hour/etc RTC registers and the
312f903129bSJavier Martinez Canillas * year values are just 0..99 so add 100 to support up to 2099.
313f903129bSJavier Martinez Canillas */
314f903129bSJavier Martinez Canillas if (!info->drv_data->alarm_enable_reg)
315f903129bSJavier Martinez Canillas tm->tm_year += 100;
316fca1dd03SJonghwa Lee }
317fca1dd03SJonghwa Lee
max77686_rtc_tm_to_data(struct rtc_time * tm,u8 * data,struct max77686_rtc_info * info)318f903129bSJavier Martinez Canillas static int max77686_rtc_tm_to_data(struct rtc_time *tm, u8 *data,
319f903129bSJavier Martinez Canillas struct max77686_rtc_info *info)
320fca1dd03SJonghwa Lee {
321fca1dd03SJonghwa Lee data[RTC_SEC] = tm->tm_sec;
322fca1dd03SJonghwa Lee data[RTC_MIN] = tm->tm_min;
323fca1dd03SJonghwa Lee data[RTC_HOUR] = tm->tm_hour;
324fca1dd03SJonghwa Lee data[RTC_WEEKDAY] = 1 << tm->tm_wday;
325a26d8463SLuca Ceresoli data[RTC_MONTHDAY] = tm->tm_mday;
326fca1dd03SJonghwa Lee data[RTC_MONTH] = tm->tm_mon + 1;
327f903129bSJavier Martinez Canillas
328f903129bSJavier Martinez Canillas if (info->drv_data->alarm_enable_reg) {
329f903129bSJavier Martinez Canillas data[RTC_YEAR] = tm->tm_year;
330f903129bSJavier Martinez Canillas return 0;
331f903129bSJavier Martinez Canillas }
332f903129bSJavier Martinez Canillas
333fca1dd03SJonghwa Lee data[RTC_YEAR] = tm->tm_year > 100 ? (tm->tm_year - 100) : 0;
334fca1dd03SJonghwa Lee
335fca1dd03SJonghwa Lee if (tm->tm_year < 100) {
3361e5813bdSJavier Martinez Canillas dev_err(info->dev, "RTC cannot handle the year %d.\n",
337a737e835SJoe Perches 1900 + tm->tm_year);
338fca1dd03SJonghwa Lee return -EINVAL;
339fca1dd03SJonghwa Lee }
340f903129bSJavier Martinez Canillas
341fca1dd03SJonghwa Lee return 0;
342fca1dd03SJonghwa Lee }
343fca1dd03SJonghwa Lee
max77686_rtc_update(struct max77686_rtc_info * info,enum MAX77686_RTC_OP op)344fca1dd03SJonghwa Lee static int max77686_rtc_update(struct max77686_rtc_info *info,
345fca1dd03SJonghwa Lee enum MAX77686_RTC_OP op)
346fca1dd03SJonghwa Lee {
347fca1dd03SJonghwa Lee int ret;
348fca1dd03SJonghwa Lee unsigned int data;
34901ea01b3SJavier Martinez Canillas unsigned long delay = info->drv_data->delay;
350fca1dd03SJonghwa Lee
351fca1dd03SJonghwa Lee if (op == MAX77686_RTC_WRITE)
352fca1dd03SJonghwa Lee data = 1 << RTC_UDR_SHIFT;
353fca1dd03SJonghwa Lee else
354fca1dd03SJonghwa Lee data = 1 << RTC_RBUDR_SHIFT;
355fca1dd03SJonghwa Lee
356f604c488SLaxman Dewangan ret = regmap_update_bits(info->rtc_regmap,
35790a5698aSJavier Martinez Canillas info->drv_data->map[REG_RTC_UPDATE0],
35890a5698aSJavier Martinez Canillas data, data);
359fca1dd03SJonghwa Lee if (ret < 0)
360bf035f42SKrzysztof Kozlowski dev_err(info->dev, "Fail to write update reg(ret=%d, data=0x%x)\n",
361bf035f42SKrzysztof Kozlowski ret, data);
362fca1dd03SJonghwa Lee else {
36301ea01b3SJavier Martinez Canillas /* Minimum delay required before RTC update. */
36401ea01b3SJavier Martinez Canillas usleep_range(delay, delay * 2);
365fca1dd03SJonghwa Lee }
366fca1dd03SJonghwa Lee
367fca1dd03SJonghwa Lee return ret;
368fca1dd03SJonghwa Lee }
369fca1dd03SJonghwa Lee
max77686_rtc_read_time(struct device * dev,struct rtc_time * tm)370fca1dd03SJonghwa Lee static int max77686_rtc_read_time(struct device *dev, struct rtc_time *tm)
371fca1dd03SJonghwa Lee {
372fca1dd03SJonghwa Lee struct max77686_rtc_info *info = dev_get_drvdata(dev);
373fca1dd03SJonghwa Lee u8 data[RTC_NR_TIME];
374fca1dd03SJonghwa Lee int ret;
375fca1dd03SJonghwa Lee
376fca1dd03SJonghwa Lee mutex_lock(&info->lock);
377fca1dd03SJonghwa Lee
378fca1dd03SJonghwa Lee ret = max77686_rtc_update(info, MAX77686_RTC_READ);
379fca1dd03SJonghwa Lee if (ret < 0)
380fca1dd03SJonghwa Lee goto out;
381fca1dd03SJonghwa Lee
382f604c488SLaxman Dewangan ret = regmap_bulk_read(info->rtc_regmap,
38390a5698aSJavier Martinez Canillas info->drv_data->map[REG_RTC_SEC],
38490a5698aSJavier Martinez Canillas data, ARRAY_SIZE(data));
385fca1dd03SJonghwa Lee if (ret < 0) {
386bf035f42SKrzysztof Kozlowski dev_err(info->dev, "Fail to read time reg(%d)\n", ret);
387fca1dd03SJonghwa Lee goto out;
388fca1dd03SJonghwa Lee }
389fca1dd03SJonghwa Lee
39001ea01b3SJavier Martinez Canillas max77686_rtc_data_to_tm(data, tm, info);
391fca1dd03SJonghwa Lee
392fca1dd03SJonghwa Lee out:
393fca1dd03SJonghwa Lee mutex_unlock(&info->lock);
394b28cc6ceSChristophe JAILLET return ret;
395fca1dd03SJonghwa Lee }
396fca1dd03SJonghwa Lee
max77686_rtc_set_time(struct device * dev,struct rtc_time * tm)397fca1dd03SJonghwa Lee static int max77686_rtc_set_time(struct device *dev, struct rtc_time *tm)
398fca1dd03SJonghwa Lee {
399fca1dd03SJonghwa Lee struct max77686_rtc_info *info = dev_get_drvdata(dev);
400fca1dd03SJonghwa Lee u8 data[RTC_NR_TIME];
401fca1dd03SJonghwa Lee int ret;
402fca1dd03SJonghwa Lee
403f903129bSJavier Martinez Canillas ret = max77686_rtc_tm_to_data(tm, data, info);
404fca1dd03SJonghwa Lee if (ret < 0)
405fca1dd03SJonghwa Lee return ret;
406fca1dd03SJonghwa Lee
407fca1dd03SJonghwa Lee mutex_lock(&info->lock);
408fca1dd03SJonghwa Lee
409f604c488SLaxman Dewangan ret = regmap_bulk_write(info->rtc_regmap,
41090a5698aSJavier Martinez Canillas info->drv_data->map[REG_RTC_SEC],
41190a5698aSJavier Martinez Canillas data, ARRAY_SIZE(data));
412fca1dd03SJonghwa Lee if (ret < 0) {
413bf035f42SKrzysztof Kozlowski dev_err(info->dev, "Fail to write time reg(%d)\n", ret);
414fca1dd03SJonghwa Lee goto out;
415fca1dd03SJonghwa Lee }
416fca1dd03SJonghwa Lee
417fca1dd03SJonghwa Lee ret = max77686_rtc_update(info, MAX77686_RTC_WRITE);
418fca1dd03SJonghwa Lee
419fca1dd03SJonghwa Lee out:
420fca1dd03SJonghwa Lee mutex_unlock(&info->lock);
421fca1dd03SJonghwa Lee return ret;
422fca1dd03SJonghwa Lee }
423fca1dd03SJonghwa Lee
max77686_rtc_read_alarm(struct device * dev,struct rtc_wkalrm * alrm)424fca1dd03SJonghwa Lee static int max77686_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
425fca1dd03SJonghwa Lee {
426fca1dd03SJonghwa Lee struct max77686_rtc_info *info = dev_get_drvdata(dev);
427fca1dd03SJonghwa Lee u8 data[RTC_NR_TIME];
428fca1dd03SJonghwa Lee unsigned int val;
42990a5698aSJavier Martinez Canillas const unsigned int *map = info->drv_data->map;
430fca1dd03SJonghwa Lee int i, ret;
431fca1dd03SJonghwa Lee
432fca1dd03SJonghwa Lee mutex_lock(&info->lock);
433fca1dd03SJonghwa Lee
434fca1dd03SJonghwa Lee ret = max77686_rtc_update(info, MAX77686_RTC_READ);
435fca1dd03SJonghwa Lee if (ret < 0)
436fca1dd03SJonghwa Lee goto out;
437fca1dd03SJonghwa Lee
438f604c488SLaxman Dewangan ret = regmap_bulk_read(info->rtc_regmap, map[REG_ALARM1_SEC],
439f604c488SLaxman Dewangan data, ARRAY_SIZE(data));
440fca1dd03SJonghwa Lee if (ret < 0) {
441bf035f42SKrzysztof Kozlowski dev_err(info->dev, "Fail to read alarm reg(%d)\n", ret);
442fca1dd03SJonghwa Lee goto out;
443fca1dd03SJonghwa Lee }
444fca1dd03SJonghwa Lee
44501ea01b3SJavier Martinez Canillas max77686_rtc_data_to_tm(data, &alrm->time, info);
446fca1dd03SJonghwa Lee
447fca1dd03SJonghwa Lee alrm->enabled = 0;
448f903129bSJavier Martinez Canillas
449f903129bSJavier Martinez Canillas if (info->drv_data->alarm_enable_reg) {
450f903129bSJavier Martinez Canillas if (map[REG_RTC_AE1] == REG_RTC_NONE) {
451f903129bSJavier Martinez Canillas ret = -EINVAL;
452f903129bSJavier Martinez Canillas dev_err(info->dev,
453f903129bSJavier Martinez Canillas "alarm enable register not set(%d)\n", ret);
454f903129bSJavier Martinez Canillas goto out;
455f903129bSJavier Martinez Canillas }
456f903129bSJavier Martinez Canillas
457f604c488SLaxman Dewangan ret = regmap_read(info->rtc_regmap, map[REG_RTC_AE1], &val);
458f903129bSJavier Martinez Canillas if (ret < 0) {
459f903129bSJavier Martinez Canillas dev_err(info->dev,
460f903129bSJavier Martinez Canillas "fail to read alarm enable(%d)\n", ret);
461f903129bSJavier Martinez Canillas goto out;
462f903129bSJavier Martinez Canillas }
463f903129bSJavier Martinez Canillas
464f903129bSJavier Martinez Canillas if (val)
465f903129bSJavier Martinez Canillas alrm->enabled = 1;
466f903129bSJavier Martinez Canillas } else {
467862f9453SJavier Martinez Canillas for (i = 0; i < ARRAY_SIZE(data); i++) {
468fca1dd03SJonghwa Lee if (data[i] & ALARM_ENABLE_MASK) {
469fca1dd03SJonghwa Lee alrm->enabled = 1;
470fca1dd03SJonghwa Lee break;
471fca1dd03SJonghwa Lee }
472fca1dd03SJonghwa Lee }
473f903129bSJavier Martinez Canillas }
474fca1dd03SJonghwa Lee
475fca1dd03SJonghwa Lee alrm->pending = 0;
476726fe738SLaxman Dewangan
477726fe738SLaxman Dewangan if (info->drv_data->alarm_pending_status_reg == MAX77686_INVALID_REG)
478726fe738SLaxman Dewangan goto out;
479726fe738SLaxman Dewangan
480726fe738SLaxman Dewangan ret = regmap_read(info->regmap,
481726fe738SLaxman Dewangan info->drv_data->alarm_pending_status_reg, &val);
482fca1dd03SJonghwa Lee if (ret < 0) {
483726fe738SLaxman Dewangan dev_err(info->dev,
484726fe738SLaxman Dewangan "Fail to read alarm pending status reg(%d)\n", ret);
485fca1dd03SJonghwa Lee goto out;
486fca1dd03SJonghwa Lee }
487fca1dd03SJonghwa Lee
488fca1dd03SJonghwa Lee if (val & (1 << 4)) /* RTCA1 */
489fca1dd03SJonghwa Lee alrm->pending = 1;
490fca1dd03SJonghwa Lee
491fca1dd03SJonghwa Lee out:
492fca1dd03SJonghwa Lee mutex_unlock(&info->lock);
4937cdffeb5SJavier Martinez Canillas return ret;
494fca1dd03SJonghwa Lee }
495fca1dd03SJonghwa Lee
max77686_rtc_stop_alarm(struct max77686_rtc_info * info)496fca1dd03SJonghwa Lee static int max77686_rtc_stop_alarm(struct max77686_rtc_info *info)
497fca1dd03SJonghwa Lee {
498fca1dd03SJonghwa Lee u8 data[RTC_NR_TIME];
499fca1dd03SJonghwa Lee int ret, i;
500fca1dd03SJonghwa Lee struct rtc_time tm;
50190a5698aSJavier Martinez Canillas const unsigned int *map = info->drv_data->map;
502fca1dd03SJonghwa Lee
503fca1dd03SJonghwa Lee if (!mutex_is_locked(&info->lock))
504fca1dd03SJonghwa Lee dev_warn(info->dev, "%s: should have mutex locked\n", __func__);
505fca1dd03SJonghwa Lee
506fca1dd03SJonghwa Lee ret = max77686_rtc_update(info, MAX77686_RTC_READ);
507fca1dd03SJonghwa Lee if (ret < 0)
508fca1dd03SJonghwa Lee goto out;
509fca1dd03SJonghwa Lee
510f903129bSJavier Martinez Canillas if (info->drv_data->alarm_enable_reg) {
511f903129bSJavier Martinez Canillas if (map[REG_RTC_AE1] == REG_RTC_NONE) {
512f903129bSJavier Martinez Canillas ret = -EINVAL;
513f903129bSJavier Martinez Canillas dev_err(info->dev,
514f903129bSJavier Martinez Canillas "alarm enable register not set(%d)\n", ret);
515f903129bSJavier Martinez Canillas goto out;
516f903129bSJavier Martinez Canillas }
517f903129bSJavier Martinez Canillas
518f604c488SLaxman Dewangan ret = regmap_write(info->rtc_regmap, map[REG_RTC_AE1], 0);
519f903129bSJavier Martinez Canillas } else {
520f604c488SLaxman Dewangan ret = regmap_bulk_read(info->rtc_regmap, map[REG_ALARM1_SEC],
521f604c488SLaxman Dewangan data, ARRAY_SIZE(data));
522fca1dd03SJonghwa Lee if (ret < 0) {
523bf035f42SKrzysztof Kozlowski dev_err(info->dev, "Fail to read alarm reg(%d)\n", ret);
524fca1dd03SJonghwa Lee goto out;
525fca1dd03SJonghwa Lee }
526fca1dd03SJonghwa Lee
52701ea01b3SJavier Martinez Canillas max77686_rtc_data_to_tm(data, &tm, info);
528fca1dd03SJonghwa Lee
529862f9453SJavier Martinez Canillas for (i = 0; i < ARRAY_SIZE(data); i++)
530fca1dd03SJonghwa Lee data[i] &= ~ALARM_ENABLE_MASK;
531fca1dd03SJonghwa Lee
532f604c488SLaxman Dewangan ret = regmap_bulk_write(info->rtc_regmap, map[REG_ALARM1_SEC],
533f604c488SLaxman Dewangan data, ARRAY_SIZE(data));
534f903129bSJavier Martinez Canillas }
535f903129bSJavier Martinez Canillas
536fca1dd03SJonghwa Lee if (ret < 0) {
537bf035f42SKrzysztof Kozlowski dev_err(info->dev, "Fail to write alarm reg(%d)\n", ret);
538fca1dd03SJonghwa Lee goto out;
539fca1dd03SJonghwa Lee }
540fca1dd03SJonghwa Lee
541fca1dd03SJonghwa Lee ret = max77686_rtc_update(info, MAX77686_RTC_WRITE);
542fca1dd03SJonghwa Lee out:
543fca1dd03SJonghwa Lee return ret;
544fca1dd03SJonghwa Lee }
545fca1dd03SJonghwa Lee
max77686_rtc_start_alarm(struct max77686_rtc_info * info)546fca1dd03SJonghwa Lee static int max77686_rtc_start_alarm(struct max77686_rtc_info *info)
547fca1dd03SJonghwa Lee {
548fca1dd03SJonghwa Lee u8 data[RTC_NR_TIME];
549fca1dd03SJonghwa Lee int ret;
550fca1dd03SJonghwa Lee struct rtc_time tm;
55190a5698aSJavier Martinez Canillas const unsigned int *map = info->drv_data->map;
552fca1dd03SJonghwa Lee
553fca1dd03SJonghwa Lee if (!mutex_is_locked(&info->lock))
554fca1dd03SJonghwa Lee dev_warn(info->dev, "%s: should have mutex locked\n", __func__);
555fca1dd03SJonghwa Lee
556fca1dd03SJonghwa Lee ret = max77686_rtc_update(info, MAX77686_RTC_READ);
557fca1dd03SJonghwa Lee if (ret < 0)
558fca1dd03SJonghwa Lee goto out;
559fca1dd03SJonghwa Lee
560f903129bSJavier Martinez Canillas if (info->drv_data->alarm_enable_reg) {
561f604c488SLaxman Dewangan ret = regmap_write(info->rtc_regmap, map[REG_RTC_AE1],
562f903129bSJavier Martinez Canillas MAX77802_ALARM_ENABLE_VALUE);
563f903129bSJavier Martinez Canillas } else {
564f604c488SLaxman Dewangan ret = regmap_bulk_read(info->rtc_regmap, map[REG_ALARM1_SEC],
565f604c488SLaxman Dewangan data, ARRAY_SIZE(data));
566fca1dd03SJonghwa Lee if (ret < 0) {
567bf035f42SKrzysztof Kozlowski dev_err(info->dev, "Fail to read alarm reg(%d)\n", ret);
568fca1dd03SJonghwa Lee goto out;
569fca1dd03SJonghwa Lee }
570fca1dd03SJonghwa Lee
57101ea01b3SJavier Martinez Canillas max77686_rtc_data_to_tm(data, &tm, info);
572fca1dd03SJonghwa Lee
573fca1dd03SJonghwa Lee data[RTC_SEC] |= (1 << ALARM_ENABLE_SHIFT);
574fca1dd03SJonghwa Lee data[RTC_MIN] |= (1 << ALARM_ENABLE_SHIFT);
575fca1dd03SJonghwa Lee data[RTC_HOUR] |= (1 << ALARM_ENABLE_SHIFT);
576fca1dd03SJonghwa Lee data[RTC_WEEKDAY] &= ~ALARM_ENABLE_MASK;
577fca1dd03SJonghwa Lee if (data[RTC_MONTH] & 0xf)
578fca1dd03SJonghwa Lee data[RTC_MONTH] |= (1 << ALARM_ENABLE_SHIFT);
57901ea01b3SJavier Martinez Canillas if (data[RTC_YEAR] & info->drv_data->mask)
580fca1dd03SJonghwa Lee data[RTC_YEAR] |= (1 << ALARM_ENABLE_SHIFT);
581a26d8463SLuca Ceresoli if (data[RTC_MONTHDAY] & 0x1f)
582a26d8463SLuca Ceresoli data[RTC_MONTHDAY] |= (1 << ALARM_ENABLE_SHIFT);
583fca1dd03SJonghwa Lee
584f604c488SLaxman Dewangan ret = regmap_bulk_write(info->rtc_regmap, map[REG_ALARM1_SEC],
585f604c488SLaxman Dewangan data, ARRAY_SIZE(data));
586f903129bSJavier Martinez Canillas }
587f903129bSJavier Martinez Canillas
588fca1dd03SJonghwa Lee if (ret < 0) {
589bf035f42SKrzysztof Kozlowski dev_err(info->dev, "Fail to write alarm reg(%d)\n", ret);
590fca1dd03SJonghwa Lee goto out;
591fca1dd03SJonghwa Lee }
592fca1dd03SJonghwa Lee
593fca1dd03SJonghwa Lee ret = max77686_rtc_update(info, MAX77686_RTC_WRITE);
594fca1dd03SJonghwa Lee out:
595fca1dd03SJonghwa Lee return ret;
596fca1dd03SJonghwa Lee }
597fca1dd03SJonghwa Lee
max77686_rtc_set_alarm(struct device * dev,struct rtc_wkalrm * alrm)598fca1dd03SJonghwa Lee static int max77686_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
599fca1dd03SJonghwa Lee {
600fca1dd03SJonghwa Lee struct max77686_rtc_info *info = dev_get_drvdata(dev);
601fca1dd03SJonghwa Lee u8 data[RTC_NR_TIME];
602fca1dd03SJonghwa Lee int ret;
603fca1dd03SJonghwa Lee
604f903129bSJavier Martinez Canillas ret = max77686_rtc_tm_to_data(&alrm->time, data, info);
605fca1dd03SJonghwa Lee if (ret < 0)
606fca1dd03SJonghwa Lee return ret;
607fca1dd03SJonghwa Lee
608fca1dd03SJonghwa Lee mutex_lock(&info->lock);
609fca1dd03SJonghwa Lee
610fca1dd03SJonghwa Lee ret = max77686_rtc_stop_alarm(info);
611fca1dd03SJonghwa Lee if (ret < 0)
612fca1dd03SJonghwa Lee goto out;
613fca1dd03SJonghwa Lee
614f604c488SLaxman Dewangan ret = regmap_bulk_write(info->rtc_regmap,
61590a5698aSJavier Martinez Canillas info->drv_data->map[REG_ALARM1_SEC],
61690a5698aSJavier Martinez Canillas data, ARRAY_SIZE(data));
617fca1dd03SJonghwa Lee
618fca1dd03SJonghwa Lee if (ret < 0) {
619bf035f42SKrzysztof Kozlowski dev_err(info->dev, "Fail to write alarm reg(%d)\n", ret);
620fca1dd03SJonghwa Lee goto out;
621fca1dd03SJonghwa Lee }
622fca1dd03SJonghwa Lee
623fca1dd03SJonghwa Lee ret = max77686_rtc_update(info, MAX77686_RTC_WRITE);
624fca1dd03SJonghwa Lee if (ret < 0)
625fca1dd03SJonghwa Lee goto out;
626fca1dd03SJonghwa Lee
627fca1dd03SJonghwa Lee if (alrm->enabled)
628fca1dd03SJonghwa Lee ret = max77686_rtc_start_alarm(info);
629fca1dd03SJonghwa Lee out:
630fca1dd03SJonghwa Lee mutex_unlock(&info->lock);
631fca1dd03SJonghwa Lee return ret;
632fca1dd03SJonghwa Lee }
633fca1dd03SJonghwa Lee
max77686_rtc_alarm_irq_enable(struct device * dev,unsigned int enabled)634fca1dd03SJonghwa Lee static int max77686_rtc_alarm_irq_enable(struct device *dev,
635fca1dd03SJonghwa Lee unsigned int enabled)
636fca1dd03SJonghwa Lee {
637fca1dd03SJonghwa Lee struct max77686_rtc_info *info = dev_get_drvdata(dev);
638fca1dd03SJonghwa Lee int ret;
639fca1dd03SJonghwa Lee
640fca1dd03SJonghwa Lee mutex_lock(&info->lock);
641fca1dd03SJonghwa Lee if (enabled)
642fca1dd03SJonghwa Lee ret = max77686_rtc_start_alarm(info);
643fca1dd03SJonghwa Lee else
644fca1dd03SJonghwa Lee ret = max77686_rtc_stop_alarm(info);
645fca1dd03SJonghwa Lee mutex_unlock(&info->lock);
646fca1dd03SJonghwa Lee
647fca1dd03SJonghwa Lee return ret;
648fca1dd03SJonghwa Lee }
649fca1dd03SJonghwa Lee
max77686_rtc_alarm_irq(int irq,void * data)650fca1dd03SJonghwa Lee static irqreturn_t max77686_rtc_alarm_irq(int irq, void *data)
651fca1dd03SJonghwa Lee {
652fca1dd03SJonghwa Lee struct max77686_rtc_info *info = data;
653fca1dd03SJonghwa Lee
654bf035f42SKrzysztof Kozlowski dev_dbg(info->dev, "RTC alarm IRQ: %d\n", irq);
655fca1dd03SJonghwa Lee
656fca1dd03SJonghwa Lee rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF);
657fca1dd03SJonghwa Lee
658fca1dd03SJonghwa Lee return IRQ_HANDLED;
659fca1dd03SJonghwa Lee }
660fca1dd03SJonghwa Lee
661fca1dd03SJonghwa Lee static const struct rtc_class_ops max77686_rtc_ops = {
662fca1dd03SJonghwa Lee .read_time = max77686_rtc_read_time,
663fca1dd03SJonghwa Lee .set_time = max77686_rtc_set_time,
664fca1dd03SJonghwa Lee .read_alarm = max77686_rtc_read_alarm,
665fca1dd03SJonghwa Lee .set_alarm = max77686_rtc_set_alarm,
666fca1dd03SJonghwa Lee .alarm_irq_enable = max77686_rtc_alarm_irq_enable,
667fca1dd03SJonghwa Lee };
668fca1dd03SJonghwa Lee
max77686_rtc_init_reg(struct max77686_rtc_info * info)669fca1dd03SJonghwa Lee static int max77686_rtc_init_reg(struct max77686_rtc_info *info)
670fca1dd03SJonghwa Lee {
671fca1dd03SJonghwa Lee u8 data[2];
672fca1dd03SJonghwa Lee int ret;
673fca1dd03SJonghwa Lee
674fca1dd03SJonghwa Lee /* Set RTC control register : Binary mode, 24hour mdoe */
675fca1dd03SJonghwa Lee data[0] = (1 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT);
676fca1dd03SJonghwa Lee data[1] = (0 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT);
677fca1dd03SJonghwa Lee
678f604c488SLaxman Dewangan ret = regmap_bulk_write(info->rtc_regmap,
67990a5698aSJavier Martinez Canillas info->drv_data->map[REG_RTC_CONTROLM],
68090a5698aSJavier Martinez Canillas data, ARRAY_SIZE(data));
681fca1dd03SJonghwa Lee if (ret < 0) {
682bf035f42SKrzysztof Kozlowski dev_err(info->dev, "Fail to write controlm reg(%d)\n", ret);
683fca1dd03SJonghwa Lee return ret;
684fca1dd03SJonghwa Lee }
685fca1dd03SJonghwa Lee
686fca1dd03SJonghwa Lee ret = max77686_rtc_update(info, MAX77686_RTC_WRITE);
687fca1dd03SJonghwa Lee return ret;
688fca1dd03SJonghwa Lee }
689fca1dd03SJonghwa Lee
max77686_init_rtc_regmap(struct max77686_rtc_info * info)690f3937549SLaxman Dewangan static int max77686_init_rtc_regmap(struct max77686_rtc_info *info)
691f3937549SLaxman Dewangan {
692f3937549SLaxman Dewangan struct device *parent = info->dev->parent;
693f3937549SLaxman Dewangan struct i2c_client *parent_i2c = to_i2c_client(parent);
694f3937549SLaxman Dewangan int ret;
695f3937549SLaxman Dewangan
696726fe738SLaxman Dewangan if (info->drv_data->rtc_irq_from_platform) {
697726fe738SLaxman Dewangan struct platform_device *pdev = to_platform_device(info->dev);
698726fe738SLaxman Dewangan
699726fe738SLaxman Dewangan info->rtc_irq = platform_get_irq(pdev, 0);
700faac9102SStephen Boyd if (info->rtc_irq < 0)
701726fe738SLaxman Dewangan return info->rtc_irq;
702726fe738SLaxman Dewangan } else {
703f3937549SLaxman Dewangan info->rtc_irq = parent_i2c->irq;
704726fe738SLaxman Dewangan }
705f3937549SLaxman Dewangan
706f3937549SLaxman Dewangan info->regmap = dev_get_regmap(parent, NULL);
707f3937549SLaxman Dewangan if (!info->regmap) {
708f3937549SLaxman Dewangan dev_err(info->dev, "Failed to get rtc regmap\n");
709f3937549SLaxman Dewangan return -ENODEV;
710f3937549SLaxman Dewangan }
711f3937549SLaxman Dewangan
712f3937549SLaxman Dewangan if (info->drv_data->rtc_i2c_addr == MAX77686_INVALID_I2C_ADDR) {
713f3937549SLaxman Dewangan info->rtc_regmap = info->regmap;
714f3937549SLaxman Dewangan goto add_rtc_irq;
715f3937549SLaxman Dewangan }
716f3937549SLaxman Dewangan
71759a7f24fSWolfram Sang info->rtc = devm_i2c_new_dummy_device(info->dev, parent_i2c->adapter,
718f3937549SLaxman Dewangan info->drv_data->rtc_i2c_addr);
7197150710fSWolfram Sang if (IS_ERR(info->rtc)) {
720f3937549SLaxman Dewangan dev_err(info->dev, "Failed to allocate I2C device for RTC\n");
7217150710fSWolfram Sang return PTR_ERR(info->rtc);
722f3937549SLaxman Dewangan }
723f3937549SLaxman Dewangan
724f3937549SLaxman Dewangan info->rtc_regmap = devm_regmap_init_i2c(info->rtc,
72563a52f63SThierry Reding info->drv_data->regmap_config);
726f3937549SLaxman Dewangan if (IS_ERR(info->rtc_regmap)) {
727f3937549SLaxman Dewangan ret = PTR_ERR(info->rtc_regmap);
728f3937549SLaxman Dewangan dev_err(info->dev, "Failed to allocate RTC regmap: %d\n", ret);
72959a7f24fSWolfram Sang return ret;
730f3937549SLaxman Dewangan }
731f3937549SLaxman Dewangan
732f3937549SLaxman Dewangan add_rtc_irq:
733f3937549SLaxman Dewangan ret = regmap_add_irq_chip(info->rtc_regmap, info->rtc_irq,
734742b0d7eSKrzysztof Kozlowski IRQF_ONESHOT | IRQF_SHARED,
735742b0d7eSKrzysztof Kozlowski 0, info->drv_data->rtc_irq_chip,
736f3937549SLaxman Dewangan &info->rtc_irq_data);
737f3937549SLaxman Dewangan if (ret < 0) {
738f3937549SLaxman Dewangan dev_err(info->dev, "Failed to add RTC irq chip: %d\n", ret);
73959a7f24fSWolfram Sang return ret;
740f3937549SLaxman Dewangan }
741f3937549SLaxman Dewangan
742f3937549SLaxman Dewangan return 0;
743f3937549SLaxman Dewangan }
744f3937549SLaxman Dewangan
max77686_rtc_probe(struct platform_device * pdev)745fca1dd03SJonghwa Lee static int max77686_rtc_probe(struct platform_device *pdev)
746fca1dd03SJonghwa Lee {
747fca1dd03SJonghwa Lee struct max77686_rtc_info *info;
74801ea01b3SJavier Martinez Canillas const struct platform_device_id *id = platform_get_device_id(pdev);
7496f1c1e71SJavier Martinez Canillas int ret;
750fca1dd03SJonghwa Lee
7510f64f853SJingoo Han info = devm_kzalloc(&pdev->dev, sizeof(struct max77686_rtc_info),
7520f64f853SJingoo Han GFP_KERNEL);
753fca1dd03SJonghwa Lee if (!info)
754fca1dd03SJonghwa Lee return -ENOMEM;
755fca1dd03SJonghwa Lee
756fca1dd03SJonghwa Lee mutex_init(&info->lock);
757fca1dd03SJonghwa Lee info->dev = &pdev->dev;
75801ea01b3SJavier Martinez Canillas info->drv_data = (const struct max77686_rtc_driver_data *)
75901ea01b3SJavier Martinez Canillas id->driver_data;
7606f1c1e71SJavier Martinez Canillas
761f3937549SLaxman Dewangan ret = max77686_init_rtc_regmap(info);
762f3937549SLaxman Dewangan if (ret < 0)
763f3937549SLaxman Dewangan return ret;
764f903129bSJavier Martinez Canillas
765fca1dd03SJonghwa Lee platform_set_drvdata(pdev, info);
766fca1dd03SJonghwa Lee
767fca1dd03SJonghwa Lee ret = max77686_rtc_init_reg(info);
768fca1dd03SJonghwa Lee if (ret < 0) {
769fca1dd03SJonghwa Lee dev_err(&pdev->dev, "Failed to initialize RTC reg:%d\n", ret);
770fca1dd03SJonghwa Lee goto err_rtc;
771fca1dd03SJonghwa Lee }
772fca1dd03SJonghwa Lee
773fca1dd03SJonghwa Lee device_init_wakeup(&pdev->dev, 1);
774fca1dd03SJonghwa Lee
775f903129bSJavier Martinez Canillas info->rtc_dev = devm_rtc_device_register(&pdev->dev, id->name,
776fca1dd03SJonghwa Lee &max77686_rtc_ops, THIS_MODULE);
777fca1dd03SJonghwa Lee
778fca1dd03SJonghwa Lee if (IS_ERR(info->rtc_dev)) {
779fca1dd03SJonghwa Lee ret = PTR_ERR(info->rtc_dev);
780fca1dd03SJonghwa Lee dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret);
781fca1dd03SJonghwa Lee if (ret == 0)
782fca1dd03SJonghwa Lee ret = -EINVAL;
783fca1dd03SJonghwa Lee goto err_rtc;
784fca1dd03SJonghwa Lee }
7856f1c1e71SJavier Martinez Canillas
786f3937549SLaxman Dewangan info->virq = regmap_irq_get_virq(info->rtc_irq_data,
7876f1c1e71SJavier Martinez Canillas MAX77686_RTCIRQ_RTCA1);
788fb166ba1SKrzysztof Kozlowski if (info->virq <= 0) {
789ad819039SSachin Kamat ret = -ENXIO;
790fca1dd03SJonghwa Lee goto err_rtc;
791ad819039SSachin Kamat }
792fca1dd03SJonghwa Lee
793f3937549SLaxman Dewangan ret = request_threaded_irq(info->virq, NULL, max77686_rtc_alarm_irq, 0,
7940b4f8b08SLaxman Dewangan "rtc-alarm1", info);
795f3937549SLaxman Dewangan if (ret < 0) {
796fca1dd03SJonghwa Lee dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n",
797fca1dd03SJonghwa Lee info->virq, ret);
798f3937549SLaxman Dewangan goto err_rtc;
799f3937549SLaxman Dewangan }
800f3937549SLaxman Dewangan
801f3937549SLaxman Dewangan return 0;
802fca1dd03SJonghwa Lee
803fca1dd03SJonghwa Lee err_rtc:
804f3937549SLaxman Dewangan regmap_del_irq_chip(info->rtc_irq, info->rtc_irq_data);
805f3937549SLaxman Dewangan
806fca1dd03SJonghwa Lee return ret;
807fca1dd03SJonghwa Lee }
808fca1dd03SJonghwa Lee
max77686_rtc_remove(struct platform_device * pdev)809*bf05de01SUwe Kleine-König static void max77686_rtc_remove(struct platform_device *pdev)
810f3937549SLaxman Dewangan {
811f3937549SLaxman Dewangan struct max77686_rtc_info *info = platform_get_drvdata(pdev);
812f3937549SLaxman Dewangan
813f3937549SLaxman Dewangan free_irq(info->virq, info);
814f3937549SLaxman Dewangan regmap_del_irq_chip(info->rtc_irq, info->rtc_irq_data);
815f3937549SLaxman Dewangan }
816f3937549SLaxman Dewangan
817e7f7fc73SDoug Anderson #ifdef CONFIG_PM_SLEEP
max77686_rtc_suspend(struct device * dev)818e7f7fc73SDoug Anderson static int max77686_rtc_suspend(struct device *dev)
819e7f7fc73SDoug Anderson {
820d8f090dbSKrzysztof Kozlowski struct max77686_rtc_info *info = dev_get_drvdata(dev);
821d8f090dbSKrzysztof Kozlowski int ret = 0;
822d8f090dbSKrzysztof Kozlowski
823e7f7fc73SDoug Anderson if (device_may_wakeup(dev)) {
824e7f7fc73SDoug Anderson struct max77686_rtc_info *info = dev_get_drvdata(dev);
825e7f7fc73SDoug Anderson
826d8f090dbSKrzysztof Kozlowski ret = enable_irq_wake(info->virq);
827e7f7fc73SDoug Anderson }
828e7f7fc73SDoug Anderson
829d8f090dbSKrzysztof Kozlowski /*
83016c24801SJon Hunter * If the main IRQ (not virtual) is the parent IRQ, then it must be
83116c24801SJon Hunter * disabled during suspend because if it happens while suspended it
83216c24801SJon Hunter * will be handled before resuming I2C.
833d8f090dbSKrzysztof Kozlowski *
834d8f090dbSKrzysztof Kozlowski * Since Main IRQ is shared, all its users should disable it to be sure
835d8f090dbSKrzysztof Kozlowski * it won't fire while one of them is still suspended.
836d8f090dbSKrzysztof Kozlowski */
83716c24801SJon Hunter if (!info->drv_data->rtc_irq_from_platform)
838d8f090dbSKrzysztof Kozlowski disable_irq(info->rtc_irq);
839d8f090dbSKrzysztof Kozlowski
840d8f090dbSKrzysztof Kozlowski return ret;
841e7f7fc73SDoug Anderson }
842e7f7fc73SDoug Anderson
max77686_rtc_resume(struct device * dev)843e7f7fc73SDoug Anderson static int max77686_rtc_resume(struct device *dev)
844e7f7fc73SDoug Anderson {
845d8f090dbSKrzysztof Kozlowski struct max77686_rtc_info *info = dev_get_drvdata(dev);
846d8f090dbSKrzysztof Kozlowski
84716c24801SJon Hunter if (!info->drv_data->rtc_irq_from_platform)
848d8f090dbSKrzysztof Kozlowski enable_irq(info->rtc_irq);
849d8f090dbSKrzysztof Kozlowski
850e7f7fc73SDoug Anderson if (device_may_wakeup(dev)) {
851e7f7fc73SDoug Anderson struct max77686_rtc_info *info = dev_get_drvdata(dev);
852e7f7fc73SDoug Anderson
853e7f7fc73SDoug Anderson return disable_irq_wake(info->virq);
854e7f7fc73SDoug Anderson }
855e7f7fc73SDoug Anderson
856e7f7fc73SDoug Anderson return 0;
857e7f7fc73SDoug Anderson }
858e7f7fc73SDoug Anderson #endif
859e7f7fc73SDoug Anderson
860e7f7fc73SDoug Anderson static SIMPLE_DEV_PM_OPS(max77686_rtc_pm_ops,
861e7f7fc73SDoug Anderson max77686_rtc_suspend, max77686_rtc_resume);
862e7f7fc73SDoug Anderson
863fca1dd03SJonghwa Lee static const struct platform_device_id rtc_id[] = {
86401ea01b3SJavier Martinez Canillas { "max77686-rtc", .driver_data = (kernel_ulong_t)&max77686_drv_data, },
865f903129bSJavier Martinez Canillas { "max77802-rtc", .driver_data = (kernel_ulong_t)&max77802_drv_data, },
866726fe738SLaxman Dewangan { "max77620-rtc", .driver_data = (kernel_ulong_t)&max77620_drv_data, },
867c58e4963SLuca Ceresoli { "max77714-rtc", .driver_data = (kernel_ulong_t)&max77714_drv_data, },
868fca1dd03SJonghwa Lee {},
869fca1dd03SJonghwa Lee };
8702d0cca0dSJavier Martinez Canillas MODULE_DEVICE_TABLE(platform, rtc_id);
871fca1dd03SJonghwa Lee
872fca1dd03SJonghwa Lee static struct platform_driver max77686_rtc_driver = {
873fca1dd03SJonghwa Lee .driver = {
874fca1dd03SJonghwa Lee .name = "max77686-rtc",
875e7f7fc73SDoug Anderson .pm = &max77686_rtc_pm_ops,
876fca1dd03SJonghwa Lee },
877fca1dd03SJonghwa Lee .probe = max77686_rtc_probe,
878*bf05de01SUwe Kleine-König .remove_new = max77686_rtc_remove,
879fca1dd03SJonghwa Lee .id_table = rtc_id,
880fca1dd03SJonghwa Lee };
881fca1dd03SJonghwa Lee
8820c58ff58SJingoo Han module_platform_driver(max77686_rtc_driver);
883fca1dd03SJonghwa Lee
884fca1dd03SJonghwa Lee MODULE_DESCRIPTION("Maxim MAX77686 RTC driver");
885f5b1d3c5SJingoo Han MODULE_AUTHOR("Chiwoong Byun <woong.byun@samsung.com>");
886fca1dd03SJonghwa Lee MODULE_LICENSE("GPL");
887