xref: /openbmc/linux/drivers/rtc/rtc-max77686.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
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