Add support for the Coldfire m5441x on-chip rtc.

Signed-off-by: Stven King <sfk...@fdwdc.com>
---
 drivers/rtc/Kconfig      |   10 ++
 drivers/rtc/Makefile     |    1 +
 drivers/rtc/rtc-m5441x.c |  363 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 374 insertions(+)

diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 08cbdb9..da2b3c4 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -1087,4 +1087,14 @@ config RTC_DRV_MXC
           This driver can also be built as a module, if so, the module
           will be called "rtc-mxc".
 
+config RTC_DRV_M5441x
+       tristate "Freescale Coldfire M5441x RTC support"
+       depends on M5441x
+       help
+         This enables support for the RTC on the Freescale Coldfire 5441x
+         (54410/54415/54416/54417/54418).
+
+         This driver can also be built as a module.  If so, the module
+         will be called rtc-m5441x.
+
 endif # RTC_CLASS
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 2973921..a9ded2c 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -61,6 +61,7 @@ obj-$(CONFIG_RTC_DRV_M41T94)  += rtc-m41t94.o
 obj-$(CONFIG_RTC_DRV_M48T35)   += rtc-m48t35.o
 obj-$(CONFIG_RTC_DRV_M48T59)   += rtc-m48t59.o
 obj-$(CONFIG_RTC_DRV_M48T86)   += rtc-m48t86.o
+obj-$(CONFIG_RTC_DRV_M5441x)   += rtc-m5441x.o
 obj-$(CONFIG_RTC_DRV_MXC)      += rtc-mxc.o
 obj-$(CONFIG_RTC_DRV_MAX6900)  += rtc-max6900.o
 obj-$(CONFIG_RTC_DRV_MAX8925)  += rtc-max8925.o
diff --git a/drivers/rtc/rtc-m5441x.c b/drivers/rtc/rtc-m5441x.c
new file mode 100644
index 0000000..ba677f3
--- /dev/null
+++ b/drivers/rtc/rtc-m5441x.c
@@ -0,0 +1,363 @@
+/*
+ * RTC driver for the Freescale Coldfire 5441x SoCs.
+ *
+ * Copyright(C) 2012, Steven King <sfk...@fdwdc.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/rtc.h>
+
+static void __iomem *m5441x_rtc_base;
+static int m5441x_rtc_irq;
+static struct clk *m5441x_rtc_clk;
+
+#define M5441X_RTC_YEARMON     (m5441x_rtc_base + 0x00)
+#define M5441X_RTC_DAYS                (m5441x_rtc_base + 0x02)
+#define M5441X_RTC_HOURMIN     (m5441x_rtc_base + 0x04)
+#define M5441X_RTC_SECONDS     (m5441x_rtc_base + 0x06)
+#define M5441X_RTC_ALM_YRMON   (m5441x_rtc_base + 0x08)
+#define M5441X_RTC_ALM_DAYS    (m5441x_rtc_base + 0x0a)
+#define M5441X_RTC_ALM_HM      (m5441x_rtc_base + 0x0c)
+#define M5441X_RTC_ALM_SEC     (m5441x_rtc_base + 0x0e)
+#define M5441X_RTC_CR          (m5441x_rtc_base + 0x10)
+#define M5441X_RTC_CR_AM0      (0 << 2)        /* secs/mins/hrs */
+#define M5441X_RTC_CR_AM1      (1 << 2)        /* secs/mins/days */
+#define M5441X_RTC_CR_AM2      (2 << 2)        /* secs/mins/days/mons */
+#define M5441X_RTC_CR_AM3      (3 << 2)        /* alarm match all */
+#define M5441X_RTC_CR_DTDEN    (1 << 6)        /* DST enable */
+#define M5441X_RTC_CR_BCDEN    (1 << 7)        /* BCD enable */
+#define M5441X_RTC_CR_SWR      (1 << 8)        /* software reset */
+#define M5441X_RTC_SR          (m5441x_rtc_base + 0x12)
+#define M5441X_RTC_SR_INVAL    (1 << 0)        /* time invalid */
+#define M5441X_RTC_SR_CDON     (1 << 1)        /* Compensation done */
+#define M5441X_RTC_SR_BERR     (1 << 2)        /* Bus err */
+#define M5441X_RTC_SR_WPE      (1 << 4)        /* write protect enabled */
+#define M5441X_RTC_SR_PORB     (1 << 6)        /* power on reset boot */
+#define M5441X_RTC_ISR         (m5441x_rtc_base + 0x14)
+#define M5441X_RTC_ISR_STW     (1 << 1)        /* Stop watch */
+#define M5441X_RTC_ISR_ALM     (1 << 2)        /* Alarm */
+#define M5441X_RTC_ISR_DAY     (1 << 3)        /* day counter inc */
+#define M5441X_RTC_ISR_HR      (1 << 4)        /* hour counter inc */
+#define M5441X_RTC_ISR_MIN     (1 << 5)        /* minute counter inc */
+#define M5441X_RTC_ISR_1HZ     (1 << 6)        /* second counter inc */
+#define M5441X_RTC_ISR_2HZ     (1 << 7)        /* 2Hz counter inc */
+#define M5441X_RTC_IER         (m5441x_rtc_base + 0x16)
+#define M5441X_RTC_IER_STW     (1 << 1)        /* Stop watch */
+#define M5441X_RTC_IER_ALM     (1 << 2)        /* Alarm */
+#define M5441X_RTC_IER_DAY     (1 << 3)        /* day counter inc */
+#define M5441X_RTC_IER_HR      (1 << 4)        /* hour counter inc */
+#define M5441X_RTC_IER_MIN     (1 << 5)        /* minute counter inc */
+#define M5441X_RTC_IER_1HZ     (1 << 6)        /* second counter inc */
+#define M5441X_RTC_IER_2HZ     (1 << 7)        /* 2Hz counter inc */
+#define M5441X_RTC_COUNT_DN    (m5441x_rtc_base + 0x18)
+#define M5441X_RTC_CFG_DATA    (m5441x_rtc_base + 0x20)
+#define M5441X_RTC_DST_HOUR    (m5441x_rtc_base + 0x22)
+#define M5441X_RTC_DST_MON     (m5441x_rtc_base + 0x24)
+#define M5441X_RTC_DST_DAY     (m5441x_rtc_base + 0x26)
+#define M5441X_RTC_RTC_COMPEN  (m5441x_rtc_base + 0x28)
+#define M5441X_RTC_UP_CNTRH    (m5441x_rtc_base + 0x32)
+#define M5441X_RTC_UP_CNTRL    (m5441x_rtc_base + 0x24)
+#define M5441X_RTC_Standy_RAM  (m5441x_rtc_base + 0x40)
+
+static int m5441x_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+       u16 r[4];
+
+       do {
+               r[0] = __raw_readw(M5441X_RTC_SECONDS);
+               r[1] = __raw_readw(M5441X_RTC_HOURMIN);
+               r[2] = __raw_readw(M5441X_RTC_DAYS);
+               r[3] = __raw_readw(M5441X_RTC_YEARMON);
+       } while (r[0] != __raw_readw(M5441X_RTC_SECONDS));
+
+       tm->tm_sec = r[0];
+       tm->tm_min = r[1] & 0xff;
+       tm->tm_hour = r[1] >> 8;
+       tm->tm_mday = (r[2] & 0xff) - 1;
+       tm->tm_wday = r[2] >> 8;
+       tm->tm_mon = (r[3] & 0xff) - 1;
+       tm->tm_year = (((s16)r[3]) >> 8) + 212;
+
+       return rtc_valid_tm(tm);
+}
+
+static int m5441x_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+       u16 r[4];
+
+       if ((tm->tm_year < 84) || (tm->tm_year > 339))
+               return -EINVAL; /* 1984 <-> 2239 */
+
+       r[0] = tm->tm_sec;
+       r[1] = (tm->tm_hour << 8) | tm->tm_min;
+       r[2] = (tm->tm_wday << 8) | (tm->tm_mday + 1);
+       r[3] = ((tm->tm_year - 212) << 8) | (tm->tm_mon + 1);
+
+       /* disable write protect */
+       __raw_writew(0x0, M5441X_RTC_CR);
+       __raw_writew(0x1, M5441X_RTC_CR);
+       __raw_writew(0x3, M5441X_RTC_CR);
+       __raw_writew(0x2, M5441X_RTC_CR);
+
+       __raw_writew(r[0], M5441X_RTC_SECONDS);
+       __raw_writew(r[1], M5441X_RTC_HOURMIN);
+       __raw_writew(r[2], M5441X_RTC_DAYS);
+       __raw_writew(r[3], M5441X_RTC_YEARMON);
+
+       /* enable write protect */
+       __raw_writew(0x2, M5441X_RTC_CR);
+
+       return 0;
+}
+
+static int m5441x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+       u16 r[4];
+
+       r[0] = __raw_readw(M5441X_RTC_ALM_SEC);
+       r[1] = __raw_readw(M5441X_RTC_ALM_HM);
+       r[2] = __raw_readw(M5441X_RTC_ALM_DAYS);
+       r[3] = __raw_readw(M5441X_RTC_ALM_YRMON);
+
+       alm->time.tm_sec = r[0] & 0xff;
+       alm->time.tm_min = r[1] & 0xff;
+       alm->time.tm_hour = r[1] >> 8;
+       alm->time.tm_mday = (r[2] & 0xff) - 1;
+       alm->time.tm_mon = (r[3] & 0xff) - 1;
+       alm->time.tm_year = (((s16)r[3]) >> 8) + 212;
+
+       alm->enabled = !!(__raw_readw(M5441X_RTC_IER) & M5441X_RTC_IER_ALM);
+
+       return 0;
+}
+
+static int m5441x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+       u16 r[4];
+
+       if ((alm->time.tm_year < 84) || (alm->time.tm_year > 339))
+               return -EINVAL;
+
+       r[0] = alm->time.tm_sec;
+       r[1] = (alm->time.tm_hour << 8) | alm->time.tm_min;
+       r[2] = alm->time.tm_mday + 1;
+       r[3] = ((alm->time.tm_year - 212) << 8) | (alm->time.tm_mon + 1);
+
+       local_irq_disable();
+
+       /* disable write protect */
+       __raw_writew(0x0, M5441X_RTC_CR);
+       __raw_writew(0x1, M5441X_RTC_CR);
+       __raw_writew(0x3, M5441X_RTC_CR);
+       __raw_writew(0x2, M5441X_RTC_CR);
+
+       __raw_writew(r[0], M5441X_RTC_ALM_SEC);
+       __raw_writew(r[1], M5441X_RTC_ALM_HM);
+       __raw_writew(r[2], M5441X_RTC_ALM_DAYS);
+       __raw_writew(r[3], M5441X_RTC_ALM_YRMON);
+
+       r[0] = __raw_readw(M5441X_RTC_IER);
+       if (alm->enabled)
+               r[0] |= M5441X_RTC_IER_ALM;
+       else
+               r[0] &= ~M5441X_RTC_IER_ALM;
+       __raw_writew(r[0], M5441X_RTC_IER);
+
+       /* enable write protect */
+       __raw_writew(0x2, M5441X_RTC_CR);
+
+       local_irq_enable();
+
+       return 0;
+}
+
+static int m5441x_rtc_alarm_irq_enable(struct device *dev, unsigned int enable)
+{
+       u16 r;
+
+       local_irq_disable();
+
+       /* disable write protect */
+       __raw_writew(0x0, M5441X_RTC_CR);
+       __raw_writew(0x1, M5441X_RTC_CR);
+       __raw_writew(0x3, M5441X_RTC_CR);
+       __raw_writew(0x2, M5441X_RTC_CR);
+
+       r = __raw_readw(M5441X_RTC_IER);
+       if (enable)
+               r |= M5441X_RTC_IER_ALM;
+       else
+               r &= ~M5441X_RTC_IER_ALM;
+       __raw_writew(r, M5441X_RTC_IER);
+
+       /* enable write protect */
+       __raw_writew(0x2, M5441X_RTC_CR);
+
+       local_irq_enable();
+
+       return 0;
+}
+
+static struct rtc_class_ops m5441x_rtc_ops = {
+       .read_time              = m5441x_rtc_read_time,
+       .set_time               = m5441x_rtc_set_time,
+       .read_alarm             = m5441x_rtc_read_alarm,
+       .set_alarm              = m5441x_rtc_set_alarm,
+       .alarm_irq_enable       = m5441x_rtc_alarm_irq_enable,
+};
+
+static irqreturn_t m5441x_rtc_irq_handler(int irq, void *rtc)
+{
+       unsigned long events = 0;
+       u16 r;
+
+       /* disable write protect */
+       __raw_writew(0x0, M5441X_RTC_CR);
+       __raw_writew(0x1, M5441X_RTC_CR);
+       __raw_writew(0x3, M5441X_RTC_CR);
+       __raw_writew(0x2, M5441X_RTC_CR);
+
+       while ((r = __raw_readw(M5441X_RTC_ISR)) != 0) {
+               if (r & M5441X_RTC_ISR_ALM)
+                       events |= RTC_IRQF | RTC_AF;
+               if (r & M5441X_RTC_ISR_1HZ)
+                       events |= RTC_IRQF | RTC_UF;
+               __raw_writew(r, M5441X_RTC_ISR);
+       }
+
+       /* enable write protect */
+       __raw_writew(0x2, M5441X_RTC_CR);
+
+       if (events)
+               rtc_update_irq(rtc, 1, events);
+       return IRQ_HANDLED;
+}
+
+static int __devinit m5441x_rtc_probe(struct platform_device *pdev)
+{
+       struct rtc_device *m5441x_rtc;
+       struct resource *res;
+       int status;
+
+       m5441x_rtc_irq = platform_get_irq(pdev, 0);
+       if (m5441x_rtc_irq < 0) {
+               dev_dbg(&pdev->dev, "platform_get_irq failed\n");
+               return -ENOENT;
+       }
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               dev_dbg(&pdev->dev, "platform_get_resource failed\n");
+               return -ENOENT;
+       }
+       if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
+               dev_dbg(&pdev->dev, "request_mem_region failed\n");
+               return -EBUSY;
+       }
+
+       m5441x_rtc_base = ioremap(res->start, resource_size(res));
+       if (!m5441x_rtc_base) {
+               dev_dbg(&pdev->dev, "ioremap failed\n");
+               status = -ENXIO;
+               goto fail0;
+       }
+
+       m5441x_rtc_clk = clk_get(&pdev->dev, NULL);
+       if (IS_ERR(m5441x_rtc_clk)) {
+               dev_dbg(&pdev->dev, "clk_get failed\n");
+               status = PTR_ERR(m5441x_rtc_clk);
+               goto fail1;
+       }
+
+       m5441x_rtc = rtc_device_register("mcfrtc", &pdev->dev, &m5441x_rtc_ops,
+                       THIS_MODULE);
+       if (IS_ERR(m5441x_rtc)) {
+               dev_dbg(&pdev->dev, "rtc_device_register failed\n");
+               status = PTR_ERR(m5441x_rtc);
+               goto fail2;
+       }
+
+       platform_set_drvdata(pdev, m5441x_rtc);
+
+       if (__raw_readw(M5441X_RTC_SR) & M5441X_RTC_SR_PORB)
+               dev_info(&pdev->dev, "power lost? RTC time maybe invalid\n");
+
+       /* disable write protect */
+       __raw_writew(0x0, M5441X_RTC_CR);
+       __raw_writew(0x1, M5441X_RTC_CR);
+       __raw_writew(0x3, M5441X_RTC_CR);
+       __raw_writew(0x2, M5441X_RTC_CR);
+       /* clear any previous alarms */
+       __raw_writew(M5441X_RTC_CR_SWR, M5441X_RTC_CR);
+       /* alarm match on seconds/mmins/days/months/year */
+       __raw_writew(M5441X_RTC_CR_AM3, M5441X_RTC_CR);
+       /* 1Hz alarm */
+       __raw_writew(M5441X_RTC_IER_1HZ, M5441X_RTC_IER);
+       /* enable write protect */
+       __raw_writew(0x2, M5441X_RTC_CR);
+
+       status = request_irq(m5441x_rtc_irq, m5441x_rtc_irq_handler, 0,
+                       pdev->name, m5441x_rtc);
+       if (status) {
+               dev_dbg(&pdev->dev, "request_irq failed\n");
+               goto fail3;
+       }
+       return 0;
+
+fail3:
+       platform_set_drvdata(pdev, NULL);
+       rtc_device_unregister(m5441x_rtc);
+fail2:
+       clk_disable(m5441x_rtc_clk);
+       clk_put(m5441x_rtc_clk);
+fail1:
+       iounmap(m5441x_rtc_base);
+fail0:
+       release_mem_region(res->start, resource_size(res));
+       dev_dbg(&pdev->dev, "probe failed\n");
+
+       return status;
+}
+
+static int __devexit m5441x_rtc_remove(struct platform_device *pdev)
+{
+       struct rtc_device *m5441x_rtc = platform_get_drvdata(pdev);
+       struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+       free_irq(m5441x_rtc_irq, m5441x_rtc);
+       platform_set_drvdata(pdev, NULL);
+       rtc_device_unregister(m5441x_rtc);
+       clk_disable(m5441x_rtc_clk);
+       clk_put(m5441x_rtc_clk);
+       iounmap(m5441x_rtc_base);
+       release_mem_region(res->start, resource_size(res));
+
+       return 0;
+}
+
+static struct platform_driver m5441x_rtc_driver = {
+       .driver.name    = "mcfrtc",
+       .driver.owner   = THIS_MODULE,
+       .remove         = __devexit_p(m5441x_rtc_remove),
+};
+
+static int __init m5441x_rtc_init(void)
+{
+       return platform_driver_probe(&m5441x_rtc_driver, m5441x_rtc_probe);
+}
+module_init(m5441x_rtc_init);
+
+static void __exit m5441x_rtc_exit(void)
+{
+       platform_driver_unregister(&m5441x_rtc_driver);
+}
+module_exit(m5441x_rtc_exit);
+
+MODULE_AUTHOR("Steven King <sfk...@fdwdc.com>");
+MODULE_DESCRIPTION("M5441x RTC driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:rtc");


_______________________________________________
uClinux-dev mailing list
uClinux-dev@uclinux.org
http://mailman.uclinux.org/mailman/listinfo/uclinux-dev
This message was resent by uclinux-dev@uclinux.org
To unsubscribe see:
http://mailman.uclinux.org/mailman/options/uclinux-dev

Reply via email to