* [PATCH v3] rtc: snvs: add Freescale rtc-snvs driver
@ 2012-07-13 7:31 Shawn Guo
2012-07-13 8:22 ` Shawn Guo
2012-08-15 0:03 ` [rtc-linux] " Andrew Morton
0 siblings, 2 replies; 11+ messages in thread
From: Shawn Guo @ 2012-07-13 7:31 UTC (permalink / raw)
To: linux-arm-kernel
Add an RTC driver for Freescale Secure Non-Volatile Storage (SNVS)
Low Power (LP) RTC.
Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
Reviewed-by: Stephen Boyd <sboyd@codeaurora.org>
Cc: Alessandro Zummo <a.zummo@towertech.it>
---
Changes since v2:
* Use SIMPLE_DEV_PM_OPS to save one #ifdef as suggested by Stephen
.../devicetree/bindings/crypto/fsl-sec4.txt | 51 +++
Documentation/devicetree/bindings/rtc/snvs-rtc.txt | 1 +
drivers/rtc/Kconfig | 11 +
drivers/rtc/Makefile | 1 +
drivers/rtc/rtc-snvs.c | 350 ++++++++++++++++++++
5 files changed, 414 insertions(+), 0 deletions(-)
create mode 100644 Documentation/devicetree/bindings/rtc/snvs-rtc.txt
create mode 100644 drivers/rtc/rtc-snvs.c
diff --git a/Documentation/devicetree/bindings/crypto/fsl-sec4.txt b/Documentation/devicetree/bindings/crypto/fsl-sec4.txt
index bf57ecd..bd7ce12 100644
--- a/Documentation/devicetree/bindings/crypto/fsl-sec4.txt
+++ b/Documentation/devicetree/bindings/crypto/fsl-sec4.txt
@@ -9,6 +9,7 @@ Copyright (C) 2008-2011 Freescale Semiconductor Inc.
-Run Time Integrity Check (RTIC) Node
-Run Time Integrity Check (RTIC) Memory Node
-Secure Non-Volatile Storage (SNVS) Node
+ -Secure Non-Volatile Storage (SNVS) Low Power (LP) RTC Node
-Full Example
NOTE: the SEC 4 is also known as Freescale's Cryptographic Accelerator
@@ -294,6 +295,27 @@ Secure Non-Volatile Storage (SNVS) Node
address and length of the SEC4 configuration
registers.
+ - #address-cells
+ Usage: required
+ Value type: <u32>
+ Definition: A standard property. Defines the number of cells
+ for representing physical addresses in child nodes. Must
+ have a value of 1.
+
+ - #size-cells
+ Usage: required
+ Value type: <u32>
+ Definition: A standard property. Defines the number of cells
+ for representing the size of physical addresses in
+ child nodes. Must have a value of 1.
+
+ - ranges
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: A standard property. Specifies the physical address
+ range of the SNVS register space. A triplet that includes
+ the child address, parent address, & length.
+
- interrupts
Usage: required
Value type: <prop_encoded-array>
@@ -314,11 +336,34 @@ EXAMPLE
sec_mon at 314000 {
compatible = "fsl,sec-v4.0-mon";
reg = <0x314000 0x1000>;
+ ranges = <0 0x314000 0x1000>;
interrupt-parent = <&mpic>;
interrupts = <93 2>;
};
=====================================================================
+Secure Non-Volatile Storage (SNVS) Low Power (LP) RTC Node
+
+ A SNVS child node that defines SNVS LP RTC.
+
+ - compatible
+ Usage: required
+ Value type: <string>
+ Definition: Must include "fsl,sec-v4.0-mon-rtc-lp".
+
+ - reg
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: A standard property. Specifies the physical
+ address and length of the SNVS LP configuration registers.
+
+EXAMPLE
+ sec_mon_rtc_lp at 314000 {
+ compatible = "fsl,sec-v4.0-mon-rtc-lp";
+ reg = <0x34 0x58>;
+ };
+
+=====================================================================
FULL EXAMPLE
crypto: crypto at 300000 {
@@ -390,8 +435,14 @@ FULL EXAMPLE
sec_mon: sec_mon at 314000 {
compatible = "fsl,sec-v4.0-mon";
reg = <0x314000 0x1000>;
+ ranges = <0 0x314000 0x1000>;
interrupt-parent = <&mpic>;
interrupts = <93 2>;
+
+ sec_mon_rtc_lp at 34 {
+ compatible = "fsl,sec-v4.0-mon-rtc-lp";
+ reg = <0x34 0x58>;
+ };
};
=====================================================================
diff --git a/Documentation/devicetree/bindings/rtc/snvs-rtc.txt b/Documentation/devicetree/bindings/rtc/snvs-rtc.txt
new file mode 100644
index 0000000..fb61ed7
--- /dev/null
+++ b/Documentation/devicetree/bindings/rtc/snvs-rtc.txt
@@ -0,0 +1 @@
+See Documentation/devicetree/bindings/crypto/fsl-sec4.txt for details.
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 08cbdb9..e174e16 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -1087,4 +1087,15 @@ config RTC_DRV_MXC
This driver can also be built as a module, if so, the module
will be called "rtc-mxc".
+config RTC_DRV_SNVS
+ tristate "Freescale SNVS RTC support"
+ depends on HAS_IOMEM
+ depends on OF
+ help
+ If you say yes here you get support for the Freescale SNVS
+ Low Power (LP) RTC module.
+
+ This driver can also be built as a module, if so, the module
+ will be called "rtc-snvs".
+
endif # RTC_CLASS
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 2973921..0e3ff92 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -95,6 +95,7 @@ obj-$(CONFIG_RTC_DRV_S35390A) += rtc-s35390a.o
obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o
obj-$(CONFIG_RTC_DRV_SA1100) += rtc-sa1100.o
obj-$(CONFIG_RTC_DRV_SH) += rtc-sh.o
+obj-$(CONFIG_RTC_DRV_SNVS) += rtc-snvs.o
obj-$(CONFIG_RTC_DRV_SPEAR) += rtc-spear.o
obj-$(CONFIG_RTC_DRV_STARFIRE) += rtc-starfire.o
obj-$(CONFIG_RTC_DRV_STK17TA8) += rtc-stk17ta8.o
diff --git a/drivers/rtc/rtc-snvs.c b/drivers/rtc/rtc-snvs.c
new file mode 100644
index 0000000..912f116
--- /dev/null
+++ b/drivers/rtc/rtc-snvs.c
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) 2011-2012 Freescale Semiconductor, Inc.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+
+/* These register offsets are relative to LP (Low Power) range */
+#define SNVS_LPCR 0x04
+#define SNVS_LPSR 0x18
+#define SNVS_LPSRTCMR 0x1c
+#define SNVS_LPSRTCLR 0x20
+#define SNVS_LPTAR 0x24
+#define SNVS_LPPGDR 0x30
+
+#define SNVS_LPCR_SRTC_ENV (1 << 0)
+#define SNVS_LPCR_LPTA_EN (1 << 1)
+#define SNVS_LPCR_LPWUI_EN (1 << 3)
+#define SNVS_LPSR_LPTA (1 << 0)
+
+#define SNVS_LPPGDR_INIT 0x41736166
+#define CNTR_TO_SECS_SH 15
+
+struct snvs_rtc_data {
+ struct rtc_device *rtc;
+ void __iomem *ioaddr;
+ int irq;
+ spinlock_t lock;
+};
+
+static u32 rtc_read_lp_counter(void __iomem *ioaddr)
+{
+ u64 read1, read2;
+
+ do {
+ read1 = readl(ioaddr + SNVS_LPSRTCMR);
+ read1 <<= 32;
+ read1 |= readl(ioaddr + SNVS_LPSRTCLR);
+
+ read2 = readl(ioaddr + SNVS_LPSRTCMR);
+ read2 <<= 32;
+ read2 |= readl(ioaddr + SNVS_LPSRTCLR);
+ } while (read1 != read2);
+
+ /* Convert 47-bit counter to 32-bit raw second count */
+ return (u32) (read1 >> CNTR_TO_SECS_SH);
+}
+
+static void rtc_write_sync_lp(void __iomem *ioaddr)
+{
+ u32 count1, count2, count3;
+ int i;
+
+ /* Wait for 3 CKIL cycles */
+ for (i = 0; i < 3; i++) {
+ do {
+ count1 = readl(ioaddr + SNVS_LPSRTCLR);
+ count2 = readl(ioaddr + SNVS_LPSRTCLR);
+ } while (count1 != count2);
+
+ /* Now wait until counter value changes */
+ do {
+ do {
+ count2 = readl(ioaddr + SNVS_LPSRTCLR);
+ count3 = readl(ioaddr + SNVS_LPSRTCLR);
+ } while (count2 != count3);
+ } while (count3 == count1);
+ }
+}
+
+static int snvs_rtc_enable(struct snvs_rtc_data *data, bool enable)
+{
+ unsigned long timeout = jiffies + msecs_to_jiffies(1);
+ unsigned long flags;
+ u32 lpcr;
+
+ spin_lock_irqsave(&data->lock, flags);
+
+ lpcr = readl(data->ioaddr + SNVS_LPCR);
+ if (enable)
+ lpcr |= SNVS_LPCR_SRTC_ENV;
+ else
+ lpcr &= ~SNVS_LPCR_SRTC_ENV;
+ writel(lpcr, data->ioaddr + SNVS_LPCR);
+
+ spin_unlock_irqrestore(&data->lock, flags);
+
+ while (1) {
+ lpcr = readl(data->ioaddr + SNVS_LPCR);
+
+ if (enable) {
+ if (lpcr & SNVS_LPCR_SRTC_ENV)
+ break;
+ } else {
+ if (!(lpcr & SNVS_LPCR_SRTC_ENV))
+ break;
+ }
+
+ if (time_after(jiffies, timeout))
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int snvs_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct snvs_rtc_data *data = dev_get_drvdata(dev);
+ unsigned long time = rtc_read_lp_counter(data->ioaddr);
+
+ rtc_time_to_tm(time, tm);
+
+ return 0;
+}
+
+static int snvs_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct snvs_rtc_data *data = dev_get_drvdata(dev);
+ unsigned long time;
+
+ rtc_tm_to_time(tm, &time);
+
+ /* Disable RTC first */
+ snvs_rtc_enable(data, false);
+
+ /* Write 32-bit time to 47-bit timer, leaving 15 LSBs blank */
+ writel(time << CNTR_TO_SECS_SH, data->ioaddr + SNVS_LPSRTCLR);
+ writel(time >> (32 - CNTR_TO_SECS_SH), data->ioaddr + SNVS_LPSRTCMR);
+
+ /* Enable RTC again */
+ snvs_rtc_enable(data, true);
+
+ return 0;
+}
+
+static int snvs_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct snvs_rtc_data *data = dev_get_drvdata(dev);
+ u32 lptar, lpsr;
+
+ lptar = readl(data->ioaddr + SNVS_LPTAR);
+ rtc_time_to_tm(lptar, &alrm->time);
+
+ lpsr = readl(data->ioaddr + SNVS_LPSR);
+ alrm->pending = (lpsr & SNVS_LPSR_LPTA) ? 1 : 0;
+
+ return 0;
+}
+
+static int snvs_rtc_alarm_irq_enable(struct device *dev, unsigned int enable)
+{
+ struct snvs_rtc_data *data = dev_get_drvdata(dev);
+ u32 lpcr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&data->lock, flags);
+
+ lpcr = readl(data->ioaddr + SNVS_LPCR);
+ if (enable)
+ lpcr |= (SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN);
+ else
+ lpcr &= ~(SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN);
+ writel(lpcr, data->ioaddr + SNVS_LPCR);
+
+ spin_unlock_irqrestore(&data->lock, flags);
+
+ rtc_write_sync_lp(data->ioaddr);
+
+ return 0;
+}
+
+static int snvs_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct snvs_rtc_data *data = dev_get_drvdata(dev);
+ struct rtc_time *alrm_tm = &alrm->time;
+ unsigned long time;
+ unsigned long flags;
+ u32 lpcr;
+
+ rtc_tm_to_time(alrm_tm, &time);
+
+ spin_lock_irqsave(&data->lock, flags);
+
+ /* Have to clear LPTA_EN before programming new alarm time in LPTAR */
+ lpcr = readl(data->ioaddr + SNVS_LPCR);
+ lpcr &= ~SNVS_LPCR_LPTA_EN;
+ writel(lpcr, data->ioaddr + SNVS_LPCR);
+
+ spin_unlock_irqrestore(&data->lock, flags);
+
+ writel(time, data->ioaddr + SNVS_LPTAR);
+
+ /* Clear alarm interrupt status bit */
+ writel(SNVS_LPSR_LPTA, data->ioaddr + SNVS_LPSR);
+
+ return snvs_rtc_alarm_irq_enable(dev, alrm->enabled);
+}
+
+static const struct rtc_class_ops snvs_rtc_ops = {
+ .read_time = snvs_rtc_read_time,
+ .set_time = snvs_rtc_set_time,
+ .read_alarm = snvs_rtc_read_alarm,
+ .set_alarm = snvs_rtc_set_alarm,
+ .alarm_irq_enable = snvs_rtc_alarm_irq_enable,
+};
+
+static irqreturn_t snvs_rtc_irq_handler(int irq, void *dev_id)
+{
+ struct device *dev = dev_id;
+ struct snvs_rtc_data *data = dev_get_drvdata(dev);
+ u32 lpsr;
+ u32 events = 0;
+
+ lpsr = readl(data->ioaddr + SNVS_LPSR);
+
+ if (lpsr & SNVS_LPSR_LPTA) {
+ events |= (RTC_AF | RTC_IRQF);
+
+ /* RTC alarm should be one-shot */
+ snvs_rtc_alarm_irq_enable(dev, 0);
+
+ rtc_update_irq(data->rtc, 1, events);
+ }
+
+ /* clear interrupt status */
+ writel(lpsr, data->ioaddr + SNVS_LPSR);
+
+ return events ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static int __devinit snvs_rtc_probe(struct platform_device *pdev)
+{
+ struct snvs_rtc_data *data;
+ struct resource *res;
+ int ret;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ data->ioaddr = devm_request_and_ioremap(&pdev->dev, res);
+ if (!data->ioaddr)
+ return -EADDRNOTAVAIL;
+
+ data->irq = platform_get_irq(pdev, 0);
+ if (data->irq < 0)
+ return data->irq;
+
+ platform_set_drvdata(pdev, data);
+
+ spin_lock_init(&data->lock);
+
+ /* Initialize glitch detect */
+ writel(SNVS_LPPGDR_INIT, data->ioaddr + SNVS_LPPGDR);
+
+ /* Clear interrupt status */
+ writel(0xffffffff, data->ioaddr + SNVS_LPSR);
+
+ /* Enable RTC */
+ snvs_rtc_enable(data, true);
+
+ device_init_wakeup(&pdev->dev, true);
+
+ ret = devm_request_irq(&pdev->dev, data->irq, snvs_rtc_irq_handler,
+ IRQF_SHARED, "rtc alarm", &pdev->dev);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to request irq %d: %d\n",
+ data->irq, ret);
+ return ret;
+ }
+
+ data->rtc = rtc_device_register(pdev->name, &pdev->dev,
+ &snvs_rtc_ops, THIS_MODULE);
+ if (IS_ERR(data->rtc)) {
+ ret = PTR_ERR(data->rtc);
+ dev_err(&pdev->dev, "failed to register rtc: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int __devexit snvs_rtc_remove(struct platform_device *pdev)
+{
+ struct snvs_rtc_data *data = platform_get_drvdata(pdev);
+
+ rtc_device_unregister(data->rtc);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int snvs_rtc_suspend(struct device *dev)
+{
+ struct snvs_rtc_data *data = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(data->irq);
+
+ return 0;
+}
+
+static int snvs_rtc_resume(struct device *dev)
+{
+ struct snvs_rtc_data *data = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(data->irq);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(snvs_rtc_pm_ops, snvs_rtc_suspend, snvs_rtc_resume);
+
+static const struct of_device_id __devinitconst snvs_dt_ids[] = {
+ { .compatible = "fsl,sec-v4.0-mon-rtc-lp", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, snvs_dt_ids);
+
+static struct platform_driver snvs_rtc_driver = {
+ .driver = {
+ .name = "snvs_rtc",
+ .owner = THIS_MODULE,
+ .pm = &snvs_rtc_pm_ops,
+ .of_match_table = snvs_dt_ids,
+ },
+ .probe = snvs_rtc_probe,
+ .remove = __devexit_p(snvs_rtc_remove),
+};
+module_platform_driver(snvs_rtc_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Freescale SNVS RTC Driver");
+MODULE_LICENSE("GPL");
--
1.7.4.1
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v3] rtc: snvs: add Freescale rtc-snvs driver
2012-07-13 7:31 [PATCH v3] rtc: snvs: add Freescale rtc-snvs driver Shawn Guo
@ 2012-07-13 8:22 ` Shawn Guo
2012-07-30 4:18 ` Shawn Guo
2012-08-15 0:03 ` [rtc-linux] " Andrew Morton
1 sibling, 1 reply; 11+ messages in thread
From: Shawn Guo @ 2012-07-13 8:22 UTC (permalink / raw)
To: linux-arm-kernel
Sorry for resending the patch. For some reason, the patch did not
show up in my Linaro mailbox, and I thought it was failed to deliver.
Alessandro,
Is it possible to queue the patch for 3.6?
Regards,
Shawn
On Fri, Jul 13, 2012 at 03:31:03PM +0800, Shawn Guo wrote:
> Add an RTC driver for Freescale Secure Non-Volatile Storage (SNVS)
> Low Power (LP) RTC.
>
> Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
> Reviewed-by: Stephen Boyd <sboyd@codeaurora.org>
> Cc: Alessandro Zummo <a.zummo@towertech.it>
> ---
> Changes since v2:
> * Use SIMPLE_DEV_PM_OPS to save one #ifdef as suggested by Stephen
>
> .../devicetree/bindings/crypto/fsl-sec4.txt | 51 +++
> Documentation/devicetree/bindings/rtc/snvs-rtc.txt | 1 +
> drivers/rtc/Kconfig | 11 +
> drivers/rtc/Makefile | 1 +
> drivers/rtc/rtc-snvs.c | 350 ++++++++++++++++++++
> 5 files changed, 414 insertions(+), 0 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/rtc/snvs-rtc.txt
> create mode 100644 drivers/rtc/rtc-snvs.c
>
> diff --git a/Documentation/devicetree/bindings/crypto/fsl-sec4.txt b/Documentation/devicetree/bindings/crypto/fsl-sec4.txt
> index bf57ecd..bd7ce12 100644
> --- a/Documentation/devicetree/bindings/crypto/fsl-sec4.txt
> +++ b/Documentation/devicetree/bindings/crypto/fsl-sec4.txt
> @@ -9,6 +9,7 @@ Copyright (C) 2008-2011 Freescale Semiconductor Inc.
> -Run Time Integrity Check (RTIC) Node
> -Run Time Integrity Check (RTIC) Memory Node
> -Secure Non-Volatile Storage (SNVS) Node
> + -Secure Non-Volatile Storage (SNVS) Low Power (LP) RTC Node
> -Full Example
>
> NOTE: the SEC 4 is also known as Freescale's Cryptographic Accelerator
> @@ -294,6 +295,27 @@ Secure Non-Volatile Storage (SNVS) Node
> address and length of the SEC4 configuration
> registers.
>
> + - #address-cells
> + Usage: required
> + Value type: <u32>
> + Definition: A standard property. Defines the number of cells
> + for representing physical addresses in child nodes. Must
> + have a value of 1.
> +
> + - #size-cells
> + Usage: required
> + Value type: <u32>
> + Definition: A standard property. Defines the number of cells
> + for representing the size of physical addresses in
> + child nodes. Must have a value of 1.
> +
> + - ranges
> + Usage: required
> + Value type: <prop-encoded-array>
> + Definition: A standard property. Specifies the physical address
> + range of the SNVS register space. A triplet that includes
> + the child address, parent address, & length.
> +
> - interrupts
> Usage: required
> Value type: <prop_encoded-array>
> @@ -314,11 +336,34 @@ EXAMPLE
> sec_mon at 314000 {
> compatible = "fsl,sec-v4.0-mon";
> reg = <0x314000 0x1000>;
> + ranges = <0 0x314000 0x1000>;
> interrupt-parent = <&mpic>;
> interrupts = <93 2>;
> };
>
> =====================================================================
> +Secure Non-Volatile Storage (SNVS) Low Power (LP) RTC Node
> +
> + A SNVS child node that defines SNVS LP RTC.
> +
> + - compatible
> + Usage: required
> + Value type: <string>
> + Definition: Must include "fsl,sec-v4.0-mon-rtc-lp".
> +
> + - reg
> + Usage: required
> + Value type: <prop-encoded-array>
> + Definition: A standard property. Specifies the physical
> + address and length of the SNVS LP configuration registers.
> +
> +EXAMPLE
> + sec_mon_rtc_lp at 314000 {
> + compatible = "fsl,sec-v4.0-mon-rtc-lp";
> + reg = <0x34 0x58>;
> + };
> +
> +=====================================================================
> FULL EXAMPLE
>
> crypto: crypto at 300000 {
> @@ -390,8 +435,14 @@ FULL EXAMPLE
> sec_mon: sec_mon at 314000 {
> compatible = "fsl,sec-v4.0-mon";
> reg = <0x314000 0x1000>;
> + ranges = <0 0x314000 0x1000>;
> interrupt-parent = <&mpic>;
> interrupts = <93 2>;
> +
> + sec_mon_rtc_lp at 34 {
> + compatible = "fsl,sec-v4.0-mon-rtc-lp";
> + reg = <0x34 0x58>;
> + };
> };
>
> =====================================================================
> diff --git a/Documentation/devicetree/bindings/rtc/snvs-rtc.txt b/Documentation/devicetree/bindings/rtc/snvs-rtc.txt
> new file mode 100644
> index 0000000..fb61ed7
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/rtc/snvs-rtc.txt
> @@ -0,0 +1 @@
> +See Documentation/devicetree/bindings/crypto/fsl-sec4.txt for details.
> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
> index 08cbdb9..e174e16 100644
> --- a/drivers/rtc/Kconfig
> +++ b/drivers/rtc/Kconfig
> @@ -1087,4 +1087,15 @@ config RTC_DRV_MXC
> This driver can also be built as a module, if so, the module
> will be called "rtc-mxc".
>
> +config RTC_DRV_SNVS
> + tristate "Freescale SNVS RTC support"
> + depends on HAS_IOMEM
> + depends on OF
> + help
> + If you say yes here you get support for the Freescale SNVS
> + Low Power (LP) RTC module.
> +
> + This driver can also be built as a module, if so, the module
> + will be called "rtc-snvs".
> +
> endif # RTC_CLASS
> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
> index 2973921..0e3ff92 100644
> --- a/drivers/rtc/Makefile
> +++ b/drivers/rtc/Makefile
> @@ -95,6 +95,7 @@ obj-$(CONFIG_RTC_DRV_S35390A) += rtc-s35390a.o
> obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o
> obj-$(CONFIG_RTC_DRV_SA1100) += rtc-sa1100.o
> obj-$(CONFIG_RTC_DRV_SH) += rtc-sh.o
> +obj-$(CONFIG_RTC_DRV_SNVS) += rtc-snvs.o
> obj-$(CONFIG_RTC_DRV_SPEAR) += rtc-spear.o
> obj-$(CONFIG_RTC_DRV_STARFIRE) += rtc-starfire.o
> obj-$(CONFIG_RTC_DRV_STK17TA8) += rtc-stk17ta8.o
> diff --git a/drivers/rtc/rtc-snvs.c b/drivers/rtc/rtc-snvs.c
> new file mode 100644
> index 0000000..912f116
> --- /dev/null
> +++ b/drivers/rtc/rtc-snvs.c
> @@ -0,0 +1,350 @@
> +/*
> + * Copyright (C) 2011-2012 Freescale Semiconductor, Inc.
> + *
> + * The code contained herein is licensed under the GNU General Public
> + * License. You may obtain a copy of the GNU General Public License
> + * Version 2 or later at the following locations:
> + *
> + * http://www.opensource.org/licenses/gpl-license.html
> + * http://www.gnu.org/copyleft/gpl.html
> + */
> +
> +#include <linux/init.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/rtc.h>
> +
> +/* These register offsets are relative to LP (Low Power) range */
> +#define SNVS_LPCR 0x04
> +#define SNVS_LPSR 0x18
> +#define SNVS_LPSRTCMR 0x1c
> +#define SNVS_LPSRTCLR 0x20
> +#define SNVS_LPTAR 0x24
> +#define SNVS_LPPGDR 0x30
> +
> +#define SNVS_LPCR_SRTC_ENV (1 << 0)
> +#define SNVS_LPCR_LPTA_EN (1 << 1)
> +#define SNVS_LPCR_LPWUI_EN (1 << 3)
> +#define SNVS_LPSR_LPTA (1 << 0)
> +
> +#define SNVS_LPPGDR_INIT 0x41736166
> +#define CNTR_TO_SECS_SH 15
> +
> +struct snvs_rtc_data {
> + struct rtc_device *rtc;
> + void __iomem *ioaddr;
> + int irq;
> + spinlock_t lock;
> +};
> +
> +static u32 rtc_read_lp_counter(void __iomem *ioaddr)
> +{
> + u64 read1, read2;
> +
> + do {
> + read1 = readl(ioaddr + SNVS_LPSRTCMR);
> + read1 <<= 32;
> + read1 |= readl(ioaddr + SNVS_LPSRTCLR);
> +
> + read2 = readl(ioaddr + SNVS_LPSRTCMR);
> + read2 <<= 32;
> + read2 |= readl(ioaddr + SNVS_LPSRTCLR);
> + } while (read1 != read2);
> +
> + /* Convert 47-bit counter to 32-bit raw second count */
> + return (u32) (read1 >> CNTR_TO_SECS_SH);
> +}
> +
> +static void rtc_write_sync_lp(void __iomem *ioaddr)
> +{
> + u32 count1, count2, count3;
> + int i;
> +
> + /* Wait for 3 CKIL cycles */
> + for (i = 0; i < 3; i++) {
> + do {
> + count1 = readl(ioaddr + SNVS_LPSRTCLR);
> + count2 = readl(ioaddr + SNVS_LPSRTCLR);
> + } while (count1 != count2);
> +
> + /* Now wait until counter value changes */
> + do {
> + do {
> + count2 = readl(ioaddr + SNVS_LPSRTCLR);
> + count3 = readl(ioaddr + SNVS_LPSRTCLR);
> + } while (count2 != count3);
> + } while (count3 == count1);
> + }
> +}
> +
> +static int snvs_rtc_enable(struct snvs_rtc_data *data, bool enable)
> +{
> + unsigned long timeout = jiffies + msecs_to_jiffies(1);
> + unsigned long flags;
> + u32 lpcr;
> +
> + spin_lock_irqsave(&data->lock, flags);
> +
> + lpcr = readl(data->ioaddr + SNVS_LPCR);
> + if (enable)
> + lpcr |= SNVS_LPCR_SRTC_ENV;
> + else
> + lpcr &= ~SNVS_LPCR_SRTC_ENV;
> + writel(lpcr, data->ioaddr + SNVS_LPCR);
> +
> + spin_unlock_irqrestore(&data->lock, flags);
> +
> + while (1) {
> + lpcr = readl(data->ioaddr + SNVS_LPCR);
> +
> + if (enable) {
> + if (lpcr & SNVS_LPCR_SRTC_ENV)
> + break;
> + } else {
> + if (!(lpcr & SNVS_LPCR_SRTC_ENV))
> + break;
> + }
> +
> + if (time_after(jiffies, timeout))
> + return -ETIMEDOUT;
> + }
> +
> + return 0;
> +}
> +
> +static int snvs_rtc_read_time(struct device *dev, struct rtc_time *tm)
> +{
> + struct snvs_rtc_data *data = dev_get_drvdata(dev);
> + unsigned long time = rtc_read_lp_counter(data->ioaddr);
> +
> + rtc_time_to_tm(time, tm);
> +
> + return 0;
> +}
> +
> +static int snvs_rtc_set_time(struct device *dev, struct rtc_time *tm)
> +{
> + struct snvs_rtc_data *data = dev_get_drvdata(dev);
> + unsigned long time;
> +
> + rtc_tm_to_time(tm, &time);
> +
> + /* Disable RTC first */
> + snvs_rtc_enable(data, false);
> +
> + /* Write 32-bit time to 47-bit timer, leaving 15 LSBs blank */
> + writel(time << CNTR_TO_SECS_SH, data->ioaddr + SNVS_LPSRTCLR);
> + writel(time >> (32 - CNTR_TO_SECS_SH), data->ioaddr + SNVS_LPSRTCMR);
> +
> + /* Enable RTC again */
> + snvs_rtc_enable(data, true);
> +
> + return 0;
> +}
> +
> +static int snvs_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
> +{
> + struct snvs_rtc_data *data = dev_get_drvdata(dev);
> + u32 lptar, lpsr;
> +
> + lptar = readl(data->ioaddr + SNVS_LPTAR);
> + rtc_time_to_tm(lptar, &alrm->time);
> +
> + lpsr = readl(data->ioaddr + SNVS_LPSR);
> + alrm->pending = (lpsr & SNVS_LPSR_LPTA) ? 1 : 0;
> +
> + return 0;
> +}
> +
> +static int snvs_rtc_alarm_irq_enable(struct device *dev, unsigned int enable)
> +{
> + struct snvs_rtc_data *data = dev_get_drvdata(dev);
> + u32 lpcr;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&data->lock, flags);
> +
> + lpcr = readl(data->ioaddr + SNVS_LPCR);
> + if (enable)
> + lpcr |= (SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN);
> + else
> + lpcr &= ~(SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN);
> + writel(lpcr, data->ioaddr + SNVS_LPCR);
> +
> + spin_unlock_irqrestore(&data->lock, flags);
> +
> + rtc_write_sync_lp(data->ioaddr);
> +
> + return 0;
> +}
> +
> +static int snvs_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
> +{
> + struct snvs_rtc_data *data = dev_get_drvdata(dev);
> + struct rtc_time *alrm_tm = &alrm->time;
> + unsigned long time;
> + unsigned long flags;
> + u32 lpcr;
> +
> + rtc_tm_to_time(alrm_tm, &time);
> +
> + spin_lock_irqsave(&data->lock, flags);
> +
> + /* Have to clear LPTA_EN before programming new alarm time in LPTAR */
> + lpcr = readl(data->ioaddr + SNVS_LPCR);
> + lpcr &= ~SNVS_LPCR_LPTA_EN;
> + writel(lpcr, data->ioaddr + SNVS_LPCR);
> +
> + spin_unlock_irqrestore(&data->lock, flags);
> +
> + writel(time, data->ioaddr + SNVS_LPTAR);
> +
> + /* Clear alarm interrupt status bit */
> + writel(SNVS_LPSR_LPTA, data->ioaddr + SNVS_LPSR);
> +
> + return snvs_rtc_alarm_irq_enable(dev, alrm->enabled);
> +}
> +
> +static const struct rtc_class_ops snvs_rtc_ops = {
> + .read_time = snvs_rtc_read_time,
> + .set_time = snvs_rtc_set_time,
> + .read_alarm = snvs_rtc_read_alarm,
> + .set_alarm = snvs_rtc_set_alarm,
> + .alarm_irq_enable = snvs_rtc_alarm_irq_enable,
> +};
> +
> +static irqreturn_t snvs_rtc_irq_handler(int irq, void *dev_id)
> +{
> + struct device *dev = dev_id;
> + struct snvs_rtc_data *data = dev_get_drvdata(dev);
> + u32 lpsr;
> + u32 events = 0;
> +
> + lpsr = readl(data->ioaddr + SNVS_LPSR);
> +
> + if (lpsr & SNVS_LPSR_LPTA) {
> + events |= (RTC_AF | RTC_IRQF);
> +
> + /* RTC alarm should be one-shot */
> + snvs_rtc_alarm_irq_enable(dev, 0);
> +
> + rtc_update_irq(data->rtc, 1, events);
> + }
> +
> + /* clear interrupt status */
> + writel(lpsr, data->ioaddr + SNVS_LPSR);
> +
> + return events ? IRQ_HANDLED : IRQ_NONE;
> +}
> +
> +static int __devinit snvs_rtc_probe(struct platform_device *pdev)
> +{
> + struct snvs_rtc_data *data;
> + struct resource *res;
> + int ret;
> +
> + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
> + if (!data)
> + return -ENOMEM;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + data->ioaddr = devm_request_and_ioremap(&pdev->dev, res);
> + if (!data->ioaddr)
> + return -EADDRNOTAVAIL;
> +
> + data->irq = platform_get_irq(pdev, 0);
> + if (data->irq < 0)
> + return data->irq;
> +
> + platform_set_drvdata(pdev, data);
> +
> + spin_lock_init(&data->lock);
> +
> + /* Initialize glitch detect */
> + writel(SNVS_LPPGDR_INIT, data->ioaddr + SNVS_LPPGDR);
> +
> + /* Clear interrupt status */
> + writel(0xffffffff, data->ioaddr + SNVS_LPSR);
> +
> + /* Enable RTC */
> + snvs_rtc_enable(data, true);
> +
> + device_init_wakeup(&pdev->dev, true);
> +
> + ret = devm_request_irq(&pdev->dev, data->irq, snvs_rtc_irq_handler,
> + IRQF_SHARED, "rtc alarm", &pdev->dev);
> + if (ret) {
> + dev_err(&pdev->dev, "failed to request irq %d: %d\n",
> + data->irq, ret);
> + return ret;
> + }
> +
> + data->rtc = rtc_device_register(pdev->name, &pdev->dev,
> + &snvs_rtc_ops, THIS_MODULE);
> + if (IS_ERR(data->rtc)) {
> + ret = PTR_ERR(data->rtc);
> + dev_err(&pdev->dev, "failed to register rtc: %d\n", ret);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int __devexit snvs_rtc_remove(struct platform_device *pdev)
> +{
> + struct snvs_rtc_data *data = platform_get_drvdata(pdev);
> +
> + rtc_device_unregister(data->rtc);
> +
> + return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int snvs_rtc_suspend(struct device *dev)
> +{
> + struct snvs_rtc_data *data = dev_get_drvdata(dev);
> +
> + if (device_may_wakeup(dev))
> + enable_irq_wake(data->irq);
> +
> + return 0;
> +}
> +
> +static int snvs_rtc_resume(struct device *dev)
> +{
> + struct snvs_rtc_data *data = dev_get_drvdata(dev);
> +
> + if (device_may_wakeup(dev))
> + disable_irq_wake(data->irq);
> +
> + return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(snvs_rtc_pm_ops, snvs_rtc_suspend, snvs_rtc_resume);
> +
> +static const struct of_device_id __devinitconst snvs_dt_ids[] = {
> + { .compatible = "fsl,sec-v4.0-mon-rtc-lp", },
> + { /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, snvs_dt_ids);
> +
> +static struct platform_driver snvs_rtc_driver = {
> + .driver = {
> + .name = "snvs_rtc",
> + .owner = THIS_MODULE,
> + .pm = &snvs_rtc_pm_ops,
> + .of_match_table = snvs_dt_ids,
> + },
> + .probe = snvs_rtc_probe,
> + .remove = __devexit_p(snvs_rtc_remove),
> +};
> +module_platform_driver(snvs_rtc_driver);
> +
> +MODULE_AUTHOR("Freescale Semiconductor, Inc.");
> +MODULE_DESCRIPTION("Freescale SNVS RTC Driver");
> +MODULE_LICENSE("GPL");
> --
> 1.7.4.1
>
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v3] rtc: snvs: add Freescale rtc-snvs driver
2012-07-13 8:22 ` Shawn Guo
@ 2012-07-30 4:18 ` Shawn Guo
0 siblings, 0 replies; 11+ messages in thread
From: Shawn Guo @ 2012-07-30 4:18 UTC (permalink / raw)
To: linux-arm-kernel
On Fri, Jul 13, 2012 at 04:22:04PM +0800, Shawn Guo wrote:
> Alessandro,
>
> Is it possible to queue the patch for 3.6?
>
Hi Andrew,
It seems that you are collecting RTC patches? Can you please have
a look at the patch and see if it's possible to send it for 3.6?
--
Regards,
Shawn
^ permalink raw reply [flat|nested] 11+ messages in thread
* [rtc-linux] [PATCH v3] rtc: snvs: add Freescale rtc-snvs driver
2012-07-13 7:31 [PATCH v3] rtc: snvs: add Freescale rtc-snvs driver Shawn Guo
2012-07-13 8:22 ` Shawn Guo
@ 2012-08-15 0:03 ` Andrew Morton
2012-08-15 14:16 ` Shawn Guo
1 sibling, 1 reply; 11+ messages in thread
From: Andrew Morton @ 2012-08-15 0:03 UTC (permalink / raw)
To: linux-arm-kernel
On Fri, 13 Jul 2012 15:31:03 +0800
Shawn Guo <shawn.guo@linaro.org> wrote:
> +static int snvs_rtc_enable(struct snvs_rtc_data *data, bool enable)
> +{
> + unsigned long timeout = jiffies + msecs_to_jiffies(1);
> + unsigned long flags;
> + u32 lpcr;
> +
> + spin_lock_irqsave(&data->lock, flags);
> +
> + lpcr = readl(data->ioaddr + SNVS_LPCR);
> + if (enable)
> + lpcr |= SNVS_LPCR_SRTC_ENV;
> + else
> + lpcr &= ~SNVS_LPCR_SRTC_ENV;
> + writel(lpcr, data->ioaddr + SNVS_LPCR);
> +
> + spin_unlock_irqrestore(&data->lock, flags);
> +
> + while (1) {
> + lpcr = readl(data->ioaddr + SNVS_LPCR);
> +
> + if (enable) {
> + if (lpcr & SNVS_LPCR_SRTC_ENV)
> + break;
> + } else {
> + if (!(lpcr & SNVS_LPCR_SRTC_ENV))
> + break;
> + }
> +
> + if (time_after(jiffies, timeout))
> + return -ETIMEDOUT;
> + }
> +
> + return 0;
> +}
The timeout code here is fragile. If acquiring the spinlock takes more
than a millisecond or if this thread gets interrupted or preempted then
we could easily execute that loop just a single time, and fail.
It would be better to retry a fixed number of times, say 1000? That
would take around 1 millisecond, but might be overkill.
^ permalink raw reply [flat|nested] 11+ messages in thread
* [rtc-linux] [PATCH v3] rtc: snvs: add Freescale rtc-snvs driver
2012-08-15 0:03 ` [rtc-linux] " Andrew Morton
@ 2012-08-15 14:16 ` Shawn Guo
2012-08-15 20:32 ` Andrew Morton
0 siblings, 1 reply; 11+ messages in thread
From: Shawn Guo @ 2012-08-15 14:16 UTC (permalink / raw)
To: linux-arm-kernel
Thanks for looking at the patch, Andrew.
On Tue, Aug 14, 2012 at 05:03:00PM -0700, Andrew Morton wrote:
> On Fri, 13 Jul 2012 15:31:03 +0800
> Shawn Guo <shawn.guo@linaro.org> wrote:
>
> > +static int snvs_rtc_enable(struct snvs_rtc_data *data, bool enable)
> > +{
> > + unsigned long timeout = jiffies + msecs_to_jiffies(1);
> > + unsigned long flags;
> > + u32 lpcr;
> > +
> > + spin_lock_irqsave(&data->lock, flags);
> > +
> > + lpcr = readl(data->ioaddr + SNVS_LPCR);
> > + if (enable)
> > + lpcr |= SNVS_LPCR_SRTC_ENV;
> > + else
> > + lpcr &= ~SNVS_LPCR_SRTC_ENV;
> > + writel(lpcr, data->ioaddr + SNVS_LPCR);
> > +
> > + spin_unlock_irqrestore(&data->lock, flags);
> > +
> > + while (1) {
> > + lpcr = readl(data->ioaddr + SNVS_LPCR);
> > +
> > + if (enable) {
> > + if (lpcr & SNVS_LPCR_SRTC_ENV)
> > + break;
> > + } else {
> > + if (!(lpcr & SNVS_LPCR_SRTC_ENV))
> > + break;
> > + }
> > +
> > + if (time_after(jiffies, timeout))
> > + return -ETIMEDOUT;
> > + }
> > +
> > + return 0;
> > +}
>
> The timeout code here is fragile. If acquiring the spinlock takes more
> than a millisecond or if this thread gets interrupted or preempted then
> we could easily execute that loop just a single time, and fail.
>
So what about moving the timeout initialization to right above the
while(1) loop?
> It would be better to retry a fixed number of times, say 1000? That
> would take around 1 millisecond, but might be overkill.
>
How long a 1000 times loop takes really depends on the cpu frequency,
right?
BTW, I have received the notification telling that the patch has been
applied on -mm tree. So should I just send you an incremental patch
to address the comment?
--
Regards,
Shawn
^ permalink raw reply [flat|nested] 11+ messages in thread
* [rtc-linux] [PATCH v3] rtc: snvs: add Freescale rtc-snvs driver
2012-08-15 14:16 ` Shawn Guo
@ 2012-08-15 20:32 ` Andrew Morton
2012-08-16 8:08 ` Lothar Waßmann
0 siblings, 1 reply; 11+ messages in thread
From: Andrew Morton @ 2012-08-15 20:32 UTC (permalink / raw)
To: linux-arm-kernel
On Wed, 15 Aug 2012 22:16:10 +0800
Shawn Guo <shawn.guo@linaro.org> wrote:
> Thanks for looking at the patch, Andrew.
>
> On Tue, Aug 14, 2012 at 05:03:00PM -0700, Andrew Morton wrote:
> > On Fri, 13 Jul 2012 15:31:03 +0800
> > Shawn Guo <shawn.guo@linaro.org> wrote:
> >
> > > +static int snvs_rtc_enable(struct snvs_rtc_data *data, bool enable)
> > > +{
> > > + unsigned long timeout = jiffies + msecs_to_jiffies(1);
> > > + unsigned long flags;
> > > + u32 lpcr;
> > > +
> > > + spin_lock_irqsave(&data->lock, flags);
> > > +
> > > + lpcr = readl(data->ioaddr + SNVS_LPCR);
> > > + if (enable)
> > > + lpcr |= SNVS_LPCR_SRTC_ENV;
> > > + else
> > > + lpcr &= ~SNVS_LPCR_SRTC_ENV;
> > > + writel(lpcr, data->ioaddr + SNVS_LPCR);
> > > +
> > > + spin_unlock_irqrestore(&data->lock, flags);
> > > +
> > > + while (1) {
> > > + lpcr = readl(data->ioaddr + SNVS_LPCR);
> > > +
> > > + if (enable) {
> > > + if (lpcr & SNVS_LPCR_SRTC_ENV)
> > > + break;
> > > + } else {
> > > + if (!(lpcr & SNVS_LPCR_SRTC_ENV))
> > > + break;
> > > + }
> > > +
> > > + if (time_after(jiffies, timeout))
> > > + return -ETIMEDOUT;
> > > + }
> > > +
> > > + return 0;
> > > +}
> >
> > The timeout code here is fragile. If acquiring the spinlock takes more
> > than a millisecond or if this thread gets interrupted or preempted then
> > we could easily execute that loop just a single time, and fail.
> >
> So what about moving the timeout initialization to right above the
> while(1) loop?
It still has the same problem - a well-timed preemption would cause a
timeout.
> > It would be better to retry a fixed number of times, say 1000? That
> > would take around 1 millisecond, but might be overkill.
> >
> How long a 1000 times loop takes really depends on the cpu frequency,
> right?
No, it will depend on the preiod of that readl(), which typically takes
much much longer than a cpu cycle. It depends a lot on the bus type
but I'd guess that the readl would take 100's of nanoseconds. Thus we
can use the expected readl duration to control the timeout interval.
> BTW, I have received the notification telling that the patch has been
> applied on -mm tree. So should I just send you an incremental patch
> to address the comment?
An incremental is nice, as it lets people see what changed. A full
replacement is OK for me as well - I turn it into an incrememntal so
that I and others can review the change and I fold the two back
together again before sending the patch to someone else (in this case,
Linus).
^ permalink raw reply [flat|nested] 11+ messages in thread
* [rtc-linux] [PATCH v3] rtc: snvs: add Freescale rtc-snvs driver
2012-08-15 20:32 ` Andrew Morton
@ 2012-08-16 8:08 ` Lothar Waßmann
2012-08-17 6:14 ` Shawn Guo
0 siblings, 1 reply; 11+ messages in thread
From: Lothar Waßmann @ 2012-08-16 8:08 UTC (permalink / raw)
To: linux-arm-kernel
Hi,
Andrew Morton writes:
> On Wed, 15 Aug 2012 22:16:10 +0800
> Shawn Guo <shawn.guo@linaro.org> wrote:
>
> > Thanks for looking at the patch, Andrew.
> >
> > On Tue, Aug 14, 2012 at 05:03:00PM -0700, Andrew Morton wrote:
> > > On Fri, 13 Jul 2012 15:31:03 +0800
> > > Shawn Guo <shawn.guo@linaro.org> wrote:
> > >
> > > > +static int snvs_rtc_enable(struct snvs_rtc_data *data, bool enable)
> > > > +{
> > > > + unsigned long timeout = jiffies + msecs_to_jiffies(1);
> > > > + unsigned long flags;
> > > > + u32 lpcr;
> > > > +
> > > > + spin_lock_irqsave(&data->lock, flags);
> > > > +
> > > > + lpcr = readl(data->ioaddr + SNVS_LPCR);
> > > > + if (enable)
> > > > + lpcr |= SNVS_LPCR_SRTC_ENV;
> > > > + else
> > > > + lpcr &= ~SNVS_LPCR_SRTC_ENV;
> > > > + writel(lpcr, data->ioaddr + SNVS_LPCR);
> > > > +
> > > > + spin_unlock_irqrestore(&data->lock, flags);
> > > > +
> > > > + while (1) {
> > > > + lpcr = readl(data->ioaddr + SNVS_LPCR);
> > > > +
> > > > + if (enable) {
> > > > + if (lpcr & SNVS_LPCR_SRTC_ENV)
> > > > + break;
> > > > + } else {
> > > > + if (!(lpcr & SNVS_LPCR_SRTC_ENV))
> > > > + break;
> > > > + }
> > > > +
> > > > + if (time_after(jiffies, timeout))
> > > > + return -ETIMEDOUT;
> > > > + }
> > > > +
> > > > + return 0;
> > > > +}
> > >
> > > The timeout code here is fragile. If acquiring the spinlock takes more
> > > than a millisecond or if this thread gets interrupted or preempted then
> > > we could easily execute that loop just a single time, and fail.
> > >
> > So what about moving the timeout initialization to right above the
> > while(1) loop?
>
> It still has the same problem - a well-timed preemption would cause a
> timeout.
>
> > > It would be better to retry a fixed number of times, say 1000? That
> > > would take around 1 millisecond, but might be overkill.
> > >
> > How long a 1000 times loop takes really depends on the cpu frequency,
> > right?
>
> No, it will depend on the preiod of that readl(), which typically takes
> much much longer than a cpu cycle. It depends a lot on the bus type
> but I'd guess that the readl would take 100's of nanoseconds. Thus we
> can use the expected readl duration to control the timeout interval.
>
Why not read the register once more in case of a timeout, which
would guarantee to have a current view of the register contents no
matter how long the process may have been descheduled before:
[...]
static int snvs_rtc_enable_done(int enable, void __iomem *addr)
{
u32 lpcr = readl(addr + SNVS_LPCR);
return !!enable ^ !(lpcr & SNVS_LPCR_SRTC_ENV);
}
[...]
while (1) {
if (svns_rtc_enable_done(enable, data->ioaddr))
break;
if (time_after(jiffies, timeout)) {
if (!svns_rtc_enable_done(enable, data->ioaddr))
return -ETIMEDOUT;
}
}
Lothar Wa?mann
--
___________________________________________________________
Ka-Ro electronics GmbH | Pascalstra?e 22 | D - 52076 Aachen
Phone: +49 2408 1402-0 | Fax: +49 2408 1402-10
Gesch?ftsf?hrer: Matthias Kaussen
Handelsregistereintrag: Amtsgericht Aachen, HRB 4996
www.karo-electronics.de | info at karo-electronics.de
___________________________________________________________
^ permalink raw reply [flat|nested] 11+ messages in thread
* [rtc-linux] [PATCH v3] rtc: snvs: add Freescale rtc-snvs driver
2012-08-16 8:08 ` Lothar Waßmann
@ 2012-08-17 6:14 ` Shawn Guo
2012-08-18 6:43 ` Lothar Waßmann
0 siblings, 1 reply; 11+ messages in thread
From: Shawn Guo @ 2012-08-17 6:14 UTC (permalink / raw)
To: linux-arm-kernel
On Thu, Aug 16, 2012 at 10:08:04AM +0200, Lothar Wa?mann wrote:
> Why not read the register once more in case of a timeout, which
> would guarantee to have a current view of the register contents no
> matter how long the process may have been descheduled before:
>
> [...]
> static int snvs_rtc_enable_done(int enable, void __iomem *addr)
> {
> u32 lpcr = readl(addr + SNVS_LPCR);
>
> return !!enable ^ !(lpcr & SNVS_LPCR_SRTC_ENV);
> }
> [...]
> while (1) {
> if (svns_rtc_enable_done(enable, data->ioaddr))
> break;
>
> if (time_after(jiffies, timeout)) {
> if (!svns_rtc_enable_done(enable, data->ioaddr))
> return -ETIMEDOUT;
> }
> }
>
I'm fine with this solution. Can you send a formal patch for this?
Then we will have two incremental patches (I already sent the fixed
times loop one) for Andrew to choose one.
--
Regards,
Shawn
^ permalink raw reply [flat|nested] 11+ messages in thread
* [rtc-linux] [PATCH v3] rtc: snvs: add Freescale rtc-snvs driver
2012-08-17 6:14 ` Shawn Guo
@ 2012-08-18 6:43 ` Lothar Waßmann
2012-08-18 7:03 ` Shawn Guo
0 siblings, 1 reply; 11+ messages in thread
From: Lothar Waßmann @ 2012-08-18 6:43 UTC (permalink / raw)
To: linux-arm-kernel
Hi,
Shawn Guo writes:
> On Thu, Aug 16, 2012 at 10:08:04AM +0200, Lothar Wa?mann wrote:
> > Why not read the register once more in case of a timeout, which
> > would guarantee to have a current view of the register contents no
> > matter how long the process may have been descheduled before:
> >
> > [...]
> > static int snvs_rtc_enable_done(int enable, void __iomem *addr)
> > {
> > u32 lpcr = readl(addr + SNVS_LPCR);
> >
> > return !!enable ^ !(lpcr & SNVS_LPCR_SRTC_ENV);
> > }
> > [...]
> > while (1) {
> > if (svns_rtc_enable_done(enable, data->ioaddr))
> > break;
> >
> > if (time_after(jiffies, timeout)) {
> > if (!svns_rtc_enable_done(enable, data->ioaddr))
> > return -ETIMEDOUT;
> > }
> > }
> >
> I'm fine with this solution. Can you send a formal patch for this?
> Then we will have two incremental patches (I already sent the fixed
> times loop one) for Andrew to choose one.
>
What should I base this patch on?
Lothar Wa?mann
--
___________________________________________________________
Ka-Ro electronics GmbH | Pascalstra?e 22 | D - 52076 Aachen
Phone: +49 2408 1402-0 | Fax: +49 2408 1402-10
Gesch?ftsf?hrer: Matthias Kaussen
Handelsregistereintrag: Amtsgericht Aachen, HRB 4996
www.karo-electronics.de | info at karo-electronics.de
___________________________________________________________
^ permalink raw reply [flat|nested] 11+ messages in thread
* [rtc-linux] [PATCH v3] rtc: snvs: add Freescale rtc-snvs driver
2012-08-18 6:43 ` Lothar Waßmann
@ 2012-08-18 7:03 ` Shawn Guo
0 siblings, 0 replies; 11+ messages in thread
From: Shawn Guo @ 2012-08-18 7:03 UTC (permalink / raw)
To: linux-arm-kernel
On Sat, Aug 18, 2012 at 08:43:04AM +0200, Lothar Wa?mann wrote:
> Hi,
>
> Shawn Guo writes:
> > On Thu, Aug 16, 2012 at 10:08:04AM +0200, Lothar Wa?mann wrote:
> > > Why not read the register once more in case of a timeout, which
> > > would guarantee to have a current view of the register contents no
> > > matter how long the process may have been descheduled before:
> > >
> > > [...]
> > > static int snvs_rtc_enable_done(int enable, void __iomem *addr)
> > > {
> > > u32 lpcr = readl(addr + SNVS_LPCR);
> > >
> > > return !!enable ^ !(lpcr & SNVS_LPCR_SRTC_ENV);
> > > }
> > > [...]
> > > while (1) {
> > > if (svns_rtc_enable_done(enable, data->ioaddr))
> > > break;
> > >
> > > if (time_after(jiffies, timeout)) {
> > > if (!svns_rtc_enable_done(enable, data->ioaddr))
> > > return -ETIMEDOUT;
> > > }
> > > }
> > >
> > I'm fine with this solution. Can you send a formal patch for this?
> > Then we will have two incremental patches (I already sent the fixed
> > times loop one) for Andrew to choose one.
> >
> What should I base this patch on?
>
v3.6-rc1 with "[PATCH v3] rtc: snvs: add Freescale rtc-snvs driver"
applied.
--
Regards,
Shawn
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v3] rtc: snvs: add Freescale rtc-snvs driver
@ 2012-07-11 8:44 Shawn Guo
0 siblings, 0 replies; 11+ messages in thread
From: Shawn Guo @ 2012-07-11 8:44 UTC (permalink / raw)
To: linux-arm-kernel
Add an RTC driver for Freescale Secure Non-Volatile Storage (SNVS)
Low Power (LP) RTC.
Though the hardware block can be found on Freescale both ARM/IMX and
PowerPC families, the patch is only aiming at and tested on ARM/IMX
for now.
Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
Reviewed-by: Stephen Boyd <sboyd@codeaurora.org>
Cc: Alessandro Zummo <a.zummo@towertech.it>
---
Changes since v2:
* Use SIMPLE_DEV_PM_OPS to save one #ifdef as suggested by Stephen Boyd
* Update commit message to make it clear that the patch only supports
ARM/IMX for now
.../devicetree/bindings/crypto/fsl-sec4.txt | 51 +++
Documentation/devicetree/bindings/rtc/snvs-rtc.txt | 1 +
drivers/rtc/Kconfig | 11 +
drivers/rtc/Makefile | 1 +
drivers/rtc/rtc-snvs.c | 350 ++++++++++++++++++++
5 files changed, 414 insertions(+), 0 deletions(-)
create mode 100644 Documentation/devicetree/bindings/rtc/snvs-rtc.txt
create mode 100644 drivers/rtc/rtc-snvs.c
diff --git a/Documentation/devicetree/bindings/crypto/fsl-sec4.txt b/Documentation/devicetree/bindings/crypto/fsl-sec4.txt
index bf57ecd..bd7ce12 100644
--- a/Documentation/devicetree/bindings/crypto/fsl-sec4.txt
+++ b/Documentation/devicetree/bindings/crypto/fsl-sec4.txt
@@ -9,6 +9,7 @@ Copyright (C) 2008-2011 Freescale Semiconductor Inc.
-Run Time Integrity Check (RTIC) Node
-Run Time Integrity Check (RTIC) Memory Node
-Secure Non-Volatile Storage (SNVS) Node
+ -Secure Non-Volatile Storage (SNVS) Low Power (LP) RTC Node
-Full Example
NOTE: the SEC 4 is also known as Freescale's Cryptographic Accelerator
@@ -294,6 +295,27 @@ Secure Non-Volatile Storage (SNVS) Node
address and length of the SEC4 configuration
registers.
+ - #address-cells
+ Usage: required
+ Value type: <u32>
+ Definition: A standard property. Defines the number of cells
+ for representing physical addresses in child nodes. Must
+ have a value of 1.
+
+ - #size-cells
+ Usage: required
+ Value type: <u32>
+ Definition: A standard property. Defines the number of cells
+ for representing the size of physical addresses in
+ child nodes. Must have a value of 1.
+
+ - ranges
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: A standard property. Specifies the physical address
+ range of the SNVS register space. A triplet that includes
+ the child address, parent address, & length.
+
- interrupts
Usage: required
Value type: <prop_encoded-array>
@@ -314,11 +336,34 @@ EXAMPLE
sec_mon at 314000 {
compatible = "fsl,sec-v4.0-mon";
reg = <0x314000 0x1000>;
+ ranges = <0 0x314000 0x1000>;
interrupt-parent = <&mpic>;
interrupts = <93 2>;
};
=====================================================================
+Secure Non-Volatile Storage (SNVS) Low Power (LP) RTC Node
+
+ A SNVS child node that defines SNVS LP RTC.
+
+ - compatible
+ Usage: required
+ Value type: <string>
+ Definition: Must include "fsl,sec-v4.0-mon-rtc-lp".
+
+ - reg
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: A standard property. Specifies the physical
+ address and length of the SNVS LP configuration registers.
+
+EXAMPLE
+ sec_mon_rtc_lp at 314000 {
+ compatible = "fsl,sec-v4.0-mon-rtc-lp";
+ reg = <0x34 0x58>;
+ };
+
+=====================================================================
FULL EXAMPLE
crypto: crypto at 300000 {
@@ -390,8 +435,14 @@ FULL EXAMPLE
sec_mon: sec_mon at 314000 {
compatible = "fsl,sec-v4.0-mon";
reg = <0x314000 0x1000>;
+ ranges = <0 0x314000 0x1000>;
interrupt-parent = <&mpic>;
interrupts = <93 2>;
+
+ sec_mon_rtc_lp at 34 {
+ compatible = "fsl,sec-v4.0-mon-rtc-lp";
+ reg = <0x34 0x58>;
+ };
};
=====================================================================
diff --git a/Documentation/devicetree/bindings/rtc/snvs-rtc.txt b/Documentation/devicetree/bindings/rtc/snvs-rtc.txt
new file mode 100644
index 0000000..fb61ed7
--- /dev/null
+++ b/Documentation/devicetree/bindings/rtc/snvs-rtc.txt
@@ -0,0 +1 @@
+See Documentation/devicetree/bindings/crypto/fsl-sec4.txt for details.
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 08cbdb9..e174e16 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -1087,4 +1087,15 @@ config RTC_DRV_MXC
This driver can also be built as a module, if so, the module
will be called "rtc-mxc".
+config RTC_DRV_SNVS
+ tristate "Freescale SNVS RTC support"
+ depends on HAS_IOMEM
+ depends on OF
+ help
+ If you say yes here you get support for the Freescale SNVS
+ Low Power (LP) RTC module.
+
+ This driver can also be built as a module, if so, the module
+ will be called "rtc-snvs".
+
endif # RTC_CLASS
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 2973921..0e3ff92 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -95,6 +95,7 @@ obj-$(CONFIG_RTC_DRV_S35390A) += rtc-s35390a.o
obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o
obj-$(CONFIG_RTC_DRV_SA1100) += rtc-sa1100.o
obj-$(CONFIG_RTC_DRV_SH) += rtc-sh.o
+obj-$(CONFIG_RTC_DRV_SNVS) += rtc-snvs.o
obj-$(CONFIG_RTC_DRV_SPEAR) += rtc-spear.o
obj-$(CONFIG_RTC_DRV_STARFIRE) += rtc-starfire.o
obj-$(CONFIG_RTC_DRV_STK17TA8) += rtc-stk17ta8.o
diff --git a/drivers/rtc/rtc-snvs.c b/drivers/rtc/rtc-snvs.c
new file mode 100644
index 0000000..912f116
--- /dev/null
+++ b/drivers/rtc/rtc-snvs.c
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) 2011-2012 Freescale Semiconductor, Inc.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+
+/* These register offsets are relative to LP (Low Power) range */
+#define SNVS_LPCR 0x04
+#define SNVS_LPSR 0x18
+#define SNVS_LPSRTCMR 0x1c
+#define SNVS_LPSRTCLR 0x20
+#define SNVS_LPTAR 0x24
+#define SNVS_LPPGDR 0x30
+
+#define SNVS_LPCR_SRTC_ENV (1 << 0)
+#define SNVS_LPCR_LPTA_EN (1 << 1)
+#define SNVS_LPCR_LPWUI_EN (1 << 3)
+#define SNVS_LPSR_LPTA (1 << 0)
+
+#define SNVS_LPPGDR_INIT 0x41736166
+#define CNTR_TO_SECS_SH 15
+
+struct snvs_rtc_data {
+ struct rtc_device *rtc;
+ void __iomem *ioaddr;
+ int irq;
+ spinlock_t lock;
+};
+
+static u32 rtc_read_lp_counter(void __iomem *ioaddr)
+{
+ u64 read1, read2;
+
+ do {
+ read1 = readl(ioaddr + SNVS_LPSRTCMR);
+ read1 <<= 32;
+ read1 |= readl(ioaddr + SNVS_LPSRTCLR);
+
+ read2 = readl(ioaddr + SNVS_LPSRTCMR);
+ read2 <<= 32;
+ read2 |= readl(ioaddr + SNVS_LPSRTCLR);
+ } while (read1 != read2);
+
+ /* Convert 47-bit counter to 32-bit raw second count */
+ return (u32) (read1 >> CNTR_TO_SECS_SH);
+}
+
+static void rtc_write_sync_lp(void __iomem *ioaddr)
+{
+ u32 count1, count2, count3;
+ int i;
+
+ /* Wait for 3 CKIL cycles */
+ for (i = 0; i < 3; i++) {
+ do {
+ count1 = readl(ioaddr + SNVS_LPSRTCLR);
+ count2 = readl(ioaddr + SNVS_LPSRTCLR);
+ } while (count1 != count2);
+
+ /* Now wait until counter value changes */
+ do {
+ do {
+ count2 = readl(ioaddr + SNVS_LPSRTCLR);
+ count3 = readl(ioaddr + SNVS_LPSRTCLR);
+ } while (count2 != count3);
+ } while (count3 == count1);
+ }
+}
+
+static int snvs_rtc_enable(struct snvs_rtc_data *data, bool enable)
+{
+ unsigned long timeout = jiffies + msecs_to_jiffies(1);
+ unsigned long flags;
+ u32 lpcr;
+
+ spin_lock_irqsave(&data->lock, flags);
+
+ lpcr = readl(data->ioaddr + SNVS_LPCR);
+ if (enable)
+ lpcr |= SNVS_LPCR_SRTC_ENV;
+ else
+ lpcr &= ~SNVS_LPCR_SRTC_ENV;
+ writel(lpcr, data->ioaddr + SNVS_LPCR);
+
+ spin_unlock_irqrestore(&data->lock, flags);
+
+ while (1) {
+ lpcr = readl(data->ioaddr + SNVS_LPCR);
+
+ if (enable) {
+ if (lpcr & SNVS_LPCR_SRTC_ENV)
+ break;
+ } else {
+ if (!(lpcr & SNVS_LPCR_SRTC_ENV))
+ break;
+ }
+
+ if (time_after(jiffies, timeout))
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int snvs_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct snvs_rtc_data *data = dev_get_drvdata(dev);
+ unsigned long time = rtc_read_lp_counter(data->ioaddr);
+
+ rtc_time_to_tm(time, tm);
+
+ return 0;
+}
+
+static int snvs_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct snvs_rtc_data *data = dev_get_drvdata(dev);
+ unsigned long time;
+
+ rtc_tm_to_time(tm, &time);
+
+ /* Disable RTC first */
+ snvs_rtc_enable(data, false);
+
+ /* Write 32-bit time to 47-bit timer, leaving 15 LSBs blank */
+ writel(time << CNTR_TO_SECS_SH, data->ioaddr + SNVS_LPSRTCLR);
+ writel(time >> (32 - CNTR_TO_SECS_SH), data->ioaddr + SNVS_LPSRTCMR);
+
+ /* Enable RTC again */
+ snvs_rtc_enable(data, true);
+
+ return 0;
+}
+
+static int snvs_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct snvs_rtc_data *data = dev_get_drvdata(dev);
+ u32 lptar, lpsr;
+
+ lptar = readl(data->ioaddr + SNVS_LPTAR);
+ rtc_time_to_tm(lptar, &alrm->time);
+
+ lpsr = readl(data->ioaddr + SNVS_LPSR);
+ alrm->pending = (lpsr & SNVS_LPSR_LPTA) ? 1 : 0;
+
+ return 0;
+}
+
+static int snvs_rtc_alarm_irq_enable(struct device *dev, unsigned int enable)
+{
+ struct snvs_rtc_data *data = dev_get_drvdata(dev);
+ u32 lpcr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&data->lock, flags);
+
+ lpcr = readl(data->ioaddr + SNVS_LPCR);
+ if (enable)
+ lpcr |= (SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN);
+ else
+ lpcr &= ~(SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN);
+ writel(lpcr, data->ioaddr + SNVS_LPCR);
+
+ spin_unlock_irqrestore(&data->lock, flags);
+
+ rtc_write_sync_lp(data->ioaddr);
+
+ return 0;
+}
+
+static int snvs_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct snvs_rtc_data *data = dev_get_drvdata(dev);
+ struct rtc_time *alrm_tm = &alrm->time;
+ unsigned long time;
+ unsigned long flags;
+ u32 lpcr;
+
+ rtc_tm_to_time(alrm_tm, &time);
+
+ spin_lock_irqsave(&data->lock, flags);
+
+ /* Have to clear LPTA_EN before programming new alarm time in LPTAR */
+ lpcr = readl(data->ioaddr + SNVS_LPCR);
+ lpcr &= ~SNVS_LPCR_LPTA_EN;
+ writel(lpcr, data->ioaddr + SNVS_LPCR);
+
+ spin_unlock_irqrestore(&data->lock, flags);
+
+ writel(time, data->ioaddr + SNVS_LPTAR);
+
+ /* Clear alarm interrupt status bit */
+ writel(SNVS_LPSR_LPTA, data->ioaddr + SNVS_LPSR);
+
+ return snvs_rtc_alarm_irq_enable(dev, alrm->enabled);
+}
+
+static const struct rtc_class_ops snvs_rtc_ops = {
+ .read_time = snvs_rtc_read_time,
+ .set_time = snvs_rtc_set_time,
+ .read_alarm = snvs_rtc_read_alarm,
+ .set_alarm = snvs_rtc_set_alarm,
+ .alarm_irq_enable = snvs_rtc_alarm_irq_enable,
+};
+
+static irqreturn_t snvs_rtc_irq_handler(int irq, void *dev_id)
+{
+ struct device *dev = dev_id;
+ struct snvs_rtc_data *data = dev_get_drvdata(dev);
+ u32 lpsr;
+ u32 events = 0;
+
+ lpsr = readl(data->ioaddr + SNVS_LPSR);
+
+ if (lpsr & SNVS_LPSR_LPTA) {
+ events |= (RTC_AF | RTC_IRQF);
+
+ /* RTC alarm should be one-shot */
+ snvs_rtc_alarm_irq_enable(dev, 0);
+
+ rtc_update_irq(data->rtc, 1, events);
+ }
+
+ /* clear interrupt status */
+ writel(lpsr, data->ioaddr + SNVS_LPSR);
+
+ return events ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static int __devinit snvs_rtc_probe(struct platform_device *pdev)
+{
+ struct snvs_rtc_data *data;
+ struct resource *res;
+ int ret;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ data->ioaddr = devm_request_and_ioremap(&pdev->dev, res);
+ if (!data->ioaddr)
+ return -EADDRNOTAVAIL;
+
+ data->irq = platform_get_irq(pdev, 0);
+ if (data->irq < 0)
+ return data->irq;
+
+ platform_set_drvdata(pdev, data);
+
+ spin_lock_init(&data->lock);
+
+ /* Initialize glitch detect */
+ writel(SNVS_LPPGDR_INIT, data->ioaddr + SNVS_LPPGDR);
+
+ /* Clear interrupt status */
+ writel(0xffffffff, data->ioaddr + SNVS_LPSR);
+
+ /* Enable RTC */
+ snvs_rtc_enable(data, true);
+
+ device_init_wakeup(&pdev->dev, true);
+
+ ret = devm_request_irq(&pdev->dev, data->irq, snvs_rtc_irq_handler,
+ IRQF_SHARED, "rtc alarm", &pdev->dev);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to request irq %d: %d\n",
+ data->irq, ret);
+ return ret;
+ }
+
+ data->rtc = rtc_device_register(pdev->name, &pdev->dev,
+ &snvs_rtc_ops, THIS_MODULE);
+ if (IS_ERR(data->rtc)) {
+ ret = PTR_ERR(data->rtc);
+ dev_err(&pdev->dev, "failed to register rtc: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int __devexit snvs_rtc_remove(struct platform_device *pdev)
+{
+ struct snvs_rtc_data *data = platform_get_drvdata(pdev);
+
+ rtc_device_unregister(data->rtc);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int snvs_rtc_suspend(struct device *dev)
+{
+ struct snvs_rtc_data *data = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(data->irq);
+
+ return 0;
+}
+
+static int snvs_rtc_resume(struct device *dev)
+{
+ struct snvs_rtc_data *data = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(data->irq);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(snvs_rtc_pm_ops, snvs_rtc_suspend, snvs_rtc_resume);
+
+static const struct of_device_id __devinitconst snvs_dt_ids[] = {
+ { .compatible = "fsl,sec-v4.0-mon-rtc-lp", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, snvs_dt_ids);
+
+static struct platform_driver snvs_rtc_driver = {
+ .driver = {
+ .name = "snvs_rtc",
+ .owner = THIS_MODULE,
+ .pm = &snvs_rtc_pm_ops,
+ .of_match_table = snvs_dt_ids,
+ },
+ .probe = snvs_rtc_probe,
+ .remove = __devexit_p(snvs_rtc_remove),
+};
+module_platform_driver(snvs_rtc_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Freescale SNVS RTC Driver");
+MODULE_LICENSE("GPL");
--
1.7.5.4
^ permalink raw reply related [flat|nested] 11+ messages in thread
end of thread, other threads:[~2012-08-18 7:03 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-07-13 7:31 [PATCH v3] rtc: snvs: add Freescale rtc-snvs driver Shawn Guo
2012-07-13 8:22 ` Shawn Guo
2012-07-30 4:18 ` Shawn Guo
2012-08-15 0:03 ` [rtc-linux] " Andrew Morton
2012-08-15 14:16 ` Shawn Guo
2012-08-15 20:32 ` Andrew Morton
2012-08-16 8:08 ` Lothar Waßmann
2012-08-17 6:14 ` Shawn Guo
2012-08-18 6:43 ` Lothar Waßmann
2012-08-18 7:03 ` Shawn Guo
-- strict thread matches above, loose matches on Subject: below --
2012-07-11 8:44 Shawn Guo
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.