hwmon updates for v4.9

- 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
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABAgAGBQJX8cQ6AAoJEMsfJm/On5mBlWIP/RpA4b05gsYOG1SVW/SuZZPu
 IJfFkvGgOORgMS0gg8bNk3WyNNnqinbB4A6xucR/7Q7gRyk+3NYR6mNIsyP3UcAm
 KHJF9jgUr02vO6reMGGrqrR3cQ14omHgHqXBWQny7JkZ3/6Xyvg/LuarAZ+ZsUfp
 cNALIBsHoxyBSF4W7g5PJILtVbA+aW854CQ7UU/S0I2rjR9cqLu9HHvPUeYIazfO
 Tn4XM1vZCPLH5OhvIRURCy7FaZjwmxhPZhgF5fqAgDUF0VTQAhiOZB9jcz5NQq2q
 sSzlXSd2qqdJSTabtaBDl3O9GsfFrtT0LsFT0Rycx9JedgbV+Euy240uGVUnbHdT
 Gh8TVmOYx6qldDj/Hu5DF+kKnIH8vL6fp5blA6b/3EKwv14MUBNC/hxrmOcxyo5T
 qBnos0FlF9Ut4QLdRWM0rorZkCWxJWCXV8CvPX79b8iospKD/+Fgr69z7UgZuljA
 rcXZJR65oz4cJGMFr4dR+CcC0y2Jqqn5mcQAaGSR0RcII6EpSa+3Zb+2nBE4VGyQ
 vZpmxEEjMu9C8SI5DAczLwIMOsW4/pTTB6f8lLKiKRM2eJ7mAyk0KCF+9JIaKjaL
 qigWHlJI9FpXQiuek+pMXTVBTzs6fO/NSVfPUfEyAoyyuQePCjqVsQdn/h+lzQHC
 1FiFIvnzlxb4ESZ5pfS1
 =FLdn
 -----END PGP SIGNATURE-----

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
  ...
This commit is contained in:
Linus Torvalds 2016-10-04 10:56:14 -07:00
commit 77b0a4aa07
36 changed files with 4748 additions and 2284 deletions

View File

@ -0,0 +1,18 @@
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>;
};

View File

@ -0,0 +1,28 @@
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>;
};

View File

@ -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 temperature sensor associated with the PWM control exceeds
pwm#_auto_point2_temp. 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 Notes
----- -----

View File

@ -34,6 +34,19 @@ devm_hwmon_device_register_with_groups(struct device *dev,
const char *name, void *drvdata, const char *name, void *drvdata,
const struct attribute_group **groups); 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 hwmon_device_unregister(struct device *dev);
void devm_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_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 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. hwmon_device_unregister deregisters a registered hardware monitoring device.
The parameter of this function is the pointer to the registered hardware The parameter of this function is the pointer to the registered hardware
monitoring device structure. This function must be called from the driver monitoring device structure. This function must be called from the driver
remove function if the hardware monitoring device was registered with 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 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 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 The header file linux/hwmon-sysfs.h provides a number of useful macros to
declare and use hardware monitoring sysfs attributes. declare and use hardware monitoring sysfs attributes.

View File

@ -34,6 +34,7 @@ fan3_input ro "
fan4_input ro " fan4_input ro "
fan1_target rw desired fan speed in RPM (closed loop mode only) 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 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. pwm1 rw relative speed (0-255), 255=max. speed.
Used in open loop mode only. Used in open loop mode only.
fan1_div rw sets the speed range the inputs can handle. Legal fan1_div rw sets the speed range the inputs can handle. Legal

View File

@ -2,12 +2,13 @@ Kernel driver ucd9000
===================== =====================
Supported chips: Supported chips:
* TI UCD90120, UCD90124, UCD9090, and UCD90910 * TI UCD90120, UCD90124, UCD90160, UCD9090, and UCD90910
Prefixes: 'ucd90120', 'ucd90124', 'ucd9090', 'ucd90910' Prefixes: 'ucd90120', 'ucd90124', 'ucd90160', 'ucd9090', 'ucd90910'
Addresses scanned: - Addresses scanned: -
Datasheets: Datasheets:
http://focus.ti.com/lit/ds/symlink/ucd90120.pdf 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/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/ucd9090.pdf
http://focus.ti.com/lit/ds/symlink/ucd90910.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, functionality. Using these pins, the UCD90124 offers support for fan control,
margining, and general-purpose PWM functions. 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 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 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, voltage inputs. Twenty-three GPIO pins can be used for power supply enables,

View File

@ -0,0 +1,30 @@
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)

View File

@ -969,7 +969,6 @@ config SENSORS_LM73
config SENSORS_LM75 config SENSORS_LM75
tristate "National Semiconductor LM75 and compatibles" tristate "National Semiconductor LM75 and compatibles"
depends on I2C depends on I2C
depends on THERMAL || !THERMAL_OF
select REGMAP_I2C select REGMAP_I2C
help help
If you say yes here you get support for one common type of If you say yes here you get support for one common type of
@ -1119,6 +1118,7 @@ config SENSORS_LM95241
config SENSORS_LM95245 config SENSORS_LM95245
tristate "National Semiconductor LM95245 and compatibles" tristate "National Semiconductor LM95245 and compatibles"
depends on I2C depends on I2C
select REGMAP_I2C
help help
If you say yes here you get support for LM95235 and LM95245 If you say yes here you get support for LM95235 and LM95245
temperature sensor chips. temperature sensor chips.
@ -1572,7 +1572,6 @@ config SENSORS_THMC50
config SENSORS_TMP102 config SENSORS_TMP102
tristate "Texas Instruments TMP102" tristate "Texas Instruments TMP102"
depends on I2C depends on I2C
depends on THERMAL || !THERMAL_OF
select REGMAP_I2C select REGMAP_I2C
help help
If you say yes here you get support for Texas Instruments TMP102 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 This driver provides support for the Ultra45 workstation environmental
sensors. 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 if ACPI
comment "ACPI drivers" comment "ACPI drivers"

View File

@ -165,6 +165,7 @@ obj-$(CONFIG_SENSORS_W83L785TS) += w83l785ts.o
obj-$(CONFIG_SENSORS_W83L786NG) += w83l786ng.o obj-$(CONFIG_SENSORS_W83L786NG) += w83l786ng.o
obj-$(CONFIG_SENSORS_WM831X) += wm831x-hwmon.o obj-$(CONFIG_SENSORS_WM831X) += wm831x-hwmon.o
obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o
obj-$(CONFIG_SENSORS_XGENE) += xgene-hwmon.o
obj-$(CONFIG_PMBUS) += pmbus/ obj-$(CONFIG_PMBUS) += pmbus/

View File

@ -7,8 +7,7 @@
* it under the terms of the GNU General Public License version 2 as * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
* *
* TODO: SPI, support for external temperature sensor * TODO: SPI, use power-down mode for suspend?, interrupt handling?
* use power-down mode for suspend?, interrupt handling?
*/ */
#include <linux/kernel.h> #include <linux/kernel.h>
@ -31,6 +30,7 @@
#define ADT7411_REG_CFG1 0x18 #define ADT7411_REG_CFG1 0x18
#define ADT7411_CFG1_START_MONITOR (1 << 0) #define ADT7411_CFG1_START_MONITOR (1 << 0)
#define ADT7411_CFG1_RESERVED_BIT1 (1 << 1) #define ADT7411_CFG1_RESERVED_BIT1 (1 << 1)
#define ADT7411_CFG1_EXT_TDM (1 << 2)
#define ADT7411_CFG1_RESERVED_BIT3 (1 << 3) #define ADT7411_CFG1_RESERVED_BIT3 (1 << 3)
#define ADT7411_REG_CFG2 0x19 #define ADT7411_REG_CFG2 0x19
@ -57,6 +57,7 @@ struct adt7411_data {
unsigned long next_update; unsigned long next_update;
int vref_cached; int vref_cached;
struct i2c_client *client; 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, static ssize_t adt7411_show_temp(struct device *dev,
struct device_attribute *attr, char *buf) struct device_attribute *attr, char *buf)
{ {
int nr = to_sensor_dev_attr(attr)->index;
struct adt7411_data *data = dev_get_drvdata(dev); struct adt7411_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client; struct i2c_client *client = data->client;
int val = adt7411_read_10_bit(client, ADT7411_REG_INT_TEMP_VDD_LSB, int val;
ADT7411_REG_INT_TEMP_MSB, 0); 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) if (val < 0)
return val; return val;
@ -218,11 +228,13 @@ static ssize_t adt7411_set_bit(struct device *dev,
return ret < 0 ? ret : count; return ret < 0 ? ret : count;
} }
#define ADT7411_BIT_ATTR(__name, __reg, __bit) \ #define ADT7411_BIT_ATTR(__name, __reg, __bit) \
SENSOR_DEVICE_ATTR_2(__name, S_IRUGO | S_IWUSR, adt7411_show_bit, \ SENSOR_DEVICE_ATTR_2(__name, S_IRUGO | S_IWUSR, adt7411_show_bit, \
adt7411_set_bit, __bit, __reg) 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 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(in1_input, S_IRUGO, adt7411_show_input, NULL, 0);
static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, adt7411_show_input, NULL, 1); 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 ADT7411_BIT_ATTR(adc_ref_vdd, ADT7411_REG_CFG3, ADT7411_CFG3_REF_VDD);
static struct attribute *adt7411_attrs[] = { 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, &dev_attr_in0_input.attr,
&sensor_dev_attr_in1_input.dev_attr.attr, &sensor_dev_attr_in1_input.dev_attr.attr,
&sensor_dev_attr_in2_input.dev_attr.attr, &sensor_dev_attr_in2_input.dev_attr.attr,
@ -253,7 +266,27 @@ static struct attribute *adt7411_attrs[] = {
NULL 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, static int adt7411_detect(struct i2c_client *client,
struct i2c_board_info *info) struct i2c_board_info *info)
@ -309,6 +342,8 @@ static int adt7411_init_device(struct adt7411_data *data)
if (ret < 0) if (ret < 0)
return ret; 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 * We must only write zero to bit 1 and only one to bit 3 according to
* the datasheet. * the datasheet.

View File

@ -32,6 +32,7 @@
#include <linux/log2.h> #include <linux/log2.h>
#include <linux/kthread.h> #include <linux/kthread.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/util_macros.h>
/* Addresses to scan */ /* Addresses to scan */
static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END }; 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_MIN_MAX_ADDR 0x6D
#define ADT7470_REG_PWM_TEMP_MIN_BASE_ADDR 0x6E #define ADT7470_REG_PWM_TEMP_MIN_BASE_ADDR 0x6E
#define ADT7470_REG_PWM_TEMP_MIN_MAX_ADDR 0x71 #define ADT7470_REG_PWM_TEMP_MIN_MAX_ADDR 0x71
#define ADT7470_REG_CFG_2 0x74
#define ADT7470_REG_ACOUSTICS12 0x75 #define ADT7470_REG_ACOUSTICS12 0x75
#define ADT7470_REG_ACOUSTICS34 0x76 #define ADT7470_REG_ACOUSTICS34 0x76
#define ADT7470_REG_DEVICE 0x3D #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_PERIOD_INVALID 65535
#define FAN_DATA_VALID(x) ((x) && (x) != FAN_PERIOD_INVALID) #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 adt7470_data {
struct i2c_client *client; struct i2c_client *client;
struct mutex lock; struct mutex lock;
@ -170,7 +177,6 @@ struct adt7470_data {
u8 pwm_auto_temp[ADT7470_PWM_COUNT]; u8 pwm_auto_temp[ADT7470_PWM_COUNT];
struct task_struct *auto_update; struct task_struct *auto_update;
struct completion auto_update_stop;
unsigned int auto_update_interval; unsigned int auto_update_interval;
}; };
@ -266,12 +272,14 @@ static int adt7470_update_thread(void *p)
mutex_lock(&data->lock); mutex_lock(&data->lock);
adt7470_read_temperatures(client, data); adt7470_read_temperatures(client, data);
mutex_unlock(&data->lock); mutex_unlock(&data->lock);
set_current_state(TASK_INTERRUPTIBLE);
if (kthread_should_stop()) if (kthread_should_stop())
break; break;
msleep_interruptible(data->auto_update_interval);
schedule_timeout(msecs_to_jiffies(data->auto_update_interval));
} }
complete_all(&data->auto_update_stop);
return 0; return 0;
} }
@ -538,6 +546,28 @@ static ssize_t show_alarm_mask(struct device *dev,
return sprintf(buf, "%x\n", data->alarms_mask); 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, static ssize_t show_fan_max(struct device *dev,
struct device_attribute *devattr, struct device_attribute *devattr,
char *buf) char *buf)
@ -688,6 +718,70 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr,
return count; 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, static ssize_t show_pwm_max(struct device *dev,
struct device_attribute *devattr, struct device_attribute *devattr,
char *buf) char *buf)
@ -918,7 +1012,8 @@ static ssize_t show_alarm(struct device *dev,
return sprintf(buf, "0\n"); 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, static DEVICE_ATTR(num_temp_sensors, S_IWUSR | S_IRUGO, show_num_temp_sensors,
set_num_temp_sensors); set_num_temp_sensors);
static DEVICE_ATTR(auto_update_interval, S_IWUSR | S_IRUGO, 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(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 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, static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, S_IWUSR | S_IRUGO,
show_pwm_min, set_pwm_min, 0); show_pwm_min, set_pwm_min, 0);
static SENSOR_DEVICE_ATTR(pwm2_auto_point1_pwm, S_IWUSR | S_IRUGO, 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_fan4_alarm.dev_attr.attr,
&sensor_dev_attr_force_pwm_max.dev_attr.attr, &sensor_dev_attr_force_pwm_max.dev_attr.attr,
&sensor_dev_attr_pwm1.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_pwm2.dev_attr.attr,
&sensor_dev_attr_pwm3.dev_attr.attr, &sensor_dev_attr_pwm3.dev_attr.attr,
&sensor_dev_attr_pwm4.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)) if (IS_ERR(hwmon_dev))
return PTR_ERR(hwmon_dev); return PTR_ERR(hwmon_dev);
init_completion(&data->auto_update_stop);
data->auto_update = kthread_run(adt7470_update_thread, client, "%s", data->auto_update = kthread_run(adt7470_update_thread, client, "%s",
dev_name(hwmon_dev)); dev_name(hwmon_dev));
if (IS_ERR(data->auto_update)) { 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); struct adt7470_data *data = i2c_get_clientdata(client);
kthread_stop(data->auto_update); kthread_stop(data->auto_update);
wait_for_completion(&data->auto_update_stop);
return 0; return 0;
} }

View File

@ -36,6 +36,10 @@
#define FTS_EVENT_STATUS_REG 0x0006 #define FTS_EVENT_STATUS_REG 0x0006
#define FTS_GLOBAL_CONTROL_REG 0x0007 #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_SENSOR_EVENT_REG 0x0010
#define FTS_FAN_EVENT_REG 0x0014 #define FTS_FAN_EVENT_REG 0x0014
@ -54,6 +58,8 @@
#define FTS_NO_TEMP_SENSORS 0x10 #define FTS_NO_TEMP_SENSORS 0x10
#define FTS_NO_VOLT_SENSORS 0x04 #define FTS_NO_VOLT_SENSORS 0x04
static const unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END };
static struct i2c_device_id fts_id[] = { static struct i2c_device_id fts_id[] = {
{ "ftsteutates", 0 }, { "ftsteutates", 0 },
{ } { }
@ -734,6 +740,42 @@ static const struct attribute_group *fts_attr_groups[] = {
/*****************************************************************************/ /*****************************************************************************/
/* Module initialization / remove functions */ /* 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) static int fts_remove(struct i2c_client *client)
{ {
struct fts_data *data = dev_get_drvdata(&client->dev); 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 */ /* Module Details */
/*****************************************************************************/ /*****************************************************************************/
static struct i2c_driver fts_driver = { static struct i2c_driver fts_driver = {
.class = I2C_CLASS_HWMON,
.driver = { .driver = {
.name = "ftsteutates", .name = "ftsteutates",
}, },
.id_table = fts_id, .id_table = fts_id,
.probe = fts_probe, .probe = fts_probe,
.remove = fts_remove, .remove = fts_remove,
.detect = fts_detect,
.address_list = normal_i2c,
}; };
module_i2c_driver(fts_driver); module_i2c_driver(fts_driver);

View File

@ -12,17 +12,17 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h> #include <linux/bitops.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/slab.h>
#include <linux/kdev_t.h>
#include <linux/idr.h>
#include <linux/hwmon.h>
#include <linux/gfp.h> #include <linux/gfp.h>
#include <linux/spinlock.h> #include <linux/hwmon.h>
#include <linux/idr.h>
#include <linux/module.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/slab.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/thermal.h>
#define HWMON_ID_PREFIX "hwmon" #define HWMON_ID_PREFIX "hwmon"
#define HWMON_ID_FORMAT HWMON_ID_PREFIX "%d" #define HWMON_ID_FORMAT HWMON_ID_PREFIX "%d"
@ -30,9 +30,35 @@
struct hwmon_device { struct hwmon_device {
const char *name; const char *name;
struct device dev; struct device dev;
const struct hwmon_chip_info *chip;
struct attribute_group group;
const struct attribute_group **groups;
}; };
#define to_hwmon_device(d) container_of(d, struct hwmon_device, dev) #define to_hwmon_device(d) container_of(d, struct hwmon_device, dev)
struct hwmon_device_attribute {
struct device_attribute dev_attr;
const struct hwmon_ops *ops;
enum hwmon_sensor_types type;
u32 attr;
int index;
};
#define to_hwmon_attr(d) \
container_of(d, struct hwmon_device_attribute, dev_attr)
/*
* Thermal zone information
* In addition to the reference to the hwmon device,
* also provides the sensor index.
*/
struct hwmon_thermal_data {
struct hwmon_device *hwdev; /* Reference to hwmon device */
int index; /* sensor index */
};
static ssize_t static ssize_t
show_name(struct device *dev, struct device_attribute *attr, char *buf) show_name(struct device *dev, struct device_attribute *attr, char *buf)
{ {
@ -80,6 +106,498 @@ static struct class hwmon_class = {
static DEFINE_IDA(hwmon_ida); static DEFINE_IDA(hwmon_ida);
/* Thermal zone handling */
/*
* The complex conditional is necessary to avoid a cyclic dependency
* between hwmon and thermal_sys modules.
*/
#if IS_REACHABLE(CONFIG_THERMAL) && defined(CONFIG_THERMAL_OF) && \
(!defined(CONFIG_THERMAL_HWMON) || \
!(defined(MODULE) && IS_MODULE(CONFIG_THERMAL)))
static int hwmon_thermal_get_temp(void *data, int *temp)
{
struct hwmon_thermal_data *tdata = data;
struct hwmon_device *hwdev = tdata->hwdev;
int ret;
long t;
ret = hwdev->chip->ops->read(&hwdev->dev, hwmon_temp, hwmon_temp_input,
tdata->index, &t);
if (ret < 0)
return ret;
*temp = t;
return 0;
}
static struct thermal_zone_of_device_ops hwmon_thermal_ops = {
.get_temp = hwmon_thermal_get_temp,
};
static int hwmon_thermal_add_sensor(struct device *dev,
struct hwmon_device *hwdev, int index)
{
struct hwmon_thermal_data *tdata;
tdata = devm_kzalloc(dev, sizeof(*tdata), GFP_KERNEL);
if (!tdata)
return -ENOMEM;
tdata->hwdev = hwdev;
tdata->index = index;
devm_thermal_zone_of_sensor_register(&hwdev->dev, index, tdata,
&hwmon_thermal_ops);
return 0;
}
#else
static int hwmon_thermal_add_sensor(struct device *dev,
struct hwmon_device *hwdev, int index)
{
return 0;
}
#endif /* IS_REACHABLE(CONFIG_THERMAL) && ... */
/* sysfs attribute management */
static ssize_t hwmon_attr_show(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct hwmon_device_attribute *hattr = to_hwmon_attr(devattr);
long val;
int ret;
ret = hattr->ops->read(dev, hattr->type, hattr->attr, hattr->index,
&val);
if (ret < 0)
return ret;
return sprintf(buf, "%ld\n", val);
}
static ssize_t hwmon_attr_store(struct device *dev,
struct device_attribute *devattr,
const char *buf, size_t count)
{
struct hwmon_device_attribute *hattr = to_hwmon_attr(devattr);
long val;
int ret;
ret = kstrtol(buf, 10, &val);
if (ret < 0)
return ret;
ret = hattr->ops->write(dev, hattr->type, hattr->attr, hattr->index,
val);
if (ret < 0)
return ret;
return count;
}
static int hwmon_attr_base(enum hwmon_sensor_types type)
{
if (type == hwmon_in)
return 0;
return 1;
}
static struct attribute *hwmon_genattr(struct device *dev,
const void *drvdata,
enum hwmon_sensor_types type,
u32 attr,
int index,
const char *template,
const struct hwmon_ops *ops)
{
struct hwmon_device_attribute *hattr;
struct device_attribute *dattr;
struct attribute *a;
umode_t mode;
char *name;
/* The attribute is invisible if there is no template string */
if (!template)
return ERR_PTR(-ENOENT);
mode = ops->is_visible(drvdata, type, attr, index);
if (!mode)
return ERR_PTR(-ENOENT);
if ((mode & S_IRUGO) && !ops->read)
return ERR_PTR(-EINVAL);
if ((mode & S_IWUGO) && !ops->write)
return ERR_PTR(-EINVAL);
if (type == hwmon_chip) {
name = (char *)template;
} else {
name = devm_kzalloc(dev, strlen(template) + 16, GFP_KERNEL);
if (!name)
return ERR_PTR(-ENOMEM);
scnprintf(name, strlen(template) + 16, template,
index + hwmon_attr_base(type));
}
hattr = devm_kzalloc(dev, sizeof(*hattr), GFP_KERNEL);
if (!hattr)
return ERR_PTR(-ENOMEM);
hattr->type = type;
hattr->attr = attr;
hattr->index = index;
hattr->ops = ops;
dattr = &hattr->dev_attr;
dattr->show = hwmon_attr_show;
dattr->store = hwmon_attr_store;
a = &dattr->attr;
sysfs_attr_init(a);
a->name = name;
a->mode = mode;
return a;
}
static const char * const hwmon_chip_attr_templates[] = {
[hwmon_chip_temp_reset_history] = "temp_reset_history",
[hwmon_chip_in_reset_history] = "in_reset_history",
[hwmon_chip_curr_reset_history] = "curr_reset_history",
[hwmon_chip_power_reset_history] = "power_reset_history",
[hwmon_chip_update_interval] = "update_interval",
[hwmon_chip_alarms] = "alarms",
};
static const char * const hwmon_temp_attr_templates[] = {
[hwmon_temp_input] = "temp%d_input",
[hwmon_temp_type] = "temp%d_type",
[hwmon_temp_lcrit] = "temp%d_lcrit",
[hwmon_temp_lcrit_hyst] = "temp%d_lcrit_hyst",
[hwmon_temp_min] = "temp%d_min",
[hwmon_temp_min_hyst] = "temp%d_min_hyst",
[hwmon_temp_max] = "temp%d_max",
[hwmon_temp_max_hyst] = "temp%d_max_hyst",
[hwmon_temp_crit] = "temp%d_crit",
[hwmon_temp_crit_hyst] = "temp%d_crit_hyst",
[hwmon_temp_emergency] = "temp%d_emergency",
[hwmon_temp_emergency_hyst] = "temp%d_emergency_hyst",
[hwmon_temp_alarm] = "temp%d_alarm",
[hwmon_temp_lcrit_alarm] = "temp%d_lcrit_alarm",
[hwmon_temp_min_alarm] = "temp%d_min_alarm",
[hwmon_temp_max_alarm] = "temp%d_max_alarm",
[hwmon_temp_crit_alarm] = "temp%d_crit_alarm",
[hwmon_temp_emergency_alarm] = "temp%d_emergency_alarm",
[hwmon_temp_fault] = "temp%d_fault",
[hwmon_temp_offset] = "temp%d_offset",
[hwmon_temp_label] = "temp%d_label",
[hwmon_temp_lowest] = "temp%d_lowest",
[hwmon_temp_highest] = "temp%d_highest",
[hwmon_temp_reset_history] = "temp%d_reset_history",
};
static const char * const hwmon_in_attr_templates[] = {
[hwmon_in_input] = "in%d_input",
[hwmon_in_min] = "in%d_min",
[hwmon_in_max] = "in%d_max",
[hwmon_in_lcrit] = "in%d_lcrit",
[hwmon_in_crit] = "in%d_crit",
[hwmon_in_average] = "in%d_average",
[hwmon_in_lowest] = "in%d_lowest",
[hwmon_in_highest] = "in%d_highest",
[hwmon_in_reset_history] = "in%d_reset_history",
[hwmon_in_label] = "in%d_label",
[hwmon_in_alarm] = "in%d_alarm",
[hwmon_in_min_alarm] = "in%d_min_alarm",
[hwmon_in_max_alarm] = "in%d_max_alarm",
[hwmon_in_lcrit_alarm] = "in%d_lcrit_alarm",
[hwmon_in_crit_alarm] = "in%d_crit_alarm",
};
static const char * const hwmon_curr_attr_templates[] = {
[hwmon_curr_input] = "curr%d_input",
[hwmon_curr_min] = "curr%d_min",
[hwmon_curr_max] = "curr%d_max",
[hwmon_curr_lcrit] = "curr%d_lcrit",
[hwmon_curr_crit] = "curr%d_crit",
[hwmon_curr_average] = "curr%d_average",
[hwmon_curr_lowest] = "curr%d_lowest",
[hwmon_curr_highest] = "curr%d_highest",
[hwmon_curr_reset_history] = "curr%d_reset_history",
[hwmon_curr_label] = "curr%d_label",
[hwmon_curr_alarm] = "curr%d_alarm",
[hwmon_curr_min_alarm] = "curr%d_min_alarm",
[hwmon_curr_max_alarm] = "curr%d_max_alarm",
[hwmon_curr_lcrit_alarm] = "curr%d_lcrit_alarm",
[hwmon_curr_crit_alarm] = "curr%d_crit_alarm",
};
static const char * const hwmon_power_attr_templates[] = {
[hwmon_power_average] = "power%d_average",
[hwmon_power_average_interval] = "power%d_average_interval",
[hwmon_power_average_interval_max] = "power%d_interval_max",
[hwmon_power_average_interval_min] = "power%d_interval_min",
[hwmon_power_average_highest] = "power%d_average_highest",
[hwmon_power_average_lowest] = "power%d_average_lowest",
[hwmon_power_average_max] = "power%d_average_max",
[hwmon_power_average_min] = "power%d_average_min",
[hwmon_power_input] = "power%d_input",
[hwmon_power_input_highest] = "power%d_input_highest",
[hwmon_power_input_lowest] = "power%d_input_lowest",
[hwmon_power_reset_history] = "power%d_reset_history",
[hwmon_power_accuracy] = "power%d_accuracy",
[hwmon_power_cap] = "power%d_cap",
[hwmon_power_cap_hyst] = "power%d_cap_hyst",
[hwmon_power_cap_max] = "power%d_cap_max",
[hwmon_power_cap_min] = "power%d_cap_min",
[hwmon_power_max] = "power%d_max",
[hwmon_power_crit] = "power%d_crit",
[hwmon_power_label] = "power%d_label",
[hwmon_power_alarm] = "power%d_alarm",
[hwmon_power_cap_alarm] = "power%d_cap_alarm",
[hwmon_power_max_alarm] = "power%d_max_alarm",
[hwmon_power_crit_alarm] = "power%d_crit_alarm",
};
static const char * const hwmon_energy_attr_templates[] = {
[hwmon_energy_input] = "energy%d_input",
[hwmon_energy_label] = "energy%d_label",
};
static const char * const hwmon_humidity_attr_templates[] = {
[hwmon_humidity_input] = "humidity%d_input",
[hwmon_humidity_label] = "humidity%d_label",
[hwmon_humidity_min] = "humidity%d_min",
[hwmon_humidity_min_hyst] = "humidity%d_min_hyst",
[hwmon_humidity_max] = "humidity%d_max",
[hwmon_humidity_max_hyst] = "humidity%d_max_hyst",
[hwmon_humidity_alarm] = "humidity%d_alarm",
[hwmon_humidity_fault] = "humidity%d_fault",
};
static const char * const hwmon_fan_attr_templates[] = {
[hwmon_fan_input] = "fan%d_input",
[hwmon_fan_label] = "fan%d_label",
[hwmon_fan_min] = "fan%d_min",
[hwmon_fan_max] = "fan%d_max",
[hwmon_fan_div] = "fan%d_div",
[hwmon_fan_pulses] = "fan%d_pulses",
[hwmon_fan_target] = "fan%d_target",
[hwmon_fan_alarm] = "fan%d_alarm",
[hwmon_fan_min_alarm] = "fan%d_min_alarm",
[hwmon_fan_max_alarm] = "fan%d_max_alarm",
[hwmon_fan_fault] = "fan%d_fault",
};
static const char * const hwmon_pwm_attr_templates[] = {
[hwmon_pwm_input] = "pwm%d",
[hwmon_pwm_enable] = "pwm%d_enable",
[hwmon_pwm_mode] = "pwm%d_mode",
[hwmon_pwm_freq] = "pwm%d_freq",
};
static const char * const *__templates[] = {
[hwmon_chip] = hwmon_chip_attr_templates,
[hwmon_temp] = hwmon_temp_attr_templates,
[hwmon_in] = hwmon_in_attr_templates,
[hwmon_curr] = hwmon_curr_attr_templates,
[hwmon_power] = hwmon_power_attr_templates,
[hwmon_energy] = hwmon_energy_attr_templates,
[hwmon_humidity] = hwmon_humidity_attr_templates,
[hwmon_fan] = hwmon_fan_attr_templates,
[hwmon_pwm] = hwmon_pwm_attr_templates,
};
static const int __templates_size[] = {
[hwmon_chip] = ARRAY_SIZE(hwmon_chip_attr_templates),
[hwmon_temp] = ARRAY_SIZE(hwmon_temp_attr_templates),
[hwmon_in] = ARRAY_SIZE(hwmon_in_attr_templates),
[hwmon_curr] = ARRAY_SIZE(hwmon_curr_attr_templates),
[hwmon_power] = ARRAY_SIZE(hwmon_power_attr_templates),
[hwmon_energy] = ARRAY_SIZE(hwmon_energy_attr_templates),
[hwmon_humidity] = ARRAY_SIZE(hwmon_humidity_attr_templates),
[hwmon_fan] = ARRAY_SIZE(hwmon_fan_attr_templates),
[hwmon_pwm] = ARRAY_SIZE(hwmon_pwm_attr_templates),
};
static int hwmon_num_channel_attrs(const struct hwmon_channel_info *info)
{
int i, n;
for (i = n = 0; info->config[i]; i++)
n += hweight32(info->config[i]);
return n;
}
static int hwmon_genattrs(struct device *dev,
const void *drvdata,
struct attribute **attrs,
const struct hwmon_ops *ops,
const struct hwmon_channel_info *info)
{
const char * const *templates;
int template_size;
int i, aindex = 0;
if (info->type >= ARRAY_SIZE(__templates))
return -EINVAL;
templates = __templates[info->type];
template_size = __templates_size[info->type];
for (i = 0; info->config[i]; i++) {
u32 attr_mask = info->config[i];
u32 attr;
while (attr_mask) {
struct attribute *a;
attr = __ffs(attr_mask);
attr_mask &= ~BIT(attr);
if (attr >= template_size)
return -EINVAL;
a = hwmon_genattr(dev, drvdata, info->type, attr, i,
templates[attr], ops);
if (IS_ERR(a)) {
if (PTR_ERR(a) != -ENOENT)
return PTR_ERR(a);
continue;
}
attrs[aindex++] = a;
}
}
return aindex;
}
static struct attribute **
__hwmon_create_attrs(struct device *dev, const void *drvdata,
const struct hwmon_chip_info *chip)
{
int ret, i, aindex = 0, nattrs = 0;
struct attribute **attrs;
for (i = 0; chip->info[i]; i++)
nattrs += hwmon_num_channel_attrs(chip->info[i]);
if (nattrs == 0)
return ERR_PTR(-EINVAL);
attrs = devm_kcalloc(dev, nattrs + 1, sizeof(*attrs), GFP_KERNEL);
if (!attrs)
return ERR_PTR(-ENOMEM);
for (i = 0; chip->info[i]; i++) {
ret = hwmon_genattrs(dev, drvdata, &attrs[aindex], chip->ops,
chip->info[i]);
if (ret < 0)
return ERR_PTR(ret);
aindex += ret;
}
return attrs;
}
static struct device *
__hwmon_device_register(struct device *dev, const char *name, void *drvdata,
const struct hwmon_chip_info *chip,
const struct attribute_group **groups)
{
struct hwmon_device *hwdev;
struct device *hdev;
int i, j, err, id;
/* Do not accept invalid characters in hwmon name attribute */
if (name && (!strlen(name) || strpbrk(name, "-* \t\n")))
return ERR_PTR(-EINVAL);
id = ida_simple_get(&hwmon_ida, 0, 0, GFP_KERNEL);
if (id < 0)
return ERR_PTR(id);
hwdev = kzalloc(sizeof(*hwdev), GFP_KERNEL);
if (hwdev == NULL) {
err = -ENOMEM;
goto ida_remove;
}
hdev = &hwdev->dev;
if (chip && chip->ops->is_visible) {
struct attribute **attrs;
int ngroups = 2;
if (groups)
for (i = 0; groups[i]; i++)
ngroups++;
hwdev->groups = devm_kcalloc(dev, ngroups, sizeof(*groups),
GFP_KERNEL);
if (!hwdev->groups)
return ERR_PTR(-ENOMEM);
attrs = __hwmon_create_attrs(dev, drvdata, chip);
if (IS_ERR(attrs)) {
err = PTR_ERR(attrs);
goto free_hwmon;
}
hwdev->group.attrs = attrs;
ngroups = 0;
hwdev->groups[ngroups++] = &hwdev->group;
if (groups) {
for (i = 0; groups[i]; i++)
hwdev->groups[ngroups++] = groups[i];
}
hdev->groups = hwdev->groups;
} else {
hdev->groups = groups;
}
hwdev->name = name;
hdev->class = &hwmon_class;
hdev->parent = dev;
hdev->of_node = dev ? dev->of_node : NULL;
hwdev->chip = chip;
dev_set_drvdata(hdev, drvdata);
dev_set_name(hdev, HWMON_ID_FORMAT, id);
err = device_register(hdev);
if (err)
goto free_hwmon;
if (chip && chip->ops->is_visible && chip->ops->read &&
chip->info[0]->type == hwmon_chip &&
(chip->info[0]->config[0] & HWMON_C_REGISTER_TZ)) {
const struct hwmon_channel_info **info = chip->info;
for (i = 1; info[i]; i++) {
if (info[i]->type != hwmon_temp)
continue;
for (j = 0; info[i]->config[j]; j++) {
if (!chip->ops->is_visible(drvdata, hwmon_temp,
hwmon_temp_input, j))
continue;
if (info[i]->config[j] & HWMON_T_INPUT)
hwmon_thermal_add_sensor(dev, hwdev, j);
}
}
}
return hdev;
free_hwmon:
kfree(hwdev);
ida_remove:
ida_simple_remove(&hwmon_ida, id);
return ERR_PTR(err);
}
/** /**
* hwmon_device_register_with_groups - register w/ hwmon * hwmon_device_register_with_groups - register w/ hwmon
* @dev: the parent device * @dev: the parent device
@ -97,44 +615,36 @@ hwmon_device_register_with_groups(struct device *dev, const char *name,
void *drvdata, void *drvdata,
const struct attribute_group **groups) const struct attribute_group **groups)
{ {
struct hwmon_device *hwdev; return __hwmon_device_register(dev, name, drvdata, NULL, groups);
int err, id;
/* Do not accept invalid characters in hwmon name attribute */
if (name && (!strlen(name) || strpbrk(name, "-* \t\n")))
return ERR_PTR(-EINVAL);
id = ida_simple_get(&hwmon_ida, 0, 0, GFP_KERNEL);
if (id < 0)
return ERR_PTR(id);
hwdev = kzalloc(sizeof(*hwdev), GFP_KERNEL);
if (hwdev == NULL) {
err = -ENOMEM;
goto ida_remove;
}
hwdev->name = name;
hwdev->dev.class = &hwmon_class;
hwdev->dev.parent = dev;
hwdev->dev.groups = groups;
hwdev->dev.of_node = dev ? dev->of_node : NULL;
dev_set_drvdata(&hwdev->dev, drvdata);
dev_set_name(&hwdev->dev, HWMON_ID_FORMAT, id);
err = device_register(&hwdev->dev);
if (err)
goto free;
return &hwdev->dev;
free:
kfree(hwdev);
ida_remove:
ida_simple_remove(&hwmon_ida, id);
return ERR_PTR(err);
} }
EXPORT_SYMBOL_GPL(hwmon_device_register_with_groups); EXPORT_SYMBOL_GPL(hwmon_device_register_with_groups);
/**
* hwmon_device_register_with_info - register w/ hwmon
* @dev: the parent device
* @name: hwmon name attribute
* @drvdata: driver data to attach to created device
* @info: Pointer to hwmon chip information
* @groups - pointer to list of driver specific attribute groups
*
* hwmon_device_unregister() must be called when the device is no
* longer needed.
*
* Returns the pointer to the new device.
*/
struct device *
hwmon_device_register_with_info(struct device *dev, const char *name,
void *drvdata,
const struct hwmon_chip_info *chip,
const struct attribute_group **groups)
{
if (chip && (!chip->ops || !chip->info))
return ERR_PTR(-EINVAL);
return __hwmon_device_register(dev, name, drvdata, chip, groups);
}
EXPORT_SYMBOL_GPL(hwmon_device_register_with_info);
/** /**
* hwmon_device_register - register w/ hwmon * hwmon_device_register - register w/ hwmon
* @dev: the device to register * @dev: the device to register
@ -213,6 +723,48 @@ error:
} }
EXPORT_SYMBOL_GPL(devm_hwmon_device_register_with_groups); EXPORT_SYMBOL_GPL(devm_hwmon_device_register_with_groups);
/**
* devm_hwmon_device_register_with_info - register w/ hwmon
* @dev: the parent device
* @name: hwmon name attribute
* @drvdata: driver data to attach to created device
* @info: Pointer to hwmon chip information
* @groups - pointer to list of driver specific attribute groups
*
* Returns the pointer to the new device. The new device is automatically
* unregistered with the parent device.
*/
struct device *
devm_hwmon_device_register_with_info(struct device *dev, const char *name,
void *drvdata,
const struct hwmon_chip_info *chip,
const struct attribute_group **groups)
{
struct device **ptr, *hwdev;
if (!dev)
return ERR_PTR(-EINVAL);
ptr = devres_alloc(devm_hwmon_release, sizeof(*ptr), GFP_KERNEL);
if (!ptr)
return ERR_PTR(-ENOMEM);
hwdev = hwmon_device_register_with_info(dev, name, drvdata, chip,
groups);
if (IS_ERR(hwdev))
goto error;
*ptr = hwdev;
devres_add(dev, ptr);
return hwdev;
error:
devres_free(ptr);
return hwdev;
}
EXPORT_SYMBOL_GPL(devm_hwmon_device_register_with_info);
static int devm_hwmon_match(struct device *dev, void *res, void *data) static int devm_hwmon_match(struct device *dev, void *res, void *data)
{ {
struct device **hwdev = res; struct device **hwdev = res;

View File

@ -143,13 +143,11 @@ static void __init make_sensor_label(struct device_node *np,
if (cpuid >= 0) if (cpuid >= 0)
/* /*
* The digital thermal sensors are associated * The digital thermal sensors are associated
* with a core. Let's print out the range of * with a core.
* cpu ids corresponding to the hardware
* threads of the core.
*/ */
n += snprintf(sdata->label + n, n += snprintf(sdata->label + n,
sizeof(sdata->label) - n, " %d-%d", sizeof(sdata->label) - n, " %d",
cpuid, cpuid + threads_per_core - 1); cpuid);
else else
n += snprintf(sdata->label + n, n += snprintf(sdata->label + n,
sizeof(sdata->label) - n, " phy%d", id); sizeof(sdata->label) - n, " phy%d", id);

View File

@ -73,8 +73,11 @@ static int iio_hwmon_probe(struct platform_device *pdev)
name = dev->of_node->name; name = dev->of_node->name;
channels = iio_channel_get_all(dev); 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); return PTR_ERR(channels);
}
st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL); st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
if (st == NULL) { if (st == NULL) {

View File

@ -2011,10 +2011,10 @@ static struct attribute *it87_attributes_in[] = {
&sensor_dev_attr_in7_beep.dev_attr.attr, /* 39 */ &sensor_dev_attr_in7_beep.dev_attr.attr, /* 39 */
&sensor_dev_attr_in8_input.dev_attr.attr, /* 40 */ &sensor_dev_attr_in8_input.dev_attr.attr, /* 40 */
&sensor_dev_attr_in9_input.dev_attr.attr, /* 41 */ &sensor_dev_attr_in9_input.dev_attr.attr,
&sensor_dev_attr_in10_input.dev_attr.attr, /* 41 */ &sensor_dev_attr_in10_input.dev_attr.attr,
&sensor_dev_attr_in11_input.dev_attr.attr, /* 41 */ &sensor_dev_attr_in11_input.dev_attr.attr,
&sensor_dev_attr_in12_input.dev_attr.attr, /* 41 */ &sensor_dev_attr_in12_input.dev_attr.attr,
NULL NULL
}; };

View File

@ -28,7 +28,6 @@
#include <linux/jiffies.h> #include <linux/jiffies.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/hwmon.h> #include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/of.h> #include <linux/of.h>
@ -254,170 +253,148 @@ abort:
return ret; return ret;
} }
/* sysfs functions */ static int jc42_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
static ssize_t show_temp(struct device *dev, struct device_attribute *devattr,
char *buf)
{ {
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct jc42_data *data = jc42_update_device(dev);
if (IS_ERR(data))
return PTR_ERR(data);
return sprintf(buf, "%d\n",
jc42_temp_from_reg(data->temp[attr->index]));
}
static ssize_t show_temp_hyst(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct jc42_data *data = jc42_update_device(dev); struct jc42_data *data = jc42_update_device(dev);
int temp, hyst; int temp, hyst;
if (IS_ERR(data)) if (IS_ERR(data))
return PTR_ERR(data); return PTR_ERR(data);
temp = jc42_temp_from_reg(data->temp[attr->index]); switch (attr) {
hyst = jc42_hysteresis[(data->config & JC42_CFG_HYST_MASK) case hwmon_temp_input:
>> JC42_CFG_HYST_SHIFT]; *val = jc42_temp_from_reg(data->temp[t_input]);
return sprintf(buf, "%d\n", temp - hyst); return 0;
case hwmon_temp_min:
*val = jc42_temp_from_reg(data->temp[t_min]);
return 0;
case hwmon_temp_max:
*val = jc42_temp_from_reg(data->temp[t_max]);
return 0;
case hwmon_temp_crit:
*val = jc42_temp_from_reg(data->temp[t_crit]);
return 0;
case hwmon_temp_max_hyst:
temp = jc42_temp_from_reg(data->temp[t_max]);
hyst = jc42_hysteresis[(data->config & JC42_CFG_HYST_MASK)
>> JC42_CFG_HYST_SHIFT];
*val = temp - hyst;
return 0;
case hwmon_temp_crit_hyst:
temp = jc42_temp_from_reg(data->temp[t_crit]);
hyst = jc42_hysteresis[(data->config & JC42_CFG_HYST_MASK)
>> JC42_CFG_HYST_SHIFT];
*val = temp - hyst;
return 0;
case hwmon_temp_min_alarm:
*val = (data->temp[t_input] >> JC42_ALARM_MIN_BIT) & 1;
return 0;
case hwmon_temp_max_alarm:
*val = (data->temp[t_input] >> JC42_ALARM_MAX_BIT) & 1;
return 0;
case hwmon_temp_crit_alarm:
*val = (data->temp[t_input] >> JC42_ALARM_CRIT_BIT) & 1;
return 0;
default:
return -EOPNOTSUPP;
}
} }
static ssize_t set_temp(struct device *dev, struct device_attribute *devattr, static int jc42_write(struct device *dev, enum hwmon_sensor_types type,
const char *buf, size_t count) u32 attr, int channel, long val)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct jc42_data *data = dev_get_drvdata(dev);
int err, ret = count;
int nr = attr->index;
long val;
if (kstrtol(buf, 10, &val) < 0)
return -EINVAL;
mutex_lock(&data->update_lock);
data->temp[nr] = jc42_temp_to_reg(val, data->extended);
err = i2c_smbus_write_word_swapped(data->client, temp_regs[nr],
data->temp[nr]);
if (err < 0)
ret = err;
mutex_unlock(&data->update_lock);
return ret;
}
/*
* JC42.4 compliant chips only support four hysteresis values.
* Pick best choice and go from there.
*/
static ssize_t set_temp_crit_hyst(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{ {
struct jc42_data *data = dev_get_drvdata(dev); struct jc42_data *data = dev_get_drvdata(dev);
long val; struct i2c_client *client = data->client;
int diff, hyst; int diff, hyst;
int err; int ret;
int ret = count;
if (kstrtol(buf, 10, &val) < 0) mutex_lock(&data->update_lock);
return -EINVAL;
val = clamp_val(val, (data->extended ? JC42_TEMP_MIN_EXTENDED : switch (attr) {
JC42_TEMP_MIN) - 6000, JC42_TEMP_MAX); case hwmon_temp_min:
diff = jc42_temp_from_reg(data->temp[t_crit]) - val; data->temp[t_min] = jc42_temp_to_reg(val, data->extended);
ret = i2c_smbus_write_word_swapped(client, temp_regs[t_min],
hyst = 0; data->temp[t_min]);
if (diff > 0) { break;
if (diff < 2250) case hwmon_temp_max:
hyst = 1; /* 1.5 degrees C */ data->temp[t_max] = jc42_temp_to_reg(val, data->extended);
else if (diff < 4500) ret = i2c_smbus_write_word_swapped(client, temp_regs[t_max],
hyst = 2; /* 3.0 degrees C */ data->temp[t_max]);
else break;
hyst = 3; /* 6.0 degrees C */ case hwmon_temp_crit:
data->temp[t_crit] = jc42_temp_to_reg(val, data->extended);
ret = i2c_smbus_write_word_swapped(client, temp_regs[t_crit],
data->temp[t_crit]);
break;
case hwmon_temp_crit_hyst:
/*
* JC42.4 compliant chips only support four hysteresis values.
* Pick best choice and go from there.
*/
val = clamp_val(val, (data->extended ? JC42_TEMP_MIN_EXTENDED
: JC42_TEMP_MIN) - 6000,
JC42_TEMP_MAX);
diff = jc42_temp_from_reg(data->temp[t_crit]) - val;
hyst = 0;
if (diff > 0) {
if (diff < 2250)
hyst = 1; /* 1.5 degrees C */
else if (diff < 4500)
hyst = 2; /* 3.0 degrees C */
else
hyst = 3; /* 6.0 degrees C */
}
data->config = (data->config & ~JC42_CFG_HYST_MASK) |
(hyst << JC42_CFG_HYST_SHIFT);
ret = i2c_smbus_write_word_swapped(data->client,
JC42_REG_CONFIG,
data->config);
break;
default:
ret = -EOPNOTSUPP;
break;
} }
mutex_lock(&data->update_lock);
data->config = (data->config & ~JC42_CFG_HYST_MASK)
| (hyst << JC42_CFG_HYST_SHIFT);
err = i2c_smbus_write_word_swapped(data->client, JC42_REG_CONFIG,
data->config);
if (err < 0)
ret = err;
mutex_unlock(&data->update_lock); mutex_unlock(&data->update_lock);
return ret; return ret;
} }
static ssize_t show_alarm(struct device *dev, static umode_t jc42_is_visible(const void *_data, enum hwmon_sensor_types type,
struct device_attribute *attr, char *buf) u32 attr, int channel)
{ {
u16 bit = to_sensor_dev_attr(attr)->index; const struct jc42_data *data = _data;
struct jc42_data *data = jc42_update_device(dev);
u16 val;
if (IS_ERR(data))
return PTR_ERR(data);
val = data->temp[t_input];
if (bit != JC42_ALARM_CRIT_BIT && (data->config & JC42_CFG_CRIT_ONLY))
val = 0;
return sprintf(buf, "%u\n", (val >> bit) & 1);
}
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, t_input);
static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, show_temp, set_temp, t_crit);
static SENSOR_DEVICE_ATTR(temp1_min, S_IRUGO, show_temp, set_temp, t_min);
static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, show_temp, set_temp, t_max);
static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO, show_temp_hyst,
set_temp_crit_hyst, t_crit);
static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IRUGO, show_temp_hyst, NULL, t_max);
static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL,
JC42_ALARM_CRIT_BIT);
static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_alarm, NULL,
JC42_ALARM_MIN_BIT);
static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL,
JC42_ALARM_MAX_BIT);
static struct attribute *jc42_attributes[] = {
&sensor_dev_attr_temp1_input.dev_attr.attr,
&sensor_dev_attr_temp1_crit.dev_attr.attr,
&sensor_dev_attr_temp1_min.dev_attr.attr,
&sensor_dev_attr_temp1_max.dev_attr.attr,
&sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
&sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
&sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
&sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
NULL
};
static umode_t jc42_attribute_mode(struct kobject *kobj,
struct attribute *attr, int index)
{
struct device *dev = container_of(kobj, struct device, kobj);
struct jc42_data *data = dev_get_drvdata(dev);
unsigned int config = data->config; unsigned int config = data->config;
bool readonly; umode_t mode = S_IRUGO;
if (attr == &sensor_dev_attr_temp1_crit.dev_attr.attr) switch (attr) {
readonly = config & JC42_CFG_TCRIT_LOCK; case hwmon_temp_min:
else if (attr == &sensor_dev_attr_temp1_min.dev_attr.attr || case hwmon_temp_max:
attr == &sensor_dev_attr_temp1_max.dev_attr.attr) if (!(config & JC42_CFG_EVENT_LOCK))
readonly = config & JC42_CFG_EVENT_LOCK; mode |= S_IWUSR;
else if (attr == &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr) break;
readonly = config & (JC42_CFG_EVENT_LOCK | JC42_CFG_TCRIT_LOCK); case hwmon_temp_crit:
else if (!(config & JC42_CFG_TCRIT_LOCK))
readonly = true; mode |= S_IWUSR;
break;
return S_IRUGO | (readonly ? 0 : S_IWUSR); case hwmon_temp_crit_hyst:
if (!(config & (JC42_CFG_EVENT_LOCK | JC42_CFG_TCRIT_LOCK)))
mode |= S_IWUSR;
break;
case hwmon_temp_input:
case hwmon_temp_max_hyst:
case hwmon_temp_min_alarm:
case hwmon_temp_max_alarm:
case hwmon_temp_crit_alarm:
break;
default:
mode = 0;
break;
}
return mode;
} }
static const struct attribute_group jc42_group = {
.attrs = jc42_attributes,
.is_visible = jc42_attribute_mode,
};
__ATTRIBUTE_GROUPS(jc42);
/* Return 0 if detection is successful, -ENODEV otherwise */ /* Return 0 if detection is successful, -ENODEV otherwise */
static int jc42_detect(struct i2c_client *client, struct i2c_board_info *info) static int jc42_detect(struct i2c_client *client, struct i2c_board_info *info)
{ {
@ -450,6 +427,34 @@ static int jc42_detect(struct i2c_client *client, struct i2c_board_info *info)
return -ENODEV; return -ENODEV;
} }
static const u32 jc42_temp_config[] = {
HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_CRIT |
HWMON_T_MAX_HYST | HWMON_T_CRIT_HYST |
HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM,
0
};
static const struct hwmon_channel_info jc42_temp = {
.type = hwmon_temp,
.config = jc42_temp_config,
};
static const struct hwmon_channel_info *jc42_info[] = {
&jc42_temp,
NULL
};
static const struct hwmon_ops jc42_hwmon_ops = {
.is_visible = jc42_is_visible,
.read = jc42_read,
.write = jc42_write,
};
static const struct hwmon_chip_info jc42_chip_info = {
.ops = &jc42_hwmon_ops,
.info = jc42_info,
};
static int jc42_probe(struct i2c_client *client, const struct i2c_device_id *id) static int jc42_probe(struct i2c_client *client, const struct i2c_device_id *id)
{ {
struct device *dev = &client->dev; struct device *dev = &client->dev;
@ -482,9 +487,9 @@ static int jc42_probe(struct i2c_client *client, const struct i2c_device_id *id)
} }
data->config = config; data->config = config;
hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
data, data, &jc42_chip_info,
jc42_groups); NULL);
return PTR_ERR_OR_ZERO(hwmon_dev); return PTR_ERR_OR_ZERO(hwmon_dev);
} }

View File

@ -28,7 +28,6 @@
#include <linux/err.h> #include <linux/err.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/thermal.h>
#include "lm75.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); return ((temp >> (16 - resolution)) * 1000) >> (resolution - 8);
} }
/* sysfs attributes for hwmon */ static int lm75_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
static int lm75_read_temp(void *dev, int *temp)
{ {
struct lm75_data *data = dev_get_drvdata(dev); struct lm75_data *data = dev_get_drvdata(dev);
unsigned int _temp; unsigned int regval;
int err; int err, reg;
err = regmap_read(data->regmap, LM75_REG_TEMP, &_temp); switch (type) {
if (err < 0) case hwmon_chip:
return err; switch (attr) {
case hwmon_chip_update_interval:
*temp = lm75_reg_to_mc(_temp, data->resolution); *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; return 0;
} }
static ssize_t show_temp(struct device *dev, struct device_attribute *da, static int lm75_write(struct device *dev, enum hwmon_sensor_types type,
char *buf) u32 attr, int channel, long temp)
{ {
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
struct lm75_data *data = dev_get_drvdata(dev); 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; u8 resolution;
int reg;
error = kstrtol(buf, 10, &temp); if (type != hwmon_temp)
if (error) return -EINVAL;
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 * Resolution of limit registers is assumed to be the same as the
* temperature input register resolution unless given explicitly. * temperature input register resolution unless given explicitly.
*/ */
if (attr->index && data->resolution_limits) if (data->resolution_limits)
resolution = data->resolution_limits; resolution = data->resolution_limits;
else else
resolution = data->resolution; resolution = data->resolution;
@ -145,46 +163,78 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *da,
temp = clamp_val(temp, LM75_TEMP_MIN, LM75_TEMP_MAX); temp = clamp_val(temp, LM75_TEMP_MIN, LM75_TEMP_MAX);
temp = DIV_ROUND_CLOSEST(temp << (resolution - 8), temp = DIV_ROUND_CLOSEST(temp << (resolution - 8),
1000) << (16 - resolution); 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, static umode_t lm75_is_visible(const void *data, enum hwmon_sensor_types type,
struct device_attribute *da, char *buf) u32 attr, int channel)
{ {
struct lm75_data *data = dev_get_drvdata(dev); switch (type) {
case hwmon_chip:
return sprintf(buf, "%u\n", data->sample_time); 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,
NULL
};
ATTRIBUTE_GROUPS(lm75);
static const struct thermal_zone_of_device_ops lm75_of_thermal_ops = {
.get_temp = lm75_read_temp,
};
/*-----------------------------------------------------------------------*/ /*-----------------------------------------------------------------------*/
/* device probe and removal */ /* device probe and removal */
/* chip configuration */
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,
};
static bool lm75_is_writeable_reg(struct device *dev, unsigned int reg) static bool lm75_is_writeable_reg(struct device *dev, unsigned int reg)
{ {
return reg != LM75_REG_TEMP; return reg != LM75_REG_TEMP;
@ -337,15 +387,12 @@ lm75_probe(struct i2c_client *client, const struct i2c_device_id *id)
dev_dbg(dev, "Config %02x\n", new); dev_dbg(dev, "Config %02x\n", new);
hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
data, lm75_groups); data, &lm75_chip_info,
NULL);
if (IS_ERR(hwmon_dev)) if (IS_ERR(hwmon_dev))
return PTR_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); dev_info(dev, "%s: sensor '%s'\n", dev_name(hwmon_dev), client->name);
return 0; return 0;

File diff suppressed because it is too large Load Diff

View File

@ -15,22 +15,17 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/ */
#include <linux/module.h> #include <linux/bitops.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/jiffies.h>
#include <linux/hwmon.h>
#include <linux/module.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/sysfs.h> #include <linux/slab.h>
#define DEVNAME "lm95241" #define DEVNAME "lm95241"
@ -54,26 +49,25 @@ static const unsigned short normal_i2c[] = {
#define LM95241_REG_RW_REMOTE_MODEL 0x30 #define LM95241_REG_RW_REMOTE_MODEL 0x30
/* LM95241 specific bitfields */ /* LM95241 specific bitfields */
#define CFG_STOP 0x40 #define CFG_STOP BIT(6)
#define CFG_CR0076 0x00 #define CFG_CR0076 0x00
#define CFG_CR0182 0x10 #define CFG_CR0182 BIT(4)
#define CFG_CR1000 0x20 #define CFG_CR1000 BIT(5)
#define CFG_CR2700 0x30 #define CFG_CR2700 (BIT(4) | BIT(5))
#define R1MS_SHIFT 0 #define CFG_CRMASK (BIT(4) | BIT(5))
#define R2MS_SHIFT 2 #define R1MS_MASK BIT(0)
#define R1MS_MASK (0x01 << (R1MS_SHIFT)) #define R2MS_MASK BIT(2)
#define R2MS_MASK (0x01 << (R2MS_SHIFT)) #define R1DF_MASK BIT(1)
#define R1DF_SHIFT 1 #define R2DF_MASK BIT(2)
#define R2DF_SHIFT 2 #define R1FE_MASK BIT(0)
#define R1DF_MASK (0x01 << (R1DF_SHIFT)) #define R2FE_MASK BIT(2)
#define R2DF_MASK (0x01 << (R2DF_SHIFT)) #define R1DM BIT(0)
#define R1FE_MASK 0x01 #define R2DM BIT(1)
#define R2FE_MASK 0x05 #define TT1_SHIFT 0
#define TT1_SHIFT 0 #define TT2_SHIFT 4
#define TT2_SHIFT 4 #define TT_OFF 0
#define TT_OFF 0 #define TT_ON 1
#define TT_ON 1 #define TT_MASK 7
#define TT_MASK 7
#define NATSEMI_MAN_ID 0x01 #define NATSEMI_MAN_ID 0x01
#define LM95231_CHIP_ID 0xA1 #define LM95231_CHIP_ID 0xA1
#define LM95241_CHIP_ID 0xA4 #define LM95241_CHIP_ID 0xA4
@ -91,11 +85,12 @@ static const u8 lm95241_reg_address[] = {
struct lm95241_data { struct lm95241_data {
struct i2c_client *client; struct i2c_client *client;
struct mutex update_lock; struct mutex update_lock;
unsigned long last_updated, interval; /* in jiffies */ unsigned long last_updated; /* in jiffies */
unsigned long interval; /* in milli-seconds */
char valid; /* zero until following fields are valid */ char valid; /* zero until following fields are valid */
/* registers values */ /* registers values */
u8 temp[ARRAY_SIZE(lm95241_reg_address)]; u8 temp[ARRAY_SIZE(lm95241_reg_address)];
u8 config, model, trutherm; u8 status, config, model, trutherm;
}; };
/* Conversions */ /* Conversions */
@ -118,7 +113,8 @@ static struct lm95241_data *lm95241_update_device(struct device *dev)
mutex_lock(&data->update_lock); mutex_lock(&data->update_lock);
if (time_after(jiffies, data->last_updated + data->interval) || if (time_after(jiffies, data->last_updated
+ msecs_to_jiffies(data->interval)) ||
!data->valid) { !data->valid) {
int i; int i;
@ -127,6 +123,9 @@ static struct lm95241_data *lm95241_update_device(struct device *dev)
data->temp[i] data->temp[i]
= i2c_smbus_read_byte_data(client, = i2c_smbus_read_byte_data(client,
lm95241_reg_address[i]); lm95241_reg_address[i]);
data->status = i2c_smbus_read_byte_data(client,
LM95241_REG_R_STATUS);
data->last_updated = jiffies; data->last_updated = jiffies;
data->valid = 1; data->valid = 1;
} }
@ -136,197 +135,241 @@ static struct lm95241_data *lm95241_update_device(struct device *dev)
return data; return data;
} }
/* Sysfs stuff */ static int lm95241_read_chip(struct device *dev, u32 attr, int channel,
static ssize_t show_input(struct device *dev, struct device_attribute *attr, long *val)
char *buf)
{
struct lm95241_data *data = lm95241_update_device(dev);
int index = to_sensor_dev_attr(attr)->index;
return snprintf(buf, PAGE_SIZE - 1, "%d\n",
index == 0 || (data->config & (1 << (index / 2))) ?
temp_from_reg_signed(data->temp[index], data->temp[index + 1]) :
temp_from_reg_unsigned(data->temp[index],
data->temp[index + 1]));
}
static ssize_t show_type(struct device *dev, struct device_attribute *attr,
char *buf)
{ {
struct lm95241_data *data = dev_get_drvdata(dev); struct lm95241_data *data = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE - 1, switch (attr) {
data->model & to_sensor_dev_attr(attr)->index ? "1\n" : "2\n"); case hwmon_chip_update_interval:
*val = data->interval;
return 0;
default:
return -EOPNOTSUPP;
}
} }
static ssize_t set_type(struct device *dev, struct device_attribute *attr, static int lm95241_read_temp(struct device *dev, u32 attr, int channel,
const char *buf, size_t count) long *val)
{
struct lm95241_data *data = lm95241_update_device(dev);
switch (attr) {
case hwmon_temp_input:
if (!channel || (data->config & BIT(channel - 1)))
*val = temp_from_reg_signed(data->temp[channel * 2],
data->temp[channel * 2 + 1]);
else
*val = temp_from_reg_unsigned(data->temp[channel * 2],
data->temp[channel * 2 + 1]);
return 0;
case hwmon_temp_min:
if (channel == 1)
*val = (data->config & R1DF_MASK) ? -128000 : 0;
else
*val = (data->config & R2DF_MASK) ? -128000 : 0;
return 0;
case hwmon_temp_max:
if (channel == 1)
*val = (data->config & R1DF_MASK) ? 127875 : 255875;
else
*val = (data->config & R2DF_MASK) ? 127875 : 255875;
return 0;
case hwmon_temp_type:
if (channel == 1)
*val = (data->model & R1MS_MASK) ? 1 : 2;
else
*val = (data->model & R2MS_MASK) ? 1 : 2;
return 0;
case hwmon_temp_fault:
if (channel == 1)
*val = !!(data->status & R1DM);
else
*val = !!(data->status & R2DM);
return 0;
default:
return -EOPNOTSUPP;
}
}
static int lm95241_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
switch (type) {
case hwmon_chip:
return lm95241_read_chip(dev, attr, channel, val);
case hwmon_temp:
return lm95241_read_temp(dev, attr, channel, val);
default:
return -EOPNOTSUPP;
}
}
static int lm95241_write_chip(struct device *dev, u32 attr, int channel,
long val)
{
struct lm95241_data *data = dev_get_drvdata(dev);
int convrate;
u8 config;
int ret;
mutex_lock(&data->update_lock);
switch (attr) {
case hwmon_chip_update_interval:
config = data->config & ~CFG_CRMASK;
if (val < 130) {
convrate = 76;
config |= CFG_CR0076;
} else if (val < 590) {
convrate = 182;
config |= CFG_CR0182;
} else if (val < 1850) {
convrate = 1000;
config |= CFG_CR1000;
} else {
convrate = 2700;
config |= CFG_CR2700;
}
data->interval = convrate;
data->config = config;
ret = i2c_smbus_write_byte_data(data->client,
LM95241_REG_RW_CONFIG, config);
break;
default:
ret = -EOPNOTSUPP;
break;
}
mutex_unlock(&data->update_lock);
return ret;
}
static int lm95241_write_temp(struct device *dev, u32 attr, int channel,
long val)
{ {
struct lm95241_data *data = dev_get_drvdata(dev); struct lm95241_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client; struct i2c_client *client = data->client;
unsigned long val; int ret;
int shift;
u8 mask = to_sensor_dev_attr(attr)->index;
if (kstrtoul(buf, 10, &val) < 0)
return -EINVAL;
if (val != 1 && val != 2)
return -EINVAL;
shift = mask == R1MS_MASK ? TT1_SHIFT : TT2_SHIFT;
mutex_lock(&data->update_lock); mutex_lock(&data->update_lock);
data->trutherm &= ~(TT_MASK << shift); switch (attr) {
if (val == 1) { case hwmon_temp_min:
data->model |= mask; if (channel == 1) {
data->trutherm |= (TT_ON << shift); if (val < 0)
} else { data->config |= R1DF_MASK;
data->model &= ~mask; else
data->trutherm |= (TT_OFF << shift); data->config &= ~R1DF_MASK;
} else {
if (val < 0)
data->config |= R2DF_MASK;
else
data->config &= ~R2DF_MASK;
}
data->valid = 0;
ret = i2c_smbus_write_byte_data(client, LM95241_REG_RW_CONFIG,
data->config);
break;
case hwmon_temp_max:
if (channel == 1) {
if (val <= 127875)
data->config |= R1DF_MASK;
else
data->config &= ~R1DF_MASK;
} else {
if (val <= 127875)
data->config |= R2DF_MASK;
else
data->config &= ~R2DF_MASK;
}
data->valid = 0;
ret = i2c_smbus_write_byte_data(client, LM95241_REG_RW_CONFIG,
data->config);
break;
case hwmon_temp_type:
if (val != 1 && val != 2) {
ret = -EINVAL;
break;
}
if (channel == 1) {
data->trutherm &= ~(TT_MASK << TT1_SHIFT);
if (val == 1) {
data->model |= R1MS_MASK;
data->trutherm |= (TT_ON << TT1_SHIFT);
} else {
data->model &= ~R1MS_MASK;
data->trutherm |= (TT_OFF << TT1_SHIFT);
}
} else {
data->trutherm &= ~(TT_MASK << TT2_SHIFT);
if (val == 1) {
data->model |= R2MS_MASK;
data->trutherm |= (TT_ON << TT2_SHIFT);
} else {
data->model &= ~R2MS_MASK;
data->trutherm |= (TT_OFF << TT2_SHIFT);
}
}
ret = i2c_smbus_write_byte_data(client,
LM95241_REG_RW_REMOTE_MODEL,
data->model);
if (ret < 0)
break;
ret = i2c_smbus_write_byte_data(client, LM95241_REG_RW_TRUTHERM,
data->trutherm);
break;
default:
ret = -EOPNOTSUPP;
break;
} }
data->valid = 0;
i2c_smbus_write_byte_data(client, LM95241_REG_RW_REMOTE_MODEL,
data->model);
i2c_smbus_write_byte_data(client, LM95241_REG_RW_TRUTHERM,
data->trutherm);
mutex_unlock(&data->update_lock); mutex_unlock(&data->update_lock);
return count; return ret;
} }
static ssize_t show_min(struct device *dev, struct device_attribute *attr, static int lm95241_write(struct device *dev, enum hwmon_sensor_types type,
char *buf) u32 attr, int channel, long val)
{ {
struct lm95241_data *data = dev_get_drvdata(dev); switch (type) {
case hwmon_chip:
return snprintf(buf, PAGE_SIZE - 1, return lm95241_write_chip(dev, attr, channel, val);
data->config & to_sensor_dev_attr(attr)->index ? case hwmon_temp:
"-127000\n" : "0\n"); return lm95241_write_temp(dev, attr, channel, val);
default:
return -EOPNOTSUPP;
}
} }
static ssize_t set_min(struct device *dev, struct device_attribute *attr, static umode_t lm95241_is_visible(const void *data,
const char *buf, size_t count) enum hwmon_sensor_types type,
u32 attr, int channel)
{ {
struct lm95241_data *data = dev_get_drvdata(dev); switch (type) {
long val; case hwmon_chip:
switch (attr) {
if (kstrtol(buf, 10, &val) < 0) case hwmon_chip_update_interval:
return -EINVAL; return S_IRUGO | S_IWUSR;
if (val < -128000) }
return -EINVAL; break;
case hwmon_temp:
mutex_lock(&data->update_lock); switch (attr) {
case hwmon_temp_input:
if (val < 0) return S_IRUGO;
data->config |= to_sensor_dev_attr(attr)->index; case hwmon_temp_fault:
else return S_IRUGO;
data->config &= ~to_sensor_dev_attr(attr)->index; case hwmon_temp_min:
data->valid = 0; case hwmon_temp_max:
case hwmon_temp_type:
i2c_smbus_write_byte_data(data->client, LM95241_REG_RW_CONFIG, return S_IRUGO | S_IWUSR;
data->config); }
break;
mutex_unlock(&data->update_lock); default:
break;
return count; }
return 0;
} }
static ssize_t show_max(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct lm95241_data *data = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE - 1,
data->config & to_sensor_dev_attr(attr)->index ?
"127000\n" : "255000\n");
}
static ssize_t set_max(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct lm95241_data *data = dev_get_drvdata(dev);
long val;
if (kstrtol(buf, 10, &val) < 0)
return -EINVAL;
if (val >= 256000)
return -EINVAL;
mutex_lock(&data->update_lock);
if (val <= 127000)
data->config |= to_sensor_dev_attr(attr)->index;
else
data->config &= ~to_sensor_dev_attr(attr)->index;
data->valid = 0;
i2c_smbus_write_byte_data(data->client, LM95241_REG_RW_CONFIG,
data->config);
mutex_unlock(&data->update_lock);
return count;
}
static ssize_t show_interval(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct lm95241_data *data = lm95241_update_device(dev);
return snprintf(buf, PAGE_SIZE - 1, "%lu\n", 1000 * data->interval
/ HZ);
}
static ssize_t set_interval(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct lm95241_data *data = dev_get_drvdata(dev);
unsigned long val;
if (kstrtoul(buf, 10, &val) < 0)
return -EINVAL;
data->interval = val * HZ / 1000;
return count;
}
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_input, NULL, 0);
static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_input, NULL, 2);
static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_input, NULL, 4);
static SENSOR_DEVICE_ATTR(temp2_type, S_IWUSR | S_IRUGO, show_type, set_type,
R1MS_MASK);
static SENSOR_DEVICE_ATTR(temp3_type, S_IWUSR | S_IRUGO, show_type, set_type,
R2MS_MASK);
static SENSOR_DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_min, set_min,
R1DF_MASK);
static SENSOR_DEVICE_ATTR(temp3_min, S_IWUSR | S_IRUGO, show_min, set_min,
R2DF_MASK);
static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_max, set_max,
R1DF_MASK);
static SENSOR_DEVICE_ATTR(temp3_max, S_IWUSR | S_IRUGO, show_max, set_max,
R2DF_MASK);
static DEVICE_ATTR(update_interval, S_IWUSR | S_IRUGO, show_interval,
set_interval);
static struct attribute *lm95241_attrs[] = {
&sensor_dev_attr_temp1_input.dev_attr.attr,
&sensor_dev_attr_temp2_input.dev_attr.attr,
&sensor_dev_attr_temp3_input.dev_attr.attr,
&sensor_dev_attr_temp2_type.dev_attr.attr,
&sensor_dev_attr_temp3_type.dev_attr.attr,
&sensor_dev_attr_temp2_min.dev_attr.attr,
&sensor_dev_attr_temp3_min.dev_attr.attr,
&sensor_dev_attr_temp2_max.dev_attr.attr,
&sensor_dev_attr_temp3_max.dev_attr.attr,
&dev_attr_update_interval.attr,
NULL
};
ATTRIBUTE_GROUPS(lm95241);
/* Return 0 if detection is successful, -ENODEV otherwise */ /* Return 0 if detection is successful, -ENODEV otherwise */
static int lm95241_detect(struct i2c_client *new_client, static int lm95241_detect(struct i2c_client *new_client,
struct i2c_board_info *info) struct i2c_board_info *info)
@ -362,8 +405,8 @@ static int lm95241_detect(struct i2c_client *new_client,
static void lm95241_init_client(struct i2c_client *client, static void lm95241_init_client(struct i2c_client *client,
struct lm95241_data *data) struct lm95241_data *data)
{ {
data->interval = HZ; /* 1 sec default */ data->interval = 1000;
data->config = CFG_CR0076; data->config = CFG_CR1000;
data->trutherm = (TT_OFF << TT1_SHIFT) | (TT_OFF << TT2_SHIFT); data->trutherm = (TT_OFF << TT1_SHIFT) | (TT_OFF << TT2_SHIFT);
i2c_smbus_write_byte_data(client, LM95241_REG_RW_CONFIG, data->config); i2c_smbus_write_byte_data(client, LM95241_REG_RW_CONFIG, data->config);
@ -375,6 +418,47 @@ static void lm95241_init_client(struct i2c_client *client,
data->model); data->model);
} }
static const u32 lm95241_chip_config[] = {
HWMON_C_UPDATE_INTERVAL,
0
};
static const struct hwmon_channel_info lm95241_chip = {
.type = hwmon_chip,
.config = lm95241_chip_config,
};
static const u32 lm95241_temp_config[] = {
HWMON_T_INPUT,
HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN | HWMON_T_TYPE |
HWMON_T_FAULT,
HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN | HWMON_T_TYPE |
HWMON_T_FAULT,
0
};
static const struct hwmon_channel_info lm95241_temp = {
.type = hwmon_temp,
.config = lm95241_temp_config,
};
static const struct hwmon_channel_info *lm95241_info[] = {
&lm95241_chip,
&lm95241_temp,
NULL
};
static const struct hwmon_ops lm95241_hwmon_ops = {
.is_visible = lm95241_is_visible,
.read = lm95241_read,
.write = lm95241_write,
};
static const struct hwmon_chip_info lm95241_chip_info = {
.ops = &lm95241_hwmon_ops,
.info = lm95241_info,
};
static int lm95241_probe(struct i2c_client *client, static int lm95241_probe(struct i2c_client *client,
const struct i2c_device_id *id) const struct i2c_device_id *id)
{ {
@ -392,9 +476,10 @@ static int lm95241_probe(struct i2c_client *client,
/* Initialize the LM95241 chip */ /* Initialize the LM95241 chip */
lm95241_init_client(client, data); lm95241_init_client(client, data);
hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
data, data,
lm95241_groups); &lm95241_chip_info,
NULL);
return PTR_ERR_OR_ZERO(hwmon_dev); return PTR_ERR_OR_ZERO(hwmon_dev);
} }
@ -420,5 +505,5 @@ static struct i2c_driver lm95241_driver = {
module_i2c_driver(lm95241_driver); module_i2c_driver(lm95241_driver);
MODULE_AUTHOR("Davide Rizzo <elpa.rizzo@gmail.com>"); MODULE_AUTHOR("Davide Rizzo <elpa.rizzo@gmail.com>");
MODULE_DESCRIPTION("LM95241 sensor driver"); MODULE_DESCRIPTION("LM95231/LM95241 sensor driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");

View File

@ -15,22 +15,16 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/ */
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/init.h>
#include <linux/hwmon.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/sysfs.h> #include <linux/regmap.h>
#include <linux/slab.h>
static const unsigned short normal_i2c[] = { static const unsigned short normal_i2c[] = {
0x18, 0x19, 0x29, 0x4c, 0x4d, I2C_CLIENT_END }; 0x18, 0x19, 0x29, 0x4c, 0x4d, I2C_CLIENT_END };
@ -89,6 +83,7 @@ static const unsigned short normal_i2c[] = {
#define RATE_CR1000 0x02 #define RATE_CR1000 0x02
#define RATE_CR2500 0x03 #define RATE_CR2500 0x03
#define STATUS1_ROS 0x10
#define STATUS1_DIODE_FAULT 0x04 #define STATUS1_DIODE_FAULT 0x04
#define STATUS1_RTCRIT 0x02 #define STATUS1_RTCRIT 0x02
#define STATUS1_LOC 0x01 #define STATUS1_LOC 0x01
@ -112,14 +107,9 @@ static const u8 lm95245_reg_address[] = {
/* Client data (each client gets its own) */ /* Client data (each client gets its own) */
struct lm95245_data { struct lm95245_data {
struct i2c_client *client; struct regmap *regmap;
struct mutex update_lock; struct mutex update_lock;
unsigned long last_updated; /* in jiffies */ int interval; /* in msecs */
unsigned long interval; /* in msecs */
bool valid; /* zero until following fields are valid */
/* registers values */
u8 regs[ARRAY_SIZE(lm95245_reg_address)];
u8 config1, config2;
}; };
/* Conversions */ /* Conversions */
@ -135,60 +125,36 @@ static int temp_from_reg_signed(u8 val_h, u8 val_l)
return temp_from_reg_unsigned(val_h, val_l); return temp_from_reg_unsigned(val_h, val_l);
} }
static struct lm95245_data *lm95245_update_device(struct device *dev) static int lm95245_read_conversion_rate(struct lm95245_data *data)
{ {
struct lm95245_data *data = dev_get_drvdata(dev); unsigned int rate;
struct i2c_client *client = data->client; int ret;
mutex_lock(&data->update_lock); ret = regmap_read(data->regmap, LM95245_REG_RW_CONVERS_RATE, &rate);
if (ret < 0)
if (time_after(jiffies, data->last_updated return ret;
+ msecs_to_jiffies(data->interval)) || !data->valid) {
int i;
for (i = 0; i < ARRAY_SIZE(lm95245_reg_address); i++)
data->regs[i]
= i2c_smbus_read_byte_data(client,
lm95245_reg_address[i]);
data->last_updated = jiffies;
data->valid = 1;
}
mutex_unlock(&data->update_lock);
return data;
}
static unsigned long lm95245_read_conversion_rate(struct i2c_client *client)
{
int rate;
unsigned long interval;
rate = i2c_smbus_read_byte_data(client, LM95245_REG_RW_CONVERS_RATE);
switch (rate) { switch (rate) {
case RATE_CR0063: case RATE_CR0063:
interval = 63; data->interval = 63;
break; break;
case RATE_CR0364: case RATE_CR0364:
interval = 364; data->interval = 364;
break; break;
case RATE_CR1000: case RATE_CR1000:
interval = 1000; data->interval = 1000;
break; break;
case RATE_CR2500: case RATE_CR2500:
default: default:
interval = 2500; data->interval = 2500;
break; break;
} }
return 0;
return interval;
} }
static unsigned long lm95245_set_conversion_rate(struct i2c_client *client, static int lm95245_set_conversion_rate(struct lm95245_data *data, long interval)
unsigned long interval)
{ {
int rate; int ret, rate;
if (interval <= 63) { if (interval <= 63) {
interval = 63; interval = 63;
@ -204,220 +170,288 @@ static unsigned long lm95245_set_conversion_rate(struct i2c_client *client,
rate = RATE_CR2500; rate = RATE_CR2500;
} }
i2c_smbus_write_byte_data(client, LM95245_REG_RW_CONVERS_RATE, rate); ret = regmap_write(data->regmap, LM95245_REG_RW_CONVERS_RATE, rate);
if (ret < 0)
return ret;
return interval; data->interval = interval;
return 0;
} }
/* Sysfs stuff */ static int lm95245_read_temp(struct device *dev, u32 attr, int channel,
static ssize_t show_input(struct device *dev, struct device_attribute *attr, long *val)
char *buf)
{
struct lm95245_data *data = lm95245_update_device(dev);
int temp;
int index = to_sensor_dev_attr(attr)->index;
/*
* Index 0 (Local temp) is always signed
* Index 2 (Remote temp) has both signed and unsigned data
* use signed calculation for remote if signed bit is set
*/
if (index == 0 || data->regs[index] & 0x80)
temp = temp_from_reg_signed(data->regs[index],
data->regs[index + 1]);
else
temp = temp_from_reg_unsigned(data->regs[index + 2],
data->regs[index + 3]);
return snprintf(buf, PAGE_SIZE - 1, "%d\n", temp);
}
static ssize_t show_limit(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct lm95245_data *data = lm95245_update_device(dev);
int index = to_sensor_dev_attr(attr)->index;
return snprintf(buf, PAGE_SIZE - 1, "%d\n",
data->regs[index] * 1000);
}
static ssize_t set_limit(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{ {
struct lm95245_data *data = dev_get_drvdata(dev); struct lm95245_data *data = dev_get_drvdata(dev);
int index = to_sensor_dev_attr(attr)->index; struct regmap *regmap = data->regmap;
struct i2c_client *client = data->client; int ret, regl, regh, regvall, regvalh;
unsigned long val;
if (kstrtoul(buf, 10, &val) < 0) switch (attr) {
return -EINVAL; case hwmon_temp_input:
regl = channel ? LM95245_REG_R_REMOTE_TEMPL_S :
val /= 1000; LM95245_REG_R_LOCAL_TEMPL_S;
regh = channel ? LM95245_REG_R_REMOTE_TEMPH_S :
val = clamp_val(val, 0, (index == 6 ? 127 : 255)); LM95245_REG_R_LOCAL_TEMPH_S;
ret = regmap_read(regmap, regl, &regvall);
mutex_lock(&data->update_lock); if (ret < 0)
return ret;
data->valid = 0; ret = regmap_read(regmap, regh, &regvalh);
if (ret < 0)
i2c_smbus_write_byte_data(client, lm95245_reg_address[index], val); return ret;
/*
mutex_unlock(&data->update_lock); * Local temp is always signed.
* Remote temp has both signed and unsigned data.
return count; * Use signed calculation for remote if signed bit is set
* or if reported temperature is below signed limit.
*/
if (!channel || (regvalh & 0x80) || regvalh < 0x7f) {
*val = temp_from_reg_signed(regvalh, regvall);
return 0;
}
ret = regmap_read(regmap, LM95245_REG_R_REMOTE_TEMPL_U,
&regvall);
if (ret < 0)
return ret;
ret = regmap_read(regmap, LM95245_REG_R_REMOTE_TEMPH_U,
&regvalh);
if (ret < 0)
return ret;
*val = temp_from_reg_unsigned(regvalh, regvall);
return 0;
case hwmon_temp_max:
ret = regmap_read(regmap, LM95245_REG_RW_REMOTE_OS_LIMIT,
&regvalh);
if (ret < 0)
return ret;
*val = regvalh * 1000;
return 0;
case hwmon_temp_crit:
regh = channel ? LM95245_REG_RW_REMOTE_TCRIT_LIMIT :
LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT;
ret = regmap_read(regmap, regh, &regvalh);
if (ret < 0)
return ret;
*val = regvalh * 1000;
return 0;
case hwmon_temp_max_hyst:
ret = regmap_read(regmap, LM95245_REG_RW_REMOTE_OS_LIMIT,
&regvalh);
if (ret < 0)
return ret;
ret = regmap_read(regmap, LM95245_REG_RW_COMMON_HYSTERESIS,
&regvall);
if (ret < 0)
return ret;
*val = (regvalh - regvall) * 1000;
return 0;
case hwmon_temp_crit_hyst:
regh = channel ? LM95245_REG_RW_REMOTE_TCRIT_LIMIT :
LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT;
ret = regmap_read(regmap, regh, &regvalh);
if (ret < 0)
return ret;
ret = regmap_read(regmap, LM95245_REG_RW_COMMON_HYSTERESIS,
&regvall);
if (ret < 0)
return ret;
*val = (regvalh - regvall) * 1000;
return 0;
case hwmon_temp_type:
ret = regmap_read(regmap, LM95245_REG_RW_CONFIG2, &regvalh);
if (ret < 0)
return ret;
*val = (regvalh & CFG2_REMOTE_TT) ? 1 : 2;
return 0;
case hwmon_temp_offset:
ret = regmap_read(regmap, LM95245_REG_RW_REMOTE_OFFL,
&regvall);
if (ret < 0)
return ret;
ret = regmap_read(regmap, LM95245_REG_RW_REMOTE_OFFH,
&regvalh);
if (ret < 0)
return ret;
*val = temp_from_reg_signed(regvalh, regvall);
return 0;
case hwmon_temp_max_alarm:
ret = regmap_read(regmap, LM95245_REG_R_STATUS1, &regvalh);
if (ret < 0)
return ret;
*val = !!(regvalh & STATUS1_ROS);
return 0;
case hwmon_temp_crit_alarm:
ret = regmap_read(regmap, LM95245_REG_R_STATUS1, &regvalh);
if (ret < 0)
return ret;
*val = !!(regvalh & (channel ? STATUS1_RTCRIT : STATUS1_LOC));
return 0;
case hwmon_temp_fault:
ret = regmap_read(regmap, LM95245_REG_R_STATUS1, &regvalh);
if (ret < 0)
return ret;
*val = !!(regvalh & STATUS1_DIODE_FAULT);
return 0;
default:
return -EOPNOTSUPP;
}
} }
static ssize_t show_crit_hyst(struct device *dev, struct device_attribute *attr, static int lm95245_write_temp(struct device *dev, u32 attr, int channel,
char *buf) long val)
{
struct lm95245_data *data = lm95245_update_device(dev);
int index = to_sensor_dev_attr(attr)->index;
int hyst = data->regs[index] - data->regs[8];
return snprintf(buf, PAGE_SIZE - 1, "%d\n", hyst * 1000);
}
static ssize_t set_crit_hyst(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{ {
struct lm95245_data *data = dev_get_drvdata(dev); struct lm95245_data *data = dev_get_drvdata(dev);
int index = to_sensor_dev_attr(attr)->index; struct regmap *regmap = data->regmap;
struct i2c_client *client = data->client; unsigned int regval;
unsigned long val; int ret, reg;
int hyst, limit;
if (kstrtoul(buf, 10, &val) < 0) switch (attr) {
return -EINVAL; case hwmon_temp_max:
val = clamp_val(val / 1000, 0, 255);
mutex_lock(&data->update_lock); ret = regmap_write(regmap, LM95245_REG_RW_REMOTE_OS_LIMIT, val);
return ret;
limit = i2c_smbus_read_byte_data(client, lm95245_reg_address[index]); case hwmon_temp_crit:
hyst = limit - val / 1000; reg = channel ? LM95245_REG_RW_REMOTE_TCRIT_LIMIT :
hyst = clamp_val(hyst, 0, 31); LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT;
data->regs[8] = hyst; val = clamp_val(val / 1000, 0, channel ? 255 : 127);
ret = regmap_write(regmap, reg, val);
/* shared crit hysteresis */ return ret;
i2c_smbus_write_byte_data(client, LM95245_REG_RW_COMMON_HYSTERESIS, case hwmon_temp_crit_hyst:
hyst); mutex_lock(&data->update_lock);
ret = regmap_read(regmap, LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT,
mutex_unlock(&data->update_lock); &regval);
if (ret < 0) {
return count; mutex_unlock(&data->update_lock);
return ret;
}
/* Clamp to reasonable range to prevent overflow */
val = clamp_val(val, -1000000, 1000000);
val = regval - val / 1000;
val = clamp_val(val, 0, 31);
ret = regmap_write(regmap, LM95245_REG_RW_COMMON_HYSTERESIS,
val);
mutex_unlock(&data->update_lock);
return ret;
case hwmon_temp_offset:
val = clamp_val(val, -128000, 127875);
val = val * 256 / 1000;
mutex_lock(&data->update_lock);
ret = regmap_write(regmap, LM95245_REG_RW_REMOTE_OFFL,
val & 0xe0);
if (ret < 0) {
mutex_unlock(&data->update_lock);
return ret;
}
ret = regmap_write(regmap, LM95245_REG_RW_REMOTE_OFFH,
(val >> 8) & 0xff);
mutex_unlock(&data->update_lock);
return ret;
case hwmon_temp_type:
if (val != 1 && val != 2)
return -EINVAL;
ret = regmap_update_bits(regmap, LM95245_REG_RW_CONFIG2,
CFG2_REMOTE_TT,
val == 1 ? CFG2_REMOTE_TT : 0);
return ret;
default:
return -EOPNOTSUPP;
}
} }
static ssize_t show_type(struct device *dev, struct device_attribute *attr, static int lm95245_read_chip(struct device *dev, u32 attr, int channel,
char *buf) long *val)
{ {
struct lm95245_data *data = dev_get_drvdata(dev); struct lm95245_data *data = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE - 1, switch (attr) {
data->config2 & CFG2_REMOTE_TT ? "1\n" : "2\n"); case hwmon_chip_update_interval:
*val = data->interval;
return 0;
default:
return -EOPNOTSUPP;
}
} }
static ssize_t set_type(struct device *dev, struct device_attribute *attr, static int lm95245_write_chip(struct device *dev, u32 attr, int channel,
const char *buf, size_t count) long val)
{ {
struct lm95245_data *data = dev_get_drvdata(dev); struct lm95245_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client; int ret;
unsigned long val;
if (kstrtoul(buf, 10, &val) < 0) switch (attr) {
return -EINVAL; case hwmon_chip_update_interval:
if (val != 1 && val != 2) mutex_lock(&data->update_lock);
return -EINVAL; ret = lm95245_set_conversion_rate(data, val);
mutex_unlock(&data->update_lock);
mutex_lock(&data->update_lock); return ret;
default:
if (val == 1) return -EOPNOTSUPP;
data->config2 |= CFG2_REMOTE_TT; }
else
data->config2 &= ~CFG2_REMOTE_TT;
data->valid = 0;
i2c_smbus_write_byte_data(client, LM95245_REG_RW_CONFIG2,
data->config2);
mutex_unlock(&data->update_lock);
return count;
} }
static ssize_t show_alarm(struct device *dev, struct device_attribute *attr, static int lm95245_read(struct device *dev, enum hwmon_sensor_types type,
char *buf) u32 attr, int channel, long *val)
{ {
struct lm95245_data *data = lm95245_update_device(dev); switch (type) {
int index = to_sensor_dev_attr(attr)->index; case hwmon_chip:
return lm95245_read_chip(dev, attr, channel, val);
return snprintf(buf, PAGE_SIZE - 1, "%d\n", case hwmon_temp:
!!(data->regs[9] & index)); return lm95245_read_temp(dev, attr, channel, val);
default:
return -EOPNOTSUPP;
}
} }
static ssize_t show_interval(struct device *dev, struct device_attribute *attr, static int lm95245_write(struct device *dev, enum hwmon_sensor_types type,
char *buf) u32 attr, int channel, long val)
{ {
struct lm95245_data *data = lm95245_update_device(dev); switch (type) {
case hwmon_chip:
return snprintf(buf, PAGE_SIZE - 1, "%lu\n", data->interval); return lm95245_write_chip(dev, attr, channel, val);
case hwmon_temp:
return lm95245_write_temp(dev, attr, channel, val);
default:
return -EOPNOTSUPP;
}
} }
static ssize_t set_interval(struct device *dev, struct device_attribute *attr, static umode_t lm95245_temp_is_visible(const void *data, u32 attr, int channel)
const char *buf, size_t count)
{ {
struct lm95245_data *data = dev_get_drvdata(dev); switch (attr) {
struct i2c_client *client = data->client; case hwmon_temp_input:
unsigned long val; case hwmon_temp_max_alarm:
case hwmon_temp_max_hyst:
if (kstrtoul(buf, 10, &val) < 0) case hwmon_temp_crit_alarm:
return -EINVAL; case hwmon_temp_fault:
return S_IRUGO;
mutex_lock(&data->update_lock); case hwmon_temp_type:
case hwmon_temp_max:
data->interval = lm95245_set_conversion_rate(client, val); case hwmon_temp_crit:
case hwmon_temp_offset:
mutex_unlock(&data->update_lock); return S_IRUGO | S_IWUSR;
case hwmon_temp_crit_hyst:
return count; return (channel == 0) ? S_IRUGO | S_IWUSR : S_IRUGO;
default:
return 0;
}
} }
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_input, NULL, 0); static umode_t lm95245_is_visible(const void *data,
static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_limit, enum hwmon_sensor_types type,
set_limit, 6); u32 attr, int channel)
static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_crit_hyst, {
set_crit_hyst, 6); switch (type) {
static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, case hwmon_chip:
STATUS1_LOC); switch (attr) {
case hwmon_chip_update_interval:
static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_input, NULL, 2); return S_IRUGO | S_IWUSR;
static SENSOR_DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_limit, default:
set_limit, 7); return 0;
static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_crit_hyst, NULL, 7); }
static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, show_alarm, NULL, case hwmon_temp:
STATUS1_RTCRIT); return lm95245_temp_is_visible(data, attr, channel);
static SENSOR_DEVICE_ATTR(temp2_type, S_IWUSR | S_IRUGO, show_type, default:
set_type, 0); return 0;
static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_alarm, NULL, }
STATUS1_DIODE_FAULT); }
static DEVICE_ATTR(update_interval, S_IWUSR | S_IRUGO, show_interval,
set_interval);
static struct attribute *lm95245_attrs[] = {
&sensor_dev_attr_temp1_input.dev_attr.attr,
&sensor_dev_attr_temp1_crit.dev_attr.attr,
&sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
&sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
&sensor_dev_attr_temp2_input.dev_attr.attr,
&sensor_dev_attr_temp2_crit.dev_attr.attr,
&sensor_dev_attr_temp2_crit_hyst.dev_attr.attr,
&sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
&sensor_dev_attr_temp2_type.dev_attr.attr,
&sensor_dev_attr_temp2_fault.dev_attr.attr,
&dev_attr_update_interval.attr,
NULL
};
ATTRIBUTE_GROUPS(lm95245);
/* Return 0 if detection is successful, -ENODEV otherwise */ /* Return 0 if detection is successful, -ENODEV otherwise */
static int lm95245_detect(struct i2c_client *new_client, static int lm95245_detect(struct i2c_client *new_client,
@ -453,44 +487,130 @@ static int lm95245_detect(struct i2c_client *new_client,
return 0; return 0;
} }
static void lm95245_init_client(struct i2c_client *client, static int lm95245_init_client(struct lm95245_data *data)
struct lm95245_data *data)
{ {
data->interval = lm95245_read_conversion_rate(client); int ret;
data->config1 = i2c_smbus_read_byte_data(client, ret = lm95245_read_conversion_rate(data);
LM95245_REG_RW_CONFIG1); if (ret < 0)
data->config2 = i2c_smbus_read_byte_data(client, return ret;
LM95245_REG_RW_CONFIG2);
if (data->config1 & CFG_STOP) { return regmap_update_bits(data->regmap, LM95245_REG_RW_CONFIG1,
/* Clear the standby bit */ CFG_STOP, 0);
data->config1 &= ~CFG_STOP; }
i2c_smbus_write_byte_data(client, LM95245_REG_RW_CONFIG1,
data->config1); static bool lm95245_is_writeable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case LM95245_REG_RW_CONFIG1:
case LM95245_REG_RW_CONVERS_RATE:
case LM95245_REG_W_ONE_SHOT:
case LM95245_REG_RW_CONFIG2:
case LM95245_REG_RW_REMOTE_OFFH:
case LM95245_REG_RW_REMOTE_OFFL:
case LM95245_REG_RW_REMOTE_OS_LIMIT:
case LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT:
case LM95245_REG_RW_REMOTE_TCRIT_LIMIT:
case LM95245_REG_RW_COMMON_HYSTERESIS:
return true;
default:
return false;
} }
} }
static bool lm95245_is_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case LM95245_REG_R_STATUS1:
case LM95245_REG_R_STATUS2:
case LM95245_REG_R_LOCAL_TEMPH_S:
case LM95245_REG_R_LOCAL_TEMPL_S:
case LM95245_REG_R_REMOTE_TEMPH_S:
case LM95245_REG_R_REMOTE_TEMPL_S:
case LM95245_REG_R_REMOTE_TEMPH_U:
case LM95245_REG_R_REMOTE_TEMPL_U:
return true;
default:
return false;
}
}
static const struct regmap_config lm95245_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.writeable_reg = lm95245_is_writeable_reg,
.volatile_reg = lm95245_is_volatile_reg,
.cache_type = REGCACHE_RBTREE,
.use_single_rw = true,
};
static const u32 lm95245_chip_config[] = {
HWMON_C_UPDATE_INTERVAL,
0
};
static const struct hwmon_channel_info lm95245_chip = {
.type = hwmon_chip,
.config = lm95245_chip_config,
};
static const u32 lm95245_temp_config[] = {
HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_CRIT_ALARM,
HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | HWMON_T_CRIT |
HWMON_T_CRIT_HYST | HWMON_T_FAULT | HWMON_T_MAX_ALARM |
HWMON_T_CRIT_ALARM | HWMON_T_TYPE | HWMON_T_OFFSET,
0
};
static const struct hwmon_channel_info lm95245_temp = {
.type = hwmon_temp,
.config = lm95245_temp_config,
};
static const struct hwmon_channel_info *lm95245_info[] = {
&lm95245_chip,
&lm95245_temp,
NULL
};
static const struct hwmon_ops lm95245_hwmon_ops = {
.is_visible = lm95245_is_visible,
.read = lm95245_read,
.write = lm95245_write,
};
static const struct hwmon_chip_info lm95245_chip_info = {
.ops = &lm95245_hwmon_ops,
.info = lm95245_info,
};
static int lm95245_probe(struct i2c_client *client, static int lm95245_probe(struct i2c_client *client,
const struct i2c_device_id *id) const struct i2c_device_id *id)
{ {
struct device *dev = &client->dev; struct device *dev = &client->dev;
struct lm95245_data *data; struct lm95245_data *data;
struct device *hwmon_dev; struct device *hwmon_dev;
int ret;
data = devm_kzalloc(dev, sizeof(struct lm95245_data), GFP_KERNEL); data = devm_kzalloc(dev, sizeof(struct lm95245_data), GFP_KERNEL);
if (!data) if (!data)
return -ENOMEM; return -ENOMEM;
data->client = client; data->regmap = devm_regmap_init_i2c(client, &lm95245_regmap_config);
if (IS_ERR(data->regmap))
return PTR_ERR(data->regmap);
mutex_init(&data->update_lock); mutex_init(&data->update_lock);
/* Initialize the LM95245 chip */ /* Initialize the LM95245 chip */
lm95245_init_client(client, data); ret = lm95245_init_client(data);
if (ret < 0)
return ret;
hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
data, data,
lm95245_groups); &lm95245_chip_info,
NULL);
return PTR_ERR_OR_ZERO(hwmon_dev); return PTR_ERR_OR_ZERO(hwmon_dev);
} }

View File

@ -30,6 +30,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/slab.h> #include <linux/slab.h>
@ -52,6 +53,7 @@ struct ltc4151_data {
struct mutex update_lock; struct mutex update_lock;
bool valid; bool valid;
unsigned long last_updated; /* in jiffies */ unsigned long last_updated; /* in jiffies */
unsigned int shunt; /* in micro ohms */
/* Registers */ /* Registers */
u8 regs[6]; u8 regs[6];
@ -111,9 +113,9 @@ static int ltc4151_get_value(struct ltc4151_data *data, u8 reg)
case LTC4151_SENSE_H: case LTC4151_SENSE_H:
/* /*
* 20uV resolution. Convert to current as measured with * 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; break;
case LTC4151_VIN_H: case LTC4151_VIN_H:
/* 25 mV per increment */ /* 25 mV per increment */
@ -176,6 +178,7 @@ static int ltc4151_probe(struct i2c_client *client,
struct device *dev = &client->dev; struct device *dev = &client->dev;
struct ltc4151_data *data; struct ltc4151_data *data;
struct device *hwmon_dev; struct device *hwmon_dev;
u32 shunt;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV; return -ENODEV;
@ -184,6 +187,15 @@ static int ltc4151_probe(struct i2c_client *client,
if (!data) if (!data)
return -ENOMEM; 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; data->client = client;
mutex_init(&data->update_lock); mutex_init(&data->update_lock);
@ -199,10 +211,16 @@ static const struct i2c_device_id ltc4151_id[] = {
}; };
MODULE_DEVICE_TABLE(i2c, 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 */ /* This is the driver that will be inserted */
static struct i2c_driver ltc4151_driver = { static struct i2c_driver ltc4151_driver = {
.driver = { .driver = {
.name = "ltc4151", .name = "ltc4151",
.of_match_table = of_match_ptr(ltc4151_match),
}, },
.probe = ltc4151_probe, .probe = ltc4151_probe,
.id_table = ltc4151_id, .id_table = ltc4151_id,

View File

@ -16,6 +16,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/bitops.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/i2c.h> #include <linux/i2c.h>
@ -53,8 +54,6 @@ enum ltc4245_cmd {
struct ltc4245_data { struct ltc4245_data {
struct i2c_client *client; struct i2c_client *client;
const struct attribute_group *groups[3];
struct mutex update_lock; struct mutex update_lock;
bool valid; bool valid;
unsigned long last_updated; /* in jiffies */ unsigned long last_updated; /* in jiffies */
@ -162,7 +161,7 @@ static struct ltc4245_data *ltc4245_update_device(struct device *dev)
ltc4245_update_gpios(dev); ltc4245_update_gpios(dev);
data->last_updated = jiffies; data->last_updated = jiffies;
data->valid = 1; data->valid = true;
} }
mutex_unlock(&data->update_lock); mutex_unlock(&data->update_lock);
@ -256,214 +255,205 @@ static unsigned int ltc4245_get_current(struct device *dev, u8 reg)
return curr; return curr;
} }
static ssize_t ltc4245_show_voltage(struct device *dev, /* Map from voltage channel index to voltage register */
struct device_attribute *da,
char *buf) static const s8 ltc4245_in_regs[] = {
LTC4245_12VIN, LTC4245_5VIN, LTC4245_3VIN, LTC4245_VEEIN,
LTC4245_12VOUT, LTC4245_5VOUT, LTC4245_3VOUT, LTC4245_VEEOUT,
};
/* Map from current channel index to current register */
static const s8 ltc4245_curr_regs[] = {
LTC4245_12VSENSE, LTC4245_5VSENSE, LTC4245_3VSENSE, LTC4245_VEESENSE,
};
static int ltc4245_read_curr(struct device *dev, u32 attr, int channel,
long *val)
{ {
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
const int voltage = ltc4245_get_voltage(dev, attr->index);
return snprintf(buf, PAGE_SIZE, "%d\n", voltage);
}
static ssize_t ltc4245_show_current(struct device *dev,
struct device_attribute *da,
char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
const unsigned int curr = ltc4245_get_current(dev, attr->index);
return snprintf(buf, PAGE_SIZE, "%u\n", curr);
}
static ssize_t ltc4245_show_power(struct device *dev,
struct device_attribute *da,
char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
const unsigned int curr = ltc4245_get_current(dev, attr->index);
const int output_voltage = ltc4245_get_voltage(dev, attr->index+1);
/* current in mA * voltage in mV == power in uW */
const unsigned int power = abs(output_voltage * curr);
return snprintf(buf, PAGE_SIZE, "%u\n", power);
}
static ssize_t ltc4245_show_alarm(struct device *dev,
struct device_attribute *da,
char *buf)
{
struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(da);
struct ltc4245_data *data = ltc4245_update_device(dev); struct ltc4245_data *data = ltc4245_update_device(dev);
const u8 reg = data->cregs[attr->index];
const u32 mask = attr->nr;
return snprintf(buf, PAGE_SIZE, "%u\n", (reg & mask) ? 1 : 0); switch (attr) {
case hwmon_curr_input:
*val = ltc4245_get_current(dev, ltc4245_curr_regs[channel]);
return 0;
case hwmon_curr_max_alarm:
*val = !!(data->cregs[LTC4245_FAULT1] & BIT(channel + 4));
return 0;
default:
return -EOPNOTSUPP;
}
} }
static ssize_t ltc4245_show_gpio(struct device *dev, static int ltc4245_read_in(struct device *dev, u32 attr, int channel, long *val)
struct device_attribute *da,
char *buf)
{ {
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
struct ltc4245_data *data = ltc4245_update_device(dev); struct ltc4245_data *data = ltc4245_update_device(dev);
int val = data->gpios[attr->index];
/* handle stale GPIO's */ switch (attr) {
if (val < 0) case hwmon_in_input:
return val; if (channel < 8) {
*val = ltc4245_get_voltage(dev,
ltc4245_in_regs[channel]);
} else {
int regval = data->gpios[channel - 8];
/* Convert to millivolts and print */ if (regval < 0)
return snprintf(buf, PAGE_SIZE, "%u\n", val * 10); return regval;
*val = regval * 10;
}
return 0;
case hwmon_in_min_alarm:
if (channel < 4)
*val = !!(data->cregs[LTC4245_FAULT1] & BIT(channel));
else
*val = !!(data->cregs[LTC4245_FAULT2] &
BIT(channel - 4));
return 0;
default:
return -EOPNOTSUPP;
}
} }
/* Construct a sensor_device_attribute structure for each register */ static int ltc4245_read_power(struct device *dev, u32 attr, int channel,
long *val)
/* Input voltages */
static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ltc4245_show_voltage, NULL,
LTC4245_12VIN);
static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ltc4245_show_voltage, NULL,
LTC4245_5VIN);
static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, ltc4245_show_voltage, NULL,
LTC4245_3VIN);
static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, ltc4245_show_voltage, NULL,
LTC4245_VEEIN);
/* Input undervoltage alarms */
static SENSOR_DEVICE_ATTR_2(in1_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
1 << 0, LTC4245_FAULT1);
static SENSOR_DEVICE_ATTR_2(in2_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
1 << 1, LTC4245_FAULT1);
static SENSOR_DEVICE_ATTR_2(in3_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
1 << 2, LTC4245_FAULT1);
static SENSOR_DEVICE_ATTR_2(in4_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
1 << 3, LTC4245_FAULT1);
/* Currents (via sense resistor) */
static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc4245_show_current, NULL,
LTC4245_12VSENSE);
static SENSOR_DEVICE_ATTR(curr2_input, S_IRUGO, ltc4245_show_current, NULL,
LTC4245_5VSENSE);
static SENSOR_DEVICE_ATTR(curr3_input, S_IRUGO, ltc4245_show_current, NULL,
LTC4245_3VSENSE);
static SENSOR_DEVICE_ATTR(curr4_input, S_IRUGO, ltc4245_show_current, NULL,
LTC4245_VEESENSE);
/* Overcurrent alarms */
static SENSOR_DEVICE_ATTR_2(curr1_max_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
1 << 4, LTC4245_FAULT1);
static SENSOR_DEVICE_ATTR_2(curr2_max_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
1 << 5, LTC4245_FAULT1);
static SENSOR_DEVICE_ATTR_2(curr3_max_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
1 << 6, LTC4245_FAULT1);
static SENSOR_DEVICE_ATTR_2(curr4_max_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
1 << 7, LTC4245_FAULT1);
/* Output voltages */
static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, ltc4245_show_voltage, NULL,
LTC4245_12VOUT);
static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, ltc4245_show_voltage, NULL,
LTC4245_5VOUT);
static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, ltc4245_show_voltage, NULL,
LTC4245_3VOUT);
static SENSOR_DEVICE_ATTR(in8_input, S_IRUGO, ltc4245_show_voltage, NULL,
LTC4245_VEEOUT);
/* Power Bad alarms */
static SENSOR_DEVICE_ATTR_2(in5_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
1 << 0, LTC4245_FAULT2);
static SENSOR_DEVICE_ATTR_2(in6_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
1 << 1, LTC4245_FAULT2);
static SENSOR_DEVICE_ATTR_2(in7_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
1 << 2, LTC4245_FAULT2);
static SENSOR_DEVICE_ATTR_2(in8_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
1 << 3, LTC4245_FAULT2);
/* GPIO voltages */
static SENSOR_DEVICE_ATTR(in9_input, S_IRUGO, ltc4245_show_gpio, NULL, 0);
static SENSOR_DEVICE_ATTR(in10_input, S_IRUGO, ltc4245_show_gpio, NULL, 1);
static SENSOR_DEVICE_ATTR(in11_input, S_IRUGO, ltc4245_show_gpio, NULL, 2);
/* Power Consumption (virtual) */
static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, ltc4245_show_power, NULL,
LTC4245_12VSENSE);
static SENSOR_DEVICE_ATTR(power2_input, S_IRUGO, ltc4245_show_power, NULL,
LTC4245_5VSENSE);
static SENSOR_DEVICE_ATTR(power3_input, S_IRUGO, ltc4245_show_power, NULL,
LTC4245_3VSENSE);
static SENSOR_DEVICE_ATTR(power4_input, S_IRUGO, ltc4245_show_power, NULL,
LTC4245_VEESENSE);
/*
* Finally, construct an array of pointers to members of the above objects,
* as required for sysfs_create_group()
*/
static struct attribute *ltc4245_std_attributes[] = {
&sensor_dev_attr_in1_input.dev_attr.attr,
&sensor_dev_attr_in2_input.dev_attr.attr,
&sensor_dev_attr_in3_input.dev_attr.attr,
&sensor_dev_attr_in4_input.dev_attr.attr,
&sensor_dev_attr_in1_min_alarm.dev_attr.attr,
&sensor_dev_attr_in2_min_alarm.dev_attr.attr,
&sensor_dev_attr_in3_min_alarm.dev_attr.attr,
&sensor_dev_attr_in4_min_alarm.dev_attr.attr,
&sensor_dev_attr_curr1_input.dev_attr.attr,
&sensor_dev_attr_curr2_input.dev_attr.attr,
&sensor_dev_attr_curr3_input.dev_attr.attr,
&sensor_dev_attr_curr4_input.dev_attr.attr,
&sensor_dev_attr_curr1_max_alarm.dev_attr.attr,
&sensor_dev_attr_curr2_max_alarm.dev_attr.attr,
&sensor_dev_attr_curr3_max_alarm.dev_attr.attr,
&sensor_dev_attr_curr4_max_alarm.dev_attr.attr,
&sensor_dev_attr_in5_input.dev_attr.attr,
&sensor_dev_attr_in6_input.dev_attr.attr,
&sensor_dev_attr_in7_input.dev_attr.attr,
&sensor_dev_attr_in8_input.dev_attr.attr,
&sensor_dev_attr_in5_min_alarm.dev_attr.attr,
&sensor_dev_attr_in6_min_alarm.dev_attr.attr,
&sensor_dev_attr_in7_min_alarm.dev_attr.attr,
&sensor_dev_attr_in8_min_alarm.dev_attr.attr,
&sensor_dev_attr_in9_input.dev_attr.attr,
&sensor_dev_attr_power1_input.dev_attr.attr,
&sensor_dev_attr_power2_input.dev_attr.attr,
&sensor_dev_attr_power3_input.dev_attr.attr,
&sensor_dev_attr_power4_input.dev_attr.attr,
NULL,
};
static struct attribute *ltc4245_gpio_attributes[] = {
&sensor_dev_attr_in10_input.dev_attr.attr,
&sensor_dev_attr_in11_input.dev_attr.attr,
NULL,
};
static const struct attribute_group ltc4245_std_group = {
.attrs = ltc4245_std_attributes,
};
static const struct attribute_group ltc4245_gpio_group = {
.attrs = ltc4245_gpio_attributes,
};
static void ltc4245_sysfs_add_groups(struct ltc4245_data *data)
{ {
/* standard sysfs attributes */ unsigned long curr;
data->groups[0] = &ltc4245_std_group; long voltage;
/* if we're using the extra gpio support, register it's attributes */ switch (attr) {
if (data->use_extra_gpios) case hwmon_power_input:
data->groups[1] = &ltc4245_gpio_group; (void)ltc4245_update_device(dev);
curr = ltc4245_get_current(dev, ltc4245_curr_regs[channel]);
voltage = ltc4245_get_voltage(dev, ltc4245_in_regs[channel]);
*val = abs(curr * voltage);
return 0;
default:
return -EOPNOTSUPP;
}
} }
static int ltc4245_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
switch (type) {
case hwmon_curr:
return ltc4245_read_curr(dev, attr, channel, val);
case hwmon_power:
return ltc4245_read_power(dev, attr, channel, val);
case hwmon_in:
return ltc4245_read_in(dev, attr, channel - 1, val);
default:
return -EOPNOTSUPP;
}
}
static umode_t ltc4245_is_visible(const void *_data,
enum hwmon_sensor_types type,
u32 attr, int channel)
{
const struct ltc4245_data *data = _data;
switch (type) {
case hwmon_in:
if (channel == 0)
return 0;
switch (attr) {
case hwmon_in_input:
if (channel > 9 && !data->use_extra_gpios)
return 0;
return S_IRUGO;
case hwmon_in_min_alarm:
if (channel > 8)
return 0;
return S_IRUGO;
default:
return 0;
}
case hwmon_curr:
switch (attr) {
case hwmon_curr_input:
case hwmon_curr_max_alarm:
return S_IRUGO;
default:
return 0;
}
case hwmon_power:
switch (attr) {
case hwmon_power_input:
return S_IRUGO;
default:
return 0;
}
default:
return 0;
}
}
static const u32 ltc4245_in_config[] = {
HWMON_I_INPUT, /* dummy, skipped in is_visible */
HWMON_I_INPUT | HWMON_I_MIN_ALARM,
HWMON_I_INPUT | HWMON_I_MIN_ALARM,
HWMON_I_INPUT | HWMON_I_MIN_ALARM,
HWMON_I_INPUT | HWMON_I_MIN_ALARM,
HWMON_I_INPUT | HWMON_I_MIN_ALARM,
HWMON_I_INPUT | HWMON_I_MIN_ALARM,
HWMON_I_INPUT | HWMON_I_MIN_ALARM,
HWMON_I_INPUT | HWMON_I_MIN_ALARM,
HWMON_I_INPUT,
HWMON_I_INPUT,
HWMON_I_INPUT,
0
};
static const struct hwmon_channel_info ltc4245_in = {
.type = hwmon_in,
.config = ltc4245_in_config,
};
static const u32 ltc4245_curr_config[] = {
HWMON_C_INPUT | HWMON_C_MAX_ALARM,
HWMON_C_INPUT | HWMON_C_MAX_ALARM,
HWMON_C_INPUT | HWMON_C_MAX_ALARM,
HWMON_C_INPUT | HWMON_C_MAX_ALARM,
0
};
static const struct hwmon_channel_info ltc4245_curr = {
.type = hwmon_curr,
.config = ltc4245_curr_config,
};
static const u32 ltc4245_power_config[] = {
HWMON_P_INPUT,
HWMON_P_INPUT,
HWMON_P_INPUT,
HWMON_P_INPUT,
0
};
static const struct hwmon_channel_info ltc4245_power = {
.type = hwmon_power,
.config = ltc4245_power_config,
};
static const struct hwmon_channel_info *ltc4245_info[] = {
&ltc4245_in,
&ltc4245_curr,
&ltc4245_power,
NULL
};
static const struct hwmon_ops ltc4245_hwmon_ops = {
.is_visible = ltc4245_is_visible,
.read = ltc4245_read,
};
static const struct hwmon_chip_info ltc4245_chip_info = {
.ops = &ltc4245_hwmon_ops,
.info = ltc4245_info,
};
static bool ltc4245_use_extra_gpios(struct i2c_client *client) static bool ltc4245_use_extra_gpios(struct i2c_client *client)
{ {
struct ltc4245_platform_data *pdata = dev_get_platdata(&client->dev); struct ltc4245_platform_data *pdata = dev_get_platdata(&client->dev);
@ -502,12 +492,10 @@ static int ltc4245_probe(struct i2c_client *client,
i2c_smbus_write_byte_data(client, LTC4245_FAULT1, 0x00); i2c_smbus_write_byte_data(client, LTC4245_FAULT1, 0x00);
i2c_smbus_write_byte_data(client, LTC4245_FAULT2, 0x00); i2c_smbus_write_byte_data(client, LTC4245_FAULT2, 0x00);
/* Add sysfs hooks */ hwmon_dev = devm_hwmon_device_register_with_info(&client->dev,
ltc4245_sysfs_add_groups(data); client->name, data,
&ltc4245_chip_info,
hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev, NULL);
client->name, data,
data->groups);
return PTR_ERR_OR_ZERO(hwmon_dev); return PTR_ERR_OR_ZERO(hwmon_dev);
} }

View File

@ -17,7 +17,6 @@
#include <linux/err.h> #include <linux/err.h>
#include <linux/hwmon.h> #include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/jiffies.h> #include <linux/jiffies.h>
@ -169,362 +168,288 @@ static u8 bits_for_tach_period(int rpm)
return bits; return bits;
} }
static ssize_t get_fan(struct device *dev, static int max31790_read_fan(struct device *dev, u32 attr, int channel,
struct device_attribute *devattr, char *buf) long *val)
{ {
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct max31790_data *data = max31790_update_device(dev); struct max31790_data *data = max31790_update_device(dev);
int sr, rpm; int sr, rpm;
if (IS_ERR(data)) if (IS_ERR(data))
return PTR_ERR(data); return PTR_ERR(data);
sr = get_tach_period(data->fan_dynamics[attr->index]); switch (attr) {
rpm = RPM_FROM_REG(data->tach[attr->index], sr); case hwmon_fan_input:
sr = get_tach_period(data->fan_dynamics[channel]);
return sprintf(buf, "%d\n", rpm); rpm = RPM_FROM_REG(data->tach[channel], sr);
*val = rpm;
return 0;
case hwmon_fan_target:
sr = get_tach_period(data->fan_dynamics[channel]);
rpm = RPM_FROM_REG(data->target_count[channel], sr);
*val = rpm;
return 0;
case hwmon_fan_fault:
*val = !!(data->fault_status & (1 << channel));
return 0;
default:
return -EOPNOTSUPP;
}
} }
static ssize_t get_fan_target(struct device *dev, static int max31790_write_fan(struct device *dev, u32 attr, int channel,
struct device_attribute *devattr, char *buf) long val)
{ {
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct max31790_data *data = max31790_update_device(dev);
int sr, rpm;
if (IS_ERR(data))
return PTR_ERR(data);
sr = get_tach_period(data->fan_dynamics[attr->index]);
rpm = RPM_FROM_REG(data->target_count[attr->index], sr);
return sprintf(buf, "%d\n", rpm);
}
static ssize_t set_fan_target(struct device *dev,
struct device_attribute *devattr,
const char *buf, size_t count)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct max31790_data *data = dev_get_drvdata(dev); struct max31790_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client; struct i2c_client *client = data->client;
int target_count;
int err = 0;
u8 bits; u8 bits;
int sr; int sr;
int target_count;
unsigned long rpm;
int err;
err = kstrtoul(buf, 10, &rpm);
if (err)
return err;
mutex_lock(&data->update_lock); mutex_lock(&data->update_lock);
rpm = clamp_val(rpm, FAN_RPM_MIN, FAN_RPM_MAX); switch (attr) {
bits = bits_for_tach_period(rpm); case hwmon_fan_target:
data->fan_dynamics[attr->index] = val = clamp_val(val, FAN_RPM_MIN, FAN_RPM_MAX);
((data->fan_dynamics[attr->index] bits = bits_for_tach_period(val);
& ~MAX31790_FAN_DYN_SR_MASK) data->fan_dynamics[channel] =
| (bits << MAX31790_FAN_DYN_SR_SHIFT)); ((data->fan_dynamics[channel] &
err = i2c_smbus_write_byte_data(client, ~MAX31790_FAN_DYN_SR_MASK) |
MAX31790_REG_FAN_DYNAMICS(attr->index), (bits << MAX31790_FAN_DYN_SR_SHIFT));
data->fan_dynamics[attr->index]); err = i2c_smbus_write_byte_data(client,
MAX31790_REG_FAN_DYNAMICS(channel),
data->fan_dynamics[channel]);
if (err < 0)
break;
if (err < 0) { sr = get_tach_period(data->fan_dynamics[channel]);
mutex_unlock(&data->update_lock); target_count = RPM_TO_REG(val, sr);
return err; target_count = clamp_val(target_count, 0x1, 0x7FF);
}
sr = get_tach_period(data->fan_dynamics[attr->index]); data->target_count[channel] = target_count << 5;
target_count = RPM_TO_REG(rpm, sr);
target_count = clamp_val(target_count, 0x1, 0x7FF);
data->target_count[attr->index] = target_count << 5; err = i2c_smbus_write_word_swapped(client,
MAX31790_REG_TARGET_COUNT(channel),
err = i2c_smbus_write_word_swapped(client, data->target_count[channel]);
MAX31790_REG_TARGET_COUNT(attr->index),
data->target_count[attr->index]);
mutex_unlock(&data->update_lock);
if (err < 0)
return err;
return count;
}
static ssize_t get_pwm(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct max31790_data *data = max31790_update_device(dev);
int pwm;
if (IS_ERR(data))
return PTR_ERR(data);
pwm = data->pwm[attr->index] >> 8;
return sprintf(buf, "%d\n", pwm);
}
static ssize_t set_pwm(struct device *dev,
struct device_attribute *devattr,
const char *buf, size_t count)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct max31790_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
unsigned long pwm;
int err;
err = kstrtoul(buf, 10, &pwm);
if (err)
return err;
if (pwm > 255)
return -EINVAL;
mutex_lock(&data->update_lock);
data->pwm[attr->index] = pwm << 8;
err = i2c_smbus_write_word_swapped(client,
MAX31790_REG_PWMOUT(attr->index),
data->pwm[attr->index]);
mutex_unlock(&data->update_lock);
if (err < 0)
return err;
return count;
}
static ssize_t get_pwm_enable(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct max31790_data *data = max31790_update_device(dev);
int mode;
if (IS_ERR(data))
return PTR_ERR(data);
if (data->fan_config[attr->index] & MAX31790_FAN_CFG_RPM_MODE)
mode = 2;
else if (data->fan_config[attr->index] & MAX31790_FAN_CFG_TACH_INPUT_EN)
mode = 1;
else
mode = 0;
return sprintf(buf, "%d\n", mode);
}
static ssize_t set_pwm_enable(struct device *dev,
struct device_attribute *devattr,
const char *buf, size_t count)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct max31790_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
unsigned long mode;
int err;
err = kstrtoul(buf, 10, &mode);
if (err)
return err;
switch (mode) {
case 0:
data->fan_config[attr->index] =
data->fan_config[attr->index]
& ~(MAX31790_FAN_CFG_TACH_INPUT_EN
| MAX31790_FAN_CFG_RPM_MODE);
break;
case 1:
data->fan_config[attr->index] =
(data->fan_config[attr->index]
| MAX31790_FAN_CFG_TACH_INPUT_EN)
& ~MAX31790_FAN_CFG_RPM_MODE;
break;
case 2:
data->fan_config[attr->index] =
data->fan_config[attr->index]
| MAX31790_FAN_CFG_TACH_INPUT_EN
| MAX31790_FAN_CFG_RPM_MODE;
break; break;
default: default:
return -EINVAL; err = -EOPNOTSUPP;
break;
} }
mutex_lock(&data->update_lock);
err = i2c_smbus_write_byte_data(client,
MAX31790_REG_FAN_CONFIG(attr->index),
data->fan_config[attr->index]);
mutex_unlock(&data->update_lock); mutex_unlock(&data->update_lock);
if (err < 0) return err;
return err;
return count;
} }
static ssize_t get_fan_fault(struct device *dev, static umode_t max31790_fan_is_visible(const void *_data, u32 attr, int channel)
struct device_attribute *devattr, char *buf) {
const struct max31790_data *data = _data;
u8 fan_config = data->fan_config[channel % NR_CHANNEL];
switch (attr) {
case hwmon_fan_input:
case hwmon_fan_fault:
if (channel < NR_CHANNEL ||
(fan_config & MAX31790_FAN_CFG_TACH_INPUT))
return S_IRUGO;
return 0;
case hwmon_fan_target:
if (channel < NR_CHANNEL &&
!(fan_config & MAX31790_FAN_CFG_TACH_INPUT))
return S_IRUGO | S_IWUSR;
return 0;
default:
return 0;
}
}
static int max31790_read_pwm(struct device *dev, u32 attr, int channel,
long *val)
{ {
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct max31790_data *data = max31790_update_device(dev); struct max31790_data *data = max31790_update_device(dev);
int fault; u8 fan_config = data->fan_config[channel];
if (IS_ERR(data)) if (IS_ERR(data))
return PTR_ERR(data); return PTR_ERR(data);
fault = !!(data->fault_status & (1 << attr->index)); switch (attr) {
case hwmon_pwm_input:
return sprintf(buf, "%d\n", fault); *val = data->pwm[channel] >> 8;
return 0;
case hwmon_pwm_enable:
if (fan_config & MAX31790_FAN_CFG_RPM_MODE)
*val = 2;
else if (fan_config & MAX31790_FAN_CFG_TACH_INPUT_EN)
*val = 1;
else
*val = 0;
return 0;
default:
return -EOPNOTSUPP;
}
} }
static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, get_fan, NULL, 0); static int max31790_write_pwm(struct device *dev, u32 attr, int channel,
static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, get_fan, NULL, 1); long val)
static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, get_fan, NULL, 2); {
static SENSOR_DEVICE_ATTR(fan4_input, S_IRUGO, get_fan, NULL, 3); struct max31790_data *data = dev_get_drvdata(dev);
static SENSOR_DEVICE_ATTR(fan5_input, S_IRUGO, get_fan, NULL, 4); struct i2c_client *client = data->client;
static SENSOR_DEVICE_ATTR(fan6_input, S_IRUGO, get_fan, NULL, 5); u8 fan_config;
int err = 0;
static SENSOR_DEVICE_ATTR(fan1_fault, S_IRUGO, get_fan_fault, NULL, 0); mutex_lock(&data->update_lock);
static SENSOR_DEVICE_ATTR(fan2_fault, S_IRUGO, get_fan_fault, NULL, 1);
static SENSOR_DEVICE_ATTR(fan3_fault, S_IRUGO, get_fan_fault, NULL, 2);
static SENSOR_DEVICE_ATTR(fan4_fault, S_IRUGO, get_fan_fault, NULL, 3);
static SENSOR_DEVICE_ATTR(fan5_fault, S_IRUGO, get_fan_fault, NULL, 4);
static SENSOR_DEVICE_ATTR(fan6_fault, S_IRUGO, get_fan_fault, NULL, 5);
static SENSOR_DEVICE_ATTR(fan7_input, S_IRUGO, get_fan, NULL, 6); switch (attr) {
static SENSOR_DEVICE_ATTR(fan8_input, S_IRUGO, get_fan, NULL, 7); case hwmon_pwm_input:
static SENSOR_DEVICE_ATTR(fan9_input, S_IRUGO, get_fan, NULL, 8); if (val < 0 || val > 255) {
static SENSOR_DEVICE_ATTR(fan10_input, S_IRUGO, get_fan, NULL, 9); err = -EINVAL;
static SENSOR_DEVICE_ATTR(fan11_input, S_IRUGO, get_fan, NULL, 10); break;
static SENSOR_DEVICE_ATTR(fan12_input, S_IRUGO, get_fan, NULL, 11); }
data->pwm[channel] = val << 8;
err = i2c_smbus_write_word_swapped(client,
MAX31790_REG_PWMOUT(channel),
val);
break;
case hwmon_pwm_enable:
fan_config = data->fan_config[channel];
if (val == 0) {
fan_config &= ~(MAX31790_FAN_CFG_TACH_INPUT_EN |
MAX31790_FAN_CFG_RPM_MODE);
} else if (val == 1) {
fan_config = (fan_config |
MAX31790_FAN_CFG_TACH_INPUT_EN) &
~MAX31790_FAN_CFG_RPM_MODE;
} else if (val == 2) {
fan_config |= MAX31790_FAN_CFG_TACH_INPUT_EN |
MAX31790_FAN_CFG_RPM_MODE;
} else {
err = -EINVAL;
break;
}
data->fan_config[channel] = fan_config;
err = i2c_smbus_write_byte_data(client,
MAX31790_REG_FAN_CONFIG(channel),
fan_config);
break;
default:
err = -EOPNOTSUPP;
break;
}
static SENSOR_DEVICE_ATTR(fan7_fault, S_IRUGO, get_fan_fault, NULL, 6); mutex_unlock(&data->update_lock);
static SENSOR_DEVICE_ATTR(fan8_fault, S_IRUGO, get_fan_fault, NULL, 7);
static SENSOR_DEVICE_ATTR(fan9_fault, S_IRUGO, get_fan_fault, NULL, 8);
static SENSOR_DEVICE_ATTR(fan10_fault, S_IRUGO, get_fan_fault, NULL, 9);
static SENSOR_DEVICE_ATTR(fan11_fault, S_IRUGO, get_fan_fault, NULL, 10);
static SENSOR_DEVICE_ATTR(fan12_fault, S_IRUGO, get_fan_fault, NULL, 11);
static SENSOR_DEVICE_ATTR(fan1_target, S_IWUSR | S_IRUGO, return err;
get_fan_target, set_fan_target, 0); }
static SENSOR_DEVICE_ATTR(fan2_target, S_IWUSR | S_IRUGO,
get_fan_target, set_fan_target, 1);
static SENSOR_DEVICE_ATTR(fan3_target, S_IWUSR | S_IRUGO,
get_fan_target, set_fan_target, 2);
static SENSOR_DEVICE_ATTR(fan4_target, S_IWUSR | S_IRUGO,
get_fan_target, set_fan_target, 3);
static SENSOR_DEVICE_ATTR(fan5_target, S_IWUSR | S_IRUGO,
get_fan_target, set_fan_target, 4);
static SENSOR_DEVICE_ATTR(fan6_target, S_IWUSR | S_IRUGO,
get_fan_target, set_fan_target, 5);
static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 0); static umode_t max31790_pwm_is_visible(const void *_data, u32 attr, int channel)
static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 1); {
static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 2); const struct max31790_data *data = _data;
static SENSOR_DEVICE_ATTR(pwm4, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 3); u8 fan_config = data->fan_config[channel];
static SENSOR_DEVICE_ATTR(pwm5, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 4);
static SENSOR_DEVICE_ATTR(pwm6, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 5);
static SENSOR_DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, switch (attr) {
get_pwm_enable, set_pwm_enable, 0); case hwmon_pwm_input:
static SENSOR_DEVICE_ATTR(pwm2_enable, S_IWUSR | S_IRUGO, case hwmon_pwm_enable:
get_pwm_enable, set_pwm_enable, 1); if (!(fan_config & MAX31790_FAN_CFG_TACH_INPUT))
static SENSOR_DEVICE_ATTR(pwm3_enable, S_IWUSR | S_IRUGO, return S_IRUGO | S_IWUSR;
get_pwm_enable, set_pwm_enable, 2); return 0;
static SENSOR_DEVICE_ATTR(pwm4_enable, S_IWUSR | S_IRUGO, default:
get_pwm_enable, set_pwm_enable, 3); return 0;
static SENSOR_DEVICE_ATTR(pwm5_enable, S_IWUSR | S_IRUGO, }
get_pwm_enable, set_pwm_enable, 4); }
static SENSOR_DEVICE_ATTR(pwm6_enable, S_IWUSR | S_IRUGO,
get_pwm_enable, set_pwm_enable, 5);
static struct attribute *max31790_attrs[] = { static int max31790_read(struct device *dev, enum hwmon_sensor_types type,
&sensor_dev_attr_fan1_input.dev_attr.attr, u32 attr, int channel, long *val)
&sensor_dev_attr_fan2_input.dev_attr.attr, {
&sensor_dev_attr_fan3_input.dev_attr.attr, switch (type) {
&sensor_dev_attr_fan4_input.dev_attr.attr, case hwmon_fan:
&sensor_dev_attr_fan5_input.dev_attr.attr, return max31790_read_fan(dev, attr, channel, val);
&sensor_dev_attr_fan6_input.dev_attr.attr, case hwmon_pwm:
return max31790_read_pwm(dev, attr, channel, val);
default:
return -EOPNOTSUPP;
}
}
&sensor_dev_attr_fan1_fault.dev_attr.attr, static int max31790_write(struct device *dev, enum hwmon_sensor_types type,
&sensor_dev_attr_fan2_fault.dev_attr.attr, u32 attr, int channel, long val)
&sensor_dev_attr_fan3_fault.dev_attr.attr, {
&sensor_dev_attr_fan4_fault.dev_attr.attr, switch (type) {
&sensor_dev_attr_fan5_fault.dev_attr.attr, case hwmon_fan:
&sensor_dev_attr_fan6_fault.dev_attr.attr, return max31790_write_fan(dev, attr, channel, val);
case hwmon_pwm:
return max31790_write_pwm(dev, attr, channel, val);
default:
return -EOPNOTSUPP;
}
}
&sensor_dev_attr_fan7_input.dev_attr.attr, static umode_t max31790_is_visible(const void *data,
&sensor_dev_attr_fan8_input.dev_attr.attr, enum hwmon_sensor_types type,
&sensor_dev_attr_fan9_input.dev_attr.attr, u32 attr, int channel)
&sensor_dev_attr_fan10_input.dev_attr.attr, {
&sensor_dev_attr_fan11_input.dev_attr.attr, switch (type) {
&sensor_dev_attr_fan12_input.dev_attr.attr, case hwmon_fan:
return max31790_fan_is_visible(data, attr, channel);
case hwmon_pwm:
return max31790_pwm_is_visible(data, attr, channel);
default:
return 0;
}
}
&sensor_dev_attr_fan7_fault.dev_attr.attr, static const u32 max31790_fan_config[] = {
&sensor_dev_attr_fan8_fault.dev_attr.attr, HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
&sensor_dev_attr_fan9_fault.dev_attr.attr, HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
&sensor_dev_attr_fan10_fault.dev_attr.attr, HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
&sensor_dev_attr_fan11_fault.dev_attr.attr, HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
&sensor_dev_attr_fan12_fault.dev_attr.attr, HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
HWMON_F_INPUT | HWMON_F_FAULT,
HWMON_F_INPUT | HWMON_F_FAULT,
HWMON_F_INPUT | HWMON_F_FAULT,
HWMON_F_INPUT | HWMON_F_FAULT,
HWMON_F_INPUT | HWMON_F_FAULT,
HWMON_F_INPUT | HWMON_F_FAULT,
0
};
&sensor_dev_attr_fan1_target.dev_attr.attr, static const struct hwmon_channel_info max31790_fan = {
&sensor_dev_attr_fan2_target.dev_attr.attr, .type = hwmon_fan,
&sensor_dev_attr_fan3_target.dev_attr.attr, .config = max31790_fan_config,
&sensor_dev_attr_fan4_target.dev_attr.attr, };
&sensor_dev_attr_fan5_target.dev_attr.attr,
&sensor_dev_attr_fan6_target.dev_attr.attr,
&sensor_dev_attr_pwm1.dev_attr.attr, static const u32 max31790_pwm_config[] = {
&sensor_dev_attr_pwm2.dev_attr.attr, HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
&sensor_dev_attr_pwm3.dev_attr.attr, HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
&sensor_dev_attr_pwm4.dev_attr.attr, HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
&sensor_dev_attr_pwm5.dev_attr.attr, HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
&sensor_dev_attr_pwm6.dev_attr.attr, HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
0
};
&sensor_dev_attr_pwm1_enable.dev_attr.attr, static const struct hwmon_channel_info max31790_pwm = {
&sensor_dev_attr_pwm2_enable.dev_attr.attr, .type = hwmon_pwm,
&sensor_dev_attr_pwm3_enable.dev_attr.attr, .config = max31790_pwm_config,
&sensor_dev_attr_pwm4_enable.dev_attr.attr, };
&sensor_dev_attr_pwm5_enable.dev_attr.attr,
&sensor_dev_attr_pwm6_enable.dev_attr.attr, static const struct hwmon_channel_info *max31790_info[] = {
&max31790_fan,
&max31790_pwm,
NULL NULL
}; };
static umode_t max31790_attrs_visible(struct kobject *kobj, static const struct hwmon_ops max31790_hwmon_ops = {
struct attribute *a, int n) .is_visible = max31790_is_visible,
{ .read = max31790_read,
struct device *dev = container_of(kobj, struct device, kobj); .write = max31790_write,
struct max31790_data *data = dev_get_drvdata(dev); };
struct device_attribute *devattr =
container_of(a, struct device_attribute, attr); static const struct hwmon_chip_info max31790_chip_info = {
int index = to_sensor_dev_attr(devattr)->index % NR_CHANNEL; .ops = &max31790_hwmon_ops,
u8 fan_config; .info = max31790_info,
fan_config = data->fan_config[index];
if (n >= NR_CHANNEL * 2 && n < NR_CHANNEL * 4 &&
!(fan_config & MAX31790_FAN_CFG_TACH_INPUT))
return 0;
if (n >= NR_CHANNEL * 4 && (fan_config & MAX31790_FAN_CFG_TACH_INPUT))
return 0;
return a->mode;
}
static const struct attribute_group max31790_group = {
.attrs = max31790_attrs,
.is_visible = max31790_attrs_visible,
}; };
__ATTRIBUTE_GROUPS(max31790);
static int max31790_init_client(struct i2c_client *client, static int max31790_init_client(struct i2c_client *client,
struct max31790_data *data) struct max31790_data *data)
@ -575,8 +500,10 @@ static int max31790_probe(struct i2c_client *client,
if (err) if (err)
return err; return err;
hwmon_dev = devm_hwmon_device_register_with_groups(dev, hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
client->name, data, max31790_groups); data,
&max31790_chip_info,
NULL);
return PTR_ERR_OR_ZERO(hwmon_dev); return PTR_ERR_OR_ZERO(hwmon_dev);
} }

View File

@ -39,6 +39,7 @@
#include <linux/hwmon.h> #include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h> #include <linux/hwmon-sysfs.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/of_device.h>
/* /*
* Insmod parameters * Insmod parameters
@ -48,7 +49,7 @@
static int fan_voltage; static int fan_voltage;
/* prescaler: Possible values are 1, 2, 4, 8, 16 or 0 for don't change */ /* prescaler: Possible values are 1, 2, 4, 8, 16 or 0 for don't change */
static int prescaler; static int prescaler;
/* clock: The clock frequency of the chip the driver should assume */ /* clock: The clock frequency of the chip (max6651 can be clocked externally) */
static int clock = 254000; static int clock = 254000;
module_param(fan_voltage, int, S_IRUGO); module_param(fan_voltage, int, S_IRUGO);
@ -133,6 +134,19 @@ static const u8 tach_reg[] = {
MAX6650_REG_TACH3, MAX6650_REG_TACH3,
}; };
static const struct of_device_id max6650_dt_match[] = {
{
.compatible = "maxim,max6650",
.data = (void *)1
},
{
.compatible = "maxim,max6651",
.data = (void *)4
},
{ },
};
MODULE_DEVICE_TABLE(of, max6650_dt_match);
static struct max6650_data *max6650_update_device(struct device *dev) static struct max6650_data *max6650_update_device(struct device *dev)
{ {
struct max6650_data *data = dev_get_drvdata(dev); struct max6650_data *data = dev_get_drvdata(dev);
@ -171,6 +185,30 @@ static struct max6650_data *max6650_update_device(struct device *dev)
return data; return data;
} }
/*
* Change the operating mode of the chip (if needed).
* mode is one of the MAX6650_CFG_MODE_* values.
*/
static int max6650_set_operating_mode(struct max6650_data *data, u8 mode)
{
int result;
u8 config = data->config;
if (mode == (config & MAX6650_CFG_MODE_MASK))
return 0;
config = (config & ~MAX6650_CFG_MODE_MASK) | mode;
result = i2c_smbus_write_byte_data(data->client, MAX6650_REG_CONFIG,
config);
if (result < 0)
return result;
data->config = config;
return 0;
}
static ssize_t get_fan(struct device *dev, struct device_attribute *devattr, static ssize_t get_fan(struct device *dev, struct device_attribute *devattr,
char *buf) char *buf)
{ {
@ -252,18 +290,12 @@ static ssize_t get_target(struct device *dev, struct device_attribute *devattr,
return sprintf(buf, "%d\n", rpm); return sprintf(buf, "%d\n", rpm);
} }
static ssize_t set_target(struct device *dev, struct device_attribute *devattr, static int max6650_set_target(struct max6650_data *data, unsigned long rpm)
const char *buf, size_t count)
{ {
struct max6650_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
int kscale, ktach; int kscale, ktach;
unsigned long rpm;
int err;
err = kstrtoul(buf, 10, &rpm); if (rpm == 0)
if (err) return max6650_set_operating_mode(data, MAX6650_CFG_MODE_OFF);
return err;
rpm = clamp_val(rpm, FAN_RPM_MIN, FAN_RPM_MAX); rpm = clamp_val(rpm, FAN_RPM_MIN, FAN_RPM_MAX);
@ -274,8 +306,6 @@ static ssize_t set_target(struct device *dev, struct device_attribute *devattr,
* KTACH = [(fCLK x KSCALE) / (256 x FanSpeed)] - 1 * KTACH = [(fCLK x KSCALE) / (256 x FanSpeed)] - 1
*/ */
mutex_lock(&data->update_lock);
kscale = DIV_FROM_REG(data->config); kscale = DIV_FROM_REG(data->config);
ktach = ((clock * kscale) / (256 * rpm / 60)) - 1; ktach = ((clock * kscale) / (256 * rpm / 60)) - 1;
if (ktach < 0) if (ktach < 0)
@ -284,10 +314,30 @@ static ssize_t set_target(struct device *dev, struct device_attribute *devattr,
ktach = 255; ktach = 255;
data->speed = ktach; data->speed = ktach;
i2c_smbus_write_byte_data(client, MAX6650_REG_SPEED, data->speed); return i2c_smbus_write_byte_data(data->client, MAX6650_REG_SPEED,
data->speed);
}
static ssize_t set_target(struct device *dev, struct device_attribute *devattr,
const char *buf, size_t count)
{
struct max6650_data *data = dev_get_drvdata(dev);
unsigned long rpm;
int err;
err = kstrtoul(buf, 10, &rpm);
if (err)
return err;
mutex_lock(&data->update_lock);
err = max6650_set_target(data, rpm);
mutex_unlock(&data->update_lock); mutex_unlock(&data->update_lock);
if (err < 0)
return err;
return count; return count;
} }
@ -341,12 +391,11 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr,
data->dac = 180 - (180 * pwm)/255; data->dac = 180 - (180 * pwm)/255;
else else
data->dac = 76 - (76 * pwm)/255; data->dac = 76 - (76 * pwm)/255;
err = i2c_smbus_write_byte_data(client, MAX6650_REG_DAC, data->dac);
i2c_smbus_write_byte_data(client, MAX6650_REG_DAC, data->dac);
mutex_unlock(&data->update_lock); mutex_unlock(&data->update_lock);
return count; return err < 0 ? err : count;
} }
/* /*
@ -355,14 +404,14 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr,
* 0 = Fan always on * 0 = Fan always on
* 1 = Open loop, Voltage is set according to speed, not regulated. * 1 = Open loop, Voltage is set according to speed, not regulated.
* 2 = Closed loop, RPM for all fans regulated by fan1 tachometer * 2 = Closed loop, RPM for all fans regulated by fan1 tachometer
* 3 = Fan off
*/ */
static ssize_t get_enable(struct device *dev, struct device_attribute *devattr, static ssize_t get_enable(struct device *dev, struct device_attribute *devattr,
char *buf) char *buf)
{ {
struct max6650_data *data = max6650_update_device(dev); struct max6650_data *data = max6650_update_device(dev);
int mode = (data->config & MAX6650_CFG_MODE_MASK) >> 4; int mode = (data->config & MAX6650_CFG_MODE_MASK) >> 4;
int sysfs_modes[4] = {0, 1, 2, 1}; int sysfs_modes[4] = {0, 3, 2, 1};
return sprintf(buf, "%d\n", sysfs_modes[mode]); return sprintf(buf, "%d\n", sysfs_modes[mode]);
} }
@ -371,25 +420,25 @@ static ssize_t set_enable(struct device *dev, struct device_attribute *devattr,
const char *buf, size_t count) const char *buf, size_t count)
{ {
struct max6650_data *data = dev_get_drvdata(dev); struct max6650_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
int max6650_modes[3] = {0, 3, 2};
unsigned long mode; unsigned long mode;
int err; int err;
const u8 max6650_modes[] = {
MAX6650_CFG_MODE_ON,
MAX6650_CFG_MODE_OPEN_LOOP,
MAX6650_CFG_MODE_CLOSED_LOOP,
MAX6650_CFG_MODE_OFF,
};
err = kstrtoul(buf, 10, &mode); err = kstrtoul(buf, 10, &mode);
if (err) if (err)
return err; return err;
if (mode > 2) if (mode >= ARRAY_SIZE(max6650_modes))
return -EINVAL; return -EINVAL;
mutex_lock(&data->update_lock); mutex_lock(&data->update_lock);
data->config = i2c_smbus_read_byte_data(client, MAX6650_REG_CONFIG); max6650_set_operating_mode(data, max6650_modes[mode]);
data->config = (data->config & ~MAX6650_CFG_MODE_MASK)
| (max6650_modes[mode] << 4);
i2c_smbus_write_byte_data(client, MAX6650_REG_CONFIG, data->config);
mutex_unlock(&data->update_lock); mutex_unlock(&data->update_lock);
@ -566,6 +615,18 @@ static int max6650_init_client(struct max6650_data *data,
struct device *dev = &client->dev; struct device *dev = &client->dev;
int config; int config;
int err = -EIO; int err = -EIO;
u32 voltage;
u32 prescale;
u32 target_rpm;
if (of_property_read_u32(dev->of_node, "maxim,fan-microvolt",
&voltage))
voltage = fan_voltage;
else
voltage /= 1000000; /* Microvolts to volts */
if (of_property_read_u32(dev->of_node, "maxim,fan-prescale",
&prescale))
prescale = prescaler;
config = i2c_smbus_read_byte_data(client, MAX6650_REG_CONFIG); config = i2c_smbus_read_byte_data(client, MAX6650_REG_CONFIG);
@ -574,7 +635,7 @@ static int max6650_init_client(struct max6650_data *data,
return err; return err;
} }
switch (fan_voltage) { switch (voltage) {
case 0: case 0:
break; break;
case 5: case 5:
@ -584,14 +645,10 @@ static int max6650_init_client(struct max6650_data *data,
config |= MAX6650_CFG_V12; config |= MAX6650_CFG_V12;
break; break;
default: default:
dev_err(dev, "illegal value for fan_voltage (%d)\n", dev_err(dev, "illegal value for fan_voltage (%d)\n", voltage);
fan_voltage);
} }
dev_info(dev, "Fan voltage is set to %dV.\n", switch (prescale) {
(config & MAX6650_CFG_V12) ? 12 : 5);
switch (prescaler) {
case 0: case 0:
break; break;
case 1: case 1:
@ -614,28 +671,13 @@ static int max6650_init_client(struct max6650_data *data,
| MAX6650_CFG_PRESCALER_16; | MAX6650_CFG_PRESCALER_16;
break; break;
default: default:
dev_err(dev, "illegal value for prescaler (%d)\n", prescaler); dev_err(dev, "illegal value for prescaler (%d)\n", prescale);
} }
dev_info(dev, "Prescaler is set to %d.\n", dev_info(dev, "Fan voltage: %dV, prescaler: %d.\n",
(config & MAX6650_CFG_V12) ? 12 : 5,
1 << (config & MAX6650_CFG_PRESCALER_MASK)); 1 << (config & MAX6650_CFG_PRESCALER_MASK));
/*
* If mode is set to "full off", we change it to "open loop" and
* set DAC to 255, which has the same effect. We do this because
* there's no "full off" mode defined in hwmon specifications.
*/
if ((config & MAX6650_CFG_MODE_MASK) == MAX6650_CFG_MODE_OFF) {
dev_dbg(dev, "Change mode to open loop, full off.\n");
config = (config & ~MAX6650_CFG_MODE_MASK)
| MAX6650_CFG_MODE_OPEN_LOOP;
if (i2c_smbus_write_byte_data(client, MAX6650_REG_DAC, 255)) {
dev_err(dev, "DAC write error, aborting.\n");
return err;
}
}
if (i2c_smbus_write_byte_data(client, MAX6650_REG_CONFIG, config)) { if (i2c_smbus_write_byte_data(client, MAX6650_REG_CONFIG, config)) {
dev_err(dev, "Config write error, aborting.\n"); dev_err(dev, "Config write error, aborting.\n");
return err; return err;
@ -644,6 +686,12 @@ static int max6650_init_client(struct max6650_data *data,
data->config = config; data->config = config;
data->count = i2c_smbus_read_byte_data(client, MAX6650_REG_COUNT); data->count = i2c_smbus_read_byte_data(client, MAX6650_REG_COUNT);
if (!of_property_read_u32(client->dev.of_node, "maxim,fan-target-rpm",
&target_rpm)) {
max6650_set_target(data, target_rpm);
max6650_set_operating_mode(data, MAX6650_CFG_MODE_CLOSED_LOOP);
}
return 0; return 0;
} }
@ -651,6 +699,8 @@ static int max6650_probe(struct i2c_client *client,
const struct i2c_device_id *id) const struct i2c_device_id *id)
{ {
struct device *dev = &client->dev; struct device *dev = &client->dev;
const struct of_device_id *of_id =
of_match_device(of_match_ptr(max6650_dt_match), dev);
struct max6650_data *data; struct max6650_data *data;
struct device *hwmon_dev; struct device *hwmon_dev;
int err; int err;
@ -661,7 +711,7 @@ static int max6650_probe(struct i2c_client *client,
data->client = client; data->client = client;
mutex_init(&data->update_lock); mutex_init(&data->update_lock);
data->nr_fans = id->driver_data; data->nr_fans = of_id ? (int)(uintptr_t)of_id->data : id->driver_data;
/* /*
* Initialize the max6650 chip * Initialize the max6650 chip
@ -691,6 +741,7 @@ MODULE_DEVICE_TABLE(i2c, max6650_id);
static struct i2c_driver max6650_driver = { static struct i2c_driver max6650_driver = {
.driver = { .driver = {
.name = "max6650", .name = "max6650",
.of_match_table = of_match_ptr(max6650_dt_match),
}, },
.probe = max6650_probe, .probe = max6650_probe,
.id_table = max6650_id, .id_table = max6650_id,

View File

@ -195,6 +195,8 @@ superio_exit(int ioreg)
#define NUM_FAN 6 #define NUM_FAN 6
#define TEMP_SOURCE_VIRTUAL 0x1f
/* Common and NCT6775 specific data */ /* Common and NCT6775 specific data */
/* Voltage min/max registers for nr=7..14 are in bank 5 */ /* 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; continue;
src = nct6775_read_value(data, data->REG_TEMP_SEL[i]) & 0x1f; src = nct6775_read_value(data, data->REG_TEMP_SEL[i]) & 0x1f;
if (!src || (mask & (1 << src))) if (!src)
continue; continue;
if (src >= data->temp_label_num || if (src >= data->temp_label_num ||
@ -3952,7 +3954,16 @@ static int nct6775_probe(struct platform_device *pdev)
continue; 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) */ /* Use fixed index for SYSTIN(1), CPUTIN(2), AUXTIN(3) */
if (src <= data->temp_fixed_num) { 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) if (err)
return 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; val = force_id;
else
val = (superio_inb(sioaddr, SIO_REG_DEVID) << 8)
| superio_inb(sioaddr, SIO_REG_DEVID + 1);
switch (val & SIO_ID_MASK) { switch (val & SIO_ID_MASK) {
case SIO_NCT6106_ID: case SIO_NCT6106_ID:
sio_data->kind = nct6106; sio_data->kind = nct6106;

View File

@ -21,7 +21,6 @@
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/hwmon.h> #include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#define VENDOR_ID_REG 0x7A /* Any bank */ #define VENDOR_ID_REG 0x7A /* Any bank */
#define NUVOTON_ID 0x50 #define NUVOTON_ID 0x50
@ -153,341 +152,230 @@ static int nct7904_write_reg(struct nct7904_data *data,
return ret; return ret;
} }
/* FANIN ATTR */ static int nct7904_read_fan(struct device *dev, u32 attr, int channel,
static ssize_t show_fan(struct device *dev, long *val)
struct device_attribute *devattr, char *buf)
{ {
int index = to_sensor_dev_attr(devattr)->index;
struct nct7904_data *data = dev_get_drvdata(dev); struct nct7904_data *data = dev_get_drvdata(dev);
unsigned int cnt, rpm;
int ret; int ret;
unsigned cnt, rpm;
ret = nct7904_read_reg16(data, BANK_0, FANIN1_HV_REG + index * 2); switch(attr) {
if (ret < 0) case hwmon_fan_input:
return ret; ret = nct7904_read_reg16(data, BANK_0,
cnt = ((ret & 0xff00) >> 3) | (ret & 0x1f); FANIN1_HV_REG + channel * 2);
if (cnt == 0x1fff) if (ret < 0)
rpm = 0; return ret;
else cnt = ((ret & 0xff00) >> 3) | (ret & 0x1f);
rpm = 1350000 / cnt; if (cnt == 0x1fff)
return sprintf(buf, "%u\n", rpm); rpm = 0;
else
rpm = 1350000 / cnt;
*val = rpm;
return 0;
default:
return -EOPNOTSUPP;
}
} }
static umode_t nct7904_fanin_is_visible(struct kobject *kobj, static umode_t nct7904_fan_is_visible(const void *_data, u32 attr, int channel)
struct attribute *a, int n)
{ {
struct device *dev = container_of(kobj, struct device, kobj); const struct nct7904_data *data = _data;
struct nct7904_data *data = dev_get_drvdata(dev);
if (data->fanin_mask & (1 << n)) if (attr == hwmon_fan_input && data->fanin_mask & (1 << channel))
return a->mode; return S_IRUGO;
return 0; return 0;
} }
static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0); static u8 nct7904_chan_to_index[] = {
static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1); 0, /* Not used */
static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2); 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
static SENSOR_DEVICE_ATTR(fan4_input, S_IRUGO, show_fan, NULL, 3); 18, 19, 20, 16
static SENSOR_DEVICE_ATTR(fan5_input, S_IRUGO, show_fan, NULL, 4);
static SENSOR_DEVICE_ATTR(fan6_input, S_IRUGO, show_fan, NULL, 5);
static SENSOR_DEVICE_ATTR(fan7_input, S_IRUGO, show_fan, NULL, 6);
static SENSOR_DEVICE_ATTR(fan8_input, S_IRUGO, show_fan, NULL, 7);
static SENSOR_DEVICE_ATTR(fan9_input, S_IRUGO, show_fan, NULL, 8);
static SENSOR_DEVICE_ATTR(fan10_input, S_IRUGO, show_fan, NULL, 9);
static SENSOR_DEVICE_ATTR(fan11_input, S_IRUGO, show_fan, NULL, 10);
static SENSOR_DEVICE_ATTR(fan12_input, S_IRUGO, show_fan, NULL, 11);
static struct attribute *nct7904_fanin_attrs[] = {
&sensor_dev_attr_fan1_input.dev_attr.attr,
&sensor_dev_attr_fan2_input.dev_attr.attr,
&sensor_dev_attr_fan3_input.dev_attr.attr,
&sensor_dev_attr_fan4_input.dev_attr.attr,
&sensor_dev_attr_fan5_input.dev_attr.attr,
&sensor_dev_attr_fan6_input.dev_attr.attr,
&sensor_dev_attr_fan7_input.dev_attr.attr,
&sensor_dev_attr_fan8_input.dev_attr.attr,
&sensor_dev_attr_fan9_input.dev_attr.attr,
&sensor_dev_attr_fan10_input.dev_attr.attr,
&sensor_dev_attr_fan11_input.dev_attr.attr,
&sensor_dev_attr_fan12_input.dev_attr.attr,
NULL
}; };
static const struct attribute_group nct7904_fanin_group = { static int nct7904_read_in(struct device *dev, u32 attr, int channel,
.attrs = nct7904_fanin_attrs, long *val)
.is_visible = nct7904_fanin_is_visible,
};
/* VSEN ATTR */
static ssize_t show_voltage(struct device *dev,
struct device_attribute *devattr, char *buf)
{ {
int index = to_sensor_dev_attr(devattr)->index;
struct nct7904_data *data = dev_get_drvdata(dev); struct nct7904_data *data = dev_get_drvdata(dev);
int ret; int ret, volt, index;
int volt;
ret = nct7904_read_reg16(data, BANK_0, VSEN1_HV_REG + index * 2); index = nct7904_chan_to_index[channel];
if (ret < 0)
return ret;
volt = ((ret & 0xff00) >> 5) | (ret & 0x7);
if (index < 14)
volt *= 2; /* 0.002V scale */
else
volt *= 6; /* 0.006V scale */
return sprintf(buf, "%d\n", volt); switch(attr) {
case hwmon_in_input:
ret = nct7904_read_reg16(data, BANK_0,
VSEN1_HV_REG + index * 2);
if (ret < 0)
return ret;
volt = ((ret & 0xff00) >> 5) | (ret & 0x7);
if (index < 14)
volt *= 2; /* 0.002V scale */
else
volt *= 6; /* 0.006V scale */
*val = volt;
return 0;
default:
return -EOPNOTSUPP;
}
} }
static ssize_t show_ltemp(struct device *dev, static umode_t nct7904_in_is_visible(const void *_data, u32 attr, int channel)
struct device_attribute *devattr, char *buf)
{ {
struct nct7904_data *data = dev_get_drvdata(dev); const struct nct7904_data *data = _data;
int ret; int index = nct7904_chan_to_index[channel];
int temp;
ret = nct7904_read_reg16(data, BANK_0, LTD_HV_REG); if (channel > 0 && attr == hwmon_in_input &&
if (ret < 0) (data->vsen_mask & BIT(index)))
return ret; return S_IRUGO;
temp = ((ret & 0xff00) >> 5) | (ret & 0x7);
temp = sign_extend32(temp, 10) * 125;
return sprintf(buf, "%d\n", temp);
}
static umode_t nct7904_vsen_is_visible(struct kobject *kobj,
struct attribute *a, int n)
{
struct device *dev = container_of(kobj, struct device, kobj);
struct nct7904_data *data = dev_get_drvdata(dev);
if (data->vsen_mask & (1 << n))
return a->mode;
return 0; return 0;
} }
static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_voltage, NULL, 0); static int nct7904_read_temp(struct device *dev, u32 attr, int channel,
static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_voltage, NULL, 1); long *val)
static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, show_voltage, NULL, 2);
static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, show_voltage, NULL, 3);
static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, show_voltage, NULL, 4);
static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, show_voltage, NULL, 5);
static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, show_voltage, NULL, 6);
static SENSOR_DEVICE_ATTR(in8_input, S_IRUGO, show_voltage, NULL, 7);
static SENSOR_DEVICE_ATTR(in9_input, S_IRUGO, show_voltage, NULL, 8);
static SENSOR_DEVICE_ATTR(in10_input, S_IRUGO, show_voltage, NULL, 9);
static SENSOR_DEVICE_ATTR(in11_input, S_IRUGO, show_voltage, NULL, 10);
static SENSOR_DEVICE_ATTR(in12_input, S_IRUGO, show_voltage, NULL, 11);
static SENSOR_DEVICE_ATTR(in13_input, S_IRUGO, show_voltage, NULL, 12);
static SENSOR_DEVICE_ATTR(in14_input, S_IRUGO, show_voltage, NULL, 13);
/*
* Next 3 voltage sensors have specific names in the Nuvoton doc
* (3VDD, VBAT, 3VSB) but we use vacant numbers for them.
*/
static SENSOR_DEVICE_ATTR(in15_input, S_IRUGO, show_voltage, NULL, 14);
static SENSOR_DEVICE_ATTR(in16_input, S_IRUGO, show_voltage, NULL, 15);
static SENSOR_DEVICE_ATTR(in20_input, S_IRUGO, show_voltage, NULL, 16);
/* This is not a voltage, but a local temperature sensor. */
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_ltemp, NULL, 0);
static SENSOR_DEVICE_ATTR(in17_input, S_IRUGO, show_voltage, NULL, 18);
static SENSOR_DEVICE_ATTR(in18_input, S_IRUGO, show_voltage, NULL, 19);
static SENSOR_DEVICE_ATTR(in19_input, S_IRUGO, show_voltage, NULL, 20);
static struct attribute *nct7904_vsen_attrs[] = {
&sensor_dev_attr_in1_input.dev_attr.attr,
&sensor_dev_attr_in2_input.dev_attr.attr,
&sensor_dev_attr_in3_input.dev_attr.attr,
&sensor_dev_attr_in4_input.dev_attr.attr,
&sensor_dev_attr_in5_input.dev_attr.attr,
&sensor_dev_attr_in6_input.dev_attr.attr,
&sensor_dev_attr_in7_input.dev_attr.attr,
&sensor_dev_attr_in8_input.dev_attr.attr,
&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,
&sensor_dev_attr_in13_input.dev_attr.attr,
&sensor_dev_attr_in14_input.dev_attr.attr,
&sensor_dev_attr_in15_input.dev_attr.attr,
&sensor_dev_attr_in16_input.dev_attr.attr,
&sensor_dev_attr_in20_input.dev_attr.attr,
&sensor_dev_attr_temp1_input.dev_attr.attr,
&sensor_dev_attr_in17_input.dev_attr.attr,
&sensor_dev_attr_in18_input.dev_attr.attr,
&sensor_dev_attr_in19_input.dev_attr.attr,
NULL
};
static const struct attribute_group nct7904_vsen_group = {
.attrs = nct7904_vsen_attrs,
.is_visible = nct7904_vsen_is_visible,
};
/* CPU_TEMP ATTR */
static ssize_t show_tcpu(struct device *dev,
struct device_attribute *devattr, char *buf)
{ {
int index = to_sensor_dev_attr(devattr)->index;
struct nct7904_data *data = dev_get_drvdata(dev); struct nct7904_data *data = dev_get_drvdata(dev);
int ret; int ret, temp;
int temp;
ret = nct7904_read_reg16(data, BANK_0, T_CPU1_HV_REG + index * 2); switch(attr) {
if (ret < 0) case hwmon_temp_input:
return ret; if (channel == 0)
ret = nct7904_read_reg16(data, BANK_0, LTD_HV_REG);
temp = ((ret & 0xff00) >> 5) | (ret & 0x7); else
temp = sign_extend32(temp, 10) * 125; ret = nct7904_read_reg16(data, BANK_0,
return sprintf(buf, "%d\n", temp); T_CPU1_HV_REG + (channel - 1) * 2);
if (ret < 0)
return ret;
temp = ((ret & 0xff00) >> 5) | (ret & 0x7);
*val = sign_extend32(temp, 10) * 125;
return 0;
default:
return -EOPNOTSUPP;
}
} }
static umode_t nct7904_tcpu_is_visible(struct kobject *kobj, static umode_t nct7904_temp_is_visible(const void *_data, u32 attr, int channel)
struct attribute *a, int n)
{ {
struct device *dev = container_of(kobj, struct device, kobj); const struct nct7904_data *data = _data;
struct nct7904_data *data = dev_get_drvdata(dev);
if (attr == hwmon_temp_input) {
if (channel == 0) {
if (data->vsen_mask & BIT(17))
return S_IRUGO;
} else {
if (data->tcpu_mask & BIT(channel - 1))
return S_IRUGO;
}
}
if (data->tcpu_mask & (1 << n))
return a->mode;
return 0; return 0;
} }
/* "temp1_input" reserved for local temp */ static int nct7904_read_pwm(struct device *dev, u32 attr, int channel,
static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_tcpu, NULL, 0); long *val)
static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_tcpu, NULL, 1);
static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_tcpu, NULL, 2);
static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO, show_tcpu, NULL, 3);
static SENSOR_DEVICE_ATTR(temp6_input, S_IRUGO, show_tcpu, NULL, 4);
static SENSOR_DEVICE_ATTR(temp7_input, S_IRUGO, show_tcpu, NULL, 5);
static SENSOR_DEVICE_ATTR(temp8_input, S_IRUGO, show_tcpu, NULL, 6);
static SENSOR_DEVICE_ATTR(temp9_input, S_IRUGO, show_tcpu, NULL, 7);
static struct attribute *nct7904_tcpu_attrs[] = {
&sensor_dev_attr_temp2_input.dev_attr.attr,
&sensor_dev_attr_temp3_input.dev_attr.attr,
&sensor_dev_attr_temp4_input.dev_attr.attr,
&sensor_dev_attr_temp5_input.dev_attr.attr,
&sensor_dev_attr_temp6_input.dev_attr.attr,
&sensor_dev_attr_temp7_input.dev_attr.attr,
&sensor_dev_attr_temp8_input.dev_attr.attr,
&sensor_dev_attr_temp9_input.dev_attr.attr,
NULL
};
static const struct attribute_group nct7904_tcpu_group = {
.attrs = nct7904_tcpu_attrs,
.is_visible = nct7904_tcpu_is_visible,
};
/* PWM ATTR */
static ssize_t store_pwm(struct device *dev, struct device_attribute *devattr,
const char *buf, size_t count)
{ {
int index = to_sensor_dev_attr(devattr)->index;
struct nct7904_data *data = dev_get_drvdata(dev); struct nct7904_data *data = dev_get_drvdata(dev);
unsigned long val;
int ret; int ret;
if (kstrtoul(buf, 10, &val) < 0) switch(attr) {
return -EINVAL; case hwmon_pwm_input:
if (val > 255) ret = nct7904_read_reg(data, BANK_3, FANCTL1_OUT_REG + channel);
return -EINVAL; if (ret < 0)
return ret;
*val = ret;
return 0;
case hwmon_pwm_enable:
ret = nct7904_read_reg(data, BANK_3, FANCTL1_FMR_REG + channel);
if (ret < 0)
return ret;
ret = nct7904_write_reg(data, BANK_3, FANCTL1_OUT_REG + index, val); *val = ret ? 2 : 1;
return 0;
return ret ? ret : count; default:
return -EOPNOTSUPP;
}
} }
static ssize_t show_pwm(struct device *dev, static int nct7904_write_pwm(struct device *dev, u32 attr, int channel,
struct device_attribute *devattr, char *buf) long val)
{ {
int index = to_sensor_dev_attr(devattr)->index;
struct nct7904_data *data = dev_get_drvdata(dev); struct nct7904_data *data = dev_get_drvdata(dev);
int val;
val = nct7904_read_reg(data, BANK_3, FANCTL1_OUT_REG + index);
if (val < 0)
return val;
return sprintf(buf, "%d\n", val);
}
static ssize_t store_enable(struct device *dev,
struct device_attribute *devattr,
const char *buf, size_t count)
{
int index = to_sensor_dev_attr(devattr)->index;
struct nct7904_data *data = dev_get_drvdata(dev);
unsigned long val;
int ret; int ret;
if (kstrtoul(buf, 10, &val) < 0) switch(attr) {
return -EINVAL; case hwmon_pwm_input:
if (val < 1 || val > 2 || (val == 2 && !data->fan_mode[index])) if (val < 0 || val > 255)
return -EINVAL; return -EINVAL;
ret = nct7904_write_reg(data, BANK_3, FANCTL1_OUT_REG + channel,
ret = nct7904_write_reg(data, BANK_3, FANCTL1_FMR_REG + index, val);
val == 2 ? data->fan_mode[index] : 0); return ret;
case hwmon_pwm_enable:
return ret ? ret : count; if (val < 1 || val > 2 ||
(val == 2 && !data->fan_mode[channel]))
return -EINVAL;
ret = nct7904_write_reg(data, BANK_3, FANCTL1_FMR_REG + channel,
val == 2 ? data->fan_mode[channel] : 0);
return ret;
default:
return -EOPNOTSUPP;
}
} }
/* Return 1 for manual mode or 2 for SmartFan mode */ static umode_t nct7904_pwm_is_visible(const void *_data, u32 attr, int channel)
static ssize_t show_enable(struct device *dev,
struct device_attribute *devattr, char *buf)
{ {
int index = to_sensor_dev_attr(devattr)->index; switch(attr) {
struct nct7904_data *data = dev_get_drvdata(dev); case hwmon_pwm_input:
int val; case hwmon_pwm_enable:
return S_IRUGO | S_IWUSR;
val = nct7904_read_reg(data, BANK_3, FANCTL1_FMR_REG + index); default:
if (val < 0) return 0;
return val; }
return sprintf(buf, "%d\n", val ? 2 : 1);
} }
/* 2 attributes per channel: pwm and mode */ static int nct7904_read(struct device *dev, enum hwmon_sensor_types type,
static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, u32 attr, int channel, long *val)
show_pwm, store_pwm, 0); {
static SENSOR_DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, switch (type) {
show_enable, store_enable, 0); case hwmon_in:
static SENSOR_DEVICE_ATTR(pwm2, S_IRUGO | S_IWUSR, return nct7904_read_in(dev, attr, channel, val);
show_pwm, store_pwm, 1); case hwmon_fan:
static SENSOR_DEVICE_ATTR(pwm2_enable, S_IRUGO | S_IWUSR, return nct7904_read_fan(dev, attr, channel, val);
show_enable, store_enable, 1); case hwmon_pwm:
static SENSOR_DEVICE_ATTR(pwm3, S_IRUGO | S_IWUSR, return nct7904_read_pwm(dev, attr, channel, val);
show_pwm, store_pwm, 2); case hwmon_temp:
static SENSOR_DEVICE_ATTR(pwm3_enable, S_IRUGO | S_IWUSR, return nct7904_read_temp(dev, attr, channel, val);
show_enable, store_enable, 2); default:
static SENSOR_DEVICE_ATTR(pwm4, S_IRUGO | S_IWUSR, return -EOPNOTSUPP;
show_pwm, store_pwm, 3); }
static SENSOR_DEVICE_ATTR(pwm4_enable, S_IRUGO | S_IWUSR, }
show_enable, store_enable, 3);
static struct attribute *nct7904_fanctl_attrs[] = { static int nct7904_write(struct device *dev, enum hwmon_sensor_types type,
&sensor_dev_attr_pwm1.dev_attr.attr, u32 attr, int channel, long val)
&sensor_dev_attr_pwm1_enable.dev_attr.attr, {
&sensor_dev_attr_pwm2.dev_attr.attr, switch (type) {
&sensor_dev_attr_pwm2_enable.dev_attr.attr, case hwmon_pwm:
&sensor_dev_attr_pwm3.dev_attr.attr, return nct7904_write_pwm(dev, attr, channel, val);
&sensor_dev_attr_pwm3_enable.dev_attr.attr, default:
&sensor_dev_attr_pwm4.dev_attr.attr, return -EOPNOTSUPP;
&sensor_dev_attr_pwm4_enable.dev_attr.attr, }
NULL }
};
static const struct attribute_group nct7904_fanctl_group = { static umode_t nct7904_is_visible(const void *data,
.attrs = nct7904_fanctl_attrs, enum hwmon_sensor_types type,
}; u32 attr, int channel)
{
static const struct attribute_group *nct7904_groups[] = { switch (type) {
&nct7904_fanin_group, case hwmon_in:
&nct7904_vsen_group, return nct7904_in_is_visible(data, attr, channel);
&nct7904_tcpu_group, case hwmon_fan:
&nct7904_fanctl_group, return nct7904_fan_is_visible(data, attr, channel);
NULL case hwmon_pwm:
}; return nct7904_pwm_is_visible(data, attr, channel);
case hwmon_temp:
return nct7904_temp_is_visible(data, attr, channel);
default:
return 0;
}
}
/* Return 0 if detection is successful, -ENODEV otherwise */ /* Return 0 if detection is successful, -ENODEV otherwise */
static int nct7904_detect(struct i2c_client *client, static int nct7904_detect(struct i2c_client *client,
@ -512,6 +400,103 @@ static int nct7904_detect(struct i2c_client *client,
return 0; return 0;
} }
static const u32 nct7904_in_config[] = {
HWMON_I_INPUT, /* dummy, skipped in is_visible */
HWMON_I_INPUT,
HWMON_I_INPUT,
HWMON_I_INPUT,
HWMON_I_INPUT,
HWMON_I_INPUT,
HWMON_I_INPUT,
HWMON_I_INPUT,
HWMON_I_INPUT,
HWMON_I_INPUT,
HWMON_I_INPUT,
HWMON_I_INPUT,
HWMON_I_INPUT,
HWMON_I_INPUT,
HWMON_I_INPUT,
HWMON_I_INPUT,
HWMON_I_INPUT,
HWMON_I_INPUT,
HWMON_I_INPUT,
HWMON_I_INPUT,
HWMON_I_INPUT,
0
};
static const struct hwmon_channel_info nct7904_in = {
.type = hwmon_in,
.config = nct7904_in_config,
};
static const u32 nct7904_fan_config[] = {
HWMON_F_INPUT,
HWMON_F_INPUT,
HWMON_F_INPUT,
HWMON_F_INPUT,
HWMON_F_INPUT,
HWMON_F_INPUT,
HWMON_F_INPUT,
HWMON_F_INPUT,
0
};
static const struct hwmon_channel_info nct7904_fan = {
.type = hwmon_fan,
.config = nct7904_fan_config,
};
static const u32 nct7904_pwm_config[] = {
HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
0
};
static const struct hwmon_channel_info nct7904_pwm = {
.type = hwmon_pwm,
.config = nct7904_pwm_config,
};
static const u32 nct7904_temp_config[] = {
HWMON_T_INPUT,
HWMON_T_INPUT,
HWMON_T_INPUT,
HWMON_T_INPUT,
HWMON_T_INPUT,
HWMON_T_INPUT,
HWMON_T_INPUT,
HWMON_T_INPUT,
HWMON_T_INPUT,
0
};
static const struct hwmon_channel_info nct7904_temp = {
.type = hwmon_temp,
.config = nct7904_temp_config,
};
static const struct hwmon_channel_info *nct7904_info[] = {
&nct7904_in,
&nct7904_fan,
&nct7904_pwm,
&nct7904_temp,
NULL
};
static const struct hwmon_ops nct7904_hwmon_ops = {
.is_visible = nct7904_is_visible,
.read = nct7904_read,
.write = nct7904_write,
};
static const struct hwmon_chip_info nct7904_chip_info = {
.ops = &nct7904_hwmon_ops,
.info = nct7904_info,
};
static int nct7904_probe(struct i2c_client *client, static int nct7904_probe(struct i2c_client *client,
const struct i2c_device_id *id) const struct i2c_device_id *id)
{ {
@ -566,8 +551,8 @@ static int nct7904_probe(struct i2c_client *client,
} }
hwmon_dev = hwmon_dev =
devm_hwmon_device_register_with_groups(dev, client->name, data, devm_hwmon_device_register_with_info(dev, client->name, data,
nct7904_groups); &nct7904_chip_info, NULL);
return PTR_ERR_OR_ZERO(hwmon_dev); return PTR_ERR_OR_ZERO(hwmon_dev);
} }

View File

@ -253,12 +253,9 @@ static const struct ntc_compensation b57330v2103[] = {
}; };
struct ntc_data { struct ntc_data {
struct device *hwmon_dev;
struct ntc_thermistor_platform_data *pdata; struct ntc_thermistor_platform_data *pdata;
const struct ntc_compensation *comp; const struct ntc_compensation *comp;
struct device *dev;
int n_comp; int n_comp;
char name[PLATFORM_NAME_SIZE];
}; };
#if defined(CONFIG_OF) && IS_ENABLED(CONFIG_IIO) #if defined(CONFIG_OF) && IS_ENABLED(CONFIG_IIO)
@ -316,22 +313,22 @@ static const struct of_device_id ntc_match[] = {
MODULE_DEVICE_TABLE(of, ntc_match); MODULE_DEVICE_TABLE(of, ntc_match);
static struct ntc_thermistor_platform_data * static struct ntc_thermistor_platform_data *
ntc_thermistor_parse_dt(struct platform_device *pdev) ntc_thermistor_parse_dt(struct device *dev)
{ {
struct iio_channel *chan; struct iio_channel *chan;
enum iio_chan_type type; enum iio_chan_type type;
struct device_node *np = pdev->dev.of_node; struct device_node *np = dev->of_node;
struct ntc_thermistor_platform_data *pdata; struct ntc_thermistor_platform_data *pdata;
int ret; int ret;
if (!np) if (!np)
return NULL; return NULL;
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata) if (!pdata)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
chan = iio_channel_get(&pdev->dev, NULL); chan = devm_iio_channel_get(dev, NULL);
if (IS_ERR(chan)) if (IS_ERR(chan))
return ERR_CAST(chan); return ERR_CAST(chan);
@ -359,22 +356,15 @@ ntc_thermistor_parse_dt(struct platform_device *pdev)
return pdata; return pdata;
} }
static void ntc_iio_channel_release(struct ntc_thermistor_platform_data *pdata)
{
if (pdata->chan)
iio_channel_release(pdata->chan);
}
#else #else
static struct ntc_thermistor_platform_data * static struct ntc_thermistor_platform_data *
ntc_thermistor_parse_dt(struct platform_device *pdev) ntc_thermistor_parse_dt(struct device *dev)
{ {
return NULL; return NULL;
} }
#define ntc_match NULL #define ntc_match NULL
static void ntc_iio_channel_release(struct ntc_thermistor_platform_data *pdata)
{ }
#endif #endif
static inline u64 div64_u64_safe(u64 dividend, u64 divisor) static inline u64 div64_u64_safe(u64 dividend, u64 divisor)
@ -516,9 +506,8 @@ static int ntc_thermistor_get_ohm(struct ntc_data *data)
return -EINVAL; return -EINVAL;
} }
static int ntc_read_temp(void *dev, int *temp) static int ntc_read_temp(void *data, int *temp)
{ {
struct ntc_data *data = dev_get_drvdata(dev);
int ohm; int ohm;
ohm = ntc_thermistor_get_ohm(data); ohm = ntc_thermistor_get_ohm(data);
@ -530,14 +519,6 @@ static int ntc_read_temp(void *dev, int *temp)
return 0; return 0;
} }
static ssize_t ntc_show_name(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ntc_data *data = dev_get_drvdata(dev);
return sprintf(buf, "%s\n", data->name);
}
static ssize_t ntc_show_type(struct device *dev, static ssize_t ntc_show_type(struct device *dev,
struct device_attribute *attr, char *buf) struct device_attribute *attr, char *buf)
{ {
@ -559,18 +540,13 @@ static ssize_t ntc_show_temp(struct device *dev,
static SENSOR_DEVICE_ATTR(temp1_type, S_IRUGO, ntc_show_type, NULL, 0); static SENSOR_DEVICE_ATTR(temp1_type, S_IRUGO, ntc_show_type, NULL, 0);
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, ntc_show_temp, NULL, 0); static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, ntc_show_temp, NULL, 0);
static DEVICE_ATTR(name, S_IRUGO, ntc_show_name, NULL);
static struct attribute *ntc_attributes[] = { static struct attribute *ntc_attrs[] = {
&dev_attr_name.attr,
&sensor_dev_attr_temp1_type.dev_attr.attr, &sensor_dev_attr_temp1_type.dev_attr.attr,
&sensor_dev_attr_temp1_input.dev_attr.attr, &sensor_dev_attr_temp1_input.dev_attr.attr,
NULL, NULL,
}; };
ATTRIBUTE_GROUPS(ntc);
static const struct attribute_group ntc_attr_group = {
.attrs = ntc_attributes,
};
static const struct thermal_zone_of_device_ops ntc_of_thermal_ops = { static const struct thermal_zone_of_device_ops ntc_of_thermal_ops = {
.get_temp = ntc_read_temp, .get_temp = ntc_read_temp,
@ -579,33 +555,34 @@ static const struct thermal_zone_of_device_ops ntc_of_thermal_ops = {
static int ntc_thermistor_probe(struct platform_device *pdev) static int ntc_thermistor_probe(struct platform_device *pdev)
{ {
struct thermal_zone_device *tz; struct thermal_zone_device *tz;
struct device *dev = &pdev->dev;
const struct of_device_id *of_id = const struct of_device_id *of_id =
of_match_device(of_match_ptr(ntc_match), &pdev->dev); of_match_device(of_match_ptr(ntc_match), dev);
const struct platform_device_id *pdev_id; const struct platform_device_id *pdev_id;
struct ntc_thermistor_platform_data *pdata; struct ntc_thermistor_platform_data *pdata;
struct device *hwmon_dev;
struct ntc_data *data; struct ntc_data *data;
int ret;
pdata = ntc_thermistor_parse_dt(pdev); pdata = ntc_thermistor_parse_dt(dev);
if (IS_ERR(pdata)) if (IS_ERR(pdata))
return PTR_ERR(pdata); return PTR_ERR(pdata);
else if (pdata == NULL) else if (pdata == NULL)
pdata = dev_get_platdata(&pdev->dev); pdata = dev_get_platdata(dev);
if (!pdata) { if (!pdata) {
dev_err(&pdev->dev, "No platform init data supplied.\n"); dev_err(dev, "No platform init data supplied.\n");
return -ENODEV; return -ENODEV;
} }
/* Either one of the two is required. */ /* Either one of the two is required. */
if (!pdata->read_uv && !pdata->read_ohm) { if (!pdata->read_uv && !pdata->read_ohm) {
dev_err(&pdev->dev, dev_err(dev,
"Both read_uv and read_ohm missing. Need either one of the two.\n"); "Both read_uv and read_ohm missing. Need either one of the two.\n");
return -EINVAL; return -EINVAL;
} }
if (pdata->read_uv && pdata->read_ohm) { if (pdata->read_uv && pdata->read_ohm) {
dev_warn(&pdev->dev, dev_warn(dev,
"Only one of read_uv and read_ohm is needed; ignoring read_uv.\n"); "Only one of read_uv and read_ohm is needed; ignoring read_uv.\n");
pdata->read_uv = NULL; pdata->read_uv = NULL;
} }
@ -617,20 +594,17 @@ static int ntc_thermistor_probe(struct platform_device *pdev)
NTC_CONNECTED_POSITIVE) || NTC_CONNECTED_POSITIVE) ||
(pdata->connect != NTC_CONNECTED_POSITIVE && (pdata->connect != NTC_CONNECTED_POSITIVE &&
pdata->connect != NTC_CONNECTED_GROUND))) { pdata->connect != NTC_CONNECTED_GROUND))) {
dev_err(&pdev->dev, dev_err(dev, "Required data to use read_uv not supplied.\n");
"Required data to use read_uv not supplied.\n");
return -EINVAL; return -EINVAL;
} }
data = devm_kzalloc(&pdev->dev, sizeof(struct ntc_data), GFP_KERNEL); data = devm_kzalloc(dev, sizeof(struct ntc_data), GFP_KERNEL);
if (!data) if (!data)
return -ENOMEM; return -ENOMEM;
pdev_id = of_id ? of_id->data : platform_get_device_id(pdev); pdev_id = of_id ? of_id->data : platform_get_device_id(pdev);
data->dev = &pdev->dev;
data->pdata = pdata; data->pdata = pdata;
strlcpy(data->name, pdev_id->name, sizeof(data->name));
switch (pdev_id->driver_data) { switch (pdev_id->driver_data) {
case TYPE_NCPXXWB473: case TYPE_NCPXXWB473:
@ -654,49 +628,25 @@ static int ntc_thermistor_probe(struct platform_device *pdev)
data->n_comp = ARRAY_SIZE(ncpXXxh103); data->n_comp = ARRAY_SIZE(ncpXXxh103);
break; break;
default: default:
dev_err(&pdev->dev, "Unknown device type: %lu(%s)\n", dev_err(dev, "Unknown device type: %lu(%s)\n",
pdev_id->driver_data, pdev_id->name); pdev_id->driver_data, pdev_id->name);
return -EINVAL; return -EINVAL;
} }
platform_set_drvdata(pdev, data); hwmon_dev = devm_hwmon_device_register_with_groups(dev, pdev_id->name,
data, ntc_groups);
ret = sysfs_create_group(&data->dev->kobj, &ntc_attr_group); if (IS_ERR(hwmon_dev)) {
if (ret) { dev_err(dev, "unable to register as hwmon device.\n");
dev_err(data->dev, "unable to create sysfs files\n"); return PTR_ERR(hwmon_dev);
return ret;
} }
data->hwmon_dev = hwmon_device_register(data->dev); dev_info(dev, "Thermistor type: %s successfully probed.\n",
if (IS_ERR(data->hwmon_dev)) { pdev_id->name);
dev_err(data->dev, "unable to register as hwmon device.\n");
ret = PTR_ERR(data->hwmon_dev);
goto err_after_sysfs;
}
dev_info(&pdev->dev, "Thermistor type: %s successfully probed.\n", tz = devm_thermal_zone_of_sensor_register(dev, 0, data,
pdev_id->name);
tz = devm_thermal_zone_of_sensor_register(data->dev, 0, data->dev,
&ntc_of_thermal_ops); &ntc_of_thermal_ops);
if (IS_ERR(tz)) if (IS_ERR(tz))
dev_dbg(&pdev->dev, "Failed to register to thermal fw.\n"); dev_dbg(dev, "Failed to register to thermal fw.\n");
return 0;
err_after_sysfs:
sysfs_remove_group(&data->dev->kobj, &ntc_attr_group);
ntc_iio_channel_release(pdata);
return ret;
}
static int ntc_thermistor_remove(struct platform_device *pdev)
{
struct ntc_data *data = platform_get_drvdata(pdev);
struct ntc_thermistor_platform_data *pdata = data->pdata;
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&data->dev->kobj, &ntc_attr_group);
ntc_iio_channel_release(pdata);
return 0; return 0;
} }
@ -707,7 +657,6 @@ static struct platform_driver ntc_thermistor_driver = {
.of_match_table = of_match_ptr(ntc_match), .of_match_table = of_match_ptr(ntc_match),
}, },
.probe = ntc_thermistor_probe, .probe = ntc_thermistor_probe,
.remove = ntc_thermistor_remove,
.id_table = ntc_thermistor_id, .id_table = ntc_thermistor_id,
}; };

View File

@ -126,12 +126,12 @@ config SENSORS_TPS40422
be called tps40422. be called tps40422.
config SENSORS_UCD9000 config SENSORS_UCD9000
tristate "TI UCD90120, UCD90124, UCD9090, UCD90910" tristate "TI UCD90120, UCD90124, UCD90160, UCD9090, UCD90910"
default n default n
help help
If you say yes here you get hardware monitoring support for TI If you say yes here you get hardware monitoring support for TI
UCD90120, UCD90124, UCD9090, UCD90910 Sequencer and System Health UCD90120, UCD90124, UCD90160, UCD9090, UCD90910, Sequencer and System
Controllers. Health Controllers.
This driver can also be built as a module. If so, the module will This driver can also be built as a module. If so, the module will
be called ucd9000. be called ucd9000.

View File

@ -25,6 +25,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/i2c/pmbus.h>
#include "pmbus.h" #include "pmbus.h"
/* /*
@ -167,14 +168,26 @@ static int pmbus_probe(struct i2c_client *client,
const struct i2c_device_id *id) const struct i2c_device_id *id)
{ {
struct pmbus_driver_info *info; struct pmbus_driver_info *info;
struct pmbus_platform_data *pdata = NULL;
struct device *dev = &client->dev;
info = devm_kzalloc(&client->dev, sizeof(struct pmbus_driver_info), info = devm_kzalloc(dev, sizeof(struct pmbus_driver_info), GFP_KERNEL);
GFP_KERNEL);
if (!info) if (!info)
return -ENOMEM; return -ENOMEM;
if (!strcmp(id->name, "dps460") || !strcmp(id->name, "dps800") ||
!strcmp(id->name, "sgd009")) {
pdata = devm_kzalloc(dev, sizeof(struct pmbus_platform_data),
GFP_KERNEL);
if (!pdata)
return -ENOMEM;
pdata->flags = PMBUS_SKIP_STATUS_CHECK;
}
info->pages = id->driver_data; info->pages = id->driver_data;
info->identify = pmbus_identify; info->identify = pmbus_identify;
dev->platform_data = pdata;
return pmbus_do_probe(client, id, info); return pmbus_do_probe(client, id, info);
} }
@ -186,6 +199,8 @@ static const struct i2c_device_id pmbus_id[] = {
{"adp4000", 1}, {"adp4000", 1},
{"bmr453", 1}, {"bmr453", 1},
{"bmr454", 1}, {"bmr454", 1},
{"dps460", 1},
{"dps800", 1},
{"mdt040", 1}, {"mdt040", 1},
{"ncp4200", 1}, {"ncp4200", 1},
{"ncp4208", 1}, {"ncp4208", 1},
@ -193,6 +208,7 @@ static const struct i2c_device_id pmbus_id[] = {
{"pdt006", 1}, {"pdt006", 1},
{"pdt012", 1}, {"pdt012", 1},
{"pmbus", 0}, {"pmbus", 0},
{"sgd009", 1},
{"tps40400", 1}, {"tps40400", 1},
{"tps544b20", 1}, {"tps544b20", 1},
{"tps544b25", 1}, {"tps544b25", 1},

View File

@ -28,7 +28,7 @@
#include <linux/i2c/pmbus.h> #include <linux/i2c/pmbus.h>
#include "pmbus.h" #include "pmbus.h"
enum chips { ucd9000, ucd90120, ucd90124, ucd9090, ucd90910 }; enum chips { ucd9000, ucd90120, ucd90124, ucd90160, ucd9090, ucd90910 };
#define UCD9000_MONITOR_CONFIG 0xd5 #define UCD9000_MONITOR_CONFIG 0xd5
#define UCD9000_NUM_PAGES 0xd6 #define UCD9000_NUM_PAGES 0xd6
@ -112,6 +112,7 @@ static const struct i2c_device_id ucd9000_id[] = {
{"ucd9000", ucd9000}, {"ucd9000", ucd9000},
{"ucd90120", ucd90120}, {"ucd90120", ucd90120},
{"ucd90124", ucd90124}, {"ucd90124", ucd90124},
{"ucd90160", ucd90160},
{"ucd9090", ucd9090}, {"ucd9090", ucd9090},
{"ucd90910", ucd90910}, {"ucd90910", ucd90910},
{} {}

View File

@ -255,7 +255,6 @@ static const struct of_device_id scpi_of_match[] = {
static struct platform_driver scpi_hwmon_platdrv = { static struct platform_driver scpi_hwmon_platdrv = {
.driver = { .driver = {
.name = "scpi-hwmon", .name = "scpi-hwmon",
.owner = THIS_MODULE,
.of_match_table = scpi_of_match, .of_match_table = scpi_of_match,
}, },
.probe = scpi_hwmon_probe, .probe = scpi_hwmon_probe,

View File

@ -25,7 +25,6 @@
#include <linux/device.h> #include <linux/device.h>
#include <linux/jiffies.h> #include <linux/jiffies.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/thermal.h>
#include <linux/of.h> #include <linux/of.h>
#define DRIVER_NAME "tmp102" #define DRIVER_NAME "tmp102"
@ -79,84 +78,113 @@ static inline u16 tmp102_mC_to_reg(int val)
return (val * 128) / 1000; return (val * 128) / 1000;
} }
static int tmp102_read_temp(void *dev, int *temp) static int tmp102_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *temp)
{ {
struct tmp102 *tmp102 = dev_get_drvdata(dev); struct tmp102 *tmp102 = dev_get_drvdata(dev);
unsigned int reg; unsigned int regval;
int ret; int err, reg;
if (time_before(jiffies, tmp102->ready_time)) { switch (attr) {
dev_dbg(dev, "%s: Conversion not ready yet..\n", __func__); case hwmon_temp_input:
return -EAGAIN; /* Is it too early to return a conversion ? */
if (time_before(jiffies, tmp102->ready_time)) {
dev_dbg(dev, "%s: Conversion not ready yet..\n", __func__);
return -EAGAIN;
}
reg = TMP102_TEMP_REG;
break;
case hwmon_temp_max_hyst:
reg = TMP102_TLOW_REG;
break;
case hwmon_temp_max:
reg = TMP102_THIGH_REG;
break;
default:
return -EOPNOTSUPP;
} }
ret = regmap_read(tmp102->regmap, TMP102_TEMP_REG, &reg); err = regmap_read(tmp102->regmap, reg, &regval);
if (ret < 0) if (err < 0)
return ret; return err;
*temp = tmp102_reg_to_mC(regval);
*temp = tmp102_reg_to_mC(reg);
return 0; return 0;
} }
static ssize_t tmp102_show_temp(struct device *dev, static int tmp102_write(struct device *dev, enum hwmon_sensor_types type,
struct device_attribute *attr, u32 attr, int channel, long temp)
char *buf)
{ {
struct sensor_device_attribute *sda = to_sensor_dev_attr(attr);
struct tmp102 *tmp102 = dev_get_drvdata(dev); struct tmp102 *tmp102 = dev_get_drvdata(dev);
int regaddr = sda->index; int reg;
unsigned int reg;
int err;
if (regaddr == TMP102_TEMP_REG && switch (attr) {
time_before(jiffies, tmp102->ready_time)) case hwmon_temp_max_hyst:
return -EAGAIN; reg = TMP102_TLOW_REG;
break;
case hwmon_temp_max:
reg = TMP102_THIGH_REG;
break;
default:
return -EOPNOTSUPP;
}
err = regmap_read(tmp102->regmap, regaddr, &reg); temp = clamp_val(temp, -256000, 255000);
if (err < 0) return regmap_write(tmp102->regmap, reg, tmp102_mC_to_reg(temp));
return err;
return sprintf(buf, "%d\n", tmp102_reg_to_mC(reg));
} }
static ssize_t tmp102_set_temp(struct device *dev, static umode_t tmp102_is_visible(const void *data, enum hwmon_sensor_types type,
struct device_attribute *attr, u32 attr, int channel)
const char *buf, size_t count)
{ {
struct sensor_device_attribute *sda = to_sensor_dev_attr(attr); if (type != hwmon_temp)
struct tmp102 *tmp102 = dev_get_drvdata(dev); return 0;
int reg = sda->index;
long val;
int err;
if (kstrtol(buf, 10, &val) < 0) switch (attr) {
return -EINVAL; case hwmon_temp_input:
val = clamp_val(val, -256000, 255000); return S_IRUGO;
case hwmon_temp_max_hyst:
err = regmap_write(tmp102->regmap, reg, tmp102_mC_to_reg(val)); case hwmon_temp_max:
return err ? : count; return S_IRUGO | S_IWUSR;
default:
return 0;
}
} }
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, tmp102_show_temp, NULL, static u32 tmp102_chip_config[] = {
TMP102_TEMP_REG); HWMON_C_REGISTER_TZ,
0
};
static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, tmp102_show_temp, static const struct hwmon_channel_info tmp102_chip = {
tmp102_set_temp, TMP102_TLOW_REG); .type = hwmon_chip,
.config = tmp102_chip_config,
};
static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, tmp102_show_temp, static u32 tmp102_temp_config[] = {
tmp102_set_temp, TMP102_THIGH_REG); HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST,
0
};
static struct attribute *tmp102_attrs[] = { static const struct hwmon_channel_info tmp102_temp = {
&sensor_dev_attr_temp1_input.dev_attr.attr, .type = hwmon_temp,
&sensor_dev_attr_temp1_max_hyst.dev_attr.attr, .config = tmp102_temp_config,
&sensor_dev_attr_temp1_max.dev_attr.attr, };
static const struct hwmon_channel_info *tmp102_info[] = {
&tmp102_chip,
&tmp102_temp,
NULL NULL
}; };
ATTRIBUTE_GROUPS(tmp102);
static const struct thermal_zone_of_device_ops tmp102_of_thermal_ops = { static const struct hwmon_ops tmp102_hwmon_ops = {
.get_temp = tmp102_read_temp, .is_visible = tmp102_is_visible,
.read = tmp102_read,
.write = tmp102_write,
};
static const struct hwmon_chip_info tmp102_chip_info = {
.ops = &tmp102_hwmon_ops,
.info = tmp102_info,
}; };
static void tmp102_restore_config(void *data) static void tmp102_restore_config(void *data)
@ -188,7 +216,7 @@ static const struct regmap_config tmp102_regmap_config = {
}; };
static int tmp102_probe(struct i2c_client *client, static int tmp102_probe(struct i2c_client *client,
const struct i2c_device_id *id) const struct i2c_device_id *id)
{ {
struct device *dev = &client->dev; struct device *dev = &client->dev;
struct device *hwmon_dev; struct device *hwmon_dev;
@ -249,16 +277,14 @@ static int tmp102_probe(struct i2c_client *client,
tmp102->ready_time += msecs_to_jiffies(CONVERSION_TIME_MS); tmp102->ready_time += msecs_to_jiffies(CONVERSION_TIME_MS);
} }
hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
tmp102, tmp102,
tmp102_groups); &tmp102_chip_info,
NULL);
if (IS_ERR(hwmon_dev)) { if (IS_ERR(hwmon_dev)) {
dev_dbg(dev, "unable to register hwmon device\n"); dev_dbg(dev, "unable to register hwmon device\n");
return PTR_ERR(hwmon_dev); return PTR_ERR(hwmon_dev);
} }
devm_thermal_zone_of_sensor_register(hwmon_dev, 0, hwmon_dev,
&tmp102_of_thermal_ops);
dev_info(dev, "initialized\n"); dev_info(dev, "initialized\n");
return 0; return 0;

View File

@ -72,6 +72,10 @@ MODULE_DEVICE_TABLE(i2c, tmp421_id);
struct tmp421_data { struct tmp421_data {
struct i2c_client *client; struct i2c_client *client;
struct mutex update_lock; struct mutex update_lock;
u32 temp_config[5];
struct hwmon_channel_info temp_info;
const struct hwmon_channel_info *info[2];
struct hwmon_chip_info chip;
char valid; char valid;
unsigned long last_updated; unsigned long last_updated;
int channels; int channels;
@ -125,85 +129,46 @@ static struct tmp421_data *tmp421_update_device(struct device *dev)
return data; return data;
} }
static ssize_t show_temp_value(struct device *dev, static int tmp421_read(struct device *dev, enum hwmon_sensor_types type,
struct device_attribute *devattr, char *buf) u32 attr, int channel, long *val)
{ {
int index = to_sensor_dev_attr(devattr)->index; struct tmp421_data *tmp421 = tmp421_update_device(dev);
struct tmp421_data *data = tmp421_update_device(dev);
int temp;
mutex_lock(&data->update_lock); switch (attr) {
if (data->config & TMP421_CONFIG_RANGE) case hwmon_temp_input:
temp = temp_from_u16(data->temp[index]); if (tmp421->config & TMP421_CONFIG_RANGE)
else *val = temp_from_u16(tmp421->temp[channel]);
temp = temp_from_s16(data->temp[index]); else
mutex_unlock(&data->update_lock); *val = temp_from_s16(tmp421->temp[channel]);
return 0;
case hwmon_temp_fault:
/*
* The OPEN bit signals a fault. This is bit 0 of the temperature
* register (low byte).
*/
*val = tmp421->temp[channel] & 0x01;
return 0;
default:
return -EOPNOTSUPP;
}
return sprintf(buf, "%d\n", temp);
} }
static ssize_t show_fault(struct device *dev, static umode_t tmp421_is_visible(const void *data, enum hwmon_sensor_types type,
struct device_attribute *devattr, char *buf) u32 attr, int channel)
{ {
int index = to_sensor_dev_attr(devattr)->index; switch (attr) {
struct tmp421_data *data = tmp421_update_device(dev); case hwmon_temp_fault:
if (channel == 0)
/* return 0;
* The OPEN bit signals a fault. This is bit 0 of the temperature return S_IRUGO;
* register (low byte). case hwmon_temp_input:
*/ return S_IRUGO;
if (data->temp[index] & 0x01) default:
return sprintf(buf, "1\n"); return 0;
else }
return sprintf(buf, "0\n");
} }
static umode_t tmp421_is_visible(struct kobject *kobj, struct attribute *a,
int n)
{
struct device *dev = container_of(kobj, struct device, kobj);
struct tmp421_data *data = dev_get_drvdata(dev);
struct device_attribute *devattr;
unsigned int index;
devattr = container_of(a, struct device_attribute, attr);
index = to_sensor_dev_attr(devattr)->index;
if (index < data->channels)
return a->mode;
return 0;
}
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_value, NULL, 0);
static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp_value, NULL, 1);
static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_fault, NULL, 1);
static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp_value, NULL, 2);
static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_fault, NULL, 2);
static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp_value, NULL, 3);
static SENSOR_DEVICE_ATTR(temp4_fault, S_IRUGO, show_fault, NULL, 3);
static struct attribute *tmp421_attr[] = {
&sensor_dev_attr_temp1_input.dev_attr.attr,
&sensor_dev_attr_temp2_input.dev_attr.attr,
&sensor_dev_attr_temp2_fault.dev_attr.attr,
&sensor_dev_attr_temp3_input.dev_attr.attr,
&sensor_dev_attr_temp3_fault.dev_attr.attr,
&sensor_dev_attr_temp4_input.dev_attr.attr,
&sensor_dev_attr_temp4_fault.dev_attr.attr,
NULL
};
static const struct attribute_group tmp421_group = {
.attrs = tmp421_attr,
.is_visible = tmp421_is_visible,
};
static const struct attribute_group *tmp421_groups[] = {
&tmp421_group,
NULL
};
static int tmp421_init_client(struct i2c_client *client) static int tmp421_init_client(struct i2c_client *client)
{ {
int config, config_orig; int config, config_orig;
@ -289,13 +254,18 @@ static int tmp421_detect(struct i2c_client *client,
return 0; return 0;
} }
static const struct hwmon_ops tmp421_ops = {
.is_visible = tmp421_is_visible,
.read = tmp421_read,
};
static int tmp421_probe(struct i2c_client *client, static int tmp421_probe(struct i2c_client *client,
const struct i2c_device_id *id) const struct i2c_device_id *id)
{ {
struct device *dev = &client->dev; struct device *dev = &client->dev;
struct device *hwmon_dev; struct device *hwmon_dev;
struct tmp421_data *data; struct tmp421_data *data;
int err; int i, err;
data = devm_kzalloc(dev, sizeof(struct tmp421_data), GFP_KERNEL); data = devm_kzalloc(dev, sizeof(struct tmp421_data), GFP_KERNEL);
if (!data) if (!data)
@ -309,8 +279,21 @@ static int tmp421_probe(struct i2c_client *client,
if (err) if (err)
return err; return err;
hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, for (i = 0; i < data->channels; i++)
data, tmp421_groups); data->temp_config[i] = HWMON_T_INPUT | HWMON_T_FAULT;
data->chip.ops = &tmp421_ops;
data->chip.info = data->info;
data->info[0] = &data->temp_info;
data->temp_info.type = hwmon_temp;
data->temp_info.config = data->temp_config;
hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
data,
&data->chip,
NULL);
return PTR_ERR_OR_ZERO(hwmon_dev); return PTR_ERR_OR_ZERO(hwmon_dev);
} }

787
drivers/hwmon/xgene-hwmon.c Normal file
View File

@ -0,0 +1,787 @@
/*
* APM X-Gene SoC Hardware Monitoring Driver
*
* Copyright (c) 2016, Applied Micro Circuits Corporation
* Author: Loc Ho <lho@apm.com>
* Hoan Tran <hotran@apm.com>
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*
* This driver provides the following features:
* - Retrieve CPU total power (uW)
* - Retrieve IO total power (uW)
* - Retrieve SoC temperature (milli-degree C) and alarm
*/
#include <linux/acpi.h>
#include <linux/dma-mapping.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/kfifo.h>
#include <linux/mailbox_controller.h>
#include <linux/mailbox_client.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <acpi/pcc.h>
/* SLIMpro message defines */
#define MSG_TYPE_DBG 0
#define MSG_TYPE_ERR 7
#define MSG_TYPE_PWRMGMT 9
#define MSG_TYPE(v) (((v) & 0xF0000000) >> 28)
#define MSG_TYPE_SET(v) (((v) << 28) & 0xF0000000)
#define MSG_SUBTYPE(v) (((v) & 0x0F000000) >> 24)
#define MSG_SUBTYPE_SET(v) (((v) << 24) & 0x0F000000)
#define DBG_SUBTYPE_SENSOR_READ 4
#define SENSOR_RD_MSG 0x04FFE902
#define SENSOR_RD_EN_ADDR(a) ((a) & 0x000FFFFF)
#define PMD_PWR_REG 0x20
#define PMD_PWR_MW_REG 0x26
#define SOC_PWR_REG 0x21
#define SOC_PWR_MW_REG 0x27
#define SOC_TEMP_REG 0x10
#define TEMP_NEGATIVE_BIT 8
#define SENSOR_INVALID_DATA BIT(15)
#define PWRMGMT_SUBTYPE_TPC 1
#define TPC_ALARM 2
#define TPC_GET_ALARM 3
#define TPC_CMD(v) (((v) & 0x00FF0000) >> 16)
#define TPC_CMD_SET(v) (((v) << 16) & 0x00FF0000)
#define TPC_EN_MSG(hndl, cmd, type) \
(MSG_TYPE_SET(MSG_TYPE_PWRMGMT) | \
MSG_SUBTYPE_SET(hndl) | TPC_CMD_SET(cmd) | type)
/* PCC defines */
#define PCC_SIGNATURE_MASK 0x50424300
#define PCCC_GENERATE_DB_INT BIT(15)
#define PCCS_CMD_COMPLETE BIT(0)
#define PCCS_SCI_DOORBEL BIT(1)
#define PCCS_PLATFORM_NOTIFICATION BIT(3)
/*
* Arbitrary retries in case the remote processor is slow to respond
* to PCC commands
*/
#define PCC_NUM_RETRIES 500
#define ASYNC_MSG_FIFO_SIZE 16
#define MBOX_OP_TIMEOUTMS 1000
#define WATT_TO_mWATT(x) ((x) * 1000)
#define mWATT_TO_uWATT(x) ((x) * 1000)
#define CELSIUS_TO_mCELSIUS(x) ((x) * 1000)
#define to_xgene_hwmon_dev(cl) \
container_of(cl, struct xgene_hwmon_dev, mbox_client)
struct slimpro_resp_msg {
u32 msg;
u32 param1;
u32 param2;
} __packed;
struct xgene_hwmon_dev {
struct device *dev;
struct mbox_chan *mbox_chan;
struct mbox_client mbox_client;
int mbox_idx;
spinlock_t kfifo_lock;
struct mutex rd_mutex;
struct completion rd_complete;
int resp_pending;
struct slimpro_resp_msg sync_msg;
struct work_struct workq;
struct kfifo_rec_ptr_1 async_msg_fifo;
struct device *hwmon_dev;
bool temp_critical_alarm;
phys_addr_t comm_base_addr;
void *pcc_comm_addr;
u64 usecs_lat;
};
/*
* This function tests and clears a bitmask then returns its old value
*/
static u16 xgene_word_tst_and_clr(u16 *addr, u16 mask)
{
u16 ret, val;
val = le16_to_cpu(READ_ONCE(*addr));
ret = val & mask;
val &= ~mask;
WRITE_ONCE(*addr, cpu_to_le16(val));
return ret;
}
static int xgene_hwmon_pcc_rd(struct xgene_hwmon_dev *ctx, u32 *msg)
{
struct acpi_pcct_shared_memory *generic_comm_base = ctx->pcc_comm_addr;
u32 *ptr = (void *)(generic_comm_base + 1);
int rc, i;
u16 val;
mutex_lock(&ctx->rd_mutex);
init_completion(&ctx->rd_complete);
ctx->resp_pending = true;
/* Write signature for subspace */
WRITE_ONCE(generic_comm_base->signature,
cpu_to_le32(PCC_SIGNATURE_MASK | ctx->mbox_idx));
/* Write to the shared command region */
WRITE_ONCE(generic_comm_base->command,
cpu_to_le16(MSG_TYPE(msg[0]) | PCCC_GENERATE_DB_INT));
/* Flip CMD COMPLETE bit */
val = le16_to_cpu(READ_ONCE(generic_comm_base->status));
val &= ~PCCS_CMD_COMPLETE;
WRITE_ONCE(generic_comm_base->status, cpu_to_le16(val));
/* Copy the message to the PCC comm space */
for (i = 0; i < sizeof(struct slimpro_resp_msg) / 4; i++)
WRITE_ONCE(ptr[i], cpu_to_le32(msg[i]));
/* Ring the doorbell */
rc = mbox_send_message(ctx->mbox_chan, msg);
if (rc < 0) {
dev_err(ctx->dev, "Mailbox send error %d\n", rc);
goto err;
}
if (!wait_for_completion_timeout(&ctx->rd_complete,
usecs_to_jiffies(ctx->usecs_lat))) {
dev_err(ctx->dev, "Mailbox operation timed out\n");
rc = -ETIMEDOUT;
goto err;
}
/* Check for error message */
if (MSG_TYPE(ctx->sync_msg.msg) == MSG_TYPE_ERR) {
rc = -EINVAL;
goto err;
}
msg[0] = ctx->sync_msg.msg;
msg[1] = ctx->sync_msg.param1;
msg[2] = ctx->sync_msg.param2;
err:
mbox_chan_txdone(ctx->mbox_chan, 0);
ctx->resp_pending = false;
mutex_unlock(&ctx->rd_mutex);
return rc;
}
static int xgene_hwmon_rd(struct xgene_hwmon_dev *ctx, u32 *msg)
{
int rc;
mutex_lock(&ctx->rd_mutex);
init_completion(&ctx->rd_complete);
ctx->resp_pending = true;
rc = mbox_send_message(ctx->mbox_chan, msg);
if (rc < 0) {
dev_err(ctx->dev, "Mailbox send error %d\n", rc);
goto err;
}
if (!wait_for_completion_timeout(&ctx->rd_complete,
msecs_to_jiffies(MBOX_OP_TIMEOUTMS))) {
dev_err(ctx->dev, "Mailbox operation timed out\n");
rc = -ETIMEDOUT;
goto err;
}
/* Check for error message */
if (MSG_TYPE(ctx->sync_msg.msg) == MSG_TYPE_ERR) {
rc = -EINVAL;
goto err;
}
msg[0] = ctx->sync_msg.msg;
msg[1] = ctx->sync_msg.param1;
msg[2] = ctx->sync_msg.param2;
err:
ctx->resp_pending = false;
mutex_unlock(&ctx->rd_mutex);
return rc;
}
static int xgene_hwmon_reg_map_rd(struct xgene_hwmon_dev *ctx, u32 addr,
u32 *data)
{
u32 msg[3];
int rc;
msg[0] = SENSOR_RD_MSG;
msg[1] = SENSOR_RD_EN_ADDR(addr);
msg[2] = 0;
if (acpi_disabled)
rc = xgene_hwmon_rd(ctx, msg);
else
rc = xgene_hwmon_pcc_rd(ctx, msg);
if (rc < 0)
return rc;
/*
* Check if sensor data is valid.
*/
if (msg[1] & SENSOR_INVALID_DATA)
return -ENODATA;
*data = msg[1];
return rc;
}
static int xgene_hwmon_get_notification_msg(struct xgene_hwmon_dev *ctx,
u32 *amsg)
{
u32 msg[3];
int rc;
msg[0] = TPC_EN_MSG(PWRMGMT_SUBTYPE_TPC, TPC_GET_ALARM, 0);
msg[1] = 0;
msg[2] = 0;
rc = xgene_hwmon_pcc_rd(ctx, msg);
if (rc < 0)
return rc;
amsg[0] = msg[0];
amsg[1] = msg[1];
amsg[2] = msg[2];
return rc;
}
static int xgene_hwmon_get_cpu_pwr(struct xgene_hwmon_dev *ctx, u32 *val)
{
u32 watt, mwatt;
int rc;
rc = xgene_hwmon_reg_map_rd(ctx, PMD_PWR_REG, &watt);
if (rc < 0)
return rc;
rc = xgene_hwmon_reg_map_rd(ctx, PMD_PWR_MW_REG, &mwatt);
if (rc < 0)
return rc;
*val = WATT_TO_mWATT(watt) + mwatt;
return 0;
}
static int xgene_hwmon_get_io_pwr(struct xgene_hwmon_dev *ctx, u32 *val)
{
u32 watt, mwatt;
int rc;
rc = xgene_hwmon_reg_map_rd(ctx, SOC_PWR_REG, &watt);
if (rc < 0)
return rc;
rc = xgene_hwmon_reg_map_rd(ctx, SOC_PWR_MW_REG, &mwatt);
if (rc < 0)
return rc;
*val = WATT_TO_mWATT(watt) + mwatt;
return 0;
}
static int xgene_hwmon_get_temp(struct xgene_hwmon_dev *ctx, u32 *val)
{
return xgene_hwmon_reg_map_rd(ctx, SOC_TEMP_REG, val);
}
/*
* Sensor temperature/power functions
*/
static ssize_t temp1_input_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct xgene_hwmon_dev *ctx = dev_get_drvdata(dev);
int rc, temp;
u32 val;
rc = xgene_hwmon_get_temp(ctx, &val);
if (rc < 0)
return rc;
temp = sign_extend32(val, TEMP_NEGATIVE_BIT);
return snprintf(buf, PAGE_SIZE, "%d\n", CELSIUS_TO_mCELSIUS(temp));
}
static ssize_t temp1_label_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
return snprintf(buf, PAGE_SIZE, "SoC Temperature\n");
}
static ssize_t temp1_critical_alarm_show(struct device *dev,
struct device_attribute *devattr,
char *buf)
{
struct xgene_hwmon_dev *ctx = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "%d\n", ctx->temp_critical_alarm);
}
static ssize_t power1_label_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
return snprintf(buf, PAGE_SIZE, "CPU power\n");
}
static ssize_t power2_label_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
return snprintf(buf, PAGE_SIZE, "IO power\n");
}
static ssize_t power1_input_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct xgene_hwmon_dev *ctx = dev_get_drvdata(dev);
u32 val;
int rc;
rc = xgene_hwmon_get_cpu_pwr(ctx, &val);
if (rc < 0)
return rc;
return snprintf(buf, PAGE_SIZE, "%u\n", mWATT_TO_uWATT(val));
}
static ssize_t power2_input_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct xgene_hwmon_dev *ctx = dev_get_drvdata(dev);
u32 val;
int rc;
rc = xgene_hwmon_get_io_pwr(ctx, &val);
if (rc < 0)
return rc;
return snprintf(buf, PAGE_SIZE, "%u\n", mWATT_TO_uWATT(val));
}
static DEVICE_ATTR_RO(temp1_label);
static DEVICE_ATTR_RO(temp1_input);
static DEVICE_ATTR_RO(temp1_critical_alarm);
static DEVICE_ATTR_RO(power1_label);
static DEVICE_ATTR_RO(power1_input);
static DEVICE_ATTR_RO(power2_label);
static DEVICE_ATTR_RO(power2_input);
static struct attribute *xgene_hwmon_attrs[] = {
&dev_attr_temp1_label.attr,
&dev_attr_temp1_input.attr,
&dev_attr_temp1_critical_alarm.attr,
&dev_attr_power1_label.attr,
&dev_attr_power1_input.attr,
&dev_attr_power2_label.attr,
&dev_attr_power2_input.attr,
NULL,
};
ATTRIBUTE_GROUPS(xgene_hwmon);
static int xgene_hwmon_tpc_alarm(struct xgene_hwmon_dev *ctx,
struct slimpro_resp_msg *amsg)
{
ctx->temp_critical_alarm = !!amsg->param2;
sysfs_notify(&ctx->dev->kobj, NULL, "temp1_critical_alarm");
return 0;
}
static void xgene_hwmon_process_pwrmsg(struct xgene_hwmon_dev *ctx,
struct slimpro_resp_msg *amsg)
{
if ((MSG_SUBTYPE(amsg->msg) == PWRMGMT_SUBTYPE_TPC) &&
(TPC_CMD(amsg->msg) == TPC_ALARM))
xgene_hwmon_tpc_alarm(ctx, amsg);
}
/*
* This function is called to process async work queue
*/
static void xgene_hwmon_evt_work(struct work_struct *work)
{
struct slimpro_resp_msg amsg;
struct xgene_hwmon_dev *ctx;
int ret;
ctx = container_of(work, struct xgene_hwmon_dev, workq);
while (kfifo_out_spinlocked(&ctx->async_msg_fifo, &amsg,
sizeof(struct slimpro_resp_msg),
&ctx->kfifo_lock)) {
/*
* If PCC, send a consumer command to Platform to get info
* If Slimpro Mailbox, get message from specific FIFO
*/
if (!acpi_disabled) {
ret = xgene_hwmon_get_notification_msg(ctx,
(u32 *)&amsg);
if (ret < 0)
continue;
}
if (MSG_TYPE(amsg.msg) == MSG_TYPE_PWRMGMT)
xgene_hwmon_process_pwrmsg(ctx, &amsg);
}
}
static int xgene_hwmon_rx_ready(struct xgene_hwmon_dev *ctx, void *msg)
{
if (IS_ERR_OR_NULL(ctx->hwmon_dev) && !ctx->resp_pending) {
/* Enqueue to the FIFO */
kfifo_in_spinlocked(&ctx->async_msg_fifo, msg,
sizeof(struct slimpro_resp_msg),
&ctx->kfifo_lock);
return -ENODEV;
}
return 0;
}
/*
* This function is called when the SLIMpro Mailbox received a message
*/
static void xgene_hwmon_rx_cb(struct mbox_client *cl, void *msg)
{
struct xgene_hwmon_dev *ctx = to_xgene_hwmon_dev(cl);
/*
* While the driver registers with the mailbox framework, an interrupt
* can be pending before the probe function completes its
* initialization. If such condition occurs, just queue up the message
* as the driver is not ready for servicing the callback.
*/
if (xgene_hwmon_rx_ready(ctx, msg) < 0)
return;
/*
* Response message format:
* msg[0] is the return code of the operation
* msg[1] is the first parameter word
* msg[2] is the second parameter word
*
* As message only supports dword size, just assign it.
*/
/* Check for sync query */
if (ctx->resp_pending &&
((MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_ERR) ||
(MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_DBG &&
MSG_SUBTYPE(((u32 *)msg)[0]) == DBG_SUBTYPE_SENSOR_READ) ||
(MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_PWRMGMT &&
MSG_SUBTYPE(((u32 *)msg)[0]) == PWRMGMT_SUBTYPE_TPC &&
TPC_CMD(((u32 *)msg)[0]) == TPC_ALARM))) {
ctx->sync_msg.msg = ((u32 *)msg)[0];
ctx->sync_msg.param1 = ((u32 *)msg)[1];
ctx->sync_msg.param2 = ((u32 *)msg)[2];
/* Operation waiting for response */
complete(&ctx->rd_complete);
return;
}
/* Enqueue to the FIFO */
kfifo_in_spinlocked(&ctx->async_msg_fifo, msg,
sizeof(struct slimpro_resp_msg), &ctx->kfifo_lock);
/* Schedule the bottom handler */
schedule_work(&ctx->workq);
}
/*
* This function is called when the PCC Mailbox received a message
*/
static void xgene_hwmon_pcc_rx_cb(struct mbox_client *cl, void *msg)
{
struct xgene_hwmon_dev *ctx = to_xgene_hwmon_dev(cl);
struct acpi_pcct_shared_memory *generic_comm_base = ctx->pcc_comm_addr;
struct slimpro_resp_msg amsg;
/*
* While the driver registers with the mailbox framework, an interrupt
* can be pending before the probe function completes its
* initialization. If such condition occurs, just queue up the message
* as the driver is not ready for servicing the callback.
*/
if (xgene_hwmon_rx_ready(ctx, &amsg) < 0)
return;
msg = generic_comm_base + 1;
/* Check if platform sends interrupt */
if (!xgene_word_tst_and_clr(&generic_comm_base->status,
PCCS_SCI_DOORBEL))
return;
/*
* Response message format:
* msg[0] is the return code of the operation
* msg[1] is the first parameter word
* msg[2] is the second parameter word
*
* As message only supports dword size, just assign it.
*/
/* Check for sync query */
if (ctx->resp_pending &&
((MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_ERR) ||
(MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_DBG &&
MSG_SUBTYPE(((u32 *)msg)[0]) == DBG_SUBTYPE_SENSOR_READ) ||
(MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_PWRMGMT &&
MSG_SUBTYPE(((u32 *)msg)[0]) == PWRMGMT_SUBTYPE_TPC &&
TPC_CMD(((u32 *)msg)[0]) == TPC_ALARM))) {
/* Check if platform completes command */
if (xgene_word_tst_and_clr(&generic_comm_base->status,
PCCS_CMD_COMPLETE)) {
ctx->sync_msg.msg = ((u32 *)msg)[0];
ctx->sync_msg.param1 = ((u32 *)msg)[1];
ctx->sync_msg.param2 = ((u32 *)msg)[2];
/* Operation waiting for response */
complete(&ctx->rd_complete);
return;
}
}
/*
* Platform notifies interrupt to OSPM.
* OPSM schedules a consumer command to get this information
* in a workqueue. Platform must wait until OSPM has issued
* a consumer command that serves this notification.
*/
/* Enqueue to the FIFO */
kfifo_in_spinlocked(&ctx->async_msg_fifo, &amsg,
sizeof(struct slimpro_resp_msg), &ctx->kfifo_lock);
/* Schedule the bottom handler */
schedule_work(&ctx->workq);
}
static void xgene_hwmon_tx_done(struct mbox_client *cl, void *msg, int ret)
{
if (ret) {
dev_dbg(cl->dev, "TX did not complete: CMD sent:%x, ret:%d\n",
*(u16 *)msg, ret);
} else {
dev_dbg(cl->dev, "TX completed. CMD sent:%x, ret:%d\n",
*(u16 *)msg, ret);
}
}
static int xgene_hwmon_probe(struct platform_device *pdev)
{
struct xgene_hwmon_dev *ctx;
struct mbox_client *cl;
int rc;
ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
ctx->dev = &pdev->dev;
platform_set_drvdata(pdev, ctx);
cl = &ctx->mbox_client;
spin_lock_init(&ctx->kfifo_lock);
mutex_init(&ctx->rd_mutex);
rc = kfifo_alloc(&ctx->async_msg_fifo,
sizeof(struct slimpro_resp_msg) * ASYNC_MSG_FIFO_SIZE,
GFP_KERNEL);
if (rc)
goto out_mbox_free;
INIT_WORK(&ctx->workq, xgene_hwmon_evt_work);
/* Request mailbox channel */
cl->dev = &pdev->dev;
cl->tx_done = xgene_hwmon_tx_done;
cl->tx_block = false;
cl->tx_tout = MBOX_OP_TIMEOUTMS;
cl->knows_txdone = false;
if (acpi_disabled) {
cl->rx_callback = xgene_hwmon_rx_cb;
ctx->mbox_chan = mbox_request_channel(cl, 0);
if (IS_ERR(ctx->mbox_chan)) {
dev_err(&pdev->dev,
"SLIMpro mailbox channel request failed\n");
return -ENODEV;
}
} else {
struct acpi_pcct_hw_reduced *cppc_ss;
if (device_property_read_u32(&pdev->dev, "pcc-channel",
&ctx->mbox_idx)) {
dev_err(&pdev->dev, "no pcc-channel property\n");
return -ENODEV;
}
cl->rx_callback = xgene_hwmon_pcc_rx_cb;
ctx->mbox_chan = pcc_mbox_request_channel(cl, ctx->mbox_idx);
if (IS_ERR(ctx->mbox_chan)) {
dev_err(&pdev->dev,
"PPC channel request failed\n");
return -ENODEV;
}
/*
* The PCC mailbox controller driver should
* have parsed the PCCT (global table of all
* PCC channels) and stored pointers to the
* subspace communication region in con_priv.
*/
cppc_ss = ctx->mbox_chan->con_priv;
if (!cppc_ss) {
dev_err(&pdev->dev, "PPC subspace not found\n");
rc = -ENODEV;
goto out_mbox_free;
}
if (!ctx->mbox_chan->mbox->txdone_irq) {
dev_err(&pdev->dev, "PCC IRQ not supported\n");
rc = -ENODEV;
goto out_mbox_free;
}
/*
* This is the shared communication region
* for the OS and Platform to communicate over.
*/
ctx->comm_base_addr = cppc_ss->base_address;
if (ctx->comm_base_addr) {
ctx->pcc_comm_addr = memremap(ctx->comm_base_addr,
cppc_ss->length,
MEMREMAP_WB);
} else {
dev_err(&pdev->dev, "Failed to get PCC comm region\n");
rc = -ENODEV;
goto out_mbox_free;
}
if (!ctx->pcc_comm_addr) {
dev_err(&pdev->dev,
"Failed to ioremap PCC comm region\n");
rc = -ENOMEM;
goto out_mbox_free;
}
/*
* cppc_ss->latency is just a Nominal value. In reality
* the remote processor could be much slower to reply.
* So add an arbitrary amount of wait on top of Nominal.
*/
ctx->usecs_lat = PCC_NUM_RETRIES * cppc_ss->latency;
}
ctx->hwmon_dev = hwmon_device_register_with_groups(ctx->dev,
"apm_xgene",
ctx,
xgene_hwmon_groups);
if (IS_ERR(ctx->hwmon_dev)) {
dev_err(&pdev->dev, "Failed to register HW monitor device\n");
rc = PTR_ERR(ctx->hwmon_dev);
goto out;
}
/*
* Schedule the bottom handler if there is a pending message.
*/
schedule_work(&ctx->workq);
dev_info(&pdev->dev, "APM X-Gene SoC HW monitor driver registered\n");
return 0;
out:
if (acpi_disabled)
mbox_free_channel(ctx->mbox_chan);
else
pcc_mbox_free_channel(ctx->mbox_chan);
out_mbox_free:
kfifo_free(&ctx->async_msg_fifo);
return rc;
}
static int xgene_hwmon_remove(struct platform_device *pdev)
{
struct xgene_hwmon_dev *ctx = platform_get_drvdata(pdev);
hwmon_device_unregister(ctx->hwmon_dev);
kfifo_free(&ctx->async_msg_fifo);
if (acpi_disabled)
mbox_free_channel(ctx->mbox_chan);
else
pcc_mbox_free_channel(ctx->mbox_chan);
return 0;
}
#ifdef CONFIG_ACPI
static const struct acpi_device_id xgene_hwmon_acpi_match[] = {
{"APMC0D29", 0},
{},
};
MODULE_DEVICE_TABLE(acpi, xgene_hwmon_acpi_match);
#endif
static const struct of_device_id xgene_hwmon_of_match[] = {
{.compatible = "apm,xgene-slimpro-hwmon"},
{}
};
MODULE_DEVICE_TABLE(of, xgene_hwmon_of_match);
static struct platform_driver xgene_hwmon_driver __refdata = {
.probe = xgene_hwmon_probe,
.remove = xgene_hwmon_remove,
.driver = {
.name = "xgene-slimpro-hwmon",
.of_match_table = xgene_hwmon_of_match,
.acpi_match_table = ACPI_PTR(xgene_hwmon_acpi_match),
},
};
module_platform_driver(xgene_hwmon_driver);
MODULE_DESCRIPTION("APM X-Gene SoC hardware monitor");
MODULE_LICENSE("GPL");

View File

@ -14,9 +14,341 @@
#ifndef _HWMON_H_ #ifndef _HWMON_H_
#define _HWMON_H_ #define _HWMON_H_
#include <linux/bitops.h>
struct device; struct device;
struct attribute_group; struct attribute_group;
enum hwmon_sensor_types {
hwmon_chip,
hwmon_temp,
hwmon_in,
hwmon_curr,
hwmon_power,
hwmon_energy,
hwmon_humidity,
hwmon_fan,
hwmon_pwm,
};
enum hwmon_chip_attributes {
hwmon_chip_temp_reset_history,
hwmon_chip_in_reset_history,
hwmon_chip_curr_reset_history,
hwmon_chip_power_reset_history,
hwmon_chip_register_tz,
hwmon_chip_update_interval,
hwmon_chip_alarms,
};
#define HWMON_C_TEMP_RESET_HISTORY BIT(hwmon_chip_temp_reset_history)
#define HWMON_C_IN_RESET_HISTORY BIT(hwmon_chip_in_reset_history)
#define HWMON_C_CURR_RESET_HISTORY BIT(hwmon_chip_curr_reset_history)
#define HWMON_C_POWER_RESET_HISTORY BIT(hwmon_chip_power_reset_history)
#define HWMON_C_REGISTER_TZ BIT(hwmon_chip_register_tz)
#define HWMON_C_UPDATE_INTERVAL BIT(hwmon_chip_update_interval)
#define HWMON_C_ALARMS BIT(hwmon_chip_alarms)
enum hwmon_temp_attributes {
hwmon_temp_input = 0,
hwmon_temp_type,
hwmon_temp_lcrit,
hwmon_temp_lcrit_hyst,
hwmon_temp_min,
hwmon_temp_min_hyst,
hwmon_temp_max,
hwmon_temp_max_hyst,
hwmon_temp_crit,
hwmon_temp_crit_hyst,
hwmon_temp_emergency,
hwmon_temp_emergency_hyst,
hwmon_temp_alarm,
hwmon_temp_lcrit_alarm,
hwmon_temp_min_alarm,
hwmon_temp_max_alarm,
hwmon_temp_crit_alarm,
hwmon_temp_emergency_alarm,
hwmon_temp_fault,
hwmon_temp_offset,
hwmon_temp_label,
hwmon_temp_lowest,
hwmon_temp_highest,
hwmon_temp_reset_history,
};
#define HWMON_T_INPUT BIT(hwmon_temp_input)
#define HWMON_T_TYPE BIT(hwmon_temp_type)
#define HWMON_T_LCRIT BIT(hwmon_temp_lcrit)
#define HWMON_T_LCRIT_HYST BIT(hwmon_temp_lcrit_hyst)
#define HWMON_T_MIN BIT(hwmon_temp_min)
#define HWMON_T_MIN_HYST BIT(hwmon_temp_min_hyst)
#define HWMON_T_MAX BIT(hwmon_temp_max)
#define HWMON_T_MAX_HYST BIT(hwmon_temp_max_hyst)
#define HWMON_T_CRIT BIT(hwmon_temp_crit)
#define HWMON_T_CRIT_HYST BIT(hwmon_temp_crit_hyst)
#define HWMON_T_EMERGENCY BIT(hwmon_temp_emergency)
#define HWMON_T_EMERGENCY_HYST BIT(hwmon_temp_emergency_hyst)
#define HWMON_T_MIN_ALARM BIT(hwmon_temp_min_alarm)
#define HWMON_T_MAX_ALARM BIT(hwmon_temp_max_alarm)
#define HWMON_T_CRIT_ALARM BIT(hwmon_temp_crit_alarm)
#define HWMON_T_EMERGENCY_ALARM BIT(hwmon_temp_emergency_alarm)
#define HWMON_T_FAULT BIT(hwmon_temp_fault)
#define HWMON_T_OFFSET BIT(hwmon_temp_offset)
#define HWMON_T_LABEL BIT(hwmon_temp_label)
#define HWMON_T_LOWEST BIT(hwmon_temp_lowest)
#define HWMON_T_HIGHEST BIT(hwmon_temp_highest)
#define HWMON_T_RESET_HISTORY BIT(hwmon_temp_reset_history)
enum hwmon_in_attributes {
hwmon_in_input,
hwmon_in_min,
hwmon_in_max,
hwmon_in_lcrit,
hwmon_in_crit,
hwmon_in_average,
hwmon_in_lowest,
hwmon_in_highest,
hwmon_in_reset_history,
hwmon_in_label,
hwmon_in_alarm,
hwmon_in_min_alarm,
hwmon_in_max_alarm,
hwmon_in_lcrit_alarm,
hwmon_in_crit_alarm,
};
#define HWMON_I_INPUT BIT(hwmon_in_input)
#define HWMON_I_MIN BIT(hwmon_in_min)
#define HWMON_I_MAX BIT(hwmon_in_max)
#define HWMON_I_LCRIT BIT(hwmon_in_lcrit)
#define HWMON_I_CRIT BIT(hwmon_in_crit)
#define HWMON_I_AVERAGE BIT(hwmon_in_average)
#define HWMON_I_LOWEST BIT(hwmon_in_lowest)
#define HWMON_I_HIGHEST BIT(hwmon_in_highest)
#define HWMON_I_RESET_HISTORY BIT(hwmon_in_reset_history)
#define HWMON_I_LABEL BIT(hwmon_in_label)
#define HWMON_I_ALARM BIT(hwmon_in_alarm)
#define HWMON_I_MIN_ALARM BIT(hwmon_in_min_alarm)
#define HWMON_I_MAX_ALARM BIT(hwmon_in_max_alarm)
#define HWMON_I_LCRIT_ALARM BIT(hwmon_in_lcrit_alarm)
#define HWMON_I_CRIT_ALARM BIT(hwmon_in_crit_alarm)
enum hwmon_curr_attributes {
hwmon_curr_input,
hwmon_curr_min,
hwmon_curr_max,
hwmon_curr_lcrit,
hwmon_curr_crit,
hwmon_curr_average,
hwmon_curr_lowest,
hwmon_curr_highest,
hwmon_curr_reset_history,
hwmon_curr_label,
hwmon_curr_alarm,
hwmon_curr_min_alarm,
hwmon_curr_max_alarm,
hwmon_curr_lcrit_alarm,
hwmon_curr_crit_alarm,
};
#define HWMON_C_INPUT BIT(hwmon_curr_input)
#define HWMON_C_MIN BIT(hwmon_curr_min)
#define HWMON_C_MAX BIT(hwmon_curr_max)
#define HWMON_C_LCRIT BIT(hwmon_curr_lcrit)
#define HWMON_C_CRIT BIT(hwmon_curr_crit)
#define HWMON_C_AVERAGE BIT(hwmon_curr_average)
#define HWMON_C_LOWEST BIT(hwmon_curr_lowest)
#define HWMON_C_HIGHEST BIT(hwmon_curr_highest)
#define HWMON_C_RESET_HISTORY BIT(hwmon_curr_reset_history)
#define HWMON_C_LABEL BIT(hwmon_curr_label)
#define HWMON_C_ALARM BIT(hwmon_curr_alarm)
#define HWMON_C_MIN_ALARM BIT(hwmon_curr_min_alarm)
#define HWMON_C_MAX_ALARM BIT(hwmon_curr_max_alarm)
#define HWMON_C_LCRIT_ALARM BIT(hwmon_curr_lcrit_alarm)
#define HWMON_C_CRIT_ALARM BIT(hwmon_curr_crit_alarm)
enum hwmon_power_attributes {
hwmon_power_average,
hwmon_power_average_interval,
hwmon_power_average_interval_max,
hwmon_power_average_interval_min,
hwmon_power_average_highest,
hwmon_power_average_lowest,
hwmon_power_average_max,
hwmon_power_average_min,
hwmon_power_input,
hwmon_power_input_highest,
hwmon_power_input_lowest,
hwmon_power_reset_history,
hwmon_power_accuracy,
hwmon_power_cap,
hwmon_power_cap_hyst,
hwmon_power_cap_max,
hwmon_power_cap_min,
hwmon_power_max,
hwmon_power_crit,
hwmon_power_label,
hwmon_power_alarm,
hwmon_power_cap_alarm,
hwmon_power_max_alarm,
hwmon_power_crit_alarm,
};
#define HWMON_P_AVERAGE BIT(hwmon_power_average)
#define HWMON_P_AVERAGE_INTERVAL BIT(hwmon_power_average_interval)
#define HWMON_P_AVERAGE_INTERVAL_MAX BIT(hwmon_power_average_interval_max)
#define HWMON_P_AVERAGE_INTERVAL_MIN BIT(hwmon_power_average_interval_min)
#define HWMON_P_AVERAGE_HIGHEST BIT(hwmon_power_average_highest)
#define HWMON_P_AVERAGE_LOWEST BIT(hwmon_power_average_lowest)
#define HWMON_P_AVERAGE_MAX BIT(hwmon_power_average_max)
#define HWMON_P_AVERAGE_MIN BIT(hwmon_power_average_min)
#define HWMON_P_INPUT BIT(hwmon_power_input)
#define HWMON_P_INPUT_HIGHEST BIT(hwmon_power_input_highest)
#define HWMON_P_INPUT_LOWEST BIT(hwmon_power_input_lowest)
#define HWMON_P_RESET_HISTORY BIT(hwmon_power_reset_history)
#define HWMON_P_ACCURACY BIT(hwmon_power_accuracy)
#define HWMON_P_CAP BIT(hwmon_power_cap)
#define HWMON_P_CAP_HYST BIT(hwmon_power_cap_hyst)
#define HWMON_P_CAP_MAX BIT(hwmon_power_cap_max)
#define HWMON_P_CAP_MIN BIT(hwmon_power_cap_min)
#define HWMON_P_MAX BIT(hwmon_power_max)
#define HWMON_P_CRIT BIT(hwmon_power_crit)
#define HWMON_P_LABEL BIT(hwmon_power_label)
#define HWMON_P_ALARM BIT(hwmon_power_alarm)
#define HWMON_P_CAP_ALARM BIT(hwmon_power_cap_alarm)
#define HWMON_P_MAX_ALARM BIT(hwmon_power_max_alarm)
#define HWMON_P_CRIT_ALARM BIT(hwmon_power_crit_alarm)
enum hwmon_energy_attributes {
hwmon_energy_input,
hwmon_energy_label,
};
#define HWMON_E_INPUT BIT(hwmon_energy_input)
#define HWMON_E_LABEL BIT(hwmon_energy_label)
enum hwmon_humidity_attributes {
hwmon_humidity_input,
hwmon_humidity_label,
hwmon_humidity_min,
hwmon_humidity_min_hyst,
hwmon_humidity_max,
hwmon_humidity_max_hyst,
hwmon_humidity_alarm,
hwmon_humidity_fault,
};
#define HWMON_H_INPUT BIT(hwmon_humidity_input)
#define HWMON_H_LABEL BIT(hwmon_humidity_label)
#define HWMON_H_MIN BIT(hwmon_humidity_min)
#define HWMON_H_MIN_HYST BIT(hwmon_humidity_min_hyst)
#define HWMON_H_MAX BIT(hwmon_humidity_max)
#define HWMON_H_MAX_HYST BIT(hwmon_humidity_max_hyst)
#define HWMON_H_ALARM BIT(hwmon_humidity_alarm)
#define HWMON_H_FAULT BIT(hwmon_humidity_fault)
enum hwmon_fan_attributes {
hwmon_fan_input,
hwmon_fan_label,
hwmon_fan_min,
hwmon_fan_max,
hwmon_fan_div,
hwmon_fan_pulses,
hwmon_fan_target,
hwmon_fan_alarm,
hwmon_fan_min_alarm,
hwmon_fan_max_alarm,
hwmon_fan_fault,
};
#define HWMON_F_INPUT BIT(hwmon_fan_input)
#define HWMON_F_LABEL BIT(hwmon_fan_label)
#define HWMON_F_MIN BIT(hwmon_fan_min)
#define HWMON_F_MAX BIT(hwmon_fan_max)
#define HWMON_F_DIV BIT(hwmon_fan_div)
#define HWMON_F_PULSES BIT(hwmon_fan_pulses)
#define HWMON_F_TARGET BIT(hwmon_fan_target)
#define HWMON_F_ALARM BIT(hwmon_fan_alarm)
#define HWMON_F_MIN_ALARM BIT(hwmon_fan_min_alarm)
#define HWMON_F_MAX_ALARM BIT(hwmon_fan_max_alarm)
#define HWMON_F_FAULT BIT(hwmon_fan_fault)
enum hwmon_pwm_attributes {
hwmon_pwm_input,
hwmon_pwm_enable,
hwmon_pwm_mode,
hwmon_pwm_freq,
};
#define HWMON_PWM_INPUT BIT(hwmon_pwm_input)
#define HWMON_PWM_ENABLE BIT(hwmon_pwm_enable)
#define HWMON_PWM_MODE BIT(hwmon_pwm_mode)
#define HWMON_PWM_FREQ BIT(hwmon_pwm_freq)
/**
* struct hwmon_ops - hwmon device operations
* @is_visible: Callback to return attribute visibility. Mandatory.
* Parameters are:
* @const void *drvdata:
* Pointer to driver-private data structure passed
* as argument to hwmon_device_register_with_info().
* @type: Sensor type
* @attr: Sensor attribute
* @channel:
* Channel number
* The function returns the file permissions.
* If the return value is 0, no attribute will be created.
* @read: Read callback. Optional. If not provided, attributes
* will not be readable.
* Parameters are:
* @dev: Pointer to hardware monitoring device
* @type: Sensor type
* @attr: Sensor attribute
* @channel:
* Channel number
* @val: Pointer to returned value
* The function returns 0 on success or a negative error number.
* @write: Write callback. Optional. If not provided, attributes
* will not be writable.
* Parameters are:
* @dev: Pointer to hardware monitoring device
* @type: Sensor type
* @attr: Sensor attribute
* @channel:
* Channel number
* @val: Value to write
* The function returns 0 on success or a negative error number.
*/
struct hwmon_ops {
umode_t (*is_visible)(const void *drvdata, enum hwmon_sensor_types type,
u32 attr, int channel);
int (*read)(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val);
int (*write)(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long val);
};
/**
* Channel information
* @type: Channel type.
* @config: Pointer to NULL-terminated list of channel parameters.
* Use for per-channel attributes.
*/
struct hwmon_channel_info {
enum hwmon_sensor_types type;
const u32 *config;
};
/**
* Chip configuration
* @ops: Pointer to hwmon operations.
* @info: Null-terminated list of channel information.
*/
struct hwmon_chip_info {
const struct hwmon_ops *ops;
const struct hwmon_channel_info **info;
};
struct device *hwmon_device_register(struct device *dev); struct device *hwmon_device_register(struct device *dev);
struct device * struct device *
hwmon_device_register_with_groups(struct device *dev, const char *name, hwmon_device_register_with_groups(struct device *dev, const char *name,
@ -26,6 +358,16 @@ struct device *
devm_hwmon_device_register_with_groups(struct device *dev, const char *name, devm_hwmon_device_register_with_groups(struct device *dev, const char *name,
void *drvdata, void *drvdata,
const struct attribute_group **groups); 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 hwmon_device_unregister(struct device *dev);
void devm_hwmon_device_unregister(struct device *dev); void devm_hwmon_device_unregister(struct device *dev);