Commit 1c59e1ed authored by Linus Torvalds's avatar Linus Torvalds

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

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

Pull hwmon updates from Guenter Roeck:

 - new drivers for TMP108 and TC654

 - hwmon core code cleanup

 - coretemp driver cleanup

 - fix overflow issues in several drivers

 - minor fixes, cleanups and enhancements in various drivers

* tag 'hwmon-for-linus-v4.10' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (41 commits)
  hwmon: (g762) Fix overflows and crash seen when writing limit attributes
  hwmon: (emcw201) Fix overflows seen when writing into limit attributes
  hwmon: (emc2103) Fix overflows seen when temperature limit attributes
  hwmon: (lm85) Fix overflows seen when writing voltage limit attributes
  hwmon: (lm87) Fix overflow seen when writing voltage limit attributes
  hwmon: (nct7802) Fix overflows seen when writing into limit attributes
  hwmon: (adt7470) Fix overflows seen when writing into limit attributes
  hwmon: (adt7462) Fix overflows seen when writing into limit attributes
  hwmon: (adm1026) Fix overflows seen when writing into limit attributes
  hwmon: (adm1025) Fix overflows seen when writing voltage limits
  hwmon: (via-cputemp) Convert to hotplug state machine
  devicetree: hwmon: Add documentation for TMP108 driver.
  hwmon: Add Texas Instruments TMP108 temperature sensor driver.
  hwmon: (core) Simplify sysfs attribute name allocation
  hwmon: (core) Rename groups parameter in API to extra_groups
  hwmon: (core) Explain why at least two attribute groups are allocated
  hwmon: (core) Make is_visible callback truly mandatory
  hwmon: (core) Deprecate hwmon_device_register()
  hwmon: (core) Clarify use of chip attributes
  hwmon: (core) Add support for string attributes to new API
  ...
parents bb3dd056 4fccd4a1
mcp3021 properties
Required properties:
- compatible: Must be one of the following:
- "microchip,mcp3021" for mcp3021
- "microchip,mcp3221" for mcp3221
- reg: I2C address
Optional properties:
- reference-voltage-microvolt
Reference voltage in microvolt (uV)
Example:
mcp3021@4d {
compatible = "microchip,mcp3021";
reg = <0x4d>;
reference-voltage-microvolt = <4500000>; /* 4.5 V */
};
TMP108 temperature sensor
-------------------------
This device supports I2C only.
Requires node properties:
- compatible : "ti,tmp108"
- reg : the I2C address of the device. This is 0x48, 0x49, 0x4a, or 0x4b.
Example:
tmp108@48 {
compatible = "ti,tmp108";
reg = <0x48>;
};
...@@ -124,6 +124,8 @@ microchip,mcp4662-502 Microchip 8-bit Dual I2C Digital Potentiometer with NV Mem ...@@ -124,6 +124,8 @@ microchip,mcp4662-502 Microchip 8-bit Dual I2C Digital Potentiometer with NV Mem
microchip,mcp4662-103 Microchip 8-bit Dual I2C Digital Potentiometer with NV Memory (10k) microchip,mcp4662-103 Microchip 8-bit Dual I2C Digital Potentiometer with NV Memory (10k)
microchip,mcp4662-503 Microchip 8-bit Dual I2C Digital Potentiometer with NV Memory (50k) microchip,mcp4662-503 Microchip 8-bit Dual I2C Digital Potentiometer with NV Memory (50k)
microchip,mcp4662-104 Microchip 8-bit Dual I2C Digital Potentiometer with NV Memory (100k) microchip,mcp4662-104 Microchip 8-bit Dual I2C Digital Potentiometer with NV Memory (100k)
microchip,tc654 PWM Fan Speed Controller With Fan Fault Detection
microchip,tc655 PWM Fan Speed Controller With Fan Fault Detection
miramems,da226 MiraMEMS DA226 2-axis 14-bit digital accelerometer miramems,da226 MiraMEMS DA226 2-axis 14-bit digital accelerometer
miramems,da280 MiraMEMS DA280 3-axis 14-bit digital accelerometer miramems,da280 MiraMEMS DA280 3-axis 14-bit digital accelerometer
miramems,da311 MiraMEMS DA311 3-axis 12-bit digital accelerometer miramems,da311 MiraMEMS DA311 3-axis 12-bit digital accelerometer
......
...@@ -23,7 +23,6 @@ Each hardware monitoring driver must #include <linux/hwmon.h> and, in most ...@@ -23,7 +23,6 @@ Each hardware monitoring driver must #include <linux/hwmon.h> and, in most
cases, <linux/hwmon-sysfs.h>. linux/hwmon.h declares the following cases, <linux/hwmon-sysfs.h>. linux/hwmon.h declares the following
register/unregister functions: register/unregister functions:
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,
void *drvdata, void *drvdata,
...@@ -38,36 +37,31 @@ struct device * ...@@ -38,36 +37,31 @@ struct device *
hwmon_device_register_with_info(struct device *dev, hwmon_device_register_with_info(struct device *dev,
const char *name, void *drvdata, const char *name, void *drvdata,
const struct hwmon_chip_info *info, const struct hwmon_chip_info *info,
const struct attribute_group **groups); const struct attribute_group **extra_groups);
struct device * struct device *
devm_hwmon_device_register_with_info(struct device *dev, devm_hwmon_device_register_with_info(struct device *dev,
const char *name, const char *name,
void *drvdata, void *drvdata,
const struct hwmon_chip_info *info, const struct hwmon_chip_info *info,
const struct attribute_group **groups); const struct attribute_group **extra_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);
hwmon_device_register registers a hardware monitoring device. The parameter hwmon_device_register_with_groups registers a hardware monitoring device.
of this function is a pointer to the parent device. The first parameter of this function is a pointer to the parent device.
This function returns a pointer to the newly created hardware monitoring device The name parameter is a pointer to the hwmon device name. The registration
or PTR_ERR for failure. If this registration function is used, hardware function wil create a name sysfs attribute pointing to this name.
monitoring sysfs attributes are expected to have been created and attached to The drvdata parameter is the pointer to the local driver data.
the parent device prior to calling hwmon_device_register. A name attribute must hwmon_device_register_with_groups will attach this pointer to the newly
have been created by the caller. allocated hwmon device. The pointer can be retrieved by the driver using
dev_get_drvdata() on the hwmon device pointer. The groups parameter is
hwmon_device_register_with_groups is similar to hwmon_device_register. However,
it has additional parameters. The name parameter is a pointer to the hwmon
device name. The registration function wil create a name sysfs attribute
pointing to this name. The drvdata parameter is the pointer to the local
driver data. hwmon_device_register_with_groups will attach this pointer
to the newly allocated hwmon device. The pointer can be retrieved by the driver
using dev_get_drvdata() on the hwmon device pointer. The groups parameter is
a pointer to a list of sysfs attribute groups. The list must be NULL terminated. a pointer to a list of sysfs attribute groups. The list must be NULL terminated.
hwmon_device_register_with_groups creates the hwmon device with name attribute hwmon_device_register_with_groups creates the hwmon device with name attribute
as well as all sysfs attributes attached to the hwmon device. as well as all sysfs attributes attached to the hwmon device.
This function returns a pointer to the newly created hardware monitoring device
or PTR_ERR for failure.
devm_hwmon_device_register_with_groups is similar to 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
...@@ -87,13 +81,13 @@ hwmon_device_unregister deregisters a registered hardware monitoring device. ...@@ -87,13 +81,13 @@ 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, hwmon_device_register_with_groups, or hwmon_device_register_with_groups or hwmon_device_register_with_info.
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 and if the automatic the call to devm_hwmon_device_register_with_groups or
(device managed) removal would be too late. hwmon_device_register_with_info and if the automatic (device managed)
removal would be too late.
Using devm_hwmon_device_register_with_info() Using devm_hwmon_device_register_with_info()
-------------------------------------------- --------------------------------------------
...@@ -106,9 +100,9 @@ const char *name Device name ...@@ -106,9 +100,9 @@ const char *name Device name
void *drvdata Driver private data void *drvdata Driver private data
const struct hwmon_chip_info *info const struct hwmon_chip_info *info
Pointer to chip description. Pointer to chip description.
const struct attribute_group **groups const struct attribute_group **extra_groups
Null-terminated list of additional sysfs attribute Null-terminated list of additional non-standard
groups. sysfs attribute groups.
This function returns a pointer to the created hardware monitoring device This function returns a pointer to the created hardware monitoring device
on success and a negative error code for failure. on success and a negative error code for failure.
...@@ -160,7 +154,7 @@ It contains following fields: ...@@ -160,7 +154,7 @@ It contains following fields:
* type: The hardware monitoring sensor type. * type: The hardware monitoring sensor type.
Supported sensor types are Supported sensor types are
* hwmon_chip A virtual sensor type, used to describe attributes * hwmon_chip A virtual sensor type, used to describe attributes
which apply to the entire chip. * which are not bound to a specific input or output
* hwmon_temp Temperature sensor * hwmon_temp Temperature sensor
* hwmon_in Voltage sensor * hwmon_in Voltage sensor
* hwmon_curr Current sensor * hwmon_curr Current sensor
...@@ -293,9 +287,9 @@ Driver-provided sysfs attributes ...@@ -293,9 +287,9 @@ Driver-provided sysfs attributes
If the hardware monitoring device is registered with If the hardware monitoring device is registered with
hwmon_device_register_with_info or devm_hwmon_device_register_with_info, 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 it is most likely not necessary to provide sysfs attributes. Only additional
sysfs attributes need to be provided when one of those registration functions non-standard sysfs attributes need to be provided when one of those registration
is used. 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.
......
Kernel driver tc654
===================
Supported chips:
* Microship TC654 and TC655
Prefix: 'tc654'
Datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/20001734C.pdf
Authors:
Chris Packham <chris.packham@alliedtelesis.co.nz>
Masahiko Iwamoto <iwamoto@allied-telesis.co.jp>
Description
-----------
This driver implements support for the Microchip TC654 and TC655.
The TC654 uses the 2-wire interface compatible with the SMBUS 2.0
specification. The TC654 has two (2) inputs for measuring fan RPM and
one (1) PWM output which can be used for fan control.
Configuration Notes
-------------------
Ordinarily the pwm1_mode ABI is used for controlling the pwm output
mode. However, for this chip the output is always pwm, and the
pwm1_mode determines if the pwm output is controlled via the pwm1 value
or via the Vin analog input.
Setting pwm1_mode to 1 will cause the pwm output to be driven based on
the pwm1 value. Setting pwm1_mode to 0 will cause the pwm output to be
driven based on the Vin input.
Kernel driver tmp108
====================
Supported chips:
* Texas Instruments TMP108
Prefix: 'tmp108'
Addresses scanned: none
Datasheet: http://www.ti.com/product/tmp108
Author:
John Muir <john@jmuir.com>
Description
-----------
The Texas Instruments TMP108 implements one temperature sensor. An alert pin
can be set when temperatures exceed minimum or maximum values plus or minus a
hysteresis value. (This driver does not support interrupts for the alert pin,
and the device runs in comparator mode.)
The sensor is accurate to 0.75C over the range of -25 to +85 C, and to 1.0
degree from -40 to +125 C. Resolution of the sensor is 0.0625 degree. The
operating temperature has a minimum of -55 C and a maximum of +150 C.
Hysteresis values can be set to 0, 1, 2, or 4C.
The TMP108 has a programmable update rate that can select between 8, 4, 1, and
0.5 Hz.
By default the TMP108 reads the temperature continuously. To conserve power,
the TMP108 has a one-shot mode where the device is normally shut-down. When a
one shot is requested the temperature is read, the result can be retrieved,
and then the device is shut down automatically. (This driver only supports
continuous mode.)
The driver provides the common sysfs-interface for temperatures (see
Documentation/hwmon/sysfs-interface under Temperatures).
...@@ -907,6 +907,17 @@ config SENSORS_MCP3021 ...@@ -907,6 +907,17 @@ config SENSORS_MCP3021
This driver can also be built as a module. If so, the module This driver can also be built as a module. If so, the module
will be called mcp3021. will be called mcp3021.
config SENSORS_TC654
tristate "Microchip TC654/TC655 and compatibles"
depends on I2C
help
If you say yes here you get support for TC654 and TC655.
The TC654 and TC655 are PWM mode fan speed controllers with
FanSense technology for use with brushless DC fans.
This driver can also be built as a module. If so, the module
will be called tc654.
config SENSORS_MENF21BMC_HWMON config SENSORS_MENF21BMC_HWMON
tristate "MEN 14F021P00 BMC Hardware Monitoring" tristate "MEN 14F021P00 BMC Hardware Monitoring"
depends on MFD_MENF21BMC depends on MFD_MENF21BMC
...@@ -1068,8 +1079,8 @@ config SENSORS_LM90 ...@@ -1068,8 +1079,8 @@ config SENSORS_LM90
LM86, LM89 and LM99, Analog Devices ADM1032, ADT7461, and ADT7461A, LM86, LM89 and LM99, Analog Devices ADM1032, ADT7461, and ADT7461A,
Maxim MAX6646, MAX6647, MAX6648, MAX6649, MAX6657, MAX6658, MAX6659, Maxim MAX6646, MAX6647, MAX6648, MAX6649, MAX6657, MAX6658, MAX6659,
MAX6680, MAX6681, MAX6692, MAX6695, MAX6696, ON Semiconductor NCT1008, MAX6680, MAX6681, MAX6692, MAX6695, MAX6696, ON Semiconductor NCT1008,
Winbond/Nuvoton W83L771W/G/AWG/ASG, Philips SA56004, and GMT G781 Winbond/Nuvoton W83L771W/G/AWG/ASG, Philips SA56004, GMT G781, and
sensor chips. Texas Instruments TMP451 sensor chips.
This driver can also be built as a module. If so, the module This driver can also be built as a module. If so, the module
will be called lm90. will be called lm90.
...@@ -1591,6 +1602,17 @@ config SENSORS_TMP103 ...@@ -1591,6 +1602,17 @@ config SENSORS_TMP103
This driver can also be built as a module. If so, the module This driver can also be built as a module. If so, the module
will be called tmp103. will be called tmp103.
config SENSORS_TMP108
tristate "Texas Instruments TMP108"
depends on I2C
select REGMAP_I2C
help
If you say yes here you get support for Texas Instruments TMP108
sensor chips.
This driver can also be built as a module. If so, the module
will be called tmp108.
config SENSORS_TMP401 config SENSORS_TMP401
tristate "Texas Instruments TMP401 and compatibles" tristate "Texas Instruments TMP401 and compatibles"
depends on I2C depends on I2C
......
...@@ -122,6 +122,7 @@ obj-$(CONFIG_SENSORS_MAX6697) += max6697.o ...@@ -122,6 +122,7 @@ obj-$(CONFIG_SENSORS_MAX6697) += max6697.o
obj-$(CONFIG_SENSORS_MAX31790) += max31790.o obj-$(CONFIG_SENSORS_MAX31790) += max31790.o
obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o
obj-$(CONFIG_SENSORS_TC654) += tc654.o
obj-$(CONFIG_SENSORS_MENF21BMC_HWMON) += menf21bmc_hwmon.o obj-$(CONFIG_SENSORS_MENF21BMC_HWMON) += menf21bmc_hwmon.o
obj-$(CONFIG_SENSORS_NCT6683) += nct6683.o obj-$(CONFIG_SENSORS_NCT6683) += nct6683.o
obj-$(CONFIG_SENSORS_NCT6775) += nct6775.o obj-$(CONFIG_SENSORS_NCT6775) += nct6775.o
...@@ -152,6 +153,7 @@ obj-$(CONFIG_SENSORS_TC74) += tc74.o ...@@ -152,6 +153,7 @@ obj-$(CONFIG_SENSORS_TC74) += tc74.o
obj-$(CONFIG_SENSORS_THMC50) += thmc50.o obj-$(CONFIG_SENSORS_THMC50) += thmc50.o
obj-$(CONFIG_SENSORS_TMP102) += tmp102.o obj-$(CONFIG_SENSORS_TMP102) += tmp102.o
obj-$(CONFIG_SENSORS_TMP103) += tmp103.o obj-$(CONFIG_SENSORS_TMP103) += tmp103.o
obj-$(CONFIG_SENSORS_TMP108) += tmp108.o
obj-$(CONFIG_SENSORS_TMP401) += tmp401.o obj-$(CONFIG_SENSORS_TMP401) += tmp401.o
obj-$(CONFIG_SENSORS_TMP421) += tmp421.o obj-$(CONFIG_SENSORS_TMP421) += tmp421.o
obj-$(CONFIG_SENSORS_TWL4030_MADC)+= twl4030-madc-hwmon.o obj-$(CONFIG_SENSORS_TWL4030_MADC)+= twl4030-madc-hwmon.o
......
...@@ -93,7 +93,7 @@ static const int in_scale[6] = { 2500, 2250, 3300, 5000, 12000, 3300 }; ...@@ -93,7 +93,7 @@ static const int in_scale[6] = { 2500, 2250, 3300, 5000, 12000, 3300 };
#define IN_FROM_REG(reg, scale) (((reg) * (scale) + 96) / 192) #define IN_FROM_REG(reg, scale) (((reg) * (scale) + 96) / 192)
#define IN_TO_REG(val, scale) ((val) <= 0 ? 0 : \ #define IN_TO_REG(val, scale) ((val) <= 0 ? 0 : \
(val) * 192 >= (scale) * 255 ? 255 : \ (val) >= (scale) * 255 / 192 ? 255 : \
((val) * 192 + (scale) / 2) / (scale)) ((val) * 192 + (scale) / 2) / (scale))
#define TEMP_FROM_REG(reg) ((reg) * 1000) #define TEMP_FROM_REG(reg) ((reg) * 1000)
......
...@@ -197,8 +197,9 @@ static int adm1026_scaling[] = { /* .001 Volts */ ...@@ -197,8 +197,9 @@ static int adm1026_scaling[] = { /* .001 Volts */
}; };
#define NEG12_OFFSET 16000 #define NEG12_OFFSET 16000
#define SCALE(val, from, to) (((val)*(to) + ((from)/2))/(from)) #define SCALE(val, from, to) (((val)*(to) + ((from)/2))/(from))
#define INS_TO_REG(n, val) (clamp_val(SCALE(val, adm1026_scaling[n], 192),\ #define INS_TO_REG(n, val) \
0, 255)) SCALE(clamp_val(val, 0, 255 * adm1026_scaling[n] / 192), \
adm1026_scaling[n], 192)
#define INS_FROM_REG(n, val) (SCALE(val, 192, adm1026_scaling[n])) #define INS_FROM_REG(n, val) (SCALE(val, 192, adm1026_scaling[n]))
/* /*
...@@ -215,11 +216,11 @@ static int adm1026_scaling[] = { /* .001 Volts */ ...@@ -215,11 +216,11 @@ static int adm1026_scaling[] = { /* .001 Volts */
#define DIV_TO_REG(val) ((val) >= 8 ? 3 : (val) >= 4 ? 2 : (val) >= 2 ? 1 : 0) #define DIV_TO_REG(val) ((val) >= 8 ? 3 : (val) >= 4 ? 2 : (val) >= 2 ? 1 : 0)
/* Temperature is reported in 1 degC increments */ /* Temperature is reported in 1 degC increments */
#define TEMP_TO_REG(val) (clamp_val(((val) + ((val) < 0 ? -500 : 500)) \ #define TEMP_TO_REG(val) DIV_ROUND_CLOSEST(clamp_val(val, -128000, 127000), \
/ 1000, -127, 127)) 1000)
#define TEMP_FROM_REG(val) ((val) * 1000) #define TEMP_FROM_REG(val) ((val) * 1000)
#define OFFSET_TO_REG(val) (clamp_val(((val) + ((val) < 0 ? -500 : 500)) \ #define OFFSET_TO_REG(val) DIV_ROUND_CLOSEST(clamp_val(val, -128000, 127000), \
/ 1000, -127, 127)) 1000)
#define OFFSET_FROM_REG(val) ((val) * 1000) #define OFFSET_FROM_REG(val) ((val) * 1000)
#define PWM_TO_REG(val) (clamp_val(val, 0, 255)) #define PWM_TO_REG(val) (clamp_val(val, 0, 255))
...@@ -233,7 +234,8 @@ static int adm1026_scaling[] = { /* .001 Volts */ ...@@ -233,7 +234,8 @@ static int adm1026_scaling[] = { /* .001 Volts */
* indicates that the DAC could be used to drive the fans, but in our * indicates that the DAC could be used to drive the fans, but in our
* example board (Arima HDAMA) it isn't connected to the fans at all. * example board (Arima HDAMA) it isn't connected to the fans at all.
*/ */
#define DAC_TO_REG(val) (clamp_val(((((val) * 255) + 500) / 2500), 0, 255)) #define DAC_TO_REG(val) DIV_ROUND_CLOSEST(clamp_val(val, 0, 2500) * 255, \
2500)
#define DAC_FROM_REG(val) (((val) * 2500) / 255) #define DAC_FROM_REG(val) (((val) * 2500) / 255)
/* /*
...@@ -593,7 +595,10 @@ static ssize_t set_in16_min(struct device *dev, struct device_attribute *attr, ...@@ -593,7 +595,10 @@ static ssize_t set_in16_min(struct device *dev, struct device_attribute *attr,
return err; return err;
mutex_lock(&data->update_lock); mutex_lock(&data->update_lock);
data->in_min[16] = INS_TO_REG(16, val + NEG12_OFFSET); data->in_min[16] = INS_TO_REG(16,
clamp_val(val, INT_MIN,
INT_MAX - NEG12_OFFSET) +
NEG12_OFFSET);
adm1026_write_value(client, ADM1026_REG_IN_MIN[16], data->in_min[16]); adm1026_write_value(client, ADM1026_REG_IN_MIN[16], data->in_min[16]);
mutex_unlock(&data->update_lock); mutex_unlock(&data->update_lock);
return count; return count;
...@@ -618,7 +623,10 @@ static ssize_t set_in16_max(struct device *dev, struct device_attribute *attr, ...@@ -618,7 +623,10 @@ static ssize_t set_in16_max(struct device *dev, struct device_attribute *attr,
return err; return err;
mutex_lock(&data->update_lock); mutex_lock(&data->update_lock);
data->in_max[16] = INS_TO_REG(16, val+NEG12_OFFSET); data->in_max[16] = INS_TO_REG(16,
clamp_val(val, INT_MIN,
INT_MAX - NEG12_OFFSET) +
NEG12_OFFSET);
adm1026_write_value(client, ADM1026_REG_IN_MAX[16], data->in_max[16]); adm1026_write_value(client, ADM1026_REG_IN_MAX[16], data->in_max[16]);
mutex_unlock(&data->update_lock); mutex_unlock(&data->update_lock);
return count; return count;
......
...@@ -98,13 +98,15 @@ static inline unsigned int IN_FROM_REG(u8 reg, int n) ...@@ -98,13 +98,15 @@ static inline unsigned int IN_FROM_REG(u8 reg, int n)
static inline u8 IN_TO_REG(unsigned long val, int n) static inline u8 IN_TO_REG(unsigned long val, int n)
{ {
return clamp_val(SCALE(val, 192, nom_mv[n]), 0, 255); val = clamp_val(val, 0, nom_mv[n] * 255 / 192);
return SCALE(val, 192, nom_mv[n]);
} }
/* temperature range: -40..125, 127 disables temperature alarm */ /* temperature range: -40..125, 127 disables temperature alarm */
static inline s8 TEMP_TO_REG(long val) static inline s8 TEMP_TO_REG(long val)
{ {
return clamp_val(SCALE(val, 1, 1000), -40, 127); val = clamp_val(val, -40000, 127000);
return SCALE(val, 1, 1000);
} }
/* two fans, each with low fan speed limit */ /* two fans, each with low fan speed limit */
...@@ -122,7 +124,8 @@ static inline unsigned int FAN_FROM_REG(u8 reg, u8 div) ...@@ -122,7 +124,8 @@ static inline unsigned int FAN_FROM_REG(u8 reg, u8 div)
/* analog out 0..1250mV */ /* analog out 0..1250mV */
static inline u8 AOUT_TO_REG(unsigned long val) static inline u8 AOUT_TO_REG(unsigned long val)
{ {
return clamp_val(SCALE(val, 255, 1250), 0, 255); val = clamp_val(val, 0, 1250);
return SCALE(val, 255, 1250);
} }
static inline unsigned int AOUT_FROM_REG(u8 reg) static inline unsigned int AOUT_FROM_REG(u8 reg)
......
...@@ -55,7 +55,7 @@ struct adt7411_data { ...@@ -55,7 +55,7 @@ struct adt7411_data {
struct mutex device_lock; /* for "atomic" device accesses */ struct mutex device_lock; /* for "atomic" device accesses */
struct mutex update_lock; struct mutex update_lock;
unsigned long next_update; unsigned long next_update;
int vref_cached; long vref_cached;
struct i2c_client *client; struct i2c_client *client;
bool use_ext_temp; bool use_ext_temp;
}; };
...@@ -114,85 +114,6 @@ static int adt7411_modify_bit(struct i2c_client *client, u8 reg, u8 bit, ...@@ -114,85 +114,6 @@ static int adt7411_modify_bit(struct i2c_client *client, u8 reg, u8 bit,
return ret; return ret;
} }
static ssize_t adt7411_show_vdd(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct adt7411_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
int ret = adt7411_read_10_bit(client, ADT7411_REG_INT_TEMP_VDD_LSB,
ADT7411_REG_VDD_MSB, 2);
return ret < 0 ? ret : sprintf(buf, "%u\n", ret * 7000 / 1024);
}
static ssize_t adt7411_show_temp(struct device *dev,
struct device_attribute *attr, char *buf)
{
int nr = to_sensor_dev_attr(attr)->index;
struct adt7411_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
int val;
struct {
u8 low;
u8 high;
} reg[2] = {
{ ADT7411_REG_INT_TEMP_VDD_LSB, ADT7411_REG_INT_TEMP_MSB },
{ ADT7411_REG_EXT_TEMP_AIN14_LSB,
ADT7411_REG_EXT_TEMP_AIN1_MSB },
};
val = adt7411_read_10_bit(client, reg[nr].low, reg[nr].high, 0);
if (val < 0)
return val;
val = val & 0x200 ? val - 0x400 : val; /* 10 bit signed */
return sprintf(buf, "%d\n", val * 250);
}
static ssize_t adt7411_show_input(struct device *dev,
struct device_attribute *attr, char *buf)
{
int nr = to_sensor_dev_attr(attr)->index;
struct adt7411_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
int val;
u8 lsb_reg, lsb_shift;
mutex_lock(&data->update_lock);
if (time_after_eq(jiffies, data->next_update)) {
val = i2c_smbus_read_byte_data(client, ADT7411_REG_CFG3);
if (val < 0)
goto exit_unlock;
if (val & ADT7411_CFG3_REF_VDD) {
val = adt7411_read_10_bit(client,
ADT7411_REG_INT_TEMP_VDD_LSB,
ADT7411_REG_VDD_MSB, 2);
if (val < 0)
goto exit_unlock;
data->vref_cached = val * 7000 / 1024;
} else {
data->vref_cached = 2250;
}
data->next_update = jiffies + HZ;
}
lsb_reg = ADT7411_REG_EXT_TEMP_AIN14_LSB + (nr >> 2);
lsb_shift = 2 * (nr & 0x03);
val = adt7411_read_10_bit(client, lsb_reg,
ADT7411_REG_EXT_TEMP_AIN1_MSB + nr, lsb_shift);
if (val < 0)
goto exit_unlock;
val = sprintf(buf, "%u\n", val * data->vref_cached / 1024);
exit_unlock:
mutex_unlock(&data->update_lock);
return val;
}
static ssize_t adt7411_show_bit(struct device *dev, static ssize_t adt7411_show_bit(struct device *dev,
struct device_attribute *attr, char *buf) struct device_attribute *attr, char *buf)
{ {
...@@ -228,65 +149,157 @@ static ssize_t adt7411_set_bit(struct device *dev, ...@@ -228,65 +149,157 @@ 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 SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, adt7411_show_temp, NULL, 0);
static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, adt7411_show_temp, NULL, 1);
static DEVICE_ATTR(in0_input, S_IRUGO, adt7411_show_vdd, NULL);
static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, adt7411_show_input, NULL, 0);
static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, adt7411_show_input, NULL, 1);
static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, adt7411_show_input, NULL, 2);
static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, adt7411_show_input, NULL, 3);
static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, adt7411_show_input, NULL, 4);
static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, adt7411_show_input, NULL, 5);
static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, adt7411_show_input, NULL, 6);
static SENSOR_DEVICE_ATTR(in8_input, S_IRUGO, adt7411_show_input, NULL, 7);
static ADT7411_BIT_ATTR(no_average, ADT7411_REG_CFG2, ADT7411_CFG2_DISABLE_AVG); static ADT7411_BIT_ATTR(no_average, ADT7411_REG_CFG2, ADT7411_CFG2_DISABLE_AVG);
static ADT7411_BIT_ATTR(fast_sampling, ADT7411_REG_CFG3, ADT7411_CFG3_ADC_CLK_225); static ADT7411_BIT_ATTR(fast_sampling, ADT7411_REG_CFG3, ADT7411_CFG3_ADC_CLK_225);
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[] = {
&sensor_dev_attr_temp1_input.dev_attr.attr,
&sensor_dev_attr_temp2_input.dev_attr.attr,
&dev_attr_in0_input.attr,
&sensor_dev_attr_in1_input.dev_attr.attr,
&sensor_dev_attr_in2_input.dev_attr.attr,
&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_no_average.dev_attr.attr, &sensor_dev_attr_no_average.dev_attr.attr,
&sensor_dev_attr_fast_sampling.dev_attr.attr, &sensor_dev_attr_fast_sampling.dev_attr.attr,
&sensor_dev_attr_adc_ref_vdd.dev_attr.attr, &sensor_dev_attr_adc_ref_vdd.dev_attr.attr,
NULL NULL
}; };
ATTRIBUTE_GROUPS(adt7411);
static int adt7411_read_in_vdd(struct device *dev, u32 attr, long *val)
{
struct adt7411_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
int ret;
switch (attr) {
case hwmon_in_input:
ret = adt7411_read_10_bit(client, ADT7411_REG_INT_TEMP_VDD_LSB,
ADT7411_REG_VDD_MSB, 2);
if (ret < 0)
return ret;
*val = ret * 7000 / 1024;
return 0;
default:
return -EOPNOTSUPP;
}
}
static umode_t adt7411_attrs_visible(struct kobject *kobj, static int adt7411_read_in_chan(struct device *dev, u32 attr, int channel,
struct attribute *attr, int index) long *val)
{ {
struct device *dev = container_of(kobj, struct device, kobj);
struct adt7411_data *data = dev_get_drvdata(dev); struct adt7411_data *data = dev_get_drvdata(dev);
bool visible = true; struct i2c_client *client = data->client;
int ret;
int lsb_reg, lsb_shift;
int nr = channel - 1;
mutex_lock(&data->update_lock);
if (time_after_eq(jiffies, data->next_update)) {
ret = i2c_smbus_read_byte_data(client, ADT7411_REG_CFG3);
if (ret < 0)
goto exit_unlock;
if (ret & ADT7411_CFG3_REF_VDD) {
ret = adt7411_read_in_vdd(dev, hwmon_in_input,
&data->vref_cached);
if (ret < 0)
goto exit_unlock;
} else {
data->vref_cached = 2250;
}
data->next_update = jiffies + HZ;
}
if (attr == &sensor_dev_attr_temp2_input.dev_attr.attr) switch (attr) {
visible = data->use_ext_temp; case hwmon_in_input:
else if (attr == &sensor_dev_attr_in1_input.dev_attr.attr || lsb_reg = ADT7411_REG_EXT_TEMP_AIN14_LSB + (nr >> 2);
attr == &sensor_dev_attr_in2_input.dev_attr.attr) lsb_shift = 2 * (nr & 0x03);
visible = !data->use_ext_temp; ret = adt7411_read_10_bit(client, lsb_reg,
ADT7411_REG_EXT_TEMP_AIN1_MSB + nr,
lsb_shift);
if (ret < 0)
goto exit_unlock;
*val = ret * data->vref_cached / 1024;
ret = 0;
break;
default:
ret = -EOPNOTSUPP;
break;
}
exit_unlock:
mutex_unlock(&data->update_lock);
return ret;
}
return visible ? attr->mode : 0; static int adt7411_read_in(struct device *dev, u32 attr, int channel,
long *val)
{
if (channel == 0)
return adt7411_read_in_vdd(dev, attr, val);
else
return adt7411_read_in_chan(dev, attr, channel, val);
} }
static const struct attribute_group adt7411_group = { static int adt7411_read_temp(struct device *dev, u32 attr, int channel,
.attrs = adt7411_attrs, long *val)
.is_visible = adt7411_attrs_visible, {
}; struct adt7411_data *data = dev_get_drvdata(dev);
__ATTRIBUTE_GROUPS(adt7411); struct i2c_client *client = data->client;
int ret, regl, regh;
switch (attr) {
case hwmon_temp_input:
regl = channel ? ADT7411_REG_EXT_TEMP_AIN14_LSB :
ADT7411_REG_INT_TEMP_VDD_LSB;
regh = channel ? ADT7411_REG_EXT_TEMP_AIN1_MSB :
ADT7411_REG_INT_TEMP_MSB;
ret = adt7411_read_10_bit(client, regl, regh, 0);
if (ret < 0)
return ret;
ret = ret & 0x200 ? ret - 0x400 : ret; /* 10 bit signed */
*val = ret * 250;
return 0;
default:
return -EOPNOTSUPP;
}
}
static int adt7411_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
switch (type) {
case hwmon_in:
return adt7411_read_in(dev, attr, channel, val);
case hwmon_temp:
return adt7411_read_temp(dev, attr, channel, val);
default:
return -EOPNOTSUPP;
}
}
static umode_t adt7411_is_visible(const void *_data,
enum hwmon_sensor_types type,
u32 attr, int channel)
{
const struct adt7411_data *data = _data;
switch (type) {
case hwmon_in:
if (channel > 0 && channel < 3)
return data->use_ext_temp ? 0 : S_IRUGO;
else
return S_IRUGO;
case hwmon_temp:
if (channel == 1)
return data->use_ext_temp ? S_IRUGO : 0;
else
return S_IRUGO;
default:
return 0;
}
}
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)
...@@ -358,6 +371,51 @@ static int adt7411_init_device(struct adt7411_data *data) ...@@ -358,6 +371,51 @@ static int adt7411_init_device(struct adt7411_data *data)
return i2c_smbus_write_byte_data(data->client, ADT7411_REG_CFG1, val); return i2c_smbus_write_byte_data(data->client, ADT7411_REG_CFG1, val);
} }
static const u32 adt7411_in_config[] = {
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 adt7411_in = {
.type = hwmon_in,
.config = adt7411_in_config,
};
static const u32 adt7411_temp_config[] = {
HWMON_T_INPUT,
HWMON_T_INPUT,
0
};
static const struct hwmon_channel_info adt7411_temp = {
.type = hwmon_temp,
.config = adt7411_temp_config,
};
static const struct hwmon_channel_info *adt7411_info[] = {
&adt7411_in,
&adt7411_temp,
NULL
};
static const struct hwmon_ops adt7411_hwmon_ops = {
.is_visible = adt7411_is_visible,
.read = adt7411_read,
};
static const struct hwmon_chip_info adt7411_chip_info = {
.ops = &adt7411_hwmon_ops,
.info = adt7411_info,
};
static int adt7411_probe(struct i2c_client *client, static int adt7411_probe(struct i2c_client *client,
const struct i2c_device_id *id) const struct i2c_device_id *id)
{ {
...@@ -382,8 +440,9 @@ static int adt7411_probe(struct i2c_client *client, ...@@ -382,8 +440,9 @@ static int adt7411_probe(struct i2c_client *client,
/* force update on first occasion */ /* force update on first occasion */
data->next_update = jiffies; data->next_update = jiffies;
hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
data, data,
&adt7411_chip_info,
adt7411_groups); adt7411_groups);
return PTR_ERR_OR_ZERO(hwmon_dev); return PTR_ERR_OR_ZERO(hwmon_dev);
} }
......
...@@ -810,8 +810,8 @@ static ssize_t set_temp_min(struct device *dev, ...@@ -810,8 +810,8 @@ static ssize_t set_temp_min(struct device *dev,
if (kstrtol(buf, 10, &temp) || !temp_enabled(data, attr->index)) if (kstrtol(buf, 10, &temp) || !temp_enabled(data, attr->index))
return -EINVAL; return -EINVAL;
temp = clamp_val(temp, -64000, 191000);
temp = DIV_ROUND_CLOSEST(temp, 1000) + 64; temp = DIV_ROUND_CLOSEST(temp, 1000) + 64;
temp = clamp_val(temp, 0, 255);
mutex_lock(&data->lock); mutex_lock(&data->lock);
data->temp_min[attr->index] = temp; data->temp_min[attr->index] = temp;
...@@ -848,8 +848,8 @@ static ssize_t set_temp_max(struct device *dev, ...@@ -848,8 +848,8 @@ static ssize_t set_temp_max(struct device *dev,
if (kstrtol(buf, 10, &temp) || !temp_enabled(data, attr->index)) if (kstrtol(buf, 10, &temp) || !temp_enabled(data, attr->index))
return -EINVAL; return -EINVAL;
temp = clamp_val(temp, -64000, 191000);
temp = DIV_ROUND_CLOSEST(temp, 1000) + 64; temp = DIV_ROUND_CLOSEST(temp, 1000) + 64;
temp = clamp_val(temp, 0, 255);
mutex_lock(&data->lock); mutex_lock(&data->lock);
data->temp_max[attr->index] = temp; data->temp_max[attr->index] = temp;
...@@ -912,9 +912,9 @@ static ssize_t set_volt_max(struct device *dev, ...@@ -912,9 +912,9 @@ static ssize_t set_volt_max(struct device *dev,
if (kstrtol(buf, 10, &temp) || !x) if (kstrtol(buf, 10, &temp) || !x)
return -EINVAL; return -EINVAL;
temp = clamp_val(temp, 0, 255 * x / 1000);
temp *= 1000; /* convert mV to uV */ temp *= 1000; /* convert mV to uV */
temp = DIV_ROUND_CLOSEST(temp, x); temp = DIV_ROUND_CLOSEST(temp, x);
temp = clamp_val(temp, 0, 255);
mutex_lock(&data->lock); mutex_lock(&data->lock);
data->volt_max[attr->index] = temp; data->volt_max[attr->index] = temp;
...@@ -954,9 +954,9 @@ static ssize_t set_volt_min(struct device *dev, ...@@ -954,9 +954,9 @@ static ssize_t set_volt_min(struct device *dev,
if (kstrtol(buf, 10, &temp) || !x) if (kstrtol(buf, 10, &temp) || !x)
return -EINVAL; return -EINVAL;
temp = clamp_val(temp, 0, 255 * x / 1000);
temp *= 1000; /* convert mV to uV */ temp *= 1000; /* convert mV to uV */
temp = DIV_ROUND_CLOSEST(temp, x); temp = DIV_ROUND_CLOSEST(temp, x);
temp = clamp_val(temp, 0, 255);
mutex_lock(&data->lock); mutex_lock(&data->lock);
data->volt_min[attr->index] = temp; data->volt_min[attr->index] = temp;
...@@ -1220,8 +1220,8 @@ static ssize_t set_pwm_hyst(struct device *dev, ...@@ -1220,8 +1220,8 @@ static ssize_t set_pwm_hyst(struct device *dev,
if (kstrtol(buf, 10, &temp)) if (kstrtol(buf, 10, &temp))
return -EINVAL; return -EINVAL;
temp = clamp_val(temp, 0, 15000);
temp = DIV_ROUND_CLOSEST(temp, 1000); temp = DIV_ROUND_CLOSEST(temp, 1000);
temp = clamp_val(temp, 0, 15);
/* package things up */ /* package things up */
temp &= ADT7462_PWM_HYST_MASK; temp &= ADT7462_PWM_HYST_MASK;
...@@ -1306,8 +1306,8 @@ static ssize_t set_pwm_tmin(struct device *dev, ...@@ -1306,8 +1306,8 @@ static ssize_t set_pwm_tmin(struct device *dev,
if (kstrtol(buf, 10, &temp)) if (kstrtol(buf, 10, &temp))
return -EINVAL; return -EINVAL;
temp = clamp_val(temp, -64000, 191000);
temp = DIV_ROUND_CLOSEST(temp, 1000) + 64; temp = DIV_ROUND_CLOSEST(temp, 1000) + 64;
temp = clamp_val(temp, 0, 255);
mutex_lock(&data->lock); mutex_lock(&data->lock);
data->pwm_tmin[attr->index] = temp; data->pwm_tmin[attr->index] = temp;
......
...@@ -483,8 +483,8 @@ static ssize_t set_temp_min(struct device *dev, ...@@ -483,8 +483,8 @@ static ssize_t set_temp_min(struct device *dev,
if (kstrtol(buf, 10, &temp)) if (kstrtol(buf, 10, &temp))
return -EINVAL; return -EINVAL;
temp = clamp_val(temp, -128000, 127000);
temp = DIV_ROUND_CLOSEST(temp, 1000); temp = DIV_ROUND_CLOSEST(temp, 1000);
temp = clamp_val(temp, -128, 127);
mutex_lock(&data->lock); mutex_lock(&data->lock);
data->temp_min[attr->index] = temp; data->temp_min[attr->index] = temp;
...@@ -517,8 +517,8 @@ static ssize_t set_temp_max(struct device *dev, ...@@ -517,8 +517,8 @@ static ssize_t set_temp_max(struct device *dev,
if (kstrtol(buf, 10, &temp)) if (kstrtol(buf, 10, &temp))
return -EINVAL; return -EINVAL;
temp = clamp_val(temp, -128000, 127000);
temp = DIV_ROUND_CLOSEST(temp, 1000); temp = DIV_ROUND_CLOSEST(temp, 1000);
temp = clamp_val(temp, -128, 127);
mutex_lock(&data->lock); mutex_lock(&data->lock);
data->temp_max[attr->index] = temp; data->temp_max[attr->index] = temp;
...@@ -880,8 +880,8 @@ static ssize_t set_pwm_tmin(struct device *dev, ...@@ -880,8 +880,8 @@ static ssize_t set_pwm_tmin(struct device *dev,
if (kstrtol(buf, 10, &temp)) if (kstrtol(buf, 10, &temp))
return -EINVAL; return -EINVAL;
temp = clamp_val(temp, -128000, 127000);
temp = DIV_ROUND_CLOSEST(temp, 1000); temp = DIV_ROUND_CLOSEST(temp, 1000);
temp = clamp_val(temp, -128, 127);
mutex_lock(&data->lock); mutex_lock(&data->lock);
data->pwm_tmin[attr->index] = temp; data->pwm_tmin[attr->index] = temp;
......
...@@ -188,8 +188,8 @@ static struct amc6821_data *amc6821_update_device(struct device *dev) ...@@ -188,8 +188,8 @@ static struct amc6821_data *amc6821_update_device(struct device *dev)
!data->valid) { !data->valid) {
for (i = 0; i < TEMP_IDX_LEN; i++) for (i = 0; i < TEMP_IDX_LEN; i++)
data->temp[i] = i2c_smbus_read_byte_data(client, data->temp[i] = (int8_t)i2c_smbus_read_byte_data(
temp_reg[i]); client, temp_reg[i]);
data->stat1 = i2c_smbus_read_byte_data(client, data->stat1 = i2c_smbus_read_byte_data(client,
AMC6821_REG_STAT1); AMC6821_REG_STAT1);
......
This diff is collapsed.
...@@ -166,7 +166,7 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *da, ...@@ -166,7 +166,7 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *da,
if (res) if (res)
return res; return res;
val = (val * 10 / 625) * 8; val = (clamp_val(val, -128000, 128000) * 10 / 625) * 8;
mutex_lock(&data->update_lock); mutex_lock(&data->update_lock);
data->temp[attr->index] = val; data->temp[attr->index] = val;
......
...@@ -251,7 +251,7 @@ static ssize_t set_temp_min(struct device *dev, struct device_attribute *da, ...@@ -251,7 +251,7 @@ static ssize_t set_temp_min(struct device *dev, struct device_attribute *da,
if (result < 0) if (result < 0)
return result; return result;
val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), -63, 127); val = DIV_ROUND_CLOSEST(clamp_val(val, -63000, 127000), 1000);
mutex_lock(&data->update_lock); mutex_lock(&data->update_lock);
data->temp_min[nr] = val; data->temp_min[nr] = val;
...@@ -273,7 +273,7 @@ static ssize_t set_temp_max(struct device *dev, struct device_attribute *da, ...@@ -273,7 +273,7 @@ static ssize_t set_temp_max(struct device *dev, struct device_attribute *da,
if (result < 0) if (result < 0)
return result; return result;
val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), -63, 127); val = DIV_ROUND_CLOSEST(clamp_val(val, -63000, 127000), 1000);
mutex_lock(&data->update_lock); mutex_lock(&data->update_lock);
data->temp_max[nr] = val; data->temp_max[nr] = val;
......
...@@ -215,12 +215,13 @@ static ssize_t set_in(struct device *dev, struct device_attribute *devattr, ...@@ -215,12 +215,13 @@ static ssize_t set_in(struct device *dev, struct device_attribute *devattr,
if (err < 0) if (err < 0)
return err; return err;
val = DIV_ROUND_CLOSEST(val * 0xC0, nominal_mv[nr]); val = clamp_val(val, 0, 255 * nominal_mv[nr] / 192);
val = DIV_ROUND_CLOSEST(val * 192, nominal_mv[nr]);
reg = (sf == min) ? EMC6W201_REG_IN_LOW(nr) reg = (sf == min) ? EMC6W201_REG_IN_LOW(nr)
: EMC6W201_REG_IN_HIGH(nr); : EMC6W201_REG_IN_HIGH(nr);
mutex_lock(&data->update_lock); mutex_lock(&data->update_lock);
data->in[sf][nr] = clamp_val(val, 0, 255); data->in[sf][nr] = val;
err = emc6w201_write8(client, reg, data->in[sf][nr]); err = emc6w201_write8(client, reg, data->in[sf][nr]);
mutex_unlock(&data->update_lock); mutex_unlock(&data->update_lock);
...@@ -252,12 +253,13 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *devattr, ...@@ -252,12 +253,13 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *devattr,
if (err < 0) if (err < 0)
return err; return err;
val = clamp_val(val, -127000, 127000);
val = DIV_ROUND_CLOSEST(val, 1000); val = DIV_ROUND_CLOSEST(val, 1000);
reg = (sf == min) ? EMC6W201_REG_TEMP_LOW(nr) reg = (sf == min) ? EMC6W201_REG_TEMP_LOW(nr)
: EMC6W201_REG_TEMP_HIGH(nr); : EMC6W201_REG_TEMP_HIGH(nr);
mutex_lock(&data->update_lock); mutex_lock(&data->update_lock);
data->temp[sf][nr] = clamp_val(val, -127, 127); data->temp[sf][nr] = val;
err = emc6w201_write8(client, reg, data->temp[sf][nr]); err = emc6w201_write8(client, reg, data->temp[sf][nr]);
mutex_unlock(&data->update_lock); mutex_unlock(&data->update_lock);
......
...@@ -193,14 +193,17 @@ static inline unsigned int rpm_from_cnt(u8 cnt, u32 clk_freq, u16 p, ...@@ -193,14 +193,17 @@ static inline unsigned int rpm_from_cnt(u8 cnt, u32 clk_freq, u16 p,
* Convert fan RPM value from sysfs into count value for fan controller * Convert fan RPM value from sysfs into count value for fan controller
* register (FAN_SET_CNT). * register (FAN_SET_CNT).
*/ */
static inline unsigned char cnt_from_rpm(u32 rpm, u32 clk_freq, u16 p, static inline unsigned char cnt_from_rpm(unsigned long rpm, u32 clk_freq, u16 p,
u8 clk_div, u8 gear_mult) u8 clk_div, u8 gear_mult)
{ {
unsigned long f1 = clk_freq * 30 * gear_mult;
unsigned long f2 = p * clk_div;
if (!rpm) /* to stop the fan, set cnt to 255 */ if (!rpm) /* to stop the fan, set cnt to 255 */
return 0xff; return 0xff;
return clamp_val(((clk_freq * 30 * gear_mult) / (rpm * p * clk_div)), rpm = clamp_val(rpm, f1 / (255 * f2), ULONG_MAX / f2);
0, 255); return DIV_ROUND_CLOSEST(f1, rpm * f2);
} }
/* helper to grab and cache data, at most one time per second */ /* helper to grab and cache data, at most one time per second */
......
...@@ -38,12 +38,15 @@ struct hwmon_device { ...@@ -38,12 +38,15 @@ struct hwmon_device {
#define to_hwmon_device(d) container_of(d, struct hwmon_device, dev) #define to_hwmon_device(d) container_of(d, struct hwmon_device, dev)
#define MAX_SYSFS_ATTR_NAME_LENGTH 32
struct hwmon_device_attribute { struct hwmon_device_attribute {
struct device_attribute dev_attr; struct device_attribute dev_attr;
const struct hwmon_ops *ops; const struct hwmon_ops *ops;
enum hwmon_sensor_types type; enum hwmon_sensor_types type;
u32 attr; u32 attr;
int index; int index;
char name[MAX_SYSFS_ATTR_NAME_LENGTH];
}; };
#define to_hwmon_attr(d) \ #define to_hwmon_attr(d) \
...@@ -178,6 +181,22 @@ static ssize_t hwmon_attr_show(struct device *dev, ...@@ -178,6 +181,22 @@ static ssize_t hwmon_attr_show(struct device *dev,
return sprintf(buf, "%ld\n", val); return sprintf(buf, "%ld\n", val);
} }
static ssize_t hwmon_attr_show_string(struct device *dev,
struct device_attribute *devattr,
char *buf)
{
struct hwmon_device_attribute *hattr = to_hwmon_attr(devattr);
char *s;
int ret;
ret = hattr->ops->read_string(dev, hattr->type, hattr->attr,
hattr->index, &s);
if (ret < 0)
return ret;
return sprintf(buf, "%s\n", s);
}
static ssize_t hwmon_attr_store(struct device *dev, static ssize_t hwmon_attr_store(struct device *dev,
struct device_attribute *devattr, struct device_attribute *devattr,
const char *buf, size_t count) const char *buf, size_t count)
...@@ -205,6 +224,17 @@ static int hwmon_attr_base(enum hwmon_sensor_types type) ...@@ -205,6 +224,17 @@ static int hwmon_attr_base(enum hwmon_sensor_types type)
return 1; return 1;
} }
static bool is_string_attr(enum hwmon_sensor_types type, u32 attr)
{
return (type == hwmon_temp && attr == hwmon_temp_label) ||
(type == hwmon_in && attr == hwmon_in_label) ||
(type == hwmon_curr && attr == hwmon_curr_label) ||
(type == hwmon_power && attr == hwmon_power_label) ||
(type == hwmon_energy && attr == hwmon_energy_label) ||
(type == hwmon_humidity && attr == hwmon_humidity_label) ||
(type == hwmon_fan && attr == hwmon_fan_label);
}
static struct attribute *hwmon_genattr(struct device *dev, static struct attribute *hwmon_genattr(struct device *dev,
const void *drvdata, const void *drvdata,
enum hwmon_sensor_types type, enum hwmon_sensor_types type,
...@@ -218,6 +248,7 @@ static struct attribute *hwmon_genattr(struct device *dev, ...@@ -218,6 +248,7 @@ static struct attribute *hwmon_genattr(struct device *dev,
struct attribute *a; struct attribute *a;
umode_t mode; umode_t mode;
char *name; char *name;
bool is_string = is_string_attr(type, attr);
/* The attribute is invisible if there is no template string */ /* The attribute is invisible if there is no template string */
if (!template) if (!template)
...@@ -227,32 +258,31 @@ static struct attribute *hwmon_genattr(struct device *dev, ...@@ -227,32 +258,31 @@ static struct attribute *hwmon_genattr(struct device *dev,
if (!mode) if (!mode)
return ERR_PTR(-ENOENT); return ERR_PTR(-ENOENT);
if ((mode & S_IRUGO) && !ops->read) if ((mode & S_IRUGO) && ((is_string && !ops->read_string) ||
(!is_string && !ops->read)))
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
if ((mode & S_IWUGO) && !ops->write) if ((mode & S_IWUGO) && !ops->write)
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
hattr = devm_kzalloc(dev, sizeof(*hattr), GFP_KERNEL);
if (!hattr)
return ERR_PTR(-ENOMEM);
if (type == hwmon_chip) { if (type == hwmon_chip) {
name = (char *)template; name = (char *)template;
} else { } else {
name = devm_kzalloc(dev, strlen(template) + 16, GFP_KERNEL); scnprintf(hattr->name, sizeof(hattr->name), template,
if (!name)
return ERR_PTR(-ENOMEM);
scnprintf(name, strlen(template) + 16, template,
index + hwmon_attr_base(type)); index + hwmon_attr_base(type));
name = hattr->name;
} }
hattr = devm_kzalloc(dev, sizeof(*hattr), GFP_KERNEL);
if (!hattr)
return ERR_PTR(-ENOMEM);
hattr->type = type; hattr->type = type;
hattr->attr = attr; hattr->attr = attr;
hattr->index = index; hattr->index = index;
hattr->ops = ops; hattr->ops = ops;
dattr = &hattr->dev_attr; dattr = &hattr->dev_attr;
dattr->show = hwmon_attr_show; dattr->show = is_string ? hwmon_attr_show_string : hwmon_attr_show;
dattr->store = hwmon_attr_store; dattr->store = hwmon_attr_store;
a = &dattr->attr; a = &dattr->attr;
...@@ -263,7 +293,11 @@ static struct attribute *hwmon_genattr(struct device *dev, ...@@ -263,7 +293,11 @@ static struct attribute *hwmon_genattr(struct device *dev,
return a; return a;
} }
static const char * const hwmon_chip_attr_templates[] = { /*
* Chip attributes are not attribute templates but actual sysfs attributes.
* See hwmon_genattr() for special handling.
*/
static const char * const hwmon_chip_attrs[] = {
[hwmon_chip_temp_reset_history] = "temp_reset_history", [hwmon_chip_temp_reset_history] = "temp_reset_history",
[hwmon_chip_in_reset_history] = "in_reset_history", [hwmon_chip_in_reset_history] = "in_reset_history",
[hwmon_chip_curr_reset_history] = "curr_reset_history", [hwmon_chip_curr_reset_history] = "curr_reset_history",
...@@ -400,7 +434,7 @@ static const char * const hwmon_pwm_attr_templates[] = { ...@@ -400,7 +434,7 @@ static const char * const hwmon_pwm_attr_templates[] = {
}; };
static const char * const *__templates[] = { static const char * const *__templates[] = {
[hwmon_chip] = hwmon_chip_attr_templates, [hwmon_chip] = hwmon_chip_attrs,
[hwmon_temp] = hwmon_temp_attr_templates, [hwmon_temp] = hwmon_temp_attr_templates,
[hwmon_in] = hwmon_in_attr_templates, [hwmon_in] = hwmon_in_attr_templates,
[hwmon_curr] = hwmon_curr_attr_templates, [hwmon_curr] = hwmon_curr_attr_templates,
...@@ -412,7 +446,7 @@ static const char * const *__templates[] = { ...@@ -412,7 +446,7 @@ static const char * const *__templates[] = {
}; };
static const int __templates_size[] = { static const int __templates_size[] = {
[hwmon_chip] = ARRAY_SIZE(hwmon_chip_attr_templates), [hwmon_chip] = ARRAY_SIZE(hwmon_chip_attrs),
[hwmon_temp] = ARRAY_SIZE(hwmon_temp_attr_templates), [hwmon_temp] = ARRAY_SIZE(hwmon_temp_attr_templates),
[hwmon_in] = ARRAY_SIZE(hwmon_in_attr_templates), [hwmon_in] = ARRAY_SIZE(hwmon_in_attr_templates),
[hwmon_curr] = ARRAY_SIZE(hwmon_curr_attr_templates), [hwmon_curr] = ARRAY_SIZE(hwmon_curr_attr_templates),
...@@ -526,9 +560,9 @@ __hwmon_device_register(struct device *dev, const char *name, void *drvdata, ...@@ -526,9 +560,9 @@ __hwmon_device_register(struct device *dev, const char *name, void *drvdata,
hdev = &hwdev->dev; hdev = &hwdev->dev;
if (chip && chip->ops->is_visible) { if (chip) {
struct attribute **attrs; struct attribute **attrs;
int ngroups = 2; int ngroups = 2; /* terminating NULL plus &hwdev->groups */
if (groups) if (groups)
for (i = 0; groups[i]; i++) for (i = 0; groups[i]; i++)
...@@ -572,7 +606,7 @@ __hwmon_device_register(struct device *dev, const char *name, void *drvdata, ...@@ -572,7 +606,7 @@ __hwmon_device_register(struct device *dev, const char *name, void *drvdata,
if (err) if (err)
goto free_hwmon; goto free_hwmon;
if (chip && chip->ops->is_visible && chip->ops->read && if (chip && chip->ops->read &&
chip->info[0]->type == hwmon_chip && chip->info[0]->type == hwmon_chip &&
(chip->info[0]->config[0] & HWMON_C_REGISTER_TZ)) { (chip->info[0]->config[0] & HWMON_C_REGISTER_TZ)) {
const struct hwmon_channel_info **info = chip->info; const struct hwmon_channel_info **info = chip->info;
...@@ -626,8 +660,8 @@ EXPORT_SYMBOL_GPL(hwmon_device_register_with_groups); ...@@ -626,8 +660,8 @@ EXPORT_SYMBOL_GPL(hwmon_device_register_with_groups);
* @dev: the parent device * @dev: the parent device
* @name: hwmon name attribute * @name: hwmon name attribute
* @drvdata: driver data to attach to created device * @drvdata: driver data to attach to created device
* @info: Pointer to hwmon chip information * @info: pointer to hwmon chip information
* @groups - pointer to list of driver specific attribute groups * @extra_groups: pointer to list of additional non-standard attribute groups
* *
* hwmon_device_unregister() must be called when the device is no * hwmon_device_unregister() must be called when the device is no
* longer needed. * longer needed.
...@@ -638,12 +672,12 @@ struct device * ...@@ -638,12 +672,12 @@ struct device *
hwmon_device_register_with_info(struct device *dev, const char *name, hwmon_device_register_with_info(struct device *dev, const char *name,
void *drvdata, void *drvdata,
const struct hwmon_chip_info *chip, const struct hwmon_chip_info *chip,
const struct attribute_group **groups) const struct attribute_group **extra_groups)
{ {
if (chip && (!chip->ops || !chip->info)) if (chip && (!chip->ops || !chip->ops->is_visible || !chip->info))
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
return __hwmon_device_register(dev, name, drvdata, chip, groups); return __hwmon_device_register(dev, name, drvdata, chip, extra_groups);
} }
EXPORT_SYMBOL_GPL(hwmon_device_register_with_info); EXPORT_SYMBOL_GPL(hwmon_device_register_with_info);
...@@ -658,6 +692,9 @@ EXPORT_SYMBOL_GPL(hwmon_device_register_with_info); ...@@ -658,6 +692,9 @@ EXPORT_SYMBOL_GPL(hwmon_device_register_with_info);
*/ */
struct device *hwmon_device_register(struct device *dev) struct device *hwmon_device_register(struct device *dev)
{ {
dev_warn(dev,
"hwmon_device_register() is deprecated. Please convert the driver to use hwmon_device_register_with_info().\n");
return hwmon_device_register_with_groups(dev, NULL, NULL, NULL); return hwmon_device_register_with_groups(dev, NULL, NULL, NULL);
} }
EXPORT_SYMBOL_GPL(hwmon_device_register); EXPORT_SYMBOL_GPL(hwmon_device_register);
......
...@@ -136,7 +136,8 @@ static const int lm85_scaling[] = { /* .001 Volts */ ...@@ -136,7 +136,8 @@ static const int lm85_scaling[] = { /* .001 Volts */
#define SCALE(val, from, to) (((val) * (to) + ((from) / 2)) / (from)) #define SCALE(val, from, to) (((val) * (to) + ((from) / 2)) / (from))
#define INS_TO_REG(n, val) \ #define INS_TO_REG(n, val) \
clamp_val(SCALE(val, lm85_scaling[n], 192), 0, 255) SCALE(clamp_val(val, 0, 255 * lm85_scaling[n] / 192), \
lm85_scaling[n], 192)
#define INSEXT_FROM_REG(n, val, ext) \ #define INSEXT_FROM_REG(n, val, ext) \
SCALE(((val) << 4) + (ext), 192 << 4, lm85_scaling[n]) SCALE(((val) << 4) + (ext), 192 << 4, lm85_scaling[n])
......
...@@ -121,7 +121,7 @@ static u8 LM87_REG_TEMP_LOW[3] = { 0x3A, 0x38, 0x2C }; ...@@ -121,7 +121,7 @@ static u8 LM87_REG_TEMP_LOW[3] = { 0x3A, 0x38, 0x2C };
#define IN_FROM_REG(reg, scale) (((reg) * (scale) + 96) / 192) #define IN_FROM_REG(reg, scale) (((reg) * (scale) + 96) / 192)
#define IN_TO_REG(val, scale) ((val) <= 0 ? 0 : \ #define IN_TO_REG(val, scale) ((val) <= 0 ? 0 : \
(val) * 192 >= (scale) * 255 ? 255 : \ (val) >= (scale) * 255 / 192 ? 255 : \
((val) * 192 + (scale) / 2) / (scale)) ((val) * 192 + (scale) / 2) / (scale))
#define TEMP_FROM_REG(reg) ((reg) * 1000) #define TEMP_FROM_REG(reg) ((reg) * 1000)
...@@ -154,7 +154,6 @@ static u8 LM87_REG_TEMP_LOW[3] = { 0x3A, 0x38, 0x2C }; ...@@ -154,7 +154,6 @@ static u8 LM87_REG_TEMP_LOW[3] = { 0x3A, 0x38, 0x2C };
*/ */
struct lm87_data { struct lm87_data {
struct device *hwmon_dev;
struct mutex update_lock; struct mutex update_lock;
char valid; /* zero until following fields are valid */ char valid; /* zero until following fields are valid */
unsigned long last_updated; /* In jiffies */ unsigned long last_updated; /* In jiffies */
...@@ -181,6 +180,8 @@ struct lm87_data { ...@@ -181,6 +180,8 @@ struct lm87_data {
u16 alarms; /* register values, combined */ u16 alarms; /* register values, combined */
u8 vid; /* register values, combined */ u8 vid; /* register values, combined */
u8 vrm; u8 vrm;
const struct attribute_group *attr_groups[6];
}; };
static inline int lm87_read_value(struct i2c_client *client, u8 reg) static inline int lm87_read_value(struct i2c_client *client, u8 reg)
...@@ -195,7 +196,7 @@ static inline int lm87_write_value(struct i2c_client *client, u8 reg, u8 value) ...@@ -195,7 +196,7 @@ static inline int lm87_write_value(struct i2c_client *client, u8 reg, u8 value)
static struct lm87_data *lm87_update_device(struct device *dev) static struct lm87_data *lm87_update_device(struct device *dev)
{ {
struct i2c_client *client = to_i2c_client(dev); struct i2c_client *client = dev_get_drvdata(dev);
struct lm87_data *data = i2c_get_clientdata(client); struct lm87_data *data = i2c_get_clientdata(client);
mutex_lock(&data->update_lock); mutex_lock(&data->update_lock);
...@@ -309,7 +310,7 @@ static ssize_t show_in_max(struct device *dev, ...@@ -309,7 +310,7 @@ static ssize_t show_in_max(struct device *dev,
static ssize_t set_in_min(struct device *dev, struct device_attribute *attr, static ssize_t set_in_min(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count) const char *buf, size_t count)
{ {
struct i2c_client *client = to_i2c_client(dev); struct i2c_client *client = dev_get_drvdata(dev);
struct lm87_data *data = i2c_get_clientdata(client); struct lm87_data *data = i2c_get_clientdata(client);
int nr = to_sensor_dev_attr(attr)->index; int nr = to_sensor_dev_attr(attr)->index;
long val; long val;
...@@ -330,7 +331,7 @@ static ssize_t set_in_min(struct device *dev, struct device_attribute *attr, ...@@ -330,7 +331,7 @@ static ssize_t set_in_min(struct device *dev, struct device_attribute *attr,
static ssize_t set_in_max(struct device *dev, struct device_attribute *attr, static ssize_t set_in_max(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count) const char *buf, size_t count)
{ {
struct i2c_client *client = to_i2c_client(dev); struct i2c_client *client = dev_get_drvdata(dev);
struct lm87_data *data = i2c_get_clientdata(client); struct lm87_data *data = i2c_get_clientdata(client);
int nr = to_sensor_dev_attr(attr)->index; int nr = to_sensor_dev_attr(attr)->index;
long val; long val;
...@@ -396,7 +397,7 @@ static ssize_t show_temp_high(struct device *dev, ...@@ -396,7 +397,7 @@ static ssize_t show_temp_high(struct device *dev,
static ssize_t set_temp_low(struct device *dev, struct device_attribute *attr, static ssize_t set_temp_low(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count) const char *buf, size_t count)
{ {
struct i2c_client *client = to_i2c_client(dev); struct i2c_client *client = dev_get_drvdata(dev);
struct lm87_data *data = i2c_get_clientdata(client); struct lm87_data *data = i2c_get_clientdata(client);
int nr = to_sensor_dev_attr(attr)->index; int nr = to_sensor_dev_attr(attr)->index;
long val; long val;
...@@ -416,7 +417,7 @@ static ssize_t set_temp_low(struct device *dev, struct device_attribute *attr, ...@@ -416,7 +417,7 @@ static ssize_t set_temp_low(struct device *dev, struct device_attribute *attr,
static ssize_t set_temp_high(struct device *dev, struct device_attribute *attr, static ssize_t set_temp_high(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count) const char *buf, size_t count)
{ {
struct i2c_client *client = to_i2c_client(dev); struct i2c_client *client = dev_get_drvdata(dev);
struct lm87_data *data = i2c_get_clientdata(client); struct lm87_data *data = i2c_get_clientdata(client);
int nr = to_sensor_dev_attr(attr)->index; int nr = to_sensor_dev_attr(attr)->index;
long val; long val;
...@@ -495,7 +496,7 @@ static ssize_t show_fan_div(struct device *dev, ...@@ -495,7 +496,7 @@ static ssize_t show_fan_div(struct device *dev,
static ssize_t set_fan_min(struct device *dev, struct device_attribute *attr, static ssize_t set_fan_min(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count) const char *buf, size_t count)
{ {
struct i2c_client *client = to_i2c_client(dev); struct i2c_client *client = dev_get_drvdata(dev);
struct lm87_data *data = i2c_get_clientdata(client); struct lm87_data *data = i2c_get_clientdata(client);
int nr = to_sensor_dev_attr(attr)->index; int nr = to_sensor_dev_attr(attr)->index;
long val; long val;
...@@ -522,7 +523,7 @@ static ssize_t set_fan_min(struct device *dev, struct device_attribute *attr, ...@@ -522,7 +523,7 @@ static ssize_t set_fan_min(struct device *dev, struct device_attribute *attr,
static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr, static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count) const char *buf, size_t count)
{ {
struct i2c_client *client = to_i2c_client(dev); struct i2c_client *client = dev_get_drvdata(dev);
struct lm87_data *data = i2c_get_clientdata(client); struct lm87_data *data = i2c_get_clientdata(client);
int nr = to_sensor_dev_attr(attr)->index; int nr = to_sensor_dev_attr(attr)->index;
long val; long val;
...@@ -635,7 +636,7 @@ static ssize_t show_aout(struct device *dev, struct device_attribute *attr, ...@@ -635,7 +636,7 @@ static ssize_t show_aout(struct device *dev, struct device_attribute *attr,
static ssize_t set_aout(struct device *dev, struct device_attribute *attr, static ssize_t set_aout(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count) const char *buf, size_t count)
{ {
struct i2c_client *client = to_i2c_client(dev); struct i2c_client *client = dev_get_drvdata(dev);
struct lm87_data *data = i2c_get_clientdata(client); struct lm87_data *data = i2c_get_clientdata(client);
long val; long val;
int err; int err;
...@@ -841,23 +842,18 @@ static int lm87_detect(struct i2c_client *client, struct i2c_board_info *info) ...@@ -841,23 +842,18 @@ static int lm87_detect(struct i2c_client *client, struct i2c_board_info *info)
return 0; return 0;
} }
static void lm87_remove_files(struct i2c_client *client) static void lm87_restore_config(void *arg)
{ {
struct device *dev = &client->dev; struct i2c_client *client = arg;
struct lm87_data *data = i2c_get_clientdata(client);
sysfs_remove_group(&dev->kobj, &lm87_group);
sysfs_remove_group(&dev->kobj, &lm87_group_in6); lm87_write_value(client, LM87_REG_CONFIG, data->config);
sysfs_remove_group(&dev->kobj, &lm87_group_fan1);
sysfs_remove_group(&dev->kobj, &lm87_group_in7);
sysfs_remove_group(&dev->kobj, &lm87_group_fan2);
sysfs_remove_group(&dev->kobj, &lm87_group_temp3);
sysfs_remove_group(&dev->kobj, &lm87_group_in0_5);
sysfs_remove_group(&dev->kobj, &lm87_group_vid);
} }
static void lm87_init_client(struct i2c_client *client) static int lm87_init_client(struct i2c_client *client)
{ {
struct lm87_data *data = i2c_get_clientdata(client); struct lm87_data *data = i2c_get_clientdata(client);
int rc;
if (dev_get_platdata(&client->dev)) { if (dev_get_platdata(&client->dev)) {
data->channel = *(u8 *)dev_get_platdata(&client->dev); data->channel = *(u8 *)dev_get_platdata(&client->dev);
...@@ -868,6 +864,10 @@ static void lm87_init_client(struct i2c_client *client) ...@@ -868,6 +864,10 @@ static void lm87_init_client(struct i2c_client *client)
} }
data->config = lm87_read_value(client, LM87_REG_CONFIG) & 0x6F; data->config = lm87_read_value(client, LM87_REG_CONFIG) & 0x6F;
rc = devm_add_action(&client->dev, lm87_restore_config, client);
if (rc)
return rc;
if (!(data->config & 0x01)) { if (!(data->config & 0x01)) {
int i; int i;
...@@ -895,12 +895,15 @@ static void lm87_init_client(struct i2c_client *client) ...@@ -895,12 +895,15 @@ static void lm87_init_client(struct i2c_client *client)
if ((data->config & 0x09) != 0x01) if ((data->config & 0x09) != 0x01)
lm87_write_value(client, LM87_REG_CONFIG, lm87_write_value(client, LM87_REG_CONFIG,
(data->config & 0x77) | 0x01); (data->config & 0x77) | 0x01);
return 0;
} }
static int lm87_probe(struct i2c_client *client, const struct i2c_device_id *id) static int lm87_probe(struct i2c_client *client, const struct i2c_device_id *id)
{ {
struct lm87_data *data; struct lm87_data *data;
struct device *hwmon_dev;
int err; int err;
unsigned int group_tail = 0;
data = devm_kzalloc(&client->dev, sizeof(struct lm87_data), GFP_KERNEL); data = devm_kzalloc(&client->dev, sizeof(struct lm87_data), GFP_KERNEL);
if (!data) if (!data)
...@@ -910,7 +913,9 @@ static int lm87_probe(struct i2c_client *client, const struct i2c_device_id *id) ...@@ -910,7 +913,9 @@ static int lm87_probe(struct i2c_client *client, const struct i2c_device_id *id)
mutex_init(&data->update_lock); mutex_init(&data->update_lock);
/* Initialize the LM87 chip */ /* Initialize the LM87 chip */
lm87_init_client(client); err = lm87_init_client(client);
if (err)
return err;
data->in_scale[0] = 2500; data->in_scale[0] = 2500;
data->in_scale[1] = 2700; data->in_scale[1] = 2700;
...@@ -921,72 +926,34 @@ static int lm87_probe(struct i2c_client *client, const struct i2c_device_id *id) ...@@ -921,72 +926,34 @@ static int lm87_probe(struct i2c_client *client, const struct i2c_device_id *id)
data->in_scale[6] = 1875; data->in_scale[6] = 1875;
data->in_scale[7] = 1875; data->in_scale[7] = 1875;
/* Register sysfs hooks */ /*
err = sysfs_create_group(&client->dev.kobj, &lm87_group); * Construct the list of attributes, the list depends on the
if (err) * configuration of the chip
goto exit_stop; */
data->attr_groups[group_tail++] = &lm87_group;
if (data->channel & CHAN_NO_FAN(0)) { if (data->channel & CHAN_NO_FAN(0))
err = sysfs_create_group(&client->dev.kobj, &lm87_group_in6); data->attr_groups[group_tail++] = &lm87_group_in6;
if (err) else
goto exit_remove; data->attr_groups[group_tail++] = &lm87_group_fan1;
} else {
err = sysfs_create_group(&client->dev.kobj, &lm87_group_fan1); if (data->channel & CHAN_NO_FAN(1))
if (err) data->attr_groups[group_tail++] = &lm87_group_in7;
goto exit_remove; else
} data->attr_groups[group_tail++] = &lm87_group_fan2;
if (data->channel & CHAN_NO_FAN(1)) { if (data->channel & CHAN_TEMP3)
err = sysfs_create_group(&client->dev.kobj, &lm87_group_in7); data->attr_groups[group_tail++] = &lm87_group_temp3;
if (err) else
goto exit_remove; data->attr_groups[group_tail++] = &lm87_group_in0_5;
} else {
err = sysfs_create_group(&client->dev.kobj, &lm87_group_fan2);
if (err)
goto exit_remove;
}
if (data->channel & CHAN_TEMP3) {
err = sysfs_create_group(&client->dev.kobj, &lm87_group_temp3);
if (err)
goto exit_remove;
} else {
err = sysfs_create_group(&client->dev.kobj, &lm87_group_in0_5);
if (err)
goto exit_remove;
}
if (!(data->channel & CHAN_NO_VID)) { if (!(data->channel & CHAN_NO_VID)) {
data->vrm = vid_which_vrm(); data->vrm = vid_which_vrm();
err = sysfs_create_group(&client->dev.kobj, &lm87_group_vid); data->attr_groups[group_tail++] = &lm87_group_vid;
if (err)
goto exit_remove;
}
data->hwmon_dev = hwmon_device_register(&client->dev);
if (IS_ERR(data->hwmon_dev)) {
err = PTR_ERR(data->hwmon_dev);
goto exit_remove;
} }
return 0; hwmon_dev = devm_hwmon_device_register_with_groups(
&client->dev, client->name, client, data->attr_groups);
exit_remove: return PTR_ERR_OR_ZERO(hwmon_dev);
lm87_remove_files(client);
exit_stop:
lm87_write_value(client, LM87_REG_CONFIG, data->config);
return err;
}
static int lm87_remove(struct i2c_client *client)
{
struct lm87_data *data = i2c_get_clientdata(client);
hwmon_device_unregister(data->hwmon_dev);
lm87_remove_files(client);
lm87_write_value(client, LM87_REG_CONFIG, data->config);
return 0;
} }
/* /*
...@@ -1006,7 +973,6 @@ static struct i2c_driver lm87_driver = { ...@@ -1006,7 +973,6 @@ static struct i2c_driver lm87_driver = {
.name = "lm87", .name = "lm87",
}, },
.probe = lm87_probe, .probe = lm87_probe,
.remove = lm87_remove,
.id_table = lm87_id, .id_table = lm87_id,
.detect = lm87_detect, .detect = lm87_detect,
.address_list = normal_i2c, .address_list = normal_i2c,
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
* Copyright (C) 2008-2009, 2012 Freescale Semiconductor, Inc. * Copyright (C) 2008-2009, 2012 Freescale Semiconductor, Inc.
* Author: Mingkai Hu <Mingkai.hu@freescale.com> * Author: Mingkai Hu <Mingkai.hu@freescale.com>
* Reworked by Sven Schuchmann <schuchmann@schleissheimer.de> * Reworked by Sven Schuchmann <schuchmann@schleissheimer.de>
* DT support added by Clemens Gruber <clemens.gruber@pqgruber.com>
* *
* This driver export the value of analog input voltage to sysfs, the * This driver export the value of analog input voltage to sysfs, the
* voltage unit is mV. Through the sysfs interface, lm-sensors tool * voltage unit is mV. Through the sysfs interface, lm-sensors tool
...@@ -22,11 +23,13 @@ ...@@ -22,11 +23,13 @@
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/of.h>
#include <linux/of_device.h>
/* Vdd info */ /* Vdd / reference voltage in millivolt */
#define MCP3021_VDD_MAX 5500 #define MCP3021_VDD_REF_MAX 5500
#define MCP3021_VDD_MIN 2700 #define MCP3021_VDD_REF_MIN 2700
#define MCP3021_VDD_REF 3300 #define MCP3021_VDD_REF_DEFAULT 3300
/* output format */ /* output format */
#define MCP3021_SAR_SHIFT 2 #define MCP3021_SAR_SHIFT 2
...@@ -47,7 +50,7 @@ enum chips { ...@@ -47,7 +50,7 @@ enum chips {
*/ */
struct mcp3021_data { struct mcp3021_data {
struct device *hwmon_dev; struct device *hwmon_dev;
u32 vdd; /* device power supply */ u32 vdd; /* supply and reference voltage in millivolt */
u16 sar_shift; u16 sar_shift;
u16 sar_mask; u16 sar_mask;
u8 output_res; u8 output_res;
...@@ -99,13 +102,14 @@ static ssize_t show_in_input(struct device *dev, struct device_attribute *attr, ...@@ -99,13 +102,14 @@ static ssize_t show_in_input(struct device *dev, struct device_attribute *attr,
return sprintf(buf, "%d\n", in_input); return sprintf(buf, "%d\n", in_input);
} }
static DEVICE_ATTR(in0_input, S_IRUGO, show_in_input, NULL); static DEVICE_ATTR(in0_input, 0444, show_in_input, NULL);
static int mcp3021_probe(struct i2c_client *client, static int mcp3021_probe(struct i2c_client *client,
const struct i2c_device_id *id) const struct i2c_device_id *id)
{ {
int err; int err;
struct mcp3021_data *data = NULL; struct mcp3021_data *data = NULL;
struct device_node *np = client->dev.of_node;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
return -ENODEV; return -ENODEV;
...@@ -117,6 +121,21 @@ static int mcp3021_probe(struct i2c_client *client, ...@@ -117,6 +121,21 @@ static int mcp3021_probe(struct i2c_client *client,
i2c_set_clientdata(client, data); i2c_set_clientdata(client, data);
if (np) {
if (!of_property_read_u32(np, "reference-voltage-microvolt",
&data->vdd))
data->vdd /= 1000;
else
data->vdd = MCP3021_VDD_REF_DEFAULT;
} else {
u32 *pdata = dev_get_platdata(&client->dev);
if (pdata)
data->vdd = *pdata;
else
data->vdd = MCP3021_VDD_REF_DEFAULT;
}
switch (id->driver_data) { switch (id->driver_data) {
case mcp3021: case mcp3021:
data->sar_shift = MCP3021_SAR_SHIFT; data->sar_shift = MCP3021_SAR_SHIFT;
...@@ -131,13 +150,8 @@ static int mcp3021_probe(struct i2c_client *client, ...@@ -131,13 +150,8 @@ static int mcp3021_probe(struct i2c_client *client,
break; break;
} }
if (dev_get_platdata(&client->dev)) { if (data->vdd > MCP3021_VDD_REF_MAX || data->vdd < MCP3021_VDD_REF_MIN)
data->vdd = *(u32 *)dev_get_platdata(&client->dev);
if (data->vdd > MCP3021_VDD_MAX || data->vdd < MCP3021_VDD_MIN)
return -EINVAL; return -EINVAL;
} else {
data->vdd = MCP3021_VDD_REF;
}
err = sysfs_create_file(&client->dev.kobj, &dev_attr_in0_input.attr); err = sysfs_create_file(&client->dev.kobj, &dev_attr_in0_input.attr);
if (err) if (err)
...@@ -173,9 +187,19 @@ static const struct i2c_device_id mcp3021_id[] = { ...@@ -173,9 +187,19 @@ static const struct i2c_device_id mcp3021_id[] = {
}; };
MODULE_DEVICE_TABLE(i2c, mcp3021_id); MODULE_DEVICE_TABLE(i2c, mcp3021_id);
#ifdef CONFIG_OF
static const struct of_device_id of_mcp3021_match[] = {
{ .compatible = "microchip,mcp3021", .data = (void *)mcp3021 },
{ .compatible = "microchip,mcp3221", .data = (void *)mcp3221 },
{ }
};
MODULE_DEVICE_TABLE(of, of_mcp3021_match);
#endif
static struct i2c_driver mcp3021_driver = { static struct i2c_driver mcp3021_driver = {
.driver = { .driver = {
.name = "mcp3021", .name = "mcp3021",
.of_match_table = of_match_ptr(of_mcp3021_match),
}, },
.probe = mcp3021_probe, .probe = mcp3021_probe,
.remove = mcp3021_remove, .remove = mcp3021_remove,
......
...@@ -259,13 +259,15 @@ static int nct7802_read_fan_min(struct nct7802_data *data, u8 reg_fan_low, ...@@ -259,13 +259,15 @@ static int nct7802_read_fan_min(struct nct7802_data *data, u8 reg_fan_low,
ret = 0; ret = 0;
else if (ret) else if (ret)
ret = DIV_ROUND_CLOSEST(1350000U, ret); ret = DIV_ROUND_CLOSEST(1350000U, ret);
else
ret = 1350000U;
abort: abort:
mutex_unlock(&data->access_lock); mutex_unlock(&data->access_lock);
return ret; return ret;
} }
static int nct7802_write_fan_min(struct nct7802_data *data, u8 reg_fan_low, static int nct7802_write_fan_min(struct nct7802_data *data, u8 reg_fan_low,
u8 reg_fan_high, unsigned int limit) u8 reg_fan_high, unsigned long limit)
{ {
int err; int err;
...@@ -326,8 +328,8 @@ static int nct7802_write_voltage(struct nct7802_data *data, int nr, int index, ...@@ -326,8 +328,8 @@ static int nct7802_write_voltage(struct nct7802_data *data, int nr, int index,
int shift = 8 - REG_VOLTAGE_LIMIT_MSB_SHIFT[index - 1][nr]; int shift = 8 - REG_VOLTAGE_LIMIT_MSB_SHIFT[index - 1][nr];
int err; int err;
voltage = clamp_val(voltage, 0, 0x3ff * nct7802_vmul[nr]);
voltage = DIV_ROUND_CLOSEST(voltage, nct7802_vmul[nr]); voltage = DIV_ROUND_CLOSEST(voltage, nct7802_vmul[nr]);
voltage = clamp_val(voltage, 0, 0x3ff);
mutex_lock(&data->access_lock); mutex_lock(&data->access_lock);
err = regmap_write(data->regmap, err = regmap_write(data->regmap,
...@@ -402,7 +404,7 @@ static ssize_t store_temp(struct device *dev, struct device_attribute *attr, ...@@ -402,7 +404,7 @@ static ssize_t store_temp(struct device *dev, struct device_attribute *attr,
if (err < 0) if (err < 0)
return err; return err;
val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), -128, 127); val = DIV_ROUND_CLOSEST(clamp_val(val, -128000, 127000), 1000);
err = regmap_write(data->regmap, nr, val & 0xff); err = regmap_write(data->regmap, nr, val & 0xff);
return err ? : count; return err ? : count;
......
...@@ -499,15 +499,27 @@ static int adm1275_probe(struct i2c_client *client, ...@@ -499,15 +499,27 @@ static int adm1275_probe(struct i2c_client *client,
pindex = 2; pindex = 2;
tindex = 3; tindex = 3;
info->func[0] |= PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT; info->func[0] |= PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT |
PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;
/* Enable VOUT if not enabled (it is disabled by default) */
if (!(config & ADM1278_VOUT_EN)) {
config |= ADM1278_VOUT_EN;
ret = i2c_smbus_write_byte_data(client,
ADM1275_PMON_CONFIG,
config);
if (ret < 0) {
dev_err(&client->dev,
"Failed to enable VOUT monitoring\n");
return -ENODEV;
}
}
if (config & ADM1278_TEMP1_EN) if (config & ADM1278_TEMP1_EN)
info->func[0] |= info->func[0] |=
PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
if (config & ADM1278_VIN_EN) if (config & ADM1278_VIN_EN)
info->func[0] |= PMBUS_HAVE_VIN; info->func[0] |= PMBUS_HAVE_VIN;
if (config & ADM1278_VOUT_EN)
info->func[0] |=
PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;
break; break;
case adm1293: case adm1293:
case adm1294: case adm1294:
......
...@@ -251,6 +251,7 @@ static const struct of_device_id scpi_of_match[] = { ...@@ -251,6 +251,7 @@ static const struct of_device_id scpi_of_match[] = {
{.compatible = "arm,scpi-sensors"}, {.compatible = "arm,scpi-sensors"},
{}, {},
}; };
MODULE_DEVICE_TABLE(of, scpi_of_match);
static struct platform_driver scpi_hwmon_platdrv = { static struct platform_driver scpi_hwmon_platdrv = {
.driver = { .driver = {
......
...@@ -77,14 +77,15 @@ static inline unsigned int IN_FROM_REG(u8 reg, int n) ...@@ -77,14 +77,15 @@ static inline unsigned int IN_FROM_REG(u8 reg, int n)
static inline u8 IN_TO_REG(unsigned long val, int n) static inline u8 IN_TO_REG(unsigned long val, int n)
{ {
return clamp_val(SCALE(val, 192, nom_mv[n]), 0, 255); val = clamp_val(val, 0, nom_mv[n] * 255 / 192);
return SCALE(val, 192, nom_mv[n]);
} }
/* /*
* TEMP: 0.001 degC units (-128C to +127C) * TEMP: 0.001 degC units (-128C to +127C)
* REG: 1C/bit, two's complement * REG: 1C/bit, two's complement
*/ */
static inline s8 TEMP_TO_REG(int val) static inline s8 TEMP_TO_REG(long val)
{ {
return SCALE(clamp_val(val, -128000, 127000), 1, 1000); return SCALE(clamp_val(val, -128000, 127000), 1, 1000);
} }
......
This diff is collapsed.
This diff is collapsed.
...@@ -220,7 +220,7 @@ struct pdev_entry { ...@@ -220,7 +220,7 @@ struct pdev_entry {
static LIST_HEAD(pdev_list); static LIST_HEAD(pdev_list);
static DEFINE_MUTEX(pdev_list_mutex); static DEFINE_MUTEX(pdev_list_mutex);
static int via_cputemp_device_add(unsigned int cpu) static int via_cputemp_online(unsigned int cpu)
{ {
int err; int err;
struct platform_device *pdev; struct platform_device *pdev;
...@@ -261,7 +261,7 @@ static int via_cputemp_device_add(unsigned int cpu) ...@@ -261,7 +261,7 @@ static int via_cputemp_device_add(unsigned int cpu)
return err; return err;
} }
static void via_cputemp_device_remove(unsigned int cpu) static int via_cputemp_down_prep(unsigned int cpu)
{ {
struct pdev_entry *p; struct pdev_entry *p;
...@@ -272,33 +272,13 @@ static void via_cputemp_device_remove(unsigned int cpu) ...@@ -272,33 +272,13 @@ static void via_cputemp_device_remove(unsigned int cpu)
list_del(&p->list); list_del(&p->list);
mutex_unlock(&pdev_list_mutex); mutex_unlock(&pdev_list_mutex);
kfree(p); kfree(p);
return; return 0;
} }
} }
mutex_unlock(&pdev_list_mutex); mutex_unlock(&pdev_list_mutex);
return 0;
} }
static int via_cputemp_cpu_callback(struct notifier_block *nfb,
unsigned long action, void *hcpu)
{
unsigned int cpu = (unsigned long) hcpu;
switch (action) {
case CPU_ONLINE:
case CPU_DOWN_FAILED:
via_cputemp_device_add(cpu);
break;
case CPU_DOWN_PREPARE:
via_cputemp_device_remove(cpu);
break;
}
return NOTIFY_OK;
}
static struct notifier_block via_cputemp_cpu_notifier __refdata = {
.notifier_call = via_cputemp_cpu_callback,
};
static const struct x86_cpu_id __initconst cputemp_ids[] = { static const struct x86_cpu_id __initconst cputemp_ids[] = {
{ X86_VENDOR_CENTAUR, 6, 0xa, }, /* C7 A */ { X86_VENDOR_CENTAUR, 6, 0xa, }, /* C7 A */
{ X86_VENDOR_CENTAUR, 6, 0xd, }, /* C7 D */ { X86_VENDOR_CENTAUR, 6, 0xd, }, /* C7 D */
...@@ -307,9 +287,11 @@ static const struct x86_cpu_id __initconst cputemp_ids[] = { ...@@ -307,9 +287,11 @@ static const struct x86_cpu_id __initconst cputemp_ids[] = {
}; };
MODULE_DEVICE_TABLE(x86cpu, cputemp_ids); MODULE_DEVICE_TABLE(x86cpu, cputemp_ids);
static enum cpuhp_state via_temp_online;
static int __init via_cputemp_init(void) static int __init via_cputemp_init(void)
{ {
int i, err; int err;
if (!x86_match_cpu(cputemp_ids)) if (!x86_match_cpu(cputemp_ids))
return -ENODEV; return -ENODEV;
...@@ -318,58 +300,33 @@ static int __init via_cputemp_init(void) ...@@ -318,58 +300,33 @@ static int __init via_cputemp_init(void)
if (err) if (err)
goto exit; goto exit;
cpu_notifier_register_begin(); err = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "hwmon/via:online",
for_each_online_cpu(i) { via_cputemp_online, via_cputemp_down_prep);
struct cpuinfo_x86 *c = &cpu_data(i); if (err < 0)
goto exit_driver_unreg;
if (c->x86 != 6) via_temp_online = err;
continue;
if (c->x86_model < 0x0a)
continue;
if (c->x86_model > 0x0f) {
pr_warn("Unknown CPU model 0x%x\n", c->x86_model);
continue;
}
via_cputemp_device_add(i);
}
#ifndef CONFIG_HOTPLUG_CPU #ifndef CONFIG_HOTPLUG_CPU
if (list_empty(&pdev_list)) { if (list_empty(&pdev_list)) {
cpu_notifier_register_done();
err = -ENODEV; err = -ENODEV;
goto exit_driver_unreg; goto exit_hp_unreg;
} }
#endif #endif
__register_hotcpu_notifier(&via_cputemp_cpu_notifier);
cpu_notifier_register_done();
return 0; return 0;
#ifndef CONFIG_HOTPLUG_CPU #ifndef CONFIG_HOTPLUG_CPU
exit_hp_unreg:
cpuhp_remove_state_nocalls(via_temp_online);
#endif
exit_driver_unreg: exit_driver_unreg:
platform_driver_unregister(&via_cputemp_driver); platform_driver_unregister(&via_cputemp_driver);
#endif
exit: exit:
return err; return err;
} }
static void __exit via_cputemp_exit(void) static void __exit via_cputemp_exit(void)
{ {
struct pdev_entry *p, *n; cpuhp_remove_state(via_temp_online);
cpu_notifier_register_begin();
__unregister_hotcpu_notifier(&via_cputemp_cpu_notifier);
mutex_lock(&pdev_list_mutex);
list_for_each_entry_safe(p, n, &pdev_list, list) {
platform_device_unregister(p->pdev);
list_del(&p->list);
kfree(p);
}
mutex_unlock(&pdev_list_mutex);
cpu_notifier_register_done();
platform_driver_unregister(&via_cputemp_driver); platform_driver_unregister(&via_cputemp_driver);
} }
......
...@@ -298,8 +298,8 @@ enum hwmon_pwm_attributes { ...@@ -298,8 +298,8 @@ enum hwmon_pwm_attributes {
* Channel number * Channel number
* The function returns the file permissions. * The function returns the file permissions.
* If the return value is 0, no attribute will be created. * If the return value is 0, no attribute will be created.
* @read: Read callback. Optional. If not provided, attributes * @read: Read callback for data attributes. Mandatory if readable
* will not be readable. * data attributes are present.
* Parameters are: * Parameters are:
* @dev: Pointer to hardware monitoring device * @dev: Pointer to hardware monitoring device
* @type: Sensor type * @type: Sensor type
...@@ -308,8 +308,19 @@ enum hwmon_pwm_attributes { ...@@ -308,8 +308,19 @@ enum hwmon_pwm_attributes {
* Channel number * Channel number
* @val: Pointer to returned value * @val: Pointer to returned value
* The function returns 0 on success or a negative error number. * The function returns 0 on success or a negative error number.
* @write: Write callback. Optional. If not provided, attributes * @read_string:
* will not be writable. * Read callback for string attributes. Mandatory if string
* attributes are present.
* Parameters are:
* @dev: Pointer to hardware monitoring device
* @type: Sensor type
* @attr: Sensor attribute
* @channel:
* Channel number
* @str: Pointer to returned string
* The function returns 0 on success or a negative error number.
* @write: Write callback for data attributes. Mandatory if writeable
* data attributes are present.
* Parameters are: * Parameters are:
* @dev: Pointer to hardware monitoring device * @dev: Pointer to hardware monitoring device
* @type: Sensor type * @type: Sensor type
...@@ -324,6 +335,8 @@ struct hwmon_ops { ...@@ -324,6 +335,8 @@ struct hwmon_ops {
u32 attr, int channel); u32 attr, int channel);
int (*read)(struct device *dev, enum hwmon_sensor_types type, int (*read)(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val); u32 attr, int channel, long *val);
int (*read_string)(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, char **str);
int (*write)(struct device *dev, enum hwmon_sensor_types type, int (*write)(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long val); u32 attr, int channel, long val);
}; };
...@@ -349,7 +362,9 @@ struct hwmon_chip_info { ...@@ -349,7 +362,9 @@ struct hwmon_chip_info {
const struct hwmon_channel_info **info; const struct hwmon_channel_info **info;
}; };
/* hwmon_device_register() is deprecated */
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,
void *drvdata, void *drvdata,
...@@ -362,12 +377,12 @@ struct device * ...@@ -362,12 +377,12 @@ struct device *
hwmon_device_register_with_info(struct device *dev, hwmon_device_register_with_info(struct device *dev,
const char *name, void *drvdata, const char *name, void *drvdata,
const struct hwmon_chip_info *info, const struct hwmon_chip_info *info,
const struct attribute_group **groups); const struct attribute_group **extra_groups);
struct device * struct device *
devm_hwmon_device_register_with_info(struct device *dev, devm_hwmon_device_register_with_info(struct device *dev,
const char *name, void *drvdata, const char *name, void *drvdata,
const struct hwmon_chip_info *info, const struct hwmon_chip_info *info,
const struct attribute_group **groups); const struct attribute_group **extra_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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment