[PATCH] DS1337 RTC subsystem driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

this is DS1337 driver for RTC subsystem, tested on VoiceBlue board.
Patch doesn't remove old driver, let's wait for some feedback...
Please test it, so we do not miss next merge window :-)

Signed-off-by: Ladislav Michl <[email protected]>

diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 65d090d..f148e83 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -86,6 +86,16 @@ config RTC_DRV_X1205
 	  This driver can also be built as a module. If so, the module
 	  will be called rtc-x1205.
 
+config RTC_DRV_DS1337
+	tristate "Dallas DS1337"
+	depends on RTC_CLASS && I2C
+	help
+	  If you say yes here you get support for Dallas Semiconductor
+	  DS1337 and DS1339 real-time clock chips.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called rtc-ds1337.
+
 config RTC_DRV_DS1672
 	tristate "Dallas/Maxim DS1672"
 	depends on RTC_CLASS && I2C
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index a9ca0f1..d5f8617 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_RTC_INTF_DEV)	+= rtc-dev.o
 
 obj-$(CONFIG_RTC_DRV_X1205)	+= rtc-x1205.o
 obj-$(CONFIG_RTC_DRV_TEST)	+= rtc-test.o
+obj-$(CONFIG_RTC_DRV_DS1337)	+= rtc-ds1337.o
 obj-$(CONFIG_RTC_DRV_DS1672)	+= rtc-ds1672.o
 obj-$(CONFIG_RTC_DRV_PCF8563)	+= rtc-pcf8563.o
 obj-$(CONFIG_RTC_DRV_RS5C372)	+= rtc-rs5c372.o

--- /dev/null	2006-05-05 14:05:32.506751776 +0200
+++ linux-omap-2.6.git/drivers/rtc/rtc-ds1337.c	2006-05-05 16:42:16.000000000 +0200
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2006 Ladislav Michl <[email protected]>
+ * Copyright (C) 2005 James Chapman <[email protected]>
+ *
+ * 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.
+ *
+ * Driver for Dallas Semiconductor DS1337 and DS1339 real time clock chip
+ */
+
+#include <linux/bcd.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/rtc.h>
+
+/* Device registers */
+#define DS1337_REG_HOUR		0x02
+#define DS1337_REG_DAY		0x03
+#define DS1337_REG_DATE		0x04
+#define DS1337_REG_MONTH	0x05
+#define DS1337_REG_CONTROL	0x0e
+#define DS1337_REG_STATUS	0x0f
+
+static unsigned short normal_i2c[] = { 0x68, I2C_CLIENT_END };
+
+I2C_CLIENT_INSMOD_1(ds1337);
+
+static inline int ds1337_read(struct i2c_client *client, u8 reg, u8 *value)
+{
+	s32 tmp = i2c_smbus_read_byte_data(client, reg);
+
+	if (tmp < 0)
+		return -EIO;
+
+	*value = tmp;
+
+	return 0;
+}
+
+static int ds1337_probe(struct i2c_adapter *adapter, int address, int kind);
+
+/*
+ * In the routines that deal directly with the ds1337 hardware, we use
+ * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch
+ * Epoch is initialized as 2000. Time is set to UTC.
+ */
+static int ds1337_get_datetime(struct i2c_client *client, struct rtc_time *tm)
+{
+	int result;
+	u8 buf[7];
+	u8 val;
+	struct i2c_msg msg[2];
+	u8 offs = 0;
+
+	msg[0].addr = client->addr;
+	msg[0].flags = 0;
+	msg[0].len = 1;
+	msg[0].buf = &offs;
+
+	msg[1].addr = client->addr;
+	msg[1].flags = I2C_M_RD;
+	msg[1].len = sizeof(buf);
+	msg[1].buf = &buf[0];
+
+	result = i2c_transfer(client->adapter, msg, 2);
+	if (result == 2) {
+		tm->tm_sec = BCD2BIN(buf[0]);
+		tm->tm_min = BCD2BIN(buf[1]);
+		val = buf[2] & 0x3f;
+		tm->tm_hour = BCD2BIN(val);
+		tm->tm_wday = BCD2BIN(buf[3]) - 1;
+		tm->tm_mday = BCD2BIN(buf[4]);
+		val = buf[5] & 0x7f;
+		tm->tm_mon = BCD2BIN(val) - 1;
+		tm->tm_year = BCD2BIN(buf[6]);
+		if (buf[5] & 0x80)
+			tm->tm_year += 100;
+
+		dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
+				      "mday=%d, mon=%d, year=%d, wday=%d\n",
+				      __FUNCTION__, tm->tm_sec, tm->tm_min,
+				      tm->tm_hour, tm->tm_mday, tm->tm_mon,
+				      tm->tm_year, tm->tm_wday);
+
+		return 0;
+	}
+
+	dev_dbg(&client->dev, "error reading data! %d\n", result);
+
+	return -EIO;
+}
+
+static int ds1337_set_datetime(struct i2c_client *client, struct rtc_time *tm)
+{
+	int result;
+	u8 buf[8];
+	u8 val;
+	struct i2c_msg msg[1];
+
+	dev_dbg(&client->dev, "%s: secs=%d, mins=%d, hours=%d, "
+		"mday=%d, mon=%d, year=%d, wday=%d\n", __FUNCTION__,
+		tm->tm_sec, tm->tm_min, tm->tm_hour,
+		tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
+
+	buf[0] = 0;		/* reg offset */
+	buf[1] = BIN2BCD(tm->tm_sec);
+	buf[2] = BIN2BCD(tm->tm_min);
+	buf[3] = BIN2BCD(tm->tm_hour);
+	buf[4] = BIN2BCD(tm->tm_wday + 1);
+	buf[5] = BIN2BCD(tm->tm_mday);
+	buf[6] = BIN2BCD(tm->tm_mon + 1);
+	val = tm->tm_year;
+	if (val >= 100) {
+		val -= 100;
+		buf[6] |= (1 << 7);
+	}
+	buf[7] = BIN2BCD(val);
+
+	msg[0].addr = client->addr;
+	msg[0].flags = 0;
+	msg[0].len = sizeof(buf);
+	msg[0].buf = &buf[0];
+
+	result = i2c_transfer(client->adapter, msg, 1);
+	if (result == 1)
+		return 0;
+
+	dev_dbg(&client->dev, "error writing data! %d\n", result);
+
+	return -EIO;
+}
+
+static int ds1337_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+	return ds1337_get_datetime(to_i2c_client(dev), tm);
+}
+
+static int ds1337_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+	return ds1337_set_datetime(to_i2c_client(dev), tm);
+}
+
+/* sysfs callback function */
+static ssize_t show_control(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	u8 control;
+	struct i2c_client *client = to_i2c_client(dev);
+	int err = ds1337_read(client, DS1337_REG_CONTROL, &control);
+	if (err)
+		return err;
+
+	return sprintf(buf, "%s\n", (control & 0x80) ? "disabled" : "enabled");
+}
+
+static DEVICE_ATTR(control, S_IRUGO, show_control, NULL);
+
+static struct rtc_class_ops ds1337_rtc_ops = {
+	.read_time	= ds1337_rtc_read_time,
+	.set_time	= ds1337_rtc_set_time,
+};
+
+static int ds1337_attach(struct i2c_adapter *adapter)
+{
+	return i2c_probe(adapter, &addr_data, ds1337_probe);
+}
+
+static int ds1337_detach(struct i2c_client *client)
+{
+	int err;
+	struct rtc_device *rtc = i2c_get_clientdata(client);
+
+	if (rtc)
+		rtc_device_unregister(rtc);
+
+	if ((err = i2c_detach_client(client)))
+		return err;
+
+	kfree(client);
+
+	return 0;
+}
+
+static struct i2c_driver ds1337_driver = {
+	.driver		= {
+		.name	= "ds1337",
+	},
+	.attach_adapter = &ds1337_attach,
+	.detach_client	= &ds1337_detach,
+};
+
+static int ds1337_probe(struct i2c_adapter *adapter, int address, int kind)
+{
+	u8 val;
+	int err = 0;
+	struct i2c_client *client;
+	struct rtc_device *rtc;
+
+	dev_dbg(&adapter->dev, "%s\n", __FUNCTION__);
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
+				     I2C_FUNC_I2C)) {
+		err = -ENODEV;
+		goto exit;
+	}
+	if (!(client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto exit;
+	}
+
+	/* I2C client */
+	client->addr = address;
+	client->driver = &ds1337_driver;
+	client->adapter	= adapter;
+
+	/* Default to an DS1337 if forced */
+	if (kind == 0)
+		kind = ds1337;
+
+	if (kind < 0) {		/* detection and identification */
+		/* Check that status register bits 6-2 are zero */
+		if ((ds1337_read(client, DS1337_REG_STATUS, &val) < 0) ||
+		    (val & 0x7c))
+			goto exit_kfree;
+
+		/* Check for a valid day register value */
+		if ((ds1337_read(client, DS1337_REG_DAY, &val) < 0) ||
+		    (val == 0) || (val & 0xf8))
+			goto exit_kfree;
+
+		/* Check for a valid date register value */
+		if ((ds1337_read(client, DS1337_REG_DATE, &val) < 0) ||
+		    (val == 0) || (val & 0xc0) || ((val & 0x0f) > 9) ||
+		    (val >= 0x32))
+			goto exit_kfree;
+
+		/* Check for a valid month register value */
+		if ((ds1337_read(client, DS1337_REG_MONTH, &val) < 0) ||
+		    (val == 0) || (val & 0x60) || ((val & 0x0f) > 9) ||
+		    ((val >= 0x13) && (val <= 0x19)))
+			goto exit_kfree;
+
+		/* Check that control register bits 6-5 are zero */
+		if ((ds1337_read(client, DS1337_REG_CONTROL, &val) < 0) ||
+		    (val & 0x60))
+			goto exit_kfree;
+
+		kind = ds1337;
+	}
+
+	strlcpy(client->name, ds1337_driver.driver.name, I2C_NAME_SIZE);
+
+	/* Inform the i2c layer */
+	if ((err = i2c_attach_client(client)))
+		goto exit_kfree;
+
+	rtc = rtc_device_register(ds1337_driver.driver.name, &client->dev,
+				 &ds1337_rtc_ops, THIS_MODULE);
+
+	if (IS_ERR(rtc)) {
+		err = PTR_ERR(rtc);
+		goto exit_detach;
+	}
+
+	i2c_set_clientdata(client, rtc);
+
+	if (ds1337_read(client, DS1337_REG_CONTROL, &val) == 0)
+		i2c_smbus_write_byte_data(client, DS1337_REG_CONTROL,
+					  val & ~0x80);
+	i2c_smbus_write_byte_data(client, DS1337_REG_STATUS, 0);
+
+	if (ds1337_read(client, DS1337_REG_HOUR, &val) == 0 && (val & (1 << 6)))
+		i2c_smbus_write_byte_data(client, DS1337_REG_HOUR,
+					  val & 0x3f);
+
+	/* Register sysfs hooks */
+	device_create_file(&client->dev, &dev_attr_control);
+
+	return 0;
+
+exit_detach:
+	i2c_detach_client(client);
+
+exit_kfree:
+	kfree(client);
+
+exit:
+	return err;
+}
+
+static int __init ds1337_init(void)
+{
+	return i2c_add_driver(&ds1337_driver);
+}
+
+static void __exit ds1337_exit(void)
+{
+	i2c_del_driver(&ds1337_driver);
+}
+
+MODULE_AUTHOR("Ladislav Michl <[email protected]>");
+MODULE_DESCRIPTION("Dallas DS1337 RTC driver");
+MODULE_LICENSE("GPL");
+
+module_init(ds1337_init);
+module_exit(ds1337_exit);
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux