aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2015-04-13 14:39:25 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2015-04-13 14:39:25 -0700
commit4b5ca74113a1e02f4a7ea80aea9b977705f3d961 (patch)
tree2c367930ad3dae93ef612d2cd8c772a436d648d4
parentMerge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/egtvedt/linux-avr32 (diff)
parenthwmon: (pwm-fan) Update the duty cycle inorder to control the pwm-fan (diff)
downloadlinux-dev-4b5ca74113a1e02f4a7ea80aea9b977705f3d961.tar.xz
linux-dev-4b5ca74113a1e02f4a7ea80aea9b977705f3d961.zip
Merge tag 'hwmon-for-linus-v4.1' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging
Pull hwmon updates from Guenter Roeck: "Support for new chips: - add support for IT8620E, IT8781F, IT8786E, and IT8790E to it87 driver - add driver for NCT7904 Functional improvements: - support for new devicetree structure in ibmpowernv driver - register pwm-fan and gpio-fan drivers as thermal cooling devices - various minor cleanup and improvements to it87, nct6775, jc42, ibmpex, and coretemp drivers" * tag 'hwmon-for-linus-v4.1' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (43 commits) hwmon: (pwm-fan) Update the duty cycle inorder to control the pwm-fan hwmon: (it87) Use feature macros on sio_data hwmon: (ibmpowernv) Fix build error seen for some configurations hwmon: (gpio-fan) Move the thermal registration after registration is complete hwmon: (ibmpowernv) pretty print labels hwmon: (ibmpowernv) add a label attribute hwmon: (ibmpowernv) add support for the new device tree hwmon: (ibmpowernv) add a helper routine create_hwmon_attr hwmon: (it87) Add support for 6th fan of IT8620E hwmon: (it87) Add support for IT8620E hwmon: (it87) Add support for IT8790E hwmon: (it87) Introduce feature flag to reflect internal in7 sensor hwmon: (it87) Introduce configuration field for chip suffix hwmon: (it87) Fix PWM frequency display for chips with newer PWM control hwmon: (it87) Fix pwm sysfs attribute removal hwmon: (ibmpowernv) do not use the OPAL index for hwmon attribute names hwmon: (ibmpowernv) change create_hwmon_attr_name() prototype hwmon: (ibmpowernv) add a convert_opal_attr_name() routine hwmon: (ibmpowernv) add a get_sensor_type() routine hwmon: (ibmpowernv) replace AMBIENT_TEMP by TEMP ...
-rw-r--r--Documentation/devicetree/bindings/gpio/gpio-fan.txt19
-rw-r--r--Documentation/hwmon/it8748
-rw-r--r--Documentation/hwmon/jc428
-rw-r--r--Documentation/hwmon/nct790460
-rw-r--r--drivers/hwmon/Kconfig18
-rw-r--r--drivers/hwmon/Makefile1
-rw-r--r--drivers/hwmon/coretemp.c11
-rw-r--r--drivers/hwmon/gpio-fan.c130
-rw-r--r--drivers/hwmon/ibmpex.c19
-rw-r--r--drivers/hwmon/ibmpowernv.c267
-rw-r--r--drivers/hwmon/it87.c298
-rw-r--r--drivers/hwmon/jc42.c16
-rw-r--r--drivers/hwmon/nct6775.c76
-rw-r--r--drivers/hwmon/nct7904.c593
-rw-r--r--drivers/hwmon/pwm-fan.c184
-rw-r--r--drivers/hwmon/vexpress.c2
16 files changed, 1489 insertions, 261 deletions
diff --git a/Documentation/devicetree/bindings/gpio/gpio-fan.txt b/Documentation/devicetree/bindings/gpio/gpio-fan.txt
index 2dd457a3469a..439a7430fc68 100644
--- a/Documentation/devicetree/bindings/gpio/gpio-fan.txt
+++ b/Documentation/devicetree/bindings/gpio/gpio-fan.txt
@@ -2,15 +2,20 @@ Bindings for fan connected to GPIO lines
Required properties:
- compatible : "gpio-fan"
+
+Optional properties:
- gpios: Specifies the pins that map to bits in the control value,
ordered MSB-->LSB.
- gpio-fan,speed-map: A mapping of possible fan RPM speeds and the
control value that should be set to achieve them. This array
must have the RPM values in ascending order.
-
-Optional properties:
- alarm-gpios: This pin going active indicates something is wrong with
the fan, and a udev event will be fired.
+- cooling-cells: If used as a cooling device, must be <2>
+ Also see: Documentation/devicetree/bindings/thermal/thermal.txt
+ min and max states are derived from the speed-map of the fan.
+
+Note: At least one the "gpios" or "alarm-gpios" properties must be set.
Examples:
@@ -23,3 +28,13 @@ Examples:
6000 2>;
alarm-gpios = <&gpio1 15 1>;
};
+ gpio_fan_cool: gpio_fan {
+ compatible = "gpio-fan";
+ gpios = <&gpio2 14 1
+ &gpio2 13 1>;
+ gpio-fan,speed-map = <0 0>,
+ <3000 1>,
+ <6000 2>;
+ alarm-gpios = <&gpio2 15 1>;
+ #cooling-cells = <2>; /* min followed by max */
+ };
diff --git a/Documentation/hwmon/it87 b/Documentation/hwmon/it87
index fe80e9adebfa..e87294878334 100644
--- a/Documentation/hwmon/it87
+++ b/Documentation/hwmon/it87
@@ -6,6 +6,10 @@ Supported chips:
Prefix: 'it8603'
Addresses scanned: from Super I/O config space (8 I/O ports)
Datasheet: Not publicly available
+ * IT8620E
+ Prefix: 'it8620'
+ Addresses scanned: from Super I/O config space (8 I/O ports)
+ Datasheet: Not publicly available
* IT8705F
Prefix: 'it87'
Addresses scanned: from Super I/O config space (8 I/O ports)
@@ -42,6 +46,10 @@ Supported chips:
Prefix: 'it8772'
Addresses scanned: from Super I/O config space (8 I/O ports)
Datasheet: Not publicly available
+ * IT8781F
+ Prefix: 'it8781'
+ Addresses scanned: from Super I/O config space (8 I/O ports)
+ Datasheet: Not publicly available
* IT8782F
Prefix: 'it8782'
Addresses scanned: from Super I/O config space (8 I/O ports)
@@ -50,6 +58,14 @@ Supported chips:
Prefix: 'it8783'
Addresses scanned: from Super I/O config space (8 I/O ports)
Datasheet: Not publicly available
+ * IT8786E
+ Prefix: 'it8786'
+ Addresses scanned: from Super I/O config space (8 I/O ports)
+ Datasheet: Not publicly available
+ * IT8790E
+ Prefix: 'it8790'
+ Addresses scanned: from Super I/O config space (8 I/O ports)
+ Datasheet: Not publicly available
* SiS950 [clone of IT8705F]
Prefix: 'it87'
Addresses scanned: from Super I/O config space (8 I/O ports)
@@ -94,9 +110,10 @@ motherboard models.
Description
-----------
-This driver implements support for the IT8603E, IT8623E, IT8705F, IT8712F,
-IT8716F, IT8718F, IT8720F, IT8721F, IT8726F, IT8728F, IT8758E, IT8771E,
-IT8772E, IT8782F, IT8783E/F, and SiS950 chips.
+This driver implements support for the IT8603E, IT8620E, IT8623E, IT8705F,
+IT8712F, IT8716F, IT8718F, IT8720F, IT8721F, IT8726F, IT8728F, IT8758E,
+IT8771E, IT8772E, IT8781F, IT8782F, IT8783E/F, IT8786E, IT8790E, and SiS950
+chips.
These chips are 'Super I/O chips', supporting floppy disks, infrared ports,
joysticks and other miscellaneous stuff. For hardware monitoring, they
@@ -120,11 +137,11 @@ The IT8716F, IT8718F, IT8720F, IT8721F/IT8758E and later IT8712F revisions
have support for 2 additional fans. The additional fans are supported by the
driver.
-The IT8716F, IT8718F, IT8720F, IT8721F/IT8758E, IT8782F, IT8783E/F, and late
-IT8712F and IT8705F also have optional 16-bit tachometer counters for fans 1 to
-3. This is better (no more fan clock divider mess) but not compatible with the
-older chips and revisions. The 16-bit tachometer mode is enabled by the driver
-when one of the above chips is detected.
+The IT8716F, IT8718F, IT8720F, IT8721F/IT8758E, IT8781F, IT8782F, IT8783E/F,
+and late IT8712F and IT8705F also have optional 16-bit tachometer counters
+for fans 1 to 3. This is better (no more fan clock divider mess) but not
+compatible with the older chips and revisions. The 16-bit tachometer mode
+is enabled by the driver when one of the above chips is detected.
The IT8726F is just bit enhanced IT8716F with additional hardware
for AMD power sequencing. Therefore the chip will appear as IT8716F
@@ -134,8 +151,13 @@ The IT8728F, IT8771E, and IT8772E are considered compatible with the IT8721F,
until a datasheet becomes available (hopefully.)
The IT8603E/IT8623E is a custom design, hardware monitoring part is similar to
-IT8728F. It only supports 16-bit fan mode, the full speed mode of the
-fan is not supported (value 0 of pwmX_enable).
+IT8728F. It only supports 3 fans, 16-bit fan mode, and the full speed mode
+of the fan is not supported (value 0 of pwmX_enable).
+
+The IT8620E is another custom design, hardware monitoring part is similar to
+IT8728F. It only supports 16-bit fan mode.
+
+The IT8790E supports up to 3 fans. 16-bit fan mode is always enabled.
Temperatures are measured in degrees Celsius. An alarm is triggered once
when the Overtemperature Shutdown limit is crossed.
@@ -156,10 +178,10 @@ inputs can measure voltages between 0 and 4.08 volts, with a resolution of
0.016 volt (except IT8603E, IT8721F/IT8758E and IT8728F: 0.012 volt.) The
battery voltage in8 does not have limit registers.
-On the IT8603E, IT8721F/IT8758E, IT8782F, and IT8783E/F, some voltage inputs
-are internal and scaled inside the chip:
+On the IT8603E, IT8721F/IT8758E, IT8781F, IT8782F, and IT8783E/F, some
+voltage inputs are internal and scaled inside the chip:
* in3 (optional)
-* in7 (optional for IT8782F and IT8783E/F)
+* in7 (optional for IT8781F, IT8782F, and IT8783E/F)
* in8 (always)
* in9 (relevant for IT8603E only)
The driver handles this transparently so user-space doesn't have to care.
diff --git a/Documentation/hwmon/jc42 b/Documentation/hwmon/jc42
index f3893f7440de..f7f1830a2566 100644
--- a/Documentation/hwmon/jc42
+++ b/Documentation/hwmon/jc42
@@ -11,12 +11,10 @@ Supported chips:
http://www.atmel.com/Images/doc8711.pdf
http://www.atmel.com/Images/Atmel-8852-SEEPROM-AT30TSE002A-Datasheet.pdf
http://www.atmel.com/Images/Atmel-8868-DTS-AT30TSE004A-Datasheet.pdf
- * IDT TSE2002B3, TSE2002GB2, TS3000B3, TS3000GB2
+ * IDT TSE2002B3, TSE2002GB2, TSE2004GB2, TS3000B3, TS3000GB0, TS3000GB2,
+ TS3001GB2
Datasheets:
- http://www.idt.com/sites/default/files/documents/IDT_TSE2002B3C_DST_20100512_120303152056.pdf
- http://www.idt.com/sites/default/files/documents/IDT_TSE2002GB2A1_DST_20111107_120303145914.pdf
- http://www.idt.com/sites/default/files/documents/IDT_TS3000B3A_DST_20101129_120303152013.pdf
- http://www.idt.com/sites/default/files/documents/IDT_TS3000GB2A1_DST_20111104_120303151012.pdf
+ Available from IDT web site
* Maxim MAX6604
Datasheets:
http://datasheets.maxim-ic.com/en/ds/MAX6604.pdf
diff --git a/Documentation/hwmon/nct7904 b/Documentation/hwmon/nct7904
new file mode 100644
index 000000000000..014f112e2a14
--- /dev/null
+++ b/Documentation/hwmon/nct7904
@@ -0,0 +1,60 @@
+Kernel driver nct7904
+====================
+
+Supported chip:
+ * Nuvoton NCT7904D
+ Prefix: nct7904
+ Addresses: I2C 0x2d, 0x2e
+ Datasheet: Publicly available at Nuvoton website
+ http://www.nuvoton.com/
+
+Author: Vadim V. Vlasov <vvlasov@dev.rtsoft.ru>
+
+
+Description
+-----------
+
+The NCT7904D is a hardware monitor supporting up to 20 voltage sensors,
+internal temperature sensor, Intel PECI and AMD SB-TSI CPU temperature
+interface, up to 12 fan tachometer inputs, up to 4 fan control channels
+with SmartFan.
+
+
+Sysfs entries
+-------------
+
+Currently, the driver supports only the following features:
+
+in[1-20]_input Input voltage measurements (mV)
+
+fan[1-12]_input Fan tachometer measurements (rpm)
+
+temp1_input Local temperature (1/1000 degree,
+ 0.125 degree resolution)
+
+temp[2-9]_input CPU temperatures (1/1000 degree,
+ 0.125 degree resolution)
+
+fan[1-4]_mode R/W, 0/1 for manual or SmartFan mode
+ Setting SmartFan mode is supported only if it has been
+ previously configured by BIOS (or configuration EEPROM)
+
+fan[1-4]_pwm R/O in SmartFan mode, R/W in manual control mode
+
+The driver checks sensor control registers and does not export the sensors
+that are not enabled. Anyway, a sensor that is enabled may actually be not
+connected and thus provide zero readings.
+
+
+Limitations
+-----------
+
+The following features are not supported in current version:
+
+ - SmartFan control
+ - Watchdog
+ - GPIO
+ - external temperature sensors
+ - SMI
+ - min/max values
+ - many other...
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 110fade9cb74..25d9e72627e9 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -510,6 +510,7 @@ config SENSORS_G762
config SENSORS_GPIO_FAN
tristate "GPIO fan"
depends on GPIOLIB
+ depends on THERMAL || THERMAL=n
help
If you say yes here you get support for fans connected to GPIO lines.
@@ -599,8 +600,8 @@ config SENSORS_IT87
help
If you say yes here you get support for ITE IT8705F, IT8712F,
IT8716F, IT8718F, IT8720F, IT8721F, IT8726F, IT8728F, IT8758E,
- IT8771E, IT8772E, IT8782F, IT8783E/F and IT8603E sensor chips,
- and the SiS950 clone.
+ IT8771E, IT8772E, IT8781F, IT8782F, IT8783E/F, IT8786E, IT8790E,
+ IT8603E, IT8620E, and IT8623E sensor chips, and the SiS950 clone.
This driver can also be built as a module. If so, the module
will be called it87.
@@ -624,7 +625,7 @@ config SENSORS_JC42
mobile devices and servers. Support will include, but not be limited
to, ADT7408, AT30TS00, CAT34TS02, CAT6095, MAX6604, MCP9804, MCP9805,
MCP98242, MCP98243, MCP98244, MCP9843, SE97, SE98, STTS424(E),
- STTS2002, STTS3000, TSE2002B3, TSE2002GB2, TS3000B3, and TS3000GB2.
+ STTS2002, STTS3000, TSE2002, TSE2004, TS3000, and TS3001.
This driver can also be built as a module. If so, the module
will be called jc42.
@@ -1145,6 +1146,16 @@ config SENSORS_NCT7802
This driver can also be built as a module. If so, the module
will be called nct7802.
+config SENSORS_NCT7904
+ tristate "Nuvoton NCT7904"
+ depends on I2C
+ help
+ If you say yes here you get support for the Nuvoton NCT7904
+ hardware monitoring chip, including manual fan speed control.
+
+ This driver can also be built as a module. If so, the module
+ will be called nct7904.
+
config SENSORS_PCF8591
tristate "Philips PCF8591 ADC/DAC"
depends on I2C
@@ -1164,6 +1175,7 @@ source drivers/hwmon/pmbus/Kconfig
config SENSORS_PWM_FAN
tristate "PWM fan"
depends on (PWM && OF) || COMPILE_TEST
+ depends on THERMAL || THERMAL=n
help
If you say yes here you get support for fans connected to PWM lines.
The driver uses the generic PWM interface, thus it will work on a
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 6c941472e707..b4a40f17e2aa 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -120,6 +120,7 @@ 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_NCT7904) += nct7904.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/coretemp.c b/drivers/hwmon/coretemp.c
index 5b7fec824f10..ed303ba3a593 100644
--- a/drivers/hwmon/coretemp.c
+++ b/drivers/hwmon/coretemp.c
@@ -397,14 +397,13 @@ static int create_core_attrs(struct temp_data *tdata, struct device *dev,
struct device_attribute *devattr, char *buf) = {
show_label, show_crit_alarm, show_temp, show_tjmax,
show_ttarget };
- static const char *const names[TOTAL_ATTRS] = {
- "temp%d_label", "temp%d_crit_alarm",
- "temp%d_input", "temp%d_crit",
- "temp%d_max" };
+ static const char *const suffixes[TOTAL_ATTRS] = {
+ "label", "crit_alarm", "input", "crit", "max"
+ };
for (i = 0; i < tdata->attr_size; i++) {
- snprintf(tdata->attr_name[i], CORETEMP_NAME_LENGTH, names[i],
- attr_no);
+ snprintf(tdata->attr_name[i], CORETEMP_NAME_LENGTH,
+ "temp%d_%s", attr_no, suffixes[i]);
sysfs_attr_init(&tdata->sd_attrs[i].dev_attr.attr);
tdata->sd_attrs[i].dev_attr.attr.name = tdata->attr_name[i];
tdata->sd_attrs[i].dev_attr.attr.mode = S_IRUGO;
diff --git a/drivers/hwmon/gpio-fan.c b/drivers/hwmon/gpio-fan.c
index 36abf814b8c7..a3dae6d0082a 100644
--- a/drivers/hwmon/gpio-fan.c
+++ b/drivers/hwmon/gpio-fan.c
@@ -34,10 +34,13 @@
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/of_gpio.h>
+#include <linux/thermal.h>
struct gpio_fan_data {
struct platform_device *pdev;
struct device *hwmon_dev;
+ /* Cooling device if any */
+ struct thermal_cooling_device *cdev;
struct mutex lock; /* lock GPIOs operations. */
int num_ctrl;
unsigned *ctrl;
@@ -387,6 +390,53 @@ static int fan_ctrl_init(struct gpio_fan_data *fan_data,
return 0;
}
+static int gpio_fan_get_max_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ struct gpio_fan_data *fan_data = cdev->devdata;
+
+ if (!fan_data)
+ return -EINVAL;
+
+ *state = fan_data->num_speed - 1;
+ return 0;
+}
+
+static int gpio_fan_get_cur_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ struct gpio_fan_data *fan_data = cdev->devdata;
+ int r;
+
+ if (!fan_data)
+ return -EINVAL;
+
+ r = get_fan_speed_index(fan_data);
+ if (r < 0)
+ return r;
+
+ *state = r;
+ return 0;
+}
+
+static int gpio_fan_set_cur_state(struct thermal_cooling_device *cdev,
+ unsigned long state)
+{
+ struct gpio_fan_data *fan_data = cdev->devdata;
+
+ if (!fan_data)
+ return -EINVAL;
+
+ set_fan_speed(fan_data, state);
+ return 0;
+}
+
+static const struct thermal_cooling_device_ops gpio_fan_cool_ops = {
+ .get_max_state = gpio_fan_get_max_state,
+ .get_cur_state = gpio_fan_get_cur_state,
+ .set_cur_state = gpio_fan_set_cur_state,
+};
+
#ifdef CONFIG_OF_GPIO
/*
* Translate OpenFirmware node properties into platform_data
@@ -404,10 +454,32 @@ static int gpio_fan_get_of_pdata(struct device *dev,
node = dev->of_node;
+ /* Alarm GPIO if one exists */
+ if (of_gpio_named_count(node, "alarm-gpios") > 0) {
+ struct gpio_fan_alarm *alarm;
+ int val;
+ enum of_gpio_flags flags;
+
+ alarm = devm_kzalloc(dev, sizeof(struct gpio_fan_alarm),
+ GFP_KERNEL);
+ if (!alarm)
+ return -ENOMEM;
+
+ val = of_get_named_gpio_flags(node, "alarm-gpios", 0, &flags);
+ if (val < 0)
+ return val;
+ alarm->gpio = val;
+ alarm->active_low = flags & OF_GPIO_ACTIVE_LOW;
+
+ pdata->alarm = alarm;
+ }
+
/* Fill GPIO pin array */
pdata->num_ctrl = of_gpio_count(node);
if (pdata->num_ctrl <= 0) {
- dev_err(dev, "gpios DT property empty / missing");
+ if (pdata->alarm)
+ return 0;
+ dev_err(dev, "DT properties empty / missing");
return -ENODEV;
}
ctrl = devm_kzalloc(dev, pdata->num_ctrl * sizeof(unsigned),
@@ -460,26 +532,6 @@ static int gpio_fan_get_of_pdata(struct device *dev,
}
pdata->speed = speed;
- /* Alarm GPIO if one exists */
- if (of_gpio_named_count(node, "alarm-gpios") > 0) {
- struct gpio_fan_alarm *alarm;
- int val;
- enum of_gpio_flags flags;
-
- alarm = devm_kzalloc(dev, sizeof(struct gpio_fan_alarm),
- GFP_KERNEL);
- if (!alarm)
- return -ENOMEM;
-
- val = of_get_named_gpio_flags(node, "alarm-gpios", 0, &flags);
- if (val < 0)
- return val;
- alarm->gpio = val;
- alarm->active_low = flags & OF_GPIO_ACTIVE_LOW;
-
- pdata->alarm = alarm;
- }
-
return 0;
}
@@ -495,6 +547,11 @@ static int gpio_fan_probe(struct platform_device *pdev)
struct gpio_fan_data *fan_data;
struct gpio_fan_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ fan_data = devm_kzalloc(&pdev->dev, sizeof(struct gpio_fan_data),
+ GFP_KERNEL);
+ if (!fan_data)
+ return -ENOMEM;
+
#ifdef CONFIG_OF_GPIO
if (!pdata) {
pdata = devm_kzalloc(&pdev->dev,
@@ -512,11 +569,6 @@ static int gpio_fan_probe(struct platform_device *pdev)
return -EINVAL;
#endif /* CONFIG_OF_GPIO */
- fan_data = devm_kzalloc(&pdev->dev, sizeof(struct gpio_fan_data),
- GFP_KERNEL);
- if (!fan_data)
- return -ENOMEM;
-
fan_data->pdev = pdev;
platform_set_drvdata(pdev, fan_data);
mutex_init(&fan_data->lock);
@@ -544,18 +596,39 @@ static int gpio_fan_probe(struct platform_device *pdev)
gpio_fan_groups);
if (IS_ERR(fan_data->hwmon_dev))
return PTR_ERR(fan_data->hwmon_dev);
+#ifdef CONFIG_OF_GPIO
+ /* Optional cooling device register for Device tree platforms */
+ fan_data->cdev = thermal_of_cooling_device_register(pdev->dev.of_node,
+ "gpio-fan",
+ fan_data,
+ &gpio_fan_cool_ops);
+#else /* CONFIG_OF_GPIO */
+ /* Optional cooling device register for non Device tree platforms */
+ fan_data->cdev = thermal_cooling_device_register("gpio-fan", fan_data,
+ &gpio_fan_cool_ops);
+#endif /* CONFIG_OF_GPIO */
dev_info(&pdev->dev, "GPIO fan initialized\n");
return 0;
}
-static void gpio_fan_shutdown(struct platform_device *pdev)
+static int gpio_fan_remove(struct platform_device *pdev)
{
- struct gpio_fan_data *fan_data = dev_get_drvdata(&pdev->dev);
+ struct gpio_fan_data *fan_data = platform_get_drvdata(pdev);
+
+ if (!IS_ERR(fan_data->cdev))
+ thermal_cooling_device_unregister(fan_data->cdev);
if (fan_data->ctrl)
set_fan_speed(fan_data, 0);
+
+ return 0;
+}
+
+static void gpio_fan_shutdown(struct platform_device *pdev)
+{
+ gpio_fan_remove(pdev);
}
#ifdef CONFIG_PM_SLEEP
@@ -589,6 +662,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,
+ .remove = gpio_fan_remove,
.shutdown = gpio_fan_shutdown,
.driver = {
.name = "gpio-fan",
diff --git a/drivers/hwmon/ibmpex.c b/drivers/hwmon/ibmpex.c
index 030e7ff589be..21b9c72f16bd 100644
--- a/drivers/hwmon/ibmpex.c
+++ b/drivers/hwmon/ibmpex.c
@@ -56,15 +56,10 @@ static u8 const temp_sensor_sig[] = {0x74, 0x65, 0x6D};
static u8 const watt_sensor_sig[] = {0x41, 0x43};
#define PEX_NUM_SENSOR_FUNCS 3
-static char const * const power_sensor_name_templates[] = {
- "%s%d_average",
- "%s%d_average_lowest",
- "%s%d_average_highest"
-};
-static char const * const temp_sensor_name_templates[] = {
- "%s%d_input",
- "%s%d_input_lowest",
- "%s%d_input_highest"
+static const char * const sensor_name_suffixes[] = {
+ "",
+ "_lowest",
+ "_highest"
};
static void ibmpex_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data);
@@ -355,9 +350,11 @@ static int create_sensor(struct ibmpex_bmc_data *data, int type,
return -ENOMEM;
if (type == TEMP_SENSOR)
- sprintf(n, temp_sensor_name_templates[func], "temp", counter);
+ sprintf(n, "temp%d_input%s",
+ counter, sensor_name_suffixes[func]);
else if (type == POWER_SENSOR)
- sprintf(n, power_sensor_name_templates[func], "power", counter);
+ sprintf(n, "power%d_average%s",
+ counter, sensor_name_suffixes[func]);
sysfs_attr_init(&data->sensors[sensor].attr[func].dev_attr.attr);
data->sensors[sensor].attr[func].dev_attr.attr.name = n;
diff --git a/drivers/hwmon/ibmpowernv.c b/drivers/hwmon/ibmpowernv.c
index febe8175d36c..4255514b2c72 100644
--- a/drivers/hwmon/ibmpowernv.c
+++ b/drivers/hwmon/ibmpowernv.c
@@ -30,8 +30,11 @@
#include <linux/platform_device.h>
#include <asm/opal.h>
#include <linux/err.h>
+#include <asm/cputhreads.h>
+#include <asm/smp.h>
#define MAX_ATTR_LEN 32
+#define MAX_LABEL_LEN 64
/* Sensor suffix name from DT */
#define DT_FAULT_ATTR_SUFFIX "faulted"
@@ -44,17 +47,20 @@
*/
enum sensors {
FAN,
- AMBIENT_TEMP,
+ TEMP,
POWER_SUPPLY,
POWER_INPUT,
MAX_SENSOR_TYPE,
};
+#define INVALID_INDEX (-1U)
+
static struct sensor_group {
const char *name;
const char *compatible;
struct attribute_group group;
u32 attr_count;
+ u32 hwmon_index;
} sensor_groups[] = {
{"fan", "ibm,opal-sensor-cooling-fan"},
{"temp", "ibm,opal-sensor-amb-temp"},
@@ -64,7 +70,10 @@ static struct sensor_group {
struct sensor_data {
u32 id; /* An opaque id of the firmware for each sensor */
+ u32 hwmon_index;
+ u32 opal_index;
enum sensors type;
+ char label[MAX_LABEL_LEN];
char name[MAX_ATTR_LEN];
struct device_attribute dev_attr;
};
@@ -87,7 +96,7 @@ static ssize_t show_sensor(struct device *dev, struct device_attribute *devattr,
return ret;
/* Convert temperature to milli-degrees */
- if (sdata->type == AMBIENT_TEMP)
+ if (sdata->type == TEMP)
x *= 1000;
/* Convert power to micro-watts */
else if (sdata->type == POWER_INPUT)
@@ -96,8 +105,65 @@ static ssize_t show_sensor(struct device *dev, struct device_attribute *devattr,
return sprintf(buf, "%u\n", x);
}
-static int get_sensor_index_attr(const char *name, u32 *index,
- char *attr)
+static ssize_t show_label(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ struct sensor_data *sdata = container_of(devattr, struct sensor_data,
+ dev_attr);
+
+ return sprintf(buf, "%s\n", sdata->label);
+}
+
+static int __init get_logical_cpu(int hwcpu)
+{
+ int cpu;
+
+ for_each_possible_cpu(cpu)
+ if (get_hard_smp_processor_id(cpu) == hwcpu)
+ return cpu;
+
+ return -ENOENT;
+}
+
+static void __init make_sensor_label(struct device_node *np,
+ struct sensor_data *sdata,
+ const char *label)
+{
+ u32 id;
+ size_t n;
+
+ n = snprintf(sdata->label, sizeof(sdata->label), "%s", label);
+
+ /*
+ * Core temp pretty print
+ */
+ if (!of_property_read_u32(np, "ibm,pir", &id)) {
+ int cpuid = get_logical_cpu(id);
+
+ if (cpuid >= 0)
+ /*
+ * The digital thermal sensors are associated
+ * with a core. Let's print out the range of
+ * cpu ids corresponding to the hardware
+ * threads of the core.
+ */
+ n += snprintf(sdata->label + n,
+ sizeof(sdata->label) - n, " %d-%d",
+ cpuid, cpuid + threads_per_core - 1);
+ else
+ n += snprintf(sdata->label + n,
+ sizeof(sdata->label) - n, " phy%d", id);
+ }
+
+ /*
+ * Membuffer pretty print
+ */
+ if (!of_property_read_u32(np, "ibm,chip-id", &id))
+ n += snprintf(sdata->label + n, sizeof(sdata->label) - n,
+ " %d", id & 0xffff);
+}
+
+static int get_sensor_index_attr(const char *name, u32 *index, char *attr)
{
char *hash_pos = strchr(name, '#');
char buf[8] = { 0 };
@@ -127,46 +193,90 @@ static int get_sensor_index_attr(const char *name, u32 *index,
return 0;
}
+static const char *convert_opal_attr_name(enum sensors type,
+ const char *opal_attr)
+{
+ const char *attr_name = NULL;
+
+ if (!strcmp(opal_attr, DT_FAULT_ATTR_SUFFIX)) {
+ attr_name = "fault";
+ } else if (!strcmp(opal_attr, DT_DATA_ATTR_SUFFIX)) {
+ attr_name = "input";
+ } else if (!strcmp(opal_attr, DT_THRESHOLD_ATTR_SUFFIX)) {
+ if (type == TEMP)
+ attr_name = "max";
+ else if (type == FAN)
+ attr_name = "min";
+ }
+
+ return attr_name;
+}
+
/*
* This function translates the DT node name into the 'hwmon' attribute name.
* IBMPOWERNV device node appear like cooling-fan#2-data, amb-temp#1-thrs etc.
* which need to be mapped as fan2_input, temp1_max respectively before
* populating them inside hwmon device class.
*/
-static int create_hwmon_attr_name(struct device *dev, enum sensors type,
- const char *node_name,
- char *hwmon_attr_name)
+static const char *parse_opal_node_name(const char *node_name,
+ enum sensors type, u32 *index)
{
char attr_suffix[MAX_ATTR_LEN];
- char *attr_name;
- u32 index;
+ const char *attr_name;
int err;
- err = get_sensor_index_attr(node_name, &index, attr_suffix);
- if (err) {
- dev_err(dev, "Sensor device node name '%s' is invalid\n",
- node_name);
- return err;
- }
+ err = get_sensor_index_attr(node_name, index, attr_suffix);
+ if (err)
+ return ERR_PTR(err);
- if (!strcmp(attr_suffix, DT_FAULT_ATTR_SUFFIX)) {
- attr_name = "fault";
- } else if (!strcmp(attr_suffix, DT_DATA_ATTR_SUFFIX)) {
- attr_name = "input";
- } else if (!strcmp(attr_suffix, DT_THRESHOLD_ATTR_SUFFIX)) {
- if (type == AMBIENT_TEMP)
- attr_name = "max";
- else if (type == FAN)
- attr_name = "min";
- else
- return -ENOENT;
- } else {
- return -ENOENT;
+ attr_name = convert_opal_attr_name(type, attr_suffix);
+ if (!attr_name)
+ return ERR_PTR(-ENOENT);
+
+ return attr_name;
+}
+
+static int get_sensor_type(struct device_node *np)
+{
+ enum sensors type;
+ const char *str;
+
+ for (type = 0; type < MAX_SENSOR_TYPE; type++) {
+ if (of_device_is_compatible(np, sensor_groups[type].compatible))
+ return type;
}
- snprintf(hwmon_attr_name, MAX_ATTR_LEN, "%s%d_%s",
- sensor_groups[type].name, index, attr_name);
- return 0;
+ /*
+ * Let's check if we have a newer device tree
+ */
+ if (!of_device_is_compatible(np, "ibm,opal-sensor"))
+ return MAX_SENSOR_TYPE;
+
+ if (of_property_read_string(np, "sensor-type", &str))
+ return MAX_SENSOR_TYPE;
+
+ for (type = 0; type < MAX_SENSOR_TYPE; type++)
+ if (!strcmp(str, sensor_groups[type].name))
+ return type;
+
+ return MAX_SENSOR_TYPE;
+}
+
+static u32 get_sensor_hwmon_index(struct sensor_data *sdata,
+ struct sensor_data *sdata_table, int count)
+{
+ int i;
+
+ /*
+ * We don't use the OPAL index on newer device trees
+ */
+ if (sdata->opal_index != INVALID_INDEX) {
+ for (i = 0; i < count; i++)
+ if (sdata_table[i].opal_index == sdata->opal_index &&
+ sdata_table[i].type == sdata->type)
+ return sdata_table[i].hwmon_index;
+ }
+ return ++sensor_groups[sdata->type].hwmon_index;
}
static int populate_attr_groups(struct platform_device *pdev)
@@ -178,15 +288,22 @@ static int populate_attr_groups(struct platform_device *pdev)
opal = of_find_node_by_path("/ibm,opal/sensors");
for_each_child_of_node(opal, np) {
+ const char *label;
+
if (np->name == NULL)
continue;
- for (type = 0; type < MAX_SENSOR_TYPE; type++)
- if (of_device_is_compatible(np,
- sensor_groups[type].compatible)) {
- sensor_groups[type].attr_count++;
- break;
- }
+ type = get_sensor_type(np);
+ if (type == MAX_SENSOR_TYPE)
+ continue;
+
+ sensor_groups[type].attr_count++;
+
+ /*
+ * add a new attribute for labels
+ */
+ if (!of_property_read_string(np, "label", &label))
+ sensor_groups[type].attr_count++;
}
of_node_put(opal);
@@ -207,6 +324,21 @@ static int populate_attr_groups(struct platform_device *pdev)
return 0;
}
+static void create_hwmon_attr(struct sensor_data *sdata, const char *attr_name,
+ ssize_t (*show)(struct device *dev,
+ struct device_attribute *attr,
+ char *buf))
+{
+ snprintf(sdata->name, MAX_ATTR_LEN, "%s%d_%s",
+ sensor_groups[sdata->type].name, sdata->hwmon_index,
+ attr_name);
+
+ sysfs_attr_init(&sdata->dev_attr.attr);
+ sdata->dev_attr.attr.name = sdata->name;
+ sdata->dev_attr.attr.mode = S_IRUGO;
+ sdata->dev_attr.show = show;
+}
+
/*
* Iterate through the device tree for each child of 'sensors' node, create
* a sysfs attribute file, the file is named by translating the DT node name
@@ -233,18 +365,23 @@ static int create_device_attrs(struct platform_device *pdev)
}
for_each_child_of_node(opal, np) {
+ const char *attr_name;
+ u32 opal_index;
+ const char *label;
+
if (np->name == NULL)
continue;
- for (type = 0; type < MAX_SENSOR_TYPE; type++)
- if (of_device_is_compatible(np,
- sensor_groups[type].compatible))
- break;
-
+ type = get_sensor_type(np);
if (type == MAX_SENSOR_TYPE)
continue;
- if (of_property_read_u32(np, "sensor-id", &sensor_id)) {
+ /*
+ * Newer device trees use a "sensor-data" property
+ * name for input.
+ */
+ if (of_property_read_u32(np, "sensor-id", &sensor_id) &&
+ of_property_read_u32(np, "sensor-data", &sensor_id)) {
dev_info(&pdev->dev,
"'sensor-id' missing in the node '%s'\n",
np->name);
@@ -253,18 +390,46 @@ static int create_device_attrs(struct platform_device *pdev)
sdata[count].id = sensor_id;
sdata[count].type = type;
- err = create_hwmon_attr_name(&pdev->dev, type, np->name,
- sdata[count].name);
- if (err)
- goto exit_put_node;
- sysfs_attr_init(&sdata[count].dev_attr.attr);
- sdata[count].dev_attr.attr.name = sdata[count].name;
- sdata[count].dev_attr.attr.mode = S_IRUGO;
- sdata[count].dev_attr.show = show_sensor;
+ /*
+ * If we can not parse the node name, it means we are
+ * running on a newer device tree. We can just forget
+ * about the OPAL index and use a defaut value for the
+ * hwmon attribute name
+ */
+ attr_name = parse_opal_node_name(np->name, type, &opal_index);
+ if (IS_ERR(attr_name)) {
+ attr_name = "input";
+ opal_index = INVALID_INDEX;
+ }
+
+ sdata[count].opal_index = opal_index;
+ sdata[count].hwmon_index =
+ get_sensor_hwmon_index(&sdata[count], sdata, count);
+
+ create_hwmon_attr(&sdata[count], attr_name, show_sensor);
pgroups[type]->attrs[sensor_groups[type].attr_count++] =
&sdata[count++].dev_attr.attr;
+
+ if (!of_property_read_string(np, "label", &label)) {
+ /*
+ * For the label attribute, we can reuse the
+ * "properties" of the previous "input"
+ * attribute. They are related to the same
+ * sensor.
+ */
+ sdata[count].type = type;
+ sdata[count].opal_index = sdata[count - 1].opal_index;
+ sdata[count].hwmon_index = sdata[count - 1].hwmon_index;
+
+ make_sensor_label(np, &sdata[count], label);
+
+ create_hwmon_attr(&sdata[count], "label", show_label);
+
+ pgroups[type]->attrs[sensor_groups[type].attr_count++] =
+ &sdata[count++].dev_attr.attr;
+ }
}
exit_put_node:
diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c
index 409116c52cc5..d0ee556e8ce0 100644
--- a/drivers/hwmon/it87.c
+++ b/drivers/hwmon/it87.c
@@ -11,6 +11,7 @@
* similar parts. The other devices are supported by different drivers.
*
* Supports: IT8603E Super I/O chip w/LPC interface
+ * IT8620E Super I/O chip w/LPC interface
* IT8623E Super I/O chip w/LPC interface
* IT8705F Super I/O chip w/LPC interface
* IT8712F Super I/O chip w/LPC interface
@@ -23,8 +24,11 @@
* IT8758E Super I/O chip w/LPC interface
* IT8771E Super I/O chip w/LPC interface
* IT8772E Super I/O chip w/LPC interface
+ * IT8781F Super I/O chip w/LPC interface
* IT8782F Super I/O chip w/LPC interface
* IT8783E/F Super I/O chip w/LPC interface
+ * IT8786E Super I/O chip w/LPC interface
+ * IT8790E Super I/O chip w/LPC interface
* Sis950 A clone of the IT8705F
*
* Copyright (C) 2001 Chris Gauthron
@@ -66,7 +70,7 @@
#define DRVNAME "it87"
enum chips { it87, it8712, it8716, it8718, it8720, it8721, it8728, it8771,
- it8772, it8782, it8783, it8603 };
+ it8772, it8781, it8782, it8783, it8786, it8790, it8603, it8620 };
static unsigned short force_id;
module_param(force_id, ushort, 0);
@@ -146,15 +150,20 @@ static inline void superio_exit(void)
#define IT8728F_DEVID 0x8728
#define IT8771E_DEVID 0x8771
#define IT8772E_DEVID 0x8772
+#define IT8781F_DEVID 0x8781
#define IT8782F_DEVID 0x8782
#define IT8783E_DEVID 0x8783
+#define IT8786E_DEVID 0x8786
+#define IT8790E_DEVID 0x8790
#define IT8603E_DEVID 0x8603
+#define IT8620E_DEVID 0x8620
#define IT8623E_DEVID 0x8623
#define IT87_ACT_REG 0x30
#define IT87_BASE_REG 0x60
/* Logical device 7 registers (IT8712F and later) */
#define IT87_SIO_GPIO1_REG 0x25
+#define IT87_SIO_GPIO2_REG 0x26
#define IT87_SIO_GPIO3_REG 0x27
#define IT87_SIO_GPIO5_REG 0x29
#define IT87_SIO_PINX1_REG 0x2a /* Pin selection */
@@ -207,11 +216,11 @@ static bool fix_pwm_polarity;
/* Monitors: 9 voltage (0 to 7, battery), 3 temp (1 to 3), 3 fan (1 to 3) */
-static const u8 IT87_REG_FAN[] = { 0x0d, 0x0e, 0x0f, 0x80, 0x82 };
-static const u8 IT87_REG_FAN_MIN[] = { 0x10, 0x11, 0x12, 0x84, 0x86 };
-static const u8 IT87_REG_FANX[] = { 0x18, 0x19, 0x1a, 0x81, 0x83 };
-static const u8 IT87_REG_FANX_MIN[] = { 0x1b, 0x1c, 0x1d, 0x85, 0x87 };
-static const u8 IT87_REG_TEMP_OFFSET[] = { 0x56, 0x57, 0x59 };
+static const u8 IT87_REG_FAN[] = { 0x0d, 0x0e, 0x0f, 0x80, 0x82, 0x4c };
+static const u8 IT87_REG_FAN_MIN[] = { 0x10, 0x11, 0x12, 0x84, 0x86, 0x4e };
+static const u8 IT87_REG_FANX[] = { 0x18, 0x19, 0x1a, 0x81, 0x83, 0x4d };
+static const u8 IT87_REG_FANX_MIN[] = { 0x1b, 0x1c, 0x1d, 0x85, 0x87, 0x4f };
+static const u8 IT87_REG_TEMP_OFFSET[] = { 0x56, 0x57, 0x59 };
#define IT87_REG_FAN_MAIN_CTRL 0x13
#define IT87_REG_FAN_CTL 0x14
@@ -238,6 +247,7 @@ static const u8 IT87_REG_TEMP_OFFSET[] = { 0x56, 0x57, 0x59 };
struct it87_devices {
const char *name;
+ const char * const suffix;
u16 features;
u8 peci_mask;
u8 old_peci_mask;
@@ -250,79 +260,131 @@ struct it87_devices {
#define FEAT_TEMP_OFFSET (1 << 4)
#define FEAT_TEMP_PECI (1 << 5)
#define FEAT_TEMP_OLD_PECI (1 << 6)
+#define FEAT_FAN16_CONFIG (1 << 7) /* Need to enable 16-bit fans */
+#define FEAT_FIVE_FANS (1 << 8) /* Supports five fans */
+#define FEAT_VID (1 << 9) /* Set if chip supports VID */
+#define FEAT_IN7_INTERNAL (1 << 10) /* Set if in7 is internal */
+#define FEAT_SIX_FANS (1 << 11) /* Supports six fans */
static const struct it87_devices it87_devices[] = {
[it87] = {
.name = "it87",
+ .suffix = "F",
.features = FEAT_OLD_AUTOPWM, /* may need to overwrite */
},
[it8712] = {
.name = "it8712",
- .features = FEAT_OLD_AUTOPWM, /* may need to overwrite */
+ .suffix = "F",
+ .features = FEAT_OLD_AUTOPWM | FEAT_VID,
+ /* may need to overwrite */
},
[it8716] = {
.name = "it8716",
- .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET,
+ .suffix = "F",
+ .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET | FEAT_VID
+ | FEAT_FAN16_CONFIG | FEAT_FIVE_FANS,
},
[it8718] = {
.name = "it8718",
- .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET
- | FEAT_TEMP_OLD_PECI,
+ .suffix = "F",
+ .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET | FEAT_VID
+ | FEAT_TEMP_OLD_PECI | FEAT_FAN16_CONFIG | FEAT_FIVE_FANS,
.old_peci_mask = 0x4,
},
[it8720] = {
.name = "it8720",
- .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET
- | FEAT_TEMP_OLD_PECI,
+ .suffix = "F",
+ .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET | FEAT_VID
+ | FEAT_TEMP_OLD_PECI | FEAT_FAN16_CONFIG | FEAT_FIVE_FANS,
.old_peci_mask = 0x4,
},
[it8721] = {
.name = "it8721",
+ .suffix = "F",
.features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
- | FEAT_TEMP_OFFSET | FEAT_TEMP_OLD_PECI | FEAT_TEMP_PECI,
+ | FEAT_TEMP_OFFSET | FEAT_TEMP_OLD_PECI | FEAT_TEMP_PECI
+ | FEAT_FAN16_CONFIG | FEAT_FIVE_FANS | FEAT_IN7_INTERNAL,
.peci_mask = 0x05,
.old_peci_mask = 0x02, /* Actually reports PCH */
},
[it8728] = {
.name = "it8728",
+ .suffix = "F",
.features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
- | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI,
+ | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_FIVE_FANS
+ | FEAT_IN7_INTERNAL,
.peci_mask = 0x07,
},
[it8771] = {
.name = "it8771",
+ .suffix = "E",
.features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
- | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI,
- /* PECI: guesswork */
- /* 12mV ADC (OHM) */
- /* 16 bit fans (OHM) */
+ | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_IN7_INTERNAL,
+ /* PECI: guesswork */
+ /* 12mV ADC (OHM) */
+ /* 16 bit fans (OHM) */
+ /* three fans, always 16 bit (guesswork) */
.peci_mask = 0x07,
},
[it8772] = {
.name = "it8772",
+ .suffix = "E",
.features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
- | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI,
- /* PECI (coreboot) */
- /* 12mV ADC (HWSensors4, OHM) */
- /* 16 bit fans (HWSensors4, OHM) */
+ | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_IN7_INTERNAL,
+ /* PECI (coreboot) */
+ /* 12mV ADC (HWSensors4, OHM) */
+ /* 16 bit fans (HWSensors4, OHM) */
+ /* three fans, always 16 bit (datasheet) */
.peci_mask = 0x07,
},
+ [it8781] = {
+ .name = "it8781",
+ .suffix = "F",
+ .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET
+ | FEAT_TEMP_OLD_PECI | FEAT_FAN16_CONFIG,
+ .old_peci_mask = 0x4,
+ },
[it8782] = {
.name = "it8782",
+ .suffix = "F",
.features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET
- | FEAT_TEMP_OLD_PECI,
+ | FEAT_TEMP_OLD_PECI | FEAT_FAN16_CONFIG,
.old_peci_mask = 0x4,
},
[it8783] = {
.name = "it8783",
+ .suffix = "E/F",
.features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET
- | FEAT_TEMP_OLD_PECI,
+ | FEAT_TEMP_OLD_PECI | FEAT_FAN16_CONFIG,
.old_peci_mask = 0x4,
},
+ [it8786] = {
+ .name = "it8786",
+ .suffix = "E",
+ .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
+ | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_IN7_INTERNAL,
+ .peci_mask = 0x07,
+ },
+ [it8790] = {
+ .name = "it8790",
+ .suffix = "E",
+ .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
+ | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_IN7_INTERNAL,
+ .peci_mask = 0x07,
+ },
[it8603] = {
.name = "it8603",
+ .suffix = "E",
.features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
- | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI,
+ | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_IN7_INTERNAL,
+ .peci_mask = 0x07,
+ },
+ [it8620] = {
+ .name = "it8620",
+ .suffix = "E",
+ .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
+ | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_SIX_FANS
+ | FEAT_IN7_INTERNAL,
.peci_mask = 0x07,
},
};
@@ -337,6 +399,12 @@ static const struct it87_devices it87_devices[] = {
#define has_temp_old_peci(data, nr) \
(((data)->features & FEAT_TEMP_OLD_PECI) && \
((data)->old_peci_mask & (1 << nr)))
+#define has_fan16_config(data) ((data)->features & FEAT_FAN16_CONFIG)
+#define has_five_fans(data) ((data)->features & (FEAT_FIVE_FANS | \
+ FEAT_SIX_FANS))
+#define has_vid(data) ((data)->features & FEAT_VID)
+#define has_in7_internal(data) ((data)->features & FEAT_IN7_INTERNAL)
+#define has_six_fans(data) ((data)->features & FEAT_SIX_FANS)
struct it87_sio_data {
enum chips type;
@@ -373,7 +441,7 @@ struct it87_data {
u16 in_scaled; /* Internal voltage sensors are scaled */
u8 in[10][3]; /* [nr][0]=in, [1]=min, [2]=max */
u8 has_fan; /* Bitfield, fans enabled */
- u16 fan[5][2]; /* Register values, [nr][0]=fan, [1]=min */
+ u16 fan[6][2]; /* Register values, [nr][0]=fan, [1]=min */
u8 has_temp; /* Bitfield, temp sensors enabled */
s8 temp[3][4]; /* [nr][0]=temp, [1]=min, [2]=max, [3]=offset */
u8 sensor; /* Register value (IT87_REG_TEMP_ENABLE) */
@@ -475,15 +543,25 @@ static int DIV_TO_REG(int val)
}
#define DIV_FROM_REG(val) (1 << (val))
+/*
+ * PWM base frequencies. The frequency has to be divided by either 128 or 256,
+ * depending on the chip type, to calculate the actual PWM frequency.
+ *
+ * Some of the chip datasheets suggest a base frequency of 51 kHz instead
+ * of 750 kHz for the slowest base frequency, resulting in a PWM frequency
+ * of 200 Hz. Sometimes both PWM frequency select registers are affected,
+ * sometimes just one. It is unknown if this is a datasheet error or real,
+ * so this is ignored for now.
+ */
static const unsigned int pwm_freq[8] = {
- 48000000 / 128,
- 24000000 / 128,
- 12000000 / 128,
- 8000000 / 128,
- 6000000 / 128,
- 3000000 / 128,
- 1500000 / 128,
- 750000 / 128,
+ 48000000,
+ 24000000,
+ 12000000,
+ 8000000,
+ 6000000,
+ 3000000,
+ 1500000,
+ 750000,
};
static int it87_probe(struct platform_device *pdev);
@@ -801,8 +879,11 @@ static ssize_t show_pwm_freq(struct device *dev, struct device_attribute *attr,
{
struct it87_data *data = it87_update_device(dev);
int index = (data->fan_ctl >> 4) & 0x07;
+ unsigned int freq;
- return sprintf(buf, "%u\n", pwm_freq[index]);
+ freq = pwm_freq[index] / (has_newer_autopwm(data) ? 256 : 128);
+
+ return sprintf(buf, "%u\n", freq);
}
static ssize_t set_fan(struct device *dev, struct device_attribute *attr,
@@ -1024,6 +1105,9 @@ static ssize_t set_pwm_freq(struct device *dev,
if (kstrtoul(buf, 10, &val) < 0)
return -EINVAL;
+ val = clamp_val(val, 0, 1000000);
+ val *= has_newer_autopwm(data) ? 256 : 128;
+
/* Search for the nearest available frequency */
for (i = 0; i < 7; i++) {
if (val > (pwm_freq[i] + pwm_freq[i+1]) / 2)
@@ -1196,6 +1280,10 @@ static SENSOR_DEVICE_ATTR_2(fan5_input, S_IRUGO, show_fan, NULL, 4, 0);
static SENSOR_DEVICE_ATTR_2(fan5_min, S_IRUGO | S_IWUSR, show_fan, set_fan,
4, 1);
+static SENSOR_DEVICE_ATTR_2(fan6_input, S_IRUGO, show_fan, NULL, 5, 0);
+static SENSOR_DEVICE_ATTR_2(fan6_min, S_IRUGO | S_IWUSR, show_fan, set_fan,
+ 5, 1);
+
static SENSOR_DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR,
show_pwm_enable, set_pwm_enable, 0);
static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, show_pwm, set_pwm, 0);
@@ -1326,6 +1414,7 @@ static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, 1);
static SENSOR_DEVICE_ATTR(fan3_alarm, S_IRUGO, show_alarm, NULL, 2);
static SENSOR_DEVICE_ATTR(fan4_alarm, S_IRUGO, show_alarm, NULL, 3);
static SENSOR_DEVICE_ATTR(fan5_alarm, S_IRUGO, show_alarm, NULL, 6);
+static SENSOR_DEVICE_ATTR(fan6_alarm, S_IRUGO, show_alarm, NULL, 7);
static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 16);
static SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 17);
static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 18);
@@ -1376,6 +1465,7 @@ static SENSOR_DEVICE_ATTR(fan2_beep, S_IRUGO, show_beep, set_beep, 0);
static SENSOR_DEVICE_ATTR(fan3_beep, S_IRUGO, show_beep, set_beep, 0);
static SENSOR_DEVICE_ATTR(fan4_beep, S_IRUGO, show_beep, set_beep, 0);
static SENSOR_DEVICE_ATTR(fan5_beep, S_IRUGO, show_beep, set_beep, 0);
+static SENSOR_DEVICE_ATTR(fan6_beep, S_IRUGO, show_beep, set_beep, 0);
static SENSOR_DEVICE_ATTR(temp1_beep, S_IRUGO | S_IWUSR,
show_beep, set_beep, 2);
static SENSOR_DEVICE_ATTR(temp2_beep, S_IRUGO, show_beep, NULL, 2);
@@ -1579,7 +1669,7 @@ static struct attribute *it87_attributes_temp_beep[] = {
&sensor_dev_attr_temp3_beep.dev_attr.attr,
};
-static struct attribute *it87_attributes_fan[5][3+1] = { {
+static struct attribute *it87_attributes_fan[6][3+1] = { {
&sensor_dev_attr_fan1_input.dev_attr.attr,
&sensor_dev_attr_fan1_min.dev_attr.attr,
&sensor_dev_attr_fan1_alarm.dev_attr.attr,
@@ -1604,14 +1694,20 @@ static struct attribute *it87_attributes_fan[5][3+1] = { {
&sensor_dev_attr_fan5_min.dev_attr.attr,
&sensor_dev_attr_fan5_alarm.dev_attr.attr,
NULL
+}, {
+ &sensor_dev_attr_fan6_input.dev_attr.attr,
+ &sensor_dev_attr_fan6_min.dev_attr.attr,
+ &sensor_dev_attr_fan6_alarm.dev_attr.attr,
+ NULL
} };
-static const struct attribute_group it87_group_fan[5] = {
+static const struct attribute_group it87_group_fan[6] = {
{ .attrs = it87_attributes_fan[0] },
{ .attrs = it87_attributes_fan[1] },
{ .attrs = it87_attributes_fan[2] },
{ .attrs = it87_attributes_fan[3] },
{ .attrs = it87_attributes_fan[4] },
+ { .attrs = it87_attributes_fan[5] },
};
static const struct attribute *it87_attributes_fan_div[] = {
@@ -1693,6 +1789,7 @@ static struct attribute *it87_attributes_fan_beep[] = {
&sensor_dev_attr_fan3_beep.dev_attr.attr,
&sensor_dev_attr_fan4_beep.dev_attr.attr,
&sensor_dev_attr_fan5_beep.dev_attr.attr,
+ &sensor_dev_attr_fan6_beep.dev_attr.attr,
};
static struct attribute *it87_attributes_vid[] = {
@@ -1724,6 +1821,7 @@ static int __init it87_find(unsigned short *address,
int err;
u16 chip_type;
const char *board_vendor, *board_name;
+ const struct it87_devices *config;
err = superio_enter();
if (err)
@@ -1761,16 +1859,28 @@ static int __init it87_find(unsigned short *address,
case IT8772E_DEVID:
sio_data->type = it8772;
break;
+ case IT8781F_DEVID:
+ sio_data->type = it8781;
+ break;
case IT8782F_DEVID:
sio_data->type = it8782;
break;
case IT8783E_DEVID:
sio_data->type = it8783;
break;
+ case IT8786E_DEVID:
+ sio_data->type = it8786;
+ break;
+ case IT8790E_DEVID:
+ sio_data->type = it8790;
+ break;
case IT8603E_DEVID:
case IT8623E_DEVID:
sio_data->type = it8603;
break;
+ case IT8620E_DEVID:
+ sio_data->type = it8620;
+ break;
case 0xffff: /* No device at all */
goto exit;
default:
@@ -1792,30 +1902,34 @@ static int __init it87_find(unsigned short *address,
err = 0;
sio_data->revision = superio_inb(DEVREV) & 0x0f;
- pr_info("Found IT%04x%c chip at 0x%x, revision %d\n", chip_type,
- chip_type == 0x8771 || chip_type == 0x8772 ||
- chip_type == 0x8603 ? 'E' : 'F', *address,
- sio_data->revision);
+ pr_info("Found IT%04x%s chip at 0x%x, revision %d\n", chip_type,
+ it87_devices[sio_data->type].suffix,
+ *address, sio_data->revision);
+
+ config = &it87_devices[sio_data->type];
+
+ /* in7 (VSB or VCCH5V) is always internal on some chips */
+ if (has_in7_internal(config))
+ sio_data->internal |= (1 << 1);
/* in8 (Vbat) is always internal */
- sio_data->internal = (1 << 2);
+ sio_data->internal |= (1 << 2);
+
/* Only the IT8603E has in9 */
if (sio_data->type != it8603)
sio_data->skip_in |= (1 << 9);
- /* Read GPIO config and VID value from LDN 7 (GPIO) */
- if (sio_data->type == it87) {
- /* The IT8705F doesn't have VID pins at all */
+ if (!has_vid(config))
sio_data->skip_vid = 1;
+ /* Read GPIO config and VID value from LDN 7 (GPIO) */
+ if (sio_data->type == it87) {
/* The IT8705F has a different LD number for GPIO */
superio_select(5);
sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f;
} else if (sio_data->type == it8783) {
int reg25, reg27, reg2a, reg2c, regef;
- sio_data->skip_vid = 1; /* No VID */
-
superio_select(GPIO);
reg25 = superio_inb(IT87_SIO_GPIO1_REG);
@@ -1881,7 +1995,6 @@ static int __init it87_find(unsigned short *address,
} else if (sio_data->type == it8603) {
int reg27, reg29;
- sio_data->skip_vid = 1; /* No VID */
superio_select(GPIO);
reg27 = superio_inb(IT87_SIO_GPIO3_REG);
@@ -1902,14 +2015,36 @@ static int __init it87_find(unsigned short *address,
sio_data->skip_in |= (1 << 5); /* No VIN5 */
sio_data->skip_in |= (1 << 6); /* No VIN6 */
- /* no fan4 */
- sio_data->skip_pwm |= (1 << 3);
- sio_data->skip_fan |= (1 << 3);
-
- sio_data->internal |= (1 << 1); /* in7 is VSB */
sio_data->internal |= (1 << 3); /* in9 is AVCC */
sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f;
+ } else if (sio_data->type == it8620) {
+ int reg;
+
+ superio_select(GPIO);
+
+ /* Check for fan4, fan5 */
+ reg = superio_inb(IT87_SIO_GPIO2_REG);
+ if (!(reg & (1 << 5)))
+ sio_data->skip_fan |= (1 << 3);
+ if (!(reg & (1 << 4)))
+ sio_data->skip_fan |= (1 << 4);
+
+ /* Check for pwm3, fan3 */
+ reg = superio_inb(IT87_SIO_GPIO3_REG);
+ if (reg & (1 << 6))
+ sio_data->skip_pwm |= (1 << 2);
+ if (reg & (1 << 7))
+ sio_data->skip_fan |= (1 << 2);
+
+ /* Check for pwm2, fan2 */
+ reg = superio_inb(IT87_SIO_GPIO5_REG);
+ if (reg & (1 << 1))
+ sio_data->skip_pwm |= (1 << 1);
+ if (reg & (1 << 2))
+ sio_data->skip_fan |= (1 << 1);
+
+ sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f;
} else {
int reg;
bool uart6;
@@ -1917,15 +2052,7 @@ static int __init it87_find(unsigned short *address,
superio_select(GPIO);
reg = superio_inb(IT87_SIO_GPIO3_REG);
- if (sio_data->type == it8721 || sio_data->type == it8728 ||
- sio_data->type == it8771 || sio_data->type == it8772 ||
- sio_data->type == it8782) {
- /*
- * IT8721F/IT8758E, and IT8782F don't have VID pins
- * at all, not sure about the IT8728F and compatibles.
- */
- sio_data->skip_vid = 1;
- } else {
+ if (!sio_data->skip_vid) {
/* We need at least 4 VID pins */
if (reg & 0x0f) {
pr_info("VID is disabled (pins used for GPIO)\n");
@@ -1975,10 +2102,7 @@ static int __init it87_find(unsigned short *address,
}
if (reg & (1 << 0))
sio_data->internal |= (1 << 0);
- if ((reg & (1 << 1)) || sio_data->type == it8721 ||
- sio_data->type == it8728 ||
- sio_data->type == it8771 ||
- sio_data->type == it8772)
+ if (reg & (1 << 1))
sio_data->internal |= (1 << 1);
/*
@@ -2050,7 +2174,7 @@ static void it87_remove_files(struct device *dev)
sysfs_remove_file(&dev->kobj,
it87_attributes_temp_beep[i]);
}
- for (i = 0; i < 5; i++) {
+ for (i = 0; i < 6; i++) {
if (!(data->has_fan & (1 << i)))
continue;
sysfs_remove_group(&dev->kobj, &it87_group_fan[i]);
@@ -2062,7 +2186,7 @@ static void it87_remove_files(struct device *dev)
it87_attributes_fan_div[i]);
}
for (i = 0; i < 3; i++) {
- if (sio_data->skip_pwm & (1 << 0))
+ if (sio_data->skip_pwm & (1 << i))
continue;
sysfs_remove_group(&dev->kobj, &it87_group_pwm[i]);
if (has_old_autopwm(data))
@@ -2112,13 +2236,14 @@ static int it87_probe(struct platform_device *pdev)
case it87:
if (sio_data->revision >= 0x03) {
data->features &= ~FEAT_OLD_AUTOPWM;
- data->features |= FEAT_16BIT_FANS;
+ data->features |= FEAT_FAN16_CONFIG | FEAT_16BIT_FANS;
}
break;
case it8712:
if (sio_data->revision >= 0x08) {
data->features &= ~FEAT_OLD_AUTOPWM;
- data->features |= FEAT_16BIT_FANS;
+ data->features |= FEAT_FAN16_CONFIG | FEAT_16BIT_FANS |
+ FEAT_FIVE_FANS;
}
break;
default:
@@ -2147,7 +2272,8 @@ static int it87_probe(struct platform_device *pdev)
data->in_scaled |= (1 << 8); /* in8 is Vbat */
if (sio_data->internal & (1 << 3))
data->in_scaled |= (1 << 9); /* in9 is AVCC */
- } else if (sio_data->type == it8782 || sio_data->type == it8783) {
+ } else if (sio_data->type == it8781 || sio_data->type == it8782 ||
+ sio_data->type == it8783) {
if (sio_data->internal & (1 << 0))
data->in_scaled |= (1 << 3); /* in3 is VCC5V */
if (sio_data->internal & (1 << 1))
@@ -2205,7 +2331,7 @@ static int it87_probe(struct platform_device *pdev)
/* Do not create fan files for disabled fans */
fan_beep_need_rw = 1;
- for (i = 0; i < 5; i++) {
+ for (i = 0; i < 6; i++) {
if (!(data->has_fan & (1 << i)))
continue;
err = sysfs_create_group(&dev->kobj, &it87_group_fan[i]);
@@ -2450,24 +2576,26 @@ static void it87_init_device(struct platform_device *pdev)
}
data->has_fan = (data->fan_main_ctrl >> 4) & 0x07;
- /* Set tachometers to 16-bit mode if needed, IT8603E (and IT8728F?)
- * has it by default */
- if (has_16bit_fans(data) && data->type != it8603) {
- tmp = it87_read_value(data, IT87_REG_FAN_16BIT);
+ tmp = it87_read_value(data, IT87_REG_FAN_16BIT);
+
+ /* Set tachometers to 16-bit mode if needed */
+ if (has_fan16_config(data)) {
if (~tmp & 0x07 & data->has_fan) {
dev_dbg(&pdev->dev,
"Setting fan1-3 to 16-bit mode\n");
it87_write_value(data, IT87_REG_FAN_16BIT,
tmp | 0x07);
}
- /* IT8705F, IT8782F, and IT8783E/F only support three fans. */
- if (data->type != it87 && data->type != it8782 &&
- data->type != it8783) {
- if (tmp & (1 << 4))
- data->has_fan |= (1 << 3); /* fan4 enabled */
- if (tmp & (1 << 5))
- data->has_fan |= (1 << 4); /* fan5 enabled */
- }
+ }
+
+ /* Check for additional fans */
+ if (has_five_fans(data)) {
+ if (tmp & (1 << 4))
+ data->has_fan |= (1 << 3); /* fan4 enabled */
+ if (tmp & (1 << 5))
+ data->has_fan |= (1 << 4); /* fan5 enabled */
+ if (has_six_fans(data) && (tmp & (1 << 2)))
+ data->has_fan |= (1 << 5); /* fan6 enabled */
}
/* Fan input pins may be used for alternative functions */
@@ -2535,7 +2663,7 @@ static struct it87_data *it87_update_device(struct device *dev)
if (data->type == it8603)
data->in[9][0] = it87_read_value(data, 0x2f);
- for (i = 0; i < 5; i++) {
+ for (i = 0; i < 6; i++) {
/* Skip disabled fans */
if (!(data->has_fan & (1 << i)))
continue;
diff --git a/drivers/hwmon/jc42.c b/drivers/hwmon/jc42.c
index 996bdfd5cf25..9887d3224a86 100644
--- a/drivers/hwmon/jc42.c
+++ b/drivers/hwmon/jc42.c
@@ -87,11 +87,14 @@ static const unsigned short normal_i2c[] = {
#define AT30TSE004_DEVID_MASK 0xffff
/* IDT */
-#define TS3000B3_DEVID 0x2903 /* Also matches TSE2002B3 */
-#define TS3000B3_DEVID_MASK 0xffff
+#define TSE2004_DEVID 0x2200
+#define TSE2004_DEVID_MASK 0xff00
-#define TS3000GB2_DEVID 0x2912 /* Also matches TSE2002GB2 */
-#define TS3000GB2_DEVID_MASK 0xffff
+#define TS3000_DEVID 0x2900 /* Also matches TSE2002 */
+#define TS3000_DEVID_MASK 0xff00
+
+#define TS3001_DEVID 0x3000
+#define TS3001_DEVID_MASK 0xff00
/* Maxim */
#define MAX6604_DEVID 0x3e00
@@ -152,8 +155,9 @@ static struct jc42_chips jc42_chips[] = {
{ ADT_MANID, ADT7408_DEVID, ADT7408_DEVID_MASK },
{ ATMEL_MANID, AT30TS00_DEVID, AT30TS00_DEVID_MASK },
{ ATMEL_MANID2, AT30TSE004_DEVID, AT30TSE004_DEVID_MASK },
- { IDT_MANID, TS3000B3_DEVID, TS3000B3_DEVID_MASK },
- { IDT_MANID, TS3000GB2_DEVID, TS3000GB2_DEVID_MASK },
+ { IDT_MANID, TSE2004_DEVID, TSE2004_DEVID_MASK },
+ { IDT_MANID, TS3000_DEVID, TS3000_DEVID_MASK },
+ { IDT_MANID, TS3001_DEVID, TS3001_DEVID_MASK },
{ MAX_MANID, MAX6604_DEVID, MAX6604_DEVID_MASK },
{ MCP_MANID, MCP9804_DEVID, MCP9804_DEVID_MASK },
{ MCP_MANID, MCP98242_DEVID, MCP98242_DEVID_MASK },
diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c
index 1be41177b620..4fcb48103299 100644
--- a/drivers/hwmon/nct6775.c
+++ b/drivers/hwmon/nct6775.c
@@ -57,6 +57,7 @@
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/acpi.h>
+#include <linux/dmi.h>
#include <linux/io.h>
#include "lm75.h"
@@ -880,12 +881,12 @@ struct nct6775_data {
u16 have_temp;
u16 have_temp_fixed;
u16 have_in;
-#ifdef CONFIG_PM
+
/* Remember extra register values over suspend/resume */
u8 vbat;
u8 fandiv1;
u8 fandiv2;
-#endif
+ u8 sio_reg_enable;
};
struct nct6775_sio_data {
@@ -3178,6 +3179,10 @@ nct6775_check_fan_inputs(struct nct6775_data *data)
int sioreg = data->sioreg;
int regval;
+ /* Store SIO_REG_ENABLE for use during resume */
+ superio_select(sioreg, NCT6775_LD_HWM);
+ data->sio_reg_enable = superio_inb(sioreg, SIO_REG_ENABLE);
+
/* fan4 and fan5 share some pins with the GPIO and serial flash */
if (data->kind == nct6775) {
regval = superio_inb(sioreg, 0x2c);
@@ -3195,21 +3200,38 @@ nct6775_check_fan_inputs(struct nct6775_data *data)
pwm6pin = false;
} else if (data->kind == nct6776) {
bool gpok = superio_inb(sioreg, 0x27) & 0x80;
+ const char *board_vendor, *board_name;
- superio_select(sioreg, NCT6775_LD_HWM);
- regval = superio_inb(sioreg, SIO_REG_ENABLE);
+ board_vendor = dmi_get_system_info(DMI_BOARD_VENDOR);
+ board_name = dmi_get_system_info(DMI_BOARD_NAME);
+
+ if (board_name && board_vendor &&
+ !strcmp(board_vendor, "ASRock")) {
+ /*
+ * Auxiliary fan monitoring is not enabled on ASRock
+ * Z77 Pro4-M if booted in UEFI Ultra-FastBoot mode.
+ * Observed with BIOS version 2.00.
+ */
+ if (!strcmp(board_name, "Z77 Pro4-M")) {
+ if ((data->sio_reg_enable & 0xe0) != 0xe0) {
+ data->sio_reg_enable |= 0xe0;
+ superio_outb(sioreg, SIO_REG_ENABLE,
+ data->sio_reg_enable);
+ }
+ }
+ }
- if (regval & 0x80)
+ if (data->sio_reg_enable & 0x80)
fan3pin = gpok;
else
fan3pin = !(superio_inb(sioreg, 0x24) & 0x40);
- if (regval & 0x40)
+ if (data->sio_reg_enable & 0x40)
fan4pin = gpok;
else
fan4pin = superio_inb(sioreg, 0x1C) & 0x01;
- if (regval & 0x20)
+ if (data->sio_reg_enable & 0x20)
fan5pin = gpok;
else
fan5pin = superio_inb(sioreg, 0x1C) & 0x02;
@@ -3989,8 +4011,7 @@ static void nct6791_enable_io_mapping(int sioaddr)
}
}
-#ifdef CONFIG_PM
-static int nct6775_suspend(struct device *dev)
+static int __maybe_unused nct6775_suspend(struct device *dev)
{
struct nct6775_data *data = nct6775_update_device(dev);
@@ -4005,22 +4026,29 @@ static int nct6775_suspend(struct device *dev)
return 0;
}
-static int nct6775_resume(struct device *dev)
+static int __maybe_unused nct6775_resume(struct device *dev)
{
struct nct6775_data *data = dev_get_drvdata(dev);
+ int sioreg = data->sioreg;
int i, j, err = 0;
+ u8 reg;
mutex_lock(&data->update_lock);
data->bank = 0xff; /* Force initial bank selection */
- if (data->kind == nct6791 || data->kind == nct6792) {
- err = superio_enter(data->sioreg);
- if (err)
- goto abort;
+ err = superio_enter(sioreg);
+ if (err)
+ goto abort;
- nct6791_enable_io_mapping(data->sioreg);
- superio_exit(data->sioreg);
- }
+ superio_select(sioreg, NCT6775_LD_HWM);
+ reg = superio_inb(sioreg, SIO_REG_ENABLE);
+ if (reg != data->sio_reg_enable)
+ superio_outb(sioreg, SIO_REG_ENABLE, data->sio_reg_enable);
+
+ if (data->kind == nct6791 || data->kind == nct6792)
+ nct6791_enable_io_mapping(sioreg);
+
+ superio_exit(sioreg);
/* Restore limits */
for (i = 0; i < data->in_num; i++) {
@@ -4066,22 +4094,12 @@ abort:
return err;
}
-static const struct dev_pm_ops nct6775_dev_pm_ops = {
- .suspend = nct6775_suspend,
- .resume = nct6775_resume,
- .freeze = nct6775_suspend,
- .restore = nct6775_resume,
-};
-
-#define NCT6775_DEV_PM_OPS (&nct6775_dev_pm_ops)
-#else
-#define NCT6775_DEV_PM_OPS NULL
-#endif /* CONFIG_PM */
+static SIMPLE_DEV_PM_OPS(nct6775_dev_pm_ops, nct6775_suspend, nct6775_resume);
static struct platform_driver nct6775_driver = {
.driver = {
.name = DRVNAME,
- .pm = NCT6775_DEV_PM_OPS,
+ .pm = &nct6775_dev_pm_ops,
},
.probe = nct6775_probe,
};
diff --git a/drivers/hwmon/nct7904.c b/drivers/hwmon/nct7904.c
new file mode 100644
index 000000000000..b77b82f24480
--- /dev/null
+++ b/drivers/hwmon/nct7904.c
@@ -0,0 +1,593 @@
+/*
+ * nct7904.c - driver for Nuvoton NCT7904D.
+ *
+ * Copyright (c) 2015 Kontron
+ * Author: Vadim V. Vlasov <vvlasov@dev.rtsoft.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+
+#define VENDOR_ID_REG 0x7A /* Any bank */
+#define NUVOTON_ID 0x50
+#define CHIP_ID_REG 0x7B /* Any bank */
+#define NCT7904_ID 0xC5
+#define DEVICE_ID_REG 0x7C /* Any bank */
+
+#define BANK_SEL_REG 0xFF
+#define BANK_0 0x00
+#define BANK_1 0x01
+#define BANK_2 0x02
+#define BANK_3 0x03
+#define BANK_4 0x04
+#define BANK_MAX 0x04
+
+#define FANIN_MAX 12 /* Counted from 1 */
+#define VSEN_MAX 21 /* VSEN1..14, 3VDD, VBAT, V3VSB,
+ LTD (not a voltage), VSEN17..19 */
+#define FANCTL_MAX 4 /* Counted from 1 */
+#define TCPU_MAX 8 /* Counted from 1 */
+#define TEMP_MAX 4 /* Counted from 1 */
+
+#define VT_ADC_CTRL0_REG 0x20 /* Bank 0 */
+#define VT_ADC_CTRL1_REG 0x21 /* Bank 0 */
+#define VT_ADC_CTRL2_REG 0x22 /* Bank 0 */
+#define FANIN_CTRL0_REG 0x24
+#define FANIN_CTRL1_REG 0x25
+#define DTS_T_CTRL0_REG 0x26
+#define DTS_T_CTRL1_REG 0x27
+#define VT_ADC_MD_REG 0x2E
+
+#define VSEN1_HV_REG 0x40 /* Bank 0; 2 regs (HV/LV) per sensor */
+#define TEMP_CH1_HV_REG 0x42 /* Bank 0; same as VSEN2_HV */
+#define LTD_HV_REG 0x62 /* Bank 0; 2 regs in VSEN range */
+#define FANIN1_HV_REG 0x80 /* Bank 0; 2 regs (HV/LV) per sensor */
+#define T_CPU1_HV_REG 0xA0 /* Bank 0; 2 regs (HV/LV) per sensor */
+
+#define PRTS_REG 0x03 /* Bank 2 */
+#define FANCTL1_FMR_REG 0x00 /* Bank 3; 1 reg per channel */
+#define FANCTL1_OUT_REG 0x10 /* Bank 3; 1 reg per channel */
+
+static const unsigned short normal_i2c[] = {
+ 0x2d, 0x2e, I2C_CLIENT_END
+};
+
+struct nct7904_data {
+ struct i2c_client *client;
+ struct mutex bank_lock;
+ int bank_sel;
+ u32 fanin_mask;
+ u32 vsen_mask;
+ u32 tcpu_mask;
+ u8 fan_mode[FANCTL_MAX];
+};
+
+/* Access functions */
+static int nct7904_bank_lock(struct nct7904_data *data, unsigned bank)
+{
+ int ret;
+
+ mutex_lock(&data->bank_lock);
+ if (data->bank_sel == bank)
+ return 0;
+ ret = i2c_smbus_write_byte_data(data->client, BANK_SEL_REG, bank);
+ if (ret == 0)
+ data->bank_sel = bank;
+ else
+ data->bank_sel = -1;
+ return ret;
+}
+
+static inline void nct7904_bank_release(struct nct7904_data *data)
+{
+ mutex_unlock(&data->bank_lock);
+}
+
+/* Read 1-byte register. Returns unsigned reg or -ERRNO on error. */
+static int nct7904_read_reg(struct nct7904_data *data,
+ unsigned bank, unsigned reg)
+{
+ struct i2c_client *client = data->client;
+ int ret;
+
+ ret = nct7904_bank_lock(data, bank);
+ if (ret == 0)
+ ret = i2c_smbus_read_byte_data(client, reg);
+
+ nct7904_bank_release(data);
+ return ret;
+}
+
+/*
+ * Read 2-byte register. Returns register in big-endian format or
+ * -ERRNO on error.
+ */
+static int nct7904_read_reg16(struct nct7904_data *data,
+ unsigned bank, unsigned reg)
+{
+ struct i2c_client *client = data->client;
+ int ret, hi;
+
+ ret = nct7904_bank_lock(data, bank);
+ if (ret == 0) {
+ ret = i2c_smbus_read_byte_data(client, reg);
+ if (ret >= 0) {
+ hi = ret;
+ ret = i2c_smbus_read_byte_data(client, reg + 1);
+ if (ret >= 0)
+ ret |= hi << 8;
+ }
+ }
+
+ nct7904_bank_release(data);
+ return ret;
+}
+
+/* Write 1-byte register. Returns 0 or -ERRNO on error. */
+static int nct7904_write_reg(struct nct7904_data *data,
+ unsigned bank, unsigned reg, u8 val)
+{
+ struct i2c_client *client = data->client;
+ int ret;
+
+ ret = nct7904_bank_lock(data, bank);
+ if (ret == 0)
+ ret = i2c_smbus_write_byte_data(client, reg, val);
+
+ nct7904_bank_release(data);
+ return ret;
+}
+
+/* FANIN ATTR */
+static ssize_t show_fan(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ struct nct7904_data *data = dev_get_drvdata(dev);
+ int ret;
+ unsigned cnt, rpm;
+
+ ret = nct7904_read_reg16(data, BANK_0, FANIN1_HV_REG + index * 2);
+ if (ret < 0)
+ return ret;
+ cnt = ((ret & 0xff00) >> 3) | (ret & 0x1f);
+ if (cnt == 0x1fff)
+ rpm = 0;
+ else
+ rpm = 1350000 / cnt;
+ return sprintf(buf, "%u\n", rpm);
+}
+
+static umode_t nct7904_fanin_is_visible(struct kobject *kobj,
+ struct attribute *a, int n)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct nct7904_data *data = dev_get_drvdata(dev);
+
+ if (data->fanin_mask & (1 << n))
+ return a->mode;
+ return 0;
+}
+
+static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0);
+static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1);
+static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2);
+static SENSOR_DEVICE_ATTR(fan4_input, S_IRUGO, show_fan, NULL, 3);
+static SENSOR_DEVICE_ATTR(fan5_input, S_IRUGO, show_fan, NULL, 4);
+static SENSOR_DEVICE_ATTR(fan6_input, S_IRUGO, show_fan, NULL, 5);
+static SENSOR_DEVICE_ATTR(fan7_input, S_IRUGO, show_fan, NULL, 6);
+static SENSOR_DEVICE_ATTR(fan8_input, S_IRUGO, show_fan, NULL, 7);
+static SENSOR_DEVICE_ATTR(fan9_input, S_IRUGO, show_fan, NULL, 8);
+static SENSOR_DEVICE_ATTR(fan10_input, S_IRUGO, show_fan, NULL, 9);
+static SENSOR_DEVICE_ATTR(fan11_input, S_IRUGO, show_fan, NULL, 10);
+static SENSOR_DEVICE_ATTR(fan12_input, S_IRUGO, show_fan, NULL, 11);
+
+static struct attribute *nct7904_fanin_attrs[] = {
+ &sensor_dev_attr_fan1_input.dev_attr.attr,
+ &sensor_dev_attr_fan2_input.dev_attr.attr,
+ &sensor_dev_attr_fan3_input.dev_attr.attr,
+ &sensor_dev_attr_fan4_input.dev_attr.attr,
+ &sensor_dev_attr_fan5_input.dev_attr.attr,
+ &sensor_dev_attr_fan6_input.dev_attr.attr,
+ &sensor_dev_attr_fan7_input.dev_attr.attr,
+ &sensor_dev_attr_fan8_input.dev_attr.attr,
+ &sensor_dev_attr_fan9_input.dev_attr.attr,
+ &sensor_dev_attr_fan10_input.dev_attr.attr,
+ &sensor_dev_attr_fan11_input.dev_attr.attr,
+ &sensor_dev_attr_fan12_input.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group nct7904_fanin_group = {
+ .attrs = nct7904_fanin_attrs,
+ .is_visible = nct7904_fanin_is_visible,
+};
+
+/* VSEN ATTR */
+static ssize_t show_voltage(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ struct nct7904_data *data = dev_get_drvdata(dev);
+ int ret;
+ int volt;
+
+ ret = nct7904_read_reg16(data, BANK_0, VSEN1_HV_REG + index * 2);
+ if (ret < 0)
+ return ret;
+ volt = ((ret & 0xff00) >> 5) | (ret & 0x7);
+ if (index < 14)
+ volt *= 2; /* 0.002V scale */
+ else
+ volt *= 6; /* 0.006V scale */
+
+ return sprintf(buf, "%d\n", volt);
+}
+
+static ssize_t show_ltemp(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct nct7904_data *data = dev_get_drvdata(dev);
+ int ret;
+ int temp;
+
+ ret = nct7904_read_reg16(data, BANK_0, LTD_HV_REG);
+ if (ret < 0)
+ return ret;
+ temp = ((ret & 0xff00) >> 5) | (ret & 0x7);
+ temp = sign_extend32(temp, 10) * 125;
+
+ return sprintf(buf, "%d\n", temp);
+}
+
+static umode_t nct7904_vsen_is_visible(struct kobject *kobj,
+ struct attribute *a, int n)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct nct7904_data *data = dev_get_drvdata(dev);
+
+ if (data->vsen_mask & (1 << n))
+ return a->mode;
+ return 0;
+}
+
+static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_voltage, NULL, 0);
+static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_voltage, NULL, 1);
+static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, show_voltage, NULL, 2);
+static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, show_voltage, NULL, 3);
+static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, show_voltage, NULL, 4);
+static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, show_voltage, NULL, 5);
+static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, show_voltage, NULL, 6);
+static SENSOR_DEVICE_ATTR(in8_input, S_IRUGO, show_voltage, NULL, 7);
+static SENSOR_DEVICE_ATTR(in9_input, S_IRUGO, show_voltage, NULL, 8);
+static SENSOR_DEVICE_ATTR(in10_input, S_IRUGO, show_voltage, NULL, 9);
+static SENSOR_DEVICE_ATTR(in11_input, S_IRUGO, show_voltage, NULL, 10);
+static SENSOR_DEVICE_ATTR(in12_input, S_IRUGO, show_voltage, NULL, 11);
+static SENSOR_DEVICE_ATTR(in13_input, S_IRUGO, show_voltage, NULL, 12);
+static SENSOR_DEVICE_ATTR(in14_input, S_IRUGO, show_voltage, NULL, 13);
+/*
+ * Next 3 voltage sensors have specific names in the Nuvoton doc
+ * (3VDD, VBAT, 3VSB) but we use vacant numbers for them.
+ */
+static SENSOR_DEVICE_ATTR(in15_input, S_IRUGO, show_voltage, NULL, 14);
+static SENSOR_DEVICE_ATTR(in16_input, S_IRUGO, show_voltage, NULL, 15);
+static SENSOR_DEVICE_ATTR(in20_input, S_IRUGO, show_voltage, NULL, 16);
+/* This is not a voltage, but a local temperature sensor. */
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_ltemp, NULL, 0);
+static SENSOR_DEVICE_ATTR(in17_input, S_IRUGO, show_voltage, NULL, 18);
+static SENSOR_DEVICE_ATTR(in18_input, S_IRUGO, show_voltage, NULL, 19);
+static SENSOR_DEVICE_ATTR(in19_input, S_IRUGO, show_voltage, NULL, 20);
+
+static struct attribute *nct7904_vsen_attrs[] = {
+ &sensor_dev_attr_in1_input.dev_attr.attr,
+ &sensor_dev_attr_in2_input.dev_attr.attr,
+ &sensor_dev_attr_in3_input.dev_attr.attr,
+ &sensor_dev_attr_in4_input.dev_attr.attr,
+ &sensor_dev_attr_in5_input.dev_attr.attr,
+ &sensor_dev_attr_in6_input.dev_attr.attr,
+ &sensor_dev_attr_in7_input.dev_attr.attr,
+ &sensor_dev_attr_in8_input.dev_attr.attr,
+ &sensor_dev_attr_in9_input.dev_attr.attr,
+ &sensor_dev_attr_in10_input.dev_attr.attr,
+ &sensor_dev_attr_in11_input.dev_attr.attr,
+ &sensor_dev_attr_in12_input.dev_attr.attr,
+ &sensor_dev_attr_in13_input.dev_attr.attr,
+ &sensor_dev_attr_in14_input.dev_attr.attr,
+ &sensor_dev_attr_in15_input.dev_attr.attr,
+ &sensor_dev_attr_in16_input.dev_attr.attr,
+ &sensor_dev_attr_in20_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_in17_input.dev_attr.attr,
+ &sensor_dev_attr_in18_input.dev_attr.attr,
+ &sensor_dev_attr_in19_input.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group nct7904_vsen_group = {
+ .attrs = nct7904_vsen_attrs,
+ .is_visible = nct7904_vsen_is_visible,
+};
+
+/* CPU_TEMP ATTR */
+static ssize_t show_tcpu(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ struct nct7904_data *data = dev_get_drvdata(dev);
+ int ret;
+ int temp;
+
+ ret = nct7904_read_reg16(data, BANK_0, T_CPU1_HV_REG + index * 2);
+ if (ret < 0)
+ return ret;
+
+ temp = ((ret & 0xff00) >> 5) | (ret & 0x7);
+ temp = sign_extend32(temp, 10) * 125;
+ return sprintf(buf, "%d\n", temp);
+}
+
+static umode_t nct7904_tcpu_is_visible(struct kobject *kobj,
+ struct attribute *a, int n)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct nct7904_data *data = dev_get_drvdata(dev);
+
+ if (data->tcpu_mask & (1 << n))
+ return a->mode;
+ return 0;
+}
+
+/* "temp1_input" reserved for local temp */
+static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_tcpu, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_tcpu, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_tcpu, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO, show_tcpu, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp6_input, S_IRUGO, show_tcpu, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp7_input, S_IRUGO, show_tcpu, NULL, 5);
+static SENSOR_DEVICE_ATTR(temp8_input, S_IRUGO, show_tcpu, NULL, 6);
+static SENSOR_DEVICE_ATTR(temp9_input, S_IRUGO, show_tcpu, NULL, 7);
+
+static struct attribute *nct7904_tcpu_attrs[] = {
+ &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_temp6_input.dev_attr.attr,
+ &sensor_dev_attr_temp7_input.dev_attr.attr,
+ &sensor_dev_attr_temp8_input.dev_attr.attr,
+ &sensor_dev_attr_temp9_input.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group nct7904_tcpu_group = {
+ .attrs = nct7904_tcpu_attrs,
+ .is_visible = nct7904_tcpu_is_visible,
+};
+
+/* PWM ATTR */
+static ssize_t store_pwm(struct device *dev, struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ struct nct7904_data *data = dev_get_drvdata(dev);
+ unsigned long val;
+ int ret;
+
+ if (kstrtoul(buf, 10, &val) < 0)
+ return -EINVAL;
+ if (val > 255)
+ return -EINVAL;
+
+ ret = nct7904_write_reg(data, BANK_3, FANCTL1_OUT_REG + index, val);
+
+ return ret ? ret : count;
+}
+
+static ssize_t show_pwm(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ struct nct7904_data *data = dev_get_drvdata(dev);
+ int val;
+
+ val = nct7904_read_reg(data, BANK_3, FANCTL1_OUT_REG + index);
+ if (val < 0)
+ return val;
+
+ return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t store_mode(struct device *dev, struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ struct nct7904_data *data = dev_get_drvdata(dev);
+ unsigned long val;
+ int ret;
+
+ if (kstrtoul(buf, 10, &val) < 0)
+ return -EINVAL;
+ if (val > 1 || (val && !data->fan_mode[index]))
+ return -EINVAL;
+
+ ret = nct7904_write_reg(data, BANK_3, FANCTL1_FMR_REG + index,
+ val ? data->fan_mode[index] : 0);
+
+ return ret ? ret : count;
+}
+
+/* Return 0 for manual mode or 1 for SmartFan mode */
+static ssize_t show_mode(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ struct nct7904_data *data = dev_get_drvdata(dev);
+ int val;
+
+ val = nct7904_read_reg(data, BANK_3, FANCTL1_FMR_REG + index);
+ if (val < 0)
+ return val;
+
+ return sprintf(buf, "%d\n", val ? 1 : 0);
+}
+
+/* 2 attributes per channel: pwm and mode */
+static SENSOR_DEVICE_ATTR(fan1_pwm, S_IRUGO | S_IWUSR,
+ show_pwm, store_pwm, 0);
+static SENSOR_DEVICE_ATTR(fan1_mode, S_IRUGO | S_IWUSR,
+ show_mode, store_mode, 0);
+static SENSOR_DEVICE_ATTR(fan2_pwm, S_IRUGO | S_IWUSR,
+ show_pwm, store_pwm, 1);
+static SENSOR_DEVICE_ATTR(fan2_mode, S_IRUGO | S_IWUSR,
+ show_mode, store_mode, 1);
+static SENSOR_DEVICE_ATTR(fan3_pwm, S_IRUGO | S_IWUSR,
+ show_pwm, store_pwm, 2);
+static SENSOR_DEVICE_ATTR(fan3_mode, S_IRUGO | S_IWUSR,
+ show_mode, store_mode, 2);
+static SENSOR_DEVICE_ATTR(fan4_pwm, S_IRUGO | S_IWUSR,
+ show_pwm, store_pwm, 3);
+static SENSOR_DEVICE_ATTR(fan4_mode, S_IRUGO | S_IWUSR,
+ show_mode, store_mode, 3);
+
+static struct attribute *nct7904_fanctl_attrs[] = {
+ &sensor_dev_attr_fan1_pwm.dev_attr.attr,
+ &sensor_dev_attr_fan1_mode.dev_attr.attr,
+ &sensor_dev_attr_fan2_pwm.dev_attr.attr,
+ &sensor_dev_attr_fan2_mode.dev_attr.attr,
+ &sensor_dev_attr_fan3_pwm.dev_attr.attr,
+ &sensor_dev_attr_fan3_mode.dev_attr.attr,
+ &sensor_dev_attr_fan4_pwm.dev_attr.attr,
+ &sensor_dev_attr_fan4_mode.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group nct7904_fanctl_group = {
+ .attrs = nct7904_fanctl_attrs,
+};
+
+static const struct attribute_group *nct7904_groups[] = {
+ &nct7904_fanin_group,
+ &nct7904_vsen_group,
+ &nct7904_tcpu_group,
+ &nct7904_fanctl_group,
+ NULL
+};
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int nct7904_detect(struct i2c_client *client,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+
+ if (!i2c_check_functionality(adapter,
+ I2C_FUNC_SMBUS_READ_BYTE |
+ I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
+ return -ENODEV;
+
+ /* Determine the chip type. */
+ if (i2c_smbus_read_byte_data(client, VENDOR_ID_REG) != NUVOTON_ID ||
+ i2c_smbus_read_byte_data(client, CHIP_ID_REG) != NCT7904_ID ||
+ (i2c_smbus_read_byte_data(client, DEVICE_ID_REG) & 0xf0) != 0x50 ||
+ (i2c_smbus_read_byte_data(client, BANK_SEL_REG) & 0xf8) != 0x00)
+ return -ENODEV;
+
+ strlcpy(info->type, "nct7904", I2C_NAME_SIZE);
+
+ return 0;
+}
+
+static int nct7904_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct nct7904_data *data;
+ struct device *hwmon_dev;
+ struct device *dev = &client->dev;
+ int ret, i;
+ u32 mask;
+
+ data = devm_kzalloc(dev, sizeof(struct nct7904_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->client = client;
+ mutex_init(&data->bank_lock);
+ data->bank_sel = -1;
+
+ /* Setup sensor groups. */
+ /* FANIN attributes */
+ ret = nct7904_read_reg16(data, BANK_0, FANIN_CTRL0_REG);
+ if (ret < 0)
+ return ret;
+ data->fanin_mask = (ret >> 8) | ((ret & 0xff) << 8);
+
+ /*
+ * VSEN attributes
+ *
+ * Note: voltage sensors overlap with external temperature
+ * sensors. So, if we ever decide to support the latter
+ * we will have to adjust 'vsen_mask' accordingly.
+ */
+ mask = 0;
+ ret = nct7904_read_reg16(data, BANK_0, VT_ADC_CTRL0_REG);
+ if (ret >= 0)
+ mask = (ret >> 8) | ((ret & 0xff) << 8);
+ ret = nct7904_read_reg(data, BANK_0, VT_ADC_CTRL2_REG);
+ if (ret >= 0)
+ mask |= (ret << 16);
+ data->vsen_mask = mask;
+
+ /* CPU_TEMP attributes */
+ ret = nct7904_read_reg16(data, BANK_0, DTS_T_CTRL0_REG);
+ if (ret < 0)
+ return ret;
+ data->tcpu_mask = ((ret >> 8) & 0xf) | ((ret & 0xf) << 4);
+
+ for (i = 0; i < FANCTL_MAX; i++) {
+ ret = nct7904_read_reg(data, BANK_3, FANCTL1_FMR_REG + i);
+ if (ret < 0)
+ return ret;
+ data->fan_mode[i] = ret;
+ }
+
+ hwmon_dev =
+ devm_hwmon_device_register_with_groups(dev, client->name, data,
+ nct7904_groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id nct7904_id[] = {
+ {"nct7904", 0},
+ {}
+};
+
+static struct i2c_driver nct7904_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "nct7904",
+ },
+ .probe = nct7904_probe,
+ .id_table = nct7904_id,
+ .detect = nct7904_detect,
+ .address_list = normal_i2c,
+};
+
+module_i2c_driver(nct7904_driver);
+
+MODULE_AUTHOR("Vadim V. Vlasov <vvlasov@dev.rtsoft.ru>");
+MODULE_DESCRIPTION("Hwmon driver for NUVOTON NCT7904");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c
index 1991d9032c38..2d9a712699ff 100644
--- a/drivers/hwmon/pwm-fan.c
+++ b/drivers/hwmon/pwm-fan.c
@@ -24,55 +24,78 @@
#include <linux/platform_device.h>
#include <linux/pwm.h>
#include <linux/sysfs.h>
+#include <linux/thermal.h>
#define MAX_PWM 255
struct pwm_fan_ctx {
struct mutex lock;
struct pwm_device *pwm;
- unsigned char pwm_value;
+ unsigned int pwm_value;
+ unsigned int pwm_fan_state;
+ unsigned int pwm_fan_max_state;
+ unsigned int *pwm_fan_cooling_levels;
+ struct thermal_cooling_device *cdev;
};
-static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm)
{
- struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
- unsigned long pwm, duty;
- ssize_t ret;
-
- if (kstrtoul(buf, 10, &pwm) || pwm > MAX_PWM)
- return -EINVAL;
+ unsigned long duty;
+ int ret = 0;
mutex_lock(&ctx->lock);
-
if (ctx->pwm_value == pwm)
- goto exit_set_pwm_no_change;
-
- if (pwm == 0) {
- pwm_disable(ctx->pwm);
- goto exit_set_pwm;
- }
+ goto exit_set_pwm_err;
duty = DIV_ROUND_UP(pwm * (ctx->pwm->period - 1), MAX_PWM);
ret = pwm_config(ctx->pwm, duty, ctx->pwm->period);
if (ret)
goto exit_set_pwm_err;
+ if (pwm == 0)
+ pwm_disable(ctx->pwm);
+
if (ctx->pwm_value == 0) {
ret = pwm_enable(ctx->pwm);
if (ret)
goto exit_set_pwm_err;
}
-exit_set_pwm:
ctx->pwm_value = pwm;
-exit_set_pwm_no_change:
- ret = count;
exit_set_pwm_err:
mutex_unlock(&ctx->lock);
return ret;
}
+static void pwm_fan_update_state(struct pwm_fan_ctx *ctx, unsigned long pwm)
+{
+ int i;
+
+ for (i = 0; i < ctx->pwm_fan_max_state; ++i)
+ if (pwm < ctx->pwm_fan_cooling_levels[i + 1])
+ break;
+
+ ctx->pwm_fan_state = i;
+}
+
+static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
+ unsigned long pwm;
+ int ret;
+
+ if (kstrtoul(buf, 10, &pwm) || pwm > MAX_PWM)
+ return -EINVAL;
+
+ ret = __set_pwm(ctx, pwm);
+ if (ret)
+ return ret;
+
+ pwm_fan_update_state(ctx, pwm);
+ return count;
+}
+
static ssize_t show_pwm(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -91,10 +114,108 @@ static struct attribute *pwm_fan_attrs[] = {
ATTRIBUTE_GROUPS(pwm_fan);
+/* thermal cooling device callbacks */
+static int pwm_fan_get_max_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ struct pwm_fan_ctx *ctx = cdev->devdata;
+
+ if (!ctx)
+ return -EINVAL;
+
+ *state = ctx->pwm_fan_max_state;
+
+ return 0;
+}
+
+static int pwm_fan_get_cur_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ struct pwm_fan_ctx *ctx = cdev->devdata;
+
+ if (!ctx)
+ return -EINVAL;
+
+ *state = ctx->pwm_fan_state;
+
+ return 0;
+}
+
+static int
+pwm_fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
+{
+ struct pwm_fan_ctx *ctx = cdev->devdata;
+ int ret;
+
+ if (!ctx || (state > ctx->pwm_fan_max_state))
+ return -EINVAL;
+
+ if (state == ctx->pwm_fan_state)
+ return 0;
+
+ ret = __set_pwm(ctx, ctx->pwm_fan_cooling_levels[state]);
+ if (ret) {
+ dev_err(&cdev->device, "Cannot set pwm!\n");
+ return ret;
+ }
+
+ ctx->pwm_fan_state = state;
+
+ return ret;
+}
+
+static const struct thermal_cooling_device_ops pwm_fan_cooling_ops = {
+ .get_max_state = pwm_fan_get_max_state,
+ .get_cur_state = pwm_fan_get_cur_state,
+ .set_cur_state = pwm_fan_set_cur_state,
+};
+
+static int pwm_fan_of_get_cooling_data(struct device *dev,
+ struct pwm_fan_ctx *ctx)
+{
+ struct device_node *np = dev->of_node;
+ int num, i, ret;
+
+ if (!of_find_property(np, "cooling-levels", NULL))
+ return 0;
+
+ ret = of_property_count_u32_elems(np, "cooling-levels");
+ if (ret <= 0) {
+ dev_err(dev, "Wrong data!\n");
+ return ret ? : -EINVAL;
+ }
+
+ num = ret;
+ ctx->pwm_fan_cooling_levels = devm_kzalloc(dev, num * sizeof(u32),
+ GFP_KERNEL);
+ if (!ctx->pwm_fan_cooling_levels)
+ return -ENOMEM;
+
+ ret = of_property_read_u32_array(np, "cooling-levels",
+ ctx->pwm_fan_cooling_levels, num);
+ if (ret) {
+ dev_err(dev, "Property 'cooling-levels' cannot be read!\n");
+ return ret;
+ }
+
+ for (i = 0; i < num; i++) {
+ if (ctx->pwm_fan_cooling_levels[i] > MAX_PWM) {
+ dev_err(dev, "PWM fan state[%d]:%d > %d\n", i,
+ ctx->pwm_fan_cooling_levels[i], MAX_PWM);
+ return -EINVAL;
+ }
+ }
+
+ ctx->pwm_fan_max_state = num - 1;
+
+ return 0;
+}
+
static int pwm_fan_probe(struct platform_device *pdev)
{
- struct device *hwmon;
+ struct thermal_cooling_device *cdev;
struct pwm_fan_ctx *ctx;
+ struct device *hwmon;
int duty_cycle;
int ret;
@@ -136,6 +257,26 @@ static int pwm_fan_probe(struct platform_device *pdev)
pwm_disable(ctx->pwm);
return PTR_ERR(hwmon);
}
+
+ ret = pwm_fan_of_get_cooling_data(&pdev->dev, ctx);
+ if (ret)
+ return ret;
+
+ ctx->pwm_fan_state = ctx->pwm_fan_max_state;
+ if (IS_ENABLED(CONFIG_THERMAL)) {
+ cdev = thermal_of_cooling_device_register(pdev->dev.of_node,
+ "pwm-fan", ctx,
+ &pwm_fan_cooling_ops);
+ if (IS_ERR(cdev)) {
+ dev_err(&pdev->dev,
+ "Failed to register pwm-fan as cooling device");
+ pwm_disable(ctx->pwm);
+ return PTR_ERR(cdev);
+ }
+ ctx->cdev = cdev;
+ thermal_cdev_update(cdev);
+ }
+
return 0;
}
@@ -143,6 +284,7 @@ static int pwm_fan_remove(struct platform_device *pdev)
{
struct pwm_fan_ctx *ctx = platform_get_drvdata(pdev);
+ thermal_cooling_device_unregister(ctx->cdev);
if (ctx->pwm_value)
pwm_disable(ctx->pwm);
return 0;
@@ -177,7 +319,7 @@ static int pwm_fan_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(pwm_fan_pm, pwm_fan_suspend, pwm_fan_resume);
-static struct of_device_id of_pwm_fan_match[] = {
+static const struct of_device_id of_pwm_fan_match[] = {
{ .compatible = "pwm-fan", },
{},
};
diff --git a/drivers/hwmon/vexpress.c b/drivers/hwmon/vexpress.c
index cf1848b8fb32..8ba419d343f8 100644
--- a/drivers/hwmon/vexpress.c
+++ b/drivers/hwmon/vexpress.c
@@ -193,7 +193,7 @@ static struct vexpress_hwmon_type vexpress_hwmon_energy = {
},
};
-static struct of_device_id vexpress_hwmon_of_match[] = {
+static const struct of_device_id vexpress_hwmon_of_match[] = {
#if !defined(CONFIG_REGULATOR_VEXPRESS)
{
.compatible = "arm,vexpress-volt",