mirror of
https://github.com/torvalds/linux.git
synced 2024-12-31 23:31:29 +00:00
hwmon updates for v4.13:
- Add PMBus client driver for IR35221 - Add support for NCT6795D to nct6775 driver - Functional improvements to adt7475, aspeed-pwm-tacho, and ibmpowernv drivers - Minor fixes and cleanups in various drivers -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJZWYZwAAoJEMsfJm/On5mBCSYP/3GmiLKCS1aTupt31QUlHBw0 yJfoL8RgwbQFR83y2fI679G1dgM1a0fyIBmFCNER5jPuxhbg5ZMb+BqT5SUDYXD8 +isABxmOkWfszze5p6UlQHl5wBg+D+rV9IluxLniT74nKRtlKcIU3ePjii6vpAfK 5rPxqTzwzVNHfDygiCK8DwflS5s8njOUJy1zLXroe4kUvu+NJnZb0nDdkD26zN75 JHYNiqso2UQd2kD3kJgNzJ4t9OGcNWwPse4Y/z0P6YAMEbs8zNuS0MbVURsk17/9 12w8eNOzsmj9m3MGKg9d6c75tG75ZRdsZzUxLEngO1LafKZ7o1YV9DBjIyhVTRxi nKofp0m/7xw/5R6XhyezL5EYohDFxjTNCT+h+GQoqvG4mZl4piG6ew4t6iG0AmJy 91rXmUdYagXKU+DxNbBNM4MMwXkNHG6+jhufF9g1MTr9K7kLu9URU2gWA7Tm5/1u /0/Qt+XBr112T+luj8BHVYTiC7iaEhtBWFIFPNeay2+2Jt2nyYLcngiIuJLmwuCe vyK9i9j5P7jhxS23omzn5Ca8hdUmtOABR5cKOwpPur0yfGLjLAQG+vBW5mYu6yJt El8QftmRN/PWnEglG+nIJG35RL3XzCl6/CyI//C6tVHJViItUPQl/ZXpZUFXHsFY TeXRyHZGGcCQtBbsFUoZ =J5ye -----END PGP SIGNATURE----- Merge tag 'hwmon-for-linus-v4.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging Pull hwmon updates from Guenter Roeck: - Add PMBus client driver for IR35221 - Add support for NCT6795D to nct6775 driver - Functional improvements to adt7475, aspeed-pwm-tacho, and ibmpowernv drivers - Minor fixes and cleanups in various drivers * tag 'hwmon-for-linus-v4.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (22 commits) hwmon: (aspeed-pwm-tacho) Poll with short sleeps. hwmon: (aspeed-pwm-tacho) reduce fan_tach period hwmon: (ibmpowernv) Add current(A) sensor hwmon: (ibmpowernv) introduce a legacy_compatibles array hwmon: (pwm-fan) Switch to new atomic PWM API hwmon: (scpi) Fix the scale of SCP sensor readings hwmon: (aspeed-pwm-tacho) Enable both edge measurement. hwmon: (ibmpowernv) Add highest/lowest attributes to sensors hwmon: (pmbus) move header file out of I2C realm hwmon: (max6639) move header file out of I2C realm hwmon: (ltc4245) move header file out of I2C realm hwmon: (ds620) move header file out of I2C realm hwmon: (ads1015) move header file out of I2C realm hwmon: (adt7475) temperature smoothing hwmon: (adt7475) add high frequency support hwmon: (adt7475) fan stall prevention hwmon: (adt7475) replace find_nearest() with find_closest() hwmon: (pmbus) Add client driver for IR35221 hwmon: (nct6775) Add support for NCT6795D hwmon: (nct6775) Improve fan detection ...
This commit is contained in:
commit
24d734a2ba
@ -40,7 +40,7 @@ By default all inputs are exported.
|
||||
Platform Data
|
||||
-------------
|
||||
|
||||
In linux/i2c/ads1015.h platform data is defined, channel_data contains
|
||||
In linux/platform_data/ads1015.h platform data is defined, channel_data contains
|
||||
configuration data for the used input combinations:
|
||||
- pga is the programmable gain amplifier (values are full scale)
|
||||
0: +/- 6.144 V
|
||||
|
@ -109,6 +109,15 @@ fan speed) is applied. PWM values range from 0 (off) to 255 (full speed).
|
||||
Fan speed may be set to maximum when the temperature sensor associated with
|
||||
the PWM control exceeds temp#_max.
|
||||
|
||||
At Tmin - hysteresis the PWM output can either be off (0% duty cycle) or at the
|
||||
minimum (i.e. auto_point1_pwm). This behaviour can be configured using the
|
||||
pwm[1-*]_stall_disable sysfs attribute. A value of 0 means the fans will shut
|
||||
off. A value of 1 means the fans will run at auto_point1_pwm.
|
||||
|
||||
The responsiveness of the ADT747x to temperature changes can be configured.
|
||||
This allows smoothing of the fan speed transition. To set the transition time
|
||||
set the value in ms in the temp[1-*]_smoothing sysfs attribute.
|
||||
|
||||
Notes
|
||||
-----
|
||||
|
||||
|
87
Documentation/hwmon/ir35221
Normal file
87
Documentation/hwmon/ir35221
Normal file
@ -0,0 +1,87 @@
|
||||
Kernel driver ir35221
|
||||
=====================
|
||||
|
||||
Supported chips:
|
||||
* Infinion IR35221
|
||||
Prefix: 'ir35221'
|
||||
Addresses scanned: -
|
||||
Datasheet: Datasheet is not publicly available.
|
||||
|
||||
Author: Samuel Mendoza-Jonas <sam@mendozajonas.com>
|
||||
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
IR35221 is a Digital DC-DC Multiphase Converter
|
||||
|
||||
|
||||
Usage Notes
|
||||
-----------
|
||||
|
||||
This driver does not probe for PMBus devices. You will have to instantiate
|
||||
devices explicitly.
|
||||
|
||||
Example: the following commands will load the driver for an IR35221
|
||||
at address 0x70 on I2C bus #4:
|
||||
|
||||
# modprobe ir35221
|
||||
# echo ir35221 0x70 > /sys/bus/i2c/devices/i2c-4/new_device
|
||||
|
||||
|
||||
Sysfs attributes
|
||||
----------------
|
||||
|
||||
curr1_label "iin"
|
||||
curr1_input Measured input current
|
||||
curr1_max Maximum current
|
||||
curr1_max_alarm Current high alarm
|
||||
|
||||
curr[2-3]_label "iout[1-2]"
|
||||
curr[2-3]_input Measured output current
|
||||
curr[2-3]_crit Critical maximum current
|
||||
curr[2-3]_crit_alarm Current critical high alarm
|
||||
curr[2-3]_highest Highest output current
|
||||
curr[2-3]_lowest Lowest output current
|
||||
curr[2-3]_max Maximum current
|
||||
curr[2-3]_max_alarm Current high alarm
|
||||
|
||||
in1_label "vin"
|
||||
in1_input Measured input voltage
|
||||
in1_crit Critical maximum input voltage
|
||||
in1_crit_alarm Input voltage critical high alarm
|
||||
in1_highest Highest input voltage
|
||||
in1_lowest Lowest input voltage
|
||||
in1_min Minimum input voltage
|
||||
in1_min_alarm Input voltage low alarm
|
||||
|
||||
in[2-3]_label "vout[1-2]"
|
||||
in[2-3]_input Measured output voltage
|
||||
in[2-3]_lcrit Critical minimum output voltage
|
||||
in[2-3]_lcrit_alarm Output voltage critical low alarm
|
||||
in[2-3]_crit Critical maximum output voltage
|
||||
in[2-3]_crit_alarm Output voltage critical high alarm
|
||||
in[2-3]_highest Highest output voltage
|
||||
in[2-3]_lowest Lowest output voltage
|
||||
in[2-3]_max Maximum output voltage
|
||||
in[2-3]_max_alarm Output voltage high alarm
|
||||
in[2-3]_min Minimum output voltage
|
||||
in[2-3]_min_alarm Output voltage low alarm
|
||||
|
||||
power1_label "pin"
|
||||
power1_input Measured input power
|
||||
power1_alarm Input power high alarm
|
||||
power1_max Input power limit
|
||||
|
||||
power[2-3]_label "pout[1-2]"
|
||||
power[2-3]_input Measured output power
|
||||
power[2-3]_max Output power limit
|
||||
power[2-3]_max_alarm Output power high alarm
|
||||
|
||||
temp[1-2]_input Measured temperature
|
||||
temp[1-2]_crit Critical high temperature
|
||||
temp[1-2]_crit_alarm Chip temperature critical high alarm
|
||||
temp[1-2]_highest Highest temperature
|
||||
temp[1-2]_lowest Lowest temperature
|
||||
temp[1-2]_max Maximum temperature
|
||||
temp[1-2]_max_alarm Chip temperature high alarm
|
@ -96,7 +96,7 @@ slowly, -EAGAIN will be returned when you read the sysfs attribute containing
|
||||
the sensor reading.
|
||||
|
||||
The LTC4245 chip can be configured to sample all GPIO pins with two methods:
|
||||
1) platform data -- see include/linux/i2c/ltc4245.h
|
||||
1) platform data -- see include/linux/platform_data/ltc4245.h
|
||||
2) OF device tree -- add the "ltc4245,use-extra-gpios" property to each chip
|
||||
|
||||
The default mode of operation is to sample a single GPIO pin.
|
||||
|
@ -253,7 +253,7 @@ Specifically, it provides the following information.
|
||||
PMBus driver platform data
|
||||
==========================
|
||||
|
||||
PMBus platform data is defined in include/linux/i2c/pmbus.h. Platform data
|
||||
PMBus platform data is defined in include/linux/pmbus.h. Platform data
|
||||
currently only provides a flag field with a single bit used.
|
||||
|
||||
#define PMBUS_SKIP_STATUS_CHECK (1 << 0)
|
||||
|
@ -478,7 +478,7 @@ L: linux-hwmon@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/hwmon/ads1015
|
||||
F: drivers/hwmon/ads1015.c
|
||||
F: include/linux/i2c/ads1015.h
|
||||
F: include/linux/platform_data/ads1015.h
|
||||
|
||||
ADT746X FAN DRIVER
|
||||
M: Colin Leroy <colin@colino.net>
|
||||
@ -10179,7 +10179,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging.git
|
||||
S: Maintained
|
||||
F: Documentation/hwmon/pmbus
|
||||
F: drivers/hwmon/pmbus/
|
||||
F: include/linux/i2c/pmbus.h
|
||||
F: include/linux/pmbus.h
|
||||
|
||||
PMC SIERRA MaxRAID DRIVER
|
||||
L: linux-scsi@vger.kernel.org
|
||||
|
@ -34,7 +34,7 @@
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include <linux/i2c/ads1015.h>
|
||||
#include <linux/platform_data/ads1015.h>
|
||||
|
||||
/* ADS1015 registers */
|
||||
enum {
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <linux/hwmon-vid.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/util_macros.h>
|
||||
|
||||
/* Indexes for the sysfs hooks */
|
||||
|
||||
@ -78,6 +79,9 @@
|
||||
|
||||
#define REG_TEMP_TRANGE_BASE 0x5F
|
||||
|
||||
#define REG_ENHANCE_ACOUSTICS1 0x62
|
||||
#define REG_ENHANCE_ACOUSTICS2 0x63
|
||||
|
||||
#define REG_PWM_MIN_BASE 0x64
|
||||
|
||||
#define REG_TEMP_TMIN_BASE 0x67
|
||||
@ -208,6 +212,7 @@ struct adt7475_data {
|
||||
u8 range[3];
|
||||
u8 pwmctl[3];
|
||||
u8 pwmchan[3];
|
||||
u8 enh_acoustics[2];
|
||||
|
||||
u8 vid;
|
||||
u8 vrm;
|
||||
@ -314,35 +319,6 @@ static void adt7475_write_word(struct i2c_client *client, int reg, u16 val)
|
||||
i2c_smbus_write_byte_data(client, reg, val & 0xFF);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the nearest value in a table - used for pwm frequency and
|
||||
* auto temp range
|
||||
*/
|
||||
static int find_nearest(long val, const int *array, int size)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (val < array[0])
|
||||
return 0;
|
||||
|
||||
if (val > array[size - 1])
|
||||
return size - 1;
|
||||
|
||||
for (i = 0; i < size - 1; i++) {
|
||||
int a, b;
|
||||
|
||||
if (val > array[i + 1])
|
||||
continue;
|
||||
|
||||
a = val - array[i];
|
||||
b = array[i + 1] - val;
|
||||
|
||||
return (a <= b) ? i : i + 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t show_voltage(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
@ -550,6 +526,88 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *attr,
|
||||
return count;
|
||||
}
|
||||
|
||||
/* Assuming CONFIG6[SLOW] is 0 */
|
||||
static const int ad7475_st_map[] = {
|
||||
37500, 18800, 12500, 7500, 4700, 3100, 1600, 800,
|
||||
};
|
||||
|
||||
static ssize_t show_temp_st(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct adt7475_data *data = i2c_get_clientdata(client);
|
||||
long val;
|
||||
|
||||
switch (sattr->index) {
|
||||
case 0:
|
||||
val = data->enh_acoustics[0] & 0xf;
|
||||
break;
|
||||
case 1:
|
||||
val = (data->enh_acoustics[1] >> 4) & 0xf;
|
||||
break;
|
||||
case 2:
|
||||
default:
|
||||
val = data->enh_acoustics[1] & 0xf;
|
||||
break;
|
||||
}
|
||||
|
||||
if (val & 0x8)
|
||||
return sprintf(buf, "%d\n", ad7475_st_map[val & 0x7]);
|
||||
else
|
||||
return sprintf(buf, "0\n");
|
||||
}
|
||||
|
||||
static ssize_t set_temp_st(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct adt7475_data *data = i2c_get_clientdata(client);
|
||||
unsigned char reg;
|
||||
int shift, idx;
|
||||
ulong val;
|
||||
|
||||
if (kstrtoul(buf, 10, &val))
|
||||
return -EINVAL;
|
||||
|
||||
switch (sattr->index) {
|
||||
case 0:
|
||||
reg = REG_ENHANCE_ACOUSTICS1;
|
||||
shift = 0;
|
||||
idx = 0;
|
||||
break;
|
||||
case 1:
|
||||
reg = REG_ENHANCE_ACOUSTICS2;
|
||||
shift = 0;
|
||||
idx = 1;
|
||||
break;
|
||||
case 2:
|
||||
default:
|
||||
reg = REG_ENHANCE_ACOUSTICS2;
|
||||
shift = 4;
|
||||
idx = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (val > 0) {
|
||||
val = find_closest_descending(val, ad7475_st_map,
|
||||
ARRAY_SIZE(ad7475_st_map));
|
||||
val |= 0x8;
|
||||
}
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
|
||||
data->enh_acoustics[idx] &= ~(0xf << shift);
|
||||
data->enh_acoustics[idx] |= (val << shift);
|
||||
|
||||
i2c_smbus_write_byte_data(client, reg, data->enh_acoustics[idx]);
|
||||
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Table of autorange values - the user will write the value in millidegrees,
|
||||
* and we'll convert it
|
||||
@ -606,7 +664,7 @@ static ssize_t set_point2(struct device *dev, struct device_attribute *attr,
|
||||
val -= temp;
|
||||
|
||||
/* Find the nearest table entry to what the user wrote */
|
||||
val = find_nearest(val, autorange_table, ARRAY_SIZE(autorange_table));
|
||||
val = find_closest(val, autorange_table, ARRAY_SIZE(autorange_table));
|
||||
|
||||
data->range[sattr->index] &= ~0xF0;
|
||||
data->range[sattr->index] |= val << 4;
|
||||
@ -728,6 +786,43 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
|
||||
data->pwm[sattr->nr][sattr->index] = clamp_val(val, 0, 0xFF);
|
||||
i2c_smbus_write_byte_data(client, reg,
|
||||
data->pwm[sattr->nr][sattr->index]);
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t show_stall_disable(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct adt7475_data *data = i2c_get_clientdata(client);
|
||||
u8 mask = BIT(5 + sattr->index);
|
||||
|
||||
return sprintf(buf, "%d\n", !!(data->enh_acoustics[0] & mask));
|
||||
}
|
||||
|
||||
static ssize_t set_stall_disable(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct adt7475_data *data = i2c_get_clientdata(client);
|
||||
long val;
|
||||
u8 mask = BIT(5 + sattr->index);
|
||||
|
||||
if (kstrtol(buf, 10, &val))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
|
||||
data->enh_acoustics[0] &= ~mask;
|
||||
if (val)
|
||||
data->enh_acoustics[0] |= mask;
|
||||
|
||||
i2c_smbus_write_byte_data(client, REG_ENHANCE_ACOUSTICS1,
|
||||
data->enh_acoustics[0]);
|
||||
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
@ -839,7 +934,7 @@ static ssize_t set_pwmctrl(struct device *dev, struct device_attribute *attr,
|
||||
|
||||
/* List of frequencies for the PWM */
|
||||
static const int pwmfreq_table[] = {
|
||||
11, 14, 22, 29, 35, 44, 58, 88
|
||||
11, 14, 22, 29, 35, 44, 58, 88, 22500
|
||||
};
|
||||
|
||||
static ssize_t show_pwmfreq(struct device *dev, struct device_attribute *attr,
|
||||
@ -847,9 +942,10 @@ static ssize_t show_pwmfreq(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);
|
||||
int i = clamp_val(data->range[sattr->index] & 0xf, 0,
|
||||
ARRAY_SIZE(pwmfreq_table) - 1);
|
||||
|
||||
return sprintf(buf, "%d\n",
|
||||
pwmfreq_table[data->range[sattr->index] & 7]);
|
||||
return sprintf(buf, "%d\n", pwmfreq_table[i]);
|
||||
}
|
||||
|
||||
static ssize_t set_pwmfreq(struct device *dev, struct device_attribute *attr,
|
||||
@ -864,13 +960,13 @@ static ssize_t set_pwmfreq(struct device *dev, struct device_attribute *attr,
|
||||
if (kstrtol(buf, 10, &val))
|
||||
return -EINVAL;
|
||||
|
||||
out = find_nearest(val, pwmfreq_table, ARRAY_SIZE(pwmfreq_table));
|
||||
out = find_closest(val, pwmfreq_table, ARRAY_SIZE(pwmfreq_table));
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
|
||||
data->range[sattr->index] =
|
||||
adt7475_read(TEMP_TRANGE_REG(sattr->index));
|
||||
data->range[sattr->index] &= ~7;
|
||||
data->range[sattr->index] &= ~0xf;
|
||||
data->range[sattr->index] |= out;
|
||||
|
||||
i2c_smbus_write_byte_data(client, TEMP_TRANGE_REG(sattr->index),
|
||||
@ -995,6 +1091,8 @@ static SENSOR_DEVICE_ATTR_2(temp1_crit, S_IRUGO | S_IWUSR, show_temp, set_temp,
|
||||
THERM, 0);
|
||||
static SENSOR_DEVICE_ATTR_2(temp1_crit_hyst, S_IRUGO | S_IWUSR, show_temp,
|
||||
set_temp, HYSTERSIS, 0);
|
||||
static SENSOR_DEVICE_ATTR_2(temp1_smoothing, S_IRUGO | S_IWUSR, show_temp_st,
|
||||
set_temp_st, 0, 0);
|
||||
static SENSOR_DEVICE_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, INPUT, 1);
|
||||
static SENSOR_DEVICE_ATTR_2(temp2_alarm, S_IRUGO, show_temp, NULL, ALARM, 1);
|
||||
static SENSOR_DEVICE_ATTR_2(temp2_max, S_IRUGO | S_IWUSR, show_temp, set_temp,
|
||||
@ -1011,6 +1109,8 @@ static SENSOR_DEVICE_ATTR_2(temp2_crit, S_IRUGO | S_IWUSR, show_temp, set_temp,
|
||||
THERM, 1);
|
||||
static SENSOR_DEVICE_ATTR_2(temp2_crit_hyst, S_IRUGO | S_IWUSR, show_temp,
|
||||
set_temp, HYSTERSIS, 1);
|
||||
static SENSOR_DEVICE_ATTR_2(temp2_smoothing, S_IRUGO | S_IWUSR, show_temp_st,
|
||||
set_temp_st, 0, 1);
|
||||
static SENSOR_DEVICE_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, INPUT, 2);
|
||||
static SENSOR_DEVICE_ATTR_2(temp3_alarm, S_IRUGO, show_temp, NULL, ALARM, 2);
|
||||
static SENSOR_DEVICE_ATTR_2(temp3_fault, S_IRUGO, show_temp, NULL, FAULT, 2);
|
||||
@ -1028,6 +1128,8 @@ static SENSOR_DEVICE_ATTR_2(temp3_crit, S_IRUGO | S_IWUSR, show_temp, set_temp,
|
||||
THERM, 2);
|
||||
static SENSOR_DEVICE_ATTR_2(temp3_crit_hyst, S_IRUGO | S_IWUSR, show_temp,
|
||||
set_temp, HYSTERSIS, 2);
|
||||
static SENSOR_DEVICE_ATTR_2(temp3_smoothing, S_IRUGO | S_IWUSR, show_temp_st,
|
||||
set_temp_st, 0, 2);
|
||||
static SENSOR_DEVICE_ATTR_2(fan1_input, S_IRUGO, show_tach, NULL, INPUT, 0);
|
||||
static SENSOR_DEVICE_ATTR_2(fan1_min, S_IRUGO | S_IWUSR, show_tach, set_tach,
|
||||
MIN, 0);
|
||||
@ -1056,6 +1158,8 @@ static SENSOR_DEVICE_ATTR_2(pwm1_auto_point1_pwm, S_IRUGO | S_IWUSR, show_pwm,
|
||||
set_pwm, MIN, 0);
|
||||
static SENSOR_DEVICE_ATTR_2(pwm1_auto_point2_pwm, S_IRUGO | S_IWUSR, show_pwm,
|
||||
set_pwm, MAX, 0);
|
||||
static SENSOR_DEVICE_ATTR_2(pwm1_stall_disable, S_IRUGO | S_IWUSR,
|
||||
show_stall_disable, set_stall_disable, 0, 0);
|
||||
static SENSOR_DEVICE_ATTR_2(pwm2, S_IRUGO | S_IWUSR, show_pwm, set_pwm, INPUT,
|
||||
1);
|
||||
static SENSOR_DEVICE_ATTR_2(pwm2_freq, S_IRUGO | S_IWUSR, show_pwmfreq,
|
||||
@ -1068,6 +1172,8 @@ static SENSOR_DEVICE_ATTR_2(pwm2_auto_point1_pwm, S_IRUGO | S_IWUSR, show_pwm,
|
||||
set_pwm, MIN, 1);
|
||||
static SENSOR_DEVICE_ATTR_2(pwm2_auto_point2_pwm, S_IRUGO | S_IWUSR, show_pwm,
|
||||
set_pwm, MAX, 1);
|
||||
static SENSOR_DEVICE_ATTR_2(pwm2_stall_disable, S_IRUGO | S_IWUSR,
|
||||
show_stall_disable, set_stall_disable, 0, 1);
|
||||
static SENSOR_DEVICE_ATTR_2(pwm3, S_IRUGO | S_IWUSR, show_pwm, set_pwm, INPUT,
|
||||
2);
|
||||
static SENSOR_DEVICE_ATTR_2(pwm3_freq, S_IRUGO | S_IWUSR, show_pwmfreq,
|
||||
@ -1080,6 +1186,8 @@ static SENSOR_DEVICE_ATTR_2(pwm3_auto_point1_pwm, S_IRUGO | S_IWUSR, show_pwm,
|
||||
set_pwm, MIN, 2);
|
||||
static SENSOR_DEVICE_ATTR_2(pwm3_auto_point2_pwm, S_IRUGO | S_IWUSR, show_pwm,
|
||||
set_pwm, MAX, 2);
|
||||
static SENSOR_DEVICE_ATTR_2(pwm3_stall_disable, S_IRUGO | S_IWUSR,
|
||||
show_stall_disable, set_stall_disable, 0, 2);
|
||||
|
||||
/* Non-standard name, might need revisiting */
|
||||
static DEVICE_ATTR_RW(pwm_use_point2_pwm_at_crit);
|
||||
@ -1106,6 +1214,7 @@ static struct attribute *adt7475_attrs[] = {
|
||||
&sensor_dev_attr_temp1_auto_point2_temp.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_crit.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_smoothing.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_max.dev_attr.attr,
|
||||
@ -1115,6 +1224,7 @@ static struct attribute *adt7475_attrs[] = {
|
||||
&sensor_dev_attr_temp2_auto_point2_temp.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_crit.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_crit_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_smoothing.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_alarm.dev_attr.attr,
|
||||
@ -1125,6 +1235,7 @@ static struct attribute *adt7475_attrs[] = {
|
||||
&sensor_dev_attr_temp3_auto_point2_temp.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_crit.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_crit_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_smoothing.dev_attr.attr,
|
||||
&sensor_dev_attr_fan1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_fan1_min.dev_attr.attr,
|
||||
&sensor_dev_attr_fan1_alarm.dev_attr.attr,
|
||||
@ -1140,12 +1251,14 @@ static struct attribute *adt7475_attrs[] = {
|
||||
&sensor_dev_attr_pwm1_auto_channels_temp.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_stall_disable.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm3.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm3_freq.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm3_enable.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm3_auto_channels_temp.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm3_auto_point2_pwm.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm3_stall_disable.dev_attr.attr,
|
||||
&dev_attr_pwm_use_point2_pwm_at_crit.attr,
|
||||
NULL,
|
||||
};
|
||||
@ -1164,6 +1277,7 @@ static struct attribute *pwm2_attrs[] = {
|
||||
&sensor_dev_attr_pwm2_auto_channels_temp.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm2_auto_point2_pwm.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm2_stall_disable.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
|
@ -146,14 +146,26 @@
|
||||
|
||||
#define PWM_MAX 255
|
||||
|
||||
#define BOTH_EDGES 0x02 /* 10b */
|
||||
|
||||
#define M_PWM_DIV_H 0x00
|
||||
#define M_PWM_DIV_L 0x05
|
||||
#define M_PWM_PERIOD 0x5F
|
||||
#define M_TACH_CLK_DIV 0x00
|
||||
#define M_TACH_MODE 0x00
|
||||
#define M_TACH_UNIT 0x1000
|
||||
/*
|
||||
* 5:4 Type N fan tach mode selection bit:
|
||||
* 00: falling
|
||||
* 01: rising
|
||||
* 10: both
|
||||
* 11: reserved.
|
||||
*/
|
||||
#define M_TACH_MODE 0x02 /* 10b */
|
||||
#define M_TACH_UNIT 0x00c0
|
||||
#define INIT_FAN_CTRL 0xFF
|
||||
|
||||
/* How long we sleep in us while waiting for an RPM result. */
|
||||
#define ASPEED_RPM_STATUS_SLEEP_USEC 500
|
||||
|
||||
struct aspeed_pwm_tacho_data {
|
||||
struct regmap *regmap;
|
||||
unsigned long clk_freq;
|
||||
@ -163,6 +175,7 @@ struct aspeed_pwm_tacho_data {
|
||||
u8 type_pwm_clock_division_h[3];
|
||||
u8 type_pwm_clock_division_l[3];
|
||||
u8 type_fan_tach_clock_division[3];
|
||||
u8 type_fan_tach_mode[3];
|
||||
u16 type_fan_tach_unit[3];
|
||||
u8 pwm_port_type[8];
|
||||
u8 pwm_port_fan_ctrl[8];
|
||||
@ -498,8 +511,9 @@ static u32 aspeed_get_fan_tach_ch_measure_period(struct aspeed_pwm_tacho_data
|
||||
static int aspeed_get_fan_tach_ch_rpm(struct aspeed_pwm_tacho_data *priv,
|
||||
u8 fan_tach_ch)
|
||||
{
|
||||
u32 raw_data, tach_div, clk_source, sec, val;
|
||||
u8 fan_tach_ch_source, type;
|
||||
u32 raw_data, tach_div, clk_source, msec, usec, val;
|
||||
u8 fan_tach_ch_source, type, mode, both;
|
||||
int ret;
|
||||
|
||||
regmap_write(priv->regmap, ASPEED_PTCR_TRIGGER, 0);
|
||||
regmap_write(priv->regmap, ASPEED_PTCR_TRIGGER, 0x1 << fan_tach_ch);
|
||||
@ -507,16 +521,31 @@ static int aspeed_get_fan_tach_ch_rpm(struct aspeed_pwm_tacho_data *priv,
|
||||
fan_tach_ch_source = priv->fan_tach_ch_source[fan_tach_ch];
|
||||
type = priv->pwm_port_type[fan_tach_ch_source];
|
||||
|
||||
sec = (1000 / aspeed_get_fan_tach_ch_measure_period(priv, type));
|
||||
msleep(sec);
|
||||
msec = (1000 / aspeed_get_fan_tach_ch_measure_period(priv, type));
|
||||
usec = msec * 1000;
|
||||
|
||||
regmap_read(priv->regmap, ASPEED_PTCR_RESULT, &val);
|
||||
if (!(val & RESULT_STATUS_MASK))
|
||||
return -ETIMEDOUT;
|
||||
ret = regmap_read_poll_timeout(
|
||||
priv->regmap,
|
||||
ASPEED_PTCR_RESULT,
|
||||
val,
|
||||
(val & RESULT_STATUS_MASK),
|
||||
ASPEED_RPM_STATUS_SLEEP_USEC,
|
||||
usec);
|
||||
|
||||
/* return -ETIMEDOUT if we didn't get an answer. */
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
raw_data = val & RESULT_VALUE_MASK;
|
||||
tach_div = priv->type_fan_tach_clock_division[type];
|
||||
tach_div = 0x4 << (tach_div * 2);
|
||||
/*
|
||||
* We need the mode to determine if the raw_data is double (from
|
||||
* counting both edges).
|
||||
*/
|
||||
mode = priv->type_fan_tach_mode[type];
|
||||
both = (mode & BOTH_EDGES) ? 1 : 0;
|
||||
|
||||
tach_div = (0x4 << both) << (tach_div * 2);
|
||||
clk_source = priv->clk_freq;
|
||||
|
||||
if (raw_data == 0)
|
||||
@ -702,6 +731,7 @@ static void aspeed_create_type(struct aspeed_pwm_tacho_data *priv)
|
||||
aspeed_set_tacho_type_enable(priv->regmap, TYPEM, true);
|
||||
priv->type_fan_tach_clock_division[TYPEM] = M_TACH_CLK_DIV;
|
||||
priv->type_fan_tach_unit[TYPEM] = M_TACH_UNIT;
|
||||
priv->type_fan_tach_mode[TYPEM] = M_TACH_MODE;
|
||||
aspeed_set_tacho_type_values(priv->regmap, TYPEM, M_TACH_MODE,
|
||||
M_TACH_UNIT, M_TACH_CLK_DIV);
|
||||
}
|
||||
|
@ -30,7 +30,7 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/i2c/ds620.h>
|
||||
#include <linux/platform_data/ds620.h>
|
||||
|
||||
/*
|
||||
* Many DS620 constants specified below
|
||||
|
@ -50,22 +50,34 @@ enum sensors {
|
||||
TEMP,
|
||||
POWER_SUPPLY,
|
||||
POWER_INPUT,
|
||||
CURRENT,
|
||||
MAX_SENSOR_TYPE,
|
||||
};
|
||||
|
||||
#define INVALID_INDEX (-1U)
|
||||
|
||||
/*
|
||||
* 'compatible' string properties for sensor types as defined in old
|
||||
* PowerNV firmware (skiboot). These are ordered as 'enum sensors'.
|
||||
*/
|
||||
static const char * const legacy_compatibles[] = {
|
||||
"ibm,opal-sensor-cooling-fan",
|
||||
"ibm,opal-sensor-amb-temp",
|
||||
"ibm,opal-sensor-power-supply",
|
||||
"ibm,opal-sensor-power"
|
||||
};
|
||||
|
||||
static struct sensor_group {
|
||||
const char *name;
|
||||
const char *compatible;
|
||||
const char *name; /* matches property 'sensor-type' */
|
||||
struct attribute_group group;
|
||||
u32 attr_count;
|
||||
u32 hwmon_index;
|
||||
} sensor_groups[] = {
|
||||
{"fan", "ibm,opal-sensor-cooling-fan"},
|
||||
{"temp", "ibm,opal-sensor-amb-temp"},
|
||||
{"in", "ibm,opal-sensor-power-supply"},
|
||||
{"power", "ibm,opal-sensor-power"}
|
||||
{ "fan" },
|
||||
{ "temp" },
|
||||
{ "in" },
|
||||
{ "power" },
|
||||
{ "curr" },
|
||||
};
|
||||
|
||||
struct sensor_data {
|
||||
@ -239,8 +251,8 @@ static int get_sensor_type(struct device_node *np)
|
||||
enum sensors type;
|
||||
const char *str;
|
||||
|
||||
for (type = 0; type < MAX_SENSOR_TYPE; type++) {
|
||||
if (of_device_is_compatible(np, sensor_groups[type].compatible))
|
||||
for (type = 0; type < ARRAY_SIZE(legacy_compatibles); type++) {
|
||||
if (of_device_is_compatible(np, legacy_compatibles[type]))
|
||||
return type;
|
||||
}
|
||||
|
||||
@ -298,10 +310,14 @@ static int populate_attr_groups(struct platform_device *pdev)
|
||||
sensor_groups[type].attr_count++;
|
||||
|
||||
/*
|
||||
* add a new attribute for labels
|
||||
* add attributes for labels, min and max
|
||||
*/
|
||||
if (!of_property_read_string(np, "label", &label))
|
||||
sensor_groups[type].attr_count++;
|
||||
if (of_find_property(np, "sensor-data-min", NULL))
|
||||
sensor_groups[type].attr_count++;
|
||||
if (of_find_property(np, "sensor-data-max", NULL))
|
||||
sensor_groups[type].attr_count++;
|
||||
}
|
||||
|
||||
of_node_put(opal);
|
||||
@ -337,6 +353,41 @@ static void create_hwmon_attr(struct sensor_data *sdata, const char *attr_name,
|
||||
sdata->dev_attr.show = show;
|
||||
}
|
||||
|
||||
static void populate_sensor(struct sensor_data *sdata, int od, int hd, int sid,
|
||||
const char *attr_name, enum sensors type,
|
||||
const struct attribute_group *pgroup,
|
||||
ssize_t (*show)(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf))
|
||||
{
|
||||
sdata->id = sid;
|
||||
sdata->type = type;
|
||||
sdata->opal_index = od;
|
||||
sdata->hwmon_index = hd;
|
||||
create_hwmon_attr(sdata, attr_name, show);
|
||||
pgroup->attrs[sensor_groups[type].attr_count++] = &sdata->dev_attr.attr;
|
||||
}
|
||||
|
||||
static char *get_max_attr(enum sensors type)
|
||||
{
|
||||
switch (type) {
|
||||
case POWER_INPUT:
|
||||
return "input_highest";
|
||||
default:
|
||||
return "highest";
|
||||
}
|
||||
}
|
||||
|
||||
static char *get_min_attr(enum sensors type)
|
||||
{
|
||||
switch (type) {
|
||||
case POWER_INPUT:
|
||||
return "input_lowest";
|
||||
default:
|
||||
return "lowest";
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterate through the device tree for each child of 'sensors' node, create
|
||||
* a sysfs attribute file, the file is named by translating the DT node name
|
||||
@ -417,16 +468,31 @@ static int create_device_attrs(struct platform_device *pdev)
|
||||
* attribute. They are related to the same
|
||||
* sensor.
|
||||
*/
|
||||
sdata[count].type = type;
|
||||
sdata[count].opal_index = sdata[count - 1].opal_index;
|
||||
sdata[count].hwmon_index = sdata[count - 1].hwmon_index;
|
||||
|
||||
make_sensor_label(np, &sdata[count], label);
|
||||
populate_sensor(&sdata[count], opal_index,
|
||||
sdata[count - 1].hwmon_index,
|
||||
sensor_id, "label", type, pgroups[type],
|
||||
show_label);
|
||||
count++;
|
||||
}
|
||||
|
||||
create_hwmon_attr(&sdata[count], "label", show_label);
|
||||
if (!of_property_read_u32(np, "sensor-data-max", &sensor_id)) {
|
||||
attr_name = get_max_attr(type);
|
||||
populate_sensor(&sdata[count], opal_index,
|
||||
sdata[count - 1].hwmon_index,
|
||||
sensor_id, attr_name, type,
|
||||
pgroups[type], show_sensor);
|
||||
count++;
|
||||
}
|
||||
|
||||
pgroups[type]->attrs[sensor_groups[type].attr_count++] =
|
||||
&sdata[count++].dev_attr.attr;
|
||||
if (!of_property_read_u32(np, "sensor-data-min", &sensor_id)) {
|
||||
attr_name = get_min_attr(type);
|
||||
populate_sensor(&sdata[count], opal_index,
|
||||
sdata[count - 1].hwmon_index,
|
||||
sensor_id, attr_name, type,
|
||||
pgroups[type], show_sensor);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,7 @@
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/i2c/ltc4245.h>
|
||||
#include <linux/platform_data/ltc4245.h>
|
||||
|
||||
/* Here are names of the chip's registers (a.k.a. commands) */
|
||||
enum ltc4245_cmd {
|
||||
|
@ -32,7 +32,7 @@
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/i2c/max6639.h>
|
||||
#include <linux/platform_data/max6639.h>
|
||||
|
||||
/* Addresses to scan */
|
||||
static const unsigned short normal_i2c[] = { 0x2c, 0x2e, 0x2f, I2C_CLIENT_END };
|
||||
|
@ -40,6 +40,8 @@
|
||||
* nct6791d 15 6 6 2+6 0xc800 0xc1 0x5ca3
|
||||
* nct6792d 15 6 6 2+6 0xc910 0xc1 0x5ca3
|
||||
* nct6793d 15 6 6 2+6 0xd120 0xc1 0x5ca3
|
||||
* nct6795d 14 6 6 2+6 0xd350 0xc1 0x5ca3
|
||||
*
|
||||
*
|
||||
* #temp lists the number of monitored temperature sources (first value) plus
|
||||
* the number of directly connectable temperature sensors (second value).
|
||||
@ -58,13 +60,15 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/io.h>
|
||||
#include "lm75.h"
|
||||
|
||||
#define USE_ALTERNATE
|
||||
|
||||
enum kinds { nct6106, nct6775, nct6776, nct6779, nct6791, nct6792, nct6793 };
|
||||
enum kinds { nct6106, nct6775, nct6776, nct6779, nct6791, nct6792, nct6793,
|
||||
nct6795 };
|
||||
|
||||
/* used to set data->name = nct6775_device_names[data->sio_kind] */
|
||||
static const char * const nct6775_device_names[] = {
|
||||
@ -75,6 +79,7 @@ static const char * const nct6775_device_names[] = {
|
||||
"nct6791",
|
||||
"nct6792",
|
||||
"nct6793",
|
||||
"nct6795",
|
||||
};
|
||||
|
||||
static const char * const nct6775_sio_names[] __initconst = {
|
||||
@ -85,6 +90,7 @@ static const char * const nct6775_sio_names[] __initconst = {
|
||||
"NCT6791D",
|
||||
"NCT6792D",
|
||||
"NCT6793D",
|
||||
"NCT6795D",
|
||||
};
|
||||
|
||||
static unsigned short force_id;
|
||||
@ -104,6 +110,7 @@ MODULE_PARM_DESC(fan_debounce, "Enable debouncing for fan RPM signal");
|
||||
#define NCT6775_LD_ACPI 0x0a
|
||||
#define NCT6775_LD_HWM 0x0b
|
||||
#define NCT6775_LD_VID 0x0d
|
||||
#define NCT6775_LD_12 0x12
|
||||
|
||||
#define SIO_REG_LDSEL 0x07 /* Logical device select */
|
||||
#define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */
|
||||
@ -117,6 +124,7 @@ MODULE_PARM_DESC(fan_debounce, "Enable debouncing for fan RPM signal");
|
||||
#define SIO_NCT6791_ID 0xc800
|
||||
#define SIO_NCT6792_ID 0xc910
|
||||
#define SIO_NCT6793_ID 0xd120
|
||||
#define SIO_NCT6795_ID 0xd350
|
||||
#define SIO_ID_MASK 0xFFF0
|
||||
|
||||
enum pwm_enable { off, manual, thermal_cruise, speed_cruise, sf3, sf4 };
|
||||
@ -360,12 +368,24 @@ static const char *const nct6775_temp_label[] = {
|
||||
"PCH_DIM3_TEMP"
|
||||
};
|
||||
|
||||
static const u16 NCT6775_REG_TEMP_ALTERNATE[ARRAY_SIZE(nct6775_temp_label) - 1]
|
||||
= { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x661, 0x662, 0x664 };
|
||||
#define NCT6775_TEMP_MASK 0x001ffffe
|
||||
|
||||
static const u16 NCT6775_REG_TEMP_CRIT[ARRAY_SIZE(nct6775_temp_label) - 1]
|
||||
= { 0, 0, 0, 0, 0xa00, 0xa01, 0xa02, 0xa03, 0xa04, 0xa05, 0xa06,
|
||||
0xa07 };
|
||||
static const u16 NCT6775_REG_TEMP_ALTERNATE[32] = {
|
||||
[13] = 0x661,
|
||||
[14] = 0x662,
|
||||
[15] = 0x664,
|
||||
};
|
||||
|
||||
static const u16 NCT6775_REG_TEMP_CRIT[32] = {
|
||||
[4] = 0xa00,
|
||||
[5] = 0xa01,
|
||||
[6] = 0xa02,
|
||||
[7] = 0xa03,
|
||||
[8] = 0xa04,
|
||||
[9] = 0xa05,
|
||||
[10] = 0xa06,
|
||||
[11] = 0xa07
|
||||
};
|
||||
|
||||
/* NCT6776 specific data */
|
||||
|
||||
@ -434,11 +454,18 @@ static const char *const nct6776_temp_label[] = {
|
||||
"BYTE_TEMP"
|
||||
};
|
||||
|
||||
static const u16 NCT6776_REG_TEMP_ALTERNATE[ARRAY_SIZE(nct6776_temp_label) - 1]
|
||||
= { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x401, 0x402, 0x404 };
|
||||
#define NCT6776_TEMP_MASK 0x007ffffe
|
||||
|
||||
static const u16 NCT6776_REG_TEMP_CRIT[ARRAY_SIZE(nct6776_temp_label) - 1]
|
||||
= { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x709, 0x70a };
|
||||
static const u16 NCT6776_REG_TEMP_ALTERNATE[32] = {
|
||||
[14] = 0x401,
|
||||
[15] = 0x402,
|
||||
[16] = 0x404,
|
||||
};
|
||||
|
||||
static const u16 NCT6776_REG_TEMP_CRIT[32] = {
|
||||
[11] = 0x709,
|
||||
[12] = 0x70a,
|
||||
};
|
||||
|
||||
/* NCT6779 specific data */
|
||||
|
||||
@ -525,17 +552,19 @@ static const char *const nct6779_temp_label[] = {
|
||||
"Virtual_TEMP"
|
||||
};
|
||||
|
||||
#define NCT6779_NUM_LABELS (ARRAY_SIZE(nct6779_temp_label) - 5)
|
||||
#define NCT6791_NUM_LABELS ARRAY_SIZE(nct6779_temp_label)
|
||||
#define NCT6779_TEMP_MASK 0x07ffff7e
|
||||
#define NCT6791_TEMP_MASK 0x87ffff7e
|
||||
|
||||
static const u16 NCT6779_REG_TEMP_ALTERNATE[NCT6791_NUM_LABELS - 1]
|
||||
static const u16 NCT6779_REG_TEMP_ALTERNATE[32]
|
||||
= { 0x490, 0x491, 0x492, 0x493, 0x494, 0x495, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0x400, 0x401, 0x402, 0x404, 0x405, 0x406, 0x407,
|
||||
0x408, 0 };
|
||||
|
||||
static const u16 NCT6779_REG_TEMP_CRIT[NCT6791_NUM_LABELS - 1]
|
||||
= { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x709, 0x70a };
|
||||
static const u16 NCT6779_REG_TEMP_CRIT[32] = {
|
||||
[15] = 0x709,
|
||||
[16] = 0x70a,
|
||||
};
|
||||
|
||||
/* NCT6791 specific data */
|
||||
|
||||
@ -602,6 +631,8 @@ static const char *const nct6792_temp_label[] = {
|
||||
"Virtual_TEMP"
|
||||
};
|
||||
|
||||
#define NCT6792_TEMP_MASK 0x9fffff7e
|
||||
|
||||
static const char *const nct6793_temp_label[] = {
|
||||
"",
|
||||
"SYSTIN",
|
||||
@ -637,6 +668,45 @@ static const char *const nct6793_temp_label[] = {
|
||||
"Virtual_TEMP"
|
||||
};
|
||||
|
||||
#define NCT6793_TEMP_MASK 0xbfff037e
|
||||
|
||||
static const char *const nct6795_temp_label[] = {
|
||||
"",
|
||||
"SYSTIN",
|
||||
"CPUTIN",
|
||||
"AUXTIN0",
|
||||
"AUXTIN1",
|
||||
"AUXTIN2",
|
||||
"AUXTIN3",
|
||||
"",
|
||||
"SMBUSMASTER 0",
|
||||
"SMBUSMASTER 1",
|
||||
"SMBUSMASTER 2",
|
||||
"SMBUSMASTER 3",
|
||||
"SMBUSMASTER 4",
|
||||
"SMBUSMASTER 5",
|
||||
"SMBUSMASTER 6",
|
||||
"SMBUSMASTER 7",
|
||||
"PECI Agent 0",
|
||||
"PECI Agent 1",
|
||||
"PCH_CHIP_CPU_MAX_TEMP",
|
||||
"PCH_CHIP_TEMP",
|
||||
"PCH_CPU_TEMP",
|
||||
"PCH_MCH_TEMP",
|
||||
"PCH_DIM0_TEMP",
|
||||
"PCH_DIM1_TEMP",
|
||||
"PCH_DIM2_TEMP",
|
||||
"PCH_DIM3_TEMP",
|
||||
"BYTE_TEMP0",
|
||||
"BYTE_TEMP1",
|
||||
"PECI Agent 0 Calibration",
|
||||
"PECI Agent 1 Calibration",
|
||||
"",
|
||||
"Virtual_TEMP"
|
||||
};
|
||||
|
||||
#define NCT6795_TEMP_MASK 0xbfffff7e
|
||||
|
||||
/* NCT6102D/NCT6106D specific data */
|
||||
|
||||
#define NCT6106_REG_VBAT 0x318
|
||||
@ -731,11 +801,16 @@ static const s8 NCT6106_BEEP_BITS[] = {
|
||||
34, -1 /* intrusion0, intrusion1 */
|
||||
};
|
||||
|
||||
static const u16 NCT6106_REG_TEMP_ALTERNATE[ARRAY_SIZE(nct6776_temp_label) - 1]
|
||||
= { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x51, 0x52, 0x54 };
|
||||
static const u16 NCT6106_REG_TEMP_ALTERNATE[32] = {
|
||||
[14] = 0x51,
|
||||
[15] = 0x52,
|
||||
[16] = 0x54,
|
||||
};
|
||||
|
||||
static const u16 NCT6106_REG_TEMP_CRIT[ARRAY_SIZE(nct6776_temp_label) - 1]
|
||||
= { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x204, 0x205 };
|
||||
static const u16 NCT6106_REG_TEMP_CRIT[32] = {
|
||||
[11] = 0x204,
|
||||
[12] = 0x205,
|
||||
};
|
||||
|
||||
static enum pwm_enable reg_to_pwm_enable(int pwm, int mode)
|
||||
{
|
||||
@ -810,7 +885,7 @@ static u16 fan_to_reg(u32 fan, unsigned int divreg)
|
||||
static inline unsigned int
|
||||
div_from_reg(u8 reg)
|
||||
{
|
||||
return 1 << reg;
|
||||
return BIT(reg);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -850,7 +925,7 @@ struct nct6775_data {
|
||||
u8 temp_src[NUM_TEMP];
|
||||
u16 reg_temp_config[NUM_TEMP];
|
||||
const char * const *temp_label;
|
||||
int temp_label_num;
|
||||
u32 temp_mask;
|
||||
|
||||
u16 REG_CONFIG;
|
||||
u16 REG_VBAT;
|
||||
@ -1155,6 +1230,7 @@ static bool is_word_sized(struct nct6775_data *data, u16 reg)
|
||||
case nct6791:
|
||||
case nct6792:
|
||||
case nct6793:
|
||||
case nct6795:
|
||||
return reg == 0x150 || reg == 0x153 || reg == 0x155 ||
|
||||
((reg & 0xfff0) == 0x4b0 && (reg & 0x000f) < 0x0b) ||
|
||||
reg == 0x402 ||
|
||||
@ -1276,7 +1352,7 @@ static void nct6775_update_fan_div(struct nct6775_data *data)
|
||||
data->fan_div[1] = (i & 0x70) >> 4;
|
||||
i = nct6775_read_value(data, NCT6775_REG_FANDIV2);
|
||||
data->fan_div[2] = i & 0x7;
|
||||
if (data->has_fan & (1 << 3))
|
||||
if (data->has_fan & BIT(3))
|
||||
data->fan_div[3] = (i & 0x70) >> 4;
|
||||
}
|
||||
|
||||
@ -1298,7 +1374,7 @@ static void nct6775_init_fan_div(struct nct6775_data *data)
|
||||
* We'll compute a better divider later on.
|
||||
*/
|
||||
for (i = 0; i < ARRAY_SIZE(data->fan_div); i++) {
|
||||
if (!(data->has_fan & (1 << i)))
|
||||
if (!(data->has_fan & BIT(i)))
|
||||
continue;
|
||||
if (data->fan_div[i] == 0) {
|
||||
data->fan_div[i] = 7;
|
||||
@ -1321,7 +1397,7 @@ static void nct6775_init_fan_common(struct device *dev,
|
||||
* prevents the unnecessary warning when fanX_min is reported as 0.
|
||||
*/
|
||||
for (i = 0; i < ARRAY_SIZE(data->fan_min); i++) {
|
||||
if (data->has_fan_min & (1 << i)) {
|
||||
if (data->has_fan_min & BIT(i)) {
|
||||
reg = nct6775_read_value(data, data->REG_FAN_MIN[i]);
|
||||
if (!reg)
|
||||
nct6775_write_value(data, data->REG_FAN_MIN[i],
|
||||
@ -1356,7 +1432,7 @@ static void nct6775_select_fan_div(struct device *dev,
|
||||
div_from_reg(fan_div));
|
||||
|
||||
/* Preserve min limit if possible */
|
||||
if (data->has_fan_min & (1 << nr)) {
|
||||
if (data->has_fan_min & BIT(nr)) {
|
||||
fan_min = data->fan_min[nr];
|
||||
if (fan_div > data->fan_div[nr]) {
|
||||
if (fan_min != 255 && fan_min > 1)
|
||||
@ -1387,7 +1463,7 @@ static void nct6775_update_pwm(struct device *dev)
|
||||
bool duty_is_dc;
|
||||
|
||||
for (i = 0; i < data->pwm_num; i++) {
|
||||
if (!(data->has_pwm & (1 << i)))
|
||||
if (!(data->has_pwm & BIT(i)))
|
||||
continue;
|
||||
|
||||
duty_is_dc = data->REG_PWM_MODE[i] &&
|
||||
@ -1457,7 +1533,7 @@ static void nct6775_update_pwm_limits(struct device *dev)
|
||||
u16 reg_t;
|
||||
|
||||
for (i = 0; i < data->pwm_num; i++) {
|
||||
if (!(data->has_pwm & (1 << i)))
|
||||
if (!(data->has_pwm & BIT(i)))
|
||||
continue;
|
||||
|
||||
for (j = 0; j < ARRAY_SIZE(data->fan_time); j++) {
|
||||
@ -1507,6 +1583,7 @@ static void nct6775_update_pwm_limits(struct device *dev)
|
||||
case nct6791:
|
||||
case nct6792:
|
||||
case nct6793:
|
||||
case nct6795:
|
||||
reg = nct6775_read_value(data,
|
||||
data->REG_CRITICAL_PWM_ENABLE[i]);
|
||||
if (reg & data->CRITICAL_PWM_ENABLE_MASK)
|
||||
@ -1534,7 +1611,7 @@ static struct nct6775_data *nct6775_update_device(struct device *dev)
|
||||
|
||||
/* Measured voltages and limits */
|
||||
for (i = 0; i < data->in_num; i++) {
|
||||
if (!(data->have_in & (1 << i)))
|
||||
if (!(data->have_in & BIT(i)))
|
||||
continue;
|
||||
|
||||
data->in[i][0] = nct6775_read_value(data,
|
||||
@ -1549,14 +1626,14 @@ static struct nct6775_data *nct6775_update_device(struct device *dev)
|
||||
for (i = 0; i < ARRAY_SIZE(data->rpm); i++) {
|
||||
u16 reg;
|
||||
|
||||
if (!(data->has_fan & (1 << i)))
|
||||
if (!(data->has_fan & BIT(i)))
|
||||
continue;
|
||||
|
||||
reg = nct6775_read_value(data, data->REG_FAN[i]);
|
||||
data->rpm[i] = data->fan_from_reg(reg,
|
||||
data->fan_div[i]);
|
||||
|
||||
if (data->has_fan_min & (1 << i))
|
||||
if (data->has_fan_min & BIT(i))
|
||||
data->fan_min[i] = nct6775_read_value(data,
|
||||
data->REG_FAN_MIN[i]);
|
||||
data->fan_pulses[i] =
|
||||
@ -1571,7 +1648,7 @@ static struct nct6775_data *nct6775_update_device(struct device *dev)
|
||||
|
||||
/* Measured temperatures and limits */
|
||||
for (i = 0; i < NUM_TEMP; i++) {
|
||||
if (!(data->have_temp & (1 << i)))
|
||||
if (!(data->have_temp & BIT(i)))
|
||||
continue;
|
||||
for (j = 0; j < ARRAY_SIZE(data->reg_temp); j++) {
|
||||
if (data->reg_temp[j][i])
|
||||
@ -1580,7 +1657,7 @@ static struct nct6775_data *nct6775_update_device(struct device *dev)
|
||||
data->reg_temp[j][i]);
|
||||
}
|
||||
if (i >= NUM_TEMP_FIXED ||
|
||||
!(data->have_temp_fixed & (1 << i)))
|
||||
!(data->have_temp_fixed & BIT(i)))
|
||||
continue;
|
||||
data->temp_offset[i]
|
||||
= nct6775_read_value(data, data->REG_TEMP_OFFSET[i]);
|
||||
@ -1801,7 +1878,7 @@ static umode_t nct6775_in_is_visible(struct kobject *kobj,
|
||||
struct nct6775_data *data = dev_get_drvdata(dev);
|
||||
int in = index / 5; /* voltage index */
|
||||
|
||||
if (!(data->have_in & (1 << in)))
|
||||
if (!(data->have_in & BIT(in)))
|
||||
return 0;
|
||||
|
||||
return attr->mode;
|
||||
@ -1911,7 +1988,7 @@ store_fan_min(struct device *dev, struct device_attribute *attr,
|
||||
* even with the highest divider (128)
|
||||
*/
|
||||
data->fan_min[nr] = 254;
|
||||
new_div = 7; /* 128 == (1 << 7) */
|
||||
new_div = 7; /* 128 == BIT(7) */
|
||||
dev_warn(dev,
|
||||
"fan%u low limit %lu below minimum %u, set to minimum\n",
|
||||
nr + 1, val, data->fan_from_reg_min(254, 7));
|
||||
@ -1921,7 +1998,7 @@ store_fan_min(struct device *dev, struct device_attribute *attr,
|
||||
* even with the lowest divider (1)
|
||||
*/
|
||||
data->fan_min[nr] = 1;
|
||||
new_div = 0; /* 1 == (1 << 0) */
|
||||
new_div = 0; /* 1 == BIT(0) */
|
||||
dev_warn(dev,
|
||||
"fan%u low limit %lu above maximum %u, set to maximum\n",
|
||||
nr + 1, val, data->fan_from_reg_min(1, 0));
|
||||
@ -2008,14 +2085,14 @@ static umode_t nct6775_fan_is_visible(struct kobject *kobj,
|
||||
int fan = index / 6; /* fan index */
|
||||
int nr = index % 6; /* attribute index */
|
||||
|
||||
if (!(data->has_fan & (1 << fan)))
|
||||
if (!(data->has_fan & BIT(fan)))
|
||||
return 0;
|
||||
|
||||
if (nr == 1 && data->ALARM_BITS[FAN_ALARM_BASE + fan] == -1)
|
||||
return 0;
|
||||
if (nr == 2 && data->BEEP_BITS[FAN_ALARM_BASE + fan] == -1)
|
||||
return 0;
|
||||
if (nr == 4 && !(data->has_fan_min & (1 << fan)))
|
||||
if (nr == 4 && !(data->has_fan_min & BIT(fan)))
|
||||
return 0;
|
||||
if (nr == 5 && data->kind != nct6775)
|
||||
return 0;
|
||||
@ -2193,7 +2270,10 @@ static umode_t nct6775_temp_is_visible(struct kobject *kobj,
|
||||
int temp = index / 10; /* temp index */
|
||||
int nr = index % 10; /* attribute index */
|
||||
|
||||
if (!(data->have_temp & (1 << temp)))
|
||||
if (!(data->have_temp & BIT(temp)))
|
||||
return 0;
|
||||
|
||||
if (nr == 1 && !data->temp_label)
|
||||
return 0;
|
||||
|
||||
if (nr == 2 && find_temp_source(data, temp, data->num_temp_alarms) < 0)
|
||||
@ -2215,7 +2295,7 @@ static umode_t nct6775_temp_is_visible(struct kobject *kobj,
|
||||
return 0;
|
||||
|
||||
/* offset and type only apply to fixed sensors */
|
||||
if (nr > 7 && !(data->have_temp_fixed & (1 << temp)))
|
||||
if (nr > 7 && !(data->have_temp_fixed & BIT(temp)))
|
||||
return 0;
|
||||
|
||||
return attr->mode;
|
||||
@ -2484,7 +2564,7 @@ show_pwm_temp_sel_common(struct nct6775_data *data, char *buf, int src)
|
||||
int i, sel = 0;
|
||||
|
||||
for (i = 0; i < NUM_TEMP; i++) {
|
||||
if (!(data->have_temp & (1 << i)))
|
||||
if (!(data->have_temp & BIT(i)))
|
||||
continue;
|
||||
if (src == data->temp_src[i]) {
|
||||
sel = i + 1;
|
||||
@ -2520,7 +2600,7 @@ store_pwm_temp_sel(struct device *dev, struct device_attribute *attr,
|
||||
return err;
|
||||
if (val == 0 || val > NUM_TEMP)
|
||||
return -EINVAL;
|
||||
if (!(data->have_temp & (1 << (val - 1))) || !data->temp_src[val - 1])
|
||||
if (!(data->have_temp & BIT(val - 1)) || !data->temp_src[val - 1])
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
@ -2562,7 +2642,7 @@ store_pwm_weight_temp_sel(struct device *dev, struct device_attribute *attr,
|
||||
return err;
|
||||
if (val > NUM_TEMP)
|
||||
return -EINVAL;
|
||||
if (val && (!(data->have_temp & (1 << (val - 1))) ||
|
||||
if (val && (!(data->have_temp & BIT(val - 1)) ||
|
||||
!data->temp_src[val - 1]))
|
||||
return -EINVAL;
|
||||
|
||||
@ -2923,6 +3003,7 @@ store_auto_pwm(struct device *dev, struct device_attribute *attr,
|
||||
case nct6791:
|
||||
case nct6792:
|
||||
case nct6793:
|
||||
case nct6795:
|
||||
nct6775_write_value(data, data->REG_CRITICAL_PWM[nr],
|
||||
val);
|
||||
reg = nct6775_read_value(data,
|
||||
@ -2995,7 +3076,7 @@ static umode_t nct6775_pwm_is_visible(struct kobject *kobj,
|
||||
int pwm = index / 36; /* pwm index */
|
||||
int nr = index % 36; /* attribute index */
|
||||
|
||||
if (!(data->has_pwm & (1 << pwm)))
|
||||
if (!(data->has_pwm & BIT(pwm)))
|
||||
return 0;
|
||||
|
||||
if ((nr >= 14 && nr <= 18) || nr == 21) /* weight */
|
||||
@ -3246,7 +3327,7 @@ static inline void nct6775_init_device(struct nct6775_data *data)
|
||||
|
||||
/* Enable temperature sensors if needed */
|
||||
for (i = 0; i < NUM_TEMP; i++) {
|
||||
if (!(data->have_temp & (1 << i)))
|
||||
if (!(data->have_temp & BIT(i)))
|
||||
continue;
|
||||
if (!data->reg_temp_config[i])
|
||||
continue;
|
||||
@ -3264,7 +3345,7 @@ static inline void nct6775_init_device(struct nct6775_data *data)
|
||||
diode = nct6775_read_value(data, data->REG_DIODE);
|
||||
|
||||
for (i = 0; i < data->temp_fixed_num; i++) {
|
||||
if (!(data->have_temp_fixed & (1 << i)))
|
||||
if (!(data->have_temp_fixed & BIT(i)))
|
||||
continue;
|
||||
if ((tmp & (data->DIODE_MASK << i))) /* diode */
|
||||
data->temp_type[i]
|
||||
@ -3290,8 +3371,8 @@ nct6775_check_fan_inputs(struct nct6775_data *data)
|
||||
if (data->kind == nct6775) {
|
||||
regval = superio_inb(sioreg, 0x2c);
|
||||
|
||||
fan3pin = regval & (1 << 6);
|
||||
pwm3pin = regval & (1 << 7);
|
||||
fan3pin = regval & BIT(6);
|
||||
pwm3pin = regval & BIT(7);
|
||||
|
||||
/* On NCT6775, fan4 shares pins with the fdc interface */
|
||||
fan4pin = !(superio_inb(sioreg, 0x2A) & 0x80);
|
||||
@ -3357,28 +3438,57 @@ nct6775_check_fan_inputs(struct nct6775_data *data)
|
||||
pwm4pin = false;
|
||||
pwm5pin = false;
|
||||
pwm6pin = false;
|
||||
} else { /* NCT6779D, NCT6791D, NCT6792D, or NCT6793D */
|
||||
} else { /* NCT6779D, NCT6791D, NCT6792D, NCT6793D, or NCT6795D */
|
||||
int regval_1b, regval_2a, regval_eb;
|
||||
|
||||
regval = superio_inb(sioreg, 0x1c);
|
||||
|
||||
fan3pin = !(regval & (1 << 5));
|
||||
fan4pin = !(regval & (1 << 6));
|
||||
fan5pin = !(regval & (1 << 7));
|
||||
fan3pin = !(regval & BIT(5));
|
||||
fan4pin = !(regval & BIT(6));
|
||||
fan5pin = !(regval & BIT(7));
|
||||
|
||||
pwm3pin = !(regval & (1 << 0));
|
||||
pwm4pin = !(regval & (1 << 1));
|
||||
pwm5pin = !(regval & (1 << 2));
|
||||
pwm3pin = !(regval & BIT(0));
|
||||
pwm4pin = !(regval & BIT(1));
|
||||
pwm5pin = !(regval & BIT(2));
|
||||
|
||||
fan4min = fan4pin;
|
||||
regval = superio_inb(sioreg, 0x2d);
|
||||
switch (data->kind) {
|
||||
case nct6791:
|
||||
case nct6792:
|
||||
fan6pin = regval & BIT(1);
|
||||
pwm6pin = regval & BIT(0);
|
||||
break;
|
||||
case nct6793:
|
||||
case nct6795:
|
||||
regval_1b = superio_inb(sioreg, 0x1b);
|
||||
regval_2a = superio_inb(sioreg, 0x2a);
|
||||
|
||||
if (data->kind == nct6791 || data->kind == nct6792 ||
|
||||
data->kind == nct6793) {
|
||||
regval = superio_inb(sioreg, 0x2d);
|
||||
fan6pin = (regval & (1 << 1));
|
||||
pwm6pin = (regval & (1 << 0));
|
||||
} else { /* NCT6779D */
|
||||
if (!pwm5pin)
|
||||
pwm5pin = regval & BIT(7);
|
||||
fan6pin = regval & BIT(1);
|
||||
pwm6pin = regval & BIT(0);
|
||||
if (!fan5pin)
|
||||
fan5pin = regval_1b & BIT(5);
|
||||
|
||||
superio_select(sioreg, NCT6775_LD_12);
|
||||
regval_eb = superio_inb(sioreg, 0xeb);
|
||||
if (!fan5pin)
|
||||
fan5pin = regval_eb & BIT(5);
|
||||
if (!pwm5pin)
|
||||
pwm5pin = (regval_eb & BIT(4)) &&
|
||||
!(regval_2a & BIT(0));
|
||||
if (!fan6pin)
|
||||
fan6pin = regval_eb & BIT(3);
|
||||
if (!pwm6pin)
|
||||
pwm6pin = regval_eb & BIT(2);
|
||||
break;
|
||||
default: /* NCT6779D */
|
||||
fan6pin = false;
|
||||
pwm6pin = false;
|
||||
break;
|
||||
}
|
||||
|
||||
fan4min = fan4pin;
|
||||
}
|
||||
|
||||
/* fan 1 and 2 (0x03) are always present */
|
||||
@ -3403,16 +3513,15 @@ static void add_temp_sensors(struct nct6775_data *data, const u16 *regp,
|
||||
continue;
|
||||
src = nct6775_read_value(data, regp[i]);
|
||||
src &= 0x1f;
|
||||
if (!src || (*mask & (1 << src)))
|
||||
if (!src || (*mask & BIT(src)))
|
||||
continue;
|
||||
if (src >= data->temp_label_num ||
|
||||
!strlen(data->temp_label[src]))
|
||||
if (!(data->temp_mask & BIT(src)))
|
||||
continue;
|
||||
|
||||
index = __ffs(*available);
|
||||
nct6775_write_value(data, data->REG_TEMP_SOURCE[index], src);
|
||||
*available &= ~(1 << index);
|
||||
*mask |= 1 << src;
|
||||
*available &= ~BIT(index);
|
||||
*mask |= BIT(src);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3464,7 +3573,7 @@ static int nct6775_probe(struct platform_device *pdev)
|
||||
data->fan_from_reg_min = fan_from_reg13;
|
||||
|
||||
data->temp_label = nct6776_temp_label;
|
||||
data->temp_label_num = ARRAY_SIZE(nct6776_temp_label);
|
||||
data->temp_mask = NCT6776_TEMP_MASK;
|
||||
|
||||
data->REG_VBAT = NCT6106_REG_VBAT;
|
||||
data->REG_DIODE = NCT6106_REG_DIODE;
|
||||
@ -3542,7 +3651,7 @@ static int nct6775_probe(struct platform_device *pdev)
|
||||
data->speed_tolerance_limit = 15;
|
||||
|
||||
data->temp_label = nct6775_temp_label;
|
||||
data->temp_label_num = ARRAY_SIZE(nct6775_temp_label);
|
||||
data->temp_mask = NCT6775_TEMP_MASK;
|
||||
|
||||
data->REG_CONFIG = NCT6775_REG_CONFIG;
|
||||
data->REG_VBAT = NCT6775_REG_VBAT;
|
||||
@ -3614,7 +3723,7 @@ static int nct6775_probe(struct platform_device *pdev)
|
||||
data->speed_tolerance_limit = 63;
|
||||
|
||||
data->temp_label = nct6776_temp_label;
|
||||
data->temp_label_num = ARRAY_SIZE(nct6776_temp_label);
|
||||
data->temp_mask = NCT6776_TEMP_MASK;
|
||||
|
||||
data->REG_CONFIG = NCT6775_REG_CONFIG;
|
||||
data->REG_VBAT = NCT6775_REG_VBAT;
|
||||
@ -3686,7 +3795,7 @@ static int nct6775_probe(struct platform_device *pdev)
|
||||
data->speed_tolerance_limit = 63;
|
||||
|
||||
data->temp_label = nct6779_temp_label;
|
||||
data->temp_label_num = NCT6779_NUM_LABELS;
|
||||
data->temp_mask = NCT6779_TEMP_MASK;
|
||||
|
||||
data->REG_CONFIG = NCT6775_REG_CONFIG;
|
||||
data->REG_VBAT = NCT6775_REG_VBAT;
|
||||
@ -3746,6 +3855,7 @@ static int nct6775_probe(struct platform_device *pdev)
|
||||
case nct6791:
|
||||
case nct6792:
|
||||
case nct6793:
|
||||
case nct6795:
|
||||
data->in_num = 15;
|
||||
data->pwm_num = 6;
|
||||
data->auto_pwm_num = 4;
|
||||
@ -3767,15 +3877,21 @@ static int nct6775_probe(struct platform_device *pdev)
|
||||
default:
|
||||
case nct6791:
|
||||
data->temp_label = nct6779_temp_label;
|
||||
data->temp_mask = NCT6791_TEMP_MASK;
|
||||
break;
|
||||
case nct6792:
|
||||
data->temp_label = nct6792_temp_label;
|
||||
data->temp_mask = NCT6792_TEMP_MASK;
|
||||
break;
|
||||
case nct6793:
|
||||
data->temp_label = nct6793_temp_label;
|
||||
data->temp_mask = NCT6793_TEMP_MASK;
|
||||
break;
|
||||
case nct6795:
|
||||
data->temp_label = nct6795_temp_label;
|
||||
data->temp_mask = NCT6795_TEMP_MASK;
|
||||
break;
|
||||
}
|
||||
data->temp_label_num = NCT6791_NUM_LABELS;
|
||||
|
||||
data->REG_CONFIG = NCT6775_REG_CONFIG;
|
||||
data->REG_VBAT = NCT6775_REG_VBAT;
|
||||
@ -3843,7 +3959,7 @@ static int nct6775_probe(struct platform_device *pdev)
|
||||
default:
|
||||
return -ENODEV;
|
||||
}
|
||||
data->have_in = (1 << data->in_num) - 1;
|
||||
data->have_in = BIT(data->in_num) - 1;
|
||||
data->have_temp = 0;
|
||||
|
||||
/*
|
||||
@ -3861,10 +3977,10 @@ static int nct6775_probe(struct platform_device *pdev)
|
||||
continue;
|
||||
|
||||
src = nct6775_read_value(data, data->REG_TEMP_SOURCE[i]) & 0x1f;
|
||||
if (!src || (mask & (1 << src)))
|
||||
available |= 1 << i;
|
||||
if (!src || (mask & BIT(src)))
|
||||
available |= BIT(i);
|
||||
|
||||
mask |= 1 << src;
|
||||
mask |= BIT(src);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -3881,23 +3997,22 @@ static int nct6775_probe(struct platform_device *pdev)
|
||||
continue;
|
||||
|
||||
src = nct6775_read_value(data, data->REG_TEMP_SOURCE[i]) & 0x1f;
|
||||
if (!src || (mask & (1 << src)))
|
||||
if (!src || (mask & BIT(src)))
|
||||
continue;
|
||||
|
||||
if (src >= data->temp_label_num ||
|
||||
!strlen(data->temp_label[src])) {
|
||||
if (!(data->temp_mask & BIT(src))) {
|
||||
dev_info(dev,
|
||||
"Invalid temperature source %d at index %d, source register 0x%x, temp register 0x%x\n",
|
||||
src, i, data->REG_TEMP_SOURCE[i], reg_temp[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
mask |= 1 << src;
|
||||
mask |= BIT(src);
|
||||
|
||||
/* Use fixed index for SYSTIN(1), CPUTIN(2), AUXTIN(3) */
|
||||
if (src <= data->temp_fixed_num) {
|
||||
data->have_temp |= 1 << (src - 1);
|
||||
data->have_temp_fixed |= 1 << (src - 1);
|
||||
data->have_temp |= BIT(src - 1);
|
||||
data->have_temp_fixed |= BIT(src - 1);
|
||||
data->reg_temp[0][src - 1] = reg_temp[i];
|
||||
data->reg_temp[1][src - 1] = reg_temp_over[i];
|
||||
data->reg_temp[2][src - 1] = reg_temp_hyst[i];
|
||||
@ -3917,7 +4032,7 @@ static int nct6775_probe(struct platform_device *pdev)
|
||||
continue;
|
||||
|
||||
/* Use dynamic index for other sources */
|
||||
data->have_temp |= 1 << s;
|
||||
data->have_temp |= BIT(s);
|
||||
data->reg_temp[0][s] = reg_temp[i];
|
||||
data->reg_temp[1][s] = reg_temp_over[i];
|
||||
data->reg_temp[2][s] = reg_temp_hyst[i];
|
||||
@ -3945,8 +4060,7 @@ static int nct6775_probe(struct platform_device *pdev)
|
||||
if (!src)
|
||||
continue;
|
||||
|
||||
if (src >= data->temp_label_num ||
|
||||
!strlen(data->temp_label[src])) {
|
||||
if (!(data->temp_mask & BIT(src))) {
|
||||
dev_info(dev,
|
||||
"Invalid temperature source %d at index %d, source register 0x%x, temp register 0x%x\n",
|
||||
src, i, data->REG_TEMP_SEL[i],
|
||||
@ -3960,17 +4074,17 @@ static int nct6775_probe(struct platform_device *pdev)
|
||||
* are no duplicates.
|
||||
*/
|
||||
if (src != TEMP_SOURCE_VIRTUAL) {
|
||||
if (mask & (1 << src))
|
||||
if (mask & BIT(src))
|
||||
continue;
|
||||
mask |= 1 << src;
|
||||
mask |= BIT(src);
|
||||
}
|
||||
|
||||
/* Use fixed index for SYSTIN(1), CPUTIN(2), AUXTIN(3) */
|
||||
if (src <= data->temp_fixed_num) {
|
||||
if (data->have_temp & (1 << (src - 1)))
|
||||
if (data->have_temp & BIT(src - 1))
|
||||
continue;
|
||||
data->have_temp |= 1 << (src - 1);
|
||||
data->have_temp_fixed |= 1 << (src - 1);
|
||||
data->have_temp |= BIT(src - 1);
|
||||
data->have_temp_fixed |= BIT(src - 1);
|
||||
data->reg_temp[0][src - 1] = reg_temp_mon[i];
|
||||
data->temp_src[src - 1] = src;
|
||||
continue;
|
||||
@ -3980,7 +4094,7 @@ static int nct6775_probe(struct platform_device *pdev)
|
||||
continue;
|
||||
|
||||
/* Use dynamic index for other sources */
|
||||
data->have_temp |= 1 << s;
|
||||
data->have_temp |= BIT(s);
|
||||
data->reg_temp[0][s] = reg_temp_mon[i];
|
||||
data->temp_src[s] = src;
|
||||
s++;
|
||||
@ -3993,16 +4107,18 @@ static int nct6775_probe(struct platform_device *pdev)
|
||||
* The temperature is already monitored if the respective bit in <mask>
|
||||
* is set.
|
||||
*/
|
||||
for (i = 0; i < data->temp_label_num - 1; i++) {
|
||||
for (i = 0; i < 32; i++) {
|
||||
if (!(data->temp_mask & BIT(i + 1)))
|
||||
continue;
|
||||
if (!reg_temp_alternate[i])
|
||||
continue;
|
||||
if (mask & (1 << (i + 1)))
|
||||
if (mask & BIT(i + 1))
|
||||
continue;
|
||||
if (i < data->temp_fixed_num) {
|
||||
if (data->have_temp & (1 << i))
|
||||
if (data->have_temp & BIT(i))
|
||||
continue;
|
||||
data->have_temp |= 1 << i;
|
||||
data->have_temp_fixed |= 1 << i;
|
||||
data->have_temp |= BIT(i);
|
||||
data->have_temp_fixed |= BIT(i);
|
||||
data->reg_temp[0][i] = reg_temp_alternate[i];
|
||||
if (i < num_reg_temp) {
|
||||
data->reg_temp[1][i] = reg_temp_over[i];
|
||||
@ -4015,7 +4131,7 @@ static int nct6775_probe(struct platform_device *pdev)
|
||||
if (s >= NUM_TEMP) /* Abort if no more space */
|
||||
break;
|
||||
|
||||
data->have_temp |= 1 << s;
|
||||
data->have_temp |= BIT(s);
|
||||
data->reg_temp[0][s] = reg_temp_alternate[i];
|
||||
data->temp_src[s] = i + 1;
|
||||
s++;
|
||||
@ -4042,6 +4158,7 @@ static int nct6775_probe(struct platform_device *pdev)
|
||||
case nct6791:
|
||||
case nct6792:
|
||||
case nct6793:
|
||||
case nct6795:
|
||||
break;
|
||||
}
|
||||
|
||||
@ -4075,6 +4192,7 @@ static int nct6775_probe(struct platform_device *pdev)
|
||||
case nct6791:
|
||||
case nct6792:
|
||||
case nct6793:
|
||||
case nct6795:
|
||||
tmp |= 0x7e;
|
||||
break;
|
||||
}
|
||||
@ -4173,14 +4291,14 @@ static int __maybe_unused nct6775_resume(struct device *dev)
|
||||
superio_outb(sioreg, SIO_REG_ENABLE, data->sio_reg_enable);
|
||||
|
||||
if (data->kind == nct6791 || data->kind == nct6792 ||
|
||||
data->kind == nct6793)
|
||||
data->kind == nct6793 || data->kind == nct6795)
|
||||
nct6791_enable_io_mapping(sioreg);
|
||||
|
||||
superio_exit(sioreg);
|
||||
|
||||
/* Restore limits */
|
||||
for (i = 0; i < data->in_num; i++) {
|
||||
if (!(data->have_in & (1 << i)))
|
||||
if (!(data->have_in & BIT(i)))
|
||||
continue;
|
||||
|
||||
nct6775_write_value(data, data->REG_IN_MINMAX[0][i],
|
||||
@ -4190,7 +4308,7 @@ static int __maybe_unused nct6775_resume(struct device *dev)
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(data->fan_min); i++) {
|
||||
if (!(data->has_fan_min & (1 << i)))
|
||||
if (!(data->has_fan_min & BIT(i)))
|
||||
continue;
|
||||
|
||||
nct6775_write_value(data, data->REG_FAN_MIN[i],
|
||||
@ -4198,7 +4316,7 @@ static int __maybe_unused nct6775_resume(struct device *dev)
|
||||
}
|
||||
|
||||
for (i = 0; i < NUM_TEMP; i++) {
|
||||
if (!(data->have_temp & (1 << i)))
|
||||
if (!(data->have_temp & BIT(i)))
|
||||
continue;
|
||||
|
||||
for (j = 1; j < ARRAY_SIZE(data->reg_temp); j++)
|
||||
@ -4270,6 +4388,9 @@ static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data)
|
||||
case SIO_NCT6793_ID:
|
||||
sio_data->kind = nct6793;
|
||||
break;
|
||||
case SIO_NCT6795_ID:
|
||||
sio_data->kind = nct6795;
|
||||
break;
|
||||
default:
|
||||
if (val != 0xffff)
|
||||
pr_debug("unsupported chip ID: 0x%04x\n", val);
|
||||
@ -4296,7 +4417,7 @@ static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data)
|
||||
}
|
||||
|
||||
if (sio_data->kind == nct6791 || sio_data->kind == nct6792 ||
|
||||
sio_data->kind == nct6793)
|
||||
sio_data->kind == nct6793 || sio_data->kind == nct6795)
|
||||
nct6791_enable_io_mapping(sioaddr);
|
||||
|
||||
superio_exit(sioaddr);
|
||||
|
@ -37,6 +37,16 @@ config SENSORS_ADM1275
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called adm1275.
|
||||
|
||||
config SENSORS_IR35221
|
||||
tristate "Infineon IR35221"
|
||||
default n
|
||||
help
|
||||
If you say yes here you get hardware monitoring support for the
|
||||
Infineon IR35221 controller.
|
||||
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called ir35521.
|
||||
|
||||
config SENSORS_LM25066
|
||||
tristate "National Semiconductor LM25066 and compatibles"
|
||||
default n
|
||||
|
@ -5,6 +5,7 @@
|
||||
obj-$(CONFIG_PMBUS) += pmbus_core.o
|
||||
obj-$(CONFIG_SENSORS_PMBUS) += pmbus.o
|
||||
obj-$(CONFIG_SENSORS_ADM1275) += adm1275.o
|
||||
obj-$(CONFIG_SENSORS_IR35221) += ir35221.o
|
||||
obj-$(CONFIG_SENSORS_LM25066) += lm25066.o
|
||||
obj-$(CONFIG_SENSORS_LTC2978) += ltc2978.o
|
||||
obj-$(CONFIG_SENSORS_LTC3815) += ltc3815.o
|
||||
|
337
drivers/hwmon/pmbus/ir35221.c
Normal file
337
drivers/hwmon/pmbus/ir35221.c
Normal file
@ -0,0 +1,337 @@
|
||||
/*
|
||||
* Hardware monitoring driver for IR35221
|
||||
*
|
||||
* Copyright (C) IBM Corporation 2017.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include "pmbus.h"
|
||||
|
||||
#define IR35221_MFR_VIN_PEAK 0xc5
|
||||
#define IR35221_MFR_VOUT_PEAK 0xc6
|
||||
#define IR35221_MFR_IOUT_PEAK 0xc7
|
||||
#define IR35221_MFR_TEMP_PEAK 0xc8
|
||||
#define IR35221_MFR_VIN_VALLEY 0xc9
|
||||
#define IR35221_MFR_VOUT_VALLEY 0xca
|
||||
#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);
|
||||
break;
|
||||
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,
|
||||
IR35221_MFR_VOUT_VALLEY);
|
||||
break;
|
||||
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,
|
||||
IR35221_MFR_TEMP_VALLEY);
|
||||
break;
|
||||
default:
|
||||
ret = -ENODATA;
|
||||
break;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct pmbus_driver_info *info;
|
||||
u8 buf[I2C_SMBUS_BLOCK_MAX];
|
||||
int ret;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_READ_BYTE_DATA
|
||||
| I2C_FUNC_SMBUS_READ_WORD_DATA
|
||||
| I2C_FUNC_SMBUS_READ_BLOCK_DATA))
|
||||
return -ENODEV;
|
||||
|
||||
ret = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, buf);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Failed to read PMBUS_MFR_ID\n");
|
||||
return ret;
|
||||
}
|
||||
if (ret != 2 || strncmp(buf, "RI", strlen("RI"))) {
|
||||
dev_err(&client->dev, "MFR_ID unrecognised\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, buf);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Failed to read PMBUS_MFR_MODEL\n");
|
||||
return ret;
|
||||
}
|
||||
if (ret != 2 || !(buf[0] == 0x6c && buf[1] == 0x00)) {
|
||||
dev_err(&client->dev, "MFR_MODEL unrecognised\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
info = devm_kzalloc(&client->dev, sizeof(struct pmbus_driver_info),
|
||||
GFP_KERNEL);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
||||
info->write_word_data = ir35221_write_word_data;
|
||||
info->read_word_data = ir35221_read_word_data;
|
||||
|
||||
info->pages = 2;
|
||||
info->format[PSC_VOLTAGE_IN] = linear;
|
||||
info->format[PSC_VOLTAGE_OUT] = linear;
|
||||
info->format[PSC_CURRENT_IN] = linear;
|
||||
info->format[PSC_CURRENT_OUT] = linear;
|
||||
info->format[PSC_POWER] = linear;
|
||||
info->format[PSC_TEMPERATURE] = linear;
|
||||
|
||||
info->func[0] = PMBUS_HAVE_VIN
|
||||
| PMBUS_HAVE_VOUT | PMBUS_HAVE_IIN
|
||||
| PMBUS_HAVE_IOUT | PMBUS_HAVE_PIN
|
||||
| PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP
|
||||
| PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT
|
||||
| PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP;
|
||||
info->func[1] = info->func[0];
|
||||
|
||||
return pmbus_do_probe(client, id, info);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ir35221_id[] = {
|
||||
{"ir35221", 0},
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, ir35221_id);
|
||||
|
||||
static struct i2c_driver ir35221_driver = {
|
||||
.driver = {
|
||||
.name = "ir35221",
|
||||
},
|
||||
.probe = ir35221_probe,
|
||||
.remove = pmbus_do_remove,
|
||||
.id_table = ir35221_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(ir35221_driver);
|
||||
|
||||
MODULE_AUTHOR("Samuel Mendoza-Jonas <sam@mendozajonas.com");
|
||||
MODULE_DESCRIPTION("PMBus driver for IR35221");
|
||||
MODULE_LICENSE("GPL");
|
@ -25,7 +25,7 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c/pmbus.h>
|
||||
#include <linux/pmbus.h>
|
||||
#include "pmbus.h"
|
||||
|
||||
/*
|
||||
|
@ -28,7 +28,7 @@
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/i2c/pmbus.h>
|
||||
#include <linux/pmbus.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
#include "pmbus.h"
|
||||
|
@ -26,7 +26,7 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c/pmbus.h>
|
||||
#include <linux/pmbus.h>
|
||||
#include "pmbus.h"
|
||||
|
||||
enum chips { ucd9000, ucd90120, ucd90124, ucd90160, ucd9090, ucd90910 };
|
||||
|
@ -25,7 +25,7 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c/pmbus.h>
|
||||
#include <linux/pmbus.h>
|
||||
#include "pmbus.h"
|
||||
|
||||
#define UCD9200_PHASE_INFO 0xd2
|
||||
|
@ -40,31 +40,22 @@ struct pwm_fan_ctx {
|
||||
|
||||
static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm)
|
||||
{
|
||||
struct pwm_args pargs;
|
||||
unsigned long duty;
|
||||
unsigned long period;
|
||||
int ret = 0;
|
||||
|
||||
pwm_get_args(ctx->pwm, &pargs);
|
||||
struct pwm_state state = { };
|
||||
|
||||
mutex_lock(&ctx->lock);
|
||||
if (ctx->pwm_value == pwm)
|
||||
goto exit_set_pwm_err;
|
||||
|
||||
duty = DIV_ROUND_UP(pwm * (pargs.period - 1), MAX_PWM);
|
||||
ret = pwm_config(ctx->pwm, duty, pargs.period);
|
||||
if (ret)
|
||||
goto exit_set_pwm_err;
|
||||
pwm_init_state(ctx->pwm, &state);
|
||||
period = ctx->pwm->args.period;
|
||||
state.duty_cycle = DIV_ROUND_UP(pwm * (period - 1), MAX_PWM);
|
||||
state.enabled = pwm ? true : false;
|
||||
|
||||
if (pwm == 0)
|
||||
pwm_disable(ctx->pwm);
|
||||
|
||||
if (ctx->pwm_value == 0) {
|
||||
ret = pwm_enable(ctx->pwm);
|
||||
if (ret)
|
||||
goto exit_set_pwm_err;
|
||||
}
|
||||
|
||||
ctx->pwm_value = pwm;
|
||||
ret = pwm_apply_state(ctx->pwm, &state);
|
||||
if (!ret)
|
||||
ctx->pwm_value = pwm;
|
||||
exit_set_pwm_err:
|
||||
mutex_unlock(&ctx->lock);
|
||||
return ret;
|
||||
@ -218,10 +209,9 @@ static int pwm_fan_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct thermal_cooling_device *cdev;
|
||||
struct pwm_fan_ctx *ctx;
|
||||
struct pwm_args pargs;
|
||||
struct device *hwmon;
|
||||
int duty_cycle;
|
||||
int ret;
|
||||
struct pwm_state state = { };
|
||||
|
||||
ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
|
||||
if (!ctx)
|
||||
@ -237,37 +227,25 @@ static int pwm_fan_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, ctx);
|
||||
|
||||
/*
|
||||
* FIXME: pwm_apply_args() should be removed when switching to the
|
||||
* atomic PWM API.
|
||||
*/
|
||||
pwm_apply_args(ctx->pwm);
|
||||
|
||||
/* Set duty cycle to maximum allowed */
|
||||
pwm_get_args(ctx->pwm, &pargs);
|
||||
|
||||
duty_cycle = pargs.period - 1;
|
||||
ctx->pwm_value = MAX_PWM;
|
||||
|
||||
ret = pwm_config(ctx->pwm, duty_cycle, pargs.period);
|
||||
/* Set duty cycle to maximum allowed and enable PWM output */
|
||||
pwm_init_state(ctx->pwm, &state);
|
||||
state.duty_cycle = ctx->pwm->args.period - 1;
|
||||
state.enabled = true;
|
||||
|
||||
ret = pwm_apply_state(ctx->pwm, &state);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to configure PWM\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Enbale PWM output */
|
||||
ret = pwm_enable(ctx->pwm);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to enable PWM\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
hwmon = devm_hwmon_device_register_with_groups(&pdev->dev, "pwmfan",
|
||||
ctx, pwm_fan_groups);
|
||||
if (IS_ERR(hwmon)) {
|
||||
dev_err(&pdev->dev, "Failed to register hwmon device\n");
|
||||
pwm_disable(ctx->pwm);
|
||||
return PTR_ERR(hwmon);
|
||||
ret = PTR_ERR(hwmon);
|
||||
goto err_pwm_disable;
|
||||
}
|
||||
|
||||
ret = pwm_fan_of_get_cooling_data(&pdev->dev, ctx);
|
||||
@ -282,14 +260,20 @@ static int pwm_fan_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(cdev)) {
|
||||
dev_err(&pdev->dev,
|
||||
"Failed to register pwm-fan as cooling device");
|
||||
pwm_disable(ctx->pwm);
|
||||
return PTR_ERR(cdev);
|
||||
ret = PTR_ERR(cdev);
|
||||
goto err_pwm_disable;
|
||||
}
|
||||
ctx->cdev = cdev;
|
||||
thermal_cdev_update(cdev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_pwm_disable:
|
||||
state.enabled = false;
|
||||
pwm_apply_state(ctx->pwm, &state);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pwm_fan_remove(struct platform_device *pdev)
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/scpi_protocol.h>
|
||||
#include <linux/slab.h>
|
||||
@ -23,6 +24,7 @@
|
||||
#include <linux/thermal.h>
|
||||
|
||||
struct sensor_data {
|
||||
unsigned int scale;
|
||||
struct scpi_sensor_info info;
|
||||
struct device_attribute dev_attr_input;
|
||||
struct device_attribute dev_attr_label;
|
||||
@ -44,6 +46,30 @@ struct scpi_sensors {
|
||||
const struct attribute_group *groups[2];
|
||||
};
|
||||
|
||||
static const u32 gxbb_scpi_scale[] = {
|
||||
[TEMPERATURE] = 1, /* (celsius) */
|
||||
[VOLTAGE] = 1000, /* (millivolts) */
|
||||
[CURRENT] = 1000, /* (milliamperes) */
|
||||
[POWER] = 1000000, /* (microwatts) */
|
||||
[ENERGY] = 1000000, /* (microjoules) */
|
||||
};
|
||||
|
||||
static const u32 scpi_scale[] = {
|
||||
[TEMPERATURE] = 1000, /* (millicelsius) */
|
||||
[VOLTAGE] = 1000, /* (millivolts) */
|
||||
[CURRENT] = 1000, /* (milliamperes) */
|
||||
[POWER] = 1000000, /* (microwatts) */
|
||||
[ENERGY] = 1000000, /* (microjoules) */
|
||||
};
|
||||
|
||||
static void scpi_scale_reading(u64 *value, struct sensor_data *sensor)
|
||||
{
|
||||
if (scpi_scale[sensor->info.class] != sensor->scale) {
|
||||
*value *= scpi_scale[sensor->info.class];
|
||||
do_div(*value, sensor->scale);
|
||||
}
|
||||
}
|
||||
|
||||
static int scpi_read_temp(void *dev, int *temp)
|
||||
{
|
||||
struct scpi_thermal_zone *zone = dev;
|
||||
@ -57,6 +83,8 @@ static int scpi_read_temp(void *dev, int *temp)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
scpi_scale_reading(&value, sensor);
|
||||
|
||||
*temp = value;
|
||||
return 0;
|
||||
}
|
||||
@ -77,6 +105,8 @@ scpi_show_sensor(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
scpi_scale_reading(&value, sensor);
|
||||
|
||||
return sprintf(buf, "%llu\n", value);
|
||||
}
|
||||
|
||||
@ -94,14 +124,23 @@ static struct thermal_zone_of_device_ops scpi_sensor_ops = {
|
||||
.get_temp = scpi_read_temp,
|
||||
};
|
||||
|
||||
static const struct of_device_id scpi_of_match[] = {
|
||||
{.compatible = "arm,scpi-sensors", .data = &scpi_scale},
|
||||
{.compatible = "amlogic,meson-gxbb-scpi-sensors", .data = &gxbb_scpi_scale},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, scpi_of_match);
|
||||
|
||||
static int scpi_hwmon_probe(struct platform_device *pdev)
|
||||
{
|
||||
u16 nr_sensors, i;
|
||||
const u32 *scale;
|
||||
int num_temp = 0, num_volt = 0, num_current = 0, num_power = 0;
|
||||
int num_energy = 0;
|
||||
struct scpi_ops *scpi_ops;
|
||||
struct device *hwdev, *dev = &pdev->dev;
|
||||
struct scpi_sensors *scpi_sensors;
|
||||
const struct of_device_id *of_id;
|
||||
int idx, ret;
|
||||
|
||||
scpi_ops = get_scpi_ops();
|
||||
@ -131,6 +170,13 @@ static int scpi_hwmon_probe(struct platform_device *pdev)
|
||||
|
||||
scpi_sensors->scpi_ops = scpi_ops;
|
||||
|
||||
of_id = of_match_device(scpi_of_match, &pdev->dev);
|
||||
if (!of_id) {
|
||||
dev_err(&pdev->dev, "Unable to initialize scpi-hwmon data\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
scale = of_id->data;
|
||||
|
||||
for (i = 0, idx = 0; i < nr_sensors; i++) {
|
||||
struct sensor_data *sensor = &scpi_sensors->data[idx];
|
||||
|
||||
@ -178,6 +224,8 @@ static int scpi_hwmon_probe(struct platform_device *pdev)
|
||||
continue;
|
||||
}
|
||||
|
||||
sensor->scale = scale[sensor->info.class];
|
||||
|
||||
sensor->dev_attr_input.attr.mode = S_IRUGO;
|
||||
sensor->dev_attr_input.show = scpi_show_sensor;
|
||||
sensor->dev_attr_input.attr.name = sensor->input;
|
||||
@ -247,12 +295,6 @@ static int scpi_hwmon_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id scpi_of_match[] = {
|
||||
{.compatible = "arm,scpi-sensors"},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, scpi_of_match);
|
||||
|
||||
static struct platform_driver scpi_hwmon_platdrv = {
|
||||
.driver = {
|
||||
.name = "scpi-hwmon",
|
||||
|
@ -23,7 +23,7 @@
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <linux/i2c/ads1015.h>
|
||||
#include <linux/platform_data/ads1015.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/types.h>
|
||||
|
Loading…
Reference in New Issue
Block a user