18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. 38c2ecf20Sopenharmony_ci */ 48c2ecf20Sopenharmony_ci#include <linux/of.h> 58c2ecf20Sopenharmony_ci#include <linux/module.h> 68c2ecf20Sopenharmony_ci#include <linux/init.h> 78c2ecf20Sopenharmony_ci#include <linux/rtc.h> 88c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 98c2ecf20Sopenharmony_ci#include <linux/pm.h> 108c2ecf20Sopenharmony_ci#include <linux/regmap.h> 118c2ecf20Sopenharmony_ci#include <linux/slab.h> 128c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci/* RTC Register offsets from RTC CTRL REG */ 158c2ecf20Sopenharmony_ci#define PM8XXX_ALARM_CTRL_OFFSET 0x01 168c2ecf20Sopenharmony_ci#define PM8XXX_RTC_WRITE_OFFSET 0x02 178c2ecf20Sopenharmony_ci#define PM8XXX_RTC_READ_OFFSET 0x06 188c2ecf20Sopenharmony_ci#define PM8XXX_ALARM_RW_OFFSET 0x0A 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci/* RTC_CTRL register bit fields */ 218c2ecf20Sopenharmony_ci#define PM8xxx_RTC_ENABLE BIT(7) 228c2ecf20Sopenharmony_ci#define PM8xxx_RTC_ALARM_CLEAR BIT(0) 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define NUM_8_BIT_RTC_REGS 0x4 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/** 278c2ecf20Sopenharmony_ci * struct pm8xxx_rtc_regs - describe RTC registers per PMIC versions 288c2ecf20Sopenharmony_ci * @ctrl: base address of control register 298c2ecf20Sopenharmony_ci * @write: base address of write register 308c2ecf20Sopenharmony_ci * @read: base address of read register 318c2ecf20Sopenharmony_ci * @alarm_ctrl: base address of alarm control register 328c2ecf20Sopenharmony_ci * @alarm_ctrl2: base address of alarm control2 register 338c2ecf20Sopenharmony_ci * @alarm_rw: base address of alarm read-write register 348c2ecf20Sopenharmony_ci * @alarm_en: alarm enable mask 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_cistruct pm8xxx_rtc_regs { 378c2ecf20Sopenharmony_ci unsigned int ctrl; 388c2ecf20Sopenharmony_ci unsigned int write; 398c2ecf20Sopenharmony_ci unsigned int read; 408c2ecf20Sopenharmony_ci unsigned int alarm_ctrl; 418c2ecf20Sopenharmony_ci unsigned int alarm_ctrl2; 428c2ecf20Sopenharmony_ci unsigned int alarm_rw; 438c2ecf20Sopenharmony_ci unsigned int alarm_en; 448c2ecf20Sopenharmony_ci}; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci/** 478c2ecf20Sopenharmony_ci * struct pm8xxx_rtc - rtc driver internal structure 488c2ecf20Sopenharmony_ci * @rtc: rtc device for this driver. 498c2ecf20Sopenharmony_ci * @regmap: regmap used to access RTC registers 508c2ecf20Sopenharmony_ci * @allow_set_time: indicates whether writing to the RTC is allowed 518c2ecf20Sopenharmony_ci * @rtc_alarm_irq: rtc alarm irq number. 528c2ecf20Sopenharmony_ci * @regs: rtc registers description. 538c2ecf20Sopenharmony_ci * @rtc_dev: device structure. 548c2ecf20Sopenharmony_ci * @ctrl_reg_lock: spinlock protecting access to ctrl_reg. 558c2ecf20Sopenharmony_ci */ 568c2ecf20Sopenharmony_cistruct pm8xxx_rtc { 578c2ecf20Sopenharmony_ci struct rtc_device *rtc; 588c2ecf20Sopenharmony_ci struct regmap *regmap; 598c2ecf20Sopenharmony_ci bool allow_set_time; 608c2ecf20Sopenharmony_ci int rtc_alarm_irq; 618c2ecf20Sopenharmony_ci const struct pm8xxx_rtc_regs *regs; 628c2ecf20Sopenharmony_ci struct device *rtc_dev; 638c2ecf20Sopenharmony_ci spinlock_t ctrl_reg_lock; 648c2ecf20Sopenharmony_ci}; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci/* 678c2ecf20Sopenharmony_ci * Steps to write the RTC registers. 688c2ecf20Sopenharmony_ci * 1. Disable alarm if enabled. 698c2ecf20Sopenharmony_ci * 2. Disable rtc if enabled. 708c2ecf20Sopenharmony_ci * 3. Write 0x00 to LSB. 718c2ecf20Sopenharmony_ci * 4. Write Byte[1], Byte[2], Byte[3] then Byte[0]. 728c2ecf20Sopenharmony_ci * 5. Enable rtc if disabled in step 2. 738c2ecf20Sopenharmony_ci * 6. Enable alarm if disabled in step 1. 748c2ecf20Sopenharmony_ci */ 758c2ecf20Sopenharmony_cistatic int pm8xxx_rtc_set_time(struct device *dev, struct rtc_time *tm) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci int rc, i; 788c2ecf20Sopenharmony_ci unsigned long secs, irq_flags; 798c2ecf20Sopenharmony_ci u8 value[NUM_8_BIT_RTC_REGS], alarm_enabled = 0, rtc_disabled = 0; 808c2ecf20Sopenharmony_ci unsigned int ctrl_reg, rtc_ctrl_reg; 818c2ecf20Sopenharmony_ci struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev); 828c2ecf20Sopenharmony_ci const struct pm8xxx_rtc_regs *regs = rtc_dd->regs; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci if (!rtc_dd->allow_set_time) 858c2ecf20Sopenharmony_ci return -EACCES; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci secs = rtc_tm_to_time64(tm); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci dev_dbg(dev, "Seconds value to be written to RTC = %lu\n", secs); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci for (i = 0; i < NUM_8_BIT_RTC_REGS; i++) { 928c2ecf20Sopenharmony_ci value[i] = secs & 0xFF; 938c2ecf20Sopenharmony_ci secs >>= 8; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci spin_lock_irqsave(&rtc_dd->ctrl_reg_lock, irq_flags); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci rc = regmap_read(rtc_dd->regmap, regs->alarm_ctrl, &ctrl_reg); 998c2ecf20Sopenharmony_ci if (rc) 1008c2ecf20Sopenharmony_ci goto rtc_rw_fail; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci if (ctrl_reg & regs->alarm_en) { 1038c2ecf20Sopenharmony_ci alarm_enabled = 1; 1048c2ecf20Sopenharmony_ci ctrl_reg &= ~regs->alarm_en; 1058c2ecf20Sopenharmony_ci rc = regmap_write(rtc_dd->regmap, regs->alarm_ctrl, ctrl_reg); 1068c2ecf20Sopenharmony_ci if (rc) { 1078c2ecf20Sopenharmony_ci dev_err(dev, "Write to RTC Alarm control register failed\n"); 1088c2ecf20Sopenharmony_ci goto rtc_rw_fail; 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci /* Disable RTC H/w before writing on RTC register */ 1138c2ecf20Sopenharmony_ci rc = regmap_read(rtc_dd->regmap, regs->ctrl, &rtc_ctrl_reg); 1148c2ecf20Sopenharmony_ci if (rc) 1158c2ecf20Sopenharmony_ci goto rtc_rw_fail; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci if (rtc_ctrl_reg & PM8xxx_RTC_ENABLE) { 1188c2ecf20Sopenharmony_ci rtc_disabled = 1; 1198c2ecf20Sopenharmony_ci rtc_ctrl_reg &= ~PM8xxx_RTC_ENABLE; 1208c2ecf20Sopenharmony_ci rc = regmap_write(rtc_dd->regmap, regs->ctrl, rtc_ctrl_reg); 1218c2ecf20Sopenharmony_ci if (rc) { 1228c2ecf20Sopenharmony_ci dev_err(dev, "Write to RTC control register failed\n"); 1238c2ecf20Sopenharmony_ci goto rtc_rw_fail; 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci /* Write 0 to Byte[0] */ 1288c2ecf20Sopenharmony_ci rc = regmap_write(rtc_dd->regmap, regs->write, 0); 1298c2ecf20Sopenharmony_ci if (rc) { 1308c2ecf20Sopenharmony_ci dev_err(dev, "Write to RTC write data register failed\n"); 1318c2ecf20Sopenharmony_ci goto rtc_rw_fail; 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci /* Write Byte[1], Byte[2], Byte[3] */ 1358c2ecf20Sopenharmony_ci rc = regmap_bulk_write(rtc_dd->regmap, regs->write + 1, 1368c2ecf20Sopenharmony_ci &value[1], sizeof(value) - 1); 1378c2ecf20Sopenharmony_ci if (rc) { 1388c2ecf20Sopenharmony_ci dev_err(dev, "Write to RTC write data register failed\n"); 1398c2ecf20Sopenharmony_ci goto rtc_rw_fail; 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci /* Write Byte[0] */ 1438c2ecf20Sopenharmony_ci rc = regmap_write(rtc_dd->regmap, regs->write, value[0]); 1448c2ecf20Sopenharmony_ci if (rc) { 1458c2ecf20Sopenharmony_ci dev_err(dev, "Write to RTC write data register failed\n"); 1468c2ecf20Sopenharmony_ci goto rtc_rw_fail; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci /* Enable RTC H/w after writing on RTC register */ 1508c2ecf20Sopenharmony_ci if (rtc_disabled) { 1518c2ecf20Sopenharmony_ci rtc_ctrl_reg |= PM8xxx_RTC_ENABLE; 1528c2ecf20Sopenharmony_ci rc = regmap_write(rtc_dd->regmap, regs->ctrl, rtc_ctrl_reg); 1538c2ecf20Sopenharmony_ci if (rc) { 1548c2ecf20Sopenharmony_ci dev_err(dev, "Write to RTC control register failed\n"); 1558c2ecf20Sopenharmony_ci goto rtc_rw_fail; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci if (alarm_enabled) { 1608c2ecf20Sopenharmony_ci ctrl_reg |= regs->alarm_en; 1618c2ecf20Sopenharmony_ci rc = regmap_write(rtc_dd->regmap, regs->alarm_ctrl, ctrl_reg); 1628c2ecf20Sopenharmony_ci if (rc) { 1638c2ecf20Sopenharmony_ci dev_err(dev, "Write to RTC Alarm control register failed\n"); 1648c2ecf20Sopenharmony_ci goto rtc_rw_fail; 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cirtc_rw_fail: 1698c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci return rc; 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic int pm8xxx_rtc_read_time(struct device *dev, struct rtc_time *tm) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci int rc; 1778c2ecf20Sopenharmony_ci u8 value[NUM_8_BIT_RTC_REGS]; 1788c2ecf20Sopenharmony_ci unsigned long secs; 1798c2ecf20Sopenharmony_ci unsigned int reg; 1808c2ecf20Sopenharmony_ci struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev); 1818c2ecf20Sopenharmony_ci const struct pm8xxx_rtc_regs *regs = rtc_dd->regs; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci rc = regmap_bulk_read(rtc_dd->regmap, regs->read, value, sizeof(value)); 1848c2ecf20Sopenharmony_ci if (rc) { 1858c2ecf20Sopenharmony_ci dev_err(dev, "RTC read data register failed\n"); 1868c2ecf20Sopenharmony_ci return rc; 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci /* 1908c2ecf20Sopenharmony_ci * Read the LSB again and check if there has been a carry over. 1918c2ecf20Sopenharmony_ci * If there is, redo the read operation. 1928c2ecf20Sopenharmony_ci */ 1938c2ecf20Sopenharmony_ci rc = regmap_read(rtc_dd->regmap, regs->read, ®); 1948c2ecf20Sopenharmony_ci if (rc < 0) { 1958c2ecf20Sopenharmony_ci dev_err(dev, "RTC read data register failed\n"); 1968c2ecf20Sopenharmony_ci return rc; 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci if (unlikely(reg < value[0])) { 2008c2ecf20Sopenharmony_ci rc = regmap_bulk_read(rtc_dd->regmap, regs->read, 2018c2ecf20Sopenharmony_ci value, sizeof(value)); 2028c2ecf20Sopenharmony_ci if (rc) { 2038c2ecf20Sopenharmony_ci dev_err(dev, "RTC read data register failed\n"); 2048c2ecf20Sopenharmony_ci return rc; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci secs = value[0] | (value[1] << 8) | (value[2] << 16) | 2098c2ecf20Sopenharmony_ci ((unsigned long)value[3] << 24); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci rtc_time64_to_tm(secs, tm); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci dev_dbg(dev, "secs = %lu, h:m:s == %ptRt, y-m-d = %ptRdr\n", secs, tm, tm); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci return 0; 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistatic int pm8xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci int rc, i; 2218c2ecf20Sopenharmony_ci u8 value[NUM_8_BIT_RTC_REGS]; 2228c2ecf20Sopenharmony_ci unsigned long secs, irq_flags; 2238c2ecf20Sopenharmony_ci struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev); 2248c2ecf20Sopenharmony_ci const struct pm8xxx_rtc_regs *regs = rtc_dd->regs; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci secs = rtc_tm_to_time64(&alarm->time); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci for (i = 0; i < NUM_8_BIT_RTC_REGS; i++) { 2298c2ecf20Sopenharmony_ci value[i] = secs & 0xFF; 2308c2ecf20Sopenharmony_ci secs >>= 8; 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci rc = regmap_update_bits(rtc_dd->regmap, regs->alarm_ctrl, 2348c2ecf20Sopenharmony_ci regs->alarm_en, 0); 2358c2ecf20Sopenharmony_ci if (rc) 2368c2ecf20Sopenharmony_ci return rc; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci spin_lock_irqsave(&rtc_dd->ctrl_reg_lock, irq_flags); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci rc = regmap_bulk_write(rtc_dd->regmap, regs->alarm_rw, value, 2418c2ecf20Sopenharmony_ci sizeof(value)); 2428c2ecf20Sopenharmony_ci if (rc) { 2438c2ecf20Sopenharmony_ci dev_err(dev, "Write to RTC ALARM register failed\n"); 2448c2ecf20Sopenharmony_ci goto rtc_rw_fail; 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci if (alarm->enabled) { 2488c2ecf20Sopenharmony_ci rc = regmap_update_bits(rtc_dd->regmap, regs->alarm_ctrl, 2498c2ecf20Sopenharmony_ci regs->alarm_en, regs->alarm_en); 2508c2ecf20Sopenharmony_ci if (rc) 2518c2ecf20Sopenharmony_ci goto rtc_rw_fail; 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci dev_dbg(dev, "Alarm Set for h:m:s=%ptRt, y-m-d=%ptRdr\n", 2558c2ecf20Sopenharmony_ci &alarm->time, &alarm->time); 2568c2ecf20Sopenharmony_cirtc_rw_fail: 2578c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags); 2588c2ecf20Sopenharmony_ci return rc; 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cistatic int pm8xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci int rc; 2648c2ecf20Sopenharmony_ci u8 value[NUM_8_BIT_RTC_REGS]; 2658c2ecf20Sopenharmony_ci unsigned long secs; 2668c2ecf20Sopenharmony_ci struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev); 2678c2ecf20Sopenharmony_ci const struct pm8xxx_rtc_regs *regs = rtc_dd->regs; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci rc = regmap_bulk_read(rtc_dd->regmap, regs->alarm_rw, value, 2708c2ecf20Sopenharmony_ci sizeof(value)); 2718c2ecf20Sopenharmony_ci if (rc) { 2728c2ecf20Sopenharmony_ci dev_err(dev, "RTC alarm time read failed\n"); 2738c2ecf20Sopenharmony_ci return rc; 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci secs = value[0] | (value[1] << 8) | (value[2] << 16) | 2778c2ecf20Sopenharmony_ci ((unsigned long)value[3] << 24); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci rtc_time64_to_tm(secs, &alarm->time); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci dev_dbg(dev, "Alarm set for - h:m:s=%ptRt, y-m-d=%ptRdr\n", 2828c2ecf20Sopenharmony_ci &alarm->time, &alarm->time); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci return 0; 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_cistatic int pm8xxx_rtc_alarm_irq_enable(struct device *dev, unsigned int enable) 2888c2ecf20Sopenharmony_ci{ 2898c2ecf20Sopenharmony_ci int rc; 2908c2ecf20Sopenharmony_ci unsigned long irq_flags; 2918c2ecf20Sopenharmony_ci struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev); 2928c2ecf20Sopenharmony_ci const struct pm8xxx_rtc_regs *regs = rtc_dd->regs; 2938c2ecf20Sopenharmony_ci unsigned int ctrl_reg; 2948c2ecf20Sopenharmony_ci u8 value[NUM_8_BIT_RTC_REGS] = {0}; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci spin_lock_irqsave(&rtc_dd->ctrl_reg_lock, irq_flags); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci rc = regmap_read(rtc_dd->regmap, regs->alarm_ctrl, &ctrl_reg); 2998c2ecf20Sopenharmony_ci if (rc) 3008c2ecf20Sopenharmony_ci goto rtc_rw_fail; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci if (enable) 3038c2ecf20Sopenharmony_ci ctrl_reg |= regs->alarm_en; 3048c2ecf20Sopenharmony_ci else 3058c2ecf20Sopenharmony_ci ctrl_reg &= ~regs->alarm_en; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci rc = regmap_write(rtc_dd->regmap, regs->alarm_ctrl, ctrl_reg); 3088c2ecf20Sopenharmony_ci if (rc) { 3098c2ecf20Sopenharmony_ci dev_err(dev, "Write to RTC control register failed\n"); 3108c2ecf20Sopenharmony_ci goto rtc_rw_fail; 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci /* Clear Alarm register */ 3148c2ecf20Sopenharmony_ci if (!enable) { 3158c2ecf20Sopenharmony_ci rc = regmap_bulk_write(rtc_dd->regmap, regs->alarm_rw, value, 3168c2ecf20Sopenharmony_ci sizeof(value)); 3178c2ecf20Sopenharmony_ci if (rc) { 3188c2ecf20Sopenharmony_ci dev_err(dev, "Clear RTC ALARM register failed\n"); 3198c2ecf20Sopenharmony_ci goto rtc_rw_fail; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_cirtc_rw_fail: 3248c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags); 3258c2ecf20Sopenharmony_ci return rc; 3268c2ecf20Sopenharmony_ci} 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_cistatic const struct rtc_class_ops pm8xxx_rtc_ops = { 3298c2ecf20Sopenharmony_ci .read_time = pm8xxx_rtc_read_time, 3308c2ecf20Sopenharmony_ci .set_time = pm8xxx_rtc_set_time, 3318c2ecf20Sopenharmony_ci .set_alarm = pm8xxx_rtc_set_alarm, 3328c2ecf20Sopenharmony_ci .read_alarm = pm8xxx_rtc_read_alarm, 3338c2ecf20Sopenharmony_ci .alarm_irq_enable = pm8xxx_rtc_alarm_irq_enable, 3348c2ecf20Sopenharmony_ci}; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_cistatic irqreturn_t pm8xxx_alarm_trigger(int irq, void *dev_id) 3378c2ecf20Sopenharmony_ci{ 3388c2ecf20Sopenharmony_ci struct pm8xxx_rtc *rtc_dd = dev_id; 3398c2ecf20Sopenharmony_ci const struct pm8xxx_rtc_regs *regs = rtc_dd->regs; 3408c2ecf20Sopenharmony_ci unsigned int ctrl_reg; 3418c2ecf20Sopenharmony_ci int rc; 3428c2ecf20Sopenharmony_ci unsigned long irq_flags; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci rtc_update_irq(rtc_dd->rtc, 1, RTC_IRQF | RTC_AF); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci spin_lock_irqsave(&rtc_dd->ctrl_reg_lock, irq_flags); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci /* Clear the alarm enable bit */ 3498c2ecf20Sopenharmony_ci rc = regmap_read(rtc_dd->regmap, regs->alarm_ctrl, &ctrl_reg); 3508c2ecf20Sopenharmony_ci if (rc) { 3518c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags); 3528c2ecf20Sopenharmony_ci goto rtc_alarm_handled; 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci ctrl_reg &= ~regs->alarm_en; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci rc = regmap_write(rtc_dd->regmap, regs->alarm_ctrl, ctrl_reg); 3588c2ecf20Sopenharmony_ci if (rc) { 3598c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags); 3608c2ecf20Sopenharmony_ci dev_err(rtc_dd->rtc_dev, 3618c2ecf20Sopenharmony_ci "Write to alarm control register failed\n"); 3628c2ecf20Sopenharmony_ci goto rtc_alarm_handled; 3638c2ecf20Sopenharmony_ci } 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci /* Clear RTC alarm register */ 3688c2ecf20Sopenharmony_ci rc = regmap_read(rtc_dd->regmap, regs->alarm_ctrl2, &ctrl_reg); 3698c2ecf20Sopenharmony_ci if (rc) { 3708c2ecf20Sopenharmony_ci dev_err(rtc_dd->rtc_dev, 3718c2ecf20Sopenharmony_ci "RTC Alarm control2 register read failed\n"); 3728c2ecf20Sopenharmony_ci goto rtc_alarm_handled; 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci ctrl_reg |= PM8xxx_RTC_ALARM_CLEAR; 3768c2ecf20Sopenharmony_ci rc = regmap_write(rtc_dd->regmap, regs->alarm_ctrl2, ctrl_reg); 3778c2ecf20Sopenharmony_ci if (rc) 3788c2ecf20Sopenharmony_ci dev_err(rtc_dd->rtc_dev, 3798c2ecf20Sopenharmony_ci "Write to RTC Alarm control2 register failed\n"); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_cirtc_alarm_handled: 3828c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3838c2ecf20Sopenharmony_ci} 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_cistatic int pm8xxx_rtc_enable(struct pm8xxx_rtc *rtc_dd) 3868c2ecf20Sopenharmony_ci{ 3878c2ecf20Sopenharmony_ci const struct pm8xxx_rtc_regs *regs = rtc_dd->regs; 3888c2ecf20Sopenharmony_ci unsigned int ctrl_reg; 3898c2ecf20Sopenharmony_ci int rc; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci /* Check if the RTC is on, else turn it on */ 3928c2ecf20Sopenharmony_ci rc = regmap_read(rtc_dd->regmap, regs->ctrl, &ctrl_reg); 3938c2ecf20Sopenharmony_ci if (rc) 3948c2ecf20Sopenharmony_ci return rc; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci if (!(ctrl_reg & PM8xxx_RTC_ENABLE)) { 3978c2ecf20Sopenharmony_ci ctrl_reg |= PM8xxx_RTC_ENABLE; 3988c2ecf20Sopenharmony_ci rc = regmap_write(rtc_dd->regmap, regs->ctrl, ctrl_reg); 3998c2ecf20Sopenharmony_ci if (rc) 4008c2ecf20Sopenharmony_ci return rc; 4018c2ecf20Sopenharmony_ci } 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci return 0; 4048c2ecf20Sopenharmony_ci} 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_cistatic const struct pm8xxx_rtc_regs pm8921_regs = { 4078c2ecf20Sopenharmony_ci .ctrl = 0x11d, 4088c2ecf20Sopenharmony_ci .write = 0x11f, 4098c2ecf20Sopenharmony_ci .read = 0x123, 4108c2ecf20Sopenharmony_ci .alarm_rw = 0x127, 4118c2ecf20Sopenharmony_ci .alarm_ctrl = 0x11d, 4128c2ecf20Sopenharmony_ci .alarm_ctrl2 = 0x11e, 4138c2ecf20Sopenharmony_ci .alarm_en = BIT(1), 4148c2ecf20Sopenharmony_ci}; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_cistatic const struct pm8xxx_rtc_regs pm8058_regs = { 4178c2ecf20Sopenharmony_ci .ctrl = 0x1e8, 4188c2ecf20Sopenharmony_ci .write = 0x1ea, 4198c2ecf20Sopenharmony_ci .read = 0x1ee, 4208c2ecf20Sopenharmony_ci .alarm_rw = 0x1f2, 4218c2ecf20Sopenharmony_ci .alarm_ctrl = 0x1e8, 4228c2ecf20Sopenharmony_ci .alarm_ctrl2 = 0x1e9, 4238c2ecf20Sopenharmony_ci .alarm_en = BIT(1), 4248c2ecf20Sopenharmony_ci}; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_cistatic const struct pm8xxx_rtc_regs pm8941_regs = { 4278c2ecf20Sopenharmony_ci .ctrl = 0x6046, 4288c2ecf20Sopenharmony_ci .write = 0x6040, 4298c2ecf20Sopenharmony_ci .read = 0x6048, 4308c2ecf20Sopenharmony_ci .alarm_rw = 0x6140, 4318c2ecf20Sopenharmony_ci .alarm_ctrl = 0x6146, 4328c2ecf20Sopenharmony_ci .alarm_ctrl2 = 0x6148, 4338c2ecf20Sopenharmony_ci .alarm_en = BIT(7), 4348c2ecf20Sopenharmony_ci}; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci/* 4378c2ecf20Sopenharmony_ci * Hardcoded RTC bases until IORESOURCE_REG mapping is figured out 4388c2ecf20Sopenharmony_ci */ 4398c2ecf20Sopenharmony_cistatic const struct of_device_id pm8xxx_id_table[] = { 4408c2ecf20Sopenharmony_ci { .compatible = "qcom,pm8921-rtc", .data = &pm8921_regs }, 4418c2ecf20Sopenharmony_ci { .compatible = "qcom,pm8018-rtc", .data = &pm8921_regs }, 4428c2ecf20Sopenharmony_ci { .compatible = "qcom,pm8058-rtc", .data = &pm8058_regs }, 4438c2ecf20Sopenharmony_ci { .compatible = "qcom,pm8941-rtc", .data = &pm8941_regs }, 4448c2ecf20Sopenharmony_ci { }, 4458c2ecf20Sopenharmony_ci}; 4468c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, pm8xxx_id_table); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_cistatic int pm8xxx_rtc_probe(struct platform_device *pdev) 4498c2ecf20Sopenharmony_ci{ 4508c2ecf20Sopenharmony_ci int rc; 4518c2ecf20Sopenharmony_ci struct pm8xxx_rtc *rtc_dd; 4528c2ecf20Sopenharmony_ci const struct of_device_id *match; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci match = of_match_node(pm8xxx_id_table, pdev->dev.of_node); 4558c2ecf20Sopenharmony_ci if (!match) 4568c2ecf20Sopenharmony_ci return -ENXIO; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci rtc_dd = devm_kzalloc(&pdev->dev, sizeof(*rtc_dd), GFP_KERNEL); 4598c2ecf20Sopenharmony_ci if (rtc_dd == NULL) 4608c2ecf20Sopenharmony_ci return -ENOMEM; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci /* Initialise spinlock to protect RTC control register */ 4638c2ecf20Sopenharmony_ci spin_lock_init(&rtc_dd->ctrl_reg_lock); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci rtc_dd->regmap = dev_get_regmap(pdev->dev.parent, NULL); 4668c2ecf20Sopenharmony_ci if (!rtc_dd->regmap) { 4678c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Parent regmap unavailable.\n"); 4688c2ecf20Sopenharmony_ci return -ENXIO; 4698c2ecf20Sopenharmony_ci } 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci rtc_dd->rtc_alarm_irq = platform_get_irq(pdev, 0); 4728c2ecf20Sopenharmony_ci if (rtc_dd->rtc_alarm_irq < 0) 4738c2ecf20Sopenharmony_ci return -ENXIO; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci rtc_dd->allow_set_time = of_property_read_bool(pdev->dev.of_node, 4768c2ecf20Sopenharmony_ci "allow-set-time"); 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci rtc_dd->regs = match->data; 4798c2ecf20Sopenharmony_ci rtc_dd->rtc_dev = &pdev->dev; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci rc = pm8xxx_rtc_enable(rtc_dd); 4828c2ecf20Sopenharmony_ci if (rc) 4838c2ecf20Sopenharmony_ci return rc; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, rtc_dd); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci device_init_wakeup(&pdev->dev, 1); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci /* Register the RTC device */ 4908c2ecf20Sopenharmony_ci rtc_dd->rtc = devm_rtc_allocate_device(&pdev->dev); 4918c2ecf20Sopenharmony_ci if (IS_ERR(rtc_dd->rtc)) 4928c2ecf20Sopenharmony_ci return PTR_ERR(rtc_dd->rtc); 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci rtc_dd->rtc->ops = &pm8xxx_rtc_ops; 4958c2ecf20Sopenharmony_ci rtc_dd->rtc->range_max = U32_MAX; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci /* Request the alarm IRQ */ 4988c2ecf20Sopenharmony_ci rc = devm_request_any_context_irq(&pdev->dev, rtc_dd->rtc_alarm_irq, 4998c2ecf20Sopenharmony_ci pm8xxx_alarm_trigger, 5008c2ecf20Sopenharmony_ci IRQF_TRIGGER_RISING, 5018c2ecf20Sopenharmony_ci "pm8xxx_rtc_alarm", rtc_dd); 5028c2ecf20Sopenharmony_ci if (rc < 0) { 5038c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Request IRQ failed (%d)\n", rc); 5048c2ecf20Sopenharmony_ci return rc; 5058c2ecf20Sopenharmony_ci } 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci return rtc_register_device(rtc_dd->rtc); 5088c2ecf20Sopenharmony_ci} 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 5118c2ecf20Sopenharmony_cistatic int pm8xxx_rtc_resume(struct device *dev) 5128c2ecf20Sopenharmony_ci{ 5138c2ecf20Sopenharmony_ci struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci if (device_may_wakeup(dev)) 5168c2ecf20Sopenharmony_ci disable_irq_wake(rtc_dd->rtc_alarm_irq); 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci return 0; 5198c2ecf20Sopenharmony_ci} 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_cistatic int pm8xxx_rtc_suspend(struct device *dev) 5228c2ecf20Sopenharmony_ci{ 5238c2ecf20Sopenharmony_ci struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci if (device_may_wakeup(dev)) 5268c2ecf20Sopenharmony_ci enable_irq_wake(rtc_dd->rtc_alarm_irq); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci return 0; 5298c2ecf20Sopenharmony_ci} 5308c2ecf20Sopenharmony_ci#endif 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(pm8xxx_rtc_pm_ops, 5338c2ecf20Sopenharmony_ci pm8xxx_rtc_suspend, 5348c2ecf20Sopenharmony_ci pm8xxx_rtc_resume); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_cistatic struct platform_driver pm8xxx_rtc_driver = { 5378c2ecf20Sopenharmony_ci .probe = pm8xxx_rtc_probe, 5388c2ecf20Sopenharmony_ci .driver = { 5398c2ecf20Sopenharmony_ci .name = "rtc-pm8xxx", 5408c2ecf20Sopenharmony_ci .pm = &pm8xxx_rtc_pm_ops, 5418c2ecf20Sopenharmony_ci .of_match_table = pm8xxx_id_table, 5428c2ecf20Sopenharmony_ci }, 5438c2ecf20Sopenharmony_ci}; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_cimodule_platform_driver(pm8xxx_rtc_driver); 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:rtc-pm8xxx"); 5488c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("PMIC8xxx RTC driver"); 5498c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 5508c2ecf20Sopenharmony_ciMODULE_AUTHOR("Anirudh Ghayal <aghayal@codeaurora.org>"); 551