diff options
author | Laxman Dewangan <ldewangan@nvidia.com> | 2013-11-08 15:37:55 +0530 |
---|---|---|
committer | Laxman Dewangan <ldewangan@nvidia.com> | 2013-11-09 02:58:53 -0800 |
commit | d03b078ff879554c3a02f55e91c66d90562c2a7a (patch) | |
tree | 93b1fe1e5f4a09efe9348d0f06c3c54ce0197882 /drivers/rtc | |
parent | 7e305905f2baf97ddaacf8e9b4497da648ca6bb8 (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/Kconfig | 6 | ||||
-rw-r--r-- | drivers/rtc/rtc-as3722.c | 398 |
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, ®); + 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"); |