From: wellsk40@gmail.com (wellsk40 at gmail.com)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH 1/2] rtc: rtc-lpc32xx: Introduce RTC driver for the LPC32XX SoC
Date: Mon, 9 Aug 2010 09:17:29 -0700 [thread overview]
Message-ID: <1281370650-29520-2-git-send-email-wellsk40@gmail.com> (raw)
In-Reply-To: <1281370650-29520-1-git-send-email-wellsk40@gmail.com>
From: Kevin Wells <wellsk40@gmail.com>
This patch contains the RTC driver for the built-in RTC in
the LPC32XX SoC.
Signed-off-by: Kevin Wells <wellsk40@gmail.com>
Signed-off-by: Durgesh Pattamatta <durgesh.pattamatta@nxp.com>
---
drivers/rtc/rtc-lpc32xx.c | 391 +++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 391 insertions(+), 0 deletions(-)
create mode 100644 drivers/rtc/rtc-lpc32xx.c
diff --git a/drivers/rtc/rtc-lpc32xx.c b/drivers/rtc/rtc-lpc32xx.c
new file mode 100644
index 0000000..7803c68
--- /dev/null
+++ b/drivers/rtc/rtc-lpc32xx.c
@@ -0,0 +1,391 @@
+/*
+ * Copyright (C) 2010 NXP Semiconductors
+ *
+ * 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.
+ *
+ * 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.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/rtc.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+
+/*
+ * Clock and Power control register offsets
+ */
+#define RTC_UCOUNT 0x00
+#define RTC_DCOUNT 0x04
+#define RTC_MATCH0 0x08
+#define RTC_MATCH1 0x0C
+#define RTC_CTRL 0x10
+#define RTC_INTSTAT 0x14
+#define RTC_KEY 0x18
+#define RTC_SRAM 0x80
+
+#define RTC_MATCH0_EN (1 << 0)
+#define RTC_MATCH1_EN (1 << 1)
+#define RTC_ONSW_MATCH0_EN (1 << 2)
+#define RTC_ONSW_MATCH1_EN (1 << 3)
+#define RTC_SW_RESET (1 << 4)
+#define RTC_CNTR_DIS (1 << 6)
+#define RTC_ONSW_FORCE_HIGH (1 << 7)
+
+#define RTC_MATCH0_INT_STS (1 << 0)
+#define RTC_MATCH1_INT_STS (1 << 1)
+#define RTC_ONSW_INT_STS (1 << 2)
+
+#define RTC_KEY_ONSW_LOADVAL 0xB5C13F27
+
+#define RTC_NAME "rtc-lpc32xx"
+
+#define rtc_readl(dev, reg) \
+ __raw_readl((dev)->rtc_base + reg)
+#define rtc_writel(dev, reg, val) \
+ __raw_writel((val), (dev)->rtc_base + reg)
+
+struct lpc32xx_rtc {
+ void __iomem *rtc_base;
+ unsigned int irq;
+ int alarm_enabled;
+ struct rtc_device *rtc;
+ spinlock_t lock;
+};
+
+static int lpc32xx_rtc_read_time(struct device *dev, struct rtc_time *time)
+{
+ unsigned long elapsed_sec;
+ struct lpc32xx_rtc *rtc = dev_get_drvdata(dev);
+
+ elapsed_sec = rtc_readl(rtc, RTC_UCOUNT);
+ rtc_time_to_tm(elapsed_sec, time);
+
+ return rtc_valid_tm(time);
+}
+
+static int lpc32xx_rtc_set_mmss(struct device *dev, unsigned long secs)
+{
+ struct lpc32xx_rtc *rtc = dev_get_drvdata(dev);
+ u32 tmp;
+
+ spin_lock_irq(&rtc->lock);
+
+ /* RTC must be disabled during count update */
+ tmp = rtc_readl(rtc, RTC_CTRL);
+ rtc_writel(rtc, RTC_CTRL, tmp | RTC_CNTR_DIS);
+ rtc_writel(rtc, RTC_UCOUNT, secs);
+ rtc_writel(rtc, RTC_DCOUNT, 0xFFFFFFFF - secs);
+ rtc_writel(rtc, RTC_CTRL, tmp &= ~RTC_CNTR_DIS);
+
+ spin_unlock_irq(&rtc->lock);
+
+ return 0;
+}
+
+static int lpc32xx_rtc_read_alarm(struct device *dev,
+ struct rtc_wkalrm *wkalrm)
+{
+ struct lpc32xx_rtc *rtc = dev_get_drvdata(dev);
+
+ rtc_time_to_tm(rtc_readl(rtc, RTC_MATCH0), &wkalrm->time);
+ wkalrm->enabled = rtc->alarm_enabled;
+
+ return rtc_valid_tm(&wkalrm->time);
+}
+
+static int lpc32xx_rtc_set_alarm(struct device *dev,
+ struct rtc_wkalrm *wkalrm)
+{
+ struct lpc32xx_rtc *rtc = dev_get_drvdata(dev);
+ unsigned long alarmsecs;
+ u32 tmp;
+ int ret;
+ ret = rtc_tm_to_time(&wkalrm->time, &alarmsecs);
+ if (ret < 0) {
+ dev_err(dev, "Failed to convert time: %d\n", ret);
+ return ret;
+ }
+
+ spin_lock_irq(&rtc->lock);
+
+ /* Disable alarm during update */
+ tmp = rtc_readl(rtc, RTC_CTRL);
+ rtc_writel(rtc, RTC_CTRL, tmp & ~RTC_MATCH0_EN);
+
+ rtc->alarm_enabled = wkalrm->enabled = 1;
+ if (wkalrm->enabled) {
+ rtc_writel(rtc, RTC_MATCH0, alarmsecs);
+ rtc_writel(rtc, RTC_INTSTAT, RTC_MATCH0_INT_STS);
+ rtc_writel(rtc, RTC_CTRL, tmp | RTC_MATCH0_EN);
+ }
+
+ spin_unlock_irq(&rtc->lock);
+
+ return 0;
+}
+
+static int lpc32xx_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct lpc32xx_rtc *rtc = dev_get_drvdata(dev);
+ u32 tmp;
+
+ spin_lock_irq(&rtc->lock);
+ rtc->alarm_enabled = (int) enabled;
+ tmp = rtc_readl(rtc, RTC_CTRL);
+
+ if (enabled)
+ tmp |= RTC_MATCH0_EN;
+ else
+ tmp &= ~RTC_MATCH0_EN;
+
+ rtc_writel(rtc, RTC_CTRL, tmp);
+ spin_unlock_irq(&rtc->lock);
+
+ return 0;
+}
+
+static irqreturn_t lpc32xx_rtc_alarm_interrupt(int irq, void *dev)
+{
+ struct lpc32xx_rtc *rtc = (struct lpc32xx_rtc *) dev;
+
+ spin_lock(&rtc->lock);
+
+ /* Disable alarm interrupt */
+ rtc_writel(rtc, RTC_CTRL,
+ rtc_readl(rtc, RTC_CTRL) & ~RTC_MATCH0_EN);
+ rtc->alarm_enabled = 0;
+
+ /*
+ * Write a large value to the match value so the RTC won't
+ * keep firing the match status
+ */
+ rtc_writel(rtc, RTC_MATCH0, 0xFFFFFFFF);
+ rtc_writel(rtc, RTC_INTSTAT, RTC_MATCH0_INT_STS);
+
+ spin_unlock(&rtc->lock);
+
+ rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF);
+
+ return IRQ_HANDLED;
+}
+
+static const struct rtc_class_ops lpc32xx_rtc_ops = {
+ .read_time = lpc32xx_rtc_read_time,
+ .set_mmss = lpc32xx_rtc_set_mmss,
+ .read_alarm = lpc32xx_rtc_read_alarm,
+ .set_alarm = lpc32xx_rtc_set_alarm,
+ .alarm_irq_enable = lpc32xx_rtc_alarm_irq_enable,
+};
+
+static int __devinit lpc32xx_rtc_probe(struct platform_device *pdev)
+{
+ struct resource *res, *mem = NULL;
+ struct lpc32xx_rtc *rtc = NULL;
+ int rtcirq, retval;
+ u32 tmp;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "Can't get memory resource\n");
+ return -ENOENT;
+ }
+
+ rtcirq = platform_get_irq(pdev, 0);
+ if ((rtcirq < 0) || (rtcirq >= NR_IRQS)) {
+ dev_err(&pdev->dev, "Can't get interrupt resource\n");
+ return -ENOENT;
+ }
+
+ rtc = kzalloc(sizeof(struct lpc32xx_rtc), GFP_KERNEL);
+ if (unlikely(!rtc)) {
+ dev_err(&pdev->dev, "Can't allocate memory\n");
+ return -ENOMEM;
+ }
+ rtc->irq = rtcirq;
+
+ mem = request_mem_region(res->start, resource_size(res), pdev->name);
+ if (!mem) {
+ dev_err(&pdev->dev, "RTC registers are not free\n");
+ retval = -EBUSY;
+ goto err_reqmem;
+ }
+
+ rtc->rtc_base = ioremap(res->start, res->end - res->start + 1);
+ if (!rtc->rtc_base) {
+ dev_err(&pdev->dev, "Can't map memory\n");
+ retval = -EIO;
+ goto err_noremap;
+ }
+
+ spin_lock_init(&rtc->lock);
+
+ /*
+ * The RTC is on a seperate power domain and can keep it's state
+ * across a chip power cycle. If the RTC has never been previously
+ * setup, then set it up now for the first time.
+ */
+ if (rtc_readl(rtc, RTC_KEY) == RTC_KEY_ONSW_LOADVAL) {
+ tmp = rtc_readl(rtc, RTC_CTRL);
+ tmp &= ~(RTC_SW_RESET | RTC_CNTR_DIS | RTC_MATCH0_EN |
+ RTC_MATCH1_EN | RTC_ONSW_MATCH0_EN |
+ RTC_ONSW_MATCH1_EN | RTC_ONSW_FORCE_HIGH);
+ rtc_writel(rtc, RTC_CTRL, tmp);
+
+ /* Clear latched interrupt states */
+ rtc_writel(rtc, RTC_MATCH0, 0xFFFFFFFF);
+ rtc_writel(rtc, RTC_INTSTAT, RTC_MATCH0_INT_STS |
+ RTC_MATCH1_INT_STS | RTC_ONSW_INT_STS);
+
+ /* Write key value to RTC so it won't reload on reset */
+ rtc_writel(rtc, RTC_KEY, RTC_KEY_ONSW_LOADVAL);
+ } else if (rtc_readl(rtc, RTC_CTRL) & RTC_MATCH0_EN)
+ rtc->alarm_enabled = 1;
+
+ platform_set_drvdata(pdev, rtc);
+
+ device_init_wakeup(&pdev->dev, 1);
+ rtc->rtc = rtc_device_register(RTC_NAME, &pdev->dev, &lpc32xx_rtc_ops,
+ THIS_MODULE);
+ if (IS_ERR(rtc->rtc)) {
+ dev_err(&pdev->dev, "Can't get RTC\n");
+ retval = PTR_ERR(rtc->rtc);
+ goto err_noreg;
+ }
+
+ retval = request_irq(rtc->irq, lpc32xx_rtc_alarm_interrupt,
+ IRQF_DISABLED, "rtcalarm", rtc);
+ if (retval < 0) {
+ dev_err(&pdev->dev, "Can't request interrupt\n");
+ goto err_free_irq;
+ }
+
+ return 0;
+
+err_free_irq:
+ rtc_device_unregister(rtc->rtc);
+err_noreg:
+ iounmap(rtc->rtc_base);
+err_noremap:
+ release_resource(mem);
+err_reqmem:
+ kfree(rtc);
+
+ return retval;
+}
+
+static int __devexit lpc32xx_rtc_remove(struct platform_device *pdev)
+{
+ struct lpc32xx_rtc *rtc = platform_get_drvdata(pdev);
+
+ free_irq(rtc->irq, pdev);
+ rtc_device_unregister(rtc->rtc);
+ iounmap(rtc->rtc_base);
+ release_resource(dev_get_drvdata(&rtc->rtc->dev));
+ kfree(rtc);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int lpc32xx_rtc_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lpc32xx_rtc *rtc = platform_get_drvdata(pdev);
+
+ if (device_may_wakeup(&pdev->dev))
+ enable_irq_wake(rtc->irq);
+ else
+ disable_irq_wake(rtc->irq);
+
+ return 0;
+}
+
+static int lpc32xx_rtc_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lpc32xx_rtc *rtc = platform_get_drvdata(pdev);
+
+ if (device_may_wakeup(&pdev->dev))
+ disable_irq_wake(rtc->irq);
+
+ return 0;
+}
+
+/* Unconditionally disable the alarm */
+static int lpc32xx_rtc_freeze(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lpc32xx_rtc *rtc = platform_get_drvdata(pdev);
+
+ spin_lock_irq(&rtc->lock);
+
+ rtc_writel(rtc, RTC_CTRL,
+ rtc_readl(rtc, RTC_CTRL) & ~RTC_MATCH0_EN);
+
+ spin_unlock_irq(&rtc->lock);
+
+ return 0;
+}
+
+static int lpc32xx_rtc_thaw(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lpc32xx_rtc *rtc = platform_get_drvdata(pdev);
+
+ if (rtc->alarm_enabled)
+ rtc_writel(rtc, RTC_CTRL,
+ rtc_readl(rtc, RTC_CTRL) | RTC_MATCH0_EN);
+
+ return 0;
+}
+
+#else
+#define lpc32xx_rtc_suspend NULL
+#define lpc32xx_rtc_resume NULL
+#define lpc32xx_rtc_freeze NULL
+#define lpc32xx_rtc_thaw NULL
+#endif
+
+static const struct dev_pm_ops lpc32xx_rtc_pm_ops = {
+ .suspend = lpc32xx_rtc_suspend,
+ .resume = lpc32xx_rtc_resume,
+ .freeze = lpc32xx_rtc_freeze,
+ .thaw = lpc32xx_rtc_thaw,
+ .restore = lpc32xx_rtc_resume
+};
+
+static struct platform_driver lpc32xx_rtc_driver = {
+ .probe = lpc32xx_rtc_probe,
+ .remove = __devexit_p(lpc32xx_rtc_remove),
+ .driver = {
+ .name = RTC_NAME,
+ .pm = &lpc32xx_rtc_pm_ops,
+ },
+};
+
+static int __init lpc32xx_rtc_init(void)
+{
+ return platform_driver_register(&lpc32xx_rtc_driver);
+}
+
+static void __exit lpc32xx_rtc_exit(void)
+{
+ platform_driver_unregister(&lpc32xx_rtc_driver);
+}
+
+module_init(lpc32xx_rtc_init);
+module_exit(lpc32xx_rtc_exit);
+
+MODULE_AUTHOR("Kevin Wells <wellsk40@gmail.com");
+MODULE_DESCRIPTION("RTC driver for the LPC32xx SoC");
+MODULE_LICENSE("GPL");
--
1.7.1.1
WARNING: multiple messages have this Message-ID (diff)
From: wellsk40@gmail.com
To: rtc-linux@googlegroups.com, linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org
Cc: Kevin Wells <wellsk40@gmail.com>,
Durgesh Pattamatta <durgesh.pattamatta@nxp.com>
Subject: [PATCH 1/2] rtc: rtc-lpc32xx: Introduce RTC driver for the LPC32XX SoC
Date: Mon, 9 Aug 2010 09:17:29 -0700 [thread overview]
Message-ID: <1281370650-29520-2-git-send-email-wellsk40@gmail.com> (raw)
In-Reply-To: <1281370650-29520-1-git-send-email-wellsk40@gmail.com>
From: Kevin Wells <wellsk40@gmail.com>
This patch contains the RTC driver for the built-in RTC in
the LPC32XX SoC.
Signed-off-by: Kevin Wells <wellsk40@gmail.com>
Signed-off-by: Durgesh Pattamatta <durgesh.pattamatta@nxp.com>
---
drivers/rtc/rtc-lpc32xx.c | 391 +++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 391 insertions(+), 0 deletions(-)
create mode 100644 drivers/rtc/rtc-lpc32xx.c
diff --git a/drivers/rtc/rtc-lpc32xx.c b/drivers/rtc/rtc-lpc32xx.c
new file mode 100644
index 0000000..7803c68
--- /dev/null
+++ b/drivers/rtc/rtc-lpc32xx.c
@@ -0,0 +1,391 @@
+/*
+ * Copyright (C) 2010 NXP Semiconductors
+ *
+ * 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.
+ *
+ * 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.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/rtc.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+
+/*
+ * Clock and Power control register offsets
+ */
+#define RTC_UCOUNT 0x00
+#define RTC_DCOUNT 0x04
+#define RTC_MATCH0 0x08
+#define RTC_MATCH1 0x0C
+#define RTC_CTRL 0x10
+#define RTC_INTSTAT 0x14
+#define RTC_KEY 0x18
+#define RTC_SRAM 0x80
+
+#define RTC_MATCH0_EN (1 << 0)
+#define RTC_MATCH1_EN (1 << 1)
+#define RTC_ONSW_MATCH0_EN (1 << 2)
+#define RTC_ONSW_MATCH1_EN (1 << 3)
+#define RTC_SW_RESET (1 << 4)
+#define RTC_CNTR_DIS (1 << 6)
+#define RTC_ONSW_FORCE_HIGH (1 << 7)
+
+#define RTC_MATCH0_INT_STS (1 << 0)
+#define RTC_MATCH1_INT_STS (1 << 1)
+#define RTC_ONSW_INT_STS (1 << 2)
+
+#define RTC_KEY_ONSW_LOADVAL 0xB5C13F27
+
+#define RTC_NAME "rtc-lpc32xx"
+
+#define rtc_readl(dev, reg) \
+ __raw_readl((dev)->rtc_base + reg)
+#define rtc_writel(dev, reg, val) \
+ __raw_writel((val), (dev)->rtc_base + reg)
+
+struct lpc32xx_rtc {
+ void __iomem *rtc_base;
+ unsigned int irq;
+ int alarm_enabled;
+ struct rtc_device *rtc;
+ spinlock_t lock;
+};
+
+static int lpc32xx_rtc_read_time(struct device *dev, struct rtc_time *time)
+{
+ unsigned long elapsed_sec;
+ struct lpc32xx_rtc *rtc = dev_get_drvdata(dev);
+
+ elapsed_sec = rtc_readl(rtc, RTC_UCOUNT);
+ rtc_time_to_tm(elapsed_sec, time);
+
+ return rtc_valid_tm(time);
+}
+
+static int lpc32xx_rtc_set_mmss(struct device *dev, unsigned long secs)
+{
+ struct lpc32xx_rtc *rtc = dev_get_drvdata(dev);
+ u32 tmp;
+
+ spin_lock_irq(&rtc->lock);
+
+ /* RTC must be disabled during count update */
+ tmp = rtc_readl(rtc, RTC_CTRL);
+ rtc_writel(rtc, RTC_CTRL, tmp | RTC_CNTR_DIS);
+ rtc_writel(rtc, RTC_UCOUNT, secs);
+ rtc_writel(rtc, RTC_DCOUNT, 0xFFFFFFFF - secs);
+ rtc_writel(rtc, RTC_CTRL, tmp &= ~RTC_CNTR_DIS);
+
+ spin_unlock_irq(&rtc->lock);
+
+ return 0;
+}
+
+static int lpc32xx_rtc_read_alarm(struct device *dev,
+ struct rtc_wkalrm *wkalrm)
+{
+ struct lpc32xx_rtc *rtc = dev_get_drvdata(dev);
+
+ rtc_time_to_tm(rtc_readl(rtc, RTC_MATCH0), &wkalrm->time);
+ wkalrm->enabled = rtc->alarm_enabled;
+
+ return rtc_valid_tm(&wkalrm->time);
+}
+
+static int lpc32xx_rtc_set_alarm(struct device *dev,
+ struct rtc_wkalrm *wkalrm)
+{
+ struct lpc32xx_rtc *rtc = dev_get_drvdata(dev);
+ unsigned long alarmsecs;
+ u32 tmp;
+ int ret;
+ ret = rtc_tm_to_time(&wkalrm->time, &alarmsecs);
+ if (ret < 0) {
+ dev_err(dev, "Failed to convert time: %d\n", ret);
+ return ret;
+ }
+
+ spin_lock_irq(&rtc->lock);
+
+ /* Disable alarm during update */
+ tmp = rtc_readl(rtc, RTC_CTRL);
+ rtc_writel(rtc, RTC_CTRL, tmp & ~RTC_MATCH0_EN);
+
+ rtc->alarm_enabled = wkalrm->enabled = 1;
+ if (wkalrm->enabled) {
+ rtc_writel(rtc, RTC_MATCH0, alarmsecs);
+ rtc_writel(rtc, RTC_INTSTAT, RTC_MATCH0_INT_STS);
+ rtc_writel(rtc, RTC_CTRL, tmp | RTC_MATCH0_EN);
+ }
+
+ spin_unlock_irq(&rtc->lock);
+
+ return 0;
+}
+
+static int lpc32xx_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct lpc32xx_rtc *rtc = dev_get_drvdata(dev);
+ u32 tmp;
+
+ spin_lock_irq(&rtc->lock);
+ rtc->alarm_enabled = (int) enabled;
+ tmp = rtc_readl(rtc, RTC_CTRL);
+
+ if (enabled)
+ tmp |= RTC_MATCH0_EN;
+ else
+ tmp &= ~RTC_MATCH0_EN;
+
+ rtc_writel(rtc, RTC_CTRL, tmp);
+ spin_unlock_irq(&rtc->lock);
+
+ return 0;
+}
+
+static irqreturn_t lpc32xx_rtc_alarm_interrupt(int irq, void *dev)
+{
+ struct lpc32xx_rtc *rtc = (struct lpc32xx_rtc *) dev;
+
+ spin_lock(&rtc->lock);
+
+ /* Disable alarm interrupt */
+ rtc_writel(rtc, RTC_CTRL,
+ rtc_readl(rtc, RTC_CTRL) & ~RTC_MATCH0_EN);
+ rtc->alarm_enabled = 0;
+
+ /*
+ * Write a large value to the match value so the RTC won't
+ * keep firing the match status
+ */
+ rtc_writel(rtc, RTC_MATCH0, 0xFFFFFFFF);
+ rtc_writel(rtc, RTC_INTSTAT, RTC_MATCH0_INT_STS);
+
+ spin_unlock(&rtc->lock);
+
+ rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF);
+
+ return IRQ_HANDLED;
+}
+
+static const struct rtc_class_ops lpc32xx_rtc_ops = {
+ .read_time = lpc32xx_rtc_read_time,
+ .set_mmss = lpc32xx_rtc_set_mmss,
+ .read_alarm = lpc32xx_rtc_read_alarm,
+ .set_alarm = lpc32xx_rtc_set_alarm,
+ .alarm_irq_enable = lpc32xx_rtc_alarm_irq_enable,
+};
+
+static int __devinit lpc32xx_rtc_probe(struct platform_device *pdev)
+{
+ struct resource *res, *mem = NULL;
+ struct lpc32xx_rtc *rtc = NULL;
+ int rtcirq, retval;
+ u32 tmp;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "Can't get memory resource\n");
+ return -ENOENT;
+ }
+
+ rtcirq = platform_get_irq(pdev, 0);
+ if ((rtcirq < 0) || (rtcirq >= NR_IRQS)) {
+ dev_err(&pdev->dev, "Can't get interrupt resource\n");
+ return -ENOENT;
+ }
+
+ rtc = kzalloc(sizeof(struct lpc32xx_rtc), GFP_KERNEL);
+ if (unlikely(!rtc)) {
+ dev_err(&pdev->dev, "Can't allocate memory\n");
+ return -ENOMEM;
+ }
+ rtc->irq = rtcirq;
+
+ mem = request_mem_region(res->start, resource_size(res), pdev->name);
+ if (!mem) {
+ dev_err(&pdev->dev, "RTC registers are not free\n");
+ retval = -EBUSY;
+ goto err_reqmem;
+ }
+
+ rtc->rtc_base = ioremap(res->start, res->end - res->start + 1);
+ if (!rtc->rtc_base) {
+ dev_err(&pdev->dev, "Can't map memory\n");
+ retval = -EIO;
+ goto err_noremap;
+ }
+
+ spin_lock_init(&rtc->lock);
+
+ /*
+ * The RTC is on a seperate power domain and can keep it's state
+ * across a chip power cycle. If the RTC has never been previously
+ * setup, then set it up now for the first time.
+ */
+ if (rtc_readl(rtc, RTC_KEY) == RTC_KEY_ONSW_LOADVAL) {
+ tmp = rtc_readl(rtc, RTC_CTRL);
+ tmp &= ~(RTC_SW_RESET | RTC_CNTR_DIS | RTC_MATCH0_EN |
+ RTC_MATCH1_EN | RTC_ONSW_MATCH0_EN |
+ RTC_ONSW_MATCH1_EN | RTC_ONSW_FORCE_HIGH);
+ rtc_writel(rtc, RTC_CTRL, tmp);
+
+ /* Clear latched interrupt states */
+ rtc_writel(rtc, RTC_MATCH0, 0xFFFFFFFF);
+ rtc_writel(rtc, RTC_INTSTAT, RTC_MATCH0_INT_STS |
+ RTC_MATCH1_INT_STS | RTC_ONSW_INT_STS);
+
+ /* Write key value to RTC so it won't reload on reset */
+ rtc_writel(rtc, RTC_KEY, RTC_KEY_ONSW_LOADVAL);
+ } else if (rtc_readl(rtc, RTC_CTRL) & RTC_MATCH0_EN)
+ rtc->alarm_enabled = 1;
+
+ platform_set_drvdata(pdev, rtc);
+
+ device_init_wakeup(&pdev->dev, 1);
+ rtc->rtc = rtc_device_register(RTC_NAME, &pdev->dev, &lpc32xx_rtc_ops,
+ THIS_MODULE);
+ if (IS_ERR(rtc->rtc)) {
+ dev_err(&pdev->dev, "Can't get RTC\n");
+ retval = PTR_ERR(rtc->rtc);
+ goto err_noreg;
+ }
+
+ retval = request_irq(rtc->irq, lpc32xx_rtc_alarm_interrupt,
+ IRQF_DISABLED, "rtcalarm", rtc);
+ if (retval < 0) {
+ dev_err(&pdev->dev, "Can't request interrupt\n");
+ goto err_free_irq;
+ }
+
+ return 0;
+
+err_free_irq:
+ rtc_device_unregister(rtc->rtc);
+err_noreg:
+ iounmap(rtc->rtc_base);
+err_noremap:
+ release_resource(mem);
+err_reqmem:
+ kfree(rtc);
+
+ return retval;
+}
+
+static int __devexit lpc32xx_rtc_remove(struct platform_device *pdev)
+{
+ struct lpc32xx_rtc *rtc = platform_get_drvdata(pdev);
+
+ free_irq(rtc->irq, pdev);
+ rtc_device_unregister(rtc->rtc);
+ iounmap(rtc->rtc_base);
+ release_resource(dev_get_drvdata(&rtc->rtc->dev));
+ kfree(rtc);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int lpc32xx_rtc_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lpc32xx_rtc *rtc = platform_get_drvdata(pdev);
+
+ if (device_may_wakeup(&pdev->dev))
+ enable_irq_wake(rtc->irq);
+ else
+ disable_irq_wake(rtc->irq);
+
+ return 0;
+}
+
+static int lpc32xx_rtc_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lpc32xx_rtc *rtc = platform_get_drvdata(pdev);
+
+ if (device_may_wakeup(&pdev->dev))
+ disable_irq_wake(rtc->irq);
+
+ return 0;
+}
+
+/* Unconditionally disable the alarm */
+static int lpc32xx_rtc_freeze(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lpc32xx_rtc *rtc = platform_get_drvdata(pdev);
+
+ spin_lock_irq(&rtc->lock);
+
+ rtc_writel(rtc, RTC_CTRL,
+ rtc_readl(rtc, RTC_CTRL) & ~RTC_MATCH0_EN);
+
+ spin_unlock_irq(&rtc->lock);
+
+ return 0;
+}
+
+static int lpc32xx_rtc_thaw(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lpc32xx_rtc *rtc = platform_get_drvdata(pdev);
+
+ if (rtc->alarm_enabled)
+ rtc_writel(rtc, RTC_CTRL,
+ rtc_readl(rtc, RTC_CTRL) | RTC_MATCH0_EN);
+
+ return 0;
+}
+
+#else
+#define lpc32xx_rtc_suspend NULL
+#define lpc32xx_rtc_resume NULL
+#define lpc32xx_rtc_freeze NULL
+#define lpc32xx_rtc_thaw NULL
+#endif
+
+static const struct dev_pm_ops lpc32xx_rtc_pm_ops = {
+ .suspend = lpc32xx_rtc_suspend,
+ .resume = lpc32xx_rtc_resume,
+ .freeze = lpc32xx_rtc_freeze,
+ .thaw = lpc32xx_rtc_thaw,
+ .restore = lpc32xx_rtc_resume
+};
+
+static struct platform_driver lpc32xx_rtc_driver = {
+ .probe = lpc32xx_rtc_probe,
+ .remove = __devexit_p(lpc32xx_rtc_remove),
+ .driver = {
+ .name = RTC_NAME,
+ .pm = &lpc32xx_rtc_pm_ops,
+ },
+};
+
+static int __init lpc32xx_rtc_init(void)
+{
+ return platform_driver_register(&lpc32xx_rtc_driver);
+}
+
+static void __exit lpc32xx_rtc_exit(void)
+{
+ platform_driver_unregister(&lpc32xx_rtc_driver);
+}
+
+module_init(lpc32xx_rtc_init);
+module_exit(lpc32xx_rtc_exit);
+
+MODULE_AUTHOR("Kevin Wells <wellsk40@gmail.com");
+MODULE_DESCRIPTION("RTC driver for the LPC32xx SoC");
+MODULE_LICENSE("GPL");
--
1.7.1.1
next prev parent reply other threads:[~2010-08-09 16:17 UTC|newest]
Thread overview: 28+ messages / expand[flat|nested] mbox.gz Atom feed top
2010-08-09 16:17 RTC: LPC32xx: Introduce RTC driver for the LPC32xx wellsk40 at gmail.com
2010-08-09 16:17 ` wellsk40
2010-08-09 16:17 ` wellsk40 at gmail.com [this message]
2010-08-09 16:17 ` [PATCH 1/2] rtc: rtc-lpc32xx: Introduce RTC driver for the LPC32XX SoC wellsk40
2010-08-10 1:55 ` [rtc-linux] " Wan ZongShun
2010-08-10 1:55 ` Wan ZongShun
2010-08-10 19:08 ` Kevin Wells
2010-08-10 19:08 ` Kevin Wells
2010-08-11 1:40 ` Wan ZongShun
2010-08-11 1:40 ` Wan ZongShun
2010-08-10 10:25 ` Wolfram Sang
2010-08-10 10:25 ` Wolfram Sang
2010-08-10 12:00 ` Uwe Kleine-König
2010-08-10 12:00 ` Uwe Kleine-König
2010-08-10 13:40 ` Wolfram Sang
2010-08-10 13:40 ` Wolfram Sang
2010-08-10 19:08 ` Kevin Wells
2010-08-10 19:08 ` Kevin Wells
2010-08-10 13:34 ` Alessandro Zummo
2010-08-10 13:34 ` Alessandro Zummo
2010-08-10 19:08 ` Kevin Wells
2010-08-10 19:08 ` Kevin Wells
2010-08-09 16:17 ` [PATCH 2/2] rtc: Add support in Makefile/Kconfig for the LPC32XX RTC wellsk40 at gmail.com
2010-08-09 16:17 ` wellsk40
2010-08-10 1:45 ` [rtc-linux] " Wan ZongShun
2010-08-10 1:45 ` Wan ZongShun
2010-08-10 9:25 ` Wolfram Sang
2010-08-10 9:25 ` Wolfram Sang
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1281370650-29520-2-git-send-email-wellsk40@gmail.com \
--to=wellsk40@gmail.com \
--cc=linux-arm-kernel@lists.infradead.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.