mirror of
https://github.com/torvalds/linux.git
synced 2024-11-26 22:21:42 +00:00
New driver for NCT6683D
New chip support to existing drivers: Add support for STTS2004 and AT30TSE004 to jc42 driver Add support for EMC1402/EMC1412/EMC1422 to emc1403 driver Other notable changes: Document hwmon kernel API Convert jc42, lm70, lm75, lm77, lm83, lm92, max1619, tmp421, and tmp102 drivers to use new hwmon API functions Replace function macros in lm80, lm92, and jc42 drivers with real code Convert emc1403 driver to use regmap, add support for additional attributes, and add device IDs for EMC1412, EMC1413, and EMC1414 Various additional cleanup and minor bug fixes in several drivers -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJTjUZbAAoJEMsfJm/On5mBnY4P/A15UCpfNzNa/tYzHUYtb0/Y 7yI6fEWhgSegiWuWl3I7jOJrz+uU2emrIdOVW1ci8Y7WNjoL+TWYTlvvwf0OYwMz k7duMcNWcIOzBAm+swyaKvPQjoLcxtKYn+pSCxb1dzVkjzpelnjHctBNnfbvrNQt FCMs+t2azIZGkgNf9G1U/uV702/vKKYWrBc69/EB3osDxSRLKvM2sPqnScxmBR6q oohZ5pxbpDhO0gXc9s7AprUFf4puMEWw6eul3QPMzBAgZqh44iOv+yNvJk6pjG5L FwWkand4U2BVMGY3+dnBCeA8fsYq9owPCK8WN+5NI1W+CLMnKQFHK4iOEDbiaSHH PsgGrFnj5ArVxcmPKUQ3l2nWQcwXUvuBTxRmc707HNLt93y2JrdIncdSnhNR5PVk nlUNwTXCqfQmv0aY/yTDIzrgYfXXxhngaw59kEZXBPyO7rH7IcXZw6sq/scn8+G3 Nd8lHLaFOAZwZVNU1hUWkow88t/NwkW/CZ2c2796pjsjAuJfBS1P+C4hkkUQ7+QB SavLg8S9E+Ienxacx2tZs8OdcAXsvlrHMeM0TTiWLbKPosDz3FbwbPeqlCpcVQAY kSPIXA+l1Dps0v8Yf6pdJCL1eX1jyzdfA+YKMT0aCPALTQYEPQXAE4Nced+qCZ52 1vm6eTqDAf1KQfOmxxxT =dytt -----END PGP SIGNATURE----- Merge tag 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging into next Pull hwmon updates from Guenter Roeck: "New driver for NCT6683D New chip support to existing drivers: - add support for STTS2004 and AT30TSE004 to jc42 driver - add support for EMC1402/EMC1412/EMC1422 to emc1403 driver Other notable changes: - document hwmon kernel API - convert jc42, lm70, lm75, lm77, lm83, lm92, max1619, tmp421, and tmp102 drivers to use new hwmon API functions - replace function macros in lm80, lm92, and jc42 drivers with real code - convert emc1403 driver to use regmap, add support for additional attributes, and add device IDs for EMC1412, EMC1413, and EMC1414 - various additional cleanup and minor bug fixes in several drivers" * tag 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (60 commits) hwmon: (nct6775) Fix probe unwind paths to properly unregister platform devices hwmon: (nct6683) Fix probe unwind paths to properly unregister platform devices hwmon: (ultra45_env) Introduce managed version of kzalloc hwmon: Driver for NCT6683D hwmon: (lm80) Rearrange code to avoid forward declarations hwmon: (lm80) Convert fan display function macros into functions hwmon: (lm80) Convert voltage display function macros into functions hwmon: (lm80) Convert temperature display function macros into functions hwmon: (lm80) Normalize all temperature values to 16 bit hwmon: (lm80) Simplify TEMP_FROM_REG hwmon: (lm83) Convert to use devm_hwmon_device_register_with_groups hwmon: (lm83) Rearange code to avoid forward declarations hwmon: (lm83) Drop FSF address hwmon: (max1619) Convert to use devm_hwmon_device_register_with_groups hwmon: (max1619) Drop function macros hwmon: (max1619) Rearrange code to avoid forward declarations hwmon: (max1619) Drop FSF address hwmon: (max1619) Fix critical alarm display hwmon: (jc42) Add support for STTS2004 and AT30TSE004 hwmon: (jc42) Convert function macros into functions ...
This commit is contained in:
commit
f456205265
59
Documentation/hwmon/emc1403
Normal file
59
Documentation/hwmon/emc1403
Normal file
@ -0,0 +1,59 @@
|
||||
Kernel driver emc1403
|
||||
=====================
|
||||
|
||||
Supported chips:
|
||||
* SMSC / Microchip EMC1402, EMC1412
|
||||
Addresses scanned: I2C 0x18, 0x1c, 0x29, 0x4c, 0x4d, 0x5c
|
||||
Prefix: 'emc1402'
|
||||
Datasheets:
|
||||
http://ww1.microchip.com/downloads/en/DeviceDoc/1412.pdf
|
||||
http://ww1.microchip.com/downloads/en/DeviceDoc/1402.pdf
|
||||
* SMSC / Microchip EMC1403, EMC1404, EMC1413, EMC1414
|
||||
Addresses scanned: I2C 0x18, 0x29, 0x4c, 0x4d
|
||||
Prefix: 'emc1403', 'emc1404'
|
||||
Datasheets:
|
||||
http://ww1.microchip.com/downloads/en/DeviceDoc/1403_1404.pdf
|
||||
http://ww1.microchip.com/downloads/en/DeviceDoc/1413_1414.pdf
|
||||
* SMSC / Microchip EMC1422
|
||||
Addresses scanned: I2C 0x4c
|
||||
Prefix: 'emc1422'
|
||||
Datasheet:
|
||||
http://ww1.microchip.com/downloads/en/DeviceDoc/1422.pdf
|
||||
* SMSC / Microchip EMC1423, EMC1424
|
||||
Addresses scanned: I2C 0x4c
|
||||
Prefix: 'emc1423', 'emc1424'
|
||||
Datasheet:
|
||||
http://ww1.microchip.com/downloads/en/DeviceDoc/1423_1424.pdf
|
||||
|
||||
Author:
|
||||
Kalhan Trisal <kalhan.trisal@intel.com
|
||||
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
The Standard Microsystems Corporation (SMSC) / Microchip EMC14xx chips
|
||||
contain up to four temperature sensors. EMC14x2 support two sensors
|
||||
(one internal, one external). EMC14x3 support three sensors (one internal,
|
||||
two external), and EMC14x4 support four sensors (one internal, three
|
||||
external).
|
||||
|
||||
The chips implement three limits for each sensor: low (tempX_min), high
|
||||
(tempX_max) and critical (tempX_crit.) The chips also implement an
|
||||
hysteresis mechanism which applies to all limits. The relative difference
|
||||
is stored in a single register on the chip, which means that the relative
|
||||
difference between the limit and its hysteresis is always the same for
|
||||
all three limits.
|
||||
|
||||
This implementation detail implies the following:
|
||||
* When setting a limit, its hysteresis will automatically follow, the
|
||||
difference staying unchanged. For example, if the old critical limit
|
||||
was 80 degrees C, and the hysteresis was 75 degrees C, and you change
|
||||
the critical limit to 90 degrees C, then the hysteresis will
|
||||
automatically change to 85 degrees C.
|
||||
* The hysteresis values can't be set independently. We decided to make
|
||||
only temp1_crit_hyst writable, while all other hysteresis attributes
|
||||
are read-only. Setting temp1_crit_hyst writes the difference between
|
||||
temp1_crit_hyst and temp1_crit into the chip, and the same relative
|
||||
hysteresis applies automatically to all other limits.
|
||||
* The limits should be set before the hysteresis.
|
107
Documentation/hwmon/hwmon-kernel-api.txt
Normal file
107
Documentation/hwmon/hwmon-kernel-api.txt
Normal file
@ -0,0 +1,107 @@
|
||||
The Linux Hardware Monitoring kernel API.
|
||||
=========================================
|
||||
|
||||
Guenter Roeck
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
This document describes the API that can be used by hardware monitoring
|
||||
drivers that want to use the hardware monitoring framework.
|
||||
|
||||
This document does not describe what a hardware monitoring (hwmon) Driver or
|
||||
Device is. It also does not describe the API which can be used by user space
|
||||
to communicate with a hardware monitoring device. If you want to know this
|
||||
then please read the following file: Documentation/hwmon/sysfs-interface.
|
||||
|
||||
For additional guidelines on how to write and improve hwmon drivers, please
|
||||
also read Documentation/hwmon/submitting-patches.
|
||||
|
||||
The API
|
||||
-------
|
||||
Each hardware monitoring driver must #include <linux/hwmon.h> and, in most
|
||||
cases, <linux/hwmon-sysfs.h>. linux/hwmon.h declares the following
|
||||
register/unregister functions:
|
||||
|
||||
struct device *hwmon_device_register(struct device *dev);
|
||||
struct device *
|
||||
hwmon_device_register_with_groups(struct device *dev, const char *name,
|
||||
void *drvdata,
|
||||
const struct attribute_group **groups);
|
||||
|
||||
struct device *
|
||||
devm_hwmon_device_register_with_groups(struct device *dev,
|
||||
const char *name, void *drvdata,
|
||||
const struct attribute_group **groups);
|
||||
|
||||
void hwmon_device_unregister(struct device *dev);
|
||||
void devm_hwmon_device_unregister(struct device *dev);
|
||||
|
||||
hwmon_device_register registers a hardware monitoring device. The parameter
|
||||
of this function is a pointer to the parent device.
|
||||
This function returns a pointer to the newly created hardware monitoring device
|
||||
or PTR_ERR for failure. If this registration function is used, hardware
|
||||
monitoring sysfs attributes are expected to have been created and attached to
|
||||
the parent device prior to calling hwmon_device_register. A name attribute must
|
||||
have been created by the caller.
|
||||
|
||||
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.
|
||||
hwmon_device_register_with_groups creates the hwmon device with name attribute
|
||||
as well as all sysfs attributes attached to the hwmon device.
|
||||
|
||||
devm_hwmon_device_register_with_groups is similar to
|
||||
hwmon_device_register_with_groups. However, it is device managed, meaning the
|
||||
hwmon device does not have to be removed explicitly by the removal function.
|
||||
|
||||
hwmon_device_unregister deregisters a registered hardware monitoring device.
|
||||
The parameter of this function is the pointer to the registered hardware
|
||||
monitoring device structure. This function must be called from the driver
|
||||
remove function if the hardware monitoring device was registered with
|
||||
hwmon_device_register or with hwmon_device_register_with_groups.
|
||||
|
||||
devm_hwmon_device_unregister does not normally have to be called. It is only
|
||||
needed for error handling, and only needed if the driver probe fails after
|
||||
the call to devm_hwmon_device_register_with_groups.
|
||||
|
||||
The header file linux/hwmon-sysfs.h provides a number of useful macros to
|
||||
declare and use hardware monitoring sysfs attributes.
|
||||
|
||||
In many cases, you can use the exsting define DEVICE_ATTR to declare such
|
||||
attributes. This is feasible if an attribute has no additional context. However,
|
||||
in many cases there will be additional information such as a sensor index which
|
||||
will need to be passed to the sysfs attribute handling function.
|
||||
|
||||
SENSOR_DEVICE_ATTR and SENSOR_DEVICE_ATTR_2 can be used to define attributes
|
||||
which need such additional context information. SENSOR_DEVICE_ATTR requires
|
||||
one additional argument, SENSOR_DEVICE_ATTR_2 requires two.
|
||||
|
||||
SENSOR_DEVICE_ATTR defines a struct sensor_device_attribute variable.
|
||||
This structure has the following fields.
|
||||
|
||||
struct sensor_device_attribute {
|
||||
struct device_attribute dev_attr;
|
||||
int index;
|
||||
};
|
||||
|
||||
You can use to_sensor_dev_attr to get the pointer to this structure from the
|
||||
attribute read or write function. Its parameter is the device to which the
|
||||
attribute is attached.
|
||||
|
||||
SENSOR_DEVICE_ATTR_2 defines a struct sensor_device_attribute_2 variable,
|
||||
which is defined as follows.
|
||||
|
||||
struct sensor_device_attribute_2 {
|
||||
struct device_attribute dev_attr;
|
||||
u8 index;
|
||||
u8 nr;
|
||||
};
|
||||
|
||||
Use to_sensor_dev_attr_2 to get the pointer to this structure. Its parameter
|
||||
is the device to which the attribute is attached.
|
@ -5,9 +5,12 @@ Supported chips:
|
||||
* Analog Devices ADT7408
|
||||
Datasheets:
|
||||
http://www.analog.com/static/imported-files/data_sheets/ADT7408.pdf
|
||||
* Atmel AT30TS00
|
||||
* Atmel AT30TS00, AT30TS002A/B, AT30TSE004A
|
||||
Datasheets:
|
||||
http://www.atmel.com/Images/doc8585.pdf
|
||||
http://www.atmel.com/Images/doc8711.pdf
|
||||
http://www.atmel.com/Images/Atmel-8852-SEEPROM-AT30TSE002A-Datasheet.pdf
|
||||
http://www.atmel.com/Images/Atmel-8868-DTS-AT30TSE004A-Datasheet.pdf
|
||||
* IDT TSE2002B3, TSE2002GB2, TS3000B3, TS3000GB2
|
||||
Datasheets:
|
||||
http://www.idt.com/sites/default/files/documents/IDT_TSE2002B3C_DST_20100512_120303152056.pdf
|
||||
@ -34,12 +37,13 @@ Supported chips:
|
||||
Datasheet:
|
||||
http://www.onsemi.com/pub_link/Collateral/CAT34TS02-D.PDF
|
||||
http://www.onsemi.com/pub/Collateral/CAT6095-D.PDF
|
||||
* ST Microelectronics STTS424, STTS424E02, STTS2002, STTS3000
|
||||
* ST Microelectronics STTS424, STTS424E02, STTS2002, STTS2004, STTS3000
|
||||
Datasheets:
|
||||
http://www.st.com/internet/com/TECHNICAL_RESOURCES/TECHNICAL_LITERATURE/DATASHEET/CD00157556.pdf
|
||||
http://www.st.com/internet/com/TECHNICAL_RESOURCES/TECHNICAL_LITERATURE/DATASHEET/CD00157558.pdf
|
||||
http://www.st.com/internet/com/TECHNICAL_RESOURCES/TECHNICAL_LITERATURE/DATASHEET/CD00225278.pdf
|
||||
http://www.st.com/internet/com/TECHNICAL_RESOURCES/TECHNICAL_LITERATURE/DATA_BRIEF/CD00270920.pdf
|
||||
http://www.st.com/web/en/resource/technical/document/datasheet/CD00157556.pdf
|
||||
http://www.st.com/web/en/resource/technical/document/datasheet/CD00157558.pdf
|
||||
http://www.st.com/web/en/resource/technical/document/datasheet/CD00266638.pdf
|
||||
http://www.st.com/web/en/resource/technical/document/datasheet/CD00225278.pdf
|
||||
http://www.st.com/web/en/resource/technical/document/datasheet/DM00076709.pdf
|
||||
* JEDEC JC 42.4 compliant temperature sensor chips
|
||||
Datasheet:
|
||||
http://www.jedec.org/sites/default/files/docs/4_01_04R19.pdf
|
||||
|
@ -18,5 +18,21 @@ sensor incorporates a band-gap type temperature sensor,
|
||||
10-bit ADC, and a digital comparator with user-programmable upper
|
||||
and lower limit values.
|
||||
|
||||
Limits can be set through the Overtemperature Shutdown register and
|
||||
Hysteresis register.
|
||||
The LM77 implements 3 limits: low (temp1_min), high (temp1_max) and
|
||||
critical (temp1_crit.) It also implements an hysteresis mechanism which
|
||||
applies to all 3 limits. The relative difference is stored in a single
|
||||
register on the chip, which means that the relative difference between
|
||||
the limit and its hysteresis is always the same for all 3 limits.
|
||||
|
||||
This implementation detail implies the following:
|
||||
* When setting a limit, its hysteresis will automatically follow, the
|
||||
difference staying unchanged. For example, if the old critical limit
|
||||
was 80 degrees C, and the hysteresis was 75 degrees C, and you change
|
||||
the critical limit to 90 degrees C, then the hysteresis will
|
||||
automatically change to 85 degrees C.
|
||||
* All 3 hysteresis can't be set independently. We decided to make
|
||||
temp1_crit_hyst writable, while temp1_min_hyst and temp1_max_hyst are
|
||||
read-only. Setting temp1_crit_hyst writes the difference between
|
||||
temp1_crit_hyst and temp1_crit into the chip, and the same relative
|
||||
hysteresis applies automatically to the low and high limits.
|
||||
* The limits should be set before the hysteresis.
|
||||
|
57
Documentation/hwmon/nct6683
Normal file
57
Documentation/hwmon/nct6683
Normal file
@ -0,0 +1,57 @@
|
||||
Kernel driver nct6683
|
||||
=====================
|
||||
|
||||
Supported chips:
|
||||
* Nuvoton NCT6683D
|
||||
Prefix: 'nct6683'
|
||||
Addresses scanned: ISA address retrieved from Super I/O registers
|
||||
Datasheet: Available from Nuvoton upon request
|
||||
|
||||
Authors:
|
||||
Guenter Roeck <linux@roeck-us.net>
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
This driver implements support for the Nuvoton NCT6683D eSIO chip.
|
||||
|
||||
The chips implement up to shared 32 temperature and voltage sensors.
|
||||
It supports up to 16 fan rotation sensors and up to 8 fan control engines.
|
||||
|
||||
Temperatures are measured in degrees Celsius. Measurement resolution is
|
||||
0.5 degrees C.
|
||||
|
||||
Voltage sensors (also known as IN sensors) report their values in millivolts.
|
||||
|
||||
Fan rotation speeds are reported in RPM (rotations per minute).
|
||||
|
||||
Usage Note
|
||||
----------
|
||||
|
||||
Limit register locations on Intel boards with EC firmware version 1.0
|
||||
build date 04/03/13 do not match the register locations in the Nuvoton
|
||||
datasheet. Nuvoton confirms that Intel uses a special firmware version
|
||||
with different register addresses. The specification describing the Intel
|
||||
firmware is held under NDA by Nuvoton and Intel and not available
|
||||
to the public.
|
||||
|
||||
Some of the register locations can be reverse engineered; others are too
|
||||
well hidden. Given this, writing any values from the operating system is
|
||||
considered too risky with this firmware and has been disabled. All limits
|
||||
must all be written from the BIOS.
|
||||
|
||||
The driver has only been tested with the Intel firmware, and by default
|
||||
only instantiates on Intel boards. To enable it on non-Intel boards,
|
||||
set the 'force' module parameter to 1.
|
||||
|
||||
Tested Boards and Firmware Versions
|
||||
-----------------------------------
|
||||
|
||||
The driver has been reported to work with the following boards and
|
||||
firmware versions.
|
||||
|
||||
Board Firmware version
|
||||
---------------------------------------------------------------
|
||||
Intel DH87RL NCT6683D EC firmware version 1.0 build 04/03/13
|
||||
Intel DH87MC NCT6683D EC firmware version 1.0 build 04/03/13
|
||||
Intel DB85FL NCT6683D EC firmware version 1.0 build 04/03/13
|
@ -355,7 +355,7 @@ F: Documentation/hwmon/adm1025
|
||||
F: drivers/hwmon/adm1025.c
|
||||
|
||||
ADM1029 HARDWARE MONITOR DRIVER
|
||||
M: Corentin Labbe <corentin.labbe@geomatys.fr>
|
||||
M: Corentin Labbe <clabbe.montjoie@gmail.com>
|
||||
L: lm-sensors@lm-sensors.org
|
||||
S: Maintained
|
||||
F: drivers/hwmon/adm1029.c
|
||||
|
@ -1065,6 +1065,16 @@ config SENSORS_NTC_THERMISTOR
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called ntc-thermistor.
|
||||
|
||||
config SENSORS_NCT6683
|
||||
tristate "Nuvoton NCT6683D"
|
||||
depends on !PPC
|
||||
help
|
||||
If you say yes here you get support for the hardware monitoring
|
||||
functionality of the Nuvoton NCT6683D eSIO chip.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called nct6683.
|
||||
|
||||
config SENSORS_NCT6775
|
||||
tristate "Nuvoton NCT6775F and compatibles"
|
||||
depends on !PPC
|
||||
|
@ -114,6 +114,7 @@ obj-$(CONFIG_SENSORS_MAX6650) += max6650.o
|
||||
obj-$(CONFIG_SENSORS_MAX6697) += max6697.o
|
||||
obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
|
||||
obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o
|
||||
obj-$(CONFIG_SENSORS_NCT6683) += nct6683.o
|
||||
obj-$(CONFIG_SENSORS_NCT6775) += nct6775.o
|
||||
obj-$(CONFIG_SENSORS_NTC_THERMISTOR) += ntc_thermistor.o
|
||||
obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* adm1029.c - Part of lm_sensors, Linux kernel modules for hardware monitoring
|
||||
*
|
||||
* Copyright (C) 2006 Corentin LABBE <corentin.labbe@geomatys.fr>
|
||||
* Copyright (C) 2006 Corentin LABBE <clabbe.montjoie@gmail.com>
|
||||
*
|
||||
* Based on LM83 Driver by Jean Delvare <jdelvare@suse.de>
|
||||
*
|
||||
@ -449,6 +449,6 @@ static struct adm1029_data *adm1029_update_device(struct device *dev)
|
||||
|
||||
module_i2c_driver(adm1029_driver);
|
||||
|
||||
MODULE_AUTHOR("Corentin LABBE <corentin.labbe@geomatys.fr>");
|
||||
MODULE_AUTHOR("Corentin LABBE <clabbe.montjoie@gmail.com>");
|
||||
MODULE_DESCRIPTION("adm1029 driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -18,9 +18,6 @@
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* TODO
|
||||
* - cache alarm and critical limit registers
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
@ -32,22 +29,18 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#define THERMAL_PID_REG 0xfd
|
||||
#define THERMAL_SMSC_ID_REG 0xfe
|
||||
#define THERMAL_REVISION_REG 0xff
|
||||
|
||||
enum emc1403_chip { emc1402, emc1403, emc1404 };
|
||||
|
||||
struct thermal_data {
|
||||
struct i2c_client *client;
|
||||
const struct attribute_group *groups[3];
|
||||
struct regmap *regmap;
|
||||
struct mutex mutex;
|
||||
/*
|
||||
* Cache the hyst value so we don't keep re-reading it. In theory
|
||||
* we could cache it forever as nobody else should be writing it.
|
||||
*/
|
||||
u8 cached_hyst;
|
||||
unsigned long hyst_valid;
|
||||
const struct attribute_group *groups[4];
|
||||
};
|
||||
|
||||
static ssize_t show_temp(struct device *dev,
|
||||
@ -55,12 +48,13 @@ static ssize_t show_temp(struct device *dev,
|
||||
{
|
||||
struct sensor_device_attribute *sda = to_sensor_dev_attr(attr);
|
||||
struct thermal_data *data = dev_get_drvdata(dev);
|
||||
unsigned int val;
|
||||
int retval;
|
||||
|
||||
retval = i2c_smbus_read_byte_data(data->client, sda->index);
|
||||
retval = regmap_read(data->regmap, sda->index, &val);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
return sprintf(buf, "%d000\n", retval);
|
||||
return sprintf(buf, "%d000\n", val);
|
||||
}
|
||||
|
||||
static ssize_t show_bit(struct device *dev,
|
||||
@ -68,12 +62,13 @@ static ssize_t show_bit(struct device *dev,
|
||||
{
|
||||
struct sensor_device_attribute_2 *sda = to_sensor_dev_attr_2(attr);
|
||||
struct thermal_data *data = dev_get_drvdata(dev);
|
||||
unsigned int val;
|
||||
int retval;
|
||||
|
||||
retval = i2c_smbus_read_byte_data(data->client, sda->nr);
|
||||
retval = regmap_read(data->regmap, sda->nr, &val);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
return sprintf(buf, "%d\n", !!(retval & sda->index));
|
||||
return sprintf(buf, "%d\n", !!(val & sda->index));
|
||||
}
|
||||
|
||||
static ssize_t store_temp(struct device *dev,
|
||||
@ -86,8 +81,8 @@ static ssize_t store_temp(struct device *dev,
|
||||
|
||||
if (kstrtoul(buf, 10, &val))
|
||||
return -EINVAL;
|
||||
retval = i2c_smbus_write_byte_data(data->client, sda->index,
|
||||
DIV_ROUND_CLOSEST(val, 1000));
|
||||
retval = regmap_write(data->regmap, sda->index,
|
||||
DIV_ROUND_CLOSEST(val, 1000));
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
return count;
|
||||
@ -98,51 +93,51 @@ static ssize_t store_bit(struct device *dev,
|
||||
{
|
||||
struct sensor_device_attribute_2 *sda = to_sensor_dev_attr_2(attr);
|
||||
struct thermal_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
unsigned long val;
|
||||
int retval;
|
||||
|
||||
if (kstrtoul(buf, 10, &val))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
retval = i2c_smbus_read_byte_data(client, sda->nr);
|
||||
retval = regmap_update_bits(data->regmap, sda->nr, sda->index,
|
||||
val ? sda->index : 0);
|
||||
if (retval < 0)
|
||||
goto fail;
|
||||
|
||||
retval &= ~sda->index;
|
||||
if (val)
|
||||
retval |= sda->index;
|
||||
|
||||
retval = i2c_smbus_write_byte_data(client, sda->index, retval);
|
||||
if (retval == 0)
|
||||
retval = count;
|
||||
fail:
|
||||
mutex_unlock(&data->mutex);
|
||||
return retval;
|
||||
return retval;
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t show_hyst(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
static ssize_t show_hyst_common(struct device *dev,
|
||||
struct device_attribute *attr, char *buf,
|
||||
bool is_min)
|
||||
{
|
||||
struct sensor_device_attribute *sda = to_sensor_dev_attr(attr);
|
||||
struct thermal_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
struct regmap *regmap = data->regmap;
|
||||
unsigned int limit;
|
||||
unsigned int hyst;
|
||||
int retval;
|
||||
int hyst;
|
||||
|
||||
retval = i2c_smbus_read_byte_data(client, sda->index);
|
||||
retval = regmap_read(regmap, sda->index, &limit);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
|
||||
if (time_after(jiffies, data->hyst_valid)) {
|
||||
hyst = i2c_smbus_read_byte_data(client, 0x21);
|
||||
if (hyst < 0)
|
||||
return retval;
|
||||
data->cached_hyst = hyst;
|
||||
data->hyst_valid = jiffies + HZ;
|
||||
}
|
||||
return sprintf(buf, "%d000\n", retval - data->cached_hyst);
|
||||
retval = regmap_read(regmap, 0x21, &hyst);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
|
||||
return sprintf(buf, "%d000\n", is_min ? limit + hyst : limit - hyst);
|
||||
}
|
||||
|
||||
static ssize_t show_hyst(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return show_hyst_common(dev, attr, buf, false);
|
||||
}
|
||||
|
||||
static ssize_t show_min_hyst(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return show_hyst_common(dev, attr, buf, true);
|
||||
}
|
||||
|
||||
static ssize_t store_hyst(struct device *dev,
|
||||
@ -150,7 +145,8 @@ static ssize_t store_hyst(struct device *dev,
|
||||
{
|
||||
struct sensor_device_attribute *sda = to_sensor_dev_attr(attr);
|
||||
struct thermal_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
struct regmap *regmap = data->regmap;
|
||||
unsigned int limit;
|
||||
int retval;
|
||||
int hyst;
|
||||
unsigned long val;
|
||||
@ -159,23 +155,15 @@ static ssize_t store_hyst(struct device *dev,
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
retval = i2c_smbus_read_byte_data(client, sda->index);
|
||||
retval = regmap_read(regmap, sda->index, &limit);
|
||||
if (retval < 0)
|
||||
goto fail;
|
||||
|
||||
hyst = retval * 1000 - val;
|
||||
hyst = DIV_ROUND_CLOSEST(hyst, 1000);
|
||||
if (hyst < 0 || hyst > 255) {
|
||||
retval = -ERANGE;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
retval = i2c_smbus_write_byte_data(client, 0x21, hyst);
|
||||
if (retval == 0) {
|
||||
hyst = limit * 1000 - val;
|
||||
hyst = clamp_val(DIV_ROUND_CLOSEST(hyst, 1000), 0, 255);
|
||||
retval = regmap_write(regmap, 0x21, hyst);
|
||||
if (retval == 0)
|
||||
retval = count;
|
||||
data->cached_hyst = hyst;
|
||||
data->hyst_valid = jiffies + HZ;
|
||||
}
|
||||
fail:
|
||||
mutex_unlock(&data->mutex);
|
||||
return retval;
|
||||
@ -198,6 +186,8 @@ static SENSOR_DEVICE_ATTR_2(temp1_max_alarm, S_IRUGO,
|
||||
show_bit, NULL, 0x35, 0x01);
|
||||
static SENSOR_DEVICE_ATTR_2(temp1_crit_alarm, S_IRUGO,
|
||||
show_bit, NULL, 0x37, 0x01);
|
||||
static SENSOR_DEVICE_ATTR(temp1_min_hyst, S_IRUGO, show_min_hyst, NULL, 0x06);
|
||||
static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IRUGO, show_hyst, NULL, 0x05);
|
||||
static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO | S_IWUSR,
|
||||
show_hyst, store_hyst, 0x20);
|
||||
|
||||
@ -208,14 +198,16 @@ static SENSOR_DEVICE_ATTR(temp2_max, S_IRUGO | S_IWUSR,
|
||||
static SENSOR_DEVICE_ATTR(temp2_crit, S_IRUGO | S_IWUSR,
|
||||
show_temp, store_temp, 0x19);
|
||||
static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 0x01);
|
||||
static SENSOR_DEVICE_ATTR_2(temp2_fault, S_IRUGO, show_bit, NULL, 0x1b, 0x02);
|
||||
static SENSOR_DEVICE_ATTR_2(temp2_min_alarm, S_IRUGO,
|
||||
show_bit, NULL, 0x36, 0x02);
|
||||
static SENSOR_DEVICE_ATTR_2(temp2_max_alarm, S_IRUGO,
|
||||
show_bit, NULL, 0x35, 0x02);
|
||||
static SENSOR_DEVICE_ATTR_2(temp2_crit_alarm, S_IRUGO,
|
||||
show_bit, NULL, 0x37, 0x02);
|
||||
static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO | S_IWUSR,
|
||||
show_hyst, store_hyst, 0x19);
|
||||
static SENSOR_DEVICE_ATTR(temp2_min_hyst, S_IRUGO, show_min_hyst, NULL, 0x08);
|
||||
static SENSOR_DEVICE_ATTR(temp2_max_hyst, S_IRUGO, show_hyst, NULL, 0x07);
|
||||
static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_hyst, NULL, 0x19);
|
||||
|
||||
static SENSOR_DEVICE_ATTR(temp3_min, S_IRUGO | S_IWUSR,
|
||||
show_temp, store_temp, 0x16);
|
||||
@ -224,14 +216,16 @@ static SENSOR_DEVICE_ATTR(temp3_max, S_IRUGO | S_IWUSR,
|
||||
static SENSOR_DEVICE_ATTR(temp3_crit, S_IRUGO | S_IWUSR,
|
||||
show_temp, store_temp, 0x1A);
|
||||
static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 0x23);
|
||||
static SENSOR_DEVICE_ATTR_2(temp3_fault, S_IRUGO, show_bit, NULL, 0x1b, 0x04);
|
||||
static SENSOR_DEVICE_ATTR_2(temp3_min_alarm, S_IRUGO,
|
||||
show_bit, NULL, 0x36, 0x04);
|
||||
static SENSOR_DEVICE_ATTR_2(temp3_max_alarm, S_IRUGO,
|
||||
show_bit, NULL, 0x35, 0x04);
|
||||
static SENSOR_DEVICE_ATTR_2(temp3_crit_alarm, S_IRUGO,
|
||||
show_bit, NULL, 0x37, 0x04);
|
||||
static SENSOR_DEVICE_ATTR(temp3_crit_hyst, S_IRUGO | S_IWUSR,
|
||||
show_hyst, store_hyst, 0x1A);
|
||||
static SENSOR_DEVICE_ATTR(temp3_min_hyst, S_IRUGO, show_min_hyst, NULL, 0x16);
|
||||
static SENSOR_DEVICE_ATTR(temp3_max_hyst, S_IRUGO, show_hyst, NULL, 0x15);
|
||||
static SENSOR_DEVICE_ATTR(temp3_crit_hyst, S_IRUGO, show_hyst, NULL, 0x1A);
|
||||
|
||||
static SENSOR_DEVICE_ATTR(temp4_min, S_IRUGO | S_IWUSR,
|
||||
show_temp, store_temp, 0x2D);
|
||||
@ -240,44 +234,66 @@ static SENSOR_DEVICE_ATTR(temp4_max, S_IRUGO | S_IWUSR,
|
||||
static SENSOR_DEVICE_ATTR(temp4_crit, S_IRUGO | S_IWUSR,
|
||||
show_temp, store_temp, 0x30);
|
||||
static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp, NULL, 0x2A);
|
||||
static SENSOR_DEVICE_ATTR_2(temp4_fault, S_IRUGO, show_bit, NULL, 0x1b, 0x08);
|
||||
static SENSOR_DEVICE_ATTR_2(temp4_min_alarm, S_IRUGO,
|
||||
show_bit, NULL, 0x36, 0x08);
|
||||
static SENSOR_DEVICE_ATTR_2(temp4_max_alarm, S_IRUGO,
|
||||
show_bit, NULL, 0x35, 0x08);
|
||||
static SENSOR_DEVICE_ATTR_2(temp4_crit_alarm, S_IRUGO,
|
||||
show_bit, NULL, 0x37, 0x08);
|
||||
static SENSOR_DEVICE_ATTR(temp4_crit_hyst, S_IRUGO | S_IWUSR,
|
||||
show_hyst, store_hyst, 0x30);
|
||||
static SENSOR_DEVICE_ATTR(temp4_min_hyst, S_IRUGO, show_min_hyst, NULL, 0x2D);
|
||||
static SENSOR_DEVICE_ATTR(temp4_max_hyst, S_IRUGO, show_hyst, NULL, 0x2C);
|
||||
static SENSOR_DEVICE_ATTR(temp4_crit_hyst, S_IRUGO, show_hyst, NULL, 0x30);
|
||||
|
||||
static SENSOR_DEVICE_ATTR_2(power_state, S_IRUGO | S_IWUSR,
|
||||
show_bit, store_bit, 0x03, 0x40);
|
||||
|
||||
static struct attribute *emc1403_attrs[] = {
|
||||
static struct attribute *emc1402_attrs[] = {
|
||||
&sensor_dev_attr_temp1_min.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_crit.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_min_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
|
||||
|
||||
&sensor_dev_attr_temp2_min.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_crit.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_min_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_max_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_crit_hyst.dev_attr.attr,
|
||||
|
||||
&sensor_dev_attr_power_state.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group emc1402_group = {
|
||||
.attrs = emc1402_attrs,
|
||||
};
|
||||
|
||||
static struct attribute *emc1403_attrs[] = {
|
||||
&sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
|
||||
|
||||
&sensor_dev_attr_temp2_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_crit_hyst.dev_attr.attr,
|
||||
|
||||
&sensor_dev_attr_temp3_min.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_crit.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_min_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_crit_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_min_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_max_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_crit_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_power_state.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
@ -290,9 +306,12 @@ static struct attribute *emc1404_attrs[] = {
|
||||
&sensor_dev_attr_temp4_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp4_crit.dev_attr.attr,
|
||||
&sensor_dev_attr_temp4_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp4_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_temp4_min_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp4_max_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp4_crit_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp4_min_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_temp4_max_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_temp4_crit_hyst.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
@ -301,6 +320,39 @@ static const struct attribute_group emc1404_group = {
|
||||
.attrs = emc1404_attrs,
|
||||
};
|
||||
|
||||
/*
|
||||
* EMC14x2 uses a different register and different bits to report alarm and
|
||||
* fault status. For simplicity, provide a separate attribute group for this
|
||||
* chip series.
|
||||
* Since we can not re-use the same attribute names, create a separate attribute
|
||||
* array.
|
||||
*/
|
||||
static struct sensor_device_attribute_2 emc1402_alarms[] = {
|
||||
SENSOR_ATTR_2(temp1_min_alarm, S_IRUGO, show_bit, NULL, 0x02, 0x20),
|
||||
SENSOR_ATTR_2(temp1_max_alarm, S_IRUGO, show_bit, NULL, 0x02, 0x40),
|
||||
SENSOR_ATTR_2(temp1_crit_alarm, S_IRUGO, show_bit, NULL, 0x02, 0x01),
|
||||
|
||||
SENSOR_ATTR_2(temp2_fault, S_IRUGO, show_bit, NULL, 0x02, 0x04),
|
||||
SENSOR_ATTR_2(temp2_min_alarm, S_IRUGO, show_bit, NULL, 0x02, 0x08),
|
||||
SENSOR_ATTR_2(temp2_max_alarm, S_IRUGO, show_bit, NULL, 0x02, 0x10),
|
||||
SENSOR_ATTR_2(temp2_crit_alarm, S_IRUGO, show_bit, NULL, 0x02, 0x02),
|
||||
};
|
||||
|
||||
static struct attribute *emc1402_alarm_attrs[] = {
|
||||
&emc1402_alarms[0].dev_attr.attr,
|
||||
&emc1402_alarms[1].dev_attr.attr,
|
||||
&emc1402_alarms[2].dev_attr.attr,
|
||||
&emc1402_alarms[3].dev_attr.attr,
|
||||
&emc1402_alarms[4].dev_attr.attr,
|
||||
&emc1402_alarms[5].dev_attr.attr,
|
||||
&emc1402_alarms[6].dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group emc1402_alarm_group = {
|
||||
.attrs = emc1402_alarm_attrs,
|
||||
};
|
||||
|
||||
static int emc1403_detect(struct i2c_client *client,
|
||||
struct i2c_board_info *info)
|
||||
{
|
||||
@ -313,9 +365,15 @@ static int emc1403_detect(struct i2c_client *client,
|
||||
|
||||
id = i2c_smbus_read_byte_data(client, THERMAL_PID_REG);
|
||||
switch (id) {
|
||||
case 0x20:
|
||||
strlcpy(info->type, "emc1402", I2C_NAME_SIZE);
|
||||
break;
|
||||
case 0x21:
|
||||
strlcpy(info->type, "emc1403", I2C_NAME_SIZE);
|
||||
break;
|
||||
case 0x22:
|
||||
strlcpy(info->type, "emc1422", I2C_NAME_SIZE);
|
||||
break;
|
||||
case 0x23:
|
||||
strlcpy(info->type, "emc1423", I2C_NAME_SIZE);
|
||||
break;
|
||||
@ -336,6 +394,35 @@ static int emc1403_detect(struct i2c_client *client,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool emc1403_regmap_is_volatile(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case 0x00: /* internal diode high byte */
|
||||
case 0x01: /* external diode 1 high byte */
|
||||
case 0x02: /* status */
|
||||
case 0x10: /* external diode 1 low byte */
|
||||
case 0x1b: /* external diode fault */
|
||||
case 0x23: /* external diode 2 high byte */
|
||||
case 0x24: /* external diode 2 low byte */
|
||||
case 0x29: /* internal diode low byte */
|
||||
case 0x2a: /* externl diode 3 high byte */
|
||||
case 0x2b: /* external diode 3 low byte */
|
||||
case 0x35: /* high limit status */
|
||||
case 0x36: /* low limit status */
|
||||
case 0x37: /* therm limit status */
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static struct regmap_config emc1403_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.volatile_reg = emc1403_regmap_is_volatile,
|
||||
};
|
||||
|
||||
static int emc1403_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
@ -347,13 +434,23 @@ static int emc1403_probe(struct i2c_client *client,
|
||||
if (data == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
data->client = client;
|
||||
mutex_init(&data->mutex);
|
||||
data->hyst_valid = jiffies - 1; /* Expired */
|
||||
data->regmap = devm_regmap_init_i2c(client, &emc1403_regmap_config);
|
||||
if (IS_ERR(data->regmap))
|
||||
return PTR_ERR(data->regmap);
|
||||
|
||||
data->groups[0] = &emc1403_group;
|
||||
if (id->driver_data)
|
||||
data->groups[1] = &emc1404_group;
|
||||
mutex_init(&data->mutex);
|
||||
|
||||
switch (id->driver_data) {
|
||||
case emc1404:
|
||||
data->groups[2] = &emc1404_group;
|
||||
case emc1403:
|
||||
data->groups[1] = &emc1403_group;
|
||||
case emc1402:
|
||||
data->groups[0] = &emc1402_group;
|
||||
}
|
||||
|
||||
if (id->driver_data == emc1402)
|
||||
data->groups[1] = &emc1402_alarm_group;
|
||||
|
||||
hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev,
|
||||
client->name, data,
|
||||
@ -366,14 +463,20 @@ static int emc1403_probe(struct i2c_client *client,
|
||||
}
|
||||
|
||||
static const unsigned short emc1403_address_list[] = {
|
||||
0x18, 0x29, 0x4c, 0x4d, I2C_CLIENT_END
|
||||
0x18, 0x1c, 0x29, 0x4c, 0x4d, 0x5c, I2C_CLIENT_END
|
||||
};
|
||||
|
||||
/* Last digit of chip name indicates number of channels */
|
||||
static const struct i2c_device_id emc1403_idtable[] = {
|
||||
{ "emc1403", 0 },
|
||||
{ "emc1404", 1 },
|
||||
{ "emc1423", 0 },
|
||||
{ "emc1424", 1 },
|
||||
{ "emc1402", emc1402 },
|
||||
{ "emc1403", emc1403 },
|
||||
{ "emc1404", emc1404 },
|
||||
{ "emc1412", emc1402 },
|
||||
{ "emc1413", emc1403 },
|
||||
{ "emc1414", emc1404 },
|
||||
{ "emc1422", emc1402 },
|
||||
{ "emc1423", emc1403 },
|
||||
{ "emc1424", emc1404 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, emc1403_idtable);
|
||||
|
@ -1387,10 +1387,8 @@ static int f71805f_probe(struct platform_device *pdev)
|
||||
|
||||
data = devm_kzalloc(&pdev->dev, sizeof(struct f71805f_data),
|
||||
GFP_KERNEL);
|
||||
if (!data) {
|
||||
pr_err("Out of memory\n");
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
|
||||
if (!devm_request_region(&pdev->dev, res->start + ADDR_REG_OFFSET, 2,
|
||||
|
@ -586,7 +586,7 @@ static int do_set_fan_startv(struct device *dev, unsigned long val)
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static struct of_device_id g762_dt_match[] = {
|
||||
static const struct of_device_id g762_dt_match[] = {
|
||||
{ .compatible = "gmt,g762" },
|
||||
{ .compatible = "gmt,g763" },
|
||||
{ },
|
||||
|
@ -482,7 +482,7 @@ static int gpio_fan_get_of_pdata(struct device *dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct of_device_id of_gpio_fan_match[] = {
|
||||
static const struct of_device_id of_gpio_fan_match[] = {
|
||||
{ .compatible = "gpio-fan", },
|
||||
{},
|
||||
};
|
||||
|
@ -463,10 +463,8 @@ static void ibmpex_register_bmc(int iface, struct device *dev)
|
||||
int err;
|
||||
|
||||
data = kzalloc(sizeof(*data), GFP_KERNEL);
|
||||
if (!data) {
|
||||
dev_err(dev, "Insufficient memory for BMC interface.\n");
|
||||
if (!data)
|
||||
return;
|
||||
}
|
||||
|
||||
data->address.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
|
||||
data->address.channel = IPMI_BMC_CHANNEL;
|
||||
|
@ -163,7 +163,7 @@ static int iio_hwmon_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct of_device_id iio_hwmon_of_match[] = {
|
||||
static const struct of_device_id iio_hwmon_of_match[] = {
|
||||
{ .compatible = "iio-hwmon", },
|
||||
{ }
|
||||
};
|
||||
|
@ -65,6 +65,7 @@ static const unsigned short normal_i2c[] = {
|
||||
/* Manufacturer IDs */
|
||||
#define ADT_MANID 0x11d4 /* Analog Devices */
|
||||
#define ATMEL_MANID 0x001f /* Atmel */
|
||||
#define ATMEL_MANID2 0x1114 /* Atmel */
|
||||
#define MAX_MANID 0x004d /* Maxim */
|
||||
#define IDT_MANID 0x00b3 /* IDT */
|
||||
#define MCP_MANID 0x0054 /* Microchip */
|
||||
@ -82,6 +83,9 @@ static const unsigned short normal_i2c[] = {
|
||||
#define AT30TS00_DEVID 0x8201
|
||||
#define AT30TS00_DEVID_MASK 0xffff
|
||||
|
||||
#define AT30TSE004_DEVID 0x2200
|
||||
#define AT30TSE004_DEVID_MASK 0xffff
|
||||
|
||||
/* IDT */
|
||||
#define TS3000B3_DEVID 0x2903 /* Also matches TSE2002B3 */
|
||||
#define TS3000B3_DEVID_MASK 0xffff
|
||||
@ -130,6 +134,9 @@ static const unsigned short normal_i2c[] = {
|
||||
#define STTS2002_DEVID 0x0300
|
||||
#define STTS2002_DEVID_MASK 0xffff
|
||||
|
||||
#define STTS2004_DEVID 0x2201
|
||||
#define STTS2004_DEVID_MASK 0xffff
|
||||
|
||||
#define STTS3000_DEVID 0x0200
|
||||
#define STTS3000_DEVID_MASK 0xffff
|
||||
|
||||
@ -144,6 +151,7 @@ struct jc42_chips {
|
||||
static struct jc42_chips jc42_chips[] = {
|
||||
{ ADT_MANID, ADT7408_DEVID, ADT7408_DEVID_MASK },
|
||||
{ ATMEL_MANID, AT30TS00_DEVID, AT30TS00_DEVID_MASK },
|
||||
{ ATMEL_MANID2, AT30TSE004_DEVID, AT30TSE004_DEVID_MASK },
|
||||
{ IDT_MANID, TS3000B3_DEVID, TS3000B3_DEVID_MASK },
|
||||
{ IDT_MANID, TS3000GB2_DEVID, TS3000GB2_DEVID_MASK },
|
||||
{ MAX_MANID, MAX6604_DEVID, MAX6604_DEVID_MASK },
|
||||
@ -158,9 +166,25 @@ static struct jc42_chips jc42_chips[] = {
|
||||
{ STM_MANID, STTS424_DEVID, STTS424_DEVID_MASK },
|
||||
{ STM_MANID, STTS424E_DEVID, STTS424E_DEVID_MASK },
|
||||
{ STM_MANID, STTS2002_DEVID, STTS2002_DEVID_MASK },
|
||||
{ STM_MANID, STTS2004_DEVID, STTS2004_DEVID_MASK },
|
||||
{ STM_MANID, STTS3000_DEVID, STTS3000_DEVID_MASK },
|
||||
};
|
||||
|
||||
enum temp_index {
|
||||
t_input = 0,
|
||||
t_crit,
|
||||
t_min,
|
||||
t_max,
|
||||
t_num_temp
|
||||
};
|
||||
|
||||
static const u8 temp_regs[t_num_temp] = {
|
||||
[t_input] = JC42_REG_TEMP,
|
||||
[t_crit] = JC42_REG_TEMP_CRITICAL,
|
||||
[t_min] = JC42_REG_TEMP_LOWER,
|
||||
[t_max] = JC42_REG_TEMP_UPPER,
|
||||
};
|
||||
|
||||
/* Each client has this additional data */
|
||||
struct jc42_data {
|
||||
struct i2c_client *client;
|
||||
@ -170,69 +194,7 @@ struct jc42_data {
|
||||
unsigned long last_updated; /* In jiffies */
|
||||
u16 orig_config; /* original configuration */
|
||||
u16 config; /* current configuration */
|
||||
u16 temp_input; /* Temperatures */
|
||||
u16 temp_crit;
|
||||
u16 temp_min;
|
||||
u16 temp_max;
|
||||
};
|
||||
|
||||
static int jc42_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id);
|
||||
static int jc42_detect(struct i2c_client *client, struct i2c_board_info *info);
|
||||
static int jc42_remove(struct i2c_client *client);
|
||||
|
||||
static struct jc42_data *jc42_update_device(struct device *dev);
|
||||
|
||||
static const struct i2c_device_id jc42_id[] = {
|
||||
{ "jc42", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, jc42_id);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
static int jc42_suspend(struct device *dev)
|
||||
{
|
||||
struct jc42_data *data = dev_get_drvdata(dev);
|
||||
|
||||
data->config |= JC42_CFG_SHUTDOWN;
|
||||
i2c_smbus_write_word_swapped(data->client, JC42_REG_CONFIG,
|
||||
data->config);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int jc42_resume(struct device *dev)
|
||||
{
|
||||
struct jc42_data *data = dev_get_drvdata(dev);
|
||||
|
||||
data->config &= ~JC42_CFG_SHUTDOWN;
|
||||
i2c_smbus_write_word_swapped(data->client, JC42_REG_CONFIG,
|
||||
data->config);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops jc42_dev_pm_ops = {
|
||||
.suspend = jc42_suspend,
|
||||
.resume = jc42_resume,
|
||||
};
|
||||
|
||||
#define JC42_DEV_PM_OPS (&jc42_dev_pm_ops)
|
||||
#else
|
||||
#define JC42_DEV_PM_OPS NULL
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
/* This is the driver that will be inserted */
|
||||
static struct i2c_driver jc42_driver = {
|
||||
.class = I2C_CLASS_SPD,
|
||||
.driver = {
|
||||
.name = "jc42",
|
||||
.pm = JC42_DEV_PM_OPS,
|
||||
},
|
||||
.probe = jc42_probe,
|
||||
.remove = jc42_remove,
|
||||
.id_table = jc42_id,
|
||||
.detect = jc42_detect,
|
||||
.address_list = normal_i2c,
|
||||
u16 temp[t_num_temp];/* Temperatures */
|
||||
};
|
||||
|
||||
#define JC42_TEMP_MIN_EXTENDED (-40000)
|
||||
@ -261,80 +223,82 @@ static int jc42_temp_from_reg(s16 reg)
|
||||
return reg * 125 / 2;
|
||||
}
|
||||
|
||||
/* sysfs stuff */
|
||||
static struct jc42_data *jc42_update_device(struct device *dev)
|
||||
{
|
||||
struct jc42_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
struct jc42_data *ret = data;
|
||||
int i, val;
|
||||
|
||||
/* read routines for temperature limits */
|
||||
#define show(value) \
|
||||
static ssize_t show_##value(struct device *dev, \
|
||||
struct device_attribute *attr, \
|
||||
char *buf) \
|
||||
{ \
|
||||
struct jc42_data *data = jc42_update_device(dev); \
|
||||
if (IS_ERR(data)) \
|
||||
return PTR_ERR(data); \
|
||||
return sprintf(buf, "%d\n", jc42_temp_from_reg(data->value)); \
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
|
||||
for (i = 0; i < t_num_temp; i++) {
|
||||
val = i2c_smbus_read_word_swapped(client, temp_regs[i]);
|
||||
if (val < 0) {
|
||||
ret = ERR_PTR(val);
|
||||
goto abort;
|
||||
}
|
||||
data->temp[i] = val;
|
||||
}
|
||||
data->last_updated = jiffies;
|
||||
data->valid = true;
|
||||
}
|
||||
abort:
|
||||
mutex_unlock(&data->update_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
show(temp_input);
|
||||
show(temp_crit);
|
||||
show(temp_min);
|
||||
show(temp_max);
|
||||
/* sysfs functions */
|
||||
|
||||
/* read routines for hysteresis values */
|
||||
static ssize_t show_temp_crit_hyst(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
static ssize_t show_temp(struct device *dev, struct device_attribute *devattr,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct jc42_data *data = jc42_update_device(dev);
|
||||
if (IS_ERR(data))
|
||||
return PTR_ERR(data);
|
||||
return sprintf(buf, "%d\n",
|
||||
jc42_temp_from_reg(data->temp[attr->index]));
|
||||
}
|
||||
|
||||
static ssize_t show_temp_hyst(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct jc42_data *data = jc42_update_device(dev);
|
||||
int temp, hyst;
|
||||
|
||||
if (IS_ERR(data))
|
||||
return PTR_ERR(data);
|
||||
|
||||
temp = jc42_temp_from_reg(data->temp_crit);
|
||||
temp = jc42_temp_from_reg(data->temp[attr->index]);
|
||||
hyst = jc42_hysteresis[(data->config & JC42_CFG_HYST_MASK)
|
||||
>> JC42_CFG_HYST_SHIFT];
|
||||
return sprintf(buf, "%d\n", temp - hyst);
|
||||
}
|
||||
|
||||
static ssize_t show_temp_max_hyst(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
static ssize_t set_temp(struct device *dev, struct device_attribute *devattr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct jc42_data *data = jc42_update_device(dev);
|
||||
int temp, hyst;
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct jc42_data *data = dev_get_drvdata(dev);
|
||||
int err, ret = count;
|
||||
int nr = attr->index;
|
||||
long val;
|
||||
|
||||
if (IS_ERR(data))
|
||||
return PTR_ERR(data);
|
||||
|
||||
temp = jc42_temp_from_reg(data->temp_max);
|
||||
hyst = jc42_hysteresis[(data->config & JC42_CFG_HYST_MASK)
|
||||
>> JC42_CFG_HYST_SHIFT];
|
||||
return sprintf(buf, "%d\n", temp - hyst);
|
||||
if (kstrtol(buf, 10, &val) < 0)
|
||||
return -EINVAL;
|
||||
mutex_lock(&data->update_lock);
|
||||
data->temp[nr] = jc42_temp_to_reg(val, data->extended);
|
||||
err = i2c_smbus_write_word_swapped(data->client, temp_regs[nr],
|
||||
data->temp[nr]);
|
||||
if (err < 0)
|
||||
ret = err;
|
||||
mutex_unlock(&data->update_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* write routines */
|
||||
#define set(value, reg) \
|
||||
static ssize_t set_##value(struct device *dev, \
|
||||
struct device_attribute *attr, \
|
||||
const char *buf, size_t count) \
|
||||
{ \
|
||||
struct jc42_data *data = dev_get_drvdata(dev); \
|
||||
int err, ret = count; \
|
||||
long val; \
|
||||
if (kstrtol(buf, 10, &val) < 0) \
|
||||
return -EINVAL; \
|
||||
mutex_lock(&data->update_lock); \
|
||||
data->value = jc42_temp_to_reg(val, data->extended); \
|
||||
err = i2c_smbus_write_word_swapped(data->client, reg, data->value); \
|
||||
if (err < 0) \
|
||||
ret = err; \
|
||||
mutex_unlock(&data->update_lock); \
|
||||
return ret; \
|
||||
}
|
||||
|
||||
set(temp_min, JC42_REG_TEMP_LOWER);
|
||||
set(temp_max, JC42_REG_TEMP_UPPER);
|
||||
set(temp_crit, JC42_REG_TEMP_CRITICAL);
|
||||
|
||||
/*
|
||||
* JC42.4 compliant chips only support four hysteresis values.
|
||||
* Pick best choice and go from there.
|
||||
@ -352,7 +316,7 @@ static ssize_t set_temp_crit_hyst(struct device *dev,
|
||||
if (kstrtoul(buf, 10, &val) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
diff = jc42_temp_from_reg(data->temp_crit) - val;
|
||||
diff = jc42_temp_from_reg(data->temp[t_crit]) - val;
|
||||
hyst = 0;
|
||||
if (diff > 0) {
|
||||
if (diff < 2250)
|
||||
@ -384,25 +348,20 @@ static ssize_t show_alarm(struct device *dev,
|
||||
if (IS_ERR(data))
|
||||
return PTR_ERR(data);
|
||||
|
||||
val = data->temp_input;
|
||||
val = data->temp[t_input];
|
||||
if (bit != JC42_ALARM_CRIT_BIT && (data->config & JC42_CFG_CRIT_ONLY))
|
||||
val = 0;
|
||||
return sprintf(buf, "%u\n", (val >> bit) & 1);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(temp1_input, S_IRUGO,
|
||||
show_temp_input, NULL);
|
||||
static DEVICE_ATTR(temp1_crit, S_IRUGO,
|
||||
show_temp_crit, set_temp_crit);
|
||||
static DEVICE_ATTR(temp1_min, S_IRUGO,
|
||||
show_temp_min, set_temp_min);
|
||||
static DEVICE_ATTR(temp1_max, S_IRUGO,
|
||||
show_temp_max, set_temp_max);
|
||||
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, t_input);
|
||||
static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, show_temp, set_temp, t_crit);
|
||||
static SENSOR_DEVICE_ATTR(temp1_min, S_IRUGO, show_temp, set_temp, t_min);
|
||||
static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, show_temp, set_temp, t_max);
|
||||
|
||||
static DEVICE_ATTR(temp1_crit_hyst, S_IRUGO,
|
||||
show_temp_crit_hyst, set_temp_crit_hyst);
|
||||
static DEVICE_ATTR(temp1_max_hyst, S_IRUGO,
|
||||
show_temp_max_hyst, NULL);
|
||||
static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO, show_temp_hyst,
|
||||
set_temp_crit_hyst, t_crit);
|
||||
static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IRUGO, show_temp_hyst, NULL, t_max);
|
||||
|
||||
static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL,
|
||||
JC42_ALARM_CRIT_BIT);
|
||||
@ -412,12 +371,12 @@ static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL,
|
||||
JC42_ALARM_MAX_BIT);
|
||||
|
||||
static struct attribute *jc42_attributes[] = {
|
||||
&dev_attr_temp1_input.attr,
|
||||
&dev_attr_temp1_crit.attr,
|
||||
&dev_attr_temp1_min.attr,
|
||||
&dev_attr_temp1_max.attr,
|
||||
&dev_attr_temp1_crit_hyst.attr,
|
||||
&dev_attr_temp1_max_hyst.attr,
|
||||
&sensor_dev_attr_temp1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_crit.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_min.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
|
||||
@ -432,12 +391,12 @@ static umode_t jc42_attribute_mode(struct kobject *kobj,
|
||||
unsigned int config = data->config;
|
||||
bool readonly;
|
||||
|
||||
if (attr == &dev_attr_temp1_crit.attr)
|
||||
if (attr == &sensor_dev_attr_temp1_crit.dev_attr.attr)
|
||||
readonly = config & JC42_CFG_TCRIT_LOCK;
|
||||
else if (attr == &dev_attr_temp1_min.attr ||
|
||||
attr == &dev_attr_temp1_max.attr)
|
||||
else if (attr == &sensor_dev_attr_temp1_min.dev_attr.attr ||
|
||||
attr == &sensor_dev_attr_temp1_max.dev_attr.attr)
|
||||
readonly = config & JC42_CFG_EVENT_LOCK;
|
||||
else if (attr == &dev_attr_temp1_crit_hyst.attr)
|
||||
else if (attr == &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr)
|
||||
readonly = config & (JC42_CFG_EVENT_LOCK | JC42_CFG_TCRIT_LOCK);
|
||||
else
|
||||
readonly = true;
|
||||
@ -537,53 +496,57 @@ static int jc42_remove(struct i2c_client *client)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct jc42_data *jc42_update_device(struct device *dev)
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
static int jc42_suspend(struct device *dev)
|
||||
{
|
||||
struct jc42_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
struct jc42_data *ret = data;
|
||||
int val;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
|
||||
val = i2c_smbus_read_word_swapped(client, JC42_REG_TEMP);
|
||||
if (val < 0) {
|
||||
ret = ERR_PTR(val);
|
||||
goto abort;
|
||||
}
|
||||
data->temp_input = val;
|
||||
|
||||
val = i2c_smbus_read_word_swapped(client,
|
||||
JC42_REG_TEMP_CRITICAL);
|
||||
if (val < 0) {
|
||||
ret = ERR_PTR(val);
|
||||
goto abort;
|
||||
}
|
||||
data->temp_crit = val;
|
||||
|
||||
val = i2c_smbus_read_word_swapped(client, JC42_REG_TEMP_LOWER);
|
||||
if (val < 0) {
|
||||
ret = ERR_PTR(val);
|
||||
goto abort;
|
||||
}
|
||||
data->temp_min = val;
|
||||
|
||||
val = i2c_smbus_read_word_swapped(client, JC42_REG_TEMP_UPPER);
|
||||
if (val < 0) {
|
||||
ret = ERR_PTR(val);
|
||||
goto abort;
|
||||
}
|
||||
data->temp_max = val;
|
||||
|
||||
data->last_updated = jiffies;
|
||||
data->valid = true;
|
||||
}
|
||||
abort:
|
||||
mutex_unlock(&data->update_lock);
|
||||
return ret;
|
||||
data->config |= JC42_CFG_SHUTDOWN;
|
||||
i2c_smbus_write_word_swapped(data->client, JC42_REG_CONFIG,
|
||||
data->config);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int jc42_resume(struct device *dev)
|
||||
{
|
||||
struct jc42_data *data = dev_get_drvdata(dev);
|
||||
|
||||
data->config &= ~JC42_CFG_SHUTDOWN;
|
||||
i2c_smbus_write_word_swapped(data->client, JC42_REG_CONFIG,
|
||||
data->config);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops jc42_dev_pm_ops = {
|
||||
.suspend = jc42_suspend,
|
||||
.resume = jc42_resume,
|
||||
};
|
||||
|
||||
#define JC42_DEV_PM_OPS (&jc42_dev_pm_ops)
|
||||
#else
|
||||
#define JC42_DEV_PM_OPS NULL
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static const struct i2c_device_id jc42_id[] = {
|
||||
{ "jc42", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, jc42_id);
|
||||
|
||||
static struct i2c_driver jc42_driver = {
|
||||
.class = I2C_CLASS_SPD,
|
||||
.driver = {
|
||||
.name = "jc42",
|
||||
.pm = JC42_DEV_PM_OPS,
|
||||
},
|
||||
.probe = jc42_probe,
|
||||
.remove = jc42_remove,
|
||||
.id_table = jc42_id,
|
||||
.detect = jc42_detect,
|
||||
.address_list = normal_i2c,
|
||||
};
|
||||
|
||||
module_i2c_driver(jc42_driver);
|
||||
|
||||
MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
|
||||
|
@ -47,7 +47,7 @@
|
||||
#define LM70_CHIP_LM74 3 /* NS LM74 */
|
||||
|
||||
struct lm70 {
|
||||
struct device *hwmon_dev;
|
||||
struct spi_device *spi;
|
||||
struct mutex lock;
|
||||
unsigned int chip;
|
||||
};
|
||||
@ -56,11 +56,11 @@ struct lm70 {
|
||||
static ssize_t lm70_sense_temp(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
struct lm70 *p_lm70 = dev_get_drvdata(dev);
|
||||
struct spi_device *spi = p_lm70->spi;
|
||||
int status, val = 0;
|
||||
u8 rxbuf[2];
|
||||
s16 raw = 0;
|
||||
struct lm70 *p_lm70 = spi_get_drvdata(spi);
|
||||
|
||||
if (mutex_lock_interruptible(&p_lm70->lock))
|
||||
return -ERESTARTSYS;
|
||||
@ -121,21 +121,20 @@ out:
|
||||
|
||||
static DEVICE_ATTR(temp1_input, S_IRUGO, lm70_sense_temp, NULL);
|
||||
|
||||
static ssize_t lm70_show_name(struct device *dev, struct device_attribute
|
||||
*devattr, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%s\n", to_spi_device(dev)->modalias);
|
||||
}
|
||||
static struct attribute *lm70_attrs[] = {
|
||||
&dev_attr_temp1_input.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static DEVICE_ATTR(name, S_IRUGO, lm70_show_name, NULL);
|
||||
ATTRIBUTE_GROUPS(lm70);
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
static int lm70_probe(struct spi_device *spi)
|
||||
{
|
||||
int chip = spi_get_device_id(spi)->driver_data;
|
||||
struct device *hwmon_dev;
|
||||
struct lm70 *p_lm70;
|
||||
int status;
|
||||
|
||||
/* signaling is SPI_MODE_0 */
|
||||
if (spi->mode & (SPI_CPOL | SPI_CPHA))
|
||||
@ -149,46 +148,14 @@ static int lm70_probe(struct spi_device *spi)
|
||||
|
||||
mutex_init(&p_lm70->lock);
|
||||
p_lm70->chip = chip;
|
||||
p_lm70->spi = spi;
|
||||
|
||||
spi_set_drvdata(spi, p_lm70);
|
||||
|
||||
status = device_create_file(&spi->dev, &dev_attr_temp1_input);
|
||||
if (status)
|
||||
goto out_dev_create_temp_file_failed;
|
||||
status = device_create_file(&spi->dev, &dev_attr_name);
|
||||
if (status)
|
||||
goto out_dev_create_file_failed;
|
||||
|
||||
/* sysfs hook */
|
||||
p_lm70->hwmon_dev = hwmon_device_register(&spi->dev);
|
||||
if (IS_ERR(p_lm70->hwmon_dev)) {
|
||||
dev_dbg(&spi->dev, "hwmon_device_register failed.\n");
|
||||
status = PTR_ERR(p_lm70->hwmon_dev);
|
||||
goto out_dev_reg_failed;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_dev_reg_failed:
|
||||
device_remove_file(&spi->dev, &dev_attr_name);
|
||||
out_dev_create_file_failed:
|
||||
device_remove_file(&spi->dev, &dev_attr_temp1_input);
|
||||
out_dev_create_temp_file_failed:
|
||||
return status;
|
||||
hwmon_dev = devm_hwmon_device_register_with_groups(&spi->dev,
|
||||
spi->modalias,
|
||||
p_lm70, lm70_groups);
|
||||
return PTR_ERR_OR_ZERO(hwmon_dev);
|
||||
}
|
||||
|
||||
static int lm70_remove(struct spi_device *spi)
|
||||
{
|
||||
struct lm70 *p_lm70 = spi_get_drvdata(spi);
|
||||
|
||||
hwmon_device_unregister(p_lm70->hwmon_dev);
|
||||
device_remove_file(&spi->dev, &dev_attr_temp1_input);
|
||||
device_remove_file(&spi->dev, &dev_attr_name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const struct spi_device_id lm70_ids[] = {
|
||||
{ "lm70", LM70_CHIP_LM70 },
|
||||
{ "tmp121", LM70_CHIP_TMP121 },
|
||||
@ -205,7 +172,6 @@ static struct spi_driver lm70_driver = {
|
||||
},
|
||||
.id_table = lm70_ids,
|
||||
.probe = lm70_probe,
|
||||
.remove = lm70_remove,
|
||||
};
|
||||
|
||||
module_spi_driver(lm70_driver);
|
||||
|
@ -72,6 +72,7 @@ static const u8 LM75_REG_TEMP[3] = {
|
||||
|
||||
/* Each client has this additional data */
|
||||
struct lm75_data {
|
||||
struct i2c_client *client;
|
||||
struct device *hwmon_dev;
|
||||
struct thermal_zone_device *tz;
|
||||
struct mutex update_lock;
|
||||
@ -130,8 +131,8 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *da,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct lm75_data *data = i2c_get_clientdata(client);
|
||||
struct lm75_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
int nr = attr->index;
|
||||
long temp;
|
||||
int error;
|
||||
@ -165,17 +166,14 @@ static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO,
|
||||
show_temp, set_temp, 2);
|
||||
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0);
|
||||
|
||||
static struct attribute *lm75_attributes[] = {
|
||||
static struct attribute *lm75_attrs[] = {
|
||||
&sensor_dev_attr_temp1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
|
||||
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group lm75_group = {
|
||||
.attrs = lm75_attributes,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(lm75);
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
@ -184,6 +182,7 @@ static const struct attribute_group lm75_group = {
|
||||
static int
|
||||
lm75_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct lm75_data *data;
|
||||
int status;
|
||||
u8 set_mask, clr_mask;
|
||||
@ -194,10 +193,11 @@ lm75_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
||||
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA))
|
||||
return -EIO;
|
||||
|
||||
data = devm_kzalloc(&client->dev, sizeof(struct lm75_data), GFP_KERNEL);
|
||||
data = devm_kzalloc(dev, sizeof(struct lm75_data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
data->client = client;
|
||||
i2c_set_clientdata(client, data);
|
||||
mutex_init(&data->update_lock);
|
||||
|
||||
@ -269,7 +269,7 @@ lm75_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
||||
/* configure as specified */
|
||||
status = lm75_read_value(client, LM75_REG_CONF);
|
||||
if (status < 0) {
|
||||
dev_dbg(&client->dev, "Can't read config? %d\n", status);
|
||||
dev_dbg(dev, "Can't read config? %d\n", status);
|
||||
return status;
|
||||
}
|
||||
data->orig_conf = status;
|
||||
@ -277,43 +277,32 @@ lm75_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
||||
new |= set_mask;
|
||||
if (status != new)
|
||||
lm75_write_value(client, LM75_REG_CONF, new);
|
||||
dev_dbg(&client->dev, "Config %02x\n", new);
|
||||
dev_dbg(dev, "Config %02x\n", new);
|
||||
|
||||
/* Register sysfs hooks */
|
||||
status = sysfs_create_group(&client->dev.kobj, &lm75_group);
|
||||
if (status)
|
||||
return status;
|
||||
data->hwmon_dev = hwmon_device_register_with_groups(dev, client->name,
|
||||
data, lm75_groups);
|
||||
if (IS_ERR(data->hwmon_dev))
|
||||
return PTR_ERR(data->hwmon_dev);
|
||||
|
||||
data->hwmon_dev = hwmon_device_register(&client->dev);
|
||||
if (IS_ERR(data->hwmon_dev)) {
|
||||
status = PTR_ERR(data->hwmon_dev);
|
||||
goto exit_remove;
|
||||
}
|
||||
|
||||
data->tz = thermal_zone_of_sensor_register(&client->dev,
|
||||
data->tz = thermal_zone_of_sensor_register(data->hwmon_dev,
|
||||
0,
|
||||
&client->dev,
|
||||
data->hwmon_dev,
|
||||
lm75_read_temp, NULL);
|
||||
if (IS_ERR(data->tz))
|
||||
data->tz = NULL;
|
||||
|
||||
dev_info(&client->dev, "%s: sensor '%s'\n",
|
||||
dev_info(dev, "%s: sensor '%s'\n",
|
||||
dev_name(data->hwmon_dev), client->name);
|
||||
|
||||
return 0;
|
||||
|
||||
exit_remove:
|
||||
sysfs_remove_group(&client->dev.kobj, &lm75_group);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int lm75_remove(struct i2c_client *client)
|
||||
{
|
||||
struct lm75_data *data = i2c_get_clientdata(client);
|
||||
|
||||
thermal_zone_of_sensor_unregister(&client->dev, data->tz);
|
||||
thermal_zone_of_sensor_unregister(data->hwmon_dev, data->tz);
|
||||
hwmon_device_unregister(data->hwmon_dev);
|
||||
sysfs_remove_group(&client->dev.kobj, &lm75_group);
|
||||
lm75_write_value(client, LM75_REG_CONF, data->orig_conf);
|
||||
return 0;
|
||||
}
|
||||
@ -507,8 +496,8 @@ static int lm75_write_value(struct i2c_client *client, u8 reg, u16 value)
|
||||
|
||||
static struct lm75_data *lm75_update_device(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct lm75_data *data = i2c_get_clientdata(client);
|
||||
struct lm75_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
struct lm75_data *ret = data;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
@ -19,10 +19,6 @@
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
@ -47,50 +43,33 @@ static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b,
|
||||
#define LM77_REG_TEMP_MIN 0x04
|
||||
#define LM77_REG_TEMP_MAX 0x05
|
||||
|
||||
enum temp_index {
|
||||
t_input = 0,
|
||||
t_crit,
|
||||
t_min,
|
||||
t_max,
|
||||
t_hyst,
|
||||
t_num_temp
|
||||
};
|
||||
|
||||
static const u8 temp_regs[t_num_temp] = {
|
||||
[t_input] = LM77_REG_TEMP,
|
||||
[t_min] = LM77_REG_TEMP_MIN,
|
||||
[t_max] = LM77_REG_TEMP_MAX,
|
||||
[t_crit] = LM77_REG_TEMP_CRIT,
|
||||
[t_hyst] = LM77_REG_TEMP_HYST,
|
||||
};
|
||||
|
||||
/* Each client has this additional data */
|
||||
struct lm77_data {
|
||||
struct device *hwmon_dev;
|
||||
struct i2c_client *client;
|
||||
struct mutex update_lock;
|
||||
char valid;
|
||||
unsigned long last_updated; /* In jiffies */
|
||||
int temp_input; /* Temperatures */
|
||||
int temp_crit;
|
||||
int temp_min;
|
||||
int temp_max;
|
||||
int temp_hyst;
|
||||
int temp[t_num_temp]; /* index using temp_index */
|
||||
u8 alarms;
|
||||
};
|
||||
|
||||
static int lm77_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id);
|
||||
static int lm77_detect(struct i2c_client *client, struct i2c_board_info *info);
|
||||
static void lm77_init_client(struct i2c_client *client);
|
||||
static int lm77_remove(struct i2c_client *client);
|
||||
static u16 lm77_read_value(struct i2c_client *client, u8 reg);
|
||||
static int lm77_write_value(struct i2c_client *client, u8 reg, u16 value);
|
||||
|
||||
static struct lm77_data *lm77_update_device(struct device *dev);
|
||||
|
||||
|
||||
static const struct i2c_device_id lm77_id[] = {
|
||||
{ "lm77", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, lm77_id);
|
||||
|
||||
/* This is the driver that will be inserted */
|
||||
static struct i2c_driver lm77_driver = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = "lm77",
|
||||
},
|
||||
.probe = lm77_probe,
|
||||
.remove = lm77_remove,
|
||||
.id_table = lm77_id,
|
||||
.detect = lm77_detect,
|
||||
.address_list = normal_i2c,
|
||||
};
|
||||
|
||||
/* straight from the datasheet */
|
||||
#define LM77_TEMP_MIN (-55000)
|
||||
#define LM77_TEMP_MAX 125000
|
||||
@ -110,97 +89,109 @@ static inline int LM77_TEMP_FROM_REG(s16 reg)
|
||||
return (reg / 8) * 500;
|
||||
}
|
||||
|
||||
/*
|
||||
* All registers are word-sized, except for the configuration register.
|
||||
* The LM77 uses the high-byte first convention.
|
||||
*/
|
||||
static u16 lm77_read_value(struct i2c_client *client, u8 reg)
|
||||
{
|
||||
if (reg == LM77_REG_CONF)
|
||||
return i2c_smbus_read_byte_data(client, reg);
|
||||
else
|
||||
return i2c_smbus_read_word_swapped(client, reg);
|
||||
}
|
||||
|
||||
static int lm77_write_value(struct i2c_client *client, u8 reg, u16 value)
|
||||
{
|
||||
if (reg == LM77_REG_CONF)
|
||||
return i2c_smbus_write_byte_data(client, reg, value);
|
||||
else
|
||||
return i2c_smbus_write_word_swapped(client, reg, value);
|
||||
}
|
||||
|
||||
static struct lm77_data *lm77_update_device(struct device *dev)
|
||||
{
|
||||
struct lm77_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
int i;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
|
||||
|| !data->valid) {
|
||||
dev_dbg(&client->dev, "Starting lm77 update\n");
|
||||
for (i = 0; i < t_num_temp; i++) {
|
||||
data->temp[i] =
|
||||
LM77_TEMP_FROM_REG(lm77_read_value(client,
|
||||
temp_regs[i]));
|
||||
}
|
||||
data->alarms =
|
||||
lm77_read_value(client, LM77_REG_TEMP) & 0x0007;
|
||||
data->last_updated = jiffies;
|
||||
data->valid = 1;
|
||||
}
|
||||
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/* sysfs stuff */
|
||||
|
||||
/* read routines for temperature limits */
|
||||
#define show(value) \
|
||||
static ssize_t show_##value(struct device *dev, \
|
||||
struct device_attribute *attr, \
|
||||
char *buf) \
|
||||
{ \
|
||||
struct lm77_data *data = lm77_update_device(dev); \
|
||||
return sprintf(buf, "%d\n", data->value); \
|
||||
}
|
||||
|
||||
show(temp_input);
|
||||
show(temp_crit);
|
||||
show(temp_min);
|
||||
show(temp_max);
|
||||
|
||||
/* read routines for hysteresis values */
|
||||
static ssize_t show_temp_crit_hyst(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
static ssize_t show_temp(struct device *dev, struct device_attribute *devattr,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct lm77_data *data = lm77_update_device(dev);
|
||||
return sprintf(buf, "%d\n", data->temp_crit - data->temp_hyst);
|
||||
|
||||
return sprintf(buf, "%d\n", data->temp[attr->index]);
|
||||
}
|
||||
static ssize_t show_temp_min_hyst(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
|
||||
static ssize_t show_temp_hyst(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct lm77_data *data = lm77_update_device(dev);
|
||||
return sprintf(buf, "%d\n", data->temp_min + data->temp_hyst);
|
||||
int nr = attr->index;
|
||||
int temp;
|
||||
|
||||
temp = nr == t_min ? data->temp[nr] + data->temp[t_hyst] :
|
||||
data->temp[nr] - data->temp[t_hyst];
|
||||
|
||||
return sprintf(buf, "%d\n", temp);
|
||||
}
|
||||
static ssize_t show_temp_max_hyst(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
|
||||
static ssize_t set_temp(struct device *dev, struct device_attribute *devattr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct lm77_data *data = lm77_update_device(dev);
|
||||
return sprintf(buf, "%d\n", data->temp_max - data->temp_hyst);
|
||||
}
|
||||
|
||||
/* write routines */
|
||||
#define set(value, reg) \
|
||||
static ssize_t set_##value(struct device *dev, struct device_attribute *attr, \
|
||||
const char *buf, size_t count) \
|
||||
{ \
|
||||
struct i2c_client *client = to_i2c_client(dev); \
|
||||
struct lm77_data *data = i2c_get_clientdata(client); \
|
||||
long val; \
|
||||
int err = kstrtol(buf, 10, &val); \
|
||||
if (err) \
|
||||
return err; \
|
||||
\
|
||||
mutex_lock(&data->update_lock); \
|
||||
data->value = val; \
|
||||
lm77_write_value(client, reg, LM77_TEMP_TO_REG(data->value)); \
|
||||
mutex_unlock(&data->update_lock); \
|
||||
return count; \
|
||||
}
|
||||
|
||||
set(temp_min, LM77_REG_TEMP_MIN);
|
||||
set(temp_max, LM77_REG_TEMP_MAX);
|
||||
|
||||
/*
|
||||
* hysteresis is stored as a relative value on the chip, so it has to be
|
||||
* converted first
|
||||
*/
|
||||
static ssize_t set_temp_crit_hyst(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct lm77_data *data = i2c_get_clientdata(client);
|
||||
unsigned long val;
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct lm77_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
int nr = attr->index;
|
||||
long val;
|
||||
int err;
|
||||
|
||||
err = kstrtoul(buf, 10, &val);
|
||||
err = kstrtol(buf, 10, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->temp_hyst = data->temp_crit - val;
|
||||
lm77_write_value(client, LM77_REG_TEMP_HYST,
|
||||
LM77_TEMP_TO_REG(data->temp_hyst));
|
||||
data->temp[nr] = val;
|
||||
lm77_write_value(client, temp_regs[nr], LM77_TEMP_TO_REG(val));
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
|
||||
/* preserve hysteresis when setting T_crit */
|
||||
static ssize_t set_temp_crit(struct device *dev, struct device_attribute *attr,
|
||||
/*
|
||||
* hysteresis is stored as a relative value on the chip, so it has to be
|
||||
* converted first.
|
||||
*/
|
||||
static ssize_t set_temp_hyst(struct device *dev,
|
||||
struct device_attribute *devattr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct lm77_data *data = i2c_get_clientdata(client);
|
||||
int oldcrithyst;
|
||||
struct lm77_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
unsigned long val;
|
||||
int err;
|
||||
|
||||
@ -209,13 +200,9 @@ static ssize_t set_temp_crit(struct device *dev, struct device_attribute *attr,
|
||||
return err;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
oldcrithyst = data->temp_crit - data->temp_hyst;
|
||||
data->temp_crit = val;
|
||||
data->temp_hyst = data->temp_crit - oldcrithyst;
|
||||
lm77_write_value(client, LM77_REG_TEMP_CRIT,
|
||||
LM77_TEMP_TO_REG(data->temp_crit));
|
||||
data->temp[t_hyst] = data->temp[t_crit] - val;
|
||||
lm77_write_value(client, LM77_REG_TEMP_HYST,
|
||||
LM77_TEMP_TO_REG(data->temp_hyst));
|
||||
LM77_TEMP_TO_REG(data->temp[t_hyst]));
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
@ -228,43 +215,37 @@ static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,
|
||||
return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(temp1_input, S_IRUGO,
|
||||
show_temp_input, NULL);
|
||||
static DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO,
|
||||
show_temp_crit, set_temp_crit);
|
||||
static DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO,
|
||||
show_temp_min, set_temp_min);
|
||||
static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO,
|
||||
show_temp_max, set_temp_max);
|
||||
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, t_input);
|
||||
static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp, set_temp,
|
||||
t_crit);
|
||||
static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp, set_temp,
|
||||
t_min);
|
||||
static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp, set_temp,
|
||||
t_max);
|
||||
|
||||
static DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO,
|
||||
show_temp_crit_hyst, set_temp_crit_hyst);
|
||||
static DEVICE_ATTR(temp1_min_hyst, S_IRUGO,
|
||||
show_temp_min_hyst, NULL);
|
||||
static DEVICE_ATTR(temp1_max_hyst, S_IRUGO,
|
||||
show_temp_max_hyst, NULL);
|
||||
static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temp_hyst,
|
||||
set_temp_hyst, t_crit);
|
||||
static SENSOR_DEVICE_ATTR(temp1_min_hyst, S_IRUGO, show_temp_hyst, NULL, t_min);
|
||||
static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IRUGO, show_temp_hyst, NULL, t_max);
|
||||
|
||||
static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, 2);
|
||||
static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_alarm, NULL, 0);
|
||||
static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 1);
|
||||
|
||||
static struct attribute *lm77_attributes[] = {
|
||||
&dev_attr_temp1_input.attr,
|
||||
&dev_attr_temp1_crit.attr,
|
||||
&dev_attr_temp1_min.attr,
|
||||
&dev_attr_temp1_max.attr,
|
||||
&dev_attr_temp1_crit_hyst.attr,
|
||||
&dev_attr_temp1_min_hyst.attr,
|
||||
&dev_attr_temp1_max_hyst.attr,
|
||||
static struct attribute *lm77_attrs[] = {
|
||||
&sensor_dev_attr_temp1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_crit.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_min.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_min_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group lm77_group = {
|
||||
.attrs = lm77_attributes,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(lm77);
|
||||
|
||||
/* Return 0 if detection is successful, -ENODEV otherwise */
|
||||
static int lm77_detect(struct i2c_client *client, struct i2c_board_info *info)
|
||||
@ -337,68 +318,6 @@ static int lm77_detect(struct i2c_client *client, struct i2c_board_info *info)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lm77_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct lm77_data *data;
|
||||
int err;
|
||||
|
||||
data = devm_kzalloc(dev, sizeof(struct lm77_data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_set_clientdata(client, data);
|
||||
mutex_init(&data->update_lock);
|
||||
|
||||
/* Initialize the LM77 chip */
|
||||
lm77_init_client(client);
|
||||
|
||||
/* Register sysfs hooks */
|
||||
err = sysfs_create_group(&dev->kobj, &lm77_group);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
data->hwmon_dev = hwmon_device_register(dev);
|
||||
if (IS_ERR(data->hwmon_dev)) {
|
||||
err = PTR_ERR(data->hwmon_dev);
|
||||
goto exit_remove;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
exit_remove:
|
||||
sysfs_remove_group(&dev->kobj, &lm77_group);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int lm77_remove(struct i2c_client *client)
|
||||
{
|
||||
struct lm77_data *data = i2c_get_clientdata(client);
|
||||
hwmon_device_unregister(data->hwmon_dev);
|
||||
sysfs_remove_group(&client->dev.kobj, &lm77_group);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* All registers are word-sized, except for the configuration register.
|
||||
* The LM77 uses the high-byte first convention.
|
||||
*/
|
||||
static u16 lm77_read_value(struct i2c_client *client, u8 reg)
|
||||
{
|
||||
if (reg == LM77_REG_CONF)
|
||||
return i2c_smbus_read_byte_data(client, reg);
|
||||
else
|
||||
return i2c_smbus_read_word_swapped(client, reg);
|
||||
}
|
||||
|
||||
static int lm77_write_value(struct i2c_client *client, u8 reg, u16 value)
|
||||
{
|
||||
if (reg == LM77_REG_CONF)
|
||||
return i2c_smbus_write_byte_data(client, reg, value);
|
||||
else
|
||||
return i2c_smbus_write_word_swapped(client, reg, value);
|
||||
}
|
||||
|
||||
static void lm77_init_client(struct i2c_client *client)
|
||||
{
|
||||
/* Initialize the LM77 chip - turn off shutdown mode */
|
||||
@ -407,42 +326,45 @@ static void lm77_init_client(struct i2c_client *client)
|
||||
lm77_write_value(client, LM77_REG_CONF, conf & 0xfe);
|
||||
}
|
||||
|
||||
static struct lm77_data *lm77_update_device(struct device *dev)
|
||||
static int lm77_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct lm77_data *data = i2c_get_clientdata(client);
|
||||
struct device *dev = &client->dev;
|
||||
struct device *hwmon_dev;
|
||||
struct lm77_data *data;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data = devm_kzalloc(dev, sizeof(struct lm77_data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
|
||||
|| !data->valid) {
|
||||
dev_dbg(&client->dev, "Starting lm77 update\n");
|
||||
data->temp_input =
|
||||
LM77_TEMP_FROM_REG(lm77_read_value(client,
|
||||
LM77_REG_TEMP));
|
||||
data->temp_hyst =
|
||||
LM77_TEMP_FROM_REG(lm77_read_value(client,
|
||||
LM77_REG_TEMP_HYST));
|
||||
data->temp_crit =
|
||||
LM77_TEMP_FROM_REG(lm77_read_value(client,
|
||||
LM77_REG_TEMP_CRIT));
|
||||
data->temp_min =
|
||||
LM77_TEMP_FROM_REG(lm77_read_value(client,
|
||||
LM77_REG_TEMP_MIN));
|
||||
data->temp_max =
|
||||
LM77_TEMP_FROM_REG(lm77_read_value(client,
|
||||
LM77_REG_TEMP_MAX));
|
||||
data->alarms =
|
||||
lm77_read_value(client, LM77_REG_TEMP) & 0x0007;
|
||||
data->last_updated = jiffies;
|
||||
data->valid = 1;
|
||||
}
|
||||
data->client = client;
|
||||
mutex_init(&data->update_lock);
|
||||
|
||||
mutex_unlock(&data->update_lock);
|
||||
/* Initialize the LM77 chip */
|
||||
lm77_init_client(client);
|
||||
|
||||
return data;
|
||||
hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
|
||||
data, lm77_groups);
|
||||
return PTR_ERR_OR_ZERO(hwmon_dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id lm77_id[] = {
|
||||
{ "lm77", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, lm77_id);
|
||||
|
||||
/* This is the driver that will be inserted */
|
||||
static struct i2c_driver lm77_driver = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = "lm77",
|
||||
},
|
||||
.probe = lm77_probe,
|
||||
.id_table = lm77_id,
|
||||
.detect = lm77_detect,
|
||||
.address_list = normal_i2c,
|
||||
};
|
||||
|
||||
module_i2c_driver(lm77_driver);
|
||||
|
||||
MODULE_AUTHOR("Andras BALI <drewie@freemail.hu>");
|
||||
|
@ -86,27 +86,42 @@ static inline unsigned char FAN_TO_REG(unsigned rpm, unsigned div)
|
||||
#define FAN_FROM_REG(val, div) ((val) == 0 ? -1 : \
|
||||
(val) == 255 ? 0 : 1350000/((div) * (val)))
|
||||
|
||||
static inline long TEMP_FROM_REG(u16 temp)
|
||||
{
|
||||
long res;
|
||||
|
||||
temp >>= 4;
|
||||
if (temp < 0x0800)
|
||||
res = 625 * (long) temp;
|
||||
else
|
||||
res = ((long) temp - 0x01000) * 625;
|
||||
|
||||
return res / 10;
|
||||
}
|
||||
|
||||
#define TEMP_LIMIT_FROM_REG(val) (((val) > 0x80 ? \
|
||||
(val) - 0x100 : (val)) * 1000)
|
||||
|
||||
#define TEMP_LIMIT_TO_REG(val) clamp_val((val) < 0 ? \
|
||||
((val) - 500) / 1000 : ((val) + 500) / 1000, 0, 255)
|
||||
#define TEMP_FROM_REG(reg) ((reg) * 125 / 32)
|
||||
#define TEMP_TO_REG(temp) (DIV_ROUND_CLOSEST(clamp_val((temp), \
|
||||
-128000, 127000), 1000) << 8)
|
||||
|
||||
#define DIV_FROM_REG(val) (1 << (val))
|
||||
|
||||
enum temp_index {
|
||||
t_input = 0,
|
||||
t_hot_max,
|
||||
t_hot_hyst,
|
||||
t_os_max,
|
||||
t_os_hyst,
|
||||
t_num_temp
|
||||
};
|
||||
|
||||
static const u8 temp_regs[t_num_temp] = {
|
||||
[t_input] = LM80_REG_TEMP,
|
||||
[t_hot_max] = LM80_REG_TEMP_HOT_MAX,
|
||||
[t_hot_hyst] = LM80_REG_TEMP_HOT_HYST,
|
||||
[t_os_max] = LM80_REG_TEMP_OS_MAX,
|
||||
[t_os_hyst] = LM80_REG_TEMP_OS_HYST,
|
||||
};
|
||||
|
||||
enum in_index {
|
||||
i_input = 0,
|
||||
i_max,
|
||||
i_min,
|
||||
i_num_in
|
||||
};
|
||||
|
||||
enum fan_index {
|
||||
f_input,
|
||||
f_min,
|
||||
f_num_fan
|
||||
};
|
||||
|
||||
/*
|
||||
* Client data (each client gets its own)
|
||||
*/
|
||||
@ -118,106 +133,187 @@ struct lm80_data {
|
||||
char valid; /* !=0 if following fields are valid */
|
||||
unsigned long last_updated; /* In jiffies */
|
||||
|
||||
u8 in[7]; /* Register value */
|
||||
u8 in_max[7]; /* Register value */
|
||||
u8 in_min[7]; /* Register value */
|
||||
u8 fan[2]; /* Register value */
|
||||
u8 fan_min[2]; /* Register value */
|
||||
u8 in[i_num_in][7]; /* Register value, 1st index is enum in_index */
|
||||
u8 fan[f_num_fan][2]; /* Register value, 1st index enum fan_index */
|
||||
u8 fan_div[2]; /* Register encoding, shifted right */
|
||||
u16 temp; /* Register values, shifted right */
|
||||
u8 temp_hot_max; /* Register value */
|
||||
u8 temp_hot_hyst; /* Register value */
|
||||
u8 temp_os_max; /* Register value */
|
||||
u8 temp_os_hyst; /* Register value */
|
||||
s16 temp[t_num_temp]; /* Register values, normalized to 16 bit */
|
||||
u16 alarms; /* Register encoding, combined */
|
||||
};
|
||||
|
||||
/*
|
||||
* Functions declaration
|
||||
*/
|
||||
static int lm80_read_value(struct i2c_client *client, u8 reg)
|
||||
{
|
||||
return i2c_smbus_read_byte_data(client, reg);
|
||||
}
|
||||
|
||||
static int lm80_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id);
|
||||
static int lm80_detect(struct i2c_client *client, struct i2c_board_info *info);
|
||||
static void lm80_init_client(struct i2c_client *client);
|
||||
static struct lm80_data *lm80_update_device(struct device *dev);
|
||||
static int lm80_read_value(struct i2c_client *client, u8 reg);
|
||||
static int lm80_write_value(struct i2c_client *client, u8 reg, u8 value);
|
||||
static int lm80_write_value(struct i2c_client *client, u8 reg, u8 value)
|
||||
{
|
||||
return i2c_smbus_write_byte_data(client, reg, value);
|
||||
}
|
||||
|
||||
/*
|
||||
* Driver data (common to all clients)
|
||||
*/
|
||||
/* Called when we have found a new LM80 and after read errors */
|
||||
static void lm80_init_client(struct i2c_client *client)
|
||||
{
|
||||
/*
|
||||
* Reset all except Watchdog values and last conversion values
|
||||
* This sets fan-divs to 2, among others. This makes most other
|
||||
* initializations unnecessary
|
||||
*/
|
||||
lm80_write_value(client, LM80_REG_CONFIG, 0x80);
|
||||
/* Set 11-bit temperature resolution */
|
||||
lm80_write_value(client, LM80_REG_RES, 0x08);
|
||||
|
||||
static const struct i2c_device_id lm80_id[] = {
|
||||
{ "lm80", 0 },
|
||||
{ "lm96080", 1 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, lm80_id);
|
||||
/* Start monitoring */
|
||||
lm80_write_value(client, LM80_REG_CONFIG, 0x01);
|
||||
}
|
||||
|
||||
static struct i2c_driver lm80_driver = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = "lm80",
|
||||
},
|
||||
.probe = lm80_probe,
|
||||
.id_table = lm80_id,
|
||||
.detect = lm80_detect,
|
||||
.address_list = normal_i2c,
|
||||
};
|
||||
static struct lm80_data *lm80_update_device(struct device *dev)
|
||||
{
|
||||
struct lm80_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
int i;
|
||||
int rv;
|
||||
int prev_rv;
|
||||
struct lm80_data *ret = data;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
if (data->error)
|
||||
lm80_init_client(client);
|
||||
|
||||
if (time_after(jiffies, data->last_updated + 2 * HZ) || !data->valid) {
|
||||
dev_dbg(dev, "Starting lm80 update\n");
|
||||
for (i = 0; i <= 6; i++) {
|
||||
rv = lm80_read_value(client, LM80_REG_IN(i));
|
||||
if (rv < 0)
|
||||
goto abort;
|
||||
data->in[i_input][i] = rv;
|
||||
|
||||
rv = lm80_read_value(client, LM80_REG_IN_MIN(i));
|
||||
if (rv < 0)
|
||||
goto abort;
|
||||
data->in[i_min][i] = rv;
|
||||
|
||||
rv = lm80_read_value(client, LM80_REG_IN_MAX(i));
|
||||
if (rv < 0)
|
||||
goto abort;
|
||||
data->in[i_max][i] = rv;
|
||||
}
|
||||
|
||||
rv = lm80_read_value(client, LM80_REG_FAN1);
|
||||
if (rv < 0)
|
||||
goto abort;
|
||||
data->fan[f_input][0] = rv;
|
||||
|
||||
rv = lm80_read_value(client, LM80_REG_FAN_MIN(1));
|
||||
if (rv < 0)
|
||||
goto abort;
|
||||
data->fan[f_min][0] = rv;
|
||||
|
||||
rv = lm80_read_value(client, LM80_REG_FAN2);
|
||||
if (rv < 0)
|
||||
goto abort;
|
||||
data->fan[f_input][1] = rv;
|
||||
|
||||
rv = lm80_read_value(client, LM80_REG_FAN_MIN(2));
|
||||
if (rv < 0)
|
||||
goto abort;
|
||||
data->fan[f_min][1] = rv;
|
||||
|
||||
prev_rv = rv = lm80_read_value(client, LM80_REG_TEMP);
|
||||
if (rv < 0)
|
||||
goto abort;
|
||||
rv = lm80_read_value(client, LM80_REG_RES);
|
||||
if (rv < 0)
|
||||
goto abort;
|
||||
data->temp[t_input] = (prev_rv << 8) | (rv & 0xf0);
|
||||
|
||||
for (i = t_input + 1; i < t_num_temp; i++) {
|
||||
rv = lm80_read_value(client, temp_regs[i]);
|
||||
if (rv < 0)
|
||||
goto abort;
|
||||
data->temp[i] = rv << 8;
|
||||
}
|
||||
|
||||
rv = lm80_read_value(client, LM80_REG_FANDIV);
|
||||
if (rv < 0)
|
||||
goto abort;
|
||||
data->fan_div[0] = (rv >> 2) & 0x03;
|
||||
data->fan_div[1] = (rv >> 4) & 0x03;
|
||||
|
||||
prev_rv = rv = lm80_read_value(client, LM80_REG_ALARM1);
|
||||
if (rv < 0)
|
||||
goto abort;
|
||||
rv = lm80_read_value(client, LM80_REG_ALARM2);
|
||||
if (rv < 0)
|
||||
goto abort;
|
||||
data->alarms = prev_rv + (rv << 8);
|
||||
|
||||
data->last_updated = jiffies;
|
||||
data->valid = 1;
|
||||
data->error = 0;
|
||||
}
|
||||
goto done;
|
||||
|
||||
abort:
|
||||
ret = ERR_PTR(rv);
|
||||
data->valid = 0;
|
||||
data->error = 1;
|
||||
|
||||
done:
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sysfs stuff
|
||||
*/
|
||||
|
||||
#define show_in(suffix, value) \
|
||||
static ssize_t show_in_##suffix(struct device *dev, \
|
||||
struct device_attribute *attr, char *buf) \
|
||||
{ \
|
||||
int nr = to_sensor_dev_attr(attr)->index; \
|
||||
struct lm80_data *data = lm80_update_device(dev); \
|
||||
if (IS_ERR(data)) \
|
||||
return PTR_ERR(data); \
|
||||
return sprintf(buf, "%d\n", IN_FROM_REG(data->value[nr])); \
|
||||
}
|
||||
show_in(min, in_min)
|
||||
show_in(max, in_max)
|
||||
show_in(input, in)
|
||||
static ssize_t show_in(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct lm80_data *data = lm80_update_device(dev);
|
||||
int index = to_sensor_dev_attr_2(attr)->index;
|
||||
int nr = to_sensor_dev_attr_2(attr)->nr;
|
||||
|
||||
#define set_in(suffix, value, reg) \
|
||||
static ssize_t set_in_##suffix(struct device *dev, \
|
||||
struct device_attribute *attr, const char *buf, size_t count) \
|
||||
{ \
|
||||
int nr = to_sensor_dev_attr(attr)->index; \
|
||||
struct lm80_data *data = dev_get_drvdata(dev); \
|
||||
struct i2c_client *client = data->client; \
|
||||
long val; \
|
||||
int err = kstrtol(buf, 10, &val); \
|
||||
if (err < 0) \
|
||||
return err; \
|
||||
\
|
||||
mutex_lock(&data->update_lock);\
|
||||
data->value[nr] = IN_TO_REG(val); \
|
||||
lm80_write_value(client, reg(nr), data->value[nr]); \
|
||||
mutex_unlock(&data->update_lock);\
|
||||
return count; \
|
||||
if (IS_ERR(data))
|
||||
return PTR_ERR(data);
|
||||
return sprintf(buf, "%d\n", IN_FROM_REG(data->in[nr][index]));
|
||||
}
|
||||
set_in(min, in_min, LM80_REG_IN_MIN)
|
||||
set_in(max, in_max, LM80_REG_IN_MAX)
|
||||
|
||||
#define show_fan(suffix, value) \
|
||||
static ssize_t show_fan_##suffix(struct device *dev, \
|
||||
struct device_attribute *attr, char *buf) \
|
||||
{ \
|
||||
int nr = to_sensor_dev_attr(attr)->index; \
|
||||
struct lm80_data *data = lm80_update_device(dev); \
|
||||
if (IS_ERR(data)) \
|
||||
return PTR_ERR(data); \
|
||||
return sprintf(buf, "%d\n", FAN_FROM_REG(data->value[nr], \
|
||||
DIV_FROM_REG(data->fan_div[nr]))); \
|
||||
static ssize_t set_in(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct lm80_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
int index = to_sensor_dev_attr_2(attr)->index;
|
||||
int nr = to_sensor_dev_attr_2(attr)->nr;
|
||||
long val;
|
||||
u8 reg;
|
||||
int err = kstrtol(buf, 10, &val);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
reg = nr == i_min ? LM80_REG_IN_MIN(index) : LM80_REG_IN_MAX(index);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->in[nr][index] = IN_TO_REG(val);
|
||||
lm80_write_value(client, reg, data->in[nr][index]);
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t show_fan(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int index = to_sensor_dev_attr_2(attr)->index;
|
||||
int nr = to_sensor_dev_attr_2(attr)->nr;
|
||||
struct lm80_data *data = lm80_update_device(dev);
|
||||
if (IS_ERR(data))
|
||||
return PTR_ERR(data);
|
||||
return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr][index],
|
||||
DIV_FROM_REG(data->fan_div[index])));
|
||||
}
|
||||
show_fan(min, fan_min)
|
||||
show_fan(input, fan)
|
||||
|
||||
static ssize_t show_fan_div(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
@ -232,7 +328,8 @@ static ssize_t show_fan_div(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)
|
||||
{
|
||||
int nr = to_sensor_dev_attr(attr)->index;
|
||||
int index = to_sensor_dev_attr_2(attr)->index;
|
||||
int nr = to_sensor_dev_attr_2(attr)->nr;
|
||||
struct lm80_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
unsigned long val;
|
||||
@ -241,8 +338,10 @@ static ssize_t set_fan_min(struct device *dev, struct device_attribute *attr,
|
||||
return err;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr]));
|
||||
lm80_write_value(client, LM80_REG_FAN_MIN(nr + 1), data->fan_min[nr]);
|
||||
data->fan[nr][index] = FAN_TO_REG(val,
|
||||
DIV_FROM_REG(data->fan_div[index]));
|
||||
lm80_write_value(client, LM80_REG_FAN_MIN(index + 1),
|
||||
data->fan[nr][index]);
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
@ -267,7 +366,7 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr,
|
||||
|
||||
/* Save fan_min */
|
||||
mutex_lock(&data->update_lock);
|
||||
min = FAN_FROM_REG(data->fan_min[nr],
|
||||
min = FAN_FROM_REG(data->fan[f_min][nr],
|
||||
DIV_FROM_REG(data->fan_div[nr]));
|
||||
|
||||
switch (val) {
|
||||
@ -291,62 +390,47 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
reg = (lm80_read_value(client, LM80_REG_FANDIV) & ~(3 << (2 * (nr + 1))))
|
||||
| (data->fan_div[nr] << (2 * (nr + 1)));
|
||||
reg = (lm80_read_value(client, LM80_REG_FANDIV) &
|
||||
~(3 << (2 * (nr + 1)))) | (data->fan_div[nr] << (2 * (nr + 1)));
|
||||
lm80_write_value(client, LM80_REG_FANDIV, reg);
|
||||
|
||||
/* Restore fan_min */
|
||||
data->fan_min[nr] = FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr]));
|
||||
lm80_write_value(client, LM80_REG_FAN_MIN(nr + 1), data->fan_min[nr]);
|
||||
data->fan[f_min][nr] = FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr]));
|
||||
lm80_write_value(client, LM80_REG_FAN_MIN(nr + 1),
|
||||
data->fan[f_min][nr]);
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t show_temp_input1(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
static ssize_t show_temp(struct device *dev, struct device_attribute *devattr,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct lm80_data *data = lm80_update_device(dev);
|
||||
if (IS_ERR(data))
|
||||
return PTR_ERR(data);
|
||||
return sprintf(buf, "%ld\n", TEMP_FROM_REG(data->temp));
|
||||
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[attr->index]));
|
||||
}
|
||||
|
||||
#define show_temp(suffix, value) \
|
||||
static ssize_t show_temp_##suffix(struct device *dev, \
|
||||
struct device_attribute *attr, char *buf) \
|
||||
{ \
|
||||
struct lm80_data *data = lm80_update_device(dev); \
|
||||
if (IS_ERR(data)) \
|
||||
return PTR_ERR(data); \
|
||||
return sprintf(buf, "%d\n", TEMP_LIMIT_FROM_REG(data->value)); \
|
||||
}
|
||||
show_temp(hot_max, temp_hot_max);
|
||||
show_temp(hot_hyst, temp_hot_hyst);
|
||||
show_temp(os_max, temp_os_max);
|
||||
show_temp(os_hyst, temp_os_hyst);
|
||||
static ssize_t set_temp(struct device *dev, struct device_attribute *devattr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct lm80_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
int nr = attr->index;
|
||||
long val;
|
||||
int err = kstrtol(buf, 10, &val);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
#define set_temp(suffix, value, reg) \
|
||||
static ssize_t set_temp_##suffix(struct device *dev, \
|
||||
struct device_attribute *attr, const char *buf, size_t count) \
|
||||
{ \
|
||||
struct lm80_data *data = dev_get_drvdata(dev); \
|
||||
struct i2c_client *client = data->client; \
|
||||
long val; \
|
||||
int err = kstrtol(buf, 10, &val); \
|
||||
if (err < 0) \
|
||||
return err; \
|
||||
\
|
||||
mutex_lock(&data->update_lock); \
|
||||
data->value = TEMP_LIMIT_TO_REG(val); \
|
||||
lm80_write_value(client, reg, data->value); \
|
||||
mutex_unlock(&data->update_lock); \
|
||||
return count; \
|
||||
mutex_lock(&data->update_lock);
|
||||
data->temp[nr] = TEMP_TO_REG(val);
|
||||
lm80_write_value(client, temp_regs[nr], data->temp[nr] >> 8);
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
set_temp(hot_max, temp_hot_max, LM80_REG_TEMP_HOT_MAX);
|
||||
set_temp(hot_hyst, temp_hot_hyst, LM80_REG_TEMP_HOT_HYST);
|
||||
set_temp(os_max, temp_os_max, LM80_REG_TEMP_OS_MAX);
|
||||
set_temp(os_hyst, temp_os_hyst, LM80_REG_TEMP_OS_HYST);
|
||||
|
||||
static ssize_t show_alarms(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
@ -367,60 +451,60 @@ static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,
|
||||
return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1);
|
||||
}
|
||||
|
||||
static SENSOR_DEVICE_ATTR(in0_min, S_IWUSR | S_IRUGO,
|
||||
show_in_min, set_in_min, 0);
|
||||
static SENSOR_DEVICE_ATTR(in1_min, S_IWUSR | S_IRUGO,
|
||||
show_in_min, set_in_min, 1);
|
||||
static SENSOR_DEVICE_ATTR(in2_min, S_IWUSR | S_IRUGO,
|
||||
show_in_min, set_in_min, 2);
|
||||
static SENSOR_DEVICE_ATTR(in3_min, S_IWUSR | S_IRUGO,
|
||||
show_in_min, set_in_min, 3);
|
||||
static SENSOR_DEVICE_ATTR(in4_min, S_IWUSR | S_IRUGO,
|
||||
show_in_min, set_in_min, 4);
|
||||
static SENSOR_DEVICE_ATTR(in5_min, S_IWUSR | S_IRUGO,
|
||||
show_in_min, set_in_min, 5);
|
||||
static SENSOR_DEVICE_ATTR(in6_min, S_IWUSR | S_IRUGO,
|
||||
show_in_min, set_in_min, 6);
|
||||
static SENSOR_DEVICE_ATTR(in0_max, S_IWUSR | S_IRUGO,
|
||||
show_in_max, set_in_max, 0);
|
||||
static SENSOR_DEVICE_ATTR(in1_max, S_IWUSR | S_IRUGO,
|
||||
show_in_max, set_in_max, 1);
|
||||
static SENSOR_DEVICE_ATTR(in2_max, S_IWUSR | S_IRUGO,
|
||||
show_in_max, set_in_max, 2);
|
||||
static SENSOR_DEVICE_ATTR(in3_max, S_IWUSR | S_IRUGO,
|
||||
show_in_max, set_in_max, 3);
|
||||
static SENSOR_DEVICE_ATTR(in4_max, S_IWUSR | S_IRUGO,
|
||||
show_in_max, set_in_max, 4);
|
||||
static SENSOR_DEVICE_ATTR(in5_max, S_IWUSR | S_IRUGO,
|
||||
show_in_max, set_in_max, 5);
|
||||
static SENSOR_DEVICE_ATTR(in6_max, S_IWUSR | S_IRUGO,
|
||||
show_in_max, set_in_max, 6);
|
||||
static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, show_in_input, NULL, 0);
|
||||
static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_in_input, NULL, 1);
|
||||
static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_in_input, NULL, 2);
|
||||
static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, show_in_input, NULL, 3);
|
||||
static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, show_in_input, NULL, 4);
|
||||
static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, show_in_input, NULL, 5);
|
||||
static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, show_in_input, NULL, 6);
|
||||
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_input, S_IRUGO, show_fan_input, NULL, 0);
|
||||
static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan_input, NULL, 1);
|
||||
static SENSOR_DEVICE_ATTR_2(in0_min, S_IWUSR | S_IRUGO,
|
||||
show_in, set_in, i_min, 0);
|
||||
static SENSOR_DEVICE_ATTR_2(in1_min, S_IWUSR | S_IRUGO,
|
||||
show_in, set_in, i_min, 1);
|
||||
static SENSOR_DEVICE_ATTR_2(in2_min, S_IWUSR | S_IRUGO,
|
||||
show_in, set_in, i_min, 2);
|
||||
static SENSOR_DEVICE_ATTR_2(in3_min, S_IWUSR | S_IRUGO,
|
||||
show_in, set_in, i_min, 3);
|
||||
static SENSOR_DEVICE_ATTR_2(in4_min, S_IWUSR | S_IRUGO,
|
||||
show_in, set_in, i_min, 4);
|
||||
static SENSOR_DEVICE_ATTR_2(in5_min, S_IWUSR | S_IRUGO,
|
||||
show_in, set_in, i_min, 5);
|
||||
static SENSOR_DEVICE_ATTR_2(in6_min, S_IWUSR | S_IRUGO,
|
||||
show_in, set_in, i_min, 6);
|
||||
static SENSOR_DEVICE_ATTR_2(in0_max, S_IWUSR | S_IRUGO,
|
||||
show_in, set_in, i_max, 0);
|
||||
static SENSOR_DEVICE_ATTR_2(in1_max, S_IWUSR | S_IRUGO,
|
||||
show_in, set_in, i_max, 1);
|
||||
static SENSOR_DEVICE_ATTR_2(in2_max, S_IWUSR | S_IRUGO,
|
||||
show_in, set_in, i_max, 2);
|
||||
static SENSOR_DEVICE_ATTR_2(in3_max, S_IWUSR | S_IRUGO,
|
||||
show_in, set_in, i_max, 3);
|
||||
static SENSOR_DEVICE_ATTR_2(in4_max, S_IWUSR | S_IRUGO,
|
||||
show_in, set_in, i_max, 4);
|
||||
static SENSOR_DEVICE_ATTR_2(in5_max, S_IWUSR | S_IRUGO,
|
||||
show_in, set_in, i_max, 5);
|
||||
static SENSOR_DEVICE_ATTR_2(in6_max, S_IWUSR | S_IRUGO,
|
||||
show_in, set_in, i_max, 6);
|
||||
static SENSOR_DEVICE_ATTR_2(in0_input, S_IRUGO, show_in, NULL, i_input, 0);
|
||||
static SENSOR_DEVICE_ATTR_2(in1_input, S_IRUGO, show_in, NULL, i_input, 1);
|
||||
static SENSOR_DEVICE_ATTR_2(in2_input, S_IRUGO, show_in, NULL, i_input, 2);
|
||||
static SENSOR_DEVICE_ATTR_2(in3_input, S_IRUGO, show_in, NULL, i_input, 3);
|
||||
static SENSOR_DEVICE_ATTR_2(in4_input, S_IRUGO, show_in, NULL, i_input, 4);
|
||||
static SENSOR_DEVICE_ATTR_2(in5_input, S_IRUGO, show_in, NULL, i_input, 5);
|
||||
static SENSOR_DEVICE_ATTR_2(in6_input, S_IRUGO, show_in, NULL, i_input, 6);
|
||||
static SENSOR_DEVICE_ATTR_2(fan1_min, S_IWUSR | S_IRUGO,
|
||||
show_fan, set_fan_min, f_min, 0);
|
||||
static SENSOR_DEVICE_ATTR_2(fan2_min, S_IWUSR | S_IRUGO,
|
||||
show_fan, set_fan_min, f_min, 1);
|
||||
static SENSOR_DEVICE_ATTR_2(fan1_input, S_IRUGO, show_fan, NULL, f_input, 0);
|
||||
static SENSOR_DEVICE_ATTR_2(fan2_input, S_IRUGO, show_fan, NULL, f_input, 1);
|
||||
static SENSOR_DEVICE_ATTR(fan1_div, S_IWUSR | S_IRUGO,
|
||||
show_fan_div, set_fan_div, 0);
|
||||
static SENSOR_DEVICE_ATTR(fan2_div, S_IWUSR | S_IRUGO,
|
||||
show_fan_div, set_fan_div, 1);
|
||||
static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input1, NULL);
|
||||
static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_hot_max,
|
||||
set_temp_hot_max);
|
||||
static DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, show_temp_hot_hyst,
|
||||
set_temp_hot_hyst);
|
||||
static DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp_os_max,
|
||||
set_temp_os_max);
|
||||
static DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temp_os_hyst,
|
||||
set_temp_os_hyst);
|
||||
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, t_input);
|
||||
static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp,
|
||||
set_temp, t_hot_max);
|
||||
static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, show_temp,
|
||||
set_temp, t_hot_hyst);
|
||||
static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp,
|
||||
set_temp, t_os_max);
|
||||
static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temp,
|
||||
set_temp, t_os_hyst);
|
||||
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
|
||||
static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0);
|
||||
static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 1);
|
||||
@ -466,11 +550,11 @@ static struct attribute *lm80_attrs[] = {
|
||||
&sensor_dev_attr_fan2_input.dev_attr.attr,
|
||||
&sensor_dev_attr_fan1_div.dev_attr.attr,
|
||||
&sensor_dev_attr_fan2_div.dev_attr.attr,
|
||||
&dev_attr_temp1_input.attr,
|
||||
&dev_attr_temp1_max.attr,
|
||||
&dev_attr_temp1_max_hyst.attr,
|
||||
&dev_attr_temp1_crit.attr,
|
||||
&dev_attr_temp1_crit_hyst.attr,
|
||||
&sensor_dev_attr_temp1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_crit.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
|
||||
&dev_attr_alarms.attr,
|
||||
&sensor_dev_attr_in0_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_in1_alarm.dev_attr.attr,
|
||||
@ -551,8 +635,8 @@ static int lm80_probe(struct i2c_client *client,
|
||||
lm80_init_client(client);
|
||||
|
||||
/* A few vars need to be filled upon startup */
|
||||
data->fan_min[0] = lm80_read_value(client, LM80_REG_FAN_MIN(1));
|
||||
data->fan_min[1] = lm80_read_value(client, LM80_REG_FAN_MIN(2));
|
||||
data->fan[f_min][0] = lm80_read_value(client, LM80_REG_FAN_MIN(1));
|
||||
data->fan[f_min][1] = lm80_read_value(client, LM80_REG_FAN_MIN(2));
|
||||
|
||||
hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
|
||||
data, lm80_groups);
|
||||
@ -560,143 +644,27 @@ static int lm80_probe(struct i2c_client *client,
|
||||
return PTR_ERR_OR_ZERO(hwmon_dev);
|
||||
}
|
||||
|
||||
static int lm80_read_value(struct i2c_client *client, u8 reg)
|
||||
{
|
||||
return i2c_smbus_read_byte_data(client, reg);
|
||||
}
|
||||
/*
|
||||
* Driver data (common to all clients)
|
||||
*/
|
||||
|
||||
static int lm80_write_value(struct i2c_client *client, u8 reg, u8 value)
|
||||
{
|
||||
return i2c_smbus_write_byte_data(client, reg, value);
|
||||
}
|
||||
static const struct i2c_device_id lm80_id[] = {
|
||||
{ "lm80", 0 },
|
||||
{ "lm96080", 1 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, lm80_id);
|
||||
|
||||
/* Called when we have found a new LM80. */
|
||||
static void lm80_init_client(struct i2c_client *client)
|
||||
{
|
||||
/*
|
||||
* Reset all except Watchdog values and last conversion values
|
||||
* This sets fan-divs to 2, among others. This makes most other
|
||||
* initializations unnecessary
|
||||
*/
|
||||
lm80_write_value(client, LM80_REG_CONFIG, 0x80);
|
||||
/* Set 11-bit temperature resolution */
|
||||
lm80_write_value(client, LM80_REG_RES, 0x08);
|
||||
|
||||
/* Start monitoring */
|
||||
lm80_write_value(client, LM80_REG_CONFIG, 0x01);
|
||||
}
|
||||
|
||||
static struct lm80_data *lm80_update_device(struct device *dev)
|
||||
{
|
||||
struct lm80_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
int i;
|
||||
int rv;
|
||||
int prev_rv;
|
||||
struct lm80_data *ret = data;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
if (data->error)
|
||||
lm80_init_client(client);
|
||||
|
||||
if (time_after(jiffies, data->last_updated + 2 * HZ) || !data->valid) {
|
||||
dev_dbg(dev, "Starting lm80 update\n");
|
||||
for (i = 0; i <= 6; i++) {
|
||||
rv = lm80_read_value(client, LM80_REG_IN(i));
|
||||
if (rv < 0)
|
||||
goto abort;
|
||||
data->in[i] = rv;
|
||||
|
||||
rv = lm80_read_value(client, LM80_REG_IN_MIN(i));
|
||||
if (rv < 0)
|
||||
goto abort;
|
||||
data->in_min[i] = rv;
|
||||
|
||||
rv = lm80_read_value(client, LM80_REG_IN_MAX(i));
|
||||
if (rv < 0)
|
||||
goto abort;
|
||||
data->in_max[i] = rv;
|
||||
}
|
||||
|
||||
rv = lm80_read_value(client, LM80_REG_FAN1);
|
||||
if (rv < 0)
|
||||
goto abort;
|
||||
data->fan[0] = rv;
|
||||
|
||||
rv = lm80_read_value(client, LM80_REG_FAN_MIN(1));
|
||||
if (rv < 0)
|
||||
goto abort;
|
||||
data->fan_min[0] = rv;
|
||||
|
||||
rv = lm80_read_value(client, LM80_REG_FAN2);
|
||||
if (rv < 0)
|
||||
goto abort;
|
||||
data->fan[1] = rv;
|
||||
|
||||
rv = lm80_read_value(client, LM80_REG_FAN_MIN(2));
|
||||
if (rv < 0)
|
||||
goto abort;
|
||||
data->fan_min[1] = rv;
|
||||
|
||||
prev_rv = rv = lm80_read_value(client, LM80_REG_TEMP);
|
||||
if (rv < 0)
|
||||
goto abort;
|
||||
rv = lm80_read_value(client, LM80_REG_RES);
|
||||
if (rv < 0)
|
||||
goto abort;
|
||||
data->temp = (prev_rv << 8) | (rv & 0xf0);
|
||||
|
||||
rv = lm80_read_value(client, LM80_REG_TEMP_OS_MAX);
|
||||
if (rv < 0)
|
||||
goto abort;
|
||||
data->temp_os_max = rv;
|
||||
|
||||
rv = lm80_read_value(client, LM80_REG_TEMP_OS_HYST);
|
||||
if (rv < 0)
|
||||
goto abort;
|
||||
data->temp_os_hyst = rv;
|
||||
|
||||
rv = lm80_read_value(client, LM80_REG_TEMP_HOT_MAX);
|
||||
if (rv < 0)
|
||||
goto abort;
|
||||
data->temp_hot_max = rv;
|
||||
|
||||
rv = lm80_read_value(client, LM80_REG_TEMP_HOT_HYST);
|
||||
if (rv < 0)
|
||||
goto abort;
|
||||
data->temp_hot_hyst = rv;
|
||||
|
||||
rv = lm80_read_value(client, LM80_REG_FANDIV);
|
||||
if (rv < 0)
|
||||
goto abort;
|
||||
data->fan_div[0] = (rv >> 2) & 0x03;
|
||||
data->fan_div[1] = (rv >> 4) & 0x03;
|
||||
|
||||
prev_rv = rv = lm80_read_value(client, LM80_REG_ALARM1);
|
||||
if (rv < 0)
|
||||
goto abort;
|
||||
rv = lm80_read_value(client, LM80_REG_ALARM2);
|
||||
if (rv < 0)
|
||||
goto abort;
|
||||
data->alarms = prev_rv + (rv << 8);
|
||||
|
||||
data->last_updated = jiffies;
|
||||
data->valid = 1;
|
||||
data->error = 0;
|
||||
}
|
||||
goto done;
|
||||
|
||||
abort:
|
||||
ret = ERR_PTR(rv);
|
||||
data->valid = 0;
|
||||
data->error = 1;
|
||||
|
||||
done:
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
static struct i2c_driver lm80_driver = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = "lm80",
|
||||
},
|
||||
.probe = lm80_probe,
|
||||
.id_table = lm80_id,
|
||||
.detect = lm80_detect,
|
||||
.address_list = normal_i2c,
|
||||
};
|
||||
|
||||
module_i2c_driver(lm80_driver);
|
||||
|
||||
|
@ -25,10 +25,6 @@
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
@ -110,46 +106,13 @@ static const u8 LM83_REG_W_HIGH[] = {
|
||||
LM83_REG_W_TCRIT,
|
||||
};
|
||||
|
||||
/*
|
||||
* Functions declaration
|
||||
*/
|
||||
|
||||
static int lm83_detect(struct i2c_client *new_client,
|
||||
struct i2c_board_info *info);
|
||||
static int lm83_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id);
|
||||
static int lm83_remove(struct i2c_client *client);
|
||||
static struct lm83_data *lm83_update_device(struct device *dev);
|
||||
|
||||
/*
|
||||
* Driver data (common to all clients)
|
||||
*/
|
||||
|
||||
static const struct i2c_device_id lm83_id[] = {
|
||||
{ "lm83", lm83 },
|
||||
{ "lm82", lm82 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, lm83_id);
|
||||
|
||||
static struct i2c_driver lm83_driver = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = "lm83",
|
||||
},
|
||||
.probe = lm83_probe,
|
||||
.remove = lm83_remove,
|
||||
.id_table = lm83_id,
|
||||
.detect = lm83_detect,
|
||||
.address_list = normal_i2c,
|
||||
};
|
||||
|
||||
/*
|
||||
* Client data (each client gets its own)
|
||||
*/
|
||||
|
||||
struct lm83_data {
|
||||
struct device *hwmon_dev;
|
||||
struct i2c_client *client;
|
||||
const struct attribute_group *groups[3];
|
||||
struct mutex update_lock;
|
||||
char valid; /* zero until following fields are valid */
|
||||
unsigned long last_updated; /* in jiffies */
|
||||
@ -161,6 +124,36 @@ struct lm83_data {
|
||||
u16 alarms; /* bitvector, combined */
|
||||
};
|
||||
|
||||
static struct lm83_data *lm83_update_device(struct device *dev)
|
||||
{
|
||||
struct lm83_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) {
|
||||
int nr;
|
||||
|
||||
dev_dbg(&client->dev, "Updating lm83 data.\n");
|
||||
for (nr = 0; nr < 9; nr++) {
|
||||
data->temp[nr] =
|
||||
i2c_smbus_read_byte_data(client,
|
||||
LM83_REG_R_TEMP[nr]);
|
||||
}
|
||||
data->alarms =
|
||||
i2c_smbus_read_byte_data(client, LM83_REG_R_STATUS1)
|
||||
+ (i2c_smbus_read_byte_data(client, LM83_REG_R_STATUS2)
|
||||
<< 8);
|
||||
|
||||
data->last_updated = jiffies;
|
||||
data->valid = 1;
|
||||
}
|
||||
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sysfs stuff
|
||||
*/
|
||||
@ -177,8 +170,8 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *devattr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct lm83_data *data = i2c_get_clientdata(client);
|
||||
struct lm83_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
long val;
|
||||
int nr = attr->index;
|
||||
int err;
|
||||
@ -340,15 +333,15 @@ static int lm83_detect(struct i2c_client *new_client,
|
||||
static int lm83_probe(struct i2c_client *new_client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct device *hwmon_dev;
|
||||
struct lm83_data *data;
|
||||
int err;
|
||||
|
||||
data = devm_kzalloc(&new_client->dev, sizeof(struct lm83_data),
|
||||
GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_set_clientdata(new_client, data);
|
||||
data->client = new_client;
|
||||
mutex_init(&data->update_lock);
|
||||
|
||||
/*
|
||||
@ -357,72 +350,37 @@ static int lm83_probe(struct i2c_client *new_client,
|
||||
* at the same register as the LM83 temp3 entry - so we
|
||||
* declare 1 and 3 common, and then 2 and 4 only for the LM83.
|
||||
*/
|
||||
data->groups[0] = &lm83_group;
|
||||
if (id->driver_data == lm83)
|
||||
data->groups[1] = &lm83_group_opt;
|
||||
|
||||
err = sysfs_create_group(&new_client->dev.kobj, &lm83_group);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (id->driver_data == lm83) {
|
||||
err = sysfs_create_group(&new_client->dev.kobj,
|
||||
&lm83_group_opt);
|
||||
if (err)
|
||||
goto exit_remove_files;
|
||||
}
|
||||
|
||||
data->hwmon_dev = hwmon_device_register(&new_client->dev);
|
||||
if (IS_ERR(data->hwmon_dev)) {
|
||||
err = PTR_ERR(data->hwmon_dev);
|
||||
goto exit_remove_files;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
exit_remove_files:
|
||||
sysfs_remove_group(&new_client->dev.kobj, &lm83_group);
|
||||
sysfs_remove_group(&new_client->dev.kobj, &lm83_group_opt);
|
||||
return err;
|
||||
hwmon_dev = devm_hwmon_device_register_with_groups(&new_client->dev,
|
||||
new_client->name,
|
||||
data, data->groups);
|
||||
return PTR_ERR_OR_ZERO(hwmon_dev);
|
||||
}
|
||||
|
||||
static int lm83_remove(struct i2c_client *client)
|
||||
{
|
||||
struct lm83_data *data = i2c_get_clientdata(client);
|
||||
/*
|
||||
* Driver data (common to all clients)
|
||||
*/
|
||||
|
||||
hwmon_device_unregister(data->hwmon_dev);
|
||||
sysfs_remove_group(&client->dev.kobj, &lm83_group);
|
||||
sysfs_remove_group(&client->dev.kobj, &lm83_group_opt);
|
||||
static const struct i2c_device_id lm83_id[] = {
|
||||
{ "lm83", lm83 },
|
||||
{ "lm82", lm82 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, lm83_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct lm83_data *lm83_update_device(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct lm83_data *data = i2c_get_clientdata(client);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) {
|
||||
int nr;
|
||||
|
||||
dev_dbg(&client->dev, "Updating lm83 data.\n");
|
||||
for (nr = 0; nr < 9; nr++) {
|
||||
data->temp[nr] =
|
||||
i2c_smbus_read_byte_data(client,
|
||||
LM83_REG_R_TEMP[nr]);
|
||||
}
|
||||
data->alarms =
|
||||
i2c_smbus_read_byte_data(client, LM83_REG_R_STATUS1)
|
||||
+ (i2c_smbus_read_byte_data(client, LM83_REG_R_STATUS2)
|
||||
<< 8);
|
||||
|
||||
data->last_updated = jiffies;
|
||||
data->valid = 1;
|
||||
}
|
||||
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return data;
|
||||
}
|
||||
static struct i2c_driver lm83_driver = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = "lm83",
|
||||
},
|
||||
.probe = lm83_probe,
|
||||
.id_table = lm83_id,
|
||||
.detect = lm83_detect,
|
||||
.address_list = normal_i2c,
|
||||
};
|
||||
|
||||
module_i2c_driver(lm83_driver);
|
||||
|
||||
|
@ -34,10 +34,6 @@
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
@ -93,46 +89,53 @@ static inline u8 ALARMS_FROM_REG(s16 reg)
|
||||
return reg & 0x0007;
|
||||
}
|
||||
|
||||
/* Driver data (common to all clients) */
|
||||
static struct i2c_driver lm92_driver;
|
||||
enum temp_index {
|
||||
t_input,
|
||||
t_crit,
|
||||
t_min,
|
||||
t_max,
|
||||
t_hyst,
|
||||
t_num_regs
|
||||
};
|
||||
|
||||
static const u8 regs[t_num_regs] = {
|
||||
[t_input] = LM92_REG_TEMP,
|
||||
[t_crit] = LM92_REG_TEMP_CRIT,
|
||||
[t_min] = LM92_REG_TEMP_LOW,
|
||||
[t_max] = LM92_REG_TEMP_HIGH,
|
||||
[t_hyst] = LM92_REG_TEMP_HYST,
|
||||
};
|
||||
|
||||
/* Client data (each client gets its own) */
|
||||
struct lm92_data {
|
||||
struct device *hwmon_dev;
|
||||
struct i2c_client *client;
|
||||
struct mutex update_lock;
|
||||
char valid; /* zero until following fields are valid */
|
||||
unsigned long last_updated; /* in jiffies */
|
||||
|
||||
/* registers values */
|
||||
s16 temp1_input, temp1_crit, temp1_min, temp1_max, temp1_hyst;
|
||||
s16 temp[t_num_regs]; /* index with enum temp_index */
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Sysfs attributes and callback functions
|
||||
*/
|
||||
|
||||
static struct lm92_data *lm92_update_device(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct lm92_data *data = i2c_get_clientdata(client);
|
||||
struct lm92_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
int i;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
if (time_after(jiffies, data->last_updated + HZ)
|
||||
|| !data->valid) {
|
||||
dev_dbg(&client->dev, "Updating lm92 data\n");
|
||||
data->temp1_input = i2c_smbus_read_word_swapped(client,
|
||||
LM92_REG_TEMP);
|
||||
data->temp1_hyst = i2c_smbus_read_word_swapped(client,
|
||||
LM92_REG_TEMP_HYST);
|
||||
data->temp1_crit = i2c_smbus_read_word_swapped(client,
|
||||
LM92_REG_TEMP_CRIT);
|
||||
data->temp1_min = i2c_smbus_read_word_swapped(client,
|
||||
LM92_REG_TEMP_LOW);
|
||||
data->temp1_max = i2c_smbus_read_word_swapped(client,
|
||||
LM92_REG_TEMP_HIGH);
|
||||
|
||||
for (i = 0; i < t_num_regs; i++) {
|
||||
data->temp[i] =
|
||||
i2c_smbus_read_word_swapped(client, regs[i]);
|
||||
}
|
||||
data->last_updated = jiffies;
|
||||
data->valid = 1;
|
||||
}
|
||||
@ -142,68 +145,60 @@ static struct lm92_data *lm92_update_device(struct device *dev)
|
||||
return data;
|
||||
}
|
||||
|
||||
#define show_temp(value) \
|
||||
static ssize_t show_##value(struct device *dev, struct device_attribute *attr, \
|
||||
char *buf) \
|
||||
{ \
|
||||
struct lm92_data *data = lm92_update_device(dev); \
|
||||
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->value)); \
|
||||
}
|
||||
show_temp(temp1_input);
|
||||
show_temp(temp1_crit);
|
||||
show_temp(temp1_min);
|
||||
show_temp(temp1_max);
|
||||
static ssize_t show_temp(struct device *dev, struct device_attribute *devattr,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct lm92_data *data = lm92_update_device(dev);
|
||||
|
||||
#define set_temp(value, reg) \
|
||||
static ssize_t set_##value(struct device *dev, struct device_attribute *attr, \
|
||||
const char *buf, \
|
||||
size_t count) \
|
||||
{ \
|
||||
struct i2c_client *client = to_i2c_client(dev); \
|
||||
struct lm92_data *data = i2c_get_clientdata(client); \
|
||||
long val; \
|
||||
int err = kstrtol(buf, 10, &val); \
|
||||
if (err) \
|
||||
return err; \
|
||||
\
|
||||
mutex_lock(&data->update_lock); \
|
||||
data->value = TEMP_TO_REG(val); \
|
||||
i2c_smbus_write_word_swapped(client, reg, data->value); \
|
||||
mutex_unlock(&data->update_lock); \
|
||||
return count; \
|
||||
}
|
||||
set_temp(temp1_crit, LM92_REG_TEMP_CRIT);
|
||||
set_temp(temp1_min, LM92_REG_TEMP_LOW);
|
||||
set_temp(temp1_max, LM92_REG_TEMP_HIGH);
|
||||
|
||||
static ssize_t show_temp1_crit_hyst(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct lm92_data *data = lm92_update_device(dev);
|
||||
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp1_crit)
|
||||
- TEMP_FROM_REG(data->temp1_hyst));
|
||||
}
|
||||
static ssize_t show_temp1_max_hyst(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct lm92_data *data = lm92_update_device(dev);
|
||||
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp1_max)
|
||||
- TEMP_FROM_REG(data->temp1_hyst));
|
||||
}
|
||||
static ssize_t show_temp1_min_hyst(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct lm92_data *data = lm92_update_device(dev);
|
||||
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp1_min)
|
||||
+ TEMP_FROM_REG(data->temp1_hyst));
|
||||
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[attr->index]));
|
||||
}
|
||||
|
||||
static ssize_t set_temp1_crit_hyst(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
static ssize_t set_temp(struct device *dev, struct device_attribute *devattr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct lm92_data *data = i2c_get_clientdata(client);
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct lm92_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
int nr = attr->index;
|
||||
long val;
|
||||
int err;
|
||||
|
||||
err = kstrtol(buf, 10, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->temp[nr] = TEMP_TO_REG(val);
|
||||
i2c_smbus_write_word_swapped(client, regs[nr], data->temp[nr]);
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t show_temp_hyst(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct lm92_data *data = lm92_update_device(dev);
|
||||
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[attr->index])
|
||||
- TEMP_FROM_REG(data->temp[t_hyst]));
|
||||
}
|
||||
|
||||
static ssize_t show_temp_min_hyst(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct lm92_data *data = lm92_update_device(dev);
|
||||
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[t_min])
|
||||
+ TEMP_FROM_REG(data->temp[t_hyst]));
|
||||
}
|
||||
|
||||
static ssize_t set_temp_hyst(struct device *dev,
|
||||
struct device_attribute *devattr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct lm92_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
long val;
|
||||
int err;
|
||||
|
||||
@ -212,9 +207,9 @@ static ssize_t set_temp1_crit_hyst(struct device *dev,
|
||||
return err;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->temp1_hyst = TEMP_FROM_REG(data->temp1_crit) - val;
|
||||
data->temp[t_hyst] = TEMP_FROM_REG(data->temp[attr->index]) - val;
|
||||
i2c_smbus_write_word_swapped(client, LM92_REG_TEMP_HYST,
|
||||
TEMP_TO_REG(data->temp1_hyst));
|
||||
TEMP_TO_REG(data->temp[t_hyst]));
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
@ -223,7 +218,7 @@ static ssize_t show_alarms(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct lm92_data *data = lm92_update_device(dev);
|
||||
return sprintf(buf, "%d\n", ALARMS_FROM_REG(data->temp1_input));
|
||||
return sprintf(buf, "%d\n", ALARMS_FROM_REG(data->temp[t_input]));
|
||||
}
|
||||
|
||||
static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,
|
||||
@ -231,26 +226,25 @@ static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,
|
||||
{
|
||||
int bitnr = to_sensor_dev_attr(attr)->index;
|
||||
struct lm92_data *data = lm92_update_device(dev);
|
||||
return sprintf(buf, "%d\n", (data->temp1_input >> bitnr) & 1);
|
||||
return sprintf(buf, "%d\n", (data->temp[t_input] >> bitnr) & 1);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp1_input, NULL);
|
||||
static DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp1_crit,
|
||||
set_temp1_crit);
|
||||
static DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temp1_crit_hyst,
|
||||
set_temp1_crit_hyst);
|
||||
static DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp1_min,
|
||||
set_temp1_min);
|
||||
static DEVICE_ATTR(temp1_min_hyst, S_IRUGO, show_temp1_min_hyst, NULL);
|
||||
static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp1_max,
|
||||
set_temp1_max);
|
||||
static DEVICE_ATTR(temp1_max_hyst, S_IRUGO, show_temp1_max_hyst, NULL);
|
||||
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, t_input);
|
||||
static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp, set_temp,
|
||||
t_crit);
|
||||
static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temp_hyst,
|
||||
set_temp_hyst, t_crit);
|
||||
static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp, set_temp,
|
||||
t_min);
|
||||
static DEVICE_ATTR(temp1_min_hyst, S_IRUGO, show_temp_min_hyst, NULL);
|
||||
static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp, set_temp,
|
||||
t_max);
|
||||
static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IRUGO, show_temp_hyst, NULL, t_max);
|
||||
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
|
||||
static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, 2);
|
||||
static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_alarm, NULL, 0);
|
||||
static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 1);
|
||||
|
||||
|
||||
/*
|
||||
* Detection and registration
|
||||
*/
|
||||
@ -322,24 +316,21 @@ static int max6635_check(struct i2c_client *client)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct attribute *lm92_attributes[] = {
|
||||
&dev_attr_temp1_input.attr,
|
||||
&dev_attr_temp1_crit.attr,
|
||||
&dev_attr_temp1_crit_hyst.attr,
|
||||
&dev_attr_temp1_min.attr,
|
||||
static struct attribute *lm92_attrs[] = {
|
||||
&sensor_dev_attr_temp1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_crit.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_min.dev_attr.attr,
|
||||
&dev_attr_temp1_min_hyst.attr,
|
||||
&dev_attr_temp1_max.attr,
|
||||
&dev_attr_temp1_max_hyst.attr,
|
||||
&sensor_dev_attr_temp1_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
|
||||
&dev_attr_alarms.attr,
|
||||
&sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group lm92_group = {
|
||||
.attrs = lm92_attributes,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(lm92);
|
||||
|
||||
/* Return 0 if detection is successful, -ENODEV otherwise */
|
||||
static int lm92_detect(struct i2c_client *new_client,
|
||||
@ -371,46 +362,24 @@ static int lm92_detect(struct i2c_client *new_client,
|
||||
static int lm92_probe(struct i2c_client *new_client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct device *hwmon_dev;
|
||||
struct lm92_data *data;
|
||||
int err;
|
||||
|
||||
data = devm_kzalloc(&new_client->dev, sizeof(struct lm92_data),
|
||||
GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_set_clientdata(new_client, data);
|
||||
data->client = new_client;
|
||||
mutex_init(&data->update_lock);
|
||||
|
||||
/* Initialize the chipset */
|
||||
lm92_init_client(new_client);
|
||||
|
||||
/* Register sysfs hooks */
|
||||
err = sysfs_create_group(&new_client->dev.kobj, &lm92_group);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
data->hwmon_dev = hwmon_device_register(&new_client->dev);
|
||||
if (IS_ERR(data->hwmon_dev)) {
|
||||
err = PTR_ERR(data->hwmon_dev);
|
||||
goto exit_remove;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
exit_remove:
|
||||
sysfs_remove_group(&new_client->dev.kobj, &lm92_group);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int lm92_remove(struct i2c_client *client)
|
||||
{
|
||||
struct lm92_data *data = i2c_get_clientdata(client);
|
||||
|
||||
hwmon_device_unregister(data->hwmon_dev);
|
||||
sysfs_remove_group(&client->dev.kobj, &lm92_group);
|
||||
|
||||
return 0;
|
||||
hwmon_dev = devm_hwmon_device_register_with_groups(&new_client->dev,
|
||||
new_client->name,
|
||||
data, lm92_groups);
|
||||
return PTR_ERR_OR_ZERO(hwmon_dev);
|
||||
}
|
||||
|
||||
|
||||
@ -431,7 +400,6 @@ static struct i2c_driver lm92_driver = {
|
||||
.name = "lm92",
|
||||
},
|
||||
.probe = lm92_probe,
|
||||
.remove = lm92_remove,
|
||||
.id_table = lm92_id,
|
||||
.detect = lm92_detect,
|
||||
.address_list = normal_i2c,
|
||||
|
@ -2747,10 +2747,8 @@ static int lm93_probe(struct i2c_client *client,
|
||||
}
|
||||
|
||||
data = devm_kzalloc(&client->dev, sizeof(struct lm93_data), GFP_KERNEL);
|
||||
if (!data) {
|
||||
dev_dbg(&client->dev, "out of memory!\n");
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
}
|
||||
i2c_set_clientdata(client, data);
|
||||
|
||||
/* housekeeping */
|
||||
|
@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
* Driver for Linear Technology LTC2945 I2C Power Monitor
|
||||
*
|
||||
* Copyright (c) 2014 Guenter Roeck
|
||||
|
@ -192,10 +192,8 @@ static int max1111_probe(struct spi_device *spi)
|
||||
return err;
|
||||
|
||||
data = devm_kzalloc(&spi->dev, sizeof(struct max1111_data), GFP_KERNEL);
|
||||
if (data == NULL) {
|
||||
dev_err(&spi->dev, "failed to allocate memory\n");
|
||||
if (data == NULL)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
switch (chip) {
|
||||
case max1110:
|
||||
|
@ -19,13 +19,8 @@
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
@ -76,38 +71,14 @@ static int temp_to_reg(int val)
|
||||
return (val < 0 ? val+0x100*1000 : val) / 1000;
|
||||
}
|
||||
|
||||
/*
|
||||
* Functions declaration
|
||||
*/
|
||||
|
||||
static int max1619_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id);
|
||||
static int max1619_detect(struct i2c_client *client,
|
||||
struct i2c_board_info *info);
|
||||
static void max1619_init_client(struct i2c_client *client);
|
||||
static int max1619_remove(struct i2c_client *client);
|
||||
static struct max1619_data *max1619_update_device(struct device *dev);
|
||||
|
||||
/*
|
||||
* Driver data (common to all clients)
|
||||
*/
|
||||
|
||||
static const struct i2c_device_id max1619_id[] = {
|
||||
{ "max1619", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, max1619_id);
|
||||
|
||||
static struct i2c_driver max1619_driver = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = "max1619",
|
||||
},
|
||||
.probe = max1619_probe,
|
||||
.remove = max1619_remove,
|
||||
.id_table = max1619_id,
|
||||
.detect = max1619_detect,
|
||||
.address_list = normal_i2c,
|
||||
enum temp_index {
|
||||
t_input1 = 0,
|
||||
t_input2,
|
||||
t_low2,
|
||||
t_high2,
|
||||
t_crit2,
|
||||
t_hyst2,
|
||||
t_num_regs
|
||||
};
|
||||
|
||||
/*
|
||||
@ -115,60 +86,92 @@ static struct i2c_driver max1619_driver = {
|
||||
*/
|
||||
|
||||
struct max1619_data {
|
||||
struct device *hwmon_dev;
|
||||
struct i2c_client *client;
|
||||
struct mutex update_lock;
|
||||
char valid; /* zero until following fields are valid */
|
||||
unsigned long last_updated; /* in jiffies */
|
||||
|
||||
/* registers values */
|
||||
u8 temp_input1; /* local */
|
||||
u8 temp_input2, temp_low2, temp_high2; /* remote */
|
||||
u8 temp_crit2;
|
||||
u8 temp_hyst2;
|
||||
u8 temp[t_num_regs]; /* index with enum temp_index */
|
||||
u8 alarms;
|
||||
};
|
||||
|
||||
static const u8 regs_read[t_num_regs] = {
|
||||
[t_input1] = MAX1619_REG_R_LOCAL_TEMP,
|
||||
[t_input2] = MAX1619_REG_R_REMOTE_TEMP,
|
||||
[t_low2] = MAX1619_REG_R_REMOTE_LOW,
|
||||
[t_high2] = MAX1619_REG_R_REMOTE_HIGH,
|
||||
[t_crit2] = MAX1619_REG_R_REMOTE_CRIT,
|
||||
[t_hyst2] = MAX1619_REG_R_TCRIT_HYST,
|
||||
};
|
||||
|
||||
static const u8 regs_write[t_num_regs] = {
|
||||
[t_low2] = MAX1619_REG_W_REMOTE_LOW,
|
||||
[t_high2] = MAX1619_REG_W_REMOTE_HIGH,
|
||||
[t_crit2] = MAX1619_REG_W_REMOTE_CRIT,
|
||||
[t_hyst2] = MAX1619_REG_W_TCRIT_HYST,
|
||||
};
|
||||
|
||||
static struct max1619_data *max1619_update_device(struct device *dev)
|
||||
{
|
||||
struct max1619_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
int config, i;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) {
|
||||
dev_dbg(&client->dev, "Updating max1619 data.\n");
|
||||
for (i = 0; i < t_num_regs; i++)
|
||||
data->temp[i] = i2c_smbus_read_byte_data(client,
|
||||
regs_read[i]);
|
||||
data->alarms = i2c_smbus_read_byte_data(client,
|
||||
MAX1619_REG_R_STATUS);
|
||||
/* If OVERT polarity is low, reverse alarm bit */
|
||||
config = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CONFIG);
|
||||
if (!(config & 0x20))
|
||||
data->alarms ^= 0x02;
|
||||
|
||||
data->last_updated = jiffies;
|
||||
data->valid = 1;
|
||||
}
|
||||
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sysfs stuff
|
||||
*/
|
||||
|
||||
#define show_temp(value) \
|
||||
static ssize_t show_##value(struct device *dev, struct device_attribute *attr, \
|
||||
char *buf) \
|
||||
{ \
|
||||
struct max1619_data *data = max1619_update_device(dev); \
|
||||
return sprintf(buf, "%d\n", temp_from_reg(data->value)); \
|
||||
}
|
||||
show_temp(temp_input1);
|
||||
show_temp(temp_input2);
|
||||
show_temp(temp_low2);
|
||||
show_temp(temp_high2);
|
||||
show_temp(temp_crit2);
|
||||
show_temp(temp_hyst2);
|
||||
static ssize_t show_temp(struct device *dev, struct device_attribute *devattr,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct max1619_data *data = max1619_update_device(dev);
|
||||
|
||||
#define set_temp2(value, reg) \
|
||||
static ssize_t set_##value(struct device *dev, struct device_attribute *attr, \
|
||||
const char *buf, \
|
||||
size_t count) \
|
||||
{ \
|
||||
struct i2c_client *client = to_i2c_client(dev); \
|
||||
struct max1619_data *data = i2c_get_clientdata(client); \
|
||||
long val; \
|
||||
int err = kstrtol(buf, 10, &val); \
|
||||
if (err) \
|
||||
return err; \
|
||||
\
|
||||
mutex_lock(&data->update_lock); \
|
||||
data->value = temp_to_reg(val); \
|
||||
i2c_smbus_write_byte_data(client, reg, data->value); \
|
||||
mutex_unlock(&data->update_lock); \
|
||||
return count; \
|
||||
return sprintf(buf, "%d\n", temp_from_reg(data->temp[attr->index]));
|
||||
}
|
||||
|
||||
set_temp2(temp_low2, MAX1619_REG_W_REMOTE_LOW);
|
||||
set_temp2(temp_high2, MAX1619_REG_W_REMOTE_HIGH);
|
||||
set_temp2(temp_crit2, MAX1619_REG_W_REMOTE_CRIT);
|
||||
set_temp2(temp_hyst2, MAX1619_REG_W_TCRIT_HYST);
|
||||
static ssize_t set_temp(struct device *dev, struct device_attribute *devattr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct max1619_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
long val;
|
||||
int err = kstrtol(buf, 10, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->temp[attr->index] = temp_to_reg(val);
|
||||
i2c_smbus_write_byte_data(client, regs_write[attr->index],
|
||||
data->temp[attr->index]);
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t show_alarms(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
@ -185,29 +188,30 @@ static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,
|
||||
return sprintf(buf, "%d\n", (data->alarms >> bitnr) & 1);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input1, NULL);
|
||||
static DEVICE_ATTR(temp2_input, S_IRUGO, show_temp_input2, NULL);
|
||||
static DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_temp_low2,
|
||||
set_temp_low2);
|
||||
static DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp_high2,
|
||||
set_temp_high2);
|
||||
static DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_temp_crit2,
|
||||
set_temp_crit2);
|
||||
static DEVICE_ATTR(temp2_crit_hyst, S_IWUSR | S_IRUGO, show_temp_hyst2,
|
||||
set_temp_hyst2);
|
||||
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, t_input1);
|
||||
static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, t_input2);
|
||||
static SENSOR_DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_temp, set_temp,
|
||||
t_low2);
|
||||
static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp, set_temp,
|
||||
t_high2);
|
||||
static SENSOR_DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_temp, set_temp,
|
||||
t_crit2);
|
||||
static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IWUSR | S_IRUGO, show_temp,
|
||||
set_temp, t_hyst2);
|
||||
|
||||
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
|
||||
static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, show_alarm, NULL, 1);
|
||||
static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_alarm, NULL, 2);
|
||||
static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO, show_alarm, NULL, 3);
|
||||
static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_alarm, NULL, 4);
|
||||
|
||||
static struct attribute *max1619_attributes[] = {
|
||||
&dev_attr_temp1_input.attr,
|
||||
&dev_attr_temp2_input.attr,
|
||||
&dev_attr_temp2_min.attr,
|
||||
&dev_attr_temp2_max.attr,
|
||||
&dev_attr_temp2_crit.attr,
|
||||
&dev_attr_temp2_crit_hyst.attr,
|
||||
static struct attribute *max1619_attrs[] = {
|
||||
&sensor_dev_attr_temp1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_min.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_crit.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_crit_hyst.dev_attr.attr,
|
||||
|
||||
&dev_attr_alarms.attr,
|
||||
&sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
|
||||
@ -216,14 +220,7 @@ static struct attribute *max1619_attributes[] = {
|
||||
&sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group max1619_group = {
|
||||
.attrs = max1619_attributes,
|
||||
};
|
||||
|
||||
/*
|
||||
* Real code
|
||||
*/
|
||||
ATTRIBUTE_GROUPS(max1619);
|
||||
|
||||
/* Return 0 if detection is successful, -ENODEV otherwise */
|
||||
static int max1619_detect(struct i2c_client *client,
|
||||
@ -261,41 +258,6 @@ static int max1619_detect(struct i2c_client *client,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max1619_probe(struct i2c_client *new_client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct max1619_data *data;
|
||||
int err;
|
||||
|
||||
data = devm_kzalloc(&new_client->dev, sizeof(struct max1619_data),
|
||||
GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_set_clientdata(new_client, data);
|
||||
mutex_init(&data->update_lock);
|
||||
|
||||
/* Initialize the MAX1619 chip */
|
||||
max1619_init_client(new_client);
|
||||
|
||||
/* Register sysfs hooks */
|
||||
err = sysfs_create_group(&new_client->dev.kobj, &max1619_group);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
data->hwmon_dev = hwmon_device_register(&new_client->dev);
|
||||
if (IS_ERR(data->hwmon_dev)) {
|
||||
err = PTR_ERR(data->hwmon_dev);
|
||||
goto exit_remove_files;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
exit_remove_files:
|
||||
sysfs_remove_group(&new_client->dev.kobj, &max1619_group);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void max1619_init_client(struct i2c_client *client)
|
||||
{
|
||||
u8 config;
|
||||
@ -311,48 +273,46 @@ static void max1619_init_client(struct i2c_client *client)
|
||||
config & 0xBF); /* run */
|
||||
}
|
||||
|
||||
static int max1619_remove(struct i2c_client *client)
|
||||
static int max1619_probe(struct i2c_client *new_client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct max1619_data *data = i2c_get_clientdata(client);
|
||||
struct max1619_data *data;
|
||||
struct device *hwmon_dev;
|
||||
|
||||
hwmon_device_unregister(data->hwmon_dev);
|
||||
sysfs_remove_group(&client->dev.kobj, &max1619_group);
|
||||
data = devm_kzalloc(&new_client->dev, sizeof(struct max1619_data),
|
||||
GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
data->client = new_client;
|
||||
mutex_init(&data->update_lock);
|
||||
|
||||
/* Initialize the MAX1619 chip */
|
||||
max1619_init_client(new_client);
|
||||
|
||||
hwmon_dev = devm_hwmon_device_register_with_groups(&new_client->dev,
|
||||
new_client->name,
|
||||
data,
|
||||
max1619_groups);
|
||||
return PTR_ERR_OR_ZERO(hwmon_dev);
|
||||
}
|
||||
|
||||
static struct max1619_data *max1619_update_device(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct max1619_data *data = i2c_get_clientdata(client);
|
||||
static const struct i2c_device_id max1619_id[] = {
|
||||
{ "max1619", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, max1619_id);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) {
|
||||
dev_dbg(&client->dev, "Updating max1619 data.\n");
|
||||
data->temp_input1 = i2c_smbus_read_byte_data(client,
|
||||
MAX1619_REG_R_LOCAL_TEMP);
|
||||
data->temp_input2 = i2c_smbus_read_byte_data(client,
|
||||
MAX1619_REG_R_REMOTE_TEMP);
|
||||
data->temp_high2 = i2c_smbus_read_byte_data(client,
|
||||
MAX1619_REG_R_REMOTE_HIGH);
|
||||
data->temp_low2 = i2c_smbus_read_byte_data(client,
|
||||
MAX1619_REG_R_REMOTE_LOW);
|
||||
data->temp_crit2 = i2c_smbus_read_byte_data(client,
|
||||
MAX1619_REG_R_REMOTE_CRIT);
|
||||
data->temp_hyst2 = i2c_smbus_read_byte_data(client,
|
||||
MAX1619_REG_R_TCRIT_HYST);
|
||||
data->alarms = i2c_smbus_read_byte_data(client,
|
||||
MAX1619_REG_R_STATUS);
|
||||
|
||||
data->last_updated = jiffies;
|
||||
data->valid = 1;
|
||||
}
|
||||
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return data;
|
||||
}
|
||||
static struct i2c_driver max1619_driver = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = "max1619",
|
||||
},
|
||||
.probe = max1619_probe,
|
||||
.id_table = max1619_id,
|
||||
.detect = max1619_detect,
|
||||
.address_list = normal_i2c,
|
||||
};
|
||||
|
||||
module_i2c_driver(max1619_driver);
|
||||
|
||||
|
@ -275,10 +275,8 @@ static int max197_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
data = devm_kzalloc(&pdev->dev, sizeof(struct max197_data), GFP_KERNEL);
|
||||
if (!data) {
|
||||
dev_err(&pdev->dev, "devm_kzalloc failed\n");
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
data->pdata = pdata;
|
||||
mutex_init(&data->lock);
|
||||
|
1457
drivers/hwmon/nct6683.c
Normal file
1457
drivers/hwmon/nct6683.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -4160,7 +4160,7 @@ static int __init sensors_nct6775_init(void)
|
||||
pdev[i] = platform_device_alloc(DRVNAME, address);
|
||||
if (!pdev[i]) {
|
||||
err = -ENOMEM;
|
||||
goto exit_device_put;
|
||||
goto exit_device_unregister;
|
||||
}
|
||||
|
||||
err = platform_device_add_data(pdev[i], &sio_data,
|
||||
@ -4198,9 +4198,11 @@ static int __init sensors_nct6775_init(void)
|
||||
return 0;
|
||||
|
||||
exit_device_put:
|
||||
for (i = 0; i < ARRAY_SIZE(pdev); i++) {
|
||||
platform_device_put(pdev[i]);
|
||||
exit_device_unregister:
|
||||
while (--i >= 0) {
|
||||
if (pdev[i])
|
||||
platform_device_put(pdev[i]);
|
||||
platform_device_unregister(pdev[i]);
|
||||
}
|
||||
exit_unregister:
|
||||
platform_driver_unregister(&nct6775_driver);
|
||||
|
@ -1081,10 +1081,8 @@ static int pc87427_probe(struct platform_device *pdev)
|
||||
|
||||
data = devm_kzalloc(&pdev->dev, sizeof(struct pc87427_data),
|
||||
GFP_KERNEL);
|
||||
if (!data) {
|
||||
pr_err("Out of memory\n");
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
data->address[0] = sio_data->address[0];
|
||||
data->address[1] = sio_data->address[1];
|
||||
|
@ -285,10 +285,8 @@ static int s3c_hwmon_probe(struct platform_device *dev)
|
||||
}
|
||||
|
||||
hwmon = devm_kzalloc(&dev->dev, sizeof(struct s3c_hwmon), GFP_KERNEL);
|
||||
if (hwmon == NULL) {
|
||||
dev_err(&dev->dev, "no memory\n");
|
||||
if (hwmon == NULL)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
platform_set_drvdata(dev, hwmon);
|
||||
|
||||
|
@ -51,6 +51,7 @@
|
||||
#define TMP102_THIGH_REG 0x03
|
||||
|
||||
struct tmp102 {
|
||||
struct i2c_client *client;
|
||||
struct device *hwmon_dev;
|
||||
struct thermal_zone_device *tz;
|
||||
struct mutex lock;
|
||||
@ -77,9 +78,10 @@ static const u8 tmp102_reg[] = {
|
||||
TMP102_THIGH_REG,
|
||||
};
|
||||
|
||||
static struct tmp102 *tmp102_update_device(struct i2c_client *client)
|
||||
static struct tmp102 *tmp102_update_device(struct device *dev)
|
||||
{
|
||||
struct tmp102 *tmp102 = i2c_get_clientdata(client);
|
||||
struct tmp102 *tmp102 = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = tmp102->client;
|
||||
|
||||
mutex_lock(&tmp102->lock);
|
||||
if (time_after(jiffies, tmp102->last_update + HZ / 3)) {
|
||||
@ -98,7 +100,7 @@ static struct tmp102 *tmp102_update_device(struct i2c_client *client)
|
||||
|
||||
static int tmp102_read_temp(void *dev, long *temp)
|
||||
{
|
||||
struct tmp102 *tmp102 = tmp102_update_device(to_i2c_client(dev));
|
||||
struct tmp102 *tmp102 = tmp102_update_device(dev);
|
||||
|
||||
*temp = tmp102->temp[0];
|
||||
|
||||
@ -110,7 +112,7 @@ static ssize_t tmp102_show_temp(struct device *dev,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *sda = to_sensor_dev_attr(attr);
|
||||
struct tmp102 *tmp102 = tmp102_update_device(to_i2c_client(dev));
|
||||
struct tmp102 *tmp102 = tmp102_update_device(dev);
|
||||
|
||||
return sprintf(buf, "%d\n", tmp102->temp[sda->index]);
|
||||
}
|
||||
@ -120,8 +122,8 @@ static ssize_t tmp102_set_temp(struct device *dev,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct sensor_device_attribute *sda = to_sensor_dev_attr(attr);
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct tmp102 *tmp102 = i2c_get_clientdata(client);
|
||||
struct tmp102 *tmp102 = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = tmp102->client;
|
||||
long val;
|
||||
int status;
|
||||
|
||||
@ -145,16 +147,13 @@ static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, tmp102_show_temp,
|
||||
static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, tmp102_show_temp,
|
||||
tmp102_set_temp, 2);
|
||||
|
||||
static struct attribute *tmp102_attributes[] = {
|
||||
static struct attribute *tmp102_attrs[] = {
|
||||
&sensor_dev_attr_temp1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_max.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group tmp102_attr_group = {
|
||||
.attrs = tmp102_attributes,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(tmp102);
|
||||
|
||||
#define TMP102_CONFIG (TMP102_CONF_TM | TMP102_CONF_EM | TMP102_CONF_CR1)
|
||||
#define TMP102_CONFIG_RD_ONLY (TMP102_CONF_R0 | TMP102_CONF_R1 | TMP102_CONF_AL)
|
||||
@ -162,72 +161,68 @@ static const struct attribute_group tmp102_attr_group = {
|
||||
static int tmp102_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct device *hwmon_dev;
|
||||
struct tmp102 *tmp102;
|
||||
int status;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_WORD_DATA)) {
|
||||
dev_err(&client->dev,
|
||||
dev_err(dev,
|
||||
"adapter doesn't support SMBus word transactions\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
tmp102 = devm_kzalloc(&client->dev, sizeof(*tmp102), GFP_KERNEL);
|
||||
tmp102 = devm_kzalloc(dev, sizeof(*tmp102), GFP_KERNEL);
|
||||
if (!tmp102)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_set_clientdata(client, tmp102);
|
||||
tmp102->client = client;
|
||||
|
||||
status = i2c_smbus_read_word_swapped(client, TMP102_CONF_REG);
|
||||
if (status < 0) {
|
||||
dev_err(&client->dev, "error reading config register\n");
|
||||
dev_err(dev, "error reading config register\n");
|
||||
return status;
|
||||
}
|
||||
tmp102->config_orig = status;
|
||||
status = i2c_smbus_write_word_swapped(client, TMP102_CONF_REG,
|
||||
TMP102_CONFIG);
|
||||
if (status < 0) {
|
||||
dev_err(&client->dev, "error writing config register\n");
|
||||
dev_err(dev, "error writing config register\n");
|
||||
goto fail_restore_config;
|
||||
}
|
||||
status = i2c_smbus_read_word_swapped(client, TMP102_CONF_REG);
|
||||
if (status < 0) {
|
||||
dev_err(&client->dev, "error reading config register\n");
|
||||
dev_err(dev, "error reading config register\n");
|
||||
goto fail_restore_config;
|
||||
}
|
||||
status &= ~TMP102_CONFIG_RD_ONLY;
|
||||
if (status != TMP102_CONFIG) {
|
||||
dev_err(&client->dev, "config settings did not stick\n");
|
||||
dev_err(dev, "config settings did not stick\n");
|
||||
status = -ENODEV;
|
||||
goto fail_restore_config;
|
||||
}
|
||||
tmp102->last_update = jiffies - HZ;
|
||||
mutex_init(&tmp102->lock);
|
||||
|
||||
status = sysfs_create_group(&client->dev.kobj, &tmp102_attr_group);
|
||||
if (status) {
|
||||
dev_dbg(&client->dev, "could not create sysfs files\n");
|
||||
hwmon_dev = hwmon_device_register_with_groups(dev, client->name,
|
||||
tmp102, tmp102_groups);
|
||||
if (IS_ERR(hwmon_dev)) {
|
||||
dev_dbg(dev, "unable to register hwmon device\n");
|
||||
status = PTR_ERR(hwmon_dev);
|
||||
goto fail_restore_config;
|
||||
}
|
||||
tmp102->hwmon_dev = hwmon_device_register(&client->dev);
|
||||
if (IS_ERR(tmp102->hwmon_dev)) {
|
||||
dev_dbg(&client->dev, "unable to register hwmon device\n");
|
||||
status = PTR_ERR(tmp102->hwmon_dev);
|
||||
goto fail_remove_sysfs;
|
||||
}
|
||||
|
||||
tmp102->tz = thermal_zone_of_sensor_register(&client->dev, 0,
|
||||
&client->dev,
|
||||
tmp102->hwmon_dev = hwmon_dev;
|
||||
tmp102->tz = thermal_zone_of_sensor_register(hwmon_dev, 0, hwmon_dev,
|
||||
tmp102_read_temp, NULL);
|
||||
if (IS_ERR(tmp102->tz))
|
||||
tmp102->tz = NULL;
|
||||
|
||||
dev_info(&client->dev, "initialized\n");
|
||||
dev_info(dev, "initialized\n");
|
||||
|
||||
return 0;
|
||||
|
||||
fail_remove_sysfs:
|
||||
sysfs_remove_group(&client->dev.kobj, &tmp102_attr_group);
|
||||
fail_restore_config:
|
||||
i2c_smbus_write_word_swapped(client, TMP102_CONF_REG,
|
||||
tmp102->config_orig);
|
||||
@ -238,9 +233,8 @@ static int tmp102_remove(struct i2c_client *client)
|
||||
{
|
||||
struct tmp102 *tmp102 = i2c_get_clientdata(client);
|
||||
|
||||
thermal_zone_of_sensor_unregister(&client->dev, tmp102->tz);
|
||||
thermal_zone_of_sensor_unregister(tmp102->hwmon_dev, tmp102->tz);
|
||||
hwmon_device_unregister(tmp102->hwmon_dev);
|
||||
sysfs_remove_group(&client->dev.kobj, &tmp102_attr_group);
|
||||
|
||||
/* Stop monitoring if device was stopped originally */
|
||||
if (tmp102->config_orig & TMP102_CONF_SD) {
|
||||
|
@ -69,7 +69,7 @@ static const struct i2c_device_id tmp421_id[] = {
|
||||
MODULE_DEVICE_TABLE(i2c, tmp421_id);
|
||||
|
||||
struct tmp421_data {
|
||||
struct device *hwmon_dev;
|
||||
struct i2c_client *client;
|
||||
struct mutex update_lock;
|
||||
char valid;
|
||||
unsigned long last_updated;
|
||||
@ -99,8 +99,8 @@ static int temp_from_u16(u16 reg)
|
||||
|
||||
static struct tmp421_data *tmp421_update_device(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct tmp421_data *data = i2c_get_clientdata(client);
|
||||
struct tmp421_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
int i;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
@ -198,6 +198,11 @@ static const struct attribute_group tmp421_group = {
|
||||
.is_visible = tmp421_is_visible,
|
||||
};
|
||||
|
||||
static const struct attribute_group *tmp421_groups[] = {
|
||||
&tmp421_group,
|
||||
NULL
|
||||
};
|
||||
|
||||
static int tmp421_init_client(struct i2c_client *client)
|
||||
{
|
||||
int config, config_orig;
|
||||
@ -264,47 +269,26 @@ static int tmp421_detect(struct i2c_client *client,
|
||||
static int tmp421_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct device *hwmon_dev;
|
||||
struct tmp421_data *data;
|
||||
int err;
|
||||
|
||||
data = devm_kzalloc(&client->dev, sizeof(struct tmp421_data),
|
||||
GFP_KERNEL);
|
||||
data = devm_kzalloc(dev, sizeof(struct tmp421_data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_set_clientdata(client, data);
|
||||
mutex_init(&data->update_lock);
|
||||
data->channels = id->driver_data;
|
||||
data->client = client;
|
||||
|
||||
err = tmp421_init_client(client);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = sysfs_create_group(&client->dev.kobj, &tmp421_group);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
data->hwmon_dev = hwmon_device_register(&client->dev);
|
||||
if (IS_ERR(data->hwmon_dev)) {
|
||||
err = PTR_ERR(data->hwmon_dev);
|
||||
data->hwmon_dev = NULL;
|
||||
goto exit_remove;
|
||||
}
|
||||
return 0;
|
||||
|
||||
exit_remove:
|
||||
sysfs_remove_group(&client->dev.kobj, &tmp421_group);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int tmp421_remove(struct i2c_client *client)
|
||||
{
|
||||
struct tmp421_data *data = i2c_get_clientdata(client);
|
||||
|
||||
hwmon_device_unregister(data->hwmon_dev);
|
||||
sysfs_remove_group(&client->dev.kobj, &tmp421_group);
|
||||
|
||||
return 0;
|
||||
hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
|
||||
data, tmp421_groups);
|
||||
return PTR_ERR_OR_ZERO(hwmon_dev);
|
||||
}
|
||||
|
||||
static struct i2c_driver tmp421_driver = {
|
||||
@ -313,7 +297,6 @@ static struct i2c_driver tmp421_driver = {
|
||||
.name = "tmp421",
|
||||
},
|
||||
.probe = tmp421_probe,
|
||||
.remove = tmp421_remove,
|
||||
.id_table = tmp421_id,
|
||||
.detect = tmp421_detect,
|
||||
.address_list = normal_i2c,
|
||||
|
@ -252,7 +252,7 @@ static const struct attribute_group env_group = {
|
||||
|
||||
static int env_probe(struct platform_device *op)
|
||||
{
|
||||
struct env *p = kzalloc(sizeof(*p), GFP_KERNEL);
|
||||
struct env *p = devm_kzalloc(&op->dev, sizeof(*p), GFP_KERNEL);
|
||||
int err = -ENOMEM;
|
||||
|
||||
if (!p)
|
||||
@ -262,7 +262,7 @@ static int env_probe(struct platform_device *op)
|
||||
|
||||
p->regs = of_ioremap(&op->resource[0], 0, REG_SIZE, "pic16f747");
|
||||
if (!p->regs)
|
||||
goto out_free;
|
||||
goto out;
|
||||
|
||||
err = sysfs_create_group(&op->dev.kobj, &env_group);
|
||||
if (err)
|
||||
@ -286,8 +286,6 @@ out_sysfs_remove_group:
|
||||
out_iounmap:
|
||||
of_iounmap(&op->resource[0], p->regs, REG_SIZE);
|
||||
|
||||
out_free:
|
||||
kfree(p);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -299,7 +297,6 @@ static int env_remove(struct platform_device *op)
|
||||
sysfs_remove_group(&op->dev.kobj, &env_group);
|
||||
hwmon_device_unregister(p->hwmon_dev);
|
||||
of_iounmap(&op->resource[0], p->regs, REG_SIZE);
|
||||
kfree(p);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -1152,10 +1152,8 @@ static int vt1211_probe(struct platform_device *pdev)
|
||||
int i, err;
|
||||
|
||||
data = devm_kzalloc(dev, sizeof(struct vt1211_data), GFP_KERNEL);
|
||||
if (!data) {
|
||||
dev_err(dev, "Out of memory\n");
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
|
||||
if (!devm_request_region(dev, res->start, resource_size(res),
|
||||
|
Loading…
Reference in New Issue
Block a user