From: Sandeep Paulraj <[email protected]>

Patch adds support for the RTC Driver in DM365.
Apart from receiving comments on the driver itself
I would also like comments on where i am registering the
RTC Driver for DM365. At present i register the RTC
device in SOC specific file(dm365.c).
RTC INTMUX should most probably be done in some other way
than what i am doing at the moment.
I Added this just before registering the RTC.

Did some basic testing using this driver. "hwclock"
command works. Also transitions between months/years/leap years
has been verified

Signed-off-by: Sandeep Paulraj <[email protected]>
---
 arch/arm/mach-davinci/dm365.c |   23 ++
 drivers/rtc/Kconfig           |    6 +
 drivers/rtc/Makefile          |    1 +
 drivers/rtc/rtc-dm365.c       |  671 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 701 insertions(+), 0 deletions(-)
 create mode 100644 drivers/rtc/rtc-dm365.c

diff --git a/arch/arm/mach-davinci/dm365.c b/arch/arm/mach-davinci/dm365.c
index 4d430a6..bb85129 100644
--- a/arch/arm/mach-davinci/dm365.c
+++ b/arch/arm/mach-davinci/dm365.c
@@ -37,6 +37,7 @@
 #include "mux.h"
 
 #define DM365_REF_FREQ         24000000        /* 24 MHz on the DM365 EVM */
+#define DM365_RTC_BASE         0x01c69000
 
 static struct pll_data pll1_data = {
        .num            = 1,
@@ -593,6 +594,25 @@ static struct platform_device dm365_emac_device = {
        .resource       = dm365_emac_resources,
 };
 
+static struct resource dm365_rtc_resources[] = {
+       {
+               .start = DM365_RTC_BASE,
+               .end = DM365_RTC_BASE + SZ_1K - 1,
+               .flags = IORESOURCE_MEM,
+       },
+       {
+               .start = 29,
+               .flags = IORESOURCE_IRQ,
+       },
+};
+
+static struct platform_device dm365_rtc_device = {
+       .name = "rtc_davinci_dm365",
+       .id = 0,
+       .num_resources = ARRAY_SIZE(dm365_rtc_resources),
+       .resource = dm365_rtc_resources,
+};
+
 static u8 dm365_default_priorities[DAVINCI_N_AINTC_IRQ] = {
        [IRQ_VDINT0]                    = 2,
        [IRQ_VDINT1]                    = 6,
@@ -847,6 +867,9 @@ static int __init dm365_init_devices(void)
        platform_device_register(&dm365_edma_device);
        platform_device_register(&dm365_emac_device);
 
+       davinci_cfg_reg(DM365_INT_PRTCSS);
+       platform_device_register(&dm365_rtc_device);
+
        return 0;
 }
 postcore_initcall(dm365_init_devices);
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index a3e07dd..70279ee 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -257,6 +257,12 @@ config RTC_DRV_DM355EVM
        help
          Supports the RTC firmware in the MSP430 on the DM355 EVM.
 
+config RTC_DRV_DM365
+       tristate "TI DaVinci DM365 RTC"
+       depends on ARCH_DAVINCI_DM365
+       help
+         Supports the RTC module in DaVinci DM365
+
 config RTC_DRV_TWL92330
        boolean "TI TWL92330/Menelaus"
        depends on MENELAUS
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index e454631..6cc5d40 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_RTC_DRV_BFIN)    += rtc-bfin.o
 obj-$(CONFIG_RTC_DRV_CMOS)     += rtc-cmos.o
 obj-$(CONFIG_RTC_DRV_DAVINCI_EVM) += rtc-davinci-evm.o
 obj-$(CONFIG_RTC_DRV_DM355EVM) += rtc-dm355evm.o
+obj-$(CONFIG_RTC_DRV_DM365)    += rtc-dm365.o
 obj-$(CONFIG_RTC_DRV_DS1216)   += rtc-ds1216.o
 obj-$(CONFIG_RTC_DRV_DS1286)   += rtc-ds1286.o
 obj-$(CONFIG_RTC_DRV_DS1302)   += rtc-ds1302.o
diff --git a/drivers/rtc/rtc-dm365.c b/drivers/rtc/rtc-dm365.c
new file mode 100644
index 0000000..e417678
--- /dev/null
+++ b/drivers/rtc/rtc-dm365.c
@@ -0,0 +1,671 @@
+/*
+ * Copyright (C) 2009 Texas Instruments Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+
+#define DM365_RTC_BASE         0x01c69000
+
+/*
+ * The DM365 RTC is a simple RTC with the following
+ * Sec: 0 - 59 : BCD count
+ * Min: 0 - 59 : BCD count
+ * Hour: 0 - 23 : BCD count
+ * Day: 0 - 0x7FFF(32767) : Binary count ( Over 89 years )
+ */
+
+#define DM365_RTCIF_PID_REG            0x00
+#define DM365_RTCIF_DMA_CMD_REG                0x04
+#define DM365_RTCIF_DMA_DATA0_REG      0x08
+#define DM365_RTCIF_DMA_DATA1_REG      0x0C
+#define DM365_RTCIF_INT_ENA_REG                0x10
+#define DM365_RTCIF_INT_FLG_REG                0x14
+
+/* DM365_RTCIF_DMA_CMD_REG bit fields */
+#define DM365_RTCIF_DMA_CMD_BUSY               (1<<31)
+#define DM365_RTCIF_DMA_CMD_SIZE_2WORD         (1<<25)
+#define DM365_RTCIF_DMA_CMD_DIR_READ           (1<<24)
+#define DM365_RTCIF_DMA_CMD_BYTEENA1_LSB       (1<<20)
+#define DM365_RTCIF_DMA_CMD_BYTEENA1_2ND_LSB   (1<<21)
+#define DM365_RTCIF_DMA_CMD_BYTEENA1_3RD_LSB   (1<<22)
+#define DM365_RTCIF_DMA_CMD_BYTEENA1_MSB       (1<<23)
+#define DM365_RTCIF_DMA_CMD_BYTEENA1_MASK      (0x00F00000)
+#define DM365_RTCIF_DMA_CMD_BYTEENA0_LSB       (1<<16)
+#define DM365_RTCIF_DMA_CMD_BYTEENA0_2ND_LSB   (1<<17)
+#define DM365_RTCIF_DMA_CMD_BYTEENA0_3RD_LSB   (1<<18)
+#define DM365_RTCIF_DMA_CMD_BYTEENA0_MSB       (1<<19)
+#define DM365_RTCIF_DMA_CMD_BYTEENA0_MASK      (0x000F0000)
+
+#define DM365_RTCIF_INT_ENA_RTCSS_INTENA       (1<<1)
+#define DM365_RTCIF_INT_ENA_RTCIF_INTENA       (1<<0)
+#define DM365_RTCIF_INT_FLG_RTCSS_INTFLG       (1<<1)
+#define DM365_RTCIF_INT_FLG_RTCIF_INTFLG       (1<<0)
+
+#define DM365_RTCIF_INT_FLG_MASK       (DM365_RTCIF_INT_FLG_RTCSS_INTFLG \
+                                        | DM365_RTCIF_INT_FLG_RTCIF_INTFLG)
+
+/* RTC registers */
+#define RTCSS_RTC_BASE                 0x10
+#define RTCSS_RTC_CTRL_REG             (RTCSS_RTC_BASE + 0x00)
+#define RTCSS_RTC_WDT_REG              (RTCSS_RTC_BASE + 0x01)
+#define RTCSS_RTC_TMR0_REG             (RTCSS_RTC_BASE + 0x02)
+#define RTCSS_RTC_TMR1_REG             (RTCSS_RTC_BASE + 0x03)
+#define RTCSS_RTC_CCTRL_REG            (RTCSS_RTC_BASE + 0x04)
+#define RTCSS_RTC_SEC_REG              (RTCSS_RTC_BASE + 0x05)
+#define RTCSS_RTC_MIN_REG              (RTCSS_RTC_BASE + 0x06)
+#define RTCSS_RTC_HOUR_REG             (RTCSS_RTC_BASE + 0x07)
+#define RTCSS_RTC_DAY0_REG             (RTCSS_RTC_BASE + 0x08)
+#define RTCSS_RTC_DAY1_REG             (RTCSS_RTC_BASE + 0x09)
+#define RTCSS_RTC_ALARM_MIN_REG                (RTCSS_RTC_BASE + 0x0A)
+#define RTCSS_RTC_ALARM_HOUR_REG       (RTCSS_RTC_BASE + 0x0B)
+#define RTCSS_RTC_ALARM_DAY0_REG       (RTCSS_RTC_BASE + 0x0C)
+#define RTCSS_RTC_ALARM_DAY1_REG       (RTCSS_RTC_BASE + 0x0D)
+#define RTCSS_RTC_CLKC_CNT             (RTCSS_RTC_BASE + 0x10)
+
+/* RTCSS_RTC_CTRL_REG bit fields: */
+#define RTCSS_RTC_CTRL_WDTBUS          (1<<7)
+#define RTCSS_RTC_CTRL_WEN             (1<<6)
+#define RTCSS_RTC_CTRL_WDRT            (1<<5)
+#define RTCSS_RTC_CTRL_WDTFLG          (1<<4)
+#define RTCSS_RTC_CTRL_TE              (1<<3)
+#define RTCSS_RTC_CTRL_TIEN            (1<<2)
+#define RTCSS_RTC_CTRL_TMRFLG          (1<<1)
+#define RTCSS_RTC_CTRL_TMMD_FREERUN    (1<<1)
+
+/* RTCSS_RTC_CCTRL_REG bit fields: */
+#define RTCSS_RTC_CCTRL_CALBUSY        (1<<7)
+#define RTCSS_RTC_CCTRL_DAEN   (1<<5)
+#define RTCSS_RTC_CCTRL_HAEN   (1<<4)
+#define RTCSS_RTC_CCTRL_MAEN   (1<<3)
+#define RTCSS_RTC_CCTRL_ALMFLG (1<<2)
+#define RTCSS_RTC_CCTRL_AIEN   (1<<1)
+#define RTCSS_RTC_CCTRL_CAEN   (1<<0)
+
+#define rtcif_read(addr)       __raw_readl( \
+               (unsigned int *)((u32)dm365_rtc_base + (u32)(addr)))
+#define rtcif_write(val, addr) __raw_writel(val, \
+               (unsigned int *)((u32)dm365_rtc_base + (u32)(addr)))
+
+static DEFINE_SPINLOCK(dm365_rtc_lock);
+
+static void __iomem    *dm365_rtc_base;
+static resource_size_t dm365_rtc_pbase;
+static size_t          dm365_rtc_base_size;
+static int             dm365_rtc_irq;
+
+static void rtcss_write_rtc(unsigned long val, u8 addr)
+{
+       unsigned int cmd;
+
+       while
+               (rtcif_read(DM365_RTCIF_DMA_CMD_REG) &
+                       DM365_RTCIF_DMA_CMD_BUSY);
+
+       rtcif_write(DM365_RTCIF_INT_ENA_RTCSS_INTENA |
+                   DM365_RTCIF_INT_ENA_RTCIF_INTENA, DM365_RTCIF_INT_FLG_REG);
+
+       cmd = DM365_RTCIF_DMA_CMD_BYTEENA0_LSB | addr;
+
+       rtcif_write(cmd, DM365_RTCIF_DMA_CMD_REG);
+
+       rtcif_write(val, DM365_RTCIF_DMA_DATA0_REG);
+
+       while
+               (rtcif_read(DM365_RTCIF_DMA_CMD_REG) &
+                       DM365_RTCIF_DMA_CMD_BUSY);
+}
+
+static u8 rtcss_read_rtc(u8 addr)
+{
+       unsigned int cmd;
+       u8 regval;
+
+       while
+               (rtcif_read(DM365_RTCIF_DMA_CMD_REG) &
+                       DM365_RTCIF_DMA_CMD_BUSY);
+
+       cmd = DM365_RTCIF_DMA_CMD_DIR_READ | DM365_RTCIF_DMA_CMD_BYTEENA0_LSB |
+               addr;
+
+       rtcif_write(cmd, DM365_RTCIF_DMA_CMD_REG);
+
+       rtcif_write(DM365_RTCIF_INT_ENA_RTCSS_INTENA |
+                   DM365_RTCIF_INT_ENA_RTCIF_INTENA, DM365_RTCIF_INT_FLG_REG);
+
+       while
+               (rtcif_read(DM365_RTCIF_DMA_CMD_REG) &
+                       DM365_RTCIF_DMA_CMD_BUSY);
+
+       regval = rtcif_read(DM365_RTCIF_DMA_DATA0_REG);
+
+       return regval;
+}
+
+static irqreturn_t dm365_rtc_isr(int irq, void *class_dev)
+{
+       unsigned long events = 0;
+       u32 irq_flg;
+
+       irq_flg = rtcif_read(DM365_RTCIF_INT_FLG_REG);
+
+       if ((irq_flg & DM365_RTCIF_INT_FLG_MASK)) {
+               if (irq_flg & DM365_RTCIF_INT_FLG_RTCIF_INTFLG) {
+                       rtcif_write(DM365_RTCIF_INT_ENA_RTCSS_INTENA |
+                                       DM365_RTCIF_INT_ENA_RTCIF_INTENA,
+                                       DM365_RTCIF_INT_FLG_REG);
+               } else
+                       events |= RTC_IRQF | RTC_AF;
+
+               rtc_update_irq(class_dev, 1, events);
+               return IRQ_HANDLED;
+       }
+       return IRQ_NONE;
+}
+
+static int dm365_rtc_update_timer(unsigned int cmd)
+{
+       u8 rtc_ctrl;
+
+       rtc_ctrl = rtcss_read_rtc(RTCSS_RTC_CTRL_REG);
+
+       switch (cmd) {
+       case RTC_UIE_ON:
+               while
+                       (rtcss_read_rtc(RTCSS_RTC_CTRL_REG) &
+                               RTCSS_RTC_CTRL_WDTBUS);
+               rtc_ctrl |= RTCSS_RTC_CTRL_TE;
+               rtcss_write_rtc(rtc_ctrl, RTCSS_RTC_CTRL_REG);
+               rtcss_write_rtc(0x0, RTCSS_RTC_CLKC_CNT);
+               rtc_ctrl |= RTCSS_RTC_CTRL_TIEN | RTCSS_RTC_CTRL_TMMD_FREERUN;
+               rtcss_write_rtc(rtc_ctrl, RTCSS_RTC_CTRL_REG);
+               rtcss_write_rtc(0x80, RTCSS_RTC_TMR0_REG);
+               rtcss_write_rtc(0x0, RTCSS_RTC_TMR1_REG);
+               break;
+       case RTC_UIE_OFF:
+               rtc_ctrl = rtcss_read_rtc(RTCSS_RTC_CTRL_REG);
+               rtc_ctrl &= ~RTCSS_RTC_CTRL_TIEN;
+               rtcss_write_rtc(rtc_ctrl, RTCSS_RTC_CTRL_REG);
+               break;
+       }
+
+       return 0;
+}
+
+static int
+dm365_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
+{
+       u8 rtc_ctrl;
+       unsigned long flags;
+
+       switch (cmd) {
+       case RTC_AIE_OFF:
+       case RTC_AIE_ON:
+       case RTC_UIE_OFF:
+       case RTC_UIE_ON:
+       case RTC_IRQP_READ:
+       case RTC_IRQP_SET:
+       case RTC_PIE_OFF:
+       case RTC_PIE_ON:
+       case RTC_WIE_ON:
+       case RTC_WIE_OFF:
+               break;
+       default:
+               return -ENOIOCTLCMD;
+       }
+
+       spin_lock_irqsave(&dm365_rtc_lock, flags);
+       rtc_ctrl = rtcss_read_rtc(RTCSS_RTC_CCTRL_REG);
+
+       switch (cmd) {
+       case RTC_AIE_OFF:
+               rtc_ctrl &= ~RTCSS_RTC_CCTRL_AIEN;
+               break;
+       case RTC_AIE_ON:
+               rtc_ctrl |= RTCSS_RTC_CCTRL_AIEN;
+               break;
+       case RTC_WIE_ON:
+               rtc_ctrl |= (RTCSS_RTC_CTRL_WEN | RTCSS_RTC_CTRL_WDTFLG);
+               break;
+       case RTC_WIE_OFF:
+               rtc_ctrl &= ~RTCSS_RTC_CTRL_WEN;
+               break;
+       case RTC_UIE_OFF:
+       case RTC_UIE_ON:
+               dm365_rtc_update_timer(cmd);
+       }
+
+       rtcss_write_rtc(rtc_ctrl, RTCSS_RTC_CTRL_REG);
+       spin_unlock_irqrestore(&dm365_rtc_lock, flags);
+
+       return 0;
+}
+
+static int convertfromdays(u16 days, struct rtc_time *tm)
+{
+       int tmp_days, year, mon;
+
+       for (year = 2000;; year++) {
+               tmp_days = rtc_year_days(1, 12, year);
+               if (days >= tmp_days)
+                       days -= tmp_days;
+               else {
+                       for (mon = 0;; mon++) {
+                               tmp_days = rtc_month_days(mon, year);
+                               if (days >= tmp_days) {
+                                       days -= tmp_days;
+                               } else {
+                                       tm->tm_year = year - 1900;
+                                       tm->tm_mon = mon;
+                                       tm->tm_mday = days + 1;
+                                       break;
+                               }
+                       }
+                       break;
+               }
+       }
+       return 0;
+}
+
+static int convert2days(u16 *days, struct rtc_time *tm)
+{
+       int i;
+
+       *days = 0;
+
+       /* epoch == 1900 */
+       if (tm->tm_year < 100 || tm->tm_year > 199)
+               return -EINVAL;
+
+       for (i = 2000; i < 1900 + tm->tm_year; i++)
+               *days += rtc_year_days(1, 12, i);
+
+       *days += rtc_year_days(tm->tm_mday, tm->tm_mon, 1900 + tm->tm_year);
+
+       return 0;
+}
+
+static int dm365_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+       u16 days = 0;
+       u8 day0, day1;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dm365_rtc_lock, flags);
+
+       while
+               (rtcss_read_rtc(RTCSS_RTC_CCTRL_REG) &
+                       RTCSS_RTC_CCTRL_CALBUSY);
+
+       tm->tm_sec = bcd2bin(rtcss_read_rtc(RTCSS_RTC_SEC_REG));
+
+       while
+               (rtcss_read_rtc(RTCSS_RTC_CCTRL_REG) &
+                       RTCSS_RTC_CCTRL_CALBUSY);
+
+       tm->tm_min = bcd2bin(rtcss_read_rtc(RTCSS_RTC_MIN_REG));
+
+       while
+               (rtcss_read_rtc(RTCSS_RTC_CCTRL_REG) &
+                       RTCSS_RTC_CCTRL_CALBUSY);
+
+       tm->tm_hour = bcd2bin(rtcss_read_rtc(RTCSS_RTC_HOUR_REG));
+
+       while
+               (rtcss_read_rtc(RTCSS_RTC_CCTRL_REG) &
+                       RTCSS_RTC_CCTRL_CALBUSY);
+
+       day0 = rtcss_read_rtc(RTCSS_RTC_DAY0_REG);
+
+       while
+               (rtcss_read_rtc(RTCSS_RTC_CCTRL_REG) &
+                       RTCSS_RTC_CCTRL_CALBUSY);
+
+       day1 = rtcss_read_rtc(RTCSS_RTC_DAY1_REG);
+
+       spin_unlock_irqrestore(&dm365_rtc_lock, flags);
+
+       days |= day1;
+       days <<= 8;
+       days |= day0;
+
+       if (convertfromdays(days, tm) < 0)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int dm365_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+       u16 days;
+       u8 new_cctrl;
+       unsigned long flags;
+
+       if (convert2days(&days, tm) < 0)
+               return -EINVAL;
+
+       spin_lock_irqsave(&dm365_rtc_lock, flags);
+
+       while
+               (rtcss_read_rtc(RTCSS_RTC_CCTRL_REG) &
+                       RTCSS_RTC_CCTRL_CALBUSY);
+
+       rtcss_write_rtc(bin2bcd(tm->tm_sec), RTCSS_RTC_SEC_REG);
+
+       while
+               (rtcss_read_rtc(RTCSS_RTC_CCTRL_REG) &
+                       RTCSS_RTC_CCTRL_CALBUSY);
+
+       rtcss_write_rtc(bin2bcd(tm->tm_min), RTCSS_RTC_MIN_REG);
+
+       while
+               (rtcss_read_rtc(RTCSS_RTC_CCTRL_REG) &
+                       RTCSS_RTC_CCTRL_CALBUSY);
+
+       rtcss_write_rtc(bin2bcd(tm->tm_hour), RTCSS_RTC_HOUR_REG);
+
+       while
+               (rtcss_read_rtc(RTCSS_RTC_CCTRL_REG) &
+                       RTCSS_RTC_CCTRL_CALBUSY);
+
+       rtcss_write_rtc(days & 0xFF, RTCSS_RTC_DAY0_REG);
+
+       while
+               (rtcss_read_rtc(RTCSS_RTC_CCTRL_REG) &
+                       RTCSS_RTC_CCTRL_CALBUSY);
+
+       rtcss_write_rtc((days & 0xFF00) >> 8, RTCSS_RTC_DAY1_REG);
+
+       new_cctrl = rtcss_read_rtc(RTCSS_RTC_CCTRL_REG);
+
+       new_cctrl |= RTCSS_RTC_CCTRL_CAEN;
+
+       rtcss_write_rtc(new_cctrl, RTCSS_RTC_CCTRL_REG);
+
+       spin_unlock_irqrestore(&dm365_rtc_lock, flags);
+
+       return 0;
+}
+
+static int dm365_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+       u16 days = 0;
+       u8 day0, day1;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dm365_rtc_lock, flags);
+
+       while
+               (rtcss_read_rtc(RTCSS_RTC_CCTRL_REG) &
+                       RTCSS_RTC_CCTRL_CALBUSY);
+
+       alm->time.tm_min = bcd2bin(rtcss_read_rtc(RTCSS_RTC_ALARM_MIN_REG));
+
+       while
+               (rtcss_read_rtc(RTCSS_RTC_CCTRL_REG) &
+                       RTCSS_RTC_CCTRL_CALBUSY);
+
+       alm->time.tm_hour = bcd2bin(rtcss_read_rtc(RTCSS_RTC_ALARM_HOUR_REG));
+
+       while
+               (rtcss_read_rtc(RTCSS_RTC_CCTRL_REG) &
+                       RTCSS_RTC_CCTRL_CALBUSY);
+
+       day0 = rtcss_read_rtc(RTCSS_RTC_ALARM_DAY0_REG);
+
+       while
+               (rtcss_read_rtc(RTCSS_RTC_CCTRL_REG) &
+                       RTCSS_RTC_CCTRL_CALBUSY);
+
+       day1 = rtcss_read_rtc(RTCSS_RTC_ALARM_DAY1_REG);
+
+       spin_unlock_irqrestore(&dm365_rtc_lock, flags);
+       days |= day1;
+       days <<= 8;
+       days |= day0;
+
+       if (convertfromdays(days, &alm->time) < 0)
+               return -EINVAL;
+
+       alm->pending = !!(rtcss_read_rtc(RTCSS_RTC_CCTRL_REG) &
+                         RTCSS_RTC_CCTRL_AIEN);
+       alm->enabled = alm->pending && device_may_wakeup(dev);
+
+       return 0;
+}
+
+static int dm365_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+       u16 days;
+       u8 new_cctrl;
+       unsigned long flags;
+
+       if (alm->time.tm_mday <= 0 && alm->time.tm_mon < 0
+           && alm->time.tm_year < 0) {
+               struct rtc_time tm;
+               unsigned long now, then;
+
+               dm365_rtc_read_time(dev, &tm);
+               rtc_tm_to_time(&tm, &now);
+
+               alm->time.tm_mday = tm.tm_mday;
+               alm->time.tm_mon = tm.tm_mon;
+               alm->time.tm_year = tm.tm_year;
+               rtc_tm_to_time(&alm->time, &then);
+
+               /* sometimes the alarm wraps into tomorrow */
+               if (then < now) {
+                       rtc_time_to_tm(now + 24 * 60 * 60, &tm);
+                       alm->time.tm_mday = tm.tm_mday;
+                       alm->time.tm_mon = tm.tm_mon;
+                       alm->time.tm_year = tm.tm_year;
+               }
+       }
+
+       if (convert2days(&days, &alm->time) < 0)
+               return -EINVAL;
+
+       spin_lock_irqsave(&dm365_rtc_lock, flags);
+
+       while
+               (rtcss_read_rtc(RTCSS_RTC_CCTRL_REG) &
+                       RTCSS_RTC_CCTRL_CALBUSY);
+
+       rtcss_write_rtc(bin2bcd(alm->time.tm_min), RTCSS_RTC_ALARM_MIN_REG);
+
+       while
+               (rtcss_read_rtc(RTCSS_RTC_CCTRL_REG) &
+                       RTCSS_RTC_CCTRL_CALBUSY);
+
+       rtcss_write_rtc(bin2bcd(alm->time.tm_hour), RTCSS_RTC_ALARM_HOUR_REG);
+
+       while
+               (rtcss_read_rtc(RTCSS_RTC_CCTRL_REG) &
+                       RTCSS_RTC_CCTRL_CALBUSY);
+
+       rtcss_write_rtc(days & 0xFF, RTCSS_RTC_ALARM_DAY0_REG);
+
+       while
+               (rtcss_read_rtc(RTCSS_RTC_CCTRL_REG) &
+                       RTCSS_RTC_CCTRL_CALBUSY);
+
+       rtcss_write_rtc((days & 0xFF00) >> 8, RTCSS_RTC_ALARM_DAY1_REG);
+
+       new_cctrl = rtcss_read_rtc(RTCSS_RTC_CCTRL_REG);
+
+       if (alm->enabled)
+               new_cctrl |= (RTCSS_RTC_CCTRL_DAEN |
+                             RTCSS_RTC_CCTRL_HAEN |
+                             RTCSS_RTC_CCTRL_MAEN |
+                             RTCSS_RTC_CCTRL_ALMFLG | RTCSS_RTC_CCTRL_AIEN);
+       else
+               new_cctrl &= ~RTCSS_RTC_CCTRL_AIEN;
+
+       rtcss_write_rtc(new_cctrl, RTCSS_RTC_CCTRL_REG);
+
+       spin_unlock_irqrestore(&dm365_rtc_lock, flags);
+
+       return 0;
+}
+
+static struct rtc_class_ops dm365_rtc_ops = {
+       .ioctl = dm365_rtc_ioctl,
+       .read_time = dm365_rtc_read_time,
+       .set_time = dm365_rtc_set_time,
+       .read_alarm = dm365_rtc_read_alarm,
+       .set_alarm = dm365_rtc_set_alarm,
+};
+
+static int __init dm365_rtc_probe(struct platform_device *pdev)
+{
+       struct resource *res, *mem;
+       struct rtc_device *rtc;
+       u8 new_ctrl = 0;
+
+       dm365_rtc_irq = platform_get_irq(pdev, 0);
+       if (dm365_rtc_irq <= 0) {
+               pr_debug("%s: no RTC irq?\n", pdev->name);
+               return -ENOENT;
+       }
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (res && res->start != DM365_RTC_BASE) {
+               pr_debug("%s: RTC registers at %08x, expected %08x\n",
+                        pdev->name, (unsigned)res->start, DM365_RTC_BASE);
+               return -ENOENT;
+       }
+
+       dm365_rtc_pbase = res->start;
+       dm365_rtc_base_size = res->end - res->start + 1;
+
+       if (res)
+               mem = request_mem_region(res->start,
+                                        dm365_rtc_base_size, pdev->name);
+       else
+               mem = NULL;
+       if (!mem) {
+               pr_debug("%s: RTC registers at %08x are not free\n",
+                        pdev->name, DM365_RTC_BASE);
+               return -EBUSY;
+       }
+
+       dm365_rtc_base = ioremap(res->start, dm365_rtc_base_size);
+       if (dm365_rtc_base == NULL) {
+               pr_debug("%s: Can't ioremap MEM resource.\n", pdev->name);
+               goto fail;
+       }
+
+       rtc =
+           rtc_device_register(pdev->name, &pdev->dev, &dm365_rtc_ops,
+                               THIS_MODULE);
+       if (IS_ERR(rtc)) {
+               pr_debug("%s: can't register RTC device, err %ld\n",
+                        pdev->name, PTR_ERR(rtc));
+               goto fail1;
+       }
+       platform_set_drvdata(pdev, rtc);
+       dev_set_drvdata(&rtc->dev, mem);
+
+       rtcif_write(0, DM365_RTCIF_INT_ENA_REG);
+       rtcif_write(0, DM365_RTCIF_INT_FLG_REG);
+
+       rtcss_write_rtc(0, RTCSS_RTC_CTRL_REG);
+       rtcss_write_rtc(0, RTCSS_RTC_CCTRL_REG);
+
+       if (request_irq(dm365_rtc_irq, dm365_rtc_isr, IRQF_DISABLED,
+                       dev_name(&rtc->dev), rtc)) {
+               pr_debug("%s: RTC timer interrupt IRQ%d already claimed\n",
+                        pdev->name, dm365_rtc_irq);
+               goto fail0;
+       }
+
+       new_ctrl |= (DM365_RTCIF_INT_ENA_RTCSS_INTENA
+                    | DM365_RTCIF_INT_ENA_RTCIF_INTENA);
+
+       rtcif_write(new_ctrl, DM365_RTCIF_INT_ENA_REG);
+
+       while (rtcss_read_rtc(RTCSS_RTC_CCTRL_REG) & RTCSS_RTC_CCTRL_CALBUSY)
+               rtcss_read_rtc(RTCSS_RTC_SEC_REG);
+
+       rtcss_write_rtc(RTCSS_RTC_CCTRL_CAEN, RTCSS_RTC_CCTRL_REG);
+       device_init_wakeup(&pdev->dev, 0);
+
+       return 0;
+
+fail0:
+       rtc_device_unregister(rtc);
+fail1:
+       iounmap(dm365_rtc_base);
+fail:
+       release_mem_region(dm365_rtc_pbase, dm365_rtc_base_size);
+
+       return -EIO;
+}
+
+static int __exit dm365_rtc_remove(struct platform_device *pdev)
+{
+       struct rtc_device *rtc = platform_get_drvdata(pdev);
+
+       device_init_wakeup(&pdev->dev, 0);
+
+       rtcif_write(0, DM365_RTCIF_INT_ENA_REG);
+
+       free_irq(dm365_rtc_irq, rtc);
+
+       release_resource(dev_get_drvdata(&rtc->dev));
+
+       rtc_device_unregister(rtc);
+
+       return 0;
+}
+
+MODULE_ALIAS("dm365_rtc");
+
+static struct platform_driver dm365_rtc_driver = {
+       .probe          = dm365_rtc_probe,
+       .remove         = __exit_p(dm365_rtc_remove),
+       .driver         = {
+               .name = "rtc_davinci_dm365",
+               .owner = THIS_MODULE,
+       },
+};
+
+static int __init rtc_init(void)
+{
+       return platform_driver_register(&dm365_rtc_driver);
+}
+
+static void __exit rtc_exit(void)
+{
+       platform_driver_unregister(&dm365_rtc_driver);
+}
+
+module_init(rtc_init);
+module_exit(rtc_exit);
+
+MODULE_AUTHOR("Sandeep Paulraj");
+MODULE_DESCRIPTION("Texas Instruments DaVinci DM365 RTC Driver");
+MODULE_LICENSE("GPL");
+
-- 
1.6.0.4

_______________________________________________
Davinci-linux-open-source mailing list
[email protected]
http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source

Reply via email to