Commit 4b5ca741 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'hwmon-for-linus-v4.1' of...

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
  ...
parents cb0fc55d f354169e
......@@ -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 */
};
......@@ -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.
......
......@@ -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
......
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...
......@@ -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
......
......@@ -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
......
......@@ -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;
......
......@@ -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",
......
......@@ -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;
......
This diff is collapsed.
This diff is collapsed.
......@@ -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 },
......
......@@ -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);
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 @@ static int nct6775_resume(struct device *dev)
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,
};
......
This diff is collapsed.
......@@ -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", },
{},
};
......
......@@ -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",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment