mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-13 07:08:21 -04:00
Merge tag 'hwmon-for-linus-v4.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging
Pull hwmon updates from Guenter Roeck: - new driver for NPCM7xx PWM and Fan controller - new driver for Mellanox FAN controller - add support for MAX34451 to max34440 driver - add support for new Threadripper variants to k10temp driver - add error handling to adt7475 driver - cleanup nct6775 and nct7904 drivers - document sensor enable ABI attributes * tag 'hwmon-for-linus-v4.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: hwmon: (adt7475) Change show functions to return error data correctly hwmon: (adt7475) Change update functions to add error handling hwmon: (adt7475) Change valid parameter to bool type hwmon: (adt7475) Split device update function to measure and limits hwmon: k10temp: Support Threadripper 2920X, 2970WX; simplify offset table hwmon: (k10temp) 27C Offset needed for Threadripper2 hwmon: (iio_hwmon) Use devm functions hwmon: Add NPCM7xx PWM and Fan driver dt-binding: hwmon: Add NPCM7xx PWM and Fan controller documentation hwmon: (pmbus/max34440) Add support for MAX34451. hwmon: Document the sensor enable attribute hwmon: (mlxreg-fan) Add support for Mellanox FAN driver hwmon: Mark expected switch fall-throughs hwmon: (nct6775) Fix comment in the description of pwm_mode hwmon: (nct7904) Fix UNSPECIFIED_INT warning hwmon: (nct7904) Fix CODE_INDENT error hwmon: (nct7904) Fix SPACING errors
This commit is contained in:
84
Documentation/devicetree/bindings/hwmon/npcm750-pwm-fan.txt
Normal file
84
Documentation/devicetree/bindings/hwmon/npcm750-pwm-fan.txt
Normal file
@@ -0,0 +1,84 @@
|
||||
Nuvoton NPCM7xx PWM and Fan Tacho controller device
|
||||
|
||||
The Nuvoton BMC NPCM7XX supports 8 Pulse-width modulation (PWM)
|
||||
controller outputs and 16 Fan tachometer controller inputs.
|
||||
|
||||
Required properties for pwm-fan node
|
||||
- #address-cells : should be 1.
|
||||
- #size-cells : should be 0.
|
||||
- compatible : "nuvoton,npcm750-pwm-fan" for Poleg NPCM7XX.
|
||||
- reg : specifies physical base address and size of the registers.
|
||||
- reg-names : must contain:
|
||||
* "pwm" for the PWM registers.
|
||||
* "fan" for the Fan registers.
|
||||
- clocks : phandle of reference clocks.
|
||||
- clock-names : must contain
|
||||
* "pwm" for PWM controller operating clock.
|
||||
* "fan" for Fan controller operating clock.
|
||||
- interrupts : contain the Fan interrupts with flags for falling edge.
|
||||
- pinctrl-names : a pinctrl state named "default" must be defined.
|
||||
- pinctrl-0 : phandle referencing pin configuration of the PWM and Fan
|
||||
controller ports.
|
||||
|
||||
fan subnode format:
|
||||
===================
|
||||
Under fan subnode can be upto 8 child nodes, each child node representing a fan.
|
||||
Each fan subnode must have one PWM channel and atleast one Fan tach channel.
|
||||
|
||||
For PWM channel can be configured cooling-levels to create cooling device.
|
||||
Cooling device could be bound to a thermal zone for the thermal control.
|
||||
|
||||
Required properties for each child node:
|
||||
- reg : specify the PWM output channel.
|
||||
integer value in the range 0 through 7, that represent
|
||||
the PWM channel number that used.
|
||||
|
||||
- fan-tach-ch : specify the Fan tach input channel.
|
||||
integer value in the range 0 through 15, that represent
|
||||
the fan tach channel number that used.
|
||||
|
||||
At least one Fan tach input channel is required
|
||||
|
||||
Optional property for each child node:
|
||||
- cooling-levels: PWM duty cycle values in a range from 0 to 255
|
||||
which correspond to thermal cooling states.
|
||||
|
||||
Examples:
|
||||
|
||||
pwm_fan:pwm-fan-controller@103000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "nuvoton,npcm750-pwm-fan";
|
||||
reg = <0x103000 0x2000>,
|
||||
<0x180000 0x8000>;
|
||||
reg-names = "pwm", "fan";
|
||||
clocks = <&clk NPCM7XX_CLK_APB3>,
|
||||
<&clk NPCM7XX_CLK_APB4>;
|
||||
clock-names = "pwm","fan";
|
||||
interrupts = <GIC_SPI 96 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 97 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 99 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 101 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 102 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pwm0_pins &pwm1_pins &pwm2_pins
|
||||
&fanin0_pins &fanin1_pins &fanin2_pins
|
||||
&fanin3_pins &fanin4_pins>;
|
||||
fan@0 {
|
||||
reg = <0x00>;
|
||||
fan-tach-ch = /bits/ 8 <0x00 0x01>;
|
||||
cooling-levels = <127 255>;
|
||||
};
|
||||
fan@1 {
|
||||
reg = <0x01>;
|
||||
fan-tach-ch = /bits/ 8 <0x02 0x03>;
|
||||
};
|
||||
fan@2 {
|
||||
reg = <0x02>;
|
||||
fan-tach-ch = /bits/ 8 <0x04>;
|
||||
};
|
||||
|
||||
};
|
||||
@@ -16,6 +16,11 @@ Supported chips:
|
||||
Prefixes: 'max34446'
|
||||
Addresses scanned: -
|
||||
Datasheet: http://datasheets.maximintegrated.com/en/ds/MAX34446.pdf
|
||||
* Maxim MAX34451
|
||||
PMBus 16-Channel V/I Monitor and 12-Channel Sequencer/Marginer
|
||||
Prefixes: 'max34451'
|
||||
Addresses scanned: -
|
||||
Datasheet: http://datasheets.maximintegrated.com/en/ds/MAX34451.pdf
|
||||
* Maxim MAX34460
|
||||
PMBus 12-Channel Voltage Monitor & Sequencer
|
||||
Prefix: 'max34460'
|
||||
@@ -36,9 +41,10 @@ Description
|
||||
This driver supports hardware monitoring for Maxim MAX34440 PMBus 6-Channel
|
||||
Power-Supply Manager, MAX34441 PMBus 5-Channel Power-Supply Manager
|
||||
and Intelligent Fan Controller, and MAX34446 PMBus Power-Supply Data Logger.
|
||||
It also supports the MAX34460 and MAX34461 PMBus Voltage Monitor & Sequencers.
|
||||
The MAX34460 supports 12 voltage channels, and the MAX34461 supports 16 voltage
|
||||
channels.
|
||||
It also supports the MAX34451, MAX34460, and MAX34461 PMBus Voltage Monitor &
|
||||
Sequencers. The MAX34451 supports monitoring voltage or current of 12 channels
|
||||
based on GIN pins. The MAX34460 supports 12 voltage channels, and the MAX34461
|
||||
supports 16 voltage channels.
|
||||
|
||||
The driver is a client driver to the core PMBus driver. Please see
|
||||
Documentation/hwmon/pmbus for details on PMBus client drivers.
|
||||
@@ -93,7 +99,7 @@ curr[1-6]_max Maximum current. From IOUT_OC_WARN_LIMIT register.
|
||||
curr[1-6]_crit Critical maximum current. From IOUT_OC_FAULT_LIMIT register.
|
||||
curr[1-6]_max_alarm Current high alarm. From IOUT_OC_WARNING status.
|
||||
curr[1-6]_crit_alarm Current critical high alarm. From IOUT_OC_FAULT status.
|
||||
curr[1-4]_average Historical average current (MAX34446 only).
|
||||
curr[1-4]_average Historical average current (MAX34446/34451 only).
|
||||
curr[1-6]_highest Historical maximum current.
|
||||
curr[1-6]_reset_history Write any value to reset history.
|
||||
|
||||
@@ -123,5 +129,7 @@ temp[1-8]_reset_history Write any value to reset history.
|
||||
temp7 and temp8 attributes only exist for MAX34440.
|
||||
MAX34446 only supports temp[1-3].
|
||||
|
||||
MAX34451 supports attribute groups in[1-16] (or curr[1-16] based on input pins)
|
||||
and temp[1-5].
|
||||
MAX34460 supports attribute groups in[1-12] and temp[1-5].
|
||||
MAX34461 supports attribute groups in[1-16] and temp[1-5].
|
||||
|
||||
60
Documentation/hwmon/mlxreg-fan
Normal file
60
Documentation/hwmon/mlxreg-fan
Normal file
@@ -0,0 +1,60 @@
|
||||
Kernel driver mlxreg-fan
|
||||
========================
|
||||
|
||||
Provides FAN control for the next Mellanox systems:
|
||||
QMB700, equipped with 40x200GbE InfiniBand ports;
|
||||
MSN3700, equipped with 32x200GbE or 16x400GbE Ethernet ports;
|
||||
MSN3410, equipped with 6x400GbE plus 48x50GbE Ethernet ports;
|
||||
MSN3800, equipped with 64x1000GbE Ethernet ports;
|
||||
These are the Top of the Rack systems, equipped with Mellanox switch
|
||||
board with Mellanox Quantum or Spectrume-2 devices.
|
||||
FAN controller is implemented by the programmable device logic.
|
||||
|
||||
The default registers offsets set within the programmable device is as
|
||||
following:
|
||||
- pwm1 0xe3
|
||||
- fan1 (tacho1) 0xe4
|
||||
- fan2 (tacho2) 0xe5
|
||||
- fan3 (tacho3) 0xe6
|
||||
- fan4 (tacho4) 0xe7
|
||||
- fan5 (tacho5) 0xe8
|
||||
- fan6 (tacho6) 0xe9
|
||||
- fan7 (tacho7) 0xea
|
||||
- fan8 (tacho8) 0xeb
|
||||
- fan9 (tacho9) 0xec
|
||||
- fan10 (tacho10) 0xed
|
||||
- fan11 (tacho11) 0xee
|
||||
- fan12 (tacho12) 0xef
|
||||
This setup can be re-programmed with other registers.
|
||||
|
||||
Author: Vadim Pasternak <vadimp@mellanox.com>
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
The driver implements a simple interface for driving a fan connected to
|
||||
a PWM output and tachometer inputs.
|
||||
This driver obtains PWM and tachometers registers location according to
|
||||
the system configuration and creates FAN/PWM hwmon objects and a cooling
|
||||
device. PWM and tachometers are sensed through the on-board programmable
|
||||
device, which exports its register map. This device could be attached to
|
||||
any bus type, for which register mapping is supported.
|
||||
Single instance is created with one PWM control, up to 12 tachometers and
|
||||
one cooling device. It could be as many instances as programmable device
|
||||
supports.
|
||||
The driver exposes the fan to the user space through the hwmon's and
|
||||
thermal's sysfs interfaces.
|
||||
|
||||
/sys files in hwmon subsystem
|
||||
-----------------------------
|
||||
|
||||
fan[1-12]_fault - RO files for tachometers TACH1-TACH12 fault indication
|
||||
fan[1-12]_input - RO files for tachometers TACH1-TACH12 input (in RPM)
|
||||
pwm1 - RW file for fan[1-12] target duty cycle (0..255)
|
||||
|
||||
/sys files in thermal subsystem
|
||||
-------------------------------
|
||||
|
||||
cur_state - RW file for current cooling state of the cooling device
|
||||
(0..max_state)
|
||||
max_state - RO file for maximum cooling state of the cooling device
|
||||
22
Documentation/hwmon/npcm750-pwm-fan
Normal file
22
Documentation/hwmon/npcm750-pwm-fan
Normal file
@@ -0,0 +1,22 @@
|
||||
Kernel driver npcm750-pwm-fan
|
||||
=============================
|
||||
|
||||
Supported chips:
|
||||
NUVOTON NPCM750/730/715/705
|
||||
|
||||
Authors:
|
||||
<tomer.maimon@nuvoton.com>
|
||||
|
||||
Description:
|
||||
------------
|
||||
This driver implements support for NUVOTON NPCM7XX PWM and Fan Tacho
|
||||
controller. The PWM controller supports up to 8 PWM outputs. The Fan tacho
|
||||
controller supports up to 16 tachometer inputs.
|
||||
|
||||
The driver provides the following sensor accesses in sysfs:
|
||||
|
||||
fanX_input ro provide current fan rotation value in RPM as reported
|
||||
by the fan to the device.
|
||||
|
||||
pwmX rw get or set PWM fan control value. This is an integer
|
||||
value between 0(off) and 255(full speed).
|
||||
@@ -171,6 +171,13 @@ in[0-*]_label Suggested voltage channel label.
|
||||
user-space.
|
||||
RO
|
||||
|
||||
in[0-*]_enable
|
||||
Enable or disable the sensors.
|
||||
When disabled the sensor read will return -ENODATA.
|
||||
1: Enable
|
||||
0: Disable
|
||||
RW
|
||||
|
||||
cpu[0-*]_vid CPU core reference voltage.
|
||||
Unit: millivolt
|
||||
RO
|
||||
@@ -236,6 +243,13 @@ fan[1-*]_label Suggested fan channel label.
|
||||
In all other cases, the label is provided by user-space.
|
||||
RO
|
||||
|
||||
fan[1-*]_enable
|
||||
Enable or disable the sensors.
|
||||
When disabled the sensor read will return -ENODATA.
|
||||
1: Enable
|
||||
0: Disable
|
||||
RW
|
||||
|
||||
Also see the Alarms section for status flags associated with fans.
|
||||
|
||||
|
||||
@@ -409,6 +423,13 @@ temp_reset_history
|
||||
Reset temp_lowest and temp_highest for all sensors
|
||||
WO
|
||||
|
||||
temp[1-*]_enable
|
||||
Enable or disable the sensors.
|
||||
When disabled the sensor read will return -ENODATA.
|
||||
1: Enable
|
||||
0: Disable
|
||||
RW
|
||||
|
||||
Some chips measure temperature using external thermistors and an ADC, and
|
||||
report the temperature measurement as a voltage. Converting this voltage
|
||||
back to a temperature (or the other way around for limits) requires
|
||||
@@ -468,6 +489,13 @@ curr_reset_history
|
||||
Reset currX_lowest and currX_highest for all sensors
|
||||
WO
|
||||
|
||||
curr[1-*]_enable
|
||||
Enable or disable the sensors.
|
||||
When disabled the sensor read will return -ENODATA.
|
||||
1: Enable
|
||||
0: Disable
|
||||
RW
|
||||
|
||||
Also see the Alarms section for status flags associated with currents.
|
||||
|
||||
*********
|
||||
@@ -566,6 +594,13 @@ power[1-*]_crit Critical maximum power.
|
||||
Unit: microWatt
|
||||
RW
|
||||
|
||||
power[1-*]_enable Enable or disable the sensors.
|
||||
When disabled the sensor read will return
|
||||
-ENODATA.
|
||||
1: Enable
|
||||
0: Disable
|
||||
RW
|
||||
|
||||
Also see the Alarms section for status flags associated with power readings.
|
||||
|
||||
**********
|
||||
@@ -576,6 +611,12 @@ energy[1-*]_input Cumulative energy use
|
||||
Unit: microJoule
|
||||
RO
|
||||
|
||||
energy[1-*]_enable Enable or disable the sensors.
|
||||
When disabled the sensor read will return
|
||||
-ENODATA.
|
||||
1: Enable
|
||||
0: Disable
|
||||
RW
|
||||
|
||||
************
|
||||
* Humidity *
|
||||
@@ -586,6 +627,13 @@ humidity[1-*]_input Humidity
|
||||
RO
|
||||
|
||||
|
||||
humidity[1-*]_enable Enable or disable the sensors
|
||||
When disabled the sensor read will return
|
||||
-ENODATA.
|
||||
1: Enable
|
||||
0: Disable
|
||||
RW
|
||||
|
||||
**********
|
||||
* Alarms *
|
||||
**********
|
||||
|
||||
@@ -937,6 +937,18 @@ config SENSORS_MCP3021
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called mcp3021.
|
||||
|
||||
config SENSORS_MLXREG_FAN
|
||||
tristate "Mellanox Mellanox FAN driver"
|
||||
depends on MELLANOX_PLATFORM
|
||||
imply THERMAL
|
||||
select REGMAP
|
||||
help
|
||||
This option enables support for the FAN control on the Mellanox
|
||||
Ethernet and InfiniBand switches. The driver can be activated by the
|
||||
platform device add call. Say Y to enable these. To compile this
|
||||
driver as a module, choose 'M' here: the module will be called
|
||||
mlxreg-fan.
|
||||
|
||||
config SENSORS_TC654
|
||||
tristate "Microchip TC654/TC655 and compatibles"
|
||||
depends on I2C
|
||||
@@ -1256,6 +1268,16 @@ config SENSORS_NCT7904
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called nct7904.
|
||||
|
||||
config SENSORS_NPCM7XX
|
||||
tristate "Nuvoton NPCM750 and compatible PWM and Fan controllers"
|
||||
imply THERMAL
|
||||
help
|
||||
This driver provides support for Nuvoton NPCM750/730/715/705 PWM
|
||||
and Fan controllers.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called npcm750-pwm-fan.
|
||||
|
||||
config SENSORS_NSA320
|
||||
tristate "ZyXEL NSA320 and compatible fan speed and temperature sensors"
|
||||
depends on GPIOLIB && OF
|
||||
|
||||
@@ -129,11 +129,13 @@ obj-$(CONFIG_SENSORS_MAX31790) += max31790.o
|
||||
obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
|
||||
obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o
|
||||
obj-$(CONFIG_SENSORS_TC654) += tc654.o
|
||||
obj-$(CONFIG_SENSORS_MLXREG_FAN) += mlxreg-fan.o
|
||||
obj-$(CONFIG_SENSORS_MENF21BMC_HWMON) += menf21bmc_hwmon.o
|
||||
obj-$(CONFIG_SENSORS_NCT6683) += nct6683.o
|
||||
obj-$(CONFIG_SENSORS_NCT6775) += nct6775.o
|
||||
obj-$(CONFIG_SENSORS_NCT7802) += nct7802.o
|
||||
obj-$(CONFIG_SENSORS_NCT7904) += nct7904.o
|
||||
obj-$(CONFIG_SENSORS_NPCM7XX) += npcm750-pwm-fan.o
|
||||
obj-$(CONFIG_SENSORS_NSA320) += nsa320-hwmon.o
|
||||
obj-$(CONFIG_SENSORS_NTC_THERMISTOR) += ntc_thermistor.o
|
||||
obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
|
||||
|
||||
@@ -194,8 +194,7 @@ struct adt7475_data {
|
||||
struct mutex lock;
|
||||
|
||||
unsigned long measure_updated;
|
||||
unsigned long limits_updated;
|
||||
char valid;
|
||||
bool valid;
|
||||
|
||||
u8 config4;
|
||||
u8 config5;
|
||||
@@ -326,6 +325,9 @@ static ssize_t show_voltage(struct device *dev, struct device_attribute *attr,
|
||||
struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
|
||||
unsigned short val;
|
||||
|
||||
if (IS_ERR(data))
|
||||
return PTR_ERR(data);
|
||||
|
||||
switch (sattr->nr) {
|
||||
case ALARM:
|
||||
return sprintf(buf, "%d\n",
|
||||
@@ -381,6 +383,9 @@ static ssize_t show_temp(struct device *dev, struct device_attribute *attr,
|
||||
struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
|
||||
int out;
|
||||
|
||||
if (IS_ERR(data))
|
||||
return PTR_ERR(data);
|
||||
|
||||
switch (sattr->nr) {
|
||||
case HYSTERSIS:
|
||||
mutex_lock(&data->lock);
|
||||
@@ -625,6 +630,9 @@ static ssize_t show_point2(struct device *dev, struct device_attribute *attr,
|
||||
struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
|
||||
int out, val;
|
||||
|
||||
if (IS_ERR(data))
|
||||
return PTR_ERR(data);
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
out = (data->range[sattr->index] >> 4) & 0x0F;
|
||||
val = reg2temp(data, data->temp[AUTOMIN][sattr->index]);
|
||||
@@ -683,6 +691,9 @@ static ssize_t show_tach(struct device *dev, struct device_attribute *attr,
|
||||
struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
|
||||
int out;
|
||||
|
||||
if (IS_ERR(data))
|
||||
return PTR_ERR(data);
|
||||
|
||||
if (sattr->nr == ALARM)
|
||||
out = (data->alarms >> (sattr->index + 10)) & 1;
|
||||
else
|
||||
@@ -720,6 +731,9 @@ static ssize_t show_pwm(struct device *dev, struct device_attribute *attr,
|
||||
struct adt7475_data *data = adt7475_update_device(dev);
|
||||
struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
|
||||
|
||||
if (IS_ERR(data))
|
||||
return PTR_ERR(data);
|
||||
|
||||
return sprintf(buf, "%d\n", data->pwm[sattr->nr][sattr->index]);
|
||||
}
|
||||
|
||||
@@ -729,6 +743,9 @@ static ssize_t show_pwmchan(struct device *dev, struct device_attribute *attr,
|
||||
struct adt7475_data *data = adt7475_update_device(dev);
|
||||
struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
|
||||
|
||||
if (IS_ERR(data))
|
||||
return PTR_ERR(data);
|
||||
|
||||
return sprintf(buf, "%d\n", data->pwmchan[sattr->index]);
|
||||
}
|
||||
|
||||
@@ -738,6 +755,9 @@ static ssize_t show_pwmctrl(struct device *dev, struct device_attribute *attr,
|
||||
struct adt7475_data *data = adt7475_update_device(dev);
|
||||
struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
|
||||
|
||||
if (IS_ERR(data))
|
||||
return PTR_ERR(data);
|
||||
|
||||
return sprintf(buf, "%d\n", data->pwmctl[sattr->index]);
|
||||
}
|
||||
|
||||
@@ -945,6 +965,9 @@ static ssize_t show_pwmfreq(struct device *dev, struct device_attribute *attr,
|
||||
int i = clamp_val(data->range[sattr->index] & 0xf, 0,
|
||||
ARRAY_SIZE(pwmfreq_table) - 1);
|
||||
|
||||
if (IS_ERR(data))
|
||||
return PTR_ERR(data);
|
||||
|
||||
return sprintf(buf, "%d\n", pwmfreq_table[i]);
|
||||
}
|
||||
|
||||
@@ -1035,6 +1058,10 @@ static ssize_t cpu0_vid_show(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
struct adt7475_data *data = adt7475_update_device(dev);
|
||||
|
||||
if (IS_ERR(data))
|
||||
return PTR_ERR(data);
|
||||
|
||||
return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm));
|
||||
}
|
||||
|
||||
@@ -1385,6 +1412,121 @@ static void adt7475_remove_files(struct i2c_client *client,
|
||||
sysfs_remove_group(&client->dev.kobj, &vid_attr_group);
|
||||
}
|
||||
|
||||
static int adt7475_update_limits(struct i2c_client *client)
|
||||
{
|
||||
struct adt7475_data *data = i2c_get_clientdata(client);
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
ret = adt7475_read(REG_CONFIG4);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
data->config4 = ret;
|
||||
|
||||
ret = adt7475_read(REG_CONFIG5);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
data->config5 = ret;
|
||||
|
||||
for (i = 0; i < ADT7475_VOLTAGE_COUNT; i++) {
|
||||
if (!(data->has_voltage & (1 << i)))
|
||||
continue;
|
||||
/* Adjust values so they match the input precision */
|
||||
ret = adt7475_read(VOLTAGE_MIN_REG(i));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
data->voltage[MIN][i] = ret << 2;
|
||||
|
||||
ret = adt7475_read(VOLTAGE_MAX_REG(i));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
data->voltage[MAX][i] = ret << 2;
|
||||
}
|
||||
|
||||
if (data->has_voltage & (1 << 5)) {
|
||||
ret = adt7475_read(REG_VTT_MIN);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
data->voltage[MIN][5] = ret << 2;
|
||||
|
||||
ret = adt7475_read(REG_VTT_MAX);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
data->voltage[MAX][5] = ret << 2;
|
||||
}
|
||||
|
||||
for (i = 0; i < ADT7475_TEMP_COUNT; i++) {
|
||||
/* Adjust values so they match the input precision */
|
||||
ret = adt7475_read(TEMP_MIN_REG(i));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
data->temp[MIN][i] = ret << 2;
|
||||
|
||||
ret = adt7475_read(TEMP_MAX_REG(i));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
data->temp[MAX][i] = ret << 2;
|
||||
|
||||
ret = adt7475_read(TEMP_TMIN_REG(i));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
data->temp[AUTOMIN][i] = ret << 2;
|
||||
|
||||
ret = adt7475_read(TEMP_THERM_REG(i));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
data->temp[THERM][i] = ret << 2;
|
||||
|
||||
ret = adt7475_read(TEMP_OFFSET_REG(i));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
data->temp[OFFSET][i] = ret;
|
||||
}
|
||||
adt7475_read_hystersis(client);
|
||||
|
||||
for (i = 0; i < ADT7475_TACH_COUNT; i++) {
|
||||
if (i == 3 && !data->has_fan4)
|
||||
continue;
|
||||
ret = adt7475_read_word(client, TACH_MIN_REG(i));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
data->tach[MIN][i] = ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < ADT7475_PWM_COUNT; i++) {
|
||||
if (i == 1 && !data->has_pwm2)
|
||||
continue;
|
||||
ret = adt7475_read(PWM_MAX_REG(i));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
data->pwm[MAX][i] = ret;
|
||||
|
||||
ret = adt7475_read(PWM_MIN_REG(i));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
data->pwm[MIN][i] = ret;
|
||||
/* Set the channel and control information */
|
||||
adt7475_read_pwm(client, i);
|
||||
}
|
||||
|
||||
ret = adt7475_read(TEMP_TRANGE_REG(0));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
data->range[0] = ret;
|
||||
|
||||
ret = adt7475_read(TEMP_TRANGE_REG(1));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
data->range[1] = ret;
|
||||
|
||||
ret = adt7475_read(TEMP_TRANGE_REG(2));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
data->range[2] = ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adt7475_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
@@ -1562,6 +1704,11 @@ static int adt7475_probe(struct i2c_client *client,
|
||||
(data->bypass_attn & (1 << 3)) ? " in3" : "",
|
||||
(data->bypass_attn & (1 << 4)) ? " in4" : "");
|
||||
|
||||
/* Limits and settings, should never change update more than once */
|
||||
ret = adt7475_update_limits(client);
|
||||
if (ret)
|
||||
goto eremove;
|
||||
|
||||
return 0;
|
||||
|
||||
eremove:
|
||||
@@ -1658,121 +1805,122 @@ static void adt7475_read_pwm(struct i2c_client *client, int index)
|
||||
}
|
||||
}
|
||||
|
||||
static struct adt7475_data *adt7475_update_device(struct device *dev)
|
||||
static int adt7475_update_measure(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct adt7475_data *data = i2c_get_clientdata(client);
|
||||
u16 ext;
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
ret = adt7475_read(REG_STATUS2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
data->alarms = ret << 8;
|
||||
|
||||
ret = adt7475_read(REG_STATUS1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
data->alarms |= ret;
|
||||
|
||||
ret = adt7475_read(REG_EXTEND2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ext = (ret << 8);
|
||||
|
||||
ret = adt7475_read(REG_EXTEND1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ext |= ret;
|
||||
|
||||
for (i = 0; i < ADT7475_VOLTAGE_COUNT; i++) {
|
||||
if (!(data->has_voltage & (1 << i)))
|
||||
continue;
|
||||
ret = adt7475_read(VOLTAGE_REG(i));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
data->voltage[INPUT][i] =
|
||||
(ret << 2) |
|
||||
((ext >> (i * 2)) & 3);
|
||||
}
|
||||
|
||||
for (i = 0; i < ADT7475_TEMP_COUNT; i++) {
|
||||
ret = adt7475_read(TEMP_REG(i));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
data->temp[INPUT][i] =
|
||||
(ret << 2) |
|
||||
((ext >> ((i + 5) * 2)) & 3);
|
||||
}
|
||||
|
||||
if (data->has_voltage & (1 << 5)) {
|
||||
ret = adt7475_read(REG_STATUS4);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
data->alarms |= ret << 24;
|
||||
|
||||
ret = adt7475_read(REG_EXTEND3);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ext = ret;
|
||||
|
||||
ret = adt7475_read(REG_VTT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
data->voltage[INPUT][5] = ret << 2 |
|
||||
((ext >> 4) & 3);
|
||||
}
|
||||
|
||||
for (i = 0; i < ADT7475_TACH_COUNT; i++) {
|
||||
if (i == 3 && !data->has_fan4)
|
||||
continue;
|
||||
ret = adt7475_read_word(client, TACH_REG(i));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
data->tach[INPUT][i] = ret;
|
||||
}
|
||||
|
||||
/* Updated by hw when in auto mode */
|
||||
for (i = 0; i < ADT7475_PWM_COUNT; i++) {
|
||||
if (i == 1 && !data->has_pwm2)
|
||||
continue;
|
||||
ret = adt7475_read(PWM_REG(i));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
data->pwm[INPUT][i] = ret;
|
||||
}
|
||||
|
||||
if (data->has_vid) {
|
||||
ret = adt7475_read(REG_VID);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
data->vid = ret & 0x3f;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct adt7475_data *adt7475_update_device(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct adt7475_data *data = i2c_get_clientdata(client);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
|
||||
/* Measurement values update every 2 seconds */
|
||||
if (time_after(jiffies, data->measure_updated + HZ * 2) ||
|
||||
!data->valid) {
|
||||
data->alarms = adt7475_read(REG_STATUS2) << 8;
|
||||
data->alarms |= adt7475_read(REG_STATUS1);
|
||||
|
||||
ext = (adt7475_read(REG_EXTEND2) << 8) |
|
||||
adt7475_read(REG_EXTEND1);
|
||||
for (i = 0; i < ADT7475_VOLTAGE_COUNT; i++) {
|
||||
if (!(data->has_voltage & (1 << i)))
|
||||
continue;
|
||||
data->voltage[INPUT][i] =
|
||||
(adt7475_read(VOLTAGE_REG(i)) << 2) |
|
||||
((ext >> (i * 2)) & 3);
|
||||
ret = adt7475_update_measure(dev);
|
||||
if (ret) {
|
||||
data->valid = false;
|
||||
mutex_unlock(&data->lock);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
for (i = 0; i < ADT7475_TEMP_COUNT; i++)
|
||||
data->temp[INPUT][i] =
|
||||
(adt7475_read(TEMP_REG(i)) << 2) |
|
||||
((ext >> ((i + 5) * 2)) & 3);
|
||||
|
||||
if (data->has_voltage & (1 << 5)) {
|
||||
data->alarms |= adt7475_read(REG_STATUS4) << 24;
|
||||
ext = adt7475_read(REG_EXTEND3);
|
||||
data->voltage[INPUT][5] = adt7475_read(REG_VTT) << 2 |
|
||||
((ext >> 4) & 3);
|
||||
}
|
||||
|
||||
for (i = 0; i < ADT7475_TACH_COUNT; i++) {
|
||||
if (i == 3 && !data->has_fan4)
|
||||
continue;
|
||||
data->tach[INPUT][i] =
|
||||
adt7475_read_word(client, TACH_REG(i));
|
||||
}
|
||||
|
||||
/* Updated by hw when in auto mode */
|
||||
for (i = 0; i < ADT7475_PWM_COUNT; i++) {
|
||||
if (i == 1 && !data->has_pwm2)
|
||||
continue;
|
||||
data->pwm[INPUT][i] = adt7475_read(PWM_REG(i));
|
||||
}
|
||||
|
||||
if (data->has_vid)
|
||||
data->vid = adt7475_read(REG_VID) & 0x3f;
|
||||
|
||||
data->measure_updated = jiffies;
|
||||
}
|
||||
|
||||
/* Limits and settings, should never change update every 60 seconds */
|
||||
if (time_after(jiffies, data->limits_updated + HZ * 60) ||
|
||||
!data->valid) {
|
||||
data->config4 = adt7475_read(REG_CONFIG4);
|
||||
data->config5 = adt7475_read(REG_CONFIG5);
|
||||
|
||||
for (i = 0; i < ADT7475_VOLTAGE_COUNT; i++) {
|
||||
if (!(data->has_voltage & (1 << i)))
|
||||
continue;
|
||||
/* Adjust values so they match the input precision */
|
||||
data->voltage[MIN][i] =
|
||||
adt7475_read(VOLTAGE_MIN_REG(i)) << 2;
|
||||
data->voltage[MAX][i] =
|
||||
adt7475_read(VOLTAGE_MAX_REG(i)) << 2;
|
||||
}
|
||||
|
||||
if (data->has_voltage & (1 << 5)) {
|
||||
data->voltage[MIN][5] = adt7475_read(REG_VTT_MIN) << 2;
|
||||
data->voltage[MAX][5] = adt7475_read(REG_VTT_MAX) << 2;
|
||||
}
|
||||
|
||||
for (i = 0; i < ADT7475_TEMP_COUNT; i++) {
|
||||
/* Adjust values so they match the input precision */
|
||||
data->temp[MIN][i] =
|
||||
adt7475_read(TEMP_MIN_REG(i)) << 2;
|
||||
data->temp[MAX][i] =
|
||||
adt7475_read(TEMP_MAX_REG(i)) << 2;
|
||||
data->temp[AUTOMIN][i] =
|
||||
adt7475_read(TEMP_TMIN_REG(i)) << 2;
|
||||
data->temp[THERM][i] =
|
||||
adt7475_read(TEMP_THERM_REG(i)) << 2;
|
||||
data->temp[OFFSET][i] =
|
||||
adt7475_read(TEMP_OFFSET_REG(i));
|
||||
}
|
||||
adt7475_read_hystersis(client);
|
||||
|
||||
for (i = 0; i < ADT7475_TACH_COUNT; i++) {
|
||||
if (i == 3 && !data->has_fan4)
|
||||
continue;
|
||||
data->tach[MIN][i] =
|
||||
adt7475_read_word(client, TACH_MIN_REG(i));
|
||||
}
|
||||
|
||||
for (i = 0; i < ADT7475_PWM_COUNT; i++) {
|
||||
if (i == 1 && !data->has_pwm2)
|
||||
continue;
|
||||
data->pwm[MAX][i] = adt7475_read(PWM_MAX_REG(i));
|
||||
data->pwm[MIN][i] = adt7475_read(PWM_MIN_REG(i));
|
||||
/* Set the channel and control information */
|
||||
adt7475_read_pwm(client, i);
|
||||
}
|
||||
|
||||
data->range[0] = adt7475_read(TEMP_TRANGE_REG(0));
|
||||
data->range[1] = adt7475_read(TEMP_TRANGE_REG(1));
|
||||
data->range[2] = adt7475_read(TEMP_TRANGE_REG(2));
|
||||
|
||||
data->limits_updated = jiffies;
|
||||
data->valid = 1;
|
||||
data->valid = true;
|
||||
}
|
||||
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
@@ -443,8 +443,10 @@ static int emc1403_probe(struct i2c_client *client,
|
||||
switch (id->driver_data) {
|
||||
case emc1404:
|
||||
data->groups[2] = &emc1404_group;
|
||||
/* fall through */
|
||||
case emc1403:
|
||||
data->groups[1] = &emc1403_group;
|
||||
/* fall through */
|
||||
case emc1402:
|
||||
data->groups[0] = &emc1402_group;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
* struct iio_hwmon_state - device instance state
|
||||
* @channels: filled with array of channels from iio
|
||||
* @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.
|
||||
@@ -30,7 +29,6 @@
|
||||
struct iio_hwmon_state {
|
||||
struct iio_channel *channels;
|
||||
int num_channels;
|
||||
struct device *hwmon_dev;
|
||||
struct attribute_group attr_group;
|
||||
const struct attribute_group *groups[2];
|
||||
struct attribute **attrs;
|
||||
@@ -68,12 +66,13 @@ static int iio_hwmon_probe(struct platform_device *pdev)
|
||||
enum iio_chan_type type;
|
||||
struct iio_channel *channels;
|
||||
const char *name = "iio_hwmon";
|
||||
struct device *hwmon_dev;
|
||||
char *sname;
|
||||
|
||||
if (dev->of_node && dev->of_node->name)
|
||||
name = dev->of_node->name;
|
||||
|
||||
channels = iio_channel_get_all(dev);
|
||||
channels = devm_iio_channel_get_all(dev);
|
||||
if (IS_ERR(channels)) {
|
||||
if (PTR_ERR(channels) == -ENODEV)
|
||||
return -EPROBE_DEFER;
|
||||
@@ -81,10 +80,8 @@ static int iio_hwmon_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
|
||||
if (st == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error_release_channels;
|
||||
}
|
||||
if (st == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
st->channels = channels;
|
||||
|
||||
@@ -95,22 +92,18 @@ static int iio_hwmon_probe(struct platform_device *pdev)
|
||||
st->attrs = devm_kcalloc(dev,
|
||||
st->num_channels + 1, sizeof(*st->attrs),
|
||||
GFP_KERNEL);
|
||||
if (st->attrs == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error_release_channels;
|
||||
}
|
||||
if (st->attrs == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < st->num_channels; i++) {
|
||||
a = devm_kzalloc(dev, sizeof(*a), GFP_KERNEL);
|
||||
if (a == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error_release_channels;
|
||||
}
|
||||
if (a == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
sysfs_attr_init(&a->dev_attr.attr);
|
||||
ret = iio_get_channel_type(&st->channels[i], &type);
|
||||
if (ret < 0)
|
||||
goto error_release_channels;
|
||||
return ret;
|
||||
|
||||
switch (type) {
|
||||
case IIO_VOLTAGE:
|
||||
@@ -134,13 +127,11 @@ static int iio_hwmon_probe(struct platform_device *pdev)
|
||||
humidity_i++);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
goto error_release_channels;
|
||||
}
|
||||
if (a->dev_attr.attr.name == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error_release_channels;
|
||||
return -EINVAL;
|
||||
}
|
||||
if (a->dev_attr.attr.name == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
a->dev_attr.show = iio_hwmon_read_val;
|
||||
a->dev_attr.attr.mode = S_IRUGO;
|
||||
a->index = i;
|
||||
@@ -151,34 +142,13 @@ static int iio_hwmon_probe(struct platform_device *pdev)
|
||||
st->groups[0] = &st->attr_group;
|
||||
|
||||
sname = devm_kstrdup(dev, name, GFP_KERNEL);
|
||||
if (!sname) {
|
||||
ret = -ENOMEM;
|
||||
goto error_release_channels;
|
||||
}
|
||||
if (!sname)
|
||||
return -ENOMEM;
|
||||
|
||||
strreplace(sname, '-', '_');
|
||||
st->hwmon_dev = hwmon_device_register_with_groups(dev, sname, st,
|
||||
st->groups);
|
||||
if (IS_ERR(st->hwmon_dev)) {
|
||||
ret = PTR_ERR(st->hwmon_dev);
|
||||
goto error_release_channels;
|
||||
}
|
||||
platform_set_drvdata(pdev, st);
|
||||
return 0;
|
||||
|
||||
error_release_channels:
|
||||
iio_channel_release_all(channels);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int iio_hwmon_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_hwmon_state *st = platform_get_drvdata(pdev);
|
||||
|
||||
hwmon_device_unregister(st->hwmon_dev);
|
||||
iio_channel_release_all(st->channels);
|
||||
|
||||
return 0;
|
||||
hwmon_dev = devm_hwmon_device_register_with_groups(dev, sname, st,
|
||||
st->groups);
|
||||
return PTR_ERR_OR_ZERO(hwmon_dev);
|
||||
}
|
||||
|
||||
static const struct of_device_id iio_hwmon_of_match[] = {
|
||||
@@ -193,7 +163,6 @@ static struct platform_driver __refdata iio_hwmon_driver = {
|
||||
.of_match_table = iio_hwmon_of_match,
|
||||
},
|
||||
.probe = iio_hwmon_probe,
|
||||
.remove = iio_hwmon_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(iio_hwmon_driver);
|
||||
|
||||
@@ -99,12 +99,8 @@ static const struct tctl_offset tctl_offset_table[] = {
|
||||
{ 0x17, "AMD Ryzen 7 1700X", 20000 },
|
||||
{ 0x17, "AMD Ryzen 7 1800X", 20000 },
|
||||
{ 0x17, "AMD Ryzen 7 2700X", 10000 },
|
||||
{ 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 },
|
||||
{ 0x17, "AMD Ryzen Threadripper 19", 27000 }, /* 19{00,20,50}X */
|
||||
{ 0x17, "AMD Ryzen Threadripper 29", 27000 }, /* 29{20,50,70,90}[W]X */
|
||||
};
|
||||
|
||||
static void read_htcreg_pci(struct pci_dev *pdev, u32 *regval)
|
||||
|
||||
489
drivers/hwmon/mlxreg-fan.c
Normal file
489
drivers/hwmon/mlxreg-fan.c
Normal file
@@ -0,0 +1,489 @@
|
||||
// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
|
||||
//
|
||||
// Copyright (c) 2018 Mellanox Technologies. All rights reserved.
|
||||
// Copyright (c) 2018 Vadim Pasternak <vadimp@mellanox.com>
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_data/mlxreg.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/thermal.h>
|
||||
|
||||
#define MLXREG_FAN_MAX_TACHO 12
|
||||
#define MLXREG_FAN_MAX_STATE 10
|
||||
#define MLXREG_FAN_MIN_DUTY 51 /* 20% */
|
||||
#define MLXREG_FAN_MAX_DUTY 255 /* 100% */
|
||||
/*
|
||||
* Minimum and maximum FAN allowed speed in percent: from 20% to 100%. Values
|
||||
* MLXREG_FAN_MAX_STATE + x, where x is between 2 and 10 are used for
|
||||
* setting FAN speed dynamic minimum. For example, if value is set to 14 (40%)
|
||||
* cooling levels vector will be set to 4, 4, 4, 4, 4, 5, 6, 7, 8, 9, 10 to
|
||||
* introduce PWM speed in percent: 40, 40, 40, 40, 40, 50, 60. 70, 80, 90, 100.
|
||||
*/
|
||||
#define MLXREG_FAN_SPEED_MIN (MLXREG_FAN_MAX_STATE + 2)
|
||||
#define MLXREG_FAN_SPEED_MAX (MLXREG_FAN_MAX_STATE * 2)
|
||||
#define MLXREG_FAN_SPEED_MIN_LEVEL 2 /* 20 percent */
|
||||
#define MLXREG_FAN_TACHO_SAMPLES_PER_PULSE_DEF 44
|
||||
#define MLXREG_FAN_TACHO_DIVIDER_DEF 1132
|
||||
/*
|
||||
* FAN datasheet defines the formula for RPM calculations as RPM = 15/t-high.
|
||||
* The logic in a programmable device measures the time t-high by sampling the
|
||||
* tachometer every t-sample (with the default value 11.32 uS) and increment
|
||||
* a counter (N) as long as the pulse has not change:
|
||||
* RPM = 15 / (t-sample * (K + Regval)), where:
|
||||
* Regval: is the value read from the programmable device register;
|
||||
* - 0xff - represents tachometer fault;
|
||||
* - 0xfe - represents tachometer minimum value , which is 4444 RPM;
|
||||
* - 0x00 - represents tachometer maximum value , which is 300000 RPM;
|
||||
* K: is 44 and it represents the minimum allowed samples per pulse;
|
||||
* N: is equal K + Regval;
|
||||
* In order to calculate RPM from the register value the following formula is
|
||||
* used: RPM = 15 / ((Regval + K) * 11.32) * 10^(-6)), which in the
|
||||
* default case is modified to:
|
||||
* RPM = 15000000 * 100 / ((Regval + 44) * 1132);
|
||||
* - for Regval 0x00, RPM will be 15000000 * 100 / (44 * 1132) = 30115;
|
||||
* - for Regval 0xfe, RPM will be 15000000 * 100 / ((254 + 44) * 1132) = 4446;
|
||||
* In common case the formula is modified to:
|
||||
* RPM = 15000000 * 100 / ((Regval + samples) * divider).
|
||||
*/
|
||||
#define MLXREG_FAN_GET_RPM(rval, d, s) (DIV_ROUND_CLOSEST(15000000 * 100, \
|
||||
((rval) + (s)) * (d)))
|
||||
#define MLXREG_FAN_GET_FAULT(val, mask) (!!((val) ^ (mask)))
|
||||
#define MLXREG_FAN_PWM_DUTY2STATE(duty) (DIV_ROUND_CLOSEST((duty) * \
|
||||
MLXREG_FAN_MAX_STATE, \
|
||||
MLXREG_FAN_MAX_DUTY))
|
||||
#define MLXREG_FAN_PWM_STATE2DUTY(stat) (DIV_ROUND_CLOSEST((stat) * \
|
||||
MLXREG_FAN_MAX_DUTY, \
|
||||
MLXREG_FAN_MAX_STATE))
|
||||
|
||||
/*
|
||||
* struct mlxreg_fan_tacho - tachometer data (internal use):
|
||||
*
|
||||
* @connected: indicates if tachometer is connected;
|
||||
* @reg: register offset;
|
||||
* @mask: fault mask;
|
||||
*/
|
||||
struct mlxreg_fan_tacho {
|
||||
bool connected;
|
||||
u32 reg;
|
||||
u32 mask;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct mlxreg_fan_pwm - PWM data (internal use):
|
||||
*
|
||||
* @connected: indicates if PWM is connected;
|
||||
* @reg: register offset;
|
||||
*/
|
||||
struct mlxreg_fan_pwm {
|
||||
bool connected;
|
||||
u32 reg;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct mlxreg_fan - private data (internal use):
|
||||
*
|
||||
* @dev: basic device;
|
||||
* @regmap: register map of parent device;
|
||||
* @tacho: tachometer data;
|
||||
* @pwm: PWM data;
|
||||
* @samples: minimum allowed samples per pulse;
|
||||
* @divider: divider value for tachometer RPM calculation;
|
||||
* @cooling: cooling device levels;
|
||||
* @cdev: cooling device;
|
||||
*/
|
||||
struct mlxreg_fan {
|
||||
struct device *dev;
|
||||
void *regmap;
|
||||
struct mlxreg_core_platform_data *pdata;
|
||||
struct mlxreg_fan_tacho tacho[MLXREG_FAN_MAX_TACHO];
|
||||
struct mlxreg_fan_pwm pwm;
|
||||
int samples;
|
||||
int divider;
|
||||
u8 cooling_levels[MLXREG_FAN_MAX_STATE + 1];
|
||||
struct thermal_cooling_device *cdev;
|
||||
};
|
||||
|
||||
static int
|
||||
mlxreg_fan_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
|
||||
int channel, long *val)
|
||||
{
|
||||
struct mlxreg_fan *fan = dev_get_drvdata(dev);
|
||||
struct mlxreg_fan_tacho *tacho;
|
||||
u32 regval;
|
||||
int err;
|
||||
|
||||
switch (type) {
|
||||
case hwmon_fan:
|
||||
tacho = &fan->tacho[channel];
|
||||
switch (attr) {
|
||||
case hwmon_fan_input:
|
||||
err = regmap_read(fan->regmap, tacho->reg, ®val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
*val = MLXREG_FAN_GET_RPM(regval, fan->divider,
|
||||
fan->samples);
|
||||
break;
|
||||
|
||||
case hwmon_fan_fault:
|
||||
err = regmap_read(fan->regmap, tacho->reg, ®val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
*val = MLXREG_FAN_GET_FAULT(regval, tacho->mask);
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
break;
|
||||
|
||||
case hwmon_pwm:
|
||||
switch (attr) {
|
||||
case hwmon_pwm_input:
|
||||
err = regmap_read(fan->regmap, fan->pwm.reg, ®val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
*val = regval;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mlxreg_fan_write(struct device *dev, enum hwmon_sensor_types type, u32 attr,
|
||||
int channel, long val)
|
||||
{
|
||||
struct mlxreg_fan *fan = dev_get_drvdata(dev);
|
||||
|
||||
switch (type) {
|
||||
case hwmon_pwm:
|
||||
switch (attr) {
|
||||
case hwmon_pwm_input:
|
||||
if (val < MLXREG_FAN_MIN_DUTY ||
|
||||
val > MLXREG_FAN_MAX_DUTY)
|
||||
return -EINVAL;
|
||||
return regmap_write(fan->regmap, fan->pwm.reg, val);
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static umode_t
|
||||
mlxreg_fan_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr,
|
||||
int channel)
|
||||
{
|
||||
switch (type) {
|
||||
case hwmon_fan:
|
||||
if (!(((struct mlxreg_fan *)data)->tacho[channel].connected))
|
||||
return 0;
|
||||
|
||||
switch (attr) {
|
||||
case hwmon_fan_input:
|
||||
case hwmon_fan_fault:
|
||||
return 0444;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case hwmon_pwm:
|
||||
if (!(((struct mlxreg_fan *)data)->pwm.connected))
|
||||
return 0;
|
||||
|
||||
switch (attr) {
|
||||
case hwmon_pwm_input:
|
||||
return 0644;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const u32 mlxreg_fan_hwmon_fan_config[] = {
|
||||
HWMON_F_INPUT | HWMON_F_FAULT,
|
||||
HWMON_F_INPUT | HWMON_F_FAULT,
|
||||
HWMON_F_INPUT | HWMON_F_FAULT,
|
||||
HWMON_F_INPUT | HWMON_F_FAULT,
|
||||
HWMON_F_INPUT | HWMON_F_FAULT,
|
||||
HWMON_F_INPUT | HWMON_F_FAULT,
|
||||
HWMON_F_INPUT | HWMON_F_FAULT,
|
||||
HWMON_F_INPUT | HWMON_F_FAULT,
|
||||
HWMON_F_INPUT | HWMON_F_FAULT,
|
||||
HWMON_F_INPUT | HWMON_F_FAULT,
|
||||
HWMON_F_INPUT | HWMON_F_FAULT,
|
||||
HWMON_F_INPUT | HWMON_F_FAULT,
|
||||
0
|
||||
};
|
||||
|
||||
static const struct hwmon_channel_info mlxreg_fan_hwmon_fan = {
|
||||
.type = hwmon_fan,
|
||||
.config = mlxreg_fan_hwmon_fan_config,
|
||||
};
|
||||
|
||||
static const u32 mlxreg_fan_hwmon_pwm_config[] = {
|
||||
HWMON_PWM_INPUT,
|
||||
0
|
||||
};
|
||||
|
||||
static const struct hwmon_channel_info mlxreg_fan_hwmon_pwm = {
|
||||
.type = hwmon_pwm,
|
||||
.config = mlxreg_fan_hwmon_pwm_config,
|
||||
};
|
||||
|
||||
static const struct hwmon_channel_info *mlxreg_fan_hwmon_info[] = {
|
||||
&mlxreg_fan_hwmon_fan,
|
||||
&mlxreg_fan_hwmon_pwm,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct hwmon_ops mlxreg_fan_hwmon_hwmon_ops = {
|
||||
.is_visible = mlxreg_fan_is_visible,
|
||||
.read = mlxreg_fan_read,
|
||||
.write = mlxreg_fan_write,
|
||||
};
|
||||
|
||||
static const struct hwmon_chip_info mlxreg_fan_hwmon_chip_info = {
|
||||
.ops = &mlxreg_fan_hwmon_hwmon_ops,
|
||||
.info = mlxreg_fan_hwmon_info,
|
||||
};
|
||||
|
||||
static int mlxreg_fan_get_max_state(struct thermal_cooling_device *cdev,
|
||||
unsigned long *state)
|
||||
{
|
||||
*state = MLXREG_FAN_MAX_STATE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mlxreg_fan_get_cur_state(struct thermal_cooling_device *cdev,
|
||||
unsigned long *state)
|
||||
|
||||
{
|
||||
struct mlxreg_fan *fan = cdev->devdata;
|
||||
u32 regval;
|
||||
int err;
|
||||
|
||||
err = regmap_read(fan->regmap, fan->pwm.reg, ®val);
|
||||
if (err) {
|
||||
dev_err(fan->dev, "Failed to query PWM duty\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
*state = MLXREG_FAN_PWM_DUTY2STATE(regval);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mlxreg_fan_set_cur_state(struct thermal_cooling_device *cdev,
|
||||
unsigned long state)
|
||||
|
||||
{
|
||||
struct mlxreg_fan *fan = cdev->devdata;
|
||||
unsigned long cur_state;
|
||||
u32 regval;
|
||||
int i;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* Verify if this request is for changing allowed FAN dynamical
|
||||
* minimum. If it is - update cooling levels accordingly and update
|
||||
* state, if current state is below the newly requested minimum state.
|
||||
* For example, if current state is 5, and minimal state is to be
|
||||
* changed from 4 to 6, fan->cooling_levels[0 to 5] will be changed all
|
||||
* from 4 to 6. And state 5 (fan->cooling_levels[4]) should be
|
||||
* overwritten.
|
||||
*/
|
||||
if (state >= MLXREG_FAN_SPEED_MIN && state <= MLXREG_FAN_SPEED_MAX) {
|
||||
state -= MLXREG_FAN_MAX_STATE;
|
||||
for (i = 0; i < state; i++)
|
||||
fan->cooling_levels[i] = state;
|
||||
for (i = state; i <= MLXREG_FAN_MAX_STATE; i++)
|
||||
fan->cooling_levels[i] = i;
|
||||
|
||||
err = regmap_read(fan->regmap, fan->pwm.reg, ®val);
|
||||
if (err) {
|
||||
dev_err(fan->dev, "Failed to query PWM duty\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
cur_state = MLXREG_FAN_PWM_DUTY2STATE(regval);
|
||||
if (state < cur_state)
|
||||
return 0;
|
||||
|
||||
state = cur_state;
|
||||
}
|
||||
|
||||
if (state > MLXREG_FAN_MAX_STATE)
|
||||
return -EINVAL;
|
||||
|
||||
/* Normalize the state to the valid speed range. */
|
||||
state = fan->cooling_levels[state];
|
||||
err = regmap_write(fan->regmap, fan->pwm.reg,
|
||||
MLXREG_FAN_PWM_STATE2DUTY(state));
|
||||
if (err) {
|
||||
dev_err(fan->dev, "Failed to write PWM duty\n");
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct thermal_cooling_device_ops mlxreg_fan_cooling_ops = {
|
||||
.get_max_state = mlxreg_fan_get_max_state,
|
||||
.get_cur_state = mlxreg_fan_get_cur_state,
|
||||
.set_cur_state = mlxreg_fan_set_cur_state,
|
||||
};
|
||||
|
||||
static int mlxreg_fan_config(struct mlxreg_fan *fan,
|
||||
struct mlxreg_core_platform_data *pdata)
|
||||
{
|
||||
struct mlxreg_core_data *data = pdata->data;
|
||||
bool configured = false;
|
||||
int tacho_num = 0, i;
|
||||
|
||||
fan->samples = MLXREG_FAN_TACHO_SAMPLES_PER_PULSE_DEF;
|
||||
fan->divider = MLXREG_FAN_TACHO_DIVIDER_DEF;
|
||||
for (i = 0; i < pdata->counter; i++, data++) {
|
||||
if (strnstr(data->label, "tacho", sizeof(data->label))) {
|
||||
if (tacho_num == MLXREG_FAN_MAX_TACHO) {
|
||||
dev_err(fan->dev, "too many tacho entries: %s\n",
|
||||
data->label);
|
||||
return -EINVAL;
|
||||
}
|
||||
fan->tacho[tacho_num].reg = data->reg;
|
||||
fan->tacho[tacho_num].mask = data->mask;
|
||||
fan->tacho[tacho_num++].connected = true;
|
||||
} else if (strnstr(data->label, "pwm", sizeof(data->label))) {
|
||||
if (fan->pwm.connected) {
|
||||
dev_err(fan->dev, "duplicate pwm entry: %s\n",
|
||||
data->label);
|
||||
return -EINVAL;
|
||||
}
|
||||
fan->pwm.reg = data->reg;
|
||||
fan->pwm.connected = true;
|
||||
} else if (strnstr(data->label, "conf", sizeof(data->label))) {
|
||||
if (configured) {
|
||||
dev_err(fan->dev, "duplicate conf entry: %s\n",
|
||||
data->label);
|
||||
return -EINVAL;
|
||||
}
|
||||
/* Validate that conf parameters are not zeros. */
|
||||
if (!data->mask || !data->bit) {
|
||||
dev_err(fan->dev, "invalid conf entry params: %s\n",
|
||||
data->label);
|
||||
return -EINVAL;
|
||||
}
|
||||
fan->samples = data->mask;
|
||||
fan->divider = data->bit;
|
||||
configured = true;
|
||||
} else {
|
||||
dev_err(fan->dev, "invalid label: %s\n", data->label);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Init cooling levels per PWM state. */
|
||||
for (i = 0; i < MLXREG_FAN_SPEED_MIN_LEVEL; i++)
|
||||
fan->cooling_levels[i] = MLXREG_FAN_SPEED_MIN_LEVEL;
|
||||
for (i = MLXREG_FAN_SPEED_MIN_LEVEL; i <= MLXREG_FAN_MAX_STATE; i++)
|
||||
fan->cooling_levels[i] = i;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mlxreg_fan_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct mlxreg_core_platform_data *pdata;
|
||||
struct mlxreg_fan *fan;
|
||||
struct device *hwm;
|
||||
int err;
|
||||
|
||||
pdata = dev_get_platdata(&pdev->dev);
|
||||
if (!pdata) {
|
||||
dev_err(&pdev->dev, "Failed to get platform data.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fan = devm_kzalloc(&pdev->dev, sizeof(*fan), GFP_KERNEL);
|
||||
if (!fan)
|
||||
return -ENOMEM;
|
||||
|
||||
fan->dev = &pdev->dev;
|
||||
fan->regmap = pdata->regmap;
|
||||
platform_set_drvdata(pdev, fan);
|
||||
|
||||
err = mlxreg_fan_config(fan, pdata);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
hwm = devm_hwmon_device_register_with_info(&pdev->dev, "mlxreg_fan",
|
||||
fan,
|
||||
&mlxreg_fan_hwmon_chip_info,
|
||||
NULL);
|
||||
if (IS_ERR(hwm)) {
|
||||
dev_err(&pdev->dev, "Failed to register hwmon device\n");
|
||||
return PTR_ERR(hwm);
|
||||
}
|
||||
|
||||
if (IS_REACHABLE(CONFIG_THERMAL)) {
|
||||
fan->cdev = thermal_cooling_device_register("mlxreg_fan", fan,
|
||||
&mlxreg_fan_cooling_ops);
|
||||
if (IS_ERR(fan->cdev)) {
|
||||
dev_err(&pdev->dev, "Failed to register cooling device\n");
|
||||
return PTR_ERR(fan->cdev);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mlxreg_fan_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mlxreg_fan *fan = platform_get_drvdata(pdev);
|
||||
|
||||
if (IS_REACHABLE(CONFIG_THERMAL))
|
||||
thermal_cooling_device_unregister(fan->cdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver mlxreg_fan_driver = {
|
||||
.driver = {
|
||||
.name = "mlxreg-fan",
|
||||
},
|
||||
.probe = mlxreg_fan_probe,
|
||||
.remove = mlxreg_fan_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(mlxreg_fan_driver);
|
||||
|
||||
MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>");
|
||||
MODULE_DESCRIPTION("Mellanox FAN driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:mlxreg-fan");
|
||||
@@ -1050,8 +1050,8 @@ struct nct6775_data {
|
||||
u64 beeps;
|
||||
|
||||
u8 pwm_num; /* number of pwm */
|
||||
u8 pwm_mode[NUM_FAN]; /* 1->DC variable voltage,
|
||||
* 0->PWM variable duty cycle
|
||||
u8 pwm_mode[NUM_FAN]; /* 0->DC variable voltage,
|
||||
* 1->PWM variable duty cycle
|
||||
*/
|
||||
enum pwm_enable pwm_enable[NUM_FAN];
|
||||
/* 0->off
|
||||
@@ -2541,7 +2541,7 @@ static void pwm_update_registers(struct nct6775_data *data, int nr)
|
||||
case thermal_cruise:
|
||||
nct6775_write_value(data, data->REG_TARGET[nr],
|
||||
data->target_temp[nr]);
|
||||
/* intentional */
|
||||
/* fall through */
|
||||
default:
|
||||
reg = nct6775_read_value(data, data->REG_FAN_MODE[nr]);
|
||||
reg = (reg & ~data->tolerance_mask) |
|
||||
|
||||
@@ -77,7 +77,7 @@ struct nct7904_data {
|
||||
};
|
||||
|
||||
/* Access functions */
|
||||
static int nct7904_bank_lock(struct nct7904_data *data, unsigned bank)
|
||||
static int nct7904_bank_lock(struct nct7904_data *data, unsigned int bank)
|
||||
{
|
||||
int ret;
|
||||
|
||||
@@ -99,7 +99,7 @@ static inline void nct7904_bank_release(struct nct7904_data *data)
|
||||
|
||||
/* Read 1-byte register. Returns unsigned reg or -ERRNO on error. */
|
||||
static int nct7904_read_reg(struct nct7904_data *data,
|
||||
unsigned bank, unsigned reg)
|
||||
unsigned int bank, unsigned int reg)
|
||||
{
|
||||
struct i2c_client *client = data->client;
|
||||
int ret;
|
||||
@@ -117,7 +117,7 @@ static int nct7904_read_reg(struct nct7904_data *data,
|
||||
* -ERRNO on error.
|
||||
*/
|
||||
static int nct7904_read_reg16(struct nct7904_data *data,
|
||||
unsigned bank, unsigned reg)
|
||||
unsigned int bank, unsigned int reg)
|
||||
{
|
||||
struct i2c_client *client = data->client;
|
||||
int ret, hi;
|
||||
@@ -139,7 +139,7 @@ static int nct7904_read_reg16(struct nct7904_data *data,
|
||||
|
||||
/* Write 1-byte register. Returns 0 or -ERRNO on error. */
|
||||
static int nct7904_write_reg(struct nct7904_data *data,
|
||||
unsigned bank, unsigned reg, u8 val)
|
||||
unsigned int bank, unsigned int reg, u8 val)
|
||||
{
|
||||
struct i2c_client *client = data->client;
|
||||
int ret;
|
||||
@@ -159,7 +159,7 @@ static int nct7904_read_fan(struct device *dev, u32 attr, int channel,
|
||||
unsigned int cnt, rpm;
|
||||
int ret;
|
||||
|
||||
switch(attr) {
|
||||
switch (attr) {
|
||||
case hwmon_fan_input:
|
||||
ret = nct7904_read_reg16(data, BANK_0,
|
||||
FANIN1_HV_REG + channel * 2);
|
||||
@@ -200,7 +200,7 @@ static int nct7904_read_in(struct device *dev, u32 attr, int channel,
|
||||
|
||||
index = nct7904_chan_to_index[channel];
|
||||
|
||||
switch(attr) {
|
||||
switch (attr) {
|
||||
case hwmon_in_input:
|
||||
ret = nct7904_read_reg16(data, BANK_0,
|
||||
VSEN1_HV_REG + index * 2);
|
||||
@@ -236,7 +236,7 @@ static int nct7904_read_temp(struct device *dev, u32 attr, int channel,
|
||||
struct nct7904_data *data = dev_get_drvdata(dev);
|
||||
int ret, temp;
|
||||
|
||||
switch(attr) {
|
||||
switch (attr) {
|
||||
case hwmon_temp_input:
|
||||
if (channel == 0)
|
||||
ret = nct7904_read_reg16(data, BANK_0, LTD_HV_REG);
|
||||
@@ -276,7 +276,7 @@ static int nct7904_read_pwm(struct device *dev, u32 attr, int channel,
|
||||
struct nct7904_data *data = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
switch(attr) {
|
||||
switch (attr) {
|
||||
case hwmon_pwm_input:
|
||||
ret = nct7904_read_reg(data, BANK_3, FANCTL1_OUT_REG + channel);
|
||||
if (ret < 0)
|
||||
@@ -301,7 +301,7 @@ static int nct7904_write_pwm(struct device *dev, u32 attr, int channel,
|
||||
struct nct7904_data *data = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
switch(attr) {
|
||||
switch (attr) {
|
||||
case hwmon_pwm_input:
|
||||
if (val < 0 || val > 255)
|
||||
return -EINVAL;
|
||||
@@ -322,7 +322,7 @@ static int nct7904_write_pwm(struct device *dev, u32 attr, int channel,
|
||||
|
||||
static umode_t nct7904_pwm_is_visible(const void *_data, u32 attr, int channel)
|
||||
{
|
||||
switch(attr) {
|
||||
switch (attr) {
|
||||
case hwmon_pwm_input:
|
||||
case hwmon_pwm_enable:
|
||||
return S_IRUGO | S_IWUSR;
|
||||
@@ -431,15 +431,15 @@ static const struct hwmon_channel_info nct7904_in = {
|
||||
};
|
||||
|
||||
static const u32 nct7904_fan_config[] = {
|
||||
HWMON_F_INPUT,
|
||||
HWMON_F_INPUT,
|
||||
HWMON_F_INPUT,
|
||||
HWMON_F_INPUT,
|
||||
HWMON_F_INPUT,
|
||||
HWMON_F_INPUT,
|
||||
HWMON_F_INPUT,
|
||||
HWMON_F_INPUT,
|
||||
0
|
||||
HWMON_F_INPUT,
|
||||
HWMON_F_INPUT,
|
||||
HWMON_F_INPUT,
|
||||
HWMON_F_INPUT,
|
||||
HWMON_F_INPUT,
|
||||
HWMON_F_INPUT,
|
||||
HWMON_F_INPUT,
|
||||
HWMON_F_INPUT,
|
||||
0
|
||||
};
|
||||
|
||||
static const struct hwmon_channel_info nct7904_fan = {
|
||||
@@ -448,11 +448,11 @@ static const struct hwmon_channel_info nct7904_fan = {
|
||||
};
|
||||
|
||||
static const u32 nct7904_pwm_config[] = {
|
||||
HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
|
||||
HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
|
||||
HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
|
||||
HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
|
||||
0
|
||||
HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
|
||||
HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
|
||||
HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
|
||||
HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
|
||||
0
|
||||
};
|
||||
|
||||
static const struct hwmon_channel_info nct7904_pwm = {
|
||||
@@ -461,16 +461,16 @@ static const struct hwmon_channel_info nct7904_pwm = {
|
||||
};
|
||||
|
||||
static const u32 nct7904_temp_config[] = {
|
||||
HWMON_T_INPUT,
|
||||
HWMON_T_INPUT,
|
||||
HWMON_T_INPUT,
|
||||
HWMON_T_INPUT,
|
||||
HWMON_T_INPUT,
|
||||
HWMON_T_INPUT,
|
||||
HWMON_T_INPUT,
|
||||
HWMON_T_INPUT,
|
||||
HWMON_T_INPUT,
|
||||
0
|
||||
HWMON_T_INPUT,
|
||||
HWMON_T_INPUT,
|
||||
HWMON_T_INPUT,
|
||||
HWMON_T_INPUT,
|
||||
HWMON_T_INPUT,
|
||||
HWMON_T_INPUT,
|
||||
HWMON_T_INPUT,
|
||||
HWMON_T_INPUT,
|
||||
HWMON_T_INPUT,
|
||||
0
|
||||
};
|
||||
|
||||
static const struct hwmon_channel_info nct7904_temp = {
|
||||
|
||||
1057
drivers/hwmon/npcm750-pwm-fan.c
Normal file
1057
drivers/hwmon/npcm750-pwm-fan.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -130,7 +130,7 @@ config SENSORS_MAX34440
|
||||
default n
|
||||
help
|
||||
If you say yes here you get hardware monitoring support for Maxim
|
||||
MAX34440, MAX34441, MAX34446, MAX34460, and MAX34461.
|
||||
MAX34440, MAX34441, MAX34446, MAX34451, MAX34460, and MAX34461.
|
||||
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called max34440.
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#include <linux/i2c.h>
|
||||
#include "pmbus.h"
|
||||
|
||||
enum chips { max34440, max34441, max34446, max34460, max34461 };
|
||||
enum chips { max34440, max34441, max34446, max34451, max34460, max34461 };
|
||||
|
||||
#define MAX34440_MFR_VOUT_PEAK 0xd4
|
||||
#define MAX34440_MFR_IOUT_PEAK 0xd5
|
||||
@@ -44,6 +44,9 @@ enum chips { max34440, max34441, max34446, max34460, max34461 };
|
||||
#define MAX34440_STATUS_OT_FAULT BIT(5)
|
||||
#define MAX34440_STATUS_OT_WARN BIT(6)
|
||||
|
||||
#define MAX34451_MFR_CHANNEL_CONFIG 0xe4
|
||||
#define MAX34451_MFR_CHANNEL_CONFIG_SEL_MASK 0x3f
|
||||
|
||||
struct max34440_data {
|
||||
int id;
|
||||
struct pmbus_driver_info info;
|
||||
@@ -67,7 +70,7 @@ static int max34440_read_word_data(struct i2c_client *client, int page, int reg)
|
||||
MAX34440_MFR_VOUT_PEAK);
|
||||
break;
|
||||
case PMBUS_VIRT_READ_IOUT_AVG:
|
||||
if (data->id != max34446)
|
||||
if (data->id != max34446 && data->id != max34451)
|
||||
return -ENXIO;
|
||||
ret = pmbus_read_word_data(client, page,
|
||||
MAX34446_MFR_IOUT_AVG);
|
||||
@@ -143,7 +146,7 @@ static int max34440_write_word_data(struct i2c_client *client, int page,
|
||||
case PMBUS_VIRT_RESET_IOUT_HISTORY:
|
||||
ret = pmbus_write_word_data(client, page,
|
||||
MAX34440_MFR_IOUT_PEAK, 0);
|
||||
if (!ret && data->id == max34446)
|
||||
if (!ret && (data->id == max34446 || data->id == max34451))
|
||||
ret = pmbus_write_word_data(client, page,
|
||||
MAX34446_MFR_IOUT_AVG, 0);
|
||||
|
||||
@@ -202,6 +205,58 @@ static int max34440_read_byte_data(struct i2c_client *client, int page, int reg)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max34451_set_supported_funcs(struct i2c_client *client,
|
||||
struct max34440_data *data)
|
||||
{
|
||||
/*
|
||||
* Each of the channel 0-15 can be configured to monitor the following
|
||||
* functions based on MFR_CHANNEL_CONFIG[5:0]
|
||||
* 0x10: Sequencing + voltage monitoring (only valid for PAGES 0–11)
|
||||
* 0x20: Voltage monitoring (no sequencing)
|
||||
* 0x21: Voltage read only
|
||||
* 0x22: Current monitoring
|
||||
* 0x23: Current read only
|
||||
* 0x30: General-purpose input active low
|
||||
* 0x34: General-purpose input active high
|
||||
* 0x00: Disabled
|
||||
*/
|
||||
|
||||
int page, rv;
|
||||
|
||||
for (page = 0; page < 16; page++) {
|
||||
rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page);
|
||||
if (rv < 0)
|
||||
return rv;
|
||||
|
||||
rv = i2c_smbus_read_word_data(client,
|
||||
MAX34451_MFR_CHANNEL_CONFIG);
|
||||
if (rv < 0)
|
||||
return rv;
|
||||
|
||||
switch (rv & MAX34451_MFR_CHANNEL_CONFIG_SEL_MASK) {
|
||||
case 0x10:
|
||||
case 0x20:
|
||||
data->info.func[page] = PMBUS_HAVE_VOUT |
|
||||
PMBUS_HAVE_STATUS_VOUT;
|
||||
break;
|
||||
case 0x21:
|
||||
data->info.func[page] = PMBUS_HAVE_VOUT;
|
||||
break;
|
||||
case 0x22:
|
||||
data->info.func[page] = PMBUS_HAVE_IOUT |
|
||||
PMBUS_HAVE_STATUS_IOUT;
|
||||
break;
|
||||
case 0x23:
|
||||
data->info.func[page] = PMBUS_HAVE_IOUT;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct pmbus_driver_info max34440_info[] = {
|
||||
[max34440] = {
|
||||
.pages = 14,
|
||||
@@ -325,6 +380,30 @@ static struct pmbus_driver_info max34440_info[] = {
|
||||
.read_word_data = max34440_read_word_data,
|
||||
.write_word_data = max34440_write_word_data,
|
||||
},
|
||||
[max34451] = {
|
||||
.pages = 21,
|
||||
.format[PSC_VOLTAGE_OUT] = direct,
|
||||
.format[PSC_TEMPERATURE] = direct,
|
||||
.format[PSC_CURRENT_OUT] = direct,
|
||||
.m[PSC_VOLTAGE_OUT] = 1,
|
||||
.b[PSC_VOLTAGE_OUT] = 0,
|
||||
.R[PSC_VOLTAGE_OUT] = 3,
|
||||
.m[PSC_CURRENT_OUT] = 1,
|
||||
.b[PSC_CURRENT_OUT] = 0,
|
||||
.R[PSC_CURRENT_OUT] = 2,
|
||||
.m[PSC_TEMPERATURE] = 1,
|
||||
.b[PSC_TEMPERATURE] = 0,
|
||||
.R[PSC_TEMPERATURE] = 2,
|
||||
/* func 0-15 is set dynamically before probing */
|
||||
.func[16] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
|
||||
.func[17] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
|
||||
.func[18] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
|
||||
.func[19] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
|
||||
.func[20] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
|
||||
.read_byte_data = max34440_read_byte_data,
|
||||
.read_word_data = max34440_read_word_data,
|
||||
.write_word_data = max34440_write_word_data,
|
||||
},
|
||||
[max34460] = {
|
||||
.pages = 18,
|
||||
.format[PSC_VOLTAGE_OUT] = direct,
|
||||
@@ -398,6 +477,7 @@ static int max34440_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct max34440_data *data;
|
||||
int rv;
|
||||
|
||||
data = devm_kzalloc(&client->dev, sizeof(struct max34440_data),
|
||||
GFP_KERNEL);
|
||||
@@ -406,6 +486,12 @@ static int max34440_probe(struct i2c_client *client,
|
||||
data->id = id->driver_data;
|
||||
data->info = max34440_info[id->driver_data];
|
||||
|
||||
if (data->id == max34451) {
|
||||
rv = max34451_set_supported_funcs(client, data);
|
||||
if (rv)
|
||||
return rv;
|
||||
}
|
||||
|
||||
return pmbus_do_probe(client, id, &data->info);
|
||||
}
|
||||
|
||||
@@ -413,6 +499,7 @@ static const struct i2c_device_id max34440_id[] = {
|
||||
{"max34440", max34440},
|
||||
{"max34441", max34441},
|
||||
{"max34446", max34446},
|
||||
{"max34451", max34451},
|
||||
{"max34460", max34460},
|
||||
{"max34461", max34461},
|
||||
{}
|
||||
|
||||
Reference in New Issue
Block a user