diff options
author | David Brownell <david-b@pacbell.net> | 2008-05-04 06:33:15 +0400 |
---|---|---|
committer | Mark M. Hoffman <mhoffman@lightlink.com> | 2008-08-01 07:44:02 +0400 |
commit | 9ebd3d822efeca2e73565516a80373c76ce3fa12 (patch) | |
tree | 35947ad745ced49a5bfcd5dc1337ad0a4935d829 /drivers/hwmon/lm75.c | |
parent | 01a52397e95a8532c59506691759dba9262d6be7 (diff) | |
download | linux-9ebd3d822efeca2e73565516a80373c76ce3fa12.tar.xz |
hwmon: (lm75) add new-style driver binding
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@users.sourceforge.net>
Acked-by: Jean Delvare <khali@linux-fr.org>
Signed-off-by: Mark M. Hoffman <mhoffman@lightlink.com>
Diffstat (limited to 'drivers/hwmon/lm75.c')
-rw-r--r-- | drivers/hwmon/lm75.c | 206 |
1 files changed, 159 insertions, 47 deletions
diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c index 25ed26584100..7880c273c2c5 100644 --- a/drivers/hwmon/lm75.c +++ b/drivers/hwmon/lm75.c @@ -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_group = { /*-----------------------------------------------------------------------*/ +/* "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_adapter *adapter, int address, int kind) /* 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_adapter *adapter, int address, int kind) 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 i2c_adapter *adapter) 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_client *client, u8 reg, u16 value) 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_device(struct device *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); } |