aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2014-12-08 20:30:25 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2014-12-08 20:30:25 -0800
commit177808cd28ac793d654bb1ae5ae1f778e7b3864f (patch)
treee54e877b3aecd09d91d473da36981ed1702cfa87
parentMerge tag 'edac_for_3.19' of git://git.kernel.org/pub/scm/linux/kernel/git/bp/bp (diff)
parenthwmon: (tmp401) Detect TMP435 on all addresses it supports (diff)
downloadlinux-dev-177808cd28ac793d654bb1ae5ae1f778e7b3864f.tar.xz
linux-dev-177808cd28ac793d654bb1ae5ae1f778e7b3864f.zip
Merge tag 'hwmon-for-linus-3.19' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging
Pull hwmon updates from Guenter Roeck: "Notable changes: - new driver for NCT7802Y - support for TMP435, LM95233, LM95235, NCT6792D, and NXP LM75B - regulator support for PMBus chips, specifically LTX2978 - support for humidity sensors to iio-hwmon bridge driver * tag 'hwmon-for-linus-3.19' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (21 commits) hwmon: (tmp401) Detect TMP435 on all addresses it supports hwmon: (lm75) Strengthen detect function hwmon: (gpio-fan) Add a shutdown handler to poweroff the fans hwmon: (gpio-fan) Allow usage of gpio operations that may sleep hwmon: (tmp401) Bail out from tmp401_probe() in case of write errors hwmon: (tmp401) Add support for TI TMP435 hwmon: (lm95234) Add support for LM95233 hwmon: (lm95245) Add support for LM95235 hwmon: (ina2xx) bail-out from ina2xx_probe() in case of configuration errors hwmon: (nct6775) Add blank lines after declarations hwmon: (nct6775) Add support for NCT6792D hwmon: (nct6775) Documentation updates hwmon: (lm75) Add support for the NXP LM75B hwmon: Driver for Nuvoton NCT7802Y hwmon: (ibmpowernv) Convert to module_platform_driver hwmon: (ibmpowernv) Use platform 'id_table' to probe the device hwmon: (iio_hwmon) Add support for humidity sensors hwmon: (ltc2978) Add regulator support hwmon: (pmbus) Add regulator support hwmon: (pmbus) add helpers for byte write and read modify write ...
-rw-r--r--Documentation/devicetree/bindings/hwmon/ltc2978.txt39
-rw-r--r--Documentation/hwmon/lm755
-rw-r--r--Documentation/hwmon/lm9523415
-rw-r--r--Documentation/hwmon/lm9524514
-rw-r--r--Documentation/hwmon/nct677514
-rw-r--r--Documentation/hwmon/nct780232
-rw-r--r--Documentation/hwmon/tmp4018
-rw-r--r--arch/powerpc/platforms/powernv/opal-sensor.c20
-rw-r--r--drivers/hwmon/Kconfig28
-rw-r--r--drivers/hwmon/Makefile1
-rw-r--r--drivers/hwmon/gpio-fan.c18
-rw-r--r--drivers/hwmon/ibmpowernv.c78
-rw-r--r--drivers/hwmon/iio_hwmon.c7
-rw-r--r--drivers/hwmon/ina2xx.c26
-rw-r--r--drivers/hwmon/lm75.c12
-rw-r--r--drivers/hwmon/lm95234.c91
-rw-r--r--drivers/hwmon/lm95245.c41
-rw-r--r--drivers/hwmon/nct6775.c77
-rw-r--r--drivers/hwmon/nct7802.c860
-rw-r--r--drivers/hwmon/pmbus/Kconfig11
-rw-r--r--drivers/hwmon/pmbus/ltc2978.c39
-rw-r--r--drivers/hwmon/pmbus/pmbus.h30
-rw-r--r--drivers/hwmon/pmbus/pmbus_core.c118
-rw-r--r--drivers/hwmon/tmp401.c42
-rw-r--r--include/linux/i2c/pmbus.h4
25 files changed, 1465 insertions, 165 deletions
diff --git a/Documentation/devicetree/bindings/hwmon/ltc2978.txt b/Documentation/devicetree/bindings/hwmon/ltc2978.txt
new file mode 100644
index 000000000000..ed2f09dc2483
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/ltc2978.txt
@@ -0,0 +1,39 @@
+ltc2978
+
+Required properties:
+- compatible: should contain one of:
+ * "lltc,ltc2974"
+ * "lltc,ltc2977"
+ * "lltc,ltc2978"
+ * "lltc,ltc3880"
+ * "lltc,ltc3883"
+ * "lltc,ltm4676"
+- reg: I2C slave address
+
+Optional properties:
+- regulators: A node that houses a sub-node for each regulator controlled by
+ the device. Each sub-node is identified using the node's name, with valid
+ values listed below. The content of each sub-node is defined by the
+ standard binding for regulators; see regulator.txt.
+
+Valid names of regulators depend on number of supplies supported per device:
+ * ltc2974 : vout0 - vout3
+ * ltc2977 : vout0 - vout7
+ * ltc2978 : vout0 - vout7
+ * ltc3880 : vout0 - vout1
+ * ltc3883 : vout0
+ * ltm4676 : vout0 - vout1
+
+Example:
+ltc2978@5e {
+ compatible = "lltc,ltc2978";
+ reg = <0x5e>;
+ regulators {
+ vout0 {
+ regulator-name = "FPGA-2.5V";
+ };
+ vout2 {
+ regulator-name = "FPGA-1.5V";
+ };
+ };
+};
diff --git a/Documentation/hwmon/lm75 b/Documentation/hwmon/lm75
index c6a5ff1b4641..67691a0aa41d 100644
--- a/Documentation/hwmon/lm75
+++ b/Documentation/hwmon/lm75
@@ -53,6 +53,11 @@ Supported chips:
http://www.ti.com/product/tmp75
http://www.ti.com/product/tmp175
http://www.ti.com/product/tmp275
+ * NXP LM75B
+ Prefix: 'lm75b'
+ Addresses scanned: none
+ Datasheet: Publicly available at the NXP website
+ http://www.nxp.com/documents/data_sheet/LM75B.pdf
Author: Frodo Looijaard <frodol@dds.nl>
diff --git a/Documentation/hwmon/lm95234 b/Documentation/hwmon/lm95234
index a0e95ddfd372..32b777ef224c 100644
--- a/Documentation/hwmon/lm95234
+++ b/Documentation/hwmon/lm95234
@@ -2,6 +2,10 @@ Kernel driver lm95234
=====================
Supported chips:
+ * National Semiconductor / Texas Instruments LM95233
+ Addresses scanned: I2C 0x18, 0x2a, 0x2b
+ Datasheet: Publicly available at the Texas Instruments website
+ http://www.ti.com/product/lm95233
* National Semiconductor / Texas Instruments LM95234
Addresses scanned: I2C 0x18, 0x4d, 0x4e
Datasheet: Publicly available at the Texas Instruments website
@@ -13,11 +17,12 @@ Author: Guenter Roeck <linux@roeck-us.net>
Description
-----------
-LM95234 is an 11-bit digital temperature sensor with a 2-wire System Management
-Bus (SMBus) interface and TrueTherm technology that can very accurately monitor
-the temperature of four remote diodes as well as its own temperature.
-The four remote diodes can be external devices such as microprocessors,
-graphics processors or diode-connected 2N3904s. The LM95234's TruTherm
+LM95233 and LM95234 are 11-bit digital temperature sensors with a 2-wire
+System Management Bus (SMBus) interface and TrueTherm technology
+that can very accurately monitor the temperature of two (LM95233)
+or four (LM95234) remote diodes as well as its own temperature.
+The remote diodes can be external devices such as microprocessors,
+graphics processors or diode-connected 2N3904s. The chip's TruTherm
beta compensation technology allows sensing of 90 nm or 65 nm process
thermal diodes accurately.
diff --git a/Documentation/hwmon/lm95245 b/Documentation/hwmon/lm95245
index 77eaf2812d25..d755901f58c4 100644
--- a/Documentation/hwmon/lm95245
+++ b/Documentation/hwmon/lm95245
@@ -2,10 +2,14 @@ Kernel driver lm95245
==================
Supported chips:
- * National Semiconductor LM95245
+ * TI LM95235
+ Addresses scanned: I2C 0x18, 0x29, 0x4c
+ Datasheet: Publicly available at the TI website
+ http://www.ti.com/lit/ds/symlink/lm95235.pdf
+ * TI / National Semiconductor LM95245
Addresses scanned: I2C 0x18, 0x19, 0x29, 0x4c, 0x4d
- Datasheet: Publicly available at the National Semiconductor website
- http://www.national.com/mpf/LM/LM95245.html
+ Datasheet: Publicly available at the TI website
+ http://www.ti.com/lit/ds/symlink/lm95245.pdf
Author: Alexander Stein <alexander.stein@systec-electronic.com>
@@ -13,10 +17,10 @@ Author: Alexander Stein <alexander.stein@systec-electronic.com>
Description
-----------
-The LM95245 is an 11-bit digital temperature sensor with a 2-wire System
+LM95235 and LM95245 are 11-bit digital temperature sensors with a 2-wire System
Management Bus (SMBus) interface and TruTherm technology that can monitor
the temperature of a remote diode as well as its own temperature.
-The LM95245 can be used to very accurately monitor the temperature of
+The chips can be used to very accurately monitor the temperature of
external devices such as microprocessors.
All temperature values are given in millidegrees Celsius. Local temperature
diff --git a/Documentation/hwmon/nct6775 b/Documentation/hwmon/nct6775
index 4e9ef60e8c6c..f0dd3d2fec96 100644
--- a/Documentation/hwmon/nct6775
+++ b/Documentation/hwmon/nct6775
@@ -8,11 +8,15 @@ Kernel driver NCT6775
=====================
Supported chips:
+ * Nuvoton NCT6102D/NCT6104D/NCT6106D
+ Prefix: 'nct6106'
+ Addresses scanned: ISA address retrieved from Super I/O registers
+ Datasheet: Available from the Nuvoton web site
* Nuvoton NCT5572D/NCT6771F/NCT6772F/NCT6775F/W83677HG-I
Prefix: 'nct6775'
Addresses scanned: ISA address retrieved from Super I/O registers
Datasheet: Available from Nuvoton upon request
- * Nuvoton NCT5577D/NCT6776D/NCT6776F
+ * Nuvoton NCT5573D/NCT5577D/NCT6776D/NCT6776F
Prefix: 'nct6776'
Addresses scanned: ISA address retrieved from Super I/O registers
Datasheet: Available from Nuvoton upon request
@@ -20,6 +24,14 @@ Supported chips:
Prefix: 'nct6779'
Addresses scanned: ISA address retrieved from Super I/O registers
Datasheet: Available from Nuvoton upon request
+ * Nuvoton NCT6791D
+ Prefix: 'nct6791'
+ Addresses scanned: ISA address retrieved from Super I/O registers
+ Datasheet: Available from Nuvoton upon request
+ * Nuvoton NCT6792D
+ Prefix: 'nct6792'
+ Addresses scanned: ISA address retrieved from Super I/O registers
+ Datasheet: Available from Nuvoton upon request
Authors:
Guenter Roeck <linux@roeck-us.net>
diff --git a/Documentation/hwmon/nct7802 b/Documentation/hwmon/nct7802
new file mode 100644
index 000000000000..2e00f5e344bc
--- /dev/null
+++ b/Documentation/hwmon/nct7802
@@ -0,0 +1,32 @@
+Kernel driver nct7802
+=====================
+
+Supported chips:
+ * Nuvoton NCT7802Y
+ Prefix: 'nct7802'
+ Addresses scanned: I2C 0x28..0x2f
+ Datasheet: Available from Nuvoton web site
+
+Authors:
+ Guenter Roeck <linux@roeck-us.net>
+
+Description
+-----------
+
+This driver implements support for the Nuvoton NCT7802Y hardware monitoring
+chip. NCT7802Y supports 6 temperature sensors, 5 voltage sensors, and 3 fan
+speed sensors.
+
+The chip also supports intelligent fan speed control. This functionality is
+not currently supported by the driver.
+
+Tested Boards and BIOS Versions
+-------------------------------
+
+The driver has been reported to work with the following boards and
+BIOS versions.
+
+Board BIOS version
+---------------------------------------------------------------
+Kontron COMe-bSC2 CHR2E934.001.GGO
+Kontron COMe-bIP2 CCR2E212
diff --git a/Documentation/hwmon/tmp401 b/Documentation/hwmon/tmp401
index f91e3fa7e5ec..8eb88e974055 100644
--- a/Documentation/hwmon/tmp401
+++ b/Documentation/hwmon/tmp401
@@ -18,6 +18,10 @@ Supported chips:
Prefix: 'tmp432'
Addresses scanned: I2C 0x4c, 0x4d
Datasheet: http://focus.ti.com/docs/prod/folders/print/tmp432.html
+ * Texas Instruments TMP435
+ Prefix: 'tmp435'
+ Addresses scanned: I2C 0x37, 0x48 - 0x4f
+ Datasheet: http://focus.ti.com/docs/prod/folders/print/tmp435.html
Authors:
Hans de Goede <hdegoede@redhat.com>
@@ -27,8 +31,8 @@ Description
-----------
This driver implements support for Texas Instruments TMP401, TMP411,
-TMP431, and TMP432 chips. These chips implement one or two remote and
-one local temperature sensors. Temperature is measured in degrees
+TMP431, TMP432 and TMP435 chips. These chips implement one or two remote
+and one local temperature sensors. Temperature is measured in degrees
Celsius. Resolution of the remote sensor is 0.0625 degree. Local
sensor resolution can be set to 0.5, 0.25, 0.125 or 0.0625 degree (not
supported by the driver so far, so using the default resolution of 0.5
diff --git a/arch/powerpc/platforms/powernv/opal-sensor.c b/arch/powerpc/platforms/powernv/opal-sensor.c
index 10271ad1fac4..4ab67ef7abc9 100644
--- a/arch/powerpc/platforms/powernv/opal-sensor.c
+++ b/arch/powerpc/platforms/powernv/opal-sensor.c
@@ -20,7 +20,9 @@
#include <linux/delay.h>
#include <linux/mutex.h>
+#include <linux/of_platform.h>
#include <asm/opal.h>
+#include <asm/machdep.h>
static DEFINE_MUTEX(opal_sensor_mutex);
@@ -64,3 +66,21 @@ out:
return ret;
}
EXPORT_SYMBOL_GPL(opal_get_sensor_data);
+
+static __init int opal_sensor_init(void)
+{
+ struct platform_device *pdev;
+ struct device_node *sensor;
+
+ sensor = of_find_node_by_path("/ibm,opal/sensors");
+ if (!sensor) {
+ pr_err("Opal node 'sensors' not found\n");
+ return -ENODEV;
+ }
+
+ pdev = of_platform_device_create(sensor, "opal-sensor", NULL);
+ of_node_put(sensor);
+
+ return PTR_ERR_OR_ZERO(pdev);
+}
+machine_subsys_initcall(powernv, opal_sensor_init);
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 5286d7ce1f9e..6529c09c46f0 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1028,11 +1028,11 @@ config SENSORS_LM93
will be called lm93.
config SENSORS_LM95234
- tristate "National Semiconductor LM95234"
+ tristate "National Semiconductor LM95234 and compatibles"
depends on I2C
help
- If you say yes here you get support for the LM95234 temperature
- sensor.
+ If you say yes here you get support for the LM95233 and LM95234
+ temperature sensor chips.
This driver can also be built as a module. If so, the module
will be called lm95234.
@@ -1048,10 +1048,11 @@ config SENSORS_LM95241
will be called lm95241.
config SENSORS_LM95245
- tristate "National Semiconductor LM95245 sensor chip"
+ tristate "National Semiconductor LM95245 and compatibles"
depends on I2C
help
- If you say yes here you get support for LM95245 sensor chip.
+ If you say yes here you get support for LM95235 and LM95245
+ temperature sensor chips.
This driver can also be built as a module. If so, the module
will be called lm95245.
@@ -1117,12 +1118,23 @@ config SENSORS_NCT6775
help
If you say yes here you get support for the hardware monitoring
functionality of the Nuvoton NCT6106D, NCT6775F, NCT6776F, NCT6779D,
- NCT6791D and compatible Super-I/O chips. This driver replaces the
- w83627ehf driver for NCT6775F and NCT6776F.
+ NCT6791D, NCT6792D and compatible Super-I/O chips. This driver
+ replaces the w83627ehf driver for NCT6775F and NCT6776F.
This driver can also be built as a module. If so, the module
will be called nct6775.
+config SENSORS_NCT7802
+ tristate "Nuvoton NCT7802Y"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ If you say yes here you get support for the Nuvoton NCT7802Y
+ hardware monitoring chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called nct7802.
+
config SENSORS_PCF8591
tristate "Philips PCF8591 ADC/DAC"
depends on I2C
@@ -1454,7 +1466,7 @@ config SENSORS_TMP401
depends on I2C
help
If you say yes here you get support for Texas Instruments TMP401,
- TMP411, TMP431, and TMP432 temperature sensor chips.
+ TMP411, TMP431, TMP432 and TMP435 temperature sensor chips.
This driver can also be built as a module. If so, the module
will be called tmp401.
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index c90a7611efaa..67280643bcf0 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -118,6 +118,7 @@ obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o
obj-$(CONFIG_SENSORS_MENF21BMC_HWMON) += menf21bmc_hwmon.o
obj-$(CONFIG_SENSORS_NCT6683) += nct6683.o
obj-$(CONFIG_SENSORS_NCT6775) += nct6775.o
+obj-$(CONFIG_SENSORS_NCT7802) += nct7802.o
obj-$(CONFIG_SENSORS_NTC_THERMISTOR) += ntc_thermistor.o
obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
obj-$(CONFIG_SENSORS_PC87427) += pc87427.o
diff --git a/drivers/hwmon/gpio-fan.c b/drivers/hwmon/gpio-fan.c
index 4efa1734bdad..36abf814b8c7 100644
--- a/drivers/hwmon/gpio-fan.c
+++ b/drivers/hwmon/gpio-fan.c
@@ -79,7 +79,7 @@ static ssize_t show_fan_alarm(struct device *dev,
{
struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
struct gpio_fan_alarm *alarm = fan_data->alarm;
- int value = gpio_get_value(alarm->gpio);
+ int value = gpio_get_value_cansleep(alarm->gpio);
if (alarm->active_low)
value = !value;
@@ -131,7 +131,7 @@ static void __set_fan_ctrl(struct gpio_fan_data *fan_data, int ctrl_val)
int i;
for (i = 0; i < fan_data->num_ctrl; i++)
- gpio_set_value(fan_data->ctrl[i], (ctrl_val >> i) & 1);
+ gpio_set_value_cansleep(fan_data->ctrl[i], (ctrl_val >> i) & 1);
}
static int __get_fan_ctrl(struct gpio_fan_data *fan_data)
@@ -142,7 +142,7 @@ static int __get_fan_ctrl(struct gpio_fan_data *fan_data)
for (i = 0; i < fan_data->num_ctrl; i++) {
int value;
- value = gpio_get_value(fan_data->ctrl[i]);
+ value = gpio_get_value_cansleep(fan_data->ctrl[i]);
ctrl_val |= (value << i);
}
return ctrl_val;
@@ -369,7 +369,8 @@ static int fan_ctrl_init(struct gpio_fan_data *fan_data,
if (err)
return err;
- err = gpio_direction_output(ctrl[i], gpio_get_value(ctrl[i]));
+ err = gpio_direction_output(ctrl[i],
+ gpio_get_value_cansleep(ctrl[i]));
if (err)
return err;
}
@@ -549,6 +550,14 @@ static int gpio_fan_probe(struct platform_device *pdev)
return 0;
}
+static void gpio_fan_shutdown(struct platform_device *pdev)
+{
+ struct gpio_fan_data *fan_data = dev_get_drvdata(&pdev->dev);
+
+ if (fan_data->ctrl)
+ set_fan_speed(fan_data, 0);
+}
+
#ifdef CONFIG_PM_SLEEP
static int gpio_fan_suspend(struct device *dev)
{
@@ -580,6 +589,7 @@ static SIMPLE_DEV_PM_OPS(gpio_fan_pm, gpio_fan_suspend, gpio_fan_resume);
static struct platform_driver gpio_fan_driver = {
.probe = gpio_fan_probe,
+ .shutdown = gpio_fan_shutdown,
.driver = {
.name = "gpio-fan",
.pm = GPIO_FAN_PM,
diff --git a/drivers/hwmon/ibmpowernv.c b/drivers/hwmon/ibmpowernv.c
index 6a30eeea94be..7c2c7be182f2 100644
--- a/drivers/hwmon/ibmpowernv.c
+++ b/drivers/hwmon/ibmpowernv.c
@@ -74,9 +74,6 @@ struct platform_data {
u32 sensors_count; /* Total count of sensors from each group */
};
-/* Platform device representing all the ibmpowernv sensors */
-static struct platform_device *pdevice;
-
static ssize_t show_sensor(struct device *dev, struct device_attribute *devattr,
char *buf)
{
@@ -99,7 +96,7 @@ static ssize_t show_sensor(struct device *dev, struct device_attribute *devattr,
return sprintf(buf, "%u\n", x);
}
-static int __init get_sensor_index_attr(const char *name, u32 *index,
+static int get_sensor_index_attr(const char *name, u32 *index,
char *attr)
{
char *hash_pos = strchr(name, '#');
@@ -136,7 +133,7 @@ static int __init get_sensor_index_attr(const char *name, u32 *index,
* which need to be mapped as fan2_input, temp1_max respectively before
* populating them inside hwmon device class.
*/
-static int __init create_hwmon_attr_name(struct device *dev, enum sensors type,
+static int create_hwmon_attr_name(struct device *dev, enum sensors type,
const char *node_name,
char *hwmon_attr_name)
{
@@ -172,7 +169,7 @@ static int __init create_hwmon_attr_name(struct device *dev, enum sensors type,
return 0;
}
-static int __init populate_attr_groups(struct platform_device *pdev)
+static int populate_attr_groups(struct platform_device *pdev)
{
struct platform_data *pdata = platform_get_drvdata(pdev);
const struct attribute_group **pgroups = pdata->attr_groups;
@@ -180,11 +177,6 @@ static int __init populate_attr_groups(struct platform_device *pdev)
enum sensors type;
opal = of_find_node_by_path("/ibm,opal/sensors");
- if (!opal) {
- dev_dbg(&pdev->dev, "Opal node 'sensors' not found\n");
- return -ENODEV;
- }
-
for_each_child_of_node(opal, np) {
if (np->name == NULL)
continue;
@@ -221,7 +213,7 @@ static int __init populate_attr_groups(struct platform_device *pdev)
* to the name required by the higher 'hwmon' driver like fan1_input, temp1_max
* etc..
*/
-static int __init create_device_attrs(struct platform_device *pdev)
+static int create_device_attrs(struct platform_device *pdev)
{
struct platform_data *pdata = platform_get_drvdata(pdev);
const struct attribute_group **pgroups = pdata->attr_groups;
@@ -280,7 +272,7 @@ exit_put_node:
return err;
}
-static int __init ibmpowernv_probe(struct platform_device *pdev)
+static int ibmpowernv_probe(struct platform_device *pdev)
{
struct platform_data *pdata;
struct device *hwmon_dev;
@@ -309,57 +301,25 @@ static int __init ibmpowernv_probe(struct platform_device *pdev)
return PTR_ERR_OR_ZERO(hwmon_dev);
}
-static struct platform_driver ibmpowernv_driver = {
- .driver = {
- .owner = THIS_MODULE,
- .name = DRVNAME,
+static const struct platform_device_id opal_sensor_driver_ids[] = {
+ {
+ .name = "opal-sensor",
},
+ { }
};
+MODULE_DEVICE_TABLE(platform, opal_sensor_driver_ids);
-static int __init ibmpowernv_init(void)
-{
- int err;
-
- pdevice = platform_device_alloc(DRVNAME, 0);
- if (!pdevice) {
- pr_err("Device allocation failed\n");
- err = -ENOMEM;
- goto exit;
- }
-
- err = platform_device_add(pdevice);
- if (err) {
- pr_err("Device addition failed (%d)\n", err);
- goto exit_device_put;
- }
-
- err = platform_driver_probe(&ibmpowernv_driver, ibmpowernv_probe);
- if (err) {
- if (err != -ENODEV)
- pr_err("Platform driver probe failed (%d)\n", err);
-
- goto exit_device_del;
- }
-
- return 0;
-
-exit_device_del:
- platform_device_del(pdevice);
-exit_device_put:
- platform_device_put(pdevice);
-exit:
- return err;
-}
+static struct platform_driver ibmpowernv_driver = {
+ .probe = ibmpowernv_probe,
+ .id_table = opal_sensor_driver_ids,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = DRVNAME,
+ },
+};
-static void __exit ibmpowernv_exit(void)
-{
- platform_driver_unregister(&ibmpowernv_driver);
- platform_device_unregister(pdevice);
-}
+module_platform_driver(ibmpowernv_driver);
MODULE_AUTHOR("Neelesh Gupta <neelegup@linux.vnet.ibm.com>");
MODULE_DESCRIPTION("IBM POWERNV platform sensors");
MODULE_LICENSE("GPL");
-
-module_init(ibmpowernv_init);
-module_exit(ibmpowernv_exit);
diff --git a/drivers/hwmon/iio_hwmon.c b/drivers/hwmon/iio_hwmon.c
index 14c82daab019..980175628563 100644
--- a/drivers/hwmon/iio_hwmon.c
+++ b/drivers/hwmon/iio_hwmon.c
@@ -63,7 +63,7 @@ static int iio_hwmon_probe(struct platform_device *pdev)
struct iio_hwmon_state *st;
struct sensor_device_attribute *a;
int ret, i;
- int in_i = 1, temp_i = 1, curr_i = 1;
+ int in_i = 1, temp_i = 1, curr_i = 1, humidity_i = 1;
enum iio_chan_type type;
struct iio_channel *channels;
const char *name = "iio_hwmon";
@@ -123,6 +123,11 @@ static int iio_hwmon_probe(struct platform_device *pdev)
"curr%d_input",
curr_i++);
break;
+ case IIO_HUMIDITYRELATIVE:
+ a->dev_attr.attr.name = kasprintf(GFP_KERNEL,
+ "humidity%d_input",
+ humidity_i++);
+ break;
default:
ret = -EINVAL;
goto error_release_channels;
diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c
index bfd3f3eeabcd..e01feba909c3 100644
--- a/drivers/hwmon/ina2xx.c
+++ b/drivers/hwmon/ina2xx.c
@@ -223,6 +223,7 @@ static int ina2xx_probe(struct i2c_client *client,
struct device *hwmon_dev;
long shunt = 10000; /* default shunt value 10mOhms */
u32 val;
+ int ret;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA))
return -ENODEV;
@@ -247,12 +248,25 @@ static int ina2xx_probe(struct i2c_client *client,
data->config = &ina2xx_config[data->kind];
/* device configuration */
- i2c_smbus_write_word_swapped(client, INA2XX_CONFIG,
- data->config->config_default);
- /* set current LSB to 1mA, shunt is in uOhms */
- /* (equation 13 in datasheet) */
- i2c_smbus_write_word_swapped(client, INA2XX_CALIBRATION,
- data->config->calibration_factor / shunt);
+ ret = i2c_smbus_write_word_swapped(client, INA2XX_CONFIG,
+ data->config->config_default);
+ if (ret < 0) {
+ dev_err(dev,
+ "error writing to the config register: %d", ret);
+ return -ENODEV;
+ }
+
+ /*
+ * Set current LSB to 1mA, shunt is in uOhms
+ * (equation 13 in datasheet).
+ */
+ ret = i2c_smbus_write_word_swapped(client, INA2XX_CALIBRATION,
+ data->config->calibration_factor / shunt);
+ if (ret < 0) {
+ dev_err(dev,
+ "error writing to the calibration register: %d", ret);
+ return -ENODEV;
+ }
data->client = client;
mutex_init(&data->update_lock);
diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c
index d16dbb33a531..6753fd940c76 100644
--- a/drivers/hwmon/lm75.c
+++ b/drivers/hwmon/lm75.c
@@ -44,6 +44,7 @@ enum lm75_type { /* keep sorted in alphabetical order */
g751,
lm75,
lm75a,
+ lm75b,
max6625,
max6626,
mcp980x,
@@ -233,6 +234,10 @@ lm75_probe(struct i2c_client *client, const struct i2c_device_id *id)
data->resolution = 9;
data->sample_time = HZ / 2;
break;
+ case lm75b:
+ data->resolution = 11;
+ data->sample_time = HZ / 4;
+ break;
case max6625:
data->resolution = 9;
data->sample_time = HZ / 4;
@@ -322,6 +327,7 @@ static const struct i2c_device_id lm75_ids[] = {
{ "g751", g751, },
{ "lm75", lm75, },
{ "lm75a", lm75a, },
+ { "lm75b", lm75b, },
{ "max6625", max6625, },
{ "max6626", max6626, },
{ "mcp980x", mcp980x, },
@@ -409,6 +415,12 @@ static int lm75_detect(struct i2c_client *new_client,
|| i2c_smbus_read_byte_data(new_client, 7) != os)
return -ENODEV;
}
+ /*
+ * It is very unlikely that this is a LM75 if both
+ * hysteresis and temperature limit registers are 0.
+ */
+ if (hyst == 0 && os == 0)
+ return -ENODEV;
/* Addresses cycling */
for (i = 8; i <= 248; i += 40) {
diff --git a/drivers/hwmon/lm95234.c b/drivers/hwmon/lm95234.c
index 411202bdaf6b..8796de39ff9b 100644
--- a/drivers/hwmon/lm95234.c
+++ b/drivers/hwmon/lm95234.c
@@ -1,7 +1,7 @@
/*
* Driver for Texas Instruments / National Semiconductor LM95234
*
- * Copyright (c) 2013 Guenter Roeck <linux@roeck-us.net>
+ * Copyright (c) 2013, 2014 Guenter Roeck <linux@roeck-us.net>
*
* Derived from lm95241.c
* Copyright (C) 2008, 2010 Davide Rizzo <elpa.rizzo@gmail.com>
@@ -30,7 +30,10 @@
#define DRVNAME "lm95234"
-static const unsigned short normal_i2c[] = { 0x18, 0x4d, 0x4e, I2C_CLIENT_END };
+enum chips { lm95233, lm95234 };
+
+static const unsigned short normal_i2c[] = {
+ 0x18, 0x2a, 0x2b, 0x4d, 0x4e, I2C_CLIENT_END };
/* LM95234 registers */
#define LM95234_REG_MAN_ID 0xFE
@@ -53,11 +56,13 @@ static const unsigned short normal_i2c[] = { 0x18, 0x4d, 0x4e, I2C_CLIENT_END };
#define LM95234_REG_TCRIT_HYST 0x5a
#define NATSEMI_MAN_ID 0x01
+#define LM95233_CHIP_ID 0x89
#define LM95234_CHIP_ID 0x79
/* Client data (each client gets its own) */
struct lm95234_data {
struct i2c_client *client;
+ const struct attribute_group *groups[3];
struct mutex update_lock;
unsigned long last_updated, interval; /* in jiffies */
bool valid; /* false until following fields are valid */
@@ -564,35 +569,23 @@ static SENSOR_DEVICE_ATTR(temp5_offset, S_IWUSR | S_IRUGO, show_offset,
static DEVICE_ATTR(update_interval, S_IWUSR | S_IRUGO, show_interval,
set_interval);
-static struct attribute *lm95234_attrs[] = {
+static struct attribute *lm95234_common_attrs[] = {
&sensor_dev_attr_temp1_input.dev_attr.attr,
&sensor_dev_attr_temp2_input.dev_attr.attr,
&sensor_dev_attr_temp3_input.dev_attr.attr,
- &sensor_dev_attr_temp4_input.dev_attr.attr,
- &sensor_dev_attr_temp5_input.dev_attr.attr,
&sensor_dev_attr_temp2_fault.dev_attr.attr,
&sensor_dev_attr_temp3_fault.dev_attr.attr,
- &sensor_dev_attr_temp4_fault.dev_attr.attr,
- &sensor_dev_attr_temp5_fault.dev_attr.attr,
&sensor_dev_attr_temp2_type.dev_attr.attr,
&sensor_dev_attr_temp3_type.dev_attr.attr,
- &sensor_dev_attr_temp4_type.dev_attr.attr,
- &sensor_dev_attr_temp5_type.dev_attr.attr,
&sensor_dev_attr_temp1_max.dev_attr.attr,
&sensor_dev_attr_temp2_max.dev_attr.attr,
&sensor_dev_attr_temp3_max.dev_attr.attr,
- &sensor_dev_attr_temp4_max.dev_attr.attr,
- &sensor_dev_attr_temp5_max.dev_attr.attr,
&sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
&sensor_dev_attr_temp2_max_hyst.dev_attr.attr,
&sensor_dev_attr_temp3_max_hyst.dev_attr.attr,
- &sensor_dev_attr_temp4_max_hyst.dev_attr.attr,
- &sensor_dev_attr_temp5_max_hyst.dev_attr.attr,
&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
- &sensor_dev_attr_temp4_max_alarm.dev_attr.attr,
- &sensor_dev_attr_temp5_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp2_crit.dev_attr.attr,
&sensor_dev_attr_temp3_crit.dev_attr.attr,
&sensor_dev_attr_temp2_crit_hyst.dev_attr.attr,
@@ -601,18 +594,44 @@ static struct attribute *lm95234_attrs[] = {
&sensor_dev_attr_temp3_crit_alarm.dev_attr.attr,
&sensor_dev_attr_temp2_offset.dev_attr.attr,
&sensor_dev_attr_temp3_offset.dev_attr.attr,
+ &dev_attr_update_interval.attr,
+ NULL
+};
+
+static const struct attribute_group lm95234_common_group = {
+ .attrs = lm95234_common_attrs,
+};
+
+static struct attribute *lm95234_attrs[] = {
+ &sensor_dev_attr_temp4_input.dev_attr.attr,
+ &sensor_dev_attr_temp5_input.dev_attr.attr,
+ &sensor_dev_attr_temp4_fault.dev_attr.attr,
+ &sensor_dev_attr_temp5_fault.dev_attr.attr,
+ &sensor_dev_attr_temp4_type.dev_attr.attr,
+ &sensor_dev_attr_temp5_type.dev_attr.attr,
+ &sensor_dev_attr_temp4_max.dev_attr.attr,
+ &sensor_dev_attr_temp5_max.dev_attr.attr,
+ &sensor_dev_attr_temp4_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp5_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp4_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp5_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp4_offset.dev_attr.attr,
&sensor_dev_attr_temp5_offset.dev_attr.attr,
- &dev_attr_update_interval.attr,
NULL
};
-ATTRIBUTE_GROUPS(lm95234);
+
+static const struct attribute_group lm95234_group = {
+ .attrs = lm95234_attrs,
+};
static int lm95234_detect(struct i2c_client *client,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
+ int address = client->addr;
+ u8 config_mask, model_mask;
int mfg_id, chip_id, val;
+ const char *name;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
@@ -622,15 +641,31 @@ static int lm95234_detect(struct i2c_client *client,
return -ENODEV;
chip_id = i2c_smbus_read_byte_data(client, LM95234_REG_CHIP_ID);
- if (chip_id != LM95234_CHIP_ID)
+ switch (chip_id) {
+ case LM95233_CHIP_ID:
+ if (address != 0x18 && address != 0x2a && address != 0x2b)
+ return -ENODEV;
+ config_mask = 0xbf;
+ model_mask = 0xf9;
+ name = "lm95233";
+ break;
+ case LM95234_CHIP_ID:
+ if (address != 0x18 && address != 0x4d && address != 0x4e)
+ return -ENODEV;
+ config_mask = 0xbc;
+ model_mask = 0xe1;
+ name = "lm95234";
+ break;
+ default:
return -ENODEV;
+ }
val = i2c_smbus_read_byte_data(client, LM95234_REG_STATUS);
if (val & 0x30)
return -ENODEV;
val = i2c_smbus_read_byte_data(client, LM95234_REG_CONFIG);
- if (val & 0xbc)
+ if (val & config_mask)
return -ENODEV;
val = i2c_smbus_read_byte_data(client, LM95234_REG_CONVRATE);
@@ -638,14 +673,14 @@ static int lm95234_detect(struct i2c_client *client,
return -ENODEV;
val = i2c_smbus_read_byte_data(client, LM95234_REG_REM_MODEL);
- if (val & 0xe1)
+ if (val & model_mask)
return -ENODEV;
val = i2c_smbus_read_byte_data(client, LM95234_REG_REM_MODEL_STS);
- if (val & 0xe1)
+ if (val & model_mask)
return -ENODEV;
- strlcpy(info->type, "lm95234", I2C_NAME_SIZE);
+ strlcpy(info->type, name, I2C_NAME_SIZE);
return 0;
}
@@ -698,15 +733,19 @@ static int lm95234_probe(struct i2c_client *client,
if (err < 0)
return err;
+ data->groups[0] = &lm95234_common_group;
+ if (id->driver_data == lm95234)
+ data->groups[1] = &lm95234_group;
+
hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
- data,
- lm95234_groups);
+ data, data->groups);
return PTR_ERR_OR_ZERO(hwmon_dev);
}
/* Driver data (common to all clients) */
static const struct i2c_device_id lm95234_id[] = {
- { "lm95234", 0 },
+ { "lm95233", lm95233 },
+ { "lm95234", lm95234 },
{ }
};
MODULE_DEVICE_TABLE(i2c, lm95234_id);
@@ -725,5 +764,5 @@ static struct i2c_driver lm95234_driver = {
module_i2c_driver(lm95234_driver);
MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
-MODULE_DESCRIPTION("LM95234 sensor driver");
+MODULE_DESCRIPTION("LM95233/LM95234 sensor driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/lm95245.c b/drivers/hwmon/lm95245.c
index 0ae0dfdafdff..e7aef4561c83 100644
--- a/drivers/hwmon/lm95245.c
+++ b/drivers/hwmon/lm95245.c
@@ -1,10 +1,8 @@
/*
* Copyright (C) 2011 Alexander Stein <alexander.stein@systec-electronic.com>
*
- * The LM95245 is a sensor chip made by National Semiconductors.
+ * The LM95245 is a sensor chip made by TI / National Semiconductor.
* It reports up to two temperatures (its own plus an external one).
- * Complete datasheet can be obtained from National's website at:
- * http://www.national.com/ds.cgi/LM/LM95245.pdf
*
* This driver is based on lm95241.c
*
@@ -34,8 +32,6 @@
#include <linux/mutex.h>
#include <linux/sysfs.h>
-#define DEVNAME "lm95245"
-
static const unsigned short normal_i2c[] = {
0x18, 0x19, 0x29, 0x4c, 0x4d, I2C_CLIENT_END };
@@ -98,7 +94,8 @@ static const unsigned short normal_i2c[] = {
#define STATUS1_LOC 0x01
#define MANUFACTURER_ID 0x01
-#define DEFAULT_REVISION 0xB3
+#define LM95235_REVISION 0xB1
+#define LM95245_REVISION 0xB3
static const u8 lm95245_reg_address[] = {
LM95245_REG_R_LOCAL_TEMPH_S,
@@ -427,17 +424,32 @@ static int lm95245_detect(struct i2c_client *new_client,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = new_client->adapter;
+ int address = new_client->addr;
+ const char *name;
+ int rev, id;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
- if (i2c_smbus_read_byte_data(new_client, LM95245_REG_R_MAN_ID)
- != MANUFACTURER_ID
- || i2c_smbus_read_byte_data(new_client, LM95245_REG_R_CHIP_ID)
- != DEFAULT_REVISION)
+ id = i2c_smbus_read_byte_data(new_client, LM95245_REG_R_MAN_ID);
+ if (id != MANUFACTURER_ID)
return -ENODEV;
- strlcpy(info->type, DEVNAME, I2C_NAME_SIZE);
+ rev = i2c_smbus_read_byte_data(new_client, LM95245_REG_R_CHIP_ID);
+ switch (rev) {
+ case LM95235_REVISION:
+ if (address != 0x18 && address != 0x29 && address != 0x4c)
+ return -ENODEV;
+ name = "lm95235";
+ break;
+ case LM95245_REVISION:
+ name = "lm95245";
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ strlcpy(info->type, name, I2C_NAME_SIZE);
return 0;
}
@@ -484,7 +496,8 @@ static int lm95245_probe(struct i2c_client *client,
/* Driver data (common to all clients) */
static const struct i2c_device_id lm95245_id[] = {
- { DEVNAME, 0 },
+ { "lm95235", 0 },
+ { "lm95245", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, lm95245_id);
@@ -492,7 +505,7 @@ MODULE_DEVICE_TABLE(i2c, lm95245_id);
static struct i2c_driver lm95245_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
- .name = DEVNAME,
+ .name = "lm95245",
},
.probe = lm95245_probe,
.id_table = lm95245_id,
@@ -503,5 +516,5 @@ static struct i2c_driver lm95245_driver = {
module_i2c_driver(lm95245_driver);
MODULE_AUTHOR("Alexander Stein <alexander.stein@systec-electronic.com>");
-MODULE_DESCRIPTION("LM95245 sensor driver");
+MODULE_DESCRIPTION("LM95235/LM95245 sensor driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c
index 504cbddbdd90..dc0df57200cd 100644
--- a/drivers/hwmon/nct6775.c
+++ b/drivers/hwmon/nct6775.c
@@ -38,6 +38,7 @@
* nct6776f 9 5 3 6+3 0xc330 0xc1 0x5ca3
* nct6779d 15 5 5 2+6 0xc560 0xc1 0x5ca3
* nct6791d 15 6 6 2+6 0xc800 0xc1 0x5ca3
+ * nct6792d 15 6 6 2+6 0xc910 0xc1 0x5ca3
*
* #temp lists the number of monitored temperature sources (first value) plus
* the number of directly connectable temperature sensors (second value).
@@ -61,7 +62,7 @@
#define USE_ALTERNATE
-enum kinds { nct6106, nct6775, nct6776, nct6779, nct6791 };
+enum kinds { nct6106, nct6775, nct6776, nct6779, nct6791, nct6792 };
/* used to set data->name = nct6775_device_names[data->sio_kind] */
static const char * const nct6775_device_names[] = {
@@ -70,6 +71,7 @@ static const char * const nct6775_device_names[] = {
"nct6776",
"nct6779",
"nct6791",
+ "nct6792",
};
static unsigned short force_id;
@@ -100,6 +102,7 @@ MODULE_PARM_DESC(fan_debounce, "Enable debouncing for fan RPM signal");
#define SIO_NCT6776_ID 0xc330
#define SIO_NCT6779_ID 0xc560
#define SIO_NCT6791_ID 0xc800
+#define SIO_NCT6792_ID 0xc910
#define SIO_ID_MASK 0xFFF0
enum pwm_enable { off, manual, thermal_cruise, speed_cruise, sf3, sf4 };
@@ -529,6 +532,12 @@ static const s8 NCT6791_ALARM_BITS[] = {
4, 5, 13, -1, -1, -1, /* temp1..temp6 */
12, 9 }; /* intrusion0, intrusion1 */
+/* NCT6792 specific data */
+
+static const u16 NCT6792_REG_TEMP_MON[] = {
+ 0x73, 0x75, 0x77, 0x79, 0x7b, 0x7d };
+static const u16 NCT6792_REG_BEEP[NUM_REG_BEEP] = {
+ 0xb2, 0xb3, 0xb4, 0xb5, 0xbf };
/* NCT6102D/NCT6106D specific data */
@@ -1043,13 +1052,14 @@ static bool is_word_sized(struct nct6775_data *data, u16 reg)
reg == 0x73 || reg == 0x75 || reg == 0x77;
case nct6779:
case nct6791:
+ case nct6792:
return reg == 0x150 || reg == 0x153 || reg == 0x155 ||
((reg & 0xfff0) == 0x4b0 && (reg & 0x000f) < 0x0b) ||
reg == 0x402 ||
reg == 0x63a || reg == 0x63c || reg == 0x63e ||
reg == 0x640 || reg == 0x642 ||
reg == 0x73 || reg == 0x75 || reg == 0x77 || reg == 0x79 ||
- reg == 0x7b;
+ reg == 0x7b || reg == 0x7d;
}
return false;
}
@@ -1063,6 +1073,7 @@ static bool is_word_sized(struct nct6775_data *data, u16 reg)
static inline void nct6775_set_bank(struct nct6775_data *data, u16 reg)
{
u8 bank = reg >> 8;
+
if (data->bank != bank) {
outb_p(NCT6775_REG_BANK, data->addr + ADDR_REG_OFFSET);
outb_p(bank, data->addr + DATA_REG_OFFSET);
@@ -1300,6 +1311,7 @@ static void nct6775_update_pwm(struct device *dev)
if (!data->target_speed_tolerance[i] ||
data->pwm_enable[i] == speed_cruise) {
u8 t = fanmodecfg & 0x0f;
+
if (data->REG_TOLERANCE_H) {
t |= (nct6775_read_value(data,
data->REG_TOLERANCE_H[i]) & 0x70) >> 1;
@@ -1391,6 +1403,7 @@ static void nct6775_update_pwm_limits(struct device *dev)
case nct6106:
case nct6779:
case nct6791:
+ case nct6792:
reg = nct6775_read_value(data,
data->REG_CRITICAL_PWM_ENABLE[i]);
if (reg & data->CRITICAL_PWM_ENABLE_MASK)
@@ -1473,6 +1486,7 @@ static struct nct6775_data *nct6775_update_device(struct device *dev)
data->alarms = 0;
for (i = 0; i < NUM_REG_ALARM; i++) {
u8 alarm;
+
if (!data->REG_ALARM[i])
continue;
alarm = nct6775_read_value(data, data->REG_ALARM[i]);
@@ -1482,6 +1496,7 @@ static struct nct6775_data *nct6775_update_device(struct device *dev)
data->beeps = 0;
for (i = 0; i < NUM_REG_BEEP; i++) {
u8 beep;
+
if (!data->REG_BEEP[i])
continue;
beep = nct6775_read_value(data, data->REG_BEEP[i]);
@@ -1504,8 +1519,9 @@ show_in_reg(struct device *dev, struct device_attribute *attr, char *buf)
{
struct nct6775_data *data = nct6775_update_device(dev);
struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
- int nr = sattr->nr;
int index = sattr->index;
+ int nr = sattr->nr;
+
return sprintf(buf, "%ld\n", in_from_reg(data->in[nr][index], nr));
}
@@ -1515,10 +1531,12 @@ store_in_reg(struct device *dev, struct device_attribute *attr, const char *buf,
{
struct nct6775_data *data = dev_get_drvdata(dev);
struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
- int nr = sattr->nr;
int index = sattr->index;
+ int nr = sattr->nr;
unsigned long val;
- int err = kstrtoul(buf, 10, &val);
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
if (err < 0)
return err;
mutex_lock(&data->update_lock);
@@ -1535,6 +1553,7 @@ show_alarm(struct device *dev, struct device_attribute *attr, char *buf)
struct nct6775_data *data = nct6775_update_device(dev);
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
int nr = data->ALARM_BITS[sattr->index];
+
return sprintf(buf, "%u\n",
(unsigned int)((data->alarms >> nr) & 0x01));
}
@@ -1570,6 +1589,7 @@ show_temp_alarm(struct device *dev, struct device_attribute *attr, char *buf)
nr = find_temp_source(data, sattr->index, data->num_temp_alarms);
if (nr >= 0) {
int bit = data->ALARM_BITS[nr + TEMP_ALARM_BASE];
+
alarm = (data->alarms >> bit) & 0x01;
}
return sprintf(buf, "%u\n", alarm);
@@ -1595,8 +1615,9 @@ store_beep(struct device *dev, struct device_attribute *attr, const char *buf,
int nr = data->BEEP_BITS[sattr->index];
int regindex = nr >> 3;
unsigned long val;
+ int err;
- int err = kstrtoul(buf, 10, &val);
+ err = kstrtoul(buf, 10, &val);
if (err < 0)
return err;
if (val > 1)
@@ -1629,6 +1650,7 @@ show_temp_beep(struct device *dev, struct device_attribute *attr, char *buf)
nr = find_temp_source(data, sattr->index, data->num_temp_beeps);
if (nr >= 0) {
int bit = data->BEEP_BITS[nr + TEMP_ALARM_BASE];
+
beep = (data->beeps >> bit) & 0x01;
}
return sprintf(buf, "%u\n", beep);
@@ -1642,8 +1664,9 @@ store_temp_beep(struct device *dev, struct device_attribute *attr,
struct nct6775_data *data = dev_get_drvdata(dev);
int nr, bit, regindex;
unsigned long val;
+ int err;
- int err = kstrtoul(buf, 10, &val);
+ err = kstrtoul(buf, 10, &val);
if (err < 0)
return err;
if (val > 1)
@@ -1715,6 +1738,7 @@ show_fan(struct device *dev, struct device_attribute *attr, char *buf)
struct nct6775_data *data = nct6775_update_device(dev);
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
int nr = sattr->index;
+
return sprintf(buf, "%d\n", data->rpm[nr]);
}
@@ -1724,6 +1748,7 @@ show_fan_min(struct device *dev, struct device_attribute *attr, char *buf)
struct nct6775_data *data = nct6775_update_device(dev);
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
int nr = sattr->index;
+
return sprintf(buf, "%d\n",
data->fan_from_reg_min(data->fan_min[nr],
data->fan_div[nr]));
@@ -1735,6 +1760,7 @@ show_fan_div(struct device *dev, struct device_attribute *attr, char *buf)
struct nct6775_data *data = nct6775_update_device(dev);
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
int nr = sattr->index;
+
return sprintf(buf, "%u\n", div_from_reg(data->fan_div[nr]));
}
@@ -1746,9 +1772,9 @@ store_fan_min(struct device *dev, struct device_attribute *attr,
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
int nr = sattr->index;
unsigned long val;
- int err;
unsigned int reg;
u8 new_div;
+ int err;
err = kstrtoul(buf, 10, &val);
if (err < 0)
@@ -1932,6 +1958,7 @@ show_temp_label(struct device *dev, struct device_attribute *attr, char *buf)
struct nct6775_data *data = nct6775_update_device(dev);
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
int nr = sattr->index;
+
return sprintf(buf, "%s\n", data->temp_label[data->temp_src[nr]]);
}
@@ -2008,6 +2035,7 @@ show_temp_type(struct device *dev, struct device_attribute *attr, char *buf)
struct nct6775_data *data = nct6775_update_device(dev);
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
int nr = sattr->index;
+
return sprintf(buf, "%d\n", (int)data->temp_type[nr]);
}
@@ -2790,6 +2818,7 @@ store_auto_pwm(struct device *dev, struct device_attribute *attr,
case nct6106:
case nct6779:
case nct6791:
+ case nct6792:
nct6775_write_value(data, data->REG_CRITICAL_PWM[nr],
val);
reg = nct6775_read_value(data,
@@ -2997,6 +3026,7 @@ static ssize_t
show_vid(struct device *dev, struct device_attribute *attr, char *buf)
{
struct nct6775_data *data = dev_get_drvdata(dev);
+
return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm));
}
@@ -3202,7 +3232,7 @@ nct6775_check_fan_inputs(struct nct6775_data *data)
pwm4pin = false;
pwm5pin = false;
pwm6pin = false;
- } else { /* NCT6779D or NCT6791D */
+ } else { /* NCT6779D, NCT6791D, or NCT6792D */
regval = superio_inb(sioreg, 0x1c);
fan3pin = !(regval & (1 << 5));
@@ -3215,7 +3245,7 @@ nct6775_check_fan_inputs(struct nct6775_data *data)
fan4min = fan4pin;
- if (data->kind == nct6791) {
+ if (data->kind == nct6791 || data->kind == nct6792) {
regval = superio_inb(sioreg, 0x2d);
fan6pin = (regval & (1 << 1));
pwm6pin = (regval & (1 << 0));
@@ -3588,6 +3618,7 @@ static int nct6775_probe(struct platform_device *pdev)
break;
case nct6791:
+ case nct6792:
data->in_num = 15;
data->pwm_num = 6;
data->auto_pwm_num = 4;
@@ -3650,12 +3681,20 @@ static int nct6775_probe(struct platform_device *pdev)
data->REG_WEIGHT_TEMP[1] = NCT6791_REG_WEIGHT_TEMP_STEP_TOL;
data->REG_WEIGHT_TEMP[2] = NCT6791_REG_WEIGHT_TEMP_BASE;
data->REG_ALARM = NCT6791_REG_ALARM;
- data->REG_BEEP = NCT6776_REG_BEEP;
+ if (data->kind == nct6791)
+ data->REG_BEEP = NCT6776_REG_BEEP;
+ else
+ data->REG_BEEP = NCT6792_REG_BEEP;
reg_temp = NCT6779_REG_TEMP;
- reg_temp_mon = NCT6779_REG_TEMP_MON;
num_reg_temp = ARRAY_SIZE(NCT6779_REG_TEMP);
- num_reg_temp_mon = ARRAY_SIZE(NCT6779_REG_TEMP_MON);
+ if (data->kind == nct6791) {
+ reg_temp_mon = NCT6779_REG_TEMP_MON;
+ num_reg_temp_mon = ARRAY_SIZE(NCT6779_REG_TEMP_MON);
+ } else {
+ reg_temp_mon = NCT6792_REG_TEMP_MON;
+ num_reg_temp_mon = ARRAY_SIZE(NCT6792_REG_TEMP_MON);
+ }
reg_temp_over = NCT6779_REG_TEMP_OVER;
reg_temp_hyst = NCT6779_REG_TEMP_HYST;
reg_temp_config = NCT6779_REG_TEMP_CONFIG;
@@ -3854,6 +3893,7 @@ static int nct6775_probe(struct platform_device *pdev)
case nct6106:
case nct6779:
case nct6791:
+ case nct6792:
break;
}
@@ -3885,6 +3925,7 @@ static int nct6775_probe(struct platform_device *pdev)
tmp |= 0x3e;
break;
case nct6791:
+ case nct6792:
tmp |= 0x7e;
break;
}
@@ -3972,7 +4013,7 @@ static int nct6775_resume(struct device *dev)
mutex_lock(&data->update_lock);
data->bank = 0xff; /* Force initial bank selection */
- if (data->kind == nct6791) {
+ if (data->kind == nct6791 || data->kind == nct6792) {
err = superio_enter(data->sioreg);
if (err)
goto abort;
@@ -4052,6 +4093,7 @@ static const char * const nct6775_sio_names[] __initconst = {
"NCT6776D/F",
"NCT6779D",
"NCT6791D",
+ "NCT6792D",
};
/* nct6775_find() looks for a '627 in the Super-I/O config space */
@@ -4086,6 +4128,9 @@ static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data)
case SIO_NCT6791_ID:
sio_data->kind = nct6791;
break;
+ case SIO_NCT6792_ID:
+ sio_data->kind = nct6792;
+ break;
default:
if (val != 0xffff)
pr_debug("unsupported chip ID: 0x%04x\n", val);
@@ -4111,7 +4156,7 @@ static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data)
superio_outb(sioaddr, SIO_REG_ENABLE, val | 0x01);
}
- if (sio_data->kind == nct6791)
+ if (sio_data->kind == nct6791 || sio_data->kind == nct6792)
nct6791_enable_io_mapping(sioaddr);
superio_exit(sioaddr);
@@ -4221,7 +4266,7 @@ static void __exit sensors_nct6775_exit(void)
}
MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
-MODULE_DESCRIPTION("NCT6106D/NCT6775F/NCT6776F/NCT6779D/NCT6791D driver");
+MODULE_DESCRIPTION("NCT6106D/NCT6775F/NCT6776F/NCT6779D/NCT6791D/NCT6792D driver");
MODULE_LICENSE("GPL");
module_init(sensors_nct6775_init);
diff --git a/drivers/hwmon/nct7802.c b/drivers/hwmon/nct7802.c
new file mode 100644
index 000000000000..ec5678289e4a
--- /dev/null
+++ b/drivers/hwmon/nct7802.c
@@ -0,0 +1,860 @@
+/*
+ * nct7802 - Driver for Nuvoton NCT7802Y
+ *
+ * Copyright (C) 2014 Guenter Roeck <linux@roeck-us.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define DRVNAME "nct7802"
+
+static const u8 REG_VOLTAGE[5] = { 0x09, 0x0a, 0x0c, 0x0d, 0x0e };
+
+static const u8 REG_VOLTAGE_LIMIT_LSB[2][5] = {
+ { 0x40, 0x00, 0x42, 0x44, 0x46 },
+ { 0x3f, 0x00, 0x41, 0x43, 0x45 },
+};
+
+static const u8 REG_VOLTAGE_LIMIT_MSB[5] = { 0x48, 0x00, 0x47, 0x47, 0x48 };
+
+static const u8 REG_VOLTAGE_LIMIT_MSB_SHIFT[2][5] = {
+ { 0, 0, 4, 0, 4 },
+ { 2, 0, 6, 2, 6 },
+};
+
+#define REG_BANK 0x00
+#define REG_TEMP_LSB 0x05
+#define REG_TEMP_PECI_LSB 0x08
+#define REG_VOLTAGE_LOW 0x0f
+#define REG_FANCOUNT_LOW 0x13
+#define REG_START 0x21
+#define REG_MODE 0x22
+#define REG_PECI_ENABLE 0x23
+#define REG_FAN_ENABLE 0x24
+#define REG_VMON_ENABLE 0x25
+#define REG_VENDOR_ID 0xfd
+#define REG_CHIP_ID 0xfe
+#define REG_VERSION_ID 0xff
+
+/*
+ * Data structures and manipulation thereof
+ */
+
+struct nct7802_data {
+ struct regmap *regmap;
+ struct mutex access_lock; /* for multi-byte read and write operations */
+};
+
+static int nct7802_read_temp(struct nct7802_data *data,
+ u8 reg_temp, u8 reg_temp_low, int *temp)
+{
+ unsigned int t1, t2 = 0;
+ int err;
+
+ *temp = 0;
+
+ mutex_lock(&data->access_lock);
+ err = regmap_read(data->regmap, reg_temp, &t1);
+ if (err < 0)
+ goto abort;
+ t1 <<= 8;
+ if (reg_temp_low) { /* 11 bit data */
+ err = regmap_read(data->regmap, reg_temp_low, &t2);
+ if (err < 0)
+ goto abort;
+ }
+ t1 |= t2 & 0xe0;
+ *temp = (s16)t1 / 32 * 125;
+abort:
+ mutex_unlock(&data->access_lock);
+ return err;
+}
+
+static int nct7802_read_fan(struct nct7802_data *data, u8 reg_fan)
+{
+ unsigned int f1, f2;
+ int ret;
+
+ mutex_lock(&data->access_lock);
+ ret = regmap_read(data->regmap, reg_fan, &f1);
+ if (ret < 0)
+ goto abort;
+ ret = regmap_read(data->regmap, REG_FANCOUNT_LOW, &f2);
+ if (ret < 0)
+ goto abort;
+ ret = (f1 << 5) | (f2 >> 3);
+ /* convert fan count to rpm */
+ if (ret == 0x1fff) /* maximum value, assume fan is stopped */
+ ret = 0;
+ else if (ret)
+ ret = DIV_ROUND_CLOSEST(1350000U, ret);
+abort:
+ mutex_unlock(&data->access_lock);
+ return ret;
+}
+
+static int nct7802_read_fan_min(struct nct7802_data *data, u8 reg_fan_low,
+ u8 reg_fan_high)
+{
+ unsigned int f1, f2;
+ int ret;
+
+ mutex_lock(&data->access_lock);
+ ret = regmap_read(data->regmap, reg_fan_low, &f1);
+ if (ret < 0)
+ goto abort;
+ ret = regmap_read(data->regmap, reg_fan_high, &f2);
+ if (ret < 0)
+ goto abort;
+ ret = f1 | ((f2 & 0xf8) << 5);
+ /* convert fan count to rpm */
+ if (ret == 0x1fff) /* maximum value, assume no limit */
+ ret = 0;
+ else if (ret)
+ ret = DIV_ROUND_CLOSEST(1350000U, ret);
+abort:
+ mutex_unlock(&data->access_lock);
+ return ret;
+}
+
+static int nct7802_write_fan_min(struct nct7802_data *data, u8 reg_fan_low,
+ u8 reg_fan_high, unsigned int limit)
+{
+ int err;
+
+ if (limit)
+ limit = DIV_ROUND_CLOSEST(1350000U, limit);
+ else
+ limit = 0x1fff;
+ limit = clamp_val(limit, 0, 0x1fff);
+
+ mutex_lock(&data->access_lock);
+ err = regmap_write(data->regmap, reg_fan_low, limit & 0xff);
+ if (err < 0)
+ goto abort;
+
+ err = regmap_write(data->regmap, reg_fan_high, (limit & 0x1f00) >> 5);
+abort:
+ mutex_unlock(&data->access_lock);
+ return err;
+}
+
+static u8 nct7802_vmul[] = { 4, 2, 2, 2, 2 };
+
+static int nct7802_read_voltage(struct nct7802_data *data, int nr, int index)
+{
+ unsigned int v1, v2;
+ int ret;
+
+ mutex_lock(&data->access_lock);
+ if (index == 0) { /* voltage */
+ ret = regmap_read(data->regmap, REG_VOLTAGE[nr], &v1);
+ if (ret < 0)
+ goto abort;
+ ret = regmap_read(data->regmap, REG_VOLTAGE_LOW, &v2);
+ if (ret < 0)
+ goto abort;
+ ret = ((v1 << 2) | (v2 >> 6)) * nct7802_vmul[nr];
+ } else { /* limit */
+ int shift = 8 - REG_VOLTAGE_LIMIT_MSB_SHIFT[index - 1][nr];
+
+ ret = regmap_read(data->regmap,
+ REG_VOLTAGE_LIMIT_LSB[index - 1][nr], &v1);
+ if (ret < 0)
+ goto abort;
+ ret = regmap_read(data->regmap, REG_VOLTAGE_LIMIT_MSB[nr],
+ &v2);
+ if (ret < 0)
+ goto abort;
+ ret = (v1 | ((v2 << shift) & 0x300)) * nct7802_vmul[nr];
+ }
+abort:
+ mutex_unlock(&data->access_lock);
+ return ret;
+}
+
+static int nct7802_write_voltage(struct nct7802_data *data, int nr, int index,
+ unsigned int voltage)
+{
+ int shift = 8 - REG_VOLTAGE_LIMIT_MSB_SHIFT[index - 1][nr];
+ int err;
+
+ voltage = DIV_ROUND_CLOSEST(voltage, nct7802_vmul[nr]);
+ voltage = clamp_val(voltage, 0, 0x3ff);
+
+ mutex_lock(&data->access_lock);
+ err = regmap_write(data->regmap,
+ REG_VOLTAGE_LIMIT_LSB[index - 1][nr],
+ voltage & 0xff);
+ if (err < 0)
+ goto abort;
+
+ err = regmap_update_bits(data->regmap, REG_VOLTAGE_LIMIT_MSB[nr],
+ 0x0300 >> shift, (voltage & 0x0300) >> shift);
+abort:
+ mutex_unlock(&data->access_lock);
+ return err;
+}
+
+static ssize_t show_in(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ struct nct7802_data *data = dev_get_drvdata(dev);
+ int voltage;
+
+ voltage = nct7802_read_voltage(data, sattr->nr, sattr->index);
+ if (voltage < 0)
+ return voltage;
+
+ return sprintf(buf, "%d\n", voltage);
+}
+
+static ssize_t store_in(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ struct nct7802_data *data = dev_get_drvdata(dev);
+ int index = sattr->index;
+ int nr = sattr->nr;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ err = nct7802_write_voltage(data, nr, index, val);
+ return err ? : count;
+}
+
+static ssize_t show_temp(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct nct7802_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ int err, temp;
+
+ err = nct7802_read_temp(data, sattr->nr, sattr->index, &temp);
+ if (err < 0)
+ return err;
+
+ return sprintf(buf, "%d\n", temp);
+}
+
+static ssize_t store_temp(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ struct nct7802_data *data = dev_get_drvdata(dev);
+ int nr = sattr->nr;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), -128, 127);
+
+ err = regmap_write(data->regmap, nr, val & 0xff);
+ return err ? : count;
+}
+
+static ssize_t show_fan(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ struct nct7802_data *data = dev_get_drvdata(dev);
+ int speed;
+
+ speed = nct7802_read_fan(data, sattr->index);
+ if (speed < 0)
+ return speed;
+
+ return sprintf(buf, "%d\n", speed);
+}
+
+static ssize_t show_fan_min(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ struct nct7802_data *data = dev_get_drvdata(dev);
+ int speed;
+
+ speed = nct7802_read_fan_min(data, sattr->nr, sattr->index);
+ if (speed < 0)
+ return speed;
+
+ return sprintf(buf, "%d\n", speed);
+}
+
+static ssize_t store_fan_min(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ struct nct7802_data *data = dev_get_drvdata(dev);
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ err = nct7802_write_fan_min(data, sattr->nr, sattr->index, val);
+ return err ? : count;
+}
+
+static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct nct7802_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ int bit = sattr->index;
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(data->regmap, sattr->nr, &val);
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%u\n", !!(val & (1 << bit)));
+}
+
+static ssize_t
+show_beep(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ struct nct7802_data *data = dev_get_drvdata(dev);
+ unsigned int regval;
+ int err;
+
+ err = regmap_read(data->regmap, sattr->nr, &regval);
+ if (err)
+ return err;
+
+ return sprintf(buf, "%u\n", !!(regval & (1 << sattr->index)));
+}
+
+static ssize_t
+store_beep(struct device *dev, struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ struct nct7802_data *data = dev_get_drvdata(dev);
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+ if (val > 1)
+ return -EINVAL;
+
+ err = regmap_update_bits(data->regmap, sattr->nr, 1 << sattr->index,
+ val ? 1 << sattr->index : 0);
+ return err ? : count;
+}
+
+static SENSOR_DEVICE_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0x01,
+ REG_TEMP_LSB);
+static SENSOR_DEVICE_ATTR_2(temp1_min, S_IRUGO | S_IWUSR, show_temp,
+ store_temp, 0x31, 0);
+static SENSOR_DEVICE_ATTR_2(temp1_max, S_IRUGO | S_IWUSR, show_temp,
+ store_temp, 0x30, 0);
+static SENSOR_DEVICE_ATTR_2(temp1_crit, S_IRUGO | S_IWUSR, show_temp,
+ store_temp, 0x3a, 0);
+
+static SENSOR_DEVICE_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 0x02,
+ REG_TEMP_LSB);
+static SENSOR_DEVICE_ATTR_2(temp2_min, S_IRUGO | S_IWUSR, show_temp,
+ store_temp, 0x33, 0);
+static SENSOR_DEVICE_ATTR_2(temp2_max, S_IRUGO | S_IWUSR, show_temp,
+ store_temp, 0x32, 0);
+static SENSOR_DEVICE_ATTR_2(temp2_crit, S_IRUGO | S_IWUSR, show_temp,
+ store_temp, 0x3b, 0);
+
+static SENSOR_DEVICE_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 0x03,
+ REG_TEMP_LSB);
+static SENSOR_DEVICE_ATTR_2(temp3_min, S_IRUGO | S_IWUSR, show_temp,
+ store_temp, 0x35, 0);
+static SENSOR_DEVICE_ATTR_2(temp3_max, S_IRUGO | S_IWUSR, show_temp,
+ store_temp, 0x34, 0);
+static SENSOR_DEVICE_ATTR_2(temp3_crit, S_IRUGO | S_IWUSR, show_temp,
+ store_temp, 0x3c, 0);
+
+static SENSOR_DEVICE_ATTR_2(temp4_input, S_IRUGO, show_temp, NULL, 0x04, 0);
+static SENSOR_DEVICE_ATTR_2(temp4_min, S_IRUGO | S_IWUSR, show_temp,
+ store_temp, 0x37, 0);
+static SENSOR_DEVICE_ATTR_2(temp4_max, S_IRUGO | S_IWUSR, show_temp,
+ store_temp, 0x36, 0);
+static SENSOR_DEVICE_ATTR_2(temp4_crit, S_IRUGO | S_IWUSR, show_temp,
+ store_temp, 0x3d, 0);
+
+static SENSOR_DEVICE_ATTR_2(temp5_input, S_IRUGO, show_temp, NULL, 0x06,
+ REG_TEMP_PECI_LSB);
+static SENSOR_DEVICE_ATTR_2(temp5_min, S_IRUGO | S_IWUSR, show_temp,
+ store_temp, 0x39, 0);
+static SENSOR_DEVICE_ATTR_2(temp5_max, S_IRUGO | S_IWUSR, show_temp,
+ store_temp, 0x38, 0);
+static SENSOR_DEVICE_ATTR_2(temp5_crit, S_IRUGO | S_IWUSR, show_temp,
+ store_temp, 0x3e, 0);
+
+static SENSOR_DEVICE_ATTR_2(temp6_input, S_IRUGO, show_temp, NULL, 0x07,
+ REG_TEMP_PECI_LSB);
+
+static SENSOR_DEVICE_ATTR_2(temp1_min_alarm, S_IRUGO, show_alarm, NULL,
+ 0x18, 0);
+static SENSOR_DEVICE_ATTR_2(temp2_min_alarm, S_IRUGO, show_alarm, NULL,
+ 0x18, 1);
+static SENSOR_DEVICE_ATTR_2(temp3_min_alarm, S_IRUGO, show_alarm, NULL,
+ 0x18, 2);
+static SENSOR_DEVICE_ATTR_2(temp4_min_alarm, S_IRUGO, show_alarm, NULL,
+ 0x18, 3);
+static SENSOR_DEVICE_ATTR_2(temp5_min_alarm, S_IRUGO, show_alarm, NULL,
+ 0x18, 4);
+
+static SENSOR_DEVICE_ATTR_2(temp1_max_alarm, S_IRUGO, show_alarm, NULL,
+ 0x19, 0);
+static SENSOR_DEVICE_ATTR_2(temp2_max_alarm, S_IRUGO, show_alarm, NULL,
+ 0x19, 1);
+static SENSOR_DEVICE_ATTR_2(temp3_max_alarm, S_IRUGO, show_alarm, NULL,
+ 0x19, 2);
+static SENSOR_DEVICE_ATTR_2(temp4_max_alarm, S_IRUGO, show_alarm, NULL,
+ 0x19, 3);
+static SENSOR_DEVICE_ATTR_2(temp5_max_alarm, S_IRUGO, show_alarm, NULL,
+ 0x19, 4);
+
+static SENSOR_DEVICE_ATTR_2(temp1_crit_alarm, S_IRUGO, show_alarm, NULL,
+ 0x1b, 0);
+static SENSOR_DEVICE_ATTR_2(temp2_crit_alarm, S_IRUGO, show_alarm, NULL,
+ 0x1b, 1);
+static SENSOR_DEVICE_ATTR_2(temp3_crit_alarm, S_IRUGO, show_alarm, NULL,
+ 0x1b, 2);
+static SENSOR_DEVICE_ATTR_2(temp4_crit_alarm, S_IRUGO, show_alarm, NULL,
+ 0x1b, 3);
+static SENSOR_DEVICE_ATTR_2(temp5_crit_alarm, S_IRUGO, show_alarm, NULL,
+ 0x1b, 4);
+
+static SENSOR_DEVICE_ATTR_2(temp1_fault, S_IRUGO, show_alarm, NULL, 0x17, 0);
+static SENSOR_DEVICE_ATTR_2(temp2_fault, S_IRUGO, show_alarm, NULL, 0x17, 1);
+static SENSOR_DEVICE_ATTR_2(temp3_fault, S_IRUGO, show_alarm, NULL, 0x17, 2);
+
+static SENSOR_DEVICE_ATTR_2(temp1_beep, S_IRUGO | S_IWUSR, show_beep,
+ store_beep, 0x5c, 0);
+static SENSOR_DEVICE_ATTR_2(temp2_beep, S_IRUGO | S_IWUSR, show_beep,
+ store_beep, 0x5c, 1);
+static SENSOR_DEVICE_ATTR_2(temp3_beep, S_IRUGO | S_IWUSR, show_beep,
+ store_beep, 0x5c, 2);
+static SENSOR_DEVICE_ATTR_2(temp4_beep, S_IRUGO | S_IWUSR, show_beep,
+ store_beep, 0x5c, 3);
+static SENSOR_DEVICE_ATTR_2(temp5_beep, S_IRUGO | S_IWUSR, show_beep,
+ store_beep, 0x5c, 4);
+static SENSOR_DEVICE_ATTR_2(temp6_beep, S_IRUGO | S_IWUSR, show_beep,
+ store_beep, 0x5c, 5);
+
+static struct attribute *nct7802_temp_attrs[] = {
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_min.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit.dev_attr.attr,
+ &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_fault.dev_attr.attr,
+ &sensor_dev_attr_temp1_beep.dev_attr.attr,
+
+ &sensor_dev_attr_temp2_input.dev_attr.attr, /* 9 */
+ &sensor_dev_attr_temp2_min.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_crit.dev_attr.attr,
+ &sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_fault.dev_attr.attr,
+ &sensor_dev_attr_temp2_beep.dev_attr.attr,
+
+ &sensor_dev_attr_temp3_input.dev_attr.attr, /* 18 */
+ &sensor_dev_attr_temp3_min.dev_attr.attr,
+ &sensor_dev_attr_temp3_max.dev_attr.attr,
+ &sensor_dev_attr_temp3_crit.dev_attr.attr,
+ &sensor_dev_attr_temp3_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_fault.dev_attr.attr,
+ &sensor_dev_attr_temp3_beep.dev_attr.attr,
+
+ &sensor_dev_attr_temp4_input.dev_attr.attr, /* 27 */
+ &sensor_dev_attr_temp4_min.dev_attr.attr,
+ &sensor_dev_attr_temp4_max.dev_attr.attr,
+ &sensor_dev_attr_temp4_crit.dev_attr.attr,
+ &sensor_dev_attr_temp4_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp4_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp4_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp4_beep.dev_attr.attr,
+
+ &sensor_dev_attr_temp5_input.dev_attr.attr, /* 35 */
+ &sensor_dev_attr_temp5_min.dev_attr.attr,
+ &sensor_dev_attr_temp5_max.dev_attr.attr,
+ &sensor_dev_attr_temp5_crit.dev_attr.attr,
+ &sensor_dev_attr_temp5_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp5_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp5_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp5_beep.dev_attr.attr,
+
+ &sensor_dev_attr_temp6_input.dev_attr.attr, /* 43 */
+ &sensor_dev_attr_temp6_beep.dev_attr.attr,
+
+ NULL
+};
+
+static umode_t nct7802_temp_is_visible(struct kobject *kobj,
+ struct attribute *attr, int index)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct nct7802_data *data = dev_get_drvdata(dev);
+ unsigned int reg;
+ int err;
+
+ err = regmap_read(data->regmap, REG_MODE, &reg);
+ if (err < 0)
+ return 0;
+
+ if (index < 9 &&
+ (reg & 03) != 0x01 && (reg & 0x03) != 0x02) /* RD1 */
+ return 0;
+ if (index >= 9 && index < 18 &&
+ (reg & 0x0c) != 0x04 && (reg & 0x0c) != 0x08) /* RD2 */
+ return 0;
+ if (index >= 18 && index < 27 && (reg & 0x30) != 0x10) /* RD3 */
+ return 0;
+ if (index >= 27 && index < 35) /* local */
+ return attr->mode;
+
+ err = regmap_read(data->regmap, REG_PECI_ENABLE, &reg);
+ if (err < 0)
+ return 0;
+
+ if (index >= 35 && index < 43 && !(reg & 0x01)) /* PECI 0 */
+ return 0;
+
+ if (index >= 0x43 && (!(reg & 0x02))) /* PECI 1 */
+ return 0;
+
+ return attr->mode;
+}
+
+static struct attribute_group nct7802_temp_group = {
+ .attrs = nct7802_temp_attrs,
+ .is_visible = nct7802_temp_is_visible,
+};
+
+static SENSOR_DEVICE_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, 0);
+static SENSOR_DEVICE_ATTR_2(in0_min, S_IRUGO | S_IWUSR, show_in, store_in,
+ 0, 1);
+static SENSOR_DEVICE_ATTR_2(in0_max, S_IRUGO | S_IWUSR, show_in, store_in,
+ 0, 2);
+static SENSOR_DEVICE_ATTR_2(in0_alarm, S_IRUGO, show_alarm, NULL, 0x1e, 3);
+static SENSOR_DEVICE_ATTR_2(in0_beep, S_IRUGO | S_IWUSR, show_beep, store_beep,
+ 0x5a, 3);
+
+static SENSOR_DEVICE_ATTR_2(in1_input, S_IRUGO, show_in, NULL, 1, 0);
+
+static SENSOR_DEVICE_ATTR_2(in2_input, S_IRUGO, show_in, NULL, 2, 0);
+static SENSOR_DEVICE_ATTR_2(in2_min, S_IRUGO | S_IWUSR, show_in, store_in,
+ 2, 1);
+static SENSOR_DEVICE_ATTR_2(in2_max, S_IRUGO | S_IWUSR, show_in, store_in,
+ 2, 2);
+static SENSOR_DEVICE_ATTR_2(in2_alarm, S_IRUGO, show_alarm, NULL, 0x1e, 0);
+static SENSOR_DEVICE_ATTR_2(in2_beep, S_IRUGO | S_IWUSR, show_beep, store_beep,
+ 0x5a, 0);
+
+static SENSOR_DEVICE_ATTR_2(in3_input, S_IRUGO, show_in, NULL, 3, 0);
+static SENSOR_DEVICE_ATTR_2(in3_min, S_IRUGO | S_IWUSR, show_in, store_in,
+ 3, 1);
+static SENSOR_DEVICE_ATTR_2(in3_max, S_IRUGO | S_IWUSR, show_in, store_in,
+ 3, 2);
+static SENSOR_DEVICE_ATTR_2(in3_alarm, S_IRUGO, show_alarm, NULL, 0x1e, 1);
+static SENSOR_DEVICE_ATTR_2(in3_beep, S_IRUGO | S_IWUSR, show_beep, store_beep,
+ 0x5a, 1);
+
+static SENSOR_DEVICE_ATTR_2(in4_input, S_IRUGO, show_in, NULL, 4, 0);
+static SENSOR_DEVICE_ATTR_2(in4_min, S_IRUGO | S_IWUSR, show_in, store_in,
+ 4, 1);
+static SENSOR_DEVICE_ATTR_2(in4_max, S_IRUGO | S_IWUSR, show_in, store_in,
+ 4, 2);
+static SENSOR_DEVICE_ATTR_2(in4_alarm, S_IRUGO, show_alarm, NULL, 0x1e, 2);
+static SENSOR_DEVICE_ATTR_2(in4_beep, S_IRUGO | S_IWUSR, show_beep, store_beep,
+ 0x5a, 2);
+
+static struct attribute *nct7802_in_attrs[] = {
+ &sensor_dev_attr_in0_input.dev_attr.attr,
+ &sensor_dev_attr_in0_min.dev_attr.attr,
+ &sensor_dev_attr_in0_max.dev_attr.attr,
+ &sensor_dev_attr_in0_alarm.dev_attr.attr,
+ &sensor_dev_attr_in0_beep.dev_attr.attr,
+
+ &sensor_dev_attr_in1_input.dev_attr.attr, /* 5 */
+
+ &sensor_dev_attr_in2_input.dev_attr.attr, /* 6 */
+ &sensor_dev_attr_in2_min.dev_attr.attr,
+ &sensor_dev_attr_in2_max.dev_attr.attr,
+ &sensor_dev_attr_in2_alarm.dev_attr.attr,
+ &sensor_dev_attr_in2_beep.dev_attr.attr,
+
+ &sensor_dev_attr_in3_input.dev_attr.attr, /* 11 */
+ &sensor_dev_attr_in3_min.dev_attr.attr,
+ &sensor_dev_attr_in3_max.dev_attr.attr,
+ &sensor_dev_attr_in3_alarm.dev_attr.attr,
+ &sensor_dev_attr_in3_beep.dev_attr.attr,
+
+ &sensor_dev_attr_in4_input.dev_attr.attr, /* 17 */
+ &sensor_dev_attr_in4_min.dev_attr.attr,
+ &sensor_dev_attr_in4_max.dev_attr.attr,
+ &sensor_dev_attr_in4_alarm.dev_attr.attr,
+ &sensor_dev_attr_in4_beep.dev_attr.attr,
+
+ NULL,
+};
+
+static umode_t nct7802_in_is_visible(struct kobject *kobj,
+ struct attribute *attr, int index)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct nct7802_data *data = dev_get_drvdata(dev);
+ unsigned int reg;
+ int err;
+
+ if (index < 6) /* VCC, VCORE */
+ return attr->mode;
+
+ err = regmap_read(data->regmap, REG_MODE, &reg);
+ if (err < 0)
+ return 0;
+
+ if (index >= 6 && index < 11 && (reg & 0x03) != 0x03) /* VSEN1 */
+ return 0;
+ if (index >= 11 && index < 17 && (reg & 0x0c) != 0x0c) /* VSEN2 */
+ return 0;
+ if (index >= 17 && (reg & 0x30) != 0x30) /* VSEN3 */
+ return 0;
+
+ return attr->mode;
+}
+
+static struct attribute_group nct7802_in_group = {
+ .attrs = nct7802_in_attrs,
+ .is_visible = nct7802_in_is_visible,
+};
+
+static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0x10);
+static SENSOR_DEVICE_ATTR_2(fan1_min, S_IRUGO | S_IWUSR, show_fan_min,
+ store_fan_min, 0x49, 0x4c);
+static SENSOR_DEVICE_ATTR_2(fan1_alarm, S_IRUGO, show_alarm, NULL, 0x1a, 0);
+static SENSOR_DEVICE_ATTR_2(fan1_beep, S_IRUGO | S_IWUSR, show_beep, store_beep,
+ 0x5b, 0);
+static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 0x11);
+static SENSOR_DEVICE_ATTR_2(fan2_min, S_IRUGO | S_IWUSR, show_fan_min,
+ store_fan_min, 0x4a, 0x4d);
+static SENSOR_DEVICE_ATTR_2(fan2_alarm, S_IRUGO, show_alarm, NULL, 0x1a, 1);
+static SENSOR_DEVICE_ATTR_2(fan2_beep, S_IRUGO | S_IWUSR, show_beep, store_beep,
+ 0x5b, 1);
+static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 0x12);
+static SENSOR_DEVICE_ATTR_2(fan3_min, S_IRUGO | S_IWUSR, show_fan_min,
+ store_fan_min, 0x4b, 0x4e);
+static SENSOR_DEVICE_ATTR_2(fan3_alarm, S_IRUGO, show_alarm, NULL, 0x1a, 2);
+static SENSOR_DEVICE_ATTR_2(fan3_beep, S_IRUGO | S_IWUSR, show_beep, store_beep,
+ 0x5b, 2);
+
+static struct attribute *nct7802_fan_attrs[] = {
+ &sensor_dev_attr_fan1_input.dev_attr.attr,
+ &sensor_dev_attr_fan1_min.dev_attr.attr,
+ &sensor_dev_attr_fan1_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan1_beep.dev_attr.attr,
+ &sensor_dev_attr_fan2_input.dev_attr.attr,
+ &sensor_dev_attr_fan2_min.dev_attr.attr,
+ &sensor_dev_attr_fan2_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan2_beep.dev_attr.attr,
+ &sensor_dev_attr_fan3_input.dev_attr.attr,
+ &sensor_dev_attr_fan3_min.dev_attr.attr,
+ &sensor_dev_attr_fan3_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan3_beep.dev_attr.attr,
+
+ NULL
+};
+
+static umode_t nct7802_fan_is_visible(struct kobject *kobj,
+ struct attribute *attr, int index)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct nct7802_data *data = dev_get_drvdata(dev);
+ int fan = index / 4; /* 4 attributes per fan */
+ unsigned int reg;
+ int err;
+
+ err = regmap_read(data->regmap, REG_FAN_ENABLE, &reg);
+ if (err < 0 || !(reg & (1 << fan)))
+ return 0;
+
+ return attr->mode;
+}
+
+static struct attribute_group nct7802_fan_group = {
+ .attrs = nct7802_fan_attrs,
+ .is_visible = nct7802_fan_is_visible,
+};
+
+static const struct attribute_group *nct7802_groups[] = {
+ &nct7802_temp_group,
+ &nct7802_in_group,
+ &nct7802_fan_group,
+ NULL
+};
+
+static int nct7802_detect(struct i2c_client *client,
+ struct i2c_board_info *info)
+{
+ int reg;
+
+ /*
+ * Chip identification registers are only available in bank 0,
+ * so only attempt chip detection if bank 0 is selected
+ */
+ reg = i2c_smbus_read_byte_data(client, REG_BANK);
+ if (reg != 0x00)
+ return -ENODEV;
+
+ reg = i2c_smbus_read_byte_data(client, REG_VENDOR_ID);
+ if (reg != 0x50)
+ return -ENODEV;
+
+ reg = i2c_smbus_read_byte_data(client, REG_CHIP_ID);
+ if (reg != 0xc3)
+ return -ENODEV;
+
+ reg = i2c_smbus_read_byte_data(client, REG_VERSION_ID);
+ if (reg < 0 || (reg & 0xf0) != 0x20)
+ return -ENODEV;
+
+ /* Also validate lower bits of voltage and temperature registers */
+ reg = i2c_smbus_read_byte_data(client, REG_TEMP_LSB);
+ if (reg < 0 || (reg & 0x1f))
+ return -ENODEV;
+
+ reg = i2c_smbus_read_byte_data(client, REG_TEMP_PECI_LSB);
+ if (reg < 0 || (reg & 0x3f))
+ return -ENODEV;
+
+ reg = i2c_smbus_read_byte_data(client, REG_VOLTAGE_LOW);
+ if (reg < 0 || (reg & 0x3f))
+ return -ENODEV;
+
+ strlcpy(info->type, "nct7802", I2C_NAME_SIZE);
+ return 0;
+}
+
+static bool nct7802_regmap_is_volatile(struct device *dev, unsigned int reg)
+{
+ return reg != REG_BANK && reg <= 0x20;
+}
+
+static struct regmap_config nct7802_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .cache_type = REGCACHE_RBTREE,
+ .volatile_reg = nct7802_regmap_is_volatile,
+};
+
+static int nct7802_init_chip(struct nct7802_data *data)
+{
+ int err;
+
+ /* Enable ADC */
+ err = regmap_update_bits(data->regmap, REG_START, 0x01, 0x01);
+ if (err)
+ return err;
+
+ /* Enable local temperature sensor */
+ err = regmap_update_bits(data->regmap, REG_MODE, 0x40, 0x40);
+ if (err)
+ return err;
+
+ /* Enable Vcore and VCC voltage monitoring */
+ return regmap_update_bits(data->regmap, REG_VMON_ENABLE, 0x03, 0x03);
+}
+
+static int nct7802_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct nct7802_data *data;
+ struct device *hwmon_dev;
+ int ret;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+
+ data->regmap = devm_regmap_init_i2c(client, &nct7802_regmap_config);
+ if (IS_ERR(data->regmap))
+ return PTR_ERR(data->regmap);
+
+ mutex_init(&data->access_lock);
+
+ ret = nct7802_init_chip(data);
+ if (ret < 0)
+ return ret;
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+ data,
+ nct7802_groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const unsigned short nct7802_address_list[] = {
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, I2C_CLIENT_END
+};
+
+static const struct i2c_device_id nct7802_idtable[] = {
+ { "nct7802", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, nct7802_idtable);
+
+static struct i2c_driver nct7802_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = DRVNAME,
+ },
+ .detect = nct7802_detect,
+ .probe = nct7802_probe,
+ .id_table = nct7802_idtable,
+ .address_list = nct7802_address_list,
+};
+
+module_i2c_driver(nct7802_driver);
+
+MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
+MODULE_DESCRIPTION("NCT7802Y Hardware Monitoring Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
index 6e1e4935fc62..a674cd83a4e2 100644
--- a/drivers/hwmon/pmbus/Kconfig
+++ b/drivers/hwmon/pmbus/Kconfig
@@ -47,15 +47,22 @@ config SENSORS_LM25066
be called lm25066.
config SENSORS_LTC2978
- tristate "Linear Technologies LTC2974, LTC2978, LTC3880, and LTC3883"
+ tristate "Linear Technologies LTC2978 and compatibles"
default n
help
If you say yes here you get hardware monitoring support for Linear
- Technology LTC2974, LTC2978, LTC3880, and LTC3883.
+ Technology LTC2974, LTC2977, LTC2978, LTC3880, LTC3883, and LTM4676.
This driver can also be built as a module. If so, the module will
be called ltc2978.
+config SENSORS_LTC2978_REGULATOR
+ boolean "Regulator support for LTC2978 and compatibles"
+ depends on SENSORS_LTC2978 && REGULATOR
+ help
+ If you say yes here you get regulator support for Linear
+ Technology LTC2974, LTC2977, LTC2978, LTC3880, LTC3883, and LTM4676.
+
config SENSORS_MAX16064
tristate "Maxim MAX16064"
default n
diff --git a/drivers/hwmon/pmbus/ltc2978.c b/drivers/hwmon/pmbus/ltc2978.c
index e24ed521051a..0835050ec245 100644
--- a/drivers/hwmon/pmbus/ltc2978.c
+++ b/drivers/hwmon/pmbus/ltc2978.c
@@ -22,6 +22,7 @@
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/i2c.h>
+#include <linux/regulator/driver.h>
#include "pmbus.h"
enum chips { ltc2974, ltc2977, ltc2978, ltc3880, ltc3883, ltm4676 };
@@ -374,6 +375,19 @@ static const struct i2c_device_id ltc2978_id[] = {
};
MODULE_DEVICE_TABLE(i2c, ltc2978_id);
+#if IS_ENABLED(CONFIG_SENSORS_LTC2978_REGULATOR)
+static const struct regulator_desc ltc2978_reg_desc[] = {
+ PMBUS_REGULATOR("vout", 0),
+ PMBUS_REGULATOR("vout", 1),
+ PMBUS_REGULATOR("vout", 2),
+ PMBUS_REGULATOR("vout", 3),
+ PMBUS_REGULATOR("vout", 4),
+ PMBUS_REGULATOR("vout", 5),
+ PMBUS_REGULATOR("vout", 6),
+ PMBUS_REGULATOR("vout", 7),
+};
+#endif /* CONFIG_SENSORS_LTC2978_REGULATOR */
+
static int ltc2978_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -487,13 +501,36 @@ static int ltc2978_probe(struct i2c_client *client,
default:
return -ENODEV;
}
+
+#if IS_ENABLED(CONFIG_SENSORS_LTC2978_REGULATOR)
+ info->num_regulators = info->pages;
+ info->reg_desc = ltc2978_reg_desc;
+ if (info->num_regulators > ARRAY_SIZE(ltc2978_reg_desc)) {
+ dev_err(&client->dev, "num_regulators too large!");
+ info->num_regulators = ARRAY_SIZE(ltc2978_reg_desc);
+ }
+#endif
+
return pmbus_do_probe(client, id, info);
}
-/* This is the driver that will be inserted */
+#ifdef CONFIG_OF
+static const struct of_device_id ltc2978_of_match[] = {
+ { .compatible = "lltc,ltc2974" },
+ { .compatible = "lltc,ltc2977" },
+ { .compatible = "lltc,ltc2978" },
+ { .compatible = "lltc,ltc3880" },
+ { .compatible = "lltc,ltc3883" },
+ { .compatible = "lltc,ltm4676" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ltc2978_of_match);
+#endif
+
static struct i2c_driver ltc2978_driver = {
.driver = {
.name = "ltc2978",
+ .of_match_table = of_match_ptr(ltc2978_of_match),
},
.probe = ltc2978_probe,
.remove = pmbus_do_remove,
diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h
index fa9beb3eb60c..89a23ff836e7 100644
--- a/drivers/hwmon/pmbus/pmbus.h
+++ b/drivers/hwmon/pmbus/pmbus.h
@@ -19,6 +19,8 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#include <linux/regulator/driver.h>
+
#ifndef PMBUS_H
#define PMBUS_H
@@ -186,6 +188,11 @@
#define PMBUS_VIRT_STATUS_VMON (PMBUS_VIRT_BASE + 35)
/*
+ * OPERATION
+ */
+#define PB_OPERATION_CONTROL_ON (1<<7)
+
+/*
* CAPABILITY
*/
#define PB_CAPABILITY_SMBALERT (1<<4)
@@ -365,8 +372,27 @@ struct pmbus_driver_info {
*/
int (*identify)(struct i2c_client *client,
struct pmbus_driver_info *info);
+
+ /* Regulator functionality, if supported by this chip driver. */
+ int num_regulators;
+ const struct regulator_desc *reg_desc;
};
+/* Regulator ops */
+
+extern struct regulator_ops pmbus_regulator_ops;
+
+/* Macro for filling in array of struct regulator_desc */
+#define PMBUS_REGULATOR(_name, _id) \
+ [_id] = { \
+ .name = (_name # _id), \
+ .id = (_id), \
+ .of_match = of_match_ptr(_name # _id), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .ops = &pmbus_regulator_ops, \
+ .owner = THIS_MODULE, \
+ }
+
/* Function declarations */
void pmbus_clear_cache(struct i2c_client *client);
@@ -375,6 +401,10 @@ int pmbus_read_word_data(struct i2c_client *client, u8 page, u8 reg);
int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg, u16 word);
int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg);
int pmbus_write_byte(struct i2c_client *client, int page, u8 value);
+int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg,
+ u8 value);
+int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg,
+ u8 mask, u8 value);
void pmbus_clear_faults(struct i2c_client *client);
bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg);
bool pmbus_check_word_register(struct i2c_client *client, int page, int reg);
diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
index 291d11fe93e7..f2e47c7dd808 100644
--- a/drivers/hwmon/pmbus/pmbus_core.c
+++ b/drivers/hwmon/pmbus/pmbus_core.c
@@ -29,6 +29,8 @@
#include <linux/hwmon-sysfs.h>
#include <linux/jiffies.h>
#include <linux/i2c/pmbus.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
#include "pmbus.h"
/*
@@ -253,6 +255,37 @@ int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg)
}
EXPORT_SYMBOL_GPL(pmbus_read_byte_data);
+int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg, u8 value)
+{
+ int rv;
+
+ rv = pmbus_set_page(client, page);
+ if (rv < 0)
+ return rv;
+
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+EXPORT_SYMBOL_GPL(pmbus_write_byte_data);
+
+int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg,
+ u8 mask, u8 value)
+{
+ unsigned int tmp;
+ int rv;
+
+ rv = pmbus_read_byte_data(client, page, reg);
+ if (rv < 0)
+ return rv;
+
+ tmp = (rv & ~mask) | (value & mask);
+
+ if (tmp != rv)
+ rv = pmbus_write_byte_data(client, page, reg, tmp);
+
+ return rv;
+}
+EXPORT_SYMBOL_GPL(pmbus_update_byte_data);
+
/*
* _pmbus_read_byte_data() is similar to pmbus_read_byte_data(), but checks if
* a device specific mapping function exists and calls it if necessary.
@@ -1727,6 +1760,84 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data,
return 0;
}
+#if IS_ENABLED(CONFIG_REGULATOR)
+static int pmbus_regulator_is_enabled(struct regulator_dev *rdev)
+{
+ struct device *dev = rdev_get_dev(rdev);
+ struct i2c_client *client = to_i2c_client(dev->parent);
+ u8 page = rdev_get_id(rdev);
+ int ret;
+
+ ret = pmbus_read_byte_data(client, page, PMBUS_OPERATION);
+ if (ret < 0)
+ return ret;
+
+ return !!(ret & PB_OPERATION_CONTROL_ON);
+}
+
+static int _pmbus_regulator_on_off(struct regulator_dev *rdev, bool enable)
+{
+ struct device *dev = rdev_get_dev(rdev);
+ struct i2c_client *client = to_i2c_client(dev->parent);
+ u8 page = rdev_get_id(rdev);
+
+ return pmbus_update_byte_data(client, page, PMBUS_OPERATION,
+ PB_OPERATION_CONTROL_ON,
+ enable ? PB_OPERATION_CONTROL_ON : 0);
+}
+
+static int pmbus_regulator_enable(struct regulator_dev *rdev)
+{
+ return _pmbus_regulator_on_off(rdev, 1);
+}
+
+static int pmbus_regulator_disable(struct regulator_dev *rdev)
+{
+ return _pmbus_regulator_on_off(rdev, 0);
+}
+
+struct regulator_ops pmbus_regulator_ops = {
+ .enable = pmbus_regulator_enable,
+ .disable = pmbus_regulator_disable,
+ .is_enabled = pmbus_regulator_is_enabled,
+};
+EXPORT_SYMBOL_GPL(pmbus_regulator_ops);
+
+static int pmbus_regulator_register(struct pmbus_data *data)
+{
+ struct device *dev = data->dev;
+ const struct pmbus_driver_info *info = data->info;
+ const struct pmbus_platform_data *pdata = dev_get_platdata(dev);
+ struct regulator_dev *rdev;
+ int i;
+
+ for (i = 0; i < info->num_regulators; i++) {
+ struct regulator_config config = { };
+
+ config.dev = dev;
+ config.driver_data = data;
+
+ if (pdata && pdata->reg_init_data)
+ config.init_data = &pdata->reg_init_data[i];
+
+ rdev = devm_regulator_register(dev, &info->reg_desc[i],
+ &config);
+ if (IS_ERR(rdev)) {
+ dev_err(dev, "Failed to register %s regulator\n",
+ info->reg_desc[i].name);
+ return PTR_ERR(rdev);
+ }
+ }
+
+ return 0;
+}
+#else
+static int pmbus_regulator_register(struct pmbus_data *data)
+{
+ return 0;
+}
+#endif
+
int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id,
struct pmbus_driver_info *info)
{
@@ -1781,8 +1892,15 @@ int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id,
dev_err(dev, "Failed to register hwmon device\n");
goto out_kfree;
}
+
+ ret = pmbus_regulator_register(data);
+ if (ret)
+ goto out_unregister;
+
return 0;
+out_unregister:
+ hwmon_device_unregister(data->hwmon_dev);
out_kfree:
kfree(data->group.attrs);
return ret;
diff --git a/drivers/hwmon/tmp401.c b/drivers/hwmon/tmp401.c
index 7fa6e7d0b9b6..99664ebc738d 100644
--- a/drivers/hwmon/tmp401.c
+++ b/drivers/hwmon/tmp401.c
@@ -44,9 +44,10 @@
#include <linux/sysfs.h>
/* Addresses to scan */
-static const unsigned short normal_i2c[] = { 0x4c, 0x4d, 0x4e, I2C_CLIENT_END };
+static const unsigned short normal_i2c[] = { 0x37, 0x48, 0x49, 0x4a, 0x4c, 0x4d,
+ 0x4e, 0x4f, I2C_CLIENT_END };
-enum chips { tmp401, tmp411, tmp431, tmp432 };
+enum chips { tmp401, tmp411, tmp431, tmp432, tmp435 };
/*
* The TMP401 registers, note some registers have different addresses for
@@ -136,6 +137,7 @@ static const u8 TMP432_STATUS_REG[] = {
#define TMP411C_DEVICE_ID 0x10
#define TMP431_DEVICE_ID 0x31
#define TMP432_DEVICE_ID 0x32
+#define TMP435_DEVICE_ID 0x35
/*
* Driver data (common to all clients)
@@ -146,6 +148,7 @@ static const struct i2c_device_id tmp401_id[] = {
{ "tmp411", tmp411 },
{ "tmp431", tmp431 },
{ "tmp432", tmp432 },
+ { "tmp435", tmp435 },
{ }
};
MODULE_DEVICE_TABLE(i2c, tmp401_id);
@@ -613,10 +616,10 @@ static const struct attribute_group tmp432_group = {
* Begin non sysfs callback code (aka Real code)
*/
-static void tmp401_init_client(struct tmp401_data *data,
- struct i2c_client *client)
+static int tmp401_init_client(struct tmp401_data *data,
+ struct i2c_client *client)
{
- int config, config_orig;
+ int config, config_orig, status = 0;
/* Set the conversion rate to 2 Hz */
i2c_smbus_write_byte_data(client, TMP401_CONVERSION_RATE_WRITE, 5);
@@ -624,16 +627,18 @@ static void tmp401_init_client(struct tmp401_data *data,
/* Start conversions (disable shutdown if necessary) */
config = i2c_smbus_read_byte_data(client, TMP401_CONFIG_READ);
- if (config < 0) {
- dev_warn(&client->dev, "Initialization failed!\n");
- return;
- }
+ if (config < 0)
+ return config;
config_orig = config;
config &= ~TMP401_CONFIG_SHUTDOWN;
if (config != config_orig)
- i2c_smbus_write_byte_data(client, TMP401_CONFIG_WRITE, config);
+ status = i2c_smbus_write_byte_data(client,
+ TMP401_CONFIG_WRITE,
+ config);
+
+ return status;
}
static int tmp401_detect(struct i2c_client *client,
@@ -675,15 +680,18 @@ static int tmp401_detect(struct i2c_client *client,
kind = tmp411;
break;
case TMP431_DEVICE_ID:
- if (client->addr == 0x4e)
+ if (client->addr != 0x4c && client->addr != 0x4d)
return -ENODEV;
kind = tmp431;
break;
case TMP432_DEVICE_ID:
- if (client->addr == 0x4e)
+ if (client->addr != 0x4c && client->addr != 0x4d)
return -ENODEV;
kind = tmp432;
break;
+ case TMP435_DEVICE_ID:
+ kind = tmp435;
+ break;
default:
return -ENODEV;
}
@@ -705,11 +713,13 @@ static int tmp401_detect(struct i2c_client *client,
static int tmp401_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- const char *names[] = { "TMP401", "TMP411", "TMP431", "TMP432" };
+ static const char * const names[] = {
+ "TMP401", "TMP411", "TMP431", "TMP432", "TMP435"
+ };
struct device *dev = &client->dev;
struct device *hwmon_dev;
struct tmp401_data *data;
- int groups = 0;
+ int groups = 0, status;
data = devm_kzalloc(dev, sizeof(struct tmp401_data), GFP_KERNEL);
if (!data)
@@ -720,7 +730,9 @@ static int tmp401_probe(struct i2c_client *client,
data->kind = id->driver_data;
/* Initialize the TMP401 chip */
- tmp401_init_client(data, client);
+ status = tmp401_init_client(data, client);
+ if (status < 0)
+ return status;
/* Register sysfs hooks */
data->groups[groups++] = &tmp401_group;
diff --git a/include/linux/i2c/pmbus.h b/include/linux/i2c/pmbus.h
index 69280db02c41..ee3c2aba2a8e 100644
--- a/include/linux/i2c/pmbus.h
+++ b/include/linux/i2c/pmbus.h
@@ -40,6 +40,10 @@
struct pmbus_platform_data {
u32 flags; /* Device specific flags */
+
+ /* regulator support */
+ int num_regulators;
+ struct regulator_init_data *reg_init_data;
};
#endif /* _PMBUS_H_ */