Patchwork [v2] rtc: add rv3029c2 RTC support

login
register
mail settings
Submitter Heiko Schocher
Date Jan. 11, 2011, 7:03 a.m.
Message ID <1294729397-22151-1-git-send-email-hs@denx.de>
Download mbox | patch
Permalink /patch/78277/
State New
Headers show

Comments

Heiko Schocher - Jan. 11, 2011, 7:03 a.m.
repost patch from Gregory Hermant <gregory.hermant@calao-systems.com>
original patch:
http://kerneltrap.org/mailarchive/linux-kernel/2010/6/6/4580032

Signed-off-by: Heiko Schocher <hs@denx.de>
Signed-off-by: Gregory Hermant <gregory.hermant@calao-systems.com>
cc: Wan ZongShun <mcuos.com@gmail.com>
cc: Alessandro Zummo <a.zummo@towertech.it>
cc: rtc-linux@googlegroups.com
cc: Wolfram Sang <w.sang@pengutronix.de>
---
changes since Gregorys commit:
- add set_alarm function
- corrected 12/24h handling
- add comments from Wan ZongShun

- changes since v1:
  add comments from Wolfram Sang
  - add Signed-off-by: Gregory Hermant <gregory.hermant@calao-systems.com>
  - delete DRV_VERSION
  - no use of BUG_ON
  - use smbus_block functions
  - Sections of probe() and remove() to __devinit/exit
  - remove check if we have a rv3029c2 rtc, as it was not a really
    good test.

 ./scripts/checkpatch.pl 0001-rtc-add-rv3029c2-RTC-support.patch
total: 0 errors, 0 warnings, 454 lines checked

20110111/0001-rtc-add-rv3029c2-RTC-support.patch has no obvious style problems and is ready for submission.

 drivers/rtc/Kconfig        |    9 +
 drivers/rtc/Makefile       |    1 +
 drivers/rtc/rtc-rv3029c2.c |  432 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 442 insertions(+), 0 deletions(-)
 create mode 100644 drivers/rtc/rtc-rv3029c2.c
Heiko Schocher - March 8, 2011, 8:35 a.m.
Hello

Heiko Schocher wrote:
> repost patch from Gregory Hermant <gregory.hermant@calao-systems.com>
> original patch:
> http://kerneltrap.org/mailarchive/linux-kernel/2010/6/6/4580032
> 
> Signed-off-by: Heiko Schocher <hs@denx.de>
> Signed-off-by: Gregory Hermant <gregory.hermant@calao-systems.com>
> cc: Wan ZongShun <mcuos.com@gmail.com>
> cc: Alessandro Zummo <a.zummo@towertech.it>
> cc: rtc-linux@googlegroups.com
> cc: Wolfram Sang <w.sang@pengutronix.de>
> ---
> changes since Gregorys commit:
> - add set_alarm function
> - corrected 12/24h handling
> - add comments from Wan ZongShun
> 
> - changes since v1:
>   add comments from Wolfram Sang
>   - add Signed-off-by: Gregory Hermant <gregory.hermant@calao-systems.com>
>   - delete DRV_VERSION
>   - no use of BUG_ON
>   - use smbus_block functions
>   - Sections of probe() and remove() to __devinit/exit
>   - remove check if we have a rv3029c2 rtc, as it was not a really
>     good test.
> 
>  ./scripts/checkpatch.pl 0001-rtc-add-rv3029c2-RTC-support.patch
> total: 0 errors, 0 warnings, 454 lines checked
> 
> 20110111/0001-rtc-add-rv3029c2-RTC-support.patch has no obvious style problems and is ready for submission.
> 
>  drivers/rtc/Kconfig        |    9 +
>  drivers/rtc/Makefile       |    1 +
>  drivers/rtc/rtc-rv3029c2.c |  432 ++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 442 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/rtc/rtc-rv3029c2.c

I got no more reactions to this patch, and it is pending now for a while,
so can it go in mainline, or are there more issues with it?

bye,
Heiko
Wolfram Sang - March 8, 2011, 8:46 a.m.
Hi Heiko,

On Tue, Jan 11, 2011 at 08:03:17AM +0100, Heiko Schocher wrote:

> repost patch from Gregory Hermant <gregory.hermant@calao-systems.com>
> original patch:
> http://kerneltrap.org/mailarchive/linux-kernel/2010/6/6/4580032
> 
> Signed-off-by: Heiko Schocher <hs@denx.de>
> Signed-off-by: Gregory Hermant <gregory.hermant@calao-systems.com>
> cc: Wan ZongShun <mcuos.com@gmail.com>
> cc: Alessandro Zummo <a.zummo@towertech.it>
> cc: rtc-linux@googlegroups.com
> cc: Wolfram Sang <w.sang@pengutronix.de>

One minor nit, then you can add my

Acked-by: Wolfram Sang <w.sang@pengutronix.de>

> +static struct i2c_driver rv3029c2_driver = {
> +	.driver = {
> +		.name = "rtc-rv3029c2",
> +	},
> +	.probe = rv3029c2_probe,
> +	.remove = rv3029c2_remove,

__devexit_p()

> +	.id_table = rv3029c2_id,
> +};
Heiko Schocher - March 29, 2011, 10:51 a.m.
Hello,

Heiko Schocher wrote:
> Hello
> 
> Heiko Schocher wrote:
>> repost patch from Gregory Hermant <gregory.hermant@calao-systems.com>
>> original patch:
>> http://kerneltrap.org/mailarchive/linux-kernel/2010/6/6/4580032
>>
>> Signed-off-by: Heiko Schocher <hs@denx.de>
>> Signed-off-by: Gregory Hermant <gregory.hermant@calao-systems.com>
>> cc: Wan ZongShun <mcuos.com@gmail.com>
>> cc: Alessandro Zummo <a.zummo@towertech.it>
>> cc: rtc-linux@googlegroups.com
>> cc: Wolfram Sang <w.sang@pengutronix.de>
>> ---
>> changes since Gregorys commit:
>> - add set_alarm function
>> - corrected 12/24h handling
>> - add comments from Wan ZongShun
>>
>> - changes since v1:
>>   add comments from Wolfram Sang
>>   - add Signed-off-by: Gregory Hermant <gregory.hermant@calao-systems.com>
>>   - delete DRV_VERSION
>>   - no use of BUG_ON
>>   - use smbus_block functions
>>   - Sections of probe() and remove() to __devinit/exit
>>   - remove check if we have a rv3029c2 rtc, as it was not a really
>>     good test.
>>
>>  ./scripts/checkpatch.pl 0001-rtc-add-rv3029c2-RTC-support.patch
>> total: 0 errors, 0 warnings, 454 lines checked
>>
>> 20110111/0001-rtc-add-rv3029c2-RTC-support.patch has no obvious style problems and is ready for submission.
>>
>>  drivers/rtc/Kconfig        |    9 +
>>  drivers/rtc/Makefile       |    1 +
>>  drivers/rtc/rtc-rv3029c2.c |  432 ++++++++++++++++++++++++++++++++++++++++++++
>>  3 files changed, 442 insertions(+), 0 deletions(-)
>>  create mode 100644 drivers/rtc/rtc-rv3029c2.c
> 
> I got no more reactions to this patch, and it is pending now for a while,
> so can it go in mainline, or are there more issues with it?

ping?

bye,
Heiko
Wolfram Sang - March 29, 2011, 10:56 a.m.
> > I got no more reactions to this patch, and it is pending now for a while,
> > so can it go in mainline, or are there more issues with it?
> 
> ping?

What about my __devexit_p comment? Also, you could add Andrew Morton
when sending v3?
Heiko Schocher - March 29, 2011, 11:42 a.m.
Hello Wolfram,

Wolfram Sang wrote:
>>> I got no more reactions to this patch, and it is pending now for a while,
>>> so can it go in mainline, or are there more issues with it?
>> ping?
> 
> What about my __devexit_p comment? Also, you could add Andrew Morton

Hmm.. seems I missed that ... sorry.

> when sending v3?

Ok, i send a v3 soon

bye,
Heiko

Patch

diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 2883428..0f6c119 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -354,6 +354,15 @@  config RTC_DRV_RX8025
 	  This driver can also be built as a module. If so, the module
 	  will be called rtc-rx8025.
 
+config RTC_DRV_RV3029C2
+	tristate "Micro Crystal RTC"
+	help
+	  If you say yes here you get support for the Micro Crystal
+	  RV3029-C2 RTC chips.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called rtc-rv3029c2.
+
 endif # I2C
 
 comment "SPI RTC drivers"
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 4c2832d..16c0cdc 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -82,6 +82,7 @@  obj-$(CONFIG_RTC_DRV_RP5C01)	+= rtc-rp5c01.o
 obj-$(CONFIG_RTC_DRV_RS5C313)	+= rtc-rs5c313.o
 obj-$(CONFIG_RTC_DRV_RS5C348)	+= rtc-rs5c348.o
 obj-$(CONFIG_RTC_DRV_RS5C372)	+= rtc-rs5c372.o
+obj-$(CONFIG_RTC_DRV_RV3029C2)	+= rtc-rv3029c2.o
 obj-$(CONFIG_RTC_DRV_RX8025)	+= rtc-rx8025.o
 obj-$(CONFIG_RTC_DRV_RX8581)	+= rtc-rx8581.o
 obj-$(CONFIG_RTC_DRV_S35390A)	+= rtc-s35390a.o
diff --git a/drivers/rtc/rtc-rv3029c2.c b/drivers/rtc/rtc-rv3029c2.c
new file mode 100644
index 0000000..e6c6d2e
--- /dev/null
+++ b/drivers/rtc/rtc-rv3029c2.c
@@ -0,0 +1,432 @@ 
+/*
+ * Micro Crystal RV-3029C2 rtc class driver
+ *
+ * Author: Gregory Hermant <gregory.hermant@calao-systems.com>
+ *
+ * based on previously existing rtc class drivers
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * NOTE: Currently this driver only supports the bare minimum for read
+ * and write the RTC and alarms. The extra features provided by this chip
+ * (trickle charger, eeprom, T° compensation) are unavailable.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/bcd.h>
+#include <linux/rtc.h>
+
+/* Register map */
+/* control section */
+#define RV3029C2_ONOFF_CTRL		0x00
+#define RV3029C2_IRQ_CTRL		0x01
+#define RV3029C2_IRQ_CTRL_AIE		(1 << 0)
+#define RV3029C2_IRQ_FLAGS		0x02
+#define RV3029C2_IRQ_FLAGS_AF		(1 << 0)
+#define RV3029C2_STATUS			0x03
+#define RV3029C2_STATUS_VLOW1		(1 << 2)
+#define RV3029C2_STATUS_VLOW2		(1 << 3)
+#define RV3029C2_STATUS_SR		(1 << 4)
+#define RV3029C2_STATUS_EEBUSY		(1 << 7)
+#define RV3029C2_RST_CTRL		0x04
+#define RV3029C2_CONTROL_SECTION_LEN	0x05
+
+/* watch section */
+#define RV3029C2_W_SEC			0x08
+#define RV3029C2_W_MINUTES		0x09
+#define RV3029C2_W_HOURS		0x0A
+#define RV3029C2_REG_HR_12_24		(1<<6)  /* 24h/12h mode */
+#define RV3029C2_REG_HR_PM		(1<<5)  /* PM/AM bit in 12h mode */
+#define RV3029C2_W_DATE			0x0B
+#define RV3029C2_W_DAYS			0x0C
+#define RV3029C2_W_MONTHS		0x0D
+#define RV3029C2_W_YEARS		0x0E
+#define RV3029C2_WATCH_SECTION_LEN	0x07
+
+/* alarm section */
+#define RV3029C2_A_SC			0x10
+#define RV3029C2_A_MN			0x11
+#define RV3029C2_A_HR			0x12
+#define RV3029C2_A_DT			0x13
+#define RV3029C2_A_DW			0x14
+#define RV3029C2_A_MO			0x15
+#define RV3029C2_A_YR			0x16
+#define RV3029C2_ALARM_SECTION_LEN	0x07
+
+/* timer section */
+#define RV3029C2_TIMER_LOW		0x18
+#define RV3029C2_TIMER_HIGH		0x19
+
+/* temperature section */
+#define RV3029C2_TEMP_PAGE		0x20
+
+/* eeprom data section */
+#define RV3029C2_E2P_EEDATA1		0x28
+#define RV3029C2_E2P_EEDATA2		0x29
+
+/* eeprom control section */
+#define RV3029C2_CONTROL_E2P_EECTRL	0x30
+#define RV3029C2_TRICKLE_1K		(1<<0)  /*  1K resistance */
+#define RV3029C2_TRICKLE_5K		(1<<1)  /*  5K resistance */
+#define RV3029C2_TRICKLE_20K		(1<<2)  /* 20K resistance */
+#define RV3029C2_TRICKLE_80K		(1<<3)  /* 80K resistance */
+#define RV3029C2_CONTROL_E2P_XTALOFFSET	0x31
+#define RV3029C2_CONTROL_E2P_QCOEF	0x32
+#define RV3029C2_CONTROL_E2P_TURNOVER	0x33
+
+/* user ram section */
+#define RV3029C2_USR1_RAM_PAGE		0x38
+#define RV3029C2_USR1_SECTION_LEN	0x04
+#define RV3029C2_USR2_RAM_PAGE		0x3C
+#define RV3029C2_USR2_SECTION_LEN	0x04
+
+static int
+rv3029c2_i2c_read_regs(struct i2c_client *client, u8 reg, u8 *buf,
+	unsigned len)
+{
+	int ret;
+
+	if ((reg > RV3029C2_USR1_RAM_PAGE + 7) ||
+		(reg + len > RV3029C2_USR1_RAM_PAGE + 8))
+		return -EINVAL;
+
+	ret = i2c_smbus_read_i2c_block_data(client, reg, len, buf);
+	if (ret < 0)
+		return ret;
+	if (ret < len)
+		return -EIO;
+	return 0;
+}
+
+static int
+rv3029c2_i2c_write_regs(struct i2c_client *client, u8 reg, u8 const buf[],
+			unsigned len)
+{
+	if ((reg > RV3029C2_USR1_RAM_PAGE + 7) ||
+		(reg + len > RV3029C2_USR1_RAM_PAGE + 8))
+		return -EINVAL;
+
+	return i2c_smbus_write_i2c_block_data(client, reg, len, buf);
+}
+
+static int
+rv3029c2_i2c_get_sr(struct i2c_client *client)
+{
+	int sr = i2c_smbus_read_byte_data(client, RV3029C2_STATUS);
+
+	dev_dbg(&client->dev, "status = 0x%.2x (%d)\n", sr, sr);
+	if (sr < 0)
+		return -EIO;
+	return sr;
+}
+
+static int
+rv3029c2_i2c_read_time(struct i2c_client *client, struct rtc_time *tm)
+{
+	int sr;
+	u8 regs[RV3029C2_WATCH_SECTION_LEN] = { 0, };
+
+	sr = rv3029c2_i2c_get_sr(client);
+	if (sr < 0) {
+		dev_err(&client->dev, "%s: reading SR failed\n", __func__);
+		return -EIO;
+	}
+
+	sr = rv3029c2_i2c_read_regs(client, RV3029C2_W_SEC , regs,
+					RV3029C2_WATCH_SECTION_LEN);
+	if (sr < 0) {
+		dev_err(&client->dev, "%s: reading RTC section failed\n",
+			__func__);
+		return sr;
+	}
+
+	tm->tm_sec = bcd2bin(regs[RV3029C2_W_SEC-RV3029C2_W_SEC]);
+	tm->tm_min = bcd2bin(regs[RV3029C2_W_MINUTES-RV3029C2_W_SEC]);
+
+	/* HR field has a more complex interpretation */
+	{
+		const u8 _hr = regs[RV3029C2_W_HOURS-RV3029C2_W_SEC];
+		if (_hr & RV3029C2_REG_HR_12_24) {
+			/* 12h format */
+			tm->tm_hour = bcd2bin(_hr & 0x1f);
+			if (_hr & RV3029C2_REG_HR_PM)	/* PM flag set */
+				tm->tm_hour += 12;
+		} else /* 24h format */
+			tm->tm_hour = bcd2bin(_hr & 0x3f);
+	}
+
+	tm->tm_mday = bcd2bin(regs[RV3029C2_W_DATE-RV3029C2_W_SEC]);
+	tm->tm_mon = bcd2bin(regs[RV3029C2_W_MONTHS-RV3029C2_W_SEC]) - 1;
+	tm->tm_year = bcd2bin(regs[RV3029C2_W_YEARS-RV3029C2_W_SEC]) + 100;
+	tm->tm_wday = bcd2bin(regs[RV3029C2_W_DAYS-RV3029C2_W_SEC]) - 1;
+
+	return rtc_valid_tm(tm);
+}
+
+static int rv3029c2_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+	return rv3029c2_i2c_read_time(to_i2c_client(dev), tm);
+}
+
+static int
+rv3029c2_i2c_read_alarm(struct i2c_client *client, struct rtc_wkalrm *alarm)
+{
+	struct rtc_time *const tm = &alarm->time;
+	int sr;
+	u8 regs[8];
+
+	sr = rv3029c2_i2c_get_sr(client);
+	if (sr < 0) {
+		dev_err(&client->dev, "%s: reading SR failed\n", __func__);
+		return -EIO;
+	}
+
+	sr = rv3029c2_i2c_read_regs(client, RV3029C2_A_SC, regs,
+					RV3029C2_ALARM_SECTION_LEN);
+
+	if (sr < 0) {
+		dev_err(&client->dev, "%s: reading alarm section failed\n",
+			__func__);
+		return sr;
+	}
+
+	tm->tm_sec = bcd2bin(regs[RV3029C2_A_SC-RV3029C2_A_SC] & 0x7f);
+	tm->tm_min = bcd2bin(regs[RV3029C2_A_MN-RV3029C2_A_SC] & 0x7f);
+	tm->tm_hour = bcd2bin(regs[RV3029C2_A_HR-RV3029C2_A_SC] & 0x3f);
+	tm->tm_mday = bcd2bin(regs[RV3029C2_A_DT-RV3029C2_A_SC] & 0x3f);
+	tm->tm_mon = bcd2bin(regs[RV3029C2_A_MO-RV3029C2_A_SC] & 0x1f) - 1;
+	tm->tm_year = bcd2bin(regs[RV3029C2_A_YR-RV3029C2_A_SC] & 0x7f) + 100;
+	tm->tm_wday = bcd2bin(regs[RV3029C2_A_DW-RV3029C2_A_SC] & 0x07) - 1;
+
+	return 0;
+}
+
+static int
+rv3029c2_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+	return rv3029c2_i2c_read_alarm(to_i2c_client(dev), alarm);
+}
+
+static int rv3029c2_rtc_i2c_alarm_set_irq(struct i2c_client *client,
+					int enable)
+{
+	int ret;
+	u8 buf[1];
+
+	/* enable AIE irq */
+	ret = rv3029c2_i2c_read_regs(client, RV3029C2_IRQ_CTRL,	buf, 1);
+	if (ret < 0) {
+		dev_err(&client->dev, "can't read INT reg\n");
+		return ret;
+	}
+	if (enable)
+		buf[0] |= RV3029C2_IRQ_CTRL_AIE;
+	else
+		buf[0] &= ~RV3029C2_IRQ_CTRL_AIE;
+
+	ret = rv3029c2_i2c_write_regs(client, RV3029C2_IRQ_CTRL, buf, 1);
+	if (ret < 0) {
+		dev_err(&client->dev, "can't set INT reg\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int rv3029c2_rtc_i2c_set_alarm(struct i2c_client *client,
+					struct rtc_wkalrm *alarm)
+{
+	struct rtc_time *const tm = &alarm->time;
+	int sr;
+	int ret;
+	u8 regs[8];
+
+	/*
+	 * The clock has an 8 bit wide bcd-coded register (they never learn)
+	 * for the year. tm_year is an offset from 1900 and we are interested
+	 * in the 2000-2099 range, so any value less than 100 is invalid.
+	*/
+	if (tm->tm_year < 100)
+		return -EINVAL;
+
+	sr = rv3029c2_i2c_get_sr(client);
+	if (sr < 0) {
+		dev_err(&client->dev, "%s: reading SR failed\n", __func__);
+		return -EIO;
+	}
+	regs[RV3029C2_A_SC-RV3029C2_A_SC] = bin2bcd(tm->tm_sec & 0x7f);
+	regs[RV3029C2_A_MN-RV3029C2_A_SC] = bin2bcd(tm->tm_min & 0x7f);
+	regs[RV3029C2_A_HR-RV3029C2_A_SC] = bin2bcd(tm->tm_hour & 0x3f);
+	regs[RV3029C2_A_DT-RV3029C2_A_SC] = bin2bcd(tm->tm_mday & 0x3f);
+	regs[RV3029C2_A_MO-RV3029C2_A_SC] = bin2bcd((tm->tm_mon & 0x1f) - 1);
+	regs[RV3029C2_A_DW-RV3029C2_A_SC] = bin2bcd((tm->tm_wday & 7) - 1);
+	regs[RV3029C2_A_YR-RV3029C2_A_SC] = bin2bcd((tm->tm_year & 0x7f) - 100);
+
+	sr = rv3029c2_i2c_write_regs(client, RV3029C2_A_SC, regs,
+					RV3029C2_ALARM_SECTION_LEN);
+	if (sr < 0)
+		return sr;
+
+	if (alarm->enabled) {
+		u8 buf[1];
+
+		/* clear AF flag */
+		ret = rv3029c2_i2c_read_regs(client, RV3029C2_IRQ_FLAGS,
+						buf, 1);
+		if (ret < 0) {
+			dev_err(&client->dev, "can't read alarm flag\n");
+			return ret;
+		}
+		buf[0] &= ~RV3029C2_IRQ_FLAGS_AF;
+		ret = rv3029c2_i2c_write_regs(client, RV3029C2_IRQ_FLAGS,
+						buf, 1);
+		if (ret < 0) {
+			dev_err(&client->dev, "can't set alarm flag\n");
+			return ret;
+		}
+		/* enable AIE irq */
+		ret = rv3029c2_rtc_i2c_alarm_set_irq(client, 1);
+		if (ret)
+			return ret;
+
+		dev_dbg(&client->dev, "alarm IRQ armed\n");
+	} else {
+		/* disable AIE irq */
+		ret = rv3029c2_rtc_i2c_alarm_set_irq(client, 1);
+		if (ret)
+			return ret;
+
+		dev_dbg(&client->dev, "alarm IRQ disabled\n");
+	}
+
+	return 0;
+}
+
+static int rv3029c2_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+	return rv3029c2_rtc_i2c_set_alarm(to_i2c_client(dev), alarm);
+}
+
+static int
+rv3029c2_i2c_set_time(struct i2c_client *client, struct rtc_time const *tm)
+{
+	u8 regs[8];
+	int sr;
+
+	/*
+	 * The clock has an 8 bit wide bcd-coded register (they never learn)
+	 * for the year. tm_year is an offset from 1900 and we are interested
+	 * in the 2000-2099 range, so any value less than 100 is invalid.
+	*/
+	if (tm->tm_year < 100)
+		return -EINVAL;
+
+	regs[RV3029C2_W_SEC-RV3029C2_W_SEC] = bin2bcd(tm->tm_sec);
+	regs[RV3029C2_W_MINUTES-RV3029C2_W_SEC] = bin2bcd(tm->tm_min);
+	regs[RV3029C2_W_HOURS-RV3029C2_W_SEC] = bin2bcd(tm->tm_hour);
+	regs[RV3029C2_W_DATE-RV3029C2_W_SEC] = bin2bcd(tm->tm_mday);
+	regs[RV3029C2_W_MONTHS-RV3029C2_W_SEC] = bin2bcd(tm->tm_mon+1);
+	regs[RV3029C2_W_DAYS-RV3029C2_W_SEC] = bin2bcd((tm->tm_wday & 7)+1);
+	regs[RV3029C2_W_YEARS-RV3029C2_W_SEC] = bin2bcd(tm->tm_year - 100);
+
+	sr = rv3029c2_i2c_get_sr(client);
+	if (sr < 0) {
+		dev_err(&client->dev, "%s: reading SR failed\n", __func__);
+		return sr;
+	}
+
+	sr = rv3029c2_i2c_write_regs(client, RV3029C2_W_SEC, regs,
+					RV3029C2_WATCH_SECTION_LEN);
+	if (sr < 0)
+		return sr;
+
+	return 0;
+}
+
+static int rv3029c2_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+	return rv3029c2_i2c_set_time(to_i2c_client(dev), tm);
+}
+
+static const struct rtc_class_ops rv3029c2_rtc_ops = {
+	.read_time	= rv3029c2_rtc_read_time,
+	.set_time	= rv3029c2_rtc_set_time,
+	.read_alarm	= rv3029c2_rtc_read_alarm,
+	.set_alarm	= rv3029c2_rtc_set_alarm,
+};
+
+static struct i2c_device_id rv3029c2_id[] = {
+	{ "rv3029c2", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, rv3029c2_id);
+
+static int __devinit
+rv3029c2_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+	struct rtc_device *rtc;
+	int rc = 0;
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_EMUL))
+		return -ENODEV;
+
+	rtc = rtc_device_register(client->name,
+				&client->dev, &rv3029c2_rtc_ops,
+				THIS_MODULE);
+
+	if (IS_ERR(rtc))
+		return PTR_ERR(rtc);
+
+	i2c_set_clientdata(client, rtc);
+
+	rc = rv3029c2_i2c_get_sr(client);
+	if (rc < 0) {
+		dev_err(&client->dev, "reading status failed\n");
+		goto exit_unregister;
+	}
+
+	return 0;
+
+exit_unregister:
+	rtc_device_unregister(rtc);
+
+	return rc;
+}
+
+static int __devexit rv3029c2_remove(struct i2c_client *client)
+{
+	struct rtc_device *rtc = i2c_get_clientdata(client);
+
+	rtc_device_unregister(rtc);
+
+	return 0;
+}
+
+static struct i2c_driver rv3029c2_driver = {
+	.driver = {
+		.name = "rtc-rv3029c2",
+	},
+	.probe = rv3029c2_probe,
+	.remove = rv3029c2_remove,
+	.id_table = rv3029c2_id,
+};
+
+static int __init rv3029c2_init(void)
+{
+	return i2c_add_driver(&rv3029c2_driver);
+}
+
+static void __exit rv3029c2_exit(void)
+{
+	i2c_del_driver(&rv3029c2_driver);
+}
+
+module_init(rv3029c2_init);
+module_exit(rv3029c2_exit);
+
+MODULE_AUTHOR("Gregory Hermant <gregory.hermant@calao-systems.com>");
+MODULE_DESCRIPTION("Micro Crystal RV3029C2 RTC driver");
+MODULE_LICENSE("GPL");