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:
Linus Torvalds 2016-05-16 18:50:49 -07:00
commit fdb8a29122
11 changed files with 1793 additions and 1009 deletions

View file

@ -13,6 +13,7 @@ Required properties:
* "lltc,ltc3886"
* "lltc,ltc3887"
* "lltc,ltm2987"
* "lltc,ltm4675"
* "lltc,ltm4676"
- reg: I2C slave address

View file

@ -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".

View file

@ -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)

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -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, &regval);
hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
data,
ads7828_groups);

View file

@ -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,

File diff suppressed because it is too large Load diff

165
drivers/hwmon/max31722.c Normal file
View file

@ -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");

View file

@ -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 */