diff --git a/doc/chips/ds1621 b/doc/chips/ds1621 index bee39097..242289db 100644 --- a/doc/chips/ds1621 +++ b/doc/chips/ds1621 @@ -1,21 +1,22 @@ Kernel driver `ds1621.o' -===================== +======================== -Status: Complete and tested, alarms experimental +Status: Complete and tested; enhanced features experimental Supported chips: * Dallas Semiconductor DS1621 Prefix: `ds1621' Addresses scanned: I2C 0x48 - 0x4f (inclusive) Datasheet: Publicly available at the Dallas Semiconductor website - http://www.dalsemi.com + http://www.dalsemi.com/ * Dallas Semiconductor DS1625 Prefix: `ds1621' Addresses scanned: I2C 0x48 - 0x4f (inclusive) Datasheet: Publicly available at the Dallas Semiconductor website - http://www.dalsemi.com + http://www.dalsemi.com/ Author: Christian W. Zuckschwerdt +valueable contributions by Jan M. Sendler Module Parameters @@ -24,8 +25,8 @@ Module Parameters * force: short array (min = 1, max = 48) List of adapter,address pairs to boldly assume to be present * force_ds1621: short array (min = 1, max = 48) - List of adapter,address pairs which are unquestionably assumed to contain - a `ds1621' chip + List of adapter,address pairs which are unquestionably assumed to + contain a `ds1621' chip * ignore: short array (min = 1, max = 48) List of adapter,address pairs not to scan * ignore_range: short array (min = 1, max = 48) @@ -39,28 +40,178 @@ Module Parameters Description ----------- -The DS1621 implements one temperature sensor. Limits can be set through the -Overtemperature register and Hysteresis register. Each value can be -set and read to half-degree accuracy. -An alarm is issued when the temperature gets higher then the Overtemperature -value; or drops below the Hysteresis value. +The DS1621 is a (one instance) digital thermometer and thermostat. It +has both high and low temperature limits which can be user defined +(i. e. programmed into non-volatile on-chip registers). +Temperature range is -55 degree Celsius to +125 in 0.5 increments. You +may convert this into a Fahrenheit range of -67 to +257 degrees with +0.9 steps. Unfortunally though, you will need a hash table or even a +conversion routine for that. -It stays on until reset by the kernel module. +Temperature readings are internally converted into a 9bit value to be +read straight via the i2c-bus, but higher resolutions may be obtained +with little effort via 'precise_temp' access or using a simple algorithm. -All temperatures are in degrees Celcius, and are guaranteed within a -range of -55 to +125 degrees. +As for the thermostat, behavior can also be programmed using the +polarity toggle. +On the one hand ("heater"), the thermostat output of the chip, Tout, +will trigger when the low limit temperature is met or underrun and +stays high until the high limit is met or exceeded. On the other hand +("cooler"), vice versa. +That way "heater" equals "active low", whereas "conditioner" equals +"active high". Please note that the DS1621 data sheet is somewhat +misleading in this point since setting the polarity bit does not +simply invert Tout. -The DS1621 supplies the temperature data in a format common with chips -like the LM75, LM77 and DS75, DS1775. -While the DS1621 has three additional bits of accuracy -(12 vs. 9 for the LM75), the additional bits are not supported. -The driver also supports the DS1625. It does not distinguish the 1625 from -the 1621. The chip prefix in /proc is always ds1621. -The difference in the chips is that the 1621 supports additional registers -for greater accuracy. The driver does not use these registers. -The DS1625 has been discontinued by Dallas Semi, they recommend the -DS1621 for new designs. +A second thing is that, during extensive testing, Tout showed a +tolerance of up to +/- 0.5 degrees even when compared against precise +temperature readings. +Be sure to have a high vs. low temperature limit gap of al least 1.0 +degree Celsius to avoid Tout "bouncing", though! + +As for alarms, you can read the alarm status of the DS1621 via the +'alarms' proc file interface. The result consits mainly of bit 6 and 5 +of the configuration register of the chip; bit 6 (0x40 or 64) is the +high alarm bit and bit 5 (0x20 or 32) the low one. These bits are set +when the high or low limits are met or exceeded and are reset by the +module as soon as the respective temperature ranges are left. + +The alarm registers are in no way suitable to find out about the +actual status of Tout. They will only tell you about its history, +whether or not any of the limits have ever been met or exceeded since +last power-up or reset. Be aware: When testing, it showed that the +status of Tout can change with neither of the alarms set. + +Temperature conversion of the DS1621 takes up to 1000ms; internal +access to non-volatile registers may last for 10ms or below. + +This driver also detects DS1625. As this chip is discontinued, you +should use a DS1621 instead. Detected 1625s also get the +"ds1621"-prefix, but the accuracy registers are not supported. +The latest modification of this driver was not checked against 1625s, +though, but should work well with them. + +Default values written into the DS1621 upon detection are: +Temperature limits: High 60.0; low 0.0 degree Celsius; +Continuous mode (not enabled), Tout active high, alarms reset. + + +Accessing DS1621 via /proc interface (part I) +--------------------------------------------- + +On detection (i.e. insmod, modprobe et al.), directories are being +created for each detected DS1621: + +/proc/sys/dev/sensors/ds1621-<0>-<1>/ +where <0> is the bus the chip was detected on (e. g. i2c-0) +and <1> the chip address ([48..4f]): ./ds1621-i2c-0-48/ + +Inside these directories, there are five files each: +alarms, continuous, enable, polarity and temp. + +The temp, alarms and enable are LM75-like for compatibility reasons: + +The temp file is rw. Reading gives TEMP_HIGH:TEMP_HYST:TEMP, where +TEMP_HIGH is the upper limit temp, TEMP_HYST is the hysteresis (lower +limit) temp and TEMP is the actual temperature. (For things like +ONE_SHOT, see below.) - Writing takes two results (read: input +values): TEMP_HIGH:TEMP_HYST. This provides an easy way to set limits. + +The alarms file is ro. Basically, it is 0x40 ("64") for the high alarm +bit set plus 0x20 ("32") for the low one. Since alarms are being reset +by the module when the limit conditions are no longer met, you should +first read the alarms before you do anything else (such as reading the +temperature), or the information may be lost. + +The polarity file is rw. Reading gives "0" for active high, which +describes a need for cooling corresponds to Tout status. "1" means +active low, so you can connect a powerful heater directly to Tout +(DON'T TRY!). - Writing accepts "0" and "1" accordingly. Again, note +that the statements on page 5 of the DS1621 data sheet are misleading; +change the orientation of the vertical arrows in the sketch there +instead. + +To understand the use of the other things, it is useful to have a +brief look at the internal DS1621 procedures: + + +High Accuracy Temperature Reading +--------------------------------- + +As said before, the temperature issued via the 9bit i2c-bus data is +somewhat arbitrary. Internally, the temperature conversion is of a +different kind that is explained (not so...) well in the DS1621 data +sheet. To cut the long story short: Inside the DS1621 there are two +oscillators, both of them biassed by a temperature coefficient. + +Higher accuracy of the temperature reading can be achieved using the +internal projection, which means taking account of REG_COUNT and +REG_SLOPE (the driver manages them): + +Taken from Dallas Semiconductors App Note 068: 'Increasing Temperature +Resolution on the DS1620' and App Note 105: 'High Resolution +Temperature Measurement with Dallas Direct-to-Digital Temperature +Sensors' + +- Read the 9bit temperature and strip the LSB (Truncate the .5 degs) +- The resulting value is TEMP_READ. +- Then, read REG_COUNT. +- And then, REG_SLOPE. + + TEMP = TEMP_READ - 0.25 + ((REG_SLOPE - REG_COUNT) / REG_SLOPE) + +Note that this is what the DONE bit in the DS1621 configuration +register is good for: Internally, one temperature conversion takes up +to 1000ms. Before that conversion is complete you will not be able to +read valid things out of REG_COUNT and REG_SLOPE. The DONE bit, as you +may have guessed by now, tells you whether the conversion is complete +("done", in plain english) and thus, whether the values you read are +good or not. + +The DS1621 has two modes of operation: "Continuous" conversion, which +can be understood as the default stand-alone mode where the chip gets +the temperature and controls external devices via its Tout pin or +tells other i2c's about it if they care. The other mode is called +"1SHOT", that means that it only figures out about the temperature +when it is explicitly told to do so; this can be seen as power saving +mode. + +Now if you want to read REG_COUNT and REG_SLOPE, you have to either +stop the continuous conversions until the contents of these registers +are valid, or, in 1SHOT mode, you have to have one conversion made. + + + +Accessing DS1621 via /proc interface (part II and end) +------------------------------------------------------ + +The continuous file is rw. Reading gives "1" when the chip is in +continuous mode and "0" for 1SHOT. - Writing accepts "1" to put it +into continuous mode or "0" for 1SHOT. + +The enable file is rw. Reading gives "1" if in continuous mode the +DS1621 is "running" or if in 1SHOT mode it is "shooting". In fact, the +value returned here is the negated DONE bit, just like a "BUSY" +flag. This means, that the "1" tells you that the values in REG_COUNT +and REG_SLOPE are not valid. "0", on the other hand, means that the +continous conversion had been stopped long enough to provide valid +readings and that one shot has successfully been fi- red, +respectively. - Writing accepts "1" to start continuous conversion or +to pull the trigger in 1SHOT mode, and "0" stops continuous conversion +to allow for valid readings. Writing "0" in 1SHOT doesn't make much +sense since you cannot catch a bullet, can you? No, the thing is that +DS1621 does only do one conversion then anyway, and after that, +there's nothing to stop. + +Last, but best try writing "0" to the "continuous" file and after that +"1" to the "enable" file. This gives you the precise temperature with +two digits after the radix (decimal point). +The precise results are only updated if the DONE bit was set, thus you +have to stop continous conversion early enough prior to reading, +i. e. 1000ms. And, on the other hand, you shouldn't forget to fire off +1SHOT in order to get the actual temperature rather than that of the +day before yesterday. So read enable before any of these. Chip Features @@ -68,13 +219,19 @@ Chip Features Chip `ds1621' LABEL LABEL CLASS COMPUTE CLASS ACCESS MAGNITUDE - temp NONE NONE R 1 - temp_hyst temp temp RW 1 - temp_over temp temp RW 1 - alarms NONE NONE R 0 + temp NONE NONE R 1 + temp_hyst temp temp RW 1 + temp_over temp temp RW 1 + alarms NONE NONE R 0 + enable NONE NONE RW 0 + continuous NONE NONE RW 0 + polarity NONE NONE RW 0 - LABEL FEATURE SYMBOL SYSCTL FILE:OFFSET - temp SENSORS_DS1621_TEMP temp:3 - temp_hyst SENSORS_DS1621_TEMP_HYST temp:2 - temp_over SENSORS_DS1621_TEMP_OVER temp:1 - alarms SENSORS_DS1621_ALARMS alarms:1 + LABEL FEATURE SYMBOL SYSCTL FILE:OFFSET + temp SENSORS_DS1621_TEMP temp:3 + temp_hyst SENSORS_DS1621_TEMP_HYST temp:2 + temp_over SENSORS_DS1621_TEMP_OVER temp:1 + alarms SENSORS_DS1621_ALARMS alarms:1 + enable SENSORS_DS1621_ENABLE enable:1 + continuous SENSORS_DS1621_CONTINUOUS continuous:1 + polarity SENSORS_DS1621_POLARITY polarity:1 diff --git a/kernel/chips/ds1621.c b/kernel/chips/ds1621.c index fc452802..68e2cbcd 100644 --- a/kernel/chips/ds1621.c +++ b/kernel/chips/ds1621.c @@ -48,13 +48,21 @@ static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; /* Insmod parameters */ SENSORS_INSMOD_1(ds1621); -/* Many DS1621 constants specified below - most aren't used */ +/* Many DS1621 constants specified below */ /* Config register used for detection */ /* 7 6 5 4 3 2 1 0 */ /* |Done|THF |TLF |NVB | 1 | 0 |POL |1SHOT| */ #define DS1621_REG_CONFIG_MASK 0x0C #define DS1621_REG_CONFIG_VAL 0x08 +#define DS1621_REG_CONFIG_POLARITY 0x02 +#define DS1621_REG_CONFIG_1SHOT 0x01 +#define DS1621_REG_CONFIG_DONE 0x80 + +/* Note: the done bit is always unset if continuous conversion is in progress. + We need to stop the continuous conversion or switch to single shot + before this bit becomes available! + */ /* The DS1621 registers */ #define DS1621_REG_TEMP 0xAA /* word, RO */ @@ -76,6 +84,8 @@ SENSORS_INSMOD_1(ds1621); (((val) + 2) / 5) << 7),0,0xffff)) #define ALARMS_FROM_REG(val) ((val) & \ (DS1621_ALARM_TEMP_HIGH | DS1621_ALARM_TEMP_LOW)) +#define ITEMP_FROM_REG(val) ((((val & 0x7fff) >> 8)) | \ + ((val & 0x8000)?-256:0)) /* Initial values */ #define DS1621_INIT_TEMP_OVER 600 @@ -89,8 +99,11 @@ struct ds1621_data { char valid; /* !=0 if following fields are valid */ unsigned long last_updated; /* In jiffies */ - u16 temp, temp_over, temp_hyst; /* Register values */ + u16 temp, temp_over, temp_hyst; /* Register values, word */ u8 conf; /* Register encoding, combined */ + + char enable; /* !=0 if we're expected to restart the conversion */ + u8 temp_int, temp_counter, temp_slope; /* Register values, byte */ }; #ifdef MODULE @@ -123,6 +136,10 @@ static void ds1621_alarms(struct i2c_client *client, int operation, int ctl_name, int *nrels_mag, long *results); static void ds1621_enable(struct i2c_client *client, int operation, int ctl_name, int *nrels_mag, long *results); +static void ds1621_continuous(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void ds1621_polarity(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); static void ds1621_update_client(struct i2c_client *client); @@ -144,12 +161,16 @@ static struct i2c_driver ds1621_driver = { is done through one of the 'extra' fields which are initialized when a new copy is allocated. */ static ctl_table ds1621_dir_table_template[] = { - {DS1621_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &i2c_proc_real, - &i2c_sysctl_real, NULL, &ds1621_temp}, + {DS1621_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &ds1621_temp}, {DS1621_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, &i2c_sysctl_real, NULL, &ds1621_alarms}, {DS1621_SYSCTL_ENABLE, "enable", NULL, 0, 0644, NULL, &i2c_proc_real, &i2c_sysctl_real, NULL, &ds1621_enable}, + {DS1621_SYSCTL_CONTINUOUS, "continuous", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &ds1621_continuous}, + {DS1621_SYSCTL_POLARITY, "polarity", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &ds1621_polarity}, {0} }; @@ -368,12 +389,26 @@ void ds1621_update_client(struct i2c_client *client) #endif data->conf = ds1621_read_value(client, DS1621_REG_CONF); - /* we should wait for the DONE bit... */ - data->temp = ds1621_read_value(client, DS1621_REG_TEMP); + + data->temp = ds1621_read_value(client, + DS1621_REG_TEMP); data->temp_over = ds1621_read_value(client, DS1621_REG_TEMP_OVER); - data->temp_hyst = - ds1621_read_value(client, DS1621_REG_TEMP_HYST); + data->temp_hyst = ds1621_read_value(client, + DS1621_REG_TEMP_HYST); + + /* wait for the DONE bit before reading extended values */ + + if (data->conf & DS1621_REG_CONFIG_DONE) { + data->temp_counter = ds1621_read_value(client, + DS1621_REG_TEMP_COUNTER); + data->temp_slope = ds1621_read_value(client, + DS1621_REG_TEMP_SLOPE); + data->temp_int = ITEMP_FROM_REG(data->temp); + /* restart the conversion */ + if (data->enable) + ds1621_read_value(client, DS1621_COM_START); + } /* reset alarms if neccessary */ new_conf = data->conf; @@ -398,12 +433,29 @@ void ds1621_temp(struct i2c_client *client, int operation, int ctl_name, { struct ds1621_data *data = client->data; if (operation == SENSORS_PROC_REAL_INFO) - *nrels_mag = 1; + if (!(data->conf & DS1621_REG_CONFIG_DONE) || + (data->temp_counter > data->temp_slope) || + (data->temp_slope == 0)) { + *nrels_mag = 1; + } else { + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_READ) { ds1621_update_client(client); - results[0] = TEMP_FROM_REG(data->temp_over); - results[1] = TEMP_FROM_REG(data->temp_hyst); - results[2] = TEMP_FROM_REG(data->temp); + /* decide wether to calculate more precise temp */ + if (!(data->conf & DS1621_REG_CONFIG_DONE) || + (data->temp_counter > data->temp_slope) || + (data->temp_slope == 0)) { + results[0] = TEMP_FROM_REG(data->temp_over); + results[1] = TEMP_FROM_REG(data->temp_hyst); + results[2] = TEMP_FROM_REG(data->temp); + } else { + results[0] = TEMP_FROM_REG(data->temp_over)*10; + results[1] = TEMP_FROM_REG(data->temp_hyst)*10; + results[2] = data->temp_int * 100 - 25 + + ((data->temp_slope - data->temp_counter) * + 100 / data->temp_slope); + } *nrels_mag = 3; } else if (operation == SENSORS_PROC_REAL_WRITE) { if (*nrels_mag >= 1) { @@ -439,19 +491,76 @@ void ds1621_enable(struct i2c_client *client, int operation, int ctl_name, /* sometimes needed to (re)start the continous conversion */ /* there is no data to read so this might hang your SMBus! */ + struct ds1621_data *data = client->data; if (operation == SENSORS_PROC_REAL_INFO) *nrels_mag = 0; else if (operation == SENSORS_PROC_REAL_READ) { - *nrels_mag = 0; + ds1621_update_client(client); + results[0] = !(data->conf & DS1621_REG_CONFIG_DONE); + *nrels_mag = 1; } else if (operation == SENSORS_PROC_REAL_WRITE) { if (*nrels_mag >= 1) { if (results[0]) { ds1621_read_value(client, DS1621_COM_START); + data->enable=1; } else { ds1621_read_value(client, DS1621_COM_STOP); + data->enable=0; } } else { ds1621_read_value(client, DS1621_COM_START); + data->enable=1; + } + } +} + +void ds1621_continuous(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct ds1621_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + ds1621_update_client(client); + results[0] = !(data->conf & DS1621_REG_CONFIG_1SHOT); + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + ds1621_update_client(client); + if (*nrels_mag >= 1) { + if (results[0]) { + ds1621_write_value(client, DS1621_REG_CONF, + data->conf & ~DS1621_REG_CONFIG_1SHOT); + } else { + ds1621_write_value(client, DS1621_REG_CONF, + data->conf | DS1621_REG_CONFIG_1SHOT); + } + } else { + ds1621_write_value(client, DS1621_REG_CONF, + data->conf & ~DS1621_REG_CONFIG_1SHOT); + } + } +} + +void ds1621_polarity(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct ds1621_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + ds1621_update_client(client); + results[0] = !(!(data->conf & DS1621_REG_CONFIG_POLARITY)); + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + ds1621_update_client(client); + if (*nrels_mag >= 1) { + if (results[0]) { + ds1621_write_value(client, DS1621_REG_CONF, + data->conf | DS1621_REG_CONFIG_POLARITY); + } else { + ds1621_write_value(client, DS1621_REG_CONF, + data->conf & ~DS1621_REG_CONFIG_POLARITY); + } } } } diff --git a/kernel/include/sensors.h b/kernel/include/sensors.h index 3b613187..29fa2c4e 100644 --- a/kernel/include/sensors.h +++ b/kernel/include/sensors.h @@ -467,6 +467,8 @@ #define DS1621_ALARM_TEMP_HIGH 0x40 #define DS1621_ALARM_TEMP_LOW 0x20 #define DS1621_SYSCTL_ENABLE 2002 +#define DS1621_SYSCTL_CONTINUOUS 2003 +#define DS1621_SYSCTL_POLARITY 2004 #define IT87_SYSCTL_IN0 1000 /* Volts * 100 */ #define IT87_SYSCTL_IN1 1001 diff --git a/lib/chips.h b/lib/chips.h index f0c389be..9febea0b 100644 --- a/lib/chips.h +++ b/lib/chips.h @@ -924,6 +924,9 @@ #define SENSORS_DS1621_TEMP_HYST 52 /* RW */ #define SENSORS_DS1621_TEMP_OVER 53 /* RW */ #define SENSORS_DS1621_ALARMS 81 /* R */ +#define SENSORS_DS1621_ENABLE 82 /* RW */ +#define SENSORS_DS1621_CONTINUOUS 83 /* RW */ +#define SENSORS_DS1621_POLARITY 84 /* RW */ /* ADM1024 chip */