hwmon: Driver for Maxim MAX6697 and compatibles

Add support for MAX6581, MAX6602, MAX6622, MAX6636, MAX6689, MAX6693,
MAX6694, MAX6697, MAX6698, and MAX6699 temperature sensors

Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Reviewed-by: Jean Delvare <khali@linux-fr.org>
This commit is contained in:
Guenter Roeck 2012-10-12 23:01:58 -07:00
parent 412e29c135
commit 5372d2d71c
6 changed files with 896 additions and 0 deletions

View file

@ -0,0 +1,64 @@
max6697 properties
Required properties:
- compatible:
Should be one of
maxim,max6581
maxim,max6602
maxim,max6622
maxim,max6636
maxim,max6689
maxim,max6693
maxim,max6694
maxim,max6697
maxim,max6698
maxim,max6699
- reg: I2C address
Optional properties:
- smbus-timeout-disable
Set to disable SMBus timeout. If not specified, SMBus timeout will be
enabled.
- extended-range-enable
Only valid for MAX6581. Set to enable extended temperature range.
Extended temperature will be disabled if not specified.
- beta-compensation-enable
Only valid for MAX6693 and MX6694. Set to enable beta compensation on
remote temperature channel 1.
Beta compensation will be disabled if not specified.
- alert-mask
Alert bit mask. Alert disabled for bits set.
Select bit 0 for local temperature, bit 1..7 for remote temperatures.
If not specified, alert will be enabled for all channels.
- over-temperature-mask
Over-temperature bit mask. Over-temperature reporting disabled for
bits set.
Select bit 0 for local temperature, bit 1..7 for remote temperatures.
If not specified, over-temperature reporting will be enabled for all
channels.
- resistance-cancellation
Boolean for all chips other than MAX6581. Set to enable resistance
cancellation on remote temperature channel 1.
For MAX6581, resistance cancellation enabled for all channels if
specified as boolean, otherwise as per bit mask specified.
Only supported for remote temperatures (bit 1..7).
If not specified, resistance cancellation will be disabled for all
channels.
- transistor-ideality
For MAX6581 only. Two values; first is bit mask, second is ideality
select value as per MAX6581 data sheet. Select bit 1..7 for remote
channels.
Transistor ideality will be initialized to default (1.008) if not
specified.
Example:
temp-sensor@1a {
compatible = "maxim,max6697";
reg = <0x1a>;
smbus-timeout-disable;
resistance-cancellation;
alert-mask = <0x72>;
over-temperature-mask = <0x7f>;
};

View file

@ -0,0 +1,58 @@
Kernel driver max6697
=====================
Supported chips:
* Maxim MAX6581
Prefix: 'max6581'
Datasheet: http://datasheets.maximintegrated.com/en/ds/MAX6581.pdf
* Maxim MAX6602
Prefix: 'max6602'
Datasheet: http://datasheets.maximintegrated.com/en/ds/MAX6602.pdf
* Maxim MAX6622
Prefix: 'max6622'
Datasheet: http://datasheets.maximintegrated.com/en/ds/MAX6622.pdf
* Maxim MAX6636
Prefix: 'max6636'
Datasheet: http://datasheets.maximintegrated.com/en/ds/MAX6636.pdf
* Maxim MAX6689
Prefix: 'max6689'
Datasheet: http://datasheets.maximintegrated.com/en/ds/MAX6689.pdf
* Maxim MAX6693
Prefix: 'max6693'
Datasheet: http://datasheets.maximintegrated.com/en/ds/MAX6693.pdf
* Maxim MAX6694
Prefix: 'max6694'
Datasheet: http://datasheets.maximintegrated.com/en/ds/MAX6694.pdf
* Maxim MAX6697
Prefix: 'max6697'
Datasheet: http://datasheets.maximintegrated.com/en/ds/MAX6697.pdf
* Maxim MAX6698
Prefix: 'max6698'
Datasheet: http://datasheets.maximintegrated.com/en/ds/MAX6698.pdf
* Maxim MAX6699
Prefix: 'max6699'
Datasheet: http://datasheets.maximintegrated.com/en/ds/MAX6699.pdf
Author:
Guenter Roeck <linux@roeck-us.net>
Description
-----------
This driver implements support for several MAX6697 compatible temperature sensor
chips. The chips support one local temperature sensor plus four, six, or seven
remote temperature sensors. Remote temperature sensors are diode-connected
thermal transitors, except for MAX6698 which supports three diode-connected
thermal transistors plus three thermistors in addition to the local temperature
sensor.
The driver provides the following sysfs attributes. temp1 is the local (chip)
temperature, temp[2..n] are remote temperatures. The actually supported
per-channel attributes are chip type and channel dependent.
tempX_input RO temperature
tempX_max RW temperature maximum threshold
tempX_max_alarm RO temperature maximum threshold alarm
tempX_crit RW temperature critical threshold
tempX_crit_alarm RO temperature critical threshold alarm
tempX_fault RO temperature diode fault (remote sensors only)

View file

@ -854,6 +854,17 @@ config SENSORS_MAX6650
This driver can also be built as a module. If so, the module
will be called max6650.
config SENSORS_MAX6697
tristate "Maxim MAX6697 and compatibles"
depends on I2C
help
If you say yes here you get support for MAX6581, MAX6602, MAX6622,
MAX6636, MAX6689, MAX6693, MAX6694, MAX6697, MAX6698, and MAX6699
temperature sensor chips.
This driver can also be built as a module. If so, the module
will be called max6697.
config SENSORS_MCP3021
tristate "Microchip MCP3021 and compatibles"
depends on I2C

View file

@ -99,6 +99,7 @@ obj-$(CONFIG_SENSORS_MAX197) += max197.o
obj-$(CONFIG_SENSORS_MAX6639) += max6639.o
obj-$(CONFIG_SENSORS_MAX6642) += max6642.o
obj-$(CONFIG_SENSORS_MAX6650) += max6650.o
obj-$(CONFIG_SENSORS_MAX6697) += max6697.o
obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o
obj-$(CONFIG_SENSORS_NTC_THERMISTOR) += ntc_thermistor.o

726
drivers/hwmon/max6697.c Normal file
View file

@ -0,0 +1,726 @@
/*
* Copyright (c) 2012 Guenter Roeck <linux@roeck-us.net>
*
* based on max1668.c
* Copyright (c) 2011 David George <david.george@ska.ac.za>
*
* 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.
*/
#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/of.h>
#include <linux/platform_data/max6697.h>
enum chips { max6581, max6602, max6622, max6636, max6689, max6693, max6694,
max6697, max6698, max6699 };
/* Report local sensor as temp1 */
static const u8 MAX6697_REG_TEMP[] = {
0x07, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x08 };
static const u8 MAX6697_REG_TEMP_EXT[] = {
0x57, 0x09, 0x52, 0x53, 0x54, 0x55, 0x56, 0 };
static const u8 MAX6697_REG_MAX[] = {
0x17, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x18 };
static const u8 MAX6697_REG_CRIT[] = {
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27 };
/*
* Map device tree / platform data register bit map to chip bit map.
* Applies to alert register and over-temperature register.
*/
#define MAX6697_MAP_BITS(reg) ((((reg) & 0x7e) >> 1) | \
(((reg) & 0x01) << 6) | ((reg) & 0x80))
#define MAX6697_REG_STAT(n) (0x44 + (n))
#define MAX6697_REG_CONFIG 0x41
#define MAX6581_CONF_EXTENDED (1 << 1)
#define MAX6693_CONF_BETA (1 << 2)
#define MAX6697_CONF_RESISTANCE (1 << 3)
#define MAX6697_CONF_TIMEOUT (1 << 5)
#define MAX6697_REG_ALERT_MASK 0x42
#define MAX6697_REG_OVERT_MASK 0x43
#define MAX6581_REG_RESISTANCE 0x4a
#define MAX6581_REG_IDEALITY 0x4b
#define MAX6581_REG_IDEALITY_SELECT 0x4c
#define MAX6581_REG_OFFSET 0x4d
#define MAX6581_REG_OFFSET_SELECT 0x4e
#define MAX6697_CONV_TIME 156 /* ms per channel, worst case */
struct max6697_chip_data {
int channels;
u32 have_ext;
u32 have_crit;
u32 have_fault;
u8 valid_conf;
const u8 *alarm_map;
};
struct max6697_data {
struct device *hwmon_dev;
enum chips type;
const struct max6697_chip_data *chip;
int update_interval; /* in milli-seconds */
int temp_offset; /* in degrees C */
struct mutex update_lock;
unsigned long last_updated; /* In jiffies */
bool valid; /* true if following fields are valid */
/* 1x local and up to 7x remote */
u8 temp[8][4]; /* [nr][0]=temp [1]=ext [2]=max [3]=crit */
#define MAX6697_TEMP_INPUT 0
#define MAX6697_TEMP_EXT 1
#define MAX6697_TEMP_MAX 2
#define MAX6697_TEMP_CRIT 3
u32 alarms;
};
/* Diode fault status bits on MAX6581 are right shifted by one bit */
static const u8 max6581_alarm_map[] = {
0, 0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23 };
static const struct max6697_chip_data max6697_chip_data[] = {
[max6581] = {
.channels = 8,
.have_crit = 0xff,
.have_ext = 0x7f,
.have_fault = 0xfe,
.valid_conf = MAX6581_CONF_EXTENDED | MAX6697_CONF_TIMEOUT,
.alarm_map = max6581_alarm_map,
},
[max6602] = {
.channels = 5,
.have_crit = 0x12,
.have_ext = 0x02,
.have_fault = 0x1e,
.valid_conf = MAX6697_CONF_RESISTANCE | MAX6697_CONF_TIMEOUT,
},
[max6622] = {
.channels = 5,
.have_crit = 0x12,
.have_ext = 0x02,
.have_fault = 0x1e,
.valid_conf = MAX6697_CONF_RESISTANCE | MAX6697_CONF_TIMEOUT,
},
[max6636] = {
.channels = 7,
.have_crit = 0x72,
.have_ext = 0x02,
.have_fault = 0x7e,
.valid_conf = MAX6697_CONF_RESISTANCE | MAX6697_CONF_TIMEOUT,
},
[max6689] = {
.channels = 7,
.have_crit = 0x72,
.have_ext = 0x02,
.have_fault = 0x7e,
.valid_conf = MAX6697_CONF_RESISTANCE | MAX6697_CONF_TIMEOUT,
},
[max6693] = {
.channels = 7,
.have_crit = 0x72,
.have_ext = 0x02,
.have_fault = 0x7e,
.valid_conf = MAX6697_CONF_RESISTANCE | MAX6693_CONF_BETA |
MAX6697_CONF_TIMEOUT,
},
[max6694] = {
.channels = 5,
.have_crit = 0x12,
.have_ext = 0x02,
.have_fault = 0x1e,
.valid_conf = MAX6697_CONF_RESISTANCE | MAX6693_CONF_BETA |
MAX6697_CONF_TIMEOUT,
},
[max6697] = {
.channels = 7,
.have_crit = 0x72,
.have_ext = 0x02,
.have_fault = 0x7e,
.valid_conf = MAX6697_CONF_RESISTANCE | MAX6697_CONF_TIMEOUT,
},
[max6698] = {
.channels = 7,
.have_crit = 0x72,
.have_ext = 0x02,
.have_fault = 0x0e,
.valid_conf = MAX6697_CONF_RESISTANCE | MAX6697_CONF_TIMEOUT,
},
[max6699] = {
.channels = 5,
.have_crit = 0x12,
.have_ext = 0x02,
.have_fault = 0x1e,
.valid_conf = MAX6697_CONF_RESISTANCE | MAX6697_CONF_TIMEOUT,
},
};
static struct max6697_data *max6697_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct max6697_data *data = i2c_get_clientdata(client);
struct max6697_data *ret = data;
int val;
int i;
u32 alarms;
mutex_lock(&data->update_lock);
if (data->valid &&
!time_after(jiffies, data->last_updated
+ msecs_to_jiffies(data->update_interval)))
goto abort;
for (i = 0; i < data->chip->channels; i++) {
if (data->chip->have_ext & (1 << i)) {
val = i2c_smbus_read_byte_data(client,
MAX6697_REG_TEMP_EXT[i]);
if (unlikely(val < 0)) {
ret = ERR_PTR(val);
goto abort;
}
data->temp[i][MAX6697_TEMP_EXT] = val;
}
val = i2c_smbus_read_byte_data(client, MAX6697_REG_TEMP[i]);
if (unlikely(val < 0)) {
ret = ERR_PTR(val);
goto abort;
}
data->temp[i][MAX6697_TEMP_INPUT] = val;
val = i2c_smbus_read_byte_data(client, MAX6697_REG_MAX[i]);
if (unlikely(val < 0)) {
ret = ERR_PTR(val);
goto abort;
}
data->temp[i][MAX6697_TEMP_MAX] = val;
if (data->chip->have_crit & (1 << i)) {
val = i2c_smbus_read_byte_data(client,
MAX6697_REG_CRIT[i]);
if (unlikely(val < 0)) {
ret = ERR_PTR(val);
goto abort;
}
data->temp[i][MAX6697_TEMP_CRIT] = val;
}
}
alarms = 0;
for (i = 0; i < 3; i++) {
val = i2c_smbus_read_byte_data(client, MAX6697_REG_STAT(i));
if (unlikely(val < 0)) {
ret = ERR_PTR(val);
goto abort;
}
alarms = (alarms << 8) | val;
}
data->alarms = alarms;
data->last_updated = jiffies;
data->valid = true;
abort:
mutex_unlock(&data->update_lock);
return ret;
}
static ssize_t show_temp_input(struct device *dev,
struct device_attribute *devattr, char *buf)
{
int index = to_sensor_dev_attr(devattr)->index;
struct max6697_data *data = max6697_update_device(dev);
int temp;
if (IS_ERR(data))
return PTR_ERR(data);
temp = (data->temp[index][MAX6697_TEMP_INPUT] - data->temp_offset) << 3;
temp |= data->temp[index][MAX6697_TEMP_EXT] >> 5;
return sprintf(buf, "%d\n", temp * 125);
}
static ssize_t show_temp(struct device *dev,
struct device_attribute *devattr, char *buf)
{
int nr = to_sensor_dev_attr_2(devattr)->nr;
int index = to_sensor_dev_attr_2(devattr)->index;
struct max6697_data *data = max6697_update_device(dev);
int temp;
if (IS_ERR(data))
return PTR_ERR(data);
temp = data->temp[nr][index];
temp -= data->temp_offset;
return sprintf(buf, "%d\n", temp * 1000);
}
static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,
char *buf)
{
int index = to_sensor_dev_attr(attr)->index;
struct max6697_data *data = max6697_update_device(dev);
if (IS_ERR(data))
return PTR_ERR(data);
if (data->chip->alarm_map)
index = data->chip->alarm_map[index];
return sprintf(buf, "%u\n", (data->alarms >> index) & 0x1);
}
static ssize_t set_temp(struct device *dev,
struct device_attribute *devattr,
const char *buf, size_t count)
{
int nr = to_sensor_dev_attr_2(devattr)->nr;
int index = to_sensor_dev_attr_2(devattr)->index;
struct i2c_client *client = to_i2c_client(dev);
struct max6697_data *data = i2c_get_clientdata(client);
long temp;
int ret;
ret = kstrtol(buf, 10, &temp);
if (ret < 0)
return ret;
mutex_lock(&data->update_lock);
temp = DIV_ROUND_CLOSEST(temp, 1000) + data->temp_offset;
temp = clamp_val(temp, 0, data->type == max6581 ? 255 : 127);
data->temp[nr][index] = temp;
ret = i2c_smbus_write_byte_data(client,
index == 2 ? MAX6697_REG_MAX[nr]
: MAX6697_REG_CRIT[nr],
temp);
mutex_unlock(&data->update_lock);
return ret < 0 ? ret : count;
}
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input, NULL, 0);
static SENSOR_DEVICE_ATTR_2(temp1_max, S_IRUGO | S_IWUSR, show_temp, set_temp,
0, MAX6697_TEMP_MAX);
static SENSOR_DEVICE_ATTR_2(temp1_crit, S_IRUGO | S_IWUSR, show_temp, set_temp,
0, MAX6697_TEMP_CRIT);
static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp_input, NULL, 1);
static SENSOR_DEVICE_ATTR_2(temp2_max, S_IRUGO | S_IWUSR, show_temp, set_temp,
1, MAX6697_TEMP_MAX);
static SENSOR_DEVICE_ATTR_2(temp2_crit, S_IRUGO | S_IWUSR, show_temp, set_temp,
1, MAX6697_TEMP_CRIT);
static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp_input, NULL, 2);
static SENSOR_DEVICE_ATTR_2(temp3_max, S_IRUGO | S_IWUSR, show_temp, set_temp,
2, MAX6697_TEMP_MAX);
static SENSOR_DEVICE_ATTR_2(temp3_crit, S_IRUGO | S_IWUSR, show_temp, set_temp,
2, MAX6697_TEMP_CRIT);
static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp_input, NULL, 3);
static SENSOR_DEVICE_ATTR_2(temp4_max, S_IRUGO | S_IWUSR, show_temp, set_temp,
3, MAX6697_TEMP_MAX);
static SENSOR_DEVICE_ATTR_2(temp4_crit, S_IRUGO | S_IWUSR, show_temp, set_temp,
3, MAX6697_TEMP_CRIT);
static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO, show_temp_input, NULL, 4);
static SENSOR_DEVICE_ATTR_2(temp5_max, S_IRUGO | S_IWUSR, show_temp, set_temp,
4, MAX6697_TEMP_MAX);
static SENSOR_DEVICE_ATTR_2(temp5_crit, S_IRUGO | S_IWUSR, show_temp, set_temp,
4, MAX6697_TEMP_CRIT);
static SENSOR_DEVICE_ATTR(temp6_input, S_IRUGO, show_temp_input, NULL, 5);
static SENSOR_DEVICE_ATTR_2(temp6_max, S_IRUGO | S_IWUSR, show_temp, set_temp,
5, MAX6697_TEMP_MAX);
static SENSOR_DEVICE_ATTR_2(temp6_crit, S_IRUGO | S_IWUSR, show_temp, set_temp,
5, MAX6697_TEMP_CRIT);
static SENSOR_DEVICE_ATTR(temp7_input, S_IRUGO, show_temp_input, NULL, 6);
static SENSOR_DEVICE_ATTR_2(temp7_max, S_IRUGO | S_IWUSR, show_temp, set_temp,
6, MAX6697_TEMP_MAX);
static SENSOR_DEVICE_ATTR_2(temp7_crit, S_IRUGO | S_IWUSR, show_temp, set_temp,
6, MAX6697_TEMP_CRIT);
static SENSOR_DEVICE_ATTR(temp8_input, S_IRUGO, show_temp_input, NULL, 7);
static SENSOR_DEVICE_ATTR_2(temp8_max, S_IRUGO | S_IWUSR, show_temp, set_temp,
7, MAX6697_TEMP_MAX);
static SENSOR_DEVICE_ATTR_2(temp8_crit, S_IRUGO | S_IWUSR, show_temp, set_temp,
7, MAX6697_TEMP_CRIT);
static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 22);
static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_alarm, NULL, 16);
static SENSOR_DEVICE_ATTR(temp3_max_alarm, S_IRUGO, show_alarm, NULL, 17);
static SENSOR_DEVICE_ATTR(temp4_max_alarm, S_IRUGO, show_alarm, NULL, 18);
static SENSOR_DEVICE_ATTR(temp5_max_alarm, S_IRUGO, show_alarm, NULL, 19);
static SENSOR_DEVICE_ATTR(temp6_max_alarm, S_IRUGO, show_alarm, NULL, 20);
static SENSOR_DEVICE_ATTR(temp7_max_alarm, S_IRUGO, show_alarm, NULL, 21);
static SENSOR_DEVICE_ATTR(temp8_max_alarm, S_IRUGO, show_alarm, NULL, 23);
static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, 14);
static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, show_alarm, NULL, 8);
static SENSOR_DEVICE_ATTR(temp3_crit_alarm, S_IRUGO, show_alarm, NULL, 9);
static SENSOR_DEVICE_ATTR(temp4_crit_alarm, S_IRUGO, show_alarm, NULL, 10);
static SENSOR_DEVICE_ATTR(temp5_crit_alarm, S_IRUGO, show_alarm, NULL, 11);
static SENSOR_DEVICE_ATTR(temp6_crit_alarm, S_IRUGO, show_alarm, NULL, 12);
static SENSOR_DEVICE_ATTR(temp7_crit_alarm, S_IRUGO, show_alarm, NULL, 13);
static SENSOR_DEVICE_ATTR(temp8_crit_alarm, S_IRUGO, show_alarm, NULL, 15);
static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_alarm, NULL, 1);
static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_alarm, NULL, 2);
static SENSOR_DEVICE_ATTR(temp4_fault, S_IRUGO, show_alarm, NULL, 3);
static SENSOR_DEVICE_ATTR(temp5_fault, S_IRUGO, show_alarm, NULL, 4);
static SENSOR_DEVICE_ATTR(temp6_fault, S_IRUGO, show_alarm, NULL, 5);
static SENSOR_DEVICE_ATTR(temp7_fault, S_IRUGO, show_alarm, NULL, 6);
static SENSOR_DEVICE_ATTR(temp8_fault, S_IRUGO, show_alarm, NULL, 7);
static struct attribute *max6697_attributes[8][7] = {
{
&sensor_dev_attr_temp1_input.dev_attr.attr,
&sensor_dev_attr_temp1_max.dev_attr.attr,
&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp1_crit.dev_attr.attr,
&sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
NULL
}, {
&sensor_dev_attr_temp2_input.dev_attr.attr,
&sensor_dev_attr_temp2_max.dev_attr.attr,
&sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp2_crit.dev_attr.attr,
&sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
&sensor_dev_attr_temp2_fault.dev_attr.attr,
NULL
}, {
&sensor_dev_attr_temp3_input.dev_attr.attr,
&sensor_dev_attr_temp3_max.dev_attr.attr,
&sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp3_crit.dev_attr.attr,
&sensor_dev_attr_temp3_crit_alarm.dev_attr.attr,
&sensor_dev_attr_temp3_fault.dev_attr.attr,
NULL
}, {
&sensor_dev_attr_temp4_input.dev_attr.attr,
&sensor_dev_attr_temp4_max.dev_attr.attr,
&sensor_dev_attr_temp4_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp4_crit.dev_attr.attr,
&sensor_dev_attr_temp4_crit_alarm.dev_attr.attr,
&sensor_dev_attr_temp4_fault.dev_attr.attr,
NULL
}, {
&sensor_dev_attr_temp5_input.dev_attr.attr,
&sensor_dev_attr_temp5_max.dev_attr.attr,
&sensor_dev_attr_temp5_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp5_crit.dev_attr.attr,
&sensor_dev_attr_temp5_crit_alarm.dev_attr.attr,
&sensor_dev_attr_temp5_fault.dev_attr.attr,
NULL
}, {
&sensor_dev_attr_temp6_input.dev_attr.attr,
&sensor_dev_attr_temp6_max.dev_attr.attr,
&sensor_dev_attr_temp6_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp6_crit.dev_attr.attr,
&sensor_dev_attr_temp6_crit_alarm.dev_attr.attr,
&sensor_dev_attr_temp6_fault.dev_attr.attr,
NULL
}, {
&sensor_dev_attr_temp7_input.dev_attr.attr,
&sensor_dev_attr_temp7_max.dev_attr.attr,
&sensor_dev_attr_temp7_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp7_crit.dev_attr.attr,
&sensor_dev_attr_temp7_crit_alarm.dev_attr.attr,
&sensor_dev_attr_temp7_fault.dev_attr.attr,
NULL
}, {
&sensor_dev_attr_temp8_input.dev_attr.attr,
&sensor_dev_attr_temp8_max.dev_attr.attr,
&sensor_dev_attr_temp8_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp8_crit.dev_attr.attr,
&sensor_dev_attr_temp8_crit_alarm.dev_attr.attr,
&sensor_dev_attr_temp8_fault.dev_attr.attr,
NULL
}
};
static const struct attribute_group max6697_group[8] = {
{ .attrs = max6697_attributes[0] },
{ .attrs = max6697_attributes[1] },
{ .attrs = max6697_attributes[2] },
{ .attrs = max6697_attributes[3] },
{ .attrs = max6697_attributes[4] },
{ .attrs = max6697_attributes[5] },
{ .attrs = max6697_attributes[6] },
{ .attrs = max6697_attributes[7] },
};
static void max6697_get_config_of(struct device_node *node,
struct max6697_platform_data *pdata)
{
int len;
const __be32 *prop;
prop = of_get_property(node, "smbus-timeout-disable", &len);
if (prop)
pdata->smbus_timeout_disable = true;
prop = of_get_property(node, "extended-range-enable", &len);
if (prop)
pdata->extended_range_enable = true;
prop = of_get_property(node, "beta-compensation-enable", &len);
if (prop)
pdata->beta_compensation = true;
prop = of_get_property(node, "alert-mask", &len);
if (prop && len == sizeof(u32))
pdata->alert_mask = be32_to_cpu(prop[0]);
prop = of_get_property(node, "over-temperature-mask", &len);
if (prop && len == sizeof(u32))
pdata->over_temperature_mask = be32_to_cpu(prop[0]);
prop = of_get_property(node, "resistance-cancellation", &len);
if (prop) {
if (len == sizeof(u32))
pdata->resistance_cancellation = be32_to_cpu(prop[0]);
else
pdata->resistance_cancellation = 0xfe;
}
prop = of_get_property(node, "transistor-ideality", &len);
if (prop && len == 2 * sizeof(u32)) {
pdata->ideality_mask = be32_to_cpu(prop[0]);
pdata->ideality_value = be32_to_cpu(prop[1]);
}
}
static int max6697_init_chip(struct i2c_client *client)
{
struct max6697_data *data = i2c_get_clientdata(client);
struct max6697_platform_data *pdata = dev_get_platdata(&client->dev);
struct max6697_platform_data p;
const struct max6697_chip_data *chip = data->chip;
int factor = chip->channels;
int ret, reg;
/*
* Don't touch configuration if neither platform data nor OF
* configuration was specified. If that is the case, use the
* current chip configuration.
*/
if (!pdata && !client->dev.of_node) {
reg = i2c_smbus_read_byte_data(client, MAX6697_REG_CONFIG);
if (reg < 0)
return reg;
if (data->type == max6581) {
if (reg & MAX6581_CONF_EXTENDED)
data->temp_offset = 64;
reg = i2c_smbus_read_byte_data(client,
MAX6581_REG_RESISTANCE);
if (reg < 0)
return reg;
factor += hweight8(reg);
} else {
if (reg & MAX6697_CONF_RESISTANCE)
factor++;
}
goto done;
}
if (client->dev.of_node) {
memset(&p, 0, sizeof(p));
max6697_get_config_of(client->dev.of_node, &p);
pdata = &p;
}
reg = 0;
if (pdata->smbus_timeout_disable &&
(chip->valid_conf & MAX6697_CONF_TIMEOUT)) {
reg |= MAX6697_CONF_TIMEOUT;
}
if (pdata->extended_range_enable &&
(chip->valid_conf & MAX6581_CONF_EXTENDED)) {
reg |= MAX6581_CONF_EXTENDED;
data->temp_offset = 64;
}
if (pdata->resistance_cancellation &&
(chip->valid_conf & MAX6697_CONF_RESISTANCE)) {
reg |= MAX6697_CONF_RESISTANCE;
factor++;
}
if (pdata->beta_compensation &&
(chip->valid_conf & MAX6693_CONF_BETA)) {
reg |= MAX6693_CONF_BETA;
}
ret = i2c_smbus_write_byte_data(client, MAX6697_REG_CONFIG, reg);
if (ret < 0)
return ret;
ret = i2c_smbus_write_byte_data(client, MAX6697_REG_ALERT_MASK,
MAX6697_MAP_BITS(pdata->alert_mask));
if (ret < 0)
return ret;
ret = i2c_smbus_write_byte_data(client, MAX6697_REG_OVERT_MASK,
MAX6697_MAP_BITS(pdata->over_temperature_mask));
if (ret < 0)
return ret;
if (data->type == max6581) {
factor += hweight8(pdata->resistance_cancellation >> 1);
ret = i2c_smbus_write_byte_data(client, MAX6581_REG_RESISTANCE,
pdata->resistance_cancellation >> 1);
if (ret < 0)
return ret;
ret = i2c_smbus_write_byte_data(client, MAX6581_REG_IDEALITY,
pdata->ideality_mask >> 1);
if (ret < 0)
return ret;
ret = i2c_smbus_write_byte_data(client,
MAX6581_REG_IDEALITY_SELECT,
pdata->ideality_value);
if (ret < 0)
return ret;
}
done:
data->update_interval = factor * MAX6697_CONV_TIME;
return 0;
}
static void max6697_remove_files(struct i2c_client *client)
{
int i;
for (i = 0; i < ARRAY_SIZE(max6697_group); i++)
sysfs_remove_group(&client->dev.kobj, &max6697_group[i]);
}
static int max6697_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct i2c_adapter *adapter = client->adapter;
struct device *dev = &client->dev;
struct max6697_data *data;
int i, err;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
data = devm_kzalloc(dev, sizeof(struct max6697_data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->type = id->driver_data;
data->chip = &max6697_chip_data[data->type];
i2c_set_clientdata(client, data);
mutex_init(&data->update_lock);
err = max6697_init_chip(client);
if (err)
return err;
for (i = 0; i < data->chip->channels; i++) {
err = sysfs_create_file(&dev->kobj,
max6697_attributes[i][0]);
if (err)
goto error;
err = sysfs_create_file(&dev->kobj,
max6697_attributes[i][1]);
if (err)
goto error;
err = sysfs_create_file(&dev->kobj,
max6697_attributes[i][2]);
if (err)
goto error;
if (data->chip->have_crit & (1 << i)) {
err = sysfs_create_file(&dev->kobj,
max6697_attributes[i][3]);
if (err)
goto error;
err = sysfs_create_file(&dev->kobj,
max6697_attributes[i][4]);
if (err)
goto error;
}
if (data->chip->have_fault & (1 << i)) {
err = sysfs_create_file(&dev->kobj,
max6697_attributes[i][5]);
if (err)
goto error;
}
}
data->hwmon_dev = hwmon_device_register(dev);
if (IS_ERR(data->hwmon_dev)) {
err = PTR_ERR(data->hwmon_dev);
goto error;
}
return 0;
error:
max6697_remove_files(client);
return err;
}
static int max6697_remove(struct i2c_client *client)
{
struct max6697_data *data = i2c_get_clientdata(client);
hwmon_device_unregister(data->hwmon_dev);
max6697_remove_files(client);
return 0;
}
static const struct i2c_device_id max6697_id[] = {
{ "max6581", max6581 },
{ "max6602", max6602 },
{ "max6622", max6622 },
{ "max6636", max6636 },
{ "max6689", max6689 },
{ "max6693", max6693 },
{ "max6694", max6694 },
{ "max6697", max6697 },
{ "max6698", max6698 },
{ "max6699", max6699 },
{ }
};
MODULE_DEVICE_TABLE(i2c, max6697_id);
static struct i2c_driver max6697_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = "max6697",
},
.probe = max6697_probe,
.remove = max6697_remove,
.id_table = max6697_id,
};
module_i2c_driver(max6697_driver);
MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
MODULE_DESCRIPTION("MAX6697 temperature sensor driver");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,36 @@
/*
* max6697.h
* Copyright (c) 2012 Guenter Roeck <linux@roeck-us.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef MAX6697_H
#define MAX6697_H
#include <linux/types.h>
/*
* For all bit masks:
* bit 0: local temperature
* bit 1..7: remote temperatures
*/
struct max6697_platform_data {
bool smbus_timeout_disable; /* set to disable SMBus timeouts */
bool extended_range_enable; /* set to enable extended temp range */
bool beta_compensation; /* set to enable beta compensation */
u8 alert_mask; /* set bit to 1 to disable alert */
u8 over_temperature_mask; /* set bit to 1 to disable */
u8 resistance_cancellation; /* set bit to 0 to disable
* bit mask for MAX6581,
* boolean for other chips
*/
u8 ideality_mask; /* set bit to 0 to disable */
u8 ideality_value; /* transistor ideality as per
* MAX6581 datasheet
*/
};
#endif /* MAX6697_H */