Skip to contentSkip to conversation

ARM: OMAP2: TWL4030 RTC driver

PATCH In review Dmitry Krivoschekov 2 participants 3 messages May 31, 2007

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

PATCH 1/1 ARM: OMAP2: TWL4030 RTC driver

This patch adds TWL4030 Real-Time Clock support for OMAP2430.

---

 arch/arm/mach-omap2/board-2430sdp.c     |   42 ++
 drivers/rtc/Kconfig                     |    7 
 drivers/rtc/Makefile                    |    1 
 drivers/rtc/rtc-twl4030.c               |  636 +++++++++++++++++++++++++++++++
 include/asm-arm/arch-omap/twl4030-rtc.h |  230 +++++++++++
 5 files changed, 916 insertions(+), 0 deletions(-)
arch/arm/mach-omap2/board-2430sdp.c +42 -0
index ab9b23b..e1debbf 100644
@@ -38,6 +38,7 @@
 #include <asm/arch/keypad.h>
 #include <asm/arch/gpmc.h>
 #include <asm/arch/mcspi.h>
+#include <asm/arch/twl4030-rtc.h>
 
 #include <asm/io.h>
 
@@ -50,6 +51,8 @@
  * that driver is used for the touchscreen. */
 #define TS_GPIO                 24
 
+#define TWL4030_MSECURE_GPIO	118
+
 static struct mtd_partition sdp2430_partitions[] = {
 	/* bootloader (U-Boot, etc) in first sector */
 	{
@@ -183,11 +186,50 @@ static struct platform_device sdp2430_kp
 	},
 };
 
+static int twl4030_rtc_init(void)
+{
+	int ret = 0;
+
+	ret = omap_request_gpio(TWL4030_MSECURE_GPIO);
+	if (ret < 0) {
+		printk(KERN_ERR "twl4030_rtc_init: can't reserve GPIO:%d !\n",
+			TWL4030_MSECURE_GPIO);
+		goto out;
+	}
+	/* TWL4030 will be in secure mode if msecure line from OMAP is low.
+	 * Make msecure line high in order to change the TWL4030 RTC time
+	 * and calender registers.
+	 */
+	omap_set_gpio_direction(TWL4030_MSECURE_GPIO, 0);	/*dir out */
+	omap_set_gpio_dataout(TWL4030_MSECURE_GPIO, 1);
+out:
+	return ret;
+}
+
+static void twl4030_rtc_exit(void)
+{
+	omap_free_gpio(TWL4030_MSECURE_GPIO);
+}
+
+static struct twl4030rtc_platform_data sdp2430_twl4030rtc_data = {
+	.init = &twl4030_rtc_init,
+	.exit = &twl4030_rtc_exit,
+};
+
+static struct platform_device sdp2430_twl4030rtc_device = {
+ 	.name		= "twl4030_rtc",
+ 	.id		= -1,
+	.dev		= {
+		.platform_data	= &sdp2430_twl4030rtc_data,
+	},
+};
+
 static struct platform_device *sdp2430_devices[] __initdata = {
 	&sdp2430_smc91x_device,
 	&sdp2430_flash_device,
 	&sdp2430_kp_device,
 	&sdp2430_lcd_device,
+	&sdp2430_twl4030rtc_device,	
 };
 
 static void ads7846_dev_init(void)
drivers/rtc/Kconfig +7 -0
index 4e4c10a..17c0374 100644
@@ -121,6 +121,13 @@ config RTC_DRV_TEST
 comment "I2C RTC drivers"
 	depends on RTC_CLASS
 
+config RTC_DRV_OMAP2430_TWL4030
+	tristate "OMAP243X TWL4030 Real Time Clock"
+	depends on RTC_CLASS && TWL4030_CORE
+	help
+	  Support for TWL4030 companion chip internal RTC on OMAP2 SDP2430 board
+
+
 config RTC_DRV_DS1307
 	tristate "Dallas/Maxim DS1307/37/38/39/40, ST M41T00"
 	depends on RTC_CLASS && I2C
drivers/rtc/Makefile +1 -0
index a1afbc2..d6938c2 100644
@@ -41,3 +41,4 @@ obj-$(CONFIG_RTC_DRV_V3020)	+= rtc-v3020
 obj-$(CONFIG_RTC_DRV_AT91RM9200)+= rtc-at91rm9200.o
 obj-$(CONFIG_RTC_DRV_SH)	+= rtc-sh.o
 obj-$(CONFIG_RTC_DRV_BFIN)	+= rtc-bfin.o
+obj-$(CONFIG_RTC_DRV_OMAP2430_TWL4030)	+= rtc-twl4030.o
drivers/rtc/rtc-twl4030.c new file +636 -0

636 lines not on this page · raw message

include/asm-arm/arch-omap/twl4030-rtc.h new file +230 -0

230 lines not on this page · raw message

#1 lore raw
Quoted 41 lines · Dmitry Krivoschekov

This patch adds TWL4030 Real-Time Clock support for OMAP2430.

---

 arch/arm/mach-omap2/board-2430sdp.c     |   42 ++
 drivers/rtc/Kconfig                     |    7
 drivers/rtc/Makefile                    |    1
 drivers/rtc/rtc-twl4030.c               |  636 +++++++++++++++++++++++++++++++
 include/asm-arm/arch-omap/twl4030-rtc.h |  230 +++++++++++
 5 files changed, 916 insertions(+), 0 deletions(-)
arch/arm/mach-omap2/board-2430sdp.c +7 -0
index ab9b23b..e1debbf 100644
@@ -38,6 +38,7 @@
 #include <asm/arch/keypad.h>
 #include <asm/arch/gpmc.h>
 #include <asm/arch/mcspi.h>
+#include <asm/arch/twl4030-rtc.h>
 
 #include <asm/io.h>
 
@@ -50,6 +51,8 @@
  * that driver is used for the touchscreen. */
 #define TS_GPIO                 24
 
+#define TWL4030_MSECURE_GPIO   118
+
 static struct mtd_partition sdp2430_partitions[] = {
        /* bootloader (U-Boot, etc) in first sector */
        {
@@ -183,11 +186,50 @@ static struct platform_device sdp2430_kp
        },
 };
 
+static int twl4030_rtc_init(void)
+{
+       int ret = 0;
+

arch/arm/mach-omap2/board-2430sdp.c static struct platform_device sdp2430_kp · line 202

Quoted 10 lines
arch/arm/mach-omap2/board-2430sdp.c +10 -0
+       ret = omap_request_gpio(TWL4030_MSECURE_GPIO);
+       if (ret < 0) {
+               printk(KERN_ERR "twl4030_rtc_init: can't reserve GPIO:%d !\n",
+                       TWL4030_MSECURE_GPIO);
+               goto out;
+       }
+       /* TWL4030 will be in secure mode if msecure line from OMAP is low.
+        * Make msecure line high in order to change the TWL4030 RTC time
+        * and calender registers.
+        */

cosmetic changes required. Above multi-line comment block should look like this:

Quoted 45 lines
arch/arm/mach-omap2/board-2430sdp.c +30 -0
+       /*
+        * TWL4030 will be in secure mode if msecure line from OMAP is low.
+        * Make msecure line high in order to change the TWL4030 RTC time
+        * and calender registers.
+        */
 
 
+       omap_set_gpio_direction(TWL4030_MSECURE_GPIO, 0);       /*dir out */
+       omap_set_gpio_dataout(TWL4030_MSECURE_GPIO, 1);
+out:
+       return ret;
+}
+
+static void twl4030_rtc_exit(void)
+{
+       omap_free_gpio(TWL4030_MSECURE_GPIO);
+}
+
+static struct twl4030rtc_platform_data sdp2430_twl4030rtc_data = {
+       .init = &twl4030_rtc_init,
+       .exit = &twl4030_rtc_exit,
+};
+
+static struct platform_device sdp2430_twl4030rtc_device = {
+       .name           = "twl4030_rtc",
+       .id             = -1,
+       .dev            = {
+               .platform_data  = &sdp2430_twl4030rtc_data,
+       },
+};
+
 static struct platform_device *sdp2430_devices[] __initdata = {
        &sdp2430_smc91x_device,
        &sdp2430_flash_device,
        &sdp2430_kp_device,
        &sdp2430_lcd_device,
+       &sdp2430_twl4030rtc_device,
 };
 
 static void ads7846_dev_init(void)
drivers/rtc/Kconfig +0 -0
index 4e4c10a..17c0374 100644

drivers/rtc/Kconfig config RTC_DRV_TEST · line 129

Quoted 9 lines
drivers/rtc/Kconfig +6 -0
@@ -121,6 +121,13 @@ config RTC_DRV_TEST
 comment "I2C RTC drivers"
        depends on RTC_CLASS
 
+config RTC_DRV_OMAP2430_TWL4030
+       tristate "OMAP243X TWL4030 Real Time Clock"
+       depends on RTC_CLASS && TWL4030_CORE
+       help
+         Support for TWL4030 companion chip internal RTC on OMAP2 SDP2430 board
+

This driver should work on other boards having TWL4030 chip, so make help text more generic please. It is better if you don't have OMAP2430 in RTC_DRV_OMAP2430_TWL4030

Quoted 19 lines
drivers/rtc/Kconfig +1 -0
+
 config RTC_DRV_DS1307
        tristate "Dallas/Maxim DS1307/37/38/39/40, ST M41T00"
        depends on RTC_CLASS && I2C
drivers/rtc/Makefile +1 -0
index a1afbc2..d6938c2 100644
@@ -41,3 +41,4 @@ obj-$(CONFIG_RTC_DRV_V3020)   += rtc-v3020
 obj-$(CONFIG_RTC_DRV_AT91RM9200)+= rtc-at91rm9200.o
 obj-$(CONFIG_RTC_DRV_SH)       += rtc-sh.o
 obj-$(CONFIG_RTC_DRV_BFIN)     += rtc-bfin.o
+obj-$(CONFIG_RTC_DRV_OMAP2430_TWL4030) += rtc-twl4030.o
drivers/rtc/rtc-twl4030.c new file +0 -0
new file mode 100644
index 0000000..9811586

drivers/rtc/rtc-twl4030.c line 5

Quoted 5 lines
drivers/rtc/rtc-twl4030.c new file +5 -0
@@ -0,0 +1,636 @@
+/*
+ * drivers/rtc/rtc-twl4030.c
+ *
+ * TWL4030 Real Time Clock interface for Linux
+ *

I think this whole driver will work for Linux only. So no need to mention explicit "for Linux".

Quoted 122 lines
drivers/rtc/rtc-twl4030.c new file +122 -0
+ * Copyright (C) 2007 MontaVista Software, Inc
+ * Author: Alexandre Rusev <[email protected]>
+ *
+ * Based on original TI driver twl4030-rtc.c
+ *   Copyright (C) 2006 Texas Instruments, Inc.
+ *
+ * Based on rtc-omap.c
+ *   Copyright (C) 2003 MontaVista Software, Inc.
+ *   Author: George G. Davis <[email protected]> or <[email protected]>
+ *
+ *   Copyright (C) 2006 David Brownell
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+
+#include <asm/io.h>
+#include <asm/mach/time.h>
+#include <asm/system.h>
+#include <asm/hardware.h>
+#include <asm/irq.h>
+#include <asm/arch/twl4030.h>
+#include <asm/arch/twl4030-rtc.h>
+
+#define ALL_TIME_REGS          6
+
+/*
+ * If this driver ever becomes modularised, it will be really nice
+ * to make the epoch retain its value across module reload...
+ */
+static int epoch = 1900;       /* year corresponding to 0x00   */
+
+/*
+ * Supports 1 byte read from TWL4030 RTC register.
+ */
+static int twl4030_rtc_read_u8(u8 * data, u8 reg)
+{
+       int ret;
+
+       ret = twl4030_i2c_read_u8(TWL4030_MODULE_RTC, data, reg);
+       if (ret < 0) {
+               printk(KERN_WARNING "twl4030_rtc: Could not read TWL4030"
+                      "register %X - returned %d[%x]\n", reg, ret, ret);
+       }
+       return ret;
+}
+
+/*
+ * Supports 1 byte write to TWL4030 RTC registers.
+ */
+static int twl4030_rtc_write_u8(u8 data, u8 reg)
+{
+       int ret;
+
+       ret = twl4030_i2c_write_u8(TWL4030_MODULE_RTC, data, reg);
+       if (ret < 0) {
+               printk(KERN_WARNING "twl4030_rtc: Could not write TWL4030"
+                      "register %X - returned %d[%x]\n", reg, ret, ret);
+       }
+       return ret;
+}
+
+/*
+ * Enables timer or alarm interrupts.
+ */
+static int set_rtc_irq_bit(unsigned char bit)
+{
+       unsigned char val;
+       int ret;
+
+       ret = twl4030_rtc_read_u8(&val, REG_RTC_INTERRUPTS_REG);
+       if (ret < 0)
+               goto set_irq_out;
+
+       val |= bit;
+       ret = twl4030_rtc_write_u8(val, REG_RTC_INTERRUPTS_REG);
+
+set_irq_out:
+       return ret;
+}
+
+#ifdef CONFIG_PM
+/*
+ * Read timer or alarm interrupts register.
+ */
+static int get_rtc_irq_bit(unsigned char *val)
+{
+       int ret;
+
+       ret = twl4030_rtc_read_u8(val, REG_RTC_INTERRUPTS_REG);
+       return ret;
+}
+#endif
+/*
+ * Disables timer or alarm interrupts.
+ */
+static int mask_rtc_irq_bit(unsigned char bit)
+{
+       unsigned char val;
+       int ret;
+
+       ret = twl4030_rtc_read_u8(&val, REG_RTC_INTERRUPTS_REG);
+       if (ret < 0)
+               goto mask_irq_out;
+
+       val &= ~bit;

drivers/rtc/rtc-twl4030.c line 137

Quoted 10 lines
drivers/rtc/rtc-twl4030.c new file +10 -0
+       ret = twl4030_rtc_write_u8(val, REG_RTC_INTERRUPTS_REG);
+
+mask_irq_out:
+       return ret;
+}
+
+static int twl4030_rtc_alarm_irq_set_state(struct device *dev, int enabled)
+{
+       int ret;
+       /* Allow ints for RTC ALARM updates.  */

one empty line between above sentences please.

Quoted 142 lines
drivers/rtc/rtc-twl4030.c new file +142 -0
+       if (enabled)
+               ret = set_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_ALARM_M);
+       else
+               ret = mask_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_ALARM_M);
+
+       return ret;
+}
+
+/*
+ * Gets current TWL4030 RTC time and date parameters.
+ */
+static int get_rtc_time(struct rtc_time *rtc_tm)
+{
+       unsigned char rtc_data[ALL_TIME_REGS + 1];
+       int ret;
+       u8 save_control;
+
+       ret = twl4030_rtc_read_u8(&save_control, REG_RTC_CTRL_REG);
+       if (ret < 0)
+               return ret;
+
+       save_control |= BIT_RTC_CTRL_REG_GET_TIME_M;
+
+       ret = twl4030_rtc_write_u8(save_control, REG_RTC_CTRL_REG);
+       if (ret < 0)
+               return ret;
+
+       ret = twl4030_i2c_read(TWL4030_MODULE_RTC, rtc_data,
+                              REG_SECONDS_REG, ALL_TIME_REGS);
+
+       if (ret < 0) {
+               printk(KERN_ERR "twl4030_rtc: twl4030_i2c_read error.\n");
+               return ret;
+       }
+
+       rtc_tm->tm_sec = BCD2BIN(rtc_data[0]);
+       rtc_tm->tm_min = BCD2BIN(rtc_data[1]);
+       rtc_tm->tm_hour = BCD2BIN(rtc_data[2]);
+       rtc_tm->tm_mday = BCD2BIN(rtc_data[3]);
+       rtc_tm->tm_mon = BCD2BIN(rtc_data[4]);
+       rtc_tm->tm_year = BCD2BIN(rtc_data[5]);
+
+       /*
+        * Account for differences between how the RTC uses the values
+        * and how they are defined in a struct rtc_time;
+        */
+       if ((rtc_tm->tm_year += (epoch - 1900)) <= 69)
+               rtc_tm->tm_year += 100;
+
+       rtc_tm->tm_mon--;
+
+       return ret;
+}
+
+static int twl4030_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+       unsigned char save_control;
+       unsigned char rtc_data[ALL_TIME_REGS + 1];
+       int ret;
+
+       /* Month range is 01..12 */
+       tm->tm_mon++;
+
+       rtc_data[1] = BIN2BCD(tm->tm_sec);
+       rtc_data[2] = BIN2BCD(tm->tm_min);
+       rtc_data[3] = BIN2BCD(tm->tm_hour);
+       rtc_data[4] = BIN2BCD(tm->tm_mday);
+       rtc_data[5] = BIN2BCD(tm->tm_mon);
+       rtc_data[6] = BIN2BCD(tm->tm_year);
+
+       /* Stop RTC while updating the TC registers */
+       ret = twl4030_rtc_read_u8(&save_control, REG_RTC_CTRL_REG);
+       if (ret < 0)
+               goto out;
+
+       save_control &= ~BIT_RTC_CTRL_REG_STOP_RTC_M;
+       twl4030_rtc_write_u8(save_control, REG_RTC_CTRL_REG);
+       if (ret < 0)
+               goto out;
+
+       /* update all the alarm registers in one shot */
+       ret = twl4030_i2c_write(TWL4030_MODULE_RTC, rtc_data,
+                               REG_SECONDS_REG, ALL_TIME_REGS);
+       if (ret < 0) {
+               printk(KERN_ERR "twl4030: twl4030_i2c_write error.\n");
+               goto out;
+       }
+
+       /* Start back RTC */
+       save_control |= BIT_RTC_CTRL_REG_STOP_RTC_M;
+       ret = twl4030_rtc_write_u8(save_control, REG_RTC_CTRL_REG);
+
+out:
+       return ret;
+}
+
+/*
+ * Gets current TWL4030 RTC alarm time.
+ */
+static int get_rtc_alm_time(struct rtc_time *alm_tm)
+{
+       unsigned char rtc_data[ALL_TIME_REGS + 1];
+       int ret;
+
+       ret = twl4030_i2c_read(TWL4030_MODULE_RTC, rtc_data,
+                              REG_ALARM_SECONDS_REG, ALL_TIME_REGS);
+       if (ret < 0) {
+               printk(KERN_ERR "twl4030_rtc: twl4030_i2c_read error.\n");
+               return ret;
+       }
+
+       alm_tm->tm_sec = BCD2BIN(rtc_data[0]);
+       alm_tm->tm_min = BCD2BIN(rtc_data[1]);
+       alm_tm->tm_hour = BCD2BIN(rtc_data[2]);
+       alm_tm->tm_mday = BCD2BIN(rtc_data[3]);
+       alm_tm->tm_mon = BCD2BIN(rtc_data[4]);
+       alm_tm->tm_year = BCD2BIN(rtc_data[5]);
+
+       /*
+        * Account for differences between how the RTC uses the values
+        * and how they are defined in a struct rtc_time;
+        */
+       if ((alm_tm->tm_year += (epoch - 1900)) <= 69)
+               alm_tm->tm_year += 100;
+
+       alm_tm->tm_mon--;
+
+       return ret;
+}
+
+static int twl4030_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+       int ret;
+
+       memset(tm, 0, sizeof(struct rtc_time));
+       ret = get_rtc_time(tm);
+
+       return ret;
+}
+
+/*
+ * Gets current TWL4030 RTC alarm time.

drivers/rtc/rtc-twl4030.c line 289

Quoted 10 lines
drivers/rtc/rtc-twl4030.c new file +10 -0
+ */
+static int twl4030_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+       int ret;
+       u8 rtc_interrupts_reg = 0;
+       /*
+        * This returns a struct rtc_time. Reading >= 0xc0
+        * means "don't care" or "match all". Only the tm_hour,
+        * tm_min, and tm_sec values are filled in.
+        */

one empty line between above sentences please.

Quoted 17 lines
drivers/rtc/rtc-twl4030.c new file +17 -0
+       memset(&alm->time, 0, sizeof(struct rtc_time));
+       ret = get_rtc_alm_time(&alm->time);
+
+       if (ret)
+               goto out;
+
+       /* Check alarm enabled flag state */
+       ret =
+           ret | twl4030_i2c_read_u8(TWL4030_MODULE_RTC, &rtc_interrupts_reg,
+                                     REG_RTC_INTERRUPTS_REG);
+
+       if (ret)
+               goto out;
+
+       if ((rtc_interrupts_reg & BIT_RTC_INTERRUPTS_REG_IT_ALARM_M) != 0)
+               alm->enabled = 1;
+       else

drivers/rtc/rtc-twl4030.c line 316

Quoted 10 lines
drivers/rtc/rtc-twl4030.c new file +10 -0
+               alm->enabled = 0;
+
+out:
+       return ret;
+}
+
+static int twl4030_rtc_irq_set_state(struct device *dev, int enabled)
+{
+       int ret;
+       /* Allow ints for RTC updates.  */

one empty line between above sentences please.

Quoted 293 lines
drivers/rtc/rtc-twl4030.c new file +293 -0
+       if (enabled)
+               ret = set_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_TIMER_M);
+       else
+               ret = mask_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_TIMER_M);
+
+       return ret;
+}
+
+static int twl4030_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+       unsigned char alarm_data[ALL_TIME_REGS + 1];
+       int ret;
+
+       /* Month range is 01..12 */
+       alm->time.tm_mon++;
+
+       alarm_data[1] = BIN2BCD(alm->time.tm_sec);
+       alarm_data[2] = BIN2BCD(alm->time.tm_min);
+       alarm_data[3] = BIN2BCD(alm->time.tm_hour);
+       alarm_data[4] = BIN2BCD(alm->time.tm_mday);
+       alarm_data[5] = BIN2BCD(alm->time.tm_mon);
+       alarm_data[6] = BIN2BCD(alm->time.tm_year);
+
+       /* update all the alarm registers in one shot */
+       ret = twl4030_i2c_write(TWL4030_MODULE_RTC, alarm_data,
+                               REG_ALARM_SECONDS_REG, ALL_TIME_REGS);
+       if (ret) {
+               printk(KERN_ERR "twl4030: twl4030_i2c_write error.\n");
+               goto out;
+       }
+
+       ret = twl4030_rtc_alarm_irq_set_state(dev, alm->enabled);
+out:
+       return ret;
+}
+
+#ifdef CONFIG_RTC_INTF_DEV
+
+static int twl4030_rtc_ioctl(struct device *dev, unsigned int cmd,
+                            unsigned long arg)
+{
+
+       switch (cmd) {
+       case RTC_AIE_OFF:
+               return twl4030_rtc_alarm_irq_set_state(dev, 0);
+       case RTC_AIE_ON:
+               return twl4030_rtc_alarm_irq_set_state(dev, 1);
+       case RTC_UIE_OFF:
+               /* Mask ints from RTC updates.  */
+               return twl4030_rtc_irq_set_state(dev, 0);
+       case RTC_UIE_ON:
+               /* Allow ints for RTC updates.  */
+               return twl4030_rtc_irq_set_state(dev, 1);
+       case RTC_EPOCH_READ:
+               return put_user(epoch, (unsigned long *)arg);
+       case RTC_EPOCH_SET:
+               /*
+                * There were no RTC clocks before 1900.
+                */
+               if (arg < 1900)
+                       return -EINVAL;
+
+               if (!capable(CAP_SYS_TIME))
+                       return -EACCES;
+
+               epoch = arg;
+               return 0;
+       default:
+               return -ENOIOCTLCMD;
+       }
+}
+
+#else
+#define        omap_rtc_ioctl  NULL
+#endif
+
+static irqreturn_t twl4030_rtc_interrupt(int irq, void *rtc)
+{
+       unsigned long events = 0;
+       int ret = IRQ_NONE;
+       int res;
+       u8 rd_reg;
+
+       /* clear the RTC interrupt in TWL4030 power module */
+       res = twl4030_i2c_read_u8(TWL4030_MODULE_INT, &rd_reg, REG_PWR_ISR1);
+       if (res)
+               goto out;
+
+       /* Check if interrupt is sourced by RTC */
+       if (!(rd_reg & PWR_RTC_INT_CLR))
+               goto out;
+
+       rd_reg |= PWR_RTC_INT_CLR;
+       res = twl4030_i2c_write_u8(TWL4030_MODULE_INT, rd_reg, REG_PWR_ISR1);
+       if (res)
+               goto out;
+
+       res = twl4030_rtc_read_u8(&rd_reg, REG_RTC_STATUS_REG);
+       if (res)
+               goto out;
+       /*
+        * Figure out source of interrupt: ALARM or TIMER in RTC_STATUS_REG.
+        * only one (ALARM or RTC) interrupt source may be enabled
+        * at time, we also could check our results
+        * by reading RTS_INTERRUPTS_REGISTER[IT_TIMER,IT_ALARM]
+        */
+       if (rd_reg & BIT_RTC_STATUS_REG_ALARM_M)
+               events |= RTC_IRQF | RTC_AF;
+       else
+               events |= RTC_IRQF | RTC_UF;
+
+       res = twl4030_rtc_write_u8(rd_reg | BIT_RTC_STATUS_REG_ALARM_M,
+                                  REG_RTC_STATUS_REG);
+       if (res)
+               goto out;
+       /*
+        * Workaround for strange behaviour with T2. Need to write into ISR
+        * register one more time to clear the interrupt. Otherwise, the same
+        * RTC event generates 2 interrupts in a row.
+        * (no errata document available)
+        */
+       res = twl4030_i2c_read_u8(TWL4030_MODULE_INT, &rd_reg, REG_PWR_ISR1);
+       if (res)
+               goto out;
+
+       rd_reg |= PWR_RTC_INT_CLR;
+       res = twl4030_i2c_write_u8(TWL4030_MODULE_INT, rd_reg, REG_PWR_ISR1);
+       if (res)
+               goto out;
+
+       /* Notify RTC core on event */
+       rtc_update_irq(rtc, 1, events);
+
+       ret = IRQ_HANDLED;
+out:
+       return ret;
+}
+
+static struct rtc_class_ops twl4030_rtc_ops = {
+       .ioctl = twl4030_rtc_ioctl,
+       .read_time = twl4030_rtc_read_time,
+       .set_time = twl4030_rtc_set_time,
+       .read_alarm = twl4030_rtc_read_alarm,
+       .set_alarm = twl4030_rtc_set_alarm,
+};
+
+static int __devinit twl4030_rtc_probe(struct platform_device *pdev)
+{
+       struct twl4030rtc_platform_data *pdata = pdev->dev.platform_data;
+       struct rtc_device *rtc;
+       int ret = 0;
+       u8 rd_reg;
+
+       if (pdata != NULL && pdata->init != NULL) {
+               ret = pdata->init();
+               if (ret < 0)
+                       goto out;
+       }
+
+       rtc = rtc_device_register(pdev->name,
+                                 &pdev->dev, &twl4030_rtc_ops, THIS_MODULE);
+       if (IS_ERR(rtc)) {
+               ret = -EINVAL;
+               dev_err(&pdev->dev, "can't register RTC device, err %ld\n",
+                       PTR_ERR(rtc));
+               goto out0;
+
+       }
+
+       platform_set_drvdata(pdev, rtc);
+
+       ret = twl4030_rtc_read_u8(&rd_reg, REG_RTC_STATUS_REG);
+
+       if (ret < 0)
+               goto out1;
+
+       if (rd_reg & BIT_RTC_STATUS_REG_POWER_UP_M)
+               dev_warn(&pdev->dev, "Power up reset detected.\n");
+
+       if (rd_reg & BIT_RTC_STATUS_REG_ALARM_M)
+               dev_warn(&pdev->dev, "Pending Alarm interrupt detected.\n");
+
+       /* Clear RTC Power up reset and pending alarm interrupts */
+       ret = twl4030_rtc_write_u8(rd_reg, REG_RTC_STATUS_REG);
+       if (ret < 0)
+               goto out1;
+
+       ret = request_irq(TWL4030_MODIRQ_PWR, twl4030_rtc_interrupt,
+                         IRQF_DISABLED | IRQF_SHARED, rtc->dev.bus_id, rtc);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "IRQ is not free.\n");
+               goto out1;
+       }
+
+       /* Check RTC module status, Enable if it is off */
+       ret = twl4030_rtc_read_u8(&rd_reg, REG_RTC_CTRL_REG);
+       if (ret < 0)
+               goto out2;
+
+       if (!(rd_reg & BIT_RTC_CTRL_REG_STOP_RTC_M)) {
+               dev_info(&pdev->dev, "Enabling TWL4030-RTC.\n");
+               rd_reg = BIT_RTC_CTRL_REG_STOP_RTC_M;
+               ret = twl4030_rtc_write_u8(rd_reg, REG_RTC_CTRL_REG);
+               if (ret < 0)
+                       goto out2;
+       }
+
+       ret = twl4030_i2c_read_u8(TWL4030_MODULE_INT, &rd_reg, REG_PWR_IMR1);
+       if (ret < 0)
+               goto out2;
+
+       rd_reg &= PWR_RTC_IT_UNMASK;
+       /* MASK PWR - we will need this */
+       ret = twl4030_i2c_write_u8(TWL4030_MODULE_INT, rd_reg, REG_PWR_IMR1);
+       if (ret < 0)
+               goto out2;
+
+       ret = twl4030_i2c_read_u8(TWL4030_MODULE_INT, &rd_reg, REG_PWR_EDR1);
+       if (ret < 0)
+               goto out2;
+
+       /* Rising edge detection enabled, needed for RTC alarm */
+       rd_reg |= 0x80;
+       ret = twl4030_i2c_write_u8(TWL4030_MODULE_INT, rd_reg, REG_PWR_EDR1);
+       if (ret < 0)
+               goto out2;
+
+       return ret;
+
+
+out2:
+       free_irq(TWL4030_MODIRQ_PWR, rtc);
+out1:
+       rtc_device_unregister(rtc);
+out0:
+       if (pdata != NULL && pdata->exit != NULL)
+               pdata->exit();
+out:
+       return ret;
+}
+
+/*
+ * Disable all TWL4030 RTC module interrupts.
+ * Sets status flag to free.
+ */
+static int __devexit twl4030_rtc_remove(struct platform_device *pdev)
+{
+       /* leave rtc running, but disable irqs */
+       struct twl4030rtc_platform_data *pdata = pdev->dev.platform_data;
+       struct rtc_device *rtc = platform_get_drvdata(pdev);
+
+       mask_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_ALARM_M);
+       mask_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_TIMER_M);
+
+       free_irq(TWL4030_MODIRQ_PWR, rtc);
+
+       if (pdata != NULL && pdata->exit != NULL)
+               pdata->exit();
+
+       rtc_device_unregister(rtc);
+       return 0;
+}
+
+static void twl4030_rtc_shutdown(struct platform_device *pdev)
+{
+       twl4030_rtc_alarm_irq_set_state(&pdev->dev, 0);
+       twl4030_rtc_irq_set_state(&pdev->dev, 0);
+}
+
+#ifdef CONFIG_PM
+
+static unsigned char irqstat = 0;
+
+static int twl4030_rtc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       get_rtc_irq_bit(&irqstat);
+
+       mask_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_TIMER_M |
+                        BIT_RTC_INTERRUPTS_REG_IT_ALARM_M);
+       return 0;
+}
+
+static int twl4030_rtc_resume(struct platform_device *pdev)
+{
+       set_rtc_irq_bit(irqstat);
+       return 0;
+}
+#else
+#define twl4030_rtc_suspend NULL
+#define twl4030_rtc_resume  NULL
+#endif
+
+MODULE_ALIAS("twl4030_rtc");

drivers/rtc/rtc-twl4030.c line 619

Quoted 10 lines
drivers/rtc/rtc-twl4030.c new file +10 -0
+static struct platform_driver twl4030rtc_driver = {
+       .probe = twl4030_rtc_probe,
+       .remove = __devexit_p(twl4030_rtc_remove),
+       .shutdown = twl4030_rtc_shutdown,
+       .suspend = twl4030_rtc_suspend,
+       .resume = twl4030_rtc_resume,
+       .driver = {
+                  .owner = THIS_MODULE,
+                  .name = "twl4030_rtc",
+                  },

indentation problem?

Quoted 17 lines
drivers/rtc/rtc-twl4030.c new file +17 -0
+};
+
+static int __init twl4030_rtc_init(void)
+{
+       return platform_driver_register(&twl4030rtc_driver);
+}
+
+static void __exit twl4030_rtc_exit(void)
+{
+       platform_driver_unregister(&twl4030rtc_driver);
+}
+
+MODULE_AUTHOR("Texas Instruments, MontaVista Software");
+MODULE_LICENSE("GPL");;
+
+module_init(twl4030_rtc_init);
+module_exit(twl4030_rtc_exit);
Signature · 1 line
--Trilok Soni
#2 lore raw

Hello Trilok,

thanks for the review.

Quoted 12 lines · Trilok Soni
Quoted 8 lines
drivers/rtc/Kconfig +5 -0
+config RTC_DRV_OMAP2430_TWL4030
+       tristate "OMAP243X TWL4030 Real Time Clock"
+       depends on RTC_CLASS && TWL4030_CORE
+       help
+         Support for TWL4030 companion chip internal RTC on OMAP2

SDP2430 board
+

This driver should work on other boards having TWL4030 chip, so make help text more generic please. It is better if you don't have OMAP2430 in RTC_DRV_OMAP2430_TWL4030

Agreed. We should avoid sticking to a particular board in general. This issue, as well as codestyle issues you found, will be fixed.

Regards,
Dmitry