hwmon updates for v4.7
- Major rework of it87 driver cleanup, added support for additional attributes, added support for two chips in the system, added support for IT8728E - fam17h_power driver now reports accumulated power consumption - New driver for MAX31722/MAX31723 temperature sensors - Minor fixes to sch5636 and ads7828 drivers -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJXORXcAAoJEMsfJm/On5mBFjEP/1w6Dio2IAR1e2aKVq99Vt/z /7Wv6QSNp2z6/M9fe4aCEb+Q2SmLqrA7XDwDA1ZOyD+j1x77PoSOomIWKO0sMGTu 08XHH3wHdXoJbcc+SV7OWuzcFGyfgJUWo+1XbwwAryd0V4sg++gjWdO7WcWs6NpI h/w/tE6/8efpwrc4DwnDV3epLqwMIWZFw+q6HU8N5Ka4yq+eXT/b/fwPs6t3NZGQ kic7mV9yRw0FehFsZTOn8CEdhiE/i4dNXnP3ybDOmYRXc7vFZb/7YeuwmBBBrDXM iHzKqV/kwEVaOBMYi+uClteK+gtWGa3EZ/Wm1p0m3Ud010w2CXai3+TKshtATAI9 UB1nqN/q5+4PasD9+gyS66u+AJ8rV/ucfQYH4B2imtPqQJ+YJt81PfXoQZW4II9X +2ZlgFw0Bcoq/jY+1ZHpaZT4jiwSHDPs9mvs0HOdkiyonEcBuQuBygHGY66H4WMI PXKOvtstHWl3cDtNnH1M65T47QMamQhVvjSSHT93DKLEs1GIV1c967vB77HbUC2a h4mMz8A0G1NYA+o77fDYwfHqjqQkACozi4b8/IbGm9ztJ9/7PKJp9XCpomCCnVYc /jIQdcXGWqxiWcOeiaCruy6yQNhqIA6wePL0DCQaVJ4GPSjMrxhPurUkp1dU4ao+ bwj1+IEAAtEyQZ6Jk4ne =oj/p -----END PGP SIGNATURE----- Merge tag 'hwmon-for-linus-v4.7' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging Pull hwmon updates from Guenter Roeck: - major rework of it87 driver: cleanup, added support for additional attributes, added support for two chips in the system, added support for IT8728E - fam17h_power driver now reports accumulated power consumption - new driver for MAX31722/MAX31723 temperature sensors - minor fixes to sch5636 and ads7828 drivers * tag 'hwmon-for-linus-v4.7' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (35 commits) hwmon: (sch5636) trivial fix of spelling mistake on revision hwmon: (it87) Add support for IT8628E hwmon: (it87) Fix pwm_temp_map for system with 6 pwm channels hwmon: (it87) Support automatic pwm control on newer chips hwmon: (it87) Enhance validation for fan4 and fan5 hwmon: (it87) Support disabling fan control for all pwm control and chips hwmon: (it87) Formatting cleanup hwmon: (it87) Use defines for array sizes and sensor counts hwmon: (it87) Use BIT macro hwmon: (it87) Add support for VIN7 to VIN10 on IT8620E hwmon: (it87) Simplify reading voltage registers hwmon: (it87) Support up to 6 temperature sensors on IT8620E hwmon: (it87) Convert to use new hwmon API hwmon: (it87) Use single group and is_visible for miscellaneous attributes hwmon: (it87) Use is_visible for pwm attributes hwmon: (it87) Use is_visible for fan attributes hwmon: (it87) Use is_visible for temperature sensors hwmon: (it87) Use is_visible for voltage sensors hwmon: (it87) Rearrange code to avoid forward declarations hwmon: (it87) Add support for second Super-IO chip ...
This commit is contained in:
commit
fdb8a29122
|
@ -13,6 +13,7 @@ Required properties:
|
|||
* "lltc,ltc3886"
|
||||
* "lltc,ltc3887"
|
||||
* "lltc,ltm2987"
|
||||
* "lltc,ltm4675"
|
||||
* "lltc,ltm4676"
|
||||
- reg: I2C slave address
|
||||
|
||||
|
|
|
@ -10,14 +10,22 @@ Supported chips:
|
|||
Datasheets:
|
||||
BIOS and Kernel Developer's Guide (BKDG) For AMD Family 15h Processors
|
||||
BIOS and Kernel Developer's Guide (BKDG) For AMD Family 16h Processors
|
||||
AMD64 Architecture Programmer's Manual Volume 2: System Programming
|
||||
|
||||
Author: Andreas Herrmann <herrmann.der.user@googlemail.com>
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
1) Processor TDP (Thermal design power)
|
||||
|
||||
Given a fixed frequency and voltage, the power consumption of a
|
||||
processor varies based on the workload being executed. Derated power
|
||||
is the power consumed when running a specific application. Thermal
|
||||
design power (TDP) is an example of derated power.
|
||||
|
||||
This driver permits reading of registers providing power information
|
||||
of AMD Family 15h and 16h processors.
|
||||
of AMD Family 15h and 16h processors via TDP algorithm.
|
||||
|
||||
For AMD Family 15h and 16h processors the following power values can
|
||||
be calculated using different processor northbridge function
|
||||
|
@ -37,3 +45,58 @@ This driver provides ProcessorPwrWatts and CurrPwrWatts:
|
|||
On multi-node processors the calculated value is for the entire
|
||||
package and not for a single node. Thus the driver creates sysfs
|
||||
attributes only for internal node0 of a multi-node processor.
|
||||
|
||||
2) Accumulated Power Mechanism
|
||||
|
||||
This driver also introduces an algorithm that should be used to
|
||||
calculate the average power consumed by a processor during a
|
||||
measurement interval Tm. The feature of accumulated power mechanism is
|
||||
indicated by CPUID Fn8000_0007_EDX[12].
|
||||
|
||||
* Tsample: compute unit power accumulator sample period
|
||||
* Tref: the PTSC counter period
|
||||
* PTSC: performance timestamp counter
|
||||
* N: the ratio of compute unit power accumulator sample period to the
|
||||
PTSC period
|
||||
* Jmax: max compute unit accumulated power which is indicated by
|
||||
MaxCpuSwPwrAcc MSR C001007b
|
||||
* Jx/Jy: compute unit accumulated power which is indicated by
|
||||
CpuSwPwrAcc MSR C001007a
|
||||
* Tx/Ty: the value of performance timestamp counter which is indicated
|
||||
by CU_PTSC MSR C0010280
|
||||
* PwrCPUave: CPU average power
|
||||
|
||||
i. Determine the ratio of Tsample to Tref by executing CPUID Fn8000_0007.
|
||||
N = value of CPUID Fn8000_0007_ECX[CpuPwrSampleTimeRatio[15:0]].
|
||||
|
||||
ii. Read the full range of the cumulative energy value from the new
|
||||
MSR MaxCpuSwPwrAcc.
|
||||
Jmax = value returned.
|
||||
iii. At time x, SW reads CpuSwPwrAcc MSR and samples the PTSC.
|
||||
Jx = value read from CpuSwPwrAcc and Tx = value read from
|
||||
PTSC.
|
||||
|
||||
iv. At time y, SW reads CpuSwPwrAcc MSR and samples the PTSC.
|
||||
Jy = value read from CpuSwPwrAcc and Ty = value read from
|
||||
PTSC.
|
||||
|
||||
v. Calculate the average power consumption for a compute unit over
|
||||
time period (y-x). Unit of result is uWatt.
|
||||
if (Jy < Jx) // Rollover has occurred
|
||||
Jdelta = (Jy + Jmax) - Jx
|
||||
else
|
||||
Jdelta = Jy - Jx
|
||||
PwrCPUave = N * Jdelta * 1000 / (Ty - Tx)
|
||||
|
||||
This driver provides PwrCPUave and interval(default is 10 millisecond
|
||||
and maximum is 1 second):
|
||||
* power1_average (PwrCPUave)
|
||||
* power1_average_interval (Interval)
|
||||
|
||||
The power1_average_interval can be updated at /etc/sensors3.conf file
|
||||
as below:
|
||||
|
||||
chip "fam15h_power-*"
|
||||
set power1_average_interval 0.01
|
||||
|
||||
Then save it with "sensors -s".
|
||||
|
|
|
@ -9,6 +9,9 @@ Supported chips:
|
|||
* IT8620E
|
||||
Prefix: 'it8620'
|
||||
Addresses scanned: from Super I/O config space (8 I/O ports)
|
||||
* IT8628E
|
||||
Prefix: 'it8628'
|
||||
Addresses scanned: from Super I/O config space (8 I/O ports)
|
||||
Datasheet: Not publicly available
|
||||
* IT8705F
|
||||
Prefix: 'it87'
|
||||
|
@ -114,8 +117,8 @@ motherboard models.
|
|||
Description
|
||||
-----------
|
||||
|
||||
This driver implements support for the IT8603E, IT8620E, IT8623E, IT8705F,
|
||||
IT8712F, IT8716F, IT8718F, IT8720F, IT8721F, IT8726F, IT8728F, IT8732F,
|
||||
This driver implements support for the IT8603E, IT8620E, IT8623E, IT8628E,
|
||||
IT8705F, IT8712F, IT8716F, IT8718F, IT8720F, IT8721F, IT8726F, IT8728F, IT8732F,
|
||||
IT8758E, IT8771E, IT8772E, IT8781F, IT8782F, IT8783E/F, IT8786E, IT8790E, and
|
||||
SiS950 chips.
|
||||
|
||||
|
@ -158,8 +161,8 @@ The IT8603E/IT8623E is a custom design, hardware monitoring part is similar to
|
|||
IT8728F. It only supports 3 fans, 16-bit fan mode, and the full speed mode
|
||||
of the fan is not supported (value 0 of pwmX_enable).
|
||||
|
||||
The IT8620E is another custom design, hardware monitoring part is similar to
|
||||
IT8728F. It only supports 16-bit fan mode.
|
||||
The IT8620E and IT8628E are custom designs, hardware monitoring part is similar
|
||||
to IT8728F. It only supports 16-bit fan mode. Both chips support up to 6 fans.
|
||||
|
||||
The IT8790E supports up to 3 fans. 16-bit fan mode is always enabled.
|
||||
|
||||
|
@ -187,8 +190,8 @@ of 0.016 volt. IT8603E, IT8721F/IT8758E and IT8728F can measure between 0 and
|
|||
2.8 volts with a resolution of 0.0109 volt. The battery voltage in8 does not
|
||||
have limit registers.
|
||||
|
||||
On the IT8603E, IT8721F/IT8758E, IT8732F, IT8781F, IT8782F, and IT8783E/F, some
|
||||
voltage inputs are internal and scaled inside the chip:
|
||||
On the IT8603E, IT8620E, IT8628E, IT8721F/IT8758E, IT8732F, IT8781F, IT8782F,
|
||||
and IT8783E/F, some voltage inputs are internal and scaled inside the chip:
|
||||
* in3 (optional)
|
||||
* in7 (optional for IT8781F, IT8782F, and IT8783E/F)
|
||||
* in8 (always)
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
Kernel driver max31722
|
||||
======================
|
||||
|
||||
Supported chips:
|
||||
* Maxim Integrated MAX31722
|
||||
Prefix: 'max31722'
|
||||
ACPI ID: MAX31722
|
||||
Addresses scanned: -
|
||||
Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX31722-MAX31723.pdf
|
||||
* Maxim Integrated MAX31723
|
||||
Prefix: 'max31723'
|
||||
ACPI ID: MAX31723
|
||||
Addresses scanned: -
|
||||
Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX31722-MAX31723.pdf
|
||||
|
||||
Author: Tiberiu Breana <tiberiu.a.breana@intel.com>
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
This driver adds support for the Maxim Integrated MAX31722/MAX31723 thermometers
|
||||
and thermostats running over an SPI interface.
|
||||
|
||||
Usage Notes
|
||||
-----------
|
||||
|
||||
This driver uses ACPI to auto-detect devices. See ACPI IDs in the above section.
|
||||
|
||||
Sysfs entries
|
||||
-------------
|
||||
|
||||
The following attribute is supported:
|
||||
|
||||
temp1_input Measured temperature. Read-only.
|
|
@ -288,7 +288,7 @@ config SENSORS_K10TEMP
|
|||
|
||||
config SENSORS_FAM15H_POWER
|
||||
tristate "AMD Family 15h processor power"
|
||||
depends on X86 && PCI
|
||||
depends on X86 && PCI && CPU_SUP_AMD
|
||||
help
|
||||
If you say yes here you get support for processor power
|
||||
information of your AMD family 15h CPU.
|
||||
|
@ -621,7 +621,8 @@ config SENSORS_IT87
|
|||
If you say yes here you get support for ITE IT8705F, IT8712F, IT8716F,
|
||||
IT8718F, IT8720F, IT8721F, IT8726F, IT8728F, IT8732F, IT8758E,
|
||||
IT8771E, IT8772E, IT8781F, IT8782F, IT8783E/F, IT8786E, IT8790E,
|
||||
IT8603E, IT8620E, and IT8623E sensor chips, and the SiS950 clone.
|
||||
IT8603E, IT8620E, IT8623E, and IT8628E sensor chips, and the SiS950
|
||||
clone.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called it87.
|
||||
|
@ -821,6 +822,16 @@ config SENSORS_MAX197
|
|||
This driver can also be built as a module. If so, the module
|
||||
will be called max197.
|
||||
|
||||
config SENSORS_MAX31722
|
||||
tristate "MAX31722 temperature sensor"
|
||||
depends on SPI
|
||||
help
|
||||
Support for the Maxim Integrated MAX31722/MAX31723 digital
|
||||
thermometers/thermostats operating over an SPI interface.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called max31722.
|
||||
|
||||
config SENSORS_MAX6639
|
||||
tristate "Maxim MAX6639 sensor chip"
|
||||
depends on I2C
|
||||
|
|
|
@ -112,6 +112,7 @@ obj-$(CONFIG_SENSORS_MAX16065) += max16065.o
|
|||
obj-$(CONFIG_SENSORS_MAX1619) += max1619.o
|
||||
obj-$(CONFIG_SENSORS_MAX1668) += max1668.o
|
||||
obj-$(CONFIG_SENSORS_MAX197) += max197.o
|
||||
obj-$(CONFIG_SENSORS_MAX31722) += max31722.o
|
||||
obj-$(CONFIG_SENSORS_MAX6639) += max6639.o
|
||||
obj-$(CONFIG_SENSORS_MAX6642) += max6642.o
|
||||
obj-$(CONFIG_SENSORS_MAX6650) += max6650.o
|
||||
|
|
|
@ -120,6 +120,7 @@ static int ads7828_probe(struct i2c_client *client,
|
|||
unsigned int vref_mv = ADS7828_INT_VREF_MV;
|
||||
bool diff_input = false;
|
||||
bool ext_vref = false;
|
||||
unsigned int regval;
|
||||
|
||||
data = devm_kzalloc(dev, sizeof(struct ads7828_data), GFP_KERNEL);
|
||||
if (!data)
|
||||
|
@ -154,6 +155,15 @@ static int ads7828_probe(struct i2c_client *client,
|
|||
if (!diff_input)
|
||||
data->cmd_byte |= ADS7828_CMD_SD_SE;
|
||||
|
||||
/*
|
||||
* Datasheet specifies internal reference voltage is disabled by
|
||||
* default. The internal reference voltage needs to be enabled and
|
||||
* voltage needs to settle before getting valid ADC data. So perform a
|
||||
* dummy read to enable the internal reference voltage.
|
||||
*/
|
||||
if (!ext_vref)
|
||||
regmap_read(data->regmap, data->cmd_byte, ®val);
|
||||
|
||||
hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
|
||||
data,
|
||||
ads7828_groups);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* fam15h_power.c - AMD Family 15h processor power monitoring
|
||||
*
|
||||
* Copyright (c) 2011 Advanced Micro Devices, Inc.
|
||||
* Copyright (c) 2011-2016 Advanced Micro Devices, Inc.
|
||||
* Author: Andreas Herrmann <herrmann.der.user@googlemail.com>
|
||||
*
|
||||
*
|
||||
|
@ -25,6 +25,10 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/sched.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/msr.h>
|
||||
|
||||
|
@ -44,8 +48,14 @@ MODULE_LICENSE("GPL");
|
|||
|
||||
#define FAM15H_MIN_NUM_ATTRS 2
|
||||
#define FAM15H_NUM_GROUPS 2
|
||||
#define MAX_CUS 8
|
||||
|
||||
/* set maximum interval as 1 second */
|
||||
#define MAX_INTERVAL 1000
|
||||
|
||||
#define MSR_F15H_CU_PWR_ACCUMULATOR 0xc001007a
|
||||
#define MSR_F15H_CU_MAX_PWR_ACCUMULATOR 0xc001007b
|
||||
#define MSR_F15H_PTSC 0xc0010280
|
||||
|
||||
#define PCI_DEVICE_ID_AMD_15H_M70H_NB_F4 0x15b4
|
||||
|
||||
|
@ -59,8 +69,20 @@ struct fam15h_power_data {
|
|||
struct attribute_group group;
|
||||
/* maximum accumulated power of a compute unit */
|
||||
u64 max_cu_acc_power;
|
||||
/* accumulated power of the compute units */
|
||||
u64 cu_acc_power[MAX_CUS];
|
||||
/* performance timestamp counter */
|
||||
u64 cpu_sw_pwr_ptsc[MAX_CUS];
|
||||
/* online/offline status of current compute unit */
|
||||
int cu_on[MAX_CUS];
|
||||
unsigned long power_period;
|
||||
};
|
||||
|
||||
static bool is_carrizo_or_later(void)
|
||||
{
|
||||
return boot_cpu_data.x86 == 0x15 && boot_cpu_data.x86_model >= 0x60;
|
||||
}
|
||||
|
||||
static ssize_t show_power(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
|
@ -77,7 +99,7 @@ static ssize_t show_power(struct device *dev,
|
|||
* On Carrizo and later platforms, TdpRunAvgAccCap bit field
|
||||
* is extended to 4:31 from 4:25.
|
||||
*/
|
||||
if (boot_cpu_data.x86 == 0x15 && boot_cpu_data.x86_model >= 0x60) {
|
||||
if (is_carrizo_or_later()) {
|
||||
running_avg_capture = val >> 4;
|
||||
running_avg_capture = sign_extend32(running_avg_capture, 27);
|
||||
} else {
|
||||
|
@ -94,7 +116,7 @@ static ssize_t show_power(struct device *dev,
|
|||
* On Carrizo and later platforms, ApmTdpLimit bit field
|
||||
* is extended to 16:31 from 16:28.
|
||||
*/
|
||||
if (boot_cpu_data.x86 == 0x15 && boot_cpu_data.x86_model >= 0x60)
|
||||
if (is_carrizo_or_later())
|
||||
tdp_limit = val >> 16;
|
||||
else
|
||||
tdp_limit = (val >> 16) & 0x1fff;
|
||||
|
@ -125,6 +147,167 @@ static ssize_t show_power_crit(struct device *dev,
|
|||
}
|
||||
static DEVICE_ATTR(power1_crit, S_IRUGO, show_power_crit, NULL);
|
||||
|
||||
static void do_read_registers_on_cu(void *_data)
|
||||
{
|
||||
struct fam15h_power_data *data = _data;
|
||||
int cpu, cu;
|
||||
|
||||
cpu = smp_processor_id();
|
||||
|
||||
/*
|
||||
* With the new x86 topology modelling, cpu core id actually
|
||||
* is compute unit id.
|
||||
*/
|
||||
cu = cpu_data(cpu).cpu_core_id;
|
||||
|
||||
rdmsrl_safe(MSR_F15H_CU_PWR_ACCUMULATOR, &data->cu_acc_power[cu]);
|
||||
rdmsrl_safe(MSR_F15H_PTSC, &data->cpu_sw_pwr_ptsc[cu]);
|
||||
|
||||
data->cu_on[cu] = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is only able to be called when CPUID
|
||||
* Fn8000_0007:EDX[12] is set.
|
||||
*/
|
||||
static int read_registers(struct fam15h_power_data *data)
|
||||
{
|
||||
int this_cpu, ret, cpu;
|
||||
int core, this_core;
|
||||
cpumask_var_t mask;
|
||||
|
||||
ret = zalloc_cpumask_var(&mask, GFP_KERNEL);
|
||||
if (!ret)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(data->cu_on, 0, sizeof(int) * MAX_CUS);
|
||||
|
||||
get_online_cpus();
|
||||
this_cpu = smp_processor_id();
|
||||
|
||||
/*
|
||||
* Choose the first online core of each compute unit, and then
|
||||
* read their MSR value of power and ptsc in a single IPI,
|
||||
* because the MSR value of CPU core represent the compute
|
||||
* unit's.
|
||||
*/
|
||||
core = -1;
|
||||
|
||||
for_each_online_cpu(cpu) {
|
||||
this_core = topology_core_id(cpu);
|
||||
|
||||
if (this_core == core)
|
||||
continue;
|
||||
|
||||
core = this_core;
|
||||
|
||||
/* get any CPU on this compute unit */
|
||||
cpumask_set_cpu(cpumask_any(topology_sibling_cpumask(cpu)), mask);
|
||||
}
|
||||
|
||||
if (cpumask_test_cpu(this_cpu, mask))
|
||||
do_read_registers_on_cu(data);
|
||||
|
||||
smp_call_function_many(mask, do_read_registers_on_cu, data, true);
|
||||
put_online_cpus();
|
||||
|
||||
free_cpumask_var(mask);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t acc_show_power(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct fam15h_power_data *data = dev_get_drvdata(dev);
|
||||
u64 prev_cu_acc_power[MAX_CUS], prev_ptsc[MAX_CUS],
|
||||
jdelta[MAX_CUS];
|
||||
u64 tdelta, avg_acc;
|
||||
int cu, cu_num, ret;
|
||||
signed long leftover;
|
||||
|
||||
/*
|
||||
* With the new x86 topology modelling, x86_max_cores is the
|
||||
* compute unit number.
|
||||
*/
|
||||
cu_num = boot_cpu_data.x86_max_cores;
|
||||
|
||||
ret = read_registers(data);
|
||||
if (ret)
|
||||
return 0;
|
||||
|
||||
for (cu = 0; cu < cu_num; cu++) {
|
||||
prev_cu_acc_power[cu] = data->cu_acc_power[cu];
|
||||
prev_ptsc[cu] = data->cpu_sw_pwr_ptsc[cu];
|
||||
}
|
||||
|
||||
leftover = schedule_timeout_interruptible(msecs_to_jiffies(data->power_period));
|
||||
if (leftover)
|
||||
return 0;
|
||||
|
||||
ret = read_registers(data);
|
||||
if (ret)
|
||||
return 0;
|
||||
|
||||
for (cu = 0, avg_acc = 0; cu < cu_num; cu++) {
|
||||
/* check if current compute unit is online */
|
||||
if (data->cu_on[cu] == 0)
|
||||
continue;
|
||||
|
||||
if (data->cu_acc_power[cu] < prev_cu_acc_power[cu]) {
|
||||
jdelta[cu] = data->max_cu_acc_power + data->cu_acc_power[cu];
|
||||
jdelta[cu] -= prev_cu_acc_power[cu];
|
||||
} else {
|
||||
jdelta[cu] = data->cu_acc_power[cu] - prev_cu_acc_power[cu];
|
||||
}
|
||||
tdelta = data->cpu_sw_pwr_ptsc[cu] - prev_ptsc[cu];
|
||||
jdelta[cu] *= data->cpu_pwr_sample_ratio * 1000;
|
||||
do_div(jdelta[cu], tdelta);
|
||||
|
||||
/* the unit is microWatt */
|
||||
avg_acc += jdelta[cu];
|
||||
}
|
||||
|
||||
return sprintf(buf, "%llu\n", (unsigned long long)avg_acc);
|
||||
}
|
||||
static DEVICE_ATTR(power1_average, S_IRUGO, acc_show_power, NULL);
|
||||
|
||||
static ssize_t acc_show_power_period(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct fam15h_power_data *data = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%lu\n", data->power_period);
|
||||
}
|
||||
|
||||
static ssize_t acc_set_power_period(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct fam15h_power_data *data = dev_get_drvdata(dev);
|
||||
unsigned long temp;
|
||||
int ret;
|
||||
|
||||
ret = kstrtoul(buf, 10, &temp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (temp > MAX_INTERVAL)
|
||||
return -EINVAL;
|
||||
|
||||
/* the interval value should be greater than 0 */
|
||||
if (temp <= 0)
|
||||
return -EINVAL;
|
||||
|
||||
data->power_period = temp;
|
||||
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR(power1_average_interval, S_IRUGO | S_IWUSR,
|
||||
acc_show_power_period, acc_set_power_period);
|
||||
|
||||
static int fam15h_power_init_attrs(struct pci_dev *pdev,
|
||||
struct fam15h_power_data *data)
|
||||
{
|
||||
|
@ -137,6 +320,10 @@ static int fam15h_power_init_attrs(struct pci_dev *pdev,
|
|||
(c->x86_model >= 0x60 && c->x86_model <= 0x7f)))
|
||||
n += 1;
|
||||
|
||||
/* check if processor supports accumulated power */
|
||||
if (boot_cpu_has(X86_FEATURE_ACC_POWER))
|
||||
n += 2;
|
||||
|
||||
fam15h_power_attrs = devm_kcalloc(&pdev->dev, n,
|
||||
sizeof(*fam15h_power_attrs),
|
||||
GFP_KERNEL);
|
||||
|
@ -151,6 +338,11 @@ static int fam15h_power_init_attrs(struct pci_dev *pdev,
|
|||
(c->x86_model >= 0x60 && c->x86_model <= 0x7f)))
|
||||
fam15h_power_attrs[n++] = &dev_attr_power1_input.attr;
|
||||
|
||||
if (boot_cpu_has(X86_FEATURE_ACC_POWER)) {
|
||||
fam15h_power_attrs[n++] = &dev_attr_power1_average.attr;
|
||||
fam15h_power_attrs[n++] = &dev_attr_power1_average_interval.attr;
|
||||
}
|
||||
|
||||
data->group.attrs = fam15h_power_attrs;
|
||||
|
||||
return 0;
|
||||
|
@ -216,7 +408,7 @@ static int fam15h_power_resume(struct pci_dev *pdev)
|
|||
static int fam15h_power_init_data(struct pci_dev *f4,
|
||||
struct fam15h_power_data *data)
|
||||
{
|
||||
u32 val, eax, ebx, ecx, edx;
|
||||
u32 val;
|
||||
u64 tmp;
|
||||
int ret;
|
||||
|
||||
|
@ -243,10 +435,9 @@ static int fam15h_power_init_data(struct pci_dev *f4,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
cpuid(0x80000007, &eax, &ebx, &ecx, &edx);
|
||||
|
||||
/* CPUID Fn8000_0007:EDX[12] indicates to support accumulated power */
|
||||
if (!(edx & BIT(12)))
|
||||
if (!boot_cpu_has(X86_FEATURE_ACC_POWER))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
|
@ -254,7 +445,7 @@ static int fam15h_power_init_data(struct pci_dev *f4,
|
|||
* sample period to the PTSC counter period by executing CPUID
|
||||
* Fn8000_0007:ECX
|
||||
*/
|
||||
data->cpu_pwr_sample_ratio = ecx;
|
||||
data->cpu_pwr_sample_ratio = cpuid_ecx(0x80000007);
|
||||
|
||||
if (rdmsrl_safe(MSR_F15H_CU_MAX_PWR_ACCUMULATOR, &tmp)) {
|
||||
pr_err("Failed to read max compute unit power accumulator MSR\n");
|
||||
|
@ -263,7 +454,15 @@ static int fam15h_power_init_data(struct pci_dev *f4,
|
|||
|
||||
data->max_cu_acc_power = tmp;
|
||||
|
||||
return 0;
|
||||
/*
|
||||
* Milliseconds are a reasonable interval for the measurement.
|
||||
* But it shouldn't set too long here, because several seconds
|
||||
* would cause the read function to hang. So set default
|
||||
* interval as 10 ms.
|
||||
*/
|
||||
data->power_period = 10;
|
||||
|
||||
return read_registers(data);
|
||||
}
|
||||
|
||||
static int fam15h_power_probe(struct pci_dev *pdev,
|
||||
|
|
2279
drivers/hwmon/it87.c
2279
drivers/hwmon/it87.c
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
* max31722 - hwmon driver for Maxim Integrated MAX31722/MAX31723 SPI
|
||||
* digital thermometer and thermostats.
|
||||
*
|
||||
* Copyright (c) 2016, Intel Corporation.
|
||||
*
|
||||
* This file is subject to the terms and conditions of version 2 of
|
||||
* the GNU General Public License. See the file COPYING in the main
|
||||
* directory of this archive for more details.
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#define MAX31722_REG_CFG 0x00
|
||||
#define MAX31722_REG_TEMP_LSB 0x01
|
||||
|
||||
#define MAX31722_MODE_CONTINUOUS 0x00
|
||||
#define MAX31722_MODE_STANDBY 0x01
|
||||
#define MAX31722_MODE_MASK 0xFE
|
||||
#define MAX31722_RESOLUTION_12BIT 0x06
|
||||
#define MAX31722_WRITE_MASK 0x80
|
||||
|
||||
struct max31722_data {
|
||||
struct device *hwmon_dev;
|
||||
struct spi_device *spi_device;
|
||||
u8 mode;
|
||||
};
|
||||
|
||||
static int max31722_set_mode(struct max31722_data *data, u8 mode)
|
||||
{
|
||||
int ret;
|
||||
struct spi_device *spi = data->spi_device;
|
||||
u8 buf[2] = {
|
||||
MAX31722_REG_CFG | MAX31722_WRITE_MASK,
|
||||
(data->mode & MAX31722_MODE_MASK) | mode
|
||||
};
|
||||
|
||||
ret = spi_write(spi, &buf, sizeof(buf));
|
||||
if (ret < 0) {
|
||||
dev_err(&spi->dev, "failed to set sensor mode.\n");
|
||||
return ret;
|
||||
}
|
||||
data->mode = (data->mode & MAX31722_MODE_MASK) | mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t max31722_show_temp(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
ssize_t ret;
|
||||
struct max31722_data *data = dev_get_drvdata(dev);
|
||||
|
||||
ret = spi_w8r16(data->spi_device, MAX31722_REG_TEMP_LSB);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
/* Keep 12 bits and multiply by the scale of 62.5 millidegrees/bit. */
|
||||
return sprintf(buf, "%d\n", (s16)le16_to_cpu(ret) * 125 / 32);
|
||||
}
|
||||
|
||||
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO,
|
||||
max31722_show_temp, NULL, 0);
|
||||
|
||||
static struct attribute *max31722_attrs[] = {
|
||||
&sensor_dev_attr_temp1_input.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
ATTRIBUTE_GROUPS(max31722);
|
||||
|
||||
static int max31722_probe(struct spi_device *spi)
|
||||
{
|
||||
int ret;
|
||||
struct max31722_data *data;
|
||||
|
||||
data = devm_kzalloc(&spi->dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
spi_set_drvdata(spi, data);
|
||||
data->spi_device = spi;
|
||||
/*
|
||||
* Set SD bit to 0 so we can have continuous measurements.
|
||||
* Set resolution to 12 bits for maximum precision.
|
||||
*/
|
||||
data->mode = MAX31722_MODE_CONTINUOUS | MAX31722_RESOLUTION_12BIT;
|
||||
ret = max31722_set_mode(data, MAX31722_MODE_CONTINUOUS);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
data->hwmon_dev = hwmon_device_register_with_groups(&spi->dev,
|
||||
spi->modalias,
|
||||
data,
|
||||
max31722_groups);
|
||||
if (IS_ERR(data->hwmon_dev)) {
|
||||
max31722_set_mode(data, MAX31722_MODE_STANDBY);
|
||||
return PTR_ERR(data->hwmon_dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max31722_remove(struct spi_device *spi)
|
||||
{
|
||||
struct max31722_data *data = spi_get_drvdata(spi);
|
||||
|
||||
hwmon_device_unregister(data->hwmon_dev);
|
||||
|
||||
return max31722_set_mode(data, MAX31722_MODE_STANDBY);
|
||||
}
|
||||
|
||||
static int __maybe_unused max31722_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_device *spi_device = to_spi_device(dev);
|
||||
struct max31722_data *data = spi_get_drvdata(spi_device);
|
||||
|
||||
return max31722_set_mode(data, MAX31722_MODE_STANDBY);
|
||||
}
|
||||
|
||||
static int __maybe_unused max31722_resume(struct device *dev)
|
||||
{
|
||||
struct spi_device *spi_device = to_spi_device(dev);
|
||||
struct max31722_data *data = spi_get_drvdata(spi_device);
|
||||
|
||||
return max31722_set_mode(data, MAX31722_MODE_CONTINUOUS);
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(max31722_pm_ops, max31722_suspend, max31722_resume);
|
||||
|
||||
static const struct spi_device_id max31722_spi_id[] = {
|
||||
{"max31722", 0},
|
||||
{"max31723", 0},
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct acpi_device_id __maybe_unused max31722_acpi_id[] = {
|
||||
{"MAX31722", 0},
|
||||
{"MAX31723", 0},
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(spi, max31722_spi_id);
|
||||
|
||||
static struct spi_driver max31722_driver = {
|
||||
.driver = {
|
||||
.name = "max31722",
|
||||
.pm = &max31722_pm_ops,
|
||||
.acpi_match_table = ACPI_PTR(max31722_acpi_id),
|
||||
},
|
||||
.probe = max31722_probe,
|
||||
.remove = max31722_remove,
|
||||
.id_table = max31722_spi_id,
|
||||
};
|
||||
|
||||
module_spi_driver(max31722_driver);
|
||||
|
||||
MODULE_AUTHOR("Tiberiu Breana <tiberiu.a.breana@intel.com>");
|
||||
MODULE_DESCRIPTION("max31722 sensor driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -449,7 +449,7 @@ static int sch5636_probe(struct platform_device *pdev)
|
|||
}
|
||||
revision[i] = val;
|
||||
}
|
||||
pr_info("Found %s chip at %#hx, revison: %d.%02d\n", DEVNAME,
|
||||
pr_info("Found %s chip at %#hx, revision: %d.%02d\n", DEVNAME,
|
||||
data->addr, revision[0], revision[1]);
|
||||
|
||||
/* Read all temp + fan ctrl registers to determine which are active */
|
||||
|
|
Loading…
Reference in New Issue