summaryrefslogtreecommitdiff
path: root/drivers/rtc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/rtc')
-rw-r--r--drivers/rtc/Kconfig89
-rw-r--r--drivers/rtc/Makefile12
-rw-r--r--drivers/rtc/rtc-as3720.c324
-rw-r--r--drivers/rtc/rtc-as3722.c387
-rw-r--r--drivers/rtc/rtc-max77660.c445
-rw-r--r--drivers/rtc/rtc-max77663.c643
-rw-r--r--drivers/rtc/rtc-max8907c.c323
-rw-r--r--drivers/rtc/rtc-palmas.c119
-rw-r--r--drivers/rtc/rtc-ricoh583.c404
-rw-r--r--drivers/rtc/rtc-tegra.c3
-rw-r--r--drivers/rtc/rtc-tps6591x.c567
11 files changed, 3293 insertions, 23 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index b9838130a7b0..64f33eabf909 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -133,6 +133,16 @@ comment "I2C RTC drivers"
if I2C
+config RTC_DRV_AS3722
+ tristate "ams AS3722"
+ depends on MFD_AS3722
+ help
+ If you say Y here you will get support for the RTC feature
+ of the ams AS3722 PMIC.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-as3722.
+
config RTC_DRV_88PM860X
tristate "Marvell 88PM860x"
depends on I2C && MFD_88PM860X
@@ -267,6 +277,26 @@ config RTC_DRV_MAX77686
This driver can also be built as a module. If so, the module
will be called rtc-max77686.
+config RTC_DRV_MAX8907C
+ tristate "Maxim MAX8907C"
+ depends on MFD_MAX8907C
+ help
+ If you say yes here you will get support for the
+ RTC of Maxim MAX8907C PMIC.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-max8907c.
+
+config RTC_DRV_MAX77663
+ tristate "Maxim MAX77663"
+ depends on MFD_MAX77663
+ help
+ If you say yes here you will get support for the
+ RTC of Maxim MAX77663 PMIC.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-max77663.
+
config RTC_DRV_RS5C372
tristate "Ricoh R2025S/D, RS5C372A/B, RV5C386, RV5C387A"
help
@@ -303,16 +333,6 @@ config RTC_DRV_X1205
This driver can also be built as a module. If so, the module
will be called rtc-x1205.
-config RTC_DRV_PALMAS
- tristate "TI Palmas RTC driver"
- depends on MFD_PALMAS
- help
- If you say yes here you get support for the RTC of TI PALMA series PMIC
- chips.
-
- This driver can also be built as a module. If so, the module
- will be called rtc-palma.
-
config RTC_DRV_PCF8523
tristate "NXP PCF8523"
help
@@ -488,6 +508,16 @@ config RTC_DRV_RV3029C2
This driver can also be built as a module. If so, the module
will be called rtc-rv3029c2.
+config RTC_DRV_AS3720
+ tristate "ams AS3720"
+ depends on MFD_AS3720
+ help
+ If you say Y here you will get support for the RTC feature
+ of the AS3720 PMIC.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-as3720.
+
endif # I2C
comment "SPI RTC drivers"
@@ -1180,8 +1210,7 @@ config RTC_DRV_TEGRA
tristate "NVIDIA Tegra Internal RTC driver"
depends on ARCH_TEGRA
help
- If you say yes here you get support for the
- Tegra 200 series internal RTC module.
+ If you say yes here you get support for the Tegra internal RTC module.
This drive can also be built as a module. If so, the module
will be called rtc-tegra.
@@ -1193,6 +1222,23 @@ config RTC_DRV_TILE
Enable support for the Linux driver side of the Tilera
hypervisor's real-time clock interface.
+config RTC_DRV_TPS6591x
+ tristate "TPS6591x RTC driver"
+ depends on MFD_TPS6591X
+ default n
+ help
+ If you say yes here you get support for the TPS6591x RTC module.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-tps6591x.
+
+config RTC_DRV_PALMAS
+ tristate "PALMAS RTC driver"
+ depends on MFD_PALMAS
+ default n
+ help
+ If you say yes here you get support for the PALMAS RTC module.
+
config RTC_DRV_PUV3
tristate "PKUnity v3 RTC support"
depends on ARCH_PUV3
@@ -1212,6 +1258,25 @@ config RTC_DRV_LOONGSON1
This driver can also be built as a module. If so, the module
will be called rtc-ls1x.
+config RTC_DRV_RC5T583
+ tristate "RICOH RC5T583 PMU RTC driver"
+ depends on MFD_RICOH583
+ default n
+ help
+ If you say yes here you get support for the RICOH RC5T583 RTC module.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-rc5t583.
+config RTC_DRV_MAX77660
+ tristate "Maxim MAX77660"
+ depends on MFD_MAX77660
+ help
+ If you say yes here you will get support for the
+ RTC of Maxim MAX77660 PMIC.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-max77660.
+
config RTC_DRV_MXC
tristate "Freescale MXC Real Time Clock"
depends on ARCH_MXC
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index c33f86f1a69b..8a01aa569d20 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -2,6 +2,8 @@
# Makefile for RTC class/drivers.
#
+GCOV_PROFILE := y
+
ccflags-$(CONFIG_RTC_DEBUG) := -DDEBUG
obj-$(CONFIG_RTC_LIB) += rtc-lib.o
@@ -20,6 +22,8 @@ obj-$(CONFIG_RTC_DRV_88PM860X) += rtc-88pm860x.o
obj-$(CONFIG_RTC_DRV_88PM80X) += rtc-88pm80x.o
obj-$(CONFIG_RTC_DRV_AB3100) += rtc-ab3100.o
obj-$(CONFIG_RTC_DRV_AB8500) += rtc-ab8500.o
+obj-$(CONFIG_RTC_DRV_AS3720) += rtc-as3720.o
+obj-$(CONFIG_RTC_DRV_AS3722) += rtc-as3722.o
obj-$(CONFIG_RTC_DRV_AT32AP700X)+= rtc-at32ap700x.o
obj-$(CONFIG_RTC_DRV_AT91RM9200)+= rtc-at91rm9200.o
obj-$(CONFIG_RTC_DRV_AT91SAM9) += rtc-at91sam9.o
@@ -73,15 +77,16 @@ obj-$(CONFIG_RTC_DRV_MAX8907) += rtc-max8907.o
obj-$(CONFIG_RTC_DRV_MAX8925) += rtc-max8925.o
obj-$(CONFIG_RTC_DRV_MAX8998) += rtc-max8998.o
obj-$(CONFIG_RTC_DRV_MAX8997) += rtc-max8997.o
+obj-$(CONFIG_RTC_DRV_MAX8907C) += rtc-max8907c.o
obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o
obj-$(CONFIG_RTC_DRV_MAX77686) += rtc-max77686.o
obj-$(CONFIG_RTC_DRV_MC13XXX) += rtc-mc13xxx.o
+obj-$(CONFIG_RTC_DRV_MAX77663) += rtc-max77663.o
obj-$(CONFIG_RTC_DRV_MSM6242) += rtc-msm6242.o
obj-$(CONFIG_RTC_DRV_MPC5121) += rtc-mpc5121.o
obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o
obj-$(CONFIG_RTC_DRV_NUC900) += rtc-nuc900.o
obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o
-obj-$(CONFIG_RTC_DRV_PALMAS) += rtc-palmas.o
obj-$(CONFIG_RTC_DRV_PCAP) += rtc-pcap.o
obj-$(CONFIG_RTC_DRV_PCF8523) += rtc-pcf8523.o
obj-$(CONFIG_RTC_DRV_PCF8563) += rtc-pcf8563.o
@@ -114,9 +119,13 @@ obj-$(CONFIG_RTC_DRV_STARFIRE) += rtc-starfire.o
obj-$(CONFIG_RTC_DRV_STK17TA8) += rtc-stk17ta8.o
obj-$(CONFIG_RTC_DRV_STMP) += rtc-stmp3xxx.o
obj-$(CONFIG_RTC_DRV_SUN4V) += rtc-sun4v.o
+CFLAGS_rtc-tegra.o = -Werror
obj-$(CONFIG_RTC_DRV_TEGRA) += rtc-tegra.o
obj-$(CONFIG_RTC_DRV_TEST) += rtc-test.o
obj-$(CONFIG_RTC_DRV_TILE) += rtc-tile.o
+obj-$(CONFIG_RTC_DRV_TPS6591x) += rtc-tps6591x.o
+obj-$(CONFIG_RTC_DRV_PALMAS) += rtc-palmas.o
+#obj-$(CONFIG_RTC_DRV_RC5T583) += rtc-ricoh583.o
obj-$(CONFIG_RTC_DRV_TWL4030) += rtc-twl.o
obj-$(CONFIG_RTC_DRV_TPS6586X) += rtc-tps6586x.o
obj-$(CONFIG_RTC_DRV_TPS65910) += rtc-tps65910.o
@@ -128,3 +137,4 @@ obj-$(CONFIG_RTC_DRV_VT8500) += rtc-vt8500.o
obj-$(CONFIG_RTC_DRV_WM831X) += rtc-wm831x.o
obj-$(CONFIG_RTC_DRV_WM8350) += rtc-wm8350.o
obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o
+obj-$(CONFIG_RTC_DRV_MAX77660) += rtc-max77660.o
diff --git a/drivers/rtc/rtc-as3720.c b/drivers/rtc/rtc-as3720.c
new file mode 100644
index 000000000000..da082f211171
--- /dev/null
+++ b/drivers/rtc/rtc-as3720.c
@@ -0,0 +1,324 @@
+/*
+ * Real Time Clock driver for ams AS3720 PMICs
+ *
+ * Copyright (C) 2012 ams AG.
+ *
+ * Author: Bernhard Breinbauer <Bernhard.Breinbauer@ams.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/time.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+#include <linux/interrupt.h>
+#include <linux/ioctl.h>
+#include <linux/completion.h>
+#include <linux/mfd/as3720.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+
+#define AS3720_SET_ALM_RETRIES 5
+#define AS3720_SET_TIME_RETRIES 5
+#define AS3720_GET_TIME_RETRIES 5
+
+/*
+ * Read current time and date in RTC
+ */
+static int as3720_rtc_readtime(struct device *dev, struct rtc_time *tm)
+{
+ struct as3720 *as3720 = dev_get_drvdata(dev->parent);
+ struct as3720_platform_data *pdata = as3720->dev->platform_data;
+ u8 as_sec;
+ u8 as_min_array[3];
+ int as_min;
+ long time, start_time;
+ struct rtc_time start_tm;
+ int ret;
+
+ as3720_reg_read(as3720, AS3720_RTC_SECOND_REG, &as_sec);
+ ret = as3720_block_read(as3720, AS3720_RTC_MINUTE1_REG,
+ 3, as_min_array);
+ if (ret < 0) {
+ dev_err(dev, "failed to read time with err:%d\n", ret);
+ return ret;
+ }
+ as_min = (as_min_array[2] << 16)
+ | (as_min_array[1] << 8)
+ | (as_min_array[0]);
+ time = as_min*60 + as_sec;
+ start_tm.tm_year = (pdata->rtc_start_year - 1900);
+ start_tm.tm_mon = 0;
+ start_tm.tm_mday = 1;
+ start_tm.tm_hour = 0;
+ start_tm.tm_min = 0;
+ start_tm.tm_sec = 0;
+ rtc_tm_to_time(&start_tm, &start_time);
+ time = time + start_time;
+ rtc_time_to_tm(time, tm);
+ return 0;
+}
+
+/*
+ * Set current time and date in RTC
+ */
+static int as3720_rtc_settime(struct device *dev, struct rtc_time *tm)
+{
+ struct as3720 *as3720 = dev_get_drvdata(dev->parent);
+ struct as3720_platform_data *pdata = as3720->dev->platform_data;
+ long time, start_time;
+ u8 as_sec;
+ u8 as_min_array[3];
+ int as_min;
+ struct rtc_time start_tm;
+ int ret;
+
+ /* Write time to RTC */
+ rtc_tm_to_time(tm, &time);
+ start_tm.tm_year = (pdata->rtc_start_year - 1900);
+ start_tm.tm_mon = 0;
+ start_tm.tm_mday = 1;
+ start_tm.tm_hour = 0;
+ start_tm.tm_min = 0;
+ start_tm.tm_sec = 0;
+ rtc_tm_to_time(&start_tm, &start_time);
+ time = time - start_time;
+ as_min = time / 60;
+ as_sec = time % 60;
+ as_min_array[2] = (as_min & 0xFF0000) >> 16;
+ as_min_array[1] = (as_min & 0xFF00) >> 8;
+ as_min_array[0] = as_min & 0xFF;
+ as3720_reg_write(as3720, AS3720_RTC_SECOND_REG, as_sec);
+ ret = as3720_block_write(as3720, AS3720_RTC_MINUTE1_REG, 3,
+ as_min_array);
+ if (ret < 0) {
+ dev_err(dev, "failed to set time with err:%d\n", ret);
+ return ret;
+ }
+ return 0;
+}
+
+/*
+ * Read alarm time and date in RTC
+ */
+static int as3720_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct as3720 *as3720 = dev_get_drvdata(dev->parent);
+ struct as3720_platform_data *pdata = as3720->dev->platform_data;
+ u8 as_sec;
+ u8 as_min_array[3];
+ int as_min;
+ long time, start_time;
+ struct rtc_time start_tm;
+ int ret;
+
+ as3720_reg_read(as3720, AS3720_RTC_ALARM_SECOND_REG, &as_sec);
+ ret = as3720_block_read(as3720, AS3720_RTC_ALARM_MINUTE1_REG,
+ 3, as_min_array);
+ if (ret < 0) {
+ dev_err(dev, "failed to read alarm with err:%d\n", ret);
+ return ret;
+ }
+ as_min = (as_min_array[2] << 16)
+ | (as_min_array[1] << 8)
+ | (as_min_array[0]);
+ time = as_min*60 + as_sec;
+ start_tm.tm_year = (pdata->rtc_start_year - 1900);
+ start_tm.tm_mon = 0;
+ start_tm.tm_mday = 1;
+ start_tm.tm_hour = 0;
+ start_tm.tm_min = 0;
+ start_tm.tm_sec = 0;
+ rtc_tm_to_time(&start_tm, &start_time);
+ time = time + start_time;
+ rtc_time_to_tm(time, &alrm->time);
+ return 0;
+}
+
+static int as3720_rtc_stop_alarm(struct as3720 *as3720)
+{
+ /* disable rtc alarm interrupt */
+ return as3720_set_bits(as3720, AS3720_INTERRUPTMASK2_REG,
+ AS3720_IRQ_RTC_ALARM, 1);
+}
+
+static int as3720_rtc_start_alarm(struct as3720 *as3720)
+{
+ /* enable rtc alarm interrupt */
+ return as3720_set_bits(as3720, AS3720_INTERRUPTMASK2_REG,
+ AS3720_IRQ_RTC_ALARM, 0);
+}
+
+static int as3720_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct as3720 *as3720 = dev_get_drvdata(dev->parent);
+
+ if (enabled)
+ return as3720_rtc_start_alarm(as3720);
+ else
+ return as3720_rtc_stop_alarm(as3720);
+}
+
+static int as3720_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct as3720 *as3720 = dev_get_drvdata(dev->parent);
+ struct as3720_platform_data *pdata = as3720->dev->platform_data;
+ long time, start_time;
+ u8 as_sec;
+ u8 as_min_array[3];
+ int as_min;
+ struct rtc_time start_tm;
+ int ret;
+
+ /* Write time to RTC */
+ rtc_tm_to_time(&alrm->time, &time);
+ start_tm.tm_year = (pdata->rtc_start_year - 1900);
+ start_tm.tm_mon = 0;
+ start_tm.tm_mday = 1;
+ start_tm.tm_hour = 0;
+ start_tm.tm_min = 0;
+ start_tm.tm_sec = 0;
+ rtc_tm_to_time(&start_tm, &start_time);
+ time = time - start_time;
+ as_min = time / 60;
+ as_sec = time % 60;
+ as_min_array[2] = (as_min & 0xFF0000) >> 16;
+ as_min_array[1] = (as_min & 0xFF00) >> 8;
+ as_min_array[0] = as_min & 0xFF;
+
+ /* Write time to RTC */
+ as3720_reg_write(as3720, AS3720_RTC_ALARM_SECOND_REG, as_sec);
+ ret = as3720_block_write(as3720, AS3720_RTC_ALARM_MINUTE1_REG, 3,
+ as_min_array);
+ if (ret < 0) {
+ dev_err(dev, "failed to set alarm with err:%d\n", ret);
+ return ret;
+ }
+ return 0;
+}
+
+static const struct rtc_class_ops as3720_rtc_ops = {
+ .read_time = as3720_rtc_readtime,
+ .set_time = as3720_rtc_settime,
+ .read_alarm = as3720_rtc_readalarm,
+ .set_alarm = as3720_rtc_setalarm,
+ .alarm_irq_enable = as3720_rtc_alarm_irq_enable,
+};
+
+#ifdef CONFIG_PM_SLEEP
+static int as3720_rtc_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct as3720 *as3720 = dev_get_drvdata(pdev->dev.parent);
+ int ret = 0;
+ u32 reg;
+
+ as3720_reg_read(as3720, AS3720_INTERRUPTMASK3_REG, &reg);
+
+ if (device_may_wakeup(dev) &&
+ reg & AS3720_IRQ_MASK_RTC_ALARM) {
+ ret = as3720_rtc_stop_alarm(as3720);
+ if (ret != 0)
+ dev_err(dev, "Failed to stop RTC alarm: %d\n",
+ ret);
+ }
+
+ return ret;
+}
+
+static int as3720_rtc_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct as3720 *as3720 = dev_get_drvdata(pdev->dev.parent);
+ int ret;
+
+ if (as3720->rtc.alarm_enabled) {
+ ret = as3720_rtc_start_alarm(as3720);
+ if (ret != 0)
+ dev_err(dev,
+ "Failed to restart RTC alarm: %d\n", ret);
+ }
+
+ return 0;
+}
+
+#define DEV_PM_OPS (&as3720_rtc_pm_ops)
+#else
+#define DEV_PM_OPS NULL
+#endif
+
+static int as3720_rtc_probe(struct platform_device *pdev)
+{
+ struct as3720 *as3720 = dev_get_drvdata(pdev->dev.parent);
+ struct as3720_platform_data *pdata = dev_get_platdata(pdev->dev.parent);
+ struct as3720_rtc *rtc = &as3720->rtc;
+ int ret = 0;
+ u8 ctrl;
+
+ /* enable the RTC if it's not already enabled */
+ as3720_reg_read(as3720, AS3720_RTC_CONTROL_REG, &ctrl);
+ if (!(ctrl & AS3720_RTC_ON_MASK)) {
+ dev_info(&pdev->dev, "Starting RTC\n");
+
+ ret = as3720_set_bits(as3720, AS3720_RTC_CONTROL_REG,
+ AS3720_RTC_ON_MASK, AS3720_RTC_ON_MASK);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to enable RTC: %d\n", ret);
+ return ret;
+ }
+ }
+ /* enable alarm wakeup */
+ as3720_set_bits(as3720, AS3720_RTC_CONTROL_REG,
+ AS3720_RTC_ALARM_WAKEUP_EN_MASK,
+ AS3720_RTC_ALARM_WAKEUP_EN_MASK);
+
+ device_init_wakeup(&pdev->dev, 1);
+
+ rtc->rtc = rtc_device_register("as3720", &pdev->dev,
+ &as3720_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc->rtc)) {
+ ret = PTR_ERR(rtc->rtc);
+ dev_err(&pdev->dev, "failed to register RTC: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int as3720_rtc_remove(struct platform_device *pdev)
+{
+ struct as3720 *as3720 = platform_get_drvdata(pdev);
+ struct as3720_rtc *rtc = &as3720->rtc;
+
+ rtc_device_unregister(rtc->rtc);
+
+ return 0;
+}
+
+static const struct dev_pm_ops as3720_rtc_pm_ops = {
+ .suspend = as3720_rtc_suspend,
+ .resume = as3720_rtc_resume,
+};
+
+static struct platform_driver as3720_rtc_driver = {
+ .probe = as3720_rtc_probe,
+ .remove = as3720_rtc_remove,
+ .driver = {
+ .name = "as3720-rtc",
+ .pm = DEV_PM_OPS,
+ },
+};
+
+module_platform_driver(as3720_rtc_driver);
+
+MODULE_AUTHOR("Bernhard Breinbauer <bernhard.breinbauer@ams.com>");
+MODULE_DESCRIPTION("RTC driver for AS3720 PMICs");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:as3720-rtc");
diff --git a/drivers/rtc/rtc-as3722.c b/drivers/rtc/rtc-as3722.c
new file mode 100644
index 000000000000..ab03a68c30d9
--- /dev/null
+++ b/drivers/rtc/rtc-as3722.c
@@ -0,0 +1,387 @@
+/*
+ * rtc-as3722.c - Real Time Clock driver for ams AS3722 PMICs
+ *
+ * Copyright (C) 2013 ams AG
+ * Copyright (c) 2013, NVIDIA Corporation. All rights reserved.
+ *
+ * Author: Florian Lobmaier <florian.lobmaier@ams.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/time.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+#include <linux/interrupt.h>
+#include <linux/ioctl.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/as3722-reg.h>
+#include <linux/mfd/as3722-plat.h>
+
+/* RTC defines:
+ * start year has to be a century that rtc works
+ * correctly with leap years, etc.
+ */
+#define AS3722_RTC_START_YEAR 2000
+#define AS3722_SET_ALM_RETRIES 5
+#define AS3722_SET_TIME_RETRIES 5
+#define AS3722_GET_TIME_RETRIES 5
+
+/*
+ * Read current time and date in RTC
+ */
+static int as3722_rtc_readtime(struct device *dev, struct rtc_time *tm)
+{
+ struct as3722 *as3722 = dev_get_drvdata(dev->parent);
+ u8 as_time_array[6];
+ int ret;
+
+ ret = as3722_block_read(as3722, AS3722_RTC_SECOND_REG,
+ 6, as_time_array);
+ if (ret < 0)
+ return ret;
+
+ tm->tm_sec = ((as_time_array[0] & 0xF0) >> 4) * 10
+ + (as_time_array[0] & 0x0F);
+ tm->tm_min = ((as_time_array[1] & 0xF0) >> 4) * 10
+ + (as_time_array[1] & 0x0F);
+ tm->tm_hour = ((as_time_array[2] & 0xF0) >> 4) * 10
+ + (as_time_array[2] & 0x0F);
+ tm->tm_mday = ((as_time_array[3] & 0xF0) >> 4) * 10
+ + (as_time_array[3] & 0x0F);
+ tm->tm_mon = ((as_time_array[4] & 0xF0) >> 4) * 10
+ + (as_time_array[4] & 0x0F);
+ tm->tm_year = (AS3722_RTC_START_YEAR - 1900)
+ + ((as_time_array[5] & 0xF0) >> 4) * 10
+ + (as_time_array[5] & 0x0F);
+
+ return 0;
+}
+
+/*
+ * Set current time and date in RTC
+ */
+static int as3722_rtc_settime(struct device *dev, struct rtc_time *tm)
+{
+ struct as3722 *as3722 = dev_get_drvdata(dev->parent);
+ u8 as_time_array[6];
+ int ret;
+
+ /* Write time to RTC */
+ as_time_array[0] = ((tm->tm_sec / 10) << 4)
+ + (tm->tm_sec % 10);
+ as_time_array[1] = ((tm->tm_min / 10) << 4)
+ + (tm->tm_min % 10);
+ as_time_array[2] = ((tm->tm_hour / 10) << 4)
+ + (tm->tm_hour % 10);
+ as_time_array[3] = ((tm->tm_mday / 10) << 4)
+ + (tm->tm_mday % 10);
+ as_time_array[4] = ((tm->tm_mon / 10) << 4)
+ + (tm->tm_mon % 10);
+ if (tm->tm_year >= (AS3722_RTC_START_YEAR - 1900))
+ as_time_array[5] = (((tm->tm_year
+ - (AS3722_RTC_START_YEAR - 1900)) / 10) << 4)
+ + ((tm->tm_year
+ - (AS3722_RTC_START_YEAR - 1900)) % 10);
+ else
+ return -1;
+
+ ret = as3722_block_write(as3722, AS3722_RTC_SECOND_REG, 6,
+ as_time_array);
+
+ return ret;
+}
+
+/*
+ * Read alarm time and date in RTC
+ */
+static int as3722_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct as3722 *as3722 = dev_get_drvdata(dev->parent);
+ u8 as_time_array[6];
+ int ret;
+
+ ret = as3722_block_read(as3722, AS3722_RTC_SECOND_REG,
+ 6, as_time_array);
+ if (ret < 0)
+ return ret;
+
+ alrm->time.tm_sec = ((as_time_array[0] & 0xF0) >> 4) * 10
+ + (as_time_array[0] & 0x0F);
+ alrm->time.tm_min = ((as_time_array[1] & 0xF0) >> 4) * 10
+ + (as_time_array[1] & 0x0F);
+ alrm->time.tm_hour = ((as_time_array[2] & 0xF0) >> 4) * 10
+ + (as_time_array[2] & 0x0F);
+ alrm->time.tm_mday = ((as_time_array[3] & 0xF0) >> 4) * 10
+ + (as_time_array[3] & 0x0F);
+ alrm->time.tm_mon = ((as_time_array[4] & 0xF0) >> 4) * 10
+ + (as_time_array[4] & 0x0F);
+ alrm->time.tm_year = (AS3722_RTC_START_YEAR - 1900)
+ + ((as_time_array[5] & 0xF0) >> 4) * 10
+ + (as_time_array[5] & 0x0F);
+
+ return 0;
+}
+
+static int as3722_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct as3722 *as3722 = dev_get_drvdata(dev->parent);
+ u8 as_time_array[6];
+ int ret;
+
+ /* Write time to RTC */
+ as_time_array[0] = ((alrm->time.tm_sec / 10) << 4)
+ + (alrm->time.tm_sec % 10);
+ as_time_array[1] = ((alrm->time.tm_min / 10) << 4)
+ + (alrm->time.tm_min % 10);
+ as_time_array[2] = ((alrm->time.tm_hour / 10) << 4)
+ + (alrm->time.tm_hour % 10);
+ as_time_array[3] = ((alrm->time.tm_mday / 10) << 4)
+ + (alrm->time.tm_mday % 10);
+ as_time_array[4] = ((alrm->time.tm_mon / 10) << 4)
+ + (alrm->time.tm_mon % 10);
+ if (alrm->time.tm_year >= (AS3722_RTC_START_YEAR - 1900))
+ as_time_array[5] = (((alrm->time.tm_year
+ - (AS3722_RTC_START_YEAR - 1900)) / 10) << 4)
+ + ((alrm->time.tm_year
+ - (AS3722_RTC_START_YEAR - 1900)) % 10);
+ else
+ return -1;
+
+ ret = as3722_block_write(as3722, AS3722_RTC_SECOND_REG, 6,
+ as_time_array);
+
+ return ret;
+}
+
+static int as3722_rtc_stop_alarm(struct as3722 *as3722)
+{
+ /* disable rtc alarm interrupt */
+ return as3722_set_bits(as3722, AS3722_INTERRUPTMASK3_REG,
+ AS3722_IRQ_MASK_RTC_ALARM, AS3722_IRQ_BIT_RTC_ALARM);
+}
+
+static int as3722_rtc_start_alarm(struct as3722 *as3722)
+{
+ /* enable rtc alarm interrupt */
+ return as3722_set_bits(as3722, AS3722_INTERRUPTMASK3_REG,
+ AS3722_IRQ_MASK_RTC_ALARM, 0);
+}
+
+static int as3722_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct as3722 *as3722 = dev_get_drvdata(dev->parent);
+
+ if (enabled)
+ return as3722_rtc_start_alarm(as3722);
+ else
+ return as3722_rtc_stop_alarm(as3722);
+}
+
+static irqreturn_t as3722_alarm_irq(int irq, void *data)
+{
+ struct as3722 *as3722 = data;
+ struct rtc_device *rtc = as3722->rtc.rtc;
+
+ rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF);
+
+ return IRQ_HANDLED;
+}
+
+static const struct rtc_class_ops as3722_rtc_ops = {
+ .read_time = as3722_rtc_readtime,
+ .set_time = as3722_rtc_settime,
+ .read_alarm = as3722_rtc_readalarm,
+ .set_alarm = as3722_rtc_setalarm,
+ .alarm_irq_enable = as3722_rtc_alarm_irq_enable,
+};
+
+#ifdef CONFIG_PM
+static int as3722_rtc_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct as3722 *as3722 = dev_get_drvdata(pdev->dev.parent);
+ int ret = 0;
+ u32 reg;
+
+ as3722_reg_read(as3722, AS3722_INTERRUPTMASK3_REG, &reg);
+
+ if (device_may_wakeup(dev) &&
+ reg & AS3722_IRQ_MASK_RTC_ALARM) {
+ ret = as3722_rtc_stop_alarm(as3722);
+ if (ret != 0)
+ dev_err(dev, "Failed to stop RTC alarm: %d\n",
+ ret);
+ }
+
+ return ret;
+}
+
+static int as3722_rtc_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct as3722 *as3722 = dev_get_drvdata(pdev->dev.parent);
+ int ret;
+
+ if (as3722->rtc.alarm_enabled) {
+ ret = as3722_rtc_start_alarm(as3722);
+ if (ret != 0)
+ dev_err(dev,
+ "Failed to restart RTC alarm: %d\n", ret);
+ }
+
+ return 0;
+}
+
+/* Unconditionally disable the alarm */
+static int as3722_rtc_freeze(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct as3722 *as3722 = dev_get_drvdata(pdev->dev.parent);
+ int ret;
+
+ ret = as3722_set_bits(as3722, AS3722_RTC_CONTROL_REG,
+ AS3722_RTC_ALARM_WAKEUP_EN_MASK, 0);
+ if (ret != 0)
+ dev_err(&pdev->dev, "Failed to stop RTC alarm: %d\n", ret);
+
+ return 0;
+}
+
+#define DEV_PM_OPS (&as3722_rtc_pm_ops)
+#else
+#define DEV_PM_OPS NULL
+#endif
+
+static int as3722_rtc_probe(struct platform_device *pdev)
+{
+ struct as3722 *as3722 = dev_get_drvdata(pdev->dev.parent);
+ struct as3722_rtc *rtc = &as3722->rtc;
+ struct as3722_platform_data *pdata = as3722->dev->platform_data;
+ struct as3722_rtc_platform_data *rtc_pdata = NULL;
+
+ int alarm_irq = regmap_irq_get_virq(as3722->irq_data,
+ AS3722_IRQ_RTC_ALARM);
+ int ret = 0;
+ u32 ctrl;
+
+ if (pdata)
+ rtc_pdata = pdata->rtc_pdata;
+ /* enable the RTC if it's not already enabled */
+ as3722_reg_read(as3722, AS3722_RTC_CONTROL_REG, &ctrl);
+ if (!(ctrl & AS3722_RTC_ON_MASK)) {
+ dev_info(&pdev->dev, "Starting RTC\n");
+
+ ret = as3722_set_bits(as3722, AS3722_RTC_CONTROL_REG,
+ AS3722_RTC_ON_MASK, AS3722_RTC_ON_MASK);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "failed to enable RTC: %d\n", ret);
+ return ret;
+ }
+ }
+
+ if (rtc_pdata && rtc_pdata->enable_clk32k) {
+ /* Enable CLK32OUT Pin*/
+ ret = as3722_set_bits(as3722, AS3722_RTC_CONTROL_REG,
+ AS3722_RTC_32KCLK_MASK,
+ AS3722_RTC_32KCLK_MASK);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "failed to enable clk32k: %d\n", ret);
+ return ret;
+ }
+ } else {
+ /* Disable CLK32OUT Pin*/
+ ret = as3722_set_bits(as3722, AS3722_RTC_CONTROL_REG,
+ AS3722_RTC_32KCLK_MASK,
+ 0);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "failed to disable clk32k: %d\n", ret);
+ return ret;
+ }
+ }
+
+
+ /* enable alarm wakeup */
+ as3722_set_bits(as3722, AS3722_RTC_CONTROL_REG,
+ AS3722_RTC_ALARM_WAKEUP_EN_MASK,
+ AS3722_RTC_ALARM_WAKEUP_EN_MASK);
+
+ device_init_wakeup(&pdev->dev, 1);
+
+ rtc->rtc = rtc_device_register("as3722", &pdev->dev,
+ &as3722_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc->rtc)) {
+ ret = PTR_ERR(rtc->rtc);
+ dev_err(&pdev->dev, "failed to register RTC: %d\n", ret);
+ return ret;
+ }
+
+ ret = request_threaded_irq(alarm_irq, NULL, as3722_alarm_irq,
+ pdata->irq_type, "RTC alarm",
+ rtc->rtc);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to request alarm IRQ %d: %d\n",
+ alarm_irq, ret);
+ }
+
+ return 0;
+}
+
+static int as3722_rtc_remove(struct platform_device *pdev)
+{
+ struct as3722 *as3722 = dev_get_drvdata(pdev->dev.parent);
+ struct as3722_rtc *rtc = &as3722->rtc;
+ int alarm_irq = regmap_irq_get_virq(as3722->irq_data,
+ AS3722_IRQ_RTC_ALARM);
+
+ free_irq(alarm_irq, rtc->rtc);
+ rtc_device_unregister(rtc->rtc);
+
+ return 0;
+}
+
+static const struct dev_pm_ops as3722_rtc_pm_ops = {
+ .suspend = as3722_rtc_suspend,
+ .resume = as3722_rtc_resume,
+ .freeze = as3722_rtc_freeze,
+ .thaw = as3722_rtc_resume,
+ .restore = as3722_rtc_resume,
+ .poweroff = as3722_rtc_suspend,
+};
+
+static struct platform_driver as3722_rtc_driver = {
+ .probe = as3722_rtc_probe,
+ .remove = as3722_rtc_remove,
+ .driver = {
+ .name = "as3722-rtc",
+ .pm = DEV_PM_OPS,
+ },
+};
+
+module_platform_driver(as3722_rtc_driver);
+
+MODULE_AUTHOR("Florian Lobmaier <florian.lobmaier@ams.com>");
+MODULE_DESCRIPTION("RTC driver for AS3722 PMICs");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:as3722-rtc");
diff --git a/drivers/rtc/rtc-max77660.c b/drivers/rtc/rtc-max77660.c
new file mode 100644
index 000000000000..e438dd78f876
--- /dev/null
+++ b/drivers/rtc/rtc-max77660.c
@@ -0,0 +1,445 @@
+/*
+ * drivers/rtc/rtc-max77660.c
+ * Max77660 RTC driver
+ *
+ * Copyright 2011-2012, Maxim Integrated Products, Inc.
+ * Copyright (c) 2011-2012, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ */
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/pm.h>
+#include <linux/rtc.h>
+#include <linux/mfd/max77660/max77660-core.h>
+
+/* RTC Year base */
+#define RTC_YEAR_BASE 100
+
+/* Read/write buffer update time as per datasheet */
+#define MAX77660_RD_WR_BUFFER_UPDATE_TIME 300
+
+enum {
+ RTC_SEC,
+ RTC_MIN,
+ RTC_HOUR,
+ RTC_WEEKDAY,
+ RTC_MONTH,
+ RTC_YEAR,
+ RTC_MONTHDAY,
+ RTC_MAX_BUF
+};
+
+struct max77660_rtc {
+ struct rtc_device *rtc;
+ struct device *dev;
+ struct device *parent;
+ int irq;
+ bool shutdown_ongoing;
+ bool alarm1_enabled;
+ struct mutex rtc_reg_lock;
+};
+
+static void max77660_register_to_time(struct rtc_time *time, u8 *buf)
+{
+ time->tm_sec = buf[RTC_SEC] & MAX77660_RTC_SEC_MASK;
+ time->tm_min = buf[RTC_MIN] & MAX77660_RTC_MIN_MASK;
+ time->tm_hour = buf[RTC_HOUR] & MAX77660_RTC_HOUR_MASK;
+ time->tm_wday = ffs(buf[RTC_WEEKDAY] & MAX77660_RTC_WEEKDAY_MASK) - 1;
+ time->tm_mon = (buf[RTC_MONTH] & MAX77660_RTC_MONTH_MASK) - 1;
+ time->tm_year = (buf[RTC_YEAR] & MAX77660_RTC_YEAR_MASK) +
+ RTC_YEAR_BASE;
+ time->tm_mday = buf[RTC_MONTHDAY] & MAX77660_RTC_MONTHDAY_MASK;
+}
+
+static void max77660_time_to_register(struct rtc_time *time, u8 *buf)
+{
+ buf[RTC_SEC] = time->tm_sec & MAX77660_RTC_SEC_MASK;
+ buf[RTC_MIN] = time->tm_min & MAX77660_RTC_MIN_MASK;
+ buf[RTC_HOUR] = time->tm_hour & MAX77660_RTC_HOUR_MASK;
+ buf[RTC_WEEKDAY] = BIT(time->tm_wday & MAX77660_RTC_WEEKDAY_MASK);
+ buf[RTC_MONTH] = (time->tm_mon + 1) & MAX77660_RTC_MONTH_MASK;
+ buf[RTC_YEAR] = (time->tm_year - RTC_YEAR_BASE) &
+ MAX77660_RTC_YEAR_MASK;
+ buf[RTC_MONTHDAY] = time->tm_mday & MAX77660_RTC_MONTHDAY_MASK;
+}
+
+static int max77660_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct max77660_rtc *rtc = dev_get_drvdata(dev);
+ u8 buf[RTC_MAX_BUF];
+ int ret;
+
+ mutex_lock(&rtc->rtc_reg_lock);
+
+ /* Update the read buffer */
+ ret = max77660_reg_set_bits(rtc->parent, MAX77660_RTC_SLAVE,
+ MAX77660_RTC_UPDATE0, MAX77660_RTC_RB_UPDATE_MASK);
+ if (ret < 0) {
+ dev_err(rtc->dev, "RTC_UPDATE0 update failed: %d\n", ret);
+ goto out;
+ }
+
+ /* Wait for update */
+ udelay(MAX77660_RD_WR_BUFFER_UPDATE_TIME);
+
+ ret = max77660_reg_reads(rtc->parent, MAX77660_RTC_SLAVE,
+ MAX77660_RTC_SEC, RTC_MAX_BUF, buf);
+ if (ret < 0) {
+ dev_err(rtc->dev, "RTC_SEC read failed: %d\n", ret);
+ goto out;
+ }
+
+ max77660_register_to_time(tm, buf);
+
+ dev_dbg(dev, "%s() %d %d %d %d %d %d\n",
+ __func__, tm->tm_year, tm->tm_mon, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+out:
+ mutex_unlock(&rtc->rtc_reg_lock);
+ return 0;
+}
+
+static int max77660_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct max77660_rtc *rtc = dev_get_drvdata(dev);
+ u8 buf[RTC_MAX_BUF];
+ int ret;
+
+ dev_dbg(dev, "%s() %d %d %d %d %d %d\n",
+ __func__, tm->tm_year, tm->tm_mon, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+ max77660_time_to_register(tm, buf);
+
+ mutex_lock(&rtc->rtc_reg_lock);
+
+ ret = max77660_reg_writes(rtc->parent, MAX77660_RTC_SLAVE,
+ MAX77660_RTC_SEC, RTC_MAX_BUF, buf);
+ if (ret < 0) {
+ dev_err(rtc->dev, "RTC_SEC write failed: %d\n", ret);
+ goto out;
+ }
+
+ /* Update from write buffer */
+ ret = max77660_reg_set_bits(rtc->parent, MAX77660_RTC_SLAVE,
+ MAX77660_RTC_UPDATE0, MAX77660_RTC_WB_UPDATE_MASK);
+ if (ret < 0) {
+ dev_err(rtc->dev, "RTC_UPDATE0 update failed: %d\n", ret);
+ goto out;
+ }
+
+ /* Wait for update */
+ udelay(MAX77660_RD_WR_BUFFER_UPDATE_TIME);
+out:
+ mutex_unlock(&rtc->rtc_reg_lock);
+ return 0;
+}
+
+static int max77660_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct max77660_rtc *rtc = dev_get_drvdata(dev);
+ int ret = 0;
+
+ if (rtc->shutdown_ongoing) {
+ dev_warn(rtc->dev,
+ "device is shutdown: skipping alarm enable\n");
+ return -ESHUTDOWN;
+ }
+
+ dev_dbg(dev, "%s(): enabled %u\n", __func__, enabled);
+
+ if (enabled)
+ ret = max77660_reg_clr_bits(rtc->parent, MAX77660_RTC_SLAVE,
+ MAX77660_RTC_IRQ_MASK, MAX77660_RTC_IRQ_ALARM1_MASK);
+ else
+ ret = max77660_reg_set_bits(rtc->parent, MAX77660_RTC_SLAVE,
+ MAX77660_RTC_IRQ_MASK, MAX77660_RTC_IRQ_ALARM1_MASK);
+ if (ret < 0) {
+ dev_err(rtc->dev, "RTC_IRQ_MASK update failed: %d\n", ret);
+ return ret;
+ }
+ rtc->alarm1_enabled = enabled;
+ return 0;
+}
+
+static int max77660_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct max77660_rtc *rtc = dev_get_drvdata(dev);
+ u8 buf[RTC_MAX_BUF];
+ int ret;
+
+ if (rtc->shutdown_ongoing) {
+ dev_warn(rtc->dev,
+ "device is shutdown: skipping alarm reading\n");
+ return -ESHUTDOWN;
+ }
+
+ ret = max77660_reg_reads(rtc->parent, MAX77660_RTC_SLAVE,
+ MAX77660_RTC_ALARM_SEC1, RTC_MAX_BUF, buf);
+ if (ret < 0) {
+ dev_err(rtc->dev, "RTC_ALARM1 read failed: %d\n", ret);
+ return ret;
+ }
+ max77660_register_to_time(&alrm->time, buf);
+ dev_dbg(dev, "%s() %d %d %d %d %d %d\n", __func__,
+ alrm->time.tm_year, alrm->time.tm_mon, alrm->time.tm_mday,
+ alrm->time.tm_hour, alrm->time.tm_min, alrm->time.tm_sec);
+ alrm->enabled = rtc->alarm1_enabled;
+ return 0;
+}
+
+static int max77660_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct max77660_rtc *rtc = dev_get_drvdata(dev);
+ u8 buf[RTC_MAX_BUF];
+ int ret;
+
+ if (rtc->shutdown_ongoing) {
+ dev_warn(rtc->dev,
+ "device is shutdown: skipping alarm setting\n");
+ return -ESHUTDOWN;
+ }
+
+ /* Set alarm for sec/min/hour/month/year/day */
+ ret = max77660_reg_write(rtc->parent, MAX77660_RTC_SLAVE,
+ MAX77660_RTC_AE1, 0x77);
+ if (ret < 0) {
+ dev_err(rtc->dev, "RTC_AE1 write failed: %d\n", ret);
+ return ret;
+ }
+
+ dev_dbg(dev, "%s() %d %d %d %d %d %d\n", __func__,
+ alrm->time.tm_year, alrm->time.tm_mon, alrm->time.tm_mday,
+ alrm->time.tm_hour, alrm->time.tm_min, alrm->time.tm_sec);
+ max77660_time_to_register(&alrm->time, buf);
+ buf[RTC_WEEKDAY] = 0;
+
+ ret = max77660_reg_writes(rtc->parent, MAX77660_RTC_SLAVE,
+ MAX77660_RTC_ALARM_SEC1, RTC_MAX_BUF, buf);
+ if (ret < 0) {
+ dev_err(rtc->dev, "RTC_ALARAM1 write failed: %d\n", ret);
+ return ret;
+ }
+ return max77660_rtc_alarm_irq_enable(dev, alrm->enabled);
+}
+
+static irqreturn_t max77660_rtc_irq(int irq, void *data)
+{
+ struct max77660_rtc *rtc = (struct max77660_rtc *)data;
+ u8 status;
+ int ret;
+
+ ret = max77660_reg_read(rtc->parent, MAX77660_RTC_SLAVE,
+ MAX77660_RTC_IRQ, &status);
+ if (ret < 0) {
+ dev_err(rtc->dev, "RTC_IRQ read failed: %d\n", ret);
+ goto out;
+ }
+
+ if (!(status & MAX77660_RTC_IRQ_ALARM1_MASK)) {
+ dev_err(rtc->dev, "Unkknow RTC irq: status 0x%02x\n", status);
+ goto out;
+ }
+ rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF);
+out:
+ return IRQ_HANDLED;
+}
+
+static const struct rtc_class_ops max77660_rtc_ops = {
+ .read_time = max77660_rtc_read_time,
+ .set_time = max77660_rtc_set_time,
+ .read_alarm = max77660_rtc_read_alarm,
+ .set_alarm = max77660_rtc_set_alarm,
+ .alarm_irq_enable = max77660_rtc_alarm_irq_enable,
+};
+
+static int max77660_rtc_preinit(struct max77660_rtc *rtc)
+{
+ int ret;
+
+ ret = max77660_reg_write(rtc->parent, MAX77660_RTC_SLAVE,
+ MAX77660_RTC_IRQ_MASK, 0xFF);
+ if (ret < 0) {
+ dev_err(rtc->dev, "RTC_IRQ_MASK write failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Configure Binary mode and 24hour mode */
+ ret = max77660_reg_write(rtc->parent, MAX77660_RTC_SLAVE,
+ MAX77660_RTC_CTRL_MODE, MAX77660_RTCCNTLM_MASK);
+ if (ret < 0) {
+ dev_err(rtc->dev, "RTC_CTRL_MODE write failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = max77660_reg_write(rtc->parent, MAX77660_RTC_SLAVE,
+ MAX77660_RTC_CTRL, MAX77660_RTCCNTL_HRMODE_24);
+ if (ret < 0) {
+ dev_err(rtc->dev, "RTC_CTRL write failed: %d\n", ret);
+ return ret;
+ }
+ ret = max77660_reg_write(rtc->parent, MAX77660_RTC_SLAVE,
+ MAX77660_RTC_CTRL_MODE, 0x0);
+ if (ret < 0) {
+ dev_err(rtc->dev, "RTC_CTRL_MODE write failed: %d\n", ret);
+ return ret;
+ }
+ return 0;
+}
+
+static int max77660_rtc_probe(struct platform_device *pdev)
+{
+ static struct max77660_rtc *rtc;
+ int ret;
+
+ rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
+ if (!rtc) {
+ dev_err(&pdev->dev, "Memory allocation failed for rtc\n");
+ return -ENOMEM;
+ }
+
+ rtc->shutdown_ongoing = false;
+ dev_set_drvdata(&pdev->dev, rtc);
+ rtc->dev = &pdev->dev;
+ rtc->parent = pdev->dev.parent;
+
+ ret = max77660_rtc_preinit(rtc);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "RTC pre initilisation failed: %d\n", ret);
+ return ret;
+ }
+
+ device_init_wakeup(&pdev->dev, 1);
+
+ mutex_init(&rtc->rtc_reg_lock);
+ rtc->rtc = rtc_device_register("max77660-rtc", &pdev->dev,
+ &max77660_rtc_ops, THIS_MODULE);
+ if (IS_ERR_OR_NULL(rtc->rtc)) {
+ dev_err(&pdev->dev, "probe: Failed to register rtc\n");
+ ret = PTR_ERR(rtc->rtc);
+ goto out;
+ }
+
+ rtc->irq = platform_get_irq(pdev, 0);
+ ret = request_threaded_irq(rtc->irq, NULL, max77660_rtc_irq,
+ IRQF_ONESHOT | IRQF_EARLY_RESUME, dev_name(&pdev->dev), rtc);
+ if (ret < 0) {
+ dev_err(rtc->dev, "request irq %d failed: %dn", rtc->irq, ret);
+ goto out_rtc_free;
+ }
+
+ return 0;
+
+out_rtc_free:
+ rtc_device_unregister(rtc->rtc);
+out:
+ mutex_destroy(&rtc->rtc_reg_lock);
+ return ret;
+}
+
+static int max77660_rtc_remove(struct platform_device *pdev)
+{
+ struct max77660_rtc *rtc = dev_get_drvdata(&pdev->dev);
+
+ free_irq(rtc->irq, rtc);
+
+ rtc_device_unregister(rtc->rtc);
+ mutex_destroy(&rtc->rtc_reg_lock);
+
+ return 0;
+}
+
+static void max77660_rtc_shutdown(struct platform_device *pdev)
+{
+ struct max77660_rtc *rtc = dev_get_drvdata(&pdev->dev);
+
+ rtc->shutdown_ongoing = true;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int max77660_rtc_suspend(struct device *dev)
+{
+ struct max77660_rtc *rtc = dev_get_drvdata(dev);
+ int ret;
+
+ if (device_may_wakeup(dev)) {
+ struct rtc_wkalrm alm;
+
+ enable_irq_wake(rtc->irq);
+
+ /* Set RTC can generate the wakeup signal */
+ ret = max77660_reg_set_bits(rtc->parent, MAX77660_PWR_SLAVE,
+ MAX77660_REG_GLOBAL_CFG2, MAX77660_GLBLCNFG2_RTCWKEN);
+ if (ret < 0)
+ dev_err(rtc->dev, "RTC wake enable failed: %d\n", ret);
+
+ ret = max77660_rtc_read_alarm(dev, &alm);
+ if (!ret) {
+ dev_info(dev, "%s() alrm %d time %d %d %d %d %d %d\n",
+ __func__, alm.enabled, alm.time.tm_year,
+ alm.time.tm_mon, alm.time.tm_mday,
+ alm.time.tm_hour, alm.time.tm_min,
+ alm.time.tm_sec);
+ }
+ }
+ return 0;
+}
+
+static int max77660_rtc_resume(struct device *dev)
+{
+ struct max77660_rtc *rtc = dev_get_drvdata(dev);
+ int ret;
+
+ if (device_may_wakeup(dev)) {
+ struct rtc_time tm;
+
+ disable_irq_wake(rtc->irq);
+
+ /* Set RTC can generate the wakeup signal */
+ ret = max77660_reg_clr_bits(rtc->parent, MAX77660_PWR_SLAVE,
+ MAX77660_REG_GLOBAL_CFG2, MAX77660_GLBLCNFG2_RTCWKEN);
+ if (ret < 0)
+ dev_err(rtc->dev, "RTC wake enable failed: %d\n", ret);
+
+ ret = max77660_rtc_read_time(dev, &tm);
+ if (!ret)
+ dev_info(dev, "%s() %d %d %d %d %d %d\n",
+ __func__, tm.tm_year, tm.tm_mon, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec);
+ }
+ return 0;
+};
+#endif
+
+static const struct dev_pm_ops max77660_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(max77660_rtc_suspend, max77660_rtc_resume)
+};
+
+static struct platform_driver max77660_rtc_driver = {
+ .probe = max77660_rtc_probe,
+ .remove = max77660_rtc_remove,
+ .driver = {
+ .name = "max77660-rtc",
+ .owner = THIS_MODULE,
+ .pm = &max77660_pm_ops,
+ },
+ .shutdown = max77660_rtc_shutdown,
+};
+
+module_platform_driver(max77660_rtc_driver);
+
+MODULE_DESCRIPTION("max77660 RTC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("1.0");
+MODULE_AUTHOR("Maxim Integrated");
+MODULE_ALIAS("platform:max77660-rtc");
diff --git a/drivers/rtc/rtc-max77663.c b/drivers/rtc/rtc-max77663.c
new file mode 100644
index 000000000000..0e14ccb7b7e9
--- /dev/null
+++ b/drivers/rtc/rtc-max77663.c
@@ -0,0 +1,643 @@
+/*
+ * drivers/rtc/rtc-max77663.c
+ * Max77663 RTC driver
+ *
+ * Copyright 2011-2012, Maxim Integrated Products, Inc.
+ * Copyright (c) 2011-2012, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ */
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/rtc.h>
+#include <linux/mfd/max77663-core.h>
+
+/* RTC Registers */
+#define MAX77663_RTC_IRQ 0x00
+#define MAX77663_RTC_IRQ_MASK 0x01
+#define MAX77663_RTC_CTRL_MODE 0x02
+#define MAX77663_RTC_CTRL 0x03
+#define MAX77663_RTC_UPDATE0 0x04
+#define MAX77663_RTC_UPDATE1 0x05
+#define MAX77663_RTC_SEC 0x07
+#define MAX77663_RTC_MIN 0x08
+#define MAX77663_RTC_HOUR 0x09
+#define MAX77663_RTC_WEEKDAY 0x0A
+#define MAX77663_RTC_MONTH 0x0B
+#define MAX77663_RTC_YEAR 0x0C
+#define MAX77663_RTC_MONTHDAY 0x0D
+#define MAX77663_RTC_ALARM_SEC1 0x0E
+#define MAX77663_RTC_ALARM_MIN1 0x0F
+#define MAX77663_RTC_ALARM_HOUR1 0x10
+#define MAX77663_RTC_ALARM_WEEKDAY1 0x11
+#define MAX77663_RTC_ALARM_MONTH1 0x12
+#define MAX77663_RTC_ALARM_YEAR1 0x13
+#define MAX77663_RTC_ALARM_MONTHDAY1 0x14
+
+#define RTC_IRQ_60SEC_MASK (1 << 0)
+#define RTC_IRQ_ALARM1_MASK (1 << 1)
+#define RTC_IRQ_ALARM2_MASK (1 << 2)
+#define RTC_IRQ_SMPL_MASK (1 << 3)
+#define RTC_IRQ_1SEC_MASK (1 << 4)
+#define RTC_IRQ_MASK 0x1F
+
+#define BCD_MODE_MASK (1 << 0)
+#define HR_MODE_MASK (1 << 1)
+
+#define WB_UPDATE_MASK (1 << 0)
+#define FLAG_AUTO_CLEAR_MASK (1 << 1)
+#define FREEZE_SEC_MASK (1 << 2)
+#define RTC_WAKE_MASK (1 << 3)
+#define RB_UPDATE_MASK (1 << 4)
+
+#define WB_UPDATE_FLAG_MASK (1 << 0)
+#define RB_UPDATE_FLAG_MASK (1 << 1)
+
+#define SEC_MASK 0x7F
+#define MIN_MASK 0x7F
+#define HOUR_MASK 0x3F
+#define WEEKDAY_MASK 0x7F
+#define MONTH_MASK 0x1F
+#define YEAR_MASK 0xFF
+#define MONTHDAY_MASK 0x3F
+
+#define ALARM_EN_MASK 0x80
+#define ALARM_EN_SHIFT 7
+
+#define RTC_YEAR_BASE 100
+#define RTC_YEAR_MAX 99
+
+/* ON/OFF Registers */
+#define MAX77663_REG_ONOFF_CFG2 0x42
+
+#define ONOFF_WK_ALARM1_MASK (1 << 2)
+
+enum {
+ RTC_SEC,
+ RTC_MIN,
+ RTC_HOUR,
+ RTC_WEEKDAY,
+ RTC_MONTH,
+ RTC_YEAR,
+ RTC_MONTHDAY,
+ RTC_NR
+};
+
+struct max77663_rtc {
+ struct rtc_device *rtc;
+ struct device *dev;
+
+ struct mutex io_lock;
+ int irq;
+ u8 irq_mask;
+ bool shutdown_ongoing;
+};
+
+static inline struct device *_to_parent(struct max77663_rtc *rtc)
+{
+ return rtc->dev->parent;
+}
+
+static inline int max77663_rtc_update_buffer(struct max77663_rtc *rtc,
+ int write)
+{
+ struct device *parent = _to_parent(rtc);
+ u8 val = FLAG_AUTO_CLEAR_MASK | RTC_WAKE_MASK;
+ int ret;
+
+ if (write)
+ val |= WB_UPDATE_MASK;
+ else
+ val |= RB_UPDATE_MASK;
+
+ dev_dbg(rtc->dev, "rtc_update_buffer: write=%d, addr=0x%x, val=0x%x\n",
+ write, MAX77663_RTC_UPDATE0, val);
+ ret = max77663_write(parent, MAX77663_RTC_UPDATE0, &val, 1, 1);
+ if (ret < 0) {
+ dev_err(rtc->dev, "rtc_update_buffer: "
+ "Failed to get rtc update0\n");
+ return ret;
+ }
+
+ /*
+ * Must wait 14ms for buffer update.
+ * If the sleeping time is 10us - 20ms, usleep_range() is recommended.
+ * Please refer Documentation/timers/timers-howto.txt.
+ */
+ usleep_range(14000, 14000);
+
+ return 0;
+}
+
+static inline int max77663_rtc_write(struct max77663_rtc *rtc, u8 addr,
+ void *values, u32 len, int update_buffer)
+{
+ struct device *parent = _to_parent(rtc);
+ int ret;
+
+ mutex_lock(&rtc->io_lock);
+
+ dev_dbg(rtc->dev, "rtc_write: addr=0x%x, values=0x%x, len=%u, "
+ "update_buffer=%d\n",
+ addr, *((u8 *)values), len, update_buffer);
+ ret = max77663_write(parent, addr, values, len, 1);
+ if (ret < 0)
+ goto out;
+
+ if (update_buffer)
+ ret = max77663_rtc_update_buffer(rtc, 1);
+
+out:
+ mutex_unlock(&rtc->io_lock);
+ return ret;
+}
+
+static inline int max77663_rtc_read(struct max77663_rtc *rtc, u8 addr,
+ void *values, u32 len, int update_buffer)
+{
+ struct device *parent = _to_parent(rtc);
+ int ret;
+
+ mutex_lock(&rtc->io_lock);
+
+ if (update_buffer) {
+ ret = max77663_rtc_update_buffer(rtc, 0);
+ if (ret < 0)
+ goto out;
+ }
+
+ ret = max77663_read(parent, addr, values, len, 1);
+ dev_dbg(rtc->dev, "rtc_read: addr=0x%x, values=0x%x, len=%u, "
+ "update_buffer=%d\n",
+ addr, *((u8 *)values), len, update_buffer);
+
+out:
+ mutex_unlock(&rtc->io_lock);
+ return ret;
+}
+
+static inline int max77663_rtc_reg_to_tm(struct max77663_rtc *rtc, u8 *buf,
+ struct rtc_time *tm)
+{
+ int wday = buf[RTC_WEEKDAY] & WEEKDAY_MASK;
+
+ if (unlikely(!wday)) {
+ dev_err(rtc->dev,
+ "rtc_reg_to_tm: Invalid day of week, %d\n", wday);
+ return -EINVAL;
+ }
+
+ tm->tm_sec = (int)(buf[RTC_SEC] & SEC_MASK);
+ tm->tm_min = (int)(buf[RTC_MIN] & MIN_MASK);
+ tm->tm_hour = (int)(buf[RTC_HOUR] & HOUR_MASK);
+ tm->tm_mday = (int)(buf[RTC_MONTHDAY] & MONTHDAY_MASK);
+ tm->tm_mon = (int)(buf[RTC_MONTH] & MONTH_MASK) - 1;
+ tm->tm_year = (int)(buf[RTC_YEAR] & YEAR_MASK) + RTC_YEAR_BASE;
+ tm->tm_wday = ffs(wday) - 1;
+
+ return 0;
+}
+
+static inline int max77663_rtc_tm_to_reg(struct max77663_rtc *rtc, u8 *buf,
+ struct rtc_time *tm, int alarm)
+{
+ u8 alarm_mask = alarm ? ALARM_EN_MASK : 0;
+
+ if (unlikely((tm->tm_year < RTC_YEAR_BASE) ||
+ (tm->tm_year > RTC_YEAR_BASE + RTC_YEAR_MAX))) {
+ dev_err(rtc->dev,
+ "rtc_tm_to_reg: Invalid year, %d\n", tm->tm_year);
+ return -EINVAL;
+ }
+
+ buf[RTC_SEC] = tm->tm_sec | alarm_mask;
+ buf[RTC_MIN] = tm->tm_min | alarm_mask;
+ buf[RTC_HOUR] = tm->tm_hour | alarm_mask;
+ buf[RTC_MONTHDAY] = tm->tm_mday | alarm_mask;
+ buf[RTC_MONTH] = (tm->tm_mon + 1) | alarm_mask;
+ buf[RTC_YEAR] = (tm->tm_year - RTC_YEAR_BASE) | alarm_mask;
+
+ /* The wday is configured only when disabled alarm. */
+ if (!alarm)
+ buf[RTC_WEEKDAY] = (1 << tm->tm_wday);
+ else {
+ /* Configure its default reset value 0x01, and not enable it. */
+ buf[RTC_WEEKDAY] = 0x01;
+ }
+ return 0;
+}
+
+static inline int max77663_rtc_irq_mask(struct max77663_rtc *rtc, u8 irq)
+{
+ struct device *parent = _to_parent(rtc);
+ u8 irq_mask = rtc->irq_mask | irq;
+ int ret = 0;
+
+ ret = max77663_write(parent, MAX77663_RTC_IRQ_MASK, &irq_mask, 1, 1);
+ if (ret < 0) {
+ dev_err(rtc->dev, "rtc_irq_mask: Failed to set rtc irq mask\n");
+ goto out;
+ }
+ rtc->irq_mask = irq_mask;
+
+out:
+ return ret;
+}
+
+static inline int max77663_rtc_irq_unmask(struct max77663_rtc *rtc, u8 irq)
+{
+ struct device *parent = _to_parent(rtc);
+ u8 irq_mask = rtc->irq_mask & ~irq;
+ int ret = 0;
+
+ ret = max77663_write(parent, MAX77663_RTC_IRQ_MASK, &irq_mask, 1, 1);
+ if (ret < 0) {
+ dev_err(rtc->dev,
+ "rtc_irq_unmask: Failed to set rtc irq mask\n");
+ goto out;
+ }
+ rtc->irq_mask = irq_mask;
+
+out:
+ return ret;
+}
+
+static inline int max77663_rtc_do_irq(struct max77663_rtc *rtc)
+{
+ struct device *parent = _to_parent(rtc);
+ u8 irq_status;
+ int ret;
+
+ ret = max77663_rtc_update_buffer(rtc, 0);
+ if (ret < 0) {
+ dev_err(rtc->dev, "rtc_irq: Failed to get rtc update buffer\n");
+ return ret;
+ }
+
+ ret = max77663_read(parent, MAX77663_RTC_IRQ, &irq_status, 1, 1);
+ if (ret < 0) {
+ dev_err(rtc->dev, "rtc_irq: Failed to get rtc irq status\n");
+ return ret;
+ }
+
+ dev_dbg(rtc->dev, "rtc_do_irq: irq_mask=0x%02x, irq_status=0x%02x\n",
+ rtc->irq_mask, irq_status);
+
+ if (!(rtc->irq_mask & RTC_IRQ_ALARM1_MASK) &&
+ (irq_status & RTC_IRQ_ALARM1_MASK))
+ rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF);
+
+ if (!(rtc->irq_mask & RTC_IRQ_1SEC_MASK) &&
+ (irq_status & RTC_IRQ_1SEC_MASK))
+ rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_UF);
+
+ return ret;
+}
+
+static irqreturn_t max77663_rtc_irq(int irq, void *data)
+{
+ struct max77663_rtc *rtc = (struct max77663_rtc *)data;
+
+ max77663_rtc_do_irq(rtc);
+
+ return IRQ_HANDLED;
+}
+
+static int max77663_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct max77663_rtc *rtc = dev_get_drvdata(dev);
+ int ret = 0;
+
+ if (rtc->irq < 0)
+ return -ENXIO;
+
+ mutex_lock(&rtc->io_lock);
+
+ /* Handle pending interrupt */
+ ret = max77663_rtc_do_irq(rtc);
+ if (ret < 0)
+ goto out;
+
+ /* Config alarm interrupt */
+ if (enabled) {
+ ret = max77663_rtc_irq_unmask(rtc, RTC_IRQ_ALARM1_MASK);
+ if (ret < 0)
+ goto out;
+ } else {
+ ret = max77663_rtc_irq_mask(rtc, RTC_IRQ_ALARM1_MASK);
+ if (ret < 0)
+ goto out;
+ }
+out:
+ mutex_unlock(&rtc->io_lock);
+ return ret;
+}
+
+static int max77663_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct max77663_rtc *rtc = dev_get_drvdata(dev);
+ u8 buf[RTC_NR];
+ int ret;
+
+ ret = max77663_rtc_read(rtc, MAX77663_RTC_SEC, buf, sizeof(buf), 1);
+ if (ret < 0) {
+ dev_err(rtc->dev, "rtc_read_time: Failed to read rtc time\n");
+ return ret;
+ }
+
+ dev_dbg(rtc->dev, "rtc_read_time: "
+ "buf: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
+ buf[RTC_SEC], buf[RTC_MIN], buf[RTC_HOUR], buf[RTC_WEEKDAY],
+ buf[RTC_MONTH], buf[RTC_YEAR], buf[RTC_MONTHDAY]);
+
+ ret = max77663_rtc_reg_to_tm(rtc, buf, tm);
+ if (ret < 0) {
+ dev_err(rtc->dev, "rtc_read_time: "
+ "Failed to convert register format into time format\n");
+ return ret;
+ }
+
+ dev_dbg(rtc->dev, "rtc_read_time: "
+ "tm: %d-%02d-%02d %02d:%02d:%02d, wday=%d\n",
+ tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min,
+ tm->tm_sec, tm->tm_wday);
+
+ return ret;
+}
+
+static int max77663_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct max77663_rtc *rtc = dev_get_drvdata(dev);
+ u8 buf[RTC_NR];
+ int ret;
+
+ dev_dbg(rtc->dev, "rtc_set_time: "
+ "tm: %d-%02d-%02d %02d:%02d:%02d, wday=%d\n",
+ tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min,
+ tm->tm_sec, tm->tm_wday);
+
+ ret = max77663_rtc_tm_to_reg(rtc, buf, tm, 0);
+ if (ret < 0) {
+ dev_err(rtc->dev, "rtc_set_time: "
+ "Failed to convert time format into register format\n");
+ return ret;
+ }
+
+ dev_dbg(rtc->dev, "rtc_set_time: "
+ "buf: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
+ buf[RTC_SEC], buf[RTC_MIN], buf[RTC_HOUR], buf[RTC_WEEKDAY],
+ buf[RTC_MONTH], buf[RTC_YEAR], buf[RTC_MONTHDAY]);
+
+ return max77663_rtc_write(rtc, MAX77663_RTC_SEC, buf, sizeof(buf), 1);
+}
+
+static int max77663_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct max77663_rtc *rtc = dev_get_drvdata(dev);
+ u8 buf[RTC_NR];
+ int ret;
+
+ ret = max77663_rtc_read(rtc, MAX77663_RTC_ALARM_SEC1, buf, sizeof(buf),
+ 1);
+ if (ret < 0) {
+ dev_err(rtc->dev,
+ "rtc_read_alarm: Failed to read rtc alarm time\n");
+ return ret;
+ }
+
+ dev_dbg(rtc->dev, "rtc_read_alarm: "
+ "buf: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
+ buf[RTC_SEC], buf[RTC_MIN], buf[RTC_HOUR], buf[RTC_WEEKDAY],
+ buf[RTC_MONTH], buf[RTC_YEAR], buf[RTC_MONTHDAY]);
+
+ ret = max77663_rtc_reg_to_tm(rtc, buf, &alrm->time);
+ if (ret < 0) {
+ dev_err(rtc->dev, "rtc_read_alarm: "
+ "Failed to convert register format into time format\n");
+ return ret;
+ }
+
+ dev_dbg(rtc->dev, "rtc_read_alarm: "
+ "tm: %d-%02d-%02d %02d:%02d:%02d, wday=%d\n",
+ alrm->time.tm_year, alrm->time.tm_mon, alrm->time.tm_mday,
+ alrm->time.tm_hour, alrm->time.tm_min, alrm->time.tm_sec,
+ alrm->time.tm_wday);
+
+ if (rtc->irq_mask & RTC_IRQ_ALARM1_MASK)
+ alrm->enabled = 0;
+ else
+ alrm->enabled = 1;
+
+ return 0;
+}
+
+static int max77663_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct max77663_rtc *rtc = dev_get_drvdata(dev);
+ u8 buf[RTC_NR];
+ int ret;
+
+ if (rtc->shutdown_ongoing) {
+ dev_warn(rtc->dev, "rtc_set_alarm: "
+ "Device shutdown on-going, skip alarm setting.\n");
+ return -ESHUTDOWN;
+ }
+ dev_dbg(rtc->dev, "rtc_set_alarm: "
+ "tm: %d-%02d-%02d %02d:%02d:%02d, wday=%d [%s]\n",
+ alrm->time.tm_year, alrm->time.tm_mon, alrm->time.tm_mday,
+ alrm->time.tm_hour, alrm->time.tm_min, alrm->time.tm_sec,
+ alrm->time.tm_wday, alrm->enabled?"enable":"disable");
+
+ ret = max77663_rtc_tm_to_reg(rtc, buf, &alrm->time, 1);
+ if (ret < 0) {
+ dev_err(rtc->dev, "rtc_set_alarm: "
+ "Failed to convert time format into register format\n");
+ return ret;
+ }
+
+ dev_dbg(rtc->dev, "rtc_set_alarm: "
+ "buf: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
+ buf[RTC_SEC], buf[RTC_MIN], buf[RTC_HOUR], buf[RTC_WEEKDAY],
+ buf[RTC_MONTH], buf[RTC_YEAR], buf[RTC_MONTHDAY]);
+
+ ret = max77663_rtc_write(rtc, MAX77663_RTC_ALARM_SEC1, buf, sizeof(buf),
+ 1);
+ if (ret < 0) {
+ dev_err(rtc->dev,
+ "rtc_set_alarm: Failed to write rtc alarm time\n");
+ return ret;
+ }
+
+ ret = max77663_rtc_alarm_irq_enable(dev, alrm->enabled);
+ if (ret < 0) {
+ dev_err(rtc->dev,
+ "rtc_set_alarm: Failed to enable rtc alarm\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+static const struct rtc_class_ops max77663_rtc_ops = {
+ .read_time = max77663_rtc_read_time,
+ .set_time = max77663_rtc_set_time,
+ .read_alarm = max77663_rtc_read_alarm,
+ .set_alarm = max77663_rtc_set_alarm,
+ .alarm_irq_enable = max77663_rtc_alarm_irq_enable,
+};
+
+static int max77663_rtc_preinit(struct max77663_rtc *rtc)
+{
+ struct device *parent = _to_parent(rtc);
+ u8 val;
+ int ret;
+
+ /* Mask all interrupts */
+ rtc->irq_mask = 0xFF;
+ ret = max77663_rtc_write(rtc, MAX77663_RTC_IRQ_MASK, &rtc->irq_mask, 1,
+ 0);
+ if (ret < 0) {
+ dev_err(rtc->dev, "preinit: Failed to set rtc irq mask\n");
+ return ret;
+ }
+
+ /* Configure Binary mode and 24hour mode */
+ val = HR_MODE_MASK;
+ ret = max77663_rtc_write(rtc, MAX77663_RTC_CTRL, &val, 1, 0);
+ if (ret < 0) {
+ dev_err(rtc->dev, "preinit: Failed to set rtc control\n");
+ return ret;
+ }
+
+ /* It should be disabled alarm wakeup to wakeup from sleep
+ * by EN1 input signal */
+ ret = max77663_set_bits(parent, MAX77663_REG_ONOFF_CFG2,
+ ONOFF_WK_ALARM1_MASK, 0, 0);
+ if (ret < 0) {
+ dev_err(rtc->dev, "preinit: Failed to set onoff cfg2\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int max77663_rtc_probe(struct platform_device *pdev)
+{
+ struct max77663_platform_data *parent_pdata =
+ pdev->dev.parent->platform_data;
+ static struct max77663_rtc *rtc;
+ int ret = 0;
+
+ rtc = kzalloc(sizeof(struct max77663_rtc), GFP_KERNEL);
+ if (!rtc) {
+ dev_err(&pdev->dev, "probe: kzalloc() failed\n");
+ return -ENOMEM;
+ }
+ rtc->shutdown_ongoing = false;
+ dev_set_drvdata(&pdev->dev, rtc);
+ rtc->dev = &pdev->dev;
+ mutex_init(&rtc->io_lock);
+
+ ret = max77663_rtc_preinit(rtc);
+ if (ret) {
+ dev_err(&pdev->dev, "probe: Failed to rtc preinit\n");
+ goto out_kfree;
+ }
+
+ /*
+ * RTC should be a wakeup source, or alarm dev can't link to
+ * this devices. that cause Android time change not set into
+ * RTC register.
+ */
+ device_init_wakeup(&pdev->dev, true);
+
+ rtc->rtc = rtc_device_register("max77663-rtc", &pdev->dev,
+ &max77663_rtc_ops, THIS_MODULE);
+ if (IS_ERR_OR_NULL(rtc->rtc)) {
+ dev_err(&pdev->dev, "probe: Failed to register rtc\n");
+ ret = PTR_ERR(rtc->rtc);
+ goto out_kfree;
+ }
+
+ if (parent_pdata->irq_base < 0)
+ goto out;
+
+ rtc->irq = parent_pdata->irq_base + MAX77663_IRQ_RTC;
+ ret = request_threaded_irq(rtc->irq, NULL, max77663_rtc_irq,
+ IRQF_ONESHOT, "max77663-rtc", rtc);
+ if (ret < 0) {
+ dev_err(rtc->dev, "probe: Failed to request irq %d\n",
+ rtc->irq);
+ rtc->irq = -1;
+ } else {
+ device_init_wakeup(rtc->dev, 1);
+ enable_irq_wake(rtc->irq);
+ }
+
+ return 0;
+
+out_kfree:
+ mutex_destroy(&rtc->io_lock);
+ kfree(rtc->rtc);
+out:
+ return ret;
+}
+
+static int max77663_rtc_remove(struct platform_device *pdev)
+{
+ struct max77663_rtc *rtc = dev_get_drvdata(&pdev->dev);
+
+ if (rtc->irq != -1)
+ free_irq(rtc->irq, rtc);
+
+ rtc_device_unregister(rtc->rtc);
+ mutex_destroy(&rtc->io_lock);
+ kfree(rtc);
+
+ return 0;
+}
+
+static void max77663_rtc_shutdown(struct platform_device *pdev)
+{
+ struct max77663_rtc *rtc = dev_get_drvdata(&pdev->dev);
+ u8 buf[RTC_NR] = { 0x0, 0x0, 0x0, 0x1, 0x1, 0x0, 0x1 };
+
+ rtc->shutdown_ongoing = true;
+ dev_info(rtc->dev, "rtc_shutdown: clean alarm\n");
+ max77663_rtc_write(rtc, MAX77663_RTC_ALARM_SEC1, buf, sizeof(buf), 1);
+ max77663_rtc_alarm_irq_enable(&pdev->dev, 0);
+}
+
+static struct platform_driver max77663_rtc_driver = {
+ .probe = max77663_rtc_probe,
+ .remove = max77663_rtc_remove,
+ .driver = {
+ .name = "max77663-rtc",
+ .owner = THIS_MODULE,
+ },
+ .shutdown = max77663_rtc_shutdown,
+};
+
+static int __init max77663_rtc_init(void)
+{
+ return platform_driver_register(&max77663_rtc_driver);
+}
+module_init(max77663_rtc_init);
+
+static void __exit max77663_rtc_exit(void)
+{
+ platform_driver_unregister(&max77663_rtc_driver);
+}
+module_exit(max77663_rtc_exit);
+
+MODULE_DESCRIPTION("max77663 RTC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("1.0");
diff --git a/drivers/rtc/rtc-max8907c.c b/drivers/rtc/rtc-max8907c.c
new file mode 100644
index 000000000000..732976195dae
--- /dev/null
+++ b/drivers/rtc/rtc-max8907c.c
@@ -0,0 +1,323 @@
+/*
+ * RTC driver for Maxim MAX8907c
+ *
+ * Copyright (c) 2011, NVIDIA Corporation.
+ * Based on drivers/rtc/rtc-max8925.c, Copyright (C) 2009-2010 Marvell International Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/rtc.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/max8907c.h>
+
+enum {
+ RTC_SEC = 0,
+ RTC_MIN,
+ RTC_HOUR,
+ RTC_WEEKDAY,
+ RTC_DATE,
+ RTC_MONTH,
+ RTC_YEAR1,
+ RTC_YEAR2,
+};
+
+#define TIME_NUM 8
+#define ALARM_1SEC (1 << 7)
+#define HOUR_12 (1 << 7)
+#define HOUR_AM_PM (1 << 5)
+#define ALARM0_IRQ (1 << 3)
+#define ALARM1_IRQ (1 << 2)
+#define ALARM0_STATUS (1 << 2)
+#define ALARM1_STATUS (1 << 1)
+
+struct max8907c_rtc_info {
+ struct rtc_device *rtc_dev;
+ struct i2c_client *i2c;
+ struct max8907c *chip;
+};
+
+static irqreturn_t rtc_update_handler(int irq, void *data)
+{
+ struct max8907c_rtc_info *info = (struct max8907c_rtc_info *)data;
+
+ /* disable ALARM0 except for 1SEC alarm */
+ max8907c_set_bits(info->i2c, MAX8907C_REG_ALARM0_CNTL, 0x7f, 0);
+ rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF);
+ return IRQ_HANDLED;
+}
+
+static int tm_calc(struct rtc_time *tm, u8 *buf, int len)
+{
+ if (len < TIME_NUM)
+ return -EINVAL;
+ tm->tm_year = (buf[RTC_YEAR2] >> 4) * 1000
+ + (buf[RTC_YEAR2] & 0xf) * 100
+ + (buf[RTC_YEAR1] >> 4) * 10
+ + (buf[RTC_YEAR1] & 0xf);
+ tm->tm_year -= 1900;
+ /* RTC month index issue in max8907c
+ : January index is 1 but kernel assumes it as 0 */
+ tm->tm_mon = ((buf[RTC_MONTH] >> 4) & 0x01) * 10
+ + (buf[RTC_MONTH] & 0x0f) - 1;
+ tm->tm_mday = ((buf[RTC_DATE] >> 4) & 0x03) * 10
+ + (buf[RTC_DATE] & 0x0f);
+ tm->tm_wday = buf[RTC_WEEKDAY] & 0x07;
+ if (buf[RTC_HOUR] & HOUR_12) {
+ tm->tm_hour = ((buf[RTC_HOUR] >> 4) & 0x1) * 10
+ + (buf[RTC_HOUR] & 0x0f);
+ if (buf[RTC_HOUR] & HOUR_AM_PM)
+ tm->tm_hour += 12;
+ } else {
+ tm->tm_hour = ((buf[RTC_HOUR] >> 4) & 0x03) * 10
+ + (buf[RTC_HOUR] & 0x0f);
+ }
+ tm->tm_min = ((buf[RTC_MIN] >> 4) & 0x7) * 10
+ + (buf[RTC_MIN] & 0x0f);
+ tm->tm_sec = ((buf[RTC_SEC] >> 4) & 0x7) * 10
+ + (buf[RTC_SEC] & 0x0f);
+ return 0;
+}
+
+static int data_calc(u8 *buf, struct rtc_time *tm, int len)
+{
+ u8 high, low;
+
+ if (len < TIME_NUM)
+ return -EINVAL;
+
+ high = (tm->tm_year + 1900) / 1000;
+ low = (tm->tm_year + 1900) / 100;
+ low = low - high * 10;
+ buf[RTC_YEAR2] = (high << 4) + low;
+ high = (tm->tm_year + 1900) / 10;
+ low = tm->tm_year + 1900;
+ low = low - high * 10;
+ high = high - (high / 10) * 10;
+ buf[RTC_YEAR1] = (high << 4) + low;
+
+ /* RTC month index issue in max8907c
+ : January index is 1 but kernel assumes it as 0 */
+ high = (tm->tm_mon + 1) / 10;
+ low = (tm->tm_mon + 1) % 10;
+ buf[RTC_MONTH] = (high << 4) + low;
+
+ high = tm->tm_mday / 10;
+ low = tm->tm_mday;
+ low = low - high * 10;
+ buf[RTC_DATE] = (high << 4) + low;
+ buf[RTC_WEEKDAY] = tm->tm_wday;
+ high = tm->tm_hour / 10;
+ low = tm->tm_hour;
+ low = low - high * 10;
+ buf[RTC_HOUR] = (high << 4) + low;
+ high = tm->tm_min / 10;
+ low = tm->tm_min;
+ low = low - high * 10;
+ buf[RTC_MIN] = (high << 4) + low;
+ high = tm->tm_sec / 10;
+ low = tm->tm_sec;
+ low = low - high * 10;
+ buf[RTC_SEC] = (high << 4) + low;
+ return 0;
+}
+
+static int max8907c_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct max8907c_rtc_info *info = dev_get_drvdata(dev);
+ u8 buf[TIME_NUM];
+ int ret;
+
+ ret = max8907c_reg_bulk_read(info->i2c, MAX8907C_REG_RTC_SEC, TIME_NUM, buf);
+
+ if (ret < 0)
+ return ret;
+ ret = tm_calc(tm, buf, TIME_NUM);
+
+ return ret;
+}
+
+static int max8907c_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct max8907c_rtc_info *info = dev_get_drvdata(dev);
+ u8 buf[TIME_NUM];
+ int ret;
+
+ ret = data_calc(buf, tm, TIME_NUM);
+
+ if (ret < 0)
+ return ret;
+ ret = max8907c_reg_bulk_write(info->i2c, MAX8907C_REG_RTC_SEC, TIME_NUM, buf);
+
+ return ret;
+}
+
+static int max8907c_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct max8907c_rtc_info *info = dev_get_drvdata(dev);
+ unsigned char buf[TIME_NUM];
+ int ret;
+
+ ret = max8907c_reg_bulk_read(info->i2c, MAX8907C_REG_ALARM0_SEC, TIME_NUM, buf);
+ if (ret < 0)
+ return ret;
+ ret = tm_calc(&alrm->time, buf, TIME_NUM);
+ if (ret < 0)
+ return ret;
+ ret = max8907c_reg_read(info->i2c, MAX8907C_REG_RTC_IRQ_MASK);
+ if (ret < 0)
+ return ret;
+ if ((ret & ALARM0_IRQ) == 0)
+ alrm->enabled = 1;
+ else
+ alrm->enabled = 0;
+ ret = max8907c_reg_read(info->i2c, MAX8907C_REG_RTC_STATUS);
+ if (ret < 0)
+ return ret;
+ if (ret & ALARM0_STATUS)
+ alrm->pending = 1;
+ else
+ alrm->pending = 0;
+
+ return ret;
+}
+
+static int max8907c_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct max8907c_rtc_info *info = dev_get_drvdata(dev);
+ unsigned char buf[TIME_NUM];
+ int ret;
+
+ ret = data_calc(buf, &alrm->time, TIME_NUM);
+ if (ret < 0)
+ return ret;
+ ret = max8907c_reg_bulk_write(info->i2c, MAX8907C_REG_ALARM0_SEC, TIME_NUM, buf);
+ if (ret < 0)
+ return ret;
+ /* only enable alarm on year/month/day/hour/min/sec */
+ ret = max8907c_reg_write(info->i2c, MAX8907C_REG_ALARM0_CNTL, 0x77);
+
+ return ret;
+}
+
+static const struct rtc_class_ops max8907c_rtc_ops = {
+ .read_time = max8907c_rtc_read_time,
+ .set_time = max8907c_rtc_set_time,
+ .read_alarm = max8907c_rtc_read_alarm,
+ .set_alarm = max8907c_rtc_set_alarm,
+};
+
+static int max8907c_rtc_probe(struct platform_device *pdev)
+{
+ struct max8907c *chip = dev_get_drvdata(pdev->dev.parent);
+ struct max8907c_rtc_info *info;
+ int irq, ret;
+
+ info = kzalloc(sizeof(struct max8907c_rtc_info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+ info->i2c = chip->i2c_rtc;
+ info->chip = chip;
+
+ irq = chip->irq_base + MAX8907C_IRQ_RTC_ALARM0;
+
+ ret = request_threaded_irq(irq, NULL, rtc_update_handler,
+ IRQF_ONESHOT, "rtc-alarm0", info);
+ if (ret < 0) {
+ dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
+ irq, ret);
+ goto out_irq;
+ }
+
+ dev_set_drvdata(&pdev->dev, info);
+ info->rtc_dev = rtc_device_register("max8907c-rtc", &pdev->dev,
+ &max8907c_rtc_ops, THIS_MODULE);
+ ret = PTR_ERR(info->rtc_dev);
+ if (IS_ERR(info->rtc_dev)) {
+ dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret);
+ goto out_rtc;
+ }
+
+ max8907c_set_bits(chip->i2c_power, MAX8907C_REG_SYSENSEL, 0x2, 0x2);
+
+ platform_set_drvdata(pdev, info);
+
+ device_init_wakeup(&pdev->dev, 1);
+
+ return 0;
+out_rtc:
+ free_irq(chip->irq_base + MAX8907C_IRQ_RTC_ALARM0, info);
+
+out_irq:
+ kfree(info);
+ return ret;
+}
+
+static int max8907c_rtc_remove(struct platform_device *pdev)
+{
+ struct max8907c_rtc_info *info = platform_get_drvdata(pdev);
+
+ if (info) {
+ free_irq(info->chip->irq_base + MAX8907C_IRQ_RTC_ALARM0, info);
+
+ rtc_device_unregister(info->rtc_dev);
+ kfree(info);
+ }
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int max8907c_rtc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct device *dev=&pdev->dev;
+ struct max8907c_rtc_info *info = platform_get_drvdata(pdev);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(info->chip->irq_base + MAX8907C_IRQ_RTC_ALARM0);
+ return 0;
+}
+
+static int max8907c_rtc_resume(struct platform_device *pdev)
+{
+ struct device *dev=&pdev->dev;
+ struct max8907c_rtc_info *info = platform_get_drvdata(pdev);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(info->chip->irq_base + MAX8907C_IRQ_RTC_ALARM0);
+ return 0;
+}
+#endif
+
+static struct platform_driver max8907c_rtc_driver = {
+ .driver = {
+ .name = "max8907c-rtc",
+ .owner = THIS_MODULE,
+ },
+ .probe = max8907c_rtc_probe,
+ .remove = max8907c_rtc_remove,
+#ifdef CONFIG_PM
+ .suspend = max8907c_rtc_suspend,
+ .resume = max8907c_rtc_resume,
+#endif
+};
+
+static int __init max8907c_rtc_init(void)
+{
+ return platform_driver_register(&max8907c_rtc_driver);
+}
+module_init(max8907c_rtc_init);
+
+static void __exit max8907c_rtc_exit(void)
+{
+ platform_driver_unregister(&max8907c_rtc_driver);
+}
+module_exit(max8907c_rtc_exit);
+
+MODULE_DESCRIPTION("Maxim MAX8907C RTC driver");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/rtc/rtc-palmas.c b/drivers/rtc/rtc-palmas.c
index 50204d474eb7..ae747543450e 100644
--- a/drivers/rtc/rtc-palmas.c
+++ b/drivers/rtc/rtc-palmas.c
@@ -4,8 +4,9 @@
* RTC driver for TI Palma series devices like TPS65913,
* TPS65914 power management IC.
*
- * Copyright (c) 2012, NVIDIA Corporation.
+ * Copyright (c) 2012 - 2013, NVIDIA CORPORATION. All rights reserved.
*
+ * Author: Kasoju Mallikarjun <mkasoju@nvidia.com>
* Author: Laxman Dewangan <ldewangan@nvidia.com>
*
* This program is free software; you can redistribute it and/or
@@ -73,6 +74,9 @@ static int palmas_rtc_read_time(struct device *dev, struct rtc_time *tm)
tm->tm_mon = bcd2bin(rtc_data[4]) - 1;
tm->tm_year = bcd2bin(rtc_data[5]) + 100;
+ dev_dbg(dev, "%s() %d %d %d %d %d %d\n",
+ __func__, tm->tm_year, tm->tm_mon, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
return ret;
}
@@ -89,6 +93,10 @@ static int palmas_rtc_set_time(struct device *dev, struct rtc_time *tm)
rtc_data[4] = bin2bcd(tm->tm_mon + 1);
rtc_data[5] = bin2bcd(tm->tm_year - 100);
+ dev_dbg(dev, "%s() %d %d %d %d %d %d\n",
+ __func__, tm->tm_year, tm->tm_mon, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+
/* Stop RTC while updating the RTC time registers */
ret = palmas_update_bits(palmas, PALMAS_RTC_BASE, PALMAS_RTC_CTRL_REG,
PALMAS_RTC_CTRL_REG_STOP_RTC, 0);
@@ -144,6 +152,10 @@ static int palmas_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
alm->time.tm_mon = bcd2bin(alarm_data[4]) - 1;
alm->time.tm_year = bcd2bin(alarm_data[5]) + 100;
+ dev_dbg(dev, "%s() %d %d %d %d %d %d\n", __func__,
+ alm->time.tm_year, alm->time.tm_mon, alm->time.tm_mday,
+ alm->time.tm_hour, alm->time.tm_min, alm->time.tm_sec);
+
ret = palmas_read(palmas, PALMAS_RTC_BASE, PALMAS_RTC_INTERRUPTS_REG,
&int_val);
if (ret < 0) {
@@ -175,6 +187,10 @@ static int palmas_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
alarm_data[4] = bin2bcd(alm->time.tm_mon + 1);
alarm_data[5] = bin2bcd(alm->time.tm_year - 100);
+ dev_dbg(dev, "%s() %d %d %d %d %d %d\n", __func__,
+ alm->time.tm_year, alm->time.tm_mon, alm->time.tm_mday,
+ alm->time.tm_hour, alm->time.tm_min, alm->time.tm_sec);
+
ret = palmas_bulk_write(palmas, PALMAS_RTC_BASE,
PALMAS_ALARM_SECONDS_REG, alarm_data, PALMAS_NUM_TIME_REGS);
if (ret < 0) {
@@ -237,12 +253,33 @@ static int palmas_rtc_probe(struct platform_device *pdev)
{
struct palmas *palmas = dev_get_drvdata(pdev->dev.parent);
struct palmas_rtc *palmas_rtc = NULL;
+ struct palmas_platform_data *palmas_pdata;
+ struct palmas_rtc_platform_data *rtc_pdata = NULL;
int ret;
+ bool enable_bb_charging = false;
+ bool high_bb_charging;
+
+
+ palmas_pdata = dev_get_platdata(pdev->dev.parent);
+ if (palmas_pdata)
+ rtc_pdata = palmas_pdata->rtc_pdata;
+
+ if (rtc_pdata) {
+ enable_bb_charging = rtc_pdata->backup_battery_chargeable;
+ high_bb_charging = rtc_pdata->backup_battery_charge_high_current;
+ } else if (pdev->dev.of_node) {
+ enable_bb_charging = of_property_read_bool(pdev->dev.of_node,
+ "ti,backup-battery-chargeable");
+ high_bb_charging = of_property_read_bool(pdev->dev.of_node,
+ "ti,backup-battery-charge-high-current");
+ }
palmas_rtc = devm_kzalloc(&pdev->dev, sizeof(struct palmas_rtc),
GFP_KERNEL);
- if (!palmas_rtc)
+ if (!palmas_rtc) {
+ dev_err(&pdev->dev, "Memory allocation failed.\n");
return -ENOMEM;
+ }
/* Clear pending interrupts */
ret = palmas_clear_interrupts(&pdev->dev);
@@ -251,9 +288,44 @@ static int palmas_rtc_probe(struct platform_device *pdev)
return ret;
}
+ palmas->rtc = palmas_rtc;
palmas_rtc->dev = &pdev->dev;
platform_set_drvdata(pdev, palmas_rtc);
+ if (enable_bb_charging) {
+ unsigned reg = PALMAS_BACKUP_BATTERY_CTRL_BBS_BBC_LOW_ICHRG;
+
+ if (high_bb_charging)
+ reg = 0;
+
+ ret = palmas_update_bits(palmas, PALMAS_PMU_CONTROL_BASE,
+ PALMAS_BACKUP_BATTERY_CTRL,
+ PALMAS_BACKUP_BATTERY_CTRL_BBS_BBC_LOW_ICHRG, reg);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "BACKUP_BATTERY_CTRL update failed, %d\n", ret);
+ return ret;
+ }
+
+ ret = palmas_update_bits(palmas, PALMAS_PMU_CONTROL_BASE,
+ PALMAS_BACKUP_BATTERY_CTRL,
+ PALMAS_BACKUP_BATTERY_CTRL_BB_CHG_EN,
+ PALMAS_BACKUP_BATTERY_CTRL_BB_CHG_EN);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "BACKUP_BATTERY_CTRL update failed, %d\n", ret);
+ return ret;
+ }
+ }
+
+ ret = palmas_write(palmas, PALMAS_RTC_BASE,
+ PALMAS_RTC_INTERRUPTS_REG, 0);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "RTC_INTERRUPTS_REG write failed: %d\n",
+ ret);
+ return ret;
+ }
+
/* Start RTC */
ret = palmas_update_bits(palmas, PALMAS_RTC_BASE, PALMAS_RTC_CTRL_REG,
PALMAS_RTC_CTRL_REG_STOP_RTC,
@@ -263,33 +335,39 @@ static int palmas_rtc_probe(struct platform_device *pdev)
return ret;
}
- palmas_rtc->irq = platform_get_irq(pdev, 0);
+ palmas_rtc->irq = palmas_irq_get_virq(palmas, PALMAS_RTC_ALARM_IRQ);
+ dev_dbg(&pdev->dev, "RTC interrupt %d\n", palmas_rtc->irq);
- palmas_rtc->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
- &palmas_rtc_ops, THIS_MODULE);
+ device_init_wakeup(&pdev->dev, 1);
+ palmas_rtc->rtc = rtc_device_register(pdev->name, &pdev->dev,
+ &palmas_rtc_ops, THIS_MODULE);
if (IS_ERR(palmas_rtc->rtc)) {
ret = PTR_ERR(palmas_rtc->rtc);
dev_err(&pdev->dev, "RTC register failed, err = %d\n", ret);
return ret;
}
- ret = devm_request_threaded_irq(&pdev->dev, palmas_rtc->irq, NULL,
+ ret = request_threaded_irq(palmas_rtc->irq, NULL,
palmas_rtc_interrupt,
IRQF_TRIGGER_LOW | IRQF_ONESHOT |
IRQF_EARLY_RESUME,
dev_name(&pdev->dev), palmas_rtc);
if (ret < 0) {
dev_err(&pdev->dev, "IRQ request failed, err = %d\n", ret);
+ rtc_device_unregister(palmas_rtc->rtc);
return ret;
}
- device_set_wakeup_capable(&pdev->dev, 1);
return 0;
}
static int palmas_rtc_remove(struct platform_device *pdev)
{
+ struct palmas_rtc *palmas_rtc = platform_get_drvdata(pdev);
+
palmas_rtc_alarm_irq_enable(&pdev->dev, 0);
+ free_irq(palmas_rtc->irq, palmas_rtc);
+ rtc_device_unregister(palmas_rtc->rtc);
return 0;
}
@@ -298,8 +376,20 @@ static int palmas_rtc_suspend(struct device *dev)
{
struct palmas_rtc *palmas_rtc = dev_get_drvdata(dev);
- if (device_may_wakeup(dev))
+ if (device_may_wakeup(dev)) {
+ int ret;
+ struct rtc_wkalrm alm;
+
enable_irq_wake(palmas_rtc->irq);
+ ret = palmas_rtc_read_alarm(dev, &alm);
+ if (!ret)
+ dev_info(dev, "%s() alrm %d time %d %d %d %d %d %d\n",
+ __func__, alm.enabled,
+ alm.time.tm_year, alm.time.tm_mon,
+ alm.time.tm_mday, alm.time.tm_hour,
+ alm.time.tm_min, alm.time.tm_sec);
+ }
+
return 0;
}
@@ -307,8 +397,18 @@ static int palmas_rtc_resume(struct device *dev)
{
struct palmas_rtc *palmas_rtc = dev_get_drvdata(dev);
- if (device_may_wakeup(dev))
+ if (device_may_wakeup(dev)) {
+ struct rtc_time tm;
+ int ret;
+
disable_irq_wake(palmas_rtc->irq);
+ ret = palmas_rtc_read_time(dev, &tm);
+ if (!ret)
+ dev_info(dev, "%s() %d %d %d %d %d %d\n",
+ __func__, tm.tm_year, tm.tm_mon, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec);
+ }
+
return 0;
}
#endif
@@ -340,5 +440,6 @@ module_platform_driver(palmas_rtc_driver);
MODULE_ALIAS("platform:palmas_rtc");
MODULE_DESCRIPTION("TI PALMAS series RTC driver");
+MODULE_AUTHOR("Kasoju Mallikarjun <mkasoju@nvidia.com>");
MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/rtc/rtc-ricoh583.c b/drivers/rtc/rtc-ricoh583.c
new file mode 100644
index 000000000000..f960ffa4cbe5
--- /dev/null
+++ b/drivers/rtc/rtc-ricoh583.c
@@ -0,0 +1,404 @@
+/*
+ * drivers/rtc/rtc_ricoh583.c
+ *
+ * rtc driver for ricoh rc5t583 pmu
+ *
+ * copyright (c) 2011, nvidia corporation.
+ *
+ * this program is free software; you can redistribute it and/or modify
+ * it under the terms of the gnu general public license as published by
+ * the free software foundation; either version 2 of the license, or
+ * (at your option) any later version.
+ *
+ * this program is distributed in the hope that it will be useful, but without
+ * any warranty; without even the implied warranty of merchantability or
+ * fitness for a particular purpose. see the gnu general public license for
+ * more details.
+ *
+ * you should have received a copy of the gnu general public license along
+ * with this program; if not, write to the free software foundation, inc.,
+ * 51 franklin street, fifth floor, boston, ma 02110-1301, usa.
+ */
+
+/* #define debug 1 */
+/* #define verbose_debug 1 */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mfd/ricoh583.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+
+#define rtc_ctrl1 0xED
+#define rtc_ctrl2 0xEE
+#define rtc_seconds_reg 0xE0
+#define rtc_alarm_y 0xF0
+#define rtc_adjust 0xE7
+
+/*
+linux rtc driver refers 1900 as base year in many calculations.
+(e.g. refer drivers/rtc/rtc-lib.c)
+*/
+#define os_ref_year 1900
+
+/*
+ pmu rtc have only 2 nibbles to store year information, so using an
+ offset of 100 to set the base year as 2000 for our driver.
+*/
+#define rtc_year_offset 100
+
+struct ricoh583_rtc {
+ unsigned long epoch_start;
+ int irq;
+ struct rtc_device *rtc;
+ bool irq_en;
+};
+
+static int ricoh583_read_regs(struct device *dev, int reg, int len,
+ uint8_t *val)
+{
+ int ret;
+
+ ret = ricoh583_bulk_reads(dev->parent, reg, len, val);
+ if (ret < 0) {
+ dev_err(dev->parent, "\n %s failed reading from 0x%02x\n",
+ __func__, reg);
+ WARN_ON(1);
+ }
+ return ret;
+}
+
+static int ricoh583_write_regs(struct device *dev, int reg, int len,
+ uint8_t *val)
+{
+ int ret;
+ ret = ricoh583_bulk_writes(dev->parent, reg, len, val);
+ if (ret < 0) {
+ dev_err(dev->parent, "\n %s failed writing\n", __func__);
+ WARN_ON(1);
+ }
+
+ return ret;
+}
+
+static int ricoh583_rtc_valid_tm(struct device *dev, struct rtc_time *tm)
+{
+ if (tm->tm_year >= (rtc_year_offset + 99)
+ || tm->tm_mon > 12
+ || tm->tm_mday < 1
+ || tm->tm_mday > rtc_month_days(tm->tm_mon,
+ tm->tm_year + os_ref_year)
+ || tm->tm_hour >= 24
+ || tm->tm_min >= 60
+ || tm->tm_sec >= 60) {
+ dev_err(dev->parent, "\n returning error due to time"
+ "%d/%d/%d %d:%d:%d", tm->tm_mon, tm->tm_mday,
+ tm->tm_year, tm->tm_hour, tm->tm_min, tm->tm_sec);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static u8 dec2bcd(u8 dec)
+{
+ return ((dec/10)<<4)+(dec%10);
+}
+
+static u8 bcd2dec(u8 bcd)
+{
+ return (bcd >> 4)*10+(bcd & 0xf);
+}
+
+static void convert_bcd_to_decimal(u8 *buf, u8 len)
+{
+ int i = 0;
+ for (i = 0; i < len; i++)
+ buf[i] = bcd2dec(buf[i]);
+}
+
+static void convert_decimal_to_bcd(u8 *buf, u8 len)
+{
+ int i = 0;
+ for (i = 0; i < len; i++)
+ buf[i] = dec2bcd(buf[i]);
+}
+
+static void print_time(struct device *dev, struct rtc_time *tm)
+{
+ dev_info(dev, "rtc-time : %d/%d/%d %d:%d\n",
+ (tm->tm_mon + 1), tm->tm_mday, (tm->tm_year + os_ref_year),
+ tm->tm_hour, tm->tm_min);
+}
+
+static int ricoh583_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ u8 buff[7];
+ int err;
+ err = ricoh583_read_regs(dev, rtc_seconds_reg, sizeof(buff), buff);
+ if (err < 0) {
+ dev_err(dev, "\n %s :: failed to read time\n", __FILE__);
+ return err;
+ }
+ convert_bcd_to_decimal(buff, sizeof(buff));
+ tm->tm_sec = buff[0];
+ tm->tm_min = buff[1];
+ tm->tm_hour = buff[2];
+ tm->tm_wday = buff[3];
+ tm->tm_mday = buff[4];
+ tm->tm_mon = buff[5] - 1;
+ tm->tm_year = buff[6] + rtc_year_offset;
+ print_time(dev, tm);
+ return ricoh583_rtc_valid_tm(dev, tm);
+}
+
+static int ricoh583_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ u8 buff[7];
+ int err;
+
+ print_time(dev, tm);
+ buff[0] = tm->tm_sec;
+ buff[1] = tm->tm_min;
+ buff[2] = tm->tm_hour;
+ buff[3] = tm->tm_wday;
+ buff[4] = tm->tm_mday;
+ buff[5] = tm->tm_mon + 1;
+ buff[6] = tm->tm_year - rtc_year_offset;
+
+ convert_decimal_to_bcd(buff, sizeof(buff));
+ err = ricoh583_write_regs(dev, rtc_seconds_reg, sizeof(buff), buff);
+ if (err < 0) {
+ dev_err(dev->parent, "\n failed to program new time\n");
+ return err;
+ }
+
+ return 0;
+}
+static int ricoh583_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm);
+
+static int ricoh583_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct ricoh583_rtc *rtc = dev_get_drvdata(dev);
+ unsigned long seconds;
+ u8 buff[5];
+ int err;
+ struct rtc_time tm;
+
+ if (rtc->irq == -1)
+ return -EIO;
+
+ rtc_tm_to_time(&alrm->time, &seconds);
+ ricoh583_rtc_read_time(dev, &tm);
+ rtc_tm_to_time(&tm, &rtc->epoch_start);
+ /*
+ work around: As YAL does not provide the seconds register,
+ program minute register to next minute, in cases when alarm
+ is requested within a minute from the current time.
+ */
+ if (seconds - rtc->epoch_start < 60)
+ alrm->time.tm_min += 1;
+ dev_info(dev->parent, "\n setting alarm to requested time::\n");
+ print_time(dev->parent, &alrm->time);
+
+ if (WARN_ON(alrm->enabled && (seconds < rtc->epoch_start))) {
+ dev_err(dev->parent, "\n can't set alarm to requested time\n");
+ return -EINVAL;
+ }
+
+ if (alrm->enabled && !rtc->irq_en)
+ rtc->irq_en = true;
+ else if (!alrm->enabled && rtc->irq_en)
+ rtc->irq_en = false;
+
+ buff[0] = alrm->time.tm_min;
+ buff[1] = alrm->time.tm_hour;
+ buff[2] = alrm->time.tm_mday;
+ buff[3] = alrm->time.tm_mon + 1;
+ buff[4] = alrm->time.tm_year - rtc_year_offset;
+ convert_decimal_to_bcd(buff, sizeof(buff));
+ err = ricoh583_write_regs(dev, rtc_alarm_y, sizeof(buff), buff);
+ if (err) {
+ dev_err(dev->parent, "\n unable to set alarm\n");
+ return -EBUSY;
+ }
+ buff[0] = 0x20; /* to enable alarm_y */
+ buff[1] = 0x20; /* to enable 24-hour format */
+ err = ricoh583_write_regs(dev, rtc_ctrl1, 2, buff);
+ if (err) {
+ dev_err(dev, "failed programming rtc ctrl regs\n");
+ return -EBUSY;
+ }
+return err;
+}
+
+static int ricoh583_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ u8 buff[5];
+ int err;
+
+ err = ricoh583_read_regs(dev, rtc_alarm_y, sizeof(buff), buff);
+ if (err)
+ return err;
+ convert_bcd_to_decimal(buff, sizeof(buff));
+
+ alrm->time.tm_min = buff[0];
+ alrm->time.tm_hour = buff[1];
+ alrm->time.tm_mday = buff[2];
+ alrm->time.tm_mon = buff[3] - 1;
+ alrm->time.tm_year = buff[4] + rtc_year_offset;
+
+ dev_info(dev->parent, "\n getting alarm time::\n");
+ print_time(dev, &alrm->time);
+
+ return 0;
+}
+
+static const struct rtc_class_ops ricoh583_rtc_ops = {
+ .read_time = ricoh583_rtc_read_time,
+ .set_time = ricoh583_rtc_set_time,
+ .set_alarm = ricoh583_rtc_set_alarm,
+ .read_alarm = ricoh583_rtc_read_alarm,
+};
+
+static irqreturn_t ricoh583_rtc_irq(int irq, void *data)
+{
+ struct device *dev = data;
+ struct ricoh583_rtc *rtc = dev_get_drvdata(dev);
+ u8 reg;
+ int err;
+
+ /* clear alarm-Y status bits.*/
+ err = ricoh583_read_regs(dev, rtc_ctrl2, 1, &reg);
+ if (err) {
+ dev_err(dev->parent, "unable to read rtc_ctrl2 reg\n");
+ return -EBUSY;
+ }
+ reg &= ~0x8;
+ err = ricoh583_write_regs(dev, rtc_ctrl2, 1, &reg);
+ if (err) {
+ dev_err(dev->parent, "unable to program rtc_status reg\n");
+ return -EBUSY;
+ }
+
+ rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF);
+ return IRQ_HANDLED;
+}
+
+static int ricoh583_rtc_probe(struct platform_device *pdev)
+{
+ struct ricoh583_rtc_platform_data *pdata = pdev->dev.platform_data;
+ struct ricoh583_rtc *rtc;
+ struct rtc_time tm;
+ int err;
+ u8 reg[2];
+ rtc = kzalloc(sizeof(*rtc), GFP_KERNEL);
+
+ if (!rtc)
+ return -ENOMEM;
+
+ rtc->irq = -1;
+
+ if (!pdata) {
+ dev_err(&pdev->dev, "no platform_data specified\n");
+ return -EINVAL;
+ }
+
+ if (pdata->irq < 0)
+ dev_err(&pdev->dev, "\n no irq specified, wakeup is disabled\n");
+
+ dev_set_drvdata(&pdev->dev, rtc);
+ device_init_wakeup(&pdev->dev, 1);
+ rtc->rtc = rtc_device_register(pdev->name, &pdev->dev,
+ &ricoh583_rtc_ops, THIS_MODULE);
+
+ if (IS_ERR(rtc->rtc)) {
+ err = PTR_ERR(rtc->rtc);
+ goto fail;
+ }
+ reg[0] = 0; /* clearing RTC Adjust register */
+ err = ricoh583_write_regs(&pdev->dev, rtc_adjust, 1, reg);
+ if (err) {
+ dev_err(&pdev->dev, "unable to program rtc_adjust reg\n");
+ return -EBUSY;
+ }
+
+ reg[0] = 0x20; /* to enable alarm_y */
+ reg[1] = 0x20; /* to enable 24-hour format */
+ err = ricoh583_write_regs(&pdev->dev, rtc_ctrl1, 2, reg);
+ if (err) {
+ dev_err(&pdev->dev, "failed rtc setup\n");
+ return -EBUSY;
+ }
+
+ ricoh583_rtc_read_time(&pdev->dev, &tm);
+ if (ricoh583_rtc_valid_tm(&pdev->dev, &tm)) {
+ if (pdata->time.tm_year < 2000 || pdata->time.tm_year > 2100) {
+ memset(&pdata->time, 0, sizeof(pdata->time));
+ pdata->time.tm_year = rtc_year_offset;
+ pdata->time.tm_mday = 1;
+ } else
+ pdata->time.tm_year -= os_ref_year;
+ ricoh583_rtc_set_time(&pdev->dev, &pdata->time);
+ }
+ if (pdata && (pdata->irq >= 0)) {
+ rtc->irq = pdata->irq;
+ err = request_threaded_irq(pdata->irq, NULL, ricoh583_rtc_irq,
+ IRQF_ONESHOT, "rtc_ricoh583",
+ &pdev->dev);
+ if (err) {
+ dev_err(&pdev->dev, "request IRQ:%d fail\n", rtc->irq);
+ rtc->irq = -1;
+ } else {
+ device_init_wakeup(&pdev->dev, 1);
+ enable_irq_wake(rtc->irq);
+ }
+ }
+ return 0;
+
+fail:
+ if (!IS_ERR_OR_NULL(rtc->rtc))
+ rtc_device_unregister(rtc->rtc);
+ kfree(rtc);
+ return err;
+}
+
+static int ricoh583_rtc_remove(struct platform_device *pdev)
+{
+ struct ricoh583_rtc *rtc = dev_get_drvdata(&pdev->dev);
+
+ if (rtc->irq != -1)
+ free_irq(rtc->irq, rtc);
+ rtc_device_unregister(rtc->rtc);
+ kfree(rtc);
+ return 0;
+}
+
+static struct platform_driver ricoh583_rtc_driver = {
+ .driver = {
+ .name = "rtc_ricoh583",
+ .owner = THIS_MODULE,
+ },
+ .probe = ricoh583_rtc_probe,
+ .remove = ricoh583_rtc_remove,
+};
+
+static int __init ricoh583_rtc_init(void)
+{
+ return platform_driver_register(&ricoh583_rtc_driver);
+}
+module_init(ricoh583_rtc_init);
+
+static void __exit ricoh583_rtc_exit(void)
+{
+ platform_driver_unregister(&ricoh583_rtc_driver);
+}
+module_exit(ricoh583_rtc_exit);
+
+MODULE_DESCRIPTION("RICOH PMU ricoh583 RTC driver");
+MODULE_AUTHOR("NVIDIA Corporation");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:rtc_ricoh583");
diff --git a/drivers/rtc/rtc-tegra.c b/drivers/rtc/rtc-tegra.c
index 76af92ad5a8a..9168a6b83f36 100644
--- a/drivers/rtc/rtc-tegra.c
+++ b/drivers/rtc/rtc-tegra.c
@@ -1,7 +1,8 @@
/*
* An RTC driver for the NVIDIA Tegra 200 series internal RTC.
*
- * Copyright (c) 2010, NVIDIA Corporation.
+ * Copyright (c) 2010-2011, NVIDIA Corporation.
+ * Copyright (c) 2010 Jon Mayo <jmayo@nvidia.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
diff --git a/drivers/rtc/rtc-tps6591x.c b/drivers/rtc/rtc-tps6591x.c
new file mode 100644
index 000000000000..1897be34730b
--- /dev/null
+++ b/drivers/rtc/rtc-tps6591x.c
@@ -0,0 +1,567 @@
+/*
+ * drivers/rtc/rtc_tps6591x.c
+ *
+ * RTC driver for TI TPS6591x
+ *
+ * Copyright (c) 2011-2012, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/* #define DEBUG 1 */
+/* #define VERBOSE_DEBUG 1 */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mfd/tps6591x.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+
+#define RTC_CTRL 0x10
+#define RTC_STATUS 0x11
+#define RTC_SECONDS_REG 0x0
+#define RTC_ALARM 0x8
+#define RTC_INT 0x12
+#define RTC_RESET_STATUS 0x16
+#define RTC_BBCH_REG 0x39
+
+#define RTC_BBCH_SEL 0x02
+#define RTC_BBCH_EN 0x01
+#define ENABLE_ALARM_INT 0x8
+#define RTC_RESET_VALUE 0x80
+#define ALARM_INT_STATUS 0x40
+
+/*
+Linux RTC driver refers 1900 as base year in many calculations.
+(e.g. refer drivers/rtc/rtc-lib.c)
+*/
+#define OS_REF_YEAR 1900
+
+/*
+ PMU RTC have only 2 nibbles to store year information, so using an offset
+ of 100 to set the base year as 2000 for our driver.
+*/
+#define RTC_YEAR_OFFSET 100
+
+struct tps6591x_rtc {
+ unsigned long epoch_start;
+ int irq;
+ struct rtc_device *rtc;
+ bool irq_en;
+};
+
+static int tps6591x_read_regs(struct device *dev, int reg, int len,
+ uint8_t *val)
+{
+ int ret;
+
+ /* dummy read of STATUS_REG as per data sheet */
+ ret = tps6591x_reads(dev->parent, RTC_STATUS, 1, val);
+ if (ret < 0) {
+ dev_err(dev->parent, "\n %s failed reading from RTC_STATUS\n",
+ __func__);
+ WARN_ON(1);
+ return ret;
+ }
+
+ ret = tps6591x_reads(dev->parent, reg, len, val);
+ if (ret < 0) {
+ dev_err(dev->parent, "\n %s failed reading from 0x%02x\n",
+ __func__, reg);
+ WARN_ON(1);
+ return ret;
+ }
+ return 0;
+}
+
+static int tps6591x_write_regs(struct device *dev, int reg, int len,
+ uint8_t *val)
+{
+ int ret;
+ ret = tps6591x_writes(dev->parent, reg, len, val);
+ if (ret < 0) {
+ dev_err(dev->parent, "\n %s failed writing\n", __func__);
+ WARN_ON(1);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int tps6591x_rtc_valid_tm(struct rtc_time *tm)
+{
+ if (tm->tm_year >= (RTC_YEAR_OFFSET + 99)
+ || tm->tm_year < (RTC_YEAR_OFFSET)
+ || tm->tm_mon >= 12
+ || tm->tm_mday < 1
+ || tm->tm_mday > rtc_month_days(tm->tm_mon, tm->tm_year + OS_REF_YEAR)
+ || tm->tm_hour >= 24
+ || tm->tm_min >= 60
+ || tm->tm_sec >= 60)
+ return -EINVAL;
+ return 0;
+}
+
+static u8 dec2bcd(u8 dec)
+{
+ return ((dec/10)<<4)+(dec%10);
+}
+
+static u8 bcd2dec(u8 bcd)
+{
+ return (bcd >> 4)*10+(bcd & 0xF);
+}
+
+static void convert_bcd_to_decimal(u8 *buf, u8 len)
+{
+ int i = 0;
+ for (i = 0; i < len; i++)
+ buf[i] = bcd2dec(buf[i]);
+}
+
+static void convert_decimal_to_bcd(u8 *buf, u8 len)
+{
+ int i = 0;
+ for (i = 0; i < len; i++)
+ buf[i] = dec2bcd(buf[i]);
+}
+
+static void print_time(struct device *dev, struct rtc_time *tm)
+{
+ dev_info(dev, "RTC Time : %d/%d/%d %d:%d:%d\n",
+ (tm->tm_mon + 1), tm->tm_mday, (tm->tm_year + OS_REF_YEAR),
+ tm->tm_hour, tm->tm_min , tm->tm_sec);
+}
+
+static int tps6591x_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ u8 buff[7];
+ int err;
+ err = tps6591x_read_regs(dev, RTC_SECONDS_REG, sizeof(buff), buff);
+ if (err < 0) {
+ dev_err(dev, "\n %s :: failed to read time\n", __FILE__);
+ return err;
+ }
+ convert_bcd_to_decimal(buff, sizeof(buff));
+ tm->tm_sec = buff[0];
+ tm->tm_min = buff[1];
+ tm->tm_hour = buff[2];
+ tm->tm_mday = buff[3];
+ tm->tm_mon = buff[4] - 1;
+ tm->tm_year = buff[5] + RTC_YEAR_OFFSET;
+ tm->tm_wday = buff[6];
+ print_time(dev, tm);
+ return tps6591x_rtc_valid_tm(tm);
+}
+
+static int tps6591x_rtc_stop(struct device *dev)
+{
+ u8 reg = 0;
+ u8 retries = 0;
+ int err;
+ do {
+ err = tps6591x_read_regs(dev, RTC_CTRL, 1, &reg);
+ if (err < 0) {
+ dev_err(dev->parent, "\n failed to read RTC_CTRL reg\n");
+ return err;
+ }
+
+ /* clear STOP bit alone */
+ reg &= ~0x1;
+
+ err = tps6591x_write_regs(dev, RTC_CTRL, 1, &reg);
+ if (err < 0) {
+ dev_err(dev->parent, "\n failed to program RTC_CTRL reg\n");
+ return err;
+ }
+
+ err = tps6591x_read_regs(dev, RTC_STATUS, 1, &reg);
+ if (err < 0) {
+ dev_err(dev->parent, "\n failed to read RTC_CTRL reg\n");
+ return err;
+ }
+ /* FixMe: Is allowing up to 5 retries sufficient?? */
+ if (retries++ == 5) {
+ dev_err(dev->parent, "\n failed to stop RTC\n");
+ return -EBUSY;
+ }
+ } while (reg & 2);
+ return 0;
+}
+
+static int tps6591x_rtc_start(struct device *dev)
+{
+ u8 reg = 0;
+ u8 retries = 0;
+ int err;
+
+ do {
+ err = tps6591x_read_regs(dev, RTC_CTRL, 1, &reg);
+ if (err < 0) {
+ dev_err(dev->parent, "\n failed to read RTC_CTRL reg\n");
+ return err;
+ }
+
+ /* set STOP bit alone */
+ reg |= 0x1;
+
+ err = tps6591x_write_regs(dev, RTC_CTRL, 1, &reg);
+ if (err < 0) {
+ dev_err(dev->parent, "\n failed to program RTC_CTRL reg\n");
+ return err;
+ }
+
+ err = tps6591x_read_regs(dev, RTC_STATUS, 1, &reg);
+ if (err < 0) {
+ dev_err(dev->parent, "\n failed to read RTC_CTRL reg\n");
+ return err;
+ }
+ /* FixMe: Is allowing up to 5 retries sufficient?? */
+ if (retries++ == 5) {
+ dev_err(dev->parent, "\n failed to stop RTC\n");
+ return -EBUSY;
+ }
+ } while (!(reg & 2));
+ return 0;
+}
+
+
+static int tps6591x_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ u8 buff[7];
+ int err;
+
+ err = tps6591x_rtc_valid_tm(tm);
+ if (err < 0) {
+ dev_err(dev->parent, "\n Invalid Time\n");
+ return err;
+ }
+
+ buff[0] = tm->tm_sec;
+ buff[1] = tm->tm_min;
+ buff[2] = tm->tm_hour;
+ buff[3] = tm->tm_mday;
+ buff[4] = tm->tm_mon + 1;
+ buff[5] = tm->tm_year % RTC_YEAR_OFFSET;
+ buff[6] = tm->tm_wday;
+
+ print_time(dev, tm);
+ convert_decimal_to_bcd(buff, sizeof(buff));
+ err = tps6591x_rtc_stop(dev);
+ if (err < 0) {
+ dev_err(dev->parent, "\n failed to clear RTC_ENABLE\n");
+ return err;
+ }
+
+ err = tps6591x_write_regs(dev, RTC_SECONDS_REG, sizeof(buff), buff);
+ if (err < 0) {
+ dev_err(dev->parent, "\n failed to program new time\n");
+ return err;
+ }
+
+ err = tps6591x_rtc_start(dev);
+ if (err < 0) {
+ dev_err(dev->parent, "\n failed to set RTC_ENABLE\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static int tps6591x_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enable)
+{
+ struct tps6591x_rtc *rtc = dev_get_drvdata(dev);
+ u8 reg;
+ int err;
+
+ if (rtc->irq == -1)
+ return -EIO;
+
+ if (enable) {
+ if (rtc->irq_en == true)
+ return 0;
+ err = tps6591x_read_regs(dev, RTC_INT, 1, &reg);
+ if (err)
+ return err;
+ reg |= 0x8;
+ err = tps6591x_write_regs(dev, RTC_INT, 1, &reg);
+ if (err)
+ return err;
+ rtc->irq_en = true;
+ } else {
+ if (rtc->irq_en == false)
+ return 0;
+ err = tps6591x_read_regs(dev, RTC_INT, 1, &reg);
+ if (err)
+ return err;
+ reg &= ~0x8;
+ err = tps6591x_write_regs(dev, RTC_INT, 1, &reg);
+ if (err)
+ return err;
+ rtc->irq_en = false;
+ }
+ return 0;
+}
+
+static int tps6591x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct tps6591x_rtc *rtc = dev_get_drvdata(dev);
+ unsigned long seconds;
+ u8 buff[6];
+ int err;
+ struct rtc_time tm;
+
+ if (rtc->irq == -1)
+ return -EIO;
+
+ err = tps6591x_rtc_valid_tm(&alrm->time);
+ if (err < 0) {
+ dev_err(dev->parent, "\n Invalid alarm time\n");
+ return err;
+ }
+
+ dev_info(dev->parent, "\n setting alarm to requested time::\n");
+ print_time(dev->parent, &alrm->time);
+ rtc_tm_to_time(&alrm->time, &seconds);
+ tps6591x_rtc_read_time(dev, &tm);
+ rtc_tm_to_time(&tm, &rtc->epoch_start);
+
+ if (WARN_ON(alrm->enabled && (seconds < rtc->epoch_start))) {
+ dev_err(dev->parent, "\n can't set alarm to requested time\n");
+ return -EINVAL;
+ }
+
+ err = tps6591x_rtc_alarm_irq_enable(dev, alrm->enabled);
+ if(err) {
+ dev_err(dev->parent, "\n can't set alarm irq\n");
+ return err;
+ }
+
+ buff[0] = alrm->time.tm_sec;
+ buff[1] = alrm->time.tm_min;
+ buff[2] = alrm->time.tm_hour;
+ buff[3] = alrm->time.tm_mday;
+ buff[4] = alrm->time.tm_mon + 1;
+ buff[5] = alrm->time.tm_year % RTC_YEAR_OFFSET;
+ convert_decimal_to_bcd(buff, sizeof(buff));
+ err = tps6591x_write_regs(dev, RTC_ALARM, sizeof(buff), buff);
+ if (err)
+ dev_err(dev->parent, "\n unable to program alarm\n");
+
+ return err;
+}
+
+static int tps6591x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ u8 buff[6];
+ int err;
+
+ err = tps6591x_read_regs(dev, RTC_ALARM, sizeof(buff), buff);
+ if (err)
+ return err;
+ convert_bcd_to_decimal(buff, sizeof(buff));
+
+ alrm->time.tm_sec = buff[0];
+ alrm->time.tm_min = buff[1];
+ alrm->time.tm_hour = buff[2];
+ alrm->time.tm_mday = buff[3];
+ alrm->time.tm_mon = buff[4] - 1;
+ alrm->time.tm_year = buff[5] + RTC_YEAR_OFFSET;
+
+ dev_info(dev->parent, "\n getting alarm time::\n");
+ print_time(dev, &alrm->time);
+
+ return 0;
+}
+
+static const struct rtc_class_ops tps6591x_rtc_ops = {
+ .read_time = tps6591x_rtc_read_time,
+ .set_time = tps6591x_rtc_set_time,
+ .set_alarm = tps6591x_rtc_set_alarm,
+ .read_alarm = tps6591x_rtc_read_alarm,
+ .alarm_irq_enable = tps6591x_rtc_alarm_irq_enable,
+};
+
+static irqreturn_t tps6591x_rtc_irq(int irq, void *data)
+{
+ struct device *dev = data;
+ struct tps6591x_rtc *rtc = dev_get_drvdata(dev);
+ u8 reg;
+ int err;
+
+ /* clear Alarm status bits.*/
+ err = tps6591x_read_regs(dev, RTC_STATUS, 1, &reg);
+ if (err) {
+ dev_err(dev->parent, "unable to read RTC_STATUS reg\n");
+ return -EBUSY;
+ }
+
+ reg = ALARM_INT_STATUS;
+ err = tps6591x_write_regs(dev, RTC_STATUS, 1, &reg);
+ if (err) {
+ dev_err(dev->parent, "unable to program RTC_STATUS reg\n");
+ return -EBUSY;
+ }
+
+ rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF);
+ return IRQ_HANDLED;
+}
+
+static int tps6591x_rtc_probe(struct platform_device *pdev)
+{
+ struct tps6591x_rtc_platform_data *pdata = pdev->dev.platform_data;
+ struct tps6591x_rtc *rtc;
+ struct rtc_time tm;
+ int err;
+ u8 reg;
+
+ rtc = kzalloc(sizeof(*rtc), GFP_KERNEL);
+
+ if (!rtc)
+ return -ENOMEM;
+
+ rtc->irq = -1;
+
+ if (!pdata) {
+ dev_err(&pdev->dev, "no platform_data specified\n");
+ return -EINVAL;
+ }
+
+ if (pdata->irq < 0)
+ dev_err(&pdev->dev, "\n no IRQ specified, wakeup is disabled\n");
+
+ dev_set_drvdata(&pdev->dev, rtc);
+ device_init_wakeup(&pdev->dev, 1);
+ rtc->rtc = rtc_device_register(pdev->name, &pdev->dev,
+ &tps6591x_rtc_ops, THIS_MODULE);
+
+ if (IS_ERR(rtc->rtc)) {
+ err = PTR_ERR(rtc->rtc);
+ goto fail;
+ }
+
+ if ((int)pdev && (int)&pdev->dev)
+ err = tps6591x_read_regs(&pdev->dev, RTC_STATUS, 1, &reg);
+ else {
+ dev_err(&pdev->dev, "\n %s Input params incorrect\n", __func__);
+ return -EBUSY;
+ }
+ if (err) {
+ dev_err(&pdev->dev, "\n %s unable to read status\n", __func__);
+ return -EBUSY;
+ }
+
+ reg = RTC_BBCH_SEL | RTC_BBCH_EN;
+ tps6591x_write_regs(&pdev->dev, RTC_BBCH_REG, 1, &reg);
+ if (err) {
+ dev_err(&pdev->dev, "unable to program Charger reg\n");
+ return -EBUSY;
+ }
+
+ err = tps6591x_rtc_start(&pdev->dev);
+ if (err) {
+ dev_err(&pdev->dev, "unable to start RTC\n");
+ return -EBUSY;
+ }
+
+ tps6591x_rtc_read_time(&pdev->dev, &tm);
+
+ if (tps6591x_rtc_valid_tm(&tm) < 0) {
+ if (pdata->time.tm_year < 2000 || pdata->time.tm_year >= 2100) {
+ memset(&pdata->time, 0, sizeof(pdata->time));
+ pdata->time.tm_year = 2000;
+ pdata->time.tm_mday = 1;
+ }
+ pdata->time.tm_year -= OS_REF_YEAR;
+ tps6591x_rtc_set_time(&pdev->dev, &pdata->time);
+ }
+
+ reg = ALARM_INT_STATUS;
+ err = tps6591x_write_regs(&pdev->dev, RTC_STATUS, 1, &reg);
+ if (err) {
+ dev_err(&pdev->dev, "unable to program RTC_STATUS reg\n");
+ return -EBUSY;
+ }
+
+ reg = ENABLE_ALARM_INT;
+ tps6591x_write_regs(&pdev->dev, RTC_INT, 1, &reg);
+ if (err) {
+ dev_err(&pdev->dev, "unable to program Interrupt Mask reg\n");
+ return -EBUSY;
+ }
+
+ if (pdata && (pdata->irq >= 0)) {
+ rtc->irq = pdata->irq;
+ err = request_threaded_irq(pdata->irq, NULL, tps6591x_rtc_irq,
+ IRQF_ONESHOT, "rtc_tps6591x",
+ &pdev->dev);
+ if (err) {
+ dev_err(&pdev->dev, "request IRQ:%d fail\n", rtc->irq);
+ rtc->irq = -1;
+ } else {
+ device_init_wakeup(&pdev->dev, 1);
+ enable_irq_wake(rtc->irq);
+ }
+ }
+ return 0;
+
+fail:
+ if (!IS_ERR_OR_NULL(rtc->rtc))
+ rtc_device_unregister(rtc->rtc);
+ kfree(rtc);
+ return err;
+}
+
+static int tps6591x_rtc_remove(struct platform_device *pdev)
+{
+ struct tps6591x_rtc *rtc = dev_get_drvdata(&pdev->dev);
+
+ if (rtc->irq != -1)
+ free_irq(rtc->irq, rtc);
+ rtc_device_unregister(rtc->rtc);
+ kfree(rtc);
+ return 0;
+}
+
+static struct platform_driver tps6591x_rtc_driver = {
+ .driver = {
+ .name = "rtc_tps6591x",
+ .owner = THIS_MODULE,
+ },
+ .probe = tps6591x_rtc_probe,
+ .remove = tps6591x_rtc_remove,
+};
+
+static int __init tps6591x_rtc_init(void)
+{
+ return platform_driver_register(&tps6591x_rtc_driver);
+}
+module_init(tps6591x_rtc_init);
+
+static void __exit tps6591x_rtc_exit(void)
+{
+ platform_driver_unregister(&tps6591x_rtc_driver);
+}
+module_exit(tps6591x_rtc_exit);
+
+MODULE_DESCRIPTION("TI TPS6591x RTC driver");
+MODULE_AUTHOR("NVIDIA Corporation");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:rtc_tps6591x");