hwmon updates for v4.16

New driver for W83773G
 Fan control support for PMBus drivers
 Improvements and minor fixes in several drivers
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABAgAGBQJabPhXAAoJEMsfJm/On5mBZb8P/3PXwpM7itFwnJgqjH9oy8fk
 x5NyPZgfaGZ2cWfny/yQJVcXHH25+TO9vJjwXzyN6D+IBkPKEpXH7yqJK5rk5uwQ
 UNDMkJhW6T4Wzp8yU3gsRXHe0bsFQUxhRPsexp1hITaTbk+/NXUqfNUQO575zIGG
 MKZOO6b1qXs4EVk5a7TXF6Ncb5qKtFCI/uRSP4dPI8+fQhi9gcwrSETtujGC4j2w
 qMbccnOk6B0LpYiufuO4epZh2npJ2OKL5Hkg/GzGZaqSjYwLsXVrU80rpy7jLMuP
 57dL3E91veWj71JdLx6cm2vVQQmQUd0CURzX9AzpiTGiELIhgTJrr8njhl/1mSGH
 zOHNPJJ+hZw07gzO6Ca4CCgcZy9F0g/eRoP+aC3enMegztM27AT+Tdk7GvFkT9O7
 pSGSi6oAVk0T0hdIiyzJspq4iHqMOxEfGhmUujo+lajP4dITDgAd5ZzuCO6PboEu
 eK+vXmyNICj8aWZYc+aGjYE8BL+B/vCknsXO0gYRdW91brOSbi3giZaZIrDWYnRh
 Jg5LL3j2UgQDMP08JIGJqdZwLwGTc1fwGe2i5jLC5joc3W8cVhyjbzmL8ro8/ms7
 dtcz5dvT08yyFl8LYTCk9u5wEPDY7a3xZVgM15gJUWHRGZmktfGJJU9Uz031rfzT
 sEaJ+PPqaWkNC08lRgP2
 =D+C1
 -----END PGP SIGNATURE-----

Merge tag 'hwmon-for-linus-v4.16' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging

Pull hwmon updates from Guenter Roeck:

 - New driver for W83773G

 - Fan control support for PMBus drivers

 - Improvements and minor fixes in several drivers

* tag 'hwmon-for-linus-v4.16' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (32 commits)
  hwmon: (dell-smm) Disable fan support for Dell Vostro 3360
  hwmon: (dell-smm) Disable fan support for Dell Inspiron 7720
  hwmon: (dell-smm) Enable broken functionality via "force" module param
  hwmon: (k10temp) Add temperature offset for Ryzen 1900X
  hwmon: (lm75) Fix trailing semicolon
  hwmon: (ina2xx) Fix access to uninitialized mutex
  hwmon: (pmbus/ir35221) Remove unnecessary scaling
  hwmon: (sht3x) wait predefined limits loading complete before access
  hwmon: (pmbus/ibm-cffps) Add dependency on LEDS_CLASS
  hwmon: (pmbus/cffps) Add led class device for power supply fault led
  hwmon: (pmbus) cffps: Add PMBUS_SKIP_STATUS_CHECK
  hwmon: (aspeed-pwm-tacho) Deassert reset in probe
  dt-bindings: hwmon: aspeed-pwm-tacho: Add reset node
  hwmon: (pmbus) cffps: Add debugfs entries
  hwmon: (pmbus) Export pmbus device debugfs directory entry
  hwmon: (w83773g) Fix fault detection and reporting
  hwmon: (hih6130) Fix documentation of struct hih6130
  hwmon: (iio_hwmon) Fix documentation of struct iio_hwmon_state
  hwmon: (sht15) Fix parameter documentation of sht15_crc8()
  hwmon: (sht21) Fix documentation of struct sht21
  ...
This commit is contained in:
Linus Torvalds 2018-01-29 11:20:45 -08:00
commit 47d5cc5be3
26 changed files with 1408 additions and 382 deletions

View File

@ -22,8 +22,9 @@ Required properties for pwm-tacho node:
- compatible : should be "aspeed,ast2400-pwm-tacho" for AST2400 and - compatible : should be "aspeed,ast2400-pwm-tacho" for AST2400 and
"aspeed,ast2500-pwm-tacho" for AST2500. "aspeed,ast2500-pwm-tacho" for AST2500.
- clocks : a fixed clock providing input clock frequency(PWM - clocks : phandle to clock provider with the clock number in the second cell
and Fan Tach clock)
- resets : phandle to reset controller with the reset number in the second cell
fan subnode format: fan subnode format:
=================== ===================
@ -48,19 +49,14 @@ Required properties for each child node:
Examples: Examples:
pwm_tacho_fixed_clk: fixedclk {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <24000000>;
};
pwm_tacho: pwmtachocontroller@1e786000 { pwm_tacho: pwmtachocontroller@1e786000 {
#address-cells = <1>; #address-cells = <1>;
#size-cells = <1>; #size-cells = <1>;
#cooling-cells = <2>; #cooling-cells = <2>;
reg = <0x1E786000 0x1000>; reg = <0x1E786000 0x1000>;
compatible = "aspeed,ast2500-pwm-tacho"; compatible = "aspeed,ast2500-pwm-tacho";
clocks = <&pwm_tacho_fixed_clk>; clocks = <&syscon ASPEED_CLK_APB>;
resets = <&syscon ASPEED_RESET_PWM>;
pinctrl-names = "default"; pinctrl-names = "default";
pinctrl-0 = <&pinctrl_pwm0_default &pinctrl_pwm1_default>; pinctrl-0 = <&pinctrl_pwm0_default &pinctrl_pwm1_default>;

View File

@ -8,11 +8,6 @@ Supported chips:
Datasheets: Datasheets:
http://www.ti.com/lit/gpn/lm25056 http://www.ti.com/lit/gpn/lm25056
http://www.ti.com/lit/gpn/lm25056a http://www.ti.com/lit/gpn/lm25056a
* TI LM25063
Prefix: 'lm25063'
Addresses scanned: -
Datasheet:
To be announced
* National Semiconductor LM25066 * National Semiconductor LM25066
Prefix: 'lm25066' Prefix: 'lm25066'
Addresses scanned: - Addresses scanned: -
@ -42,7 +37,7 @@ Description
----------- -----------
This driver supports hardware monitoring for National Semiconductor / TI LM25056, This driver supports hardware monitoring for National Semiconductor / TI LM25056,
LM25063, LM25066, LM5064, and LM5066/LM5066I Power Management, Monitoring, LM25066, LM5064, and LM5066/LM5066I Power Management, Monitoring,
Control, and Protection ICs. Control, and Protection ICs.
The driver is a client driver to the core PMBus driver. Please see The driver is a client driver to the core PMBus driver. Please see
@ -74,12 +69,8 @@ in1_input Measured input voltage.
in1_average Average measured input voltage. in1_average Average measured input voltage.
in1_min Minimum input voltage. in1_min Minimum input voltage.
in1_max Maximum input voltage. in1_max Maximum input voltage.
in1_crit Critical high input voltage (LM25063 only).
in1_lcrit Critical low input voltage (LM25063 only).
in1_min_alarm Input voltage low alarm. in1_min_alarm Input voltage low alarm.
in1_max_alarm Input voltage high alarm. in1_max_alarm Input voltage high alarm.
in1_lcrit_alarm Input voltage critical low alarm (LM25063 only).
in1_crit_alarm Input voltage critical high alarm. (LM25063 only).
in2_label "vmon" in2_label "vmon"
in2_input Measured voltage on VAUX pin in2_input Measured voltage on VAUX pin
@ -94,16 +85,12 @@ in3_input Measured output voltage.
in3_average Average measured output voltage. in3_average Average measured output voltage.
in3_min Minimum output voltage. in3_min Minimum output voltage.
in3_min_alarm Output voltage low alarm. in3_min_alarm Output voltage low alarm.
in3_highest Historical minimum output voltage (LM25063 only).
in3_lowest Historical maximum output voltage (LM25063 only).
curr1_label "iin" curr1_label "iin"
curr1_input Measured input current. curr1_input Measured input current.
curr1_average Average measured input current. curr1_average Average measured input current.
curr1_max Maximum input current. curr1_max Maximum input current.
curr1_crit Critical input current (LM25063 only).
curr1_max_alarm Input current high alarm. curr1_max_alarm Input current high alarm.
curr1_crit_alarm Input current critical high alarm (LM25063 only).
power1_label "pin" power1_label "pin"
power1_input Measured input power. power1_input Measured input power.
@ -113,11 +100,6 @@ power1_alarm Input power alarm
power1_input_highest Historical maximum power. power1_input_highest Historical maximum power.
power1_reset_history Write any value to reset maximum power history. power1_reset_history Write any value to reset maximum power history.
power2_label "pout". LM25063 only.
power2_input Measured output power.
power2_max Maximum output power limit.
power2_crit Critical output power limit.
temp1_input Measured temperature. temp1_input Measured temperature.
temp1_max Maximum temperature. temp1_max Maximum temperature.
temp1_crit Critical high temperature. temp1_crit Critical high temperature.

View File

@ -17,8 +17,9 @@ management with temperature and remote voltage sensing. Various fan control
features are provided, including PWM frequency control, temperature hysteresis, features are provided, including PWM frequency control, temperature hysteresis,
dual tachometer measurements, and fan health monitoring. dual tachometer measurements, and fan health monitoring.
For dual rotor fan configuration, the MAX31785 exposes the slowest rotor of the For dual-rotor configurations the MAX31785A exposes the second rotor tachometer
two in the fan[1-4]_input attributes. readings in attributes fan[5-8]_input. By contrast the MAX31785 only exposes
the slowest rotor measurement, and does so in the fan[1-4]_input attributes.
Usage Notes Usage Notes
----------- -----------
@ -31,7 +32,9 @@ Sysfs attributes
fan[1-4]_alarm Fan alarm. fan[1-4]_alarm Fan alarm.
fan[1-4]_fault Fan fault. fan[1-4]_fault Fan fault.
fan[1-4]_input Fan RPM. fan[1-8]_input Fan RPM. On the MAX31785A, inputs 5-8 correspond to the
second rotor of fans 1-4
fan[1-4]_target Fan input target
in[1-6]_crit Critical maximum output voltage in[1-6]_crit Critical maximum output voltage
in[1-6]_crit_alarm Output voltage critical high alarm in[1-6]_crit_alarm Output voltage critical high alarm
@ -44,6 +47,12 @@ in[1-6]_max_alarm Output voltage high alarm
in[1-6]_min Minimum output voltage in[1-6]_min Minimum output voltage
in[1-6]_min_alarm Output voltage low alarm in[1-6]_min_alarm Output voltage low alarm
pwm[1-4] Fan target duty cycle (0..255)
pwm[1-4]_enable 0: Full-speed
1: Manual PWM control
2: Automatic PWM (tach-feedback RPM fan-control)
3: Automatic closed-loop (temp-feedback fan-control)
temp[1-11]_crit Critical high temperature temp[1-11]_crit Critical high temperature
temp[1-11]_crit_alarm Chip temperature critical high alarm temp[1-11]_crit_alarm Chip temperature critical high alarm
temp[1-11]_input Measured temperature temp[1-11]_input Measured temperature

View File

@ -0,0 +1,33 @@
Kernel driver w83773g
====================
Supported chips:
* Nuvoton W83773G
Prefix: 'w83773g'
Addresses scanned: I2C 0x4c and 0x4d
Datasheet: https://www.nuvoton.com/resource-files/W83773G_SG_DatasheetV1_2.pdf
Authors:
Lei YU <mine260309@gmail.com>
Description
-----------
This driver implements support for Nuvoton W83773G temperature sensor
chip. This chip implements one local and two remote sensors.
The chip also features offsets for the two remote sensors which get added to
the input readings. The chip does all the scaling by itself and the driver
therefore reports true temperatures that don't need any user-space adjustments.
Temperature is measured in degrees Celsius.
The chip is wired over I2C/SMBus and specified over a temperature
range of -40 to +125 degrees Celsius (for local sensor) and -40 to +127
degrees Celsius (for remote sensors).
Resolution for both the local and remote channels is 0.125 degree C.
The chip supports only temperature measurement. The driver exports
the temperature values via the following sysfs files:
temp[1-3]_input
temp[2-3]_fault
temp[2-3]_offset
update_interval

View File

@ -26,11 +26,9 @@ if HWMON
config HWMON_VID config HWMON_VID
tristate tristate
default n
config HWMON_DEBUG_CHIP config HWMON_DEBUG_CHIP
bool "Hardware Monitoring Chip debugging messages" bool "Hardware Monitoring Chip debugging messages"
default n
help help
Say Y here if you want the I2C chip drivers to produce a bunch of Say Y here if you want the I2C chip drivers to produce a bunch of
debug messages to the system log. Select this if you are having debug messages to the system log. Select this if you are having
@ -42,7 +40,6 @@ comment "Native drivers"
config SENSORS_AB8500 config SENSORS_AB8500
tristate "AB8500 thermal monitoring" tristate "AB8500 thermal monitoring"
depends on AB8500_GPADC && AB8500_BM depends on AB8500_GPADC && AB8500_BM
default n
help help
If you say yes here you get support for the thermal sensor part If you say yes here you get support for the thermal sensor part
of the AB8500 chip. The driver includes thermal management for of the AB8500 chip. The driver includes thermal management for
@ -302,7 +299,6 @@ config SENSORS_APPLESMC
select NEW_LEDS select NEW_LEDS
select LEDS_CLASS select LEDS_CLASS
select INPUT_POLLDEV select INPUT_POLLDEV
default n
help help
This driver provides support for the Apple System Management This driver provides support for the Apple System Management
Controller, which provides an accelerometer (Apple Sudden Motion Controller, which provides an accelerometer (Apple Sudden Motion
@ -678,7 +674,6 @@ config SENSORS_JC42
config SENSORS_POWR1220 config SENSORS_POWR1220
tristate "Lattice POWR1220 Power Monitoring" tristate "Lattice POWR1220 Power Monitoring"
depends on I2C depends on I2C
default n
help help
If you say yes here you get access to the hardware monitoring If you say yes here you get access to the hardware monitoring
functions of the Lattice POWR1220 isp Power Supply Monitoring, functions of the Lattice POWR1220 isp Power Supply Monitoring,
@ -702,7 +697,6 @@ config SENSORS_LTC2945
tristate "Linear Technology LTC2945" tristate "Linear Technology LTC2945"
depends on I2C depends on I2C
select REGMAP_I2C select REGMAP_I2C
default n
help help
If you say yes here you get support for Linear Technology LTC2945 If you say yes here you get support for Linear Technology LTC2945
I2C System Monitor. I2C System Monitor.
@ -727,7 +721,6 @@ config SENSORS_LTC2990
config SENSORS_LTC4151 config SENSORS_LTC4151
tristate "Linear Technology LTC4151" tristate "Linear Technology LTC4151"
depends on I2C depends on I2C
default n
help help
If you say yes here you get support for Linear Technology LTC4151 If you say yes here you get support for Linear Technology LTC4151
High Voltage I2C Current and Voltage Monitor interface. High Voltage I2C Current and Voltage Monitor interface.
@ -738,7 +731,6 @@ config SENSORS_LTC4151
config SENSORS_LTC4215 config SENSORS_LTC4215
tristate "Linear Technology LTC4215" tristate "Linear Technology LTC4215"
depends on I2C depends on I2C
default n
help help
If you say yes here you get support for Linear Technology LTC4215 If you say yes here you get support for Linear Technology LTC4215
Hot Swap Controller I2C interface. Hot Swap Controller I2C interface.
@ -750,7 +742,6 @@ config SENSORS_LTC4222
tristate "Linear Technology LTC4222" tristate "Linear Technology LTC4222"
depends on I2C depends on I2C
select REGMAP_I2C select REGMAP_I2C
default n
help help
If you say yes here you get support for Linear Technology LTC4222 If you say yes here you get support for Linear Technology LTC4222
Dual Hot Swap Controller I2C interface. Dual Hot Swap Controller I2C interface.
@ -761,7 +752,6 @@ config SENSORS_LTC4222
config SENSORS_LTC4245 config SENSORS_LTC4245
tristate "Linear Technology LTC4245" tristate "Linear Technology LTC4245"
depends on I2C depends on I2C
default n
help help
If you say yes here you get support for Linear Technology LTC4245 If you say yes here you get support for Linear Technology LTC4245
Multiple Supply Hot Swap Controller I2C interface. Multiple Supply Hot Swap Controller I2C interface.
@ -773,7 +763,6 @@ config SENSORS_LTC4260
tristate "Linear Technology LTC4260" tristate "Linear Technology LTC4260"
depends on I2C depends on I2C
select REGMAP_I2C select REGMAP_I2C
default n
help help
If you say yes here you get support for Linear Technology LTC4260 If you say yes here you get support for Linear Technology LTC4260
Positive Voltage Hot Swap Controller I2C interface. Positive Voltage Hot Swap Controller I2C interface.
@ -784,7 +773,6 @@ config SENSORS_LTC4260
config SENSORS_LTC4261 config SENSORS_LTC4261
tristate "Linear Technology LTC4261" tristate "Linear Technology LTC4261"
depends on I2C depends on I2C
default n
help help
If you say yes here you get support for Linear Technology LTC4261 If you say yes here you get support for Linear Technology LTC4261
Negative Voltage Hot Swap Controller I2C interface. Negative Voltage Hot Swap Controller I2C interface.
@ -1276,7 +1264,6 @@ config SENSORS_NSA320
config SENSORS_PCF8591 config SENSORS_PCF8591
tristate "Philips PCF8591 ADC/DAC" tristate "Philips PCF8591 ADC/DAC"
depends on I2C depends on I2C
default n
help help
If you say yes here you get support for Philips PCF8591 4-channel If you say yes here you get support for Philips PCF8591 4-channel
ADC, 1-channel DAC chips. ADC, 1-channel DAC chips.
@ -1459,7 +1446,6 @@ config SENSORS_SMSC47B397
config SENSORS_SCH56XX_COMMON config SENSORS_SCH56XX_COMMON
tristate tristate
default n
config SENSORS_SCH5627 config SENSORS_SCH5627
tristate "SMSC SCH5627" tristate "SMSC SCH5627"
@ -1505,7 +1491,6 @@ config SENSORS_STTS751
config SENSORS_SMM665 config SENSORS_SMM665
tristate "Summit Microelectronics SMM665" tristate "Summit Microelectronics SMM665"
depends on I2C depends on I2C
default n
help help
If you say yes here you get support for the hardware monitoring If you say yes here you get support for the hardware monitoring
features of the Summit Microelectronics SMM665/SMM665B Six-Channel features of the Summit Microelectronics SMM665/SMM665B Six-Channel
@ -1725,6 +1710,16 @@ config SENSORS_VT8231
This driver can also be built as a module. If so, the module This driver can also be built as a module. If so, the module
will be called vt8231. will be called vt8231.
config SENSORS_W83773G
tristate "Nuvoton W83773G"
depends on I2C
help
If you say yes here you get support for the Nuvoton W83773G hardware
monitoring chip.
This driver can also be built as a module. If so, the module
will be called w83773g.
config SENSORS_W83781D config SENSORS_W83781D
tristate "Winbond W83781D, W83782D, W83783S, Asus AS99127F" tristate "Winbond W83781D, W83782D, W83783S, Asus AS99127F"
depends on I2C depends on I2C
@ -1782,7 +1777,6 @@ config SENSORS_W83795
config SENSORS_W83795_FANCTRL config SENSORS_W83795_FANCTRL
bool "Include automatic fan control support (DANGEROUS)" bool "Include automatic fan control support (DANGEROUS)"
depends on SENSORS_W83795 depends on SENSORS_W83795
default n
help help
If you say yes here, support for automatic fan speed control If you say yes here, support for automatic fan speed control
will be included in the driver. will be included in the driver.

View File

@ -14,6 +14,7 @@ obj-$(CONFIG_SENSORS_ATK0110) += asus_atk0110.o
# asb100, then w83781d go first, as they can override other drivers' addresses. # asb100, then w83781d go first, as they can override other drivers' addresses.
obj-$(CONFIG_SENSORS_ASB100) += asb100.o obj-$(CONFIG_SENSORS_ASB100) += asb100.o
obj-$(CONFIG_SENSORS_W83627HF) += w83627hf.o obj-$(CONFIG_SENSORS_W83627HF) += w83627hf.o
obj-$(CONFIG_SENSORS_W83773G) += w83773g.o
obj-$(CONFIG_SENSORS_W83792D) += w83792d.o obj-$(CONFIG_SENSORS_W83792D) += w83792d.o
obj-$(CONFIG_SENSORS_W83793) += w83793.o obj-$(CONFIG_SENSORS_W83793) += w83793.o
obj-$(CONFIG_SENSORS_W83795) += w83795.o obj-$(CONFIG_SENSORS_W83795) += w83795.o

View File

@ -19,6 +19,7 @@
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/sysfs.h> #include <linux/sysfs.h>
#include <linux/thermal.h> #include <linux/thermal.h>
@ -181,6 +182,7 @@ struct aspeed_cooling_device {
struct aspeed_pwm_tacho_data { struct aspeed_pwm_tacho_data {
struct regmap *regmap; struct regmap *regmap;
struct reset_control *rst;
unsigned long clk_freq; unsigned long clk_freq;
bool pwm_present[8]; bool pwm_present[8];
bool fan_tach_present[16]; bool fan_tach_present[16];
@ -905,6 +907,13 @@ static int aspeed_create_fan(struct device *dev,
return 0; return 0;
} }
static void aspeed_pwm_tacho_remove(void *data)
{
struct aspeed_pwm_tacho_data *priv = data;
reset_control_assert(priv->rst);
}
static int aspeed_pwm_tacho_probe(struct platform_device *pdev) static int aspeed_pwm_tacho_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
@ -931,6 +940,19 @@ static int aspeed_pwm_tacho_probe(struct platform_device *pdev)
&aspeed_pwm_tacho_regmap_config); &aspeed_pwm_tacho_regmap_config);
if (IS_ERR(priv->regmap)) if (IS_ERR(priv->regmap))
return PTR_ERR(priv->regmap); return PTR_ERR(priv->regmap);
priv->rst = devm_reset_control_get_exclusive(dev, NULL);
if (IS_ERR(priv->rst)) {
dev_err(dev,
"missing or invalid reset controller device tree entry");
return PTR_ERR(priv->rst);
}
reset_control_deassert(priv->rst);
ret = devm_add_action_or_reset(dev, aspeed_pwm_tacho_remove, priv);
if (ret)
return ret;
regmap_write(priv->regmap, ASPEED_PTCR_TACH_SOURCE, 0); regmap_write(priv->regmap, ASPEED_PTCR_TACH_SOURCE, 0);
regmap_write(priv->regmap, ASPEED_PTCR_TACH_SOURCE_EXT, 0); regmap_write(priv->regmap, ASPEED_PTCR_TACH_SOURCE_EXT, 0);

View File

@ -246,7 +246,8 @@ static int adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev)
int err; int err;
u32 eax, edx; u32 eax, edx;
int i; int i;
struct pci_dev *host_bridge = pci_get_bus_and_slot(0, PCI_DEVFN(0, 0)); u16 devfn = PCI_DEVFN(0, 0);
struct pci_dev *host_bridge = pci_get_domain_bus_and_slot(0, 0, devfn);
/* /*
* Explicit tjmax table entries override heuristics. * Explicit tjmax table entries override heuristics.

View File

@ -76,6 +76,7 @@ static uint i8k_fan_mult = I8K_FAN_MULT;
static uint i8k_pwm_mult; static uint i8k_pwm_mult;
static uint i8k_fan_max = I8K_FAN_HIGH; static uint i8k_fan_max = I8K_FAN_HIGH;
static bool disallow_fan_type_call; static bool disallow_fan_type_call;
static bool disallow_fan_support;
#define I8K_HWMON_HAVE_TEMP1 (1 << 0) #define I8K_HWMON_HAVE_TEMP1 (1 << 0)
#define I8K_HWMON_HAVE_TEMP2 (1 << 1) #define I8K_HWMON_HAVE_TEMP2 (1 << 1)
@ -242,6 +243,9 @@ static int i8k_get_fan_status(int fan)
{ {
struct smm_regs regs = { .eax = I8K_SMM_GET_FAN, }; struct smm_regs regs = { .eax = I8K_SMM_GET_FAN, };
if (disallow_fan_support)
return -EINVAL;
regs.ebx = fan & 0xff; regs.ebx = fan & 0xff;
return i8k_smm(&regs) ? : regs.eax & 0xff; return i8k_smm(&regs) ? : regs.eax & 0xff;
} }
@ -253,6 +257,9 @@ static int i8k_get_fan_speed(int fan)
{ {
struct smm_regs regs = { .eax = I8K_SMM_GET_SPEED, }; struct smm_regs regs = { .eax = I8K_SMM_GET_SPEED, };
if (disallow_fan_support)
return -EINVAL;
regs.ebx = fan & 0xff; regs.ebx = fan & 0xff;
return i8k_smm(&regs) ? : (regs.eax & 0xffff) * i8k_fan_mult; return i8k_smm(&regs) ? : (regs.eax & 0xffff) * i8k_fan_mult;
} }
@ -264,7 +271,7 @@ static int _i8k_get_fan_type(int fan)
{ {
struct smm_regs regs = { .eax = I8K_SMM_GET_FAN_TYPE, }; struct smm_regs regs = { .eax = I8K_SMM_GET_FAN_TYPE, };
if (disallow_fan_type_call) if (disallow_fan_support || disallow_fan_type_call)
return -EINVAL; return -EINVAL;
regs.ebx = fan & 0xff; regs.ebx = fan & 0xff;
@ -289,6 +296,9 @@ static int i8k_get_fan_nominal_speed(int fan, int speed)
{ {
struct smm_regs regs = { .eax = I8K_SMM_GET_NOM_SPEED, }; struct smm_regs regs = { .eax = I8K_SMM_GET_NOM_SPEED, };
if (disallow_fan_support)
return -EINVAL;
regs.ebx = (fan & 0xff) | (speed << 8); regs.ebx = (fan & 0xff) | (speed << 8);
return i8k_smm(&regs) ? : (regs.eax & 0xffff) * i8k_fan_mult; return i8k_smm(&regs) ? : (regs.eax & 0xffff) * i8k_fan_mult;
} }
@ -300,6 +310,9 @@ static int i8k_set_fan(int fan, int speed)
{ {
struct smm_regs regs = { .eax = I8K_SMM_SET_FAN, }; struct smm_regs regs = { .eax = I8K_SMM_SET_FAN, };
if (disallow_fan_support)
return -EINVAL;
speed = (speed < 0) ? 0 : ((speed > i8k_fan_max) ? i8k_fan_max : speed); speed = (speed < 0) ? 0 : ((speed > i8k_fan_max) ? i8k_fan_max : speed);
regs.ebx = (fan & 0xff) | (speed << 8); regs.ebx = (fan & 0xff) | (speed << 8);
@ -772,6 +785,8 @@ static struct attribute *i8k_attrs[] = {
static umode_t i8k_is_visible(struct kobject *kobj, struct attribute *attr, static umode_t i8k_is_visible(struct kobject *kobj, struct attribute *attr,
int index) int index)
{ {
if (disallow_fan_support && index >= 8)
return 0;
if (disallow_fan_type_call && if (disallow_fan_type_call &&
(index == 9 || index == 12 || index == 15)) (index == 9 || index == 12 || index == 15))
return 0; return 0;
@ -1038,6 +1053,30 @@ static const struct dmi_system_id i8k_blacklist_fan_type_dmi_table[] __initconst
{ } { }
}; };
/*
* On some machines all fan related SMM functions implemented by Dell BIOS
* firmware freeze kernel for about 500ms. Until Dell fixes these problems fan
* support for affected blacklisted Dell machines stay disabled.
* See bug: https://bugzilla.kernel.org/show_bug.cgi?id=195751
*/
static struct dmi_system_id i8k_blacklist_fan_support_dmi_table[] __initdata = {
{
.ident = "Dell Inspiron 7720",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Inspiron 7720"),
},
},
{
.ident = "Dell Vostro 3360",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Vostro 3360"),
},
},
{ }
};
/* /*
* Probe for the presence of a supported laptop. * Probe for the presence of a supported laptop.
*/ */
@ -1060,8 +1099,17 @@ static int __init i8k_probe(void)
i8k_get_dmi_data(DMI_BIOS_VERSION)); i8k_get_dmi_data(DMI_BIOS_VERSION));
} }
if (dmi_check_system(i8k_blacklist_fan_type_dmi_table)) if (dmi_check_system(i8k_blacklist_fan_support_dmi_table)) {
disallow_fan_type_call = true; pr_warn("broken Dell BIOS detected, disallow fan support\n");
if (!force)
disallow_fan_support = true;
}
if (dmi_check_system(i8k_blacklist_fan_type_dmi_table)) {
pr_warn("broken Dell BIOS detected, disallow fan type call\n");
if (!force)
disallow_fan_type_call = true;
}
strlcpy(bios_version, i8k_get_dmi_data(DMI_BIOS_VERSION), strlcpy(bios_version, i8k_get_dmi_data(DMI_BIOS_VERSION),
sizeof(bios_version)); sizeof(bios_version));

View File

@ -37,7 +37,7 @@
/** /**
* struct hih6130 - HIH-6130 device specific data * struct hih6130 - HIH-6130 device specific data
* @hwmon_dev: device registered with hwmon * @client: pointer to I2C client device
* @lock: mutex to protect measurement values * @lock: mutex to protect measurement values
* @valid: only false before first measurement is taken * @valid: only false before first measurement is taken
* @last_update: time of last update (jiffies) * @last_update: time of last update (jiffies)

View File

@ -678,7 +678,7 @@ EXPORT_SYMBOL_GPL(hwmon_device_register_with_groups);
* @dev: the parent device * @dev: the parent device
* @name: hwmon name attribute * @name: hwmon name attribute
* @drvdata: driver data to attach to created device * @drvdata: driver data to attach to created device
* @info: pointer to hwmon chip information * @chip: pointer to hwmon chip information
* @extra_groups: pointer to list of additional non-standard attribute groups * @extra_groups: pointer to list of additional non-standard attribute groups
* *
* hwmon_device_unregister() must be called when the device is no * hwmon_device_unregister() must be called when the device is no
@ -785,11 +785,11 @@ EXPORT_SYMBOL_GPL(devm_hwmon_device_register_with_groups);
/** /**
* devm_hwmon_device_register_with_info - register w/ hwmon * devm_hwmon_device_register_with_info - register w/ hwmon
* @dev: the parent device * @dev: the parent device
* @name: hwmon name attribute * @name: hwmon name attribute
* @drvdata: driver data to attach to created device * @drvdata: driver data to attach to created device
* @info: Pointer to hwmon chip information * @chip: pointer to hwmon chip information
* @groups - pointer to list of driver specific attribute groups * @groups: pointer to list of driver specific attribute groups
* *
* Returns the pointer to the new device. The new device is automatically * Returns the pointer to the new device. The new device is automatically
* unregistered with the parent device. * unregistered with the parent device.

View File

@ -23,7 +23,8 @@
* @channels: filled with array of channels from iio * @channels: filled with array of channels from iio
* @num_channels: number of channels in channels (saves counting twice) * @num_channels: number of channels in channels (saves counting twice)
* @hwmon_dev: associated hwmon device * @hwmon_dev: associated hwmon device
* @attr_group: the group of attributes * @attr_group: the group of attributes
* @groups: null terminated array of attribute groups
* @attrs: null terminated array of attribute pointers. * @attrs: null terminated array of attribute pointers.
*/ */
struct iio_hwmon_state { struct iio_hwmon_state {

View File

@ -95,18 +95,20 @@ enum ina2xx_ids { ina219, ina226 };
struct ina2xx_config { struct ina2xx_config {
u16 config_default; u16 config_default;
int calibration_factor; int calibration_value;
int registers; int registers;
int shunt_div; int shunt_div;
int bus_voltage_shift; int bus_voltage_shift;
int bus_voltage_lsb; /* uV */ int bus_voltage_lsb; /* uV */
int power_lsb; /* uW */ int power_lsb_factor;
}; };
struct ina2xx_data { struct ina2xx_data {
const struct ina2xx_config *config; const struct ina2xx_config *config;
long rshunt; long rshunt;
long current_lsb_uA;
long power_lsb_uW;
struct mutex config_lock; struct mutex config_lock;
struct regmap *regmap; struct regmap *regmap;
@ -116,21 +118,21 @@ struct ina2xx_data {
static const struct ina2xx_config ina2xx_config[] = { static const struct ina2xx_config ina2xx_config[] = {
[ina219] = { [ina219] = {
.config_default = INA219_CONFIG_DEFAULT, .config_default = INA219_CONFIG_DEFAULT,
.calibration_factor = 40960000, .calibration_value = 4096,
.registers = INA219_REGISTERS, .registers = INA219_REGISTERS,
.shunt_div = 100, .shunt_div = 100,
.bus_voltage_shift = 3, .bus_voltage_shift = 3,
.bus_voltage_lsb = 4000, .bus_voltage_lsb = 4000,
.power_lsb = 20000, .power_lsb_factor = 20,
}, },
[ina226] = { [ina226] = {
.config_default = INA226_CONFIG_DEFAULT, .config_default = INA226_CONFIG_DEFAULT,
.calibration_factor = 5120000, .calibration_value = 2048,
.registers = INA226_REGISTERS, .registers = INA226_REGISTERS,
.shunt_div = 400, .shunt_div = 400,
.bus_voltage_shift = 0, .bus_voltage_shift = 0,
.bus_voltage_lsb = 1250, .bus_voltage_lsb = 1250,
.power_lsb = 25000, .power_lsb_factor = 25,
}, },
}; };
@ -169,12 +171,16 @@ static u16 ina226_interval_to_reg(int interval)
return INA226_SHIFT_AVG(avg_bits); return INA226_SHIFT_AVG(avg_bits);
} }
/*
* Calibration register is set to the best value, which eliminates
* truncation errors on calculating current register in hardware.
* According to datasheet (eq. 3) the best values are 2048 for
* ina226 and 4096 for ina219. They are hardcoded as calibration_value.
*/
static int ina2xx_calibrate(struct ina2xx_data *data) static int ina2xx_calibrate(struct ina2xx_data *data)
{ {
u16 val = DIV_ROUND_CLOSEST(data->config->calibration_factor, return regmap_write(data->regmap, INA2XX_CALIBRATION,
data->rshunt); data->config->calibration_value);
return regmap_write(data->regmap, INA2XX_CALIBRATION, val);
} }
/* /*
@ -187,10 +193,6 @@ static int ina2xx_init(struct ina2xx_data *data)
if (ret < 0) if (ret < 0)
return ret; return ret;
/*
* Set current LSB to 1mA, shunt is in uOhms
* (equation 13 in datasheet).
*/
return ina2xx_calibrate(data); return ina2xx_calibrate(data);
} }
@ -268,15 +270,15 @@ static int ina2xx_get_value(struct ina2xx_data *data, u8 reg,
val = DIV_ROUND_CLOSEST(val, 1000); val = DIV_ROUND_CLOSEST(val, 1000);
break; break;
case INA2XX_POWER: case INA2XX_POWER:
val = regval * data->config->power_lsb; val = regval * data->power_lsb_uW;
break; break;
case INA2XX_CURRENT: case INA2XX_CURRENT:
/* signed register, LSB=1mA (selected), in mA */ /* signed register, result in mA */
val = (s16)regval; val = regval * data->current_lsb_uA;
val = DIV_ROUND_CLOSEST(val, 1000);
break; break;
case INA2XX_CALIBRATION: case INA2XX_CALIBRATION:
val = DIV_ROUND_CLOSEST(data->config->calibration_factor, val = regval;
regval);
break; break;
default: default:
/* programmer goofed */ /* programmer goofed */
@ -304,9 +306,32 @@ static ssize_t ina2xx_show_value(struct device *dev,
ina2xx_get_value(data, attr->index, regval)); ina2xx_get_value(data, attr->index, regval));
} }
static ssize_t ina2xx_set_shunt(struct device *dev, /*
struct device_attribute *da, * In order to keep calibration register value fixed, the product
const char *buf, size_t count) * of current_lsb and shunt_resistor should also be fixed and equal
* to shunt_voltage_lsb = 1 / shunt_div multiplied by 10^9 in order
* to keep the scale.
*/
static int ina2xx_set_shunt(struct ina2xx_data *data, long val)
{
unsigned int dividend = DIV_ROUND_CLOSEST(1000000000,
data->config->shunt_div);
if (val <= 0 || val > dividend)
return -EINVAL;
mutex_lock(&data->config_lock);
data->rshunt = val;
data->current_lsb_uA = DIV_ROUND_CLOSEST(dividend, val);
data->power_lsb_uW = data->config->power_lsb_factor *
data->current_lsb_uA;
mutex_unlock(&data->config_lock);
return 0;
}
static ssize_t ina2xx_store_shunt(struct device *dev,
struct device_attribute *da,
const char *buf, size_t count)
{ {
unsigned long val; unsigned long val;
int status; int status;
@ -316,18 +341,9 @@ static ssize_t ina2xx_set_shunt(struct device *dev,
if (status < 0) if (status < 0)
return status; return status;
if (val == 0 || status = ina2xx_set_shunt(data, val);
/* Values greater than the calibration factor make no sense. */
val > data->config->calibration_factor)
return -EINVAL;
mutex_lock(&data->config_lock);
data->rshunt = val;
status = ina2xx_calibrate(data);
mutex_unlock(&data->config_lock);
if (status < 0) if (status < 0)
return status; return status;
return count; return count;
} }
@ -387,7 +403,7 @@ static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, ina2xx_show_value, NULL,
/* shunt resistance */ /* shunt resistance */
static SENSOR_DEVICE_ATTR(shunt_resistor, S_IRUGO | S_IWUSR, static SENSOR_DEVICE_ATTR(shunt_resistor, S_IRUGO | S_IWUSR,
ina2xx_show_value, ina2xx_set_shunt, ina2xx_show_value, ina2xx_store_shunt,
INA2XX_CALIBRATION); INA2XX_CALIBRATION);
/* update interval (ina226 only) */ /* update interval (ina226 only) */
@ -438,6 +454,7 @@ static int ina2xx_probe(struct i2c_client *client,
/* set the device type */ /* set the device type */
data->config = &ina2xx_config[chip]; data->config = &ina2xx_config[chip];
mutex_init(&data->config_lock);
if (of_property_read_u32(dev->of_node, "shunt-resistor", &val) < 0) { if (of_property_read_u32(dev->of_node, "shunt-resistor", &val) < 0) {
struct ina2xx_platform_data *pdata = dev_get_platdata(dev); struct ina2xx_platform_data *pdata = dev_get_platdata(dev);
@ -448,10 +465,7 @@ static int ina2xx_probe(struct i2c_client *client,
val = INA2XX_RSHUNT_DEFAULT; val = INA2XX_RSHUNT_DEFAULT;
} }
if (val <= 0 || val > data->config->calibration_factor) ina2xx_set_shunt(data, val);
return -ENODEV;
data->rshunt = val;
ina2xx_regmap_config.max_register = data->config->registers; ina2xx_regmap_config.max_register = data->config->registers;
@ -467,8 +481,6 @@ static int ina2xx_probe(struct i2c_client *client,
return -ENODEV; return -ENODEV;
} }
mutex_init(&data->config_lock);
data->groups[group++] = &ina2xx_group; data->groups[group++] = &ina2xx_group;
if (id->driver_data == ina226) if (id->driver_data == ina226)
data->groups[group++] = &ina226_group; data->groups[group++] = &ina226_group;

View File

@ -86,6 +86,7 @@ static const struct tctl_offset tctl_offset_table[] = {
{ 0x17, "AMD Ryzen 7 1800X", 20000 }, { 0x17, "AMD Ryzen 7 1800X", 20000 },
{ 0x17, "AMD Ryzen Threadripper 1950X", 27000 }, { 0x17, "AMD Ryzen Threadripper 1950X", 27000 },
{ 0x17, "AMD Ryzen Threadripper 1920X", 27000 }, { 0x17, "AMD Ryzen Threadripper 1920X", 27000 },
{ 0x17, "AMD Ryzen Threadripper 1900X", 27000 },
{ 0x17, "AMD Ryzen Threadripper 1950", 10000 }, { 0x17, "AMD Ryzen Threadripper 1950", 10000 },
{ 0x17, "AMD Ryzen Threadripper 1920", 10000 }, { 0x17, "AMD Ryzen Threadripper 1920", 10000 },
{ 0x17, "AMD Ryzen Threadripper 1910", 10000 }, { 0x17, "AMD Ryzen Threadripper 1910", 10000 },

View File

@ -100,7 +100,7 @@ static int lm75_read(struct device *dev, enum hwmon_sensor_types type,
switch (attr) { switch (attr) {
case hwmon_chip_update_interval: case hwmon_chip_update_interval:
*val = data->sample_time; *val = data->sample_time;
break;; break;
default: default:
return -EINVAL; return -EINVAL;
} }

View File

@ -39,6 +39,7 @@ config SENSORS_ADM1275
config SENSORS_IBM_CFFPS config SENSORS_IBM_CFFPS
tristate "IBM Common Form Factor Power Supply" tristate "IBM Common Form Factor Power Supply"
depends on LEDS_CLASS
help help
If you say yes here you get hardware monitoring support for the IBM If you say yes here you get hardware monitoring support for the IBM
Common Form Factor power supply. Common Form Factor power supply.

View File

@ -8,12 +8,29 @@
*/ */
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/debugfs.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/fs.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/jiffies.h>
#include <linux/leds.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/mutex.h>
#include <linux/pmbus.h>
#include "pmbus.h" #include "pmbus.h"
#define CFFPS_FRU_CMD 0x9A
#define CFFPS_PN_CMD 0x9B
#define CFFPS_SN_CMD 0x9E
#define CFFPS_CCIN_CMD 0xBD
#define CFFPS_FW_CMD_START 0xFA
#define CFFPS_FW_NUM_BYTES 4
#define CFFPS_SYS_CONFIG_CMD 0xDA
#define CFFPS_INPUT_HISTORY_CMD 0xD6
#define CFFPS_INPUT_HISTORY_SIZE 100
/* STATUS_MFR_SPECIFIC bits */ /* STATUS_MFR_SPECIFIC bits */
#define CFFPS_MFR_FAN_FAULT BIT(0) #define CFFPS_MFR_FAN_FAULT BIT(0)
#define CFFPS_MFR_THERMAL_FAULT BIT(1) #define CFFPS_MFR_THERMAL_FAULT BIT(1)
@ -24,6 +41,153 @@
#define CFFPS_MFR_VAUX_FAULT BIT(6) #define CFFPS_MFR_VAUX_FAULT BIT(6)
#define CFFPS_MFR_CURRENT_SHARE_WARNING BIT(7) #define CFFPS_MFR_CURRENT_SHARE_WARNING BIT(7)
#define CFFPS_LED_BLINK BIT(0)
#define CFFPS_LED_ON BIT(1)
#define CFFPS_LED_OFF BIT(2)
#define CFFPS_BLINK_RATE_MS 250
enum {
CFFPS_DEBUGFS_INPUT_HISTORY = 0,
CFFPS_DEBUGFS_FRU,
CFFPS_DEBUGFS_PN,
CFFPS_DEBUGFS_SN,
CFFPS_DEBUGFS_CCIN,
CFFPS_DEBUGFS_FW,
CFFPS_DEBUGFS_NUM_ENTRIES
};
struct ibm_cffps_input_history {
struct mutex update_lock;
unsigned long last_update;
u8 byte_count;
u8 data[CFFPS_INPUT_HISTORY_SIZE];
};
struct ibm_cffps {
struct i2c_client *client;
struct ibm_cffps_input_history input_history;
int debugfs_entries[CFFPS_DEBUGFS_NUM_ENTRIES];
char led_name[32];
u8 led_state;
struct led_classdev led;
};
#define to_psu(x, y) container_of((x), struct ibm_cffps, debugfs_entries[(y)])
static ssize_t ibm_cffps_read_input_history(struct ibm_cffps *psu,
char __user *buf, size_t count,
loff_t *ppos)
{
int rc;
u8 msgbuf0[1] = { CFFPS_INPUT_HISTORY_CMD };
u8 msgbuf1[CFFPS_INPUT_HISTORY_SIZE + 1] = { 0 };
struct i2c_msg msg[2] = {
{
.addr = psu->client->addr,
.flags = psu->client->flags,
.len = 1,
.buf = msgbuf0,
}, {
.addr = psu->client->addr,
.flags = psu->client->flags | I2C_M_RD,
.len = CFFPS_INPUT_HISTORY_SIZE + 1,
.buf = msgbuf1,
},
};
if (!*ppos) {
mutex_lock(&psu->input_history.update_lock);
if (time_after(jiffies, psu->input_history.last_update + HZ)) {
/*
* Use a raw i2c transfer, since we need more bytes
* than Linux I2C supports through smbus xfr (only 32).
*/
rc = i2c_transfer(psu->client->adapter, msg, 2);
if (rc < 0) {
mutex_unlock(&psu->input_history.update_lock);
return rc;
}
psu->input_history.byte_count = msgbuf1[0];
memcpy(psu->input_history.data, &msgbuf1[1],
CFFPS_INPUT_HISTORY_SIZE);
psu->input_history.last_update = jiffies;
}
mutex_unlock(&psu->input_history.update_lock);
}
return simple_read_from_buffer(buf, count, ppos,
psu->input_history.data,
psu->input_history.byte_count);
}
static ssize_t ibm_cffps_debugfs_op(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
u8 cmd;
int i, rc;
int *idxp = file->private_data;
int idx = *idxp;
struct ibm_cffps *psu = to_psu(idxp, idx);
char data[I2C_SMBUS_BLOCK_MAX] = { 0 };
switch (idx) {
case CFFPS_DEBUGFS_INPUT_HISTORY:
return ibm_cffps_read_input_history(psu, buf, count, ppos);
case CFFPS_DEBUGFS_FRU:
cmd = CFFPS_FRU_CMD;
break;
case CFFPS_DEBUGFS_PN:
cmd = CFFPS_PN_CMD;
break;
case CFFPS_DEBUGFS_SN:
cmd = CFFPS_SN_CMD;
break;
case CFFPS_DEBUGFS_CCIN:
rc = i2c_smbus_read_word_swapped(psu->client, CFFPS_CCIN_CMD);
if (rc < 0)
return rc;
rc = snprintf(data, 5, "%04X", rc);
goto done;
case CFFPS_DEBUGFS_FW:
for (i = 0; i < CFFPS_FW_NUM_BYTES; ++i) {
rc = i2c_smbus_read_byte_data(psu->client,
CFFPS_FW_CMD_START + i);
if (rc < 0)
return rc;
snprintf(&data[i * 2], 3, "%02X", rc);
}
rc = i * 2;
goto done;
default:
return -EINVAL;
}
rc = i2c_smbus_read_block_data(psu->client, cmd, data);
if (rc < 0)
return rc;
done:
data[rc] = '\n';
rc += 2;
return simple_read_from_buffer(buf, count, ppos, data, rc);
}
static const struct file_operations ibm_cffps_fops = {
.llseek = noop_llseek,
.read = ibm_cffps_debugfs_op,
.open = simple_open,
};
static int ibm_cffps_read_byte_data(struct i2c_client *client, int page, static int ibm_cffps_read_byte_data(struct i2c_client *client, int page,
int reg) int reg)
{ {
@ -105,6 +269,69 @@ static int ibm_cffps_read_word_data(struct i2c_client *client, int page,
return rc; return rc;
} }
static void ibm_cffps_led_brightness_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
int rc;
struct ibm_cffps *psu = container_of(led_cdev, struct ibm_cffps, led);
if (brightness == LED_OFF) {
psu->led_state = CFFPS_LED_OFF;
} else {
brightness = LED_FULL;
if (psu->led_state != CFFPS_LED_BLINK)
psu->led_state = CFFPS_LED_ON;
}
rc = i2c_smbus_write_byte_data(psu->client, CFFPS_SYS_CONFIG_CMD,
psu->led_state);
if (rc < 0)
return;
led_cdev->brightness = brightness;
}
static int ibm_cffps_led_blink_set(struct led_classdev *led_cdev,
unsigned long *delay_on,
unsigned long *delay_off)
{
int rc;
struct ibm_cffps *psu = container_of(led_cdev, struct ibm_cffps, led);
psu->led_state = CFFPS_LED_BLINK;
if (led_cdev->brightness == LED_OFF)
return 0;
rc = i2c_smbus_write_byte_data(psu->client, CFFPS_SYS_CONFIG_CMD,
CFFPS_LED_BLINK);
if (rc < 0)
return rc;
*delay_on = CFFPS_BLINK_RATE_MS;
*delay_off = CFFPS_BLINK_RATE_MS;
return 0;
}
static void ibm_cffps_create_led_class(struct ibm_cffps *psu)
{
int rc;
struct i2c_client *client = psu->client;
struct device *dev = &client->dev;
snprintf(psu->led_name, sizeof(psu->led_name), "%s-%02x", client->name,
client->addr);
psu->led.name = psu->led_name;
psu->led.max_brightness = LED_FULL;
psu->led.brightness_set = ibm_cffps_led_brightness_set;
psu->led.blink_set = ibm_cffps_led_blink_set;
rc = devm_led_classdev_register(dev, &psu->led);
if (rc)
dev_warn(dev, "failed to register led class: %d\n", rc);
}
static struct pmbus_driver_info ibm_cffps_info = { static struct pmbus_driver_info ibm_cffps_info = {
.pages = 1, .pages = 1,
.func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
@ -116,10 +343,69 @@ static struct pmbus_driver_info ibm_cffps_info = {
.read_word_data = ibm_cffps_read_word_data, .read_word_data = ibm_cffps_read_word_data,
}; };
static struct pmbus_platform_data ibm_cffps_pdata = {
.flags = PMBUS_SKIP_STATUS_CHECK,
};
static int ibm_cffps_probe(struct i2c_client *client, static int ibm_cffps_probe(struct i2c_client *client,
const struct i2c_device_id *id) const struct i2c_device_id *id)
{ {
return pmbus_do_probe(client, id, &ibm_cffps_info); int i, rc;
struct dentry *debugfs;
struct dentry *ibm_cffps_dir;
struct ibm_cffps *psu;
client->dev.platform_data = &ibm_cffps_pdata;
rc = pmbus_do_probe(client, id, &ibm_cffps_info);
if (rc)
return rc;
/*
* Don't fail the probe if there isn't enough memory for leds and
* debugfs.
*/
psu = devm_kzalloc(&client->dev, sizeof(*psu), GFP_KERNEL);
if (!psu)
return 0;
psu->client = client;
mutex_init(&psu->input_history.update_lock);
psu->input_history.last_update = jiffies - HZ;
ibm_cffps_create_led_class(psu);
/* Don't fail the probe if we can't create debugfs */
debugfs = pmbus_get_debugfs_dir(client);
if (!debugfs)
return 0;
ibm_cffps_dir = debugfs_create_dir(client->name, debugfs);
if (!ibm_cffps_dir)
return 0;
for (i = 0; i < CFFPS_DEBUGFS_NUM_ENTRIES; ++i)
psu->debugfs_entries[i] = i;
debugfs_create_file("input_history", 0444, ibm_cffps_dir,
&psu->debugfs_entries[CFFPS_DEBUGFS_INPUT_HISTORY],
&ibm_cffps_fops);
debugfs_create_file("fru", 0444, ibm_cffps_dir,
&psu->debugfs_entries[CFFPS_DEBUGFS_FRU],
&ibm_cffps_fops);
debugfs_create_file("part_number", 0444, ibm_cffps_dir,
&psu->debugfs_entries[CFFPS_DEBUGFS_PN],
&ibm_cffps_fops);
debugfs_create_file("serial_number", 0444, ibm_cffps_dir,
&psu->debugfs_entries[CFFPS_DEBUGFS_SN],
&ibm_cffps_fops);
debugfs_create_file("ccin", 0444, ibm_cffps_dir,
&psu->debugfs_entries[CFFPS_DEBUGFS_CCIN],
&ibm_cffps_fops);
debugfs_create_file("fw_version", 0444, ibm_cffps_dir,
&psu->debugfs_entries[CFFPS_DEBUGFS_FW],
&ibm_cffps_fops);
return 0;
} }
static const struct i2c_device_id ibm_cffps_id[] = { static const struct i2c_device_id ibm_cffps_id[] = {

View File

@ -25,168 +25,19 @@
#define IR35221_MFR_IOUT_VALLEY 0xcb #define IR35221_MFR_IOUT_VALLEY 0xcb
#define IR35221_MFR_TEMP_VALLEY 0xcc #define IR35221_MFR_TEMP_VALLEY 0xcc
static long ir35221_reg2data(int data, enum pmbus_sensor_classes class)
{
s16 exponent;
s32 mantissa;
long val;
/* We only modify LINEAR11 formats */
exponent = ((s16)data) >> 11;
mantissa = ((s16)((data & 0x7ff) << 5)) >> 5;
val = mantissa * 1000L;
/* scale result to micro-units for power sensors */
if (class == PSC_POWER)
val = val * 1000L;
if (exponent >= 0)
val <<= exponent;
else
val >>= -exponent;
return val;
}
#define MAX_MANTISSA (1023 * 1000)
#define MIN_MANTISSA (511 * 1000)
static u16 ir35221_data2reg(long val, enum pmbus_sensor_classes class)
{
s16 exponent = 0, mantissa;
bool negative = false;
if (val == 0)
return 0;
if (val < 0) {
negative = true;
val = -val;
}
/* Power is in uW. Convert to mW before converting. */
if (class == PSC_POWER)
val = DIV_ROUND_CLOSEST(val, 1000L);
/* Reduce large mantissa until it fits into 10 bit */
while (val >= MAX_MANTISSA && exponent < 15) {
exponent++;
val >>= 1;
}
/* Increase small mantissa to improve precision */
while (val < MIN_MANTISSA && exponent > -15) {
exponent--;
val <<= 1;
}
/* Convert mantissa from milli-units to units */
mantissa = DIV_ROUND_CLOSEST(val, 1000);
/* Ensure that resulting number is within range */
if (mantissa > 0x3ff)
mantissa = 0x3ff;
/* restore sign */
if (negative)
mantissa = -mantissa;
/* Convert to 5 bit exponent, 11 bit mantissa */
return (mantissa & 0x7ff) | ((exponent << 11) & 0xf800);
}
static u16 ir35221_scale_result(s16 data, int shift,
enum pmbus_sensor_classes class)
{
long val;
val = ir35221_reg2data(data, class);
if (shift < 0)
val >>= -shift;
else
val <<= shift;
return ir35221_data2reg(val, class);
}
static int ir35221_read_word_data(struct i2c_client *client, int page, int reg) static int ir35221_read_word_data(struct i2c_client *client, int page, int reg)
{ {
int ret; int ret;
switch (reg) { switch (reg) {
case PMBUS_IOUT_OC_FAULT_LIMIT:
case PMBUS_IOUT_OC_WARN_LIMIT:
ret = pmbus_read_word_data(client, page, reg);
if (ret < 0)
break;
ret = ir35221_scale_result(ret, 1, PSC_CURRENT_OUT);
break;
case PMBUS_VIN_OV_FAULT_LIMIT:
case PMBUS_VIN_OV_WARN_LIMIT:
case PMBUS_VIN_UV_WARN_LIMIT:
ret = pmbus_read_word_data(client, page, reg);
ret = ir35221_scale_result(ret, -4, PSC_VOLTAGE_IN);
break;
case PMBUS_IIN_OC_WARN_LIMIT:
ret = pmbus_read_word_data(client, page, reg);
if (ret < 0)
break;
ret = ir35221_scale_result(ret, -1, PSC_CURRENT_IN);
break;
case PMBUS_READ_VIN:
ret = pmbus_read_word_data(client, page, PMBUS_READ_VIN);
if (ret < 0)
break;
ret = ir35221_scale_result(ret, -5, PSC_VOLTAGE_IN);
break;
case PMBUS_READ_IIN:
ret = pmbus_read_word_data(client, page, PMBUS_READ_IIN);
if (ret < 0)
break;
if (page == 0)
ret = ir35221_scale_result(ret, -4, PSC_CURRENT_IN);
else
ret = ir35221_scale_result(ret, -5, PSC_CURRENT_IN);
break;
case PMBUS_READ_POUT:
ret = pmbus_read_word_data(client, page, PMBUS_READ_POUT);
if (ret < 0)
break;
ret = ir35221_scale_result(ret, -1, PSC_POWER);
break;
case PMBUS_READ_PIN:
ret = pmbus_read_word_data(client, page, PMBUS_READ_PIN);
if (ret < 0)
break;
ret = ir35221_scale_result(ret, -1, PSC_POWER);
break;
case PMBUS_READ_IOUT:
ret = pmbus_read_word_data(client, page, PMBUS_READ_IOUT);
if (ret < 0)
break;
if (page == 0)
ret = ir35221_scale_result(ret, -1, PSC_CURRENT_OUT);
else
ret = ir35221_scale_result(ret, -2, PSC_CURRENT_OUT);
break;
case PMBUS_VIRT_READ_VIN_MAX: case PMBUS_VIRT_READ_VIN_MAX:
ret = pmbus_read_word_data(client, page, IR35221_MFR_VIN_PEAK); ret = pmbus_read_word_data(client, page, IR35221_MFR_VIN_PEAK);
if (ret < 0)
break;
ret = ir35221_scale_result(ret, -5, PSC_VOLTAGE_IN);
break; break;
case PMBUS_VIRT_READ_VOUT_MAX: case PMBUS_VIRT_READ_VOUT_MAX:
ret = pmbus_read_word_data(client, page, IR35221_MFR_VOUT_PEAK); ret = pmbus_read_word_data(client, page, IR35221_MFR_VOUT_PEAK);
break; break;
case PMBUS_VIRT_READ_IOUT_MAX: case PMBUS_VIRT_READ_IOUT_MAX:
ret = pmbus_read_word_data(client, page, IR35221_MFR_IOUT_PEAK); ret = pmbus_read_word_data(client, page, IR35221_MFR_IOUT_PEAK);
if (ret < 0)
break;
if (page == 0)
ret = ir35221_scale_result(ret, -1, PSC_CURRENT_IN);
else
ret = ir35221_scale_result(ret, -2, PSC_CURRENT_IN);
break; break;
case PMBUS_VIRT_READ_TEMP_MAX: case PMBUS_VIRT_READ_TEMP_MAX:
ret = pmbus_read_word_data(client, page, IR35221_MFR_TEMP_PEAK); ret = pmbus_read_word_data(client, page, IR35221_MFR_TEMP_PEAK);
@ -194,9 +45,6 @@ static int ir35221_read_word_data(struct i2c_client *client, int page, int reg)
case PMBUS_VIRT_READ_VIN_MIN: case PMBUS_VIRT_READ_VIN_MIN:
ret = pmbus_read_word_data(client, page, ret = pmbus_read_word_data(client, page,
IR35221_MFR_VIN_VALLEY); IR35221_MFR_VIN_VALLEY);
if (ret < 0)
break;
ret = ir35221_scale_result(ret, -5, PSC_VOLTAGE_IN);
break; break;
case PMBUS_VIRT_READ_VOUT_MIN: case PMBUS_VIRT_READ_VOUT_MIN:
ret = pmbus_read_word_data(client, page, ret = pmbus_read_word_data(client, page,
@ -205,12 +53,6 @@ static int ir35221_read_word_data(struct i2c_client *client, int page, int reg)
case PMBUS_VIRT_READ_IOUT_MIN: case PMBUS_VIRT_READ_IOUT_MIN:
ret = pmbus_read_word_data(client, page, ret = pmbus_read_word_data(client, page,
IR35221_MFR_IOUT_VALLEY); IR35221_MFR_IOUT_VALLEY);
if (ret < 0)
break;
if (page == 0)
ret = ir35221_scale_result(ret, -1, PSC_CURRENT_IN);
else
ret = ir35221_scale_result(ret, -2, PSC_CURRENT_IN);
break; break;
case PMBUS_VIRT_READ_TEMP_MIN: case PMBUS_VIRT_READ_TEMP_MIN:
ret = pmbus_read_word_data(client, page, ret = pmbus_read_word_data(client, page,
@ -224,36 +66,6 @@ static int ir35221_read_word_data(struct i2c_client *client, int page, int reg)
return ret; return ret;
} }
static int ir35221_write_word_data(struct i2c_client *client, int page, int reg,
u16 word)
{
int ret;
u16 val;
switch (reg) {
case PMBUS_IOUT_OC_FAULT_LIMIT:
case PMBUS_IOUT_OC_WARN_LIMIT:
val = ir35221_scale_result(word, -1, PSC_CURRENT_OUT);
ret = pmbus_write_word_data(client, page, reg, val);
break;
case PMBUS_VIN_OV_FAULT_LIMIT:
case PMBUS_VIN_OV_WARN_LIMIT:
case PMBUS_VIN_UV_WARN_LIMIT:
val = ir35221_scale_result(word, 4, PSC_VOLTAGE_IN);
ret = pmbus_write_word_data(client, page, reg, val);
break;
case PMBUS_IIN_OC_WARN_LIMIT:
val = ir35221_scale_result(word, 1, PSC_CURRENT_IN);
ret = pmbus_write_word_data(client, page, reg, val);
break;
default:
ret = -ENODATA;
break;
}
return ret;
}
static int ir35221_probe(struct i2c_client *client, static int ir35221_probe(struct i2c_client *client,
const struct i2c_device_id *id) const struct i2c_device_id *id)
{ {
@ -292,7 +104,6 @@ static int ir35221_probe(struct i2c_client *client,
if (!info) if (!info)
return -ENOMEM; return -ENOMEM;
info->write_word_data = ir35221_write_word_data;
info->read_word_data = ir35221_read_word_data; info->read_word_data = ir35221_read_word_data;
info->pages = 2; info->pages = 2;

View File

@ -1,5 +1,5 @@
/* /*
* Hardware monitoring driver for LM25056 / LM25063 / LM25066 / LM5064 / LM5066 * Hardware monitoring driver for LM25056 / LM25066 / LM5064 / LM5066
* *
* Copyright (c) 2011 Ericsson AB. * Copyright (c) 2011 Ericsson AB.
* Copyright (c) 2013 Guenter Roeck * Copyright (c) 2013 Guenter Roeck
@ -28,7 +28,7 @@
#include <linux/i2c.h> #include <linux/i2c.h>
#include "pmbus.h" #include "pmbus.h"
enum chips { lm25056, lm25063, lm25066, lm5064, lm5066, lm5066i }; enum chips { lm25056, lm25066, lm5064, lm5066, lm5066i };
#define LM25066_READ_VAUX 0xd0 #define LM25066_READ_VAUX 0xd0
#define LM25066_MFR_READ_IIN 0xd1 #define LM25066_MFR_READ_IIN 0xd1
@ -53,11 +53,6 @@ enum chips { lm25056, lm25063, lm25066, lm5064, lm5066, lm5066i };
#define LM25056_MFR_STS_VAUX_OV_WARN BIT(1) #define LM25056_MFR_STS_VAUX_OV_WARN BIT(1)
#define LM25056_MFR_STS_VAUX_UV_WARN BIT(0) #define LM25056_MFR_STS_VAUX_UV_WARN BIT(0)
/* LM25063 only */
#define LM25063_READ_VOUT_MAX 0xe5
#define LM25063_READ_VOUT_MIN 0xe6
struct __coeff { struct __coeff {
short m, b, R; short m, b, R;
}; };
@ -122,36 +117,6 @@ static struct __coeff lm25066_coeff[6][PSC_NUM_CLASSES + 2] = {
.m = 16, .m = 16,
}, },
}, },
[lm25063] = {
[PSC_VOLTAGE_IN] = {
.m = 16000,
.R = -2,
},
[PSC_VOLTAGE_OUT] = {
.m = 16000,
.R = -2,
},
[PSC_CURRENT_IN] = {
.m = 10000,
.R = -2,
},
[PSC_CURRENT_IN_L] = {
.m = 10000,
.R = -2,
},
[PSC_POWER] = {
.m = 5000,
.R = -3,
},
[PSC_POWER_L] = {
.m = 5000,
.R = -3,
},
[PSC_TEMPERATURE] = {
.m = 15596,
.R = -3,
},
},
[lm5064] = { [lm5064] = {
[PSC_VOLTAGE_IN] = { [PSC_VOLTAGE_IN] = {
.m = 4611, .m = 4611,
@ -272,10 +237,6 @@ static int lm25066_read_word_data(struct i2c_client *client, int page, int reg)
/* VIN: 6.14 mV VAUX: 293 uV LSB */ /* VIN: 6.14 mV VAUX: 293 uV LSB */
ret = DIV_ROUND_CLOSEST(ret * 293, 6140); ret = DIV_ROUND_CLOSEST(ret * 293, 6140);
break; break;
case lm25063:
/* VIN: 6.25 mV VAUX: 200.0 uV LSB */
ret = DIV_ROUND_CLOSEST(ret * 20, 625);
break;
case lm25066: case lm25066:
/* VIN: 4.54 mV VAUX: 283.2 uV LSB */ /* VIN: 4.54 mV VAUX: 283.2 uV LSB */
ret = DIV_ROUND_CLOSEST(ret * 2832, 45400); ret = DIV_ROUND_CLOSEST(ret * 2832, 45400);
@ -330,24 +291,6 @@ static int lm25066_read_word_data(struct i2c_client *client, int page, int reg)
return ret; return ret;
} }
static int lm25063_read_word_data(struct i2c_client *client, int page, int reg)
{
int ret;
switch (reg) {
case PMBUS_VIRT_READ_VOUT_MAX:
ret = pmbus_read_word_data(client, 0, LM25063_READ_VOUT_MAX);
break;
case PMBUS_VIRT_READ_VOUT_MIN:
ret = pmbus_read_word_data(client, 0, LM25063_READ_VOUT_MIN);
break;
default:
ret = lm25066_read_word_data(client, page, reg);
break;
}
return ret;
}
static int lm25056_read_word_data(struct i2c_client *client, int page, int reg) static int lm25056_read_word_data(struct i2c_client *client, int page, int reg)
{ {
int ret; int ret;
@ -502,11 +445,6 @@ static int lm25066_probe(struct i2c_client *client,
info->read_word_data = lm25056_read_word_data; info->read_word_data = lm25056_read_word_data;
info->read_byte_data = lm25056_read_byte_data; info->read_byte_data = lm25056_read_byte_data;
data->rlimit = 0x0fff; data->rlimit = 0x0fff;
} else if (data->id == lm25063) {
info->func[0] |= PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
| PMBUS_HAVE_POUT;
info->read_word_data = lm25063_read_word_data;
data->rlimit = 0xffff;
} else { } else {
info->func[0] |= PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT; info->func[0] |= PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;
info->read_word_data = lm25066_read_word_data; info->read_word_data = lm25066_read_word_data;
@ -543,7 +481,6 @@ static int lm25066_probe(struct i2c_client *client,
static const struct i2c_device_id lm25066_id[] = { static const struct i2c_device_id lm25066_id[] = {
{"lm25056", lm25056}, {"lm25056", lm25056},
{"lm25063", lm25063},
{"lm25066", lm25066}, {"lm25066", lm25066},
{"lm5064", lm5064}, {"lm5064", lm5064},
{"lm5066", lm5066}, {"lm5066", lm5066},

View File

@ -16,12 +16,231 @@
enum max31785_regs { enum max31785_regs {
MFR_REVISION = 0x9b, MFR_REVISION = 0x9b,
MFR_FAN_CONFIG = 0xf1,
}; };
#define MAX31785 0x3030
#define MAX31785A 0x3040
#define MFR_FAN_CONFIG_DUAL_TACH BIT(12)
#define MAX31785_NR_PAGES 23 #define MAX31785_NR_PAGES 23
#define MAX31785_NR_FAN_PAGES 6
static int max31785_read_byte_data(struct i2c_client *client, int page,
int reg)
{
if (page < MAX31785_NR_PAGES)
return -ENODATA;
switch (reg) {
case PMBUS_VOUT_MODE:
return -ENOTSUPP;
case PMBUS_FAN_CONFIG_12:
return pmbus_read_byte_data(client, page - MAX31785_NR_PAGES,
reg);
}
return -ENODATA;
}
static int max31785_write_byte(struct i2c_client *client, int page, u8 value)
{
if (page < MAX31785_NR_PAGES)
return -ENODATA;
return -ENOTSUPP;
}
static int max31785_read_long_data(struct i2c_client *client, int page,
int reg, u32 *data)
{
unsigned char cmdbuf[1];
unsigned char rspbuf[4];
int rc;
struct i2c_msg msg[2] = {
{
.addr = client->addr,
.flags = 0,
.len = sizeof(cmdbuf),
.buf = cmdbuf,
},
{
.addr = client->addr,
.flags = I2C_M_RD,
.len = sizeof(rspbuf),
.buf = rspbuf,
},
};
cmdbuf[0] = reg;
rc = pmbus_set_page(client, page);
if (rc < 0)
return rc;
rc = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
if (rc < 0)
return rc;
*data = (rspbuf[0] << (0 * 8)) | (rspbuf[1] << (1 * 8)) |
(rspbuf[2] << (2 * 8)) | (rspbuf[3] << (3 * 8));
return rc;
}
static int max31785_get_pwm(struct i2c_client *client, int page)
{
int rv;
rv = pmbus_get_fan_rate_device(client, page, 0, percent);
if (rv < 0)
return rv;
else if (rv >= 0x8000)
return 0;
else if (rv >= 0x2711)
return 0x2710;
return rv;
}
static int max31785_get_pwm_mode(struct i2c_client *client, int page)
{
int config;
int command;
config = pmbus_read_byte_data(client, page, PMBUS_FAN_CONFIG_12);
if (config < 0)
return config;
command = pmbus_read_word_data(client, page, PMBUS_FAN_COMMAND_1);
if (command < 0)
return command;
if (config & PB_FAN_1_RPM)
return (command >= 0x8000) ? 3 : 2;
if (command >= 0x8000)
return 3;
else if (command >= 0x2711)
return 0;
return 1;
}
static int max31785_read_word_data(struct i2c_client *client, int page,
int reg)
{
u32 val;
int rv;
switch (reg) {
case PMBUS_READ_FAN_SPEED_1:
if (page < MAX31785_NR_PAGES)
return -ENODATA;
rv = max31785_read_long_data(client, page - MAX31785_NR_PAGES,
reg, &val);
if (rv < 0)
return rv;
rv = (val >> 16) & 0xffff;
break;
case PMBUS_FAN_COMMAND_1:
/*
* PMBUS_FAN_COMMAND_x is probed to judge whether or not to
* expose fan control registers.
*
* Don't expose fan_target attribute for virtual pages.
*/
rv = (page >= MAX31785_NR_PAGES) ? -ENOTSUPP : -ENODATA;
break;
case PMBUS_VIRT_PWM_1:
rv = max31785_get_pwm(client, page);
break;
case PMBUS_VIRT_PWM_ENABLE_1:
rv = max31785_get_pwm_mode(client, page);
break;
default:
rv = -ENODATA;
break;
}
return rv;
}
static inline u32 max31785_scale_pwm(u32 sensor_val)
{
/*
* The datasheet describes the accepted value range for manual PWM as
* [0, 0x2710], while the hwmon pwmX sysfs interface accepts values in
* [0, 255]. The MAX31785 uses DIRECT mode to scale the FAN_COMMAND
* registers and in PWM mode the coefficients are m=1, b=0, R=2. The
* important observation here is that 0x2710 == 10000 == 100 * 100.
*
* R=2 (== 10^2 == 100) accounts for scaling the value provided at the
* sysfs interface into the required hardware resolution, but it does
* not yet yield a value that we can write to the device (this initial
* scaling is handled by pmbus_data2reg()). Multiplying by 100 below
* translates the parameter value into the percentage units required by
* PMBus, and then we scale back by 255 as required by the hwmon pwmX
* interface to yield the percentage value at the appropriate
* resolution for hardware.
*/
return (sensor_val * 100) / 255;
}
static int max31785_pwm_enable(struct i2c_client *client, int page,
u16 word)
{
int config = 0;
int rate;
switch (word) {
case 0:
rate = 0x7fff;
break;
case 1:
rate = pmbus_get_fan_rate_cached(client, page, 0, percent);
if (rate < 0)
return rate;
rate = max31785_scale_pwm(rate);
break;
case 2:
config = PB_FAN_1_RPM;
rate = pmbus_get_fan_rate_cached(client, page, 0, rpm);
if (rate < 0)
return rate;
break;
case 3:
rate = 0xffff;
break;
default:
return -EINVAL;
}
return pmbus_update_fan(client, page, 0, config, PB_FAN_1_RPM, rate);
}
static int max31785_write_word_data(struct i2c_client *client, int page,
int reg, u16 word)
{
switch (reg) {
case PMBUS_VIRT_PWM_1:
return pmbus_update_fan(client, page, 0, 0, PB_FAN_1_RPM,
max31785_scale_pwm(word));
case PMBUS_VIRT_PWM_ENABLE_1:
return max31785_pwm_enable(client, page, word);
default:
break;
}
return -ENODATA;
}
#define MAX31785_FAN_FUNCS \ #define MAX31785_FAN_FUNCS \
(PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12) (PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12 | PMBUS_HAVE_PWM12)
#define MAX31785_TEMP_FUNCS \ #define MAX31785_TEMP_FUNCS \
(PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP) (PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP)
@ -29,14 +248,26 @@ enum max31785_regs {
#define MAX31785_VOUT_FUNCS \ #define MAX31785_VOUT_FUNCS \
(PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT) (PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT)
#define MAX37185_NUM_FAN_PAGES 6
static const struct pmbus_driver_info max31785_info = { static const struct pmbus_driver_info max31785_info = {
.pages = MAX31785_NR_PAGES, .pages = MAX31785_NR_PAGES,
.write_word_data = max31785_write_word_data,
.read_byte_data = max31785_read_byte_data,
.read_word_data = max31785_read_word_data,
.write_byte = max31785_write_byte,
/* RPM */ /* RPM */
.format[PSC_FAN] = direct, .format[PSC_FAN] = direct,
.m[PSC_FAN] = 1, .m[PSC_FAN] = 1,
.b[PSC_FAN] = 0, .b[PSC_FAN] = 0,
.R[PSC_FAN] = 0, .R[PSC_FAN] = 0,
/* PWM */
.format[PSC_PWM] = direct,
.m[PSC_PWM] = 1,
.b[PSC_PWM] = 0,
.R[PSC_PWM] = 2,
.func[0] = MAX31785_FAN_FUNCS, .func[0] = MAX31785_FAN_FUNCS,
.func[1] = MAX31785_FAN_FUNCS, .func[1] = MAX31785_FAN_FUNCS,
.func[2] = MAX31785_FAN_FUNCS, .func[2] = MAX31785_FAN_FUNCS,
@ -72,13 +303,46 @@ static const struct pmbus_driver_info max31785_info = {
.func[22] = MAX31785_VOUT_FUNCS, .func[22] = MAX31785_VOUT_FUNCS,
}; };
static int max31785_configure_dual_tach(struct i2c_client *client,
struct pmbus_driver_info *info)
{
int ret;
int i;
for (i = 0; i < MAX31785_NR_FAN_PAGES; i++) {
ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, i);
if (ret < 0)
return ret;
ret = i2c_smbus_read_word_data(client, MFR_FAN_CONFIG);
if (ret < 0)
return ret;
if (ret & MFR_FAN_CONFIG_DUAL_TACH) {
int virtual = MAX31785_NR_PAGES + i;
info->pages = virtual + 1;
info->func[virtual] |= PMBUS_HAVE_FAN12;
info->func[virtual] |= PMBUS_PAGE_VIRTUAL;
}
}
return 0;
}
static int max31785_probe(struct i2c_client *client, static int max31785_probe(struct i2c_client *client,
const struct i2c_device_id *id) const struct i2c_device_id *id)
{ {
struct device *dev = &client->dev; struct device *dev = &client->dev;
struct pmbus_driver_info *info; struct pmbus_driver_info *info;
bool dual_tach = false;
s64 ret; s64 ret;
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA))
return -ENODEV;
info = devm_kzalloc(dev, sizeof(struct pmbus_driver_info), GFP_KERNEL); info = devm_kzalloc(dev, sizeof(struct pmbus_driver_info), GFP_KERNEL);
if (!info) if (!info)
return -ENOMEM; return -ENOMEM;
@ -89,6 +353,25 @@ static int max31785_probe(struct i2c_client *client,
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = i2c_smbus_read_word_data(client, MFR_REVISION);
if (ret < 0)
return ret;
if (ret == MAX31785A) {
dual_tach = true;
} else if (ret == MAX31785) {
if (!strcmp("max31785a", id->name))
dev_warn(dev, "Expected max3175a, found max31785: cannot provide secondary tachometer readings\n");
} else {
return -ENODEV;
}
if (dual_tach) {
ret = max31785_configure_dual_tach(client, info);
if (ret < 0)
return ret;
}
return pmbus_do_probe(client, id, info); return pmbus_do_probe(client, id, info);
} }
@ -100,9 +383,18 @@ static const struct i2c_device_id max31785_id[] = {
MODULE_DEVICE_TABLE(i2c, max31785_id); MODULE_DEVICE_TABLE(i2c, max31785_id);
static const struct of_device_id max31785_of_match[] = {
{ .compatible = "maxim,max31785" },
{ .compatible = "maxim,max31785a" },
{ },
};
MODULE_DEVICE_TABLE(of, max31785_of_match);
static struct i2c_driver max31785_driver = { static struct i2c_driver max31785_driver = {
.driver = { .driver = {
.name = "max31785", .name = "max31785",
.of_match_table = max31785_of_match,
}, },
.probe = max31785_probe, .probe = max31785_probe,
.remove = pmbus_do_remove, .remove = pmbus_do_remove,

View File

@ -190,6 +190,33 @@ enum pmbus_regs {
PMBUS_VIRT_VMON_UV_FAULT_LIMIT, PMBUS_VIRT_VMON_UV_FAULT_LIMIT,
PMBUS_VIRT_VMON_OV_FAULT_LIMIT, PMBUS_VIRT_VMON_OV_FAULT_LIMIT,
PMBUS_VIRT_STATUS_VMON, PMBUS_VIRT_STATUS_VMON,
/*
* RPM and PWM Fan control
*
* Drivers wanting to expose PWM control must define the behaviour of
* PMBUS_VIRT_PWM_[1-4] and PMBUS_VIRT_PWM_ENABLE_[1-4] in the
* {read,write}_word_data callback.
*
* pmbus core provides a default implementation for
* PMBUS_VIRT_FAN_TARGET_[1-4].
*
* TARGET, PWM and PWM_ENABLE members must be defined sequentially;
* pmbus core uses the difference between the provided register and
* it's _1 counterpart to calculate the FAN/PWM ID.
*/
PMBUS_VIRT_FAN_TARGET_1,
PMBUS_VIRT_FAN_TARGET_2,
PMBUS_VIRT_FAN_TARGET_3,
PMBUS_VIRT_FAN_TARGET_4,
PMBUS_VIRT_PWM_1,
PMBUS_VIRT_PWM_2,
PMBUS_VIRT_PWM_3,
PMBUS_VIRT_PWM_4,
PMBUS_VIRT_PWM_ENABLE_1,
PMBUS_VIRT_PWM_ENABLE_2,
PMBUS_VIRT_PWM_ENABLE_3,
PMBUS_VIRT_PWM_ENABLE_4,
}; };
/* /*
@ -223,6 +250,8 @@ enum pmbus_regs {
#define PB_FAN_1_RPM BIT(6) #define PB_FAN_1_RPM BIT(6)
#define PB_FAN_1_INSTALLED BIT(7) #define PB_FAN_1_INSTALLED BIT(7)
enum pmbus_fan_mode { percent = 0, rpm };
/* /*
* STATUS_BYTE, STATUS_WORD (lower) * STATUS_BYTE, STATUS_WORD (lower)
*/ */
@ -313,6 +342,7 @@ enum pmbus_sensor_classes {
PSC_POWER, PSC_POWER,
PSC_TEMPERATURE, PSC_TEMPERATURE,
PSC_FAN, PSC_FAN,
PSC_PWM,
PSC_NUM_CLASSES /* Number of power sensor classes */ PSC_NUM_CLASSES /* Number of power sensor classes */
}; };
@ -339,6 +369,10 @@ enum pmbus_sensor_classes {
#define PMBUS_HAVE_STATUS_FAN34 BIT(17) #define PMBUS_HAVE_STATUS_FAN34 BIT(17)
#define PMBUS_HAVE_VMON BIT(18) #define PMBUS_HAVE_VMON BIT(18)
#define PMBUS_HAVE_STATUS_VMON BIT(19) #define PMBUS_HAVE_STATUS_VMON BIT(19)
#define PMBUS_HAVE_PWM12 BIT(20)
#define PMBUS_HAVE_PWM34 BIT(21)
#define PMBUS_PAGE_VIRTUAL BIT(31)
enum pmbus_data_format { linear = 0, direct, vid }; enum pmbus_data_format { linear = 0, direct, vid };
enum vrm_version { vr11 = 0, vr12, vr13 }; enum vrm_version { vr11 = 0, vr12, vr13 };
@ -421,5 +455,12 @@ int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id,
int pmbus_do_remove(struct i2c_client *client); int pmbus_do_remove(struct i2c_client *client);
const struct pmbus_driver_info *pmbus_get_driver_info(struct i2c_client const struct pmbus_driver_info *pmbus_get_driver_info(struct i2c_client
*client); *client);
int pmbus_get_fan_rate_device(struct i2c_client *client, int page, int id,
enum pmbus_fan_mode mode);
int pmbus_get_fan_rate_cached(struct i2c_client *client, int page, int id,
enum pmbus_fan_mode mode);
int pmbus_update_fan(struct i2c_client *client, int page, int id,
u8 config, u8 mask, u16 command);
struct dentry *pmbus_get_debugfs_dir(struct i2c_client *client);
#endif /* PMBUS_H */ #endif /* PMBUS_H */

View File

@ -65,6 +65,7 @@ struct pmbus_sensor {
u16 reg; /* register */ u16 reg; /* register */
enum pmbus_sensor_classes class; /* sensor class */ enum pmbus_sensor_classes class; /* sensor class */
bool update; /* runtime sensor update needed */ bool update; /* runtime sensor update needed */
bool convert; /* Whether or not to apply linear/vid/direct */
int data; /* Sensor data. int data; /* Sensor data.
Negative if there was a read error */ Negative if there was a read error */
}; };
@ -129,6 +130,27 @@ struct pmbus_debugfs_entry {
u8 reg; u8 reg;
}; };
static const int pmbus_fan_rpm_mask[] = {
PB_FAN_1_RPM,
PB_FAN_2_RPM,
PB_FAN_1_RPM,
PB_FAN_2_RPM,
};
static const int pmbus_fan_config_registers[] = {
PMBUS_FAN_CONFIG_12,
PMBUS_FAN_CONFIG_12,
PMBUS_FAN_CONFIG_34,
PMBUS_FAN_CONFIG_34
};
static const int pmbus_fan_command_registers[] = {
PMBUS_FAN_COMMAND_1,
PMBUS_FAN_COMMAND_2,
PMBUS_FAN_COMMAND_3,
PMBUS_FAN_COMMAND_4,
};
void pmbus_clear_cache(struct i2c_client *client) void pmbus_clear_cache(struct i2c_client *client)
{ {
struct pmbus_data *data = i2c_get_clientdata(client); struct pmbus_data *data = i2c_get_clientdata(client);
@ -140,18 +162,27 @@ EXPORT_SYMBOL_GPL(pmbus_clear_cache);
int pmbus_set_page(struct i2c_client *client, int page) int pmbus_set_page(struct i2c_client *client, int page)
{ {
struct pmbus_data *data = i2c_get_clientdata(client); struct pmbus_data *data = i2c_get_clientdata(client);
int rv = 0; int rv;
int newpage;
if (page >= 0 && page != data->currpage) { if (page < 0 || page == data->currpage)
return 0;
if (!(data->info->func[page] & PMBUS_PAGE_VIRTUAL)) {
rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page); rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page);
newpage = i2c_smbus_read_byte_data(client, PMBUS_PAGE); if (rv < 0)
if (newpage != page) return rv;
rv = -EIO;
else rv = i2c_smbus_read_byte_data(client, PMBUS_PAGE);
data->currpage = page; if (rv < 0)
return rv;
if (rv != page)
return -EIO;
} }
return rv;
data->currpage = page;
return 0;
} }
EXPORT_SYMBOL_GPL(pmbus_set_page); EXPORT_SYMBOL_GPL(pmbus_set_page);
@ -198,6 +229,28 @@ int pmbus_write_word_data(struct i2c_client *client, int page, u8 reg,
} }
EXPORT_SYMBOL_GPL(pmbus_write_word_data); EXPORT_SYMBOL_GPL(pmbus_write_word_data);
static int pmbus_write_virt_reg(struct i2c_client *client, int page, int reg,
u16 word)
{
int bit;
int id;
int rv;
switch (reg) {
case PMBUS_VIRT_FAN_TARGET_1 ... PMBUS_VIRT_FAN_TARGET_4:
id = reg - PMBUS_VIRT_FAN_TARGET_1;
bit = pmbus_fan_rpm_mask[id];
rv = pmbus_update_fan(client, page, id, bit, bit, word);
break;
default:
rv = -ENXIO;
break;
}
return rv;
}
/* /*
* _pmbus_write_word_data() is similar to pmbus_write_word_data(), but checks if * _pmbus_write_word_data() is similar to pmbus_write_word_data(), but checks if
* a device specific mapping function exists and calls it if necessary. * a device specific mapping function exists and calls it if necessary.
@ -214,11 +267,38 @@ static int _pmbus_write_word_data(struct i2c_client *client, int page, int reg,
if (status != -ENODATA) if (status != -ENODATA)
return status; return status;
} }
if (reg >= PMBUS_VIRT_BASE) if (reg >= PMBUS_VIRT_BASE)
return -ENXIO; return pmbus_write_virt_reg(client, page, reg, word);
return pmbus_write_word_data(client, page, reg, word); return pmbus_write_word_data(client, page, reg, word);
} }
int pmbus_update_fan(struct i2c_client *client, int page, int id,
u8 config, u8 mask, u16 command)
{
int from;
int rv;
u8 to;
from = pmbus_read_byte_data(client, page,
pmbus_fan_config_registers[id]);
if (from < 0)
return from;
to = (from & ~mask) | (config & mask);
if (to != from) {
rv = pmbus_write_byte_data(client, page,
pmbus_fan_config_registers[id], to);
if (rv < 0)
return rv;
}
return _pmbus_write_word_data(client, page,
pmbus_fan_command_registers[id], command);
}
EXPORT_SYMBOL_GPL(pmbus_update_fan);
int pmbus_read_word_data(struct i2c_client *client, int page, u8 reg) int pmbus_read_word_data(struct i2c_client *client, int page, u8 reg)
{ {
int rv; int rv;
@ -231,6 +311,24 @@ int pmbus_read_word_data(struct i2c_client *client, int page, u8 reg)
} }
EXPORT_SYMBOL_GPL(pmbus_read_word_data); EXPORT_SYMBOL_GPL(pmbus_read_word_data);
static int pmbus_read_virt_reg(struct i2c_client *client, int page, int reg)
{
int rv;
int id;
switch (reg) {
case PMBUS_VIRT_FAN_TARGET_1 ... PMBUS_VIRT_FAN_TARGET_4:
id = reg - PMBUS_VIRT_FAN_TARGET_1;
rv = pmbus_get_fan_rate_device(client, page, id, rpm);
break;
default:
rv = -ENXIO;
break;
}
return rv;
}
/* /*
* _pmbus_read_word_data() is similar to pmbus_read_word_data(), but checks if * _pmbus_read_word_data() is similar to pmbus_read_word_data(), but checks if
* a device specific mapping function exists and calls it if necessary. * a device specific mapping function exists and calls it if necessary.
@ -246,8 +344,10 @@ static int _pmbus_read_word_data(struct i2c_client *client, int page, int reg)
if (status != -ENODATA) if (status != -ENODATA)
return status; return status;
} }
if (reg >= PMBUS_VIRT_BASE) if (reg >= PMBUS_VIRT_BASE)
return -ENXIO; return pmbus_read_virt_reg(client, page, reg);
return pmbus_read_word_data(client, page, reg); return pmbus_read_word_data(client, page, reg);
} }
@ -312,6 +412,68 @@ static int _pmbus_read_byte_data(struct i2c_client *client, int page, int reg)
return pmbus_read_byte_data(client, page, reg); return pmbus_read_byte_data(client, page, reg);
} }
static struct pmbus_sensor *pmbus_find_sensor(struct pmbus_data *data, int page,
int reg)
{
struct pmbus_sensor *sensor;
for (sensor = data->sensors; sensor; sensor = sensor->next) {
if (sensor->page == page && sensor->reg == reg)
return sensor;
}
return ERR_PTR(-EINVAL);
}
static int pmbus_get_fan_rate(struct i2c_client *client, int page, int id,
enum pmbus_fan_mode mode,
bool from_cache)
{
struct pmbus_data *data = i2c_get_clientdata(client);
bool want_rpm, have_rpm;
struct pmbus_sensor *s;
int config;
int reg;
want_rpm = (mode == rpm);
if (from_cache) {
reg = want_rpm ? PMBUS_VIRT_FAN_TARGET_1 : PMBUS_VIRT_PWM_1;
s = pmbus_find_sensor(data, page, reg + id);
if (IS_ERR(s))
return PTR_ERR(s);
return s->data;
}
config = pmbus_read_byte_data(client, page,
pmbus_fan_config_registers[id]);
if (config < 0)
return config;
have_rpm = !!(config & pmbus_fan_rpm_mask[id]);
if (want_rpm == have_rpm)
return pmbus_read_word_data(client, page,
pmbus_fan_command_registers[id]);
/* Can't sensibly map between RPM and PWM, just return zero */
return 0;
}
int pmbus_get_fan_rate_device(struct i2c_client *client, int page, int id,
enum pmbus_fan_mode mode)
{
return pmbus_get_fan_rate(client, page, id, mode, false);
}
EXPORT_SYMBOL_GPL(pmbus_get_fan_rate_device);
int pmbus_get_fan_rate_cached(struct i2c_client *client, int page, int id,
enum pmbus_fan_mode mode)
{
return pmbus_get_fan_rate(client, page, id, mode, true);
}
EXPORT_SYMBOL_GPL(pmbus_get_fan_rate_cached);
static void pmbus_clear_fault_page(struct i2c_client *client, int page) static void pmbus_clear_fault_page(struct i2c_client *client, int page)
{ {
_pmbus_write_byte(client, page, PMBUS_CLEAR_FAULTS); _pmbus_write_byte(client, page, PMBUS_CLEAR_FAULTS);
@ -513,7 +675,7 @@ static long pmbus_reg2data_direct(struct pmbus_data *data,
/* X = 1/m * (Y * 10^-R - b) */ /* X = 1/m * (Y * 10^-R - b) */
R = -R; R = -R;
/* scale result to milli-units for everything but fans */ /* scale result to milli-units for everything but fans */
if (sensor->class != PSC_FAN) { if (!(sensor->class == PSC_FAN || sensor->class == PSC_PWM)) {
R += 3; R += 3;
b *= 1000; b *= 1000;
} }
@ -568,6 +730,9 @@ static long pmbus_reg2data(struct pmbus_data *data, struct pmbus_sensor *sensor)
{ {
long val; long val;
if (!sensor->convert)
return sensor->data;
switch (data->info->format[sensor->class]) { switch (data->info->format[sensor->class]) {
case direct: case direct:
val = pmbus_reg2data_direct(data, sensor); val = pmbus_reg2data_direct(data, sensor);
@ -672,7 +837,7 @@ static u16 pmbus_data2reg_direct(struct pmbus_data *data,
} }
/* Calculate Y = (m * X + b) * 10^R */ /* Calculate Y = (m * X + b) * 10^R */
if (sensor->class != PSC_FAN) { if (!(sensor->class == PSC_FAN || sensor->class == PSC_PWM)) {
R -= 3; /* Adjust R and b for data in milli-units */ R -= 3; /* Adjust R and b for data in milli-units */
b *= 1000; b *= 1000;
} }
@ -703,6 +868,9 @@ static u16 pmbus_data2reg(struct pmbus_data *data,
{ {
u16 regval; u16 regval;
if (!sensor->convert)
return val;
switch (data->info->format[sensor->class]) { switch (data->info->format[sensor->class]) {
case direct: case direct:
regval = pmbus_data2reg_direct(data, sensor, val); regval = pmbus_data2reg_direct(data, sensor, val);
@ -915,7 +1083,8 @@ static struct pmbus_sensor *pmbus_add_sensor(struct pmbus_data *data,
const char *name, const char *type, const char *name, const char *type,
int seq, int page, int reg, int seq, int page, int reg,
enum pmbus_sensor_classes class, enum pmbus_sensor_classes class,
bool update, bool readonly) bool update, bool readonly,
bool convert)
{ {
struct pmbus_sensor *sensor; struct pmbus_sensor *sensor;
struct device_attribute *a; struct device_attribute *a;
@ -925,12 +1094,18 @@ static struct pmbus_sensor *pmbus_add_sensor(struct pmbus_data *data,
return NULL; return NULL;
a = &sensor->attribute; a = &sensor->attribute;
snprintf(sensor->name, sizeof(sensor->name), "%s%d_%s", if (type)
name, seq, type); snprintf(sensor->name, sizeof(sensor->name), "%s%d_%s",
name, seq, type);
else
snprintf(sensor->name, sizeof(sensor->name), "%s%d",
name, seq);
sensor->page = page; sensor->page = page;
sensor->reg = reg; sensor->reg = reg;
sensor->class = class; sensor->class = class;
sensor->update = update; sensor->update = update;
sensor->convert = convert;
pmbus_dev_attr_init(a, sensor->name, pmbus_dev_attr_init(a, sensor->name,
readonly ? S_IRUGO : S_IRUGO | S_IWUSR, readonly ? S_IRUGO : S_IRUGO | S_IWUSR,
pmbus_show_sensor, pmbus_set_sensor); pmbus_show_sensor, pmbus_set_sensor);
@ -1029,7 +1204,7 @@ static int pmbus_add_limit_attrs(struct i2c_client *client,
curr = pmbus_add_sensor(data, name, l->attr, index, curr = pmbus_add_sensor(data, name, l->attr, index,
page, l->reg, attr->class, page, l->reg, attr->class,
attr->update || l->update, attr->update || l->update,
false); false, true);
if (!curr) if (!curr)
return -ENOMEM; return -ENOMEM;
if (l->sbit && (info->func[page] & attr->sfunc)) { if (l->sbit && (info->func[page] & attr->sfunc)) {
@ -1068,7 +1243,7 @@ static int pmbus_add_sensor_attrs_one(struct i2c_client *client,
return ret; return ret;
} }
base = pmbus_add_sensor(data, name, "input", index, page, attr->reg, base = pmbus_add_sensor(data, name, "input", index, page, attr->reg,
attr->class, true, true); attr->class, true, true, true);
if (!base) if (!base)
return -ENOMEM; return -ENOMEM;
if (attr->sfunc) { if (attr->sfunc) {
@ -1592,13 +1767,6 @@ static const int pmbus_fan_registers[] = {
PMBUS_READ_FAN_SPEED_4 PMBUS_READ_FAN_SPEED_4
}; };
static const int pmbus_fan_config_registers[] = {
PMBUS_FAN_CONFIG_12,
PMBUS_FAN_CONFIG_12,
PMBUS_FAN_CONFIG_34,
PMBUS_FAN_CONFIG_34
};
static const int pmbus_fan_status_registers[] = { static const int pmbus_fan_status_registers[] = {
PMBUS_STATUS_FAN_12, PMBUS_STATUS_FAN_12,
PMBUS_STATUS_FAN_12, PMBUS_STATUS_FAN_12,
@ -1621,6 +1789,42 @@ static const u32 pmbus_fan_status_flags[] = {
}; };
/* Fans */ /* Fans */
/* Precondition: FAN_CONFIG_x_y and FAN_COMMAND_x must exist for the fan ID */
static int pmbus_add_fan_ctrl(struct i2c_client *client,
struct pmbus_data *data, int index, int page, int id,
u8 config)
{
struct pmbus_sensor *sensor;
sensor = pmbus_add_sensor(data, "fan", "target", index, page,
PMBUS_VIRT_FAN_TARGET_1 + id, PSC_FAN,
false, false, true);
if (!sensor)
return -ENOMEM;
if (!((data->info->func[page] & PMBUS_HAVE_PWM12) ||
(data->info->func[page] & PMBUS_HAVE_PWM34)))
return 0;
sensor = pmbus_add_sensor(data, "pwm", NULL, index, page,
PMBUS_VIRT_PWM_1 + id, PSC_PWM,
false, false, true);
if (!sensor)
return -ENOMEM;
sensor = pmbus_add_sensor(data, "pwm", "enable", index, page,
PMBUS_VIRT_PWM_ENABLE_1 + id, PSC_PWM,
true, false, false);
if (!sensor)
return -ENOMEM;
return 0;
}
static int pmbus_add_fan_attributes(struct i2c_client *client, static int pmbus_add_fan_attributes(struct i2c_client *client,
struct pmbus_data *data) struct pmbus_data *data)
{ {
@ -1655,9 +1859,18 @@ static int pmbus_add_fan_attributes(struct i2c_client *client,
if (pmbus_add_sensor(data, "fan", "input", index, if (pmbus_add_sensor(data, "fan", "input", index,
page, pmbus_fan_registers[f], page, pmbus_fan_registers[f],
PSC_FAN, true, true) == NULL) PSC_FAN, true, true, true) == NULL)
return -ENOMEM; return -ENOMEM;
/* Fan control */
if (pmbus_check_word_register(client, page,
pmbus_fan_command_registers[f])) {
ret = pmbus_add_fan_ctrl(client, data, index,
page, f, regval);
if (ret < 0)
return ret;
}
/* /*
* Each fan status register covers multiple fans, * Each fan status register covers multiple fans,
* so we have to do some magic. * so we have to do some magic.
@ -2168,6 +2381,14 @@ int pmbus_do_remove(struct i2c_client *client)
} }
EXPORT_SYMBOL_GPL(pmbus_do_remove); EXPORT_SYMBOL_GPL(pmbus_do_remove);
struct dentry *pmbus_get_debugfs_dir(struct i2c_client *client)
{
struct pmbus_data *data = i2c_get_clientdata(client);
return data->debugfs;
}
EXPORT_SYMBOL_GPL(pmbus_get_debugfs_dir);
static int __init pmbus_core_init(void) static int __init pmbus_core_init(void)
{ {
pmbus_debugfs_dir = debugfs_create_dir("pmbus", NULL); pmbus_debugfs_dir = debugfs_create_dir("pmbus", NULL);

View File

@ -179,6 +179,7 @@ struct sht15_data {
* sht15_crc8() - compute crc8 * sht15_crc8() - compute crc8
* @data: sht15 specific data. * @data: sht15 specific data.
* @value: sht15 retrieved data. * @value: sht15 retrieved data.
* @len: Length of retrieved data
* *
* This implements section 2 of the CRC datasheet. * This implements section 2 of the CRC datasheet.
*/ */

View File

@ -41,7 +41,7 @@
/** /**
* struct sht21 - SHT21 device specific data * struct sht21 - SHT21 device specific data
* @hwmon_dev: device registered with hwmon * @client: I2C client device
* @lock: mutex to protect measurement values * @lock: mutex to protect measurement values
* @last_update: time of last update (jiffies) * @last_update: time of last update (jiffies)
* @temperature: cached temperature measurement value * @temperature: cached temperature measurement value

View File

@ -732,6 +732,13 @@ static int sht3x_probe(struct i2c_client *client,
mutex_init(&data->i2c_lock); mutex_init(&data->i2c_lock);
mutex_init(&data->data_lock); mutex_init(&data->data_lock);
/*
* An attempt to read limits register too early
* causes a NACK response from the chip.
* Waiting for an empirical delay of 500 us solves the issue.
*/
usleep_range(500, 600);
ret = limits_update(data); ret = limits_update(data);
if (ret) if (ret)
return ret; return ret;

329
drivers/hwmon/w83773g.c Normal file
View File

@ -0,0 +1,329 @@
/*
* Copyright (C) 2017 IBM Corp.
*
* 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.
*
* Driver for the Nuvoton W83773G SMBus temperature sensor IC.
* Supported models: W83773G
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
#include <linux/of_device.h>
#include <linux/regmap.h>
/* W83773 has 3 channels */
#define W83773_CHANNELS 3
/* The W83773 registers */
#define W83773_CONVERSION_RATE_REG_READ 0x04
#define W83773_CONVERSION_RATE_REG_WRITE 0x0A
#define W83773_MANUFACTURER_ID_REG 0xFE
#define W83773_LOCAL_TEMP 0x00
static const u8 W83773_STATUS[2] = { 0x02, 0x17 };
static const u8 W83773_TEMP_LSB[2] = { 0x10, 0x25 };
static const u8 W83773_TEMP_MSB[2] = { 0x01, 0x24 };
static const u8 W83773_OFFSET_LSB[2] = { 0x12, 0x16 };
static const u8 W83773_OFFSET_MSB[2] = { 0x11, 0x15 };
/* this is the number of sensors in the device */
static const struct i2c_device_id w83773_id[] = {
{ "w83773g" },
{ }
};
MODULE_DEVICE_TABLE(i2c, w83773_id);
static const struct of_device_id w83773_of_match[] = {
{
.compatible = "nuvoton,w83773g"
},
{ },
};
MODULE_DEVICE_TABLE(of, w83773_of_match);
static inline long temp_of_local(s8 reg)
{
return reg * 1000;
}
static inline long temp_of_remote(s8 hb, u8 lb)
{
return (hb << 3 | lb >> 5) * 125;
}
static int get_local_temp(struct regmap *regmap, long *val)
{
unsigned int regval;
int ret;
ret = regmap_read(regmap, W83773_LOCAL_TEMP, &regval);
if (ret < 0)
return ret;
*val = temp_of_local(regval);
return 0;
}
static int get_remote_temp(struct regmap *regmap, int index, long *val)
{
unsigned int regval_high;
unsigned int regval_low;
int ret;
ret = regmap_read(regmap, W83773_TEMP_MSB[index], &regval_high);
if (ret < 0)
return ret;
ret = regmap_read(regmap, W83773_TEMP_LSB[index], &regval_low);
if (ret < 0)
return ret;
*val = temp_of_remote(regval_high, regval_low);
return 0;
}
static int get_fault(struct regmap *regmap, int index, long *val)
{
unsigned int regval;
int ret;
ret = regmap_read(regmap, W83773_STATUS[index], &regval);
if (ret < 0)
return ret;
*val = (regval & 0x04) >> 2;
return 0;
}
static int get_offset(struct regmap *regmap, int index, long *val)
{
unsigned int regval_high;
unsigned int regval_low;
int ret;
ret = regmap_read(regmap, W83773_OFFSET_MSB[index], &regval_high);
if (ret < 0)
return ret;
ret = regmap_read(regmap, W83773_OFFSET_LSB[index], &regval_low);
if (ret < 0)
return ret;
*val = temp_of_remote(regval_high, regval_low);
return 0;
}
static int set_offset(struct regmap *regmap, int index, long val)
{
int ret;
u8 high_byte;
u8 low_byte;
val = clamp_val(val, -127825, 127825);
/* offset value equals to (high_byte << 3 | low_byte >> 5) * 125 */
val /= 125;
high_byte = val >> 3;
low_byte = (val & 0x07) << 5;
ret = regmap_write(regmap, W83773_OFFSET_MSB[index], high_byte);
if (ret < 0)
return ret;
return regmap_write(regmap, W83773_OFFSET_LSB[index], low_byte);
}
static int get_update_interval(struct regmap *regmap, long *val)
{
unsigned int regval;
int ret;
ret = regmap_read(regmap, W83773_CONVERSION_RATE_REG_READ, &regval);
if (ret < 0)
return ret;
*val = 16000 >> regval;
return 0;
}
static int set_update_interval(struct regmap *regmap, long val)
{
int rate;
/*
* For valid rates, interval can be calculated as
* interval = (1 << (8 - rate)) * 62.5;
* Rounded rate is therefore
* rate = 8 - __fls(interval * 8 / (62.5 * 7));
* Use clamp_val() to avoid overflows, and to ensure valid input
* for __fls.
*/
val = clamp_val(val, 62, 16000) * 10;
rate = 8 - __fls((val * 8 / (625 * 7)));
return regmap_write(regmap, W83773_CONVERSION_RATE_REG_WRITE, rate);
}
static int w83773_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
struct regmap *regmap = dev_get_drvdata(dev);
if (type == hwmon_chip) {
if (attr == hwmon_chip_update_interval)
return get_update_interval(regmap, val);
return -EOPNOTSUPP;
}
switch (attr) {
case hwmon_temp_input:
if (channel == 0)
return get_local_temp(regmap, val);
return get_remote_temp(regmap, channel - 1, val);
case hwmon_temp_fault:
return get_fault(regmap, channel - 1, val);
case hwmon_temp_offset:
return get_offset(regmap, channel - 1, val);
default:
return -EOPNOTSUPP;
}
}
static int w83773_write(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long val)
{
struct regmap *regmap = dev_get_drvdata(dev);
if (type == hwmon_chip && attr == hwmon_chip_update_interval)
return set_update_interval(regmap, val);
if (type == hwmon_temp && attr == hwmon_temp_offset)
return set_offset(regmap, channel - 1, val);
return -EOPNOTSUPP;
}
static umode_t w83773_is_visible(const void *data, enum hwmon_sensor_types type,
u32 attr, int channel)
{
switch (type) {
case hwmon_chip:
switch (attr) {
case hwmon_chip_update_interval:
return 0644;
}
break;
case hwmon_temp:
switch (attr) {
case hwmon_temp_input:
case hwmon_temp_fault:
return 0444;
case hwmon_temp_offset:
return 0644;
}
break;
default:
break;
}
return 0;
}
static const u32 w83773_chip_config[] = {
HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL,
0
};
static const struct hwmon_channel_info w83773_chip = {
.type = hwmon_chip,
.config = w83773_chip_config,
};
static const u32 w83773_temp_config[] = {
HWMON_T_INPUT,
HWMON_T_INPUT | HWMON_T_FAULT | HWMON_T_OFFSET,
HWMON_T_INPUT | HWMON_T_FAULT | HWMON_T_OFFSET,
0
};
static const struct hwmon_channel_info w83773_temp = {
.type = hwmon_temp,
.config = w83773_temp_config,
};
static const struct hwmon_channel_info *w83773_info[] = {
&w83773_chip,
&w83773_temp,
NULL
};
static const struct hwmon_ops w83773_ops = {
.is_visible = w83773_is_visible,
.read = w83773_read,
.write = w83773_write,
};
static const struct hwmon_chip_info w83773_chip_info = {
.ops = &w83773_ops,
.info = w83773_info,
};
static const struct regmap_config w83773_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
};
static int w83773_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct device *hwmon_dev;
struct regmap *regmap;
int ret;
regmap = devm_regmap_init_i2c(client, &w83773_regmap_config);
if (IS_ERR(regmap)) {
dev_err(dev, "failed to allocate register map\n");
return PTR_ERR(regmap);
}
/* Set the conversion rate to 2 Hz */
ret = regmap_write(regmap, W83773_CONVERSION_RATE_REG_WRITE, 0x05);
if (ret < 0) {
dev_err(&client->dev, "error writing config rate register\n");
return ret;
}
i2c_set_clientdata(client, regmap);
hwmon_dev = devm_hwmon_device_register_with_info(dev,
client->name,
regmap,
&w83773_chip_info,
NULL);
return PTR_ERR_OR_ZERO(hwmon_dev);
}
static struct i2c_driver w83773_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = "w83773g",
.of_match_table = of_match_ptr(w83773_of_match),
},
.probe = w83773_probe,
.id_table = w83773_id,
};
module_i2c_driver(w83773_driver);
MODULE_AUTHOR("Lei YU <mine260309@gmail.com>");
MODULE_DESCRIPTION("W83773G temperature sensor driver");
MODULE_LICENSE("GPL");