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