* [lm-sensors] [PATCH] k8temp: Unset driver data in exit_remove
@ 2009-05-06 7:17 Andre Prendel
2009-05-06 7:36 ` Jean Delvare
` (4 more replies)
0 siblings, 5 replies; 6+ messages in thread
From: Andre Prendel @ 2009-05-06 7:17 UTC (permalink / raw)
To: lm-sensors
Hi Jean, Rudolf,
after infiltrating the sensors user space part, I think it's time to
dive into kernel space. So I took a look at some of the sensor drivers
to get an understanding. Unfortunately I couldn't find a bug to fix,
so I had to do something else :)
I don't know whether this patch is worth to be applied, but I have to
start somewhere.
I'm very interested in doing more kernel stuff. So if you have some
work to do, please tell me about it.
Thanks
Andre
---
At the exit_free label in k8temp_probe() the driver data is set to
NULL. This field isn't set before. So this should be done in
exit_remove.
Signed-off-by: Andre Prendel <andre.prendel@gmx.de>
---
--- linux-2.6.orig/drivers/hwmon/k8temp.c 2009-05-05 21:31:44.000000000 +0200
+++ linux-2.6/drivers/hwmon/k8temp.c 2009-05-05 21:35:03.000000000 +0200
@@ -281,8 +281,8 @@
device_remove_file(&pdev->dev,
&sensor_dev_attr_temp4_input.dev_attr);
device_remove_file(&pdev->dev, &dev_attr_name);
-exit_free:
dev_set_drvdata(&pdev->dev, NULL);
+exit_free:
kfree(data);
exit:
return err;
_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [lm-sensors] [PATCH] k8temp: Unset driver data in exit_remove
2009-05-06 7:17 [lm-sensors] [PATCH] k8temp: Unset driver data in exit_remove Andre Prendel
@ 2009-05-06 7:36 ` Jean Delvare
2009-05-06 7:54 ` Andre Prendel
` (3 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Jean Delvare @ 2009-05-06 7:36 UTC (permalink / raw)
To: lm-sensors
Hi Andre,
On Wed, 6 May 2009 09:17:41 +0200, Andre Prendel wrote:
> Hi Jean, Rudolf,
>
> after infiltrating the sensors user space part, I think it's time to
> dive into kernel space. So I took a look at some of the sensor drivers
> to get an understanding. Unfortunately I couldn't find a bug to fix,
> so I had to do something else :)
>
> I don't know whether this patch is worth to be applied, but I have to
> start somewhere.
No, I don't want to apply this, sorry. While your patch is technically
correct, I prefer to always keep dev_set_drvdata(dev, NULL) or
equivalent right before freeing the data structure that may have been
used as the driver data. When developers copy and paste code around, it
makes it way less likely for them to leave dangling pointers behind.
Given that this is an error path and thus normally not taken, we don't
care about performance there, robustness is more important.
> I'm very interested in doing more kernel stuff. So if you have some
> work to do, please tell me about it.
I'll remember that :) Actually you may want to look at the abandoned
tmp401 driver. Hans de Goede worked on it originally but it never made
it into mainline. That's a pity because it was almost ready. I seem to
remember it needs to be updated due to i2c infrastructure changes, and
minor cleanups were needed before I would take the driver into mainline:
http://lists.lm-sensors.org/pipermail/lm-sensors/2008-June/023411.html
http://lists.lm-sensors.org/pipermail/lm-sensors/2008-August/024024.html
Is this something you'd be interested in? If you don't have any
supported chip, I can send you chip dumps, which you can feed into
i2c-stub to emulate a chip. Or if you are into soldering, you may be
able to ask TI for free chip samples.
> Thanks
> Andre
> ---
>
> At the exit_free label in k8temp_probe() the driver data is set to
> NULL. This field isn't set before. So this should be done in
> exit_remove.
>
> Signed-off-by: Andre Prendel <andre.prendel@gmx.de>
> ---
>
> --- linux-2.6.orig/drivers/hwmon/k8temp.c 2009-05-05 21:31:44.000000000 +0200
> +++ linux-2.6/drivers/hwmon/k8temp.c 2009-05-05 21:35:03.000000000 +0200
> @@ -281,8 +281,8 @@
> device_remove_file(&pdev->dev,
> &sensor_dev_attr_temp4_input.dev_attr);
> device_remove_file(&pdev->dev, &dev_attr_name);
> -exit_free:
> dev_set_drvdata(&pdev->dev, NULL);
> +exit_free:
> kfree(data);
> exit:
> return err;
>
--
Jean Delvare
_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [lm-sensors] [PATCH] k8temp: Unset driver data in exit_remove
2009-05-06 7:17 [lm-sensors] [PATCH] k8temp: Unset driver data in exit_remove Andre Prendel
2009-05-06 7:36 ` Jean Delvare
@ 2009-05-06 7:54 ` Andre Prendel
2009-05-06 7:54 ` Hans de Goede
` (2 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Andre Prendel @ 2009-05-06 7:54 UTC (permalink / raw)
To: lm-sensors
On Wed, May 06, 2009 at 09:36:45AM +0200, Jean Delvare wrote:
> Hi Andre,
>
> On Wed, 6 May 2009 09:17:41 +0200, Andre Prendel wrote:
> > Hi Jean, Rudolf,
> >
> > after infiltrating the sensors user space part, I think it's time to
> > dive into kernel space. So I took a look at some of the sensor drivers
> > to get an understanding. Unfortunately I couldn't find a bug to fix,
> > so I had to do something else :)
> >
> > I don't know whether this patch is worth to be applied, but I have to
> > start somewhere.
>
> No, I don't want to apply this, sorry. While your patch is technically
> correct, I prefer to always keep dev_set_drvdata(dev, NULL) or
> equivalent right before freeing the data structure that may have been
> used as the driver data. When developers copy and paste code around, it
> makes it way less likely for them to leave dangling pointers behind.
>
> Given that this is an error path and thus normally not taken, we don't
> care about performance there, robustness is more important.
I agree with you.
> > I'm very interested in doing more kernel stuff. So if you have some
> > work to do, please tell me about it.
>
> I'll remember that :) Actually you may want to look at the abandoned
> tmp401 driver. Hans de Goede worked on it originally but it never made
> it into mainline. That's a pity because it was almost ready. I seem to
> remember it needs to be updated due to i2c infrastructure changes, and
> minor cleanups were needed before I would take the driver into mainline:
>
> http://lists.lm-sensors.org/pipermail/lm-sensors/2008-June/023411.html
> http://lists.lm-sensors.org/pipermail/lm-sensors/2008-August/024024.html
>
> Is this something you'd be interested in? If you don't have any
> supported chip, I can send you chip dumps, which you can feed into
> i2c-stub to emulate a chip. Or if you are into soldering, you may be
> able to ask TI for free chip samples.
Yes, it sounds interesting, I will have a look this.
Thanks
Andre
> > Thanks
> > Andre
> > ---
> >
> > At the exit_free label in k8temp_probe() the driver data is set to
> > NULL. This field isn't set before. So this should be done in
> > exit_remove.
> >
> > Signed-off-by: Andre Prendel <andre.prendel@gmx.de>
> > ---
> >
> > --- linux-2.6.orig/drivers/hwmon/k8temp.c 2009-05-05 21:31:44.000000000 +0200
> > +++ linux-2.6/drivers/hwmon/k8temp.c 2009-05-05 21:35:03.000000000 +0200
> > @@ -281,8 +281,8 @@
> > device_remove_file(&pdev->dev,
> > &sensor_dev_attr_temp4_input.dev_attr);
> > device_remove_file(&pdev->dev, &dev_attr_name);
> > -exit_free:
> > dev_set_drvdata(&pdev->dev, NULL);
> > +exit_free:
> > kfree(data);
> > exit:
> > return err;
> >
>
>
> --
> Jean Delvare
_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [lm-sensors] [PATCH] k8temp: Unset driver data in exit_remove
2009-05-06 7:17 [lm-sensors] [PATCH] k8temp: Unset driver data in exit_remove Andre Prendel
2009-05-06 7:36 ` Jean Delvare
2009-05-06 7:54 ` Andre Prendel
@ 2009-05-06 7:54 ` Hans de Goede
2009-05-06 8:59 ` Andre Prendel
2009-05-06 14:03 ` Hans de Goede
4 siblings, 0 replies; 6+ messages in thread
From: Hans de Goede @ 2009-05-06 7:54 UTC (permalink / raw)
To: lm-sensors
On 05/06/2009 09:36 AM, Jean Delvare wrote:
> Hi Andre,
>
>
<snip>
> I'll remember that :) Actually you may want to look at the abandoned
> tmp401 driver. Hans de Goede worked on it originally but it never made
> it into mainline. That's a pity because it was almost ready. I seem to
> remember it needs to be updated due to i2c infrastructure changes, and
> minor cleanups were needed before I would take the driver into mainline:
>
> http://lists.lm-sensors.org/pipermail/lm-sensors/2008-June/023411.html
> http://lists.lm-sensors.org/pipermail/lm-sensors/2008-August/024024.html
>
> Is this something you'd be interested in? If you don't have any
> supported chip, I can send you chip dumps, which you can feed into
> i2c-stub to emulate a chip. Or if you are into soldering, you may be
> able to ask TI for free chip samples.
>
Hi Andre,
Nice to meet you! As Jean said I've been working on a TI tmp401 driver,
which is like 99% finished. If you're interested in working on this, please
let me know, I've got a much newer version sitting on my harddisk, as well
as a wip version which also adds support for the tmp411, so if you want to
work on this I'll dig them up.
As Jean said, you can ask TI for samples, or I can mail them to you if you
prefer, I still have a number of them left, the big question is how good
are you with a soldering iron? All the samples are smd and have a quite narrow
pitch.
When you've got a sample, you ofcourse need an i2c adapter to hook it up to, I
can advice using an i2c-tiny-usb adapter:
http://www.harbaum.org/till/i2c_tiny_usb/index.shtml
Regards,
Hans
_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [lm-sensors] [PATCH] k8temp: Unset driver data in exit_remove
2009-05-06 7:17 [lm-sensors] [PATCH] k8temp: Unset driver data in exit_remove Andre Prendel
` (2 preceding siblings ...)
2009-05-06 7:54 ` Hans de Goede
@ 2009-05-06 8:59 ` Andre Prendel
2009-05-06 14:03 ` Hans de Goede
4 siblings, 0 replies; 6+ messages in thread
From: Andre Prendel @ 2009-05-06 8:59 UTC (permalink / raw)
To: lm-sensors
On Wed, May 06, 2009 at 09:54:28AM +0200, Hans de Goede wrote:
>
>
> On 05/06/2009 09:36 AM, Jean Delvare wrote:
> > Hi Andre,
> >
> >
>
> <snip>
>
> > I'll remember that :) Actually you may want to look at the abandoned
> > tmp401 driver. Hans de Goede worked on it originally but it never made
> > it into mainline. That's a pity because it was almost ready. I seem to
> > remember it needs to be updated due to i2c infrastructure changes, and
> > minor cleanups were needed before I would take the driver into mainline:
> >
> > http://lists.lm-sensors.org/pipermail/lm-sensors/2008-June/023411.html
> > http://lists.lm-sensors.org/pipermail/lm-sensors/2008-August/024024.html
> >
> > Is this something you'd be interested in? If you don't have any
> > supported chip, I can send you chip dumps, which you can feed into
> > i2c-stub to emulate a chip. Or if you are into soldering, you may be
> > able to ask TI for free chip samples.
> >
>
> Hi Andre,
>
> Nice to meet you! As Jean said I've been working on a TI tmp401 driver,
> which is like 99% finished. If you're interested in working on this, please
> let me know, I've got a much newer version sitting on my harddisk, as well
> as a wip version which also adds support for the tmp411, so if you want to
> work on this I'll dig them up.
Hi Hans,
nice to meet you too. Yes I'm interested in the source code.
>
> As Jean said, you can ask TI for samples, or I can mail them to you if you
> prefer, I still have a number of them left, the big question is how good
> are you with a soldering iron? All the samples are smd and have a quite narrow
> pitch.
I'm affraid I'm a dummy with the soldering iron. Nevertheless it
sounds very interesting.
>
> When you've got a sample, you ofcourse need an i2c adapter to hook it up to, I
> can advice using an i2c-tiny-usb adapter:
> http://www.harbaum.org/till/i2c_tiny_usb/index.shtml
I will read some more about all this stuff.
Thanks for your kindess
Andre
> Regards,
>
> Hans
>
> _______________________________________________
> lm-sensors mailing list
> lm-sensors@lm-sensors.org
> http://lists.lm-sensors.org/mailman/listinfo/lm-sensors
_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [lm-sensors] [PATCH] k8temp: Unset driver data in exit_remove
2009-05-06 7:17 [lm-sensors] [PATCH] k8temp: Unset driver data in exit_remove Andre Prendel
` (3 preceding siblings ...)
2009-05-06 8:59 ` Andre Prendel
@ 2009-05-06 14:03 ` Hans de Goede
4 siblings, 0 replies; 6+ messages in thread
From: Hans de Goede @ 2009-05-06 14:03 UTC (permalink / raw)
To: lm-sensors
[-- Attachment #1: Type: text/plain, Size: 3385 bytes --]
On 05/06/2009 10:59 AM, Andre Prendel wrote:
> On Wed, May 06, 2009 at 09:54:28AM +0200, Hans de Goede wrote:
>>
>> On 05/06/2009 09:36 AM, Jean Delvare wrote:
>>> Hi Andre,
>>>
>>>
>> <snip>
>>
>>> I'll remember that :) Actually you may want to look at the abandoned
>>> tmp401 driver. Hans de Goede worked on it originally but it never made
>>> it into mainline. That's a pity because it was almost ready. I seem to
>>> remember it needs to be updated due to i2c infrastructure changes, and
>>> minor cleanups were needed before I would take the driver into mainline:
>>>
>>> http://lists.lm-sensors.org/pipermail/lm-sensors/2008-June/023411.html
>>> http://lists.lm-sensors.org/pipermail/lm-sensors/2008-August/024024.html
>>>
>>> Is this something you'd be interested in? If you don't have any
>>> supported chip, I can send you chip dumps, which you can feed into
>>> i2c-stub to emulate a chip. Or if you are into soldering, you may be
>>> able to ask TI for free chip samples.
>>>
>> Hi Andre,
>>
>> Nice to meet you! As Jean said I've been working on a TI tmp401 driver,
>> which is like 99% finished. If you're interested in working on this, please
>> let me know, I've got a much newer version sitting on my harddisk, as well
>> as a wip version which also adds support for the tmp411, so if you want to
>> work on this I'll dig them up.
>
> Hi Hans,
>
> nice to meet you too. Yes I'm interested in the source code.
>
Ok, attached you find updated source with all points from Jean's review
he linked to above addressed, except for the writing of a Documentation/hwmon
file, and a crude patch which adds tmp411 support, done by a couple of my
students. I think properly integrating tmp411 support (and writing some
docs) will make a nice starting project, you should get rid of the
global device_id variable hack my students did, and instead do the multiple
chip support handling the usual way, so change the I2C_CLIENT_INSMOD_1 macro
to a I2C_CLIENT_INSMOD_2 macro, extend the tmp401_id table, store the detected
chip kind in the data structure, etc. See for example the fschmd.c driver
for an i2c sensor driver which handles multiple chip types this way.
>> As Jean said, you can ask TI for samples, or I can mail them to you if you
>> prefer, I still have a number of them left, the big question is how good
>> are you with a soldering iron? All the samples are smd and have a quite narrow
>> pitch.
>
> I'm affraid I'm a dummy with the soldering iron. Nevertheless it
> sounds very interesting.
>
Erm, then you can probably best test using device dumps, I'll make dumps of
the tmp401 and tmp411 I've over here, and send them to you in a private mail,
Jean should be able to tell you how to use them (I've never used dumps for
testing).
>> When you've got a sample, you ofcourse need an i2c adapter to hook it up to, I
>> can advice using an i2c-tiny-usb adapter:
>> http://www.harbaum.org/till/i2c_tiny_usb/index.shtml
>
> I will read some more about all this stuff.
>
Well if you don't have any soldering skills, getting an i2c adapter is of little
use, since you will need to do soldering (and have some skill in it to pull it
off in this case) to hook up the samples to the adapter.
So I think its best if you do your development / testing with ic dumps (so with
a software simulation of the IC), and then I can test again with real hardware.
Regards,
Hans
[-- Attachment #2: diff --]
[-- Type: text/plain, Size: 5827 bytes --]
--- tmp401.c 2008-10-23 22:00:19.000000000 +0200
+++ /home/hans/tmp401.c 2009-05-06 15:41:53.000000000 +0200
@@ -1,6 +1,9 @@
/* tmp401.c
*
* Copyright (C) 2007,2008 Hans de Goede <hdegoede@redhat.com>
+ * Gabriel Konat
+ * Sander Leget
+ * Wouter Willems
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -42,7 +45,6 @@
/* Insmod parameters */
I2C_CLIENT_INSMOD_1(tmp401);
-
/*
* The TMP401 registers, note some registers have different addresses for
* reading and writing
@@ -56,7 +58,8 @@
#define TMP401_CONSECUTIVE_ALERT 0x22
#define TMP401_MANUFACTURER_ID_REG 0xFE
#define TMP401_DEVICE_ID_REG 0xFF
-
+#define TMP411_N_FACTOR_REG 0x18
+
static const u8 TMP401_TEMP_MSB[2] = { 0x00, 0x01 };
static const u8 TMP401_TEMP_LSB[2] = { 0x15, 0x10 };
static const u8 TMP401_TEMP_LOW_LIMIT_MSB_READ[2] = { 0x06, 0x08 };
@@ -67,6 +70,10 @@
static const u8 TMP401_TEMP_HIGH_LIMIT_LSB[2] = { 0x16, 0x13 };
/* These are called the THERM limit / hysteresis / mask in the datasheet */
static const u8 TMP401_TEMP_CRIT_LIMIT[2] = { 0x20, 0x19 };
+static const u8 TMP411_TEMP_LOWEST_MSB[2] = { 0x30, 0x34 };
+static const u8 TMP411_TEMP_LOWEST_LSB[2] = { 0x31, 0x35 };
+static const u8 TMP411_TEMP_HIGHEST_MSB[2] = { 0x32, 0x36 };
+static const u8 TMP411_TEMP_HIGHEST_LSB[2] = { 0x33, 0x37 };
/* Flags */
#define TMP401_CONFIG_RANGE 0x04
@@ -80,7 +87,9 @@
#define TMP401_STATUS_LOCAL_HIGH 0x40
/* Manufacturer / Device ID's */
+#define TMP411_MANUFACTURER_ID 0x55
#define TMP401_MANUFACTURER_ID 0x55
+#define TMP411_DEVICE_ID 0x12
#define TMP401_DEVICE_ID 0x11
/*
@@ -93,6 +102,7 @@
struct i2c_board_info *info);
static int tmp401_remove(struct i2c_client *client);
static struct tmp401_data *tmp401_update_device(struct device *dev);
+u8 DEVICE_ID;
/*
* Driver data (common to all clients)
@@ -133,13 +143,16 @@
u16 temp_low[2];
u16 temp_high[2];
u8 temp_crit[2];
- u8 temp_crit_hyst;
+ u8 temp_crit_hyst;
+
+ u16 temp_lowest[2];
+ u16 temp_highest[2];
};
/*
* Sysfs attr show / store functions
*/
-
+
static int tmp401_register_to_temp(u16 reg, u8 config)
{
int temp = reg;
@@ -361,6 +374,26 @@
return count;
}
+static ssize_t show_temp_highest(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ struct tmp401_data *data = tmp401_update_device(dev);
+
+ return sprintf(buf, "%d\n",
+ tmp401_register_to_temp(data->temp_highest[index], data->config));
+}
+
+static ssize_t show_temp_lowest(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ struct tmp401_data *data = tmp401_update_device(dev);
+
+ return sprintf(buf, "%d\n",
+ tmp401_register_to_temp(data->temp_lowest[index], data->config));
+}
+
static struct sensor_device_attribute tmp401_attr[] = {
SENSOR_ATTR(temp1_input, 0444, show_temp_value, NULL, 0),
SENSOR_ATTR(temp1_min, 0644, show_temp_min, store_temp_min, 0),
@@ -389,6 +422,13 @@
TMP401_STATUS_REMOTE_CRIT),
};
+static struct sensor_device_attribute tmp411_attr[] = {
+ SENSOR_ATTR(temp1_highest, 0444, show_temp_highest, NULL, 0),
+ SENSOR_ATTR(temp1_lowest, 0444, show_temp_lowest, NULL, 0),
+ SENSOR_ATTR(temp2_highest, 0444, show_temp_highest, NULL, 1),
+ SENSOR_ATTR(temp2_lowest, 0444, show_temp_lowest, NULL, 1),
+};
+
/*
* Begin non sysfs callback code (aka Real code)
*/
@@ -428,13 +468,15 @@
reg = i2c_smbus_read_byte_data(client,
TMP401_MANUFACTURER_ID_REG);
- if (reg != TMP401_MANUFACTURER_ID)
+ if (reg != TMP401_MANUFACTURER_ID && reg != TMP411_MANUFACTURER_ID )
return -ENODEV;
reg = i2c_smbus_read_byte_data(client, TMP401_DEVICE_ID_REG);
- if (reg != TMP401_DEVICE_ID)
+ if (reg != TMP401_DEVICE_ID && reg != TMP411_DEVICE_ID )
return -ENODEV;
+ DEVICE_ID = reg;
+
reg = i2c_smbus_read_byte_data(client, TMP401_CONFIG_READ);
if (reg & 0x1b)
return -ENODEV;
@@ -444,7 +486,11 @@
if (reg > 15)
return -ENODEV;
}
- strlcpy(info->type, "tmp401", I2C_NAME_SIZE);
+ if(DEVICE_ID == TMP401_DEVICE_ID)
+ strlcpy(info->type, "tmp401", I2C_NAME_SIZE);
+
+ if(DEVICE_ID == TMP411_DEVICE_ID)
+ strlcpy(info->type, "tmp411", I2C_NAME_SIZE);
return 0;
}
@@ -473,14 +519,27 @@
goto exit_remove;
}
+ /* Register sysfs hooks */
+ if(DEVICE_ID == TMP411_DEVICE_ID) {
+ for (i = 0; i < ARRAY_SIZE(tmp411_attr); i++) {
+ err = device_create_file(&client->dev,
+ &tmp411_attr[i].dev_attr);
+ if (err)
+ goto exit_remove;
+ }
+ }
+
data->hwmon_dev = hwmon_device_register(&client->dev);
if (IS_ERR(data->hwmon_dev)) {
err = PTR_ERR(data->hwmon_dev);
data->hwmon_dev = NULL;
goto exit_remove;
}
-
- dev_info(&client->dev, "Detected TI TMP401 chip\n");
+ if(DEVICE_ID == TMP401_DEVICE_ID) {
+ dev_info(&client->dev, "Detected TI TMP401 chip\n");
+ } else if(DEVICE_ID == TMP411_DEVICE_ID) {
+ dev_info(&client->dev, "Detected TI TMP411 chip\n");
+ }
return 0;
@@ -533,6 +592,19 @@
TMP401_TEMP_HIGH_LIMIT_LSB[i]);
data->temp_crit[i] = i2c_smbus_read_byte_data(client,
TMP401_TEMP_CRIT_LIMIT[i]);
+
+ /* TMP411 */
+ if(DEVICE_ID == TMP411_DEVICE_ID) {
+ data->temp_lowest[i] = i2c_smbus_read_byte_data(client,
+ TMP411_TEMP_LOWEST_MSB[i]) << 8;
+ data->temp_lowest[i] |= i2c_smbus_read_byte_data(client,
+ TMP411_TEMP_LOWEST_LSB[i]);
+
+ data->temp_highest[i] = i2c_smbus_read_byte_data(client,
+ TMP411_TEMP_HIGHEST_MSB[i]) << 8;
+ data->temp_highest[i] |= i2c_smbus_read_byte_data(client,
+ TMP411_TEMP_HIGHEST_LSB[i]);
+ }
}
data->temp_crit_hyst = i2c_smbus_read_byte_data(client,
[-- Attachment #3: tmp401.c --]
[-- Type: text/plain, Size: 15389 bytes --]
/* tmp401.c
*
* Copyright (C) 2007,2008 Hans de Goede <hdegoede@redhat.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* Driver for the Texas Instruments TMP401 SMBUS temperature sensor IC.
*
* Note this IC is in some aspect similar to the LM90, but it has quite a
* few differences too, for example the local temp has a higher resolution
* and thus has 16 bits registers for its value and limit instead of 8 bits.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/sysfs.h>
/* Addresses to scan */
static const unsigned short normal_i2c[] = { 0x4c, I2C_CLIENT_END };
/* Insmod parameters */
I2C_CLIENT_INSMOD_1(tmp401);
/*
* The TMP401 registers, note some registers have different addresses for
* reading and writing
*/
#define TMP401_STATUS 0x02
#define TMP401_CONFIG_READ 0x03
#define TMP401_CONFIG_WRITE 0x09
#define TMP401_CONVERSION_RATE_READ 0x04
#define TMP401_CONVERSION_RATE_WRITE 0x0A
#define TMP401_TEMP_CRIT_HYST 0x21
#define TMP401_CONSECUTIVE_ALERT 0x22
#define TMP401_MANUFACTURER_ID_REG 0xFE
#define TMP401_DEVICE_ID_REG 0xFF
static const u8 TMP401_TEMP_MSB[2] = { 0x00, 0x01 };
static const u8 TMP401_TEMP_LSB[2] = { 0x15, 0x10 };
static const u8 TMP401_TEMP_LOW_LIMIT_MSB_READ[2] = { 0x06, 0x08 };
static const u8 TMP401_TEMP_LOW_LIMIT_MSB_WRITE[2] = { 0x0C, 0x0E };
static const u8 TMP401_TEMP_LOW_LIMIT_LSB[2] = { 0x17, 0x14 };
static const u8 TMP401_TEMP_HIGH_LIMIT_MSB_READ[2] = { 0x05, 0x07 };
static const u8 TMP401_TEMP_HIGH_LIMIT_MSB_WRITE[2] = { 0x0B, 0x0D };
static const u8 TMP401_TEMP_HIGH_LIMIT_LSB[2] = { 0x16, 0x13 };
/* These are called the THERM limit / hysteresis / mask in the datasheet */
static const u8 TMP401_TEMP_CRIT_LIMIT[2] = { 0x20, 0x19 };
/* Flags */
#define TMP401_CONFIG_RANGE 0x04
#define TMP401_CONFIG_SHUTDOWN 0x40
#define TMP401_STATUS_LOCAL_CRIT 0x01
#define TMP401_STATUS_REMOTE_CRIT 0x02
#define TMP401_STATUS_REMOTE_OPEN 0x04
#define TMP401_STATUS_REMOTE_LOW 0x08
#define TMP401_STATUS_REMOTE_HIGH 0x10
#define TMP401_STATUS_LOCAL_LOW 0x20
#define TMP401_STATUS_LOCAL_HIGH 0x40
/* Manufacturer / Device ID's */
#define TMP401_MANUFACTURER_ID 0x55
#define TMP401_DEVICE_ID 0x11
/*
* Functions declarations
*/
static int tmp401_probe(struct i2c_client *client,
const struct i2c_device_id *id);
static int tmp401_detect(struct i2c_client *client, int kind,
struct i2c_board_info *info);
static int tmp401_remove(struct i2c_client *client);
static struct tmp401_data *tmp401_update_device(struct device *dev);
/*
* Driver data (common to all clients)
*/
static const struct i2c_device_id tmp401_id[] = {
{ "tmp401", tmp401 },
{ }
};
MODULE_DEVICE_TABLE(i2c, tmp401_id);
static struct i2c_driver tmp401_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = "tmp401",
},
.probe = tmp401_probe,
.remove = tmp401_remove,
.id_table = tmp401_id,
.detect = tmp401_detect,
.address_data = &addr_data,
};
/*
* Client data (each client gets its own)
*/
struct tmp401_data {
struct device *hwmon_dev;
struct mutex update_lock;
char valid; /* zero until following fields are valid */
unsigned long last_updated; /* in jiffies */
/* register values */
u8 status;
u8 config;
u16 temp[2];
u16 temp_low[2];
u16 temp_high[2];
u8 temp_crit[2];
u8 temp_crit_hyst;
};
/*
* Sysfs attr show / store functions
*/
static int tmp401_register_to_temp(u16 reg, u8 config)
{
int temp = reg;
if (config & TMP401_CONFIG_RANGE)
temp -= 64 * 256;
return (temp * 625 + 80) / 160;
}
static u16 tmp401_temp_to_register(long temp, u8 config)
{
if (config & TMP401_CONFIG_RANGE) {
temp = SENSORS_LIMIT(temp, -64000, 191000);
temp += 64000;
} else
temp = SENSORS_LIMIT(temp, 0, 127000);
return (temp * 160 + 312) / 625;
}
static int tmp401_crit_register_to_temp(u8 reg, u8 config)
{
int temp = reg;
if (config & TMP401_CONFIG_RANGE)
temp -= 64;
return temp * 1000;
}
static u8 tmp401_crit_temp_to_register(long temp, u8 config)
{
if (config & TMP401_CONFIG_RANGE) {
temp = SENSORS_LIMIT(temp, -64000, 191000);
temp += 64000;
} else
temp = SENSORS_LIMIT(temp, 0, 127000);
return (temp + 500) / 1000;
}
static ssize_t show_temp_value(struct device *dev,
struct device_attribute *devattr, char *buf)
{
int index = to_sensor_dev_attr(devattr)->index;
struct tmp401_data *data = tmp401_update_device(dev);
return sprintf(buf, "%d\n",
tmp401_register_to_temp(data->temp[index], data->config));
}
static ssize_t show_temp_min(struct device *dev,
struct device_attribute *devattr, char *buf)
{
int index = to_sensor_dev_attr(devattr)->index;
struct tmp401_data *data = tmp401_update_device(dev);
return sprintf(buf, "%d\n",
tmp401_register_to_temp(data->temp_low[index], data->config));
}
static ssize_t show_temp_max(struct device *dev,
struct device_attribute *devattr, char *buf)
{
int index = to_sensor_dev_attr(devattr)->index;
struct tmp401_data *data = tmp401_update_device(dev);
return sprintf(buf, "%d\n",
tmp401_register_to_temp(data->temp_high[index], data->config));
}
static ssize_t show_temp_crit(struct device *dev,
struct device_attribute *devattr, char *buf)
{
int index = to_sensor_dev_attr(devattr)->index;
struct tmp401_data *data = tmp401_update_device(dev);
return sprintf(buf, "%d\n",
tmp401_crit_register_to_temp(data->temp_crit[index],
data->config));
}
static ssize_t show_temp_crit_hyst(struct device *dev,
struct device_attribute *devattr, char *buf)
{
int temp, index = to_sensor_dev_attr(devattr)->index;
struct tmp401_data *data = tmp401_update_device(dev);
mutex_lock(&data->update_lock);
temp = tmp401_crit_register_to_temp(data->temp_crit[index],
data->config);
temp -= data->temp_crit_hyst * 1000;
mutex_unlock(&data->update_lock);
return sprintf(buf, "%d\n", temp);
}
static ssize_t show_status(struct device *dev,
struct device_attribute *devattr, char *buf)
{
int mask = to_sensor_dev_attr(devattr)->index;
struct tmp401_data *data = tmp401_update_device(dev);
if (data->status & mask)
return sprintf(buf, "1\n");
else
return sprintf(buf, "0\n");
}
static ssize_t store_temp_min(struct device *dev, struct device_attribute
*devattr, const char *buf, size_t count)
{
int index = to_sensor_dev_attr(devattr)->index;
struct tmp401_data *data = tmp401_update_device(dev);
long val;
u16 reg;
if (strict_strtol(buf, 10, &val))
return -EINVAL;
reg = tmp401_temp_to_register(val, data->config);
mutex_lock(&data->update_lock);
i2c_smbus_write_byte_data(to_i2c_client(dev),
TMP401_TEMP_LOW_LIMIT_MSB_WRITE[index], reg >> 8);
i2c_smbus_write_byte_data(to_i2c_client(dev),
TMP401_TEMP_LOW_LIMIT_LSB[index], reg & 0xFF);
data->temp_low[index] = reg;
mutex_unlock(&data->update_lock);
return count;
}
static ssize_t store_temp_max(struct device *dev, struct device_attribute
*devattr, const char *buf, size_t count)
{
int index = to_sensor_dev_attr(devattr)->index;
struct tmp401_data *data = tmp401_update_device(dev);
long val;
u16 reg;
if (strict_strtol(buf, 10, &val))
return -EINVAL;
reg = tmp401_temp_to_register(val, data->config);
mutex_lock(&data->update_lock);
i2c_smbus_write_byte_data(to_i2c_client(dev),
TMP401_TEMP_HIGH_LIMIT_MSB_WRITE[index], reg >> 8);
i2c_smbus_write_byte_data(to_i2c_client(dev),
TMP401_TEMP_HIGH_LIMIT_LSB[index], reg & 0xFF);
data->temp_high[index] = reg;
mutex_unlock(&data->update_lock);
return count;
}
static ssize_t store_temp_crit(struct device *dev, struct device_attribute
*devattr, const char *buf, size_t count)
{
int index = to_sensor_dev_attr(devattr)->index;
struct tmp401_data *data = tmp401_update_device(dev);
long val;
u8 reg;
if (strict_strtol(buf, 10, &val))
return -EINVAL;
reg = tmp401_crit_temp_to_register(val, data->config);
mutex_lock(&data->update_lock);
i2c_smbus_write_byte_data(to_i2c_client(dev),
TMP401_TEMP_CRIT_LIMIT[index], reg);
data->temp_crit[index] = reg;
mutex_unlock(&data->update_lock);
return count;
}
static ssize_t store_temp_crit_hyst(struct device *dev, struct device_attribute
*devattr, const char *buf, size_t count)
{
int temp, index = to_sensor_dev_attr(devattr)->index;
struct tmp401_data *data = tmp401_update_device(dev);
long val;
u8 reg;
if (strict_strtol(buf, 10, &val))
return -EINVAL;
if (data->config & TMP401_CONFIG_RANGE)
val = SENSORS_LIMIT(val, -64000, 191000);
else
val = SENSORS_LIMIT(val, 0, 127000);
mutex_lock(&data->update_lock);
temp = tmp401_crit_register_to_temp(data->temp_crit[index],
data->config);
val = SENSORS_LIMIT(val, temp - 255000, temp);
reg = ((temp - val) + 500) / 1000;
i2c_smbus_write_byte_data(to_i2c_client(dev),
TMP401_TEMP_CRIT_HYST, reg);
data->temp_crit_hyst = reg;
mutex_unlock(&data->update_lock);
return count;
}
static struct sensor_device_attribute tmp401_attr[] = {
SENSOR_ATTR(temp1_input, 0444, show_temp_value, NULL, 0),
SENSOR_ATTR(temp1_min, 0644, show_temp_min, store_temp_min, 0),
SENSOR_ATTR(temp1_max, 0644, show_temp_max, store_temp_max, 0),
SENSOR_ATTR(temp1_crit, 0644, show_temp_crit, store_temp_crit, 0),
SENSOR_ATTR(temp1_crit_hyst, 0644, show_temp_crit_hyst,
store_temp_crit_hyst, 0),
SENSOR_ATTR(temp1_min_alarm, 0444, show_status, NULL,
TMP401_STATUS_LOCAL_LOW),
SENSOR_ATTR(temp1_max_alarm, 0444, show_status, NULL,
TMP401_STATUS_LOCAL_HIGH),
SENSOR_ATTR(temp1_crit_alarm, 0444, show_status, NULL,
TMP401_STATUS_LOCAL_CRIT),
SENSOR_ATTR(temp2_input, 0444, show_temp_value, NULL, 1),
SENSOR_ATTR(temp2_min, 0644, show_temp_min, store_temp_min, 1),
SENSOR_ATTR(temp2_max, 0644, show_temp_max, store_temp_max, 1),
SENSOR_ATTR(temp2_crit, 0644, show_temp_crit, store_temp_crit, 1),
SENSOR_ATTR(temp2_crit_hyst, 0444, show_temp_crit_hyst, NULL, 1),
SENSOR_ATTR(temp2_fault, 0444, show_status, NULL,
TMP401_STATUS_REMOTE_OPEN),
SENSOR_ATTR(temp2_min_alarm, 0444, show_status, NULL,
TMP401_STATUS_REMOTE_LOW),
SENSOR_ATTR(temp2_max_alarm, 0444, show_status, NULL,
TMP401_STATUS_REMOTE_HIGH),
SENSOR_ATTR(temp2_crit_alarm, 0444, show_status, NULL,
TMP401_STATUS_REMOTE_CRIT),
};
/*
* Begin non sysfs callback code (aka Real code)
*/
static void tmp401_init_client(struct i2c_client *client)
{
int config, config_orig;
/* Set the conversion rate to 2 Hz */
i2c_smbus_write_byte_data(client, TMP401_CONVERSION_RATE_WRITE, 5);
/* Start conversions (disable shutdown if necessary) */
config = i2c_smbus_read_byte_data(client, TMP401_CONFIG_READ);
if (config < 0) {
dev_warn(&client->dev, "Initialization failed!\n");
return;
}
config_orig = config;
config &= ~TMP401_CONFIG_SHUTDOWN;
if (config != config_orig)
i2c_smbus_write_byte_data(client, TMP401_CONFIG_WRITE, config);
}
static int tmp401_detect(struct i2c_client *client, int kind,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
/* Detect and identify the chip */
if (kind <= 0) {
u8 reg;
reg = i2c_smbus_read_byte_data(client,
TMP401_MANUFACTURER_ID_REG);
if (reg != TMP401_MANUFACTURER_ID)
return -ENODEV;
reg = i2c_smbus_read_byte_data(client, TMP401_DEVICE_ID_REG);
if (reg != TMP401_DEVICE_ID)
return -ENODEV;
reg = i2c_smbus_read_byte_data(client, TMP401_CONFIG_READ);
if (reg & 0x1b)
return -ENODEV;
reg = i2c_smbus_read_byte_data(client,
TMP401_CONVERSION_RATE_READ);
if (reg > 15)
return -ENODEV;
}
strlcpy(info->type, "tmp401", I2C_NAME_SIZE);
return 0;
}
static int tmp401_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int i, err = 0;
struct tmp401_data *data;
data = kzalloc(sizeof(struct tmp401_data), GFP_KERNEL);
if (!data)
return -ENOMEM;
i2c_set_clientdata(client, data);
mutex_init(&data->update_lock);
/* Initialize the TMP401 chip */
tmp401_init_client(client);
/* Register sysfs hooks */
for (i = 0; i < ARRAY_SIZE(tmp401_attr); i++) {
err = device_create_file(&client->dev,
&tmp401_attr[i].dev_attr);
if (err)
goto exit_remove;
}
data->hwmon_dev = hwmon_device_register(&client->dev);
if (IS_ERR(data->hwmon_dev)) {
err = PTR_ERR(data->hwmon_dev);
data->hwmon_dev = NULL;
goto exit_remove;
}
dev_info(&client->dev, "Detected TI TMP401 chip\n");
return 0;
exit_remove:
tmp401_remove(client); /* will also free data for us */
return err;
}
static int tmp401_remove(struct i2c_client *client)
{
struct tmp401_data *data = i2c_get_clientdata(client);
int i;
if (data->hwmon_dev)
hwmon_device_unregister(data->hwmon_dev);
for (i = 0; i < ARRAY_SIZE(tmp401_attr); i++)
device_remove_file(&client->dev, &tmp401_attr[i].dev_attr);
kfree(data);
return 0;
}
static struct tmp401_data *tmp401_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct tmp401_data *data = i2c_get_clientdata(client);
int i;
mutex_lock(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
data->status = i2c_smbus_read_byte_data(client, TMP401_STATUS);
data->config = i2c_smbus_read_byte_data(client,
TMP401_CONFIG_READ);
for (i = 0; i < 2; i++) {
/* High byte must be read first immediately followed
by the low byte */
data->temp[i] = i2c_smbus_read_byte_data(client,
TMP401_TEMP_MSB[i]) << 8;
data->temp[i] |= i2c_smbus_read_byte_data(client,
TMP401_TEMP_LSB[i]);
data->temp_low[i] = i2c_smbus_read_byte_data(client,
TMP401_TEMP_LOW_LIMIT_MSB_READ[i]) << 8;
data->temp_low[i] |= i2c_smbus_read_byte_data(client,
TMP401_TEMP_LOW_LIMIT_LSB[i]);
data->temp_high[i] = i2c_smbus_read_byte_data(client,
TMP401_TEMP_HIGH_LIMIT_MSB_READ[i]) << 8;
data->temp_high[i] |= i2c_smbus_read_byte_data(client,
TMP401_TEMP_HIGH_LIMIT_LSB[i]);
data->temp_crit[i] = i2c_smbus_read_byte_data(client,
TMP401_TEMP_CRIT_LIMIT[i]);
}
data->temp_crit_hyst = i2c_smbus_read_byte_data(client,
TMP401_TEMP_CRIT_HYST);
data->last_updated = jiffies;
data->valid = 1;
}
mutex_unlock(&data->update_lock);
return data;
}
static int __init tmp401_init(void)
{
return i2c_add_driver(&tmp401_driver);
}
static void __exit tmp401_exit(void)
{
i2c_del_driver(&tmp401_driver);
}
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
MODULE_DESCRIPTION("Texas Instruments TMP401 temperature sensor driver");
MODULE_LICENSE("GPL");
module_init(tmp401_init);
module_exit(tmp401_exit);
[-- Attachment #4: Type: text/plain, Size: 153 bytes --]
_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2009-05-06 14:03 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-05-06 7:17 [lm-sensors] [PATCH] k8temp: Unset driver data in exit_remove Andre Prendel
2009-05-06 7:36 ` Jean Delvare
2009-05-06 7:54 ` Andre Prendel
2009-05-06 7:54 ` Hans de Goede
2009-05-06 8:59 ` Andre Prendel
2009-05-06 14:03 ` Hans de Goede
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.