summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorStephen Rothwell <sfr@canb.auug.org.au>2010-07-15 10:52:11 +1000
committerStephen Rothwell <sfr@canb.auug.org.au>2010-07-15 10:52:11 +1000
commit715c2aad65aaf5d5c08b417e3ad35db4b4f0c392 (patch)
tree790d0aa1a36cd3a6ddfc1f81bbcab2b5fb31f4a2 /drivers
parentafe16ec57d2571e561bdfc590283dc4c90d7bb8e (diff)
parent66b49a94a5c1676bc36466f592a188f92603584c (diff)
Merge branch 'quilt/jdelvare-hwmon'
Diffstat (limited to 'drivers')
-rw-r--r--drivers/hwmon/Kconfig10
-rw-r--r--drivers/hwmon/Makefile1
-rw-r--r--drivers/hwmon/asc7621.c9
-rw-r--r--drivers/hwmon/emc1403.c33
-rw-r--r--drivers/hwmon/emc2103.c740
-rw-r--r--drivers/hwmon/it87.c46
-rw-r--r--drivers/hwmon/lm75.c39
-rw-r--r--drivers/hwmon/lm75.h1
-rw-r--r--drivers/hwmon/ltc4245.c177
-rw-r--r--drivers/hwmon/pc87360.c30
-rw-r--r--drivers/hwmon/w83627ehf.c97
11 files changed, 1140 insertions, 43 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index e19cf8eb6ccf..2608e38470bf 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -761,6 +761,16 @@ config SENSORS_EMC1403
Threshold values can be configured using sysfs.
Data from the different diodes are accessible via sysfs.
+config SENSORS_EMC2103
+ tristate "SMSC EMC2103"
+ depends on I2C
+ help
+ If you say yes here you get support for the temperature
+ and fan sensors of the SMSC EMC2103 chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called emc2103.
+
config SENSORS_SMSC47M1
tristate "SMSC LPC47M10x and compatibles"
help
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 2138ceb1a713..b8e7e6587202 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -42,6 +42,7 @@ 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_EMC2103) += emc2103.o
obj-$(CONFIG_SENSORS_F71805F) += f71805f.o
obj-$(CONFIG_SENSORS_F71882FG) += f71882fg.o
obj-$(CONFIG_SENSORS_F75375S) += f75375s.o
diff --git a/drivers/hwmon/asc7621.c b/drivers/hwmon/asc7621.c
index 3b973f30b1f6..89b4f3babe87 100644
--- a/drivers/hwmon/asc7621.c
+++ b/drivers/hwmon/asc7621.c
@@ -1150,9 +1150,6 @@ static int asc7621_detect(struct i2c_client *client,
{
struct i2c_adapter *adapter = client->adapter;
int company, verstep, chip_index;
- struct device *dev;
-
- dev = &client->dev;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
@@ -1169,13 +1166,11 @@ static int asc7621_detect(struct i2c_client *client,
if (company == asc7621_chips[chip_index].company_id &&
verstep == asc7621_chips[chip_index].verstep_id) {
- strlcpy(client->name, asc7621_chips[chip_index].name,
- I2C_NAME_SIZE);
strlcpy(info->type, asc7621_chips[chip_index].name,
I2C_NAME_SIZE);
- dev_info(&adapter->dev, "Matched %s\n",
- asc7621_chips[chip_index].name);
+ dev_info(&adapter->dev, "Matched %s at 0x%02x\n",
+ asc7621_chips[chip_index].name, client->addr);
return 0;
}
}
diff --git a/drivers/hwmon/emc1403.c b/drivers/hwmon/emc1403.c
index 0e4b5642638d..5b58b20dead1 100644
--- a/drivers/hwmon/emc1403.c
+++ b/drivers/hwmon/emc1403.c
@@ -89,6 +89,35 @@ static ssize_t store_temp(struct device *dev,
return count;
}
+static ssize_t store_bit(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_2 *sda = to_sensor_dev_attr_2(attr);
+ unsigned long val;
+ int retval;
+
+ if (strict_strtoul(buf, 10, &val))
+ return -EINVAL;
+
+ mutex_lock(&data->mutex);
+ retval = i2c_smbus_read_byte_data(client, sda->nr);
+ if (retval < 0)
+ goto fail;
+
+ retval &= ~sda->index;
+ if (val)
+ retval |= sda->index;
+
+ retval = i2c_smbus_write_byte_data(client, sda->index, retval);
+ if (retval == 0)
+ retval = count;
+fail:
+ mutex_unlock(&data->mutex);
+ return retval;
+}
+
static ssize_t show_hyst(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -200,6 +229,9 @@ static SENSOR_DEVICE_ATTR_2(temp3_crit_alarm, S_IRUGO,
static SENSOR_DEVICE_ATTR(temp3_crit_hyst, S_IRUGO | S_IWUSR,
show_hyst, store_hyst, 0x1A);
+static SENSOR_DEVICE_ATTR_2(power_state, S_IRUGO | S_IWUSR,
+ show_bit, store_bit, 0x03, 0x40);
+
static struct attribute *mid_att_thermal[] = {
&sensor_dev_attr_temp1_min.dev_attr.attr,
&sensor_dev_attr_temp1_max.dev_attr.attr,
@@ -225,6 +257,7 @@ static struct attribute *mid_att_thermal[] = {
&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,
+ &sensor_dev_attr_power_state.dev_attr.attr,
NULL
};
diff --git a/drivers/hwmon/emc2103.c b/drivers/hwmon/emc2103.c
new file mode 100644
index 000000000000..af914ad93ece
--- /dev/null
+++ b/drivers/hwmon/emc2103.c
@@ -0,0 +1,740 @@
+/*
+ emc2103.c - Support for SMSC EMC2103
+ Copyright (c) 2010 SMSC
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/module.h>
+#include <linux/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>
+
+/* Addresses scanned */
+static const unsigned short normal_i2c[] = { 0x2E, I2C_CLIENT_END };
+
+static const u8 REG_TEMP[4] = { 0x00, 0x02, 0x04, 0x06 };
+static const u8 REG_TEMP_MIN[4] = { 0x3c, 0x38, 0x39, 0x3a };
+static const u8 REG_TEMP_MAX[4] = { 0x34, 0x30, 0x31, 0x32 };
+
+#define REG_CONF1 0x20
+#define REG_TEMP_MAX_ALARM 0x24
+#define REG_TEMP_MIN_ALARM 0x25
+#define REG_FAN_CONF1 0x42
+#define REG_FAN_TARGET_LO 0x4c
+#define REG_FAN_TARGET_HI 0x4d
+#define REG_FAN_TACH_HI 0x4e
+#define REG_FAN_TACH_LO 0x4f
+#define REG_PRODUCT_ID 0xfd
+#define REG_MFG_ID 0xfe
+
+/* equation 4 from datasheet: rpm = (3932160 * multipler) / count */
+#define FAN_RPM_FACTOR 3932160
+
+/* 2103-2 and 2103-4's 3rd temperature sensor can be connected to two diodes
+ * in anti-parallel mode, and in this configuration both can be read
+ * independently (so we have 4 temperature inputs). The device can't
+ * detect if it's connected in this mode, so we have to manually enable
+ * it. Default is to leave the device in the state it's already in (-1).
+ * This parameter allows APD mode to be optionally forced on or off */
+static int apd = -1;
+module_param(apd, bool, 0);
+MODULE_PARM_DESC(init, "Set to zero to disable anti-parallel diode mode");
+
+struct temperature {
+ s8 degrees;
+ u8 fraction; /* 0-7 multiples of 0.125 */
+};
+
+struct emc2103_data {
+ struct device *hwmon_dev;
+ struct mutex update_lock;
+ bool valid; /* registers are valid */
+ bool fan_rpm_control;
+ int temp_count; /* num of temp sensors */
+ unsigned long last_updated; /* in jiffies */
+ struct temperature temp[4]; /* internal + 3 external */
+ s8 temp_min[4]; /* no fractional part */
+ s8 temp_max[4]; /* no fractional part */
+ u8 temp_min_alarm;
+ u8 temp_max_alarm;
+ u8 fan_multiplier;
+ u16 fan_tach;
+ u16 fan_target;
+};
+
+static int read_u8_from_i2c(struct i2c_client *client, u8 i2c_reg, u8 *output)
+{
+ int status = i2c_smbus_read_byte_data(client, i2c_reg);
+ if (status < 0) {
+ dev_warn(&client->dev, "reg 0x%02x, err %d\n",
+ i2c_reg, status);
+ } else {
+ *output = status;
+ }
+ return status;
+}
+
+static void read_temp_from_i2c(struct i2c_client *client, u8 i2c_reg,
+ struct temperature *temp)
+{
+ u8 degrees, fractional;
+
+ if (read_u8_from_i2c(client, i2c_reg, &degrees) < 0)
+ return;
+
+ if (read_u8_from_i2c(client, i2c_reg + 1, &fractional) < 0)
+ return;
+
+ temp->degrees = degrees;
+ temp->fraction = (fractional & 0xe0) >> 5;
+}
+
+static void read_fan_from_i2c(struct i2c_client *client, u16 *output,
+ u8 hi_addr, u8 lo_addr)
+{
+ u8 high_byte, lo_byte;
+
+ if (read_u8_from_i2c(client, hi_addr, &high_byte) < 0)
+ return;
+
+ if (read_u8_from_i2c(client, lo_addr, &lo_byte) < 0)
+ return;
+
+ *output = ((u16)high_byte << 5) | (lo_byte >> 3);
+}
+
+static void write_fan_target_to_i2c(struct i2c_client *client, u16 new_target)
+{
+ u8 high_byte = (new_target & 0x1fe0) >> 5;
+ u8 low_byte = (new_target & 0x001f) << 3;
+ i2c_smbus_write_byte_data(client, REG_FAN_TARGET_LO, low_byte);
+ i2c_smbus_write_byte_data(client, REG_FAN_TARGET_HI, high_byte);
+}
+
+static void read_fan_config_from_i2c(struct i2c_client *client)
+
+{
+ struct emc2103_data *data = i2c_get_clientdata(client);
+ u8 conf1;
+
+ if (read_u8_from_i2c(client, REG_FAN_CONF1, &conf1) < 0)
+ return;
+
+ data->fan_multiplier = 1 << ((conf1 & 0x60) >> 5);
+ data->fan_rpm_control = (conf1 & 0x80) != 0;
+}
+
+static struct emc2103_data *emc2103_update_device(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct emc2103_data *data = i2c_get_clientdata(client);
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
+ || !data->valid) {
+ int i;
+
+ for (i = 0; i < data->temp_count; i++) {
+ read_temp_from_i2c(client, REG_TEMP[i], &data->temp[i]);
+ read_u8_from_i2c(client, REG_TEMP_MIN[i],
+ &data->temp_min[i]);
+ read_u8_from_i2c(client, REG_TEMP_MAX[i],
+ &data->temp_max[i]);
+ }
+
+ read_u8_from_i2c(client, REG_TEMP_MIN_ALARM,
+ &data->temp_min_alarm);
+ read_u8_from_i2c(client, REG_TEMP_MAX_ALARM,
+ &data->temp_max_alarm);
+
+ read_fan_from_i2c(client, &data->fan_tach,
+ REG_FAN_TACH_HI, REG_FAN_TACH_LO);
+ read_fan_from_i2c(client, &data->fan_target,
+ REG_FAN_TARGET_HI, REG_FAN_TARGET_LO);
+ read_fan_config_from_i2c(client);
+
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+
+ mutex_unlock(&data->update_lock);
+
+ return data;
+}
+
+static ssize_t
+show_temp(struct device *dev, struct device_attribute *da, char *buf)
+{
+ int nr = to_sensor_dev_attr(da)->index;
+ struct emc2103_data *data = emc2103_update_device(dev);
+ int millidegrees = data->temp[nr].degrees * 1000
+ + data->temp[nr].fraction * 125;
+ return sprintf(buf, "%d\n", millidegrees);
+}
+
+static ssize_t
+show_temp_min(struct device *dev, struct device_attribute *da, char *buf)
+{
+ int nr = to_sensor_dev_attr(da)->index;
+ struct emc2103_data *data = emc2103_update_device(dev);
+ int millidegrees = data->temp_min[nr] * 1000;
+ return sprintf(buf, "%d\n", millidegrees);
+}
+
+static ssize_t
+show_temp_max(struct device *dev, struct device_attribute *da, char *buf)
+{
+ int nr = to_sensor_dev_attr(da)->index;
+ struct emc2103_data *data = emc2103_update_device(dev);
+ int millidegrees = data->temp_max[nr] * 1000;
+ return sprintf(buf, "%d\n", millidegrees);
+}
+
+static ssize_t
+show_temp_fault(struct device *dev, struct device_attribute *da, char *buf)
+{
+ int nr = to_sensor_dev_attr(da)->index;
+ struct emc2103_data *data = emc2103_update_device(dev);
+ bool fault = (data->temp[nr].degrees == -128);
+ return sprintf(buf, "%d\n", fault ? 1 : 0);
+}
+
+static ssize_t
+show_temp_min_alarm(struct device *dev, struct device_attribute *da, char *buf)
+{
+ int nr = to_sensor_dev_attr(da)->index;
+ struct emc2103_data *data = emc2103_update_device(dev);
+ bool alarm = data->temp_min_alarm & (1 << nr);
+ return sprintf(buf, "%d\n", alarm ? 1 : 0);
+}
+
+static ssize_t
+show_temp_max_alarm(struct device *dev, struct device_attribute *da, char *buf)
+{
+ int nr = to_sensor_dev_attr(da)->index;
+ struct emc2103_data *data = emc2103_update_device(dev);
+ bool alarm = data->temp_max_alarm & (1 << nr);
+ return sprintf(buf, "%d\n", alarm ? 1 : 0);
+}
+
+static ssize_t set_temp_min(struct device *dev, struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ int nr = to_sensor_dev_attr(da)->index;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct emc2103_data *data = i2c_get_clientdata(client);
+ long val;
+
+ int result = strict_strtol(buf, 10, &val);
+ if (result < 0)
+ return -EINVAL;
+
+ val = DIV_ROUND_CLOSEST(val, 1000);
+ if ((val < -63) || (val > 127))
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ data->temp_min[nr] = val;
+ i2c_smbus_write_byte_data(client, REG_TEMP_MIN[nr], val);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t set_temp_max(struct device *dev, struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ int nr = to_sensor_dev_attr(da)->index;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct emc2103_data *data = i2c_get_clientdata(client);
+ long val;
+
+ int result = strict_strtol(buf, 10, &val);
+ if (result < 0)
+ return -EINVAL;
+
+ val = DIV_ROUND_CLOSEST(val, 1000);
+ if ((val < -63) || (val > 127))
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ data->temp_max[nr] = val;
+ i2c_smbus_write_byte_data(client, REG_TEMP_MAX[nr], val);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t
+show_fan(struct device *dev, struct device_attribute *da, char *buf)
+{
+ struct emc2103_data *data = emc2103_update_device(dev);
+ int rpm = 0;
+ if (data->fan_tach != 0)
+ rpm = (FAN_RPM_FACTOR * data->fan_multiplier) / data->fan_tach;
+ return sprintf(buf, "%d\n", rpm);
+}
+
+static ssize_t
+show_fan_div(struct device *dev, struct device_attribute *da, char *buf)
+{
+ struct emc2103_data *data = emc2103_update_device(dev);
+ int fan_div = 8 / data->fan_multiplier;
+ return sprintf(buf, "%d\n", fan_div);
+}
+
+/* Note: we also update the fan target here, because its value is
+ determined in part by the fan clock divider. This follows the principle
+ of least surprise; the user doesn't expect the fan target to change just
+ because the divider changed. */
+static ssize_t set_fan_div(struct device *dev, struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ struct emc2103_data *data = emc2103_update_device(dev);
+ struct i2c_client *client = to_i2c_client(dev);
+ int new_range_bits, old_div = 8 / data->fan_multiplier;
+ long new_div;
+
+ int status = strict_strtol(buf, 10, &new_div);
+ if (status < 0)
+ return -EINVAL;
+
+ if (new_div == old_div) /* No change */
+ return count;
+
+ switch (new_div) {
+ case 1:
+ new_range_bits = 3;
+ break;
+ case 2:
+ new_range_bits = 2;
+ break;
+ case 4:
+ new_range_bits = 1;
+ break;
+ case 8:
+ new_range_bits = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ mutex_lock(&data->update_lock);
+
+ status = i2c_smbus_read_byte_data(client, REG_FAN_CONF1);
+ if (status < 0) {
+ dev_dbg(&client->dev, "reg 0x%02x, err %d\n",
+ REG_FAN_CONF1, status);
+ mutex_unlock(&data->update_lock);
+ return -EIO;
+ }
+ status &= 0x9F;
+ status |= (new_range_bits << 5);
+ i2c_smbus_write_byte_data(client, REG_FAN_CONF1, status);
+
+ data->fan_multiplier = 8 / new_div;
+
+ /* update fan target if high byte is not disabled */
+ if ((data->fan_target & 0x1fe0) != 0x1fe0) {
+ u16 new_target = (data->fan_target * old_div) / new_div;
+ data->fan_target = min(new_target, (u16)0x1fff);
+ write_fan_target_to_i2c(client, data->fan_target);
+ }
+
+ /* invalidate data to force re-read from hardware */
+ data->valid = false;
+
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t
+show_fan_target(struct device *dev, struct device_attribute *da, char *buf)
+{
+ struct emc2103_data *data = emc2103_update_device(dev);
+ int rpm = 0;
+
+ /* high byte of 0xff indicates disabled so return 0 */
+ if ((data->fan_target != 0) && ((data->fan_target & 0x1fe0) != 0x1fe0))
+ rpm = (FAN_RPM_FACTOR * data->fan_multiplier)
+ / data->fan_target;
+
+ return sprintf(buf, "%d\n", rpm);
+}
+
+static ssize_t set_fan_target(struct device *dev, struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ struct emc2103_data *data = emc2103_update_device(dev);
+ struct i2c_client *client = to_i2c_client(dev);
+ long rpm_target;
+
+ int result = strict_strtol(buf, 10, &rpm_target);
+ if (result < 0)
+ return -EINVAL;
+
+ /* Datasheet states 16384 as maximum RPM target (table 3.2) */
+ if ((rpm_target < 0) || (rpm_target > 16384))
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+
+ if (rpm_target == 0)
+ data->fan_target = 0x1fff;
+ else
+ data->fan_target = SENSORS_LIMIT(
+ (FAN_RPM_FACTOR * data->fan_multiplier) / rpm_target,
+ 0, 0x1fff);
+
+ write_fan_target_to_i2c(client, data->fan_target);
+
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t
+show_fan_fault(struct device *dev, struct device_attribute *da, char *buf)
+{
+ struct emc2103_data *data = emc2103_update_device(dev);
+ bool fault = ((data->fan_tach & 0x1fe0) == 0x1fe0);
+ return sprintf(buf, "%d\n", fault ? 1 : 0);
+}
+
+static ssize_t
+show_pwm_enable(struct device *dev, struct device_attribute *da, char *buf)
+{
+ struct emc2103_data *data = emc2103_update_device(dev);
+ return sprintf(buf, "%d\n", data->fan_rpm_control ? 3 : 0);
+}
+
+static ssize_t set_pwm_enable(struct device *dev, struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct emc2103_data *data = i2c_get_clientdata(client);
+ long new_value;
+ u8 conf_reg;
+
+ int result = strict_strtol(buf, 10, &new_value);
+ if (result < 0)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ switch (new_value) {
+ case 0:
+ data->fan_rpm_control = false;
+ break;
+ case 3:
+ data->fan_rpm_control = true;
+ break;
+ default:
+ mutex_unlock(&data->update_lock);
+ return -EINVAL;
+ }
+
+ read_u8_from_i2c(client, REG_FAN_CONF1, &conf_reg);
+
+ if (data->fan_rpm_control)
+ conf_reg |= 0x80;
+ else
+ conf_reg &= ~0x80;
+
+ i2c_smbus_write_byte_data(client, REG_FAN_CONF1, conf_reg);
+
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_min, S_IRUGO | S_IWUSR, show_temp_min,
+ set_temp_min, 0);
+static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR, show_temp_max,
+ set_temp_max, 0);
+static SENSOR_DEVICE_ATTR(temp1_fault, S_IRUGO, show_temp_fault, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_temp_min_alarm,
+ NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_temp_max_alarm,
+ NULL, 0);
+
+static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp2_min, S_IRUGO | S_IWUSR, show_temp_min,
+ set_temp_min, 1);
+static SENSOR_DEVICE_ATTR(temp2_max, S_IRUGO | S_IWUSR, show_temp_max,
+ set_temp_max, 1);
+static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_temp_fault, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO, show_temp_min_alarm,
+ NULL, 1);
+static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_temp_max_alarm,
+ NULL, 1);
+
+static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp3_min, S_IRUGO | S_IWUSR, show_temp_min,
+ set_temp_min, 2);
+static SENSOR_DEVICE_ATTR(temp3_max, S_IRUGO | S_IWUSR, show_temp_max,
+ set_temp_max, 2);
+static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_temp_fault, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp3_min_alarm, S_IRUGO, show_temp_min_alarm,
+ NULL, 2);
+static SENSOR_DEVICE_ATTR(temp3_max_alarm, S_IRUGO, show_temp_max_alarm,
+ NULL, 2);
+
+static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp4_min, S_IRUGO | S_IWUSR, show_temp_min,
+ set_temp_min, 3);
+static SENSOR_DEVICE_ATTR(temp4_max, S_IRUGO | S_IWUSR, show_temp_max,
+ set_temp_max, 3);
+static SENSOR_DEVICE_ATTR(temp4_fault, S_IRUGO, show_temp_fault, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp4_min_alarm, S_IRUGO, show_temp_min_alarm,
+ NULL, 3);
+static SENSOR_DEVICE_ATTR(temp4_max_alarm, S_IRUGO, show_temp_max_alarm,
+ NULL, 3);
+
+static DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL);
+static DEVICE_ATTR(fan1_div, S_IRUGO | S_IWUSR, show_fan_div, set_fan_div);
+static DEVICE_ATTR(fan1_target, S_IRUGO | S_IWUSR, show_fan_target,
+ set_fan_target);
+static DEVICE_ATTR(fan1_fault, S_IRUGO, show_fan_fault, NULL);
+
+static DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, show_pwm_enable,
+ set_pwm_enable);
+
+/* sensors present on all models */
+static struct attribute *emc2103_attributes[] = {
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_min.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_fault.dev_attr.attr,
+ &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_min.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_fault.dev_attr.attr,
+ &sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
+ &dev_attr_fan1_input.attr,
+ &dev_attr_fan1_div.attr,
+ &dev_attr_fan1_target.attr,
+ &dev_attr_fan1_fault.attr,
+ &dev_attr_pwm1_enable.attr,
+ NULL
+};
+
+/* extra temperature sensors only present on 2103-2 and 2103-4 */
+static struct attribute *emc2103_attributes_temp3[] = {
+ &sensor_dev_attr_temp3_input.dev_attr.attr,
+ &sensor_dev_attr_temp3_min.dev_attr.attr,
+ &sensor_dev_attr_temp3_max.dev_attr.attr,
+ &sensor_dev_attr_temp3_fault.dev_attr.attr,
+ &sensor_dev_attr_temp3_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
+ NULL
+};
+
+/* extra temperature sensors only present on 2103-2 and 2103-4 in APD mode */
+static struct attribute *emc2103_attributes_temp4[] = {
+ &sensor_dev_attr_temp4_input.dev_attr.attr,
+ &sensor_dev_attr_temp4_min.dev_attr.attr,
+ &sensor_dev_attr_temp4_max.dev_attr.attr,
+ &sensor_dev_attr_temp4_fault.dev_attr.attr,
+ &sensor_dev_attr_temp4_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp4_max_alarm.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group emc2103_group = {
+ .attrs = emc2103_attributes,
+};
+
+static const struct attribute_group emc2103_temp3_group = {
+ .attrs = emc2103_attributes_temp3,
+};
+
+static const struct attribute_group emc2103_temp4_group = {
+ .attrs = emc2103_attributes_temp4,
+};
+
+static int
+emc2103_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct emc2103_data *data;
+ int status;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ data = kzalloc(sizeof(struct emc2103_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, data);
+ mutex_init(&data->update_lock);
+
+ /* 2103-2 and 2103-4 have 3 external diodes, 2103-1 has 1 */
+ status = i2c_smbus_read_byte_data(client, REG_PRODUCT_ID);
+ if (status == 0x24) {
+ /* 2103-1 only has 1 external diode */
+ data->temp_count = 2;
+ } else {
+ /* 2103-2 and 2103-4 have 3 or 4 external diodes */
+ status = i2c_smbus_read_byte_data(client, REG_CONF1);
+ if (status < 0) {
+ dev_dbg(&client->dev, "reg 0x%02x, err %d\n", REG_CONF1,
+ status);
+ goto exit_free;
+ }
+
+ /* detect current state of hardware */
+ data->temp_count = (status & 0x01) ? 4 : 3;
+
+ /* force APD state if module parameter is set */
+ if (apd == 0) {
+ /* force APD mode off */
+ data->temp_count = 3;
+ status &= ~(0x01);
+ i2c_smbus_write_byte_data(client, REG_CONF1, status);
+ } else if (apd == 1) {
+ /* force APD mode on */
+ data->temp_count = 4;
+ status |= 0x01;
+ i2c_smbus_write_byte_data(client, REG_CONF1, status);
+ }
+ }
+
+ /* Register sysfs hooks */
+ status = sysfs_create_group(&client->dev.kobj, &emc2103_group);
+ if (status)
+ goto exit_free;
+
+ if (data->temp_count >= 3) {
+ status = sysfs_create_group(&client->dev.kobj,
+ &emc2103_temp3_group);
+ if (status)
+ goto exit_remove;
+ }
+
+ if (data->temp_count == 4) {
+ status = sysfs_create_group(&client->dev.kobj,
+ &emc2103_temp4_group);
+ if (status)
+ goto exit_remove_temp3;
+ }
+
+ data->hwmon_dev = hwmon_device_register(&client->dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ status = PTR_ERR(data->hwmon_dev);
+ goto exit_remove_temp4;
+ }
+
+ dev_info(&client->dev, "%s: sensor '%s'\n",
+ dev_name(data->hwmon_dev), client->name);
+
+ return 0;
+
+exit_remove_temp4:
+ if (data->temp_count == 4)
+ sysfs_remove_group(&client->dev.kobj, &emc2103_temp4_group);
+exit_remove_temp3:
+ if (data->temp_count >= 3)
+ sysfs_remove_group(&client->dev.kobj, &emc2103_temp3_group);
+exit_remove:
+ sysfs_remove_group(&client->dev.kobj, &emc2103_group);
+exit_free:
+ kfree(data);
+ return status;
+}
+
+static int emc2103_remove(struct i2c_client *client)
+{
+ struct emc2103_data *data = i2c_get_clientdata(client);
+
+ hwmon_device_unregister(data->hwmon_dev);
+
+ if (data->temp_count == 4)
+ sysfs_remove_group(&client->dev.kobj, &emc2103_temp4_group);
+
+ if (data->temp_count >= 3)
+ sysfs_remove_group(&client->dev.kobj, &emc2103_temp3_group);
+
+ sysfs_remove_group(&client->dev.kobj, &emc2103_group);
+
+ kfree(data);
+ return 0;
+}
+
+static const struct i2c_device_id emc2103_ids[] = {
+ { "emc2103", 0, },
+ { /* LIST END */ }
+};
+MODULE_DEVICE_TABLE(i2c, emc2103_ids);
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int
+emc2103_detect(struct i2c_client *new_client, struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = new_client->adapter;
+ int manufacturer, product;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ manufacturer = i2c_smbus_read_byte_data(new_client, REG_MFG_ID);
+ if (manufacturer != 0x5D)
+ return -ENODEV;
+
+ product = i2c_smbus_read_byte_data(new_client, REG_PRODUCT_ID);
+ if ((product != 0x24) && (product != 0x26))
+ return -ENODEV;
+
+ strlcpy(info->type, "emc2103", I2C_NAME_SIZE);
+
+ return 0;
+}
+
+static struct i2c_driver emc2103_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "emc2103",
+ },
+ .probe = emc2103_probe,
+ .remove = emc2103_remove,
+ .id_table = emc2103_ids,
+ .detect = emc2103_detect,
+ .address_list = normal_i2c,
+};
+
+static int __init sensors_emc2103_init(void)
+{
+ return i2c_add_driver(&emc2103_driver);
+}
+
+static void __exit sensors_emc2103_exit(void)
+{
+ i2c_del_driver(&emc2103_driver);
+}
+
+MODULE_AUTHOR("Steve Glendinning <steve.glendinning@smsc.com>");
+MODULE_DESCRIPTION("SMSC EMC2103 hwmon driver");
+MODULE_LICENSE("GPL");
+
+module_init(sensors_emc2103_init);
+module_exit(sensors_emc2103_exit);
diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c
index 25763d2223b6..f7701295937d 100644
--- a/drivers/hwmon/it87.c
+++ b/drivers/hwmon/it87.c
@@ -259,6 +259,7 @@ struct it87_sio_data {
u8 revision;
u8 vid_value;
u8 beep_pin;
+ u8 internal; /* Internal sensors can be labeled */
/* Features skipped based on config or DMI */
u8 skip_vid;
u8 skip_fan;
@@ -1194,6 +1195,22 @@ static ssize_t show_vid_reg(struct device *dev, struct device_attribute *attr,
}
static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL);
+static ssize_t show_label(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ static const char *labels[] = {
+ "+5V",
+ "5VSB",
+ "Vbat",
+ };
+ int nr = to_sensor_dev_attr(attr)->index;
+
+ return sprintf(buf, "%s\n", labels[nr]);
+}
+static SENSOR_DEVICE_ATTR(in3_label, S_IRUGO, show_label, NULL, 0);
+static SENSOR_DEVICE_ATTR(in7_label, S_IRUGO, show_label, NULL, 1);
+static SENSOR_DEVICE_ATTR(in8_label, S_IRUGO, show_label, NULL, 2);
+
static ssize_t show_name(struct device *dev, struct device_attribute
*devattr, char *buf)
{
@@ -1434,6 +1451,17 @@ static const struct attribute_group it87_group_vid = {
.attrs = it87_attributes_vid,
};
+static struct attribute *it87_attributes_label[] = {
+ &sensor_dev_attr_in3_label.dev_attr.attr,
+ &sensor_dev_attr_in7_label.dev_attr.attr,
+ &sensor_dev_attr_in8_label.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group it87_group_label = {
+ .attrs = it87_attributes_vid,
+};
+
/* SuperIO detection - will change isa_address if a chip is found */
static int __init it87_find(unsigned short *address,
struct it87_sio_data *sio_data)
@@ -1487,6 +1515,9 @@ static int __init it87_find(unsigned short *address,
pr_info("it87: Found IT%04xF chip at 0x%x, revision %d\n",
chip_type, *address, sio_data->revision);
+ /* in8 (Vbat) is always internal */
+ sio_data->internal = (1 << 2);
+
/* Read GPIO config and VID value from LDN 7 (GPIO) */
if (sio_data->type == it87) {
/* The IT8705F doesn't have VID pins at all */
@@ -1540,9 +1571,9 @@ static int __init it87_find(unsigned short *address,
pr_notice("it87: Routing internal VCCH to in7\n");
}
if (reg & (1 << 0))
- pr_info("it87: in3 is VCC (+5V)\n");
+ sio_data->internal |= (1 << 0);
if (reg & (1 << 1))
- pr_info("it87: in7 is VCCH (+5V Stand-By)\n");
+ sio_data->internal |= (1 << 1);
sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f;
}
@@ -1600,6 +1631,7 @@ static void it87_remove_files(struct device *dev)
}
if (!sio_data->skip_vid)
sysfs_remove_group(&dev->kobj, &it87_group_vid);
+ sysfs_remove_group(&dev->kobj, &it87_group_label);
}
static int __devinit it87_probe(struct platform_device *pdev)
@@ -1725,6 +1757,16 @@ static int __devinit it87_probe(struct platform_device *pdev)
goto ERROR4;
}
+ /* Export labels for internal sensors */
+ for (i = 0; i < 3; i++) {
+ if (!(sio_data->internal & (1 << i)))
+ continue;
+ err = sysfs_create_file(&dev->kobj,
+ it87_attributes_label[i]);
+ if (err)
+ goto ERROR4;
+ }
+
data->hwmon_dev = hwmon_device_register(dev);
if (IS_ERR(data->hwmon_dev)) {
err = PTR_ERR(data->hwmon_dev);
diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c
index 393f354f92a4..ab5b87a81677 100644
--- a/drivers/hwmon/lm75.c
+++ b/drivers/hwmon/lm75.c
@@ -280,10 +280,49 @@ static int lm75_detect(struct i2c_client *new_client,
return 0;
}
+#ifdef CONFIG_PM
+static int lm75_suspend(struct device *dev)
+{
+ int status;
+ struct i2c_client *client = to_i2c_client(dev);
+ status = lm75_read_value(client, LM75_REG_CONF);
+ if (status < 0) {
+ dev_dbg(&client->dev, "Can't read config? %d\n", status);
+ return status;
+ }
+ status = status | LM75_SHUTDOWN;
+ lm75_write_value(client, LM75_REG_CONF, status);
+ return 0;
+}
+
+static int lm75_resume(struct device *dev)
+{
+ int status;
+ struct i2c_client *client = to_i2c_client(dev);
+ status = lm75_read_value(client, LM75_REG_CONF);
+ if (status < 0) {
+ dev_dbg(&client->dev, "Can't read config? %d\n", status);
+ return status;
+ }
+ status = status & ~LM75_SHUTDOWN;
+ lm75_write_value(client, LM75_REG_CONF, status);
+ return 0;
+}
+
+static const struct dev_pm_ops lm75_dev_pm_ops = {
+ .suspend = lm75_suspend,
+ .resume = lm75_resume,
+};
+#define LM75_DEV_PM_OPS (&lm75_dev_pm_ops)
+#else
+#define LM75_DEV_PM_OPS NULL
+#endif /* CONFIG_PM */
+
static struct i2c_driver lm75_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = "lm75",
+ .pm = LM75_DEV_PM_OPS,
},
.probe = lm75_probe,
.remove = lm75_remove,
diff --git a/drivers/hwmon/lm75.h b/drivers/hwmon/lm75.h
index 7c93454bb4e3..e547a3eb4de3 100644
--- a/drivers/hwmon/lm75.h
+++ b/drivers/hwmon/lm75.h
@@ -30,6 +30,7 @@
/* straight from the datasheet */
#define LM75_TEMP_MIN (-55000)
#define LM75_TEMP_MAX 125000
+#define LM75_SHUTDOWN 0x01
/* TEMP: 0.001C/bit (-55C to +125C)
REG: (0.5C/bit, two's complement) << 7 */
diff --git a/drivers/hwmon/ltc4245.c b/drivers/hwmon/ltc4245.c
index 21d201befc2c..659308329308 100644
--- a/drivers/hwmon/ltc4245.c
+++ b/drivers/hwmon/ltc4245.c
@@ -21,6 +21,7 @@
#include <linux/i2c.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
+#include <linux/i2c/ltc4245.h>
/* Here are names of the chip's registers (a.k.a. commands) */
enum ltc4245_cmd {
@@ -60,8 +61,72 @@ struct ltc4245_data {
/* Voltage registers */
u8 vregs[0x0d];
+
+ /* GPIO ADC registers */
+ bool use_extra_gpios;
+ int gpios[3];
};
+/*
+ * Update the readings from the GPIO pins. If the driver has been configured to
+ * sample all GPIO's as analog voltages, a round-robin sampling method is used.
+ * Otherwise, only the configured GPIO pin is sampled.
+ *
+ * LOCKING: must hold data->update_lock
+ */
+static void ltc4245_update_gpios(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct ltc4245_data *data = i2c_get_clientdata(client);
+ u8 gpio_curr, gpio_next, gpio_reg;
+ int i;
+
+ /* no extra gpio support, we're basically done */
+ if (!data->use_extra_gpios) {
+ data->gpios[0] = data->vregs[LTC4245_GPIOADC - 0x10];
+ return;
+ }
+
+ /*
+ * If the last reading was too long ago, then we mark all old GPIO
+ * readings as stale by setting them to -EAGAIN
+ */
+ if (time_after(jiffies, data->last_updated + 5 * HZ)) {
+ dev_dbg(&client->dev, "Marking GPIOs invalid\n");
+ for (i = 0; i < ARRAY_SIZE(data->gpios); i++)
+ data->gpios[i] = -EAGAIN;
+ }
+
+ /*
+ * Get the current GPIO pin
+ *
+ * The datasheet calls these GPIO[1-3], but we'll calculate the zero
+ * based array index instead, and call them GPIO[0-2]. This is much
+ * easier to think about.
+ */
+ gpio_curr = (data->cregs[LTC4245_GPIO] & 0xc0) >> 6;
+ if (gpio_curr > 0)
+ gpio_curr -= 1;
+
+ /* Read the GPIO voltage from the GPIOADC register */
+ data->gpios[gpio_curr] = data->vregs[LTC4245_GPIOADC - 0x10];
+
+ /* Find the next GPIO pin to read */
+ gpio_next = (gpio_curr + 1) % ARRAY_SIZE(data->gpios);
+
+ /*
+ * Calculate the correct setting for the GPIO register so it will
+ * sample the next GPIO pin
+ */
+ gpio_reg = (data->cregs[LTC4245_GPIO] & 0x3f) | ((gpio_next + 1) << 6);
+
+ /* Update the GPIO register */
+ i2c_smbus_write_byte_data(client, LTC4245_GPIO, gpio_reg);
+
+ /* Update saved data */
+ data->cregs[LTC4245_GPIO] = gpio_reg;
+}
+
static struct ltc4245_data *ltc4245_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
@@ -93,6 +158,9 @@ static struct ltc4245_data *ltc4245_update_device(struct device *dev)
data->vregs[i] = val;
}
+ /* Update GPIO readings */
+ ltc4245_update_gpios(dev);
+
data->last_updated = jiffies;
data->valid = 1;
}
@@ -233,6 +301,22 @@ static ssize_t ltc4245_show_alarm(struct device *dev,
return snprintf(buf, PAGE_SIZE, "%u\n", (reg & mask) ? 1 : 0);
}
+static ssize_t ltc4245_show_gpio(struct device *dev,
+ struct device_attribute *da,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct ltc4245_data *data = ltc4245_update_device(dev);
+ int val = data->gpios[attr->index];
+
+ /* handle stale GPIO's */
+ if (val < 0)
+ return val;
+
+ /* Convert to millivolts and print */
+ return snprintf(buf, PAGE_SIZE, "%u\n", val * 10);
+}
+
/* These macros are used below in constructing device attribute objects
* for use with sysfs_create_group() to make a sysfs device file
* for each register.
@@ -254,6 +338,10 @@ static ssize_t ltc4245_show_alarm(struct device *dev,
static SENSOR_DEVICE_ATTR_2(name, S_IRUGO, \
ltc4245_show_alarm, NULL, (mask), reg)
+#define LTC4245_GPIO_VOLTAGE(name, gpio_num) \
+ static SENSOR_DEVICE_ATTR(name, S_IRUGO, \
+ ltc4245_show_gpio, NULL, gpio_num)
+
/* Construct a sensor_device_attribute structure for each register */
/* Input voltages */
@@ -293,7 +381,9 @@ 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_GPIOADC);
+LTC4245_GPIO_VOLTAGE(in9_input, 0);
+LTC4245_GPIO_VOLTAGE(in10_input, 1);
+LTC4245_GPIO_VOLTAGE(in11_input, 2);
/* Power Consumption (virtual) */
LTC4245_POWER(power1_input, LTC4245_12VSENSE);
@@ -304,7 +394,7 @@ LTC4245_POWER(power4_input, LTC4245_VEESENSE);
/* Finally, construct an array of pointers to members of the above objects,
* as required for sysfs_create_group()
*/
-static struct attribute *ltc4245_attributes[] = {
+static struct attribute *ltc4245_std_attributes[] = {
&sensor_dev_attr_in1_input.dev_attr.attr,
&sensor_dev_attr_in2_input.dev_attr.attr,
&sensor_dev_attr_in3_input.dev_attr.attr,
@@ -345,10 +435,77 @@ static struct attribute *ltc4245_attributes[] = {
NULL,
};
-static const struct attribute_group ltc4245_group = {
- .attrs = ltc4245_attributes,
+static struct attribute *ltc4245_gpio_attributes[] = {
+ &sensor_dev_attr_in10_input.dev_attr.attr,
+ &sensor_dev_attr_in11_input.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ltc4245_std_group = {
+ .attrs = ltc4245_std_attributes,
+};
+
+static const struct attribute_group ltc4245_gpio_group = {
+ .attrs = ltc4245_gpio_attributes,
};
+static int ltc4245_sysfs_create_groups(struct i2c_client *client)
+{
+ struct ltc4245_data *data = i2c_get_clientdata(client);
+ struct device *dev = &client->dev;
+ int ret;
+
+ /* register the standard sysfs attributes */
+ ret = sysfs_create_group(&dev->kobj, &ltc4245_std_group);
+ if (ret) {
+ dev_err(dev, "unable to register standard attributes\n");
+ return ret;
+ }
+
+ /* if we're using the extra gpio support, register it's attributes */
+ if (data->use_extra_gpios) {
+ ret = sysfs_create_group(&dev->kobj, &ltc4245_gpio_group);
+ if (ret) {
+ dev_err(dev, "unable to register gpio attributes\n");
+ sysfs_remove_group(&dev->kobj, &ltc4245_std_group);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void ltc4245_sysfs_remove_groups(struct i2c_client *client)
+{
+ struct ltc4245_data *data = i2c_get_clientdata(client);
+ struct device *dev = &client->dev;
+
+ if (data->use_extra_gpios)
+ sysfs_remove_group(&dev->kobj, &ltc4245_gpio_group);
+
+ sysfs_remove_group(&dev->kobj, &ltc4245_std_group);
+}
+
+static bool ltc4245_use_extra_gpios(struct i2c_client *client)
+{
+ struct ltc4245_platform_data *pdata = dev_get_platdata(&client->dev);
+#ifdef CONFIG_OF
+ struct device_node *np = client->dev.of_node;
+#endif
+
+ /* prefer platform data */
+ if (pdata)
+ return pdata->use_extra_gpios;
+
+#ifdef CONFIG_OF
+ /* fallback on OF */
+ if (of_find_property(np, "ltc4245,use-extra-gpios", NULL))
+ return true;
+#endif
+
+ return false;
+}
+
static int ltc4245_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -367,15 +524,16 @@ static int ltc4245_probe(struct i2c_client *client,
i2c_set_clientdata(client, data);
mutex_init(&data->update_lock);
+ data->use_extra_gpios = ltc4245_use_extra_gpios(client);
/* Initialize the LTC4245 chip */
i2c_smbus_write_byte_data(client, LTC4245_FAULT1, 0x00);
i2c_smbus_write_byte_data(client, LTC4245_FAULT2, 0x00);
/* Register sysfs hooks */
- ret = sysfs_create_group(&client->dev.kobj, &ltc4245_group);
+ ret = ltc4245_sysfs_create_groups(client);
if (ret)
- goto out_sysfs_create_group;
+ goto out_sysfs_create_groups;
data->hwmon_dev = hwmon_device_register(&client->dev);
if (IS_ERR(data->hwmon_dev)) {
@@ -386,8 +544,8 @@ static int ltc4245_probe(struct i2c_client *client,
return 0;
out_hwmon_device_register:
- sysfs_remove_group(&client->dev.kobj, &ltc4245_group);
-out_sysfs_create_group:
+ ltc4245_sysfs_remove_groups(client);
+out_sysfs_create_groups:
kfree(data);
out_kzalloc:
return ret;
@@ -398,8 +556,7 @@ static int ltc4245_remove(struct i2c_client *client)
struct ltc4245_data *data = i2c_get_clientdata(client);
hwmon_device_unregister(data->hwmon_dev);
- sysfs_remove_group(&client->dev.kobj, &ltc4245_group);
-
+ ltc4245_sysfs_remove_groups(client);
kfree(data);
return 0;
diff --git a/drivers/hwmon/pc87360.c b/drivers/hwmon/pc87360.c
index 4a64b85d4ec9..20d63f7872aa 100644
--- a/drivers/hwmon/pc87360.c
+++ b/drivers/hwmon/pc87360.c
@@ -1610,11 +1610,8 @@ static struct pc87360_data *pc87360_update_device(struct device *dev)
static int __init pc87360_device_add(unsigned short address)
{
- struct resource res = {
- .name = "pc87360",
- .flags = IORESOURCE_IO,
- };
- int err, i;
+ struct resource res[3];
+ int err, i, res_count;
pdev = platform_device_alloc("pc87360", address);
if (!pdev) {
@@ -1623,22 +1620,27 @@ static int __init pc87360_device_add(unsigned short address)
goto exit;
}
+ memset(res, 0, 3 * sizeof(struct resource));
for (i = 0; i < 3; i++) {
if (!extra_isa[i])
continue;
- res.start = extra_isa[i];
- res.end = extra_isa[i] + PC87360_EXTENT - 1;
+ res[res_count].start = extra_isa[i];
+ res[res_count].end = extra_isa[i] + PC87360_EXTENT - 1;
+ res[res_count].name = "pc87360",
+ res[res_count].flags = IORESOURCE_IO,
- err = acpi_check_resource_conflict(&res);
+ err = acpi_check_resource_conflict(&res[res_count]);
if (err)
goto exit_device_put;
- err = platform_device_add_resources(pdev, &res, 1);
- if (err) {
- printk(KERN_ERR "pc87360: Device resource[%d] "
- "addition failed (%d)\n", i, err);
- goto exit_device_put;
- }
+ res_count++;
+ }
+
+ err = platform_device_add_resources(pdev, res, res_count);
+ if (err) {
+ printk(KERN_ERR "pc87360: Device resources addition failed "
+ "(%d)\n", err);
+ goto exit_device_put;
}
err = platform_device_add(pdev);
diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c
index 0dcaba9b7189..e96e69dd36fb 100644
--- a/drivers/hwmon/w83627ehf.c
+++ b/drivers/hwmon/w83627ehf.c
@@ -39,6 +39,7 @@
w83627dhg 9 5 4 3 0xa020 0xc1 0x5ca3
w83627dhg-p 9 5 4 3 0xb070 0xc1 0x5ca3
w83667hg 9 5 3 3 0xa510 0xc1 0x5ca3
+ w83667hg-b 9 5 3 3 0xb350 0xc1 0x5ca3
*/
#include <linux/module.h>
@@ -55,7 +56,7 @@
#include <linux/io.h>
#include "lm75.h"
-enum kinds { w83627ehf, w83627dhg, w83627dhg_p, w83667hg };
+enum kinds { w83627ehf, w83627dhg, w83627dhg_p, w83667hg, w83667hg_b };
/* used to set data->name = w83627ehf_device_names[data->sio_kind] */
static const char * w83627ehf_device_names[] = {
@@ -63,6 +64,7 @@ static const char * w83627ehf_device_names[] = {
"w83627dhg",
"w83627dhg",
"w83667hg",
+ "w83667hg",
};
static unsigned short force_id;
@@ -91,6 +93,7 @@ MODULE_PARM_DESC(force_id, "Override the detected device ID");
#define SIO_W83627DHG_ID 0xa020
#define SIO_W83627DHG_P_ID 0xb070
#define SIO_W83667HG_ID 0xa510
+#define SIO_W83667HG_B_ID 0xb350
#define SIO_ID_MASK 0xFFF0
static inline void
@@ -201,8 +204,14 @@ static const u8 W83627EHF_REG_TOLERANCE[] = { 0x07, 0x07, 0x14, 0x62 };
static const u8 W83627EHF_REG_FAN_START_OUTPUT[] = { 0x0a, 0x0b, 0x16, 0x65 };
static const u8 W83627EHF_REG_FAN_STOP_OUTPUT[] = { 0x08, 0x09, 0x15, 0x64 };
static const u8 W83627EHF_REG_FAN_STOP_TIME[] = { 0x0c, 0x0d, 0x17, 0x66 };
-static const u8 W83627EHF_REG_FAN_MAX_OUTPUT[] = { 0xff, 0x67, 0xff, 0x69 };
-static const u8 W83627EHF_REG_FAN_STEP_OUTPUT[] = { 0xff, 0x68, 0xff, 0x6a };
+
+static const u8 W83627EHF_REG_FAN_MAX_OUTPUT_COMMON[]
+ = { 0xff, 0x67, 0xff, 0x69 };
+static const u8 W83627EHF_REG_FAN_STEP_OUTPUT_COMMON[]
+ = { 0xff, 0x68, 0xff, 0x6a };
+
+static const u8 W83627EHF_REG_FAN_MAX_OUTPUT_W83667_B[] = { 0x67, 0x69, 0x6b };
+static const u8 W83627EHF_REG_FAN_STEP_OUTPUT_W83667_B[] = { 0x68, 0x6a, 0x6c };
/*
* Conversions
@@ -277,6 +286,11 @@ struct w83627ehf_data {
struct device *hwmon_dev;
struct mutex lock;
+ const u8 *REG_FAN_START_OUTPUT;
+ const u8 *REG_FAN_STOP_OUTPUT;
+ const u8 *REG_FAN_MAX_OUTPUT;
+ const u8 *REG_FAN_STEP_OUTPUT;
+
struct mutex update_lock;
char valid; /* !=0 if following fields are valid */
unsigned long last_updated; /* In jiffies */
@@ -524,7 +538,10 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev)
}
}
- for (i = 0; i < 4; i++) {
+ for (i = 0; i < data->pwm_num; i++) {
+ if (!(data->has_fan & (1 << i)))
+ continue;
+
/* pwmcfg, tolerance mapped for i=0, i=1 to same reg */
if (i != 1) {
pwmcfg = w83627ehf_read_value(data,
@@ -546,6 +563,17 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev)
W83627EHF_REG_FAN_STOP_OUTPUT[i]);
data->fan_stop_time[i] = w83627ehf_read_value(data,
W83627EHF_REG_FAN_STOP_TIME[i]);
+
+ if (data->REG_FAN_MAX_OUTPUT[i] != 0xff)
+ data->fan_max_output[i] =
+ w83627ehf_read_value(data,
+ data->REG_FAN_MAX_OUTPUT[i]);
+
+ if (data->REG_FAN_STEP_OUTPUT[i] != 0xff)
+ data->fan_step_output[i] =
+ w83627ehf_read_value(data,
+ data->REG_FAN_STEP_OUTPUT[i]);
+
data->target_temp[i] =
w83627ehf_read_value(data,
W83627EHF_REG_TARGET[i]) &
@@ -1126,7 +1154,7 @@ store_##reg(struct device *dev, struct device_attribute *attr, \
u32 val = SENSORS_LIMIT(simple_strtoul(buf, NULL, 10), 1, 255); \
mutex_lock(&data->update_lock); \
data->reg[nr] = val; \
- w83627ehf_write_value(data, W83627EHF_REG_##REG[nr], val); \
+ w83627ehf_write_value(data, data->REG_##REG[nr], val); \
mutex_unlock(&data->update_lock); \
return count; \
}
@@ -1206,12 +1234,26 @@ static struct sensor_device_attribute sda_sf3_arrays[] = {
store_fan_stop_output, 1),
SENSOR_ATTR(pwm3_stop_output, S_IWUSR | S_IRUGO, show_fan_stop_output,
store_fan_stop_output, 2),
+};
- /* pwm1 and pwm3 don't support max and step settings */
+
+/*
+ * pwm1 and pwm3 don't support max and step settings on all chips.
+ * Need to check support while generating/removing attribute files.
+ */
+static struct sensor_device_attribute sda_sf3_max_step_arrays[] = {
+ SENSOR_ATTR(pwm1_max_output, S_IWUSR | S_IRUGO, show_fan_max_output,
+ store_fan_max_output, 0),
+ SENSOR_ATTR(pwm1_step_output, S_IWUSR | S_IRUGO, show_fan_step_output,
+ store_fan_step_output, 0),
SENSOR_ATTR(pwm2_max_output, S_IWUSR | S_IRUGO, show_fan_max_output,
store_fan_max_output, 1),
SENSOR_ATTR(pwm2_step_output, S_IWUSR | S_IRUGO, show_fan_step_output,
store_fan_step_output, 1),
+ SENSOR_ATTR(pwm3_max_output, S_IWUSR | S_IRUGO, show_fan_max_output,
+ store_fan_max_output, 2),
+ SENSOR_ATTR(pwm3_step_output, S_IWUSR | S_IRUGO, show_fan_step_output,
+ store_fan_step_output, 2),
};
static ssize_t
@@ -1235,6 +1277,12 @@ static void w83627ehf_device_remove_files(struct device *dev)
for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays); i++)
device_remove_file(dev, &sda_sf3_arrays[i].dev_attr);
+ for (i = 0; i < ARRAY_SIZE(sda_sf3_max_step_arrays); i++) {
+ struct sensor_device_attribute *attr =
+ &sda_sf3_max_step_arrays[i];
+ if (data->REG_FAN_STEP_OUTPUT[attr->index] != 0xff)
+ device_remove_file(dev, &attr->dev_attr);
+ }
for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++)
device_remove_file(dev, &sda_sf3_arrays_fan4[i].dev_attr);
for (i = 0; i < data->in_num; i++) {
@@ -1343,22 +1391,37 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
/* 627EHG and 627EHF have 10 voltage inputs; 627DHG and 667HG have 9 */
data->in_num = (sio_data->kind == w83627ehf) ? 10 : 9;
/* 667HG has 3 pwms */
- data->pwm_num = (sio_data->kind == w83667hg) ? 3 : 4;
+ data->pwm_num = (sio_data->kind == w83667hg
+ || sio_data->kind == w83667hg_b) ? 3 : 4;
/* Check temp3 configuration bit for 667HG */
- if (sio_data->kind == w83667hg) {
+ if (sio_data->kind == w83667hg || sio_data->kind == w83667hg_b) {
data->temp3_disable = w83627ehf_read_value(data,
W83627EHF_REG_TEMP_CONFIG[1]) & 0x01;
data->in6_skip = !data->temp3_disable;
}
+ data->REG_FAN_START_OUTPUT = W83627EHF_REG_FAN_START_OUTPUT;
+ data->REG_FAN_STOP_OUTPUT = W83627EHF_REG_FAN_STOP_OUTPUT;
+ if (sio_data->kind == w83667hg_b) {
+ data->REG_FAN_MAX_OUTPUT =
+ W83627EHF_REG_FAN_MAX_OUTPUT_W83667_B;
+ data->REG_FAN_STEP_OUTPUT =
+ W83627EHF_REG_FAN_STEP_OUTPUT_W83667_B;
+ } else {
+ data->REG_FAN_MAX_OUTPUT =
+ W83627EHF_REG_FAN_MAX_OUTPUT_COMMON;
+ data->REG_FAN_STEP_OUTPUT =
+ W83627EHF_REG_FAN_STEP_OUTPUT_COMMON;
+ }
+
/* Initialize the chip */
w83627ehf_init_device(data);
data->vrm = vid_which_vrm();
superio_enter(sio_data->sioreg);
/* Read VID value */
- if (sio_data->kind == w83667hg) {
+ if (sio_data->kind == w83667hg || sio_data->kind == w83667hg_b) {
/* W83667HG has different pins for VID input and output, so
we can get the VID input values directly at logical device D
0xe3. */
@@ -1409,7 +1472,7 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
}
/* fan4 and fan5 share some pins with the GPIO and serial flash */
- if (sio_data->kind == w83667hg) {
+ if (sio_data->kind == w83667hg || sio_data->kind == w83667hg_b) {
fan5pin = superio_inb(sio_data->sioreg, 0x27) & 0x20;
fan4pin = superio_inb(sio_data->sioreg, 0x27) & 0x40;
} else {
@@ -1440,6 +1503,15 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
&sda_sf3_arrays[i].dev_attr)))
goto exit_remove;
+ for (i = 0; i < ARRAY_SIZE(sda_sf3_max_step_arrays); i++) {
+ struct sensor_device_attribute *attr =
+ &sda_sf3_max_step_arrays[i];
+ if (data->REG_FAN_STEP_OUTPUT[attr->index] != 0xff) {
+ err = device_create_file(dev, &attr->dev_attr);
+ if (err)
+ goto exit_remove;
+ }
+ }
/* if fan4 is enabled create the sf3 files for it */
if ((data->has_fan & (1 << 3)) && data->pwm_num >= 4)
for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++) {
@@ -1556,6 +1628,7 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr,
static const char __initdata sio_name_W83627DHG[] = "W83627DHG";
static const char __initdata sio_name_W83627DHG_P[] = "W83627DHG-P";
static const char __initdata sio_name_W83667HG[] = "W83667HG";
+ static const char __initdata sio_name_W83667HG_B[] = "W83667HG-B";
u16 val;
const char *sio_name;
@@ -1588,6 +1661,10 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr,
sio_data->kind = w83667hg;
sio_name = sio_name_W83667HG;
break;
+ case SIO_W83667HG_B_ID:
+ sio_data->kind = w83667hg_b;
+ sio_name = sio_name_W83667HG_B;
+ break;
default:
if (val != 0xffff)
pr_debug(DRVNAME ": unsupported chip ID: 0x%04x\n",