Patchwork rtc: detect() method for ds1307 rtc driver.

login
register
mail settings
Submitter mcherkashin
Date Jan. 28, 2010, 11:54 a.m.
Message ID <4c4a5102-b045-4c9d-9852-77e2b118380b@a32g2000yqm.googlegroups.com>
Download mbox | patch
Permalink /patch/43857/
State New
Headers show

Comments

mcherkashin - Jan. 28, 2010, 11:54 a.m.
Fixes an ds1307 rtc driver by adding a detect method to it.
Also epson 3231 chip detection is supported by reading a temperature
register.

This patch is for a 2.6.31 kernel.

Signed-off-by: Mikhail Cherkashin <cherkashin@gmail.com>
 #include <linux/module.h>
 #include <linux/init.h>
@@ -18,6 +18,8 @@
 #include <linux/rtc.h>
 #include <linux/bcd.h>

+static const unsigned short normal_i2c[] = { 0x68,  I2C_CLIENT_END };
+I2C_CLIENT_INSMOD;


 /* We can't determine type by probing, but if we expect pre-Linux
code
@@ -95,6 +97,9 @@
 #	define RX8025_BIT_VDET		0x40
 #	define RX8025_BIT_XST		0x20

+/* DS3231 temperature registers */
+#define DS3231_REG_TEMPHI	0x11
+#define DS3231_REG_TEMPLO	0x12

 struct ds1307 {
 	u8			offset; /* register's offset */
@@ -620,6 +625,129 @@

 static struct i2c_driver ds1307_driver;

+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int ds1307_detect(struct i2c_client *new_client, int kind,
+		       struct i2c_board_info *info)
+{
+	int			err = -ENODEV;
+	u8 type;
+	struct i2c_adapter *adapter = new_client->adapter;
+	s32 (*read_block_data)(struct i2c_client *client, u8 command,
+			       u8 length, u8 *values);
+	s32 (*write_block_data)(struct i2c_client *client, u8 command,
+				u8 length, const u8 *values);
+	int tmp;
+	u8 buf[11];
+	static const int	bbsqi_bitpos[] = {
+		[ds_1337] = 0,
+		[ds_1339] = DS1339_BIT_BBSQI,
+		[ds_3231] = DS3231_BIT_BBSQW,
+	};
+	int			want_irq = false;
+	const char *name = "";
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)
+	    && !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_I2C_BLOCK))
+		return -EIO;
+
+	if (i2c_check_functionality(adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) {
+		read_block_data = i2c_smbus_read_i2c_block_data;
+		write_block_data = i2c_smbus_write_i2c_block_data;
+	} else {
+		read_block_data = ds1307_read_block_data;
+		write_block_data = ds1307_write_block_data;
+	}
+
+	if (kind >= 0) {
+		type = ds_1337;
+		tmp = read_block_data(new_client,
+				DS1337_REG_CONTROL, 2, buf);
+		if (tmp != 2) {
+			pr_debug("read error %d\n", tmp);
+			err = -EIO;
+			goto exit;
+		}
+
+		/* oscillator off?  turn it on, so clock can tick. */
+		if ((buf[0] & DS1337_BIT_nEOSC)
+				|| (buf[1] & DS1337_BIT_OSF)) {
+			printk(KERN_ERR "no ds1337 oscillator code\n");
+			goto exit;
+		}
+	} else {
+		tmp = read_block_data(new_client,
+				DS3231_REG_TEMPHI, 2, buf);
+		if (tmp != 2) {
+			pr_debug("read error %d\n", tmp);
+			err = -EIO;
+			goto exit;
+		}
+		if (tmp == 2 && buf[0] &&
+				(buf[1] & 0x3f) == 0)
+			type = ds_3231;
+		else
+			type = ds_1307;
+
+	}
+
+read_rtc:
+	/* read RTC registers */
+	tmp = read_block_data(new_client, 0, 8, buf);
+	if (tmp != 8) {
+		pr_debug("read error %d\n", tmp);
+		err = -EIO;
+		goto exit;
+	}
+
+	/* minimal sanity checking; some chips (like DS1340) don't
+	 * specify the extra bits as must-be-zero, but there are
+	 * still a few values that are clearly out-of-range.
+	 */
+	tmp = buf[DS1307_REG_SECS];
+	/* clock halted?  turn it on, so clock can tick. */
+	if (tmp & DS1307_BIT_CH) {
+		if (type && type != ds_1307) {
+			pr_debug("not a ds1307?\n");
+			goto exit;
+		}
+		type = ds_1307;
+
+		/* this partial initialization should work for ds1307,
+		 * ds1338, ds1340, st m41t00, and more.
+		 */
+		dev_warn(&new_client->dev, "SET TIME!\n");
+		i2c_smbus_write_byte_data(new_client, DS1307_REG_SECS, 0);
+		goto read_rtc;
+	}
+
+	/* Fill the i2c board info */
+	if (type == ds_1307)
+		name = "ds1307";
+	else if (type == ds_1337)
+		name = "ds1337";
+	else if (type == ds_1338)
+		name = "ds1338";
+	else if (type == ds_1339)
+		name = "ds1339";
+	else if (type == ds_1340)
+		name = "ds1340";
+	else if (type == ds_1388)
+		name = "ds1388";
+	else if (type == ds_3231)
+		name = "ds3231";
+	else if (type == m41t00)
+		name = "im411t00";
+	else if (type == rx_8025)
+		name = "rx8025";
+	strlcpy(info->type, name, I2C_NAME_SIZE);
+
+	return 0;
+
+exit:
+	return err;
+
+}
+
 static int __devinit ds1307_probe(struct i2c_client *client,
 				  const struct i2c_device_id *id)
 {
@@ -636,6 +764,7 @@
 		[ds_3231] = DS3231_BIT_BBSQW,
 	};

+
 	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)
 	    && !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_I2C_BLOCK))
 		return -EIO;
@@ -925,10 +1054,12 @@
 		.name	= "rtc-ds1307",
 		.owner	= THIS_MODULE,
 	},
+	.detect		= ds1307_detect,
 	.probe		= ds1307_probe,
 	.remove		= __devexit_p(ds1307_remove),
 	.id_table	= ds1307_id,
+	.address_data	= &addr_data,
 };

 static int __init ds1307_init(void)
 {
Alessandro Zummo - Jan. 28, 2010, 11:57 a.m.
On Thu, 28 Jan 2010 03:54:02 -0800 (PST)
mcherkashin <mikhailcher@gmail.com> wrote:

> 
> Fixes an ds1307 rtc driver by adding a detect method to it.
> Also epson 3231 chip detection is supported by reading a temperature
> register.
> 
> This patch is for a 2.6.31 kernel.

 Hi,

  i2c chips aren't detected anymore, they must be declared
 in the p[latform code.


> Signed-off-by: Mikhail Cherkashin <cherkashin@gmail.com>
> Index: rtc-ds1307.c
> ===================================================================
> --- rtc-ds1307.c	(revision 459)
> +++ rtc-ds1307.c	(working copy)
> @@ -1,14 +1,14 @@
>  /*
> - * rtc-ds1307.c - RTC driver for some mostly-compatible I2C chips.
> - *
> - *  Copyright (C) 2005 James Chapman (ds1337 core)
> - *  Copyright (C) 2006 David Brownell
> - *  Copyright (C) 2009 Matthias Fuchs (rx8025 support)
> - *
> - * 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.
> - */
> +* rtc-ds1307.c - RTC driver for some mostly-compatible I2C chips.
> +*
> +*  Copyright (C) 2005 James Chapman (ds1337 core)
> +*  Copyright (C) 2006 David Brownell
> +*  Copyright (C) 2009 Matthias Fuchs (rx8025 support)
> +*
> +* 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/module.h>
>  #include <linux/init.h>
> @@ -18,6 +18,8 @@
>  #include <linux/rtc.h>
>  #include <linux/bcd.h>
> 
> +static const unsigned short normal_i2c[] = { 0x68,  I2C_CLIENT_END };
> +I2C_CLIENT_INSMOD;
> 
> 
>  /* We can't determine type by probing, but if we expect pre-Linux
> code
> @@ -95,6 +97,9 @@
>  #	define RX8025_BIT_VDET		0x40
>  #	define RX8025_BIT_XST		0x20
> 
> +/* DS3231 temperature registers */
> +#define DS3231_REG_TEMPHI	0x11
> +#define DS3231_REG_TEMPLO	0x12
> 
>  struct ds1307 {
>  	u8			offset; /* register's offset */
> @@ -620,6 +625,129 @@
> 
>  static struct i2c_driver ds1307_driver;
> 
> +/* Return 0 if detection is successful, -ENODEV otherwise */
> +static int ds1307_detect(struct i2c_client *new_client, int kind,
> +		       struct i2c_board_info *info)
> +{
> +	int			err = -ENODEV;
> +	u8 type;
> +	struct i2c_adapter *adapter = new_client->adapter;
> +	s32 (*read_block_data)(struct i2c_client *client, u8 command,
> +			       u8 length, u8 *values);
> +	s32 (*write_block_data)(struct i2c_client *client, u8 command,
> +				u8 length, const u8 *values);
> +	int tmp;
> +	u8 buf[11];
> +	static const int	bbsqi_bitpos[] = {
> +		[ds_1337] = 0,
> +		[ds_1339] = DS1339_BIT_BBSQI,
> +		[ds_3231] = DS3231_BIT_BBSQW,
> +	};
> +	int			want_irq = false;
> +	const char *name = "";
> +
> +	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)
> +	    && !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_I2C_BLOCK))
> +		return -EIO;
> +
> +	if (i2c_check_functionality(adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) {
> +		read_block_data = i2c_smbus_read_i2c_block_data;
> +		write_block_data = i2c_smbus_write_i2c_block_data;
> +	} else {
> +		read_block_data = ds1307_read_block_data;
> +		write_block_data = ds1307_write_block_data;
> +	}
> +
> +	if (kind >= 0) {
> +		type = ds_1337;
> +		tmp = read_block_data(new_client,
> +				DS1337_REG_CONTROL, 2, buf);
> +		if (tmp != 2) {
> +			pr_debug("read error %d\n", tmp);
> +			err = -EIO;
> +			goto exit;
> +		}
> +
> +		/* oscillator off?  turn it on, so clock can tick. */
> +		if ((buf[0] & DS1337_BIT_nEOSC)
> +				|| (buf[1] & DS1337_BIT_OSF)) {
> +			printk(KERN_ERR "no ds1337 oscillator code\n");
> +			goto exit;
> +		}
> +	} else {
> +		tmp = read_block_data(new_client,
> +				DS3231_REG_TEMPHI, 2, buf);
> +		if (tmp != 2) {
> +			pr_debug("read error %d\n", tmp);
> +			err = -EIO;
> +			goto exit;
> +		}
> +		if (tmp == 2 && buf[0] &&
> +				(buf[1] & 0x3f) == 0)
> +			type = ds_3231;
> +		else
> +			type = ds_1307;
> +
> +	}
> +
> +read_rtc:
> +	/* read RTC registers */
> +	tmp = read_block_data(new_client, 0, 8, buf);
> +	if (tmp != 8) {
> +		pr_debug("read error %d\n", tmp);
> +		err = -EIO;
> +		goto exit;
> +	}
> +
> +	/* minimal sanity checking; some chips (like DS1340) don't
> +	 * specify the extra bits as must-be-zero, but there are
> +	 * still a few values that are clearly out-of-range.
> +	 */
> +	tmp = buf[DS1307_REG_SECS];
> +	/* clock halted?  turn it on, so clock can tick. */
> +	if (tmp & DS1307_BIT_CH) {
> +		if (type && type != ds_1307) {
> +			pr_debug("not a ds1307?\n");
> +			goto exit;
> +		}
> +		type = ds_1307;
> +
> +		/* this partial initialization should work for ds1307,
> +		 * ds1338, ds1340, st m41t00, and more.
> +		 */
> +		dev_warn(&new_client->dev, "SET TIME!\n");
> +		i2c_smbus_write_byte_data(new_client, DS1307_REG_SECS, 0);
> +		goto read_rtc;
> +	}
> +
> +	/* Fill the i2c board info */
> +	if (type == ds_1307)
> +		name = "ds1307";
> +	else if (type == ds_1337)
> +		name = "ds1337";
> +	else if (type == ds_1338)
> +		name = "ds1338";
> +	else if (type == ds_1339)
> +		name = "ds1339";
> +	else if (type == ds_1340)
> +		name = "ds1340";
> +	else if (type == ds_1388)
> +		name = "ds1388";
> +	else if (type == ds_3231)
> +		name = "ds3231";
> +	else if (type == m41t00)
> +		name = "im411t00";
> +	else if (type == rx_8025)
> +		name = "rx8025";
> +	strlcpy(info->type, name, I2C_NAME_SIZE);
> +
> +	return 0;
> +
> +exit:
> +	return err;
> +
> +}
> +
>  static int __devinit ds1307_probe(struct i2c_client *client,
>  				  const struct i2c_device_id *id)
>  {
> @@ -636,6 +764,7 @@
>  		[ds_3231] = DS3231_BIT_BBSQW,
>  	};
> 
> +
>  	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)
>  	    && !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_I2C_BLOCK))
>  		return -EIO;
> @@ -925,10 +1054,12 @@
>  		.name	= "rtc-ds1307",
>  		.owner	= THIS_MODULE,
>  	},
> +	.detect		= ds1307_detect,
>  	.probe		= ds1307_probe,
>  	.remove		= __devexit_p(ds1307_remove),
>  	.id_table	= ds1307_id,
> +	.address_data	= &addr_data,
>  };
> 
>  static int __init ds1307_init(void)
>  {
> 
> -- 
> You received this message because you are subscribed to "rtc-linux".
> Membership options at http://groups.google.com/group/rtc-linux .
> Please read http://groups.google.com/group/rtc-linux/web/checklist
> before submitting a driver.

Patch

Index: rtc-ds1307.c
===================================================================
--- rtc-ds1307.c	(revision 459)
+++ rtc-ds1307.c	(working copy)
@@ -1,14 +1,14 @@ 
 /*
- * rtc-ds1307.c - RTC driver for some mostly-compatible I2C chips.
- *
- *  Copyright (C) 2005 James Chapman (ds1337 core)
- *  Copyright (C) 2006 David Brownell
- *  Copyright (C) 2009 Matthias Fuchs (rx8025 support)
- *
- * 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.
- */
+* rtc-ds1307.c - RTC driver for some mostly-compatible I2C chips.
+*
+*  Copyright (C) 2005 James Chapman (ds1337 core)
+*  Copyright (C) 2006 David Brownell
+*  Copyright (C) 2009 Matthias Fuchs (rx8025 support)
+*
+* 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.
+*/