summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Nelson <eric.nelson@boundarydevices.com>2014-02-07 14:09:47 -0700
committerTroy Kisky <troy.kisky@boundarydevices.com>2014-04-24 18:59:56 -0700
commitdab54d5113032e8e792a4d2c736395818c58a630 (patch)
tree01bd6519e28e552edb41bb8c8b145bb0c1aed530
parent5b32dbda3e0c77b5c6b862dac559ceecd9b39ba5 (diff)
Add driver for Micro Crystal RV-4162 real-time clock
-rw-r--r--drivers/rtc/Kconfig11
-rw-r--r--drivers/rtc/Makefile1
-rw-r--r--drivers/rtc/rtc-rv4162.c178
3 files changed, 189 insertions, 1 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index b9838130a7b0..04f0515ffe41 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -480,7 +480,7 @@ config RTC_DRV_EM3027
will be called rtc-em3027.
config RTC_DRV_RV3029C2
- tristate "Micro Crystal RTC"
+ tristate "Micro Crystal RV-3029-C2 RTC"
help
If you say yes here you get support for the Micro Crystal
RV3029-C2 RTC chips.
@@ -488,6 +488,15 @@ config RTC_DRV_RV3029C2
This driver can also be built as a module. If so, the module
will be called rtc-rv3029c2.
+config RTC_DRV_RV4162
+ tristate "Micro Crystal RTC RV-4162"
+ help
+ If you say yes here you get support for the Micro Crystal
+ RV4162-C7 RTC chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-rv4162.
+
endif # I2C
comment "SPI RTC drivers"
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index c33f86f1a69b..d948024f6773 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -101,6 +101,7 @@ 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_RV4162) += rtc-rv4162.o
obj-$(CONFIG_RTC_DRV_RX4581) += rtc-rx4581.o
obj-$(CONFIG_RTC_DRV_RX8025) += rtc-rx8025.o
obj-$(CONFIG_RTC_DRV_RX8581) += rtc-rx8581.o
diff --git a/drivers/rtc/rtc-rv4162.c b/drivers/rtc/rtc-rv4162.c
new file mode 100644
index 000000000000..76e35e8ea1ce
--- /dev/null
+++ b/drivers/rtc/rtc-rv4162.c
@@ -0,0 +1,178 @@
+/*
+ * An rtc/i2c driver for the Micro Crystal RV-4162 Real-time clock
+ *
+ * Copyright 2013 Boundary Devices
+ *
+ * Author: Eric Nelson <eric.nelson@boundarydevices.com>
+ *
+ * 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.
+ */
+
+#include <linux/i2c.h>
+#include <linux/rtc.h>
+
+/* Registers */
+#define RV4162_REG_SECS 0x01
+#define RV4162_SECFLAG_ST 0x80
+
+#define RV4162_REG_FLAGS 0x0f
+#define RV4162_FLAG_OF 4
+
+static struct i2c_driver rv4162_driver;
+
+#define BCD_TO_BIN(v) (((v)&0x0f)+(((v)&0xF0)>>4)*10)
+
+static int rv4162_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ u8 addr = 0;
+ u8 buf[8];
+ int err;
+ struct i2c_msg msgs[] = {
+ {client->addr, 0, 1, &addr},
+ {client->addr, I2C_M_RD, sizeof(buf), buf},
+ };
+ memset(tm,0,sizeof(*tm));
+ if (2 == (err=i2c_transfer(client->adapter, &msgs[0], 2))) {
+ tm->tm_sec = BCD_TO_BIN(buf[1]&0x7f);
+ tm->tm_min = BCD_TO_BIN(buf[2]&0x7f);
+ tm->tm_hour = BCD_TO_BIN(buf[3]);
+ tm->tm_mon = BCD_TO_BIN(buf[6]&0x1F)-1;
+ tm->tm_mday = BCD_TO_BIN(buf[5]);
+ tm->tm_wday = (buf[4]&7)-1;
+ tm->tm_yday = -1;
+ tm->tm_year = BCD_TO_BIN(buf[7])+(100*(buf[6]>>6));
+ dev_dbg(&client->dev, "%s: read time: %04u-%02u-%02u %02u:%02u:%02u\n",
+ __func__,
+ tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+ dev_dbg(&client->dev, "%s: %02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x\n",
+ __func__,
+ buf[0], buf[1], buf[2], buf[3],
+ buf[4], buf[5], buf[6], buf[7]);
+ return 0;
+ }
+ else {
+ dev_err(&client->dev, "%s: error %d reading time\n",
+ __func__, err);
+ return err;
+ }
+}
+
+#define BIN_TO_BCD(v) ((((v)/10)<<4)|((v)%10))
+
+static int rv4162_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ u8 buf[9];
+ int err;
+ struct i2c_msg msg = {
+ client->addr, 0, sizeof(buf), buf
+ };
+ buf[0] = 0;
+ buf[1] = 0;
+ buf[2] = BIN_TO_BCD(tm->tm_sec);
+ buf[3] = BIN_TO_BCD(tm->tm_min);
+ buf[4] = BIN_TO_BCD(tm->tm_hour);
+ buf[5] = tm->tm_wday+1;
+ buf[6] = BIN_TO_BCD(tm->tm_mday);
+ buf[7] = BIN_TO_BCD(tm->tm_mon+1) | ((tm->tm_year/100)<<6);
+ buf[8] = BIN_TO_BCD(tm->tm_year%100);
+ if (1 == (err=i2c_transfer(client->adapter, &msg, 1))) {
+ dev_dbg(&client->dev, "%s: %04u-%02u-%02u %02u:%02u:%02u\n",
+ __func__,
+ tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+ dev_dbg(&client->dev, "%s: %02x:%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x\n",
+ __func__,
+ buf[0], buf[1], buf[2], buf[3],
+ buf[4], buf[5], buf[6], buf[7], buf[8]);
+ return 0;
+ }
+ else {
+ dev_err(&client->dev, "%s: error %d saving time\n",
+ __func__, err);
+ return err;
+ }
+}
+
+static const struct rtc_class_ops rv4162_rtc_ops = {
+ .read_time = rv4162_rtc_read_time,
+ .set_time = rv4162_rtc_set_time,
+};
+
+static int rv4162_remove(struct i2c_client *client)
+{
+ struct rtc_device *rtc = i2c_get_clientdata(client);
+
+ if (rtc)
+ rtc_device_unregister(rtc);
+
+ return 0;
+}
+
+static int rv4162_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct rtc_device *rtc;
+ unsigned char addr = RV4162_REG_FLAGS;
+ unsigned char flags;
+ struct i2c_msg msgs[] = {
+ {client->addr, 0, 1, &addr},
+ {client->addr, I2C_M_RD, 1, &flags},
+ };
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return -ENODEV;
+
+ if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) {
+ dev_err(&client->dev, "%s: read error\n", __func__);
+ return -EIO;
+ }
+
+ dev_info(&client->dev, "%s: chip found: flags 0x%02x\n", __func__, flags);
+
+ rtc = rtc_device_register(rv4162_driver.driver.name, &client->dev,
+ &rv4162_rtc_ops, THIS_MODULE);
+
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ i2c_set_clientdata(client, rtc);
+
+ return 0;
+}
+
+static struct i2c_device_id rv4162_id[] = {
+ { "rv4162", 0 },
+ { }
+};
+
+static struct i2c_driver rv4162_driver = {
+ .driver = {
+ .name = "rtc-rv4162",
+ },
+ .probe = &rv4162_probe,
+ .remove = &rv4162_remove,
+ .id_table = rv4162_id,
+};
+
+static int __init rv4162_init(void)
+{
+ return i2c_add_driver(&rv4162_driver);
+}
+
+static void __exit rv4162_exit(void)
+{
+ i2c_del_driver(&rv4162_driver);
+}
+
+MODULE_AUTHOR("Eric Nelson <eric.nelson@boundarydevices.com>");
+MODULE_DESCRIPTION("Micro Crystal RV-4162 Real-time clock driver");
+MODULE_LICENSE("GPL");
+
+module_init(rv4162_init);
+module_exit(rv4162_exit);