18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// RTC driver for Maxim MAX8997 48c2ecf20Sopenharmony_ci// 58c2ecf20Sopenharmony_ci// Copyright (C) 2013 Samsung Electronics Co.Ltd 68c2ecf20Sopenharmony_ci// 78c2ecf20Sopenharmony_ci// based on rtc-max8998.c 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/slab.h> 128c2ecf20Sopenharmony_ci#include <linux/rtc.h> 138c2ecf20Sopenharmony_ci#include <linux/delay.h> 148c2ecf20Sopenharmony_ci#include <linux/mutex.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 178c2ecf20Sopenharmony_ci#include <linux/mfd/max8997-private.h> 188c2ecf20Sopenharmony_ci#include <linux/irqdomain.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci/* Module parameter for WTSR function control */ 218c2ecf20Sopenharmony_cistatic int wtsr_en = 1; 228c2ecf20Sopenharmony_cimodule_param(wtsr_en, int, 0444); 238c2ecf20Sopenharmony_ciMODULE_PARM_DESC(wtsr_en, "Watchdog Timeout & Software Reset (default=on)"); 248c2ecf20Sopenharmony_ci/* Module parameter for SMPL function control */ 258c2ecf20Sopenharmony_cistatic int smpl_en = 1; 268c2ecf20Sopenharmony_cimodule_param(smpl_en, int, 0444); 278c2ecf20Sopenharmony_ciMODULE_PARM_DESC(smpl_en, "Sudden Momentary Power Loss (default=on)"); 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci/* RTC Control Register */ 308c2ecf20Sopenharmony_ci#define BCD_EN_SHIFT 0 318c2ecf20Sopenharmony_ci#define BCD_EN_MASK (1 << BCD_EN_SHIFT) 328c2ecf20Sopenharmony_ci#define MODEL24_SHIFT 1 338c2ecf20Sopenharmony_ci#define MODEL24_MASK (1 << MODEL24_SHIFT) 348c2ecf20Sopenharmony_ci/* RTC Update Register1 */ 358c2ecf20Sopenharmony_ci#define RTC_UDR_SHIFT 0 368c2ecf20Sopenharmony_ci#define RTC_UDR_MASK (1 << RTC_UDR_SHIFT) 378c2ecf20Sopenharmony_ci/* WTSR and SMPL Register */ 388c2ecf20Sopenharmony_ci#define WTSRT_SHIFT 0 398c2ecf20Sopenharmony_ci#define SMPLT_SHIFT 2 408c2ecf20Sopenharmony_ci#define WTSR_EN_SHIFT 6 418c2ecf20Sopenharmony_ci#define SMPL_EN_SHIFT 7 428c2ecf20Sopenharmony_ci#define WTSRT_MASK (3 << WTSRT_SHIFT) 438c2ecf20Sopenharmony_ci#define SMPLT_MASK (3 << SMPLT_SHIFT) 448c2ecf20Sopenharmony_ci#define WTSR_EN_MASK (1 << WTSR_EN_SHIFT) 458c2ecf20Sopenharmony_ci#define SMPL_EN_MASK (1 << SMPL_EN_SHIFT) 468c2ecf20Sopenharmony_ci/* RTC Hour register */ 478c2ecf20Sopenharmony_ci#define HOUR_PM_SHIFT 6 488c2ecf20Sopenharmony_ci#define HOUR_PM_MASK (1 << HOUR_PM_SHIFT) 498c2ecf20Sopenharmony_ci/* RTC Alarm Enable */ 508c2ecf20Sopenharmony_ci#define ALARM_ENABLE_SHIFT 7 518c2ecf20Sopenharmony_ci#define ALARM_ENABLE_MASK (1 << ALARM_ENABLE_SHIFT) 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cienum { 548c2ecf20Sopenharmony_ci RTC_SEC = 0, 558c2ecf20Sopenharmony_ci RTC_MIN, 568c2ecf20Sopenharmony_ci RTC_HOUR, 578c2ecf20Sopenharmony_ci RTC_WEEKDAY, 588c2ecf20Sopenharmony_ci RTC_MONTH, 598c2ecf20Sopenharmony_ci RTC_YEAR, 608c2ecf20Sopenharmony_ci RTC_DATE, 618c2ecf20Sopenharmony_ci RTC_NR_TIME 628c2ecf20Sopenharmony_ci}; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistruct max8997_rtc_info { 658c2ecf20Sopenharmony_ci struct device *dev; 668c2ecf20Sopenharmony_ci struct max8997_dev *max8997; 678c2ecf20Sopenharmony_ci struct i2c_client *rtc; 688c2ecf20Sopenharmony_ci struct rtc_device *rtc_dev; 698c2ecf20Sopenharmony_ci struct mutex lock; 708c2ecf20Sopenharmony_ci int virq; 718c2ecf20Sopenharmony_ci int rtc_24hr_mode; 728c2ecf20Sopenharmony_ci}; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic void max8997_rtc_data_to_tm(u8 *data, struct rtc_time *tm, 758c2ecf20Sopenharmony_ci int rtc_24hr_mode) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci tm->tm_sec = data[RTC_SEC] & 0x7f; 788c2ecf20Sopenharmony_ci tm->tm_min = data[RTC_MIN] & 0x7f; 798c2ecf20Sopenharmony_ci if (rtc_24hr_mode) 808c2ecf20Sopenharmony_ci tm->tm_hour = data[RTC_HOUR] & 0x1f; 818c2ecf20Sopenharmony_ci else { 828c2ecf20Sopenharmony_ci tm->tm_hour = data[RTC_HOUR] & 0x0f; 838c2ecf20Sopenharmony_ci if (data[RTC_HOUR] & HOUR_PM_MASK) 848c2ecf20Sopenharmony_ci tm->tm_hour += 12; 858c2ecf20Sopenharmony_ci } 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci tm->tm_wday = fls(data[RTC_WEEKDAY] & 0x7f) - 1; 888c2ecf20Sopenharmony_ci tm->tm_mday = data[RTC_DATE] & 0x1f; 898c2ecf20Sopenharmony_ci tm->tm_mon = (data[RTC_MONTH] & 0x0f) - 1; 908c2ecf20Sopenharmony_ci tm->tm_year = (data[RTC_YEAR] & 0x7f) + 100; 918c2ecf20Sopenharmony_ci tm->tm_yday = 0; 928c2ecf20Sopenharmony_ci tm->tm_isdst = 0; 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic int max8997_rtc_tm_to_data(struct rtc_time *tm, u8 *data) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci data[RTC_SEC] = tm->tm_sec; 988c2ecf20Sopenharmony_ci data[RTC_MIN] = tm->tm_min; 998c2ecf20Sopenharmony_ci data[RTC_HOUR] = tm->tm_hour; 1008c2ecf20Sopenharmony_ci data[RTC_WEEKDAY] = 1 << tm->tm_wday; 1018c2ecf20Sopenharmony_ci data[RTC_DATE] = tm->tm_mday; 1028c2ecf20Sopenharmony_ci data[RTC_MONTH] = tm->tm_mon + 1; 1038c2ecf20Sopenharmony_ci data[RTC_YEAR] = tm->tm_year > 100 ? (tm->tm_year - 100) : 0; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if (tm->tm_year < 100) { 1068c2ecf20Sopenharmony_ci pr_warn("RTC cannot handle the year %d. Assume it's 2000.\n", 1078c2ecf20Sopenharmony_ci 1900 + tm->tm_year); 1088c2ecf20Sopenharmony_ci return -EINVAL; 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci return 0; 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic inline int max8997_rtc_set_update_reg(struct max8997_rtc_info *info) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci int ret; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci ret = max8997_write_reg(info->rtc, MAX8997_RTC_UPDATE1, 1188c2ecf20Sopenharmony_ci RTC_UDR_MASK); 1198c2ecf20Sopenharmony_ci if (ret < 0) 1208c2ecf20Sopenharmony_ci dev_err(info->dev, "%s: fail to write update reg(%d)\n", 1218c2ecf20Sopenharmony_ci __func__, ret); 1228c2ecf20Sopenharmony_ci else { 1238c2ecf20Sopenharmony_ci /* Minimum 16ms delay required before RTC update. 1248c2ecf20Sopenharmony_ci * Otherwise, we may read and update based on out-of-date 1258c2ecf20Sopenharmony_ci * value */ 1268c2ecf20Sopenharmony_ci msleep(20); 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci return ret; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic int max8997_rtc_read_time(struct device *dev, struct rtc_time *tm) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci struct max8997_rtc_info *info = dev_get_drvdata(dev); 1358c2ecf20Sopenharmony_ci u8 data[RTC_NR_TIME]; 1368c2ecf20Sopenharmony_ci int ret; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci mutex_lock(&info->lock); 1398c2ecf20Sopenharmony_ci ret = max8997_bulk_read(info->rtc, MAX8997_RTC_SEC, RTC_NR_TIME, data); 1408c2ecf20Sopenharmony_ci mutex_unlock(&info->lock); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci if (ret < 0) { 1438c2ecf20Sopenharmony_ci dev_err(info->dev, "%s: fail to read time reg(%d)\n", __func__, 1448c2ecf20Sopenharmony_ci ret); 1458c2ecf20Sopenharmony_ci return ret; 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci max8997_rtc_data_to_tm(data, tm, info->rtc_24hr_mode); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci return 0; 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic int max8997_rtc_set_time(struct device *dev, struct rtc_time *tm) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci struct max8997_rtc_info *info = dev_get_drvdata(dev); 1568c2ecf20Sopenharmony_ci u8 data[RTC_NR_TIME]; 1578c2ecf20Sopenharmony_ci int ret; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci ret = max8997_rtc_tm_to_data(tm, data); 1608c2ecf20Sopenharmony_ci if (ret < 0) 1618c2ecf20Sopenharmony_ci return ret; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci mutex_lock(&info->lock); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci ret = max8997_bulk_write(info->rtc, MAX8997_RTC_SEC, RTC_NR_TIME, data); 1668c2ecf20Sopenharmony_ci if (ret < 0) { 1678c2ecf20Sopenharmony_ci dev_err(info->dev, "%s: fail to write time reg(%d)\n", __func__, 1688c2ecf20Sopenharmony_ci ret); 1698c2ecf20Sopenharmony_ci goto out; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci ret = max8997_rtc_set_update_reg(info); 1738c2ecf20Sopenharmony_ciout: 1748c2ecf20Sopenharmony_ci mutex_unlock(&info->lock); 1758c2ecf20Sopenharmony_ci return ret; 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic int max8997_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci struct max8997_rtc_info *info = dev_get_drvdata(dev); 1818c2ecf20Sopenharmony_ci u8 data[RTC_NR_TIME]; 1828c2ecf20Sopenharmony_ci u8 val; 1838c2ecf20Sopenharmony_ci int i, ret; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci mutex_lock(&info->lock); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci ret = max8997_bulk_read(info->rtc, MAX8997_RTC_ALARM1_SEC, RTC_NR_TIME, 1888c2ecf20Sopenharmony_ci data); 1898c2ecf20Sopenharmony_ci if (ret < 0) { 1908c2ecf20Sopenharmony_ci dev_err(info->dev, "%s:%d fail to read alarm reg(%d)\n", 1918c2ecf20Sopenharmony_ci __func__, __LINE__, ret); 1928c2ecf20Sopenharmony_ci goto out; 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci max8997_rtc_data_to_tm(data, &alrm->time, info->rtc_24hr_mode); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci alrm->enabled = 0; 1988c2ecf20Sopenharmony_ci for (i = 0; i < RTC_NR_TIME; i++) { 1998c2ecf20Sopenharmony_ci if (data[i] & ALARM_ENABLE_MASK) { 2008c2ecf20Sopenharmony_ci alrm->enabled = 1; 2018c2ecf20Sopenharmony_ci break; 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci alrm->pending = 0; 2068c2ecf20Sopenharmony_ci ret = max8997_read_reg(info->max8997->i2c, MAX8997_REG_STATUS1, &val); 2078c2ecf20Sopenharmony_ci if (ret < 0) { 2088c2ecf20Sopenharmony_ci dev_err(info->dev, "%s:%d fail to read status1 reg(%d)\n", 2098c2ecf20Sopenharmony_ci __func__, __LINE__, ret); 2108c2ecf20Sopenharmony_ci goto out; 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci if (val & (1 << 4)) /* RTCA1 */ 2148c2ecf20Sopenharmony_ci alrm->pending = 1; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ciout: 2178c2ecf20Sopenharmony_ci mutex_unlock(&info->lock); 2188c2ecf20Sopenharmony_ci return ret; 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_cistatic int max8997_rtc_stop_alarm(struct max8997_rtc_info *info) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci u8 data[RTC_NR_TIME]; 2248c2ecf20Sopenharmony_ci int ret, i; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci if (!mutex_is_locked(&info->lock)) 2278c2ecf20Sopenharmony_ci dev_warn(info->dev, "%s: should have mutex locked\n", __func__); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci ret = max8997_bulk_read(info->rtc, MAX8997_RTC_ALARM1_SEC, RTC_NR_TIME, 2308c2ecf20Sopenharmony_ci data); 2318c2ecf20Sopenharmony_ci if (ret < 0) { 2328c2ecf20Sopenharmony_ci dev_err(info->dev, "%s: fail to read alarm reg(%d)\n", 2338c2ecf20Sopenharmony_ci __func__, ret); 2348c2ecf20Sopenharmony_ci goto out; 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci for (i = 0; i < RTC_NR_TIME; i++) 2388c2ecf20Sopenharmony_ci data[i] &= ~ALARM_ENABLE_MASK; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci ret = max8997_bulk_write(info->rtc, MAX8997_RTC_ALARM1_SEC, RTC_NR_TIME, 2418c2ecf20Sopenharmony_ci data); 2428c2ecf20Sopenharmony_ci if (ret < 0) { 2438c2ecf20Sopenharmony_ci dev_err(info->dev, "%s: fail to write alarm reg(%d)\n", 2448c2ecf20Sopenharmony_ci __func__, ret); 2458c2ecf20Sopenharmony_ci goto out; 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci ret = max8997_rtc_set_update_reg(info); 2498c2ecf20Sopenharmony_ciout: 2508c2ecf20Sopenharmony_ci return ret; 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cistatic int max8997_rtc_start_alarm(struct max8997_rtc_info *info) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci u8 data[RTC_NR_TIME]; 2568c2ecf20Sopenharmony_ci int ret; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci if (!mutex_is_locked(&info->lock)) 2598c2ecf20Sopenharmony_ci dev_warn(info->dev, "%s: should have mutex locked\n", __func__); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci ret = max8997_bulk_read(info->rtc, MAX8997_RTC_ALARM1_SEC, RTC_NR_TIME, 2628c2ecf20Sopenharmony_ci data); 2638c2ecf20Sopenharmony_ci if (ret < 0) { 2648c2ecf20Sopenharmony_ci dev_err(info->dev, "%s: fail to read alarm reg(%d)\n", 2658c2ecf20Sopenharmony_ci __func__, ret); 2668c2ecf20Sopenharmony_ci goto out; 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci data[RTC_SEC] |= (1 << ALARM_ENABLE_SHIFT); 2708c2ecf20Sopenharmony_ci data[RTC_MIN] |= (1 << ALARM_ENABLE_SHIFT); 2718c2ecf20Sopenharmony_ci data[RTC_HOUR] |= (1 << ALARM_ENABLE_SHIFT); 2728c2ecf20Sopenharmony_ci data[RTC_WEEKDAY] &= ~ALARM_ENABLE_MASK; 2738c2ecf20Sopenharmony_ci if (data[RTC_MONTH] & 0xf) 2748c2ecf20Sopenharmony_ci data[RTC_MONTH] |= (1 << ALARM_ENABLE_SHIFT); 2758c2ecf20Sopenharmony_ci if (data[RTC_YEAR] & 0x7f) 2768c2ecf20Sopenharmony_ci data[RTC_YEAR] |= (1 << ALARM_ENABLE_SHIFT); 2778c2ecf20Sopenharmony_ci if (data[RTC_DATE] & 0x1f) 2788c2ecf20Sopenharmony_ci data[RTC_DATE] |= (1 << ALARM_ENABLE_SHIFT); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci ret = max8997_bulk_write(info->rtc, MAX8997_RTC_ALARM1_SEC, RTC_NR_TIME, 2818c2ecf20Sopenharmony_ci data); 2828c2ecf20Sopenharmony_ci if (ret < 0) { 2838c2ecf20Sopenharmony_ci dev_err(info->dev, "%s: fail to write alarm reg(%d)\n", 2848c2ecf20Sopenharmony_ci __func__, ret); 2858c2ecf20Sopenharmony_ci goto out; 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci ret = max8997_rtc_set_update_reg(info); 2898c2ecf20Sopenharmony_ciout: 2908c2ecf20Sopenharmony_ci return ret; 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_cistatic int max8997_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci struct max8997_rtc_info *info = dev_get_drvdata(dev); 2958c2ecf20Sopenharmony_ci u8 data[RTC_NR_TIME]; 2968c2ecf20Sopenharmony_ci int ret; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci ret = max8997_rtc_tm_to_data(&alrm->time, data); 2998c2ecf20Sopenharmony_ci if (ret < 0) 3008c2ecf20Sopenharmony_ci return ret; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci dev_info(info->dev, "%s: %d-%02d-%02d %02d:%02d:%02d\n", __func__, 3038c2ecf20Sopenharmony_ci data[RTC_YEAR] + 2000, data[RTC_MONTH], data[RTC_DATE], 3048c2ecf20Sopenharmony_ci data[RTC_HOUR], data[RTC_MIN], data[RTC_SEC]); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci mutex_lock(&info->lock); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci ret = max8997_rtc_stop_alarm(info); 3098c2ecf20Sopenharmony_ci if (ret < 0) 3108c2ecf20Sopenharmony_ci goto out; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci ret = max8997_bulk_write(info->rtc, MAX8997_RTC_ALARM1_SEC, RTC_NR_TIME, 3138c2ecf20Sopenharmony_ci data); 3148c2ecf20Sopenharmony_ci if (ret < 0) { 3158c2ecf20Sopenharmony_ci dev_err(info->dev, "%s: fail to write alarm reg(%d)\n", 3168c2ecf20Sopenharmony_ci __func__, ret); 3178c2ecf20Sopenharmony_ci goto out; 3188c2ecf20Sopenharmony_ci } 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci ret = max8997_rtc_set_update_reg(info); 3218c2ecf20Sopenharmony_ci if (ret < 0) 3228c2ecf20Sopenharmony_ci goto out; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci if (alrm->enabled) 3258c2ecf20Sopenharmony_ci ret = max8997_rtc_start_alarm(info); 3268c2ecf20Sopenharmony_ciout: 3278c2ecf20Sopenharmony_ci mutex_unlock(&info->lock); 3288c2ecf20Sopenharmony_ci return ret; 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic int max8997_rtc_alarm_irq_enable(struct device *dev, 3328c2ecf20Sopenharmony_ci unsigned int enabled) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci struct max8997_rtc_info *info = dev_get_drvdata(dev); 3358c2ecf20Sopenharmony_ci int ret; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci mutex_lock(&info->lock); 3388c2ecf20Sopenharmony_ci if (enabled) 3398c2ecf20Sopenharmony_ci ret = max8997_rtc_start_alarm(info); 3408c2ecf20Sopenharmony_ci else 3418c2ecf20Sopenharmony_ci ret = max8997_rtc_stop_alarm(info); 3428c2ecf20Sopenharmony_ci mutex_unlock(&info->lock); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci return ret; 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cistatic irqreturn_t max8997_rtc_alarm_irq(int irq, void *data) 3488c2ecf20Sopenharmony_ci{ 3498c2ecf20Sopenharmony_ci struct max8997_rtc_info *info = data; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci dev_info(info->dev, "%s:irq(%d)\n", __func__, irq); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3568c2ecf20Sopenharmony_ci} 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_cistatic const struct rtc_class_ops max8997_rtc_ops = { 3598c2ecf20Sopenharmony_ci .read_time = max8997_rtc_read_time, 3608c2ecf20Sopenharmony_ci .set_time = max8997_rtc_set_time, 3618c2ecf20Sopenharmony_ci .read_alarm = max8997_rtc_read_alarm, 3628c2ecf20Sopenharmony_ci .set_alarm = max8997_rtc_set_alarm, 3638c2ecf20Sopenharmony_ci .alarm_irq_enable = max8997_rtc_alarm_irq_enable, 3648c2ecf20Sopenharmony_ci}; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_cistatic void max8997_rtc_enable_wtsr(struct max8997_rtc_info *info, bool enable) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci int ret; 3698c2ecf20Sopenharmony_ci u8 val, mask; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci if (!wtsr_en) 3728c2ecf20Sopenharmony_ci return; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci if (enable) 3758c2ecf20Sopenharmony_ci val = (1 << WTSR_EN_SHIFT) | (3 << WTSRT_SHIFT); 3768c2ecf20Sopenharmony_ci else 3778c2ecf20Sopenharmony_ci val = 0; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci mask = WTSR_EN_MASK | WTSRT_MASK; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci dev_info(info->dev, "%s: %s WTSR\n", __func__, 3828c2ecf20Sopenharmony_ci enable ? "enable" : "disable"); 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci ret = max8997_update_reg(info->rtc, MAX8997_RTC_WTSR_SMPL, val, mask); 3858c2ecf20Sopenharmony_ci if (ret < 0) { 3868c2ecf20Sopenharmony_ci dev_err(info->dev, "%s: fail to update WTSR reg(%d)\n", 3878c2ecf20Sopenharmony_ci __func__, ret); 3888c2ecf20Sopenharmony_ci return; 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci max8997_rtc_set_update_reg(info); 3928c2ecf20Sopenharmony_ci} 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_cistatic void max8997_rtc_enable_smpl(struct max8997_rtc_info *info, bool enable) 3958c2ecf20Sopenharmony_ci{ 3968c2ecf20Sopenharmony_ci int ret; 3978c2ecf20Sopenharmony_ci u8 val, mask; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci if (!smpl_en) 4008c2ecf20Sopenharmony_ci return; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci if (enable) 4038c2ecf20Sopenharmony_ci val = (1 << SMPL_EN_SHIFT) | (0 << SMPLT_SHIFT); 4048c2ecf20Sopenharmony_ci else 4058c2ecf20Sopenharmony_ci val = 0; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci mask = SMPL_EN_MASK | SMPLT_MASK; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci dev_info(info->dev, "%s: %s SMPL\n", __func__, 4108c2ecf20Sopenharmony_ci enable ? "enable" : "disable"); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci ret = max8997_update_reg(info->rtc, MAX8997_RTC_WTSR_SMPL, val, mask); 4138c2ecf20Sopenharmony_ci if (ret < 0) { 4148c2ecf20Sopenharmony_ci dev_err(info->dev, "%s: fail to update SMPL reg(%d)\n", 4158c2ecf20Sopenharmony_ci __func__, ret); 4168c2ecf20Sopenharmony_ci return; 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci max8997_rtc_set_update_reg(info); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci val = 0; 4228c2ecf20Sopenharmony_ci max8997_read_reg(info->rtc, MAX8997_RTC_WTSR_SMPL, &val); 4238c2ecf20Sopenharmony_ci pr_info("WTSR_SMPL(0x%02x)\n", val); 4248c2ecf20Sopenharmony_ci} 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_cistatic int max8997_rtc_init_reg(struct max8997_rtc_info *info) 4278c2ecf20Sopenharmony_ci{ 4288c2ecf20Sopenharmony_ci u8 data[2]; 4298c2ecf20Sopenharmony_ci int ret; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci /* Set RTC control register : Binary mode, 24hour mdoe */ 4328c2ecf20Sopenharmony_ci data[0] = (1 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT); 4338c2ecf20Sopenharmony_ci data[1] = (0 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci info->rtc_24hr_mode = 1; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci ret = max8997_bulk_write(info->rtc, MAX8997_RTC_CTRLMASK, 2, data); 4388c2ecf20Sopenharmony_ci if (ret < 0) { 4398c2ecf20Sopenharmony_ci dev_err(info->dev, "%s: fail to write controlm reg(%d)\n", 4408c2ecf20Sopenharmony_ci __func__, ret); 4418c2ecf20Sopenharmony_ci return ret; 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci ret = max8997_rtc_set_update_reg(info); 4458c2ecf20Sopenharmony_ci return ret; 4468c2ecf20Sopenharmony_ci} 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_cistatic int max8997_rtc_probe(struct platform_device *pdev) 4498c2ecf20Sopenharmony_ci{ 4508c2ecf20Sopenharmony_ci struct max8997_dev *max8997 = dev_get_drvdata(pdev->dev.parent); 4518c2ecf20Sopenharmony_ci struct max8997_rtc_info *info; 4528c2ecf20Sopenharmony_ci int ret, virq; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci info = devm_kzalloc(&pdev->dev, sizeof(struct max8997_rtc_info), 4558c2ecf20Sopenharmony_ci GFP_KERNEL); 4568c2ecf20Sopenharmony_ci if (!info) 4578c2ecf20Sopenharmony_ci return -ENOMEM; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci mutex_init(&info->lock); 4608c2ecf20Sopenharmony_ci info->dev = &pdev->dev; 4618c2ecf20Sopenharmony_ci info->max8997 = max8997; 4628c2ecf20Sopenharmony_ci info->rtc = max8997->rtc; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, info); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci ret = max8997_rtc_init_reg(info); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci if (ret < 0) { 4698c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to initialize RTC reg:%d\n", ret); 4708c2ecf20Sopenharmony_ci return ret; 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci max8997_rtc_enable_wtsr(info, true); 4748c2ecf20Sopenharmony_ci max8997_rtc_enable_smpl(info, true); 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci device_init_wakeup(&pdev->dev, 1); 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci info->rtc_dev = devm_rtc_device_register(&pdev->dev, "max8997-rtc", 4798c2ecf20Sopenharmony_ci &max8997_rtc_ops, THIS_MODULE); 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci if (IS_ERR(info->rtc_dev)) { 4828c2ecf20Sopenharmony_ci ret = PTR_ERR(info->rtc_dev); 4838c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret); 4848c2ecf20Sopenharmony_ci return ret; 4858c2ecf20Sopenharmony_ci } 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci virq = irq_create_mapping(max8997->irq_domain, MAX8997_PMICIRQ_RTCA1); 4888c2ecf20Sopenharmony_ci if (!virq) { 4898c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to create mapping alarm IRQ\n"); 4908c2ecf20Sopenharmony_ci ret = -ENXIO; 4918c2ecf20Sopenharmony_ci goto err_out; 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci info->virq = virq; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(&pdev->dev, virq, NULL, 4968c2ecf20Sopenharmony_ci max8997_rtc_alarm_irq, 0, 4978c2ecf20Sopenharmony_ci "rtc-alarm0", info); 4988c2ecf20Sopenharmony_ci if (ret < 0) 4998c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n", 5008c2ecf20Sopenharmony_ci info->virq, ret); 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_cierr_out: 5038c2ecf20Sopenharmony_ci return ret; 5048c2ecf20Sopenharmony_ci} 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_cistatic void max8997_rtc_shutdown(struct platform_device *pdev) 5078c2ecf20Sopenharmony_ci{ 5088c2ecf20Sopenharmony_ci struct max8997_rtc_info *info = platform_get_drvdata(pdev); 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci max8997_rtc_enable_wtsr(info, false); 5118c2ecf20Sopenharmony_ci max8997_rtc_enable_smpl(info, false); 5128c2ecf20Sopenharmony_ci} 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_cistatic const struct platform_device_id rtc_id[] = { 5158c2ecf20Sopenharmony_ci { "max8997-rtc", 0 }, 5168c2ecf20Sopenharmony_ci {}, 5178c2ecf20Sopenharmony_ci}; 5188c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(platform, rtc_id); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_cistatic struct platform_driver max8997_rtc_driver = { 5218c2ecf20Sopenharmony_ci .driver = { 5228c2ecf20Sopenharmony_ci .name = "max8997-rtc", 5238c2ecf20Sopenharmony_ci }, 5248c2ecf20Sopenharmony_ci .probe = max8997_rtc_probe, 5258c2ecf20Sopenharmony_ci .shutdown = max8997_rtc_shutdown, 5268c2ecf20Sopenharmony_ci .id_table = rtc_id, 5278c2ecf20Sopenharmony_ci}; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_cimodule_platform_driver(max8997_rtc_driver); 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Maxim MAX8997 RTC driver"); 5328c2ecf20Sopenharmony_ciMODULE_AUTHOR("<ms925.kim@samsung.com>"); 5338c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 534