emc6d102 support
Philip Pokorny
ppokorny at penguincomputing.com
Thu Feb 24 10:08:57 CET 2005
I have some comments...
Rafael Ávila de Espíndola wrote:
>The attached patchs add support for the extended precision ADC present in the
>emc6d102 and adm1027. The kernel patch also redefines some macros to
>explicitly use SCALE. This change makes then more readable.
>
>Rafael
>
>
>
>Index: drivers/i2c/chips/lm85.c
>===================================================================
>--- drivers/i2c/chips/lm85.c (revision 26738)
>+++ drivers/i2c/chips/lm85.c (working copy)
>@@ -36,7 +36,7 @@
> static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
>
> /* Insmod parameters */
>-SENSORS_INSMOD_5(lm85b, lm85c, adm1027, adt7463, emc6d100);
>+SENSORS_INSMOD_6(lm85b, lm85c, adm1027, adt7463, emc6d100, emc6d102);
>
> /* The LM85 registers */
>
>@@ -75,6 +75,7 @@
> #define LM85_VERSTEP_ADT7463 0x62
> #define LM85_VERSTEP_EMC6D100_A0 0x60
> #define LM85_VERSTEP_EMC6D100_A1 0x61
>+#define LM85_VERSTEP_EMC6D102 0x65
>
> #define LM85_REG_CONFIG 0x40
>
>@@ -102,7 +103,6 @@
> #define ADM1027_REG_INTMASK1 0x74
> #define ADM1027_REG_INTMASK2 0x75
> #define ADM1027_REG_EXTEND_ADC1 0x76
>-#define ADM1027_REG_EXTEND_ADC2 0x77
> #define ADM1027_REG_CONFIG3 0x78
> #define ADM1027_REG_FAN_PPR 0x7b
>
>
Why did you remove the definition of EXTEND_ADC2? It's not used by
name, but it's there to document the registers that are used by the driver.
>
>@@ -111,9 +111,10 @@
>
> #define EMC6D100_REG_ALARM3 0x7d
> /* IN5, IN6 and IN7 */
>-#define EMC6D100_REG_IN(nr) (0x70 + ((nr)-5))
>-#define EMC6D100_REG_IN_MIN(nr) (0x73 + ((nr)-5) * 2)
>-#define EMC6D100_REG_IN_MAX(nr) (0x74 + ((nr)-5) * 2)
>+#define EMC6D100_REG_IN(nr) (0x70 + ((nr)-5))
>+#define EMC6D100_REG_IN_MIN(nr) (0x73 + ((nr)-5) * 2)
>+#define EMC6D100_REG_IN_MAX(nr) (0x74 + ((nr)-5) * 2)
>
>
Hmmm.. The EMC6D102 datasheet shows these registers (0x70 - 0x78) as
"test registers" that are "reserved".
http://www.smsc.com/main/datasheets/6d102.pdf
We should add addtional code to prevent reading or setting the
additional in5, in6 and in7 inputs if the chip is an emc6d102.
>+#define EMC6D102_REG_EXTEND_ADC 0x85
>
>
As above, I recommend you #define ADC1, ADC2, ADC3, ADC4 to show that
there are four registers whose values you will be using.
>@@ -138,35 +139,37 @@
> these macros are called: arguments may be evaluated more than once.
> */
>
>-/* IN are scaled 1.000 == 0xc0, mag = 3 */
>-#define IN_TO_REG(val) (SENSORS_LIMIT((((val)*0xc0+500)/1000),0,255))
>-#define INEXT_FROM_REG(val,ext) (((val)*1000 + (ext)*250 + 96)/0xc0)
>-#define IN_FROM_REG(val) (INEXT_FROM_REG(val,0))
>
>
Doh. Dead code. Should have been removed before... Good catch.
>+#define INS_TO_REG(n,val) \
>+ SENSORS_LIMIT(SCALE(val,lm85_scaling[n],192),0,255)
>+
>+#define INSEXT_FROM_REG(n,val,ext,scale) \
>+ SCALE((val)*(scale) + (ext),192*(scale),lm85_scaling[n])
>
>
I think I'd leave those on one line so a 'grep' for the function would
pick out the definition as well as the function name. But that's just
me. Perhaps there is some applicable style guideline?
>-#define EXT_FROM_REG(val,sensor) (((val)>>(sensor * 2))&0x03)
>
> /* ZONEs have the following parameters:
> * Limit (low) temp, 1. degC
>@@ -354,7 +357,7 @@
> u8 pwm[3]; /* Register value */
> u8 spinup_ctl; /* Register encoding, combined */
> u8 tach_mode; /* Register encoding, combined */
>- u16 extend_adc; /* Register value */
>+ u8 extend_adc[4]; /* Register value */
> u8 fan_ppr; /* Register value */
> u8 smooth[3]; /* Register encoding */
> u8 vid; /* Register value */
>
>
Given the additional complexity introduced by the possibility of
different scaling factors (4 for adm1027, 16 for EMC6D102) I think I
would change this from a u16 to a *decoded* value in two arrays:
u8 temp_ext[3]; /* Decoded values */
u8 in_ext[8]; /* Decoded values */
u8 adc_scale; /* ADC Extended bits scaling factor */
Then put all the shift and mask code in 'lm85_update_device' and have it
read the values and break them up into the appropriate '_ext[]' values.
That's a simple loop for the adm1027 where the bits are in the same
order as the sensors. It's a bit more complicated in the case of the
EMC6D102 because things are moved around a bit, but I think it would
make more sense for that code to be in '_update_device'.
Then set adc_scale appropriately at initial detection time.
>@@ -369,6 +372,46 @@
> struct lm85_zone zone[3];
> };
>
>+static int extin_from_reg(struct lm85_data *data, int sensor){
>+ if( (data->type == adm1027) || (data->type == adt7463) ) {
>+ int val = (data->extend_adc[1] << 8) + data->extend_adc[0];
>+ return (val>>(sensor * 2))&0x03;
>+ } else if (data->type == emc6d102) {
>+ if( sensor == 0) {
>+ return data->extend_adc[2]&0x0f;
>+ } else if (sensor == 1) {
>+ return data->extend_adc[3]&0x0f;
>+ } else if (sensor == 2) {
>+ return (data->extend_adc[3] >> 8)&0x0f;
>+ } else if (sensor == 3) {
>+ return (data->extend_adc[2] >> 8)&0x0f;
>+ } else {
>+ /* sensor == 4 */
>+ return (data->extend_adc[1] >> 8)&0x0f;
>+ }
>+ } else {
>+ return 0;
>+ }
>+}
>+
>+static int exttemp_from_reg(struct lm85_data *data, int sensor){
>+ if( (data->type == adm1027) || (data->type == adt7463) ) {
>+ int val = (data->extend_adc[1] << 8) + data->extend_adc[0];
>+ return (val>>((sensor + 5) * 2))&0x03;
>+ } else if (data->type == emc6d102) {
>+ if( sensor == 0) {
>+ return data->extend_adc[0]&0x0f;
>+ } else if (sensor == 1) {
>+ return data->extend_adc[1]&0x0f;
>+ } else {
>+ return (data->extend_adc[0] >> 8)&0x0f;
>+ /* sensor == 2 */
>+ }
>+ } else {
>+ return 0;
>+ }
>+}
>+
>
>
With the previous recomendation, these go away or are incorporated into
_update_device.
> static int lm85_attach_adapter(struct i2c_adapter *adapter);
> static int lm85_detect(struct i2c_adapter *adapter, int address,
> int kind);
>@@ -538,7 +581,14 @@
> static ssize_t show_in(struct device *dev, char *buf, int nr)
> {
> struct lm85_data *data = lm85_update_device(dev);
>- return sprintf(buf,"%d\n", INS_FROM_REG(nr, data->in[nr]) );
>+ /* scale is 2^(number of LSBs). There are 4 extra bits in the
>+ emc6d102 and 2 in the adt7463 and adm1027. In all other chips ext
>+ is always 0 and the value of scale is irrelevant. So it is left in
>+ 4*/
>+ int scale = (data->type == emc6d102 ) ? 16 : 4;
>+ int ext = extin_from_reg(data, nr);
>+ return sprintf(buf,"%d\n",
>+ INSEXT_FROM_REG(nr, data->in[nr], ext, scale) );
> }
> static ssize_t show_in_min(struct device *dev, char *buf, int nr)
> {
>@@ -619,7 +669,14 @@
> static ssize_t show_temp(struct device *dev, char *buf, int nr)
> {
> struct lm85_data *data = lm85_update_device(dev);
>- return sprintf(buf,"%d\n", TEMP_FROM_REG(data->temp[nr]) );
>+ /* scale is 2^(number of LSBs). There are 4 extra bits in the
>+ emc6d102 and 2 in the adt7463 and adm1027. In all other chips ext
>+ is always 0 and the value of scale is irrelevant. So it is left in
>+ 4*/
>+ int scale = (data->type == emc6d102 ) ? 16 : 4;
>+ int ext = exttemp_from_reg(data, nr);
>+ return sprintf(buf,"%d\n",
>+ TEMPEXT_FROM_REG(data->temp[nr], ext, scale) );
> }
> static ssize_t show_temp_min(struct device *dev, char *buf, int nr)
> {
>
>
With the previous recommendation, these become a bit simpler:
return sprintf(buf, "%d\n", INSEXT_FROM_REG(nr, data->in[nr],
data->in_ext[nr], data->adc_scale) );
for example. It might even make sense to just pass 'data' to the
'???EXT_FROM_REG()' macro and let it dereference the structure pointer...
>@@ -1109,6 +1166,9 @@
> */
> kind = emc6d100 ;
> } else if( company == LM85_COMPANY_SMSC
>+ && verstep == LM85_VERSTEP_EMC6D102) {
>+ kind = emc6d102 ;
>+ } else if( company == LM85_COMPANY_SMSC
> && (verstep & LM85_VERSTEP_VMASK) == LM85_VERSTEP_GENERIC) {
> dev_err(&adapter->dev, "lm85: Detected SMSC chip\n");
> dev_err(&adapter->dev, "lm85: Unrecognized version/stepping 0x%02x"
>
>
Excellent.
>@@ -1144,6 +1204,8 @@
> type_name = "adt7463";
> } else if ( kind == emc6d100){
> type_name = "emc6d100";
>+ } else if ( kind == emc6d102 ) {
>+ type_name = "emc6d102";
> }
> strlcpy(new_client->name, type_name, I2C_NAME_SIZE);
>
>@@ -1267,7 +1329,6 @@
> case LM85_REG_FAN_MIN(2) :
> case LM85_REG_FAN_MIN(3) :
> case LM85_REG_ALARM1 : /* Read both bytes at once */
>- case ADM1027_REG_EXTEND_ADC1 : /* Read two bytes at once */
> res = i2c_smbus_read_byte_data(client, reg) & 0xff ;
> res |= i2c_smbus_read_byte_data(client, reg+1) << 8 ;
> break ;
>@@ -1371,8 +1432,10 @@
> * more significant bits that are read later.
> */
> if ( (data->type == adm1027) || (data->type == adt7463) ) {
>- data->extend_adc =
>+ data->extend_adc[0] =
> lm85_read_value(client, ADM1027_REG_EXTEND_ADC1);
>+ data->extend_adc[1] =
>+ lm85_read_value(client, ADM1027_REG_EXTEND_ADC1+1);
> }
>
> for (i = 0; i <= 4; ++i) {
>
>
I would have been clearer to leave in 'ADM1027_REG_EXTEND_ADC2', don't
you think?
>@@ -1411,6 +1474,16 @@
> /* More alarm bits */
> data->alarms |=
> lm85_read_value(client, EMC6D100_REG_ALARM3) << 16;
>+ } else if (data->type == emc6d102 ) {
>+ /* LSBs */
>+ data->extend_adc[0] = lm85_read_value(
>+ client, EMC6D102_REG_EXTEND_ADC);
>+ data->extend_adc[1] = lm85_read_value(
>+ client, EMC6D102_REG_EXTEND_ADC + 1);
>+ data->extend_adc[2] = lm85_read_value(
>+ client, EMC6D102_REG_EXTEND_ADC + 2);
>+ data->extend_adc[3] = lm85_read_value(
>+ client, EMC6D102_REG_EXTEND_ADC + 3);
> }
>
> data->last_reading = jiffies ;
>
>
I don't see any comment about the time ordering of reading the LSB's and
the MSB's. The data sheet says that reading the upper 8-bit MSB's latch
the corresponding lower 4 bit LSB's. You'll note that this is
*backward* from the ADM1027 where the LSB need to be read *first* to
latch the MSB's.
A note regarding this difference should be included in comments for the
code to read the EMC6D102 extended bits.
Also, make sure that the EMC6D100 and 101 registers 0x70-0x78 are *not*
read if this device is an EMC6D102.
:v)
More information about the lm-sensors
mailing list