[lm-sensors] [patch 2.6.26-rc1 v7+] lm75: add new-style driver binding

David Brownell david-b at pacbell.net
Sun May 4 04:33:15 CEST 2008


More LM75 updates:

 - Teach the LM75 driver to use new-style driver binding:

     * Create a second driver struct, using new-style driver binding
       methods cribbed from the legacy code.

     * Add a MODULE_DEVICE_TABLE (for "newER-style binding")

     * The legacy probe logic delegates its work to this new code.

     * The legacy driver now uses the name "lm75_legacy".

 - More careful initialization.  Chips are put into 9-bit mode so
   the current interconversion routines will never fail.

 - Save the original chip configuration, and restore it on exit.
   (Among other things, this normally turns off the mode where
   the chip is constantly sampling ... and thus saves power.)

So the new-style code should catch all chips that boards declare,
while the legacy code catches others.  This particular coexistence
strategy may need some work yet ... legacy modes might best be set
up explicitly by some tool not unlike "sensors-detect".  (Or else
completely eradicated...)

Signed-off-by: David Brownell <dbrownell at users.sourceforge.net>
---
Goes on top of the other stuff.  Addresess one more round of
comments from Jean.

 drivers/hwmon/Kconfig |    7 +
 drivers/hwmon/lm75.c  |  206 ++++++++++++++++++++++++++++++++++++++------------
 2 files changed, 165 insertions(+), 48 deletions(-)

--- g26.orig/drivers/hwmon/Kconfig	2008-05-03 18:42:52.000000000 -0700
+++ g26/drivers/hwmon/Kconfig	2008-05-03 19:03:42.000000000 -0700
@@ -392,7 +392,12 @@ config SENSORS_LM75
 		- TelCom (now Microchip) TCN75
 		- Texas Instruments TMP100, TMP101, TMP75, TMP175, TMP275
 
-	  Most of these chips will require a "force" module parameter.
+	  This driver supports driver model based binding through board
+	  specific I2C device tables.
+
+	  It also supports the "legacy" style of driver binding.  To use
+	  that with some chips which don't replicate LM75 quirks exactly,
+	  you may need the "force" module parameter.
 
 	  This driver can also be built as a module.  If so, the module
 	  will be called lm75.
--- g26.orig/drivers/hwmon/lm75.c	2008-05-03 19:03:08.000000000 -0700
+++ g26/drivers/hwmon/lm75.c	2008-05-03 19:09:22.000000000 -0700
@@ -32,10 +32,28 @@
 
 /*
  * This driver handles the LM75 and compatible digital temperature sensors.
- * Compatibles include at least the DS75, DS1775, MCP980x, STDS75, TCN75,
- * TMP100, TMP101, TMP75, TMP175, and TMP275.
+ * Only types which are _not_ listed in I2C_CLIENT_INSMOD_*() need to be
+ * listed here.  We start at 9 since I2C_CLIENT_INSMOD_*() currently allow
+ * definition of up to 8 chip types (plus zero).
  */
 
+enum lm75_type {		/* keep sorted in alphabetical order */
+	ds1775 = 9,
+	ds75,
+	/* lm75 -- in I2C_CLIENT_INSMOD_1() */
+	lm75a,
+	max6625,
+	max6626,
+	mcp980x,
+	stds75,
+	tcn75,
+	tmp100,
+	tmp101,
+	tmp175,
+	tmp275,
+	tmp75,
+};
+
 /* Addresses scanned by legacy style driver binding */
 static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c,
 					0x4d, 0x4e, 0x4f, I2C_CLIENT_END };
@@ -54,9 +72,10 @@ static const u8 LM75_REG_TEMP[3] = {
 
 /* Each client has this additional data */
 struct lm75_data {
-	struct i2c_client	client;
+	struct i2c_client	*client;
 	struct device		*hwmon_dev;
 	struct mutex		update_lock;
+	u8			orig_conf;
 	char			valid;		/* !=0 if registers are valid */
 	unsigned long		last_updated;	/* In jiffies */
 	u16			temp[3];	/* Register values,
@@ -65,7 +84,6 @@ struct lm75_data {
 						   2 = hyst */
 };
 
-static void lm75_init_client(struct i2c_client *client);
 static int lm75_read_value(struct i2c_client *client, u8 reg);
 static int lm75_write_value(struct i2c_client *client, u8 reg, u16 value);
 static struct lm75_data *lm75_update_device(struct device *dev);
@@ -120,16 +138,124 @@ static const struct attribute_group lm75
 
 /*-----------------------------------------------------------------------*/
 
+/* "New style" I2C driver binding -- following the driver model */
+
+static int
+lm75_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+	struct lm75_data *data;
+	int status;
+	u8 set_mask, clr_mask;
+	int new;
+
+	if (!i2c_check_functionality(client->adapter,
+			I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA))
+		return -EIO;
+
+	data = kzalloc(sizeof(struct lm75_data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	i2c_set_clientdata(client, data);
+
+	data->client = client;
+	mutex_init(&data->update_lock);
+
+	/* Set to LM75 resolution (9 bits, 1/2 degree C) and range.
+	 * Then tweak to be more precise when appropriate.
+	 */
+	set_mask = 0;
+	clr_mask = (1 << 0)			/* continuous conversions */
+		| (1 << 6) | (1 << 5);		/* 9-bit mode */
+
+	/* configure as specified */
+	status = lm75_read_value(client, LM75_REG_CONF);
+	if (status < 0) {
+		dev_dbg(&client->dev, "Can't read config? %d\n", status);
+		goto exit_free;
+	}
+	data->orig_conf = status;
+	new = status & ~clr_mask;
+	new |= set_mask;
+	if (status != new)
+		lm75_write_value(client, LM75_REG_CONF, new);
+	dev_dbg(&client->dev, "Config %02x\n", new);
+
+	/* Register sysfs hooks */
+	status = sysfs_create_group(&client->dev.kobj, &lm75_group);
+	if (status)
+		goto exit_free;
+
+	data->hwmon_dev = hwmon_device_register(&client->dev);
+	if (IS_ERR(data->hwmon_dev)) {
+		status = PTR_ERR(data->hwmon_dev);
+		goto exit_remove;
+	}
+
+	dev_info(&client->dev, "%s: sensor '%s'\n",
+		data->hwmon_dev->bus_id, client->name);
+
+	return 0;
+
+exit_remove:
+	sysfs_remove_group(&client->dev.kobj, &lm75_group);
+exit_free:
+	i2c_set_clientdata(client, NULL);
+	kfree(data);
+	return status;
+}
+
+static int lm75_remove(struct i2c_client *client)
+{
+	struct lm75_data *data = i2c_get_clientdata(client);
+
+	hwmon_device_unregister(data->hwmon_dev);
+	sysfs_remove_group(&client->dev.kobj, &lm75_group);
+	lm75_write_value(client, LM75_REG_CONF, data->orig_conf);
+	i2c_set_clientdata(client, NULL);
+	kfree(data);
+	return 0;
+}
+
+static const struct i2c_device_id lm75_ids[] = {
+	{ "ds1775", ds1775, },
+	{ "ds75", ds75, },
+	{ "lm75", lm75, },
+	{ "lm75a", lm75a, },
+	{ "max6625", max6625, },
+	{ "max6626", max6626, },
+	{ "mcp980x", mcp980x, },
+	{ "stds75", stds75, },
+	{ "tcn75", tcn75, },
+	{ "tmp100", tmp100, },
+	{ "tmp101", tmp101, },
+	{ "tmp175", tmp175, },
+	{ "tmp275", tmp275, },
+	{ "tmp75", tmp75, },
+	{ /* LIST END */ }
+};
+MODULE_DEVICE_TABLE(i2c, lm75_ids);
+
+static struct i2c_driver lm75_driver = {
+	.driver = {
+		.name	= "lm75",
+	},
+	.probe		= lm75_probe,
+	.remove		= lm75_remove,
+	.id_table	= lm75_ids,
+};
+
+/*-----------------------------------------------------------------------*/
+
 /* "Legacy" I2C driver binding */
 
-static struct i2c_driver lm75_driver;
+static struct i2c_driver lm75_legacy_driver;
 
 /* This function is called by i2c_probe */
 static int lm75_detect(struct i2c_adapter *adapter, int address, int kind)
 {
 	int i;
 	struct i2c_client *new_client;
-	struct lm75_data *data;
 	int err = 0;
 
 	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
@@ -139,16 +265,15 @@ static int lm75_detect(struct i2c_adapte
 	/* OK. For now, we presume we have a valid address. We create the
 	   client structure, even though there may be no sensor present.
 	   But it allows us to use i2c_smbus_read_*_data() calls. */
-	if (!(data = kzalloc(sizeof(struct lm75_data), GFP_KERNEL))) {
+	new_client = kzalloc(sizeof *new_client, GFP_KERNEL);
+	if (!new_client) {
 		err = -ENOMEM;
 		goto exit;
 	}
 
-	new_client = &data->client;
-	i2c_set_clientdata(new_client, data);
 	new_client->addr = address;
 	new_client->adapter = adapter;
-	new_client->driver = &lm75_driver;
+	new_client->driver = &lm75_legacy_driver;
 	new_client->flags = 0;
 
 	/* Now, we do the remaining detection. There is no identification-
@@ -189,38 +314,26 @@ static int lm75_detect(struct i2c_adapte
 				goto exit_free;
 	}
 
-	/* NOTE: we treat "force=..." and "force_lm75=..." the same. */
+	/* NOTE: we treat "force=..." and "force_lm75=..." the same.
+	 * Only new-style driver binding distinguishes chip types.
+	 */
 	strlcpy(new_client->name, "lm75", I2C_NAME_SIZE);
 
-	/* Fill in the remaining client fields and put it into the global list */
-	data->valid = 0;
-	mutex_init(&data->update_lock);
-
 	/* Tell the I2C layer a new client has arrived */
-	if ((err = i2c_attach_client(new_client)))
+	err = i2c_attach_client(new_client);
+	if (err)
 		goto exit_free;
 
-	/* Initialize the LM75 chip */
-	lm75_init_client(new_client);
-
-	/* Register sysfs hooks */
-	if ((err = sysfs_create_group(&new_client->dev.kobj, &lm75_group)))
+	err = lm75_probe(new_client, NULL);
+	if (err < 0)
 		goto exit_detach;
 
-	data->hwmon_dev = hwmon_device_register(&new_client->dev);
-	if (IS_ERR(data->hwmon_dev)) {
-		err = PTR_ERR(data->hwmon_dev);
-		goto exit_remove;
-	}
-
 	return 0;
 
-exit_remove:
-	sysfs_remove_group(&new_client->dev.kobj, &lm75_group);
 exit_detach:
 	i2c_detach_client(new_client);
 exit_free:
-	kfree(data);
+	kfree(new_client);
 exit:
 	return err;
 }
@@ -234,17 +347,15 @@ static int lm75_attach_adapter(struct i2
 
 static int lm75_detach_client(struct i2c_client *client)
 {
-	struct lm75_data *data = i2c_get_clientdata(client);
-	hwmon_device_unregister(data->hwmon_dev);
-	sysfs_remove_group(&client->dev.kobj, &lm75_group);
+	lm75_remove(client);
 	i2c_detach_client(client);
-	kfree(data);
+	kfree(client);
 	return 0;
 }
 
-static struct i2c_driver lm75_driver = {
+static struct i2c_driver lm75_legacy_driver = {
 	.driver = {
-		.name	= "lm75",
+		.name	= "lm75_legacy",
 	},
 	.attach_adapter	= lm75_attach_adapter,
 	.detach_client	= lm75_detach_client,
@@ -276,16 +387,6 @@ static int lm75_write_value(struct i2c_c
 		return i2c_smbus_write_word_data(client, reg, swab16(value));
 }
 
-static void lm75_init_client(struct i2c_client *client)
-{
-	int reg;
-
-	/* Enable if in shutdown mode */
-	reg = lm75_read_value(client, LM75_REG_CONF);
-	if (reg >= 0 && (reg & 0x01))
-		lm75_write_value(client, LM75_REG_CONF, reg & 0xfe);
-}
-
 static struct lm75_data *lm75_update_device(struct device *dev)
 {
 	struct i2c_client *client = to_i2c_client(dev);
@@ -323,11 +424,22 @@ static struct lm75_data *lm75_update_dev
 
 static int __init sensors_lm75_init(void)
 {
-	return i2c_add_driver(&lm75_driver);
+	int status;
+
+	status = i2c_add_driver(&lm75_driver);
+	if (status < 0)
+		return status;
+
+	status = i2c_add_driver(&lm75_legacy_driver);
+	if (status < 0)
+		i2c_del_driver(&lm75_driver);
+
+	return status;
 }
 
 static void __exit sensors_lm75_exit(void)
 {
+	i2c_del_driver(&lm75_legacy_driver);
 	i2c_del_driver(&lm75_driver);
 }
 




More information about the lm-sensors mailing list