Skip to contentSkip to conversation

RTC: LPC32xx: Introduce RTC driver for the LPC32xx

PATCH In review [email protected] 6 participants 14 messages Aug 9, 2010 – Aug 11, 2010

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

This patch set adds support for the LPC32xx built-in RTC. This is a persistent RTC with alarm on it's own power domain and a dedicated oscillator. UIE emulation is used for *_UIE_* ioctls.

#1 lore raw

PATCH 1/2 rtc: rtc-lpc32xx: Introduce RTC driver for the LPC32XX SoC

From: Kevin Wells <[email protected]>

This patch contains the RTC driver for the built-in RTC in the LPC32XX SoC.

---
 drivers/rtc/rtc-lpc32xx.c |  391 +++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 391 insertions(+), 0 deletions(-)
 create mode 100644 drivers/rtc/rtc-lpc32xx.c
drivers/rtc/rtc-lpc32xx.c new file +391 -0
new file mode 100644
index 0000000..7803c68
@@ -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 <[email protected]");
+MODULE_DESCRIPTION("RTC driver for the LPC32xx SoC");
+MODULE_LICENSE("GPL");
Signature · 1 line
1.7.1.1
#2 lore raw

Re: [rtc-linux] [PATCH 1/2] rtc: rtc-lpc32xx: Introduce RTC driver for the LPC32XX SoC

Hi Kevin ,

This is really a natty rtc patch:).

2010/8/10 <[email protected]>:

Quoted 137 lines

From: Kevin Wells <[email protected]>

This patch contains the RTC driver for the built-in RTC in the LPC32XX SoC.

---
?drivers/rtc/rtc-lpc32xx.c | ?391 +++++++++++++++++++++++++++++++++++++++++++++ ?1 files changed, 391 insertions(+), 0 deletions(-)
?create mode 100644 drivers/rtc/rtc-lpc32xx.c

drivers/rtc/rtc-lpc32xx.c new file +119 -0
new file mode 100644
index 0000000..7803c68
@@ -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);
+

drivers/rtc/rtc-lpc32xx.c line 129

Quoted 10 lines
drivers/rtc/rtc-lpc32xx.c new file +10 -0
+ ? ? ? /* 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);
+ ? ? ? }

I think this 'wkalrm->enabled ' will always be set '1', so 'if' condition will be alway true?

Quoted 84 lines
drivers/rtc/rtc-lpc32xx.c new file +84 -0
+
+ ? ? ? 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;
+ ? ? ? }

drivers/rtc/rtc-lpc32xx.c line 223

Quoted 10 lines
drivers/rtc/rtc-lpc32xx.c new file +10 -0
+ ? ? ? 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);

Use resource_size(res) here.

Quoted 176 lines
drivers/rtc/rtc-lpc32xx.c new file +168 -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 <[email protected]");
+MODULE_DESCRIPTION("RTC driver for the LPC32xx SoC");
+MODULE_LICENSE("GPL");
--

1.7.1.1

--
You received this message because you are subscribed to "rtc-linux". Membership options at http://groups.google.com/group/rtc-linux . Please read http://groups.google.com/group/rtc-linux/web/checklist
before submitting a driver.

Signature · 10 lines
*linux-arm-kernel mailing list
mail addr:linux-arm-kernel at lists.infradead.org
you can subscribe by:
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

* linux-arm-NUC900 mailing list
mail addr:NUC900 at googlegroups.com
main web: https://groups.google.com/group/NUC900
you can subscribe it by sending me mail:
mcuos.com at gmail.com
#3 lore raw

RE: [rtc-linux] [PATCH 1/2] rtc: rtc-lpc32xx: Introduce RTC driver for the LPC32XX SoC

Hi Zongshun,

Thanks for helping review this.

Quoted 8 lines

Subject: Re: [rtc-linux] [PATCH 1/2] rtc: rtc-lpc32xx: Introduce RTC driver for the LPC32XX SoC

Hi Kevin ,

This is really a natty rtc patch:).

From the urban dictionary...

"natty"
1) originating from rastafarian culture. meaning 'good','cool' and most importantly, 'elite'.
2) A slang for Natural Light beer, a favorite of poor college students the world over.

I hope it's the first one, but I guess the 2nd isn't that bad if you?re a college student o)
I'll merge both patches into a single patch for v2 and repost in a few days.

Quoted 11 lines
Quoted 10 lines

+ ? ? ? /* 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);
+ ? ? ? }

Quoted 2 lines

I think this 'wkalrm->enabled ' will always be set '1', so 'if' condition will be alway true?

Thanks for catching this. The '= 1' assignment shouldn't be there.

#4 lore raw

Re: [rtc-linux] [PATCH 1/2] rtc: rtc-lpc32xx: Introduce RTC driver for the LPC32XX SoC

2010/8/11 Kevin Wells <[email protected]>:

Quoted 9 lines

Hi Zongshun,

Thanks for helping review this.

Quoted 5 lines

Subject: Re: [rtc-linux] [PATCH 1/2] rtc: rtc-lpc32xx: Introduce RTC driver for the LPC32XX SoC

Hi Kevin ,

Quoted 9 lines
Quoted 1 line

This is really a natty rtc patch:).

From the urban dictionary...
"natty"
1) originating from rastafarian culture. meaning 'good','cool' and most importantly, 'elite'.
2) A slang for Natural Light beer, a favorite of poor college students the world over.

Hmm,I means the first, this patch looks very good to me. :)

Quoted 21 lines

I hope it's the first one, but I guess the 2nd isn't that bad if you?re a college student o)
I'll merge both patches into a single patch for v2 and repost in a few days.

Quoted 11 lines
Quoted 10 lines

+ ? ? ? /* 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);
+ ? ? ? }

Quoted 2 lines

I think this 'wkalrm->enabled ' will always be set '1', so 'if' condition will be alway true?

Thanks for catching this. The '= 1' assignment shouldn't be there.

Signature · 10 lines
*linux-arm-kernel mailing list
mail addr:linux-arm-kernel at lists.infradead.org
you can subscribe by:
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

* linux-arm-NUC900 mailing list
mail addr:NUC900 at googlegroups.com
main web: https://groups.google.com/group/NUC900
you can subscribe it by sending me mail:
mcuos.com at gmail.com
#5 lore raw

Re: [rtc-linux] [PATCH 1/2] rtc: rtc-lpc32xx: Introduce RTC driver for the LPC32XX SoC

In reply to [email protected]

Hi Kevin,

driver seems to work fine here, a few comments though.

Quoted 119 lines · On Mon, Aug 09, 2010 at 09:17:29AM -0700, wellsk40 at gmail.com wrote:

From: Kevin Wells <[email protected]>

This patch contains the RTC driver for the built-in RTC in the LPC32XX SoC.

---
 drivers/rtc/rtc-lpc32xx.c |  391 +++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 391 insertions(+), 0 deletions(-)
 create mode 100644 drivers/rtc/rtc-lpc32xx.c
drivers/rtc/rtc-lpc32xx.c new file +101 -0
new file mode 100644
index 0000000..7803c68
@@ -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;
+

drivers/rtc/rtc-lpc32xx.c line 111

Quoted 10 lines
drivers/rtc/rtc-lpc32xx.c new file +10 -0
+	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;

Empty line to seperate variables from the code

Quoted 38 lines
drivers/rtc/rtc-lpc32xx.c new file +38 -0
+	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;

drivers/rtc/rtc-lpc32xx.c line 159

Quoted 10 lines
drivers/rtc/rtc-lpc32xx.c new file +10 -0
+
+	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;

You can drop this cast.

Quoted 54 lines
drivers/rtc/rtc-lpc32xx.c new file +54 -0
+
+	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;
+	}

drivers/rtc/rtc-lpc32xx.c line 223

Quoted 10 lines
drivers/rtc/rtc-lpc32xx.c new file +10 -0
+	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);

How about using managed resources (devm_*)? They usually simplify the error paths and make the probe more readable.

Quoted 37 lines
drivers/rtc/rtc-lpc32xx.c new file +37 -0
+	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");

drivers/rtc/rtc-lpc32xx.c line 270

Quoted 10 lines
drivers/rtc/rtc-lpc32xx.c new file +10 -0
+		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;
+	}

I saw that a number of rtc-drivers register their irq after they register the device. I wonder if this is OK here? Couldn't it happen that after rtc_device_register() there is a preemption and another process could set the alarm? Then there is a race between interrupts already enabled and no handler available, no?

Quoted 107 lines
drivers/rtc/rtc-lpc32xx.c new file +107 -0
+
+	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)
+{

drivers/rtc/rtc-lpc32xx.c line 387

Quoted 10 lines
drivers/rtc/rtc-lpc32xx.c new file +10 -0
+	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);

Minor nitpick: The trend seems to be to put those lines directly after the function.

Quoted 12 lines
drivers/rtc/rtc-lpc32xx.c new file +4 -0
+
+MODULE_AUTHOR("Kevin Wells <[email protected]");
+MODULE_DESCRIPTION("RTC driver for the LPC32xx SoC");
+MODULE_LICENSE("GPL");
Signature · 7 lines
1.7.1.1

-- 
You received this message because you are subscribed to "rtc-linux".
Membership options at http://groups.google.com/group/rtc-linux .
Please read http://groups.google.com/group/rtc-linux/web/checklist
before submitting a driver.

Regards,

Wolfram

Signature · 9 lines
Pengutronix e.K.                           | Wolfram Sang                |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 198 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20100810/842103ed/attachment.sig>
#6 lore raw

Re: [rtc-linux] [PATCH 1/2] rtc: rtc-lpc32xx: Introduce RTC driver for the LPC32XX SoC

Hi Wolfram,

Quoted 4 lines
Quoted 4 lines
drivers/rtc/rtc-lpc32xx.c new file +4 -0
+	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");
Quoted 8 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+		goto err_free_irq;
+	}

I saw that a number of rtc-drivers register their irq after they register the device. I wonder if this is OK here? Couldn't it happen that after rtc_device_register() there is a preemption and another process could set the alarm? Then there is a race between interrupts already enabled and no handler available, no?

If you do it the other way around the irq might trigger and the handler reports an irq for a device that doesn't exist yet.

Best regards
Uwe

Signature · 2 lines
Pengutronix e.K.                           | Uwe Kleine-K?nig            |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
#7 lore raw

Re: [rtc-linux] [PATCH 1/2] rtc: rtc-lpc32xx: Introduce RTC driver for the LPC32XX SoC

Quoted 14 lines · Uwe Kleine-K?nig

Hi Wolfram,

Quoted 4 lines
Quoted 4 lines
drivers/rtc/rtc-lpc32xx.c new file +4 -0
+	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");
Quoted 8 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+		goto err_free_irq;
+	}

I saw that a number of rtc-drivers register their irq after they register the device. I wonder if this is OK here? Couldn't it happen that after rtc_device_register() there is a preemption and another process could set the alarm? Then there is a race between interrupts already enabled and no handler available, no?

Quoted 2 lines

If you do it the other way around the irq might trigger and the handler reports an irq for a device that doesn't exist yet.

Well, I was assuming that you initially have all interrupts disabled...

Signature · 9 lines
Pengutronix e.K.                           | Wolfram Sang                |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 198 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20100810/67be53b7/attachment.sig>
#8 lore raw

RE: [rtc-linux] [PATCH 1/2] rtc: rtc-lpc32xx: Introduce RTC driver for the LPC32XX SoC

In reply to Wolfram Sang

Hi Wolfram,

Thanks for helping review this.
I'll get your suggestions installed and an update posted in a few days.

Quoted 14 lines
Quoted 14 lines
drivers/rtc/rtc-lpc32xx.c new file +14 -0
+
+	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");
Quoted 9 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+		goto err_free_irq;
+	}

I saw that a number of rtc-drivers register their irq after they register the device. I wonder if this is OK here? Couldn't it happen that after rtc_device_register() there is a preemption and another process could set the alarm? Then there is a race between interrupts already enabled and no handler available, no?

The 32xx RTC registers can save states across power or reset cycles and are only initialized if they have never been previously initialized. When waking up from suspend, power on, or reset - the alarm IRQ may have fired and may be enabled from a previous initialization, so the RTC device needs to be registered first.

You bring up a good point though, I wonder if going through probe requires that the alarm be disabled (ie, suspend won't go via probe, but a hard system reset will). I'll review this a bit more.

#9 lore raw

Re: [rtc-linux] [PATCH 1/2] rtc: rtc-lpc32xx: Introduce RTC driver for the LPC32XX SoC

In reply to [email protected]

drivers/rtc/rtc-lpc32xx.c line 270

Quoted 7 lines · On Mon, 9 Aug 2010 09:17:29 -0700 wellsk40 at gmail.com wrote:
drivers/rtc/rtc-lpc32xx.c new file +7 -0
+
+	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;
+	}

if the driver can work without the alarm irq you
can convert this to a non fatal warning.

having an rtc without alarms is much better than having none.

Signature · 6 lines
 Best regards,

 Alessandro Zummo,
  Tower Technologies - Torino, Italy

  http://www.towertech.it
#10 lore raw

RE: [rtc-linux] [PATCH 1/2] rtc: rtc-lpc32xx: Introduce RTC driver for the LPC32XX SoC

Hi Alessandro,

Thanks for helping to review this.

Quoted 12 lines

drivers/rtc/rtc-lpc32xx.c line 270

Quoted 8 lines
drivers/rtc/rtc-lpc32xx.c new file +7 -0
+
+	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;
+	}

if the driver can work without the alarm irq you
can convert this to a non fatal warning.

Quoted 2 lines

having an rtc without alarms is much better than having none.

Good suggestion. I'll change this.

Quoted 9 lines

--

Best regards,

Alessandro Zummo,
Tower Technologies - Torino, Italy

http://www.towertech.it

#11 lore raw

PATCH 2/2 rtc: Add support in Makefile/Kconfig for the LPC32XX RTC

In reply to [email protected]

From: Kevin Wells <[email protected]>

Enable build RTC support for the LPC32XX in makefile/Kconfig

---
 drivers/rtc/Kconfig  |    9 +++++++++
 drivers/rtc/Makefile |    1 +
 2 files changed, 10 insertions(+), 0 deletions(-)
drivers/rtc/Kconfig +9 -0
index 4301a6c..8452788 100644
@@ -916,4 +916,13 @@ config RTC_DRV_JZ4740
 	  This driver can also be buillt as a module. If so, the module
 	  will be called rtc-jz4740.
 
+config RTC_DRV_LPC32XX
+        depends on ARCH_LPC32XX
+        tristate "NXP LPC32XX RTC"
+        help
+          This enables support for the NXP RTC in the LPC32XX
+
+	  This driver can also be buillt as a module. If so, the module
+	  will be called rtc-lpc32xx.
+
 endif # RTC_CLASS
drivers/rtc/Makefile +1 -0
index fedf9bb..3b8af95 100644
@@ -48,6 +48,7 @@ obj-$(CONFIG_RTC_DRV_FM3130)	+= rtc-fm3130.o
 obj-$(CONFIG_RTC_DRV_GENERIC)	+= rtc-generic.o
 obj-$(CONFIG_RTC_DRV_ISL1208)	+= rtc-isl1208.o
 obj-$(CONFIG_RTC_DRV_JZ4740)	+= rtc-jz4740.o
+obj-$(CONFIG_RTC_DRV_LPC32XX)   += rtc-lpc32xx.o
 obj-$(CONFIG_RTC_DRV_M41T80)	+= rtc-m41t80.o
 obj-$(CONFIG_RTC_DRV_M41T94)	+= rtc-m41t94.o
 obj-$(CONFIG_RTC_DRV_M48T35)	+= rtc-m48t35.o
Signature · 1 line
1.7.1.1
#12 lore raw

Re: [rtc-linux] [PATCH 2/2] rtc: Add support in Makefile/Kconfig for the LPC32XX RTC

Hi Kevin ,

2010/8/10 <[email protected]>:

Quoted 4 lines

From: Kevin Wells <[email protected]>

Enable build RTC support for the LPC32XX in makefile/Kconfig

I think you can intergrate two patches [PATCH 1/2 2/2] into one rtc driver patch.

Quoted 45 lines

---
?drivers/rtc/Kconfig ?| ? ?9 +++++++++ ?drivers/rtc/Makefile | ? ?1 +
?2 files changed, 10 insertions(+), 0 deletions(-)

drivers/rtc/Kconfig +0 -0
index 4301a6c..8452788 100644
@@ -916,4 +916,13 @@ config RTC_DRV_JZ4740

? ? ? ? ?This driver can also be buillt as a module. If so, the module ? ? ? ? ?will be called rtc-jz4740.

drivers/rtc/Kconfig +9 -0
+config RTC_DRV_LPC32XX
+ ? ? ? ?depends on ARCH_LPC32XX
+ ? ? ? ?tristate "NXP LPC32XX RTC"
+ ? ? ? ?help
+ ? ? ? ? ?This enables support for the NXP RTC in the LPC32XX
+
+ ? ? ? ? This driver can also be buillt as a module. If so, the module
+ ? ? ? ? will be called rtc-lpc32xx.
+

?endif # RTC_CLASS

drivers/rtc/Makefile +0 -0
index fedf9bb..3b8af95 100644
@@ -48,6 +48,7 @@ obj-$(CONFIG_RTC_DRV_FM3130) ?+= rtc-fm3130.o

?obj-$(CONFIG_RTC_DRV_GENERIC) ?+= rtc-generic.o
?obj-$(CONFIG_RTC_DRV_ISL1208) ?+= rtc-isl1208.o
?obj-$(CONFIG_RTC_DRV_JZ4740) ? += rtc-jz4740.o
+obj-$(CONFIG_RTC_DRV_LPC32XX) ? += rtc-lpc32xx.o
?obj-$(CONFIG_RTC_DRV_M41T80) ? += rtc-m41t80.o
?obj-$(CONFIG_RTC_DRV_M41T94) ? += rtc-m41t94.o
?obj-$(CONFIG_RTC_DRV_M48T35) ? += rtc-m48t35.o
--
1.7.1.1

--
You received this message because you are subscribed to "rtc-linux". Membership options at http://groups.google.com/group/rtc-linux . Please read http://groups.google.com/group/rtc-linux/web/checklist
before submitting a driver.

Signature · 10 lines
*linux-arm-kernel mailing list
mail addr:linux-arm-kernel at lists.infradead.org
you can subscribe by:
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

* linux-arm-NUC900 mailing list
mail addr:NUC900 at googlegroups.com
main web: https://groups.google.com/group/NUC900
you can subscribe it by sending me mail:
mcuos.com at gmail.com
#13 lore raw

Re: [rtc-linux] [PATCH 2/2] rtc: Add support in Makefile/Kconfig for the LPC32XX RTC

Quoted 10 lines · Wan ZongShun

Hi Kevin ,

2010/8/10 <[email protected]>:

Quoted 4 lines

From: Kevin Wells <[email protected]>

Enable build RTC support for the LPC32XX in makefile/Kconfig

I think you can intergrate two patches [PATCH 1/2 2/2] into one rtc driver patch.

+1. Also, please make proper use of tabs like the surrounding entries.

Regards,

Wolfram

Signature · 9 lines
Pengutronix e.K.                           | Wolfram Sang                |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 198 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20100810/5085e801/attachment-0001.sig>