summaryrefslogtreecommitdiff
path: root/drivers/rtc
diff options
context:
space:
mode:
authorJin Park <jinyoungp@nvidia.com>2011-08-29 11:47:40 +0900
committerVarun Colbert <vcolbert@nvidia.com>2011-08-29 18:06:09 -0700
commit8a6c82747b76aec43901e773dc7b8b35e81b7d10 (patch)
tree6b1b46e6130c6d95b4cc1149bd8a3ab9eba6575b /drivers/rtc
parent334a97811d1832b6a6153d2ff3df9cc68cbcbb9c (diff)
rtc: max77663: Add MAX77663 RTC driver
Add RTC driver for Maxim PMU MAX77663. Bug 849360 Change-Id: Ia7c910a852527f6a7bf5d2622cb1f76fd72222cd Signed-off-by: Jin Park <jinyoungp@nvidia.com> Reviewed-on: http://git-master/r/49584 Reviewed-by: Laxman Dewangan <ldewangan@nvidia.com>
Diffstat (limited to 'drivers/rtc')
-rw-r--r--drivers/rtc/Kconfig10
-rw-r--r--drivers/rtc/Makefile1
-rw-r--r--drivers/rtc/rtc-max77663.c562
3 files changed, 573 insertions, 0 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index a05202a015d2..4c82d428515e 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -223,6 +223,16 @@ config RTC_DRV_MAX8907C
This driver can also be built as a module. If so, the module
will be called rtc-max8907c.
+config RTC_DRV_MAX77663
+ tristate "Maxim MAX77663"
+ depends on MFD_MAX77663
+ help
+ If you say yes here you will get support for the
+ RTC of Maxim MAX77663 PMIC.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-max77663.
+
config RTC_DRV_RS5C372
tristate "Ricoh R2025S/D, RS5C372A/B, RV5C386, RV5C387A"
help
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 20a2b4f15973..aef7a660c195 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -63,6 +63,7 @@ obj-$(CONFIG_RTC_DRV_MAX6900) += rtc-max6900.o
obj-$(CONFIG_RTC_DRV_MAX8925) += rtc-max8925.o
obj-$(CONFIG_RTC_DRV_MAX8907C) += rtc-max8907c.o
obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o
+obj-$(CONFIG_RTC_DRV_MAX77663) += rtc-max77663.o
obj-$(CONFIG_RTC_DRV_MC13783) += rtc-mc13783.o
obj-$(CONFIG_RTC_DRV_MSM6242) += rtc-msm6242.o
obj-$(CONFIG_RTC_DRV_MPC5121) += rtc-mpc5121.o
diff --git a/drivers/rtc/rtc-max77663.c b/drivers/rtc/rtc-max77663.c
new file mode 100644
index 000000000000..f0bcd83624c0
--- /dev/null
+++ b/drivers/rtc/rtc-max77663.c
@@ -0,0 +1,562 @@
+/*
+ * drivers/rtc/rtc-max77663.c
+ * Max77663 RTC driver
+ *
+ * Copyright 2011 Maxim Integrated Products, Inc.
+ * Copyright (C) 2011 NVIDIA Corporation
+ *
+ * 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/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/rtc.h>
+#include <linux/mfd/max77663-core.h>
+
+/* Registers */
+#define MAX77663_RTC_IRQ 0x00
+#define MAX77663_RTC_IRQ_MASK 0x01
+#define MAX77663_RTC_CTRL_MODE 0x02
+#define MAX77663_RTC_CTRL 0x03
+#define MAX77663_RTC_UPDATE0 0x04
+#define MAX77663_RTC_UPDATE1 0x05
+#define MAX77663_RTC_SEC 0x07
+#define MAX77663_RTC_MIN 0x08
+#define MAX77663_RTC_HOUR 0x09
+#define MAX77663_RTC_WEEKDAY 0x0A
+#define MAX77663_RTC_MONTH 0x0B
+#define MAX77663_RTC_YEAR 0x0C
+#define MAX77663_RTC_MONTHDAY 0x0D
+#define MAX77663_RTC_ALARM_SEC1 0x0E
+#define MAX77663_RTC_ALARM_MIN1 0x0F
+#define MAX77663_RTC_ALARM_HOUR1 0x10
+#define MAX77663_RTC_ALARM_WEEKDAY1 0x11
+#define MAX77663_RTC_ALARM_MONTH1 0x12
+#define MAX77663_RTC_ALARM_YEAR1 0x13
+#define MAX77663_RTC_ALARM_MONTHDAY1 0x14
+
+#define RTC_IRQ_60SEC_MASK (1 << 0)
+#define RTC_IRQ_ALARM1_MASK (1 << 1)
+#define RTC_IRQ_ALARM2_MASK (1 << 2)
+#define RTC_IRQ_SMPL_MASK (1 << 3)
+#define RTC_IRQ_1SEC_MASK (1 << 4)
+#define RTC_IRQ_MASK 0x1F
+
+#define BCD_MODE_MASK (1 << 0)
+#define HR_MODE_MASK (1 << 1)
+
+#define WB_UPDATE_MASK (1 << 0)
+#define FLAG_AUTO_CLEAR_MASK (1 << 1)
+#define FREEZE_SEC_MASK (1 << 2)
+#define RTC_WAKE_MASK (1 << 3)
+#define RB_UPDATE_MASK (1 << 4)
+
+#define WB_UPDATE_FLAG_MASK (1 << 0)
+#define RB_UPDATE_FLAG_MASK (1 << 1)
+
+#define SEC_MASK 0x7F
+#define MIN_MASK 0x7F
+#define HOUR_MASK 0x3F
+#define WEEKDAY_MASK 0x7F
+#define MONTH_MASK 0x1F
+#define YEAR_MASK 0xFF
+#define MONTHDAY_MASK 0x3F
+
+#define ALARM_EN_MASK 0x80
+#define ALARM_EN_SHIFT 7
+
+#define RTC_UPDATE_RETRIES 20
+#define RTC_YEAR_BASE 100
+#define RTC_YEAR_MAX 99
+
+enum {
+ RTC_SEC,
+ RTC_MIN,
+ RTC_HOUR,
+ RTC_WEEKDAY,
+ RTC_MONTH,
+ RTC_YEAR,
+ RTC_MONTHDAY,
+ RTC_NR
+};
+
+struct max77663_rtc {
+ struct rtc_device *rtc;
+ struct device *dev;
+
+ struct mutex io_lock;
+ int irq;
+ u8 irq_mask;
+};
+
+static inline struct device *_to_parent(struct max77663_rtc *rtc)
+{
+ return rtc->dev->parent;
+}
+
+static inline int max77663_rtc_update_buffer(struct max77663_rtc *rtc,
+ int write)
+{
+ struct device *parent = _to_parent(rtc);
+ int retries = RTC_UPDATE_RETRIES;
+ u8 val = FLAG_AUTO_CLEAR_MASK | RTC_WAKE_MASK | RB_UPDATE_MASK;
+ u8 flag_mask = RB_UPDATE_FLAG_MASK;
+ int ret;
+
+ if (write) {
+ val = FLAG_AUTO_CLEAR_MASK | RTC_WAKE_MASK | WB_UPDATE_MASK;
+ flag_mask = WB_UPDATE_FLAG_MASK;
+ }
+
+ dev_dbg(rtc->dev, "rtc_update_buffer: write=%d, addr=0x%x, val=0x%x\n",
+ write, MAX77663_RTC_UPDATE0, val);
+ ret = max77663_write(parent, MAX77663_RTC_UPDATE0, &val, 1, 1);
+ if (ret < 0) {
+ dev_err(rtc->dev, "rtc_update_buffer: "
+ "Failed to get rtc update0\n");
+ return ret;
+ }
+
+ do {
+ ret = max77663_read(parent, MAX77663_RTC_UPDATE1, &val, 1, 1);
+ schedule_timeout_uninterruptible(msecs_to_jiffies(1));
+ } while (!ret && retries-- && !(val & flag_mask));
+
+ if (ret < 0) {
+ dev_err(rtc->dev,
+ "rtc_update_buffer: Failed to get rtc update1\n");
+ return ret;
+ }
+
+ if (retries <= 0) {
+ dev_err(rtc->dev, "rtc_update_buffer: "
+ "Timeout waiting for buffer update\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static inline int max77663_rtc_write(struct max77663_rtc *rtc, u8 addr,
+ void *values, u32 len, int update_buffer)
+{
+ struct device *parent = _to_parent(rtc);
+ int ret;
+
+ mutex_lock(&rtc->io_lock);
+
+ dev_dbg(rtc->dev, "rtc_write: addr=0x%x, values=0x%x, len=%u, "
+ "update_buffer=%d\n",
+ addr, *((u8 *)values), len, update_buffer);
+ ret = max77663_write(parent, addr, values, len, 1);
+ if (ret < 0)
+ goto out;
+
+ if (update_buffer)
+ ret = max77663_rtc_update_buffer(rtc, 1);
+
+out:
+ mutex_unlock(&rtc->io_lock);
+ return ret;
+}
+
+static inline int max77663_rtc_read(struct max77663_rtc *rtc, u8 addr,
+ void *values, u32 len, int update_buffer)
+{
+ struct device *parent = _to_parent(rtc);
+ int ret;
+
+ mutex_lock(&rtc->io_lock);
+
+ if (update_buffer) {
+ ret = max77663_rtc_update_buffer(rtc, 0);
+ if (ret < 0)
+ goto out;
+ }
+
+ ret = max77663_read(parent, addr, values, len, 1);
+ dev_dbg(rtc->dev, "rtc_read: addr=0x%x, values=0x%x, len=%u, "
+ "update_buffer=%d\n",
+ addr, *((u8 *)values), len, update_buffer);
+
+out:
+ mutex_unlock(&rtc->io_lock);
+ return ret;
+}
+
+static inline int max77663_rtc_reg_to_tm(struct max77663_rtc *rtc, u8 *buf,
+ struct rtc_time *tm)
+{
+ int wday = buf[RTC_WEEKDAY] & WEEKDAY_MASK;
+
+ if (unlikely(!wday)) {
+ dev_err(rtc->dev,
+ "rtc_reg_to_tm: Invalid day of week, %d\n", wday);
+ return -EINVAL;
+ }
+
+ tm->tm_sec = (int)(buf[RTC_SEC] & SEC_MASK);
+ tm->tm_min = (int)(buf[RTC_MIN] & MIN_MASK);
+ tm->tm_hour = (int)(buf[RTC_HOUR] & HOUR_MASK);
+ tm->tm_mday = (int)(buf[RTC_MONTHDAY] & MONTHDAY_MASK);
+ tm->tm_mon = (int)(buf[RTC_MONTH] & MONTH_MASK) - 1;
+ tm->tm_year = (int)(buf[RTC_YEAR] & YEAR_MASK) + RTC_YEAR_BASE;
+ tm->tm_wday = ffs(wday);
+
+ dev_dbg(rtc->dev, "rtc_reg_to_tm: buf sec=%u, min=%u, hour=%u, "
+ "mday=%u, month=%u, year=%u, wday=%u\n",
+ buf[RTC_SEC] & SEC_MASK, buf[RTC_MIN] & MIN_MASK,
+ buf[RTC_HOUR] & HOUR_MASK, buf[RTC_MONTHDAY] & MONTHDAY_MASK,
+ buf[RTC_MONTH] & MONTH_MASK, buf[RTC_YEAR] & YEAR_MASK,
+ buf[RTC_WEEKDAY] & WEEKDAY_MASK);
+ dev_dbg(rtc->dev, "rtc_reg_to_tm: tm_sec=%d, tm_min=%d, tm_hour=%d, "
+ "tm_mday=%d, tm_mon=%d, tm_year=%d, tm_wday=%d\n",
+ tm->tm_sec, tm->tm_min, tm->tm_hour, tm->tm_mday, tm->tm_mon,
+ tm->tm_year, tm->tm_wday);
+
+ return 0;
+}
+
+static inline int max77663_rtc_tm_to_reg(struct max77663_rtc *rtc, u8 *buf,
+ struct rtc_time *tm, int alarm)
+{
+ u8 alarm_mask = alarm ? ALARM_EN_MASK : 0;
+
+ if (unlikely((tm->tm_year < RTC_YEAR_BASE) ||
+ (tm->tm_year > RTC_YEAR_BASE + RTC_YEAR_MAX))) {
+ dev_err(rtc->dev,
+ "rtc_tm_to_reg: Invalid year, %d\n", tm->tm_year);
+ return -EINVAL;
+ }
+
+ buf[RTC_SEC] = tm->tm_sec | alarm_mask;
+ buf[RTC_MIN] = tm->tm_min | alarm_mask;
+ buf[RTC_HOUR] = tm->tm_hour | alarm_mask;
+ buf[RTC_MONTHDAY] = tm->tm_mday | alarm_mask;
+ buf[RTC_MONTH] = (tm->tm_mon + 1) | alarm_mask;
+ buf[RTC_YEAR] = (tm->tm_year - RTC_YEAR_BASE) | alarm_mask;
+ buf[RTC_WEEKDAY] = (1 << tm->tm_wday) | alarm_mask;
+
+ dev_dbg(rtc->dev, "rtc_tm_to_reg: tm_sec=%d, tm_min=%d, tm_hour=%d, "
+ "tm_mday=%d, tm_mon=%d, tm_year=%d, tm_wday=%d, alarm=%d\n",
+ tm->tm_sec, tm->tm_min, tm->tm_hour, tm->tm_mday, tm->tm_mon,
+ tm->tm_year, tm->tm_wday, alarm);
+ dev_dbg(rtc->dev, "rtc_tm_to_reg: buf sec=%u, min=%u, hour=%u, "
+ "mday=%u, month=%u, year=%u, wday=%u\n",
+ buf[RTC_SEC] & SEC_MASK, buf[RTC_MIN] & MIN_MASK,
+ buf[RTC_HOUR] & HOUR_MASK, buf[RTC_MONTHDAY] & MONTHDAY_MASK,
+ buf[RTC_MONTH] & MONTH_MASK, buf[RTC_YEAR] & YEAR_MASK,
+ buf[RTC_WEEKDAY] & WEEKDAY_MASK);
+
+ return 0;
+}
+
+static irqreturn_t max77663_rtc_irq(int irq, void *data)
+{
+ struct max77663_rtc *rtc = (struct max77663_rtc *)data;
+ u8 val = 0;
+ int ret;
+
+ ret = max77663_rtc_read(rtc, MAX77663_RTC_IRQ, &val, 1, 0);
+ if (ret < 0)
+ dev_err(rtc->dev, "rtc_irq: Failed to get rtc irq status\n");
+
+ if (val & RTC_IRQ_ALARM1_MASK)
+ rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF);
+
+ if (val & RTC_IRQ_1SEC_MASK)
+ rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_UF);
+
+ return IRQ_HANDLED;
+}
+
+static int max77663_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct max77663_rtc *rtc = dev_get_drvdata(dev);
+ struct device *parent = _to_parent(rtc);
+ u8 val = 0;
+ int ret = 0;
+
+ if (rtc->irq < 0)
+ return -ENXIO;
+
+ mutex_lock(&rtc->io_lock);
+ if (enabled)
+ val = rtc->irq_mask & ~RTC_IRQ_ALARM1_MASK;
+ else
+ val = rtc->irq_mask | RTC_IRQ_ALARM1_MASK;
+
+ if (val != rtc->irq_mask) {
+ ret = max77663_write(parent, MAX77663_RTC_IRQ_MASK, &val, 1, 1);
+ if (ret < 0) {
+ dev_err(rtc->dev, "rtc_alarm_irq_enable: "
+ "Failed to set rtc irq mask\n");
+ mutex_unlock(&rtc->io_lock);
+ return ret;
+ }
+
+ rtc->irq_mask = val;
+ }
+ mutex_unlock(&rtc->io_lock);
+
+ return 0;
+}
+
+static int max77663_rtc_update_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct max77663_rtc *rtc = dev_get_drvdata(dev);
+ struct device *parent = _to_parent(rtc);
+ u8 val = 0;
+ int ret = 0;
+
+ if (rtc->irq < 0)
+ return -ENXIO;
+
+ mutex_lock(&rtc->io_lock);
+ if (enabled)
+ val = rtc->irq_mask & ~RTC_IRQ_1SEC_MASK;
+ else
+ val = rtc->irq_mask | RTC_IRQ_1SEC_MASK;
+
+ if (val != rtc->irq_mask) {
+ ret = max77663_write(parent, MAX77663_RTC_IRQ_MASK, &val, 1, 1);
+ if (ret < 0) {
+ dev_err(rtc->dev, "rtc_update_irq_enable: "
+ "Failed to set rtc irq mask\n");
+ mutex_unlock(&rtc->io_lock);
+ return ret;
+ }
+
+ rtc->irq_mask = val;
+ }
+ mutex_unlock(&rtc->io_lock);
+
+ return 0;
+}
+
+static int max77663_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct max77663_rtc *rtc = dev_get_drvdata(dev);
+ u8 buf[RTC_NR];
+ int ret;
+
+ ret = max77663_rtc_read(rtc, MAX77663_RTC_SEC, buf, sizeof(buf), 1);
+ if (ret < 0) {
+ dev_err(rtc->dev, "rtc_read_time: Failed to read rtc time\n");
+ return ret;
+ }
+
+ return max77663_rtc_reg_to_tm(rtc, buf, tm);
+}
+
+static int max77663_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct max77663_rtc *rtc = dev_get_drvdata(dev);
+ u8 buf[RTC_NR];
+ int ret;
+
+ ret = max77663_rtc_tm_to_reg(rtc, buf, tm, 0);
+ if (ret < 0) {
+ dev_err(rtc->dev, "rtc_set_time: "
+ "Failed to convert time format into register format\n");
+ return ret;
+ }
+
+ return max77663_rtc_write(rtc, MAX77663_RTC_SEC, buf, sizeof(buf), 1);
+}
+
+static int max77663_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct max77663_rtc *rtc = dev_get_drvdata(dev);
+ u8 buf[RTC_NR];
+ int ret;
+
+ ret = max77663_rtc_read(rtc, MAX77663_RTC_ALARM_SEC1, buf, sizeof(buf),
+ 1);
+ if (ret < 0) {
+ dev_err(rtc->dev,
+ "rtc_read_alarm: Failed to read rtc alarm time\n");
+ return ret;
+ }
+
+ ret = max77663_rtc_reg_to_tm(rtc, buf, &alrm->time);
+ if (ret < 0) {
+ dev_err(rtc->dev, "rtc_read_alarm: "
+ "Failed to convert register format into time format\n");
+ return ret;
+ }
+
+ if (rtc->irq_mask & RTC_IRQ_ALARM1_MASK)
+ alrm->enabled = 1;
+ else
+ alrm->enabled = 0;
+
+ return 0;
+}
+
+static int max77663_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct max77663_rtc *rtc = dev_get_drvdata(dev);
+ u8 buf[RTC_NR];
+ int ret;
+
+ ret = max77663_rtc_tm_to_reg(rtc, buf, &alrm->time, 1);
+ if (ret < 0) {
+ dev_err(rtc->dev, "rtc_set_alarm: "
+ "Failed to convert time format into register format\n");
+ return ret;
+ }
+
+ ret = max77663_rtc_write(rtc, MAX77663_RTC_ALARM_SEC1, buf, sizeof(buf),
+ 1);
+ if (ret < 0) {
+ dev_err(rtc->dev,
+ "rtc_set_alarm: Failed to write rtc alarm time\n");
+ return ret;
+ }
+
+ ret = max77663_rtc_alarm_irq_enable(dev, alrm->enabled);
+ if (ret < 0) {
+ dev_err(rtc->dev,
+ "rtc_set_alarm: Failed to set rtc alarm irq\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct rtc_class_ops max77663_rtc_ops = {
+ .read_time = max77663_rtc_read_time,
+ .set_time = max77663_rtc_set_time,
+ .read_alarm = max77663_rtc_read_alarm,
+ .set_alarm = max77663_rtc_set_alarm,
+ .alarm_irq_enable = max77663_rtc_alarm_irq_enable,
+ .update_irq_enable = max77663_rtc_update_irq_enable,
+};
+
+static int max77663_rtc_preinit(struct max77663_rtc *rtc)
+{
+ u8 val;
+ int ret;
+
+ /* Mask all interrupts */
+ rtc->irq_mask = RTC_IRQ_MASK;
+ ret = max77663_rtc_write(rtc, MAX77663_RTC_IRQ_MASK, &rtc->irq_mask, 1,
+ 0);
+ if (ret < 0) {
+ dev_err(rtc->dev, "preinit: Failed to set rtc irq mask\n");
+ return ret;
+ }
+
+ /* Configure Binary mode and 24hour mode */
+ val = HR_MODE_MASK;
+ ret = max77663_rtc_write(rtc, MAX77663_RTC_CTRL, &val, 1, 0);
+ if (ret < 0) {
+ dev_err(rtc->dev, "preinit: Failed to set rtc control\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int max77663_rtc_probe(struct platform_device *pdev)
+{
+ struct max77663_platform_data *parent_pdata =
+ pdev->dev.parent->platform_data;
+ static struct max77663_rtc *rtc;
+ int ret = 0;
+
+ rtc = kzalloc(sizeof(struct max77663_rtc), GFP_KERNEL);
+ if (!rtc) {
+ dev_err(&pdev->dev, "probe: kzalloc() failed\n");
+ return -ENOMEM;
+ }
+
+ dev_set_drvdata(&pdev->dev, rtc);
+ rtc->dev = &pdev->dev;
+ mutex_init(&rtc->io_lock);
+
+ ret = max77663_rtc_preinit(rtc);
+ if (ret) {
+ dev_err(&pdev->dev, "probe: Failed to rtc preinit\n");
+ goto out_kfree;
+ }
+
+ rtc->rtc = rtc_device_register("max77663-rtc", &pdev->dev,
+ &max77663_rtc_ops, THIS_MODULE);
+ if (IS_ERR_OR_NULL(rtc->rtc)) {
+ dev_err(&pdev->dev, "probe: Failed to register rtc\n");
+ ret = PTR_ERR(rtc->rtc);
+ goto out_kfree;
+ }
+
+ if (parent_pdata->irq_base < 0)
+ goto out;
+
+ rtc->irq = parent_pdata->irq_base + MAX77663_IRQ_RTC;
+ ret = request_threaded_irq(rtc->irq, NULL, max77663_rtc_irq,
+ IRQF_ONESHOT, "max77663-rtc", rtc);
+ if (ret < 0) {
+ dev_err(rtc->dev, "probe: Failed to request irq %d\n",
+ rtc->irq);
+ rtc->irq = -1;
+ } else {
+ device_init_wakeup(rtc->dev, 1);
+ enable_irq_wake(rtc->irq);
+ }
+
+ return 0;
+
+out_kfree:
+ mutex_destroy(&rtc->io_lock);
+ kfree(rtc->rtc);
+out:
+ return ret;
+}
+
+static int __devexit max77663_rtc_remove(struct platform_device *pdev)
+{
+ struct max77663_rtc *rtc = dev_get_drvdata(&pdev->dev);
+
+ if (rtc->irq != -1)
+ free_irq(rtc->irq, rtc);
+
+ rtc_device_unregister(rtc->rtc);
+ mutex_destroy(&rtc->io_lock);
+ kfree(rtc);
+
+ return 0;
+}
+
+static struct platform_driver max77663_rtc_driver = {
+ .probe = max77663_rtc_probe,
+ .remove = __devexit_p(max77663_rtc_remove),
+ .driver = {
+ .name = "max77663-rtc",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init max77663_rtc_init(void)
+{
+ return platform_driver_register(&max77663_rtc_driver);
+}
+module_init(max77663_rtc_init);
+
+static void __exit max77663_rtc_exit(void)
+{
+ platform_driver_unregister(&max77663_rtc_driver);
+}
+module_exit(max77663_rtc_exit);
+
+MODULE_DESCRIPTION("max77663 RTC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("1.0");