(jrm): New driver and other support for the Maxim MAX6650 and MAX6651 chips.

Mainly targetted (and tested) with the 6651, but should work with
       the MAX6550.

       See doc/chips/max6650 for notes, bugs, assumptions, etc.


git-svn-id: http://lm-sensors.org/svn/lm-sensors/trunk@1987 7894878c-1315-0410-8ee3-d5d059ff63e0
This commit is contained in:
John Morris committed 2003-08-31 20:45:12 +00:00
1 parent ebb1dc9a20
commit 45576fb3fd
11 files changed
+786 -2

No files matched your search

+1
View File
@@ -58,6 +58,7 @@ ask CVS about it:
Module lm78: Fix a fan_div/fan_min bug
Module lm83: Add t_crit and alarms support
Module lm90: New
Module max6650: New
Module saa1064: New
Module w83781d: Enhance chip detection
Module xeontemp: New
+3
View File
@@ -90,3 +90,6 @@ problems.
Author of the ADM1026 chip driver.
* Sascha Volkenandt <sascha@akv-soft.de>
Author of the SAA1064 chip driver.
* John Morris (john.morris@spirentcom.com)
Author of the MAX6650 chip driver.
+1 -1
View File
@@ -77,7 +77,7 @@ At least the following hardware sensor chips are supported:
Genesys Logic GL518SM (rev 00, 80), GL520SM, GL523SM
Intel Xeon processor embedded sensors
ITE IT8705F, IT8712F embedded sensors
Maxim MAX1617 and MAX1617A
Maxim MAX1617, MAX1617A, MAX6650 and MAX6651
Microchip TC1068, TCM1617, TCN75
Myson MTP008
National Semiconductor LM75, LM77, LM78, LM78-J, LM79,
+125
View File
@@ -0,0 +1,125 @@
Sensors chip driver for MAXIM MAX6650 / MAX6651 fan controllers
===============================================================
Chip notes
----------
The MAX6551 is a fan controller. It has two distinct functions:
1) Speed control, with feedback, for a single fan. The code can write
to a register requesting that the fan run at a certain speed, and the
chip will adjust the fan voltage until it reaches that speed. This
is called "closed loop" mode in the datasheet.
2) Speed measurement (tachometers) for up to four fans, including the
controlled one.
The MAX6550 is similar but only has one measurement tachometer.
Both chips have a few general purpose i/o pins, some of which can be
configured to have special functions. At the time of writing an overview
of the chip, with links to the datasheet, can be seen at:
http://www.maxim-ic.com/quick_view2.cfm/qv_pk/2296/ln/en
If that has moved searching for MAX6550 from Maxim's home page should
work.
Driver state
------------
Currently the driver provides the following entries in /proc/sys/
dev/sensors/max6650-xxx/:
fan1 through fan4 (read only): the current measured fan speeds, in
rpm.
speed (read/write): when written, sets the chip to closed loop mode and
sets the configured speed, in rpm, to the value written. When read returns
the configured speed, again in rpm.
xdump (read only): returns all of the chip register values, except the
tachometers. Used for debug, and should probably go away or be conditionally
compiled.
The MAX665x uses byte wide registers with various scaling factors and
some more or less complex equations (in the datasheet) relating register
values to real world values. The driver takes care of all of this.
Note that the measured speed ("fan1") never exactly matches the set speed
("speed") because of the coarse resolution of the underlying registers.
Assumptions
-----------
The driver assumes that:
o The MAX665x is running from its internal 254 kHz clock, not an
external clock.
o The monitored fans produce two pulses per rotation (This same
assumption is made by Maxim throughout the datasheet.)
o The tachometer count time (register 0x16) has been configured suitably
for the fans in use, perhaps by some boot code. The driver reads this
register to compute rpm, but never writes to it.
o The speed controller prescale (low three bits of register 0x04) has
been configured suitably for the fans in use, perhaps by some boot
code. The driver reads these bits to compute rpm, but never changes
them.
The driver currently never reads or writes any other configuration
(gpio config, alarms, 5V/12V setting).
Bugs
----
o The driver assumes too much: it should be possible to set more of the
configuration dynamically instead of assuming that some other code has
done it.
o It does not differentiate between a MAX6550 and a MAX6551, as I can
see no obvious way to do this. It has only been tested with a MAX6551
so if anyone gets a board with a MAX6550 to test with maybe something
will be revealed, such as the three missing tachometer registers
returning all ones or something. (OK, so maybe I should have called
this driver max6651, and ignored the max6650 totally...)
o The /proc names don't match the standards in proc/sevelopers/proc,
as this chip doesn't map well to other fan controllers,
Detection notes
---------------
The module will autodetect the presence of a max6650 or max6651 at any
of the four possible I2C addresses for these devices. There is no ID
register, but there are several registers with all zero bits at the
high end. The driver checks that all of these are in fact zero before
declaring the chip to be a MAX6550/1.
sensors-detect
--------------
Support for MAX6650/1 has been added to sensors-detect. It has been
tested as correctly detecting a MAX6551 at I2C address 0x1B. Like the
driver, it cannot yet distinguish a MAX6650 and MAX6651.
prog/sensors
------------
Initial read-only support has been added to the "sensors" program.
Testing
-------
The driver and associated programs have been tested under Linux 2.4.18
running on an embedded Intel XScale processor (actually an IXP425 running
in big-endian mode) with a MAX6651 chip.
Author
------
John Morris (john.morris@spirentcom.com)
+539
View File
@@ -0,0 +1,539 @@
/*
* max6650.c - Part of lm_sensors, Linux kernel modules for hardware
* monitoring.
*
* Author: John Morris <john.morris@spirentcom.com>
*
* Copyright (c) 2003 Spirent Communications
*
* This module has only been tested with the MAX6651 chip. It should
* work with the MAX6650 also, though with reduced functionality. It
* does not yet distinguish max6650 and max6651 chips.
*
* Tha datasheet was last seen at:
*
* http://pdfserv.maxim-ic.com/en/ds/MAX6650-MAX6651.pdf
*
* 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.
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/i2c-proc.h>
#include <linux/i2c-id.h>
#include <linux/init.h>
#include "version.h"
/*
* Addresses to scan. There are four disjoint possibilities, by pin config.
*/
static unsigned short normal_i2c[] = {0x1b, 0x1f, 0x48, 0x4b, SENSORS_I2C_END};
static unsigned short normal_i2c_range[] = { SENSORS_I2C_END };
static unsigned int normal_isa[] = { SENSORS_ISA_END };
static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
/*
* Insmod parameters
*/
SENSORS_INSMOD_1(max6650);
/*
* MAX 6650/6651 registers
*/
#define MAX6650_REG_SPEED 0x00
#define MAX6650_REG_CONFIG 0x02
#define MAX6650_REG_GPIO_DEF 0x04
#define MAX6650_REG_DAC 0x06
#define MAX6650_REG_ALARM_EN 0x08
#define MAX6650_REG_ALARM 0x0A
#define MAX6650_REG_TACH0 0x0C
#define MAX6650_REG_TACH1 0x0E
#define MAX6650_REG_TACH2 0x10
#define MAX6650_REG_TACH3 0x12
#define MAX6650_REG_GPIO_STAT 0x14
#define MAX6650_REG_COUNT 0x16
/*
* Config register bits
*/
#define MAX6650_CFG_MODE_MASK 0x30
#define MAX6650_CFG_MODE_ON 0x00
#define MAX6650_CFG_MODE_OFF 0x10
#define MAX6650_CFG_MODE_CLOSED_LOOP 0x20
#define MAX6650_CFG_MODE_OPEN_LOOP 0x30
static const u8 tach_reg[] =
{
MAX6650_REG_TACH0, MAX6650_REG_TACH1,
MAX6650_REG_TACH2, MAX6650_REG_TACH3
};
#define MAX6650_INT_CLK 254000 /* Default clock speed - 254 kHz */
/*
* Functions declaration
*/
static void max6650_fan (struct i2c_client *client, int operation, int
ctl_name, int *nrels_mag, long *results);
static void max6650_speed (struct i2c_client *client, int operation, int
ctl_name, int *nrels_mag, long *results);
static void max6650_xdump (struct i2c_client *client, int operation, int
ctl_name, int *nrels_mag, long *results);
static int max6650_detect(struct i2c_adapter *adapter, int address, unsigned
short flags, int kind);
static int max6650_attach_adapter(struct i2c_adapter *adapter);
static int max6650_detach_client(struct i2c_client *client);
static void max6650_init_client(struct i2c_client *client);
static int max6650_read(struct i2c_client *client, u8 reg);
/*
* Driver data (common to all clients)
*/
static struct i2c_driver max6650_driver = {
.owner = THIS_MODULE,
.name = "MAX6650/1 sensor driver",
.id = I2C_DRIVERID_MAX6650,
.flags = I2C_DF_NOTIFY,
.attach_adapter = max6650_attach_adapter,
.detach_client = max6650_detach_client
};
/*
* Client data (each client gets its own)
*/
struct max6650_data
{
int sysctl_id;
struct semaphore update_lock;
char valid; /* zero until following fields are valid */
unsigned long last_updated; /* in jiffies */
/* register values */
u8 speed;
u8 config;
u8 tach[4];
u8 count;
};
/*
* Proc entries
* These files are created for each detected max6650.
*/
/* -- SENSORS SYSCTL START -- */
#define MAX6650_SYSCTL_FAN1 1101
#define MAX6650_SYSCTL_FAN2 1102
#define MAX6650_SYSCTL_FAN3 1103
#define MAX6650_SYSCTL_FAN4 1104
#define MAX6650_SYSCTL_SPEED 1105
#define MAX6650_SYSCTL_XDUMP 1106
/* -- SENSORS SYSCTL END -- */
static ctl_table max6650_dir_table_template[] =
{
{MAX6650_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL,
&i2c_proc_real, &i2c_sysctl_real, NULL, &max6650_fan},
{MAX6650_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL,
&i2c_proc_real, &i2c_sysctl_real, NULL, &max6650_fan},
{MAX6650_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL,
&i2c_proc_real, &i2c_sysctl_real, NULL, &max6650_fan},
{MAX6650_SYSCTL_FAN4, "fan4", NULL, 0, 0644, NULL,
&i2c_proc_real, &i2c_sysctl_real, NULL, &max6650_fan},
{MAX6650_SYSCTL_SPEED, "speed", NULL, 0, 0644, NULL,
&i2c_proc_real, &i2c_sysctl_real, NULL, &max6650_speed},
{MAX6650_SYSCTL_XDUMP, "xdump", NULL, 0, 0644, NULL,
&i2c_proc_real, &i2c_sysctl_real, NULL, &max6650_xdump},
{0}
};
/*
* Internal variables
*/
static int max6650_id = 0;
/*
* Real code
*/
static int max6650_attach_adapter(struct i2c_adapter *adapter)
{
return i2c_detect(adapter, &addr_data, max6650_detect);
}
/*
* The following function does more than just detection. If detection
* succeeds, it also registers the new chip.
*/
static int max6650_detect(struct i2c_adapter *adapter, int address, unsigned
short flags, int kind)
{
struct i2c_client *new_client;
struct max6650_data *data;
int err = 0;
const char *type_name = "";
const char *client_name = "";
#ifdef DEBUG
if (i2c_is_isa_adapter(adapter)) {
printk("max6650.o: Called for an ISA bus adapter, aborting.\n");
return 0;
}
#endif
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
#ifdef DEBUG
printk("max6650.o: I2C bus doesn't support byte read mode, skipping.\n");
#endif
return 0;
}
if (!(new_client = kmalloc(sizeof(struct i2c_client) + sizeof(struct
max6650_data), GFP_KERNEL))) {
printk("max6650.o: Out of memory in max6650_detect (new_client).\n");
return -ENOMEM;
}
/*
* The max6650-specific data is placed right after the common I2C
* client data, and is pointed to by the data field from the I2C
* client data.
*/
new_client->addr = address;
new_client->data = data = (struct max6650_data *) (new_client + 1);
new_client->adapter = adapter;
new_client->driver = &max6650_driver;
new_client->flags = 0;
/*
* Now we do the remaining detection. A negative kind means that
* the driver was loaded with no force parameter (default), so we
* must both detect and identify the chip (actually there is only
* one possible kind of chip for now, max6650). A zero kind means that
* the driver was loaded with the force parameter, the detection
* step shall be skipped. A positive kind means that the driver
* was loaded with the force parameter and a given kind of chip is
* requested, so both the detection and the identification steps
* are skipped.
*
* Currently I can find no way to distinguish between a MAX6650 and
* a MAX6651. This driver has only been tried on the latter.
*/
if (kind < 0) { /* detection */
if (
(max6650_read(new_client, MAX6650_REG_CONFIG) & 0xC0) ||
(max6650_read(new_client, MAX6650_REG_GPIO_STAT) & 0xE0) ||
(max6650_read(new_client, MAX6650_REG_ALARM_EN) & 0xE0) ||
(max6650_read(new_client, MAX6650_REG_ALARM) & 0xE0) ||
(max6650_read(new_client, MAX6650_REG_COUNT) & 0xFC)
)
{
#ifdef DEBUG
printk("max6650.o: max6650 detection failed at 0x%02x.\n",
address);
#endif
goto ERROR1;
}
}
if (kind <= 0) { /* identification */
kind = max6650;
}
if (kind <= 0) { /* identification failed */
printk("max6650.o: Unsupported chip.\n");
goto ERROR1;
}
if (kind == max6650) {
type_name = "max6650";
client_name = "max6650 chip";
} else {
printk("max6650.o: Unknown kind %d.\n", kind);
goto ERROR1;
}
/*
* OK, we got a valid chip so we can fill in the remaining client
* fields.
*/
strcpy(new_client->name, client_name);
new_client->id = max6650_id++;
data->valid = 0;
init_MUTEX(&data->update_lock);
/*
* Tell the I2C layer a new client has arrived.
*/
if ((err = i2c_attach_client(new_client))) {
#ifdef DEBUG
printk("max6650.o: Failed attaching client.\n");
#endif
goto ERROR1;
}
/*
* Register a new directory entry.
*/
if ((err = i2c_register_entry(new_client, type_name,
max6650_dir_table_template)) < 0) {
#ifdef DEBUG
printk("max6650.o: Failed registering directory entry.\n");
#endif
goto ERROR2;
}
data->sysctl_id = err;
/*
* Initialize the max6650 chip
*/
max6650_init_client(new_client);
return 0;
ERROR2:
i2c_detach_client(new_client);
ERROR1:
kfree(new_client);
return err;
}
static void max6650_init_client(struct i2c_client *client)
{
/* Nothing to do here - assume the BIOS has initialized the chip */
}
static int max6650_detach_client(struct i2c_client *client)
{
int err;
i2c_deregister_entry(((struct max6650_data *) (client->data))->sysctl_id);
if ((err = i2c_detach_client(client))) {
printk("max6650.o: Client deregistration failed, "
"client not detached.\n");
return err;
}
kfree(client);
return 0;
}
static int max6650_read(struct i2c_client *client, u8 reg)
{
return i2c_smbus_read_byte_data(client, reg);
}
static int max6650_write(struct i2c_client *client, u8 reg, u8 value)
{
return i2c_smbus_write_byte_data(client, reg, value);
}
static void max6650_update_client(struct i2c_client *client)
{
int i;
struct max6650_data *data = client->data;
down(&data->update_lock);
if ((jiffies - data->last_updated > HZ) ||
(jiffies < data->last_updated) || !data->valid) {
#ifdef DEBUG
printk("max6650.o: Updating max6650 data.\n");
#endif
data->speed = max6650_read (client, MAX6650_REG_SPEED);
data->config = max6650_read (client, MAX6650_REG_CONFIG);
for (i = 0; i < 4; i++) {
data->tach[i] = max6650_read(client, tach_reg[i]);
}
data->count = max6650_read (client, MAX6650_REG_COUNT);
data->last_updated = jiffies;
data->valid = 1;
}
up(&data->update_lock);
}
static void max6650_fan (struct i2c_client *client, int operation, int
ctl_name, int *nrels_mag, long *results)
{
int index = ctl_name - MAX6650_SYSCTL_FAN1;
struct max6650_data *data = client->data;
int tcount; /* Tachometer count time, 0.25 second units */
if (operation == SENSORS_PROC_REAL_INFO) {
*nrels_mag = 0;
} else if (operation == SENSORS_PROC_REAL_READ) {
max6650_update_client(client);
/*
* Calculation details:
*
* Each tachometer counts over an interval given by the "count"
* register (0.25, 0.5, 1 or 2 seconds). This module assumes
* that the fans produce two pulses per revolution (this seems
* to be the most common).
*/
tcount = 1 << data->count; /* 0.25 second units */
results[0] = (data->tach[index] * 240) / tcount; /* counts per min */
results[0] /= 2; /* Assume two counts per rev */
*nrels_mag = 1;
}
}
/*
* Set the fan speed to the specified RPM (or read back the RPM setting).
*
* The MAX6650/1 will automatically control fan speed when in closed loop
* mode.
*
* Assumptions:
*
* 1) The MAX6650/1 is running from its internal 254kHz clock (perhaps
* this should be made a module parameter).
*
* 2) The prescaler (low three bits of the config register) has already
* been set to an appropriate value.
*
* The relevant equations are given on pages 21 and 22 of the datasheet.
*
* From the datasheet, the relevant equation when in regulation is:
*
* [fCLK / (128 x (KTACH + 1))] = 2 x FanSpeed / KSCALE
*
* where:
*
* fCLK is the oscillator frequency (either the 254kHz internal
* oscillator or the externally applied clock)
*
* KTACH is the value in the speed register
*
* FanSpeed is the speed of the fan in rps
*
* KSCALE is the prescaler value (1, 2, 4, 8, or 16)
*
* When reading, we need to solve for FanSpeed. When writing, we need to
* solve for KTACH.
*
* Note: this tachometer is completely separate from the tachometers
* used to measure the fan speeds. Only one fan's speed (fan1) is
* controlled.
*/
static void max6650_speed (struct i2c_client *client, int operation, int
ctl_name, int *nrels_mag, long *results)
{
struct max6650_data *data = client->data;
int kscale, ktach, fclk, rpm;
if (operation == SENSORS_PROC_REAL_INFO) {
*nrels_mag = 0;
} else if (operation == SENSORS_PROC_REAL_READ) {
/*
* Use the datasheet equation:
*
* FanSpeed = KSCALE x fCLK / [256 x (KTACH + 1)]
*
* then multiply by 60 to give rpm.
*/
max6650_update_client(client);
kscale = 1 << (data->config & 7);
ktach = data->speed;
fclk = MAX6650_INT_CLK;
rpm = 60 * kscale * fclk / (256 * (ktach + 1));
results[0] = rpm;
*nrels_mag = 1;
} else if (operation == SENSORS_PROC_REAL_WRITE && *nrels_mag >= 1) {
/*
* Divide the required speed by 60 to get from rpm to rps, then
* use the datasheet equation:
*
* KTACH = [(fCLK x KSCALE) / (256 x FanSpeed)] - 1
*/
max6650_update_client(client);
rpm = results[0];
kscale = 1 << (data->config & 7);
fclk = MAX6650_INT_CLK;
ktach = ((fclk * kscale) / (256 * rpm / 60)) - 1;
data->speed = ktach;
data->config = (data->config & ~MAX6650_CFG_MODE_MASK) |
MAX6650_CFG_MODE_CLOSED_LOOP;
max6650_write (client, MAX6650_REG_CONFIG, data->config);
max6650_write (client, MAX6650_REG_SPEED, data->speed);
}
}
/*
* Debug - dump all registers except the tach counts.
*/
static void max6650_xdump (struct i2c_client *client, int operation, int
ctl_name, int *nrels_mag, long *results)
{
if (operation == SENSORS_PROC_REAL_INFO) {
*nrels_mag = 0;
} else if (operation == SENSORS_PROC_REAL_READ) {
results[0] = max6650_read (client, MAX6650_REG_SPEED);
results[1] = max6650_read (client, MAX6650_REG_CONFIG);
results[2] = max6650_read (client, MAX6650_REG_GPIO_DEF);
results[3] = max6650_read (client, MAX6650_REG_DAC);
results[4] = max6650_read (client, MAX6650_REG_ALARM_EN);
results[5] = max6650_read (client, MAX6650_REG_ALARM);
results[6] = max6650_read (client, MAX6650_REG_GPIO_STAT);
results[7] = max6650_read (client, MAX6650_REG_COUNT);
*nrels_mag = 8;
}
}
static int __init sm_max6650_init(void)
{
printk(KERN_INFO "max6650.o version %s (%s)\n", LM_VERSION, LM_DATE);
return i2c_add_driver(&max6650_driver);
}
static void __exit sm_max6650_exit(void)
{
i2c_del_driver(&max6650_driver);
}
MODULE_AUTHOR("john.morris@spirentcom.com");
MODULE_DESCRIPTION("max6650 sensor driver");
MODULE_LICENSE("GPL");
module_init(sm_max6650_init);
module_exit(sm_max6650_exit);
+20
View File
@@ -4443,6 +4443,25 @@ static sensors_chip_feature xeontemp_features[] =
{ 0 }
};
static sensors_chip_feature max6650_features[] =
{
{ SENSORS_MAX6650_FAN1_TACH, "fan1", SENSORS_NO_MAPPING,
SENSORS_NO_MAPPING, SENSORS_MODE_R,
MAX6650_SYSCTL_FAN1, VALUE(1), 0 },
{ SENSORS_MAX6650_FAN2_TACH, "fan2", SENSORS_NO_MAPPING,
SENSORS_NO_MAPPING, SENSORS_MODE_R,
MAX6650_SYSCTL_FAN2, VALUE(1), 0 },
{ SENSORS_MAX6650_FAN3_TACH, "fan3", SENSORS_NO_MAPPING,
SENSORS_NO_MAPPING, SENSORS_MODE_R,
MAX6650_SYSCTL_FAN3, VALUE(1), 0 },
{ SENSORS_MAX6650_FAN4_TACH, "fan4", SENSORS_NO_MAPPING,
SENSORS_NO_MAPPING, SENSORS_MODE_R,
MAX6650_SYSCTL_FAN4, VALUE(1), 0 },
{ SENSORS_MAX6650_SPEED, "speed", SENSORS_NO_MAPPING,
SENSORS_NO_MAPPING, SENSORS_MODE_RW,
MAX6650_SYSCTL_SPEED, VALUE(1), 0 },
};
sensors_chip_features sensors_chip_features_list[] =
{
{ SENSORS_LM78_PREFIX, lm78_features },
@@ -4511,5 +4530,6 @@ sensors_chip_features sensors_chip_features_list[] =
{ SENSORS_LM83_PREFIX, lm83_features },
{ SENSORS_LM90_PREFIX, lm90_features },
{ SENSORS_XEONTEMP_PREFIX, xeontemp_features },
{ SENSORS_MAX6650_PREFIX, max6650_features },
{ 0 }
};
+10
View File
@@ -1655,4 +1655,14 @@
#define SENSORS_XEONTEMP_REMOTE_TEMP_OVER 56 /* RW */
#define SENSORS_XEONTEMP_ALARMS 81 /* R */
/* MAX6650 / 1 chips */
#define SENSORS_MAX6650_PREFIX "max6650"
#define SENSORS_MAX6650_FAN1_TACH 1 /* R */
#define SENSORS_MAX6650_FAN2_TACH 2 /* R */
#define SENSORS_MAX6650_FAN3_TACH 3 /* R */
#define SENSORS_MAX6650_FAN4_TACH 4 /* R */
#define SENSORS_MAX6650_SPEED 5 /* RW */
#endif /* def LIB_SENSORS_CHIPS_H */
+35 -1
View File
@@ -776,7 +776,7 @@ use subs qw(mtp008_detect lm78_detect lm78_isa_detect lm78_alias_detect
fscscy_detect pcf8591_detect arp_detect ipmi_kcs_detect
ipmi_smic_detect via8231_isa_detect lm85_detect smartbatt_detect
adm1026_detect w83l785ts_detect lm83_detect lm90_detect
saa1064_detect w83l784r_detect mozart_detect);
saa1064_detect w83l784r_detect mozart_detect max6650_detect);
# This is a list of all recognized chips.
# Each entry must have the following fields:
@@ -1060,6 +1060,12 @@ use subs qw(mtp008_detect lm78_detect lm78_isa_detect lm78_alias_detect
i2c_addrs => [0x18..0x1a,0x29..0x2b,0x4c..0x4e],
i2c_detect => sub { adm1021_detect 3, @_ },
},
{
name => "Maxim MAX6650/MAX6651",
driver => "max6650",
i2c_addrs => [0x1b,0x1f,0x48,0x4b],
i2c_detect => sub { max6650_detect 0, @_ },
},
{
name => "TI THMC10",
driver => "adm1021",
@@ -3400,6 +3406,34 @@ sub w83l785ts_detect
return (8);
}
# $_[0]: Chip to detect. Always zero for now, but available for future use
# if somebody finds a way to distinguish MAX6650 and MAX6651.
# $_[1]: A reference to the file descriptor to access this chip.
# We may assume an i2c_set_slave_addr was already done.
# $_[2]: Address
# Returns: undef if not detected, 4 if detected.
#
# The max6650 has no device ID register. However, a few registers have
# spare bits, which are documented as being always zero on read. We read
# all of these registers check the spare bits. Any non-zero means this
# is not a max6650/1.
#
# The always zero bits are:
# configuration byte register (0x02) - top 2 bits
# gpio status register (0x14) - top 3 bits
# alarm enable register (0x08) - top 3 bits
# alarm status register (0x0A) - top 3 bits
# tachometer count time register (0x16) - top 6 bits
sub max6650_detect
{
my ($chip, $file) = @_;
return if i2c_smbus_read_byte_data($file,0x16) & 0xFC;
return if i2c_smbus_read_byte_data($file,0x0A) & 0xE0;
return if i2c_smbus_read_byte_data($file,0x08) & 0xE0;
return if i2c_smbus_read_byte_data($file,0x14) & 0xE0;
return if i2c_smbus_read_byte_data($file,0x02) & 0xC0;
return 4;
}
################
+49
View File
@@ -4511,6 +4511,55 @@ void print_xeontemp(const sensors_chip_name *name)
free_the_label(&label);
}
void print_max6650(const sensors_chip_name *name)
{
char *label = NULL;
double tach, speed;
int valid, i;
static const struct
{
int tag;
char *name;
}
tach_list[] =
{
{ SENSORS_MAX6650_FAN1_TACH, "FAN1" },
{ SENSORS_MAX6650_FAN2_TACH, "FAN2" },
{ SENSORS_MAX6650_FAN3_TACH, "FAN3" },
{ SENSORS_MAX6650_FAN4_TACH, "FAN4" }
};
/* Display full config for fan1, which is controlled */
if (!sensors_get_label_and_valid(*name,tach_list[0].tag,&label,&valid) &&
!sensors_get_feature(*name,tach_list[0].tag,&tach) &&
!sensors_get_feature(*name,SENSORS_MAX6650_SPEED,&speed)) {
if (valid) {
print_label(label,10);
printf("configured %4.0f RPM, actual %4.0f RPM.\n", speed, tach);
}
} else
printf("ERROR: Can't get %s data!\n", tach_list[i].name);
free_the_label(&label);
/* Just display the measured speed for the other three, uncontrolled fans */
for (i = 1; i < 4; i++)
{
if (!sensors_get_label_and_valid(*name,tach_list[i].tag,&label,&valid) &&
!sensors_get_feature(*name,tach_list[i].tag,&tach)) {
if (valid) {
print_label(label,10);
printf("%4.0f RPM \n", tach);
}
} else
printf("ERROR: Can't get %s data!\n", tach_list[i].name);
free_the_label(&label);
}
}
void print_unknown_chip(const sensors_chip_name *name)
{
int a,b,valid;
+1
View File
@@ -55,5 +55,6 @@ extern void print_bmc(const sensors_chip_name *name);
extern void print_lm83(const sensors_chip_name *name);
extern void print_lm90(const sensors_chip_name *name);
extern void print_xeontemp(const sensors_chip_name *name);
extern void print_max6650(const sensors_chip_name *name);
#endif /* def PROG_SENSORS_CHIPS_H */
+2
View File
@@ -380,6 +380,8 @@ void do_a_print(sensors_chip_name name)
print_lm90(&name);
else if (!strcmp(name.prefix,"xeontemp"))
print_xeontemp(&name);
else if (!strcmp(name.prefix,"max6650"))
print_max6650(&name);
else
print_unknown_chip(&name);
printf("\n");