Commit 47d5cc5b authored by Linus Torvalds's avatar Linus Torvalds

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

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
  ...
parents 0fc7e746 6fbc4232
......@@ -22,8 +22,9 @@ Required properties for pwm-tacho node:
- compatible : should be "aspeed,ast2400-pwm-tacho" for AST2400 and
"aspeed,ast2500-pwm-tacho" for AST2500.
- clocks : a fixed clock providing input clock frequency(PWM
and Fan Tach clock)
- clocks : phandle to clock provider with the clock number in the second cell
- resets : phandle to reset controller with the reset number in the second cell
fan subnode format:
===================
......@@ -48,19 +49,14 @@ Required properties for each child node:
Examples:
pwm_tacho_fixed_clk: fixedclk {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <24000000>;
};
pwm_tacho: pwmtachocontroller@1e786000 {
#address-cells = <1>;
#size-cells = <1>;
#cooling-cells = <2>;
reg = <0x1E786000 0x1000>;
compatible = "aspeed,ast2500-pwm-tacho";
clocks = <&pwm_tacho_fixed_clk>;
clocks = <&syscon ASPEED_CLK_APB>;
resets = <&syscon ASPEED_RESET_PWM>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_pwm0_default &pinctrl_pwm1_default>;
......
......@@ -8,11 +8,6 @@ Supported chips:
Datasheets:
http://www.ti.com/lit/gpn/lm25056
http://www.ti.com/lit/gpn/lm25056a
* TI LM25063
Prefix: 'lm25063'
Addresses scanned: -
Datasheet:
To be announced
* National Semiconductor LM25066
Prefix: 'lm25066'
Addresses scanned: -
......@@ -42,7 +37,7 @@ Description
-----------
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.
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_min Minimum 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_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_input Measured voltage on VAUX pin
......@@ -94,16 +85,12 @@ in3_input Measured output voltage.
in3_average Average measured output voltage.
in3_min Minimum output voltage.
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_input Measured input current.
curr1_average Average measured input current.
curr1_max Maximum input current.
curr1_crit Critical input current (LM25063 only).
curr1_max_alarm Input current high alarm.
curr1_crit_alarm Input current critical high alarm (LM25063 only).
power1_label "pin"
power1_input Measured input power.
......@@ -113,11 +100,6 @@ power1_alarm Input power alarm
power1_input_highest Historical maximum power.
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_max Maximum temperature.
temp1_crit Critical high temperature.
......
......@@ -17,8 +17,9 @@ management with temperature and remote voltage sensing. Various fan control
features are provided, including PWM frequency control, temperature hysteresis,
dual tachometer measurements, and fan health monitoring.
For dual rotor fan configuration, the MAX31785 exposes the slowest rotor of the
two in the fan[1-4]_input attributes.
For dual-rotor configurations the MAX31785A exposes the second rotor tachometer
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
-----------
......@@ -31,7 +32,9 @@ Sysfs attributes
fan[1-4]_alarm Fan alarm.
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_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_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_alarm Chip temperature critical high alarm
temp[1-11]_input Measured temperature
......
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
......@@ -26,11 +26,9 @@ if HWMON
config HWMON_VID
tristate
default n
config HWMON_DEBUG_CHIP
bool "Hardware Monitoring Chip debugging messages"
default n
help
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
......@@ -42,7 +40,6 @@ comment "Native drivers"
config SENSORS_AB8500
tristate "AB8500 thermal monitoring"
depends on AB8500_GPADC && AB8500_BM
default n
help
If you say yes here you get support for the thermal sensor part
of the AB8500 chip. The driver includes thermal management for
......@@ -302,7 +299,6 @@ config SENSORS_APPLESMC
select NEW_LEDS
select LEDS_CLASS
select INPUT_POLLDEV
default n
help
This driver provides support for the Apple System Management
Controller, which provides an accelerometer (Apple Sudden Motion
......@@ -678,7 +674,6 @@ config SENSORS_JC42
config SENSORS_POWR1220
tristate "Lattice POWR1220 Power Monitoring"
depends on I2C
default n
help
If you say yes here you get access to the hardware monitoring
functions of the Lattice POWR1220 isp Power Supply Monitoring,
......@@ -702,7 +697,6 @@ config SENSORS_LTC2945
tristate "Linear Technology LTC2945"
depends on I2C
select REGMAP_I2C
default n
help
If you say yes here you get support for Linear Technology LTC2945
I2C System Monitor.
......@@ -727,7 +721,6 @@ config SENSORS_LTC2990
config SENSORS_LTC4151
tristate "Linear Technology LTC4151"
depends on I2C
default n
help
If you say yes here you get support for Linear Technology LTC4151
High Voltage I2C Current and Voltage Monitor interface.
......@@ -738,7 +731,6 @@ config SENSORS_LTC4151
config SENSORS_LTC4215
tristate "Linear Technology LTC4215"
depends on I2C
default n
help
If you say yes here you get support for Linear Technology LTC4215
Hot Swap Controller I2C interface.
......@@ -750,7 +742,6 @@ config SENSORS_LTC4222
tristate "Linear Technology LTC4222"
depends on I2C
select REGMAP_I2C
default n
help
If you say yes here you get support for Linear Technology LTC4222
Dual Hot Swap Controller I2C interface.
......@@ -761,7 +752,6 @@ config SENSORS_LTC4222
config SENSORS_LTC4245
tristate "Linear Technology LTC4245"
depends on I2C
default n
help
If you say yes here you get support for Linear Technology LTC4245
Multiple Supply Hot Swap Controller I2C interface.
......@@ -773,7 +763,6 @@ config SENSORS_LTC4260
tristate "Linear Technology LTC4260"
depends on I2C
select REGMAP_I2C
default n
help
If you say yes here you get support for Linear Technology LTC4260
Positive Voltage Hot Swap Controller I2C interface.
......@@ -784,7 +773,6 @@ config SENSORS_LTC4260
config SENSORS_LTC4261
tristate "Linear Technology LTC4261"
depends on I2C
default n
help
If you say yes here you get support for Linear Technology LTC4261
Negative Voltage Hot Swap Controller I2C interface.
......@@ -1276,7 +1264,6 @@ config SENSORS_NSA320
config SENSORS_PCF8591
tristate "Philips PCF8591 ADC/DAC"
depends on I2C
default n
help
If you say yes here you get support for Philips PCF8591 4-channel
ADC, 1-channel DAC chips.
......@@ -1459,7 +1446,6 @@ config SENSORS_SMSC47B397
config SENSORS_SCH56XX_COMMON
tristate
default n
config SENSORS_SCH5627
tristate "SMSC SCH5627"
......@@ -1505,7 +1491,6 @@ config SENSORS_STTS751
config SENSORS_SMM665
tristate "Summit Microelectronics SMM665"
depends on I2C
default n
help
If you say yes here you get support for the hardware monitoring
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
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
tristate "Winbond W83781D, W83782D, W83783S, Asus AS99127F"
depends on I2C
......@@ -1782,7 +1777,6 @@ config SENSORS_W83795
config SENSORS_W83795_FANCTRL
bool "Include automatic fan control support (DANGEROUS)"
depends on SENSORS_W83795
default n
help
If you say yes here, support for automatic fan speed control
will be included in the driver.
......
......@@ -14,6 +14,7 @@ obj-$(CONFIG_SENSORS_ATK0110) += asus_atk0110.o
# asb100, then w83781d go first, as they can override other drivers' addresses.
obj-$(CONFIG_SENSORS_ASB100) += asb100.o
obj-$(CONFIG_SENSORS_W83627HF) += w83627hf.o
obj-$(CONFIG_SENSORS_W83773G) += w83773g.o
obj-$(CONFIG_SENSORS_W83792D) += w83792d.o
obj-$(CONFIG_SENSORS_W83793) += w83793.o
obj-$(CONFIG_SENSORS_W83795) += w83795.o
......
......@@ -19,6 +19,7 @@
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/sysfs.h>
#include <linux/thermal.h>
......@@ -181,6 +182,7 @@ struct aspeed_cooling_device {
struct aspeed_pwm_tacho_data {
struct regmap *regmap;
struct reset_control *rst;
unsigned long clk_freq;
bool pwm_present[8];
bool fan_tach_present[16];
......@@ -905,6 +907,13 @@ static int aspeed_create_fan(struct device *dev,
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)
{
struct device *dev = &pdev->dev;
......@@ -931,6 +940,19 @@ static int aspeed_pwm_tacho_probe(struct platform_device *pdev)
&aspeed_pwm_tacho_regmap_config);
if (IS_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_EXT, 0);
......
......@@ -246,7 +246,8 @@ static int adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev)
int err;
u32 eax, edx;
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.
......
......@@ -76,6 +76,7 @@ static uint i8k_fan_mult = I8K_FAN_MULT;
static uint i8k_pwm_mult;
static uint i8k_fan_max = I8K_FAN_HIGH;
static bool disallow_fan_type_call;
static bool disallow_fan_support;
#define I8K_HWMON_HAVE_TEMP1 (1 << 0)
#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, };
if (disallow_fan_support)
return -EINVAL;
regs.ebx = fan & 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, };
if (disallow_fan_support)
return -EINVAL;
regs.ebx = fan & 0xff;
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, };
if (disallow_fan_type_call)
if (disallow_fan_support || disallow_fan_type_call)
return -EINVAL;
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, };
if (disallow_fan_support)
return -EINVAL;
regs.ebx = (fan & 0xff) | (speed << 8);
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, };
if (disallow_fan_support)
return -EINVAL;
speed = (speed < 0) ? 0 : ((speed > i8k_fan_max) ? i8k_fan_max : speed);
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,
int index)
{
if (disallow_fan_support && index >= 8)
return 0;
if (disallow_fan_type_call &&
(index == 9 || index == 12 || index == 15))
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.
*/
......@@ -1060,8 +1099,17 @@ static int __init i8k_probe(void)
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)) {
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),
sizeof(bios_version));
......
......@@ -37,7 +37,7 @@
/**
* 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
* @valid: only false before first measurement is taken
* @last_update: time of last update (jiffies)
......
......@@ -678,7 +678,7 @@ EXPORT_SYMBOL_GPL(hwmon_device_register_with_groups);
* @dev: the parent device
* @name: hwmon name attribute
* @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
*
* hwmon_device_unregister() must be called when the device is no
......@@ -788,8 +788,8 @@ EXPORT_SYMBOL_GPL(devm_hwmon_device_register_with_groups);
* @dev: the parent device
* @name: hwmon name attribute
* @drvdata: driver data to attach to created device
* @info: Pointer to hwmon chip information
* @groups - pointer to list of driver specific attribute groups
* @chip: pointer to hwmon chip information
* @groups: pointer to list of driver specific attribute groups
*
* Returns the pointer to the new device. The new device is automatically
* unregistered with the parent device.
......
......@@ -24,6 +24,7 @@
* @num_channels: number of channels in channels (saves counting twice)
* @hwmon_dev: associated hwmon device
* @attr_group: the group of attributes
* @groups: null terminated array of attribute groups
* @attrs: null terminated array of attribute pointers.
*/
struct iio_hwmon_state {
......
......@@ -95,18 +95,20 @@ enum ina2xx_ids { ina219, ina226 };
struct ina2xx_config {
u16 config_default;
int calibration_factor;
int calibration_value;
int registers;
int shunt_div;
int bus_voltage_shift;
int bus_voltage_lsb; /* uV */
int power_lsb; /* uW */
int power_lsb_factor;
};
struct ina2xx_data {
const struct ina2xx_config *config;
long rshunt;
long current_lsb_uA;
long power_lsb_uW;
struct mutex config_lock;
struct regmap *regmap;
......@@ -116,21 +118,21 @@ struct ina2xx_data {
static const struct ina2xx_config ina2xx_config[] = {
[ina219] = {
.config_default = INA219_CONFIG_DEFAULT,
.calibration_factor = 40960000,
.calibration_value = 4096,
.registers = INA219_REGISTERS,
.shunt_div = 100,
.bus_voltage_shift = 3,
.bus_voltage_lsb = 4000,
.power_lsb = 20000,
.power_lsb_factor = 20,
},
[ina226] = {
.config_default = INA226_CONFIG_DEFAULT,
.calibration_factor = 5120000,
.calibration_value = 2048,
.registers = INA226_REGISTERS,
.shunt_div = 400,
.bus_voltage_shift = 0,
.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);
}
/*
* 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)
{
u16 val = DIV_ROUND_CLOSEST(data->config->calibration_factor,
data->rshunt);
return regmap_write(data->regmap, INA2XX_CALIBRATION, val);
return regmap_write(data->regmap, INA2XX_CALIBRATION,
data->config->calibration_value);
}
/*
......@@ -187,10 +193,6 @@ static int ina2xx_init(struct ina2xx_data *data)
if (ret < 0)
return ret;
/*
* Set current LSB to 1mA, shunt is in uOhms
* (equation 13 in datasheet).
*/
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);
break;
case INA2XX_POWER:
val = regval * data->config->power_lsb;
val = regval * data->power_lsb_uW;
break;
case INA2XX_CURRENT:
/* signed register, LSB=1mA (selected), in mA */
val = (s16)regval;
/* signed register, result in mA */
val = regval * data->current_lsb_uA;
val = DIV_ROUND_CLOSEST(val, 1000);
break;
case INA2XX_CALIBRATION:
val = DIV_ROUND_CLOSEST(data->config->calibration_factor,
regval);
val = regval;
break;
default:
/* programmer goofed */
......@@ -304,7 +306,30 @@ static ssize_t ina2xx_show_value(struct device *dev,
ina2xx_get_value(data, attr->index, regval));
}
static ssize_t ina2xx_set_shunt(struct device *dev,
/*
* In order to keep calibration register value fixed, the product
* 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)
{
......@@ -316,18 +341,9 @@ static ssize_t ina2xx_set_shunt(struct device *dev,
if (status < 0)
return status;
if (val == 0 ||
/* 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);
status = ina2xx_set_shunt(data, val);
if (status < 0)
return status;
return count;
}
......@@ -387,7 +403,7 @@ static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, ina2xx_show_value, NULL,
/* shunt resistance */
static SENSOR_DEVICE_ATTR(shunt_resistor, S_IRUGO | S_IWUSR,
ina2xx_show_value, ina2xx_set_shunt,
ina2xx_show_value, ina2xx_store_shunt,
INA2XX_CALIBRATION);
/* update interval (ina226 only) */
......@@ -438,6 +454,7 @@ static int ina2xx_probe(struct i2c_client *client,
/* set the device type */
data->config = &ina2xx_config[chip];
mutex_init(&data->config_lock);
if (of_property_read_u32(dev->of_node, "shunt-resistor", &val) < 0) {
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;
}
if (val <= 0 || val > data->config->calibration_factor)
return -ENODEV;
data->rshunt = val;
ina2xx_set_shunt(data, val);
ina2xx_regmap_config.max_register = data->config->registers;
......@@ -467,8 +481,6 @@ static int ina2xx_probe(struct i2c_client *client,
return -ENODEV;
}
mutex_init(&data->config_lock);
data->groups[group++] = &ina2xx_group;
if (id->driver_data == ina226)
data->groups[group++] = &ina226_group;
......
......@@ -86,6 +86,7 @@ static const struct tctl_offset tctl_offset_table[] = {
{ 0x17, "AMD Ryzen 7 1800X", 20000 },
{ 0x17, "AMD Ryzen Threadripper 1950X", 27000 },
{ 0x17, "AMD Ryzen Threadripper 1920X", 27000 },
{ 0x17, "AMD Ryzen Threadripper 1900X", 27000 },
{ 0x17, "AMD Ryzen Threadripper 1950", 10000 },
{ 0x17, "AMD Ryzen Threadripper 1920", 10000 },
{ 0x17, "AMD Ryzen Threadripper 1910", 10000 },
......
......@@ -100,7 +100,7 @@ static int lm75_read(struct device *dev, enum hwmon_sensor_types type,
switch (attr) {
case hwmon_chip_update_interval:
*val = data->sample_time;
break;;
break;
default:
return -EINVAL;
}
......
......@@ -39,6 +39,7 @@ config SENSORS_ADM1275
config SENSORS_IBM_CFFPS
tristate "IBM Common Form Factor Power Supply"
depends on LEDS_CLASS
help
If you say yes here you get hardware monitoring support for the IBM
Common Form Factor power supply.
......
......@@ -8,12 +8,29 @@
*/
#include <linux/bitops.h>
#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/i2c.h>
#include <linux/jiffies.h>
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/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 */
#define CFFPS_MFR_FAN_FAULT BIT(0)
#define CFFPS_MFR_THERMAL_FAULT BIT(1)
......@@ -24,6 +41,153 @@
#define CFFPS_MFR_VAUX_FAULT BIT(6)
#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,
int reg)
{
......@@ -105,6 +269,69 @@ static int ibm_cffps_read_word_data(struct i2c_client *client, int page,
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 = {
.pages = 1,
.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,
};
static struct pmbus_platform_data ibm_cffps_pdata = {
.flags = PMBUS_SKIP_STATUS_CHECK,
};
static int ibm_cffps_probe(struct i2c_client *client,
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[] = {
......
......@@ -25,168 +25,19 @@
#define IR35221_MFR_IOUT_VALLEY 0xcb
#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)
{
int ret;
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:
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;
case PMBUS_VIRT_READ_VOUT_MAX:
ret = pmbus_read_word_data(client, page, IR35221_MFR_VOUT_PEAK);
break;
case PMBUS_VIRT_READ_IOUT_MAX:
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;
case PMBUS_VIRT_READ_TEMP_MAX:
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:
ret = pmbus_read_word_data(client, page,
IR35221_MFR_VIN_VALLEY);
if (ret < 0)
break;
ret = ir35221_scale_result(ret, -5, PSC_VOLTAGE_IN);
break;
case PMBUS_VIRT_READ_VOUT_MIN:
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:
ret = pmbus_read_word_data(client, page,
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;
case PMBUS_VIRT_READ_TEMP_MIN:
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;
}
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,
const struct i2c_device_id *id)
{
......@@ -292,7 +104,6 @@ static int ir35221_probe(struct i2c_client *client,
if (!info)
return -ENOMEM;
info->write_word_data = ir35221_write_word_data;
info->read_word_data = ir35221_read_word_data;
info->pages = 2;
......
/*
* Hardware monitoring driver for LM25056 / LM25063 / LM25066 / LM5064 / LM5066
* Hardware monitoring driver for LM25056 / LM25066 / LM5064 / LM5066
*
* Copyright (c) 2011 Ericsson AB.
* Copyright (c) 2013 Guenter Roeck
......@@ -28,7 +28,7 @@
#include <linux/i2c.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_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_UV_WARN BIT(0)
/* LM25063 only */
#define LM25063_READ_VOUT_MAX 0xe5
#define LM25063_READ_VOUT_MIN 0xe6
struct __coeff {
short m, b, R;
};
......@@ -122,36 +117,6 @@ static struct __coeff lm25066_coeff[6][PSC_NUM_CLASSES + 2] = {
.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] = {
[PSC_VOLTAGE_IN] = {
.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 */
ret = DIV_ROUND_CLOSEST(ret * 293, 6140);
break;
case lm25063:
/* VIN: 6.25 mV VAUX: 200.0 uV LSB */
ret = DIV_ROUND_CLOSEST(ret * 20, 625);
break;
case lm25066:
/* VIN: 4.54 mV VAUX: 283.2 uV LSB */
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;
}
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)
{
int ret;
......@@ -502,11 +445,6 @@ static int lm25066_probe(struct i2c_client *client,
info->read_word_data = lm25056_read_word_data;
info->read_byte_data = lm25056_read_byte_data;
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 {
info->func[0] |= PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;
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[] = {
{"lm25056", lm25056},
{"lm25063", lm25063},
{"lm25066", lm25066},
{"lm5064", lm5064},
{"lm5066", lm5066},
......
......@@ -16,12 +16,231 @@
enum max31785_regs {
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_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 \
(PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12)
(PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12 | PMBUS_HAVE_PWM12)
#define MAX31785_TEMP_FUNCS \
(PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP)
......@@ -29,14 +248,26 @@ enum max31785_regs {
#define MAX31785_VOUT_FUNCS \
(PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT)
#define MAX37185_NUM_FAN_PAGES 6
static const struct pmbus_driver_info max31785_info = {
.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 */
.format[PSC_FAN] = direct,
.m[PSC_FAN] = 1,
.b[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[1] = 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,
};
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,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct pmbus_driver_info *info;
bool dual_tach = false;
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);
if (!info)
return -ENOMEM;
......@@ -89,6 +353,25 @@ static int max31785_probe(struct i2c_client *client,
if (ret < 0)
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);
}
......@@ -100,9 +383,18 @@ static const struct i2c_device_id 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 = {
.driver = {
.name = "max31785",
.of_match_table = max31785_of_match,
},
.probe = max31785_probe,
.remove = pmbus_do_remove,
......
......@@ -190,6 +190,33 @@ enum pmbus_regs {
PMBUS_VIRT_VMON_UV_FAULT_LIMIT,
PMBUS_VIRT_VMON_OV_FAULT_LIMIT,
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_INSTALLED BIT(7)
enum pmbus_fan_mode { percent = 0, rpm };
/*
* STATUS_BYTE, STATUS_WORD (lower)
*/
......@@ -313,6 +342,7 @@ enum pmbus_sensor_classes {
PSC_POWER,
PSC_TEMPERATURE,
PSC_FAN,
PSC_PWM,
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_VMON BIT(18)
#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 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);
const struct pmbus_driver_info *pmbus_get_driver_info(struct i2c_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 */
This diff is collapsed.
......@@ -179,6 +179,7 @@ struct sht15_data {
* sht15_crc8() - compute crc8
* @data: sht15 specific data.
* @value: sht15 retrieved data.
* @len: Length of retrieved data
*
* This implements section 2 of the CRC datasheet.
*/
......
......@@ -41,7 +41,7 @@
/**
* struct sht21 - SHT21 device specific data
* @hwmon_dev: device registered with hwmon
* @client: I2C client device
* @lock: mutex to protect measurement values
* @last_update: time of last update (jiffies)
* @temperature: cached temperature measurement value
......
......@@ -732,6 +732,13 @@ static int sht3x_probe(struct i2c_client *client,
mutex_init(&data->i2c_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);
if (ret)
return ret;
......
/*
* 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");
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