summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2011-05-23 09:26:20 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2011-05-23 09:26:20 -0700
commit6fad2b5b649fa1fa6ee7293222815f5b62499889 (patch)
treec2f9735e8868d3ce5decb5a08c56e1a1cc9730af /drivers
parent19504828b4bee5e471bcd35e214bc6fd0d380692 (diff)
parent4cc452758fd250bb5968c583d825bb0e68d65db0 (diff)
downloadlinux-6fad2b5b649fa1fa6ee7293222815f5b62499889.tar.bz2
Merge branch 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/staging
* 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/staging: hwmon: (coretemp) Fix checkpatch errors hwmon: Remove pkgtemp driver hwmon: (coretemp) Merge pkgtemp with coretemp hwmon: (pmbus) Add support for Analog Devices ADM1275 hwmon: (pmbus) Support for TI UCD90xxx series Sequencer and System Health Controllers hwmon: (pmbus) Add support for TI UCD9200 series of PWM System Controllers hwmon: (pmbus) Use device specific function to read fan configuration hwmon: (pmbus) Expand scope of device specific get_status function hwmon: (pmbus) Introduce infrastructure to detect sensors and limit registers hwmon: Driver for MAX16065 System Manager and compatibles hwmon: (sht15) add support for CRC validation hwmon: (sht15) add support for the status register hwmon: (sht15) clean-up the probe function hwmon: (sht15) general code clean-up hwmon: Add support for MAX6642
Diffstat (limited to 'drivers')
-rw-r--r--drivers/hwmon/Kconfig66
-rw-r--r--drivers/hwmon/Makefile6
-rw-r--r--drivers/hwmon/adm1275.c121
-rw-r--r--drivers/hwmon/coretemp.c722
-rw-r--r--drivers/hwmon/max16065.c717
-rw-r--r--drivers/hwmon/max34440.c6
-rw-r--r--drivers/hwmon/max6642.c356
-rw-r--r--drivers/hwmon/max8688.c4
-rw-r--r--drivers/hwmon/pkgtemp.c444
-rw-r--r--drivers/hwmon/pmbus.h10
-rw-r--r--drivers/hwmon/pmbus_core.c970
-rw-r--r--drivers/hwmon/sht15.c747
-rw-r--r--drivers/hwmon/ucd9000.c278
-rw-r--r--drivers/hwmon/ucd9200.c210
14 files changed, 3262 insertions, 1395 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 50e40dbd8bb6..43221beb9e97 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -408,13 +408,6 @@ config SENSORS_CORETEMP
sensor inside your CPU. Most of the family 6 CPUs
are supported. Check Documentation/hwmon/coretemp for details.
-config SENSORS_PKGTEMP
- tristate "Intel processor package temperature sensor"
- depends on X86 && EXPERIMENTAL
- help
- If you say yes here you get support for the package level temperature
- sensor inside your CPU. Check documentation/driver for details.
-
config SENSORS_IBMAEM
tristate "IBM Active Energy Manager temperature/power sensors and control"
select IPMI_SI
@@ -708,6 +701,22 @@ config SENSORS_MAX1111
This driver can also be built as a module. If so, the module
will be called max1111.
+config SENSORS_MAX16065
+ tristate "Maxim MAX16065 System Manager and compatibles"
+ depends on I2C
+ help
+ If you say yes here you get support for hardware monitoring
+ capabilities of the following Maxim System Manager chips.
+ MAX16065
+ MAX16066
+ MAX16067
+ MAX16068
+ MAX16070
+ MAX16071
+
+ This driver can also be built as a module. If so, the module
+ will be called max16065.
+
config SENSORS_MAX1619
tristate "Maxim MAX1619 sensor chip"
depends on I2C
@@ -727,6 +736,17 @@ config SENSORS_MAX6639
This driver can also be built as a module. If so, the module
will be called max6639.
+config SENSORS_MAX6642
+ tristate "Maxim MAX6642 sensor chip"
+ depends on I2C && EXPERIMENTAL
+ help
+ If you say yes here you get support for MAX6642 sensor chip.
+ MAX6642 is a SMBus-Compatible Remote/Local Temperature Sensor
+ with Overtemperature Alarm from Maxim.
+
+ This driver can also be built as a module. If so, the module
+ will be called max6642.
+
config SENSORS_MAX6650
tristate "Maxim MAX6650 sensor chip"
depends on I2C && EXPERIMENTAL
@@ -800,6 +820,16 @@ config SENSORS_PMBUS
This driver can also be built as a module. If so, the module will
be called pmbus.
+config SENSORS_ADM1275
+ tristate "Analog Devices ADM1275"
+ default n
+ help
+ If you say yes here you get hardware monitoring support for Analog
+ Devices ADM1275 Hot-Swap Controller and Digital Power Monitor.
+
+ This driver can also be built as a module. If so, the module will
+ be called adm1275.
+
config SENSORS_MAX16064
tristate "Maxim MAX16064"
default n
@@ -830,6 +860,28 @@ config SENSORS_MAX8688
This driver can also be built as a module. If so, the module will
be called max8688.
+config SENSORS_UCD9000
+ tristate "TI UCD90120, UCD90124, UCD9090, UCD90910"
+ default n
+ help
+ If you say yes here you get hardware monitoring support for TI
+ UCD90120, UCD90124, UCD9090, UCD90910 Sequencer and System Health
+ Controllers.
+
+ This driver can also be built as a module. If so, the module will
+ be called ucd9000.
+
+config SENSORS_UCD9200
+ tristate "TI UCD9220, UCD9222, UCD9224, UCD9240, UCD9244, UCD9246, UCD9248"
+ default n
+ help
+ If you say yes here you get hardware monitoring support for TI
+ UCD9220, UCD9222, UCD9224, UCD9240, UCD9244, UCD9246, and UCD9248
+ Digital PWM System Controllers.
+
+ This driver can also be built as a module. If so, the module will
+ be called ucd9200.
+
endif # PMBUS
config SENSORS_SHT15
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 967d0ea9447f..28e8d52f6379 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -40,7 +40,6 @@ obj-$(CONFIG_SENSORS_APPLESMC) += applesmc.o
obj-$(CONFIG_SENSORS_ASC7621) += asc7621.o
obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o
obj-$(CONFIG_SENSORS_CORETEMP) += coretemp.o
-obj-$(CONFIG_SENSORS_PKGTEMP) += pkgtemp.o
obj-$(CONFIG_SENSORS_DME1737) += dme1737.o
obj-$(CONFIG_SENSORS_DS620) += ds620.o
obj-$(CONFIG_SENSORS_DS1621) += ds1621.o
@@ -83,8 +82,10 @@ obj-$(CONFIG_SENSORS_LTC4215) += ltc4215.o
obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o
obj-$(CONFIG_SENSORS_LTC4261) += ltc4261.o
obj-$(CONFIG_SENSORS_MAX1111) += max1111.o
+obj-$(CONFIG_SENSORS_MAX16065) += max16065.o
obj-$(CONFIG_SENSORS_MAX1619) += max1619.o
obj-$(CONFIG_SENSORS_MAX6639) += max6639.o
+obj-$(CONFIG_SENSORS_MAX6642) += max6642.o
obj-$(CONFIG_SENSORS_MAX6650) += max6650.o
obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
@@ -118,9 +119,12 @@ obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o
# PMBus drivers
obj-$(CONFIG_PMBUS) += pmbus_core.o
obj-$(CONFIG_SENSORS_PMBUS) += pmbus.o
+obj-$(CONFIG_SENSORS_ADM1275) += adm1275.o
obj-$(CONFIG_SENSORS_MAX16064) += max16064.o
obj-$(CONFIG_SENSORS_MAX34440) += max34440.o
obj-$(CONFIG_SENSORS_MAX8688) += max8688.o
+obj-$(CONFIG_SENSORS_UCD9000) += ucd9000.o
+obj-$(CONFIG_SENSORS_UCD9200) += ucd9200.o
ccflags-$(CONFIG_HWMON_DEBUG_CHIP) := -DDEBUG
diff --git a/drivers/hwmon/adm1275.c b/drivers/hwmon/adm1275.c
new file mode 100644
index 000000000000..c2ee2048ab91
--- /dev/null
+++ b/drivers/hwmon/adm1275.c
@@ -0,0 +1,121 @@
+/*
+ * Hardware monitoring driver for Analog Devices ADM1275 Hot-Swap Controller
+ * and Digital Power Monitor
+ *
+ * Copyright (c) 2011 Ericsson AB.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include "pmbus.h"
+
+#define ADM1275_PMON_CONFIG 0xd4
+
+#define ADM1275_VIN_VOUT_SELECT (1 << 6)
+#define ADM1275_VRANGE (1 << 5)
+
+static int adm1275_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int config;
+ struct pmbus_driver_info *info;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_BYTE_DATA))
+ return -ENODEV;
+
+ info = kzalloc(sizeof(struct pmbus_driver_info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ config = i2c_smbus_read_byte_data(client, ADM1275_PMON_CONFIG);
+ if (config < 0)
+ return config;
+
+ info->pages = 1;
+ info->direct[PSC_VOLTAGE_IN] = true;
+ info->direct[PSC_VOLTAGE_OUT] = true;
+ info->direct[PSC_CURRENT_OUT] = true;
+ info->m[PSC_CURRENT_OUT] = 800;
+ info->b[PSC_CURRENT_OUT] = 20475;
+ info->R[PSC_CURRENT_OUT] = -1;
+ info->func[0] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT;
+
+ if (config & ADM1275_VRANGE) {
+ info->m[PSC_VOLTAGE_IN] = 19045;
+ info->b[PSC_VOLTAGE_IN] = 0;
+ info->R[PSC_VOLTAGE_IN] = -2;
+ info->m[PSC_VOLTAGE_OUT] = 19045;
+ info->b[PSC_VOLTAGE_OUT] = 0;
+ info->R[PSC_VOLTAGE_OUT] = -2;
+ } else {
+ info->m[PSC_VOLTAGE_IN] = 6666;
+ info->b[PSC_VOLTAGE_IN] = 0;
+ info->R[PSC_VOLTAGE_IN] = -1;
+ info->m[PSC_VOLTAGE_OUT] = 6666;
+ info->b[PSC_VOLTAGE_OUT] = 0;
+ info->R[PSC_VOLTAGE_OUT] = -1;
+ }
+
+ if (config & ADM1275_VIN_VOUT_SELECT)
+ info->func[0] |= PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;
+ else
+ info->func[0] |= PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT;
+
+ return pmbus_do_probe(client, id, info);
+}
+
+static int adm1275_remove(struct i2c_client *client)
+{
+ const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+ int ret;
+
+ ret = pmbus_do_remove(client);
+ kfree(info);
+ return ret;
+}
+
+static const struct i2c_device_id adm1275_id[] = {
+ {"adm1275", 0},
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adm1275_id);
+
+static struct i2c_driver adm1275_driver = {
+ .driver = {
+ .name = "adm1275",
+ },
+ .probe = adm1275_probe,
+ .remove = adm1275_remove,
+ .id_table = adm1275_id,
+};
+
+static int __init adm1275_init(void)
+{
+ return i2c_add_driver(&adm1275_driver);
+}
+
+static void __exit adm1275_exit(void)
+{
+ i2c_del_driver(&adm1275_driver);
+}
+
+MODULE_AUTHOR("Guenter Roeck");
+MODULE_DESCRIPTION("PMBus driver for Analog Devices ADM1275");
+MODULE_LICENSE("GPL");
+module_init(adm1275_init);
+module_exit(adm1275_exit);
diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c
index 194ca0aa8b0c..5c7cd60d5f9d 100644
--- a/drivers/hwmon/coretemp.c
+++ b/drivers/hwmon/coretemp.c
@@ -35,128 +35,152 @@
#include <linux/platform_device.h>
#include <linux/cpu.h>
#include <linux/pci.h>
+#include <linux/smp.h>
#include <asm/msr.h>
#include <asm/processor.h>
-#include <asm/smp.h>
#define DRVNAME "coretemp"
-typedef enum { SHOW_TEMP, SHOW_TJMAX, SHOW_TTARGET, SHOW_LABEL,
- SHOW_NAME } SHOW;
+#define BASE_SYSFS_ATTR_NO 2 /* Sysfs Base attr no for coretemp */
+#define NUM_REAL_CORES 16 /* Number of Real cores per cpu */
+#define CORETEMP_NAME_LENGTH 17 /* String Length of attrs */
+#define MAX_ATTRS 5 /* Maximum no of per-core attrs */
+#define MAX_CORE_DATA (NUM_REAL_CORES + BASE_SYSFS_ATTR_NO)
+
+#ifdef CONFIG_SMP
+#define TO_PHYS_ID(cpu) cpu_data(cpu).phys_proc_id
+#define TO_CORE_ID(cpu) cpu_data(cpu).cpu_core_id
+#define TO_ATTR_NO(cpu) (TO_CORE_ID(cpu) + BASE_SYSFS_ATTR_NO)
+#else
+#define TO_PHYS_ID(cpu) (cpu)
+#define TO_CORE_ID(cpu) (cpu)
+#define TO_ATTR_NO(cpu) (cpu)
+#endif
/*
- * Functions declaration
+ * Per-Core Temperature Data
+ * @last_updated: The time when the current temperature value was updated
+ * earlier (in jiffies).
+ * @cpu_core_id: The CPU Core from which temperature values should be read
+ * This value is passed as "id" field to rdmsr/wrmsr functions.
+ * @status_reg: One of IA32_THERM_STATUS or IA32_PACKAGE_THERM_STATUS,
+ * from where the temperature values should be read.
+ * @is_pkg_data: If this is 1, the temp_data holds pkgtemp data.
+ * Otherwise, temp_data holds coretemp data.
+ * @valid: If this is 1, the current temperature is valid.
*/
-
-static struct coretemp_data *coretemp_update_device(struct device *dev);
-
-struct coretemp_data {
- struct device *hwmon_dev;
- struct mutex update_lock;
- const char *name;
- u32 id;
- u16 core_id;
- char valid; /* zero until following fields are valid */
- unsigned long last_updated; /* in jiffies */
+struct temp_data {
int temp;
- int tjmax;
int ttarget;
- u8 alarm;
+ int tjmax;
+ unsigned long last_updated;
+ unsigned int cpu;
+ u32 cpu_core_id;
+ u32 status_reg;
+ bool is_pkg_data;
+ bool valid;
+ struct sensor_device_attribute sd_attrs[MAX_ATTRS];
+ char attr_name[MAX_ATTRS][CORETEMP_NAME_LENGTH];
+ struct mutex update_lock;
};
-/*
- * Sysfs stuff
- */
+/* Platform Data per Physical CPU */
+struct platform_data {
+ struct device *hwmon_dev;
+ u16 phys_proc_id;
+ struct temp_data *core_data[MAX_CORE_DATA];
+ struct device_attribute name_attr;
+};
-static ssize_t show_name(struct device *dev, struct device_attribute
- *devattr, char *buf)
+struct pdev_entry {
+ struct list_head list;
+ struct platform_device *pdev;
+ unsigned int cpu;
+ u16 phys_proc_id;
+ u16 cpu_core_id;
+};
+
+static LIST_HEAD(pdev_list);
+static DEFINE_MUTEX(pdev_list_mutex);
+
+static ssize_t show_name(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ return sprintf(buf, "%s\n", DRVNAME);
+}
+
+static ssize_t show_label(struct device *dev,
+ struct device_attribute *devattr, char *buf)
{
- int ret;
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- struct coretemp_data *data = dev_get_drvdata(dev);
+ struct platform_data *pdata = dev_get_drvdata(dev);
+ struct temp_data *tdata = pdata->core_data[attr->index];
- if (attr->index == SHOW_NAME)
- ret = sprintf(buf, "%s\n", data->name);
- else /* show label */
- ret = sprintf(buf, "Core %d\n", data->core_id);
- return ret;
+ if (tdata->is_pkg_data)
+ return sprintf(buf, "Physical id %u\n", pdata->phys_proc_id);
+
+ return sprintf(buf, "Core %u\n", tdata->cpu_core_id);
}
-static ssize_t show_alarm(struct device *dev, struct device_attribute
- *devattr, char *buf)
+static ssize_t show_crit_alarm(struct device *dev,
+ struct device_attribute *devattr, char *buf)
{
- struct coretemp_data *data = coretemp_update_device(dev);
- /* read the Out-of-spec log, never clear */
- return sprintf(buf, "%d\n", data->alarm);
+ u32 eax, edx;
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct platform_data *pdata = dev_get_drvdata(dev);
+ struct temp_data *tdata = pdata->core_data[attr->index];
+
+ rdmsr_on_cpu(tdata->cpu, tdata->status_reg, &eax, &edx);
+
+ return sprintf(buf, "%d\n", (eax >> 5) & 1);
}
-static ssize_t show_temp(struct device *dev,
- struct device_attribute *devattr, char *buf)
+static ssize_t show_tjmax(struct device *dev,
+ struct device_attribute *devattr, char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- struct coretemp_data *data = coretemp_update_device(dev);
- int err;
+ struct platform_data *pdata = dev_get_drvdata(dev);
- if (attr->index == SHOW_TEMP)
- err = data->valid ? sprintf(buf, "%d\n", data->temp) : -EAGAIN;
- else if (attr->index == SHOW_TJMAX)
- err = sprintf(buf, "%d\n", data->tjmax);
- else
- err = sprintf(buf, "%d\n", data->ttarget);
- return err;
+ return sprintf(buf, "%d\n", pdata->core_data[attr->index]->tjmax);
}
-static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL,
- SHOW_TEMP);
-static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, show_temp, NULL,
- SHOW_TJMAX);
-static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, show_temp, NULL,
- SHOW_TTARGET);
-static DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL);
-static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_name, NULL, SHOW_LABEL);
-static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, SHOW_NAME);
-
-static struct attribute *coretemp_attributes[] = {
- &sensor_dev_attr_name.dev_attr.attr,
- &sensor_dev_attr_temp1_label.dev_attr.attr,
- &dev_attr_temp1_crit_alarm.attr,
- &sensor_dev_attr_temp1_input.dev_attr.attr,
- &sensor_dev_attr_temp1_crit.dev_attr.attr,
- NULL
-};
+static ssize_t show_ttarget(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct platform_data *pdata = dev_get_drvdata(dev);
-static const struct attribute_group coretemp_group = {
- .attrs = coretemp_attributes,
-};
+ return sprintf(buf, "%d\n", pdata->core_data[attr->index]->ttarget);
+}
-static struct coretemp_data *coretemp_update_device(struct device *dev)
+static ssize_t show_temp(struct device *dev,
+ struct device_attribute *devattr, char *buf)
{
- struct coretemp_data *data = dev_get_drvdata(dev);
-
- mutex_lock(&data->update_lock);
+ u32 eax, edx;
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct platform_data *pdata = dev_get_drvdata(dev);
+ struct temp_data *tdata = pdata->core_data[attr->index];
- if (!data->valid || time_after(jiffies, data->last_updated + HZ)) {
- u32 eax, edx;
+ mutex_lock(&tdata->update_lock);
- data->valid = 0;
- rdmsr_on_cpu(data->id, MSR_IA32_THERM_STATUS, &eax, &edx);
- data->alarm = (eax >> 5) & 1;
- /* update only if data has been valid */
+ /* Check whether the time interval has elapsed */
+ if (!tdata->valid || time_after(jiffies, tdata->last_updated + HZ)) {
+ rdmsr_on_cpu(tdata->cpu, tdata->status_reg, &eax, &edx);
+ tdata->valid = 0;
+ /* Check whether the data is valid */
if (eax & 0x80000000) {
- data->temp = data->tjmax - (((eax >> 16)
- & 0x7f) * 1000);
- data->valid = 1;
- } else {
- dev_dbg(dev, "Temperature data invalid (0x%x)\n", eax);
+ tdata->temp = tdata->tjmax -
+ ((eax >> 16) & 0x7f) * 1000;
+ tdata->valid = 1;
}
- data->last_updated = jiffies;
+ tdata->last_updated = jiffies;
}
- mutex_unlock(&data->update_lock);
- return data;
+ mutex_unlock(&tdata->update_lock);
+ return tdata->valid ? sprintf(buf, "%d\n", tdata->temp) : -EAGAIN;
}
-static int __devinit adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev)
+static int adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev)
{
/* The 100C is default for both mobile and non mobile CPUs */
@@ -169,9 +193,8 @@ static int __devinit adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *
/* Early chips have no MSR for TjMax */
- if ((c->x86_model == 0xf) && (c->x86_mask < 4)) {
+ if (c->x86_model == 0xf && c->x86_mask < 4)
usemsr_ee = 0;
- }
/* Atom CPUs */
@@ -190,14 +213,14 @@ static int __devinit adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *
pci_dev_put(host_bridge);
}
- if ((c->x86_model > 0xe) && (usemsr_ee)) {
+ if (c->x86_model > 0xe && usemsr_ee) {
u8 platform_id;
- /* Now we can detect the mobile CPU using Intel provided table
- http://softwarecommunity.intel.com/Wiki/Mobility/720.htm
- For Core2 cores, check MSR 0x17, bit 28 1 = Mobile CPU
- */
-
+ /*
+ * Now we can detect the mobile CPU using Intel provided table
+ * http://softwarecommunity.intel.com/Wiki/Mobility/720.htm
+ * For Core2 cores, check MSR 0x17, bit 28 1 = Mobile CPU
+ */
err = rdmsr_safe_on_cpu(id, 0x17, &eax, &edx);
if (err) {
dev_warn(dev,
@@ -205,20 +228,26 @@ static int __devinit adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *
" CPU\n");
usemsr_ee = 0;
} else if (c->x86_model < 0x17 && !(eax & 0x10000000)) {
- /* Trust bit 28 up to Penryn, I could not find any
- documentation on that; if you happen to know
- someone at Intel please ask */
+ /*
+ * Trust bit 28 up to Penryn, I could not find any
+ * documentation on that; if you happen to know
+ * someone at Intel please ask
+ */
usemsr_ee = 0;
} else {
/* Platform ID bits 52:50 (EDX starts at bit 32) */
platform_id = (edx >> 18) & 0x7;
- /* Mobile Penryn CPU seems to be platform ID 7 or 5
- (guesswork) */
- if ((c->x86_model == 0x17) &&
- ((platform_id == 5) || (platform_id == 7))) {
- /* If MSR EE bit is set, set it to 90 degrees C,
- otherwise 105 degrees C */
+ /*
+ * Mobile Penryn CPU seems to be platform ID 7 or 5
+ * (guesswork)
+ */
+ if (c->x86_model == 0x17 &&
+ (platform_id == 5 || platform_id == 7)) {
+ /*
+ * If MSR EE bit is set, set it to 90 degrees C,
+ * otherwise 105 degrees C
+ */
tjmax_ee = 90000;
tjmax = 105000;
}
@@ -226,7 +255,6 @@ static int __devinit adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *
}
if (usemsr_ee) {
-
err = rdmsr_safe_on_cpu(id, 0xee, &eax, &edx);
if (err) {
dev_warn(dev,
@@ -235,25 +263,28 @@ static int __devinit adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *
} else if (eax & 0x40000000) {
tjmax = tjmax_ee;
}
- /* if we dont use msr EE it means we are desktop CPU (with exeception
- of Atom) */
} else if (tjmax == 100000) {
+ /*
+ * If we don't use msr EE it means we are desktop CPU
+ * (with exeception of Atom)
+ */
dev_warn(dev, "Using relative temperature scale!\n");
}
return tjmax;
}
-static int __devinit get_tjmax(struct cpuinfo_x86 *c, u32 id,
- struct device *dev)
+static int get_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev)
{
/* The 100C is default for both mobile and non mobile CPUs */
int err;
u32 eax, edx;
u32 val;
- /* A new feature of current Intel(R) processors, the
- IA32_TEMPERATURE_TARGET contains the TjMax value */
+ /*
+ * A new feature of current Intel(R) processors, the
+ * IA32_TEMPERATURE_TARGET contains the TjMax value
+ */
err = rdmsr_safe_on_cpu(id, MSR_IA32_TEMPERATURE_TARGET, &eax, &edx);
if (err) {
dev_warn(dev, "Unable to read TjMax from CPU.\n");
@@ -263,7 +294,7 @@ static int __devinit get_tjmax(struct cpuinfo_x86 *c, u32 id,
* If the TjMax is not plausible, an assumption
* will be used
*/
- if ((val > 80) && (val < 120)) {
+ if (val > 80 && val < 120) {
dev_info(dev, "TjMax is %d C.\n", val);
return val * 1000;
}
@@ -300,115 +331,293 @@ static void __devinit get_ucode_rev_on_cpu(void *edx)
rdmsr(MSR_IA32_UCODE_REV, eax, *(u32 *)edx);
}
-static int __devinit coretemp_probe(struct platform_device *pdev)
+static int get_pkg_tjmax(unsigned int cpu, struct device *dev)
{
- struct coretemp_data *data;
- struct cpuinfo_x86 *c = &cpu_data(pdev->id);
int err;
- u32 eax, edx;
+ u32 eax, edx, val;
- if (!(data = kzalloc(sizeof(struct coretemp_data), GFP_KERNEL))) {
- err = -ENOMEM;
- dev_err(&pdev->dev, "Out of memory\n");
- goto exit;
+ err = rdmsr_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET, &eax, &edx);
+ if (!err) {
+ val = (eax >> 16) & 0xff;
+ if (val > 80 && val < 120)
+ return val * 1000;
}
+ dev_warn(dev, "Unable to read Pkg-TjMax from CPU:%u\n", cpu);
+ return 100000; /* Default TjMax: 100 degree celsius */
+}
- data->id = pdev->id;
-#ifdef CONFIG_SMP
- data->core_id = c->cpu_core_id;
-#endif
- data->name = "coretemp";
- mutex_init(&data->update_lock);
+static int create_name_attr(struct platform_data *pdata, struct device *dev)
+{
+ pdata->name_attr.attr.name = "name";
+ pdata->name_attr.attr.mode = S_IRUGO;
+ pdata->name_attr.show = show_name;
+ return device_create_file(dev, &pdata->name_attr);
+}
- /* test if we can access the THERM_STATUS MSR */
- err = rdmsr_safe_on_cpu(data->id, MSR_IA32_THERM_STATUS, &eax, &edx);
- if (err) {
- dev_err(&pdev->dev,
- "Unable to access THERM_STATUS MSR, giving up\n");
- goto exit_free;
+static int create_core_attrs(struct temp_data *tdata, struct device *dev,
+ int attr_no)
+{
+ int err, i;
+ static ssize_t (*rd_ptr[MAX_ATTRS]) (struct device *dev,
+ struct device_attribute *devattr, char *buf) = {
+ show_label, show_crit_alarm, show_ttarget,
+ show_temp, show_tjmax };
+ static const char *names[MAX_ATTRS] = {
+ "temp%d_label", "temp%d_crit_alarm",
+ "temp%d_max", "temp%d_input",
+ "temp%d_crit" };
+
+ for (i = 0; i < MAX_ATTRS; i++) {
+ snprintf(tdata->attr_name[i], CORETEMP_NAME_LENGTH, names[i],
+ attr_no);
+ tdata->sd_attrs[i].dev_attr.attr.name = tdata->attr_name[i];
+ tdata->sd_attrs[i].dev_attr.attr.mode = S_IRUGO;
+ tdata->sd_attrs[i].dev_attr.show = rd_ptr[i];
+ tdata->sd_attrs[i].dev_attr.store = NULL;
+ tdata->sd_attrs[i].index = attr_no;
+ err = device_create_file(dev, &tdata->sd_attrs[i].dev_attr);
+ if (err)
+ goto exit_free;
+ }
+ return 0;
+
+exit_free:
+ while (--i >= 0)
+ device_remove_file(dev, &tdata->sd_attrs[i].dev_attr);
+ return err;
+}
+
+static void update_ttarget(__u8 cpu_model, struct temp_data *tdata,
+ struct device *dev)
+{
+ int err;
+ u32 eax, edx;
+
+ /*
+ * Initialize ttarget value. Eventually this will be
+ * initialized with the value from MSR_IA32_THERM_INTERRUPT
+ * register. If IA32_TEMPERATURE_TARGET is supported, this
+ * value will be over written below.
+ * To Do: Patch to initialize ttarget from MSR_IA32_THERM_INTERRUPT
+ */
+ tdata->ttarget = tdata->tjmax - 20000;
+
+ /*
+ * Read the still undocumented IA32_TEMPERATURE_TARGET. It exists
+ * on older CPUs but not in this register,
+ * Atoms don't have it either.
+ */
+ if (cpu_model > 0xe && cpu_model != 0x1c) {
+ err = rdmsr_safe_on_cpu(tdata->cpu,
+ MSR_IA32_TEMPERATURE_TARGET, &eax, &edx);
+ if (err) {
+ dev_warn(dev,
+ "Unable to read IA32_TEMPERATURE_TARGET MSR\n");
+ } else {
+ tdata->ttarget = tdata->tjmax -
+ ((eax >> 8) & 0xff) * 1000;
+ }
}
+}
- /* Check if we have problem with errata AE18 of Core processors:
- Readings might stop update when processor visited too deep sleep,
- fixed for stepping D0 (6EC).
- */
+static int chk_ucode_version(struct platform_device *pdev)
+{
+ struct cpuinfo_x86 *c = &cpu_data(pdev->id);
+ int err;
+ u32 edx;
- if ((c->x86_model == 0xe) && (c->x86_mask < 0xc)) {
+ /*
+ * Check if we have problem with errata AE18 of Core processors:
+ * Readings might stop update when processor visited too deep sleep,
+ * fixed for stepping D0 (6EC).
+ */
+ if (c->x86_model == 0xe && c->x86_mask < 0xc) {
/* check for microcode update */
- err = smp_call_function_single(data->id, get_ucode_rev_on_cpu,
+ err = smp_call_function_single(pdev->id, get_ucode_rev_on_cpu,
&edx, 1);
if (err) {
dev_err(&pdev->dev,
"Cannot determine microcode revision of "
- "CPU#%u (%d)!\n", data->id, err);
- err = -ENODEV;
- goto exit_free;
+ "CPU#%u (%d)!\n", pdev->id, err);
+ return -ENODEV;
} else if (edx < 0x39) {
- err = -ENODEV;
dev_err(&pdev->dev,
"Errata AE18 not fixed, update BIOS or "
"microcode of the CPU!\n");
- goto exit_free;
+ return -ENODEV;
}
}
+ return 0;
+}
- data->tjmax = get_tjmax(c, data->id, &pdev->dev);
- platform_set_drvdata(pdev, data);
+static struct platform_device *coretemp_get_pdev(unsigned int cpu)
+{
+ u16 phys_proc_id = TO_PHYS_ID(cpu);
+ struct pdev_entry *p;
+
+ mutex_lock(&pdev_list_mutex);
+
+ list_for_each_entry(p, &pdev_list, list)
+ if (p->phys_proc_id == phys_proc_id) {
+ mutex_unlock(&pdev_list_mutex);
+ return p->pdev;
+ }
+
+ mutex_unlock(&pdev_list_mutex);
+ return NULL;
+}
+
+static struct temp_data *init_temp_data(unsigned int cpu, int pkg_flag)
+{
+ struct temp_data *tdata;
+
+ tdata = kzalloc(sizeof(struct temp_data), GFP_KERNEL);
+ if (!tdata)
+ return NULL;
+
+ tdata->status_reg = pkg_flag ? MSR_IA32_PACKAGE_THERM_STATUS :
+ MSR_IA32_THERM_STATUS;
+ tdata->is_pkg_data = pkg_flag;
+ tdata->cpu = cpu;
+ tdata->cpu_core_id = TO_CORE_ID(cpu);
+ mutex_init(&tdata->update_lock);
+ return tdata;
+}
+
+static int create_core_data(struct platform_data *pdata,
+ struct platform_device *pdev,
+ unsigned int cpu, int pkg_flag)
+{
+ struct temp_data *tdata;
+ struct cpuinfo_x86 *c = &cpu_data(cpu);
+ u32 eax, edx;
+ int err, attr_no;
/*
- * read the still undocumented IA32_TEMPERATURE_TARGET. It exists
- * on older CPUs but not in this register,
- * Atoms don't have it either.
+ * Find attr number for sysfs:
+ * We map the attr number to core id of the CPU
+ * The attr number is always core id + 2
+ * The Pkgtemp will always show up as temp1_*, if available
*/
+ attr_no = pkg_flag ? 1 : TO_ATTR_NO(cpu);
- if ((c->x86_model > 0xe) && (c->x86_model != 0x1c)) {
- err = rdmsr_safe_on_cpu(data->id, MSR_IA32_TEMPERATURE_TARGET,
- &eax, &edx);
- if (err) {
- dev_warn(&pdev->dev, "Unable to read"
- " IA32_TEMPERATURE_TARGET MSR\n");
- } else {
- data->ttarget = data->tjmax -
- (((eax >> 8) & 0xff) * 1000);
- err = device_create_file(&pdev->dev,
- &sensor_dev_attr_temp1_max.dev_attr);
- if (err)
- goto exit_free;
- }
- }
+ if (attr_no > MAX_CORE_DATA - 1)
+ return -ERANGE;
+
+ /* Skip if it is a HT core, Not an error */
+ if (pdata->core_data[attr_no] != NULL)
+ return 0;
- if ((err = sysfs_create_group(&pdev->dev.kobj, &coretemp_group)))
- goto exit_dev;
+ tdata = init_temp_data(cpu, pkg_flag);
+ if (!tdata)
+ return -ENOMEM;
- data->hwmon_dev = hwmon_device_register(&pdev->dev);
- if (IS_ERR(data->hwmon_dev)) {
- err = PTR_ERR(data->hwmon_dev);
- dev_err(&pdev->dev, "Class registration failed (%d)\n",
- err);
- goto exit_class;
- }
+ /* Test if we can access the status register */
+ err = rdmsr_safe_on_cpu(cpu, tdata->status_reg, &eax, &edx);
+ if (err)
+ goto exit_free;
+
+ /* We can access status register. Get Critical Temperature */
+ if (pkg_flag)
+ tdata->tjmax = get_pkg_tjmax(pdev->id, &pdev->dev);
+ else
+ tdata->tjmax = get_tjmax(c, cpu, &pdev->dev);
+
+ update_ttarget(c->x86_model, tdata, &pdev->dev);
+ pdata->core_data[attr_no] = tdata;
+
+ /* Create sysfs interfaces */
+ err = create_core_attrs(tdata, &pdev->dev, attr_no);
+ if (err)
+ goto exit_free;
return 0;
+exit_free:
+ kfree(tdata);
+ return err;
+}
+
+static void coretemp_add_core(unsigned int cpu, int pkg_flag)
+{
+ struct platform_data *pdata;
+ struct platform_device *pdev = coretemp_get_pdev(cpu);
+ int err;
+
+ if (!pdev)
+ return;
+
+ pdata = platform_get_drvdata(pdev);
+
+ err = create_core_data(pdata, pdev, cpu, pkg_flag);
+ if (err)
+ dev_err(&pdev->dev, "Adding Core %u failed\n", cpu);
+}
+
+static void coretemp_remove_core(struct platform_data *pdata,
+ struct device *dev, int indx)
+{
+ int i;
+ struct temp_data *tdata = pdata->core_data[indx];
-exit_class:
- sysfs_remove_group(&pdev->dev.kobj, &coretemp_group);
-exit_dev:
- device_remove_file(&pdev->dev, &sensor_dev_attr_temp1_max.dev_attr);
+ /* Remove the sysfs attributes */
+ for (i = 0; i < MAX_ATTRS; i++)
+ device_remove_file(dev, &tdata->sd_attrs[i].dev_attr);
+
+ kfree(pdata->core_data[indx]);
+ pdata->core_data[indx] = NULL;
+}
+
+static int __devinit coretemp_probe(struct platform_device *pdev)
+{
+ struct platform_data *pdata;
+ int err;
+
+ /* Check the microcode version of the CPU */
+ err = chk_ucode_version(pdev);
+ if (err)
+ return err;
+
+ /* Initialize the per-package data structures */
+ pdata = kzalloc(sizeof(struct platform_data), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ err = create_name_attr(pdata, &pdev->dev);
+ if (err)
+ goto exit_free;
+
+ pdata->phys_proc_id = TO_PHYS_ID(pdev->id);
+ platform_set_drvdata(pdev, pdata);
+
+ pdata->hwmon_dev = hwmon_device_register(&pdev->dev);
+ if (IS_ERR(pdata->hwmon_dev)) {
+ err = PTR_ERR(pdata->hwmon_dev);
+ dev_err(&pdev->dev, "Class registration failed (%d)\n", err);
+ goto exit_name;
+ }
+ return 0;
+
+exit_name:
+ device_remove_file(&pdev->dev, &pdata->name_attr);
+ platform_set_drvdata(pdev, NULL);
exit_free:
- kfree(data);
-exit:
+ kfree(pdata);
return err;
}
static int __devexit coretemp_remove(struct platform_device *pdev)
{
- struct coretemp_data *data = platform_get_drvdata(pdev);
+ struct platform_data *pdata = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = MAX_CORE_DATA - 1; i >= 0; --i)
+ if (pdata->core_data[i])
+ coretemp_remove_core(pdata, &pdev->dev, i);
- hwmon_device_unregister(data->hwmon_dev);
- sysfs_remove_group(&pdev->dev.kobj, &coretemp_group);
- device_remove_file(&pdev->dev, &sensor_dev_attr_temp1_max.dev_attr);
+ device_remove_file(&pdev->dev, &pdata->name_attr);
+ hwmon_device_unregister(pdata->hwmon_dev);
platform_set_drvdata(pdev, NULL);
- kfree(data);
+ kfree(pdata);
return 0;
}
@@ -421,50 +630,14 @@ static struct platform_driver coretemp_driver = {
.remove = __devexit_p(coretemp_remove),
};
-struct pdev_entry {
- struct list_head list;
- struct platform_device *pdev;
- unsigned int cpu;
-#ifdef CONFIG_SMP
- u16 phys_proc_id;
- u16 cpu_core_id;
-#endif
-};
-
-static LIST_HEAD(pdev_list);
-static DEFINE_MUTEX(pdev_list_mutex);
-
static int __cpuinit coretemp_device_add(unsigned int cpu)
{
int err;
struct platform_device *pdev;
struct pdev_entry *pdev_entry;
- struct cpuinfo_x86 *c = &cpu_data(cpu);
-
- /*
- * CPUID.06H.EAX[0] indicates whether the CPU has thermal
- * sensors. We check this bit only, all the early CPUs
- * without thermal sensors will be filtered out.
- */
- if (!cpu_has(c, X86_FEATURE_DTS)) {
- pr_info("CPU (model=0x%x) has no thermal sensor\n",
- c->x86_model);
- return 0;
- }
mutex_lock(&pdev_list_mutex);
-#ifdef CONFIG_SMP
- /* Skip second HT entry of each core */
- list_for_each_entry(pdev_entry, &pdev_list, list) {
- if (c->phys_proc_id == pdev_entry->phys_proc_id &&
- c->cpu_core_id == pdev_entry->cpu_core_id) {
- err = 0; /* Not an error */
- goto exit;
- }
- }
-#endif
-
pdev = platform_device_alloc(DRVNAME, cpu);
if (!pdev) {
err = -ENOMEM;
@@ -486,10 +659,9 @@ static int __cpuinit coretemp_device_add(unsigned int cpu)
pdev_entry->pdev = pdev;
pdev_entry->cpu = cpu;
-#ifdef CONFIG_SMP
- pdev_entry->phys_proc_id = c->phys_proc_id;
- pdev_entry->cpu_core_id = c->cpu_core_id;
-#endif
+ pdev_entry->phys_proc_id = TO_PHYS_ID(cpu);
+ pdev_entry->cpu_core_id = TO_CORE_ID(cpu);
+
list_add_tail(&pdev_entry->list, &pdev_list);
mutex_unlock(&pdev_list_mutex);
@@ -504,28 +676,108 @@ exit:
return err;
}
-static void __cpuinit coretemp_device_remove(unsigned int cpu)
+static void coretemp_device_remove(unsigned int cpu)
{
- struct pdev_entry *p;
- unsigned int i;
+ struct pdev_entry *p, *n;
+ u16 phys_proc_id = TO_PHYS_ID(cpu);
mutex_lock(&pdev_list_mutex);
- list_for_each_entry(p, &pdev_list, list) {
- if (p->cpu != cpu)
+ list_for_each_entry_safe(p, n, &pdev_list, list) {
+ if (p->phys_proc_id != phys_proc_id)
continue;
-
platform_device_unregister(p->pdev);
list_del(&p->list);
- mutex_unlock(&pdev_list_mutex);
kfree(p);
- for_each_cpu(i, cpu_sibling_mask(cpu))
- if (i != cpu && !coretemp_device_add(i))
- break;
- return;
}
mutex_unlock(&pdev_list_mutex);
}
+static bool is_any_core_online(struct platform_data *pdata)
+{
+ int i;
+
+ /* Find online cores, except pkgtemp data */
+ for (i = MAX_CORE_DATA - 1; i >= 0; --i) {
+ if (pdata->core_data[i] &&
+ !pdata->core_data[i]->is_pkg_data) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static void __cpuinit get_core_online(unsigned int cpu)
+{
+ struct cpuinfo_x86 *c = &cpu_data(cpu);
+ struct platform_device *pdev = coretemp_get_pdev(cpu);
+ int err;
+
+ /*
+ * CPUID.06H.EAX[0] indicates whether the CPU has thermal
+ * sensors. We check this bit only, all the early CPUs
+ * without thermal sensors will be filtered out.
+ */
+ if (!cpu_has(c, X86_FEATURE_DTS))
+ return;
+
+ if (!pdev) {
+ /*
+ * Alright, we have DTS support.
+ * We are bringing the _first_ core in this pkg
+ * online. So, initialize per-pkg data structures and
+ * then bring this core online.
+ */
+ err = coretemp_device_add(cpu);
+ if (err)
+ return;
+ /*
+ * Check whether pkgtemp support is available.
+ * If so, add interfaces for pkgtemp.
+ */
+ if (cpu_has(c, X86_FEATURE_PTS))
+ coretemp_add_core(cpu, 1);
+ }
+ /*
+ * Physical CPU device already exists.
+ * So, just add interfaces for this core.
+ */
+ coretemp_add_core(cpu, 0);
+}
+
+static void __cpuinit put_core_offline(unsigned int cpu)
+{
+ int i, indx;
+ struct platform_data *pdata;
+ struct platform_device *pdev = coretemp_get_pdev(cpu);
+
+ /* If the physical CPU device does not exist, just return */
+ if (!pdev)
+ return;
+
+ pdata = platform_get_drvdata(pdev);
+
+ indx = TO_ATTR_NO(cpu);
+
+ if (pdata->core_data[indx] && pdata->core_data[indx]->cpu == cpu)
+ coretemp_remove_core(pdata, &pdev->dev, indx);
+
+ /* Online the HT version of this core, if any */
+ for_each_cpu(i, cpu_sibling_mask(cpu)) {
+ if (i != cpu) {
+ get_core_online(i);
+ break;
+ }
+ }
+ /*
+ * If all cores in this pkg are offline, remove the device.
+ * coretemp_device_remove calls unregister_platform_device,
+ * which in turn calls coretemp_remove. This removes the
+ * pkgtemp entry and does other clean ups.
+ */
+ if (!is_any_core_online(pdata))
+ coretemp_device_remove(cpu);
+}
+
static int __cpuinit coretemp_cpu_callback(struct notifier_block *nfb,
unsigned long action, void *hcpu)
{
@@ -534,10 +786,10 @@ static int __cpuinit coretemp_cpu_callback(struct notifier_block *nfb,
switch (action) {
case CPU_ONLINE:
case CPU_DOWN_FAILED:
- coretemp_device_add(cpu);
+ get_core_online(cpu);
break;
case CPU_DOWN_PREPARE:
- coretemp_device_remove(cpu);
+ put_core_offline(cpu);
break;
}
return NOTIFY_OK;
@@ -560,7 +812,7 @@ static int __init coretemp_init(void)
goto exit;
for_each_online_cpu(i)
- coretemp_device_add(i);
+ get_core_online(i);
#ifndef CONFIG_HOTPLUG_CPU
if (list_empty(&pdev_list)) {
diff --git a/drivers/hwmon/max16065.c b/drivers/hwmon/max16065.c
new file mode 100644
index 000000000000..d94a24fdf4ba
--- /dev/null
+++ b/drivers/hwmon/max16065.c
@@ -0,0 +1,717 @@
+/*
+ * Driver for
+ * Maxim MAX16065/MAX16066 12-Channel/8-Channel, Flash-Configurable
+ * System Managers with Nonvolatile Fault Registers
+ * Maxim MAX16067/MAX16068 6-Channel, Flash-Configurable System Managers
+ * with Nonvolatile Fault Registers
+ * Maxim MAX16070/MAX16071 12-Channel/8-Channel, Flash-Configurable System
+ * Monitors with Nonvolatile Fault Registers
+ *
+ * Copyright (C) 2011 Ericsson AB.
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/delay.h>
+#include <linux/jiffies.h>
+
+enum chips { max16065, max16066, max16067, max16068, max16070, max16071 };
+
+/*
+ * Registers
+ */
+#define MAX16065_ADC(x) ((x) * 2)
+
+#define MAX16065_CURR_SENSE 0x18
+#define MAX16065_CSP_ADC 0x19
+#define MAX16065_FAULT(x) (0x1b + (x))
+#define MAX16065_SCALE(x) (0x43 + (x))
+#define MAX16065_CURR_CONTROL 0x47
+#define MAX16065_LIMIT(l, x) (0x48 + (l) + (x) * 3) /*
+ * l: limit
+ * 0: min/max
+ * 1: crit
+ * 2: lcrit
+ * x: ADC index
+ */
+
+#define MAX16065_SW_ENABLE 0x73
+
+#define MAX16065_WARNING_OV (1 << 3) /* Set if secondary threshold is OV
+ warning */
+
+#define MAX16065_CURR_ENABLE (1 << 0)
+
+#define MAX16065_NUM_LIMIT 3
+#define MAX16065_NUM_ADC 12 /* maximum number of ADC channels */
+
+static const int max16065_num_adc[] = {
+ [max16065] = 12,
+ [max16066] = 8,
+ [max16067] = 6,
+ [max16068] = 6,
+ [max16070] = 12,
+ [max16071] = 8,
+};
+
+static const bool max16065_have_secondary[] = {
+ [max16065] = true,
+ [max16066] = true,
+ [max16067] = false,
+ [max16068] = false,
+ [max16070] = true,
+ [max16071] = true,
+};
+
+static const bool max16065_have_current[] = {
+ [max16065] = true,
+ [max16066] = true,
+ [max16067] = false,
+ [max16068] = false,
+ [max16070] = true,
+ [max16071] = true,
+};
+
+struct max16065_data {
+ enum chips type;
+ struct device *hwmon_dev;
+ struct mutex update_lock;
+ bool valid;
+ unsigned long last_updated; /* in jiffies */
+ int num_adc;
+ bool have_current;
+ int curr_gain;
+ /* limits are in mV */
+ int limit[MAX16065_NUM_LIMIT][MAX16065_NUM_ADC];
+ int range[MAX16065_NUM_ADC + 1];/* voltage range */
+ int adc[MAX16065_NUM_ADC + 1]; /* adc values (raw) including csp_adc */
+ int curr_sense;
+ int fault[2];
+};
+
+static const int max16065_adc_range[] = { 5560, 2780, 1390, 0 };
+static const int max16065_csp_adc_range[] = { 7000, 14000 };
+
+/* ADC registers have 10 bit resolution. */
+static inline int ADC_TO_MV(int adc, int range)
+{
+ return (adc * range) / 1024;
+}
+
+/*
+ * Limit registers have 8 bit resolution and match upper 8 bits of ADC
+ * registers.
+ */
+static inline int LIMIT_TO_MV(int limit, int range)
+{
+ return limit * range / 256;
+}
+
+static inline int MV_TO_LIMIT(int mv, int range)
+{
+ return SENSORS_LIMIT(DIV_ROUND_CLOSEST(mv * 256, range), 0, 255);
+}
+
+static inline int ADC_TO_CURR(int adc, int gain)
+{
+ return adc * 1400000 / gain * 255;
+}
+
+/*
+ * max16065_read_adc()
+ *
+ * Read 16 bit value from <reg>, <reg+1>.
+ * Upper 8 bits are in <reg>, lower 2 bits are in bits 7:6 of <reg+1>.
+ */
+static int max16065_read_adc(struct i2c_client *client, int reg)
+{
+ int rv;
+
+ rv = i2c_smbus_read_word_data(client, reg);
+ if (unlikely(rv < 0))
+ return rv;
+ return ((rv & 0xff) << 2) | ((rv >> 14) & 0x03);
+}
+
+static struct max16065_data *max16065_update_device(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct max16065_data *data = i2c_get_clientdata(client);
+
+ mutex_lock(&data->update_lock);
+ if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
+ int i;
+
+ for (i = 0; i < data->num_adc; i++)
+ data->adc[i]
+ = max16065_read_adc(client, MAX16065_ADC(i));
+
+ if (data->have_current) {
+ data->adc[MAX16065_NUM_ADC]
+ = max16065_read_adc(client, MAX16065_CSP_ADC);
+ data->curr_sense
+ = i2c_smbus_read_byte_data(client,
+ MAX16065_CURR_SENSE);
+ }
+
+ for (i = 0; i < DIV_ROUND_UP(data->num_adc, 8); i++)
+ data->fault[i]
+ = i2c_smbus_read_byte_data(client, MAX16065_FAULT(i));
+
+ data->last_updated = jiffies;
+ data->valid = 1;
+ }
+ mutex_unlock(&data->update_lock);
+ return data;
+}
+
+static ssize_t max16065_show_alarm(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct sensor_device_attribute_2 *attr2 = to_sensor_dev_attr_2(da);
+ struct max16065_data *data = max16065_update_device(dev);
+ int val = data->fault[attr2->nr];
+
+ if (val < 0)
+ return val;
+
+ val &= (1 << attr2->index);
+ if (val)
+ i2c_smbus_write_byte_data(to_i2c_client(dev),
+ MAX16065_FAULT(attr2->nr), val);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", !!val);
+}
+
+static ssize_t max16065_show_input(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct max16065_data *data = max16065_update_device(dev);
+ int adc = data->adc[attr->index];
+
+ if (unlikely(adc < 0))
+ return adc;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ ADC_TO_MV(adc, data->range[attr->index]));
+}
+
+static ssize_t max16065_show_current(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct max16065_data *data = max16065_update_device(dev);
+
+ if (unlikely(data->curr_sense < 0))
+ return data->curr_sense;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ ADC_TO_CURR(data->curr_sense, data->curr_gain));
+}
+
+static ssize_t max16065_set_limit(struct device *dev,
+ struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute_2 *attr2 = to_sensor_dev_attr_2(da);
+ struct i2c_client *client = to_i2c_client(dev);
+ struct max16065_data *data = i2c_get_clientdata(client);
+ unsigned long val;
+ int err;
+ int limit;
+
+ err = strict_strtoul(buf, 10, &val);
+ if (unlikely(err < 0))
+ return err;
+
+ limit = MV_TO_LIMIT(val, data->range[attr2->index]);
+
+ mutex_lock(&data->update_lock);
+ data->limit[attr2->nr][attr2->index]
+ = LIMIT_TO_MV(limit, data->range[attr2->index]);
+ i2c_smbus_write_byte_data(client,
+ MAX16065_LIMIT(attr2->nr, attr2->index),
+ limit);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t max16065_show_limit(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct sensor_device_attribute_2 *attr2 = to_sensor_dev_attr_2(da);
+ struct i2c_client *client = to_i2c_client(dev);
+ struct max16065_data *data = i2c_get_clientdata(client);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ data->limit[attr2->nr][attr2->index]);
+}
+
+/* Construct a sensor_device_attribute structure for each register */
+
+/* Input voltages */
+static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, max16065_show_input, NULL, 0);
+static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, max16065_show_input, NULL, 1);
+static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, max16065_show_input, NULL, 2);
+static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, max16065_show_input, NULL, 3);
+static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, max16065_show_input, NULL, 4);
+static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, max16065_show_input, NULL, 5);
+static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, max16065_show_input, NULL, 6);
+static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, max16065_show_input, NULL, 7);
+static SENSOR_DEVICE_ATTR(in8_input, S_IRUGO, max16065_show_input, NULL, 8);
+static SENSOR_DEVICE_ATTR(in9_input, S_IRUGO, max16065_show_input, NULL, 9);
+static SENSOR_DEVICE_ATTR(in10_input, S_IRUGO, max16065_show_input, NULL, 10);
+static SENSOR_DEVICE_ATTR(in11_input, S_IRUGO, max16065_show_input, NULL, 11);
+static SENSOR_DEVICE_ATTR(in12_input, S_IRUGO, max16065_show_input, NULL, 12);
+
+/* Input voltages lcrit */
+static SENSOR_DEVICE_ATTR_2(in0_lcrit, S_IWUSR | S_IRUGO, max16065_show_limit,
+ max16065_set_limit, 2, 0);
+static SENSOR_DEVICE_ATTR_2(in1_lcrit, S_IWUSR | S_IRUGO, max16065_show_limit,
+ max16065_set_limit, 2, 1);
+static SENSOR_DEVICE_ATTR_2(in2_lcrit, S_IWUSR | S_IRUGO, max16065_show_limit,
+ max16065_set_limit, 2, 2);
+static SENSOR_DEVICE_ATTR_2(in3_lcrit, S_IWUSR | S_IRUGO, max16065_show_limit,
+ max16065_set_limit, 2, 3);
+static SENSOR_DEVICE_ATTR_2(in4_lcrit, S_IWUSR | S_IRUGO, max16065_show_limit,
+ max16065_set_limit, 2, 4);
+static SENSOR_DEVICE_ATTR_2(in5_lcrit, S_IWUSR | S_IRUGO, max16065_show_limit,
+ max16065_set_limit, 2, 5);
+static SENSOR_DEVICE_ATTR_2(in6_lcrit, S_IWUSR | S_IRUGO, max16065_show_limit,
+ max16065_set_limit, 2, 6);
+static SENSOR_DEVICE_ATTR_2(in7_lcrit, S_IWUSR | S_IRUGO, max16065_show_limit,
+ max16065_set_limit, 2, 7);
+static SENSOR_DEVICE_ATTR_2(in8_lcrit, S_IWUSR | S_IRUGO, max16065_show_limit,
+ max16065_set_limit, 2, 8);
+static SENSOR_DEVICE_ATTR_2(in9_lcrit, S_IWUSR | S_IRUGO, max16065_show_limit,
+ max16065_set_limit, 2, 9);
+static SENSOR_DEVICE_ATTR_2(in10_lcrit, S_IWUSR | S_IRUGO, max16065_show_limit,
+ max16065_set_limit, 2, 10);
+static SENSOR_DEVICE_ATTR_2(in11_lcrit, S_IWUSR | S_IRUGO, max16065_show_limit,
+ max16065_set_limit, 2, 11);
+
+/* Input voltages crit */
+static SENSOR_DEVICE_ATTR_2(in0_crit, S_IWUSR | S_IRUGO, max16065_show_limit,
+ max16065_set_limit, 1, 0);
+static SENSOR_DEVICE_ATTR_2(in1_crit, S_IWUSR | S_IRUGO, max16065_show_limit,
+ max16065_set_limit, 1, 1);
+static SENSOR_DEVICE_ATTR_2(in2_crit, S_IWUSR | S_IRUGO, max16065_show_limit,
+ max16065_set_limit, 1, 2);
+static SENSOR_DEVICE_ATTR_2(in3_crit, S_IWUSR | S_IRUGO, max16065_show_limit,
+ max16065_set_limit, 1, 3);
+static SENSOR_DEVICE_ATTR_2(in4_crit, S_IWUSR | S_IRUGO, max16065_show_limit,
+ max16065_set_limit, 1, 4);
+static SENSOR_DEVICE_ATTR_2(in5_crit, S_IWUSR | S_IRUGO, max16065_show_limit,
+ max16065_set_limit, 1, 5);
+static SENSOR_DEVICE_ATTR_2(in6_crit, S_IWUSR | S_IRUGO, max16065_show_limit,
+ max16065_set_limit, 1, 6);
+static SENSOR_DEVICE_ATTR_2(in7_crit, S_IWUSR | S_IRUGO, max16065_show_limit,
+ max16065_set_limit, 1, 7);
+static SENSOR_DEVICE_ATTR_2(in8_crit, S_IWUSR | S_IRUGO, max16065_show_limit,
+ max16065_set_limit, 1, 8);
+static SENSOR_DEVICE_ATTR_2(in9_crit, S_IWUSR | S_IRUGO, max16065_show_limit,
+ max16065_set_limit, 1, 9);
+static SENSOR_DEVICE_ATTR_2(in10_crit, S_IWUSR | S_IRUGO, max16065_show_limit,
+ max16065_set_limit, 1, 10);
+static SENSOR_DEVICE_ATTR_2(in11_crit, S_IWUSR | S_IRUGO, max16065_show_limit,
+ max16065_set_limit, 1, 11);
+
+/* Input voltages min */
+static SENSOR_DEVICE_ATTR_2(in0_min, S_IWUSR | S_IRUGO, max16065_show_limit,
+ max16065_set_limit, 0, 0);
+static SENSOR_DEVICE_ATTR_2(in1_min, S_IWUSR | S_IRUGO, max16065_show_limit,
+ max16065_set_limit, 0, 1);
+static SENSOR_DEVICE_ATTR_2(in2_min, S_IWUSR | S_IRUGO, max16065_show_limit,
+ max16065_set_limit, 0, 2);
+static SENSOR_DEVICE_ATTR_2(in3_min, S_IWUSR | S_IRUGO, max16065_show_limit,
+ max16065_set_limit, 0, 3);
+static SENSOR_DEVICE_ATTR_2(in4_min, S_IWUSR | S_IRUGO, max16065_show_limit,
+ max16065_set_limit, 0, 4);
+static SENSOR_DEVICE_ATTR_2(in5_min, S_IWUSR | S_IRUGO, max16065_show_limit,
+ max16065_set_limit, 0, 5);
+static SENSOR_DEVICE_ATTR_2(in6_min, S_IWUSR | S_IRUGO, max16065_show_limit,
+ max16065_set_limit, 0, 6);
+static SENSOR_DEVICE_ATTR_2(in7_min, S_IWUSR | S_IRUGO, max16065_show_limit,
+ max16065_set_limit, 0, 7);
+static SENSOR_DEVICE_ATTR_2(in8_min, S_IWUSR | S_IRUGO, max16065_show_limit,
+ max16065_set_limit, 0, 8);
+static SENSOR_DEVICE_ATTR_2(in9_min, S_IWUSR | S_IRUGO, max16065_show_limit,
+ max16065_set_limit, 0, 9);
+static SENSOR_DEVICE_ATTR_2(in10_min, S_IWUSR | S_IRUGO, max16065_show_limit,
+ max16065_set_limit, 0, 10);
+static SENSOR_DEVICE_ATTR_2(in11_min, S_IWUSR | S_IRUGO, max16065_show_limit,
+ max16065_set_limit, 0, 11);
+
+/* Input voltages max */
+static SENSOR_DEVICE_ATTR_2(in0_max, S_IWUSR | S_IRUGO, max16065_show_limit,
+ max16065_set_limit, 0, 0);
+static SENSOR_DEVICE_ATTR_2(in1_max, S_IWUSR | S_IRUGO, max16065_show_limit,
+ max16065_set_limit, 0, 1);
+static SENSOR_DEVICE_ATTR_2(in2_max, S_IWUSR | S_IRUGO, max16065_show_limit,
+ max16065_set_limit, 0, 2);
+static SENSOR_DEVICE_ATTR_2(in3_max, S_IWUSR | S_IRUGO, max16065_show_limit,
+ max16065_set_limit, 0, 3);
+static SENSOR_DEVICE_ATTR_2(in4_max, S_IWUSR | S_IRUGO, max16065_show_limit,
+ max16065_set_limit, 0, 4);
+static SENSOR_DEVICE_ATTR_2(in5_max, S_IWUSR | S_IRUGO, max16065_show_limit,
+ max16065_set_limit, 0, 5);
+static SENSOR_DEVICE_ATTR_2(in6_max, S_IWUSR | S_IRUGO, max16065_show_limit,
+ max16065_set_limit, 0, 6);
+static SENSOR_DEVICE_ATTR_2(in7_max, S_IWUSR | S_IRUGO, max16065_show_limit,
+ max16065_set_limit, 0, 7);
+static SENSOR_DEVICE_ATTR_2(in8_max, S_IWUSR | S_IRUGO, max16065_show_limit,
+ max16065_set_limit, 0, 8);
+static SENSOR_DEVICE_ATTR_2(in9_max, S_IWUSR | S_IRUGO, max16065_show_limit,
+ max16065_set_limit, 0, 9);
+static SENSOR_DEVICE_ATTR_2(in10_max, S_IWUSR | S_IRUGO, max16065_show_limit,
+ max16065_set_limit, 0, 10);
+static SENSOR_DEVICE_ATTR_2(in11_max, S_IWUSR | S_IRUGO, max16065_show_limit,
+ max16065_set_limit, 0, 11);
+
+/* alarms */
+static SENSOR_DEVICE_ATTR_2(in0_alarm, S_IRUGO, max16065_show_alarm, NULL,
+ 0, 0);
+static SENSOR_DEVICE_ATTR_2(in1_alarm, S_IRUGO, max16065_show_alarm, NULL,
+ 0, 1);
+static SENSOR_DEVICE_ATTR_2(in2_alarm, S_IRUGO, max16065_show_alarm, NULL,
+ 0, 2);
+static SENSOR_DEVICE_ATTR_2(in3_alarm, S_IRUGO, max16065_show_alarm, NULL,
+ 0, 3);
+static SENSOR_DEVICE_ATTR_2(in4_alarm, S_IRUGO, max16065_show_alarm, NULL,
+ 0, 4);
+static SENSOR_DEVICE_ATTR_2(in5_alarm, S_IRUGO, max16065_show_alarm, NULL,
+ 0, 5);
+static SENSOR_DEVICE_ATTR_2(in6_alarm, S_IRUGO, max16065_show_alarm, NULL,
+ 0, 6);
+static SENSOR_DEVICE_ATTR_2(in7_alarm, S_IRUGO, max16065_show_alarm, NULL,
+ 0, 7);
+static SENSOR_DEVICE_ATTR_2(in8_alarm, S_IRUGO, max16065_show_alarm, NULL,
+ 1, 0);
+static SENSOR_DEVICE_ATTR_2(in9_alarm, S_IRUGO, max16065_show_alarm, NULL,
+ 1, 1);
+static SENSOR_DEVICE_ATTR_2(in10_alarm, S_IRUGO, max16065_show_alarm, NULL,
+ 1, 2);
+static SENSOR_DEVICE_ATTR_2(in11_alarm, S_IRUGO, max16065_show_alarm, NULL,
+ 1, 3);
+
+/* Current and alarm */
+static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, max16065_show_current, NULL, 0);
+static SENSOR_DEVICE_ATTR_2(curr1_alarm, S_IRUGO, max16065_show_alarm, NULL,
+ 1, 4);
+
+/*
+ * Finally, construct an array of pointers to members of the above objects,
+ * as required for sysfs_create_group()
+ */
+static struct attribute *max16065_basic_attributes[] = {
+ &sensor_dev_attr_in0_input.dev_attr.attr,
+ &sensor_dev_attr_in0_lcrit.dev_attr.attr,
+ &sensor_dev_attr_in0_crit.dev_attr.attr,
+ &sensor_dev_attr_in0_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_in1_input.dev_attr.attr,
+ &sensor_dev_attr_in1_lcrit.dev_attr.attr,
+ &sensor_dev_attr_in1_crit.dev_attr.attr,
+ &sensor_dev_attr_in1_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_in2_input.dev_attr.attr,
+ &sensor_dev_attr_in2_lcrit.dev_attr.attr,
+ &sensor_dev_attr_in2_crit.dev_attr.attr,
+ &sensor_dev_attr_in2_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_in3_input.dev_attr.attr,
+ &sensor_dev_attr_in3_lcrit.dev_attr.attr,
+ &sensor_dev_attr_in3_crit.dev_attr.attr,
+ &sensor_dev_attr_in3_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_in4_input.dev_attr.attr,
+ &sensor_dev_attr_in4_lcrit.dev_attr.attr,
+ &sensor_dev_attr_in4_crit.dev_attr.attr,
+ &sensor_dev_attr_in4_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_in5_input.dev_attr.attr,
+ &sensor_dev_attr_in5_lcrit.dev_attr.attr,
+ &sensor_dev_attr_in5_crit.dev_attr.attr,
+ &sensor_dev_attr_in5_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_in6_input.dev_attr.attr,
+ &sensor_dev_attr_in6_lcrit.dev_attr.attr,
+ &sensor_dev_attr_in6_crit.dev_attr.attr,
+ &sensor_dev_attr_in6_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_in7_input.dev_attr.attr,
+ &sensor_dev_attr_in7_lcrit.dev_attr.attr,
+ &sensor_dev_attr_in7_crit.dev_attr.attr,
+ &sensor_dev_attr_in7_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_in8_input.dev_attr.attr,
+ &sensor_dev_attr_in8_lcrit.dev_attr.attr,
+ &sensor_dev_attr_in8_crit.dev_attr.attr,
+ &sensor_dev_attr_in8_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_in9_input.dev_attr.attr,
+ &sensor_dev_attr_in9_lcrit.dev_attr.attr,
+ &sensor_dev_attr_in9_crit.dev_attr.attr,
+ &sensor_dev_attr_in9_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_in10_input.dev_attr.attr,
+ &sensor_dev_attr_in10_lcrit.dev_attr.attr,
+ &sensor_dev_attr_in10_crit.dev_attr.attr,
+ &sensor_dev_attr_in10_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_in11_input.dev_attr.attr,
+ &sensor_dev_attr_in11_lcrit.dev_attr.attr,
+ &sensor_dev_attr_in11_crit.dev_attr.attr,
+ &sensor_dev_attr_in11_alarm.dev_attr.attr,
+
+ NULL
+};
+
+static struct attribute *max16065_current_attributes[] = {
+ &sensor_dev_attr_in12_input.dev_attr.attr,
+ &sensor_dev_attr_curr1_input.dev_attr.attr,
+ &sensor_dev_attr_curr1_alarm.dev_attr.attr,
+ NULL
+};
+
+static struct attribute *max16065_min_attributes[] = {
+ &sensor_dev_attr_in0_min.dev_attr.attr,
+ &sensor_dev_attr_in1_min.dev_attr.attr,
+ &sensor_dev_attr_in2_min.dev_attr.attr,
+ &sensor_dev_attr_in3_min.dev_attr.attr,
+ &sensor_dev_attr_in4_min.dev_attr.attr,
+ &sensor_dev_attr_in5_min.dev_attr.attr,
+ &sensor_dev_attr_in6_min.dev_attr.attr,
+ &sensor_dev_attr_in7_min.dev_attr.attr,
+ &sensor_dev_attr_in8_min.dev_attr.attr,
+ &sensor_dev_attr_in9_min.dev_attr.attr,
+ &sensor_dev_attr_in10_min.dev_attr.attr,
+ &sensor_dev_attr_in11_min.dev_attr.attr,
+ NULL
+};
+
+static struct attribute *max16065_max_attributes[] = {
+ &sensor_dev_attr_in0_max.dev_attr.attr,
+ &sensor_dev_attr_in1_max.dev_attr.attr,
+ &sensor_dev_attr_in2_max.dev_attr.attr,
+ &sensor_dev_attr_in3_max.dev_attr.attr,
+ &sensor_dev_attr_in4_max.dev_attr.attr,
+ &sensor_dev_attr_in5_max.dev_attr.attr,
+ &sensor_dev_attr_in6_max.dev_attr.attr,
+ &sensor_dev_attr_in7_max.dev_attr.attr,
+ &sensor_dev_attr_in8_max.dev_attr.attr,
+ &sensor_dev_attr_in9_max.dev_attr.attr,
+ &sensor_dev_attr_in10_max.dev_attr.attr,
+ &sensor_dev_attr_in11_max.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group max16065_basic_group = {
+ .attrs = max16065_basic_attributes,
+};
+
+static const struct attribute_group max16065_current_group = {
+ .attrs = max16065_current_attributes,
+};
+
+static const struct attribute_group max16065_min_group = {
+ .attrs = max16065_min_attributes,
+};
+
+static const struct attribute_group max16065_max_group = {
+ .attrs = max16065_max_attributes,
+};
+
+static void max16065_cleanup(struct i2c_client *client)
+{
+ sysfs_remove_group(&client->dev.kobj, &max16065_max_group);
+ sysfs_remove_group(&client->dev.kobj, &max16065_min_group);
+ sysfs_remove_group(&client->dev.kobj, &max16065_current_group);
+ sysfs_remove_group(&client->dev.kobj, &max16065_basic_group);
+}
+
+static int max16065_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ struct max16065_data *data;
+ int i, j, val, ret;
+ bool have_secondary; /* true if chip has secondary limits */
+ bool secondary_is_max = false; /* secondary limits reflect max */
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA
+ | I2C_FUNC_SMBUS_READ_WORD_DATA))
+ return -ENODEV;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (unlikely(!data))
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, data);
+ mutex_init(&data->update_lock);
+
+ data->num_adc = max16065_num_adc[id->driver_data];
+ data->have_current = max16065_have_current[id->driver_data];
+ have_secondary = max16065_have_secondary[id->driver_data];
+
+ if (have_secondary) {
+ val = i2c_smbus_read_byte_data(client, MAX16065_SW_ENABLE);
+ if (unlikely(val < 0)) {
+ ret = val;
+ goto out_free;
+ }
+ secondary_is_max = val & MAX16065_WARNING_OV;
+ }
+
+ /* Read scale registers, convert to range */
+ for (i = 0; i < DIV_ROUND_UP(data->num_adc, 4); i++) {
+ val = i2c_smbus_read_byte_data(client, MAX16065_SCALE(i));
+ if (unlikely(val < 0)) {
+ ret = val;
+ goto out_free;
+ }
+ for (j = 0; j < 4 && i * 4 + j < data->num_adc; j++) {
+ data->range[i * 4 + j] =
+ max16065_adc_range[(val >> (j * 2)) & 0x3];
+ }
+ }
+
+ /* Read limits */
+ for (i = 0; i < MAX16065_NUM_LIMIT; i++) {
+ if (i == 0 && !have_secondary)
+ continue;
+
+ for (j = 0; j < data->num_adc; j++) {
+ val = i2c_smbus_read_byte_data(client,
+ MAX16065_LIMIT(i, j));
+ if (unlikely(val < 0)) {
+ ret = val;
+ goto out_free;
+ }
+ data->limit[i][j] = LIMIT_TO_MV(val, data->range[j]);
+ }
+ }
+
+ /* Register sysfs hooks */
+ for (i = 0; i < data->num_adc * 4; i++) {
+ /* Do not create sysfs entry if channel is disabled */
+ if (!data->range[i / 4])
+ continue;
+
+ ret = sysfs_create_file(&client->dev.kobj,
+ max16065_basic_attributes[i]);
+ if (unlikely(ret))
+ goto out;
+ }
+
+ if (have_secondary) {
+ struct attribute **attr = secondary_is_max ?
+ max16065_max_attributes : max16065_min_attributes;
+
+ for (i = 0; i < data->num_adc; i++) {
+ if (!data->range[i])
+ continue;
+
+ ret = sysfs_create_file(&client->dev.kobj, attr[i]);
+ if (unlikely(ret))
+ goto out;
+ }
+ }
+
+ if (data->have_current) {
+ val = i2c_smbus_read_byte_data(client, MAX16065_CURR_CONTROL);
+ if (unlikely(val < 0)) {
+ ret = val;
+ goto out;
+ }
+ if (val & MAX16065_CURR_ENABLE) {
+ /*
+ * Current gain is 6, 12, 24, 48 based on values in
+ * bit 2,3.
+ */
+ data->curr_gain = 6 << ((val >> 2) & 0x03);
+ data->range[MAX16065_NUM_ADC]
+ = max16065_csp_adc_range[(val >> 1) & 0x01];
+ ret = sysfs_create_group(&client->dev.kobj,
+ &max16065_current_group);
+ if (unlikely(ret))
+ goto out;
+ } else {
+ data->have_current = false;
+ }
+ }
+
+ data->hwmon_dev = hwmon_device_register(&client->dev);
+ if (unlikely(IS_ERR(data->hwmon_dev))) {
+ ret = PTR_ERR(data->hwmon_dev);
+ goto out;
+ }
+ return 0;
+
+out:
+ max16065_cleanup(client);
+out_free:
+ kfree(data);
+ return ret;
+}
+
+static int max16065_remove(struct i2c_client *client)
+{
+ struct max16065_data *data = i2c_get_clientdata(client);
+
+ hwmon_device_unregister(data->hwmon_dev);
+ max16065_cleanup(client);
+ kfree(data);
+
+ return 0;
+}
+
+static const struct i2c_device_id max16065_id[] = {
+ { "max16065", max16065 },
+ { "max16066", max16066 },
+ { "max16067", max16067 },
+ { "max16068", max16068 },
+ { "max16070", max16070 },
+ { "max16071", max16071 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, max16065_id);
+
+/* This is the driver that will be inserted */
+static struct i2c_driver max16065_driver = {
+ .driver = {
+ .name = "max16065",
+ },
+ .probe = max16065_probe,
+ .remove = max16065_remove,
+ .id_table = max16065_id,
+};
+
+static int __init max16065_init(void)
+{
+ return i2c_add_driver(&max16065_driver);
+}
+
+static void __exit max16065_exit(void)
+{
+ i2c_del_driver(&max16065_driver);
+}
+
+MODULE_AUTHOR("Guenter Roeck <guenter.roeck@ericsson.com>");
+MODULE_DESCRIPTION("MAX16065 driver");
+MODULE_LICENSE("GPL");
+
+module_init(max16065_init);
+module_exit(max16065_exit);
diff --git a/drivers/hwmon/max34440.c b/drivers/hwmon/max34440.c
index 992b701b4c5e..db11e1a175b2 100644
--- a/drivers/hwmon/max34440.c
+++ b/drivers/hwmon/max34440.c
@@ -32,7 +32,7 @@ enum chips { max34440, max34441 };
#define MAX34440_STATUS_OT_FAULT (1 << 5)
#define MAX34440_STATUS_OT_WARN (1 << 6)
-static int max34440_get_status(struct i2c_client *client, int page, int reg)
+static int max34440_read_byte_data(struct i2c_client *client, int page, int reg)
{
int ret;
int mfg_status;
@@ -108,7 +108,7 @@ static struct pmbus_driver_info max34440_info[] = {
.func[11] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
.func[12] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
.func[13] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
- .get_status = max34440_get_status,
+ .read_byte_data = max34440_read_byte_data,
},
[max34441] = {
.pages = 12,
@@ -149,7 +149,7 @@ static struct pmbus_driver_info max34440_info[] = {
.func[9] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
.func[10] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
.func[11] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
- .get_status = max34440_get_status,
+ .read_byte_data = max34440_read_byte_data,
},
};
diff --git a/drivers/hwmon/max6642.c b/drivers/hwmon/max6642.c
new file mode 100644
index 000000000000..0f9fc40379cd
--- /dev/null
+++ b/drivers/hwmon/max6642.c
@@ -0,0 +1,356 @@
+/*
+ * Driver for +/-1 degree C, SMBus-Compatible Remote/Local Temperature Sensor
+ * with Overtemperature Alarm
+ *
+ * Copyright (C) 2011 AppearTV AS
+ *
+ * Derived from:
+ *
+ * Based on the max1619 driver.
+ * Copyright (C) 2003-2004 Alexey Fisher <fishor@mail.ru>
+ * Jean Delvare <khali@linux-fr.org>
+ *
+ * The MAX6642 is a sensor chip made by Maxim.
+ * It reports up to two temperatures (its own plus up to
+ * one external one). Complete datasheet can be
+ * obtained from Maxim's website at:
+ * http://datasheets.maxim-ic.com/en/ds/MAX6642.pdf
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/sysfs.h>
+
+static const unsigned short normal_i2c[] = {
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, I2C_CLIENT_END };
+
+/*
+ * The MAX6642 registers
+ */
+
+#define MAX6642_REG_R_MAN_ID 0xFE
+#define MAX6642_REG_R_CONFIG 0x03
+#define MAX6642_REG_W_CONFIG 0x09
+#define MAX6642_REG_R_STATUS 0x02
+#define MAX6642_REG_R_LOCAL_TEMP 0x00
+#define MAX6642_REG_R_LOCAL_TEMPL 0x11
+#define MAX6642_REG_R_LOCAL_HIGH 0x05
+#define MAX6642_REG_W_LOCAL_HIGH 0x0B
+#define MAX6642_REG_R_REMOTE_TEMP 0x01
+#define MAX6642_REG_R_REMOTE_TEMPL 0x10
+#define MAX6642_REG_R_REMOTE_HIGH 0x07
+#define MAX6642_REG_W_REMOTE_HIGH 0x0D
+
+/*
+ * Conversions
+ */
+
+static int temp_from_reg10(int val)
+{
+ return val * 250;
+}
+
+static int temp_from_reg(int val)
+{
+ return val * 1000;
+}
+
+static int temp_to_reg(int val)
+{
+ return val / 1000;
+}
+
+/*
+ * Client data (each client gets its own)
+ */
+
+struct max6642_data {
+ struct device *hwmon_dev;
+ struct mutex update_lock;
+ bool valid; /* zero until following fields are valid */
+ unsigned long last_updated; /* in jiffies */
+
+ /* registers values */
+ u16 temp_input[2]; /* local/remote */
+ u16 temp_high[2]; /* local/remote */
+ u8 alarms;
+};
+
+/*
+ * Real code
+ */
+
+static void max6642_init_client(struct i2c_client *client)
+{
+ u8 config;
+ struct max6642_data *data = i2c_get_clientdata(client);
+
+ /*
+ * Start the conversions.
+ */
+ config = i2c_smbus_read_byte_data(client, MAX6642_REG_R_CONFIG);
+ if (config & 0x40)
+ i2c_smbus_write_byte_data(client, MAX6642_REG_W_CONFIG,
+ config & 0xBF); /* run */
+
+ data->temp_high[0] = i2c_smbus_read_byte_data(client,
+ MAX6642_REG_R_LOCAL_HIGH);
+ data->temp_high[1] = i2c_smbus_read_byte_data(client,
+ MAX6642_REG_R_REMOTE_HIGH);
+}
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int max6642_detect(struct i2c_client *client,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ u8 reg_config, reg_status, man_id;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ /* identification */
+ man_id = i2c_smbus_read_byte_data(client, MAX6642_REG_R_MAN_ID);
+ if (man_id != 0x4D)
+ return -ENODEV;
+
+ /*
+ * We read the config and status register, the 4 lower bits in the
+ * config register should be zero and bit 5, 3, 1 and 0 should be
+ * zero in the status register.
+ */
+ reg_config = i2c_smbus_read_byte_data(client, MAX6642_REG_R_CONFIG);
+ reg_status = i2c_smbus_read_byte_data(client, MAX6642_REG_R_STATUS);
+ if (((reg_config & 0x0f) != 0x00) ||
+ ((reg_status & 0x2b) != 0x00))
+ return -ENODEV;
+
+ strlcpy(info->type, "max6642", I2C_NAME_SIZE);
+
+ return 0;
+}
+
+static struct max6642_data *max6642_update_device(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct max6642_data *data = i2c_get_clientdata(client);
+ u16 val, tmp;
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
+ dev_dbg(&client->dev, "Updating max6642 data.\n");
+ val = i2c_smbus_read_byte_data(client,
+ MAX6642_REG_R_LOCAL_TEMPL);
+ tmp = (val >> 6) & 3;
+ val = i2c_smbus_read_byte_data(client,
+ MAX6642_REG_R_LOCAL_TEMP);
+ val = (val << 2) | tmp;
+ data->temp_input[0] = val;
+ val = i2c_smbus_read_byte_data(client,
+ MAX6642_REG_R_REMOTE_TEMPL);
+ tmp = (val >> 6) & 3;
+ val = i2c_smbus_read_byte_data(client,
+ MAX6642_REG_R_REMOTE_TEMP);
+ val = (val << 2) | tmp;
+ data->temp_input[1] = val;
+ data->alarms = i2c_smbus_read_byte_data(client,
+ MAX6642_REG_R_STATUS);
+
+ data->last_updated = jiffies;
+ data->valid = 1;
+ }
+
+ mutex_unlock(&data->update_lock);
+
+ return data;
+}
+
+/*
+ * Sysfs stuff
+ */
+
+static ssize_t show_temp_max10(struct device *dev,
+ struct device_attribute *dev_attr, char *buf)
+{
+ struct max6642_data *data = max6642_update_device(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr);
+
+ return sprintf(buf, "%d\n",
+ temp_from_reg10(data->temp_input[attr->index]));
+}
+
+static ssize_t show_temp_max(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct max6642_data *data = max6642_update_device(dev);
+ struct sensor_device_attribute_2 *attr2 = to_sensor_dev_attr_2(attr);
+
+ return sprintf(buf, "%d\n", temp_from_reg(data->temp_high[attr2->nr]));
+}
+
+static ssize_t set_temp_max(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long val;
+ int err;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct max6642_data *data = i2c_get_clientdata(client);
+ struct sensor_device_attribute_2 *attr2 = to_sensor_dev_attr_2(attr);
+
+ err = strict_strtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->temp_high[attr2->nr] = SENSORS_LIMIT(temp_to_reg(val), 0, 255);
+ i2c_smbus_write_byte_data(client, attr2->index,
+ data->temp_high[attr2->nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int bitnr = to_sensor_dev_attr(attr)->index;
+ struct max6642_data *data = max6642_update_device(dev);
+ return sprintf(buf, "%d\n", (data->alarms >> bitnr) & 1);
+}
+
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_max10, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp_max10, NULL, 1);
+static SENSOR_DEVICE_ATTR_2(temp1_max, S_IWUSR | S_IRUGO, show_temp_max,
+ set_temp_max, 0, MAX6642_REG_W_LOCAL_HIGH);
+static SENSOR_DEVICE_ATTR_2(temp2_max, S_IWUSR | S_IRUGO, show_temp_max,
+ set_temp_max, 1, MAX6642_REG_W_REMOTE_HIGH);
+static SENSOR_DEVICE_ATTR(temp_fault, S_IRUGO, show_alarm, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 6);
+static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_alarm, NULL, 4);
+
+static struct attribute *max6642_attributes[] = {
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+
+ &sensor_dev_attr_temp_fault.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group max6642_group = {
+ .attrs = max6642_attributes,
+};
+
+static int max6642_probe(struct i2c_client *new_client,
+ const struct i2c_device_id *id)
+{
+ struct max6642_data *data;
+ int err;
+
+ data = kzalloc(sizeof(struct max6642_data), GFP_KERNEL);
+ if (!data) {
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ i2c_set_clientdata(new_client, data);
+ mutex_init(&data->update_lock);
+
+ /* Initialize the MAX6642 chip */
+ max6642_init_client(new_client);
+
+ /* Register sysfs hooks */
+ err = sysfs_create_group(&new_client->dev.kobj, &max6642_group);
+ if (err)
+ goto exit_free;
+
+ data->hwmon_dev = hwmon_device_register(&new_client->dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ err = PTR_ERR(data->hwmon_dev);
+ goto exit_remove_files;
+ }
+
+ return 0;
+
+exit_remove_files:
+ sysfs_remove_group(&new_client->dev.kobj, &max6642_group);
+exit_free:
+ kfree(data);
+exit:
+ return err;
+}
+
+static int max6642_remove(struct i2c_client *client)
+{
+ struct max6642_data *data = i2c_get_clientdata(client);
+
+ hwmon_device_unregister(data->hwmon_dev);
+ sysfs_remove_group(&client->dev.kobj, &max6642_group);
+
+ kfree(data);
+ return 0;
+}
+
+/*
+ * Driver data (common to all clients)
+ */
+
+static const struct i2c_device_id max6642_id[] = {
+ { "max6642", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max6642_id);
+
+static struct i2c_driver max6642_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "max6642",
+ },
+ .probe = max6642_probe,
+ .remove = max6642_remove,
+ .id_table = max6642_id,
+ .detect = max6642_detect,
+ .address_list = normal_i2c,
+};
+
+static int __init max6642_init(void)
+{
+ return i2c_add_driver(&max6642_driver);
+}
+
+static void __exit max6642_exit(void)
+{
+ i2c_del_driver(&max6642_driver);
+}
+
+MODULE_AUTHOR("Per Dalen <per.dalen@appeartv.com>");
+MODULE_DESCRIPTION("MAX6642 sensor driver");
+MODULE_LICENSE("GPL");
+
+module_init(max6642_init);
+module_exit(max6642_exit);
diff --git a/drivers/hwmon/max8688.c b/drivers/hwmon/max8688.c
index 8ebfef2ecf26..7fb93f4e9f21 100644
--- a/drivers/hwmon/max8688.c
+++ b/drivers/hwmon/max8688.c
@@ -37,7 +37,7 @@
#define MAX8688_STATUS_OT_FAULT (1 << 13)
#define MAX8688_STATUS_OT_WARNING (1 << 14)
-static int max8688_get_status(struct i2c_client *client, int page, int reg)
+static int max8688_read_byte_data(struct i2c_client *client, int page, int reg)
{
int ret = 0;
int mfg_status;
@@ -110,7 +110,7 @@ static struct pmbus_driver_info max8688_info = {
.func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | PMBUS_HAVE_TEMP
| PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT
| PMBUS_HAVE_STATUS_TEMP,
- .get_status = max8688_get_status,
+ .read_byte_data = max8688_read_byte_data,
};
static int max8688_probe(struct i2c_client *client,
diff --git a/drivers/hwmon/pkgtemp.c b/drivers/hwmon/pkgtemp.c
deleted file mode 100644
index 21c817d98123..000000000000
--- a/drivers/hwmon/pkgtemp.c
+++ /dev/null
@@ -1,444 +0,0 @@
-/*
- * pkgtemp.c - Linux kernel module for processor package hardware monitoring
- *
- * Copyright (C) 2010 Fenghua Yu <fenghua.yu@intel.com>
- *
- * Inspired from many hwmon drivers especially coretemp.
- *
- * 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., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301 USA.
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/jiffies.h>
-#include <linux/hwmon.h>
-#include <linux/sysfs.h>
-#include <linux/hwmon-sysfs.h>
-#include <linux/err.h>
-#include <linux/mutex.h>
-#include <linux/list.h>
-#include <linux/platform_device.h>
-#include <linux/cpu.h>
-#include <asm/msr.h>
-#include <asm/processor.h>
-#include <asm/smp.h>
-
-#define DRVNAME "pkgtemp"
-
-enum { SHOW_TEMP, SHOW_TJMAX, SHOW_TTARGET, SHOW_LABEL, SHOW_NAME };
-
-/*
- * Functions declaration
- */
-
-static struct pkgtemp_data *pkgtemp_update_device(struct device *dev);
-
-struct pkgtemp_data {
- struct device *hwmon_dev;
- struct mutex update_lock;
- const char *name;
- u32 id;
- u16 phys_proc_id;
- char valid; /* zero until following fields are valid */
- unsigned long last_updated; /* in jiffies */
- int temp;
- int tjmax;
- int ttarget;
- u8 alarm;
-};
-
-/*
- * Sysfs stuff
- */
-
-static ssize_t show_name(struct device *dev, struct device_attribute
- *devattr, char *buf)
-{
- int ret;
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- struct pkgtemp_data *data = dev_get_drvdata(dev);
-
- if (attr->index == SHOW_NAME)
- ret = sprintf(buf, "%s\n", data->name);
- else /* show label */
- ret = sprintf(buf, "physical id %d\n",
- data->phys_proc_id);
- return ret;
-}
-
-static ssize_t show_alarm(struct device *dev, struct device_attribute
- *devattr, char *buf)
-{
- struct pkgtemp_data *data = pkgtemp_update_device(dev);
- /* read the Out-of-spec log, never clear */
- return sprintf(buf, "%d\n", data->alarm);
-}
-
-static ssize_t show_temp(struct device *dev,
- struct device_attribute *devattr, char *buf)
-{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- struct pkgtemp_data *data = pkgtemp_update_device(dev);
- int err = 0;
-
- if (attr->index == SHOW_TEMP)
- err = data->valid ? sprintf(buf, "%d\n", data->temp) : -EAGAIN;
- else if (attr->index == SHOW_TJMAX)
- err = sprintf(buf, "%d\n", data->tjmax);
- else
- err = sprintf(buf, "%d\n", data->ttarget);
- return err;
-}
-
-static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, SHOW_TEMP);
-static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, show_temp, NULL, SHOW_TJMAX);
-static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, show_temp, NULL, SHOW_TTARGET);
-static DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL);
-static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_name, NULL, SHOW_LABEL);
-static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, SHOW_NAME);
-
-static struct attribute *pkgtemp_attributes[] = {
- &sensor_dev_attr_name.dev_attr.attr,
- &sensor_dev_attr_temp1_label.dev_attr.attr,
- &dev_attr_temp1_crit_alarm.attr,
- &sensor_dev_attr_temp1_input.dev_attr.attr,
- &sensor_dev_attr_temp1_crit.dev_attr.attr,
- NULL
-};
-
-static const struct attribute_group pkgtemp_group = {
- .attrs = pkgtemp_attributes,
-};
-
-static struct pkgtemp_data *pkgtemp_update_device(struct device *dev)
-{
- struct pkgtemp_data *data = dev_get_drvdata(dev);
- unsigned int cpu;
- int err;
-
- mutex_lock(&data->update_lock);
-
- if (!data->valid || time_after(jiffies, data->last_updated + HZ)) {
- u32 eax, edx;
-
- data->valid = 0;
- cpu = data->id;
- err = rdmsr_on_cpu(cpu, MSR_IA32_PACKAGE_THERM_STATUS,
- &eax, &edx);
- if (!err) {
- data->alarm = (eax >> 5) & 1;
- data->temp = data->tjmax - (((eax >> 16)
- & 0x7f) * 1000);
- data->valid = 1;
- } else
- dev_dbg(dev, "Temperature data invalid (0x%x)\n", eax);
-
- data->last_updated = jiffies;
- }
-
- mutex_unlock(&data->update_lock);
- return data;
-}
-
-static int get_tjmax(int cpu, struct device *dev)
-{
- int default_tjmax = 100000;
- int err;
- u32 eax, edx;
- u32 val;
-
- /* IA32_TEMPERATURE_TARGET contains the TjMax value */
- err = rdmsr_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET, &eax, &edx);
- if (!err) {
- val = (eax >> 16) & 0xff;
- if ((val > 80) && (val < 120)) {
- dev_info(dev, "TjMax is %d C.\n", val);
- return val * 1000;
- }
- }
- dev_warn(dev, "Unable to read TjMax from CPU.\n");
- return default_tjmax;
-}
-
-static int __devinit pkgtemp_probe(struct platform_device *pdev)
-{
- struct pkgtemp_data *data;
- int err;
- u32 eax, edx;
-#ifdef CONFIG_SMP
- struct cpuinfo_x86 *c = &cpu_data(pdev->id);
-#endif
-
- data = kzalloc(sizeof(struct pkgtemp_data), GFP_KERNEL);
- if (!data) {
- err = -ENOMEM;
- dev_err(&pdev->dev, "Out of memory\n");
- goto exit;
- }
-
- data->id = pdev->id;
-#ifdef CONFIG_SMP
- data->phys_proc_id = c->phys_proc_id;
-#endif
- data->name = "pkgtemp";
- mutex_init(&data->update_lock);
-
- /* test if we can access the THERM_STATUS MSR */
- err = rdmsr_safe_on_cpu(data->id, MSR_IA32_PACKAGE_THERM_STATUS,
- &eax, &edx);
- if (err) {
- dev_err(&pdev->dev,
- "Unable to access THERM_STATUS MSR, giving up\n");
- goto exit_free;
- }
-
- data->tjmax = get_tjmax(data->id, &pdev->dev);
- platform_set_drvdata(pdev, data);
-
- err = rdmsr_safe_on_cpu(data->id, MSR_IA32_TEMPERATURE_TARGET,
- &eax, &edx);
- if (err) {
- dev_warn(&pdev->dev, "Unable to read"
- " IA32_TEMPERATURE_TARGET MSR\n");
- } else {
- data->ttarget = data->tjmax - (((eax >> 8) & 0xff) * 1000);
- err = device_create_file(&pdev->dev,
- &sensor_dev_attr_temp1_max.dev_attr);
- if (err)
- goto exit_free;
- }
-
- err = sysfs_create_group(&pdev->dev.kobj, &pkgtemp_group);
- if (err)
- goto exit_dev;
-
- data->hwmon_dev = hwmon_device_register(&pdev->dev);
- if (IS_ERR(data->hwmon_dev)) {
- err = PTR_ERR(data->hwmon_dev);
- dev_err(&pdev->dev, "Class registration failed (%d)\n",
- err);
- goto exit_class;
- }
-
- return 0;
-
-exit_class:
- sysfs_remove_group(&pdev->dev.kobj, &pkgtemp_group);
-exit_dev:
- device_remove_file(&pdev->dev, &sensor_dev_attr_temp1_max.dev_attr);
-exit_free:
- kfree(data);
-exit:
- return err;
-}
-
-static int __devexit pkgtemp_remove(struct platform_device *pdev)
-{
- struct pkgtemp_data *data = platform_get_drvdata(pdev);
-
- hwmon_device_unregister(data->hwmon_dev);
- sysfs_remove_group(&pdev->dev.kobj, &pkgtemp_group);
- device_remove_file(&pdev->dev, &sensor_dev_attr_temp1_max.dev_attr);
- platform_set_drvdata(pdev, NULL);
- kfree(data);
- return 0;
-}
-
-static struct platform_driver pkgtemp_driver = {
- .driver = {
- .owner = THIS_MODULE,
- .name = DRVNAME,
- },
- .probe = pkgtemp_probe,
- .remove = __devexit_p(pkgtemp_remove),
-};
-
-struct pdev_entry {
- struct list_head list;
- struct platform_device *pdev;
- unsigned int cpu;
-#ifdef CONFIG_SMP
- u16 phys_proc_id;
-#endif
-};
-
-static LIST_HEAD(pdev_list);
-static DEFINE_MUTEX(pdev_list_mutex);
-
-static int __cpuinit pkgtemp_device_add(unsigned int cpu)
-{
- int err;
- struct platform_device *pdev;
- struct pdev_entry *pdev_entry;
- struct cpuinfo_x86 *c = &cpu_data(cpu);
-
- if (!cpu_has(c, X86_FEATURE_PTS))
- return 0;
-
- mutex_lock(&pdev_list_mutex);
-
-#ifdef CONFIG_SMP
- /* Only keep the first entry in each package */
- list_for_each_entry(pdev_entry, &pdev_list, list) {
- if (c->phys_proc_id == pdev_entry->phys_proc_id) {
- err = 0; /* Not an error */
- goto exit;
- }
- }
-#endif
-
- pdev = platform_device_alloc(DRVNAME, cpu);
- if (!pdev) {
- err = -ENOMEM;
- pr_err("Device allocation failed\n");
- goto exit;
- }
-
- pdev_entry = kzalloc(sizeof(struct pdev_entry), GFP_KERNEL);
- if (!pdev_entry) {
- err = -ENOMEM;
- goto exit_device_put;
- }
-
- err = platform_device_add(pdev);
- if (err) {
- pr_err("Device addition failed (%d)\n", err);
- goto exit_device_free;
- }
-
-#ifdef CONFIG_SMP
- pdev_entry->phys_proc_id = c->phys_proc_id;
-#endif
- pdev_entry->pdev = pdev;
- pdev_entry->cpu = cpu;
- list_add_tail(&pdev_entry->list, &pdev_list);
- mutex_unlock(&pdev_list_mutex);
-
- return 0;
-
-exit_device_free:
- kfree(pdev_entry);
-exit_device_put:
- platform_device_put(pdev);
-exit:
- mutex_unlock(&pdev_list_mutex);
- return err;
-}
-
-static void __cpuinit pkgtemp_device_remove(unsigned int cpu)
-{
- struct pdev_entry *p;
- unsigned int i;
- int err;
-
- mutex_lock(&pdev_list_mutex);
- list_for_each_entry(p, &pdev_list, list) {
- if (p->cpu != cpu)
- continue;
-
- platform_device_unregister(p->pdev);
- list_del(&p->list);
- mutex_unlock(&pdev_list_mutex);
- kfree(p);
- for_each_cpu(i, cpu_core_mask(cpu)) {
- if (i != cpu) {
- err = pkgtemp_device_add(i);
- if (!err)
- break;
- }
- }
- return;
- }
- mutex_unlock(&pdev_list_mutex);
-}
-
-static int __cpuinit pkgtemp_cpu_callback(struct notifier_block *nfb,
- unsigned long action, void *hcpu)
-{
- unsigned int cpu = (unsigned long) hcpu;
-
- switch (action) {
- case CPU_ONLINE:
- case CPU_DOWN_FAILED:
- pkgtemp_device_add(cpu);
- break;
- case CPU_DOWN_PREPARE:
- pkgtemp_device_remove(cpu);
- break;
- }
- return NOTIFY_OK;
-}
-
-static struct notifier_block pkgtemp_cpu_notifier __refdata = {
- .notifier_call = pkgtemp_cpu_callback,
-};
-
-static int __init pkgtemp_init(void)
-{
- int i, err = -ENODEV;
-
- /* quick check if we run Intel */
- if (cpu_data(0).x86_vendor != X86_VENDOR_INTEL)
- goto exit;
-
- err = platform_driver_register(&pkgtemp_driver);
- if (err)
- goto exit;
-
- for_each_online_cpu(i)
- pkgtemp_device_add(i);
-
-#ifndef CONFIG_HOTPLUG_CPU
- if (list_empty(&pdev_list)) {
- err = -ENODEV;
- goto exit_driver_unreg;
- }
-#endif
-
- register_hotcpu_notifier(&pkgtemp_cpu_notifier);
- return 0;
-
-#ifndef CONFIG_HOTPLUG_CPU
-exit_driver_unreg:
- platform_driver_unregister(&pkgtemp_driver);
-#endif
-exit:
- return err;
-}
-
-static void __exit pkgtemp_exit(void)
-{
- struct pdev_entry *p, *n;
-
- unregister_hotcpu_notifier(&pkgtemp_cpu_notifier);
- mutex_lock(&pdev_list_mutex);
- list_for_each_entry_safe(p, n, &pdev_list, list) {
- platform_device_unregister(p->pdev);
- list_del(&p->list);
- kfree(p);
- }
- mutex_unlock(&pdev_list_mutex);
- platform_driver_unregister(&pkgtemp_driver);
-}
-
-MODULE_AUTHOR("Fenghua Yu <fenghua.yu@intel.com>");
-MODULE_DESCRIPTION("Intel processor package temperature monitor");
-MODULE_LICENSE("GPL");
-
-module_init(pkgtemp_init)
-module_exit(pkgtemp_exit)
diff --git a/drivers/hwmon/pmbus.h b/drivers/hwmon/pmbus.h
index a81f7f228762..50647ab7235a 100644
--- a/drivers/hwmon/pmbus.h
+++ b/drivers/hwmon/pmbus.h
@@ -281,13 +281,11 @@ struct pmbus_driver_info {
u32 func[PMBUS_PAGES]; /* Functionality, per page */
/*
- * The get_status function maps manufacturing specific status values
- * into PMBus standard status values.
- * This function is optional and only necessary if chip specific status
- * register values have to be mapped into standard PMBus status register
- * values.
+ * The following functions map manufacturing specific register values
+ * to PMBus standard register values. Specify only if mapping is
+ * necessary.
*/
- int (*get_status)(struct i2c_client *client, int page, int reg);
+ int (*read_byte_data)(struct i2c_client *client, int page, int reg);
/*
* The identify function determines supported PMBus functionality.
* This function is only necessary if a chip driver supports multiple
diff --git a/drivers/hwmon/pmbus_core.c b/drivers/hwmon/pmbus_core.c
index 196ffafafd88..98799bab69ce 100644
--- a/drivers/hwmon/pmbus_core.c
+++ b/drivers/hwmon/pmbus_core.c
@@ -270,18 +270,22 @@ const struct pmbus_driver_info *pmbus_get_driver_info(struct i2c_client *client)
}
EXPORT_SYMBOL_GPL(pmbus_get_driver_info);
-static int pmbus_get_status(struct i2c_client *client, int page, int reg)
+/*
+ * _pmbus_read_byte_data() is similar to pmbus_read_byte_data(), but checks if
+ * a device specific mapping funcion exists and calls it if necessary.
+ */
+static int _pmbus_read_byte_data(struct i2c_client *client, int page, int reg)
{
struct pmbus_data *data = i2c_get_clientdata(client);
const struct pmbus_driver_info *info = data->info;
int status;
- if (info->get_status) {
- status = info->get_status(client, page, reg);
+ if (info->read_byte_data) {
+ status = info->read_byte_data(client, page, reg);
if (status != -ENODATA)
return status;
}
- return pmbus_read_byte_data(client, page, reg);
+ return pmbus_read_byte_data(client, page, reg);
}
static struct pmbus_data *pmbus_update_device(struct device *dev)
@@ -302,38 +306,41 @@ static struct pmbus_data *pmbus_update_device(struct device *dev)
if (!(info->func[i] & PMBUS_HAVE_STATUS_VOUT))
continue;
data->status[PB_STATUS_VOUT_BASE + i]
- = pmbus_get_status(client, i, PMBUS_STATUS_VOUT);
+ = _pmbus_read_byte_data(client, i, PMBUS_STATUS_VOUT);
}
for (i = 0; i < info->pages; i++) {
if (!(info->func[i] & PMBUS_HAVE_STATUS_IOUT))
continue;
data->status[PB_STATUS_IOUT_BASE + i]
- = pmbus_get_status(client, i, PMBUS_STATUS_IOUT);
+ = _pmbus_read_byte_data(client, i, PMBUS_STATUS_IOUT);
}
for (i = 0; i < info->pages; i++) {
if (!(info->func[i] & PMBUS_HAVE_STATUS_TEMP))
continue;
data->status[PB_STATUS_TEMP_BASE + i]
- = pmbus_get_status(client, i,
- PMBUS_STATUS_TEMPERATURE);
+ = _pmbus_read_byte_data(client, i,
+ PMBUS_STATUS_TEMPERATURE);
}
for (i = 0; i < info->pages; i++) {
if (!(info->func[i] & PMBUS_HAVE_STATUS_FAN12))
continue;
data->status[PB_STATUS_FAN_BASE + i]
- = pmbus_get_status(client, i, PMBUS_STATUS_FAN_12);
+ = _pmbus_read_byte_data(client, i,
+ PMBUS_STATUS_FAN_12);
}
for (i = 0; i < info->pages; i++) {
if (!(info->func[i] & PMBUS_HAVE_STATUS_FAN34))
continue;
data->status[PB_STATUS_FAN34_BASE + i]
- = pmbus_get_status(client, i, PMBUS_STATUS_FAN_34);
+ = _pmbus_read_byte_data(client, i,
+ PMBUS_STATUS_FAN_34);
}
if (info->func[0] & PMBUS_HAVE_STATUS_INPUT)
data->status[PB_STATUS_INPUT_BASE]
- = pmbus_get_status(client, 0, PMBUS_STATUS_INPUT);
+ = _pmbus_read_byte_data(client, 0,
+ PMBUS_STATUS_INPUT);
for (i = 0; i < data->num_sensors; i++) {
struct pmbus_sensor *sensor = &data->sensors[i];
@@ -793,53 +800,6 @@ static void pmbus_add_label(struct pmbus_data *data,
data->num_labels++;
}
-static const int pmbus_temp_registers[] = {
- PMBUS_READ_TEMPERATURE_1,
- PMBUS_READ_TEMPERATURE_2,
- PMBUS_READ_TEMPERATURE_3
-};
-
-static const int pmbus_temp_flags[] = {
- PMBUS_HAVE_TEMP,
- PMBUS_HAVE_TEMP2,
- PMBUS_HAVE_TEMP3
-};
-
-static const int pmbus_fan_registers[] = {
- PMBUS_READ_FAN_SPEED_1,
- PMBUS_READ_FAN_SPEED_2,
- PMBUS_READ_FAN_SPEED_3,
- PMBUS_READ_FAN_SPEED_4
-};
-
-static const int pmbus_fan_config_registers[] = {
- PMBUS_FAN_CONFIG_12,
- PMBUS_FAN_CONFIG_12,
- PMBUS_FAN_CONFIG_34,
- PMBUS_FAN_CONFIG_34
-};
-
-static const int pmbus_fan_status_registers[] = {
- PMBUS_STATUS_FAN_12,
- PMBUS_STATUS_FAN_12,
- PMBUS_STATUS_FAN_34,
- PMBUS_STATUS_FAN_34
-};
-
-static const u32 pmbus_fan_flags[] = {
- PMBUS_HAVE_FAN12,
- PMBUS_HAVE_FAN12,
- PMBUS_HAVE_FAN34,
- PMBUS_HAVE_FAN34
-};
-
-static const u32 pmbus_fan_status_flags[] = {
- PMBUS_HAVE_STATUS_FAN12,
- PMBUS_HAVE_STATUS_FAN12,
- PMBUS_HAVE_STATUS_FAN34,
- PMBUS_HAVE_STATUS_FAN34
-};
-
/*
* Determine maximum number of sensors, booleans, and labels.
* To keep things simple, only make a rough high estimate.
@@ -900,499 +860,431 @@ static void pmbus_find_max_attr(struct i2c_client *client,
/*
* Search for attributes. Allocate sensors, booleans, and labels as needed.
*/
-static void pmbus_find_attributes(struct i2c_client *client,
- struct pmbus_data *data)
-{
- const struct pmbus_driver_info *info = data->info;
- int page, i0, i1, in_index;
- /*
- * Input voltage sensors
- */
- in_index = 1;
- if (info->func[0] & PMBUS_HAVE_VIN) {
- bool have_alarm = false;
-
- i0 = data->num_sensors;
- pmbus_add_label(data, "in", in_index, "vin", 0);
- pmbus_add_sensor(data, "in", "input", in_index, 0,
- PMBUS_READ_VIN, PSC_VOLTAGE_IN, true, true);
- if (pmbus_check_word_register(client, 0,
- PMBUS_VIN_UV_WARN_LIMIT)) {
- i1 = data->num_sensors;
- pmbus_add_sensor(data, "in", "min", in_index,
- 0, PMBUS_VIN_UV_WARN_LIMIT,
- PSC_VOLTAGE_IN, false, false);
- if (info->func[0] & PMBUS_HAVE_STATUS_INPUT) {
- pmbus_add_boolean_reg(data, "in", "min_alarm",
- in_index,
- PB_STATUS_INPUT_BASE,
- PB_VOLTAGE_UV_WARNING);
- have_alarm = true;
- }
- }
- if (pmbus_check_word_register(client, 0,
- PMBUS_VIN_UV_FAULT_LIMIT)) {
- i1 = data->num_sensors;
- pmbus_add_sensor(data, "in", "lcrit", in_index,
- 0, PMBUS_VIN_UV_FAULT_LIMIT,
- PSC_VOLTAGE_IN, false, false);
- if (info->func[0] & PMBUS_HAVE_STATUS_INPUT) {
- pmbus_add_boolean_reg(data, "in", "lcrit_alarm",
- in_index,
- PB_STATUS_INPUT_BASE,
- PB_VOLTAGE_UV_FAULT);
- have_alarm = true;
- }
- }
- if (pmbus_check_word_register(client, 0,
- PMBUS_VIN_OV_WARN_LIMIT)) {
- i1 = data->num_sensors;
- pmbus_add_sensor(data, "in", "max", in_index,
- 0, PMBUS_VIN_OV_WARN_LIMIT,
- PSC_VOLTAGE_IN, false, false);
- if (info->func[0] & PMBUS_HAVE_STATUS_INPUT) {
- pmbus_add_boolean_reg(data, "in", "max_alarm",
- in_index,
- PB_STATUS_INPUT_BASE,
- PB_VOLTAGE_OV_WARNING);
- have_alarm = true;
- }
- }
- if (pmbus_check_word_register(client, 0,
- PMBUS_VIN_OV_FAULT_LIMIT)) {
- i1 = data->num_sensors;
- pmbus_add_sensor(data, "in", "crit", in_index,
- 0, PMBUS_VIN_OV_FAULT_LIMIT,
- PSC_VOLTAGE_IN, false, false);
- if (info->func[0] & PMBUS_HAVE_STATUS_INPUT) {
- pmbus_add_boolean_reg(data, "in", "crit_alarm",
- in_index,
- PB_STATUS_INPUT_BASE,
- PB_VOLTAGE_OV_FAULT);
+/*
+ * The pmbus_limit_attr structure describes a single limit attribute
+ * and its associated alarm attribute.
+ */
+struct pmbus_limit_attr {
+ u8 reg; /* Limit register */
+ const char *attr; /* Attribute name */
+ const char *alarm; /* Alarm attribute name */
+ u32 sbit; /* Alarm attribute status bit */
+};
+
+/*
+ * The pmbus_sensor_attr structure describes one sensor attribute. This
+ * description includes a reference to the associated limit attributes.
+ */
+struct pmbus_sensor_attr {
+ u8 reg; /* sensor register */
+ enum pmbus_sensor_classes class;/* sensor class */
+ const char *label; /* sensor label */
+ bool paged; /* true if paged sensor */
+ bool update; /* true if update needed */
+ bool compare; /* true if compare function needed */
+ u32 func; /* sensor mask */
+ u32 sfunc; /* sensor status mask */
+ int sbase; /* status base register */
+ u32 gbit; /* generic status bit */
+ const struct pmbus_limit_attr *limit;/* limit registers */
+ int nlimit; /* # of limit registers */
+};
+
+/*
+ * Add a set of limit attributes and, if supported, the associated
+ * alarm attributes.
+ */
+static bool pmbus_add_limit_attrs(struct i2c_client *client,
+ struct pmbus_data *data,
+ const struct pmbus_driver_info *info,
+ const char *name, int index, int page,
+ int cbase,
+ const struct pmbus_sensor_attr *attr)
+{
+ const struct pmbus_limit_attr *l = attr->limit;
+ int nlimit = attr->nlimit;
+ bool have_alarm = false;
+ int i, cindex;
+
+ for (i = 0; i < nlimit; i++) {
+ if (pmbus_check_word_register(client, page, l->reg)) {
+ cindex = data->num_sensors;
+ pmbus_add_sensor(data, name, l->attr, index, page,
+ l->reg, attr->class, attr->update,
+ false);
+ if (info->func[page] & attr->sfunc) {
+ if (attr->compare) {
+ pmbus_add_boolean_cmp(data, name,
+ l->alarm, index,
+ cbase, cindex,
+ attr->sbase + page, l->sbit);
+ } else {
+ pmbus_add_boolean_reg(data, name,
+ l->alarm, index,
+ attr->sbase + page, l->sbit);
+ }
have_alarm = true;
}
}
- /*
- * Add generic alarm attribute only if there are no individual
- * attributes.
- */
- if (!have_alarm)
- pmbus_add_boolean_reg(data, "in", "alarm",
- in_index,
- PB_STATUS_BASE,
- PB_STATUS_VIN_UV);
- in_index++;
- }
- if (info->func[0] & PMBUS_HAVE_VCAP) {
- pmbus_add_label(data, "in", in_index, "vcap", 0);
- pmbus_add_sensor(data, "in", "input", in_index, 0,
- PMBUS_READ_VCAP, PSC_VOLTAGE_IN, true, true);
- in_index++;
+ l++;
}
+ return have_alarm;
+}
- /*
- * Output voltage sensors
- */
- for (page = 0; page < info->pages; page++) {
- bool have_alarm = false;
-
- if (!(info->func[page] & PMBUS_HAVE_VOUT))
- continue;
-
- i0 = data->num_sensors;
- pmbus_add_label(data, "in", in_index, "vout", page + 1);
- pmbus_add_sensor(data, "in", "input", in_index, page,
- PMBUS_READ_VOUT, PSC_VOLTAGE_OUT, true, true);
- if (pmbus_check_word_register(client, page,
- PMBUS_VOUT_UV_WARN_LIMIT)) {
- i1 = data->num_sensors;
- pmbus_add_sensor(data, "in", "min", in_index, page,
- PMBUS_VOUT_UV_WARN_LIMIT,
- PSC_VOLTAGE_OUT, false, false);
- if (info->func[page] & PMBUS_HAVE_STATUS_VOUT) {
- pmbus_add_boolean_reg(data, "in", "min_alarm",
- in_index,
- PB_STATUS_VOUT_BASE +
- page,
- PB_VOLTAGE_UV_WARNING);
- have_alarm = true;
- }
- }
- if (pmbus_check_word_register(client, page,
- PMBUS_VOUT_UV_FAULT_LIMIT)) {
- i1 = data->num_sensors;
- pmbus_add_sensor(data, "in", "lcrit", in_index, page,
- PMBUS_VOUT_UV_FAULT_LIMIT,
- PSC_VOLTAGE_OUT, false, false);
- if (info->func[page] & PMBUS_HAVE_STATUS_VOUT) {
- pmbus_add_boolean_reg(data, "in", "lcrit_alarm",
- in_index,
- PB_STATUS_VOUT_BASE +
- page,
- PB_VOLTAGE_UV_FAULT);
- have_alarm = true;
- }
- }
- if (pmbus_check_word_register(client, page,
- PMBUS_VOUT_OV_WARN_LIMIT)) {
- i1 = data->num_sensors;
- pmbus_add_sensor(data, "in", "max", in_index, page,
- PMBUS_VOUT_OV_WARN_LIMIT,
- PSC_VOLTAGE_OUT, false, false);
- if (info->func[page] & PMBUS_HAVE_STATUS_VOUT) {
- pmbus_add_boolean_reg(data, "in", "max_alarm",
- in_index,
- PB_STATUS_VOUT_BASE +
- page,
- PB_VOLTAGE_OV_WARNING);
- have_alarm = true;
- }
- }
- if (pmbus_check_word_register(client, page,
- PMBUS_VOUT_OV_FAULT_LIMIT)) {
- i1 = data->num_sensors;
- pmbus_add_sensor(data, "in", "crit", in_index, page,
- PMBUS_VOUT_OV_FAULT_LIMIT,
- PSC_VOLTAGE_OUT, false, false);
- if (info->func[page] & PMBUS_HAVE_STATUS_VOUT) {
- pmbus_add_boolean_reg(data, "in", "crit_alarm",
- in_index,
- PB_STATUS_VOUT_BASE +
- page,
- PB_VOLTAGE_OV_FAULT);
- have_alarm = true;
- }
- }
+static void pmbus_add_sensor_attrs_one(struct i2c_client *client,
+ struct pmbus_data *data,
+ const struct pmbus_driver_info *info,
+ const char *name,
+ int index, int page,
+ const struct pmbus_sensor_attr *attr)
+{
+ bool have_alarm;
+ int cbase = data->num_sensors;
+
+ if (attr->label)
+ pmbus_add_label(data, name, index, attr->label,
+ attr->paged ? page + 1 : 0);
+ pmbus_add_sensor(data, name, "input", index, page, attr->reg,
+ attr->class, true, true);
+ if (attr->sfunc) {
+ have_alarm = pmbus_add_limit_attrs(client, data, info, name,
+ index, page, cbase, attr);
/*
* Add generic alarm attribute only if there are no individual
- * attributes.
+ * alarm attributes, and if there is a global alarm bit.
*/
- if (!have_alarm)
- pmbus_add_boolean_reg(data, "in", "alarm",
- in_index,
+ if (!have_alarm && attr->gbit)
+ pmbus_add_boolean_reg(data, name, "alarm", index,
PB_STATUS_BASE + page,
- PB_STATUS_VOUT_OV);
- in_index++;
+ attr->gbit);
}
+}
- /*
- * Current sensors
- */
+static void pmbus_add_sensor_attrs(struct i2c_client *client,
+ struct pmbus_data *data,
+ const char *name,
+ const struct pmbus_sensor_attr *attrs,
+ int nattrs)
+{
+ const struct pmbus_driver_info *info = data->info;
+ int index, i;
- /*
- * Input current sensors
- */
- in_index = 1;
- if (info->func[0] & PMBUS_HAVE_IIN) {
- i0 = data->num_sensors;
- pmbus_add_label(data, "curr", in_index, "iin", 0);
- pmbus_add_sensor(data, "curr", "input", in_index, 0,
- PMBUS_READ_IIN, PSC_CURRENT_IN, true, true);
- if (pmbus_check_word_register(client, 0,
- PMBUS_IIN_OC_WARN_LIMIT)) {
- i1 = data->num_sensors;
- pmbus_add_sensor(data, "curr", "max", in_index,
- 0, PMBUS_IIN_OC_WARN_LIMIT,
- PSC_CURRENT_IN, false, false);
- if (info->func[0] & PMBUS_HAVE_STATUS_INPUT) {
- pmbus_add_boolean_reg(data, "curr", "max_alarm",
- in_index,
- PB_STATUS_INPUT_BASE,
- PB_IIN_OC_WARNING);
- }
- }
- if (pmbus_check_word_register(client, 0,
- PMBUS_IIN_OC_FAULT_LIMIT)) {
- i1 = data->num_sensors;
- pmbus_add_sensor(data, "curr", "crit", in_index,
- 0, PMBUS_IIN_OC_FAULT_LIMIT,
- PSC_CURRENT_IN, false, false);
- if (info->func[0] & PMBUS_HAVE_STATUS_INPUT)
- pmbus_add_boolean_reg(data, "curr",
- "crit_alarm",
- in_index,
- PB_STATUS_INPUT_BASE,
- PB_IIN_OC_FAULT);
+ index = 1;
+ for (i = 0; i < nattrs; i++) {
+ int page, pages;
+
+ pages = attrs->paged ? info->pages : 1;
+ for (page = 0; page < pages; page++) {
+ if (!(info->func[page] & attrs->func))
+ continue;
+ pmbus_add_sensor_attrs_one(client, data, info, name,
+ index, page, attrs);
+ index++;
}
- in_index++;
+ attrs++;
}
+}
- /*
- * Output current sensors
- */
- for (page = 0; page < info->pages; page++) {
- bool have_alarm = false;
-
- if (!(info->func[page] & PMBUS_HAVE_IOUT))
- continue;
-
- i0 = data->num_sensors;
- pmbus_add_label(data, "curr", in_index, "iout", page + 1);
- pmbus_add_sensor(data, "curr", "input", in_index, page,
- PMBUS_READ_IOUT, PSC_CURRENT_OUT, true, true);
- if (pmbus_check_word_register(client, page,
- PMBUS_IOUT_OC_WARN_LIMIT)) {
- i1 = data->num_sensors;
- pmbus_add_sensor(data, "curr", "max", in_index, page,
- PMBUS_IOUT_OC_WARN_LIMIT,
- PSC_CURRENT_OUT, false, false);
- if (info->func[page] & PMBUS_HAVE_STATUS_IOUT) {
- pmbus_add_boolean_reg(data, "curr", "max_alarm",
- in_index,
- PB_STATUS_IOUT_BASE +
- page, PB_IOUT_OC_WARNING);
- have_alarm = true;
- }
- }
- if (pmbus_check_word_register(client, page,
- PMBUS_IOUT_UC_FAULT_LIMIT)) {
- i1 = data->num_sensors;
- pmbus_add_sensor(data, "curr", "lcrit", in_index, page,
- PMBUS_IOUT_UC_FAULT_LIMIT,
- PSC_CURRENT_OUT, false, false);
- if (info->func[page] & PMBUS_HAVE_STATUS_IOUT) {
- pmbus_add_boolean_reg(data, "curr",
- "lcrit_alarm",
- in_index,
- PB_STATUS_IOUT_BASE +
- page, PB_IOUT_UC_FAULT);
- have_alarm = true;
- }
- }
- if (pmbus_check_word_register(client, page,
- PMBUS_IOUT_OC_FAULT_LIMIT)) {
- i1 = data->num_sensors;
- pmbus_add_sensor(data, "curr", "crit", in_index, page,
- PMBUS_IOUT_OC_FAULT_LIMIT,
- PSC_CURRENT_OUT, false, false);
- if (info->func[page] & PMBUS_HAVE_STATUS_IOUT) {
- pmbus_add_boolean_reg(data, "curr",
- "crit_alarm",
- in_index,
- PB_STATUS_IOUT_BASE +
- page, PB_IOUT_OC_FAULT);
- have_alarm = true;
- }
- }
- /*
- * Add generic alarm attribute only if there are no individual
- * attributes.
- */
- if (!have_alarm)
- pmbus_add_boolean_reg(data, "curr", "alarm",
- in_index,
- PB_STATUS_BASE + page,
- PB_STATUS_IOUT_OC);
- in_index++;
+static const struct pmbus_limit_attr vin_limit_attrs[] = {
+ {
+ .reg = PMBUS_VIN_UV_WARN_LIMIT,
+ .attr = "min",
+ .alarm = "min_alarm",
+ .sbit = PB_VOLTAGE_UV_WARNING,
+ }, {
+ .reg = PMBUS_VIN_UV_FAULT_LIMIT,
+ .attr = "lcrit",
+ .alarm = "lcrit_alarm",
+ .sbit = PB_VOLTAGE_UV_FAULT,
+ }, {
+ .reg = PMBUS_VIN_OV_WARN_LIMIT,
+ .attr = "max",
+ .alarm = "max_alarm",
+ .sbit = PB_VOLTAGE_OV_WARNING,
+ }, {
+ .reg = PMBUS_VIN_OV_FAULT_LIMIT,
+ .attr = "crit",
+ .alarm = "crit_alarm",
+ .sbit = PB_VOLTAGE_OV_FAULT,
+ },
+};
+
+static const struct pmbus_limit_attr vout_limit_attrs[] = {
+ {
+ .reg = PMBUS_VOUT_UV_WARN_LIMIT,
+ .attr = "min",
+ .alarm = "min_alarm",
+ .sbit = PB_VOLTAGE_UV_WARNING,
+ }, {
+ .reg = PMBUS_VOUT_UV_FAULT_LIMIT,
+ .attr = "lcrit",
+ .alarm = "lcrit_alarm",
+ .sbit = PB_VOLTAGE_UV_FAULT,
+ }, {
+ .reg = PMBUS_VOUT_OV_WARN_LIMIT,
+ .attr = "max",
+ .alarm = "max_alarm",
+ .sbit = PB_VOLTAGE_OV_WARNING,
+ }, {
+ .reg = PMBUS_VOUT_OV_FAULT_LIMIT,
+ .attr = "crit",
+ .alarm = "crit_alarm",
+ .sbit = PB_VOLTAGE_OV_FAULT,
}
+};
- /*
- * Power sensors
- */
- /*
- * Input Power sensors
- */
- in_index = 1;
- if (info->func[0] & PMBUS_HAVE_PIN) {
- i0 = data->num_sensors;
- pmbus_add_label(data, "power", in_index, "pin", 0);
- pmbus_add_sensor(data, "power", "input", in_index,
- 0, PMBUS_READ_PIN, PSC_POWER, true, true);
- if (pmbus_check_word_register(client, 0,
- PMBUS_PIN_OP_WARN_LIMIT)) {
- i1 = data->num_sensors;
- pmbus_add_sensor(data, "power", "max", in_index,
- 0, PMBUS_PIN_OP_WARN_LIMIT, PSC_POWER,
- false, false);
- if (info->func[0] & PMBUS_HAVE_STATUS_INPUT)
- pmbus_add_boolean_reg(data, "power",
- "alarm",
- in_index,
- PB_STATUS_INPUT_BASE,
- PB_PIN_OP_WARNING);
- }
- in_index++;
+static const struct pmbus_sensor_attr voltage_attributes[] = {
+ {
+ .reg = PMBUS_READ_VIN,
+ .class = PSC_VOLTAGE_IN,
+ .label = "vin",
+ .func = PMBUS_HAVE_VIN,
+ .sfunc = PMBUS_HAVE_STATUS_INPUT,
+ .sbase = PB_STATUS_INPUT_BASE,
+ .gbit = PB_STATUS_VIN_UV,
+ .limit = vin_limit_attrs,
+ .nlimit = ARRAY_SIZE(vin_limit_attrs),
+ }, {
+ .reg = PMBUS_READ_VCAP,
+ .class = PSC_VOLTAGE_IN,
+ .label = "vcap",
+ .func = PMBUS_HAVE_VCAP,
+ }, {
+ .reg = PMBUS_READ_VOUT,
+ .class = PSC_VOLTAGE_OUT,
+ .label = "vout",
+ .paged = true,
+ .func = PMBUS_HAVE_VOUT,
+ .sfunc = PMBUS_HAVE_STATUS_VOUT,
+ .sbase = PB_STATUS_VOUT_BASE,
+ .gbit = PB_STATUS_VOUT_OV,
+ .limit = vout_limit_attrs,
+ .nlimit = ARRAY_SIZE(vout_limit_attrs),
}
+};
- /*
- * Output Power sensors
- */
- for (page = 0; page < info->pages; page++) {
- bool need_alarm = false;
+/* Current attributes */
+
+static const struct pmbus_limit_attr iin_limit_attrs[] = {
+ {
+ .reg = PMBUS_IIN_OC_WARN_LIMIT,
+ .attr = "max",
+ .alarm = "max_alarm",
+ .sbit = PB_IIN_OC_WARNING,
+ }, {
+ .reg = PMBUS_IIN_OC_FAULT_LIMIT,
+ .attr = "crit",
+ .alarm = "crit_alarm",
+ .sbit = PB_IIN_OC_FAULT,
+ }
+};
- if (!(info->func[page] & PMBUS_HAVE_POUT))
- continue;
+static const struct pmbus_limit_attr iout_limit_attrs[] = {
+ {
+ .reg = PMBUS_IOUT_OC_WARN_LIMIT,
+ .attr = "max",
+ .alarm = "max_alarm",
+ .sbit = PB_IOUT_OC_WARNING,
+ }, {
+ .reg = PMBUS_IOUT_UC_FAULT_LIMIT,
+ .attr = "lcrit",
+ .alarm = "lcrit_alarm",
+ .sbit = PB_IOUT_UC_FAULT,
+ }, {
+ .reg = PMBUS_IOUT_OC_FAULT_LIMIT,
+ .attr = "crit",
+ .alarm = "crit_alarm",
+ .sbit = PB_IOUT_OC_FAULT,
+ }
+};
- i0 = data->num_sensors;
- pmbus_add_label(data, "power", in_index, "pout", page + 1);
- pmbus_add_sensor(data, "power", "input", in_index, page,
- PMBUS_READ_POUT, PSC_POWER, true, true);
- /*
- * Per hwmon sysfs API, power_cap is to be used to limit output
- * power.
- * We have two registers related to maximum output power,
- * PMBUS_POUT_MAX and PMBUS_POUT_OP_WARN_LIMIT.
- * PMBUS_POUT_MAX matches the powerX_cap attribute definition.
- * There is no attribute in the API to match
- * PMBUS_POUT_OP_WARN_LIMIT. We use powerX_max for now.
- */
- if (pmbus_check_word_register(client, page, PMBUS_POUT_MAX)) {
- i1 = data->num_sensors;
- pmbus_add_sensor(data, "power", "cap", in_index, page,
- PMBUS_POUT_MAX, PSC_POWER,
- false, false);
- need_alarm = true;
- }
- if (pmbus_check_word_register(client, page,
- PMBUS_POUT_OP_WARN_LIMIT)) {
- i1 = data->num_sensors;
- pmbus_add_sensor(data, "power", "max", in_index, page,
- PMBUS_POUT_OP_WARN_LIMIT, PSC_POWER,
- false, false);
- need_alarm = true;
- }
- if (need_alarm && (info->func[page] & PMBUS_HAVE_STATUS_IOUT))
- pmbus_add_boolean_reg(data, "power", "alarm",
- in_index,
- PB_STATUS_IOUT_BASE + page,
- PB_POUT_OP_WARNING
- | PB_POWER_LIMITING);
-
- if (pmbus_check_word_register(client, page,
- PMBUS_POUT_OP_FAULT_LIMIT)) {
- i1 = data->num_sensors;
- pmbus_add_sensor(data, "power", "crit", in_index, page,
- PMBUS_POUT_OP_FAULT_LIMIT, PSC_POWER,
- false, false);
- if (info->func[page] & PMBUS_HAVE_STATUS_IOUT)
- pmbus_add_boolean_reg(data, "power",
- "crit_alarm",
- in_index,
- PB_STATUS_IOUT_BASE
- + page,
- PB_POUT_OP_FAULT);
- }
- in_index++;
+static const struct pmbus_sensor_attr current_attributes[] = {
+ {
+ .reg = PMBUS_READ_IIN,
+ .class = PSC_CURRENT_IN,
+ .label = "iin",
+ .func = PMBUS_HAVE_IIN,
+ .sfunc = PMBUS_HAVE_STATUS_INPUT,
+ .sbase = PB_STATUS_INPUT_BASE,
+ .limit = iin_limit_attrs,
+ .nlimit = ARRAY_SIZE(iin_limit_attrs),
+ }, {
+ .reg = PMBUS_READ_IOUT,
+ .class = PSC_CURRENT_OUT,
+ .label = "iout",
+ .paged = true,
+ .func = PMBUS_HAVE_IOUT,
+ .sfunc = PMBUS_HAVE_STATUS_IOUT,
+ .sbase = PB_STATUS_IOUT_BASE,
+ .gbit = PB_STATUS_IOUT_OC,
+ .limit = iout_limit_attrs,
+ .nlimit = ARRAY_SIZE(iout_limit_attrs),
}
+};
- /*
- * Temperature sensors
- */
- in_index = 1;
- for (page = 0; page < info->pages; page++) {
- int t;
+/* Power attributes */
- for (t = 0; t < ARRAY_SIZE(pmbus_temp_registers); t++) {
- bool have_alarm = false;
+static const struct pmbus_limit_attr pin_limit_attrs[] = {
+ {
+ .reg = PMBUS_PIN_OP_WARN_LIMIT,
+ .attr = "max",
+ .alarm = "alarm",
+ .sbit = PB_PIN_OP_WARNING,
+ }
+};
- /*
- * A PMBus chip may support any combination of
- * temperature registers on any page. So we can not
- * abort after a failure to detect a register, but have
- * to continue checking for all registers on all pages.
- */
- if (!(info->func[page] & pmbus_temp_flags[t]))
- continue;
+static const struct pmbus_limit_attr pout_limit_attrs[] = {
+ {
+ .reg = PMBUS_POUT_MAX,
+ .attr = "cap",
+ .alarm = "cap_alarm",
+ .sbit = PB_POWER_LIMITING,
+ }, {
+ .reg = PMBUS_POUT_OP_WARN_LIMIT,
+ .attr = "max",
+ .alarm = "max_alarm",
+ .sbit = PB_POUT_OP_WARNING,
+ }, {
+ .reg = PMBUS_POUT_OP_FAULT_LIMIT,
+ .attr = "crit",
+ .alarm = "crit_alarm",
+ .sbit = PB_POUT_OP_FAULT,
+ }
+};
- if (!pmbus_check_word_register
- (client, page, pmbus_temp_registers[t]))
- continue;
+static const struct pmbus_sensor_attr power_attributes[] = {
+ {
+ .reg = PMBUS_READ_PIN,
+ .class = PSC_POWER,
+ .label = "pin",
+ .func = PMBUS_HAVE_PIN,
+ .sfunc = PMBUS_HAVE_STATUS_INPUT,
+ .sbase = PB_STATUS_INPUT_BASE,
+ .limit = pin_limit_attrs,
+ .nlimit = ARRAY_SIZE(pin_limit_attrs),
+ }, {
+ .reg = PMBUS_READ_POUT,
+ .class = PSC_POWER,
+ .label = "pout",
+ .paged = true,
+ .func = PMBUS_HAVE_POUT,
+ .sfunc = PMBUS_HAVE_STATUS_IOUT,
+ .sbase = PB_STATUS_IOUT_BASE,
+ .limit = pout_limit_attrs,
+ .nlimit = ARRAY_SIZE(pout_limit_attrs),
+ }
+};
- i0 = data->num_sensors;
- pmbus_add_sensor(data, "temp", "input", in_index, page,
- pmbus_temp_registers[t],
- PSC_TEMPERATURE, true, true);
+/* Temperature atributes */
+
+static const struct pmbus_limit_attr temp_limit_attrs[] = {
+ {
+ .reg = PMBUS_UT_WARN_LIMIT,
+ .attr = "min",
+ .alarm = "min_alarm",
+ .sbit = PB_TEMP_UT_WARNING,
+ }, {
+ .reg = PMBUS_UT_FAULT_LIMIT,
+ .attr = "lcrit",
+ .alarm = "lcrit_alarm",
+ .sbit = PB_TEMP_UT_FAULT,
+ }, {
+ .reg = PMBUS_OT_WARN_LIMIT,
+ .attr = "max",
+ .alarm = "max_alarm",
+ .sbit = PB_TEMP_OT_WARNING,
+ }, {
+ .reg = PMBUS_OT_FAULT_LIMIT,
+ .attr = "crit",
+ .alarm = "crit_alarm",
+ .sbit = PB_TEMP_OT_FAULT,
+ }
+};
- /*
- * PMBus provides only one status register for TEMP1-3.
- * Thus, we can not use the status register to determine
- * which of the three sensors actually caused an alarm.
- * Always compare current temperature against the limit
- * registers to determine alarm conditions for a
- * specific sensor.
- *
- * Since there is only one set of limit registers for
- * up to three temperature sensors, we need to update
- * all limit registers after the limit was changed for
- * one of the sensors. This ensures that correct limits
- * are reported for all temperature sensors.
- */
- if (pmbus_check_word_register
- (client, page, PMBUS_UT_WARN_LIMIT)) {
- i1 = data->num_sensors;
- pmbus_add_sensor(data, "temp", "min", in_index,
- page, PMBUS_UT_WARN_LIMIT,
- PSC_TEMPERATURE, true, false);
- if (info->func[page] & PMBUS_HAVE_STATUS_TEMP) {
- pmbus_add_boolean_cmp(data, "temp",
- "min_alarm", in_index, i1, i0,
- PB_STATUS_TEMP_BASE + page,
- PB_TEMP_UT_WARNING);
- have_alarm = true;
- }
- }
- if (pmbus_check_word_register(client, page,
- PMBUS_UT_FAULT_LIMIT)) {
- i1 = data->num_sensors;
- pmbus_add_sensor(data, "temp", "lcrit",
- in_index, page,
- PMBUS_UT_FAULT_LIMIT,
- PSC_TEMPERATURE, true, false);
- if (info->func[page] & PMBUS_HAVE_STATUS_TEMP) {
- pmbus_add_boolean_cmp(data, "temp",
- "lcrit_alarm", in_index, i1, i0,
- PB_STATUS_TEMP_BASE + page,
- PB_TEMP_UT_FAULT);
- have_alarm = true;
- }
- }
- if (pmbus_check_word_register
- (client, page, PMBUS_OT_WARN_LIMIT)) {
- i1 = data->num_sensors;
- pmbus_add_sensor(data, "temp", "max", in_index,
- page, PMBUS_OT_WARN_LIMIT,
- PSC_TEMPERATURE, true, false);
- if (info->func[page] & PMBUS_HAVE_STATUS_TEMP) {
- pmbus_add_boolean_cmp(data, "temp",
- "max_alarm", in_index, i0, i1,
- PB_STATUS_TEMP_BASE + page,
- PB_TEMP_OT_WARNING);
- have_alarm = true;
- }
- }
- if (pmbus_check_word_register(client, page,
- PMBUS_OT_FAULT_LIMIT)) {
- i1 = data->num_sensors;
- pmbus_add_sensor(data, "temp", "crit", in_index,
- page, PMBUS_OT_FAULT_LIMIT,
- PSC_TEMPERATURE, true, false);
- if (info->func[page] & PMBUS_HAVE_STATUS_TEMP) {
- pmbus_add_boolean_cmp(data, "temp",
- "crit_alarm", in_index, i0, i1,
- PB_STATUS_TEMP_BASE + page,
- PB_TEMP_OT_FAULT);
- have_alarm = true;
- }
- }
- /*
- * Last resort - we were not able to create any alarm
- * registers. Report alarm for all sensors using the
- * status register temperature alarm bit.
- */
- if (!have_alarm)
- pmbus_add_boolean_reg(data, "temp", "alarm",
- in_index,
- PB_STATUS_BASE + page,
- PB_STATUS_TEMPERATURE);
- in_index++;
- }
+static const struct pmbus_sensor_attr temp_attributes[] = {
+ {
+ .reg = PMBUS_READ_TEMPERATURE_1,
+ .class = PSC_TEMPERATURE,
+ .paged = true,
+ .update = true,
+ .compare = true,
+ .func = PMBUS_HAVE_TEMP,
+ .sfunc = PMBUS_HAVE_STATUS_TEMP,
+ .sbase = PB_STATUS_TEMP_BASE,
+ .gbit = PB_STATUS_TEMPERATURE,
+ .limit = temp_limit_attrs,
+ .nlimit = ARRAY_SIZE(temp_limit_attrs),
+ }, {
+ .reg = PMBUS_READ_TEMPERATURE_2,
+ .class = PSC_TEMPERATURE,
+ .paged = true,
+ .update = true,
+ .compare = true,
+ .func = PMBUS_HAVE_TEMP2,
+ .sfunc = PMBUS_HAVE_STATUS_TEMP,
+ .sbase = PB_STATUS_TEMP_BASE,
+ .gbit = PB_STATUS_TEMPERATURE,
+ .limit = temp_limit_attrs,
+ .nlimit = ARRAY_SIZE(temp_limit_attrs),
+ }, {
+ .reg = PMBUS_READ_TEMPERATURE_3,
+ .class = PSC_TEMPERATURE,
+ .paged = true,
+ .update = true,
+ .compare = true,
+ .func = PMBUS_HAVE_TEMP3,
+ .sfunc = PMBUS_HAVE_STATUS_TEMP,
+ .sbase = PB_STATUS_TEMP_BASE,
+ .gbit = PB_STATUS_TEMPERATURE,
+ .limit = temp_limit_attrs,
+ .nlimit = ARRAY_SIZE(temp_limit_attrs),
}
+};
+
+static const int pmbus_fan_registers[] = {
+ PMBUS_READ_FAN_SPEED_1,
+ PMBUS_READ_FAN_SPEED_2,
+ PMBUS_READ_FAN_SPEED_3,
+ PMBUS_READ_FAN_SPEED_4
+};
+
+static const int pmbus_fan_config_registers[] = {
+ PMBUS_FAN_CONFIG_12,
+ PMBUS_FAN_CONFIG_12,
+ PMBUS_FAN_CONFIG_34,
+ PMBUS_FAN_CONFIG_34
+};
+
+static const int pmbus_fan_status_registers[] = {
+ PMBUS_STATUS_FAN_12,
+ PMBUS_STATUS_FAN_12,
+ PMBUS_STATUS_FAN_34,
+ PMBUS_STATUS_FAN_34
+};
+
+static const u32 pmbus_fan_flags[] = {
+ PMBUS_HAVE_FAN12,
+ PMBUS_HAVE_FAN12,
+ PMBUS_HAVE_FAN34,
+ PMBUS_HAVE_FAN34
+};
+
+static const u32 pmbus_fan_status_flags[] = {
+ PMBUS_HAVE_STATUS_FAN12,
+ PMBUS_HAVE_STATUS_FAN12,
+ PMBUS_HAVE_STATUS_FAN34,
+ PMBUS_HAVE_STATUS_FAN34
+};
+
+/* Fans */
+static void pmbus_add_fan_attributes(struct i2c_client *client,
+ struct pmbus_data *data)
+{
+ const struct pmbus_driver_info *info = data->info;
+ int index = 1;
+ int page;
- /*
- * Fans
- */
- in_index = 1;
for (page = 0; page < info->pages; page++) {
int f;
@@ -1403,9 +1295,7 @@ static void pmbus_find_attributes(struct i2c_client *client,
break;
if (!pmbus_check_word_register(client, page,
- pmbus_fan_registers[f])
- || !pmbus_check_byte_register(client, page,
- pmbus_fan_config_registers[f]))
+ pmbus_fan_registers[f]))
break;
/*
@@ -1413,14 +1303,13 @@ static void pmbus_find_attributes(struct i2c_client *client,
* Each fan configuration register covers multiple fans,
* so we have to do some magic.
*/
- regval = pmbus_read_byte_data(client, page,
+ regval = _pmbus_read_byte_data(client, page,
pmbus_fan_config_registers[f]);
if (regval < 0 ||
(!(regval & (PB_FAN_1_INSTALLED >> ((f & 1) * 4)))))
continue;
- i0 = data->num_sensors;
- pmbus_add_sensor(data, "fan", "input", in_index, page,
+ pmbus_add_sensor(data, "fan", "input", index, page,
pmbus_fan_registers[f], PSC_FAN, true,
true);
@@ -1438,17 +1327,40 @@ static void pmbus_find_attributes(struct i2c_client *client,
else
base = PB_STATUS_FAN_BASE + page;
pmbus_add_boolean_reg(data, "fan", "alarm",
- in_index, base,
+ index, base,
PB_FAN_FAN1_WARNING >> (f & 1));
pmbus_add_boolean_reg(data, "fan", "fault",
- in_index, base,
+ index, base,
PB_FAN_FAN1_FAULT >> (f & 1));
}
- in_index++;
+ index++;
}
}
}
+static void pmbus_find_attributes(struct i2c_client *client,
+ struct pmbus_data *data)
+{
+ /* Voltage sensors */
+ pmbus_add_sensor_attrs(client, data, "in", voltage_attributes,
+ ARRAY_SIZE(voltage_attributes));
+
+ /* Current sensors */
+ pmbus_add_sensor_attrs(client, data, "curr", current_attributes,
+ ARRAY_SIZE(current_attributes));
+
+ /* Power sensors */
+ pmbus_add_sensor_attrs(client, data, "power", power_attributes,
+ ARRAY_SIZE(power_attributes));
+
+ /* Temperature sensors */
+ pmbus_add_sensor_attrs(client, data, "temp", temp_attributes,
+ ARRAY_SIZE(temp_attributes));
+
+ /* Fans */
+ pmbus_add_fan_attributes(client, data);
+}
+
/*
* Identify chip parameters.
* This function is called for all chips.
diff --git a/drivers/hwmon/sht15.c b/drivers/hwmon/sht15.c
index f4e617adb220..cf4330b352ef 100644
--- a/drivers/hwmon/sht15.c
+++ b/drivers/hwmon/sht15.c
@@ -1,6 +1,10 @@
/*
* sht15.c - support for the SHT15 Temperature and Humidity Sensor
*
+ * Portions Copyright (c) 2010-2011 Savoir-faire Linux Inc.
+ * Jerome Oufella <jerome.oufella@savoirfairelinux.com>
+ * Vivien Didelot <vivien.didelot@savoirfairelinux.com>
+ *
* Copyright (c) 2009 Jonathan Cameron
*
* Copyright (c) 2007 Wouter Horre
@@ -9,16 +13,7 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
- * Currently ignoring checksum on readings.
- * Default resolution only (14bit temp, 12bit humidity)
- * Ignoring battery status.
- * Heater not enabled.
- * Timings are all conservative.
- *
- * Data sheet available (1/2009) at
- * http://www.sensirion.ch/en/pdf/product_information/Datasheet-humidity-sensor-SHT1x.pdf
- *
- * Regulator supply name = vcc
+ * For further information, see the Documentation/hwmon/sht15 file.
*/
#include <linux/interrupt.h>
@@ -39,17 +34,31 @@
#include <linux/slab.h>
#include <asm/atomic.h>
-#define SHT15_MEASURE_TEMP 3
-#define SHT15_MEASURE_RH 5
-
-#define SHT15_READING_NOTHING 0
-#define SHT15_READING_TEMP 1
-#define SHT15_READING_HUMID 2
-
-/* Min timings in nsecs */
-#define SHT15_TSCKL 100 /* clock low */
-#define SHT15_TSCKH 100 /* clock high */
-#define SHT15_TSU 150 /* data setup time */
+/* Commands */
+#define SHT15_MEASURE_TEMP 0x03
+#define SHT15_MEASURE_RH 0x05
+#define SHT15_WRITE_STATUS 0x06
+#define SHT15_READ_STATUS 0x07
+#define SHT15_SOFT_RESET 0x1E
+
+/* Min timings */
+#define SHT15_TSCKL 100 /* (nsecs) clock low */
+#define SHT15_TSCKH 100 /* (nsecs) clock high */
+#define SHT15_TSU 150 /* (nsecs) data setup time */
+#define SHT15_TSRST 11 /* (msecs) soft reset time */
+
+/* Status Register Bits */
+#define SHT15_STATUS_LOW_RESOLUTION 0x01
+#define SHT15_STATUS_NO_OTP_RELOAD 0x02
+#define SHT15_STATUS_HEATER 0x04
+#define SHT15_STATUS_LOW_BATTERY 0x40
+
+/* Actions the driver may be doing */
+enum sht15_state {
+ SHT15_READING_NOTHING,
+ SHT15_READING_TEMP,
+ SHT15_READING_HUMID
+};
/**
* struct sht15_temppair - elements of voltage dependent temp calc
@@ -61,9 +70,7 @@ struct sht15_temppair {
int d1;
};
-/* Table 9 from data sheet - relates temperature calculation
- * to supply voltage.
- */
+/* Table 9 from datasheet - relates temperature calculation to supply voltage */
static const struct sht15_temppair temppoints[] = {
{ 2500000, -39400 },
{ 3000000, -39600 },
@@ -72,29 +79,70 @@ static const struct sht15_temppair temppoints[] = {
{ 5000000, -40100 },
};
+/* Table from CRC datasheet, section 2.4 */
+static const u8 sht15_crc8_table[] = {
+ 0, 49, 98, 83, 196, 245, 166, 151,
+ 185, 136, 219, 234, 125, 76, 31, 46,
+ 67, 114, 33, 16, 135, 182, 229, 212,
+ 250, 203, 152, 169, 62, 15, 92, 109,
+ 134, 183, 228, 213, 66, 115, 32, 17,
+ 63, 14, 93, 108, 251, 202, 153, 168,
+ 197, 244, 167, 150, 1, 48, 99, 82,
+ 124, 77, 30, 47, 184, 137, 218, 235,
+ 61, 12, 95, 110, 249, 200, 155, 170,
+ 132, 181, 230, 215, 64, 113, 34, 19,
+ 126, 79, 28, 45, 186, 139, 216, 233,
+ 199, 246, 165, 148, 3, 50, 97, 80,
+ 187, 138, 217, 232, 127, 78, 29, 44,
+ 2, 51, 96, 81, 198, 247, 164, 149,
+ 248, 201, 154, 171, 60, 13, 94, 111,
+ 65, 112, 35, 18, 133, 180, 231, 214,
+ 122, 75, 24, 41, 190, 143, 220, 237,
+ 195, 242, 161, 144, 7, 54, 101, 84,
+ 57, 8, 91, 106, 253, 204, 159, 174,
+ 128, 177, 226, 211, 68, 117, 38, 23,
+ 252, 205, 158, 175, 56, 9, 90, 107,
+ 69, 116, 39, 22, 129, 176, 227, 210,
+ 191, 142, 221, 236, 123, 74, 25, 40,
+ 6, 55, 100, 85, 194, 243, 160, 145,
+ 71, 118, 37, 20, 131, 178, 225, 208,
+ 254, 207, 156, 173, 58, 11, 88, 105,
+ 4, 53, 102, 87, 192, 241, 162, 147,
+ 189, 140, 223, 238, 121, 72, 27, 42,
+ 193, 240, 163, 146, 5, 52, 103, 86,
+ 120, 73, 26, 43, 188, 141, 222, 239,
+ 130, 179, 224, 209, 70, 119, 36, 21,
+ 59, 10, 89, 104, 255, 206, 157, 172
+};
+
/**
* struct sht15_data - device instance specific data
- * @pdata: platform data (gpio's etc)
- * @read_work: bh of interrupt handler
- * @wait_queue: wait queue for getting values from device
- * @val_temp: last temperature value read from device
- * @val_humid: last humidity value read from device
- * @flag: status flag used to identify what the last request was
- * @valid: are the current stored values valid (start condition)
- * @last_updat: time of last update
- * @read_lock: mutex to ensure only one read in progress
- * at a time.
- * @dev: associate device structure
- * @hwmon_dev: device associated with hwmon subsystem
- * @reg: associated regulator (if specified)
- * @nb: notifier block to handle notifications of voltage changes
- * @supply_uV: local copy of supply voltage used to allow
- * use of regulator consumer if available
- * @supply_uV_valid: indicates that an updated value has not yet
- * been obtained from the regulator and so any calculations
- * based upon it will be invalid.
- * @update_supply_work: work struct that is used to update the supply_uV
- * @interrupt_handled: flag used to indicate a hander has been scheduled
+ * @pdata: platform data (gpio's etc).
+ * @read_work: bh of interrupt handler.
+ * @wait_queue: wait queue for getting values from device.
+ * @val_temp: last temperature value read from device.
+ * @val_humid: last humidity value read from device.
+ * @val_status: last status register value read from device.
+ * @checksum_ok: last value read from the device passed CRC validation.
+ * @checksumming: flag used to enable the data validation with CRC.
+ * @state: state identifying the action the driver is doing.
+ * @measurements_valid: are the current stored measures valid (start condition).
+ * @status_valid: is the current stored status valid (start condition).
+ * @last_measurement: time of last measure.
+ * @last_status: time of last status reading.
+ * @read_lock: mutex to ensure only one read in progress at a time.
+ * @dev: associate device structure.
+ * @hwmon_dev: device associated with hwmon subsystem.
+ * @reg: associated regulator (if specified).
+ * @nb: notifier block to handle notifications of voltage
+ * changes.
+ * @supply_uV: local copy of supply voltage used to allow use of
+ * regulator consumer if available.
+ * @supply_uV_valid: indicates that an updated value has not yet been
+ * obtained from the regulator and so any calculations
+ * based upon it will be invalid.
+ * @update_supply_work: work struct that is used to update the supply_uV.
+ * @interrupt_handled: flag used to indicate a handler has been scheduled.
*/
struct sht15_data {
struct sht15_platform_data *pdata;
@@ -102,21 +150,60 @@ struct sht15_data {
wait_queue_head_t wait_queue;
uint16_t val_temp;
uint16_t val_humid;
- u8 flag;
- u8 valid;
- unsigned long last_updat;
+ u8 val_status;
+ bool checksum_ok;
+ bool checksumming;
+ enum sht15_state state;
+ bool measurements_valid;
+ bool status_valid;
+ unsigned long last_measurement;
+ unsigned long last_status;
struct mutex read_lock;
struct device *dev;
struct device *hwmon_dev;
struct regulator *reg;
struct notifier_block nb;
int supply_uV;
- int supply_uV_valid;
+ bool supply_uV_valid;
struct work_struct update_supply_work;
atomic_t interrupt_handled;
};
/**
+ * sht15_reverse() - reverse a byte
+ * @byte: byte to reverse.
+ */
+static u8 sht15_reverse(u8 byte)
+{
+ u8 i, c;
+
+ for (c = 0, i = 0; i < 8; i++)
+ c |= (!!(byte & (1 << i))) << (7 - i);
+ return c;
+}
+
+/**
+ * sht15_crc8() - compute crc8
+ * @data: sht15 specific data.
+ * @value: sht15 retrieved data.
+ *
+ * This implements section 2 of the CRC datasheet.
+ */
+static u8 sht15_crc8(struct sht15_data *data,
+ const u8 *value,
+ int len)
+{
+ u8 crc = sht15_reverse(data->val_status & 0x0F);
+
+ while (len--) {
+ crc = sht15_crc8_table[*value ^ crc];
+ value++;
+ }
+
+ return crc;
+}
+
+/**
* sht15_connection_reset() - reset the comms interface
* @data: sht15 specific data
*
@@ -125,6 +212,7 @@ struct sht15_data {
static void sht15_connection_reset(struct sht15_data *data)
{
int i;
+
gpio_direction_output(data->pdata->gpio_data, 1);
ndelay(SHT15_TSCKL);
gpio_set_value(data->pdata->gpio_sck, 0);
@@ -136,14 +224,14 @@ static void sht15_connection_reset(struct sht15_data *data)
ndelay(SHT15_TSCKL);
}
}
+
/**
* sht15_send_bit() - send an individual bit to the device
* @data: device state data
* @val: value of bit to be sent
- **/
+ */
static inline void sht15_send_bit(struct sht15_data *data, int val)
{
-
gpio_set_value(data->pdata->gpio_data, val);
ndelay(SHT15_TSU);
gpio_set_value(data->pdata->gpio_sck, 1);
@@ -154,12 +242,12 @@ static inline void sht15_send_bit(struct sht15_data *data, int val)
/**
* sht15_transmission_start() - specific sequence for new transmission
- *
* @data: device state data
+ *
* Timings for this are not documented on the data sheet, so very
* conservative ones used in implementation. This implements
* figure 12 on the data sheet.
- **/
+ */
static void sht15_transmission_start(struct sht15_data *data)
{
/* ensure data is high and output */
@@ -180,23 +268,26 @@ static void sht15_transmission_start(struct sht15_data *data)
gpio_set_value(data->pdata->gpio_sck, 0);
ndelay(SHT15_TSCKL);
}
+
/**
* sht15_send_byte() - send a single byte to the device
* @data: device state
* @byte: value to be sent
- **/
+ */
static void sht15_send_byte(struct sht15_data *data, u8 byte)
{
int i;
+
for (i = 0; i < 8; i++) {
sht15_send_bit(data, !!(byte & 0x80));
byte <<= 1;
}
}
+
/**
* sht15_wait_for_response() - checks for ack from device
* @data: device state
- **/
+ */
static int sht15_wait_for_response(struct sht15_data *data)
{
gpio_direction_input(data->pdata->gpio_data);
@@ -220,27 +311,199 @@ static int sht15_wait_for_response(struct sht15_data *data)
*
* On entry, sck is output low, data is output pull high
* and the interrupt disabled.
- **/
+ */
static int sht15_send_cmd(struct sht15_data *data, u8 cmd)
{
int ret = 0;
+
sht15_transmission_start(data);
sht15_send_byte(data, cmd);
ret = sht15_wait_for_response(data);
return ret;
}
+
+/**
+ * sht15_soft_reset() - send a soft reset command
+ * @data: sht15 specific data.
+ *
+ * As described in section 3.2 of the datasheet.
+ */
+static int sht15_soft_reset(struct sht15_data *data)
+{
+ int ret;
+
+ ret = sht15_send_cmd(data, SHT15_SOFT_RESET);
+ if (ret)
+ return ret;
+ msleep(SHT15_TSRST);
+ /* device resets default hardware status register value */
+ data->val_status = 0;
+
+ return ret;
+}
+
+/**
+ * sht15_ack() - send a ack
+ * @data: sht15 specific data.
+ *
+ * Each byte of data is acknowledged by pulling the data line
+ * low for one clock pulse.
+ */
+static void sht15_ack(struct sht15_data *data)
+{
+ gpio_direction_output(data->pdata->gpio_data, 0);
+ ndelay(SHT15_TSU);
+ gpio_set_value(data->pdata->gpio_sck, 1);
+ ndelay(SHT15_TSU);
+ gpio_set_value(data->pdata->gpio_sck, 0);
+ ndelay(SHT15_TSU);
+ gpio_set_value(data->pdata->gpio_data, 1);
+
+ gpio_direction_input(data->pdata->gpio_data);
+}
+
+/**
+ * sht15_end_transmission() - notify device of end of transmission
+ * @data: device state.
+ *
+ * This is basically a NAK (single clock pulse, data high).
+ */
+static void sht15_end_transmission(struct sht15_data *data)
+{
+ gpio_direction_output(data->pdata->gpio_data, 1);
+ ndelay(SHT15_TSU);
+ gpio_set_value(data->pdata->gpio_sck, 1);
+ ndelay(SHT15_TSCKH);
+ gpio_set_value(data->pdata->gpio_sck, 0);
+ ndelay(SHT15_TSCKL);
+}
+
+/**
+ * sht15_read_byte() - Read a byte back from the device
+ * @data: device state.
+ */
+static u8 sht15_read_byte(struct sht15_data *data)
+{
+ int i;
+ u8 byte = 0;
+
+ for (i = 0; i < 8; ++i) {
+ byte <<= 1;
+ gpio_set_value(data->pdata->gpio_sck, 1);
+ ndelay(SHT15_TSCKH);
+ byte |= !!gpio_get_value(data->pdata->gpio_data);
+ gpio_set_value(data->pdata->gpio_sck, 0);
+ ndelay(SHT15_TSCKL);
+ }
+ return byte;
+}
+
+/**
+ * sht15_send_status() - write the status register byte
+ * @data: sht15 specific data.
+ * @status: the byte to set the status register with.
+ *
+ * As described in figure 14 and table 5 of the datasheet.
+ */
+static int sht15_send_status(struct sht15_data *data, u8 status)
+{
+ int ret;
+
+ ret = sht15_send_cmd(data, SHT15_WRITE_STATUS);
+ if (ret)
+ return ret;
+ gpio_direction_output(data->pdata->gpio_data, 1);
+ ndelay(SHT15_TSU);
+ sht15_send_byte(data, status);
+ ret = sht15_wait_for_response(data);
+ if (ret)
+ return ret;
+
+ data->val_status = status;
+ return 0;
+}
+
+/**
+ * sht15_update_status() - get updated status register from device if too old
+ * @data: device instance specific data.
+ *
+ * As described in figure 15 and table 5 of the datasheet.
+ */
+static int sht15_update_status(struct sht15_data *data)
+{
+ int ret = 0;
+ u8 status;
+ u8 previous_config;
+ u8 dev_checksum = 0;
+ u8 checksum_vals[2];
+ int timeout = HZ;
+
+ mutex_lock(&data->read_lock);
+ if (time_after(jiffies, data->last_status + timeout)
+ || !data->status_valid) {
+ ret = sht15_send_cmd(data, SHT15_READ_STATUS);
+ if (ret)
+ goto error_ret;
+ status = sht15_read_byte(data);
+
+ if (data->checksumming) {
+ sht15_ack(data);
+ dev_checksum = sht15_reverse(sht15_read_byte(data));
+ checksum_vals[0] = SHT15_READ_STATUS;
+ checksum_vals[1] = status;
+ data->checksum_ok = (sht15_crc8(data, checksum_vals, 2)
+ == dev_checksum);
+ }
+
+ sht15_end_transmission(data);
+
+ /*
+ * Perform checksum validation on the received data.
+ * Specification mentions that in case a checksum verification
+ * fails, a soft reset command must be sent to the device.
+ */
+ if (data->checksumming && !data->checksum_ok) {
+ previous_config = data->val_status & 0x07;
+ ret = sht15_soft_reset(data);
+ if (ret)
+ goto error_ret;
+ if (previous_config) {
+ ret = sht15_send_status(data, previous_config);
+ if (ret) {
+ dev_err(data->dev,
+ "CRC validation failed, unable "
+ "to restore device settings\n");
+ goto error_ret;
+ }
+ }
+ ret = -EAGAIN;
+ goto error_ret;
+ }
+
+ data->val_status = status;
+ data->status_valid = true;
+ data->last_status = jiffies;
+ }
+error_ret:
+ mutex_unlock(&data->read_lock);
+
+ return ret;
+}
+
/**
- * sht15_update_single_val() - get a new value from device
+ * sht15_measurement() - get a new value from device
* @data: device instance specific data
* @command: command sent to request value
* @timeout_msecs: timeout after which comms are assumed
* to have failed are reset.
- **/
-static inline int sht15_update_single_val(struct sht15_data *data,
- int command,
- int timeout_msecs)
+ */
+static int sht15_measurement(struct sht15_data *data,
+ int command,
+ int timeout_msecs)
{
int ret;
+ u8 previous_config;
+
ret = sht15_send_cmd(data, command);
if (ret)
return ret;
@@ -256,38 +519,61 @@ static inline int sht15_update_single_val(struct sht15_data *data,
schedule_work(&data->read_work);
}
ret = wait_event_timeout(data->wait_queue,
- (data->flag == SHT15_READING_NOTHING),
+ (data->state == SHT15_READING_NOTHING),
msecs_to_jiffies(timeout_msecs));
if (ret == 0) {/* timeout occurred */
disable_irq_nosync(gpio_to_irq(data->pdata->gpio_data));
sht15_connection_reset(data);
return -ETIME;
}
+
+ /*
+ * Perform checksum validation on the received data.
+ * Specification mentions that in case a checksum verification fails,
+ * a soft reset command must be sent to the device.
+ */
+ if (data->checksumming && !data->checksum_ok) {
+ previous_config = data->val_status & 0x07;
+ ret = sht15_soft_reset(data);
+ if (ret)
+ return ret;
+ if (previous_config) {
+ ret = sht15_send_status(data, previous_config);
+ if (ret) {
+ dev_err(data->dev,
+ "CRC validation failed, unable "
+ "to restore device settings\n");
+ return ret;
+ }
+ }
+ return -EAGAIN;
+ }
+
return 0;
}
/**
- * sht15_update_vals() - get updated readings from device if too old
+ * sht15_update_measurements() - get updated measures from device if too old
* @data: device state
- **/
-static int sht15_update_vals(struct sht15_data *data)
+ */
+static int sht15_update_measurements(struct sht15_data *data)
{
int ret = 0;
int timeout = HZ;
mutex_lock(&data->read_lock);
- if (time_after(jiffies, data->last_updat + timeout)
- || !data->valid) {
- data->flag = SHT15_READING_HUMID;
- ret = sht15_update_single_val(data, SHT15_MEASURE_RH, 160);
+ if (time_after(jiffies, data->last_measurement + timeout)
+ || !data->measurements_valid) {
+ data->state = SHT15_READING_HUMID;
+ ret = sht15_measurement(data, SHT15_MEASURE_RH, 160);
if (ret)
goto error_ret;
- data->flag = SHT15_READING_TEMP;
- ret = sht15_update_single_val(data, SHT15_MEASURE_TEMP, 400);
+ data->state = SHT15_READING_TEMP;
+ ret = sht15_measurement(data, SHT15_MEASURE_TEMP, 400);
if (ret)
goto error_ret;
- data->valid = 1;
- data->last_updat = jiffies;
+ data->measurements_valid = true;
+ data->last_measurement = jiffies;
}
error_ret:
mutex_unlock(&data->read_lock);
@@ -300,10 +586,11 @@ error_ret:
* @data: device state
*
* As per section 4.3 of the data sheet.
- **/
+ */
static inline int sht15_calc_temp(struct sht15_data *data)
{
int d1 = temppoints[0].d1;
+ int d2 = (data->val_status & SHT15_STATUS_LOW_RESOLUTION) ? 40 : 10;
int i;
for (i = ARRAY_SIZE(temppoints) - 1; i > 0; i--)
@@ -316,7 +603,7 @@ static inline int sht15_calc_temp(struct sht15_data *data)
break;
}
- return data->val_temp*10 + d1;
+ return data->val_temp * d2 + d1;
}
/**
@@ -325,23 +612,102 @@ static inline int sht15_calc_temp(struct sht15_data *data)
*
* This is the temperature compensated version as per section 4.2 of
* the data sheet.
- **/
+ *
+ * The sensor is assumed to be V3, which is compatible with V4.
+ * Humidity conversion coefficients are shown in table 7 of the datasheet.
+ */
static inline int sht15_calc_humid(struct sht15_data *data)
{
- int RHlinear; /* milli percent */
+ int rh_linear; /* milli percent */
int temp = sht15_calc_temp(data);
-
+ int c2, c3;
+ int t2;
const int c1 = -4;
- const int c2 = 40500; /* x 10 ^ -6 */
- const int c3 = -28; /* x 10 ^ -7 */
- RHlinear = c1*1000
- + c2 * data->val_humid/1000
+ if (data->val_status & SHT15_STATUS_LOW_RESOLUTION) {
+ c2 = 648000; /* x 10 ^ -6 */
+ c3 = -7200; /* x 10 ^ -7 */
+ t2 = 1280;
+ } else {
+ c2 = 40500; /* x 10 ^ -6 */
+ c3 = -28; /* x 10 ^ -7 */
+ t2 = 80;
+ }
+
+ rh_linear = c1 * 1000
+ + c2 * data->val_humid / 1000
+ (data->val_humid * data->val_humid * c3) / 10000;
- return (temp - 25000) * (10000 + 80 * data->val_humid)
- / 1000000 + RHlinear;
+ return (temp - 25000) * (10000 + t2 * data->val_humid)
+ / 1000000 + rh_linear;
+}
+
+/**
+ * sht15_show_status() - show status information in sysfs
+ * @dev: device.
+ * @attr: device attribute.
+ * @buf: sysfs buffer where information is written to.
+ *
+ * Will be called on read access to temp1_fault, humidity1_fault
+ * and heater_enable sysfs attributes.
+ * Returns number of bytes written into buffer, negative errno on error.
+ */
+static ssize_t sht15_show_status(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int ret;
+ struct sht15_data *data = dev_get_drvdata(dev);
+ u8 bit = to_sensor_dev_attr(attr)->index;
+
+ ret = sht15_update_status(data);
+
+ return ret ? ret : sprintf(buf, "%d\n", !!(data->val_status & bit));
+}
+
+/**
+ * sht15_store_heater() - change heater state via sysfs
+ * @dev: device.
+ * @attr: device attribute.
+ * @buf: sysfs buffer to read the new heater state from.
+ * @count: length of the data.
+ *
+ * Will be called on read access to heater_enable sysfs attribute.
+ * Returns number of bytes actually decoded, negative errno on error.
+ */
+static ssize_t sht15_store_heater(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret;
+ struct sht15_data *data = dev_get_drvdata(dev);
+ long value;
+ u8 status;
+
+ if (strict_strtol(buf, 10, &value))
+ return -EINVAL;
+
+ mutex_lock(&data->read_lock);
+ status = data->val_status & 0x07;
+ if (!!value)
+ status |= SHT15_STATUS_HEATER;
+ else
+ status &= ~SHT15_STATUS_HEATER;
+
+ ret = sht15_send_status(data, status);
+ mutex_unlock(&data->read_lock);
+
+ return ret ? ret : count;
}
+/**
+ * sht15_show_temp() - show temperature measurement value in sysfs
+ * @dev: device.
+ * @attr: device attribute.
+ * @buf: sysfs buffer where measurement values are written to.
+ *
+ * Will be called on read access to temp1_input sysfs attribute.
+ * Returns number of bytes written into buffer, negative errno on error.
+ */
static ssize_t sht15_show_temp(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -350,12 +716,21 @@ static ssize_t sht15_show_temp(struct device *dev,
struct sht15_data *data = dev_get_drvdata(dev);
/* Technically no need to read humidity as well */
- ret = sht15_update_vals(data);
+ ret = sht15_update_measurements(data);
return ret ? ret : sprintf(buf, "%d\n",
sht15_calc_temp(data));
}
+/**
+ * sht15_show_humidity() - show humidity measurement value in sysfs
+ * @dev: device.
+ * @attr: device attribute.
+ * @buf: sysfs buffer where measurement values are written to.
+ *
+ * Will be called on read access to humidity1_input sysfs attribute.
+ * Returns number of bytes written into buffer, negative errno on error.
+ */
static ssize_t sht15_show_humidity(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -363,11 +738,11 @@ static ssize_t sht15_show_humidity(struct device *dev,
int ret;
struct sht15_data *data = dev_get_drvdata(dev);
- ret = sht15_update_vals(data);
+ ret = sht15_update_measurements(data);
return ret ? ret : sprintf(buf, "%d\n", sht15_calc_humid(data));
+}
-};
static ssize_t show_name(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -376,16 +751,23 @@ static ssize_t show_name(struct device *dev,
return sprintf(buf, "%s\n", pdev->name);
}
-static SENSOR_DEVICE_ATTR(temp1_input,
- S_IRUGO, sht15_show_temp,
- NULL, 0);
-static SENSOR_DEVICE_ATTR(humidity1_input,
- S_IRUGO, sht15_show_humidity,
- NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO,
+ sht15_show_temp, NULL, 0);
+static SENSOR_DEVICE_ATTR(humidity1_input, S_IRUGO,
+ sht15_show_humidity, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_fault, S_IRUGO, sht15_show_status, NULL,
+ SHT15_STATUS_LOW_BATTERY);
+static SENSOR_DEVICE_ATTR(humidity1_fault, S_IRUGO, sht15_show_status, NULL,
+ SHT15_STATUS_LOW_BATTERY);
+static SENSOR_DEVICE_ATTR(heater_enable, S_IRUGO | S_IWUSR, sht15_show_status,
+ sht15_store_heater, SHT15_STATUS_HEATER);
static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
static struct attribute *sht15_attrs[] = {
&sensor_dev_attr_temp1_input.dev_attr.attr,
&sensor_dev_attr_humidity1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_fault.dev_attr.attr,
+ &sensor_dev_attr_humidity1_fault.dev_attr.attr,
+ &sensor_dev_attr_heater_enable.dev_attr.attr,
&dev_attr_name.attr,
NULL,
};
@@ -397,59 +779,31 @@ static const struct attribute_group sht15_attr_group = {
static irqreturn_t sht15_interrupt_fired(int irq, void *d)
{
struct sht15_data *data = d;
+
/* First disable the interrupt */
disable_irq_nosync(irq);
atomic_inc(&data->interrupt_handled);
/* Then schedule a reading work struct */
- if (data->flag != SHT15_READING_NOTHING)
+ if (data->state != SHT15_READING_NOTHING)
schedule_work(&data->read_work);
return IRQ_HANDLED;
}
-/* Each byte of data is acknowledged by pulling the data line
- * low for one clock pulse.
- */
-static void sht15_ack(struct sht15_data *data)
-{
- gpio_direction_output(data->pdata->gpio_data, 0);
- ndelay(SHT15_TSU);
- gpio_set_value(data->pdata->gpio_sck, 1);
- ndelay(SHT15_TSU);
- gpio_set_value(data->pdata->gpio_sck, 0);
- ndelay(SHT15_TSU);
- gpio_set_value(data->pdata->gpio_data, 1);
-
- gpio_direction_input(data->pdata->gpio_data);
-}
-/**
- * sht15_end_transmission() - notify device of end of transmission
- * @data: device state
- *
- * This is basically a NAK. (single clock pulse, data high)
- **/
-static void sht15_end_transmission(struct sht15_data *data)
-{
- gpio_direction_output(data->pdata->gpio_data, 1);
- ndelay(SHT15_TSU);
- gpio_set_value(data->pdata->gpio_sck, 1);
- ndelay(SHT15_TSCKH);
- gpio_set_value(data->pdata->gpio_sck, 0);
- ndelay(SHT15_TSCKL);
-}
-
static void sht15_bh_read_data(struct work_struct *work_s)
{
- int i;
uint16_t val = 0;
+ u8 dev_checksum = 0;
+ u8 checksum_vals[3];
struct sht15_data *data
= container_of(work_s, struct sht15_data,
read_work);
+
/* Firstly, verify the line is low */
if (gpio_get_value(data->pdata->gpio_data)) {
- /* If not, then start the interrupt again - care
- here as could have gone low in meantime so verify
- it hasn't!
- */
+ /*
+ * If not, then start the interrupt again - care here as could
+ * have gone low in meantime so verify it hasn't!
+ */
atomic_set(&data->interrupt_handled, 0);
enable_irq(gpio_to_irq(data->pdata->gpio_data));
/* If still not occurred or another handler has been scheduled */
@@ -457,30 +811,43 @@ static void sht15_bh_read_data(struct work_struct *work_s)
|| atomic_read(&data->interrupt_handled))
return;
}
+
/* Read the data back from the device */
- for (i = 0; i < 16; ++i) {
- val <<= 1;
- gpio_set_value(data->pdata->gpio_sck, 1);
- ndelay(SHT15_TSCKH);
- val |= !!gpio_get_value(data->pdata->gpio_data);
- gpio_set_value(data->pdata->gpio_sck, 0);
- ndelay(SHT15_TSCKL);
- if (i == 7)
- sht15_ack(data);
+ val = sht15_read_byte(data);
+ val <<= 8;
+ sht15_ack(data);
+ val |= sht15_read_byte(data);
+
+ if (data->checksumming) {
+ /*
+ * Ask the device for a checksum and read it back.
+ * Note: the device sends the checksum byte reversed.
+ */
+ sht15_ack(data);
+ dev_checksum = sht15_reverse(sht15_read_byte(data));
+ checksum_vals[0] = (data->state == SHT15_READING_TEMP) ?
+ SHT15_MEASURE_TEMP : SHT15_MEASURE_RH;
+ checksum_vals[1] = (u8) (val >> 8);
+ checksum_vals[2] = (u8) val;
+ data->checksum_ok
+ = (sht15_crc8(data, checksum_vals, 3) == dev_checksum);
}
+
/* Tell the device we are done */
sht15_end_transmission(data);
- switch (data->flag) {
+ switch (data->state) {
case SHT15_READING_TEMP:
data->val_temp = val;
break;
case SHT15_READING_HUMID:
data->val_humid = val;
break;
+ default:
+ break;
}
- data->flag = SHT15_READING_NOTHING;
+ data->state = SHT15_READING_NOTHING;
wake_up(&data->wait_queue);
}
@@ -500,10 +867,10 @@ static void sht15_update_voltage(struct work_struct *work_s)
*
* Note that as the notification code holds the regulator lock, we have
* to schedule an update of the supply voltage rather than getting it directly.
- **/
+ */
static int sht15_invalidate_voltage(struct notifier_block *nb,
- unsigned long event,
- void *ignored)
+ unsigned long event,
+ void *ignored)
{
struct sht15_data *data = container_of(nb, struct sht15_data, nb);
@@ -518,10 +885,11 @@ static int __devinit sht15_probe(struct platform_device *pdev)
{
int ret = 0;
struct sht15_data *data = kzalloc(sizeof(*data), GFP_KERNEL);
+ u8 status = 0;
if (!data) {
ret = -ENOMEM;
- dev_err(&pdev->dev, "kzalloc failed");
+ dev_err(&pdev->dev, "kzalloc failed\n");
goto error_ret;
}
@@ -533,13 +901,22 @@ static int __devinit sht15_probe(struct platform_device *pdev)
init_waitqueue_head(&data->wait_queue);
if (pdev->dev.platform_data == NULL) {
- dev_err(&pdev->dev, "no platform data supplied");
+ dev_err(&pdev->dev, "no platform data supplied\n");
goto err_free_data;
}
data->pdata = pdev->dev.platform_data;
- data->supply_uV = data->pdata->supply_mv*1000;
-
-/* If a regulator is available, query what the supply voltage actually is!*/
+ data->supply_uV = data->pdata->supply_mv * 1000;
+ if (data->pdata->checksum)
+ data->checksumming = true;
+ if (data->pdata->no_otp_reload)
+ status |= SHT15_STATUS_NO_OTP_RELOAD;
+ if (data->pdata->low_resolution)
+ status |= SHT15_STATUS_LOW_RESOLUTION;
+
+ /*
+ * If a regulator is available,
+ * query what the supply voltage actually is!
+ */
data->reg = regulator_get(data->dev, "vcc");
if (!IS_ERR(data->reg)) {
int voltage;
@@ -549,28 +926,34 @@ static int __devinit sht15_probe(struct platform_device *pdev)
data->supply_uV = voltage;
regulator_enable(data->reg);
- /* setup a notifier block to update this if another device
- * causes the voltage to change */
+ /*
+ * Setup a notifier block to update this if another device
+ * causes the voltage to change
+ */
data->nb.notifier_call = &sht15_invalidate_voltage;
ret = regulator_register_notifier(data->reg, &data->nb);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "regulator notifier request failed\n");
+ regulator_disable(data->reg);
+ regulator_put(data->reg);
+ goto err_free_data;
+ }
}
-/* Try requesting the GPIOs */
+
+ /* Try requesting the GPIOs */
ret = gpio_request(data->pdata->gpio_sck, "SHT15 sck");
if (ret) {
- dev_err(&pdev->dev, "gpio request failed");
- goto err_free_data;
+ dev_err(&pdev->dev, "gpio request failed\n");
+ goto err_release_reg;
}
gpio_direction_output(data->pdata->gpio_sck, 0);
+
ret = gpio_request(data->pdata->gpio_data, "SHT15 data");
if (ret) {
- dev_err(&pdev->dev, "gpio request failed");
+ dev_err(&pdev->dev, "gpio request failed\n");
goto err_release_gpio_sck;
}
- ret = sysfs_create_group(&pdev->dev.kobj, &sht15_attr_group);
- if (ret) {
- dev_err(&pdev->dev, "sysfs create failed");
- goto err_release_gpio_data;
- }
ret = request_irq(gpio_to_irq(data->pdata->gpio_data),
sht15_interrupt_fired,
@@ -578,30 +961,53 @@ static int __devinit sht15_probe(struct platform_device *pdev)
"sht15 data",
data);
if (ret) {
- dev_err(&pdev->dev, "failed to get irq for data line");
+ dev_err(&pdev->dev, "failed to get irq for data line\n");
goto err_release_gpio_data;
}
disable_irq_nosync(gpio_to_irq(data->pdata->gpio_data));
sht15_connection_reset(data);
- sht15_send_cmd(data, 0x1E);
+ ret = sht15_soft_reset(data);
+ if (ret)
+ goto err_release_irq;
+
+ /* write status with platform data options */
+ if (status) {
+ ret = sht15_send_status(data, status);
+ if (ret)
+ goto err_release_irq;
+ }
+
+ ret = sysfs_create_group(&pdev->dev.kobj, &sht15_attr_group);
+ if (ret) {
+ dev_err(&pdev->dev, "sysfs create failed\n");
+ goto err_release_irq;
+ }
data->hwmon_dev = hwmon_device_register(data->dev);
if (IS_ERR(data->hwmon_dev)) {
ret = PTR_ERR(data->hwmon_dev);
- goto err_release_irq;
+ goto err_release_sysfs_group;
}
+
return 0;
+err_release_sysfs_group:
+ sysfs_remove_group(&pdev->dev.kobj, &sht15_attr_group);
err_release_irq:
free_irq(gpio_to_irq(data->pdata->gpio_data), data);
err_release_gpio_data:
gpio_free(data->pdata->gpio_data);
err_release_gpio_sck:
gpio_free(data->pdata->gpio_sck);
+err_release_reg:
+ if (!IS_ERR(data->reg)) {
+ regulator_unregister_notifier(data->reg, &data->nb);
+ regulator_disable(data->reg);
+ regulator_put(data->reg);
+ }
err_free_data:
kfree(data);
error_ret:
-
return ret;
}
@@ -609,9 +1015,15 @@ static int __devexit sht15_remove(struct platform_device *pdev)
{
struct sht15_data *data = platform_get_drvdata(pdev);
- /* Make sure any reads from the device are done and
- * prevent new ones from beginning */
+ /*
+ * Make sure any reads from the device are done and
+ * prevent new ones beginning
+ */
mutex_lock(&data->read_lock);
+ if (sht15_soft_reset(data)) {
+ mutex_unlock(&data->read_lock);
+ return -EFAULT;
+ }
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&pdev->dev.kobj, &sht15_attr_group);
if (!IS_ERR(data->reg)) {
@@ -625,10 +1037,10 @@ static int __devexit sht15_remove(struct platform_device *pdev)
gpio_free(data->pdata->gpio_sck);
mutex_unlock(&data->read_lock);
kfree(data);
+
return 0;
}
-
/*
* sht_drivers simultaneously refers to __devinit and __devexit function
* which causes spurious section mismatch warning. So use __refdata to
@@ -673,7 +1085,6 @@ static struct platform_driver __refdata sht_drivers[] = {
},
};
-
static int __init sht15_init(void)
{
int ret;
diff --git a/drivers/hwmon/ucd9000.c b/drivers/hwmon/ucd9000.c
new file mode 100644
index 000000000000..ace1c7319734
--- /dev/null
+++ b/drivers/hwmon/ucd9000.c
@@ -0,0 +1,278 @@
+/*
+ * Hardware monitoring driver for UCD90xxx Sequencer and System Health
+ * Controller series
+ *
+ * Copyright (C) 2011 Ericsson AB.
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c/pmbus.h>
+#include "pmbus.h"
+
+enum chips { ucd9000, ucd90120, ucd90124, ucd9090, ucd90910 };
+
+#define UCD9000_MONITOR_CONFIG 0xd5
+#define UCD9000_NUM_PAGES 0xd6
+#define UCD9000_FAN_CONFIG_INDEX 0xe7
+#define UCD9000_FAN_CONFIG 0xe8
+#define UCD9000_DEVICE_ID 0xfd
+
+#define UCD9000_MON_TYPE(x) (((x) >> 5) & 0x07)
+#define UCD9000_MON_PAGE(x) ((x) & 0x0f)
+
+#define UCD9000_MON_VOLTAGE 1
+#define UCD9000_MON_TEMPERATURE 2
+#define UCD9000_MON_CURRENT 3
+#define UCD9000_MON_VOLTAGE_HW 4
+
+#define UCD9000_NUM_FAN 4
+
+struct ucd9000_data {
+ u8 fan_data[UCD9000_NUM_FAN][I2C_SMBUS_BLOCK_MAX];
+ struct pmbus_driver_info info;
+};
+#define to_ucd9000_data(_info) container_of(_info, struct ucd9000_data, info)
+
+static int ucd9000_get_fan_config(struct i2c_client *client, int fan)
+{
+ int fan_config = 0;
+ struct ucd9000_data *data
+ = to_ucd9000_data(pmbus_get_driver_info(client));
+
+ if (data->fan_data[fan][3] & 1)
+ fan_config |= PB_FAN_2_INSTALLED; /* Use lower bit position */
+
+ /* Pulses/revolution */
+ fan_config |= (data->fan_data[fan][3] & 0x06) >> 1;
+
+ return fan_config;
+}
+
+static int ucd9000_read_byte_data(struct i2c_client *client, int page, int reg)
+{
+ int ret = 0;
+ int fan_config;
+
+ switch (reg) {
+ case PMBUS_FAN_CONFIG_12:
+ if (page)
+ return -EINVAL;
+
+ ret = ucd9000_get_fan_config(client, 0);
+ if (ret < 0)
+ return ret;
+ fan_config = ret << 4;
+ ret = ucd9000_get_fan_config(client, 1);
+ if (ret < 0)
+ return ret;
+ fan_config |= ret;
+ ret = fan_config;
+ break;
+ case PMBUS_FAN_CONFIG_34:
+ if (page)
+ return -EINVAL;
+
+ ret = ucd9000_get_fan_config(client, 2);
+ if (ret < 0)
+ return ret;
+ fan_config = ret << 4;
+ ret = ucd9000_get_fan_config(client, 3);
+ if (ret < 0)
+ return ret;
+ fan_config |= ret;
+ ret = fan_config;
+ break;
+ default:
+ ret = -ENODATA;
+ break;
+ }
+ return ret;
+}
+
+static const struct i2c_device_id ucd9000_id[] = {
+ {"ucd9000", ucd9000},
+ {"ucd90120", ucd90120},
+ {"ucd90124", ucd90124},
+ {"ucd9090", ucd9090},
+ {"ucd90910", ucd90910},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, ucd9000_id);
+
+static int ucd9000_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ u8 block_buffer[I2C_SMBUS_BLOCK_MAX + 1];
+ struct ucd9000_data *data;
+ struct pmbus_driver_info *info;
+ const struct i2c_device_id *mid;
+ int i, ret;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_BLOCK_DATA))
+ return -ENODEV;
+
+ ret = i2c_smbus_read_block_data(client, UCD9000_DEVICE_ID,
+ block_buffer);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to read device ID\n");
+ return ret;
+ }
+ block_buffer[ret] = '\0';
+ dev_info(&client->dev, "Device ID %s\n", block_buffer);
+
+ mid = NULL;
+ for (i = 0; i < ARRAY_SIZE(ucd9000_id); i++) {
+ mid = &ucd9000_id[i];
+ if (!strncasecmp(mid->name, block_buffer, strlen(mid->name)))
+ break;
+ }
+ if (!mid || !strlen(mid->name)) {
+ dev_err(&client->dev, "Unsupported device\n");
+ return -ENODEV;
+ }
+
+ if (id->driver_data != ucd9000 && id->driver_data != mid->driver_data)
+ dev_notice(&client->dev,
+ "Device mismatch: Configured %s, detected %s\n",
+ id->name, mid->name);
+
+ data = kzalloc(sizeof(struct ucd9000_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+ info = &data->info;
+
+ ret = i2c_smbus_read_byte_data(client, UCD9000_NUM_PAGES);
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "Failed to read number of active pages\n");
+ goto out;
+ }
+ info->pages = ret;
+ if (!info->pages) {
+ dev_err(&client->dev, "No pages configured\n");
+ ret = -ENODEV;
+ goto out;
+ }
+
+ /* The internal temperature sensor is always active */
+ info->func[0] = PMBUS_HAVE_TEMP;
+
+ /* Everything else is configurable */
+ ret = i2c_smbus_read_block_data(client, UCD9000_MONITOR_CONFIG,
+ block_buffer);
+ if (ret <= 0) {
+ dev_err(&client->dev, "Failed to read configuration data\n");
+ ret = -ENODEV;
+ goto out;
+ }
+ for (i = 0; i < ret; i++) {
+ int page = UCD9000_MON_PAGE(block_buffer[i]);
+
+ if (page >= info->pages)
+ continue;
+
+ switch (UCD9000_MON_TYPE(block_buffer[i])) {
+ case UCD9000_MON_VOLTAGE:
+ case UCD9000_MON_VOLTAGE_HW:
+ info->func[page] |= PMBUS_HAVE_VOUT
+ | PMBUS_HAVE_STATUS_VOUT;
+ break;
+ case UCD9000_MON_TEMPERATURE:
+ info->func[page] |= PMBUS_HAVE_TEMP2
+ | PMBUS_HAVE_STATUS_TEMP;
+ break;
+ case UCD9000_MON_CURRENT:
+ info->func[page] |= PMBUS_HAVE_IOUT
+ | PMBUS_HAVE_STATUS_IOUT;
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* Fan configuration */
+ if (mid->driver_data == ucd90124) {
+ for (i = 0; i < UCD9000_NUM_FAN; i++) {
+ i2c_smbus_write_byte_data(client,
+ UCD9000_FAN_CONFIG_INDEX, i);
+ ret = i2c_smbus_read_block_data(client,
+ UCD9000_FAN_CONFIG,
+ data->fan_data[i]);
+ if (ret < 0)
+ goto out;
+ }
+ i2c_smbus_write_byte_data(client, UCD9000_FAN_CONFIG_INDEX, 0);
+
+ info->read_byte_data = ucd9000_read_byte_data;
+ info->func[0] |= PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12
+ | PMBUS_HAVE_FAN34 | PMBUS_HAVE_STATUS_FAN34;
+ }
+
+ ret = pmbus_do_probe(client, mid, info);
+ if (ret < 0)
+ goto out;
+ return 0;
+
+out:
+ kfree(data);
+ return ret;
+}
+
+static int ucd9000_remove(struct i2c_client *client)
+{
+ int ret;
+ struct ucd9000_data *data;
+
+ data = to_ucd9000_data(pmbus_get_driver_info(client));
+ ret = pmbus_do_remove(client);
+ kfree(data);
+ return ret;
+}
+
+
+/* This is the driver that will be inserted */
+static struct i2c_driver ucd9000_driver = {
+ .driver = {
+ .name = "ucd9000",
+ },
+ .probe = ucd9000_probe,
+ .remove = ucd9000_remove,
+ .id_table = ucd9000_id,
+};
+
+static int __init ucd9000_init(void)
+{
+ return i2c_add_driver(&ucd9000_driver);
+}
+
+static void __exit ucd9000_exit(void)
+{
+ i2c_del_driver(&ucd9000_driver);
+}
+
+MODULE_AUTHOR("Guenter Roeck");
+MODULE_DESCRIPTION("PMBus driver for TI UCD90xxx");
+MODULE_LICENSE("GPL");
+module_init(ucd9000_init);
+module_exit(ucd9000_exit);
diff --git a/drivers/hwmon/ucd9200.c b/drivers/hwmon/ucd9200.c
new file mode 100644
index 000000000000..ffcc1cf3609d
--- /dev/null
+++ b/drivers/hwmon/ucd9200.c
@@ -0,0 +1,210 @@
+/*
+ * Hardware monitoring driver for ucd9200 series Digital PWM System Controllers
+ *
+ * Copyright (C) 2011 Ericsson AB.
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c/pmbus.h>
+#include "pmbus.h"
+
+#define UCD9200_PHASE_INFO 0xd2
+#define UCD9200_DEVICE_ID 0xfd
+
+enum chips { ucd9200, ucd9220, ucd9222, ucd9224, ucd9240, ucd9244, ucd9246,
+ ucd9248 };
+
+static const struct i2c_device_id ucd9200_id[] = {
+ {"ucd9200", ucd9200},
+ {"ucd9220", ucd9220},
+ {"ucd9222", ucd9222},
+ {"ucd9224", ucd9224},
+ {"ucd9240", ucd9240},
+ {"ucd9244", ucd9244},
+ {"ucd9246", ucd9246},
+ {"ucd9248", ucd9248},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, ucd9200_id);
+
+static int ucd9200_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ u8 block_buffer[I2C_SMBUS_BLOCK_MAX + 1];
+ struct pmbus_driver_info *info;
+ const struct i2c_device_id *mid;
+ int i, j, ret;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_BLOCK_DATA))
+ return -ENODEV;
+
+ ret = i2c_smbus_read_block_data(client, UCD9200_DEVICE_ID,
+ block_buffer);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to read device ID\n");
+ return ret;
+ }
+ block_buffer[ret] = '\0';
+ dev_info(&client->dev, "Device ID %s\n", block_buffer);
+
+ mid = NULL;
+ for (i = 0; i < ARRAY_SIZE(ucd9200_id); i++) {
+ mid = &ucd9200_id[i];
+ if (!strncasecmp(mid->name, block_buffer, strlen(mid->name)))
+ break;
+ }
+ if (!mid || !strlen(mid->name)) {
+ dev_err(&client->dev, "Unsupported device\n");
+ return -ENODEV;
+ }
+ if (id->driver_data != ucd9200 && id->driver_data != mid->driver_data)
+ dev_notice(&client->dev,
+ "Device mismatch: Configured %s, detected %s\n",
+ id->name, mid->name);
+
+ info = kzalloc(sizeof(struct pmbus_driver_info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ ret = i2c_smbus_read_block_data(client, UCD9200_PHASE_INFO,
+ block_buffer);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to read phase information\n");
+ goto out;
+ }
+
+ /*
+ * Calculate number of configured pages (rails) from PHASE_INFO
+ * register.
+ * Rails have to be sequential, so we can abort after finding
+ * the first unconfigured rail.
+ */
+ info->pages = 0;
+ for (i = 0; i < ret; i++) {
+ if (!block_buffer[i])
+ break;
+ info->pages++;
+ }
+ if (!info->pages) {
+ dev_err(&client->dev, "No rails configured\n");
+ ret = -ENODEV;
+ goto out;
+ }
+ dev_info(&client->dev, "%d rails configured\n", info->pages);
+
+ /*
+ * Set PHASE registers on all pages to 0xff to ensure that phase
+ * specific commands will apply to all phases of a given page (rail).
+ * This only affects the READ_IOUT and READ_TEMPERATURE2 registers.
+ * READ_IOUT will return the sum of currents of all phases of a rail,
+ * and READ_TEMPERATURE2 will return the maximum temperature detected
+ * for the the phases of the rail.
+ */
+ for (i = 0; i < info->pages; i++) {
+ /*
+ * Setting PAGE & PHASE fails once in a while for no obvious
+ * reason, so we need to retry a couple of times.
+ */
+ for (j = 0; j < 3; j++) {
+ ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, i);
+ if (ret < 0)
+ continue;
+ ret = i2c_smbus_write_byte_data(client, PMBUS_PHASE,
+ 0xff);
+ if (ret < 0)
+ continue;
+ break;
+ }
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "Failed to initialize PHASE registers\n");
+ goto out;
+ }
+ }
+ if (info->pages > 1)
+ i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0);
+
+ info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT |
+ PMBUS_HAVE_IIN | PMBUS_HAVE_PIN |
+ PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
+ PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT |
+ PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP |
+ PMBUS_HAVE_TEMP2 | PMBUS_HAVE_STATUS_TEMP;
+
+ for (i = 1; i < info->pages; i++)
+ info->func[i] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
+ PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT |
+ PMBUS_HAVE_POUT |
+ PMBUS_HAVE_TEMP2 | PMBUS_HAVE_STATUS_TEMP;
+
+ /* ucd9240 supports a single fan */
+ if (mid->driver_data == ucd9240)
+ info->func[0] |= PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12;
+
+ ret = pmbus_do_probe(client, mid, info);
+ if (ret < 0)
+ goto out;
+ return 0;
+out:
+ kfree(info);
+ return ret;
+}
+
+static int ucd9200_remove(struct i2c_client *client)
+{
+ int ret;
+ const struct pmbus_driver_info *info;
+
+ info = pmbus_get_driver_info(client);
+ ret = pmbus_do_remove(client);
+ kfree(info);
+ return ret;
+}
+
+
+/* This is the driver that will be inserted */
+static struct i2c_driver ucd9200_driver = {
+ .driver = {
+ .name = "ucd9200",
+ },
+ .probe = ucd9200_probe,
+ .remove = ucd9200_remove,
+ .id_table = ucd9200_id,
+};
+
+static int __init ucd9200_init(void)
+{
+ return i2c_add_driver(&ucd9200_driver);
+}
+
+static void __exit ucd9200_exit(void)
+{
+ i2c_del_driver(&ucd9200_driver);
+}
+
+MODULE_AUTHOR("Guenter Roeck");
+MODULE_DESCRIPTION("PMBus driver for TI UCD922x, UCD924x");
+MODULE_LICENSE("GPL");
+module_init(ucd9200_init);
+module_exit(ucd9200_exit);