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, ®);
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