summaryrefslogtreecommitdiff
path: root/drivers/rtc
diff options
context:
space:
mode:
authorLaxman Dewangan <ldewangan@nvidia.com>2013-11-08 15:37:55 +0530
committerLaxman Dewangan <ldewangan@nvidia.com>2013-11-09 02:58:53 -0800
commitd03b078ff879554c3a02f55e91c66d90562c2a7a (patch)
tree93b1fe1e5f4a09efe9348d0f06c3c54ce0197882 /drivers/rtc
parent7e305905f2baf97ddaacf8e9b4497da648ca6bb8 (diff)
rtc: as3722: align driver with mainline
Align the RTC driver of ams AS3722 based on mainline: /** commit a1e01867211112691e80701a01ed9900655f7fe5 drivers/rtc/rtc-as3722: add RTC driver Add a driver to support accessing the RTC found on the ams AS3722 PMIC using RTC framework. Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com> Signed-off-by: Florian Lobmaier <florian.lobmaier@ams.com> **/ (Cherrypicked commit from a1e01867211112691e80701a01ed9900655f7fe5) Fixed compilation issue happened during integration. Integration done by: bbasu and ldewangan Change-Id: I68812519d7ff2263193d2b4c91da71037cfd43fb Signed-off-by: Bibek Basu <bbasu@nvidia.com> Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com> Reviewed-on: http://git-master/r/328289 Reviewed-by: Automatic_Commit_Validation_User GVS: Gerrit_Virtual_Submit
Diffstat (limited to 'drivers/rtc')
-rw-r--r--drivers/rtc/Kconfig6
-rw-r--r--drivers/rtc/rtc-as3722.c398
2 files changed, 162 insertions, 242 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 64f33eabf909..b8532a08fd6f 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -134,11 +134,11 @@ comment "I2C RTC drivers"
if I2C
config RTC_DRV_AS3722
- tristate "ams AS3722"
+ tristate "ams AS3722 RTC driver"
depends on MFD_AS3722
help
- If you say Y here you will get support for the RTC feature
- of the ams AS3722 PMIC.
+ If you say yes here you get support for the RTC of ams AS3722 PMIC
+ chips.
This driver can also be built as a module. If so, the module
will be called rtc-as3722.
diff --git a/drivers/rtc/rtc-as3722.c b/drivers/rtc/rtc-as3722.c
index 7f76102d2b8a..a796e8f04c92 100644
--- a/drivers/rtc/rtc-as3722.c
+++ b/drivers/rtc/rtc-as3722.c
@@ -5,6 +5,7 @@
* Copyright (c) 2013, NVIDIA Corporation. All rights reserved.
*
* Author: Florian Lobmaier <florian.lobmaier@ams.com>
+ * Author: Laxman Dewangan <ldewangan@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
@@ -22,325 +23,244 @@
*
*/
-#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/interrupt.h>
+#include <linux/ioctl.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mfd/as3722.h>
#include <linux/platform_device.h>
-#include <linux/mfd/as3722-reg.h>
-#include <linux/mfd/as3722-plat.h>
+#include <linux/rtc.h>
+#include <linux/time.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
+#define AS3722_RTC_START_YEAR 2000
+struct as3722_rtc {
+ struct rtc_device *rtc;
+ struct device *dev;
+ struct as3722 *as3722;
+ int alarm_irq;
+ bool irq_enable;
+};
-/*
- * Read current time and date in RTC
- */
-static int as3722_rtc_readtime(struct device *dev, struct rtc_time *tm)
+static void as3722_time_to_reg(u8 *rbuff, struct rtc_time *tm)
+{
+ rbuff[0] = bin2bcd(tm->tm_sec);
+ rbuff[1] = bin2bcd(tm->tm_min);
+ rbuff[2] = bin2bcd(tm->tm_hour);
+ rbuff[3] = bin2bcd(tm->tm_mday);
+ rbuff[4] = bin2bcd(tm->tm_mon);
+ rbuff[5] = bin2bcd(tm->tm_year - (AS3722_RTC_START_YEAR - 1900));
+}
+
+static void as3722_reg_to_time(u8 *rbuff, struct rtc_time *tm)
+{
+ tm->tm_sec = bcd2bin(rbuff[0] & 0x7F);
+ tm->tm_min = bcd2bin(rbuff[1] & 0x7F);
+ tm->tm_hour = bcd2bin(rbuff[2] & 0x3F);
+ tm->tm_mday = bcd2bin(rbuff[3] & 0x3F);
+ tm->tm_mon = bcd2bin(rbuff[4] & 0x1F);
+ tm->tm_year = (AS3722_RTC_START_YEAR - 1900) + bcd2bin(rbuff[5] & 0x7F);
+ return;
+}
+
+static int as3722_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
- struct as3722 *as3722 = dev_get_drvdata(dev->parent);
+ struct as3722_rtc *as3722_rtc = dev_get_drvdata(dev);
+ struct as3722 *as3722 = as3722_rtc->as3722;
u8 as_time_array[6];
int ret;
ret = as3722_block_read(as3722, AS3722_RTC_SECOND_REG,
6, as_time_array);
- if (ret < 0)
+ if (ret < 0) {
+ dev_err(dev, "RTC_SECOND reg block read failed %d\n", ret);
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);
-
+ }
+ as3722_reg_to_time(as_time_array, tm);
return 0;
}
-/*
- * Set current time and date in RTC
- */
-static int as3722_rtc_settime(struct device *dev, struct rtc_time *tm)
+static int as3722_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
- struct as3722 *as3722 = dev_get_drvdata(dev->parent);
+ struct as3722_rtc *as3722_rtc = dev_get_drvdata(dev);
+ struct as3722 *as3722 = as3722_rtc->as3722;
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;
+ if (tm->tm_year < (AS3722_RTC_START_YEAR - 1900))
+ return -EINVAL;
+ as3722_time_to_reg(as_time_array, tm);
ret = as3722_block_write(as3722, AS3722_RTC_SECOND_REG, 6,
as_time_array);
-
+ if (ret < 0)
+ dev_err(dev, "RTC_SECOND reg block write failed %d\n", ret);
return ret;
}
-/*
- * Read alarm time and date in RTC
- */
-static int as3722_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
+static int as3722_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct as3722_rtc *as3722_rtc = dev_get_drvdata(dev);
+
+ if (enabled && !as3722_rtc->irq_enable) {
+ enable_irq(as3722_rtc->alarm_irq);
+ as3722_rtc->irq_enable = true;
+ } else if (!enabled && as3722_rtc->irq_enable) {
+ disable_irq(as3722_rtc->alarm_irq);
+ as3722_rtc->irq_enable = false;
+ }
+ return 0;
+}
+
+static int as3722_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
- struct as3722 *as3722 = dev_get_drvdata(dev->parent);
+ struct as3722_rtc *as3722_rtc = dev_get_drvdata(dev);
+ struct as3722 *as3722 = as3722_rtc->as3722;
u8 as_time_array[6];
int ret;
- ret = as3722_block_read(as3722, AS3722_RTC_SECOND_REG,
- 6, as_time_array);
- if (ret < 0)
+ ret = as3722_block_read(as3722, AS3722_RTC_ALARM_SECOND_REG, 6,
+ as_time_array);
+ if (ret < 0) {
+ dev_err(dev, "RTC_ALARM_SECOND block read failed %d\n", ret);
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);
-
+ as3722_reg_to_time(as_time_array, &alrm->time);
return 0;
}
-static int as3722_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
+static int as3722_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
- struct as3722 *as3722 = dev_get_drvdata(dev->parent);
+ struct as3722_rtc *as3722_rtc = dev_get_drvdata(dev);
+ struct as3722 *as3722 = as3722_rtc->as3722;
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;
+ if (alrm->time.tm_year < (AS3722_RTC_START_YEAR - 1900))
+ return -EINVAL;
- ret = as3722_block_write(as3722, AS3722_RTC_SECOND_REG, 6,
+ ret = as3722_rtc_alarm_irq_enable(dev, 0);
+ if (ret < 0) {
+ dev_err(dev, "Disable RTC alarm failed\n");
+ return ret;
+ }
+
+ as3722_time_to_reg(as_time_array, &alrm->time);
+ ret = as3722_block_write(as3722, AS3722_RTC_ALARM_SECOND_REG, 6,
as_time_array);
+ if (ret < 0) {
+ dev_err(dev, "RTC_ALARM_SECOND block write failed %d\n", ret);
+ return ret;
+ }
+ if (alrm->enabled)
+ ret = as3722_rtc_alarm_irq_enable(dev, alrm->enabled);
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);
+ struct as3722_rtc *as3722_rtc = data;
+ rtc_update_irq(as3722_rtc->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,
+ .read_time = as3722_rtc_read_time,
+ .set_time = as3722_rtc_set_time,
+ .read_alarm = as3722_rtc_read_alarm,
+ .set_alarm = as3722_rtc_set_alarm,
.alarm_irq_enable = as3722_rtc_alarm_irq_enable,
};
-#ifdef CONFIG_PM
-static int as3722_rtc_suspend(struct device *dev)
+static int as3722_rtc_probe(struct platform_device *pdev)
{
- struct platform_device *pdev = to_platform_device(dev);
struct as3722 *as3722 = dev_get_drvdata(pdev->dev.parent);
- int ret = 0;
- u32 reg;
+ struct as3722_rtc *as3722_rtc;
+ int ret;
- as3722_reg_read(as3722, AS3722_INTERRUPTMASK3_REG, &reg);
+ as3722_rtc = devm_kzalloc(&pdev->dev, sizeof(*as3722_rtc), GFP_KERNEL);
+ if (!as3722_rtc)
+ return -ENOMEM;
- 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);
- }
+ as3722_rtc->as3722 = as3722;
+ as3722_rtc->dev = &pdev->dev;
+ platform_set_drvdata(pdev, as3722_rtc);
- return ret;
-}
+ /* Enable the RTC to make sure it is running. */
+ ret = as3722_update_bits(as3722, AS3722_RTC_CONTROL_REG,
+ AS3722_RTC_ON | AS3722_RTC_ALARM_WAKEUP_EN,
+ AS3722_RTC_ON | AS3722_RTC_ALARM_WAKEUP_EN);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "RTC_CONTROL reg write failed: %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;
+ device_init_wakeup(&pdev->dev, 1);
- 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);
+ as3722_rtc->rtc = rtc_device_register("as3722", &pdev->dev,
+ &as3722_rtc_ops, THIS_MODULE);
+ if (IS_ERR(as3722_rtc->rtc)) {
+ ret = PTR_ERR(as3722_rtc->rtc);
+ dev_err(&pdev->dev, "RTC register failed: %d\n", ret);
+ return ret;
}
+ as3722_rtc->alarm_irq = platform_get_irq(pdev, 0);
+ dev_info(&pdev->dev, "RTC interrupt %d\n", as3722_rtc->alarm_irq);
+
+ ret = request_threaded_irq(as3722_rtc->alarm_irq, NULL,
+ as3722_alarm_irq, IRQF_ONESHOT | IRQF_EARLY_RESUME,
+ "rtc-alarm", as3722_rtc);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to request alarm IRQ %d: %d\n",
+ as3722_rtc->alarm_irq, ret);
+ goto scrub;
+ }
+ disable_irq(as3722_rtc->alarm_irq);
return 0;
+scrub:
+ rtc_device_unregister(as3722_rtc->rtc);
+ return ret;
}
-/* Unconditionally disable the alarm */
-static int as3722_rtc_freeze(struct device *dev)
+static int as3722_rtc_remove(struct platform_device *pdev)
{
- 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);
+ struct as3722_rtc *as3722_rtc = platform_get_drvdata(pdev);
+ free_irq(as3722_rtc->alarm_irq, as3722_rtc);
+ rtc_device_unregister(as3722_rtc->rtc);
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)
+#ifdef CONFIG_PM_SLEEP
+static int as3722_rtc_suspend(struct device *dev)
{
- 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);
- int ret = 0;
- u32 ctrl;
-
- /* 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;
- }
- }
+ struct as3722_rtc *as3722_rtc = dev_get_drvdata(dev);
- /* 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,
- IRQF_ONESHOT | IRQF_EARLY_RESUME, "RTC alarm",
- rtc->rtc);
- if (ret != 0) {
- dev_err(&pdev->dev, "Failed to request alarm IRQ %d: %d\n",
- alarm_irq, ret);
- }
+ if (device_may_wakeup(dev))
+ enable_irq_wake(as3722_rtc->alarm_irq);
return 0;
}
-static int as3722_rtc_remove(struct platform_device *pdev)
+static int as3722_rtc_resume(struct device *dev)
{
- 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);
+ struct as3722_rtc *as3722_rtc = dev_get_drvdata(dev);
+ if (device_may_wakeup(dev))
+ disable_irq_wake(as3722_rtc->alarm_irq);
return 0;
}
+#endif
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,
+ SET_SYSTEM_SLEEP_PM_OPS(as3722_rtc_suspend, as3722_rtc_resume)
};
static struct platform_driver as3722_rtc_driver = {
@@ -348,13 +268,13 @@ static struct platform_driver as3722_rtc_driver = {
.remove = as3722_rtc_remove,
.driver = {
.name = "as3722-rtc",
- .pm = DEV_PM_OPS,
+ .pm = &as3722_rtc_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");
+MODULE_AUTHOR("Florian Lobmaier <florian.lobmaier@ams.com>");
+MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
+MODULE_LICENSE("GPL");