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 umode_t adt7411_attrs_visible(struct kobject *kobj, static int adt7411_read_in_vdd(struct device *dev, u32 attr, long *val)
struct attribute *attr, int index)
{ {
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;
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 || ret = adt7411_read_10_bit(client, ADT7411_REG_INT_TEMP_VDD_LSB,
attr == &sensor_dev_attr_in2_input.dev_attr.attr) ADT7411_REG_VDD_MSB, 2);
visible = !data->use_ext_temp; if (ret < 0)
return ret;
*val = ret * 7000 / 1024;
return 0;
default:
return -EOPNOTSUPP;
}
}
static int adt7411_read_in_chan(struct device *dev, u32 attr, int channel,
long *val)
{
struct adt7411_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
return visible ? attr->mode : 0; 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;
}
switch (attr) {
case hwmon_in_input:
lsb_reg = ADT7411_REG_EXT_TEMP_AIN14_LSB + (nr >> 2);
lsb_shift = 2 * (nr & 0x03);
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;
} }
static const struct attribute_group adt7411_group = { static int adt7411_read_in(struct device *dev, u32 attr, int channel,
.attrs = adt7411_attrs, long *val)
.is_visible = adt7411_attrs_visible, {
}; if (channel == 0)
__ATTRIBUTE_GROUPS(adt7411); return adt7411_read_in_vdd(dev, attr, val);
else
return adt7411_read_in_chan(dev, attr, channel, val);
}
static int adt7411_read_temp(struct device *dev, u32 attr, int channel,
long *val)
{
struct adt7411_data *data = dev_get_drvdata(dev);
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,9 +440,10 @@ static int adt7411_probe(struct i2c_client *client, ...@@ -382,9 +440,10 @@ 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_groups); &adt7411_chip_info,
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);
......
...@@ -51,6 +51,7 @@ static int force_tjmax; ...@@ -51,6 +51,7 @@ static int force_tjmax;
module_param_named(tjmax, force_tjmax, int, 0444); module_param_named(tjmax, force_tjmax, int, 0444);
MODULE_PARM_DESC(tjmax, "TjMax value in degrees Celsius"); MODULE_PARM_DESC(tjmax, "TjMax value in degrees Celsius");
#define PKG_SYSFS_ATTR_NO 1 /* Sysfs attribute for package temp */
#define BASE_SYSFS_ATTR_NO 2 /* Sysfs Base attr no for coretemp */ #define BASE_SYSFS_ATTR_NO 2 /* Sysfs Base attr no for coretemp */
#define NUM_REAL_CORES 128 /* Number of Real cores per cpu */ #define NUM_REAL_CORES 128 /* Number of Real cores per cpu */
#define CORETEMP_NAME_LENGTH 19 /* String Length of attrs */ #define CORETEMP_NAME_LENGTH 19 /* String Length of attrs */
...@@ -58,7 +59,6 @@ MODULE_PARM_DESC(tjmax, "TjMax value in degrees Celsius"); ...@@ -58,7 +59,6 @@ MODULE_PARM_DESC(tjmax, "TjMax value in degrees Celsius");
#define TOTAL_ATTRS (MAX_CORE_ATTRS + 1) #define TOTAL_ATTRS (MAX_CORE_ATTRS + 1)
#define MAX_CORE_DATA (NUM_REAL_CORES + BASE_SYSFS_ATTR_NO) #define MAX_CORE_DATA (NUM_REAL_CORES + BASE_SYSFS_ATTR_NO)
#define TO_PHYS_ID(cpu) (cpu_data(cpu).phys_proc_id)
#define TO_CORE_ID(cpu) (cpu_data(cpu).cpu_core_id) #define TO_CORE_ID(cpu) (cpu_data(cpu).cpu_core_id)
#define TO_ATTR_NO(cpu) (TO_CORE_ID(cpu) + BASE_SYSFS_ATTR_NO) #define TO_ATTR_NO(cpu) (TO_CORE_ID(cpu) + BASE_SYSFS_ATTR_NO)
...@@ -102,20 +102,17 @@ struct temp_data { ...@@ -102,20 +102,17 @@ struct temp_data {
/* Platform Data per Physical CPU */ /* Platform Data per Physical CPU */
struct platform_data { struct platform_data {
struct device *hwmon_dev; struct device *hwmon_dev;
u16 phys_proc_id; u16 pkg_id;
struct temp_data *core_data[MAX_CORE_DATA]; struct cpumask cpumask;
struct temp_data *core_data[MAX_CORE_DATA];
struct device_attribute name_attr; struct device_attribute name_attr;
}; };
struct pdev_entry { /* Keep track of how many package pointers we allocated in init() */
struct list_head list; static int max_packages __read_mostly;
struct platform_device *pdev; /* Array of package pointers. Serialized by cpu hotplug lock */
u16 phys_proc_id; static struct platform_device **pkg_devices;
};
static LIST_HEAD(pdev_list);
static DEFINE_MUTEX(pdev_list_mutex);
static ssize_t show_label(struct device *dev, static ssize_t show_label(struct device *dev,
struct device_attribute *devattr, char *buf) struct device_attribute *devattr, char *buf)
...@@ -125,7 +122,7 @@ static ssize_t show_label(struct device *dev, ...@@ -125,7 +122,7 @@ static ssize_t show_label(struct device *dev,
struct temp_data *tdata = pdata->core_data[attr->index]; struct temp_data *tdata = pdata->core_data[attr->index];
if (tdata->is_pkg_data) if (tdata->is_pkg_data)
return sprintf(buf, "Physical id %u\n", pdata->phys_proc_id); return sprintf(buf, "Package id %u\n", pdata->pkg_id);
return sprintf(buf, "Core %u\n", tdata->cpu_core_id); return sprintf(buf, "Core %u\n", tdata->cpu_core_id);
} }
...@@ -138,7 +135,9 @@ static ssize_t show_crit_alarm(struct device *dev, ...@@ -138,7 +135,9 @@ static ssize_t show_crit_alarm(struct device *dev,
struct platform_data *pdata = dev_get_drvdata(dev); struct platform_data *pdata = dev_get_drvdata(dev);
struct temp_data *tdata = pdata->core_data[attr->index]; struct temp_data *tdata = pdata->core_data[attr->index];
mutex_lock(&tdata->update_lock);
rdmsr_on_cpu(tdata->cpu, tdata->status_reg, &eax, &edx); rdmsr_on_cpu(tdata->cpu, tdata->status_reg, &eax, &edx);
mutex_unlock(&tdata->update_lock);
return sprintf(buf, "%d\n", (eax >> 5) & 1); return sprintf(buf, "%d\n", (eax >> 5) & 1);
} }
...@@ -435,18 +434,10 @@ static int chk_ucode_version(unsigned int cpu) ...@@ -435,18 +434,10 @@ static int chk_ucode_version(unsigned int cpu)
static struct platform_device *coretemp_get_pdev(unsigned int cpu) static struct platform_device *coretemp_get_pdev(unsigned int cpu)
{ {
u16 phys_proc_id = TO_PHYS_ID(cpu); int pkgid = topology_logical_package_id(cpu);
struct pdev_entry *p;
mutex_lock(&pdev_list_mutex);
list_for_each_entry(p, &pdev_list, list) if (pkgid >= 0 && pkgid < max_packages)
if (p->phys_proc_id == phys_proc_id) { return pkg_devices[pkgid];
mutex_unlock(&pdev_list_mutex);
return p->pdev;
}
mutex_unlock(&pdev_list_mutex);
return NULL; return NULL;
} }
...@@ -483,21 +474,11 @@ static int create_core_data(struct platform_device *pdev, unsigned int cpu, ...@@ -483,21 +474,11 @@ static int create_core_data(struct platform_device *pdev, unsigned int cpu,
* The attr number is always core id + 2 * The attr number is always core id + 2
* The Pkgtemp will always show up as temp1_*, if available * The Pkgtemp will always show up as temp1_*, if available
*/ */
attr_no = pkg_flag ? 1 : TO_ATTR_NO(cpu); attr_no = pkg_flag ? PKG_SYSFS_ATTR_NO : TO_ATTR_NO(cpu);
if (attr_no > MAX_CORE_DATA - 1) if (attr_no > MAX_CORE_DATA - 1)
return -ERANGE; return -ERANGE;
/*
* Provide a single set of attributes for all HT siblings of a core
* to avoid duplicate sensors (the processor ID and core ID of all
* HT siblings of a core are the same).
* Skip if a HT sibling of this core is already registered.
* This is not an error.
*/
if (pdata->core_data[attr_no] != NULL)
return 0;
tdata = init_temp_data(cpu, pkg_flag); tdata = init_temp_data(cpu, pkg_flag);
if (!tdata) if (!tdata)
return -ENOMEM; return -ENOMEM;
...@@ -539,21 +520,14 @@ static int create_core_data(struct platform_device *pdev, unsigned int cpu, ...@@ -539,21 +520,14 @@ static int create_core_data(struct platform_device *pdev, unsigned int cpu,
return err; return err;
} }
static void coretemp_add_core(unsigned int cpu, int pkg_flag) static void
coretemp_add_core(struct platform_device *pdev, unsigned int cpu, int pkg_flag)
{ {
struct platform_device *pdev = coretemp_get_pdev(cpu); if (create_core_data(pdev, cpu, pkg_flag))
int err;
if (!pdev)
return;
err = create_core_data(pdev, cpu, pkg_flag);
if (err)
dev_err(&pdev->dev, "Adding Core %u failed\n", cpu); dev_err(&pdev->dev, "Adding Core %u failed\n", cpu);
} }
static void coretemp_remove_core(struct platform_data *pdata, static void coretemp_remove_core(struct platform_data *pdata, int indx)
int indx)
{ {
struct temp_data *tdata = pdata->core_data[indx]; struct temp_data *tdata = pdata->core_data[indx];
...@@ -574,7 +548,7 @@ static int coretemp_probe(struct platform_device *pdev) ...@@ -574,7 +548,7 @@ static int coretemp_probe(struct platform_device *pdev)
if (!pdata) if (!pdata)
return -ENOMEM; return -ENOMEM;
pdata->phys_proc_id = pdev->id; pdata->pkg_id = pdev->id;
platform_set_drvdata(pdev, pdata); platform_set_drvdata(pdev, pdata);
pdata->hwmon_dev = devm_hwmon_device_register_with_groups(dev, DRVNAME, pdata->hwmon_dev = devm_hwmon_device_register_with_groups(dev, DRVNAME,
...@@ -602,85 +576,33 @@ static struct platform_driver coretemp_driver = { ...@@ -602,85 +576,33 @@ static struct platform_driver coretemp_driver = {
.remove = coretemp_remove, .remove = coretemp_remove,
}; };
static int coretemp_device_add(unsigned int cpu) static struct platform_device *coretemp_device_add(unsigned int cpu)
{ {
int err; int err, pkgid = topology_logical_package_id(cpu);
struct platform_device *pdev; struct platform_device *pdev;
struct pdev_entry *pdev_entry;
mutex_lock(&pdev_list_mutex); if (pkgid < 0)
return ERR_PTR(-ENOMEM);
pdev = platform_device_alloc(DRVNAME, TO_PHYS_ID(cpu)); pdev = platform_device_alloc(DRVNAME, pkgid);
if (!pdev) { if (!pdev)
err = -ENOMEM; return ERR_PTR(-ENOMEM);
pr_err("Device allocation failed\n");
goto exit;
}
pdev_entry = kzalloc(sizeof(struct pdev_entry), GFP_KERNEL);
if (!pdev_entry) {
err = -ENOMEM;
goto exit_device_put;
}
err = platform_device_add(pdev); err = platform_device_add(pdev);
if (err) { if (err) {
pr_err("Device addition failed (%d)\n", err); platform_device_put(pdev);
goto exit_device_free; return ERR_PTR(err);
}
pdev_entry->pdev = pdev;
pdev_entry->phys_proc_id = pdev->id;
list_add_tail(&pdev_entry->list, &pdev_list);
mutex_unlock(&pdev_list_mutex);
return 0;
exit_device_free:
kfree(pdev_entry);
exit_device_put:
platform_device_put(pdev);
exit:
mutex_unlock(&pdev_list_mutex);
return err;
}
static void coretemp_device_remove(unsigned int cpu)
{
struct pdev_entry *p, *n;
u16 phys_proc_id = TO_PHYS_ID(cpu);
mutex_lock(&pdev_list_mutex);
list_for_each_entry_safe(p, n, &pdev_list, list) {
if (p->phys_proc_id != phys_proc_id)
continue;
platform_device_unregister(p->pdev);
list_del(&p->list);
kfree(p);
} }
mutex_unlock(&pdev_list_mutex);
}
static bool is_any_core_online(struct platform_data *pdata)
{
int i;
/* Find online cores, except pkgtemp data */ pkg_devices[pkgid] = pdev;
for (i = MAX_CORE_DATA - 1; i >= 0; --i) { return pdev;
if (pdata->core_data[i] &&
!pdata->core_data[i]->is_pkg_data) {
return true;
}
}
return false;
} }
static void get_core_online(unsigned int cpu) static int coretemp_cpu_online(unsigned int cpu)
{ {
struct cpuinfo_x86 *c = &cpu_data(cpu);
struct platform_device *pdev = coretemp_get_pdev(cpu); struct platform_device *pdev = coretemp_get_pdev(cpu);
int err; struct cpuinfo_x86 *c = &cpu_data(cpu);
struct platform_data *pdata;
/* /*
* CPUID.06H.EAX[0] indicates whether the CPU has thermal * CPUID.06H.EAX[0] indicates whether the CPU has thermal
...@@ -688,12 +610,12 @@ static void get_core_online(unsigned int cpu) ...@@ -688,12 +610,12 @@ static void get_core_online(unsigned int cpu)
* without thermal sensors will be filtered out. * without thermal sensors will be filtered out.
*/ */
if (!cpu_has(c, X86_FEATURE_DTHERM)) if (!cpu_has(c, X86_FEATURE_DTHERM))
return; return -ENODEV;
if (!pdev) { if (!pdev) {
/* Check the microcode version of the CPU */ /* Check the microcode version of the CPU */
if (chk_ucode_version(cpu)) if (chk_ucode_version(cpu))
return; return -EINVAL;
/* /*
* Alright, we have DTS support. * Alright, we have DTS support.
...@@ -701,101 +623,100 @@ static void get_core_online(unsigned int cpu) ...@@ -701,101 +623,100 @@ static void get_core_online(unsigned int cpu)
* online. So, initialize per-pkg data structures and * online. So, initialize per-pkg data structures and
* then bring this core online. * then bring this core online.
*/ */
err = coretemp_device_add(cpu); pdev = coretemp_device_add(cpu);
if (err) if (IS_ERR(pdev))
return; return PTR_ERR(pdev);
/* /*
* Check whether pkgtemp support is available. * Check whether pkgtemp support is available.
* If so, add interfaces for pkgtemp. * If so, add interfaces for pkgtemp.
*/ */
if (cpu_has(c, X86_FEATURE_PTS)) if (cpu_has(c, X86_FEATURE_PTS))
coretemp_add_core(cpu, 1); coretemp_add_core(pdev, cpu, 1);
} }
pdata = platform_get_drvdata(pdev);
/* /*
* Physical CPU device already exists. * Check whether a thread sibling is already online. If not add the
* So, just add interfaces for this core. * interface for this CPU core.
*/ */
coretemp_add_core(cpu, 0); if (!cpumask_intersects(&pdata->cpumask, topology_sibling_cpumask(cpu)))
coretemp_add_core(pdev, cpu, 0);
cpumask_set_cpu(cpu, &pdata->cpumask);
return 0;
} }
static void put_core_offline(unsigned int cpu) static int coretemp_cpu_offline(unsigned int cpu)
{ {
int i, indx;
struct platform_data *pdata;
struct platform_device *pdev = coretemp_get_pdev(cpu); struct platform_device *pdev = coretemp_get_pdev(cpu);
struct platform_data *pd;
struct temp_data *tdata;
int indx, target;
/* If the physical CPU device does not exist, just return */ /* If the physical CPU device does not exist, just return */
if (!pdev) if (!pdev)
return; return 0;
pdata = platform_get_drvdata(pdev);
indx = TO_ATTR_NO(cpu);
/* The core id is too big, just return */ /* The core id is too big, just return */
indx = TO_ATTR_NO(cpu);
if (indx > MAX_CORE_DATA - 1) if (indx > MAX_CORE_DATA - 1)
return; return 0;
if (pdata->core_data[indx] && pdata->core_data[indx]->cpu == cpu) pd = platform_get_drvdata(pdev);
coretemp_remove_core(pdata, indx); tdata = pd->core_data[indx];
cpumask_clear_cpu(cpu, &pd->cpumask);
/* /*
* If a HT sibling of a core is taken offline, but another HT sibling * If this is the last thread sibling, remove the CPU core
* of the same core is still online, register the alternate sibling. * interface, If there is still a sibling online, transfer the
* This ensures that exactly one set of attributes is provided as long * target cpu of that core interface to it.
* as at least one HT sibling of a core is online.
*/ */
for_each_sibling(i, cpu) { target = cpumask_any_and(&pd->cpumask, topology_sibling_cpumask(cpu));
if (i != cpu) { if (target >= nr_cpu_ids) {
get_core_online(i); coretemp_remove_core(pd, indx);
/* } else if (tdata && tdata->cpu == cpu) {
* Display temperature sensor data for one HT sibling mutex_lock(&tdata->update_lock);
* per core only, so abort the loop after one such tdata->cpu = target;
* sibling has been found. mutex_unlock(&tdata->update_lock);
*/
break;
}
} }
/* /*
* If all cores in this pkg are offline, remove the device. * If all cores in this pkg are offline, remove the device. This
* coretemp_device_remove calls unregister_platform_device, * will invoke the platform driver remove function, which cleans up
* which in turn calls coretemp_remove. This removes the * the rest.
* pkgtemp entry and does other clean ups.
*/ */
if (!is_any_core_online(pdata)) if (cpumask_empty(&pd->cpumask)) {
coretemp_device_remove(cpu); pkg_devices[topology_logical_package_id(cpu)] = NULL;
} platform_device_unregister(pdev);
return 0;
}
static int coretemp_cpu_callback(struct notifier_block *nfb, /*
unsigned long action, void *hcpu) * Check whether this core is the target for the package
{ * interface. We need to assign it to some other cpu.
unsigned int cpu = (unsigned long) hcpu; */
tdata = pd->core_data[PKG_SYSFS_ATTR_NO];
switch (action) { if (tdata && tdata->cpu == cpu) {
case CPU_ONLINE: target = cpumask_first(&pd->cpumask);
case CPU_DOWN_FAILED: mutex_lock(&tdata->update_lock);
get_core_online(cpu); tdata->cpu = target;
break; mutex_unlock(&tdata->update_lock);
case CPU_DOWN_PREPARE:
put_core_offline(cpu);
break;
} }
return NOTIFY_OK; return 0;
} }
static struct notifier_block coretemp_cpu_notifier __refdata = {
.notifier_call = coretemp_cpu_callback,
};
static const struct x86_cpu_id __initconst coretemp_ids[] = { static const struct x86_cpu_id __initconst coretemp_ids[] = {
{ X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, X86_FEATURE_DTHERM }, { X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, X86_FEATURE_DTHERM },
{} {}
}; };
MODULE_DEVICE_TABLE(x86cpu, coretemp_ids); MODULE_DEVICE_TABLE(x86cpu, coretemp_ids);
static enum cpuhp_state coretemp_hp_online;
static int __init coretemp_init(void) static int __init coretemp_init(void)
{ {
int i, err; int err;
/* /*
* CPUID.06H.EAX[0] indicates whether the CPU has thermal * CPUID.06H.EAX[0] indicates whether the CPU has thermal
...@@ -805,54 +726,38 @@ static int __init coretemp_init(void) ...@@ -805,54 +726,38 @@ static int __init coretemp_init(void)
if (!x86_match_cpu(coretemp_ids)) if (!x86_match_cpu(coretemp_ids))
return -ENODEV; return -ENODEV;
max_packages = topology_max_packages();
pkg_devices = kzalloc(max_packages * sizeof(struct platform_device *),
GFP_KERNEL);
if (!pkg_devices)
return -ENOMEM;
err = platform_driver_register(&coretemp_driver); err = platform_driver_register(&coretemp_driver);
if (err) if (err)
goto exit; return err;
cpu_notifier_register_begin(); err = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "hwmon/coretemp:online",
for_each_online_cpu(i) coretemp_cpu_online, coretemp_cpu_offline);
get_core_online(i); if (err < 0)
goto outdrv;
#ifndef CONFIG_HOTPLUG_CPU coretemp_hp_online = err;
if (list_empty(&pdev_list)) {
cpu_notifier_register_done();
err = -ENODEV;
goto exit_driver_unreg;
}
#endif
__register_hotcpu_notifier(&coretemp_cpu_notifier);
cpu_notifier_register_done();
return 0; return 0;
#ifndef CONFIG_HOTPLUG_CPU outdrv:
exit_driver_unreg:
platform_driver_unregister(&coretemp_driver); platform_driver_unregister(&coretemp_driver);
#endif kfree(pkg_devices);
exit:
return err; return err;
} }
module_init(coretemp_init)
static void __exit coretemp_exit(void) static void __exit coretemp_exit(void)
{ {
struct pdev_entry *p, *n; cpuhp_remove_state(coretemp_hp_online);
cpu_notifier_register_begin();
__unregister_hotcpu_notifier(&coretemp_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(&coretemp_driver); platform_driver_unregister(&coretemp_driver);
kfree(pkg_devices);
} }
module_exit(coretemp_exit)
MODULE_AUTHOR("Rudolf Marek <r.marek@assembler.cz>"); MODULE_AUTHOR("Rudolf Marek <r.marek@assembler.cz>");
MODULE_DESCRIPTION("Intel Core temperature monitor"); MODULE_DESCRIPTION("Intel Core temperature monitor");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
module_init(coretemp_init)
module_exit(coretemp_exit)
...@@ -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)
{ {
if (!rpm) /* to stop the fan, set cnt to 255 */ unsigned long f1 = clk_freq * 30 * gear_mult;
unsigned long f2 = p * clk_div;
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); return -EINVAL;
if (data->vdd > MCP3021_VDD_MAX || data->vdd < MCP3021_VDD_MIN)
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);
} }
......
/*
* tc654.c - Linux kernel modules for fan speed controller
*
* Copyright (C) 2016 Allied Telesis Labs NZ
*
* 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.
*/
#include <linux/bitops.h>
#include <linux/err.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/jiffies.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/util_macros.h>
enum tc654_regs {
TC654_REG_RPM1 = 0x00, /* RPM Output 1 */
TC654_REG_RPM2 = 0x01, /* RPM Output 2 */
TC654_REG_FAN_FAULT1 = 0x02, /* Fan Fault 1 Threshold */
TC654_REG_FAN_FAULT2 = 0x03, /* Fan Fault 2 Threshold */
TC654_REG_CONFIG = 0x04, /* Configuration */
TC654_REG_STATUS = 0x05, /* Status */
TC654_REG_DUTY_CYCLE = 0x06, /* Fan Speed Duty Cycle */
TC654_REG_MFR_ID = 0x07, /* Manufacturer Identification */
TC654_REG_VER_ID = 0x08, /* Version Identification */
};
/* Macros to easily index the registers */
#define TC654_REG_RPM(idx) (TC654_REG_RPM1 + (idx))
#define TC654_REG_FAN_FAULT(idx) (TC654_REG_FAN_FAULT1 + (idx))
/* Config register bits */
#define TC654_REG_CONFIG_RES BIT(6) /* Resolution Selection */
#define TC654_REG_CONFIG_DUTYC BIT(5) /* Duty Cycle Control */
#define TC654_REG_CONFIG_SDM BIT(0) /* Shutdown Mode */
/* Status register bits */
#define TC654_REG_STATUS_F2F BIT(1) /* Fan 2 Fault */
#define TC654_REG_STATUS_F1F BIT(0) /* Fan 1 Fault */
/* RPM resolution for RPM Output registers */
#define TC654_HIGH_RPM_RESOLUTION 25 /* 25 RPM resolution */
#define TC654_LOW_RPM_RESOLUTION 50 /* 50 RPM resolution */
/* Convert to the fan fault RPM threshold from register value */
#define TC654_FAN_FAULT_FROM_REG(val) ((val) * 50) /* 50 RPM resolution */
/* Convert to register value from the fan fault RPM threshold */
#define TC654_FAN_FAULT_TO_REG(val) (((val) / 50) & 0xff)
/* Register data is read (and cached) at most once per second. */
#define TC654_UPDATE_INTERVAL HZ
struct tc654_data {
struct i2c_client *client;
/* update mutex */
struct mutex update_lock;
/* tc654 register cache */
bool valid;
unsigned long last_updated; /* in jiffies */
u8 rpm_output[2]; /* The fan RPM data for fans 1 and 2 is then
* written to registers RPM1 and RPM2
*/
u8 fan_fault[2]; /* The Fan Fault Threshold Registers are used to
* set the fan fault threshold levels for fan 1
* and fan 2
*/
u8 config; /* The Configuration Register is an 8-bit read/
* writable multi-function control register
* 7: Fan Fault Clear
* 1 = Clear Fan Fault
* 0 = Normal Operation (default)
* 6: Resolution Selection for RPM Output Registers
* RPM Output Registers (RPM1 and RPM2) will be
* set for
* 1 = 25 RPM (9-bit) resolution
* 0 = 50 RPM (8-bit) resolution (default)
* 5: Duty Cycle Control Method
* The V OUT duty cycle will be controlled via
* 1 = the SMBus interface.
* 0 = via the V IN analog input pin. (default)
* 4,3: Fan 2 Pulses Per Rotation
* 00 = 1
* 01 = 2 (default)
* 10 = 4
* 11 = 8
* 2,1: Fan 1 Pulses Per Rotation
* 00 = 1
* 01 = 2 (default)
* 10 = 4
* 11 = 8
* 0: Shutdown Mode
* 1 = Shutdown mode.
* 0 = Normal operation. (default)
*/
u8 status; /* The Status register provides all the information
* about what is going on within the TC654/TC655
* devices.
* 7,6: Unimplemented, Read as '0'
* 5: Over-Temperature Fault Condition
* 1 = Over-Temperature condition has occurred
* 0 = Normal operation. V IN is less than 2.6V
* 4: RPM2 Counter Overflow
* 1 = Fault condition
* 0 = Normal operation
* 3: RPM1 Counter Overflow
* 1 = Fault condition
* 0 = Normal operation
* 2: V IN Input Status
* 1 = V IN is open
* 0 = Normal operation. voltage present at V IN
* 1: Fan 2 Fault
* 1 = Fault condition
* 0 = Normal operation
* 0: Fan 1 Fault
* 1 = Fault condition
* 0 = Normal operation
*/
u8 duty_cycle; /* The DUTY_CYCLE register is a 4-bit read/
* writable register used to control the duty
* cycle of the V OUT output.
*/
};
/* helper to grab and cache data, at most one time per second */
static struct tc654_data *tc654_update_client(struct device *dev)
{
struct tc654_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
int ret = 0;
mutex_lock(&data->update_lock);
if (time_before(jiffies, data->last_updated + TC654_UPDATE_INTERVAL) &&
likely(data->valid))
goto out;
ret = i2c_smbus_read_byte_data(client, TC654_REG_RPM(0));
if (ret < 0)
goto out;
data->rpm_output[0] = ret;
ret = i2c_smbus_read_byte_data(client, TC654_REG_RPM(1));
if (ret < 0)
goto out;
data->rpm_output[1] = ret;
ret = i2c_smbus_read_byte_data(client, TC654_REG_FAN_FAULT(0));
if (ret < 0)
goto out;
data->fan_fault[0] = ret;
ret = i2c_smbus_read_byte_data(client, TC654_REG_FAN_FAULT(1));
if (ret < 0)
goto out;
data->fan_fault[1] = ret;
ret = i2c_smbus_read_byte_data(client, TC654_REG_CONFIG);
if (ret < 0)
goto out;
data->config = ret;
ret = i2c_smbus_read_byte_data(client, TC654_REG_STATUS);
if (ret < 0)
goto out;
data->status = ret;
ret = i2c_smbus_read_byte_data(client, TC654_REG_DUTY_CYCLE);
if (ret < 0)
goto out;
data->duty_cycle = ret & 0x0f;
data->last_updated = jiffies;
data->valid = true;
out:
mutex_unlock(&data->update_lock);
if (ret < 0) /* upon error, encode it in return value */
data = ERR_PTR(ret);
return data;
}
/*
* sysfs attributes
*/
static ssize_t show_fan(struct device *dev, struct device_attribute *da,
char *buf)
{
int nr = to_sensor_dev_attr(da)->index;
struct tc654_data *data = tc654_update_client(dev);
int val;
if (IS_ERR(data))
return PTR_ERR(data);
if (data->config & TC654_REG_CONFIG_RES)
val = data->rpm_output[nr] * TC654_HIGH_RPM_RESOLUTION;
else
val = data->rpm_output[nr] * TC654_LOW_RPM_RESOLUTION;
return sprintf(buf, "%d\n", val);
}
static ssize_t show_fan_min(struct device *dev, struct device_attribute *da,
char *buf)
{
int nr = to_sensor_dev_attr(da)->index;
struct tc654_data *data = tc654_update_client(dev);
if (IS_ERR(data))
return PTR_ERR(data);
return sprintf(buf, "%d\n",
TC654_FAN_FAULT_FROM_REG(data->fan_fault[nr]));
}
static ssize_t set_fan_min(struct device *dev, struct device_attribute *da,
const char *buf, size_t count)
{
int nr = to_sensor_dev_attr(da)->index;
struct tc654_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
unsigned long val;
int ret;
if (kstrtoul(buf, 10, &val))
return -EINVAL;
val = clamp_val(val, 0, 12750);
mutex_lock(&data->update_lock);
data->fan_fault[nr] = TC654_FAN_FAULT_TO_REG(val);
ret = i2c_smbus_write_byte_data(client, TC654_REG_FAN_FAULT(nr),
data->fan_fault[nr]);
mutex_unlock(&data->update_lock);
return ret < 0 ? ret : count;
}
static ssize_t show_fan_alarm(struct device *dev, struct device_attribute *da,
char *buf)
{
int nr = to_sensor_dev_attr(da)->index;
struct tc654_data *data = tc654_update_client(dev);
int val;
if (IS_ERR(data))
return PTR_ERR(data);
if (nr == 0)
val = !!(data->status & TC654_REG_STATUS_F1F);
else
val = !!(data->status & TC654_REG_STATUS_F2F);
return sprintf(buf, "%d\n", val);
}
static const u8 TC654_FAN_PULSE_SHIFT[] = { 1, 3 };
static ssize_t show_fan_pulses(struct device *dev, struct device_attribute *da,
char *buf)
{
int nr = to_sensor_dev_attr(da)->index;
struct tc654_data *data = tc654_update_client(dev);
u8 val;
if (IS_ERR(data))
return PTR_ERR(data);
val = BIT((data->config >> TC654_FAN_PULSE_SHIFT[nr]) & 0x03);
return sprintf(buf, "%d\n", val);
}
static ssize_t set_fan_pulses(struct device *dev, struct device_attribute *da,
const char *buf, size_t count)
{
int nr = to_sensor_dev_attr(da)->index;
struct tc654_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
u8 config;
unsigned long val;
int ret;
if (kstrtoul(buf, 10, &val))
return -EINVAL;
switch (val) {
case 1:
config = 0;
break;
case 2:
config = 1;
break;
case 4:
config = 2;
break;
case 8:
config = 3;
break;
default:
return -EINVAL;
}
mutex_lock(&data->update_lock);
data->config &= ~(0x03 << TC654_FAN_PULSE_SHIFT[nr]);
data->config |= (config << TC654_FAN_PULSE_SHIFT[nr]);
ret = i2c_smbus_write_byte_data(client, TC654_REG_CONFIG, data->config);
mutex_unlock(&data->update_lock);
return ret < 0 ? ret : count;
}
static ssize_t show_pwm_mode(struct device *dev,
struct device_attribute *da, char *buf)
{
struct tc654_data *data = tc654_update_client(dev);
if (IS_ERR(data))
return PTR_ERR(data);
return sprintf(buf, "%d\n", !!(data->config & TC654_REG_CONFIG_DUTYC));
}
static ssize_t set_pwm_mode(struct device *dev,
struct device_attribute *da,
const char *buf, size_t count)
{
struct tc654_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
unsigned long val;
int ret;
if (kstrtoul(buf, 10, &val))
return -EINVAL;
if (val != 0 && val != 1)
return -EINVAL;
mutex_lock(&data->update_lock);
if (val)
data->config |= TC654_REG_CONFIG_DUTYC;
else
data->config &= ~TC654_REG_CONFIG_DUTYC;
ret = i2c_smbus_write_byte_data(client, TC654_REG_CONFIG, data->config);
mutex_unlock(&data->update_lock);
return ret < 0 ? ret : count;
}
static const int tc654_pwm_map[16] = { 77, 88, 102, 112, 124, 136, 148, 160,
172, 184, 196, 207, 219, 231, 243, 255};
static ssize_t show_pwm(struct device *dev, struct device_attribute *da,
char *buf)
{
struct tc654_data *data = tc654_update_client(dev);
int pwm;
if (IS_ERR(data))
return PTR_ERR(data);
if (data->config & TC654_REG_CONFIG_SDM)
pwm = 0;
else
pwm = tc654_pwm_map[data->duty_cycle];
return sprintf(buf, "%d\n", pwm);
}
static ssize_t set_pwm(struct device *dev, struct device_attribute *da,
const char *buf, size_t count)
{
struct tc654_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
unsigned long val;
int ret;
if (kstrtoul(buf, 10, &val))
return -EINVAL;
if (val > 255)
return -EINVAL;
mutex_lock(&data->update_lock);
if (val == 0)
data->config |= TC654_REG_CONFIG_SDM;
else
data->config &= ~TC654_REG_CONFIG_SDM;
data->duty_cycle = find_closest(val, tc654_pwm_map,
ARRAY_SIZE(tc654_pwm_map));
ret = i2c_smbus_write_byte_data(client, TC654_REG_CONFIG, data->config);
if (ret < 0)
goto out;
ret = i2c_smbus_write_byte_data(client, TC654_REG_DUTY_CYCLE,
data->duty_cycle);
out:
mutex_unlock(&data->update_lock);
return ret < 0 ? ret : count;
}
static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0);
static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1);
static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan_min,
set_fan_min, 0);
static SENSOR_DEVICE_ATTR(fan2_min, S_IWUSR | S_IRUGO, show_fan_min,
set_fan_min, 1);
static SENSOR_DEVICE_ATTR(fan1_alarm, S_IRUGO, show_fan_alarm, NULL, 0);
static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_fan_alarm, NULL, 1);
static SENSOR_DEVICE_ATTR(fan1_pulses, S_IWUSR | S_IRUGO, show_fan_pulses,
set_fan_pulses, 0);
static SENSOR_DEVICE_ATTR(fan2_pulses, S_IWUSR | S_IRUGO, show_fan_pulses,
set_fan_pulses, 1);
static SENSOR_DEVICE_ATTR(pwm1_mode, S_IWUSR | S_IRUGO,
show_pwm_mode, set_pwm_mode, 0);
static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm,
set_pwm, 0);
/* Driver data */
static struct attribute *tc654_attrs[] = {
&sensor_dev_attr_fan1_input.dev_attr.attr,
&sensor_dev_attr_fan2_input.dev_attr.attr,
&sensor_dev_attr_fan1_min.dev_attr.attr,
&sensor_dev_attr_fan2_min.dev_attr.attr,
&sensor_dev_attr_fan1_alarm.dev_attr.attr,
&sensor_dev_attr_fan2_alarm.dev_attr.attr,
&sensor_dev_attr_fan1_pulses.dev_attr.attr,
&sensor_dev_attr_fan2_pulses.dev_attr.attr,
&sensor_dev_attr_pwm1_mode.dev_attr.attr,
&sensor_dev_attr_pwm1.dev_attr.attr,
NULL
};
ATTRIBUTE_GROUPS(tc654);
/*
* device probe and removal
*/
static int tc654_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct tc654_data *data;
struct device *hwmon_dev;
int ret;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
data = devm_kzalloc(dev, sizeof(struct tc654_data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->client = client;
mutex_init(&data->update_lock);
ret = i2c_smbus_read_byte_data(client, TC654_REG_CONFIG);
if (ret < 0)
return ret;
data->config = ret;
hwmon_dev =
devm_hwmon_device_register_with_groups(dev, client->name, data,
tc654_groups);
return PTR_ERR_OR_ZERO(hwmon_dev);
}
static const struct i2c_device_id tc654_id[] = {
{"tc654", 0},
{"tc655", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, tc654_id);
static struct i2c_driver tc654_driver = {
.driver = {
.name = "tc654",
},
.probe = tc654_probe,
.id_table = tc654_id,
};
module_i2c_driver(tc654_driver);
MODULE_AUTHOR("Allied Telesis Labs");
MODULE_DESCRIPTION("Microchip TC654/TC655 driver");
MODULE_LICENSE("GPL");
/* Texas Instruments TMP108 SMBus temperature sensor driver
*
* Copyright (C) 2016 John Muir <john@jmuir.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.
*/
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/jiffies.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#define DRIVER_NAME "tmp108"
#define TMP108_REG_TEMP 0x00
#define TMP108_REG_CONF 0x01
#define TMP108_REG_TLOW 0x02
#define TMP108_REG_THIGH 0x03
#define TMP108_TEMP_MIN_MC -50000 /* Minimum millicelcius. */
#define TMP108_TEMP_MAX_MC 127937 /* Maximum millicelcius. */
/* Configuration register bits.
* Note: these bit definitions are byte swapped.
*/
#define TMP108_CONF_M0 0x0100 /* Sensor mode. */
#define TMP108_CONF_M1 0x0200
#define TMP108_CONF_TM 0x0400 /* Thermostat mode. */
#define TMP108_CONF_FL 0x0800 /* Watchdog flag - TLOW */
#define TMP108_CONF_FH 0x1000 /* Watchdog flag - THIGH */
#define TMP108_CONF_CR0 0x2000 /* Conversion rate. */
#define TMP108_CONF_CR1 0x4000
#define TMP108_CONF_ID 0x8000
#define TMP108_CONF_HYS0 0x0010 /* Hysteresis. */
#define TMP108_CONF_HYS1 0x0020
#define TMP108_CONF_POL 0x0080 /* Polarity of alert. */
/* Defaults set by the hardware upon reset. */
#define TMP108_CONF_DEFAULTS (TMP108_CONF_CR0 | TMP108_CONF_TM |\
TMP108_CONF_HYS0 | TMP108_CONF_M1)
/* These bits are read-only. */
#define TMP108_CONF_READ_ONLY (TMP108_CONF_FL | TMP108_CONF_FH |\
TMP108_CONF_ID)
#define TMP108_CONF_MODE_MASK (TMP108_CONF_M0|TMP108_CONF_M1)
#define TMP108_MODE_SHUTDOWN 0x0000
#define TMP108_MODE_ONE_SHOT TMP108_CONF_M0
#define TMP108_MODE_CONTINUOUS TMP108_CONF_M1 /* Default */
/* When M1 is set, M0 is ignored. */
#define TMP108_CONF_CONVRATE_MASK (TMP108_CONF_CR0|TMP108_CONF_CR1)
#define TMP108_CONVRATE_0P25HZ 0x0000
#define TMP108_CONVRATE_1HZ TMP108_CONF_CR0 /* Default */
#define TMP108_CONVRATE_4HZ TMP108_CONF_CR1
#define TMP108_CONVRATE_16HZ (TMP108_CONF_CR0|TMP108_CONF_CR1)
#define TMP108_CONF_HYSTERESIS_MASK (TMP108_CONF_HYS0|TMP108_CONF_HYS1)
#define TMP108_HYSTERESIS_0C 0x0000
#define TMP108_HYSTERESIS_1C TMP108_CONF_HYS0 /* Default */
#define TMP108_HYSTERESIS_2C TMP108_CONF_HYS1
#define TMP108_HYSTERESIS_4C (TMP108_CONF_HYS0|TMP108_CONF_HYS1)
#define TMP108_CONVERSION_TIME_MS 30 /* in milli-seconds */
struct tmp108 {
struct regmap *regmap;
u16 orig_config;
unsigned long ready_time;
};
/* convert 12-bit TMP108 register value to milliCelsius */
static inline int tmp108_temp_reg_to_mC(s16 val)
{
return (val & ~0x0f) * 1000 / 256;
}
/* convert milliCelsius to left adjusted 12-bit TMP108 register value */
static inline u16 tmp108_mC_to_temp_reg(int val)
{
return (val * 256) / 1000;
}
static int tmp108_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *temp)
{
struct tmp108 *tmp108 = dev_get_drvdata(dev);
unsigned int regval;
int err, hyst;
if (type == hwmon_chip) {
if (attr == hwmon_chip_update_interval) {
err = regmap_read(tmp108->regmap, TMP108_REG_CONF,
&regval);
if (err < 0)
return err;
switch (regval & TMP108_CONF_CONVRATE_MASK) {
case TMP108_CONVRATE_0P25HZ:
default:
*temp = 4000;
break;
case TMP108_CONVRATE_1HZ:
*temp = 1000;
break;
case TMP108_CONVRATE_4HZ:
*temp = 250;
break;
case TMP108_CONVRATE_16HZ:
*temp = 63;
break;
}
return 0;
}
return -EOPNOTSUPP;
}
switch (attr) {
case hwmon_temp_input:
/* Is it too early to return a conversion ? */
if (time_before(jiffies, tmp108->ready_time)) {
dev_dbg(dev, "%s: Conversion not ready yet..\n",
__func__);
return -EAGAIN;
}
err = regmap_read(tmp108->regmap, TMP108_REG_TEMP, &regval);
if (err < 0)
return err;
*temp = tmp108_temp_reg_to_mC(regval);
break;
case hwmon_temp_min:
case hwmon_temp_max:
err = regmap_read(tmp108->regmap, attr == hwmon_temp_min ?
TMP108_REG_TLOW : TMP108_REG_THIGH, &regval);
if (err < 0)
return err;
*temp = tmp108_temp_reg_to_mC(regval);
break;
case hwmon_temp_min_alarm:
case hwmon_temp_max_alarm:
err = regmap_read(tmp108->regmap, TMP108_REG_CONF, &regval);
if (err < 0)
return err;
*temp = !!(regval & (attr == hwmon_temp_min_alarm ?
TMP108_CONF_FL : TMP108_CONF_FH));
break;
case hwmon_temp_min_hyst:
case hwmon_temp_max_hyst:
err = regmap_read(tmp108->regmap, TMP108_REG_CONF, &regval);
if (err < 0)
return err;
switch (regval & TMP108_CONF_HYSTERESIS_MASK) {
case TMP108_HYSTERESIS_0C:
default:
hyst = 0;
break;
case TMP108_HYSTERESIS_1C:
hyst = 1000;
break;
case TMP108_HYSTERESIS_2C:
hyst = 2000;
break;
case TMP108_HYSTERESIS_4C:
hyst = 4000;
break;
}
err = regmap_read(tmp108->regmap, attr == hwmon_temp_min_hyst ?
TMP108_REG_TLOW : TMP108_REG_THIGH, &regval);
if (err < 0)
return err;
*temp = tmp108_temp_reg_to_mC(regval);
if (attr == hwmon_temp_min_hyst)
*temp += hyst;
else
*temp -= hyst;
break;
default:
return -EOPNOTSUPP;
}
return 0;
}
static int tmp108_write(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long temp)
{
struct tmp108 *tmp108 = dev_get_drvdata(dev);
u32 regval, mask;
int err;
if (type == hwmon_chip) {
if (attr == hwmon_chip_update_interval) {
if (temp < 156)
mask = TMP108_CONVRATE_16HZ;
else if (temp < 625)
mask = TMP108_CONVRATE_4HZ;
else if (temp < 2500)
mask = TMP108_CONVRATE_1HZ;
else
mask = TMP108_CONVRATE_0P25HZ;
return regmap_update_bits(tmp108->regmap,
TMP108_REG_CONF,
TMP108_CONF_CONVRATE_MASK,
mask);
}
return -EOPNOTSUPP;
}
switch (attr) {
case hwmon_temp_min:
case hwmon_temp_max:
temp = clamp_val(temp, TMP108_TEMP_MIN_MC, TMP108_TEMP_MAX_MC);
return regmap_write(tmp108->regmap,
attr == hwmon_temp_min ?
TMP108_REG_TLOW : TMP108_REG_THIGH,
tmp108_mC_to_temp_reg(temp));
case hwmon_temp_min_hyst:
case hwmon_temp_max_hyst:
temp = clamp_val(temp, TMP108_TEMP_MIN_MC, TMP108_TEMP_MAX_MC);
err = regmap_read(tmp108->regmap,
attr == hwmon_temp_min_hyst ?
TMP108_REG_TLOW : TMP108_REG_THIGH,
&regval);
if (err < 0)
return err;
if (attr == hwmon_temp_min_hyst)
temp -= tmp108_temp_reg_to_mC(regval);
else
temp = tmp108_temp_reg_to_mC(regval) - temp;
if (temp < 500)
mask = TMP108_HYSTERESIS_0C;
else if (temp < 1500)
mask = TMP108_HYSTERESIS_1C;
else if (temp < 3000)
mask = TMP108_HYSTERESIS_2C;
else
mask = TMP108_HYSTERESIS_4C;
return regmap_update_bits(tmp108->regmap, TMP108_REG_CONF,
TMP108_CONF_HYSTERESIS_MASK, mask);
default:
return -EOPNOTSUPP;
}
}
static umode_t tmp108_is_visible(const void *data, enum hwmon_sensor_types type,
u32 attr, int channel)
{
if (type == hwmon_chip && attr == hwmon_chip_update_interval)
return 0644;
if (type != hwmon_temp)
return 0;
switch (attr) {
case hwmon_temp_input:
case hwmon_temp_min_alarm:
case hwmon_temp_max_alarm:
return 0444;
case hwmon_temp_min:
case hwmon_temp_max:
case hwmon_temp_min_hyst:
case hwmon_temp_max_hyst:
return 0644;
default:
return 0;
}
}
static u32 tmp108_chip_config[] = {
HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL,
0
};
static const struct hwmon_channel_info tmp108_chip = {
.type = hwmon_chip,
.config = tmp108_chip_config,
};
static u32 tmp108_temp_config[] = {
HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN | HWMON_T_MIN_HYST
| HWMON_T_MAX_HYST | HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM,
0
};
static const struct hwmon_channel_info tmp108_temp = {
.type = hwmon_temp,
.config = tmp108_temp_config,
};
static const struct hwmon_channel_info *tmp108_info[] = {
&tmp108_chip,
&tmp108_temp,
NULL
};
static const struct hwmon_ops tmp108_hwmon_ops = {
.is_visible = tmp108_is_visible,
.read = tmp108_read,
.write = tmp108_write,
};
static const struct hwmon_chip_info tmp108_chip_info = {
.ops = &tmp108_hwmon_ops,
.info = tmp108_info,
};
static void tmp108_restore_config(void *data)
{
struct tmp108 *tmp108 = data;
regmap_write(tmp108->regmap, TMP108_REG_CONF, tmp108->orig_config);
}
static bool tmp108_is_writeable_reg(struct device *dev, unsigned int reg)
{
return reg != TMP108_REG_TEMP;
}
static bool tmp108_is_volatile_reg(struct device *dev, unsigned int reg)
{
/* Configuration register must be volatile to enable FL and FH. */
return reg == TMP108_REG_TEMP || reg == TMP108_REG_CONF;
}
static const struct regmap_config tmp108_regmap_config = {
.reg_bits = 8,
.val_bits = 16,
.max_register = TMP108_REG_THIGH,
.writeable_reg = tmp108_is_writeable_reg,
.volatile_reg = tmp108_is_volatile_reg,
.val_format_endian = REGMAP_ENDIAN_BIG,
.cache_type = REGCACHE_RBTREE,
.use_single_rw = true,
};
static int tmp108_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct device *hwmon_dev;
struct tmp108 *tmp108;
int err;
u32 config;
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_WORD_DATA)) {
dev_err(dev,
"adapter doesn't support SMBus word transactions\n");
return -ENODEV;
}
tmp108 = devm_kzalloc(dev, sizeof(*tmp108), GFP_KERNEL);
if (!tmp108)
return -ENOMEM;
dev_set_drvdata(dev, tmp108);
tmp108->regmap = devm_regmap_init_i2c(client, &tmp108_regmap_config);
if (IS_ERR(tmp108->regmap)) {
err = PTR_ERR(tmp108->regmap);
dev_err(dev, "regmap init failed: %d", err);
return err;
}
err = regmap_read(tmp108->regmap, TMP108_REG_CONF, &config);
if (err < 0) {
dev_err(dev, "error reading config register: %d", err);
return err;
}
tmp108->orig_config = config;
/* Only continuous mode is supported. */
config &= ~TMP108_CONF_MODE_MASK;
config |= TMP108_MODE_CONTINUOUS;
/* Only comparator mode is supported. */
config &= ~TMP108_CONF_TM;
err = regmap_write(tmp108->regmap, TMP108_REG_CONF, config);
if (err < 0) {
dev_err(dev, "error writing config register: %d", err);
return err;
}
tmp108->ready_time = jiffies;
if ((tmp108->orig_config & TMP108_CONF_MODE_MASK) ==
TMP108_MODE_SHUTDOWN)
tmp108->ready_time +=
msecs_to_jiffies(TMP108_CONVERSION_TIME_MS);
err = devm_add_action_or_reset(dev, tmp108_restore_config, tmp108);
if (err) {
dev_err(dev, "add action or reset failed: %d", err);
return err;
}
hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
tmp108,
&tmp108_chip_info,
NULL);
return PTR_ERR_OR_ZERO(hwmon_dev);
}
static int __maybe_unused tmp108_suspend(struct device *dev)
{
struct tmp108 *tmp108 = dev_get_drvdata(dev);
return regmap_update_bits(tmp108->regmap, TMP108_REG_CONF,
TMP108_CONF_MODE_MASK, TMP108_MODE_SHUTDOWN);
}
static int __maybe_unused tmp108_resume(struct device *dev)
{
struct tmp108 *tmp108 = dev_get_drvdata(dev);
int err;
err = regmap_update_bits(tmp108->regmap, TMP108_REG_CONF,
TMP108_CONF_MODE_MASK, TMP108_MODE_CONTINUOUS);
tmp108->ready_time = jiffies +
msecs_to_jiffies(TMP108_CONVERSION_TIME_MS);
return err;
}
static SIMPLE_DEV_PM_OPS(tmp108_dev_pm_ops, tmp108_suspend, tmp108_resume);
static const struct i2c_device_id tmp108_i2c_ids[] = {
{ "tmp108", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, tmp108_i2c_ids);
#ifdef CONFIG_OF
static const struct of_device_id tmp108_of_ids[] = {
{ .compatible = "ti,tmp108", },
{}
};
MODULE_DEVICE_TABLE(of, tmp108_of_ids);
#endif
static struct i2c_driver tmp108_driver = {
.driver = {
.name = DRIVER_NAME,
.pm = &tmp108_dev_pm_ops,
.of_match_table = of_match_ptr(tmp108_of_ids),
},
.probe = tmp108_probe,
.id_table = tmp108_i2c_ids,
};
module_i2c_driver(tmp108_driver);
MODULE_AUTHOR("John Muir <john@jmuir.com>");
MODULE_DESCRIPTION("Texas Instruments TMP108 temperature sensor driver");
MODULE_LICENSE("GPL");
...@@ -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