You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
Merge branch 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jdelvare/staging
* 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jdelvare/staging: (23 commits) hwmon: (lm75) Add support for the Texas Instruments TMP105 hwmon: (ltc4245) Read only one GPIO pin hwmon: (dme1737) Add SCH5127 support hwmon: (tmp102) Don't always stop chip at exit hwmon: (tmp102) Fix suspend and resume functions hwmon: (tmp102) Various fixes hwmon: Driver for TI TMP102 temperature sensor hwmon: EMC1403 thermal sensor support hwmon: (applesmc) Add temperature sensor labels to sysfs interface hwmon: (applesmc) Add generic support for MacBook Pro 7 hwmon: (applesmc) Add generic support for MacBook Pro 6 hwmon: (applesmc) Add support for MacBook Pro 5,3 and 5,4 hwmon: (tmp401) Reorganize code to get rid of static forward declarations hwmon: (tmp401) Use constants for sysfs file permissions hwmon: (adm1031) Allow setting update rate hwmon: Add description of the update_rate sysfs attribute hwmon: (lm90) Use programmed update rate hwmon: (f71882fg) Acquire I/O regions while we're working with them hwmon: (f71882fg) Code cleanup hwmon: (f71882fg) Use strict_stro(l|ul) instead of simple_strto$1 ...
This commit is contained in:
@@ -1206,6 +1206,15 @@ int acpi_check_mem_region(resource_size_t start, resource_size_t n,
|
||||
}
|
||||
EXPORT_SYMBOL(acpi_check_mem_region);
|
||||
|
||||
/*
|
||||
* Let drivers know whether the resource checks are effective
|
||||
*/
|
||||
int acpi_resources_are_enforced(void)
|
||||
{
|
||||
return acpi_enforce_resources == ENFORCE_RESOURCES_STRICT;
|
||||
}
|
||||
EXPORT_SYMBOL(acpi_resources_are_enforced);
|
||||
|
||||
/*
|
||||
* Acquire a spinlock.
|
||||
*
|
||||
|
||||
+28
-6
@@ -447,13 +447,14 @@ config SENSORS_IT87
|
||||
will be called it87.
|
||||
|
||||
config SENSORS_LM63
|
||||
tristate "National Semiconductor LM63"
|
||||
tristate "National Semiconductor LM63 and LM64"
|
||||
depends on I2C
|
||||
help
|
||||
If you say yes here you get support for the National Semiconductor
|
||||
LM63 remote diode digital temperature sensor with integrated fan
|
||||
control. Such chips are found on the Tyan S4882 (Thunder K8QS Pro)
|
||||
motherboard, among others.
|
||||
If you say yes here you get support for the National
|
||||
Semiconductor LM63 and LM64 remote diode digital temperature
|
||||
sensors with integrated fan control. Such chips are found
|
||||
on the Tyan S4882 (Thunder K8QS Pro) motherboard, among
|
||||
others.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called lm63.
|
||||
@@ -492,7 +493,8 @@ config SENSORS_LM75
|
||||
- NXP's LM75A
|
||||
- ST Microelectronics STDS75
|
||||
- TelCom (now Microchip) TCN75
|
||||
- Texas Instruments TMP100, TMP101, TMP75, TMP175, TMP275
|
||||
- Texas Instruments TMP100, TMP101, TMP105, TMP75, TMP175,
|
||||
TMP275
|
||||
|
||||
This driver supports driver model based binding through board
|
||||
specific I2C device tables.
|
||||
@@ -749,6 +751,16 @@ config SENSORS_DME1737
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called dme1737.
|
||||
|
||||
config SENSORS_EMC1403
|
||||
tristate "SMSC EMC1403 thermal sensor"
|
||||
depends on I2C
|
||||
help
|
||||
If you say yes here you get support for the SMSC EMC1403
|
||||
temperature monitoring chip.
|
||||
|
||||
Threshold values can be configured using sysfs.
|
||||
Data from the different diodes are accessible via sysfs.
|
||||
|
||||
config SENSORS_SMSC47M1
|
||||
tristate "SMSC LPC47M10x and compatibles"
|
||||
help
|
||||
@@ -831,6 +843,16 @@ config SENSORS_THMC50
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called thmc50.
|
||||
|
||||
config SENSORS_TMP102
|
||||
tristate "Texas Instruments TMP102"
|
||||
depends on I2C && EXPERIMENTAL
|
||||
help
|
||||
If you say yes here you get support for Texas Instruments TMP102
|
||||
sensor chips.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called tmp102.
|
||||
|
||||
config SENSORS_TMP401
|
||||
tristate "Texas Instruments TMP401 and compatibles"
|
||||
depends on I2C && EXPERIMENTAL
|
||||
|
||||
@@ -41,6 +41,7 @@ obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o
|
||||
obj-$(CONFIG_SENSORS_CORETEMP) += coretemp.o
|
||||
obj-$(CONFIG_SENSORS_DME1737) += dme1737.o
|
||||
obj-$(CONFIG_SENSORS_DS1621) += ds1621.o
|
||||
obj-$(CONFIG_SENSORS_EMC1403) += emc1403.o
|
||||
obj-$(CONFIG_SENSORS_F71805F) += f71805f.o
|
||||
obj-$(CONFIG_SENSORS_F71882FG) += f71882fg.o
|
||||
obj-$(CONFIG_SENSORS_F75375S) += f75375s.o
|
||||
@@ -90,6 +91,7 @@ obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o
|
||||
obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o
|
||||
obj-$(CONFIG_SENSORS_AMC6821) += amc6821.o
|
||||
obj-$(CONFIG_SENSORS_THMC50) += thmc50.o
|
||||
obj-$(CONFIG_SENSORS_TMP102) += tmp102.o
|
||||
obj-$(CONFIG_SENSORS_TMP401) += tmp401.o
|
||||
obj-$(CONFIG_SENSORS_TMP421) += tmp421.o
|
||||
obj-$(CONFIG_SENSORS_VIA_CPUTEMP)+= via-cputemp.o
|
||||
|
||||
+66
-2
@@ -36,6 +36,7 @@
|
||||
#define ADM1031_REG_FAN_DIV(nr) (0x20 + (nr))
|
||||
#define ADM1031_REG_PWM (0x22)
|
||||
#define ADM1031_REG_FAN_MIN(nr) (0x10 + (nr))
|
||||
#define ADM1031_REG_FAN_FILTER (0x23)
|
||||
|
||||
#define ADM1031_REG_TEMP_OFFSET(nr) (0x0d + (nr))
|
||||
#define ADM1031_REG_TEMP_MAX(nr) (0x14 + 4 * (nr))
|
||||
@@ -61,6 +62,9 @@
|
||||
#define ADM1031_CONF2_TACH2_ENABLE 0x08
|
||||
#define ADM1031_CONF2_TEMP_ENABLE(chan) (0x10 << (chan))
|
||||
|
||||
#define ADM1031_UPDATE_RATE_MASK 0x1c
|
||||
#define ADM1031_UPDATE_RATE_SHIFT 2
|
||||
|
||||
/* Addresses to scan */
|
||||
static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
|
||||
|
||||
@@ -75,6 +79,7 @@ struct adm1031_data {
|
||||
int chip_type;
|
||||
char valid; /* !=0 if following fields are valid */
|
||||
unsigned long last_updated; /* In jiffies */
|
||||
unsigned int update_rate; /* In milliseconds */
|
||||
/* The chan_select_table contains the possible configurations for
|
||||
* auto fan control.
|
||||
*/
|
||||
@@ -738,6 +743,57 @@ static SENSOR_DEVICE_ATTR(temp3_crit_alarm, S_IRUGO, show_alarm, NULL, 12);
|
||||
static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_alarm, NULL, 13);
|
||||
static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, 14);
|
||||
|
||||
/* Update Rate */
|
||||
static const unsigned int update_rates[] = {
|
||||
16000, 8000, 4000, 2000, 1000, 500, 250, 125,
|
||||
};
|
||||
|
||||
static ssize_t show_update_rate(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct adm1031_data *data = i2c_get_clientdata(client);
|
||||
|
||||
return sprintf(buf, "%u\n", data->update_rate);
|
||||
}
|
||||
|
||||
static ssize_t set_update_rate(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct adm1031_data *data = i2c_get_clientdata(client);
|
||||
unsigned long val;
|
||||
int i, err;
|
||||
u8 reg;
|
||||
|
||||
err = strict_strtoul(buf, 10, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* find the nearest update rate from the table */
|
||||
for (i = 0; i < ARRAY_SIZE(update_rates) - 1; i++) {
|
||||
if (val >= update_rates[i])
|
||||
break;
|
||||
}
|
||||
/* if not found, we point to the last entry (lowest update rate) */
|
||||
|
||||
/* set the new update rate while preserving other settings */
|
||||
reg = adm1031_read_value(client, ADM1031_REG_FAN_FILTER);
|
||||
reg &= ~ADM1031_UPDATE_RATE_MASK;
|
||||
reg |= i << ADM1031_UPDATE_RATE_SHIFT;
|
||||
adm1031_write_value(client, ADM1031_REG_FAN_FILTER, reg);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->update_rate = update_rates[i];
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(update_rate, S_IRUGO | S_IWUSR, show_update_rate,
|
||||
set_update_rate);
|
||||
|
||||
static struct attribute *adm1031_attributes[] = {
|
||||
&sensor_dev_attr_fan1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_fan1_div.dev_attr.attr,
|
||||
@@ -774,6 +830,7 @@ static struct attribute *adm1031_attributes[] = {
|
||||
|
||||
&sensor_dev_attr_auto_fan1_min_pwm.dev_attr.attr,
|
||||
|
||||
&dev_attr_update_rate.attr,
|
||||
&dev_attr_alarms.attr,
|
||||
|
||||
NULL
|
||||
@@ -900,6 +957,7 @@ static void adm1031_init_client(struct i2c_client *client)
|
||||
{
|
||||
unsigned int read_val;
|
||||
unsigned int mask;
|
||||
int i;
|
||||
struct adm1031_data *data = i2c_get_clientdata(client);
|
||||
|
||||
mask = (ADM1031_CONF2_PWM1_ENABLE | ADM1031_CONF2_TACH1_ENABLE);
|
||||
@@ -919,18 +977,24 @@ static void adm1031_init_client(struct i2c_client *client)
|
||||
ADM1031_CONF1_MONITOR_ENABLE);
|
||||
}
|
||||
|
||||
/* Read the chip's update rate */
|
||||
mask = ADM1031_UPDATE_RATE_MASK;
|
||||
read_val = adm1031_read_value(client, ADM1031_REG_FAN_FILTER);
|
||||
i = (read_val & mask) >> ADM1031_UPDATE_RATE_SHIFT;
|
||||
data->update_rate = update_rates[i];
|
||||
}
|
||||
|
||||
static struct adm1031_data *adm1031_update_device(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct adm1031_data *data = i2c_get_clientdata(client);
|
||||
unsigned long next_update;
|
||||
int chan;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
|
||||
|| !data->valid) {
|
||||
next_update = data->last_updated + msecs_to_jiffies(data->update_rate);
|
||||
if (time_after(jiffies, next_update) || !data->valid) {
|
||||
|
||||
dev_dbg(&client->dev, "Starting adm1031 update\n");
|
||||
for (chan = 0;
|
||||
|
||||
+185
-1
@@ -148,6 +148,20 @@ static const char *temperature_sensors_sets[][41] = {
|
||||
/* Set 18: MacBook Pro 2,2 */
|
||||
{ "TB0T", "TC0D", "TC0P", "TG0H", "TG0P", "TG0T", "TM0P", "TTF0",
|
||||
"Th0H", "Th1H", "Tm0P", "Ts0P", NULL },
|
||||
/* Set 19: Macbook Pro 5,3 */
|
||||
{ "TB0T", "TB1T", "TB2T", "TB3T", "TC0D", "TC0F", "TC0P", "TG0D",
|
||||
"TG0F", "TG0H", "TG0P", "TG0T", "TN0D", "TN0P", "TTF0", "Th2H",
|
||||
"Tm0P", "Ts0P", "Ts0S", NULL },
|
||||
/* Set 20: MacBook Pro 5,4 */
|
||||
{ "TB0T", "TB1T", "TB2T", "TB3T", "TC0D", "TC0F", "TC0P", "TN0D",
|
||||
"TN0P", "TTF0", "Th2H", "Ts0P", "Ts0S", NULL },
|
||||
/* Set 21: MacBook Pro 6,2 */
|
||||
{ "TB0T", "TB1T", "TB2T", "TC0C", "TC0D", "TC0P", "TC1C", "TG0D",
|
||||
"TG0P", "TG0T", "TMCD", "TP0P", "TPCD", "Th1H", "Th2H", "Tm0P",
|
||||
"Ts0P", "Ts0S", NULL },
|
||||
/* Set 22: MacBook Pro 7,1 */
|
||||
{ "TB0T", "TB1T", "TB2T", "TC0D", "TC0P", "TN0D", "TN0P", "TN0S",
|
||||
"TN1D", "TN1F", "TN1G", "TN1S", "Th1H", "Ts0P", "Ts0S", NULL },
|
||||
};
|
||||
|
||||
/* List of keys used to read/write fan speeds */
|
||||
@@ -646,6 +660,17 @@ out:
|
||||
return snprintf(sysfsbuf, PAGE_SIZE, "(%d,%d)\n", left, right);
|
||||
}
|
||||
|
||||
/* Displays sensor key as label */
|
||||
static ssize_t applesmc_show_sensor_label(struct device *dev,
|
||||
struct device_attribute *devattr, char *sysfsbuf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
const char *key =
|
||||
temperature_sensors_sets[applesmc_temperature_set][attr->index];
|
||||
|
||||
return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", key);
|
||||
}
|
||||
|
||||
/* Displays degree Celsius * 1000 */
|
||||
static ssize_t applesmc_show_temperature(struct device *dev,
|
||||
struct device_attribute *devattr, char *sysfsbuf)
|
||||
@@ -1113,6 +1138,86 @@ static const struct attribute_group fan_attribute_groups[] = {
|
||||
/*
|
||||
* Temperature sensors sysfs entries.
|
||||
*/
|
||||
static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO,
|
||||
applesmc_show_sensor_label, NULL, 0);
|
||||
static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO,
|
||||
applesmc_show_sensor_label, NULL, 1);
|
||||
static SENSOR_DEVICE_ATTR(temp3_label, S_IRUGO,
|
||||
applesmc_show_sensor_label, NULL, 2);
|
||||
static SENSOR_DEVICE_ATTR(temp4_label, S_IRUGO,
|
||||
applesmc_show_sensor_label, NULL, 3);
|
||||
static SENSOR_DEVICE_ATTR(temp5_label, S_IRUGO,
|
||||
applesmc_show_sensor_label, NULL, 4);
|
||||
static SENSOR_DEVICE_ATTR(temp6_label, S_IRUGO,
|
||||
applesmc_show_sensor_label, NULL, 5);
|
||||
static SENSOR_DEVICE_ATTR(temp7_label, S_IRUGO,
|
||||
applesmc_show_sensor_label, NULL, 6);
|
||||
static SENSOR_DEVICE_ATTR(temp8_label, S_IRUGO,
|
||||
applesmc_show_sensor_label, NULL, 7);
|
||||
static SENSOR_DEVICE_ATTR(temp9_label, S_IRUGO,
|
||||
applesmc_show_sensor_label, NULL, 8);
|
||||
static SENSOR_DEVICE_ATTR(temp10_label, S_IRUGO,
|
||||
applesmc_show_sensor_label, NULL, 9);
|
||||
static SENSOR_DEVICE_ATTR(temp11_label, S_IRUGO,
|
||||
applesmc_show_sensor_label, NULL, 10);
|
||||
static SENSOR_DEVICE_ATTR(temp12_label, S_IRUGO,
|
||||
applesmc_show_sensor_label, NULL, 11);
|
||||
static SENSOR_DEVICE_ATTR(temp13_label, S_IRUGO,
|
||||
applesmc_show_sensor_label, NULL, 12);
|
||||
static SENSOR_DEVICE_ATTR(temp14_label, S_IRUGO,
|
||||
applesmc_show_sensor_label, NULL, 13);
|
||||
static SENSOR_DEVICE_ATTR(temp15_label, S_IRUGO,
|
||||
applesmc_show_sensor_label, NULL, 14);
|
||||
static SENSOR_DEVICE_ATTR(temp16_label, S_IRUGO,
|
||||
applesmc_show_sensor_label, NULL, 15);
|
||||
static SENSOR_DEVICE_ATTR(temp17_label, S_IRUGO,
|
||||
applesmc_show_sensor_label, NULL, 16);
|
||||
static SENSOR_DEVICE_ATTR(temp18_label, S_IRUGO,
|
||||
applesmc_show_sensor_label, NULL, 17);
|
||||
static SENSOR_DEVICE_ATTR(temp19_label, S_IRUGO,
|
||||
applesmc_show_sensor_label, NULL, 18);
|
||||
static SENSOR_DEVICE_ATTR(temp20_label, S_IRUGO,
|
||||
applesmc_show_sensor_label, NULL, 19);
|
||||
static SENSOR_DEVICE_ATTR(temp21_label, S_IRUGO,
|
||||
applesmc_show_sensor_label, NULL, 20);
|
||||
static SENSOR_DEVICE_ATTR(temp22_label, S_IRUGO,
|
||||
applesmc_show_sensor_label, NULL, 21);
|
||||
static SENSOR_DEVICE_ATTR(temp23_label, S_IRUGO,
|
||||
applesmc_show_sensor_label, NULL, 22);
|
||||
static SENSOR_DEVICE_ATTR(temp24_label, S_IRUGO,
|
||||
applesmc_show_sensor_label, NULL, 23);
|
||||
static SENSOR_DEVICE_ATTR(temp25_label, S_IRUGO,
|
||||
applesmc_show_sensor_label, NULL, 24);
|
||||
static SENSOR_DEVICE_ATTR(temp26_label, S_IRUGO,
|
||||
applesmc_show_sensor_label, NULL, 25);
|
||||
static SENSOR_DEVICE_ATTR(temp27_label, S_IRUGO,
|
||||
applesmc_show_sensor_label, NULL, 26);
|
||||
static SENSOR_DEVICE_ATTR(temp28_label, S_IRUGO,
|
||||
applesmc_show_sensor_label, NULL, 27);
|
||||
static SENSOR_DEVICE_ATTR(temp29_label, S_IRUGO,
|
||||
applesmc_show_sensor_label, NULL, 28);
|
||||
static SENSOR_DEVICE_ATTR(temp30_label, S_IRUGO,
|
||||
applesmc_show_sensor_label, NULL, 29);
|
||||
static SENSOR_DEVICE_ATTR(temp31_label, S_IRUGO,
|
||||
applesmc_show_sensor_label, NULL, 30);
|
||||
static SENSOR_DEVICE_ATTR(temp32_label, S_IRUGO,
|
||||
applesmc_show_sensor_label, NULL, 31);
|
||||
static SENSOR_DEVICE_ATTR(temp33_label, S_IRUGO,
|
||||
applesmc_show_sensor_label, NULL, 32);
|
||||
static SENSOR_DEVICE_ATTR(temp34_label, S_IRUGO,
|
||||
applesmc_show_sensor_label, NULL, 33);
|
||||
static SENSOR_DEVICE_ATTR(temp35_label, S_IRUGO,
|
||||
applesmc_show_sensor_label, NULL, 34);
|
||||
static SENSOR_DEVICE_ATTR(temp36_label, S_IRUGO,
|
||||
applesmc_show_sensor_label, NULL, 35);
|
||||
static SENSOR_DEVICE_ATTR(temp37_label, S_IRUGO,
|
||||
applesmc_show_sensor_label, NULL, 36);
|
||||
static SENSOR_DEVICE_ATTR(temp38_label, S_IRUGO,
|
||||
applesmc_show_sensor_label, NULL, 37);
|
||||
static SENSOR_DEVICE_ATTR(temp39_label, S_IRUGO,
|
||||
applesmc_show_sensor_label, NULL, 38);
|
||||
static SENSOR_DEVICE_ATTR(temp40_label, S_IRUGO,
|
||||
applesmc_show_sensor_label, NULL, 39);
|
||||
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO,
|
||||
applesmc_show_temperature, NULL, 0);
|
||||
static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO,
|
||||
@@ -1194,6 +1299,50 @@ static SENSOR_DEVICE_ATTR(temp39_input, S_IRUGO,
|
||||
static SENSOR_DEVICE_ATTR(temp40_input, S_IRUGO,
|
||||
applesmc_show_temperature, NULL, 39);
|
||||
|
||||
static struct attribute *label_attributes[] = {
|
||||
&sensor_dev_attr_temp1_label.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_label.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_label.dev_attr.attr,
|
||||
&sensor_dev_attr_temp4_label.dev_attr.attr,
|
||||
&sensor_dev_attr_temp5_label.dev_attr.attr,
|
||||
&sensor_dev_attr_temp6_label.dev_attr.attr,
|
||||
&sensor_dev_attr_temp7_label.dev_attr.attr,
|
||||
&sensor_dev_attr_temp8_label.dev_attr.attr,
|
||||
&sensor_dev_attr_temp9_label.dev_attr.attr,
|
||||
&sensor_dev_attr_temp10_label.dev_attr.attr,
|
||||
&sensor_dev_attr_temp11_label.dev_attr.attr,
|
||||
&sensor_dev_attr_temp12_label.dev_attr.attr,
|
||||
&sensor_dev_attr_temp13_label.dev_attr.attr,
|
||||
&sensor_dev_attr_temp14_label.dev_attr.attr,
|
||||
&sensor_dev_attr_temp15_label.dev_attr.attr,
|
||||
&sensor_dev_attr_temp16_label.dev_attr.attr,
|
||||
&sensor_dev_attr_temp17_label.dev_attr.attr,
|
||||
&sensor_dev_attr_temp18_label.dev_attr.attr,
|
||||
&sensor_dev_attr_temp19_label.dev_attr.attr,
|
||||
&sensor_dev_attr_temp20_label.dev_attr.attr,
|
||||
&sensor_dev_attr_temp21_label.dev_attr.attr,
|
||||
&sensor_dev_attr_temp22_label.dev_attr.attr,
|
||||
&sensor_dev_attr_temp23_label.dev_attr.attr,
|
||||
&sensor_dev_attr_temp24_label.dev_attr.attr,
|
||||
&sensor_dev_attr_temp25_label.dev_attr.attr,
|
||||
&sensor_dev_attr_temp26_label.dev_attr.attr,
|
||||
&sensor_dev_attr_temp27_label.dev_attr.attr,
|
||||
&sensor_dev_attr_temp28_label.dev_attr.attr,
|
||||
&sensor_dev_attr_temp29_label.dev_attr.attr,
|
||||
&sensor_dev_attr_temp30_label.dev_attr.attr,
|
||||
&sensor_dev_attr_temp31_label.dev_attr.attr,
|
||||
&sensor_dev_attr_temp32_label.dev_attr.attr,
|
||||
&sensor_dev_attr_temp33_label.dev_attr.attr,
|
||||
&sensor_dev_attr_temp34_label.dev_attr.attr,
|
||||
&sensor_dev_attr_temp35_label.dev_attr.attr,
|
||||
&sensor_dev_attr_temp36_label.dev_attr.attr,
|
||||
&sensor_dev_attr_temp37_label.dev_attr.attr,
|
||||
&sensor_dev_attr_temp38_label.dev_attr.attr,
|
||||
&sensor_dev_attr_temp39_label.dev_attr.attr,
|
||||
&sensor_dev_attr_temp40_label.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct attribute *temperature_attributes[] = {
|
||||
&sensor_dev_attr_temp1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_input.dev_attr.attr,
|
||||
@@ -1241,6 +1390,10 @@ static struct attribute *temperature_attributes[] = {
|
||||
static const struct attribute_group temperature_attributes_group =
|
||||
{ .attrs = temperature_attributes };
|
||||
|
||||
static const struct attribute_group label_attributes_group = {
|
||||
.attrs = label_attributes
|
||||
};
|
||||
|
||||
/* Module stuff */
|
||||
|
||||
/*
|
||||
@@ -1363,6 +1516,14 @@ static __initdata struct dmi_match_data applesmc_dmi_data[] = {
|
||||
{ .accelerometer = 0, .light = 0, .temperature_set = 17 },
|
||||
/* MacBook Pro 2,2: accelerometer, backlight and temperature set 18 */
|
||||
{ .accelerometer = 1, .light = 1, .temperature_set = 18 },
|
||||
/* MacBook Pro 5,3: accelerometer, backlight and temperature set 19 */
|
||||
{ .accelerometer = 1, .light = 1, .temperature_set = 19 },
|
||||
/* MacBook Pro 5,4: accelerometer, backlight and temperature set 20 */
|
||||
{ .accelerometer = 1, .light = 1, .temperature_set = 20 },
|
||||
/* MacBook Pro 6,2: accelerometer, backlight and temperature set 21 */
|
||||
{ .accelerometer = 1, .light = 1, .temperature_set = 21 },
|
||||
/* MacBook Pro 7,1: accelerometer, backlight and temperature set 22 */
|
||||
{ .accelerometer = 1, .light = 1, .temperature_set = 22 },
|
||||
};
|
||||
|
||||
/* Note that DMI_MATCH(...,"MacBook") will match "MacBookPro1,1".
|
||||
@@ -1376,6 +1537,22 @@ static __initdata struct dmi_system_id applesmc_whitelist[] = {
|
||||
DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir") },
|
||||
&applesmc_dmi_data[7]},
|
||||
{ applesmc_dmi_match, "Apple MacBook Pro 7", {
|
||||
DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro7") },
|
||||
&applesmc_dmi_data[22]},
|
||||
{ applesmc_dmi_match, "Apple MacBook Pro 5,4", {
|
||||
DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5,4") },
|
||||
&applesmc_dmi_data[20]},
|
||||
{ applesmc_dmi_match, "Apple MacBook Pro 5,3", {
|
||||
DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5,3") },
|
||||
&applesmc_dmi_data[19]},
|
||||
{ applesmc_dmi_match, "Apple MacBook Pro 6", {
|
||||
DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro6") },
|
||||
&applesmc_dmi_data[21]},
|
||||
{ applesmc_dmi_match, "Apple MacBook Pro 5", {
|
||||
DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5") },
|
||||
@@ -1518,7 +1695,8 @@ static int __init applesmc_init(void)
|
||||
for (i = 0;
|
||||
temperature_sensors_sets[applesmc_temperature_set][i] != NULL;
|
||||
i++) {
|
||||
if (temperature_attributes[i] == NULL) {
|
||||
if (temperature_attributes[i] == NULL ||
|
||||
label_attributes[i] == NULL) {
|
||||
printk(KERN_ERR "applesmc: More temperature sensors "
|
||||
"in temperature_sensors_sets (at least %i)"
|
||||
"than available sysfs files in "
|
||||
@@ -1530,6 +1708,10 @@ static int __init applesmc_init(void)
|
||||
temperature_attributes[i]);
|
||||
if (ret)
|
||||
goto out_temperature;
|
||||
ret = sysfs_create_file(&pdev->dev.kobj,
|
||||
label_attributes[i]);
|
||||
if (ret)
|
||||
goto out_temperature;
|
||||
}
|
||||
|
||||
if (applesmc_accelerometer) {
|
||||
@@ -1580,6 +1762,7 @@ out_accelerometer:
|
||||
if (applesmc_accelerometer)
|
||||
applesmc_release_accelerometer();
|
||||
out_temperature:
|
||||
sysfs_remove_group(&pdev->dev.kobj, &label_attributes_group);
|
||||
sysfs_remove_group(&pdev->dev.kobj, &temperature_attributes_group);
|
||||
out_fans:
|
||||
while (fans_handled)
|
||||
@@ -1609,6 +1792,7 @@ static void __exit applesmc_exit(void)
|
||||
}
|
||||
if (applesmc_accelerometer)
|
||||
applesmc_release_accelerometer();
|
||||
sysfs_remove_group(&pdev->dev.kobj, &label_attributes_group);
|
||||
sysfs_remove_group(&pdev->dev.kobj, &temperature_attributes_group);
|
||||
while (fans_handled)
|
||||
sysfs_remove_group(&pdev->dev.kobj,
|
||||
|
||||
@@ -1411,6 +1411,13 @@ static int __init atk0110_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Make sure it's safe to access the device through ACPI */
|
||||
if (!acpi_resources_are_enforced()) {
|
||||
pr_err("atk: Resources not safely usable due to "
|
||||
"acpi_enforce_resources kernel parameter\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
ret = acpi_bus_register_driver(&atk_driver);
|
||||
if (ret)
|
||||
pr_info("atk: acpi_bus_register_driver failed: %d\n", ret);
|
||||
|
||||
+225
-103
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,344 @@
|
||||
/*
|
||||
* emc1403.c - SMSC Thermal Driver
|
||||
*
|
||||
* Copyright (C) 2008 Intel Corp
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* 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; version 2 of the License.
|
||||
*
|
||||
* 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.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* TODO
|
||||
* - cache alarm and critical limit registers
|
||||
* - add emc1404 support
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#define THERMAL_PID_REG 0xfd
|
||||
#define THERMAL_SMSC_ID_REG 0xfe
|
||||
#define THERMAL_REVISION_REG 0xff
|
||||
|
||||
struct thermal_data {
|
||||
struct device *hwmon_dev;
|
||||
struct mutex mutex;
|
||||
/* Cache the hyst value so we don't keep re-reading it. In theory
|
||||
we could cache it forever as nobody else should be writing it. */
|
||||
u8 cached_hyst;
|
||||
unsigned long hyst_valid;
|
||||
};
|
||||
|
||||
static ssize_t show_temp(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct sensor_device_attribute *sda = to_sensor_dev_attr(attr);
|
||||
int retval = i2c_smbus_read_byte_data(client, sda->index);
|
||||
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
return sprintf(buf, "%d000\n", retval);
|
||||
}
|
||||
|
||||
static ssize_t show_bit(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct sensor_device_attribute_2 *sda = to_sensor_dev_attr_2(attr);
|
||||
int retval = i2c_smbus_read_byte_data(client, sda->nr);
|
||||
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
retval &= sda->index;
|
||||
return sprintf(buf, "%d\n", retval ? 1 : 0);
|
||||
}
|
||||
|
||||
static ssize_t store_temp(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct sensor_device_attribute *sda = to_sensor_dev_attr(attr);
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
unsigned long val;
|
||||
int retval;
|
||||
|
||||
if (strict_strtoul(buf, 10, &val))
|
||||
return -EINVAL;
|
||||
retval = i2c_smbus_write_byte_data(client, sda->index,
|
||||
DIV_ROUND_CLOSEST(val, 1000));
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t show_hyst(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct thermal_data *data = i2c_get_clientdata(client);
|
||||
struct sensor_device_attribute *sda = to_sensor_dev_attr(attr);
|
||||
int retval;
|
||||
int hyst;
|
||||
|
||||
retval = i2c_smbus_read_byte_data(client, sda->index);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
|
||||
if (time_after(jiffies, data->hyst_valid)) {
|
||||
hyst = i2c_smbus_read_byte_data(client, 0x21);
|
||||
if (hyst < 0)
|
||||
return retval;
|
||||
data->cached_hyst = hyst;
|
||||
data->hyst_valid = jiffies + HZ;
|
||||
}
|
||||
return sprintf(buf, "%d000\n", retval - data->cached_hyst);
|
||||
}
|
||||
|
||||
static ssize_t store_hyst(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct thermal_data *data = i2c_get_clientdata(client);
|
||||
struct sensor_device_attribute *sda = to_sensor_dev_attr(attr);
|
||||
int retval;
|
||||
int hyst;
|
||||
unsigned long val;
|
||||
|
||||
if (strict_strtoul(buf, 10, &val))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
retval = i2c_smbus_read_byte_data(client, sda->index);
|
||||
if (retval < 0)
|
||||
goto fail;
|
||||
|
||||
hyst = val - retval * 1000;
|
||||
hyst = DIV_ROUND_CLOSEST(hyst, 1000);
|
||||
if (hyst < 0 || hyst > 255) {
|
||||
retval = -ERANGE;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
retval = i2c_smbus_write_byte_data(client, 0x21, hyst);
|
||||
if (retval == 0) {
|
||||
retval = count;
|
||||
data->cached_hyst = hyst;
|
||||
data->hyst_valid = jiffies + HZ;
|
||||
}
|
||||
fail:
|
||||
mutex_unlock(&data->mutex);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sensors. We pass the actual i2c register to the methods.
|
||||
*/
|
||||
|
||||
static SENSOR_DEVICE_ATTR(temp1_min, S_IRUGO | S_IWUSR,
|
||||
show_temp, store_temp, 0x06);
|
||||
static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR,
|
||||
show_temp, store_temp, 0x05);
|
||||
static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO | S_IWUSR,
|
||||
show_temp, store_temp, 0x20);
|
||||
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0x00);
|
||||
static SENSOR_DEVICE_ATTR_2(temp1_min_alarm, S_IRUGO,
|
||||
show_bit, NULL, 0x36, 0x01);
|
||||
static SENSOR_DEVICE_ATTR_2(temp1_max_alarm, S_IRUGO,
|
||||
show_bit, NULL, 0x35, 0x01);
|
||||
static SENSOR_DEVICE_ATTR_2(temp1_crit_alarm, S_IRUGO,
|
||||
show_bit, NULL, 0x37, 0x01);
|
||||
static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO | S_IWUSR,
|
||||
show_hyst, store_hyst, 0x20);
|
||||
|
||||
static SENSOR_DEVICE_ATTR(temp2_min, S_IRUGO | S_IWUSR,
|
||||
show_temp, store_temp, 0x08);
|
||||
static SENSOR_DEVICE_ATTR(temp2_max, S_IRUGO | S_IWUSR,
|
||||
show_temp, store_temp, 0x07);
|
||||
static SENSOR_DEVICE_ATTR(temp2_crit, S_IRUGO | S_IWUSR,
|
||||
show_temp, store_temp, 0x19);
|
||||
static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 0x01);
|
||||
static SENSOR_DEVICE_ATTR_2(temp2_min_alarm, S_IRUGO,
|
||||
show_bit, NULL, 0x36, 0x02);
|
||||
static SENSOR_DEVICE_ATTR_2(temp2_max_alarm, S_IRUGO,
|
||||
show_bit, NULL, 0x35, 0x02);
|
||||
static SENSOR_DEVICE_ATTR_2(temp2_crit_alarm, S_IRUGO,
|
||||
show_bit, NULL, 0x37, 0x02);
|
||||
static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO | S_IWUSR,
|
||||
show_hyst, store_hyst, 0x19);
|
||||
|
||||
static SENSOR_DEVICE_ATTR(temp3_min, S_IRUGO | S_IWUSR,
|
||||
show_temp, store_temp, 0x16);
|
||||
static SENSOR_DEVICE_ATTR(temp3_max, S_IRUGO | S_IWUSR,
|
||||
show_temp, store_temp, 0x15);
|
||||
static SENSOR_DEVICE_ATTR(temp3_crit, S_IRUGO | S_IWUSR,
|
||||
show_temp, store_temp, 0x1A);
|
||||
static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 0x23);
|
||||
static SENSOR_DEVICE_ATTR_2(temp3_min_alarm, S_IRUGO,
|
||||
show_bit, NULL, 0x36, 0x04);
|
||||
static SENSOR_DEVICE_ATTR_2(temp3_max_alarm, S_IRUGO,
|
||||
show_bit, NULL, 0x35, 0x04);
|
||||
static SENSOR_DEVICE_ATTR_2(temp3_crit_alarm, S_IRUGO,
|
||||
show_bit, NULL, 0x37, 0x04);
|
||||
static SENSOR_DEVICE_ATTR(temp3_crit_hyst, S_IRUGO | S_IWUSR,
|
||||
show_hyst, store_hyst, 0x1A);
|
||||
|
||||
static struct attribute *mid_att_thermal[] = {
|
||||
&sensor_dev_attr_temp1_min.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_crit.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_min.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_crit.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_crit_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_min.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_crit.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_min_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_crit_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_crit_hyst.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group m_thermal_gr = {
|
||||
.attrs = mid_att_thermal
|
||||
};
|
||||
|
||||
static int emc1403_detect(struct i2c_client *client,
|
||||
struct i2c_board_info *info)
|
||||
{
|
||||
int id;
|
||||
/* Check if thermal chip is SMSC and EMC1403 */
|
||||
|
||||
id = i2c_smbus_read_byte_data(client, THERMAL_SMSC_ID_REG);
|
||||
if (id != 0x5d)
|
||||
return -ENODEV;
|
||||
|
||||
/* Note: 0x25 is the 1404 which is very similar and this
|
||||
driver could be extended */
|
||||
id = i2c_smbus_read_byte_data(client, THERMAL_PID_REG);
|
||||
if (id != 0x21)
|
||||
return -ENODEV;
|
||||
|
||||
id = i2c_smbus_read_byte_data(client, THERMAL_REVISION_REG);
|
||||
if (id != 0x01)
|
||||
return -ENODEV;
|
||||
|
||||
strlcpy(info->type, "emc1403", I2C_NAME_SIZE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int emc1403_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int res;
|
||||
struct thermal_data *data;
|
||||
|
||||
data = kzalloc(sizeof(struct thermal_data), GFP_KERNEL);
|
||||
if (data == NULL) {
|
||||
dev_warn(&client->dev, "out of memory");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
i2c_set_clientdata(client, data);
|
||||
mutex_init(&data->mutex);
|
||||
data->hyst_valid = jiffies - 1; /* Expired */
|
||||
|
||||
res = sysfs_create_group(&client->dev.kobj, &m_thermal_gr);
|
||||
if (res) {
|
||||
dev_warn(&client->dev, "create group failed\n");
|
||||
hwmon_device_unregister(data->hwmon_dev);
|
||||
goto thermal_error1;
|
||||
}
|
||||
data->hwmon_dev = hwmon_device_register(&client->dev);
|
||||
if (IS_ERR(data->hwmon_dev)) {
|
||||
res = PTR_ERR(data->hwmon_dev);
|
||||
dev_warn(&client->dev, "register hwmon dev failed\n");
|
||||
goto thermal_error2;
|
||||
}
|
||||
dev_info(&client->dev, "EMC1403 Thermal chip found\n");
|
||||
return res;
|
||||
|
||||
thermal_error2:
|
||||
sysfs_remove_group(&client->dev.kobj, &m_thermal_gr);
|
||||
thermal_error1:
|
||||
kfree(data);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int emc1403_remove(struct i2c_client *client)
|
||||
{
|
||||
struct thermal_data *data = i2c_get_clientdata(client);
|
||||
|
||||
hwmon_device_unregister(data->hwmon_dev);
|
||||
sysfs_remove_group(&client->dev.kobj, &m_thermal_gr);
|
||||
kfree(data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const unsigned short emc1403_address_list[] = {
|
||||
0x18, 0x2a, 0x4c, 0x4d, I2C_CLIENT_END
|
||||
};
|
||||
|
||||
static const struct i2c_device_id emc1403_idtable[] = {
|
||||
{ "emc1403", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, emc1403_idtable);
|
||||
|
||||
static struct i2c_driver sensor_emc1403 = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = "emc1403",
|
||||
},
|
||||
.detect = emc1403_detect,
|
||||
.probe = emc1403_probe,
|
||||
.remove = emc1403_remove,
|
||||
.id_table = emc1403_idtable,
|
||||
.address_list = emc1403_address_list,
|
||||
};
|
||||
|
||||
static int __init sensor_emc1403_init(void)
|
||||
{
|
||||
return i2c_add_driver(&sensor_emc1403);
|
||||
}
|
||||
|
||||
static void __exit sensor_emc1403_exit(void)
|
||||
{
|
||||
i2c_del_driver(&sensor_emc1403);
|
||||
}
|
||||
|
||||
module_init(sensor_emc1403_init);
|
||||
module_exit(sensor_emc1403_exit);
|
||||
|
||||
MODULE_AUTHOR("Kalhan Trisal <kalhan.trisal@intel.com");
|
||||
MODULE_DESCRIPTION("emc1403 Thermal Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
+123
-47
@@ -856,21 +856,19 @@ static inline int superio_inb(int base, int reg)
|
||||
static int superio_inw(int base, int reg)
|
||||
{
|
||||
int val;
|
||||
outb(reg++, base);
|
||||
val = inb(base + 1) << 8;
|
||||
outb(reg, base);
|
||||
val |= inb(base + 1);
|
||||
val = superio_inb(base, reg) << 8;
|
||||
val |= superio_inb(base, reg + 1);
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline void superio_enter(int base)
|
||||
{
|
||||
/* according to the datasheet the key must be send twice! */
|
||||
outb( SIO_UNLOCK_KEY, base);
|
||||
outb( SIO_UNLOCK_KEY, base);
|
||||
outb(SIO_UNLOCK_KEY, base);
|
||||
outb(SIO_UNLOCK_KEY, base);
|
||||
}
|
||||
|
||||
static inline void superio_select( int base, int ld)
|
||||
static inline void superio_select(int base, int ld)
|
||||
{
|
||||
outb(SIO_REG_LDSEL, base);
|
||||
outb(ld, base + 1);
|
||||
@@ -905,10 +903,8 @@ static u16 f71882fg_read16(struct f71882fg_data *data, u8 reg)
|
||||
{
|
||||
u16 val;
|
||||
|
||||
outb(reg++, data->addr + ADDR_REG_OFFSET);
|
||||
val = inb(data->addr + DATA_REG_OFFSET) << 8;
|
||||
outb(reg, data->addr + ADDR_REG_OFFSET);
|
||||
val |= inb(data->addr + DATA_REG_OFFSET);
|
||||
val = f71882fg_read8(data, reg) << 8;
|
||||
val |= f71882fg_read8(data, reg + 1);
|
||||
|
||||
return val;
|
||||
}
|
||||
@@ -921,10 +917,8 @@ static void f71882fg_write8(struct f71882fg_data *data, u8 reg, u8 val)
|
||||
|
||||
static void f71882fg_write16(struct f71882fg_data *data, u8 reg, u16 val)
|
||||
{
|
||||
outb(reg++, data->addr + ADDR_REG_OFFSET);
|
||||
outb(val >> 8, data->addr + DATA_REG_OFFSET);
|
||||
outb(reg, data->addr + ADDR_REG_OFFSET);
|
||||
outb(val & 255, data->addr + DATA_REG_OFFSET);
|
||||
f71882fg_write8(data, reg, val >> 8);
|
||||
f71882fg_write8(data, reg + 1, val & 0xff);
|
||||
}
|
||||
|
||||
static u16 f71882fg_read_temp(struct f71882fg_data *data, int nr)
|
||||
@@ -945,7 +939,7 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev)
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
/* Update once every 60 seconds */
|
||||
if ( time_after(jiffies, data->last_limits + 60 * HZ ) ||
|
||||
if (time_after(jiffies, data->last_limits + 60 * HZ) ||
|
||||
!data->valid) {
|
||||
if (data->type == f71882fg || data->type == f71889fg) {
|
||||
data->in1_max =
|
||||
@@ -1127,8 +1121,12 @@ static ssize_t store_fan_full_speed(struct device *dev,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct f71882fg_data *data = dev_get_drvdata(dev);
|
||||
int nr = to_sensor_dev_attr_2(devattr)->index;
|
||||
long val = simple_strtol(buf, NULL, 10);
|
||||
int err, nr = to_sensor_dev_attr_2(devattr)->index;
|
||||
long val;
|
||||
|
||||
err = strict_strtol(buf, 10, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
val = SENSORS_LIMIT(val, 23, 1500000);
|
||||
val = fan_to_reg(val);
|
||||
@@ -1157,8 +1155,12 @@ static ssize_t store_fan_beep(struct device *dev, struct device_attribute
|
||||
*devattr, const char *buf, size_t count)
|
||||
{
|
||||
struct f71882fg_data *data = dev_get_drvdata(dev);
|
||||
int nr = to_sensor_dev_attr_2(devattr)->index;
|
||||
unsigned long val = simple_strtoul(buf, NULL, 10);
|
||||
int err, nr = to_sensor_dev_attr_2(devattr)->index;
|
||||
unsigned long val;
|
||||
|
||||
err = strict_strtoul(buf, 10, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->fan_beep = f71882fg_read8(data, F71882FG_REG_FAN_BEEP);
|
||||
@@ -1206,7 +1208,14 @@ static ssize_t store_in_max(struct device *dev, struct device_attribute
|
||||
*devattr, const char *buf, size_t count)
|
||||
{
|
||||
struct f71882fg_data *data = dev_get_drvdata(dev);
|
||||
long val = simple_strtol(buf, NULL, 10) / 8;
|
||||
int err;
|
||||
long val;
|
||||
|
||||
err = strict_strtol(buf, 10, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
val /= 8;
|
||||
val = SENSORS_LIMIT(val, 0, 255);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
@@ -1233,8 +1242,12 @@ static ssize_t store_in_beep(struct device *dev, struct device_attribute
|
||||
*devattr, const char *buf, size_t count)
|
||||
{
|
||||
struct f71882fg_data *data = dev_get_drvdata(dev);
|
||||
int nr = to_sensor_dev_attr_2(devattr)->index;
|
||||
unsigned long val = simple_strtoul(buf, NULL, 10);
|
||||
int err, nr = to_sensor_dev_attr_2(devattr)->index;
|
||||
unsigned long val;
|
||||
|
||||
err = strict_strtoul(buf, 10, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->in_beep = f71882fg_read8(data, F71882FG_REG_IN_BEEP);
|
||||
@@ -1299,8 +1312,14 @@ static ssize_t store_temp_max(struct device *dev, struct device_attribute
|
||||
*devattr, const char *buf, size_t count)
|
||||
{
|
||||
struct f71882fg_data *data = dev_get_drvdata(dev);
|
||||
int nr = to_sensor_dev_attr_2(devattr)->index;
|
||||
long val = simple_strtol(buf, NULL, 10) / 1000;
|
||||
int err, nr = to_sensor_dev_attr_2(devattr)->index;
|
||||
long val;
|
||||
|
||||
err = strict_strtol(buf, 10, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
val /= 1000;
|
||||
val = SENSORS_LIMIT(val, 0, 255);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
@@ -1333,10 +1352,16 @@ static ssize_t store_temp_max_hyst(struct device *dev, struct device_attribute
|
||||
*devattr, const char *buf, size_t count)
|
||||
{
|
||||
struct f71882fg_data *data = dev_get_drvdata(dev);
|
||||
int nr = to_sensor_dev_attr_2(devattr)->index;
|
||||
long val = simple_strtol(buf, NULL, 10) / 1000;
|
||||
int err, nr = to_sensor_dev_attr_2(devattr)->index;
|
||||
ssize_t ret = count;
|
||||
u8 reg;
|
||||
long val;
|
||||
|
||||
err = strict_strtol(buf, 10, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
val /= 1000;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
@@ -1372,8 +1397,14 @@ static ssize_t store_temp_crit(struct device *dev, struct device_attribute
|
||||
*devattr, const char *buf, size_t count)
|
||||
{
|
||||
struct f71882fg_data *data = dev_get_drvdata(dev);
|
||||
int nr = to_sensor_dev_attr_2(devattr)->index;
|
||||
long val = simple_strtol(buf, NULL, 10) / 1000;
|
||||
int err, nr = to_sensor_dev_attr_2(devattr)->index;
|
||||
long val;
|
||||
|
||||
err = strict_strtol(buf, 10, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
val /= 1000;
|
||||
val = SENSORS_LIMIT(val, 0, 255);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
@@ -1427,8 +1458,12 @@ static ssize_t store_temp_beep(struct device *dev, struct device_attribute
|
||||
*devattr, const char *buf, size_t count)
|
||||
{
|
||||
struct f71882fg_data *data = dev_get_drvdata(dev);
|
||||
int nr = to_sensor_dev_attr_2(devattr)->index;
|
||||
unsigned long val = simple_strtoul(buf, NULL, 10);
|
||||
int err, nr = to_sensor_dev_attr_2(devattr)->index;
|
||||
unsigned long val;
|
||||
|
||||
err = strict_strtoul(buf, 10, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->temp_beep = f71882fg_read8(data, F71882FG_REG_TEMP_BEEP);
|
||||
@@ -1490,8 +1525,13 @@ static ssize_t store_pwm(struct device *dev,
|
||||
size_t count)
|
||||
{
|
||||
struct f71882fg_data *data = dev_get_drvdata(dev);
|
||||
int nr = to_sensor_dev_attr_2(devattr)->index;
|
||||
long val = simple_strtol(buf, NULL, 10);
|
||||
int err, nr = to_sensor_dev_attr_2(devattr)->index;
|
||||
long val;
|
||||
|
||||
err = strict_strtol(buf, 10, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
val = SENSORS_LIMIT(val, 0, 255);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
@@ -1551,8 +1591,12 @@ static ssize_t store_pwm_enable(struct device *dev, struct device_attribute
|
||||
*devattr, const char *buf, size_t count)
|
||||
{
|
||||
struct f71882fg_data *data = dev_get_drvdata(dev);
|
||||
int nr = to_sensor_dev_attr_2(devattr)->index;
|
||||
long val = simple_strtol(buf, NULL, 10);
|
||||
int err, nr = to_sensor_dev_attr_2(devattr)->index;
|
||||
long val;
|
||||
|
||||
err = strict_strtol(buf, 10, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Special case for F8000 pwm channel 3 which only does auto mode */
|
||||
if (data->type == f8000 && nr == 2 && val != 2)
|
||||
@@ -1626,9 +1670,14 @@ static ssize_t store_pwm_auto_point_pwm(struct device *dev,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct f71882fg_data *data = dev_get_drvdata(dev);
|
||||
int pwm = to_sensor_dev_attr_2(devattr)->index;
|
||||
int err, pwm = to_sensor_dev_attr_2(devattr)->index;
|
||||
int point = to_sensor_dev_attr_2(devattr)->nr;
|
||||
long val = simple_strtol(buf, NULL, 10);
|
||||
long val;
|
||||
|
||||
err = strict_strtol(buf, 10, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
val = SENSORS_LIMIT(val, 0, 255);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
@@ -1674,10 +1723,16 @@ static ssize_t store_pwm_auto_point_temp_hyst(struct device *dev,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct f71882fg_data *data = dev_get_drvdata(dev);
|
||||
int nr = to_sensor_dev_attr_2(devattr)->index;
|
||||
int err, nr = to_sensor_dev_attr_2(devattr)->index;
|
||||
int point = to_sensor_dev_attr_2(devattr)->nr;
|
||||
long val = simple_strtol(buf, NULL, 10) / 1000;
|
||||
u8 reg;
|
||||
long val;
|
||||
|
||||
err = strict_strtol(buf, 10, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
val /= 1000;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->pwm_auto_point_temp[nr][point] =
|
||||
@@ -1716,8 +1771,12 @@ static ssize_t store_pwm_interpolate(struct device *dev,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct f71882fg_data *data = dev_get_drvdata(dev);
|
||||
int nr = to_sensor_dev_attr_2(devattr)->index;
|
||||
unsigned long val = simple_strtoul(buf, NULL, 10);
|
||||
int err, nr = to_sensor_dev_attr_2(devattr)->index;
|
||||
unsigned long val;
|
||||
|
||||
err = strict_strtoul(buf, 10, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->pwm_auto_point_mapping[nr] =
|
||||
@@ -1752,8 +1811,12 @@ static ssize_t store_pwm_auto_point_channel(struct device *dev,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct f71882fg_data *data = dev_get_drvdata(dev);
|
||||
int nr = to_sensor_dev_attr_2(devattr)->index;
|
||||
long val = simple_strtol(buf, NULL, 10);
|
||||
int err, nr = to_sensor_dev_attr_2(devattr)->index;
|
||||
long val;
|
||||
|
||||
err = strict_strtol(buf, 10, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
switch (val) {
|
||||
case 1:
|
||||
@@ -1798,9 +1861,15 @@ static ssize_t store_pwm_auto_point_temp(struct device *dev,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct f71882fg_data *data = dev_get_drvdata(dev);
|
||||
int pwm = to_sensor_dev_attr_2(devattr)->index;
|
||||
int err, pwm = to_sensor_dev_attr_2(devattr)->index;
|
||||
int point = to_sensor_dev_attr_2(devattr)->nr;
|
||||
long val = simple_strtol(buf, NULL, 10) / 1000;
|
||||
long val;
|
||||
|
||||
err = strict_strtol(buf, 10, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
val /= 1000;
|
||||
|
||||
if (data->type == f71889fg)
|
||||
val = SENSORS_LIMIT(val, -128, 127);
|
||||
@@ -2109,6 +2178,13 @@ static int __init f71882fg_find(int sioaddr, unsigned short *address,
|
||||
int err = -ENODEV;
|
||||
u16 devid;
|
||||
|
||||
/* Don't step on other drivers' I/O space by accident */
|
||||
if (!request_region(sioaddr, 2, DRVNAME)) {
|
||||
printk(KERN_ERR DRVNAME ": I/O address 0x%04x already in use\n",
|
||||
(int)sioaddr);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
superio_enter(sioaddr);
|
||||
|
||||
devid = superio_inw(sioaddr, SIO_REG_MANID);
|
||||
@@ -2151,8 +2227,7 @@ static int __init f71882fg_find(int sioaddr, unsigned short *address,
|
||||
}
|
||||
|
||||
*address = superio_inw(sioaddr, SIO_REG_ADDR);
|
||||
if (*address == 0)
|
||||
{
|
||||
if (*address == 0) {
|
||||
printk(KERN_WARNING DRVNAME ": Base address not set\n");
|
||||
goto exit;
|
||||
}
|
||||
@@ -2164,6 +2239,7 @@ static int __init f71882fg_find(int sioaddr, unsigned short *address,
|
||||
(int)superio_inb(sioaddr, SIO_REG_DEVREV));
|
||||
exit:
|
||||
superio_exit(sioaddr);
|
||||
release_region(sioaddr, 2);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
+12
-4
@@ -53,7 +53,7 @@
|
||||
* Address is fully defined internally and cannot be changed.
|
||||
*/
|
||||
|
||||
static const unsigned short normal_i2c[] = { 0x4c, I2C_CLIENT_END };
|
||||
static const unsigned short normal_i2c[] = { 0x18, 0x4c, 0x4e, I2C_CLIENT_END };
|
||||
|
||||
/*
|
||||
* The LM63 registers
|
||||
@@ -131,12 +131,15 @@ static struct lm63_data *lm63_update_device(struct device *dev);
|
||||
static int lm63_detect(struct i2c_client *client, struct i2c_board_info *info);
|
||||
static void lm63_init_client(struct i2c_client *client);
|
||||
|
||||
enum chips { lm63, lm64 };
|
||||
|
||||
/*
|
||||
* Driver data (common to all clients)
|
||||
*/
|
||||
|
||||
static const struct i2c_device_id lm63_id[] = {
|
||||
{ "lm63", 0 },
|
||||
{ "lm63", lm63 },
|
||||
{ "lm64", lm64 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, lm63_id);
|
||||
@@ -422,6 +425,7 @@ static int lm63_detect(struct i2c_client *new_client,
|
||||
struct i2c_adapter *adapter = new_client->adapter;
|
||||
u8 man_id, chip_id, reg_config1, reg_config2;
|
||||
u8 reg_alert_status, reg_alert_mask;
|
||||
int address = new_client->addr;
|
||||
|
||||
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
|
||||
return -ENODEV;
|
||||
@@ -439,7 +443,6 @@ static int lm63_detect(struct i2c_client *new_client,
|
||||
LM63_REG_ALERT_MASK);
|
||||
|
||||
if (man_id != 0x01 /* National Semiconductor */
|
||||
|| chip_id != 0x41 /* LM63 */
|
||||
|| (reg_config1 & 0x18) != 0x00
|
||||
|| (reg_config2 & 0xF8) != 0x00
|
||||
|| (reg_alert_status & 0x20) != 0x00
|
||||
@@ -450,7 +453,12 @@ static int lm63_detect(struct i2c_client *new_client,
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
strlcpy(info->type, "lm63", I2C_NAME_SIZE);
|
||||
if (chip_id == 0x41 && address == 0x4c)
|
||||
strlcpy(info->type, "lm63", I2C_NAME_SIZE);
|
||||
else if (chip_id == 0x51 && (address == 0x18 || address == 0x4e))
|
||||
strlcpy(info->type, "lm64", I2C_NAME_SIZE);
|
||||
else
|
||||
return -ENODEV;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ enum lm75_type { /* keep sorted in alphabetical order */
|
||||
tcn75,
|
||||
tmp100,
|
||||
tmp101,
|
||||
tmp105,
|
||||
tmp175,
|
||||
tmp275,
|
||||
tmp75,
|
||||
@@ -220,6 +221,7 @@ static const struct i2c_device_id lm75_ids[] = {
|
||||
{ "tcn75", tcn75, },
|
||||
{ "tmp100", tmp100, },
|
||||
{ "tmp101", tmp101, },
|
||||
{ "tmp105", tmp105, },
|
||||
{ "tmp175", tmp175, },
|
||||
{ "tmp275", tmp275, },
|
||||
{ "tmp75", tmp75, },
|
||||
|
||||
@@ -982,7 +982,8 @@ static struct lm90_data *lm90_update_device(struct device *dev)
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) {
|
||||
if (time_after(jiffies, data->last_updated + HZ / 2 + HZ / 10)
|
||||
|| !data->valid) {
|
||||
u8 h, l;
|
||||
|
||||
dev_dbg(&client->dev, "Updating lm90 data.\n");
|
||||
|
||||
+5
-13
@@ -45,9 +45,7 @@ enum ltc4245_cmd {
|
||||
LTC4245_VEEIN = 0x19,
|
||||
LTC4245_VEESENSE = 0x1a,
|
||||
LTC4245_VEEOUT = 0x1b,
|
||||
LTC4245_GPIOADC1 = 0x1c,
|
||||
LTC4245_GPIOADC2 = 0x1d,
|
||||
LTC4245_GPIOADC3 = 0x1e,
|
||||
LTC4245_GPIOADC = 0x1c,
|
||||
};
|
||||
|
||||
struct ltc4245_data {
|
||||
@@ -61,7 +59,7 @@ struct ltc4245_data {
|
||||
u8 cregs[0x08];
|
||||
|
||||
/* Voltage registers */
|
||||
u8 vregs[0x0f];
|
||||
u8 vregs[0x0d];
|
||||
};
|
||||
|
||||
static struct ltc4245_data *ltc4245_update_device(struct device *dev)
|
||||
@@ -86,7 +84,7 @@ static struct ltc4245_data *ltc4245_update_device(struct device *dev)
|
||||
data->cregs[i] = val;
|
||||
}
|
||||
|
||||
/* Read voltage registers -- 0x10 to 0x1f */
|
||||
/* Read voltage registers -- 0x10 to 0x1c */
|
||||
for (i = 0; i < ARRAY_SIZE(data->vregs); i++) {
|
||||
val = i2c_smbus_read_byte_data(client, i+0x10);
|
||||
if (unlikely(val < 0))
|
||||
@@ -128,9 +126,7 @@ static int ltc4245_get_voltage(struct device *dev, u8 reg)
|
||||
case LTC4245_VEEOUT:
|
||||
voltage = regval * -55;
|
||||
break;
|
||||
case LTC4245_GPIOADC1:
|
||||
case LTC4245_GPIOADC2:
|
||||
case LTC4245_GPIOADC3:
|
||||
case LTC4245_GPIOADC:
|
||||
voltage = regval * 10;
|
||||
break;
|
||||
default:
|
||||
@@ -297,9 +293,7 @@ LTC4245_ALARM(in7_min_alarm, (1 << 2), LTC4245_FAULT2);
|
||||
LTC4245_ALARM(in8_min_alarm, (1 << 3), LTC4245_FAULT2);
|
||||
|
||||
/* GPIO voltages */
|
||||
LTC4245_VOLTAGE(in9_input, LTC4245_GPIOADC1);
|
||||
LTC4245_VOLTAGE(in10_input, LTC4245_GPIOADC2);
|
||||
LTC4245_VOLTAGE(in11_input, LTC4245_GPIOADC3);
|
||||
LTC4245_VOLTAGE(in9_input, LTC4245_GPIOADC);
|
||||
|
||||
/* Power Consumption (virtual) */
|
||||
LTC4245_POWER(power1_input, LTC4245_12VSENSE);
|
||||
@@ -342,8 +336,6 @@ static struct attribute *ltc4245_attributes[] = {
|
||||
&sensor_dev_attr_in8_min_alarm.dev_attr.attr,
|
||||
|
||||
&sensor_dev_attr_in9_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in10_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in11_input.dev_attr.attr,
|
||||
|
||||
&sensor_dev_attr_power1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_power2_input.dev_attr.attr,
|
||||
|
||||
@@ -0,0 +1,321 @@
|
||||
/* Texas Instruments TMP102 SMBus temperature sensor driver
|
||||
*
|
||||
* Copyright (C) 2010 Steven King <sfking@fdwdc.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., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
#define DRIVER_NAME "tmp102"
|
||||
|
||||
#define TMP102_TEMP_REG 0x00
|
||||
#define TMP102_CONF_REG 0x01
|
||||
/* note: these bit definitions are byte swapped */
|
||||
#define TMP102_CONF_SD 0x0100
|
||||
#define TMP102_CONF_TM 0x0200
|
||||
#define TMP102_CONF_POL 0x0400
|
||||
#define TMP102_CONF_F0 0x0800
|
||||
#define TMP102_CONF_F1 0x1000
|
||||
#define TMP102_CONF_R0 0x2000
|
||||
#define TMP102_CONF_R1 0x4000
|
||||
#define TMP102_CONF_OS 0x8000
|
||||
#define TMP102_CONF_EM 0x0010
|
||||
#define TMP102_CONF_AL 0x0020
|
||||
#define TMP102_CONF_CR0 0x0040
|
||||
#define TMP102_CONF_CR1 0x0080
|
||||
#define TMP102_TLOW_REG 0x02
|
||||
#define TMP102_THIGH_REG 0x03
|
||||
|
||||
struct tmp102 {
|
||||
struct device *hwmon_dev;
|
||||
struct mutex lock;
|
||||
u16 config_orig;
|
||||
unsigned long last_update;
|
||||
int temp[3];
|
||||
};
|
||||
|
||||
/* SMBus specifies low byte first, but the TMP102 returns high byte first,
|
||||
* so we have to swab16 the values */
|
||||
static inline int tmp102_read_reg(struct i2c_client *client, u8 reg)
|
||||
{
|
||||
int result = i2c_smbus_read_word_data(client, reg);
|
||||
return result < 0 ? result : swab16(result);
|
||||
}
|
||||
|
||||
static inline int tmp102_write_reg(struct i2c_client *client, u8 reg, u16 val)
|
||||
{
|
||||
return i2c_smbus_write_word_data(client, reg, swab16(val));
|
||||
}
|
||||
|
||||
/* convert left adjusted 13-bit TMP102 register value to milliCelsius */
|
||||
static inline int tmp102_reg_to_mC(s16 val)
|
||||
{
|
||||
return ((val & ~0x01) * 1000) / 128;
|
||||
}
|
||||
|
||||
/* convert milliCelsius to left adjusted 13-bit TMP102 register value */
|
||||
static inline u16 tmp102_mC_to_reg(int val)
|
||||
{
|
||||
return (val * 128) / 1000;
|
||||
}
|
||||
|
||||
static const u8 tmp102_reg[] = {
|
||||
TMP102_TEMP_REG,
|
||||
TMP102_TLOW_REG,
|
||||
TMP102_THIGH_REG,
|
||||
};
|
||||
|
||||
static struct tmp102 *tmp102_update_device(struct i2c_client *client)
|
||||
{
|
||||
struct tmp102 *tmp102 = i2c_get_clientdata(client);
|
||||
|
||||
mutex_lock(&tmp102->lock);
|
||||
if (time_after(jiffies, tmp102->last_update + HZ / 3)) {
|
||||
int i;
|
||||
for (i = 0; i < ARRAY_SIZE(tmp102->temp); ++i) {
|
||||
int status = tmp102_read_reg(client, tmp102_reg[i]);
|
||||
if (status > -1)
|
||||
tmp102->temp[i] = tmp102_reg_to_mC(status);
|
||||
}
|
||||
tmp102->last_update = jiffies;
|
||||
}
|
||||
mutex_unlock(&tmp102->lock);
|
||||
return tmp102;
|
||||
}
|
||||
|
||||
static ssize_t tmp102_show_temp(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *sda = to_sensor_dev_attr(attr);
|
||||
struct tmp102 *tmp102 = tmp102_update_device(to_i2c_client(dev));
|
||||
|
||||
return sprintf(buf, "%d\n", tmp102->temp[sda->index]);
|
||||
}
|
||||
|
||||
static ssize_t tmp102_set_temp(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct sensor_device_attribute *sda = to_sensor_dev_attr(attr);
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct tmp102 *tmp102 = i2c_get_clientdata(client);
|
||||
long val;
|
||||
int status;
|
||||
|
||||
if (strict_strtol(buf, 10, &val) < 0)
|
||||
return -EINVAL;
|
||||
val = SENSORS_LIMIT(val, -256000, 255000);
|
||||
|
||||
mutex_lock(&tmp102->lock);
|
||||
tmp102->temp[sda->index] = val;
|
||||
status = tmp102_write_reg(client, tmp102_reg[sda->index],
|
||||
tmp102_mC_to_reg(val));
|
||||
mutex_unlock(&tmp102->lock);
|
||||
return status ? : count;
|
||||
}
|
||||
|
||||
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, tmp102_show_temp, NULL , 0);
|
||||
|
||||
static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, tmp102_show_temp,
|
||||
tmp102_set_temp, 1);
|
||||
|
||||
static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, tmp102_show_temp,
|
||||
tmp102_set_temp, 2);
|
||||
|
||||
static struct attribute *tmp102_attributes[] = {
|
||||
&sensor_dev_attr_temp1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_max.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group tmp102_attr_group = {
|
||||
.attrs = tmp102_attributes,
|
||||
};
|
||||
|
||||
#define TMP102_CONFIG (TMP102_CONF_TM | TMP102_CONF_EM | TMP102_CONF_CR1)
|
||||
#define TMP102_CONFIG_RD_ONLY (TMP102_CONF_R0 | TMP102_CONF_R1 | TMP102_CONF_AL)
|
||||
|
||||
static int __devinit tmp102_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct tmp102 *tmp102;
|
||||
int status;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_WORD_DATA)) {
|
||||
dev_err(&client->dev, "adapter doesnt support SMBus word "
|
||||
"transactions\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
tmp102 = kzalloc(sizeof(*tmp102), GFP_KERNEL);
|
||||
if (!tmp102) {
|
||||
dev_dbg(&client->dev, "kzalloc failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
i2c_set_clientdata(client, tmp102);
|
||||
|
||||
status = tmp102_read_reg(client, TMP102_CONF_REG);
|
||||
if (status < 0) {
|
||||
dev_err(&client->dev, "error reading config register\n");
|
||||
goto fail_free;
|
||||
}
|
||||
tmp102->config_orig = status;
|
||||
status = tmp102_write_reg(client, TMP102_CONF_REG, TMP102_CONFIG);
|
||||
if (status < 0) {
|
||||
dev_err(&client->dev, "error writing config register\n");
|
||||
goto fail_restore_config;
|
||||
}
|
||||
status = tmp102_read_reg(client, TMP102_CONF_REG);
|
||||
if (status < 0) {
|
||||
dev_err(&client->dev, "error reading config register\n");
|
||||
goto fail_restore_config;
|
||||
}
|
||||
status &= ~TMP102_CONFIG_RD_ONLY;
|
||||
if (status != TMP102_CONFIG) {
|
||||
dev_err(&client->dev, "config settings did not stick\n");
|
||||
status = -ENODEV;
|
||||
goto fail_restore_config;
|
||||
}
|
||||
tmp102->last_update = jiffies - HZ;
|
||||
mutex_init(&tmp102->lock);
|
||||
|
||||
status = sysfs_create_group(&client->dev.kobj, &tmp102_attr_group);
|
||||
if (status) {
|
||||
dev_dbg(&client->dev, "could not create sysfs files\n");
|
||||
goto fail_restore_config;
|
||||
}
|
||||
tmp102->hwmon_dev = hwmon_device_register(&client->dev);
|
||||
if (IS_ERR(tmp102->hwmon_dev)) {
|
||||
dev_dbg(&client->dev, "unable to register hwmon device\n");
|
||||
status = PTR_ERR(tmp102->hwmon_dev);
|
||||
goto fail_remove_sysfs;
|
||||
}
|
||||
|
||||
dev_info(&client->dev, "initialized\n");
|
||||
|
||||
return 0;
|
||||
|
||||
fail_remove_sysfs:
|
||||
sysfs_remove_group(&client->dev.kobj, &tmp102_attr_group);
|
||||
fail_restore_config:
|
||||
tmp102_write_reg(client, TMP102_CONF_REG, tmp102->config_orig);
|
||||
fail_free:
|
||||
i2c_set_clientdata(client, NULL);
|
||||
kfree(tmp102);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int __devexit tmp102_remove(struct i2c_client *client)
|
||||
{
|
||||
struct tmp102 *tmp102 = i2c_get_clientdata(client);
|
||||
|
||||
hwmon_device_unregister(tmp102->hwmon_dev);
|
||||
sysfs_remove_group(&client->dev.kobj, &tmp102_attr_group);
|
||||
|
||||
/* Stop monitoring if device was stopped originally */
|
||||
if (tmp102->config_orig & TMP102_CONF_SD) {
|
||||
int config;
|
||||
|
||||
config = tmp102_read_reg(client, TMP102_CONF_REG);
|
||||
if (config >= 0)
|
||||
tmp102_write_reg(client, TMP102_CONF_REG,
|
||||
config | TMP102_CONF_SD);
|
||||
}
|
||||
|
||||
i2c_set_clientdata(client, NULL);
|
||||
kfree(tmp102);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int tmp102_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
int config;
|
||||
|
||||
config = tmp102_read_reg(client, TMP102_CONF_REG);
|
||||
if (config < 0)
|
||||
return config;
|
||||
|
||||
config |= TMP102_CONF_SD;
|
||||
return tmp102_write_reg(client, TMP102_CONF_REG, config);
|
||||
}
|
||||
|
||||
static int tmp102_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
int config;
|
||||
|
||||
config = tmp102_read_reg(client, TMP102_CONF_REG);
|
||||
if (config < 0)
|
||||
return config;
|
||||
|
||||
config &= ~TMP102_CONF_SD;
|
||||
return tmp102_write_reg(client, TMP102_CONF_REG, config);
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops tmp102_dev_pm_ops = {
|
||||
.suspend = tmp102_suspend,
|
||||
.resume = tmp102_resume,
|
||||
};
|
||||
|
||||
#define TMP102_DEV_PM_OPS (&tmp102_dev_pm_ops)
|
||||
#else
|
||||
#define TMP102_DEV_PM_OPS NULL
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static const struct i2c_device_id tmp102_id[] = {
|
||||
{ "tmp102", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, tmp102_id);
|
||||
|
||||
static struct i2c_driver tmp102_driver = {
|
||||
.driver.name = DRIVER_NAME,
|
||||
.driver.pm = TMP102_DEV_PM_OPS,
|
||||
.probe = tmp102_probe,
|
||||
.remove = __devexit_p(tmp102_remove),
|
||||
.id_table = tmp102_id,
|
||||
};
|
||||
|
||||
static int __init tmp102_init(void)
|
||||
{
|
||||
return i2c_add_driver(&tmp102_driver);
|
||||
}
|
||||
module_init(tmp102_init);
|
||||
|
||||
static void __exit tmp102_exit(void)
|
||||
{
|
||||
i2c_del_driver(&tmp102_driver);
|
||||
}
|
||||
module_exit(tmp102_exit);
|
||||
|
||||
MODULE_AUTHOR("Steven King <sfking@fdwdc.com>");
|
||||
MODULE_DESCRIPTION("Texas Instruments TMP102 temperature sensor driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
+125
-130
@@ -91,17 +91,6 @@ static const u8 TMP411_TEMP_HIGHEST_LSB[2] = { 0x33, 0x37 };
|
||||
#define TMP401_DEVICE_ID 0x11
|
||||
#define TMP411_DEVICE_ID 0x12
|
||||
|
||||
/*
|
||||
* Functions declarations
|
||||
*/
|
||||
|
||||
static int tmp401_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id);
|
||||
static int tmp401_detect(struct i2c_client *client,
|
||||
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)
|
||||
*/
|
||||
@@ -113,18 +102,6 @@ static const struct i2c_device_id tmp401_id[] = {
|
||||
};
|
||||
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_list = normal_i2c,
|
||||
};
|
||||
|
||||
/*
|
||||
* Client data (each client gets its own)
|
||||
*/
|
||||
@@ -194,6 +171,71 @@ static u8 tmp401_crit_temp_to_register(long temp, u8 config)
|
||||
return (temp + 500) / 1000;
|
||||
}
|
||||
|
||||
static struct tmp401_data *tmp401_update_device_reg16(
|
||||
struct i2c_client *client, struct tmp401_data *data)
|
||||
{
|
||||
int i;
|
||||
|
||||
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]);
|
||||
|
||||
if (data->kind == tmp411) {
|
||||
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]);
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
tmp401_update_device_reg16(client, data);
|
||||
|
||||
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 ssize_t show_temp_value(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
@@ -420,30 +462,36 @@ static ssize_t reset_temp_history(struct device *dev,
|
||||
}
|
||||
|
||||
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,
|
||||
SENSOR_ATTR(temp1_input, S_IRUGO, show_temp_value, NULL, 0),
|
||||
SENSOR_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp_min,
|
||||
store_temp_min, 0),
|
||||
SENSOR_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_max,
|
||||
store_temp_max, 0),
|
||||
SENSOR_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp_crit,
|
||||
store_temp_crit, 0),
|
||||
SENSOR_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temp_crit_hyst,
|
||||
store_temp_crit_hyst, 0),
|
||||
SENSOR_ATTR(temp1_min_alarm, 0444, show_status, NULL,
|
||||
SENSOR_ATTR(temp1_min_alarm, S_IRUGO, show_status, NULL,
|
||||
TMP401_STATUS_LOCAL_LOW),
|
||||
SENSOR_ATTR(temp1_max_alarm, 0444, show_status, NULL,
|
||||
SENSOR_ATTR(temp1_max_alarm, S_IRUGO, show_status, NULL,
|
||||
TMP401_STATUS_LOCAL_HIGH),
|
||||
SENSOR_ATTR(temp1_crit_alarm, 0444, show_status, NULL,
|
||||
SENSOR_ATTR(temp1_crit_alarm, S_IRUGO, 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,
|
||||
SENSOR_ATTR(temp2_input, S_IRUGO, show_temp_value, NULL, 1),
|
||||
SENSOR_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_temp_min,
|
||||
store_temp_min, 1),
|
||||
SENSOR_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp_max,
|
||||
store_temp_max, 1),
|
||||
SENSOR_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_temp_crit,
|
||||
store_temp_crit, 1),
|
||||
SENSOR_ATTR(temp2_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, 1),
|
||||
SENSOR_ATTR(temp2_fault, S_IRUGO, show_status, NULL,
|
||||
TMP401_STATUS_REMOTE_OPEN),
|
||||
SENSOR_ATTR(temp2_min_alarm, 0444, show_status, NULL,
|
||||
SENSOR_ATTR(temp2_min_alarm, S_IRUGO, show_status, NULL,
|
||||
TMP401_STATUS_REMOTE_LOW),
|
||||
SENSOR_ATTR(temp2_max_alarm, 0444, show_status, NULL,
|
||||
SENSOR_ATTR(temp2_max_alarm, S_IRUGO, show_status, NULL,
|
||||
TMP401_STATUS_REMOTE_HIGH),
|
||||
SENSOR_ATTR(temp2_crit_alarm, 0444, show_status, NULL,
|
||||
SENSOR_ATTR(temp2_crit_alarm, S_IRUGO, show_status, NULL,
|
||||
TMP401_STATUS_REMOTE_CRIT),
|
||||
};
|
||||
|
||||
@@ -455,11 +503,11 @@ static struct sensor_device_attribute tmp401_attr[] = {
|
||||
* and remote channels.
|
||||
*/
|
||||
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),
|
||||
SENSOR_ATTR(temp_reset_history, 0200, NULL, reset_temp_history, 0),
|
||||
SENSOR_ATTR(temp1_highest, S_IRUGO, show_temp_highest, NULL, 0),
|
||||
SENSOR_ATTR(temp1_lowest, S_IRUGO, show_temp_lowest, NULL, 0),
|
||||
SENSOR_ATTR(temp2_highest, S_IRUGO, show_temp_highest, NULL, 1),
|
||||
SENSOR_ATTR(temp2_lowest, S_IRUGO, show_temp_lowest, NULL, 1),
|
||||
SENSOR_ATTR(temp_reset_history, S_IWUSR, NULL, reset_temp_history, 0),
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -529,6 +577,27 @@ static int tmp401_detect(struct i2c_client *client,
|
||||
return 0;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (data->kind == tmp411) {
|
||||
for (i = 0; i < ARRAY_SIZE(tmp411_attr); i++)
|
||||
device_remove_file(&client->dev,
|
||||
&tmp411_attr[i].dev_attr);
|
||||
}
|
||||
|
||||
kfree(data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tmp401_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
@@ -581,91 +650,17 @@ exit_remove:
|
||||
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);
|
||||
|
||||
if (data->kind == tmp411) {
|
||||
for (i = 0; i < ARRAY_SIZE(tmp411_attr); i++)
|
||||
device_remove_file(&client->dev,
|
||||
&tmp411_attr[i].dev_attr);
|
||||
}
|
||||
|
||||
kfree(data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct tmp401_data *tmp401_update_device_reg16(
|
||||
struct i2c_client *client, struct tmp401_data *data)
|
||||
{
|
||||
int i;
|
||||
|
||||
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]);
|
||||
|
||||
if (data->kind == tmp411) {
|
||||
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]);
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
tmp401_update_device_reg16(client, data);
|
||||
|
||||
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 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_list = normal_i2c,
|
||||
};
|
||||
|
||||
static int __init tmp401_init(void)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user