18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * rtc-tps65910.c -- TPS65910 Real Time Clock interface 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. 68c2ecf20Sopenharmony_ci * Author: Venu Byravarasu <vbyravarasu@nvidia.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Based on original TI driver rtc-twl.c 98c2ecf20Sopenharmony_ci * Copyright (C) 2007 MontaVista Software, Inc 108c2ecf20Sopenharmony_ci * Author: Alexandre Rusev <source@mvista.com> 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <linux/errno.h> 158c2ecf20Sopenharmony_ci#include <linux/init.h> 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci#include <linux/types.h> 188c2ecf20Sopenharmony_ci#include <linux/rtc.h> 198c2ecf20Sopenharmony_ci#include <linux/bcd.h> 208c2ecf20Sopenharmony_ci#include <linux/math64.h> 218c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 228c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 238c2ecf20Sopenharmony_ci#include <linux/mfd/tps65910.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistruct tps65910_rtc { 268c2ecf20Sopenharmony_ci struct rtc_device *rtc; 278c2ecf20Sopenharmony_ci int irq; 288c2ecf20Sopenharmony_ci}; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/* Total number of RTC registers needed to set time*/ 318c2ecf20Sopenharmony_ci#define NUM_TIME_REGS (TPS65910_YEARS - TPS65910_SECONDS + 1) 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci/* Total number of RTC registers needed to set compensation registers */ 348c2ecf20Sopenharmony_ci#define NUM_COMP_REGS (TPS65910_RTC_COMP_MSB - TPS65910_RTC_COMP_LSB + 1) 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* Min and max values supported with 'offset' interface (swapped sign) */ 378c2ecf20Sopenharmony_ci#define MIN_OFFSET (-277761) 388c2ecf20Sopenharmony_ci#define MAX_OFFSET (277778) 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/* Number of ticks per hour */ 418c2ecf20Sopenharmony_ci#define TICKS_PER_HOUR (32768 * 3600) 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/* Multiplier for ppb conversions */ 448c2ecf20Sopenharmony_ci#define PPB_MULT (1000000000LL) 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic int tps65910_rtc_alarm_irq_enable(struct device *dev, 478c2ecf20Sopenharmony_ci unsigned int enabled) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci struct tps65910 *tps = dev_get_drvdata(dev->parent); 508c2ecf20Sopenharmony_ci u8 val = 0; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci if (enabled) 538c2ecf20Sopenharmony_ci val = TPS65910_RTC_INTERRUPTS_IT_ALARM; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci return regmap_write(tps->regmap, TPS65910_RTC_INTERRUPTS, val); 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/* 598c2ecf20Sopenharmony_ci * Gets current tps65910 RTC time and date parameters. 608c2ecf20Sopenharmony_ci * 618c2ecf20Sopenharmony_ci * The RTC's time/alarm representation is not what gmtime(3) requires 628c2ecf20Sopenharmony_ci * Linux to use: 638c2ecf20Sopenharmony_ci * 648c2ecf20Sopenharmony_ci * - Months are 1..12 vs Linux 0-11 658c2ecf20Sopenharmony_ci * - Years are 0..99 vs Linux 1900..N (we assume 21st century) 668c2ecf20Sopenharmony_ci */ 678c2ecf20Sopenharmony_cistatic int tps65910_rtc_read_time(struct device *dev, struct rtc_time *tm) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci unsigned char rtc_data[NUM_TIME_REGS]; 708c2ecf20Sopenharmony_ci struct tps65910 *tps = dev_get_drvdata(dev->parent); 718c2ecf20Sopenharmony_ci int ret; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci /* Copy RTC counting registers to static registers or latches */ 748c2ecf20Sopenharmony_ci ret = regmap_update_bits(tps->regmap, TPS65910_RTC_CTRL, 758c2ecf20Sopenharmony_ci TPS65910_RTC_CTRL_GET_TIME, TPS65910_RTC_CTRL_GET_TIME); 768c2ecf20Sopenharmony_ci if (ret < 0) { 778c2ecf20Sopenharmony_ci dev_err(dev, "RTC CTRL reg update failed with err:%d\n", ret); 788c2ecf20Sopenharmony_ci return ret; 798c2ecf20Sopenharmony_ci } 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci ret = regmap_bulk_read(tps->regmap, TPS65910_SECONDS, rtc_data, 828c2ecf20Sopenharmony_ci NUM_TIME_REGS); 838c2ecf20Sopenharmony_ci if (ret < 0) { 848c2ecf20Sopenharmony_ci dev_err(dev, "reading from RTC failed with err:%d\n", ret); 858c2ecf20Sopenharmony_ci return ret; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci tm->tm_sec = bcd2bin(rtc_data[0]); 898c2ecf20Sopenharmony_ci tm->tm_min = bcd2bin(rtc_data[1]); 908c2ecf20Sopenharmony_ci tm->tm_hour = bcd2bin(rtc_data[2]); 918c2ecf20Sopenharmony_ci tm->tm_mday = bcd2bin(rtc_data[3]); 928c2ecf20Sopenharmony_ci tm->tm_mon = bcd2bin(rtc_data[4]) - 1; 938c2ecf20Sopenharmony_ci tm->tm_year = bcd2bin(rtc_data[5]) + 100; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci return ret; 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic int tps65910_rtc_set_time(struct device *dev, struct rtc_time *tm) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci unsigned char rtc_data[NUM_TIME_REGS]; 1018c2ecf20Sopenharmony_ci struct tps65910 *tps = dev_get_drvdata(dev->parent); 1028c2ecf20Sopenharmony_ci int ret; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci rtc_data[0] = bin2bcd(tm->tm_sec); 1058c2ecf20Sopenharmony_ci rtc_data[1] = bin2bcd(tm->tm_min); 1068c2ecf20Sopenharmony_ci rtc_data[2] = bin2bcd(tm->tm_hour); 1078c2ecf20Sopenharmony_ci rtc_data[3] = bin2bcd(tm->tm_mday); 1088c2ecf20Sopenharmony_ci rtc_data[4] = bin2bcd(tm->tm_mon + 1); 1098c2ecf20Sopenharmony_ci rtc_data[5] = bin2bcd(tm->tm_year - 100); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci /* Stop RTC while updating the RTC time registers */ 1128c2ecf20Sopenharmony_ci ret = regmap_update_bits(tps->regmap, TPS65910_RTC_CTRL, 1138c2ecf20Sopenharmony_ci TPS65910_RTC_CTRL_STOP_RTC, 0); 1148c2ecf20Sopenharmony_ci if (ret < 0) { 1158c2ecf20Sopenharmony_ci dev_err(dev, "RTC stop failed with err:%d\n", ret); 1168c2ecf20Sopenharmony_ci return ret; 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci /* update all the time registers in one shot */ 1208c2ecf20Sopenharmony_ci ret = regmap_bulk_write(tps->regmap, TPS65910_SECONDS, rtc_data, 1218c2ecf20Sopenharmony_ci NUM_TIME_REGS); 1228c2ecf20Sopenharmony_ci if (ret < 0) { 1238c2ecf20Sopenharmony_ci dev_err(dev, "rtc_set_time error %d\n", ret); 1248c2ecf20Sopenharmony_ci return ret; 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci /* Start back RTC */ 1288c2ecf20Sopenharmony_ci ret = regmap_update_bits(tps->regmap, TPS65910_RTC_CTRL, 1298c2ecf20Sopenharmony_ci TPS65910_RTC_CTRL_STOP_RTC, 1); 1308c2ecf20Sopenharmony_ci if (ret < 0) 1318c2ecf20Sopenharmony_ci dev_err(dev, "RTC start failed with err:%d\n", ret); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci return ret; 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci/* 1378c2ecf20Sopenharmony_ci * Gets current tps65910 RTC alarm time. 1388c2ecf20Sopenharmony_ci */ 1398c2ecf20Sopenharmony_cistatic int tps65910_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci unsigned char alarm_data[NUM_TIME_REGS]; 1428c2ecf20Sopenharmony_ci u32 int_val; 1438c2ecf20Sopenharmony_ci struct tps65910 *tps = dev_get_drvdata(dev->parent); 1448c2ecf20Sopenharmony_ci int ret; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci ret = regmap_bulk_read(tps->regmap, TPS65910_ALARM_SECONDS, alarm_data, 1478c2ecf20Sopenharmony_ci NUM_TIME_REGS); 1488c2ecf20Sopenharmony_ci if (ret < 0) { 1498c2ecf20Sopenharmony_ci dev_err(dev, "rtc_read_alarm error %d\n", ret); 1508c2ecf20Sopenharmony_ci return ret; 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci alm->time.tm_sec = bcd2bin(alarm_data[0]); 1548c2ecf20Sopenharmony_ci alm->time.tm_min = bcd2bin(alarm_data[1]); 1558c2ecf20Sopenharmony_ci alm->time.tm_hour = bcd2bin(alarm_data[2]); 1568c2ecf20Sopenharmony_ci alm->time.tm_mday = bcd2bin(alarm_data[3]); 1578c2ecf20Sopenharmony_ci alm->time.tm_mon = bcd2bin(alarm_data[4]) - 1; 1588c2ecf20Sopenharmony_ci alm->time.tm_year = bcd2bin(alarm_data[5]) + 100; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci ret = regmap_read(tps->regmap, TPS65910_RTC_INTERRUPTS, &int_val); 1618c2ecf20Sopenharmony_ci if (ret < 0) 1628c2ecf20Sopenharmony_ci return ret; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci if (int_val & TPS65910_RTC_INTERRUPTS_IT_ALARM) 1658c2ecf20Sopenharmony_ci alm->enabled = 1; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci return ret; 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic int tps65910_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci unsigned char alarm_data[NUM_TIME_REGS]; 1738c2ecf20Sopenharmony_ci struct tps65910 *tps = dev_get_drvdata(dev->parent); 1748c2ecf20Sopenharmony_ci int ret; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci ret = tps65910_rtc_alarm_irq_enable(dev, 0); 1778c2ecf20Sopenharmony_ci if (ret) 1788c2ecf20Sopenharmony_ci return ret; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci alarm_data[0] = bin2bcd(alm->time.tm_sec); 1818c2ecf20Sopenharmony_ci alarm_data[1] = bin2bcd(alm->time.tm_min); 1828c2ecf20Sopenharmony_ci alarm_data[2] = bin2bcd(alm->time.tm_hour); 1838c2ecf20Sopenharmony_ci alarm_data[3] = bin2bcd(alm->time.tm_mday); 1848c2ecf20Sopenharmony_ci alarm_data[4] = bin2bcd(alm->time.tm_mon + 1); 1858c2ecf20Sopenharmony_ci alarm_data[5] = bin2bcd(alm->time.tm_year - 100); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci /* update all the alarm registers in one shot */ 1888c2ecf20Sopenharmony_ci ret = regmap_bulk_write(tps->regmap, TPS65910_ALARM_SECONDS, 1898c2ecf20Sopenharmony_ci alarm_data, NUM_TIME_REGS); 1908c2ecf20Sopenharmony_ci if (ret) { 1918c2ecf20Sopenharmony_ci dev_err(dev, "rtc_set_alarm error %d\n", ret); 1928c2ecf20Sopenharmony_ci return ret; 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci if (alm->enabled) 1968c2ecf20Sopenharmony_ci ret = tps65910_rtc_alarm_irq_enable(dev, 1); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci return ret; 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistatic int tps65910_rtc_set_calibration(struct device *dev, int calibration) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci unsigned char comp_data[NUM_COMP_REGS]; 2048c2ecf20Sopenharmony_ci struct tps65910 *tps = dev_get_drvdata(dev->parent); 2058c2ecf20Sopenharmony_ci s16 value; 2068c2ecf20Sopenharmony_ci int ret; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci /* 2098c2ecf20Sopenharmony_ci * TPS65910 uses two's complement 16 bit value for compensation for RTC 2108c2ecf20Sopenharmony_ci * crystal inaccuracies. One time every hour when seconds counter 2118c2ecf20Sopenharmony_ci * increments from 0 to 1 compensation value will be added to internal 2128c2ecf20Sopenharmony_ci * RTC counter value. 2138c2ecf20Sopenharmony_ci * 2148c2ecf20Sopenharmony_ci * Compensation value 0x7FFF is prohibited value. 2158c2ecf20Sopenharmony_ci * 2168c2ecf20Sopenharmony_ci * Valid range for compensation value: [-32768 .. 32766] 2178c2ecf20Sopenharmony_ci */ 2188c2ecf20Sopenharmony_ci if ((calibration < -32768) || (calibration > 32766)) { 2198c2ecf20Sopenharmony_ci dev_err(dev, "RTC calibration value out of range: %d\n", 2208c2ecf20Sopenharmony_ci calibration); 2218c2ecf20Sopenharmony_ci return -EINVAL; 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci value = (s16)calibration; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci comp_data[0] = (u16)value & 0xFF; 2278c2ecf20Sopenharmony_ci comp_data[1] = ((u16)value >> 8) & 0xFF; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci /* Update all the compensation registers in one shot */ 2308c2ecf20Sopenharmony_ci ret = regmap_bulk_write(tps->regmap, TPS65910_RTC_COMP_LSB, 2318c2ecf20Sopenharmony_ci comp_data, NUM_COMP_REGS); 2328c2ecf20Sopenharmony_ci if (ret < 0) { 2338c2ecf20Sopenharmony_ci dev_err(dev, "rtc_set_calibration error: %d\n", ret); 2348c2ecf20Sopenharmony_ci return ret; 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci /* Enable automatic compensation */ 2388c2ecf20Sopenharmony_ci ret = regmap_update_bits(tps->regmap, TPS65910_RTC_CTRL, 2398c2ecf20Sopenharmony_ci TPS65910_RTC_CTRL_AUTO_COMP, TPS65910_RTC_CTRL_AUTO_COMP); 2408c2ecf20Sopenharmony_ci if (ret < 0) 2418c2ecf20Sopenharmony_ci dev_err(dev, "auto_comp enable failed with error: %d\n", ret); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci return ret; 2448c2ecf20Sopenharmony_ci} 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cistatic int tps65910_rtc_get_calibration(struct device *dev, int *calibration) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci unsigned char comp_data[NUM_COMP_REGS]; 2498c2ecf20Sopenharmony_ci struct tps65910 *tps = dev_get_drvdata(dev->parent); 2508c2ecf20Sopenharmony_ci unsigned int ctrl; 2518c2ecf20Sopenharmony_ci u16 value; 2528c2ecf20Sopenharmony_ci int ret; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci ret = regmap_read(tps->regmap, TPS65910_RTC_CTRL, &ctrl); 2558c2ecf20Sopenharmony_ci if (ret < 0) 2568c2ecf20Sopenharmony_ci return ret; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci /* If automatic compensation is not enabled report back zero */ 2598c2ecf20Sopenharmony_ci if (!(ctrl & TPS65910_RTC_CTRL_AUTO_COMP)) { 2608c2ecf20Sopenharmony_ci *calibration = 0; 2618c2ecf20Sopenharmony_ci return 0; 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci ret = regmap_bulk_read(tps->regmap, TPS65910_RTC_COMP_LSB, comp_data, 2658c2ecf20Sopenharmony_ci NUM_COMP_REGS); 2668c2ecf20Sopenharmony_ci if (ret < 0) { 2678c2ecf20Sopenharmony_ci dev_err(dev, "rtc_get_calibration error: %d\n", ret); 2688c2ecf20Sopenharmony_ci return ret; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci value = (u16)comp_data[0] | ((u16)comp_data[1] << 8); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci *calibration = (s16)value; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci return 0; 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_cistatic int tps65910_read_offset(struct device *dev, long *offset) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci int calibration; 2818c2ecf20Sopenharmony_ci s64 tmp; 2828c2ecf20Sopenharmony_ci int ret; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci ret = tps65910_rtc_get_calibration(dev, &calibration); 2858c2ecf20Sopenharmony_ci if (ret < 0) 2868c2ecf20Sopenharmony_ci return ret; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci /* Convert from RTC calibration register format to ppb format */ 2898c2ecf20Sopenharmony_ci tmp = calibration * (s64)PPB_MULT; 2908c2ecf20Sopenharmony_ci if (tmp < 0) 2918c2ecf20Sopenharmony_ci tmp -= TICKS_PER_HOUR / 2LL; 2928c2ecf20Sopenharmony_ci else 2938c2ecf20Sopenharmony_ci tmp += TICKS_PER_HOUR / 2LL; 2948c2ecf20Sopenharmony_ci tmp = div_s64(tmp, TICKS_PER_HOUR); 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci /* Offset value operates in negative way, so swap sign */ 2978c2ecf20Sopenharmony_ci *offset = (long)-tmp; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci return 0; 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistatic int tps65910_set_offset(struct device *dev, long offset) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci int calibration; 3058c2ecf20Sopenharmony_ci s64 tmp; 3068c2ecf20Sopenharmony_ci int ret; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci /* Make sure offset value is within supported range */ 3098c2ecf20Sopenharmony_ci if (offset < MIN_OFFSET || offset > MAX_OFFSET) 3108c2ecf20Sopenharmony_ci return -ERANGE; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci /* Convert from ppb format to RTC calibration register format */ 3138c2ecf20Sopenharmony_ci tmp = offset * (s64)TICKS_PER_HOUR; 3148c2ecf20Sopenharmony_ci if (tmp < 0) 3158c2ecf20Sopenharmony_ci tmp -= PPB_MULT / 2LL; 3168c2ecf20Sopenharmony_ci else 3178c2ecf20Sopenharmony_ci tmp += PPB_MULT / 2LL; 3188c2ecf20Sopenharmony_ci tmp = div_s64(tmp, PPB_MULT); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci /* Offset value operates in negative way, so swap sign */ 3218c2ecf20Sopenharmony_ci calibration = (int)-tmp; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci ret = tps65910_rtc_set_calibration(dev, calibration); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci return ret; 3268c2ecf20Sopenharmony_ci} 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_cistatic irqreturn_t tps65910_rtc_interrupt(int irq, void *rtc) 3298c2ecf20Sopenharmony_ci{ 3308c2ecf20Sopenharmony_ci struct device *dev = rtc; 3318c2ecf20Sopenharmony_ci unsigned long events = 0; 3328c2ecf20Sopenharmony_ci struct tps65910 *tps = dev_get_drvdata(dev->parent); 3338c2ecf20Sopenharmony_ci struct tps65910_rtc *tps_rtc = dev_get_drvdata(dev); 3348c2ecf20Sopenharmony_ci int ret; 3358c2ecf20Sopenharmony_ci u32 rtc_reg; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci ret = regmap_read(tps->regmap, TPS65910_RTC_STATUS, &rtc_reg); 3388c2ecf20Sopenharmony_ci if (ret) 3398c2ecf20Sopenharmony_ci return IRQ_NONE; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci if (rtc_reg & TPS65910_RTC_STATUS_ALARM) 3428c2ecf20Sopenharmony_ci events = RTC_IRQF | RTC_AF; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci ret = regmap_write(tps->regmap, TPS65910_RTC_STATUS, rtc_reg); 3458c2ecf20Sopenharmony_ci if (ret) 3468c2ecf20Sopenharmony_ci return IRQ_NONE; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci /* Notify RTC core on event */ 3498c2ecf20Sopenharmony_ci rtc_update_irq(tps_rtc->rtc, 1, events); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic const struct rtc_class_ops tps65910_rtc_ops = { 3558c2ecf20Sopenharmony_ci .read_time = tps65910_rtc_read_time, 3568c2ecf20Sopenharmony_ci .set_time = tps65910_rtc_set_time, 3578c2ecf20Sopenharmony_ci .read_alarm = tps65910_rtc_read_alarm, 3588c2ecf20Sopenharmony_ci .set_alarm = tps65910_rtc_set_alarm, 3598c2ecf20Sopenharmony_ci .alarm_irq_enable = tps65910_rtc_alarm_irq_enable, 3608c2ecf20Sopenharmony_ci .read_offset = tps65910_read_offset, 3618c2ecf20Sopenharmony_ci .set_offset = tps65910_set_offset, 3628c2ecf20Sopenharmony_ci}; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_cistatic const struct rtc_class_ops tps65910_rtc_ops_noirq = { 3658c2ecf20Sopenharmony_ci .read_time = tps65910_rtc_read_time, 3668c2ecf20Sopenharmony_ci .set_time = tps65910_rtc_set_time, 3678c2ecf20Sopenharmony_ci .read_offset = tps65910_read_offset, 3688c2ecf20Sopenharmony_ci .set_offset = tps65910_set_offset, 3698c2ecf20Sopenharmony_ci}; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_cistatic int tps65910_rtc_probe(struct platform_device *pdev) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci struct tps65910 *tps65910 = NULL; 3748c2ecf20Sopenharmony_ci struct tps65910_rtc *tps_rtc = NULL; 3758c2ecf20Sopenharmony_ci int ret; 3768c2ecf20Sopenharmony_ci int irq; 3778c2ecf20Sopenharmony_ci u32 rtc_reg; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci tps65910 = dev_get_drvdata(pdev->dev.parent); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci tps_rtc = devm_kzalloc(&pdev->dev, sizeof(struct tps65910_rtc), 3828c2ecf20Sopenharmony_ci GFP_KERNEL); 3838c2ecf20Sopenharmony_ci if (!tps_rtc) 3848c2ecf20Sopenharmony_ci return -ENOMEM; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci tps_rtc->rtc = devm_rtc_allocate_device(&pdev->dev); 3878c2ecf20Sopenharmony_ci if (IS_ERR(tps_rtc->rtc)) 3888c2ecf20Sopenharmony_ci return PTR_ERR(tps_rtc->rtc); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci /* Clear pending interrupts */ 3918c2ecf20Sopenharmony_ci ret = regmap_read(tps65910->regmap, TPS65910_RTC_STATUS, &rtc_reg); 3928c2ecf20Sopenharmony_ci if (ret < 0) 3938c2ecf20Sopenharmony_ci return ret; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci ret = regmap_write(tps65910->regmap, TPS65910_RTC_STATUS, rtc_reg); 3968c2ecf20Sopenharmony_ci if (ret < 0) 3978c2ecf20Sopenharmony_ci return ret; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "Enabling rtc-tps65910.\n"); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci /* Enable RTC digital power domain */ 4028c2ecf20Sopenharmony_ci ret = regmap_update_bits(tps65910->regmap, TPS65910_DEVCTRL, 4038c2ecf20Sopenharmony_ci DEVCTRL_RTC_PWDN_MASK, 0 << DEVCTRL_RTC_PWDN_SHIFT); 4048c2ecf20Sopenharmony_ci if (ret < 0) 4058c2ecf20Sopenharmony_ci return ret; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci rtc_reg = TPS65910_RTC_CTRL_STOP_RTC; 4088c2ecf20Sopenharmony_ci ret = regmap_write(tps65910->regmap, TPS65910_RTC_CTRL, rtc_reg); 4098c2ecf20Sopenharmony_ci if (ret < 0) 4108c2ecf20Sopenharmony_ci return ret; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, tps_rtc); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 4158c2ecf20Sopenharmony_ci if (irq <= 0) { 4168c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "Wake up is not possible as irq = %d\n", 4178c2ecf20Sopenharmony_ci irq); 4188c2ecf20Sopenharmony_ci return -ENXIO; 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, 4228c2ecf20Sopenharmony_ci tps65910_rtc_interrupt, IRQF_TRIGGER_LOW, 4238c2ecf20Sopenharmony_ci dev_name(&pdev->dev), &pdev->dev); 4248c2ecf20Sopenharmony_ci if (ret < 0) 4258c2ecf20Sopenharmony_ci irq = -1; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci tps_rtc->irq = irq; 4288c2ecf20Sopenharmony_ci if (irq != -1) { 4298c2ecf20Sopenharmony_ci device_set_wakeup_capable(&pdev->dev, 1); 4308c2ecf20Sopenharmony_ci tps_rtc->rtc->ops = &tps65910_rtc_ops; 4318c2ecf20Sopenharmony_ci } else 4328c2ecf20Sopenharmony_ci tps_rtc->rtc->ops = &tps65910_rtc_ops_noirq; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci tps_rtc->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; 4358c2ecf20Sopenharmony_ci tps_rtc->rtc->range_max = RTC_TIMESTAMP_END_2099; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci return rtc_register_device(tps_rtc->rtc); 4388c2ecf20Sopenharmony_ci} 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 4418c2ecf20Sopenharmony_cistatic int tps65910_rtc_suspend(struct device *dev) 4428c2ecf20Sopenharmony_ci{ 4438c2ecf20Sopenharmony_ci struct tps65910_rtc *tps_rtc = dev_get_drvdata(dev); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci if (device_may_wakeup(dev)) 4468c2ecf20Sopenharmony_ci enable_irq_wake(tps_rtc->irq); 4478c2ecf20Sopenharmony_ci return 0; 4488c2ecf20Sopenharmony_ci} 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_cistatic int tps65910_rtc_resume(struct device *dev) 4518c2ecf20Sopenharmony_ci{ 4528c2ecf20Sopenharmony_ci struct tps65910_rtc *tps_rtc = dev_get_drvdata(dev); 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci if (device_may_wakeup(dev)) 4558c2ecf20Sopenharmony_ci disable_irq_wake(tps_rtc->irq); 4568c2ecf20Sopenharmony_ci return 0; 4578c2ecf20Sopenharmony_ci} 4588c2ecf20Sopenharmony_ci#endif 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(tps65910_rtc_pm_ops, tps65910_rtc_suspend, 4618c2ecf20Sopenharmony_ci tps65910_rtc_resume); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_cistatic struct platform_driver tps65910_rtc_driver = { 4648c2ecf20Sopenharmony_ci .probe = tps65910_rtc_probe, 4658c2ecf20Sopenharmony_ci .driver = { 4668c2ecf20Sopenharmony_ci .name = "tps65910-rtc", 4678c2ecf20Sopenharmony_ci .pm = &tps65910_rtc_pm_ops, 4688c2ecf20Sopenharmony_ci }, 4698c2ecf20Sopenharmony_ci}; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_cimodule_platform_driver(tps65910_rtc_driver); 4728c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:tps65910-rtc"); 4738c2ecf20Sopenharmony_ciMODULE_AUTHOR("Venu Byravarasu <vbyravarasu@nvidia.com>"); 4748c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 475