18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// Copyright (c) 2013-2014 Samsung Electronics Co., Ltd 48c2ecf20Sopenharmony_ci// http://www.samsung.com 58c2ecf20Sopenharmony_ci// 68c2ecf20Sopenharmony_ci// Copyright (C) 2013 Google, Inc 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/i2c.h> 128c2ecf20Sopenharmony_ci#include <linux/bcd.h> 138c2ecf20Sopenharmony_ci#include <linux/regmap.h> 148c2ecf20Sopenharmony_ci#include <linux/rtc.h> 158c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 168c2ecf20Sopenharmony_ci#include <linux/mfd/samsung/core.h> 178c2ecf20Sopenharmony_ci#include <linux/mfd/samsung/irq.h> 188c2ecf20Sopenharmony_ci#include <linux/mfd/samsung/rtc.h> 198c2ecf20Sopenharmony_ci#include <linux/mfd/samsung/s2mps14.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/* 228c2ecf20Sopenharmony_ci * Maximum number of retries for checking changes in UDR field 238c2ecf20Sopenharmony_ci * of S5M_RTC_UDR_CON register (to limit possible endless loop). 248c2ecf20Sopenharmony_ci * 258c2ecf20Sopenharmony_ci * After writing to RTC registers (setting time or alarm) read the UDR field 268c2ecf20Sopenharmony_ci * in S5M_RTC_UDR_CON register. UDR is auto-cleared when data have 278c2ecf20Sopenharmony_ci * been transferred. 288c2ecf20Sopenharmony_ci */ 298c2ecf20Sopenharmony_ci#define UDR_READ_RETRY_CNT 5 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cienum { 328c2ecf20Sopenharmony_ci RTC_SEC = 0, 338c2ecf20Sopenharmony_ci RTC_MIN, 348c2ecf20Sopenharmony_ci RTC_HOUR, 358c2ecf20Sopenharmony_ci RTC_WEEKDAY, 368c2ecf20Sopenharmony_ci RTC_DATE, 378c2ecf20Sopenharmony_ci RTC_MONTH, 388c2ecf20Sopenharmony_ci RTC_YEAR1, 398c2ecf20Sopenharmony_ci RTC_YEAR2, 408c2ecf20Sopenharmony_ci /* Make sure this is always the last enum name. */ 418c2ecf20Sopenharmony_ci RTC_MAX_NUM_TIME_REGS 428c2ecf20Sopenharmony_ci}; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/* 458c2ecf20Sopenharmony_ci * Registers used by the driver which are different between chipsets. 468c2ecf20Sopenharmony_ci * 478c2ecf20Sopenharmony_ci * Operations like read time and write alarm/time require updating 488c2ecf20Sopenharmony_ci * specific fields in UDR register. These fields usually are auto-cleared 498c2ecf20Sopenharmony_ci * (with some exceptions). 508c2ecf20Sopenharmony_ci * 518c2ecf20Sopenharmony_ci * Table of operations per device: 528c2ecf20Sopenharmony_ci * 538c2ecf20Sopenharmony_ci * Device | Write time | Read time | Write alarm 548c2ecf20Sopenharmony_ci * ================================================= 558c2ecf20Sopenharmony_ci * S5M8767 | UDR + TIME | | UDR 568c2ecf20Sopenharmony_ci * S2MPS11/14 | WUDR | RUDR | WUDR + RUDR 578c2ecf20Sopenharmony_ci * S2MPS13 | WUDR | RUDR | WUDR + AUDR 588c2ecf20Sopenharmony_ci * S2MPS15 | WUDR | RUDR | AUDR 598c2ecf20Sopenharmony_ci */ 608c2ecf20Sopenharmony_cistruct s5m_rtc_reg_config { 618c2ecf20Sopenharmony_ci /* Number of registers used for setting time/alarm0/alarm1 */ 628c2ecf20Sopenharmony_ci unsigned int regs_count; 638c2ecf20Sopenharmony_ci /* First register for time, seconds */ 648c2ecf20Sopenharmony_ci unsigned int time; 658c2ecf20Sopenharmony_ci /* RTC control register */ 668c2ecf20Sopenharmony_ci unsigned int ctrl; 678c2ecf20Sopenharmony_ci /* First register for alarm 0, seconds */ 688c2ecf20Sopenharmony_ci unsigned int alarm0; 698c2ecf20Sopenharmony_ci /* First register for alarm 1, seconds */ 708c2ecf20Sopenharmony_ci unsigned int alarm1; 718c2ecf20Sopenharmony_ci /* 728c2ecf20Sopenharmony_ci * Register for update flag (UDR). Typically setting UDR field to 1 738c2ecf20Sopenharmony_ci * will enable update of time or alarm register. Then it will be 748c2ecf20Sopenharmony_ci * auto-cleared after successful update. 758c2ecf20Sopenharmony_ci */ 768c2ecf20Sopenharmony_ci unsigned int udr_update; 778c2ecf20Sopenharmony_ci /* Auto-cleared mask in UDR field for writing time and alarm */ 788c2ecf20Sopenharmony_ci unsigned int autoclear_udr_mask; 798c2ecf20Sopenharmony_ci /* 808c2ecf20Sopenharmony_ci * Masks in UDR field for time and alarm operations. 818c2ecf20Sopenharmony_ci * The read time mask can be 0. Rest should not. 828c2ecf20Sopenharmony_ci */ 838c2ecf20Sopenharmony_ci unsigned int read_time_udr_mask; 848c2ecf20Sopenharmony_ci unsigned int write_time_udr_mask; 858c2ecf20Sopenharmony_ci unsigned int write_alarm_udr_mask; 868c2ecf20Sopenharmony_ci}; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci/* Register map for S5M8763 and S5M8767 */ 898c2ecf20Sopenharmony_cistatic const struct s5m_rtc_reg_config s5m_rtc_regs = { 908c2ecf20Sopenharmony_ci .regs_count = 8, 918c2ecf20Sopenharmony_ci .time = S5M_RTC_SEC, 928c2ecf20Sopenharmony_ci .ctrl = S5M_ALARM1_CONF, 938c2ecf20Sopenharmony_ci .alarm0 = S5M_ALARM0_SEC, 948c2ecf20Sopenharmony_ci .alarm1 = S5M_ALARM1_SEC, 958c2ecf20Sopenharmony_ci .udr_update = S5M_RTC_UDR_CON, 968c2ecf20Sopenharmony_ci .autoclear_udr_mask = S5M_RTC_UDR_MASK, 978c2ecf20Sopenharmony_ci .read_time_udr_mask = 0, /* Not needed */ 988c2ecf20Sopenharmony_ci .write_time_udr_mask = S5M_RTC_UDR_MASK | S5M_RTC_TIME_EN_MASK, 998c2ecf20Sopenharmony_ci .write_alarm_udr_mask = S5M_RTC_UDR_MASK, 1008c2ecf20Sopenharmony_ci}; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci/* Register map for S2MPS13 */ 1038c2ecf20Sopenharmony_cistatic const struct s5m_rtc_reg_config s2mps13_rtc_regs = { 1048c2ecf20Sopenharmony_ci .regs_count = 7, 1058c2ecf20Sopenharmony_ci .time = S2MPS_RTC_SEC, 1068c2ecf20Sopenharmony_ci .ctrl = S2MPS_RTC_CTRL, 1078c2ecf20Sopenharmony_ci .alarm0 = S2MPS_ALARM0_SEC, 1088c2ecf20Sopenharmony_ci .alarm1 = S2MPS_ALARM1_SEC, 1098c2ecf20Sopenharmony_ci .udr_update = S2MPS_RTC_UDR_CON, 1108c2ecf20Sopenharmony_ci .autoclear_udr_mask = S2MPS_RTC_WUDR_MASK, 1118c2ecf20Sopenharmony_ci .read_time_udr_mask = S2MPS_RTC_RUDR_MASK, 1128c2ecf20Sopenharmony_ci .write_time_udr_mask = S2MPS_RTC_WUDR_MASK, 1138c2ecf20Sopenharmony_ci .write_alarm_udr_mask = S2MPS_RTC_WUDR_MASK | S2MPS13_RTC_AUDR_MASK, 1148c2ecf20Sopenharmony_ci}; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci/* Register map for S2MPS11/14 */ 1178c2ecf20Sopenharmony_cistatic const struct s5m_rtc_reg_config s2mps14_rtc_regs = { 1188c2ecf20Sopenharmony_ci .regs_count = 7, 1198c2ecf20Sopenharmony_ci .time = S2MPS_RTC_SEC, 1208c2ecf20Sopenharmony_ci .ctrl = S2MPS_RTC_CTRL, 1218c2ecf20Sopenharmony_ci .alarm0 = S2MPS_ALARM0_SEC, 1228c2ecf20Sopenharmony_ci .alarm1 = S2MPS_ALARM1_SEC, 1238c2ecf20Sopenharmony_ci .udr_update = S2MPS_RTC_UDR_CON, 1248c2ecf20Sopenharmony_ci .autoclear_udr_mask = S2MPS_RTC_WUDR_MASK, 1258c2ecf20Sopenharmony_ci .read_time_udr_mask = S2MPS_RTC_RUDR_MASK, 1268c2ecf20Sopenharmony_ci .write_time_udr_mask = S2MPS_RTC_WUDR_MASK, 1278c2ecf20Sopenharmony_ci .write_alarm_udr_mask = S2MPS_RTC_WUDR_MASK | S2MPS_RTC_RUDR_MASK, 1288c2ecf20Sopenharmony_ci}; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci/* 1318c2ecf20Sopenharmony_ci * Register map for S2MPS15 - in comparison to S2MPS14 the WUDR and AUDR bits 1328c2ecf20Sopenharmony_ci * are swapped. 1338c2ecf20Sopenharmony_ci */ 1348c2ecf20Sopenharmony_cistatic const struct s5m_rtc_reg_config s2mps15_rtc_regs = { 1358c2ecf20Sopenharmony_ci .regs_count = 7, 1368c2ecf20Sopenharmony_ci .time = S2MPS_RTC_SEC, 1378c2ecf20Sopenharmony_ci .ctrl = S2MPS_RTC_CTRL, 1388c2ecf20Sopenharmony_ci .alarm0 = S2MPS_ALARM0_SEC, 1398c2ecf20Sopenharmony_ci .alarm1 = S2MPS_ALARM1_SEC, 1408c2ecf20Sopenharmony_ci .udr_update = S2MPS_RTC_UDR_CON, 1418c2ecf20Sopenharmony_ci .autoclear_udr_mask = S2MPS_RTC_WUDR_MASK, 1428c2ecf20Sopenharmony_ci .read_time_udr_mask = S2MPS_RTC_RUDR_MASK, 1438c2ecf20Sopenharmony_ci .write_time_udr_mask = S2MPS15_RTC_WUDR_MASK, 1448c2ecf20Sopenharmony_ci .write_alarm_udr_mask = S2MPS15_RTC_AUDR_MASK, 1458c2ecf20Sopenharmony_ci}; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistruct s5m_rtc_info { 1488c2ecf20Sopenharmony_ci struct device *dev; 1498c2ecf20Sopenharmony_ci struct i2c_client *i2c; 1508c2ecf20Sopenharmony_ci struct sec_pmic_dev *s5m87xx; 1518c2ecf20Sopenharmony_ci struct regmap *regmap; 1528c2ecf20Sopenharmony_ci struct rtc_device *rtc_dev; 1538c2ecf20Sopenharmony_ci int irq; 1548c2ecf20Sopenharmony_ci enum sec_device_type device_type; 1558c2ecf20Sopenharmony_ci int rtc_24hr_mode; 1568c2ecf20Sopenharmony_ci const struct s5m_rtc_reg_config *regs; 1578c2ecf20Sopenharmony_ci}; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic const struct regmap_config s5m_rtc_regmap_config = { 1608c2ecf20Sopenharmony_ci .reg_bits = 8, 1618c2ecf20Sopenharmony_ci .val_bits = 8, 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci .max_register = S5M_RTC_REG_MAX, 1648c2ecf20Sopenharmony_ci}; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic const struct regmap_config s2mps14_rtc_regmap_config = { 1678c2ecf20Sopenharmony_ci .reg_bits = 8, 1688c2ecf20Sopenharmony_ci .val_bits = 8, 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci .max_register = S2MPS_RTC_REG_MAX, 1718c2ecf20Sopenharmony_ci}; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic void s5m8767_data_to_tm(u8 *data, struct rtc_time *tm, 1748c2ecf20Sopenharmony_ci int rtc_24hr_mode) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci tm->tm_sec = data[RTC_SEC] & 0x7f; 1778c2ecf20Sopenharmony_ci tm->tm_min = data[RTC_MIN] & 0x7f; 1788c2ecf20Sopenharmony_ci if (rtc_24hr_mode) { 1798c2ecf20Sopenharmony_ci tm->tm_hour = data[RTC_HOUR] & 0x1f; 1808c2ecf20Sopenharmony_ci } else { 1818c2ecf20Sopenharmony_ci tm->tm_hour = data[RTC_HOUR] & 0x0f; 1828c2ecf20Sopenharmony_ci if (data[RTC_HOUR] & HOUR_PM_MASK) 1838c2ecf20Sopenharmony_ci tm->tm_hour += 12; 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci tm->tm_wday = ffs(data[RTC_WEEKDAY] & 0x7f); 1878c2ecf20Sopenharmony_ci tm->tm_mday = data[RTC_DATE] & 0x1f; 1888c2ecf20Sopenharmony_ci tm->tm_mon = (data[RTC_MONTH] & 0x0f) - 1; 1898c2ecf20Sopenharmony_ci tm->tm_year = (data[RTC_YEAR1] & 0x7f) + 100; 1908c2ecf20Sopenharmony_ci tm->tm_yday = 0; 1918c2ecf20Sopenharmony_ci tm->tm_isdst = 0; 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistatic int s5m8767_tm_to_data(struct rtc_time *tm, u8 *data) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci data[RTC_SEC] = tm->tm_sec; 1978c2ecf20Sopenharmony_ci data[RTC_MIN] = tm->tm_min; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci if (tm->tm_hour >= 12) 2008c2ecf20Sopenharmony_ci data[RTC_HOUR] = tm->tm_hour | HOUR_PM_MASK; 2018c2ecf20Sopenharmony_ci else 2028c2ecf20Sopenharmony_ci data[RTC_HOUR] = tm->tm_hour & ~HOUR_PM_MASK; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci data[RTC_WEEKDAY] = 1 << tm->tm_wday; 2058c2ecf20Sopenharmony_ci data[RTC_DATE] = tm->tm_mday; 2068c2ecf20Sopenharmony_ci data[RTC_MONTH] = tm->tm_mon + 1; 2078c2ecf20Sopenharmony_ci data[RTC_YEAR1] = tm->tm_year > 100 ? (tm->tm_year - 100) : 0; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (tm->tm_year < 100) { 2108c2ecf20Sopenharmony_ci pr_err("RTC cannot handle the year %d\n", 2118c2ecf20Sopenharmony_ci 1900 + tm->tm_year); 2128c2ecf20Sopenharmony_ci return -EINVAL; 2138c2ecf20Sopenharmony_ci } else { 2148c2ecf20Sopenharmony_ci return 0; 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci/* 2198c2ecf20Sopenharmony_ci * Read RTC_UDR_CON register and wait till UDR field is cleared. 2208c2ecf20Sopenharmony_ci * This indicates that time/alarm update ended. 2218c2ecf20Sopenharmony_ci */ 2228c2ecf20Sopenharmony_cistatic int s5m8767_wait_for_udr_update(struct s5m_rtc_info *info) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci int ret, retry = UDR_READ_RETRY_CNT; 2258c2ecf20Sopenharmony_ci unsigned int data; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci do { 2288c2ecf20Sopenharmony_ci ret = regmap_read(info->regmap, info->regs->udr_update, &data); 2298c2ecf20Sopenharmony_ci } while (--retry && (data & info->regs->autoclear_udr_mask) && !ret); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci if (!retry) 2328c2ecf20Sopenharmony_ci dev_err(info->dev, "waiting for UDR update, reached max number of retries\n"); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci return ret; 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic int s5m_check_peding_alarm_interrupt(struct s5m_rtc_info *info, 2388c2ecf20Sopenharmony_ci struct rtc_wkalrm *alarm) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci int ret; 2418c2ecf20Sopenharmony_ci unsigned int val; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci switch (info->device_type) { 2448c2ecf20Sopenharmony_ci case S5M8767X: 2458c2ecf20Sopenharmony_ci case S5M8763X: 2468c2ecf20Sopenharmony_ci ret = regmap_read(info->regmap, S5M_RTC_STATUS, &val); 2478c2ecf20Sopenharmony_ci val &= S5M_ALARM0_STATUS; 2488c2ecf20Sopenharmony_ci break; 2498c2ecf20Sopenharmony_ci case S2MPS15X: 2508c2ecf20Sopenharmony_ci case S2MPS14X: 2518c2ecf20Sopenharmony_ci case S2MPS13X: 2528c2ecf20Sopenharmony_ci ret = regmap_read(info->s5m87xx->regmap_pmic, S2MPS14_REG_ST2, 2538c2ecf20Sopenharmony_ci &val); 2548c2ecf20Sopenharmony_ci val &= S2MPS_ALARM0_STATUS; 2558c2ecf20Sopenharmony_ci break; 2568c2ecf20Sopenharmony_ci default: 2578c2ecf20Sopenharmony_ci return -EINVAL; 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci if (ret < 0) 2608c2ecf20Sopenharmony_ci return ret; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci if (val) 2638c2ecf20Sopenharmony_ci alarm->pending = 1; 2648c2ecf20Sopenharmony_ci else 2658c2ecf20Sopenharmony_ci alarm->pending = 0; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci return 0; 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistatic int s5m8767_rtc_set_time_reg(struct s5m_rtc_info *info) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci int ret; 2738c2ecf20Sopenharmony_ci unsigned int data; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci ret = regmap_read(info->regmap, info->regs->udr_update, &data); 2768c2ecf20Sopenharmony_ci if (ret < 0) { 2778c2ecf20Sopenharmony_ci dev_err(info->dev, "failed to read update reg(%d)\n", ret); 2788c2ecf20Sopenharmony_ci return ret; 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci data |= info->regs->write_time_udr_mask; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci ret = regmap_write(info->regmap, info->regs->udr_update, data); 2848c2ecf20Sopenharmony_ci if (ret < 0) { 2858c2ecf20Sopenharmony_ci dev_err(info->dev, "failed to write update reg(%d)\n", ret); 2868c2ecf20Sopenharmony_ci return ret; 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci ret = s5m8767_wait_for_udr_update(info); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci return ret; 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic int s5m8767_rtc_set_alarm_reg(struct s5m_rtc_info *info) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci int ret; 2978c2ecf20Sopenharmony_ci unsigned int data; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci ret = regmap_read(info->regmap, info->regs->udr_update, &data); 3008c2ecf20Sopenharmony_ci if (ret < 0) { 3018c2ecf20Sopenharmony_ci dev_err(info->dev, "%s: fail to read update reg(%d)\n", 3028c2ecf20Sopenharmony_ci __func__, ret); 3038c2ecf20Sopenharmony_ci return ret; 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci data |= info->regs->write_alarm_udr_mask; 3078c2ecf20Sopenharmony_ci switch (info->device_type) { 3088c2ecf20Sopenharmony_ci case S5M8763X: 3098c2ecf20Sopenharmony_ci case S5M8767X: 3108c2ecf20Sopenharmony_ci data &= ~S5M_RTC_TIME_EN_MASK; 3118c2ecf20Sopenharmony_ci break; 3128c2ecf20Sopenharmony_ci case S2MPS15X: 3138c2ecf20Sopenharmony_ci case S2MPS14X: 3148c2ecf20Sopenharmony_ci case S2MPS13X: 3158c2ecf20Sopenharmony_ci /* No exceptions needed */ 3168c2ecf20Sopenharmony_ci break; 3178c2ecf20Sopenharmony_ci default: 3188c2ecf20Sopenharmony_ci return -EINVAL; 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci ret = regmap_write(info->regmap, info->regs->udr_update, data); 3228c2ecf20Sopenharmony_ci if (ret < 0) { 3238c2ecf20Sopenharmony_ci dev_err(info->dev, "%s: fail to write update reg(%d)\n", 3248c2ecf20Sopenharmony_ci __func__, ret); 3258c2ecf20Sopenharmony_ci return ret; 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci ret = s5m8767_wait_for_udr_update(info); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci /* On S2MPS13 the AUDR is not auto-cleared */ 3318c2ecf20Sopenharmony_ci if (info->device_type == S2MPS13X) 3328c2ecf20Sopenharmony_ci regmap_update_bits(info->regmap, info->regs->udr_update, 3338c2ecf20Sopenharmony_ci S2MPS13_RTC_AUDR_MASK, 0); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci return ret; 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic void s5m8763_data_to_tm(u8 *data, struct rtc_time *tm) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci tm->tm_sec = bcd2bin(data[RTC_SEC]); 3418c2ecf20Sopenharmony_ci tm->tm_min = bcd2bin(data[RTC_MIN]); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci if (data[RTC_HOUR] & HOUR_12) { 3448c2ecf20Sopenharmony_ci tm->tm_hour = bcd2bin(data[RTC_HOUR] & 0x1f); 3458c2ecf20Sopenharmony_ci if (data[RTC_HOUR] & HOUR_PM) 3468c2ecf20Sopenharmony_ci tm->tm_hour += 12; 3478c2ecf20Sopenharmony_ci } else { 3488c2ecf20Sopenharmony_ci tm->tm_hour = bcd2bin(data[RTC_HOUR] & 0x3f); 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci tm->tm_wday = data[RTC_WEEKDAY] & 0x07; 3528c2ecf20Sopenharmony_ci tm->tm_mday = bcd2bin(data[RTC_DATE]); 3538c2ecf20Sopenharmony_ci tm->tm_mon = bcd2bin(data[RTC_MONTH]); 3548c2ecf20Sopenharmony_ci tm->tm_year = bcd2bin(data[RTC_YEAR1]) + bcd2bin(data[RTC_YEAR2]) * 100; 3558c2ecf20Sopenharmony_ci tm->tm_year -= 1900; 3568c2ecf20Sopenharmony_ci} 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_cistatic void s5m8763_tm_to_data(struct rtc_time *tm, u8 *data) 3598c2ecf20Sopenharmony_ci{ 3608c2ecf20Sopenharmony_ci data[RTC_SEC] = bin2bcd(tm->tm_sec); 3618c2ecf20Sopenharmony_ci data[RTC_MIN] = bin2bcd(tm->tm_min); 3628c2ecf20Sopenharmony_ci data[RTC_HOUR] = bin2bcd(tm->tm_hour); 3638c2ecf20Sopenharmony_ci data[RTC_WEEKDAY] = tm->tm_wday; 3648c2ecf20Sopenharmony_ci data[RTC_DATE] = bin2bcd(tm->tm_mday); 3658c2ecf20Sopenharmony_ci data[RTC_MONTH] = bin2bcd(tm->tm_mon); 3668c2ecf20Sopenharmony_ci data[RTC_YEAR1] = bin2bcd(tm->tm_year % 100); 3678c2ecf20Sopenharmony_ci data[RTC_YEAR2] = bin2bcd((tm->tm_year + 1900) / 100); 3688c2ecf20Sopenharmony_ci} 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_cistatic int s5m_rtc_read_time(struct device *dev, struct rtc_time *tm) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci struct s5m_rtc_info *info = dev_get_drvdata(dev); 3738c2ecf20Sopenharmony_ci u8 data[RTC_MAX_NUM_TIME_REGS]; 3748c2ecf20Sopenharmony_ci int ret; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci if (info->regs->read_time_udr_mask) { 3778c2ecf20Sopenharmony_ci ret = regmap_update_bits(info->regmap, 3788c2ecf20Sopenharmony_ci info->regs->udr_update, 3798c2ecf20Sopenharmony_ci info->regs->read_time_udr_mask, 3808c2ecf20Sopenharmony_ci info->regs->read_time_udr_mask); 3818c2ecf20Sopenharmony_ci if (ret) { 3828c2ecf20Sopenharmony_ci dev_err(dev, 3838c2ecf20Sopenharmony_ci "Failed to prepare registers for time reading: %d\n", 3848c2ecf20Sopenharmony_ci ret); 3858c2ecf20Sopenharmony_ci return ret; 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci ret = regmap_bulk_read(info->regmap, info->regs->time, data, 3898c2ecf20Sopenharmony_ci info->regs->regs_count); 3908c2ecf20Sopenharmony_ci if (ret < 0) 3918c2ecf20Sopenharmony_ci return ret; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci switch (info->device_type) { 3948c2ecf20Sopenharmony_ci case S5M8763X: 3958c2ecf20Sopenharmony_ci s5m8763_data_to_tm(data, tm); 3968c2ecf20Sopenharmony_ci break; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci case S5M8767X: 3998c2ecf20Sopenharmony_ci case S2MPS15X: 4008c2ecf20Sopenharmony_ci case S2MPS14X: 4018c2ecf20Sopenharmony_ci case S2MPS13X: 4028c2ecf20Sopenharmony_ci s5m8767_data_to_tm(data, tm, info->rtc_24hr_mode); 4038c2ecf20Sopenharmony_ci break; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci default: 4068c2ecf20Sopenharmony_ci return -EINVAL; 4078c2ecf20Sopenharmony_ci } 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci dev_dbg(dev, "%s: %ptR(%d)\n", __func__, tm, tm->tm_wday); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci return 0; 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_cistatic int s5m_rtc_set_time(struct device *dev, struct rtc_time *tm) 4158c2ecf20Sopenharmony_ci{ 4168c2ecf20Sopenharmony_ci struct s5m_rtc_info *info = dev_get_drvdata(dev); 4178c2ecf20Sopenharmony_ci u8 data[RTC_MAX_NUM_TIME_REGS]; 4188c2ecf20Sopenharmony_ci int ret = 0; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci switch (info->device_type) { 4218c2ecf20Sopenharmony_ci case S5M8763X: 4228c2ecf20Sopenharmony_ci s5m8763_tm_to_data(tm, data); 4238c2ecf20Sopenharmony_ci break; 4248c2ecf20Sopenharmony_ci case S5M8767X: 4258c2ecf20Sopenharmony_ci case S2MPS15X: 4268c2ecf20Sopenharmony_ci case S2MPS14X: 4278c2ecf20Sopenharmony_ci case S2MPS13X: 4288c2ecf20Sopenharmony_ci ret = s5m8767_tm_to_data(tm, data); 4298c2ecf20Sopenharmony_ci break; 4308c2ecf20Sopenharmony_ci default: 4318c2ecf20Sopenharmony_ci return -EINVAL; 4328c2ecf20Sopenharmony_ci } 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci if (ret < 0) 4358c2ecf20Sopenharmony_ci return ret; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci dev_dbg(dev, "%s: %ptR(%d)\n", __func__, tm, tm->tm_wday); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci ret = regmap_raw_write(info->regmap, info->regs->time, data, 4408c2ecf20Sopenharmony_ci info->regs->regs_count); 4418c2ecf20Sopenharmony_ci if (ret < 0) 4428c2ecf20Sopenharmony_ci return ret; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci ret = s5m8767_rtc_set_time_reg(info); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci return ret; 4478c2ecf20Sopenharmony_ci} 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_cistatic int s5m_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) 4508c2ecf20Sopenharmony_ci{ 4518c2ecf20Sopenharmony_ci struct s5m_rtc_info *info = dev_get_drvdata(dev); 4528c2ecf20Sopenharmony_ci u8 data[RTC_MAX_NUM_TIME_REGS]; 4538c2ecf20Sopenharmony_ci unsigned int val; 4548c2ecf20Sopenharmony_ci int ret, i; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci ret = regmap_bulk_read(info->regmap, info->regs->alarm0, data, 4578c2ecf20Sopenharmony_ci info->regs->regs_count); 4588c2ecf20Sopenharmony_ci if (ret < 0) 4598c2ecf20Sopenharmony_ci return ret; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci switch (info->device_type) { 4628c2ecf20Sopenharmony_ci case S5M8763X: 4638c2ecf20Sopenharmony_ci s5m8763_data_to_tm(data, &alrm->time); 4648c2ecf20Sopenharmony_ci ret = regmap_read(info->regmap, S5M_ALARM0_CONF, &val); 4658c2ecf20Sopenharmony_ci if (ret < 0) 4668c2ecf20Sopenharmony_ci return ret; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci alrm->enabled = !!val; 4698c2ecf20Sopenharmony_ci break; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci case S5M8767X: 4728c2ecf20Sopenharmony_ci case S2MPS15X: 4738c2ecf20Sopenharmony_ci case S2MPS14X: 4748c2ecf20Sopenharmony_ci case S2MPS13X: 4758c2ecf20Sopenharmony_ci s5m8767_data_to_tm(data, &alrm->time, info->rtc_24hr_mode); 4768c2ecf20Sopenharmony_ci alrm->enabled = 0; 4778c2ecf20Sopenharmony_ci for (i = 0; i < info->regs->regs_count; i++) { 4788c2ecf20Sopenharmony_ci if (data[i] & ALARM_ENABLE_MASK) { 4798c2ecf20Sopenharmony_ci alrm->enabled = 1; 4808c2ecf20Sopenharmony_ci break; 4818c2ecf20Sopenharmony_ci } 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci break; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci default: 4868c2ecf20Sopenharmony_ci return -EINVAL; 4878c2ecf20Sopenharmony_ci } 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci dev_dbg(dev, "%s: %ptR(%d)\n", __func__, &alrm->time, alrm->time.tm_wday); 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci ret = s5m_check_peding_alarm_interrupt(info, alrm); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci return 0; 4948c2ecf20Sopenharmony_ci} 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_cistatic int s5m_rtc_stop_alarm(struct s5m_rtc_info *info) 4978c2ecf20Sopenharmony_ci{ 4988c2ecf20Sopenharmony_ci u8 data[RTC_MAX_NUM_TIME_REGS]; 4998c2ecf20Sopenharmony_ci int ret, i; 5008c2ecf20Sopenharmony_ci struct rtc_time tm; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci ret = regmap_bulk_read(info->regmap, info->regs->alarm0, data, 5038c2ecf20Sopenharmony_ci info->regs->regs_count); 5048c2ecf20Sopenharmony_ci if (ret < 0) 5058c2ecf20Sopenharmony_ci return ret; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci s5m8767_data_to_tm(data, &tm, info->rtc_24hr_mode); 5088c2ecf20Sopenharmony_ci dev_dbg(info->dev, "%s: %ptR(%d)\n", __func__, &tm, tm.tm_wday); 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci switch (info->device_type) { 5118c2ecf20Sopenharmony_ci case S5M8763X: 5128c2ecf20Sopenharmony_ci ret = regmap_write(info->regmap, S5M_ALARM0_CONF, 0); 5138c2ecf20Sopenharmony_ci break; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci case S5M8767X: 5168c2ecf20Sopenharmony_ci case S2MPS15X: 5178c2ecf20Sopenharmony_ci case S2MPS14X: 5188c2ecf20Sopenharmony_ci case S2MPS13X: 5198c2ecf20Sopenharmony_ci for (i = 0; i < info->regs->regs_count; i++) 5208c2ecf20Sopenharmony_ci data[i] &= ~ALARM_ENABLE_MASK; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci ret = regmap_raw_write(info->regmap, info->regs->alarm0, data, 5238c2ecf20Sopenharmony_ci info->regs->regs_count); 5248c2ecf20Sopenharmony_ci if (ret < 0) 5258c2ecf20Sopenharmony_ci return ret; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci ret = s5m8767_rtc_set_alarm_reg(info); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci break; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci default: 5328c2ecf20Sopenharmony_ci return -EINVAL; 5338c2ecf20Sopenharmony_ci } 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci return ret; 5368c2ecf20Sopenharmony_ci} 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_cistatic int s5m_rtc_start_alarm(struct s5m_rtc_info *info) 5398c2ecf20Sopenharmony_ci{ 5408c2ecf20Sopenharmony_ci int ret; 5418c2ecf20Sopenharmony_ci u8 data[RTC_MAX_NUM_TIME_REGS]; 5428c2ecf20Sopenharmony_ci u8 alarm0_conf; 5438c2ecf20Sopenharmony_ci struct rtc_time tm; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci ret = regmap_bulk_read(info->regmap, info->regs->alarm0, data, 5468c2ecf20Sopenharmony_ci info->regs->regs_count); 5478c2ecf20Sopenharmony_ci if (ret < 0) 5488c2ecf20Sopenharmony_ci return ret; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci s5m8767_data_to_tm(data, &tm, info->rtc_24hr_mode); 5518c2ecf20Sopenharmony_ci dev_dbg(info->dev, "%s: %ptR(%d)\n", __func__, &tm, tm.tm_wday); 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci switch (info->device_type) { 5548c2ecf20Sopenharmony_ci case S5M8763X: 5558c2ecf20Sopenharmony_ci alarm0_conf = 0x77; 5568c2ecf20Sopenharmony_ci ret = regmap_write(info->regmap, S5M_ALARM0_CONF, alarm0_conf); 5578c2ecf20Sopenharmony_ci break; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci case S5M8767X: 5608c2ecf20Sopenharmony_ci case S2MPS15X: 5618c2ecf20Sopenharmony_ci case S2MPS14X: 5628c2ecf20Sopenharmony_ci case S2MPS13X: 5638c2ecf20Sopenharmony_ci data[RTC_SEC] |= ALARM_ENABLE_MASK; 5648c2ecf20Sopenharmony_ci data[RTC_MIN] |= ALARM_ENABLE_MASK; 5658c2ecf20Sopenharmony_ci data[RTC_HOUR] |= ALARM_ENABLE_MASK; 5668c2ecf20Sopenharmony_ci data[RTC_WEEKDAY] &= ~ALARM_ENABLE_MASK; 5678c2ecf20Sopenharmony_ci if (data[RTC_DATE] & 0x1f) 5688c2ecf20Sopenharmony_ci data[RTC_DATE] |= ALARM_ENABLE_MASK; 5698c2ecf20Sopenharmony_ci if (data[RTC_MONTH] & 0xf) 5708c2ecf20Sopenharmony_ci data[RTC_MONTH] |= ALARM_ENABLE_MASK; 5718c2ecf20Sopenharmony_ci if (data[RTC_YEAR1] & 0x7f) 5728c2ecf20Sopenharmony_ci data[RTC_YEAR1] |= ALARM_ENABLE_MASK; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci ret = regmap_raw_write(info->regmap, info->regs->alarm0, data, 5758c2ecf20Sopenharmony_ci info->regs->regs_count); 5768c2ecf20Sopenharmony_ci if (ret < 0) 5778c2ecf20Sopenharmony_ci return ret; 5788c2ecf20Sopenharmony_ci ret = s5m8767_rtc_set_alarm_reg(info); 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci break; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci default: 5838c2ecf20Sopenharmony_ci return -EINVAL; 5848c2ecf20Sopenharmony_ci } 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci return ret; 5878c2ecf20Sopenharmony_ci} 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_cistatic int s5m_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) 5908c2ecf20Sopenharmony_ci{ 5918c2ecf20Sopenharmony_ci struct s5m_rtc_info *info = dev_get_drvdata(dev); 5928c2ecf20Sopenharmony_ci u8 data[RTC_MAX_NUM_TIME_REGS]; 5938c2ecf20Sopenharmony_ci int ret; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci switch (info->device_type) { 5968c2ecf20Sopenharmony_ci case S5M8763X: 5978c2ecf20Sopenharmony_ci s5m8763_tm_to_data(&alrm->time, data); 5988c2ecf20Sopenharmony_ci break; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci case S5M8767X: 6018c2ecf20Sopenharmony_ci case S2MPS15X: 6028c2ecf20Sopenharmony_ci case S2MPS14X: 6038c2ecf20Sopenharmony_ci case S2MPS13X: 6048c2ecf20Sopenharmony_ci s5m8767_tm_to_data(&alrm->time, data); 6058c2ecf20Sopenharmony_ci break; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci default: 6088c2ecf20Sopenharmony_ci return -EINVAL; 6098c2ecf20Sopenharmony_ci } 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci dev_dbg(dev, "%s: %ptR(%d)\n", __func__, &alrm->time, alrm->time.tm_wday); 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci ret = s5m_rtc_stop_alarm(info); 6148c2ecf20Sopenharmony_ci if (ret < 0) 6158c2ecf20Sopenharmony_ci return ret; 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci ret = regmap_raw_write(info->regmap, info->regs->alarm0, data, 6188c2ecf20Sopenharmony_ci info->regs->regs_count); 6198c2ecf20Sopenharmony_ci if (ret < 0) 6208c2ecf20Sopenharmony_ci return ret; 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci ret = s5m8767_rtc_set_alarm_reg(info); 6238c2ecf20Sopenharmony_ci if (ret < 0) 6248c2ecf20Sopenharmony_ci return ret; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci if (alrm->enabled) 6278c2ecf20Sopenharmony_ci ret = s5m_rtc_start_alarm(info); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci return ret; 6308c2ecf20Sopenharmony_ci} 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_cistatic int s5m_rtc_alarm_irq_enable(struct device *dev, 6338c2ecf20Sopenharmony_ci unsigned int enabled) 6348c2ecf20Sopenharmony_ci{ 6358c2ecf20Sopenharmony_ci struct s5m_rtc_info *info = dev_get_drvdata(dev); 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci if (enabled) 6388c2ecf20Sopenharmony_ci return s5m_rtc_start_alarm(info); 6398c2ecf20Sopenharmony_ci else 6408c2ecf20Sopenharmony_ci return s5m_rtc_stop_alarm(info); 6418c2ecf20Sopenharmony_ci} 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_cistatic irqreturn_t s5m_rtc_alarm_irq(int irq, void *data) 6448c2ecf20Sopenharmony_ci{ 6458c2ecf20Sopenharmony_ci struct s5m_rtc_info *info = data; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF); 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci return IRQ_HANDLED; 6508c2ecf20Sopenharmony_ci} 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_cistatic const struct rtc_class_ops s5m_rtc_ops = { 6538c2ecf20Sopenharmony_ci .read_time = s5m_rtc_read_time, 6548c2ecf20Sopenharmony_ci .set_time = s5m_rtc_set_time, 6558c2ecf20Sopenharmony_ci .read_alarm = s5m_rtc_read_alarm, 6568c2ecf20Sopenharmony_ci .set_alarm = s5m_rtc_set_alarm, 6578c2ecf20Sopenharmony_ci .alarm_irq_enable = s5m_rtc_alarm_irq_enable, 6588c2ecf20Sopenharmony_ci}; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_cistatic int s5m8767_rtc_init_reg(struct s5m_rtc_info *info) 6618c2ecf20Sopenharmony_ci{ 6628c2ecf20Sopenharmony_ci u8 data[2]; 6638c2ecf20Sopenharmony_ci int ret; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci switch (info->device_type) { 6668c2ecf20Sopenharmony_ci case S5M8763X: 6678c2ecf20Sopenharmony_ci case S5M8767X: 6688c2ecf20Sopenharmony_ci /* UDR update time. Default of 7.32 ms is too long. */ 6698c2ecf20Sopenharmony_ci ret = regmap_update_bits(info->regmap, S5M_RTC_UDR_CON, 6708c2ecf20Sopenharmony_ci S5M_RTC_UDR_T_MASK, S5M_RTC_UDR_T_450_US); 6718c2ecf20Sopenharmony_ci if (ret < 0) 6728c2ecf20Sopenharmony_ci dev_err(info->dev, "%s: fail to change UDR time: %d\n", 6738c2ecf20Sopenharmony_ci __func__, ret); 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci /* Set RTC control register : Binary mode, 24hour mode */ 6768c2ecf20Sopenharmony_ci data[0] = (1 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT); 6778c2ecf20Sopenharmony_ci data[1] = (0 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT); 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci ret = regmap_raw_write(info->regmap, S5M_ALARM0_CONF, data, 2); 6808c2ecf20Sopenharmony_ci break; 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci case S2MPS15X: 6838c2ecf20Sopenharmony_ci case S2MPS14X: 6848c2ecf20Sopenharmony_ci case S2MPS13X: 6858c2ecf20Sopenharmony_ci data[0] = (0 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT); 6868c2ecf20Sopenharmony_ci ret = regmap_write(info->regmap, info->regs->ctrl, data[0]); 6878c2ecf20Sopenharmony_ci if (ret < 0) 6888c2ecf20Sopenharmony_ci break; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci /* 6918c2ecf20Sopenharmony_ci * Should set WUDR & (RUDR or AUDR) bits to high after writing 6928c2ecf20Sopenharmony_ci * RTC_CTRL register like writing Alarm registers. We can't find 6938c2ecf20Sopenharmony_ci * the description from datasheet but vendor code does that 6948c2ecf20Sopenharmony_ci * really. 6958c2ecf20Sopenharmony_ci */ 6968c2ecf20Sopenharmony_ci ret = s5m8767_rtc_set_alarm_reg(info); 6978c2ecf20Sopenharmony_ci break; 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci default: 7008c2ecf20Sopenharmony_ci return -EINVAL; 7018c2ecf20Sopenharmony_ci } 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci info->rtc_24hr_mode = 1; 7048c2ecf20Sopenharmony_ci if (ret < 0) { 7058c2ecf20Sopenharmony_ci dev_err(info->dev, "%s: fail to write controlm reg(%d)\n", 7068c2ecf20Sopenharmony_ci __func__, ret); 7078c2ecf20Sopenharmony_ci return ret; 7088c2ecf20Sopenharmony_ci } 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci return ret; 7118c2ecf20Sopenharmony_ci} 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_cistatic int s5m_rtc_probe(struct platform_device *pdev) 7148c2ecf20Sopenharmony_ci{ 7158c2ecf20Sopenharmony_ci struct sec_pmic_dev *s5m87xx = dev_get_drvdata(pdev->dev.parent); 7168c2ecf20Sopenharmony_ci struct sec_platform_data *pdata = s5m87xx->pdata; 7178c2ecf20Sopenharmony_ci struct s5m_rtc_info *info; 7188c2ecf20Sopenharmony_ci const struct regmap_config *regmap_cfg; 7198c2ecf20Sopenharmony_ci int ret, alarm_irq; 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci if (!pdata) { 7228c2ecf20Sopenharmony_ci dev_err(pdev->dev.parent, "Platform data not supplied\n"); 7238c2ecf20Sopenharmony_ci return -ENODEV; 7248c2ecf20Sopenharmony_ci } 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); 7278c2ecf20Sopenharmony_ci if (!info) 7288c2ecf20Sopenharmony_ci return -ENOMEM; 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci switch (platform_get_device_id(pdev)->driver_data) { 7318c2ecf20Sopenharmony_ci case S2MPS15X: 7328c2ecf20Sopenharmony_ci regmap_cfg = &s2mps14_rtc_regmap_config; 7338c2ecf20Sopenharmony_ci info->regs = &s2mps15_rtc_regs; 7348c2ecf20Sopenharmony_ci alarm_irq = S2MPS14_IRQ_RTCA0; 7358c2ecf20Sopenharmony_ci break; 7368c2ecf20Sopenharmony_ci case S2MPS14X: 7378c2ecf20Sopenharmony_ci regmap_cfg = &s2mps14_rtc_regmap_config; 7388c2ecf20Sopenharmony_ci info->regs = &s2mps14_rtc_regs; 7398c2ecf20Sopenharmony_ci alarm_irq = S2MPS14_IRQ_RTCA0; 7408c2ecf20Sopenharmony_ci break; 7418c2ecf20Sopenharmony_ci case S2MPS13X: 7428c2ecf20Sopenharmony_ci regmap_cfg = &s2mps14_rtc_regmap_config; 7438c2ecf20Sopenharmony_ci info->regs = &s2mps13_rtc_regs; 7448c2ecf20Sopenharmony_ci alarm_irq = S2MPS14_IRQ_RTCA0; 7458c2ecf20Sopenharmony_ci break; 7468c2ecf20Sopenharmony_ci case S5M8763X: 7478c2ecf20Sopenharmony_ci regmap_cfg = &s5m_rtc_regmap_config; 7488c2ecf20Sopenharmony_ci info->regs = &s5m_rtc_regs; 7498c2ecf20Sopenharmony_ci alarm_irq = S5M8763_IRQ_ALARM0; 7508c2ecf20Sopenharmony_ci break; 7518c2ecf20Sopenharmony_ci case S5M8767X: 7528c2ecf20Sopenharmony_ci regmap_cfg = &s5m_rtc_regmap_config; 7538c2ecf20Sopenharmony_ci info->regs = &s5m_rtc_regs; 7548c2ecf20Sopenharmony_ci alarm_irq = S5M8767_IRQ_RTCA1; 7558c2ecf20Sopenharmony_ci break; 7568c2ecf20Sopenharmony_ci default: 7578c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 7588c2ecf20Sopenharmony_ci "Device type %lu is not supported by RTC driver\n", 7598c2ecf20Sopenharmony_ci platform_get_device_id(pdev)->driver_data); 7608c2ecf20Sopenharmony_ci return -ENODEV; 7618c2ecf20Sopenharmony_ci } 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci info->i2c = i2c_new_dummy_device(s5m87xx->i2c->adapter, RTC_I2C_ADDR); 7648c2ecf20Sopenharmony_ci if (IS_ERR(info->i2c)) { 7658c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to allocate I2C for RTC\n"); 7668c2ecf20Sopenharmony_ci return PTR_ERR(info->i2c); 7678c2ecf20Sopenharmony_ci } 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci info->regmap = devm_regmap_init_i2c(info->i2c, regmap_cfg); 7708c2ecf20Sopenharmony_ci if (IS_ERR(info->regmap)) { 7718c2ecf20Sopenharmony_ci ret = PTR_ERR(info->regmap); 7728c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to allocate RTC register map: %d\n", 7738c2ecf20Sopenharmony_ci ret); 7748c2ecf20Sopenharmony_ci goto err; 7758c2ecf20Sopenharmony_ci } 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci info->dev = &pdev->dev; 7788c2ecf20Sopenharmony_ci info->s5m87xx = s5m87xx; 7798c2ecf20Sopenharmony_ci info->device_type = platform_get_device_id(pdev)->driver_data; 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci if (s5m87xx->irq_data) { 7828c2ecf20Sopenharmony_ci info->irq = regmap_irq_get_virq(s5m87xx->irq_data, alarm_irq); 7838c2ecf20Sopenharmony_ci if (info->irq <= 0) { 7848c2ecf20Sopenharmony_ci ret = -EINVAL; 7858c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to get virtual IRQ %d\n", 7868c2ecf20Sopenharmony_ci alarm_irq); 7878c2ecf20Sopenharmony_ci goto err; 7888c2ecf20Sopenharmony_ci } 7898c2ecf20Sopenharmony_ci } 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, info); 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci ret = s5m8767_rtc_init_reg(info); 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci device_init_wakeup(&pdev->dev, 1); 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci info->rtc_dev = devm_rtc_device_register(&pdev->dev, "s5m-rtc", 7988c2ecf20Sopenharmony_ci &s5m_rtc_ops, THIS_MODULE); 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci if (IS_ERR(info->rtc_dev)) { 8018c2ecf20Sopenharmony_ci ret = PTR_ERR(info->rtc_dev); 8028c2ecf20Sopenharmony_ci goto err; 8038c2ecf20Sopenharmony_ci } 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci if (!info->irq) { 8068c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "Alarm IRQ not available\n"); 8078c2ecf20Sopenharmony_ci return 0; 8088c2ecf20Sopenharmony_ci } 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(&pdev->dev, info->irq, NULL, 8118c2ecf20Sopenharmony_ci s5m_rtc_alarm_irq, 0, "rtc-alarm0", 8128c2ecf20Sopenharmony_ci info); 8138c2ecf20Sopenharmony_ci if (ret < 0) { 8148c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n", 8158c2ecf20Sopenharmony_ci info->irq, ret); 8168c2ecf20Sopenharmony_ci goto err; 8178c2ecf20Sopenharmony_ci } 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci return 0; 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_cierr: 8228c2ecf20Sopenharmony_ci i2c_unregister_device(info->i2c); 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci return ret; 8258c2ecf20Sopenharmony_ci} 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_cistatic int s5m_rtc_remove(struct platform_device *pdev) 8288c2ecf20Sopenharmony_ci{ 8298c2ecf20Sopenharmony_ci struct s5m_rtc_info *info = platform_get_drvdata(pdev); 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci i2c_unregister_device(info->i2c); 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci return 0; 8348c2ecf20Sopenharmony_ci} 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 8378c2ecf20Sopenharmony_cistatic int s5m_rtc_resume(struct device *dev) 8388c2ecf20Sopenharmony_ci{ 8398c2ecf20Sopenharmony_ci struct s5m_rtc_info *info = dev_get_drvdata(dev); 8408c2ecf20Sopenharmony_ci int ret = 0; 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci if (info->irq && device_may_wakeup(dev)) 8438c2ecf20Sopenharmony_ci ret = disable_irq_wake(info->irq); 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci return ret; 8468c2ecf20Sopenharmony_ci} 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_cistatic int s5m_rtc_suspend(struct device *dev) 8498c2ecf20Sopenharmony_ci{ 8508c2ecf20Sopenharmony_ci struct s5m_rtc_info *info = dev_get_drvdata(dev); 8518c2ecf20Sopenharmony_ci int ret = 0; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci if (info->irq && device_may_wakeup(dev)) 8548c2ecf20Sopenharmony_ci ret = enable_irq_wake(info->irq); 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci return ret; 8578c2ecf20Sopenharmony_ci} 8588c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(s5m_rtc_pm_ops, s5m_rtc_suspend, s5m_rtc_resume); 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_cistatic const struct platform_device_id s5m_rtc_id[] = { 8638c2ecf20Sopenharmony_ci { "s5m-rtc", S5M8767X }, 8648c2ecf20Sopenharmony_ci { "s2mps13-rtc", S2MPS13X }, 8658c2ecf20Sopenharmony_ci { "s2mps14-rtc", S2MPS14X }, 8668c2ecf20Sopenharmony_ci { "s2mps15-rtc", S2MPS15X }, 8678c2ecf20Sopenharmony_ci { }, 8688c2ecf20Sopenharmony_ci}; 8698c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(platform, s5m_rtc_id); 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_cistatic struct platform_driver s5m_rtc_driver = { 8728c2ecf20Sopenharmony_ci .driver = { 8738c2ecf20Sopenharmony_ci .name = "s5m-rtc", 8748c2ecf20Sopenharmony_ci .pm = &s5m_rtc_pm_ops, 8758c2ecf20Sopenharmony_ci }, 8768c2ecf20Sopenharmony_ci .probe = s5m_rtc_probe, 8778c2ecf20Sopenharmony_ci .remove = s5m_rtc_remove, 8788c2ecf20Sopenharmony_ci .id_table = s5m_rtc_id, 8798c2ecf20Sopenharmony_ci}; 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_cimodule_platform_driver(s5m_rtc_driver); 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci/* Module information */ 8848c2ecf20Sopenharmony_ciMODULE_AUTHOR("Sangbeom Kim <sbkim73@samsung.com>"); 8858c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Samsung S5M/S2MPS14 RTC driver"); 8868c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 8878c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:s5m-rtc"); 888