18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// RTC driver for Maxim MAX77686 and MAX77802 48c2ecf20Sopenharmony_ci// 58c2ecf20Sopenharmony_ci// Copyright (C) 2012 Samsung Electronics Co.Ltd 68c2ecf20Sopenharmony_ci// 78c2ecf20Sopenharmony_ci// based on rtc-max8997.c 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/i2c.h> 108c2ecf20Sopenharmony_ci#include <linux/slab.h> 118c2ecf20Sopenharmony_ci#include <linux/rtc.h> 128c2ecf20Sopenharmony_ci#include <linux/delay.h> 138c2ecf20Sopenharmony_ci#include <linux/mutex.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 168c2ecf20Sopenharmony_ci#include <linux/mfd/max77686-private.h> 178c2ecf20Sopenharmony_ci#include <linux/irqdomain.h> 188c2ecf20Sopenharmony_ci#include <linux/regmap.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define MAX77686_I2C_ADDR_RTC (0x0C >> 1) 218c2ecf20Sopenharmony_ci#define MAX77620_I2C_ADDR_RTC 0x68 228c2ecf20Sopenharmony_ci#define MAX77686_INVALID_I2C_ADDR (-1) 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci/* Define non existing register */ 258c2ecf20Sopenharmony_ci#define MAX77686_INVALID_REG (-1) 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* RTC Control Register */ 288c2ecf20Sopenharmony_ci#define BCD_EN_SHIFT 0 298c2ecf20Sopenharmony_ci#define BCD_EN_MASK BIT(BCD_EN_SHIFT) 308c2ecf20Sopenharmony_ci#define MODEL24_SHIFT 1 318c2ecf20Sopenharmony_ci#define MODEL24_MASK BIT(MODEL24_SHIFT) 328c2ecf20Sopenharmony_ci/* RTC Update Register1 */ 338c2ecf20Sopenharmony_ci#define RTC_UDR_SHIFT 0 348c2ecf20Sopenharmony_ci#define RTC_UDR_MASK BIT(RTC_UDR_SHIFT) 358c2ecf20Sopenharmony_ci#define RTC_RBUDR_SHIFT 4 368c2ecf20Sopenharmony_ci#define RTC_RBUDR_MASK BIT(RTC_RBUDR_SHIFT) 378c2ecf20Sopenharmony_ci/* RTC Hour register */ 388c2ecf20Sopenharmony_ci#define HOUR_PM_SHIFT 6 398c2ecf20Sopenharmony_ci#define HOUR_PM_MASK BIT(HOUR_PM_SHIFT) 408c2ecf20Sopenharmony_ci/* RTC Alarm Enable */ 418c2ecf20Sopenharmony_ci#define ALARM_ENABLE_SHIFT 7 428c2ecf20Sopenharmony_ci#define ALARM_ENABLE_MASK BIT(ALARM_ENABLE_SHIFT) 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#define REG_RTC_NONE 0xdeadbeef 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci/* 478c2ecf20Sopenharmony_ci * MAX77802 has separate register (RTCAE1) for alarm enable instead 488c2ecf20Sopenharmony_ci * using 1 bit from registers RTC{SEC,MIN,HOUR,DAY,MONTH,YEAR,DATE} 498c2ecf20Sopenharmony_ci * as in done in MAX77686. 508c2ecf20Sopenharmony_ci */ 518c2ecf20Sopenharmony_ci#define MAX77802_ALARM_ENABLE_VALUE 0x77 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 max77686_rtc_driver_data { 658c2ecf20Sopenharmony_ci /* Minimum usecs needed for a RTC update */ 668c2ecf20Sopenharmony_ci unsigned long delay; 678c2ecf20Sopenharmony_ci /* Mask used to read RTC registers value */ 688c2ecf20Sopenharmony_ci u8 mask; 698c2ecf20Sopenharmony_ci /* Registers offset to I2C addresses map */ 708c2ecf20Sopenharmony_ci const unsigned int *map; 718c2ecf20Sopenharmony_ci /* Has a separate alarm enable register? */ 728c2ecf20Sopenharmony_ci bool alarm_enable_reg; 738c2ecf20Sopenharmony_ci /* I2C address for RTC block */ 748c2ecf20Sopenharmony_ci int rtc_i2c_addr; 758c2ecf20Sopenharmony_ci /* RTC interrupt via platform resource */ 768c2ecf20Sopenharmony_ci bool rtc_irq_from_platform; 778c2ecf20Sopenharmony_ci /* Pending alarm status register */ 788c2ecf20Sopenharmony_ci int alarm_pending_status_reg; 798c2ecf20Sopenharmony_ci /* RTC IRQ CHIP for regmap */ 808c2ecf20Sopenharmony_ci const struct regmap_irq_chip *rtc_irq_chip; 818c2ecf20Sopenharmony_ci /* regmap configuration for the chip */ 828c2ecf20Sopenharmony_ci const struct regmap_config *regmap_config; 838c2ecf20Sopenharmony_ci}; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistruct max77686_rtc_info { 868c2ecf20Sopenharmony_ci struct device *dev; 878c2ecf20Sopenharmony_ci struct i2c_client *rtc; 888c2ecf20Sopenharmony_ci struct rtc_device *rtc_dev; 898c2ecf20Sopenharmony_ci struct mutex lock; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci struct regmap *regmap; 928c2ecf20Sopenharmony_ci struct regmap *rtc_regmap; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci const struct max77686_rtc_driver_data *drv_data; 958c2ecf20Sopenharmony_ci struct regmap_irq_chip_data *rtc_irq_data; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci int rtc_irq; 988c2ecf20Sopenharmony_ci int virq; 998c2ecf20Sopenharmony_ci int rtc_24hr_mode; 1008c2ecf20Sopenharmony_ci}; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cienum MAX77686_RTC_OP { 1038c2ecf20Sopenharmony_ci MAX77686_RTC_WRITE, 1048c2ecf20Sopenharmony_ci MAX77686_RTC_READ, 1058c2ecf20Sopenharmony_ci}; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci/* These are not registers but just offsets that are mapped to addresses */ 1088c2ecf20Sopenharmony_cienum max77686_rtc_reg_offset { 1098c2ecf20Sopenharmony_ci REG_RTC_CONTROLM = 0, 1108c2ecf20Sopenharmony_ci REG_RTC_CONTROL, 1118c2ecf20Sopenharmony_ci REG_RTC_UPDATE0, 1128c2ecf20Sopenharmony_ci REG_WTSR_SMPL_CNTL, 1138c2ecf20Sopenharmony_ci REG_RTC_SEC, 1148c2ecf20Sopenharmony_ci REG_RTC_MIN, 1158c2ecf20Sopenharmony_ci REG_RTC_HOUR, 1168c2ecf20Sopenharmony_ci REG_RTC_WEEKDAY, 1178c2ecf20Sopenharmony_ci REG_RTC_MONTH, 1188c2ecf20Sopenharmony_ci REG_RTC_YEAR, 1198c2ecf20Sopenharmony_ci REG_RTC_DATE, 1208c2ecf20Sopenharmony_ci REG_ALARM1_SEC, 1218c2ecf20Sopenharmony_ci REG_ALARM1_MIN, 1228c2ecf20Sopenharmony_ci REG_ALARM1_HOUR, 1238c2ecf20Sopenharmony_ci REG_ALARM1_WEEKDAY, 1248c2ecf20Sopenharmony_ci REG_ALARM1_MONTH, 1258c2ecf20Sopenharmony_ci REG_ALARM1_YEAR, 1268c2ecf20Sopenharmony_ci REG_ALARM1_DATE, 1278c2ecf20Sopenharmony_ci REG_ALARM2_SEC, 1288c2ecf20Sopenharmony_ci REG_ALARM2_MIN, 1298c2ecf20Sopenharmony_ci REG_ALARM2_HOUR, 1308c2ecf20Sopenharmony_ci REG_ALARM2_WEEKDAY, 1318c2ecf20Sopenharmony_ci REG_ALARM2_MONTH, 1328c2ecf20Sopenharmony_ci REG_ALARM2_YEAR, 1338c2ecf20Sopenharmony_ci REG_ALARM2_DATE, 1348c2ecf20Sopenharmony_ci REG_RTC_AE1, 1358c2ecf20Sopenharmony_ci REG_RTC_END, 1368c2ecf20Sopenharmony_ci}; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci/* Maps RTC registers offset to the MAX77686 register addresses */ 1398c2ecf20Sopenharmony_cistatic const unsigned int max77686_map[REG_RTC_END] = { 1408c2ecf20Sopenharmony_ci [REG_RTC_CONTROLM] = MAX77686_RTC_CONTROLM, 1418c2ecf20Sopenharmony_ci [REG_RTC_CONTROL] = MAX77686_RTC_CONTROL, 1428c2ecf20Sopenharmony_ci [REG_RTC_UPDATE0] = MAX77686_RTC_UPDATE0, 1438c2ecf20Sopenharmony_ci [REG_WTSR_SMPL_CNTL] = MAX77686_WTSR_SMPL_CNTL, 1448c2ecf20Sopenharmony_ci [REG_RTC_SEC] = MAX77686_RTC_SEC, 1458c2ecf20Sopenharmony_ci [REG_RTC_MIN] = MAX77686_RTC_MIN, 1468c2ecf20Sopenharmony_ci [REG_RTC_HOUR] = MAX77686_RTC_HOUR, 1478c2ecf20Sopenharmony_ci [REG_RTC_WEEKDAY] = MAX77686_RTC_WEEKDAY, 1488c2ecf20Sopenharmony_ci [REG_RTC_MONTH] = MAX77686_RTC_MONTH, 1498c2ecf20Sopenharmony_ci [REG_RTC_YEAR] = MAX77686_RTC_YEAR, 1508c2ecf20Sopenharmony_ci [REG_RTC_DATE] = MAX77686_RTC_DATE, 1518c2ecf20Sopenharmony_ci [REG_ALARM1_SEC] = MAX77686_ALARM1_SEC, 1528c2ecf20Sopenharmony_ci [REG_ALARM1_MIN] = MAX77686_ALARM1_MIN, 1538c2ecf20Sopenharmony_ci [REG_ALARM1_HOUR] = MAX77686_ALARM1_HOUR, 1548c2ecf20Sopenharmony_ci [REG_ALARM1_WEEKDAY] = MAX77686_ALARM1_WEEKDAY, 1558c2ecf20Sopenharmony_ci [REG_ALARM1_MONTH] = MAX77686_ALARM1_MONTH, 1568c2ecf20Sopenharmony_ci [REG_ALARM1_YEAR] = MAX77686_ALARM1_YEAR, 1578c2ecf20Sopenharmony_ci [REG_ALARM1_DATE] = MAX77686_ALARM1_DATE, 1588c2ecf20Sopenharmony_ci [REG_ALARM2_SEC] = MAX77686_ALARM2_SEC, 1598c2ecf20Sopenharmony_ci [REG_ALARM2_MIN] = MAX77686_ALARM2_MIN, 1608c2ecf20Sopenharmony_ci [REG_ALARM2_HOUR] = MAX77686_ALARM2_HOUR, 1618c2ecf20Sopenharmony_ci [REG_ALARM2_WEEKDAY] = MAX77686_ALARM2_WEEKDAY, 1628c2ecf20Sopenharmony_ci [REG_ALARM2_MONTH] = MAX77686_ALARM2_MONTH, 1638c2ecf20Sopenharmony_ci [REG_ALARM2_YEAR] = MAX77686_ALARM2_YEAR, 1648c2ecf20Sopenharmony_ci [REG_ALARM2_DATE] = MAX77686_ALARM2_DATE, 1658c2ecf20Sopenharmony_ci [REG_RTC_AE1] = REG_RTC_NONE, 1668c2ecf20Sopenharmony_ci}; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic const struct regmap_irq max77686_rtc_irqs[] = { 1698c2ecf20Sopenharmony_ci /* RTC interrupts */ 1708c2ecf20Sopenharmony_ci REGMAP_IRQ_REG(0, 0, MAX77686_RTCINT_RTC60S_MSK), 1718c2ecf20Sopenharmony_ci REGMAP_IRQ_REG(1, 0, MAX77686_RTCINT_RTCA1_MSK), 1728c2ecf20Sopenharmony_ci REGMAP_IRQ_REG(2, 0, MAX77686_RTCINT_RTCA2_MSK), 1738c2ecf20Sopenharmony_ci REGMAP_IRQ_REG(3, 0, MAX77686_RTCINT_SMPL_MSK), 1748c2ecf20Sopenharmony_ci REGMAP_IRQ_REG(4, 0, MAX77686_RTCINT_RTC1S_MSK), 1758c2ecf20Sopenharmony_ci REGMAP_IRQ_REG(5, 0, MAX77686_RTCINT_WTSR_MSK), 1768c2ecf20Sopenharmony_ci}; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic const struct regmap_irq_chip max77686_rtc_irq_chip = { 1798c2ecf20Sopenharmony_ci .name = "max77686-rtc", 1808c2ecf20Sopenharmony_ci .status_base = MAX77686_RTC_INT, 1818c2ecf20Sopenharmony_ci .mask_base = MAX77686_RTC_INTM, 1828c2ecf20Sopenharmony_ci .num_regs = 1, 1838c2ecf20Sopenharmony_ci .irqs = max77686_rtc_irqs, 1848c2ecf20Sopenharmony_ci .num_irqs = ARRAY_SIZE(max77686_rtc_irqs), 1858c2ecf20Sopenharmony_ci}; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic const struct regmap_config max77686_rtc_regmap_config = { 1888c2ecf20Sopenharmony_ci .reg_bits = 8, 1898c2ecf20Sopenharmony_ci .val_bits = 8, 1908c2ecf20Sopenharmony_ci}; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic const struct max77686_rtc_driver_data max77686_drv_data = { 1938c2ecf20Sopenharmony_ci .delay = 16000, 1948c2ecf20Sopenharmony_ci .mask = 0x7f, 1958c2ecf20Sopenharmony_ci .map = max77686_map, 1968c2ecf20Sopenharmony_ci .alarm_enable_reg = false, 1978c2ecf20Sopenharmony_ci .rtc_irq_from_platform = false, 1988c2ecf20Sopenharmony_ci .alarm_pending_status_reg = MAX77686_REG_STATUS2, 1998c2ecf20Sopenharmony_ci .rtc_i2c_addr = MAX77686_I2C_ADDR_RTC, 2008c2ecf20Sopenharmony_ci .rtc_irq_chip = &max77686_rtc_irq_chip, 2018c2ecf20Sopenharmony_ci .regmap_config = &max77686_rtc_regmap_config, 2028c2ecf20Sopenharmony_ci}; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic const struct regmap_config max77620_rtc_regmap_config = { 2058c2ecf20Sopenharmony_ci .reg_bits = 8, 2068c2ecf20Sopenharmony_ci .val_bits = 8, 2078c2ecf20Sopenharmony_ci .use_single_write = true, 2088c2ecf20Sopenharmony_ci}; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic const struct max77686_rtc_driver_data max77620_drv_data = { 2118c2ecf20Sopenharmony_ci .delay = 16000, 2128c2ecf20Sopenharmony_ci .mask = 0x7f, 2138c2ecf20Sopenharmony_ci .map = max77686_map, 2148c2ecf20Sopenharmony_ci .alarm_enable_reg = false, 2158c2ecf20Sopenharmony_ci .rtc_irq_from_platform = true, 2168c2ecf20Sopenharmony_ci .alarm_pending_status_reg = MAX77686_INVALID_REG, 2178c2ecf20Sopenharmony_ci .rtc_i2c_addr = MAX77620_I2C_ADDR_RTC, 2188c2ecf20Sopenharmony_ci .rtc_irq_chip = &max77686_rtc_irq_chip, 2198c2ecf20Sopenharmony_ci .regmap_config = &max77620_rtc_regmap_config, 2208c2ecf20Sopenharmony_ci}; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic const unsigned int max77802_map[REG_RTC_END] = { 2238c2ecf20Sopenharmony_ci [REG_RTC_CONTROLM] = MAX77802_RTC_CONTROLM, 2248c2ecf20Sopenharmony_ci [REG_RTC_CONTROL] = MAX77802_RTC_CONTROL, 2258c2ecf20Sopenharmony_ci [REG_RTC_UPDATE0] = MAX77802_RTC_UPDATE0, 2268c2ecf20Sopenharmony_ci [REG_WTSR_SMPL_CNTL] = MAX77802_WTSR_SMPL_CNTL, 2278c2ecf20Sopenharmony_ci [REG_RTC_SEC] = MAX77802_RTC_SEC, 2288c2ecf20Sopenharmony_ci [REG_RTC_MIN] = MAX77802_RTC_MIN, 2298c2ecf20Sopenharmony_ci [REG_RTC_HOUR] = MAX77802_RTC_HOUR, 2308c2ecf20Sopenharmony_ci [REG_RTC_WEEKDAY] = MAX77802_RTC_WEEKDAY, 2318c2ecf20Sopenharmony_ci [REG_RTC_MONTH] = MAX77802_RTC_MONTH, 2328c2ecf20Sopenharmony_ci [REG_RTC_YEAR] = MAX77802_RTC_YEAR, 2338c2ecf20Sopenharmony_ci [REG_RTC_DATE] = MAX77802_RTC_DATE, 2348c2ecf20Sopenharmony_ci [REG_ALARM1_SEC] = MAX77802_ALARM1_SEC, 2358c2ecf20Sopenharmony_ci [REG_ALARM1_MIN] = MAX77802_ALARM1_MIN, 2368c2ecf20Sopenharmony_ci [REG_ALARM1_HOUR] = MAX77802_ALARM1_HOUR, 2378c2ecf20Sopenharmony_ci [REG_ALARM1_WEEKDAY] = MAX77802_ALARM1_WEEKDAY, 2388c2ecf20Sopenharmony_ci [REG_ALARM1_MONTH] = MAX77802_ALARM1_MONTH, 2398c2ecf20Sopenharmony_ci [REG_ALARM1_YEAR] = MAX77802_ALARM1_YEAR, 2408c2ecf20Sopenharmony_ci [REG_ALARM1_DATE] = MAX77802_ALARM1_DATE, 2418c2ecf20Sopenharmony_ci [REG_ALARM2_SEC] = MAX77802_ALARM2_SEC, 2428c2ecf20Sopenharmony_ci [REG_ALARM2_MIN] = MAX77802_ALARM2_MIN, 2438c2ecf20Sopenharmony_ci [REG_ALARM2_HOUR] = MAX77802_ALARM2_HOUR, 2448c2ecf20Sopenharmony_ci [REG_ALARM2_WEEKDAY] = MAX77802_ALARM2_WEEKDAY, 2458c2ecf20Sopenharmony_ci [REG_ALARM2_MONTH] = MAX77802_ALARM2_MONTH, 2468c2ecf20Sopenharmony_ci [REG_ALARM2_YEAR] = MAX77802_ALARM2_YEAR, 2478c2ecf20Sopenharmony_ci [REG_ALARM2_DATE] = MAX77802_ALARM2_DATE, 2488c2ecf20Sopenharmony_ci [REG_RTC_AE1] = MAX77802_RTC_AE1, 2498c2ecf20Sopenharmony_ci}; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic const struct regmap_irq_chip max77802_rtc_irq_chip = { 2528c2ecf20Sopenharmony_ci .name = "max77802-rtc", 2538c2ecf20Sopenharmony_ci .status_base = MAX77802_RTC_INT, 2548c2ecf20Sopenharmony_ci .mask_base = MAX77802_RTC_INTM, 2558c2ecf20Sopenharmony_ci .num_regs = 1, 2568c2ecf20Sopenharmony_ci .irqs = max77686_rtc_irqs, /* same masks as 77686 */ 2578c2ecf20Sopenharmony_ci .num_irqs = ARRAY_SIZE(max77686_rtc_irqs), 2588c2ecf20Sopenharmony_ci}; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_cistatic const struct max77686_rtc_driver_data max77802_drv_data = { 2618c2ecf20Sopenharmony_ci .delay = 200, 2628c2ecf20Sopenharmony_ci .mask = 0xff, 2638c2ecf20Sopenharmony_ci .map = max77802_map, 2648c2ecf20Sopenharmony_ci .alarm_enable_reg = true, 2658c2ecf20Sopenharmony_ci .rtc_irq_from_platform = false, 2668c2ecf20Sopenharmony_ci .alarm_pending_status_reg = MAX77686_REG_STATUS2, 2678c2ecf20Sopenharmony_ci .rtc_i2c_addr = MAX77686_INVALID_I2C_ADDR, 2688c2ecf20Sopenharmony_ci .rtc_irq_chip = &max77802_rtc_irq_chip, 2698c2ecf20Sopenharmony_ci}; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic void max77686_rtc_data_to_tm(u8 *data, struct rtc_time *tm, 2728c2ecf20Sopenharmony_ci struct max77686_rtc_info *info) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci u8 mask = info->drv_data->mask; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci tm->tm_sec = data[RTC_SEC] & mask; 2778c2ecf20Sopenharmony_ci tm->tm_min = data[RTC_MIN] & mask; 2788c2ecf20Sopenharmony_ci if (info->rtc_24hr_mode) { 2798c2ecf20Sopenharmony_ci tm->tm_hour = data[RTC_HOUR] & 0x1f; 2808c2ecf20Sopenharmony_ci } else { 2818c2ecf20Sopenharmony_ci tm->tm_hour = data[RTC_HOUR] & 0x0f; 2828c2ecf20Sopenharmony_ci if (data[RTC_HOUR] & HOUR_PM_MASK) 2838c2ecf20Sopenharmony_ci tm->tm_hour += 12; 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci /* Only a single bit is set in data[], so fls() would be equivalent */ 2878c2ecf20Sopenharmony_ci tm->tm_wday = ffs(data[RTC_WEEKDAY] & mask) - 1; 2888c2ecf20Sopenharmony_ci tm->tm_mday = data[RTC_DATE] & 0x1f; 2898c2ecf20Sopenharmony_ci tm->tm_mon = (data[RTC_MONTH] & 0x0f) - 1; 2908c2ecf20Sopenharmony_ci tm->tm_year = data[RTC_YEAR] & mask; 2918c2ecf20Sopenharmony_ci tm->tm_yday = 0; 2928c2ecf20Sopenharmony_ci tm->tm_isdst = 0; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci /* 2958c2ecf20Sopenharmony_ci * MAX77686 uses 1 bit from sec/min/hour/etc RTC registers and the 2968c2ecf20Sopenharmony_ci * year values are just 0..99 so add 100 to support up to 2099. 2978c2ecf20Sopenharmony_ci */ 2988c2ecf20Sopenharmony_ci if (!info->drv_data->alarm_enable_reg) 2998c2ecf20Sopenharmony_ci tm->tm_year += 100; 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistatic int max77686_rtc_tm_to_data(struct rtc_time *tm, u8 *data, 3038c2ecf20Sopenharmony_ci struct max77686_rtc_info *info) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci data[RTC_SEC] = tm->tm_sec; 3068c2ecf20Sopenharmony_ci data[RTC_MIN] = tm->tm_min; 3078c2ecf20Sopenharmony_ci data[RTC_HOUR] = tm->tm_hour; 3088c2ecf20Sopenharmony_ci data[RTC_WEEKDAY] = 1 << tm->tm_wday; 3098c2ecf20Sopenharmony_ci data[RTC_DATE] = tm->tm_mday; 3108c2ecf20Sopenharmony_ci data[RTC_MONTH] = tm->tm_mon + 1; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci if (info->drv_data->alarm_enable_reg) { 3138c2ecf20Sopenharmony_ci data[RTC_YEAR] = tm->tm_year; 3148c2ecf20Sopenharmony_ci return 0; 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci data[RTC_YEAR] = tm->tm_year > 100 ? (tm->tm_year - 100) : 0; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci if (tm->tm_year < 100) { 3208c2ecf20Sopenharmony_ci dev_err(info->dev, "RTC cannot handle the year %d.\n", 3218c2ecf20Sopenharmony_ci 1900 + tm->tm_year); 3228c2ecf20Sopenharmony_ci return -EINVAL; 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci return 0; 3268c2ecf20Sopenharmony_ci} 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_cistatic int max77686_rtc_update(struct max77686_rtc_info *info, 3298c2ecf20Sopenharmony_ci enum MAX77686_RTC_OP op) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci int ret; 3328c2ecf20Sopenharmony_ci unsigned int data; 3338c2ecf20Sopenharmony_ci unsigned long delay = info->drv_data->delay; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci if (op == MAX77686_RTC_WRITE) 3368c2ecf20Sopenharmony_ci data = 1 << RTC_UDR_SHIFT; 3378c2ecf20Sopenharmony_ci else 3388c2ecf20Sopenharmony_ci data = 1 << RTC_RBUDR_SHIFT; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci ret = regmap_update_bits(info->rtc_regmap, 3418c2ecf20Sopenharmony_ci info->drv_data->map[REG_RTC_UPDATE0], 3428c2ecf20Sopenharmony_ci data, data); 3438c2ecf20Sopenharmony_ci if (ret < 0) 3448c2ecf20Sopenharmony_ci dev_err(info->dev, "Fail to write update reg(ret=%d, data=0x%x)\n", 3458c2ecf20Sopenharmony_ci ret, data); 3468c2ecf20Sopenharmony_ci else { 3478c2ecf20Sopenharmony_ci /* Minimum delay required before RTC update. */ 3488c2ecf20Sopenharmony_ci usleep_range(delay, delay * 2); 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci return ret; 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic int max77686_rtc_read_time(struct device *dev, struct rtc_time *tm) 3558c2ecf20Sopenharmony_ci{ 3568c2ecf20Sopenharmony_ci struct max77686_rtc_info *info = dev_get_drvdata(dev); 3578c2ecf20Sopenharmony_ci u8 data[RTC_NR_TIME]; 3588c2ecf20Sopenharmony_ci int ret; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci mutex_lock(&info->lock); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci ret = max77686_rtc_update(info, MAX77686_RTC_READ); 3638c2ecf20Sopenharmony_ci if (ret < 0) 3648c2ecf20Sopenharmony_ci goto out; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci ret = regmap_bulk_read(info->rtc_regmap, 3678c2ecf20Sopenharmony_ci info->drv_data->map[REG_RTC_SEC], 3688c2ecf20Sopenharmony_ci data, ARRAY_SIZE(data)); 3698c2ecf20Sopenharmony_ci if (ret < 0) { 3708c2ecf20Sopenharmony_ci dev_err(info->dev, "Fail to read time reg(%d)\n", ret); 3718c2ecf20Sopenharmony_ci goto out; 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci max77686_rtc_data_to_tm(data, tm, info); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ciout: 3778c2ecf20Sopenharmony_ci mutex_unlock(&info->lock); 3788c2ecf20Sopenharmony_ci return ret; 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_cistatic int max77686_rtc_set_time(struct device *dev, struct rtc_time *tm) 3828c2ecf20Sopenharmony_ci{ 3838c2ecf20Sopenharmony_ci struct max77686_rtc_info *info = dev_get_drvdata(dev); 3848c2ecf20Sopenharmony_ci u8 data[RTC_NR_TIME]; 3858c2ecf20Sopenharmony_ci int ret; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci ret = max77686_rtc_tm_to_data(tm, data, info); 3888c2ecf20Sopenharmony_ci if (ret < 0) 3898c2ecf20Sopenharmony_ci return ret; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci mutex_lock(&info->lock); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci ret = regmap_bulk_write(info->rtc_regmap, 3948c2ecf20Sopenharmony_ci info->drv_data->map[REG_RTC_SEC], 3958c2ecf20Sopenharmony_ci data, ARRAY_SIZE(data)); 3968c2ecf20Sopenharmony_ci if (ret < 0) { 3978c2ecf20Sopenharmony_ci dev_err(info->dev, "Fail to write time reg(%d)\n", ret); 3988c2ecf20Sopenharmony_ci goto out; 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci ret = max77686_rtc_update(info, MAX77686_RTC_WRITE); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ciout: 4048c2ecf20Sopenharmony_ci mutex_unlock(&info->lock); 4058c2ecf20Sopenharmony_ci return ret; 4068c2ecf20Sopenharmony_ci} 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_cistatic int max77686_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) 4098c2ecf20Sopenharmony_ci{ 4108c2ecf20Sopenharmony_ci struct max77686_rtc_info *info = dev_get_drvdata(dev); 4118c2ecf20Sopenharmony_ci u8 data[RTC_NR_TIME]; 4128c2ecf20Sopenharmony_ci unsigned int val; 4138c2ecf20Sopenharmony_ci const unsigned int *map = info->drv_data->map; 4148c2ecf20Sopenharmony_ci int i, ret; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci mutex_lock(&info->lock); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci ret = max77686_rtc_update(info, MAX77686_RTC_READ); 4198c2ecf20Sopenharmony_ci if (ret < 0) 4208c2ecf20Sopenharmony_ci goto out; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci ret = regmap_bulk_read(info->rtc_regmap, map[REG_ALARM1_SEC], 4238c2ecf20Sopenharmony_ci data, ARRAY_SIZE(data)); 4248c2ecf20Sopenharmony_ci if (ret < 0) { 4258c2ecf20Sopenharmony_ci dev_err(info->dev, "Fail to read alarm reg(%d)\n", ret); 4268c2ecf20Sopenharmony_ci goto out; 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci max77686_rtc_data_to_tm(data, &alrm->time, info); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci alrm->enabled = 0; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci if (info->drv_data->alarm_enable_reg) { 4348c2ecf20Sopenharmony_ci if (map[REG_RTC_AE1] == REG_RTC_NONE) { 4358c2ecf20Sopenharmony_ci ret = -EINVAL; 4368c2ecf20Sopenharmony_ci dev_err(info->dev, 4378c2ecf20Sopenharmony_ci "alarm enable register not set(%d)\n", ret); 4388c2ecf20Sopenharmony_ci goto out; 4398c2ecf20Sopenharmony_ci } 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci ret = regmap_read(info->rtc_regmap, map[REG_RTC_AE1], &val); 4428c2ecf20Sopenharmony_ci if (ret < 0) { 4438c2ecf20Sopenharmony_ci dev_err(info->dev, 4448c2ecf20Sopenharmony_ci "fail to read alarm enable(%d)\n", ret); 4458c2ecf20Sopenharmony_ci goto out; 4468c2ecf20Sopenharmony_ci } 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci if (val) 4498c2ecf20Sopenharmony_ci alrm->enabled = 1; 4508c2ecf20Sopenharmony_ci } else { 4518c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(data); i++) { 4528c2ecf20Sopenharmony_ci if (data[i] & ALARM_ENABLE_MASK) { 4538c2ecf20Sopenharmony_ci alrm->enabled = 1; 4548c2ecf20Sopenharmony_ci break; 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci alrm->pending = 0; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci if (info->drv_data->alarm_pending_status_reg == MAX77686_INVALID_REG) 4628c2ecf20Sopenharmony_ci goto out; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci ret = regmap_read(info->regmap, 4658c2ecf20Sopenharmony_ci info->drv_data->alarm_pending_status_reg, &val); 4668c2ecf20Sopenharmony_ci if (ret < 0) { 4678c2ecf20Sopenharmony_ci dev_err(info->dev, 4688c2ecf20Sopenharmony_ci "Fail to read alarm pending status reg(%d)\n", ret); 4698c2ecf20Sopenharmony_ci goto out; 4708c2ecf20Sopenharmony_ci } 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci if (val & (1 << 4)) /* RTCA1 */ 4738c2ecf20Sopenharmony_ci alrm->pending = 1; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ciout: 4768c2ecf20Sopenharmony_ci mutex_unlock(&info->lock); 4778c2ecf20Sopenharmony_ci return ret; 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_cistatic int max77686_rtc_stop_alarm(struct max77686_rtc_info *info) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci u8 data[RTC_NR_TIME]; 4838c2ecf20Sopenharmony_ci int ret, i; 4848c2ecf20Sopenharmony_ci struct rtc_time tm; 4858c2ecf20Sopenharmony_ci const unsigned int *map = info->drv_data->map; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci if (!mutex_is_locked(&info->lock)) 4888c2ecf20Sopenharmony_ci dev_warn(info->dev, "%s: should have mutex locked\n", __func__); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci ret = max77686_rtc_update(info, MAX77686_RTC_READ); 4918c2ecf20Sopenharmony_ci if (ret < 0) 4928c2ecf20Sopenharmony_ci goto out; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci if (info->drv_data->alarm_enable_reg) { 4958c2ecf20Sopenharmony_ci if (map[REG_RTC_AE1] == REG_RTC_NONE) { 4968c2ecf20Sopenharmony_ci ret = -EINVAL; 4978c2ecf20Sopenharmony_ci dev_err(info->dev, 4988c2ecf20Sopenharmony_ci "alarm enable register not set(%d)\n", ret); 4998c2ecf20Sopenharmony_ci goto out; 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci ret = regmap_write(info->rtc_regmap, map[REG_RTC_AE1], 0); 5038c2ecf20Sopenharmony_ci } else { 5048c2ecf20Sopenharmony_ci ret = regmap_bulk_read(info->rtc_regmap, map[REG_ALARM1_SEC], 5058c2ecf20Sopenharmony_ci data, ARRAY_SIZE(data)); 5068c2ecf20Sopenharmony_ci if (ret < 0) { 5078c2ecf20Sopenharmony_ci dev_err(info->dev, "Fail to read alarm reg(%d)\n", ret); 5088c2ecf20Sopenharmony_ci goto out; 5098c2ecf20Sopenharmony_ci } 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci max77686_rtc_data_to_tm(data, &tm, info); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(data); i++) 5148c2ecf20Sopenharmony_ci data[i] &= ~ALARM_ENABLE_MASK; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci ret = regmap_bulk_write(info->rtc_regmap, map[REG_ALARM1_SEC], 5178c2ecf20Sopenharmony_ci data, ARRAY_SIZE(data)); 5188c2ecf20Sopenharmony_ci } 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci if (ret < 0) { 5218c2ecf20Sopenharmony_ci dev_err(info->dev, "Fail to write alarm reg(%d)\n", ret); 5228c2ecf20Sopenharmony_ci goto out; 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci ret = max77686_rtc_update(info, MAX77686_RTC_WRITE); 5268c2ecf20Sopenharmony_ciout: 5278c2ecf20Sopenharmony_ci return ret; 5288c2ecf20Sopenharmony_ci} 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_cistatic int max77686_rtc_start_alarm(struct max77686_rtc_info *info) 5318c2ecf20Sopenharmony_ci{ 5328c2ecf20Sopenharmony_ci u8 data[RTC_NR_TIME]; 5338c2ecf20Sopenharmony_ci int ret; 5348c2ecf20Sopenharmony_ci struct rtc_time tm; 5358c2ecf20Sopenharmony_ci const unsigned int *map = info->drv_data->map; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci if (!mutex_is_locked(&info->lock)) 5388c2ecf20Sopenharmony_ci dev_warn(info->dev, "%s: should have mutex locked\n", __func__); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci ret = max77686_rtc_update(info, MAX77686_RTC_READ); 5418c2ecf20Sopenharmony_ci if (ret < 0) 5428c2ecf20Sopenharmony_ci goto out; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci if (info->drv_data->alarm_enable_reg) { 5458c2ecf20Sopenharmony_ci ret = regmap_write(info->rtc_regmap, map[REG_RTC_AE1], 5468c2ecf20Sopenharmony_ci MAX77802_ALARM_ENABLE_VALUE); 5478c2ecf20Sopenharmony_ci } else { 5488c2ecf20Sopenharmony_ci ret = regmap_bulk_read(info->rtc_regmap, map[REG_ALARM1_SEC], 5498c2ecf20Sopenharmony_ci data, ARRAY_SIZE(data)); 5508c2ecf20Sopenharmony_ci if (ret < 0) { 5518c2ecf20Sopenharmony_ci dev_err(info->dev, "Fail to read alarm reg(%d)\n", ret); 5528c2ecf20Sopenharmony_ci goto out; 5538c2ecf20Sopenharmony_ci } 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci max77686_rtc_data_to_tm(data, &tm, info); 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci data[RTC_SEC] |= (1 << ALARM_ENABLE_SHIFT); 5588c2ecf20Sopenharmony_ci data[RTC_MIN] |= (1 << ALARM_ENABLE_SHIFT); 5598c2ecf20Sopenharmony_ci data[RTC_HOUR] |= (1 << ALARM_ENABLE_SHIFT); 5608c2ecf20Sopenharmony_ci data[RTC_WEEKDAY] &= ~ALARM_ENABLE_MASK; 5618c2ecf20Sopenharmony_ci if (data[RTC_MONTH] & 0xf) 5628c2ecf20Sopenharmony_ci data[RTC_MONTH] |= (1 << ALARM_ENABLE_SHIFT); 5638c2ecf20Sopenharmony_ci if (data[RTC_YEAR] & info->drv_data->mask) 5648c2ecf20Sopenharmony_ci data[RTC_YEAR] |= (1 << ALARM_ENABLE_SHIFT); 5658c2ecf20Sopenharmony_ci if (data[RTC_DATE] & 0x1f) 5668c2ecf20Sopenharmony_ci data[RTC_DATE] |= (1 << ALARM_ENABLE_SHIFT); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci ret = regmap_bulk_write(info->rtc_regmap, map[REG_ALARM1_SEC], 5698c2ecf20Sopenharmony_ci data, ARRAY_SIZE(data)); 5708c2ecf20Sopenharmony_ci } 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci if (ret < 0) { 5738c2ecf20Sopenharmony_ci dev_err(info->dev, "Fail to write alarm reg(%d)\n", ret); 5748c2ecf20Sopenharmony_ci goto out; 5758c2ecf20Sopenharmony_ci } 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci ret = max77686_rtc_update(info, MAX77686_RTC_WRITE); 5788c2ecf20Sopenharmony_ciout: 5798c2ecf20Sopenharmony_ci return ret; 5808c2ecf20Sopenharmony_ci} 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_cistatic int max77686_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) 5838c2ecf20Sopenharmony_ci{ 5848c2ecf20Sopenharmony_ci struct max77686_rtc_info *info = dev_get_drvdata(dev); 5858c2ecf20Sopenharmony_ci u8 data[RTC_NR_TIME]; 5868c2ecf20Sopenharmony_ci int ret; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci ret = max77686_rtc_tm_to_data(&alrm->time, data, info); 5898c2ecf20Sopenharmony_ci if (ret < 0) 5908c2ecf20Sopenharmony_ci return ret; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci mutex_lock(&info->lock); 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci ret = max77686_rtc_stop_alarm(info); 5958c2ecf20Sopenharmony_ci if (ret < 0) 5968c2ecf20Sopenharmony_ci goto out; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci ret = regmap_bulk_write(info->rtc_regmap, 5998c2ecf20Sopenharmony_ci info->drv_data->map[REG_ALARM1_SEC], 6008c2ecf20Sopenharmony_ci data, ARRAY_SIZE(data)); 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci if (ret < 0) { 6038c2ecf20Sopenharmony_ci dev_err(info->dev, "Fail to write alarm reg(%d)\n", ret); 6048c2ecf20Sopenharmony_ci goto out; 6058c2ecf20Sopenharmony_ci } 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci ret = max77686_rtc_update(info, MAX77686_RTC_WRITE); 6088c2ecf20Sopenharmony_ci if (ret < 0) 6098c2ecf20Sopenharmony_ci goto out; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci if (alrm->enabled) 6128c2ecf20Sopenharmony_ci ret = max77686_rtc_start_alarm(info); 6138c2ecf20Sopenharmony_ciout: 6148c2ecf20Sopenharmony_ci mutex_unlock(&info->lock); 6158c2ecf20Sopenharmony_ci return ret; 6168c2ecf20Sopenharmony_ci} 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_cistatic int max77686_rtc_alarm_irq_enable(struct device *dev, 6198c2ecf20Sopenharmony_ci unsigned int enabled) 6208c2ecf20Sopenharmony_ci{ 6218c2ecf20Sopenharmony_ci struct max77686_rtc_info *info = dev_get_drvdata(dev); 6228c2ecf20Sopenharmony_ci int ret; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci mutex_lock(&info->lock); 6258c2ecf20Sopenharmony_ci if (enabled) 6268c2ecf20Sopenharmony_ci ret = max77686_rtc_start_alarm(info); 6278c2ecf20Sopenharmony_ci else 6288c2ecf20Sopenharmony_ci ret = max77686_rtc_stop_alarm(info); 6298c2ecf20Sopenharmony_ci mutex_unlock(&info->lock); 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci return ret; 6328c2ecf20Sopenharmony_ci} 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_cistatic irqreturn_t max77686_rtc_alarm_irq(int irq, void *data) 6358c2ecf20Sopenharmony_ci{ 6368c2ecf20Sopenharmony_ci struct max77686_rtc_info *info = data; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci dev_dbg(info->dev, "RTC alarm IRQ: %d\n", irq); 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF); 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci return IRQ_HANDLED; 6438c2ecf20Sopenharmony_ci} 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_cistatic const struct rtc_class_ops max77686_rtc_ops = { 6468c2ecf20Sopenharmony_ci .read_time = max77686_rtc_read_time, 6478c2ecf20Sopenharmony_ci .set_time = max77686_rtc_set_time, 6488c2ecf20Sopenharmony_ci .read_alarm = max77686_rtc_read_alarm, 6498c2ecf20Sopenharmony_ci .set_alarm = max77686_rtc_set_alarm, 6508c2ecf20Sopenharmony_ci .alarm_irq_enable = max77686_rtc_alarm_irq_enable, 6518c2ecf20Sopenharmony_ci}; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_cistatic int max77686_rtc_init_reg(struct max77686_rtc_info *info) 6548c2ecf20Sopenharmony_ci{ 6558c2ecf20Sopenharmony_ci u8 data[2]; 6568c2ecf20Sopenharmony_ci int ret; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci /* Set RTC control register : Binary mode, 24hour mdoe */ 6598c2ecf20Sopenharmony_ci data[0] = (1 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT); 6608c2ecf20Sopenharmony_ci data[1] = (0 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT); 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci info->rtc_24hr_mode = 1; 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci ret = regmap_bulk_write(info->rtc_regmap, 6658c2ecf20Sopenharmony_ci info->drv_data->map[REG_RTC_CONTROLM], 6668c2ecf20Sopenharmony_ci data, ARRAY_SIZE(data)); 6678c2ecf20Sopenharmony_ci if (ret < 0) { 6688c2ecf20Sopenharmony_ci dev_err(info->dev, "Fail to write controlm reg(%d)\n", ret); 6698c2ecf20Sopenharmony_ci return ret; 6708c2ecf20Sopenharmony_ci } 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci ret = max77686_rtc_update(info, MAX77686_RTC_WRITE); 6738c2ecf20Sopenharmony_ci return ret; 6748c2ecf20Sopenharmony_ci} 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_cistatic int max77686_init_rtc_regmap(struct max77686_rtc_info *info) 6778c2ecf20Sopenharmony_ci{ 6788c2ecf20Sopenharmony_ci struct device *parent = info->dev->parent; 6798c2ecf20Sopenharmony_ci struct i2c_client *parent_i2c = to_i2c_client(parent); 6808c2ecf20Sopenharmony_ci int ret; 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci if (info->drv_data->rtc_irq_from_platform) { 6838c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(info->dev); 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci info->rtc_irq = platform_get_irq(pdev, 0); 6868c2ecf20Sopenharmony_ci if (info->rtc_irq < 0) 6878c2ecf20Sopenharmony_ci return info->rtc_irq; 6888c2ecf20Sopenharmony_ci } else { 6898c2ecf20Sopenharmony_ci info->rtc_irq = parent_i2c->irq; 6908c2ecf20Sopenharmony_ci } 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci info->regmap = dev_get_regmap(parent, NULL); 6938c2ecf20Sopenharmony_ci if (!info->regmap) { 6948c2ecf20Sopenharmony_ci dev_err(info->dev, "Failed to get rtc regmap\n"); 6958c2ecf20Sopenharmony_ci return -ENODEV; 6968c2ecf20Sopenharmony_ci } 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci if (info->drv_data->rtc_i2c_addr == MAX77686_INVALID_I2C_ADDR) { 6998c2ecf20Sopenharmony_ci info->rtc_regmap = info->regmap; 7008c2ecf20Sopenharmony_ci goto add_rtc_irq; 7018c2ecf20Sopenharmony_ci } 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci info->rtc = devm_i2c_new_dummy_device(info->dev, parent_i2c->adapter, 7048c2ecf20Sopenharmony_ci info->drv_data->rtc_i2c_addr); 7058c2ecf20Sopenharmony_ci if (IS_ERR(info->rtc)) { 7068c2ecf20Sopenharmony_ci dev_err(info->dev, "Failed to allocate I2C device for RTC\n"); 7078c2ecf20Sopenharmony_ci return PTR_ERR(info->rtc); 7088c2ecf20Sopenharmony_ci } 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci info->rtc_regmap = devm_regmap_init_i2c(info->rtc, 7118c2ecf20Sopenharmony_ci info->drv_data->regmap_config); 7128c2ecf20Sopenharmony_ci if (IS_ERR(info->rtc_regmap)) { 7138c2ecf20Sopenharmony_ci ret = PTR_ERR(info->rtc_regmap); 7148c2ecf20Sopenharmony_ci dev_err(info->dev, "Failed to allocate RTC regmap: %d\n", ret); 7158c2ecf20Sopenharmony_ci return ret; 7168c2ecf20Sopenharmony_ci } 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ciadd_rtc_irq: 7198c2ecf20Sopenharmony_ci ret = regmap_add_irq_chip(info->rtc_regmap, info->rtc_irq, 7208c2ecf20Sopenharmony_ci IRQF_ONESHOT | IRQF_SHARED, 7218c2ecf20Sopenharmony_ci 0, info->drv_data->rtc_irq_chip, 7228c2ecf20Sopenharmony_ci &info->rtc_irq_data); 7238c2ecf20Sopenharmony_ci if (ret < 0) { 7248c2ecf20Sopenharmony_ci dev_err(info->dev, "Failed to add RTC irq chip: %d\n", ret); 7258c2ecf20Sopenharmony_ci return ret; 7268c2ecf20Sopenharmony_ci } 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci return 0; 7298c2ecf20Sopenharmony_ci} 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_cistatic int max77686_rtc_probe(struct platform_device *pdev) 7328c2ecf20Sopenharmony_ci{ 7338c2ecf20Sopenharmony_ci struct max77686_rtc_info *info; 7348c2ecf20Sopenharmony_ci const struct platform_device_id *id = platform_get_device_id(pdev); 7358c2ecf20Sopenharmony_ci int ret; 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci info = devm_kzalloc(&pdev->dev, sizeof(struct max77686_rtc_info), 7388c2ecf20Sopenharmony_ci GFP_KERNEL); 7398c2ecf20Sopenharmony_ci if (!info) 7408c2ecf20Sopenharmony_ci return -ENOMEM; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci mutex_init(&info->lock); 7438c2ecf20Sopenharmony_ci info->dev = &pdev->dev; 7448c2ecf20Sopenharmony_ci info->drv_data = (const struct max77686_rtc_driver_data *) 7458c2ecf20Sopenharmony_ci id->driver_data; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci ret = max77686_init_rtc_regmap(info); 7488c2ecf20Sopenharmony_ci if (ret < 0) 7498c2ecf20Sopenharmony_ci return ret; 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, info); 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci ret = max77686_rtc_init_reg(info); 7548c2ecf20Sopenharmony_ci if (ret < 0) { 7558c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to initialize RTC reg:%d\n", ret); 7568c2ecf20Sopenharmony_ci goto err_rtc; 7578c2ecf20Sopenharmony_ci } 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci device_init_wakeup(&pdev->dev, 1); 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci info->rtc_dev = devm_rtc_device_register(&pdev->dev, id->name, 7628c2ecf20Sopenharmony_ci &max77686_rtc_ops, THIS_MODULE); 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci if (IS_ERR(info->rtc_dev)) { 7658c2ecf20Sopenharmony_ci ret = PTR_ERR(info->rtc_dev); 7668c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret); 7678c2ecf20Sopenharmony_ci if (ret == 0) 7688c2ecf20Sopenharmony_ci ret = -EINVAL; 7698c2ecf20Sopenharmony_ci goto err_rtc; 7708c2ecf20Sopenharmony_ci } 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci info->virq = regmap_irq_get_virq(info->rtc_irq_data, 7738c2ecf20Sopenharmony_ci MAX77686_RTCIRQ_RTCA1); 7748c2ecf20Sopenharmony_ci if (info->virq <= 0) { 7758c2ecf20Sopenharmony_ci ret = -ENXIO; 7768c2ecf20Sopenharmony_ci goto err_rtc; 7778c2ecf20Sopenharmony_ci } 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci ret = request_threaded_irq(info->virq, NULL, max77686_rtc_alarm_irq, 0, 7808c2ecf20Sopenharmony_ci "rtc-alarm1", info); 7818c2ecf20Sopenharmony_ci if (ret < 0) { 7828c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n", 7838c2ecf20Sopenharmony_ci info->virq, ret); 7848c2ecf20Sopenharmony_ci goto err_rtc; 7858c2ecf20Sopenharmony_ci } 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci return 0; 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_cierr_rtc: 7908c2ecf20Sopenharmony_ci regmap_del_irq_chip(info->rtc_irq, info->rtc_irq_data); 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci return ret; 7938c2ecf20Sopenharmony_ci} 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_cistatic int max77686_rtc_remove(struct platform_device *pdev) 7968c2ecf20Sopenharmony_ci{ 7978c2ecf20Sopenharmony_ci struct max77686_rtc_info *info = platform_get_drvdata(pdev); 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci free_irq(info->virq, info); 8008c2ecf20Sopenharmony_ci regmap_del_irq_chip(info->rtc_irq, info->rtc_irq_data); 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci return 0; 8038c2ecf20Sopenharmony_ci} 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 8068c2ecf20Sopenharmony_cistatic int max77686_rtc_suspend(struct device *dev) 8078c2ecf20Sopenharmony_ci{ 8088c2ecf20Sopenharmony_ci struct max77686_rtc_info *info = dev_get_drvdata(dev); 8098c2ecf20Sopenharmony_ci int ret = 0; 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci if (device_may_wakeup(dev)) { 8128c2ecf20Sopenharmony_ci struct max77686_rtc_info *info = dev_get_drvdata(dev); 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci ret = enable_irq_wake(info->virq); 8158c2ecf20Sopenharmony_ci } 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci /* 8188c2ecf20Sopenharmony_ci * If the main IRQ (not virtual) is the parent IRQ, then it must be 8198c2ecf20Sopenharmony_ci * disabled during suspend because if it happens while suspended it 8208c2ecf20Sopenharmony_ci * will be handled before resuming I2C. 8218c2ecf20Sopenharmony_ci * 8228c2ecf20Sopenharmony_ci * Since Main IRQ is shared, all its users should disable it to be sure 8238c2ecf20Sopenharmony_ci * it won't fire while one of them is still suspended. 8248c2ecf20Sopenharmony_ci */ 8258c2ecf20Sopenharmony_ci if (!info->drv_data->rtc_irq_from_platform) 8268c2ecf20Sopenharmony_ci disable_irq(info->rtc_irq); 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci return ret; 8298c2ecf20Sopenharmony_ci} 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_cistatic int max77686_rtc_resume(struct device *dev) 8328c2ecf20Sopenharmony_ci{ 8338c2ecf20Sopenharmony_ci struct max77686_rtc_info *info = dev_get_drvdata(dev); 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci if (!info->drv_data->rtc_irq_from_platform) 8368c2ecf20Sopenharmony_ci enable_irq(info->rtc_irq); 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci if (device_may_wakeup(dev)) { 8398c2ecf20Sopenharmony_ci struct max77686_rtc_info *info = dev_get_drvdata(dev); 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci return disable_irq_wake(info->virq); 8428c2ecf20Sopenharmony_ci } 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci return 0; 8458c2ecf20Sopenharmony_ci} 8468c2ecf20Sopenharmony_ci#endif 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(max77686_rtc_pm_ops, 8498c2ecf20Sopenharmony_ci max77686_rtc_suspend, max77686_rtc_resume); 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_cistatic const struct platform_device_id rtc_id[] = { 8528c2ecf20Sopenharmony_ci { "max77686-rtc", .driver_data = (kernel_ulong_t)&max77686_drv_data, }, 8538c2ecf20Sopenharmony_ci { "max77802-rtc", .driver_data = (kernel_ulong_t)&max77802_drv_data, }, 8548c2ecf20Sopenharmony_ci { "max77620-rtc", .driver_data = (kernel_ulong_t)&max77620_drv_data, }, 8558c2ecf20Sopenharmony_ci {}, 8568c2ecf20Sopenharmony_ci}; 8578c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(platform, rtc_id); 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_cistatic struct platform_driver max77686_rtc_driver = { 8608c2ecf20Sopenharmony_ci .driver = { 8618c2ecf20Sopenharmony_ci .name = "max77686-rtc", 8628c2ecf20Sopenharmony_ci .pm = &max77686_rtc_pm_ops, 8638c2ecf20Sopenharmony_ci }, 8648c2ecf20Sopenharmony_ci .probe = max77686_rtc_probe, 8658c2ecf20Sopenharmony_ci .remove = max77686_rtc_remove, 8668c2ecf20Sopenharmony_ci .id_table = rtc_id, 8678c2ecf20Sopenharmony_ci}; 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_cimodule_platform_driver(max77686_rtc_driver); 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Maxim MAX77686 RTC driver"); 8728c2ecf20Sopenharmony_ciMODULE_AUTHOR("Chiwoong Byun <woong.byun@samsung.com>"); 8738c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 874