Commit 77b0a4aa authored by Linus Torvalds's avatar Linus Torvalds

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

Merge tag 'hwmon-for-linus-v4.9' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging

Pull hwmon updates from Guenter Roeck:

 - New hwmon registration API, including ports of several drivers to the
   new API

 - New hwmon driver for APM X-Gene SoC

 - Added support for UCD90160, DPS-460, DPS-800, and SGD009 PMBUs chips

 - Various cleanups, minor improvements, and fixes in several drivers

* tag 'hwmon-for-linus-v4.9' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (54 commits)
  hwmon: (nct6775) Add support for multiple virtual temperature sources
  hwmon: (adt7470) No need for additional synchronization on kthread_stop()
  hwmon: (lm95241) Update module description to include LM95231
  hwmon: (lm95245) Select REGMAP_I2C
  hwmon: (ibmpowernv) Fix label for cores numbers not threads
  hwmon: (adt7470) Allow faster removal
  hwmon: (adt7470) Add write support to alarm_mask
  hwmon: (xgene) access mailbox as RAM
  hwmon: (lm95245) Use new hwmon registration API
  hwmon: (lm95241) Convert to use new hwmon registration API
  hwmon: (jc42) Convert to use new hwmon registration API
  hwmon: (max31790) Convert to use new hwmon registration API
  hwmon: (nct7904) Convert to use new hwmon registration API
  hwmon: (ltc4245) Convert to use new hwmon registration API
  hwmon: (tmp421) Convert to use new hwmon registration API
  hwmon: (tmp102) Convert to use new hwmon registration API
  hwmon: (lm90) Convert to use new hwmon registration API
  hwmon: (lm75) Convert to use new hwmon registration API
  hwmon: (xgene) Fix crash when alarm occurs before driver probe
  hwmon: (iio_hwmon) defer probe when no channel is found
  ...
parents f80fa182 7ce4190c
LTC4151 High Voltage I2C Current and Voltage Monitor
Required properties:
- compatible: Must be "lltc,ltc4151"
- reg: I2C address
Optional properties:
- shunt-resistor-micro-ohms
Shunt resistor value in micro-Ohms
Defaults to <1000> if unset.
Example:
ltc4151@6e {
compatible = "lltc,ltc4151";
reg = <0x6e>;
shunt-resistor-micro-ohms = <1500>;
};
Bindings for MAX6651 and MAX6650 I2C fan controllers
Reference:
[1] https://datasheets.maximintegrated.com/en/ds/MAX6650-MAX6651.pdf
Required properties:
- compatible : One of "maxim,max6650" or "maxim,max6651"
- reg : I2C address, one of 0x1b, 0x1f, 0x4b, 0x48.
Optional properties, default is to retain the chip's current setting:
- maxim,fan-microvolt : The supply voltage of the fan, either 5000000 uV or
12000000 uV.
- maxim,fan-prescale : Pre-scaling value, as per datasheet [1]. Lower values
allow more fine-grained control of slower fans.
Valid: 1, 2, 4, 8, 16.
- maxim,fan-target-rpm: Initial requested fan rotation speed. If specified, the
driver selects closed-loop mode and the requested speed.
This ensures the fan is already running before userspace
takes over.
Example:
fan-max6650: max6650@1b {
reg = <0x1b>;
compatible = "maxim,max6650";
maxim,fan-microvolt = <12000000>;
maxim,fan-prescale = <4>;
maxim,fan-target-rpm = <1200>;
};
......@@ -65,6 +65,23 @@ from 0 (off) to 255 (full speed). Fan speed will be set to maximum when the
temperature sensor associated with the PWM control exceeds
pwm#_auto_point2_temp.
The driver also allows control of the PWM frequency:
* pwm1_freq
The PWM frequency is rounded to the nearest one of:
* 11.0 Hz
* 14.7 Hz
* 22.1 Hz
* 29.4 Hz
* 35.3 Hz
* 44.1 Hz
* 58.8 Hz
* 88.2 Hz
* 1.4 kHz
* 22.5 kHz
Notes
-----
......
......@@ -34,6 +34,19 @@ devm_hwmon_device_register_with_groups(struct device *dev,
const char *name, void *drvdata,
const struct attribute_group **groups);
struct device *
hwmon_device_register_with_info(struct device *dev,
const char *name, void *drvdata,
const struct hwmon_chip_info *info,
const struct attribute_group **groups);
struct device *
devm_hwmon_device_register_with_info(struct device *dev,
const char *name,
void *drvdata,
const struct hwmon_chip_info *info,
const struct attribute_group **groups);
void hwmon_device_unregister(struct device *dev);
void devm_hwmon_device_unregister(struct device *dev);
......@@ -60,15 +73,229 @@ devm_hwmon_device_register_with_groups is similar to
hwmon_device_register_with_groups. However, it is device managed, meaning the
hwmon device does not have to be removed explicitly by the removal function.
hwmon_device_register_with_info is the most comprehensive and preferred means
to register a hardware monitoring device. It creates the standard sysfs
attributes in the hardware monitoring core, letting the driver focus on reading
from and writing to the chip instead of having to bother with sysfs attributes.
Its parameters are described in more detail below.
devm_hwmon_device_register_with_info is similar to
hwmon_device_register_with_info. However, it is device managed, meaning the
hwmon device does not have to be removed explicitly by the removal function.
hwmon_device_unregister deregisters a registered hardware monitoring device.
The parameter of this function is the pointer to the registered hardware
monitoring device structure. This function must be called from the driver
remove function if the hardware monitoring device was registered with
hwmon_device_register or with hwmon_device_register_with_groups.
hwmon_device_register, hwmon_device_register_with_groups, or
hwmon_device_register_with_info.
devm_hwmon_device_unregister does not normally have to be called. It is only
needed for error handling, and only needed if the driver probe fails after
the call to devm_hwmon_device_register_with_groups.
the call to devm_hwmon_device_register_with_groups and if the automatic
(device managed) removal would be too late.
Using devm_hwmon_device_register_with_info()
--------------------------------------------
hwmon_device_register_with_info() registers a hardware monitoring device.
The parameters to this function are
struct device *dev Pointer to parent device
const char *name Device name
void *drvdata Driver private data
const struct hwmon_chip_info *info
Pointer to chip description.
const struct attribute_group **groups
Null-terminated list of additional sysfs attribute
groups.
This function returns a pointer to the created hardware monitoring device
on success and a negative error code for failure.
The hwmon_chip_info structure looks as follows.
struct hwmon_chip_info {
const struct hwmon_ops *ops;
const struct hwmon_channel_info **info;
};
It contains the following fields:
* ops: Pointer to device operations.
* info: NULL-terminated list of device channel descriptors.
The list of hwmon operations is defined as:
struct hwmon_ops {
umode_t (*is_visible)(const void *, enum hwmon_sensor_types type,
u32 attr, int);
int (*read)(struct device *, enum hwmon_sensor_types type,
u32 attr, int, long *);
int (*write)(struct device *, enum hwmon_sensor_types type,
u32 attr, int, long);
};
It defines the following operations.
* is_visible: Pointer to a function to return the file mode for each supported
attribute. This function is mandatory.
* read: Pointer to a function for reading a value from the chip. This function
is optional, but must be provided if any readable attributes exist.
* write: Pointer to a function for writing a value to the chip. This function is
optional, but must be provided if any writeable attributes exist.
Each sensor channel is described with struct hwmon_channel_info, which is
defined as follows.
struct hwmon_channel_info {
enum hwmon_sensor_types type;
u32 *config;
};
It contains following fields:
* type: The hardware monitoring sensor type.
Supported sensor types are
* hwmon_chip A virtual sensor type, used to describe attributes
which apply to the entire chip.
* hwmon_temp Temperature sensor
* hwmon_in Voltage sensor
* hwmon_curr Current sensor
* hwmon_power Power sensor
* hwmon_energy Energy sensor
* hwmon_humidity Humidity sensor
* hwmon_fan Fan speed sensor
* hwmon_pwm PWM control
* config: Pointer to a 0-terminated list of configuration values for each
sensor of the given type. Each value is a combination of bit values
describing the attributes supposed by a single sensor.
As an example, here is the complete description file for a LM75 compatible
sensor chip. The chip has a single temperature sensor. The driver wants to
register with the thermal subsystem (HWMON_C_REGISTER_TZ), and it supports
the update_interval attribute (HWMON_C_UPDATE_INTERVAL). The chip supports
reading the temperature (HWMON_T_INPUT), it has a maximum temperature
register (HWMON_T_MAX) as well as a maximum temperature hysteresis register
(HWMON_T_MAX_HYST).
static const u32 lm75_chip_config[] = {
HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL,
0
};
static const struct hwmon_channel_info lm75_chip = {
.type = hwmon_chip,
.config = lm75_chip_config,
};
static const u32 lm75_temp_config[] = {
HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST,
0
};
static const struct hwmon_channel_info lm75_temp = {
.type = hwmon_temp,
.config = lm75_temp_config,
};
static const struct hwmon_channel_info *lm75_info[] = {
&lm75_chip,
&lm75_temp,
NULL
};
static const struct hwmon_ops lm75_hwmon_ops = {
.is_visible = lm75_is_visible,
.read = lm75_read,
.write = lm75_write,
};
static const struct hwmon_chip_info lm75_chip_info = {
.ops = &lm75_hwmon_ops,
.info = lm75_info,
};
A complete list of bit values indicating individual attribute support
is defined in include/linux/hwmon.h. Definition prefixes are as follows.
HWMON_C_xxxx Chip attributes, for use with hwmon_chip.
HWMON_T_xxxx Temperature attributes, for use with hwmon_temp.
HWMON_I_xxxx Voltage attributes, for use with hwmon_in.
HWMON_C_xxxx Current attributes, for use with hwmon_curr.
Notice the prefix overlap with chip attributes.
HWMON_P_xxxx Power attributes, for use with hwmon_power.
HWMON_E_xxxx Energy attributes, for use with hwmon_energy.
HWMON_H_xxxx Humidity attributes, for use with hwmon_humidity.
HWMON_F_xxxx Fan speed attributes, for use with hwmon_fan.
HWMON_PWM_xxxx PWM control attributes, for use with hwmon_pwm.
Driver callback functions
-------------------------
Each driver provides is_visible, read, and write functions. Parameters
and return values for those functions are as follows.
umode_t is_visible_func(const void *data, enum hwmon_sensor_types type,
u32 attr, int channel)
Parameters:
data: Pointer to device private data structure.
type: The sensor type.
attr: Attribute identifier associated with a specific attribute.
For example, the attribute value for HWMON_T_INPUT would be
hwmon_temp_input. For complete mappings of bit fields to
attribute values please see include/linux/hwmon.h.
channel:The sensor channel number.
Return value:
The file mode for this attribute. Typically, this will be 0 (the
attribute will not be created), S_IRUGO, or 'S_IRUGO | S_IWUSR'.
int read_func(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
Parameters:
dev: Pointer to the hardware monitoring device.
type: The sensor type.
attr: Attribute identifier associated with a specific attribute.
For example, the attribute value for HWMON_T_INPUT would be
hwmon_temp_input. For complete mappings please see
include/linux/hwmon.h.
channel:The sensor channel number.
val: Pointer to attribute value.
Return value:
0 on success, a negative error number otherwise.
int write_func(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long val)
Parameters:
dev: Pointer to the hardware monitoring device.
type: The sensor type.
attr: Attribute identifier associated with a specific attribute.
For example, the attribute value for HWMON_T_INPUT would be
hwmon_temp_input. For complete mappings please see
include/linux/hwmon.h.
channel:The sensor channel number.
val: The value to write to the chip.
Return value:
0 on success, a negative error number otherwise.
Driver-provided sysfs attributes
--------------------------------
If the hardware monitoring device is registered with
hwmon_device_register_with_info or devm_hwmon_device_register_with_info,
it is most likely not necessary to provide sysfs attributes. Only non-standard
sysfs attributes need to be provided when one of those registration functions
is used.
The header file linux/hwmon-sysfs.h provides a number of useful macros to
declare and use hardware monitoring sysfs attributes.
......
......@@ -34,6 +34,7 @@ fan3_input ro "
fan4_input ro "
fan1_target rw desired fan speed in RPM (closed loop mode only)
pwm1_enable rw regulator mode, 0=full on, 1=open loop, 2=closed loop
3=off
pwm1 rw relative speed (0-255), 255=max. speed.
Used in open loop mode only.
fan1_div rw sets the speed range the inputs can handle. Legal
......
......@@ -2,12 +2,13 @@ Kernel driver ucd9000
=====================
Supported chips:
* TI UCD90120, UCD90124, UCD9090, and UCD90910
Prefixes: 'ucd90120', 'ucd90124', 'ucd9090', 'ucd90910'
* TI UCD90120, UCD90124, UCD90160, UCD9090, and UCD90910
Prefixes: 'ucd90120', 'ucd90124', 'ucd90160', 'ucd9090', 'ucd90910'
Addresses scanned: -
Datasheets:
http://focus.ti.com/lit/ds/symlink/ucd90120.pdf
http://focus.ti.com/lit/ds/symlink/ucd90124.pdf
http://focus.ti.com/lit/ds/symlink/ucd90160.pdf
http://focus.ti.com/lit/ds/symlink/ucd9090.pdf
http://focus.ti.com/lit/ds/symlink/ucd90910.pdf
......@@ -32,6 +33,13 @@ interrupts, cascading, or other system functions. Twelve of these pins offer PWM
functionality. Using these pins, the UCD90124 offers support for fan control,
margining, and general-purpose PWM functions.
The UCD90160 is a 16-rail PMBus/I2C addressable power-supply sequencer and
monitor. The device integrates a 12-bit ADC for monitoring up to 16 power-supply
voltage inputs. Twenty-six GPIO pins can be used for power supply enables,
power-on reset signals, external interrupts, cascading, or other system
functions. Twelve of these pins offer PWM functionality. Using these pins, the
UCD90160 offers support for margining, and general-purpose PWM functions.
The UCD9090 is a 10-rail PMBus/I2C addressable power-supply sequencer and
monitor. The device integrates a 12-bit ADC for monitoring up to 10 power-supply
voltage inputs. Twenty-three GPIO pins can be used for power supply enables,
......
Kernel driver xgene-hwmon
========================
Supported chips:
* APM X-Gene SoC
Description
-----------
This driver adds hardware temperature and power reading support for
APM X-Gene SoC using the mailbox communication interface.
For device tree, it is the standard DT mailbox.
For ACPI, it is the PCC mailbox.
The following sensors are supported
* Temperature
- SoC on-die temperature in milli-degree C
- Alarm when high/over temperature occurs
* Power
- CPU power in uW
- IO power in uW
sysfs-Interface
---------------
temp0_input - SoC on-die temperature (milli-degree C)
temp0_critical_alarm - An 1 would indicates on-die temperature exceeded threshold
power0_input - CPU power in (uW)
power1_input - IO power in (uW)
......@@ -969,7 +969,6 @@ config SENSORS_LM73
config SENSORS_LM75
tristate "National Semiconductor LM75 and compatibles"
depends on I2C
depends on THERMAL || !THERMAL_OF
select REGMAP_I2C
help
If you say yes here you get support for one common type of
......@@ -1119,6 +1118,7 @@ config SENSORS_LM95241
config SENSORS_LM95245
tristate "National Semiconductor LM95245 and compatibles"
depends on I2C
select REGMAP_I2C
help
If you say yes here you get support for LM95235 and LM95245
temperature sensor chips.
......@@ -1572,7 +1572,6 @@ config SENSORS_THMC50
config SENSORS_TMP102
tristate "Texas Instruments TMP102"
depends on I2C
depends on THERMAL || !THERMAL_OF
select REGMAP_I2C
help
If you say yes here you get support for Texas Instruments TMP102
......@@ -1823,6 +1822,13 @@ config SENSORS_ULTRA45
This driver provides support for the Ultra45 workstation environmental
sensors.
config SENSORS_XGENE
tristate "APM X-Gene SoC hardware monitoring driver"
depends on XGENE_SLIMPRO_MBOX || PCC
help
If you say yes here you get support for the temperature
and power sensors for APM X-Gene SoC.
if ACPI
comment "ACPI drivers"
......
......@@ -165,6 +165,7 @@ obj-$(CONFIG_SENSORS_W83L785TS) += w83l785ts.o
obj-$(CONFIG_SENSORS_W83L786NG) += w83l786ng.o
obj-$(CONFIG_SENSORS_WM831X) += wm831x-hwmon.o
obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o
obj-$(CONFIG_SENSORS_XGENE) += xgene-hwmon.o
obj-$(CONFIG_PMBUS) += pmbus/
......
......@@ -7,8 +7,7 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* TODO: SPI, support for external temperature sensor
* use power-down mode for suspend?, interrupt handling?
* TODO: SPI, use power-down mode for suspend?, interrupt handling?
*/
#include <linux/kernel.h>
......@@ -31,6 +30,7 @@
#define ADT7411_REG_CFG1 0x18
#define ADT7411_CFG1_START_MONITOR (1 << 0)
#define ADT7411_CFG1_RESERVED_BIT1 (1 << 1)
#define ADT7411_CFG1_EXT_TDM (1 << 2)
#define ADT7411_CFG1_RESERVED_BIT3 (1 << 3)
#define ADT7411_REG_CFG2 0x19
......@@ -57,6 +57,7 @@ struct adt7411_data {
unsigned long next_update;
int vref_cached;
struct i2c_client *client;
bool use_ext_temp;
};
/*
......@@ -127,11 +128,20 @@ static ssize_t adt7411_show_vdd(struct device *dev,
static ssize_t adt7411_show_temp(struct device *dev,
struct device_attribute *attr, char *buf)
{
int nr = to_sensor_dev_attr(attr)->index;
struct adt7411_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
int val = adt7411_read_10_bit(client, ADT7411_REG_INT_TEMP_VDD_LSB,
ADT7411_REG_INT_TEMP_MSB, 0);
int val;
struct {
u8 low;
u8 high;
} reg[2] = {
{ ADT7411_REG_INT_TEMP_VDD_LSB, ADT7411_REG_INT_TEMP_MSB },
{ ADT7411_REG_EXT_TEMP_AIN14_LSB,
ADT7411_REG_EXT_TEMP_AIN1_MSB },
};
val = adt7411_read_10_bit(client, reg[nr].low, reg[nr].high, 0);
if (val < 0)
return val;
......@@ -218,11 +228,13 @@ static ssize_t adt7411_set_bit(struct device *dev,
return ret < 0 ? ret : count;
}
#define ADT7411_BIT_ATTR(__name, __reg, __bit) \
SENSOR_DEVICE_ATTR_2(__name, S_IRUGO | S_IWUSR, adt7411_show_bit, \
adt7411_set_bit, __bit, __reg)
static DEVICE_ATTR(temp1_input, S_IRUGO, adt7411_show_temp, NULL);
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, adt7411_show_temp, NULL, 0);
static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, adt7411_show_temp, NULL, 1);
static DEVICE_ATTR(in0_input, S_IRUGO, adt7411_show_vdd, NULL);
static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, adt7411_show_input, NULL, 0);
static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, adt7411_show_input, NULL, 1);
......@@ -237,7 +249,8 @@ static ADT7411_BIT_ATTR(fast_sampling, ADT7411_REG_CFG3, ADT7411_CFG3_ADC_CLK_22
static ADT7411_BIT_ATTR(adc_ref_vdd, ADT7411_REG_CFG3, ADT7411_CFG3_REF_VDD);
static struct attribute *adt7411_attrs[] = {
&dev_attr_temp1_input.attr,
&sensor_dev_attr_temp1_input.dev_attr.attr,
&sensor_dev_attr_temp2_input.dev_attr.attr,
&dev_attr_in0_input.attr,
&sensor_dev_attr_in1_input.dev_attr.attr,
&sensor_dev_attr_in2_input.dev_attr.attr,
......@@ -253,7 +266,27 @@ static struct attribute *adt7411_attrs[] = {
NULL
};
ATTRIBUTE_GROUPS(adt7411);
static umode_t adt7411_attrs_visible(struct kobject *kobj,
struct attribute *attr, int index)
{
struct device *dev = container_of(kobj, struct device, kobj);
struct adt7411_data *data = dev_get_drvdata(dev);
bool visible = true;
if (attr == &sensor_dev_attr_temp2_input.dev_attr.attr)
visible = data->use_ext_temp;
else if (attr == &sensor_dev_attr_in1_input.dev_attr.attr ||
attr == &sensor_dev_attr_in2_input.dev_attr.attr)
visible = !data->use_ext_temp;
return visible ? attr->mode : 0;
}
static const struct attribute_group adt7411_group = {
.attrs = adt7411_attrs,
.is_visible = adt7411_attrs_visible,
};
__ATTRIBUTE_GROUPS(adt7411);
static int adt7411_detect(struct i2c_client *client,
struct i2c_board_info *info)
......@@ -309,6 +342,8 @@ static int adt7411_init_device(struct adt7411_data *data)
if (ret < 0)
return ret;
data->use_ext_temp = ret & ADT7411_CFG1_EXT_TDM;
/*
* We must only write zero to bit 1 and only one to bit 3 according to
* the datasheet.
......
......@@ -32,6 +32,7 @@
#include <linux/log2.h>
#include <linux/kthread.h>
#include <linux/slab.h>
#include <linux/util_macros.h>
/* Addresses to scan */
static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
......@@ -83,6 +84,7 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
#define ADT7470_REG_PWM_MIN_MAX_ADDR 0x6D
#define ADT7470_REG_PWM_TEMP_MIN_BASE_ADDR 0x6E
#define ADT7470_REG_PWM_TEMP_MIN_MAX_ADDR 0x71
#define ADT7470_REG_CFG_2 0x74
#define ADT7470_REG_ACOUSTICS12 0x75
#define ADT7470_REG_ACOUSTICS34 0x76
#define ADT7470_REG_DEVICE 0x3D
......@@ -142,6 +144,11 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
#define FAN_PERIOD_INVALID 65535
#define FAN_DATA_VALID(x) ((x) && (x) != FAN_PERIOD_INVALID)
/* Config registers 1 and 2 include fields for selecting the PWM frequency */
#define ADT7470_CFG_LF 0x40
#define ADT7470_FREQ_MASK 0x70
#define ADT7470_FREQ_SHIFT 4
struct adt7470_data {
struct i2c_client *client;
struct mutex lock;
......@@ -170,7 +177,6 @@ struct adt7470_data {
u8 pwm_auto_temp[ADT7470_PWM_COUNT];
struct task_struct *auto_update;
struct completion auto_update_stop;
unsigned int auto_update_interval;
};
......@@ -266,12 +272,14 @@ static int adt7470_update_thread(void *p)
mutex_lock(&data->lock);
adt7470_read_temperatures(client, data);
mutex_unlock(&data->lock);
set_current_state(TASK_INTERRUPTIBLE);
if (kthread_should_stop())
break;
msleep_interruptible(data->auto_update_interval);
schedule_timeout(msecs_to_jiffies(data->auto_update_interval));
}
complete_all(&data->auto_update_stop);
return 0;
}
......@@ -538,6 +546,28 @@ static ssize_t show_alarm_mask(struct device *dev,
return sprintf(buf, "%x\n", data->alarms_mask);
}
static ssize_t set_alarm_mask(struct device *dev,
struct device_attribute *devattr,
const char *buf,
size_t count)
{
struct adt7470_data *data = dev_get_drvdata(dev);
long mask;
if (kstrtoul(buf, 0, &mask))
return -EINVAL;
if (mask & ~0xffff)
return -EINVAL;
mutex_lock(&data->lock);
data->alarms_mask = mask;
adt7470_write_word_data(data->client, ADT7470_REG_ALARM1_MASK, mask);
mutex_unlock(&data->lock);
return count;
}
static ssize_t show_fan_max(struct device *dev,
struct device_attribute *devattr,
char *buf)
......@@ -688,6 +718,70 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr,
return count;
}
/* These are the valid PWM frequencies to the nearest Hz */
static const int adt7470_freq_map[] = {
11, 15, 22, 29, 35, 44, 59, 88, 1400, 22500
};
static ssize_t show_pwm_freq(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct adt7470_data *data = adt7470_update_device(dev);
unsigned char cfg_reg_1;
unsigned char cfg_reg_2;
int index;
mutex_lock(&data->lock);
cfg_reg_1 = i2c_smbus_read_byte_data(data->client, ADT7470_REG_CFG);
cfg_reg_2 = i2c_smbus_read_byte_data(data->client, ADT7470_REG_CFG_2);
mutex_unlock(&data->lock);
index = (cfg_reg_2 & ADT7470_FREQ_MASK) >> ADT7470_FREQ_SHIFT;
if (!(cfg_reg_1 & ADT7470_CFG_LF))
index += 8;
if (index >= ARRAY_SIZE(adt7470_freq_map))
index = ARRAY_SIZE(adt7470_freq_map) - 1;
return scnprintf(buf, PAGE_SIZE, "%d\n", adt7470_freq_map[index]);
}
static ssize_t set_pwm_freq(struct device *dev,
struct device_attribute *devattr,
const char *buf, size_t count)
{
struct adt7470_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
long freq;
int index;
int low_freq = ADT7470_CFG_LF;
unsigned char val;
if (kstrtol(buf, 10, &freq))
return -EINVAL;
/* Round the user value given to the closest available frequency */
index = find_closest(freq, adt7470_freq_map,
ARRAY_SIZE(adt7470_freq_map));
if (index >= 8) {
index -= 8;
low_freq = 0;
}
mutex_lock(&data->lock);
/* Configuration Register 1 */
val = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG);
i2c_smbus_write_byte_data(client, ADT7470_REG_CFG,
(val & ~ADT7470_CFG_LF) | low_freq);
/* Configuration Register 2 */
val = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG_2);
i2c_smbus_write_byte_data(client, ADT7470_REG_CFG_2,
(val & ~ADT7470_FREQ_MASK) | (index << ADT7470_FREQ_SHIFT));
mutex_unlock(&data->lock);
return count;
}
static ssize_t show_pwm_max(struct device *dev,
struct device_attribute *devattr,
char *buf)
......@@ -918,7 +1012,8 @@ static ssize_t show_alarm(struct device *dev,
return sprintf(buf, "0\n");
}
static DEVICE_ATTR(alarm_mask, S_IRUGO, show_alarm_mask, NULL);
static DEVICE_ATTR(alarm_mask, S_IWUSR | S_IRUGO, show_alarm_mask,
set_alarm_mask);
static DEVICE_ATTR(num_temp_sensors, S_IWUSR | S_IRUGO, show_num_temp_sensors,
set_num_temp_sensors);
static DEVICE_ATTR(auto_update_interval, S_IWUSR | S_IRUGO,
......@@ -1038,6 +1133,8 @@ static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 1);
static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 2);
static SENSOR_DEVICE_ATTR(pwm4, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 3);
static DEVICE_ATTR(pwm1_freq, S_IWUSR | S_IRUGO, show_pwm_freq, set_pwm_freq);
static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, S_IWUSR | S_IRUGO,
show_pwm_min, set_pwm_min, 0);
static SENSOR_DEVICE_ATTR(pwm2_auto_point1_pwm, S_IWUSR | S_IRUGO,
......@@ -1154,6 +1251,7 @@ static struct attribute *adt7470_attrs[] = {
&sensor_dev_attr_fan4_alarm.dev_attr.attr,
&sensor_dev_attr_force_pwm_max.dev_attr.attr,
&sensor_dev_attr_pwm1.dev_attr.attr,
&dev_attr_pwm1_freq.attr,
&sensor_dev_attr_pwm2.dev_attr.attr,
&sensor_dev_attr_pwm3.dev_attr.attr,
&sensor_dev_attr_pwm4.dev_attr.attr,
......@@ -1256,7 +1354,6 @@ static int adt7470_probe(struct i2c_client *client,
if (IS_ERR(hwmon_dev))
return PTR_ERR(hwmon_dev);
init_completion(&data->auto_update_stop);
data->auto_update = kthread_run(adt7470_update_thread, client, "%s",
dev_name(hwmon_dev));
if (IS_ERR(data->auto_update)) {
......@@ -1271,7 +1368,6 @@ static int adt7470_remove(struct i2c_client *client)
struct adt7470_data *data = i2c_get_clientdata(client);
kthread_stop(data->auto_update);
wait_for_completion(&data->auto_update_stop);
return 0;
}
......
......@@ -36,6 +36,10 @@
#define FTS_EVENT_STATUS_REG 0x0006
#define FTS_GLOBAL_CONTROL_REG 0x0007
#define FTS_DEVICE_DETECT_REG_1 0x0C
#define FTS_DEVICE_DETECT_REG_2 0x0D
#define FTS_DEVICE_DETECT_REG_3 0x0E
#define FTS_SENSOR_EVENT_REG 0x0010
#define FTS_FAN_EVENT_REG 0x0014
......@@ -54,6 +58,8 @@
#define FTS_NO_TEMP_SENSORS 0x10
#define FTS_NO_VOLT_SENSORS 0x04
static const unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END };
static struct i2c_device_id fts_id[] = {
{ "ftsteutates", 0 },
{ }
......@@ -734,6 +740,42 @@ static const struct attribute_group *fts_attr_groups[] = {
/*****************************************************************************/
/* Module initialization / remove functions */
/*****************************************************************************/
static int fts_detect(struct i2c_client *client,
struct i2c_board_info *info)
{
int val;
/* detection works with revsion greater or equal to 0x2b */
val = i2c_smbus_read_byte_data(client, FTS_DEVICE_REVISION_REG);
if (val < 0x2b)
return -ENODEV;
/* Device Detect Regs must have 0x17 0x34 and 0x54 */
val = i2c_smbus_read_byte_data(client, FTS_DEVICE_DETECT_REG_1);
if (val != 0x17)
return -ENODEV;
val = i2c_smbus_read_byte_data(client, FTS_DEVICE_DETECT_REG_2);
if (val != 0x34)
return -ENODEV;
val = i2c_smbus_read_byte_data(client, FTS_DEVICE_DETECT_REG_3);
if (val != 0x54)
return -ENODEV;
/*
* 0x10 == Baseboard Management Controller, 0x01 == Teutates
* Device ID Reg needs to be 0x11
*/
val = i2c_smbus_read_byte_data(client, FTS_DEVICE_ID_REG);
if (val != 0x11)
return -ENODEV;
strlcpy(info->type, fts_id[0].name, I2C_NAME_SIZE);
info->flags = 0;
return 0;
}
static int fts_remove(struct i2c_client *client)
{
struct fts_data *data = dev_get_drvdata(&client->dev);
......@@ -804,12 +846,15 @@ static int fts_probe(struct i2c_client *client, const struct i2c_device_id *id)
/* Module Details */
/*****************************************************************************/
static struct i2c_driver fts_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = "ftsteutates",
},
.id_table = fts_id,
.probe = fts_probe,
.remove = fts_remove,
.detect = fts_detect,
.address_list = normal_i2c,
};
module_i2c_driver(fts_driver);
......
This diff is collapsed.
......@@ -143,13 +143,11 @@ static void __init make_sensor_label(struct device_node *np,
if (cpuid >= 0)
/*
* The digital thermal sensors are associated
* with a core. Let's print out the range of
* cpu ids corresponding to the hardware
* threads of the core.
* with a core.
*/
n += snprintf(sdata->label + n,
sizeof(sdata->label) - n, " %d-%d",
cpuid, cpuid + threads_per_core - 1);
sizeof(sdata->label) - n, " %d",
cpuid);
else
n += snprintf(sdata->label + n,
sizeof(sdata->label) - n, " phy%d", id);
......
......@@ -73,8 +73,11 @@ static int iio_hwmon_probe(struct platform_device *pdev)
name = dev->of_node->name;
channels = iio_channel_get_all(dev);
if (IS_ERR(channels))
if (IS_ERR(channels)) {
if (PTR_ERR(channels) == -ENODEV)
return -EPROBE_DEFER;
return PTR_ERR(channels);
}
st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
if (st == NULL) {
......
......@@ -2011,10 +2011,10 @@ static struct attribute *it87_attributes_in[] = {
&sensor_dev_attr_in7_beep.dev_attr.attr, /* 39 */
&sensor_dev_attr_in8_input.dev_attr.attr, /* 40 */
&sensor_dev_attr_in9_input.dev_attr.attr, /* 41 */
&sensor_dev_attr_in10_input.dev_attr.attr, /* 41 */
&sensor_dev_attr_in11_input.dev_attr.attr, /* 41 */
&sensor_dev_attr_in12_input.dev_attr.attr, /* 41 */
&sensor_dev_attr_in9_input.dev_attr.attr,
&sensor_dev_attr_in10_input.dev_attr.attr,
&sensor_dev_attr_in11_input.dev_attr.attr,
&sensor_dev_attr_in12_input.dev_attr.attr,
NULL
};
......
This diff is collapsed.
......@@ -28,7 +28,6 @@
#include <linux/err.h>
#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/thermal.h>
#include "lm75.h"
......@@ -88,56 +87,75 @@ static inline long lm75_reg_to_mc(s16 temp, u8 resolution)
return ((temp >> (16 - resolution)) * 1000) >> (resolution - 8);
}
/* sysfs attributes for hwmon */
static int lm75_read_temp(void *dev, int *temp)
static int lm75_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
struct lm75_data *data = dev_get_drvdata(dev);
unsigned int _temp;
int err;
err = regmap_read(data->regmap, LM75_REG_TEMP, &_temp);
if (err < 0)
return err;
*temp = lm75_reg_to_mc(_temp, data->resolution);
unsigned int regval;
int err, reg;
switch (type) {
case hwmon_chip:
switch (attr) {
case hwmon_chip_update_interval:
*val = data->sample_time;
break;;
default:
return -EINVAL;
}
break;
case hwmon_temp:
switch (attr) {
case hwmon_temp_input:
reg = LM75_REG_TEMP;
break;
case hwmon_temp_max:
reg = LM75_REG_MAX;
break;
case hwmon_temp_max_hyst:
reg = LM75_REG_HYST;
break;
default:
return -EINVAL;
}
err = regmap_read(data->regmap, reg, &regval);
if (err < 0)
return err;
*val = lm75_reg_to_mc(regval, data->resolution);
break;
default:
return -EINVAL;
}
return 0;
}
static ssize_t show_temp(struct device *dev, struct device_attribute *da,
char *buf)
static int lm75_write(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long temp)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
struct lm75_data *data = dev_get_drvdata(dev);
unsigned int temp = 0;
int err;
err = regmap_read(data->regmap, attr->index, &temp);
if (err < 0)
return err;
return sprintf(buf, "%ld\n", lm75_reg_to_mc(temp, data->resolution));
}
static ssize_t set_temp(struct device *dev, struct device_attribute *da,
const char *buf, size_t count)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
struct lm75_data *data = dev_get_drvdata(dev);
long temp;
int error;
u8 resolution;
int reg;
if (type != hwmon_temp)
return -EINVAL;
error = kstrtol(buf, 10, &temp);
if (error)
return error;
switch (attr) {
case hwmon_temp_max:
reg = LM75_REG_MAX;
break;
case hwmon_temp_max_hyst:
reg = LM75_REG_HYST;
break;
default:
return -EINVAL;
}
/*
* Resolution of limit registers is assumed to be the same as the
* temperature input register resolution unless given explicitly.
*/
if (attr->index && data->resolution_limits)
if (data->resolution_limits)
resolution = data->resolution_limits;
else
resolution = data->resolution;
......@@ -145,45 +163,77 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *da,
temp = clamp_val(temp, LM75_TEMP_MIN, LM75_TEMP_MAX);
temp = DIV_ROUND_CLOSEST(temp << (resolution - 8),
1000) << (16 - resolution);
error = regmap_write(data->regmap, attr->index, temp);
if (error < 0)
return error;
return count;
return regmap_write(data->regmap, reg, temp);
}
static ssize_t show_update_interval(struct device *dev,
struct device_attribute *da, char *buf)
static umode_t lm75_is_visible(const void *data, enum hwmon_sensor_types type,
u32 attr, int channel)
{
struct lm75_data *data = dev_get_drvdata(dev);
return sprintf(buf, "%u\n", data->sample_time);
switch (type) {
case hwmon_chip:
switch (attr) {
case hwmon_chip_update_interval:
return S_IRUGO;
}
break;
case hwmon_temp:
switch (attr) {
case hwmon_temp_input:
return S_IRUGO;
case hwmon_temp_max:
case hwmon_temp_max_hyst:
return S_IRUGO | S_IWUSR;
}
break;
default:
break;
}
return 0;
}
static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO,
show_temp, set_temp, LM75_REG_MAX);
static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO,
show_temp, set_temp, LM75_REG_HYST);
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, LM75_REG_TEMP);
static DEVICE_ATTR(update_interval, S_IRUGO, show_update_interval, NULL);
/*-----------------------------------------------------------------------*/
static struct attribute *lm75_attrs[] = {
&sensor_dev_attr_temp1_input.dev_attr.attr,
&sensor_dev_attr_temp1_max.dev_attr.attr,
&sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
&dev_attr_update_interval.attr,
/* device probe and removal */
NULL
/* chip configuration */
static const u32 lm75_chip_config[] = {
HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL,
0
};
ATTRIBUTE_GROUPS(lm75);
static const struct thermal_zone_of_device_ops lm75_of_thermal_ops = {
.get_temp = lm75_read_temp,
static const struct hwmon_channel_info lm75_chip = {
.type = hwmon_chip,
.config = lm75_chip_config,
};
/*-----------------------------------------------------------------------*/
static const u32 lm75_temp_config[] = {
HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST,
0
};
/* device probe and removal */
static const struct hwmon_channel_info lm75_temp = {
.type = hwmon_temp,
.config = lm75_temp_config,
};
static const struct hwmon_channel_info *lm75_info[] = {
&lm75_chip,
&lm75_temp,
NULL
};
static const struct hwmon_ops lm75_hwmon_ops = {
.is_visible = lm75_is_visible,
.read = lm75_read,
.write = lm75_write,
};
static const struct hwmon_chip_info lm75_chip_info = {
.ops = &lm75_hwmon_ops,
.info = lm75_info,
};
static bool lm75_is_writeable_reg(struct device *dev, unsigned int reg)
{
......@@ -337,15 +387,12 @@ lm75_probe(struct i2c_client *client, const struct i2c_device_id *id)
dev_dbg(dev, "Config %02x\n", new);
hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
data, lm75_groups);
hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
data, &lm75_chip_info,
NULL);
if (IS_ERR(hwmon_dev))
return PTR_ERR(hwmon_dev);
devm_thermal_zone_of_sensor_register(hwmon_dev, 0,
hwmon_dev,
&lm75_of_thermal_ops);
dev_info(dev, "%s: sensor '%s'\n", dev_name(hwmon_dev), client->name);
return 0;
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -30,6 +30,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/slab.h>
......@@ -52,6 +53,7 @@ struct ltc4151_data {
struct mutex update_lock;
bool valid;
unsigned long last_updated; /* in jiffies */
unsigned int shunt; /* in micro ohms */
/* Registers */
u8 regs[6];
......@@ -111,9 +113,9 @@ static int ltc4151_get_value(struct ltc4151_data *data, u8 reg)
case LTC4151_SENSE_H:
/*
* 20uV resolution. Convert to current as measured with
* an 1 mOhm sense resistor, in mA.
* a given sense resistor, in mA.
*/
val = val * 20;
val = val * 20 * 1000 / data->shunt;
break;
case LTC4151_VIN_H:
/* 25 mV per increment */
......@@ -176,6 +178,7 @@ static int ltc4151_probe(struct i2c_client *client,
struct device *dev = &client->dev;
struct ltc4151_data *data;
struct device *hwmon_dev;
u32 shunt;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
......@@ -184,6 +187,15 @@ static int ltc4151_probe(struct i2c_client *client,
if (!data)
return -ENOMEM;
if (of_property_read_u32(client->dev.of_node,
"shunt-resistor-micro-ohms", &shunt))
shunt = 1000; /* 1 mOhm if not set via DT */
if (shunt == 0)
return -EINVAL;
data->shunt = shunt;
data->client = client;
mutex_init(&data->update_lock);
......@@ -199,10 +211,16 @@ static const struct i2c_device_id ltc4151_id[] = {
};
MODULE_DEVICE_TABLE(i2c, ltc4151_id);
static const struct of_device_id ltc4151_match[] = {
{ .compatible = "lltc,ltc4151" },
{},
};
/* This is the driver that will be inserted */
static struct i2c_driver ltc4151_driver = {
.driver = {
.name = "ltc4151",
.of_match_table = of_match_ptr(ltc4151_match),
},
.probe = ltc4151_probe,
.id_table = ltc4151_id,
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -195,6 +195,8 @@ superio_exit(int ioreg)
#define NUM_FAN 6
#define TEMP_SOURCE_VIRTUAL 0x1f
/* Common and NCT6775 specific data */
/* Voltage min/max registers for nr=7..14 are in bank 5 */
......@@ -3940,7 +3942,7 @@ static int nct6775_probe(struct platform_device *pdev)
continue;
src = nct6775_read_value(data, data->REG_TEMP_SEL[i]) & 0x1f;
if (!src || (mask & (1 << src)))
if (!src)
continue;
if (src >= data->temp_label_num ||
......@@ -3952,7 +3954,16 @@ static int nct6775_probe(struct platform_device *pdev)
continue;
}
mask |= 1 << src;
/*
* For virtual temperature sources, the 'virtual' temperature
* for each fan reflects a different temperature, and there
* are no duplicates.
*/
if (src != TEMP_SOURCE_VIRTUAL) {
if (mask & (1 << src))
continue;
mask |= 1 << src;
}
/* Use fixed index for SYSTIN(1), CPUTIN(2), AUXTIN(3) */
if (src <= data->temp_fixed_num) {
......@@ -4232,11 +4243,11 @@ static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data)
if (err)
return err;
if (force_id)
val = (superio_inb(sioaddr, SIO_REG_DEVID) << 8) |
superio_inb(sioaddr, SIO_REG_DEVID + 1);
if (force_id && val != 0xffff)
val = force_id;
else
val = (superio_inb(sioaddr, SIO_REG_DEVID) << 8)
| superio_inb(sioaddr, SIO_REG_DEVID + 1);
switch (val & SIO_ID_MASK) {
case SIO_NCT6106_ID:
sio_data->kind = nct6106;
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment