xref: /openbmc/linux/drivers/rtc/rtc-pm8xxx.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
197fb5e8dSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
23ca04951SJohan Hovold /*
33ca04951SJohan Hovold  * pm8xxx RTC driver
43ca04951SJohan Hovold  *
53ca04951SJohan Hovold  * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
63ca04951SJohan Hovold  * Copyright (c) 2023, Linaro Limited
79a9a54adSAnirudh Ghayal  */
85a418558SJosh Cartwright #include <linux/of.h>
99a9a54adSAnirudh Ghayal #include <linux/module.h>
103ca04951SJohan Hovold #include <linux/nvmem-consumer.h>
119a9a54adSAnirudh Ghayal #include <linux/init.h>
129a9a54adSAnirudh Ghayal #include <linux/rtc.h>
135d7dc4cfSJosh Cartwright #include <linux/platform_device.h>
149a9a54adSAnirudh Ghayal #include <linux/pm.h>
15b5bf5b28SLoic Poulain #include <linux/pm_wakeirq.h>
165d7dc4cfSJosh Cartwright #include <linux/regmap.h>
179a9a54adSAnirudh Ghayal #include <linux/slab.h>
189a9a54adSAnirudh Ghayal #include <linux/spinlock.h>
199a9a54adSAnirudh Ghayal 
2079dd7566SJohan Hovold #include <asm/unaligned.h>
2179dd7566SJohan Hovold 
229a9a54adSAnirudh Ghayal /* RTC_CTRL register bit fields */
239a9a54adSAnirudh Ghayal #define PM8xxx_RTC_ENABLE		BIT(7)
249a9a54adSAnirudh Ghayal #define PM8xxx_RTC_ALARM_CLEAR		BIT(0)
25121f54efSGuixiong Wei #define PM8xxx_RTC_ALARM_ENABLE		BIT(7)
269a9a54adSAnirudh Ghayal 
279a9a54adSAnirudh Ghayal #define NUM_8_BIT_RTC_REGS		0x4
289a9a54adSAnirudh Ghayal 
299a9a54adSAnirudh Ghayal /**
30c8d523a4SStanimir Varbanov  * struct pm8xxx_rtc_regs - describe RTC registers per PMIC versions
313c332639SJohan Hovold  * @ctrl:		address of control register
323c332639SJohan Hovold  * @write:		base address of write registers
333c332639SJohan Hovold  * @read:		base address of read registers
343c332639SJohan Hovold  * @alarm_ctrl:		address of alarm control register
353c332639SJohan Hovold  * @alarm_ctrl2:	address of alarm control2 register
363c332639SJohan Hovold  * @alarm_rw:		base address of alarm read-write registers
37c8d523a4SStanimir Varbanov  * @alarm_en:		alarm enable mask
38c8d523a4SStanimir Varbanov  */
39c8d523a4SStanimir Varbanov struct pm8xxx_rtc_regs {
40c8d523a4SStanimir Varbanov 	unsigned int ctrl;
41c8d523a4SStanimir Varbanov 	unsigned int write;
42c8d523a4SStanimir Varbanov 	unsigned int read;
43c8d523a4SStanimir Varbanov 	unsigned int alarm_ctrl;
44c8d523a4SStanimir Varbanov 	unsigned int alarm_ctrl2;
45c8d523a4SStanimir Varbanov 	unsigned int alarm_rw;
46c8d523a4SStanimir Varbanov 	unsigned int alarm_en;
47c8d523a4SStanimir Varbanov };
48c8d523a4SStanimir Varbanov 
49c8d523a4SStanimir Varbanov /**
503c332639SJohan Hovold  * struct pm8xxx_rtc -  RTC driver internal structure
513c332639SJohan Hovold  * @rtc:		RTC device
523c332639SJohan Hovold  * @regmap:		regmap used to access registers
533c332639SJohan Hovold  * @allow_set_time:	whether the time can be set
544727b58fSJohan Hovold  * @alarm_irq:		alarm irq number
553c332639SJohan Hovold  * @regs:		register description
56a375510eSJohan Hovold  * @dev:		device structure
573ca04951SJohan Hovold  * @nvmem_cell:		nvmem cell for offset
583ca04951SJohan Hovold  * @offset:		offset from epoch in seconds
599a9a54adSAnirudh Ghayal  */
609a9a54adSAnirudh Ghayal struct pm8xxx_rtc {
619a9a54adSAnirudh Ghayal 	struct rtc_device *rtc;
625d7dc4cfSJosh Cartwright 	struct regmap *regmap;
635a418558SJosh Cartwright 	bool allow_set_time;
644727b58fSJohan Hovold 	int alarm_irq;
65c8d523a4SStanimir Varbanov 	const struct pm8xxx_rtc_regs *regs;
66a375510eSJohan Hovold 	struct device *dev;
673ca04951SJohan Hovold 	struct nvmem_cell *nvmem_cell;
683ca04951SJohan Hovold 	u32 offset;
699a9a54adSAnirudh Ghayal };
709a9a54adSAnirudh Ghayal 
pm8xxx_rtc_read_nvmem_offset(struct pm8xxx_rtc * rtc_dd)713ca04951SJohan Hovold static int pm8xxx_rtc_read_nvmem_offset(struct pm8xxx_rtc *rtc_dd)
723ca04951SJohan Hovold {
733ca04951SJohan Hovold 	size_t len;
743ca04951SJohan Hovold 	void *buf;
753ca04951SJohan Hovold 	int rc;
763ca04951SJohan Hovold 
773ca04951SJohan Hovold 	buf = nvmem_cell_read(rtc_dd->nvmem_cell, &len);
783ca04951SJohan Hovold 	if (IS_ERR(buf)) {
793ca04951SJohan Hovold 		rc = PTR_ERR(buf);
803ca04951SJohan Hovold 		dev_dbg(rtc_dd->dev, "failed to read nvmem offset: %d\n", rc);
813ca04951SJohan Hovold 		return rc;
823ca04951SJohan Hovold 	}
833ca04951SJohan Hovold 
843ca04951SJohan Hovold 	if (len != sizeof(u32)) {
853ca04951SJohan Hovold 		dev_dbg(rtc_dd->dev, "unexpected nvmem cell size %zu\n", len);
863ca04951SJohan Hovold 		kfree(buf);
873ca04951SJohan Hovold 		return -EINVAL;
883ca04951SJohan Hovold 	}
893ca04951SJohan Hovold 
903ca04951SJohan Hovold 	rtc_dd->offset = get_unaligned_le32(buf);
913ca04951SJohan Hovold 
923ca04951SJohan Hovold 	kfree(buf);
933ca04951SJohan Hovold 
943ca04951SJohan Hovold 	return 0;
953ca04951SJohan Hovold }
963ca04951SJohan Hovold 
pm8xxx_rtc_write_nvmem_offset(struct pm8xxx_rtc * rtc_dd,u32 offset)973ca04951SJohan Hovold static int pm8xxx_rtc_write_nvmem_offset(struct pm8xxx_rtc *rtc_dd, u32 offset)
983ca04951SJohan Hovold {
993ca04951SJohan Hovold 	u8 buf[sizeof(u32)];
1003ca04951SJohan Hovold 	int rc;
1013ca04951SJohan Hovold 
1023ca04951SJohan Hovold 	put_unaligned_le32(offset, buf);
1033ca04951SJohan Hovold 
1043ca04951SJohan Hovold 	rc = nvmem_cell_write(rtc_dd->nvmem_cell, buf, sizeof(buf));
1053ca04951SJohan Hovold 	if (rc < 0) {
1063ca04951SJohan Hovold 		dev_dbg(rtc_dd->dev, "failed to write nvmem offset: %d\n", rc);
1073ca04951SJohan Hovold 		return rc;
1083ca04951SJohan Hovold 	}
1093ca04951SJohan Hovold 
1103ca04951SJohan Hovold 	return 0;
1113ca04951SJohan Hovold }
1123ca04951SJohan Hovold 
pm8xxx_rtc_read_offset(struct pm8xxx_rtc * rtc_dd)1133ca04951SJohan Hovold static int pm8xxx_rtc_read_offset(struct pm8xxx_rtc *rtc_dd)
1143ca04951SJohan Hovold {
1153ca04951SJohan Hovold 	if (!rtc_dd->nvmem_cell)
1163ca04951SJohan Hovold 		return 0;
1173ca04951SJohan Hovold 
1183ca04951SJohan Hovold 	return pm8xxx_rtc_read_nvmem_offset(rtc_dd);
1193ca04951SJohan Hovold }
1203ca04951SJohan Hovold 
pm8xxx_rtc_read_raw(struct pm8xxx_rtc * rtc_dd,u32 * secs)121da862c3dSJohan Hovold static int pm8xxx_rtc_read_raw(struct pm8xxx_rtc *rtc_dd, u32 *secs)
122da862c3dSJohan Hovold {
123da862c3dSJohan Hovold 	const struct pm8xxx_rtc_regs *regs = rtc_dd->regs;
124da862c3dSJohan Hovold 	u8 value[NUM_8_BIT_RTC_REGS];
125da862c3dSJohan Hovold 	unsigned int reg;
126da862c3dSJohan Hovold 	int rc;
127da862c3dSJohan Hovold 
128da862c3dSJohan Hovold 	rc = regmap_bulk_read(rtc_dd->regmap, regs->read, value, sizeof(value));
129da862c3dSJohan Hovold 	if (rc)
130da862c3dSJohan Hovold 		return rc;
131da862c3dSJohan Hovold 
132da862c3dSJohan Hovold 	/*
133da862c3dSJohan Hovold 	 * Read the LSB again and check if there has been a carry over.
134da862c3dSJohan Hovold 	 * If there has, redo the read operation.
135da862c3dSJohan Hovold 	 */
136da862c3dSJohan Hovold 	rc = regmap_read(rtc_dd->regmap, regs->read, &reg);
137da862c3dSJohan Hovold 	if (rc < 0)
138da862c3dSJohan Hovold 		return rc;
139da862c3dSJohan Hovold 
140da862c3dSJohan Hovold 	if (reg < value[0]) {
141da862c3dSJohan Hovold 		rc = regmap_bulk_read(rtc_dd->regmap, regs->read, value,
142da862c3dSJohan Hovold 				      sizeof(value));
143da862c3dSJohan Hovold 		if (rc)
144da862c3dSJohan Hovold 			return rc;
145da862c3dSJohan Hovold 	}
146da862c3dSJohan Hovold 
147da862c3dSJohan Hovold 	*secs = get_unaligned_le32(value);
148da862c3dSJohan Hovold 
149da862c3dSJohan Hovold 	return 0;
150da862c3dSJohan Hovold }
151da862c3dSJohan Hovold 
pm8xxx_rtc_update_offset(struct pm8xxx_rtc * rtc_dd,u32 secs)1523ca04951SJohan Hovold static int pm8xxx_rtc_update_offset(struct pm8xxx_rtc *rtc_dd, u32 secs)
1533ca04951SJohan Hovold {
1543ca04951SJohan Hovold 	u32 raw_secs;
1553ca04951SJohan Hovold 	u32 offset;
1563ca04951SJohan Hovold 	int rc;
1573ca04951SJohan Hovold 
1583ca04951SJohan Hovold 	if (!rtc_dd->nvmem_cell)
1593ca04951SJohan Hovold 		return -ENODEV;
1603ca04951SJohan Hovold 
1613ca04951SJohan Hovold 	rc = pm8xxx_rtc_read_raw(rtc_dd, &raw_secs);
1623ca04951SJohan Hovold 	if (rc)
1633ca04951SJohan Hovold 		return rc;
1643ca04951SJohan Hovold 
1653ca04951SJohan Hovold 	offset = secs - raw_secs;
1663ca04951SJohan Hovold 
1673ca04951SJohan Hovold 	if (offset == rtc_dd->offset)
1683ca04951SJohan Hovold 		return 0;
1693ca04951SJohan Hovold 
1703ca04951SJohan Hovold 	rc = pm8xxx_rtc_write_nvmem_offset(rtc_dd, offset);
1713ca04951SJohan Hovold 	if (rc)
1723ca04951SJohan Hovold 		return rc;
1733ca04951SJohan Hovold 
1743ca04951SJohan Hovold 	rtc_dd->offset = offset;
1753ca04951SJohan Hovold 
1763ca04951SJohan Hovold 	return 0;
1773ca04951SJohan Hovold }
1783ca04951SJohan Hovold 
1799a9a54adSAnirudh Ghayal /*
1809a9a54adSAnirudh Ghayal  * Steps to write the RTC registers.
1819a9a54adSAnirudh Ghayal  * 1. Disable alarm if enabled.
18283220bf3SMohit Aggarwal  * 2. Disable rtc if enabled.
18383220bf3SMohit Aggarwal  * 3. Write 0x00 to LSB.
18483220bf3SMohit Aggarwal  * 4. Write Byte[1], Byte[2], Byte[3] then Byte[0].
18583220bf3SMohit Aggarwal  * 5. Enable rtc if disabled in step 2.
18683220bf3SMohit Aggarwal  * 6. Enable alarm if disabled in step 1.
1879a9a54adSAnirudh Ghayal  */
__pm8xxx_rtc_set_time(struct pm8xxx_rtc * rtc_dd,u32 secs)1883ca04951SJohan Hovold static int __pm8xxx_rtc_set_time(struct pm8xxx_rtc *rtc_dd, u32 secs)
1899a9a54adSAnirudh Ghayal {
190c8d523a4SStanimir Varbanov 	const struct pm8xxx_rtc_regs *regs = rtc_dd->regs;
191182c23bbSJohan Hovold 	u8 value[NUM_8_BIT_RTC_REGS];
192182c23bbSJohan Hovold 	bool alarm_enabled;
19379dd7566SJohan Hovold 	int rc;
1949a9a54adSAnirudh Ghayal 
19579dd7566SJohan Hovold 	put_unaligned_le32(secs, value);
1969a9a54adSAnirudh Ghayal 
197182c23bbSJohan Hovold 	rc = regmap_update_bits_check(rtc_dd->regmap, regs->alarm_ctrl,
198182c23bbSJohan Hovold 				      regs->alarm_en, 0, &alarm_enabled);
199c8d523a4SStanimir Varbanov 	if (rc)
2008d273f33SJohan Hovold 		return rc;
201c8d523a4SStanimir Varbanov 
2023c332639SJohan Hovold 	/* Disable RTC */
203182c23bbSJohan Hovold 	rc = regmap_update_bits(rtc_dd->regmap, regs->ctrl, PM8xxx_RTC_ENABLE, 0);
20483220bf3SMohit Aggarwal 	if (rc)
2058d273f33SJohan Hovold 		return rc;
20683220bf3SMohit Aggarwal 
2079a9a54adSAnirudh Ghayal 	/* Write 0 to Byte[0] */
208c8d523a4SStanimir Varbanov 	rc = regmap_write(rtc_dd->regmap, regs->write, 0);
209eb245631SJohan Hovold 	if (rc)
2108d273f33SJohan Hovold 		return rc;
2119a9a54adSAnirudh Ghayal 
2129a9a54adSAnirudh Ghayal 	/* Write Byte[1], Byte[2], Byte[3] */
213c8d523a4SStanimir Varbanov 	rc = regmap_bulk_write(rtc_dd->regmap, regs->write + 1,
2145d7dc4cfSJosh Cartwright 			       &value[1], sizeof(value) - 1);
215eb245631SJohan Hovold 	if (rc)
2168d273f33SJohan Hovold 		return rc;
2179a9a54adSAnirudh Ghayal 
2189a9a54adSAnirudh Ghayal 	/* Write Byte[0] */
219c8d523a4SStanimir Varbanov 	rc = regmap_write(rtc_dd->regmap, regs->write, value[0]);
220eb245631SJohan Hovold 	if (rc)
2218d273f33SJohan Hovold 		return rc;
2229a9a54adSAnirudh Ghayal 
2233c332639SJohan Hovold 	/* Enable RTC */
224182c23bbSJohan Hovold 	rc = regmap_update_bits(rtc_dd->regmap, regs->ctrl, PM8xxx_RTC_ENABLE,
225182c23bbSJohan Hovold 				PM8xxx_RTC_ENABLE);
226eb245631SJohan Hovold 	if (rc)
2278d273f33SJohan Hovold 		return rc;
2289a9a54adSAnirudh Ghayal 
22983220bf3SMohit Aggarwal 	if (alarm_enabled) {
230182c23bbSJohan Hovold 		rc = regmap_update_bits(rtc_dd->regmap, regs->alarm_ctrl,
231182c23bbSJohan Hovold 					regs->alarm_en, regs->alarm_en);
232eb245631SJohan Hovold 		if (rc)
2338d273f33SJohan Hovold 			return rc;
23483220bf3SMohit Aggarwal 	}
23583220bf3SMohit Aggarwal 
2368d273f33SJohan Hovold 	return 0;
2379a9a54adSAnirudh Ghayal }
2389a9a54adSAnirudh Ghayal 
pm8xxx_rtc_set_time(struct device * dev,struct rtc_time * tm)2393ca04951SJohan Hovold static int pm8xxx_rtc_set_time(struct device *dev, struct rtc_time *tm)
2403ca04951SJohan Hovold {
2413ca04951SJohan Hovold 	struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
2423ca04951SJohan Hovold 	u32 secs;
2433ca04951SJohan Hovold 	int rc;
2443ca04951SJohan Hovold 
2453ca04951SJohan Hovold 	secs = rtc_tm_to_time64(tm);
2463ca04951SJohan Hovold 
2473ca04951SJohan Hovold 	if (rtc_dd->allow_set_time)
2483ca04951SJohan Hovold 		rc = __pm8xxx_rtc_set_time(rtc_dd, secs);
2493ca04951SJohan Hovold 	else
2503ca04951SJohan Hovold 		rc = pm8xxx_rtc_update_offset(rtc_dd, secs);
2513ca04951SJohan Hovold 
2523ca04951SJohan Hovold 	if (rc)
2533ca04951SJohan Hovold 		return rc;
2543ca04951SJohan Hovold 
2553ca04951SJohan Hovold 	dev_dbg(dev, "set time: %ptRd %ptRt (%u + %u)\n", tm, tm,
2563ca04951SJohan Hovold 			secs - rtc_dd->offset, rtc_dd->offset);
2573ca04951SJohan Hovold 	return 0;
2583ca04951SJohan Hovold }
2593ca04951SJohan Hovold 
pm8xxx_rtc_read_time(struct device * dev,struct rtc_time * tm)2609a9a54adSAnirudh Ghayal static int pm8xxx_rtc_read_time(struct device *dev, struct rtc_time *tm)
2619a9a54adSAnirudh Ghayal {
2629a9a54adSAnirudh Ghayal 	struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
26335d9c472SJohan Hovold 	u32 secs;
264da862c3dSJohan Hovold 	int rc;
2659a9a54adSAnirudh Ghayal 
266da862c3dSJohan Hovold 	rc = pm8xxx_rtc_read_raw(rtc_dd, &secs);
267eb245631SJohan Hovold 	if (rc)
2689a9a54adSAnirudh Ghayal 		return rc;
2699a9a54adSAnirudh Ghayal 
2703ca04951SJohan Hovold 	secs += rtc_dd->offset;
2714c470b2fSAlexandre Belloni 	rtc_time64_to_tm(secs, tm);
2729a9a54adSAnirudh Ghayal 
2733ca04951SJohan Hovold 	dev_dbg(dev, "read time: %ptRd %ptRt (%u + %u)\n", tm, tm,
2743ca04951SJohan Hovold 			secs - rtc_dd->offset, rtc_dd->offset);
2759a9a54adSAnirudh Ghayal 	return 0;
2769a9a54adSAnirudh Ghayal }
2779a9a54adSAnirudh Ghayal 
pm8xxx_rtc_set_alarm(struct device * dev,struct rtc_wkalrm * alarm)2789a9a54adSAnirudh Ghayal static int pm8xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
2799a9a54adSAnirudh Ghayal {
2809a9a54adSAnirudh Ghayal 	struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
281c8d523a4SStanimir Varbanov 	const struct pm8xxx_rtc_regs *regs = rtc_dd->regs;
2829e5a7991SJohan Hovold 	u8 value[NUM_8_BIT_RTC_REGS];
28335d9c472SJohan Hovold 	u32 secs;
28479dd7566SJohan Hovold 	int rc;
2859a9a54adSAnirudh Ghayal 
2864c470b2fSAlexandre Belloni 	secs = rtc_tm_to_time64(&alarm->time);
2873ca04951SJohan Hovold 	secs -= rtc_dd->offset;
28879dd7566SJohan Hovold 	put_unaligned_le32(secs, value);
2899a9a54adSAnirudh Ghayal 
290c88db0efSJohan Hovold 	rc = regmap_update_bits(rtc_dd->regmap, regs->alarm_ctrl,
291c88db0efSJohan Hovold 				regs->alarm_en, 0);
292c88db0efSJohan Hovold 	if (rc)
293c88db0efSJohan Hovold 		return rc;
294c88db0efSJohan Hovold 
295c8d523a4SStanimir Varbanov 	rc = regmap_bulk_write(rtc_dd->regmap, regs->alarm_rw, value,
2965d7dc4cfSJosh Cartwright 			       sizeof(value));
297eb245631SJohan Hovold 	if (rc)
2988d273f33SJohan Hovold 		return rc;
2999a9a54adSAnirudh Ghayal 
300c88db0efSJohan Hovold 	if (alarm->enabled) {
301c88db0efSJohan Hovold 		rc = regmap_update_bits(rtc_dd->regmap, regs->alarm_ctrl,
302c88db0efSJohan Hovold 					regs->alarm_en, regs->alarm_en);
303c8d523a4SStanimir Varbanov 		if (rc)
3048d273f33SJohan Hovold 			return rc;
3059a9a54adSAnirudh Ghayal 	}
3069a9a54adSAnirudh Ghayal 
307c996956fSJohan Hovold 	dev_dbg(dev, "set alarm: %ptRd %ptRt\n", &alarm->time, &alarm->time);
3088d273f33SJohan Hovold 
3098d273f33SJohan Hovold 	return 0;
3109a9a54adSAnirudh Ghayal }
3119a9a54adSAnirudh Ghayal 
pm8xxx_rtc_read_alarm(struct device * dev,struct rtc_wkalrm * alarm)3129a9a54adSAnirudh Ghayal static int pm8xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
3139a9a54adSAnirudh Ghayal {
3149a9a54adSAnirudh Ghayal 	struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
315c8d523a4SStanimir Varbanov 	const struct pm8xxx_rtc_regs *regs = rtc_dd->regs;
3169e5a7991SJohan Hovold 	u8 value[NUM_8_BIT_RTC_REGS];
3179e5a7991SJohan Hovold 	unsigned int ctrl_reg;
31835d9c472SJohan Hovold 	u32 secs;
3199e5a7991SJohan Hovold 	int rc;
3209a9a54adSAnirudh Ghayal 
321c8d523a4SStanimir Varbanov 	rc = regmap_bulk_read(rtc_dd->regmap, regs->alarm_rw, value,
3225d7dc4cfSJosh Cartwright 			      sizeof(value));
323eb245631SJohan Hovold 	if (rc)
3249a9a54adSAnirudh Ghayal 		return rc;
3259a9a54adSAnirudh Ghayal 
32679dd7566SJohan Hovold 	secs = get_unaligned_le32(value);
3273ca04951SJohan Hovold 	secs += rtc_dd->offset;
3284c470b2fSAlexandre Belloni 	rtc_time64_to_tm(secs, &alarm->time);
3299a9a54adSAnirudh Ghayal 
330121f54efSGuixiong Wei 	rc = regmap_read(rtc_dd->regmap, regs->alarm_ctrl, &ctrl_reg);
331eb245631SJohan Hovold 	if (rc)
332121f54efSGuixiong Wei 		return rc;
333eb245631SJohan Hovold 
334121f54efSGuixiong Wei 	alarm->enabled = !!(ctrl_reg & PM8xxx_RTC_ALARM_ENABLE);
335121f54efSGuixiong Wei 
336c996956fSJohan Hovold 	dev_dbg(dev, "read alarm: %ptRd %ptRt\n", &alarm->time, &alarm->time);
3379a9a54adSAnirudh Ghayal 
3389a9a54adSAnirudh Ghayal 	return 0;
3399a9a54adSAnirudh Ghayal }
3409a9a54adSAnirudh Ghayal 
pm8xxx_rtc_alarm_irq_enable(struct device * dev,unsigned int enable)3419a9a54adSAnirudh Ghayal static int pm8xxx_rtc_alarm_irq_enable(struct device *dev, unsigned int enable)
3429a9a54adSAnirudh Ghayal {
3439a9a54adSAnirudh Ghayal 	struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
344c8d523a4SStanimir Varbanov 	const struct pm8xxx_rtc_regs *regs = rtc_dd->regs;
34534ce2977S韩科才 	u8 value[NUM_8_BIT_RTC_REGS] = {0};
346182c23bbSJohan Hovold 	unsigned int val;
3479e5a7991SJohan Hovold 	int rc;
3489a9a54adSAnirudh Ghayal 
3495bed811dSJosh Cartwright 	if (enable)
350182c23bbSJohan Hovold 		val = regs->alarm_en;
3515bed811dSJosh Cartwright 	else
352182c23bbSJohan Hovold 		val = 0;
3539a9a54adSAnirudh Ghayal 
354182c23bbSJohan Hovold 	rc = regmap_update_bits(rtc_dd->regmap, regs->alarm_ctrl,
355182c23bbSJohan Hovold 				regs->alarm_en, val);
356eb245631SJohan Hovold 	if (rc)
3578d273f33SJohan Hovold 		return rc;
3589a9a54adSAnirudh Ghayal 
3593c332639SJohan Hovold 	/* Clear alarm register */
36034ce2977S韩科才 	if (!enable) {
36134ce2977S韩科才 		rc = regmap_bulk_write(rtc_dd->regmap, regs->alarm_rw, value,
36234ce2977S韩科才 				       sizeof(value));
363eb245631SJohan Hovold 		if (rc)
3648d273f33SJohan Hovold 			return rc;
36534ce2977S韩科才 	}
36634ce2977S韩科才 
3678d273f33SJohan Hovold 	return 0;
3689a9a54adSAnirudh Ghayal }
3699a9a54adSAnirudh Ghayal 
3705a418558SJosh Cartwright static const struct rtc_class_ops pm8xxx_rtc_ops = {
3719a9a54adSAnirudh Ghayal 	.read_time	= pm8xxx_rtc_read_time,
3725a418558SJosh Cartwright 	.set_time	= pm8xxx_rtc_set_time,
3739a9a54adSAnirudh Ghayal 	.set_alarm	= pm8xxx_rtc_set_alarm,
3749a9a54adSAnirudh Ghayal 	.read_alarm	= pm8xxx_rtc_read_alarm,
3759a9a54adSAnirudh Ghayal 	.alarm_irq_enable = pm8xxx_rtc_alarm_irq_enable,
3769a9a54adSAnirudh Ghayal };
3779a9a54adSAnirudh Ghayal 
pm8xxx_alarm_trigger(int irq,void * dev_id)3789a9a54adSAnirudh Ghayal static irqreturn_t pm8xxx_alarm_trigger(int irq, void *dev_id)
3799a9a54adSAnirudh Ghayal {
3809a9a54adSAnirudh Ghayal 	struct pm8xxx_rtc *rtc_dd = dev_id;
381c8d523a4SStanimir Varbanov 	const struct pm8xxx_rtc_regs *regs = rtc_dd->regs;
3829a9a54adSAnirudh Ghayal 	int rc;
3839a9a54adSAnirudh Ghayal 
3849a9a54adSAnirudh Ghayal 	rtc_update_irq(rtc_dd->rtc, 1, RTC_IRQF | RTC_AF);
3859a9a54adSAnirudh Ghayal 
3863c332639SJohan Hovold 	/* Disable alarm */
387182c23bbSJohan Hovold 	rc = regmap_update_bits(rtc_dd->regmap, regs->alarm_ctrl,
388182c23bbSJohan Hovold 				regs->alarm_en, 0);
3898d273f33SJohan Hovold 	if (rc)
390cb9bb7b2SJohan Hovold 		return IRQ_NONE;
3919a9a54adSAnirudh Ghayal 
3923c332639SJohan Hovold 	/* Clear alarm status */
393182c23bbSJohan Hovold 	rc = regmap_update_bits(rtc_dd->regmap, regs->alarm_ctrl2,
394182c23bbSJohan Hovold 				PM8xxx_RTC_ALARM_CLEAR, 0);
395eb245631SJohan Hovold 	if (rc)
396cb9bb7b2SJohan Hovold 		return IRQ_NONE;
397cb9bb7b2SJohan Hovold 
3989a9a54adSAnirudh Ghayal 	return IRQ_HANDLED;
3999a9a54adSAnirudh Ghayal }
4009a9a54adSAnirudh Ghayal 
pm8xxx_rtc_enable(struct pm8xxx_rtc * rtc_dd)401c8d523a4SStanimir Varbanov static int pm8xxx_rtc_enable(struct pm8xxx_rtc *rtc_dd)
402c8d523a4SStanimir Varbanov {
403c8d523a4SStanimir Varbanov 	const struct pm8xxx_rtc_regs *regs = rtc_dd->regs;
404c8d523a4SStanimir Varbanov 
405182c23bbSJohan Hovold 	return regmap_update_bits(rtc_dd->regmap, regs->ctrl, PM8xxx_RTC_ENABLE,
406182c23bbSJohan Hovold 				  PM8xxx_RTC_ENABLE);
407c8d523a4SStanimir Varbanov }
408c8d523a4SStanimir Varbanov 
409c8d523a4SStanimir Varbanov static const struct pm8xxx_rtc_regs pm8921_regs = {
410c8d523a4SStanimir Varbanov 	.ctrl		= 0x11d,
411c8d523a4SStanimir Varbanov 	.write		= 0x11f,
412c8d523a4SStanimir Varbanov 	.read		= 0x123,
413c8d523a4SStanimir Varbanov 	.alarm_rw	= 0x127,
414c8d523a4SStanimir Varbanov 	.alarm_ctrl	= 0x11d,
415c8d523a4SStanimir Varbanov 	.alarm_ctrl2	= 0x11e,
416c8d523a4SStanimir Varbanov 	.alarm_en	= BIT(1),
417c8d523a4SStanimir Varbanov };
418c8d523a4SStanimir Varbanov 
419c8d523a4SStanimir Varbanov static const struct pm8xxx_rtc_regs pm8058_regs = {
420c8d523a4SStanimir Varbanov 	.ctrl		= 0x1e8,
421c8d523a4SStanimir Varbanov 	.write		= 0x1ea,
422c8d523a4SStanimir Varbanov 	.read		= 0x1ee,
423c8d523a4SStanimir Varbanov 	.alarm_rw	= 0x1f2,
424c8d523a4SStanimir Varbanov 	.alarm_ctrl	= 0x1e8,
425c8d523a4SStanimir Varbanov 	.alarm_ctrl2	= 0x1e9,
426c8d523a4SStanimir Varbanov 	.alarm_en	= BIT(1),
427c8d523a4SStanimir Varbanov };
428c8d523a4SStanimir Varbanov 
429c8d523a4SStanimir Varbanov static const struct pm8xxx_rtc_regs pm8941_regs = {
430c8d523a4SStanimir Varbanov 	.ctrl		= 0x6046,
431c8d523a4SStanimir Varbanov 	.write		= 0x6040,
432c8d523a4SStanimir Varbanov 	.read		= 0x6048,
433c8d523a4SStanimir Varbanov 	.alarm_rw	= 0x6140,
434c8d523a4SStanimir Varbanov 	.alarm_ctrl	= 0x6146,
435c8d523a4SStanimir Varbanov 	.alarm_ctrl2	= 0x6148,
436c8d523a4SStanimir Varbanov 	.alarm_en	= BIT(7),
437c8d523a4SStanimir Varbanov };
438c8d523a4SStanimir Varbanov 
439c8f0ca8bSsatya priya static const struct pm8xxx_rtc_regs pmk8350_regs = {
440c8f0ca8bSsatya priya 	.ctrl		= 0x6146,
441c8f0ca8bSsatya priya 	.write		= 0x6140,
442c8f0ca8bSsatya priya 	.read		= 0x6148,
443c8f0ca8bSsatya priya 	.alarm_rw	= 0x6240,
444c8f0ca8bSsatya priya 	.alarm_ctrl	= 0x6246,
445c8f0ca8bSsatya priya 	.alarm_ctrl2	= 0x6248,
446c8f0ca8bSsatya priya 	.alarm_en	= BIT(7),
447c8f0ca8bSsatya priya };
448c8f0ca8bSsatya priya 
4495a418558SJosh Cartwright static const struct of_device_id pm8xxx_id_table[] = {
450c8d523a4SStanimir Varbanov 	{ .compatible = "qcom,pm8921-rtc", .data = &pm8921_regs },
451c8d523a4SStanimir Varbanov 	{ .compatible = "qcom,pm8058-rtc", .data = &pm8058_regs },
452c8d523a4SStanimir Varbanov 	{ .compatible = "qcom,pm8941-rtc", .data = &pm8941_regs },
453c8f0ca8bSsatya priya 	{ .compatible = "qcom,pmk8350-rtc", .data = &pmk8350_regs },
4545a418558SJosh Cartwright 	{ },
4555a418558SJosh Cartwright };
4565a418558SJosh Cartwright MODULE_DEVICE_TABLE(of, pm8xxx_id_table);
4575a418558SJosh Cartwright 
pm8xxx_rtc_probe(struct platform_device * pdev)4585a167f45SGreg Kroah-Hartman static int pm8xxx_rtc_probe(struct platform_device *pdev)
4599a9a54adSAnirudh Ghayal {
4605a418558SJosh Cartwright 	const struct of_device_id *match;
4619e5a7991SJohan Hovold 	struct pm8xxx_rtc *rtc_dd;
4629e5a7991SJohan Hovold 	int rc;
4639a9a54adSAnirudh Ghayal 
4645a418558SJosh Cartwright 	match = of_match_node(pm8xxx_id_table, pdev->dev.of_node);
4655a418558SJosh Cartwright 	if (!match)
4665a418558SJosh Cartwright 		return -ENXIO;
4679a9a54adSAnirudh Ghayal 
468c417299cSJingoo Han 	rtc_dd = devm_kzalloc(&pdev->dev, sizeof(*rtc_dd), GFP_KERNEL);
46949ae425bSJingoo Han 	if (rtc_dd == NULL)
4709a9a54adSAnirudh Ghayal 		return -ENOMEM;
4719a9a54adSAnirudh Ghayal 
4725d7dc4cfSJosh Cartwright 	rtc_dd->regmap = dev_get_regmap(pdev->dev.parent, NULL);
473c94fb939SJohan Hovold 	if (!rtc_dd->regmap)
4745d7dc4cfSJosh Cartwright 		return -ENXIO;
4755d7dc4cfSJosh Cartwright 
4764727b58fSJohan Hovold 	rtc_dd->alarm_irq = platform_get_irq(pdev, 0);
4774727b58fSJohan Hovold 	if (rtc_dd->alarm_irq < 0)
478c417299cSJingoo Han 		return -ENXIO;
4799a9a54adSAnirudh Ghayal 
4805a418558SJosh Cartwright 	rtc_dd->allow_set_time = of_property_read_bool(pdev->dev.of_node,
4815a418558SJosh Cartwright 						      "allow-set-time");
4829a9a54adSAnirudh Ghayal 
4833ca04951SJohan Hovold 	rtc_dd->nvmem_cell = devm_nvmem_cell_get(&pdev->dev, "offset");
4843ca04951SJohan Hovold 	if (IS_ERR(rtc_dd->nvmem_cell)) {
4853ca04951SJohan Hovold 		rc = PTR_ERR(rtc_dd->nvmem_cell);
4863ca04951SJohan Hovold 		if (rc != -ENOENT)
4873ca04951SJohan Hovold 			return rc;
4883ca04951SJohan Hovold 		rtc_dd->nvmem_cell = NULL;
4893ca04951SJohan Hovold 	}
4903ca04951SJohan Hovold 
491c8d523a4SStanimir Varbanov 	rtc_dd->regs = match->data;
492a375510eSJohan Hovold 	rtc_dd->dev = &pdev->dev;
4939a9a54adSAnirudh Ghayal 
4943ca04951SJohan Hovold 	if (!rtc_dd->allow_set_time) {
4953ca04951SJohan Hovold 		rc = pm8xxx_rtc_read_offset(rtc_dd);
4963ca04951SJohan Hovold 		if (rc)
4973ca04951SJohan Hovold 			return rc;
4983ca04951SJohan Hovold 	}
4993ca04951SJohan Hovold 
500c8d523a4SStanimir Varbanov 	rc = pm8xxx_rtc_enable(rtc_dd);
501c8d523a4SStanimir Varbanov 	if (rc)
502c417299cSJingoo Han 		return rc;
5039a9a54adSAnirudh Ghayal 
5049a9a54adSAnirudh Ghayal 	platform_set_drvdata(pdev, rtc_dd);
5059a9a54adSAnirudh Ghayal 
506fda9909dSJosh Cartwright 	device_init_wakeup(&pdev->dev, 1);
507fda9909dSJosh Cartwright 
508d5d55b70SAlexandre Belloni 	rtc_dd->rtc = devm_rtc_allocate_device(&pdev->dev);
509d5d55b70SAlexandre Belloni 	if (IS_ERR(rtc_dd->rtc))
510c417299cSJingoo Han 		return PTR_ERR(rtc_dd->rtc);
511d5d55b70SAlexandre Belloni 
512d5d55b70SAlexandre Belloni 	rtc_dd->rtc->ops = &pm8xxx_rtc_ops;
5133cfe5260SAlexandre Belloni 	rtc_dd->rtc->range_max = U32_MAX;
5149a9a54adSAnirudh Ghayal 
5154727b58fSJohan Hovold 	rc = devm_request_any_context_irq(&pdev->dev, rtc_dd->alarm_irq,
516bffcbc08SJosh Cartwright 					  pm8xxx_alarm_trigger,
517bffcbc08SJosh Cartwright 					  IRQF_TRIGGER_RISING,
5189a9a54adSAnirudh Ghayal 					  "pm8xxx_rtc_alarm", rtc_dd);
519c94fb939SJohan Hovold 	if (rc < 0)
520c417299cSJingoo Han 		return rc;
5219a9a54adSAnirudh Ghayal 
522b5bf5b28SLoic Poulain 	rc = devm_rtc_register_device(rtc_dd->rtc);
523b5bf5b28SLoic Poulain 	if (rc)
524b5bf5b28SLoic Poulain 		return rc;
5259a9a54adSAnirudh Ghayal 
5264727b58fSJohan Hovold 	rc = dev_pm_set_wake_irq(&pdev->dev, rtc_dd->alarm_irq);
527b5bf5b28SLoic Poulain 	if (rc)
528b5bf5b28SLoic Poulain 		return rc;
5299a9a54adSAnirudh Ghayal 
5309a9a54adSAnirudh Ghayal 	return 0;
5319a9a54adSAnirudh Ghayal }
5329a9a54adSAnirudh Ghayal 
pm8xxx_remove(struct platform_device * pdev)533*3fc5029aSUwe Kleine-König static void pm8xxx_remove(struct platform_device *pdev)
5349a9a54adSAnirudh Ghayal {
535b5bf5b28SLoic Poulain 	dev_pm_clear_wake_irq(&pdev->dev);
5369a9a54adSAnirudh Ghayal }
5379a9a54adSAnirudh Ghayal 
5389a9a54adSAnirudh Ghayal static struct platform_driver pm8xxx_rtc_driver = {
5399a9a54adSAnirudh Ghayal 	.probe		= pm8xxx_rtc_probe,
540*3fc5029aSUwe Kleine-König 	.remove_new	= pm8xxx_remove,
5419a9a54adSAnirudh Ghayal 	.driver	= {
5425a418558SJosh Cartwright 		.name		= "rtc-pm8xxx",
5435a418558SJosh Cartwright 		.of_match_table	= pm8xxx_id_table,
5449a9a54adSAnirudh Ghayal 	},
5459a9a54adSAnirudh Ghayal };
5469a9a54adSAnirudh Ghayal 
5470c4eae66SAxel Lin module_platform_driver(pm8xxx_rtc_driver);
5489a9a54adSAnirudh Ghayal 
5499a9a54adSAnirudh Ghayal MODULE_ALIAS("platform:rtc-pm8xxx");
5509a9a54adSAnirudh Ghayal MODULE_DESCRIPTION("PMIC8xxx RTC driver");
5519a9a54adSAnirudh Ghayal MODULE_LICENSE("GPL v2");
5529a9a54adSAnirudh Ghayal MODULE_AUTHOR("Anirudh Ghayal <aghayal@codeaurora.org>");
5533ca04951SJohan Hovold MODULE_AUTHOR("Johan Hovold <johan@kernel.org>");
554