All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/3] RTC driver for i.MX25
@ 2010-01-27 13:00 Baruch Siach
  2010-01-27 13:00 ` [PATCH 1/3] rtc: driver for the DryIce block found in i.MX25 chips Baruch Siach
                   ` (3 more replies)
  0 siblings, 4 replies; 20+ messages in thread
From: Baruch Siach @ 2010-01-27 13:00 UTC (permalink / raw)
  To: linux-arm-kernel

These patches add support for the RTC of the i.MX25 chip, which is based on the 
DryIce block. The driver is based on code from the Freescale i.MX BSP.

Also included platform code for i.MX25 chip, and the i.MX25 PDK board.

These patches have been tested on the i.MX25 PDK board.

Baruch Siach (3):
  rtc: driver for the DryIce block found in i.MX25 chips
  mx25: add support for the DryIce rtc
  mx25pdk: platform code for the DryIce RTC module

 arch/arm/mach-mx25/clock.c            |    2 +
 arch/arm/mach-mx25/devices.c          |   19 ++
 arch/arm/mach-mx25/devices.h          |    1 +
 arch/arm/mach-mx25/mx25pdk.c          |    1 +
 arch/arm/plat-mxc/include/mach/mx25.h |    2 +
 drivers/rtc/Kconfig                   |   10 +
 drivers/rtc/Makefile                  |    1 +
 drivers/rtc/rtc-imxdi.c               |  559 +++++++++++++++++++++++++++++++++
 8 files changed, 595 insertions(+), 0 deletions(-)
 create mode 100644 drivers/rtc/rtc-imxdi.c

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH 1/3] rtc: driver for the DryIce block found in i.MX25 chips
  2010-01-27 13:00 [PATCH 0/3] RTC driver for i.MX25 Baruch Siach
@ 2010-01-27 13:00 ` Baruch Siach
  2010-01-27 13:30   ` Lothar Waßmann
  2010-01-29 10:10   ` [rtc-linux] " Alessandro Zummo
  2010-01-27 13:00 ` [PATCH 2/3] mx25: add support for the DryIce rtc Baruch Siach
                   ` (2 subsequent siblings)
  3 siblings, 2 replies; 20+ messages in thread
From: Baruch Siach @ 2010-01-27 13:00 UTC (permalink / raw)
  To: linux-arm-kernel

This driver is based on code from Freescale which accompanies their i.MX25 PDK
board, with some cleanup.

Signed-off-by: Baruch Siach <baruch@tkos.co.il>
---
 drivers/rtc/Kconfig     |   10 +
 drivers/rtc/Makefile    |    1 +
 drivers/rtc/rtc-imxdi.c |  559 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 570 insertions(+), 0 deletions(-)
 create mode 100644 drivers/rtc/rtc-imxdi.c

diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 8167e9e..83bd602 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -527,6 +527,16 @@ config RTC_DRV_MSM6242
 	  This driver can also be built as a module. If so, the module
 	  will be called rtc-msm6242.
 
+config RTC_DRV_IMXDI
+	tristate "Freescale IMX DryIce Real Time Clock"
+	depends on ARCH_MX25
+	depends on RTC_CLASS
+	help
+	   Support for Freescale IMX DryIce RTC
+
+	   This driver can also be built as a module, if so, the module
+	   will be called "rtc-imxdi".
+
 config RTC_MXC
 	tristate "Freescale MXC Real Time Clock"
 	depends on ARCH_MXC
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index e5160fd..f2d3420 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -44,6 +44,7 @@ obj-$(CONFIG_RTC_DRV_EFI)	+= rtc-efi.o
 obj-$(CONFIG_RTC_DRV_EP93XX)	+= rtc-ep93xx.o
 obj-$(CONFIG_RTC_DRV_FM3130)	+= rtc-fm3130.o
 obj-$(CONFIG_RTC_DRV_GENERIC)	+= rtc-generic.o
+obj-$(CONFIG_RTC_DRV_IMXDI)	+= rtc-imxdi.o
 obj-$(CONFIG_RTC_DRV_ISL1208)	+= rtc-isl1208.o
 obj-$(CONFIG_RTC_DRV_M41T80)	+= rtc-m41t80.o
 obj-$(CONFIG_RTC_DRV_M41T94)	+= rtc-m41t94.o
diff --git a/drivers/rtc/rtc-imxdi.c b/drivers/rtc/rtc-imxdi.c
new file mode 100644
index 0000000..12a6df5
--- /dev/null
+++ b/drivers/rtc/rtc-imxdi.c
@@ -0,0 +1,559 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2010 Orex Computed Radiography
+ */
+
+/*
+ * 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
+ */
+
+/* based on rtc-mc13892.c */
+
+/*
+ * This driver uses the 47-bit 32 kHz counter in the Freescale DryIce block
+ * to implement a Linux RTC. Times and alarms are truncated to seconds.
+ * Since the RTC framework performs API locking via rtc->ops_lock the
+ * only simultaneous accesses we need to deal with is updating DryIce
+ * registers while servicing an alarm.
+ *
+ * Note that reading the DSR (DryIce Status Register) automatically clears
+ * the WCF (Write Complete Flag). All DryIce writes are synchronized to the
+ * LP (Low Power) domain and set the WCF upon completion. Writes to the
+ * DIER (DryIce Interrupt Enable Register) are the only exception. These
+ * occur at normal bus speeds and do not set WCF.  Periodic interrupts are
+ * not supported by the hardware.
+ */
+
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/workqueue.h>
+
+/* DryIce Register Definitions */
+
+#define DTCMR     0x00           /* Time Counter MSB Reg */
+#define DTCLR     0x04           /* Time Counter LSB Reg */
+
+#define DCAMR     0x08           /* Clock Alarm MSB Reg */
+#define DCALR     0x0c           /* Clock Alarm LSB Reg */
+#define DCAMR_UNSET  0xFFFFFFFF  /* doomsday - 1 sec */
+
+#define DCR       0x10           /* Control Reg */
+#define DCR_TCE   (1 << 3)       /* Time Counter Enable */
+
+#define DSR       0x14           /* Status Reg */
+#define DSR_WBF   (1 << 10)      /* Write Busy Flag */
+#define DSR_WNF   (1 << 9)       /* Write Next Flag */
+#define DSR_WCF   (1 << 8)       /* Write Complete Flag */
+#define DSR_WEF   (1 << 7)       /* Write Error Flag */
+#define DSR_CAF   (1 << 4)       /* Clock Alarm Flag */
+#define DSR_NVF   (1 << 1)       /* Non-Valid Flag */
+#define DSR_SVF   (1 << 0)       /* Security Violation Flag */
+
+#define DIER      0x18           /* Interrupt Enable Reg */
+#define DIER_WNIE (1 << 9)       /* Write Next Interrupt Enable */
+#define DIER_WCIE (1 << 8)       /* Write Complete Interrupt Enable */
+#define DIER_WEIE (1 << 7)       /* Write Error Interrupt Enable */
+#define DIER_CAIE (1 << 4)       /* Clock Alarm Interrupt Enable */
+
+/**
+ * struct imxdi_dev - private imxdi rtc data
+ * @pdev: pionter to platform dev
+ * @rtc: pointer to rtc struct
+ * @ioaddr: IO registers pointer
+ * @irq: dryice normal interrupt
+ * @clk: input reference clock
+ * @dsr: copy of the DSR register
+ * @irq_lock: interrupt enable register (DIER) lock
+ * @write_wait: registers write complete queue
+ * @write_mutex: serialize registers write
+ * @work: schedule alarm work
+ */
+struct imxdi_dev {
+	struct platform_device *pdev;
+	struct rtc_device *rtc;
+	void __iomem *ioaddr;
+	int irq;
+	struct clk *clk;
+	u32 dsr;
+	spinlock_t irq_lock;
+	wait_queue_head_t write_wait;
+	struct mutex write_mutex;
+	struct work_struct work;
+};
+
+/*
+ * enable a dryice interrupt
+ */
+static inline void di_int_enable(struct imxdi_dev *imxdi, u32 intr)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&imxdi->irq_lock, flags);
+	__raw_writel(__raw_readl(imxdi->ioaddr+DIER) | intr,
+			imxdi->ioaddr+DIER);
+	spin_unlock_irqrestore(&imxdi->irq_lock, flags);
+}
+
+/*
+ * disable a dryice interrupt
+ */
+static inline void di_int_disable(struct imxdi_dev *imxdi, u32 intr)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&imxdi->irq_lock, flags);
+	__raw_writel(__raw_readl(imxdi->ioaddr+DIER) & ~intr,
+			imxdi->ioaddr+DIER);
+	spin_unlock_irqrestore(&imxdi->irq_lock, flags);
+}
+
+/*
+ * This function attempts to clear the dryice write-error flag.
+ *
+ * A dryice write error is similar to a bus fault and should not occur in
+ * normal operation.  Clearing the flag requires another write, so the root
+ * cause of the problem may need to be fixed before the flag can be cleared.
+ */
+static void clear_write_error(struct imxdi_dev *imxdi)
+{
+	int cnt;
+
+	dev_warn(&imxdi->pdev->dev, "WARNING: Register write error!\n");
+
+	for (;;) {
+		/* clear the write error flag */
+		__raw_writel(DSR_WEF, imxdi->ioaddr+DSR);
+
+		/* wait for it to take effect */
+		for (cnt = 0; cnt < 100; cnt++) {
+			if ((__raw_readl(imxdi->ioaddr+DSR) & DSR_WEF) == 0)
+				return;
+			udelay(10);
+		}
+		dev_err(&imxdi->pdev->dev,
+			"ERROR: Cannot clear write-error flag!\n");
+	}
+}
+
+/*
+ * Write a dryice register and wait until it completes.
+ *
+ * This function uses interrupts to determine when the
+ * write has completed.
+ */
+static int di_write_wait(struct imxdi_dev *imxdi, u32 val, int reg)
+{
+	int ret;
+	int rc = 0;
+
+	/* serialize register writes */
+	mutex_lock(&imxdi->write_mutex);
+
+	/* enable the write-complete interrupt */
+	di_int_enable(imxdi, DIER_WCIE);
+
+	imxdi->dsr = 0;
+
+	/* do the register write */
+	__raw_writel(val, imxdi->ioaddr+reg);
+
+	/* wait for the write to finish */
+	ret = wait_event_interruptible_timeout(imxdi->write_wait,
+			imxdi->dsr & (DSR_WCF | DSR_WEF), 1 * HZ);
+	if (ret == 0)
+		dev_warn(&imxdi->pdev->dev,
+				"Write-wait timeout "
+				"val = 0x%08x reg = 0x%08x\n", val, reg);
+
+	/* check for write error */
+	if (imxdi->dsr & DSR_WEF) {
+		clear_write_error(imxdi);
+		rc = -EIO;
+	}
+	mutex_unlock(&imxdi->write_mutex);
+	return rc;
+}
+
+/*
+ * rtc device ioctl
+ *
+ * The rtc framework handles the basic rtc ioctls on behalf
+ * of the driver by calling the functions registered in the
+ * rtc_ops structure.
+ */
+static int dryice_rtc_ioctl(struct device *dev, unsigned int cmd,
+			    unsigned long arg)
+{
+	struct imxdi_dev *imxdi = dev_get_drvdata(dev);
+
+	dev_dbg(dev, "%s(0x%x)\n", __func__, cmd);
+	switch (cmd) {
+	case RTC_AIE_OFF:  /* alarm disable */
+		di_int_disable(imxdi, DIER_CAIE);
+		return 0;
+
+	case RTC_AIE_ON:  /* alarm enable */
+		di_int_enable(imxdi, DIER_CAIE);
+		return 0;
+	}
+	return -ENOIOCTLCMD;
+}
+
+/*
+ * read the seconds portion of the current time from the dryice time counter
+ */
+static int dryice_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+	struct imxdi_dev *imxdi = dev_get_drvdata(dev);
+	unsigned long now;
+
+	now = __raw_readl(imxdi->ioaddr+DTCMR);
+	rtc_time_to_tm(now, tm);
+
+	return 0;
+}
+
+/*
+ * set the seconds portion of dryice time counter and clear the
+ * fractional part.
+ */
+static int dryice_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+	struct imxdi_dev *imxdi = dev_get_drvdata(dev);
+	unsigned long now;
+	int rc;
+
+	rc = rtc_tm_to_time(tm, &now);
+	if (rc == 0)
+		/* zero the fractional part first */
+		rc = di_write_wait(imxdi, 0, DTCLR);
+	if (rc == 0)
+		rc = di_write_wait(imxdi, now, DTCMR);
+
+	return rc;
+}
+
+/*
+ * read the seconds portion of the alarm register.
+ * the fractional part of the alarm register is always zero.
+ */
+static int dryice_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+	struct imxdi_dev *imxdi = dev_get_drvdata(dev);
+	u32 dcamr;
+
+	dcamr = __raw_readl(imxdi->ioaddr+DCAMR);
+	rtc_time_to_tm(dcamr, &alarm->time);
+
+	/* alarm is enabled if the interrupt is enabled */
+	alarm->enabled = (__raw_readl(imxdi->ioaddr+DIER) & DIER_CAIE) != 0;
+
+	/* don't allow the DSR read to mess up DSR_WCF */
+	mutex_lock(&imxdi->write_mutex);
+
+	/* alarm is pending if the alarm flag is set */
+	alarm->pending = (__raw_readl(imxdi->ioaddr+DSR) & DSR_CAF) != 0;
+
+	mutex_unlock(&imxdi->write_mutex);
+
+	return 0;
+}
+
+/*
+ * set the seconds portion of dryice alarm register
+ */
+static int dryice_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+	struct imxdi_dev *imxdi = dev_get_drvdata(dev);
+	unsigned long now;
+	unsigned long alarm_time;
+	int rc;
+
+	rc = rtc_tm_to_time(&alarm->time, &alarm_time);
+	if (rc)
+		return rc;
+
+	/* don't allow setting alarm in the past */
+	now = __raw_readl(imxdi->ioaddr+DTCMR);
+	if (alarm_time < now)
+		return -EINVAL;
+
+	/* write the new alarm time */
+	rc = di_write_wait(imxdi, (u32)alarm_time, DCAMR);
+	if (rc)
+		return rc;
+
+	if (alarm->enabled)
+		di_int_enable(imxdi, DIER_CAIE);  /* enable alarm intr */
+	else
+		di_int_disable(imxdi, DIER_CAIE); /* disable alarm intr */
+
+	return 0;
+}
+
+static struct rtc_class_ops dryice_rtc_ops = {
+	.ioctl = dryice_rtc_ioctl,
+	.read_time = dryice_rtc_read_time,
+	.set_time = dryice_rtc_set_time,
+	.read_alarm = dryice_rtc_read_alarm,
+	.set_alarm = dryice_rtc_set_alarm,
+};
+
+/*
+ * dryice "normal" interrupt handler
+ */
+static irqreturn_t dryice_norm_irq(int irq, void *dev_id)
+{
+	struct imxdi_dev *imxdi = dev_id;
+	u32 dsr, dier;
+	irqreturn_t rc = IRQ_NONE;
+
+	dier = __raw_readl(imxdi->ioaddr+DIER);
+
+	/* handle write complete and write error cases */
+	if ((dier & DIER_WCIE)) {
+		/*If the write wait queue is empty then there is no pending
+		  operations. It means the interrupt is for DryIce -Security.
+		  IRQ must be returned as none.*/
+		if (list_empty_careful(&imxdi->write_wait.task_list))
+			return rc;
+
+		/* DSR_WCF clears itself on DSR read */
+		dsr = __raw_readl(imxdi->ioaddr+DSR);
+		if ((dsr & (DSR_WCF | DSR_WEF))) {
+			/* mask the interrupt */
+			di_int_disable(imxdi, DIER_WCIE);
+
+			/* save the dsr value for the wait queue */
+			imxdi->dsr |= dsr;
+
+			wake_up_interruptible(&imxdi->write_wait);
+			rc = IRQ_HANDLED;
+		}
+	}
+
+	/* handle the alarm case */
+	if ((dier & DIER_CAIE)) {
+		/* DSR_WCF clears itself on DSR read */
+		dsr = __raw_readl(imxdi->ioaddr+DSR);
+		if (dsr & DSR_CAF) {
+			/* mask the interrupt */
+			di_int_disable(imxdi, DIER_CAIE);
+
+			/* finish alarm in user context */
+			schedule_work(&imxdi->work);
+			rc = IRQ_HANDLED;
+		}
+	}
+	return rc;
+}
+
+/*
+ * post the alarm event from user context so it can sleep
+ * on the write completion.
+ */
+static void dryice_work(struct work_struct *work)
+{
+	struct imxdi_dev *imxdi = container_of(work,
+			struct imxdi_dev, work);
+
+	/* dismiss the interrupt (ignore error) */
+	di_write_wait(imxdi, DSR_CAF, DSR);
+
+	/*
+	 * pass the alarm event to the rtc framework. note that
+	 * rtc_update_irq expects to be called with interrupts off.
+	 */
+	local_irq_disable();
+	rtc_update_irq(imxdi->rtc, 1, RTC_AF | RTC_IRQF);
+	local_irq_enable();
+}
+
+/*
+ * probe for dryice rtc device
+ */
+static int dryice_rtc_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	struct imxdi_dev *imxdi = NULL;
+	int rc = 0;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENODEV;
+
+	imxdi = kzalloc(sizeof(*imxdi), GFP_KERNEL);
+	if (!imxdi)
+		return -ENOMEM;
+
+	imxdi->pdev = pdev;
+	imxdi->irq = -1;
+
+	if (!request_mem_region(res->start, resource_size(res),
+				pdev->name)) {
+		rc = -EBUSY;
+		goto err_free_mem;
+	}
+	imxdi->ioaddr = ioremap(res->start, resource_size(res));
+	if (imxdi->ioaddr == NULL) {
+		rc = -ENOMEM;
+		goto err_release_region;
+	}
+	imxdi->irq = platform_get_irq(pdev, 0);
+
+	init_waitqueue_head(&imxdi->write_wait);
+
+	INIT_WORK(&imxdi->work, dryice_work);
+
+	mutex_init(&imxdi->write_mutex);
+
+	imxdi->clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(imxdi->clk)) {
+		rc = -ENODEV;
+		goto err;
+	}
+	clk_enable(imxdi->clk);
+
+	if (imxdi->irq >= 0) {
+		if (request_irq(imxdi->irq, dryice_norm_irq, IRQF_SHARED,
+				pdev->name, imxdi) < 0) {
+			dev_warn(&pdev->dev, "interrupt not available.\n");
+			imxdi->irq = -1;
+			goto err;
+		}
+	}
+
+	/*
+	 * Initialize dryice hardware
+	 */
+
+	/* put dryice into valid state */
+	if (__raw_readl(imxdi->ioaddr+DSR) & DSR_NVF)
+		rc = di_write_wait(imxdi, DSR_NVF | DSR_SVF, DSR);
+	if (rc)
+		goto err;
+
+	/* mask alarm interrupt */
+	di_int_disable(imxdi, DIER_CAIE);
+
+	/* initialize alarm */
+	rc = di_write_wait(imxdi, DCAMR_UNSET, DCAMR);
+	if (rc == 0)
+		rc = di_write_wait(imxdi, 0, DCALR);
+	if (rc)
+		goto err;
+
+	/* clear alarm flag */
+	if (__raw_readl(imxdi->ioaddr+DSR) & DSR_CAF)
+		rc = di_write_wait(imxdi, DSR_CAF, DSR);
+	if (rc)
+		goto err;
+
+	/* the timer won't count if it has never been written to */
+	if (__raw_readl(imxdi->ioaddr+DTCMR) == 0)
+		rc = di_write_wait(imxdi, 0, DTCMR);
+	if (rc)
+		goto err;
+
+	/* start keeping time */
+	if (!(__raw_readl(imxdi->ioaddr+DCR) & DCR_TCE))
+		rc = di_write_wait(imxdi,
+				__raw_readl(imxdi->ioaddr+DCR) | DCR_TCE, DCR);
+	if (rc)
+		goto err;
+
+	imxdi->rtc = rtc_device_register(pdev->name, &pdev->dev,
+				  &dryice_rtc_ops, THIS_MODULE);
+	if (IS_ERR(imxdi->rtc)) {
+		rc = PTR_ERR(imxdi->rtc);
+		goto err;
+	}
+	platform_set_drvdata(pdev, imxdi);
+
+	return 0;
+err:
+	if (imxdi->rtc && !IS_ERR(imxdi->rtc))
+		rtc_device_unregister(imxdi->rtc);
+
+	if (imxdi->irq >= 0)
+		free_irq(imxdi->irq, imxdi);
+
+	if (imxdi->clk && !IS_ERR(imxdi->clk)) {
+		clk_disable(imxdi->clk);
+		clk_put(imxdi->clk);
+	}
+
+	if (imxdi->ioaddr)
+		iounmap(imxdi->ioaddr);
+
+err_release_region:
+	release_mem_region(res->start, resource_size(res));
+
+err_free_mem:
+	kfree(imxdi);
+
+	return rc;
+}
+
+static int __exit dryice_rtc_remove(struct platform_device *pdev)
+{
+	struct imxdi_dev *imxdi = platform_get_drvdata(pdev);
+	struct resource *mem;
+
+	flush_scheduled_work();
+
+	rtc_device_unregister(imxdi->rtc);
+
+	/* mask alarm interrupt */
+	di_int_disable(imxdi, DIER_CAIE);
+
+	if (imxdi->irq >= 0)
+		free_irq(imxdi->irq, imxdi);
+
+	clk_disable(imxdi->clk);
+	clk_put(imxdi->clk);
+
+	iounmap(imxdi->ioaddr);
+
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	release_mem_region(mem->start, resource_size(mem));
+
+	kfree(imxdi);
+
+	return 0;
+}
+
+static struct platform_driver dryice_rtc_driver = {
+	.driver = {
+		   .name = "imxdi_rtc",
+		   .owner = THIS_MODULE,
+		   },
+	.probe = dryice_rtc_probe,
+	.remove = __exit_p(dryice_rtc_remove),
+};
+
+static int __init dryice_rtc_init(void)
+{
+	return platform_driver_register(&dryice_rtc_driver);
+}
+
+static void __exit dryice_rtc_exit(void)
+{
+	platform_driver_unregister(&dryice_rtc_driver);
+}
+
+module_init(dryice_rtc_init);
+module_exit(dryice_rtc_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("IMXDI Realtime Clock Driver (RTC)");
+MODULE_LICENSE("GPL");
-- 
1.6.5

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 2/3] mx25: add support for the DryIce rtc
  2010-01-27 13:00 [PATCH 0/3] RTC driver for i.MX25 Baruch Siach
  2010-01-27 13:00 ` [PATCH 1/3] rtc: driver for the DryIce block found in i.MX25 chips Baruch Siach
@ 2010-01-27 13:00 ` Baruch Siach
  2010-01-27 13:00 ` [PATCH 3/3] mx25pdk: platform code for the DryIce RTC module Baruch Siach
  2010-01-29  9:45 ` [PATCH 0/3] RTC driver for i.MX25 Sascha Hauer
  3 siblings, 0 replies; 20+ messages in thread
From: Baruch Siach @ 2010-01-27 13:00 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Baruch Siach <baruch@tkos.co.il>
---
 arch/arm/mach-mx25/clock.c            |    2 ++
 arch/arm/mach-mx25/devices.c          |   19 +++++++++++++++++++
 arch/arm/mach-mx25/devices.h          |    1 +
 arch/arm/plat-mxc/include/mach/mx25.h |    2 ++
 4 files changed, 24 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-mx25/clock.c b/arch/arm/mach-mx25/clock.c
index 6acc88b..744b52a 100644
--- a/arch/arm/mach-mx25/clock.c
+++ b/arch/arm/mach-mx25/clock.c
@@ -182,6 +182,7 @@ DEFINE_CLOCK(kpp_clk,	 0, CCM_CGCR1, 28, get_rate_ipg, NULL, NULL);
 DEFINE_CLOCK(tsc_clk,	 0, CCM_CGCR2, 13, get_rate_ipg, NULL, NULL);
 DEFINE_CLOCK(i2c_clk,	 0, CCM_CGCR0,  6, get_rate_i2c, NULL, NULL);
 DEFINE_CLOCK(fec_clk,	 0, CCM_CGCR1, 15, get_rate_ipg, NULL, &fec_ahb_clk);
+DEFINE_CLOCK(dryice_clk, 0, CCM_CGCR1,  8, get_rate_ipg, NULL, NULL);
 
 #define _REGISTER_CLOCK(d, n, c)	\
 	{				\
@@ -214,6 +215,7 @@ static struct clk_lookup lookups[] = {
 	_REGISTER_CLOCK("imx-i2c.1", NULL, i2c_clk)
 	_REGISTER_CLOCK("imx-i2c.2", NULL, i2c_clk)
 	_REGISTER_CLOCK("fec.0", NULL, fec_clk)
+	_REGISTER_CLOCK("imxdi_rtc.0", NULL, dryice_clk)
 };
 
 int __init mx25_clocks_init(void)
diff --git a/arch/arm/mach-mx25/devices.c b/arch/arm/mach-mx25/devices.c
index dd40697..b0b75fc 100644
--- a/arch/arm/mach-mx25/devices.c
+++ b/arch/arm/mach-mx25/devices.c
@@ -458,3 +458,22 @@ struct platform_device mxc_nand_device = {
 	.num_resources	= ARRAY_SIZE(mxc_nand_resources),
 	.resource	= mxc_nand_resources,
 };
+
+static struct resource mx25_rtc_resources[] = {
+	{
+		.start	= MX25_DRYICE_BASE_ADDR,
+		.end	= MX25_DRYICE_BASE_ADDR + 0x40,
+		.flags	= IORESOURCE_MEM,
+	},
+	{
+		.start	= MX25_INT_DRYICE,
+		.flags	= IORESOURCE_IRQ
+	},
+};
+
+struct platform_device mx25_rtc_device = {
+	.name	= "imxdi_rtc",
+	.id	= 0,
+	.num_resources	= ARRAY_SIZE(mx25_rtc_resources),
+	.resource	= mx25_rtc_resources,
+};
diff --git a/arch/arm/mach-mx25/devices.h b/arch/arm/mach-mx25/devices.h
index 8f55300..37e6a08 100644
--- a/arch/arm/mach-mx25/devices.h
+++ b/arch/arm/mach-mx25/devices.h
@@ -19,3 +19,4 @@ extern struct platform_device mxc_i2c_device1;
 extern struct platform_device mxc_i2c_device2;
 extern struct platform_device mx25_fec_device;
 extern struct platform_device mxc_nand_device;
+extern struct platform_device mx25_rtc_device;
diff --git a/arch/arm/plat-mxc/include/mach/mx25.h b/arch/arm/plat-mxc/include/mach/mx25.h
index 58e0bd3..215c4d8 100644
--- a/arch/arm/plat-mxc/include/mach/mx25.h
+++ b/arch/arm/plat-mxc/include/mach/mx25.h
@@ -43,7 +43,9 @@
 
 #define MX25_FEC_BASE_ADDR		0x50038000
 #define MX25_NFC_BASE_ADDR		0xbb000000
+#define MX25_DRYICE_BASE_ADDR		0x53ffc000
 
+#define MX25_INT_DRYICE	25
 #define MX25_INT_FEC	57
 #define MX25_INT_NANDFC	33
 
-- 
1.6.5

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 3/3] mx25pdk: platform code for the DryIce RTC module
  2010-01-27 13:00 [PATCH 0/3] RTC driver for i.MX25 Baruch Siach
  2010-01-27 13:00 ` [PATCH 1/3] rtc: driver for the DryIce block found in i.MX25 chips Baruch Siach
  2010-01-27 13:00 ` [PATCH 2/3] mx25: add support for the DryIce rtc Baruch Siach
@ 2010-01-27 13:00 ` Baruch Siach
  2010-01-29  9:45 ` [PATCH 0/3] RTC driver for i.MX25 Sascha Hauer
  3 siblings, 0 replies; 20+ messages in thread
From: Baruch Siach @ 2010-01-27 13:00 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Baruch Siach <baruch@tkos.co.il>
---
 arch/arm/mach-mx25/mx25pdk.c |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-mx25/mx25pdk.c b/arch/arm/mach-mx25/mx25pdk.c
index 232f9ca..f4e8b98 100644
--- a/arch/arm/mach-mx25/mx25pdk.c
+++ b/arch/arm/mach-mx25/mx25pdk.c
@@ -91,6 +91,7 @@ static void __init mx25pdk_init(void)
 	mxc_register_device(&mxc_uart_device0, &uart_pdata);
 	mxc_register_device(&mxc_usbh2, NULL);
 	mxc_register_device(&mxc_nand_device, &mx25pdk_nand_board_info);
+	mxc_register_device(&mx25_rtc_device, NULL);
 
 	mx25pdk_fec_reset();
 	mxc_register_device(&mx25_fec_device, &mx25_fec_pdata);
-- 
1.6.5

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 1/3] rtc: driver for the DryIce block found in i.MX25 chips
  2010-01-27 13:00 ` [PATCH 1/3] rtc: driver for the DryIce block found in i.MX25 chips Baruch Siach
@ 2010-01-27 13:30   ` Lothar Waßmann
  2010-01-27 21:46     ` Russell King - ARM Linux
  2010-01-29 10:10   ` [rtc-linux] " Alessandro Zummo
  1 sibling, 1 reply; 20+ messages in thread
From: Lothar Waßmann @ 2010-01-27 13:30 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

Baruch Siach writes:
> This driver is based on code from Freescale which accompanies their i.MX25 PDK
> board, with some cleanup.
> 
> Signed-off-by: Baruch Siach <baruch@tkos.co.il>
> ---
>  drivers/rtc/Kconfig     |   10 +
>  drivers/rtc/Makefile    |    1 +
>  drivers/rtc/rtc-imxdi.c |  559 +++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 570 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/rtc/rtc-imxdi.c
> 
> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
> index 8167e9e..83bd602 100644
> --- a/drivers/rtc/Kconfig
> +++ b/drivers/rtc/Kconfig
> @@ -527,6 +527,16 @@ config RTC_DRV_MSM6242
>  	  This driver can also be built as a module. If so, the module
>  	  will be called rtc-msm6242.
>  
> +config RTC_DRV_IMXDI
> +	tristate "Freescale IMX DryIce Real Time Clock"
> +	depends on ARCH_MX25
> +	depends on RTC_CLASS
> +	help
> +	   Support for Freescale IMX DryIce RTC
> +
> +	   This driver can also be built as a module, if so, the module
> +	   will be called "rtc-imxdi".
> +
>  config RTC_MXC
>  	tristate "Freescale MXC Real Time Clock"
>  	depends on ARCH_MXC
> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
> index e5160fd..f2d3420 100644
> --- a/drivers/rtc/Makefile
> +++ b/drivers/rtc/Makefile
> @@ -44,6 +44,7 @@ obj-$(CONFIG_RTC_DRV_EFI)	+= rtc-efi.o
>  obj-$(CONFIG_RTC_DRV_EP93XX)	+= rtc-ep93xx.o
>  obj-$(CONFIG_RTC_DRV_FM3130)	+= rtc-fm3130.o
>  obj-$(CONFIG_RTC_DRV_GENERIC)	+= rtc-generic.o
> +obj-$(CONFIG_RTC_DRV_IMXDI)	+= rtc-imxdi.o
>  obj-$(CONFIG_RTC_DRV_ISL1208)	+= rtc-isl1208.o
>  obj-$(CONFIG_RTC_DRV_M41T80)	+= rtc-m41t80.o
>  obj-$(CONFIG_RTC_DRV_M41T94)	+= rtc-m41t94.o
> diff --git a/drivers/rtc/rtc-imxdi.c b/drivers/rtc/rtc-imxdi.c
> new file mode 100644
> index 0000000..12a6df5
> --- /dev/null
> +++ b/drivers/rtc/rtc-imxdi.c
[...]
> +static int dryice_rtc_probe(struct platform_device *pdev)
> +{
> +	struct resource *res;
> +	struct imxdi_dev *imxdi = NULL;
> +	int rc = 0;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res)
> +		return -ENODEV;
> +
> +	imxdi = kzalloc(sizeof(*imxdi), GFP_KERNEL);
> +	if (!imxdi)
> +		return -ENOMEM;
> +
> +	imxdi->pdev = pdev;
> +	imxdi->irq = -1;
> +
> +	if (!request_mem_region(res->start, resource_size(res),
> +				pdev->name)) {
> +		rc = -EBUSY;
> +		goto err_free_mem;
> +	}
> +	imxdi->ioaddr = ioremap(res->start, resource_size(res));
> +	if (imxdi->ioaddr == NULL) {
> +		rc = -ENOMEM;
> +		goto err_release_region;
> +	}
> +	imxdi->irq = platform_get_irq(pdev, 0);
> +
> +	init_waitqueue_head(&imxdi->write_wait);
> +
> +	INIT_WORK(&imxdi->work, dryice_work);
> +
> +	mutex_init(&imxdi->write_mutex);
> +
> +	imxdi->clk = clk_get(&pdev->dev, NULL);
> +	if (IS_ERR(imxdi->clk)) {
> +		rc = -ENODEV;
>
rc = PTR_ERR(imxdi->clk);


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] 20+ messages in thread

* [PATCH 1/3] rtc: driver for the DryIce block found in i.MX25 chips
  2010-01-27 13:30   ` Lothar Waßmann
@ 2010-01-27 21:46     ` Russell King - ARM Linux
  0 siblings, 0 replies; 20+ messages in thread
From: Russell King - ARM Linux @ 2010-01-27 21:46 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Jan 27, 2010 at 02:30:49PM +0100, Lothar Wa?mann wrote:
> Hi,
> 
> Baruch Siach writes:
> > +	imxdi->clk = clk_get(&pdev->dev, NULL);
> > +	if (IS_ERR(imxdi->clk)) {
> > +		rc = -ENODEV;
> >
> rc = PTR_ERR(imxdi->clk);

Grr.

Why do people find the general principle that "if you have an error
code, propagate it upwards" is soo difficult to deal with?

We seem to have stuff like the above constantly appearing, or the evil
"lets return -1 because we can't be bothered to find an appropriate
error value" sillyness.

Why is this?

A suggestion - try thinking like this: "If I find myself typing
'return -1', immediate stop typing and take the time to find a better
value.  If I find myself typing IS_ERR() make sure I use PTR_ERR to
assign an error code."

Simples.

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH 0/3] RTC driver for i.MX25
  2010-01-27 13:00 [PATCH 0/3] RTC driver for i.MX25 Baruch Siach
                   ` (2 preceding siblings ...)
  2010-01-27 13:00 ` [PATCH 3/3] mx25pdk: platform code for the DryIce RTC module Baruch Siach
@ 2010-01-29  9:45 ` Sascha Hauer
  3 siblings, 0 replies; 20+ messages in thread
From: Sascha Hauer @ 2010-01-29  9:45 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Jan 27, 2010 at 03:00:46PM +0200, Baruch Siach wrote:
> These patches add support for the RTC of the i.MX25 chip, which is based on the 
> DryIce block. The driver is based on code from the Freescale i.MX BSP.
> 
> Also included platform code for i.MX25 chip, and the i.MX25 PDK board.
> 
> These patches have been tested on the i.MX25 PDK board.

I added 2/3 and 3/3 to mxc-master. The rtc guys should take care of the
rest.

Sascha


> 
> Baruch Siach (3):
>   rtc: driver for the DryIce block found in i.MX25 chips
>   mx25: add support for the DryIce rtc
>   mx25pdk: platform code for the DryIce RTC module
> 
>  arch/arm/mach-mx25/clock.c            |    2 +
>  arch/arm/mach-mx25/devices.c          |   19 ++
>  arch/arm/mach-mx25/devices.h          |    1 +
>  arch/arm/mach-mx25/mx25pdk.c          |    1 +
>  arch/arm/plat-mxc/include/mach/mx25.h |    2 +
>  drivers/rtc/Kconfig                   |   10 +
>  drivers/rtc/Makefile                  |    1 +
>  drivers/rtc/rtc-imxdi.c               |  559 +++++++++++++++++++++++++++++++++
>  8 files changed, 595 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/rtc/rtc-imxdi.c
> 
> 

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [rtc-linux] [PATCH 1/3] rtc: driver for the DryIce block found in  i.MX25 chips
  2010-01-27 13:00 ` [PATCH 1/3] rtc: driver for the DryIce block found in i.MX25 chips Baruch Siach
  2010-01-27 13:30   ` Lothar Waßmann
@ 2010-01-29 10:10   ` Alessandro Zummo
  2010-02-01  7:36     ` [PATCH] " Baruch Siach
  1 sibling, 1 reply; 20+ messages in thread
From: Alessandro Zummo @ 2010-01-29 10:10 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, 27 Jan 2010 15:00:47 +0200
Baruch Siach <baruch@tkos.co.il> wrote:

> 
> This driver is based on code from Freescale which accompanies their i.MX25 PDK
> board, with some cleanup.
> 
> Signed-off-by: Baruch Siach <baruch@tkos.co.il>

 Hi Baruch, please find a few comments below and be sure to
 read http://groups.google.com/group/rtc-linux/web/checklist

> ---
>  drivers/rtc/Kconfig     |   10 +
>  drivers/rtc/Makefile    |    1 +
>  drivers/rtc/rtc-imxdi.c |  559 +++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 570 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/rtc/rtc-imxdi.c
> 
> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
> index 8167e9e..83bd602 100644
> --- a/drivers/rtc/Kconfig
> +++ b/drivers/rtc/Kconfig
> @@ -527,6 +527,16 @@ config RTC_DRV_MSM6242
>  	  This driver can also be built as a module. If so, the module
>  	  will be called rtc-msm6242.
>  
> +config RTC_DRV_IMXDI
> +	tristate "Freescale IMX DryIce Real Time Clock"
> +	depends on ARCH_MX25
> +	depends on RTC_CLASS
> +	help
> +	   Support for Freescale IMX DryIce RTC
> +
> +	   This driver can also be built as a module, if so, the module
> +	   will be called "rtc-imxdi".
> +
>  config RTC_MXC
>  	tristate "Freescale MXC Real Time Clock"
>  	depends on ARCH_MXC
> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
> index e5160fd..f2d3420 100644
> --- a/drivers/rtc/Makefile
> +++ b/drivers/rtc/Makefile
> @@ -44,6 +44,7 @@ obj-$(CONFIG_RTC_DRV_EFI)	+= rtc-efi.o
>  obj-$(CONFIG_RTC_DRV_EP93XX)	+= rtc-ep93xx.o
>  obj-$(CONFIG_RTC_DRV_FM3130)	+= rtc-fm3130.o
>  obj-$(CONFIG_RTC_DRV_GENERIC)	+= rtc-generic.o
> +obj-$(CONFIG_RTC_DRV_IMXDI)	+= rtc-imxdi.o
>  obj-$(CONFIG_RTC_DRV_ISL1208)	+= rtc-isl1208.o
>  obj-$(CONFIG_RTC_DRV_M41T80)	+= rtc-m41t80.o
>  obj-$(CONFIG_RTC_DRV_M41T94)	+= rtc-m41t94.o
> diff --git a/drivers/rtc/rtc-imxdi.c b/drivers/rtc/rtc-imxdi.c
> new file mode 100644
> index 0000000..12a6df5
> --- /dev/null
> +++ b/drivers/rtc/rtc-imxdi.c
> @@ -0,0 +1,559 @@
> +/*
> + * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
> + * Copyright 2010 Orex Computed Radiography
> + */
> +
> +/*
> + * 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
> + */
> +
> +/* based on rtc-mc13892.c */
> +
> +/*
> + * This driver uses the 47-bit 32 kHz counter in the Freescale DryIce block
> + * to implement a Linux RTC. Times and alarms are truncated to seconds.
> + * Since the RTC framework performs API locking via rtc->ops_lock the
> + * only simultaneous accesses we need to deal with is updating DryIce
> + * registers while servicing an alarm.
> + *
> + * Note that reading the DSR (DryIce Status Register) automatically clears
> + * the WCF (Write Complete Flag). All DryIce writes are synchronized to the
> + * LP (Low Power) domain and set the WCF upon completion. Writes to the
> + * DIER (DryIce Interrupt Enable Register) are the only exception. These
> + * occur at normal bus speeds and do not set WCF.  Periodic interrupts are
> + * not supported by the hardware.
> + */
> +
> +#include <linux/io.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/rtc.h>
> +#include <linux/workqueue.h>
> +
> +/* DryIce Register Definitions */
> +
> +#define DTCMR     0x00           /* Time Counter MSB Reg */
> +#define DTCLR     0x04           /* Time Counter LSB Reg */
> +
> +#define DCAMR     0x08           /* Clock Alarm MSB Reg */
> +#define DCALR     0x0c           /* Clock Alarm LSB Reg */
> +#define DCAMR_UNSET  0xFFFFFFFF  /* doomsday - 1 sec */
> +
> +#define DCR       0x10           /* Control Reg */
> +#define DCR_TCE   (1 << 3)       /* Time Counter Enable */
> +
> +#define DSR       0x14           /* Status Reg */
> +#define DSR_WBF   (1 << 10)      /* Write Busy Flag */
> +#define DSR_WNF   (1 << 9)       /* Write Next Flag */
> +#define DSR_WCF   (1 << 8)       /* Write Complete Flag */
> +#define DSR_WEF   (1 << 7)       /* Write Error Flag */
> +#define DSR_CAF   (1 << 4)       /* Clock Alarm Flag */
> +#define DSR_NVF   (1 << 1)       /* Non-Valid Flag */
> +#define DSR_SVF   (1 << 0)       /* Security Violation Flag */
> +
> +#define DIER      0x18           /* Interrupt Enable Reg */
> +#define DIER_WNIE (1 << 9)       /* Write Next Interrupt Enable */
> +#define DIER_WCIE (1 << 8)       /* Write Complete Interrupt Enable */
> +#define DIER_WEIE (1 << 7)       /* Write Error Interrupt Enable */
> +#define DIER_CAIE (1 << 4)       /* Clock Alarm Interrupt Enable */
> +
> +/**
> + * struct imxdi_dev - private imxdi rtc data
> + * @pdev: pionter to platform dev
> + * @rtc: pointer to rtc struct
> + * @ioaddr: IO registers pointer
> + * @irq: dryice normal interrupt
> + * @clk: input reference clock
> + * @dsr: copy of the DSR register
> + * @irq_lock: interrupt enable register (DIER) lock
> + * @write_wait: registers write complete queue
> + * @write_mutex: serialize registers write
> + * @work: schedule alarm work
> + */
> +struct imxdi_dev {
> +	struct platform_device *pdev;
> +	struct rtc_device *rtc;
> +	void __iomem *ioaddr;
> +	int irq;
> +	struct clk *clk;
> +	u32 dsr;
> +	spinlock_t irq_lock;
> +	wait_queue_head_t write_wait;
> +	struct mutex write_mutex;
> +	struct work_struct work;
> +};
> +
> +/*
> + * enable a dryice interrupt
> + */
> +static inline void di_int_enable(struct imxdi_dev *imxdi, u32 intr)
> +{
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&imxdi->irq_lock, flags);
> +	__raw_writel(__raw_readl(imxdi->ioaddr+DIER) | intr,
> +			imxdi->ioaddr+DIER);
> +	spin_unlock_irqrestore(&imxdi->irq_lock, flags);
> +}
> +
> +/*
> + * disable a dryice interrupt
> + */
> +static inline void di_int_disable(struct imxdi_dev *imxdi, u32 intr)
> +{
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&imxdi->irq_lock, flags);
> +	__raw_writel(__raw_readl(imxdi->ioaddr+DIER) & ~intr,
> +			imxdi->ioaddr+DIER);
> +	spin_unlock_irqrestore(&imxdi->irq_lock, flags);
> +}
> +
> +/*
> + * This function attempts to clear the dryice write-error flag.
> + *
> + * A dryice write error is similar to a bus fault and should not occur in
> + * normal operation.  Clearing the flag requires another write, so the root
> + * cause of the problem may need to be fixed before the flag can be cleared.
> + */
> +static void clear_write_error(struct imxdi_dev *imxdi)
> +{
> +	int cnt;
> +
> +	dev_warn(&imxdi->pdev->dev, "WARNING: Register write error!\n");
> +
> +	for (;;) {
> +		/* clear the write error flag */
> +		__raw_writel(DSR_WEF, imxdi->ioaddr+DSR);
> +
> +		/* wait for it to take effect */
> +		for (cnt = 0; cnt < 100; cnt++) {
> +			if ((__raw_readl(imxdi->ioaddr+DSR) & DSR_WEF) == 0)
> +				return;
> +			udelay(10);
> +		}
> +		dev_err(&imxdi->pdev->dev,
> +			"ERROR: Cannot clear write-error flag!\n");
> +	}
> +}
> +
> +/*
> + * Write a dryice register and wait until it completes.
> + *
> + * This function uses interrupts to determine when the
> + * write has completed.
> + */
> +static int di_write_wait(struct imxdi_dev *imxdi, u32 val, int reg)
> +{
> +	int ret;
> +	int rc = 0;
> +
> +	/* serialize register writes */
> +	mutex_lock(&imxdi->write_mutex);
> +
> +	/* enable the write-complete interrupt */
> +	di_int_enable(imxdi, DIER_WCIE);
> +
> +	imxdi->dsr = 0;
> +
> +	/* do the register write */
> +	__raw_writel(val, imxdi->ioaddr+reg);
> +
> +	/* wait for the write to finish */
> +	ret = wait_event_interruptible_timeout(imxdi->write_wait,
> +			imxdi->dsr & (DSR_WCF | DSR_WEF), 1 * HZ);
> +	if (ret == 0)
> +		dev_warn(&imxdi->pdev->dev,
> +				"Write-wait timeout "
> +				"val = 0x%08x reg = 0x%08x\n", val, reg);
> +
> +	/* check for write error */
> +	if (imxdi->dsr & DSR_WEF) {
> +		clear_write_error(imxdi);
> +		rc = -EIO;
> +	}
> +	mutex_unlock(&imxdi->write_mutex);
> +	return rc;
> +}
> +
> +/*
> + * rtc device ioctl
> + *
> + * The rtc framework handles the basic rtc ioctls on behalf
> + * of the driver by calling the functions registered in the
> + * rtc_ops structure.
> + */

 there are ops for alarm handling too, ioctl is not needed

> +static int dryice_rtc_ioctl(struct device *dev, unsigned int cmd,
> +			    unsigned long arg)
> +{
> +	struct imxdi_dev *imxdi = dev_get_drvdata(dev);
> +
> +	dev_dbg(dev, "%s(0x%x)\n", __func__, cmd);
> +	switch (cmd) {
> +	case RTC_AIE_OFF:  /* alarm disable */
> +		di_int_disable(imxdi, DIER_CAIE);
> +		return 0;
> +
> +	case RTC_AIE_ON:  /* alarm enable */
> +		di_int_enable(imxdi, DIER_CAIE);
> +		return 0;
> +	}
> +	return -ENOIOCTLCMD;
> +}
> +
> +/*
> + * read the seconds portion of the current time from the dryice time counter
> + */
> +static int dryice_rtc_read_time(struct device *dev, struct rtc_time *tm)
> +{
> +	struct imxdi_dev *imxdi = dev_get_drvdata(dev);
> +	unsigned long now;
> +
> +	now = __raw_readl(imxdi->ioaddr+DTCMR);
> +	rtc_time_to_tm(now, tm);
> +
> +	return 0;
> +}
> +
> +/*
> + * set the seconds portion of dryice time counter and clear the
> + * fractional part.
> + */
> +static int dryice_rtc_set_time(struct device *dev, struct rtc_time *tm)
> +{
> +	struct imxdi_dev *imxdi = dev_get_drvdata(dev);
> +	unsigned long now;
> +	int rc;
> +
> +	rc = rtc_tm_to_time(tm, &now);
> +	if (rc == 0)
> +		/* zero the fractional part first */
> +		rc = di_write_wait(imxdi, 0, DTCLR);
> +	if (rc == 0)
> +		rc = di_write_wait(imxdi, now, DTCMR);

 two if ?

 please implement set_mmss instead of set_time

> +
> +	return rc;
> +}
> +
> +/*
> + * read the seconds portion of the alarm register.
> + * the fractional part of the alarm register is always zero.
> + */
> +static int dryice_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
> +{
> +	struct imxdi_dev *imxdi = dev_get_drvdata(dev);
> +	u32 dcamr;
> +
> +	dcamr = __raw_readl(imxdi->ioaddr+DCAMR);
> +	rtc_time_to_tm(dcamr, &alarm->time);
> +
> +	/* alarm is enabled if the interrupt is enabled */
> +	alarm->enabled = (__raw_readl(imxdi->ioaddr+DIER) & DIER_CAIE) != 0;
> +
> +	/* don't allow the DSR read to mess up DSR_WCF */
> +	mutex_lock(&imxdi->write_mutex);
> +
> +	/* alarm is pending if the alarm flag is set */
> +	alarm->pending = (__raw_readl(imxdi->ioaddr+DSR) & DSR_CAF) != 0;
> +
> +	mutex_unlock(&imxdi->write_mutex);
> +
> +	return 0;
> +}
> +
> +/*
> + * set the seconds portion of dryice alarm register
> + */
> +static int dryice_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
> +{
> +	struct imxdi_dev *imxdi = dev_get_drvdata(dev);
> +	unsigned long now;
> +	unsigned long alarm_time;
> +	int rc;
> +
> +	rc = rtc_tm_to_time(&alarm->time, &alarm_time);
> +	if (rc)
> +		return rc;
> +
> +	/* don't allow setting alarm in the past */
> +	now = __raw_readl(imxdi->ioaddr+DTCMR);
> +	if (alarm_time < now)
> +		return -EINVAL;
> +
> +	/* write the new alarm time */
> +	rc = di_write_wait(imxdi, (u32)alarm_time, DCAMR);
> +	if (rc)
> +		return rc;
> +
> +	if (alarm->enabled)
> +		di_int_enable(imxdi, DIER_CAIE);  /* enable alarm intr */
> +	else
> +		di_int_disable(imxdi, DIER_CAIE); /* disable alarm intr */
> +
> +	return 0;
> +}
> +
> +static struct rtc_class_ops dryice_rtc_ops = {
> +	.ioctl = dryice_rtc_ioctl,
> +	.read_time = dryice_rtc_read_time,
> +	.set_time = dryice_rtc_set_time,
> +	.read_alarm = dryice_rtc_read_alarm,
> +	.set_alarm = dryice_rtc_set_alarm,
> +};
> +
> +/*
> + * dryice "normal" interrupt handler
> + */
> +static irqreturn_t dryice_norm_irq(int irq, void *dev_id)
> +{
> +	struct imxdi_dev *imxdi = dev_id;
> +	u32 dsr, dier;
> +	irqreturn_t rc = IRQ_NONE;
> +
> +	dier = __raw_readl(imxdi->ioaddr+DIER);
> +
> +	/* handle write complete and write error cases */
> +	if ((dier & DIER_WCIE)) {
> +		/*If the write wait queue is empty then there is no pending
> +		  operations. It means the interrupt is for DryIce -Security.
> +		  IRQ must be returned as none.*/
> +		if (list_empty_careful(&imxdi->write_wait.task_list))
> +			return rc;
> +
> +		/* DSR_WCF clears itself on DSR read */
> +		dsr = __raw_readl(imxdi->ioaddr+DSR);
> +		if ((dsr & (DSR_WCF | DSR_WEF))) {
> +			/* mask the interrupt */
> +			di_int_disable(imxdi, DIER_WCIE);
> +
> +			/* save the dsr value for the wait queue */
> +			imxdi->dsr |= dsr;
> +
> +			wake_up_interruptible(&imxdi->write_wait);
> +			rc = IRQ_HANDLED;
> +		}
> +	}
> +
> +	/* handle the alarm case */
> +	if ((dier & DIER_CAIE)) {
> +		/* DSR_WCF clears itself on DSR read */
> +		dsr = __raw_readl(imxdi->ioaddr+DSR);
> +		if (dsr & DSR_CAF) {
> +			/* mask the interrupt */
> +			di_int_disable(imxdi, DIER_CAIE);
> +
> +			/* finish alarm in user context */
> +			schedule_work(&imxdi->work);
> +			rc = IRQ_HANDLED;
> +		}
> +	}
> +	return rc;
> +}
> +
> +/*
> + * post the alarm event from user context so it can sleep
> + * on the write completion.
> + */
> +static void dryice_work(struct work_struct *work)
> +{
> +	struct imxdi_dev *imxdi = container_of(work,
> +			struct imxdi_dev, work);
> +
> +	/* dismiss the interrupt (ignore error) */
> +	di_write_wait(imxdi, DSR_CAF, DSR);
> +
> +	/*
> +	 * pass the alarm event to the rtc framework. note that
> +	 * rtc_update_irq expects to be called with interrupts off.
> +	 */
> +	local_irq_disable();
> +	rtc_update_irq(imxdi->rtc, 1, RTC_AF | RTC_IRQF);
> +	local_irq_enable();
> +}
> +
> +/*
> + * probe for dryice rtc device
> + */
> +static int dryice_rtc_probe(struct platform_device *pdev)
> +{
> +	struct resource *res;
> +	struct imxdi_dev *imxdi = NULL;
> +	int rc = 0;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res)
> +		return -ENODEV;
> +
> +	imxdi = kzalloc(sizeof(*imxdi), GFP_KERNEL);
> +	if (!imxdi)
> +		return -ENOMEM;
> +
> +	imxdi->pdev = pdev;
> +	imxdi->irq = -1;
> +
> +	if (!request_mem_region(res->start, resource_size(res),
> +				pdev->name)) {
> +		rc = -EBUSY;
> +		goto err_free_mem;
> +	}
> +	imxdi->ioaddr = ioremap(res->start, resource_size(res));
> +	if (imxdi->ioaddr == NULL) {
> +		rc = -ENOMEM;
> +		goto err_release_region;
> +	}
> +	imxdi->irq = platform_get_irq(pdev, 0);
> +	init_waitqueue_head(&imxdi->write_wait);

 this routine has initialization and error sequence issues,
 I'd suggest to move to the resource manager
 
Documentation/driver-model/devres.txt

> +	INIT_WORK(&imxdi->work, dryice_work);
> +
> +	mutex_init(&imxdi->write_mutex);
> +
> +	imxdi->clk = clk_get(&pdev->dev, NULL);
> +	if (IS_ERR(imxdi->clk)) {
> +		rc = -ENODEV;
> +		goto err;
> +	}
> +	clk_enable(imxdi->clk);
> +
> +	if (imxdi->irq >= 0) {
> +		if (request_irq(imxdi->irq, dryice_norm_irq, IRQF_SHARED,
> +				pdev->name, imxdi) < 0) {
> +			dev_warn(&pdev->dev, "interrupt not available.\n");
> +			imxdi->irq = -1;
> +			goto err;
> +		}
> +	}

 be sure no irq is enabled prior to registration of the rtc device.

> +
> +	/*
> +	 * Initialize dryice hardware
> +	 */
> +
> +	/* put dryice into valid state */
> +	if (__raw_readl(imxdi->ioaddr+DSR) & DSR_NVF)
> +		rc = di_write_wait(imxdi, DSR_NVF | DSR_SVF, DSR);
> +	if (rc)
> +		goto err;
> +
> +	/* mask alarm interrupt */
> +	di_int_disable(imxdi, DIER_CAIE);
> +
> +	/* initialize alarm */
> +	rc = di_write_wait(imxdi, DCAMR_UNSET, DCAMR);
> +	if (rc == 0)
> +		rc = di_write_wait(imxdi, 0, DCALR);
> +	if (rc)
> +		goto err;
> +
> +	/* clear alarm flag */
> +	if (__raw_readl(imxdi->ioaddr+DSR) & DSR_CAF)
> +		rc = di_write_wait(imxdi, DSR_CAF, DSR);
> +	if (rc)
> +		goto err;
> +
> +	/* the timer won't count if it has never been written to */
> +	if (__raw_readl(imxdi->ioaddr+DTCMR) == 0)
> +		rc = di_write_wait(imxdi, 0, DTCMR);
> +	if (rc)
> +		goto err;
> +
> +	/* start keeping time */
> +	if (!(__raw_readl(imxdi->ioaddr+DCR) & DCR_TCE))
> +		rc = di_write_wait(imxdi,
> +				__raw_readl(imxdi->ioaddr+DCR) | DCR_TCE, DCR);
> +	if (rc)
> +		goto err;
> +
> +	imxdi->rtc = rtc_device_register(pdev->name, &pdev->dev,
> +				  &dryice_rtc_ops, THIS_MODULE);
> +	if (IS_ERR(imxdi->rtc)) {
> +		rc = PTR_ERR(imxdi->rtc);
> +		goto err;
> +	}
> +	platform_set_drvdata(pdev, imxdi);
> +
> +	return 0;
> +err:
> +	if (imxdi->rtc && !IS_ERR(imxdi->rtc))
> +		rtc_device_unregister(imxdi->rtc);
> +
> +	if (imxdi->irq >= 0)
> +		free_irq(imxdi->irq, imxdi);
> +
> +	if (imxdi->clk && !IS_ERR(imxdi->clk)) {
> +		clk_disable(imxdi->clk);
> +		clk_put(imxdi->clk);
> +	}
> +
> +	if (imxdi->ioaddr)
> +		iounmap(imxdi->ioaddr);
> +
> +err_release_region:
> +	release_mem_region(res->start, resource_size(res));
> +
> +err_free_mem:
> +	kfree(imxdi);
> +
> +	return rc;
> +}
> +
> +static int __exit dryice_rtc_remove(struct platform_device *pdev)
> +{
> +	struct imxdi_dev *imxdi = platform_get_drvdata(pdev);
> +	struct resource *mem;
> +
> +	flush_scheduled_work();
> +
> +	rtc_device_unregister(imxdi->rtc);
> +
> +	/* mask alarm interrupt */
> +	di_int_disable(imxdi, DIER_CAIE);
> +
> +	if (imxdi->irq >= 0)
> +		free_irq(imxdi->irq, imxdi);

 bbzt, wrong. disable prior to unregistration

> +	clk_disable(imxdi->clk);
> +	clk_put(imxdi->clk);
> +
> +	iounmap(imxdi->ioaddr);
> +
> +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	release_mem_region(mem->start, resource_size(mem));
> +
> +	kfree(imxdi);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver dryice_rtc_driver = {
> +	.driver = {
> +		   .name = "imxdi_rtc",
> +		   .owner = THIS_MODULE,
> +		   },
> +	.probe = dryice_rtc_probe,
> +	.remove = __exit_p(dryice_rtc_remove),
> +};
> +
> +static int __init dryice_rtc_init(void)
> +{
> +	return platform_driver_register(&dryice_rtc_driver);
> +}
> +
> +static void __exit dryice_rtc_exit(void)
> +{
> +	platform_driver_unregister(&dryice_rtc_driver);
> +}
> +
> +module_init(dryice_rtc_init);
> +module_exit(dryice_rtc_exit);
> +
> +MODULE_AUTHOR("Freescale Semiconductor, Inc.");

 email here please or you will not be
 notified of changes to the driver (not by me, at least ).

> +MODULE_DESCRIPTION("IMXDI Realtime Clock Driver (RTC)");
> +MODULE_LICENSE("GPL");
> -- 
> 1.6.5
> 
> -- 
> 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.


-- 

 Best regards,

 Alessandro Zummo,
  Tower Technologies - Torino, Italy

  http://www.towertech.it

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH] rtc: driver for the DryIce block found in i.MX25 chips
  2010-01-29 10:10   ` [rtc-linux] " Alessandro Zummo
@ 2010-02-01  7:36     ` Baruch Siach
  2010-02-01  8:08       ` Lothar Waßmann
  2010-02-01  9:20       ` Alessandro Zummo
  0 siblings, 2 replies; 20+ messages in thread
From: Baruch Siach @ 2010-02-01  7:36 UTC (permalink / raw)
  To: linux-arm-kernel

This driver is based on code from Freescale which accompanies their i.MX25 PDK
board, with some cleanup.

Signed-off-by: Baruch Siach <baruch@tkos.co.il>
---
 drivers/rtc/Kconfig     |   10 +
 drivers/rtc/Makefile    |    1 +
 drivers/rtc/rtc-imxdi.c |  532 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 543 insertions(+), 0 deletions(-)
 create mode 100644 drivers/rtc/rtc-imxdi.c

Changes v1 -> v2:

Address the comments of Alessandro Zummo:

	* remove the .ioctl implementation
	* implement .set_mmss instead of .set_time
	* reorder the .probe implementation
	* make sure not to have enabled interrupts when registering or 
	  unregistering the rtc device
	* add MODULE_AUTHOR() with my email

Address the comment of Lothar Wa??mann:

	* return PTR_ERR on clk_get failure

Slightly change MODULE_DESCRIPTION().
Use timeout which is independent of HZ for wait_event_interruptible_timeout().

diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 8167e9e..83bd602 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -527,6 +527,16 @@ config RTC_DRV_MSM6242
 	  This driver can also be built as a module. If so, the module
 	  will be called rtc-msm6242.
 
+config RTC_DRV_IMXDI
+	tristate "Freescale IMX DryIce Real Time Clock"
+	depends on ARCH_MX25
+	depends on RTC_CLASS
+	help
+	   Support for Freescale IMX DryIce RTC
+
+	   This driver can also be built as a module, if so, the module
+	   will be called "rtc-imxdi".
+
 config RTC_MXC
 	tristate "Freescale MXC Real Time Clock"
 	depends on ARCH_MXC
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index e5160fd..f2d3420 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -44,6 +44,7 @@ obj-$(CONFIG_RTC_DRV_EFI)	+= rtc-efi.o
 obj-$(CONFIG_RTC_DRV_EP93XX)	+= rtc-ep93xx.o
 obj-$(CONFIG_RTC_DRV_FM3130)	+= rtc-fm3130.o
 obj-$(CONFIG_RTC_DRV_GENERIC)	+= rtc-generic.o
+obj-$(CONFIG_RTC_DRV_IMXDI)	+= rtc-imxdi.o
 obj-$(CONFIG_RTC_DRV_ISL1208)	+= rtc-isl1208.o
 obj-$(CONFIG_RTC_DRV_M41T80)	+= rtc-m41t80.o
 obj-$(CONFIG_RTC_DRV_M41T94)	+= rtc-m41t94.o
diff --git a/drivers/rtc/rtc-imxdi.c b/drivers/rtc/rtc-imxdi.c
new file mode 100644
index 0000000..f9d4872
--- /dev/null
+++ b/drivers/rtc/rtc-imxdi.c
@@ -0,0 +1,532 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2010 Orex Computed Radiography
+ */
+
+/*
+ * 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
+ */
+
+/* based on rtc-mc13892.c */
+
+/*
+ * This driver uses the 47-bit 32 kHz counter in the Freescale DryIce block
+ * to implement a Linux RTC. Times and alarms are truncated to seconds.
+ * Since the RTC framework performs API locking via rtc->ops_lock the
+ * only simultaneous accesses we need to deal with is updating DryIce
+ * registers while servicing an alarm.
+ *
+ * Note that reading the DSR (DryIce Status Register) automatically clears
+ * the WCF (Write Complete Flag). All DryIce writes are synchronized to the
+ * LP (Low Power) domain and set the WCF upon completion. Writes to the
+ * DIER (DryIce Interrupt Enable Register) are the only exception. These
+ * occur at normal bus speeds and do not set WCF.  Periodic interrupts are
+ * not supported by the hardware.
+ */
+
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/workqueue.h>
+
+/* DryIce Register Definitions */
+
+#define DTCMR     0x00           /* Time Counter MSB Reg */
+#define DTCLR     0x04           /* Time Counter LSB Reg */
+
+#define DCAMR     0x08           /* Clock Alarm MSB Reg */
+#define DCALR     0x0c           /* Clock Alarm LSB Reg */
+#define DCAMR_UNSET  0xFFFFFFFF  /* doomsday - 1 sec */
+
+#define DCR       0x10           /* Control Reg */
+#define DCR_TCE   (1 << 3)       /* Time Counter Enable */
+
+#define DSR       0x14           /* Status Reg */
+#define DSR_WBF   (1 << 10)      /* Write Busy Flag */
+#define DSR_WNF   (1 << 9)       /* Write Next Flag */
+#define DSR_WCF   (1 << 8)       /* Write Complete Flag */
+#define DSR_WEF   (1 << 7)       /* Write Error Flag */
+#define DSR_CAF   (1 << 4)       /* Clock Alarm Flag */
+#define DSR_NVF   (1 << 1)       /* Non-Valid Flag */
+#define DSR_SVF   (1 << 0)       /* Security Violation Flag */
+
+#define DIER      0x18           /* Interrupt Enable Reg */
+#define DIER_WNIE (1 << 9)       /* Write Next Interrupt Enable */
+#define DIER_WCIE (1 << 8)       /* Write Complete Interrupt Enable */
+#define DIER_WEIE (1 << 7)       /* Write Error Interrupt Enable */
+#define DIER_CAIE (1 << 4)       /* Clock Alarm Interrupt Enable */
+
+/**
+ * struct imxdi_dev - private imxdi rtc data
+ * @pdev: pionter to platform dev
+ * @rtc: pointer to rtc struct
+ * @ioaddr: IO registers pointer
+ * @irq: dryice normal interrupt
+ * @clk: input reference clock
+ * @dsr: copy of the DSR register
+ * @irq_lock: interrupt enable register (DIER) lock
+ * @write_wait: registers write complete queue
+ * @write_mutex: serialize registers write
+ * @work: schedule alarm work
+ */
+struct imxdi_dev {
+	struct platform_device *pdev;
+	struct rtc_device *rtc;
+	void __iomem *ioaddr;
+	int irq;
+	struct clk *clk;
+	u32 dsr;
+	spinlock_t irq_lock;
+	wait_queue_head_t write_wait;
+	struct mutex write_mutex;
+	struct work_struct work;
+};
+
+/*
+ * enable a dryice interrupt
+ */
+static inline void di_int_enable(struct imxdi_dev *imxdi, u32 intr)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&imxdi->irq_lock, flags);
+	__raw_writel(__raw_readl(imxdi->ioaddr+DIER) | intr,
+			imxdi->ioaddr+DIER);
+	spin_unlock_irqrestore(&imxdi->irq_lock, flags);
+}
+
+/*
+ * disable a dryice interrupt
+ */
+static inline void di_int_disable(struct imxdi_dev *imxdi, u32 intr)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&imxdi->irq_lock, flags);
+	__raw_writel(__raw_readl(imxdi->ioaddr+DIER) & ~intr,
+			imxdi->ioaddr+DIER);
+	spin_unlock_irqrestore(&imxdi->irq_lock, flags);
+}
+
+/*
+ * This function attempts to clear the dryice write-error flag.
+ *
+ * A dryice write error is similar to a bus fault and should not occur in
+ * normal operation.  Clearing the flag requires another write, so the root
+ * cause of the problem may need to be fixed before the flag can be cleared.
+ */
+static void clear_write_error(struct imxdi_dev *imxdi)
+{
+	int cnt;
+
+	dev_warn(&imxdi->pdev->dev, "WARNING: Register write error!\n");
+
+	for (;;) {
+		/* clear the write error flag */
+		__raw_writel(DSR_WEF, imxdi->ioaddr+DSR);
+
+		/* wait for it to take effect */
+		for (cnt = 0; cnt < 100; cnt++) {
+			if ((__raw_readl(imxdi->ioaddr+DSR) & DSR_WEF) == 0)
+				return;
+			udelay(10);
+		}
+		dev_err(&imxdi->pdev->dev,
+			"ERROR: Cannot clear write-error flag!\n");
+	}
+}
+
+/*
+ * Write a dryice register and wait until it completes.
+ *
+ * This function uses interrupts to determine when the
+ * write has completed.
+ */
+static int di_write_wait(struct imxdi_dev *imxdi, u32 val, int reg)
+{
+	int ret;
+	int rc = 0;
+
+	/* serialize register writes */
+	mutex_lock(&imxdi->write_mutex);
+
+	/* enable the write-complete interrupt */
+	di_int_enable(imxdi, DIER_WCIE);
+
+	imxdi->dsr = 0;
+
+	/* do the register write */
+	__raw_writel(val, imxdi->ioaddr+reg);
+
+	/* wait for the write to finish */
+	ret = wait_event_interruptible_timeout(imxdi->write_wait,
+			imxdi->dsr & (DSR_WCF | DSR_WEF), msecs_to_jiffies(1));
+	if (ret == 0)
+		dev_warn(&imxdi->pdev->dev,
+				"Write-wait timeout "
+				"val = 0x%08x reg = 0x%08x\n", val, reg);
+
+	/* check for write error */
+	if (imxdi->dsr & DSR_WEF) {
+		clear_write_error(imxdi);
+		rc = -EIO;
+	}
+	mutex_unlock(&imxdi->write_mutex);
+	return rc;
+}
+
+/*
+ * read the seconds portion of the current time from the dryice time counter
+ */
+static int dryice_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+	struct imxdi_dev *imxdi = dev_get_drvdata(dev);
+	unsigned long now;
+
+	now = __raw_readl(imxdi->ioaddr+DTCMR);
+	rtc_time_to_tm(now, tm);
+
+	return 0;
+}
+
+/*
+ * set the seconds portion of dryice time counter and clear the
+ * fractional part.
+ */
+static int dryice_rtc_set_mmss(struct device *dev, unsigned long secs)
+{
+	struct imxdi_dev *imxdi = dev_get_drvdata(dev);
+	int rc;
+
+	/* zero the fractional part first */
+	rc = di_write_wait(imxdi, 0, DTCLR);
+	if (rc == 0)
+		rc = di_write_wait(imxdi, secs, DTCMR);
+
+	return rc;
+}
+
+/*
+ * read the seconds portion of the alarm register.
+ * the fractional part of the alarm register is always zero.
+ */
+static int dryice_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+	struct imxdi_dev *imxdi = dev_get_drvdata(dev);
+	u32 dcamr;
+
+	dcamr = __raw_readl(imxdi->ioaddr+DCAMR);
+	rtc_time_to_tm(dcamr, &alarm->time);
+
+	/* alarm is enabled if the interrupt is enabled */
+	alarm->enabled = (__raw_readl(imxdi->ioaddr+DIER) & DIER_CAIE) != 0;
+
+	/* don't allow the DSR read to mess up DSR_WCF */
+	mutex_lock(&imxdi->write_mutex);
+
+	/* alarm is pending if the alarm flag is set */
+	alarm->pending = (__raw_readl(imxdi->ioaddr+DSR) & DSR_CAF) != 0;
+
+	mutex_unlock(&imxdi->write_mutex);
+
+	return 0;
+}
+
+/*
+ * set the seconds portion of dryice alarm register
+ */
+static int dryice_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+	struct imxdi_dev *imxdi = dev_get_drvdata(dev);
+	unsigned long now;
+	unsigned long alarm_time;
+	int rc;
+
+	rc = rtc_tm_to_time(&alarm->time, &alarm_time);
+	if (rc)
+		return rc;
+
+	/* don't allow setting alarm in the past */
+	now = __raw_readl(imxdi->ioaddr+DTCMR);
+	if (alarm_time < now)
+		return -EINVAL;
+
+	/* write the new alarm time */
+	rc = di_write_wait(imxdi, (u32)alarm_time, DCAMR);
+	if (rc)
+		return rc;
+
+	if (alarm->enabled)
+		di_int_enable(imxdi, DIER_CAIE);  /* enable alarm intr */
+	else
+		di_int_disable(imxdi, DIER_CAIE); /* disable alarm intr */
+
+	return 0;
+}
+
+static struct rtc_class_ops dryice_rtc_ops = {
+	.read_time = dryice_rtc_read_time,
+	.set_mmss = dryice_rtc_set_mmss,
+	.read_alarm = dryice_rtc_read_alarm,
+	.set_alarm = dryice_rtc_set_alarm,
+};
+
+/*
+ * dryice "normal" interrupt handler
+ */
+static irqreturn_t dryice_norm_irq(int irq, void *dev_id)
+{
+	struct imxdi_dev *imxdi = dev_id;
+	u32 dsr, dier;
+	irqreturn_t rc = IRQ_NONE;
+
+	dier = __raw_readl(imxdi->ioaddr+DIER);
+
+	/* handle write complete and write error cases */
+	if ((dier & DIER_WCIE)) {
+		/*If the write wait queue is empty then there is no pending
+		  operations. It means the interrupt is for DryIce -Security.
+		  IRQ must be returned as none.*/
+		if (list_empty_careful(&imxdi->write_wait.task_list))
+			return rc;
+
+		/* DSR_WCF clears itself on DSR read */
+		dsr = __raw_readl(imxdi->ioaddr+DSR);
+		if ((dsr & (DSR_WCF | DSR_WEF))) {
+			/* mask the interrupt */
+			di_int_disable(imxdi, DIER_WCIE);
+
+			/* save the dsr value for the wait queue */
+			imxdi->dsr |= dsr;
+
+			wake_up_interruptible(&imxdi->write_wait);
+			rc = IRQ_HANDLED;
+		}
+	}
+
+	/* handle the alarm case */
+	if ((dier & DIER_CAIE)) {
+		/* DSR_WCF clears itself on DSR read */
+		dsr = __raw_readl(imxdi->ioaddr+DSR);
+		if (dsr & DSR_CAF) {
+			/* mask the interrupt */
+			di_int_disable(imxdi, DIER_CAIE);
+
+			/* finish alarm in user context */
+			schedule_work(&imxdi->work);
+			rc = IRQ_HANDLED;
+		}
+	}
+	return rc;
+}
+
+/*
+ * post the alarm event from user context so it can sleep
+ * on the write completion.
+ */
+static void dryice_work(struct work_struct *work)
+{
+	struct imxdi_dev *imxdi = container_of(work,
+			struct imxdi_dev, work);
+
+	/* dismiss the interrupt (ignore error) */
+	di_write_wait(imxdi, DSR_CAF, DSR);
+
+	/*
+	 * pass the alarm event to the rtc framework. note that
+	 * rtc_update_irq expects to be called with interrupts off.
+	 */
+	local_irq_disable();
+	rtc_update_irq(imxdi->rtc, 1, RTC_AF | RTC_IRQF);
+	local_irq_enable();
+}
+
+/*
+ * probe for dryice rtc device
+ */
+static int dryice_rtc_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	struct imxdi_dev *imxdi = NULL;
+	int rc = 0;
+
+	imxdi = kzalloc(sizeof(*imxdi), GFP_KERNEL);
+	if (!imxdi)
+		return -ENOMEM;
+
+	imxdi->pdev = pdev;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENODEV;
+
+	if (!request_mem_region(res->start, resource_size(res),
+				pdev->name)) {
+		rc = -EBUSY;
+		goto err_free_mem;
+	}
+	imxdi->ioaddr = ioremap(res->start, resource_size(res));
+	if (imxdi->ioaddr == NULL) {
+		rc = -ENOMEM;
+		goto err_release_region;
+	}
+
+	if ((imxdi->irq = platform_get_irq(pdev, 0)) < 0) {
+		rc = imxdi->irq;
+		goto err;
+	}
+
+	init_waitqueue_head(&imxdi->write_wait);
+
+	INIT_WORK(&imxdi->work, dryice_work);
+
+	mutex_init(&imxdi->write_mutex);
+
+	imxdi->clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(imxdi->clk)) {
+		rc = PTR_ERR(imxdi->clk);
+		goto err;
+	}
+	clk_enable(imxdi->clk);
+
+	/*
+	 * Initialize dryice hardware
+	 */
+
+	/* put dryice into valid state */
+	if (__raw_readl(imxdi->ioaddr+DSR) & DSR_NVF)
+		rc = di_write_wait(imxdi, DSR_NVF | DSR_SVF, DSR);
+	if (rc)
+		goto err;
+
+	/* mask all interrupts */
+	__raw_writel(0, imxdi->ioaddr+DIER);
+
+	if (request_irq(imxdi->irq, dryice_norm_irq, IRQF_SHARED,
+				pdev->name, imxdi) < 0) {
+		dev_warn(&pdev->dev, "interrupt not available.\n");
+		imxdi->irq = -1;
+		goto err;
+	}
+
+	/* initialize alarm */
+	rc = di_write_wait(imxdi, DCAMR_UNSET, DCAMR);
+	if (rc == 0)
+		rc = di_write_wait(imxdi, 0, DCALR);
+	if (rc)
+		goto err;
+
+	/* clear alarm flag */
+	if (__raw_readl(imxdi->ioaddr+DSR) & DSR_CAF)
+		rc = di_write_wait(imxdi, DSR_CAF, DSR);
+	if (rc)
+		goto err;
+
+	/* the timer won't count if it has never been written to */
+	if (__raw_readl(imxdi->ioaddr+DTCMR) == 0)
+		rc = di_write_wait(imxdi, 0, DTCMR);
+	if (rc)
+		goto err;
+
+	/* start keeping time */
+	if (!(__raw_readl(imxdi->ioaddr+DCR) & DCR_TCE))
+		rc = di_write_wait(imxdi,
+				__raw_readl(imxdi->ioaddr+DCR) | DCR_TCE, DCR);
+	if (rc)
+		goto err;
+
+	imxdi->rtc = rtc_device_register(pdev->name, &pdev->dev,
+				  &dryice_rtc_ops, THIS_MODULE);
+	if (IS_ERR(imxdi->rtc)) {
+		rc = PTR_ERR(imxdi->rtc);
+		goto err;
+	}
+	platform_set_drvdata(pdev, imxdi);
+
+	return 0;
+err:
+	if (imxdi->rtc && !IS_ERR(imxdi->rtc))
+		rtc_device_unregister(imxdi->rtc);
+
+	if (imxdi->irq >= 0)
+		free_irq(imxdi->irq, imxdi);
+
+	if (imxdi->clk && !IS_ERR(imxdi->clk)) {
+		clk_disable(imxdi->clk);
+		clk_put(imxdi->clk);
+	}
+
+	if (imxdi->ioaddr)
+		iounmap(imxdi->ioaddr);
+
+err_release_region:
+	release_mem_region(res->start, resource_size(res));
+
+err_free_mem:
+	kfree(imxdi);
+
+	return rc;
+}
+
+static int __exit dryice_rtc_remove(struct platform_device *pdev)
+{
+	struct imxdi_dev *imxdi = platform_get_drvdata(pdev);
+	struct resource *mem;
+
+	flush_scheduled_work();
+
+	/* mask alarm interrupt */
+	di_int_disable(imxdi, DIER_CAIE);
+
+	if (imxdi->irq >= 0)
+		free_irq(imxdi->irq, imxdi);
+
+	rtc_device_unregister(imxdi->rtc);
+
+	clk_disable(imxdi->clk);
+	clk_put(imxdi->clk);
+
+	iounmap(imxdi->ioaddr);
+
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	release_mem_region(mem->start, resource_size(mem));
+
+	kfree(imxdi);
+
+	return 0;
+}
+
+static struct platform_driver dryice_rtc_driver = {
+	.driver = {
+		   .name = "imxdi_rtc",
+		   .owner = THIS_MODULE,
+		   },
+	.probe = dryice_rtc_probe,
+	.remove = __exit_p(dryice_rtc_remove),
+};
+
+static int __init dryice_rtc_init(void)
+{
+	return platform_driver_register(&dryice_rtc_driver);
+}
+
+static void __exit dryice_rtc_exit(void)
+{
+	platform_driver_unregister(&dryice_rtc_driver);
+}
+
+module_init(dryice_rtc_init);
+module_exit(dryice_rtc_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>");
+MODULE_DESCRIPTION("IMX DryIce Realtime Clock Driver (RTC)");
+MODULE_LICENSE("GPL");
-- 
1.6.5

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH] rtc: driver for the DryIce block found in i.MX25 chips
  2010-02-01  7:36     ` [PATCH] " Baruch Siach
@ 2010-02-01  8:08       ` Lothar Waßmann
  2010-02-01  8:28         ` Baruch Siach
  2010-02-01  9:20       ` Alessandro Zummo
  1 sibling, 1 reply; 20+ messages in thread
From: Lothar Waßmann @ 2010-02-01  8:08 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

Just one nitpick, I didn't notice when replying to your first mail...
Baruch Siach writes:
[...]
> +static inline void di_int_enable(struct imxdi_dev *imxdi, u32 intr)
> +{
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&imxdi->irq_lock, flags);
> +	__raw_writel(__raw_readl(imxdi->ioaddr+DIER) | intr,
                                              ^
> +			imxdi->ioaddr+DIER);
                                     ^
>
Spaces around operators are welcome in the kernel.


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] 20+ messages in thread

* [PATCH] rtc: driver for the DryIce block found in i.MX25 chips
  2010-02-01  8:08       ` Lothar Waßmann
@ 2010-02-01  8:28         ` Baruch Siach
  0 siblings, 0 replies; 20+ messages in thread
From: Baruch Siach @ 2010-02-01  8:28 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Lothar,

On Mon, Feb 01, 2010 at 09:08:37AM +0100, Lothar Wa?mann wrote:
> Just one nitpick, I didn't notice when replying to your first mail...
> Baruch Siach writes:
> [...]
> > +static inline void di_int_enable(struct imxdi_dev *imxdi, u32 intr)
> > +{
> > +	unsigned long flags;
> > +
> > +	spin_lock_irqsave(&imxdi->irq_lock, flags);
> > +	__raw_writel(__raw_readl(imxdi->ioaddr+DIER) | intr,
>                                             ^
> > +			imxdi->ioaddr+DIER);
>                            ^
> >
> Spaces around operators are welcome in the kernel.

Thanks for your review.
Will fix in the next version.

baruch

-- 
                                                     ~. .~   Tk Open Systems
=}------------------------------------------------ooO--U--Ooo------------{=
   - baruch at tkos.co.il - tel: +972.2.679.5364, http://www.tkos.co.il -

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH] rtc: driver for the DryIce block found in i.MX25 chips
  2010-02-01  7:36     ` [PATCH] " Baruch Siach
  2010-02-01  8:08       ` Lothar Waßmann
@ 2010-02-01  9:20       ` Alessandro Zummo
  2010-02-02  6:47         ` [PATCH v3] " Baruch Siach
  1 sibling, 1 reply; 20+ messages in thread
From: Alessandro Zummo @ 2010-02-01  9:20 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon,  1 Feb 2010 09:36:51 +0200
Baruch Siach <baruch@tkos.co.il> wrote:

> Address the comments of Alessandro Zummo:
> 
> 	* remove the .ioctl implementation

 you did not add the ops for alarm irq handling... ?

 more below.

> +static int dryice_rtc_probe(struct platform_device *pdev)
> +{
> +	struct resource *res;
> +	struct imxdi_dev *imxdi = NULL;

 = NULL is not necessary

> +	int rc = 0;

 idem for = 0

> +	imxdi = kzalloc(sizeof(*imxdi), GFP_KERNEL);
> +	if (!imxdi)
> +		return -ENOMEM;
> +
> +	imxdi->pdev = pdev;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res)
> +		return -ENODEV;

 you forgot to release imxdi here. I still suggest to
 use devm.

> +
> +	if (!request_mem_region(res->start, resource_size(res),
> +				pdev->name)) {
> +		rc = -EBUSY;
> +		goto err_free_mem;
> +	}
> +	imxdi->ioaddr = ioremap(res->start, resource_size(res));
> +	if (imxdi->ioaddr == NULL) {
> +		rc = -ENOMEM;
> +		goto err_release_region;
> +	}
> +
> +	if ((imxdi->irq = platform_get_irq(pdev, 0)) < 0) {
> +		rc = imxdi->irq;
> +		goto err;
> +	}

 no if inline with the assignment please, it's not readable.

> +	init_waitqueue_head(&imxdi->write_wait);
> +
> +	INIT_WORK(&imxdi->work, dryice_work);
> +
> +	mutex_init(&imxdi->write_mutex);
> +
> +	imxdi->clk = clk_get(&pdev->dev, NULL);
> +	if (IS_ERR(imxdi->clk)) {
> +		rc = PTR_ERR(imxdi->clk);
> +		goto err;
> +	}
> +	clk_enable(imxdi->clk);
> +
> +	/*
> +	 * Initialize dryice hardware
> +	 */
> +
> +	/* put dryice into valid state */
> +	if (__raw_readl(imxdi->ioaddr+DSR) & DSR_NVF)
> +		rc = di_write_wait(imxdi, DSR_NVF | DSR_SVF, DSR);
> +	if (rc)
> +		goto err;
> +
> +	/* mask all interrupts */
> +	__raw_writel(0, imxdi->ioaddr+DIER);
> +
> +	if (request_irq(imxdi->irq, dryice_norm_irq, IRQF_SHARED,
> +				pdev->name, imxdi) < 0) {
> +		dev_warn(&pdev->dev, "interrupt not available.\n");
> +		imxdi->irq = -1;
> +		goto err;
> +	}

 ditto for the if. what's the value of rc here?

> +	/* initialize alarm */
> +	rc = di_write_wait(imxdi, DCAMR_UNSET, DCAMR);
> +	if (rc == 0)
> +		rc = di_write_wait(imxdi, 0, DCALR);
> +	if (rc)
> +		goto err;
> +
> +	/* clear alarm flag */
> +	if (__raw_readl(imxdi->ioaddr+DSR) & DSR_CAF)
> +		rc = di_write_wait(imxdi, DSR_CAF, DSR);
> +	if (rc)
> +		goto err;
> +
> +	/* the timer won't count if it has never been written to */
> +	if (__raw_readl(imxdi->ioaddr+DTCMR) == 0)
> +		rc = di_write_wait(imxdi, 0, DTCMR);
> +	if (rc)
> +		goto err;
> +
> +	/* start keeping time */
> +	if (!(__raw_readl(imxdi->ioaddr+DCR) & DCR_TCE))
> +		rc = di_write_wait(imxdi,
> +				__raw_readl(imxdi->ioaddr+DCR) | DCR_TCE, DCR);
> +	if (rc)
> +		goto err;

 spaces are welcomed around the +

> +	imxdi->rtc = rtc_device_register(pdev->name, &pdev->dev,
> +				  &dryice_rtc_ops, THIS_MODULE);
> +	if (IS_ERR(imxdi->rtc)) {
> +		rc = PTR_ERR(imxdi->rtc);
> +		goto err;
> +	}
> +	platform_set_drvdata(pdev, imxdi);
> +
> +	return 0;
> +err:
> +	if (imxdi->rtc && !IS_ERR(imxdi->rtc))
> +		rtc_device_unregister(imxdi->rtc);
> +
> +	if (imxdi->irq >= 0)
> +		free_irq(imxdi->irq, imxdi);
> +
> +	if (imxdi->clk && !IS_ERR(imxdi->clk)) {
> +		clk_disable(imxdi->clk);
> +		clk_put(imxdi->clk);
> +	}
> +
> +	if (imxdi->ioaddr)
> +		iounmap(imxdi->ioaddr);
> +
> +err_release_region:
> +	release_mem_region(res->start, resource_size(res));
> +
> +err_free_mem:
> +	kfree(imxdi);
> +
> +	return rc;
> +}
> +
> +static int __exit dryice_rtc_remove(struct platform_device *pdev)

 no __devexit? 

> +{
> +	struct imxdi_dev *imxdi = platform_get_drvdata(pdev);
> +	struct resource *mem;
> +
> +	flush_scheduled_work();
> +
> +	/* mask alarm interrupt */
> +	di_int_disable(imxdi, DIER_CAIE);
> +
> +	if (imxdi->irq >= 0)
> +		free_irq(imxdi->irq, imxdi);
> +
> +	rtc_device_unregister(imxdi->rtc);
> +
> +	clk_disable(imxdi->clk);
> +	clk_put(imxdi->clk);
> +
> +	iounmap(imxdi->ioaddr);
> +
> +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	release_mem_region(mem->start, resource_size(mem));
> +
> +	kfree(imxdi);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver dryice_rtc_driver = {
> +	.driver = {
> +		   .name = "imxdi_rtc",
> +		   .owner = THIS_MODULE,
> +		   },
> +	.probe = dryice_rtc_probe,
> +	.remove = __exit_p(dryice_rtc_remove),
 
 __devexit_p ?

> +};
> +
> +static int __init dryice_rtc_init(void)
> +{
> +	return platform_driver_register(&dryice_rtc_driver);
> +}
> +
> +static void __exit dryice_rtc_exit(void)
> +{
> +	platform_driver_unregister(&dryice_rtc_driver);
> +}
> +
> +module_init(dryice_rtc_init);
> +module_exit(dryice_rtc_exit);
> +
> +MODULE_AUTHOR("Freescale Semiconductor, Inc.");
> +MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>");
> +MODULE_DESCRIPTION("IMX DryIce Realtime Clock Driver (RTC)");
> +MODULE_LICENSE("GPL");
> -- 
> 1.6.5
> 


-- 

 Best regards,

 Alessandro Zummo,
  Tower Technologies - Torino, Italy

  http://www.towertech.it

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH v3] rtc: driver for the DryIce block found in i.MX25 chips
  2010-02-01  9:20       ` Alessandro Zummo
@ 2010-02-02  6:47         ` Baruch Siach
  2010-02-02  8:22           ` [rtc-linux] " Alessandro Zummo
  0 siblings, 1 reply; 20+ messages in thread
From: Baruch Siach @ 2010-02-02  6:47 UTC (permalink / raw)
  To: linux-arm-kernel

This driver is based on code from Freescale which accompanies their i.MX25 PDK
board, with some cleanup.

Signed-off-by: Baruch Siach <baruch@tkos.co.il>
---
 drivers/rtc/Kconfig     |   10 +
 drivers/rtc/Makefile    |    1 +
 drivers/rtc/rtc-imxdi.c |  532 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 543 insertions(+), 0 deletions(-)
 create mode 100644 drivers/rtc/rtc-imxdi.c

Changes v2 -> v3:

Add spaces around the '+' operator as suggested by Alessandro Zummo and Lothar 
Wa??mann.

Address other comments of Alessandro Zummo:

	* add implementation of the alarm_irq_enable rtc op
	* use devres in .probe
	* remove unneeded initializations

Changes v1 -> v2:

Address the comments of Alessandro Zummo:

	* remove the .ioctl implementation
	* implement .set_mmss instead of .set_time
	* reorder the .probe implementation
	* make sure not to have enabled interrupts when registering or 
	  unregistering the rtc device
	* add MODULE_AUTHOR() with my email

Address the comment of Lothar Wa??mann:

	* return PTR_ERR on clk_get failure

Slightly change MODULE_DESCRIPTION().
Use timeout which is independent of HZ for wait_event_interruptible_timeout().

diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 8167e9e..83bd602 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -527,6 +527,16 @@ config RTC_DRV_MSM6242
 	  This driver can also be built as a module. If so, the module
 	  will be called rtc-msm6242.
 
+config RTC_DRV_IMXDI
+	tristate "Freescale IMX DryIce Real Time Clock"
+	depends on ARCH_MX25
+	depends on RTC_CLASS
+	help
+	   Support for Freescale IMX DryIce RTC
+
+	   This driver can also be built as a module, if so, the module
+	   will be called "rtc-imxdi".
+
 config RTC_MXC
 	tristate "Freescale MXC Real Time Clock"
 	depends on ARCH_MXC
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index e5160fd..f2d3420 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -44,6 +44,7 @@ obj-$(CONFIG_RTC_DRV_EFI)	+= rtc-efi.o
 obj-$(CONFIG_RTC_DRV_EP93XX)	+= rtc-ep93xx.o
 obj-$(CONFIG_RTC_DRV_FM3130)	+= rtc-fm3130.o
 obj-$(CONFIG_RTC_DRV_GENERIC)	+= rtc-generic.o
+obj-$(CONFIG_RTC_DRV_IMXDI)	+= rtc-imxdi.o
 obj-$(CONFIG_RTC_DRV_ISL1208)	+= rtc-isl1208.o
 obj-$(CONFIG_RTC_DRV_M41T80)	+= rtc-m41t80.o
 obj-$(CONFIG_RTC_DRV_M41T94)	+= rtc-m41t94.o
diff --git a/drivers/rtc/rtc-imxdi.c b/drivers/rtc/rtc-imxdi.c
new file mode 100644
index 0000000..f9d4872
--- /dev/null
+++ b/drivers/rtc/rtc-imxdi.c
@@ -0,0 +1,532 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2010 Orex Computed Radiography
+ */
+
+/*
+ * 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
+ */
+
+/* based on rtc-mc13892.c */
+
+/*
+ * This driver uses the 47-bit 32 kHz counter in the Freescale DryIce block
+ * to implement a Linux RTC. Times and alarms are truncated to seconds.
+ * Since the RTC framework performs API locking via rtc->ops_lock the
+ * only simultaneous accesses we need to deal with is updating DryIce
+ * registers while servicing an alarm.
+ *
+ * Note that reading the DSR (DryIce Status Register) automatically clears
+ * the WCF (Write Complete Flag). All DryIce writes are synchronized to the
+ * LP (Low Power) domain and set the WCF upon completion. Writes to the
+ * DIER (DryIce Interrupt Enable Register) are the only exception. These
+ * occur at normal bus speeds and do not set WCF.  Periodic interrupts are
+ * not supported by the hardware.
+ */
+
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/workqueue.h>
+
+/* DryIce Register Definitions */
+
+#define DTCMR     0x00           /* Time Counter MSB Reg */
+#define DTCLR     0x04           /* Time Counter LSB Reg */
+
+#define DCAMR     0x08           /* Clock Alarm MSB Reg */
+#define DCALR     0x0c           /* Clock Alarm LSB Reg */
+#define DCAMR_UNSET  0xFFFFFFFF  /* doomsday - 1 sec */
+
+#define DCR       0x10           /* Control Reg */
+#define DCR_TCE   (1 << 3)       /* Time Counter Enable */
+
+#define DSR       0x14           /* Status Reg */
+#define DSR_WBF   (1 << 10)      /* Write Busy Flag */
+#define DSR_WNF   (1 << 9)       /* Write Next Flag */
+#define DSR_WCF   (1 << 8)       /* Write Complete Flag */
+#define DSR_WEF   (1 << 7)       /* Write Error Flag */
+#define DSR_CAF   (1 << 4)       /* Clock Alarm Flag */
+#define DSR_NVF   (1 << 1)       /* Non-Valid Flag */
+#define DSR_SVF   (1 << 0)       /* Security Violation Flag */
+
+#define DIER      0x18           /* Interrupt Enable Reg */
+#define DIER_WNIE (1 << 9)       /* Write Next Interrupt Enable */
+#define DIER_WCIE (1 << 8)       /* Write Complete Interrupt Enable */
+#define DIER_WEIE (1 << 7)       /* Write Error Interrupt Enable */
+#define DIER_CAIE (1 << 4)       /* Clock Alarm Interrupt Enable */
+
+/**
+ * struct imxdi_dev - private imxdi rtc data
+ * @pdev: pionter to platform dev
+ * @rtc: pointer to rtc struct
+ * @ioaddr: IO registers pointer
+ * @irq: dryice normal interrupt
+ * @clk: input reference clock
+ * @dsr: copy of the DSR register
+ * @irq_lock: interrupt enable register (DIER) lock
+ * @write_wait: registers write complete queue
+ * @write_mutex: serialize registers write
+ * @work: schedule alarm work
+ */
+struct imxdi_dev {
+	struct platform_device *pdev;
+	struct rtc_device *rtc;
+	void __iomem *ioaddr;
+	int irq;
+	struct clk *clk;
+	u32 dsr;
+	spinlock_t irq_lock;
+	wait_queue_head_t write_wait;
+	struct mutex write_mutex;
+	struct work_struct work;
+};
+
+/*
+ * enable a dryice interrupt
+ */
+static inline void di_int_enable(struct imxdi_dev *imxdi, u32 intr)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&imxdi->irq_lock, flags);
+	__raw_writel(__raw_readl(imxdi->ioaddr+DIER) | intr,
+			imxdi->ioaddr+DIER);
+	spin_unlock_irqrestore(&imxdi->irq_lock, flags);
+}
+
+/*
+ * disable a dryice interrupt
+ */
+static inline void di_int_disable(struct imxdi_dev *imxdi, u32 intr)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&imxdi->irq_lock, flags);
+	__raw_writel(__raw_readl(imxdi->ioaddr+DIER) & ~intr,
+			imxdi->ioaddr+DIER);
+	spin_unlock_irqrestore(&imxdi->irq_lock, flags);
+}
+
+/*
+ * This function attempts to clear the dryice write-error flag.
+ *
+ * A dryice write error is similar to a bus fault and should not occur in
+ * normal operation.  Clearing the flag requires another write, so the root
+ * cause of the problem may need to be fixed before the flag can be cleared.
+ */
+static void clear_write_error(struct imxdi_dev *imxdi)
+{
+	int cnt;
+
+	dev_warn(&imxdi->pdev->dev, "WARNING: Register write error!\n");
+
+	for (;;) {
+		/* clear the write error flag */
+		__raw_writel(DSR_WEF, imxdi->ioaddr+DSR);
+
+		/* wait for it to take effect */
+		for (cnt = 0; cnt < 100; cnt++) {
+			if ((__raw_readl(imxdi->ioaddr+DSR) & DSR_WEF) == 0)
+				return;
+			udelay(10);
+		}
+		dev_err(&imxdi->pdev->dev,
+			"ERROR: Cannot clear write-error flag!\n");
+	}
+}
+
+/*
+ * Write a dryice register and wait until it completes.
+ *
+ * This function uses interrupts to determine when the
+ * write has completed.
+ */
+static int di_write_wait(struct imxdi_dev *imxdi, u32 val, int reg)
+{
+	int ret;
+	int rc = 0;
+
+	/* serialize register writes */
+	mutex_lock(&imxdi->write_mutex);
+
+	/* enable the write-complete interrupt */
+	di_int_enable(imxdi, DIER_WCIE);
+
+	imxdi->dsr = 0;
+
+	/* do the register write */
+	__raw_writel(val, imxdi->ioaddr+reg);
+
+	/* wait for the write to finish */
+	ret = wait_event_interruptible_timeout(imxdi->write_wait,
+			imxdi->dsr & (DSR_WCF | DSR_WEF), msecs_to_jiffies(1));
+	if (ret == 0)
+		dev_warn(&imxdi->pdev->dev,
+				"Write-wait timeout "
+				"val = 0x%08x reg = 0x%08x\n", val, reg);
+
+	/* check for write error */
+	if (imxdi->dsr & DSR_WEF) {
+		clear_write_error(imxdi);
+		rc = -EIO;
+	}
+	mutex_unlock(&imxdi->write_mutex);
+	return rc;
+}
+
+/*
+ * read the seconds portion of the current time from the dryice time counter
+ */
+static int dryice_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+	struct imxdi_dev *imxdi = dev_get_drvdata(dev);
+	unsigned long now;
+
+	now = __raw_readl(imxdi->ioaddr+DTCMR);
+	rtc_time_to_tm(now, tm);
+
+	return 0;
+}
+
+/*
+ * set the seconds portion of dryice time counter and clear the
+ * fractional part.
+ */
+static int dryice_rtc_set_mmss(struct device *dev, unsigned long secs)
+{
+	struct imxdi_dev *imxdi = dev_get_drvdata(dev);
+	int rc;
+
+	/* zero the fractional part first */
+	rc = di_write_wait(imxdi, 0, DTCLR);
+	if (rc == 0)
+		rc = di_write_wait(imxdi, secs, DTCMR);
+
+	return rc;
+}
+
+/*
+ * read the seconds portion of the alarm register.
+ * the fractional part of the alarm register is always zero.
+ */
+static int dryice_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+	struct imxdi_dev *imxdi = dev_get_drvdata(dev);
+	u32 dcamr;
+
+	dcamr = __raw_readl(imxdi->ioaddr+DCAMR);
+	rtc_time_to_tm(dcamr, &alarm->time);
+
+	/* alarm is enabled if the interrupt is enabled */
+	alarm->enabled = (__raw_readl(imxdi->ioaddr+DIER) & DIER_CAIE) != 0;
+
+	/* don't allow the DSR read to mess up DSR_WCF */
+	mutex_lock(&imxdi->write_mutex);
+
+	/* alarm is pending if the alarm flag is set */
+	alarm->pending = (__raw_readl(imxdi->ioaddr+DSR) & DSR_CAF) != 0;
+
+	mutex_unlock(&imxdi->write_mutex);
+
+	return 0;
+}
+
+/*
+ * set the seconds portion of dryice alarm register
+ */
+static int dryice_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+	struct imxdi_dev *imxdi = dev_get_drvdata(dev);
+	unsigned long now;
+	unsigned long alarm_time;
+	int rc;
+
+	rc = rtc_tm_to_time(&alarm->time, &alarm_time);
+	if (rc)
+		return rc;
+
+	/* don't allow setting alarm in the past */
+	now = __raw_readl(imxdi->ioaddr+DTCMR);
+	if (alarm_time < now)
+		return -EINVAL;
+
+	/* write the new alarm time */
+	rc = di_write_wait(imxdi, (u32)alarm_time, DCAMR);
+	if (rc)
+		return rc;
+
+	if (alarm->enabled)
+		di_int_enable(imxdi, DIER_CAIE);  /* enable alarm intr */
+	else
+		di_int_disable(imxdi, DIER_CAIE); /* disable alarm intr */
+
+	return 0;
+}
+
+static struct rtc_class_ops dryice_rtc_ops = {
+	.read_time = dryice_rtc_read_time,
+	.set_mmss = dryice_rtc_set_mmss,
+	.read_alarm = dryice_rtc_read_alarm,
+	.set_alarm = dryice_rtc_set_alarm,
+};
+
+/*
+ * dryice "normal" interrupt handler
+ */
+static irqreturn_t dryice_norm_irq(int irq, void *dev_id)
+{
+	struct imxdi_dev *imxdi = dev_id;
+	u32 dsr, dier;
+	irqreturn_t rc = IRQ_NONE;
+
+	dier = __raw_readl(imxdi->ioaddr+DIER);
+
+	/* handle write complete and write error cases */
+	if ((dier & DIER_WCIE)) {
+		/*If the write wait queue is empty then there is no pending
+		  operations. It means the interrupt is for DryIce -Security.
+		  IRQ must be returned as none.*/
+		if (list_empty_careful(&imxdi->write_wait.task_list))
+			return rc;
+
+		/* DSR_WCF clears itself on DSR read */
+		dsr = __raw_readl(imxdi->ioaddr+DSR);
+		if ((dsr & (DSR_WCF | DSR_WEF))) {
+			/* mask the interrupt */
+			di_int_disable(imxdi, DIER_WCIE);
+
+			/* save the dsr value for the wait queue */
+			imxdi->dsr |= dsr;
+
+			wake_up_interruptible(&imxdi->write_wait);
+			rc = IRQ_HANDLED;
+		}
+	}
+
+	/* handle the alarm case */
+	if ((dier & DIER_CAIE)) {
+		/* DSR_WCF clears itself on DSR read */
+		dsr = __raw_readl(imxdi->ioaddr+DSR);
+		if (dsr & DSR_CAF) {
+			/* mask the interrupt */
+			di_int_disable(imxdi, DIER_CAIE);
+
+			/* finish alarm in user context */
+			schedule_work(&imxdi->work);
+			rc = IRQ_HANDLED;
+		}
+	}
+	return rc;
+}
+
+/*
+ * post the alarm event from user context so it can sleep
+ * on the write completion.
+ */
+static void dryice_work(struct work_struct *work)
+{
+	struct imxdi_dev *imxdi = container_of(work,
+			struct imxdi_dev, work);
+
+	/* dismiss the interrupt (ignore error) */
+	di_write_wait(imxdi, DSR_CAF, DSR);
+
+	/*
+	 * pass the alarm event to the rtc framework. note that
+	 * rtc_update_irq expects to be called with interrupts off.
+	 */
+	local_irq_disable();
+	rtc_update_irq(imxdi->rtc, 1, RTC_AF | RTC_IRQF);
+	local_irq_enable();
+}
+
+/*
+ * probe for dryice rtc device
+ */
+static int dryice_rtc_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	struct imxdi_dev *imxdi = NULL;
+	int rc = 0;
+
+	imxdi = kzalloc(sizeof(*imxdi), GFP_KERNEL);
+	if (!imxdi)
+		return -ENOMEM;
+
+	imxdi->pdev = pdev;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENODEV;
+
+	if (!request_mem_region(res->start, resource_size(res),
+				pdev->name)) {
+		rc = -EBUSY;
+		goto err_free_mem;
+	}
+	imxdi->ioaddr = ioremap(res->start, resource_size(res));
+	if (imxdi->ioaddr == NULL) {
+		rc = -ENOMEM;
+		goto err_release_region;
+	}
+
+	if ((imxdi->irq = platform_get_irq(pdev, 0)) < 0) {
+		rc = imxdi->irq;
+		goto err;
+	}
+
+	init_waitqueue_head(&imxdi->write_wait);
+
+	INIT_WORK(&imxdi->work, dryice_work);
+
+	mutex_init(&imxdi->write_mutex);
+
+	imxdi->clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(imxdi->clk)) {
+		rc = PTR_ERR(imxdi->clk);
+		goto err;
+	}
+	clk_enable(imxdi->clk);
+
+	/*
+	 * Initialize dryice hardware
+	 */
+
+	/* put dryice into valid state */
+	if (__raw_readl(imxdi->ioaddr+DSR) & DSR_NVF)
+		rc = di_write_wait(imxdi, DSR_NVF | DSR_SVF, DSR);
+	if (rc)
+		goto err;
+
+	/* mask all interrupts */
+	__raw_writel(0, imxdi->ioaddr+DIER);
+
+	if (request_irq(imxdi->irq, dryice_norm_irq, IRQF_SHARED,
+				pdev->name, imxdi) < 0) {
+		dev_warn(&pdev->dev, "interrupt not available.\n");
+		imxdi->irq = -1;
+		goto err;
+	}
+
+	/* initialize alarm */
+	rc = di_write_wait(imxdi, DCAMR_UNSET, DCAMR);
+	if (rc == 0)
+		rc = di_write_wait(imxdi, 0, DCALR);
+	if (rc)
+		goto err;
+
+	/* clear alarm flag */
+	if (__raw_readl(imxdi->ioaddr+DSR) & DSR_CAF)
+		rc = di_write_wait(imxdi, DSR_CAF, DSR);
+	if (rc)
+		goto err;
+
+	/* the timer won't count if it has never been written to */
+	if (__raw_readl(imxdi->ioaddr+DTCMR) == 0)
+		rc = di_write_wait(imxdi, 0, DTCMR);
+	if (rc)
+		goto err;
+
+	/* start keeping time */
+	if (!(__raw_readl(imxdi->ioaddr+DCR) & DCR_TCE))
+		rc = di_write_wait(imxdi,
+				__raw_readl(imxdi->ioaddr+DCR) | DCR_TCE, DCR);
+	if (rc)
+		goto err;
+
+	imxdi->rtc = rtc_device_register(pdev->name, &pdev->dev,
+				  &dryice_rtc_ops, THIS_MODULE);
+	if (IS_ERR(imxdi->rtc)) {
+		rc = PTR_ERR(imxdi->rtc);
+		goto err;
+	}
+	platform_set_drvdata(pdev, imxdi);
+
+	return 0;
+err:
+	if (imxdi->rtc && !IS_ERR(imxdi->rtc))
+		rtc_device_unregister(imxdi->rtc);
+
+	if (imxdi->irq >= 0)
+		free_irq(imxdi->irq, imxdi);
+
+	if (imxdi->clk && !IS_ERR(imxdi->clk)) {
+		clk_disable(imxdi->clk);
+		clk_put(imxdi->clk);
+	}
+
+	if (imxdi->ioaddr)
+		iounmap(imxdi->ioaddr);
+
+err_release_region:
+	release_mem_region(res->start, resource_size(res));
+
+err_free_mem:
+	kfree(imxdi);
+
+	return rc;
+}
+
+static int __exit dryice_rtc_remove(struct platform_device *pdev)
+{
+	struct imxdi_dev *imxdi = platform_get_drvdata(pdev);
+	struct resource *mem;
+
+	flush_scheduled_work();
+
+	/* mask alarm interrupt */
+	di_int_disable(imxdi, DIER_CAIE);
+
+	if (imxdi->irq >= 0)
+		free_irq(imxdi->irq, imxdi);
+
+	rtc_device_unregister(imxdi->rtc);
+
+	clk_disable(imxdi->clk);
+	clk_put(imxdi->clk);
+
+	iounmap(imxdi->ioaddr);
+
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	release_mem_region(mem->start, resource_size(mem));
+
+	kfree(imxdi);
+
+	return 0;
+}
+
+static struct platform_driver dryice_rtc_driver = {
+	.driver = {
+		   .name = "imxdi_rtc",
+		   .owner = THIS_MODULE,
+		   },
+	.probe = dryice_rtc_probe,
+	.remove = __exit_p(dryice_rtc_remove),
+};
+
+static int __init dryice_rtc_init(void)
+{
+	return platform_driver_register(&dryice_rtc_driver);
+}
+
+static void __exit dryice_rtc_exit(void)
+{
+	platform_driver_unregister(&dryice_rtc_driver);
+}
+
+module_init(dryice_rtc_init);
+module_exit(dryice_rtc_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>");
+MODULE_DESCRIPTION("IMX DryIce Realtime Clock Driver (RTC)");
+MODULE_LICENSE("GPL");
-- 
1.6.5

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [rtc-linux] [PATCH v3] rtc: driver for the DryIce block found in  i.MX25 chips
  2010-02-02  6:47         ` [PATCH v3] " Baruch Siach
@ 2010-02-02  8:22           ` Alessandro Zummo
  2010-02-02  8:29             ` Baruch Siach
  2010-02-02  8:33             ` Baruch Siach
  0 siblings, 2 replies; 20+ messages in thread
From: Alessandro Zummo @ 2010-02-02  8:22 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue,  2 Feb 2010 08:47:04 +0200
Baruch Siach <baruch@tkos.co.il> wrote:

> 
> Changes v2 -> v3:
> 
> Add spaces around the '+' operator as suggested by Alessandro Zummo and Lothar 
> Wa?mann.
> 
> Address other comments of Alessandro Zummo:
> 
> 	* add implementation of the alarm_irq_enable rtc op
> 	* use devres in .probe
> 	* remove unneeded initializations

 I believe you sent the old version ;)

-- 

 Best regards,

 Alessandro Zummo,
  Tower Technologies - Torino, Italy

  http://www.towertech.it

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [rtc-linux] [PATCH v3] rtc: driver for the DryIce block found in  i.MX25 chips
  2010-02-02  8:22           ` [rtc-linux] " Alessandro Zummo
@ 2010-02-02  8:29             ` Baruch Siach
  2010-02-02  8:33             ` Baruch Siach
  1 sibling, 0 replies; 20+ messages in thread
From: Baruch Siach @ 2010-02-02  8:29 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Alessandro,

On Tue, Feb 02, 2010 at 09:22:20AM +0100, Alessandro Zummo wrote:
> On Tue,  2 Feb 2010 08:47:04 +0200
> Baruch Siach <baruch@tkos.co.il> wrote:
> > 
> > Changes v2 -> v3:
> > 
> > Add spaces around the '+' operator as suggested by Alessandro Zummo and Lothar 
> > Wa?mann.
> > 
> > Address other comments of Alessandro Zummo:
> > 
> > 	* add implementation of the alarm_irq_enable rtc op
> > 	* use devres in .probe
> > 	* remove unneeded initializations
> 
>  I believe you sent the old version ;)

Oops. Sending again.

baruch

-- 
                                                     ~. .~   Tk Open Systems
=}------------------------------------------------ooO--U--Ooo------------{=
   - baruch at tkos.co.il - tel: +972.2.679.5364, http://www.tkos.co.il -

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH v3] rtc: driver for the DryIce block found in i.MX25 chips
  2010-02-02  8:22           ` [rtc-linux] " Alessandro Zummo
  2010-02-02  8:29             ` Baruch Siach
@ 2010-02-02  8:33             ` Baruch Siach
  2010-03-09  7:41               ` Baruch Siach
  1 sibling, 1 reply; 20+ messages in thread
From: Baruch Siach @ 2010-02-02  8:33 UTC (permalink / raw)
  To: linux-arm-kernel

This driver is based on code from Freescale which accompanies their i.MX25 PDK
board, with some cleanup.

Signed-off-by: Baruch Siach <baruch@tkos.co.il>
---
 drivers/rtc/Kconfig     |   10 +
 drivers/rtc/Makefile    |    1 +
 drivers/rtc/rtc-imxdi.c |  520 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 531 insertions(+), 0 deletions(-)
 create mode 100644 drivers/rtc/rtc-imxdi.c

Changes v2 -> v3:

Add spaces around the '+' operator as suggested by Alessandro Zummo and Lothar 
Wa??mann.

Address other comments of Alessandro Zummo:

	* add implementation of the alarm_irq_enable rtc op
	* use devres in .probe
	* remove unneeded initializations

Changes v1 -> v2:

Address the comments of Alessandro Zummo:

	* remove the .ioctl implementation
	* implement .set_mmss instead of .set_time
	* reorder the .probe implementation
	* make sure not to have enabled interrupts when registering or 
	  unregistering the rtc device
	* add MODULE_AUTHOR() with my email

Address the comment of Lothar Wa??mann:

	* return PTR_ERR on clk_get failure

Slightly change MODULE_DESCRIPTION().
Use timeout which is independent of HZ for wait_event_interruptible_timeout().

diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 8167e9e..83bd602 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -527,6 +527,16 @@ config RTC_DRV_MSM6242
 	  This driver can also be built as a module. If so, the module
 	  will be called rtc-msm6242.
 
+config RTC_DRV_IMXDI
+	tristate "Freescale IMX DryIce Real Time Clock"
+	depends on ARCH_MX25
+	depends on RTC_CLASS
+	help
+	   Support for Freescale IMX DryIce RTC
+
+	   This driver can also be built as a module, if so, the module
+	   will be called "rtc-imxdi".
+
 config RTC_MXC
 	tristate "Freescale MXC Real Time Clock"
 	depends on ARCH_MXC
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index e5160fd..f2d3420 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -44,6 +44,7 @@ obj-$(CONFIG_RTC_DRV_EFI)	+= rtc-efi.o
 obj-$(CONFIG_RTC_DRV_EP93XX)	+= rtc-ep93xx.o
 obj-$(CONFIG_RTC_DRV_FM3130)	+= rtc-fm3130.o
 obj-$(CONFIG_RTC_DRV_GENERIC)	+= rtc-generic.o
+obj-$(CONFIG_RTC_DRV_IMXDI)	+= rtc-imxdi.o
 obj-$(CONFIG_RTC_DRV_ISL1208)	+= rtc-isl1208.o
 obj-$(CONFIG_RTC_DRV_M41T80)	+= rtc-m41t80.o
 obj-$(CONFIG_RTC_DRV_M41T94)	+= rtc-m41t94.o
diff --git a/drivers/rtc/rtc-imxdi.c b/drivers/rtc/rtc-imxdi.c
new file mode 100644
index 0000000..1f4820e
--- /dev/null
+++ b/drivers/rtc/rtc-imxdi.c
@@ -0,0 +1,520 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2010 Orex Computed Radiography
+ */
+
+/*
+ * 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
+ */
+
+/* based on rtc-mc13892.c */
+
+/*
+ * This driver uses the 47-bit 32 kHz counter in the Freescale DryIce block
+ * to implement a Linux RTC. Times and alarms are truncated to seconds.
+ * Since the RTC framework performs API locking via rtc->ops_lock the
+ * only simultaneous accesses we need to deal with is updating DryIce
+ * registers while servicing an alarm.
+ *
+ * Note that reading the DSR (DryIce Status Register) automatically clears
+ * the WCF (Write Complete Flag). All DryIce writes are synchronized to the
+ * LP (Low Power) domain and set the WCF upon completion. Writes to the
+ * DIER (DryIce Interrupt Enable Register) are the only exception. These
+ * occur at normal bus speeds and do not set WCF.  Periodic interrupts are
+ * not supported by the hardware.
+ */
+
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/workqueue.h>
+
+/* DryIce Register Definitions */
+
+#define DTCMR     0x00           /* Time Counter MSB Reg */
+#define DTCLR     0x04           /* Time Counter LSB Reg */
+
+#define DCAMR     0x08           /* Clock Alarm MSB Reg */
+#define DCALR     0x0c           /* Clock Alarm LSB Reg */
+#define DCAMR_UNSET  0xFFFFFFFF  /* doomsday - 1 sec */
+
+#define DCR       0x10           /* Control Reg */
+#define DCR_TCE   (1 << 3)       /* Time Counter Enable */
+
+#define DSR       0x14           /* Status Reg */
+#define DSR_WBF   (1 << 10)      /* Write Busy Flag */
+#define DSR_WNF   (1 << 9)       /* Write Next Flag */
+#define DSR_WCF   (1 << 8)       /* Write Complete Flag */
+#define DSR_WEF   (1 << 7)       /* Write Error Flag */
+#define DSR_CAF   (1 << 4)       /* Clock Alarm Flag */
+#define DSR_NVF   (1 << 1)       /* Non-Valid Flag */
+#define DSR_SVF   (1 << 0)       /* Security Violation Flag */
+
+#define DIER      0x18           /* Interrupt Enable Reg */
+#define DIER_WNIE (1 << 9)       /* Write Next Interrupt Enable */
+#define DIER_WCIE (1 << 8)       /* Write Complete Interrupt Enable */
+#define DIER_WEIE (1 << 7)       /* Write Error Interrupt Enable */
+#define DIER_CAIE (1 << 4)       /* Clock Alarm Interrupt Enable */
+
+/**
+ * struct imxdi_dev - private imxdi rtc data
+ * @pdev: pionter to platform dev
+ * @rtc: pointer to rtc struct
+ * @ioaddr: IO registers pointer
+ * @irq: dryice normal interrupt
+ * @clk: input reference clock
+ * @dsr: copy of the DSR register
+ * @irq_lock: interrupt enable register (DIER) lock
+ * @write_wait: registers write complete queue
+ * @write_mutex: serialize registers write
+ * @work: schedule alarm work
+ */
+struct imxdi_dev {
+	struct platform_device *pdev;
+	struct rtc_device *rtc;
+	void __iomem *ioaddr;
+	int irq;
+	struct clk *clk;
+	u32 dsr;
+	spinlock_t irq_lock;
+	wait_queue_head_t write_wait;
+	struct mutex write_mutex;
+	struct work_struct work;
+};
+
+/*
+ * enable a dryice interrupt
+ */
+static inline void di_int_enable(struct imxdi_dev *imxdi, u32 intr)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&imxdi->irq_lock, flags);
+	__raw_writel(__raw_readl(imxdi->ioaddr + DIER) | intr,
+			imxdi->ioaddr + DIER);
+	spin_unlock_irqrestore(&imxdi->irq_lock, flags);
+}
+
+/*
+ * disable a dryice interrupt
+ */
+static inline void di_int_disable(struct imxdi_dev *imxdi, u32 intr)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&imxdi->irq_lock, flags);
+	__raw_writel(__raw_readl(imxdi->ioaddr + DIER) & ~intr,
+			imxdi->ioaddr + DIER);
+	spin_unlock_irqrestore(&imxdi->irq_lock, flags);
+}
+
+/*
+ * This function attempts to clear the dryice write-error flag.
+ *
+ * A dryice write error is similar to a bus fault and should not occur in
+ * normal operation.  Clearing the flag requires another write, so the root
+ * cause of the problem may need to be fixed before the flag can be cleared.
+ */
+static void clear_write_error(struct imxdi_dev *imxdi)
+{
+	int cnt;
+
+	dev_warn(&imxdi->pdev->dev, "WARNING: Register write error!\n");
+
+	for (;;) {
+		/* clear the write error flag */
+		__raw_writel(DSR_WEF, imxdi->ioaddr + DSR);
+
+		/* wait for it to take effect */
+		for (cnt = 0; cnt < 100; cnt++) {
+			if ((__raw_readl(imxdi->ioaddr + DSR) & DSR_WEF) == 0)
+				return;
+			udelay(10);
+		}
+		dev_err(&imxdi->pdev->dev,
+			"ERROR: Cannot clear write-error flag!\n");
+	}
+}
+
+/*
+ * Write a dryice register and wait until it completes.
+ *
+ * This function uses interrupts to determine when the
+ * write has completed.
+ */
+static int di_write_wait(struct imxdi_dev *imxdi, u32 val, int reg)
+{
+	int ret;
+	int rc = 0;
+
+	/* serialize register writes */
+	mutex_lock(&imxdi->write_mutex);
+
+	/* enable the write-complete interrupt */
+	di_int_enable(imxdi, DIER_WCIE);
+
+	imxdi->dsr = 0;
+
+	/* do the register write */
+	__raw_writel(val, imxdi->ioaddr + reg);
+
+	/* wait for the write to finish */
+	ret = wait_event_interruptible_timeout(imxdi->write_wait,
+			imxdi->dsr & (DSR_WCF | DSR_WEF), msecs_to_jiffies(1));
+	if (ret == 0)
+		dev_warn(&imxdi->pdev->dev,
+				"Write-wait timeout "
+				"val = 0x%08x reg = 0x%08x\n", val, reg);
+
+	/* check for write error */
+	if (imxdi->dsr & DSR_WEF) {
+		clear_write_error(imxdi);
+		rc = -EIO;
+	}
+	mutex_unlock(&imxdi->write_mutex);
+	return rc;
+}
+
+/*
+ * read the seconds portion of the current time from the dryice time counter
+ */
+static int dryice_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+	struct imxdi_dev *imxdi = dev_get_drvdata(dev);
+	unsigned long now;
+
+	now = __raw_readl(imxdi->ioaddr + DTCMR);
+	rtc_time_to_tm(now, tm);
+
+	return 0;
+}
+
+/*
+ * set the seconds portion of dryice time counter and clear the
+ * fractional part.
+ */
+static int dryice_rtc_set_mmss(struct device *dev, unsigned long secs)
+{
+	struct imxdi_dev *imxdi = dev_get_drvdata(dev);
+	int rc;
+
+	/* zero the fractional part first */
+	rc = di_write_wait(imxdi, 0, DTCLR);
+	if (rc == 0)
+		rc = di_write_wait(imxdi, secs, DTCMR);
+
+	return rc;
+}
+
+static int dryice_rtc_alarm_irq_enable(struct device *dev,
+		unsigned int enabled)
+{
+	struct imxdi_dev *imxdi = dev_get_drvdata(dev);
+
+	if (enabled)
+		di_int_enable(imxdi, DIER_CAIE);
+	else
+		di_int_disable(imxdi, DIER_CAIE);
+
+	return 0;
+}
+
+/*
+ * read the seconds portion of the alarm register.
+ * the fractional part of the alarm register is always zero.
+ */
+static int dryice_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+	struct imxdi_dev *imxdi = dev_get_drvdata(dev);
+	u32 dcamr;
+
+	dcamr = __raw_readl(imxdi->ioaddr + DCAMR);
+	rtc_time_to_tm(dcamr, &alarm->time);
+
+	/* alarm is enabled if the interrupt is enabled */
+	alarm->enabled = (__raw_readl(imxdi->ioaddr + DIER) & DIER_CAIE) != 0;
+
+	/* don't allow the DSR read to mess up DSR_WCF */
+	mutex_lock(&imxdi->write_mutex);
+
+	/* alarm is pending if the alarm flag is set */
+	alarm->pending = (__raw_readl(imxdi->ioaddr + DSR) & DSR_CAF) != 0;
+
+	mutex_unlock(&imxdi->write_mutex);
+
+	return 0;
+}
+
+/*
+ * set the seconds portion of dryice alarm register
+ */
+static int dryice_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+	struct imxdi_dev *imxdi = dev_get_drvdata(dev);
+	unsigned long now;
+	unsigned long alarm_time;
+	int rc;
+
+	rc = rtc_tm_to_time(&alarm->time, &alarm_time);
+	if (rc)
+		return rc;
+
+	/* don't allow setting alarm in the past */
+	now = __raw_readl(imxdi->ioaddr + DTCMR);
+	if (alarm_time < now)
+		return -EINVAL;
+
+	/* write the new alarm time */
+	rc = di_write_wait(imxdi, (u32)alarm_time, DCAMR);
+	if (rc)
+		return rc;
+
+	if (alarm->enabled)
+		di_int_enable(imxdi, DIER_CAIE);  /* enable alarm intr */
+	else
+		di_int_disable(imxdi, DIER_CAIE); /* disable alarm intr */
+
+	return 0;
+}
+
+static struct rtc_class_ops dryice_rtc_ops = {
+	.read_time		= dryice_rtc_read_time,
+	.set_mmss		= dryice_rtc_set_mmss,
+	.alarm_irq_enable	= dryice_rtc_alarm_irq_enable,
+	.read_alarm		= dryice_rtc_read_alarm,
+	.set_alarm		= dryice_rtc_set_alarm,
+};
+
+/*
+ * dryice "normal" interrupt handler
+ */
+static irqreturn_t dryice_norm_irq(int irq, void *dev_id)
+{
+	struct imxdi_dev *imxdi = dev_id;
+	u32 dsr, dier;
+	irqreturn_t rc = IRQ_NONE;
+
+	dier = __raw_readl(imxdi->ioaddr + DIER);
+
+	/* handle write complete and write error cases */
+	if ((dier & DIER_WCIE)) {
+		/*If the write wait queue is empty then there is no pending
+		  operations. It means the interrupt is for DryIce -Security.
+		  IRQ must be returned as none.*/
+		if (list_empty_careful(&imxdi->write_wait.task_list))
+			return rc;
+
+		/* DSR_WCF clears itself on DSR read */
+		dsr = __raw_readl(imxdi->ioaddr + DSR);
+		if ((dsr & (DSR_WCF | DSR_WEF))) {
+			/* mask the interrupt */
+			di_int_disable(imxdi, DIER_WCIE);
+
+			/* save the dsr value for the wait queue */
+			imxdi->dsr |= dsr;
+
+			wake_up_interruptible(&imxdi->write_wait);
+			rc = IRQ_HANDLED;
+		}
+	}
+
+	/* handle the alarm case */
+	if ((dier & DIER_CAIE)) {
+		/* DSR_WCF clears itself on DSR read */
+		dsr = __raw_readl(imxdi->ioaddr + DSR);
+		if (dsr & DSR_CAF) {
+			/* mask the interrupt */
+			di_int_disable(imxdi, DIER_CAIE);
+
+			/* finish alarm in user context */
+			schedule_work(&imxdi->work);
+			rc = IRQ_HANDLED;
+		}
+	}
+	return rc;
+}
+
+/*
+ * post the alarm event from user context so it can sleep
+ * on the write completion.
+ */
+static void dryice_work(struct work_struct *work)
+{
+	struct imxdi_dev *imxdi = container_of(work,
+			struct imxdi_dev, work);
+
+	/* dismiss the interrupt (ignore error) */
+	di_write_wait(imxdi, DSR_CAF, DSR);
+
+	/*
+	 * pass the alarm event to the rtc framework. note that
+	 * rtc_update_irq expects to be called with interrupts off.
+	 */
+	local_irq_disable();
+	rtc_update_irq(imxdi->rtc, 1, RTC_AF | RTC_IRQF);
+	local_irq_enable();
+}
+
+/*
+ * probe for dryice rtc device
+ */
+static int dryice_rtc_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	struct imxdi_dev *imxdi;
+	int rc;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENODEV;
+
+	imxdi = devm_kzalloc(&pdev->dev, sizeof(*imxdi), GFP_KERNEL);
+	if (!imxdi)
+		return -ENOMEM;
+
+	imxdi->pdev = pdev;
+
+	if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res),
+				pdev->name))
+		return -EBUSY;
+
+	imxdi->ioaddr = devm_ioremap(&pdev->dev, res->start,
+			resource_size(res));
+	if (imxdi->ioaddr == NULL)
+		return -ENOMEM;
+
+	imxdi->irq = platform_get_irq(pdev, 0);
+	if (imxdi->irq < 0)
+		return imxdi->irq;
+
+	init_waitqueue_head(&imxdi->write_wait);
+
+	INIT_WORK(&imxdi->work, dryice_work);
+
+	mutex_init(&imxdi->write_mutex);
+
+	imxdi->clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(imxdi->clk))
+		return PTR_ERR(imxdi->clk);
+	clk_enable(imxdi->clk);
+
+	/*
+	 * Initialize dryice hardware
+	 */
+
+	/* put dryice into valid state */
+	if (__raw_readl(imxdi->ioaddr + DSR) & DSR_NVF) {
+		rc = di_write_wait(imxdi, DSR_NVF | DSR_SVF, DSR);
+		if (rc)
+			goto err;
+	}
+
+	/* mask all interrupts */
+	__raw_writel(0, imxdi->ioaddr + DIER);
+
+	rc = devm_request_irq(&pdev->dev, imxdi->irq, dryice_norm_irq,
+			IRQF_SHARED, pdev->name, imxdi);
+	if (rc) {
+		dev_warn(&pdev->dev, "interrupt not available.\n");
+		goto err;
+	}
+
+	/* initialize alarm */
+	rc = di_write_wait(imxdi, DCAMR_UNSET, DCAMR);
+	if (rc)
+		goto err;
+	rc = di_write_wait(imxdi, 0, DCALR);
+	if (rc)
+		goto err;
+
+	/* clear alarm flag */
+	if (__raw_readl(imxdi->ioaddr + DSR) & DSR_CAF) {
+		rc = di_write_wait(imxdi, DSR_CAF, DSR);
+		if (rc)
+			goto err;
+	}
+
+	/* the timer won't count if it has never been written to */
+	if (__raw_readl(imxdi->ioaddr + DTCMR) == 0) {
+		rc = di_write_wait(imxdi, 0, DTCMR);
+		if (rc)
+			goto err;
+	}
+
+	/* start keeping time */
+	if (!(__raw_readl(imxdi->ioaddr + DCR) & DCR_TCE)) {
+		rc = di_write_wait(imxdi,
+				__raw_readl(imxdi->ioaddr + DCR) | DCR_TCE,
+				DCR);
+		if (rc)
+			goto err;
+	}
+
+	imxdi->rtc = rtc_device_register(pdev->name, &pdev->dev,
+				  &dryice_rtc_ops, THIS_MODULE);
+	if (IS_ERR(imxdi->rtc)) {
+		rc = PTR_ERR(imxdi->rtc);
+		goto err;
+	}
+	platform_set_drvdata(pdev, imxdi);
+
+	return 0;
+
+err:
+	clk_disable(imxdi->clk);
+	clk_put(imxdi->clk);
+
+	return rc;
+}
+
+static int __devexit dryice_rtc_remove(struct platform_device *pdev)
+{
+	struct imxdi_dev *imxdi = platform_get_drvdata(pdev);
+
+	flush_scheduled_work();
+
+	/* mask all interrupts */
+	__raw_writel(0, imxdi->ioaddr + DIER);
+
+	rtc_device_unregister(imxdi->rtc);
+
+	clk_disable(imxdi->clk);
+	clk_put(imxdi->clk);
+
+	return 0;
+}
+
+static struct platform_driver dryice_rtc_driver = {
+	.driver = {
+		   .name = "imxdi_rtc",
+		   .owner = THIS_MODULE,
+		   },
+	.probe = dryice_rtc_probe,
+	.remove = __devexit_p(dryice_rtc_remove),
+};
+
+static int __init dryice_rtc_init(void)
+{
+	return platform_driver_register(&dryice_rtc_driver);
+}
+
+static void __exit dryice_rtc_exit(void)
+{
+	platform_driver_unregister(&dryice_rtc_driver);
+}
+
+module_init(dryice_rtc_init);
+module_exit(dryice_rtc_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>");
+MODULE_DESCRIPTION("IMX DryIce Realtime Clock Driver (RTC)");
+MODULE_LICENSE("GPL");
-- 
1.6.5

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v3] rtc: driver for the DryIce block found in i.MX25 chips
  2010-02-02  8:33             ` Baruch Siach
@ 2010-03-09  7:41               ` Baruch Siach
  2010-03-09 10:21                 ` [rtc-linux] " Alessandro Zummo
  0 siblings, 1 reply; 20+ messages in thread
From: Baruch Siach @ 2010-03-09  7:41 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Alessandro,

On Tue, Feb 02, 2010 at 10:33:03AM +0200, Baruch Siach wrote:
> This driver is based on code from Freescale which accompanies their i.MX25 PDK
> board, with some cleanup.
> 
> Signed-off-by: Baruch Siach <baruch@tkos.co.il>
> ---
>  drivers/rtc/Kconfig     |   10 +
>  drivers/rtc/Makefile    |    1 +
>  drivers/rtc/rtc-imxdi.c |  520 +++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 531 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/rtc/rtc-imxdi.c

Ping?

Since .34-rc1 has already been released, I guess this is not going to get into 
2.6.34. Are there any remaining issues to resolve in order to merge this 
upstream?

baruch

-- 
                                                     ~. .~   Tk Open Systems
=}------------------------------------------------ooO--U--Ooo------------{=
   - baruch at tkos.co.il - tel: +972.2.679.5364, http://www.tkos.co.il -

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [rtc-linux] Re: [PATCH v3] rtc: driver for the DryIce block found in  i.MX25 chips
  2010-03-09  7:41               ` Baruch Siach
@ 2010-03-09 10:21                 ` Alessandro Zummo
  2010-03-09 10:34                   ` Baruch Siach
  2010-05-04  6:56                   ` Baruch Siach
  0 siblings, 2 replies; 20+ messages in thread
From: Alessandro Zummo @ 2010-03-09 10:21 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, 9 Mar 2010 09:41:14 +0200
Baruch Siach <baruch@tkos.co.il> wrote:

> Ping?
> 
> Since .34-rc1 has already been released, I guess this is not going to get into 
> 2.6.34. Are there any remaining issues to resolve in order to merge this 
> upstream?

 I just have to find some time to review it. don't worry, since is a completely
 new driver it could go upstream at any time.

-- 

 Best regards,

 Alessandro Zummo,
  Tower Technologies - Torino, Italy

  http://www.towertech.it

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [rtc-linux] Re: [PATCH v3] rtc: driver for the DryIce block found in  i.MX25 chips
  2010-03-09 10:21                 ` [rtc-linux] " Alessandro Zummo
@ 2010-03-09 10:34                   ` Baruch Siach
  2010-05-04  6:56                   ` Baruch Siach
  1 sibling, 0 replies; 20+ messages in thread
From: Baruch Siach @ 2010-03-09 10:34 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Alessandro,

On Tue, Mar 09, 2010 at 11:21:46AM +0100, Alessandro Zummo wrote:
> On Tue, 9 Mar 2010 09:41:14 +0200 Baruch Siach <baruch@tkos.co.il> wrote:
> > Ping?
> > 
> > Since .34-rc1 has already been released, I guess this is not going to get into 
> > 2.6.34. Are there any remaining issues to resolve in order to merge this 
> > upstream?
> 
>  I just have to find some time to review it. don't worry, since is a completely
>  new driver it could go upstream at any time.

Thanks. Please note another patch against this one fixing an irq related bug:

http://groups.google.com/group/rtc-linux/browse_thread/thread/2847baa17905dfc3#

baruch

-- 
                                                     ~. .~   Tk Open Systems
=}------------------------------------------------ooO--U--Ooo------------{=
   - baruch at tkos.co.il - tel: +972.2.679.5364, http://www.tkos.co.il -

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [rtc-linux] Re: [PATCH v3] rtc: driver for the DryIce block found in  i.MX25 chips
  2010-03-09 10:21                 ` [rtc-linux] " Alessandro Zummo
  2010-03-09 10:34                   ` Baruch Siach
@ 2010-05-04  6:56                   ` Baruch Siach
  1 sibling, 0 replies; 20+ messages in thread
From: Baruch Siach @ 2010-05-04  6:56 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Alessandro,

On Tue, Mar 09, 2010 at 11:21:46AM +0100, Alessandro Zummo wrote:
> On Tue, 9 Mar 2010 09:41:14 +0200
> Baruch Siach <baruch@tkos.co.il> wrote:
> > Ping?
> > 
> > Since .34-rc1 has already been released, I guess this is not going to get into 
> > 2.6.34. Are there any remaining issues to resolve in order to merge this 
> > upstream?
> 
>  I just have to find some time to review it. don't worry, since is a completely
>  new driver it could go upstream at any time.

Any news about this driver?

baruch

-- 
                                                     ~. .~   Tk Open Systems
=}------------------------------------------------ooO--U--Ooo------------{=
   - baruch at tkos.co.il - tel: +972.2.679.5364, http://www.tkos.co.il -

^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2010-05-04  6:56 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-01-27 13:00 [PATCH 0/3] RTC driver for i.MX25 Baruch Siach
2010-01-27 13:00 ` [PATCH 1/3] rtc: driver for the DryIce block found in i.MX25 chips Baruch Siach
2010-01-27 13:30   ` Lothar Waßmann
2010-01-27 21:46     ` Russell King - ARM Linux
2010-01-29 10:10   ` [rtc-linux] " Alessandro Zummo
2010-02-01  7:36     ` [PATCH] " Baruch Siach
2010-02-01  8:08       ` Lothar Waßmann
2010-02-01  8:28         ` Baruch Siach
2010-02-01  9:20       ` Alessandro Zummo
2010-02-02  6:47         ` [PATCH v3] " Baruch Siach
2010-02-02  8:22           ` [rtc-linux] " Alessandro Zummo
2010-02-02  8:29             ` Baruch Siach
2010-02-02  8:33             ` Baruch Siach
2010-03-09  7:41               ` Baruch Siach
2010-03-09 10:21                 ` [rtc-linux] " Alessandro Zummo
2010-03-09 10:34                   ` Baruch Siach
2010-05-04  6:56                   ` Baruch Siach
2010-01-27 13:00 ` [PATCH 2/3] mx25: add support for the DryIce rtc Baruch Siach
2010-01-27 13:00 ` [PATCH 3/3] mx25pdk: platform code for the DryIce RTC module Baruch Siach
2010-01-29  9:45 ` [PATCH 0/3] RTC driver for i.MX25 Sascha Hauer

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.