diff options
Diffstat (limited to 'drivers/rtc')
-rw-r--r-- | drivers/rtc/Kconfig | 10 | ||||
-rw-r--r-- | drivers/rtc/Makefile | 1 | ||||
-rw-r--r-- | drivers/rtc/rtc-at91rm9200.c | 50 | ||||
-rw-r--r-- | drivers/rtc/rtc-at91rm9200.h | 1 | ||||
-rw-r--r-- | drivers/rtc/rtc-da9052.c | 8 | ||||
-rw-r--r-- | drivers/rtc/rtc-mv.c | 28 | ||||
-rw-r--r-- | drivers/rtc/rtc-palmas.c | 339 | ||||
-rw-r--r-- | drivers/rtc/rtc-stmp3xxx.c | 64 |
8 files changed, 472 insertions, 29 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index e6ab071fb6fd..79fbe3832dfc 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -305,6 +305,16 @@ 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 diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index e8f2e2fee06f..c33f86f1a69b 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -81,6 +81,7 @@ 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 diff --git a/drivers/rtc/rtc-at91rm9200.c b/drivers/rtc/rtc-at91rm9200.c index 434ebc3a99dc..0a9f27e094ea 100644 --- a/drivers/rtc/rtc-at91rm9200.c +++ b/drivers/rtc/rtc-at91rm9200.c @@ -44,6 +44,7 @@ static DECLARE_COMPLETION(at91_rtc_updated); static unsigned int at91_alarm_year = AT91_RTC_EPOCH; static void __iomem *at91_rtc_regs; static int irq; +static u32 at91_rtc_imr; /* * Decode time/date into rtc_time structure @@ -108,9 +109,11 @@ static int at91_rtc_settime(struct device *dev, struct rtc_time *tm) cr = at91_rtc_read(AT91_RTC_CR); at91_rtc_write(AT91_RTC_CR, cr | AT91_RTC_UPDCAL | AT91_RTC_UPDTIM); + at91_rtc_imr |= AT91_RTC_ACKUPD; at91_rtc_write(AT91_RTC_IER, AT91_RTC_ACKUPD); wait_for_completion(&at91_rtc_updated); /* wait for ACKUPD interrupt */ at91_rtc_write(AT91_RTC_IDR, AT91_RTC_ACKUPD); + at91_rtc_imr &= ~AT91_RTC_ACKUPD; at91_rtc_write(AT91_RTC_TIMR, bin2bcd(tm->tm_sec) << 0 @@ -142,7 +145,7 @@ static int at91_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm) tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year); tm->tm_year = at91_alarm_year - 1900; - alrm->enabled = (at91_rtc_read(AT91_RTC_IMR) & AT91_RTC_ALARM) + alrm->enabled = (at91_rtc_imr & AT91_RTC_ALARM) ? 1 : 0; dev_dbg(dev, "%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __func__, @@ -168,6 +171,7 @@ static int at91_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) tm.tm_sec = alrm->time.tm_sec; at91_rtc_write(AT91_RTC_IDR, AT91_RTC_ALARM); + at91_rtc_imr &= ~AT91_RTC_ALARM; at91_rtc_write(AT91_RTC_TIMALR, bin2bcd(tm.tm_sec) << 0 | bin2bcd(tm.tm_min) << 8 @@ -180,6 +184,7 @@ static int at91_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) if (alrm->enabled) { at91_rtc_write(AT91_RTC_SCCR, AT91_RTC_ALARM); + at91_rtc_imr |= AT91_RTC_ALARM; at91_rtc_write(AT91_RTC_IER, AT91_RTC_ALARM); } @@ -196,9 +201,12 @@ static int at91_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) if (enabled) { at91_rtc_write(AT91_RTC_SCCR, AT91_RTC_ALARM); + at91_rtc_imr |= AT91_RTC_ALARM; at91_rtc_write(AT91_RTC_IER, AT91_RTC_ALARM); - } else + } else { at91_rtc_write(AT91_RTC_IDR, AT91_RTC_ALARM); + at91_rtc_imr &= ~AT91_RTC_ALARM; + } return 0; } @@ -207,12 +215,10 @@ static int at91_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) */ static int at91_rtc_proc(struct device *dev, struct seq_file *seq) { - unsigned long imr = at91_rtc_read(AT91_RTC_IMR); - seq_printf(seq, "update_IRQ\t: %s\n", - (imr & AT91_RTC_ACKUPD) ? "yes" : "no"); + (at91_rtc_imr & AT91_RTC_ACKUPD) ? "yes" : "no"); seq_printf(seq, "periodic_IRQ\t: %s\n", - (imr & AT91_RTC_SECEV) ? "yes" : "no"); + (at91_rtc_imr & AT91_RTC_SECEV) ? "yes" : "no"); return 0; } @@ -227,7 +233,7 @@ static irqreturn_t at91_rtc_interrupt(int irq, void *dev_id) unsigned int rtsr; unsigned long events = 0; - rtsr = at91_rtc_read(AT91_RTC_SR) & at91_rtc_read(AT91_RTC_IMR); + rtsr = at91_rtc_read(AT91_RTC_SR) & at91_rtc_imr; if (rtsr) { /* this interrupt is shared! Is it ours? */ if (rtsr & AT91_RTC_ALARM) events |= (RTC_AF | RTC_IRQF); @@ -291,6 +297,7 @@ static int __init at91_rtc_probe(struct platform_device *pdev) at91_rtc_write(AT91_RTC_IDR, AT91_RTC_ACKUPD | AT91_RTC_ALARM | AT91_RTC_SECEV | AT91_RTC_TIMEV | AT91_RTC_CALEV); + at91_rtc_imr = 0; ret = request_irq(irq, at91_rtc_interrupt, IRQF_SHARED, @@ -329,6 +336,7 @@ static int __exit at91_rtc_remove(struct platform_device *pdev) at91_rtc_write(AT91_RTC_IDR, AT91_RTC_ACKUPD | AT91_RTC_ALARM | AT91_RTC_SECEV | AT91_RTC_TIMEV | AT91_RTC_CALEV); + at91_rtc_imr = 0; free_irq(irq, pdev); rtc_device_unregister(rtc); @@ -341,31 +349,35 @@ static int __exit at91_rtc_remove(struct platform_device *pdev) /* AT91RM9200 RTC Power management control */ -static u32 at91_rtc_imr; +static u32 at91_rtc_bkpimr; + static int at91_rtc_suspend(struct device *dev) { /* this IRQ is shared with DBGU and other hardware which isn't * necessarily doing PM like we are... */ - at91_rtc_imr = at91_rtc_read(AT91_RTC_IMR) - & (AT91_RTC_ALARM|AT91_RTC_SECEV); - if (at91_rtc_imr) { - if (device_may_wakeup(dev)) + at91_rtc_bkpimr = at91_rtc_imr & (AT91_RTC_ALARM|AT91_RTC_SECEV); + if (at91_rtc_bkpimr) { + if (device_may_wakeup(dev)) { enable_irq_wake(irq); - else - at91_rtc_write(AT91_RTC_IDR, at91_rtc_imr); - } + } else { + at91_rtc_write(AT91_RTC_IDR, at91_rtc_bkpimr); + at91_rtc_imr &= ~at91_rtc_bkpimr; + } +} return 0; } static int at91_rtc_resume(struct device *dev) { - if (at91_rtc_imr) { - if (device_may_wakeup(dev)) + if (at91_rtc_bkpimr) { + if (device_may_wakeup(dev)) { disable_irq_wake(irq); - else - at91_rtc_write(AT91_RTC_IER, at91_rtc_imr); + } else { + at91_rtc_imr |= at91_rtc_bkpimr; + at91_rtc_write(AT91_RTC_IER, at91_rtc_bkpimr); + } } return 0; } diff --git a/drivers/rtc/rtc-at91rm9200.h b/drivers/rtc/rtc-at91rm9200.h index da1945e5f714..5f940b6844cb 100644 --- a/drivers/rtc/rtc-at91rm9200.h +++ b/drivers/rtc/rtc-at91rm9200.h @@ -64,7 +64,6 @@ #define AT91_RTC_SCCR 0x1c /* Status Clear Command Register */ #define AT91_RTC_IER 0x20 /* Interrupt Enable Register */ #define AT91_RTC_IDR 0x24 /* Interrupt Disable Register */ -#define AT91_RTC_IMR 0x28 /* Interrupt Mask Register */ #define AT91_RTC_VER 0x2c /* Valid Entry Register */ #define AT91_RTC_NVTIM (1 << 0) /* Non valid Time */ diff --git a/drivers/rtc/rtc-da9052.c b/drivers/rtc/rtc-da9052.c index 0dde688ca09b..969abbad7fe3 100644 --- a/drivers/rtc/rtc-da9052.c +++ b/drivers/rtc/rtc-da9052.c @@ -239,11 +239,9 @@ static int da9052_rtc_probe(struct platform_device *pdev) rtc->da9052 = dev_get_drvdata(pdev->dev.parent); platform_set_drvdata(pdev, rtc); - rtc->irq = platform_get_irq_byname(pdev, "ALM"); - ret = devm_request_threaded_irq(&pdev->dev, rtc->irq, NULL, - da9052_rtc_irq, - IRQF_TRIGGER_LOW | IRQF_ONESHOT, - "ALM", rtc); + rtc->irq = DA9052_IRQ_ALARM; + ret = da9052_request_irq(rtc->da9052, rtc->irq, "ALM", + da9052_rtc_irq, rtc); if (ret != 0) { rtc_err(rtc->da9052, "irq registration failed: %d\n", ret); return ret; diff --git a/drivers/rtc/rtc-mv.c b/drivers/rtc/rtc-mv.c index 57233c885998..8f87fec27ce7 100644 --- a/drivers/rtc/rtc-mv.c +++ b/drivers/rtc/rtc-mv.c @@ -14,6 +14,7 @@ #include <linux/platform_device.h> #include <linux/of.h> #include <linux/delay.h> +#include <linux/clk.h> #include <linux/gfp.h> #include <linux/module.h> @@ -41,6 +42,7 @@ struct rtc_plat_data { struct rtc_device *rtc; void __iomem *ioaddr; int irq; + struct clk *clk; }; static int mv_rtc_set_time(struct device *dev, struct rtc_time *tm) @@ -221,6 +223,7 @@ static int mv_rtc_probe(struct platform_device *pdev) struct rtc_plat_data *pdata; resource_size_t size; u32 rtc_time; + int ret = 0; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) @@ -239,11 +242,17 @@ static int mv_rtc_probe(struct platform_device *pdev) if (!pdata->ioaddr) return -ENOMEM; + pdata->clk = devm_clk_get(&pdev->dev, NULL); + /* Not all SoCs require a clock.*/ + if (!IS_ERR(pdata->clk)) + clk_prepare_enable(pdata->clk); + /* make sure the 24 hours mode is enabled */ rtc_time = readl(pdata->ioaddr + RTC_TIME_REG_OFFS); if (rtc_time & RTC_HOURS_12H_MODE) { dev_err(&pdev->dev, "24 Hours mode not supported.\n"); - return -EINVAL; + ret = -EINVAL; + goto out; } /* make sure it is actually functional */ @@ -252,7 +261,8 @@ static int mv_rtc_probe(struct platform_device *pdev) rtc_time = readl(pdata->ioaddr + RTC_TIME_REG_OFFS); if (rtc_time == 0x01000000) { dev_err(&pdev->dev, "internal RTC not ticking\n"); - return -ENODEV; + ret = -ENODEV; + goto out; } } @@ -268,8 +278,10 @@ static int mv_rtc_probe(struct platform_device *pdev) } else pdata->rtc = rtc_device_register(pdev->name, &pdev->dev, &mv_rtc_ops, THIS_MODULE); - if (IS_ERR(pdata->rtc)) - return PTR_ERR(pdata->rtc); + if (IS_ERR(pdata->rtc)) { + ret = PTR_ERR(pdata->rtc); + goto out; + } if (pdata->irq >= 0) { writel(0, pdata->ioaddr + RTC_ALARM_INTERRUPT_MASK_REG_OFFS); @@ -282,6 +294,11 @@ static int mv_rtc_probe(struct platform_device *pdev) } return 0; +out: + if (!IS_ERR(pdata->clk)) + clk_disable_unprepare(pdata->clk); + + return ret; } static int __exit mv_rtc_remove(struct platform_device *pdev) @@ -292,6 +309,9 @@ static int __exit mv_rtc_remove(struct platform_device *pdev) device_init_wakeup(&pdev->dev, 0); rtc_device_unregister(pdata->rtc); + if (!IS_ERR(pdata->clk)) + clk_disable_unprepare(pdata->clk); + return 0; } diff --git a/drivers/rtc/rtc-palmas.c b/drivers/rtc/rtc-palmas.c new file mode 100644 index 000000000000..59c42986254e --- /dev/null +++ b/drivers/rtc/rtc-palmas.c @@ -0,0 +1,339 @@ +/* + * rtc-palmas.c -- Palmas Real Time Clock driver. + + * RTC driver for TI Palma series devices like TPS65913, + * TPS65914 power management IC. + * + * Copyright (c) 2012, NVIDIA Corporation. + * + * 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 the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, + * whether express or implied; 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/bcd.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/mfd/palmas.h> +#include <linux/module.h> +#include <linux/rtc.h> +#include <linux/types.h> +#include <linux/platform_device.h> +#include <linux/pm.h> + +struct palmas_rtc { + struct rtc_device *rtc; + struct device *dev; + unsigned int irq; +}; + +/* Total number of RTC registers needed to set time*/ +#define PALMAS_NUM_TIME_REGS (PALMAS_YEARS_REG - PALMAS_SECONDS_REG + 1) + +static int palmas_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + unsigned char rtc_data[PALMAS_NUM_TIME_REGS]; + struct palmas *palmas = dev_get_drvdata(dev->parent); + int ret; + + /* Copy RTC counting registers to static registers or latches */ + ret = palmas_update_bits(palmas, PALMAS_RTC_BASE, PALMAS_RTC_CTRL_REG, + PALMAS_RTC_CTRL_REG_GET_TIME, PALMAS_RTC_CTRL_REG_GET_TIME); + if (ret < 0) { + dev_err(dev, "RTC CTRL reg update failed, err: %d\n", ret); + return ret; + } + + ret = palmas_bulk_read(palmas, PALMAS_RTC_BASE, PALMAS_SECONDS_REG, + rtc_data, PALMAS_NUM_TIME_REGS); + if (ret < 0) { + dev_err(dev, "RTC_SECONDS reg read failed, err = %d\n", ret); + return ret; + } + + tm->tm_sec = bcd2bin(rtc_data[0]); + tm->tm_min = bcd2bin(rtc_data[1]); + tm->tm_hour = bcd2bin(rtc_data[2]); + tm->tm_mday = bcd2bin(rtc_data[3]); + tm->tm_mon = bcd2bin(rtc_data[4]) - 1; + tm->tm_year = bcd2bin(rtc_data[5]) + 100; + + return ret; +} + +static int palmas_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + unsigned char rtc_data[PALMAS_NUM_TIME_REGS]; + struct palmas *palmas = dev_get_drvdata(dev->parent); + int ret; + + rtc_data[0] = bin2bcd(tm->tm_sec); + rtc_data[1] = bin2bcd(tm->tm_min); + rtc_data[2] = bin2bcd(tm->tm_hour); + rtc_data[3] = bin2bcd(tm->tm_mday); + rtc_data[4] = bin2bcd(tm->tm_mon + 1); + rtc_data[5] = bin2bcd(tm->tm_year - 100); + + /* 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); + if (ret < 0) { + dev_err(dev, "RTC stop failed, err = %d\n", ret); + return ret; + } + + ret = palmas_bulk_write(palmas, PALMAS_RTC_BASE, PALMAS_SECONDS_REG, + rtc_data, PALMAS_NUM_TIME_REGS); + if (ret < 0) { + dev_err(dev, "RTC_SECONDS reg write failed, err = %d\n", ret); + return ret; + } + + /* Start back RTC */ + ret = palmas_update_bits(palmas, PALMAS_RTC_BASE, PALMAS_RTC_CTRL_REG, + PALMAS_RTC_CTRL_REG_STOP_RTC, PALMAS_RTC_CTRL_REG_STOP_RTC); + if (ret < 0) + dev_err(dev, "RTC start failed, err = %d\n", ret); + return ret; +} + +static int palmas_rtc_alarm_irq_enable(struct device *dev, unsigned enabled) +{ + struct palmas *palmas = dev_get_drvdata(dev->parent); + u8 val; + + val = enabled ? PALMAS_RTC_INTERRUPTS_REG_IT_ALARM : 0; + return palmas_write(palmas, PALMAS_RTC_BASE, + PALMAS_RTC_INTERRUPTS_REG, val); +} + +static int palmas_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + unsigned char alarm_data[PALMAS_NUM_TIME_REGS]; + u32 int_val; + struct palmas *palmas = dev_get_drvdata(dev->parent); + int ret; + + ret = palmas_bulk_read(palmas, PALMAS_RTC_BASE, + PALMAS_ALARM_SECONDS_REG, + alarm_data, PALMAS_NUM_TIME_REGS); + if (ret < 0) { + dev_err(dev, "RTC_ALARM_SECONDS read failed, err = %d\n", ret); + return ret; + } + + alm->time.tm_sec = bcd2bin(alarm_data[0]); + alm->time.tm_min = bcd2bin(alarm_data[1]); + alm->time.tm_hour = bcd2bin(alarm_data[2]); + alm->time.tm_mday = bcd2bin(alarm_data[3]); + alm->time.tm_mon = bcd2bin(alarm_data[4]) - 1; + alm->time.tm_year = bcd2bin(alarm_data[5]) + 100; + + ret = palmas_read(palmas, PALMAS_RTC_BASE, PALMAS_RTC_INTERRUPTS_REG, + &int_val); + if (ret < 0) { + dev_err(dev, "RTC_INTERRUPTS reg read failed, err = %d\n", ret); + return ret; + } + + if (int_val & PALMAS_RTC_INTERRUPTS_REG_IT_ALARM) + alm->enabled = 1; + return ret; +} + +static int palmas_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + unsigned char alarm_data[PALMAS_NUM_TIME_REGS]; + struct palmas *palmas = dev_get_drvdata(dev->parent); + int ret; + + ret = palmas_rtc_alarm_irq_enable(dev, 0); + if (ret < 0) { + dev_err(dev, "Disable RTC alarm failed\n"); + return ret; + } + + alarm_data[0] = bin2bcd(alm->time.tm_sec); + alarm_data[1] = bin2bcd(alm->time.tm_min); + alarm_data[2] = bin2bcd(alm->time.tm_hour); + alarm_data[3] = bin2bcd(alm->time.tm_mday); + alarm_data[4] = bin2bcd(alm->time.tm_mon + 1); + alarm_data[5] = bin2bcd(alm->time.tm_year - 100); + + ret = palmas_bulk_write(palmas, PALMAS_RTC_BASE, + PALMAS_ALARM_SECONDS_REG, alarm_data, PALMAS_NUM_TIME_REGS); + if (ret < 0) { + dev_err(dev, "ALARM_SECONDS_REG write failed, err = %d\n", ret); + return ret; + } + + if (alm->enabled) + ret = palmas_rtc_alarm_irq_enable(dev, 1); + return ret; +} + +static int palmas_clear_interrupts(struct device *dev) +{ + struct palmas *palmas = dev_get_drvdata(dev->parent); + unsigned int rtc_reg; + int ret; + + ret = palmas_read(palmas, PALMAS_RTC_BASE, PALMAS_RTC_STATUS_REG, + &rtc_reg); + if (ret < 0) { + dev_err(dev, "RTC_STATUS read failed, err = %d\n", ret); + return ret; + } + + ret = palmas_write(palmas, PALMAS_RTC_BASE, PALMAS_RTC_STATUS_REG, + rtc_reg); + if (ret < 0) { + dev_err(dev, "RTC_STATUS write failed, err = %d\n", ret); + return ret; + } + return 0; +} + +static irqreturn_t palmas_rtc_interrupt(int irq, void *context) +{ + struct palmas_rtc *palmas_rtc = context; + struct device *dev = palmas_rtc->dev; + int ret; + + ret = palmas_clear_interrupts(dev); + if (ret < 0) { + dev_err(dev, "RTC interrupt clear failed, err = %d\n", ret); + return IRQ_NONE; + } + + rtc_update_irq(palmas_rtc->rtc, 1, RTC_IRQF | RTC_AF); + return IRQ_HANDLED; +} + +static struct rtc_class_ops palmas_rtc_ops = { + .read_time = palmas_rtc_read_time, + .set_time = palmas_rtc_set_time, + .read_alarm = palmas_rtc_read_alarm, + .set_alarm = palmas_rtc_set_alarm, + .alarm_irq_enable = palmas_rtc_alarm_irq_enable, +}; + +static int palmas_rtc_probe(struct platform_device *pdev) +{ + struct palmas *palmas = dev_get_drvdata(pdev->dev.parent); + struct palmas_rtc *palmas_rtc = NULL; + int ret; + + palmas_rtc = devm_kzalloc(&pdev->dev, sizeof(struct palmas_rtc), + GFP_KERNEL); + if (!palmas_rtc) + return -ENOMEM; + + /* Clear pending interrupts */ + ret = palmas_clear_interrupts(&pdev->dev); + if (ret < 0) { + dev_err(&pdev->dev, "clear RTC int failed, err = %d\n", ret); + return ret; + } + + palmas_rtc->dev = &pdev->dev; + platform_set_drvdata(pdev, palmas_rtc); + + /* Start RTC */ + ret = palmas_update_bits(palmas, PALMAS_RTC_BASE, PALMAS_RTC_CTRL_REG, + PALMAS_RTC_CTRL_REG_STOP_RTC, + PALMAS_RTC_CTRL_REG_STOP_RTC); + if (ret < 0) { + dev_err(&pdev->dev, "RTC_CTRL write failed, err = %d\n", ret); + return ret; + } + + palmas_rtc->irq = platform_get_irq(pdev, 0); + + 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 = 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; +} + +#ifdef CONFIG_PM_SLEEP +static int palmas_rtc_suspend(struct device *dev) +{ + struct palmas_rtc *palmas_rtc = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(palmas_rtc->irq); + return 0; +} + +static int palmas_rtc_resume(struct device *dev) +{ + struct palmas_rtc *palmas_rtc = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(palmas_rtc->irq); + return 0; +} +#endif + +static const struct dev_pm_ops palmas_rtc_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(palmas_rtc_suspend, palmas_rtc_resume) +}; + +static struct platform_driver palmas_rtc_driver = { + .probe = palmas_rtc_probe, + .remove = palmas_rtc_remove, + .driver = { + .owner = THIS_MODULE, + .name = "palmas-rtc", + .pm = &palmas_rtc_pm_ops, + }, +}; + +module_platform_driver(palmas_rtc_driver); + +MODULE_ALIAS("platform:palmas_rtc"); +MODULE_DESCRIPTION("TI PALMAS series RTC driver"); +MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/rtc-stmp3xxx.c b/drivers/rtc/rtc-stmp3xxx.c index b2a8ed99b2bf..98f0d3c30738 100644 --- a/drivers/rtc/rtc-stmp3xxx.c +++ b/drivers/rtc/rtc-stmp3xxx.c @@ -27,6 +27,8 @@ #include <linux/slab.h> #include <linux/of_device.h> #include <linux/of.h> +#include <linux/stmp_device.h> +#include <linux/stmp3xxx_rtc_wdt.h> #include <mach/common.h> @@ -36,6 +38,7 @@ #define STMP3XXX_RTC_CTRL_ALARM_IRQ_EN 0x00000001 #define STMP3XXX_RTC_CTRL_ONEMSEC_IRQ_EN 0x00000002 #define STMP3XXX_RTC_CTRL_ALARM_IRQ 0x00000004 +#define STMP3XXX_RTC_CTRL_WATCHDOGEN 0x00000010 #define STMP3XXX_RTC_STAT 0x10 #define STMP3XXX_RTC_STAT_STALE_SHIFT 16 @@ -45,6 +48,8 @@ #define STMP3XXX_RTC_ALARM 0x40 +#define STMP3XXX_RTC_WATCHDOG 0x50 + #define STMP3XXX_RTC_PERSISTENT0 0x60 #define STMP3XXX_RTC_PERSISTENT0_SET 0x64 #define STMP3XXX_RTC_PERSISTENT0_CLR 0x68 @@ -52,12 +57,70 @@ #define STMP3XXX_RTC_PERSISTENT0_ALARM_EN 0x00000004 #define STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE 0x00000080 +#define STMP3XXX_RTC_PERSISTENT1 0x70 +/* missing bitmask in headers */ +#define STMP3XXX_RTC_PERSISTENT1_FORCE_UPDATER 0x80000000 + struct stmp3xxx_rtc_data { struct rtc_device *rtc; void __iomem *io; int irq_alarm; }; +#if IS_ENABLED(CONFIG_STMP3XXX_RTC_WATCHDOG) +/** + * stmp3xxx_wdt_set_timeout - configure the watchdog inside the STMP3xxx RTC + * @dev: the parent device of the watchdog (= the RTC) + * @timeout: the desired value for the timeout register of the watchdog. + * 0 disables the watchdog + * + * The watchdog needs one register and two bits which are in the RTC domain. + * To handle the resource conflict, the RTC driver will create another + * platform_device for the watchdog driver as a child of the RTC device. + * The watchdog driver is passed the below accessor function via platform_data + * to configure the watchdog. Locking is not needed because accessing SET/CLR + * registers is atomic. + */ + +static void stmp3xxx_wdt_set_timeout(struct device *dev, u32 timeout) +{ + struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); + + if (timeout) { + writel(timeout, rtc_data->io + STMP3XXX_RTC_WATCHDOG); + writel(STMP3XXX_RTC_CTRL_WATCHDOGEN, + rtc_data->io + STMP3XXX_RTC_CTRL + STMP_OFFSET_REG_SET); + writel(STMP3XXX_RTC_PERSISTENT1_FORCE_UPDATER, + rtc_data->io + STMP3XXX_RTC_PERSISTENT1 + STMP_OFFSET_REG_SET); + } else { + writel(STMP3XXX_RTC_CTRL_WATCHDOGEN, + rtc_data->io + STMP3XXX_RTC_CTRL + STMP_OFFSET_REG_CLR); + writel(STMP3XXX_RTC_PERSISTENT1_FORCE_UPDATER, + rtc_data->io + STMP3XXX_RTC_PERSISTENT1 + STMP_OFFSET_REG_CLR); + } +} + +static struct stmp3xxx_wdt_pdata wdt_pdata = { + .wdt_set_timeout = stmp3xxx_wdt_set_timeout, +}; + +static void stmp3xxx_wdt_register(struct platform_device *rtc_pdev) +{ + struct platform_device *wdt_pdev = + platform_device_alloc("stmp3xxx_rtc_wdt", rtc_pdev->id); + + if (wdt_pdev) { + wdt_pdev->dev.parent = &rtc_pdev->dev; + wdt_pdev->dev.platform_data = &wdt_pdata; + platform_device_add(wdt_pdev); + } +} +#else +static void stmp3xxx_wdt_register(struct platform_device *rtc_pdev) +{ +} +#endif /* CONFIG_STMP3XXX_RTC_WATCHDOG */ + static void stmp3xxx_wait_time(struct stmp3xxx_rtc_data *rtc_data) { /* @@ -233,6 +296,7 @@ static int stmp3xxx_rtc_probe(struct platform_device *pdev) goto out_irq_alarm; } + stmp3xxx_wdt_register(pdev); return 0; out_irq_alarm: |