xref: /openbmc/linux/drivers/rtc/rtc-rc5t583.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
19952f691SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
290829c08SVenu Byravarasu /*
390829c08SVenu Byravarasu  * rtc-rc5t583.c -- RICOH RC5T583 Real Time Clock
490829c08SVenu Byravarasu  *
590829c08SVenu Byravarasu  * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved.
690829c08SVenu Byravarasu  * Author: Venu Byravarasu <vbyravarasu@nvidia.com>
79952f691SThomas Gleixner  */
890829c08SVenu Byravarasu 
990829c08SVenu Byravarasu #include <linux/kernel.h>
1090829c08SVenu Byravarasu #include <linux/errno.h>
1190829c08SVenu Byravarasu #include <linux/init.h>
1290829c08SVenu Byravarasu #include <linux/module.h>
1390829c08SVenu Byravarasu #include <linux/types.h>
1490829c08SVenu Byravarasu #include <linux/rtc.h>
1590829c08SVenu Byravarasu #include <linux/bcd.h>
1690829c08SVenu Byravarasu #include <linux/platform_device.h>
1790829c08SVenu Byravarasu #include <linux/interrupt.h>
1890829c08SVenu Byravarasu #include <linux/mfd/rc5t583.h>
1990829c08SVenu Byravarasu 
2090829c08SVenu Byravarasu struct rc5t583_rtc {
2190829c08SVenu Byravarasu 	struct rtc_device	*rtc;
2290829c08SVenu Byravarasu 	/* To store the list of enabled interrupts, during system suspend */
2390829c08SVenu Byravarasu 	u32 irqen;
2490829c08SVenu Byravarasu };
2590829c08SVenu Byravarasu 
2690829c08SVenu Byravarasu /* Total number of RTC registers needed to set time*/
2790829c08SVenu Byravarasu #define NUM_TIME_REGS	(RC5T583_RTC_YEAR - RC5T583_RTC_SEC + 1)
2890829c08SVenu Byravarasu 
2990829c08SVenu Byravarasu /* Total number of RTC registers needed to set Y-Alarm*/
3090829c08SVenu Byravarasu #define NUM_YAL_REGS	(RC5T583_RTC_AY_YEAR - RC5T583_RTC_AY_MIN + 1)
3190829c08SVenu Byravarasu 
3290829c08SVenu Byravarasu /* Set Y-Alarm interrupt */
3390829c08SVenu Byravarasu #define SET_YAL BIT(5)
3490829c08SVenu Byravarasu 
3590829c08SVenu Byravarasu /* Get Y-Alarm interrupt status*/
3690829c08SVenu Byravarasu #define GET_YAL_STATUS BIT(3)
3790829c08SVenu Byravarasu 
rc5t583_rtc_alarm_irq_enable(struct device * dev,unsigned enabled)3890829c08SVenu Byravarasu static int rc5t583_rtc_alarm_irq_enable(struct device *dev, unsigned enabled)
3990829c08SVenu Byravarasu {
4090829c08SVenu Byravarasu 	struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent);
4190829c08SVenu Byravarasu 	u8 val;
4290829c08SVenu Byravarasu 
4390829c08SVenu Byravarasu 	/* Set Y-Alarm, based on 'enabled' */
4490829c08SVenu Byravarasu 	val = enabled ? SET_YAL : 0;
4590829c08SVenu Byravarasu 
4690829c08SVenu Byravarasu 	return regmap_update_bits(rc5t583->regmap, RC5T583_RTC_CTL1, SET_YAL,
4790829c08SVenu Byravarasu 		val);
4890829c08SVenu Byravarasu }
4990829c08SVenu Byravarasu 
5090829c08SVenu Byravarasu /*
5190829c08SVenu Byravarasu  * Gets current rc5t583 RTC time and date parameters.
5290829c08SVenu Byravarasu  *
5390829c08SVenu Byravarasu  * The RTC's time/alarm representation is not what gmtime(3) requires
5490829c08SVenu Byravarasu  * Linux to use:
5590829c08SVenu Byravarasu  *
5690829c08SVenu Byravarasu  *  - Months are 1..12 vs Linux 0-11
5790829c08SVenu Byravarasu  *  - Years are 0..99 vs Linux 1900..N (we assume 21st century)
5890829c08SVenu Byravarasu  */
rc5t583_rtc_read_time(struct device * dev,struct rtc_time * tm)5990829c08SVenu Byravarasu static int rc5t583_rtc_read_time(struct device *dev, struct rtc_time *tm)
6090829c08SVenu Byravarasu {
6190829c08SVenu Byravarasu 	struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent);
6290829c08SVenu Byravarasu 	u8 rtc_data[NUM_TIME_REGS];
6390829c08SVenu Byravarasu 	int ret;
6490829c08SVenu Byravarasu 
6590829c08SVenu Byravarasu 	ret = regmap_bulk_read(rc5t583->regmap, RC5T583_RTC_SEC, rtc_data,
6690829c08SVenu Byravarasu 		NUM_TIME_REGS);
6790829c08SVenu Byravarasu 	if (ret < 0) {
6890829c08SVenu Byravarasu 		dev_err(dev, "RTC read time failed with err:%d\n", ret);
6990829c08SVenu Byravarasu 		return ret;
7090829c08SVenu Byravarasu 	}
7190829c08SVenu Byravarasu 
7290829c08SVenu Byravarasu 	tm->tm_sec = bcd2bin(rtc_data[0]);
7390829c08SVenu Byravarasu 	tm->tm_min = bcd2bin(rtc_data[1]);
7490829c08SVenu Byravarasu 	tm->tm_hour = bcd2bin(rtc_data[2]);
7590829c08SVenu Byravarasu 	tm->tm_wday = bcd2bin(rtc_data[3]);
7690829c08SVenu Byravarasu 	tm->tm_mday = bcd2bin(rtc_data[4]);
7790829c08SVenu Byravarasu 	tm->tm_mon = bcd2bin(rtc_data[5]) - 1;
7890829c08SVenu Byravarasu 	tm->tm_year = bcd2bin(rtc_data[6]) + 100;
7990829c08SVenu Byravarasu 
8090829c08SVenu Byravarasu 	return ret;
8190829c08SVenu Byravarasu }
8290829c08SVenu Byravarasu 
rc5t583_rtc_set_time(struct device * dev,struct rtc_time * tm)8390829c08SVenu Byravarasu static int rc5t583_rtc_set_time(struct device *dev, struct rtc_time *tm)
8490829c08SVenu Byravarasu {
8590829c08SVenu Byravarasu 	struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent);
8690829c08SVenu Byravarasu 	unsigned char rtc_data[NUM_TIME_REGS];
8790829c08SVenu Byravarasu 	int ret;
8890829c08SVenu Byravarasu 
8990829c08SVenu Byravarasu 	rtc_data[0] = bin2bcd(tm->tm_sec);
9090829c08SVenu Byravarasu 	rtc_data[1] = bin2bcd(tm->tm_min);
9190829c08SVenu Byravarasu 	rtc_data[2] = bin2bcd(tm->tm_hour);
9290829c08SVenu Byravarasu 	rtc_data[3] = bin2bcd(tm->tm_wday);
9390829c08SVenu Byravarasu 	rtc_data[4] = bin2bcd(tm->tm_mday);
9490829c08SVenu Byravarasu 	rtc_data[5] = bin2bcd(tm->tm_mon + 1);
9590829c08SVenu Byravarasu 	rtc_data[6] = bin2bcd(tm->tm_year - 100);
9690829c08SVenu Byravarasu 
9790829c08SVenu Byravarasu 	ret = regmap_bulk_write(rc5t583->regmap, RC5T583_RTC_SEC, rtc_data,
9890829c08SVenu Byravarasu 		NUM_TIME_REGS);
9990829c08SVenu Byravarasu 	if (ret < 0) {
10090829c08SVenu Byravarasu 		dev_err(dev, "RTC set time failed with error %d\n", ret);
10190829c08SVenu Byravarasu 		return ret;
10290829c08SVenu Byravarasu 	}
10390829c08SVenu Byravarasu 
10490829c08SVenu Byravarasu 	return ret;
10590829c08SVenu Byravarasu }
10690829c08SVenu Byravarasu 
rc5t583_rtc_read_alarm(struct device * dev,struct rtc_wkalrm * alm)10790829c08SVenu Byravarasu static int rc5t583_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
10890829c08SVenu Byravarasu {
10990829c08SVenu Byravarasu 	struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent);
11090829c08SVenu Byravarasu 	unsigned char alarm_data[NUM_YAL_REGS];
11190829c08SVenu Byravarasu 	u32 interrupt_enable;
11290829c08SVenu Byravarasu 	int ret;
11390829c08SVenu Byravarasu 
11490829c08SVenu Byravarasu 	ret = regmap_bulk_read(rc5t583->regmap, RC5T583_RTC_AY_MIN, alarm_data,
11590829c08SVenu Byravarasu 		NUM_YAL_REGS);
11690829c08SVenu Byravarasu 	if (ret < 0) {
11790829c08SVenu Byravarasu 		dev_err(dev, "rtc_read_alarm error %d\n", ret);
11890829c08SVenu Byravarasu 		return ret;
11990829c08SVenu Byravarasu 	}
12090829c08SVenu Byravarasu 
121d2c92705SUwe Kleine-König 	alm->time.tm_sec = 0;
12290829c08SVenu Byravarasu 	alm->time.tm_min = bcd2bin(alarm_data[0]);
12390829c08SVenu Byravarasu 	alm->time.tm_hour = bcd2bin(alarm_data[1]);
12490829c08SVenu Byravarasu 	alm->time.tm_mday = bcd2bin(alarm_data[2]);
12590829c08SVenu Byravarasu 	alm->time.tm_mon = bcd2bin(alarm_data[3]) - 1;
12690829c08SVenu Byravarasu 	alm->time.tm_year = bcd2bin(alarm_data[4]) + 100;
12790829c08SVenu Byravarasu 
12890829c08SVenu Byravarasu 	ret = regmap_read(rc5t583->regmap, RC5T583_RTC_CTL1, &interrupt_enable);
12990829c08SVenu Byravarasu 	if (ret < 0)
13090829c08SVenu Byravarasu 		return ret;
13190829c08SVenu Byravarasu 
13290829c08SVenu Byravarasu 	/* check if YALE is set */
13390829c08SVenu Byravarasu 	if (interrupt_enable & SET_YAL)
13490829c08SVenu Byravarasu 		alm->enabled = 1;
13590829c08SVenu Byravarasu 
13690829c08SVenu Byravarasu 	return ret;
13790829c08SVenu Byravarasu }
13890829c08SVenu Byravarasu 
rc5t583_rtc_set_alarm(struct device * dev,struct rtc_wkalrm * alm)13990829c08SVenu Byravarasu static int rc5t583_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
14090829c08SVenu Byravarasu {
14190829c08SVenu Byravarasu 	struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent);
14290829c08SVenu Byravarasu 	unsigned char alarm_data[NUM_YAL_REGS];
14390829c08SVenu Byravarasu 	int ret;
14490829c08SVenu Byravarasu 
14590829c08SVenu Byravarasu 	ret = rc5t583_rtc_alarm_irq_enable(dev, 0);
14690829c08SVenu Byravarasu 	if (ret)
14790829c08SVenu Byravarasu 		return ret;
14890829c08SVenu Byravarasu 
14990829c08SVenu Byravarasu 	alarm_data[0] = bin2bcd(alm->time.tm_min);
15090829c08SVenu Byravarasu 	alarm_data[1] = bin2bcd(alm->time.tm_hour);
15190829c08SVenu Byravarasu 	alarm_data[2] = bin2bcd(alm->time.tm_mday);
15290829c08SVenu Byravarasu 	alarm_data[3] = bin2bcd(alm->time.tm_mon + 1);
15390829c08SVenu Byravarasu 	alarm_data[4] = bin2bcd(alm->time.tm_year - 100);
15490829c08SVenu Byravarasu 
15590829c08SVenu Byravarasu 	ret = regmap_bulk_write(rc5t583->regmap, RC5T583_RTC_AY_MIN, alarm_data,
15690829c08SVenu Byravarasu 		NUM_YAL_REGS);
15790829c08SVenu Byravarasu 	if (ret) {
15890829c08SVenu Byravarasu 		dev_err(dev, "rtc_set_alarm error %d\n", ret);
15990829c08SVenu Byravarasu 		return ret;
16090829c08SVenu Byravarasu 	}
16190829c08SVenu Byravarasu 
16290829c08SVenu Byravarasu 	if (alm->enabled)
16390829c08SVenu Byravarasu 		ret = rc5t583_rtc_alarm_irq_enable(dev, 1);
16490829c08SVenu Byravarasu 
16590829c08SVenu Byravarasu 	return ret;
16690829c08SVenu Byravarasu }
16790829c08SVenu Byravarasu 
rc5t583_rtc_interrupt(int irq,void * rtc)16890829c08SVenu Byravarasu static irqreturn_t rc5t583_rtc_interrupt(int irq, void *rtc)
16990829c08SVenu Byravarasu {
17090829c08SVenu Byravarasu 	struct device *dev = rtc;
17190829c08SVenu Byravarasu 	struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent);
17290829c08SVenu Byravarasu 	struct rc5t583_rtc *rc5t583_rtc = dev_get_drvdata(dev);
17390829c08SVenu Byravarasu 	unsigned long events = 0;
17490829c08SVenu Byravarasu 	int ret;
17590829c08SVenu Byravarasu 	u32 rtc_reg;
17690829c08SVenu Byravarasu 
17790829c08SVenu Byravarasu 	ret = regmap_read(rc5t583->regmap, RC5T583_RTC_CTL2, &rtc_reg);
17890829c08SVenu Byravarasu 	if (ret < 0)
17990829c08SVenu Byravarasu 		return IRQ_NONE;
18090829c08SVenu Byravarasu 
18190829c08SVenu Byravarasu 	if (rtc_reg & GET_YAL_STATUS) {
18290829c08SVenu Byravarasu 		events = RTC_IRQF | RTC_AF;
18390829c08SVenu Byravarasu 		/* clear pending Y-alarm interrupt bit */
18490829c08SVenu Byravarasu 		rtc_reg &= ~GET_YAL_STATUS;
18590829c08SVenu Byravarasu 	}
18690829c08SVenu Byravarasu 
18790829c08SVenu Byravarasu 	ret = regmap_write(rc5t583->regmap, RC5T583_RTC_CTL2, rtc_reg);
18890829c08SVenu Byravarasu 	if (ret)
18990829c08SVenu Byravarasu 		return IRQ_NONE;
19090829c08SVenu Byravarasu 
19190829c08SVenu Byravarasu 	/* Notify RTC core on event */
19290829c08SVenu Byravarasu 	rtc_update_irq(rc5t583_rtc->rtc, 1, events);
19390829c08SVenu Byravarasu 
19490829c08SVenu Byravarasu 	return IRQ_HANDLED;
19590829c08SVenu Byravarasu }
19690829c08SVenu Byravarasu 
19790829c08SVenu Byravarasu static const struct rtc_class_ops rc5t583_rtc_ops = {
19890829c08SVenu Byravarasu 	.read_time	= rc5t583_rtc_read_time,
19990829c08SVenu Byravarasu 	.set_time	= rc5t583_rtc_set_time,
20090829c08SVenu Byravarasu 	.read_alarm	= rc5t583_rtc_read_alarm,
20190829c08SVenu Byravarasu 	.set_alarm	= rc5t583_rtc_set_alarm,
20290829c08SVenu Byravarasu 	.alarm_irq_enable = rc5t583_rtc_alarm_irq_enable,
20390829c08SVenu Byravarasu };
20490829c08SVenu Byravarasu 
rc5t583_rtc_probe(struct platform_device * pdev)2055a167f45SGreg Kroah-Hartman static int rc5t583_rtc_probe(struct platform_device *pdev)
20690829c08SVenu Byravarasu {
20790829c08SVenu Byravarasu 	struct rc5t583 *rc5t583 = dev_get_drvdata(pdev->dev.parent);
20890829c08SVenu Byravarasu 	struct rc5t583_rtc *ricoh_rtc;
20990829c08SVenu Byravarasu 	struct rc5t583_platform_data *pmic_plat_data;
21090829c08SVenu Byravarasu 	int ret;
21190829c08SVenu Byravarasu 	int irq;
21290829c08SVenu Byravarasu 
21390829c08SVenu Byravarasu 	ricoh_rtc = devm_kzalloc(&pdev->dev, sizeof(struct rc5t583_rtc),
21490829c08SVenu Byravarasu 			GFP_KERNEL);
21590829c08SVenu Byravarasu 	if (!ricoh_rtc)
21690829c08SVenu Byravarasu 		return -ENOMEM;
21790829c08SVenu Byravarasu 
21890829c08SVenu Byravarasu 	platform_set_drvdata(pdev, ricoh_rtc);
21990829c08SVenu Byravarasu 
22090829c08SVenu Byravarasu 	/* Clear pending interrupts */
22190829c08SVenu Byravarasu 	ret = regmap_write(rc5t583->regmap, RC5T583_RTC_CTL2, 0);
22290829c08SVenu Byravarasu 	if (ret < 0)
22390829c08SVenu Byravarasu 		return ret;
22490829c08SVenu Byravarasu 
22590829c08SVenu Byravarasu 	/* clear RTC Adjust register */
22690829c08SVenu Byravarasu 	ret = regmap_write(rc5t583->regmap, RC5T583_RTC_ADJ, 0);
22790829c08SVenu Byravarasu 	if (ret < 0) {
22890829c08SVenu Byravarasu 		dev_err(&pdev->dev, "unable to program rtc_adjust reg\n");
22990829c08SVenu Byravarasu 		return -EBUSY;
23090829c08SVenu Byravarasu 	}
23190829c08SVenu Byravarasu 
23290829c08SVenu Byravarasu 	pmic_plat_data = dev_get_platdata(rc5t583->dev);
23390829c08SVenu Byravarasu 	irq = pmic_plat_data->irq_base;
23490829c08SVenu Byravarasu 	if (irq <= 0) {
23590829c08SVenu Byravarasu 		dev_warn(&pdev->dev, "Wake up is not possible as irq = %d\n",
23690829c08SVenu Byravarasu 			irq);
23790829c08SVenu Byravarasu 		return ret;
23890829c08SVenu Byravarasu 	}
23990829c08SVenu Byravarasu 
24090829c08SVenu Byravarasu 	irq += RC5T583_IRQ_YALE;
24190829c08SVenu Byravarasu 	ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
24290829c08SVenu Byravarasu 		rc5t583_rtc_interrupt, IRQF_TRIGGER_LOW,
24390829c08SVenu Byravarasu 		"rtc-rc5t583", &pdev->dev);
24490829c08SVenu Byravarasu 	if (ret < 0) {
24590829c08SVenu Byravarasu 		dev_err(&pdev->dev, "IRQ is not free.\n");
24690829c08SVenu Byravarasu 		return ret;
24790829c08SVenu Byravarasu 	}
24890829c08SVenu Byravarasu 	device_init_wakeup(&pdev->dev, 1);
24990829c08SVenu Byravarasu 
2508b7980dbSJingoo Han 	ricoh_rtc->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
25190829c08SVenu Byravarasu 		&rc5t583_rtc_ops, THIS_MODULE);
25290829c08SVenu Byravarasu 	if (IS_ERR(ricoh_rtc->rtc)) {
25390829c08SVenu Byravarasu 		ret = PTR_ERR(ricoh_rtc->rtc);
25490829c08SVenu Byravarasu 		dev_err(&pdev->dev, "RTC device register: err %d\n", ret);
25590829c08SVenu Byravarasu 		return ret;
25690829c08SVenu Byravarasu 	}
25790829c08SVenu Byravarasu 
25890829c08SVenu Byravarasu 	return 0;
25990829c08SVenu Byravarasu }
26090829c08SVenu Byravarasu 
26190829c08SVenu Byravarasu /*
26290829c08SVenu Byravarasu  * Disable rc5t583 RTC interrupts.
26390829c08SVenu Byravarasu  * Sets status flag to free.
26490829c08SVenu Byravarasu  */
rc5t583_rtc_remove(struct platform_device * pdev)265*ade527ddSUwe Kleine-König static void rc5t583_rtc_remove(struct platform_device *pdev)
26690829c08SVenu Byravarasu {
267e1c2f989SJingoo Han 	struct rc5t583_rtc *rc5t583_rtc = platform_get_drvdata(pdev);
26890829c08SVenu Byravarasu 
26990829c08SVenu Byravarasu 	rc5t583_rtc_alarm_irq_enable(&rc5t583_rtc->rtc->dev, 0);
27090829c08SVenu Byravarasu }
27190829c08SVenu Byravarasu 
27290829c08SVenu Byravarasu #ifdef CONFIG_PM_SLEEP
rc5t583_rtc_suspend(struct device * dev)27390829c08SVenu Byravarasu static int rc5t583_rtc_suspend(struct device *dev)
27490829c08SVenu Byravarasu {
27590829c08SVenu Byravarasu 	struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent);
27690829c08SVenu Byravarasu 	struct rc5t583_rtc *rc5t583_rtc = dev_get_drvdata(dev);
27790829c08SVenu Byravarasu 	int ret;
27890829c08SVenu Byravarasu 
27990829c08SVenu Byravarasu 	/* Store current list of enabled interrupts*/
28090829c08SVenu Byravarasu 	ret = regmap_read(rc5t583->regmap, RC5T583_RTC_CTL1,
28190829c08SVenu Byravarasu 		&rc5t583_rtc->irqen);
28290829c08SVenu Byravarasu 	return ret;
28390829c08SVenu Byravarasu }
28490829c08SVenu Byravarasu 
rc5t583_rtc_resume(struct device * dev)28590829c08SVenu Byravarasu static int rc5t583_rtc_resume(struct device *dev)
28690829c08SVenu Byravarasu {
28790829c08SVenu Byravarasu 	struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent);
28890829c08SVenu Byravarasu 	struct rc5t583_rtc *rc5t583_rtc = dev_get_drvdata(dev);
28990829c08SVenu Byravarasu 
29090829c08SVenu Byravarasu 	/* Restore list of enabled interrupts before suspend */
29190829c08SVenu Byravarasu 	return regmap_write(rc5t583->regmap, RC5T583_RTC_CTL1,
29290829c08SVenu Byravarasu 		rc5t583_rtc->irqen);
29390829c08SVenu Byravarasu }
29490829c08SVenu Byravarasu #endif
29590829c08SVenu Byravarasu 
29629756a52SJingoo Han static SIMPLE_DEV_PM_OPS(rc5t583_rtc_pm_ops, rc5t583_rtc_suspend,
29729756a52SJingoo Han 			rc5t583_rtc_resume);
29829756a52SJingoo Han 
29990829c08SVenu Byravarasu static struct platform_driver rc5t583_rtc_driver = {
30090829c08SVenu Byravarasu 	.probe		= rc5t583_rtc_probe,
301*ade527ddSUwe Kleine-König 	.remove_new	= rc5t583_rtc_remove,
30290829c08SVenu Byravarasu 	.driver		= {
30390829c08SVenu Byravarasu 		.name	= "rtc-rc5t583",
30429756a52SJingoo Han 		.pm	= &rc5t583_rtc_pm_ops,
30590829c08SVenu Byravarasu 	},
30690829c08SVenu Byravarasu };
30790829c08SVenu Byravarasu 
30890829c08SVenu Byravarasu module_platform_driver(rc5t583_rtc_driver);
30990829c08SVenu Byravarasu MODULE_ALIAS("platform:rtc-rc5t583");
31090829c08SVenu Byravarasu MODULE_AUTHOR("Venu Byravarasu <vbyravarasu@nvidia.com>");
31190829c08SVenu Byravarasu MODULE_LICENSE("GPL v2");
312